[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*.cpp]\nindent_style = space\nindent_size = 4\ninsert_final_newline = true\n\n[*.hpp]\nindent_style = space\nindent_size = 4\ninsert_final_newline = true\n\n[*.glsl]\nindent_style = space\nindent_size = 4\ninsert_final_newline = false"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "patreon: davidcernat\ncustom: https://www.paypal.me/DavidCernat\n"
  },
  {
    "path": ".gitignore",
    "content": "## make\nCMakeFiles\n*/CMakeFiles\nCMakeCache.txt\ncmake_install.cmake\nMakefile\nmakefile\nbuild*/\nprebuilt\n\n##windows build process\n/deps\n/MSVC*\n\n## doxygen\nDoxygen\n\n## ides/editors\n*~\n*.kdev4\n*.swp\n*.swo\n*.kate-swp\n.cproject\n.project\n.settings\n.directory\n.idea\ncmake-build-*\nfiles/windows/*.aps\ncmake-build-*\n## qt-creator\nCMakeLists.txt.user*\n.vs\n.vscode\n\n## resources\ndata\nresources\n/*.cfg\n/*.desktop\n/*.install\n\n## binaries\n/esmtool\n/openmw\n/opencs\n/niftest\n/bsatool\n/openmw-cs\n/openmw-essimporter\n/openmw-iniimporter\n/openmw-launcher\n/openmw-wizard\n\n## generated objects\napps/openmw/config.hpp\napps/launcher/ui_contentselector.h\napps/launcher/ui_settingspage.h\napps/opencs/ui_contentselector.h\napps/opencs/ui_filedialog.h\napps/wizard/qrc_wizard.cxx\napps/wizard/ui_componentselectionpage.h\napps/wizard/ui_conclusionpage.h\napps/wizard/ui_existinginstallationpage.h\napps/wizard/ui_importpage.h\napps/wizard/ui_installationpage.h\napps/wizard/ui_installationtargetpage.h\napps/wizard/ui_intropage.h\napps/wizard/ui_languageselectionpage.h\napps/wizard/ui_methodselectionpage.h\ncomponents/ui_contentselector.h\ndocs/mainpage.hpp\ndocs/Doxyfile\ndocs/DoxyfilePages\nmoc_*.cxx\n*.cxx_parameters\n*qrc_launcher.cxx\n*qrc_resources.cxx\n*__*\n*ui_datafilespage.h\n*ui_graphicspage.h\n*ui_mainwindow.h\n*ui_playpage.h\n*.[ao]\n*.so\nvenv/\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "# Note: We set `needs` on each job to control the job DAG.\n# See https://docs.gitlab.com/ee/ci/yaml/#needs\nstages:\n  - build\n\n.Debian_Image:\n  tags:\n    - docker\n    - linux\n  image: debian:bullseye\n\n.Debian:\n  extends: .Debian_Image\n  cache:\n    paths:\n    - apt-cache/\n    - ccache/\n  stage: build\n  script:\n    - export CCACHE_BASEDIR=\"`pwd`\"\n    - export CCACHE_DIR=\"`pwd`/ccache\" && mkdir -pv \"$CCACHE_DIR\"\n    - ccache -z -M \"${CCACHE_SIZE}\"\n    - CI/before_script.linux.sh\n    - cd build\n    - cmake --build . -- -j $(nproc)\n    - cmake --install .\n    - if [[ \"${BUILD_TESTS_ONLY}\" ]]; then ./openmw_test_suite; fi\n    - if [[ \"${BUILD_TESTS_ONLY}\" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi\n    - ccache -s\n  artifacts:\n    paths:\n      - build/install/\n\nCoverity:\n  extends: .Debian_Image\n  stage: build\n  rules:\n    - if: '$CI_PIPELINE_SOURCE == \"schedule\"'\n  before_script:\n    - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic coverity\n    - curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN\n    - tar xfz /tmp/cov-analysis-linux64.tgz\n  script:\n    - CI/before_script.linux.sh\n    # Add more than just `openmw` once we can build everything under 3h\n    - cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc) openmw\n  after_script:\n    - tar cfz cov-int.tar.gz cov-int\n    - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME\n      --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL\n      --form file=@cov-int.tar.gz --form version=\"`git describe --tags`\"\n      --form description=\"`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID\"\n  variables:\n    CC: gcc\n    CXX: g++\n  timeout: 8h\n\nDebian_GCC:\n  extends: .Debian\n  cache:\n    key: Debian_GCC.v2\n  before_script:\n    - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic\n  variables:\n    CC: gcc\n    CXX: g++\n    CCACHE_SIZE: 3G\n  # When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.\n  timeout: 2h\n\nDebian_GCC_tests:\n  extends: Debian_GCC\n  cache:\n    key: Debian_GCC_tests.v2\n  variables:\n    CCACHE_SIZE: 1G\n    BUILD_TESTS_ONLY: 1\n\nDebian_GCC_Static_Deps:\n  extends: Debian_GCC\n  cache:\n    key: Debian_GCC_Static_Deps\n    paths:\n    - apt-cache/\n    - ccache/\n    - build/extern/fetched/\n  before_script:\n    - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-static\n  variables:\n    CI_OPENMW_USE_STATIC_DEPS: 1\n\nDebian_GCC_Static_Deps_tests:\n  extends: Debian_GCC_Static_Deps\n  cache:\n    key: Debian_GCC_Static_Deps_tests\n  variables:\n    CCACHE_SIZE: 1G\n    BUILD_TESTS_ONLY: 1\n\nDebian_Clang:\n  extends: .Debian\n  before_script:\n    - CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic\n  cache:\n    key: Debian_Clang.v2\n  variables:\n    CC: clang\n    CXX: clang++\n    CCACHE_SIZE: 2G\n  # When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.\n  timeout: 2h\n\nDebian_Clang_tests:\n  extends: Debian_Clang\n  cache:\n    key: Debian_Clang_tests.v2\n  variables:\n    CCACHE_SIZE: 1G\n    BUILD_TESTS_ONLY: 1\n\n.MacOS:\n  image: macos-11-xcode-12\n  tags:\n    - shared-macos-amd64\n  stage: build\n  only:\n    variables:\n      - $CI_PROJECT_ID == \"7107382\"\n  cache:\n    paths:\n    - ccache/\n  script:\n    - rm -fr build  # remove the build directory\n    - CI/before_install.osx.sh\n    - export CCACHE_BASEDIR=\"$(pwd)\"\n    - export CCACHE_DIR=\"$(pwd)/ccache\"\n    - mkdir -pv \"${CCACHE_DIR}\"\n    - ccache -z -M \"${CCACHE_SIZE}\"\n    - CI/before_script.osx.sh\n    - cd build; make -j $(sysctl -n hw.logicalcpu) package\n    - for dmg in *.dmg; do mv \"$dmg\" \"${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg\"; done\n    - ccache -s\n  artifacts:\n    paths:\n      - build/OpenMW-*.dmg\n      - \"build/**/*.log\"\n\nmacOS11_Xcode12:\n  extends: .MacOS\n  image: macos-11-xcode-12\n  allow_failure: true\n  cache:\n    key: macOS11_Xcode12.v1\n  variables:\n    CCACHE_SIZE: 3G\n\nmacOS10.15_Xcode11:\n  extends: .MacOS\n  image: macos-10.15-xcode-11\n  cache:\n    key: macOS10.15_Xcode11.v1\n  variables:\n    CCACHE_SIZE: 3G\n\nvariables: &engine-targets\n  targets: \"openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard\"\n  package: \"Engine\"\n\nvariables: &cs-targets\n  targets: \"openmw-cs,bsatool,esmtool,niftest\"\n  package: \"CS\"\n\nvariables: &tests-targets\n  targets: \"openmw_test_suite,openmw_detournavigator_navmeshtilescache_benchmark\"\n  package: \"Tests\"\n\n.Windows_Ninja_Base:\n  tags:\n    - windows\n  before_script:\n  - Import-Module \"$env:ChocolateyInstall\\helpers\\chocolateyProfile.psm1\"\n  - choco source add -n=openmw-proxy -s=\"https://repo.openmw.org/repository/Chocolatey/\" --priority=1\n  - choco install git --force --params \"/GitAndUnixToolsOnPath\" -y\n  - choco install 7zip -y\n  - choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y\n  - choco install vswhere -y\n  - choco install ninja -y\n  - choco install python -y\n  - refreshenv\n  stage: build\n  script:\n    - $time = (Get-Date -Format \"HH:mm:ss\")\n    - echo ${time}\n    - echo \"started by ${GITLAB_USER_NAME}\"\n    - sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N -b -t\n    - cd MSVC2019_64_Ninja\n    - .\\ActivateMSVC.ps1\n    - cmake --build . --config $config --target ($targets.Split(','))\n    - cd $config\n    - echo \"CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}\" | Out-File -Encoding UTF8 CI-ID.txt\n    - |\n      if (Get-ChildItem -Recurse *.pdb) {\n        7z a -tzip ..\\..\\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt\n        Get-ChildItem -Recurse *.pdb | Remove-Item\n      }\n    - 7z a -tzip ..\\..\\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*'\n    - if ($executables) { foreach ($exe in $executables.Split(',')) { & .\\$exe } }\n  after_script:\n    - Copy-Item C:\\ProgramData\\chocolatey\\logs\\chocolatey.log\n  cache:\n    key: ninja-v2\n    paths:\n    - deps\n    - MSVC2019_64_Ninja/deps/Qt\n  artifacts:\n    when: always\n    paths:\n    - \"*.zip\"\n    - \"*.log\"\n    - MSVC2019_64_Ninja/*.log\n    - MSVC2019_64_Ninja/*/*.log\n    - MSVC2019_64_Ninja/*/*/*.log\n    - MSVC2019_64_Ninja/*/*/*/*.log\n    - MSVC2019_64_Ninja/*/*/*/*/*.log\n    - MSVC2019_64_Ninja/*/*/*/*/*/*.log\n    - MSVC2019_64_Ninja/*/*/*/*/*/*/*.log\n    - MSVC2019_64_Ninja/*/*/*/*/*/*/*/*.log\n\nWindows_Ninja_Engine_Release:\n  extends:\n    - .Windows_Ninja_Base\n  variables:\n    <<: *engine-targets\n    config: \"Release\"\n\nWindows_Ninja_Engine_Debug:\n  extends:\n    - .Windows_Ninja_Base\n  variables:\n    <<: *engine-targets\n    config: \"Debug\"\n\nWindows_Ninja_Engine_RelWithDebInfo:\n  extends:\n    - .Windows_Ninja_Base\n  variables:\n    <<: *engine-targets\n    config: \"RelWithDebInfo\"\n\nWindows_Ninja_CS_Release:\n  extends:\n    - .Windows_Ninja_Base\n  variables:\n    <<: *cs-targets\n    config: \"Release\"\n\nWindows_Ninja_CS_Debug:\n  extends:\n    - .Windows_Ninja_Base\n  variables:\n    <<: *cs-targets\n    config: \"Debug\"\n\nWindows_Ninja_CS_RelWithDebInfo:\n  extends:\n    - .Windows_Ninja_Base\n  variables:\n    <<: *cs-targets\n    config: \"RelWithDebInfo\"\n\nWindows_Ninja_Tests_RelWithDebInfo:\n  extends: .Windows_Ninja_Base\n  stage: build\n  variables:\n    <<: *tests-targets\n    config: \"RelWithDebInfo\"\n    # Gitlab can't successfully execute following binaries due to unknown reason\n    # executables: \"openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe\"\n\n.Windows_MSBuild_Base:\n  tags:\n    - windows\n  before_script:\n  - Import-Module \"$env:ChocolateyInstall\\helpers\\chocolateyProfile.psm1\"\n  - choco source add -n=openmw-proxy -s=\"https://repo.openmw.org/repository/Chocolatey/\" --priority=1\n  - choco install git --force --params \"/GitAndUnixToolsOnPath\" -y\n  - choco install 7zip -y\n  - choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y\n  - choco install vswhere -y\n  - choco install python -y\n  - refreshenv\n  stage: build\n  script:\n    - $time = (Get-Date -Format \"HH:mm:ss\")\n    - echo ${time}\n    - echo \"started by ${GITLAB_USER_NAME}\"\n    - sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -b -t\n    - cd MSVC2019_64\n    - cmake --build . --config $config --target ($targets.Split(','))\n    - cd $config\n    - echo \"CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}\" | Out-File -Encoding UTF8 CI-ID.txt\n    - |\n      if (Get-ChildItem -Recurse *.pdb) {\n        7z a -tzip ..\\..\\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt\n        Get-ChildItem -Recurse *.pdb | Remove-Item\n      }\n    - 7z a -tzip ..\\..\\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*'\n    - if ($executables) { foreach ($exe in $executables.Split(',')) { & .\\$exe } }\n  after_script:\n    - Copy-Item C:\\ProgramData\\chocolatey\\logs\\chocolatey.log\n  cache:\n    key: msbuild-v2\n    paths:\n    - deps\n    - MSVC2019_64/deps/Qt\n  artifacts:\n    when: always\n    paths:\n    - \"*.zip\"\n    - \"*.log\"\n    - MSVC2019_64/*.log\n    - MSVC2019_64/*/*.log\n    - MSVC2019_64/*/*/*.log\n    - MSVC2019_64/*/*/*/*.log\n    - MSVC2019_64/*/*/*/*/*.log\n    - MSVC2019_64/*/*/*/*/*/*.log\n    - MSVC2019_64/*/*/*/*/*/*/*.log\n    - MSVC2019_64/*/*/*/*/*/*/*/*.log\n\nWindows_MSBuild_Engine_Release:\n  extends:\n    - .Windows_MSBuild_Base\n  variables:\n    <<: *engine-targets\n    config: \"Release\"\n\nWindows_MSBuild_Engine_Debug:\n  extends:\n    - .Windows_MSBuild_Base\n  variables:\n    <<: *engine-targets\n    config: \"Debug\"\n\nWindows_MSBuild_Engine_RelWithDebInfo:\n  extends:\n    - .Windows_MSBuild_Base\n  variables:\n    <<: *engine-targets\n    config: \"RelWithDebInfo\"\n\nWindows_MSBuild_CS_Release:\n  extends:\n    - .Windows_MSBuild_Base\n  variables:\n    <<: *cs-targets\n    config: \"Release\"\n\nWindows_MSBuild_CS_Debug:\n  extends:\n    - .Windows_MSBuild_Base\n  variables:\n    <<: *cs-targets\n    config: \"Debug\"\n\nWindows_MSBuild_CS_RelWithDebInfo:\n  extends:\n    - .Windows_MSBuild_Base\n  variables:\n    <<: *cs-targets\n    config: \"RelWithDebInfo\"\n\nWindows_MSBuild_Tests_RelWithDebInfo:\n  extends: .Windows_MSBuild_Base\n  stage: build\n  variables:\n    <<: *tests-targets\n    config: \"RelWithDebInfo\"\n    # Gitlab can't successfully execute following binaries due to unknown reason\n    # executables: \"openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe\"\n\nDebian_AndroidNDK_arm64-v8a:\n  tags:\n    - linux\n  image: debian:bullseye\n  variables:\n    CCACHE_SIZE: 3G\n  cache:\n    key: Debian_AndroidNDK_arm64-v8a.v3\n    paths:\n      - apt-cache/\n      - ccache/\n      - build/extern/fetched/\n  before_script:\n    - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR\n    - echo \"deb http://deb.debian.org/debian unstable main contrib\" > /etc/apt/sources.list\n    - echo \"google-android-ndk-installer google-android-installers/mirror select https://dl.google.com\" | debconf-set-selections\n    - apt-get update -yq\n    - apt-get -q -o dir::cache::archives=\"$APT_CACHE_DIR\" install -y cmake ccache curl unzip git build-essential google-android-ndk-installer\n  stage: build\n  script:\n    - export CCACHE_BASEDIR=\"`pwd`\"\n    - export CCACHE_DIR=\"`pwd`/ccache\" && mkdir -pv \"$CCACHE_DIR\"\n    - ccache -z -M \"${CCACHE_SIZE}\"\n    - CI/before_install.android.sh\n    - CI/before_script.android.sh\n    - cd build\n    - cmake --build . -- -j $(nproc)\n    - cmake --install .\n    - ccache -s\n  artifacts:\n    paths:\n      - build/install/\n  # When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.\n  timeout: 1h30m\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"extern/breakpad\"]\n\tpath = extern/breakpad\n\turl = https://chromium.googlesource.com/breakpad/breakpad\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "version: 2\n\nsphinx:\n  configuration: docs/source/conf.py\n\npython:\n  version: 3.8\n  install:\n    - requirements: docs/requirements.txt\n\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: cpp\nbranches:\n  only:\n    - master\n    - coverity_scan\n    - /openmw-.*$/\n    - /^[0-9]+\\.[0-9]+\\.[0-9]+.*$/\nenv:\n  global:\n   # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created\n   #   via the \"travis encrypt\" command using the project repo's public key\n   - secure: \"1QK0yVyoOB+gf2I7XzvhXu9w/5lq4stBXIwJbVCTjz4Q4XVHCosURaW1MAgKzMrPnbFEwjyn5uQ8BwsvvfkuN1AZD0YXITgc7gyI+J1wQ/p/ljxRxglakU6WEgsTs2J5z9UmGac4YTXg+quK7YP3rv+zuGim2I2rhzImejyzp0Ym3kRCnNcy+SGBsiRaevRJMe00Ch8zGAbEhduQGeSoS6W0rcu02DNlQKiq5NktWsXR+TWWWVfIeIlQR/lbPsCd0pdxMaMv2QCY0rVbwrYxWJwr/Qe45dAdWp+8/C3PbXpeMSGxlLa33nJNX4Lf/djxbjm8KWk6edaXPajrjR/0iwcpwq0jg2Jt6XfEdnJt35F1gpXlc04sxStjG45uloOKCFYT0wdhIO1Lq+hDP54wypQl+JInd5qC001O7pwhVxO36EgKWqo8HD+BqGDBwsNj2engy9Qcp3wO6G0rLBPB3CrZsk9wrHVv5cSiQSLMhId3Xviu3ZI2qEDA+kgTvxrKrsnMj4bILVCyG5Ka2Mj22wIDW9e8oIab9oTdujax3DTN1GkD6QuOAGzwDsNwGASsgfoeZ+FUhgM75RlBWGMilgkmnF7EJ0oAXLEpjtABnEr2d4qHv+y08kOuTDBLB9ExzCIj024dYYYNLZrqPKx0ncHuCMG2QNj2aJAJEZtj1rQ=\"\ncache: ccache\naddons:\n  apt:\n    sources:\n      - sourceline: 'ppa:openmw/openmw'\n      - ubuntu-toolchain-r-test\n    packages: [\n      # Dev\n      cmake, clang-tools-7, gcc-8, g++-8, ccache,\n      # Boost\n      libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev,\n      # FFmpeg\n      libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev,\n      # Audio, Video and Misc. deps\n      libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev,\n      # The other ones from OpenMW ppa\n      libbullet-dev, libopenscenegraph-3.4-dev, libmygui-dev,\n      # tes3mp stuff\n      libboost-dev, libqt5opengl5-dev, libluajit-5.1-dev\n    ]\n  coverity_scan:\n    project:\n      name: \"TES3MP/openmw-tes3mp\"\n      description: \"<Your project description here>\"\n    branch_pattern: coverity_scan\n    notification_email: koncord@tes3mp.com\n    build_command_prepend: \"cov-configure --comptype gcc --compiler gcc-8 --template; cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE\"\n    build_command: \"make VERBOSE=1 -j3\"\nmatrix:\n  include:\n    - os: linux\n      env:\n        - ANALYZE=\"scan-build-7 --use-cc clang-7 --use-c++ clang++-7 \"\n        - MATRIX_CC=\"CC=clang-7 && CXX=clang++-7\"\n      compiler: clang\n      sudo: required\n      dist: bionic\n    - os: linux\n      env:\n        - MATRIX_CC=\"CC=gcc-8 && CXX=g++-8\"\n      sudo: required\n      dist: bionic\n    - os: linux\n      env:\n        - MATRIX_CC=\"CC=clang-7 && CXX=clang++-7\"\n      sudo: required\n      dist: bionic\n  allow_failures:\n    - env:\n        - MATRIX_CC=\"CC=clang-7 && CXX=clang++-7\"\n    - env:\n        - ANALYZE=\"scan-build-7 --use-cc clang-7 --use-c++ clang++-7 \"\n        - MATRIX_CC=\"CC=clang-7 && CXX=clang++-7\"\n\nbefore_install:\n    - ./CI/before_install.${TRAVIS_OS_NAME}.sh\nbefore_script:\n    - ccache -z\n    - ./CI/before_script.${TRAVIS_OS_NAME}.sh\nscript:\n - cd ./build\n - ${ANALYZE} make -j3; fi\n - if [ \"${TRAVIS_OS_NAME}\" = \"osx\" ]; then make package; fi\n - if [ \"${TRAVIS_OS_NAME}\" = \"osx\" ]; then ../CI/check_package.osx.sh; fi\n - if [ \"${TRAVIS_OS_NAME}\" = \"linux\" ] && [ \"${BUILD_TESTS_ONLY}\" ]; then ./openmw_test_suite; fi\n - if [ \"${TRAVIS_OS_NAME}\" = \"linux\" ]; then cd .. && ./CI/check_tabs.sh; fi\n - cd \"${TRAVIS_BUILD_DIR}\"\n - ccache -s\n#deploy:\n# provider: script\n# script: ./CI/deploy.osx.sh\n# skip_cleanup: true\n# on:\n#  branch: master\n#  condition: \"$TRAVIS_EVENT_TYPE = cron && $TRAVIS_OS_NAME = osx\"\n#  repo: TES3MP/openmw-tes3mp\n#notifications:\n#  email:\n#    recipients:\n#      - corrmage+travis-ci@gmail.com\n#    on_success: change\n#    on_failure: always\n#  irc:\n#    channels:\n#      - \"chat.freenode.net#openmw\"\n#    on_success: change\n#    on_failure: always\n#    use_notice: true\n"
  },
  {
    "path": "AUTHORS.md",
    "content": "Contributors\n============\n\nThe OpenMW project was started in 2008 by Nicolay Korslund.\nIn the course of years many people have contributed to the project.\n\nIf you feel your name is missing from this list, please add it to `AUTHORS.md`.\n\n\nProgrammers\n-----------\n\n    Bret Curtis (psi29a) - Project leader 2019-present\n    Marc Zinnschlag (Zini) - Project leader 2010-2018\n    Nicolay Korslund - Project leader 2008-2010\n    scrawl - Top contributor\n\n    Adam Hogan (aurix)\n    Aesylwinn\n    aegis\n    AHSauge\n    Aleksandar Jovanov\n    Alex Haddad (rainChu)\n    Alex McKibben\n    alexanderkjall\n    Alexander Nadeau (wareya)\n    Alexander Olofsson (Ananace)\n    Alex Rice\n    Alex S (docwest)\n    Allofich\n    Andrei Kortunov (akortunov)\n    AnyOldName3\n    Ardekantur\n    Armin Preiml\n    Artem Kotsynyak (greye)\n    Artem Nykolenko (anikm21)\n    artemutin\n    Arthur Moore (EmperorArthur)\n    Assumeru\n    athile\n    Aussiemon\n    Austin Salgat (Salgat)\n    Ben Shealy (bentsherman)\n    Berulacks\n    Britt Mathis (galdor557)\n    Capostrophic\n    Carl Maxwell\n    cc9cii\n    Cédric Mocquillon\n    Chris Boyce (slothlife)\n    Chris Robinson (KittyCat)\n    Cody Glassman (Wazabear)\n    Coleman Smith (olcoal)\n    Cory F. Cohen (cfcohen)\n    Cris Mihalache (Mirceam)\n    crussell187\n    DanielVukelich\n    darkf\n    David Cernat (davidcernat)\n    Declan Millar (declan-millar)\n    devnexen\n    Dieho\n    Dmitry Shkurskiy (endorph)\n    Douglas Diniz (Dgdiniz)\n    Douglas Mencken (dougmencken)\n    dreamer-dead\n    David Teviotdale (dteviot)\n    Diggory Hardy\n    Dmitry Marakasov (AMDmi3)\n    Edmondo Tommasina (edmondo)\n    Eduard Cot (trombonecot)\n    Eli2\n    Emanuel Guével (potatoesmaster)\n    eroen\n    escondida\n    Evgeniy Mineev (sandstranger)\n    Federico Guerra (FedeWar)\n    Fil Krynicki (filkry)\n    Finbar Crago (finbar-crago)\n    Florian Weber (Florianjw)\n    Frédéric Chardon (fr3dz10)\n    Gaëtan Dezeiraud (Brouilles)\n    Gašper Sedej\n    Gijsbert ter Horst (Ghostbird)\n    Gohan1989\n    gugus/gus\n    guidoj\n    Haoda Wang (h313)\n    hristoast\n    Internecine\n    Jackerty\n    Jacob Essex (Yacoby)\n    Jacob Turnbull (Tankinfrank)\n    Jake Westrip (16bitint)\n    James Carty (MrTopCat)\n    James Moore (moore.work)\n    James Stephens (james-h-stephens)\n    Jan-Peter Nilsson (peppe)\n    Jan Borsodi (am0s)\n    Jason Hooks (jhooks)\n    jeaye\n    jefetienne\n    Jeffrey Haines (Jyby)\n    Jengerer\n    Jiří Kuneš (kunesj)\n    Joe Wilkerson (neuralroberts)\n    Joel Graff (graffy)\n    John Blomberg (fstp)\n    Jordan Ayers\n    Jordan Milne\n    Josua Grawitter\n    Jules Blok (Armada651)\n    julianko\n    Julien Voisin (jvoisin/ap0)\n    Karl-Felix Glatzer (k1ll)\n    Kevin Poitra (PuppyKevin)\n    Koncord\n    Kurnevsky Evgeny (kurnevsky)\n    Lars Söderberg (Lazaroth)\n    lazydev\n    Leon Krieg (lkrieg)\n    Leon Saunders (emoose)\n    logzero\n    lohikaarme\n    Lordrea\n    Łukasz Gołębiewski (lukago)\n    Lukasz Gromanowski (lgro)\n    Marc Bouvier (CramitDeFrog)\n    Marcin Hulist (Gohan)\n    Mark Siewert (mark76)\n    Marco Melletti (mellotanica)\n    Marco Schulze\n    Martin Otto (MAtahualpa)\n    Mateusz Kołaczek (PL_kolek)\n    Mateusz Malisz (malice)\n    Max Henzerling (SaintMercury)\n    megaton\n    Michael Hogan (Xethik)\n    Michael Mc Donnell\n    Michael Papageorgiou (werdanith)\n    Michael Stopa (Stomy)\n    Michał Ściubidło (mike-sc)\n    Michał Bień (Glorf)\n    Michał Moroz (dragonee)\n    Miloslav Číž (drummyfish)\n    Miroslav Puda (pakanek)\n    MiroslavR\n    Mitchell Schwitzer (schwitzerm)\n    naclander\n    Narmo\n    Nat Meo (Utopium)\n    Nathan Jeffords (blunted2night)\n    NeveHanter\n    Nialsy\n    Nick Crawford (nighthawk469)\n    Nikolay Kasyanov (corristo)\n    nobrakal\n    Nolan Poe (nopoe)\n    Oleg Chkan (mrcheko)\n    Paul Cercueil (pcercuei)\n    Paul McElroy (Greendogo)\n    pchan3\n    Perry Hugh\n    Petr Mikheev (ptmikheev)\n    Phillip Andrews (PhillipAnd)\n    Pi03k\n    Pieter van der Kloet (pvdk)\n    pkubik\n    PLkolek\n    PlutonicOverkill\n    Radu-Marius Popovici (rpopovici)\n    Rafael Moura (dhustkoder)\n    rdimesio\n    rexelion\n    riothamus\n    Rob Cutmore (rcutmore)\n    Robert MacGregor (Ragora)\n    Rohit Nirmal\n    Roman Melnik (Kromgart)\n    Roman Proskuryakov (kpp)\n    Roman Siromakha (elsid)\n    Sandy Carter (bwrsandman)\n    Scott Howard (maqifrnswa)\n    Sebastian Wick (swick)\n    Sergey Fukanchik\n    Sergey Shambir (sergey-shambir)\n    sergoz\n    ShadowRadiance\n    Siimacore\n    Simon Meulenbeek (simonmb)\n    sir_herrbatka\n    smbas\n    Sophie Kirschner (pineapplemachine)\n    spycrab\n    Stefan Galowicz (bogglez)\n    Stanislav Bobrov (Jiub)\n    Stanislaw Halik (sthalik)\n    Star-Demon\n    stil-t\n    svaante\n    Sylvain Thesnieres (Garvek)\n    t6\n    terrorfisch\n    Tess (tescoShoppah)\n    thegriglat\n    Thomas Luppi (Digmaster)\n    tlmullis\n    tri4ng1e\n    Thoronador\n    Tom Mason (wheybags)\n    Torben Leif Carrington (TorbenC)\n    unelsson\n    uramer\n    viadanna\n    Vincent Heuken\n    Vladimir Panteleev (CyberShadow)\n    Wang Ryu (bzzt)\n    Will Herrmann (Thunderforge)\n    vocollapse\n    xyzz\n    Yohaulticetl\n    Yuri Krupenin\n    zelurker\n    Noah Gooder\n    Andrew Appuhamy (andrew-app)\n\nDocumentation\n-------------\n\n    Adam Bowen (adamnbowen)\n    Alejandro Sanchez (HiPhish)\n    Bodillium\n    Bret Curtis (psi29a)\n    Cramal\n    David Walley (Loriel)\n    Diego Crespo\n    Joakim Berg (lysol90)\n    Ryan Tucker (Ravenwing)\n    sir_herrbatka\n\nPackagers\n---------\n\n    Alexander Olofsson (Ananace) - Windows and Flatpak\n    Alexey Sokolov (DarthGandalf) - Gentoo Linux\n    Bret Curtis (psi29a) - Debian and Ubuntu Linux\n    Edmondo Tommasina (edmondo) - Gentoo Linux\n    Julian Ospald (hasufell) - Gentoo Linux\n    Karl-Felix Glatzer (k1ll) - Linux Binaries\n    Kenny Armstrong (artorius) - Fedora Linux\n    Nikolay Kasyanov (corristo) - Mac OS X\n    Sandy Carter (bwrsandman) - Arch Linux\n\nPublic Relations and Translations\n---------------------------------\n\n    Artem Kotsynyak (greye) - Russian News Writer\n    Dawid Lakomy (Vedyimyn) - Polish News Writer\n    ElderTroll - Release Manager\n    Jim Clauwaert (Zedd) - Public Outreach\n    juanmnzsk8 - Spanish News Writer\n    Julien Voisin (jvoisin/ap0) - French News Writer\n    Kingpix - Italian News Writer\n    Lukasz Gromanowski (lgro) - English News Writer\n    Martin Otto (Atahualpa) - Podcaster, Public Outreach, German Translator\n    Mickey Lyle (raevol) - Release Manager\n    Nekochan - English News Writer\n    penguinroad - Indonesian News Writer\n    Pithorn - Chinese News Writer\n    sir_herrbatka - Polish News Writer\n    spyboot - German Translator\n    Tom Koenderink (Okulo) - English News Writer\n\nWebsite\n-------\n\n    Lukasz Gromanowski (Lgro) - Website Administrator\n    Ryan Sardonic (Wry) - Wiki Editor\n    sir_herrbatka - Forum Administrator\n\nFormula Research\n----------------\n\n    Hrnchamd\n    Epsilon\n    fragonard\n    Greendogo\n    HiPhish\n    modred11\n    Myckel\n    natirips\n    Sadler\n\nArtwork\n-------\n\n    Necrod - OpenMW Logo\n    Mickey Lyle (raevol) - Wordpress Theme\n    Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel, Lamoot - OpenMW Editor Icons\n\nAdditional Credits\n------------------\nIn this section we would like to thank people not part of OpenMW for their work.\n\nThanks to Maxim Nikolaev,\nfor allowing us to use his excellent Morrowind fan-art on our website and in other places.\n\nThanks to DokterDume,\nfor kindly providing us with the Moon and Star logo, used as the application icon and project logo.\n\nThanks to Kevin Ryan,\nfor creating the icon used for the Data Files tab of the OpenMW Launcher.\n\nThanks to DejaVu team,\nfor their DejaVuLGCSansMono fontface, see DejaVuFontLicense.txt for their license terms.\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "0.47.0\n------\n\n    Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path\n    Bug #1901: Actors colliding behaviour is different from vanilla\n    Bug #1952: Incorrect particle lighting\n    Bug #2069: Fireflies in Fireflies invade Morrowind look wrong\n    Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs\n    Bug #2473: Unable to overstock merchants\n    Bug #2976: [reopened]: Issues combining settings from the command line and both config files\n    Bug #3137: Walking into a wall prevents jumping\n    Bug #3372: Projectiles and magic bolts go through moving targets\n    Bug #3676: NiParticleColorModifier isn't applied properly\n    Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects\n    Bug #3789: Crash in visitEffectSources while in battle\n    Bug #3862: Random container contents behave differently than vanilla\n    Bug #3929: Leveled list merchant containers respawn on barter\n    Bug #4021: Attributes and skills are not stored as floats\n    Bug #4039: Multiple followers should have the same following distance\n    Bug #4055: Local scripts don't inherit variables from their base record\n    Bug #4083: Door animation freezes when colliding with actors\n    Bug #4247: Cannot walk up stairs in Ebonheart docks\n    Bug #4357: OpenMW-CS: TopicInfos index sorting and rearranging isn't fully functional\n    Bug #4363: OpenMW-CS: Defect in Clone Function for Dialogue Info records\n    Bug #4447: Actor collision capsule shape allows looking through some walls\n    Bug #4465: Collision shape overlapping causes twitching\n    Bug #4476: Abot Gondoliers: player hangs in air during scenic travel\n    Bug #4568: Too many actors in one spot can push other actors out of bounds\n    Bug #4623: Corprus implementation is incorrect\n    Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level\n    Bug #4764: Data race in osg ParticleSystem\n    Bug #4765: Data race in ChunkManager -> Array::setBinding\n    Bug #4774: Guards are ignorant of an invisible player that tries to attack them\n    Bug #5026: Data races with rain intensity uniform set by sky and used by water\n    Bug #5101: Hostile followers travel with the player\n    Bug #5108: Savegame bloating due to inefficient fog textures format\n    Bug #5165: Active spells should use real time intead of timestamps\n    Bug #5300: NPCs don't switch from torch to shield when starting combat\n    Bug #5358: ForceGreeting always resets the dialogue window completely\n    Bug #5363: Enchantment autocalc not always 0/1\n    Bug #5364: Script fails/stops if trying to startscript an unknown script\n    Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound\n    Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures\n    Bug #5370: Opening an unlocked but trapped door uses the key\n    Bug #5384: OpenMW-CS: Deleting an instance requires reload of scene window to show in editor\n    Bug #5387: Move/MoveWorld don't update the object's cell properly\n    Bug #5391: Races Redone 1.2 bodies don't show on the inventory\n    Bug #5397: NPC greeting does not reset if you leave + reenter area\n    Bug #5400: OpenMW-CS: Verifier checks race of non-skin bodyparts\n    Bug #5403: Enchantment effect doesn't show on an enemy during death animation\n    Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work\n    Bug #5416: Junk non-node records before the root node are not handled gracefully\n    Bug #5422: The player loses all spells when resurrected\n    Bug #5423: Guar follows actors too closely\n    Bug #5424: Creatures do not headtrack player\n    Bug #5425: Poison effect only appears for one frame\n    Bug #5427: GetDistance unknown ID error is misleading\n    Bug #5431: Physics performance degradation after a specific number of actors on a scene\n    Bug #5435: Enemies can't hurt the player when collision is off\n    Bug #5441: Enemies can't push a player character when in critical strike stance\n    Bug #5451: Magic projectiles don't disappear with the caster\n    Bug #5452: Autowalk is being included in savegames\n    Bug #5469: Local map is reset when re-entering certain cells\n    Bug #5472: Mistify mod causes CTD in 0.46 on Mac\n    Bug #5473: OpenMW-CS: Cell border lines don't update properly on terrain change\n    Bug #5479: NPCs who should be walking around town are standing around without walking\n    Bug #5484: Zero value items shouldn't be able to be bought or sold for 1 gold\n    Bug #5485: Intimidate doesn't increase disposition on marginal wins\n    Bug #5490: Hits to carried left slot aren't redistributed if there's no shield equipped\n    Bug #5499: Faction advance is available when requirements not met\n    Bug #5502: Dead zone for analogue stick movement is too small\n    Bug #5507: Sound volume is not clamped on ingame settings update\n    Bug #5525: Case-insensitive search in the inventory window does not work with non-ASCII characters\n    Bug #5531: Actors flee using current rotation by axis x\n    Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution\n    Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue\n    Bug #5557: Diagonal movement is noticeably slower with analogue stick\n    Bug #5588: Randomly clicking on the journal's right-side page when it's empty shows random topics\n    Bug #5603: Setting constant effect cast style doesn't correct effects view\n    Bug #5604: Only one valid NIF root node is loaded from a single file\n    Bug #5611: Usable items with \"0 Uses\" should be used only once\n    Bug #5619: Input events are queued during save loading\n    Bug #5622: Can't properly interact with the console when in pause menu\n    Bug #5627: Bookart not shown if it isn't followed by <BR> statement\n    Bug #5633: Damage Spells in effect before god mode is enabled continue to hurt the player character and can kill them\n    Bug #5639: Tooltips cover Messageboxes\n    Bug #5644: Summon effects running on the player during game initialization cause crashes\n    Bug #5656: Sneaking characters block hits while standing\n    Bug #5661: Region sounds don't play at the right interval\n    Bug #5675: OpenMW-CS: FRMR subrecords are saved with the wrong MastIdx\n    Bug #5680: Bull Netches incorrectly aim over the player character's head and always miss\n    Bug #5681: Player character can clip or pass through bridges instead of colliding against them\n    Bug #5687: Bound items covering the same inventory slot expiring at the same time freezes the game\n    Bug #5688: Water shader broken indoors with enable indoor shadows = false\n    Bug #5695: ExplodeSpell for actors doesn't target the ground\n    Bug #5703: OpenMW-CS menu system crashing on XFCE\n    Bug #5706: AI sequences stop looping after the saved game is reloaded\n    Bug #5713: OpenMW-CS: Collada models are corrupted in Qt-based scene view\n    Bug #5731: OpenMW-CS: skirts are invisible on characters\n    Bug #5739: Saving and loading the save a second or two before hitting the ground doesn't count fall damage\n    Bug #5758: Paralyzed actors behavior is inconsistent with vanilla\n    Bug #5762: Movement solver is insufficiently robust\n    Bug #5800: Equipping a CE enchanted ring deselects an already equipped and selected enchanted ring from the spell menu\n    Bug #5807: Video decoding crash on ARM\n    Bug #5821: NPCs from mods getting removed if mod order was changed\n    Bug #5835: OpenMW doesn't accept negative values for NPC's hello, alarm, fight, and flee\n    Bug #5836: OpenMW dialogue/greeting/voice filter doesn't accept negative Ai values for NPC's hello, alarm, fight, and flee\n    Bug #5838: Local map and other menus become blank in some locations while playing Wizards' Islands mod.\n    Bug #5840: GetSoundPlaying \"Health Damage\" doesn't play when NPC hits target with shield effect ( vanilla engine behavior )\n    Bug #5841: Can't Cast Zero Cost Spells When Magicka is < 0\n    Bug #5869: Guards can initiate arrest dialogue behind locked doors\n    Bug #5871: The console appears if you type the Russian letter \"Ё\" in the name of the enchantment\n    Bug #5877: Effects appearing with empty icon\n    Bug #5899: Visible modal windows and dropdowns crashing game on exit\n    Bug #5902: NiZBufferProperty is unable to disable the depth test\n    Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs\n    Bug #5912: ImprovedBound mod doesn't work\n    Bug #5914: BM: The Swimmer can't reach destination\n    Bug #5923: Clicking on empty spaces between journal entries might show random topics\n    Bug #5934: AddItem command doesn't accept negative values\n    Bug #5975: NIF controllers from sheath meshes are used\n    Bug #5991: Activate should always be allowed for inventory items\n    Bug #5995: NiUVController doesn't calculate the UV offset properly\n    Bug #6007: Crash when ending cutscene is playing\n    Bug #6016: Greeting interrupts Fargoth's sneak-walk\n    Bug #6022: OpenMW-CS: Terrain selection is not updated when undoing/redoing terrain changes\n    Bug #6023: OpenMW-CS: Clicking on a reference in \"Terrain land editing\" mode discards corresponding select/edit action\n    Bug #6028: Particle system controller values are incorrectly used\n    Bug #6035: OpenMW-CS: Circle brush in \"Terrain land editing\" mode sometimes includes vertices outside its radius\n    Bug #6036: OpenMW-CS: Terrain selection at the border of cells omits certain corner vertices\n    Bug #6043: Actor can have torch missing when torch animation is played\n    Bug #6047: Mouse bindings can be triggered during save loading\n    Bug #6136: Game freezes when NPCs try to open doors that are about to be closed\n    Bug #6294: Game crashes with empty pathgrid\n    Feature #390: 3rd person look \"over the shoulder\"\n    Feature #832: OpenMW-CS: Handle deleted references\n    Feature #1536: Show more information about level on menu\n    Feature #2159: \"Graying out\" exhausted dialogue topics\n    Feature #2386: Distant Statics in the form of Object Paging\n    Feature #2404: Levelled List can not be placed into a container\n    Feature #2686: Timestamps in openmw.log\n    Feature #2798: Mutable ESM records\n    Feature #3171: OpenMW-CS: Instance drag selection\n    Feature #3983: Wizard: Add link to buy Morrowind\n    Feature #4201: Projectile-projectile collision\n    Feature #4486: Handle crashes on Windows\n    Feature #4894: Consider actors as obstacles for pathfinding\n    Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing\n    Feature #4917: Do not trigger NavMesh update when RecastMesh update should not change NavMesh\n    Feature #4977: Use the \"default icon.tga\" when an item's icon is not found\n    Feature #5043: Head Bobbing\n    Feature #5199: OpenMW-CS: Improve scene view colors\n    Feature #5297: Add a search function to the \"Datafiles\" tab of the OpenMW launcher\n    Feature #5362: Show the soul gems' trapped soul in count dialog\n    Feature #5445: Handle NiLines\n    Feature #5456: Basic collada animation support\n    Feature #5457: Realistic diagonal movement\n    Feature #5486: Fixes trainers to choose their training skills based on their base skill points\n    Feature #5500: Prepare enough navmesh tiles before scene loading ends\n    Feature #5511: Add in game option to toggle HRTF support in OpenMW\n    Feature #5519: Code Patch tab in launcher\n    Feature #5524: Resume failed script execution after reload\n    Feature #5545: Option to allow stealing from an unconscious NPC during combat\n    Feature #5551: Do not reboot PC after OpenMW installation on Windows\n    Feature #5563: Run physics update in background thread\n    Feature #5579: MCP SetAngle enhancement\n    Feature #5580: Service refusal filtering\n    Feature #5610: Actors movement should be smoother\n    Feature #5642: Ability to attach arrows to actor skeleton instead of bow mesh\n    Feature #5649: Skyrim SE compressed BSA format support\n    Feature #5672: Make stretch menu background configuration more accessible\n    Feature #5692: Improve spell/magic item search to factor in magic effect names\n    Feature #5730: Add graphic herbalism option to the launcher and documents\n    Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used.\n    Feature #5813: Instanced groundcover support\n    Feature #5814: Bsatool should be able to create BSA archives, not only to extract it\n    Feature #5828: Support more than 8 lights\n    Feature #5910: Fall back to delta time when physics can't keep up\n    Feature #5980: Support Bullet with double precision instead of one with single precision\n    Feature #6024: OpenMW-CS: Selecting terrain in \"Terrain land editing\" should support \"Add to selection\" and \"Remove from selection\" modes\n    Feature #6033: Include pathgrid to navigation mesh\n    Feature #6034: Find path based on area cost depending on NPC stats\n    Task #5480: Drop Qt4 support\n    Task #5520: Improve cell name autocompleter implementation\n\n0.46.0\n------\n\n    Bug #1515: Opening console masks dialogue, inventory menu\n    Bug #1933: Actors can have few stocks of the same item\n    Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin\n    Bug #2679: Unable to map mouse wheel under control settings\n    Bug #2969: Scripted items can stack\n    Bug #2976: [reopened in 0.47] Data lines in global openmw.cfg take priority over user openmw.cfg\n    Bug #2987: Editor: some chance and AI data fields can overflow\n    Bug #3006: 'else if' operator breaks script compilation\n    Bug #3109: SetPos/Position handles actors differently\n    Bug #3282: Unintended behaviour when assigning F3 and Windows keys\n    Bug #3550: Companion from mod attacks the air after combat has ended\n    Bug #3609: Items from evidence chest are not considered to be stolen if player is allowed to use the chest\n    Bug #3623: Display scaling breaks mouse recognition\n    Bug #3725: Using script function in a non-conditional expression breaks script compilation\n    Bug #3733: Normal maps are inverted on mirrored UVs\n    Bug #3765: DisableTeleporting makes Mark/Recall/Intervention effects undetectable\n    Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation\n    Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled\n    Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe\n    Bug #3977: Non-ASCII characters in object ID's are not supported\n    Bug #4009: Launcher does not show data files on the first run after installing\n    Bug #4077: Enchanted items are not recharged if they are not in the player's inventory\n    Bug #4141: PCSkipEquip isn't set to 1 when reading books/scrolls\n    Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect\n    Bug #4262: Rain settings are hardcoded\n    Bug #4270: Closing doors while they are obstructed desyncs closing sfx\n    Bug #4276: Resizing character window differs from vanilla\n    Bug #4284: ForceSneak behaviour is inconsistent if the target has AiWander package\n    Bug #4329: Removed birthsign abilities are restored after reloading the save\n    Bug #4341: Error message about missing GDB is too vague\n    Bug #4383: Bow model obscures crosshair when arrow is drawn\n    Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons\n    Bug #4411: Reloading a saved game while falling prevents damage in some cases\n    Bug #4449: Value returned by GetWindSpeed is incorrect\n    Bug #4456: AiActivate should not be cancelled after target activation\n    Bug #4493: If the setup doesn't find what it is expecting, it fails silently and displays the requester again instead of letting the user know what wasn't found.\n    Bug #4523: \"player->ModCurrentFatigue -0.001\" in global script does not cause the running player to fall\n    Bug #4540: Rain delay when exiting water\n    Bug #4594: Actors without AI packages don't use Hello dialogue\n    Bug #4598: Script parser does not support non-ASCII characters\n    Bug #4600: Crash when no sound output is available or --no-sound is used.\n    Bug #4601: Filtering referenceables by gender is broken\n    Bug #4639: Black screen after completing first mages guild mission + training\n    Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog\n    Bug #4680: Heap corruption on faulty esp\n    Bug #4701: PrisonMarker record is not hardcoded like other markers\n    Bug #4703: Editor: it's possible to preview levelled list records\n    Bug #4705: Editor: unable to open exterior cell views from Instances table\n    Bug #4714: Crash upon game load in the repair menu while the \"Your repair failed!\" message is active\n    Bug #4715: \"Cannot get class of an empty object\" exception after pressing ESC in the dialogue mode\n    Bug #4720: Inventory avatar has shield with two-handed weapon during [un]equipping animation\n    Bug #4723: ResetActors command works incorrectly\n    Bug #4736: LandTexture records overrides do not work\n    Bug #4745: Editor: Interior cell lighting field values are not displayed as colors\n    Bug #4746: Non-solid player can't run or sneak\n    Bug #4747: Bones are not read from X.NIF file for NPC animation\n    Bug #4748: Editor: Cloned, moved, added instances re-use RefNum indices\n    Bug #4750: Sneaking doesn't work in first person view if the player is in attack ready state\n    Bug #4756: Animation issues with VAOs\n    Bug #4757: Content selector: files can be cleared when there aren't any files to clear\n    Bug #4768: Fallback numerical value recovery chokes on invalid arguments\n    Bug #4775: Slowfall effect resets player jumping flag\n    Bug #4778: Interiors of Illusion puzzle in Sotha Sil Expanded mod is broken\n    Bug #4783: Blizzard behavior is incorrect\n    Bug #4787: Sneaking makes 1st person walking/bobbing animation super-slow\n    Bug #4797: Player sneaking and running stances are not accounted for when in air\n    Bug #4800: Standing collisions are not updated immediately when an object is teleported without a cell change\n    Bug #4802: You can rest before taking falling damage from landing from a jump\n    Bug #4803: Stray special characters before begin statement break script compilation\n    Bug #4804: Particle system with the \"Has Sizes = false\" causes an exception\n    Bug #4805: NPC movement speed calculations do not take race Weight into account\n    Bug #4810: Raki creature broken in OpenMW\n    Bug #4813: Creatures with known file but no \"Sound Gen Creature\" assigned use default sounds\n    Bug #4815: \"Finished\" journal entry with lower index doesn't close journal, SetJournalIndex closes journal\n    Bug #4820: Spell absorption is broken\n    Bug #4823: Jail progress bar works incorrectly\n    Bug #4826: Uninitialized memory in unit test\n    Bug #4827: NiUVController is handled incorrectly\n    Bug #4828: Potion looping effects VFX are not shown for NPCs\n    Bug #4837: CTD when a mesh with NiLODNode root node with particles is loaded\n    Bug #4841: Russian localization ignores implicit keywords\n    Bug #4844: Data race in savegame loading / GlobalMap render\n    Bug #4847: Idle animation reset oddities\n    Bug #4851: No shadows since switch to OSG\n    Bug #4860: Actors outside of processing range visible for one frame after spawning\n    Bug #4867: Arbitrary text after local variable declarations breaks script compilation\n    Bug #4876: AI ratings handling inconsistencies\n    Bug #4877: Startup script executes only on a new game start\n    Bug #4879: SayDone returns 0 on the frame Say is called\n    Bug #4888: Global variable stray explicit reference calls break script compilation\n    Bug #4896: Title screen music doesn't loop\n    Bug #4902: Using scrollbars in settings causes resolution to change\n    Bug #4904: Editor: Texture painting with duplicate of a base-version texture\n    Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5\n    Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used.\n    Bug #4918: Abilities don't play looping VFX when they're initially applied\n    Bug #4920: Combat AI uses incorrect invisibility check\n    Bug #4922: Werewolves can not attack if the transformation happens during attack\n    Bug #4927: Spell effect having both a skill and an attribute assigned is a fatal error\n    Bug #4932: Invalid records matching when loading save with edited plugin\n    Bug #4933: Field of View not equal with Morrowind\n    Bug #4938: Strings from subrecords with actually empty headers can't be empty\n    Bug #4942: Hand-to-Hand attack type is chosen randomly when \"always use best attack\" is turned off\n    Bug #4945: Poor random magic magnitude distribution\n    Bug #4947: Player character doesn't use lip animation\n    Bug #4948: Footstep sounds while levitating on ground level\n    Bug #4952: Torches held by NPCs flicker too quickly\n    Bug #4961: Flying creature combat engagement takes z-axis into account\n    Bug #4963: Enchant skill progress is incorrect\n    Bug #4964: Multiple effect spell projectile sounds play louder than vanilla\n    Bug #4965: Global light attenuation settings setup is lacking\n    Bug #4969: \"Miss\" sound plays for any actor\n    Bug #4972: Player is able to use quickkeys while disableplayerfighting is active\n    Bug #4979: AiTravel maximum range depends on \"actors processing range\" setting\n    Bug #4980: Drowning mechanics is applied for actors indifferently from distance to player\n    Bug #4984: \"Friendly hits\" feature should be used only for player's followers\n    Bug #4989: Object dimension-dependent VFX scaling behavior is inconsistent\n    Bug #4990: Dead bodies prevent you from hitting\n    Bug #4991: Jumping occasionally takes too much fatigue\n    Bug #4999: Drop instruction behaves differently from vanilla\n    Bug #5001: Possible data race in the Animation::setAlpha()\n    Bug #5004: Werewolves shield their eyes during storm\n    Bug #5012: \"Take all\" on owned container generates a messagebox per item\n    Bug #5018: Spell tooltips don't support purely negative magnitudes\n    Bug #5025: Data race in the ICO::setMaximumNumOfObjectsToCompilePerFrame()\n    Bug #5028: Offered price caps are not trading-specific\n    Bug #5038: Enchanting success chance calculations are blatantly wrong\n    Bug #5047: # in cell names sets color\n    Bug #5050: Invalid spell effects are not handled gracefully\n    Bug #5055: Mark, Recall, Intervention magic effect abilities have no effect when added and removed in the same frame\n    Bug #5056: Calling Cast function on player doesn't equip the spell but casts it\n    Bug #5059: Modded animation with combined attack keys always does max damage and can double damage\n    Bug #5060: Magic effect visuals stop when death animation begins instead of when it ends\n    Bug #5063: Shape named \"Tri Shadow\" in creature mesh is visible if it isn't hidden\n    Bug #5067: Ranged attacks on unaware opponents (\"critical hits\") differ from the vanilla engine\n    Bug #5069: Blocking creatures' attacks doesn't degrade shields\n    Bug #5073: NPCs open doors in front of them even if they don't have to\n    Bug #5074: Paralyzed actors greet the player\n    Bug #5075: Enchanting cast style can be changed if there's no object\n    Bug #5078: DisablePlayerLooking is broken\n    Bug #5081: OpenMW-CS: Apparatus type \"Alembic\" is erroneously named \"Albemic\"\n    Bug #5082: Scrolling with controller in GUI mode is broken\n    Bug #5087: Some valid script names can't be used as string arguments\n    Bug #5089: Swimming/Underwater creatures only swim around ground level\n    Bug #5092: NPCs with enchanted weapons play sound when out of charges\n    Bug #5093: Hand to hand sound plays on knocked out enemies\n    Bug #5097: String arguments can't be parsed as number literals in scripts\n    Bug #5099: Non-swimming enemies will enter water if player is water walking\n    Bug #5103: Sneaking state behavior is still inconsistent\n    Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels\n    Bug #5106: Still can jump even when encumbered\n    Bug #5110: ModRegion with a redundant numerical argument breaks script execution\n    Bug #5112: Insufficient magicka for current spell not reflected on HUD icon\n    Bug #5113: Unknown alchemy question mark not centered\n    Bug #5123: Script won't run on respawn\n    Bug #5124: Arrow remains attached to actor if pulling animation was cancelled\n    Bug #5126: Swimming creatures without RunForward animations are motionless during combat\n    Bug #5134: Doors rotation by \"Lock\" console command is inconsistent\n    Bug #5136: LegionUniform script: can not access local variables\n    Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries\n    Bug #5138: Actors stuck in half closed door\n    Bug #5149: Failing lock pick attempts isn't always a crime\n    Bug #5155: Lock/unlock behavior differs from vanilla\n    Bug #5158: Objects without a name don't fallback to their ID\n    Bug #5159: NiMaterialColorController can only control the diffuse color\n    Bug #5161: Creature companions can't be activated when they are knocked down\n    Bug #5163: UserData is not copied during node cloning\n    Bug #5164: Faction owned items handling is incorrect\n    Bug #5166: Scripts still should be executed after player's death\n    Bug #5167: Player can select and cast spells before magic menu is enabled\n    Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change\n    Bug #5169: Nested levelled items/creatures have significantly higher chance not to spawn\n    Bug #5175: Random script function returns an integer value\n    Bug #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved\n    Bug #5182: OnPCEquip doesn't trigger on skipped beast race attempts to equip something not equippable by beasts\n    Bug #5186: Equipped item enchantments don't affect creatures\n    Bug #5190: On-strike enchantments can be applied to and used with non-projectile ranged weapons\n    Bug #5196: Dwarven ghosts do not use idle animations\n    Bug #5206: A \"class does not have NPC stats\" error when player's follower kills an enemy with damage spell\n    Bug #5209: Spellcasting ignores race height\n    Bug #5210: AiActivate allows actors to open dialogue and inventory windows\n    Bug #5211: Screen fades in if the first loaded save is in interior cell\n    Bug #5212: AiTravel does not work for actors outside of AI processing range\n    Bug #5213: SameFaction script function is broken\n    Bug #5218: Crash when disabling ToggleBorders\n    Bug #5220: GetLOS crashes when actor isn't loaded\n    Bug #5222: Empty cell name subrecords are not saved\n    Bug #5223: Bow replacement during attack animation removes attached arrow\n    Bug #5226: Reputation should be capped\n    Bug #5229: Crash if mesh controller node has no data node\n    Bug #5239: OpenMW-CS does not support non-ASCII characters in path names\n    Bug #5241: On-self absorb spells cannot be detected\n    Bug #5242: ExplodeSpell behavior differs from Cast behavior\n    Bug #5246: Water ripples persist after cell change\n    Bug #5249: Wandering NPCs start walking too soon after they hello\n    Bug #5250: Creatures display shield ground mesh instead of shield body part\n    Bug #5255: \"GetTarget, player\" doesn't return 1 during NPC hello\n    Bug #5261: Creatures can sometimes become stuck playing idles and never wander again\n    Bug #5264: \"Damage Fatigue\" Magic Effect Can Bring Fatigue below 0\n    Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted\n    Bug #5278: Console command Show doesn't fall back to global variable after local var not found\n    Bug #5308: World map copying makes save loading much slower\n    Bug #5313: Node properties of identical type are not applied in the correct order\n    Bug #5326: Formatting issues in the settings.cfg\n    Bug #5328: Skills aren't properly reset for dead actors\n    Bug #5345: Dopey Necromancy does not work due to a missing quote\n    Bug #5350: An attempt to launch magic bolt causes \"AL error invalid value\" error\n    Bug #5352: Light source items' duration is decremented while they aren't visible\n    Feature #1724: Handle AvoidNode\n    Feature #2229: Improve pathfinding AI\n    Feature #3025: Analogue gamepad movement controls\n    Feature #3442: Default values for fallbacks from ini file\n    Feature #3517: Multiple projectiles enchantment\n    Feature #3610: Option to invert X axis\n    Feature #3871: Editor: Terrain Selection\n    Feature #3893: Implicit target for \"set\" function in console\n    Feature #3980: In-game option to disable controller\n    Feature #3999: Shift + Double Click should maximize/restore menu size\n    Feature #4001: Toggle sneak controller shortcut\n    Feature #4068: OpenMW-CS: Add a button to reset key bindings to defaults\n    Feature #4129: Beta Comment to File\n    Feature #4202: Open .omwaddon files without needing to open openmw-cs first\n    Feature #4209: Editor: Faction rank sub-table\n    Feature #4255: Handle broken RepairedOnMe script function\n    Feature #4316: Implement RaiseRank/LowerRank functions properly\n    Feature #4360: Improve default controller bindings\n    Feature #4544: Actors movement deceleration\n    Feature #4673: Weapon sheathing\n    Feature #4675: Support for NiRollController\n    Feature #4708: Radial fog support\n    Feature #4730: Native animated containers support\n    Feature #4784: Launcher: Duplicate Content Lists\n    Feature #4812: Support NiSwitchNode\n    Feature #4831: Item search in the player's inventory\n    Feature #4836: Daytime node switch\n    Feature #4840: Editor: Transient terrain change support\n    Feature #4859: Make water reflections more configurable\n    Feature #4882: Support for NiPalette node\n    Feature #4887: Add openmw command option to set initial random seed\n    Feature #4890: Make Distant Terrain configurable\n    Feature #4944: Pause audio when OpenMW is minimized\n    Feature #4958: Support eight blood types\n    Feature #4962: Add casting animations for magic items\n    Feature #4968: Scalable UI widget skins\n    Feature #4971: OpenMW-CS: Make rotations display as degrees instead of radians\n    Feature #4994: Persistent pinnable windows hiding\n    Feature #5000: Compressed BSA format support\n    Feature #5005: Editor: Instance window via Scene window\n    Feature #5010: Native graphics herbalism support\n    Feature #5031: Make GetWeaponType function return different values for tools\n    Feature #5033: Magic armor mitigation for creatures\n    Feature #5034: Make enchanting window stay open after a failed attempt\n    Feature #5036: Allow scripted faction leaving\n    Feature #5046: Gamepad thumbstick cursor speed\n    Feature #5051: Provide a separate textures for scrollbars\n    Feature #5091: Human-readable light source duration\n    Feature #5094: Unix like console hotkeys\n    Feature #5098: Allow user controller bindings\n    Feature #5114: Refresh launcher mod list\n    Feature #5121: Handle NiTriStrips and NiTriStripsData\n    Feature #5122: Use magic glow for enchanted arrows\n    Feature #5131: Custom skeleton bones\n    Feature #5132: Unique animations for different weapon types\n    Feature #5146: Safe Dispose corpse\n    Feature #5147: Show spell magicka cost in spell buying window\n    Feature #5170: Editor: Land shape editing, land selection\n    Feature #5172: Editor: Delete instances/references with keypress in scene window\n    Feature #5193: Shields sheathing\n    Feature #5201: Editor: Show tool outline in scene view, when using editmodes\n    Feature #5219: Impelement TestCells console command\n    Feature #5224: Handle NiKeyframeController for NiTriShape\n    Feature #5274: Editor: Keyboard shortcut to drop objects to ground/obstacle in scene view\n    Feature #5304: Morrowind-style bump-mapping\n    Feature #5311: Support for gyroscopic input (e.g. Android)\n    Feature #5314: Ingredient filter in the alchemy window\n    Task #4686: Upgrade media decoder to a more current FFmpeg API\n    Task #4695: Optimize Distant Terrain memory consumption\n    Task #4789: Optimize cell transitions\n    Task #4721: Add NMake support to the Windows prebuild script\n\n0.45.0\n------\n\n    Bug #1875: Actors in inactive cells don't heal from resting\n    Bug #1990: Sunrise/sunset not set correct\n    Bug #2131: Lustidrike's spell misses the player every time\n    Bug #2222: Fatigue's effect on selling price is backwards\n    Bug #2256: Landing sound not playing when jumping immediately after landing\n    Bug #2274: Thin platform clips through player character instead of lifting\n    Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped\n    Bug #2446: Restore Attribute/Skill should allow restoring drained attributes\n    Bug #2455: Creatures attacks degrade armor\n    Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash\n    Bug #2626: Resurrecting the player does not resume the game\n    Bug #2772: Non-existing class or faction freezes the game\n    Bug #2835: Player able to slowly move when overencumbered\n    Bug #2852: No murder bounty when a player follower commits murder\n    Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit\n    Bug #2872: Tab completion in console doesn't work with explicit reference\n    Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y\n    Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue\n    Bug #3059: Unable to hit with marksman weapons when too close to an enemy\n    Bug #3072: Fatal error on AddItem <item> that has a script containing Equip <item>\n    Bug #3219: NPC and creature initial position tracing down limit is too small\n    Bug #3249: Fixed revert function not updating views properly\n    Bug #3288: TrueType fonts are handled incorrectly\n    Bug #3374: Touch spells not hitting kwama foragers\n    Bug #3486: [Mod] NPC Commands does not work\n    Bug #3533: GetSpellEffects should detect effects with zero duration\n    Bug #3591: Angled hit distance too low\n    Bug #3629: DB assassin attack never triggers creature spawning\n    Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers\n    Bug #3762: AddSoulGem and RemoveSpell redundant count arguments break script execution\n    Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla\n    Bug #3836: Script fails to compile when command argument contains \"\\n\"\n    Bug #3876: Landscape texture painting is misaligned\n    Bug #3890: Magic light source attenuation is inaccurate\n    Bug #3897: Have Goodbye give all choices the effects of Goodbye\n    Bug #3911: [macOS] Typing in the \"Content List name\" dialog box produces double characters\n    Bug #3920: RemoveSpellEffects doesn't remove constant effects\n    Bug #3948: AiCombat moving target aiming uses incorrect speed for magic projectiles\n    Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes\n    Bug #3993: Terrain texture blending map is not upscaled\n    Bug #3997: Almalexia doesn't pace\n    Bug #4036: Weird behaviour of AI packages if package target has non-unique ID\n    Bug #4047: OpenMW not reporting its version number in MacOS; OpenMW-CS not doing it fully\n    Bug #4110: Fixed undo / redo menu text losing the assigned shortcuts\n    Bug #4125: OpenMW logo cropped on bugtracker\n    Bug #4215: OpenMW shows book text after last EOL tag\n    Bug #4217: Fixme implementation differs from Morrowind's\n    Bug #4221: Characters get stuck in V-shaped terrain\n    Bug #4230: AiTravel package issues break some Tribunal quests\n    Bug #4231: Infected rats from the \"Crimson Plague\" quest rendered unconscious by change in Drain Fatigue functionality\n    Bug #4251: Stationary NPCs do not return to their position after combat\n    Bug #4260: Keyboard navigation makes persuasion exploitable\n    Bug #4271: Scamp flickers when attacking\n    Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+\n    Bug #4286: Scripted animations can be interrupted\n    Bug #4291: Non-persistent actors that started the game as dead do not play death animations\n    Bug #4292: CenterOnCell implementation differs from vanilla\n    Bug #4293: Faction members are not aware of faction ownerships in barter\n    Bug #4304: \"Follow\" not working as a second AI package\n    Bug #4307: World cleanup should remove dead bodies only if death animation is finished\n    Bug #4311: OpenMW does not handle RootCollisionNode correctly\n    Bug #4327: Missing animations during spell/weapon stance switching\n    Bug #4333: Keyboard navigation in containers is not intuitive\n    Bug #4358: Running animation is interrupted when magic mode is toggled\n    Bug #4368: Settings window ok button doesn't have key focus by default\n    Bug #4378: On-self absorb spells restore stats\n    Bug #4393: NPCs walk back to where they were after using ResetActors\n    Bug #4416: Non-music files crash the game when they are tried to be played\n    Bug #4419: MRK NiStringExtraData is handled incorrectly\n    Bug #4426: RotateWorld behavior is incorrect\n    Bug #4429: [Windows] Error on build INSTALL.vcxproj project (debug) with cmake 3.7.2\n    Bug #4431: \"Lock 0\" console command is a no-op\n    Bug #4432: Guards behaviour is incorrect if they do not have AI packages\n    Bug #4433: Guard behaviour is incorrect with Alarm = 0\n    Bug #4451: Script fails to compile when using \"Begin, [ScriptName]\" syntax\n    Bug #4452: Default terrain texture bleeds through texture transitions\n    Bug #4453: Quick keys behaviour is invalid for equipment\n    Bug #4454: AI opens doors too slow\n    Bug #4457: Item without CanCarry flag prevents shield autoequipping in dark areas\n    Bug #4458: AiWander console command handles idle chances incorrectly\n    Bug #4459: NotCell dialogue condition doesn't support partial matches\n    Bug #4460: Script function \"Equip\" doesn't bypass beast restrictions\n    Bug #4461: \"Open\" spell from non-player caster isn't a crime\n    Bug #4463: %g format doesn't return more digits\n    Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages\n    Bug #4467: Content selector: cyrillic characters are decoded incorrectly in plugin descriptions\n    Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal\n    Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour\n    Bug #4474: No fallback when getVampireHead fails\n    Bug #4475: Scripted animations should not cause movement\n    Bug #4479: \"Game\" category on Advanced page is getting too long\n    Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory\n    Bug #4489: Goodbye doesn't block dialogue hyperlinks\n    Bug #4490: PositionCell on player gives \"Error: tried to add local script twice\"\n    Bug #4494: Training cap based off Base Skill instead of Modified Skill\n    Bug #4495: Crossbow animations blending is buggy\n    Bug #4496: SpellTurnLeft and SpellTurnRight animation groups are unused\n    Bug #4497: File names starting with x or X are not classified as animation\n    Bug #4503: Cast and ExplodeSpell commands increase alteration skill\n    Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute\n    Bug #4519: Knockdown does not discard movement in the 1st-person mode\n    Bug #4527: Sun renders on water shader in some situations where it shouldn't\n    Bug #4531: Movement does not reset idle animations\n    Bug #4532: Underwater sfx isn't tied to 3rd person camera\n    Bug #4539: Paper Doll is affected by GUI scaling\n    Bug #4543: Picking cursed items through inventory (menumode) makes it disappear\n    Bug #4545: Creatures flee from werewolves\n    Bug #4551: Replace 0 sound range with default range separately\n    Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed\n    Bug #4557: Topics with reserved names are handled differently from vanilla\n    Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive\n    Bug #4560: OpenMW does not update pinned windows properly\n    Bug #4563: Fast travel price logic checks destination cell instead of service actor cell\n    Bug #4565: Underwater view distance should be limited\n    Bug #4573: Player uses headtracking in the 1st-person mode\n    Bug #4574: Player turning animations are twitchy\n    Bug #4575: Weird result of attack animation blending with movement animations\n    Bug #4576: Reset of idle animations when attack can not be started\n    Bug #4591: Attack strength should be 0 if player did not hold the attack button\n    Bug #4593: Editor: Instance dragging is broken\n    Bug #4597: <> operator causes a compile error\n    Bug #4604: Picking up gold from the ground only makes 1 grabbed\n    Bug #4607: Scaling for animated collision shapes is applied twice\n    Bug #4608: Falling damage is applied twice\n    Bug #4611: Instant magic effects have 0 duration in custom spell cost calculations unlike vanilla\n    Bug #4614: Crash due to division by zero when FlipController has no textures\n    Bug #4615: Flicker effects for light sources are handled incorrectly\n    Bug #4617: First person sneaking offset is not applied while the character is in air\n    Bug #4618: Sneaking is possible while the character is flying\n    Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails\n    Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type\n    Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch\n    Bug #4641: GetPCJumping is handled incorrectly\n    Bug #4644: %Name should be available for all actors, not just for NPCs\n    Bug #4646: Weapon force-equipment messes up ongoing attack animations\n    Bug #4648: Hud thinks that throwing weapons have condition\n    Bug #4649: Levelup fully restores health\n    Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader\n    Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects\n    Bug #4656: Combat AI: back up behaviour is incorrect\n    Bug #4668: Editor: Light source color is displayed as an integer\n    Bug #4669: ToggleCollision should trace the player down after collision being enabled\n    Bug #4671: knownEffect functions should use modified Alchemy skill\n    Bug #4672: Pitch factor is handled incorrectly for crossbow animations\n    Bug #4674: Journal can be opened when settings window is open\n    Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one\n    Bug #4678: Crash in ESP parser when SCVR has no variable names\n    Bug #4684: Spell Absorption is additive\n    Bug #4685: Missing sound causes an exception inside Say command\n    Bug #4689: Default creature soundgen entries are not used\n    Bug #4691: Loading bar for cell should be moved up when text is still active at bottom of screen\n    Feature #912: Editor: Add missing icons to UniversalId tables\n    Feature #1221: Editor: Creature/NPC rendering\n    Feature #1617: Editor: Enchantment effect record verifier\n    Feature #1645: Casting effects from objects\n    Feature #2606: Editor: Implemented (optional) case sensitive global search\n    Feature #2787: Use the autogenerated collision box, if the creature mesh has no predefined one\n    Feature #2845: Editor: add record view and preview default keybindings\n    Feature #2847: Content selector: allow to copy the path to a file by using the context menu\n    Feature #3083: Play animation when NPC is casting spell via script\n    Feature #3103: Provide option for disposition to get increased by successful trade\n    Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results\n    Feature #3641: Editor: Limit FPS in 3d preview window\n    Feature #3703: Ranged sneak attack criticals\n    Feature #4012: Editor: Write a log file if OpenCS crashes\n    Feature #4222: 360° screenshots\n    Feature #4256: Implement ToggleBorders (TB) console command\n    Feature #4285: Support soundgen calls for activators\n    Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts\n    Feature #4345: Add equivalents for the command line commands to Launcher\n    Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically\n    Feature #4444: Per-group KF-animation files support\n    Feature #4466: Editor: Add option to ignore \"Base\" records when running verifier\n    Feature #4488: Make water shader rougher during rain\n    Feature #4509: Show count of enchanted items in stack in the spells list\n    Feature #4512: Editor: Use markers for lights and creatures levelled lists\n    Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill\n    Feature #4549: Weapon priority: use the actual damage in weapon rating calculations\n    Feature #4550: Weapon priority: make ranged weapon bonus more sensible\n    Feature #4579: Add option for applying Strength into hand to hand damage\n    Feature #4581: Use proper logging system\n    Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment\n    Feature #4625: Weapon priority: use weighted mean for melee damage rating\n    Feature #4626: Weapon priority: account for weapon speed\n    Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating\n    Feature #4636: Use sTo GMST in spellmaking menu\n    Feature #4642: Batching potion creation\n    Feature #4647: Cull actors outside of AI processing range\n    Feature #4682: Use the collision box from basic creature mesh if the X one have no collisions\n    Feature #4697: Use the real thrown weapon damage in tooltips and AI\n    Task #2490: Don't open command prompt window on Release-mode builds automatically\n    Task #4545: Enable is_pod string test\n    Task #4605: Optimize skinning\n    Task #4606: Support Rapture3D's OpenAL driver\n    Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9\n    Task #4621: Optimize combat AI\n    Task #4643: Revise editor record verifying functionality\n    Task #4645: Use constants instead of widely used magic numbers\n    Task #4652: Move call to enemiesNearby() from InputManager::rest() to World::canRest()\n\n0.44.0\n------\n\n    Bug #1428: Daedra summoning scripts aren't executed when the item is taken through the inventory\n    Bug #1987: Some glyphs are not supported\n    Bug #2254: Magic related visual effects are not rendered when loading a saved game\n    Bug #2485: Journal alphabetical index doesn't match \"Morrowind content language\" setting\n    Bug #2703: OnPCHitMe is not handled correctly\n    Bug #2829: Incorrect order for content list consisting of a game file and an esp without dependencies\n    Bug #2841: \"Total eclipse\" happens if weather settings are not defined.\n    Bug #2897: Editor: Rename \"Original creature\" field\n    Bug #3278: Editor: Unchecking \"Auto Calc\" flag changes certain values\n    Bug #3343: Editor: ID sorting is case-sensitive in certain tables\n    Bug #3557: Resource priority confusion when using the local data path as installation root\n    Bug #3587: Pathgrid and Flying Creatures wrong behaviour – abotWhereAreAllBirdsGoing\n    Bug #3603: SetPos should not skip weather transitions\n    Bug #3618: Myar Aranath total conversion can't be started due to capital-case extension of the master file\n    Bug #3638: Fast forwarding can move NPC inside objects\n    Bug #3664: Combat music does not start in dialogue\n    Bug #3696: Newlines are accompanied by empty rectangle glyph in dialogs\n    Bug #3708: Controllers broken on macOS\n    Bug #3726: Items with suppressed activation can be picked up via the inventory menu\n    Bug #3783: [Mod] Abot's Silt Striders 1.16 - silt strider \"falls\" to ground and glides on floor during travel\n    Bug #3863: Can be forced to not resist arrest if you cast Calm Humanoid on aggroed death warrant guards\n    Bug #3884: Incorrect enemy behavior when exhausted\n    Bug #3926: Installation Wizard places Morrowind.esm after Tribunal/Bloodmoon if it has a later file creation date\n    Bug #4061: Scripts error on special token included in name\n    Bug #4111: Crash when mouse over soulgem with a now-missing soul\n    Bug #4122: Swim animation should not be interrupted during underwater attack\n    Bug #4134: Battle music behaves different than vanilla\n    Bug #4135: Reflecting an absorb spell different from vanilla\n    Bug #4136: Enchanted weapons without \"ignore normal weapons\" flag don't bypass creature \"ignore normal weapons\" effect\n    Bug #4143: Antialiasing produces graphical artifacts when used with shader lighting\n    Bug #4159: NPCs' base skeleton files should not be optimized\n    Bug #4177: Jumping/landing animation interference/flickering\n    Bug #4179: NPCs do not face target\n    Bug #4180: Weapon switch sound playing even though no weapon is switched\n    Bug #4184: Guards can initiate dialogue even though you are far above them\n    Bug #4190: Enchanted clothes changes visibility with Chameleon on equip/unequip\n    Bug #4191: \"screenshot saved\" message also appears in the screenshot image\n    Bug #4192: Archers in OpenMW have shorter attack range than archers in Morrowind\n    Bug #4210: Some dialogue topics are not highlighted on first encounter\n    Bug #4211: FPS drops after minimizing the game during rainy weather\n    Bug #4216: Thrown weapon projectile doesn't rotate\n    Bug #4223: Displayed spell casting chance must be 0 if player doesn't have enough magicka to cast it\n    Bug #4225: Double \"Activate\" key presses with Mouse and Gamepad.\n    Bug #4226: The current player's class should be default value in the class select menu\n    Bug #4229: Tribunal/Bloodmoon summoned creatures fight other summons\n    Bug #4233: W and A keys override S and D Keys\n    Bug #4235: Wireframe mode affects local map\n    Bug #4239: Quick load from container screen causes crash\n    Bug #4242: Crime greetings display in Journal\n    Bug #4245: Merchant NPCs sell ingredients growing on potted plants they own\n    Bug #4246: Take armor condition into account when calcuting armor rating\n    Bug #4250: Jumping is not as fluid as it was pre-0.43.0\n    Bug #4252: \"Error in frame: FFmpeg exception: Failed to allocate input stream\" message spam if OpenMW encounter non-music file in the Music folder\n    Bug #4261: Magic effects from eaten ingredients always have 1 sec duration\n    Bug #4263: Arrow position is incorrect in 3rd person view during attack for beast races\n    Bug #4264: Player in god mode can be affected by some negative spell effects\n    Bug #4269: Crash when hovering the faction section and the 'sAnd' GMST is missing (as in MW 1.0)\n    Bug #4272: Root note transformations are discarded again\n    Bug #4279: Sometimes cells are not marked as explored on the map\n    Bug #4298: Problem with MessageBox and chargen menu interaction order\n    Bug #4301: Optimizer breaks LOD nodes\n    Bug #4308: PlaceAtMe doesn't inherit scale of calling object\n    Bug #4309: Only harmful effects with resistance effect set are resistable\n    Bug #4313: Non-humanoid creatures are capable of opening doors\n    Bug #4314: Rainy weather slows down the game when changing from indoors/outdoors\n    Bug #4319: Collisions for certain meshes are incorrectly ignored\n    Bug #4320: Using mouse 1 to move forward causes selection dialogues to jump selections forward.\n    Bug #4322: NPC disposition: negative faction reaction modifier doesn't take PC rank into account\n    Bug #4328: Ownership by dead actors is not cleared from picked items\n    Bug #4334: Torch and shield usage inconsistent with original game\n    Bug #4336: Wizard: Incorrect Morrowind assets path autodetection\n    Bug #4343: Error message for coc and starting cell shouldn't imply that it only works for interior cells\n    Bug #4346: Count formatting does not work well with very high numbers\n    Bug #4351: Using AddSoulgem fills all soul gems of the specified type\n    Bug #4391: No visual indication is provided when an unavailable spell fails to be chosen via a quick key\n    Bug #4392: Inventory filter breaks after loading a game\n    Bug #4405: No default terrain in empty cells when distant terrain is enabled\n    Bug #4410: [Mod] Arktwend: OpenMW does not use default marker definitions\n    Bug #4412: openmw-iniimporter ignores data paths from config\n    Bug #4413: Moving with 0 strength uses all of your fatigue\n    Bug #4420: Camera flickering when I open up and close menus while sneaking\n    Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK\n    Bug #4435: Item health is considered a signed integer\n    Bug #4441: Adding items to currently disabled weapon-wielding creatures crashes the game\n    Feature #1786: Round up encumbrance value in the encumbrance bar\n    Feature #2694: Editor: rename \"model\" column to make its purpose clear\n    Feature #3870: Editor: Terrain Texture Brush Button\n    Feature #3872: Editor: Edit functions in terrain texture editing mode\n    Feature #4054: Launcher: Create menu for settings.cfg options\n    Feature #4064: Option for fast travel services to charge for the first companion\n    Feature #4142: Implement fWereWolfHealth GMST\n    Feature #4174: Multiple quicksaves\n    Feature #4407: Support NiLookAtController\n    Feature #4423: Rebalance soul gem values\n    Task #4015: Use AppVeyor build artifact features to make continuous builds available\n    Editor: New (and more complete) icon set\n\n0.43.0\n------\n\n    Bug #815: Different settings cause inconsistent underwater visibility\n    Bug #1452: autosave is not executed when waiting\n    Bug #1555: Closing containers with spacebar doesn't work after touching an item\n    Bug #1692: Can't close container when item is \"held\"\n    Bug #2405: Maximum distance for guards attacking hostile creatures is incorrect\n    Bug #2445: Spellcasting can be interrupted\n    Bug #2489: Keeping map open not persisted between saves\n    Bug #2594: 1st person view uses wrong body texture with Better bodies\n    Bug #2628: enablestatreviewmenu command doen't read race, class and sign values from current game\n    Bug #2639: Attacking flag isn't reset upon reloading\n    Bug #2698: Snow and rain VFX move with the player\n    Bug #2704: Some creature swim animations not being used\n    Bug #2789: Potential risk of misunderstanding using the colored \"owned\" crosshair feature\n    Bug #3045: Settings containing '#' cannot be loaded\n    Bug #3097: Drop() doesn't work when an item is held (with the mouse)\n    Bug #3110: GetDetected doesn't work without a reference\n    Bug #3126: Framerate nosedives when adjusting dialogue window size\n    Bug #3243: Ampersand in configuration files isn't escaped automatically\n    Bug #3365: Wrong water reflection along banks\n    Bug #3441: Golden saint always dispelling soul trap / spell priority issue\n    Bug #3528: Disposing of corpses breaks quests\n    Bug #3531: No FPS limit when playing bink videos even though \"framerate limit\" is set in settings.cfg\n    Bug #3647: Multi-effect spells play audio louder than in Vanilla\n    Bug #3656: NPCs forget where their place in the world is\n    Bug #3665: Music transitions are too abrupt\n    Bug #3679: Spell cast effect should disappear after using rest command\n    Bug #3684: Merchants do not restock empty soul gems if they acquire filled ones.\n    Bug #3694: Wrong magicka bonus applied on character creation\n    Bug #3706: Guards don't try to arrest the player if attacked\n    Bug #3709: Editor: Camera is not positioned correctly on mode switches related to orbital mode\n    Bug #3720: Death counter not cleaned of non-existing IDs when loading a game\n    Bug #3744: \"Greater/lesser or equal\" operators are not parsed when their signs are swapped\n    Bug #3749: Yagrum Bagarn moves to different position on encountering\n    Bug #3766: DisableLevitation does not remove visuals of preexisting effect\n    Bug #3787: Script commands in result box for voiced dialogue are ignored\n    Bug #3793: OpenMW tries to animate animated references even when they are disabled\n    Bug #3794: Default sound buffer size is too small for mods\n    Bug #3796: Mod 'Undress for me' doesn't work: NPCs re-equip everything\n    Bug #3798: tgm command behaviour differs from vanilla\n    Bug #3804: [Mod] Animated Morrowind: some animations do not loop correctly\n    Bug #3805: Slight enchant miscalculation\n    Bug #3826: Rendering problems with an image in a letter\n    Bug #3833: [Mod] Windows Glow: windows textures are much darker than in original game\n    Bug #3835: Bodyparts with multiple NiTriShapes are not handled correctly\n    Bug #3839: InventoryStore::purgeEffect() removes only first effect with argument ID\n    Bug #3843: Wrong jumping fatigue loss calculations\n    Bug #3850: Boethiah's voice is distorted underwater\n    Bug #3851: NPCs and player say things while underwater\n    Bug #3864: Crash when exiting to Khartag point from Ilunibi\n    Bug #3878: Swapping soul gems while enchanting allows constant effect enchantments using any soul gem\n    Bug #3879: Dialogue option: Go to jail, persists beyond quickload\n    Bug #3891: Journal displays empty entries\n    Bug #3892: Empty space before dialogue entry display\n    Bug #3898: (mod) PositionCell in dialogue results closes dialogue window\n    Bug #3906: \"Could not find Data Files location\" dialog can appear multiple times\n    Bug #3908: [Wizard] User gets stuck if they cancel out of installing from a CD\n    Bug #3909: Morrowind Content Language dropdown is the only element on the right half of the Settings window\n    Bug #3910: Launcher window can be resized so that it cuts off the scroll\n    Bug #3915: NC text key on nifs doesn't work\n    Bug #3919: Closing inventory while cursor hovers over spell (or other magic menu item) produces left click sound\n    Bug #3922: Combat AI should avoid enemy hits when casts Self-ranged spells\n    Bug #3934: [macOS] Copy/Paste from system clipboard uses Control key instead of Command key\n    Bug #3935: Incorrect attack strength for AI actors\n    Bug #3937: Combat AI: enchanted weapons have too high rating\n    Bug #3942: UI sounds are distorted underwater\n    Bug #3943: CPU/GPU usage should stop when the game is minimised\n    Bug #3944: Attempting to sell stolen items back to their owner does not remove them from your inventory\n    Bug #3955: Player's avatar rendering issues\n    Bug #3956: EditEffectDialog: Cancel button does not update a Range button and an Area slider properly\n    Bug #3957: Weird bodypart rendering if a node has reserved name\n    Bug #3960: Clothes with high cost (> 32768) are not handled properly\n    Bug #3963: When on edge of being burdened the condition doesn't lower as you run.\n    Bug #3971: Editor: Incorrect colour field in cell table\n    Bug #3974: Journal page turning doesn't produce sounds\n    Bug #3978: Instant opening and closing happens when using a Controller with Menus/Containers\n    Bug #3981: Lagging when spells are cast, especially noticeable on new landmasses such as Tamriel Rebuilt\n    Bug #3982: Down sounds instead of Up ones are played when trading\n    Bug #3987: NPCs attack after some taunting with no \"Goodbye\"\n    Bug #3991: Journal can still be opened at main menu\n    Bug #3995: Dispel cancels every temporary magic effect\n    Bug #4002: Build broken on OpenBSD with clang\n    Bug #4003: Reduce Render Area of Inventory Doll to Fit Within Border\n    Bug #4004: Manis Virmaulese attacks without saying anything\n    Bug #4010: AiWander: \"return to the spawn position\" feature does not work properly\n    Bug #4016: Closing menus with spacebar will still send certain assigned actions through afterwards\n    Bug #4017: GetPCRunning and GetPCSneaking should check that the PC is actually moving\n    Bug #4024: Poor music track distribution\n    Bug #4025: Custom spell with copy-pasted name always sorts to top of spell list\n    Bug #4027: Editor: OpenMW-CS misreports its own name as \"OpenCS\", under Mac OS\n    Bug #4033: Archers don't attack if the arrows have run out and there is no other weapon\n    Bug #4037: Editor: New greetings do not work in-game.\n    Bug #4049: Reloading a saved game while falling prevents damage\n    Bug #4056: Draw animation should not be played when player equips a new weapon\n    Bug #4074: Editor: Merging of LAND/LTEX records\n    Bug #4076: Disposition bar is not updated when \"goodbye\" selected in dialogue\n    Bug #4079: Alchemy skill increases do not take effect until next batch\n    Bug #4093: GetResistFire, getResistFrost and getResistShock doesn't work as in vanilla\n    Bug #4094: Level-up messages for levels past 20 are hardcoded not to be used\n    Bug #4095: Error in framelistener when take all items from a dead corpse\n    Bug #4096: Messagebox with the \"%0.f\" format should use 0 digit precision\n    Bug #4104: Cycling through weapons does not skip broken ones\n    Bug #4105: birthsign generation menu does not show full details\n    Bug #4107: Editor: Left pane in Preferences window is too narrow\n    Bug #4112: Inventory sort order is inconsistent\n    Bug #4113: 'Resolution not supported in fullscreen' message is inconvenient\n    Bug #4131: Pickpocketing behaviour is different from vanilla\n    Bug #4155: NPCs don't equip a second ring in some cases\n    Bug #4156: Snow doesn't create water ripples\n    Bug #4165: NPCs autoequip new clothing with the same price\n    Feature #452: Rain-induced water ripples\n    Feature #824: Fading for doors and teleport commands\n    Feature #933: Editor: LTEX record table\n    Feature #936: Editor: LAND record table\n    Feature #1374: AI: Resurface to breathe\n    Feature #2320: ess-Importer: convert projectiles\n    Feature #2509: Editor: highlighting occurrences of a word in a script\n    Feature #2748: Editor: Should use one resource manager per document\n    Feature #2834: Have openMW's UI remember what menu items were 'pinned' across boots.\n    Feature #2923: Option to show the damage of the arrows through tooltip.\n    Feature #3099: Disabling inventory while dragging an item forces you to drop it\n    Feature #3274: Editor: Script Editor - Shortcuts and context menu options for commenting code out and uncommenting code respectively\n    Feature #3275: Editor: User Settings- Add an option to reset settings to their default status (per category / all)\n    Feature #3400: Add keyboard shortcuts for menus\n    Feature #3492: Show success rate while enchanting\n    Feature #3530: Editor: Reload data files\n    Feature #3682: Editor: Default key binding reset\n    Feature #3921: Combat AI: aggro priorities\n    Feature #3941: Allow starting at an unnamed exterior cell with --start\n    Feature #3952: Add Visual Studio 2017 support\n    Feature #3953: Combat AI: use \"WhenUsed\" enchantments\n    Feature #4082: Leave the stack of ingredients or potions grabbed after using an ingredient/potion\n    Task #2258: Windows installer: launch OpenMW tickbox\n    Task #4152: The Windows CI script is moving files around that CMake should be dealing with\n\n0.42.0\n------\n\n    Bug #1956: Duplicate objects after loading the game, when a mod was edited\n    Bug #2100: Falling leaves in Vurt's Leafy West Gash II not rendered correctly\n    Bug #2116: Cant fit through some doorways pressed against staircases\n    Bug #2289: Some modal dialogs are not centered on the screen when the window resizes\n    Bug #2409: Softlock when pressing weapon/magic switch keys during chargen, afterwards switches weapons even though a text field is selected\n    Bug #2483: Previous/Next Weapon hotkeys triggered while typing the name of game save\n    Bug #2629: centeroncell, coc causes death / fall damage time to time when teleporting from high\n    Bug #2645: Cycling weapons is possible while console/pause menu is open\n    Bug #2678: Combat with water creatures do not end upon exiting water\n    Bug #2759: Light Problems in Therana's Chamber in Tel Branora\n    Bug #2771: unhandled sdl event of type 0x302\n    Bug #2777: (constant/on cast) disintegrate armor/weapon on self is seemingly not working\n    Bug #2838: Editor: '.' in a record name should be allowed\n    Bug #2909: NPCs appear floating when standing on a slope\n    Bug #3093: Controller movement cannot be used while mouse is moving\n    Bug #3134: Crash possible when using console with open container\n    Bug #3254: AI enemies hit between them.\n    Bug #3344: Editor: Verification results sorting by Type is not alphabetical.\n    Bug #3345: Editor: Cloned and added pathgrids are lost after reopen of saved omwgame file\n    Bug #3355: [MGSO] Physics maxing out in south cornerclub Balmora\n    Bug #3484: Editor: camera position is not set when changing cell via drag&drop\n    Bug #3508: Slowfall kills Jump momentum\n    Bug #3580: Crash: Error ElementBufferObject::remove BufferData<0> out of range\n    Bug #3581: NPCs wander too much\n    Bug #3601: Menu Titles not centered vertically\n    Bug #3607: [Mac OS] Beginning of NPC speech cut off (same issue as closed bug #3453)\n    Bug #3613: Can not map \"next weapon\" or \"next spell\" to controller\n    Bug #3617: Enchanted arrows don't explode when hitting the ground\n    Bug #3645: Unable to use steps in Vivec, Palace of Vivec\n    Bug #3650: Tamriel Rebuilt 16.09.1 – Hist Cuirass GND nif is rendered inside a Pink Box\n    Bug #3652: Item icon shadows get stuck in the alchemy GUI\n    Bug #3653: Incorrect swish sounds\n    Bug #3666: NPC collision should not be disabled until death animation has finished\n    Bug #3669: Editor: Text field was missing from book object editing dialogue\n    Bug #3670: Unhandled SDL event of type 0x304\n    Bug #3671: Incorrect local variable value after picking up bittercup\n    Bug #3686: Travelling followers doesn't increase travel fee\n    Bug #3689: Problematic greetings from Antares Big Mod that override the appropriate ones.\n    Bug #3690: Certain summoned creatures do not engage in combat with underwater creatures\n    Bug #3691: Enemies do not initiate combat with player followers on sight\n    Bug #3695: [Regression] Dispel does not always dispel spell effects in 0.41\n    Bug #3699: Crash on MWWorld::ProjectileManager::moveMagicBolts\n    Bug #3700: Climbing on rocks and mountains\n    Bug #3704: Creatures don't auto-equip their shields on creation\n    Bug #3705: AI combat engagement logic differs from vanilla\n    Bug #3707: Animation playing does some very odd things if pc comes in contact with the animated mesh\n    Bug #3712: [Mod] Freeze upon entering Adanumuran with mod Adanumuran Reclaimed\n    Bug #3713: [Regression] Cancelling dialogue or using travel with creatures throws a (possibly game-breaking) exception\n    Bug #3719: Dropped identification papers can't be picked up again\n    Bug #3722: Command spell doesn't bring enemies out of combat\n    Bug #3727: Using \"Activate\" mid-script-execution invalidates interpreter context\n    Bug #3746: Editor: Book records show attribute IDs instead of skill IDs for teached skills entry.\n    Bug #3755: Followers stop following after loading from savegame\n    Bug #3772: ModStat lowers attribute to 100 if it was greater\n    Bug #3781: Guns in Clean Hunter Rifles mod use crossbow sounds\n    Bug #3797: NPC and creature names don't show up in combat when RMB windows are displayed\n    Bug #3800: Wrong tooltip maximum width\n    Bug #3801: Drowning widget is bugged\n    Bug #3802: BarterOffer shouldn't limit pcMercantile\n    Bug #3813: Some fatal error\n    Bug #3816: Expression parser thinks the -> token is unexpected when a given explicit refID clashes with a journal ID\n    Bug #3822: Custom added creatures are not animated\n    Feature #451: Water sounds\n    Feature #2691: Light particles sometimes not shown in inventory character preview\n    Feature #3523: Light source on magic projectiles\n    Feature #3644: Nif NiSphericalCollider Unknown Record Type\n    Feature #3675: ess-Importer: convert mark location\n    Feature #3693: ess-Importer: convert last known exterior cell\n    Feature #3748: Editor: Replace \"Scroll\" check box in Book records with \"Book Type\" combo box.\n    Feature #3751: Editor: Replace \"Xyz Blood\" check boxes in NPC and Creature records with \"Blood Type\" combo box\n    Feature #3752: Editor: Replace emitter check boxes in Light records with \"Emitter Type\" combo box\n    Feature #3756: Editor: Replace \"Female\" check box in NPC records with \"Gender\" combo box\n    Feature #3757: Editor: Replace \"Female\" check box in BodyPart records with \"Gender\" combo box\n    Task #3092: const version of ContainerStoreIterator\n    Task #3795: /deps folder not in .gitignore\n\n0.41.0\n------\n\n    Bug #1138: Casting water walking doesn't move the player out of the water\n    Bug #1931: Rocks from blocked passage in Bamz-Amschend, Radacs Forge can reset and cant be removed again.\n    Bug #2048: Almvisi and Divine Intervention display wrong spell effect\n    Bug #2054: Show effect-indicator for \"instant effect\" spells and potions\n    Bug #2150: Clockwork City door animation problem\n    Bug #2288: Playback of weapon idle animation not correct\n    Bug #2410: Stat-review window doesn't display starting spells, powers, or abilities\n    Bug #2493: Repairing occasionally very slow\n    Bug #2716: [OSG] Water surface is too transparent from some angles\n    Bug #2859: [MAC OS X] Cannot exit fullscreen once enabled\n    Bug #3091: Editor: will not save addon if global variable value type is null\n    Bug #3277: Editor: Non-functional nested tables in subviews need to be hidden instead of being disabled\n    Bug #3348: Disabled map markers show on minimap\n    Bug #3350: Extending selection to instances with same object results in duplicates.\n    Bug #3353: [Mod] Romance version 3.7 script failed\n    Bug #3376: [Mod] Vampire Embrace script fails to execute\n    Bug #3385: Banners don't animate in stormy weather as they do in the original game\n    Bug #3393: Akulakhan re-enabled after main quest\n    Bug #3427: Editor: OpenMW-CS instances won´t get deleted\n    Bug #3451: Feril Salmyn corpse isn't where it is supposed to be\n    Bug #3497: Zero-weight armor is displayed as \"heavy\" in inventory tooltip\n    Bug #3499: Idle animations don't always loop\n    Bug #3500: Spark showers at Sotha Sil do not appear until you look at the ceiling\n    Bug #3515: Editor: Moved objects in interior cells are teleported to exterior cells.\n    Bug #3520: Editor: OpenMW-CS cannot find project file when launching the game\n    Bug #3521: Armed NPCs don't use correct melee attacks\n    Bug #3535: Changing cell immediately after dying causes character to freeze.\n    Bug #3542: Unable to rest if unalerted slaughterfish are in the cell with you\n    Bug #3549: Blood effects occur even when a hit is resisted\n    Bug #3551: NPC Todwendy in german version can't interact\n    Bug #3552: Opening the journal when fonts are missing results in a crash\n    Bug #3555: SetInvisible command should not apply graphic effect\n    Bug #3561: Editor: changes from omwaddon are not loaded in [New Addon] mode\n    Bug #3562: Non-hostile NPCs can be disarmed by stealing their weapons via sneaking\n    Bug #3564: Editor: openmw-cs verification results\n    Bug #3568: Items that should be invisible are shown in the inventory\n    Bug #3574: Alchemy: Alembics and retorts are used in reverse\n    Bug #3575: Diaglog choices don't work in mw 0.40\n    Bug #3576: Minor differences in AI reaction to hostile spell effects\n    Bug #3577: not local nolore dialog test\n    Bug #3578: Animation Replacer hangs after one cicle/step\n    Bug #3579: Bound Armor skillups and sounds\n    Bug #3583: Targetted GetCurrentAiPackage returns 0\n    Bug #3584: Persuasion bug\n    Bug #3590: Vendor, Ilen Faveran, auto equips items from stock\n    Bug #3594: Weather doesn't seem to update correctly in Mournhold\n    Bug #3598: Saving doesn't save status of objects\n    Bug #3600: Screen goes black when trying to travel to Sadrith Mora\n    Bug #3608: Water ripples aren't created when walking on water\n    Bug #3626: Argonian NPCs swim like khajiits\n    Bug #3627: Cannot delete \"Blessed touch\" spell from spellbook\n    Bug #3634: An enchanted throwing weapon consumes charges from the stack in your inventory. (0.40.0)\n    Bug #3635: Levelled items in merchants are \"re-rolled\" (not bug 2952, see inside)\n    Feature #1118: AI combat: flee\n    Feature #1596: Editor: Render water\n    Feature #2042: Adding a non-portable Light to the inventory should cause the player to glow\n    Feature #3166: Editor: Instance editing mode - rotate sub mode\n    Feature #3167: Editor: Instance editing mode - scale sub mode\n    Feature #3420: ess-Importer: player control flags\n    Feature #3489: You shouldn't be be able to re-cast a bound equipment spell\n    Feature #3496: Zero-weight boots should play light boot footsteps\n    Feature #3516: Water Walking should give a \"can't cast\" message and fail when you are too deep\n    Feature #3519: Play audio and visual effects for all effects in a spell\n    Feature #3527: Double spell explosion scaling\n    Feature #3534: Play particle textures for spell effects\n    Feature #3539: Make NPCs use opponent's weapon range to decide whether to dodge\n    Feature #3540: Allow dodging for creatures with \"biped\" flag\n    Feature #3545: Drop shadow for items in menu\n    Feature #3558: Implement same spell range for \"on touch\" spells as original engine\n    Feature #3560: Allow using telekinesis with touch spells on objects\n    Task #3585: Some objects added by Morrowind Rebirth do not display properly their texture\n\n0.40.0\n------\n\n    Bug #1320: AiWander - Creatures in cells without pathgrids do not wander\n    Bug #1873: Death events are triggered at the beginning of the death animation\n    Bug #1996: Resting interrupts magic effects\n    Bug #2399: Vampires can rest in broad daylight and survive the experience\n    Bug #2604: Incorrect magicka recalculation\n    Bug #2721: Telekinesis extends interaction range where it shouldn't\n    Bug #2981: When waiting, NPCs can go where they wouldn't go normally.\n    Bug #3045: Esp files containing the letter '#' in the file name cannot be loaded on startup\n    Bug #3071: Slowfall does not stop momentum when jumping\n    Bug #3085: Plugins can not replace parent cell references with a cell reference of different type\n    Bug #3145: Bug with AI Cliff Racer. He will not attack you, unless you put in front of him.\n    Bug #3149: Editor: Weather tables were missing from regions\n    Bug #3201: Netch shoots over your head\n    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\n    Bug #3286: Editor: Script editor tab width\n    Bug #3329: Teleportation spells cause crash to desktop after build update from 0.37 to 0.38.0\n    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\n    Bug #3332: Editor: Scene view: Tool tips only occur when holding the left mouse button\n    Bug #3340: ESS-Importer does not separate item stacks\n    Bug #3342: Editor: Creation of pathgrids did not check if the pathgrid already existed\n    Bug #3346: \"Talked to PC\" is always 0 for \"Hello\" dialogue\n    Bug #3349: AITravel doesn't repeat\n    Bug #3370: NPCs wandering to invalid locations after training\n    Bug #3378: \"StopCombat\" command does not function in vanilla quest\n    Bug #3384: Battle at Nchurdamz - Larienna Macrina does not stop combat after killing Hrelvesuu\n    Bug #3388: Monster Respawn tied to Quicksave\n    Bug #3390: Strange visual effect in Dagoth Ur's chamber\n    Bug #3391: Inappropriate Blight weather behavior at end of main quest\n    Bug #3394: Replaced dialogue inherits some of its old data\n    Bug #3397: Actors that start the game dead always have the same death pose\n    Bug #3401: Sirollus Saccus sells not glass arrows\n    Bug #3402: Editor: Weapon data not being properly set\n    Bug #3405: Mulvisic Othril will not use her chitin throwing stars\n    Bug #3407: Tanisie Verethi will immediately detect the player\n    Bug #3408: Improper behavior of ashmire particles\n    Bug #3412: Ai Wander start time resets when saving/loading the game\n    Bug #3416: 1st person and 3rd person camera isn't converted from .ess correctly\n    Bug #3421: Idling long enough while paralyzed sometimes causes character to get stuck\n    Bug #3423: Sleep interruption inside dungeons too agressive\n    Bug #3424: Pickpocketing sometimes won't work\n    Bug #3432: AiFollow / AiEscort durations handled incorrectly\n    Bug #3434: Dead NPC's and Creatures still contribute to sneak skill increases\n    Bug #3437: Weather-conditioned dialogue should not play in interiors\n    Bug #3439: Effects cast by summon stick around after their death\n    Bug #3440: Parallax maps looks weird\n    Bug #3443: Class graphic for custom class should be Acrobat\n    Bug #3446: OpenMW segfaults when using Atrayonis's \"Anthology Solstheim: Tomb of the Snow Prince\" mod\n    Bug #3448: After dispelled, invisibility icon is still displayed\n    Bug #3453: First couple of seconds of NPC speech is muted\n    Bug #3455: Portable house mods lock player and npc movement up exiting house.\n    Bug #3456: Equipping an item will undo dispel of constant effect invisibility\n    Bug #3458: Constant effect restore health doesn't work during Wait\n    Bug #3466: It is possible to stack multiple scroll effects of the same type\n    Bug #3471: When two mods delete the same references, many references are not disabled by the engine.\n    Bug #3473: 3rd person camera can be glitched\n    Feature #1424: NPC \"Face\" function\n    Feature #2974: Editor: Multiple Deletion of Subrecords\n    Feature #3044: Editor: Render path grid v2\n    Feature #3362: Editor: Configurable key bindings\n    Feature #3375: Make sun / moon reflections weather dependent\n    Feature #3386: Editor: Edit pathgrid\n\n0.39.0\n------\n\n    Bug #1384: Dark Brotherhood Assassin (and other scripted NPCs?) spawns beneath/inside solid objects\n    Bug #1544: \"Drop\" drops equipped item in a separate stack\n    Bug #1587: Collision detection glitches\n    Bug #1629: Container UI locks up in Vivec at Jeanne's\n    Bug #1771: Dark Brotherhood Assassin oddity in Eight Plates\n    Bug #1827: Unhandled NiTextureEffect in ex_dwrv_ruin30.nif\n    Bug #2089: When saving while swimming in water in an interior cell, you will be spawned under water on loading\n    Bug #2295: Internal texture not showing, nipixeldata\n    Bug #2363: Corpses don't disappear\n    Bug #2369: Respawns should be timed individually\n    Bug #2393: Сharacter is stuck in the tree\n    Bug #2444: [Mod] NPCs from Animated Morrowind appears not using proper animations\n    Bug #2467: Creatures do not respawn\n    Bug #2515: Ghosts in Ibar-Dad spawn stuck in walls\n    Bug #2610: FixMe script still needs to be implemented\n    Bug #2689: Riekling raider pig constantly screams while running\n    Bug #2719: Vivec don't put their hands on the knees with this replacer (Psymoniser Vivec God Replacement NPC Edition v1.0\n    Bug #2737: Camera shaking when side stepping around object\n    Bug #2760: AI Combat Priority Problem - Use of restoration spell instead of attacking\n    Bug #2806: Stack overflow in LocalScripts::getNext\n    Bug #2807: Collision detection allows player to become stuck inside objects\n    Bug #2814: Stairs to Marandus have improper collision\n    Bug #2925: Ranes Ienith will not appear, breaking the Morag Tong and Thieves Guid questlines\n    Bug #3024: Editor: Creator bar in startscript subview does not accept script ID drops\n    Bug #3046: Sleep creature: Velk is spawned half-underground in the Thirr River Valley\n    Bug #3080: Calling aifollow without operant in local script every frame causes mechanics to overheat + log\n    Bug #3101: Regression: White guar does not move\n    Bug #3108: Game Freeze after Killing Diseased Rat in Foreign Quarter Tomb\n    Bug #3124: Bloodmoon Quest - Rite of the Wolf Giver (BM_WolfGiver) – Innocent victim won't turn werewolf\n    Bug #3125: Improper dialogue window behavior when talking to creatures\n    Bug #3130: Some wandering NPCs disappearing, cannot finish quests\n    Bug #3132: Editor: GMST ID named sMake Enchantment is instead named sMake when making new game from scratch\n    Bug #3133: OpenMW and the OpenCS are writting warnings about scripts that use the function GetDisabled.\n    Bug #3135: Journal entry for The Pigrim's Path missing name\n    Bug #3136: Dropped bow is displaced\n    Bug #3140: Editor: OpenMW-CS fails to open newly converted and saved omwaddon file.\n    Bug #3142: Duplicate Resist Magic message\n    Bug #3143: Azura missing her head\n    Bug #3146: Potion effect showing when ingredient effects are not known\n    Bug #3155: When executing chop attack with a spear, hands turn partly invisible\n    Bug #3161: Fast travel from Silt Strider or Boat Ride will break save files made afterwards\n    Bug #3163: Editor: Objects dropped to scene do not always save\n    Bug #3173: Game Crashes After Casting Recall Spell\n    Bug #3174: Constant effect enchantments play spell animation on dead bodies\n    Bug #3175: Spell effects do not wear down when caster dies\n    Bug #3176: NPCs appearing randomly far away from towns\n    Bug #3177: Submerged corpse floats ontop of water when it shouldn't (Widow Vabdas' Deed quest)\n    Bug #3184: Bacola Closcius in Balmora, South Wall Cornerclub spams magic effects if attacked\n    Bug #3207: Editor: New objects do not render\n    Bug #3212: Arrow of Ranged Silence\n    Bug #3213: Looking at Floor After Magical Transport\n    Bug #3220: The number of remaining ingredients in the alchemy window doesn't go down when failing to brew a potion\n    Bug #3222: Falling through the water in Vivec\n    Bug #3223: Crash at the beginning with MOD (The Symphony)\n    Bug #3228: Purple screen when leveling up.\n    Bug #3233: Infinite disposition via MWDialogue::Filter::testDisposition() glitch\n    Bug #3234: Armor mesh stuck on body in inventory menu\n    Bug #3235: Unlike vanilla, OpenMW don't allow statics and activators cast effects on the player.\n    Bug #3238: Not loading cells when using Poorly Placed Object Fix.esm\n    Bug #3248: Editor: Using the \"Next Script\" and \"Previous Script\" buttons changes the record status to \"Modified\"\n    Bug #3258: Woman biped skeleton\n    Bug #3259: No alternating punches\n    Bug #3262: Crash in class selection menu\n    Bug #3279: Load menu: Deleting a savegame makes scroll bar jump to the top\n    Bug #3326: Starting a new game, getting to class selection, then starting another new game temporarily assigns Acrobat class\n    Bug #3327: Stuck in table after loading when character was sneaking when quicksave\n    Feature #652: Editor: GMST verifier\n    Feature #929: Editor: Info record verifier\n    Feature #1279: Editor: Render cell border markers\n    Feature #2482: Background cell loading and caching of loaded cells\n    Feature #2484: Editor: point lighting\n    Feature #2801: Support NIF bump map textures in osg\n    Feature #2926: Editor: Optional line wrap in script editor wrap lines\n    Feature #3000: Editor: Reimplement 3D scene camera system\n    Feature #3035: Editor: Make scenes a drop target for referenceables\n    Feature #3043: Editor: Render cell markers v2\n    Feature #3164: Editor: Instance Selection Menu\n    Feature #3165: Editor: Instance editing mode - move sub mode\n    Feature #3244: Allow changing water Level of Interiors behaving like exteriors\n    Feature #3250: Editor: Use \"Enter\" key instead of clicking \"Create\" button to confirm ID input in Creator Bar\n    Support #3179: Fatal error on startup\n\n0.38.0\n------\n\n    Bug #1699: Guard will continuously run into mudcrab\n    Bug #1934: Saw in Dome of Kasia doesnt harm the player\n    Bug #1962: Rat floats when killed near the door\n    Bug #1963: Kwama eggsacks pulse too fast\n    Bug #2198: NPC voice sound source should be placed at their head\n    Bug #2210: OpenMW installation wizard crashes...\n    Bug #2211: Editor: handle DELE subrecord at the end of a record\n    Bug #2413: ESM error Unknown subrecord in Grandmaster of Hlaalu\n    Bug #2537: Bloodmoon quest Ristaag: Sattir not consistently dying, plot fails to advance; same with Grerid\n    Bug #2697: \"The Swimmer\" moves away after leading you to underwater cave\n    Bug #2724: Loading previous save duplicates containers and harvestables\n    Bug #2769: Inventory doll - Cursor not respecting order of clothes\n    Bug #2865: Scripts silently fail when moving NPCs between cells.\n    Bug #2873: Starting a new game leads to CTD / Fatal Error\n    Bug #2918: Editor: it's not possible to create an omwaddon containing a dot in the file name\n    Bug #2933: Dialog box can't disable a npc if it is in another cell. (Rescue Madura Seran).\n    Bug #2942: atronach sign behavior (spell absorption) changes when trying to receive a blessing at \"shrine of tribunal\"\n    Bug #2952: Enchantment Merchant Items reshuffled EVERY time 'barter' is clicked\n    Bug #2961: ESM Error: Unknown subrecord if Deus Ex Machina mod is loaded\n    Bug #2972: Resurrecting the player via console does not work when health was 0\n    Bug #2986: Projectile weapons work underwater\n    Bug #2988: \"Expected subrecord\" bugs showing up.\n    Bug #2991: Can't use keywords in strings for MessageBox\n    Bug #2993: Tribunal:The Shrine of the Dead – Urvel Dulni can't stop to follow the player.\n    Bug #3008: NIFFile Error while loading meshes with a NiLODNode\n    Bug #3010: Engine: items should sink to the ground when dropped under water\n    Bug #3011: NIFFile Error while loading meshes with a NiPointLight\n    Bug #3016: Engine: something wrong with scripting - crash / fatal error\n    Bug #3020: Editor: verify does not check if given \"item ID\" (as content) for a \"container\" exists\n    Bug #3026: [MOD: Julan Ashlander Companion] Dialogue not triggering correctly\n    Bug #3028: Tooltips for Health, Magicka and Fatigue show in Options menu even when bars aren't visible\n    Bug #3034: Item count check dialogue option doesn't work (Guards accept gold even if you don't have enough)\n    Bug #3036: Owned tooltip color affects spell tooltips incorrrectly\n    Bug #3037: Fatal error loading old ES_Landscape.esp in Store<ESM::LandTexture>::search\n    Bug #3038: Player sounds come from underneath\n    Bug #3040: Execution of script failed: There is a message box already\n    Bug #3047: [MOD: Julan Ashlander Companion] Scripts KS_Bedscript or KS_JulanNight not working as intended\n    Bug #3048: Fatal Error\n    Bug #3051: High field of view results in first person rendering glitches\n    Bug #3053: Crash on new game at character class selection\n    Bug #3058: Physiched sleeves aren't rendered correctly.\n    Bug #3060: NPCs use wrong landing sound\n    Bug #3062: Mod support regression: Andromeda's fast travel.\n    Bug #3063: Missing Journal Textures without Tribunal and Bloodmoon installed\n    Bug #3077: repeated aifollow causes the distance to stack\n    Bug #3078: Creature Dialogues not showing when certain Function/Conditions are required.\n    Bug #3082: Crash when entering Holamayan Monastery with mesh replacer installed\n    Bug #3086: Party at Boro's House – Creature with Class don't talk under OpenMW\n    Bug #3089: Dreamers spawn too soon\n    Bug #3100: Certain controls erroneously work as a werewolf\n    Bug #3102: Multiple unique soultrap spell sources clone souls.\n    Bug #3105: Summoned creatures and objects disappear at midnight\n    Bug #3112: gamecontrollerdb file creation with wrong extension\n    Bug #3116: Dialogue Function \"Same Race\" is avoided\n    Bug #3117: Dialogue Bug: Choice conditions are tested when not in a choice\n    Bug #3118: Body Parts are not rendered when used in a pose.\n    Bug #3122: NPC direction is reversed during sneak awareness check\n    Feature #776: Sound effects from one direction don't necessarily affect both speakers in stereo\n    Feature #858: Different fov settings for hands and the game world\n    Feature #1176: Handle movement of objects between cells\n    Feature #2507: Editor: choosing colors for syntax highlighting\n    Feature #2867: Editor: hide script error list when there are no errors\n    Feature #2885: Accept a file format other than nif\n    Feature #2982: player->SetDelete 1 results in: PC can't move, menu can be opened\n    Feature #2996: Editor: make it possible to preset the height of the script check area in a script view\n    Feature #3014: Editor: Tooltips in 3D scene\n    Feature #3064: Werewolf field of view\n    Feature #3074: Quicksave indicator\n    Task #287: const version of Ptr\n    Task #2542: Editor: redo user settings system\n\n0.37.0\n------\n\n    Bug #385: Light emitting objects have a too short distance of activation\n    Bug #455: Animation doesn't resize creature's bounding box\n    Bug #602: Only collision model is updated when modifying objects trough console\n    Bug #639: Sky horizon at nighttime\n    Bug #672: incorrect trajectory of the moons\n    Bug #814: incorrect NPC width\n    Bug #827: Inaccurate raycasting for dead actors\n    Bug #996: Can see underwater clearly when at right height/angle\n    Bug #1317: Erene Llenim in Seyda Neen does not walk around\n    Bug #1330: Cliff racers fail to hit the player\n    Bug #1366: Combat AI can't aim down (in order to hit small creatures)\n    Bug #1511: View distance while under water is much too short\n    Bug #1563: Terrain positioned incorrectly and appears to vibrate in far-out cells\n    Bug #1612: First person models clip through walls\n    Bug #1647: Crash switching from full screen to windows mode - D3D9\n    Bug #1650: No textures with directx on windows\n    Bug #1730: Scripts names starting with digit(s) fail to compile\n    Bug #1738: Socucius Ergalla's greetings are doubled during the tutorial\n    Bug #1784: First person weapons always in the same position\n    Bug #1813: Underwater flora lighting up entire area.\n    Bug #1871: Handle controller extrapolation flags\n    Bug #1921: Footstep frequency and velocity do not immediately update when speed attribute changes\n    Bug #2001: OpenMW crashes on start with OpenGL 1.4 drivers\n    Bug #2014: Antialiasing setting does nothing on Linux\n    Bug #2037: Some enemies attack the air when spotting the player\n    Bug #2052: NIF rotation matrices including scales are not supported\n    Bug #2062: Crank in Old Mournhold: Forgotten Sewer turns about the wrong axis\n    Bug #2111: Raindrops in front of fire look wrong\n    Bug #2140: [OpenGL] Water effects, flames and parts of creatures solid black when observed through brazier flame\n    Bug #2147: Trueflame and Hopesfire flame effects not properly aligned with blade\n    Bug #2148: Verminous fabricants have little coloured box beneath their feet\n    Bug #2149: Sparks in Clockwork City should bounce off the floor\n    Bug #2151: Clockwork City dicer trap doesn't activate when you're too close\n    Bug #2186: Mini map contains scrambled pixels that cause the mini map to flicker\n    Bug #2187: NIF file with more than 255 NiBillboardNodes does not load\n    Bug #2191: Editor: Crash when trying to view cell in render view in OpenCS\n    Bug #2270: Objects flicker transparently\n    Bug #2280: Latest 32bit windows build of openmw runns out of vram\n    Bug #2281: NPCs don't scream when they die\n    Bug #2286: Jumping animation restarts when equipping mid-air\n    Bug #2287: Weapon idle animation stops when turning\n    Bug #2355: Light spell doesn't work in 1st person view\n    Bug #2362: Lantern glas opaque to flame effect from certain viewing angles\n    Bug #2364: Light spells are not as bright as in Morrowind\n    Bug #2383: Remove the alpha testing override list\n    Bug #2436: Crash on entering cell \"Tower of Tel Fyr, Hall of Fyr\"\n    Bug #2457: Player followers should not report crimes\n    Bug #2458: crash in some fighting situations\n    Bug #2464: Hiding an emitter node should make that emitter stop firing particles\n    Bug #2466: Can't load a save created with OpenMW-0.35.0-win64\n    Bug #2468: music from title screen continues after loading savegame\n    Bug #2494: Map not consistent between saves\n    Bug #2504: Dialog scroll should always start at the top\n    Bug #2506: Editor: Undo/Redo shortcuts do not work in script editor\n    Bug #2513: Mannequins in mods appear as dead bodies\n    Bug #2524: Editor: TopicInfo \"custom\" condition section is missing\n    Bug #2540: Editor: search and verification result table can not be sorted by clicking on the column names\n    Bug #2543: Editor: there is a problem with spell effects\n    Bug #2544: Editor fails to save NPC information correctly.\n    Bug #2545: Editor: delete record in Objects (referenceables) table messes up data\n    Bug #2546: Editor: race base attributes and skill boni are not displayed, thus not editable\n    Bug #2547: Editor: some NPC data is not displayed, thus not editable\n    Bug #2551: Editor: missing data in cell definition\n    Bug #2553: Editor: value filter does not work for float values\n    Bug #2555: Editor: undo leaves the record status as Modified\n    Bug #2559: Make Detect Enchantment marks appear on top of the player arrow\n    Bug #2563: position consoling npc doesn't work without cell reload\n    Bug #2564: Editor: Closing a subview from code does not clean up properly and will lead to crash on opening the next subview\n    Bug #2568: Editor: Setting default window size is ignored\n    Bug #2569: Editor: saving from an esp to omwaddon file results in data loss for TopicInfo\n    Bug #2575: Editor: Deleted record (with Added (ModifiedOnly) status) remains in the Dialog SubView\n    Bug #2576: Editor: Editor doesn't scroll to a newly opened subview, when ScrollBar Only mode is active\n    Bug #2578: Editor: changing Level or Reputation of an NPC crashes the editor\n    Bug #2579: Editor: filters not updated when adding or cloning records\n    Bug #2580: Editor: omwaddon makes OpenMW crash\n    Bug #2581: Editor: focus problems in edit subviews single- and multiline input fields\n    Bug #2582: Editor: object verifier should check for non-existing scripts being referenced\n    Bug #2583: Editor: applying filter to TopicInfo on mods that have added dialouge makes the Editor crash\n    Bug #2586: Editor: some dialogue only editable items do not refresh after undo\n    Bug #2588: Editor: Cancel button exits program\n    Bug #2589: Editor: Regions table - mapcolor does not change correctly\n    Bug #2591: Placeatme - spurious 5th parameter raises error\n    Bug #2593: COC command prints multiple times when GUI is hidden\n    Bug #2598: Editor: scene view of instances has to be zoomed out to displaying something - center camera instance please\n    Bug #2607: water behind an invisible NPC becomes invisible as well\n    Bug #2611: Editor: Sort problem in Objects table when few nested rows are added\n    Bug #2621: crash when a creature has no model\n    Bug #2624: Editor: missing columns in tables\n    Bug #2627: Character sheet doesn't properly update when backing out of CharGen\n    Bug #2642: Editor: endif without if - is not reported as error when \"verify\" was executed\n    Bug #2644: Editor: rebuild the list of available content files when opening the open/new dialogues\n    Bug #2656: OpenMW & OpenMW-CS: setting \"Flies\" flag for ghosts has no effect\n    Bug #2659: OpenMW & OpenMW-CS: savegame load fail due to script attached to NPCs\n    Bug #2668: Editor: reputation value in the input field is not stored\n    Bug #2696: Horkers use land idle animations under water\n    Bug #2705: Editor: Sort by Record Type (Objects table) is incorrect\n    Bug #2711: Map notes on an exterior cell that shows up with a map marker on the world map do not show up in the tooltip for that cell's marker on the world map\n    Bug #2714: Editor: Can't reorder rows with the same topic in different letter case\n    Bug #2720: Head tracking for creatures not implemented\n    Bug #2722: Alchemy should only include effects shared by at least 2 ingredients\n    Bug #2723: \"ori\" console command is not working\n    Bug #2726: Ashlanders in front of Ghostgate start wandering around\n    Bug #2727: ESM writer does not handle encoding when saving the TES3 header\n    Bug #2728: Editor: Incorrect position of an added row in Info tables\n    Bug #2731: Editor: Deleting a record triggers a Qt warning\n    Bug #2733: Editor: Undo doesn't restore the Modified status of a record when a nested data is changed\n    Bug #2734: Editor: The Search doesn't work\n    Bug #2738: Additive moon blending\n    Bug #2746: NIF node names should be case insensitive\n    Bug #2752: Fog depth/density not handled correctly\n    Bug #2753: Editor: line edit in dialogue subview tables shows after a single click\n    Bug #2755: Combat AI changes target too frequently\n    Bug #2761: Can't attack during block animations\n    Bug #2764: Player doesn't raise arm in 3rd person for weathertype 9\n    Bug #2768: Current screen resolution not selected in options when starting OpenMW\n    Bug #2773: Editor: Deleted scripts are editable\n    Bug #2776: ordinators still think I'm wearing their helm even though Khajiit and argonians can't\n    Bug #2779: Slider bars continue to move if you don't release mouse button\n    Bug #2781: sleep interruption is a little off (is this an added feature?)\n    Bug #2782: erroneously able to ready weapon/magic (+sheathe weapon/magic) while paralyzed\n    Bug #2785: Editor: Incorrect GMSTs for newly created omwgame files\n    Bug #2786: Kwama Queen head is inverted under OpenMW\n    Bug #2788: additem and removeitem incorrect gold behavior\n    Bug #2790: --start doesn't trace down\n    Bug #2791: Editor: Listed attributes and skill should not be based on number of NPC objects.\n    Bug #2792: glitched merchantile/infinite free items\n    Bug #2794: Need to ignore quotes in names of script function\n    Bug #2797: Editor: Crash when removing the first row in a nested table\n    Bug #2800: Show an error message when S3TC support is missing\n    Bug #2811: Targetted Open spell effect persists.\n    Bug #2819: Editor: bodypart's race filter not displayed correctly\n    Bug #2820: Editor: table sorting is inverted\n    Bug #2821: Editor: undo/redo command labels are incorrect\n    Bug #2826: locking beds that have been locked via magic psuedo-freezes the game\n    Bug #2830: Script compiler does not accept IDs as instruction/functions arguments if the ID is also a keyword\n    Bug #2832: Cell names are not localized on the world map\n    Bug #2833: [cosmetic] Players swimming at water's surface are slightly too low.\n    Bug #2840: Save/load menu is not entirely localized\n    Bug #2853: [exploit/bug] disintegrate weapon incorrectly applying to lockpicks, probes. creates unbreakable lockpicks\n    Bug #2855: Mouse wheel in journal is not disabled by \"Options\" panel.\n    Bug #2856: Heart of Lorkhan doesn't visually respond to attacks\n    Bug #2863: Inventory highlights wrong category after load\n    Bug #2864: Illuminated Order 1.0c Bug – The teleport amulet is not placed in the PC inventory.\n    Bug #2866: Editor: use checkbox instead of combobox for boolean values\n    Bug #2875: special cases of fSleepRandMod not behaving properly.\n    Bug #2878: Editor: Verify reports \"creature has non-positive level\" but there is no level setting\n    Bug #2879: Editor: entered value of field \"Buys *\" is not saved for a creature\n    Bug #2880: OpenMW & OpenMW-CS: having a scale value of 0.000 makes the game laggy\n    Bug #2882: Freeze when entering cell \"Guild of Fighters (Ald'ruhn)\" after dropping some items inside\n    Bug #2883: game not playable if mod providing a spell is removed but the list of known spells still contains it\n    Bug #2884: NPC chats about wrong player race\n    Bug #2886: Adding custom races breaks existing numbering of PcRace\n    Bug #2888: Editor: value entered in \"AI Wander Idle\" is not kept\n    Bug #2889: Editor: creatures made with the CS (not cloned) are always dead\n    Bug #2890: Editor: can't make NPC say a specific \"Hello\" voice-dialouge\n    Bug #2893: Editor: making a creature use textual dialogue doesn't work.\n    Bug #2901: Editor: gold for trading can not be set for creatures\n    Bug #2907: looking from uderwater part of the PC that is below the surface looks like it would be above the water\n    Bug #2914: Magicka not recalculated on character generation\n    Bug #2915: When paralyzed, you can still enter and exit sneak\n    Bug #2917: chameleon does not work for creatures\n    Bug #2927: Editor: in the automatic script checker local variable caches are not invalidated/updated on modifications of other scripts\n    Bug #2930: Editor: AIWander Idle can not be set for a creature\n    Bug #2932: Editor: you can add rows to \"Creature Attack\" but you can not enter values\n    Bug #2938: Editor: Can't add a start script.\n    Bug #2944: Spell chance for power to show as 0 on hud when used\n    Bug #2953: Editor: rightclick in an empty place in the menu bar shows an unnamed checkbox\n    Bug #2956: Editor: freezes while editing Filter\n    Bug #2959: space character in field enchantment (of an amulet) prevents rendering of surroundings\n    Bug #2962: OpenMW: Assertion `it != invStore.end()' failed\n    Bug #2964: Recursive script execution can corrupt script runtime data\n    Bug #2973: Editor: placing a chest in the game world and activating it heavily blurrs the character portrait\n    Bug #2978: Editor: Cannot edit alchemy ingredient properties\n    Bug #2980: Editor: Attribute and Skill can be selected for spells that do not require these parameters, leading to non-functional spells\n    Bug #2990: Compiling a script with warning mode 2 and enabled error downgrading leads to infinite recursion\n    Bug #2992: [Mod: Great House Dagoth] Killing Dagoth Gares freezes the game\n    Bug #3007: PlaceItem takes radians instead of degrees + angle reliability\n    Feature #706: Editor: Script Editor enhancements\n    Feature #872: Editor: Colour values in tables\n    Feature #880: Editor: ID auto-complete\n    Feature #928: Editor: Partial sorting in info tables\n    Feature #942: Editor: Dialogue for editing/viewing content file meta information\n    Feature #1057: NiStencilProperty\n    Feature #1278: Editor: Mouse picking in worldspace widget\n    Feature #1280: Editor: Cell border arrows\n    Feature #1401: Editor: Cloning enhancements\n    Feature #1463: Editor: Fine grained configuration of extended revert/delete commands\n    Feature #1591: Editor: Make fields in creation bar drop targets where applicable\n    Feature #1998: Editor: Magic effect record verifier\n    Feature #1999: Editor Sound Gen record verifier\n    Feature #2000: Editor: Pathgrid record verifier\n    Feature #2528: Game Time Tracker\n    Feature #2534: Editor: global search does not auomatically focus the search input field\n    Feature #2535: OpenMW: allow comments in openmw.cfg\n    Feature #2541: Editor: provide a go to the very bottom button for TopicInfo and JournalInfo\n    Feature #2549: Editor: add a horizontal slider to scroll between opened tables\n    Feature #2558: Editor: provide a shortcut for closing the subview that has the focus\n    Feature #2565: Editor: add context menu for dialogue sub view fields with an item matching \"Edit 'x'\" from the table subview context menu\n    Feature #2585: Editor: Ignore mouse wheel input for numeric values unless the respective widget has the focus\n    Feature #2620: Editor: make the verify-view refreshable\n    Feature #2622: Editor: Make double click behaviour in result tables configurable (see ID tables)\n    Feature #2717: Editor: Add severity column to report tables\n    Feature #2729: Editor: Various dialogue button bar improvements\n    Feature #2739: Profiling overlay\n    Feature #2740: Resource manager optimizations\n    Feature #2741: Make NIF files into proper resources\n    Feature #2742: Use the skinning data in NIF files as-is\n    Feature #2743: Small feature culling\n    Feature #2744: Configurable near clip distance\n    Feature #2745: GUI scaling option\n    Feature #2747: Support anonymous textures\n    Feature #2749: Loading screen optimizations\n    Feature #2751: Character preview optimization\n    Feature #2804: Editor: Merge Tool\n    Feature #2818: Editor: allow copying a record ID to the clipboard\n    Feature #2946: Editor: add script line number in results of search\n    Feature #2963: Editor: Mouse button bindings in 3D scene\n    Feature #2983: Sun Glare fader\n    Feature #2999: Scaling of journal and books\n    Task #2665: Support building with Qt5\n    Task #2725: Editor: Remove Display_YesNo\n    Task #2730: Replace hardcoded column numbers in SimpleDialogueSubView/DialogueSubView\n    Task #2750: Bullet shape instancing optimization\n    Task #2793: Replace grid size setting with half grid size setting\n    Task #3003: Support FFMPEG 2.9 (Debian request)\n\n0.36.1\n------\n\n    Bug #2590: Start scripts not added correctly\n\n0.36.0\n------\n\n    Bug #923: Editor: Operations-Multithreading is broken\n    Bug #1317: Erene Llenim in Seyda Neen does not walk around\n    Bug #1405: Water rendering glitch near Seyda Neen lighthouse\n    Bug #1621: \"Error Detecting Morrowind Installation\" in the default directory\n    Bug #2216: Creating a clone of the player stops you moving.\n    Bug #2387: Casting bound weapon spell doesn't switch to \"ready weapon\" mode\n    Bug #2407: Default to (0, 0) when \"unknown cell\" is encountered.\n    Bug #2411: enchanted item charges don't update/refresh if spell list window is pinned open\n    Bug #2428: Editor: cloning / creating new container class results in invalid omwaddon file - openmw-0.35\n    Bug #2429: Editor - cloning omits some values or sets different values than the original has\n    Bug #2430: NPC with negative fatigue don't fall (LGNPC Vivec, Foreign Quarter v2.21)\n    Bug #2432: Error on startup with Uvirith's Legacy enabled\n    Bug #2435: Editor: changed entries in the objects window are not shown as such\n    Bug #2437: Editor: changing an entry of a container/NPC/clothing/ingredient/globals will not be saved in the omwaddon file\n    Bug #2447: Editor doesn't save terrain information\n    Bug #2451: Editor not listing files with accented characters\n    Bug #2453: Chargen: sex, race and hair sliders not initialized properly\n    Bug #2459: Minor terrain clipping through statics due to difference in triangle alignment\n    Bug #2461: Invisible sound mark has collision in Sandus Ancestral Tomb\n    Bug #2465: tainted gold stack\n    Bug #2475: cumulative stacks of 100 point fortify skill speechcraft boosts do not apply correctly\n    Bug #2498: Editor: crash when issuing undo command after the table subview is closed\n    Bug #2500: Editor: object table - can't undo delete record\n    Bug #2518: OpenMW detect spell returns false positives\n    Bug #2521: NPCs don't react to stealing when inventory menu is open.\n    Bug #2525: Can't click on red dialogue choice [rise of house telvanni][60fffec]\n    Bug #2530: GetSpellEffects not working as in vanilla\n    Bug #2557: Crash on first launch after choosing \"Run installation wizard\"\n    Feature #139: Editor: Global Search & Replace\n    Feature #1219: Editor: Add dialogue mode only columns\n    Feature #2024: Hotkey for hand to hand (i.e. unequip any weapon)\n    Feature #2119: \"Always Sneak\" key bind\n    Feature #2262: Editor: Handle moved instances\n    Feature #2425: Editor: Add start script table\n    Feature #2426: Editor: start script record verifier\n    Feature #2480: Launcher: Multiselect entries in the Data Files list\n    Feature #2505: Editor: optionally show a line number column in the script editor\n    Feature #2512: Editor: Offer use of monospace fonts in the script editor as an option\n    Feature #2514: Editor: focus on ID input field on clone/add\n    Feature #2519: it is not possible to change icons that appear on the map after casting the Detect <animal | enchantment | key> spells\n    Task #2460: OS X: Use Application Support directory as user data path\n    Task #2516: Editor: Change References / Referenceables terminology\n\n0.35.1\n------\n\n    Bug #781: incorrect trajectory of the sun\n    Bug #1079: Wrong starting position in \"Character Stuff Wonderland\"\n    Bug #1443: Repetitive taking of a stolen object is repetitively considered as a crime\n    Bug #1533: Divine Intervention goes to the wrong place.\n    Bug #1714: No visual indicator for time passed during training\n    Bug #1916: Telekinesis does not allow safe opening of traps\n    Bug #2227: Editor: addon file name inconsistency\n    Bug #2271: Player can melee enemies from water with impunity\n    Bug #2275: Objects with bigger scale move further using Move script\n    Bug #2285: Aryon's Dominator enchantment does not work properly\n    Bug #2290: No punishment for stealing gold from owned containers\n    Bug #2328: Launcher does not respond to Ctrl+C\n    Bug #2334: Drag-and-drop on a content file in the launcher creates duplicate items\n    Bug #2338: Arrows reclaimed from corpses do not stack sometimes\n    Bug #2344: Launcher - Settings importer running correctly?\n    Bug #2346: Launcher - Importing plugins into content list screws up the load order\n    Bug #2348: Mod: H.E.L.L.U.V.A. Handy Holdables does not appear in the content list\n    Bug #2353: Detect Animal detects dead creatures\n    Bug #2354: Cmake does not respect LIB_SUFFIX\n    Bug #2356: Active magic set inactive when switching magic items\n    Bug #2361: ERROR: ESM Error: Previous record contains unread bytes\n    Bug #2382: Switching spells with \"next spell\" or \"previous spell\" while holding shift promps delete spell dialog\n    Bug #2388: Regression: Can't toggle map on/off\n    Bug #2392: MOD Shrines - Restore Health and Cancel Options adds 100 health points\n    Bug #2394: List of Data Files tab in openmw-laucher needs to show all content files.\n    Bug #2402: Editor: skills saved incorrectly\n    Bug #2408: Equipping a constant effect Restore Health/Magicka/Fatigue item will permanently boost the stat it's restoring\n    Bug #2415: It is now possible to fall off the prison ship into the water when starting a new game\n    Bug #2419: MOD MCA crash to desktop\n    Bug #2420: Game crashes when character enters a certain area\n    Bug #2421: infinite loop when using cycle weapon without having a weapon\n    Feature #2221: Cannot dress dead NPCs\n    Feature #2349: Check CMake sets correct MSVC compiler settings for release build.\n    Feature #2397: Set default values for global mandatory records.\n    Feature #2412: Basic joystick support\n\n0.35.0\n------\n\n    Bug #244: Clipping/static in relation to the ghostgate/fence sound.\n    Bug #531: Missing transparent menu items\n    Bug #811: Content Lists in openmw.cfg are overwritten\n    Bug #925: OpenCS doesn't launch because it thinks its already started\n    Bug #969: Water shader strange behaviour on AMD card\n    Bug #1049: Partially highlighted word in dialogue may cause incorrect line break\n    Bug #1069: omwlauncher.exe crashes due to file lock\n    Bug #1192: It is possible to jump on top of hostile creatures in combat\n    Bug #1342: Loud ambient sounds\n    Bug #1431: Creatures can climb the player\n    Bug #1605: Guard in CharGen doesn't turn around to face you when reaching stairs\n    Bug #1624: Moon edges don't transition properly\n    Bug #1634: Items dropped by PC have collision\n    Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults?\n    Bug #1638: Cannot climb staircases\n    Bug #1648: Enchanted equipment badly handled at game reload\n    Bug #1663: Crash when casting spell at enemy near you\n    Bug #1683: Scale doesn't apply to animated collision nodes\n    Bug #1702: Active enchanted item forgotten\n    Bug #1730: Scripts names starting with digit(s) fail to compile\n    Bug #1743: Moons are transparent\n    Bug #1745: Shadows crash: Assertion `mEffects.empty()' failed.\n    Bug #1785: Can't equip two-handed weapon and shield\n    Bug #1809: Player falls too easily\n    Bug #1825: Sword of Perithia can´t run in OpenMW\n    Bug #1899: The launcher resets any alterations you´ve made in the mod list order,\n    Bug #1964: Idle voices/dialogs not triggered correctly\n    Bug #1980: Please, change default click behavior in OpenMW Launchers Data Files list\n    Bug #1984: Vampire corpses standing up when looting the first item\n    Bug #1985: Calm spell does nothing\n    Bug #1986: Spell name lights up on mouseover but spell cost does not\n    Bug #1989: Tooltip still shown when menu toggled off\n    Bug #2010: Raindrops Displayed While Underwater\n    Bug #2023: Walking into plants causes massive framedrop\n    Bug #2031: [MOD: Shrines - Restore Health and Cancel Options]: Restore health option doesn't work\n    Bug #2039: Lake Fjalding pillar of fire not rendered\n    Bug #2040: AI_follow should stop further from the target\n    Bug #2076: Slaughterfish AI\n    Bug #2077: Direction of long jump can be changed much more than it is possible in vanilla\n    Bug #2078: error during rendering: Object '' not found (const)\n    Bug #2105: Lockpicking causes screen sync glitch\n    Bug #2113: [MOD: Julan Ashlander Companion] Julan does not act correctly within the Ghostfence.\n    Bug #2123: Window glow mod: Collision issues\n    Bug #2133: Missing collision for bridges in Balmora when using Morrowind Rebirth 2.81\n    Bug #2135: Casting a summon spell while the summon is active does not reset the summon.\n    Bug #2144: Changing equipment will unequip drawn arrows/bolts\n    Bug #2169: Yellow on faces when using opengl renderer and mods from overhaul on windows\n    Bug #2175: Pathgrid mods do not overwrite the existing pathgrid\n    Bug #2176: Morrowind -Russian localization end add-on ChaosHeart. Error in framelistener;object ;frenzying toush; not found <const>\n    Bug #2181: Mod Morrowind crafting merchants die.\n    Bug #2182: mods changing skill progression double the bonus for class specialization\n    Bug #2183: Editor: Skills \"use value\" only allows integer between 0 and 99\n    Bug #2184: Animated Morrowind Expanded produces an error on Open MW Launch\n    Bug #2185: Conditional Operator formats\n    Bug #2193: Quest: Gateway Ghost\n    Bug #2194: Cannot summon multiples of the same creature\n    Bug #2195: Pathgrid in the (0,0) exterior cell not loaded\n    Bug #2200: Outdoor NPCs can stray away and keep walking into a wall\n    Bug #2201: Creatures do not receive fall damage\n    Bug #2202: The enchantment the item can hold is calculated incorrectly\n    Bug #2203: Having the mod Living Cities of Vvardenfall running causes the game world to fail to load after leaving the prison ship\n    Bug #2204: Abot's Water Life - Book rendered incorrectly\n    Bug #2205: sound_waterfall script no longer compiles\n    Bug #2206: Dialogue script fails to compile (extra .)\n    Bug #2207: Script using – instead of - character does not compile\n    Bug #2208: Failing dialogue scripts in french Morrowind.esm\n    Bug #2214: LGNPC Vivec Redoran 1.62 and The King Rat (Size and inventory Issues)\n    Bug #2215: Beast races can use enchanted boots\n    Bug #2218: Incorrect names body parts in 3D models for open helmet with skinning\n    Bug #2219: Orcs in Ghorak Manor in Caldera don't attack if you pick their pockets.\n    Bug #2220: Chargen race preview head incorrect orientation\n    Bug #2223: Reseting rock falling animation\n    Bug #2224: Fortify Attribute effects do not stack when Spellmaking.\n    Bug #2226: OpenCS pseudo-crash\n    Bug #2230: segfaulting when entering Ald'ruhn with a specific mod: \"fermeture la nuit\" (closed by night)\n    Bug #2233: Area effect spells on touch do not have the area effect\n    Bug #2234: Dwarven Crossbow clips through the ground when dropped\n    Bug #2235: class SettingsBase<> reverses the order of entries with multiple keys.\n    Bug #2236: Weird two handed longsword + torch interaction\n    Bug #2237: Shooting arrows while sneaking do not agro\n    Bug #2238: Bipedal creatures not using weapons are not handled properly\n    Bug #2245: Incorrect topic highlighting in HT_SpyBaladas quest\n    Bug #2252: Tab completion incomplete for places using COC from the console.\n    Bug #2255: Camera reverts to first person on load\n    Bug #2259: enhancement: the save/load progress bar is not very progressive\n    Bug #2263: TogglePOV can not be bound to Alt key\n    Bug #2267: dialogue disabling via mod\n    Bug #2268: Highlighting Files with load order problems in Data Files tab of Launcher\n    Bug #2276: [Mod]ShotN issues with Karthwasten\n    Bug #2283: Count argument for PlaceAt functions not working\n    Bug #2284: Local map notes should be visible on door marker leading to the cell with the note\n    Bug #2293: There is a graphical glitch at the end of the spell's animation in 3rd Person (looking over the shoulder) view\n    Bug #2294: When using Skyrim UI Overhaul, the tops of pinnable menus are invisible\n    Bug #2302: Random leveled items repeat way too often in a single dungeon\n    Bug #2306: Enchanted arrows should not be retrievable from corpses\n    Bug #2308: No sound effect when drawing the next throwing knife\n    Bug #2309: Guards chase see the player character even if they're invisible\n    Bug #2319: Inverted controls and other issues after becoming a vampire\n    Bug #2324: Spells cast when crossing cell border are imprinted on the local map\n    Bug #2330: Actors with Drain Health effect retain health after dying\n    Bug #2331: tgm (god mode) won't allow the player to cast spells if the player doesn't have enough mana\n    Bug #2332: Error in framelistener: Need a skeleton to attach the arrow to\n    Feature #114: ess-Importer\n    Feature #504: Editor: Delete selected rows from result windows\n    Feature #1024: Addition of remaining equipping hotkeys\n    Feature #1067: Handle NIF interpolation type 4 (XYZ_ROTATION_KEY)\n    Feature #1125: AI fast-forward\n    Feature #1228: Drowning while knocked out\n    Feature #1325: Editor: Opening window and User Settings window cleanup\n    Feature #1537: Ability to change the grid size from 3x3 to 5x5 (or more with good pc)\n    Feature #1546: Leveled list script functions\n    Feature #1659: Test dialogue scripts in --script-all\n    Feature #1720: NPC lookAt controller\n    Feature #2178: Load initial particle system state from NIF files\n    Feature #2197: Editor: When clicking on a script error in the report window set cursor in script editor to the respective line/column\n    Feature #2261: Warn when loading save games with mod mismatch\n    Feature #2313: ess-Importer: convert global map exploration overlay\n    Feature #2318: Add commandline option to load a save game\n    Task #810: Rename \"profile\" to \"content list\"\n    Task #2196: Label local/global openmw.cfg files via comments\n\n0.34.0\n------\n\n    Bug #904: omwlauncher doesn't allow installing Tribunal and Bloodmoon if only MW is installed\n    Bug #986: Launcher: renaming profile names is broken\n    Bug #1061: \"Browse to CD...\" launcher crash\n    Bug #1135: Launcher crashes if user does not have write permission\n    Bug #1231: Current installer in launcher does not correctly import russian Morrowind.ini settings from setup.inx\n    Bug #1288: Fix the Alignment of the Resolution Combobox\n    Bug #1343: BIK videos occasionally out of sync with audio\n    Bug #1684: Morrowind Grass Mod graphical glitches\n    Bug #1734: NPC in fight with invisible/sneaking player\n    Bug #1982: Long class names are cut off in the UI\n    Bug #2012: Editor: OpenCS script compiler sometimes fails to find IDs\n    Bug #2015: Running while levitating does not affect speed but still drains fatigue\n    Bug #2018: OpenMW don´t reset modified cells to vanilla when a plugin is deselected and don´t apply changes to cells already visited.\n    Bug #2045: ToggleMenus command should close dialogue windows\n    Bug #2046: Crash: light_de_streetlight_01_223\n    Bug #2047: Buglamp tooltip minor correction\n    Bug #2050: Roobrush floating texture bits\n    Bug #2053: Slaves react negatively to PC picking up slave's bracers\n    Bug #2055: Dremora corpses use the wrong model\n    Bug #2056: Mansilamat Vabdas's corpse is floating in the water\n    Bug #2057: \"Quest: Larius Varro Tells A Little Story\": Bounty not completely removed after finishing quest\n    Bug #2059: Silenced enemies try to cast spells anyway\n    Bug #2060: Editor: Special case implementation for top level window with single sub-window should be optional\n    Bug #2061: Editor: SubView closing that is not directly triggered by the user isn't handled properly\n    Bug #2063: Tribunal: Quest 'The Warlords' doesn't work\n    Bug #2064: Sneak attack on hostiles causes bounty\n    Bug #2065: Editor: Qt signal-slot error when closing a dialogue subview\n    Bug #2070: Loading ESP in OpenMW works but fails in OpenCS\n    Bug #2071: CTD in 0.33\n    Bug #2073: Storm atronach animation stops now and then\n    Bug #2075: Molag Amur Region, Map shows water on solid ground\n    Bug #2080: game won't work with fair magicka regen\n    Bug #2082: NPCs appear frozen or switched off after leaving and quickly reentering a cell\n    Bug #2088: OpenMW is unable to play OGG files.\n    Bug #2093: Darth Gares talks to you in Ilunibi even when he's not there, screwing up the Main Quests\n    Bug #2095: Coordinate and rotation editing in the Reference table does not work.\n    Bug #2096: Some overflow fun and bartering exploit\n    Bug #2098: [D3D] Game crash on maximize\n    Bug #2099: Activate, player seems not to work\n    Bug #2104: Only labels are sensitive in buttons\n    Bug #2107: \"Slowfall\" effect is too weak\n    Bug #2114: OpenCS doesn't load an ESP file full of errors even though Vanilla MW Construction Set can\n    Bug #2117: Crash when encountering bandits on opposite side of river from the egg mine south of Balmora\n    Bug #2124: [Mod: Baldurians Transparent Glass Amor] Armor above head\n    Bug #2125: Unnamed NiNodes in weapons problem in First Person\n    Bug #2126: Dirty dialog script in tribunal.esm causing bug in Tribunal MQ\n    Bug #2128: Crash when picking character's face\n    Bug #2129: Disable the third-person zoom feature by default\n    Bug #2130: Ash storm particles shown too long during transition to clear sky\n    Bug #2137: Editor: exception caused by following the Creature column of a SoundGen record\n    Bug #2139: Mouse movement should be ignored during intro video\n    Bug #2143: Editor: Saving is broken\n    Bug #2145: OpenMW - crash while exiting x64 debug build\n    Bug #2152: You can attack Almalexia during her final monologue\n    Bug #2154: Visual effects behave weirdly after loading/taking a screenshot\n    Bug #2155: Vivec has too little magicka\n    Bug #2156: Azura's spirit fades away too fast\n    Bug #2158: [Mod]Julan Ashlander Companion 2.0: Negative magicka\n    Bug #2161: Editor: combat/magic/stealth values of creature not displayed correctly\n    Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon.\n    Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly.\n    Bug #2170: Mods using conversations to update PC inconsistant\n    Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources\n    Bug #2212: Crash or unexpected behavior while closing OpenCS cell render window on OS X\n    Feature #238: Add UI to run INI-importer from the launcher\n    Feature #854: Editor: Add user setting to show status bar\n    Feature #987: Launcher: first launch instructions for CD need to be more explicit\n    Feature #1232: There is no way to set the \"encoding\" option using launcher UI.\n    Feature #1281: Editor: Render cell markers\n    Feature #1918: Editor: Functionality for Double-Clicking in Tables\n    Feature #1966: Editor: User Settings dialogue grouping/labelling/tooltips\n    Feature #2097: Editor: Edit position of references in 3D scene\n    Feature #2121: Editor: Add edit mode button to scene toolbar\n    Task #1965: Editor: Improve layout of user settings dialogue\n\n0.33.1\n------\n\n    Bug #2108: OpenCS fails to build\n\n0.33.0\n------\n\n    Bug #371: If console assigned to ` (probably to any symbolic key), \"`\" symbol will be added to console every time it closed\n    Bug #1148: Some books'/scrolls' contents are displayed incorrectly\n    Bug #1290: Editor: status bar is not updated when record filter is changed\n    Bug #1292: Editor: Documents are not removed on closing the last view\n    Bug #1301: Editor: File->Exit only checks the document it was issued from.\n    Bug #1353: Bluetooth on with no speaker connected results in significantly longer initial load times\n    Bug #1436: NPCs react from too far distance\n    Bug #1472: PC is placed on top of following NPC when changing cell\n    Bug #1487: Tall PC can get stuck in staircases\n    Bug #1565: Editor: Subviews are deleted on shutdown instead when they are closed\n    Bug #1623: Door marker on Ghorak Manor's balcony makes PC stuck\n    Bug #1633: Loaddoor to Sadrith Mora, Telvanni Council House spawns PC in the air\n    Bug #1655: Use Appropriate Application Icons on Windows\n    Bug #1679: Tribunal expansion, Meryn Othralas the backstage manager in the theatre group in Mournhold in the great bazaar district is floating a good feet above the ground.\n    Bug #1705: Rain is broken in third person\n    Bug #1706: Thunder and lighting still occurs while the game is paused during the rain\n    Bug #1708: No long jumping\n    Bug #1710: Editor: ReferenceableID drag to references record filter field creates incorrect filter\n    Bug #1712: Rest on Water\n    Bug #1715: \"Cancel\" button is not always on the same side of menu\n    Bug #1725: Editor: content file can be opened multiple times from the same dialogue\n    Bug #1730: [MOD: Less Generic Nerevarine] Compile failure attempting to enter the Corprusarium.\n    Bug #1733: Unhandled ffmpeg sample formats\n    Bug #1735: Editor: \"Edit Record\" context menu button not opening subview for journal infos\n    Bug #1750: Editor: record edits result in duplicate entries\n    Bug #1789: Editor: Some characters cannot be used in addon name\n    Bug #1803: Resizing the map does not keep the pre-resize center at the post-resize center\n    Bug #1821: Recovering Cloudcleaver quest: attacking Sosia is considered a crime when you side with Hlormar\n    Bug #1838: Editor: Preferences window appears off screen\n    Bug #1839: Editor: Record filter title should be moved two pixels to the right\n    Bug #1849: Subrecord error in MAO_Containers\n    Bug #1854: Knocked-out actors don't fully act knocked out\n    Bug #1855: \"Soul trapped\" sound doesn't play\n    Bug #1857: Missing sound effect for enchanted items with empty charge\n    Bug #1859: Missing console command: ResetActors (RA)\n    Bug #1861: Vendor category \"MagicItems\" is unhandled\n    Bug #1862: Launcher doesn't start if a file listed in launcher.cfg has correct name but wrong capitalization\n    Bug #1864: Editor: Region field for cell record in dialogue subview not working\n    Bug #1869: Editor: Change label \"Musics\" to \"Music\"\n    Bug #1870: Goblins killed while knocked down remain in knockdown-pose\n    Bug #1874: CellChanged events should not trigger when crossing exterior cell border\n    Bug #1877: Spriggans killed instantly if hit while regening\n    Bug #1878: Magic Menu text not un-highlighting correctly when going from spell to item as active magic\n    Bug #1881: Stuck in ceiling when entering castle karstaags tower\n    Bug #1884: Unlit torches still produce a burning sound\n    Bug #1885: Can type text in price field in barter window\n    Bug #1887: Equipped items do not emit sounds\n    Bug #1889: draugr lord aesliip will attack you and remain non-hostile\n    Bug #1892: Guard asks player to pay bounty of 0 gold\n    Bug #1895: getdistance should only return max float if ref and target are in different worldspaces\n    Bug #1896: Crash Report\n    Bug #1897: Conjured Equipment cant be re-equipped if removed\n    Bug #1898: Only Gidar Verothan follows you during establish the mine quest\n    Bug #1900: Black screen when you open the door and breath underwater\n    Bug #1904: Crash on casting recall spell\n    Bug #1906: Bound item checks should use the GMSTs\n    Bug #1907: Bugged door. Mournhold, The Winged Guar\n    Bug #1908: Crime reported for attacking Drathas Nerus's henchmen while they attack Dilborn\n    Bug #1909: Weird Quest Flow Infidelities quest\n    Bug #1910: Follower fighting with gone npc\n    Bug #1911: Npcs will drown themselves\n    Bug #1912: World map arrow stays static when inside a building\n    Bug #1920: Ulyne Henim disappears when game is loaded inside Vas\n    Bug #1922: alchemy-> potion of paralyze\n    Bug #1923: \"levitation magic cannot be used here\" shows outside of tribunal\n    Bug #1927: AI prefer melee over magic.\n    Bug #1929: Tamriel Rebuilt: Named cells that lie within the overlap with Morrowind.esm are not shown\n    Bug #1932: BTB - Spells 14.1 magic effects don´t overwrite the Vanilla ones but are added\n    Bug #1935: Stacks of items are worth more when sold individually\n    Bug #1940: Launcher does not list addon files if base game file is renamed to a different case\n    Bug #1946: Mod \"Tel Nechim - moved\" breaks savegames\n    Bug #1947: Buying/Selling price doesn't properly affect the growth of mercantile skill\n    Bug #1950: followers from east empire company quest will fight each other if combat happens with anything\n    Bug #1958: Journal can be scrolled indefinitely with a mouse wheel\n    Bug #1959: Follower not leaving party on quest end\n    Bug #1960: Key bindings not always saved correctly\n    Bug #1961: Spell merchants selling racial bonus spells\n    Bug #1967: segmentation fault on load saves\n    Bug #1968: Jump sounds are not controlled by footsteps slider, sound weird compared to footsteps\n    Bug #1970: PC suffers silently when taking damage from lava\n    Bug #1971: Dwarven Sceptre collision area is not removed after killing one\n    Bug #1974: Dalin/Daris Norvayne follows player indefinitely\n    Bug #1975: East Empire Company faction rank breaks during Raven Rock questline\n    Bug #1979: 0 strength = permanently over encumbered\n    Bug #1993: Shrine blessing in Maar Gan doesn't work\n    Bug #2008: Enchanted items do not recharge\n    Bug #2011: Editor: OpenCS script compiler doesn't handle member variable access properly\n    Bug #2016: Dagoth Ur already dead in Facility Cavern\n    Bug #2017: Fighters Guild Quest: The Code Book - dialogue loop when UMP is loaded.\n    Bug #2019: Animation of 'Correct UV Mudcrabs' broken\n    Bug #2022: Alchemy window - Removing ingredient doesn't remove the number of ingredients\n    Bug #2025: Missing mouse-over text for non affordable items\n    Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell \"Ruinous Keep, Great Hall\"\n    Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding\n    Feature #471: Editor: Special case implementation for top-level window with single sub-window\n    Feature #472: Editor: Sub-Window re-use settings\n    Feature #704: Font colors import from fallback settings\n    Feature #879: Editor: Open sub-views in a new top-level window\n    Feature #932: Editor: magic effect table\n    Feature #937: Editor: Path Grid table\n    Feature #938: Editor: Sound Gen table\n    Feature #1117: Death and LevelUp music\n    Feature #1226: Editor: Request UniversalId editing from table columns\n    Feature #1545: Targeting console on player\n    Feature #1597: Editor: Render terrain\n    Feature #1695: Editor: add column for CellRef's global variable\n    Feature #1696: Editor: use ESM::Cell's RefNum counter\n    Feature #1697: Redden player's vision when hit\n    Feature #1856: Spellcasting for non-biped creatures\n    Feature #1879: Editor: Run OpenMW with the currently edited content list\n    Task #1851: Move AI temporary state out of AI packages\n    Task #1865: Replace char type in records\n\n0.32.0\n------\n\n    Bug #1132: Unable to jump when facing a wall\n    Bug #1341: Summoned Creatures do not immediately disappear when killed.\n    Bug #1430: CharGen Revamped script does not compile\n    Bug #1451: NPCs shouldn't equip weapons prior to fighting\n    Bug #1461: Stopped start scripts do not restart on load\n    Bug #1473: Dead NPC standing and in 2 pieces\n    Bug #1482: Abilities are depleted when interrupted during casting\n    Bug #1503: Behaviour of NPCs facing the player\n    Bug #1506: Missing character, French edition: three-points\n    Bug #1528: Inventory very slow after 2 hours\n    Bug #1540: Extra arguments should be ignored for script functions\n    Bug #1541: Helseth's Champion: Tribunal\n    Bug #1570: Journal cannot be opened while in inventory screen\n    Bug #1573: PC joins factions at random\n    Bug #1576: NPCs aren't switching their weapons when out of ammo\n    Bug #1579: Guards detect creatures in far distance, instead on sight\n    Bug #1588: The Siege of the Skaal Village: bloodmoon\n    Bug #1593: The script compiler isn't recognising some names that contain a -\n    Bug #1606: Books: Question marks instead of quotation marks\n    Bug #1608: Dead bodies prevent door from opening/closing.\n    Bug #1609: Imperial guards in Sadrith Mora are not using their spears\n    Bug #1610: The bounty number is not displayed properly with high numbers\n    Bug #1620: Implement correct formula for auto-calculated NPC spells\n    Bug #1630: Boats standing vertically in Vivec\n    Bug #1635: Arrest dialogue is executed second time after I select \"Go to jail\"\n    Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults?\n    Bug #1641: Persuasion dialog remains after loading, possibly resulting in crash\n    Bug #1644: \"Goodbye\" and similar options on dialogues prevents escape working properly.\n    Bug #1646: PC skill stats are not updated immediately when changing equipment\n    Bug #1652: Non-aggressive creature\n    Bug #1653: Quickloading while the container window is open crashes the game\n    Bug #1654: Priority of checks in organic containers\n    Bug #1656: Inventory items merge issue when repairing\n    Bug #1657: Attacked state of NPCs is not saved properly\n    Bug #1660: Rank dialogue condition ignored\n    Bug #1668: Game starts on day 2 instead of day 1\n    Bug #1669: Critical Strikes while fighting a target who is currently fighting me\n    Bug #1672: OpenCS doesn't save the projects\n    Bug #1673: Fatigue decreasing by only one point when running\n    Bug #1675: Minimap and localmap graphic glitches\n    Bug #1676: Pressing the OK button on the travel menu cancels the travel and exits the menu\n    Bug #1677: Sleeping in a rented bed is considered a crime\n    Bug #1685: NPCs turn towards player even if invisible/sneaking\n    Bug #1686: UI bug: cursor is clicking \"world/local\" map button while inventory window is closed?\n    Bug #1690: Double clicking on a inventory window header doesn't close it.\n    Bug #1693: Spell Absorption does not absorb shrine blessings\n    Bug #1694: journal displays learned topics as quests\n    Bug #1700: Sideways scroll of text boxes\n    Bug #1701: Player enchanting requires player hold money, always 100% sucessful.\n    Bug #1704: self-made Fortify Intelligence/Drain willpower potions are broken\n    Bug #1707: Pausing the game through the esc menu will silence rain, pausing it by opening the inventory will not.\n    Bug #1709: Remesa Othril is hostile to Hlaalu members\n    Bug #1713: Crash on load after death\n    Bug #1719: Blind effect has slight border at the edge of the screen where it is ineffective.\n    Bug #1722: Crash after creating enchanted item, reloading saved game\n    Bug #1723: Content refs that are stacked share the same index after unstacking\n    Bug #1726: Can't finish Aengoth the Jeweler's quest : Retrieve the Scrap Metal\n    Bug #1727: Targets almost always resist soultrap scrolls\n    Bug #1728: Casting a soultrap spell on invalid target yields no message\n    Bug #1729: Chop attack doesn't work if walking diagonally\n    Bug #1732: Error handling for missing script function arguments produces weird message\n    Bug #1736: Alt-tabbing removes detail from overworld map.\n    Bug #1737: Going through doors with (high magnitude?) leviation will put the player high up, possibly even out of bounds.\n    Bug #1739: Setting a variable on an NPC from another NPC's dialogue result sets the wrong variable\n    Bug #1741: The wait dialogue doesn't black the screen out properly during waiting.\n    Bug #1742: ERROR: Object 'sDifficulty' not found (const)\n    Bug #1744: Night sky in Skies V.IV (& possibly v3) by SWG rendered incorrectly\n    Bug #1746: Bow/marksman weapon condition does not degrade with use\n    Bug #1749: Constant Battle Music\n    Bug #1752: Alt-Tabbing in the character menus makes the paper doll disappear temporarily\n    Bug #1753: Cost of training is not added to merchant's inventory\n    Bug #1755: Disposition changes do not persist if the conversation menu is closed by purchasing training.\n    Bug #1756: Caught Blight after being cured of Corprus\n    Bug #1758: Crash Upon Loading New Cell\n    Bug #1760: Player's Magicka is not recalculated upon drained or boosted intelligence\n    Bug #1761: Equiped torches lost on reload\n    Bug #1762: Your spell did not get a target. Soul trap. Gorenea Andrano\n    Bug #1763: Custom Spell Magicka Cost\n    Bug #1765: Azuras Star breaks on recharging item\n    Bug #1767: GetPCRank did not handle ignored explicit references\n    Bug #1772: Dark Brotherhood Assassins never use their Carved Ebony Dart, sticking to their melee weapon.\n    Bug #1774: String table overflow also occurs when loading TheGloryRoad.esm\n    Bug #1776: dagoth uthol runs in slow motion\n    Bug #1778: Incorrect values in spellmaking window\n    Bug #1779: Icon of Master Propylon Index is not visible\n    Bug #1783: Invisible NPC after looting corpse\n    Bug #1787: Health Calculation\n    Bug #1788: Skeletons, ghosts etc block doors when we try to open\n    Bug #1791: [MOD: LGNPC Foreign Quarter] NPC in completely the wrong place.\n    Bug #1792: Potions should show more effects\n    Bug #1793: Encumbrance while bartering\n    Bug #1794: Fortify attribute not affecting fatigue\n    Bug #1795: Too much magicka\n    Bug #1796: \"Off by default\" torch burning\n    Bug #1797: Fish too slow\n    Bug #1798: Rest until healed shouldn't show with full health and magicka\n    Bug #1802: Mark location moved\n    Bug #1804: stutter with recent builds\n    Bug #1810: attack gothens dremora doesnt agro the others.\n    Bug #1811: Regression: Crash Upon Loading New Cell\n    Bug #1812: Mod: \"QuickChar\" weird button placement\n    Bug #1815: Keys show value and weight, Vanilla Morrowind's keys dont.\n    Bug #1817: Persuasion results do not show using unpatched MW ESM\n    Bug #1818: Quest B3_ZainabBride moves to stage 47 upon loading save while Falura Llervu is following\n    Bug #1823: AI response to theft incorrect - only guards react, in vanilla everyone does.\n    Bug #1829: On-Target Spells Rendered Behind Water Surface Effects\n    Bug #1830: Galsa Gindu's house is on fire\n    Bug #1832: Fatal Error: OGRE Exception(2:InvalidParametersException)\n    Bug #1836: Attacked Guards open \"fine/jail/resist\"-dialogue after killing you\n    Bug #1840: Infinite recursion in ActionTeleport\n    Bug #1843: Escorted people change into player's cell after completion of escort stage\n    Bug #1845: Typing 'j' into 'Name' fields opens the journal\n    Bug #1846: Text pasted into the console still appears twice (Windows)\n    Bug #1847: \"setfatigue 0\" doesn't render NPC unconscious\n    Bug #1848: I can talk to unconscious actors\n    Bug #1866: Crash when player gets killed by a creature summoned by him\n    Bug #1868: Memory leaking when openmw window is minimized\n    Feature #47: Magic Effects\n    Feature #642: Control NPC mouth movement using current Say sound\n    Feature #939: Editor: Resources tables\n    Feature #961: AI Combat for magic (spells, potions and enchanted items)\n    Feature #1111: Collision script instructions (used e.g. by Lava)\n    Feature #1120: Command creature/humanoid magic effects\n    Feature #1121: Elemental shield magic effects\n    Feature #1122: Light magic effect\n    Feature #1139: AI: Friendly hits\n    Feature #1141: AI: combat party\n    Feature #1326: Editor: Add tooltips to all graphical buttons\n    Feature #1489: Magic effect Get/Mod/Set functions\n    Feature #1505: Difficulty slider\n    Feature #1538: Targeted scripts\n    Feature #1571: Allow creating custom markers on the local map\n    Feature #1615: Determine local variables from compiled scripts instead of the values in the script record\n    Feature #1616: Editor: Body part record verifier\n    Feature #1651: Editor: Improved keyboard navigation for scene toolbar\n    Feature #1666: Script blacklisting\n    Feature #1711: Including the Git revision number from the command line \"--version\" switch.\n    Feature #1721: NPC eye blinking\n    Feature #1740: Scene toolbar buttons for selecting which type of elements are rendered\n    Feature #1790: Mouse wheel scrolling for the journal\n    Feature #1850: NiBSPArrayController\n    Task #768: On windows, settings folder should be \"OpenMW\", not \"openmw\"\n    Task #908: Share keyframe data\n    Task #1716: Remove defunct option for building without FFmpeg\n\n0.31.0\n------\n\n    Bug #245: Cloud direction and weather systems differ from Morrowind\n    Bug #275: Local Map does not always show objects that span multiple cells\n    Bug #538: Update CenterOnCell (COC) function behavior\n    Bug #618: Local and World Map Textures are sometimes Black\n    Bug #640: Water behaviour at night\n    Bug #668: OpenMW doesn't support non-latin paths on Windows\n    Bug #746: OpenMW doesn't check if the background music was already played\n    Bug #747: Door is stuck if cell is left before animation finishes\n    Bug #772: Disabled statics are visible on map\n    Bug #829: OpenMW uses up all available vram, when playing for extended time\n    Bug #869: Dead bodies don't collide with anything\n    Bug #894: Various character creation issues\n    Bug #897/#1369: opencs Segmentation Fault after \"new\" or \"load\"\n    Bug #899: Various jumping issues\n    Bug #952: Reflection effects are one frame delayed\n    Bug #993: Able to interact with world during Wait/Rest dialog\n    Bug #995: Dropped items can be placed inside the wall\n    Bug #1008: Corpses always face up upon reentering the cell\n    Bug #1035: Random colour patterns appearing in automap\n    Bug #1037: Footstep volume issues\n    Bug #1047: Creation of wrong links in dialogue window\n    Bug #1129: Summoned creature time life duration seems infinite\n    Bug #1134: Crimes can be committed against hostile NPCs\n    Bug #1136: Creature run speed formula is incorrect\n    Bug #1150: Weakness to Fire doesn't apply to Fire Damage in the same spell\n    Bug #1155: NPCs killing each other\n    Bug #1166: Bittercup script still does not work\n    Bug #1178: .bsa file names are case sensitive.\n    Bug #1179: Crash after trying to load game after being killed\n    Bug #1180: Changing footstep sound location\n    Bug #1196: Jumping not disabled when showing messageboxes\n    Bug #1202: \"strange\" keys are not shown in binding menu, and are not saved either, but works\n    Bug #1216: Broken dialog topics in russian Morrowind\n    Bug #1217: Container content changes based on the current position of the mouse\n    Bug #1234: Loading/saving issues with dynamic records\n    Bug #1277: Text pasted into the console appears twice\n    Bug #1284: Crash on New Game\n    Bug #1303: It's possible to skip the chargen\n    Bug #1304: Slaughterfish should not detect the player unless the player is in the water\n    Bug #1311: Editor: deleting Record Filter line does not reset the filter\n    Bug #1324: ERROR: ESM Error: String table overflow when loading Animated Morrowind.esp\n    Bug #1328: Editor: Bogus Filter created when dragging multiple records to filter bar of non-applicable table\n    Bug #1331: Walking/running sound persist after killing NPC`s that are walking/running.\n    Bug #1334: Previously equipped items not shown as unequipped after attempting to sell them.\n    Bug #1335: Actors ignore vertical axis when deciding to attack\n    Bug #1338: Unknown toggle option for shadows\n    Bug #1339: \"Ashlands Region\" is visible when beginning new game during \"Loading Area\" process\n    Bug #1340: Guards prompt Player with punishment options after resisting arrest with another guard.\n    Bug #1348: Regression: Bug #1098 has returned with a vengeance\n    Bug #1349: [TR] TR_Data mesh tr_ex_imp_gatejamb01 cannot be activated\n    Bug #1352: Disabling an ESX file does not disable dependent ESX files\n    Bug #1355: CppCat Checks OpenMW\n    Bug #1356: Incorrect voice type filtering for sleep interrupts\n    Bug #1357: Restarting the game clears saves\n    Bug #1360: Seyda Neen silk rider dialog problem\n    Bug #1361: Some lights don't work\n    Bug #1364: It is difficult to bind \"Mouse 1\" to an action in the options menu\n    Bug #1370: Animation compilation mod does not work properly\n    Bug #1371: SL_Pick01.nif from third party fails to load in openmw, but works in Vanilla\n    Bug #1373: When stealing in front of Sellus Gravius cannot exit the dialog\n    Bug #1378: Installs to /usr/local are not working\n    Bug #1380: Loading a save file fail if one of the content files is disabled\n    Bug #1382: \"getHExact() size mismatch\" crash on loading official plugin \"Siege at Firemoth.esp\"\n    Bug #1386: Arkngthand door will not open\n    Bug #1388: Segfault when modifying View Distance in Menu options\n    Bug #1389: Crash when loading a save after dying\n    Bug #1390: Apostrophe characters not displayed [French version]\n    Bug #1391: Custom made icon background texture for magical weapons and stuff isn't scaled properly on GUI.\n    Bug #1393: Coin icon during the level up dialogue are off of the background\n    Bug #1394: Alt+F4 doesn't work on Win version\n    Bug #1395: Changing rings switches only the last one put on\n    Bug #1396: Pauldron parts aren't showing when the robe is equipped\n    Bug #1402: Dialogue of some shrines have wrong button orientation\n    Bug #1403: Items are floating in the air when they're dropped onto dead bodies.\n    Bug #1404: Forearms are not rendered on Argonian females\n    Bug #1407: Alchemy allows making potions from two of the same item\n    Bug #1408: \"Max sale\" button gives you all the items AND all the trader's gold\n    Bug #1409: Rest \"Until Healed\" broken for characters with stunted magicka.\n    Bug #1412: Empty travel window opens while playing through start game\n    Bug #1413: Save game ignores missing writing permission\n    Bug #1414: The Underground 2 ESM Error\n    Bug #1416: Not all splash screens in the Splash directory are used\n    Bug #1417: Loading saved game does not terminate\n    Bug #1419: Skyrim: Home of the Nords error\n    Bug #1422: ClearInfoActor\n    Bug #1423: ForceGreeting closes existing dialogue windows\n    Bug #1425: Cannot load save game\n    Bug #1426: Read skill books aren't stored in savegame\n    Bug #1427: Useless items can be set under hotkeys\n    Bug #1429: Text variables in journal\n    Bug #1432: When attacking friendly NPC, the crime is reported and bounty is raised after each swing\n    Bug #1435: Stealing priceless items is without punishment\n    Bug #1437: Door marker at Jobasha's Rare Books is spawning PC in the air\n    Bug #1440: Topic selection menu should be wider\n    Bug #1441: Dropping items on the rug makes them inaccessible\n    Bug #1442: When dropping and taking some looted items, bystanders consider that as a crime\n    Bug #1444: Arrows and bolts are not dropped where the cursor points\n    Bug #1445: Security trainers offering acrobatics instead\n    Bug #1447: Character dash not displayed, French edition\n    Bug #1448: When the player is killed by the guard while having a bounty on his head, the guard dialogue opens over and over instead of loading dialogue\n    Bug #1454: Script error in SkipTutorial\n    Bug #1456: Bad lighting when using certain Morrowind.ini generated by MGE\n    Bug #1457: Heart of Lorkan comes after you when attacking it\n    Bug #1458: Modified Keybindings are not remembered\n    Bug #1459: Dura Gra-Bol doesn't respond to PC attack\n    Bug #1462: Interior cells not loaded with Morrowind Patch active\n    Bug #1469: Item tooltip should show the base value, not real value\n    Bug #1477: Death count is not stored in savegame\n    Bug #1478: AiActivate does not trigger activate scripts\n    Bug #1481: Weapon not rendered when partially submerged in water\n    Bug #1483: Enemies are attacking even while dying\n    Bug #1486: ESM Error: Don't know what to do with INFO\n    Bug #1490: Arrows shot at PC can end up in inventory\n    Bug #1492: Monsters respawn on top of one another\n    Bug #1493: Dialogue box opens with follower NPC even if NPC is dead\n    Bug #1494: Paralysed cliffracers remain airbourne\n    Bug #1495: Dialogue box opens with follower NPC even the game is paused\n    Bug #1496: GUI messages are not cleared when loading another saved game\n    Bug #1499: Underwater sound sometimes plays when transitioning from interior.\n    Bug #1500: Targetted spells and water.\n    Bug #1502: Console error message on info refusal\n    Bug #1507: Bloodmoon MQ The Ritual of Beasts: Can't remove the arrow\n    Bug #1508: Bloodmoon: Fort Frostmoth, cant talk with Carnius Magius\n    Bug #1516: PositionCell doesn't move actors to current cell\n    Bug #1518: ForceGreeting broken for explicit references\n    Bug #1522: Crash after attempting to play non-music file\n    Bug #1523: World map empty after loading interior save\n    Bug #1524: Arrows in waiting/resting dialog act like minimum and maximum buttons\n    Bug #1525: Werewolf: Killed NPC's don't fill werewolfs hunger for blood\n    Bug #1527: Werewolf: Detect life detects wrong type of actor\n    Bug #1529: OpenMW crash during \"the shrine of the dead\" mission (tribunal)\n    Bug #1530: Selected text in the console has the same color as the background\n    Bug #1539: Barilzar's Mazed Band: Tribunal\n    Bug #1542: Looping taunts from NPC`s after death: Tribunal\n    Bug #1543: OpenCS crash when using drag&drop in script editor\n    Bug #1547: Bamz-Amschend: Centurion Archers combat problem\n    Bug #1548: The Missing Hand: Tribunal\n    Bug #1549: The Mad God: Tribunal, Dome of Serlyn\n    Bug #1557: A bounty is calculated from actual item cost\n    Bug #1562: Invisible terrain on top of Red Mountain\n    Bug #1564: Cave of the hidden music: Bloodmoon\n    Bug #1567: Editor: Deleting of referenceables does not work\n    Bug #1568: Picking up a stack of items and holding the enter key and moving your mouse around paints a bunch of garbage on screen.\n    Bug #1574: Solstheim: Drauger cant inflict damage on player\n    Bug #1578: Solstheim: Bonewolf running animation not working\n    Bug #1585: Particle effects on PC are stopped when paralyzed\n    Bug #1589: Tribunal: Crimson Plague quest does not update when Gedna Relvel is killed\n    Bug #1590: Failed to save game: compile error\n    Bug #1598: Segfault when making Drain/Fortify Skill spells\n    Bug #1599: Unable to switch to fullscreen\n    Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed\n    Bug #1618: Death notice fails to show up\n    Bug #1628: Alt+Tab Segfault\n    Feature #32: Periodic Cleanup/Refill\n    Feature #41: Precipitation and weather particles\n    Feature #568: Editor: Configuration setup\n    Feature #649: Editor: Threaded loading\n    Feature #930: Editor: Cell record saving\n    Feature #934: Editor: Body part table\n    Feature #935: Editor: Enchantment effect table\n    Feature #1162: Dialogue merging\n    Feature #1174: Saved Game: add missing creature state\n    Feature #1177: Saved Game: fog of war state\n    Feature #1312: Editor: Combat/Magic/Stealth values for creatures are not displayed\n    Feature #1314: Make NPCs and creatures fight each other\n    Feature #1315: Crime: Murder\n    Feature #1321: Sneak skill enhancements\n    Feature #1323: Handle restocking items\n    Feature #1332: Saved Game: levelled creatures\n    Feature #1347: modFactionReaction script instruction\n    Feature #1362: Animated main menu support\n    Feature #1433: Store walk/run toggle\n    Feature #1449: Use names instead of numbers for saved game files and folders\n    Feature #1453: Adding Delete button to the load menu\n    Feature #1460: Enable Journal screen while in dialogue\n    Feature #1480: Play Battle music when in combat\n    Feature #1501: Followers unable to fast travel with you\n    Feature #1520: Disposition and distance-based aggression/ShouldAttack\n    Feature #1595: Editor: Object rendering in cells\n    Task #940: Move license to locations where applicable\n    Task #1333: Remove cmake git tag reading\n    Task #1566: Editor: Object rendering refactoring\n\n0.30.0\n------\n\n    Bug #416: Extreme shaking can occur during cell transitions while moving\n    Bug #1003: Province Cyrodiil: Ogre Exception in Stirk\n    Bug #1071: Crash when given a non-existent content file\n    Bug #1080: OpenMW allows resting/using a bed while in combat\n    Bug #1097: Wrong punishment for stealing in Census and Excise Office at the start of a new game\n    Bug #1098: Unlocked evidence chests should get locked after new evidence is put into them\n    Bug #1099: NPCs that you attacked still fight you after you went to jail/paid your fine\n    Bug #1100: Taking items from a corpse is considered stealing\n    Bug #1126: Some creatures can't get close enough to attack\n    Bug #1144: Killed creatures seem to die again each time player transitions indoors/outdoors\n    Bug #1181: loading a saved game does not reset the player control status\n    Bug #1185: Collision issues in Addamasartus\n    Bug #1187: Athyn Sarethi mission, rescuing varvur sarethi from the doesnt end the mission\n    Bug #1189: Crash when entering interior cell \"Gnisis, Arvs-Drelen\"\n    Bug #1191: Picking up papers without inventory in new game\n    Bug #1195: NPCs do not equip torches in certain interiors\n    Bug #1197: mouse wheel makes things scroll too fast\n    Bug #1200: door blocked by monsters\n    Bug #1201: item's magical charges are only refreshed when they are used\n    Bug #1203: Scribs do not defend themselves\n    Bug #1204: creatures life is not empty when they are dead\n    Bug #1205: armor experience does not progress when hits are taken\n    Bug #1206: blood particules always red. Undeads and mechanicals should have a different one.\n    Bug #1209: Tarhiel never falls\n    Bug #1210: journal adding script is ran again after having saved/loaded\n    Bug #1224: Names of custom classes are not properly handled in save games\n    Bug #1227: Editor: Fixed case handling for broken localised versions of Morrowind.esm\n    Bug #1235: Indoors walk stutter\n    Bug #1236: Aborting intro movie brings up the menu\n    Bug #1239: NPCs get stuck when walking past each other\n    Bug #1240: BTB - Settings 14.1 and Health Bar.\n    Bug #1241: BTB - Character and Khajiit Prejudice\n    Bug #1248: GUI Weapon icon is changed to hand-to-hand after save load\n    Bug #1254: Guild ranks do not show in dialogue\n    Bug #1255: When opening a container and selecting \"Take All\", the screen flashes blue\n    Bug #1260: Level Up menu doesn't show image when using a custom class\n    Bug #1265: Quit Menu Has Misaligned Buttons\n    Bug #1270: Active weapon icon is not updated when weapon is repaired\n    Bug #1271: NPC Stuck in hovering \"Jumping\" animation\n    Bug #1272: Crash when attempting to load Big City esm file.\n    Bug #1276: Editor: Dropping a region into the filter of a cell subview fails\n    Bug #1286: Dialogue topic list clips with window frame\n    Bug #1291: Saved game: store faction membership\n    Bug #1293: Pluginless Khajiit Head Pack by ashiraniir makes OpenMW close.\n    Bug #1294: Pasting in console adds text to end, not at cursor\n    Bug #1295: Conversation loop when asking about \"specific place\" in Vivec\n    Bug #1296: Caius doesn't leave at start of quest \"Mehra Milo and the Lost Prophecies\"\n    Bug #1297: Saved game: map markers\n    Bug #1302: ring_keley script causes vector::_M_range_check exception\n    Bug #1309: Bug on \"You violated the law\" dialog\n    Bug #1319: Creatures sometimes rendered incorrectly\n    Feature #50: Ranged Combat\n    Feature #58: Sneaking Skill\n    Feature #73: Crime and Punishment\n    Feature #135: Editor: OGRE integration\n    Feature #541: Editor: Dialogue Sub-Views\n    Feature #853: Editor: Rework User Settings\n    Feature #944: Editor: lighting modes\n    Feature #945: Editor: Camera navigation mode\n    Feature #953: Trader gold\n    Feature #1140: AI: summoned creatures\n    Feature #1142: AI follow: Run stance\n    Feature #1154: Not all NPCs get aggressive when one is attacked\n    Feature #1169: Terrain threading\n    Feature #1172: Loading screen and progress bars during saved/loading game\n    Feature #1173: Saved Game: include weather state\n    Feature #1207: Class creation form does not remember\n    Feature #1220: Editor: Preview Subview\n    Feature #1223: Saved Game: Local Variables\n    Feature #1229: Quicksave, quickload, autosave\n    Feature #1230: Deleting saves\n    Feature #1233: Bribe gold is placed into NPCs inventory\n    Feature #1252: Saved Game: quick key bindings\n    Feature #1273: Editor: Region Map context menu\n    Feature #1274: Editor: Region Map drag & drop\n    Feature #1275: Editor: Scene subview drop\n    Feature #1282: Non-faction member crime recognition.\n    Feature #1289: NPCs return to default position\n    Task #941: Remove unused cmake files\n\n0.29.0\n------\n\n    Bug #556: Video soundtrack not played when music volume is set to zero\n    Bug #829: OpenMW uses up all available vram, when playing for extended time\n    Bug #848: Wrong amount of footsteps playing in 1st person\n    Bug #888: Ascended Sleepers have movement issues\n    Bug #892: Explicit references are allowed on all script functions\n    Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly\n    Bug #1009: Lake Fjalding AI related slowdown.\n    Bug #1041: Music playback issues on OS X >= 10.9\n    Bug #1043: No message box when advancing skill \"Speechcraft\" while in dialog window\n    Bug #1060: Some message boxes are cut off at the bottom\n    Bug #1062: Bittercup script does not work ('end' variable)\n    Bug #1074: Inventory paperdoll obscures armour rating\n    Bug #1077: Message after killing an essential NPC disappears too fast\n    Bug #1078: \"Clutterbane\" shows empty charge bar\n    Bug #1083: UndoWerewolf fails\n    Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered\n    Bug #1090: Start scripts fail when going to a non-predefined cell\n    Bug #1091: Crash: Assertion `!q.isNaN() && \"Invalid orientation supplied as parameter\"' failed.\n    Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior\n    Bug #1105: Magicka is depleted when using uncastable spells\n    Bug #1106: Creatures should be able to run\n    Bug #1107: TR cliffs have way too huge collision boxes in OpenMW\n    Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded.\n    Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop)\n    Bug #1115: Memory leak when spying on Fargoth\n    Bug #1137: Script execution fails (drenSlaveOwners script)\n    Bug #1143: Mehra Milo quest (vivec informants) is broken\n    Bug #1145: Issues with moving gold between inventory and containers\n    Bug #1146: Issues with picking up stacks of gold\n    Bug #1147: Dwemer Crossbows are held incorrectly\n    Bug #1158: Armor rating should always stay below inventory mannequin\n    Bug #1159: Quick keys can be set during character generation\n    Bug #1160: Crash on equip lockpick when\n    Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file\n    Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file\n    Feature #30: Loading/Saving (still missing a few parts)\n    Feature #101: AI Package: Activate\n    Feature #103: AI Package: Follow, FollowCell\n    Feature #138: Editor: Drag & Drop\n    Feature #428: Player death\n    Feature #505: Editor: Record Cloning\n    Feature #701: Levelled creatures\n    Feature #708: Improved Local Variable handling\n    Feature #709: Editor: Script verifier\n    Feature #764: Missing journal backend features\n    Feature #777: Creature weapons/shields\n    Feature #789: Editor: Referenceable record verifier\n    Feature #924: Load/Save GUI (still missing loading screen and progress bars)\n    Feature #946: Knockdown\n    Feature #947: Decrease fatigue when running, swimming and attacking\n    Feature #956: Melee Combat: Blocking\n    Feature #957: Area magic\n    Feature #960: Combat/AI combat for creatures\n    Feature #962: Combat-Related AI instructions\n    Feature #1075: Damage/Restore skill/attribute magic effects\n    Feature #1076: Soultrap magic effect\n    Feature #1081: Disease contraction\n    Feature #1086: Blood particles\n    Feature #1092: Interrupt resting\n    Feature #1101: Inventory equip scripts\n    Feature #1116: Version/Build number in Launcher window\n    Feature #1119: Resistance/weakness to normal weapons magic effect\n    Feature #1123: Slow Fall magic effect\n    Feature #1130: Auto-calculate spells\n    Feature #1164: Editor: Case-insensitive sorting in tables\n\n0.28.0\n------\n\n    Bug #399: Inventory changes are not visible immediately\n    Bug #417: Apply weather instantly when teleporting\n    Bug #566: Global Map position marker not updated for interior cells\n    Bug #712: Looting corpse delay\n    Bug #716: Problem with the \"Vurt's Ascadian Isles Mod\" mod\n    Bug #805: Two TR meshes appear black (v0.24RC)\n    Bug #841: Third-person activation distance taken from camera rather than head\n    Bug #845: NPCs hold torches during the day\n    Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24\n    Bug #856: Maormer race by Mac Kom - The heads are way up\n    Bug #864: Walk locks during loading in 3rd person\n    Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog\n    Bug #882: Hircine's Ring doesn't always work\n    Bug #909: [Tamriel Rebuilt] crashes in Akamora\n    Bug #922: Launcher writing merged openmw.cfg files\n    Bug #943: Random magnitude should be calculated per effect\n    Bug #948: Negative fatigue level should be allowed\n    Bug #949: Particles in world space\n    Bug #950: Hard crash on x64 Linux running --new-game (on startup)\n    Bug #951: setMagicka and setFatigue have no effect\n    Bug #954: Problem with equipping inventory items when using a keyboard shortcut\n    Bug #955: Issues with equipping torches\n    Bug #966: Shield is visible when casting spell\n    Bug #967: Game crashes when equipping silver candlestick\n    Bug #970: Segmentation fault when starting at Bal Isra\n    Bug #977: Pressing down key in console doesn't go forward in history\n    Bug #979: Tooltip disappears when changing inventory\n    Bug #980: Barter: item category is remembered, but not shown\n    Bug #981: Mod: replacing model has wrong position/orientation\n    Bug #982: Launcher: Addon unchecking is not saved\n    Bug #983: Fix controllers to affect objects attached to the base node\n    Bug #985: Player can talk to NPCs who are in combat\n    Bug #989: OpenMW crashes when trying to include mod with capital .ESP\n    Bug #991: Merchants equip items with harmful constant effect enchantments\n    Bug #994: Don't cap skills/attributes when set via console\n    Bug #998: Setting the max health should also set the current health\n    Bug #1005: Torches are visible when casting spells and during hand to hand combat.\n    Bug #1006: Many NPCs have 0 skill\n    Bug #1007: Console fills up with text\n    Bug #1013: Player randomly loses health or dies\n    Bug #1014: Persuasion window is not centered in maximized window\n    Bug #1015: Player status window scroll state resets on status change\n    Bug #1016: Notification window not big enough for all skill level ups\n    Bug #1020: Saved window positions are not rescaled appropriately on resolution change\n    Bug #1022: Messages stuck permanently on screen when they pile up\n    Bug #1023: Journals doesn't open\n    Bug #1026: Game loses track of torch usage.\n    Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level\n    Bug #1029: Quick keys menu: Select compatible replacement when tool used up\n    Bug #1042: TES3 header data wrong encoding\n    Bug #1045: OS X: deployed OpenCS won't launch\n    Bug #1046: All damaged weaponry is worth 1 gold\n    Bug #1048: Links in \"locked\" dialogue are still clickable\n    Bug #1052: Using color codes when naming your character actually changes the name's color\n    Bug #1054: Spell effects not visible in front of water\n    Bug #1055: Power-Spell animation starts even though you already casted it that day\n    Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability\n    Bug #1063: Crash upon checking out game start ship area in Seyda Neen\n    Bug #1064: openmw binaries link to unnecessary libraries\n    Bug #1065: Landing from a high place in water still causes fall damage\n    Bug #1072: Drawing weapon increases torch brightness\n    Bug #1073: Merchants sell stacks of gold\n    Feature #43: Visuals for Magic Effects\n    Feature #51: Ranged Magic\n    Feature #52: Touch Range Magic\n    Feature #53: Self Range Magic\n    Feature #54: Spell Casting\n    Feature #70: Vampirism\n    Feature #100: Combat AI\n    Feature #171: Implement NIF record NiFlipController\n    Feature #410: Window to restore enchanted item charge\n    Feature #647: Enchanted item glow\n    Feature #723: Invisibility/Chameleon magic effects\n    Feature #737: Resist Magicka magic effect\n    Feature #758: GetLOS\n    Feature #926: Editor: Info-Record tables\n    Feature #958: Material controllers\n    Feature #959: Terrain bump, specular, & parallax mapping\n    Feature #990: Request: unlock mouse when in any menu\n    Feature #1018: Do not allow view mode switching while performing an action\n    Feature #1027: Vertex morph animation (NiGeomMorpherController)\n    Feature #1031: Handle NiBillboardNode\n    Feature #1051: Implement NIF texture slot DarkTexture\n    Task #873: Unify OGRE initialisation\n\n0.27.0\n------\n\n    Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp\n    Bug #794: incorrect display of decimal numbers\n    Bug #840: First-person sneaking camera height\n    Bug #887: Ambient sounds playing while paused\n    Bug #902: Problems with Polish character encoding\n    Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key\n    Bug #910: Some CDs not working correctly with Unshield installer\n    Bug #917: Quick character creation plugin does not work\n    Bug #918: Fatigue does not refill\n    Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2)\n    Feature #57: Acrobatics Skill\n    Feature #462: Editor: Start Dialogue\n    Feature #546: Modify ESX selector to handle new content file scheme\n    Feature #588: Editor: Adjust name/path of edited content files\n    Feature #644: Editor: Save\n    Feature #710: Editor: Configure script compiler context\n    Feature #790: God Mode\n    Feature #881: Editor: Allow only one instance of OpenCS\n    Feature #889: Editor: Record filtering\n    Feature #895: Extinguish torches\n    Feature #898: Breath meter enhancements\n    Feature #901: Editor: Default record filter\n    Feature #913: Merge --master and --plugin switches\n\n0.26.0\n------\n\n    Bug #274: Inconsistencies in the terrain\n    Bug #557: Already-dead NPCs do not equip clothing/items.\n    Bug #592: Window resizing\n    Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren)\n    Bug #664: Heart of lorkhan acts like a dead body (container)\n    Bug #767: Wonky ramp physics & water\n    Bug #780: Swimming out of water\n    Bug #792: Wrong ground alignment on actors when no clipping\n    Bug #796: Opening and closing door sound issue\n    Bug #797: No clipping hinders opening and closing of doors\n    Bug #799: sliders in enchanting window\n    Bug #838: Pressing key during startup procedure freezes the game\n    Bug #839: Combat/magic stances during character creation\n    Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment\n    Bug #844: Resting \"until healed\" option given even with full stats\n    Bug #846: Equipped torches are invisible.\n    Bug #847: Incorrect formula for autocalculated NPC initial health\n    Bug #850: Shealt weapon sound plays when leaving magic-ready stance\n    Bug #852: Some boots do not produce footstep sounds\n    Bug #860: FPS bar misalignment\n    Bug #861: Unable to print screen\n    Bug #863: No sneaking and jumping at the same time\n    Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start.\n    Bug #867: Dancing girls in \"Suran, Desele's House of Earthly Delights\" don't dance.\n    Bug #868: Idle animations are repeated\n    Bug #874: Underwater swimming close to the ground is jerky\n    Bug #875: Animation problem while swimming on the surface and looking up\n    Bug #876: Always a starting upper case letter in the inventory\n    Bug #878: Active spell effects don't update the layout properly when ended\n    Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load\n    Bug #896: New game sound issue\n    Feature #49: Melee Combat\n    Feature #71: Lycanthropy\n    Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList\n    Feature #622: Multiple positions for inventory window\n    Feature #627: Drowning\n    Feature #786: Allow the 'Activate' key to close the countdialog window\n    Feature #798: Morrowind installation via Launcher (Linux/Max OS only)\n    Feature #851: First/Third person transitions with mouse wheel\n    Task #689: change PhysicActor::enableCollisions\n    Task #707: Reorganise Compiler\n\n0.25.0\n------\n\n    Bug #411: Launcher crash on OS X < 10.8\n    Bug #604: Terrible performance drop in the Census and Excise Office.\n    Bug #676: Start Scripts fail to load\n    Bug #677: OpenMW does not accept script names with -\n    Bug #766: Extra space in front of topic links\n    Bug #793: AIWander Isn't Being Passed The Repeat Parameter\n    Bug #795: Sound playing with drawn weapon and crossing cell-border\n    Bug #800: can't select weapon for enchantment\n    Bug #801: Player can move while over-encumbered\n    Bug #802: Dead Keys not working\n    Bug #808: mouse capture\n    Bug #809: ini Importer does not work without an existing cfg file\n    Bug #812: Launcher will run OpenMW with no ESM or ESP selected\n    Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected\n    Bug #817: Dead NPCs and Creatures still have collision boxes\n    Bug #820: Incorrect sorting of answers (Dialogue)\n    Bug #826: mwinimport dumps core when given an unknown parameter\n    Bug #833: getting stuck in door\n    Bug #835: Journals/books not showing up properly.\n    Feature #38: SoundGen\n    Feature #105: AI Package: Wander\n    Feature #230: 64-bit compatibility for OS X\n    Feature #263: Hardware mouse cursors\n    Feature #449: Allow mouse outside of window while paused\n    Feature #736: First person animations\n    Feature #750: Using mouse wheel in third person mode\n    Feature #822: Autorepeat for slider buttons\n\n0.24.0\n------\n\n    Bug #284: Book's text misalignment\n    Bug #445: Camera able to get slightly below floor / terrain\n    Bug #582: Seam issue in Red Mountain\n    Bug #632: Journal Next Button shows white square\n    Bug #653: IndexedStore ignores index\n    Bug #694: Parser does not recognize float values starting with .\n    Bug #699: Resource handling broken with Ogre 1.9 trunk\n    Bug #718: components/esm/loadcell is using the mwworld subsystem\n    Bug #729: Levelled item list tries to add nonexistent item\n    Bug #730: Arrow buttons in the settings menu do not work.\n    Bug #732: Erroneous behavior when binding keys\n    Bug #733: Unclickable dialogue topic\n    Bug #734: Book empty line problem\n    Bug #738: OnDeath only works with implicit references\n    Bug #740: Script compiler fails on scripts with special names\n    Bug #742: Wait while no clipping\n    Bug #743: Problem with changeweather console command\n    Bug #744: No wait dialogue after starting a new game\n    Bug #748: Player is not able to unselect objects with the console\n    Bug #751: AddItem should only spawn a message box when called from dialogue\n    Bug #752: The enter button has several functions in trade and looting that is not impelemted.\n    Bug #753: Fargoth's Ring Quest Strange Behavior\n    Bug #755: Launcher writes duplicate lines into settings.cfg\n    Bug #759: Second quest in mages guild does not work\n    Bug #763: Enchantment cast cost is wrong\n    Bug #770: The \"Take\" and \"Close\" buttons in the scroll GUI are stretched incorrectly\n    Bug #773: AIWander Isn't Being Passed The Correct idle Values\n    Bug #778: The journal can be opened at the start of a new game\n    Bug #779: Divayth Fyr starts as dead\n    Bug #787: \"Batch count\" on detailed FPS counter gets cut-off\n    Bug #788: chargen scroll layout does not match vanilla\n    Feature #60: Atlethics Skill\n    Feature #65: Security Skill\n    Feature #74: Interaction with non-load-doors\n    Feature #98: Render Weapon and Shield\n    Feature #102: AI Package: Escort, EscortCell\n    Feature #182: Advanced Journal GUI\n    Feature #288: Trading enhancements\n    Feature #405: Integrate \"new game\" into the menu\n    Feature #537: Highlight dialogue topic links\n    Feature #658: Rotate, RotateWorld script instructions and local rotations\n    Feature #690: Animation Layering\n    Feature #722: Night Eye/Blind magic effects\n    Feature #735: Move, MoveWorld script instructions.\n    Feature #760: Non-removable corpses\n\n0.23.0\n------\n\n    Bug #522: Player collides with placeable items\n    Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open\n    Bug #561: Tooltip word wrapping delay\n    Bug #578: Bribing works incorrectly\n    Bug #601: PositionCell fails on negative coordinates\n    Bug #606: Some NPCs hairs not rendered with Better Heads addon\n    Bug #609: Bad rendering of bone boots\n    Bug #613: Messagebox causing assert to fail\n    Bug #631: Segfault on shutdown\n    Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard\n    Bug #635: Scale NPCs depending on race\n    Bug #643: Dialogue Race select function is inverted\n    Bug #646: Twohanded weapons don't work properly\n    Bug #654: Crash when dropping objects without a collision shape\n    Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell\n    Bug #660: \"g\" in \"change\" cut off in Race Menu\n    Bug #661: Arrille sells me the key to his upstairs room\n    Bug #662: Day counter starts at 2 instead of 1\n    Bug #663: Cannot select \"come unprepared\" topic in dialog with Dagoth Ur\n    Bug #665: Pickpocket -> \"Grab all\" grabs all NPC inventory, even not listed in container window.\n    Bug #666: Looking up/down problem\n    Bug #667: Active effects border visible during loading\n    Bug #669: incorrect player position at new game start\n    Bug #670: race selection menu: sex, face and hair left button not totally clickable\n    Bug #671: new game: player is naked\n    Bug #674: buying or selling items doesn't change amount of gold\n    Bug #675: fatigue is not set to its maximum when starting a new game\n    Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly\n    Bug #680: different gold coins in Tel Mara\n    Bug #682: Race menu ignores playable flag for some hairs and faces\n    Bug #685: Script compiler does not accept \":\" after a function name\n    Bug #688: dispose corpse makes cross-hair to disappear\n    Bug #691: Auto equipping ignores equipment conditions\n    Bug #692: OpenMW doesnt load \"loose file\" texture packs that places resources directly in data folder\n    Bug #696: Draugr incorrect head offset\n    Bug #697: Sail transparency issue\n    Bug #700: \"On the rocks\" mod does not load its UV coordinates correctly.\n    Bug #702: Some race mods don't work\n    Bug #711: Crash during character creation\n    Bug #715: Growing Tauryon\n    Bug #725: Auto calculate stats\n    Bug #728: Failure to open container and talk dialogue\n    Bug #731: Crash with Mush-Mere's \"background\" topic\n    Feature #55/657: Item Repairing\n    Feature #62/87: Enchanting\n    Feature #99: Pathfinding\n    Feature #104: AI Package: Travel\n    Feature #129: Levelled items\n    Feature #204: Texture animations\n    Feature #239: Fallback-Settings\n    Feature #535: Console object selection improvements\n    Feature #629: Add levelup description in levelup layout dialog\n    Feature #630: Optional format subrecord in (tes3) header\n    Feature #641: Armor rating\n    Feature #645: OnDeath script function\n    Feature #683: Companion item UI\n    Feature #698: Basic Particles\n    Task #648: Split up components/esm/loadlocks\n    Task #695: mwgui cleanup\n\n0.22.0\n------\n\n    Bug #311: Potential infinite recursion in script compiler\n    Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit.\n    Bug #382: Weird effect in 3rd person on water\n    Bug #387: Always use detailed shape for physics raycasts\n    Bug #420: Potion/ingredient effects do not stack\n    Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips\n    Bug #434/Bug #605: Object movement between cells not properly implemented\n    Bug #502: Duplicate player collision model at origin\n    Bug #509: Dialogue topic list shifts inappropriately\n    Bug #513: Sliding stairs\n    Bug #515: Launcher does not support non-latin strings\n    Bug #525: Race selection preview camera wrong position\n    Bug #526: Attributes / skills should not go below zero\n    Bug #529: Class and Birthsign menus options should be preselected\n    Bug #530: Lock window button graphic missing\n    Bug #532: Missing map menu graphics\n    Bug #545: ESX selector does not list ESM files properly\n    Bug #547: Global variables of type short are read incorrectly\n    Bug #550: Invisible meshes collision and tooltip\n    Bug #551: Performance drop when loading multiple ESM files\n    Bug #552: Don't list CG in options if it is not available\n    Bug #555: Character creation windows \"OK\" button broken\n    Bug #558: Segmentation fault when Alt-tabbing with console opened\n    Bug #559: Dialog window should not be available before character creation is finished\n    Bug #560: Tooltip borders should be stretched\n    Bug #562: Sound should not be played when an object cannot be picked up\n    Bug #565: Water animation speed + timescale\n    Bug #572: Better Bodies' textures don't work\n    Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod)\n    Bug #574: Moving left/right should not cancel auto-run\n    Bug #575: Crash entering the Chamber of Song\n    Bug #576: Missing includes\n    Bug #577: Left Gloves Addon causes ESMReader exception\n    Bug #579: Unable to open container \"Kvama Egg Sack\"\n    Bug #581: Mimicking vanilla Morrowind water\n    Bug #583: Gender not recognized\n    Bug #586: Wrong char gen behaviour\n    Bug #587: \"End\" script statements with spaces don't work\n    Bug #589: Closing message boxes by pressing the activation key\n    Bug #590: Ugly Dagoth Ur rendering\n    Bug #591: Race selection issues\n    Bug #593: Persuasion response should be random\n    Bug #595: Footless guard\n    Bug #599: Waterfalls are invisible from a certain distance\n    Bug #600: Waterfalls rendered incorrectly, cut off by water\n    Bug #607: New beast bodies mod crashes\n    Bug #608: Crash in cell \"Mournhold, Royal Palace\"\n    Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt\n    Bug #613: Messagebox causing assert to fail\n    Bug #615: Meshes invisible from above water\n    Bug #617: Potion effects should be hidden until discovered\n    Bug #619: certain moss hanging from tree has rendering bug\n    Bug #621: Batching bloodmoon's trees\n    Bug #623: NiMaterialProperty alpha unhandled\n    Bug #628: Launcher in latest master crashes the game\n    Bug #633: Crash on startup: Better Heads\n    Bug #636: Incorrect Char Gen Menu Behavior\n    Feature #29: Allow ESPs and multiple ESMs\n    Feature #94: Finish class selection-dialogue\n    Feature #149: Texture Alphas\n    Feature #237: Run Morrowind-ini importer from launcher\n    Feature #286: Update Active Spell Icons\n    Feature #334: Swimming animation\n    Feature #335: Walking animation\n    Feature #360: Proper collision shapes for NPCs and creatures\n    Feature #367: Lights that behave more like original morrowind implementation\n    Feature #477: Special local scripting variables\n    Feature #528: Message boxes should close when enter is pressed under certain conditions.\n    Feature #543: Add bsa files to the settings imported by the ini importer\n    Feature #594: coordinate space and utility functions\n    Feature #625: Zoom in vanity mode\n    Task #464: Refactor launcher ESX selector into a re-usable component\n    Task #624: Unified implementation of type-variable sub-records\n\n0.21.0\n------\n\n    Bug #253: Dialogs don't work for Russian version of Morrowind\n    Bug #267: Activating creatures without dialogue can still activate the dialogue GUI\n    Bug #354: True flickering lights\n    Bug #386: The main menu's first entry is wrong (in french)\n    Bug #479: Adding the spell \"Ash Woe Blight\" to the player causes strange attribute oscillations\n    Bug #495: Activation Range\n    Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned\n    Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available\n    Bug #500: Disposition for most NPCs is 0/100\n    Bug #501: Getdisposition command wrongly returns base disposition\n    Bug #506: Journal UI doesn't update anymore\n    Bug #507: EnableRestMenu is not a valid command - change it to EnableRest\n    Bug #508: Crash in Ald Daedroth Shrine\n    Bug #517: Wrong price calculation when untrading an item\n    Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin\n    Bug #524: Beast races are able to wear shoes\n    Bug #527: Background music fails to play\n    Bug #533: The arch at Gnisis entrance is not displayed\n    Bug #534: Terrain gets its correct shape only some time after the cell is loaded\n    Bug #536: The same entry can be added multiple times to the journal\n    Bug #539: Race selection is broken\n    Bug #544: Terrain normal map corrupt when the map is rendered\n    Feature #39: Video Playback\n    Feature #151: ^-escape sequences in text output\n    Feature #392: Add AI related script functions\n    Feature #456: Determine required ini fallback values and adjust the ini importer accordingly\n    Feature #460: Experimental DirArchives improvements\n    Feature #540: Execute scripts of objects in containers/inventories in active cells\n    Task #401: Review GMST fixing\n    Task #453: Unify case smashing/folding\n    Task #512: Rewrite utf8 component\n\n0.20.0\n------\n\n    Bug #366: Changing the player's race during character creation does not change the look of the player character\n    Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell\n    Bug #437: Stop animations when paused\n    Bug #438: Time displays as \"0 a.m.\" when it should be \"12 a.m.\"\n    Bug #439: Text in \"name\" field of potion/spell creation window is persistent\n    Bug #440: Starting date at a new game is off by one day\n    Bug #442: Console window doesn't close properly sometimes\n    Bug #448: Do not break container window formatting when item names are very long\n    Bug #458: Topics sometimes not automatically added to known topic list\n    Bug #476: Auto-Moving allows player movement after using DisablePlayerControls\n    Bug #478: After sleeping in a bed the rest dialogue window opens automtically again\n    Bug #492: On creating potions the ingredients are removed twice\n    Feature #63: Mercantile skill\n    Feature #82: Persuasion Dialogue\n    Feature #219: Missing dialogue filters/functions\n    Feature #369: Add a FailedAction\n    Feature #377: Select head/hair on character creation\n    Feature #391: Dummy AI package classes\n    Feature #435: Global Map, 2nd Layer\n    Feature #450: Persuasion\n    Feature #457: Add more script instructions\n    Feature #474: update the global variable pcrace when the player's race is changed\n    Task #158: Move dynamically generated classes from Player class to World Class\n    Task #159: ESMStore rework and cleanup\n    Task #163: More Component Namespace Cleanup\n    Task #402: Move player data from MWWorld::Player to the player's NPC record\n    Task #446: Fix no namespace in BulletShapeLoader\n\n0.19.0\n------\n\n    Bug #374: Character shakes in 3rd person mode near the origin\n    Bug #404: Gamma correct rendering\n    Bug #407: Shoes of St. Rilm do not work\n    Bug #408: Rugs has collision even if they are not supposed to\n    Bug #412: Birthsign menu sorted incorrectly\n    Bug #413: Resolutions presented multiple times in launcher\n    Bug #414: launcher.cfg file stored in wrong directory\n    Bug #415: Wrong esm order in openmw.cfg\n    Bug #418: Sound listener position updates incorrectly\n    Bug #423: wrong usage of \"Version\" entry in openmw.desktop\n    Bug #426: Do not use hardcoded splash images\n    Bug #431: Don't use markers for raycast\n    Bug #432: Crash after picking up items from an NPC\n    Feature #21/#95: Sleeping/resting\n    Feature #61: Alchemy Skill\n    Feature #68: Death\n    Feature #69/#86: Spell Creation\n    Feature #72/#84: Travel\n    Feature #76: Global Map, 1st Layer\n    Feature #120: Trainer Window\n    Feature #152: Skill Increase from Skill Books\n    Feature #160: Record Saving\n    Task #400: Review GMST access\n\n0.18.0\n------\n\n    Bug #310: Button of the \"preferences menu\" are too small\n    Bug #361: Hand-to-hand skill is always 100\n    Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to\n    Bug #372: playSound3D uses original coordinates instead of current coordinates.\n    Bug #373: Static OGRE build faulty\n    Bug #375: Alt-tab toggle view\n    Bug #376: Screenshots are disable\n    Bug #378: Exception when drinking self-made potions\n    Bug #380: Cloth visibility problem\n    Bug #384: Weird character on doors tooltip.\n    Bug #398: Some objects do not collide in MW, but do so in OpenMW\n    Feature #22: Implement level-up\n    Feature #36: Hide Marker\n    Feature #88: Hotkey Window\n    Feature #91: Level-Up Dialogue\n    Feature #118: Keyboard and Mouse-Button bindings\n    Feature #119: Spell Buying Window\n    Feature #133: Handle resources across multiple data directories\n    Feature #134: Generate a suitable default-value for --data-local\n    Feature #292: Object Movement/Creation Script Instructions\n    Feature #340: AIPackage data structures\n    Feature #356: Ingredients use\n    Feature #358: Input system rewrite\n    Feature #370: Target handling in actions\n    Feature #379: Door markers on the local map\n    Feature #389: AI framework\n    Feature #395: Using keys to open doors / containers\n    Feature #396: Loading screens\n    Feature #397: Inventory avatar image and race selection head preview\n    Task #339: Move sounds into Action\n\n0.17.0\n------\n\n    Bug #225: Valgrind reports about 40MB of leaked memory\n    Bug #241: Some physics meshes still don't match\n    Bug #248: Some textures are too dark\n    Bug #300: Dependency on proprietary CG toolkit\n    Bug #302: Some objects don't collide although they should\n    Bug #308: Freeze in Balmora, Meldor: Armorer\n    Bug #313: openmw without a ~/.config/openmw folder segfault.\n    Bug #317: adding non-existing spell via console locks game\n    Bug #318: Wrong character normals\n    Bug #341: Building with Ogre Debug libraries does not use debug version of plugins\n    Bug #347: Crash when running openmw with --start=\"XYZ\"\n    Bug #353: FindMyGUI.cmake breaks path on Windows\n    Bug #359: WindowManager throws exception at destruction\n    Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation\n    Feature #33: Allow objects to cross cell-borders\n    Feature #59: Dropping Items (replaced stopgap implementation with a proper one)\n    Feature #93: Main Menu\n    Feature #96/329/330/331/332/333: Player Control\n    Feature #180: Object rotation and scaling.\n    Feature #272: Incorrect NIF material sharing\n    Feature #314: Potion usage\n    Feature #324: Skill Gain\n    Feature #342: Drain/fortify dynamic stats/attributes magic effects\n    Feature #350: Allow console only script instructions\n    Feature #352: Run scripts in console on startup\n    Task #107: Refactor mw*-subsystems\n    Task #325: Make CreatureStats into a class\n    Task #345: Use Ogre's animation system\n    Task #351: Rewrite Action class to support automatic sound playing\n\n0.16.0\n------\n\n    Bug #250: OpenMW launcher erratic behaviour\n    Bug #270: Crash because of underwater effect on OS X\n    Bug #277: Auto-equipping in some cells not working\n    Bug #294: Container GUI ignores disabled inventory menu\n    Bug #297: Stats review dialog shows all skills and attribute values as 0\n    Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses\n    Bug #299: Crash in World::disable\n    Bug #306: Non-existent ~/.config/openmw \"crash\" the launcher.\n    Bug #307: False \"Data Files\" location make the launcher \"crash\"\n    Feature #81: Spell Window\n    Feature #85: Alchemy Window\n    Feature #181: Support for x.y script syntax\n    Feature #242: Weapon and Spell icons\n    Feature #254: Ingame settings window\n    Feature #293: Allow \"stacking\" game modes\n    Feature #295: Class creation dialog tooltips\n    Feature #296: Clicking on the HUD elements should show/hide the respective window\n    Feature #301: Direction after using a Teleport Door\n    Feature #303: Allow object selection in the console\n    Feature #305: Allow the use of = as a synonym for ==\n    Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts\n    Task #176: Restructure enabling/disabling of MW-references\n    Task #283: Integrate ogre.cfg file in settings file\n    Task #290: Auto-Close MW-reference related GUI windows\n\n0.15.0\n------\n\n    Bug #5: Physics reimplementation (fixes various issues)\n    Bug #258: Resizing arrow's background is not transparent\n    Bug #268: Widening the stats window in X direction causes layout problems\n    Bug #269: Topic pane in dialgoue window is too small for some longer topics\n    Bug #271: Dialog choices are sorted incorrectly\n    Bug #281: The single quote character is not rendered on dialog windows\n    Bug #285: Terrain not handled properly in cells that are not predefined\n    Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs\n    Feature #15: Collision with Terrain\n    Feature #17: Inventory-, Container- and Trade-Windows\n    Feature #44: Floating Labels above Focussed Objects\n    Feature #80: Tooltips\n    Feature #83: Barter Dialogue\n    Feature #90: Book and Scroll Windows\n    Feature #156: Item Stacking in Containers\n    Feature #213: Pulsating lights\n    Feature #218: Feather & Burden\n    Feature #256: Implement magic effect bookkeeping\n    Feature #259: Add missing information to Stats window\n    Feature #260: Correct case for dialogue topics\n    Feature #280: GUI texture atlasing\n    Feature #291: Ability to use GMST strings from GUI layout files\n    Task #255: Make MWWorld::Environment into a singleton\n\n0.14.0\n------\n\n    Bug #1: Meshes rendered with wrong orientation\n    Bug #6/Task #220: Picking up small objects doesn't always work\n    Bug #127: tcg doesn't work\n    Bug #178: Compablity problems with Ogre 1.8.0 RC 1\n    Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI\n    Bug #227: Terrain crashes when moving away from predefined cells\n    Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces\n    Bug #235: TGA texture loading problem\n    Bug #246: wireframe mode does not work in water\n    Feature #8/#232: Water Rendering\n    Feature #13: Terrain Rendering\n    Feature #37: Render Path Grid\n    Feature #66: Factions\n    Feature #77: Local Map\n    Feature #78: Compass/Mini-Map\n    Feature #97: Render Clothing/Armour\n    Feature #121: Window Pinning\n    Feature #205: Auto equip\n    Feature #217: Contiainer should track changes to its content\n    Feature #221: NPC Dialogue Window Enhancements\n    Feature #233: Game settings manager\n    Feature #240: Spell List and selected spell (no GUI yet)\n    Feature #243: Draw State\n    Task #113: Morrowind.ini Importer\n    Task #215: Refactor the sound code\n    Task #216: Update MyGUI\n\n0.13.0\n------\n\n    Bug #145: Fixed sound problems after cell change\n    Bug #179: Pressing space in console triggers activation\n    Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux\n    Bug #189: ASCII 16 character added to console on it's activation on Mac OS X\n    Bug #190: Case Folding fails with music files\n    Bug #192: Keypresses write Text into Console no matter which gui element is active\n    Bug #196: Collision shapes out of place\n    Bug #202: ESMTool doesn't not work with localised ESM files anymore\n    Bug #203: Torch lights only visible on short distance\n    Bug #207: Ogre.log not written\n    Bug #209: Sounds do not play\n    Bug #210: Ogre crash at Dren plantation\n    Bug #214: Unsupported file format version\n    Bug #222: Launcher is writing openmw.cfg file to wrong location\n    Feature #9: NPC Dialogue Window\n    Feature #16/42: New sky/weather implementation\n    Feature #40: Fading\n    Feature #48: NPC Dialogue System\n    Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet)\n    Feature #161: Load REC_PGRD records\n    Feature #195: Wireframe-mode\n    Feature #198/199: Various sound effects\n    Feature #206: Allow picking data path from launcher if non is set\n    Task #108: Refactor window manager class\n    Task #172: Sound Manager Cleanup\n    Task #173: Create OpenEngine systems in the appropriate manager classes\n    Task #184: Adjust MSVC and gcc warning levels\n    Task #185: RefData rewrite\n    Task #201: Workaround for transparency issues\n    Task #208: silenced esm_reader.hpp warning\n\n0.12.0\n------\n\n    Bug #154: FPS Drop\n    Bug #169: Local scripts continue running if associated object is deleted\n    Bug #174: OpenMW fails to start if the config directory doesn't exist\n    Bug #187: Missing lighting\n    Bug #188: Lights without a mesh are not rendered\n    Bug #191: Taking screenshot causes crash when running installed\n    Feature #28: Sort out the cell load problem\n    Feature #31: Allow the player to move away from pre-defined cells\n    Feature #35: Use alternate storage location for modified object position\n    Feature #45: NPC animations\n    Feature #46: Creature Animation\n    Feature #89: Basic Journal Window\n    Feature #110: Automatically pick up the path of existing MW-installations\n    Feature #183: More FPS display settings\n    Task #19: Refactor engine class\n    Task #109/Feature #162: Automate Packaging\n    Task #112: Catch exceptions thrown in input handling functions\n    Task #128/#168: Cleanup Configuration File Handling\n    Task #131: NPC Activation doesn't work properly\n    Task #144: MWRender cleanup\n    Task #155: cmake cleanup\n\n0.11.1\n------\n\n    Bug #2: Resources loading doesn't work outside of bsa files\n    Bug #3: GUI does not render non-English characters\n    Bug #7: openmw.cfg location doesn't match\n    Bug #124: The TCL alias for ToggleCollision is missing.\n    Bug #125: Some command line options can't be used from a .cfg file\n    Bug #126: Toggle-type script instructions are less verbose compared with original MW\n    Bug #130: NPC-Record Loading fails for some NPCs\n    Bug #167: Launcher sets invalid parameters in ogre config\n    Feature #10: Journal\n    Feature #12: Rendering Optimisations\n    Feature #23: Change Launcher GUI to a tabbed interface\n    Feature #24: Integrate the OGRE settings window into the launcher\n    Feature #25: Determine openmw.cfg location (Launcher)\n    Feature #26: Launcher Profiles\n    Feature #79: MessageBox\n    Feature #116: Tab-Completion in Console\n    Feature #132: --data-local and multiple --data\n    Feature #143: Non-Rendering Performance-Optimisations\n    Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs\n    Feature #157: Version Handling\n    Task #14: Replace tabs with 4 spaces\n    Task #18: Move components from global namespace into their own namespace\n    Task #123: refactor header files in components/esm\n\n0.10.0\n------\n\n* NPC dialogue window (not functional yet)\n* Collisions with objects\n* Refactor the PlayerPos class\n* Adjust file locations\n* CMake files and test linking for Bullet\n* Replace Ogre raycasting test for activation with something more precise\n* Adjust player movement according to collision results\n* FPS display\n* Various Portability Improvements\n* Mac OS X support is back!\n\n0.9.0\n-----\n\n* Exterior cells loading, unloading and management\n* Character Creation GUI\n* Character creation\n* Make cell names case insensitive when doing internal lookups\n* Music player\n* NPCs rendering\n\n0.8.0\n-----\n\n* GUI\n* Complete and working script engine\n* In game console\n* Sky rendering\n* Sound and music\n* Tons of smaller stuff\n\n0.7.0\n-----\n\n* This release is a complete rewrite in C++.\n* All D code has been culled, and all modules have been rewritten.\n* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase.\n\n0.6.0\n-----\n\n* Coded a GUI system using MyGUI\n* Skinned MyGUI to look like Morrowind (work in progress)\n* Integrated the Monster script engine\n* Rewrote some functions into script code\n* Very early MyGUI < > Monster binding\n* Fixed Windows sound problems (replaced old openal32.dll)\n\n0.5.0\n-----\n\n* Collision detection with Bullet\n* Experimental walk & fall character physics\n* New key bindings:\n  * t toggle physics mode (walking, flying, ghost),\n  * n night eye, brightens the scene\n* Fixed incompatability with DMD 1.032 and newer compilers\n* * (thanks to tomqyp)\n* Various minor changes and updates\n\n0.4.0\n-----\n\n* Switched from Audiere to OpenAL\n* * (BIG thanks to Chris Robinson)\n* Added complete Makefile (again) as a alternative build tool\n* More realistic lighting (thanks again to Chris Robinson)\n* Various localization fixes tested with Russian and French versions\n* Temporary workaround for the Unicode issue: invalid UTF displayed as '?'\n* Added ns option to disable sound, for debugging\n* Various bug fixes\n* Cosmetic changes to placate gdc Wall\n\n0.3.0\n-----\n\n* Built and tested on Windows XP\n* Partial support for FreeBSD (exceptions do not work)\n* You no longer have to download Monster separately\n* Made an alternative for building without DSSS (but DSSS still works)\n* Renamed main program from 'morro' to 'openmw'\n* Made the config system more robust\n* Added oc switch for showing Ogre config window on startup\n* Removed some config files, these are auto generated when missing.\n* Separated plugins.cfg into linux and windows versions.\n* Updated Makefile and sources for increased portability\n* confirmed to work against OIS 1.0.0 (Ubuntu repository package)\n\n0.2.0\n-----\n\n* Compiles with gdc\n* Switched to DSSS for building D code\n* Includes the program esmtool\n\n0.1.0\n-----\n\nfirst release\n"
  },
  {
    "path": "CHANGELOG_PR.md",
    "content": "*** PLEASE PUT YOUR ISSUE DESCRIPTION FOR DUMMIES HERE FOR REVIEW ***\n\n- I'm just a placeholder description (#1337)\n- I'm also just a placeholder description, but I'm a more recent one (#42)\n\n***\n\n0.47.0\n------\n\nThe OpenMW team is proud to announce the release of version 0.47.0! Grab it from our Downloads Page for all operating systems. ***short summary: XXX ***\n\nCheck out the release video (***add link***) and the OpenMW-CS release video (***add link***) by the ***add flattering adjective*** Atahualpa, and see below for the full list of changes.\n\nKnown Issues:\n- To use generic Linux binaries, Qt4 and libpng12 must be installed on your system\n- On macOS, launching OpenMW from OpenMW-CS requires OpenMW.app and OpenMW-CS.app to be siblings\n\nNew Features:\n- Dialogue to split item stacks now displays the name of the trapped soul for stacks of soul gems (#5362)\n- Basics of Collada animations are now supported via osgAnimation plugin (#5456)\n\nNew Editor Features:\n- Instance selection modes are now implemented (centred cube, corner-dragged cube, sphere) with four user-configurable actions (select only, add to selection, remove from selection, invert selection) (#3171)\n\nBug Fixes:\n- NiParticleColorModifier in NIF files is now properly handled which solves issues regarding particle effects, e.g., smoke and fire (#1952, #3676)\n- Targetting non-unique actors in scripts is now supported (#2311)\n- Guards no longer ignore attacks of invisible players but rather initiate dialogue and flee if the player resists being arrested (#4774)\n- Changing the dialogue window without closing it no longer clears the dialogue history in order to allow, e.g., emulation of three-way dialogue via ForceGreeting (#5358)\n- Scripts which try to start a non-existent global script now skip that step and continue execution instead of breaking (#5364)\n- Selecting already equipped spells or magic items via hotkey no longer triggers the equip sound to play (#5367)\n- 'Scale' argument in levelled creature lists is now taken into account when spawning creatures from such lists (#5369)\n- Morrowind legacy madness: Using a key on a trapped door/container now only disarms the trap if the door/container is locked (#5370)\n\nEditor Bug Fixes:\n- Deleted and moved objects within a cell are now saved properly (#832)\n- Disabled record sorting in Topic and Journal Info tables, implemented drag-move for records (#4357)\n- Topic and Journal Info records can now be cloned with a different parent Topic/Journal Id (#4363)\n- Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400)\n- Cell borders are now properly redrawn when undoing/redoing terrain changes (#5473)\n- Loading mods now keeps the master index (#5675)\n- Flicker and crashing on XFCE4 fixed (#5703)\n- Collada models render properly in the Editor (#5713)\n- Terrain-selection grid is now properly updated when undoing/redoing terrain changes (#6022)\n- Tool outline and select/edit actions in \"Terrain land editing\" mode now ignore references (#6023)\n- Primary-select and secondary-select actions in \"Terrain land editing\" mode now behave like in \"Instance editing\" mode (#6024)\n- Using the circle brush to select terrain in the \"Terrain land editing\" mode no longer selects vertices outside the circle (#6035)\n- Vertices at the NW and SE corners of a cell can now also be selected in \"Terrain land editing\" mode if the adjacent cells aren't loaded yet (#6036)\n\nMiscellaneous:\n- Prevent save-game bloating by using an appropriate fog texture format (#5108)\n- Ensure that 'Enchantment autocalc\" flag is treated as flag in OpenMW-CS and in our esm tools (#5363)\n"
  },
  {
    "path": "CI/ActivateMSVC.ps1",
    "content": "& \"${env:COMSPEC}\" /c ActivateMSVC.bat \"&&\" set | ForEach-Object {\n    if ($_.Contains(\"=\")) {\n        $name, $value = $_ -split '=', 2\n        Set-Content env:\\\"$name\" $value\n    }\n}\n\n$MissingTools = $false\n$tools = \"cl\", \"link\", \"rc\", \"mt\"\n$descriptions = \"MSVC Compiler\", \"MSVC Linker\", \"MS Windows Resource Compiler\", \"MS Windows Manifest Tool\"\nfor ($i = 0; $i -lt $tools.Length; $i++) {\n    $present = $true\n    try {\n        Get-Command $tools[$i] *>&1 | Out-Null\n        $present = $present -and $?\n    } catch {\n        $present = $false\n    }\n    if (!$present) {\n        Write-Warning \"$($tools[$i]) ($($descriptions[$i])) missing.\"\n        $MissingTools = $true\n    }\n}\n\nif ($MissingTools) {\n    Write-Error \"Some build tools were unavailable after activating MSVC in the shell. It's likely that your Visual Studio $MSVC_DISPLAY_YEAR installation needs repairing.\"\n    exit 1\n}"
  },
  {
    "path": "CI/activate_msvc.sh",
    "content": "#!/bin/bash\n\noldSettings=$-\nset -eu\n\nfunction restoreOldSettings {\n    if [[ $oldSettings != *e* ]]; then\n        set +e\n    fi\n    if [[ $oldSettings != *u* ]]; then\n        set +u\n    fi\n}\n\nif [[ \"${BASH_SOURCE[0]}\" == \"${0}\" ]]; then\n    echo \"Error: Script not sourced.\"\n    echo \"You must source this script for it to work, i.e. \"\n    echo \"source ./activate_msvc.sh\"\n    echo \"or\"\n    echo \". ./activate_msvc.sh\"\n    restoreOldSettings\n    exit 1\nfi\n\ncommand -v unixPathAsWindows >/dev/null 2>&1 || function unixPathAsWindows {\n\tif command -v cygpath >/dev/null 2>&1; then\n\t\tcygpath -w $1\n\telse\n\t\techo \"$1\" | sed \"s,^/\\([^/]\\)/,\\\\1:/,\" | sed \"s,/,\\\\\\\\,g\"\n\tfi\n}\n\n\n# capture CMD environment in a shell with MSVC activated\ncmd //c \"$(unixPathAsWindows \"$(dirname \"${BASH_SOURCE[0]}\")\")\\ActivateMSVC.bat\" \"&&\" \"bash\" \"-c\" \"declare -px > declared_env.sh\"\nsource ./declared_env.sh\nrm declared_env.sh\n\nMISSINGTOOLS=0\n\ncommand -v cl >/dev/null 2>&1 || { echo \"Error: cl (MSVC Compiler) missing.\"; MISSINGTOOLS=1; }\ncommand -v link >/dev/null 2>&1 || { echo \"Error: link (MSVC Linker) missing.\"; MISSINGTOOLS=1; }\ncommand -v rc >/dev/null 2>&1 || { echo \"Error: rc (MS Windows Resource Compiler) missing.\"; MISSINGTOOLS=1; }\ncommand -v mt >/dev/null 2>&1 || { echo \"Error: mt (MS Windows Manifest Tool) missing.\"; MISSINGTOOLS=1; }\n\nif [ $MISSINGTOOLS -ne 0 ]; then\n    echo \"Some build tools were unavailable after activating MSVC in the shell. It's likely that your Visual Studio $MSVC_DISPLAY_YEAR installation needs repairing.\"\n    restoreOldSettings\n    return 1\nfi\n\nrestoreOldSettings\n"
  },
  {
    "path": "CI/before_install.android.sh",
    "content": "#!/bin/sh -ex\n\ncurl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20201129.zip -o ~/openmw-android-deps.zip\nunzip -o ~/openmw-android-deps -d /usr/lib/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null\n"
  },
  {
    "path": "CI/before_install.linux.sh",
    "content": "#!/bin/sh -ex\necho -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-\n\n# Set up compilers\nif [ ! -z \"${MATRIX_CC}\" ]; then\n    eval \"${MATRIX_CC}\"\nfi\n\ncd ~/\ngit clone https://github.com/TES3MP/CrabNet\ncd CrabNet\ncmake . -DCRABNET_ENABLE_DLL=OFF -DCRABNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release\nmake -j3\n"
  },
  {
    "path": "CI/before_install.osx.sh",
    "content": "#!/bin/sh -ex\n\n# workaround python issue on travis\n[ -z \"${TRAVIS}\" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true\n[ -z \"${TRAVIS}\" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || true\n[ -z \"${TRAVIS}\" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true\n\n# Some of these tools can come from places other than brew, so check before installing\ncommand -v ccache >/dev/null 2>&1 || brew install ccache\ncommand -v cmake >/dev/null 2>&1 || brew install cmake\ncommand -v qmake >/dev/null 2>&1 || brew install qt@5\nexport PATH=\"/usr/local/opt/qt@5/bin:$PATH\"  # needed to use qmake in none default path as qt now points to qt6\n\nccache --version\ncmake --version\nqmake --version\n\ncurl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20210617.zip -o ~/openmw-deps.zip\nunzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null\n\n# additional libraries\n[ -z \"${TRAVIS}\" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew install fontconfig"
  },
  {
    "path": "CI/before_script.android.sh",
    "content": "#!/bin/sh -ex\n\n# hack to work around: FFmpeg version is too old, 3.2 is required\nsed -i s/\"NOT FFVER_OK\"/\"FALSE\"/ CMakeLists.txt\n\nmkdir -p build\ncd build\n\ncmake \\\n-DCMAKE_TOOLCHAIN_FILE=/usr/lib/android-sdk/ndk-bundle/build/cmake/android.toolchain.cmake \\\n-DANDROID_ABI=arm64-v8a \\\n-DANDROID_PLATFORM=android-21 \\\n-DCMAKE_C_COMPILER_LAUNCHER=ccache \\\n-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \\\n-DCMAKE_INSTALL_PREFIX=install \\\n-DBUILD_BSATOOL=0 \\\n-DBUILD_NIFTEST=0 \\\n-DBUILD_ESMTOOL=0 \\\n-DBUILD_LAUNCHER=0 \\\n-DBUILD_MWINIIMPORTER=0 \\\n-DBUILD_ESSIMPORTER=0 \\\n-DBUILD_OPENCS=0 \\\n-DBUILD_WIZARD=0 \\\n-DOPENMW_USE_SYSTEM_MYGUI=OFF \\\n-DOPENMW_USE_SYSTEM_OSG=OFF \\\n-DOPENMW_USE_SYSTEM_BULLET=OFF \\\n..\n"
  },
  {
    "path": "CI/before_script.linux.sh",
    "content": "#!/bin/bash\n\nset -xeo pipefail\n\nfree -m\n\nBUILD_UNITTESTS=OFF\nBUILD_BENCHMARKS=OFF\n\nif [[ \"${BUILD_TESTS_ONLY}\" ]]; then\n    export GOOGLETEST_DIR=\"${PWD}/googletest/build/install\"\n    env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh\n    BUILD_UNITTESTS=ON\n    BUILD_BENCHMARKS=ON\nfi\n\ndeclare -a CMAKE_CONF_OPTS=(\n    -DCMAKE_C_COMPILER=\"${CC:-/usr/bin/cc}\"\n    -DCMAKE_CXX_COMPILER=\"${CXX:-/usr/bin/c++}\"\n    -DCMAKE_C_COMPILER_LAUNCHER=ccache\n    -DCMAKE_CXX_COMPILER_LAUNCHER=ccache\n    -DCMAKE_INSTALL_PREFIX=install\n    -DCMAKE_BUILD_TYPE=RelWithDebInfo\n    -DBUILD_SHARED_LIBS=OFF\n    -DUSE_SYSTEM_TINYXML=ON\n    -DCMAKE_INSTALL_PREFIX=install\n    -DRakNet_LIBRARY_RELEASE=~/CrabNet/lib/libRakNetLibStatic.a\n    -DRakNet_LIBRARY_DEBUG=~/CrabNet/lib/libRakNetLibStatic.a\n)\n\nif [[ $CI_OPENMW_USE_STATIC_DEPS ]]; then\n    CMAKE_CONF_OPTS+=(\n        -DOPENMW_USE_SYSTEM_MYGUI=OFF\n        -DOPENMW_USE_SYSTEM_OSG=OFF\n        -DOPENMW_USE_SYSTEM_BULLET=OFF\n    )\nfi\n\nmkdir -p build\ncd build\n\n# Set up compilers\nif [ ! -z \"${MATRIX_CC}\" ]; then\n    eval \"${MATRIX_CC}\"\nfi\n\nexport RAKNET_ROOT=~/CrabNet\n\nif [[ \"${BUILD_TESTS_ONLY}\" ]]; then\n    ${ANALYZE} cmake \\\n        \"${CMAKE_CONF_OPTS[@]}\" \\\n        -DBUILD_OPENMW=OFF \\\n        -DBUILD_OPENMW_MP=OFF \\\n        -DBUILD_BSATOOL=OFF \\\n        -DBUILD_ESMTOOL=OFF \\\n        -DBUILD_LAUNCHER=OFF \\\n        -DBUILD_BROWSER=OFF \\\n        -DBUILD_MWINIIMPORTER=OFF \\\n        -DBUILD_ESSIMPORTER=OFF \\\n        -DBUILD_OPENCS=OFF \\\n        -DBUILD_WIZARD=OFF \\\n        -DBUILD_UNITTESTS=${BUILD_UNITTESTS} \\\n        -DBUILD_BENCHMARKS=${BUILD_BENCHMARKS} \\\n        -DGTEST_ROOT=\"${GOOGLETEST_DIR}\" \\\n        -DGMOCK_ROOT=\"${GOOGLETEST_DIR}\" \\\n        ..\nelse\n    ${ANALYZE} cmake \\\n        \"${CMAKE_CONF_OPTS[@]}\" \\\n        ..\nfi\n"
  },
  {
    "path": "CI/before_script.msvc.sh",
    "content": "#!/bin/bash\n# set -x  # turn-on for debugging\n\nfunction wrappedExit {\n\tif [[ \"${BASH_SOURCE[0]}\" == \"${0}\" ]]; then\n\t\texit $1\n\telse\n\t\treturn $1\n\tfi\n}\n\nMISSINGTOOLS=0\n\ncommand -v 7z >/dev/null 2>&1 || { echo \"Error: 7z (7zip) is not on the path.\"; MISSINGTOOLS=1; }\ncommand -v cmake >/dev/null 2>&1 || { echo \"Error: cmake (CMake) is not on the path.\"; MISSINGTOOLS=1; }\n\nMISSINGPYTHON=0\nif ! command -v python >/dev/null 2>&1; then\n\techo \"Warning: Python is not on the path, automatic Qt installation impossible.\"\n\tMISSINGPYTHON=1\nelif ! python --version >/dev/null 2>&1; then\n\techo \"Warning: Python is (probably) fake stub Python that comes bundled with newer versions of Windows, automatic Qt installation impossible.\"\n\techo \"If you think you have Python installed, try changing the order of your PATH environment variable in Advanced System Settings.\"\n\tMISSINGPYTHON=1\nfi\n\nif [ $MISSINGTOOLS -ne 0 ]; then\n\twrappedExit 1\nfi\n\nWORKINGDIR=\"$(pwd)\"\ncase \"$WORKINGDIR\" in\n\t*[[:space:]]*)\n\t\techo \"Error: Working directory contains spaces.\"\n\t\twrappedExit 1\n\t\t;;\nesac\n\nset -euo pipefail\n\nfunction windowsPathAsUnix {\n\tif command -v cygpath >/dev/null 2>&1; then\n\t\tcygpath -u $1\n\telse\n\t\techo \"$1\" | sed \"s,\\\\\\\\,/,g\" | sed \"s,\\(.\\):,/\\\\1,\"\n\tfi\n}\n\nfunction unixPathAsWindows {\n\tif command -v cygpath >/dev/null 2>&1; then\n\t\tcygpath -w $1\n\telse\n\t\techo \"$1\" | sed \"s,^/\\([^/]\\)/,\\\\1:/,\" | sed \"s,/,\\\\\\\\,g\"\n\tfi\n}\n\nAPPVEYOR=${APPVEYOR:-}\nCI=${CI:-}\nSTEP=${STEP:-}\n\nVERBOSE=\"\"\nSTRIP=\"\"\nSKIP_DOWNLOAD=\"\"\nSKIP_EXTRACT=\"\"\nKEEP=\"\"\nUNITY_BUILD=\"\"\nVS_VERSION=\"\"\nNMAKE=\"\"\nNINJA=\"\"\nPDBS=\"\"\nPLATFORM=\"\"\nCONFIGURATIONS=()\nTEST_FRAMEWORK=\"\"\nGOOGLE_INSTALL_ROOT=\"\"\nINSTALL_PREFIX=\".\"\nBUILD_BENCHMARKS=\"\"\n\nACTIVATE_MSVC=\"\"\nSINGLE_CONFIG=\"\"\n\nwhile [ $# -gt 0 ]; do\n\tARGSTR=$1\n\tshift\n\n\tif [ ${ARGSTR:0:1} != \"-\" ]; then\n\t\techo \"Unknown argument $ARGSTR\"\n\t\techo \"Try '$0 -h'\"\n\t\twrappedExit 1\n\tfi\n\n\tfor (( i=1; i<${#ARGSTR}; i++ )); do\n\t\tARG=${ARGSTR:$i:1}\n\t\tcase $ARG in\n\t\t\tV )\n\t\t\t\tVERBOSE=true ;;\n\n\t\t\td )\n\t\t\t\tSKIP_DOWNLOAD=true ;;\n\n\t\t\te )\n\t\t\t\tSKIP_EXTRACT=true ;;\n\n\t\t\tk )\n\t\t\t\tKEEP=true ;;\n\n\t\t\tu )\n\t\t\t\tUNITY_BUILD=true ;;\n\n\t\t\tv )\n\t\t\t\tVS_VERSION=$1\n\t\t\t\tshift ;;\n\n\t\t\tn )\n\t\t\t\tNMAKE=true ;;\n\t\t\t\n\t\t\tN )\n\t\t\t\tNINJA=true ;;\n\n\t\t\tp )\n\t\t\t\tPLATFORM=$1\n\t\t\t\tshift ;;\n\n\t\t\tP )\n\t\t\t\tPDBS=true ;;\n\n\t\t\tc )\n\t\t\t\tCONFIGURATIONS+=( $1 )\n\t\t\t\tshift ;;\n\n\t\t\tt )\n\t\t\t\tTEST_FRAMEWORK=true ;;\n\n\t\t\ti )\n\t\t\t\tINSTALL_PREFIX=$(echo \"$1\" | sed 's;\\\\;/;g' | sed -E 's;/+;/;g')\n\t\t\t\tshift ;;\n\n\t\t\tb )\n\t\t\t\tBUILD_BENCHMARKS=true ;;\n\n\t\t\th )\n\t\t\t\tcat <<EOF\nUsage: $0 [-cdehkpuvVi]\nOptions:\n\t-c <Release/Debug/RelWithDebInfo>\n\t\tSet the configuration, can also be set with environment variable CONFIGURATION.\n\t\tFor mutli-config generators, this is ignored, and all configurations are set up.\n\t\tFor single-config generators, several configurations can be set up at once by specifying -c multiple times.\n\t-d\n\t\tSkip checking the downloads.\n\t-e\n\t\tSkip extracting dependencies.\n\t-h\n\t\tShow this message.\n\t-k\n\t\tKeep the old build directory, default is to delete it.\n\t-p <Win32/Win64>\n\t\tSet the build platform, can also be set with environment variable PLATFORM.\n\t-t\n\t\tBuild unit tests / Google test\n\t-u\n\t\tConfigure for unity builds.\n\t-v <2017/2019>\n\t\tChoose the Visual Studio version to use.\n\t-n\n\t\tProduce NMake makefiles instead of a Visual Studio solution. Cannot be used with -N.\n\t-N\n\t\tProduce Ninja (multi-config if CMake is new enough to support it) files instead of a Visual Studio solution. Cannot be used with -n..\n\t-P\n\t\tDownload debug symbols where available\n\t-V\n\t\tRun verbosely\n\t-i\n\t\tCMake install prefix\n\t-b\n\t\tBuild benchmarks\nEOF\n\t\t\t\twrappedExit 0\n\t\t\t\t;;\n\n\t\t\t* )\n\t\t\t\techo \"Unknown argument $ARG.\"\n\t\t\t\techo \"Try '$0 -h'\"\n\t\t\t\twrappedExit 1 ;;\n\t\tesac\n\tdone\ndone\n\nif [ -n \"$NMAKE\" ] || [ -n \"$NINJA\" ]; then\n\tif [ -n \"$NMAKE\" ] && [ -n \"$NINJA\" ]; then\n\t\techo \"Cannot run in NMake and Ninja mode at the same time.\"\n\t\twrappedExit 1\n\tfi\n\tACTIVATE_MSVC=true\nfi\n\nif [ -z $VERBOSE ]; then\n\tSTRIP=\"> /dev/null 2>&1\"\nfi\n\nif [ -z $APPVEYOR ]; then\n\techo \"Running prebuild outside of Appveyor.\"\n\n\tDIR=$(windowsPathAsUnix \"${BASH_SOURCE[0]}\")\n\tcd $(dirname \"$DIR\")/..\nelse\n\techo \"Running prebuild in Appveyor.\"\n\n\tcd \"$APPVEYOR_BUILD_FOLDER\"\nfi\n\nrun_cmd() {\n\tCMD=\"$1\"\n\tshift\n\n\tif [ -z $VERBOSE ]; then\n\t\tRET=0\n\t\teval $CMD $@ > output.log 2>&1 || RET=$?\n\n\t\tif [ $RET -ne 0 ]; then\n\t\t\tif [ -z $APPVEYOR ]; then\n\t\t\t\techo \"Command $CMD failed, output can be found in $(real_pwd)/output.log\"\n\t\t\telse\n\t\t\t\techo\n\t\t\t\techo \"Command $CMD failed;\"\n\t\t\t\tcat output.log\n\t\t\tfi\n\t\telse\n\t\t\trm output.log\n\t\tfi\n\n\t\treturn $RET\n\telse\n\t\tRET=0\n\t\teval $CMD $@ || RET=$?\n\t\treturn $RET\n\tfi\n}\n\ndownload() {\n\tif [ $# -lt 3 ]; then\n\t\techo \"Invalid parameters to download.\"\n\t\treturn 1\n\tfi\n\n\tNAME=$1\n\tshift\n\n\techo \"$NAME...\"\n\n\twhile [ $# -gt 1 ]; do\n\t\tURL=$1\n\t\tFILE=$2\n\t\tshift\n\t\tshift\n\n\t\tif ! [ -f $FILE ]; then\n\t\t\tprintf \"  Downloading $FILE... \"\n\n\t\t\tif [ -z $VERBOSE ]; then\n\t\t\t\tRET=0\n\t\t\t\tcurl --silent --retry 10 -Ly 5 -o $FILE $URL || RET=$?\n\t\t\telse\n\t\t\t\tRET=0\n\t\t\t\tcurl --retry 10 -Ly 5 -o $FILE $URL || RET=$?\n\t\t\tfi\n\n\t\t\tif [ $RET -ne 0 ]; then\n\t\t\t\techo \"Failed!\"\n\t\t\t\twrappedExit $RET\n\t\t\telse\n\t\t\t\techo \"Done.\"\n\t\t\tfi\n\t\telse\n\t\t\techo \"  $FILE exists, skipping.\"\n\t\tfi\n\tdone\n\n\tif [ $# -ne 0 ]; then\n\t\techo \"Missing parameter.\"\n\tfi\n}\n\nreal_pwd() {\n\tif type cygpath >/dev/null 2>&1; then\n\t\tcygpath -am \"$PWD\"\n\telse\n\t\tpwd # not git bash, Cygwin or the like\n\tfi\n}\n\nCMAKE_OPTS=\"\"\nadd_cmake_opts() {\n\tCMAKE_OPTS=\"$CMAKE_OPTS $@\"\n}\n\ndeclare -A RUNTIME_DLLS\nRUNTIME_DLLS[\"Release\"]=\"\"\nRUNTIME_DLLS[\"Debug\"]=\"\"\nRUNTIME_DLLS[\"RelWithDebInfo\"]=\"\"\nadd_runtime_dlls() {\n\tlocal CONFIG=$1\n\tshift\n\tRUNTIME_DLLS[$CONFIG]=\"${RUNTIME_DLLS[$CONFIG]} $@\"\n}\n\ndeclare -A OSG_PLUGINS\nOSG_PLUGINS[\"Release\"]=\"\"\nOSG_PLUGINS[\"Debug\"]=\"\"\nOSG_PLUGINS[\"RelWithDebInfo\"]=\"\"\nadd_osg_dlls() {\n\tlocal CONFIG=$1\n\tshift\n\tOSG_PLUGINS[$CONFIG]=\"${OSG_PLUGINS[$CONFIG]} $@\"\n}\n\ndeclare -A QT_PLATFORMS\nQT_PLATFORMS[\"Release\"]=\"\"\nQT_PLATFORMS[\"Debug\"]=\"\"\nQT_PLATFORMS[\"RelWithDebInfo\"]=\"\"\nadd_qt_platform_dlls() {\n\tlocal CONFIG=$1\n\tshift\n\tQT_PLATFORMS[$CONFIG]=\"${QT_PLATFORMS[$CONFIG]} $@\"\n}\n\ndeclare -A QT_STYLES\nQT_STYLES[\"Release\"]=\"\"\nQT_STYLES[\"Debug\"]=\"\"\nQT_STYLES[\"RelWithDebInfo\"]=\"\"\nadd_qt_style_dlls() {\n\tlocal CONFIG=$1\n\tshift\n\tQT_STYLES[$CONFIG]=\"${QT_STYLES[$CONFIG]} $@\"\n}\n\nif [ -z $PLATFORM ]; then\n\tPLATFORM=\"$(uname -m)\"\nfi\n\nif [ -z $VS_VERSION ]; then\n\tVS_VERSION=\"2017\"\nfi\n\ncase $VS_VERSION in\n\t16|16.0|2019 )\n\t\tGENERATOR=\"Visual Studio 16 2019\"\n\t\tTOOLSET=\"vc142\"\n\t\tMSVC_REAL_VER=\"16\"\n\t\tMSVC_VER=\"14.2\"\n\t\tMSVC_YEAR=\"2015\"\n\t\tMSVC_REAL_YEAR=\"2019\"\n\t\tMSVC_DISPLAY_YEAR=\"2019\"\n\t\tBOOST_VER=\"1.71.0\"\n\t\tBOOST_VER_URL=\"1_71_0\"\n\t\tBOOST_VER_SDK=\"107100\"\n\t\t;;\n\n\t15|15.0|2017 )\n\t\tGENERATOR=\"Visual Studio 15 2017\"\n\t\tTOOLSET=\"vc141\"\n\t\tMSVC_REAL_VER=\"15\"\n\t\tMSVC_VER=\"14.1\"\n\t\tMSVC_YEAR=\"2015\"\n\t\tMSVC_REAL_YEAR=\"2017\"\n\t\tMSVC_DISPLAY_YEAR=\"2017\"\n\t\tBOOST_VER=\"1.67.0\"\n\t\tBOOST_VER_URL=\"1_67_0\"\n\t\tBOOST_VER_SDK=\"106700\"\n\t\t;;\n\n\t14|14.0|2015 )\n\t\techo \"Visual Studio 2015 is no longer supported\"\n\t\twrappedExit 1\n\t\t;;\n\n\t12|12.0|2013 )\n\t\techo \"Visual Studio 2013 is no longer supported\"\n\t\twrappedExit 1\n\t\t;;\nesac\n\ncase $PLATFORM in\n\tx64|x86_64|x86-64|win64|Win64 )\n\t\tARCHNAME=\"x86-64\"\n\t\tARCHSUFFIX=\"64\"\n\t\tBITS=\"64\"\n\t\t;;\n\n\tx32|x86|i686|i386|win32|Win32 )\n\t\tARCHNAME=\"x86\"\n\t\tARCHSUFFIX=\"86\"\n\t\tBITS=\"32\"\n\t\t;;\n\n\t* )\n\t\techo \"Unknown platform $PLATFORM.\"\n\t\twrappedExit 1\n\t\t;;\nesac\n\nif [ $BITS -eq 64 ] && [ $MSVC_REAL_VER -lt 16 ]; then\n\tGENERATOR=\"${GENERATOR} Win64\"\nfi\n\nif [ -n \"$NMAKE\" ]; then\n\tGENERATOR=\"NMake Makefiles\"\n\tSINGLE_CONFIG=true\nfi\n\nif [ -n \"$NINJA\" ]; then\n\tGENERATOR=\"Ninja Multi-Config\"\n\tif ! cmake -E capabilities | grep -F \"$GENERATOR\" > /dev/null; then\n\t\tSINGLE_CONFIG=true\n\t\tGENERATOR=\"Ninja\"\n\tfi\nfi\n\nif [ -n \"$SINGLE_CONFIG\" ]; then\n\tif [ ${#CONFIGURATIONS[@]} -eq 0 ]; then\n\t\tif [ -n \"${CONFIGURATION:-}\" ]; then\n\t\t\tCONFIGURATIONS=(\"$CONFIGURATION\")\n\t\telse\n\t\t\tCONFIGURATIONS=(\"Debug\")\n\t\tfi\n\telif [ ${#CONFIGURATIONS[@]} -ne 1 ]; then\n\t\t# It's simplest just to recursively call the script a few times.\n\t\tRECURSIVE_OPTIONS=()\n\t\tif [ -n \"$VERBOSE\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-V\")\n\t\tfi\n\t\tif [ -n \"$SKIP_DOWNLOAD\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-d\")\n\t\tfi\n\t\tif [ -n \"$SKIP_EXTRACT\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-e\")\n\t\tfi\n\t\tif [ -n \"$KEEP\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-k\")\n\t\tfi\n\t\tif [ -n \"$UNITY_BUILD\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-u\")\n\t\tfi\n\t\tif [ -n \"$NMAKE\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-n\")\n\t\tfi\n\t\tif [ -n \"$NINJA\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-N\")\n\t\tfi\n\t\tif [ -n \"$PDBS\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-P\")\n\t\tfi\n\t\tif [ -n \"$TEST_FRAMEWORK\" ]; then\n\t\t\tRECURSIVE_OPTIONS+=(\"-t\")\n\t\tfi\n\t\tRECURSIVE_OPTIONS+=(\"-v $VS_VERSION\")\n\t\tRECURSIVE_OPTIONS+=(\"-p $PLATFORM\")\n\t\tRECURSIVE_OPTIONS+=(\"-i '$INSTALL_PREFIX'\")\n\n\t\tfor config in ${CONFIGURATIONS[@]}; do\n\t\t\t$0 ${RECURSIVE_OPTIONS[@]} -c $config\n\t\tdone\n\n\t\twrappedExit 1\n\tfi\nelse\n\tif [ ${#CONFIGURATIONS[@]} -ne 0 ]; then\n\t\techo \"Ignoring configurations argument - generator is multi-config\"\n\tfi\n\tCONFIGURATIONS=(\"Release\" \"Debug\" \"RelWithDebInfo\")\nfi\n\nfor i in ${!CONFIGURATIONS[@]}; do\n\tcase ${CONFIGURATIONS[$i]} in\n\t\tdebug|Debug|DEBUG )\n\t\t\tCONFIGURATIONS[$i]=Debug\n\t\t\t;;\n\n\t\trelease|Release|RELEASE )\n\t\t\tCONFIGURATIONS[$i]=Release\n\t\t\t;;\n\n\t\trelwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )\n\t\t\tCONFIGURATIONS[$i]=RelWithDebInfo\n\t\t\t;;\n\tesac\ndone\n\nif [ $MSVC_REAL_VER -ge 16 ] && [ -z \"$NMAKE\" ] && [ -z \"$NINJA\" ]; then\n\tif [ $BITS -eq 64 ]; then\n\t\tadd_cmake_opts \"-G\\\"$GENERATOR\\\" -A x64\"\n\telse\n\t\tadd_cmake_opts \"-G\\\"$GENERATOR\\\" -A Win32\"\n\tfi\nelse\n\tadd_cmake_opts \"-G\\\"$GENERATOR\\\"\"\nfi\n\nif [ -n \"$SINGLE_CONFIG\" ]; then\n\tadd_cmake_opts \"-DCMAKE_BUILD_TYPE=${CONFIGURATIONS[0]}\"\nfi\n\nif ! [ -z $UNITY_BUILD ]; then\n\tadd_cmake_opts \"-DOPENMW_UNITY_BUILD=True\"\nfi\n\necho\necho \"===================================\"\necho \"Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}\"\necho \"===================================\"\necho\n\n# cd OpenMW/AppVeyor-test\nmkdir -p deps\ncd deps\n\nDEPS=\"$(pwd)\"\n\nif [ -z $SKIP_DOWNLOAD ]; then\n\techo \"Downloading dependency packages.\"\n\techo\n\n\t# Boost\n\tif [ -z $APPVEYOR ]; then\n\t\tdownload \"Boost ${BOOST_VER}\" \\\n\t\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/boost_${BOOST_VER_URL}-msvc-${MSVC_VER}-${BITS}.exe\" \\\n\t\t\t\"boost-${BOOST_VER}-msvc${MSVC_VER}-win${BITS}.exe\"\n\tfi\n\n\t# Bullet\n\tdownload \"Bullet 2.89\" \\\n\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z\" \\\n\t\t\"Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z\"\n\n\t# FFmpeg\n\tdownload \"FFmpeg 4.2.2\" \\\n\t  \"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-win${BITS}.zip\" \\\n\t\t\"ffmpeg-4.2.2-win${BITS}.zip\" \\\n\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-dev-win${BITS}.zip\" \\\n\t\t\"ffmpeg-4.2.2-dev-win${BITS}.zip\"\n\n\t# MyGUI\n\tdownload \"MyGUI 3.4.0\" \\\n\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z\" \\\n\t\t\"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z\"\n\n\tif [ -n \"$PDBS\" ]; then\n\t\tdownload \"MyGUI symbols\" \\\n\t\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z\" \\\n\t\t\t\"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z\"\n\tfi\n\n\t# OpenAL\n\tdownload \"OpenAL-Soft 1.20.1\" \\\n\t  \"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OpenAL-Soft-1.20.1.zip\" \\\n\t\t\"OpenAL-Soft-1.20.1.zip\"\n\n\t# OSG\n\tdownload \"OpenSceneGraph 3.6.5\" \\\n\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z\" \\\n\t\t\"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z\"\n\n\tif [ -n \"$PDBS\" ]; then\n\t\tdownload \"OpenSceneGraph symbols\" \\\n\t\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z\" \\\n\t\t\t\"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z\"\n\tfi\n\n\t# SDL2\n\tdownload \"SDL 2.0.12\" \\\n\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-2.0.12.zip\" \\\n\t\t\"SDL2-2.0.12.zip\"\n\n\t# LZ4\n\tdownload \"LZ4 1.9.2\" \\\n\t\t\"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/lz4_win${BITS}_v1_9_2.7z\" \\\n\t\t\"lz4_win${BITS}_v1_9_2.7z\"\n\n\t# Google test and mock\n\tif [ ! -z $TEST_FRAMEWORK ]; then\n\t\techo \"Google test 1.10.0...\"\n\t\tif [ -d googletest ]; then\n\t\t\tprintf \"  Google test exists, skipping.\"\n\t\telse\n\t\t\tgit clone -b release-1.10.0 https://github.com/google/googletest.git\n\t\tfi\n\tfi\nfi\n\ncd .. #/..\n\n# Set up dependencies\nBUILD_DIR=\"MSVC${MSVC_DISPLAY_YEAR}_${BITS}\"\n\nif [ -n \"$NMAKE\" ]; then\n\tBUILD_DIR=\"${BUILD_DIR}_NMake\"\nelif [ -n \"$NINJA\" ]; then\n\tBUILD_DIR=\"${BUILD_DIR}_Ninja\"\nfi\n\nif [ -n \"$SINGLE_CONFIG\" ]; then\n\tBUILD_DIR=\"${BUILD_DIR}_${CONFIGURATIONS[0]}\"\nfi\n\nif [ -z $KEEP ]; then\n\techo\n\techo \"(Re)Creating build directory.\"\n\n\trm -rf \"$BUILD_DIR\"\nfi\n\nmkdir -p \"${BUILD_DIR}/deps\"\ncd \"${BUILD_DIR}/deps\"\n\nDEPS_INSTALL=\"$(pwd)\"\ncd $DEPS\n\necho\necho \"Extracting dependencies, this might take a while...\"\necho \"---------------------------------------------------\"\necho\n\n\n# Boost\nif [ -z $APPVEYOR ]; then\n\tprintf \"Boost ${BOOST_VER}... \"\nelse\n\tprintf \"Boost ${BOOST_VER} AppVeyor... \"\nfi\n{\n\tif [ -z $APPVEYOR ]; then\n\t\tcd $DEPS_INSTALL\n\n\t\tBOOST_SDK=\"$(real_pwd)/Boost\"\n\n\t\t# Boost's installer is still based on ms-dos API that doesn't support larger than 260 char path names\n\t\t# We work around this by installing to root of the current working drive and then move it to our deps\n\t\t# get the current working drive's root, we'll install to that temporarily\n\t\tCWD_DRIVE_ROOT=\"$(powershell -command '(get-location).Drive.Root')Boost_temp\"\n\t\tCWD_DRIVE_ROOT_BASH=$(windowsPathAsUnix \"$CWD_DRIVE_ROOT\")\n\t\tif [ -d CWD_DRIVE_ROOT_BASH ]; then\n\t\t\tprintf \"Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. \";\n\t\t\twrappedExit 1;\n\t\tfi\n\n\t\tif [ -d ${BOOST_SDK} ] && grep \"BOOST_VERSION ${BOOST_VER_SDK}\" Boost/boost/version.hpp > /dev/null; then\n\t\t\tprintf \"Exists. \"\n\t\telif [ -z $SKIP_EXTRACT ]; then\n\t\t\trm -rf Boost\n\t\t\tCI_EXTRA_INNO_OPTIONS=\"\"\n\t\t\t[ -n \"$CI\" ] && CI_EXTRA_INNO_OPTIONS=\"//SUPPRESSMSGBOXES //LOG='boost_install.log'\"\n\t\t\t\"${DEPS}/boost-${BOOST_VER}-msvc${MSVC_VER}-win${BITS}.exe\" //DIR=\"${CWD_DRIVE_ROOT}\" //VERYSILENT //NORESTART ${CI_EXTRA_INNO_OPTIONS}\n\t\t\tmv \"${CWD_DRIVE_ROOT_BASH}\" \"${BOOST_SDK}\"\n\t\tfi\n\t\tadd_cmake_opts -DBOOST_ROOT=\"$BOOST_SDK\" \\\n\t\t\t-DBOOST_LIBRARYDIR=\"${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}\"\n\t\tadd_cmake_opts -DBoost_COMPILER=\"-${TOOLSET}\"\n\t\techo Done.\n\telse\n\t\t# Appveyor has all the boost we need already\n\t\tBOOST_SDK=\"c:/Libraries/boost_${BOOST_VER_URL}\"\n\n\t\tadd_cmake_opts -DBOOST_ROOT=\"$BOOST_SDK\" \\\n\t\t\t-DBOOST_LIBRARYDIR=\"${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.1\"\n\t\tadd_cmake_opts -DBoost_COMPILER=\"-${TOOLSET}\"\n\n\t\techo Done.\n\tfi\n}\ncd $DEPS\necho\n# Bullet\nprintf \"Bullet 2.89... \"\n{\n\tcd $DEPS_INSTALL\n\tif [ -d Bullet ]; then\n\t\tprintf -- \"Exists. (No version checking) \"\n\telif [ -z $SKIP_EXTRACT ]; then\n\t\trm -rf Bullet\n\t\teval 7z x -y \"${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z\" $STRIP\n\t\tmv \"Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double\" Bullet\n\tfi\n\tadd_cmake_opts -DBULLET_ROOT=\"$(real_pwd)/Bullet\"\n\techo Done.\n}\ncd $DEPS\necho\n# FFmpeg\nprintf \"FFmpeg 4.2.2... \"\n{\n\tcd $DEPS_INSTALL\n\tif [ -d FFmpeg ] && grep \"4.2.2\" FFmpeg/README.txt > /dev/null; then\n\t\tprintf \"Exists. \"\n\telif [ -z $SKIP_EXTRACT ]; then\n\t\trm -rf FFmpeg\n\t\teval 7z x -y \"${DEPS}/ffmpeg-4.2.2-win${BITS}.zip\" $STRIP\n\t\teval 7z x -y \"${DEPS}/ffmpeg-4.2.2-dev-win${BITS}.zip\" $STRIP\n\t\tmv \"ffmpeg-4.2.2-win${BITS}-shared\" FFmpeg\n\t\tcp -r \"ffmpeg-4.2.2-win${BITS}-dev/\"* FFmpeg/\n\t\trm -rf \"ffmpeg-4.2.2-win${BITS}-dev\"\n\tfi\n\texport FFMPEG_HOME=\"$(real_pwd)/FFmpeg\"\n\tfor config in ${CONFIGURATIONS[@]}; do\n\t\tadd_runtime_dlls $config \"$(pwd)/FFmpeg/bin/\"{avcodec-58,avformat-58,avutil-56,swresample-3,swscale-5}.dll\n\tdone\n\tif [ $BITS -eq 32 ]; then\n\t\tadd_cmake_opts \"-DCMAKE_EXE_LINKER_FLAGS=\\\"/machine:X86 /safeseh:no\\\"\"\n\tfi\n\techo Done.\n}\ncd $DEPS\necho\n# MyGUI\nprintf \"MyGUI 3.4.0... \"\n{\n\tcd $DEPS_INSTALL\n\tif [ -d MyGUI ] && \\\n\t\tgrep \"MYGUI_VERSION_MAJOR 3\" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \\\n\t\tgrep \"MYGUI_VERSION_MINOR 4\" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \\\n\t\tgrep \"MYGUI_VERSION_PATCH 0\" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null\n\tthen\n\t\tprintf \"Exists. \"\n\telif [ -z $SKIP_EXTRACT ]; then\n\t\trm -rf MyGUI\n\t\teval 7z x -y \"${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z\" $STRIP\n\t\t[ -n \"$PDBS\" ] && eval 7z x -y \"${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z\" $STRIP\n\t\tmv \"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}\" MyGUI\n\tfi\n\texport MYGUI_HOME=\"$(real_pwd)/MyGUI\"\n\tfor CONFIGURATION in ${CONFIGURATIONS[@]}; do\n\t\tif [ $CONFIGURATION == \"Debug\" ]; then\n\t\t\tSUFFIX=\"_d\"\n\t\t\tMYGUI_CONFIGURATION=\"Debug\"\n\t\telse\n\t\t\tSUFFIX=\"\"\n\t\t\tMYGUI_CONFIGURATION=\"RelWithDebInfo\"\n\t\tfi\n\t\tadd_runtime_dlls $CONFIGURATION \"$(pwd)/MyGUI/bin/${MYGUI_CONFIGURATION}/MyGUIEngine${SUFFIX}.dll\"\n\tdone\n\techo Done.\n}\ncd $DEPS\necho\n# OpenAL\nprintf \"OpenAL-Soft 1.20.1... \"\n{\n\tif [ -d openal-soft-1.20.1-bin ]; then\n\t\tprintf \"Exists. \"\n\telif [ -z $SKIP_EXTRACT ]; then\n\t\trm -rf openal-soft-1.20.1-bin\n\t\teval 7z x -y OpenAL-Soft-1.20.1.zip $STRIP\n\tfi\n\tOPENAL_SDK=\"$(real_pwd)/openal-soft-1.20.1-bin\"\n\tadd_cmake_opts -DOPENAL_INCLUDE_DIR=\"${OPENAL_SDK}/include/AL\" \\\n\t\t-DOPENAL_LIBRARY=\"${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib\"\n\tfor config in ${CONFIGURATIONS[@]}; do\n\t\tadd_runtime_dlls $config \"$(pwd)/openal-soft-1.20.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll\"\n\tdone\n\techo Done.\n}\ncd $DEPS\necho\n# OSG\nprintf \"OSG 3.6.5... \"\n{\n\tcd $DEPS_INSTALL\n\tif [ -d OSG ] && \\\n\t\tgrep \"OPENSCENEGRAPH_MAJOR_VERSION    3\" OSG/include/osg/Version > /dev/null && \\\n\t\tgrep \"OPENSCENEGRAPH_MINOR_VERSION    6\" OSG/include/osg/Version > /dev/null && \\\n\t\tgrep \"OPENSCENEGRAPH_PATCH_VERSION    5\" OSG/include/osg/Version > /dev/null\n\tthen\n\t\tprintf \"Exists. \"\n\telif [ -z $SKIP_EXTRACT ]; then\n\t\trm -rf OSG\n\t\teval 7z x -y \"${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z\" $STRIP\n\t\t[ -n \"$PDBS\" ] && eval 7z x -y \"${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z\" $STRIP\n\t\tmv \"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}\" OSG\n\tfi\n\tOSG_SDK=\"$(real_pwd)/OSG\"\n\tadd_cmake_opts -DOSG_DIR=\"$OSG_SDK\"\n\tfor CONFIGURATION in ${CONFIGURATIONS[@]}; do\n\t\tif [ $CONFIGURATION == \"Debug\" ]; then\n\t\t\tSUFFIX=\"d\"\n\t\telse\n\t\t\tSUFFIX=\"\"\n\t\tfi\n\t\tadd_runtime_dlls $CONFIGURATION \"$(pwd)/OSG/bin/\"{OpenThreads,zlib,libpng}${SUFFIX}.dll \\\n\t\t\t\"$(pwd)/OSG/bin/osg\"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll\n\t\tadd_osg_dlls $CONFIGURATION \"$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_\"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll\n\t\tadd_osg_dlls $CONFIGURATION \"$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_serializers_osg\"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll\n\tdone\n\techo Done.\n}\ncd $DEPS\necho\n# Qt\nif [ -z $APPVEYOR ]; then\n\tprintf \"Qt 5.15.0... \"\nelse\n\tprintf \"Qt 5.13 AppVeyor... \"\nfi\n{\n\tif [ $BITS -eq 64 ]; then\n\t\tSUFFIX=\"_64\"\n\telse\n\t\tSUFFIX=\"\"\n\tfi\n\tif [ -z $APPVEYOR ]; then\n\t\tcd $DEPS_INSTALL\n\n\t\tqt_version=\"5.15.0\"\n\t\tif [ \"win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}\" == \"win64_msvc2017_64\" ]; then\n\t\t\techo \"This combination of options is known not to work. Falling back to Qt 5.14.2.\"\n\t\t\tqt_version=\"5.14.2\"\n\t\tfi\n\n\t\tQT_SDK=\"$(real_pwd)/Qt/${qt_version}/msvc${MSVC_REAL_YEAR}${SUFFIX}\"\n\n\t\tif [ -d \"Qt/${qt_version}\" ]; then\n\t\t\tprintf \"Exists. \"\n\t\telif [ -z $SKIP_EXTRACT ]; then\n\t\t\tif [ $MISSINGPYTHON -ne 0 ]; then\n\t\t\t\techo \"Can't be automatically installed without Python.\"\n\t\t\t\twrappedExit 1\n\t\t\tfi\n\n\t\t\tpushd \"$DEPS\" > /dev/null\n\t\t\tif ! [ -d 'aqt-venv' ]; then\n\t\t\t\techo \"  Creating Virtualenv for aqt...\"\n\t\t\t\trun_cmd python -m venv aqt-venv\n\t\t\tfi\n\t\t\tif [ -d 'aqt-venv/bin' ]; then\n\t\t\t\tVENV_BIN_DIR='bin'\n\t\t\telif [ -d 'aqt-venv/Scripts' ]; then\n\t\t\t\tVENV_BIN_DIR='Scripts'\n\t\t\telse\n\t\t\t\techo \"Error: Failed to create virtualenv in expected location.\"\n\t\t\t\twrappedExit 1\n\t\t\tfi\n\n\t\t\t# check version\n\t\t\taqt-venv/${VENV_BIN_DIR}/pip list | grep 'aqtinstall\\s*1.1.3' || [ $? -ne 0 ]\n\t\t\tif [ $? -eq 0 ]; then\n\t\t\t\techo \"  Installing aqt wheel into virtualenv...\"\n\t\t\t\trun_cmd \"aqt-venv/${VENV_BIN_DIR}/pip\" install aqtinstall==1.1.3\n\t\t\tfi\n\t\t\tpopd > /dev/null\n\n\t\t\trm -rf Qt\n\n\t\t\tmkdir Qt\n\t\t\tcd Qt\n\n\t\t\trun_cmd \"${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt\" install $qt_version windows desktop \"win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}\"\n\n\t\t\tprintf \"  Cleaning up extraneous data... \"\n\t\t\trm -rf Qt/{aqtinstall.log,Tools}\n\n\t\t\techo Done.\n\t\tfi\n\n\t\tcd $QT_SDK\n\t\tadd_cmake_opts -DQT_QMAKE_EXECUTABLE=\"${QT_SDK}/bin/qmake.exe\" \\\n\t\t\t-DCMAKE_PREFIX_PATH=\"$QT_SDK\"\n\t\tfor CONFIGURATION in ${CONFIGURATIONS[@]}; do\n\t\t\tif [ $CONFIGURATION == \"Debug\" ]; then\n\t\t\t\tDLLSUFFIX=\"d\"\n\t\t\telse\n\t\t\t\tDLLSUFFIX=\"\"\n\t\t\tfi\n\t\t\tadd_runtime_dlls $CONFIGURATION \"$(pwd)/bin/Qt5\"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll\n\t\t\tadd_qt_platform_dlls $CONFIGURATION \"$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll\"\n\t\t\tadd_qt_style_dlls $CONFIGURATION \"$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll\"\n\t\tdone\n\t\techo Done.\n\telse\n\t\tQT_SDK=\"C:/Qt/5.13/msvc2017${SUFFIX}\"\n\t\tadd_cmake_opts -DQT_QMAKE_EXECUTABLE=\"${QT_SDK}/bin/qmake.exe\" \\\n\t\t\t-DCMAKE_PREFIX_PATH=\"$QT_SDK\"\n\t\tfor CONFIGURATION in ${CONFIGURATIONS[@]}; do\n\t\t\tif [ $CONFIGURATION == \"Debug\" ]; then\n\t\t\t\tDLLSUFFIX=\"d\"\n\t\t\telse\n\t\t\t\tDLLSUFFIX=\"\"\n\t\t\tfi\n\t\t\tDIR=$(windowsPathAsUnix \"${QT_SDK}\")\n\t\t\tadd_runtime_dlls $CONFIGURATION \"${DIR}/bin/Qt5\"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll\n\t\t\tadd_qt_platform_dlls $CONFIGURATION \"${DIR}/plugins/platforms/qwindows${DLLSUFFIX}.dll\"\n\t\t\tadd_qt_style_dlls $CONFIGURATION \"${DIR}/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll\"\n\t\tdone\n\t\techo Done.\n\tfi\n}\ncd $DEPS\necho\n# SDL2\nprintf \"SDL 2.0.12... \"\n{\n\tif [ -d SDL2-2.0.12 ]; then\n\t\tprintf \"Exists. \"\n\telif [ -z $SKIP_EXTRACT ]; then\n\t\trm -rf SDL2-2.0.12\n\t\teval 7z x -y SDL2-2.0.12.zip $STRIP\n\tfi\n\texport SDL2DIR=\"$(real_pwd)/SDL2-2.0.12\"\n\tfor config in ${CONFIGURATIONS[@]}; do\n\t\tadd_runtime_dlls $config \"$(pwd)/SDL2-2.0.12/lib/x${ARCHSUFFIX}/SDL2.dll\"\n\tdone\n\techo Done.\n}\ncd $DEPS\necho\n# LZ4\nprintf \"LZ4 1.9.2... \"\n{\n\tif [ -d LZ4_1.9.2 ]; then\n\t\tprintf \"Exists. \"\n\telif [ -z $SKIP_EXTRACT ]; then\n\t\trm -rf LZ4_1.9.2\n\t\teval 7z x -y lz4_win${BITS}_v1_9_2.7z -o$(real_pwd)/LZ4_1.9.2 $STRIP\n\tfi\n\texport LZ4DIR=\"$(real_pwd)/LZ4_1.9.2\"\n\tadd_cmake_opts -DLZ4_INCLUDE_DIR=\"${LZ4DIR}/include\" \\\n\t\t-DLZ4_LIBRARY=\"${LZ4DIR}/lib/liblz4.lib\"\n\tfor CONFIGURATION in ${CONFIGURATIONS[@]}; do\n\t\tif [ $CONFIGURATION == \"Debug\" ]; then\n\t\t\tLZ4_CONFIGURATION=\"Debug\"\n\t\telse\n\t\t\tSUFFIX=\"\"\n\t\t\tLZ4_CONFIGURATION=\"Release\"\n\t\tfi\n\t\tadd_runtime_dlls $CONFIGURATION \"$(pwd)/LZ4_1.9.2/bin/${LZ4_CONFIGURATION}/liblz4.dll\"\n\tdone\n\techo Done.\n}\ncd $DEPS\necho\n# Google Test and Google Mock\nif [ ! -z $TEST_FRAMEWORK ]; then\n\tprintf \"Google test 1.10.0 ...\"\n\n\tcd googletest\n\tmkdir -p build${MSVC_REAL_YEAR}\n\n\tcd build${MSVC_REAL_YEAR}\n\n\tGOOGLE_INSTALL_ROOT=\"${DEPS_INSTALL}/GoogleTest\"\n\t\n\tfor CONFIGURATION in ${CONFIGURATIONS[@]}; do\n\t\t# FindGMock.cmake mentions Release explicitly, but not RelWithDebInfo. Only one optimised library config can be used, so go for the safer one.\n\t\tGTEST_CONFIG=$([ $CONFIGURATION == \"RelWithDebInfo\" ] && echo \"Release\" || echo \"$CONFIGURATION\" )\n\t\tif [ $GTEST_CONFIG == \"Debug\" ]; then\n\t\t\tDEBUG_SUFFIX=\"d\"\n\t\telse\n\t\t\tDEBUG_SUFFIX=\"\"\n\t\tfi\n\n\t\tif [ ! -f \"$GOOGLE_INSTALL_ROOT/lib/gtest${DEBUG_SUFFIX}.lib\" ]; then\n\t\t\t# Always use MSBuild solution files as they don't need the environment activating\n\t\t\tcmake .. -DCMAKE_USE_WIN32_THREADS_INIT=1 -G \"Visual Studio $MSVC_REAL_VER $MSVC_REAL_YEAR$([ $BITS -eq 64 ] && [ $MSVC_REAL_VER -lt 16 ] && echo \" Win64\")\" $([ $MSVC_REAL_VER -ge 16 ] && echo \"-A $([ $BITS -eq 64 ] && echo \"x64\" || echo \"Win32\")\") -DBUILD_SHARED_LIBS=1\n\t\t\tcmake --build . --config \"${GTEST_CONFIG}\"\n\t\t\tcmake --install . --config \"${GTEST_CONFIG}\" --prefix \"${GOOGLE_INSTALL_ROOT}\"\n\t\tfi\n\n\t\tadd_runtime_dlls $CONFIGURATION \"${GOOGLE_INSTALL_ROOT}\\bin\\gtest_main${DEBUG_SUFFIX}.dll\"\n\t\tadd_runtime_dlls $CONFIGURATION \"${GOOGLE_INSTALL_ROOT}\\bin\\gtest${DEBUG_SUFFIX}.dll\"\n\t\tadd_runtime_dlls $CONFIGURATION \"${GOOGLE_INSTALL_ROOT}\\bin\\gmock_main${DEBUG_SUFFIX}.dll\"\n\t\tadd_runtime_dlls $CONFIGURATION \"${GOOGLE_INSTALL_ROOT}\\bin\\gmock${DEBUG_SUFFIX}.dll\"\n\tdone\n\n\tadd_cmake_opts -DBUILD_UNITTESTS=yes\n\t# FindGTest and FindGMock do not work perfectly on Windows\n\t# but we can help them by telling them everything we know about installation\n\tadd_cmake_opts -DGMOCK_ROOT=\"$GOOGLE_INSTALL_ROOT\"\n\tadd_cmake_opts -DGTEST_ROOT=\"$GOOGLE_INSTALL_ROOT\"\n\tadd_cmake_opts -DGTEST_LIBRARY=\"$GOOGLE_INSTALL_ROOT/lib/gtest.lib\"\n\tadd_cmake_opts -DGTEST_MAIN_LIBRARY=\"$GOOGLE_INSTALL_ROOT/lib/gtest_main.lib\"\n\tadd_cmake_opts -DGMOCK_LIBRARY=\"$GOOGLE_INSTALL_ROOT/lib/gmock.lib\"\n\tadd_cmake_opts -DGMOCK_MAIN_LIBRARY=\"$GOOGLE_INSTALL_ROOT/lib/gmock_main.lib\"\n\tadd_cmake_opts -DGTEST_LIBRARY_DEBUG=\"$GOOGLE_INSTALL_ROOT/lib/gtestd.lib\"\n\tadd_cmake_opts -DGTEST_MAIN_LIBRARY_DEBUG=\"$GOOGLE_INSTALL_ROOT/lib/gtest_maind.lib\"\n\tadd_cmake_opts -DGMOCK_LIBRARY_DEBUG=\"$GOOGLE_INSTALL_ROOT/lib/gmockd.lib\"\n\tadd_cmake_opts -DGMOCK_MAIN_LIBRARY_DEBUG=\"$GOOGLE_INSTALL_ROOT/lib/gmock_maind.lib\"\n\tadd_cmake_opts -DGTEST_LINKED_AS_SHARED_LIBRARY=True\n\tadd_cmake_opts -DGTEST_LIBRARY_TYPE=SHARED\n\tadd_cmake_opts -DGTEST_MAIN_LIBRARY_TYPE=SHARED\n\n\techo Done.\n\nfi\n\necho\ncd $DEPS_INSTALL/..\necho\necho \"Setting up OpenMW build...\"\nadd_cmake_opts -DOPENMW_MP_BUILD=on\nadd_cmake_opts -DCMAKE_INSTALL_PREFIX=\"${INSTALL_PREFIX}\"\nif [ ! -z $CI ]; then\n\tcase $STEP in\n\t\tcomponents )\n\t\t\techo \"  Building subproject: Components.\"\n\t\t\tadd_cmake_opts -DBUILD_ESSIMPORTER=no \\\n\t\t\t\t-DBUILD_LAUNCHER=no \\\n\t\t\t\t-DBUILD_MWINIIMPORTER=no \\\n\t\t\t\t-DBUILD_OPENCS=no \\\n\t\t\t\t-DBUILD_OPENMW=no \\\n\t\t\t\t-DBUILD_WIZARD=no\n\t\t\t;;\n\t\topenmw )\n\t\t\techo \"  Building subproject: OpenMW.\"\n\t\t\tadd_cmake_opts -DBUILD_ESSIMPORTER=no \\\n\t\t\t\t-DBUILD_LAUNCHER=no \\\n\t\t\t\t-DBUILD_MWINIIMPORTER=no \\\n\t\t\t\t-DBUILD_OPENCS=no \\\n\t\t\t\t-DBUILD_WIZARD=no\n\t\t\t;;\n\t\topencs )\n\t\t\techo \"  Building subproject: OpenCS.\"\n\t\t\tadd_cmake_opts -DBUILD_ESSIMPORTER=no \\\n\t\t\t\t-DBUILD_LAUNCHER=no \\\n\t\t\t\t-DBUILD_MWINIIMPORTER=no \\\n\t\t\t\t-DBUILD_OPENMW=no \\\n\t\t\t\t-DBUILD_WIZARD=no\n\t\t\t;;\n\t\tmisc )\n\t\t\techo \"  Building subprojects: Misc.\"\n\t\t\tadd_cmake_opts -DBUILD_OPENCS=no \\\n\t\t\t\t-DBUILD_OPENMW=no\n\t\t\t;;\n\tesac\nfi\n# NOTE: Disable this when/if we want to run test cases\n#if [ -z $CI ]; then\n\tfor CONFIGURATION in ${CONFIGURATIONS[@]}; do\n\t\techo \"- Copying Runtime DLLs for $CONFIGURATION...\"\n\t\tDLL_PREFIX=\"\"\n\t\tif [ -z $SINGLE_CONFIG ]; then\n\t\t\tmkdir -p $CONFIGURATION\n\t\t\tDLL_PREFIX=\"$CONFIGURATION/\"\n\t\tfi\n\t\tfor DLL in ${RUNTIME_DLLS[$CONFIGURATION]}; do\n\t\t\tTARGET=\"$(basename \"$DLL\")\"\n\t\t\tif [[ \"$DLL\" == *\":\"* ]]; then\n\t\t\t\toriginalIFS=\"$IFS\"\n\t\t\t\tIFS=':'; SPLIT=( ${DLL} ); IFS=$originalIFS\n\t\t\t\tDLL=${SPLIT[0]}\n\t\t\t\tTARGET=${SPLIT[1]}\n\t\t\tfi\n\t\t\techo \"    ${TARGET}.\"\n\t\t\tcp \"$DLL\" \"${DLL_PREFIX}$TARGET\"\n\t\tdone\n\t\techo\n\t\techo \"- OSG Plugin DLLs...\"\n\t\tmkdir -p ${DLL_PREFIX}osgPlugins-3.6.5\n\t\tfor DLL in ${OSG_PLUGINS[$CONFIGURATION]}; do\n\t\t\techo \"    $(basename $DLL).\"\n\t\t\tcp \"$DLL\" ${DLL_PREFIX}osgPlugins-3.6.5\n\t\tdone\n\t\techo\n\t\techo \"- Qt Platform DLLs...\"\n\t\tmkdir -p ${DLL_PREFIX}platforms\n\t\tfor DLL in ${QT_PLATFORMS[$CONFIGURATION]}; do\n\t\t\techo \"    $(basename $DLL)\"\n\t\t\tcp \"$DLL\" \"${DLL_PREFIX}platforms\"\n\t\tdone\n\t\techo\n\t\techo \"- Qt Style DLLs...\"\n\t\tmkdir -p ${DLL_PREFIX}styles\n\t\tfor DLL in ${QT_STYLES[$CONFIGURATION]}; do\n\t\t\techo \"    $(basename $DLL)\"\n\t\t\tcp \"$DLL\" \"${DLL_PREFIX}styles\"\n\t\tdone\n\t\techo\n\tdone\n#fi\n\nif [ \"${BUILD_BENCHMARKS}\" ]; then\n\tadd_cmake_opts -DBUILD_BENCHMARKS=ON\nfi\n\nif [ -n \"$ACTIVATE_MSVC\" ]; then\n\techo -n \"- Activating MSVC in the current shell... \"\n\tcommand -v vswhere >/dev/null 2>&1 || { echo \"Error: vswhere is not on the path.\"; wrappedExit 1; }\n\n\t# There are so many arguments now that I'm going to document them:\n\t# * products: allow Visual Studio or standalone build tools\n\t# * version: obvious. Awk helps make a version range by adding one.\n\t# * property installationPath: only give the installation path.\n\t# * latest: return only one result if several candidates exist. Prefer the last installed/updated\n\t# * requires: make sure it's got the MSVC compiler instead of, for example, just the .NET compiler. The .x86.x64 suffix means it's for either, not that it's the x64 on x86 cross compiler as you always get both\n\tMSVC_INSTALLATION_PATH=$(vswhere -products '*' -version \"[$MSVC_REAL_VER,$(awk \"BEGIN { print $MSVC_REAL_VER + 1; exit }\"))\" -property installationPath -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64)\n\tif [ -z \"$MSVC_INSTALLATION_PATH\" ]; then\n\t\techo \"vswhere was unable to find MSVC $MSVC_DISPLAY_YEAR\"\n\t\twrappedExit 1\n\tfi\n\t\n\techo \"@\\\"${MSVC_INSTALLATION_PATH}\\Common7\\Tools\\VsDevCmd.bat\\\" -no_logo -arch=$([ $BITS -eq 64 ] && echo \"amd64\" || echo \"x86\") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo \"amd64\" || echo \"x86\")\" > ActivateMSVC.bat\n\t\n\tcp \"../CI/activate_msvc.sh\" .\n\tsed -i \"s/\\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g\" activate_msvc.sh\n\tsource ./activate_msvc.sh\n\t\n\tcp \"../CI/ActivateMSVC.ps1\" .\n\tsed -i \"s/\\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g\" ActivateMSVC.ps1\n\n\techo \"done.\"\n\techo\nfi\n\nif [ -z $VERBOSE ]; then\n\tprintf -- \"- Configuring... \"\nelse\n\techo \"- cmake .. $CMAKE_OPTS\"\nfi\nRET=0\nrun_cmd cmake .. $CMAKE_OPTS || RET=$?\nif [ -z $VERBOSE ]; then\n\tif [ $RET -eq 0 ]; then\n\t\techo Done.\n\telse\n\t\techo Failed.\n\tfi\nfi\nif [ $RET -ne 0 ]; then\n\twrappedExit $RET\nfi\n\necho \"Script completed successfully.\"\necho \"You now have an OpenMW build system at $(unixPathAsWindows \"$(pwd)\")\"\n\nif [ -n \"$ACTIVATE_MSVC\" ]; then\n\techo\n\techo \"Note: you must manually activate MSVC for the shell in which you want to do the build.\"\n\techo\n\techo \"Some scripts have been created in the build directory to do so in an existing shell.\"\n\techo \"Bash: source activate_msvc.sh\"\n\techo \"CMD: ActivateMSVC.bat\"\n\techo \"PowerShell: ActivateMSVC.ps1\"\n\techo\n\techo \"You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio.\"\n\techo\n\tif [ $(uname -m) == 'x86_64' ]; then\n\t\tif [ $BITS -eq 64 ]; then\n\t\t\tinheritEnvironments=msvc_x64_x64\n\t\telse\n\t\t\tinheritEnvironments=msvc_x64\n\t\tfi\n\telse\n\t\tif [ $BITS -eq 64 ]; then\n\t\t\tinheritEnvironments=msvc_x86_x64\n\t\telse\n\t\t\tinheritEnvironments=msvc_x86\n\t\tfi\n\tfi\n\techo \"In Visual Studio 15.3 (2017 Update 3) or later, try setting '\\\"inheritEnvironments\\\": [ \\\"$inheritEnvironments\\\" ]' in CMakeSettings.json to build in the IDE.\"\nfi\n\nwrappedExit $RET\n"
  },
  {
    "path": "CI/before_script.osx.sh",
    "content": "#!/bin/sh -e\n\nexport CXX=clang++\nexport CC=clang\n\nDEPENDENCIES_ROOT=\"/private/tmp/openmw-deps/openmw-deps\"\nQT_PATH=$(brew --prefix qt@5)\nCCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache\nmkdir build\ncd build\n\ncmake \\\n-D CMAKE_PREFIX_PATH=\"$DEPENDENCIES_ROOT;$QT_PATH\" \\\n-D CMAKE_C_COMPILER_LAUNCHER=\"$CCACHE_EXECUTABLE\" \\\n-D CMAKE_CXX_COMPILER_LAUNCHER=\"$CCACHE_EXECUTABLE\" \\\n-D CMAKE_CXX_FLAGS=\"-stdlib=libc++\" \\\n-D CMAKE_C_FLAGS_RELEASE=\"-g -O0\" \\\n-D CMAKE_CXX_FLAGS_RELEASE=\"-g -O0\" \\\n-D CMAKE_OSX_DEPLOYMENT_TARGET=\"10.14\" \\\n-D CMAKE_BUILD_TYPE=RELEASE \\\n-D OPENMW_OSX_DEPLOYMENT=TRUE \\\n-D BUILD_OPENMW=TRUE \\\n-D BUILD_OPENCS=TRUE \\\n-D BUILD_ESMTOOL=TRUE \\\n-D BUILD_BSATOOL=TRUE \\\n-D BUILD_ESSIMPORTER=TRUE \\\n-D BUILD_NIFTEST=TRUE \\\n-G\"Unix Makefiles\" \\\n..\n"
  },
  {
    "path": "CI/build.msvc.sh",
    "content": "#!/bin/bash\n\nAPPVEYOR=\"\"\nCI=\"\"\n\nPACKAGE=\"\"\nPLATFORM=\"\"\nCONFIGURATION=\"\"\nVS_VERSION=\"\"\n\nif [ -z $PLATFORM ]; then\n\tPLATFORM=`uname -m`\nfi\n\nif [ -z $CONFIGURATION ]; then\n\tCONFIGURATION=\"Debug\"\nfi\n\ncase $VS_VERSION in\n\t14|14.0|2015 )\n\t\tGENERATOR=\"Visual Studio 14 2015\"\n\t\tMSVC_YEAR=\"2015\"\n\t\tMSVC_VER=\"14.0\"\n\t\t;;\n\n#\t12|2013|\n\t* )\n\t\tGENERATOR=\"Visual Studio 12 2013\"\n\t\tMSVC_YEAR=\"2013\"\n\t\tMVSC_VER=\"12.0\"\n\t\t;;\nesac\n\ncase $PLATFORM in\n\tx64|x86_64|x86-64|win64|Win64 )\n\t\tBITS=64\n\t\t;;\n\n\tx32|x86|i686|i386|win32|Win32 )\n\t\tBITS=32\n\t\t;;\nesac\n\ncase $CONFIGURATION in\n\tdebug|Debug|DEBUG )\n\t\tCONFIGURATION=Debug\n\t\t;;\n\n\trelease|Release|RELEASE )\n\t\tCONFIGURATION=Release\n\t\t;;\n\n\trelwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )\n\t\tCONFIGURATION=RelWithDebInfo\n\t\t;;\nesac\n\nif [ -z $APPVEYOR ]; then\n\techo \"Running ${BITS}-bit MSVC${MSVC_YEAR} ${CONFIGURATION} build outside of Appveyor.\"\n\n\tDIR=$(echo \"$0\" | sed \"s,\\\\\\\\,/,g\" | sed \"s,\\(.\\):,/\\\\1,\")\n\tcd $(dirname \"$DIR\")/..\nelse\n\techo \"Running ${BITS}-bit MSVC${MSVC_YEAR} ${CONFIGURATION} build in Appveyor.\"\n\n\tcd $APPVEYOR_BUILD_FOLDER\nfi\n\nBUILD_DIR=\"MSVC${MSVC_YEAR}_${BITS}\"\ncd ${BUILD_DIR}\n\nwhich msbuild > /dev/null\nif [ $? -ne 0 ]; then\n\tmsbuild() {\n\t\t/c/Program\\ Files\\ \\(x86\\)/MSBuild/${MSVC_VER}/Bin/MSBuild.exe \"$@\"\n\t}\nfi\n\nif [ -z $APPVEYOR ]; then\n\tmsbuild OpenMW.sln //t:Build //p:Configuration=${CONFIGURATION} //m:8\nelse\n\tmsbuild OpenMW.sln //t:Build //p:Configuration=${CONFIGURATION} //m:8 //logger:\"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll\"\nfi\n\nRET=$?\nif [ $RET -eq 0 ] && [ ! -z $PACKAGE ]; then\n\tmsbuild PACKAGE.vcxproj //t:Build //m:8\n\tRET=$?\nfi\n\nexit $RET\n"
  },
  {
    "path": "CI/build_googletest.sh",
    "content": "#!/bin/sh -ex\n\ngit clone -b release-1.10.0 https://github.com/google/googletest.git\ncd googletest\nmkdir build\ncd build\ncmake \\\n    -D CMAKE_C_COMPILER=\"${CC}\" \\\n    -D CMAKE_CXX_COMPILER=\"${CXX}\" \\\n    -D CMAKE_C_COMPILER_LAUNCHER=ccache \\\n    -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \\\n    -D CMAKE_BUILD_TYPE=\"${CONFIGURATION}\" \\\n    -D CMAKE_INSTALL_PREFIX=\"${GOOGLETEST_DIR}\" \\\n    -G \"${GENERATOR}\" \\\n    ..\ncmake --build . --config \"${CONFIGURATION}\" -- -j $(nproc)\ncmake --install . --config \"${CONFIGURATION}\"\n"
  },
  {
    "path": "CI/check_package.osx.sh",
    "content": "#!/usr/bin/env bash\n\nhdiutil attach ./*.dmg -mountpoint \"${TRAVIS_BUILD_DIR}/openmw-package\" > /dev/null || echo \"hdutil has failed\"\n\nEXPECTED_PACKAGE_FILES=('Applications' 'OpenMW-CS.app' 'OpenMW.app')\nPACKAGE_FILES=$(ls \"${TRAVIS_BUILD_DIR}/openmw-package\" | LC_ALL=C sort)\n\nDIFF=$(diff <(printf \"%s\\n\" \"${EXPECTED_PACKAGE_FILES[@]}\") <(printf \"%s\\n\" \"${PACKAGE_FILES[@]}\"))\nDIFF_STATUS=$?\n\nif [[ $DIFF_STATUS -ne 0 ]]; then\n  echo \"The package should only contain an Applications symlink and two applications, see the following diff for details.\" >&2\n  echo \"$DIFF\" >&2\n  exit 1\nfi\n"
  },
  {
    "path": "CI/check_tabs.sh",
    "content": "#!/bin/bash\n\nOUTPUT=$(grep -nRP '\\t' --include=\\*.{cpp,hpp,c,h} --exclude=ui_\\* apps components)\n\nif [[ $OUTPUT ]] ; then\n    echo \"Error: Tab characters found!\"\n    echo $OUTPUT\n    exit 1\nfi\n"
  },
  {
    "path": "CI/deploy.osx.sh",
    "content": "#!/bin/sh\n\n# This script expect the following environment variables to be set:\n# - OSX_DEPLOY_KEY: private SSH key, must be encoded like this before adding it to Travis secrets: https://github.com/travis-ci/travis-ci/issues/7715#issuecomment-433301692\n# - OSX_DEPLOY_HOST: string specifying SSH of the following format: ssh-user@ssh-host\n# - OSX_DEPLOY_PORT: SSH port, it can't be a part of the host string because scp doesn't accept hosts with ports\n# - OSX_DEPLOY_HOST_FINGERPRINT: fingerprint of the host, can be obtained by using ssh-keygen -F [host]:port & putting it in double quotes when adding to Travis secrets\n\nSSH_KEY_PATH=\"$HOME/.ssh/openmw_deploy\"\nREMOTE_PATH=\"\\$HOME/nightly\"\n\necho \"$OSX_DEPLOY_KEY\" > \"$SSH_KEY_PATH\"\nchmod 600 \"$SSH_KEY_PATH\"\necho \"$OSX_DEPLOY_HOST_FINGERPRINT\" >> \"$HOME/.ssh/known_hosts\"\n\ncd build || exit 1\n\nDATE=$(date +'%d%m%Y')\nSHORT_COMMIT=$(git rev-parse --short \"${TRAVIS_COMMIT}\")\nTARGET_FILENAME=\"OpenMW-${DATE}-${SHORT_COMMIT}.dmg\"\n\nif ! ssh -p \"$OSX_DEPLOY_PORT\" -i \"$SSH_KEY_PATH\" \"$OSX_DEPLOY_HOST\" \"ls \\\"$REMOTE_PATH\\\"\" | grep \"$SHORT_COMMIT\" > /dev/null; then\n    scp -P \"$OSX_DEPLOY_PORT\" -i \"$SSH_KEY_PATH\" ./*.dmg \"$OSX_DEPLOY_HOST:$REMOTE_PATH/$TARGET_FILENAME\"\nelse\n    echo \"An existing nightly build for commit ${SHORT_COMMIT} has been found, skipping upload.\"\nfi\n"
  },
  {
    "path": "CI/install_debian_deps.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\nprint_help() {\n  echo \"usage: $0 [group]...\"\n  echo\n  echo \"  available groups: \"${!GROUPED_DEPS[@]}\"\"\n}\n\ndeclare -rA GROUPED_DEPS=(\n  [gcc]=\"binutils gcc g++ libc-dev\"\n  [clang]=\"binutils clang\"\n\n  # Common dependencies for building OpenMW.\n  [openmw-deps]=\"\n    make cmake ccache git pkg-config\n\n    libboost-filesystem-dev libboost-program-options-dev\n    libboost-system-dev libboost-iostreams-dev\n    \n    libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev\n    libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev\n    libbullet-dev liblz4-dev libpng-dev libjpeg-dev\n    ca-certificates\n  \"\n  # TODO: add librecastnavigation-dev when debian is ready\n\n  # These dependencies can alternatively be built and linked statically.\n  [openmw-deps-dynamic]=\"libmygui-dev libopenscenegraph-dev\"\n  [coverity]=\"curl\"\n\n  # Pre-requisites for building MyGUI and OSG for static linking.\n  #\n  # * MyGUI and OSG: libsdl2-dev liblz4-dev libfreetype6-dev\n  # * OSG: libgl-dev\n  #\n  #   Plugins:\n  #   * DAE: libcollada-dom-dev libboost-system-dev libboost-filesystem-dev\n  #   * JPEG: libjpeg-dev\n  #   * PNG: libpng-dev\n  [openmw-deps-static]=\"\n    make cmake\n    ccache curl unzip libcollada-dom-dev libfreetype6-dev libjpeg-dev libpng-dev\n    libsdl2-dev libboost-system-dev libboost-filesystem-dev libgl-dev\n  \"\n)\n\nif [[ $# -eq 0 ]]; then\n  >&2 print_help\n  exit 1\nfi\n\ndeps=()\nfor group in \"$@\"; do\n  if [[ ! -v GROUPED_DEPS[$group] ]]; then\n    >&2 echo \"error: unknown group ${group}\"\n    exit 1\n  fi\n  deps+=(${GROUPED_DEPS[$group]})\ndone\n\nexport APT_CACHE_DIR=\"${PWD}/apt-cache\"\nset -x\nmkdir -pv \"$APT_CACHE_DIR\"\napt-get update -yq\napt-get -q -o dir::cache::archives=\"$APT_CACHE_DIR\" install -y --no-install-recommends \"${deps[@]}\"\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "project(OpenMW)\ncmake_minimum_required(VERSION 3.1.0)\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n# for link time optimization, remove if cmake version is >= 3.9\nif(POLICY CMP0069) # LTO\n  cmake_policy(SET CMP0069 NEW)\nendif()\n\n# for position-independent executable, remove if cmake version is >= 3.14\nif(POLICY CMP0083)\n  cmake_policy(SET CMP0083 NEW)\nendif()\n\noption(OPENMW_GL4ES_MANUAL_INIT \"Manually initialize gl4es. This is more reliable on platforms without a windowing system. Requires gl4es to be configured with -DNOEGL=ON -DNO_LOADER=ON -DNO_INIT_CONSTRUCTOR=ON.\" OFF)\nif(OPENMW_GL4ES_MANUAL_INIT)\n    add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)\nendif()\n\n# Apps and tools\noption(BUILD_OPENMW             \"Build OpenMW\" ON)\noption(BUILD_LAUNCHER           \"Build Launcher\" ON)\noption(BUILD_WIZARD             \"Build Installation Wizard\" ON)\noption(BUILD_MWINIIMPORTER      \"Build MWiniImporter\" ON)\noption(BUILD_OPENCS             \"Build OpenMW Construction Set\" ON)\noption(BUILD_ESSIMPORTER        \"Build ESS (Morrowind save game) importer\" ON)\noption(BUILD_BSATOOL            \"Build BSA extractor\" ON)\noption(BUILD_ESMTOOL            \"Build ESM inspector\" ON)\noption(BUILD_NIFTEST            \"Build nif file tester\" ON)\noption(BUILD_DOCS               \"Build documentation.\" OFF )\noption(BUILD_WITH_CODE_COVERAGE \"Enable code coverage with gconv\" OFF)\noption(BUILD_UNITTESTS          \"Enable Unittests with Google C++ Unittest\" OFF)\noption(BUILD_BENCHMARKS         \"Build benchmarks with Google Benchmark\" OFF)\noption(BUILD_OPENMW_MP          \"Build OpenMW-MP\" ON)\noption(BUILD_BROWSER            \"Build tes3mp Server Browser\" ON)\noption(BUILD_MASTER             \"Build tes3mp Master Server\" OFF)\n\nset(OpenGL_GL_PREFERENCE LEGACY)  # Use LEGACY as we use GL2; GLNVD is for GL3 and up.\n\nif (NOT BUILD_LAUNCHER AND NOT BUILD_BROWSER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)\n   set(USE_QT FALSE)\nelse()\n   set(USE_QT TRUE)\nendif()\n\n# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.\nIF(NOT CMAKE_BUILD_TYPE)\n    SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING\n        \"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.\"\n        FORCE)\n    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS None Debug Release RelWithDebInfo MinSizeRel)\nENDIF()\n\nif (APPLE)\n    set(APP_BUNDLE_NAME \"${CMAKE_PROJECT_NAME}.app\")\n\n    set(APP_BUNDLE_DIR \"${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}\")\nendif (APPLE)\n\nset(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)\n\nif (ANDROID)\n    set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} \"${CMAKE_FIND_ROOT_PATH}\")\nendif()\n\n# Version\nmessage(STATUS \"Configuring OpenMW...\")\n\nset(OPENMW_VERSION_MAJOR 0)\nset(OPENMW_VERSION_MINOR 47)\nset(OPENMW_VERSION_RELEASE 0)\n\nset(OPENMW_VERSION_COMMITHASH \"\")\nset(OPENMW_VERSION_TAGHASH \"\")\nset(OPENMW_VERSION_COMMITDATE \"\")\n\nset(OPENMW_VERSION \"${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}\")\n\nset(OPENMW_DOC_BASEURL \"https://openmw.readthedocs.io/en/stable/\")\n\nset(GIT_CHECKOUT FALSE)\nif(EXISTS ${PROJECT_SOURCE_DIR}/.git)\n    find_package(Git)\n\n    if(GIT_FOUND)\n        set(GIT_CHECKOUT TRUE)\n    else(GIT_FOUND)\n        message(WARNING \"Git executable not found\")\n    endif(GIT_FOUND)\n\n    if(GIT_FOUND)\n        execute_process (\n                COMMAND ${GIT_EXECUTABLE} log -1 --format='%aI'\n                WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}\n                RESULT_VARIABLE EXITCODE3\n                OUTPUT_VARIABLE OPENMW_VERSION_COMMITDATE\n                OUTPUT_STRIP_TRAILING_WHITESPACE)\n        if(NOT EXITCODE3)\n            string(SUBSTRING ${OPENMW_VERSION_COMMITDATE} 1 10 OPENMW_VERSION_COMMITDATE)\n        endif(NOT EXITCODE3)\n    endif(GIT_FOUND)\nendif(EXISTS ${PROJECT_SOURCE_DIR}/.git)\n\n# Macros\ninclude(OpenMWMacros)\ninclude(WholeArchive)\n\n# doxygen main page\n\nconfigure_file (\"${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake\" \"${OpenMW_BINARY_DIR}/docs/mainpage.hpp\")\n\noption(BOOST_STATIC \"Link static build of Boost into the binaries\" FALSE)\noption(SDL2_STATIC \"Link static build of SDL into the binaries\" FALSE)\noption(QT_STATIC \"Link static build of QT into the binaries\" FALSE)\n\noption(OPENMW_USE_SYSTEM_BULLET \"Use system provided bullet physics library\" ON)\nif(OPENMW_USE_SYSTEM_BULLET)\n    set(_bullet_static_default OFF)\nelse()\n    set(_bullet_static_default ON)\nendif()\noption(BULLET_STATIC \"Link static build of Bullet into the binaries\" ${_bullet_static_default})\n\noption(OPENMW_USE_SYSTEM_OSG \"Use system provided OpenSceneGraph libraries\" ON)\nif(OPENMW_USE_SYSTEM_OSG)\n    set(_osg_static_default OFF)\nelse()\n    set(_osg_static_default ON)\nendif()\noption(OSG_STATIC \"Link static build of OpenSceneGraph into the binaries\" ${_osg_static_default})\n\noption(OPENMW_USE_SYSTEM_MYGUI \"Use system provided mygui library\" ON)\nif(OPENMW_USE_SYSTEM_MYGUI)\n    set(_mygui_static_default OFF)\nelse()\n    set(_mygui_static_default ON)\nendif()\noption(MYGUI_STATIC \"Link static build of Mygui into the binaries\" ${_mygui_static_default})\n\noption(OPENMW_USE_SYSTEM_RECASTNAVIGATION \"Use system provided recastnavigation library\" OFF)\nif(OPENMW_USE_SYSTEM_RECASTNAVIGATION)\n    set(_recastnavigation_static_default OFF)\n    find_package(RecastNavigation REQUIRED)\nelse()\n    set(_recastnavigation_static_default ON)\nendif()\noption(RECASTNAVIGATION_STATIC \"Build recastnavigation static libraries\" ${_recastnavigation_static_default})\n\noption(OPENMW_UNITY_BUILD \"Use fewer compilation units to speed up compile time\" FALSE)\noption(OPENMW_LTO_BUILD \"Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)\" OFF)\n\n# what is necessary to build documentation\nIF( BUILD_DOCS )\n    # Builds the documentation.\n    FIND_PACKAGE( Sphinx REQUIRED )\n    FIND_PACKAGE( Doxygen REQUIRED )\nENDIF()\n\n# OS X deployment\noption(OPENMW_OSX_DEPLOYMENT OFF)\n\nif (MSVC)\n    option(OPENMW_MP_BUILD \"Build OpenMW with /MP flag\" OFF)\nendif()\n\n# Set up common paths\nif (APPLE)\n    set(MORROWIND_DATA_FILES \"./data\" CACHE PATH \"location of Morrowind data files\")\n    set(OPENMW_RESOURCE_FILES \"../Resources/resources\" CACHE PATH \"location of OpenMW resources files\")\nelseif(UNIX)\n    # Paths\n    SET(BINDIR \"${CMAKE_INSTALL_PREFIX}/bin\" CACHE PATH \"Where to install binaries\")\n    SET(LIBDIR \"${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}\" CACHE PATH \"Where to install libraries\")\n    SET(DATAROOTDIR \"${CMAKE_INSTALL_PREFIX}/share\" CACHE PATH \"Sets the root of data directories to a non-default location\")\n    SET(GLOBAL_DATA_PATH \"${DATAROOTDIR}/games/\" CACHE PATH \"Set data path prefix\")\n    SET(DATADIR \"${GLOBAL_DATA_PATH}/openmw\" CACHE PATH \"Sets the openmw data directories to a non-default location\")\n    SET(ICONDIR \"${DATAROOTDIR}/pixmaps\" CACHE PATH \"Set icon dir\")\n    SET(LICDIR \"${DATAROOTDIR}/licenses/openmw\" CACHE PATH \"Sets the openmw license directory to a non-default location.\")\n    IF(\"${CMAKE_INSTALL_PREFIX}\" STREQUAL \"/usr\")\n        SET(GLOBAL_CONFIG_PATH \"/etc/\" CACHE PATH \"Set config dir prefix\")\n    ELSE()\n        SET(GLOBAL_CONFIG_PATH \"${CMAKE_INSTALL_PREFIX}/etc/\" CACHE PATH \"Set config dir prefix\")\n    ENDIF()\n    SET(SYSCONFDIR \"${GLOBAL_CONFIG_PATH}/openmw\" CACHE PATH \"Set config dir\")\n\n    set(MORROWIND_DATA_FILES \"${DATADIR}/data\" CACHE PATH \"location of Morrowind data files\")\n    set(OPENMW_RESOURCE_FILES \"${DATADIR}/resources\" CACHE PATH \"location of OpenMW resources files\")\nelse()\n    set(MORROWIND_DATA_FILES \"data\" CACHE PATH \"location of Morrowind data files\")\n    set(OPENMW_RESOURCE_FILES \"resources\" CACHE PATH \"location of OpenMW resources files\")\nendif(APPLE)\n\nif (WIN32)\n    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)\nendif()\n\nfind_package(RakNet REQUIRED)\ninclude_directories(${RakNet_INCLUDES})\n\n# Dependencies\nfind_package(OpenGL REQUIRED)\n\nfind_package(LZ4 REQUIRED)\n\nif (USE_QT)\n    find_package(Qt5Core 5.9 REQUIRED) # Temporary adjustment for TES3MP CI\n    find_package(Qt5Widgets REQUIRED)\n    find_package(Qt5Network REQUIRED)\n    find_package(Qt5OpenGL REQUIRED)\n    # Instruct CMake to run moc automatically when needed.\n    #set(CMAKE_AUTOMOC ON)\nendif()\n\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nset(USED_OSG_COMPONENTS\n    osgDB\n    osgViewer\n    osgText\n    osgGA\n    osgParticle\n    osgUtil\n    osgFX\n    osgShadow\n    osgAnimation)\nset(USED_OSG_PLUGINS\n    osgdb_bmp\n    osgdb_dds\n    osgdb_freetype\n    osgdb_jpeg\n    osgdb_osg\n    osgdb_png\n    osgdb_serializers_osg\n    osgdb_tga)\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nENDIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\n\nadd_subdirectory(extern)\n\n# Sound setup\n\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\n# Require at least ffmpeg 3.2 for now\nSET(FFVER_OK FALSE)\n\nfind_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE)\n\nif(FFmpeg_FOUND)\n    SET(FFVER_OK TRUE)\n\n    # Can not detect FFmpeg version on Windows for now\n    if (NOT WIN32)\n        if(FFmpeg_AVFORMAT_VERSION VERSION_LESS \"57.56.100\")\n            message(STATUS \"libavformat is too old! (${FFmpeg_AVFORMAT_VERSION}, wanted 57.56.100)\")\n            set(FFVER_OK FALSE)\n        endif()\n        if(FFmpeg_AVCODEC_VERSION VERSION_LESS \"57.64.100\")\n            message(STATUS \"libavcodec is too old! (${FFmpeg_AVCODEC_VERSION}, wanted 57.64.100)\")\n            set(FFVER_OK FALSE)\n        endif()\n        if(FFmpeg_AVUTIL_VERSION VERSION_LESS \"55.34.100\")\n            message(STATUS \"libavutil is too old! (${FFmpeg_AVUTIL_VERSION}, wanted 55.34.100)\")\n            set(FFVER_OK FALSE)\n        endif()\n        if(FFmpeg_SWSCALE_VERSION VERSION_LESS \"4.2.100\")\n            message(STATUS \"libswscale is too old! (${FFmpeg_SWSCALE_VERSION}, wanted 4.2.100)\")\n            set(FFVER_OK FALSE)\n        endif()\n        if(FFmpeg_SWRESAMPLE_VERSION VERSION_LESS \"2.3.100\")\n            message(STATUS \"libswresample is too old! (${FFmpeg_SWRESAMPLE_VERSION}, wanted 2.3.100)\")\n            set(FFVER_OK FALSE)\n        endif()\n    endif()\n\n    if(NOT FFVER_OK AND NOT APPLE)  # unable to detect on version on MacOS < 11.0\n        message(FATAL_ERROR \"FFmpeg version is too old, 3.2 is required\" )\n    endif()\nendif()\n\nif(NOT FFmpeg_FOUND)\n    message(FATAL_ERROR \"FFmpeg was not found\" )\nendif()\n\nif(WIN32)\n    message(\"Can not detect FFmpeg version, at least the 3.2 is required\" )\nendif()\n\n# Required for building the FFmpeg headers\nadd_definitions(-D__STDC_CONSTANT_MACROS)\n\n# Reqiuired for unity build\nadd_definitions(-DMYGUI_DONT_REPLACE_NULLPTR)\n\n# TinyXML\noption(USE_SYSTEM_TINYXML \"Use system TinyXML library instead of internal.\" OFF)\nif (USE_SYSTEM_TINYXML)\n    find_package(TinyXML REQUIRED)\n    add_definitions (-DTIXML_USE_STL)\n    include_directories(SYSTEM ${TinyXML_INCLUDE_DIRS})\nendif()\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nENDIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\n\n# Platform specific\nif (WIN32)\n    if(NOT MINGW)\n    set(Boost_USE_STATIC_LIBS   ON)\n    add_definitions(-DBOOST_ALL_NO_LIB)\n    endif(NOT MINGW)\n\n    # Suppress WinMain(), provided by SDL\n    add_definitions(-DSDL_MAIN_HANDLED)\n\n    # Get rid of useless crud from windows.h\n    add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)\nendif()\n\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nif(OPENMW_USE_SYSTEM_BULLET)\n    set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape\n    if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR})\n        set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine\n    endif()\n\n    # First, try BulletConfig-float64.cmake which comes with Debian derivatives.\n    # This file does not define the Bullet version in a CMake-friendly way.\n    find_package(Bullet CONFIGS BulletConfig-float64.cmake QUIET COMPONENTS BulletCollision LinearMath)\n    if (BULLET_FOUND)\n        string(REPLACE \".\" \"\" _bullet_version_num ${BULLET_VERSION_STRING})\n        if (_bullet_version_num VERSION_LESS REQUIRED_BULLET_VERSION)\n            message(FATAL_ERROR \"System bullet version too old, OpenMW requires at least ${REQUIRED_BULLET_VERSION}, got ${_bullet_version_num}\")\n        endif()\n        # Fix the relative include:\n        set(BULLET_INCLUDE_DIRS \"${BULLET_ROOT_DIR}/${BULLET_INCLUDE_DIRS}\")\n        include(FindPackageMessage)\n        find_package_message(Bullet \"Found Bullet: ${BULLET_LIBRARIES} ${BULLET_VERSION_STRING}\" \"${BULLET_VERSION_STRING}-float64\")\n    else()\n        find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)\n    endif()\n\n    # Only link the Bullet libraries that we need:\n    string(REGEX MATCHALL \"((optimized|debug);)?[^;]*(BulletCollision|LinearMath)[^;]*\" BULLET_LIBRARIES \"${BULLET_LIBRARIES}\")\n\n    include(cmake/CheckBulletPrecision.cmake)\n    if (HAS_DOUBLE_PRECISION_BULLET)\n        message(STATUS \"Bullet uses double precision\")\n    else()\n        message(FATAL_ERROR \"Bullet does not uses double precision\")\n    endif()\nendif()\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nENDIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\n\nif (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer\n    find_package(LIBUNSHIELD REQUIRED) # required only for non win32 when building openmw-wizard\n    set(OPENMW_USE_UNSHIELD TRUE)\nendif()\n\n# Fix for not visible pthreads functions for linker with glibc 2.15\nif (UNIX AND NOT APPLE)\n    find_package (Threads)\nendif()\n\n# Look for stdint.h\ninclude(CheckIncludeFile)\ncheck_include_file(stdint.h HAVE_STDINT_H)\nif(NOT HAVE_STDINT_H)\n    unset(HAVE_STDINT_H CACHE)\n    message(FATAL_ERROR \"stdint.h was not found\" )\nendif()\n\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\n# but keep OSG's headers (HACK)\nIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nif(OPENMW_USE_SYSTEM_OSG)\n    find_package(OpenSceneGraph 3.4.0 REQUIRED ${USED_OSG_COMPONENTS})\n    if (${OPENSCENEGRAPH_VERSION} VERSION_GREATER 3.6.2 AND ${OPENSCENEGRAPH_VERSION} VERSION_LESS 3.6.5)\n        message(FATAL_ERROR \"OpenSceneGraph version ${OPENSCENEGRAPH_VERSION} has critical regressions which cause crashes. Please upgrade to 3.6.5 or later. We strongly recommend using the tip of the official 'OpenSceneGraph-3.6' branch or the tip of '3.6' OpenMW/osg (OSGoS).\")\n    endif()\n\n    if(OSG_STATIC)\n        find_package(OSGPlugins REQUIRED COMPONENTS ${USED_OSG_PLUGINS})\n    endif()\nendif()\n\ninclude_directories(BEFORE SYSTEM ${OPENSCENEGRAPH_INCLUDE_DIRS})\n\nif(OSG_STATIC)\n    add_definitions(-DOSG_LIBRARY_STATIC)\nendif()\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\n# but keep OSG's headers (HACK)\nELSE(BUILD_OPENMW OR BUILD_OPENCS)\n    include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})\nENDIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\n\nset(BOOST_COMPONENTS system filesystem program_options iostreams)\nif(WIN32)\n    set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)\n    if(MSVC)\n        # boost-zlib is not present (nor needed) in vcpkg version of boost.\n        # there, it is part of boost-iostreams instead.\n        set(BOOST_OPTIONAL_COMPONENTS zlib)\n    endif(MSVC)\nendif(WIN32)\n\nIF(BOOST_STATIC)\n    set(Boost_USE_STATIC_LIBS   ON)\nendif()\n\nset(Boost_NO_BOOST_CMAKE ON)\n\nfind_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${BOOST_OPTIONAL_COMPONENTS})\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nif(OPENMW_USE_SYSTEM_MYGUI)\n    find_package(MyGUI 3.2.2 REQUIRED)\nendif()\n# End of tes3mp addition\n#\n# Don't require certain dependencies for the server\nENDIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nfind_package(SDL2 2.0.9 REQUIRED)\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nfind_package(OpenAL REQUIRED)\n# End of tes3mp addition\n#\n# Don't require certain dependencies for the server\nENDIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\n\ninclude_directories(\n    BEFORE SYSTEM\n    \".\"\n    ${SDL2_INCLUDE_DIR}\n    ${Boost_INCLUDE_DIR}\n    ${MyGUI_INCLUDE_DIRS}\n    ${OPENAL_INCLUDE_DIR}\n    ${OPENGL_INCLUDE_DIR}\n    ${BULLET_INCLUDE_DIRS}\n)\n\nlink_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})\n\nif(MYGUI_STATIC)\n   add_definitions(-DMYGUI_STATIC)\nendif (MYGUI_STATIC)\n\nif (APPLE)\n    configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw-Info.plist.in\n        \"${APP_BUNDLE_DIR}/Contents/Info.plist\")\n\n    configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns\n        \"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns\" COPYONLY)\nendif (APPLE)\n\nif (NOT APPLE)\n    set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR})\n    set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR})\nendif ()\n\nadd_subdirectory(files/)\n\n# Specify build paths\n\nif (APPLE)\n    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"${APP_BUNDLE_DIR}/Contents/MacOS\")\n    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY \"${APP_BUNDLE_DIR}/Contents/MacOS\")\n\n    if (OPENMW_OSX_DEPLOYMENT)\n        SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)\n    endif()\nelse (APPLE)\n    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"${OpenMW_BINARY_DIR}\")\n    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY \"${OpenMW_BINARY_DIR}\")\nendif (APPLE)\n\n# Other files\n\nconfigure_resource_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-client-default.cfg\n    \"${OpenMW_BINARY_DIR}\" \"tes3mp-client-default.cfg\")\n\nconfigure_resource_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-server-default.cfg\n    \"${OpenMW_BINARY_DIR}\" \"tes3mp-server-default.cfg\")\n\npack_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg\n    \"${OpenMW_BINARY_DIR}\" \"defaults.bin\")\n\nconfigure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml\n    \"${OpenMW_BINARY_DIR}\" \"openmw.appdata.xml\")\n\nif (NOT APPLE)\n    configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local\n        \"${OpenMW_BINARY_DIR}\" \"openmw.cfg\")\n    configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg\n        \"${OpenMW_BINARY_DIR}\" \"openmw.cfg.install\")\nelse ()\n    configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg\n        \"${OpenMW_BINARY_DIR}/openmw.cfg\")\nendif ()\n\npack_resource_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg\n    \"${OpenMW_BINARY_DIR}\" \"defaults-cs.bin\")\n\n# Needs the copy version because the configure version assumes the end of the file has been reached when a null character is reached and there are no CMake expressions to evaluate.\ncopy_resource_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters\n    \"${OpenMW_BINARY_DIR}\" \"resources/defaultfilters\")\n\nconfigure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt\n    \"${OpenMW_BINARY_DIR}\" \"gamecontrollerdb.txt\")\n\nif (NOT WIN32 AND NOT APPLE)\n    configure_file(${OpenMW_SOURCE_DIR}/files/org.openmw.launcher.desktop\n        \"${OpenMW_BINARY_DIR}/org.openmw.launcher.desktop\")\n    configure_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml\n        \"${OpenMW_BINARY_DIR}/openmw.appdata.xml\")\n    configure_file(${OpenMW_SOURCE_DIR}/files/tes3mp-browser.desktop\n\t    \"${OpenMW_BINARY_DIR}/tes3mp-browser.desktop\")\n    configure_file(${OpenMW_SOURCE_DIR}/files/org.openmw.cs.desktop\n        \"${OpenMW_BINARY_DIR}/org.openmw.cs.desktop\")\nendif()\n\nif(OPENMW_LTO_BUILD)\n    if(NOT CMAKE_VERSION VERSION_LESS 3.9)\n        include(CheckIPOSupported)\n        check_ipo_supported(RESULT HAVE_IPO OUTPUT HAVE_IPO_OUTPUT)\n        if(HAVE_IPO)\n            message(STATUS \"LTO enabled for Release configuration.\")\n            set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)\n        else()\n            message(WARNING \"Requested option OPENMW_LTO_BUILD not supported by this compiler: ${HAVE_IPO_OUTPUT}\")\n            if(MSVC)\n                message(STATUS \"Note: Flags used to be set manually for this setting with MSVC. We now rely on CMake for this. Upgrade CMake to at least 3.13 to re-enable this setting.\")\n            endif()\n        endif()\n    else()\n        message(WARNING \"Requested option OPENMW_LTO_BUILD not supported by this cmake version: ${CMAKE_VERSION}. Upgrade CMake to at least 3.9 to enable support for certain compilers. Newer CMake versions support more compilers.\")\n    endif()\nendif()\n\n\nif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long\")\n    add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )\n\n    if (APPLE)\n        set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -stdlib=libc++\")\n        set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++\")\n    endif()\n\n    if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)\n        if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)\n            set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression\")\n        endif ()\n    endif()\n\n    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)\n        set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter\")\n    endif()\nendif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)\n\n# Extern\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nadd_subdirectory (extern/osg-ffmpeg-videoplayer)\nadd_subdirectory (extern/oics)\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nENDIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nadd_subdirectory (extern/Base64)\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\nif (BUILD_OPENCS)\n    add_subdirectory (extern/osgQt)\nendif()\n# Start of tes3mp addition\n#\n# Don't require certain dependencies for the server\nENDIF(BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp addition\n\n# Components\nadd_subdirectory (components)\ntarget_compile_definitions(components PRIVATE OPENMW_DOC_BASEURL=\"${OPENMW_DOC_BASEURL}\")\n\n# Apps and tools\nif (BUILD_OPENMW_MP)\n    add_subdirectory( apps/openmw-mp )\nendif()\n\nif (BUILD_MASTER)\n    add_subdirectory( apps/master )\nendif()\n\nif (BUILD_OPENMW)\n    add_subdirectory( apps/openmw )\nendif()\n\nif (BUILD_BSATOOL)\n  add_subdirectory( apps/bsatool )\nendif()\n\nif (BUILD_ESMTOOL)\n  add_subdirectory( apps/esmtool )\nendif()\n\nif (BUILD_LAUNCHER)\n   add_subdirectory( apps/launcher )\nendif()\n\nif (BUILD_BROWSER)\n    add_subdirectory( apps/browser )\nendif()\n\nif (BUILD_MWINIIMPORTER)\n   add_subdirectory( apps/mwiniimporter )\nendif()\n\nif (BUILD_ESSIMPORTER)\n   add_subdirectory (apps/essimporter )\nendif()\n\nif (BUILD_OPENCS)\n   add_subdirectory (apps/opencs)\nendif()\n\nif (BUILD_WIZARD)\n   add_subdirectory(apps/wizard)\nendif()\n\nif (BUILD_NIFTEST)\n    add_subdirectory(apps/niftest)\nendif(BUILD_NIFTEST)\n\n# UnitTests\nif (BUILD_UNITTESTS)\n  add_subdirectory( apps/openmw_test_suite )\nendif()\n\nif (BUILD_BENCHMARKS)\n  add_subdirectory(apps/benchmarks)\nendif()\n\nif (WIN32)\n  if (MSVC)\n    if (OPENMW_MP_BUILD)\n        set( MT_BUILD \"/MP\")\n    endif()\n\n    foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )\n        string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )\n        set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} \"$(SolutionDir)$(Configuration)\" )\n        set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} \"$(ProjectDir)$(Configuration)\" )\n    endforeach( OUTPUTCONFIG )\n\n    if (USE_DEBUG_CONSOLE AND BUILD_OPENMW)\n      set_target_properties(tes3mp PROPERTIES LINK_FLAGS_DEBUG \"/SUBSYSTEM:CONSOLE\")\n      set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELWITHDEBINFO \"/SUBSYSTEM:CONSOLE\")\n      set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_CONSOLE>)\n    elseif (BUILD_OPENMW)\n      # Turn off debug console, debug output will be written to visual studio output instead\n      set_target_properties(tes3mp PROPERTIES LINK_FLAGS_DEBUG \"/SUBSYSTEM:WINDOWS\")\n      set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELWITHDEBINFO \"/SUBSYSTEM:WINDOWS\")\n    endif()\n\n    if (BUILD_OPENMW)\n        # Release builds don't use the debug console\n        set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE \"/SUBSYSTEM:WINDOWS\")\n        set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL \"/SUBSYSTEM:WINDOWS\")\n    endif()\n\n    # Play a bit with the warning levels\n\n    set(WARNINGS \"/W4\")\n\n    set(WARNINGS_DISABLE\n        4100 # Unreferenced formal parameter (-Wunused-parameter)\n        4127 # Conditional expression is constant\n        4996 # Function was declared deprecated\n        )\n    \n    if( \"${MyGUI_VERSION}\" VERSION_LESS_EQUAL \"3.4.0\" )\n        set(WARNINGS_DISABLE ${WARNINGS_DISABLE}\n        4866 # compiler may not enforce left-to-right evaluation order for call\n        )\n    endif()\n\n    if( \"${MyGUI_VERSION}\" VERSION_LESS_EQUAL \"3.4.1\" )\n        set(WARNINGS_DISABLE ${WARNINGS_DISABLE}\n        4275 # non dll-interface class 'MyGUI::delegates::IDelegateUnlink' used as base for dll-interface class 'MyGUI::Widget'\n        )\n    endif()\n\n    foreach(d ${WARNINGS_DISABLE})\n        set(WARNINGS \"${WARNINGS} /wd${d}\")\n    endforeach(d)\n\n    set_target_properties(components PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n\n    if (BUILD_OPENMW)\n        set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n    \n    if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)\n        target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)\n    endif()\n\n    if (BUILD_BSATOOL)\n        set_target_properties(bsatool PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n\n    if (BUILD_ESMTOOL)\n        set_target_properties(esmtool PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n\n    if (BUILD_ESSIMPORTER)\n        set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n\n    if (BUILD_LAUNCHER)\n        set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n\n    if (BUILD_BROWSER)\n        set_target_properties(tes3mp-browser PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n\n    if (BUILD_MWINIIMPORTER)\n        set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n\n    if (BUILD_OPENCS)\n        set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n\n    if (BUILD_OPENMW)\n        if (OPENMW_UNITY_BUILD)\n            set_target_properties(tes3mp PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD} /bigobj\")\n        else()\n            set_target_properties(tes3mp PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n        endif()\n    endif()\n\n    if (BUILD_WIZARD)\n        set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n\n    if (BUILD_BENCHMARKS)\n        set_target_properties(openmw_detournavigator_navmeshtilescache_benchmark PROPERTIES COMPILE_FLAGS \"${WARNINGS} ${MT_BUILD}\")\n    endif()\n  endif(MSVC)\n\n  # TODO: At some point release builds should not use the console but rather write to a log file\n  #set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE \"/SUBSYSTEM:WINDOWS\")\n  #set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL \"/SUBSYSTEM:WINDOWS\")\nendif()\n\n# Apple bundling\nif (OPENMW_OSX_DEPLOYMENT AND APPLE)\n    if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CMAKE_VERSION VERSION_LESS 3.13.4)\n        message(FATAL_ERROR \"macOS packaging is broken in early CMake 3.13 releases, see https://gitlab.com/OpenMW/openmw/issues/4767. Please use at least 3.13.4 or an older version like 3.12.4\")\n    endif ()\n\n    get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE)\n    get_filename_component(QT_COCOA_PLUGIN_DIR \"${QT_COCOA_PLUGIN_PATH}\" DIRECTORY)\n    get_filename_component(QT_COCOA_PLUGIN_GROUP \"${QT_COCOA_PLUGIN_DIR}\" NAME)\n    get_filename_component(QT_COCOA_PLUGIN_NAME \"${QT_COCOA_PLUGIN_PATH}\" NAME)\n    configure_file(\"${QT_COCOA_PLUGIN_PATH}\" \"${APP_BUNDLE_DIR}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}\" COPYONLY)\n    configure_file(\"${OpenMW_SOURCE_DIR}/files/mac/qt.conf\" \"${APP_BUNDLE_DIR}/Contents/Resources/qt.conf\" COPYONLY)\n\n    if (BUILD_OPENCS)\n      get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME)\n      set(OPENCS_BUNDLE_NAME \"${OPENCS_BUNDLE_NAME_TMP}.app\")\n      configure_file(\"${QT_COCOA_PLUGIN_PATH}\" \"${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}\" COPYONLY)\n      configure_file(\"${OpenMW_SOURCE_DIR}/files/mac/qt.conf\" \"${OPENCS_BUNDLE_NAME}/Contents/Resources/qt.conf\" COPYONLY)\n    endif ()\n\n    install(DIRECTORY \"${APP_BUNDLE_DIR}\" USE_SOURCE_PERMISSIONS DESTINATION \".\" COMPONENT Runtime)\n\n    set(CPACK_GENERATOR \"DragNDrop\")\n    set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})\n    set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})\n    set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})\n    set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})\n\n    set(INSTALLED_OPENMW_APP \"\\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_NAME}\")\n    set(INSTALLED_OPENCS_APP \"\\${CMAKE_INSTALL_PREFIX}/${OPENCS_BUNDLE_NAME}\")\n\n    install(CODE \"\n        set(BU_CHMOD_BUNDLE_ITEMS ON)\n        set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})\n        include(BundleUtilities)\n        cmake_minimum_required(VERSION 3.1)\n    \" COMPONENT Runtime)\n\n    set(ABSOLUTE_PLUGINS \"\")\n\n    set(OSGPlugins_DONT_FIND_DEPENDENCIES 1)\n    find_package(OSGPlugins REQUIRED COMPONENTS ${USED_OSG_PLUGINS})\n\n    foreach (PLUGIN_NAME ${USED_OSG_PLUGINS})\n        string(TOUPPER ${PLUGIN_NAME} PLUGIN_NAME_UC)\n        if(${PLUGIN_NAME_UC}_LIBRARY_RELEASE)\n            set(PLUGIN_ABS ${${PLUGIN_NAME_UC}_LIBRARY_RELEASE})\n        elseif(${PLUGIN_NAME_UC}_LIBRARY)\n            set(PLUGIN_ABS ${${PLUGIN_NAME_UC}_LIBRARY})\n        else()\n            message(FATAL_ERROR \"Can't find library file for ${PLUGIN_NAME}\")\n            # We used to construct the path manually from OSGPlugins_LIB_DIR and the plugin name.\n            # Maybe that could be restored as a fallback?\n        endif()\n        set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS})\n    endforeach ()\n\n    set(OSG_PLUGIN_PREFIX_DIR \"osgPlugins-${OPENSCENEGRAPH_VERSION}\")\n\n    # installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX})\n    # and returns list of install paths for all installed plugins\n    function (install_plugins_for_bundle bundle_path plugins_var)\n        set(RELATIVE_PLUGIN_INSTALL_BASE \"${bundle_path}/Contents/PlugIns/${OSG_PLUGIN_PREFIX_DIR}\")\n\n        set(PLUGINS \"\")\n        set(PLUGIN_INSTALL_BASE \"\\${CMAKE_INSTALL_PREFIX}/${RELATIVE_PLUGIN_INSTALL_BASE}\")\n\n        foreach (PLUGIN ${ABSOLUTE_PLUGINS})\n            get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME)\n            get_filename_component(PLUGIN_RELATIVE_WE ${PLUGIN} NAME_WE)\n\n            set(PLUGIN_DYLIB_IN_BUNDLE \"${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}\")\n            set(PLUGINS ${PLUGINS} \"${PLUGIN_DYLIB_IN_BUNDLE}\")\n\n            install(CODE \"\n                copy_resolved_item_into_bundle(\\\"${PLUGIN}\\\" \\\"${PLUGIN_DYLIB_IN_BUNDLE}\\\")\n            \" COMPONENT Runtime)\n        endforeach ()\n\n        set(${plugins_var} ${PLUGINS} PARENT_SCOPE)\n    endfunction (install_plugins_for_bundle)\n\n    install_plugins_for_bundle(\"${APP_BUNDLE_NAME}\" PLUGINS)\n    install_plugins_for_bundle(\"${OPENCS_BUNDLE_NAME}\" OPENCS_PLUGINS)\n\n    set(PLUGINS ${PLUGINS} \"${INSTALLED_OPENMW_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}\")\n    set(OPENCS_PLUGINS ${OPENCS_PLUGINS} \"${INSTALLED_OPENCS_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}\")\n\n    install(CODE \"\n        function(gp_item_default_embedded_path_override item  default_embedded_path_var)\n            if (\\${item} MATCHES ${OSG_PLUGIN_PREFIX_DIR})\n              set(path \\\"@executable_path/../PlugIns/${OSG_PLUGIN_PREFIX_DIR}\\\")\n              set(\\${default_embedded_path_var} \\\"\\${path}\\\" PARENT_SCOPE)\n            endif()\n        endfunction()\n\n        fixup_bundle(\\\"${INSTALLED_OPENMW_APP}\\\" \\\"${PLUGINS}\\\" \\\"\\\")\n        fixup_bundle(\\\"${INSTALLED_OPENCS_APP}\\\" \\\"${OPENCS_PLUGINS}\\\" \\\"\\\")\n        \" COMPONENT Runtime)\n    include(CPack)\nelseif(NOT APPLE)\n    get_generator_is_multi_config(multi_config)\n    if (multi_config)\n        SET(INSTALL_SOURCE \"${OpenMW_BINARY_DIR}/$<CONFIG>\")\n    else ()\n        SET(INSTALL_SOURCE \"${OpenMW_BINARY_DIR}\")\n    endif ()\n\n    if(WIN32)\n        INSTALL(DIRECTORY \"${INSTALL_SOURCE}/\" DESTINATION \".\" FILES_MATCHING PATTERN \"*.dll\"\n                                                                              PATTERN \"deps\" EXCLUDE\n                                                                              PATTERN \"apps\" EXCLUDE\n                                                                              PATTERN \"CMakeFiles\" EXCLUDE\n                                                                              PATTERN \"components\" EXCLUDE\n                                                                              PATTERN \"docs\" EXCLUDE\n                                                                              PATTERN \"extern\" EXCLUDE\n                                                                              PATTERN \"files\" EXCLUDE\n                                                                              PATTERN \"Testing\" EXCLUDE)\n        INSTALL(DIRECTORY \"${INSTALL_SOURCE}/\" DESTINATION \".\" CONFIGURATIONS Debug;RelWithDebInfo FILES_MATCHING PATTERN \"*.pdb\"\n                                                                                                                  PATTERN \"deps\" EXCLUDE\n                                                                                                                  PATTERN \"apps\" EXCLUDE\n                                                                                                                  PATTERN \"CMakeFiles\" EXCLUDE\n                                                                                                                  PATTERN \"components\" EXCLUDE\n                                                                                                                  PATTERN \"docs\" EXCLUDE\n                                                                                                                  PATTERN \"extern\" EXCLUDE\n                                                                                                                  PATTERN \"files\" EXCLUDE\n                                                                                                                  PATTERN \"Testing\" EXCLUDE)\n        INSTALL(FILES \"${INSTALL_SOURCE}/openmw.cfg.install\" DESTINATION \".\" RENAME \"openmw.cfg\")\n        INSTALL(FILES \"${OpenMW_SOURCE_DIR}/CHANGELOG.md\" DESTINATION \".\" RENAME \"CHANGELOG.txt\")\n        INSTALL(FILES \"${OpenMW_SOURCE_DIR}/README.md\" DESTINATION \".\" RENAME \"README.txt\")\n        INSTALL(FILES \"${OpenMW_SOURCE_DIR}/LICENSE\" DESTINATION \".\" RENAME \"LICENSE.txt\")\n        INSTALL(FILES \"${OpenMW_SOURCE_DIR}/files/mygui/DejaVuFontLicense.txt\" DESTINATION \".\")\n        INSTALL(FILES \"${INSTALL_SOURCE}/defaults.bin\" DESTINATION \".\")\n        # Start of tes3mp addition\n        INSTALL(FILES \"${INSTALL_SOURCE}/tes3mp-client-default.cfg\" DESTINATION \".\")\n        INSTALL(FILES \"${INSTALL_SOURCE}/tes3mp-server-default.cfg\" DESTINATION \".\")\n        # End of tes3mp addition\n        INSTALL(FILES \"${INSTALL_SOURCE}/gamecontrollerdb.txt\" DESTINATION \".\")\n\n        INSTALL(DIRECTORY \"${INSTALL_SOURCE}/resources\" DESTINATION \".\")\n\n        SET(CPACK_GENERATOR \"NSIS\")\n        SET(CPACK_PACKAGE_NAME \"OpenMW\")\n        SET(CPACK_PACKAGE_VENDOR \"OpenMW.org\")\n        SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})\n        SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})\n        SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})\n        SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})\n        SET(CPACK_PACKAGE_EXECUTABLES \"openmw;OpenMW\")\n        IF(BUILD_LAUNCHER)\n            SET(CPACK_PACKAGE_EXECUTABLES \"${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher\")\n        ENDIF(BUILD_LAUNCHER)\n        # Start of tes3mp addition\n        IF(BUILD_BROWSER)\n            SET(CPACK_PACKAGE_EXECUTABLES \"${CPACK_PACKAGE_EXECUTABLES};tes3mp-browser;tes3mp Launcher\")\n        ENDIF(BUILD_BROWSER)\n        # End of tes3mp addition\n        IF(BUILD_OPENCS)\n            SET(CPACK_PACKAGE_EXECUTABLES \"${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set\")\n        ENDIF(BUILD_OPENCS)\n        IF(BUILD_WIZARD)\n            SET(CPACK_PACKAGE_EXECUTABLES \"${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard\")\n        ENDIF(BUILD_WIZARD)\n        SET(CPACK_NSIS_CREATE_ICONS_EXTRA \"CreateShortCut '\\$SMPROGRAMS\\\\\\\\$STARTMENU_FOLDER\\\\\\\\Readme.lnk' '\\$INSTDIR\\\\\\\\README.txt'\")\n        SET(CPACK_NSIS_DELETE_ICONS_EXTRA \"\n            !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP\n            Delete \\\\\\\"$SMPROGRAMS\\\\\\\\$MUI_TEMP\\\\\\\\Readme.lnk\\\\\\\"\n            \")\n        SET(CPACK_RESOURCE_FILE_README \"${OpenMW_SOURCE_DIR}/README.md\")\n        SET(CPACK_PACKAGE_DESCRIPTION_FILE \"${OpenMW_SOURCE_DIR}/README.md\")\n        SET(CPACK_NSIS_EXECUTABLES_DIRECTORY \".\")\n        SET(CPACK_NSIS_DISPLAY_NAME \"OpenMW ${OPENMW_VERSION}\")\n        SET(CPACK_NSIS_HELP_LINK \"https:\\\\\\\\\\\\\\\\www.openmw.org\")\n        SET(CPACK_NSIS_URL_INFO_ABOUT \"https:\\\\\\\\\\\\\\\\www.openmw.org\")\n        SET(CPACK_NSIS_INSTALLED_ICON_NAME \"openmw-launcher.exe\")\n        SET(CPACK_NSIS_MUI_FINISHPAGE_RUN \"openmw-launcher.exe\")\n        # Start of tes3mp change (major)\n        SET(CPACK_NSIS_MUI_ICON \"${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp.ico\")\n        SET(CPACK_NSIS_MUI_UNIICON \"${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp.ico\")\n        # End of tes3mp change (major)\n        SET(CPACK_PACKAGE_ICON \"${OpenMW_SOURCE_DIR}\\\\\\\\files\\\\\\\\openmw.bmp\")\n\n        SET(VCREDIST32 \"${OpenMW_BINARY_DIR}/vcredist_x86.exe\")\n        if(EXISTS ${VCREDIST32})\n            INSTALL(FILES ${VCREDIST32} DESTINATION \"redist\")\n            SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS \"ExecWait '\\\\\\\"$INSTDIR\\\\\\\\redist\\\\\\\\vcredist_x86.exe\\\\\\\" /q /norestart'\" )\n        endif(EXISTS ${VCREDIST32})\n\n        SET(VCREDIST64 \"${OpenMW_BINARY_DIR}/vcredist_x64.exe\")\n        if(EXISTS ${VCREDIST64})\n            INSTALL(FILES ${VCREDIST64} DESTINATION \"redist\")\n            SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS \"ExecWait '\\\\\\\"$INSTDIR\\\\\\\\redist\\\\\\\\vcredist_x64.exe\\\\\\\" /q /norestart'\" )\n        endif(EXISTS ${VCREDIST64})\n\n        SET(OALREDIST \"${OpenMW_BINARY_DIR}/oalinst.exe\")\n        if(EXISTS ${OALREDIST})\n            INSTALL(FILES ${OALREDIST} DESTINATION \"redist\")\n            SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS \"${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}\n                ExecWait '\\\\\\\"$INSTDIR\\\\\\\\redist\\\\\\\\oalinst.exe\\\\\\\" /s'\" )\n        endif(EXISTS ${OALREDIST})\n\n        if(CMAKE_CL_64)\n            SET(CPACK_NSIS_INSTALL_ROOT \"$PROGRAMFILES64\")\n        endif()\n\n        include(CPack)\n    else(WIN32)\n        # Linux installation\n\n        # Install binaries\n        IF(BUILD_OPENMW)\n            # Start of tes3mp change (major)\n            #\n            # Use \"tes3mp\" as the target filename\n            INSTALL(PROGRAMS \"$<TARGET_FILE:tes3mp>\" DESTINATION \"${BINDIR}\" )\n            # End of tes3mp change (major)\n        ENDIF(BUILD_OPENMW)\n        # Start of tes3mp addition\n        #\n        # Add a setting related to building the server\n        IF(BUILD_OPENMW_MP)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/tes3mp-server\" DESTINATION \"${BINDIR}\")\n        ENDIF(BUILD_OPENMW_MP)\n        # End of tes3mp addition\n        IF(BUILD_LAUNCHER)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/openmw-launcher\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_LAUNCHER)\n        # Start of tes3mp addition\n        #\n        # Add a setting related to building the server browser\n        IF(BUILD_BROWSER)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/tes3mp-browser\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_BROWSER)\n        # End of tes3mp addition\n        IF(BUILD_BSATOOL)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/bsatool\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_BSATOOL)\n        IF(BUILD_ESMTOOL)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/esmtool\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_ESMTOOL)\n        IF(BUILD_NIFTEST)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/niftest\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_NIFTEST)\n        IF(BUILD_MWINIIMPORTER)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/openmw-iniimporter\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_MWINIIMPORTER)\n        IF(BUILD_ESSIMPORTER)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/openmw-essimporter\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_ESSIMPORTER)\n        IF(BUILD_OPENCS)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/openmw-cs\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_OPENCS)\n        IF(BUILD_WIZARD)\n            INSTALL(PROGRAMS \"${INSTALL_SOURCE}/openmw-wizard\" DESTINATION \"${BINDIR}\" )\n        ENDIF(BUILD_WIZARD)\n\n        # Install licenses\n        INSTALL(FILES \"files/mygui/DejaVuFontLicense.txt\" DESTINATION \"${LICDIR}\" )\n\n        # Install icon and desktop file\n        INSTALL(FILES \"${OpenMW_BINARY_DIR}/org.openmw.launcher.desktop\" DESTINATION \"${DATAROOTDIR}/applications\" COMPONENT \"openmw\")\n        INSTALL(FILES \"${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png\" DESTINATION \"${ICONDIR}\" COMPONENT \"openmw\")\n        INSTALL(FILES \"${OpenMW_BINARY_DIR}/openmw.appdata.xml\" DESTINATION \"${DATAROOTDIR}/metainfo\" COMPONENT \"openmw\")\n        # Start of tes3mp addition\n        IF(BUILD_BROWSER)\n            INSTALL(FILES \"${OpenMW_BINARY_DIR}/tes3mp-browser.desktop\" DESTINATION \"${DATAROOTDIR}/applications\" COMPONENT \"browser\")\n        ENDIF(BUILD_BROWSER)\n        # End of tes3mp addition\n        IF(BUILD_OPENCS)\n            INSTALL(FILES \"${OpenMW_BINARY_DIR}/org.openmw.cs.desktop\" DESTINATION \"${DATAROOTDIR}/applications\" COMPONENT \"opencs\")\n            INSTALL(FILES \"${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png\" DESTINATION \"${ICONDIR}\" COMPONENT \"opencs\")\n        ENDIF(BUILD_OPENCS)\n\n        # Install global configuration files\n        INSTALL(FILES \"${INSTALL_SOURCE}/defaults.bin\" DESTINATION \"${SYSCONFDIR}\" COMPONENT \"openmw\")\n        INSTALL(FILES \"${INSTALL_SOURCE}/openmw.cfg.install\" DESTINATION \"${SYSCONFDIR}\" RENAME \"openmw.cfg\" COMPONENT \"openmw\")\n        INSTALL(FILES \"${INSTALL_SOURCE}/resources/version\" DESTINATION \"${SYSCONFDIR}\" COMPONENT \"openmw\")\n        INSTALL(FILES \"${INSTALL_SOURCE}/gamecontrollerdb.txt\" DESTINATION \"${SYSCONFDIR}\" COMPONENT \"openmw\")\n\n        # Start of tes3mp addition\n        INSTALL(FILES \"${INSTALL_SOURCE}/tes3mp-client-default.cfg\" DESTINATION \"${SYSCONFDIR}\" COMPONENT \"openmw\")\n        INSTALL(FILES \"${INSTALL_SOURCE}/tes3mp-server-default.cfg\" DESTINATION \"${SYSCONFDIR}\" COMPONENT \"openmw-mp\")\n        # End of tes3mp addition\n\n        IF(BUILD_OPENCS)\n            INSTALL(FILES \"${INSTALL_SOURCE}/defaults-cs.bin\" DESTINATION \"${SYSCONFDIR}\" COMPONENT \"opencs\")\n        ENDIF(BUILD_OPENCS)\n\n        # Install resources\n        INSTALL(DIRECTORY \"${INSTALL_SOURCE}/resources\" DESTINATION \"${DATADIR}\" COMPONENT \"Resources\")\n        INSTALL(DIRECTORY DESTINATION \"${DATADIR}/data\" COMPONENT \"Resources\")\n    endif(WIN32)\nendif(OPENMW_OSX_DEPLOYMENT AND APPLE)\n\n# Doxygen Target -- simply run 'make doc' or 'make doc_pages'\n# output directory for 'make doc'       is \"${OpenMW_BINARY_DIR}/docs/Doxygen\"\n# output directory for 'make doc_pages' is \"${DOXYGEN_PAGES_OUTPUT_DIR}\" if defined\n#                                       or \"${OpenMW_BINARY_DIR}/docs/Pages\" otherwise\nfind_package(Doxygen)\nif (DOXYGEN_FOUND)\n    # determine output directory for doc_pages\n    if (NOT DEFINED DOXYGEN_PAGES_OUTPUT_DIR)\n        set(DOXYGEN_PAGES_OUTPUT_DIR \"${OpenMW_BINARY_DIR}/docs/Pages\")\n    endif ()\n    configure_file(${OpenMW_SOURCE_DIR}/docs/Doxyfile.cmake ${OpenMW_BINARY_DIR}/docs/Doxyfile @ONLY)\n    configure_file(${OpenMW_SOURCE_DIR}/docs/DoxyfilePages.cmake ${OpenMW_BINARY_DIR}/docs/DoxyfilePages @ONLY)\n    add_custom_target(doc\n        ${DOXYGEN_EXECUTABLE} ${OpenMW_BINARY_DIR}/docs/Doxyfile\n        WORKING_DIRECTORY ${OpenMW_BINARY_DIR}\n        COMMENT \"Generating Doxygen documentation at ${OpenMW_BINARY_DIR}/docs/Doxygen\"\n        VERBATIM)\n    add_custom_target(doc_pages\n        ${DOXYGEN_EXECUTABLE} ${OpenMW_BINARY_DIR}/docs/DoxyfilePages\n        WORKING_DIRECTORY ${OpenMW_BINARY_DIR}\n        COMMENT \"Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}\" VERBATIM)\nendif ()\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "How to contribute to OpenMW\n=======================\n\nNot sure what to do with all your free time? Pick out a task from here:\n\nhttps://gitlab.com/OpenMW/openmw/issues\n\nCurrently, we are focused on completing the MW game experience and general polishing. Features out of this scope may be approved in some cases, but you should probably start a discussion first.\n\nNote:\n* Tasks set to 'openmw-future' are usually out of the current scope of the project and can't be started yet.\n* Bugs that are not 'Confirmed' should be confirmed first.\n* Often, it's best to start a discussion about possible solutions before you jump into coding, especially for larger features.\n\nAside from coding, you can also help by triaging the issues list. Check for bugs that are 'Unconfirmed' and try to confirm them on your end, working out any details that may be necessary. Check for bugs that do not conform to [Bug reporting guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) and improve them to do so!\n\nThere are various [Tools](https://wiki.openmw.org/index.php?title=Tools) to facilitate testing/development.\n\nPull Request Guidelines\n=======================\n\nTo facilitate the review process, your pull request description should include the following, if applicable:\n\n* A link back to the bug report or forum discussion that prompted the change. Note: when linking bugs, use the syntax ```[Bug #xyz](https://gitlab.com/OpenMW/openmw/issues/#xyz)``` to create a clickable link. Writing only 'Bug #xyz' will unfortunately create a link to the Github pull request with that number instead.\n* Summary of the changes made\n* Reasoning / motivation behind the change\n* What testing you have carried out to verify the change\n\nFurthermore, we advise to:\n\n* Avoid stuffing unrelated commits into one pull request. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A \"mega\" pull request with lots of unrelated commits in it is likely to get held up in review for a long time.\n* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.\n* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).\n* Make sure each of your changes has a clear objective. Unnecessary changes may lead to merge conflicts, clutter the commit history and slow down review. Code formatting 'fixes' should be avoided, unless you were already changing that particular line anyway.\n* Reference the bug / feature ticket(s) in your commit message (e.g. 'Bug #123') to make it easier to keep track of what we changed for what reason. Our bugtracker will show those commits next to the ticket. If your commit message includes 'Fixes #123', that bug/feature will automatically be set to 'Closed' when your commit is merged.\n* When pulling changes from master, prefer rebase over merge. Consider using a merge if there are conflicts or for long-running PRs.\n\nGuidelines for original engine \"fixes\"\n=================================\n\nFrom time to time you may be tempted to \"fix\" what you think was a \"bug\" in the original game engine.\n\nUnfortunately, the definition of what is a \"bug\" is not so clear. Consider that your \"bug\" is actually a feature unless proven otherwise:\n\n* We have no way of knowing what the original developers really intended (short of asking them, good luck with that).\n* What may seem like an illogical mechanic can actually be part of an attempt to balance the game. \n* Many people will actually <i>like</i> these \"bugs\" because that is what they remember the game for.\n* Exploits may be part of the fun of an open-world game - they reward knowledge with power. There are too many of them to plug them all, anyway.\n\nOpenMW, in its default configuration, is meant to be a faithful reimplementation of Morrowind, minus things like crash bugs, stability issues and severe design errors. However, we try to avoid touching anything that affects the core gameplay, the balancing of the game or introduces incompatibilities with existing mod content.\n\nThat said, we may sometimes evaluate such issues on an individual basis. Common exceptions to the above would be:\n\n* Issues so glaring that they would severely limit the capabilities of the engine in the future (for example, the scripting engine not being allowed to access objects in remote cells)\n* Bugs where the intent is very obvious, and that have little to no balancing impact (e.g. the bug were being tired made it easier to repair items, instead of harder)\n* Bugs that were fixed in an official patch for Morrowind \n\nFeature additions policy\n=====================\n\nWe get it, you have waited so long for feature XYZ to be available in Morrowind and now that OpenMW is here you can not wait to implement your ingenious idea and share it with the world.\n\nUnfortunately, since maintaining features comes at a cost and our resources are limited, we have to be a little selective in what features we allow into the main repository. Generally:\n\n* Features should be as generic and non-redundant as possible.\n* Any feature that is also possible with modding should be done as a mod instead.\n* In the future, OpenMW plans to expand the scope of what is possible with modding, e.g. by moving certain game logic into editable scripts.\n* Currently, modders can edit OpenMW's GUI skins and layout XML files, although there are still a few missing hooks (e.g. scripting support) in order to make this into a powerful way of modding.\n* If a feature introduces new game UI strings, that reduces its chance of being accepted because we do not currently have any way of localizing these to the user's Morrowind installation language.\n\nIf you are in doubt of your feature being within our scope, it is probably best to start a forum discussion first. See the [settings documentation](https://openmw.readthedocs.io/en/stable/reference/modding/settings/index.html) and [Features list](https://wiki.openmw.org/index.php?title=Features) for some examples of features that were deemed acceptable.\n\nReviewing pull requests\n=======================\n\nWe welcome any help in reviewing open PRs. You don't need to be a developer to comment on new features. We also encourage [\"junior\" developers to review senior's work](https://pagefault.blog/2018/04/08/why-junior-devs-should-review-seniors-commits/).\n\nThis review process is divided into two sections because complaining about code or style issues hardly makes sense until the functionality of the PR is deemed OK. Anyone can help with the Functionality Review while most parts of the Code Review require you to have programming experience.\n\nIn addition to the checklist below, make sure to check that the Pull Request Guidelines (first half of this document) were followed.\n\nFirst review\n============\n\n1. Ask for missing information or clarifications. Compare against the project's design goals and roadmap.\n2. Check if the automated tests are passing. If they are not, make the PR author aware of the issue and potentially link to the error line on Travis CI or Appveyor. If the error appears unrelated to the PR and/or the master branch is failing with the same error, our CI has broken and needs to be fixed independently of any open PRs. Raise this issue on the forums, bug tracker or with the relevant maintainer. The PR can be merged in this case as long as you've built it yourself to make sure it does build.\n3. Make sure that the new code has been tested thoroughly, either by asking the author or, preferably, testing yourself. In a complex project like OpenMW, it is easy to make mistakes, typos, etc. Therefore, prefer testing all code changes, no matter how trivial they look. When you have tested a PR that no one has tested so far, post a comment letting us know.\n4. On long running PRs, request the author to update its description with the current state or a checklist of things left to do.\n\nCode Review\n===========\n\n1. Carefully review each line for issues the author may not have thought of, paying special attention to 'special' cases. Often, people build their code with a particular mindset and forget about other configurations or unexpected interactions.\n2. If any changes are workarounds for an issue in an upstream library, make sure the issue was reported upstream so we can eventually drop the workaround when the issue is fixed and the new version of that library is a build dependency.\n3. Make sure PRs do not turn into arguments about hardly related issues. If the PR author disagrees with an established part of the project (e.g. supported build environments), they should open a forum discussion or bug report and in the meantime adjust the PR to adhere to the established way, rather than leaving the PR hanging on a dispute.\n4. Check if the code matches our style guidelines.\n5. Check to make sure the commit history is clean. Squashing should be considered if the review process has made the commit history particularly long. Commits that don't build should be avoided because they are a nuisance for ```git bisect```.\n\nMerging\n=======\n\nTo be able to merge PRs, commit priviledges are required. If you do not have the priviledges, just ping someone that does have them with a short comment like \"Looks good to me @user\".\n\nThe person to merge the PR may either use github's Merge button or if using the command line, use the ```--no-ff``` flag (so a merge commit is created, just like with Github's merge button) and include the pull request number in the commit description.\n\nDealing with regressions\n========================\n\nThe master branch should always be in a working state that is not worse than the previous release in any way. If a regression is found, the first and foremost priority should be to get the regression fixed quickly, either by reverting the change that caused it or finding a better solution. Please avoid leaving the project in the 'broken' state for an extensive period of time while proper solutions are found. If the solution takes more than a day or so then it is usually better to revert the offending change first and reapply it later when fixed.\n\nOther resources\n===============\n\n[GitHub blog - how to write the perfect pull request](https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/)\n\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n\n\nADDITIONAL TERMS APPLICABLE TO THE TES3MP GPL Source Code.\n\n  The following additional terms (\"Additional Terms\") supplement and modify\nthe GNU General Public License, Version 3 (\"GPL\") applicable to the TES3MP\nGPL Source Code (\"TES3MP Source Code\").  In addition to the terms and conditions\nof the GPL, the TES3MP Source Code is subject to the further restrictions below.\n\n1. Replacement of Section 15.  Section 15 of the GPL shall be deleted in its\nentirety and replaced with the following:\n\n\"15. Disclaimer of Warranty.  \n\nTHE PROGRAM IS PROVIDED WITHOUT ANY WARRANTIES, WHETHER EXPRESSED OR IMPLIED,\nINCLUDING, WITHOUT LIMITATION, IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR\nPURPOSE, NON-INFRINGEMENT, TITLE AND MERCHANTABILITY.  THE PROGRAM IS BEING\nDELIVERED OR MADE AVAILABLE \"AS IS\", \"WITH ALL FAULTS\" AND WITHOUT WARRANTY OR\nREPRESENTATION.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE\nPROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST\nOF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\"\n\n2. Replacement of Section 16.  Section 16 of the GPL shall be deleted in its\nentirety and replaced with the following:\n\n\"16.  LIMITATION OF LIABILITY.  \n\nUNDER NO CIRCUMSTANCES SHALL ANY COPYRIGHT HOLDER OR ITS AFFILIATES, OR ANY\nOTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE\nLIABLE TO YOU, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, FOR ANY\nDAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, DIRECT, INDIRECT, SPECIAL,\nINCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM OR OTHER DEALINGS WITH\nTHE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED\nINACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE\nPROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), WHETHER OR NOT ANY COPYRIGHT\nHOLDER OR SUCH OTHER PARTY RECEIVES NOTICE OF ANY SUCH DAMAGES AND WHETHER\nOR NOT SUCH DAMAGES COULD HAVE BEEN FORESEEN.\"\n\n3. LEGAL NOTICES; NO TRADEMARK LICENSE; ORIGIN.  You must reproduce faithfully\nall trademark, copyright and other proprietary and legal notices on any copies\nof the Program or any other required author attributions.  This license does\nnot grant you rights to use any copyright holder or any other party's name,\nlogo, or trademarks.  Neither the name of the copyright holder or its\naffiliates, or any other party who modifies and/or conveys the Program may be\nused to endorse or promote products derived from this software without\nspecific prior written permission.  The origin of the Program must not be\nmisrepresented; you must not claim that you wrote the original Program.\n\nAltered source versions must be plainly marked as such, and must not be\nmisrepresented as being the original Program.\n\n4. INDEMNIFICATION.  IF YOU CONVEY A COVERED WORK AND AGREE WITH ANY RECIPIENT\nOF THAT COVERED WORK THAT YOU WILL ASSUME ANY LIABILITY FOR THAT COVERED WORK,\nYOU HEREBY AGREE TO INDEMNIFY, DEFEND AND HOLD HARMLESS THE OTHER LICENSORS\nAND AUTHORS OF THAT COVERED WORK FOR ANY DAMAEGS, DEMANDS, CLAIMS, LOSSES,\nCAUSES OF ACTION, LAWSUITS, JUDGMENTS EXPENSES (INCLUDING WITHOUT LIMITATION\nREASONABLE ATTORNEYS' FEES AND EXPENSES) OR ANY OTHER LIABLITY ARISING FROM,\nRELATED TO OR IN CONNECTION WITH YOUR ASSUMPTIONS OF LIABILITY.\n"
  },
  {
    "path": "README.md",
    "content": "TES3MP\n======\n\nCopyright (c) 2008-2015, OpenMW Team  \nCopyright (c) 2016-2022, David Cernat & Stanislav Zhukov\n\nTES3MP is a project adding multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), an open-source game engine that supports playing \"The Elder Scrolls III: Morrowind\" by Bethesda Softworks.\n\n* TES3MP version: 0.8.1\n* OpenMW version: 0.47.0\n* License: GPLv3 with additional allowed terms (see [LICENSE](https://github.com/TES3MP/TES3MP/blob/master/LICENSE) for more information)\n\nFont Licenses:\n* DejaVuLGCSansMono.ttf: custom (see [files/mygui/DejaVuFontLicense.txt](https://github.com/TES3MP/TES3MP/blob/master/files/mygui/DejaVuFontLicense.txt) for more information)\n\nProject status\n--------------\n\n[Version changelog](https://github.com/TES3MP/TES3MP/blob/master/tes3mp-changelog.md)\n\nAs of version 0.8.1, TES3MP is fully playable, providing very extensive player, NPC, world and quest synchronization, as well as state saving and loading, all of which are highly customizable via [serverside Lua scripts](https://github.com/TES3MP/CoreScripts).\n\nRemaining gameplay problems mostly relate to AI and the fact that clientside script variables need to be placed on a synchronization whitelist to avoid packet spam.\n\nTES3MP now also has a [VR branch](https://github.com/TES3MP/TES3MP/tree/0.8.1-vr) that combines its code with that of Mads Buvik Sandvei's [OpenMW VR](https://gitlab.com/madsbuvi/openmw).\n\nDonations\n---------------\n\nYou can benefit the project by donating on Patreon to our two developers, [David Cernat](https://www.patreon.com/davidcernat) and [Koncord](https://www.patreon.com/Koncord), as well as by supporting [OpenMW](https://openmw.org).\n\nContributing\n---------------\n\nHelping us with documentation, bug hunting and video showcases is always greatly appreciated.\n\nFor code contributions, it's best to start out with modestly sized fixes and features and work your way up. There are so many different possible implementations of more major features – many of which would cause undesirable code or vision conflicts with OpenMW – that those should be talked over in advance with the existing developers before effort is spent on them.\n\nFeel free to contact the [team members](https://github.com/TES3MP/TES3MP/blob/master/tes3mp-credits.md) for any questions you might have.\n\nGetting started\n---------------\n\n* [Quickstart guide](https://github.com/TES3MP/TES3MP/wiki/Quickstart-guide)\n* [Steam group](https://steamcommunity.com/groups/mwmulti) and its [detailed FAQ](https://steamcommunity.com/groups/mwmulti/discussions/1/353916184342480541/)\n* [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=45)\n* [Discord server](https://discord.gg/ECJk293)\n* [Subreddit](https://www.reddit.com/r/tes3mp)\n* [Known issues and bug reports](https://github.com/TES3MP/TES3MP/issues)\n"
  },
  {
    "path": "apps/benchmarks/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.11)\n\nset(BENCHMARK_ENABLE_TESTING OFF)\nset(BENCHMARK_ENABLE_INSTALL OFF)\nset(BENCHMARK_ENABLE_GTEST_TESTS OFF)\n\nset(SAVED_CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\")\n\nstring(REPLACE \"-Wsuggest-override\" \"\" CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\")\nstring(REPLACE \"-Wundef\" \"\" CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\")\ninclude(FetchContent)\nFetchContent_Declare(benchmark\n    URL https://github.com/google/benchmark/archive/refs/tags/v1.5.2.zip\n    URL_HASH MD5=49395b757a7c4656d70f1328d93efd00\n    SOURCE_DIR fetched/benchmark\n)\nFetchContent_MakeAvailableExcludeFromAll(benchmark)\n\nset(CMAKE_CXX_FLAGS \"${SAVED_CMAKE_CXX_FLAGS}\")\n\nopenmw_add_executable(openmw_detournavigator_navmeshtilescache_benchmark detournavigator/navmeshtilescache.cpp)\ntarget_compile_features(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE cxx_std_17)\ntarget_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark benchmark::benchmark components)\n\nif (UNIX AND NOT APPLE)\n    target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})\nendif()\n"
  },
  {
    "path": "apps/benchmarks/detournavigator/navmeshtilescache.cpp",
    "content": "#include <benchmark/benchmark.h>\n\n#include <components/detournavigator/navmeshtilescache.hpp>\n\n#include <algorithm>\n#include <random>\n#include <iostream>\n\nnamespace\n{\n    using namespace DetourNavigator;\n\n    struct Key\n    {\n        osg::Vec3f mAgentHalfExtents;\n        TilePosition mTilePosition;\n        RecastMesh mRecastMesh;\n        std::vector<OffMeshConnection> mOffMeshConnections;\n    };\n\n    struct Item\n    {\n        Key mKey;\n        NavMeshData mValue;\n    };\n\n    template <typename Random>\n    TilePosition generateTilePosition(int max, Random& random)\n    {\n        std::uniform_int_distribution<int> distribution(0, max);\n        return TilePosition(distribution(random), distribution(random));\n    }\n\n    template <typename Random>\n    osg::Vec3f generateAgentHalfExtents(float min, float max, Random& random)\n    {\n        std::uniform_int_distribution<int> distribution(min, max);\n        return osg::Vec3f(distribution(random), distribution(random), distribution(random));\n    }\n\n    template <typename OutputIterator, typename Random>\n    void generateVertices(OutputIterator out, std::size_t number, Random& random)\n    {\n        std::uniform_real_distribution<float> distribution(0.0, 1.0);\n        std::generate_n(out, 3 * (number - number % 3), [&] { return distribution(random); });\n    }\n\n    template <typename OutputIterator, typename Random>\n    void generateIndices(OutputIterator out, int max, std::size_t number, Random& random)\n    {\n        std::uniform_int_distribution<int> distribution(0, max);\n        std::generate_n(out, number - number % 3, [&] { return distribution(random); });\n    }\n\n    AreaType toAreaType(int index)\n    {\n        switch (index)\n        {\n            case 0: return AreaType_null;\n            case 1: return AreaType_water;\n            case 2: return AreaType_door;\n            case 3: return AreaType_pathgrid;\n            case 4: return AreaType_ground;\n        }\n        return AreaType_null;\n    }\n\n    template <typename Random>\n    AreaType generateAreaType(Random& random)\n    {\n        std::uniform_int_distribution<int> distribution(0, 4);\n        return toAreaType(distribution(random));;\n    }\n\n    template <typename OutputIterator, typename Random>\n    void generateAreaTypes(OutputIterator out, std::size_t triangles, Random& random)\n    {\n        std::generate_n(out, triangles, [&] { return generateAreaType(random); });\n    }\n\n    template <typename OutputIterator, typename Random>\n    void generateWater(OutputIterator out, std::size_t count, Random& random)\n    {\n        std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);\n        std::generate_n(out, count, [&] {\n            const btVector3 shift(distribution(random), distribution(random), distribution(random));\n            return RecastMesh::Water {1, btTransform(btMatrix3x3::getIdentity(), shift)};\n        });\n    }\n\n    template <typename OutputIterator, typename Random>\n    void generateOffMeshConnection(OutputIterator out, std::size_t count, Random& random)\n    {\n        std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);\n        std::generate_n(out, count, [&] {\n            const osg::Vec3f start(distribution(random), distribution(random), distribution(random));\n            const osg::Vec3f end(distribution(random), distribution(random), distribution(random));\n            return OffMeshConnection {start, end, generateAreaType(random)};\n        });\n    }\n\n    template <class Random>\n    Key generateKey(std::size_t triangles, Random& random)\n    {\n        const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random);\n        const TilePosition tilePosition = generateTilePosition(10000, random);\n        const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);\n        const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);\n        std::vector<float> vertices;\n        generateVertices(std::back_inserter(vertices), triangles * 1.98, random);\n        std::vector<int> indices;\n        generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.53, random);\n        std::vector<AreaType> areaTypes;\n        generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);\n        std::vector<RecastMesh::Water> water;\n        generateWater(std::back_inserter(water), 2, random);\n        RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices),\n                              std::move(areaTypes), std::move(water));\n        std::vector<OffMeshConnection> offMeshConnections;\n        generateOffMeshConnection(std::back_inserter(offMeshConnections), 300, random);\n        return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)};\n    }\n\n    constexpr std::size_t trianglesPerTile = 310;\n\n    template <typename OutputIterator, typename Random>\n    void generateKeys(OutputIterator out, std::size_t count, Random& random)\n    {\n        std::generate_n(out, count, [&] { return generateKey(trianglesPerTile, random); });\n    }\n\n    template <typename OutputIterator, typename Random>\n    void fillCache(OutputIterator out, Random& random, NavMeshTilesCache& cache)\n    {\n        std::size_t size = cache.getStats().mNavMeshCacheSize;\n\n        while (true)\n        {\n            Key key = generateKey(trianglesPerTile, random);\n            cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData());\n            *out++ = std::move(key);\n            const std::size_t newSize = cache.getStats().mNavMeshCacheSize;\n            if (size >= newSize)\n                break;\n            size = newSize;\n        }\n    }\n\n    template <std::size_t maxCacheSize, int hitPercentage>\n    void getFromFilledCache(benchmark::State& state)\n    {\n        NavMeshTilesCache cache(maxCacheSize);\n        std::minstd_rand random;\n        std::vector<Key> keys;\n        fillCache(std::back_inserter(keys), random, cache);\n        generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random);\n        std::size_t n = 0;\n\n        while (state.KeepRunning())\n        {\n            const auto& key = keys[n++ % keys.size()];\n            const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections);\n            benchmark::DoNotOptimize(result);\n        }\n    }\n\n    constexpr auto getFromFilledCache_1m_100hit = getFromFilledCache<1 * 1024 * 1024, 100>;\n    constexpr auto getFromFilledCache_4m_100hit = getFromFilledCache<4 * 1024 * 1024, 100>;\n    constexpr auto getFromFilledCache_16m_100hit = getFromFilledCache<16 * 1024 * 1024, 100>;\n    constexpr auto getFromFilledCache_64m_100hit = getFromFilledCache<64 * 1024 * 1024, 100>;\n    constexpr auto getFromFilledCache_1m_70hit = getFromFilledCache<1 * 1024 * 1024, 70>;\n    constexpr auto getFromFilledCache_4m_70hit = getFromFilledCache<4 * 1024 * 1024, 70>;\n    constexpr auto getFromFilledCache_16m_70hit = getFromFilledCache<16 * 1024 * 1024, 70>;\n    constexpr auto getFromFilledCache_64m_70hit = getFromFilledCache<64 * 1024 * 1024, 70>;\n\n    template <std::size_t maxCacheSize>\n    void setToBoundedNonEmptyCache(benchmark::State& state)\n    {\n        NavMeshTilesCache cache(maxCacheSize);\n        std::minstd_rand random;\n        std::vector<Key> keys;\n        fillCache(std::back_inserter(keys), random, cache);\n        generateKeys(std::back_inserter(keys), keys.size() * 2, random);\n        std::reverse(keys.begin(), keys.end());\n        std::size_t n = 0;\n\n        while (state.KeepRunning())\n        {\n            const auto& key = keys[n++ % keys.size()];\n            const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData());\n            benchmark::DoNotOptimize(result);\n        }\n    }\n\n    constexpr auto setToBoundedNonEmptyCache_1m = setToBoundedNonEmptyCache<1 * 1024 * 1024>;\n    constexpr auto setToBoundedNonEmptyCache_4m = setToBoundedNonEmptyCache<4 * 1024 * 1024>;\n    constexpr auto setToBoundedNonEmptyCache_16m = setToBoundedNonEmptyCache<16 * 1024 * 1024>;\n    constexpr auto setToBoundedNonEmptyCache_64m = setToBoundedNonEmptyCache<64 * 1024 * 1024>;\n} // namespace\n\nBENCHMARK(getFromFilledCache_1m_100hit);\nBENCHMARK(getFromFilledCache_4m_100hit);\nBENCHMARK(getFromFilledCache_16m_100hit);\nBENCHMARK(getFromFilledCache_64m_100hit);\nBENCHMARK(getFromFilledCache_1m_70hit);\nBENCHMARK(getFromFilledCache_4m_70hit);\nBENCHMARK(getFromFilledCache_16m_70hit);\nBENCHMARK(getFromFilledCache_64m_70hit);\nBENCHMARK(setToBoundedNonEmptyCache_1m);\nBENCHMARK(setToBoundedNonEmptyCache_4m);\nBENCHMARK(setToBoundedNonEmptyCache_16m);\nBENCHMARK(setToBoundedNonEmptyCache_64m);\n\nBENCHMARK_MAIN();\n"
  },
  {
    "path": "apps/browser/CMakeLists.txt",
    "content": "\nset (CMAKE_CXX_STANDARD 14)\n\nset(BROWSER_UI\n        ${CMAKE_SOURCE_DIR}/files/tes3mp/ui/Main.ui\n        ${CMAKE_SOURCE_DIR}/files/tes3mp/ui/ServerInfo.ui\n        )\nset(BROWSER\n        main.cpp\n        MainWindow.cpp\n        ServerModel.cpp\n        ServerInfoDialog.cpp\n        MySortFilterProxyModel.cpp\n        netutils/HTTPNetwork.cpp\n        netutils/Utils.cpp\n        netutils/QueryClient.cpp\n        PingUpdater.cpp\n        PingHelper.cpp\n        QueryHelper.cpp\n        ${CMAKE_SOURCE_DIR}/files/tes3mp/browser.rc\n        )\n\nset(BROWSER_HEADER_MOC\n        MainWindow.hpp\n        ServerModel.hpp\n        ServerInfoDialog.hpp\n        MySortFilterProxyModel.hpp\n        PingUpdater.hpp\n        PingHelper.hpp\n        QueryHelper.hpp\n        )\n\nset(BROWSER_HEADER\n        ${BROWSER_HEADER_MOC}\n        netutils/HTTPNetwork.hpp\n        netutils/Utils.hpp\n        netutils/QueryClient.hpp\n        Types.hpp\n        )\n\nsource_group(browser FILES ${BROWSER} ${BROWSER_HEADER})\n\nset(QT_USE_QTGUI 1)\n\n# Set some platform specific settings\nif(WIN32)\n    set(GUI_TYPE WIN32)\n    set(QT_USE_QTMAIN TRUE)\nendif(WIN32)\n\nif (DESIRED_QT_VERSION MATCHES 4)\n    message(SEND_ERROR \"QT4 is not supported.\")\n    #include(${QT_USE_FILE})\n    #QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)\n    #QT4_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})\n    #QT4_WRAP_UI(UI_HDRS ${BROWSER_UI})\nelse()\n    QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)\n    QT5_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})\n    QT5_WRAP_UI(UI_HDRS ${BROWSER_UI})\nendif()\n\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR})\nif(NOT WIN32)\n    include_directories(${LIBUNSHIELD_INCLUDE_DIR})\nendif(NOT WIN32)\n\n# Main executable\nadd_executable(tes3mp-browser\n        ${GUI_TYPE}\n        ${BROWSER}\n        ${BROWSER_HEADER}\n        ${RCC_SRCS}\n        ${MOC_SRCS}\n        ${UI_HDRS}\n        )\n\nif (WIN32)\n    INSTALL(TARGETS tes3mp-browser RUNTIME DESTINATION \".\")\nendif (WIN32)\n\ntarget_link_libraries(tes3mp-browser\n        ${SDL2_LIBRARY_ONLY}\n        ${RakNet_LIBRARY}\n        components\n        )\n\nif (DESIRED_QT_VERSION MATCHES 4)\n#    target_link_libraries(tes3mp-browser ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY})\n#    if(WIN32)\n#        target_link_libraries(tes3mp-browser ${QT_QTMAIN_LIBRARY})\n#    endif(WIN32)\nelse()\n    qt5_use_modules(tes3mp-browser Widgets Core)\nendif()\n\nif (BUILD_WITH_CODE_COVERAGE)\n    add_definitions (--coverage)\n    target_link_libraries(tes3mp-browser gcov)\nendif()\n"
  },
  {
    "path": "apps/browser/MainWindow.cpp",
    "content": "#include \"MainWindow.hpp\"\n#include \"QueryHelper.hpp\"\n#include \"PingHelper.hpp\"\n#include \"ServerInfoDialog.hpp\"\n#include \"components/files/configurationmanager.hpp\"\n#include <qdebug.h>\n#include <QInputDialog>\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QFile>\n#include <QJsonDocument>\n\n\nusing namespace Process;\nusing namespace std;\n\nMainWindow::MainWindow(QWidget *parent)\n{\n    setupUi(this);\n\n    mGameInvoker = new ProcessInvoker();\n\n    browser = new ServerModel;\n    favorites = new ServerModel;\n    proxyModel = new MySortFilterProxyModel(this);\n    proxyModel->setSourceModel(browser);\n    tblServerBrowser->setModel(proxyModel);\n    tblFavorites->setModel(proxyModel);\n\n    // Remove Favorites tab while it remains broken\n    tabWidget->removeTab(1);\n\n    tblServerBrowser->hideColumn(ServerData::ADDR);\n    tblFavorites->hideColumn(ServerData::ADDR);\n\n    PingHelper::Get().SetModel((ServerModel*)proxyModel->sourceModel());\n    queryHelper = new QueryHelper(proxyModel->sourceModel());\n    connect(queryHelper, &QueryHelper::started, [this](){actionRefresh->setEnabled(false);});\n    connect(queryHelper, &QueryHelper::finished, [this](){actionRefresh->setEnabled(true);});\n\n    connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSwitched(int)));\n    connect(actionAdd, SIGNAL(triggered(bool)), this, SLOT(addServer()));\n    connect(actionAdd_by_IP, SIGNAL(triggered(bool)), this, SLOT(addServerByIP()));\n    connect(actionDelete, SIGNAL(triggered(bool)), this, SLOT(deleteServer()));\n    connect(actionRefresh, SIGNAL(triggered(bool)), queryHelper, SLOT(refresh()));\n    connect(actionPlay, SIGNAL(triggered(bool)), this, SLOT(play()));\n    connect(tblServerBrowser, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));\n    connect(tblFavorites, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));\n    connect(tblFavorites, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));\n    connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));\n    connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));\n    connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(bool)));\n    connect(cBBoxWOPass, SIGNAL(toggled(bool)), this, SLOT(noPasswordSwitch(bool)));\n    connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));\n    connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));\n    loadFavorites();\n    queryHelper->refresh();\n}\n\nMainWindow::~MainWindow()\n{\n    delete mGameInvoker;\n}\n\nvoid MainWindow::addServerAndUpdate(const QString &addr)\n{\n    favorites->insertRow(0);\n    QModelIndex mi = favorites->index(0, ServerData::ADDR);\n    favorites->setData(mi, addr, Qt::EditRole);\n    //NetController::get()->updateInfo(favorites, mi);\n    //QueryClient::Update(RakNet::SystemAddress())\n    /*auto data = QueryClient::Get().Query();\n    if (data.empty())\n       return;\n    transform(data.begin(), data.end(), back_inserter());*/\n}\n\nvoid MainWindow::addServer()\n{\n    int id = tblServerBrowser->selectionModel()->currentIndex().row();\n\n    if (id >= 0)\n    {\n        int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();\n        favorites->myData.push_back(browser->myData[sourceId]);\n    }\n}\n\nvoid MainWindow::addServerByIP()\n{\n    bool ok;\n    QString text = QInputDialog::getText(this, tr(\"Add Server by address\"), tr(\"Address:\"), QLineEdit::Normal, \"\", &ok);\n    if (ok && !text.isEmpty())\n        addServerAndUpdate(text);\n}\n\nvoid MainWindow::deleteServer()\n{\n    if (tabWidget->currentIndex() != 1)\n        return;\n    int id = tblFavorites->selectionModel()->currentIndex().row();\n    if (id >= 0)\n    {\n        int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();\n        favorites->removeRow(sourceId);\n        if (favorites->myData.isEmpty())\n        {\n            actionPlay->setEnabled(false);\n            actionDelete->setEnabled(false);\n        }\n    }\n}\n\nvoid MainWindow::play()\n{\n    QTableView *curTable = tabWidget->currentIndex() ? tblFavorites : tblServerBrowser;\n    int id = curTable->selectionModel()->currentIndex().row();\n    if (id < 0)\n        return;\n\n\n    ServerModel *sm = ((ServerModel*)proxyModel->sourceModel());\n\n    int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();\n    ServerInfoDialog infoDialog(sm->myData[sourceId].addr, this);\n\n    if (!infoDialog.exec())\n        return;\n\n    if (!infoDialog.isUpdated())\n        return;\n\n    QStringList arguments;\n    arguments.append(QLatin1String(\"--connect=\") + sm->myData[sourceId].addr.toLatin1());\n\n    if (sm->myData[sourceId].GetPassword() == 1)\n    {\n        bool ok;\n        QString passw = QInputDialog::getText(this, tr(\"Connecting to: \") + sm->myData[sourceId].addr, tr(\"Password: \"),\n                                              QLineEdit::Password, \"\", &ok);\n        if (!ok)\n            return;\n        arguments.append(QLatin1String(\"--password=\") + passw.toLatin1());\n    }\n\n    if (mGameInvoker->startProcess(QLatin1String(\"tes3mp\"), arguments, true))\n        return qApp->quit();\n}\n\nvoid MainWindow::tabSwitched(int index)\n{\n    if (index == 0)\n    {\n        proxyModel->setSourceModel(browser);\n        actionDelete->setEnabled(false);\n    }\n    else\n    {\n        proxyModel->setSourceModel(favorites);\n    }\n    actionPlay->setEnabled(false);\n    actionAdd->setEnabled(false);\n}\n\nvoid MainWindow::serverSelected()\n{\n    actionPlay->setEnabled(true);\n    if (tabWidget->currentIndex() == 0)\n        actionAdd->setEnabled(true);\n    if (tabWidget->currentIndex() == 1)\n        actionDelete->setEnabled(true);\n}\n\nvoid MainWindow::closeEvent(QCloseEvent *event)\n{\n    Files::ConfigurationManager cfgMgr;\n    QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / \"favorites.dat\").string());\n\n    QJsonArray saveData;\n    for (auto server : favorites->myData)\n        saveData.push_back(server.addr);\n\n    QFile file(cfgPath);\n\n    if (!file.open(QIODevice::WriteOnly))\n    {\n        qDebug() << \"Cannot save \" << cfgPath;\n        return;\n    }\n\n    file.write(QJsonDocument(saveData).toJson());\n    file.close();\n}\n\n\nvoid MainWindow::loadFavorites()\n{\n    Files::ConfigurationManager cfgMgr;\n    QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / \"favorites.dat\").string());\n\n    QFile file(cfgPath);\n    if (!file.open(QIODevice::ReadOnly))\n    {\n        qDebug() << \"Cannot open \" << cfgPath;\n        return;\n    }\n\n    QJsonDocument jsonDoc(QJsonDocument::fromJson(file.readAll()));\n\n    for (auto server : jsonDoc.array())\n        addServerAndUpdate(server.toString());\n\n    file.close();\n}\n\nvoid MainWindow::notFullSwitch(bool state)\n{\n    proxyModel->filterFullServer(state);\n}\n\nvoid MainWindow::havePlayersSwitch(bool state)\n{\n    proxyModel->filterEmptyServers(state);\n}\n\nvoid MainWindow::noPasswordSwitch(bool state)\n{\n    proxyModel->filterPassworded(state);\n}\n\nvoid MainWindow::maxLatencyChanged(int index)\n{\n    int maxLatency = index * 50;\n    proxyModel->pingLessThan(maxLatency);\n\n}\n\nvoid MainWindow::gamemodeChanged(const QString &text)\n{\n    proxyModel->setFilterFixedString(text);\n    proxyModel->setFilterKeyColumn(ServerData::MODNAME);\n}\n"
  },
  {
    "path": "apps/browser/MainWindow.hpp",
    "content": "#ifndef NEWLAUNCHER_MAIN_HPP\n#define NEWLAUNCHER_MAIN_HPP\n\n\n#include \"ui_Main.h\"\n#include \"ServerModel.hpp\"\n#include \"MySortFilterProxyModel.hpp\"\n#include <components/process/processinvoker.hpp>\n\nclass QueryHelper;\n\nclass MainWindow : public QMainWindow,  private Ui::MainWindow\n{\n    Q_OBJECT\npublic:\n    explicit MainWindow(QWidget *parent = nullptr);\n    ~MainWindow() override;\nprotected:\n    void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;\n    void addServerAndUpdate(const QString &addr);\nprotected slots:\n    void tabSwitched(int index);\n    void addServer();\n    void addServerByIP();\n    void deleteServer();\n    void play();\n    void serverSelected();\n    void notFullSwitch(bool state);\n    void havePlayersSwitch(bool state);\n    void noPasswordSwitch(bool state);\n    void maxLatencyChanged(int index);\n    void gamemodeChanged(const QString &text);\nprivate:\n    QueryHelper *queryHelper;\n    Process::ProcessInvoker *mGameInvoker;\n    ServerModel *browser, *favorites;\n    MySortFilterProxyModel *proxyModel;\n    void loadFavorites();\n};\n\n\n#endif //NEWLAUNCHER_MAIN_HPP\n"
  },
  {
    "path": "apps/browser/MySortFilterProxyModel.cpp",
    "content": "#include \"MySortFilterProxyModel.hpp\"\n#include \"ServerModel.hpp\"\n\n#include <qdebug.h>\n#include <apps/browser/netutils/Utils.hpp>\n\nbool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const\n{\n\n    QModelIndex pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);\n    QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);\n    QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_PLAYERS, sourceParent);\n    QModelIndex passwordIndex = sourceModel()->index(sourceRow, ServerData::PASSW, sourceParent);\n\n    bool pingOk;\n    int ping = sourceModel()->data(pingIndex).toInt(&pingOk);\n    int players =  sourceModel()->data(plIndex).toInt();\n    int maxPlayers =  sourceModel()->data(maxPlIndex).toInt();\n\n    if (maxPing > 0 && (ping == -1 || ping > maxPing || !pingOk))\n        return false;\n    if (filterEmpty && players == 0)\n        return false;\n    if (filterFull && players >= maxPlayers)\n        return false;\n    if(filterPasswEnabled && sourceModel()->data(passwordIndex).toString() == \"Yes\")\n        return false;\n\n    return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);\n}\n\nbool MySortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const\n{\n    if(sortColumn() == ServerData::PING)\n    {\n        bool valid;\n\n        int pingright = sourceModel()->data(source_right).toInt(&valid);\n        pingright = valid ? pingright : PING_UNREACHABLE;\n\n        int pingleft = sourceModel()->data(source_left).toInt(&valid);\n        pingleft = valid ? pingleft : PING_UNREACHABLE;\n        return pingleft < pingright;\n    }\n    else\n        return QSortFilterProxyModel::lessThan(source_left, source_right);\n}\n\nMySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)\n{\n    filterEmpty = false;\n    filterFull = false;\n    filterPasswEnabled = false;\n    maxPing = 0;\n    setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);\n}\n\nvoid MySortFilterProxyModel::filterEmptyServers(bool state)\n{\n    filterEmpty = state;\n    invalidateFilter();\n}\n\nvoid MySortFilterProxyModel::filterFullServer(bool state)\n{\n    filterFull = state;\n    invalidateFilter();\n}\n\nvoid MySortFilterProxyModel::pingLessThan(int maxPing)\n{\n    this->maxPing = maxPing;\n    invalidateFilter();\n}\n\nvoid MySortFilterProxyModel::filterPassworded(bool state)\n{\n    filterPasswEnabled = state;\n    invalidateFilter();\n}\n"
  },
  {
    "path": "apps/browser/MySortFilterProxyModel.hpp",
    "content": "#ifndef OPENMW_MYSORTFILTERPROXYMODEL_HPP\n#define OPENMW_MYSORTFILTERPROXYMODEL_HPP\n\n\n#include <QSortFilterProxyModel>\n\nclass MySortFilterProxyModel : public QSortFilterProxyModel\n{\n    Q_OBJECT\nprotected:\n    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;\n    bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const Q_DECL_FINAL;\npublic:\n    explicit MySortFilterProxyModel(QObject *parent);\n    void filterFullServer(bool state);\n    void filterEmptyServers(bool state);\n    void filterPassworded(bool state);\n    void pingLessThan(int maxPing);\nprivate:\n    bool filterEmpty, filterFull, filterPasswEnabled;\n    int maxPing;\n};\n\n\n#endif //OPENMW_MYSORTFILTERPROXYMODEL_HPP\n"
  },
  {
    "path": "apps/browser/PingHelper.cpp",
    "content": "#include \"PingHelper.hpp\"\n#include \"ServerModel.hpp\"\n#include <QDebug>\n#include \"PingUpdater.hpp\"\n\nvoid PingHelper::Add(int row, const AddrPair &addrPair)\n{\n    pingUpdater->addServer(row, addrPair);\n    if (!pingThread->isRunning())\n        pingThread->start();\n}\n\nvoid PingHelper::Reset()\n{\n    //if (pingThread->isRunning())\n    Stop();\n}\n\nvoid PingHelper::Stop()\n{\n    emit pingUpdater->stop();\n}\n\nvoid PingHelper::SetModel(QAbstractTableModel *model)\n{\n    this->model = model;\n}\n\nvoid PingHelper::update(int row, unsigned ping)\n{\n    model->setData(model->index(row, ServerData::PING), ping);\n}\n\nPingHelper &PingHelper::Get()\n{\n    static PingHelper helper;\n    return helper;\n}\n\nPingHelper::PingHelper() : QObject()\n{\n    pingThread = new QThread;\n    pingUpdater = new PingUpdater;\n    pingUpdater->moveToThread(pingThread);\n\n    connect(pingThread, SIGNAL(started()), pingUpdater, SLOT(process()));\n    connect(pingUpdater, SIGNAL(start()), pingThread, SLOT(start()));\n    connect(pingUpdater, SIGNAL(finished()), pingThread, SLOT(quit()));\n    connect(this, SIGNAL(stop()), pingUpdater, SLOT(stop()));\n    //connect(pingUpdater, SIGNAL(finished()), pingUpdater, SLOT(deleteLater()));\n    connect(pingUpdater, SIGNAL(updateModel(int, unsigned)), this, SLOT(update(int, unsigned)));\n}\n"
  },
  {
    "path": "apps/browser/PingHelper.hpp",
    "content": "#ifndef OPENMW_PINGHELPER_HPP\n#define OPENMW_PINGHELPER_HPP\n\n#include <QObject>\n#include <QAbstractTableModel>\n#include <QThread>\n#include \"Types.hpp\"\n\nclass PingUpdater;\n\nclass PingHelper : public QObject\n{\n    Q_OBJECT\npublic:\n\n    void Reset();\n    void Add(int row, const AddrPair &addrPair);\n    void Stop();\n    void SetModel(QAbstractTableModel *model);\n    //void UpdateImmedialy(PingUpdater::AddrPair addrPair);\n    static PingHelper &Get();\n\n    PingHelper(const PingHelper&) = delete;\n    PingHelper& operator=(const PingHelper&) = delete;\nprivate:\n    PingHelper();\nsignals:\n    void stop();\npublic slots:\n    void update(int row, unsigned ping);\nprivate:\n    QThread *pingThread;\n    PingUpdater *pingUpdater;\n    QAbstractTableModel *model;\n};\n\n\n#endif //OPENMW_PINGHELPER_HPP\n"
  },
  {
    "path": "apps/browser/PingUpdater.cpp",
    "content": "#include \"PingUpdater.hpp\"\n#include \"netutils/Utils.hpp\"\n#include <QDebug>\n#include <QModelIndex>\n#include <QThread>\n\nvoid PingUpdater::stop()\n{\n    servers.clear();\n    run = false;\n}\n\nvoid PingUpdater::addServer(int row, const AddrPair &addr)\n{\n    servers.push_back({row, addr});\n    run = true;\n    emit start();\n}\n\nvoid PingUpdater::process()\n{\n    while (run)\n    {\n        if (servers.count() == 0)\n        {\n            QThread::msleep(1000);\n            if (servers.count() == 0)\n            {\n                qDebug() << \"PingUpdater stopped due to inactivity\";\n                run = false;\n                continue;\n            }\n        }\n\n        ServerRow server = servers.back();\n        servers.pop_back();\n\n        unsigned ping = PingRakNetServer(server.second.first.toLatin1(), server.second.second);\n\n        qDebug() << \"Pong from\" << server.second.first + \"|\" + QString::number(server.second.second)\n                 << \":\" << ping << \"ms\" << \"Sizeof servers: \" << servers.size();\n\n        emit updateModel(server.first, ping);\n    }\n    emit finished();\n}\n"
  },
  {
    "path": "apps/browser/PingUpdater.hpp",
    "content": "#ifndef OPENMW_PINGUPDATER_HPP\n#define OPENMW_PINGUPDATER_HPP\n\n#include <QObject>\n#include <QVector>\n\n#include \"Types.hpp\"\n\nclass PingUpdater : public QObject\n{\n    Q_OBJECT\npublic:\n    void addServer(int row, const AddrPair &addrPair);\npublic slots:\n    void stop();\n    void process();\nsignals:\n    void start();\n    void updateModel(int row, unsigned ping);\n    void finished();\nprivate:\n    QVector<ServerRow> servers;\n    bool run;\n};\n\n\n#endif //OPENMW_PINGUPDATER_HPP\n"
  },
  {
    "path": "apps/browser/QueryHelper.cpp",
    "content": "#include \"netutils/QueryClient.hpp\"\n#include \"netutils/Utils.hpp\"\n#include \"QueryHelper.hpp\"\n#include \"PingHelper.hpp\"\n\nQueryUpdate *queryUpdate;\n\n\nQueryHelper::QueryHelper(QAbstractItemModel *model)\n{\n    qRegisterMetaType<QueryData>(\"QueryData\");\n    queryThread = new QThread;\n    queryUpdate = new QueryUpdate;\n    _model = model;\n    connect(queryThread, SIGNAL(started()), queryUpdate, SLOT(process()));\n    connect(queryUpdate, SIGNAL(finished()), queryThread, SLOT(quit()));\n    connect(queryUpdate, &QueryUpdate::finished, [this](){emit finished();});\n    connect(queryUpdate, SIGNAL(updateModel(const QString&, unsigned short, const QueryData&)),\n            this, SLOT(update(const QString&, unsigned short, const QueryData&)));\n    queryUpdate->moveToThread(queryThread);\n}\n\nvoid QueryHelper::refresh()\n{\n    if (!queryThread->isRunning())\n    {\n        _model->removeRows(0, _model->rowCount());\n        PingHelper::Get().Stop();\n        queryThread->start();\n        emit started();\n    }\n}\n\nvoid QueryHelper::terminate()\n{\n    queryThread->terminate();\n}\n\nvoid QueryHelper::update(const QString &addr, unsigned short port, const QueryData& data)\n{\n    ServerModel *model = ((ServerModel*)_model);\n    model->insertRow(model->rowCount());\n    int row = model->rowCount() - 1;\n\n    QModelIndex mi = model->index(row, ServerData::ADDR);\n    model->setData(mi, addr + \":\" + QString::number(port));\n\n    mi = model->index(row, ServerData::PLAYERS);\n    model->setData(mi, (int)data.players.size());\n\n    mi = model->index(row, ServerData::MAX_PLAYERS);\n    model->setData(mi, data.GetMaxPlayers());\n\n    mi = model->index(row, ServerData::HOSTNAME);\n    model->setData(mi, data.GetName());\n\n    mi = model->index(row, ServerData::MODNAME);\n    model->setData(mi, data.GetGameMode());\n\n    mi = model->index(row, ServerData::VERSION);\n    model->setData(mi, data.GetVersion());\n\n    mi = model->index(row, ServerData::PASSW);\n    model->setData(mi, data.GetPassword() == 1);\n\n    mi = model->index(row, ServerData::PING);\n    model->setData(mi, PING_UNREACHABLE);\n    PingHelper::Get().Add(row, {addr, port});\n}\n\nvoid QueryUpdate::process()\n{\n    auto data = QueryClient::Get().Query();\n    if (QueryClient::Get().Status() != ID_MASTER_QUERY)\n    {\n        emit finished();\n        return;\n    }\n\n    for (const auto &server : data)\n        emit updateModel(server.first.ToString(false), server.first.GetPort(), server.second);\n    emit finished();\n}\n"
  },
  {
    "path": "apps/browser/QueryHelper.hpp",
    "content": "#ifndef OPENMW_QUERYHELPER_HPP\n#define OPENMW_QUERYHELPER_HPP\n\n\n#include <QObject>\n#include <vector>\n#include <QAbstractItemModel>\n#include <components/openmw-mp/Master/MasterData.hpp>\n\nQ_DECLARE_METATYPE(QueryData)\n\nclass QueryHelper : public QObject\n{\nQ_OBJECT\npublic:\n    explicit QueryHelper(QAbstractItemModel *model);\npublic slots:\n    void refresh();\n    void terminate();\nprivate slots:\n    void update(const QString &addr, unsigned short port, const QueryData& data);\nsignals:\n    void finished();\n    void started();\nprivate:\n    QThread *queryThread;\n    QAbstractItemModel *_model;\n};\n\nclass QueryUpdate : public QObject\n{\n    friend class QueryHelper;\nQ_OBJECT\nsignals:\n    void finished();\n    void updateModel(const QString &addr, unsigned short port, const QueryData& data);\npublic slots:\n    void process();\n};\n\n#endif //OPENMW_QUERYHELPER_HPP\n"
  },
  {
    "path": "apps/browser/ServerInfoDialog.cpp",
    "content": "#include <apps/browser/netutils/QueryClient.hpp>\n#include \"qdebug.h\"\n\n#include \"ServerInfoDialog.hpp\"\n#include <algorithm>\n#include <utility>\n#include <QThread>\n\nusing namespace std;\nusing namespace RakNet;\n\nThrWorker::ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port): addr(std::move(addr)), port(port), stopped(false)\n{\n    this->dialog = dialog;\n}\n\nvoid ThrWorker::process()\n{\n    stopped = false;\n    auto newSD = QueryClient::Get().Update(SystemAddress(addr.toUtf8(), port));\n    if (dialog != nullptr)\n        dialog->setData(newSD);\n    stopped = true;\n    emit finished();\n}\n\nServerInfoDialog::ServerInfoDialog(const QString &addr, QWidget *parent): QDialog(parent)\n{\n    setupUi(this);\n    refreshThread = new QThread;\n\n    QStringList list = addr.split(':');\n    worker = new ThrWorker(this, list[0].toLatin1(), list[1].toUShort());\n    worker->moveToThread(refreshThread);\n    connect(refreshThread, SIGNAL(started()), worker, SLOT(process()));\n    connect(worker, SIGNAL(finished()), refreshThread, SLOT(quit()));\n    connect(refreshThread, SIGNAL(finished()), this, SLOT(refresh()));\n\n    connect(btnRefresh, &QPushButton::clicked, [this]{\n        if (!refreshThread->isRunning())\n            refreshThread->start();\n    });\n}\n\nServerInfoDialog::~ServerInfoDialog()\n{\n    worker->dialog = nullptr;\n    if (!refreshThread->isRunning())\n        refreshThread->terminate();\n}\n\nbool ServerInfoDialog::isUpdated()\n{\n    return sd.first != UNASSIGNED_SYSTEM_ADDRESS;\n}\n\nvoid ServerInfoDialog::setData(std::pair<RakNet::SystemAddress, QueryData> &newSD)\n{\n    sd = newSD;\n}\n\nvoid ServerInfoDialog::refresh()\n{\n    if (sd.first != UNASSIGNED_SYSTEM_ADDRESS)\n    {\n        leAddr->setText(sd.first.ToString(true, ':'));\n        lblName->setText(sd.second.GetName());\n        int ping = PingRakNetServer(sd.first.ToString(false), sd.first.GetPort());\n        lblPing->setNum(ping);\n        btnConnect->setDisabled(ping == PING_UNREACHABLE);\n\n        listPlayers->clear();\n        for (const auto &player : sd.second.players)\n            listPlayers->addItem(QString::fromStdString(player));\n\n        listPlugins->clear();\n        for (const auto &plugin : sd.second.plugins)\n            listPlugins->addItem(QString::fromStdString(plugin.name));\n\n        listRules->clear();\n        const static vector<std::string> defaultRules {\"gamemode\", \"maxPlayers\", \"name\", \"passw\", \"players\", \"version\"};\n        for (auto &rule : sd.second.rules)\n        {\n            if (::find(defaultRules.begin(), defaultRules.end(), rule.first) != defaultRules.end())\n                continue;\n            QString ruleStr = QString::fromStdString(rule.first) + \" : \";\n            if (rule.second.type == 's')\n                ruleStr += QString::fromStdString(rule.second.str);\n            else\n                ruleStr += QString::number(rule.second.val);\n            listRules->addItem(ruleStr);\n        }\n\n        lblPlayers->setText(QString::number(sd.second.players.size()) + \" / \" + QString::number(sd.second.GetMaxPlayers()));\n    }\n}\n\nint ServerInfoDialog::exec()\n{\n    if (!refreshThread->isRunning())\n        refreshThread->start();\n    return QDialog::exec();\n}\n"
  },
  {
    "path": "apps/browser/ServerInfoDialog.hpp",
    "content": "#ifndef NEWLAUNCHER_SERVERINFODIALOG_HPP\n#define NEWLAUNCHER_SERVERINFODIALOG_HPP\n\n#include \"ui_ServerInfo.h\"\n#include <apps/browser/netutils/Utils.hpp>\n#include <RakNetTypes.h>\n#include <components/openmw-mp/Master/MasterData.hpp>\n\nclass ThrWorker;\n\nclass ServerInfoDialog : public QDialog,  public Ui::Dialog\n{\n    Q_OBJECT\npublic:\n    explicit ServerInfoDialog(const QString &addr, QWidget *parent = nullptr);\n    ~ServerInfoDialog() override;\n    bool isUpdated();\n    void setData(std::pair<RakNet::SystemAddress, QueryData> &newSD);\npublic slots:\n    void refresh();\n    int exec() Q_DECL_OVERRIDE;\nprivate:\n    QThread *refreshThread;\n    ThrWorker* worker;\n    std::pair<RakNet::SystemAddress, QueryData> sd;\n};\n\nclass ThrWorker: public QObject\n{\n    friend class ServerInfoDialog;\nQ_OBJECT\npublic:\n    ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port);\n\npublic slots:\n    void process();\nsignals:\n    void finished();\nprivate:\n    QString addr;\n    unsigned short port;\n    bool stopped;\n    ServerInfoDialog *dialog;\n};\n\n\n#endif //NEWLAUNCHER_SERVERINFODIALOG_HPP\n"
  },
  {
    "path": "apps/browser/ServerModel.cpp",
    "content": "#include <qmessagebox.h>\n#include \"ServerModel.hpp\"\n#include <qdebug.h>\n#include <apps/browser/netutils/Utils.hpp>\n\nServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)\n{\n}\n/*QHash<int, QByteArray> ServerModel::roleNames() const\n{\n    return roles;\n}*/\n\nQVariant ServerModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid())\n        return QVariant();\n    if (index.row() < 0 || index.row() > myData.size())\n        return QVariant();\n\n    const ServerData &sd = myData.at(index.row());\n\n    if (role == Qt::DisplayRole)\n    {\n        QVariant var;\n        switch (index.column())\n        {\n            case ServerData::ADDR:\n                var = sd.addr;\n                break;\n            case ServerData::PASSW:\n                var = (int)(sd.rules.at(\"passw\").val) == 1 ? \"Yes\" : \"No\";\n                break;\n            case ServerData::VERSION:\n                var = QString(sd.rules.at(\"version\").str.c_str());\n                break;\n            case ServerData::PLAYERS:\n                var = (int) sd.rules.at(\"players\").val;\n                break;\n            case ServerData::MAX_PLAYERS:\n                var = (int) sd.rules.at(\"maxPlayers\").val;\n                break;\n            case ServerData::HOSTNAME:\n                var = QString(sd.rules.at(\"name\").str.c_str());\n                break;\n            case ServerData::PING:\n                var = sd.ping == PING_UNREACHABLE ? QVariant(\"Unreachable\") : sd.ping;\n                break;\n            case ServerData::MODNAME:\n                if (sd.rules.at(\"gamemode\").str == \"\")\n                    var = \"default\";\n                else\n                    var = QString(sd.rules.at(\"gamemode\").str.c_str());\n                break;\n        }\n        return var;\n    }\n    return QVariant();\n}\n\nQVariant ServerModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n    QVariant var;\n    if (orientation == Qt::Horizontal)\n    {\n        if (role == Qt::SizeHintRole)\n        {\n            /*if (section == ServerData::HOSTNAME)\n                var = QSize(200, 25);*/\n        }\n        else if (role == Qt::DisplayRole)\n        {\n\n            switch (section)\n            {\n                case ServerData::ADDR:\n                    var = \"Address\";\n                    break;\n                case ServerData::PASSW:\n                    var = \"Password\";\n                    break;\n                case ServerData::VERSION:\n                    var = \"Version\";\n                    break;\n                case ServerData::HOSTNAME:\n                    var = \"Host name\";\n                    break;\n                case ServerData::PLAYERS:\n                    var = \"Players\";\n                    break;\n                case ServerData::MAX_PLAYERS:\n                    var = \"Max players\";\n                    break;\n                case ServerData::PING:\n                    var = \"Ping\";\n                    break;\n                case ServerData::MODNAME:\n                    var = \"Game mode\";\n            }\n        }\n    }\n    return var;\n}\n\nint ServerModel::rowCount(const QModelIndex &parent) const\n{\n    return myData.size();\n}\n\nint ServerModel::columnCount(const QModelIndex &parent) const\n{\n    return ServerData::LAST;\n}\n\nbool ServerModel::setData(const QModelIndex &index, const QVariant &value, int role)\n{\n    if (index.isValid() && role == Qt::EditRole)\n    {\n        int row = index.row();\n        int col = index.column();\n\n        ServerData &sd = myData[row];\n        bool ok = true;\n        switch(col)\n        {\n            case ServerData::ADDR:\n                sd.addr = value.toString();\n                ok = !sd.addr.isEmpty();\n                break;\n            case ServerData::PASSW:\n                sd.SetPassword(value.toBool());\n                break;\n            case ServerData::VERSION:\n                sd.SetVersion(value.toString().toUtf8());\n                ok = !sd.addr.isEmpty();\n                break;\n            case ServerData::PLAYERS:\n                sd.SetPlayers(value.toInt(&ok));\n                break;\n            case ServerData::MAX_PLAYERS:\n                sd.SetMaxPlayers(value.toInt(&ok));\n                break;\n            case ServerData::HOSTNAME:\n                sd.SetName(value.toString().toUtf8());\n                ok = !sd.addr.isEmpty();\n                break;\n            case ServerData::PING:\n                sd.ping = value.toInt(&ok);\n                break;\n            case ServerData::MODNAME:\n                sd.SetGameMode(value.toString().toUtf8());\n                break;\n            default:\n                return false;\n        }\n        if (ok)\n            emit(dataChanged(index, index));\n        return true;\n    }\n    return false;\n}\n\nbool ServerModel::insertRows(int position, int count, const QModelIndex &index)\n{\n    Q_UNUSED(index);\n    beginInsertRows(QModelIndex(), position, position + count - 1);\n\n    myData.insert(position, count, {});\n\n    endInsertRows();\n    return true;\n}\n\nbool ServerModel::removeRows(int position, int count, const QModelIndex &parent)\n{\n    if (count == 0)\n        return false;\n\n    beginRemoveRows(parent, position, position + count - 1);\n    myData.erase(myData.begin()+position, myData.begin() + position + count);\n    endRemoveRows();\n\n    return true;\n}\n\nQModelIndex ServerModel::index(int row, int column, const QModelIndex &parent) const\n{\n\n    QModelIndex index = QAbstractTableModel::index(row, column, parent);\n    return index;\n}\n"
  },
  {
    "path": "apps/browser/ServerModel.hpp",
    "content": "#ifndef SERVERMODEL_FONTMODEL_HPP\n#define SERVERMODEL_FONTMODEL_HPP\n\n#include <QObject>\n#include <vector>\n#include <QString>\n#include <QAbstractTableModel>\n#include <components/openmw-mp/Master/MasterData.hpp>\n\nstruct ServerData : public QueryData\n{\n    QString addr;\n    int ping;\n    enum IDS\n    {\n        ADDR,\n        HOSTNAME,\n        PLAYERS,\n        MAX_PLAYERS,\n        PASSW,\n        MODNAME,\n        PING,\n        VERSION,\n        LAST\n    };\n};\n\nclass ServerModel: public QAbstractTableModel\n{\n    Q_OBJECT\npublic:\n    explicit ServerModel(QObject *parent = nullptr);\n    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;\n    int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;\n    int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;\n    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_FINAL;\n\n    bool insertRows(int row, int count, const QModelIndex &index = QModelIndex()) Q_DECL_FINAL;\n    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_FINAL;\n\n    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_FINAL;\n    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;\n\n\npublic:\n    //QHash<int, QByteArray> roles;\n    QVector<ServerData> myData;\n};\n\n\n#endif //SERVERMODEL_FONTMODEL_HPP\n"
  },
  {
    "path": "apps/browser/Types.hpp",
    "content": "#ifndef OPENMW_TYPES_HPP\n#define OPENMW_TYPES_HPP\n\n#include <QPair>\n#include <QString>\n\ntypedef QPair <QString, unsigned short> AddrPair;\ntypedef QPair <int, AddrPair> ServerRow;\n\n\n#endif //OPENMW_TYPES_HPP\n"
  },
  {
    "path": "apps/browser/main.cpp",
    "content": "#include <QApplication>\n#include <components/settings/settings.hpp>\n#include <components/files/configurationmanager.hpp>\n#include <apps/browser/netutils/QueryClient.hpp>\n#include \"MainWindow.hpp\"\n\nstd::string loadSettings (Settings::Manager & settings)\n{\n    Files::ConfigurationManager mCfgMgr;\n    // Create the settings manager and load default settings file\n    const std::string localdefault = (mCfgMgr.getLocalPath() / \"tes3mp-client-default.cfg\").string();\n    const std::string globaldefault = (mCfgMgr.getGlobalPath() / \"tes3mp-client-default.cfg\").string();\n\n    // prefer local\n    if (boost::filesystem::exists(localdefault))\n        settings.loadDefault(localdefault, false);\n    else if (boost::filesystem::exists(globaldefault))\n        settings.loadDefault(globaldefault, false);\n    else\n        throw std::runtime_error (\"No default settings file found! Make sure the file \\\"tes3mp-client-default.cfg\\\" was properly installed.\");\n\n    // load user settings if they exist\n    const std::string settingspath = (mCfgMgr.getUserConfigPath() / \"tes3mp-client.cfg\").string();\n    if (boost::filesystem::exists(settingspath))\n        settings.loadUser(settingspath);\n\n    return settingspath;\n}\n\nint main(int argc, char *argv[])\n{\n    Settings::Manager mgr;\n\n    loadSettings(mgr);\n\n    std::string addr = mgr.getString(\"address\", \"Master\");\n    int port = mgr.getInt(\"port\", \"Master\");\n\n    // Is this an attempt to connect to the official master server at the old port? If so,\n    // redirect it to the correct port for the currently used fork of RakNet\n    if (Misc::StringUtils::ciEqual(addr, \"master.tes3mp.com\") && port == 25560)\n        port = 25561;\n\n    // initialize resources, if needed\n    // Q_INIT_RESOURCE(resfile);\n\n    QueryClient::Get().SetServer(addr, port);\n    QApplication app(argc, argv);\n    MainWindow d;\n\n    d.show();\n    return app.exec();\n}\n"
  },
  {
    "path": "apps/browser/netutils/HTTPNetwork.cpp",
    "content": "#include <RakPeer.h>\n#include <HTTPConnection2.h>\n#include <TCPInterface.h>\n#include <RakSleep.h>\n\n#include <sstream>\n\n#include \"HTTPNetwork.hpp\"\n\nusing namespace RakNet;\n\nHTTPNetwork::HTTPNetwork(std::string addr, unsigned short port) : address(addr), port(port)\n{\n    httpConnection = HTTPConnection2::GetInstance();\n    tcpInterface = new TCPInterface;\n    tcpInterface->Start(0, 64);\n    tcpInterface->AttachPlugin(httpConnection);\n}\n\nHTTPNetwork::~HTTPNetwork()\n{\n    delete tcpInterface;\n}\n\nstd::string HTTPNetwork::answer()\n{\n    RakNet::SystemAddress sa;\n    RakNet::Packet *packet;\n    RakNet::SystemAddress hostReceived;\n    RakNet::RakString response;\n    RakNet::RakString transmitted, hostTransmitted;\n    int contentOffset = 0;\n\n    while (true)\n    {\n        // This is kind of crappy, but for TCP plugins, always do HasCompletedConnectionAttempt,\n        // then Receive(), then HasFailedConnectionAttempt(),HasLostConnection()\n        sa = tcpInterface->HasCompletedConnectionAttempt();\n        if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)\n            printf(\"Connected to master server: %s\\n\", sa.ToString());\n\n        sa = tcpInterface->HasFailedConnectionAttempt();\n        if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)\n        {\n            printf(\"Failed to connect to master server: %s\\n\", sa.ToString());\n            return \"FAIL_CONNECT\";\n        }\n        sa = tcpInterface->HasLostConnection();\n        if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)\n        {\n            printf(\"Lost connection to master server: %s\\n\", sa.ToString());\n            return \"LOST_CONNECTION\";\n        }\n\n        for (packet = tcpInterface->Receive(); packet; tcpInterface->DeallocatePacket(\n                packet), packet = tcpInterface->Receive());\n\n        if (httpConnection->GetResponse(transmitted, hostTransmitted, response, hostReceived, contentOffset))\n        {\n            if (contentOffset < 0)\n                return \"NO_CONTENT\"; // no content\n            tcpInterface->CloseConnection(sa);\n\n            return (response.C_String() + contentOffset);\n        }\n        RakSleep(30);\n    }\n}\n\nstd::string HTTPNetwork::getData(const char *uri)\n{\n    RakNet::RakString createRequest = RakNet::RakString::FormatForGET(uri);\n\n    if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))\n        return \"UNKNOWN_ADDRESS\";\n    return answer();\n}\n\nstd::string HTTPNetwork::getDataPOST(const char *uri, const char* body, const char* contentType)\n{\n    RakNet::RakString createRequest = RakNet::RakString::FormatForPOST(uri, contentType, body);\n\n    if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))\n        return \"UNKNOWN_ADDRESS\";\n    return answer();\n}\n\nstd::string HTTPNetwork::getDataPUT(const char *uri, const char* body, const char* contentType)\n{\n    RakNet::RakString createRequest = RakNet::RakString::FormatForPUT(uri, contentType, body);\n\n    if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))\n        return \"UNKNOWN_ADDRESS\";\n    return answer();\n}\n"
  },
  {
    "path": "apps/browser/netutils/HTTPNetwork.hpp",
    "content": "#ifndef NEWLAUNCHER_HTTPNETWORK_HPP\n#define NEWLAUNCHER_HTTPNETWORK_HPP\n\n\n#include <string>\n\nnamespace RakNet\n{\n    class TCPInterface;\n    class HTTPConnection2;\n}\n\nclass HTTPNetwork\n{\npublic:\n    HTTPNetwork(std::string addr, unsigned short port);\n    ~HTTPNetwork();\n    std::string getData(const char *uri);\n    std::string getDataPOST(const char *uri, const char* body,  const char* contentType = \"application/json\");\n    std::string getDataPUT(const char *uri, const char* body, const char* contentType = \"application/json\");\n\nprotected:\n    RakNet::TCPInterface *tcpInterface;\n    RakNet::HTTPConnection2 *httpConnection;\n    std::string address;\n    unsigned short port;\n    std::string answer();\n};\n\n\n#endif //NEWLAUNCHER_HTTPNETWORK_HPP\n"
  },
  {
    "path": "apps/browser/netutils/QueryClient.cpp",
    "content": "#include \"QueryClient.hpp\"\n#include <RakSleep.h>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <iostream>\n#include <components/openmw-mp/Version.hpp>\n#include <qdebug.h>\n\nusing namespace RakNet;\nusing namespace std;\nusing namespace mwmp;\n\nQueryClient::QueryClient()\n{\n    peer = RakPeerInterface::GetInstance();\n    pmq = new PacketMasterQuery(peer);\n    pmu = new PacketMasterUpdate(peer);\n    RakNet::SocketDescriptor sd;\n    peer->Startup(8, &sd, 1);\n    status = -1;\n}\n\nQueryClient::~QueryClient()\n{\n    delete pmq;\n    delete pmu;\n    RakPeerInterface::DestroyInstance(peer);\n}\n\nvoid QueryClient::SetServer(const string &addr, unsigned short port)\n{\n    masterAddr = SystemAddress(addr.c_str(), port);\n}\n\nQueryClient &QueryClient::Get()\n{\n    static QueryClient myInstance;\n    return myInstance;\n}\n\nmap<SystemAddress, QueryData> QueryClient::Query()\n{\n    map<SystemAddress, QueryData> query;\n    BitStream bs;\n    bs.Write((unsigned char) (ID_MASTER_QUERY));\n    qDebug() << \"Locking mutex in QueryClient::Query()\";\n    mxServers.lock();\n    status = -1;\n    int attempts = 3;\n    do\n    {\n        if (Connect() == IS_NOT_CONNECTED)\n        {\n            qDebug() << \"Unlocking mutex in QueryClient::Query()\";\n            mxServers.unlock();\n            return query;\n        }\n\n        int code = peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);\n\n        if (code == 0)\n        {\n            qDebug() << \"Unlocking mutex in QueryClient::Query()\";\n            mxServers.unlock();\n            return query;\n        }\n\n        pmq->SetServers(&query);\n        status = GetAnswer(ID_MASTER_QUERY);\n        RakSleep(100);\n    }\n    while(status != ID_MASTER_QUERY && attempts-- > 0);\n    if(status != ID_MASTER_QUERY)\n        qDebug() << \"Getting query was failed\";\n    qDebug() << \"Unlocking mutex in QueryClient::Query()\";\n    peer->CloseConnection(masterAddr, true);\n    mxServers.unlock();\n    qDebug() <<\"Answer\" << (status == ID_MASTER_QUERY ? \"ok.\" : \"wrong.\");\n\n    return query;\n}\n\npair<SystemAddress, QueryData> QueryClient::Update(const RakNet::SystemAddress &addr)\n{\n    qDebug() << \"Locking mutex in QueryClient::Update(RakNet::SystemAddress addr)\";\n    pair<SystemAddress, QueryData> server;\n    BitStream bs;\n    bs.Write((unsigned char) (ID_MASTER_UPDATE));\n    bs.Write(addr);\n\n    mxServers.lock();\n    status = -1;\n    int attempts = 3;\n    pmu->SetServer(&server);\n    do\n    {\n        if (Connect() == IS_NOT_CONNECTED)\n        {\n            qDebug() << IS_NOT_CONNECTED;\n            qDebug() << \"Unlocking mutex in QueryClient::Update(RakNet::SystemAddress addr)\";\n            mxServers.unlock();\n            return server;\n        }\n\n        peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);\n        status = GetAnswer(ID_MASTER_UPDATE);\n        RakSleep(100);\n    }\n    while(status != ID_MASTER_UPDATE && attempts-- > 0);\n    if(status != ID_MASTER_UPDATE)\n        qDebug() << \"Getting update was failed\";\n    peer->CloseConnection(masterAddr, true);\n    qDebug() << \"Unlocking mutex in QueryClient::Update(RakNet::SystemAddress addr)\";\n    mxServers.unlock();\n    return server;\n}\n\nMASTER_PACKETS QueryClient::GetAnswer(MASTER_PACKETS waitingPacket)\n{\n    RakNet::Packet *packet;\n    bool update = true;\n    unsigned char pid = 0;\n    int id = -1;\n    while (update)\n    {\n        for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())\n        {\n            BitStream data(packet->data, packet->length, false);\n            pmq->SetReadStream(&data);\n            pmu->SetReadStream(&data);\n            data.Read(pid);\n            switch(pid)\n            {\n                case ID_CONNECTION_LOST:\n                    qDebug() << \"ID_CONNECTION_LOST\";\n                case ID_DISCONNECTION_NOTIFICATION:\n                    qDebug() << \"Disconnected\";\n                    update = false;\n                    break;\n                case ID_MASTER_QUERY:\n                    qDebug() << \"ID_MASTER_QUERY\";\n                    if (waitingPacket == ID_MASTER_QUERY)\n                        pmq->Read();\n                    else\n                        qDebug() << \"Got wrong packet\";\n                    update = false;\n                    id = pid;\n                    break;\n                case ID_MASTER_UPDATE:\n                    qDebug() << \"ID_MASTER_UPDATE\";\n                    if (waitingPacket == ID_MASTER_UPDATE)\n                        pmu->Read();\n                    else\n                        qDebug() << \"Got wrong packet\";\n                    update = false;\n                    id = pid;\n                    break;\n                case ID_MASTER_ANNOUNCE:\n                    qDebug() << \"ID_MASTER_ANNOUNCE\";\n                    update = false;\n                    id = pid;\n                    break;\n                case ID_CONNECTION_REQUEST_ACCEPTED:\n                    qDebug() << \"ID_CONNECTION_REQUEST_ACCEPTED\";\n                    break;\n                default:\n                    break;\n            }\n        }\n        RakSleep(500);\n    }\n    return (MASTER_PACKETS)(id);\n}\n\nConnectionState QueryClient::Connect()\n{\n\n    ConnectionAttemptResult car = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), TES3MP_MASTERSERVER_PASSW,\n                                                strlen(TES3MP_MASTERSERVER_PASSW), nullptr, 0, 5, 500);\n\n    while (true)\n    {\n        ConnectionState state = peer->GetConnectionState(masterAddr);\n        switch (state)\n        {\n            case IS_CONNECTED:\n                qDebug() << \"Connected\";\n                return IS_CONNECTED;\n            case IS_NOT_CONNECTED:\n            case IS_DISCONNECTED:\n            case IS_SILENTLY_DISCONNECTING:\n            case IS_DISCONNECTING:\n            {\n                qDebug() << \"Cannot connect to the master server. Code:\"<< state;\n                return IS_NOT_CONNECTED;\n            }\n            case IS_PENDING:\n            case IS_CONNECTING:\n                qDebug() << \"Pending\";\n                break;\n        }\n        RakSleep(500);\n    }\n}\n\nint QueryClient::Status()\n{\n    return status;\n}\n"
  },
  {
    "path": "apps/browser/netutils/QueryClient.hpp",
    "content": "#ifndef OPENMW_QUERYCLIENT_HPP\n#define OPENMW_QUERYCLIENT_HPP\n\n#include <string>\n#include <RakPeerInterface.h>\n#include <components/openmw-mp/Master/PacketMasterQuery.hpp>\n#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>\n#include <apps/browser/ServerModel.hpp>\n#include <mutex>\n\nclass QueryClient\n{\npublic:\n    QueryClient(QueryClient const &) = delete;\n    QueryClient(QueryClient &&) = delete;\n    QueryClient &operator=(QueryClient const &) = delete;\n    QueryClient &operator=(QueryClient &&) = delete;\n\n    static QueryClient &Get();\n    void SetServer(const std::string &addr, unsigned short port);\n    std::map<RakNet::SystemAddress, QueryData> Query();\n    std::pair<RakNet::SystemAddress, QueryData> Update(const RakNet::SystemAddress &addr);\n    int Status();\nprivate:\n    RakNet::ConnectionState Connect();\n    MASTER_PACKETS GetAnswer(MASTER_PACKETS packet);\nprotected:\n    QueryClient();\n    ~QueryClient();\nprivate:\n    int status;\n    RakNet::RakPeerInterface *peer;\n    RakNet::SystemAddress masterAddr;\n    mwmp::PacketMasterQuery *pmq;\n    mwmp::PacketMasterUpdate *pmu;\n    std::pair<RakNet::SystemAddress, ServerData> server;\n    std::mutex mxServers;\n\n};\n\n\n#endif //OPENMW_QUERYCLIENT_HPP\n"
  },
  {
    "path": "apps/browser/netutils/Utils.cpp",
    "content": "#include <RakPeer.h>\n#include <MessageIdentifiers.h>\n#include <RakSleep.h>\n#include <GetTime.h>\n\n#include <sstream>\n#include <components/openmw-mp/Version.hpp>\n\n#include \"Utils.hpp\"\n\nusing namespace std;\n\nunsigned int PingRakNetServer(const char *addr, unsigned short port)\n{\n    RakNet::Packet *packet;\n    bool done = false;\n    RakNet::TimeMS time = PING_UNREACHABLE;\n\n    RakNet::SocketDescriptor socketDescriptor{0, \"\"};\n    RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();\n    peer->Startup(1, &socketDescriptor, 1);\n    if (!peer->Ping(addr, port, false))\n        return time;\n    RakNet::TimeMS start = RakNet::GetTimeMS();\n    while (!done)\n    {\n        RakNet::TimeMS now = RakNet::GetTimeMS();\n        if (now - start >= PING_UNREACHABLE)\n            break;\n\n        packet = peer->Receive();\n        if (!packet)\n            continue;\n\n        switch (packet->data[0])\n        {\n            case ID_DISCONNECTION_NOTIFICATION:\n            case ID_CONNECTION_LOST:\n                done = true;\n                break;\n            case ID_CONNECTED_PING:\n            case ID_UNCONNECTED_PONG:\n            {\n                RakNet::BitStream bsIn(&packet->data[1], packet->length, false);\n                bsIn.Read(time);\n                time = now - time;\n                done = true;\n                break;\n            }\n            default:\n                break;\n        }\n        peer->DeallocatePacket(packet);\n    }\n\n    peer->Shutdown(0);\n    RakNet::RakPeerInterface::DestroyInstance(peer);\n    return time > PING_UNREACHABLE ? PING_UNREACHABLE : time;\n}\n\nServerExtendedData getExtendedData(const char *addr, unsigned short port)\n{\n    ServerExtendedData data;\n    RakNet::SocketDescriptor socketDescriptor = {0, \"\"};\n    RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();\n    peer->Startup(1, &socketDescriptor, 1);\n\n    stringstream sstr;\n    sstr << TES3MP_VERSION;\n    sstr << TES3MP_PROTO_VERSION;\n\n    std::string msg;\n\n    if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), nullptr, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)\n        msg = \"Connection attempt failed.\\n\";\n\n\n    int queue = 0;\n    while (queue == 0)\n    {\n        for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(\n                packet), packet = peer->Receive())\n        {\n            switch (packet->data[0])\n            {\n                case ID_CONNECTION_ATTEMPT_FAILED:\n                {\n                    msg = \"Connection failed.\\n\"\n                            \"Either the IP address is wrong or a firewall on either system is blocking\\n\"\n                            \"UDP packets on the port you have chosen.\";\n                    queue = -1;\n                    break;\n                }\n                case ID_INVALID_PASSWORD:\n                {\n                    msg = \"Connection failed.\\n\"\n                            \"The client or server is outdated.\\n\";\n                    queue = -1;\n                    break;\n                }\n                case ID_CONNECTION_REQUEST_ACCEPTED:\n                {\n                    msg = \"Connection accepted.\\n\";\n                    queue = 1;\n                    break;\n                }\n                case ID_DISCONNECTION_NOTIFICATION:\n                    throw runtime_error(\"ID_DISCONNECTION_NOTIFICATION.\\n\");\n                case ID_CONNECTION_BANNED:\n                    throw runtime_error(\"ID_CONNECTION_BANNED.\\n\");\n                case ID_CONNECTION_LOST:\n                    throw runtime_error(\"ID_CONNECTION_LOST.\\n\");\n                default:\n                    printf(\"Connection message with identifier %i has arrived in initialization.\\n\", packet->data[0]);\n            }\n        }\n    }\n    puts(msg.c_str());\n\n    if (queue == -1) // connection is failed\n        return data;\n\n    {\n        RakNet::BitStream bs;\n        bs.Write((unsigned char) (ID_USER_PACKET_ENUM + 1));\n        peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);\n    }\n\n    RakNet::Packet *packet;\n    bool done = false;\n    while (!done)\n    {\n        for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())\n        {\n            if (packet->data[0] == (ID_USER_PACKET_ENUM + 1))\n            {\n                RakNet::BitStream bs(packet->data, packet->length, false);\n                bs.IgnoreBytes(1);\n                size_t length = 0;\n                bs.Read(length);\n                for (size_t i = 0; i < length; i++)\n                {\n                    RakNet::RakString str;\n                    bs.Read(str);\n                    data.players.emplace_back(str.C_String());\n                }\n                bs.Read(length);\n                for (size_t i = 0; i < length; i++)\n                {\n                    RakNet::RakString str;\n                    bs.Read(str);\n                    data.plugins.emplace_back(str.C_String());\n                }\n                done = true;\n            }\n        }\n    }\n\n    peer->Shutdown(0);\n    RakSleep(10);\n    RakNet::RakPeerInterface::DestroyInstance(peer);\n    return data;\n}\n"
  },
  {
    "path": "apps/browser/netutils/Utils.hpp",
    "content": "#ifndef NEWLAUNCHER_PING_HPP\n#define NEWLAUNCHER_PING_HPP\n\n#include <vector>\n#include <string>\n\n\n#define PING_UNREACHABLE 999\n\nunsigned int PingRakNetServer(const char *addr, unsigned short port);\n\nstruct ServerExtendedData\n{\n    std::vector<std::string> players;\n    std::vector<std::string> plugins;\n};\n\nServerExtendedData getExtendedData(const char *addr, unsigned short port);\n\n#endif //NEWLAUNCHER_PING_HPP\n"
  },
  {
    "path": "apps/bsatool/CMakeLists.txt",
    "content": "set(BSATOOL\n\tbsatool.cpp\n)\nsource_group(apps\\\\bsatool FILES ${BSATOOL})\n\n# Main executable\nopenmw_add_executable(bsatool\n\t${BSATOOL}\n)\n\ntarget_link_libraries(bsatool\n  ${Boost_PROGRAM_OPTIONS_LIBRARY}\n  ${Boost_FILESYSTEM_LIBRARY}\n  components\n)\n\nif (BUILD_WITH_CODE_COVERAGE)\n  add_definitions (--coverage)\n  target_link_libraries(bsatool gcov)\nendif()\n"
  },
  {
    "path": "apps/bsatool/bsatool.cpp",
    "content": "#include <iostream>\n#include <iomanip>\n#include <vector>\n\n#include <boost/program_options.hpp>\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <components/bsa/compressedbsafile.hpp>\n#include <components/misc/stringops.hpp>\n\n#define BSATOOL_VERSION 1.1\n\n// Create local aliases for brevity\nnamespace bpo = boost::program_options;\nnamespace bfs = boost::filesystem;\n\nstruct Arguments\n{\n    std::string mode;\n    std::string filename;\n    std::string extractfile;\n    std::string addfile;\n    std::string outdir;\n\n    bool longformat;\n    bool fullpath;\n};\n\nbool parseOptions (int argc, char** argv, Arguments &info)\n{\n    bpo::options_description desc(\"Inspect and extract files from Bethesda BSA archives\\n\\n\"\n            \"Usages:\\n\"\n            \"  bsatool list [-l] archivefile\\n\"\n            \"      List the files presents in the input archive.\\n\\n\"\n            \"  bsatool extract [-f] archivefile [file_to_extract] [output_directory]\\n\"\n            \"      Extract a file from the input archive.\\n\\n\"\n            \"  bsatool extractall archivefile [output_directory]\\n\"\n            \"      Extract all files from the input archive.\\n\\n\"\n            \"  bsatool add [-a] archivefile file_to_add\\n\"\n            \"      Add a file to the input archive.\\n\\n\"\n            \"  bsatool create [-c] archivefile\\n\"\n            \"      Create an archive.\\n\\n\"\n            \"Allowed options\");\n\n    desc.add_options()\n        (\"help,h\", \"print help message.\")\n        (\"version,v\", \"print version information and quit.\")\n        (\"long,l\", \"Include extra information in archive listing.\")\n        (\"full-path,f\", \"Create directory hierarchy on file extraction \"\n         \"(always true for extractall).\")\n        ;\n\n    // input-file is hidden and used as a positional argument\n    bpo::options_description hidden(\"Hidden Options\");\n\n    hidden.add_options()\n        ( \"mode,m\", bpo::value<std::string>(), \"bsatool mode\")\n        ( \"input-file,i\", bpo::value< std::vector<std::string> >(), \"input file\")\n        ;\n\n    bpo::positional_options_description p;\n    p.add(\"mode\", 1).add(\"input-file\", 3);\n\n    // there might be a better way to do this\n    bpo::options_description all;\n    all.add(desc).add(hidden);\n\n    bpo::variables_map variables;\n    try\n    {\n        bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)\n            .options(all).positional(p).run();\n        bpo::store(valid_opts, variables);\n    }\n    catch(std::exception &e)\n    {\n        std::cout << \"ERROR parsing arguments: \" << e.what() << \"\\n\\n\"\n            << desc << std::endl;\n        return false;\n    }\n\n    bpo::notify(variables);\n\n    if (variables.count (\"help\"))\n    {\n        std::cout << desc << std::endl;\n        return false;\n    }\n    if (variables.count (\"version\"))\n    {\n        std::cout << \"BSATool version \" << BSATOOL_VERSION << std::endl;\n        return false;\n    }\n    if (!variables.count(\"mode\"))\n    {\n        std::cout << \"ERROR: no mode specified!\\n\\n\"\n            << desc << std::endl;\n        return false;\n    }\n\n    info.mode = variables[\"mode\"].as<std::string>();\n    if (!(info.mode == \"list\" || info.mode == \"extract\" || info.mode == \"extractall\" || info.mode == \"add\" || info.mode == \"create\"))\n    {\n        std::cout << std::endl << \"ERROR: invalid mode \\\"\" << info.mode << \"\\\"\\n\\n\"\n            << desc << std::endl;\n        return false;\n    }\n\n    if (!variables.count(\"input-file\"))\n    {\n        std::cout << \"\\nERROR: missing BSA archive\\n\\n\"\n            << desc << std::endl;\n        return false;\n    }\n    info.filename = variables[\"input-file\"].as< std::vector<std::string> >()[0];\n\n    // Default output to the working directory\n    info.outdir = \".\";\n\n    if (info.mode == \"extract\")\n    {\n        if (variables[\"input-file\"].as< std::vector<std::string> >().size() < 2)\n        {\n            std::cout << \"\\nERROR: file to extract unspecified\\n\\n\"\n                << desc << std::endl;\n            return false;\n        }\n        if (variables[\"input-file\"].as< std::vector<std::string> >().size() > 1)\n            info.extractfile = variables[\"input-file\"].as< std::vector<std::string> >()[1];\n        if (variables[\"input-file\"].as< std::vector<std::string> >().size() > 2)\n            info.outdir = variables[\"input-file\"].as< std::vector<std::string> >()[2];\n    }\n    else if (info.mode == \"add\")\n    {\n        if (variables[\"input-file\"].as< std::vector<std::string> >().size() < 1)\n        {\n            std::cout << \"\\nERROR: file to add unspecified\\n\\n\"\n                << desc << std::endl;\n            return false;\n        }\n        if (variables[\"input-file\"].as< std::vector<std::string> >().size() > 1)\n            info.addfile = variables[\"input-file\"].as< std::vector<std::string> >()[1];\n    }\n    else if (variables[\"input-file\"].as< std::vector<std::string> >().size() > 1)\n        info.outdir = variables[\"input-file\"].as< std::vector<std::string> >()[1];\n\n    info.longformat = variables.count(\"long\") != 0;\n    info.fullpath = variables.count(\"full-path\") != 0;\n\n    return true;\n}\n\nint list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);\nint extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);\nint extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);\nint add(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);\n\nint main(int argc, char** argv)\n{\n    try\n    {\n        Arguments info;\n        if(!parseOptions (argc, argv, info))\n            return 1;\n\n        // Open file\n        std::unique_ptr<Bsa::BSAFile> bsa;\n\n        Bsa::BsaVersion bsaVersion = Bsa::CompressedBSAFile::detectVersion(info.filename);\n\n        if (bsaVersion == Bsa::BSAVER_COMPRESSED)\n            bsa = std::make_unique<Bsa::CompressedBSAFile>(Bsa::CompressedBSAFile());\n        else\n            bsa = std::make_unique<Bsa::BSAFile>(Bsa::BSAFile());\n\n        if (info.mode == \"create\")\n        {\n            bsa->open(info.filename);\n            return 0;\n        }\n\n        bsa->open(info.filename);\n\n        if (info.mode == \"list\")\n            return list(bsa, info);\n        else if (info.mode == \"extract\")\n            return extract(bsa, info);\n        else if (info.mode == \"extractall\")\n            return extractAll(bsa, info);\n        else if (info.mode == \"add\")\n            return add(bsa, info);\n        else\n        {\n            std::cout << \"Unsupported mode. That is not supposed to happen.\" << std::endl;\n            return 1;\n        }\n    }\n    catch (std::exception& e)\n    {\n        std::cerr << \"ERROR reading BSA archive\\nDetails:\\n\" << e.what() << std::endl;\n        return 2;\n    }\n}\n\nint list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)\n{\n    // List all files\n    const Bsa::BSAFile::FileList &files = bsa->getList();\n    for (const auto& file : files)\n    {\n        if(info.longformat)\n        {\n            // Long format\n            std::ios::fmtflags f(std::cout.flags());\n            std::cout << std::setw(50) << std::left << file.name();\n            std::cout << std::setw(8) << std::left << std::dec << file.fileSize;\n            std::cout << \"@ 0x\" << std::hex << file.offset << std::endl;\n            std::cout.flags(f);\n        }\n        else\n            std::cout << file.name() << std::endl;\n    }\n\n    return 0;\n}\n\nint extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)\n{\n    std::string archivePath = info.extractfile;\n    Misc::StringUtils::replaceAll(archivePath, \"/\", \"\\\\\");\n\n    std::string extractPath = info.extractfile;\n    Misc::StringUtils::replaceAll(extractPath, \"\\\\\", \"/\");\n\n    if (!bsa->exists(archivePath.c_str()))\n    {\n        std::cout << \"ERROR: file '\" << archivePath << \"' not found\\n\";\n        std::cout << \"In archive: \" << info.filename << std::endl;\n        return 3;\n    }\n\n    // Get the target path (the path the file will be extracted to)\n    bfs::path relPath (extractPath);\n    bfs::path outdir (info.outdir);\n\n    bfs::path target;\n    if (info.fullpath)\n        target = outdir / relPath;\n    else\n        target = outdir / relPath.filename();\n\n    // Create the directory hierarchy\n    bfs::create_directories(target.parent_path());\n\n    bfs::file_status s = bfs::status(target.parent_path());\n    if (!bfs::is_directory(s))\n    {\n        std::cout << \"ERROR: \" << target.parent_path() << \" is not a directory.\" << std::endl;\n        return 3;\n    }\n\n    // Get a stream for the file to extract\n    Files::IStreamPtr stream = bsa->getFile(archivePath.c_str());\n\n    bfs::ofstream out(target, std::ios::binary);\n\n    // Write the file to disk\n    std::cout << \"Extracting \" << info.extractfile << \" to \" << target << std::endl;\n\n    out << stream->rdbuf();\n    out.close();\n\n    return 0;\n}\n\nint extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)\n{\n    for (const auto &file : bsa->getList())\n    {\n        std::string extractPath(file.name());\n        Misc::StringUtils::replaceAll(extractPath, \"\\\\\", \"/\");\n\n        // Get the target path (the path the file will be extracted to)\n        bfs::path target (info.outdir);\n        target /= extractPath;\n\n        // Create the directory hierarchy\n        bfs::create_directories(target.parent_path());\n\n        bfs::file_status s = bfs::status(target.parent_path());\n        if (!bfs::is_directory(s))\n        {\n            std::cout << \"ERROR: \" << target.parent_path() << \" is not a directory.\" << std::endl;\n            return 3;\n        }\n\n        // Get a stream for the file to extract\n        // (inefficient because getFile iter on the list again)\n        Files::IStreamPtr data = bsa->getFile(file.name());\n        bfs::ofstream out(target, std::ios::binary);\n\n        // Write the file to disk\n        std::cout << \"Extracting \" << target << std::endl;\n        out << data->rdbuf();\n        out.close();\n    }\n\n    return 0;\n}\n\nint add(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)\n{\n    boost::filesystem::fstream stream(info.addfile, std::ios_base::binary | std::ios_base::out | std::ios_base::in);\n    bsa->addFile(info.addfile, stream);\n\n    return 0;\n}\n"
  },
  {
    "path": "apps/doc.hpp",
    "content": "// Note: This is not a regular source file.\n\n/// \\defgroup apps Applications\n"
  },
  {
    "path": "apps/essimporter/CMakeLists.txt",
    "content": "set(ESSIMPORTER_FILES\n    main.cpp\n    importer.cpp\n    importplayer.cpp\n    importnpcc.cpp\n    importcrec.cpp\n    importcellref.cpp\n    importacdt.cpp\n    importinventory.cpp\n    importklst.cpp\n    importcntc.cpp\n    importgame.cpp\n    importinfo.cpp\n    importdial.cpp\n    importques.cpp\n    importjour.cpp\n    importscri.cpp\n    importscpt.cpp\n    importproj.cpp\n    importsplm.cpp\n    importercontext.cpp\n    converter.cpp\n    convertacdt.cpp\n    convertnpcc.cpp\n    convertinventory.cpp\n    convertcrec.cpp\n    convertcntc.cpp\n    convertscri.cpp\n    convertscpt.cpp\n    convertplayer.cpp\n)\n\nopenmw_add_executable(openmw-essimporter\n    ${ESSIMPORTER_FILES}\n)\n\ntarget_link_libraries(openmw-essimporter\n    ${Boost_PROGRAM_OPTIONS_LIBRARY}\n    ${Boost_FILESYSTEM_LIBRARY}\n    components\n)\n\nif (BUILD_WITH_CODE_COVERAGE)\n  add_definitions (--coverage)\n  target_link_libraries(openmw-essimporter gcov)\nendif()\n\nif (WIN32)\n  INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION \".\")\nendif(WIN32)\n"
  },
  {
    "path": "apps/essimporter/convertacdt.cpp",
    "content": "#include <string>\n#include <iostream>\n#include <limits>\n\n#include <components/misc/stringops.hpp>\n\n#include \"convertacdt.hpp\"\n\nnamespace ESSImport\n{\n\n    int translateDynamicIndex(int mwIndex)\n    {\n        if (mwIndex == 1)\n            return 2;\n        else if (mwIndex == 2)\n            return 1;\n        return mwIndex;\n    }\n\n    void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats)\n    {\n        for (int i=0; i<3; ++i)\n        {\n            int writeIndex = translateDynamicIndex(i);\n            cStats.mDynamic[writeIndex].mBase = acdt.mDynamic[i][1];\n            cStats.mDynamic[writeIndex].mMod = acdt.mDynamic[i][1];\n            cStats.mDynamic[writeIndex].mCurrent = acdt.mDynamic[i][0];\n        }\n        for (int i=0; i<8; ++i)\n        {\n            cStats.mAttributes[i].mBase = static_cast<int>(acdt.mAttributes[i][1]);\n            cStats.mAttributes[i].mMod = static_cast<int>(acdt.mAttributes[i][0]);\n            cStats.mAttributes[i].mCurrent = static_cast<int>(acdt.mAttributes[i][0]);\n        }\n        cStats.mGoldPool = acdt.mGoldPool;\n        cStats.mTalkedTo = (acdt.mFlags & TalkedToPlayer) != 0;\n        cStats.mAttacked = (acdt.mFlags & Attacked) != 0;\n    }\n\n    void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats)\n    {\n        cStats.mDead = (acsc.mFlags & Dead) != 0;\n    }\n\n    void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats)\n    {\n        for (int i=0; i<ESM::Skill::Length; ++i)\n        {\n            npcStats.mSkills[i].mMod = actorData.mSkills[i][1];\n            npcStats.mSkills[i].mCurrent = actorData.mSkills[i][1];\n            npcStats.mSkills[i].mBase = actorData.mSkills[i][0];\n        }\n\n        npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;\n    }\n\n    void convertANIS (const ANIS& anis, ESM::AnimationState& state)\n    {\n        static const char* animGroups[] =\n        {\n            \"Idle\", \"Idle2\", \"Idle3\", \"Idle4\", \"Idle5\", \"Idle6\", \"Idle7\", \"Idle8\", \"Idle9\", \"Idlehh\", \"Idle1h\", \"Idle2c\",\n            \"Idle2w\", \"IdleSwim\", \"IdleSpell\", \"IdleCrossbow\", \"IdleSneak\", \"IdleStorm\", \"Torch\", \"Hit1\", \"Hit2\", \"Hit3\",\n            \"Hit4\", \"Hit5\", \"SwimHit1\", \"SwimHit2\", \"SwimHit3\", \"Death1\", \"Death2\", \"Death3\", \"Death4\", \"Death5\",\n            \"DeathKnockDown\", \"DeathKnockOut\", \"KnockDown\", \"KnockOut\", \"SwimDeath\", \"SwimDeath2\", \"SwimDeath3\",\n            \"SwimDeathKnockDown\", \"SwimDeathKnockOut\", \"SwimKnockOut\", \"SwimKnockDown\", \"SwimWalkForward\",\n            \"SwimWalkBack\", \"SwimWalkLeft\", \"SwimWalkRight\", \"SwimRunForward\", \"SwimRunBack\", \"SwimRunLeft\",\n            \"SwimRunRight\", \"SwimTurnLeft\", \"SwimTurnRight\", \"WalkForward\", \"WalkBack\", \"WalkLeft\", \"WalkRight\",\n            \"TurnLeft\", \"TurnRight\", \"RunForward\", \"RunBack\", \"RunLeft\", \"RunRight\", \"SneakForward\", \"SneakBack\",\n            \"SneakLeft\", \"SneakRight\", \"Jump\", \"WalkForwardhh\", \"WalkBackhh\", \"WalkLefthh\", \"WalkRighthh\",\n            \"TurnLefthh\", \"TurnRighthh\", \"RunForwardhh\", \"RunBackhh\", \"RunLefthh\", \"RunRighthh\", \"SneakForwardhh\",\n            \"SneakBackhh\", \"SneakLefthh\", \"SneakRighthh\", \"Jumphh\", \"WalkForward1h\", \"WalkBack1h\", \"WalkLeft1h\",\n            \"WalkRight1h\", \"TurnLeft1h\", \"TurnRight1h\", \"RunForward1h\", \"RunBack1h\", \"RunLeft1h\", \"RunRight1h\",\n            \"SneakForward1h\", \"SneakBack1h\", \"SneakLeft1h\", \"SneakRight1h\", \"Jump1h\", \"WalkForward2c\", \"WalkBack2c\",\n            \"WalkLeft2c\", \"WalkRight2c\", \"TurnLeft2c\", \"TurnRight2c\", \"RunForward2c\", \"RunBack2c\", \"RunLeft2c\",\n            \"RunRight2c\", \"SneakForward2c\", \"SneakBack2c\", \"SneakLeft2c\", \"SneakRight2c\", \"Jump2c\", \"WalkForward2w\",\n            \"WalkBack2w\", \"WalkLeft2w\", \"WalkRight2w\", \"TurnLeft2w\", \"TurnRight2w\", \"RunForward2w\", \"RunBack2w\",\n            \"RunLeft2w\", \"RunRight2w\", \"SneakForward2w\", \"SneakBack2w\", \"SneakLeft2w\", \"SneakRight2w\", \"Jump2w\",\n            \"SpellCast\", \"SpellTurnLeft\", \"SpellTurnRight\", \"Attack1\", \"Attack2\", \"Attack3\", \"SwimAttack1\",\n            \"SwimAttack2\", \"SwimAttack3\", \"HandToHand\", \"Crossbow\", \"BowAndArrow\", \"ThrowWeapon\", \"WeaponOneHand\",\n            \"WeaponTwoHand\", \"WeaponTwoWide\", \"Shield\", \"PickProbe\", \"InventoryHandToHand\", \"InventoryWeaponOneHand\",\n            \"InventoryWeaponTwoHand\", \"InventoryWeaponTwoWide\"\n        };\n\n        if (anis.mGroupIndex < (sizeof(animGroups) / sizeof(*animGroups)))\n        {\n            std::string group(animGroups[anis.mGroupIndex]);\n            Misc::StringUtils::lowerCaseInPlace(group);\n\n            ESM::AnimationState::ScriptedAnimation scriptedAnim;\n            scriptedAnim.mGroup = group;\n            scriptedAnim.mTime = anis.mTime;\n            scriptedAnim.mAbsolute = true;\n            // Neither loop count nor queueing seems to be supported by the ess format.\n            scriptedAnim.mLoopCount = std::numeric_limits<size_t>::max();\n            state.mScriptedAnims.push_back(scriptedAnim);\n        }\n        else\n            // TODO: Handle 0xFF index, which seems to be used for finished animations.\n            std::cerr << \"unknown animation group index: \" << static_cast<unsigned int>(anis.mGroupIndex) << std::endl;\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/convertacdt.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTACDT_H\n#define OPENMW_ESSIMPORT_CONVERTACDT_H\n\n#include <components/esm/creaturestats.hpp>\n#include <components/esm/npcstats.hpp>\n#include <components/esm/loadskil.hpp>\n#include <components/esm/animationstate.hpp>\n\n#include \"importacdt.hpp\"\n\nnamespace ESSImport\n{\n\n    // OpenMW uses Health,Magicka,Fatigue, MW uses Health,Fatigue,Magicka\n    int translateDynamicIndex(int mwIndex);\n\n\n    void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats);\n    void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);\n\n    void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);\n\n    void convertANIS (const ANIS& anis, ESM::AnimationState& state);\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/convertcntc.cpp",
    "content": "#include \"convertcntc.hpp\"\n\n#include \"convertinventory.hpp\"\n\nnamespace ESSImport\n{\n\n    void convertCNTC(const CNTC &cntc, ESM::ContainerState &state)\n    {\n        convertInventory(cntc.mInventory, state.mInventory);\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/convertcntc.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTCNTC_H\n#define OPENMW_ESSIMPORT_CONVERTCNTC_H\n\n#include \"importcntc.hpp\"\n\n#include <components/esm/containerstate.hpp>\n\nnamespace ESSImport\n{\n\n    void convertCNTC(const CNTC& cntc, ESM::ContainerState& state);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/convertcrec.cpp",
    "content": "#include \"convertcrec.hpp\"\n\n#include \"convertinventory.hpp\"\n\nnamespace ESSImport\n{\n\n    void convertCREC(const CREC &crec, ESM::CreatureState &state)\n    {\n        convertInventory(crec.mInventory, state.mInventory);\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/convertcrec.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTCREC_H\n#define OPENMW_ESSIMPORT_CONVERTCREC_H\n\n#include \"importcrec.hpp\"\n\n#include <components/esm/creaturestate.hpp>\n\nnamespace ESSImport\n{\n\n    void convertCREC(const CREC& crec, ESM::CreatureState& state);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/converter.cpp",
    "content": "#include \"converter.hpp\"\n\n#include <stdexcept>\n#include <algorithm>\n\n#include <osgDB/WriteFile>\n\n#include <components/esm/creaturestate.hpp>\n#include <components/esm/containerstate.hpp>\n\n#include <components/misc/constants.hpp>\n\n#include \"convertcrec.hpp\"\n#include \"convertcntc.hpp\"\n#include \"convertscri.hpp\"\n\nnamespace\n{\n\n    void convertImage(char* data, int size, int width, int height, GLenum pf, const std::string& out)\n    {\n        osg::ref_ptr<osg::Image> image (new osg::Image);\n        image->allocateImage(width, height, 1, pf, GL_UNSIGNED_BYTE);\n        memcpy(image->data(), data, size);\n        image->flipVertical();\n\n        osgDB::writeImageFile(*image, out);\n    }\n\n\n    void convertCellRef(const ESSImport::CellRef& cellref, ESM::ObjectState& objstate)\n    {\n        objstate.mEnabled = cellref.mEnabled;\n        objstate.mPosition = cellref.mPos;\n        objstate.mRef.mRefNum = cellref.mRefNum;\n        if (cellref.mDeleted)\n            objstate.mCount = 0;\n        convertSCRI(cellref.mSCRI, objstate.mLocals);\n        objstate.mHasLocals = !objstate.mLocals.mVariables.empty();\n\n        if (cellref.mHasANIS)\n            convertANIS(cellref.mANIS, objstate.mAnimationState);\n    }\n\n    bool isIndexedRefId(const std::string& indexedRefId)\n    {\n        if (indexedRefId.size() <= 8)\n            return false;\n\n        if (indexedRefId.find_first_not_of(\"0123456789\") == std::string::npos)\n            return false; // entirely numeric refid, this is a reference to\n                          // a dynamically created record e.g. player-enchanted weapon\n\n        std::string index = indexedRefId.substr(indexedRefId.size()-8);\n        return index.find_first_not_of(\"0123456789ABCDEF\") == std::string::npos;\n    }\n\n    void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId)\n    {\n        std::stringstream stream;\n        stream << std::hex << indexedRefId.substr(indexedRefId.size()-8,8);\n        stream >> refIndex;\n\n        refId = indexedRefId.substr(0,indexedRefId.size()-8);\n    }\n\n    int convertActorId(const std::string& indexedRefId, ESSImport::Context& context)\n    {\n        if (isIndexedRefId(indexedRefId))\n        {\n            int refIndex;\n            std::string refId;\n            splitIndexedRefId(indexedRefId, refIndex, refId);\n\n            auto it = context.mActorIdMap.find(std::make_pair(refIndex, refId));\n            if (it == context.mActorIdMap.end())\n                return -1;\n            return it->second;\n        }\n        else if (indexedRefId == \"PlayerSaveGame\")\n        {\n            return context.mPlayer.mObject.mCreatureStats.mActorId;\n        }\n\n        return -1;\n    }\n}\n\nnamespace ESSImport\n{\n\n\n    struct MAPH\n    {\n        unsigned int size;\n        unsigned int value;\n    };\n\n    void ConvertFMAP::read(ESM::ESMReader &esm)\n    {\n        MAPH maph;\n        esm.getHNT(maph, \"MAPH\");\n        std::vector<char> data;\n        esm.getSubNameIs(\"MAPD\");\n        esm.getSubHeader();\n        data.resize(esm.getSubSize());\n        esm.getExact(&data[0], data.size());\n\n        mGlobalMapImage = new osg::Image;\n        mGlobalMapImage->allocateImage(maph.size, maph.size, 1, GL_RGB, GL_UNSIGNED_BYTE);\n        memcpy(mGlobalMapImage->data(), &data[0], data.size());\n\n        // to match openmw size\n        // FIXME: filtering?\n        mGlobalMapImage->scaleImage(maph.size*2, maph.size*2, 1, GL_UNSIGNED_BYTE);\n    }\n\n    void ConvertFMAP::write(ESM::ESMWriter &esm)\n    {\n        int numcells = mGlobalMapImage->s() / 18; // NB truncating, doesn't divide perfectly\n                                                       // with the 512x512 map the game has by default\n        int cellSize = mGlobalMapImage->s()/numcells;\n\n        // Note the upper left corner of the (0,0) cell should be at (width/2, height/2)\n\n        mContext->mGlobalMapState.mBounds.mMinX = -numcells/2;\n        mContext->mGlobalMapState.mBounds.mMaxX = (numcells-1)/2;\n        mContext->mGlobalMapState.mBounds.mMinY = -(numcells-1)/2;\n        mContext->mGlobalMapState.mBounds.mMaxY = numcells/2;\n\n        osg::ref_ptr<osg::Image> image2 (new osg::Image);\n        int width = cellSize*numcells;\n        int height = cellSize*numcells;\n        std::vector<unsigned char> data;\n        data.resize(width*height*4, 0);\n\n        image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);\n        memcpy(image2->data(), &data[0], data.size());\n\n        for (const auto & exploredCell : mContext->mExploredCells)\n        {\n            if (exploredCell.first > mContext->mGlobalMapState.mBounds.mMaxX\n                    || exploredCell.first < mContext->mGlobalMapState.mBounds.mMinX\n                    || exploredCell.second > mContext->mGlobalMapState.mBounds.mMaxY\n                    || exploredCell.second < mContext->mGlobalMapState.mBounds.mMinY)\n            {\n                // out of bounds, I think this could happen, since the original engine had a fixed-size map\n                continue;\n            }\n\n            int imageLeftSrc = mGlobalMapImage->s()/2;\n            int imageTopSrc = mGlobalMapImage->t()/2;\n            imageLeftSrc += exploredCell.first * cellSize;\n            imageTopSrc -= exploredCell.second * cellSize;\n            int imageLeftDst = width/2;\n            int imageTopDst = height/2;\n            imageLeftDst += exploredCell.first * cellSize;\n            imageTopDst -= exploredCell.second * cellSize;\n            for (int x=0; x<cellSize; ++x)\n                for (int y=0; y<cellSize; ++y)\n                {\n                    unsigned int col = *(unsigned int*)mGlobalMapImage->data(imageLeftSrc+x, imageTopSrc+y, 0);\n                    *(unsigned int*)image2->data(imageLeftDst+x, imageTopDst+y, 0) = col;\n                }\n        }\n\n        std::stringstream ostream;\n        osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n        if (!readerwriter)\n        {\n            std::cerr << \"Error: can't write global map image, no png readerwriter found\" << std::endl;\n            return;\n        }\n\n        image2->flipVertical();\n\n        osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image2, ostream);\n        if (!result.success())\n        {\n            std::cerr << \"Error: can't write global map image: \" << result.message() << \" code \" << result.status() << std::endl;\n            return;\n        }\n\n        std::string outData = ostream.str();\n        mContext->mGlobalMapState.mImageData = std::vector<char>(outData.begin(), outData.end());\n\n        esm.startRecord(ESM::REC_GMAP);\n        mContext->mGlobalMapState.save(esm);\n        esm.endRecord(ESM::REC_GMAP);\n    }\n\n    void ConvertCell::read(ESM::ESMReader &esm)\n    {\n        ESM::Cell cell;\n        bool isDeleted = false;\n\n        cell.load(esm, isDeleted, false);\n\n        // I wonder what 0x40 does?\n        if (cell.isExterior() && cell.mData.mFlags & 0x20)\n        {\n            mContext->mGlobalMapState.mMarkers.insert(std::make_pair(cell.mData.mX, cell.mData.mY));\n        }\n\n        // note if the player is in a nameless exterior cell, we will assign the cellId later based on player position\n        if (cell.mName == mContext->mPlayerCellName)\n        {\n            mContext->mPlayer.mCellId = cell.getCellId();\n        }\n\n        Cell newcell;\n        newcell.mCell = cell;\n\n        // fog of war\n        // seems to be a 1-bit pixel format, 16*16 pixels\n        // TODO: add bleeding of FOW into neighbouring cells (openmw handles this by writing to the textures,\n        // MW handles it when rendering only)\n        unsigned char nam8[32];\n        // exterior has 1 NAM8, interior can have multiple ones, and have an extra 4 byte flag at the start\n        // (probably offset of that specific fog texture?)\n        while (esm.isNextSub(\"NAM8\"))\n        {\n            if (cell.isExterior()) // TODO: NAM8 occasionally exists for cells that haven't been explored.\n                                   // are there any flags marking explored cells?\n                mContext->mExploredCells.insert(std::make_pair(cell.mData.mX, cell.mData.mY));\n\n            esm.getSubHeader();\n\n            if (esm.getSubSize() == 36)\n            {\n                // flag on interiors\n                esm.skip(4);\n            }\n\n            esm.getExact(nam8, 32);\n\n            newcell.mFogOfWar.reserve(16*16);\n            for (int x=0; x<16; ++x)\n            {\n                for (int y=0; y<16; ++y)\n                {\n                    size_t pos = x*16+y;\n                    size_t bytepos = pos/8;\n                    assert(bytepos<32);\n                    int bit = pos%8;\n                    newcell.mFogOfWar.push_back(((nam8[bytepos] >> bit) & (0x1)) ? 0xffffffff : 0x000000ff);\n                }\n            }\n\n            if (cell.isExterior())\n            {\n                std::ostringstream filename;\n                filename << \"fog_\" << cell.mData.mX << \"_\" << cell.mData.mY << \".tga\";\n\n                convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, GL_RGBA, filename.str());\n            }\n        }\n\n        // moved reference, not handled yet\n        // NOTE: MVRF can also occur in within normal references (importcellref.cpp)?\n        // this does not match the ESM file implementation,\n        // verify if that can happen with ESM files too\n        while (esm.isNextSub(\"MVRF\"))\n        {\n            esm.skipHSub(); // skip MVRF\n            esm.getSubName();\n            esm.skipHSub(); // skip CNDT\n        }\n\n        std::vector<CellRef> cellrefs;\n        while (esm.hasMoreSubs() && esm.isNextSub(\"FRMR\"))\n        {\n            CellRef ref;\n            ref.load (esm);\n            cellrefs.push_back(ref);\n        }\n\n        while (esm.isNextSub(\"MPCD\"))\n        {\n            float notepos[3];\n            esm.getHT(notepos, 3*sizeof(float));\n\n            // Markers seem to be arranged in a 32*32 grid, notepos has grid-indices.\n            // This seems to be the reason markers can't be placed everywhere in interior cells,\n            // i.e. when the grid is exceeded.\n            // Converting the interior markers correctly could be rather tricky, but is probably similar logic\n            // as used for the FoW texture placement, which we need to figure out anyway\n            notepos[1] += 31.f;\n            notepos[0] += 0.5;\n            notepos[1] += 0.5;\n            notepos[0] = Constants::CellSizeInUnits * notepos[0] / 32.f;\n            notepos[1] = Constants::CellSizeInUnits * notepos[1] / 32.f;\n            if (cell.isExterior())\n            {\n                notepos[0] += Constants::CellSizeInUnits * cell.mData.mX;\n                notepos[1] += Constants::CellSizeInUnits * cell.mData.mY;\n            }\n            // TODO: what encoding is this in?\n            std::string note = esm.getHNString(\"MPNT\");\n            ESM::CustomMarker marker;\n            marker.mWorldX = notepos[0];\n            marker.mWorldY = notepos[1];\n            marker.mNote = note;\n            marker.mCell = cell.getCellId();\n            mMarkers.push_back(marker);\n        }\n\n        newcell.mRefs = cellrefs;\n\n\n        if (cell.isExterior())\n            mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;\n        else\n            mIntCells[cell.mName] = newcell;\n    }\n\n    void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm)\n    {\n        ESM::Cell esmcell = cell.mCell;\n        esm.startRecord(ESM::REC_CSTA);\n        ESM::CellState csta;\n        csta.mHasFogOfWar = 0;\n        csta.mId = esmcell.getCellId();\n        csta.mId.save(esm);\n        // TODO csta.mLastRespawn;\n        // shouldn't be needed if we respawn on global schedule like in original MW\n        csta.mWaterLevel = esmcell.mWater;\n        csta.save(esm);\n\n        for (const auto & cellref : cell.mRefs)\n        {\n            ESM::CellRef out (cellref);\n\n            // TODO: use mContext->mCreatures/mNpcs\n\n            if (!isIndexedRefId(cellref.mIndexedRefId))\n            {\n                // non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it\n                // this could be any type of object really (even creatures/npcs too)\n                out.mRefID = cellref.mIndexedRefId;\n                std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);\n\n                ESM::ObjectState objstate;\n                objstate.blank();\n                objstate.mRef = out;\n                objstate.mRef.mRefID = idLower;\n                objstate.mHasCustomState = false;\n                convertCellRef(cellref, objstate);\n                esm.writeHNT (\"OBJE\", 0);\n                objstate.save(esm);\n                continue;\n            }\n            else\n            {\n                int refIndex;\n                splitIndexedRefId(cellref.mIndexedRefId, refIndex, out.mRefID);\n\n                std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);\n\n                std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(\n                            std::make_pair(refIndex, out.mRefID));\n                if (npccIt != mContext->mNpcChanges.end())\n                {\n                    ESM::NpcState objstate;\n                    objstate.blank();\n                    objstate.mRef = out;\n                    objstate.mRef.mRefID = idLower;\n                    // TODO: need more micromanagement here so we don't overwrite values\n                    // from the ESM with default values\n                    if (cellref.mHasACDT)\n                        convertACDT(cellref.mACDT, objstate.mCreatureStats);\n                    if (cellref.mHasACSC)\n                        convertACSC(cellref.mACSC, objstate.mCreatureStats);\n                    convertNpcData(cellref, objstate.mNpcStats);\n                    convertNPCC(npccIt->second, objstate);\n                    convertCellRef(cellref, objstate);\n\n                    objstate.mCreatureStats.mActorId = mContext->generateActorId();\n                    mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId));\n\n                    esm.writeHNT (\"OBJE\", ESM::REC_NPC_);\n                    objstate.save(esm);\n                    continue;\n                }\n\n                std::map<std::pair<int, std::string>, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find(\n                            std::make_pair(refIndex, out.mRefID));\n                if (cntcIt != mContext->mContainerChanges.end())\n                {\n                    ESM::ContainerState objstate;\n                    objstate.blank();\n                    objstate.mRef = out;\n                    objstate.mRef.mRefID = idLower;\n                    convertCNTC(cntcIt->second, objstate);\n                    convertCellRef(cellref, objstate);\n                    esm.writeHNT (\"OBJE\", ESM::REC_CONT);\n                    objstate.save(esm);\n                    continue;\n                }\n\n                std::map<std::pair<int, std::string>, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find(\n                            std::make_pair(refIndex, out.mRefID));\n                if (crecIt != mContext->mCreatureChanges.end())\n                {\n                    ESM::CreatureState objstate;\n                    objstate.blank();\n                    objstate.mRef = out;\n                    objstate.mRef.mRefID = idLower;\n                    // TODO: need more micromanagement here so we don't overwrite values\n                    // from the ESM with default values\n                    if (cellref.mHasACDT)\n                        convertACDT(cellref.mACDT, objstate.mCreatureStats);\n                    if (cellref.mHasACSC)\n                        convertACSC(cellref.mACSC, objstate.mCreatureStats);\n                    convertCREC(crecIt->second, objstate);\n                    convertCellRef(cellref, objstate);\n\n                    objstate.mCreatureStats.mActorId = mContext->generateActorId();\n                    mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId));\n\n                    esm.writeHNT (\"OBJE\", ESM::REC_CREA);\n                    objstate.save(esm);\n                    continue;\n                }\n\n                std::stringstream error;\n                error << \"Can't find type for \" << cellref.mIndexedRefId << std::endl;\n                throw std::runtime_error(error.str());\n            }\n        }\n\n        esm.endRecord(ESM::REC_CSTA);\n    }\n\n    void ConvertCell::write(ESM::ESMWriter &esm)\n    {\n        for (const auto & cell : mIntCells)\n            writeCell(cell.second, esm);\n\n        for (const auto & cell : mExtCells)\n            writeCell(cell.second, esm);\n\n        for (const auto & marker : mMarkers)\n        {\n            esm.startRecord(ESM::REC_MARK);\n            marker.save(esm);\n            esm.endRecord(ESM::REC_MARK);\n        }\n    }\n\n    void ConvertPROJ::read(ESM::ESMReader& esm)\n    {\n        mProj.load(esm);\n    }\n\n    void ConvertPROJ::write(ESM::ESMWriter& esm)\n    {\n        for (const PROJ::PNAM& pnam : mProj.mProjectiles)\n        {\n            if (!pnam.isMagic())\n            {\n                ESM::ProjectileState out;\n                convertBaseState(out, pnam);\n\n                out.mBowId = pnam.mBowId.toString();\n                out.mVelocity = pnam.mVelocity;\n                out.mAttackStrength = pnam.mAttackStrength;\n\n                esm.startRecord(ESM::REC_PROJ);\n                out.save(esm);\n                esm.endRecord(ESM::REC_PROJ);\n            }\n            else\n            {\n                ESM::MagicBoltState out;\n                convertBaseState(out, pnam);\n\n                auto it = std::find_if(mContext->mActiveSpells.begin(), mContext->mActiveSpells.end(),\n                                       [&pnam](const SPLM::ActiveSpell& spell) -> bool { return spell.mIndex == pnam.mSplmIndex; });\n\n                if (it == mContext->mActiveSpells.end())\n                {\n                    std::cerr << \"Warning: Skipped conversion for magic projectile \\\"\" << pnam.mArrowId.toString() << \"\\\" (invalid spell link)\" << std::endl;\n                    continue;\n                }\n\n                out.mSpellId = it->mSPDT.mId.toString();\n                out.mSpeed = pnam.mSpeed * 0.001f; // not sure where this factor comes from\n\n                esm.startRecord(ESM::REC_MPRJ);\n                out.save(esm);\n                esm.endRecord(ESM::REC_MPRJ);\n            }\n        }\n    }\n\n    void ConvertPROJ::convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam)\n    {\n        base.mId = pnam.mArrowId.toString();\n        base.mPosition = pnam.mPosition;\n\n        osg::Quat orient;\n        orient.makeRotate(osg::Vec3f(0,1,0), pnam.mVelocity);\n        base.mOrientation = orient;\n\n        base.mActorId = convertActorId(pnam.mActorId.toString(), *mContext);\n    }\n\n    void ConvertSPLM::read(ESM::ESMReader& esm)\n    {\n        mSPLM.load(esm);\n        mContext->mActiveSpells = mSPLM.mActiveSpells;\n    }\n\n    void ConvertSPLM::write(ESM::ESMWriter& esm)\n    {\n        std::cerr << \"Warning: Skipped active spell conversion (not implemented)\" << std::endl;\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/converter.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTER_H\n#define OPENMW_ESSIMPORT_CONVERTER_H\n\n#include <limits>\n\n#include <osg/Image>\n#include <osg/ref_ptr>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n\n#include <components/esm/loadcell.hpp>\n#include <components/esm/loadbook.hpp>\n#include <components/esm/loadclas.hpp>\n#include <components/esm/loadglob.hpp>\n#include <components/esm/cellstate.hpp>\n#include <components/esm/loadfact.hpp>\n#include <components/esm/dialoguestate.hpp>\n#include <components/esm/custommarkerstate.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/weatherstate.hpp>\n#include <components/esm/globalscript.hpp>\n#include <components/esm/queststate.hpp>\n#include <components/esm/stolenitems.hpp>\n#include <components/esm/projectilestate.hpp>\n\n#include \"importcrec.hpp\"\n#include \"importcntc.hpp\"\n\n#include \"importercontext.hpp\"\n#include \"importcellref.hpp\"\n#include \"importklst.hpp\"\n#include \"importgame.hpp\"\n#include \"importinfo.hpp\"\n#include \"importdial.hpp\"\n#include \"importques.hpp\"\n#include \"importjour.hpp\"\n#include \"importscpt.hpp\"\n#include \"importproj.h\"\n#include \"importsplm.h\"\n\n#include \"convertacdt.hpp\"\n#include \"convertnpcc.hpp\"\n#include \"convertscpt.hpp\"\n#include \"convertplayer.hpp\"\n\nnamespace ESSImport\n{\n\nclass Converter\n{\npublic:\n    /// @return the order for writing this converter's records to the output file, in relation to other converters\n    virtual int getStage() { return 1; }\n\n    virtual ~Converter() {}\n\n    void setContext(Context& context) { mContext = &context; }\n\n    /// @note The load method of ESM records accept the deleted flag as a parameter.\n    /// I don't know can the DELE sub-record appear in saved games, so the deleted flag will be ignored.\n    virtual void read(ESM::ESMReader& esm)\n    {\n    }\n\n    /// Called after the input file has been read in completely, which may be necessary\n    /// if the conversion process relies on information in other records\n    virtual void write(ESM::ESMWriter& esm)\n    {\n\n    }\n\nprotected:\n    Context* mContext;\n};\n\n/// Default converter: simply reads the record and writes it unmodified to the output\ntemplate <typename T>\nclass DefaultConverter : public Converter\n{\npublic:\n    int getStage() override { return 0; }\n\n    void read(ESM::ESMReader& esm) override\n    {\n        T record;\n        bool isDeleted = false;\n\n        record.load(esm, isDeleted);\n        mRecords[record.mId] = record;\n    }\n\n    void write(ESM::ESMWriter& esm) override\n    {\n        for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)\n        {\n            esm.startRecord(T::sRecordId);\n            it->second.save(esm);\n            esm.endRecord(T::sRecordId);\n        }\n    }\n\nprotected:\n    std::map<std::string, T> mRecords;\n};\n\nclass ConvertNPC : public Converter\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        ESM::NPC npc;\n        bool isDeleted = false;\n\n        npc.load(esm, isDeleted);\n        if (npc.mId != \"player\")\n        {\n            // Handles changes to the NPC struct, but since there is no index here\n            // it will apply to ALL instances of the class. seems to be the reason for the\n            // \"feature\" in MW where changing AI settings of one guard will change it for all guards of that refID.\n            mContext->mNpcs[Misc::StringUtils::lowerCase(npc.mId)] = npc;\n        }\n        else\n        {\n            mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt.mLevel;\n            mContext->mPlayerBase = npc;\n            ESM::SpellState::SpellParams empty;\n            // FIXME: player start spells and birthsign spells aren't listed here,\n            // need to fix openmw to account for this\n            for (const auto & spell : npc.mSpells.mList)\n                mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty;\n\n            // Clear the list now that we've written it, this prevents issues cropping up with\n            // ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal.\n            mContext->mPlayerBase.mSpells.mList.clear();\n\n            // Same with inventory. Actually it's strange this would contain something, since there's already an\n            // inventory list in NPCC. There seems to be a fair amount of redundancy in this format.\n            mContext->mPlayerBase.mInventory.mList.clear();\n        }\n    }\n};\n\nclass ConvertCREA : public Converter\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        // See comment in ConvertNPC\n        ESM::Creature creature;\n        bool isDeleted = false;\n\n        creature.load(esm, isDeleted);\n        mContext->mCreatures[Misc::StringUtils::lowerCase(creature.mId)] = creature;\n    }\n};\n\n// Do we need ConvertCONT?\n// I've seen a CONT record in a certain save file, but the container contents in it\n// were identical to a corresponding CNTC record. See previous comment about redundancy...\n\nclass ConvertGlobal : public DefaultConverter<ESM::Global>\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        ESM::Global global;\n        bool isDeleted = false;\n\n        global.load(esm, isDeleted);\n        if (Misc::StringUtils::ciEqual(global.mId, \"gamehour\"))\n            mContext->mHour = global.mValue.getFloat();\n        if (Misc::StringUtils::ciEqual(global.mId, \"day\"))\n            mContext->mDay = global.mValue.getInteger();\n        if (Misc::StringUtils::ciEqual(global.mId, \"month\"))\n            mContext->mMonth = global.mValue.getInteger();\n        if (Misc::StringUtils::ciEqual(global.mId, \"year\"))\n            mContext->mYear = global.mValue.getInteger();\n        mRecords[global.mId] = global;\n    }\n};\n\nclass ConvertClass : public DefaultConverter<ESM::Class>\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        ESM::Class class_;\n        bool isDeleted = false;\n\n        class_.load(esm, isDeleted);\n        if (class_.mId == \"NEWCLASSID_CHARGEN\")\n            mContext->mCustomPlayerClassName = class_.mName;\n\n        mRecords[class_.mId] = class_;\n    }\n};\n\nclass ConvertBook : public DefaultConverter<ESM::Book>\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        ESM::Book book;\n        bool isDeleted = false;\n\n        book.load(esm, isDeleted);\n        if (book.mData.mSkillId == -1)\n            mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));\n\n        mRecords[book.mId] = book;\n    }\n};\n\nclass ConvertNPCC : public Converter\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        std::string id = esm.getHNString(\"NAME\");\n        NPCC npcc;\n        npcc.load(esm);\n        if (id == \"PlayerSaveGame\")\n        {\n            convertNPCC(npcc, mContext->mPlayer.mObject);\n        }\n        else\n        {\n            int index = npcc.mNPDT.mIndex;\n            mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc));\n        }\n    }\n};\n\nclass ConvertREFR : public Converter\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        REFR refr;\n        refr.load(esm);\n        assert(refr.mRefID == \"PlayerSaveGame\");\n        mContext->mPlayer.mObject.mPosition = refr.mPos;\n\n        ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats;\n        convertACDT(refr.mActorData.mACDT, cStats);\n\n        ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats;\n        convertNpcData(refr.mActorData, npcStats);\n\n        mSelectedSpell = refr.mActorData.mSelectedSpell;\n        if (!refr.mActorData.mSelectedEnchantItem.empty())\n        {\n            ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory;\n\n            for (unsigned int i=0; i<invState.mItems.size(); ++i)\n            {\n                // FIXME: in case of conflict (multiple items with this refID) use the already equipped one?\n                if (Misc::StringUtils::ciEqual(invState.mItems[i].mRef.mRefID, refr.mActorData.mSelectedEnchantItem))\n                    invState.mSelectedEnchantItem = i;\n            }\n        }\n    }\n    void write(ESM::ESMWriter& esm) override\n    {\n        esm.startRecord(ESM::REC_ASPL);\n        esm.writeHNString(\"ID__\", mSelectedSpell);\n        esm.endRecord(ESM::REC_ASPL);\n    }\nprivate:\n    std::string mSelectedSpell;\n};\n\nclass ConvertPCDT : public Converter\n{\npublic:\n    ConvertPCDT()\n        : mFirstPersonCam(true),\n          mTeleportingEnabled(true),\n          mLevitationEnabled(true)\n    {}\n\n    void read(ESM::ESMReader &esm) override\n    {\n        PCDT pcdt;\n        pcdt.load(esm);\n\n        convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState);\n    }\n    void write(ESM::ESMWriter &esm) override\n    {\n        esm.startRecord(ESM::REC_ENAB);\n        esm.writeHNT(\"TELE\", mTeleportingEnabled);\n        esm.writeHNT(\"LEVT\", mLevitationEnabled);\n        esm.endRecord(ESM::REC_ENAB);\n\n        esm.startRecord(ESM::REC_CAM_);\n        esm.writeHNT(\"FIRS\", mFirstPersonCam);\n        esm.endRecord(ESM::REC_CAM_);\n    }\nprivate:\n    bool mFirstPersonCam;\n    bool mTeleportingEnabled;\n    bool mLevitationEnabled;\n};\n\nclass ConvertCNTC : public Converter\n{\n    void read(ESM::ESMReader &esm) override\n    {\n        std::string id = esm.getHNString(\"NAME\");\n        CNTC cntc;\n        cntc.load(esm);\n        mContext->mContainerChanges.insert(std::make_pair(std::make_pair(cntc.mIndex,id), cntc));\n    }\n};\n\nclass ConvertCREC : public Converter\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        std::string id = esm.getHNString(\"NAME\");\n        CREC crec;\n        crec.load(esm);\n        mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec));\n    }\n};\n\nclass ConvertFMAP : public Converter\n{\npublic:\n    void read(ESM::ESMReader &esm) override;\n    void write(ESM::ESMWriter &esm) override;\n\nprivate:\n    osg::ref_ptr<osg::Image> mGlobalMapImage;\n};\n\nclass ConvertCell : public Converter\n{\npublic:\n    void read(ESM::ESMReader& esm) override;\n    void write(ESM::ESMWriter& esm) override;\n\nprivate:\n    struct Cell\n    {\n        ESM::Cell mCell;\n        std::vector<CellRef> mRefs;\n        std::vector<unsigned int> mFogOfWar;\n    };\n\n    std::map<std::string, Cell> mIntCells;\n    std::map<std::pair<int, int>, Cell> mExtCells;\n\n    std::vector<ESM::CustomMarker> mMarkers;\n\n    void writeCell(const Cell& cell, ESM::ESMWriter &esm);\n};\n\nclass ConvertKLST : public Converter\n{\npublic:\n    void read(ESM::ESMReader& esm) override\n    {\n        KLST klst;\n        klst.load(esm);\n        mKillCounter = klst.mKillCounter;\n\n        mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills;\n    }\n\n    void write(ESM::ESMWriter &esm) override\n    {\n        esm.startRecord(ESM::REC_DCOU);\n        for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)\n        {\n            esm.writeHNString(\"ID__\", it->first);\n            esm.writeHNT (\"COUN\", it->second);\n        }\n        esm.endRecord(ESM::REC_DCOU);\n    }\n\nprivate:\n    std::map<std::string, int> mKillCounter;\n};\n\nclass ConvertFACT : public Converter\n{\npublic:\n    void read(ESM::ESMReader& esm) override\n    {\n        ESM::Faction faction;\n        bool isDeleted = false;\n\n        faction.load(esm, isDeleted);\n        std::string id = Misc::StringUtils::lowerCase(faction.mId);\n\n        for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)\n        {\n            std::string faction2 = Misc::StringUtils::lowerCase(it->first);\n            mContext->mDialogueState.mChangedFactionReaction[id].insert(std::make_pair(faction2, it->second));\n        }\n    }\n};\n\n/// Stolen items\nclass ConvertSTLN : public Converter\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        std::string itemid = esm.getHNString(\"NAME\");\n        Misc::StringUtils::lowerCaseInPlace(itemid);\n\n        while (esm.isNextSub(\"FNAM\") || esm.isNextSub(\"ONAM\"))\n        {\n            if (esm.retSubName().toString() == \"FNAM\")\n            {\n                std::string factionid = esm.getHString();\n                mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(factionid), true));\n            }\n            else\n            {\n                std::string ownerid = esm.getHString();\n                mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false));\n            }\n        }\n    }\n    void write(ESM::ESMWriter &esm) override\n    {\n        ESM::StolenItems items;\n        for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)\n        {\n            std::map<std::pair<std::string, bool>, int> owners;\n            for (const auto & ownerIt : it->second)\n            {\n                owners.insert(std::make_pair(std::make_pair(ownerIt.first, ownerIt.second)\n                                             // Since OpenMW doesn't suffer from the owner contamination bug,\n                                             // it needs a count argument. But for legacy savegames, we don't know\n                                             // this count, so must assume all items of that ID are stolen,\n                                             // like vanilla MW did.\n                                             ,std::numeric_limits<int>::max()));\n            }\n\n            items.mStolenItems.insert(std::make_pair(it->first, owners));\n        }\n\n        esm.startRecord(ESM::REC_STLN);\n        items.write(esm);\n        esm.endRecord(ESM::REC_STLN);\n    }\n\nprivate:\n    typedef std::pair<std::string, bool> Owner; // <owner id, bool isFaction>\n\n    std::map<std::string, std::set<Owner> > mStolenItems;\n};\n\n/// Seen responses for a dialogue topic?\n/// Each DIAL record is followed by a number of INFO records, I believe, just like in ESMs\n/// Dialogue conversion problems:\n/// - Journal is stored in one continuous HTML markup rather than each entry separately with associated info ID.\n/// - Seen dialogue responses only store the INFO id, rather than the fulltext.\n/// - Quest stages only store the INFO id, rather than the journal entry fulltext.\nclass ConvertINFO : public Converter\n{\npublic:\n    void read(ESM::ESMReader& esm) override\n    {\n        INFO info;\n        info.load(esm);\n    }\n};\n\nclass ConvertDIAL : public Converter\n{\npublic:\n    void read(ESM::ESMReader& esm) override\n    {\n        std::string id = esm.getHNString(\"NAME\");\n        DIAL dial;\n        dial.load(esm);\n        if (dial.mIndex > 0)\n            mDials[id] = dial;\n    }\n    void write(ESM::ESMWriter &esm) override\n    {\n        for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it)\n        {\n            esm.startRecord(ESM::REC_QUES);\n            ESM::QuestState state;\n            state.mFinished = 0;\n            state.mState = it->second.mIndex;\n            state.mTopic = Misc::StringUtils::lowerCase(it->first);\n            state.save(esm);\n            esm.endRecord(ESM::REC_QUES);\n        }\n    }\nprivate:\n    std::map<std::string, DIAL> mDials;\n};\n\nclass ConvertQUES : public Converter\n{\npublic:\n    void read(ESM::ESMReader& esm) override\n    {\n        std::string id = esm.getHNString(\"NAME\");\n        QUES quest;\n        quest.load(esm);\n    }\n};\n\nclass ConvertJOUR : public Converter\n{\npublic:\n    void read(ESM::ESMReader& esm) override\n    {\n        JOUR journal;\n        journal.load(esm);\n    }\n};\n\nclass ConvertGAME : public Converter\n{\npublic:\n    ConvertGAME()\n        : mHasGame(false)\n    {\n    }\n\n    void read(ESM::ESMReader &esm) override\n    {\n        mGame.load(esm);\n        mHasGame = true;\n    }\n\n    int validateWeatherID(int weatherID)\n    {\n        if(weatherID >= -1 && weatherID < 10)\n        {\n            return weatherID;\n        }\n        else\n        {\n            std::stringstream error;\n            error << \"Invalid weather ID:\" << weatherID << std::endl;\n            throw std::runtime_error(error.str());\n        }\n    }\n\n    void write(ESM::ESMWriter &esm) override\n    {\n        if (!mHasGame)\n            return;\n        esm.startRecord(ESM::REC_WTHR);\n        ESM::WeatherState weather;\n        weather.mTimePassed = 0.0f;\n        weather.mFastForward = false;\n        weather.mWeatherUpdateTime = mGame.mGMDT.mTimeOfNextTransition - mContext->mHour;\n        weather.mTransitionFactor = 1 - (mGame.mGMDT.mWeatherTransition / 100.0f);\n        weather.mCurrentWeather = validateWeatherID(mGame.mGMDT.mCurrentWeather);\n        weather.mNextWeather = validateWeatherID(mGame.mGMDT.mNextWeather);\n        weather.mQueuedWeather = -1;\n        // TODO: Determine how ModRegion modifiers are saved in Morrowind.\n        weather.save(esm);\n        esm.endRecord(ESM::REC_WTHR);\n    }\n\nprivate:\n    bool mHasGame;\n    GAME mGame;\n};\n\n/// Running global script\nclass ConvertSCPT : public Converter\n{\npublic:\n    void read(ESM::ESMReader &esm) override\n    {\n        SCPT script;\n        script.load(esm);\n        ESM::GlobalScript out;\n        convertSCPT(script, out);\n        mScripts.push_back(out);\n    }\n    void write(ESM::ESMWriter &esm) override\n    {\n        for (const auto & script : mScripts)\n        {\n            esm.startRecord(ESM::REC_GSCR);\n            script.save(esm);\n            esm.endRecord(ESM::REC_GSCR);\n        }\n    }\nprivate:\n    std::vector<ESM::GlobalScript> mScripts;\n};\n\n/// Projectile converter\nclass ConvertPROJ : public Converter\n{\npublic:\n    int getStage() override { return 2; }\n    void read(ESM::ESMReader& esm) override;\n    void write(ESM::ESMWriter& esm) override;\nprivate:\n    void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);\n    PROJ mProj;\n};\n\nclass ConvertSPLM : public Converter\n{\npublic:\n    void read(ESM::ESMReader& esm) override;\n    void write(ESM::ESMWriter& esm) override;\nprivate:\n    SPLM mSPLM;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/convertinventory.cpp",
    "content": "#include \"convertinventory.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <cstdlib>\n\nnamespace ESSImport\n{\n\n    void convertInventory(const Inventory &inventory, ESM::InventoryState &state)\n    {\n        int index = 0;\n        for (const auto & item : inventory.mItems)\n        {\n            ESM::ObjectState objstate;\n            objstate.blank();\n            objstate.mRef = item;\n            objstate.mRef.mRefID = Misc::StringUtils::lowerCase(item.mId);\n            objstate.mCount = std::abs(item.mCount); // restocking items have negative count in the savefile\n                                                    // openmw handles them differently, so no need to set any flags\n            state.mItems.push_back(objstate);\n            if (item.mRelativeEquipmentSlot != -1)\n                // Note we should really write the absolute slot here, which we do not know about\n                // Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when\n                // an item could be equipped in two different slots (e.g. equipped two rings)\n                state.mEquipmentSlots[index] = item.mRelativeEquipmentSlot;\n            ++index;\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/convertinventory.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTINVENTORY_H\n#define OPENMW_ESSIMPORT_CONVERTINVENTORY_H\n\n#include \"importinventory.hpp\"\n\n#include <components/esm/inventorystate.hpp>\n\nnamespace ESSImport\n{\n\n    void convertInventory (const Inventory& inventory, ESM::InventoryState& state);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/convertnpcc.cpp",
    "content": "#include \"convertnpcc.hpp\"\n\n#include \"convertinventory.hpp\"\n\nnamespace ESSImport\n{\n\n    void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState)\n    {\n        npcState.mNpcStats.mDisposition = npcc.mNPDT.mDisposition;\n        npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation;\n\n        convertInventory(npcc.mInventory, npcState.mInventory);\n    }\n}\n"
  },
  {
    "path": "apps/essimporter/convertnpcc.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTNPCC_H\n#define OPENMW_ESSIMPORT_CONVERTNPCC_H\n\n#include \"importnpcc.hpp\"\n\n#include <components/esm/npcstate.hpp>\n\nnamespace ESSImport\n{\n\n    void convertNPCC (const NPCC& npcc, ESM::NpcState& npcState);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/convertplayer.cpp",
    "content": "#include \"convertplayer.hpp\"\n\n#include <components/misc/constants.hpp>\n#include <components/misc/stringops.hpp>\n\nnamespace ESSImport\n{\n\n    void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls)\n    {\n        out.mBirthsign = pcdt.mBirthsign;\n        out.mObject.mNpcStats.mBounty = pcdt.mBounty;\n        for (const auto & essFaction : pcdt.mFactions)\n        {\n            ESM::NpcStats::Faction faction;\n            faction.mExpelled = (essFaction.mFlags & 0x2) != 0;\n            faction.mRank = essFaction.mRank;\n            faction.mReputation = essFaction.mReputation;\n            out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(essFaction.mFactionName.toString())] = faction;\n        }\n        for (int i=0; i<3; ++i)\n            out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];\n        for (int i=0; i<8; ++i)\n            out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];\n        for (int i=0; i<27; ++i)\n            out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];\n        out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;\n\n        if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawn)\n            out.mObject.mCreatureStats.mDrawState = 1;\n        if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawn)\n            out.mObject.mCreatureStats.mDrawState = 2;\n\n        firstPersonCam = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ThirdPerson);\n        teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled);\n        levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled);\n\n        for (const auto & knownDialogueTopic : pcdt.mKnownDialogueTopics)\n        {\n            outDialogueTopics.push_back(Misc::StringUtils::lowerCase(knownDialogueTopic));\n        }\n\n        controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled;\n        controls.mControlsDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ControlsDisabled;\n        controls.mJumpingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_JumpingDisabled;\n        controls.mLookingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LookingDisabled;\n        controls.mVanityModeDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_VanityModeDisabled;\n        controls.mWeaponDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawingDisabled;\n        controls.mSpellDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawingDisabled;\n\n        if (pcdt.mHasMark)\n        {\n            out.mHasMark = 1;\n\n            const PCDT::PNAM::MarkLocation& mark = pcdt.mPNAM.mMarkLocation;\n\n            ESM::CellId cell;\n            cell.mWorldspace = ESM::CellId::sDefaultWorldspace;\n            cell.mPaged = true;\n\n            cell.mIndex.mX = mark.mCellX;\n            cell.mIndex.mY = mark.mCellY;\n\n            // TODO: Figure out a better way to detect interiors. (0, 0) is a valid exterior cell.\n            if (mark.mCellX == 0 && mark.mCellY == 0)\n            {\n                cell.mWorldspace = pcdt.mMNAM;\n                cell.mPaged = false;\n            }\n\n            out.mMarkedCell = cell;\n            out.mMarkedPosition.pos[0] = mark.mX;\n            out.mMarkedPosition.pos[1] = mark.mY;\n            out.mMarkedPosition.pos[2] = mark.mZ;\n            out.mMarkedPosition.rot[0] = out.mMarkedPosition.rot[1] = 0.0f;\n            out.mMarkedPosition.rot[2] = mark.mRotZ;\n        }\n\n        if (pcdt.mHasENAM)\n        {\n            out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * Constants::CellSizeInUnits;\n            out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * Constants::CellSizeInUnits;\n            out.mLastKnownExteriorPosition[2] = 0.0f;\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/convertplayer.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTPLAYER_H\n#define OPENMW_ESSIMPORT_CONVERTPLAYER_H\n\n#include \"importplayer.hpp\"\n\n#include <components/esm/player.hpp>\n#include <components/esm/controlsstate.hpp>\n\nnamespace ESSImport\n{\n\n    void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/convertscpt.cpp",
    "content": "#include \"convertscpt.hpp\"\n\n#include <components/misc/stringops.hpp>\n\n#include \"convertscri.hpp\"\n\nnamespace ESSImport\n{\n\n    void convertSCPT(const SCPT &scpt, ESM::GlobalScript &out)\n    {\n        out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString());\n        out.mRunning = scpt.mRunning;\n        out.mTargetRef.unset(); // TODO: convert target reference of global script\n        convertSCRI(scpt.mSCRI, out.mLocals);\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/convertscpt.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTSCPT_H\n#define OPENMW_ESSIMPORT_CONVERTSCPT_H\n\n#include <components/esm/globalscript.hpp>\n\n#include \"importscpt.hpp\"\n\nnamespace ESSImport\n{\n\nvoid convertSCPT(const SCPT& scpt, ESM::GlobalScript& out);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/convertscri.cpp",
    "content": "#include \"convertscri.hpp\"\n\nnamespace\n{\n\n    template <typename T, ESM::VarType VariantType>\n    void storeVariables(const std::vector<T>& variables, ESM::Locals& locals, const std::string& scriptname)\n    {\n        for (const auto& variable : variables)\n        {\n            ESM::Variant val(variable);\n            val.setType(VariantType);\n            locals.mVariables.emplace_back(std::string(), val);\n        }\n    }\n\n}\n\nnamespace ESSImport\n{\n\n    void convertSCRI(const SCRI &scri, ESM::Locals &locals)\n    {\n        // order *is* important, as we do not have variable names available in this format\n        storeVariables<short, ESM::VT_Short> (scri.mShorts, locals, scri.mScript);\n        storeVariables<int, ESM::VT_Int> (scri.mLongs, locals, scri.mScript);\n        storeVariables<float, ESM::VT_Float> (scri.mFloats, locals, scri.mScript);\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/convertscri.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONVERTSCRI_H\n#define OPENMW_ESSIMPORT_CONVERTSCRI_H\n\n#include \"importscri.hpp\"\n\n#include <components/esm/locals.hpp>\n\nnamespace ESSImport\n{\n\n    /// Convert script variable assignments\n    void convertSCRI (const SCRI& scri, ESM::Locals& locals);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importacdt.cpp",
    "content": "#include \"importacdt.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\n#include <components/esm/cellref.hpp>\n\nnamespace ESSImport\n{\n\n    void ActorData::load(ESM::ESMReader &esm)\n    {\n        if (esm.isNextSub(\"ACTN\"))\n        {\n            /*\n            Activation flags:\n            ActivationFlag_UseEnabled  = 1\n            ActivationFlag_OnActivate  = 2\n            ActivationFlag_OnDeath  = 10h\n            ActivationFlag_OnKnockout  = 20h\n            ActivationFlag_OnMurder  = 40h\n            ActivationFlag_DoorOpening  = 100h\n            ActivationFlag_DoorClosing  = 200h\n            ActivationFlag_DoorJammedOpening  = 400h\n            ActivationFlag_DoorJammedClosing  = 800h\n            */\n            esm.skipHSub();\n        }\n\n        if (esm.isNextSub(\"STPR\"))\n            esm.skipHSub();\n\n        if (esm.isNextSub(\"MNAM\"))\n           esm.skipHSub();\n\n        bool isDeleted = false;\n        ESM::CellRef::loadData(esm, isDeleted);\n\n        mHasACDT = false;\n        if (esm.isNextSub(\"ACDT\"))\n        {\n            mHasACDT = true;\n            esm.getHT(mACDT);\n        }\n\n        mHasACSC = false;\n        if (esm.isNextSub(\"ACSC\"))\n        {\n            mHasACSC = true;\n            esm.getHT(mACSC);\n        }\n\n        if (esm.isNextSub(\"ACSL\"))\n            esm.skipHSubSize(112);\n\n        if (esm.isNextSub(\"CSTN\"))\n            esm.skipHSub(); // \"PlayerSaveGame\", link to some object?\n\n        if (esm.isNextSub(\"LSTN\"))\n            esm.skipHSub(); // \"PlayerSaveGame\", link to some object?\n\n        // unsure at which point between LSTN and TGTN\n        if (esm.isNextSub(\"CSHN\"))\n            esm.skipHSub(); // \"PlayerSaveGame\", link to some object?\n\n        // unsure if before or after CSTN/LSTN\n        if (esm.isNextSub(\"LSHN\"))\n            esm.skipHSub(); // \"PlayerSaveGame\", link to some object?\n\n        while (esm.isNextSub(\"TGTN\"))\n            esm.skipHSub(); // \"PlayerSaveGame\", link to some object?\n\n        while (esm.isNextSub(\"FGTN\"))\n            esm.getHString(); // fight target?\n\n        // unsure at which point between TGTN and CRED\n        if (esm.isNextSub(\"AADT\"))\n        {\n            // occurred when a creature was in the middle of its attack, 44 bytes\n            esm.skipHSub();\n        }\n\n        // unsure at which point between FGTN and CHRD\n        if (esm.isNextSub(\"PWPC\"))\n            esm.skipHSub();\n        if (esm.isNextSub(\"PWPS\"))\n            esm.skipHSub();\n\n        if (esm.isNextSub(\"WNAM\"))\n        {\n            std::string id = esm.getHString();\n\n            if (esm.isNextSub(\"XNAM\"))\n                mSelectedEnchantItem = esm.getHString();\n            else\n                mSelectedSpell = id;\n\n            if (esm.isNextSub(\"YNAM\"))\n                esm.skipHSub(); // 4 byte, 0\n        }\n\n        while (esm.isNextSub(\"APUD\"))\n        {\n            // used power\n            esm.getSubHeader();\n            std::string id = esm.getString(32);\n            (void)id;\n            // timestamp can't be used: this is the total hours passed, calculated by\n            // timestamp = 24 * (365 * year + cumulativeDays[month] + day)\n            // unfortunately cumulativeDays[month] is not clearly defined,\n            // in the (non-MCP) vanilla version the first month was missing, but MCP added it.\n            double timestamp;\n            esm.getT(timestamp);\n        }\n\n        // FIXME: not all actors have this, add flag\n        if (esm.isNextSub(\"CHRD\")) // npc only\n                esm.getHExact(mSkills, 27*2*sizeof(int));\n\n        if (esm.isNextSub(\"CRED\")) // creature only\n            esm.getHExact(mCombatStats, 3*2*sizeof(int));\n\n        mSCRI.load(esm);\n\n        if (esm.isNextSub(\"ND3D\"))\n            esm.skipHSub();\n\n        mHasANIS = false;\n        if (esm.isNextSub(\"ANIS\"))\n        {\n            mHasANIS = true;\n            esm.getHT(mANIS);\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importacdt.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_ACDT_H\n#define OPENMW_ESSIMPORT_ACDT_H\n\n#include <string>\n\n#include <components/esm/cellref.hpp>\n\n#include \"importscri.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    enum ACDTFlags\n    {\n        TalkedToPlayer = 0x4,\n        Attacked = 0x100,\n        Unknown = 0x200\n    };\n    enum ACSCFlags\n    {\n        Dead = 0x2\n    };\n\n    /// Actor data, shared by (at least) REFR and CellRef\n#pragma pack(push)\n#pragma pack(1)\n    struct ACDT\n    {\n        // Note, not stored at *all*:\n        // - Level changes are lost on reload, except for the player (there it's in the NPC record).\n        unsigned char mUnknown[12];\n        unsigned int mFlags;\n        float mBreathMeter; // Seconds left before drowning\n        unsigned char mUnknown2[20];\n        float mDynamic[3][2];\n        unsigned char mUnknown3[16];\n        float mAttributes[8][2];\n        float mMagicEffects[27]; // Effect attributes: https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes\n        unsigned char mUnknown4[4];\n        unsigned int mGoldPool;\n        unsigned char mCountDown; // seen the same value as in ACSC.mCorpseClearCountdown, maybe\n                                  // this one is for respawning?\n        unsigned char mUnknown5[3];\n    };\n    struct ACSC\n    {\n        unsigned char mUnknown1[17];\n        unsigned char mFlags; // ACSCFlags\n        unsigned char mUnknown2[22];\n        unsigned char mCorpseClearCountdown; // hours?\n        unsigned char mUnknown3[71];\n    };\n    struct ANIS\n    {\n        unsigned char mGroupIndex;\n        unsigned char mUnknown[3];\n        float mTime;\n    };\n#pragma pack(pop)\n\n    struct ActorData : public ESM::CellRef\n    {\n        bool mHasACDT;\n        ACDT mACDT;\n\n        bool mHasACSC;\n        ACSC mACSC;\n\n        int mSkills[27][2]; // skills, base and modified\n\n        // creature combat stats, base and modified\n        // I think these can be ignored in the conversion, because it is not possible\n        // to change them ingame\n        int mCombatStats[3][2];\n\n        std::string mSelectedSpell;\n        std::string mSelectedEnchantItem;\n\n        SCRI mSCRI;\n\n        bool mHasANIS;\n        ANIS mANIS; // scripted animation state\n\n        virtual void load(ESM::ESMReader& esm);\n\n        virtual ~ActorData() = default;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importcellref.cpp",
    "content": "#include \"importcellref.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void CellRef::load(ESM::ESMReader &esm)\n    {\n        blank();\n\n        // (FRMR subrecord name is already read by the loop in ConvertCell)\n        esm.getHT(mRefNum.mIndex); // FRMR\n\n        // this is required since openmw supports more than 255 content files\n        int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24;\n        mRefNum.mContentFile = pluginIndex-1;\n        mRefNum.mIndex &= 0x00ffffff;\n\n        mIndexedRefId = esm.getHNString(\"NAME\");\n\n        ActorData::load(esm);\n        if (esm.isNextSub(\"LVCR\"))\n        {\n            // occurs on levelled creature spawner references\n            // probably some identifier for the creature that has been spawned?\n            unsigned char lvcr;\n            esm.getHT(lvcr);\n            //std::cout << \"LVCR: \" << (int)lvcr << std::endl;\n        }\n\n        mEnabled = true;\n        esm.getHNOT(mEnabled, \"ZNAM\");\n\n        // DATA should occur for all references, except levelled creature spawners\n        // I've seen DATA *twice* on a creature record, and with the exact same content too! weird\n        // alarmvoi0000.ess\n        esm.getHNOT(mPos, \"DATA\", 24);\n        esm.getHNOT(mPos, \"DATA\", 24);\n\n        mDeleted = 0;\n        if (esm.isNextSub(\"DELE\"))\n        {\n            unsigned int deleted;\n            esm.getHT(deleted);\n            mDeleted = ((deleted >> 24) & 0x2) != 0; // the other 3 bytes seem to be uninitialized garbage\n        }\n\n        if (esm.isNextSub(\"MVRF\"))\n        {\n            esm.skipHSub();\n            esm.getSubName();\n            esm.skipHSub();\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importcellref.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CELLREF_H\n#define OPENMW_ESSIMPORT_CELLREF_H\n\n#include <string>\n\n#include <components/esm/cellref.hpp>\n\n#include \"importacdt.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    struct CellRef : public ActorData\n    {\n        std::string mIndexedRefId;\n\n        std::string mScript;\n\n        bool mEnabled;\n\n        bool mDeleted;\n\n        void load(ESM::ESMReader& esm) override;\n\n        virtual ~CellRef() = default;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importcntc.cpp",
    "content": "#include \"importcntc.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void CNTC::load(ESM::ESMReader &esm)\n    {\n        mIndex = 0;\n        esm.getHNT(mIndex, \"INDX\");\n\n        mInventory.load(esm);\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importcntc.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTCNTC_H\n#define OPENMW_ESSIMPORT_IMPORTCNTC_H\n\n#include \"importinventory.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    /// Changed container contents\n    struct CNTC\n    {\n        int mIndex;\n\n        Inventory mInventory;\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n#endif\n"
  },
  {
    "path": "apps/essimporter/importcrec.cpp",
    "content": "#include \"importcrec.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void CREC::load(ESM::ESMReader &esm)\n    {\n        esm.getHNT(mIndex, \"INDX\");\n\n        // equivalent of ESM::Creature XSCL? probably don't have to convert this,\n        // since the value can't be changed\n        float scale;\n        esm.getHNOT(scale, \"XSCL\");\n\n\n        while (esm.isNextSub(\"AI_W\") || esm.isNextSub(\"AI_E\") || esm.isNextSub(\"AI_T\") || esm.isNextSub(\"AI_F\")\n               || esm.isNextSub(\"AI_A\"))\n            mAiPackages.add(esm);\n\n        mInventory.load(esm);\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importcrec.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CREC_H\n#define OPENMW_ESSIMPORT_CREC_H\n\n#include \"importinventory.hpp\"\n#include <components/esm/aipackage.hpp>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    /// Creature changes\n    struct CREC\n    {\n        int mIndex;\n\n        Inventory mInventory;\n        ESM::AIPackageList mAiPackages;\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importdial.cpp",
    "content": "#include \"importdial.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void DIAL::load(ESM::ESMReader &esm)\n    {\n        // See ESM::Dialogue::Type enum, not sure why we would need this here though\n        int type = 0;\n        esm.getHNOT(type, \"DATA\");\n\n        // Deleted dialogue in a savefile. No clue what this means...\n        int deleted = 0;\n        esm.getHNOT(deleted, \"DELE\");\n\n        mIndex = 0;\n        // *should* always occur except when the dialogue is deleted, but leaving it optional just in case...\n        esm.getHNOT(mIndex, \"XIDX\");\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importdial.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTDIAL_H\n#define OPENMW_ESSIMPORT_IMPORTDIAL_H\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    struct DIAL\n    {\n        int mIndex; // Journal index\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importer.cpp",
    "content": "#include \"importer.hpp\"\n\n#include <iomanip>\n\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <osgDB/ReadFile>\n#include <osg/ImageUtils>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/defs.hpp>\n\n#include <components/esm/savedgame.hpp>\n#include <components/esm/player.hpp>\n\n#include <components/esm/loadalch.hpp>\n#include <components/esm/loadspel.hpp>\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadweap.hpp>\n#include <components/esm/loadclot.hpp>\n#include <components/esm/loadench.hpp>\n#include <components/esm/loadlevlist.hpp>\n\n#include <components/misc/constants.hpp>\n\n#include <components/to_utf8/to_utf8.hpp>\n\n#include \"importercontext.hpp\"\n\n#include \"converter.hpp\"\n\nnamespace\n{\n\n    void writeScreenshot(const ESM::Header& fileHeader, ESM::SavedGame& out)\n    {\n        if (fileHeader.mSCRS.size() != 128*128*4)\n        {\n            std::cerr << \"Error: unexpected screenshot size \" << std::endl;\n            return;\n        }\n\n        osg::ref_ptr<osg::Image> image (new osg::Image);\n        image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE);\n\n        // need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise\n        auto it = fileHeader.mSCRS.begin();\n        for (int y=0; y<128; ++y)\n        {\n            for (int x=0; x<128; ++x)\n            {\n                assert(image->data(x,y));\n                *(image->data(x,y)+2) = *it++;\n                *(image->data(x,y)+1) = *it++;\n                *image->data(x,y) = *it++;\n                ++it; // skip alpha\n            }\n        }\n\n        image->flipVertical();\n\n        std::stringstream ostream;\n\n        osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"jpg\");\n        if (!readerwriter)\n        {\n            std::cerr << \"Error: can't write screenshot: no jpg readerwriter found\" << std::endl;\n            return;\n        }\n\n        osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image, ostream);\n        if (!result.success())\n        {\n            std::cerr << \"Error: can't write screenshot: \" << result.message() << \" code \" << result.status() << std::endl;\n            return;\n        }\n\n        std::string data = ostream.str();\n        out.mScreenshot = std::vector<char>(data.begin(), data.end());\n    }\n\n}\n\nnamespace ESSImport\n{\n\n    Importer::Importer(const std::string &essfile, const std::string &outfile, const std::string &encoding)\n        : mEssFile(essfile)\n        , mOutFile(outfile)\n        , mEncoding(encoding)\n    {\n\n    }\n\n    struct File\n    {\n        struct Subrecord\n        {\n            std::string mName;\n            size_t mFileOffset;\n            std::vector<unsigned char> mData;\n        };\n\n        struct Record\n        {\n            std::string mName;\n            size_t mFileOffset;\n            std::vector<Subrecord> mSubrecords;\n        };\n\n        std::vector<Record> mRecords;\n    };\n\n    void read(const std::string& filename, File& file)\n    {\n        ESM::ESMReader esm;\n        esm.open(filename);\n\n        while (esm.hasMoreRecs())\n        {\n            ESM::NAME n = esm.getRecName();\n            esm.getRecHeader();\n\n            File::Record rec;\n            rec.mName = n.toString();\n            rec.mFileOffset = esm.getFileOffset();\n            while (esm.hasMoreSubs())\n            {\n                File::Subrecord sub;\n                esm.getSubName();\n                esm.getSubHeader();\n                sub.mFileOffset = esm.getFileOffset();\n                sub.mName = esm.retSubName().toString();\n                sub.mData.resize(esm.getSubSize());\n                esm.getExact(&sub.mData[0], sub.mData.size());\n                rec.mSubrecords.push_back(sub);\n            }\n            file.mRecords.push_back(rec);\n        }\n    }\n\n    void Importer::compare()\n    {\n        // data that always changes (and/or is already fully decoded) should be blacklisted\n        std::set<std::pair<std::string, std::string> > blacklist;\n        blacklist.insert(std::make_pair(\"GLOB\", \"FLTV\")); // gamehour\n        blacklist.insert(std::make_pair(\"REFR\", \"DATA\")); // player position\n        blacklist.insert(std::make_pair(\"CELL\", \"NAM8\")); // fog of war\n        blacklist.insert(std::make_pair(\"GAME\", \"GMDT\")); // weather data, current time always changes\n        blacklist.insert(std::make_pair(\"CELL\", \"DELE\")); // first 3 bytes are uninitialized\n\n         // this changes way too often, name suggests some renderer internal data?\n        blacklist.insert(std::make_pair(\"CELL\", \"ND3D\"));\n        blacklist.insert(std::make_pair(\"REFR\", \"ND3D\"));\n\n        File file1;\n        read(mEssFile, file1);\n        File file2;\n        read(mOutFile, file2); // todo rename variable\n\n        // FIXME: use max(size1, size2)\n        for (unsigned int i=0; i<file1.mRecords.size(); ++i)\n        {\n            File::Record rec = file1.mRecords[i];\n\n            if (i >= file2.mRecords.size())\n            {\n                std::ios::fmtflags f(std::cout.flags());\n                std::cout << \"Record in file1 not present in file2: (1) 0x\" << std::hex << rec.mFileOffset << std::endl;\n                std::cout.flags(f);\n                return;\n            }\n\n            File::Record rec2 = file2.mRecords[i];\n\n            if (rec.mName != rec2.mName)\n            {\n                std::ios::fmtflags f(std::cout.flags());\n                std::cout << \"Different record name at (2) 0x\" << std::hex << rec2.mFileOffset << std::endl;\n                std::cout.flags(f);\n                return; // TODO: try to recover\n            }\n\n            // FIXME: use max(size1, size2)\n            for (unsigned int j=0; j<rec.mSubrecords.size(); ++j)\n            {\n                File::Subrecord sub = rec.mSubrecords[j];\n\n                if (j >= rec2.mSubrecords.size())\n                {\n                    std::ios::fmtflags f(std::cout.flags());\n                    std::cout << \"Subrecord in file1 not present in file2: (1) 0x\" << std::hex << sub.mFileOffset << std::endl;\n                    std::cout.flags(f);\n                    return;\n                }\n\n                File::Subrecord sub2 = rec2.mSubrecords[j];\n\n                if (sub.mName != sub2.mName)\n                {\n                    std::ios::fmtflags f(std::cout.flags());\n                    std::cout << \"Different subrecord name (\" << rec.mName << \".\" << sub.mName << \" vs. \" << sub2.mName << \") at (1) 0x\" << std::hex << sub.mFileOffset\n                              << \" (2) 0x\" << sub2.mFileOffset << std::endl;\n                    std::cout.flags(f);\n                    break; // TODO: try to recover\n                }\n\n                if (sub.mData != sub2.mData)\n                {\n                    if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end())\n                        continue;\n\n                    std::ios::fmtflags f(std::cout.flags());\n\n                    std::cout << \"Different subrecord data for \" << rec.mName << \".\" << sub.mName << \" at (1) 0x\" << std::hex << sub.mFileOffset\n                              << \" (2) 0x\" << sub2.mFileOffset << std::endl;\n\n                    std::cout << \"Data 1:\" << std::endl;\n                    for (unsigned int k=0; k<sub.mData.size(); ++k)\n                    {\n                        bool different = false;\n                        if (k >= sub2.mData.size() || sub2.mData[k] != sub.mData[k])\n                            different = true;\n\n                        if (different)\n                            std::cout << \"\\033[033m\";\n                        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub.mData[k] << \" \";\n                        if (different)\n                            std::cout << \"\\033[0m\";\n                    }\n                    std::cout << std::endl;\n\n                    std::cout << \"Data 2:\" << std::endl;\n                    for (unsigned int k=0; k<sub2.mData.size(); ++k)\n                    {\n                        bool different = false;\n                        if (k >= sub.mData.size() || sub.mData[k] != sub2.mData[k])\n                            different = true;\n\n                        if (different)\n                            std::cout << \"\\033[033m\";\n                        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub2.mData[k] << \" \";\n                        if (different)\n                            std::cout << \"\\033[0m\";\n                    }\n                    std::cout << std::endl;\n                    std::cout.flags(f);\n                }\n            }\n        }\n    }\n\n    void Importer::run()\n    {\n        ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding));\n        ESM::ESMReader esm;\n        esm.open(mEssFile);\n        esm.setEncoder(&encoder);\n\n        Context context;\n\n        const ESM::Header& header = esm.getHeader();\n        context.mPlayerCellName = header.mGameData.mCurrentCell.toString();\n\n        const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value;\n        const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value;\n        const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value;\n        const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value;\n        const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value;\n        const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value;\n        const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value;\n        const unsigned int recSPLM = ESM::FourCC<'S','P','L','M'>::value;\n\n        std::map<unsigned int, std::shared_ptr<Converter> > converters;\n        converters[ESM::REC_GLOB] = std::shared_ptr<Converter>(new ConvertGlobal());\n        converters[ESM::REC_BOOK] = std::shared_ptr<Converter>(new ConvertBook());\n        converters[ESM::REC_NPC_] = std::shared_ptr<Converter>(new ConvertNPC());\n        converters[ESM::REC_CREA] = std::shared_ptr<Converter>(new ConvertCREA());\n        converters[ESM::REC_NPCC] = std::shared_ptr<Converter>(new ConvertNPCC());\n        converters[ESM::REC_CREC] = std::shared_ptr<Converter>(new ConvertCREC());\n        converters[recREFR      ] = std::shared_ptr<Converter>(new ConvertREFR());\n        converters[recPCDT      ] = std::shared_ptr<Converter>(new ConvertPCDT());\n        converters[recFMAP      ] = std::shared_ptr<Converter>(new ConvertFMAP());\n        converters[recKLST      ] = std::shared_ptr<Converter>(new ConvertKLST());\n        converters[recSTLN      ] = std::shared_ptr<Converter>(new ConvertSTLN());\n        converters[recGAME      ] = std::shared_ptr<Converter>(new ConvertGAME());\n        converters[ESM::REC_CELL] = std::shared_ptr<Converter>(new ConvertCell());\n        converters[ESM::REC_ALCH] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());\n        converters[ESM::REC_CLAS] = std::shared_ptr<Converter>(new ConvertClass());\n        converters[ESM::REC_SPEL] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Spell>());\n        converters[ESM::REC_ARMO] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Armor>());\n        converters[ESM::REC_WEAP] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());\n        converters[ESM::REC_CLOT] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Clothing>());\n        converters[ESM::REC_ENCH] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Enchantment>());\n        converters[ESM::REC_WEAP] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());\n        converters[ESM::REC_LEVC] = std::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>());\n        converters[ESM::REC_LEVI] = std::shared_ptr<Converter>(new DefaultConverter<ESM::ItemLevList>());\n        converters[ESM::REC_CNTC] = std::shared_ptr<Converter>(new ConvertCNTC());\n        converters[ESM::REC_FACT] = std::shared_ptr<Converter>(new ConvertFACT());\n        converters[ESM::REC_INFO] = std::shared_ptr<Converter>(new ConvertINFO());\n        converters[ESM::REC_DIAL] = std::shared_ptr<Converter>(new ConvertDIAL());\n        converters[ESM::REC_QUES] = std::shared_ptr<Converter>(new ConvertQUES());\n        converters[recJOUR      ] = std::shared_ptr<Converter>(new ConvertJOUR());\n        converters[ESM::REC_SCPT] = std::shared_ptr<Converter>(new ConvertSCPT());\n        converters[ESM::REC_PROJ] = std::shared_ptr<Converter>(new ConvertPROJ());\n        converters[recSPLM] = std::shared_ptr<Converter>(new ConvertSPLM());\n\n        // TODO:\n        // - REGN (weather in certain regions?)\n        // - VFXM\n        // - SPLM (active spell effects)\n\n        std::set<unsigned int> unknownRecords;\n\n        for (const auto & converter : converters)\n        {\n            converter.second->setContext(context);\n        }\n\n        while (esm.hasMoreRecs())\n        {\n            ESM::NAME n = esm.getRecName();\n            esm.getRecHeader();\n\n            auto it = converters.find(n.intval);\n            if (it != converters.end())\n            {\n                it->second->read(esm);\n            }\n            else\n            {\n                if (unknownRecords.insert(n.intval).second)\n                {\n                    std::ios::fmtflags f(std::cerr.flags());\n                    std::cerr << \"Error: unknown record \" << n.toString() << \" (0x\" << std::hex << esm.getFileOffset() << \")\" << std::endl;\n                    std::cerr.flags(f);\n                }\n\n                esm.skipRecord();\n            }\n        }\n\n        ESM::ESMWriter writer;\n\n        writer.setFormat (ESM::SavedGame::sCurrentFormat);\n\n        boost::filesystem::ofstream stream(boost::filesystem::path(mOutFile), std::ios::out | std::ios::binary);\n        // all unused\n        writer.setVersion(0);\n        writer.setType(0);\n        writer.setAuthor(\"\");\n        writer.setDescription(\"\");\n        writer.setRecordCount (0);\n\n        for (const auto & master : header.mMaster)\n            writer.addMaster(master.name, 0); // not using the size information anyway -> use value of 0\n\n        writer.save (stream);\n\n        ESM::SavedGame profile;\n        for (const auto & master : header.mMaster)\n        {\n            profile.mContentFiles.push_back(master.name);\n        }\n        profile.mDescription = esm.getDesc();\n        profile.mInGameTime.mDay = context.mDay;\n        profile.mInGameTime.mGameHour = context.mHour;\n        profile.mInGameTime.mMonth = context.mMonth;\n        profile.mInGameTime.mYear = context.mYear;\n        profile.mPlayerCell = header.mGameData.mCurrentCell.toString();\n        if (context.mPlayerBase.mClass == \"NEWCLASSID_CHARGEN\")\n            profile.mPlayerClassName = context.mCustomPlayerClassName;\n        else\n            profile.mPlayerClassId = context.mPlayerBase.mClass;\n        profile.mPlayerLevel = context.mPlayerBase.mNpdt.mLevel;\n        profile.mPlayerName = header.mGameData.mPlayerName.toString();\n\n        writeScreenshot(header, profile);\n\n        writer.startRecord (ESM::REC_SAVE);\n        profile.save (writer);\n        writer.endRecord (ESM::REC_SAVE);\n\n        // Writing order should be Dynamic Store -> Cells -> Player,\n        // so that references to dynamic records can be recognized when loading\n        for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();\n             it != converters.end(); ++it)\n        {\n            if (it->second->getStage() != 0)\n                continue;\n            it->second->write(writer);\n        }\n\n        writer.startRecord(ESM::REC_NPC_);\n        context.mPlayerBase.mId = \"player\";\n        context.mPlayerBase.save(writer);\n        writer.endRecord(ESM::REC_NPC_);\n\n        for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();\n             it != converters.end(); ++it)\n        {\n            if (it->second->getStage() != 1)\n                continue;\n            it->second->write(writer);\n        }\n\n        writer.startRecord(ESM::REC_PLAY);\n        if (context.mPlayer.mCellId.mPaged)\n        {\n            // exterior cell -> determine cell coordinates based on position\n            int cellX = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0] / Constants::CellSizeInUnits));\n            int cellY = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / Constants::CellSizeInUnits));\n            context.mPlayer.mCellId.mIndex.mX = cellX;\n            context.mPlayer.mCellId.mIndex.mY = cellY;\n        }\n        context.mPlayer.save(writer);\n        writer.endRecord(ESM::REC_PLAY);\n\n        writer.startRecord(ESM::REC_ACTC);\n        writer.writeHNT(\"COUN\", context.mNextActorId);\n        writer.endRecord(ESM::REC_ACTC);\n\n        // Stage 2 requires cell references to be written / actors IDs assigned\n        for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();\n             it != converters.end(); ++it)\n        {\n            if (it->second->getStage() != 2)\n                continue;\n            it->second->write(writer);\n        }\n\n        writer.startRecord (ESM::REC_DIAS);\n        context.mDialogueState.save(writer);\n        writer.endRecord(ESM::REC_DIAS);\n\n        writer.startRecord(ESM::REC_INPU);\n        context.mControlsState.save(writer);\n        writer.endRecord(ESM::REC_INPU);\n    }\n\n\n}\n"
  },
  {
    "path": "apps/essimporter/importer.hpp",
    "content": "#ifndef OPENMW_ESSIMPORTER_IMPORTER_H\n#define OPENMW_ESSIMPORTER_IMPORTER_H\n\n#include <string>\n\nnamespace ESSImport\n{\n\n    class Importer\n    {\n    public:\n        Importer(const std::string& essfile, const std::string& outfile, const std::string& encoding);\n\n        void run();\n\n        void compare();\n\n    private:\n        std::string mEssFile;\n        std::string mOutFile;\n        std::string mEncoding;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importercontext.cpp",
    "content": ""
  },
  {
    "path": "apps/essimporter/importercontext.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_CONTEXT_H\n#define OPENMW_ESSIMPORT_CONTEXT_H\n\n#include <map>\n\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/player.hpp>\n#include <components/esm/dialoguestate.hpp>\n#include <components/esm/globalmap.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/controlsstate.hpp>\n\n#include \"importnpcc.hpp\"\n#include \"importcrec.hpp\"\n#include \"importcntc.hpp\"\n#include \"importplayer.hpp\"\n#include \"importsplm.h\"\n\n\n\nnamespace ESSImport\n{\n\n    struct Context\n    {\n        // set from the TES3 header\n        std::string mPlayerCellName;\n\n        ESM::Player mPlayer;\n        ESM::NPC mPlayerBase;\n        std::string mCustomPlayerClassName;\n\n        ESM::DialogueState mDialogueState;\n\n        ESM::ControlsState mControlsState;\n\n        // cells which should show an explored overlay on the global map\n        std::set<std::pair<int, int> > mExploredCells;\n\n        ESM::GlobalMap mGlobalMapState;\n\n        int mDay, mMonth, mYear;\n        float mHour;\n\n        // key <refIndex, refId>\n        std::map<std::pair<int, std::string>, CREC> mCreatureChanges;\n        std::map<std::pair<int, std::string>, NPCC> mNpcChanges;\n        std::map<std::pair<int, std::string>, CNTC> mContainerChanges;\n\n        std::map<std::pair<int, std::string>, int> mActorIdMap;\n        int mNextActorId;\n\n        std::map<std::string, ESM::Creature> mCreatures;\n        std::map<std::string, ESM::NPC> mNpcs;\n\n        std::vector<SPLM::ActiveSpell> mActiveSpells;\n\n        Context()\n            : mDay(0)\n            , mMonth(0)\n            , mYear(0)\n            , mHour(0.f)\n            , mNextActorId(0)\n        {\n            ESM::CellId playerCellId;\n            playerCellId.mPaged = true;\n            playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;\n            mPlayer.mCellId = playerCellId;\n            mPlayer.mLastKnownExteriorPosition[0]\n                = mPlayer.mLastKnownExteriorPosition[1]\n                = mPlayer.mLastKnownExteriorPosition[2]\n                = 0.0f;\n            mPlayer.mHasMark = 0;\n            mPlayer.mCurrentCrimeId = -1; // TODO\n            mPlayer.mPaidCrimeId = -1;\n            mPlayer.mObject.blank();\n            mPlayer.mObject.mEnabled = true;\n            mPlayer.mObject.mRef.mRefID = \"player\"; // REFR.mRefID would be PlayerSaveGame\n            mPlayer.mObject.mCreatureStats.mActorId = generateActorId();\n\n            mGlobalMapState.mBounds.mMinX = 0;\n            mGlobalMapState.mBounds.mMaxX = 0;\n            mGlobalMapState.mBounds.mMinY = 0;\n            mGlobalMapState.mBounds.mMaxY = 0;\n\n            mPlayerBase.blank();\n        }\n\n        int generateActorId()\n        {\n            return mNextActorId++;\n        }\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importgame.cpp",
    "content": "#include \"importgame.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\nvoid GAME::load(ESM::ESMReader &esm)\n{\n    esm.getSubNameIs(\"GMDT\");\n    esm.getSubHeader();\n    if (esm.getSubSize() == 92)\n    {\n        esm.getExact(&mGMDT, 92);\n        mGMDT.mSecundaPhase = 0;\n    }\n    else if (esm.getSubSize() == 96)\n    {\n        esm.getT(mGMDT);\n    }\n    else\n        esm.fail(\"unexpected subrecord size for GAME.GMDT\");\n\n    mGMDT.mWeatherTransition &= (0x000000ff);\n    mGMDT.mSecundaPhase &= (0x000000ff);\n    mGMDT.mMasserPhase &= (0x000000ff);\n}\n\n}\n"
  },
  {
    "path": "apps/essimporter/importgame.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_GAME_H\n#define OPENMW_ESSIMPORT_GAME_H\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    /// Weather data\n    struct GAME\n    {\n        struct GMDT\n        {\n           char mCellName[64] {};\n           int mFogColour {0};\n           float mFogDensity {0.f};\n           int mCurrentWeather {0}, mNextWeather {0};\n           int mWeatherTransition {0}; // 0-100 transition between weathers, top 3 bytes may be garbage\n           float mTimeOfNextTransition {0.f}; // weather changes when gamehour == timeOfNextTransition\n           int mMasserPhase {0}, mSecundaPhase {0}; // top 3 bytes may be garbage\n        };\n\n        GMDT mGMDT;\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importinfo.cpp",
    "content": "#include \"importinfo.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void INFO::load(ESM::ESMReader &esm)\n    {\n        mInfo = esm.getHNString(\"INAM\");\n        mActorRefId = esm.getHNString(\"ACDT\");\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importinfo.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTINFO_H\n#define OPENMW_ESSIMPORT_IMPORTINFO_H\n\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    struct INFO\n    {\n        std::string mInfo;\n        std::string mActorRefId;\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importinventory.cpp",
    "content": "#include \"importinventory.hpp\"\n\n#include <stdexcept>\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void Inventory::load(ESM::ESMReader &esm)\n    {\n        while (esm.isNextSub(\"NPCO\"))\n        {\n            ContItem contItem;\n            esm.getHT(contItem);\n\n            InventoryItem item;\n            item.mId = contItem.mItem.toString();\n            item.mCount = contItem.mCount;\n            item.mRelativeEquipmentSlot = -1;\n            item.mLockLevel = 0;\n            item.mRefNum.unset();\n\n            unsigned int itemCount = std::abs(item.mCount);\n            bool separateStacks = false;\n            for (unsigned int i=0;i<itemCount;++i)\n            {\n                bool newStack = esm.isNextSub(\"XIDX\");\n                if (newStack)\n                {\n                    unsigned int idx;\n                    esm.getHT(idx);\n                    separateStacks = true;\n                    item.mCount = 1;\n                }\n\n                item.mSCRI.load(esm);\n\n                // for XSOL and XCHG seen so far, but probably others too\n                bool isDeleted = false;\n                item.ESM::CellRef::loadData(esm, isDeleted);\n\n                int charge=-1;\n                esm.getHNOT(charge, \"XHLT\");\n                item.mChargeInt = charge;\n\n                if (newStack)\n                    mItems.push_back(item);\n            }\n\n            if (!separateStacks)\n                mItems.push_back(item);\n        }\n\n        // equipped items\n        while (esm.isNextSub(\"WIDX\"))\n        {\n            // note: same item can be equipped 2 items (e.g. 2 rings)\n            // and will be *stacked* in the NPCO list, unlike openmw!\n            // this is currently not handled properly.\n\n            esm.getSubHeader();\n            int itemIndex; // index of the item in the NPCO list\n            esm.getT(itemIndex);\n\n            if (itemIndex < 0 || itemIndex >= int(mItems.size()))\n                esm.fail(\"equipment item index out of range\");\n\n            // appears to be a relative index for only the *possible* slots this item can be equipped in,\n            // i.e. 0 most of the time\n            int slotIndex;\n            esm.getT(slotIndex);\n\n            mItems[itemIndex].mRelativeEquipmentSlot = slotIndex;\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importinventory.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTINVENTORY_H\n#define OPENMW_ESSIMPORT_IMPORTINVENTORY_H\n\n#include <vector>\n#include <string>\n\n#include <components/esm/cellref.hpp>\n#include <components/esm/esmcommon.hpp>\n\n#include \"importscri.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    struct ContItem\n    {\n        int mCount;\n        ESM::NAME32 mItem;\n    };\n\n    struct Inventory\n    {\n        struct InventoryItem : public ESM::CellRef\n        {\n            std::string mId;\n            int mCount;\n            int mRelativeEquipmentSlot;\n            SCRI mSCRI;\n        };\n        std::vector<InventoryItem> mItems;\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importjour.cpp",
    "content": "#include \"importjour.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void JOUR::load(ESM::ESMReader &esm)\n    {\n        mText = esm.getHNString(\"NAME\");\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importjour.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTJOUR_H\n#define OPENMW_ESSIMPORT_IMPORTJOUR_H\n\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    /// Journal\n    struct JOUR\n    {\n        // The entire journal, in HTML\n        std::string mText;\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importklst.cpp",
    "content": "#include \"importklst.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void KLST::load(ESM::ESMReader &esm)\n    {\n        while (esm.isNextSub(\"KNAM\"))\n        {\n            std::string refId = esm.getHString();\n            int count;\n            esm.getHNT(count, \"CNAM\");\n            mKillCounter[refId] = count;\n        }\n\n        mWerewolfKills = 0;\n        esm.getHNOT(mWerewolfKills, \"INTV\");\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importklst.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_KLST_H\n#define OPENMW_ESSIMPORT_KLST_H\n\n#include <string>\n#include <map>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    /// Kill Stats\n    struct KLST\n    {\n        void load(ESM::ESMReader& esm);\n\n        /// RefId, kill count\n        std::map<std::string, int> mKillCounter;\n\n        int mWerewolfKills;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importnpcc.cpp",
    "content": "#include \"importnpcc.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void NPCC::load(ESM::ESMReader &esm)\n    {\n        esm.getHNT(mNPDT, \"NPDT\");\n\n        while (esm.isNextSub(\"AI_W\") || esm.isNextSub(\"AI_E\") || esm.isNextSub(\"AI_T\") || esm.isNextSub(\"AI_F\")\n               || esm.isNextSub(\"AI_A\"))\n            mAiPackages.add(esm);\n\n        mInventory.load(esm);\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importnpcc.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_NPCC_H\n#define OPENMW_ESSIMPORT_NPCC_H\n\n#include <components/esm/loadcont.hpp>\n\n#include <components/esm/aipackage.hpp>\n\n#include \"importinventory.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    struct NPCC\n    {\n        struct NPDT\n        {\n            unsigned char mDisposition;\n            unsigned char unknown;\n            unsigned char mReputation;\n            unsigned char unknown2;\n            int mIndex;\n        } mNPDT;\n\n        Inventory mInventory;\n        ESM::AIPackageList mAiPackages;\n\n        void load(ESM::ESMReader &esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importplayer.cpp",
    "content": "#include \"importplayer.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void REFR::load(ESM::ESMReader &esm)\n    {\n        esm.getHNT(mRefNum.mIndex, \"FRMR\");\n\n        mRefID = esm.getHNString(\"NAME\");\n\n        mActorData.load(esm);\n\n        esm.getHNOT(mPos, \"DATA\", 24);\n    }\n\n    void PCDT::load(ESM::ESMReader &esm)\n    {\n        while (esm.isNextSub(\"DNAM\"))\n        {\n            mKnownDialogueTopics.push_back(esm.getHString());\n        }\n\n        mHasMark = false;\n        if (esm.isNextSub(\"MNAM\"))\n        {\n            mHasMark = true;\n            mMNAM = esm.getHString();\n        }\n\n        esm.getHNT(mPNAM, \"PNAM\");\n\n        if (esm.isNextSub(\"SNAM\"))\n            esm.skipHSub();\n        if (esm.isNextSub(\"NAM9\"))\n            esm.skipHSub();\n\n        // Rest state. You shouldn't even be able to save during rest, but skip just in case.\n        if (esm.isNextSub(\"RNAM\"))\n            /*\n                int hoursLeft;\n                float x, y, z; // resting position\n            */\n            esm.skipHSub(); // 16 bytes\n\n        mBounty = 0;\n        esm.getHNOT(mBounty, \"CNAM\");\n\n        mBirthsign = esm.getHNOString(\"BNAM\");\n\n        // Holds the names of the last used Alchemy apparatus. Don't need to import this ATM,\n        // because our GUI auto-selects the best apparatus.\n        if (esm.isNextSub(\"NAM0\"))\n            esm.skipHSub();\n        if (esm.isNextSub(\"NAM1\"))\n            esm.skipHSub();\n        if (esm.isNextSub(\"NAM2\"))\n            esm.skipHSub();\n        if (esm.isNextSub(\"NAM3\"))\n            esm.skipHSub();\n\n        mHasENAM = false;\n        if (esm.isNextSub(\"ENAM\"))\n        {\n            mHasENAM = true;\n            esm.getHT(mENAM);\n        }\n\n        if (esm.isNextSub(\"LNAM\"))\n            esm.skipHSub();\n\n        while (esm.isNextSub(\"FNAM\"))\n        {\n            FNAM fnam;\n            esm.getHT(fnam);\n            mFactions.push_back(fnam);\n        }\n\n        mHasAADT = false;\n        if (esm.isNextSub(\"AADT\")) // Attack animation data?\n        {\n            mHasAADT = true;\n            esm.getHT(mAADT);\n        }\n\n        if (esm.isNextSub(\"KNAM\"))\n            esm.skipHSub(); // assigned Quick Keys, I think\n\n        if (esm.isNextSub(\"ANIS\"))\n            esm.skipHSub(); // 16 bytes\n\n        if (esm.isNextSub(\"WERE\"))\n        {\n            // some werewolf data, 152 bytes\n            // maybe current skills and attributes for werewolf form\n            esm.getSubHeader();\n            esm.skip(152);\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importplayer.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_PLAYER_H\n#define OPENMW_ESSIMPORT_PLAYER_H\n\n#include <vector>\n#include <string>\n\n#include <components/esm/defs.hpp>\n#include <components/esm/cellref.hpp>\n#include <components/esm/esmcommon.hpp>\n\n#include \"importacdt.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n/// Player-agnostic player data\nstruct REFR\n{\n    ActorData mActorData;\n\n    std::string mRefID;\n    ESM::Position mPos;\n    ESM::RefNum mRefNum;\n\n    void load(ESM::ESMReader& esm);\n};\n\n/// Other player data\nstruct PCDT\n{\n    int mBounty;\n    std::string mBirthsign;\n\n    std::vector<std::string> mKnownDialogueTopics;\n\n    enum PlayerFlags\n    {\n        PlayerFlags_ViewSwitchDisabled = 0x1,\n        PlayerFlags_ControlsDisabled = 0x4,\n        PlayerFlags_Sleeping = 0x10,\n        PlayerFlags_Waiting = 0x40,\n        PlayerFlags_WeaponDrawn = 0x80,\n        PlayerFlags_SpellDrawn = 0x100,\n        PlayerFlags_InJail = 0x200,\n        PlayerFlags_JumpingDisabled = 0x1000,\n        PlayerFlags_LookingDisabled = 0x2000,\n        PlayerFlags_VanityModeDisabled = 0x4000,\n        PlayerFlags_WeaponDrawingDisabled = 0x8000,\n        PlayerFlags_SpellDrawingDisabled = 0x10000,\n        PlayerFlags_ThirdPerson = 0x20000,\n        PlayerFlags_TeleportingDisabled = 0x40000,\n        PlayerFlags_LevitationDisabled = 0x80000\n    };\n\n#pragma pack(push)\n#pragma pack(1)\n    struct FNAM\n    {\n        unsigned char mRank;\n        unsigned char mUnknown1[3];\n        int mReputation;\n        unsigned char mFlags; // 0x1: unknown, 0x2: expelled\n        unsigned char mUnknown2[3];\n        ESM::NAME32 mFactionName;\n    };\n\n    struct PNAM\n    {\n        struct MarkLocation\n        {\n            float mX, mY, mZ; // worldspace position\n            float mRotZ; // Z angle in radians\n            int mCellX, mCellY; // grid coordinates; for interior cells this is always (0, 0)\n        };\n\n        int mPlayerFlags; // controls, camera and draw state\n        unsigned int mLevelProgress;\n        float mSkillProgress[27]; // skill progress, non-uniform scaled\n        unsigned char mSkillIncreases[8]; // number of skill increases for each attribute\n        int mTelekinesisRangeBonus; // in units; seems redundant\n        float mVisionBonus; // range: <0.0, 1.0>; affected by light spells and Get/Mod/SetPCVisionBonus\n        int mDetectKeyMagnitude; // seems redundant\n        int mDetectEnchantmentMagnitude; // seems redundant\n        int mDetectAnimalMagnitude; // seems redundant\n        MarkLocation mMarkLocation;\n        unsigned char mUnknown3[40];\n        unsigned char mSpecIncreases[3]; // number of skill increases for each specialization\n        unsigned char mUnknown4;\n    };\n\n    struct ENAM\n    {\n        int mCellX;\n        int mCellY;\n    };\n\n    struct AADT // 44 bytes\n    {\n        int animGroupIndex; // See convertANIS() for the mapping.\n        unsigned char mUnknown5[40];\n    };\n#pragma pack(pop)\n\n    std::vector<FNAM> mFactions;\n    PNAM mPNAM;\n\n    bool mHasMark;\n    std::string mMNAM; // mark cell name; can also be sDefaultCellname or region name\n\n    bool mHasENAM;\n    ENAM mENAM; // last exterior cell\n\n    bool mHasAADT;\n    AADT mAADT;\n\n    void load(ESM::ESMReader& esm);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importproj.cpp",
    "content": "#include \"importproj.h\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\nvoid ESSImport::PROJ::load(ESM::ESMReader& esm)\n{\n    while (esm.isNextSub(\"PNAM\"))\n    {\n        PNAM pnam;\n        esm.getHT(pnam);\n        mProjectiles.push_back(pnam);\n    }\n}\n\n}\n"
  },
  {
    "path": "apps/essimporter/importproj.h",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTPROJ_H\n#define OPENMW_ESSIMPORT_IMPORTPROJ_H\n\n#include <vector>\n#include <components/esm/esmcommon.hpp>\n#include <components/esm/util.hpp>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\nstruct PROJ\n{\n\n#pragma pack(push)\n#pragma pack(1)\n    struct PNAM // 184 bytes\n    {\n        float mAttackStrength;\n        float mSpeed;\n        unsigned char mUnknown[4*2];\n        float mFlightTime;\n        int mSplmIndex; // reference to a SPLM record (0 for ballistic projectiles)\n        unsigned char mUnknown2[4];\n        ESM::Vector3 mVelocity;\n        ESM::Vector3 mPosition;\n        unsigned char mUnknown3[4*9];\n        ESM::NAME32 mActorId; // indexed refID (with the exception of \"PlayerSaveGame\")\n        ESM::NAME32 mArrowId;\n        ESM::NAME32 mBowId;\n\n        bool isMagic() const { return mSplmIndex != 0; }\n    };\n#pragma pack(pop)\n\n    std::vector<PNAM> mProjectiles;\n\n    void load(ESM::ESMReader& esm);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importques.cpp",
    "content": "#include \"importques.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void QUES::load(ESM::ESMReader &esm)\n    {\n        while (esm.isNextSub(\"DATA\"))\n            mInfo.push_back(esm.getHString());\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importques.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTQUES_H\n#define OPENMW_ESSIMPORT_IMPORTQUES_H\n\n#include <string>\n#include <vector>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    /// State for a quest\n    /// Presumably this record only exists when Tribunal is installed,\n    /// since pre-Tribunal there weren't any quest names in the data files.\n    struct QUES\n    {\n        std::string mName; // NAME, should be assigned from outside as usual\n        std::vector<std::string> mInfo; // list of journal entries for the quest\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importscpt.cpp",
    "content": "#include \"importscpt.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\n\n\nnamespace ESSImport\n{\n\n    void SCPT::load(ESM::ESMReader &esm)\n    {\n        esm.getHNT(mSCHD, \"SCHD\");\n\n        mSCRI.load(esm);\n\n        mRefNum = -1;\n        if (esm.isNextSub(\"RNAM\"))\n        {\n            mRunning = true;\n            esm.getHT(mRefNum);\n        }\n        else\n            mRunning = false;\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importscpt.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTSCPT_H\n#define OPENMW_ESSIMPORT_IMPORTSCPT_H\n\n#include \"importscri.hpp\"\n\n#include <components/esm/loadscpt.hpp>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    struct SCHD\n    {\n        ESM::NAME32              mName;\n        ESM::Script::SCHDstruct  mData;\n    };\n\n    // A running global script\n    struct SCPT\n    {\n        SCHD mSCHD;\n\n        // values of local variables\n        SCRI mSCRI;\n\n        bool mRunning;\n        int mRefNum; // Targeted reference, -1: no reference\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importscri.cpp",
    "content": "#include \"importscri.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\n    void SCRI::load(ESM::ESMReader &esm)\n    {\n        mScript = esm.getHNOString(\"SCRI\");\n\n        int numShorts = 0, numLongs = 0, numFloats = 0;\n        if (esm.isNextSub(\"SLCS\"))\n        {\n            esm.getSubHeader();\n            esm.getT(numShorts);\n            esm.getT(numLongs);\n            esm.getT(numFloats);\n        }\n\n        if (esm.isNextSub(\"SLSD\"))\n        {\n            esm.getSubHeader();\n            for (int i=0; i<numShorts; ++i)\n            {\n                short val;\n                esm.getT(val);\n                mShorts.push_back(val);\n            }\n        }\n        // I haven't seen Longs in a save file yet, but SLLD would make sense for the name\n        // TODO: test this\n        if (esm.isNextSub(\"SLLD\"))\n        {\n            esm.getSubHeader();\n            for (int i=0; i<numLongs; ++i)\n            {\n                int val;\n                esm.getT(val);\n                mLongs.push_back(val);\n            }\n        }\n        if (esm.isNextSub(\"SLFD\"))\n        {\n            esm.getSubHeader();\n            for (int i=0; i<numFloats; ++i)\n            {\n                float val;\n                esm.getT(val);\n                mFloats.push_back(val);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/essimporter/importscri.hpp",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTSCRI_H\n#define OPENMW_ESSIMPORT_IMPORTSCRI_H\n\n#include <components/esm/variant.hpp>\n\n#include <vector>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\n    /// Local variable assignments for a running script\n    struct SCRI\n    {\n        std::string mScript;\n\n        std::vector<short> mShorts;\n        std::vector<int> mLongs;\n        std::vector<float> mFloats;\n\n        void load(ESM::ESMReader& esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/importsplm.cpp",
    "content": "#include \"importsplm.h\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESSImport\n{\n\nvoid SPLM::load(ESM::ESMReader& esm)\n{\n    while (esm.isNextSub(\"NAME\"))\n    {\n        ActiveSpell spell;\n        esm.getHT(spell.mIndex);\n        esm.getHNT(spell.mSPDT, \"SPDT\");\n        spell.mTarget = esm.getHNOString(\"TNAM\");\n\n        while (esm.isNextSub(\"NPDT\"))\n        {\n            ActiveEffect effect;\n            esm.getHT(effect.mNPDT);\n\n            // Effect-specific subrecords can follow:\n            // - INAM for disintegration and bound effects\n            // - CNAM for summoning and command effects\n            // - VNAM for vampirism\n            // NOTE: There can be multiple INAMs per effect.\n            // TODO: Needs more research.\n\n            esm.skipHSubUntil(\"NAM0\"); // sentinel\n            esm.getSubName();\n            esm.skipHSub();\n\n            spell.mActiveEffects.push_back(effect);\n        }\n\n        unsigned char xnam; // sentinel\n        esm.getHNT(xnam, \"XNAM\");\n\n        mActiveSpells.push_back(spell);\n    }\n}\n\n}\n"
  },
  {
    "path": "apps/essimporter/importsplm.h",
    "content": "#ifndef OPENMW_ESSIMPORT_IMPORTSPLM_H\n#define OPENMW_ESSIMPORT_IMPORTSPLM_H\n\n#include <vector>\n#include <components/esm/esmcommon.hpp>\n#include <components/esm/util.hpp>\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace ESSImport\n{\n\nstruct SPLM\n{\n\n#pragma pack(push)\n#pragma pack(1)\n    struct SPDT // 160 bytes\n    {\n        int mType; // 1 = spell, 2 = enchantment, 3 = potion\n        ESM::NAME32 mId; // base ID of a spell/enchantment/potion\n        unsigned char mUnknown[4*4];\n        ESM::NAME32 mCasterId;\n        ESM::NAME32 mSourceId; // empty for spells\n        unsigned char mUnknown2[4*11];\n    };\n\n    struct NPDT // 56 bytes\n    {\n        ESM::NAME32 mAffectedActorId;\n        unsigned char mUnknown[4*2];\n        int mMagnitude;\n        float mSecondsActive;\n        unsigned char mUnknown2[4*2];\n    };\n\n    struct INAM // 40 bytes\n    {\n        int mUnknown;\n        unsigned char mUnknown2;\n        ESM::FIXED_STRING<35> mItemId; // disintegrated item / bound item / item to re-equip after expiration\n    };\n\n    struct CNAM // 36 bytes\n    {\n        int mUnknown; // seems to always be 0\n        ESM::NAME32 mSummonedOrCommandedActor[32];\n    };\n\n    struct VNAM // 4 bytes\n    {\n        int mUnknown;\n    };\n\n\n#pragma pack(pop)\n\n    struct ActiveEffect\n    {\n        NPDT mNPDT;\n    };\n\n    struct ActiveSpell\n    {\n        int mIndex;\n        SPDT mSPDT;\n        std::string mTarget;\n        std::vector<ActiveEffect> mActiveEffects;\n    };\n\n    std::vector<ActiveSpell> mActiveSpells;\n\n    void load(ESM::ESMReader& esm);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/essimporter/main.cpp",
    "content": "#include <iostream>\n\n#include <boost/program_options.hpp>\n#include <boost/filesystem.hpp>\n\n#include <components/files/configurationmanager.hpp>\n\n#include \"importer.hpp\"\n\nnamespace bpo = boost::program_options;\nnamespace bfs = boost::filesystem;\n\n\n\nint main(int argc, char** argv)\n{\n    try\n    {\n        bpo::options_description desc(\"Syntax: openmw-essimporter <options> infile.ess outfile.omwsave\\nAllowed options\");\n        bpo::positional_options_description p_desc;\n        desc.add_options()\n            (\"help,h\", \"produce help message\")\n            (\"mwsave,m\", bpo::value<std::string>(), \"morrowind .ess save file\")\n            (\"output,o\", bpo::value<std::string>(), \"output file (.omwsave)\")\n            (\"compare,c\", \"compare two .ess files\")\n            (\"encoding\", boost::program_options::value<std::string>()->default_value(\"win1252\"), \"encoding of the save file\")\n        ;\n        p_desc.add(\"mwsave\", 1).add(\"output\", 1);\n\n        bpo::variables_map variables;\n\n        bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)\n            .options(desc)\n            .positional(p_desc)\n            .run();\n\n        bpo::store(parsed, variables);\n\n        if(variables.count(\"help\") || !variables.count(\"mwsave\") || !variables.count(\"output\")) {\n            std::cout << desc;\n            return 0;\n        }\n\n        bpo::notify(variables);\n\n        Files::ConfigurationManager cfgManager(true);\n        cfgManager.readConfiguration(variables, desc);\n\n        std::string essFile = variables[\"mwsave\"].as<std::string>();\n        std::string outputFile = variables[\"output\"].as<std::string>();\n        std::string encoding = variables[\"encoding\"].as<std::string>();\n\n        ESSImport::Importer importer(essFile, outputFile, encoding);\n\n        if (variables.count(\"compare\"))\n            importer.compare();\n        else\n        {\n            const std::string& ext = \".omwsave\";\n            if (bfs::exists(bfs::path(outputFile))\n                    && (outputFile.size() < ext.size() || outputFile.substr(outputFile.size()-ext.size()) != ext))\n            {\n                throw std::runtime_error(\"Output file already exists and does not end in .omwsave. Did you mean to use --compare?\");\n            }\n            importer.run();\n        }\n    }\n    catch (std::exception& e)\n    {\n        std::cerr << \"Error: \" << e.what() << std::endl;\n        return 1;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "apps/launcher/CMakeLists.txt",
    "content": "set(LAUNCHER\n    datafilespage.cpp\n    graphicspage.cpp\n    sdlinit.cpp\n    main.cpp\n    maindialog.cpp\n    playpage.cpp\n    textslotmsgbox.cpp\n    settingspage.cpp\n    advancedpage.cpp\n\n    utils/cellnameloader.cpp\n    utils/profilescombobox.cpp\n    utils/textinputdialog.cpp\n    utils/lineedit.cpp\n    utils/openalutil.cpp\n\n    ${CMAKE_SOURCE_DIR}/files/windows/launcher.rc\n)\n\nset(LAUNCHER_HEADER\n    datafilespage.hpp\n    graphicspage.hpp\n    sdlinit.hpp\n    maindialog.hpp\n    playpage.hpp\n    textslotmsgbox.hpp\n    settingspage.hpp\n    advancedpage.hpp\n\n    utils/cellnameloader.hpp\n    utils/profilescombobox.hpp\n    utils/textinputdialog.hpp\n    utils/lineedit.hpp\n    utils/openalutil.hpp\n)\n\n# Headers that must be pre-processed\nset(LAUNCHER_HEADER_MOC\n    datafilespage.hpp\n    graphicspage.hpp\n    maindialog.hpp\n    playpage.hpp\n    textslotmsgbox.hpp\n    settingspage.hpp\n    advancedpage.hpp\n\n    utils/cellnameloader.hpp\n    utils/textinputdialog.hpp\n    utils/profilescombobox.hpp\n    utils/lineedit.hpp\n    utils/openalutil.hpp\n\n)\n\nset(LAUNCHER_UI\n    ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/playpage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/advancedpage.ui\n)\n\nsource_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})\n\nset(QT_USE_QTGUI 1)\n\n# Set some platform specific settings\nif(WIN32)\n    set(GUI_TYPE WIN32)\n    set(QT_USE_QTMAIN TRUE)\nendif(WIN32)\n\nQT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)\nQT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})\nQT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI})\n\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR})\nif(NOT WIN32)\n    include_directories(${LIBUNSHIELD_INCLUDE_DIR})\nendif(NOT WIN32)\n\n# Main executable\nopenmw_add_executable(openmw-launcher\n    ${GUI_TYPE}\n    ${LAUNCHER}\n    ${LAUNCHER_HEADER}\n    ${RCC_SRCS}\n    ${MOC_SRCS}\n    ${UI_HDRS}\n)\n\nif (WIN32)\n    INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION \".\")\nendif (WIN32)\n\ntarget_link_libraries(openmw-launcher\n    ${SDL2_LIBRARY_ONLY}\n    ${OPENAL_LIBRARY}\n    components\n)\n\ntarget_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core)\n\nif (BUILD_WITH_CODE_COVERAGE)\n  add_definitions (--coverage)\n  target_link_libraries(openmw-launcher gcov)\nendif()\n\n\n"
  },
  {
    "path": "apps/launcher/advancedpage.cpp",
    "content": "#include \"advancedpage.hpp\"\n\n#include <array>\n\n#include <components/config/gamesettings.hpp>\n#include <QFileDialog>\n#include <QCompleter>\n#include <QString>\n#include <components/contentselector/view/contentselector.hpp>\n#include <components/contentselector/model/esmfile.hpp>\n\n#include <cmath>\n\n#include \"utils/openalutil.hpp\"\n\nLauncher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, QWidget *parent)\n        : QWidget(parent)\n        , mGameSettings(gameSettings)\n{\n    setObjectName (\"AdvancedPage\");\n    setupUi(this);\n\n    for(const char * name : Launcher::enumerateOpenALDevices())\n    {\n        audioDeviceSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name));\n    }\n    for(const char * name : Launcher::enumerateOpenALDevicesHrtf())\n    {\n        hrtfProfileSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name));\n    }\n\n    loadSettings();\n\n    mCellNameCompleter.setModel(&mCellNameCompleterModel);\n    startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);\n}\n\nvoid Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) {\n    // Update the list of suggestions for the \"Start default character at\" field\n    mCellNameCompleterModel.setStringList(cellNames);\n    mCellNameCompleter.setCompletionMode(QCompleter::PopupCompletion);\n    mCellNameCompleter.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);\n}\n\nvoid Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) {\n    startDefaultCharacterAtLabel->setEnabled(state == Qt::Checked);\n    startDefaultCharacterAtField->setEnabled(state == Qt::Checked);\n}\n\nvoid Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()\n{\n    QString scriptFile = QFileDialog::getOpenFileName(\n            this,\n            QObject::tr(\"Select script file\"),\n            QDir::currentPath(),\n            QString(tr(\"Text file (*.txt)\")));\n\n    if (scriptFile.isEmpty())\n        return;\n\n    QFileInfo info(scriptFile);\n\n    if (!info.exists() || !info.isReadable())\n        return;\n\n    const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));\n    runScriptAfterStartupField->setText(path);\n}\n\nnamespace\n{\n    constexpr double CellSizeInUnits = 8192;\n\n    double convertToCells(double unitRadius)\n    {\n        return std::round((unitRadius + 1024) / CellSizeInUnits);\n    }\n\n    double convertToUnits(double CellGridRadius)\n    {\n        return CellSizeInUnits * CellGridRadius - 1024;\n    }\n}\n\nbool Launcher::AdvancedPage::loadSettings()\n{\n    // Game mechanics\n    {\n        loadSettingBool(toggleSneakCheckBox, \"toggle sneak\", \"Input\");\n        loadSettingBool(canLootDuringDeathAnimationCheckBox, \"can loot during death animation\", \"Game\");\n        loadSettingBool(followersAttackOnSightCheckBox, \"followers attack on sight\", \"Game\");\n        loadSettingBool(rebalanceSoulGemValuesCheckBox, \"rebalance soul gem values\", \"Game\");\n        loadSettingBool(enchantedWeaponsMagicalCheckBox, \"enchanted weapons are magical\", \"Game\");\n        loadSettingBool(permanentBarterDispositionChangeCheckBox, \"barter disposition change is permanent\", \"Game\");\n        loadSettingBool(classicReflectedAbsorbSpellsCheckBox, \"classic reflected absorb spells behavior\", \"Game\");\n        loadSettingBool(requireAppropriateAmmunitionCheckBox, \"only appropriate ammunition bypasses resistance\", \"Game\");\n        loadSettingBool(uncappedDamageFatigueCheckBox, \"uncapped damage fatigue\", \"Game\");\n        loadSettingBool(normaliseRaceSpeedCheckBox, \"normalise race speed\", \"Game\");\n        loadSettingBool(swimUpwardCorrectionCheckBox, \"swim upward correction\", \"Game\");\n        loadSettingBool(avoidCollisionsCheckBox, \"NPCs avoid collisions\", \"Game\");\n        int unarmedFactorsStrengthIndex = Settings::Manager::getInt(\"strength influences hand to hand\", \"Game\");\n        if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)\n            unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);\n        loadSettingBool(stealingFromKnockedOutCheckBox, \"always allow stealing from knocked out actors\", \"Game\");\n        loadSettingBool(enableNavigatorCheckBox, \"enable\", \"Navigator\");\n        int numPhysicsThreads = Settings::Manager::getInt(\"async num threads\", \"Physics\");\n        if (numPhysicsThreads >= 0)\n            physicsThreadsSpinBox->setValue(numPhysicsThreads);\n        loadSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, \"allow actors to follow over water surface\", \"Game\");\n    }\n\n    // Visuals\n    {\n        loadSettingBool(autoUseObjectNormalMapsCheckBox, \"auto use object normal maps\", \"Shaders\");\n        loadSettingBool(autoUseObjectSpecularMapsCheckBox, \"auto use object specular maps\", \"Shaders\");\n        loadSettingBool(autoUseTerrainNormalMapsCheckBox, \"auto use terrain normal maps\", \"Shaders\");\n        loadSettingBool(autoUseTerrainSpecularMapsCheckBox, \"auto use terrain specular maps\", \"Shaders\");\n        loadSettingBool(bumpMapLocalLightingCheckBox, \"apply lighting to environment maps\", \"Shaders\");\n        loadSettingBool(radialFogCheckBox, \"radial fog\", \"Shaders\");\n        loadSettingBool(magicItemAnimationsCheckBox, \"use magic item animations\", \"Game\");\n        connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool)));\n        loadSettingBool(animSourcesCheckBox, \"use additional anim sources\", \"Game\");\n        if (animSourcesCheckBox->checkState() != Qt::Unchecked)\n        {\n            loadSettingBool(weaponSheathingCheckBox, \"weapon sheathing\", \"Game\");\n            loadSettingBool(shieldSheathingCheckBox, \"shield sheathing\", \"Game\");\n        }\n        loadSettingBool(turnToMovementDirectionCheckBox, \"turn to movement direction\", \"Game\");\n        loadSettingBool(smoothMovementCheckBox, \"smooth movement\", \"Game\");\n\n        const bool distantTerrain = Settings::Manager::getBool(\"distant terrain\", \"Terrain\");\n        const bool objectPaging = Settings::Manager::getBool(\"object paging\", \"Terrain\");\n        if (distantTerrain && objectPaging) {\n            distantLandCheckBox->setCheckState(Qt::Checked);\n        }\n\n        loadSettingBool(activeGridObjectPagingCheckBox, \"object paging active grid\", \"Terrain\");\n        viewingDistanceComboBox->setValue(convertToCells(Settings::Manager::getInt(\"viewing distance\", \"Camera\")));\n        objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble(\"object paging min size\", \"Terrain\"));\n    }\n\n    // Audio\n    {\n        std::string selectedAudioDevice = Settings::Manager::getString(\"device\", \"Sound\");\n        if (selectedAudioDevice.empty() == false)\n        {\n            int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice));\n            if (audioDeviceIndex != -1)\n            {\n                audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex);\n            }\n        }\n        int hrtfEnabledIndex = Settings::Manager::getInt(\"hrtf enable\", \"Sound\");\n        if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1)\n        {\n            enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1);\n        }\n        std::string selectedHRTFProfile = Settings::Manager::getString(\"hrtf\", \"Sound\");\n        if (selectedHRTFProfile.empty() == false)\n        {\n            int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile));\n            if (hrtfProfileIndex != -1)\n            {\n                hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);\n            }\n        }\n    }\n\n\n    // Camera\n    {\n        loadSettingBool(viewOverShoulderCheckBox, \"view over shoulder\", \"Camera\");\n        connect(viewOverShoulderCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotViewOverShoulderToggled(bool)));\n        viewOverShoulderVerticalLayout->setEnabled(viewOverShoulderCheckBox->checkState());\n        loadSettingBool(autoSwitchShoulderCheckBox, \"auto switch shoulder\", \"Camera\");\n        loadSettingBool(previewIfStandStillCheckBox, \"preview if stand still\", \"Camera\");\n        loadSettingBool(deferredPreviewRotationCheckBox, \"deferred preview rotation\", \"Camera\");\n        loadSettingBool(headBobbingCheckBox, \"head bobbing\", \"Camera\");\n        defaultShoulderComboBox->setCurrentIndex(\n            Settings::Manager::getVector2(\"view over shoulder offset\", \"Camera\").x() >= 0 ? 0 : 1);\n    }\n\n    // Interface Changes\n    {\n        loadSettingBool(showEffectDurationCheckBox, \"show effect duration\", \"Game\");\n        loadSettingBool(showEnchantChanceCheckBox, \"show enchant chance\", \"Game\");\n        loadSettingBool(showMeleeInfoCheckBox, \"show melee info\", \"Game\");\n        loadSettingBool(showProjectileDamageCheckBox, \"show projectile damage\", \"Game\");\n        loadSettingBool(changeDialogTopicsCheckBox, \"color topic enable\", \"GUI\");\n        int showOwnedIndex = Settings::Manager::getInt(\"show owned\", \"Game\");\n        // Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.\n        if (showOwnedIndex >= 0 && showOwnedIndex <= 3)\n            showOwnedComboBox->setCurrentIndex(showOwnedIndex);\n        loadSettingBool(stretchBackgroundCheckBox, \"stretch menu background\", \"GUI\");\n        loadSettingBool(graphicHerbalismCheckBox, \"graphic herbalism\", \"Game\");\n        scalingSpinBox->setValue(Settings::Manager::getFloat(\"scaling factor\", \"GUI\"));\n    }\n\n    // Bug fixes\n    {\n        loadSettingBool(preventMerchantEquippingCheckBox, \"prevent merchant equipping\", \"Game\");\n        loadSettingBool(trainersTrainingSkillsBasedOnBaseSkillCheckBox, \"trainers training skills based on base skill\", \"Game\");\n    }\n\n    // Miscellaneous\n    {\n        // Saves\n        loadSettingBool(timePlayedCheckbox, \"timeplayed\", \"Saves\");\n        maximumQuicksavesComboBox->setValue(Settings::Manager::getInt(\"max quicksaves\", \"Saves\"));\n\n        // Other Settings\n        QString screenshotFormatString = QString::fromStdString(Settings::Manager::getString(\"screenshot format\", \"General\")).toUpper();\n        if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)\n            screenshotFormatComboBox->addItem(screenshotFormatString);\n        screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString));\n    }\n\n    // Testing\n    {\n        loadSettingBool(grabCursorCheckBox, \"grab cursor\", \"Input\");\n\n        bool skipMenu = mGameSettings.value(\"skip-menu\").toInt() == 1;\n        if (skipMenu)\n        {\n            skipMenuCheckBox->setCheckState(Qt::Checked);\n        }\n        startDefaultCharacterAtLabel->setEnabled(skipMenu);\n        startDefaultCharacterAtField->setEnabled(skipMenu);\n\n        startDefaultCharacterAtField->setText(mGameSettings.value(\"start\"));\n        runScriptAfterStartupField->setText(mGameSettings.value(\"script-run\"));\n    }\n    return true;\n}\n\nvoid Launcher::AdvancedPage::saveSettings()\n{\n    // Game mechanics\n    {\n        saveSettingBool(toggleSneakCheckBox, \"toggle sneak\", \"Input\");\n        saveSettingBool(canLootDuringDeathAnimationCheckBox, \"can loot during death animation\", \"Game\");\n        saveSettingBool(followersAttackOnSightCheckBox, \"followers attack on sight\", \"Game\");\n        saveSettingBool(rebalanceSoulGemValuesCheckBox, \"rebalance soul gem values\", \"Game\");\n        saveSettingBool(enchantedWeaponsMagicalCheckBox, \"enchanted weapons are magical\", \"Game\");\n        saveSettingBool(permanentBarterDispositionChangeCheckBox, \"barter disposition change is permanent\", \"Game\");\n        saveSettingBool(classicReflectedAbsorbSpellsCheckBox, \"classic reflected absorb spells behavior\", \"Game\");\n        saveSettingBool(requireAppropriateAmmunitionCheckBox, \"only appropriate ammunition bypasses resistance\", \"Game\");\n        saveSettingBool(uncappedDamageFatigueCheckBox, \"uncapped damage fatigue\", \"Game\");\n        saveSettingBool(normaliseRaceSpeedCheckBox, \"normalise race speed\", \"Game\");\n        saveSettingBool(swimUpwardCorrectionCheckBox, \"swim upward correction\", \"Game\");\n        saveSettingBool(avoidCollisionsCheckBox, \"NPCs avoid collisions\", \"Game\");\n        int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex();\n        if (unarmedFactorsStrengthIndex != Settings::Manager::getInt(\"strength influences hand to hand\", \"Game\"))\n            Settings::Manager::setInt(\"strength influences hand to hand\", \"Game\", unarmedFactorsStrengthIndex);\n        saveSettingBool(stealingFromKnockedOutCheckBox, \"always allow stealing from knocked out actors\", \"Game\");\n        saveSettingBool(enableNavigatorCheckBox, \"enable\", \"Navigator\");\n        int numPhysicsThreads = physicsThreadsSpinBox->value();\n        if (numPhysicsThreads != Settings::Manager::getInt(\"async num threads\", \"Physics\"))\n            Settings::Manager::setInt(\"async num threads\", \"Physics\", numPhysicsThreads);\n    }\n\n    // Visuals\n    {\n        saveSettingBool(autoUseObjectNormalMapsCheckBox, \"auto use object normal maps\", \"Shaders\");\n        saveSettingBool(autoUseObjectSpecularMapsCheckBox, \"auto use object specular maps\", \"Shaders\");\n        saveSettingBool(autoUseTerrainNormalMapsCheckBox, \"auto use terrain normal maps\", \"Shaders\");\n        saveSettingBool(autoUseTerrainSpecularMapsCheckBox, \"auto use terrain specular maps\", \"Shaders\");\n        saveSettingBool(bumpMapLocalLightingCheckBox, \"apply lighting to environment maps\", \"Shaders\");\n        saveSettingBool(radialFogCheckBox, \"radial fog\", \"Shaders\");\n        saveSettingBool(magicItemAnimationsCheckBox, \"use magic item animations\", \"Game\");\n        saveSettingBool(animSourcesCheckBox, \"use additional anim sources\", \"Game\");\n        saveSettingBool(weaponSheathingCheckBox, \"weapon sheathing\", \"Game\");\n        saveSettingBool(shieldSheathingCheckBox, \"shield sheathing\", \"Game\");\n        saveSettingBool(turnToMovementDirectionCheckBox, \"turn to movement direction\", \"Game\");\n        saveSettingBool(smoothMovementCheckBox, \"smooth movement\", \"Game\");\n\n        const bool distantTerrain = Settings::Manager::getBool(\"distant terrain\", \"Terrain\");\n        const bool objectPaging = Settings::Manager::getBool(\"object paging\", \"Terrain\");\n        const bool wantDistantLand = distantLandCheckBox->checkState();\n        if (wantDistantLand != (distantTerrain && objectPaging)) {\n            Settings::Manager::setBool(\"distant terrain\", \"Terrain\", wantDistantLand);\n            Settings::Manager::setBool(\"object paging\", \"Terrain\", wantDistantLand);\n        }\n\n        saveSettingBool(activeGridObjectPagingCheckBox, \"object paging active grid\", \"Terrain\");\n        double viewingDistance = viewingDistanceComboBox->value();\n        if (viewingDistance != convertToCells(Settings::Manager::getInt(\"viewing distance\", \"Camera\")))\n        {\n            Settings::Manager::setInt(\"viewing distance\", \"Camera\", convertToUnits(viewingDistance));\n        }\n        double objectPagingMinSize = objectPagingMinSizeComboBox->value();\n        if (objectPagingMinSize != Settings::Manager::getDouble(\"object paging min size\", \"Terrain\"))\n            Settings::Manager::setDouble(\"object paging min size\", \"Terrain\", objectPagingMinSize);\n    }\n    \n    // Audio\n    {\n        int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex();\n        if (audioDeviceIndex != 0)\n        {\n            Settings::Manager::setString(\"device\", \"Sound\", audioDeviceSelectorComboBox->currentText().toUtf8().constData());\n        } \n        else \n        {\n            Settings::Manager::setString(\"device\", \"Sound\", \"\");\n        }\n        int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1;\n        if (hrtfEnabledIndex != Settings::Manager::getInt(\"hrtf enable\", \"Sound\"))\n        {\n            Settings::Manager::setInt(\"hrtf enable\", \"Sound\", hrtfEnabledIndex);\n        }\n        int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex();\n        if (selectedHRTFProfileIndex != 0)\n        {\n            Settings::Manager::setString(\"hrtf\", \"Sound\", hrtfProfileSelectorComboBox->currentText().toUtf8().constData());\n        }\n        else \n        {\n            Settings::Manager::setString(\"hrtf\", \"Sound\", \"\");\n        }\n    }\n\n    // Camera\n    {\n        saveSettingBool(viewOverShoulderCheckBox, \"view over shoulder\", \"Camera\");\n        saveSettingBool(autoSwitchShoulderCheckBox, \"auto switch shoulder\", \"Camera\");\n        saveSettingBool(previewIfStandStillCheckBox, \"preview if stand still\", \"Camera\");\n        saveSettingBool(deferredPreviewRotationCheckBox, \"deferred preview rotation\", \"Camera\");\n        saveSettingBool(headBobbingCheckBox, \"head bobbing\", \"Camera\");\n\n        osg::Vec2f shoulderOffset = Settings::Manager::getVector2(\"view over shoulder offset\", \"Camera\");\n        if (defaultShoulderComboBox->currentIndex() != (shoulderOffset.x() >= 0 ? 0 : 1))\n        {\n            if (defaultShoulderComboBox->currentIndex() == 0)\n                shoulderOffset.x() = std::abs(shoulderOffset.x());\n            else\n                shoulderOffset.x() = -std::abs(shoulderOffset.x());\n            Settings::Manager::setVector2(\"view over shoulder offset\", \"Camera\", shoulderOffset);\n        }\n    }\n\n    // Interface Changes\n    {\n        saveSettingBool(showEffectDurationCheckBox, \"show effect duration\", \"Game\");\n        saveSettingBool(showEnchantChanceCheckBox, \"show enchant chance\", \"Game\");\n        saveSettingBool(showMeleeInfoCheckBox, \"show melee info\", \"Game\");\n        saveSettingBool(showProjectileDamageCheckBox, \"show projectile damage\", \"Game\");\n        saveSettingBool(changeDialogTopicsCheckBox, \"color topic enable\", \"GUI\");\n        int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();\n        if (showOwnedCurrentIndex != Settings::Manager::getInt(\"show owned\", \"Game\"))\n            Settings::Manager::setInt(\"show owned\", \"Game\", showOwnedCurrentIndex);\n        saveSettingBool(stretchBackgroundCheckBox, \"stretch menu background\", \"GUI\");\n        saveSettingBool(graphicHerbalismCheckBox, \"graphic herbalism\", \"Game\");\n        float uiScalingFactor = scalingSpinBox->value();\n        if (uiScalingFactor != Settings::Manager::getFloat(\"scaling factor\", \"GUI\"))\n            Settings::Manager::setFloat(\"scaling factor\", \"GUI\", uiScalingFactor);\n    }\n\n    // Bug fixes\n    {\n        saveSettingBool(preventMerchantEquippingCheckBox, \"prevent merchant equipping\", \"Game\");\n        saveSettingBool(trainersTrainingSkillsBasedOnBaseSkillCheckBox, \"trainers training skills based on base skill\", \"Game\");\n    }\n\n    // Miscellaneous\n    {\n        // Saves Settings\n        saveSettingBool(timePlayedCheckbox, \"timeplayed\", \"Saves\");\n        int maximumQuicksaves = maximumQuicksavesComboBox->value();\n        if (maximumQuicksaves != Settings::Manager::getInt(\"max quicksaves\", \"Saves\"))\n        {\n            Settings::Manager::setInt(\"max quicksaves\", \"Saves\", maximumQuicksaves);\n        }\n\n        // Other Settings\n        std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();\n        if (screenshotFormatString != Settings::Manager::getString(\"screenshot format\", \"General\"))\n            Settings::Manager::setString(\"screenshot format\", \"General\", screenshotFormatString);\n    }\n\n    // Testing\n    {\n        saveSettingBool(grabCursorCheckBox, \"grab cursor\", \"Input\");\n\n        int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked;\n        if (skipMenu != mGameSettings.value(\"skip-menu\").toInt())\n            mGameSettings.setValue(\"skip-menu\", QString::number(skipMenu));\n\n        QString startCell = startDefaultCharacterAtField->text();\n        if (startCell != mGameSettings.value(\"start\"))\n        {\n            mGameSettings.setValue(\"start\", startCell);\n        }\n        QString scriptRun = runScriptAfterStartupField->text();\n        if (scriptRun != mGameSettings.value(\"script-run\"))\n            mGameSettings.setValue(\"script-run\", scriptRun);\n    }\n}\n\nvoid Launcher::AdvancedPage::loadSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group)\n{\n    if (Settings::Manager::getBool(setting, group))\n        checkbox->setCheckState(Qt::Checked);\n}\n\nvoid Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group)\n{\n    bool cValue = checkbox->checkState();\n    if (cValue != Settings::Manager::getBool(setting, group))\n        Settings::Manager::setBool(setting, group, cValue);\n}\n\nvoid Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)\n{\n    loadCellsForAutocomplete(cellNames);\n}\n\nvoid Launcher::AdvancedPage::slotAnimSourcesToggled(bool checked)\n{\n    weaponSheathingCheckBox->setEnabled(checked);\n    shieldSheathingCheckBox->setEnabled(checked);\n    if (!checked)\n    {\n        weaponSheathingCheckBox->setCheckState(Qt::Unchecked);\n        shieldSheathingCheckBox->setCheckState(Qt::Unchecked);\n    }\n}\n\nvoid Launcher::AdvancedPage::slotViewOverShoulderToggled(bool checked)\n{\n    viewOverShoulderVerticalLayout->setEnabled(viewOverShoulderCheckBox->checkState());\n}\n"
  },
  {
    "path": "apps/launcher/advancedpage.hpp",
    "content": "#ifndef ADVANCEDPAGE_H\n#define ADVANCEDPAGE_H\n\n#include <QCompleter>\n#include <QStringListModel>\n\n#include \"ui_advancedpage.h\"\n\n#include <components/settings/settings.hpp>\n\nnamespace Config { class GameSettings; }\n\nnamespace Launcher\n{\n    class AdvancedPage : public QWidget, private Ui::AdvancedPage\n    {\n        Q_OBJECT\n\n    public:\n        explicit AdvancedPage(Config::GameSettings &gameSettings, QWidget *parent = nullptr);\n\n        bool loadSettings();\n        void saveSettings();\n\n    public slots:\n        void slotLoadedCellsChanged(QStringList cellNames);\n\n    private slots:\n        void on_skipMenuCheckBox_stateChanged(int state);\n        void on_runScriptAfterStartupBrowseButton_clicked();\n        void slotAnimSourcesToggled(bool checked);\n        void slotViewOverShoulderToggled(bool checked);\n\n    private:\n        Config::GameSettings &mGameSettings;\n        QCompleter mCellNameCompleter;\n        QStringListModel mCellNameCompleterModel;\n\n        /**\n         * Load the cells associated with the given content files for use in autocomplete\n         * @param filePaths the file paths of the content files to be examined\n         */\n        void loadCellsForAutocomplete(QStringList filePaths);\n        void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);\n        void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/launcher/datafilespage.cpp",
    "content": "#include \"datafilespage.hpp\"\n\n#include <QDebug>\n\n#include <QPushButton>\n#include <QMessageBox>\n#include <QMenu>\n#include <QSortFilterProxyModel>\n#include <thread>\n#include <mutex>\n\n#include <apps/launcher/utils/cellnameloader.hpp>\n#include <components/files/configurationmanager.hpp>\n\n#include <components/contentselector/model/esmfile.hpp>\n#include <components/contentselector/view/contentselector.hpp>\n\n#include <components/config/gamesettings.hpp>\n#include <components/config/launchersettings.hpp>\n#include <iostream>\n\n#include \"utils/textinputdialog.hpp\"\n#include \"utils/profilescombobox.hpp\"\n\n\nconst char *Launcher::DataFilesPage::mDefaultContentListName = \"Default\";\n\nLauncher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)\n    : QWidget(parent)\n    , mCfgMgr(cfg)\n    , mGameSettings(gameSettings)\n    , mLauncherSettings(launcherSettings)\n{\n    ui.setupUi (this);\n    setObjectName (\"DataFilesPage\");\n    mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);\n    const QString encoding = mGameSettings.value(\"encoding\", \"win1252\");\n    mSelector->setEncoding(encoding);\n\n    mNewProfileDialog = new TextInputDialog(tr(\"New Content List\"), tr(\"Content List name:\"), this);\n    mCloneProfileDialog = new TextInputDialog(tr(\"Clone Content List\"), tr(\"Content List name:\"), this);\n\n    connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)),\n            this, SLOT(updateNewProfileOkButton(QString)));\n    connect(mCloneProfileDialog->lineEdit(), SIGNAL(textChanged(QString)),\n            this, SLOT(updateCloneProfileOkButton(QString)));\n\n    buildView();\n    loadSettings();\n\n    // Connect signal and slot after the settings have been loaded. We only care about the user changing\n    // the addons and don't want to get signals of the system doing it during startup.\n    connect(mSelector, SIGNAL(signalAddonDataChanged(QModelIndex,QModelIndex)),\n            this, SLOT(slotAddonDataChanged()));\n    // Call manually to indicate all changes to addon data during startup.\n    slotAddonDataChanged();\n}\n\nvoid Launcher::DataFilesPage::buildView()\n{\n    ui.verticalLayout->insertWidget (0, mSelector->uiWidget());\n\n    QToolButton * refreshButton = mSelector->refreshButton();    \n\n    //tool buttons\n    ui.newProfileButton->setToolTip (\"Create a new Content List\");\n    ui.cloneProfileButton->setToolTip (\"Clone the current Content List\");\n    ui.deleteProfileButton->setToolTip (\"Delete an existing Content List\");\n    refreshButton->setToolTip(\"Refresh Data Files\");\n\n    //combo box\n    ui.profilesComboBox->addItem(mDefaultContentListName);\n    ui.profilesComboBox->setPlaceholderText (QString(\"Select a Content List...\"));\n    ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(QLatin1String(mDefaultContentListName)));\n\n    // Add the actions to the toolbuttons\n    ui.newProfileButton->setDefaultAction (ui.newProfileAction);\n    ui.cloneProfileButton->setDefaultAction (ui.cloneProfileAction);\n    ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction);\n    refreshButton->setDefaultAction(ui.refreshDataFilesAction);\n\n    //establish connections\n    connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)),\n             this, SLOT (slotProfileChanged(int)));\n\n    connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString, QString)),\n             this, SLOT (slotProfileRenamed(QString, QString)));\n\n    connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)),\n             this, SLOT (slotProfileChangedByUser(QString, QString)));\n\n    connect(ui.refreshDataFilesAction, SIGNAL(triggered()),this, SLOT(slotRefreshButtonClicked()));\n}\n\nbool Launcher::DataFilesPage::loadSettings()\n{\n    QStringList profiles = mLauncherSettings.getContentLists();\n    QString currentProfile = mLauncherSettings.getCurrentContentListName();\n\n    qDebug() << \"The current profile is: \" << currentProfile;\n\n    for (const QString &item : profiles)\n        addProfile (item, false);\n\n    // Hack: also add the current profile\n    if (!currentProfile.isEmpty())\n        addProfile(currentProfile, true);\n\n    return true;\n}\n\nvoid Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)\n{\n    QStringList paths = mGameSettings.getDataDirs();\n\n    mDataLocal = mGameSettings.getDataLocal();\n\n    if (!mDataLocal.isEmpty())\n        paths.insert(0, mDataLocal);\n\n    mSelector->clearFiles();\n\n    for (const QString &path : paths)\n        mSelector->addFiles(path);\n\n    PathIterator pathIterator(paths);\n\n    mSelector->setProfileContent(filesInProfile(contentModelName, pathIterator));\n}\n\nQStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName, PathIterator& pathIterator)\n{\n    QStringList files = mLauncherSettings.getContentListFiles(profileName);\n    QStringList filepaths;\n\n    for (const QString& file : files)\n    {\n        QString filepath = pathIterator.findFirstPath(file);\n\n        if (!filepath.isEmpty())\n            filepaths << filepath;\n    }\n\n    return filepaths;\n}\n\nvoid Launcher::DataFilesPage::saveSettings(const QString &profile)\n{\n   QString profileName = profile;\n\n   if (profileName.isEmpty())\n       profileName = ui.profilesComboBox->currentText();\n\n   //retrieve the files selected for the profile\n   ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();\n\n    //set the value of the current profile (not necessarily the profile being saved!)\n    mLauncherSettings.setCurrentContentListName(ui.profilesComboBox->currentText());\n\n    QStringList fileNames;\n    for (const ContentSelectorModel::EsmFile *item : items)\n    {\n        fileNames.append(item->fileName());\n    }\n    mLauncherSettings.setContentList(profileName, fileNames);\n    mGameSettings.setContentList(fileNames);\n}\n\nQStringList Launcher::DataFilesPage::selectedFilePaths()\n{\n    //retrieve the files selected for the profile\n    ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();\n    QStringList filePaths;\n    for (const ContentSelectorModel::EsmFile *item : items)\n    {\n        QFile file(item->filePath());\n        \n        if(file.exists())\n        {\n            filePaths.append(item->filePath());\n        }\n        else\n        {\n            slotRefreshButtonClicked();\n        }\n    }\n    return filePaths;\n}\n\nvoid Launcher::DataFilesPage::removeProfile(const QString &profile)\n{\n    mLauncherSettings.removeContentList(profile);\n}\n\nQAbstractItemModel *Launcher::DataFilesPage::profilesModel() const\n{\n    return ui.profilesComboBox->model();\n}\n\nint Launcher::DataFilesPage::profilesIndex() const\n{\n    return ui.profilesComboBox->currentIndex();\n}\n\nvoid Launcher::DataFilesPage::setProfile(int index, bool savePrevious)\n{\n    if (index >= -1 && index < ui.profilesComboBox->count())\n    {\n        QString previous = mPreviousProfile;\n        QString current = ui.profilesComboBox->itemText(index);\n\n        mPreviousProfile = current;\n\n        setProfile (previous, current, savePrevious);\n    }\n}\n\nvoid Launcher::DataFilesPage::setProfile (const QString &previous, const QString &current, bool savePrevious)\n{\n    //abort if no change (poss. duplicate signal)\n    if (previous == current)\n            return;\n\n    if (!previous.isEmpty() && savePrevious)\n        saveSettings (previous);\n\n    ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current));\n\n    populateFileViews(current);\n\n    checkForDefaultProfile();\n}\n\nvoid Launcher::DataFilesPage::slotProfileDeleted (const QString &item)\n{\n    removeProfile (item);\n}\n\nvoid Launcher::DataFilesPage:: refreshDataFilesView ()\n{\n    QString currentProfile = ui.profilesComboBox->currentText();\n    saveSettings(currentProfile);\n    populateFileViews(currentProfile);\n}\n\nvoid Launcher::DataFilesPage::slotRefreshButtonClicked ()\n{\n    refreshDataFilesView();\n}\n\nvoid Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString &current)\n{\n    setProfile(previous, current, true);\n    emit signalProfileChanged (ui.profilesComboBox->findText(current));\n}\n\nvoid Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString &current)\n{\n    if (previous.isEmpty())\n        return;\n\n    // Save the new profile name\n    saveSettings();\n\n    // Remove the old one\n    removeProfile (previous);\n\n    loadSettings();\n}\n\nvoid Launcher::DataFilesPage::slotProfileChanged(int index)\n{\n    // in case the event was triggered externally\n    if (ui.profilesComboBox->currentIndex() != index)\n        ui.profilesComboBox->setCurrentIndex(index);\n\n    setProfile (index, true);\n}\n\nvoid Launcher::DataFilesPage::on_newProfileAction_triggered()\n{\n    if (mNewProfileDialog->exec() != QDialog::Accepted)\n        return;\n\n    QString profile = mNewProfileDialog->lineEdit()->text();\n\n    if (profile.isEmpty())\n        return;\n\n    saveSettings();\n\n    mLauncherSettings.setCurrentContentListName(profile);\n\n    addProfile(profile, true);\n}\n\nvoid Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurrent)\n{\n    if (profile.isEmpty())\n        return;\n\n    if (ui.profilesComboBox->findText (profile) == -1)\n        ui.profilesComboBox->addItem (profile);\n\n    if (setAsCurrent)\n        setProfile (ui.profilesComboBox->findText (profile), false);\n}\n\nvoid Launcher::DataFilesPage::on_cloneProfileAction_triggered()\n{\n    if (mCloneProfileDialog->exec() != QDialog::Accepted)\n        return;\n\n    QString profile = mCloneProfileDialog->lineEdit()->text();\n\n    if (profile.isEmpty())\n        return;\n\n    mLauncherSettings.setContentList(profile, selectedFilePaths());\n    addProfile(profile, true);\n}\n\nvoid Launcher::DataFilesPage::on_deleteProfileAction_triggered()\n{\n    QString profile = ui.profilesComboBox->currentText();\n\n    if (profile.isEmpty())\n        return;\n\n    if (!showDeleteMessageBox (profile))\n        return;\n\n    // this should work since the Default profile can't be deleted and is always index 0\n    int next = ui.profilesComboBox->currentIndex()-1;\n\n    // changing the profile forces a reload of plugin file views.\n    ui.profilesComboBox->setCurrentIndex(next);\n\n    removeProfile(profile);\n    ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile));\n\n    checkForDefaultProfile();\n}\n\nvoid Launcher::DataFilesPage::updateNewProfileOkButton(const QString &text)\n{\n    // We do this here because we need the profiles combobox text\n    mNewProfileDialog->setOkButtonEnabled(!text.isEmpty() && ui.profilesComboBox->findText(text) == -1);\n}\n\nvoid Launcher::DataFilesPage::updateCloneProfileOkButton(const QString &text)\n{\n    // We do this here because we need the profiles combobox text\n    mCloneProfileDialog->setOkButtonEnabled(!text.isEmpty() && ui.profilesComboBox->findText(text) == -1);\n}\n\nvoid Launcher::DataFilesPage::checkForDefaultProfile()\n{\n    //don't allow deleting \"Default\" profile\n    bool success = (ui.profilesComboBox->currentText() != mDefaultContentListName);\n\n    ui.deleteProfileAction->setEnabled (success);\n    ui.profilesComboBox->setEditEnabled (success);\n}\n\nbool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text)\n{\n    QMessageBox msgBox(this);\n    msgBox.setWindowTitle(tr(\"Delete Content List\"));\n    msgBox.setIcon(QMessageBox::Warning);\n    msgBox.setStandardButtons(QMessageBox::Cancel);\n    msgBox.setText(tr(\"Are you sure you want to delete <b>%1</b>?\").arg(text));\n\n    QAbstractButton *deleteButton =\n    msgBox.addButton(tr(\"Delete\"), QMessageBox::ActionRole);\n\n    msgBox.exec();\n\n    return (msgBox.clickedButton() == deleteButton);\n}\n\nvoid Launcher::DataFilesPage::slotAddonDataChanged()\n{\n    QStringList selectedFiles = selectedFilePaths();\n    if (previousSelectedFiles != selectedFiles) {\n        previousSelectedFiles = selectedFiles;\n        // Loading cells for core Morrowind + Expansions takes about 0.2 seconds, which is enough to cause a\n        // barely perceptible UI lag. Splitting into its own thread to alleviate that.\n        std::thread loadCellsThread(&DataFilesPage::reloadCells, this, selectedFiles);\n        loadCellsThread.detach();\n    }\n}\n\n// Mutex lock to run reloadCells synchronously.\nstd::mutex _reloadCellsMutex;\n\nvoid Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)\n{\n    // Use a mutex lock so that we can prevent two threads from executing the rest of this code at the same time\n    // Based on https://stackoverflow.com/a/5429695/531762\n    std::unique_lock<std::mutex> lock(_reloadCellsMutex);\n\n    // The following code will run only if there is not another thread currently running it\n    CellNameLoader cellNameLoader;\n#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)\n    QSet<QString> set = cellNameLoader.getCellNames(selectedFiles);\n    QStringList cellNamesList(set.begin(), set.end());\n#else\n    QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles));\n#endif\n    std::sort(cellNamesList.begin(), cellNamesList.end());\n    emit signalLoadedCellsChanged(cellNamesList);\n}\n"
  },
  {
    "path": "apps/launcher/datafilespage.hpp",
    "content": "#ifndef DATAFILESPAGE_H\n#define DATAFILESPAGE_H\n\n#include \"ui_datafilespage.h\"\n#include <QWidget>\n\n\n#include <QDir>\n#include <QStringList>\n\nclass QSortFilterProxyModel;\nclass QAbstractItemModel;\nclass QMenu;\n\nnamespace Files { struct ConfigurationManager; }\nnamespace ContentSelectorView { class ContentSelector; }\nnamespace Config { class GameSettings;\n                   class LauncherSettings; }\n\nnamespace Launcher\n{\n    class TextInputDialog;\n    class ProfilesComboBox;\n\n    class DataFilesPage : public QWidget\n    {\n        Q_OBJECT\n\n        ContentSelectorView::ContentSelector *mSelector;\n        Ui::DataFilesPage ui;\n\n    public:\n        explicit DataFilesPage (Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,\n                                Config::LauncherSettings &launcherSettings, QWidget *parent = nullptr);\n\n        QAbstractItemModel* profilesModel() const;\n\n        int profilesIndex() const;\n\n        //void writeConfig(QString profile = QString());\n        void saveSettings(const QString &profile = \"\");\n        bool loadSettings();\n\n        /**\n         * Returns the file paths of all selected content files\n         * @return the file paths of all selected content files\n         */\n        QStringList selectedFilePaths();\n\n    signals:\n        void signalProfileChanged (int index);\n        void signalLoadedCellsChanged(QStringList selectedFiles);\n\n    public slots:\n        void slotProfileChanged (int index);\n\n    private slots:\n\n        void slotProfileChangedByUser(const QString &previous, const QString &current);\n        void slotProfileRenamed(const QString &previous, const QString &current);\n        void slotProfileDeleted(const QString &item);\n        void slotAddonDataChanged ();\n        void slotRefreshButtonClicked ();\n\n        void updateNewProfileOkButton(const QString &text);\n        void updateCloneProfileOkButton(const QString &text);\n\n        void on_newProfileAction_triggered();\n        void on_cloneProfileAction_triggered();\n        void on_deleteProfileAction_triggered();\n\n    public:\n        /// Content List that is always present\n        const static char *mDefaultContentListName;\n\n    private:\n\n        TextInputDialog *mNewProfileDialog;\n        TextInputDialog *mCloneProfileDialog;\n\n        Files::ConfigurationManager &mCfgMgr;\n\n        Config::GameSettings &mGameSettings;\n        Config::LauncherSettings &mLauncherSettings;\n\n        QString mPreviousProfile;\n        QStringList previousSelectedFiles;\n        QString mDataLocal;\n\n        void buildView();\n        void setProfile (int index, bool savePrevious);\n        void setProfile (const QString &previous, const QString &current, bool savePrevious);\n        void removeProfile (const QString &profile);\n        bool showDeleteMessageBox (const QString &text);\n        void addProfile (const QString &profile, bool setAsCurrent);\n        void checkForDefaultProfile();\n        void populateFileViews(const QString& contentModelName);\n        void reloadCells(QStringList selectedFiles);\n        void refreshDataFilesView ();\n\n        class PathIterator\n        {\n            QStringList::ConstIterator mCitEnd;\n            QStringList::ConstIterator mCitCurrent;\n            QStringList::ConstIterator mCitBegin;\n            QString mFile;\n            QString mFilePath;\n\n        public:\n            PathIterator (const QStringList &list)\n            {\n                mCitBegin = list.constBegin();\n                mCitCurrent = mCitBegin;\n                mCitEnd = list.constEnd();\n            }\n\n            QString findFirstPath (const QString &file)\n            {\n                mCitCurrent = mCitBegin;\n                mFile = file;\n                return path();\n            }\n\n            QString findNextPath () { return path(); }\n\n        private:\n\n            QString path ()\n            {\n                bool success = false;\n                QDir dir;\n                QFileInfo file;\n\n                while (!success)\n                {\n                    if (mCitCurrent == mCitEnd)\n                        break;\n\n                    dir.setPath (*(mCitCurrent++));\n                    file.setFile (dir.absoluteFilePath (mFile));\n\n                    success = file.exists();\n                }\n\n                if (success)\n                    return file.absoluteFilePath();\n\n                return \"\";\n            }\n\n        };\n\n        QStringList filesInProfile(const QString& profileName, PathIterator& pathIterator);\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/launcher/graphicspage.cpp",
    "content": "#include \"graphicspage.hpp\"\n\n#include <QDesktopWidget>\n#include <QMessageBox>\n#include <QDir>\n#include <QScreen>\n\n#ifdef MAC_OS_X_VERSION_MIN_REQUIRED\n#undef MAC_OS_X_VERSION_MIN_REQUIRED\n// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154\n#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__\n#endif // MAC_OS_X_VERSION_MIN_REQUIRED\n\n#include <SDL_video.h>\n\n#include <numeric>\n\n#include <components/files/configurationmanager.hpp>\n\nQString getAspect(int x, int y)\n{\n    int gcd = std::gcd (x, y);\n    if (gcd == 0)\n        return QString();\n\n    int xaspect = x / gcd;\n    int yaspect = y / gcd;\n    // special case: 8 : 5 is usually referred to as 16:10\n    if (xaspect == 8 && yaspect == 5)\n        return QString(\"16:10\");\n\n    return QString(QString::number(xaspect) + \":\" + QString::number(yaspect));\n}\n\nLauncher::GraphicsPage::GraphicsPage(QWidget *parent)\n    : QWidget(parent)\n{\n    setObjectName (\"GraphicsPage\");\n    setupUi(this);\n\n    // Set the maximum res we can set in windowed mode\n    QRect res = getMaximumResolution();\n    customWidthSpinBox->setMaximum(res.width());\n    customHeightSpinBox->setMaximum(res.height());\n\n    connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int)));\n    connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool)));\n    connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int)));\n    connect(framerateLimitCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotFramerateLimitToggled(bool)));\n    connect(shadowDistanceCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotShadowDistLimitToggled(bool)));\n\n}\n\nbool Launcher::GraphicsPage::setupSDL()\n{\n    bool sdlConnectSuccessful = initSDL();\n    if (!sdlConnectSuccessful)\n    {\n        return false;\n    }\n\n    int displays = SDL_GetNumVideoDisplays();\n\n    if (displays < 0)\n    {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error receiving number of screens\"));\n        msgBox.setIcon(QMessageBox::Critical);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<br><b>SDL_GetNumVideoDisplays failed:</b><br><br>\") + QString::fromUtf8(SDL_GetError()) + \"<br>\");\n        msgBox.exec();\n        return false;\n    }\n\n    screenComboBox->clear();\n    mResolutionsPerScreen.clear();\n    for (int i = 0; i < displays; i++)\n    {\n        mResolutionsPerScreen.append(getAvailableResolutions(i));\n        screenComboBox->addItem(QString(tr(\"Screen \")) + QString::number(i + 1));\n    }\n    screenChanged(0);\n\n    // Disconnect from SDL processes\n    quitSDL();\n\n    return true;\n}\n\nbool Launcher::GraphicsPage::loadSettings()\n{\n    if (!setupSDL())\n        return false;\n\n    // Visuals\n    if (Settings::Manager::getBool(\"vsync\", \"Video\"))\n        vSyncCheckBox->setCheckState(Qt::Checked);\n\n    if (Settings::Manager::getBool(\"fullscreen\", \"Video\"))\n        fullScreenCheckBox->setCheckState(Qt::Checked);\n\n    if (Settings::Manager::getBool(\"window border\", \"Video\"))\n        windowBorderCheckBox->setCheckState(Qt::Checked);\n\n    // aaValue is the actual value (0, 1, 2, 4, 8, 16)\n    int aaValue = Settings::Manager::getInt(\"antialiasing\", \"Video\");\n    // aaIndex is the index into the allowed values in the pull down.\n    int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue));\n    if (aaIndex != -1)\n        antiAliasingComboBox->setCurrentIndex(aaIndex);\n\n    int width = Settings::Manager::getInt(\"resolution x\", \"Video\");\n    int height = Settings::Manager::getInt(\"resolution y\", \"Video\");\n    QString resolution = QString::number(width) + QString(\" x \") + QString::number(height);\n    screenComboBox->setCurrentIndex(Settings::Manager::getInt(\"screen\", \"Video\"));\n\n    int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);\n\n    if (resIndex != -1) {\n        standardRadioButton->toggle();\n        resolutionComboBox->setCurrentIndex(resIndex);\n    } else {\n        customRadioButton->toggle();\n        customWidthSpinBox->setValue(width);\n        customHeightSpinBox->setValue(height);\n    }\n\n    float fpsLimit = Settings::Manager::getFloat(\"framerate limit\", \"Video\");\n    if (fpsLimit != 0)\n    {\n        framerateLimitCheckBox->setCheckState(Qt::Checked);\n        framerateLimitSpinBox->setValue(fpsLimit);\n    }\n\n    // Lighting\n    int lightingMethod = 1;\n    if (Settings::Manager::getString(\"lighting method\", \"Shaders\") == \"legacy\")\n        lightingMethod = 0;\n    else if (Settings::Manager::getString(\"lighting method\", \"Shaders\") == \"shaders\")\n        lightingMethod = 2;\n    lightingMethodComboBox->setCurrentIndex(lightingMethod);\n\n    // Shadows\n    if (Settings::Manager::getBool(\"actor shadows\", \"Shadows\"))\n        actorShadowsCheckBox->setCheckState(Qt::Checked);\n    if (Settings::Manager::getBool(\"player shadows\", \"Shadows\"))\n        playerShadowsCheckBox->setCheckState(Qt::Checked);\n    if (Settings::Manager::getBool(\"terrain shadows\", \"Shadows\"))\n        terrainShadowsCheckBox->setCheckState(Qt::Checked);\n    if (Settings::Manager::getBool(\"object shadows\", \"Shadows\"))\n        objectShadowsCheckBox->setCheckState(Qt::Checked);\n    if (Settings::Manager::getBool(\"enable indoor shadows\", \"Shadows\"))\n        indoorShadowsCheckBox->setCheckState(Qt::Checked);\n\n    shadowComputeSceneBoundsComboBox->setCurrentIndex(\n        shadowComputeSceneBoundsComboBox->findText(\n            QString(tr(Settings::Manager::getString(\"compute scene bounds\", \"Shadows\").c_str()))));\n\n    int shadowDistLimit = Settings::Manager::getInt(\"maximum shadow map distance\", \"Shadows\");\n    if (shadowDistLimit > 0)\n    {\n        shadowDistanceCheckBox->setCheckState(Qt::Checked);\n        shadowDistanceSpinBox->setValue(shadowDistLimit);\n    }\n\n    float shadowFadeStart = Settings::Manager::getFloat(\"shadow fade start\", \"Shadows\");\n    if (shadowFadeStart != 0)\n        fadeStartSpinBox->setValue(shadowFadeStart);\n\n    int shadowRes = Settings::Manager::getInt(\"shadow map resolution\", \"Shadows\");\n    int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));\n    if (shadowResIndex != -1)\n        shadowResolutionComboBox->setCurrentIndex(shadowResIndex);\n\n    return true;\n}\n\nvoid Launcher::GraphicsPage::saveSettings()\n{\n    // Visuals\n\n    // Ensure we only set the new settings if they changed. This is to avoid cluttering the\n    // user settings file (which by definition should only contain settings the user has touched)\n    bool cVSync = vSyncCheckBox->checkState();\n    if (cVSync != Settings::Manager::getBool(\"vsync\", \"Video\"))\n        Settings::Manager::setBool(\"vsync\", \"Video\", cVSync);\n\n    bool cFullScreen = fullScreenCheckBox->checkState();\n    if (cFullScreen != Settings::Manager::getBool(\"fullscreen\", \"Video\"))\n        Settings::Manager::setBool(\"fullscreen\", \"Video\", cFullScreen);\n\n    bool cWindowBorder = windowBorderCheckBox->checkState();\n    if (cWindowBorder != Settings::Manager::getBool(\"window border\", \"Video\"))\n        Settings::Manager::setBool(\"window border\", \"Video\", cWindowBorder);\n\n    int cAAValue = antiAliasingComboBox->currentText().toInt();\n    if (cAAValue != Settings::Manager::getInt(\"antialiasing\", \"Video\"))\n        Settings::Manager::setInt(\"antialiasing\", \"Video\", cAAValue);\n\n    int cWidth = 0;\n    int cHeight = 0;\n    if (standardRadioButton->isChecked()) {\n        QRegExp resolutionRe(QString(\"(\\\\d+) x (\\\\d+).*\"));\n        if (resolutionRe.exactMatch(resolutionComboBox->currentText().simplified())) {\n            cWidth = resolutionRe.cap(1).toInt();\n            cHeight = resolutionRe.cap(2).toInt();\n        }\n    } else {\n        cWidth = customWidthSpinBox->value();\n        cHeight = customHeightSpinBox->value();\n    }\n\n    if (cWidth != Settings::Manager::getInt(\"resolution x\", \"Video\"))\n        Settings::Manager::setInt(\"resolution x\", \"Video\", cWidth);\n\n    if (cHeight != Settings::Manager::getInt(\"resolution y\", \"Video\"))\n        Settings::Manager::setInt(\"resolution y\", \"Video\", cHeight);\n\n    int cScreen = screenComboBox->currentIndex();\n    if (cScreen != Settings::Manager::getInt(\"screen\", \"Video\"))\n        Settings::Manager::setInt(\"screen\", \"Video\", cScreen);\n\n    if (framerateLimitCheckBox->checkState() != Qt::Unchecked)\n    {\n        float cFpsLimit = framerateLimitSpinBox->value();\n        if (cFpsLimit != Settings::Manager::getFloat(\"framerate limit\", \"Video\"))\n            Settings::Manager::setFloat(\"framerate limit\", \"Video\", cFpsLimit);\n    }\n    else if (Settings::Manager::getFloat(\"framerate limit\", \"Video\") != 0)\n    {\n        Settings::Manager::setFloat(\"framerate limit\", \"Video\", 0);\n    }\n\n    // Lighting\n    static std::array<std::string, 3> lightingMethodMap = {\"legacy\", \"shaders compatibility\", \"shaders\"};\n    Settings::Manager::setString(\"lighting method\", \"Shaders\", lightingMethodMap[lightingMethodComboBox->currentIndex()]);\n\n    // Shadows\n    int cShadowDist = shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0;\n    if (Settings::Manager::getInt(\"maximum shadow map distance\", \"Shadows\") != cShadowDist)\n        Settings::Manager::setInt(\"maximum shadow map distance\", \"Shadows\", cShadowDist);\n    float cFadeStart = fadeStartSpinBox->value();\n    if (cShadowDist > 0 && Settings::Manager::getFloat(\"shadow fade start\", \"Shadows\") != cFadeStart)\n        Settings::Manager::setFloat(\"shadow fade start\", \"Shadows\", cFadeStart);\n\n    bool cActorShadows = actorShadowsCheckBox->checkState();\n    bool cObjectShadows = objectShadowsCheckBox->checkState();\n    bool cTerrainShadows = terrainShadowsCheckBox->checkState();\n    bool cPlayerShadows = playerShadowsCheckBox->checkState();\n    if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)\n    {\n        if (!Settings::Manager::getBool(\"enable shadows\", \"Shadows\"))\n            Settings::Manager::setBool(\"enable shadows\", \"Shadows\", true);\n        if (Settings::Manager::getBool(\"actor shadows\", \"Shadows\") != cActorShadows)\n            Settings::Manager::setBool(\"actor shadows\", \"Shadows\", cActorShadows);\n        if (Settings::Manager::getBool(\"player shadows\", \"Shadows\") != cPlayerShadows)\n            Settings::Manager::setBool(\"player shadows\", \"Shadows\", cPlayerShadows);\n        if (Settings::Manager::getBool(\"object shadows\", \"Shadows\") != cObjectShadows)\n            Settings::Manager::setBool(\"object shadows\", \"Shadows\", cObjectShadows);\n        if (Settings::Manager::getBool(\"terrain shadows\", \"Shadows\") != cTerrainShadows)\n            Settings::Manager::setBool(\"terrain shadows\", \"Shadows\", cTerrainShadows);\n    }\n    else\n    {\n        if (Settings::Manager::getBool(\"enable shadows\", \"Shadows\"))\n            Settings::Manager::setBool(\"enable shadows\", \"Shadows\", false);\n        if (Settings::Manager::getBool(\"actor shadows\", \"Shadows\"))\n            Settings::Manager::setBool(\"actor shadows\", \"Shadows\", false);\n        if (Settings::Manager::getBool(\"player shadows\", \"Shadows\"))\n            Settings::Manager::setBool(\"player shadows\", \"Shadows\", false);\n        if (Settings::Manager::getBool(\"object shadows\", \"Shadows\"))\n            Settings::Manager::setBool(\"object shadows\", \"Shadows\", false);\n        if (Settings::Manager::getBool(\"terrain shadows\", \"Shadows\"))\n            Settings::Manager::setBool(\"terrain shadows\", \"Shadows\", false);\n    }\n\n    bool cIndoorShadows = indoorShadowsCheckBox->checkState();\n    if (Settings::Manager::getBool(\"enable indoor shadows\", \"Shadows\") != cIndoorShadows)\n        Settings::Manager::setBool(\"enable indoor shadows\", \"Shadows\", cIndoorShadows);\n\n    int cShadowRes = shadowResolutionComboBox->currentText().toInt();\n    if (cShadowRes != Settings::Manager::getInt(\"shadow map resolution\", \"Shadows\"))\n        Settings::Manager::setInt(\"shadow map resolution\", \"Shadows\", cShadowRes);\n\n    auto cComputeSceneBounds = shadowComputeSceneBoundsComboBox->currentText().toStdString();\n    if (cComputeSceneBounds != Settings::Manager::getString(\"compute scene bounds\", \"Shadows\"))\n        Settings::Manager::setString(\"compute scene bounds\", \"Shadows\", cComputeSceneBounds);\n}\n\nQStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)\n{\n    QStringList result;\n    SDL_DisplayMode mode;\n    int modeIndex, modes = SDL_GetNumDisplayModes(screen);\n\n    if (modes < 0)\n    {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error receiving resolutions\"));\n        msgBox.setIcon(QMessageBox::Critical);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<br><b>SDL_GetNumDisplayModes failed:</b><br><br>\") + QString::fromUtf8(SDL_GetError()) + \"<br>\");\n        msgBox.exec();\n        return result;\n    }\n\n    for (modeIndex = 0; modeIndex < modes; modeIndex++)\n    {\n        if (SDL_GetDisplayMode(screen, modeIndex, &mode) < 0)\n        {\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error receiving resolutions\"));\n            msgBox.setIcon(QMessageBox::Critical);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(tr(\"<br><b>SDL_GetDisplayMode failed:</b><br><br>\") + QString::fromUtf8(SDL_GetError()) + \"<br>\");\n            msgBox.exec();\n            return result;\n        }\n\n        QString resolution = QString::number(mode.w) + QString(\" x \") + QString::number(mode.h);\n\n        QString aspect = getAspect(mode.w, mode.h);\n        if (aspect == QLatin1String(\"16:9\") || aspect == QLatin1String(\"16:10\")) {\n            resolution.append(tr(\"\\t(Wide \") + aspect + \")\");\n\n        } else if (aspect == QLatin1String(\"4:3\")) {\n            resolution.append(tr(\"\\t(Standard 4:3)\"));\n        }\n\n        result.append(resolution);\n    }\n\n    result.removeDuplicates();\n    return result;\n}\n\nQRect Launcher::GraphicsPage::getMaximumResolution()\n{\n    QRect max;\n\n    for (QScreen* screen : QGuiApplication::screens())\n    {\n        QRect res = screen->geometry();\n        if (res.width() > max.width())\n            max.setWidth(res.width());\n        if (res.height() > max.height())\n            max.setHeight(res.height());\n    }\n    return max;\n}\n\nvoid Launcher::GraphicsPage::screenChanged(int screen)\n{\n    if (screen >= 0) {\n        resolutionComboBox->clear();\n        resolutionComboBox->addItems(mResolutionsPerScreen[screen]);\n    }\n}\n\nvoid Launcher::GraphicsPage::slotFullScreenChanged(int state)\n{\n    if (state == Qt::Checked) {\n        standardRadioButton->toggle();\n        customRadioButton->setEnabled(false);\n        customWidthSpinBox->setEnabled(false);\n        customHeightSpinBox->setEnabled(false);\n        windowBorderCheckBox->setEnabled(false);\n    } else {\n        customRadioButton->setEnabled(true);\n        customWidthSpinBox->setEnabled(true);\n        customHeightSpinBox->setEnabled(true);\n        windowBorderCheckBox->setEnabled(true);\n    }\n}\n\nvoid Launcher::GraphicsPage::slotStandardToggled(bool checked)\n{\n    if (checked) {\n        resolutionComboBox->setEnabled(true);\n        customWidthSpinBox->setEnabled(false);\n        customHeightSpinBox->setEnabled(false);\n    } else {\n        resolutionComboBox->setEnabled(false);\n        customWidthSpinBox->setEnabled(true);\n        customHeightSpinBox->setEnabled(true);\n    }\n}\n\nvoid Launcher::GraphicsPage::slotFramerateLimitToggled(bool checked)\n{\n    framerateLimitSpinBox->setEnabled(checked);\n}\n\nvoid Launcher::GraphicsPage::slotShadowDistLimitToggled(bool checked)\n{\n    shadowDistanceSpinBox->setEnabled(checked);\n    fadeStartSpinBox->setEnabled(checked);\n}\n"
  },
  {
    "path": "apps/launcher/graphicspage.hpp",
    "content": "#ifndef GRAPHICSPAGE_H\n#define GRAPHICSPAGE_H\n\n#include \"ui_graphicspage.h\"\n\n#include <components/settings/settings.hpp>\n\n#include \"sdlinit.hpp\"\n\nnamespace Files { struct ConfigurationManager; }\n\nnamespace Launcher\n{\n    class GraphicsSettings;\n\n    class GraphicsPage : public QWidget, private Ui::GraphicsPage\n    {\n        Q_OBJECT\n\n    public:\n        explicit GraphicsPage(QWidget *parent = nullptr);\n\n        void saveSettings();\n        bool loadSettings();\n\n    public slots:\n        void screenChanged(int screen);\n\n    private slots:\n        void slotFullScreenChanged(int state);\n        void slotStandardToggled(bool checked);\n        void slotFramerateLimitToggled(bool checked);\n        void slotShadowDistLimitToggled(bool checked);\n\n    private:\n        QVector<QStringList> mResolutionsPerScreen;\n\n        static QStringList getAvailableResolutions(int screen);\n        static QRect getMaximumResolution();\n\n        bool setupSDL();\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/launcher/main.cpp",
    "content": "#include <iostream>\n\n#include <QTranslator>\n#include <QTextCodec>\n#include <QDir>\n\n#ifdef MAC_OS_X_VERSION_MIN_REQUIRED\n#undef MAC_OS_X_VERSION_MIN_REQUIRED\n// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154\n#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__\n#endif // MAC_OS_X_VERSION_MIN_REQUIRED\n\n#include \"maindialog.hpp\"\n\nint main(int argc, char *argv[])\n{\n    try\n    {\n        QApplication app(argc, argv);\n\n        // Internationalization \n        QString locale = QLocale::system().name().section('_', 0, 0);\n\n        QTranslator appTranslator;\n        appTranslator.load(\":/translations/\" + locale + \".qm\");\n        app.installTranslator(&appTranslator);\n\n        // Now we make sure the current dir is set to application path\n        QDir dir(QCoreApplication::applicationDirPath());\n\n        QDir::setCurrent(dir.absolutePath());\n\n        Launcher::MainDialog mainWin;\n\n        Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog();\n        if (result == Launcher::FirstRunDialogResultFailure)\n            return 0;\n\n        if (result == Launcher::FirstRunDialogResultContinue)\n            mainWin.show();\n\n        int exitCode = app.exec();\n\n        return exitCode;\n    }\n    catch (std::exception& e)\n    {\n        std::cerr << \"ERROR: \" << e.what() << std::endl;\n        return 0;\n    }\n}\n"
  },
  {
    "path": "apps/launcher/maindialog.cpp",
    "content": "#include \"maindialog.hpp\"\n\n#include <components/version/version.hpp>\n#include <components/misc/helpviewer.hpp>\n\n#include <QDate>\n#include <QMessageBox>\n#include <QFontDatabase>\n#include <QInputDialog>\n#include <QFileDialog>\n#include <QCloseEvent>\n#include <QTextCodec>\n\n#include \"playpage.hpp\"\n#include \"graphicspage.hpp\"\n#include \"datafilespage.hpp\"\n#include \"settingspage.hpp\"\n#include \"advancedpage.hpp\"\n\nusing namespace Process;\n\nvoid cfgError(const QString& title, const QString& msg) {\n    QMessageBox msgBox;\n    msgBox.setWindowTitle(title);\n    msgBox.setIcon(QMessageBox::Critical);\n    msgBox.setStandardButtons(QMessageBox::Ok);\n    msgBox.setText(msg);\n    msgBox.exec();\n}\n\nLauncher::MainDialog::MainDialog(QWidget *parent)\n    : QMainWindow(parent), mGameSettings (mCfgMgr)\n{\n    setupUi(this);\n\n    mGameInvoker = new ProcessInvoker();\n    mWizardInvoker = new ProcessInvoker();\n\n    connect(mWizardInvoker->getProcess(), SIGNAL(started()),\n            this, SLOT(wizardStarted()));\n\n    connect(mWizardInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),\n            this, SLOT(wizardFinished(int,QProcess::ExitStatus)));\n\n    iconWidget->setViewMode(QListView::IconMode);\n    iconWidget->setWrapping(false);\n    iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure\n    iconWidget->setIconSize(QSize(48, 48));\n    iconWidget->setMovement(QListView::Static);\n\n    iconWidget->setSpacing(4);\n    iconWidget->setCurrentRow(0);\n    iconWidget->setFlow(QListView::LeftToRight);\n\n    QPushButton *helpButton = new QPushButton(tr(\"Help\"));\n    QPushButton *playButton = new QPushButton(tr(\"Play\"));\n    buttonBox->button(QDialogButtonBox::Close)->setText(tr(\"Close\"));\n    buttonBox->addButton(helpButton, QDialogButtonBox::HelpRole);\n    buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);\n\n    connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));\n    connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));\n    connect(buttonBox, SIGNAL(helpRequested()), this, SLOT(help()));\n\n    // Remove what's this? button\n    setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n    createIcons();\n}\n\nLauncher::MainDialog::~MainDialog()\n{\n    delete mGameInvoker;\n    delete mWizardInvoker;\n}\n\nvoid Launcher::MainDialog::createIcons()\n{\n    if (!QIcon::hasThemeIcon(\"document-new\"))\n        QIcon::setThemeName(\"tango\");\n\n    QListWidgetItem *playButton = new QListWidgetItem(iconWidget);\n    playButton->setIcon(QIcon(\":/images/openmw.png\"));\n    playButton->setText(tr(\"Play\"));\n    playButton->setTextAlignment(Qt::AlignCenter);\n    playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n\n    QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);\n    dataFilesButton->setIcon(QIcon(\":/images/openmw-plugin.png\"));\n    dataFilesButton->setText(tr(\"Data Files\"));\n    dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);\n    dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n\n    QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);\n    graphicsButton->setIcon(QIcon(\":/images/preferences-video.png\"));\n    graphicsButton->setText(tr(\"Graphics\"));\n    graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);\n    graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n\n    QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);\n    settingsButton->setIcon(QIcon(\":/images/preferences.png\"));\n    settingsButton->setText(tr(\"Settings\"));\n    settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);\n    settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n\n    QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);\n    advancedButton->setIcon(QIcon(\":/images/preferences-advanced.png\"));\n    advancedButton->setText(tr(\"Advanced\"));\n    advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);\n    advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n\n    connect(iconWidget,\n            SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),\n            this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));\n\n}\n\nvoid Launcher::MainDialog::createPages()\n{\n    // Avoid creating the widgets twice\n    if (pagesWidget->count() != 0)\n        return;\n\n    mPlayPage = new PlayPage(this);\n    mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);\n    mGraphicsPage = new GraphicsPage(this);\n    mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);\n    mAdvancedPage = new AdvancedPage(mGameSettings, this);\n\n    // Set the combobox of the play page to imitate the combobox on the datafilespage\n    mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());\n    mPlayPage->setProfilesIndex(mDataFilesPage->profilesIndex());\n\n    // Add the pages to the stacked widget\n    pagesWidget->addWidget(mPlayPage);\n    pagesWidget->addWidget(mDataFilesPage);\n    pagesWidget->addWidget(mGraphicsPage);\n    pagesWidget->addWidget(mSettingsPage);\n    pagesWidget->addWidget(mAdvancedPage);\n\n    // Select the first page\n    iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);\n\n    connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play()));\n\n    connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int)));\n    connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));\n    // Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread\n    connect(mDataFilesPage, SIGNAL(signalLoadedCellsChanged(QStringList)), mAdvancedPage, SLOT(slotLoadedCellsChanged(QStringList)), Qt::QueuedConnection);\n\n}\n\nLauncher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()\n{\n    if (!setupLauncherSettings())\n        return FirstRunDialogResultFailure;\n\n    if (mLauncherSettings.value(QString(\"General/firstrun\"), QString(\"true\")) == QLatin1String(\"true\"))\n    {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"First run\"));\n        msgBox.setIcon(QMessageBox::Question);\n        msgBox.setStandardButtons(QMessageBox::NoButton);\n        msgBox.setText(tr(\"<html><head/><body><p><b>Welcome to OpenMW!</b></p> \\\n                          <p>It is recommended to run the Installation Wizard.</p> \\\n                          <p>The Wizard will let you select an existing Morrowind installation, \\\n                          or install Morrowind for OpenMW to use.</p></body></html>\"));\n\n        QAbstractButton *wizardButton =\n                msgBox.addButton(tr(\"Run &Installation Wizard\"), QMessageBox::AcceptRole); // ActionRole doesn't work?!\n        QAbstractButton *skipButton =\n                msgBox.addButton(tr(\"Skip\"), QMessageBox::RejectRole);\n\n        msgBox.exec();\n\n        if (msgBox.clickedButton() == wizardButton)\n        {\n            if (mWizardInvoker->startProcess(QLatin1String(\"openmw-wizard\"), false))\n                return FirstRunDialogResultWizard;\n        }\n        else if (msgBox.clickedButton() == skipButton)\n        {\n            // Don't bother setting up absent game data.\n            if (setup())\n                return FirstRunDialogResultContinue;\n        }\n        return FirstRunDialogResultFailure;\n    }\n\n    if (!setup() || !setupGameData()) {\n        return FirstRunDialogResultFailure;\n    }\n    return FirstRunDialogResultContinue;\n}\n\nvoid Launcher::MainDialog::setVersionLabel()\n{\n    // Add version information to bottom of the window\n    Version::Version v = Version::getOpenmwVersion(mGameSettings.value(\"resources\").toUtf8().constData());\n\n    QString revision(QString::fromUtf8(v.mCommitHash.c_str()));\n    QString tag(QString::fromUtf8(v.mTagHash.c_str()));\n\n    versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);\n    if (!v.mVersion.empty() && (revision.isEmpty() || revision == tag))\n        versionLabel->setText(tr(\"OpenMW %1 release\").arg(QString::fromUtf8(v.mVersion.c_str())));\n    else\n        versionLabel->setText(tr(\"OpenMW development (%1)\").arg(revision.left(10)));\n\n    // Add the compile date and time\n    auto compileDate = QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), QLatin1String(\"MMM d yyyy\"));\n    auto compileTime = QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), QLatin1String(\"hh:mm:ss\"));\n    versionLabel->setToolTip(tr(\"Compiled on %1 %2\").arg(QLocale::system().toString(compileDate, QLocale::LongFormat),\n                                                         QLocale::system().toString(compileTime, QLocale::ShortFormat)));\n}\n\nbool Launcher::MainDialog::setup()\n{\n    if (!setupGameSettings())\n        return false;\n\n    setVersionLabel();\n\n    mLauncherSettings.setContentList(mGameSettings);\n\n    if (!setupGraphicsSettings())\n        return false;\n\n    // Now create the pages as they need the settings\n    createPages();\n\n    // Call this so we can exit on SDL errors before mainwindow is shown\n    if (!mGraphicsPage->loadSettings())\n        return false;\n\n    loadSettings();\n\n    return true;\n}\n\nbool Launcher::MainDialog::reloadSettings()\n{\n    if (!setupLauncherSettings())\n        return false;\n\n    if (!setupGameSettings())\n        return false;\n\n    mLauncherSettings.setContentList(mGameSettings);\n\n    if (!setupGraphicsSettings())\n        return false;\n\n    if (!mSettingsPage->loadSettings())\n        return false;\n\n    if (!mDataFilesPage->loadSettings())\n        return false;\n\n    if (!mGraphicsPage->loadSettings())\n        return false;\n\n    if (!mAdvancedPage->loadSettings())\n        return false;\n\n    return true;\n}\n\nvoid Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)\n{\n    if (!current)\n        current = previous;\n\n    int currentIndex = iconWidget->row(current);\n    pagesWidget->setCurrentIndex(currentIndex);\n    mSettingsPage->resetProgressBar();\n}\n\nbool Launcher::MainDialog::setupLauncherSettings()\n{\n    mLauncherSettings.clear();\n\n    mLauncherSettings.setMultiValueEnabled(true);\n\n    QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());\n\n    QStringList paths;\n    paths.append(QString(Config::LauncherSettings::sLauncherConfigFileName));\n    paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));\n\n    for (const QString &path : paths)\n    {\n        qDebug() << \"Loading config file:\" << path.toUtf8().constData();\n        QFile file(path);\n        if (file.exists()) {\n            if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n                cfgError(tr(\"Error opening OpenMW configuration file\"),\n                         tr(\"<br><b>Could not open %0 for reading</b><br><br> \\\n                             Please make sure you have the right permissions \\\n                             and try again.<br>\").arg(file.fileName()));\n                return false;\n            }\n            QTextStream stream(&file);\n            stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n            mLauncherSettings.readFile(stream);\n        }\n        file.close();\n    }\n\n    return true;\n}\n\nbool Launcher::MainDialog::setupGameSettings()\n{\n    mGameSettings.clear();\n\n    QString localPath = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str());\n    QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());\n    QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());\n\n    // Load the user config file first, separately\n    // So we can write it properly, uncontaminated\n    QString path = userPath + QLatin1String(\"openmw.cfg\");\n    QFile file(path);\n\n    qDebug() << \"Loading config file:\" << path.toUtf8().constData();\n\n    if (file.exists()) {\n        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n            cfgError(tr(\"Error opening OpenMW configuration file\"),\n                     tr(\"<br><b>Could not open %0 for reading</b><br><br> \\\n                         Please make sure you have the right permissions \\\n                         and try again.<br>\").arg(file.fileName()));\n            return false;\n        }\n        QTextStream stream(&file);\n        stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n        mGameSettings.readUserFile(stream);\n        file.close();\n    }\n\n    // Now the rest - priority: user > local > global\n    QStringList paths;\n    paths.append(globalPath + QString(\"openmw.cfg\"));\n    paths.append(localPath + QString(\"openmw.cfg\"));\n    paths.append(userPath + QString(\"openmw.cfg\"));\n\n    for (const QString &path2 : paths)\n    {\n        qDebug() << \"Loading config file:\" << path2.toUtf8().constData();\n\n        file.setFileName(path2);\n        if (file.exists()) {\n            if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n                cfgError(tr(\"Error opening OpenMW configuration file\"),\n                         tr(\"<br><b>Could not open %0 for reading</b><br><br> \\\n                             Please make sure you have the right permissions \\\n                             and try again.<br>\").arg(file.fileName()));\n                return false;\n            }\n            QTextStream stream(&file);\n            stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n            mGameSettings.readFile(stream);\n            file.close();\n        }\n    }\n\n    return true;\n}\n\nbool Launcher::MainDialog::setupGameData()\n{\n    QStringList dataDirs;\n\n    // Check if the paths actually contain data files\n    for (const QString& path3 : mGameSettings.getDataDirs())\n    {\n        QDir dir(path3);\n        QStringList filters;\n        filters << \"*.esp\" << \"*.esm\" << \"*.omwgame\" << \"*.omwaddon\";\n\n        if (!dir.entryList(filters).isEmpty())\n            dataDirs.append(path3);\n    }\n\n    if (dataDirs.isEmpty())\n    {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error detecting Morrowind installation\"));\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setStandardButtons(QMessageBox::NoButton);\n        msgBox.setText(tr(\"<br><b>Could not find the Data Files location</b><br><br> \\\n                                   The directory containing the data files was not found.\"));\n\n        QAbstractButton *wizardButton =\n                msgBox.addButton(tr(\"Run &Installation Wizard...\"), QMessageBox::ActionRole);\n        QAbstractButton *skipButton =\n                msgBox.addButton(tr(\"Skip\"), QMessageBox::RejectRole);\n\n        Q_UNUSED(skipButton); // Suppress compiler unused warning\n\n        msgBox.exec();\n\n        if (msgBox.clickedButton() == wizardButton)\n        {\n            if (!mWizardInvoker->startProcess(QLatin1String(\"openmw-wizard\"), false))\n                return false;\n        }\n    }\n\n    return true;\n}\n\nbool Launcher::MainDialog::setupGraphicsSettings()\n{\n    // This method is almost a copy of OMW::Engine::loadSettings().  They should definitely\n    // remain consistent, and possibly be merged into a shared component.  At the very least\n    // the filenames should be in the CfgMgr component.\n\n    // Ensure to clear previous settings in case we had already loaded settings.\n    mEngineSettings.clear();\n\n    // Create the settings manager and load default settings file\n    const std::string localDefault = (mCfgMgr.getLocalPath() / \"defaults.bin\").string();\n    const std::string globalDefault = (mCfgMgr.getGlobalPath() / \"defaults.bin\").string();\n    std::string defaultPath;\n\n    // Prefer the defaults.bin in the current directory.\n    if (boost::filesystem::exists(localDefault))\n        defaultPath = localDefault;\n    else if (boost::filesystem::exists(globalDefault))\n        defaultPath = globalDefault;\n    // Something's very wrong if we can't find the file at all.\n    else {\n        cfgError(tr(\"Error reading OpenMW configuration file\"),\n                 tr(\"<br><b>Could not find defaults.bin</b><br><br> \\\n                     The problem may be due to an incomplete installation of OpenMW.<br> \\\n                     Reinstalling OpenMW may resolve the problem.\"));\n        return false;\n    }\n\n    // Load the default settings, report any parsing errors.\n    try {\n        mEngineSettings.loadDefault(defaultPath);\n    }\n    catch (std::exception& e) {\n        std::string msg = std::string(\"<br><b>Error reading defaults.bin</b><br><br>\") + e.what();\n        cfgError(tr(\"Error reading OpenMW configuration file\"), tr(msg.c_str()));\n        return false;\n    }\n\n    // Load user settings if they exist\n    const std::string userPath = (mCfgMgr.getUserConfigPath() / \"settings.cfg\").string();\n    // User settings are not required to exist, so if they don't we're done.\n    if (!boost::filesystem::exists(userPath)) return true;\n\n    try {\n        mEngineSettings.loadUser(userPath);\n    }\n    catch (std::exception& e) {\n        std::string msg = std::string(\"<br><b>Error reading settings.cfg</b><br><br>\") + e.what();\n        cfgError(tr(\"Error reading OpenMW configuration file\"), tr(msg.c_str()));\n        return false;\n    }\n\n    return true;\n}\n\nvoid Launcher::MainDialog::loadSettings()\n{\n    int width = mLauncherSettings.value(QString(\"General/MainWindow/width\")).toInt();\n    int height = mLauncherSettings.value(QString(\"General/MainWindow/height\")).toInt();\n\n    int posX = mLauncherSettings.value(QString(\"General/MainWindow/posx\")).toInt();\n    int posY = mLauncherSettings.value(QString(\"General/MainWindow/posy\")).toInt();\n\n    resize(width, height);\n    move(posX, posY);\n}\n\nvoid Launcher::MainDialog::saveSettings()\n{\n    QString width = QString::number(this->width());\n    QString height = QString::number(this->height());\n\n    mLauncherSettings.setValue(QString(\"General/MainWindow/width\"), width);\n    mLauncherSettings.setValue(QString(\"General/MainWindow/height\"), height);\n\n    QString posX = QString::number(this->pos().x());\n    QString posY = QString::number(this->pos().y());\n\n    mLauncherSettings.setValue(QString(\"General/MainWindow/posx\"), posX);\n    mLauncherSettings.setValue(QString(\"General/MainWindow/posy\"), posY);\n\n    mLauncherSettings.setValue(QString(\"General/firstrun\"), QString(\"false\"));\n\n}\n\nbool Launcher::MainDialog::writeSettings()\n{\n    // Now write all config files\n    saveSettings();\n    mDataFilesPage->saveSettings();\n    mGraphicsPage->saveSettings();\n    mSettingsPage->saveSettings();\n    mAdvancedPage->saveSettings();\n\n    QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());\n    QDir dir(userPath);\n\n    if (!dir.exists()) {\n        if (!dir.mkpath(userPath)) {\n            cfgError(tr(\"Error creating OpenMW configuration directory\"),\n                     tr(\"<br><b>Could not create %0</b><br><br> \\\n                         Please make sure you have the right permissions \\\n                         and try again.<br>\").arg(userPath));\n            return false;\n        }\n    }\n\n    // Game settings\n    QFile file(userPath + QString(\"openmw.cfg\"));\n\n    if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {\n        // File cannot be opened or created\n        cfgError(tr(\"Error writing OpenMW configuration file\"),\n                 tr(\"<br><b>Could not open or create %0 for writing</b><br><br> \\\n                     Please make sure you have the right permissions \\\n                     and try again.<br>\").arg(file.fileName()));\n        return false;\n    }\n\n\n    mGameSettings.writeFileWithComments(file);\n    file.close();\n\n    // Graphics settings\n    const std::string settingsPath = (mCfgMgr.getUserConfigPath() / \"settings.cfg\").string();\n    try {\n        mEngineSettings.saveUser(settingsPath);\n    }\n    catch (std::exception& e) {\n        std::string msg = \"<br><b>Error writing settings.cfg</b><br><br>\" +\n            settingsPath + \"<br><br>\" + e.what();\n        cfgError(tr(\"Error writing user settings file\"), tr(msg.c_str()));\n        return false;\n    }\n\n    // Launcher settings\n    file.setFileName(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));\n\n    if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {\n        // File cannot be opened or created\n        cfgError(tr(\"Error writing Launcher configuration file\"),\n                 tr(\"<br><b>Could not open or create %0 for writing</b><br><br> \\\n                     Please make sure you have the right permissions \\\n                     and try again.<br>\").arg(file.fileName()));\n        return false;\n    }\n\n    QTextStream stream(&file);\n    stream.setDevice(&file);\n    stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n    mLauncherSettings.writeFile(stream);\n    file.close();\n\n    return true;\n}\n\nvoid Launcher::MainDialog::closeEvent(QCloseEvent *event)\n{\n    writeSettings();\n    event->accept();\n}\n\nvoid Launcher::MainDialog::wizardStarted()\n{\n    hide();\n}\n\nvoid Launcher::MainDialog::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus)\n{\n    if (exitCode != 0 || exitStatus == QProcess::CrashExit)\n        return qApp->quit();\n\n    // HACK: Ensure the pages are created, else segfault\n    setup();\n\n    if (setupGameData() && reloadSettings())\n        show();\n}\n\nvoid Launcher::MainDialog::play()\n{\n    if (!writeSettings())\n        return qApp->quit();\n\n    if (!mGameSettings.hasMaster()) {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"No game file selected\"));\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<br><b>You do not have a game file selected.</b><br><br> \\\n                          OpenMW will not start without a game file selected.<br>\"));\n                          msgBox.exec();\n        return;\n    }\n\n    // Launch the game detached\n\n    if (mGameInvoker->startProcess(QLatin1String(\"tes3mp-browser\"), true))\n        return qApp->quit();\n}\n\nvoid Launcher::MainDialog::help()\n{\n    Misc::HelpViewer::openHelp(\"reference/index.html\");\n}\n"
  },
  {
    "path": "apps/launcher/maindialog.hpp",
    "content": "#ifndef MAINDIALOG_H\n#define MAINDIALOG_H\n\n\n#ifndef Q_MOC_RUN\n#include <components/files/configurationmanager.hpp>\n\n\n#include <components/process/processinvoker.hpp>\n\n#include <components/config/gamesettings.hpp>\n#include <components/config/launchersettings.hpp>\n\n#include <components/settings/settings.hpp>\n#endif\n#include \"ui_mainwindow.h\"\n\nclass QListWidgetItem;\nclass QStackedWidget;\nclass QStringList;\nclass QStringListModel;\nclass QString;\n\nnamespace Launcher\n{\n    class PlayPage;\n    class GraphicsPage;\n    class DataFilesPage;\n    class UnshieldThread;\n    class SettingsPage;\n    class AdvancedPage;\n\n    enum FirstRunDialogResult\n    {\n        FirstRunDialogResultFailure,\n        FirstRunDialogResultContinue,\n        FirstRunDialogResultWizard\n    };\n\n#ifndef WIN32\n    bool expansions(Launcher::UnshieldThread& cd);\n#endif\n\n    class MainDialog : public QMainWindow, private Ui::MainWindow\n    {\n        Q_OBJECT\n\n    public:\n        explicit MainDialog(QWidget *parent = nullptr);\n        ~MainDialog();\n\n        FirstRunDialogResult showFirstRunDialog();\n\n        bool reloadSettings();\n        bool writeSettings();\n\n    public slots:\n        void changePage(QListWidgetItem *current, QListWidgetItem *previous);\n        void play();\n        void help();\n\n    private slots:\n        void wizardStarted();\n        void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus);\n\n    private:\n        bool setup();\n\n        void createIcons();\n        void createPages();\n\n        bool setupLauncherSettings();\n        bool setupGameSettings();\n        bool setupGraphicsSettings();\n        bool setupGameData();\n\n        void setVersionLabel();\n\n        void loadSettings();\n        void saveSettings();\n\n        inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); }\n        bool startProgram(const QString &name, const QStringList &arguments, bool detached = false);\n\n        void closeEvent(QCloseEvent *event) override;\n\n        PlayPage *mPlayPage;\n        GraphicsPage *mGraphicsPage;\n        DataFilesPage *mDataFilesPage;\n        SettingsPage *mSettingsPage;\n        AdvancedPage *mAdvancedPage;\n\n        Process::ProcessInvoker *mGameInvoker;\n        Process::ProcessInvoker *mWizardInvoker;\n\n        Files::ConfigurationManager mCfgMgr;\n\n        Config::GameSettings mGameSettings;\n        Settings::Manager mEngineSettings;\n        Config::LauncherSettings mLauncherSettings;\n\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/launcher/playpage.cpp",
    "content": "#include \"playpage.hpp\"\n\n#include <QListView>\n\nLauncher::PlayPage::PlayPage(QWidget *parent) : QWidget(parent)\n{\n    setObjectName (\"PlayPage\");\n    setupUi(this);\n\n    profilesComboBox->setView(new QListView());\n\n    connect(profilesComboBox, SIGNAL(activated(int)), this, SIGNAL (signalProfileChanged(int)));\n    connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked()));\n\n}\n\nvoid Launcher::PlayPage::setProfilesModel(QAbstractItemModel *model)\n{\n    profilesComboBox->setModel(model);\n}\n\nvoid Launcher::PlayPage::setProfilesIndex(int index)\n{\n    profilesComboBox->setCurrentIndex(index);\n}\n\nvoid Launcher::PlayPage::slotPlayClicked()\n{\n    emit playButtonClicked();\n}\n"
  },
  {
    "path": "apps/launcher/playpage.hpp",
    "content": "#ifndef PLAYPAGE_H\n#define PLAYPAGE_H\n\n#include \"ui_playpage.h\"\n\nclass QComboBox;\nclass QPushButton;\nclass QAbstractItemModel;\n\nnamespace Launcher\n{\n    class PlayPage : public QWidget, private Ui::PlayPage\n    {\n        Q_OBJECT\n\n    public:\n        PlayPage(QWidget *parent = nullptr);\n        void setProfilesModel(QAbstractItemModel *model);\n\n    signals:\n        void signalProfileChanged(int index);\n        void playButtonClicked();\n\n    public slots:\n        void setProfilesIndex(int index);\n\n    private slots:\n        void slotPlayClicked();\n\n\n\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/launcher/sdlinit.cpp",
    "content": "#include <signal.h>\n\n#include <SDL.h>\n\nbool initSDL()\n{\n    SDL_SetHint(SDL_HINT_RENDER_DRIVER, \"software\");\n    SDL_SetMainReady();\n    // Required for determining screen resolution and such on the Graphics tab\n    if (SDL_Init(SDL_INIT_VIDEO) != 0)\n    {\n        return false;\n    }\n    signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,\n    // so reset SIGINT which SDL wants to redirect to an SDL_Quit event.\n\n    return true;\n}\n\nvoid quitSDL()\n{\n    // Disconnect from SDL processes\n    SDL_Quit();\n}\n"
  },
  {
    "path": "apps/launcher/sdlinit.hpp",
    "content": "#ifndef SDLINIT_H\n#define SDLINIT_H\n\nbool initSDL();\n\nvoid quitSDL();\n\n#endif\n"
  },
  {
    "path": "apps/launcher/settingspage.cpp",
    "content": "#include \"settingspage.hpp\"\n\n#include <QFileDialog>\n#include <QMessageBox>\n#include <QDir>\n\n#include <components/files/configurationmanager.hpp>\n\n#include <components/config/gamesettings.hpp>\n#include <components/config/launchersettings.hpp>\n\n#include \"utils/textinputdialog.hpp\"\n#include \"datafilespage.hpp\"\n\nusing namespace Process;\n\nLauncher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,\n                                     Config::GameSettings &gameSettings,\n                                     Config::LauncherSettings &launcherSettings, MainDialog *parent)\n    : QWidget(parent)\n    , mCfgMgr(cfg)\n    , mGameSettings(gameSettings)\n    , mLauncherSettings(launcherSettings)\n    , mMain(parent)\n{\n    setupUi(this);\n\n    QStringList languages;\n    languages << tr(\"English\")\n              << tr(\"French\")\n              << tr(\"German\")\n              << tr(\"Italian\")\n              << tr(\"Polish\")\n              << tr(\"Russian\")\n              << tr(\"Spanish\");\n\n    languageComboBox->addItems(languages);\n\n    mWizardInvoker = new ProcessInvoker();\n    mImporterInvoker = new ProcessInvoker();\n    resetProgressBar();\n\n    connect(mWizardInvoker->getProcess(), SIGNAL(started()),\n            this, SLOT(wizardStarted()));\n\n    connect(mWizardInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),\n            this, SLOT(wizardFinished(int,QProcess::ExitStatus)));\n\n    connect(mImporterInvoker->getProcess(), SIGNAL(started()),\n            this, SLOT(importerStarted()));\n\n    connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),\n            this, SLOT(importerFinished(int,QProcess::ExitStatus)));\n\n    mProfileDialog = new TextInputDialog(tr(\"New Content List\"), tr(\"Content List name:\"), this);\n\n    connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)),\n            this, SLOT(updateOkButton(QString)));\n\n    // Detect Morrowind configuration files\n    QStringList iniPaths;\n\n    for (const QString &path : mGameSettings.getDataDirs())\n    {\n        QDir dir(path);\n        dir.setPath(dir.canonicalPath()); // Resolve symlinks\n\n        if (dir.exists(QString(\"Morrowind.ini\")))\n            iniPaths.append(dir.absoluteFilePath(QString(\"Morrowind.ini\")));\n        else\n        {\n            if (!dir.cdUp())\n                continue; // Cannot move from Data Files\n\n            if (dir.exists(QString(\"Morrowind.ini\")))\n                iniPaths.append(dir.absoluteFilePath(QString(\"Morrowind.ini\")));\n        }\n    }\n\n    if (!iniPaths.isEmpty()) {\n        settingsComboBox->addItems(iniPaths);\n        importerButton->setEnabled(true);\n    } else {\n        importerButton->setEnabled(false);\n    }\n\n    loadSettings();\n}\n\nLauncher::SettingsPage::~SettingsPage()\n{\n    delete mWizardInvoker;\n    delete mImporterInvoker;\n}\n\nvoid Launcher::SettingsPage::on_wizardButton_clicked()\n{\n    mMain->writeSettings();\n\n    if (!mWizardInvoker->startProcess(QLatin1String(\"openmw-wizard\"), false))\n        return;\n}\n\nvoid Launcher::SettingsPage::on_importerButton_clicked()\n{\n    mMain->writeSettings();\n\n    // Create the file if it doesn't already exist, else the importer will fail\n    QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));\n    path.append(QLatin1String(\"openmw.cfg\"));\n    QFile file(path);\n\n    if (!file.exists()) {\n        if (!file.open(QIODevice::ReadWrite)) {\n            // File cannot be created\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error writing OpenMW configuration file\"));\n            msgBox.setIcon(QMessageBox::Critical);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(tr(\"<html><head/><body><p><b>Could not open or create %1 for writing </b></p> \\\n                              <p>Please make sure you have the right permissions \\\n                              and try again.</p></body></html>\").arg(file.fileName()));\n            msgBox.exec();\n            return;\n        }\n\n        file.close();\n    }\n\n    // Construct the arguments to run the importer\n    QStringList arguments;\n\n    if (addonsCheckBox->isChecked())\n        arguments.append(QString(\"--game-files\"));\n\n    arguments.append(QString(\"--encoding\"));\n    arguments.append(mGameSettings.value(QString(\"encoding\"), QString(\"win1252\")));\n    arguments.append(QString(\"--ini\"));\n    arguments.append(settingsComboBox->currentText());\n    arguments.append(QString(\"--cfg\"));\n    arguments.append(path);\n\n    qDebug() << \"arguments \" << arguments;\n\n    // start the progress bar as a \"bouncing ball\"\n    progressBar->setMaximum(0);\n    progressBar->setValue(0);\n    if (!mImporterInvoker->startProcess(QLatin1String(\"openmw-iniimporter\"), arguments, false))\n    {\n        resetProgressBar();\n    }\n}\n\nvoid Launcher::SettingsPage::on_browseButton_clicked()\n{\n    QString iniFile = QFileDialog::getOpenFileName(\n                this,\n                QObject::tr(\"Select configuration file\"),\n                QDir::currentPath(),\n                QString(tr(\"Morrowind configuration file (*.ini)\")));\n\n\n    if (iniFile.isEmpty())\n        return;\n\n    QFileInfo info(iniFile);\n\n    if (!info.exists() || !info.isReadable())\n        return;\n\n    const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));\n\n    if (settingsComboBox->findText(path) == -1) {\n        settingsComboBox->addItem(path);\n        settingsComboBox->setCurrentIndex(settingsComboBox->findText(path));\n        importerButton->setEnabled(true);\n    }\n}\n\nvoid Launcher::SettingsPage::wizardStarted()\n{\n    mMain->hide(); // Hide the launcher\n\n    wizardButton->setEnabled(false);\n}\n\nvoid Launcher::SettingsPage::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus)\n{\n    if (exitCode != 0 || exitStatus == QProcess::CrashExit)\n        return qApp->quit();\n\n    mMain->reloadSettings();\n    wizardButton->setEnabled(true);\n\n    mMain->show(); // Show the launcher again\n}\n\nvoid Launcher::SettingsPage::importerStarted()\n{\n    importerButton->setEnabled(false);\n}\n\nvoid Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)\n{\n    if (exitCode != 0 || exitStatus == QProcess::CrashExit)\n    {\n        resetProgressBar();\n\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Importer finished\"));\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setText(tr(\"Failed to import settings from INI file.\"));\n        msgBox.exec();\n    }\n    else\n    {\n        // indicate progress finished\n        progressBar->setMaximum(1);\n        progressBar->setValue(1);\n\n        // Importer may have changed settings, so refresh\n        mMain->reloadSettings();\n    }\n\n    importerButton->setEnabled(true);\n}\n\nvoid Launcher::SettingsPage::resetProgressBar()\n{\n    // set progress bar to 0 %\n    progressBar->reset();\n}\n\nvoid Launcher::SettingsPage::updateOkButton(const QString &text)\n{\n    // We do this here because we need to access the profiles\n    if (text.isEmpty()) {\n         mProfileDialog->setOkButtonEnabled(false);\n         return;\n    }\n\n    const QStringList profiles(mLauncherSettings.getContentLists());\n\n    (profiles.contains(text))\n            ? mProfileDialog->setOkButtonEnabled(false)\n            : mProfileDialog->setOkButtonEnabled(true);\n}\n\nvoid Launcher::SettingsPage::saveSettings()\n{\n    QString language(languageComboBox->currentText());\n\n    mLauncherSettings.setValue(QLatin1String(\"Settings/language\"), language);\n\n    if (language == QLatin1String(\"Polish\")) {\n        mGameSettings.setValue(QLatin1String(\"encoding\"), QLatin1String(\"win1250\"));\n    } else if (language == QLatin1String(\"Russian\")) {\n        mGameSettings.setValue(QLatin1String(\"encoding\"), QLatin1String(\"win1251\"));\n    }  else {\n        mGameSettings.setValue(QLatin1String(\"encoding\"), QLatin1String(\"win1252\"));\n    }\n}\n\nbool Launcher::SettingsPage::loadSettings()\n{\n    QString language(mLauncherSettings.value(QLatin1String(\"Settings/language\")));\n\n    int index = languageComboBox->findText(language);\n\n    if (index != -1)\n        languageComboBox->setCurrentIndex(index);\n\n    return true;\n}\n"
  },
  {
    "path": "apps/launcher/settingspage.hpp",
    "content": "#ifndef SETTINGSPAGE_HPP\n#define SETTINGSPAGE_HPP\n\n#include <components/process/processinvoker.hpp>\n\n#include \"ui_settingspage.h\"\n\n#include \"maindialog.hpp\"\n\nnamespace Files { struct ConfigurationManager; }\nnamespace Config { class GameSettings;\n                   class LauncherSettings; }\n\nnamespace Launcher\n{\n    class TextInputDialog;\n\n    class SettingsPage : public QWidget, private Ui::SettingsPage\n    {\n        Q_OBJECT\n\n    public:\n        SettingsPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,\n                      Config::LauncherSettings &launcherSettings, MainDialog *parent = nullptr);\n        ~SettingsPage();\n\n        void saveSettings();\n        bool loadSettings();\n        \n        /// set progress bar on page to 0%\n        void resetProgressBar();\n\n    private slots:\n\n        void on_wizardButton_clicked();\n        void on_importerButton_clicked();\n        void on_browseButton_clicked();\n\n        void wizardStarted();\n        void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus);\n\n        void importerStarted();\n        void importerFinished(int exitCode, QProcess::ExitStatus exitStatus);\n\n        void updateOkButton(const QString &text);\n\n    private:\n\n        Process::ProcessInvoker *mWizardInvoker;\n        Process::ProcessInvoker *mImporterInvoker;\n\n        Files::ConfigurationManager &mCfgMgr;\n\n        Config::GameSettings &mGameSettings;\n        Config::LauncherSettings &mLauncherSettings;\n\n        MainDialog *mMain;\n        TextInputDialog *mProfileDialog;\n\n    };\n}\n\n#endif // SETTINGSPAGE_HPP\n"
  },
  {
    "path": "apps/launcher/textslotmsgbox.cpp",
    "content": "#include \"textslotmsgbox.hpp\"\n\nvoid Launcher::TextSlotMsgBox::setTextSlot(const QString& string)\n{\n    setText(string);\n}\n"
  },
  {
    "path": "apps/launcher/textslotmsgbox.hpp",
    "content": "#ifndef TEXT_SLOT_MSG_BOX\n#define TEXT_SLOT_MSG_BOX\n\n#include <QMessageBox>\n\nnamespace Launcher\n{\n    class TextSlotMsgBox : public QMessageBox\n    {\n    Q_OBJECT\n        public slots:\n            void setTextSlot(const QString& string);\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/launcher/utils/cellnameloader.cpp",
    "content": "#include \"cellnameloader.hpp\"\n\n#include <components/esm/loadcell.hpp>\n#include <components/contentselector/view/contentselector.hpp>\n\nQSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)\n{\n    QSet<QString> cellNames;\n    ESM::ESMReader esmReader;\n\n    // Loop through all content files\n    for (auto &contentPath : contentPaths) {\n        esmReader.open(contentPath.toStdString());\n\n        // Loop through all records\n        while(esmReader.hasMoreRecs())\n        {\n            ESM::NAME recordName = esmReader.getRecName();\n            esmReader.getRecHeader();\n\n            if (isCellRecord(recordName)) {\n                QString cellName = getCellName(esmReader);\n                if (!cellName.isEmpty()) {\n                    cellNames.insert(cellName);\n                }\n            }\n\n            // Stop loading content for this record and continue to the next\n            esmReader.skipRecord();\n        }\n    }\n\n    return cellNames;\n}\n\nbool CellNameLoader::isCellRecord(ESM::NAME &recordName)\n{\n    return recordName.intval == ESM::REC_CELL;\n}\n\nQString CellNameLoader::getCellName(ESM::ESMReader &esmReader)\n{\n    ESM::Cell cell;\n    bool isDeleted = false;\n    cell.loadNameAndData(esmReader, isDeleted);\n\n    return QString::fromStdString(cell.mName);\n}\n\n"
  },
  {
    "path": "apps/launcher/utils/cellnameloader.hpp",
    "content": "#ifndef OPENMW_CELLNAMELOADER_H\n#define OPENMW_CELLNAMELOADER_H\n\n#include <QSet>\n#include <QString>\n\n#include <components/esm/esmreader.hpp>\n\nnamespace ESM {class ESMReader; struct Cell;}\nnamespace ContentSelectorView {class ContentSelector;}\n\nclass CellNameLoader {\n\npublic:\n\n    /**\n     * Returns the names of all cells contained within the given content files\n     * @param contentPaths the file paths of each content file to be examined\n     * @return the names of all cells\n     */\n    QSet<QString> getCellNames(QStringList &contentPaths);\n\nprivate:\n    /**\n     * Returns whether or not the given record is of type \"Cell\"\n     * @param name The name associated with the record\n     * @return whether or not the given record is of type \"Cell\"\n     */\n    bool isCellRecord(ESM::NAME &name);\n\n    /**\n     * Returns the name of the cell\n     * @param esmReader the reader currently pointed to a loaded cell\n     * @return the name of the cell\n     */\n    QString getCellName(ESM::ESMReader &esmReader);\n};\n\n\n#endif //OPENMW_CELLNAMELOADER_H\n"
  },
  {
    "path": "apps/launcher/utils/lineedit.cpp",
    "content": "#include \"lineedit.hpp\"\n\nLineEdit::LineEdit(QWidget *parent)\n    : QLineEdit(parent)\n{\n    setupClearButton();\n}\n\nvoid LineEdit::setupClearButton()\n{\n    mClearButton = new QToolButton(this);\n    QPixmap pixmap(\":images/clear.png\");\n    mClearButton->setIcon(QIcon(pixmap));\n    mClearButton->setIconSize(pixmap.size());\n    mClearButton->setCursor(Qt::ArrowCursor);\n    mClearButton->setStyleSheet(\"QToolButton { border: none; padding: 0px; }\");\n    mClearButton->hide();\n    connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear()));\n    connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&)));\n}\n\nvoid LineEdit::resizeEvent(QResizeEvent *)\n{\n    QSize sz = mClearButton->sizeHint();\n    int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);\n    mClearButton->move(rect().right() - frameWidth - sz.width(),\n                      (rect().bottom() + 1 - sz.height())/2);\n}\n\nvoid LineEdit::updateClearButton(const QString& text)\n{\n    mClearButton->setVisible(!text.isEmpty());\n}\n"
  },
  {
    "path": "apps/launcher/utils/lineedit.hpp",
    "content": "/****************************************************************************\n**\n** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>\n**\n** Use, modification and distribution is allowed without limitation,\n** warranty, liability or support of any kind.\n**\n****************************************************************************/\n\n#ifndef LINEEDIT_H\n#define LINEEDIT_H\n\n#include <QLineEdit>\n#include <QStylePainter>\n#include <QToolButton>\n\nclass QToolButton;\n\nclass LineEdit : public QLineEdit\n{\n    Q_OBJECT\n\n    QString mPlaceholderText;\n\npublic:\n    LineEdit(QWidget *parent = nullptr);\n\nprotected:\n    void resizeEvent(QResizeEvent *) override;\n\nprivate slots:\n    void updateClearButton(const QString &text);\n\nprotected:\n    QToolButton *mClearButton;\n\n    void setupClearButton();\n};\n\n#endif // LIENEDIT_H\n\n"
  },
  {
    "path": "apps/launcher/utils/openalutil.cpp",
    "content": "#include <cstring>\n#include <vector>\n#include <memory>\n\n#include <apps/openmw/mwsound/alext.h>\n\n#include \"openalutil.hpp\"\n\n#ifndef ALC_ALL_DEVICES_SPECIFIER\n#define ALC_ALL_DEVICES_SPECIFIER 0x1013\n#endif\n\nstd::vector<const char *> Launcher::enumerateOpenALDevices()\n{\n    std::vector<const char *> devlist;\n    const ALCchar *devnames;\n\n    if(alcIsExtensionPresent(nullptr, \"ALC_ENUMERATE_ALL_EXT\"))\n    {\n        devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);\n    }\n    else\n    {\n        devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);\n    }\n    \n    while(devnames && *devnames)\n    {\n        devlist.emplace_back(devnames);\n        devnames += strlen(devnames)+1;\n    }\n    return devlist;\n}\n\nstd::vector<const char *> Launcher::enumerateOpenALDevicesHrtf()\n{\n    std::vector<const char *> ret;\n\n    ALCdevice *device = alcOpenDevice(nullptr);\n    if(device)\n    {\n        if(alcIsExtensionPresent(device, \"ALC_SOFT_HRTF\"))\n        {\n            LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;\n            void* funcPtr = alcGetProcAddress(device, \"alcGetStringiSOFT\");\n            memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr));\n            ALCint num_hrtf;\n            alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);\n            ret.reserve(num_hrtf);\n            for(ALCint i = 0;i < num_hrtf;++i)\n            {\n                const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);\n                if(strcmp(entry, \"\") == 0)\n                    break;\n                ret.emplace_back(entry);\n            }\n        }\n        alcCloseDevice(device);\n    }\n    return ret;\n}\n"
  },
  {
    "path": "apps/launcher/utils/openalutil.hpp",
    "content": "#include <vector>\n\nnamespace Launcher\n{\n    std::vector<const char *> enumerateOpenALDevices();\n    std::vector<const char *> enumerateOpenALDevicesHrtf();\n}"
  },
  {
    "path": "apps/launcher/utils/profilescombobox.cpp",
    "content": "#include <QRegExpValidator>\n#include <QString>\n#include <QApplication>\n#include <QKeyEvent>\n\n#include \"profilescombobox.hpp\"\n\nProfilesComboBox::ProfilesComboBox(QWidget *parent) :\n    ContentSelectorView::ComboBox(parent)\n{\n    connect(this, SIGNAL(activated(int)), this,\n            SLOT(slotIndexChangedByUser(int)));\n\n    setInsertPolicy(QComboBox::NoInsert);\n}\n\nvoid ProfilesComboBox::setEditEnabled(bool editable)\n{\n    if (isEditable() == editable)\n        return;\n\n    if (!editable) {\n        disconnect(lineEdit(), SIGNAL(editingFinished()), this, SLOT(slotEditingFinished()));\n        disconnect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString)));\n        return setEditable(false);\n    }\n\n    // Reset the completer and validator\n    setEditable(true);\n    setValidator(mValidator);\n\n    ComboBoxLineEdit *edit = new ComboBoxLineEdit(this);\n\n    setLineEdit(edit);\n    setCompleter(nullptr);\n\n    connect(lineEdit(), SIGNAL(editingFinished()), this,\n                SLOT(slotEditingFinished()));\n\n    connect(lineEdit(), SIGNAL(textChanged(QString)), this,\n            SLOT(slotTextChanged(QString)));\n\n    connect (lineEdit(), SIGNAL(textChanged(QString)), this,\n             SIGNAL (signalProfileTextChanged (QString)));\n}\n\nvoid ProfilesComboBox::slotTextChanged(const QString &text)\n{\n    QPalette palette;\n    palette.setColor(QPalette::Text,Qt::red);\n\n    int index = findText(text);\n\n    if (text.isEmpty() || (index != -1 && index != currentIndex())) {\n        lineEdit()->setPalette(palette);\n    } else {\n        lineEdit()->setPalette(QApplication::palette());\n    }\n}\n\nvoid ProfilesComboBox::slotEditingFinished()\n{\n    QString current = currentText();\n    QString previous = itemText(currentIndex());\n\n    if (currentIndex() == -1)\n        return;\n\n    if (current.isEmpty())\n        return;\n\n    if (current == previous)\n        return;\n\n    if (findText(current) != -1)\n        return;\n\n    setItemText(currentIndex(), current);\n    emit(profileRenamed(previous, current));\n}\n\nvoid ProfilesComboBox::slotIndexChangedByUser(int index)\n{\n    if (index == -1)\n        return;\n\n    emit (signalProfileChanged(mOldProfile, currentText()));\n    mOldProfile = currentText();\n}\n\nProfilesComboBox::ComboBoxLineEdit::ComboBoxLineEdit (QWidget *parent)\n    : LineEdit (parent)\n{\n    int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);\n\n    setObjectName(QString(\"ComboBoxLineEdit\"));\n    setStyleSheet(QString(\"ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } \").arg(mClearButton->sizeHint().width() + frameWidth + 1));\n}\n"
  },
  {
    "path": "apps/launcher/utils/profilescombobox.hpp",
    "content": "#ifndef PROFILESCOMBOBOX_HPP\n#define PROFILESCOMBOBOX_HPP\n\n#include \"components/contentselector/view/combobox.hpp\"\n#include \"lineedit.hpp\"\n\n#include <QDebug>\n\nclass QString;\n\nclass ProfilesComboBox : public ContentSelectorView::ComboBox\n{\n    Q_OBJECT\n\npublic:\n    class ComboBoxLineEdit : public LineEdit\n    {\n    public:\n        explicit ComboBoxLineEdit (QWidget *parent = nullptr);\n    };\n\npublic:\n\n    explicit ProfilesComboBox(QWidget *parent = nullptr);\n    void setEditEnabled(bool editable);\n    void setCurrentProfile(int index)\n    {\n        ComboBox::setCurrentIndex(index);\n        mOldProfile = currentText();\n    }\n\nsignals:\n    void signalProfileTextChanged(const QString &item);\n    void signalProfileChanged(const QString &previous, const QString &current);\n    void signalProfileChanged(int index);\n    void profileRenamed(const QString &oldName, const QString &newName);\n\nprivate slots:\n\n    void slotEditingFinished();\n    void slotIndexChangedByUser(int index);\n    void slotTextChanged(const QString &text);\n\nprivate:\n    QString mOldProfile;\n};\n#endif // PROFILESCOMBOBOX_HPP\n"
  },
  {
    "path": "apps/launcher/utils/textinputdialog.cpp",
    "content": "#include \"textinputdialog.hpp\"\n\n#include <QDialogButtonBox>\n#include <QApplication>\n#include <QPushButton>\n#include <QVBoxLayout>\n#include <QValidator>\n#include <QLabel>\n\nLauncher::TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) :\n    QDialog(parent)\n{\n    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n    mButtonBox = new QDialogButtonBox(this);\n    mButtonBox->addButton(QDialogButtonBox::Ok);\n    mButtonBox->addButton(QDialogButtonBox::Cancel);\n    mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false);\n\n    QLabel *label = new QLabel(this);\n    label->setText(text);\n\n    // Line edit\n    QValidator *validator = new QRegExpValidator(QRegExp(\"^[a-zA-Z0-9_]*$\"), this); // Alpha-numeric + underscore\n    mLineEdit = new LineEdit(this);\n    mLineEdit->setValidator(validator);\n    mLineEdit->setCompleter(nullptr);\n\n    QVBoxLayout *dialogLayout = new QVBoxLayout(this);\n    dialogLayout->addWidget(label);\n    dialogLayout->addWidget(mLineEdit);\n    dialogLayout->addWidget(mButtonBox);\n\n    // Messageboxes on mac have no title\n#ifndef Q_OS_MAC\n    setWindowTitle(title);\n#else\n    Q_UNUSED(title);\n#endif\n\n    setModal(true);\n\n    connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));\n    connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));\n}\n\nLauncher::TextInputDialog::~TextInputDialog()\n{\n}\n\nint Launcher::TextInputDialog::exec()\n{\n    mLineEdit->clear();\n    mLineEdit->setFocus();\n    return QDialog::exec();\n}\n\nvoid Launcher::TextInputDialog::setOkButtonEnabled(bool enabled)\n{\n    QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);\n    okButton->setEnabled(enabled);\n\n    QPalette palette;\n    palette.setColor(QPalette::Text, Qt::red);\n\n    if (enabled) {\n        mLineEdit->setPalette(QApplication::palette());\n    } else {\n        // Existing profile name, make the text red\n        mLineEdit->setPalette(palette);\n    }\n}\n"
  },
  {
    "path": "apps/launcher/utils/textinputdialog.hpp",
    "content": "#ifndef TEXTINPUTDIALOG_HPP\n#define TEXTINPUTDIALOG_HPP\n\n#include <QDialog>\n\n#include \"lineedit.hpp\"\n\nclass QDialogButtonBox;\n\nnamespace Launcher\n{\n    class TextInputDialog : public QDialog\n    {\n        Q_OBJECT\n\n    public:\n\n        explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = nullptr);\n        ~TextInputDialog ();\n\n        inline LineEdit *lineEdit() { return mLineEdit; }\n        void setOkButtonEnabled(bool enabled);\n\n        int exec() override;\n\n    private:\n\n        QDialogButtonBox *mButtonBox;\n        LineEdit *mLineEdit;\n\n    };\n}\n\n#endif // TEXTINPUTDIALOG_HPP\n"
  },
  {
    "path": "apps/master/CMakeLists.txt",
    "content": "project(masterserver)\n\n#set(CMAKE_CXX_STANDARD 14)\nadd_definitions(-std=gnu++14)\n\ninclude_directories(\"./\")\n\nset(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp RestServer.cpp RestServer.hpp)\n\nadd_executable(masterserver ${SOURCE_FILES})\ntarget_link_libraries(masterserver ${RakNet_LIBRARY} components)\n\noption(BUILD_MASTER_TEST \"build master server test program\" OFF)\n\nif(BUILD_MASTER_TEST)\n    add_executable(ServerTest ServerTest.cpp)\n    target_link_libraries(ServerTest ${RakNet_LIBRARY} components)\nendif()\n\nif (UNIX)\n    # Fix for not visible pthreads functions for linker with glibc 2.15\n    if(NOT APPLE)\n        target_link_libraries(masterserver ${CMAKE_THREAD_LIBS_INIT})\n        if(BUILD_MASTER_TEST)\n            target_link_libraries(ServerTest ${CMAKE_THREAD_LIBS_INIT})\n        endif()\n    endif(NOT APPLE)\nendif(UNIX)\n\nif(WIN32) \n    target_link_libraries(masterserver wsock32) \nendif(WIN32)\n"
  },
  {
    "path": "apps/master/MasterServer.cpp",
    "content": "#include <RakPeerInterface.h>\n#include <RakSleep.h>\n#include <BitStream.h>\n#include <iostream>\n#include \"MasterServer.hpp\"\n\n#include <components/openmw-mp/Master/PacketMasterQuery.hpp>\n#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>\n#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>\n#include <components/openmw-mp/Version.hpp>\n\nusing namespace RakNet;\nusing namespace std;\nusing namespace mwmp;\nusing namespace chrono;\n\nMasterServer::MasterServer(unsigned short maxConnections, unsigned short port)\n{\n    peer = RakPeerInterface::GetInstance();\n    sockdescr = SocketDescriptor(port, 0);\n    peer->Startup(maxConnections, &sockdescr, 1, 1000);\n\n    peer->SetMaximumIncomingConnections(maxConnections);\n    peer->SetIncomingPassword(TES3MP_MASTERSERVER_PASSW, (int) strlen(TES3MP_MASTERSERVER_PASSW));\n    run = false;\n}\n\nMasterServer::~MasterServer()\n{\n    Stop(true);\n}\n\nusing namespace chrono;\n\nvoid MasterServer::Thread()\n{\n    unsigned char packetId = 0;\n\n    auto startTime = chrono::steady_clock::now();\n\n    BitStream send;\n    PacketMasterQuery pmq(peer);\n    pmq.SetSendStream(&send);\n\n    PacketMasterUpdate pmu(peer);\n    pmu.SetSendStream(&send);\n\n    PacketMasterAnnounce pma(peer);\n    pma.SetSendStream(&send);\n\n    while (run)\n    {\n        Packet *packet = peer->Receive();\n\n        auto now = steady_clock::now();\n        if (now - startTime >= 60s)\n        {\n            startTime = steady_clock::now();\n            for (auto it = servers.begin(); it != servers.end();)\n            {\n\n                if (it->second.lastUpdate + 60s <= now)\n                    servers.erase(it++);\n                else ++it;\n            }\n            for(auto id = pendingACKs.begin(); id != pendingACKs.end();)\n            {\n                if(now - id->second >= 30s)\n                {\n                    cout << \"timeout: \" << peer->GetSystemAddressFromGuid(id->first).ToString() << endl;\n                    peer->CloseConnection(id->first, true);\n                    id = pendingACKs.erase(id);\n                }\n                else\n                    ++id;\n            }\n        }\n\n        if (packet == nullptr)\n            RakSleep(10);\n        else\n            for (; packet; peer->DeallocatePacket(packet), packet = peer->Receive())\n            {\n                BitStream data(packet->data, packet->length, false);\n                data.Read(packetId);\n                switch (packetId)\n                {\n                    case ID_NEW_INCOMING_CONNECTION:\n                        cout << \"New incoming connection: \" << packet->systemAddress.ToString() << endl;\n                        break;\n                    case ID_DISCONNECTION_NOTIFICATION:\n                        cout << \"Disconnected: \" << packet->systemAddress.ToString() << endl;\n                        break;\n                    case ID_CONNECTION_LOST:\n                        cout << \"Connection lost: \" << packet->systemAddress.ToString() << endl;\n                        break;\n                    case ID_MASTER_QUERY:\n                    {\n                        pmq.SetServers(reinterpret_cast<map<SystemAddress, QueryData> *>(&servers));\n                        pmq.Send(packet->systemAddress);\n                        pendingACKs[packet->guid] = steady_clock::now();\n\n                        cout << \"Sent info about all \" << servers.size() << \" servers to \"\n                             << packet->systemAddress.ToString() << endl;\n                        break;\n                    }\n                    case ID_MASTER_UPDATE:\n                    {\n                        SystemAddress addr;\n                        data.Read(addr); // update 1 server\n\n                        ServerIter it = servers.find(addr);\n                        if (it != servers.end())\n                        {\n                            pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));\n                            pmu.SetServer(&pairPtr);\n                            pmu.Send(packet->systemAddress);\n                            pendingACKs[packet->guid] = steady_clock::now();\n                            cout << \"Sent info about \" << addr.ToString() << \" to \" << packet->systemAddress.ToString()\n                                 << endl;\n                        }\n                        break;\n                    }\n                    case ID_MASTER_ANNOUNCE:\n                    {\n                        ServerIter iter = servers.find(packet->systemAddress);\n\n                        pma.SetReadStream(&data);\n                        SServer server;\n                        pma.SetServer(&server);\n                        pma.Read();\n\n                        auto keepAliveFunc = [&]() {\n                            iter->second.lastUpdate = now;\n                            pma.SetFunc(PacketMasterAnnounce::FUNCTION_KEEP);\n                            pma.Send(packet->systemAddress);\n                            pendingACKs[packet->guid] = steady_clock::now();\n                        };\n\n                        if (iter != servers.end())\n                        {\n                            if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)\n                            {\n                                servers.erase(iter);\n                                cout << \"Deleted\";\n                                pma.Send(packet->systemAddress);\n                                pendingACKs[packet->guid] = steady_clock::now();\n                            }\n                            else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)\n                            {\n                                cout << \"Updated\";\n                                iter->second = server;\n                                keepAliveFunc();\n                            }\n                            else\n                            {\n                                cout << \"Keeping alive\";\n                                keepAliveFunc();\n                            }\n                        }\n                        else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)\n                        {\n                            cout << \"Added\";\n                            iter = servers.insert({packet->systemAddress, server}).first;\n                            keepAliveFunc();\n                        }\n                        else\n                        {\n                            cout << \"Unknown\";\n                            pma.SetFunc(PacketMasterAnnounce::FUNCTION_DELETE);\n                            pma.Send(packet->systemAddress);\n                            pendingACKs[packet->guid] = steady_clock::now();\n                        }\n                        cout << \" server \" << packet->systemAddress.ToString() << endl;\n                        break;\n                    }\n                    case ID_SND_RECEIPT_ACKED:\n                        uint32_t num;\n                        memcpy(&num, packet->data+1, 4);\n                        cout << \"Packet with id \" << num << \" was delivered.\" << endl;\n                        pendingACKs.erase(packet->guid);\n                        peer->CloseConnection(packet->systemAddress, true);\n                        break;\n                    default:\n                        cout << \"Wrong packet. id \" << (unsigned) packet->data[0] << \" packet length \" << packet->length << \" from \" << packet->systemAddress.ToString() << endl;\n                        peer->CloseConnection(packet->systemAddress, true);\n                }\n            }\n    }\n    peer->Shutdown(1000);\n    RakPeerInterface::DestroyInstance(peer);\n    cout << \"Server thread stopped\" << endl;\n}\n\nvoid MasterServer::Start()\n{\n    if (!run)\n    {\n        run = true;\n        tMasterThread = thread(&MasterServer::Thread, this);\n        cout << \"Started\" << endl;\n    }\n}\n\nvoid MasterServer::Stop(bool wait)\n{\n    if (run)\n    {\n        run = false;\n        if (wait && tMasterThread.joinable())\n            tMasterThread.join();\n    }\n}\n\nbool MasterServer::isRunning()\n{\n    return run;\n}\n\nvoid MasterServer::Wait()\n{\n    if (run)\n    {\n        if (tMasterThread.joinable())\n            tMasterThread.join();\n    }\n}\n\nMasterServer::ServerMap *MasterServer::GetServers()\n{\n    return &servers;\n}\n"
  },
  {
    "path": "apps/master/MasterServer.hpp",
    "content": "#ifndef NEWMASTERPROTO_MASTERSERVER_HPP\n#define NEWMASTERPROTO_MASTERSERVER_HPP\n\n#include <thread>\n#include <chrono>\n#include <RakPeerInterface.h>\n#include <components/openmw-mp/Master/MasterData.hpp>\n\nclass MasterServer\n{\npublic:\n    struct Ban\n    {\n        RakNet::SystemAddress sa;\n        bool permanent;\n        struct Date\n        {\n        } date;\n    };\n    struct SServer : QueryData\n    {\n        std::chrono::steady_clock::time_point lastUpdate;\n    };\n    typedef std::map<RakNet::SystemAddress, SServer> ServerMap;\n    //typedef ServerMap::const_iterator ServerCIter;\n    typedef ServerMap::iterator ServerIter;\n\n    MasterServer(unsigned short maxConnections, unsigned short port);\n    ~MasterServer();\n\n    void Start();\n    void Stop(bool wait = false);\n    bool isRunning();\n    void Wait();\n\n    ServerMap* GetServers();\n\nprivate:\n    void Thread();\n\nprivate:\n    std::thread tMasterThread;\n    RakNet::RakPeerInterface* peer;\n    RakNet::SocketDescriptor sockdescr;\n    ServerMap servers;\n    bool run;\n    std::map<RakNet::RakNetGUID, std::chrono::steady_clock::time_point> pendingACKs;\n};\n\n\n#endif //NEWMASTERPROTO_MASTERSERVER_HPP\n"
  },
  {
    "path": "apps/master/RestServer.cpp",
    "content": "#include \"RestServer.hpp\"\n\n#include <boost/property_tree/ptree.hpp>\n#include <boost/property_tree/json_parser.hpp>\n\nusing namespace std;\nusing namespace chrono;\nusing namespace boost::property_tree;\n\nstatic string response201 = \"HTTP/1.1 201 Created\\r\\nContent-Length: 7\\r\\n\\r\\nCreated\";\nstatic string response202 = \"HTTP/1.1 202 Accepted\\r\\nContent-Length: 8\\r\\n\\r\\nAccepted\";\nstatic string response400 = \"HTTP/1.1 400 Bad Request\\r\\nContent-Length: 11\\r\\n\\r\\nbad request\";\n\ninline void ResponseStr(HttpServer::Response &response, string content, string type = \"\", string code = \"200 OK\")\n{\n    response << \"HTTP/1.1 \" << code << \"\\r\\n\";\n    if (!type.empty())\n        response << \"Content-Type: \" << type <<\"\\r\\n\";\n    response << \"Content-Length: \" << content.length() << \"\\r\\n\\r\\n\" << content;\n}\n\ninline void ptreeToServer(boost::property_tree::ptree &pt, MasterServer::SServer &server)\n{\n    server.SetName(pt.get<string>(\"hostname\").c_str());\n    server.SetGameMode(pt.get<string>(\"modname\").c_str());\n    server.SetVersion(pt.get<string>(\"version\").c_str());\n    server.SetPassword(pt.get<bool>(\"passw\"));\n    //server.query_port = pt.get<unsigned short>(\"query_port\");\n    server.SetPlayers(pt.get<unsigned>(\"players\"));\n    server.SetMaxPlayers(pt.get<unsigned>(\"max_players\"));\n}\n\ninline void queryToStringStream(stringstream &ss, string addr, MasterServer::SServer &query)\n{\n    ss <<\"\\\"\" << addr << \"\\\":{\";\n    ss << \"\\\"modname\\\": \\\"\" << query.GetGameMode() << \"\\\"\" << \", \";\n    ss << \"\\\"passw\\\": \" << (query.GetPassword() ? \"true\" : \"false\") << \", \";\n    ss << \"\\\"hostname\\\": \\\"\" << query.GetName() << \"\\\"\" << \", \";\n    ss << \"\\\"query_port\\\": \" << 0 << \", \";\n    ss << \"\\\"last_update\\\": \" << duration_cast<seconds>(steady_clock::now() - query.lastUpdate).count() << \", \";\n    ss << \"\\\"players\\\": \" << query.GetPlayers() << \", \";\n    ss << \"\\\"version\\\": \\\"\" << query.GetVersion() << \"\\\"\" << \", \";\n    ss << \"\\\"max_players\\\": \" << query.GetMaxPlayers();\n    ss << \"}\";\n}\n\nRestServer::RestServer(unsigned short port, MasterServer::ServerMap *pMap) : serverMap(pMap)\n{\n    httpServer.config.port = port;\n}\n\nvoid RestServer::start()\n{\n    static const string ValidIpAddressRegex = \"(?:[0-9]{1,3}\\\\.){3}[0-9]{1,3}\";\n    static const string ValidPortRegex = \"(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$\";\n    static const string ServersRegex = \"^/api/servers(?:/(\" + ValidIpAddressRegex + \"\\\\:\" + ValidPortRegex + \"))?\";\n\n    httpServer.resource[ServersRegex][\"GET\"] = [this](auto response, auto request) {\n        if (request->path_match[1].length() > 0)\n        {\n            try\n            {\n                stringstream ss;\n                ss << \"{\";\n                auto addr = request->path_match[1].str();\n                auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));\n                queryToStringStream(ss, \"server\", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));\n                ss << \"}\";\n                ResponseStr(*response, ss.str(), \"application/json\");\n            }\n            catch(out_of_range e)\n            {\n                *response << response400;\n            }\n        }\n        else\n        {\n            static string str;\n\n            //if (updatedCache)\n            {\n                stringstream ss;\n                ss << \"{\";\n                ss << \"\\\"list servers\\\":{\";\n                for (auto query = serverMap->begin(); query != serverMap->end(); query++)\n                {\n                    queryToStringStream(ss, query->first.ToString(true, ':'), query->second);\n                    if (next(query) != serverMap->end())\n                        ss << \", \";\n                }\n                ss << \"}}\";\n                ResponseStr(*response, ss.str(), \"application/json\");\n                updatedCache = false;\n            }\n            *response << str;\n        }\n    };\n\n    //Add query for < 0.6 servers\n    httpServer.resource[ServersRegex][\"POST\"] = [this](auto response, auto request) {\n        try\n        {\n            ptree pt;\n            read_json(request->content, pt);\n\n            MasterServer::SServer server;\n            ptreeToServer(pt, server);\n\n            unsigned short port = pt.get<unsigned short>(\"port\");\n            server.lastUpdate = steady_clock::now();\n            serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});\n            updatedCache = true;\n\n            *response << response201;\n        }\n        catch (exception& e)\n        {\n            cout << e.what() << endl;\n            *response << response400;\n        }\n    };\n\n    //Update query for < 0.6 servers\n    httpServer.resource[ServersRegex][\"PUT\"] = [this](auto response, auto request) {\n        auto addr = request->path_match[1].str();\n        auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));\n\n        auto query = serverMap->find(RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port));\n\n        if (query == serverMap->end())\n        {\n            cout << request->remote_endpoint_address + \": Trying to update a non-existent server or without permissions.\" << endl;\n            *response << response400;\n            return;\n        }\n\n        if (request->content.size() != 0)\n        {\n            try\n            {\n                ptree pt;\n                read_json(request->content, pt);\n\n                ptreeToServer(pt, query->second);\n\n                updatedCache = true;\n            }\n            catch(exception &e)\n            {\n                cout << e.what() << endl;\n                *response << response400;\n            }\n        }\n\n        query->second.lastUpdate = steady_clock::now();\n\n        *response << response202;\n    };\n\n    httpServer.resource[\"/api/servers/info\"][\"GET\"] = [this](auto response, auto /*request*/) {\n        stringstream ss;\n        ss << '{';\n        ss << \"\\\"servers\\\": \" << serverMap->size();\n        unsigned int players = 0;\n        for (auto s : *serverMap)\n            players += s.second.GetPlayers();\n        ss << \", \\\"players\\\": \" << players;\n        ss << \"}\";\n\n        ResponseStr(*response, ss.str(), \"application/json\");\n    };\n\n    httpServer.default_resource[\"GET\"]=[](auto response, auto /*request*/) {\n        *response << response400;\n    };\n\n    httpServer.start();\n}\n\nvoid RestServer::cacheUpdated()\n{\n    updatedCache = true;\n}\n\nvoid RestServer::stop()\n{\n    httpServer.stop();\n}\n"
  },
  {
    "path": "apps/master/RestServer.hpp",
    "content": "#ifndef NEWRESTAPI_RESTSERVER_HPP\n#define NEWRESTAPI_RESTSERVER_HPP\n\n#include <string>\n#include <unordered_map>\n#include \"MasterServer.hpp\"\n#include \"SimpleWeb/http_server.hpp\"\n\ntypedef SimpleWeb::Server<SimpleWeb::HTTP> HttpServer;\n\nclass RestServer\n{\npublic:\n    RestServer(unsigned short port, MasterServer::ServerMap *pMap);\n    void start();\n    void stop();\n    void cacheUpdated();\n\nprivate:\n    HttpServer httpServer;\n    MasterServer::ServerMap *serverMap;\n    bool updatedCache = true;\n};\n\n\n#endif //NEWRESTAPI_RESTSERVER_HPP\n"
  },
  {
    "path": "apps/master/ServerTest.cpp",
    "content": "#include <RakPeerInterface.h>\n#include <RakSleep.h>\n#include <BitStream.h>\n#include <iostream>\n#include <Kbhit.h>\n#include <Gets.h>\n#include <components/openmw-mp/Master/MasterData.hpp>\n#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>\n#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>\n#include <components/openmw-mp/Master/PacketMasterQuery.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace std;\nusing namespace RakNet;\nusing namespace mwmp;\n\nint main()\n{\n    cout << \"Server test\" << endl;\n\n    SystemAddress masterAddr(\"127.0.0.1\", 25560);\n\n    RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();\n\n    RakNet::SocketDescriptor sd(25565, 0);\n    peer->Startup(8, &sd, 1);\n\n    ConnectionAttemptResult result = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), \"pass\",\n                                                   (int)(strlen(\"pass\")), 0, 0, 5, 500);\n\n    assert(result == RakNet::CONNECTION_ATTEMPT_STARTED);\n\n    char message[2048];\n    BitStream send;\n\n    PacketMasterQuery pmq(peer);\n    pmq.SetSendStream(&send);\n\n    PacketMasterAnnounce pma(peer);\n    pma.SetSendStream(&send);\n\n    while (true)\n    {\n        RakSleep(30);\n\n        if (kbhit())\n        {\n            Gets(message, sizeof(message));\n\n            if (strcmp(message, \"quit\") == 0)\n            {\n                puts(\"Quitting.\");\n                break;\n            }\n            else if (strcmp(message, \"send\") == 0)\n            {\n                puts(\"Sending data about server\");\n                QueryData server;\n                server.SetName(\"Super Server\");\n                server.SetPlayers(0);\n                server.SetMaxPlayers(0);\n\n                pma.SetServer(&server);\n                pma.SetFunc(PacketMasterAnnounce::FUNCTION_ANNOUNCE);\n                pma.Send(masterAddr);\n            }\n            else if (strcmp(message, \"get\") == 0)\n            {\n                puts(\"Request query info\");\n                send.Reset();\n                send.Write((unsigned char) (ID_MASTER_QUERY));\n                peer->Send(&send, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);\n            }\n            else if (strcmp(message, \"getme\") == 0)\n            {\n                send.Reset();\n                send.Write((unsigned char) (ID_MASTER_UPDATE));\n                send.Write(SystemAddress(\"127.0.0.1\", 25565));\n                peer->Send(&send, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);\n            }\n            else if (strcmp(message, \"status\") == 0)\n            {\n                cout << (peer->GetConnectionState(masterAddr) == IS_CONNECTED ? \"Connected\" : \"Not connected\") << endl;\n            }\n            else if (strcmp(message, \"keep\") == 0)\n            {\n                cout << \"Sending keep alive\" << endl;\n                pma.SetFunc(PacketMasterAnnounce::FUNCTION_KEEP);\n                pma.Send(masterAddr);\n            }\n        }\n\n        for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())\n        {\n            BitStream data(packet->data, packet->length, false);\n            unsigned char packetID;\n            data.Read(packetID);\n            switch (packetID)\n            {\n                case ID_DISCONNECTION_NOTIFICATION:\n                    // Connection lost normally\n                    printf(\"ID_DISCONNECTION_NOTIFICATION\\n\");\n                    break;\n                case ID_ALREADY_CONNECTED:\n                    // Connection lost normally\n                    printf(\"ID_ALREADY_CONNECTED with guid %lu\\n\", packet->guid.g);\n                    break;\n                case ID_INCOMPATIBLE_PROTOCOL_VERSION:\n                    printf(\"ID_INCOMPATIBLE_PROTOCOL_VERSION\\n\");\n                    break;\n                case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully.  You can manually broadcast this in a peer to peer enviroment if you want.\n                    printf(\"ID_REMOTE_DISCONNECTION_NOTIFICATION\\n\");\n                    break;\n                case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully.  You can manually broadcast this in a peer to peer enviroment if you want.\n                    printf(\"ID_REMOTE_CONNECTION_LOST\\n\");\n                    break;\n                case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting.  You can manually broadcast this in a peer to peer enviroment if you want.\n                    printf(\"ID_REMOTE_NEW_INCOMING_CONNECTION\\n\");\n                    break;\n                case ID_CONNECTION_BANNED: // Banned from this server\n                    printf(\"We are banned from this server.\\n\");\n                    break;\n                case ID_CONNECTION_ATTEMPT_FAILED:\n                    printf(\"Connection attempt failed\\n\");\n                    break;\n                case ID_NO_FREE_INCOMING_CONNECTIONS:\n                    // Sorry, the server is full.  I don't do anything here but\n                    // A real app should tell the user\n                    printf(\"ID_NO_FREE_INCOMING_CONNECTIONS\\n\");\n                    break;\n\n                case ID_INVALID_PASSWORD:\n                    printf(\"ID_INVALID_PASSWORD\\n\");\n                    break;\n\n                case ID_CONNECTION_LOST:\n                    // Couldn't deliver a reliable packet - i.e. the other system was abnormally\n                    // terminated\n                    printf(\"ID_CONNECTION_LOST\\n\");\n                    return 0;\n                    break;\n\n                case ID_CONNECTION_REQUEST_ACCEPTED:\n                    // This tells the client they have connected\n                    printf(\"ID_CONNECTION_REQUEST_ACCEPTED to %s with GUID %s\\n\", packet->systemAddress.ToString(true),\n                           packet->guid.ToString());\n                    printf(\"My external address is %s\\n\", peer->GetExternalID(packet->systemAddress).ToString(true));\n                    break;\n                case ID_MASTER_QUERY:\n                {\n                    map<SystemAddress, QueryData> servers;\n\n                    pmq.SetReadStream(&data);\n                    pmq.SetServers(&servers);\n                    pmq.Read();\n\n                    cout << \"Received query data about \" << servers.size() << \" servers\" << endl;\n\n                    for (auto serv : servers)\n                        cout << serv.second.GetName() << endl;\n\n                    break;\n                }\n                case ID_MASTER_UPDATE:\n                {\n                    pair<SystemAddress, QueryData> serverPair;\n                    PacketMasterUpdate pmu(peer);\n                    pmu.SetReadStream(&data);\n                    pmu.SetServer(&serverPair);\n                    pmu.Read();\n                    cout << \"Received info about \" << serverPair.first.ToString() << endl;\n                    cout << serverPair.second.GetName() << endl;\n                    break;\n                }\n                default:\n                    cout << \"Wrong packet\" << endl;\n            }\n        }\n    }\n    peer->Shutdown(1000);\n    RakPeerInterface::DestroyInstance(peer);\n}\n"
  },
  {
    "path": "apps/master/SimpleWeb/base_server.hpp",
    "content": "#ifndef BASE_SERVER_HPP\n#define BASE_SERVER_HPP\n\n#include <boost/asio.hpp>\n#include <boost/algorithm/string/predicate.hpp>\n#include <boost/functional/hash.hpp>\n\n#include <map>\n#include <unordered_map>\n#include <thread>\n#include <functional>\n#include <iostream>\n#include <sstream>\n#include <regex>\n\n#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH\n#define CASE_INSENSITIVE_EQUALS_AND_HASH\n\n//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html\nstruct case_insensitive_equals\n{\n    bool operator()(const std::string &key1, const std::string &key2) const\n    {\n        return boost::algorithm::iequals(key1, key2);\n    }\n};\n\nstruct case_insensitive_hash\n{\n    size_t operator()(const std::string &key) const\n    {\n        std::size_t seed = 0;\n        for (auto &c: key)\n            boost::hash_combine(seed, std::tolower(c));\n        return seed;\n    }\n};\n\n#endif\n\nnamespace SimpleWeb\n{\n    template<class socket_type>\n    class Server;\n\n    template<class socket_type>\n    class ServerBase\n    {\n    public:\n        virtual ~ServerBase()\n        {}\n\n        class Response : public std::ostream\n        {\n            friend class ServerBase<socket_type>;\n\n            boost::asio::streambuf streambuf;\n\n            std::shared_ptr<socket_type> socket;\n\n            Response(const std::shared_ptr<socket_type> &socket) : std::ostream(&streambuf), socket(socket)\n            {}\n\n        public:\n            size_t size()\n            {\n                return streambuf.size();\n            }\n\n            /// If true, force server to close the connection after the response have been sent.\n            ///\n            /// This is useful when implementing a HTTP/1.0-server sending content\n            /// without specifying the content length.\n            bool close_connection_after_response = false;\n        };\n\n        class Content : public std::istream\n        {\n            friend class ServerBase<socket_type>;\n\n        public:\n            size_t size()\n            {\n                return streambuf.size();\n            }\n\n            std::string string()\n            {\n                std::stringstream ss;\n                ss << rdbuf();\n                return ss.str();\n            }\n\n        private:\n            boost::asio::streambuf &streambuf;\n\n            Content(boost::asio::streambuf &streambuf) : std::istream(&streambuf), streambuf(streambuf)\n            {}\n        };\n\n        class Request\n        {\n            friend class ServerBase<socket_type>;\n\n            friend class Server<socket_type>;\n\n        public:\n            std::string method, path, http_version;\n\n            Content content;\n\n            std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;\n\n            std::smatch path_match;\n\n            std::string remote_endpoint_address;\n            unsigned short remote_endpoint_port;\n\n        private:\n            Request(const socket_type &socket) : content(streambuf)\n            {\n                try\n                {\n                    remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();\n                    remote_endpoint_port = socket.lowest_layer().remote_endpoint().port();\n                }\n                catch (...)\n                {}\n            }\n\n            boost::asio::streambuf streambuf;\n        };\n\n        class Config\n        {\n            friend class ServerBase<socket_type>;\n\n            Config(unsigned short port) : port(port)\n            {}\n\n        public:\n            /// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS.\n            unsigned short port;\n            /// Number of threads that the server will use when start() is called. Defaults to 1 thread.\n            size_t thread_pool_size = 1;\n            /// Timeout on request handling. Defaults to 5 seconds.\n            size_t timeout_request = 5;\n            /// Timeout on content handling. Defaults to 300 seconds.\n            size_t timeout_content = 300;\n            /// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.\n            /// If empty, the address will be any address.\n            std::string address;\n            /// Set to false to avoid binding the socket to an address that is already in use. Defaults to true.\n            bool reuse_address = true;\n        };\n\n        ///Set before calling start().\n        Config config;\n\n    private:\n        class regex_orderable : public std::regex\n        {\n            std::string str;\n        public:\n            regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr)\n            {}\n\n            regex_orderable(const std::string &regex_str) : std::regex(regex_str), str(regex_str)\n            {}\n\n            bool operator<(const regex_orderable &rhs) const\n            {\n                return str < rhs.str;\n            }\n        };\n\n    public:\n        /// Warning: do not add or remove resources after start() is called\n        std::map<regex_orderable, std::map<std::string,\n                std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,\n                                   std::shared_ptr<typename ServerBase<socket_type>::Request>)>>>\n                resource;\n\n        std::map<std::string,\n                std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,\n                                   std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;\n\n        std::function<\n                void(std::shared_ptr<typename ServerBase<socket_type>::Request>,\n                     const boost::system::error_code &)>\n                on_error;\n\n        std::function<void(std::shared_ptr<socket_type> socket,\n                           std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;\n\n        virtual void start()\n        {\n            if (!io_service)\n                io_service = std::make_shared<boost::asio::io_service>();\n\n            if (io_service->stopped())\n                io_service->reset();\n\n            boost::asio::ip::tcp::endpoint endpoint;\n            if (config.address.size() > 0)\n                endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address),\n                                                          config.port);\n            else\n                endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);\n\n            if (!acceptor)\n                acceptor = std::unique_ptr<boost::asio::ip::tcp::acceptor>(\n                        new boost::asio::ip::tcp::acceptor(*io_service));\n            acceptor->open(endpoint.protocol());\n            acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address));\n            acceptor->bind(endpoint);\n            acceptor->listen();\n\n            accept();\n\n            //If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling\n            threads.clear();\n            for (size_t c = 1; c < config.thread_pool_size; c++)\n            {\n                threads.emplace_back([this]()\n                                     {\n                                         io_service->run();\n                                     });\n            }\n\n            //Main thread\n            if (config.thread_pool_size > 0)\n                io_service->run();\n\n            //Wait for the rest of the threads, if any, to finish as well\n            for (auto &t: threads)\n            {\n                t.join();\n            }\n        }\n\n        void stop()\n        {\n            acceptor->close();\n            if (config.thread_pool_size > 0)\n                io_service->stop();\n        }\n\n        ///Use this function if you need to recursively send parts of a longer message\n        void send(const std::shared_ptr<Response> &response,\n                  const std::function<void(const boost::system::error_code &)> &callback = nullptr) const\n        {\n            boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback]\n                    (const boost::system::error_code &ec, size_t /*bytes_transferred*/)\n            {\n                if (callback)\n                    callback(ec);\n            });\n        }\n\n        /// If you have your own boost::asio::io_service, store its pointer here before running start().\n        /// You might also want to set config.thread_pool_size to 0.\n        std::shared_ptr<boost::asio::io_service> io_service;\n    protected:\n        std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;\n        std::vector<std::thread> threads;\n\n        ServerBase(unsigned short port) : config(port)\n        {}\n\n        virtual void accept()=0;\n\n        std::shared_ptr<boost::asio::deadline_timer>\n        get_timeout_timer(const std::shared_ptr<socket_type> &socket, long seconds)\n        {\n            if (seconds == 0)\n                return nullptr;\n\n            auto timer = std::make_shared<boost::asio::deadline_timer>(*io_service);\n            timer->expires_from_now(boost::posix_time::seconds(seconds));\n            timer->async_wait([socket](const boost::system::error_code &ec)\n                              {\n                                  if (!ec)\n                                  {\n                                      boost::system::error_code ec;\n                                      socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);\n                                      socket->lowest_layer().close();\n                                  }\n                              });\n            return timer;\n        }\n\n        void read_request_and_content(const std::shared_ptr<socket_type> &socket)\n        {\n            //Create new streambuf (Request::streambuf) for async_read_until()\n            //shared_ptr is used to pass temporary objects to the asynchronous functions\n            std::shared_ptr<Request> request(new Request(*socket));\n\n            //Set timeout on the following boost::asio::async-read or write function\n            auto timer = this->get_timeout_timer(socket, config.timeout_request);\n\n            boost::asio::async_read_until(*socket, request->streambuf, \"\\r\\n\\r\\n\", [this, socket, request, timer]\n                    (const boost::system::error_code &ec,\n                     size_t bytes_transferred)\n            {\n                if (timer)\n                    timer->cancel();\n                if (!ec)\n                {\n                    //request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:\n                    //\"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter\"\n                    //The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the\n                    //streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).\n                    size_t num_additional_bytes =\n                            request->streambuf.size() - bytes_transferred;\n\n                    if (!this->parse_request(request))\n                        return;\n\n                    //If content, read that as well\n                    auto it = request->header.find(\"Content-Length\");\n                    if (it != request->header.end())\n                    {\n                        unsigned long long content_length;\n                        try\n                        {\n                            content_length = stoull(it->second);\n                        }\n                        catch (const std::exception &e)\n                        {\n                            if (on_error)\n                                on_error(request, boost::system::error_code(\n                                        boost::system::errc::protocol_error,\n                                        boost::system::generic_category()));\n                            return;\n                        }\n                        if (content_length > num_additional_bytes)\n                        {\n                            //Set timeout on the following boost::asio::async-read or write function\n                            auto timer = this->get_timeout_timer(socket,\n                                                                 config.timeout_content);\n                            boost::asio::async_read(*socket, request->streambuf,\n                                                    boost::asio::transfer_exactly(\n                                                            content_length -\n                                                            num_additional_bytes),\n                                                    [this, socket, request, timer]\n                                                            (const boost::system::error_code &ec,\n                                                             size_t /*bytes_transferred*/)\n                                                    {\n                                                        if (timer)\n                                                            timer->cancel();\n                                                        if (!ec)\n                                                            this->find_resource(socket,\n                                                                                request);\n                                                        else if (on_error)\n                                                            on_error(request, ec);\n                                                    });\n                        }\n                        else\n                            this->find_resource(socket, request);\n                    }\n                    else\n                        this->find_resource(socket, request);\n                }\n                else if (on_error)\n                    on_error(request, ec);\n            });\n        }\n\n        bool parse_request(const std::shared_ptr<Request> &request) const\n        {\n            std::string line;\n            getline(request->content, line);\n            size_t method_end;\n            if ((method_end = line.find(' ')) != std::string::npos)\n            {\n                size_t path_end;\n                if ((path_end = line.find(' ', method_end + 1)) != std::string::npos)\n                {\n                    request->method = line.substr(0, method_end);\n                    request->path = line.substr(method_end + 1, path_end - method_end - 1);\n\n                    size_t protocol_end;\n                    if ((protocol_end = line.find('/', path_end + 1)) != std::string::npos)\n                    {\n                        if (line.compare(path_end + 1, protocol_end - path_end - 1, \"HTTP\") != 0)\n                            return false;\n                        request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);\n                    }\n                    else\n                        return false;\n\n                    getline(request->content, line);\n                    size_t param_end;\n                    while ((param_end = line.find(':')) != std::string::npos)\n                    {\n                        size_t value_start = param_end + 1;\n                        if ((value_start) < line.size())\n                        {\n                            if (line[value_start] == ' ')\n                                value_start++;\n                            if (value_start < line.size())\n                                request->header.emplace(line.substr(0, param_end),\n                                                        line.substr(value_start, line.size() - value_start - 1));\n                        }\n\n                        getline(request->content, line);\n                    }\n                }\n                else\n                    return false;\n            }\n            else\n                return false;\n            return true;\n        }\n\n        void find_resource(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request)\n        {\n            //Upgrade connection\n            if (on_upgrade)\n            {\n                auto it = request->header.find(\"Upgrade\");\n                if (it != request->header.end())\n                {\n                    on_upgrade(socket, request);\n                    return;\n                }\n            }\n            //Find path- and method-match, and call write_response\n            for (auto &regex_method: resource)\n            {\n                auto it = regex_method.second.find(request->method);\n                if (it != regex_method.second.end())\n                {\n                    std::smatch sm_res;\n                    if (std::regex_match(request->path, sm_res, regex_method.first))\n                    {\n                        request->path_match = std::move(sm_res);\n                        write_response(socket, request, it->second);\n                        return;\n                    }\n                }\n            }\n            auto it = default_resource.find(request->method);\n            if (it != default_resource.end())\n            {\n                write_response(socket, request, it->second);\n            }\n        }\n\n        void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request,\n                            std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,\n                                               std::shared_ptr<\n                                                       typename ServerBase<socket_type>::Request>)> &resource_function)\n        {\n            //Set timeout on the following boost::asio::async-read or write function\n            auto timer = this->get_timeout_timer(socket, config.timeout_content);\n\n            auto response = std::shared_ptr<Response>(new Response(socket), [this, request, timer]\n                    (Response *response_ptr)\n            {\n                auto response = std::shared_ptr<Response>(response_ptr);\n                this->send(response, [this, response, request, timer](\n                        const boost::system::error_code &ec)\n                {\n                    if (timer)\n                        timer->cancel();\n                    if (!ec)\n                    {\n                        if (response->close_connection_after_response)\n                            return;\n\n                        auto range = request->header.equal_range(\n                                \"Connection\");\n                        for (auto it = range.first; it != range.second; it++)\n                        {\n                            if (boost::iequals(it->second, \"close\"))\n                            {\n                                return;\n                            }\n                            else if (boost::iequals(it->second, \"keep-alive\"))\n                            {\n                                this->read_request_and_content(\n                                        response->socket);\n                                return;\n                            }\n                        }\n                        if (request->http_version >= \"1.1\")\n                            this->read_request_and_content(response->socket);\n                    }\n                    else if (on_error)\n                        on_error(request, ec);\n                });\n            });\n\n            try\n            {\n                resource_function(response, request);\n            }\n            catch (const std::exception &e)\n            {\n                if (on_error)\n                    on_error(request, boost::system::error_code(boost::system::errc::operation_canceled,\n                                                                boost::system::generic_category()));\n                return;\n            }\n        }\n    };\n}\n#endif //BASE_SERVER_HPP\n"
  },
  {
    "path": "apps/master/SimpleWeb/http_server.hpp",
    "content": "/*\n *  https://github.com/eidheim/Simple-Web-Server/\n *\n *  The MIT License (MIT)\n *  Copyright (c) 2014-2016 Ole Christian Eidheim\n */\n\n#ifndef SERVER_HTTP_HPP\n#define SERVER_HTTP_HPP\n\n#include \"base_server.hpp\"\n\nnamespace SimpleWeb\n{\n\n    template<class socket_type>\n    class Server : public ServerBase<socket_type> {};\n\n    typedef boost::asio::ip::tcp::socket HTTP;\n\n    template<>\n    class Server<HTTP> : public ServerBase<HTTP>\n    {\n    public:\n        Server() : ServerBase<HTTP>::ServerBase(80)\n        {}\n\n    protected:\n        virtual void accept()\n        {\n            //Create new socket for this connection\n            //Shared_ptr is used to pass temporary objects to the asynchronous functions\n            auto socket = std::make_shared<HTTP>(*io_service);\n\n            acceptor->async_accept(*socket, [this, socket](const boost::system::error_code &ec)\n            {\n                //Immediately start accepting a new connection (if io_service hasn't been stopped)\n                if (ec != boost::asio::error::operation_aborted)\n                    accept();\n\n                if (!ec)\n                {\n                    boost::asio::ip::tcp::no_delay option(true);\n                    socket->set_option(option);\n\n                    this->read_request_and_content(socket);\n                }\n                else if (on_error)\n                    on_error(std::shared_ptr<Request>(new Request(*socket)), ec);\n            });\n        }\n    };\n}\n\n#endif //SERVER_HTTP_HPP\n"
  },
  {
    "path": "apps/master/SimpleWeb/https_server.hpp",
    "content": "#ifndef HTTPS_SERVER_HPP\n#define HTTPS_SERVER_HPP\n\n#include \"base_server.hpp\"\n#include <boost/asio/ssl.hpp>\n#include <openssl/ssl.h>\n#include <algorithm>\n\nnamespace SimpleWeb\n{\n    typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;\n\n    template<>\n    class Server<HTTPS> : public ServerBase<HTTPS>\n    {\n        std::string session_id_context;\n        bool set_session_id_context = false;\n    public:\n        Server(const std::string &cert_file, const std::string &private_key_file,\n               const std::string &verify_file = std::string()) : ServerBase<HTTPS>::ServerBase(443),\n                                                                 context(boost::asio::ssl::context::tlsv12)\n        {\n            context.use_certificate_chain_file(cert_file);\n            context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem);\n\n            if (verify_file.size() > 0)\n            {\n                context.load_verify_file(verify_file);\n                context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert |\n                                        boost::asio::ssl::verify_client_once);\n                set_session_id_context = true;\n            }\n        }\n\n        void start()\n        {\n            if (set_session_id_context)\n            {\n                // Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH\n                session_id_context = std::to_string(config.port) + ':';\n                session_id_context.append(config.address.rbegin(), config.address.rend());\n                SSL_CTX_set_session_id_context(context.native_handle(),\n                                               reinterpret_cast<const unsigned char *>(session_id_context.data()),\n                                               std::min<size_t>(session_id_context.size(),\n                                                                SSL_MAX_SSL_SESSION_ID_LENGTH));\n            }\n            ServerBase::start();\n        }\n\n    protected:\n        boost::asio::ssl::context context;\n\n        virtual void accept()\n        {\n            //Create new socket for this connection\n            //Shared_ptr is used to pass temporary objects to the asynchronous functions\n            auto socket = std::make_shared<HTTPS>(*io_service, context);\n\n            acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code &ec)\n            {\n                //Immediately start accepting a new connection (if io_service hasn't been stopped)\n                if (ec != boost::asio::error::operation_aborted)\n                    accept();\n\n\n                if (!ec)\n                {\n                    boost::asio::ip::tcp::no_delay option(true);\n                    socket->lowest_layer().set_option(option);\n\n                    //Set timeout on the following boost::asio::ssl::stream::async_handshake\n                    auto timer = get_timeout_timer(socket, config.timeout_request);\n                    socket->async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]\n                            (const boost::system::error_code &ec)\n                    {\n                        if (timer)\n                            timer->cancel();\n                        if (!ec)\n                            read_request_and_content(socket);\n                        else if (on_error)\n                            on_error(std::shared_ptr<Request>(new Request(*socket)), ec);\n                    });\n                }\n                else if (on_error)\n                    on_error(std::shared_ptr<Request>(new Request(*socket)), ec);\n            });\n        }\n    };\n}\n\n#endif //HTTPS_SERVER_HPP\n"
  },
  {
    "path": "apps/master/main.cpp",
    "content": "#include <iostream>\n#include <Kbhit.h>\n#include <RakSleep.h>\n#include \"MasterServer.hpp\"\n#include \"RestServer.hpp\"\n\nusing namespace RakNet;\nusing namespace std;\n\nunique_ptr<RestServer> restServer;\nunique_ptr<MasterServer> masterServer;\nbool run = true;\n\nint main()\n{\n    masterServer.reset(new MasterServer(2000, 25560));\n    restServer.reset(new RestServer(8080, masterServer->GetServers()));\n\n    auto onExit = [](int /*sig*/){\n        restServer->stop();\n        masterServer->Stop(false);\n        masterServer->Wait();\n        run = false;\n    };\n\n    signal(SIGINT, onExit);\n    signal(SIGTERM, onExit);\n\n    masterServer->Start();\n\n    thread server_thread([]() { restServer->start(); });\n\n    server_thread.join();\n    masterServer->Wait();\n\n    return 0;\n}\n"
  },
  {
    "path": "apps/mwiniimporter/CMakeLists.txt",
    "content": "set(MWINIIMPORT\n    main.cpp\n    importer.cpp\n)\n\nset(MWINIIMPORT_HEADER\n    importer.hpp\n)\n\nsource_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})\n\nopenmw_add_executable(openmw-iniimporter\n    ${MWINIIMPORT}\n)\n\ntarget_link_libraries(openmw-iniimporter\n    ${Boost_PROGRAM_OPTIONS_LIBRARY}\n    ${Boost_FILESYSTEM_LIBRARY}\n    components\n)\n\nif (WIN32)\n    target_link_libraries(openmw-iniimporter\n    ${Boost_LOCALE_LIBRARY})\n    INSTALL(TARGETS openmw-iniimporter RUNTIME DESTINATION \".\")\nendif(WIN32)\n\nif (MINGW)\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -municode\")\nendif()\n\nif (BUILD_WITH_CODE_COVERAGE)\n  add_definitions (--coverage)\n  target_link_libraries(openmw-iniimporter gcov)\nendif()\n"
  },
  {
    "path": "apps/mwiniimporter/importer.cpp",
    "content": "#include \"importer.hpp\"\n\n#include <iostream>\n#include <sstream>\n#include <components/misc/stringops.hpp>\n#include <components/esm/esmreader.hpp>\n\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n#include <boost/version.hpp>\n\nnamespace bfs = boost::filesystem;\n\nMwIniImporter::MwIniImporter()\n    : mVerbose(false)\n    , mEncoding(ToUTF8::WINDOWS_1250)\n{\n    const char *map[][2] =\n    {\n        { \"no-sound\", \"General:Disable Audio\" },\n        { 0, 0 }\n    };\n    const char *fallback[] = {\n\n        // light\n        \"LightAttenuation:UseConstant\",\n        \"LightAttenuation:ConstantValue\",\n        \"LightAttenuation:UseLinear\",\n        \"LightAttenuation:LinearMethod\",\n        \"LightAttenuation:LinearValue\",\n        \"LightAttenuation:LinearRadiusMult\",\n        \"LightAttenuation:UseQuadratic\",\n        \"LightAttenuation:QuadraticMethod\",\n        \"LightAttenuation:QuadraticValue\",\n        \"LightAttenuation:QuadraticRadiusMult\",\n        \"LightAttenuation:OutQuadInLin\",\n\n        // inventory\n        \"Inventory:DirectionalDiffuseR\",\n        \"Inventory:DirectionalDiffuseG\",\n        \"Inventory:DirectionalDiffuseB\",\n        \"Inventory:DirectionalAmbientR\",\n        \"Inventory:DirectionalAmbientG\",\n        \"Inventory:DirectionalAmbientB\",\n        \"Inventory:DirectionalRotationX\",\n        \"Inventory:DirectionalRotationY\",\n        \"Inventory:UniformScaling\",\n\n        // map\n        \"Map:Travel Siltstrider Red\",\n        \"Map:Travel Siltstrider Green\",\n        \"Map:Travel Siltstrider Blue\",\n        \"Map:Travel Boat Red\",\n        \"Map:Travel Boat Green\",\n        \"Map:Travel Boat Blue\",\n        \"Map:Travel Magic Red\",\n        \"Map:Travel Magic Green\",\n        \"Map:Travel Magic Blue\",\n        \"Map:Show Travel Lines\",\n\n        // water\n        \"Water:Map Alpha\",\n        \"Water:World Alpha\",\n        \"Water:SurfaceTextureSize\",\n        \"Water:SurfaceTileCount\",\n        \"Water:SurfaceFPS\",\n        \"Water:SurfaceTexture\",\n        \"Water:SurfaceFrameCount\",\n        \"Water:TileTextureDivisor\",\n        \"Water:RippleTexture\",\n        \"Water:RippleFrameCount\",\n        \"Water:RippleLifetime\",\n        \"Water:MaxNumberRipples\",\n        \"Water:RippleScale\",\n        \"Water:RippleRotSpeed\",\n        \"Water:RippleAlphas\",\n        \"Water:PSWaterReflectTerrain\",\n        \"Water:PSWaterReflectUpdate\",\n        \"Water:NearWaterRadius\",\n        \"Water:NearWaterPoints\",\n        \"Water:NearWaterUnderwaterFreq\",\n        \"Water:NearWaterUnderwaterVolume\",\n        \"Water:NearWaterIndoorTolerance\",\n        \"Water:NearWaterOutdoorTolerance\",\n        \"Water:NearWaterIndoorID\",\n        \"Water:NearWaterOutdoorID\",\n        \"Water:UnderwaterSunriseFog\",\n        \"Water:UnderwaterDayFog\",\n        \"Water:UnderwaterSunsetFog\",\n        \"Water:UnderwaterNightFog\",\n        \"Water:UnderwaterIndoorFog\",\n        \"Water:UnderwaterColor\",\n        \"Water:UnderwaterColorWeight\",\n\n        // pixelwater\n        \"PixelWater:SurfaceFPS\",\n        \"PixelWater:TileCount\",\n        \"PixelWater:Resolution\",\n\n        // fonts\n        \"Fonts:Font 0\",\n        \"Fonts:Font 1\",\n        \"Fonts:Font 2\",\n\n        // UI colors\n        \"FontColor:color_normal\",\n        \"FontColor:color_normal_over\",\n        \"FontColor:color_normal_pressed\",\n        \"FontColor:color_active\",\n        \"FontColor:color_active_over\",\n        \"FontColor:color_active_pressed\",\n        \"FontColor:color_disabled\",\n        \"FontColor:color_disabled_over\",\n        \"FontColor:color_disabled_pressed\",\n        \"FontColor:color_link\",\n        \"FontColor:color_link_over\",\n        \"FontColor:color_link_pressed\",\n        \"FontColor:color_journal_link\",\n        \"FontColor:color_journal_link_over\",\n        \"FontColor:color_journal_link_pressed\",\n        \"FontColor:color_journal_topic\",\n        \"FontColor:color_journal_topic_over\",\n        \"FontColor:color_journal_topic_pressed\",\n        \"FontColor:color_answer\",\n        \"FontColor:color_answer_over\",\n        \"FontColor:color_answer_pressed\",\n        \"FontColor:color_header\",\n        \"FontColor:color_notify\",\n        \"FontColor:color_big_normal\",\n        \"FontColor:color_big_normal_over\",\n        \"FontColor:color_big_normal_pressed\",\n        \"FontColor:color_big_link\",\n        \"FontColor:color_big_link_over\",\n        \"FontColor:color_big_link_pressed\",\n        \"FontColor:color_big_answer\",\n        \"FontColor:color_big_answer_over\",\n        \"FontColor:color_big_answer_pressed\",\n        \"FontColor:color_big_header\",\n        \"FontColor:color_big_notify\",\n        \"FontColor:color_background\",\n        \"FontColor:color_focus\",\n        \"FontColor:color_health\",\n        \"FontColor:color_magic\",\n        \"FontColor:color_fatigue\",\n        \"FontColor:color_misc\",\n        \"FontColor:color_weapon_fill\",\n        \"FontColor:color_magic_fill\",\n        \"FontColor:color_positive\",\n        \"FontColor:color_negative\",\n        \"FontColor:color_count\",\n\n        // level up messages\n        \"Level Up:Level2\",\n        \"Level Up:Level3\",\n        \"Level Up:Level4\",\n        \"Level Up:Level5\",\n        \"Level Up:Level6\",\n        \"Level Up:Level7\",\n        \"Level Up:Level8\",\n        \"Level Up:Level9\",\n        \"Level Up:Level10\",\n        \"Level Up:Level11\",\n        \"Level Up:Level12\",\n        \"Level Up:Level13\",\n        \"Level Up:Level14\",\n        \"Level Up:Level15\",\n        \"Level Up:Level16\",\n        \"Level Up:Level17\",\n        \"Level Up:Level18\",\n        \"Level Up:Level19\",\n        \"Level Up:Level20\",\n        \"Level Up:Default\",\n\n        // character creation multiple choice test\n        \"Question 1:Question\",\n        \"Question 1:AnswerOne\",\n        \"Question 1:AnswerTwo\",\n        \"Question 1:AnswerThree\",\n        \"Question 1:Sound\",\n        \"Question 2:Question\",\n        \"Question 2:AnswerOne\",\n        \"Question 2:AnswerTwo\",\n        \"Question 2:AnswerThree\",\n        \"Question 2:Sound\",\n        \"Question 3:Question\",\n        \"Question 3:AnswerOne\",\n        \"Question 3:AnswerTwo\",\n        \"Question 3:AnswerThree\",\n        \"Question 3:Sound\",\n        \"Question 4:Question\",\n        \"Question 4:AnswerOne\",\n        \"Question 4:AnswerTwo\",\n        \"Question 4:AnswerThree\",\n        \"Question 4:Sound\",\n        \"Question 5:Question\",\n        \"Question 5:AnswerOne\",\n        \"Question 5:AnswerTwo\",\n        \"Question 5:AnswerThree\",\n        \"Question 5:Sound\",\n        \"Question 6:Question\",\n        \"Question 6:AnswerOne\",\n        \"Question 6:AnswerTwo\",\n        \"Question 6:AnswerThree\",\n        \"Question 6:Sound\",\n        \"Question 7:Question\",\n        \"Question 7:AnswerOne\",\n        \"Question 7:AnswerTwo\",\n        \"Question 7:AnswerThree\",\n        \"Question 7:Sound\",\n        \"Question 8:Question\",\n        \"Question 8:AnswerOne\",\n        \"Question 8:AnswerTwo\",\n        \"Question 8:AnswerThree\",\n        \"Question 8:Sound\",\n        \"Question 9:Question\",\n        \"Question 9:AnswerOne\",\n        \"Question 9:AnswerTwo\",\n        \"Question 9:AnswerThree\",\n        \"Question 9:Sound\",\n        \"Question 10:Question\",\n        \"Question 10:AnswerOne\",\n        \"Question 10:AnswerTwo\",\n        \"Question 10:AnswerThree\",\n        \"Question 10:Sound\",\n\n        // blood textures and models\n        \"Blood:Model 0\",\n        \"Blood:Model 1\",\n        \"Blood:Model 2\",\n        \"Blood:Texture 0\",\n        \"Blood:Texture 1\",\n        \"Blood:Texture 2\",\n        \"Blood:Texture 3\",\n        \"Blood:Texture 4\",\n        \"Blood:Texture 5\",\n        \"Blood:Texture 6\",\n        \"Blood:Texture 7\",\n        \"Blood:Texture Name 0\",\n        \"Blood:Texture Name 1\",\n        \"Blood:Texture Name 2\",\n        \"Blood:Texture Name 3\",\n        \"Blood:Texture Name 4\",\n        \"Blood:Texture Name 5\",\n        \"Blood:Texture Name 6\",\n        \"Blood:Texture Name 7\",\n\n        // movies\n        \"Movies:Company Logo\",\n        \"Movies:Morrowind Logo\",\n        \"Movies:New Game\",\n        \"Movies:Loading\",\n        \"Movies:Options Menu\",\n\n        // weather related values\n\n        \"Weather Thunderstorm:Thunder Sound ID 0\",\n        \"Weather Thunderstorm:Thunder Sound ID 1\",\n        \"Weather Thunderstorm:Thunder Sound ID 2\",\n        \"Weather Thunderstorm:Thunder Sound ID 3\",\n        \"Weather:Sunrise Time\",\n        \"Weather:Sunset Time\",\n        \"Weather:Sunrise Duration\",\n        \"Weather:Sunset Duration\",\n        \"Weather:Hours Between Weather Changes\", // AKA weather update time\n        \"Weather Thunderstorm:Thunder Frequency\",\n        \"Weather Thunderstorm:Thunder Threshold\",\n\n        \"Weather:EnvReduceColor\",\n        \"Weather:LerpCloseColor\",\n        \"Weather:BumpFadeColor\",\n        \"Weather:AlphaReduce\",\n        \"Weather:Minimum Time Between Environmental Sounds\",\n        \"Weather:Maximum Time Between Environmental Sounds\",\n        \"Weather:Sun Glare Fader Max\",\n        \"Weather:Sun Glare Fader Angle Max\",\n        \"Weather:Sun Glare Fader Color\",\n        \"Weather:Timescale Clouds\",\n        \"Weather:Precip Gravity\",\n        \"Weather:Rain Ripples\",\n        \"Weather:Rain Ripple Radius\",\n        \"Weather:Rain Ripples Per Drop\",\n        \"Weather:Rain Ripple Scale\",\n        \"Weather:Rain Ripple Speed\",\n        \"Weather:Fog Depth Change Speed\",\n        \"Weather:Sky Pre-Sunrise Time\",\n        \"Weather:Sky Post-Sunrise Time\",\n        \"Weather:Sky Pre-Sunset Time\",\n        \"Weather:Sky Post-Sunset Time\",\n        \"Weather:Ambient Pre-Sunrise Time\",\n        \"Weather:Ambient Post-Sunrise Time\",\n        \"Weather:Ambient Pre-Sunset Time\",\n        \"Weather:Ambient Post-Sunset Time\",\n        \"Weather:Fog Pre-Sunrise Time\",\n        \"Weather:Fog Post-Sunrise Time\",\n        \"Weather:Fog Pre-Sunset Time\",\n        \"Weather:Fog Post-Sunset Time\",\n        \"Weather:Sun Pre-Sunrise Time\",\n        \"Weather:Sun Post-Sunrise Time\",\n        \"Weather:Sun Pre-Sunset Time\",\n        \"Weather:Sun Post-Sunset Time\",\n        \"Weather:Stars Post-Sunset Start\",\n        \"Weather:Stars Pre-Sunrise Finish\",\n        \"Weather:Stars Fading Duration\",\n        \"Weather:Snow Ripples\",\n        \"Weather:Snow Ripple Radius\",\n        \"Weather:Snow Ripples Per Flake\",\n        \"Weather:Snow Ripple Scale\",\n        \"Weather:Snow Ripple Speed\",\n        \"Weather:Snow Gravity Scale\",\n        \"Weather:Snow High Kill\",\n        \"Weather:Snow Low Kill\",\n\n        \"Weather Clear:Cloud Texture\",\n        \"Weather Clear:Clouds Maximum Percent\",\n        \"Weather Clear:Transition Delta\",\n        \"Weather Clear:Sky Sunrise Color\",\n        \"Weather Clear:Sky Day Color\",\n        \"Weather Clear:Sky Sunset Color\",\n        \"Weather Clear:Sky Night Color\",\n        \"Weather Clear:Fog Sunrise Color\",\n        \"Weather Clear:Fog Day Color\",\n        \"Weather Clear:Fog Sunset Color\",\n        \"Weather Clear:Fog Night Color\",\n        \"Weather Clear:Ambient Sunrise Color\",\n        \"Weather Clear:Ambient Day Color\",\n        \"Weather Clear:Ambient Sunset Color\",\n        \"Weather Clear:Ambient Night Color\",\n        \"Weather Clear:Sun Sunrise Color\",\n        \"Weather Clear:Sun Day Color\",\n        \"Weather Clear:Sun Sunset Color\",\n        \"Weather Clear:Sun Night Color\",\n        \"Weather Clear:Sun Disc Sunset Color\",\n        \"Weather Clear:Land Fog Day Depth\",\n        \"Weather Clear:Land Fog Night Depth\",\n        \"Weather Clear:Wind Speed\",\n        \"Weather Clear:Cloud Speed\",\n        \"Weather Clear:Glare View\",\n        \"Weather Clear:Ambient Loop Sound ID\",\n\n        \"Weather Cloudy:Cloud Texture\",\n        \"Weather Cloudy:Clouds Maximum Percent\",\n        \"Weather Cloudy:Transition Delta\",\n        \"Weather Cloudy:Sky Sunrise Color\",\n        \"Weather Cloudy:Sky Day Color\",\n        \"Weather Cloudy:Sky Sunset Color\",\n        \"Weather Cloudy:Sky Night Color\",\n        \"Weather Cloudy:Fog Sunrise Color\",\n        \"Weather Cloudy:Fog Day Color\",\n        \"Weather Cloudy:Fog Sunset Color\",\n        \"Weather Cloudy:Fog Night Color\",\n        \"Weather Cloudy:Ambient Sunrise Color\",\n        \"Weather Cloudy:Ambient Day Color\",\n        \"Weather Cloudy:Ambient Sunset Color\",\n        \"Weather Cloudy:Ambient Night Color\",\n        \"Weather Cloudy:Sun Sunrise Color\",\n        \"Weather Cloudy:Sun Day Color\",\n        \"Weather Cloudy:Sun Sunset Color\",\n        \"Weather Cloudy:Sun Night Color\",\n        \"Weather Cloudy:Sun Disc Sunset Color\",\n        \"Weather Cloudy:Land Fog Day Depth\",\n        \"Weather Cloudy:Land Fog Night Depth\",\n        \"Weather Cloudy:Wind Speed\",\n        \"Weather Cloudy:Cloud Speed\",\n        \"Weather Cloudy:Glare View\",\n        \"Weather Cloudy:Ambient Loop Sound ID\",\n\n        \"Weather Foggy:Cloud Texture\",\n        \"Weather Foggy:Clouds Maximum Percent\",\n        \"Weather Foggy:Transition Delta\",\n        \"Weather Foggy:Sky Sunrise Color\",\n        \"Weather Foggy:Sky Day Color\",\n        \"Weather Foggy:Sky Sunset Color\",\n        \"Weather Foggy:Sky Night Color\",\n        \"Weather Foggy:Fog Sunrise Color\",\n        \"Weather Foggy:Fog Day Color\",\n        \"Weather Foggy:Fog Sunset Color\",\n        \"Weather Foggy:Fog Night Color\",\n        \"Weather Foggy:Ambient Sunrise Color\",\n        \"Weather Foggy:Ambient Day Color\",\n        \"Weather Foggy:Ambient Sunset Color\",\n        \"Weather Foggy:Ambient Night Color\",\n        \"Weather Foggy:Sun Sunrise Color\",\n        \"Weather Foggy:Sun Day Color\",\n        \"Weather Foggy:Sun Sunset Color\",\n        \"Weather Foggy:Sun Night Color\",\n        \"Weather Foggy:Sun Disc Sunset Color\",\n        \"Weather Foggy:Land Fog Day Depth\",\n        \"Weather Foggy:Land Fog Night Depth\",\n        \"Weather Foggy:Wind Speed\",\n        \"Weather Foggy:Cloud Speed\",\n        \"Weather Foggy:Glare View\",\n        \"Weather Foggy:Ambient Loop Sound ID\",\n\n        \"Weather Thunderstorm:Cloud Texture\",\n        \"Weather Thunderstorm:Clouds Maximum Percent\",\n        \"Weather Thunderstorm:Transition Delta\",\n        \"Weather Thunderstorm:Sky Sunrise Color\",\n        \"Weather Thunderstorm:Sky Day Color\",\n        \"Weather Thunderstorm:Sky Sunset Color\",\n        \"Weather Thunderstorm:Sky Night Color\",\n        \"Weather Thunderstorm:Fog Sunrise Color\",\n        \"Weather Thunderstorm:Fog Day Color\",\n        \"Weather Thunderstorm:Fog Sunset Color\",\n        \"Weather Thunderstorm:Fog Night Color\",\n        \"Weather Thunderstorm:Ambient Sunrise Color\",\n        \"Weather Thunderstorm:Ambient Day Color\",\n        \"Weather Thunderstorm:Ambient Sunset Color\",\n        \"Weather Thunderstorm:Ambient Night Color\",\n        \"Weather Thunderstorm:Sun Sunrise Color\",\n        \"Weather Thunderstorm:Sun Day Color\",\n        \"Weather Thunderstorm:Sun Sunset Color\",\n        \"Weather Thunderstorm:Sun Night Color\",\n        \"Weather Thunderstorm:Sun Disc Sunset Color\",\n        \"Weather Thunderstorm:Land Fog Day Depth\",\n        \"Weather Thunderstorm:Land Fog Night Depth\",\n        \"Weather Thunderstorm:Wind Speed\",\n        \"Weather Thunderstorm:Cloud Speed\",\n        \"Weather Thunderstorm:Glare View\",\n        \"Weather Thunderstorm:Rain Loop Sound ID\",\n        \"Weather Thunderstorm:Using Precip\",\n        \"Weather Thunderstorm:Rain Diameter\",\n        \"Weather Thunderstorm:Rain Height Min\",\n        \"Weather Thunderstorm:Rain Height Max\",\n        \"Weather Thunderstorm:Rain Threshold\",\n        \"Weather Thunderstorm:Max Raindrops\",\n        \"Weather Thunderstorm:Rain Entrance Speed\",\n        \"Weather Thunderstorm:Ambient Loop Sound ID\",\n        \"Weather Thunderstorm:Flash Decrement\",\n\n        \"Weather Rain:Cloud Texture\",\n        \"Weather Rain:Clouds Maximum Percent\",\n        \"Weather Rain:Transition Delta\",\n        \"Weather Rain:Sky Sunrise Color\",\n        \"Weather Rain:Sky Day Color\",\n        \"Weather Rain:Sky Sunset Color\",\n        \"Weather Rain:Sky Night Color\",\n        \"Weather Rain:Fog Sunrise Color\",\n        \"Weather Rain:Fog Day Color\",\n        \"Weather Rain:Fog Sunset Color\",\n        \"Weather Rain:Fog Night Color\",\n        \"Weather Rain:Ambient Sunrise Color\",\n        \"Weather Rain:Ambient Day Color\",\n        \"Weather Rain:Ambient Sunset Color\",\n        \"Weather Rain:Ambient Night Color\",\n        \"Weather Rain:Sun Sunrise Color\",\n        \"Weather Rain:Sun Day Color\",\n        \"Weather Rain:Sun Sunset Color\",\n        \"Weather Rain:Sun Night Color\",\n        \"Weather Rain:Sun Disc Sunset Color\",\n        \"Weather Rain:Land Fog Day Depth\",\n        \"Weather Rain:Land Fog Night Depth\",\n        \"Weather Rain:Wind Speed\",\n        \"Weather Rain:Cloud Speed\",\n        \"Weather Rain:Glare View\",\n        \"Weather Rain:Rain Loop Sound ID\",\n        \"Weather Rain:Using Precip\",\n        \"Weather Rain:Rain Diameter\",\n        \"Weather Rain:Rain Height Min\",\n        \"Weather Rain:Rain Height Max\",\n        \"Weather Rain:Rain Threshold\",\n        \"Weather Rain:Rain Entrance Speed\",\n        \"Weather Rain:Ambient Loop Sound ID\",\n        \"Weather Rain:Max Raindrops\",\n\n        \"Weather Overcast:Cloud Texture\",\n        \"Weather Overcast:Clouds Maximum Percent\",\n        \"Weather Overcast:Transition Delta\",\n        \"Weather Overcast:Sky Sunrise Color\",\n        \"Weather Overcast:Sky Day Color\",\n        \"Weather Overcast:Sky Sunset Color\",\n        \"Weather Overcast:Sky Night Color\",\n        \"Weather Overcast:Fog Sunrise Color\",\n        \"Weather Overcast:Fog Day Color\",\n        \"Weather Overcast:Fog Sunset Color\",\n        \"Weather Overcast:Fog Night Color\",\n        \"Weather Overcast:Ambient Sunrise Color\",\n        \"Weather Overcast:Ambient Day Color\",\n        \"Weather Overcast:Ambient Sunset Color\",\n        \"Weather Overcast:Ambient Night Color\",\n        \"Weather Overcast:Sun Sunrise Color\",\n        \"Weather Overcast:Sun Day Color\",\n        \"Weather Overcast:Sun Sunset Color\",\n        \"Weather Overcast:Sun Night Color\",\n        \"Weather Overcast:Sun Disc Sunset Color\",\n        \"Weather Overcast:Land Fog Day Depth\",\n        \"Weather Overcast:Land Fog Night Depth\",\n        \"Weather Overcast:Wind Speed\",\n        \"Weather Overcast:Cloud Speed\",\n        \"Weather Overcast:Glare View\",\n        \"Weather Overcast:Ambient Loop Sound ID\",\n\n        \"Weather Ashstorm:Cloud Texture\",\n        \"Weather Ashstorm:Clouds Maximum Percent\",\n        \"Weather Ashstorm:Transition Delta\",\n        \"Weather Ashstorm:Sky Sunrise Color\",\n        \"Weather Ashstorm:Sky Day Color\",\n        \"Weather Ashstorm:Sky Sunset Color\",\n        \"Weather Ashstorm:Sky Night Color\",\n        \"Weather Ashstorm:Fog Sunrise Color\",\n        \"Weather Ashstorm:Fog Day Color\",\n        \"Weather Ashstorm:Fog Sunset Color\",\n        \"Weather Ashstorm:Fog Night Color\",\n        \"Weather Ashstorm:Ambient Sunrise Color\",\n        \"Weather Ashstorm:Ambient Day Color\",\n        \"Weather Ashstorm:Ambient Sunset Color\",\n        \"Weather Ashstorm:Ambient Night Color\",\n        \"Weather Ashstorm:Sun Sunrise Color\",\n        \"Weather Ashstorm:Sun Day Color\",\n        \"Weather Ashstorm:Sun Sunset Color\",\n        \"Weather Ashstorm:Sun Night Color\",\n        \"Weather Ashstorm:Sun Disc Sunset Color\",\n        \"Weather Ashstorm:Land Fog Day Depth\",\n        \"Weather Ashstorm:Land Fog Night Depth\",\n        \"Weather Ashstorm:Wind Speed\",\n        \"Weather Ashstorm:Cloud Speed\",\n        \"Weather Ashstorm:Glare View\",\n        \"Weather Ashstorm:Ambient Loop Sound ID\",\n        \"Weather Ashstorm:Storm Threshold\",\n\n        \"Weather Blight:Cloud Texture\",\n        \"Weather Blight:Clouds Maximum Percent\",\n        \"Weather Blight:Transition Delta\",\n        \"Weather Blight:Sky Sunrise Color\",\n        \"Weather Blight:Sky Day Color\",\n        \"Weather Blight:Sky Sunset Color\",\n        \"Weather Blight:Sky Night Color\",\n        \"Weather Blight:Fog Sunrise Color\",\n        \"Weather Blight:Fog Day Color\",\n        \"Weather Blight:Fog Sunset Color\",\n        \"Weather Blight:Fog Night Color\",\n        \"Weather Blight:Ambient Sunrise Color\",\n        \"Weather Blight:Ambient Day Color\",\n        \"Weather Blight:Ambient Sunset Color\",\n        \"Weather Blight:Ambient Night Color\",\n        \"Weather Blight:Sun Sunrise Color\",\n        \"Weather Blight:Sun Day Color\",\n        \"Weather Blight:Sun Sunset Color\",\n        \"Weather Blight:Sun Night Color\",\n        \"Weather Blight:Sun Disc Sunset Color\",\n        \"Weather Blight:Land Fog Day Depth\",\n        \"Weather Blight:Land Fog Night Depth\",\n        \"Weather Blight:Wind Speed\",\n        \"Weather Blight:Cloud Speed\",\n        \"Weather Blight:Glare View\",\n        \"Weather Blight:Ambient Loop Sound ID\",\n        \"Weather Blight:Storm Threshold\",\n        \"Weather Blight:Disease Chance\",\n\n        // for Bloodmoon\n        \"Weather Snow:Cloud Texture\",\n        \"Weather Snow:Clouds Maximum Percent\",\n        \"Weather Snow:Transition Delta\",\n        \"Weather Snow:Sky Sunrise Color\",\n        \"Weather Snow:Sky Day Color\",\n        \"Weather Snow:Sky Sunset Color\",\n        \"Weather Snow:Sky Night Color\",\n        \"Weather Snow:Fog Sunrise Color\",\n        \"Weather Snow:Fog Day Color\",\n        \"Weather Snow:Fog Sunset Color\",\n        \"Weather Snow:Fog Night Color\",\n        \"Weather Snow:Ambient Sunrise Color\",\n        \"Weather Snow:Ambient Day Color\",\n        \"Weather Snow:Ambient Sunset Color\",\n        \"Weather Snow:Ambient Night Color\",\n        \"Weather Snow:Sun Sunrise Color\",\n        \"Weather Snow:Sun Day Color\",\n        \"Weather Snow:Sun Sunset Color\",\n        \"Weather Snow:Sun Night Color\",\n        \"Weather Snow:Sun Disc Sunset Color\",\n        \"Weather Snow:Land Fog Day Depth\",\n        \"Weather Snow:Land Fog Night Depth\",\n        \"Weather Snow:Wind Speed\",\n        \"Weather Snow:Cloud Speed\",\n        \"Weather Snow:Glare View\",\n        \"Weather Snow:Snow Diameter\",\n        \"Weather Snow:Snow Height Min\",\n        \"Weather Snow:Snow Height Max\",\n        \"Weather Snow:Snow Entrance Speed\",\n        \"Weather Snow:Max Snowflakes\",\n        \"Weather Snow:Ambient Loop Sound ID\",\n        \"Weather Snow:Snow Threshold\",\n\n        // for Bloodmoon\n        \"Weather Blizzard:Cloud Texture\",\n        \"Weather Blizzard:Clouds Maximum Percent\",\n        \"Weather Blizzard:Transition Delta\",\n        \"Weather Blizzard:Sky Sunrise Color\",\n        \"Weather Blizzard:Sky Day Color\",\n        \"Weather Blizzard:Sky Sunset Color\",\n        \"Weather Blizzard:Sky Night Color\",\n        \"Weather Blizzard:Fog Sunrise Color\",\n        \"Weather Blizzard:Fog Day Color\",\n        \"Weather Blizzard:Fog Sunset Color\",\n        \"Weather Blizzard:Fog Night Color\",\n        \"Weather Blizzard:Ambient Sunrise Color\",\n        \"Weather Blizzard:Ambient Day Color\",\n        \"Weather Blizzard:Ambient Sunset Color\",\n        \"Weather Blizzard:Ambient Night Color\",\n        \"Weather Blizzard:Sun Sunrise Color\",\n        \"Weather Blizzard:Sun Day Color\",\n        \"Weather Blizzard:Sun Sunset Color\",\n        \"Weather Blizzard:Sun Night Color\",\n        \"Weather Blizzard:Sun Disc Sunset Color\",\n        \"Weather Blizzard:Land Fog Day Depth\",\n        \"Weather Blizzard:Land Fog Night Depth\",\n        \"Weather Blizzard:Wind Speed\",\n        \"Weather Blizzard:Cloud Speed\",\n        \"Weather Blizzard:Glare View\",\n        \"Weather Blizzard:Ambient Loop Sound ID\",\n        \"Weather Blizzard:Storm Threshold\",\n\n        // moons\n        \"Moons:Secunda Size\",\n        \"Moons:Secunda Axis Offset\",\n        \"Moons:Secunda Speed\",\n        \"Moons:Secunda Daily Increment\",\n        \"Moons:Secunda Moon Shadow Early Fade Angle\",\n        \"Moons:Secunda Fade Start Angle\",\n        \"Moons:Secunda Fade End Angle\",\n        \"Moons:Secunda Fade In Start\",\n        \"Moons:Secunda Fade In Finish\",\n        \"Moons:Secunda Fade Out Start\",\n        \"Moons:Secunda Fade Out Finish\",\n        \"Moons:Masser Size\",\n        \"Moons:Masser Axis Offset\",\n        \"Moons:Masser Speed\",\n        \"Moons:Masser Daily Increment\",\n        \"Moons:Masser Moon Shadow Early Fade Angle\",\n        \"Moons:Masser Fade Start Angle\",\n        \"Moons:Masser Fade End Angle\",\n        \"Moons:Masser Fade In Start\",\n        \"Moons:Masser Fade In Finish\",\n        \"Moons:Masser Fade Out Start\",\n        \"Moons:Masser Fade Out Finish\",\n        \"Moons:Script Color\",\n\n        // werewolf (Bloodmoon)\n        \"General:Werewolf FOV\",\n\n        0\n    };\n\n    for(int i=0; map[i][0]; i++) {\n        mMergeMap.insert(std::make_pair<std::string, std::string>(map[i][0], map[i][1]));\n    }\n\n    for(int i=0; fallback[i]; i++) {\n        mMergeFallback.emplace_back(fallback[i]);\n    }\n}\n\nvoid MwIniImporter::setVerbose(bool verbose) {\n    mVerbose = verbose;\n}\n\nMwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path&  filename) const {\n    std::cout << \"load ini file: \" << filename << std::endl;\n\n    std::string section(\"\");\n    MwIniImporter::multistrmap map;\n    bfs::ifstream file((bfs::path(filename)));\n    ToUTF8::Utf8Encoder encoder(mEncoding);\n\n    std::string line;\n    while (std::getline(file, line)) {\n\n        line = encoder.getUtf8(line);\n\n        // unify Unix-style and Windows file ending\n        if (!(line.empty()) && (line[line.length()-1]) == '\\r') {\n            line = line.substr(0, line.length()-1);\n        }\n\n        if(line.empty()) {\n            continue;\n        }\n\n        if(line[0] == '[') {\n            int pos = static_cast<int>(line.find(']'));\n            if(pos < 2) {\n                std::cout << \"Warning: ini file wrongly formatted (\" << line << \"). Line ignored.\" << std::endl;\n                continue;\n            }\n\n            section = line.substr(1, line.find(']')-1);\n            continue;\n        }\n\n        int comment_pos = static_cast<int>(line.find(';'));\n        if(comment_pos > 0) {\n            line = line.substr(0,comment_pos);\n        }\n\n        int pos = static_cast<int>(line.find('='));\n        if(pos < 1) {\n            continue;\n        }\n\n        std::string key(section + \":\" + line.substr(0,pos));\n        std::string value(line.substr(pos+1));\n        if(value.empty()) {\n            std::cout << \"Warning: ignored empty value for key '\" << key << \"'.\" << std::endl;\n            continue;\n        }\n\n        if(map.find(key) == map.end()) {\n            map.insert( std::make_pair (key, std::vector<std::string>() ) );\n        }\n        map[key].push_back(value);\n    }\n\n    return map;\n}\n\nMwIniImporter::multistrmap MwIniImporter::loadCfgFile(const boost::filesystem::path& filename) {\n    std::cout << \"load cfg file: \" << filename << std::endl;\n\n    MwIniImporter::multistrmap map;\n    bfs::ifstream file((bfs::path(filename)));\n\n    std::string line;\n    while (std::getline(file, line)) {\n\n        // we cant say comment by only looking at first char anymore\n        int comment_pos = static_cast<int>(line.find('#'));\n        if(comment_pos > 0) {\n            line = line.substr(0,comment_pos);\n        }\n\n        if(line.empty()) {\n            continue;\n        }\n\n        int pos = static_cast<int>(line.find('='));\n        if(pos < 1) {\n            continue;\n        }\n\n        std::string key(line.substr(0,pos));\n        std::string value(line.substr(pos+1));\n\n        if(map.find(key) == map.end()) {\n            map.insert( std::make_pair (key, std::vector<std::string>() ) );\n        }\n        map[key].push_back(value);\n    }\n\n    return map;\n}\n\nvoid MwIniImporter::merge(multistrmap &cfg, const multistrmap &ini) const {\n    multistrmap::const_iterator iniIt;\n    for(strmap::const_iterator it=mMergeMap.begin(); it!=mMergeMap.end(); ++it) {\n        if((iniIt = ini.find(it->second)) != ini.end()) {\n            for(std::vector<std::string>::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) {\n                cfg.erase(it->first);\n                insertMultistrmap(cfg, it->first, *vc);\n            }\n        }\n    }\n}\n\nvoid MwIniImporter::mergeFallback(multistrmap &cfg, const multistrmap &ini) const {\n    cfg.erase(\"fallback\");\n\n    multistrmap::const_iterator iniIt;\n    for(std::vector<std::string>::const_iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); ++it) {\n        if((iniIt = ini.find(*it)) != ini.end()) {\n            for(std::vector<std::string>::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) {\n                std::string value(*it);\n                std::replace( value.begin(), value.end(), ' ', '_' );\n                std::replace( value.begin(), value.end(), ':', '_' );\n                value.append(\",\").append(vc->substr(0,vc->length()));\n                insertMultistrmap(cfg, \"fallback\", value);\n            }\n        }\n    }\n}\n\nvoid MwIniImporter::insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value) {\n    const multistrmap::const_iterator it = cfg.find(key);\n    if(it == cfg.end()) {\n        cfg.insert(std::make_pair (key, std::vector<std::string>() ));\n    }\n    cfg[key].push_back(value);\n}\n\nvoid MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) const {\n    std::vector<std::string> archives;\n    std::string baseArchive(\"Archives:Archive \");\n    std::string archive;\n\n    // Search archives listed in ini file\n    multistrmap::const_iterator it = ini.begin();\n    for(int i=0; it != ini.end(); i++) {\n        archive = baseArchive;\n        archive.append(std::to_string(i));\n\n        it = ini.find(archive);\n        if(it == ini.end()) {\n            break;\n        }\n\n        for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {\n            archives.push_back(*entry);\n        }\n    }\n\n    cfg.erase(\"fallback-archive\");\n    cfg.insert( std::make_pair<std::string, std::vector<std::string> > (\"fallback-archive\", std::vector<std::string>()));\n\n    // Add Morrowind.bsa by default, since Vanilla loads this archive even if it\n    // does not appears in the ini file\n    cfg[\"fallback-archive\"].push_back(\"Morrowind.bsa\");\n\n    for(std::vector<std::string>::const_iterator iter=archives.begin(); iter!=archives.end(); ++iter) {\n        cfg[\"fallback-archive\"].push_back(*iter);\n    }\n}\n\nvoid MwIniImporter::dependencySortStep(std::string& element, MwIniImporter::dependencyList& source, std::vector<std::string>& result)\n{\n    auto iter = std::find_if(\n        source.begin(),\n        source.end(),\n        [&element](std::pair< std::string, std::vector<std::string> >& sourceElement)\n        {\n            return sourceElement.first == element;\n        }\n    );\n    if (iter != source.end())\n    {\n        auto foundElement = std::move(*iter);\n        source.erase(iter);\n        for (auto name : foundElement.second)\n        {\n            MwIniImporter::dependencySortStep(name, source, result);\n        }\n        result.push_back(std::move(foundElement.first));\n    }\n}\n\nstd::vector<std::string> MwIniImporter::dependencySort(MwIniImporter::dependencyList source)\n{\n    std::vector<std::string> result;\n    while (!source.empty())\n    {\n        MwIniImporter::dependencySortStep(source.begin()->first, source, result);\n    }\n    return result;\n}\n\nstd::vector<std::string>::iterator MwIniImporter::findString(std::vector<std::string>& source, const std::string& string)\n{\n    return std::find_if(source.begin(), source.end(), [&string](const std::string& sourceString)\n    {\n        return Misc::StringUtils::ciEqual(sourceString, string);\n    });\n}\n\nvoid MwIniImporter::addPaths(std::vector<boost::filesystem::path>& output, std::vector<std::string> input) {\n    for (auto& path : input)\n    {\n        if (path.front() == '\"')\n        {\n            // Drop first and last characters - quotation marks\n            path = path.substr(1, path.size() - 2);\n        }\n        output.emplace_back(path);\n    }\n}\n\nvoid MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const\n{\n    std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles;\n    std::string baseGameFile(\"Game Files:GameFile\");\n    std::time_t defaultTime = 0;\n    ToUTF8::Utf8Encoder encoder(mEncoding);\n\n    std::vector<boost::filesystem::path> dataPaths;\n    if (cfg.count(\"data\"))\n        addPaths(dataPaths, cfg[\"data\"]);\n\n    if (cfg.count(\"data-local\"))\n        addPaths(dataPaths, cfg[\"data-local\"]);\n\n    dataPaths.push_back(iniFilename.parent_path() /= \"Data Files\");\n\n    multistrmap::const_iterator it = ini.begin();\n    for (int i=0; it != ini.end(); i++)\n    {\n        std::string gameFile = baseGameFile;\n        gameFile.append(std::to_string(i));\n\n        it = ini.find(gameFile);\n        if(it == ini.end())\n            break;\n\n        for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry)\n        {\n            std::string filetype(entry->substr(entry->length()-3));\n            Misc::StringUtils::lowerCaseInPlace(filetype);\n\n            if(filetype.compare(\"esm\") == 0 || filetype.compare(\"esp\") == 0)\n            {\n                bool found = false;\n                for (auto & dataPath : dataPaths)\n                {\n                    boost::filesystem::path path = dataPath / *entry;\n                    std::time_t time = lastWriteTime(path, defaultTime);\n                    if (time != defaultTime)\n                    {\n                        contentFiles.emplace_back(time, std::move(path));\n                        found = true;\n                        break;\n                    }\n                }\n                if (!found)\n                    std::cout << \"Warning: \" << *entry << \" not found, ignoring\" << std::endl;\n            }\n        }\n    }\n\n    cfg.erase(\"content\");\n    cfg.insert( std::make_pair(\"content\", std::vector<std::string>() ) );\n\n    // sort by timestamp\n    sort(contentFiles.begin(), contentFiles.end());\n\n    MwIniImporter::dependencyList unsortedFiles;\n\n    ESM::ESMReader reader;\n    reader.setEncoder(&encoder);\n    for (auto& file : contentFiles)\n    {\n        reader.open(file.second.string());\n        std::vector<std::string> dependencies;\n        for (auto& gameFile : reader.getGameFiles())\n        {\n            dependencies.push_back(gameFile.name);\n        }\n        unsortedFiles.emplace_back(boost::filesystem::path(reader.getName()).filename().string(), dependencies);\n        reader.close();\n    }\n\n    auto sortedFiles = dependencySort(unsortedFiles);\n\n    // hard-coded dependency Morrowind - Tribunal - Bloodmoon\n    if(findString(sortedFiles, \"Morrowind.esm\") != sortedFiles.end())\n    {\n        auto tribunalIter  = findString(sortedFiles, \"Tribunal.esm\");\n        auto bloodmoonIter = findString(sortedFiles, \"Bloodmoon.esm\");\n\n        if (bloodmoonIter != sortedFiles.end() && tribunalIter != sortedFiles.end())\n        {\n            size_t bloodmoonIndex = std::distance(sortedFiles.begin(), bloodmoonIter);\n            size_t tribunalIndex  = std::distance(sortedFiles.begin(), tribunalIter);\n            if (bloodmoonIndex < tribunalIndex)\n                tribunalIndex++;\n            sortedFiles.insert(bloodmoonIter, *tribunalIter);\n            sortedFiles.erase(sortedFiles.begin() + tribunalIndex);\n        }\n    }\n\n    for (auto& file : sortedFiles)\n        cfg[\"content\"].push_back(file);\n}\n\nvoid MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) {\n\n    for(multistrmap::const_iterator it=cfg.begin(); it != cfg.end(); ++it) {\n        for(std::vector<std::string>::const_iterator entry=it->second.begin(); entry != it->second.end(); ++entry) {\n            out << (it->first) << \"=\" << (*entry) << std::endl;\n        }\n    }\n}\n\nvoid MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding)\n{\n  mEncoding = encoding;\n}\n\nstd::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime)\n{\n    std::time_t writeTime(defaultTime);\n    if (boost::filesystem::exists(filename))\n    {\n        boost::filesystem::path resolved = boost::filesystem::canonical(filename);\n        writeTime = boost::filesystem::last_write_time(resolved);\n\n        // print timestamp\n        const int size=1024;\n        char timeStrBuffer[size];\n        if (std::strftime(timeStrBuffer, size, \"%x %X\", localtime(&writeTime)) > 0)\n            std::cout << \"content file: \" << resolved << \" timestamp = (\" << writeTime <<\n                \") \" << timeStrBuffer << std::endl;\n    }\n    return writeTime;\n}\n"
  },
  {
    "path": "apps/mwiniimporter/importer.hpp",
    "content": "#ifndef MWINIIMPORTER_IMPORTER\n#define MWINIIMPORTER_IMPORTER 1\n\n#include <string>\n#include <map>\n#include <vector>\n#include <exception>\n#include <iosfwd>\n#include <boost/filesystem/path.hpp>\n\n#include <components/to_utf8/to_utf8.hpp>\n\nclass MwIniImporter {\n  public:\n    typedef std::map<std::string, std::string> strmap;\n    typedef std::map<std::string, std::vector<std::string> > multistrmap;\n    typedef std::vector< std::pair< std::string, std::vector<std::string> > > dependencyList;\n\n    MwIniImporter();\n    void    setInputEncoding(const ToUTF8::FromType& encoding);\n    void    setVerbose(bool verbose);\n    multistrmap  loadIniFile(const boost::filesystem::path& filename) const;\n    static multistrmap  loadCfgFile(const boost::filesystem::path& filename);\n    void    merge(multistrmap &cfg, const multistrmap &ini) const;\n    void    mergeFallback(multistrmap &cfg, const multistrmap &ini) const;\n    void    importGameFiles(multistrmap &cfg, const multistrmap &ini,\n        const boost::filesystem::path& iniFilename) const;\n    void    importArchives(multistrmap &cfg, const multistrmap &ini) const;\n    static void    writeToFile(std::ostream &out, const multistrmap &cfg);\n\n    static std::vector<std::string> dependencySort(MwIniImporter::dependencyList source);\n\n  private:\n    static void dependencySortStep(std::string& element, MwIniImporter::dependencyList& source, std::vector<std::string>& result);\n    static std::vector<std::string>::iterator findString(std::vector<std::string>& source, const std::string& string);\n\n    static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);\n    static void addPaths(std::vector<boost::filesystem::path>& output, std::vector<std::string> input);\n\n    /// \\return file's \"last modified time\", used in original MW to determine plug-in load order\n    static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime);\n\n    bool mVerbose;\n    strmap mMergeMap;\n    std::vector<std::string> mMergeFallback;\n    ToUTF8::FromType mEncoding;\n};\n\n#endif\n"
  },
  {
    "path": "apps/mwiniimporter/main.cpp",
    "content": "#include \"importer.hpp\"\n\n#include <iostream>\n\n#include <boost/program_options.hpp>\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n\nnamespace bpo = boost::program_options;\nnamespace bfs = boost::filesystem;\n\n#ifndef _WIN32\nint main(int argc, char *argv[]) {\n#else\n\n// Include on Windows only\n#include <boost/locale.hpp>\n\nclass utf8argv\n{\npublic:\n\n    utf8argv(int argc, wchar_t *wargv[])\n    {\n        args.reserve(argc);\n        argv = new const char *[argc];\n\n        for (int i = 0; i < argc; ++i) {\n            args.push_back(boost::locale::conv::utf_to_utf<char>(wargv[i]));\n            argv[i] = args.back().c_str();\n        }\n    }\n\n    ~utf8argv() { delete[] argv; }\n    char **get() const { return const_cast<char **>(argv); }\n\nprivate:\n    utf8argv(const utf8argv&);\n    utf8argv& operator=(const utf8argv&);\n\n    const char **argv;\n    std::vector<std::string> args;\n};\n\n/*  The only way to pass Unicode on Winodws with CLI is to use wide\n    characters interface which presents UTF-16 encoding. The rest of\n    OpenMW application stack assumes UTF-8 encoding, therefore this\n    conversion.\n\n    For boost::filesystem::path::imbue see components/files/windowspath.cpp\n*/\nint wmain(int argc, wchar_t *wargv[]) {\n    utf8argv converter(argc, wargv);\n    char **argv = converter.get();\n    boost::filesystem::path::imbue(boost::locale::generator().generate(\"\"));\n#endif\n\n    try\n    {\n        bpo::options_description desc(\"Syntax: openmw-iniimporter <options> inifile configfile\\nAllowed options\");\n        bpo::positional_options_description p_desc;\n        desc.add_options()\n            (\"help,h\", \"produce help message\")\n            (\"verbose,v\", \"verbose output\")\n            (\"ini,i\", bpo::value<std::string>(), \"morrowind.ini file\")\n            (\"cfg,c\", bpo::value<std::string>(), \"openmw.cfg file\")\n            (\"output,o\", bpo::value<std::string>()->default_value(\"\"), \"openmw.cfg file\")\n            (\"game-files,g\", \"import esm and esp files\")\n            (\"no-archives,A\", \"disable bsa archives import\")\n            (\"encoding,e\", bpo::value<std::string>()-> default_value(\"win1252\"),\n                \"Character encoding used in OpenMW game messages:\\n\"\n                \"\\n\\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\\n\"\n                \"\\n\\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\\n\"\n                \"\\n\\twin1252 - Western European (Latin) alphabet, used by default\")\n            ;\n        p_desc.add(\"ini\", 1).add(\"cfg\", 1);\n\n        bpo::variables_map vm;\n\n        bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)\n            .options(desc)\n            .positional(p_desc)\n            .run();\n\n        bpo::store(parsed, vm);\n\n        if(vm.count(\"help\") || !vm.count(\"ini\") || !vm.count(\"cfg\")) {\n            std::cout << desc;\n            return 0;\n        }\n\n        bpo::notify(vm);\n\n        boost::filesystem::path iniFile(vm[\"ini\"].as<std::string>());\n        boost::filesystem::path cfgFile(vm[\"cfg\"].as<std::string>());\n\n        // if no output is given, write back to cfg file\n        std::string outputFile(vm[\"output\"].as<std::string>());\n        if(vm[\"output\"].defaulted()) {\n            outputFile = vm[\"cfg\"].as<std::string>();\n        }\n\n        if(!boost::filesystem::exists(iniFile)) {\n            std::cerr << \"ini file does not exist\" << std::endl;\n            return -3;\n        }\n        if(!boost::filesystem::exists(cfgFile))\n            std::cerr << \"cfg file does not exist\" << std::endl;\n\n        MwIniImporter importer;\n        importer.setVerbose(vm.count(\"verbose\") != 0);\n\n        // Font encoding settings\n        std::string encoding(vm[\"encoding\"].as<std::string>());\n        importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));\n\n        MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);\n        MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);\n\n        importer.merge(cfg, ini);\n        importer.mergeFallback(cfg, ini);\n\n        if(vm.count(\"game-files\")) {\n            importer.importGameFiles(cfg, ini, iniFile);\n        }\n\n        if(!vm.count(\"no-archives\")) {\n            importer.importArchives(cfg, ini);\n        }\n\n        std::cout << \"write to: \" << outputFile << std::endl;\n        bfs::ofstream file((bfs::path(outputFile)));\n        importer.writeToFile(file, cfg);\n    }\n    catch (std::exception& e)\n    {\n        std::cerr << \"ERROR: \" << e.what() << std::endl;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "apps/niftest/CMakeLists.txt",
    "content": "set(NIFTEST\n    niftest.cpp\n)\nsource_group(components\\\\nif\\\\tests FILES ${NIFTEST})\n\n# Main executable\nopenmw_add_executable(niftest\n    ${NIFTEST}\n)\n\ntarget_link_libraries(niftest\n  ${Boost_FILESYSTEM_LIBRARY}\n  components\n)\n\nif (BUILD_WITH_CODE_COVERAGE)\n  add_definitions (--coverage)\n  target_link_libraries(niftest gcov)\nendif()\n"
  },
  {
    "path": "apps/niftest/niftest.cpp",
    "content": "///Program to test .nif files both on the FileSystem and in BSA archives.\n\n#include <iostream>\n#include <fstream>\n#include <cstdlib>\n\n#include <components/nif/niffile.hpp>\n#include <components/files/constrainedfilestream.hpp>\n#include <components/vfs/manager.hpp>\n#include <components/vfs/bsaarchive.hpp>\n#include <components/vfs/filesystemarchive.hpp>\n\n#include <boost/program_options.hpp>\n#include <boost/filesystem.hpp>\n\n// Create local aliases for brevity\nnamespace bpo = boost::program_options;\nnamespace bfs = boost::filesystem;\n\n///See if the file has the named extension\nbool hasExtension(std::string filename, std::string  extensionToFind)\n{\n    std::string extension = filename.substr(filename.find_last_of('.')+1);\n\n    //Convert strings to lower case for comparison\n    std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);\n    std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower);\n\n    if(extension == extensionToFind)\n        return true;\n    else\n        return false;\n}\n\n///See if the file has the \"nif\" extension.\nbool isNIF(const std::string & filename)\n{\n    return hasExtension(filename,\"nif\");\n}\n///See if the file has the \"bsa\" extension.\nbool isBSA(const std::string & filename)\n{\n    return hasExtension(filename,\"bsa\");\n}\n\n/// Check all the nif files in a given VFS::Archive\n/// \\note Takes ownership!\n/// \\note Can not read a bsa file inside of a bsa file.\nvoid readVFS(VFS::Archive* anArchive,std::string archivePath = \"\")\n{\n    VFS::Manager myManager(true);\n    myManager.addArchive(anArchive);\n    myManager.buildIndex();\n\n    std::map<std::string, VFS::File*> files=myManager.getIndex();\n    for(std::map<std::string, VFS::File*>::const_iterator it=files.begin(); it!=files.end(); ++it)\n    {\n        std::string name = it->first;\n\n        try{\n            if(isNIF(name))\n            {\n            //           std::cout << \"Decoding: \" << name << std::endl;\n                Nif::NIFFile temp_nif(myManager.get(name),archivePath+name);\n            }\n            else if(isBSA(name))\n            {\n                if(!archivePath.empty() && !isBSA(archivePath))\n                {\n//                     std::cout << \"Reading BSA File: \" << name << std::endl;\n                    readVFS(new VFS::BsaArchive(archivePath+name),archivePath+name+\"/\");\n//                     std::cout << \"Done with BSA File: \" << name << std::endl;\n                }\n            }\n        }\n        catch (std::exception& e)\n        {\n            std::cerr << \"ERROR, an exception has occurred:  \" << e.what() << std::endl;\n        }\n    }\n}\n\nbool parseOptions (int argc, char** argv, std::vector<std::string>& files)\n{\n    bpo::options_description desc(\"Ensure that OpenMW can use the provided NIF and BSA files\\n\\n\"\n        \"Usages:\\n\"\n        \"  niftool <nif files, BSA files, or directories>\\n\"\n        \"      Scan the file or directories for nif errors.\\n\\n\"\n        \"Allowed options\");\n    desc.add_options()\n        (\"help,h\", \"print help message.\")\n        (\"input-file\", bpo::value< std::vector<std::string> >(), \"input file\")\n        ;\n\n    //Default option if none provided\n    bpo::positional_options_description p;\n    p.add(\"input-file\", -1);\n\n    bpo::variables_map variables;\n    try\n    {\n        bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).\n            options(desc).positional(p).run();\n        bpo::store(valid_opts, variables);\n        bpo::notify(variables);\n        if (variables.count (\"help\"))\n        {\n            std::cout << desc << std::endl;\n            return false;\n        }\n        if (variables.count(\"input-file\"))\n        {\n            files = variables[\"input-file\"].as< std::vector<std::string> >();\n            return true;\n        }\n    }\n    catch(std::exception &e)\n    {\n        std::cout << \"ERROR parsing arguments: \" << e.what() << \"\\n\\n\"\n            << desc << std::endl;\n        return false;\n    }\n\n    std::cout << \"No input files or directories specified!\" << std::endl;\n    std::cout << desc << std::endl;\n    return false;\n}\n\nint main(int argc, char **argv)\n{\n    std::vector<std::string> files;\n    if(!parseOptions (argc, argv, files))\n        return 1;\n\n    Nif::NIFFile::setLoadUnsupportedFiles(true);\n//     std::cout << \"Reading Files\" << std::endl;\n    for(std::vector<std::string>::const_iterator it=files.begin(); it!=files.end(); ++it)\n    {\n        std::string name = *it;\n\n        try\n        {\n            if(isNIF(name))\n            {\n                //std::cout << \"Decoding: \" << name << std::endl;\n                Nif::NIFFile temp_nif(Files::openConstrainedFileStream(name.c_str()),name);\n             }\n             else if(isBSA(name))\n             {\n//                 std::cout << \"Reading BSA File: \" << name << std::endl;\n                readVFS(new VFS::BsaArchive(name));\n             }\n             else if(bfs::is_directory(bfs::path(name)))\n             {\n//                 std::cout << \"Reading All Files in: \" << name << std::endl;\n                readVFS(new VFS::FileSystemArchive(name),name);\n             }\n             else\n             {\n                 std::cerr << \"ERROR:  \\\"\" << name << \"\\\" is not a nif file, bsa file, or directory!\" << std::endl;\n             }\n        }\n        catch (std::exception& e)\n        {\n            std::cerr << \"ERROR, an exception has occurred:  \" << e.what() << std::endl;\n        }\n     }\n     return 0;\n}\n"
  },
  {
    "path": "apps/opencs/CMakeLists.txt",
    "content": "set (OPENCS_SRC main.cpp\n    ${CMAKE_SOURCE_DIR}/files/windows/opencs.rc\n    )\n\nopencs_units (. editor)\n\nopencs_units (model/doc\n    document operation saving documentmanager loader runner operationholder\n    )\n\nopencs_units_noqt (model/doc\n    stage savingstate savingstages blacklist messages\n    )\n\nopencs_hdrs_noqt (model/doc\n    state\n    )\n\n\nopencs_units (model/world\n    idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel\n    actoradapter\n    )\n\n\nopencs_units_noqt (model/world\n    universalid record commands columnbase columnimp scriptcontext cell refidcollection\n    refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope\n    pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection\n    idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro\n    )\n\nopencs_hdrs_noqt (model/world\n    columnimp idcollection collection info subcellcollection\n    )\n\n\nopencs_units (model/tools\n    tools reportmodel mergeoperation\n    )\n\nopencs_units_noqt (model/tools\n    mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck\n    birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck\n    startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck\n    mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck\n    )\n\nopencs_hdrs_noqt (model/tools\n    mergestate\n    )\n\n\nopencs_units (view/doc\n    viewmanager view operations operation subview startup filedialog newgame\n    filewidget adjusterwidget loader globaldebugprofilemenu runlogsubview sizehint\n    )\n\n\nopencs_units_noqt (view/doc\n    subviewfactory\n    )\n\nopencs_hdrs_noqt (view/doc\n    subviewfactoryimp\n    )\n\n\nopencs_units (view/world\n    table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator globalcreator\n    cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview\n    infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable\n    dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator\n    bodypartcreator landtexturecreator landcreator\n    )\n\nopencs_units_noqt (view/world\n    subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate\n    scripthighlighter idvalidator dialoguecreator idcompletiondelegate\n    colordelegate dragdroputils\n    )\n\nopencs_units (view/widget\n    scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton\n    scenetooltoggle2 scenetooltexturebrush scenetoolshapebrush completerpopup coloreditor colorpickerpopup droplineedit\n    )\n\nopencs_units (view/render\n    scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget\n    previewwidget editmode instancemode instanceselectionmode instancemovemode\n    orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller\n    cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands\n    )\n\nopencs_units_noqt (view/render\n    lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase\n    cellarrow cellmarker cellborder pathgrid\n    )\n\nopencs_hdrs_noqt (view/render\n    mask\n    )\n\n\nopencs_units (view/tools\n    reportsubview reporttable searchsubview searchbox merge\n    )\n\nopencs_units_noqt (view/tools\n    subviews\n    )\n\nopencs_units (view/prefs\n    dialogue pagebase page keybindingpage contextmenulist\n    )\n\nopencs_units (model/prefs\n    state setting intsetting doublesetting boolsetting enumsetting coloursetting shortcut\n    shortcuteventhandler shortcutmanager shortcutsetting modifiersetting stringsetting\n    )\n\nopencs_units_noqt (model/prefs\n    category\n    )\n\nopencs_units_noqt (model/filter\n    node unarynode narynode leafnode booleannode parser andnode ornode notnode textnode valuenode\n    )\n\nopencs_units (view/filter\n    filterbox recordfilterbox editwidget\n    )\n\nset (OPENCS_US\n    )\n\nset (OPENCS_RES ${CMAKE_SOURCE_DIR}/files/opencs/resources.qrc\n                ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc\n    )\n\nset (OPENCS_UI\n    ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui\n    )\n\nsource_group (openmw-cs FILES ${OPENCS_SRC} ${OPENCS_HDR})\n\nif(WIN32)\n    set(QT_USE_QTMAIN TRUE)\nendif(WIN32)\n\nqt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})\nqt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})\nqt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES})\n\n# for compiled .ui files\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR})\n\nif(APPLE)\n    set (OPENCS_MAC_ICON \"${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns\")\n    set (OPENCS_CFG \"${OpenMW_BINARY_DIR}/defaults-cs.bin\")\n    set (OPENCS_DEFAULT_FILTERS_FILE \"${OpenMW_BINARY_DIR}/resources/defaultfilters\")\n    set (OPENCS_OPENMW_CFG \"${OpenMW_BINARY_DIR}/openmw.cfg\")\nelse()\n    set (OPENCS_MAC_ICON \"\")\n    set (OPENCS_CFG \"\")\n    set (OPENCS_DEFAULT_FILTERS_FILE \"\")\n    set (OPENCS_OPENMW_CFG \"\")\nendif(APPLE)\n\nopenmw_add_executable(openmw-cs\n    MACOSX_BUNDLE\n    ${OPENCS_SRC}\n    ${OPENCS_UI_HDR}\n    ${OPENCS_MOC_SRC}\n    ${OPENCS_RES_SRC}\n    ${OPENCS_MAC_ICON}\n    ${OPENCS_CFG}\n    ${OPENCS_DEFAULT_FILTERS_FILE}\n    ${OPENCS_OPENMW_CFG}\n)\n\nif(APPLE)\n    set(OPENCS_BUNDLE_NAME \"OpenMW-CS\")\n    set(OPENCS_BUNDLE_RESOURCES_DIR \"${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources\")\n\n    set(OPENMW_MYGUI_FILES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})\n    set(OPENMW_SHADERS_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})\n\n    add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)\n\n    set_target_properties(openmw-cs PROPERTIES\n        RUNTIME_OUTPUT_DIRECTORY \"${OpenMW_BINARY_DIR}\"\n        OUTPUT_NAME ${OPENCS_BUNDLE_NAME}\n        MACOSX_BUNDLE_ICON_FILE \"openmw-cs.icns\"\n        MACOSX_BUNDLE_BUNDLE_NAME \"OpenMW-CS\"\n        MACOSX_BUNDLE_GUI_IDENTIFIER \"org.openmw.opencs\"\n        MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}\n        MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION}\n        MACOSX_BUNDLE_INFO_PLIST \"${CMAKE_SOURCE_DIR}/files/mac/openmw-cs-Info.plist.in\"\n        )\n\n    set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES\n        MACOSX_PACKAGE_LOCATION Resources)\n    set_source_files_properties(${OPENCS_CFG} PROPERTIES\n        MACOSX_PACKAGE_LOCATION Resources)\n    set_source_files_properties(${OPENCS_DEFAULT_FILTERS_FILE} PROPERTIES\n        MACOSX_PACKAGE_LOCATION Resources/resources)\n    set_source_files_properties(${OPENCS_OPENMW_CFG} PROPERTIES\n        MACOSX_PACKAGE_LOCATION Resources)\n\n    add_custom_command(TARGET openmw-cs\n        POST_BUILD\n        COMMAND cp \"${OpenMW_BINARY_DIR}/resources/version\" \"${OPENCS_BUNDLE_RESOURCES_DIR}/resources\")\nendif(APPLE)\n\ntarget_link_libraries(openmw-cs\n    # CMake's built-in OSG finder does not use pkgconfig, so we have to\n    # manually ensure the order is correct for inter-library dependencies.\n    # This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.\n    # https://gitlab.kitware.com/cmake/cmake/-/issues/21701\n    ${OSGVIEWER_LIBRARIES}\n    ${OSGFX_LIBRARIES}\n    ${OSGGA_LIBRARIES}\n    ${OSGUTIL_LIBRARIES}\n    ${OSGTEXT_LIBRARIES}\n    ${OSG_LIBRARIES}\n    ${EXTERN_OSGQT_LIBRARY}\n    ${Boost_SYSTEM_LIBRARY}\n    ${Boost_FILESYSTEM_LIBRARY}\n    ${Boost_PROGRAM_OPTIONS_LIBRARY}\n    components\n)\n\nif(OSG_STATIC)\n    unset(_osg_plugins_static_files)\n    add_library(openmw_cs_osg_plugins INTERFACE)\n    foreach(_plugin ${USED_OSG_PLUGINS})\n        string(TOUPPER ${_plugin} _plugin_uc)\n        if(OPENMW_USE_SYSTEM_OSG)\n            list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY})\n        else()\n            list(APPEND _osg_plugins_static_files $<TARGET_FILE:${${_plugin_uc}_LIBRARY}>)\n            target_link_libraries(openmw_cs_osg_plugins INTERFACE $<TARGET_PROPERTY:${${_plugin_uc}_LIBRARY},LINK_LIBRARIES>)\n            add_dependencies(openmw_cs_osg_plugins ${${_plugin_uc}_LIBRARY})\n        endif()\n    endforeach()\n    # We use --whole-archive because OSG plugins use registration.\n    get_whole_archive_options(_opts ${_osg_plugins_static_files})\n    target_link_options(openmw_cs_osg_plugins INTERFACE ${_opts})\n    target_link_libraries(openmw-cs openmw_cs_osg_plugins)\n\n    if(OPENMW_USE_SYSTEM_OSG)\n        # OSG plugin pkgconfig files are missing these dependencies.\n        # https://github.com/openscenegraph/OpenSceneGraph/issues/1052\n        target_link_libraries(openmw freetype jpeg png)\n    endif()\nendif(OSG_STATIC)\n\ntarget_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)\n\nif (WIN32)\n    target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})\n    INSTALL(TARGETS openmw-cs RUNTIME DESTINATION \".\")\n\n    get_generator_is_multi_config(multi_config)\n    if (multi_config)\n        SET(INSTALL_SOURCE \"${OpenMW_BINARY_DIR}/$<CONFIG>\")\n    else ()\n        SET(INSTALL_SOURCE \"${OpenMW_BINARY_DIR}\")\n    endif ()\n\n    INSTALL(FILES \"${INSTALL_SOURCE}/defaults-cs.bin\" DESTINATION \".\")\nendif()\n\nif (MSVC)\n    # Debug version needs increased number of sections beyond 2^16\n    if (CMAKE_CL_64)\n        set (CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} /bigobj\")\n    endif (CMAKE_CL_64)\nendif (MSVC)\n\n\nif(APPLE)\n    INSTALL(TARGETS openmw-cs BUNDLE DESTINATION \".\" COMPONENT Bundle)\nendif()\n"
  },
  {
    "path": "apps/opencs/Networking.cpp",
    "content": "#include \"editor.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <string>\n\n#include <QApplication>\n#include <QIcon>\n#include <QMetaType>\n\n#include \"model/doc/messages.hpp\"\n\n#include \"model/world/universalid.hpp\"\n\n#ifdef Q_OS_MAC\n#include <QDir>\n#endif\n\nQ_DECLARE_METATYPE (std::string)\n\nclass Application : public QApplication\n{\n    private:\n\n        bool notify (QObject *receiver, QEvent *event)\n        {\n            try\n            {\n                return QApplication::notify (receiver, event);\n            }\n            catch (const std::exception& exception)\n            {\n                std::cerr << \"An exception has been caught: \" << exception.what() << std::endl;\n            }\n\n            return false;\n        }\n\n    public:\n\n        Application (int& argc, char *argv[]) : QApplication (argc, argv) {}\n};\n\nint main(int argc, char *argv[])\n{\n    #ifdef Q_OS_MAC\n        setenv(\"OSG_GL_TEXTURE_STORAGE\", \"OFF\", 0);\n    #endif\n\n    try\n    {\n        // To allow background thread drawing in OSG\n        QApplication::setAttribute(Qt::AA_X11InitThreads, true);\n\n        Q_INIT_RESOURCE (resources);\n\n        qRegisterMetaType<std::string> (\"std::string\");\n        qRegisterMetaType<CSMWorld::UniversalId> (\"CSMWorld::UniversalId\");\n        qRegisterMetaType<CSMDoc::Message> (\"CSMDoc::Message\");\n\n        Application application (argc, argv);\n\n    #ifdef Q_OS_MAC\n        QDir dir(QCoreApplication::applicationDirPath());\n        if (dir.dirName() == \"MacOS\") {\n            dir.cdUp();\n            dir.cdUp();\n            dir.cdUp();\n        }\n        QDir::setCurrent(dir.absolutePath());\n    #endif\n\n        application.setWindowIcon (QIcon (\":./openmw-cs.png\"));\n\n        CS::Editor editor;\n\n        if(!editor.makeIPCServer())\n        {\n            editor.connectToIPCServer();\n            return 0;\n        }\n        return editor.run();\n    }\n    catch (std::exception& e)\n    {\n        std::cerr << \"ERROR: \" << e.what() << std::endl;\n        return 0;\n    }\n\n}\n"
  },
  {
    "path": "apps/opencs/editor.cpp",
    "content": "#include \"editor.hpp\"\n\n#include <QApplication>\n#include <QLocalServer>\n#include <QLocalSocket>\n#include <QMessageBox>\n\n#include <components/debug/debuglog.hpp>\n#include <components/fallback/validate.hpp>\n#include <components/misc/rng.hpp>\n#include <components/nifosg/nifloader.hpp>\n\n#include \"model/doc/document.hpp\"\n#include \"model/world/data.hpp\"\n\n#ifdef _WIN32\n#include <Windows.h>\n#endif\n\nusing namespace Fallback;\n\nCS::Editor::Editor (int argc, char **argv)\n: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),\n  mPid(\"\"), mLock(), mMerge (mDocumentManager),\n  mIpcServerName (\"org.openmw.OpenCS\"), mServer(nullptr), mClientSocket(nullptr)\n{\n    std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();\n\n    mViewManager = new CSVDoc::ViewManager(mDocumentManager);\n    if (argc > 1)\n    {\n        mFileToLoad = argv[1];\n        mDataDirs = config.first;\n    }\n\n    NifOsg::Loader::setShowMarkers(true);\n\n    mDocumentManager.setFileData(mFsStrict, config.first, config.second);\n\n    mNewGame.setLocalData (mLocal);\n    mFileDialog.setLocalData (mLocal);\n    mMerge.setLocalData (mLocal);\n\n    connect (&mDocumentManager, SIGNAL (documentAdded (CSMDoc::Document *)),\n        this, SLOT (documentAdded (CSMDoc::Document *)));\n    connect (&mDocumentManager, SIGNAL (documentAboutToBeRemoved (CSMDoc::Document *)),\n        this, SLOT (documentAboutToBeRemoved (CSMDoc::Document *)));\n    connect (&mDocumentManager, SIGNAL (lastDocumentDeleted()),\n        this, SLOT (lastDocumentDeleted()));\n\n    connect (mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));\n    connect (mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));\n    connect (mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));\n    connect (mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ()));\n    connect (mViewManager, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SLOT (mergeDocument (CSMDoc::Document *)));\n\n    connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ()));\n    connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ()));\n    connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));\n    connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ()));\n\n    connect (&mFileDialog, SIGNAL(signalOpenFiles (const boost::filesystem::path&)),\n             this, SLOT(openFiles (const boost::filesystem::path&)));\n\n    connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)),\n             this, SLOT(createNewFile (const boost::filesystem::path&)));\n    connect (&mFileDialog, SIGNAL (rejected()), this, SLOT (cancelFileDialog ()));\n\n    connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),\n             this, SLOT (createNewGame (const boost::filesystem::path&)));\n    connect (&mNewGame, SIGNAL (cancelCreateGame()), this, SLOT (cancelCreateGame ()));\n}\n\nCS::Editor::~Editor ()\n{\n    delete mViewManager;\n\n    mPidFile.close();\n\n    if(mServer && boost::filesystem::exists(mPid))\n        static_cast<void> ( // silence coverity warning\n        remove(mPid.string().c_str())); // ignore any error\n}\n\nstd::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfig(bool quiet)\n{\n    boost::program_options::variables_map variables;\n    boost::program_options::options_description desc(\"Syntax: openmw-cs <options>\\nAllowed options\");\n\n    desc.add_options()\n    (\"data\", boost::program_options::value<Files::EscapePathContainer>()->default_value(Files::EscapePathContainer(), \"data\")->multitoken()->composing())\n    (\"data-local\", boost::program_options::value<Files::EscapePath>()->default_value(Files::EscapePath(), \"\"))\n    (\"fs-strict\", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))\n    (\"encoding\", boost::program_options::value<Files::EscapeHashString>()->default_value(\"win1252\"))\n    (\"resources\", boost::program_options::value<Files::EscapePath>()->default_value(Files::EscapePath(), \"resources\"))\n    (\"fallback-archive\", boost::program_options::value<Files::EscapeStringVector>()->\n        default_value(Files::EscapeStringVector(), \"fallback-archive\")->multitoken())\n    (\"fallback\", boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), \"\")\n        ->multitoken()->composing(), \"fallback values\")\n    (\"script-blacklist\", boost::program_options::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), \"\")\n        ->multitoken(), \"exclude specified script from the verifier (if the use of the blacklist is enabled)\")\n    (\"script-blacklist-use\", boost::program_options::value<bool>()->implicit_value(true)\n        ->default_value(true), \"enable script blacklisting\");\n\n    boost::program_options::notify(variables);\n\n    mCfgMgr.readConfiguration(variables, desc, false);\n\n    Fallback::Map::init(variables[\"fallback\"].as<FallbackMap>().mMap);\n\n    mEncodingName = variables[\"encoding\"].as<Files::EscapeHashString>().toStdString();\n    mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName));\n    mFileDialog.setEncoding (QString::fromUtf8(mEncodingName.c_str()));\n\n    mDocumentManager.setResourceDir (mResources = variables[\"resources\"].as<Files::EscapePath>().mPath);\n\n    if (variables[\"script-blacklist-use\"].as<bool>())\n        mDocumentManager.setBlacklistedScripts (\n            variables[\"script-blacklist\"].as<Files::EscapeStringVector>().toStdStringVector());\n\n    mFsStrict = variables[\"fs-strict\"].as<bool>();\n\n    Files::PathContainer dataDirs, dataLocal;\n    if (!variables[\"data\"].empty()) {\n        dataDirs = Files::PathContainer(Files::EscapePath::toPathContainer(variables[\"data\"].as<Files::EscapePathContainer>()));\n    }\n\n    Files::PathContainer::value_type local(variables[\"data-local\"].as<Files::EscapePath>().mPath);\n    if (!local.empty())\n        dataLocal.push_back(local);\n\n    mCfgMgr.processPaths (dataDirs);\n    mCfgMgr.processPaths (dataLocal, true);\n\n    if (!dataLocal.empty())\n        mLocal = dataLocal[0];\n    else\n    {\n        QMessageBox messageBox;\n        messageBox.setWindowTitle (tr (\"No local data path available\"));\n        messageBox.setIcon (QMessageBox::Critical);\n        messageBox.setStandardButtons (QMessageBox::Ok);\n        messageBox.setText(tr(\"<br><b>OpenCS is unable to access the local data directory. This may indicate a faulty configuration or a broken install.</b>\"));\n        messageBox.exec();\n\n        QApplication::exit (1);\n    }\n\n    dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());\n\n    //iterate the data directories and add them to the file dialog for loading\n    for (Files::PathContainer::const_reverse_iterator iter = dataDirs.rbegin(); iter != dataDirs.rend(); ++iter)\n    {\n        QString path = QString::fromUtf8 (iter->string().c_str());\n        mFileDialog.addFiles(path);\n    }\n\n    return std::make_pair (dataDirs, variables[\"fallback-archive\"].as<Files::EscapeStringVector>().toStdStringVector());\n}\n\nvoid CS::Editor::createGame()\n{\n    mStartup.hide();\n\n    if (mNewGame.isHidden())\n        mNewGame.show();\n\n    mNewGame.raise();\n    mNewGame.activateWindow();\n}\n\nvoid CS::Editor::cancelCreateGame()\n{\n    if (!mDocumentManager.isEmpty())\n        return;\n\n    mNewGame.hide();\n\n    if (mStartup.isHidden())\n        mStartup.show();\n\n    mStartup.raise();\n    mStartup.activateWindow();\n}\n\nvoid CS::Editor::createAddon()\n{\n    mStartup.hide();\n\n    mFileDialog.clearFiles();\n    readConfig(/*quiet*/true);\n\n    mFileDialog.showDialog (CSVDoc::ContentAction_New);\n}\n\nvoid CS::Editor::cancelFileDialog()\n{\n    if (!mDocumentManager.isEmpty())\n        return;\n\n    mFileDialog.hide();\n\n    if (mStartup.isHidden())\n        mStartup.show();\n\n    mStartup.raise();\n    mStartup.activateWindow();\n}\n\nvoid CS::Editor::loadDocument()\n{\n    mStartup.hide();\n\n    mFileDialog.clearFiles();\n    readConfig(/*quiet*/true);\n\n    mFileDialog.showDialog (CSVDoc::ContentAction_Edit);\n}\n\nvoid CS::Editor::openFiles (const boost::filesystem::path &savePath, const std::vector<boost::filesystem::path> &discoveredFiles)\n{\n    std::vector<boost::filesystem::path> files;\n\n    if(discoveredFiles.empty())\n    {\n        for (const QString &path : mFileDialog.selectedFilePaths())\n            files.emplace_back(path.toUtf8().constData());\n    }\n    else\n    {\n        files = discoveredFiles;\n    }\n\n    mDocumentManager.addDocument (files, savePath, false);\n\n    mFileDialog.hide();\n}\n\nvoid CS::Editor::createNewFile (const boost::filesystem::path &savePath)\n{\n    std::vector<boost::filesystem::path> files;\n\n    for (const QString &path : mFileDialog.selectedFilePaths()) {\n        files.emplace_back(path.toUtf8().constData());\n    }\n\n    files.push_back (savePath);\n\n    mDocumentManager.addDocument (files, savePath, true);\n\n    mFileDialog.hide();\n}\n\nvoid CS::Editor::createNewGame (const boost::filesystem::path& file)\n{\n    std::vector<boost::filesystem::path> files;\n\n    files.push_back (file);\n\n    mDocumentManager.addDocument (files, file, true);\n\n    mNewGame.hide();\n}\n\nvoid CS::Editor::showStartup()\n{\n    if(mStartup.isHidden())\n        mStartup.show();\n    mStartup.raise();\n    mStartup.activateWindow();\n}\n\nvoid CS::Editor::showSettings()\n{\n    if (mSettings.isHidden())\n        mSettings.show();\n\n    mSettings.move (QCursor::pos());\n    mSettings.raise();\n    mSettings.activateWindow();\n}\n\nbool CS::Editor::makeIPCServer()\n{\n    try\n    {\n        mPid = boost::filesystem::temp_directory_path();\n        mPid /= \"openmw-cs.pid\";\n        bool pidExists = boost::filesystem::exists(mPid);\n\n        mPidFile.open(mPid);\n\n        mLock = boost::interprocess::file_lock(mPid.string().c_str());\n        if(!mLock.try_lock())\n        {\n            Log(Debug::Error) << \"Error: OpenMW-CS is already running.\";\n            return false;\n        }\n\n#ifdef _WIN32\n        mPidFile << GetCurrentProcessId() << std::endl;\n#else\n        mPidFile << getpid() << std::endl;\n#endif\n\n        mServer = new QLocalServer(this);\n\n        if(pidExists)\n        {\n            // hack to get the temp directory path\n            mServer->listen(\"dummy\");\n            QString fullPath = mServer->fullServerName();\n            mServer->close();\n            fullPath.remove(QRegExp(\"dummy$\"));\n            fullPath += mIpcServerName;\n            if(boost::filesystem::exists(fullPath.toUtf8().constData()))\n            {\n                // TODO: compare pid of the current process with that in the file\n                Log(Debug::Info) << \"Detected unclean shutdown.\";\n                // delete the stale file\n                if(remove(fullPath.toUtf8().constData()))\n                    Log(Debug::Error) << \"Error: can not remove stale connection file.\";\n            }\n        }\n    }\n\n    catch(const std::exception& e)\n    {\n        Log(Debug::Error) << \"Error: \" << e.what();\n        return false;\n    }\n\n    if(mServer->listen(mIpcServerName))\n    {\n        connect(mServer, SIGNAL(newConnection()), this, SLOT(showStartup()));\n        return true;\n    }\n\n    mServer->close();\n    mServer = nullptr;\n    return false;\n}\n\nvoid CS::Editor::connectToIPCServer()\n{\n    mClientSocket = new QLocalSocket(this);\n    mClientSocket->connectToServer(mIpcServerName);\n    mClientSocket->close();\n}\n\nint CS::Editor::run()\n{\n    if (mLocal.empty())\n        return 1;\n\n    Misc::Rng::init();\n\n    QApplication::setQuitOnLastWindowClosed(true);\n\n    if (mFileToLoad.empty())\n    {\n        mStartup.show();\n    }\n    else\n    {\n        ESM::ESMReader fileReader;\n        ToUTF8::Utf8Encoder encoder = ToUTF8::calculateEncoding(mEncodingName);\n        fileReader.setEncoder(&encoder);\n        fileReader.open(mFileToLoad.string());\n\n        std::vector<boost::filesystem::path> discoveredFiles;\n\n        for (std::vector<ESM::Header::MasterData>::const_iterator itemIter = fileReader.getGameFiles().begin();\n            itemIter != fileReader.getGameFiles().end(); ++itemIter)\n        {\n            for (Files::PathContainer::const_iterator pathIter = mDataDirs.begin();\n                pathIter != mDataDirs.end(); ++pathIter)\n            {\n                const boost::filesystem::path masterPath = *pathIter / itemIter->name;\n                if (boost::filesystem::exists(masterPath))\n                {\n                    discoveredFiles.push_back(masterPath);\n                    break;\n                }\n            }\n        }\n        discoveredFiles.push_back(mFileToLoad);\n\n        QString extension = QString::fromStdString(mFileToLoad.extension().string()).toLower();\n        if (extension == \".esm\")\n        {\n            mFileToLoad.replace_extension(\".omwgame\");\n            mDocumentManager.addDocument(discoveredFiles, mFileToLoad, false);\n        }\n        else if (extension == \".esp\")\n        {\n            mFileToLoad.replace_extension(\".omwaddon\");\n            mDocumentManager.addDocument(discoveredFiles, mFileToLoad, false);\n        }\n        else\n        {\n            openFiles(mFileToLoad, discoveredFiles);\n        }\n    }\n\n    return QApplication::exec();\n}\n\nvoid CS::Editor::documentAdded (CSMDoc::Document *document)\n{\n    mViewManager->addView (document);\n}\n\nvoid CS::Editor::documentAboutToBeRemoved (CSMDoc::Document *document)\n{\n    if (mMerge.getDocument()==document)\n        mMerge.cancel();\n}\n\nvoid CS::Editor::lastDocumentDeleted()\n{\n    QApplication::quit();\n}\n\nvoid CS::Editor::mergeDocument (CSMDoc::Document *document)\n{\n    mMerge.configure (document);\n    mMerge.show();\n    mMerge.raise();\n    mMerge.activateWindow();\n}\n"
  },
  {
    "path": "apps/opencs/editor.hpp",
    "content": "#ifndef CS_EDITOR_H\n#define CS_EDITOR_H\n\n#include <boost/interprocess/sync/file_lock.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <QObject>\n#include <QString>\n#include <QLocalServer>\n#include <QLocalSocket>\n\n#ifndef Q_MOC_RUN\n#include <components/files/configurationmanager.hpp>\n#endif\n\n#include <components/files/multidircollection.hpp>\n\n#include \"model/doc/documentmanager.hpp\"\n\n#include \"model/prefs/state.hpp\"\n\n#include \"view/doc/viewmanager.hpp\"\n#include \"view/doc/startup.hpp\"\n#include \"view/doc/filedialog.hpp\"\n#include \"view/doc/newgame.hpp\"\n\n#include \"view/prefs/dialogue.hpp\"\n\n#include \"view/tools/merge.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CS\n{\n    class Editor : public QObject\n    {\n            Q_OBJECT\n\n            Files::ConfigurationManager mCfgMgr;\n            CSMPrefs::State mSettingsState;\n            CSMDoc::DocumentManager mDocumentManager;\n            CSVDoc::StartupDialogue mStartup;\n            CSVDoc::NewGameDialogue mNewGame;\n            CSVPrefs::Dialogue mSettings;\n            CSVDoc::FileDialog mFileDialog;\n            boost::filesystem::path mLocal;\n            boost::filesystem::path mResources;\n            boost::filesystem::path mPid;\n            boost::interprocess::file_lock mLock;\n            boost::filesystem::ofstream mPidFile;\n            bool mFsStrict;\n            CSVTools::Merge mMerge;\n            CSVDoc::ViewManager* mViewManager;\n            boost::filesystem::path mFileToLoad;\n            Files::PathContainer mDataDirs;\n            std::string mEncodingName;\n\n            std::pair<Files::PathContainer, std::vector<std::string> > readConfig(bool quiet=false);\n            ///< \\return data paths\n\n            // not implemented\n            Editor (const Editor&);\n            Editor& operator= (const Editor&);\n\n        public:\n\n            Editor (int argc, char **argv);\n            ~Editor ();\n\n            bool makeIPCServer();\n            void connectToIPCServer();\n\n            int run();\n            ///< \\return error status\n\n        private slots:\n\n            void createGame();\n            void createAddon();\n            void cancelCreateGame();\n            void cancelFileDialog();\n\n            void loadDocument();\n            void openFiles (const boost::filesystem::path &path, const std::vector<boost::filesystem::path> &discoveredFiles = std::vector<boost::filesystem::path>());\n            void createNewFile (const boost::filesystem::path& path);\n            void createNewGame (const boost::filesystem::path& file);\n\n            void showStartup();\n\n            void showSettings();\n\n            void documentAdded (CSMDoc::Document *document);\n\n            void documentAboutToBeRemoved (CSMDoc::Document *document);\n\n            void lastDocumentDeleted();\n\n            void mergeDocument (CSMDoc::Document *document);\n\n        private:\n\n            QString mIpcServerName;\n            QLocalServer *mServer;\n            QLocalSocket *mClientSocket;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/main.cpp",
    "content": "#include \"editor.hpp\"\n\n#include <exception>\n#include <string>\n\n#include <QApplication>\n#include <QIcon>\n#include <QMetaType>\n\n#include <components/debug/debugging.hpp>\n\n#include \"model/doc/messages.hpp\"\n#include \"model/world/universalid.hpp\"\n\n#ifdef Q_OS_MAC\n#include <QDir>\n#endif\n\nQ_DECLARE_METATYPE (std::string)\n\nclass Application : public QApplication\n{\n    private:\n\n        bool notify (QObject *receiver, QEvent *event) override\n        {\n            try\n            {\n                return QApplication::notify (receiver, event);\n            }\n            catch (const std::exception& exception)\n            {\n                Log(Debug::Error) << \"An exception has been caught: \" << exception.what();\n            }\n\n            return false;\n        }\n\n    public:\n\n        Application (int& argc, char *argv[]) : QApplication (argc, argv) {}\n};\n\nint runApplication(int argc, char *argv[])\n{\n#ifdef Q_OS_MAC\n    setenv(\"OSG_GL_TEXTURE_STORAGE\", \"OFF\", 0);\n#endif\n\n    Q_INIT_RESOURCE (resources);\n\n    qRegisterMetaType<std::string> (\"std::string\");\n    qRegisterMetaType<CSMWorld::UniversalId> (\"CSMWorld::UniversalId\");\n    qRegisterMetaType<CSMDoc::Message> (\"CSMDoc::Message\");\n\n    Application application (argc, argv);\n\n#ifdef Q_OS_MAC\n    QDir dir(QCoreApplication::applicationDirPath());\n    QDir::setCurrent(dir.absolutePath());\n#endif\n\n    application.setWindowIcon (QIcon (\":./openmw-cs.png\"));\n\n    CS::Editor editor(argc, argv);\n#ifdef __linux__\n    setlocale(LC_NUMERIC,\"C\");\n#endif\n\n    if(!editor.makeIPCServer())\n    {\n        editor.connectToIPCServer();\n        return 0;\n    }\n\n    return editor.run();\n}\n\n\nint main(int argc, char *argv[])\n{\n    return wrapApplication(&runApplication, argc, argv, \"OpenMW-CS\");\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/blacklist.cpp",
    "content": "#include \"blacklist.hpp\"\n\n#include <algorithm>\n\n#include <components/misc/stringops.hpp>\n\nbool CSMDoc::Blacklist::isBlacklisted (const CSMWorld::UniversalId& id) const\n{\n    std::map<CSMWorld::UniversalId::Type, std::vector<std::string> >::const_iterator iter =\n        mIds.find (id.getType());\n\n    if (iter==mIds.end())\n        return false;\n\n    return std::binary_search (iter->second.begin(), iter->second.end(),\n        Misc::StringUtils::lowerCase (id.getId()));\n}\n\nvoid CSMDoc::Blacklist::add (CSMWorld::UniversalId::Type type,\n    const std::vector<std::string>& ids)\n{\n    std::vector<std::string>& list = mIds[type];\n\n    size_t size = list.size();\n\n    list.resize (size+ids.size());\n\n    std::transform (ids.begin(), ids.end(), list.begin()+size, Misc::StringUtils::lowerCase);\n    std::sort (list.begin(), list.end());\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/blacklist.hpp",
    "content": "#ifndef CSM_DOC_BLACKLIST_H\n#define CSM_DOC_BLACKLIST_H\n\n#include <map>\n#include <vector>\n#include <string>\n\n#include \"../world/universalid.hpp\"\n\nnamespace CSMDoc\n{\n    /// \\brief ID blacklist sorted by UniversalId type\n    class Blacklist\n    {\n            std::map<CSMWorld::UniversalId::Type, std::vector<std::string> > mIds;\n\n        public:\n\n            bool isBlacklisted (const CSMWorld::UniversalId& id) const;\n\n            void add (CSMWorld::UniversalId::Type type, const std::vector<std::string>& ids);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/document.cpp",
    "content": "#include \"document.hpp\"\n\n#include <cassert>\n\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include \"../world/defaultgmsts.hpp\"\n\n#ifndef Q_MOC_RUN\n#include <components/files/configurationmanager.hpp>\n#endif\n\n#include <components/debug/debuglog.hpp>\n\nvoid CSMDoc::Document::addGmsts()\n{\n    for (size_t i=0; i < CSMWorld::DefaultGmsts::FloatCount; ++i)\n    {\n        ESM::GameSetting gmst;\n        gmst.mId = CSMWorld::DefaultGmsts::Floats[i];\n        gmst.mValue.setType (ESM::VT_Float);\n        gmst.mValue.setFloat (CSMWorld::DefaultGmsts::FloatsDefaultValues[i]);\n        getData().getGmsts().add (gmst);\n    }\n\n    for (size_t i=0; i < CSMWorld::DefaultGmsts::IntCount; ++i)\n    {\n        ESM::GameSetting gmst;\n        gmst.mId = CSMWorld::DefaultGmsts::Ints[i];\n        gmst.mValue.setType (ESM::VT_Int);\n        gmst.mValue.setInteger (CSMWorld::DefaultGmsts::IntsDefaultValues[i]);\n        getData().getGmsts().add (gmst);\n    }\n\n    for (size_t i=0; i < CSMWorld::DefaultGmsts::StringCount; ++i)\n    {\n        ESM::GameSetting gmst;\n        gmst.mId = CSMWorld::DefaultGmsts::Strings[i];\n        gmst.mValue.setType (ESM::VT_String);\n        gmst.mValue.setString (\"\");\n        getData().getGmsts().add (gmst);\n    }\n}\n\nvoid CSMDoc::Document::addOptionalGmsts()\n{\n    for (size_t i=0; i < CSMWorld::DefaultGmsts::OptionalFloatCount; ++i)\n    {\n        ESM::GameSetting gmst;\n        gmst.mId = CSMWorld::DefaultGmsts::OptionalFloats[i];\n        gmst.blank();\n        gmst.mValue.setType (ESM::VT_Float);\n        addOptionalGmst (gmst);\n    }\n\n    for (size_t i=0; i < CSMWorld::DefaultGmsts::OptionalIntCount; ++i)\n    {\n        ESM::GameSetting gmst;\n        gmst.mId = CSMWorld::DefaultGmsts::OptionalInts[i];\n        gmst.blank();\n        gmst.mValue.setType (ESM::VT_Int);\n        addOptionalGmst (gmst);\n    }\n\n    for (size_t i=0; i < CSMWorld::DefaultGmsts::OptionalStringCount; ++i)\n    {\n        ESM::GameSetting gmst;\n        gmst.mId = CSMWorld::DefaultGmsts::OptionalStrings[i];\n        gmst.blank();\n        gmst.mValue.setType (ESM::VT_String);\n        gmst.mValue.setString (\"<no text>\");\n        addOptionalGmst (gmst);\n    }\n}\n\nvoid CSMDoc::Document::addOptionalGlobals()\n{\n    static const char *sGlobals[] =\n    {\n        \"DaysPassed\",\n        \"PCWerewolf\",\n        \"PCYear\",\n        0\n    };\n\n    for (int i=0; sGlobals[i]; ++i)\n    {\n        ESM::Global global;\n        global.mId = sGlobals[i];\n        global.blank();\n        global.mValue.setType (ESM::VT_Long);\n\n        if (i==0)\n            global.mValue.setInteger (1); // dayspassed starts counting at 1\n\n        addOptionalGlobal (global);\n    }\n}\n\nvoid CSMDoc::Document::addOptionalMagicEffects()\n{\n    for (int i=ESM::MagicEffect::SummonFabricant; i<=ESM::MagicEffect::SummonCreature05; ++i)\n    {\n        ESM::MagicEffect effect;\n        effect.mIndex = i;\n        effect.mId = ESM::MagicEffect::indexToId (i);\n        effect.blank();\n\n        addOptionalMagicEffect (effect);\n    }\n}\n\nvoid CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst)\n{\n    if (getData().getGmsts().searchId (gmst.mId)==-1)\n    {\n        CSMWorld::Record<ESM::GameSetting> record;\n        record.mBase = gmst;\n        record.mState = CSMWorld::RecordBase::State_BaseOnly;\n        getData().getGmsts().appendRecord (record);\n    }\n}\n\nvoid CSMDoc::Document::addOptionalGlobal (const ESM::Global& global)\n{\n    if (getData().getGlobals().searchId (global.mId)==-1)\n    {\n        CSMWorld::Record<ESM::Global> record;\n        record.mBase = global;\n        record.mState = CSMWorld::RecordBase::State_BaseOnly;\n        getData().getGlobals().appendRecord (record);\n    }\n}\n\nvoid CSMDoc::Document::addOptionalMagicEffect (const ESM::MagicEffect& magicEffect)\n{\n    if (getData().getMagicEffects().searchId (magicEffect.mId)==-1)\n    {\n        CSMWorld::Record<ESM::MagicEffect> record;\n        record.mBase = magicEffect;\n        record.mState = CSMWorld::RecordBase::State_BaseOnly;\n        getData().getMagicEffects().appendRecord (record);\n    }\n}\n\nvoid CSMDoc::Document::createBase()\n{\n    static const char *sGlobals[] =\n    {\n        \"Day\",\n        \"DaysPassed\",\n        \"GameHour\",\n        \"Month\",\n        \"PCRace\",\n        \"PCVampire\",\n        \"PCWerewolf\",\n        \"PCYear\",\n        0\n    };\n\n    for (int i=0; sGlobals[i]; ++i)\n    {\n        ESM::Global record;\n        record.mId = sGlobals[i];\n        record.mValue.setType (i==2 ? ESM::VT_Float : ESM::VT_Long);\n\n        if (i==0 || i==1)\n            record.mValue.setInteger (1);\n\n        getData().getGlobals().add (record);\n    }\n\n    addGmsts();\n\n    for (int i=0; i<27; ++i)\n    {\n        ESM::Skill record;\n        record.mIndex = i;\n        record.mId = ESM::Skill::indexToId (record.mIndex);\n        record.blank();\n\n        getData().getSkills().add (record);\n    }\n\n    static const char *sVoice[] =\n    {\n        \"Intruder\",\n        \"Attack\",\n        \"Hello\",\n        \"Thief\",\n        \"Alarm\",\n        \"Idle\",\n        \"Flee\",\n        \"Hit\",\n        0\n    };\n\n    for (int i=0; sVoice[i]; ++i)\n    {\n        ESM::Dialogue record;\n        record.mId = sVoice[i];\n        record.mType = ESM::Dialogue::Voice;\n        record.blank();\n\n        getData().getTopics().add (record);\n    }\n\n    static const char *sGreetings[] =\n    {\n        \"Greeting 0\",\n        \"Greeting 1\",\n        \"Greeting 2\",\n        \"Greeting 3\",\n        \"Greeting 4\",\n        \"Greeting 5\",\n        \"Greeting 6\",\n        \"Greeting 7\",\n        \"Greeting 8\",\n        \"Greeting 9\",\n        0\n    };\n\n    for (int i=0; sGreetings[i]; ++i)\n    {\n        ESM::Dialogue record;\n        record.mId = sGreetings[i];\n        record.mType = ESM::Dialogue::Greeting;\n        record.blank();\n\n        getData().getTopics().add (record);\n    }\n\n    static const char *sPersuasion[] =\n    {\n        \"Intimidate Success\",\n        \"Intimidate Fail\",\n        \"Service Refusal\",\n        \"Admire Success\",\n        \"Taunt Success\",\n        \"Bribe Success\",\n        \"Info Refusal\",\n        \"Admire Fail\",\n        \"Taunt Fail\",\n        \"Bribe Fail\",\n        0\n    };\n\n    for (int i=0; sPersuasion[i]; ++i)\n    {\n        ESM::Dialogue record;\n        record.mId = sPersuasion[i];\n        record.mType = ESM::Dialogue::Persuasion;\n        record.blank();\n\n        getData().getTopics().add (record);\n    }\n\n    for (int i=0; i<ESM::MagicEffect::Length; ++i)\n    {\n        ESM::MagicEffect record;\n\n        record.mIndex = i;\n        record.mId = ESM::MagicEffect::indexToId (i);\n\n        record.blank();\n\n        getData().getMagicEffects().add (record);\n    }\n}\n\nCSMDoc::Document::Document (const Files::ConfigurationManager& configuration,\n    const std::vector< boost::filesystem::path >& files,bool new_,\n    const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,\n    ToUTF8::FromType encoding, const std::vector<std::string>& blacklistedScripts,\n    bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)\n: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, resDir),\n  mTools (*this, encoding),\n  mProjectPath ((configuration.getUserDataPath() / \"projects\") /\n  (savePath.filename().string() + \".project\")),\n  mSavingOperation (*this, mProjectPath, encoding),\n  mSaving (&mSavingOperation),\n  mResDir(resDir), mRunner (mProjectPath),\n  mDirty (false), mIdCompletionManager(mData)\n{\n    if (mContentFiles.empty())\n        throw std::runtime_error (\"Empty content file sequence\");\n\n    if (mNew || !boost::filesystem::exists (mProjectPath))\n    {\n        boost::filesystem::path filtersPath (configuration.getUserDataPath() / \"defaultfilters\");\n\n        boost::filesystem::ofstream destination(mProjectPath, std::ios::out | std::ios::binary);\n        if (!destination.is_open())\n            throw std::runtime_error(\"Can not create project file: \" + mProjectPath.string());\n        destination.exceptions(std::ios::failbit | std::ios::badbit);\n\n        if (!boost::filesystem::exists (filtersPath))\n            filtersPath = mResDir / \"defaultfilters\";\n\n        boost::filesystem::ifstream source(filtersPath, std::ios::in | std::ios::binary);\n        if (!source.is_open())\n            throw std::runtime_error(\"Can not read filters file: \" + filtersPath.string());\n        source.exceptions(std::ios::failbit | std::ios::badbit);\n\n        destination << source.rdbuf();\n    }\n\n    if (mNew)\n    {\n        if (mContentFiles.size()==1)\n            createBase();\n    }\n\n    mBlacklist.add (CSMWorld::UniversalId::Type_Script, blacklistedScripts);\n\n    addOptionalGmsts();\n    addOptionalGlobals();\n    addOptionalMagicEffects();\n\n    connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));\n\n    connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));\n    connect (&mTools, SIGNAL (done (int, bool)), this, SIGNAL (operationDone (int, bool)));\n    connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone2 (int, bool)));\n    connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)),\n            this, SIGNAL (mergeDone (CSMDoc::Document*)));\n\n    connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));\n    connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone2 (int, bool)));\n\n    connect (\n        &mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)),\n        this, SLOT (reportMessage (const CSMDoc::Message&, int)));\n\n    connect (&mRunner, SIGNAL (runStateChanged()), this, SLOT (runStateChanged()));\n}\n\nCSMDoc::Document::~Document()\n{\n}\n\nQUndoStack& CSMDoc::Document::getUndoStack()\n{\n    return mUndoStack;\n}\n\nint CSMDoc::Document::getState() const\n{\n    int state = 0;\n\n    if (!mUndoStack.isClean() || mDirty)\n        state |= State_Modified;\n\n    if (mSaving.isRunning())\n        state |= State_Locked | State_Saving | State_Operation;\n\n    if (mRunner.isRunning())\n        state |= State_Locked | State_Running;\n\n    if (int operations = mTools.getRunningOperations())\n        state |= State_Locked | State_Operation | operations;\n\n    return state;\n}\n\nconst boost::filesystem::path& CSMDoc::Document::getResourceDir() const\n{\n    return mResDir;\n}\n\nconst boost::filesystem::path& CSMDoc::Document::getSavePath() const\n{\n    return mSavePath;\n}\n\nconst boost::filesystem::path& CSMDoc::Document::getProjectPath() const\n{\n    return mProjectPath;\n}\n\nconst std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const\n{\n    return mContentFiles;\n}\n\nbool CSMDoc::Document::isNew() const\n{\n    return mNew;\n}\n\nvoid CSMDoc::Document::save()\n{\n    if (mSaving.isRunning())\n        throw std::logic_error (\n            \"Failed to initiate save, because a save operation is already running.\");\n\n    mSaving.start();\n\n    emit stateChanged (getState(), this);\n}\n\nCSMWorld::UniversalId CSMDoc::Document::verify (const CSMWorld::UniversalId& reportId)\n{\n    CSMWorld::UniversalId id = mTools.runVerifier (reportId);\n    emit stateChanged (getState(), this);\n    return id;\n}\n\n\nCSMWorld::UniversalId CSMDoc::Document::newSearch()\n{\n    return mTools.newSearch();\n}\n\nvoid CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search)\n{\n    mTools.runSearch (searchId, search);\n    emit stateChanged (getState(), this);\n}\n\nvoid CSMDoc::Document::runMerge (std::unique_ptr<CSMDoc::Document> target)\n{\n    mTools.runMerge (std::move(target));\n    emit stateChanged (getState(), this);\n}\n\nvoid CSMDoc::Document::abortOperation (int type)\n{\n    if (type==State_Saving)\n        mSaving.abort();\n    else\n        mTools.abortOperation (type);\n}\n\nvoid CSMDoc::Document::modificationStateChanged (bool clean)\n{\n    emit stateChanged (getState(), this);\n}\n\nvoid CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type)\n{\n    /// \\todo find a better way to get these messages to the user.\n    Log(Debug::Info) << message.mMessage;\n}\n\nvoid CSMDoc::Document::operationDone2 (int type, bool failed)\n{\n    if (type==CSMDoc::State_Saving && !failed)\n        mDirty = false;\n\n    emit stateChanged (getState(), this);\n}\n\nconst CSMWorld::Data& CSMDoc::Document::getData() const\n{\n    return mData;\n}\n\nCSMWorld::Data& CSMDoc::Document::getData()\n{\n    return mData;\n}\n\nCSMTools::ReportModel *CSMDoc::Document::getReport (const CSMWorld::UniversalId& id)\n{\n    return mTools.getReport (id);\n}\n\nbool CSMDoc::Document::isBlacklisted (const CSMWorld::UniversalId& id)\n    const\n{\n    return mBlacklist.isBlacklisted (id);\n}\n\nvoid CSMDoc::Document::startRunning (const std::string& profile,\n    const std::string& startupInstruction)\n{\n    std::vector<std::string> contentFiles;\n\n    for (std::vector<boost::filesystem::path>::const_iterator iter (mContentFiles.begin());\n        iter!=mContentFiles.end(); ++iter)\n        contentFiles.push_back (iter->filename().string());\n\n    mRunner.configure (getData().getDebugProfiles().getRecord (profile).get(), contentFiles,\n        startupInstruction);\n\n    int state = getState();\n\n    if (state & State_Modified)\n    {\n        // need to save first\n        mRunner.start (true);\n\n        new SaveWatcher (&mRunner, &mSaving); // no, that is not a memory leak. Qt is weird.\n\n        if (!(state & State_Saving))\n            save();\n    }\n    else\n        mRunner.start();\n}\n\nvoid CSMDoc::Document::stopRunning()\n{\n    mRunner.stop();\n}\n\nQTextDocument *CSMDoc::Document::getRunLog()\n{\n    return mRunner.getLog();\n}\n\nvoid CSMDoc::Document::runStateChanged()\n{\n    emit stateChanged (getState(), this);\n}\n\nvoid CSMDoc::Document::progress (int current, int max, int type)\n{\n    emit progress (current, max, type, 1, this);\n}\n\nCSMWorld::IdCompletionManager &CSMDoc::Document::getIdCompletionManager()\n{\n    return mIdCompletionManager;\n}\n\nvoid CSMDoc::Document::flagAsDirty()\n{\n    mDirty = true;\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/document.hpp",
    "content": "#ifndef CSM_DOC_DOCUMENT_H\n#define CSM_DOC_DOCUMENT_H\n\n#include <string>\n\n#include <boost/filesystem/path.hpp>\n\n#include <QUndoStack>\n#include <QObject>\n#include <QTimer>\n\n#include <components/files/multidircollection.hpp>\n#include <components/to_utf8/to_utf8.hpp>\n\n#include \"../world/data.hpp\"\n#include \"../world/idcompletionmanager.hpp\"\n\n#include \"../tools/tools.hpp\"\n\n#include \"state.hpp\"\n#include \"saving.hpp\"\n#include \"blacklist.hpp\"\n#include \"runner.hpp\"\n#include \"operationholder.hpp\"\n\nclass QAbstractItemModel;\n\nnamespace Fallback\n{\n    class Map;\n}\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace ESM\n{\n    struct GameSetting;\n    struct Global;\n    struct MagicEffect;\n}\n\nnamespace Files\n{\n    struct ConfigurationManager;\n}\n\nnamespace CSMWorld\n{\n    class ResourcesManager;\n}\n\nnamespace CSMDoc\n{\n    class Document : public QObject\n    {\n            Q_OBJECT\n\n        private:\n\n            boost::filesystem::path mSavePath;\n            std::vector<boost::filesystem::path> mContentFiles;\n            bool mNew;\n            CSMWorld::Data mData;\n            CSMTools::Tools mTools;\n            boost::filesystem::path mProjectPath;\n            Saving mSavingOperation;\n            OperationHolder mSaving;\n            boost::filesystem::path mResDir;\n            Blacklist mBlacklist;\n            Runner mRunner;\n            bool mDirty;\n\n            CSMWorld::IdCompletionManager mIdCompletionManager;\n\n            // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is\n            // using other member variables.  Unfortunately this connection is cut only in the QObject destructor, which is way too late.\n            QUndoStack mUndoStack;\n\n            // not implemented\n            Document (const Document&);\n            Document& operator= (const Document&);\n\n            void createBase();\n\n            void addGmsts();\n\n            void addOptionalGmsts();\n\n            void addOptionalGlobals();\n\n            void addOptionalMagicEffects();\n\n            void addOptionalGmst (const ESM::GameSetting& gmst);\n\n            void addOptionalGlobal (const ESM::Global& global);\n\n            void addOptionalMagicEffect (const ESM::MagicEffect& effect);\n\n        public:\n\n            Document (const Files::ConfigurationManager& configuration,\n                const std::vector< boost::filesystem::path >& files, bool new_,\n                const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,\n                ToUTF8::FromType encoding, const std::vector<std::string>& blacklistedScripts,\n                bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);\n\n            ~Document();\n\n            QUndoStack& getUndoStack();\n\n            int getState() const;\n\n            const boost::filesystem::path& getResourceDir() const;\n\n            const boost::filesystem::path& getSavePath() const;\n\n            const boost::filesystem::path& getProjectPath() const;\n\n            const std::vector<boost::filesystem::path>& getContentFiles() const;\n            ///< \\attention The last element in this collection is the file that is being edited,\n            /// but with its original path instead of the save path.\n\n            bool isNew() const;\n            ///< Is this a newly created content file?\n\n            void save();\n\n            CSMWorld::UniversalId verify (const CSMWorld::UniversalId& reportId = CSMWorld::UniversalId());\n\n            CSMWorld::UniversalId newSearch();\n\n            void runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search);\n\n            void runMerge (std::unique_ptr<CSMDoc::Document> target);\n\n            void abortOperation (int type);\n\n            const CSMWorld::Data& getData() const;\n\n            CSMWorld::Data& getData();\n\n            CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id);\n            ///< The ownership of the returned report is not transferred.\n\n            bool isBlacklisted (const CSMWorld::UniversalId& id) const;\n\n            void startRunning (const std::string& profile,\n                const std::string& startupInstruction = \"\");\n\n            void stopRunning();\n\n            QTextDocument *getRunLog();\n\n            CSMWorld::IdCompletionManager &getIdCompletionManager();\n\n            void flagAsDirty();\n\n        signals:\n\n            void stateChanged (int state, CSMDoc::Document *document);\n\n            void progress (int current, int max, int type, int threads, CSMDoc::Document *document);\n\n            /// \\attention When this signal is emitted, *this hands over the ownership of the\n            /// document. This signal must be handled to avoid a leak.\n            void mergeDone (CSMDoc::Document *document);\n\n            void operationDone (int type, bool failed);\n\n        private slots:\n\n            void modificationStateChanged (bool clean);\n\n            void reportMessage (const CSMDoc::Message& message, int type);\n\n            void operationDone2 (int type, bool failed);\n\n            void runStateChanged();\n\n        public slots:\n\n            void progress (int current, int max, int type);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/documentmanager.cpp",
    "content": "#include \"documentmanager.hpp\"\n\n#include <boost/filesystem.hpp>\n\n#ifndef Q_MOC_RUN\n#include <components/files/configurationmanager.hpp>\n#endif\n\n#include \"document.hpp\"\n\nCSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)\n: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mFsStrict(false)\n{\n    boost::filesystem::path projectPath = configuration.getUserDataPath() / \"projects\";\n\n    if (!boost::filesystem::is_directory (projectPath))\n        boost::filesystem::create_directories (projectPath);\n\n    mLoader.moveToThread (&mLoaderThread);\n    mLoaderThread.start();\n\n    connect (&mLoader, SIGNAL (documentLoaded (Document *)),\n        this, SLOT (documentLoaded (Document *)));\n    connect (&mLoader, SIGNAL (documentNotLoaded (Document *, const std::string&)),\n        this, SLOT (documentNotLoaded (Document *, const std::string&)));\n    connect (this, SIGNAL (loadRequest (CSMDoc::Document *)),\n        &mLoader, SLOT (loadDocument (CSMDoc::Document *)));\n    connect (&mLoader, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)),\n        this, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)));\n    connect (&mLoader, SIGNAL (nextRecord (CSMDoc::Document *, int)),\n        this, SIGNAL (nextRecord (CSMDoc::Document *, int)));\n    connect (this, SIGNAL (cancelLoading (CSMDoc::Document *)),\n        &mLoader, SLOT (abortLoading (CSMDoc::Document *)));\n    connect (&mLoader, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)),\n        this, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)));\n}\n\nCSMDoc::DocumentManager::~DocumentManager()\n{\n    mLoaderThread.quit();\n    mLoader.stop();\n    mLoader.hasThingsToDo().wakeAll();\n    mLoaderThread.wait();\n\n    for (std::vector<Document *>::iterator iter (mDocuments.begin()); iter!=mDocuments.end(); ++iter)\n        delete *iter;\n}\n\nbool CSMDoc::DocumentManager::isEmpty()\n{\n    return mDocuments.empty();\n}\n\nvoid CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,\n    bool new_)\n{\n    Document *document = makeDocument (files, savePath, new_);\n    insertDocument (document);\n}\n\nCSMDoc::Document *CSMDoc::DocumentManager::makeDocument (\n    const std::vector< boost::filesystem::path >& files,\n    const boost::filesystem::path& savePath, bool new_)\n{\n    return new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mBlacklistedScripts, mFsStrict, mDataPaths, mArchives);\n}\n\nvoid CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)\n{\n    mDocuments.push_back (document);\n\n    connect (document, SIGNAL (mergeDone (CSMDoc::Document*)),\n        this, SLOT (insertDocument (CSMDoc::Document*)));\n\n    emit loadRequest (document);\n\n    mLoader.hasThingsToDo().wakeAll();\n}\n\nvoid CSMDoc::DocumentManager::removeDocument (CSMDoc::Document *document)\n{\n    std::vector<Document *>::iterator iter = std::find (mDocuments.begin(), mDocuments.end(), document);\n\n    if (iter==mDocuments.end())\n        throw std::runtime_error (\"removing invalid document\");\n\n    emit documentAboutToBeRemoved (document);\n\n    mDocuments.erase (iter);\n    document->deleteLater();\n\n    if (mDocuments.empty())\n        emit lastDocumentDeleted();\n}\n\nvoid CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir)\n{\n    mResDir = boost::filesystem::system_complete(parResDir);\n}\n\nvoid CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding)\n{\n    mEncoding = encoding;\n}\n\nvoid CSMDoc::DocumentManager::setBlacklistedScripts (const std::vector<std::string>& scriptIds)\n{\n    mBlacklistedScripts = scriptIds;\n}\n\nvoid CSMDoc::DocumentManager::documentLoaded (Document *document)\n{\n    emit documentAdded (document);\n    emit loadingStopped (document, true, \"\");\n}\n\nvoid CSMDoc::DocumentManager::documentNotLoaded (Document *document, const std::string& error)\n{\n    emit loadingStopped (document, false, error);\n\n    if (error.empty()) // do not remove the document yet, if we have an error\n        removeDocument (document);\n}\n\nvoid CSMDoc::DocumentManager::setFileData(bool strict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)\n{\n    mFsStrict = strict;\n    mDataPaths = dataPaths;\n    mArchives = archives;\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/documentmanager.hpp",
    "content": "#ifndef CSM_DOC_DOCUMENTMGR_H\n#define CSM_DOC_DOCUMENTMGR_H\n\n#include <vector>\n#include <string>\n\n#include <boost/filesystem/path.hpp>\n\n#include <QObject>\n#include <QThread>\n\n#include <components/to_utf8/to_utf8.hpp>\n#include <components/fallback/fallback.hpp>\n#include <components/files/multidircollection.hpp>\n\n#include \"loader.hpp\"\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace Files\n{\n    struct ConfigurationManager;\n}\n\nnamespace CSMDoc\n{\n    class Document;\n\n    class DocumentManager : public QObject\n    {\n            Q_OBJECT\n\n            std::vector<Document *> mDocuments;\n            const Files::ConfigurationManager& mConfiguration;\n            QThread mLoaderThread;\n            Loader mLoader;\n            ToUTF8::FromType mEncoding;\n            std::vector<std::string> mBlacklistedScripts;\n\n            boost::filesystem::path mResDir;\n\n            bool mFsStrict;\n            Files::PathContainer mDataPaths;\n            std::vector<std::string> mArchives;\n\n            DocumentManager (const DocumentManager&);\n            DocumentManager& operator= (const DocumentManager&);\n\n        public:\n\n            DocumentManager (const Files::ConfigurationManager& configuration);\n\n            ~DocumentManager();\n\n            void addDocument (const std::vector< boost::filesystem::path >& files,\n                const boost::filesystem::path& savePath, bool new_);\n            ///< \\param new_ Do not load the last content file in \\a files and instead create in an\n            /// appropriate way.\n\n            /// Create a new document. The ownership of the created document is transferred to\n            /// the calling function. The DocumentManager does not manage it. Loading has not\n            /// taken place at the point when the document is returned.\n            ///\n            /// \\param new_ Do not load the last content file in \\a files and instead create in an\n            /// appropriate way.\n            Document *makeDocument (const std::vector< boost::filesystem::path >& files,\n                const boost::filesystem::path& savePath, bool new_);\n\n            void setResourceDir (const boost::filesystem::path& parResDir);\n\n            void setEncoding (ToUTF8::FromType encoding);\n\n            void setBlacklistedScripts (const std::vector<std::string>& scriptIds);\n\n            /// Sets the file data that gets passed to newly created documents.\n            void setFileData(bool strict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);\n\n            bool isEmpty();\n\n        private slots:\n\n            void documentLoaded (Document *document);\n            ///< The ownership of \\a document is not transferred.\n\n            void documentNotLoaded (Document *document, const std::string& error);\n            ///< Document load has been interrupted either because of a call to abortLoading\n            /// or a problem during loading). In the former case error will be an empty string.\n\n        public slots:\n\n            void removeDocument (CSMDoc::Document *document);\n            ///< Emits the lastDocumentDeleted signal, if applicable.\n\n            /// Hand over document to *this. The ownership is transferred. The DocumentManager\n            /// will initiate the load procedure, if necessary\n            void insertDocument (CSMDoc::Document *document);\n\n        signals:\n\n            void documentAdded (CSMDoc::Document *document);\n\n            void documentAboutToBeRemoved (CSMDoc::Document *document);\n\n            void loadRequest (CSMDoc::Document *document);\n\n            void lastDocumentDeleted();\n\n            void loadingStopped (CSMDoc::Document *document, bool completed,\n                const std::string& error);\n\n            void nextStage (CSMDoc::Document *document, const std::string& name,\n                int totalRecords);\n\n            void nextRecord (CSMDoc::Document *document, int records);\n\n            void cancelLoading (CSMDoc::Document *document);\n\n            void loadMessage (CSMDoc::Document *document, const std::string& message);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/loader.cpp",
    "content": "#include \"loader.hpp\"\n\n#include <iostream>\n\n#include \"../tools/reportmodel.hpp\"\n\n#include \"document.hpp\"\n\nCSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLoaded (0), mRecordsLeft (false) {}\n\n\nCSMDoc::Loader::Loader()\n    : mShouldStop(false)\n{\n    mTimer = new QTimer (this);\n\n    connect (mTimer, SIGNAL (timeout()), this, SLOT (load()));\n    mTimer->start();\n}\n\nQWaitCondition& CSMDoc::Loader::hasThingsToDo()\n{\n    return mThingsToDo;\n}\n\nvoid CSMDoc::Loader::stop()\n{\n    mShouldStop = true;\n}\n\nvoid CSMDoc::Loader::load()\n{\n    if (mDocuments.empty())\n    {\n        mMutex.lock();\n        mThingsToDo.wait (&mMutex);\n        mMutex.unlock();\n\n        if (mShouldStop)\n            mTimer->stop();\n\n        return;\n    }\n\n    std::vector<std::pair<Document *, Stage> >::iterator iter = mDocuments.begin();\n\n    Document *document = iter->first;\n\n    int size = static_cast<int> (document->getContentFiles().size());\n    int editedIndex = size-1; // index of the file to be edited/created\n\n    if (document->isNew())\n        --size;\n\n    bool done = false;\n\n    try\n    {\n        if (iter->second.mRecordsLeft)\n        {\n            Messages messages (Message::Severity_Error);\n            const int batchingSize = 50;\n            for (int i=0; i<batchingSize; ++i) // do not flood the system with update signals\n                if (document->getData().continueLoading (messages))\n                {\n                    iter->second.mRecordsLeft = false;\n                    break;\n                }\n                else\n                    ++(iter->second.mRecordsLoaded);\n\n            CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0);\n\n            { // silence a g++ warning\n            for (CSMDoc::Messages::Iterator messageIter (messages.begin());\n                messageIter!=messages.end(); ++messageIter)\n            {\n                document->getReport (log)->add (*messageIter);\n                emit loadMessage (document, messageIter->mMessage);\n            }\n            }\n\n            emit nextRecord (document, iter->second.mRecordsLoaded);\n\n            return;\n        }\n\n        if (iter->second.mFile<size)\n        {\n            boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];\n\n            int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false);\n            iter->second.mRecordsLeft = true;\n            iter->second.mRecordsLoaded = 0;\n\n            emit nextStage (document, path.filename().string(), steps);\n        }\n        else if (iter->second.mFile==size)\n        {\n            int steps = document->getData().startLoading (document->getProjectPath(), false, true);\n            iter->second.mRecordsLeft = true;\n            iter->second.mRecordsLoaded = 0;\n\n            emit nextStage (document, \"Project File\", steps);\n        }\n        else\n        {\n            done = true;\n        }\n\n        ++(iter->second.mFile);\n    }\n    catch (const std::exception& e)\n    {\n        mDocuments.erase (iter);\n        emit documentNotLoaded (document, e.what());\n        return;\n    }\n\n    if (done)\n    {\n        mDocuments.erase (iter);\n        emit documentLoaded (document);\n    }\n}\n\nvoid CSMDoc::Loader::loadDocument (CSMDoc::Document *document)\n{\n    mDocuments.emplace_back (document, Stage());\n}\n\nvoid CSMDoc::Loader::abortLoading (CSMDoc::Document *document)\n{\n    for (std::vector<std::pair<Document *, Stage> >::iterator iter = mDocuments.begin();\n        iter!=mDocuments.end(); ++iter)\n    {\n        if (iter->first==document)\n        {\n            mDocuments.erase (iter);\n            emit documentNotLoaded (document, \"\");\n            break;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/loader.hpp",
    "content": "#ifndef CSM_DOC_LOADER_H\n#define CSM_DOC_LOADER_H\n\n#include <vector>\n\n#include <QObject>\n#include <QMutex>\n#include <QTimer>\n#include <QWaitCondition>\n\nnamespace CSMDoc\n{\n    class Document;\n\n    class Loader : public QObject\n    {\n            Q_OBJECT\n\n            struct Stage\n            {\n                int mFile;\n                int mRecordsLoaded;\n                bool mRecordsLeft;\n\n                Stage();\n            };\n\n            QMutex mMutex;\n            QWaitCondition mThingsToDo;\n            std::vector<std::pair<Document *, Stage> > mDocuments;\n\n            QTimer* mTimer;\n            bool mShouldStop;\n\n        public:\n\n            Loader();\n\n            QWaitCondition& hasThingsToDo();\n\n            void stop();\n\n        private slots:\n\n            void load();\n\n        public slots:\n\n            void loadDocument (CSMDoc::Document *document);\n            ///< The ownership of \\a document is not transferred.\n\n            void abortLoading (CSMDoc::Document *document);\n            ///< Abort loading \\a docuemnt (ignored if \\a document has already finished being\n            /// loaded). Will result in a documentNotLoaded signal, once the Loader has finished\n            /// cleaning up.\n\n        signals:\n\n            void documentLoaded (Document *document);\n            ///< The ownership of \\a document is not transferred.\n\n            void documentNotLoaded (Document *document, const std::string& error);\n            ///< Document load has been interrupted either because of a call to abortLoading\n            /// or a problem during loading). In the former case error will be an empty string.\n\n            void nextStage (CSMDoc::Document *document, const std::string& name,\n                int totalRecords);\n\n            void nextRecord (CSMDoc::Document *document, int records);\n            ///< \\note This signal is only given once per group of records. The group size is\n            /// approximately the total number of records divided by the steps value of the\n            /// previous nextStage signal.\n\n            void loadMessage (CSMDoc::Document *document, const std::string& message);\n            ///< Non-critical load error or warning\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/messages.cpp",
    "content": "#include \"messages.hpp\"\n\nCSMDoc::Message::Message() : mSeverity(Severity_Default){}\n\nCSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& message,\n    const std::string& hint, Severity severity)\n: mId (id), mMessage (message), mHint (hint), mSeverity (severity)\n{}\n\nstd::string CSMDoc::Message::toString (Severity severity)\n{\n    switch (severity)\n    {\n        case CSMDoc::Message::Severity_Info: return \"Information\";\n        case CSMDoc::Message::Severity_Warning: return \"Warning\";\n        case CSMDoc::Message::Severity_Error: return \"Error\";\n        case CSMDoc::Message::Severity_SeriousError: return \"Serious Error\";\n        case CSMDoc::Message::Severity_Default: break;\n    }\n\n    return \"\";\n}\n\n\nCSMDoc::Messages::Messages (Message::Severity default_)\n: mDefault (default_)\n{}\n\nvoid CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& message,\n    const std::string& hint, Message::Severity severity)\n{\n    if (severity==Message::Severity_Default)\n        severity = mDefault;\n\n    mMessages.push_back (Message (id, message, hint, severity));\n}\n\nCSMDoc::Messages::Iterator CSMDoc::Messages::begin() const\n{\n    return mMessages.begin();\n}\n\nCSMDoc::Messages::Iterator CSMDoc::Messages::end() const\n{\n    return mMessages.end();\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/messages.hpp",
    "content": "#ifndef CSM_DOC_MESSAGES_H\n#define CSM_DOC_MESSAGES_H\n\n#include <string>\n#include <vector>\n\n#include <QMetaType>\n\n#include \"../world/universalid.hpp\"\n\nnamespace CSMDoc\n{\n    struct Message\n    {\n        enum Severity\n        {\n            Severity_Info = 0,         // no problem\n            Severity_Warning = 1,      // a potential problem, but we are probably fine\n            Severity_Error = 2,        // an error; we are not fine\n            Severity_SeriousError = 3, // an error so bad we can't even be sure if we are\n                                       // reporting it correctly\n            Severity_Default = 4\n        };\n\n        CSMWorld::UniversalId mId;\n        std::string mMessage;\n        std::string mHint;\n        Severity mSeverity;\n\n        Message();\n\n        Message (const CSMWorld::UniversalId& id, const std::string& message,\n            const std::string& hint, Severity severity);\n\n        static std::string toString (Severity severity);\n    };\n\n    class Messages\n    {\n        public:\n\n            typedef std::vector<Message> Collection;\n\n            typedef Collection::const_iterator Iterator;\n\n        private:\n\n            Collection mMessages;\n            Message::Severity mDefault;\n\n        public:\n\n            Messages (Message::Severity default_);\n\n            void add (const CSMWorld::UniversalId& id, const std::string& message,\n                const std::string& hint = \"\",\n                Message::Severity severity = Message::Severity_Default);\n\n            Iterator begin() const;\n\n            Iterator end() const;\n    };\n}\n\nQ_DECLARE_METATYPE (CSMDoc::Message)\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/operation.cpp",
    "content": "#include \"operation.hpp\"\n\n#include <string>\n#include <vector>\n\n#include <QTimer>\n\n#include \"../world/universalid.hpp\"\n\n#include \"stage.hpp\"\n\nvoid CSMDoc::Operation::prepareStages()\n{\n    mCurrentStage = mStages.begin();\n    mCurrentStep = 0;\n    mCurrentStepTotal = 0;\n    mTotalSteps = 0;\n    mError = false;\n\n    for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)\n    {\n        iter->second = iter->first->setup();\n        mTotalSteps += iter->second;\n    }\n}\n\nCSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways)\n: mType (type), mStages(std::vector<std::pair<Stage *, int> >()), mCurrentStage(mStages.begin()),\n  mCurrentStep(0), mCurrentStepTotal(0), mTotalSteps(0), mOrdered (ordered),\n  mFinalAlways (finalAlways), mError(false), mConnected (false), mPrepared (false),\n  mDefaultSeverity (Message::Severity_Error)\n{\n    mTimer = new QTimer (this);\n}\n\nCSMDoc::Operation::~Operation()\n{\n    for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)\n        delete iter->first;\n}\n\nvoid CSMDoc::Operation::run()\n{\n    mTimer->stop();\n\n    if (!mConnected)\n    {\n        connect (mTimer, SIGNAL (timeout()), this, SLOT (executeStage()));\n        mConnected = true;\n    }\n\n    mPrepared = false;\n\n    mTimer->start (0);\n}\n\nvoid CSMDoc::Operation::appendStage (Stage *stage)\n{\n    mStages.emplace_back (stage, 0);\n}\n\nvoid CSMDoc::Operation::setDefaultSeverity (Message::Severity severity)\n{\n    mDefaultSeverity = severity;\n}\n\nbool CSMDoc::Operation::hasError() const\n{\n    return mError;\n}\n\nvoid CSMDoc::Operation::abort()\n{\n    if (!mTimer->isActive())\n        return;\n\n    mError = true;\n\n    if (mFinalAlways)\n    {\n        if (mStages.begin()!=mStages.end() && mCurrentStage!=--mStages.end())\n        {\n            mCurrentStep = 0;\n            mCurrentStage = --mStages.end();\n        }\n    }\n    else\n        mCurrentStage = mStages.end();\n}\n\nvoid CSMDoc::Operation::executeStage()\n{\n    if (!mPrepared)\n    {\n        prepareStages();\n        mPrepared = true;\n    }\n\n    Messages messages (mDefaultSeverity);\n\n    while (mCurrentStage!=mStages.end())\n    {\n        if (mCurrentStep>=mCurrentStage->second)\n        {\n            mCurrentStep = 0;\n            ++mCurrentStage;\n        }\n        else\n        {\n            try\n            {\n                mCurrentStage->first->perform (mCurrentStep++, messages);\n            }\n            catch (const std::exception& e)\n            {\n                emit reportMessage (Message (CSMWorld::UniversalId(), e.what(), \"\", Message::Severity_SeriousError), mType);\n                abort();\n            }\n\n            ++mCurrentStepTotal;\n            break;\n        }\n    }\n\n    emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType);\n\n    for (Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter)\n        emit reportMessage (*iter, mType);\n\n    if (mCurrentStage==mStages.end())\n        operationDone();\n}\n\nvoid CSMDoc::Operation::operationDone()\n{\n    mTimer->stop();\n    emit done (mType, mError);\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/operation.hpp",
    "content": "#ifndef CSM_DOC_OPERATION_H\n#define CSM_DOC_OPERATION_H\n\n#include <vector>\n#include <map>\n\n#include <QObject>\n#include <QTimer>\n#include <QStringList>\n\n#include \"messages.hpp\"\n\nnamespace CSMWorld\n{\n    class UniversalId;\n}\n\nnamespace CSMDoc\n{\n    class Stage;\n\n    class Operation : public QObject\n    {\n            Q_OBJECT\n\n            int mType;\n            std::vector<std::pair<Stage *, int> > mStages; // stage, number of steps\n            std::vector<std::pair<Stage *, int> >::iterator mCurrentStage;\n            int mCurrentStep;\n            int mCurrentStepTotal;\n            int mTotalSteps;\n            int mOrdered;\n            bool mFinalAlways;\n            bool mError;\n            bool mConnected;\n            QTimer *mTimer;\n            bool mPrepared;\n            Message::Severity mDefaultSeverity;\n\n            void prepareStages();\n\n        public:\n\n            Operation (int type, bool ordered, bool finalAlways = false);\n            ///< \\param ordered Stages must be executed in the given order.\n            /// \\param finalAlways Execute last stage even if an error occurred during earlier stages.\n\n            virtual ~Operation();\n\n            void appendStage (Stage *stage);\n            ///< The ownership of \\a stage is transferred to *this.\n            ///\n            /// \\attention Do no call this function while this Operation is running.\n\n            /// \\attention Do no call this function while this Operation is running.\n            void setDefaultSeverity (Message::Severity severity);\n\n            bool hasError() const;\n\n        signals:\n\n            void progress (int current, int max, int type);\n\n            void reportMessage (const CSMDoc::Message& message, int type);\n\n            void done (int type, bool failed);\n\n        public slots:\n\n            void abort();\n\n            void run();\n\n        private slots:\n\n            void executeStage();\n\n        protected slots:\n\n            virtual void operationDone();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/operationholder.cpp",
    "content": "#include \"operationholder.hpp\"\n\n#include \"operation.hpp\"\n\nCSMDoc::OperationHolder::OperationHolder (Operation *operation)\n    : mOperation(nullptr)\n    , mRunning (false)\n{\n    if (operation)\n        setOperation (operation);\n}\n\nvoid CSMDoc::OperationHolder::setOperation (Operation *operation)\n{\n    mOperation = operation;\n    mOperation->moveToThread (&mThread);\n\n    connect (\n        mOperation, SIGNAL (progress (int, int, int)),\n        this, SIGNAL (progress (int, int, int)));\n\n    connect (\n        mOperation, SIGNAL (reportMessage (const CSMDoc::Message&, int)),\n        this, SIGNAL (reportMessage (const CSMDoc::Message&, int)));\n\n    connect (\n        mOperation, SIGNAL (done (int, bool)),\n        this, SLOT (doneSlot (int, bool)));\n\n    connect (this, SIGNAL (abortSignal()), mOperation, SLOT (abort()));\n\n    connect (&mThread, SIGNAL (started()), mOperation, SLOT (run()));\n}\n\nbool CSMDoc::OperationHolder::isRunning() const\n{\n    return mRunning;\n}\n\nvoid CSMDoc::OperationHolder::start()\n{\n    mRunning = true;\n    mThread.start();\n}\n\nvoid CSMDoc::OperationHolder::abort()\n{\n    mRunning = false;\n    emit abortSignal();\n}\n\nvoid CSMDoc::OperationHolder::abortAndWait()\n{\n    if (mRunning)\n    {\n        mThread.quit();\n        mThread.wait();\n    }\n}\n\nvoid CSMDoc::OperationHolder::doneSlot (int type, bool failed)\n{\n    mRunning = false;\n    mThread.quit();\n    emit done (type, failed);\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/operationholder.hpp",
    "content": "#ifndef CSM_DOC_OPERATIONHOLDER_H\n#define CSM_DOC_OPERATIONHOLDER_H\n\n#include <QObject>\n#include <QThread>\n\n#include \"messages.hpp\"\n\nnamespace CSMWorld\n{\n    class UniversalId;\n}\n\nnamespace CSMDoc\n{\n    class Operation;\n\n    class OperationHolder : public QObject\n    {\n            Q_OBJECT\n            \n            QThread mThread;\n            Operation *mOperation;\n            bool mRunning;\n\n        public:\n\n            OperationHolder (Operation *operation = nullptr);\n\n            void setOperation (Operation *operation);\n\n            bool isRunning() const;\n\n            void start();\n\n            void abort();\n\n            // Abort and wait until thread has finished.\n            void abortAndWait();\n\n        private slots:\n\n            void doneSlot (int type, bool failed);\n            \n        signals:\n\n            void progress (int current, int max, int type);\n\n            void reportMessage (const CSMDoc::Message& message, int type);\n\n            void done (int type, bool failed);\n\n            void abortSignal();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/runner.cpp",
    "content": "#include \"runner.hpp\"\n\n#include <QApplication>\n#include <QDir>\n#include <QTemporaryFile>\n#include <QTextStream>\n\n#include \"operationholder.hpp\"\n\nCSMDoc::Runner::Runner (const boost::filesystem::path& projectPath)\n: mRunning (false), mStartup (nullptr), mProjectPath (projectPath)\n{\n    connect (&mProcess, SIGNAL (finished (int, QProcess::ExitStatus)),\n        this, SLOT (finished (int, QProcess::ExitStatus)));\n\n    connect (&mProcess, SIGNAL (readyReadStandardOutput()),\n        this, SLOT (readyReadStandardOutput()));\n\n    mProcess.setProcessChannelMode (QProcess::MergedChannels);\n\n    mProfile.blank();\n}\n\nCSMDoc::Runner::~Runner()\n{\n    if (mRunning)\n    {\n        disconnect (&mProcess, nullptr, this, nullptr);\n        mProcess.kill();\n        mProcess.waitForFinished();\n    }\n}\n\nvoid CSMDoc::Runner::start (bool delayed)\n{\n    if (mStartup)\n    {\n        delete mStartup;\n        mStartup = nullptr;\n    }\n\n    if (!delayed)\n    {\n        mLog.clear();\n\n        QString path = \"openmw\";\n#ifdef Q_OS_WIN\n        path.append(QString(\".exe\"));\n#elif defined(Q_OS_MAC)\n        QDir dir(QCoreApplication::applicationDirPath());\n        dir.cdUp();\n        dir.cdUp();\n        dir.cdUp();\n        path = dir.absoluteFilePath(path.prepend(\"OpenMW.app/Contents/MacOS/\"));\n#else\n        path.prepend(QString(\"./\"));\n#endif\n\n        mStartup = new QTemporaryFile (this);\n        mStartup->open();\n\n        {\n            QTextStream stream (mStartup);\n\n            if (!mStartupInstruction.empty())\n                stream << QString::fromUtf8 (mStartupInstruction.c_str()) << '\\n';\n\n            stream << QString::fromUtf8 (mProfile.mScriptText.c_str());\n        }\n\n        mStartup->close();\n\n        QStringList arguments;\n        arguments << \"--skip-menu\";\n\n        if (mProfile.mFlags & ESM::DebugProfile::Flag_BypassNewGame)\n            arguments << \"--new-game=0\";\n        else\n            arguments << \"--new-game=1\";\n\n        arguments << (\"--script-run=\"+mStartup->fileName());;\n\n        arguments <<\n            QString::fromUtf8 ((\"--data=\\\"\"+mProjectPath.parent_path().string()+\"\\\"\").c_str());\n\n        arguments << \"--replace=content\";\n\n        for (std::vector<std::string>::const_iterator iter (mContentFiles.begin());\n            iter!=mContentFiles.end(); ++iter)\n        {\n            arguments << QString::fromUtf8 ((\"--content=\"+*iter).c_str());\n        }\n\n        arguments\n            << QString::fromUtf8 ((\"--content=\"+mProjectPath.filename().string()).c_str());\n\n        mProcess.start (path, arguments);\n    }\n\n    mRunning = true;\n    emit runStateChanged();\n}\n\nvoid CSMDoc::Runner::stop()\n{\n    delete mStartup;\n    mStartup = nullptr;\n\n    if (mProcess.state()==QProcess::NotRunning)\n    {\n        mRunning = false;\n        emit runStateChanged();\n    }\n    else\n        mProcess.kill();\n}\n\nbool CSMDoc::Runner::isRunning() const\n{\n    return mRunning;\n}\n\nvoid CSMDoc::Runner::configure (const ESM::DebugProfile& profile,\n    const std::vector<std::string>& contentFiles, const std::string& startupInstruction)\n{\n    mProfile = profile;\n    mContentFiles = contentFiles;\n    mStartupInstruction = startupInstruction;\n}\n\nvoid CSMDoc::Runner::finished (int exitCode, QProcess::ExitStatus exitStatus)\n{\n    mRunning = false;\n    emit runStateChanged();\n}\n\nQTextDocument *CSMDoc::Runner::getLog()\n{\n    return &mLog;\n}\n\nvoid CSMDoc::Runner::readyReadStandardOutput()\n{\n    mLog.setPlainText (\n        mLog.toPlainText() + QString::fromUtf8 (mProcess.readAllStandardOutput()));\n}\n\n\nCSMDoc::SaveWatcher::SaveWatcher (Runner *runner, OperationHolder *operation)\n: QObject (runner), mRunner (runner)\n{\n    connect (operation, SIGNAL (done (int, bool)), this, SLOT (saveDone (int, bool)));\n}\n\nvoid CSMDoc::SaveWatcher::saveDone (int type, bool failed)\n{\n    if (failed)\n        mRunner->stop();\n    else\n        mRunner->start();\n\n    deleteLater();\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/runner.hpp",
    "content": "#ifndef CSM_DOC_RUNNER_H\n#define CSM_DOC_RUNNER_H\n\n#include <vector>\n#include <string>\n\n#include <boost/filesystem/path.hpp>\n\n#include <QObject>\n#include <QProcess>\n#include <QTextDocument>\n\n#include <components/esm/debugprofile.hpp>\n\nclass QTemporaryFile;\n\nnamespace CSMDoc\n{\n    class OperationHolder;\n    \n    class Runner : public QObject\n    {\n            Q_OBJECT\n\n            QProcess mProcess;\n            bool mRunning;\n            ESM::DebugProfile mProfile;\n            std::vector<std::string> mContentFiles;\n            std::string mStartupInstruction;\n            QTemporaryFile *mStartup;\n            QTextDocument mLog;\n            boost::filesystem::path mProjectPath;\n\n        public:\n\n            Runner (const boost::filesystem::path& projectPath);\n\n            ~Runner();\n\n            /// \\param delayed Flag as running but do not start the OpenMW process yet (the\n            /// process must be started by another call of start with delayed==false)\n            void start (bool delayed = false);\n\n            void stop();\n\n            /// \\note Running state is entered when the start function is called. This\n            /// is not necessarily identical to the moment the child process is started.\n            bool isRunning() const;\n\n            void configure (const ESM::DebugProfile& profile,\n                const std::vector<std::string>& contentFiles,\n                const std::string& startupInstruction);\n\n            QTextDocument *getLog();\n\n        signals:\n\n            void runStateChanged();\n\n        private slots:\n\n            void finished (int exitCode, QProcess::ExitStatus exitStatus);\n\n            void readyReadStandardOutput();\n    };\n\n    class Operation;\n\n    /// \\brief Watch for end of save operation and restart or stop runner\n    class SaveWatcher : public QObject\n    {\n            Q_OBJECT\n\n            Runner *mRunner;\n\n        public:\n\n            /// *this attaches itself to runner\n            SaveWatcher (Runner *runner, OperationHolder *operation);\n\n        private slots:\n\n            void saveDone (int type, bool failed);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/saving.cpp",
    "content": "#include \"saving.hpp\"\n\n#include \"../world/data.hpp\"\n#include \"../world/idcollection.hpp\"\n\n#include \"state.hpp\"\n#include \"savingstages.hpp\"\n#include \"document.hpp\"\n\nCSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath,\n    ToUTF8::FromType encoding)\n: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath, encoding)\n{\n    // save project file\n    appendStage (new OpenSaveStage (mDocument, mState, true));\n\n    appendStage (new WriteHeaderStage (mDocument, mState, true));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Filter> > (\n        mDocument.getData().getFilters(), mState, CSMWorld::Scope_Project));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::DebugProfile> > (\n        mDocument.getData().getDebugProfiles(), mState, CSMWorld::Scope_Project));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Script> > (\n        mDocument.getData().getScripts(), mState, CSMWorld::Scope_Project));\n\n    appendStage (new CloseSaveStage (mState));\n\n    // save content file\n    appendStage (new OpenSaveStage (mDocument, mState, false));\n\n    appendStage (new WriteHeaderStage (mDocument, mState, false));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Global> >\n        (mDocument.getData().getGlobals(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::GameSetting> >\n        (mDocument.getData().getGmsts(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Skill> >\n        (mDocument.getData().getSkills(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Class> >\n        (mDocument.getData().getClasses(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Faction> >\n        (mDocument.getData().getFactions(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Race> >\n        (mDocument.getData().getRaces(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Sound> >\n        (mDocument.getData().getSounds(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Script> >\n        (mDocument.getData().getScripts(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Region> >\n        (mDocument.getData().getRegions(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::BirthSign> >\n        (mDocument.getData().getBirthsigns(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Spell> >\n        (mDocument.getData().getSpells(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Enchantment> >\n        (mDocument.getData().getEnchantments(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::BodyPart> >\n        (mDocument.getData().getBodyParts(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::SoundGenerator> >\n        (mDocument.getData().getSoundGens(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::MagicEffect> >\n        (mDocument.getData().getMagicEffects(), mState));\n\n    appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::StartScript> >\n        (mDocument.getData().getStartScripts(), mState));\n\n    appendStage (new WriteRefIdCollectionStage (mDocument, mState));\n\n    appendStage (new CollectionReferencesStage (mDocument, mState));\n\n    appendStage (new WriteCellCollectionStage (mDocument, mState));\n\n    // Dialogue can reference objects and cells so must be written after these records for vanilla-compatible files\n\n    appendStage (new WriteDialogueCollectionStage (mDocument, mState, false));\n\n    appendStage (new WriteDialogueCollectionStage (mDocument, mState, true));\n\n    appendStage (new WritePathgridCollectionStage (mDocument, mState));\n\n    appendStage (new WriteLandTextureCollectionStage (mDocument, mState));\n\n    // references Land Textures\n    appendStage (new WriteLandCollectionStage (mDocument, mState));\n\n    // close file and clean up\n    appendStage (new CloseSaveStage (mState));\n\n    appendStage (new FinalSavingStage (mDocument, mState));\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/saving.hpp",
    "content": "#ifndef CSM_DOC_SAVING_H\n#define CSM_DOC_SAVING_H\n\n#include <boost/filesystem/path.hpp>\n\n#include <components/to_utf8/to_utf8.hpp>\n\n#include \"operation.hpp\"\n#include \"savingstate.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n\n    class Saving : public Operation\n    {\n            Q_OBJECT\n\n            Document& mDocument;\n            SavingState mState;\n\n        public:\n\n            Saving (Document& document, const boost::filesystem::path& projectPath,\n                ToUTF8::FromType encoding);\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/savingstages.cpp",
    "content": "#include \"savingstages.hpp\"\n\n#include <boost/filesystem.hpp>\n\n#include <QUndoStack>\n\n#include <components/esm/loaddial.hpp>\n\n#include \"../world/infocollection.hpp\"\n#include \"../world/cellcoordinates.hpp\"\n\n#include \"document.hpp\"\n\nCSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state, bool projectFile)\n: mDocument (document), mState (state), mProjectFile (projectFile)\n{}\n\nint CSMDoc::OpenSaveStage::setup()\n{\n    return 1;\n}\n\nvoid CSMDoc::OpenSaveStage::perform (int stage, Messages& messages)\n{\n    mState.start (mDocument, mProjectFile);\n\n    mState.getStream().open (\n        mProjectFile ? mState.getPath() : mState.getTmpPath(),\n        std::ios::binary);\n\n    if (!mState.getStream().is_open())\n        throw std::runtime_error (\"failed to open stream for saving\");\n}\n\n\nCSMDoc::WriteHeaderStage::WriteHeaderStage (Document& document, SavingState& state, bool simple)\n: mDocument (document), mState (state), mSimple (simple)\n{}\n\nint CSMDoc::WriteHeaderStage::setup()\n{\n    return 1;\n}\n\nvoid CSMDoc::WriteHeaderStage::perform (int stage, Messages& messages)\n{\n    mState.getWriter().setVersion();\n\n    mState.getWriter().clearMaster();\n\n    if (mSimple)\n    {\n        mState.getWriter().setAuthor (\"\");\n        mState.getWriter().setDescription (\"\");\n        mState.getWriter().setRecordCount (0);\n        mState.getWriter().setFormat (ESM::Header::CurrentFormat);\n    }\n    else\n    {\n        mDocument.getData().getMetaData().save (mState.getWriter());\n        mState.getWriter().setRecordCount (\n            mDocument.getData().count (CSMWorld::RecordBase::State_Modified) +\n            mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) +\n            mDocument.getData().count (CSMWorld::RecordBase::State_Deleted));\n\n        /// \\todo refine dependency list (at least remove redundant dependencies)\n        std::vector<boost::filesystem::path> dependencies = mDocument.getContentFiles();\n        std::vector<boost::filesystem::path>::const_iterator end (--dependencies.end());\n\n        for (std::vector<boost::filesystem::path>::const_iterator iter (dependencies.begin());\n            iter!=end; ++iter)\n        {\n            std::string name = iter->filename().string();\n            uint64_t size = boost::filesystem::file_size (*iter);\n\n            mState.getWriter().addMaster (name, size);\n        }\n    }\n\n    mState.getWriter().save (mState.getStream());\n}\n\n\nCSMDoc::WriteDialogueCollectionStage::WriteDialogueCollectionStage (Document& document,\n    SavingState& state, bool journal)\n: mState (state),\n  mTopics (journal ? document.getData().getJournals() : document.getData().getTopics()),\n  mInfos (journal ? document.getData().getJournalInfos() : document.getData().getTopicInfos())\n{}\n\nint CSMDoc::WriteDialogueCollectionStage::setup()\n{\n    return mTopics.getSize();\n}\n\nvoid CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& messages)\n{\n    ESM::ESMWriter& writer = mState.getWriter();\n    const CSMWorld::Record<ESM::Dialogue>& topic = mTopics.getRecord (stage);\n\n    if (topic.mState == CSMWorld::RecordBase::State_Deleted)\n    {\n        // if the topic is deleted, we do not need to bother with INFO records.\n        ESM::Dialogue dialogue = topic.get();\n        writer.startRecord(dialogue.sRecordId);\n        dialogue.save(writer, true);\n        writer.endRecord(dialogue.sRecordId);\n        return;\n    }\n\n    // Test, if we need to save anything associated info records.\n    bool infoModified = false;\n    CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId);\n\n    for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)\n    {\n        if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)\n        {\n            infoModified = true;\n            break;\n        }\n    }\n\n    if (topic.isModified() || infoModified)\n    {\n        if (infoModified && topic.mState != CSMWorld::RecordBase::State_Modified\n                         && topic.mState != CSMWorld::RecordBase::State_ModifiedOnly)\n        {\n            mState.getWriter().startRecord (topic.mBase.sRecordId);\n            topic.mBase.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted);\n            mState.getWriter().endRecord (topic.mBase.sRecordId);\n        }\n        else\n        {\n            mState.getWriter().startRecord (topic.mModified.sRecordId);\n            topic.mModified.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted);\n            mState.getWriter().endRecord (topic.mModified.sRecordId);\n        }\n\n        // write modified selected info records\n        for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)\n        {\n            if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)\n            {\n                ESM::DialInfo info = iter->get();\n                info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);\n\n                info.mPrev = \"\";\n                if (iter!=range.first)\n                {\n                    CSMWorld::InfoCollection::RecordConstIterator prev = iter;\n                    --prev;\n\n                    info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1);\n                }\n\n                CSMWorld::InfoCollection::RecordConstIterator next = iter;\n                ++next;\n\n                info.mNext = \"\";\n                if (next!=range.second)\n                {\n                    info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1);\n                }\n\n                writer.startRecord (info.sRecordId);\n                info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted);\n                writer.endRecord (info.sRecordId);\n            }\n        }\n    }\n}\n\n\nCSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state)\n: mDocument (document), mState (state)\n{}\n\nint CSMDoc::WriteRefIdCollectionStage::setup()\n{\n    return mDocument.getData().getReferenceables().getSize();\n}\n\nvoid CSMDoc::WriteRefIdCollectionStage::perform (int stage, Messages& messages)\n{\n    mDocument.getData().getReferenceables().save (stage, mState.getWriter());\n}\n\n\nCSMDoc::CollectionReferencesStage::CollectionReferencesStage (Document& document,\n    SavingState& state)\n: mDocument (document), mState (state)\n{}\n\nint CSMDoc::CollectionReferencesStage::setup()\n{\n    mState.getSubRecords().clear();\n\n    int size = mDocument.getData().getReferences().getSize();\n\n    int steps = size/100;\n    if (size%100) ++steps;\n\n    return steps;\n}\n\nvoid CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)\n{\n    int size = mDocument.getData().getReferences().getSize();\n\n    for (int i=stage*100; i<stage*100+100 && i<size; ++i)\n    {\n        const CSMWorld::Record<CSMWorld::CellRef>& record =\n            mDocument.getData().getReferences().getRecord (i);\n\n        if (record.isModified() || record.mState == CSMWorld::RecordBase::State_Deleted)\n        {\n            std::string cellId = record.get().mOriginalCell.empty() ?\n                record.get().mCell : record.get().mOriginalCell;\n\n            std::deque<int>& indices =\n                mState.getSubRecords()[Misc::StringUtils::lowerCase (cellId)];\n\n            // collect moved references at the end of the container\n            bool interior = cellId.substr (0, 1)!=\"#\";\n            std::ostringstream stream;\n            if (!interior)\n            {\n                // recalculate the ref's cell location\n                std::pair<int, int> index = record.get().getCellIndex();\n                stream << \"#\" << index.first << \" \" << index.second;\n            }\n\n            // An empty mOriginalCell is meant to indicate that it is the same as\n            // the current cell.  It is possible that a moved ref is moved again.\n            if ((record.get().mOriginalCell.empty() ?\n                    record.get().mCell : record.get().mOriginalCell) != stream.str() && !interior && record.mState!=CSMWorld::RecordBase::State_ModifiedOnly && !record.get().mNew)\n                indices.push_back (i);\n            else\n                indices.push_front (i);\n        }\n    }\n}\n\n\nCSMDoc::WriteCellCollectionStage::WriteCellCollectionStage (Document& document,\n    SavingState& state)\n: mDocument (document), mState (state)\n{}\n\nint CSMDoc::WriteCellCollectionStage::setup()\n{\n    return mDocument.getData().getCells().getSize();\n}\n\nvoid CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)\n{\n    ESM::ESMWriter& writer = mState.getWriter();\n    const CSMWorld::Record<CSMWorld::Cell>& cell = mDocument.getData().getCells().getRecord (stage);\n\n    std::map<std::string, std::deque<int> >::const_iterator references =\n        mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));\n\n    if (cell.isModified() ||\n        cell.mState == CSMWorld::RecordBase::State_Deleted ||\n        references!=mState.getSubRecords().end())\n    {\n        CSMWorld::Cell cellRecord = cell.get();\n        bool interior = cellRecord.mId.substr (0, 1)!=\"#\";\n\n        // count new references and adjust RefNumCount accordingsly\n        unsigned int newRefNum = cellRecord.mRefNumCounter;\n\n        if (references!=mState.getSubRecords().end())\n        {\n            for (std::deque<int>::const_iterator iter (references->second.begin());\n                iter!=references->second.end(); ++iter)\n            {\n                const CSMWorld::Record<CSMWorld::CellRef>& ref =\n                    mDocument.getData().getReferences().getRecord (*iter);\n\n                CSMWorld::CellRef refRecord = ref.get();\n\n                if (refRecord.mNew ||\n                    (!interior && ref.mState==CSMWorld::RecordBase::State_ModifiedOnly &&\n                    /// \\todo consider worldspace\n                    CSMWorld::CellCoordinates (refRecord.getCellIndex()).getId(\"\") != refRecord.mCell))\n                    ++cellRecord.mRefNumCounter;\n\n                if (refRecord.mRefNum.mIndex >= newRefNum)\n                    newRefNum = refRecord.mRefNum.mIndex + 1;\n\n            }\n        }\n\n        // write cell data\n        writer.startRecord (cellRecord.sRecordId);\n\n        if (interior)\n            cellRecord.mData.mFlags |= ESM::Cell::Interior;\n        else\n        {\n            cellRecord.mData.mFlags &= ~ESM::Cell::Interior;\n\n            std::istringstream stream (cellRecord.mId.c_str());\n            char ignore;\n            stream >> ignore >> cellRecord.mData.mX >> cellRecord.mData.mY;\n        }\n\n        cellRecord.save (writer, cell.mState == CSMWorld::RecordBase::State_Deleted);\n\n        // write references\n        if (references!=mState.getSubRecords().end())\n        {\n            for (std::deque<int>::const_iterator iter (references->second.begin());\n                iter!=references->second.end(); ++iter)\n            {\n                const CSMWorld::Record<CSMWorld::CellRef>& ref =\n                    mDocument.getData().getReferences().getRecord (*iter);\n\n                if (ref.isModified() || ref.mState == CSMWorld::RecordBase::State_Deleted)\n                {\n                    CSMWorld::CellRef refRecord = ref.get();\n\n                    // Check for uninitialized content file\n                    if (!refRecord.mRefNum.hasContentFile())\n                        refRecord.mRefNum.mContentFile = 0;\n\n                    // recalculate the ref's cell location\n                    std::ostringstream stream;\n                    if (!interior)\n                    {\n                        std::pair<int, int> index = refRecord.getCellIndex();\n                        stream << \"#\" << index.first << \" \" << index.second;\n                    }\n\n                    if (refRecord.mNew || refRecord.mRefNum.mIndex == 0 ||\n                        (!interior && ref.mState==CSMWorld::RecordBase::State_ModifiedOnly &&\n                        refRecord.mCell!=stream.str()))\n                    {\n                        refRecord.mRefNum.mIndex = newRefNum++;\n                    }\n                    else if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell)\n                            != stream.str() && !interior)\n                    {\n                        // An empty mOriginalCell is meant to indicate that it is the same as\n                        // the current cell.  It is possible that a moved ref is moved again.\n\n                        ESM::MovedCellRef moved;\n                        moved.mRefNum = refRecord.mRefNum;\n\n                        // Need to fill mTarget with the ref's new position.\n                        std::istringstream istream (stream.str().c_str());\n\n                        char ignore;\n                        istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1];\n\n                        refRecord.mRefNum.save (writer, false, \"MVRF\");\n                        writer.writeHNT (\"CNDT\", moved.mTarget);\n                    }\n\n                    refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted);\n                }\n            }\n        }\n\n        writer.endRecord (cellRecord.sRecordId);\n    }\n}\n\n\nCSMDoc::WritePathgridCollectionStage::WritePathgridCollectionStage (Document& document,\n    SavingState& state)\n: mDocument (document), mState (state)\n{}\n\nint CSMDoc::WritePathgridCollectionStage::setup()\n{\n    return mDocument.getData().getPathgrids().getSize();\n}\n\nvoid CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages)\n{\n    ESM::ESMWriter& writer = mState.getWriter();\n    const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =\n        mDocument.getData().getPathgrids().getRecord (stage);\n\n    if (pathgrid.isModified() || pathgrid.mState == CSMWorld::RecordBase::State_Deleted)\n    {\n        CSMWorld::Pathgrid record = pathgrid.get();\n\n        if (record.mId.substr (0, 1)==\"#\")\n        {\n            std::istringstream stream (record.mId.c_str());\n            char ignore;\n            stream >> ignore >> record.mData.mX >> record.mData.mY;\n        }\n        else\n            record.mCell = record.mId;\n\n        writer.startRecord (record.sRecordId);\n        record.save (writer, pathgrid.mState == CSMWorld::RecordBase::State_Deleted);\n        writer.endRecord (record.sRecordId);\n    }\n}\n\n\nCSMDoc::WriteLandCollectionStage::WriteLandCollectionStage (Document& document,\n    SavingState& state)\n: mDocument (document), mState (state)\n{}\n\nint CSMDoc::WriteLandCollectionStage::setup()\n{\n    return mDocument.getData().getLand().getSize();\n}\n\nvoid CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)\n{\n    ESM::ESMWriter& writer = mState.getWriter();\n    const CSMWorld::Record<CSMWorld::Land>& land =\n        mDocument.getData().getLand().getRecord (stage);\n\n    if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted)\n    {\n        CSMWorld::Land record = land.get();\n        writer.startRecord (record.sRecordId);\n        record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted);\n        writer.endRecord (record.sRecordId);\n    }\n}\n\n\nCSMDoc::WriteLandTextureCollectionStage::WriteLandTextureCollectionStage (Document& document,\n    SavingState& state)\n: mDocument (document), mState (state)\n{}\n\nint CSMDoc::WriteLandTextureCollectionStage::setup()\n{\n    return mDocument.getData().getLandTextures().getSize();\n}\n\nvoid CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages)\n{\n    ESM::ESMWriter& writer = mState.getWriter();\n    const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =\n        mDocument.getData().getLandTextures().getRecord (stage);\n\n    if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted)\n    {\n        CSMWorld::LandTexture record = landTexture.get();\n        writer.startRecord (record.sRecordId);\n        record.save (writer, landTexture.mState == CSMWorld::RecordBase::State_Deleted);\n        writer.endRecord (record.sRecordId);\n    }\n}\n\n\nCSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state)\n: mState (state)\n{}\n\nint CSMDoc::CloseSaveStage::setup()\n{\n    return 1;\n}\n\nvoid CSMDoc::CloseSaveStage::perform (int stage, Messages& messages)\n{\n    mState.getStream().close();\n\n    if (!mState.getStream())\n        throw std::runtime_error (\"saving failed\");\n}\n\n\nCSMDoc::FinalSavingStage::FinalSavingStage (Document& document, SavingState& state)\n: mDocument (document), mState (state)\n{}\n\nint CSMDoc::FinalSavingStage::setup()\n{\n    return 1;\n}\n\nvoid CSMDoc::FinalSavingStage::perform (int stage, Messages& messages)\n{\n    if (mState.hasError())\n    {\n        mState.getWriter().close();\n        mState.getStream().close();\n\n        if (boost::filesystem::exists (mState.getTmpPath()))\n            boost::filesystem::remove (mState.getTmpPath());\n    }\n    else if (!mState.isProjectFile())\n    {\n        if (boost::filesystem::exists (mState.getPath()))\n            boost::filesystem::remove (mState.getPath());\n\n        boost::filesystem::rename (mState.getTmpPath(), mState.getPath());\n\n        mDocument.getUndoStack().setClean();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/savingstages.hpp",
    "content": "#ifndef CSM_DOC_SAVINGSTAGES_H\n#define CSM_DOC_SAVINGSTAGES_H\n\n#include \"stage.hpp\"\n\n#include \"../world/record.hpp\"\n#include \"../world/idcollection.hpp\"\n#include \"../world/scope.hpp\"\n\n#include <components/esm/defs.hpp>\n\n#include \"savingstate.hpp\"\n\nnamespace ESM\n{\n    struct Dialogue;\n}\n\nnamespace CSMWorld\n{\n    class InfoCollection;\n}\n\nnamespace CSMDoc\n{\n    class Document;\n    class SavingState;\n\n    class OpenSaveStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n            bool mProjectFile;\n\n        public:\n\n            OpenSaveStage (Document& document, SavingState& state, bool projectFile);\n            ///< \\param projectFile Saving the project file instead of the content file.\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    class WriteHeaderStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n            bool mSimple;\n\n        public:\n\n            WriteHeaderStage (Document& document, SavingState& state, bool simple);\n            ///< \\param simple Simplified header (used for project files).\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n\n    template<class CollectionT>\n    class WriteCollectionStage : public Stage\n    {\n            const CollectionT& mCollection;\n            SavingState& mState;\n            CSMWorld::Scope mScope;\n\n        public:\n\n            WriteCollectionStage (const CollectionT& collection, SavingState& state,\n                CSMWorld::Scope scope = CSMWorld::Scope_Content);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    template<class CollectionT>\n    WriteCollectionStage<CollectionT>::WriteCollectionStage (const CollectionT& collection,\n        SavingState& state, CSMWorld::Scope scope)\n    : mCollection (collection), mState (state), mScope (scope)\n    {}\n\n    template<class CollectionT>\n    int WriteCollectionStage<CollectionT>::setup()\n    {\n        return mCollection.getSize();\n    }\n\n    template<class CollectionT>\n    void WriteCollectionStage<CollectionT>::perform (int stage, Messages& messages)\n    {\n        if (CSMWorld::getScopeFromId (mCollection.getRecord (stage).get().mId)!=mScope)\n            return;\n\n        ESM::ESMWriter& writer = mState.getWriter();\n        CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState;\n        typename CollectionT::ESXRecord record = mCollection.getRecord (stage).get();\n\n        if (state == CSMWorld::RecordBase::State_Modified ||\n            state == CSMWorld::RecordBase::State_ModifiedOnly ||\n            state == CSMWorld::RecordBase::State_Deleted)\n        {\n            writer.startRecord (record.sRecordId);\n            record.save (writer, state == CSMWorld::RecordBase::State_Deleted);\n            writer.endRecord (record.sRecordId);\n        }\n    }\n\n\n    class WriteDialogueCollectionStage : public Stage\n    {\n            SavingState& mState;\n            const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;\n            CSMWorld::InfoCollection& mInfos;\n\n        public:\n\n            WriteDialogueCollectionStage (Document& document, SavingState& state, bool journal);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n\n    class WriteRefIdCollectionStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n\n        public:\n\n            WriteRefIdCollectionStage (Document& document, SavingState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n\n    class CollectionReferencesStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n\n        public:\n\n            CollectionReferencesStage (Document& document, SavingState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    class WriteCellCollectionStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n\n        public:\n\n            WriteCellCollectionStage (Document& document, SavingState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n\n    class WritePathgridCollectionStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n\n        public:\n\n            WritePathgridCollectionStage (Document& document, SavingState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n\n    class WriteLandCollectionStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n\n        public:\n\n            WriteLandCollectionStage (Document& document, SavingState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n\n    class WriteLandTextureCollectionStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n\n        public:\n\n            WriteLandTextureCollectionStage (Document& document, SavingState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    class CloseSaveStage : public Stage\n    {\n            SavingState& mState;\n\n        public:\n\n            CloseSaveStage (SavingState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    class FinalSavingStage : public Stage\n    {\n            Document& mDocument;\n            SavingState& mState;\n\n        public:\n\n            FinalSavingStage (Document& document, SavingState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/savingstate.cpp",
    "content": "#include \"savingstate.hpp\"\n\n#include <boost/filesystem/fstream.hpp>\n\n#include \"operation.hpp\"\n#include \"document.hpp\"\n\nCSMDoc::SavingState::SavingState (Operation& operation, const boost::filesystem::path& projectPath,\n    ToUTF8::FromType encoding)\n: mOperation (operation), mEncoder (encoding),  mProjectPath (projectPath), mProjectFile (false)\n{\n    mWriter.setEncoder (&mEncoder);\n}\n\nbool CSMDoc::SavingState::hasError() const\n{\n    return mOperation.hasError();\n}\n\nvoid CSMDoc::SavingState::start (Document& document, bool project)\n{\n    mProjectFile = project;\n\n    if (mStream.is_open())\n        mStream.close();\n\n    mStream.clear();\n\n    mSubRecords.clear();\n\n    if (project)\n        mPath = mProjectPath;\n    else\n        mPath = document.getSavePath();\n\n    boost::filesystem::path file (mPath.filename().string() + \".tmp\");\n\n    mTmpPath = mPath.parent_path();\n\n    mTmpPath /= file;\n}\n\nconst boost::filesystem::path& CSMDoc::SavingState::getPath() const\n{\n    return mPath;\n}\n\nconst boost::filesystem::path& CSMDoc::SavingState::getTmpPath() const\n{\n    return mTmpPath;\n}\n\nboost::filesystem::ofstream& CSMDoc::SavingState::getStream()\n{\n    return mStream;\n}\n\nESM::ESMWriter& CSMDoc::SavingState::getWriter()\n{\n    return mWriter;\n}\n\nbool CSMDoc::SavingState::isProjectFile() const\n{\n    return mProjectFile;\n}\n\nstd::map<std::string, std::deque<int> >& CSMDoc::SavingState::getSubRecords()\n{\n    return mSubRecords;\n}\n"
  },
  {
    "path": "apps/opencs/model/doc/savingstate.hpp",
    "content": "#ifndef CSM_DOC_SAVINGSTATE_H\n#define CSM_DOC_SAVINGSTATE_H\n\n#include <fstream>\n#include <map>\n#include <deque>\n\n#include <boost/filesystem/path.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <components/esm/esmwriter.hpp>\n\n#include <components/to_utf8/to_utf8.hpp>\n\nnamespace CSMDoc\n{\n    class Operation;\n    class Document;\n\n    class SavingState\n    {\n            Operation& mOperation;\n            boost::filesystem::path mPath;\n            boost::filesystem::path mTmpPath;\n            ToUTF8::Utf8Encoder mEncoder;\n            boost::filesystem::ofstream mStream;\n            ESM::ESMWriter mWriter;\n            boost::filesystem::path mProjectPath;\n            bool mProjectFile;\n            std::map<std::string, std::deque<int> > mSubRecords; // record ID, list of subrecords\n\n        public:\n\n            SavingState (Operation& operation, const boost::filesystem::path& projectPath,\n                ToUTF8::FromType encoding);\n\n            bool hasError() const;\n\n            void start (Document& document, bool project);\n            ///< \\param project Save project file instead of content file.\n\n            const boost::filesystem::path& getPath() const;\n\n            const boost::filesystem::path& getTmpPath() const;\n\n            boost::filesystem::ofstream& getStream();\n\n            ESM::ESMWriter& getWriter();\n\n            bool isProjectFile() const;\n            ///< Currently saving project file? (instead of content file)\n\n            std::map<std::string, std::deque<int> >& getSubRecords();\n    };\n\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/stage.cpp",
    "content": "#include \"stage.hpp\"\n\nCSMDoc::Stage::~Stage() {}\n"
  },
  {
    "path": "apps/opencs/model/doc/stage.hpp",
    "content": "#ifndef CSM_DOC_STAGE_H\n#define CSM_DOC_STAGE_H\n\n#include <vector>\n#include <string>\n\n#include \"../world/universalid.hpp\"\n\n#include \"messages.hpp\"\n\nclass QString;\n\nnamespace CSMDoc\n{\n    class Stage\n    {\n        public:\n\n            virtual ~Stage();\n\n            virtual int setup() = 0;\n            ///< \\return number of steps\n\n            virtual void perform (int stage, Messages& messages) = 0;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/doc/state.hpp",
    "content": "#ifndef CSM_DOC_STATE_H\n#define CSM_DOC_STATE_H\n\nnamespace CSMDoc\n{\n    enum State\n    {\n        State_Modified = 1,\n        State_Locked = 2,\n        State_Operation = 4,\n        State_Running = 8,\n\n        State_Saving = 16,\n        State_Verifying = 32,\n        State_Merging = 64,\n        State_Searching = 128,\n        State_Loading = 256   // pseudo-state; can not be encountered in a loaded document\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/andnode.cpp",
    "content": "#include \"andnode.hpp\"\n\n#include <sstream>\n\nCSMFilter::AndNode::AndNode (const std::vector<std::shared_ptr<Node> >& nodes)\n: NAryNode (nodes, \"and\")\n{}\n\nbool CSMFilter::AndNode::test (const CSMWorld::IdTableBase& table, int row,\n    const std::map<int, int>& columns) const\n{\n    int size = getSize();\n\n    for (int i=0; i<size; ++i)\n        if (!(*this)[i].test (table, row, columns))\n            return false;\n\n    return true;\n}\n"
  },
  {
    "path": "apps/opencs/model/filter/andnode.hpp",
    "content": "#ifndef CSM_FILTER_ANDNODE_H\n#define CSM_FILTER_ANDNODE_H\n\n#include \"narynode.hpp\"\n\nnamespace CSMFilter\n{\n    class AndNode : public NAryNode\n    {\n        public:\n\n            AndNode (const std::vector<std::shared_ptr<Node> >& nodes);\n\n            bool test (const CSMWorld::IdTableBase& table, int row,\n                const std::map<int, int>& columns) const override;\n            ///< \\return Can the specified table row pass through to filter?\n            /// \\param columns column ID to column index mapping\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/booleannode.cpp",
    "content": "#include \"booleannode.hpp\"\n\nCSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {}\n\nbool CSMFilter::BooleanNode::test (const CSMWorld::IdTableBase& table, int row,\n    const std::map<int, int>& columns) const\n{\n    return mTrue;\n}\n\nstd::string CSMFilter::BooleanNode::toString (bool numericColumns) const\n{\n    return mTrue ? \"true\" : \"false\";\n}\n"
  },
  {
    "path": "apps/opencs/model/filter/booleannode.hpp",
    "content": "#ifndef CSM_FILTER_BOOLEANNODE_H\n#define CSM_FILTER_BOOLEANNODE_H\n\n#include \"leafnode.hpp\"\n\nnamespace CSMFilter\n{\n    class BooleanNode : public LeafNode\n    {\n            bool mTrue;\n\n        public:\n\n            BooleanNode (bool true_);\n\n            bool test (const CSMWorld::IdTableBase& table, int row,\n                const std::map<int, int>& columns) const override;\n            ///< \\return Can the specified table row pass through to filter?\n            /// \\param columns column ID to column index mapping\n\n            std::string toString (bool numericColumns) const override;\n            ///< Return a string that represents this node.\n            ///\n            /// \\param numericColumns Use numeric IDs instead of string to represent columns.\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/leafnode.cpp",
    "content": "#include \"leafnode.hpp\"\n\nstd::vector<int> CSMFilter::LeafNode::getReferencedColumns() const\n{\n    return std::vector<int>();\n}\n\n"
  },
  {
    "path": "apps/opencs/model/filter/leafnode.hpp",
    "content": "#ifndef CSM_FILTER_LEAFNODE_H\n#define CSM_FILTER_LEAFNODE_H\n\n#include <memory>\n\n#include \"node.hpp\"\n\nnamespace CSMFilter\n{\n    class LeafNode : public Node\n    {\n        public:\n\n            std::vector<int> getReferencedColumns() const override;\n            ///< Return a list of the IDs of the columns referenced by this node. The column mapping\n            /// passed into test as columns must contain all columns listed here.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/narynode.cpp",
    "content": "#include \"narynode.hpp\"\n\n#include <sstream>\n\nCSMFilter::NAryNode::NAryNode (const std::vector<std::shared_ptr<Node> >& nodes,\n    const std::string& name)\n: mNodes (nodes), mName (name)\n{}\n\nint CSMFilter::NAryNode::getSize() const\n{\n    return static_cast<int>(mNodes.size());\n}\n\nconst CSMFilter::Node& CSMFilter::NAryNode::operator[] (int index) const\n{\n    return *mNodes.at (index);\n}\n\nstd::vector<int> CSMFilter::NAryNode::getReferencedColumns() const\n{\n    std::vector<int> columns;\n\n    for (std::vector<std::shared_ptr<Node> >::const_iterator iter (mNodes.begin());\n         iter!=mNodes.end(); ++iter)\n    {\n        std::vector<int> columns2 = (*iter)->getReferencedColumns();\n\n        columns.insert (columns.end(), columns2.begin(), columns2.end());\n    }\n\n    return columns;\n}\n\nstd::string CSMFilter::NAryNode::toString (bool numericColumns) const\n{\n    std::ostringstream stream;\n\n    stream << mName << \" (\";\n\n    bool first = true;\n    int size = getSize();\n\n    for (int i=0; i<size; ++i)\n    {\n        if (first)\n            first = false;\n        else\n            stream << \", \";\n\n        stream << (*this)[i].toString (numericColumns);\n    }\n\n    stream << \")\";\n\n    return stream.str();\n}\n\n\n"
  },
  {
    "path": "apps/opencs/model/filter/narynode.hpp",
    "content": "#ifndef CSM_FILTER_NARYNODE_H\n#define CSM_FILTER_NARYNODE_H\n\n#include <vector>\n#include <string>\n\n#include \"node.hpp\"\n\nnamespace CSMFilter\n{\n    class NAryNode : public Node\n    {\n            std::vector<std::shared_ptr<Node> > mNodes;\n            std::string mName;\n\n        public:\n\n            NAryNode (const std::vector<std::shared_ptr<Node> >& nodes, const std::string& name);\n\n            int getSize() const;\n\n            const Node& operator[] (int index) const;\n\n            std::vector<int> getReferencedColumns() const override;\n            ///< Return a list of the IDs of the columns referenced by this node. The column mapping\n            /// passed into test as columns must contain all columns listed here.\n\n            std::string toString (bool numericColumns) const override;\n            ///< Return a string that represents this node.\n            ///\n            /// \\param numericColumns Use numeric IDs instead of string to represent columns.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/node.cpp",
    "content": "#include \"node.hpp\"\n\nCSMFilter::Node::Node() {}\n\nCSMFilter::Node::~Node() {}\n"
  },
  {
    "path": "apps/opencs/model/filter/node.hpp",
    "content": "#ifndef CSM_FILTER_NODE_H\n#define CSM_FILTER_NODE_H\n\n#include <string>\n#include <map>\n#include <memory>\n#include <vector>\n\n#include <QMetaType>\n\nnamespace CSMWorld\n{\n    class IdTableBase;\n}\n\nnamespace CSMFilter\n{\n    /// \\brief Root class for the filter node hierarchy\n    ///\n    /// \\note When the function documentation for this class mentions \"this node\", this should be\n    /// interpreted as \"the node and all its children\".\n    class Node\n    {\n            // not implemented\n            Node (const Node&);\n            Node& operator= (const Node&);\n\n        public:\n\n            Node();\n\n            virtual ~Node();\n\n            virtual bool test (const CSMWorld::IdTableBase& table, int row,\n                const std::map<int, int>& columns) const = 0;\n            ///< \\return Can the specified table row pass through to filter?\n            /// \\param columns column ID to column index mapping\n\n            virtual std::vector<int> getReferencedColumns() const = 0;\n            ///< Return a list of the IDs of the columns referenced by this node. The column mapping\n            /// passed into test as columns must contain all columns listed here.\n\n            virtual std::string toString (bool numericColumns) const = 0;\n            ///< Return a string that represents this node.\n            ///\n            /// \\param numericColumns Use numeric IDs instead of string to represent columns.\n    };\n}\n\nQ_DECLARE_METATYPE (std::shared_ptr<CSMFilter::Node>)\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/notnode.cpp",
    "content": "#include \"notnode.hpp\"\n\nCSMFilter::NotNode::NotNode (std::shared_ptr<Node> child) : UnaryNode (child, \"not\") {}\n\nbool CSMFilter::NotNode::test (const CSMWorld::IdTableBase& table, int row,\n    const std::map<int, int>& columns) const\n{\n    return !getChild().test (table, row, columns);\n}\n"
  },
  {
    "path": "apps/opencs/model/filter/notnode.hpp",
    "content": "#ifndef CSM_FILTER_NOTNODE_H\n#define CSM_FILTER_NOTNODE_H\n\n#include \"unarynode.hpp\"\n\nnamespace CSMFilter\n{\n    class NotNode : public UnaryNode\n    {\n        public:\n\n            NotNode (std::shared_ptr<Node> child);\n\n            bool test (const CSMWorld::IdTableBase& table, int row,\n                const std::map<int, int>& columns) const override;\n            ///< \\return Can the specified table row pass through to filter?\n            /// \\param columns column ID to column index mapping\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/ornode.cpp",
    "content": "#include \"ornode.hpp\"\n\n#include <sstream>\n\nCSMFilter::OrNode::OrNode (const std::vector<std::shared_ptr<Node> >& nodes)\n: NAryNode (nodes, \"or\")\n{}\n\nbool CSMFilter::OrNode::test (const CSMWorld::IdTableBase& table, int row,\n    const std::map<int, int>& columns) const\n{\n    int size = getSize();\n\n    for (int i=0; i<size; ++i)\n        if ((*this)[i].test (table, row, columns))\n            return true;\n\n    return false;\n}\n"
  },
  {
    "path": "apps/opencs/model/filter/ornode.hpp",
    "content": "#ifndef CSM_FILTER_ORNODE_H\n#define CSM_FILTER_ORNODE_H\n\n#include \"narynode.hpp\"\n\nnamespace CSMFilter\n{\n    class OrNode : public NAryNode\n    {\n        public:\n\n            OrNode (const std::vector<std::shared_ptr<Node> >& nodes);\n\n            bool test (const CSMWorld::IdTableBase& table, int row,\n                const std::map<int, int>& columns) const override;\n            ///< \\return Can the specified table row pass through to filter?\n            /// \\param columns column ID to column index mapping\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/parser.cpp",
    "content": "#include \"parser.hpp\"\n\n#include <cctype>\n#include <stdexcept>\n#include <sstream>\n\n#include <components/misc/stringops.hpp>\n\n#include \"../world/columns.hpp\"\n#include \"../world/data.hpp\"\n#include \"../world/idcollection.hpp\"\n\n#include \"booleannode.hpp\"\n#include \"ornode.hpp\"\n#include \"andnode.hpp\"\n#include \"notnode.hpp\"\n#include \"textnode.hpp\"\n#include \"valuenode.hpp\"\n\nnamespace CSMFilter\n{\n    struct Token\n    {\n        enum Type\n        {\n            Type_EOS,\n            Type_None,\n            Type_String,\n            Type_Number,\n            Type_Open,\n            Type_Close,\n            Type_OpenSquare,\n            Type_CloseSquare,\n            Type_Comma,\n            Type_OneShot,\n            Type_Keyword_True, ///< \\attention Keyword enums must be arranged continuously.\n            Type_Keyword_False,\n            Type_Keyword_And,\n            Type_Keyword_Or,\n            Type_Keyword_Not,\n            Type_Keyword_Text,\n            Type_Keyword_Value\n        };\n\n        Type mType;\n        std::string mString;\n        double mNumber;\n\n        Token (Type type = Type_None);\n\n        Token (Type type, const std::string& string);\n        ///< Non-string type that can also be interpreted as a string.\n\n        Token (const std::string& string);\n\n        Token (double number);\n\n        operator bool() const;\n\n        bool isString() const;\n    };\n\n    Token::Token (Type type) : mType (type), mNumber(0.0) {}\n\n    Token::Token (Type type, const std::string& string) : mType (type), mString (string), mNumber(0.0) {}\n\n    Token::Token (const std::string& string) : mType (Type_String), mString (string), mNumber(0.0) {}\n\n    Token::Token (double number) : mType (Type_Number), mNumber (number) {}\n\n    bool Token::isString() const\n    {\n        return mType==Type_String || mType>=Type_Keyword_True;\n    }\n\n    Token::operator bool() const\n    {\n        return mType!=Type_None;\n    }\n\n    bool operator== (const Token& left, const Token& right)\n    {\n        if (left.mType!=right.mType)\n            return false;\n\n        switch (left.mType)\n        {\n            case Token::Type_String: return left.mString==right.mString;\n            case Token::Type_Number: return left.mNumber==right.mNumber;\n\n            default: return true;\n        }\n    }\n}\n\nCSMFilter::Token CSMFilter::Parser::getStringToken()\n{\n    std::string string;\n\n    int size = static_cast<int> (mInput.size());\n\n    for (; mIndex<size; ++mIndex)\n    {\n        char c = mInput[mIndex];\n\n        if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='\"' ||\n            (!string.empty() && string[0]=='\"'))\n            string += c;\n        else\n            break;\n\n        if (c=='\"' && string.size()>1)\n        {\n            ++mIndex;\n            break;\n        }\n    };\n\n    if (!string.empty())\n    {\n        if (string[0]=='\"' && (string[string.size()-1]!='\"' || string.size()<2) )\n        {\n            error();\n            return Token (Token::Type_None);\n        }\n\n        if (string[0]!='\"' && string[string.size()-1]=='\"')\n        {\n            error();\n            return Token (Token::Type_None);\n        }\n\n        if (string[0]=='\"')\n            return string.substr (1, string.size()-2);\n    }\n\n    return checkKeywords (string);\n}\n\nCSMFilter::Token CSMFilter::Parser::getNumberToken()\n{\n    std::string string;\n\n    int size = static_cast<int> (mInput.size());\n\n    bool hasDecimalPoint = false;\n    bool hasDigit = false;\n\n    for (; mIndex<size; ++mIndex)\n    {\n        char c = mInput[mIndex];\n\n        if (std::isdigit (c))\n        {\n            string += c;\n            hasDigit = true;\n        }\n        else if (c=='.' && !hasDecimalPoint)\n        {\n            string += c;\n            hasDecimalPoint = true;\n        }\n        else if (string.empty() && c=='-')\n            string += c;\n        else\n            break;\n    }\n\n    if (!hasDigit)\n    {\n        error();\n        return Token (Token::Type_None);\n    }\n\n    float value;\n    std::istringstream stream (string.c_str());\n    stream >> value;\n\n    return value;\n}\n\nCSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token)\n{\n    static const char *sKeywords[] =\n    {\n        \"true\", \"false\",\n        \"and\", \"or\", \"not\",\n        \"string\", \"value\",\n        0\n    };\n\n    std::string string = Misc::StringUtils::lowerCase (token.mString);\n\n    for (int i=0; sKeywords[i]; ++i)\n        if (sKeywords[i]==string || (string.size()==1 && sKeywords[i][0]==string[0]))\n            return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True), token.mString);\n\n    return token;\n}\n\nCSMFilter::Token CSMFilter::Parser::getNextToken()\n{\n    int size = static_cast<int> (mInput.size());\n\n    char c = 0;\n\n    for (; mIndex<size; ++mIndex)\n    {\n        c = mInput[mIndex];\n\n        if (c!=' ')\n            break;\n    }\n\n    if (mIndex>=size)\n        return Token (Token::Type_EOS);\n\n    switch (c)\n    {\n        case '(': ++mIndex; return Token (Token::Type_Open);\n        case ')': ++mIndex; return Token (Token::Type_Close);\n        case '[': ++mIndex; return Token (Token::Type_OpenSquare);\n        case ']': ++mIndex; return Token (Token::Type_CloseSquare);\n        case ',': ++mIndex; return Token (Token::Type_Comma);\n        case '!': ++mIndex; return Token (Token::Type_OneShot);\n    }\n\n    if (c=='\"' || c=='_' || std::isalpha (c) || c==':')\n        return getStringToken();\n\n    if (c=='-' || c=='.' || std::isdigit (c))\n        return getNumberToken();\n\n    error();\n    return Token (Token::Type_None);\n}\n\nstd::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseImp (bool allowEmpty, bool ignoreOneShot)\n{\n    if (Token token = getNextToken())\n    {\n        if (token==Token (Token::Type_OneShot))\n            token = getNextToken();\n\n        if (token)\n            switch (token.mType)\n            {\n                case Token::Type_Keyword_True:\n\n                    return std::shared_ptr<CSMFilter::Node> (new BooleanNode (true));\n\n                case Token::Type_Keyword_False:\n\n                    return std::shared_ptr<CSMFilter::Node> (new BooleanNode (false));\n\n                case Token::Type_Keyword_And:\n                case Token::Type_Keyword_Or:\n\n                    return parseNAry (token);\n\n                case Token::Type_Keyword_Not:\n                {\n                    std::shared_ptr<CSMFilter::Node> node = parseImp();\n\n                    if (mError)\n                        return std::shared_ptr<Node>();\n\n                    return std::shared_ptr<CSMFilter::Node> (new NotNode (node));\n                }\n\n                case Token::Type_Keyword_Text:\n\n                    return parseText();\n\n                case Token::Type_Keyword_Value:\n\n                    return parseValue();\n\n                case Token::Type_EOS:\n\n                    if (!allowEmpty)\n                        error();\n\n                    return std::shared_ptr<Node>();\n\n                default:\n\n                    error();\n            }\n    }\n\n    return std::shared_ptr<Node>();\n}\n\nstd::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseNAry (const Token& keyword)\n{\n    std::vector<std::shared_ptr<Node> > nodes;\n\n    Token token = getNextToken();\n\n    if (token.mType!=Token::Type_Open)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    for (;;)\n    {\n        std::shared_ptr<Node> node = parseImp();\n\n        if (mError)\n            return std::shared_ptr<Node>();\n\n        nodes.push_back (node);\n\n        token = getNextToken();\n\n        if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma))\n        {\n            error();\n            return std::shared_ptr<Node>();\n        }\n\n        if (token.mType==Token::Type_Close)\n            break;\n    }\n\n    switch (keyword.mType)\n    {\n        case Token::Type_Keyword_And: return std::shared_ptr<CSMFilter::Node> (new AndNode (nodes));\n        case Token::Type_Keyword_Or: return std::shared_ptr<CSMFilter::Node> (new OrNode (nodes));\n        default: error(); return std::shared_ptr<Node>();\n    }\n}\n\nstd::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseText()\n{\n    Token token = getNextToken();\n\n    if (token.mType!=Token::Type_Open)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    token = getNextToken();\n\n    if (!token)\n        return std::shared_ptr<Node>();\n\n    // parse column ID\n    int columnId = -1;\n\n    if (token.mType==Token::Type_Number)\n    {\n        if (static_cast<int> (token.mNumber)==token.mNumber)\n            columnId = static_cast<int> (token.mNumber);\n    }\n    else if (token.isString())\n    {\n        columnId = CSMWorld::Columns::getId (token.mString);\n    }\n\n    if (columnId<0)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    token = getNextToken();\n\n    if (token.mType!=Token::Type_Comma)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    // parse text pattern\n    token = getNextToken();\n\n    if (!token.isString())\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    std::string text = token.mString;\n\n    token = getNextToken();\n\n    if (token.mType!=Token::Type_Close)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    return std::shared_ptr<Node> (new TextNode (columnId, text));\n}\n\nstd::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue()\n{\n    Token token = getNextToken();\n\n    if (token.mType!=Token::Type_Open)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    token = getNextToken();\n\n    if (!token)\n        return std::shared_ptr<Node>();\n\n    // parse column ID\n    int columnId = -1;\n\n    if (token.mType==Token::Type_Number)\n    {\n        if (static_cast<int> (token.mNumber)==token.mNumber)\n            columnId = static_cast<int> (token.mNumber);\n    }\n    else if (token.isString())\n    {\n        columnId = CSMWorld::Columns::getId (token.mString);\n    }\n\n    if (columnId<0)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    token = getNextToken();\n\n    if (token.mType!=Token::Type_Comma)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    // parse value\n    double lower = 0;\n    double upper = 0;\n    ValueNode::Type lowerType = ValueNode::Type_Open;\n    ValueNode::Type upperType = ValueNode::Type_Open;\n\n    token = getNextToken();\n\n    if (token.mType==Token::Type_Number)\n    {\n        // single value\n        lower = upper = token.mNumber;\n        lowerType = upperType = ValueNode::Type_Closed;\n    }\n    else\n    {\n        // interval\n        if (token.mType==Token::Type_OpenSquare)\n            lowerType = ValueNode::Type_Closed;\n        else if (token.mType!=Token::Type_CloseSquare && token.mType!=Token::Type_Open)\n        {\n            error();\n            return std::shared_ptr<Node>();\n        }\n\n        token = getNextToken();\n\n        if (token.mType==Token::Type_Number)\n        {\n            lower = token.mNumber;\n\n            token = getNextToken();\n\n            if (token.mType!=Token::Type_Comma)\n            {\n                error();\n                return std::shared_ptr<Node>();\n            }\n        }\n        else if (token.mType==Token::Type_Comma)\n        {\n            lowerType = ValueNode::Type_Infinite;\n        }\n        else\n        {\n            error();\n            return std::shared_ptr<Node>();\n        }\n\n        token = getNextToken();\n\n        if (token.mType==Token::Type_Number)\n        {\n            upper = token.mNumber;\n\n            token = getNextToken();\n        }\n        else\n            upperType = ValueNode::Type_Infinite;\n\n        if (token.mType==Token::Type_CloseSquare)\n        {\n            if (upperType!=ValueNode::Type_Infinite)\n                upperType = ValueNode::Type_Closed;\n        }\n        else if (token.mType!=Token::Type_OpenSquare && token.mType!=Token::Type_Close)\n        {\n            error();\n            return std::shared_ptr<Node>();\n        }\n    }\n\n    token = getNextToken();\n\n    if (token.mType!=Token::Type_Close)\n    {\n        error();\n        return std::shared_ptr<Node>();\n    }\n\n    return std::shared_ptr<Node> (new ValueNode (columnId, lowerType, upperType, lower, upper));\n}\n\nvoid CSMFilter::Parser::error()\n{\n    mError = true;\n}\n\nCSMFilter::Parser::Parser (const CSMWorld::Data& data)\n: mIndex (0), mError (false), mData (data) {}\n\nbool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined)\n{\n    // reset\n    mFilter.reset();\n    mError = false;\n    mInput = filter;\n    mIndex = 0;\n\n    Token token;\n\n    if (allowPredefined)\n        token = getNextToken();\n\n    if (allowPredefined && token==Token (Token::Type_EOS))\n    {\n        mFilter.reset();\n        return true;\n    }\n    else if (!allowPredefined || token==Token (Token::Type_OneShot))\n    {\n        std::shared_ptr<Node> node = parseImp (true, token!=Token (Token::Type_OneShot));\n\n        if (mError)\n            return false;\n\n        if (getNextToken()!=Token (Token::Type_EOS))\n        {\n            error();\n            return false;\n        }\n\n        if (node)\n            mFilter = node;\n        else\n        {\n            // Empty filter string equals to filter \"true\".\n            mFilter.reset (new BooleanNode (true));\n        }\n\n        return true;\n    }\n    // We do not use isString() here, because there could be a pre-defined filter with an ID that is\n    // equal a filter keyword.\n    else if (token.mType==Token::Type_String)\n    {\n        if (getNextToken()!=Token (Token::Type_EOS))\n        {\n            error();\n            return false;\n        }\n\n        int index = mData.getFilters().searchId (token.mString);\n\n        if (index==-1)\n        {\n            error();\n            return false;\n        }\n\n        const CSMWorld::Record<ESM::Filter>& record = mData.getFilters().getRecord (index);\n\n        if (record.isDeleted())\n        {\n            error();\n            return false;\n        }\n\n        return parse (record.get().mFilter, false);\n    }\n    else\n    {\n        error();\n        return false;\n    }\n}\n\nstd::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const\n{\n    if (mError)\n        throw std::logic_error (\"No filter available\");\n\n    return mFilter;\n}\n"
  },
  {
    "path": "apps/opencs/model/filter/parser.hpp",
    "content": "#ifndef CSM_FILTER_PARSER_H\n#define CSM_FILTER_PARSER_H\n\n#include \"node.hpp\"\n\nnamespace CSMWorld\n{\n    class Data;\n}\n\nnamespace CSMFilter\n{\n    struct Token;\n\n    class Parser\n    {\n            std::shared_ptr<Node> mFilter;\n            std::string mInput;\n            int mIndex;\n            bool mError;\n            const CSMWorld::Data& mData;\n\n            Token getStringToken();\n\n            Token getNumberToken();\n\n            Token getNextToken();\n\n            Token checkKeywords (const Token& token);\n            ///< Turn string token into keyword token, if possible.\n\n            std::shared_ptr<Node> parseImp (bool allowEmpty = false, bool ignoreOneShot = false);\n            ///< Will return a null-pointer, if there is nothing more to parse.\n\n            std::shared_ptr<Node> parseNAry (const Token& keyword);\n\n            std::shared_ptr<Node> parseText();\n\n            std::shared_ptr<Node> parseValue();\n\n            void error();\n\n        public:\n\n            Parser (const CSMWorld::Data& data);\n\n            bool parse (const std::string& filter, bool allowPredefined = true);\n            ///< Discards any previous calls to parse\n            ///\n            /// \\return Success?\n\n            std::shared_ptr<Node> getFilter() const;\n            ///< Throws an exception if the last call to parse did not return true.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/textnode.cpp",
    "content": "#include \"textnode.hpp\"\n\n#include <sstream>\n#include <stdexcept>\n\n#include <QRegExp>\n\n#include \"../world/columns.hpp\"\n#include \"../world/idtablebase.hpp\"\n\nCSMFilter::TextNode::TextNode (int columnId, const std::string& text)\n: mColumnId (columnId), mText (text)\n{}\n\nbool CSMFilter::TextNode::test (const CSMWorld::IdTableBase& table, int row,\n    const std::map<int, int>& columns) const\n{\n    const std::map<int, int>::const_iterator iter = columns.find (mColumnId);\n\n    if (iter==columns.end())\n        throw std::logic_error (\"invalid column in text node test\");\n\n    if (iter->second==-1)\n        return true;\n\n    QModelIndex index = table.index (row, iter->second);\n\n    QVariant data = table.data (index);\n\n    QString string;\n\n    if (data.type()==QVariant::String)\n    {\n        string = data.toString();\n    }\n    else if ((data.type()==QVariant::Int || data.type()==QVariant::UInt) &&\n        CSMWorld::Columns::hasEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId)))\n    {\n        int value = data.toInt();\n\n        std::vector<std::pair<int,std::string>> enums =\n            CSMWorld::Columns::getEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId));\n\n        if (value>=0 && value<static_cast<int> (enums.size()))\n            string = QString::fromUtf8 (enums[value].second.c_str());\n    }\n    else if (data.type()==QVariant::Bool)\n    {\n        string = data.toBool() ? \"true\" : \"false\";\n    }\n    else if (mText.empty() && !data.isValid())\n        return true;\n    else\n        return false;\n\n    /// \\todo make pattern syntax configurable\n    QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive);\n\n    return regExp.exactMatch (string);\n}\n\nstd::vector<int> CSMFilter::TextNode::getReferencedColumns() const\n{\n    return std::vector<int> (1, mColumnId);\n}\n\nstd::string CSMFilter::TextNode::toString (bool numericColumns) const\n{\n    std::ostringstream stream;\n\n    stream << \"text (\";\n\n    if (numericColumns)\n        stream << mColumnId;\n    else\n        stream\n            << \"\\\"\"\n            << CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))\n            << \"\\\"\";\n\n    stream << \", \\\"\" << mText << \"\\\")\";\n\n    return stream.str();\n}\n"
  },
  {
    "path": "apps/opencs/model/filter/textnode.hpp",
    "content": "#ifndef CSM_FILTER_TEXTNODE_H\n#define CSM_FILTER_TEXTNODE_H\n\n#include \"leafnode.hpp\"\n\nnamespace CSMFilter\n{\n    class TextNode : public LeafNode\n    {\n            int mColumnId;\n            std::string mText;\n\n        public:\n\n            TextNode (int columnId, const std::string& text);\n\n            bool test (const CSMWorld::IdTableBase& table, int row,\n                const std::map<int, int>& columns) const override;\n            ///< \\return Can the specified table row pass through to filter?\n            /// \\param columns column ID to column index mapping\n\n            std::vector<int> getReferencedColumns() const override;\n            ///< Return a list of the IDs of the columns referenced by this node. The column mapping\n            /// passed into test as columns must contain all columns listed here.\n\n            std::string toString (bool numericColumns) const override;\n            ///< Return a string that represents this node.\n            ///\n            /// \\param numericColumns Use numeric IDs instead of string to represent columns.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/unarynode.cpp",
    "content": "#include \"unarynode.hpp\"\n\nCSMFilter::UnaryNode::UnaryNode (std::shared_ptr<Node> child, const std::string& name)\n: mChild (child), mName (name)\n{}\n\nconst CSMFilter::Node& CSMFilter::UnaryNode::getChild() const\n{\n    return *mChild;\n}\n\nCSMFilter::Node& CSMFilter::UnaryNode::getChild()\n{\n    return *mChild;\n}\n\nstd::vector<int> CSMFilter::UnaryNode::getReferencedColumns() const\n{\n    return mChild->getReferencedColumns();\n}\n\nstd::string CSMFilter::UnaryNode::toString (bool numericColumns) const\n{\n    return mName + \" \" + mChild->toString (numericColumns);\n}\n"
  },
  {
    "path": "apps/opencs/model/filter/unarynode.hpp",
    "content": "#ifndef CSM_FILTER_UNARYNODE_H\n#define CSM_FILTER_UNARYNODE_H\n\n#include \"node.hpp\"\n\nnamespace CSMFilter\n{\n    class UnaryNode : public Node\n    {\n            std::shared_ptr<Node> mChild;\n            std::string mName;\n\n        public:\n\n            UnaryNode (std::shared_ptr<Node> child, const std::string& name);\n\n            const Node& getChild() const;\n\n            Node& getChild();\n\n            std::vector<int> getReferencedColumns() const override;\n            ///< Return a list of the IDs of the columns referenced by this node. The column mapping\n            /// passed into test as columns must contain all columns listed here.\n\n            std::string toString (bool numericColumns) const override;\n            ///< Return a string that represents this node.\n            ///\n            /// \\param numericColumns Use numeric IDs instead of string to represent columns.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/filter/valuenode.cpp",
    "content": "#include \"valuenode.hpp\"\n\n#include <sstream>\n#include <stdexcept>\n\n#include \"../world/columns.hpp\"\n#include \"../world/idtablebase.hpp\"\n\nCSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType,\n    double lower, double upper)\n: mColumnId (columnId), mLower (lower), mUpper (upper), mLowerType (lowerType), mUpperType (upperType){}\n\nbool CSMFilter::ValueNode::test (const CSMWorld::IdTableBase& table, int row,\n    const std::map<int, int>& columns) const\n{\n    const std::map<int, int>::const_iterator iter = columns.find (mColumnId);\n\n    if (iter==columns.end())\n        throw std::logic_error (\"invalid column in value node test\");\n\n    if (iter->second==-1)\n        return true;\n\n    QModelIndex index = table.index (row, iter->second);\n\n    QVariant data = table.data (index);\n\n    if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int &&\n        data.type()!=QVariant::UInt && data.type()!=static_cast<QVariant::Type> (QMetaType::Float))\n        return false;\n\n    double value = data.toDouble();\n\n    switch (mLowerType)\n    {\n        case Type_Closed: if (value<mLower) return false; break;\n        case Type_Open: if (value<=mLower) return false; break;\n        case Type_Infinite: break;\n    }\n\n    switch (mUpperType)\n    {\n        case Type_Closed: if (value>mUpper) return false; break;\n        case Type_Open: if (value>=mUpper) return false; break;\n        case Type_Infinite: break;\n    }\n\n    return true;\n}\n\nstd::vector<int> CSMFilter::ValueNode::getReferencedColumns() const\n{\n    return std::vector<int> (1, mColumnId);\n}\n\nstd::string CSMFilter::ValueNode::toString (bool numericColumns) const\n{\n    std::ostringstream stream;\n\n    stream << \"value (\";\n\n    if (numericColumns)\n        stream << mColumnId;\n    else\n        stream\n            << \"\\\"\"\n            << CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))\n            << \"\\\"\";\n\n    stream << \", \";\n\n    if (mLower==mUpper && mLowerType!=Type_Infinite && mUpperType!=Type_Infinite)\n        stream << mLower;\n    else\n    {\n        switch (mLowerType)\n        {\n            case Type_Closed: stream << \"[\" << mLower; break;\n            case Type_Open: stream << \"(\" << mLower; break;\n            case Type_Infinite: stream << \"(\"; break;\n        }\n\n        stream << \", \";\n\n        switch (mUpperType)\n        {\n            case Type_Closed: stream << mUpper << \"]\"; break;\n            case Type_Open: stream << mUpper << \")\"; break;\n            case Type_Infinite: stream << \")\"; break;\n        }\n    }\n\n    stream << \")\";\n\n    return stream.str();\n}\n"
  },
  {
    "path": "apps/opencs/model/filter/valuenode.hpp",
    "content": "#ifndef CSM_FILTER_VALUENODE_H\n#define CSM_FILTER_VALUENODE_H\n\n#include \"leafnode.hpp\"\n\nnamespace CSMFilter\n{\n    class ValueNode : public LeafNode\n    {\n        public:\n\n            enum Type\n            {\n                Type_Closed, Type_Open, Type_Infinite\n            };\n\n        private:\n\n            int mColumnId;\n            std::string mText;\n            double mLower;\n            double mUpper;\n            Type mLowerType;\n            Type mUpperType;\n\n        public:\n\n            ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper);\n\n            bool test (const CSMWorld::IdTableBase& table, int row,\n                const std::map<int, int>& columns) const override;\n            ///< \\return Can the specified table row pass through to filter?\n            /// \\param columns column ID to column index mapping\n\n            std::vector<int> getReferencedColumns() const override;\n            ///< Return a list of the IDs of the columns referenced by this node. The column mapping\n            /// passed into test as columns must contain all columns listed here.\n\n            std::string toString (bool numericColumns) const override;\n            ///< Return a string that represents this node.\n            ///\n            /// \\param numericColumns Use numeric IDs instead of string to represent columns.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/boolsetting.cpp",
    "content": "\n#include \"boolsetting.hpp\"\n\n#include <QCheckBox>\n#include <QMutexLocker>\n\n#include <components/settings/settings.hpp>\n\n#include \"category.hpp\"\n#include \"state.hpp\"\n\nCSMPrefs::BoolSetting::BoolSetting (Category *parent, Settings::Manager *values,\n  QMutex *mutex, const std::string& key, const std::string& label, bool default_)\n: Setting (parent, values, mutex, key, label),  mDefault (default_), mWidget(nullptr)\n{}\n\nCSMPrefs::BoolSetting& CSMPrefs::BoolSetting::setTooltip (const std::string& tooltip)\n{\n    mTooltip = tooltip;\n    return *this;\n}\n\nstd::pair<QWidget *, QWidget *> CSMPrefs::BoolSetting::makeWidgets (QWidget *parent)\n{\n    mWidget = new QCheckBox (QString::fromUtf8 (getLabel().c_str()), parent);\n    mWidget->setCheckState (mDefault ? Qt::Checked : Qt::Unchecked);\n\n    if (!mTooltip.empty())\n    {\n        QString tooltip = QString::fromUtf8 (mTooltip.c_str());\n        mWidget->setToolTip (tooltip);\n    }\n\n    connect (mWidget, SIGNAL (stateChanged (int)), this, SLOT (valueChanged (int)));\n\n    return std::make_pair (static_cast<QWidget *> (nullptr), mWidget);\n}\n\nvoid CSMPrefs::BoolSetting::updateWidget()\n{\n    if (mWidget)\n    {\n        mWidget->setCheckState(getValues().getBool(getKey(), getParent()->getKey())\n            ? Qt::Checked\n            : Qt::Unchecked);\n    }\n}\n\nvoid CSMPrefs::BoolSetting::valueChanged (int value)\n{\n    {\n        QMutexLocker lock (getMutex());\n        getValues().setBool (getKey(), getParent()->getKey(), value);\n    }\n\n    getParent()->getState()->update (*this);\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/boolsetting.hpp",
    "content": "#ifndef CSM_PREFS_BOOLSETTING_H\n#define CSM_PREFS_BOOLSETTING_H\n\n#include \"setting.hpp\"\n\nclass QCheckBox;\n\nnamespace CSMPrefs\n{\n    class BoolSetting : public Setting\n    {\n            Q_OBJECT\n\n            std::string mTooltip;\n            bool mDefault;\n            QCheckBox* mWidget;\n\n        public:\n\n            BoolSetting (Category *parent, Settings::Manager *values,\n                QMutex *mutex, const std::string& key, const std::string& label, bool default_);\n\n            BoolSetting& setTooltip (const std::string& tooltip);\n\n            /// Return label, input widget.\n            std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent) override;\n\n            void updateWidget() override;\n\n        private slots:\n\n            void valueChanged (int value);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/category.cpp",
    "content": "\n#include \"category.hpp\"\n\n#include <stdexcept>\n\n#include \"setting.hpp\"\n#include \"state.hpp\"\n\nCSMPrefs::Category::Category (State *parent, const std::string& key)\n: mParent (parent), mKey (key)\n{}\n\nconst std::string& CSMPrefs::Category::getKey() const\n{\n    return mKey;\n}\n\nCSMPrefs::State *CSMPrefs::Category::getState() const\n{\n    return mParent;\n}\n\nvoid CSMPrefs::Category::addSetting (Setting *setting)\n{\n    mSettings.push_back (setting);\n}\n\nCSMPrefs::Category::Iterator CSMPrefs::Category::begin()\n{\n    return mSettings.begin();\n}\n\nCSMPrefs::Category::Iterator CSMPrefs::Category::end()\n{\n    return mSettings.end();\n}\n\nCSMPrefs::Setting& CSMPrefs::Category::operator[] (const std::string& key)\n{\n    for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter)\n        if ((*iter)->getKey()==key)\n            return **iter;\n\n    throw std::logic_error (\"Invalid user setting: \" + key);\n}\n\nvoid CSMPrefs::Category::update()\n{\n    for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter)\n        mParent->update (**iter);\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/category.hpp",
    "content": "#ifndef CSM_PREFS_CATEGORY_H\n#define CSM_PREFS_CATEGORY_H\n\n#include <string>\n#include <vector>\n\nnamespace CSMPrefs\n{\n    class State;\n    class Setting;\n\n    class Category\n    {\n        public:\n\n            typedef std::vector<Setting *> Container;\n            typedef Container::iterator Iterator;\n\n        private:\n\n            State *mParent;\n            std::string mKey;\n            Container mSettings;\n\n        public:\n\n            Category (State *parent, const std::string& key);\n\n            const std::string& getKey() const;\n\n            State *getState() const;\n\n            void addSetting (Setting *setting);\n\n            Iterator begin();\n\n            Iterator end();\n\n            Setting& operator[] (const std::string& key);\n\n            void update();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/coloursetting.cpp",
    "content": "\n#include \"coloursetting.hpp\"\n\n#include <QLabel>\n#include <QMutexLocker>\n#include <QString>\n\n#include <components/settings/settings.hpp>\n\n#include \"../../view/widget/coloreditor.hpp\"\n\n#include \"category.hpp\"\n#include \"state.hpp\"\n\nCSMPrefs::ColourSetting::ColourSetting (Category *parent, Settings::Manager *values,\n  QMutex *mutex, const std::string& key, const std::string& label, QColor default_)\n: Setting (parent, values, mutex, key, label), mDefault (default_), mWidget(nullptr)\n{}\n\nCSMPrefs::ColourSetting& CSMPrefs::ColourSetting::setTooltip (const std::string& tooltip)\n{\n    mTooltip = tooltip;\n    return *this;\n}\n\nstd::pair<QWidget *, QWidget *> CSMPrefs::ColourSetting::makeWidgets (QWidget *parent)\n{\n    QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);\n\n    mWidget = new CSVWidget::ColorEditor (mDefault, parent);\n\n    if (!mTooltip.empty())\n    {\n        QString tooltip = QString::fromUtf8 (mTooltip.c_str());\n        label->setToolTip (tooltip);\n        mWidget->setToolTip (tooltip);\n    }\n\n    connect (mWidget, SIGNAL (pickingFinished()), this, SLOT (valueChanged()));\n\n    return std::make_pair (label, mWidget);\n}\n\nvoid CSMPrefs::ColourSetting::updateWidget()\n{\n    if (mWidget)\n    {\n        mWidget->setColor(QString::fromStdString\n            (getValues().getString(getKey(), getParent()->getKey())));\n    }\n}\n\nvoid CSMPrefs::ColourSetting::valueChanged()\n{\n    CSVWidget::ColorEditor& widget = dynamic_cast<CSVWidget::ColorEditor&> (*sender());\n    {\n        QMutexLocker lock (getMutex());\n        getValues().setString (getKey(), getParent()->getKey(), widget.color().name().toUtf8().data());\n    }\n\n    getParent()->getState()->update (*this);\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/coloursetting.hpp",
    "content": "#ifndef CSM_PREFS_COLOURSETTING_H\n#define CSM_PREFS_COLOURSETTING_H\n\n#include \"setting.hpp\"\n\n#include <QColor>\n\nnamespace CSVWidget\n{\n    class ColorEditor;\n}\n\nnamespace CSMPrefs\n{\n    class ColourSetting : public Setting\n    {\n            Q_OBJECT\n\n            std::string mTooltip;\n            QColor mDefault;\n            CSVWidget::ColorEditor* mWidget;\n\n        public:\n\n            ColourSetting (Category *parent, Settings::Manager *values,\n                QMutex *mutex, const std::string& key, const std::string& label,\n                QColor default_);\n\n            ColourSetting& setTooltip (const std::string& tooltip);\n\n            /// Return label, input widget.\n            std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent) override;\n\n            void updateWidget() override;\n\n        private slots:\n\n            void valueChanged();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/doublesetting.cpp",
    "content": "\n#include \"doublesetting.hpp\"\n\n#include <limits>\n\n#include <QLabel>\n#include <QDoubleSpinBox>\n#include <QMutexLocker>\n\n#include <components/settings/settings.hpp>\n\n#include \"category.hpp\"\n#include \"state.hpp\"\n\nCSMPrefs::DoubleSetting::DoubleSetting (Category *parent, Settings::Manager *values,\n  QMutex *mutex, const std::string& key, const std::string& label, double default_)\n: Setting (parent, values, mutex, key, label),\n  mPrecision(2), mMin (0), mMax (std::numeric_limits<double>::max()),\n  mDefault (default_), mWidget(nullptr)\n{}\n\nCSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setPrecision(int precision)\n{\n    mPrecision = precision;\n    return *this;\n}\n\nCSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setRange (double min, double max)\n{\n    mMin = min;\n    mMax = max;\n    return *this;\n}\n\nCSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMin (double min)\n{\n    mMin = min;\n    return *this;\n}\n\nCSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMax (double max)\n{\n    mMax = max;\n    return *this;\n}\n\nCSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setTooltip (const std::string& tooltip)\n{\n    mTooltip = tooltip;\n    return *this;\n}\n\nstd::pair<QWidget *, QWidget *> CSMPrefs::DoubleSetting::makeWidgets (QWidget *parent)\n{\n    QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);\n\n    mWidget = new QDoubleSpinBox (parent);\n    mWidget->setDecimals(mPrecision);\n    mWidget->setRange (mMin, mMax);\n    mWidget->setValue (mDefault);\n\n    if (!mTooltip.empty())\n    {\n        QString tooltip = QString::fromUtf8 (mTooltip.c_str());\n        label->setToolTip (tooltip);\n        mWidget->setToolTip (tooltip);\n    }\n\n    connect (mWidget, SIGNAL (valueChanged (double)), this, SLOT (valueChanged (double)));\n\n    return std::make_pair (label, mWidget);\n}\n\nvoid CSMPrefs::DoubleSetting::updateWidget()\n{\n    if (mWidget)\n    {\n        mWidget->setValue(getValues().getFloat(getKey(), getParent()->getKey()));\n    }\n}\n\nvoid CSMPrefs::DoubleSetting::valueChanged (double value)\n{\n    {\n        QMutexLocker lock (getMutex());\n        getValues().setFloat (getKey(), getParent()->getKey(), value);\n    }\n\n    getParent()->getState()->update (*this);\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/doublesetting.hpp",
    "content": "#ifndef CSM_PREFS_DOUBLESETTING_H\n#define CSM_PREFS_DOUBLESETTING_H\n\n#include \"setting.hpp\"\n\nclass QDoubleSpinBox;\n\nnamespace CSMPrefs\n{\n    class DoubleSetting : public Setting\n    {\n            Q_OBJECT\n\n            int mPrecision;\n            double mMin;\n            double mMax;\n            std::string mTooltip;\n            double mDefault;\n            QDoubleSpinBox* mWidget;\n\n        public:\n\n            DoubleSetting (Category *parent, Settings::Manager *values,\n                QMutex *mutex, const std::string& key, const std::string& label,\n                double default_);\n\n            DoubleSetting& setPrecision (int precision);\n\n            // defaults to [0, std::numeric_limits<double>::max()]\n            DoubleSetting& setRange (double min, double max);\n\n            DoubleSetting& setMin (double min);\n\n            DoubleSetting& setMax (double max);\n\n            DoubleSetting& setTooltip (const std::string& tooltip);\n\n            /// Return label, input widget.\n            std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent) override;\n\n            void updateWidget() override;\n\n        private slots:\n\n            void valueChanged (double value);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/enumsetting.cpp",
    "content": "\n#include \"enumsetting.hpp\"\n\n#include <QLabel>\n#include <QComboBox>\n#include <QMutexLocker>\n#include <QString>\n\n#include <components/settings/settings.hpp>\n\n#include \"category.hpp\"\n#include \"state.hpp\"\n\n\nCSMPrefs::EnumValue::EnumValue (const std::string& value, const std::string& tooltip)\n: mValue (value), mTooltip (tooltip)\n{}\n\nCSMPrefs::EnumValue::EnumValue (const char *value)\n: mValue (value)\n{}\n\n\nCSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValues& values)\n{\n    mValues.insert (mValues.end(), values.mValues.begin(), values.mValues.end());\n    return *this;\n}\n\nCSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValue& value)\n{\n    mValues.push_back (value);\n    return *this;\n}\n\nCSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const std::string& value, const std::string& tooltip)\n{\n    mValues.emplace_back(value, tooltip);\n    return *this;\n}\n\n\nCSMPrefs::EnumSetting::EnumSetting (Category *parent, Settings::Manager *values,\n  QMutex *mutex, const std::string& key, const std::string& label, const EnumValue& default_)\n: Setting (parent, values, mutex, key, label), mDefault (default_), mWidget(nullptr)\n{}\n\nCSMPrefs::EnumSetting& CSMPrefs::EnumSetting::setTooltip (const std::string& tooltip)\n{\n    mTooltip = tooltip;\n    return *this;\n}\n\nCSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValues (const EnumValues& values)\n{\n    mValues.add (values);\n    return *this;\n}\n\nCSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const EnumValue& value)\n{\n    mValues.add (value);\n    return *this;\n}\n\nCSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const std::string& value, const std::string& tooltip)\n{\n    mValues.add (value, tooltip);\n    return *this;\n}\n\nstd::pair<QWidget *, QWidget *> CSMPrefs::EnumSetting::makeWidgets (QWidget *parent)\n{\n    QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);\n\n    mWidget = new QComboBox (parent);\n\n    int index = 0;\n\n    for (int i=0; i<static_cast<int> (mValues.mValues.size()); ++i)\n    {\n        if (mDefault.mValue==mValues.mValues[i].mValue)\n            index = i;\n\n        mWidget->addItem (QString::fromUtf8 (mValues.mValues[i].mValue.c_str()));\n\n        if (!mValues.mValues[i].mTooltip.empty())\n            mWidget->setItemData (i, QString::fromUtf8 (mValues.mValues[i].mTooltip.c_str()),\n                Qt::ToolTipRole);\n    }\n\n    mWidget->setCurrentIndex (index);\n\n    if (!mTooltip.empty())\n    {\n        QString tooltip = QString::fromUtf8 (mTooltip.c_str());\n        label->setToolTip (tooltip);\n    }\n\n    connect (mWidget, SIGNAL (currentIndexChanged (int)), this, SLOT (valueChanged (int)));\n\n    return std::make_pair (label, mWidget);\n}\n\nvoid CSMPrefs::EnumSetting::updateWidget()\n{\n    if (mWidget)\n    {\n        int index = mWidget->findText(QString::fromStdString\n            (getValues().getString(getKey(), getParent()->getKey())));\n\n        mWidget->setCurrentIndex(index);\n    }\n}\n\nvoid CSMPrefs::EnumSetting::valueChanged (int value)\n{\n    {\n        QMutexLocker lock (getMutex());\n        getValues().setString (getKey(), getParent()->getKey(), mValues.mValues.at (value).mValue);\n    }\n\n    getParent()->getState()->update (*this);\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/enumsetting.hpp",
    "content": "#ifndef CSM_PREFS_ENUMSETTING_H\n#define CSM_PREFS_ENUMSETTING_H\n\n#include <vector>\n\n#include \"setting.hpp\"\n\nclass QComboBox;\n\nnamespace CSMPrefs\n{\n    struct EnumValue\n    {\n        std::string mValue;\n        std::string mTooltip;\n\n        EnumValue (const std::string& value, const std::string& tooltip = \"\");\n\n        EnumValue (const char *value);\n    };\n\n    struct EnumValues\n    {\n        std::vector<EnumValue> mValues;\n\n        EnumValues& add (const EnumValues& values);\n\n        EnumValues& add (const EnumValue& value);\n\n        EnumValues& add (const std::string& value, const std::string& tooltip);\n    };\n\n    class EnumSetting : public Setting\n    {\n            Q_OBJECT\n\n            std::string mTooltip;\n            EnumValue mDefault;\n            EnumValues mValues;\n            QComboBox* mWidget;\n\n        public:\n\n            EnumSetting (Category *parent, Settings::Manager *values,\n                QMutex *mutex, const std::string& key, const std::string& label,\n                const EnumValue& default_);\n\n            EnumSetting& setTooltip (const std::string& tooltip);\n\n            EnumSetting& addValues (const EnumValues& values);\n\n            EnumSetting& addValue (const EnumValue& value);\n\n            EnumSetting& addValue (const std::string& value, const std::string& tooltip);\n\n            /// Return label, input widget.\n            std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent) override;\n\n            void updateWidget() override;\n\n        private slots:\n\n            void valueChanged (int value);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/intsetting.cpp",
    "content": "\n#include \"intsetting.hpp\"\n\n#include <limits>\n\n#include <QLabel>\n#include <QSpinBox>\n#include <QMutexLocker>\n\n#include <components/settings/settings.hpp>\n\n#include \"category.hpp\"\n#include \"state.hpp\"\n\nCSMPrefs::IntSetting::IntSetting (Category *parent, Settings::Manager *values,\n  QMutex *mutex, const std::string& key, const std::string& label, int default_)\n: Setting (parent, values, mutex, key, label), mMin (0), mMax (std::numeric_limits<int>::max()),\n  mDefault (default_), mWidget(nullptr)\n{}\n\nCSMPrefs::IntSetting& CSMPrefs::IntSetting::setRange (int min, int max)\n{\n    mMin = min;\n    mMax = max;\n    return *this;\n}\n\nCSMPrefs::IntSetting& CSMPrefs::IntSetting::setMin (int min)\n{\n    mMin = min;\n    return *this;\n}\n\nCSMPrefs::IntSetting& CSMPrefs::IntSetting::setMax (int max)\n{\n    mMax = max;\n    return *this;\n}\n\nCSMPrefs::IntSetting& CSMPrefs::IntSetting::setTooltip (const std::string& tooltip)\n{\n    mTooltip = tooltip;\n    return *this;\n}\n\nstd::pair<QWidget *, QWidget *> CSMPrefs::IntSetting::makeWidgets (QWidget *parent)\n{\n    QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);\n\n    mWidget = new QSpinBox (parent);\n    mWidget->setRange (mMin, mMax);\n    mWidget->setValue (mDefault);\n\n    if (!mTooltip.empty())\n    {\n        QString tooltip = QString::fromUtf8 (mTooltip.c_str());\n        label->setToolTip (tooltip);\n        mWidget->setToolTip (tooltip);\n    }\n\n    connect (mWidget, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));\n\n    return std::make_pair (label, mWidget);\n}\n\nvoid CSMPrefs::IntSetting::updateWidget()\n{\n    if (mWidget)\n    {\n        mWidget->setValue(getValues().getInt(getKey(), getParent()->getKey()));\n    }\n}\n\nvoid CSMPrefs::IntSetting::valueChanged (int value)\n{\n    {\n        QMutexLocker lock (getMutex());\n        getValues().setInt (getKey(), getParent()->getKey(), value);\n    }\n\n    getParent()->getState()->update (*this);\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/intsetting.hpp",
    "content": "#ifndef CSM_PREFS_INTSETTING_H\n#define CSM_PREFS_INTSETTING_H\n\n#include \"setting.hpp\"\n\nclass QSpinBox;\n\nnamespace CSMPrefs\n{\n    class IntSetting : public Setting\n    {\n            Q_OBJECT\n\n            int mMin;\n            int mMax;\n            std::string mTooltip;\n            int mDefault;\n            QSpinBox* mWidget;\n\n        public:\n\n            IntSetting (Category *parent, Settings::Manager *values,\n                QMutex *mutex, const std::string& key, const std::string& label, int default_);\n\n            // defaults to [0, std::numeric_limits<int>::max()]\n            IntSetting& setRange (int min, int max);\n\n            IntSetting& setMin (int min);\n\n            IntSetting& setMax (int max);\n\n            IntSetting& setTooltip (const std::string& tooltip);\n\n            /// Return label, input widget.\n            std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent) override;\n\n            void updateWidget() override;\n\n        private slots:\n\n            void valueChanged (int value);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/modifiersetting.cpp",
    "content": "#include \"modifiersetting.hpp\"\n\n#include <QEvent>\n#include <QKeyEvent>\n#include <QLabel>\n#include <QMouseEvent>\n#include <QPushButton>\n#include <QWidget>\n\n#include \"state.hpp\"\n#include \"shortcutmanager.hpp\"\n\nnamespace CSMPrefs\n{\n    ModifierSetting::ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,\n        const std::string& label)\n        : Setting(parent, values, mutex, key, label)\n        , mButton(nullptr)\n        , mEditorActive(false)\n    {\n    }\n\n    std::pair<QWidget*, QWidget*> ModifierSetting::makeWidgets(QWidget* parent)\n    {\n        int modifier = 0;\n        State::get().getShortcutManager().getModifier(getKey(), modifier);\n\n        QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(modifier).c_str());\n\n        QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);\n        QPushButton* widget = new QPushButton(text, parent);\n\n        widget->setCheckable(true);\n        widget->installEventFilter(this);\n\n        // right clicking on button sets shortcut to RMB, so context menu should not appear\n        widget->setContextMenuPolicy(Qt::PreventContextMenu);\n\n        mButton = widget;\n\n        connect(widget, SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool)));\n\n        return std::make_pair(label, widget);\n    }\n\n    void ModifierSetting::updateWidget()\n    {\n        if (mButton)\n        {\n            std::string shortcut = getValues().getString(getKey(), getParent()->getKey());\n\n            int modifier;\n            State::get().getShortcutManager().convertFromString(shortcut, modifier);\n            State::get().getShortcutManager().setModifier(getKey(), modifier);\n            resetState();\n        }\n    }\n\n    bool ModifierSetting::eventFilter(QObject* target, QEvent* event)\n    {\n        if (event->type() == QEvent::KeyPress)\n        {\n            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);\n            if (keyEvent->isAutoRepeat())\n                return true;\n\n            int mod = keyEvent->modifiers();\n            int key = keyEvent->key();\n\n            return handleEvent(target, mod, key);\n        }\n        else if (event->type() == QEvent::MouseButtonPress)\n        {\n            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);\n            int mod = mouseEvent->modifiers();\n            int button = mouseEvent->button();\n\n            return handleEvent(target, mod, button);\n        }\n        else if (event->type() == QEvent::FocusOut)\n        {\n            resetState();\n        }\n\n        return false;\n    }\n\n    bool ModifierSetting::handleEvent(QObject* target, int mod, int value)\n    {\n        // For potential future exceptions\n        const int Blacklist[] =\n        {\n            0\n        };\n\n        const size_t BlacklistSize = sizeof(Blacklist) / sizeof(int);\n\n        if (!mEditorActive)\n        {\n            if (value == Qt::RightButton)\n            {\n                // Clear modifier\n                int modifier = 0;\n                storeValue(modifier);\n\n                resetState();\n            }\n\n            return false;\n        }\n\n        // Handle blacklist\n        for (size_t i = 0; i < BlacklistSize; ++i)\n        {\n            if (value == Blacklist[i])\n                return true;\n        }\n\n\n        // Update modifier\n        int modifier = value;\n        storeValue(modifier);\n\n        resetState();\n\n        return true;\n    }\n\n    void ModifierSetting::storeValue(int modifier)\n    {\n        State::get().getShortcutManager().setModifier(getKey(), modifier);\n\n        // Convert to string and assign\n        std::string value = State::get().getShortcutManager().convertToString(modifier);\n\n        {\n            QMutexLocker lock(getMutex());\n            getValues().setString(getKey(), getParent()->getKey(), value);\n        }\n\n        getParent()->getState()->update(*this);\n    }\n\n    void ModifierSetting::resetState()\n    {\n        mButton->setChecked(false);\n        mEditorActive = false;\n\n        // Button text\n        int modifier = 0;\n        State::get().getShortcutManager().getModifier(getKey(), modifier);\n\n        QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(modifier).c_str());\n        mButton->setText(text);\n    }\n\n    void ModifierSetting::buttonToggled(bool checked)\n    {\n        if (checked)\n            mButton->setText(\"Press keys or click here...\");\n\n        mEditorActive = checked;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/modifiersetting.hpp",
    "content": "#ifndef CSM_PREFS_MODIFIERSETTING_H\n#define CSM_PREFS_MODIFIERSETTING_H\n\n#include <QKeySequence>\n\n#include \"setting.hpp\"\n\nclass QEvent;\nclass QPushButton;\n\nnamespace CSMPrefs\n{\n    class ModifierSetting : public Setting\n    {\n            Q_OBJECT\n\n        public:\n\n            ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,\n                const std::string& label);\n\n            std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;\n\n            void updateWidget() override;\n\n        protected:\n\n            bool eventFilter(QObject* target, QEvent* event) override;\n\n        private:\n\n            bool handleEvent(QObject* target, int mod, int value);\n\n            void storeValue(int modifier);\n            void resetState();\n\n            QPushButton* mButton;\n            bool mEditorActive;\n\n        private slots:\n\n            void buttonToggled(bool checked);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/setting.cpp",
    "content": "\n#include \"setting.hpp\"\n\n#include <QColor>\n#include <QMutexLocker>\n\n#include \"category.hpp\"\n#include \"state.hpp\"\n\nSettings::Manager& CSMPrefs::Setting::getValues()\n{\n    return *mValues;\n}\n\nQMutex *CSMPrefs::Setting::getMutex()\n{\n    return mMutex;\n}\n\nCSMPrefs::Setting::Setting (Category *parent, Settings::Manager *values, QMutex *mutex,\n    const std::string& key, const std::string& label)\n: QObject (parent->getState()), mParent (parent), mValues (values), mMutex (mutex), mKey (key),\n  mLabel (label)\n{}\n\nCSMPrefs::Setting:: ~Setting() {}\n\nstd::pair<QWidget *, QWidget *> CSMPrefs::Setting::makeWidgets (QWidget *parent)\n{\n    return std::pair<QWidget *, QWidget *> (0, 0);\n}\n\nvoid CSMPrefs::Setting::updateWidget()\n{\n}\n\nconst CSMPrefs::Category *CSMPrefs::Setting::getParent() const\n{\n    return mParent;\n}\n\nconst std::string& CSMPrefs::Setting::getKey() const\n{\n    return mKey;\n}\n\nconst std::string& CSMPrefs::Setting::getLabel() const\n{\n    return mLabel;\n}\n\nint CSMPrefs::Setting::toInt() const\n{\n    QMutexLocker lock (mMutex);\n    return mValues->getInt (mKey, mParent->getKey());\n}\n\ndouble CSMPrefs::Setting::toDouble() const\n{\n    QMutexLocker lock (mMutex);\n    return mValues->getFloat (mKey, mParent->getKey());\n}\n\nstd::string CSMPrefs::Setting::toString() const\n{\n    QMutexLocker lock (mMutex);\n    return mValues->getString (mKey, mParent->getKey());\n}\n\nbool CSMPrefs::Setting::isTrue() const\n{\n    QMutexLocker lock (mMutex);\n    return mValues->getBool (mKey, mParent->getKey());\n}\n\nQColor CSMPrefs::Setting::toColor() const\n{\n    // toString() handles lock\n    return QColor (QString::fromUtf8 (toString().c_str()));\n}\n\nbool CSMPrefs::operator== (const Setting& setting, const std::string& key)\n{\n    std::string fullKey = setting.getParent()->getKey() + \"/\" + setting.getKey();\n    return fullKey==key;\n}\n\nbool CSMPrefs::operator== (const std::string& key, const Setting& setting)\n{\n    return setting==key;\n}\n\nbool CSMPrefs::operator!= (const Setting& setting, const std::string& key)\n{\n    return !(setting==key);\n}\n\nbool CSMPrefs::operator!= (const std::string& key, const Setting& setting)\n{\n    return !(key==setting);\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/setting.hpp",
    "content": "#ifndef CSM_PREFS_SETTING_H\n#define CSM_PREFS_SETTING_H\n\n#include <string>\n#include <utility>\n\n#include <QObject>\n\nclass QWidget;\nclass QColor;\nclass QMutex;\n\nnamespace Settings\n{\n    class Manager;\n}\n\nnamespace CSMPrefs\n{\n    class Category;\n\n    class Setting : public QObject\n    {\n            Q_OBJECT\n\n            Category *mParent;\n            Settings::Manager *mValues;\n            QMutex *mMutex;\n            std::string mKey;\n            std::string mLabel;\n\n        protected:\n\n            Settings::Manager& getValues();\n\n            QMutex *getMutex();\n\n        public:\n\n            Setting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label);\n\n            virtual ~Setting();\n\n            /// Return label, input widget.\n            ///\n            /// \\note first can be a 0-pointer, which means that the label is part of the input\n            /// widget.\n            virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);\n\n            /// Updates the widget returned by makeWidgets() to the current setting.\n            ///\n            /// \\note If make_widgets() has not been called yet then nothing happens.\n            virtual void updateWidget();\n\n            const Category *getParent() const;\n\n            const std::string& getKey() const;\n\n            const std::string& getLabel() const;\n\n            int toInt() const;\n\n            double toDouble() const;\n\n            std::string toString() const;\n\n            bool isTrue() const;\n\n            QColor toColor() const;\n    };\n\n    // note: fullKeys have the format categoryKey/settingKey\n    bool operator== (const Setting& setting, const std::string& fullKey);\n    bool operator== (const std::string& fullKey, const Setting& setting);\n    bool operator!= (const Setting& setting, const std::string& fullKey);\n    bool operator!= (const std::string& fullKey, const Setting& setting);\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/shortcut.cpp",
    "content": "#include \"shortcut.hpp\"\n\n#include <cassert>\n\n#include <QAction>\n#include <QWidget>\n\n#include <components/debug/debuglog.hpp>\n\n#include \"state.hpp\"\n#include \"shortcutmanager.hpp\"\n\nnamespace CSMPrefs\n{\n    Shortcut::Shortcut(const std::string& name, QWidget* parent)\n        : QObject(parent)\n        , mEnabled(true)\n        , mName(name)\n        , mModName(\"\")\n        , mSecondaryMode(SM_Ignore)\n        , mModifier(0)\n        , mCurrentPos(0)\n        , mLastPos(0)\n        , mActivationStatus(AS_Inactive)\n        , mModifierStatus(false)\n        , mAction(nullptr)\n    {\n        assert (parent);\n\n        State::get().getShortcutManager().addShortcut(this);\n        State::get().getShortcutManager().getSequence(name, mSequence);\n    }\n\n    Shortcut::Shortcut(const std::string& name, const std::string& modName, QWidget* parent)\n        : QObject(parent)\n        , mEnabled(true)\n        , mName(name)\n        , mModName(modName)\n        , mSecondaryMode(SM_Ignore)\n        , mModifier(0)\n        , mCurrentPos(0)\n        , mLastPos(0)\n        , mActivationStatus(AS_Inactive)\n        , mModifierStatus(false)\n        , mAction(nullptr)\n    {\n        assert (parent);\n\n        State::get().getShortcutManager().addShortcut(this);\n        State::get().getShortcutManager().getSequence(name, mSequence);\n        State::get().getShortcutManager().getModifier(modName, mModifier);\n    }\n\n    Shortcut::Shortcut(const std::string& name, const std::string& modName, SecondaryMode secMode, QWidget* parent)\n        : QObject(parent)\n        , mEnabled(true)\n        , mName(name)\n        , mModName(modName)\n        , mSecondaryMode(secMode)\n        , mModifier(0)\n        , mCurrentPos(0)\n        , mLastPos(0)\n        , mActivationStatus(AS_Inactive)\n        , mModifierStatus(false)\n        , mAction(nullptr)\n    {\n        assert (parent);\n\n        State::get().getShortcutManager().addShortcut(this);\n        State::get().getShortcutManager().getSequence(name, mSequence);\n        State::get().getShortcutManager().getModifier(modName, mModifier);\n    }\n\n    Shortcut::~Shortcut()\n    {\n        try\n        {\n            State::get().getShortcutManager().removeShortcut(this);\n        }\n        catch(const std::exception& e)\n        {\n            Log(Debug::Error) << \"Error in the destructor: \" << e.what();\n        }\n    }\n\n    bool Shortcut::isEnabled() const\n    {\n        return mEnabled;\n    }\n\n    const std::string& Shortcut::getName() const\n    {\n        return mName;\n    }\n\n    const std::string& Shortcut::getModifierName() const\n    {\n        return mModName;\n    }\n\n    Shortcut::SecondaryMode Shortcut::getSecondaryMode() const\n    {\n        return mSecondaryMode;\n    }\n\n    const QKeySequence& Shortcut::getSequence() const\n    {\n        return mSequence;\n    }\n\n    int Shortcut::getModifier() const\n    {\n        return mModifier;\n    }\n\n    int Shortcut::getPosition() const\n    {\n        return mCurrentPos;\n    }\n\n    int Shortcut::getLastPosition() const\n    {\n        return mLastPos;\n    }\n\n    Shortcut::ActivationStatus Shortcut::getActivationStatus() const\n    {\n        return mActivationStatus;\n    }\n\n    bool Shortcut::getModifierStatus() const\n    {\n        return mModifierStatus;\n    }\n\n    void Shortcut::enable(bool state)\n    {\n        mEnabled = state;\n    }\n\n    void Shortcut::setSequence(const QKeySequence& sequence)\n    {\n        mSequence = sequence;\n        mCurrentPos = 0;\n        mLastPos = sequence.count() - 1;\n\n        if (mAction)\n        {\n            mAction->setText(mActionText + \"\\t\" + State::get().getShortcutManager().convertToString(mSequence).data());\n        }\n    }\n\n    void Shortcut::setModifier(int modifier)\n    {\n        mModifier = modifier;\n    }\n\n    void Shortcut::setPosition(int pos)\n    {\n        mCurrentPos = pos;\n    }\n\n    void Shortcut::setActivationStatus(ActivationStatus status)\n    {\n        mActivationStatus = status;\n    }\n\n    void Shortcut::setModifierStatus(bool status)\n    {\n        mModifierStatus = status;\n    }\n\n    void Shortcut::associateAction(QAction* action)\n    {\n        if (mAction)\n        {\n            mAction->setText(mActionText);\n\n            disconnect(this, SIGNAL(activated()), mAction, SLOT(trigger()));\n            disconnect(mAction, SIGNAL(destroyed()), this, SLOT(actionDeleted()));\n        }\n\n        mAction = action;\n\n        if (mAction)\n        {\n            mActionText = mAction->text();\n            mAction->setText(mActionText + \"\\t\" + State::get().getShortcutManager().convertToString(mSequence).data());\n\n            connect(this, SIGNAL(activated()), mAction, SLOT(trigger()));\n            connect(mAction, SIGNAL(destroyed()), this, SLOT(actionDeleted()));\n        }\n    }\n\n    void Shortcut::signalActivated(bool state)\n    {\n        emit activated(state);\n    }\n\n    void Shortcut::signalActivated()\n    {\n        emit activated();\n    }\n\n    void Shortcut::signalSecondary(bool state)\n    {\n        emit secondary(state);\n    }\n    void Shortcut::signalSecondary()\n    {\n        emit secondary();\n    }\n\n    QString Shortcut::toString() const\n    {\n        return QString(State::get().getShortcutManager().convertToString(mSequence, mModifier).data());\n    }\n\n    void Shortcut::actionDeleted()\n    {\n        mAction = nullptr;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/shortcut.hpp",
    "content": "#ifndef CSM_PREFS_SHORTCUT_H\n#define CSM_PREFS_SHORTCUT_H\n\n#include <string>\n\n#include <QKeySequence>\n#include <QObject>\n#include <QString>\n\nclass QAction;\nclass QWidget;\n\nnamespace CSMPrefs\n{\n    /// A class similar in purpose to QShortcut, but with the ability to use mouse buttons\n    class Shortcut : public QObject\n    {\n            Q_OBJECT\n\n        public:\n\n            enum ActivationStatus\n            {\n                AS_Regular,\n                AS_Secondary,\n                AS_Inactive\n            };\n\n            enum SecondaryMode\n            {\n                SM_Replace, ///< The secondary signal replaces the regular signal when the modifier is active\n                SM_Detach,  ///< The secondary signal is emitted independent of the regular signal, even if not active\n                SM_Ignore   ///< The secondary signal will not ever be emitted\n            };\n\n            Shortcut(const std::string& name, QWidget* parent);\n            Shortcut(const std::string& name, const std::string& modName, QWidget* parent);\n            Shortcut(const std::string& name, const std::string& modName, SecondaryMode secMode, QWidget* parent);\n\n            ~Shortcut();\n\n            bool isEnabled() const;\n\n            const std::string& getName() const;\n            const std::string& getModifierName() const;\n\n            SecondaryMode getSecondaryMode() const;\n\n            const QKeySequence& getSequence() const;\n            int getModifier() const;\n\n            /// The position in the sequence\n            int getPosition() const;\n            /// The position in the sequence\n            int getLastPosition() const;\n\n            ActivationStatus getActivationStatus() const;\n            bool getModifierStatus() const;\n\n            void enable(bool state);\n\n            void setSequence(const QKeySequence& sequence);\n            void setModifier(int modifier);\n\n            /// The position in the sequence\n            void setPosition(int pos);\n\n            void setActivationStatus(ActivationStatus status);\n            void setModifierStatus(bool status);\n\n            /// Appends the sequence to the QAction text, also keeps it up to date\n            void associateAction(QAction* action);\n\n            // Workaround for Qt4 signals being \"protected\"\n            void signalActivated(bool state);\n            void signalActivated();\n\n            void signalSecondary(bool state);\n            void signalSecondary();\n\n            QString toString() const;\n\n        private:\n\n            bool mEnabled;\n\n            std::string mName;\n            std::string mModName;\n            SecondaryMode mSecondaryMode;\n            QKeySequence mSequence;\n            int mModifier;\n\n            int mCurrentPos;\n            int mLastPos;\n\n            ActivationStatus mActivationStatus;\n            bool mModifierStatus;\n\n            QAction* mAction;\n            QString mActionText;\n\n        private slots:\n\n            void actionDeleted();\n\n        signals:\n\n            /// Triggered when the shortcut is activated or deactivated; can be determined from \\p state\n            void activated(bool state);\n\n            /// Convenience signal.\n            void activated();\n\n            /// Triggered depending on SecondaryMode\n            void secondary(bool state);\n\n            /// Convenience signal.\n            void secondary();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/shortcuteventhandler.cpp",
    "content": "#include \"shortcuteventhandler.hpp\"\n\n#include <algorithm>\n#include <cassert>\n\n#include <QEvent>\n#include <QKeyEvent>\n#include <QMouseEvent>\n#include <QWidget>\n\n#include \"shortcut.hpp\"\n\nnamespace CSMPrefs\n{\n    ShortcutEventHandler::ShortcutEventHandler(QObject* parent)\n        : QObject(parent)\n    {\n    }\n\n    void ShortcutEventHandler::addShortcut(Shortcut* shortcut)\n    {\n        // Enforced by shortcut class\n        QWidget* widget = static_cast<QWidget*>(shortcut->parent());\n\n        // Check if widget setup is needed\n        ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);\n        if (shortcutListIt == mWidgetShortcuts.end())\n        {\n            // Create list\n            shortcutListIt = mWidgetShortcuts.insert(std::make_pair(widget, ShortcutList())).first;\n\n            // Check if widget has a parent with shortcuts, unfortunately it is not typically set yet\n            updateParent(widget);\n\n            // Intercept widget events\n            widget->installEventFilter(this);\n            connect(widget, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));\n        }\n\n        // Add to list\n        shortcutListIt->second.push_back(shortcut);\n    }\n\n    void ShortcutEventHandler::removeShortcut(Shortcut* shortcut)\n    {\n        // Enforced by shortcut class\n        QWidget* widget = static_cast<QWidget*>(shortcut->parent());\n\n        ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);\n        if (shortcutListIt != mWidgetShortcuts.end())\n        {\n            shortcutListIt->second.erase(std::remove(shortcutListIt->second.begin(), shortcutListIt->second.end(), shortcut), shortcutListIt->second.end());\n        }\n    }\n\n    bool ShortcutEventHandler::eventFilter(QObject* watched, QEvent* event)\n    {\n        // Process event\n        if (event->type() == QEvent::KeyPress)\n        {\n            QWidget* widget = static_cast<QWidget*>(watched);\n            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);\n            unsigned int mod = (unsigned int) keyEvent->modifiers();\n            unsigned int key = (unsigned int) keyEvent->key();\n\n            if (!keyEvent->isAutoRepeat())\n                return activate(widget, mod, key);\n        }\n        else if (event->type() == QEvent::KeyRelease)\n        {\n            QWidget* widget = static_cast<QWidget*>(watched);\n            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);\n            unsigned int mod = (unsigned int) keyEvent->modifiers();\n            unsigned int key = (unsigned int) keyEvent->key();\n\n            if (!keyEvent->isAutoRepeat())\n                return deactivate(widget, mod, key);\n        }\n        else if (event->type() == QEvent::MouseButtonPress)\n        {\n            QWidget* widget = static_cast<QWidget*>(watched);\n            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);\n            unsigned int mod = (unsigned int) mouseEvent->modifiers();\n            unsigned int button = (unsigned int) mouseEvent->button();\n\n            return activate(widget, mod, button);\n        }\n        else if (event->type() == QEvent::MouseButtonRelease)\n        {\n            QWidget* widget = static_cast<QWidget*>(watched);\n            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);\n            unsigned int mod = (unsigned int) mouseEvent->modifiers();\n            unsigned int button = (unsigned int) mouseEvent->button();\n\n            return deactivate(widget, mod, button);\n        }\n        else if (event->type() == QEvent::FocusOut)\n        {\n            QWidget* widget = static_cast<QWidget*>(watched);\n            ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);\n\n            // Deactivate in case events are missed\n            for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)\n            {\n                Shortcut* shortcut = *it;\n\n                shortcut->setPosition(0);\n                shortcut->setModifierStatus(false);\n\n                if (shortcut->getActivationStatus() == Shortcut::AS_Regular)\n                {\n                    shortcut->setActivationStatus(Shortcut::AS_Inactive);\n                    shortcut->signalActivated(false);\n                }\n                else if (shortcut->getActivationStatus() == Shortcut::AS_Secondary)\n                {\n                    shortcut->setActivationStatus(Shortcut::AS_Inactive);\n                    shortcut->signalSecondary(false);\n                }\n            }\n        }\n        else if (event->type() == QEvent::FocusIn)\n        {\n            QWidget* widget = static_cast<QWidget*>(watched);\n            updateParent(widget);\n        }\n\n        return false;\n    }\n\n    void ShortcutEventHandler::updateParent(QWidget* widget)\n    {\n        QWidget* parent = widget->parentWidget();\n        while (parent)\n        {\n            ShortcutMap::iterator parentIt = mWidgetShortcuts.find(parent);\n            if (parentIt != mWidgetShortcuts.end())\n            {\n                mChildParentRelations.insert(std::make_pair(widget, parent));\n                updateParent(parent);\n                break;\n            }\n\n            // Check next\n            parent = parent->parentWidget();\n        }\n    }\n\n    bool ShortcutEventHandler::activate(QWidget* widget, unsigned int mod, unsigned int button)\n    {\n        std::vector<std::pair<MatchResult, Shortcut*> > potentials;\n        bool used = false;\n\n        while (widget)\n        {\n            ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);\n            assert(shortcutListIt != mWidgetShortcuts.end());\n\n            // Find potential activations\n            for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)\n            {\n                Shortcut* shortcut = *it;\n\n                if (!shortcut->isEnabled())\n                    continue;\n\n                if (checkModifier(mod, button, shortcut, true))\n                    used = true;\n\n                if (shortcut->getActivationStatus() != Shortcut::AS_Inactive)\n                    continue;\n\n                int pos = shortcut->getPosition();\n                int lastPos = shortcut->getLastPosition();\n                MatchResult result = match(mod, button, shortcut->getSequence()[pos]);\n\n                if (result == Matches_WithMod || result == Matches_NoMod)\n                {\n                    if (pos < lastPos && (result == Matches_WithMod || pos > 0))\n                    {\n                        shortcut->setPosition(pos+1);\n                    }\n                    else if (pos == lastPos)\n                    {\n                        potentials.emplace_back(result, shortcut);\n                    }\n                }\n            }\n\n            // Move on to parent\n            WidgetMap::iterator widgetIt = mChildParentRelations.find(widget);\n            widget = (widgetIt != mChildParentRelations.end()) ? widgetIt->second : 0;\n        }\n\n        // Only activate the best match; in exact conflicts, this will favor the first shortcut added.\n        if (!potentials.empty())\n        {\n            std::stable_sort(potentials.begin(), potentials.end(), ShortcutEventHandler::sort);\n            Shortcut* shortcut = potentials.front().second;\n\n            if (shortcut->getModifierStatus() && shortcut->getSecondaryMode() == Shortcut::SM_Replace)\n            {\n                shortcut->setActivationStatus(Shortcut::AS_Secondary);\n                shortcut->signalSecondary(true);\n                shortcut->signalSecondary();\n            }\n            else\n            {\n                shortcut->setActivationStatus(Shortcut::AS_Regular);\n                shortcut->signalActivated(true);\n                shortcut->signalActivated();\n            }\n\n            used = true;\n        }\n\n        return used;\n    }\n\n    bool ShortcutEventHandler::deactivate(QWidget* widget, unsigned int mod, unsigned int button)\n    {\n        const int KeyMask = 0x01FFFFFF;\n\n        bool used = false;\n\n        while (widget)\n        {\n            ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);\n            assert(shortcutListIt != mWidgetShortcuts.end());\n\n            for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)\n            {\n                Shortcut* shortcut = *it;\n\n                if (checkModifier(mod, button, shortcut, false))\n                    used = true;\n\n                int pos = shortcut->getPosition();\n                MatchResult result = match(0, button, shortcut->getSequence()[pos] & KeyMask);\n\n                if (result != Matches_Not)\n                {\n                    shortcut->setPosition(0);\n\n                    if (shortcut->getActivationStatus() == Shortcut::AS_Regular)\n                    {\n                        shortcut->setActivationStatus(Shortcut::AS_Inactive);\n                        shortcut->signalActivated(false);\n                        used = true;\n                    }\n                    else if (shortcut->getActivationStatus() == Shortcut::AS_Secondary)\n                    {\n                        shortcut->setActivationStatus(Shortcut::AS_Inactive);\n                        shortcut->signalSecondary(false);\n                        used = true;\n                    }\n                }\n            }\n\n            // Move on to parent\n            WidgetMap::iterator widgetIt = mChildParentRelations.find(widget);\n            widget = (widgetIt != mChildParentRelations.end()) ? widgetIt->second : 0;\n        }\n\n        return used;\n    }\n\n    bool ShortcutEventHandler::checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate)\n    {\n        if (!shortcut->isEnabled() || !shortcut->getModifier() || shortcut->getSecondaryMode() == Shortcut::SM_Ignore ||\n            shortcut->getModifierStatus() == activate)\n            return false;\n\n        MatchResult result = match(mod, button, shortcut->getModifier());\n        bool used = false;\n\n        if (result != Matches_Not)\n        {\n            shortcut->setModifierStatus(activate);\n\n            if (shortcut->getSecondaryMode() == Shortcut::SM_Detach)\n            {\n                if (activate)\n                {\n                    shortcut->signalSecondary(true);\n                    shortcut->signalSecondary();\n                }\n                else\n                {\n                    shortcut->signalSecondary(false);\n                }\n            }\n            else if (!activate && shortcut->getActivationStatus() == Shortcut::AS_Secondary)\n            {\n                shortcut->setActivationStatus(Shortcut::AS_Inactive);\n                shortcut->setPosition(0);\n                shortcut->signalSecondary(false);\n                used = true;\n            }\n        }\n\n        return used;\n    }\n\n    ShortcutEventHandler::MatchResult ShortcutEventHandler::match(unsigned int mod, unsigned int button,\n        unsigned int value)\n    {\n        if ((mod | button) == value)\n        {\n            return Matches_WithMod;\n        }\n        else if (button == value)\n        {\n            return Matches_NoMod;\n        }\n        else\n        {\n            return Matches_Not;\n        }\n    }\n\n    bool ShortcutEventHandler::sort(const std::pair<MatchResult, Shortcut*>& left,\n        const std::pair<MatchResult, Shortcut*>& right)\n    {\n        if (left.first == Matches_WithMod && right.first == Matches_NoMod)\n            return true;\n        else\n            return left.second->getPosition() > right.second->getPosition();\n    }\n\n    void ShortcutEventHandler::widgetDestroyed()\n    {\n        QWidget* widget = static_cast<QWidget*>(sender());\n\n        mWidgetShortcuts.erase(widget);\n        mChildParentRelations.erase(widget);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/shortcuteventhandler.hpp",
    "content": "#ifndef CSM_PREFS_SHORTCUT_EVENT_HANDLER_H\n#define CSM_PREFS_SHORTCUT_EVENT_HANDLER_H\n\n#include <map>\n#include <vector>\n\n#include <QObject>\n\nclass QEvent;\nclass QWidget;\n\nnamespace CSMPrefs\n{\n    class Shortcut;\n\n    /// Users of this class should install it as an event handler\n    class ShortcutEventHandler : public QObject\n    {\n            Q_OBJECT\n\n        public:\n\n            ShortcutEventHandler(QObject* parent);\n\n            void addShortcut(Shortcut* shortcut);\n            void removeShortcut(Shortcut* shortcut);\n\n        protected:\n\n            bool eventFilter(QObject* watched, QEvent* event) override;\n\n        private:\n\n            typedef std::vector<Shortcut*> ShortcutList;\n            // Child, Parent\n            typedef std::map<QWidget*, QWidget*> WidgetMap;\n            typedef std::map<QWidget*, ShortcutList> ShortcutMap;\n\n            enum MatchResult\n            {\n                Matches_WithMod,\n                Matches_NoMod,\n                Matches_Not\n            };\n\n            void updateParent(QWidget* widget);\n\n            bool activate(QWidget* widget, unsigned int mod, unsigned int button);\n\n            bool deactivate(QWidget* widget, unsigned int mod, unsigned int button);\n\n            bool checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate);\n\n            MatchResult match(unsigned int mod, unsigned int button, unsigned int value);\n\n            // Prefers Matches_WithMod and a larger number of buttons\n            static bool sort(const std::pair<MatchResult, Shortcut*>& left,\n                const std::pair<MatchResult, Shortcut*>& right);\n\n            WidgetMap mChildParentRelations;\n            ShortcutMap mWidgetShortcuts;\n\n        private slots:\n\n            void widgetDestroyed();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/shortcutmanager.cpp",
    "content": "#include \"shortcutmanager.hpp\"\n\n#include <algorithm>\n\n#include <QApplication>\n#include <QStringList>\n\n#include \"shortcut.hpp\"\n#include \"shortcuteventhandler.hpp\"\n\nnamespace CSMPrefs\n{\n    ShortcutManager::ShortcutManager()\n    {\n        createLookupTables();\n        mEventHandler = new ShortcutEventHandler(this);\n    }\n\n    void ShortcutManager::addShortcut(Shortcut* shortcut)\n    {\n        mShortcuts.insert(std::make_pair(shortcut->getName(), shortcut));\n        mShortcuts.insert(std::make_pair(shortcut->getModifierName(), shortcut));\n        mEventHandler->addShortcut(shortcut);\n    }\n\n    void ShortcutManager::removeShortcut(Shortcut* shortcut)\n    {\n        std::pair<ShortcutMap::iterator, ShortcutMap::iterator> range = mShortcuts.equal_range(shortcut->getName());\n\n        for (ShortcutMap::iterator it = range.first; it != range.second;)\n        {\n            if (it->second == shortcut)\n            {\n                mShortcuts.erase(it++);\n            }\n            else\n            {\n                ++it;\n            }\n        }\n\n        mEventHandler->removeShortcut(shortcut);\n    }\n\n    bool ShortcutManager::getSequence(const std::string& name, QKeySequence& sequence) const\n    {\n        SequenceMap::const_iterator item = mSequences.find(name);\n        if (item != mSequences.end())\n        {\n            sequence = item->second;\n\n            return true;\n        }\n        else\n            return false;\n    }\n\n    void ShortcutManager::setSequence(const std::string& name, const QKeySequence& sequence)\n    {\n        // Add to map/modify\n        SequenceMap::iterator item = mSequences.find(name);\n        if (item != mSequences.end())\n        {\n            item->second = sequence;\n        }\n        else\n        {\n            mSequences.insert(std::make_pair(name, sequence));\n        }\n\n        // Change active shortcuts\n        std::pair<ShortcutMap::iterator, ShortcutMap::iterator> rangeS = mShortcuts.equal_range(name);\n\n        for (ShortcutMap::iterator it = rangeS.first; it != rangeS.second; ++it)\n        {\n            it->second->setSequence(sequence);\n        }\n    }\n\n    bool ShortcutManager::getModifier(const std::string& name, int& modifier) const\n    {\n        ModifierMap::const_iterator item = mModifiers.find(name);\n        if (item != mModifiers.end())\n        {\n            modifier = item->second;\n\n            return true;\n        }\n        else\n            return false;\n    }\n\n    void ShortcutManager::setModifier(const std::string& name, int modifier)\n    {\n        // Add to map/modify\n        ModifierMap::iterator item = mModifiers.find(name);\n        if (item != mModifiers.end())\n        {\n            item->second = modifier;\n        }\n        else\n        {\n            mModifiers.insert(std::make_pair(name, modifier));\n        }\n\n        // Change active shortcuts\n        std::pair<ShortcutMap::iterator, ShortcutMap::iterator> rangeS = mShortcuts.equal_range(name);\n\n        for (ShortcutMap::iterator it = rangeS.first; it != rangeS.second; ++it)\n        {\n            it->second->setModifier(modifier);\n        }\n    }\n\n    std::string ShortcutManager::convertToString(const QKeySequence& sequence) const\n    {\n        const int MouseKeyMask = 0x01FFFFFF;\n        const int ModMask = 0x7E000000;\n\n        std::string result;\n\n        for (int i = 0; i < (int)sequence.count(); ++i)\n        {\n            int mods = sequence[i] & ModMask;\n            int key = sequence[i] & MouseKeyMask;\n\n            if (key)\n            {\n                NameMap::const_iterator searchResult = mNames.find(key);\n                if (searchResult != mNames.end())\n                {\n                    if (mods && i == 0)\n                    {\n                        if (mods & Qt::ControlModifier)\n                            result.append(\"Ctrl+\");\n                        if (mods & Qt::ShiftModifier)\n                            result.append(\"Shift+\");\n                        if (mods & Qt::AltModifier)\n                            result.append(\"Alt+\");\n                        if (mods & Qt::MetaModifier)\n                            result.append(\"Meta+\");\n                        if (mods & Qt::KeypadModifier)\n                            result.append(\"Keypad+\");\n                        if (mods & Qt::GroupSwitchModifier)\n                            result.append(\"GroupSwitch+\");\n                    }\n                    else if (i > 0)\n                    {\n                        result.append(\"+\");\n                    }\n\n                    result.append(searchResult->second);\n                }\n            }\n        }\n\n        return result;\n    }\n\n    std::string ShortcutManager::convertToString(int modifier) const\n    {\n        NameMap::const_iterator searchResult = mNames.find(modifier);\n        if (searchResult != mNames.end())\n        {\n            return searchResult->second;\n        }\n        else\n            return \"\";\n    }\n\n    std::string ShortcutManager::convertToString(const QKeySequence& sequence, int modifier) const\n    {\n        std::string concat = convertToString(sequence) + \";\" + convertToString(modifier);\n        return concat;\n    }\n\n    void ShortcutManager::convertFromString(const std::string& data, QKeySequence& sequence) const\n    {\n        const int MaxKeys = 4; // A limitation of QKeySequence\n\n        size_t end = data.find(';');\n        size_t size = std::min(end, data.size());\n\n        std::string value = data.substr(0, size);\n        size_t start = 0;\n\n        int keyPos = 0;\n        int mods = 0;\n\n        int keys[MaxKeys] = {};\n\n        while (start < value.size())\n        {\n            end = data.find('+', start);\n            end = std::min(end, value.size());\n\n            std::string name = value.substr(start, end - start);\n\n            if (name == \"Ctrl\")\n            {\n                mods |= Qt::ControlModifier;\n            }\n            else if (name == \"Shift\")\n            {\n                mods |= Qt::ShiftModifier;\n            }\n            else if (name == \"Alt\")\n            {\n                mods |= Qt::AltModifier;\n            }\n            else if (name == \"Meta\")\n            {\n                mods |= Qt::MetaModifier;\n            }\n            else if (name == \"Keypad\")\n            {\n                mods |= Qt::KeypadModifier;\n            }\n            else if (name == \"GroupSwitch\")\n            {\n                mods |= Qt::GroupSwitchModifier;\n            }\n            else\n            {\n                KeyMap::const_iterator searchResult = mKeys.find(name);\n                if (searchResult != mKeys.end())\n                {\n                    keys[keyPos] = mods | searchResult->second;\n\n                    mods = 0;\n                    keyPos += 1;\n\n                    if (keyPos >= MaxKeys)\n                        break;\n                }\n            }\n\n            start = end + 1;\n        }\n\n        sequence = QKeySequence(keys[0], keys[1], keys[2], keys[3]);\n    }\n\n    void ShortcutManager::convertFromString(const std::string& data, int& modifier) const\n    {\n        size_t start = data.find(';') + 1;\n        start = std::min(start, data.size());\n\n        std::string name = data.substr(start);\n        KeyMap::const_iterator searchResult = mKeys.find(name);\n        if (searchResult != mKeys.end())\n        {\n            modifier = searchResult->second;\n        }\n        else\n        {\n            modifier = 0;\n        }\n    }\n\n    void ShortcutManager::convertFromString(const std::string& data, QKeySequence& sequence, int& modifier) const\n    {\n        convertFromString(data, sequence);\n        convertFromString(data, modifier);\n    }\n\n    void ShortcutManager::createLookupTables()\n    {\n        // Mouse buttons\n        mNames.insert(std::make_pair(Qt::LeftButton, \"LMB\"));\n        mNames.insert(std::make_pair(Qt::RightButton, \"RMB\"));\n        mNames.insert(std::make_pair(Qt::MiddleButton, \"MMB\"));\n        mNames.insert(std::make_pair(Qt::XButton1, \"Mouse4\"));\n        mNames.insert(std::make_pair(Qt::XButton2, \"Mouse5\"));\n\n        // Keyboard buttons\n        for (size_t i = 0; QtKeys[i].first != 0; ++i)\n        {\n            mNames.insert(QtKeys[i]);\n        }\n\n        // Generate inverse map\n        for (NameMap::const_iterator it = mNames.begin(); it != mNames.end(); ++it)\n        {\n            mKeys.insert(std::make_pair(it->second, it->first));\n        }\n    }\n\n    QString ShortcutManager::processToolTip(const QString& toolTip) const\n    {\n        const QChar SequenceStart = '{';\n        const QChar SequenceEnd = '}';\n\n        QStringList substrings;\n\n        int prevIndex = 0;\n        int startIndex = toolTip.indexOf(SequenceStart);\n        int endIndex = (startIndex != -1) ? toolTip.indexOf(SequenceEnd, startIndex) : -1;\n\n        // Process every valid shortcut escape sequence\n        while (startIndex != -1 && endIndex != -1)\n        {\n            int count = startIndex - prevIndex;\n            if (count > 0)\n            {\n                substrings.push_back(toolTip.mid(prevIndex, count));\n            }\n\n            // Find sequence name\n            startIndex += 1; // '{' character\n            count = endIndex - startIndex;\n            if (count > 0)\n            {\n                QString settingName = toolTip.mid(startIndex, count);\n\n                QKeySequence sequence;\n                int modifier;\n\n                if (getSequence(settingName.toUtf8().data(), sequence))\n                {\n                    QString value = QString::fromUtf8(convertToString(sequence).c_str());\n                    substrings.push_back(value);\n                }\n                else if (getModifier(settingName.toUtf8().data(), modifier))\n                {\n                    QString value = QString::fromUtf8(convertToString(modifier).c_str());\n                    substrings.push_back(value);\n                }\n\n                prevIndex = endIndex + 1; // '}' character\n            }\n\n            startIndex = toolTip.indexOf(SequenceStart, endIndex);\n            endIndex = (startIndex != -1) ? toolTip.indexOf(SequenceEnd, startIndex) : -1;\n        }\n\n        if (prevIndex < toolTip.size())\n        {\n            substrings.push_back(toolTip.mid(prevIndex));\n        }\n\n        return substrings.join(\"\");\n    }\n\n    const std::pair<int, const char*> ShortcutManager::QtKeys[] =\n    {\n        std::make_pair((int)Qt::Key_Space                  , \"Space\"),\n        std::make_pair((int)Qt::Key_Exclam                 , \"Exclam\"),\n        std::make_pair((int)Qt::Key_QuoteDbl               , \"QuoteDbl\"),\n        std::make_pair((int)Qt::Key_NumberSign             , \"NumberSign\"),\n        std::make_pair((int)Qt::Key_Dollar                 , \"Dollar\"),\n        std::make_pair((int)Qt::Key_Percent                , \"Percent\"),\n        std::make_pair((int)Qt::Key_Ampersand              , \"Ampersand\"),\n        std::make_pair((int)Qt::Key_Apostrophe             , \"Apostrophe\"),\n        std::make_pair((int)Qt::Key_ParenLeft              , \"ParenLeft\"),\n        std::make_pair((int)Qt::Key_ParenRight             , \"ParenRight\"),\n        std::make_pair((int)Qt::Key_Asterisk               , \"Asterisk\"),\n        std::make_pair((int)Qt::Key_Plus                   , \"Plus\"),\n        std::make_pair((int)Qt::Key_Comma                  , \"Comma\"),\n        std::make_pair((int)Qt::Key_Minus                  , \"Minus\"),\n        std::make_pair((int)Qt::Key_Period                 , \"Period\"),\n        std::make_pair((int)Qt::Key_Slash                  , \"Slash\"),\n        std::make_pair((int)Qt::Key_0                      , \"0\"),\n        std::make_pair((int)Qt::Key_1                      , \"1\"),\n        std::make_pair((int)Qt::Key_2                      , \"2\"),\n        std::make_pair((int)Qt::Key_3                      , \"3\"),\n        std::make_pair((int)Qt::Key_4                      , \"4\"),\n        std::make_pair((int)Qt::Key_5                      , \"5\"),\n        std::make_pair((int)Qt::Key_6                      , \"6\"),\n        std::make_pair((int)Qt::Key_7                      , \"7\"),\n        std::make_pair((int)Qt::Key_8                      , \"8\"),\n        std::make_pair((int)Qt::Key_9                      , \"9\"),\n        std::make_pair((int)Qt::Key_Colon                  , \"Colon\"),\n        std::make_pair((int)Qt::Key_Semicolon              , \"Semicolon\"),\n        std::make_pair((int)Qt::Key_Less                   , \"Less\"),\n        std::make_pair((int)Qt::Key_Equal                  , \"Equal\"),\n        std::make_pair((int)Qt::Key_Greater                , \"Greater\"),\n        std::make_pair((int)Qt::Key_Question               , \"Question\"),\n        std::make_pair((int)Qt::Key_At                     , \"At\"),\n        std::make_pair((int)Qt::Key_A                      , \"A\"),\n        std::make_pair((int)Qt::Key_B                      , \"B\"),\n        std::make_pair((int)Qt::Key_C                      , \"C\"),\n        std::make_pair((int)Qt::Key_D                      , \"D\"),\n        std::make_pair((int)Qt::Key_E                      , \"E\"),\n        std::make_pair((int)Qt::Key_F                      , \"F\"),\n        std::make_pair((int)Qt::Key_G                      , \"G\"),\n        std::make_pair((int)Qt::Key_H                      , \"H\"),\n        std::make_pair((int)Qt::Key_I                      , \"I\"),\n        std::make_pair((int)Qt::Key_J                      , \"J\"),\n        std::make_pair((int)Qt::Key_K                      , \"K\"),\n        std::make_pair((int)Qt::Key_L                      , \"L\"),\n        std::make_pair((int)Qt::Key_M                      , \"M\"),\n        std::make_pair((int)Qt::Key_N                      , \"N\"),\n        std::make_pair((int)Qt::Key_O                      , \"O\"),\n        std::make_pair((int)Qt::Key_P                      , \"P\"),\n        std::make_pair((int)Qt::Key_Q                      , \"Q\"),\n        std::make_pair((int)Qt::Key_R                      , \"R\"),\n        std::make_pair((int)Qt::Key_S                      , \"S\"),\n        std::make_pair((int)Qt::Key_T                      , \"T\"),\n        std::make_pair((int)Qt::Key_U                      , \"U\"),\n        std::make_pair((int)Qt::Key_V                      , \"V\"),\n        std::make_pair((int)Qt::Key_W                      , \"W\"),\n        std::make_pair((int)Qt::Key_X                      , \"X\"),\n        std::make_pair((int)Qt::Key_Y                      , \"Y\"),\n        std::make_pair((int)Qt::Key_Z                      , \"Z\"),\n        std::make_pair((int)Qt::Key_BracketLeft            , \"BracketLeft\"),\n        std::make_pair((int)Qt::Key_Backslash              , \"Backslash\"),\n        std::make_pair((int)Qt::Key_BracketRight           , \"BracketRight\"),\n        std::make_pair((int)Qt::Key_AsciiCircum            , \"AsciiCircum\"),\n        std::make_pair((int)Qt::Key_Underscore             , \"Underscore\"),\n        std::make_pair((int)Qt::Key_QuoteLeft              , \"QuoteLeft\"),\n        std::make_pair((int)Qt::Key_BraceLeft              , \"BraceLeft\"),\n        std::make_pair((int)Qt::Key_Bar                    , \"Bar\"),\n        std::make_pair((int)Qt::Key_BraceRight             , \"BraceRight\"),\n        std::make_pair((int)Qt::Key_AsciiTilde             , \"AsciiTilde\"),\n        std::make_pair((int)Qt::Key_nobreakspace           , \"nobreakspace\"),\n        std::make_pair((int)Qt::Key_exclamdown             , \"exclamdown\"),\n        std::make_pair((int)Qt::Key_cent                   , \"cent\"),\n        std::make_pair((int)Qt::Key_sterling               , \"sterling\"),\n        std::make_pair((int)Qt::Key_currency               , \"currency\"),\n        std::make_pair((int)Qt::Key_yen                    , \"yen\"),\n        std::make_pair((int)Qt::Key_brokenbar              , \"brokenbar\"),\n        std::make_pair((int)Qt::Key_section                , \"section\"),\n        std::make_pair((int)Qt::Key_diaeresis              , \"diaeresis\"),\n        std::make_pair((int)Qt::Key_copyright              , \"copyright\"),\n        std::make_pair((int)Qt::Key_ordfeminine            , \"ordfeminine\"),\n        std::make_pair((int)Qt::Key_guillemotleft          , \"guillemotleft\"),\n        std::make_pair((int)Qt::Key_notsign                , \"notsign\"),\n        std::make_pair((int)Qt::Key_hyphen                 , \"hyphen\"),\n        std::make_pair((int)Qt::Key_registered             , \"registered\"),\n        std::make_pair((int)Qt::Key_macron                 , \"macron\"),\n        std::make_pair((int)Qt::Key_degree                 , \"degree\"),\n        std::make_pair((int)Qt::Key_plusminus              , \"plusminus\"),\n        std::make_pair((int)Qt::Key_twosuperior            , \"twosuperior\"),\n        std::make_pair((int)Qt::Key_threesuperior          , \"threesuperior\"),\n        std::make_pair((int)Qt::Key_acute                  , \"acute\"),\n        std::make_pair((int)Qt::Key_mu                     , \"mu\"),\n        std::make_pair((int)Qt::Key_paragraph              , \"paragraph\"),\n        std::make_pair((int)Qt::Key_periodcentered         , \"periodcentered\"),\n        std::make_pair((int)Qt::Key_cedilla                , \"cedilla\"),\n        std::make_pair((int)Qt::Key_onesuperior            , \"onesuperior\"),\n        std::make_pair((int)Qt::Key_masculine              , \"masculine\"),\n        std::make_pair((int)Qt::Key_guillemotright         , \"guillemotright\"),\n        std::make_pair((int)Qt::Key_onequarter             , \"onequarter\"),\n        std::make_pair((int)Qt::Key_onehalf                , \"onehalf\"),\n        std::make_pair((int)Qt::Key_threequarters          , \"threequarters\"),\n        std::make_pair((int)Qt::Key_questiondown           , \"questiondown\"),\n        std::make_pair((int)Qt::Key_Agrave                 , \"Agrave\"),\n        std::make_pair((int)Qt::Key_Aacute                 , \"Aacute\"),\n        std::make_pair((int)Qt::Key_Acircumflex            , \"Acircumflex\"),\n        std::make_pair((int)Qt::Key_Atilde                 , \"Atilde\"),\n        std::make_pair((int)Qt::Key_Adiaeresis             , \"Adiaeresis\"),\n        std::make_pair((int)Qt::Key_Aring                  , \"Aring\"),\n        std::make_pair((int)Qt::Key_AE                     , \"AE\"),\n        std::make_pair((int)Qt::Key_Ccedilla               , \"Ccedilla\"),\n        std::make_pair((int)Qt::Key_Egrave                 , \"Egrave\"),\n        std::make_pair((int)Qt::Key_Eacute                 , \"Eacute\"),\n        std::make_pair((int)Qt::Key_Ecircumflex            , \"Ecircumflex\"),\n        std::make_pair((int)Qt::Key_Ediaeresis             , \"Ediaeresis\"),\n        std::make_pair((int)Qt::Key_Igrave                 , \"Igrave\"),\n        std::make_pair((int)Qt::Key_Iacute                 , \"Iacute\"),\n        std::make_pair((int)Qt::Key_Icircumflex            , \"Icircumflex\"),\n        std::make_pair((int)Qt::Key_Idiaeresis             , \"Idiaeresis\"),\n        std::make_pair((int)Qt::Key_ETH                    , \"ETH\"),\n        std::make_pair((int)Qt::Key_Ntilde                 , \"Ntilde\"),\n        std::make_pair((int)Qt::Key_Ograve                 , \"Ograve\"),\n        std::make_pair((int)Qt::Key_Oacute                 , \"Oacute\"),\n        std::make_pair((int)Qt::Key_Ocircumflex            , \"Ocircumflex\"),\n        std::make_pair((int)Qt::Key_Otilde                 , \"Otilde\"),\n        std::make_pair((int)Qt::Key_Odiaeresis             , \"Odiaeresis\"),\n        std::make_pair((int)Qt::Key_multiply               , \"multiply\"),\n        std::make_pair((int)Qt::Key_Ooblique               , \"Ooblique\"),\n        std::make_pair((int)Qt::Key_Ugrave                 , \"Ugrave\"),\n        std::make_pair((int)Qt::Key_Uacute                 , \"Uacute\"),\n        std::make_pair((int)Qt::Key_Ucircumflex            , \"Ucircumflex\"),\n        std::make_pair((int)Qt::Key_Udiaeresis             , \"Udiaeresis\"),\n        std::make_pair((int)Qt::Key_Yacute                 , \"Yacute\"),\n        std::make_pair((int)Qt::Key_THORN                  , \"THORN\"),\n        std::make_pair((int)Qt::Key_ssharp                 , \"ssharp\"),\n        std::make_pair((int)Qt::Key_division               , \"division\"),\n        std::make_pair((int)Qt::Key_ydiaeresis             , \"ydiaeresis\"),\n        std::make_pair((int)Qt::Key_Escape                 , \"Escape\"),\n        std::make_pair((int)Qt::Key_Tab                    , \"Tab\"),\n        std::make_pair((int)Qt::Key_Backtab                , \"Backtab\"),\n        std::make_pair((int)Qt::Key_Backspace              , \"Backspace\"),\n        std::make_pair((int)Qt::Key_Return                 , \"Return\"),\n        std::make_pair((int)Qt::Key_Enter                  , \"Enter\"),\n        std::make_pair((int)Qt::Key_Insert                 , \"Insert\"),\n        std::make_pair((int)Qt::Key_Delete                 , \"Delete\"),\n        std::make_pair((int)Qt::Key_Pause                  , \"Pause\"),\n        std::make_pair((int)Qt::Key_Print                  , \"Print\"),\n        std::make_pair((int)Qt::Key_SysReq                 , \"SysReq\"),\n        std::make_pair((int)Qt::Key_Clear                  , \"Clear\"),\n        std::make_pair((int)Qt::Key_Home                   , \"Home\"),\n        std::make_pair((int)Qt::Key_End                    , \"End\"),\n        std::make_pair((int)Qt::Key_Left                   , \"Left\"),\n        std::make_pair((int)Qt::Key_Up                     , \"Up\"),\n        std::make_pair((int)Qt::Key_Right                  , \"Right\"),\n        std::make_pair((int)Qt::Key_Down                   , \"Down\"),\n        std::make_pair((int)Qt::Key_PageUp                 , \"PageUp\"),\n        std::make_pair((int)Qt::Key_PageDown               , \"PageDown\"),\n        std::make_pair((int)Qt::Key_Shift                  , \"Shift\"),\n        std::make_pair((int)Qt::Key_Control                , \"Control\"),\n        std::make_pair((int)Qt::Key_Meta                   , \"Meta\"),\n        std::make_pair((int)Qt::Key_Alt                    , \"Alt\"),\n        std::make_pair((int)Qt::Key_CapsLock               , \"CapsLock\"),\n        std::make_pair((int)Qt::Key_NumLock                , \"NumLock\"),\n        std::make_pair((int)Qt::Key_ScrollLock             , \"ScrollLock\"),\n        std::make_pair((int)Qt::Key_F1                     , \"F1\"),\n        std::make_pair((int)Qt::Key_F2                     , \"F2\"),\n        std::make_pair((int)Qt::Key_F3                     , \"F3\"),\n        std::make_pair((int)Qt::Key_F4                     , \"F4\"),\n        std::make_pair((int)Qt::Key_F5                     , \"F5\"),\n        std::make_pair((int)Qt::Key_F6                     , \"F6\"),\n        std::make_pair((int)Qt::Key_F7                     , \"F7\"),\n        std::make_pair((int)Qt::Key_F8                     , \"F8\"),\n        std::make_pair((int)Qt::Key_F9                     , \"F9\"),\n        std::make_pair((int)Qt::Key_F10                    , \"F10\"),\n        std::make_pair((int)Qt::Key_F11                    , \"F11\"),\n        std::make_pair((int)Qt::Key_F12                    , \"F12\"),\n        std::make_pair((int)Qt::Key_F13                    , \"F13\"),\n        std::make_pair((int)Qt::Key_F14                    , \"F14\"),\n        std::make_pair((int)Qt::Key_F15                    , \"F15\"),\n        std::make_pair((int)Qt::Key_F16                    , \"F16\"),\n        std::make_pair((int)Qt::Key_F17                    , \"F17\"),\n        std::make_pair((int)Qt::Key_F18                    , \"F18\"),\n        std::make_pair((int)Qt::Key_F19                    , \"F19\"),\n        std::make_pair((int)Qt::Key_F20                    , \"F20\"),\n        std::make_pair((int)Qt::Key_F21                    , \"F21\"),\n        std::make_pair((int)Qt::Key_F22                    , \"F22\"),\n        std::make_pair((int)Qt::Key_F23                    , \"F23\"),\n        std::make_pair((int)Qt::Key_F24                    , \"F24\"),\n        std::make_pair((int)Qt::Key_F25                    , \"F25\"),\n        std::make_pair((int)Qt::Key_F26                    , \"F26\"),\n        std::make_pair((int)Qt::Key_F27                    , \"F27\"),\n        std::make_pair((int)Qt::Key_F28                    , \"F28\"),\n        std::make_pair((int)Qt::Key_F29                    , \"F29\"),\n        std::make_pair((int)Qt::Key_F30                    , \"F30\"),\n        std::make_pair((int)Qt::Key_F31                    , \"F31\"),\n        std::make_pair((int)Qt::Key_F32                    , \"F32\"),\n        std::make_pair((int)Qt::Key_F33                    , \"F33\"),\n        std::make_pair((int)Qt::Key_F34                    , \"F34\"),\n        std::make_pair((int)Qt::Key_F35                    , \"F35\"),\n        std::make_pair((int)Qt::Key_Super_L                , \"Super_L\"),\n        std::make_pair((int)Qt::Key_Super_R                , \"Super_R\"),\n        std::make_pair((int)Qt::Key_Menu                   , \"Menu\"),\n        std::make_pair((int)Qt::Key_Hyper_L                , \"Hyper_L\"),\n        std::make_pair((int)Qt::Key_Hyper_R                , \"Hyper_R\"),\n        std::make_pair((int)Qt::Key_Help                   , \"Help\"),\n        std::make_pair((int)Qt::Key_Direction_L            , \"Direction_L\"),\n        std::make_pair((int)Qt::Key_Direction_R            , \"Direction_R\"),\n        std::make_pair((int)Qt::Key_Back                   , \"Back\"),\n        std::make_pair((int)Qt::Key_Forward                , \"Forward\"),\n        std::make_pair((int)Qt::Key_Stop                   , \"Stop\"),\n        std::make_pair((int)Qt::Key_Refresh                , \"Refresh\"),\n        std::make_pair((int)Qt::Key_VolumeDown             , \"VolumeDown\"),\n        std::make_pair((int)Qt::Key_VolumeMute             , \"VolumeMute\"),\n        std::make_pair((int)Qt::Key_VolumeUp               , \"VolumeUp\"),\n        std::make_pair((int)Qt::Key_BassBoost              , \"BassBoost\"),\n        std::make_pair((int)Qt::Key_BassUp                 , \"BassUp\"),\n        std::make_pair((int)Qt::Key_BassDown               , \"BassDown\"),\n        std::make_pair((int)Qt::Key_TrebleUp               , \"TrebleUp\"),\n        std::make_pair((int)Qt::Key_TrebleDown             , \"TrebleDown\"),\n        std::make_pair((int)Qt::Key_MediaPlay              , \"MediaPlay\"),\n        std::make_pair((int)Qt::Key_MediaStop              , \"MediaStop\"),\n        std::make_pair((int)Qt::Key_MediaPrevious          , \"MediaPrevious\"),\n        std::make_pair((int)Qt::Key_MediaNext              , \"MediaNext\"),\n        std::make_pair((int)Qt::Key_MediaRecord            , \"MediaRecord\"),\n        std::make_pair((int)Qt::Key_MediaPause             , \"MediaPause\"),\n        std::make_pair((int)Qt::Key_MediaTogglePlayPause   , \"MediaTogglePlayPause\"),\n        std::make_pair((int)Qt::Key_HomePage               , \"HomePage\"),\n        std::make_pair((int)Qt::Key_Favorites              , \"Favorites\"),\n        std::make_pair((int)Qt::Key_Search                 , \"Search\"),\n        std::make_pair((int)Qt::Key_Standby                , \"Standby\"),\n        std::make_pair((int)Qt::Key_OpenUrl                , \"OpenUrl\"),\n        std::make_pair((int)Qt::Key_LaunchMail             , \"LaunchMail\"),\n        std::make_pair((int)Qt::Key_LaunchMedia            , \"LaunchMedia\"),\n        std::make_pair((int)Qt::Key_Launch0                , \"Launch0\"),\n        std::make_pair((int)Qt::Key_Launch1                , \"Launch1\"),\n        std::make_pair((int)Qt::Key_Launch2                , \"Launch2\"),\n        std::make_pair((int)Qt::Key_Launch3                , \"Launch3\"),\n        std::make_pair((int)Qt::Key_Launch4                , \"Launch4\"),\n        std::make_pair((int)Qt::Key_Launch5                , \"Launch5\"),\n        std::make_pair((int)Qt::Key_Launch6                , \"Launch6\"),\n        std::make_pair((int)Qt::Key_Launch7                , \"Launch7\"),\n        std::make_pair((int)Qt::Key_Launch8                , \"Launch8\"),\n        std::make_pair((int)Qt::Key_Launch9                , \"Launch9\"),\n        std::make_pair((int)Qt::Key_LaunchA                , \"LaunchA\"),\n        std::make_pair((int)Qt::Key_LaunchB                , \"LaunchB\"),\n        std::make_pair((int)Qt::Key_LaunchC                , \"LaunchC\"),\n        std::make_pair((int)Qt::Key_LaunchD                , \"LaunchD\"),\n        std::make_pair((int)Qt::Key_LaunchE                , \"LaunchE\"),\n        std::make_pair((int)Qt::Key_LaunchF                , \"LaunchF\"),\n        std::make_pair((int)Qt::Key_MonBrightnessUp        , \"MonBrightnessUp\"),\n        std::make_pair((int)Qt::Key_MonBrightnessDown      , \"MonBrightnessDown\"),\n        std::make_pair((int)Qt::Key_KeyboardLightOnOff     , \"KeyboardLightOnOff\"),\n        std::make_pair((int)Qt::Key_KeyboardBrightnessUp   , \"KeyboardBrightnessUp\"),\n        std::make_pair((int)Qt::Key_KeyboardBrightnessDown , \"KeyboardBrightnessDown\"),\n        std::make_pair((int)Qt::Key_PowerOff               , \"PowerOff\"),\n        std::make_pair((int)Qt::Key_WakeUp                 , \"WakeUp\"),\n        std::make_pair((int)Qt::Key_Eject                  , \"Eject\"),\n        std::make_pair((int)Qt::Key_ScreenSaver            , \"ScreenSaver\"),\n        std::make_pair((int)Qt::Key_WWW                    , \"WWW\"),\n        std::make_pair((int)Qt::Key_Memo                   , \"Memo\"),\n        std::make_pair((int)Qt::Key_LightBulb              , \"LightBulb\"),\n        std::make_pair((int)Qt::Key_Shop                   , \"Shop\"),\n        std::make_pair((int)Qt::Key_History                , \"History\"),\n        std::make_pair((int)Qt::Key_AddFavorite            , \"AddFavorite\"),\n        std::make_pair((int)Qt::Key_HotLinks               , \"HotLinks\"),\n        std::make_pair((int)Qt::Key_BrightnessAdjust       , \"BrightnessAdjust\"),\n        std::make_pair((int)Qt::Key_Finance                , \"Finance\"),\n        std::make_pair((int)Qt::Key_Community              , \"Community\"),\n        std::make_pair((int)Qt::Key_AudioRewind            , \"AudioRewind\"),\n        std::make_pair((int)Qt::Key_BackForward            , \"BackForward\"),\n        std::make_pair((int)Qt::Key_ApplicationLeft        , \"ApplicationLeft\"),\n        std::make_pair((int)Qt::Key_ApplicationRight       , \"ApplicationRight\"),\n        std::make_pair((int)Qt::Key_Book                   , \"Book\"),\n        std::make_pair((int)Qt::Key_CD                     , \"CD\"),\n        std::make_pair((int)Qt::Key_Calculator             , \"Calculator\"),\n        std::make_pair((int)Qt::Key_ToDoList               , \"ToDoList\"),\n        std::make_pair((int)Qt::Key_ClearGrab              , \"ClearGrab\"),\n        std::make_pair((int)Qt::Key_Close                  , \"Close\"),\n        std::make_pair((int)Qt::Key_Copy                   , \"Copy\"),\n        std::make_pair((int)Qt::Key_Cut                    , \"Cut\"),\n        std::make_pair((int)Qt::Key_Display                , \"Display\"),\n        std::make_pair((int)Qt::Key_DOS                    , \"DOS\"),\n        std::make_pair((int)Qt::Key_Documents              , \"Documents\"),\n        std::make_pair((int)Qt::Key_Excel                  , \"Excel\"),\n        std::make_pair((int)Qt::Key_Explorer               , \"Explorer\"),\n        std::make_pair((int)Qt::Key_Game                   , \"Game\"),\n        std::make_pair((int)Qt::Key_Go                     , \"Go\"),\n        std::make_pair((int)Qt::Key_iTouch                 , \"iTouch\"),\n        std::make_pair((int)Qt::Key_LogOff                 , \"LogOff\"),\n        std::make_pair((int)Qt::Key_Market                 , \"Market\"),\n        std::make_pair((int)Qt::Key_Meeting                , \"Meeting\"),\n        std::make_pair((int)Qt::Key_MenuKB                 , \"MenuKB\"),\n        std::make_pair((int)Qt::Key_MenuPB                 , \"MenuPB\"),\n        std::make_pair((int)Qt::Key_MySites                , \"MySites\"),\n        std::make_pair((int)Qt::Key_News                   , \"News\"),\n        std::make_pair((int)Qt::Key_OfficeHome             , \"OfficeHome\"),\n        std::make_pair((int)Qt::Key_Option                 , \"Option\"),\n        std::make_pair((int)Qt::Key_Paste                  , \"Paste\"),\n        std::make_pair((int)Qt::Key_Phone                  , \"Phone\"),\n        std::make_pair((int)Qt::Key_Calendar               , \"Calendar\"),\n        std::make_pair((int)Qt::Key_Reply                  , \"Reply\"),\n        std::make_pair((int)Qt::Key_Reload                 , \"Reload\"),\n        std::make_pair((int)Qt::Key_RotateWindows          , \"RotateWindows\"),\n        std::make_pair((int)Qt::Key_RotationPB             , \"RotationPB\"),\n        std::make_pair((int)Qt::Key_RotationKB             , \"RotationKB\"),\n        std::make_pair((int)Qt::Key_Save                   , \"Save\"),\n        std::make_pair((int)Qt::Key_Send                   , \"Send\"),\n        std::make_pair((int)Qt::Key_Spell                  , \"Spell\"),\n        std::make_pair((int)Qt::Key_SplitScreen            , \"SplitScreen\"),\n        std::make_pair((int)Qt::Key_Support                , \"Support\"),\n        std::make_pair((int)Qt::Key_TaskPane               , \"TaskPane\"),\n        std::make_pair((int)Qt::Key_Terminal               , \"Terminal\"),\n        std::make_pair((int)Qt::Key_Tools                  , \"Tools\"),\n        std::make_pair((int)Qt::Key_Travel                 , \"Travel\"),\n        std::make_pair((int)Qt::Key_Video                  , \"Video\"),\n        std::make_pair((int)Qt::Key_Word                   , \"Word\"),\n        std::make_pair((int)Qt::Key_Xfer                   , \"Xfer\"),\n        std::make_pair((int)Qt::Key_ZoomIn                 , \"ZoomIn\"),\n        std::make_pair((int)Qt::Key_ZoomOut                , \"ZoomOut\"),\n        std::make_pair((int)Qt::Key_Away                   , \"Away\"),\n        std::make_pair((int)Qt::Key_Messenger              , \"Messenger\"),\n        std::make_pair((int)Qt::Key_WebCam                 , \"WebCam\"),\n        std::make_pair((int)Qt::Key_MailForward            , \"MailForward\"),\n        std::make_pair((int)Qt::Key_Pictures               , \"Pictures\"),\n        std::make_pair((int)Qt::Key_Music                  , \"Music\"),\n        std::make_pair((int)Qt::Key_Battery                , \"Battery\"),\n        std::make_pair((int)Qt::Key_Bluetooth              , \"Bluetooth\"),\n        std::make_pair((int)Qt::Key_WLAN                   , \"WLAN\"),\n        std::make_pair((int)Qt::Key_UWB                    , \"UWB\"),\n        std::make_pair((int)Qt::Key_AudioForward           , \"AudioForward\"),\n        std::make_pair((int)Qt::Key_AudioRepeat            , \"AudioRepeat\"),\n        std::make_pair((int)Qt::Key_AudioRandomPlay        , \"AudioRandomPlay\"),\n        std::make_pair((int)Qt::Key_Subtitle               , \"Subtitle\"),\n        std::make_pair((int)Qt::Key_AudioCycleTrack        , \"AudioCycleTrack\"),\n        std::make_pair((int)Qt::Key_Time                   , \"Time\"),\n        std::make_pair((int)Qt::Key_Hibernate              , \"Hibernate\"),\n        std::make_pair((int)Qt::Key_View                   , \"View\"),\n        std::make_pair((int)Qt::Key_TopMenu                , \"TopMenu\"),\n        std::make_pair((int)Qt::Key_PowerDown              , \"PowerDown\"),\n        std::make_pair((int)Qt::Key_Suspend                , \"Suspend\"),\n        std::make_pair((int)Qt::Key_ContrastAdjust         , \"ContrastAdjust\"),\n        std::make_pair((int)Qt::Key_LaunchG                , \"LaunchG\"),\n        std::make_pair((int)Qt::Key_LaunchH                , \"LaunchH\"),\n        std::make_pair((int)Qt::Key_TouchpadToggle         , \"TouchpadToggle\"),\n        std::make_pair((int)Qt::Key_TouchpadOn             , \"TouchpadOn\"),\n        std::make_pair((int)Qt::Key_TouchpadOff            , \"TouchpadOff\"),\n        std::make_pair((int)Qt::Key_MicMute                , \"MicMute\"),\n        std::make_pair((int)Qt::Key_Red                    , \"Red\"),\n        std::make_pair((int)Qt::Key_Green                  , \"Green\"),\n        std::make_pair((int)Qt::Key_Yellow                 , \"Yellow\"),\n        std::make_pair((int)Qt::Key_Blue                   , \"Blue\"),\n        std::make_pair((int)Qt::Key_ChannelUp              , \"ChannelUp\"),\n        std::make_pair((int)Qt::Key_ChannelDown            , \"ChannelDown\"),\n        std::make_pair((int)Qt::Key_Guide                  , \"Guide\"),\n        std::make_pair((int)Qt::Key_Info                   , \"Info\"),\n        std::make_pair((int)Qt::Key_Settings               , \"Settings\"),\n        std::make_pair((int)Qt::Key_MicVolumeUp            , \"MicVolumeUp\"),\n        std::make_pair((int)Qt::Key_MicVolumeDown          , \"MicVolumeDown\"),\n        std::make_pair((int)Qt::Key_New                    , \"New\"),\n        std::make_pair((int)Qt::Key_Open                   , \"Open\"),\n        std::make_pair((int)Qt::Key_Find                   , \"Find\"),\n        std::make_pair((int)Qt::Key_Undo                   , \"Undo\"),\n        std::make_pair((int)Qt::Key_Redo                   , \"Redo\"),\n        std::make_pair((int)Qt::Key_AltGr                  , \"AltGr\"),\n        std::make_pair((int)Qt::Key_Multi_key              , \"Multi_key\"),\n        std::make_pair((int)Qt::Key_Kanji                  , \"Kanji\"),\n        std::make_pair((int)Qt::Key_Muhenkan               , \"Muhenkan\"),\n        std::make_pair((int)Qt::Key_Henkan                 , \"Henkan\"),\n        std::make_pair((int)Qt::Key_Romaji                 , \"Romaji\"),\n        std::make_pair((int)Qt::Key_Hiragana               , \"Hiragana\"),\n        std::make_pair((int)Qt::Key_Katakana               , \"Katakana\"),\n        std::make_pair((int)Qt::Key_Hiragana_Katakana      , \"Hiragana_Katakana\"),\n        std::make_pair((int)Qt::Key_Zenkaku                , \"Zenkaku\"),\n        std::make_pair((int)Qt::Key_Hankaku                , \"Hankaku\"),\n        std::make_pair((int)Qt::Key_Zenkaku_Hankaku        , \"Zenkaku_Hankaku\"),\n        std::make_pair((int)Qt::Key_Touroku                , \"Touroku\"),\n        std::make_pair((int)Qt::Key_Massyo                 , \"Massyo\"),\n        std::make_pair((int)Qt::Key_Kana_Lock              , \"Kana_Lock\"),\n        std::make_pair((int)Qt::Key_Kana_Shift             , \"Kana_Shift\"),\n        std::make_pair((int)Qt::Key_Eisu_Shift             , \"Eisu_Shift\"),\n        std::make_pair((int)Qt::Key_Eisu_toggle            , \"Eisu_toggle\"),\n        std::make_pair((int)Qt::Key_Hangul                 , \"Hangul\"),\n        std::make_pair((int)Qt::Key_Hangul_Start           , \"Hangul_Start\"),\n        std::make_pair((int)Qt::Key_Hangul_End             , \"Hangul_End\"),\n        std::make_pair((int)Qt::Key_Hangul_Hanja           , \"Hangul_Hanja\"),\n        std::make_pair((int)Qt::Key_Hangul_Jamo            , \"Hangul_Jamo\"),\n        std::make_pair((int)Qt::Key_Hangul_Romaja          , \"Hangul_Romaja\"),\n        std::make_pair((int)Qt::Key_Codeinput              , \"Codeinput\"),\n        std::make_pair((int)Qt::Key_Hangul_Jeonja          , \"Hangul_Jeonja\"),\n        std::make_pair((int)Qt::Key_Hangul_Banja           , \"Hangul_Banja\"),\n        std::make_pair((int)Qt::Key_Hangul_PreHanja        , \"Hangul_PreHanja\"),\n        std::make_pair((int)Qt::Key_Hangul_PostHanja       , \"Hangul_PostHanja\"),\n        std::make_pair((int)Qt::Key_SingleCandidate        , \"SingleCandidate\"),\n        std::make_pair((int)Qt::Key_MultipleCandidate      , \"MultipleCandidate\"),\n        std::make_pair((int)Qt::Key_PreviousCandidate      , \"PreviousCandidate\"),\n        std::make_pair((int)Qt::Key_Hangul_Special         , \"Hangul_Special\"),\n        std::make_pair((int)Qt::Key_Mode_switch            , \"Mode_switch\"),\n        std::make_pair((int)Qt::Key_Dead_Grave             , \"Dead_Grave\"),\n        std::make_pair((int)Qt::Key_Dead_Acute             , \"Dead_Acute\"),\n        std::make_pair((int)Qt::Key_Dead_Circumflex        , \"Dead_Circumflex\"),\n        std::make_pair((int)Qt::Key_Dead_Tilde             , \"Dead_Tilde\"),\n        std::make_pair((int)Qt::Key_Dead_Macron            , \"Dead_Macron\"),\n        std::make_pair((int)Qt::Key_Dead_Breve             , \"Dead_Breve\"),\n        std::make_pair((int)Qt::Key_Dead_Abovedot          , \"Dead_Abovedot\"),\n        std::make_pair((int)Qt::Key_Dead_Diaeresis         , \"Dead_Diaeresis\"),\n        std::make_pair((int)Qt::Key_Dead_Abovering         , \"Dead_Abovering\"),\n        std::make_pair((int)Qt::Key_Dead_Doubleacute       , \"Dead_Doubleacute\"),\n        std::make_pair((int)Qt::Key_Dead_Caron             , \"Dead_Caron\"),\n        std::make_pair((int)Qt::Key_Dead_Cedilla           , \"Dead_Cedilla\"),\n        std::make_pair((int)Qt::Key_Dead_Ogonek            , \"Dead_Ogonek\"),\n        std::make_pair((int)Qt::Key_Dead_Iota              , \"Dead_Iota\"),\n        std::make_pair((int)Qt::Key_Dead_Voiced_Sound      , \"Dead_Voiced_Sound\"),\n        std::make_pair((int)Qt::Key_Dead_Semivoiced_Sound  , \"Dead_Semivoiced_Sound\"),\n        std::make_pair((int)Qt::Key_Dead_Belowdot          , \"Dead_Belowdot\"),\n        std::make_pair((int)Qt::Key_Dead_Hook              , \"Dead_Hook\"),\n        std::make_pair((int)Qt::Key_Dead_Horn              , \"Dead_Horn\"),\n        std::make_pair((int)Qt::Key_MediaLast              , \"MediaLast\"),\n        std::make_pair((int)Qt::Key_Select                 , \"Select\"),\n        std::make_pair((int)Qt::Key_Yes                    , \"Yes\"),\n        std::make_pair((int)Qt::Key_No                     , \"No\"),\n        std::make_pair((int)Qt::Key_Cancel                 , \"Cancel\"),\n        std::make_pair((int)Qt::Key_Printer                , \"Printer\"),\n        std::make_pair((int)Qt::Key_Execute                , \"Execute\"),\n        std::make_pair((int)Qt::Key_Sleep                  , \"Sleep\"),\n        std::make_pair((int)Qt::Key_Play                   , \"Play\"),\n        std::make_pair((int)Qt::Key_Zoom                   , \"Zoom\"),\n        std::make_pair((int)Qt::Key_Exit                   , \"Exit\"),\n        std::make_pair((int)Qt::Key_Context1               , \"Context1\"),\n        std::make_pair((int)Qt::Key_Context2               , \"Context2\"),\n        std::make_pair((int)Qt::Key_Context3               , \"Context3\"),\n        std::make_pair((int)Qt::Key_Context4               , \"Context4\"),\n        std::make_pair((int)Qt::Key_Call                   , \"Call\"),\n        std::make_pair((int)Qt::Key_Hangup                 , \"Hangup\"),\n        std::make_pair((int)Qt::Key_Flip                   , \"Flip\"),\n        std::make_pair((int)Qt::Key_ToggleCallHangup       , \"ToggleCallHangup\"),\n        std::make_pair((int)Qt::Key_VoiceDial              , \"VoiceDial\"),\n        std::make_pair((int)Qt::Key_LastNumberRedial       , \"LastNumberRedial\"),\n        std::make_pair((int)Qt::Key_Camera                 , \"Camera\"),\n        std::make_pair((int)Qt::Key_CameraFocus            , \"CameraFocus\"),\n        std::make_pair(0                                   , (const char*) nullptr)\n    };\n\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/shortcutmanager.hpp",
    "content": "#ifndef CSM_PREFS_SHORTCUTMANAGER_H\n#define CSM_PREFS_SHORTCUTMANAGER_H\n\n#include <map>\n\n#include <QKeySequence>\n#include <QObject>\n#include <QString>\n\nnamespace CSMPrefs\n{\n    class Shortcut;\n    class ShortcutEventHandler;\n\n    /// Class used to track and update shortcuts/sequences\n    class ShortcutManager : public QObject\n    {\n            Q_OBJECT\n\n        public:\n\n            ShortcutManager();\n\n            /// The shortcut class will do this automatically\n            void addShortcut(Shortcut* shortcut);\n\n            /// The shortcut class will do this automatically\n            void removeShortcut(Shortcut* shortcut);\n\n            bool getSequence(const std::string& name, QKeySequence& sequence) const;\n            void setSequence(const std::string& name, const QKeySequence& sequence);\n\n            bool getModifier(const std::string& name, int& modifier) const;\n            void setModifier(const std::string& name, int modifier);\n\n            std::string convertToString(const QKeySequence& sequence) const;\n            std::string convertToString(int modifier) const;\n\n            std::string convertToString(const QKeySequence& sequence, int modifier) const;\n\n            void convertFromString(const std::string& data, QKeySequence& sequence) const;\n            void convertFromString(const std::string& data, int& modifier) const;\n\n            void convertFromString(const std::string& data, QKeySequence& sequence, int& modifier) const;\n\n            /// Replaces \"{sequence-name}\" or \"{modifier-name}\" with the appropriate text\n            QString processToolTip(const QString& toolTip) const;\n\n        private:\n\n            // Need a multimap in case multiple shortcuts share the same name\n            typedef std::multimap<std::string, Shortcut*> ShortcutMap;\n            typedef std::map<std::string, QKeySequence> SequenceMap;\n            typedef std::map<std::string, int> ModifierMap;\n            typedef std::map<int, std::string> NameMap;\n            typedef std::map<std::string, int> KeyMap;\n\n            ShortcutMap mShortcuts;\n            SequenceMap mSequences;\n            ModifierMap mModifiers;\n\n            NameMap mNames;\n            KeyMap mKeys;\n\n            ShortcutEventHandler* mEventHandler;\n\n            void createLookupTables();\n\n            static const std::pair<int, const char*> QtKeys[];\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/shortcutsetting.cpp",
    "content": "#include \"shortcutsetting.hpp\"\n\n#include <QEvent>\n#include <QKeyEvent>\n#include <QLabel>\n#include <QMouseEvent>\n#include <QPushButton>\n#include <QWidget>\n#include <QString>\n\n#include \"state.hpp\"\n#include \"shortcutmanager.hpp\"\n\nnamespace CSMPrefs\n{\n    ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,\n        const std::string& label)\n        : Setting(parent, values, mutex, key, label)\n        , mButton(nullptr)\n        , mEditorActive(false)\n        , mEditorPos(0)\n    {\n        for (int i = 0; i < MaxKeys; ++i)\n        {\n            mEditorKeys[i] = 0;\n        }\n    }\n\n    std::pair<QWidget*, QWidget*> ShortcutSetting::makeWidgets(QWidget* parent)\n    {\n        QKeySequence sequence;\n        State::get().getShortcutManager().getSequence(getKey(), sequence);\n\n        QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(sequence).c_str());\n\n        QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);\n        QPushButton* widget = new QPushButton(text, parent);\n\n        widget->setCheckable(true);\n        widget->installEventFilter(this);\n\n        // right clicking on button sets shortcut to RMB, so context menu should not appear\n        widget->setContextMenuPolicy(Qt::PreventContextMenu);\n\n        mButton = widget;\n\n        connect(widget, SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool)));\n\n        return std::make_pair(label, widget);\n    }\n\n    void ShortcutSetting::updateWidget()\n    {\n        if (mButton)\n        {\n            std::string shortcut = getValues().getString(getKey(), getParent()->getKey());\n\n            QKeySequence sequence;\n            State::get().getShortcutManager().convertFromString(shortcut, sequence);\n            State::get().getShortcutManager().setSequence(getKey(), sequence);\n            resetState();\n        }\n    }\n\n    bool ShortcutSetting::eventFilter(QObject* target, QEvent* event)\n    {\n        if (event->type() == QEvent::KeyPress)\n        {\n            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);\n            if (keyEvent->isAutoRepeat())\n                return true;\n\n            int mod = keyEvent->modifiers();\n            int key = keyEvent->key();\n\n            return handleEvent(target, mod, key, true);\n        }\n        else if (event->type() == QEvent::KeyRelease)\n        {\n            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);\n            if (keyEvent->isAutoRepeat())\n                return true;\n\n            int mod = keyEvent->modifiers();\n            int key = keyEvent->key();\n\n            return handleEvent(target, mod, key, false);\n        }\n        else if (event->type() == QEvent::MouseButtonPress)\n        {\n            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);\n            int mod = mouseEvent->modifiers();\n            int key = mouseEvent->button();\n\n            return handleEvent(target, mod, key, true);\n        }\n        else if (event->type() == QEvent::MouseButtonRelease)\n        {\n            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);\n            int mod = mouseEvent->modifiers();\n            int key = mouseEvent->button();\n\n            return handleEvent(target, mod, key, false);\n        }\n        else if (event->type() == QEvent::FocusOut)\n        {\n            resetState();\n        }\n\n        return false;\n    }\n\n    bool ShortcutSetting::handleEvent(QObject* target, int mod, int value, bool active)\n    {\n        // Modifiers are handled differently\n        const int Blacklist[] =\n        {\n            Qt::Key_Shift,\n            Qt::Key_Control,\n            Qt::Key_Meta,\n            Qt::Key_Alt,\n            Qt::Key_AltGr\n        };\n\n        const size_t BlacklistSize = sizeof(Blacklist) / sizeof(int);\n\n        if (!mEditorActive)\n        {\n            if (value == Qt::RightButton && !active)\n            {\n                // Clear sequence\n                QKeySequence sequence = QKeySequence(0, 0, 0, 0);\n                storeValue(sequence);\n\n                resetState();\n            }\n\n            return false;\n        }\n\n        // Handle blacklist\n        for (size_t i = 0; i < BlacklistSize; ++i)\n        {\n            if (value == Blacklist[i])\n                return true;\n        }\n\n        if (!active || mEditorPos >= MaxKeys)\n        {\n            // Update key\n            QKeySequence sequence = QKeySequence(mEditorKeys[0], mEditorKeys[1], mEditorKeys[2], mEditorKeys[3]);\n            storeValue(sequence);\n\n            resetState();\n        }\n        else\n        {\n            if (mEditorPos == 0)\n            {\n                mEditorKeys[0] = mod | value;\n            }\n            else\n            {\n                mEditorKeys[mEditorPos] = value;\n            }\n\n            mEditorPos += 1;\n        }\n\n        return true;\n    }\n\n    void ShortcutSetting::storeValue(const QKeySequence& sequence)\n    {\n        State::get().getShortcutManager().setSequence(getKey(), sequence);\n\n        // Convert to string and assign\n        std::string value = State::get().getShortcutManager().convertToString(sequence);\n\n        {\n            QMutexLocker lock(getMutex());\n            getValues().setString(getKey(), getParent()->getKey(), value);\n        }\n\n        getParent()->getState()->update(*this);\n    }\n\n    void ShortcutSetting::resetState()\n    {\n        mButton->setChecked(false);\n        mEditorActive = false;\n        mEditorPos = 0;\n        for (int i = 0; i < MaxKeys; ++i)\n        {\n            mEditorKeys[i] = 0;\n        }\n\n        // Button text\n        QKeySequence sequence;\n        State::get().getShortcutManager().getSequence(getKey(), sequence);\n\n        QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(sequence).c_str());\n        mButton->setText(text);\n    }\n\n    void ShortcutSetting::buttonToggled(bool checked)\n    {\n        if (checked)\n            mButton->setText(\"Press keys or click here...\");\n\n        mEditorActive = checked;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/shortcutsetting.hpp",
    "content": "#ifndef CSM_PREFS_SHORTCUTSETTING_H\n#define CSM_PREFS_SHORTCUTSETTING_H\n\n#include <QKeySequence>\n\n#include \"setting.hpp\"\n\nclass QEvent;\nclass QPushButton;\n\nnamespace CSMPrefs\n{\n    class ShortcutSetting : public Setting\n    {\n            Q_OBJECT\n\n        public:\n\n            ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,\n                const std::string& label);\n\n            std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;\n\n            void updateWidget() override;\n\n        protected:\n\n            bool eventFilter(QObject* target, QEvent* event) override;\n\n        private:\n\n            bool handleEvent(QObject* target, int mod, int value, bool active);\n\n            void storeValue(const QKeySequence& sequence);\n            void resetState();\n\n            static constexpr int MaxKeys = 4;\n\n            QPushButton* mButton;\n\n            bool mEditorActive;\n            int mEditorPos;\n            int mEditorKeys[MaxKeys];\n\n        private slots:\n\n            void buttonToggled(bool checked);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/state.cpp",
    "content": "\n#include \"state.hpp\"\n\n#include <stdexcept>\n#include <algorithm>\n#include <sstream>\n\n#include \"intsetting.hpp\"\n#include \"doublesetting.hpp\"\n#include \"boolsetting.hpp\"\n#include \"coloursetting.hpp\"\n#include \"shortcutsetting.hpp\"\n#include \"modifiersetting.hpp\"\n\nCSMPrefs::State *CSMPrefs::State::sThis = nullptr;\n\nvoid CSMPrefs::State::load()\n{\n    // default settings file\n    boost::filesystem::path local = mConfigurationManager.getLocalPath() / mDefaultConfigFile;\n    boost::filesystem::path global = mConfigurationManager.getGlobalPath() / mDefaultConfigFile;\n\n    if (boost::filesystem::exists (local))\n        mSettings.loadDefault (local.string());\n    else if (boost::filesystem::exists (global))\n        mSettings.loadDefault (global.string());\n    else\n        throw std::runtime_error (\"No default settings file found! Make sure the file \\\"\" + mDefaultConfigFile + \"\\\" was properly installed.\");\n\n    // user settings file\n    boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile;\n\n    if (boost::filesystem::exists (user))\n        mSettings.loadUser (user.string());\n}\n\nvoid CSMPrefs::State::declare()\n{\n    declareCategory (\"Windows\");\n    declareInt (\"default-width\", \"Default window width\", 800).\n        setTooltip (\"Newly opened top-level windows will open with this width.\").\n        setMin (80);\n    declareInt (\"default-height\", \"Default window height\", 600).\n        setTooltip (\"Newly opened top-level windows will open with this height.\").\n        setMin (80);\n    declareBool (\"show-statusbar\", \"Show Status Bar\", true).\n        setTooltip (\"If a newly open top level window is showing status bars or not. \"\n        \" Note that this does not affect existing windows.\");\n    declareSeparator();\n    declareBool (\"reuse\", \"Reuse Subviews\", true).\n        setTooltip (\"When a new subview is requested and a matching subview already \"\n        \" exist, do not open a new subview and use the existing one instead.\");\n    declareInt (\"max-subviews\", \"Maximum number of subviews per top-level window\", 256).\n        setTooltip (\"If the maximum number is reached and a new subview is opened \"\n            \"it will be placed into a new top-level window.\").\n        setRange (1, 256);\n    declareBool (\"hide-subview\", \"Hide single subview\", false).\n        setTooltip (\"When a view contains only a single subview, hide the subview title \"\n        \"bar and if this subview is closed also close the view (unless it is the last \"\n        \"view for this document)\");\n    declareInt (\"minimum-width\", \"Minimum subview width\", 325).\n        setTooltip (\"Minimum width of subviews.\").\n        setRange (50, 10000);\n    declareSeparator();\n    EnumValue scrollbarOnly (\"Scrollbar Only\", \"Simple addition of scrollbars, the view window \"\n        \"does not grow automatically.\");\n    declareEnum (\"mainwindow-scrollbar\", \"Horizontal scrollbar mode for main window.\", scrollbarOnly).\n        addValue (scrollbarOnly).\n        addValue (\"Grow Only\", \"The view window grows as subviews are added. No scrollbars.\").\n        addValue (\"Grow then Scroll\", \"The view window grows. The scrollbar appears once it cannot grow any further.\");\n    declareBool (\"grow-limit\", \"Grow Limit Screen\", false).\n        setTooltip (\"When \\\"Grow then Scroll\\\" option is selected, the window size grows to\"\n        \" the width of the virtual desktop. \\nIf this option is selected the the window growth\"\n        \"is limited to the current screen.\");\n\n    declareCategory (\"Records\");\n    EnumValue iconAndText (\"Icon and Text\");\n    EnumValues recordValues;\n    recordValues.add (iconAndText).add (\"Icon Only\").add (\"Text Only\");\n    declareEnum (\"status-format\", \"Modification status display format\", iconAndText).\n        addValues (recordValues);\n    declareEnum (\"type-format\", \"ID type display format\", iconAndText).\n        addValues (recordValues);\n\n    declareCategory (\"ID Tables\");\n    EnumValue inPlaceEdit (\"Edit in Place\", \"Edit the clicked cell\");\n    EnumValue editRecord (\"Edit Record\", \"Open a dialogue subview for the clicked record\");\n    EnumValue view (\"View\", \"Open a scene subview for the clicked record (not available everywhere)\");\n    EnumValue editRecordAndClose (\"Edit Record and Close\");\n    EnumValues doubleClickValues;\n    doubleClickValues.add (inPlaceEdit).add (editRecord).add (view).add (\"Revert\").\n        add (\"Delete\").add (editRecordAndClose).\n        add (\"View and Close\", \"Open a scene subview for the clicked record and close the table subview\");\n    declareEnum (\"double\", \"Double Click\", inPlaceEdit).addValues (doubleClickValues);\n    declareEnum (\"double-s\", \"Shift Double Click\", editRecord).addValues (doubleClickValues);\n    declareEnum (\"double-c\", \"Control Double Click\", view).addValues (doubleClickValues);\n    declareEnum (\"double-sc\", \"Shift Control Double Click\", editRecordAndClose).addValues (doubleClickValues);\n    declareSeparator();\n    EnumValue jumpAndSelect (\"Jump and Select\", \"Scroll new record into view and make it the selection\");\n    declareEnum (\"jump-to-added\", \"Action on adding or cloning a record\", jumpAndSelect).\n        addValue (jumpAndSelect).\n        addValue (\"Jump Only\", \"Scroll new record into view\").\n        addValue (\"No Jump\", \"No special action\");\n    declareBool (\"extended-config\",\n        \"Manually specify affected record types for an extended delete/revert\", false).\n        setTooltip (\"Delete and revert commands have an extended form that also affects \"\n        \"associated records.\\n\\n\"\n        \"If this option is enabled, types of affected records are selected \"\n        \"manually before a command execution.\\nOtherwise, all associated \"\n        \"records are deleted/reverted immediately.\");\n\n    declareCategory (\"ID Dialogues\");\n    declareBool (\"toolbar\", \"Show toolbar\", true);\n\n    declareCategory (\"Reports\");\n    EnumValue actionNone (\"None\");\n    EnumValue actionEdit (\"Edit\", \"Open a table or dialogue suitable for addressing the listed report\");\n    EnumValue actionRemove (\"Remove\", \"Remove the report from the report table\");\n    EnumValue actionEditAndRemove (\"Edit And Remove\", \"Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table\");\n    EnumValues reportValues;\n    reportValues.add (actionNone).add (actionEdit).add (actionRemove).add (actionEditAndRemove);\n    declareEnum (\"double\", \"Double Click\", actionEdit).addValues (reportValues);\n    declareEnum (\"double-s\", \"Shift Double Click\", actionRemove).addValues (reportValues);\n    declareEnum (\"double-c\", \"Control Double Click\", actionEditAndRemove).addValues (reportValues);\n    declareEnum (\"double-sc\", \"Shift Control Double Click\", actionNone).addValues (reportValues);\n    declareBool(\"ignore-base-records\", \"Ignore base records in verifier\", false);\n\n    declareCategory (\"Search & Replace\");\n    declareInt (\"char-before\", \"Characters before search string\", 10).\n        setTooltip (\"Maximum number of character to display in search result before the searched text\");\n    declareInt (\"char-after\", \"Characters after search string\", 10).\n        setTooltip (\"Maximum number of character to display in search result after the searched text\");\n    declareBool (\"auto-delete\", \"Delete row from result table after a successful replace\", true);\n\n    declareCategory (\"Scripts\");\n    declareBool (\"show-linenum\", \"Show Line Numbers\", true).\n        setTooltip (\"Show line numbers to the left of the script editor window.\"\n        \"The current row and column numbers of the text cursor are shown at the bottom.\");\n    declareBool (\"wrap-lines\", \"Wrap Lines\", false).\n        setTooltip (\"Wrap lines longer than width of script editor.\");\n    declareBool (\"mono-font\", \"Use monospace font\", true);\n    declareInt (\"tab-width\", \"Tab Width\", 4).\n        setTooltip (\"Number of characters for tab width\").\n        setRange (1, 10);\n    EnumValue warningsNormal (\"Normal\", \"Report warnings as warning\");\n    declareEnum (\"warnings\", \"Warning Mode\", warningsNormal).\n        addValue (\"Ignore\", \"Do not report warning\").\n        addValue (warningsNormal).\n        addValue (\"Strict\", \"Promote warning to an error\");\n    declareBool (\"toolbar\", \"Show toolbar\", true);\n    declareInt (\"compile-delay\", \"Delay between updating of source errors\", 100).\n        setTooltip (\"Delay in milliseconds\").\n        setRange (0, 10000);\n    declareInt (\"error-height\", \"Initial height of the error panel\", 100).\n        setRange (100, 10000);\n    declareBool (\"highlight-occurrences\", \"Highlight other occurrences of selected names\", true);\n    declareColour (\"colour-highlight\", \"Colour of highlighted occurrences\", QColor(\"lightcyan\"));\n    declareSeparator();\n    declareColour (\"colour-int\", \"Highlight Colour: Integer Literals\", QColor (\"darkmagenta\"));\n    declareColour (\"colour-float\", \"Highlight Colour: Float Literals\", QColor (\"magenta\"));\n    declareColour (\"colour-name\", \"Highlight Colour: Names\", QColor (\"grey\"));\n    declareColour (\"colour-keyword\", \"Highlight Colour: Keywords\", QColor (\"red\"));\n    declareColour (\"colour-special\", \"Highlight Colour: Special Characters\", QColor (\"darkorange\"));\n    declareColour (\"colour-comment\", \"Highlight Colour: Comments\", QColor (\"green\"));\n    declareColour (\"colour-id\", \"Highlight Colour: IDs\", QColor (\"blue\"));\n\n    declareCategory (\"General Input\");\n    declareBool (\"cycle\", \"Cyclic next/previous\", false).\n        setTooltip (\"When using next/previous functions at the last/first item of a \"\n        \"list go to the first/last item\");\n\n    declareCategory (\"3D Scene Input\");\n\n    declareDouble (\"navi-wheel-factor\", \"Camera Zoom Sensitivity\", 8).setRange(-100.0, 100.0);\n    declareDouble (\"s-navi-sensitivity\", \"Secondary Camera Movement Sensitivity\", 50.0).setRange(-1000.0, 1000.0);\n    declareSeparator();\n\n    declareDouble (\"p-navi-free-sensitivity\", \"Free Camera Sensitivity\", 1/650.).setPrecision(5).setRange(0.0, 1.0);\n    declareBool (\"p-navi-free-invert\", \"Invert Free Camera Mouse Input\", false);\n    declareDouble (\"navi-free-lin-speed\", \"Free Camera Linear Speed\", 1000.0).setRange(1.0, 10000.0);\n    declareDouble (\"navi-free-rot-speed\", \"Free Camera Rotational Speed\", 3.14 / 2).setRange(0.001, 6.28);\n    declareDouble (\"navi-free-speed-mult\", \"Free Camera Speed Multiplier (from Modifier)\", 8).setRange(0.001, 1000.0);\n    declareSeparator();\n\n    declareDouble (\"p-navi-orbit-sensitivity\", \"Orbit Camera Sensitivity\", 1/650.).setPrecision(5).setRange(0.0, 1.0);\n    declareBool (\"p-navi-orbit-invert\", \"Invert Orbit Camera Mouse Input\", false);\n    declareDouble (\"navi-orbit-rot-speed\", \"Orbital Camera Rotational Speed\", 3.14 / 4).setRange(0.001, 6.28);\n    declareDouble (\"navi-orbit-speed-mult\", \"Orbital Camera Speed Multiplier (from Modifier)\", 4).setRange(0.001, 1000.0);\n    declareBool (\"navi-orbit-const-roll\", \"Keep camera roll constant for orbital camera\", true);\n    declareSeparator();\n\n    declareBool (\"context-select\", \"Context Sensitive Selection\", false);\n    declareDouble (\"drag-factor\", \"Mouse sensitivity during drag operations\", 1.0).\n        setRange (0.001, 100.0);\n    declareDouble (\"drag-wheel-factor\", \"Mouse wheel sensitivity during drag operations\", 1.0).\n        setRange (0.001, 100.0);\n    declareDouble (\"drag-shift-factor\",\n            \"Shift-acceleration factor during drag operations\", 4.0).\n        setTooltip (\"Acceleration factor during drag operations while holding down shift\").\n        setRange (0.001, 100.0);\n    declareDouble (\"rotate-factor\", \"Free rotation factor\", 0.007).setPrecision(4).setRange(0.0001, 0.1);\n\n    declareCategory (\"Rendering\");\n    declareInt (\"framerate-limit\", \"FPS limit\", 60).\n        setTooltip(\"Framerate limit in 3D preview windows. Zero value means \\\"unlimited\\\".\").\n        setRange(0, 10000);\n    declareInt (\"camera-fov\", \"Camera FOV\", 90).setRange(10, 170);\n    declareBool (\"camera-ortho\", \"Orthographic projection for camera\", false);\n    declareInt (\"camera-ortho-size\", \"Orthographic projection size parameter\", 100).\n        setTooltip(\"Size of the orthographic frustum, greater value will allow the camera to see more of the world.\").\n        setRange(10, 10000);\n    declareDouble (\"object-marker-alpha\", \"Object Marker Transparency\", 0.5).setPrecision(2).setRange(0,1);\n    declareBool(\"scene-use-gradient\", \"Use Gradient Background\", true);\n    declareColour (\"scene-day-background-colour\", \"Day Background Colour\", QColor (110, 120, 128, 255));\n    declareColour (\"scene-day-gradient-colour\", \"Day Gradient  Colour\", QColor (47, 51, 51, 255)).\n        setTooltip(\"Sets the gradient color to use in conjunction with the day background color. Ignored if \"\n                   \"the gradient option is disabled.\");\n    declareColour (\"scene-bright-background-colour\", \"Scene Bright Background Colour\", QColor (79, 87, 92, 255));\n    declareColour (\"scene-bright-gradient-colour\", \"Scene Bright Gradient Colour\", QColor (47, 51, 51, 255)).\n        setTooltip(\"Sets the gradient color to use in conjunction with the bright background color. Ignored if \"\n            \"the gradient option is disabled.\");\n    declareColour (\"scene-night-background-colour\", \"Scene Night Background Colour\", QColor (64, 77, 79, 255));\n    declareColour (\"scene-night-gradient-colour\", \"Scene Night Gradient Colour\", QColor (47, 51, 51, 255)).\n        setTooltip(\"Sets the gradient color to use in conjunction with the night background color. Ignored if \"\n            \"the gradient option is disabled.\");\n\n    declareCategory (\"Tooltips\");\n    declareBool (\"scene\", \"Show Tooltips in 3D scenes\", true);\n    declareBool (\"scene-hide-basic\", \"Hide basic  3D scenes tooltips\", false);\n    declareInt (\"scene-delay\", \"Tooltip delay in milliseconds\", 500).\n        setMin (1);\n\n    EnumValue createAndInsert (\"Create cell and insert\");\n    EnumValue showAndInsert (\"Show cell and insert\");\n    EnumValue dontInsert (\"Discard\");\n    EnumValue insertAnyway (\"Insert anyway\");\n    EnumValues insertOutsideCell;\n    insertOutsideCell.add (createAndInsert).add (dontInsert).add (insertAnyway);\n    EnumValues insertOutsideVisibleCell;\n    insertOutsideVisibleCell.add (showAndInsert).add (dontInsert).add (insertAnyway);\n\n    EnumValue createAndLandEdit (\"Create cell and land, then edit\");\n    EnumValue showAndLandEdit (\"Show cell and edit\");\n    EnumValue dontLandEdit (\"Discard\");\n    EnumValues landeditOutsideCell;\n    landeditOutsideCell.add (createAndLandEdit).add (dontLandEdit);\n    EnumValues landeditOutsideVisibleCell;\n    landeditOutsideVisibleCell.add (showAndLandEdit).add (dontLandEdit);\n\n    EnumValue SelectOnly (\"Select only\");\n    EnumValue SelectAdd (\"Add to selection\");\n    EnumValue SelectRemove (\"Remove from selection\");\n    EnumValue selectInvert (\"Invert selection\");\n    EnumValues primarySelectAction;\n    primarySelectAction.add (SelectOnly).add (SelectAdd).add (SelectRemove).add (selectInvert);\n    EnumValues secondarySelectAction;\n    secondarySelectAction.add (SelectOnly).add (SelectAdd).add (SelectRemove).add (selectInvert);\n\n    declareCategory (\"3D Scene Editing\");\n    declareInt (\"distance\", \"Drop Distance\", 50).\n        setTooltip (\"If an instance drop can not be placed against another object at the \"\n            \"insert point, it will be placed by this distance from the insert point instead\");\n    declareEnum (\"outside-drop\", \"Handling drops outside of cells\", createAndInsert).\n        addValues (insertOutsideCell);\n    declareEnum (\"outside-visible-drop\", \"Handling drops outside of visible cells\", showAndInsert).\n        addValues (insertOutsideVisibleCell);\n    declareEnum (\"outside-landedit\", \"Handling terrain edit outside of cells\", createAndLandEdit).\n        setTooltip(\"Behavior of terrain editing, if land editing brush reaches an area without cell record.\").\n        addValues (landeditOutsideCell);\n    declareEnum (\"outside-visible-landedit\", \"Handling terrain edit outside of visible cells\", showAndLandEdit).\n        setTooltip(\"Behavior of terrain editing, if land editing brush reaches an area that is not currently visible.\").\n        addValues (landeditOutsideVisibleCell);\n    declareInt (\"texturebrush-maximumsize\", \"Maximum texture brush size\", 50).\n        setMin (1);\n    declareInt (\"shapebrush-maximumsize\", \"Maximum height edit brush size\", 100).\n        setTooltip(\"Setting for the slider range of brush size in terrain height editing.\").\n        setMin (1);\n    declareBool (\"landedit-post-smoothpainting\", \"Smooth land after painting height\", false).\n        setTooltip(\"Raise and lower tools will leave bumpy finish without this option\");\n    declareDouble (\"landedit-post-smoothstrength\", \"Smoothing strength (post-edit)\", 0.25).\n        setTooltip(\"If smoothing land after painting height is used, this is the percentage of smooth applied afterwards. \"\n        \"Negative values may be used to roughen instead of smooth.\").\n        setMin (-1).\n        setMax (1);\n    declareBool (\"open-list-view\", \"Open displays list view\", false).\n        setTooltip (\"When opening a reference from the scene view, it will open the\"\n        \" instance list view instead of the individual instance record view.\");\n    declareEnum (\"primary-select-action\", \"Action for primary select\", SelectOnly).\n        setTooltip(\"Selection can be chosen between select only, add to selection, remove from selection and invert selection.\").\n        addValues (primarySelectAction);\n    declareEnum (\"secondary-select-action\", \"Action for secondary select\", SelectAdd).\n        setTooltip(\"Selection can be chosen between select only, add to selection, remove from selection and invert selection.\").\n        addValues (secondarySelectAction);\n\n    declareCategory (\"Key Bindings\");\n\n    declareSubcategory (\"Document\");\n    declareShortcut (\"document-file-newgame\", \"New Game\", QKeySequence(Qt::ControlModifier | Qt::Key_N));\n    declareShortcut (\"document-file-newaddon\", \"New Addon\", QKeySequence());\n    declareShortcut (\"document-file-open\", \"Open\", QKeySequence(Qt::ControlModifier | Qt::Key_O));\n    declareShortcut (\"document-file-save\", \"Save\", QKeySequence(Qt::ControlModifier | Qt::Key_S));\n    declareShortcut (\"document-help-help\", \"Help\", QKeySequence(Qt::Key_F1));\n    declareShortcut (\"document-help-tutorial\", \"Tutorial\", QKeySequence());\n    declareShortcut (\"document-file-verify\", \"Verify\", QKeySequence());\n    declareShortcut (\"document-file-merge\", \"Merge\", QKeySequence());\n    declareShortcut (\"document-file-errorlog\", \"Open Load Error Log\", QKeySequence());\n    declareShortcut (\"document-file-metadata\", \"Meta Data\", QKeySequence());\n    declareShortcut (\"document-file-close\", \"Close Document\", QKeySequence(Qt::ControlModifier | Qt::Key_W));\n    declareShortcut (\"document-file-exit\", \"Exit Application\", QKeySequence(Qt::ControlModifier | Qt::Key_Q));\n    declareShortcut (\"document-edit-undo\", \"Undo\", QKeySequence(Qt::ControlModifier | Qt::Key_Z));\n    declareShortcut (\"document-edit-redo\", \"Redo\", QKeySequence(Qt::ControlModifier | Qt::ShiftModifier | Qt::Key_Z));\n    declareShortcut (\"document-edit-preferences\", \"Open Preferences\", QKeySequence());\n    declareShortcut (\"document-edit-search\", \"Search\", QKeySequence(Qt::ControlModifier | Qt::Key_F));\n    declareShortcut (\"document-view-newview\", \"New View\", QKeySequence());\n    declareShortcut (\"document-view-statusbar\", \"Toggle Status Bar\", QKeySequence());\n    declareShortcut (\"document-view-filters\", \"Open Filter List\", QKeySequence());\n    declareShortcut (\"document-world-regions\", \"Open Region List\", QKeySequence());\n    declareShortcut (\"document-world-cells\", \"Open Cell List\", QKeySequence());\n    declareShortcut (\"document-world-referencables\", \"Open Object List\", QKeySequence());\n    declareShortcut (\"document-world-references\", \"Open Instance List\", QKeySequence());\n    declareShortcut (\"document-world-lands\", \"Open Lands List\", QKeySequence());\n    declareShortcut (\"document-world-landtextures\", \"Open Land Textures List\", QKeySequence());\n    declareShortcut (\"document-world-pathgrid\", \"Open Pathgrid List\", QKeySequence());\n    declareShortcut (\"document-world-regionmap\", \"Open Region Map\", QKeySequence());\n    declareShortcut (\"document-mechanics-globals\", \"Open Global List\", QKeySequence());\n    declareShortcut (\"document-mechanics-gamesettings\", \"Open Game Settings\", QKeySequence());\n    declareShortcut (\"document-mechanics-scripts\", \"Open Script List\", QKeySequence());\n    declareShortcut (\"document-mechanics-spells\", \"Open Spell List\", QKeySequence());\n    declareShortcut (\"document-mechanics-enchantments\", \"Open Enchantment List\", QKeySequence());\n    declareShortcut (\"document-mechanics-magiceffects\", \"Open Magic Effect List\", QKeySequence());\n    declareShortcut (\"document-mechanics-startscripts\", \"Open Start Script List\", QKeySequence());\n    declareShortcut (\"document-character-skills\", \"Open Skill List\", QKeySequence());\n    declareShortcut (\"document-character-classes\", \"Open Class List\", QKeySequence());\n    declareShortcut (\"document-character-factions\", \"Open Faction List\", QKeySequence());\n    declareShortcut (\"document-character-races\", \"Open Race List\", QKeySequence());\n    declareShortcut (\"document-character-birthsigns\", \"Open Birthsign List\", QKeySequence());\n    declareShortcut (\"document-character-topics\", \"Open Topic List\", QKeySequence());\n    declareShortcut (\"document-character-journals\", \"Open Journal List\", QKeySequence());\n    declareShortcut (\"document-character-topicinfos\", \"Open Topic Info List\", QKeySequence());\n    declareShortcut (\"document-character-journalinfos\", \"Open Journal Info List\", QKeySequence());\n    declareShortcut (\"document-character-bodyparts\", \"Open Body Part List\", QKeySequence());\n    declareShortcut (\"document-assets-reload\", \"Reload Assets\", QKeySequence(Qt::Key_F5));\n    declareShortcut (\"document-assets-sounds\", \"Open Sound Asset List\", QKeySequence());\n    declareShortcut (\"document-assets-soundgens\", \"Open Sound Generator List\", QKeySequence());\n    declareShortcut (\"document-assets-meshes\", \"Open Mesh Asset List\", QKeySequence());\n    declareShortcut (\"document-assets-icons\", \"Open Icon Asset List\", QKeySequence());\n    declareShortcut (\"document-assets-music\", \"Open Music Asset List\", QKeySequence());\n    declareShortcut (\"document-assets-soundres\", \"Open Sound File List\", QKeySequence());\n    declareShortcut (\"document-assets-textures\", \"Open Texture Asset List\", QKeySequence());\n    declareShortcut (\"document-assets-videos\", \"Open Video Asset List\", QKeySequence());\n    declareShortcut (\"document-debug-run\", \"Run Debug\", QKeySequence());\n    declareShortcut (\"document-debug-shutdown\", \"Stop Debug\", QKeySequence());\n    declareShortcut (\"document-debug-profiles\", \"Debug Profiles\", QKeySequence());\n    declareShortcut (\"document-debug-runlog\", \"Open Run Log\", QKeySequence());\n\n    declareSubcategory (\"Table\");\n    declareShortcut (\"table-edit\", \"Edit Record\", QKeySequence());\n    declareShortcut (\"table-add\", \"Add Row/Record\", QKeySequence(Qt::ShiftModifier | Qt::Key_A));\n    declareShortcut (\"table-clone\", \"Clone Record\", QKeySequence(Qt::ShiftModifier | Qt::Key_D));\n    declareShortcut (\"touch-record\", \"Touch Record\", QKeySequence());\n    declareShortcut (\"table-revert\", \"Revert Record\", QKeySequence());\n    declareShortcut (\"table-remove\", \"Remove Row/Record\", QKeySequence(Qt::Key_Delete));\n    declareShortcut (\"table-moveup\", \"Move Record Up\", QKeySequence());\n    declareShortcut (\"table-movedown\", \"Move Record Down\", QKeySequence());\n    declareShortcut (\"table-view\", \"View Record\", QKeySequence(Qt::ShiftModifier | Qt::Key_C));\n    declareShortcut (\"table-preview\", \"Preview Record\", QKeySequence(Qt::ShiftModifier | Qt::Key_V));\n    declareShortcut (\"table-extendeddelete\", \"Extended Record Deletion\", QKeySequence());\n    declareShortcut (\"table-extendedrevert\", \"Extended Record Revertion\", QKeySequence());\n\n    declareSubcategory (\"Report Table\");\n    declareShortcut (\"reporttable-show\", \"Show Report\", QKeySequence());\n    declareShortcut (\"reporttable-remove\", \"Remove Report\", QKeySequence(Qt::Key_Delete));\n    declareShortcut (\"reporttable-replace\", \"Replace Report\", QKeySequence());\n    declareShortcut (\"reporttable-refresh\", \"Refresh Report\", QKeySequence());\n\n    declareSubcategory (\"Scene\");\n    declareShortcut (\"scene-navi-primary\", \"Camera Rotation From Mouse Movement\", QKeySequence(Qt::LeftButton));\n    declareShortcut (\"scene-navi-secondary\", \"Camera Translation From Mouse Movement\",\n        QKeySequence(Qt::ControlModifier | (int)Qt::LeftButton));\n    declareShortcut (\"scene-open-primary\", \"Primary Open\", QKeySequence(Qt::ShiftModifier | (int)Qt::LeftButton));\n    declareShortcut (\"scene-edit-primary\", \"Primary Edit\", QKeySequence(Qt::RightButton));\n    declareShortcut (\"scene-edit-secondary\", \"Secondary Edit\",\n        QKeySequence(Qt::ControlModifier | (int)Qt::RightButton));\n    declareShortcut (\"scene-select-primary\", \"Primary Select\", QKeySequence(Qt::MiddleButton));\n    declareShortcut (\"scene-select-secondary\", \"Secondary Select\",\n        QKeySequence(Qt::ControlModifier | (int)Qt::MiddleButton));\n    declareModifier (\"scene-speed-modifier\", \"Speed Modifier\", Qt::Key_Shift);\n    declareShortcut (\"scene-delete\", \"Delete Instance\", QKeySequence(Qt::Key_Delete));\n    declareShortcut (\"scene-instance-drop-terrain\", \"Drop to terrain level\", QKeySequence(Qt::Key_G));\n    declareShortcut (\"scene-instance-drop-collision\", \"Drop to collision\", QKeySequence(Qt::Key_H));\n    declareShortcut (\"scene-instance-drop-terrain-separately\", \"Drop to terrain level separately\", QKeySequence());\n    declareShortcut (\"scene-instance-drop-collision-separately\", \"Drop to collision separately\", QKeySequence());\n    declareShortcut (\"scene-load-cam-cell\", \"Load Camera Cell\", QKeySequence(Qt::KeypadModifier | Qt::Key_5));\n    declareShortcut (\"scene-load-cam-eastcell\", \"Load East Cell\", QKeySequence(Qt::KeypadModifier | Qt::Key_6));\n    declareShortcut (\"scene-load-cam-northcell\", \"Load North Cell\", QKeySequence(Qt::KeypadModifier | Qt::Key_8));\n    declareShortcut (\"scene-load-cam-westcell\", \"Load West Cell\", QKeySequence(Qt::KeypadModifier | Qt::Key_4));\n    declareShortcut (\"scene-load-cam-southcell\", \"Load South Cell\", QKeySequence(Qt::KeypadModifier | Qt::Key_2));\n    declareShortcut (\"scene-edit-abort\", \"Abort\", QKeySequence(Qt::Key_Escape));\n    declareShortcut (\"scene-focus-toolbar\", \"Toggle Toolbar Focus\", QKeySequence(Qt::Key_T));\n    declareShortcut (\"scene-render-stats\", \"Debug Rendering Stats\", QKeySequence(Qt::Key_F3));\n\n    declareSubcategory (\"1st/Free Camera\");\n    declareShortcut (\"free-forward\", \"Forward\", QKeySequence(Qt::Key_W));\n    declareShortcut (\"free-backward\", \"Backward\", QKeySequence(Qt::Key_S));\n    declareShortcut (\"free-left\", \"Left\", QKeySequence(Qt::Key_A));\n    declareShortcut (\"free-right\", \"Right\", QKeySequence(Qt::Key_D));\n    declareShortcut (\"free-roll-left\", \"Roll Left\", QKeySequence(Qt::Key_Q));\n    declareShortcut (\"free-roll-right\", \"Roll Right\", QKeySequence(Qt::Key_E));\n    declareShortcut (\"free-speed-mode\", \"Toggle Speed Mode\", QKeySequence(Qt::Key_F));\n\n    declareSubcategory (\"Orbit Camera\");\n    declareShortcut (\"orbit-up\", \"Up\", QKeySequence(Qt::Key_W));\n    declareShortcut (\"orbit-down\", \"Down\", QKeySequence(Qt::Key_S));\n    declareShortcut (\"orbit-left\", \"Left\", QKeySequence(Qt::Key_A));\n    declareShortcut (\"orbit-right\", \"Right\", QKeySequence(Qt::Key_D));\n    declareShortcut (\"orbit-roll-left\", \"Roll Left\", QKeySequence(Qt::Key_Q));\n    declareShortcut (\"orbit-roll-right\", \"Roll Right\", QKeySequence(Qt::Key_E));\n    declareShortcut (\"orbit-speed-mode\", \"Toggle Speed Mode\", QKeySequence(Qt::Key_F));\n    declareShortcut (\"orbit-center-selection\", \"Center On Selected\", QKeySequence(Qt::Key_C));\n\n    declareSubcategory (\"Script Editor\");\n    declareShortcut (\"script-editor-comment\", \"Comment Selection\", QKeySequence());\n    declareShortcut (\"script-editor-uncomment\", \"Uncomment Selection\", QKeySequence());\n\n    declareCategory (\"Models\");\n    declareString (\"baseanim\", \"base animations\", \"meshes/base_anim.nif\").\n        setTooltip(\"3rd person base model with textkeys-data\");\n    declareString (\"baseanimkna\", \"base animations, kna\", \"meshes/base_animkna.nif\").\n        setTooltip(\"3rd person beast race base model with textkeys-data\");\n    declareString (\"baseanimfemale\", \"base animations, female\", \"meshes/base_anim_female.nif\").\n        setTooltip(\"3rd person female base model with textkeys-data\");\n    declareString (\"wolfskin\", \"base animations, wolf\", \"meshes/wolf/skin.nif\").\n        setTooltip(\"3rd person werewolf skin\");\n}\n\nvoid CSMPrefs::State::declareCategory (const std::string& key)\n{\n    std::map<std::string, Category>::iterator iter = mCategories.find (key);\n\n    if (iter!=mCategories.end())\n    {\n        mCurrentCategory = iter;\n    }\n    else\n    {\n        mCurrentCategory =\n            mCategories.insert (std::make_pair (key, Category (this, key))).first;\n    }\n}\n\nCSMPrefs::IntSetting& CSMPrefs::State::declareInt (const std::string& key,\n    const std::string& label, int default_)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    setDefault(key, std::to_string(default_));\n\n    default_ = mSettings.getInt (key, mCurrentCategory->second.getKey());\n\n    CSMPrefs::IntSetting *setting =\n        new CSMPrefs::IntSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,\n        default_);\n\n    mCurrentCategory->second.addSetting (setting);\n\n    return *setting;\n}\n\nCSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble (const std::string& key,\n    const std::string& label, double default_)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    std::ostringstream stream;\n    stream << default_;\n    setDefault(key, stream.str());\n\n    default_ = mSettings.getFloat (key, mCurrentCategory->second.getKey());\n\n    CSMPrefs::DoubleSetting *setting =\n        new CSMPrefs::DoubleSetting (&mCurrentCategory->second, &mSettings, &mMutex,\n        key, label, default_);\n\n    mCurrentCategory->second.addSetting (setting);\n\n    return *setting;\n}\n\nCSMPrefs::BoolSetting& CSMPrefs::State::declareBool (const std::string& key,\n    const std::string& label, bool default_)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    setDefault (key, default_ ? \"true\" : \"false\");\n\n    default_ = mSettings.getBool (key, mCurrentCategory->second.getKey());\n\n    CSMPrefs::BoolSetting *setting =\n        new CSMPrefs::BoolSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,\n        default_);\n\n    mCurrentCategory->second.addSetting (setting);\n\n    return *setting;\n}\n\nCSMPrefs::EnumSetting& CSMPrefs::State::declareEnum (const std::string& key,\n    const std::string& label, EnumValue default_)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    setDefault (key, default_.mValue);\n\n    default_.mValue = mSettings.getString (key, mCurrentCategory->second.getKey());\n\n    CSMPrefs::EnumSetting *setting =\n        new CSMPrefs::EnumSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,\n        default_);\n\n    mCurrentCategory->second.addSetting (setting);\n\n    return *setting;\n}\n\nCSMPrefs::ColourSetting& CSMPrefs::State::declareColour (const std::string& key,\n    const std::string& label, QColor default_)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    setDefault (key, default_.name().toUtf8().data());\n\n    default_.setNamedColor (QString::fromUtf8 (mSettings.getString (key, mCurrentCategory->second.getKey()).c_str()));\n\n    CSMPrefs::ColourSetting *setting =\n        new CSMPrefs::ColourSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,\n        default_);\n\n    mCurrentCategory->second.addSetting (setting);\n\n    return *setting;\n}\n\nCSMPrefs::ShortcutSetting& CSMPrefs::State::declareShortcut (const std::string& key, const std::string& label,\n    const QKeySequence& default_)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    std::string seqStr = getShortcutManager().convertToString(default_);\n    setDefault (key, seqStr);\n\n    // Setup with actual data\n    QKeySequence sequence;\n\n    getShortcutManager().convertFromString(mSettings.getString(key, mCurrentCategory->second.getKey()), sequence);\n    getShortcutManager().setSequence(key, sequence);\n\n    CSMPrefs::ShortcutSetting *setting = new CSMPrefs::ShortcutSetting (&mCurrentCategory->second, &mSettings, &mMutex,\n        key, label);\n    mCurrentCategory->second.addSetting (setting);\n\n    return *setting;\n}\n\nCSMPrefs::StringSetting& CSMPrefs::State::declareString (const std::string& key, const std::string& label, std::string default_)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    setDefault (key, default_);\n\n    default_ = mSettings.getString (key, mCurrentCategory->second.getKey());\n\n    CSMPrefs::StringSetting *setting =\n        new CSMPrefs::StringSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,\n        default_);\n\n    mCurrentCategory->second.addSetting (setting);\n\n    return *setting;\n}\n\nCSMPrefs::ModifierSetting& CSMPrefs::State::declareModifier(const std::string& key, const std::string& label,\n    int default_)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    std::string modStr = getShortcutManager().convertToString(default_);\n    setDefault (key, modStr);\n\n    // Setup with actual data\n    int modifier;\n\n    getShortcutManager().convertFromString(mSettings.getString(key, mCurrentCategory->second.getKey()), modifier);\n    getShortcutManager().setModifier(key, modifier);\n\n    CSMPrefs::ModifierSetting *setting = new CSMPrefs::ModifierSetting (&mCurrentCategory->second, &mSettings, &mMutex,\n        key, label);\n    mCurrentCategory->second.addSetting (setting);\n\n    return *setting;\n}\n\nvoid CSMPrefs::State::declareSeparator()\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    CSMPrefs::Setting *setting =\n        new CSMPrefs::Setting (&mCurrentCategory->second, &mSettings, &mMutex, \"\", \"\");\n\n    mCurrentCategory->second.addSetting (setting);\n}\n\nvoid CSMPrefs::State::declareSubcategory(const std::string& label)\n{\n    if (mCurrentCategory==mCategories.end())\n        throw std::logic_error (\"no category for setting\");\n\n    CSMPrefs::Setting *setting =\n        new CSMPrefs::Setting (&mCurrentCategory->second, &mSettings, &mMutex, \"\", label);\n\n    mCurrentCategory->second.addSetting (setting);\n}\n\nvoid CSMPrefs::State::setDefault (const std::string& key, const std::string& default_)\n{\n    Settings::CategorySetting fullKey (mCurrentCategory->second.getKey(), key);\n\n    Settings::CategorySettingValueMap::iterator iter =\n        mSettings.mDefaultSettings.find (fullKey);\n\n    if (iter==mSettings.mDefaultSettings.end())\n        mSettings.mDefaultSettings.insert (std::make_pair (fullKey, default_));\n}\n\nCSMPrefs::State::State (const Files::ConfigurationManager& configurationManager)\n: mConfigFile (\"openmw-cs.cfg\"), mDefaultConfigFile(\"defaults-cs.bin\"), mConfigurationManager (configurationManager),\n  mCurrentCategory (mCategories.end())\n{\n    if (sThis)\n        throw std::logic_error (\"An instance of CSMPRefs::State already exists\");\n\n    sThis = this;\n\n    load();\n    declare();\n}\n\nCSMPrefs::State::~State()\n{\n    sThis = nullptr;\n}\n\nvoid CSMPrefs::State::save()\n{\n    boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile;\n    mSettings.saveUser (user.string());\n}\n\nCSMPrefs::State::Iterator CSMPrefs::State::begin()\n{\n    return mCategories.begin();\n}\n\nCSMPrefs::State::Iterator CSMPrefs::State::end()\n{\n    return mCategories.end();\n}\n\nCSMPrefs::ShortcutManager& CSMPrefs::State::getShortcutManager()\n{\n    return mShortcutManager;\n}\n\nCSMPrefs::Category& CSMPrefs::State::operator[] (const std::string& key)\n{\n    Iterator iter = mCategories.find (key);\n\n    if (iter==mCategories.end())\n        throw std::logic_error (\"Invalid user settings category: \" + key);\n\n    return iter->second;\n}\n\nvoid CSMPrefs::State::update (const Setting& setting)\n{\n    emit (settingChanged (&setting));\n}\n\nCSMPrefs::State& CSMPrefs::State::get()\n{\n    if (!sThis)\n        throw std::logic_error (\"No instance of CSMPrefs::State\");\n\n    return *sThis;\n}\n\nvoid CSMPrefs::State::resetCategory(const std::string& category)\n{\n    for (Settings::CategorySettingValueMap::iterator i = mSettings.mUserSettings.begin();\n         i != mSettings.mUserSettings.end(); ++i)\n    {\n        // if the category matches\n        if (i->first.first == category)\n        {\n            // mark the setting as changed\n            mSettings.mChangedSettings.insert(std::make_pair(i->first.first, i->first.second));\n            // reset the value to the default\n            i->second = mSettings.mDefaultSettings[i->first];\n        }\n    }\n\n    Collection::iterator container = mCategories.find(category);\n    if (container != mCategories.end())\n    {\n        Category settings = container->second;\n        for (Category::Iterator i = settings.begin(); i != settings.end(); ++i)\n        {\n            (*i)->updateWidget();\n            update(**i);\n        }\n    }\n}\n\nvoid CSMPrefs::State::resetAll()\n{\n    for (Collection::iterator iter = mCategories.begin(); iter != mCategories.end(); ++iter)\n    {\n        resetCategory(iter->first);\n    }\n}\n\n\nCSMPrefs::State& CSMPrefs::get()\n{\n    return State::get();\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/state.hpp",
    "content": "#ifndef CSM_PREFS_STATE_H\n#define CSM_PREFS_STATE_H\n\n#include <map>\n#include <string>\n\n#include <QObject>\n#include <QMutex>\n\n#ifndef Q_MOC_RUN\n#include <components/files/configurationmanager.hpp>\n#endif\n\n#include <components/settings/settings.hpp>\n\n#include \"category.hpp\"\n#include \"setting.hpp\"\n#include \"enumsetting.hpp\"\n#include \"stringsetting.hpp\"\n#include \"shortcutmanager.hpp\"\n\nclass QColor;\n\nnamespace CSMPrefs\n{\n    class IntSetting;\n    class DoubleSetting;\n    class BoolSetting;\n    class ColourSetting;\n    class ShortcutSetting;\n    class ModifierSetting;\n\n    /// \\brief User settings state\n    ///\n    /// \\note Access to the user settings is thread-safe once all declarations and loading has\n    /// been completed.\n    class State : public QObject\n    {\n            Q_OBJECT\n\n            static State *sThis;\n\n        public:\n\n            typedef std::map<std::string, Category> Collection;\n            typedef Collection::iterator Iterator;\n\n        private:\n\n            const std::string mConfigFile;\n            const std::string mDefaultConfigFile;\n            const Files::ConfigurationManager& mConfigurationManager;\n            ShortcutManager mShortcutManager;\n            Settings::Manager mSettings;\n            Collection mCategories;\n            Iterator mCurrentCategory;\n            QMutex mMutex;\n\n            // not implemented\n            State (const State&);\n            State& operator= (const State&);\n\n        private:\n\n            void load();\n\n            void declare();\n\n            void declareCategory (const std::string& key);\n\n            IntSetting& declareInt (const std::string& key, const std::string& label, int default_);\n            DoubleSetting& declareDouble (const std::string& key, const std::string& label, double default_);\n\n            BoolSetting& declareBool (const std::string& key, const std::string& label, bool default_);\n\n            EnumSetting& declareEnum (const std::string& key, const std::string& label, EnumValue default_);\n\n            ColourSetting& declareColour (const std::string& key, const std::string& label, QColor default_);\n\n            ShortcutSetting& declareShortcut (const std::string& key, const std::string& label,\n                const QKeySequence& default_);\n\n            StringSetting& declareString (const std::string& key, const std::string& label, std::string default_);\n\n            ModifierSetting& declareModifier(const std::string& key, const std::string& label, int modifier_);\n\n            void declareSeparator();\n\n            void declareSubcategory(const std::string& label);\n\n            void setDefault (const std::string& key, const std::string& default_);\n\n        public:\n\n            State (const Files::ConfigurationManager& configurationManager);\n\n            ~State();\n\n            void save();\n\n            Iterator begin();\n\n            Iterator end();\n\n            ShortcutManager& getShortcutManager();\n\n            Category& operator[](const std::string& key);\n\n            void update (const Setting& setting);\n\n            static State& get();\n\n            void resetCategory(const std::string& category);\n\n            void resetAll();\n\n        signals:\n\n            void settingChanged (const CSMPrefs::Setting *setting);\n    };\n\n    // convenience function\n    State& get();\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/prefs/stringsetting.cpp",
    "content": "\n#include \"stringsetting.hpp\"\n\n#include <QLineEdit>\n#include <QMutexLocker>\n\n#include <components/settings/settings.hpp>\n\n#include \"category.hpp\"\n#include \"state.hpp\"\n\nCSMPrefs::StringSetting::StringSetting (Category *parent, Settings::Manager *values,\n  QMutex *mutex, const std::string& key, const std::string& label, std::string default_)\n: Setting (parent, values, mutex, key, label),  mDefault (default_), mWidget(nullptr)\n{}\n\nCSMPrefs::StringSetting& CSMPrefs::StringSetting::setTooltip (const std::string& tooltip)\n{\n    mTooltip = tooltip;\n    return *this;\n}\n\nstd::pair<QWidget *, QWidget *> CSMPrefs::StringSetting::makeWidgets (QWidget *parent)\n{\n    mWidget = new QLineEdit (QString::fromUtf8 (mDefault.c_str()), parent);\n\n    if (!mTooltip.empty())\n    {\n        QString tooltip = QString::fromUtf8 (mTooltip.c_str());\n        mWidget->setToolTip (tooltip);\n    }\n\n    connect (mWidget, SIGNAL (textChanged (QString)), this, SLOT (textChanged (QString)));\n\n    return std::make_pair (static_cast<QWidget *> (nullptr), mWidget);\n}\n\nvoid CSMPrefs::StringSetting::updateWidget()\n{\n    if (mWidget)\n    {\n        mWidget->setText(QString::fromStdString(getValues().getString(getKey(), getParent()->getKey())));\n    }\n}\n\nvoid CSMPrefs::StringSetting::textChanged (const QString& text)\n{\n    {\n        QMutexLocker lock (getMutex());\n        getValues().setString (getKey(), getParent()->getKey(), text.toStdString());\n    }\n\n    getParent()->getState()->update (*this);\n}\n"
  },
  {
    "path": "apps/opencs/model/prefs/stringsetting.hpp",
    "content": "#ifndef CSM_PREFS_StringSetting_H\n#define CSM_PREFS_StringSetting_H\n\n#include \"setting.hpp\"\n\nclass QLineEdit;\n\nnamespace CSMPrefs\n{\n    class StringSetting : public Setting\n    {\n            Q_OBJECT\n\n            std::string mTooltip;\n            std::string mDefault;\n            QLineEdit* mWidget;\n\n        public:\n\n            StringSetting (Category *parent, Settings::Manager *values,\n                QMutex *mutex, const std::string& key, const std::string& label, std::string default_);\n\n            StringSetting& setTooltip (const std::string& tooltip);\n\n            /// Return label, input widget.\n            std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent) override;\n\n            void updateWidget() override;\n\n        private slots:\n\n            void textChanged (const QString& text);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/birthsigncheck.cpp",
    "content": "#include \"birthsigncheck.hpp\"\n\n#include <components/misc/resourcehelpers.hpp>\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nCSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns,\n                                                    const CSMWorld::Resources &textures)\n: mBirthsigns(birthsigns),\n  mTextures(textures)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::BirthsignCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mBirthsigns.getSize();\n}\n\nvoid CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::BirthSign>& record = mBirthsigns.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::BirthSign& birthsign = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId);\n\n    if (birthsign.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (birthsign.mDescription.empty())\n        messages.add(id, \"Description is missing\", \"\", CSMDoc::Message::Severity_Warning);\n\n    if (birthsign.mTexture.empty())\n        messages.add(id, \"Image is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mTextures.searchId(birthsign.mTexture) == -1)\n    {\n        std::string ddsTexture = birthsign.mTexture;\n        if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1))\n            messages.add(id,  \"Image '\" + birthsign.mTexture + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    /// \\todo check data members that can't be edited in the table view\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/birthsigncheck.hpp",
    "content": "#ifndef CSM_TOOLS_BIRTHSIGNCHECK_H\n#define CSM_TOOLS_BIRTHSIGNCHECK_H\n\n#include <components/esm/loadbsgn.hpp>\n\n#include \"../world/idcollection.hpp\"\n#include \"../world/resources.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that birthsign records are internally consistent\n    class BirthsignCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::BirthSign> &mBirthsigns;\n            const CSMWorld::Resources &mTextures;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign> &birthsigns,\n                                 const CSMWorld::Resources &textures);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/bodypartcheck.cpp",
    "content": "#include \"bodypartcheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\nCSMTools::BodyPartCheckStage::BodyPartCheckStage(\n        const CSMWorld::IdCollection<ESM::BodyPart> &bodyParts,\n        const CSMWorld::Resources                   &meshes,\n        const CSMWorld::IdCollection<ESM::Race>     &races ) :\n    mBodyParts(bodyParts),\n    mMeshes(meshes),\n    mRaces(races)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::BodyPartCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mBodyParts.getSize();\n}\n\nvoid CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &messages)\n{\n    const CSMWorld::Record<ESM::BodyPart> &record = mBodyParts.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::BodyPart &bodyPart = record.get();\n\n    CSMWorld::UniversalId id( CSMWorld::UniversalId::Type_BodyPart, bodyPart.mId );\n\n    // Check BYDT\n    if (bodyPart.mData.mPart >= ESM::BodyPart::MP_Count )\n        messages.add(id, \"Invalid part\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (bodyPart.mData.mType > ESM::BodyPart::MT_Armor )\n        messages.add(id, \"Invalid type\", \"\", CSMDoc::Message::Severity_Error);\n\n    // Check MODL\n    if ( bodyPart.mModel.empty() )\n        messages.add(id, \"Model is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if ( mMeshes.searchId( bodyPart.mModel ) == -1 )\n        messages.add(id, \"Model '\" + bodyPart.mModel + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    // Check FNAM for skin body parts (for non-skin body parts it's meaningless)\n    if ( bodyPart.mData.mType == ESM::BodyPart::MT_Skin )\n    {\n        if ( bodyPart.mRace.empty() )\n            messages.add(id, \"Race is missing\", \"\", CSMDoc::Message::Severity_Error);\n        else if ( mRaces.searchId( bodyPart.mRace ) == -1 )\n            messages.add(id, \"Race '\" + bodyPart.mRace + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/bodypartcheck.hpp",
    "content": "#ifndef CSM_TOOLS_BODYPARTCHECK_H\n#define CSM_TOOLS_BODYPARTCHECK_H\n\n#include <components/esm/loadbody.hpp>\n#include <components/esm/loadrace.hpp>\n\n#include \"../world/resources.hpp\"\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that body part records are internally consistent\n    class BodyPartCheckStage : public CSMDoc::Stage\n    {\n        const CSMWorld::IdCollection<ESM::BodyPart> &mBodyParts;\n        const CSMWorld::Resources                   &mMeshes;\n        const CSMWorld::IdCollection<ESM::Race>     &mRaces;\n        bool                                        mIgnoreBaseRecords;\n\n    public:\n        BodyPartCheckStage(\n            const CSMWorld::IdCollection<ESM::BodyPart> &bodyParts,\n            const CSMWorld::Resources                   &meshes,\n            const CSMWorld::IdCollection<ESM::Race>     &races );\n\n        int setup() override;\n        ///< \\return number of steps\n\n        void perform(int stage, CSMDoc::Messages &messages) override;\n        ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/classcheck.cpp",
    "content": "#include \"classcheck.hpp\"\n\n#include <map>\n\n#include <components/esm/loadclas.hpp>\n#include <components/esm/loadskil.hpp>\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nCSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes)\n: mClasses (classes)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::ClassCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mClasses.getSize();\n}\n\nvoid CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Class>& record = mClasses.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::Class& class_ = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId);\n\n    // A class should have a name\n    if (class_.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    // A playable class should have a description\n    if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty())\n        messages.add(id, \"Description of a playable class is missing\", \"\", CSMDoc::Message::Severity_Warning);\n\n    // test for invalid attributes\n    for (int i=0; i<2; ++i)\n        if (class_.mData.mAttribute[i]==-1)\n        {\n            messages.add(id, \"Attribute #\" + std::to_string(i) + \" is not set\", \"\", CSMDoc::Message::Severity_Error);\n        }\n\n    if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1)\n    {\n        messages.add(id, \"Same attribute is listed twice\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    // test for non-unique skill\n    std::map<int, int> skills; // ID, number of occurrences\n\n    for (int i=0; i<5; ++i)\n        for (int i2=0; i2<2; ++i2)\n            ++skills[class_.mData.mSkills[i][i2]];\n\n    for (auto &skill : skills)\n        if (skill.second>1)\n        {\n            messages.add(id, \"Skill \" + ESM::Skill::indexToId (skill.first) + \" is listed more than once\", \"\", CSMDoc::Message::Severity_Error);\n        }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/classcheck.hpp",
    "content": "#ifndef CSM_TOOLS_CLASSCHECK_H\n#define CSM_TOOLS_CLASSCHECK_H\n\n#include <components/esm/loadclas.hpp>\n\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that class records are internally consistent\n    class ClassCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::Class>& mClasses;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/enchantmentcheck.cpp",
    "content": "#include \"enchantmentcheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nCSMTools::EnchantmentCheckStage::EnchantmentCheckStage (const CSMWorld::IdCollection<ESM::Enchantment>& enchantments)\n    : mEnchantments (enchantments)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::EnchantmentCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mEnchantments.getSize();\n}\n\nvoid CSMTools::EnchantmentCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Enchantment>& record = mEnchantments.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::Enchantment& enchantment = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Enchantment, enchantment.mId);\n\n    if (enchantment.mData.mType < 0 || enchantment.mData.mType > 3)\n        messages.add(id, \"Invalid type\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (enchantment.mData.mCost < 0)\n        messages.add(id, \"Cost is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (enchantment.mData.mCharge < 0)\n        messages.add(id, \"Charge is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (enchantment.mData.mCost > enchantment.mData.mCharge)\n        messages.add(id, \"Cost is higher than charge\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (enchantment.mEffects.mList.empty())\n    {\n        messages.add(id, \"Enchantment doesn't have any magic effects\", \"\", CSMDoc::Message::Severity_Warning);\n    }\n    else\n    {\n        std::vector<ESM::ENAMstruct>::const_iterator effect = enchantment.mEffects.mList.begin();\n\n        for (size_t i = 1; i <= enchantment.mEffects.mList.size(); i++)\n        {\n            const std::string number = std::to_string(i);\n            // At the time of writing this effects, attributes and skills are hardcoded\n            if (effect->mEffectID < 0 || effect->mEffectID > 142)\n            {\n                messages.add(id, \"Effect #\" + number + \" is invalid\", \"\", CSMDoc::Message::Severity_Error);\n                ++effect;\n                continue;\n            }\n\n            if (effect->mSkill < -1 || effect->mSkill > 26)\n                messages.add(id, \"Effect #\" + number + \" affected skill is invalid\", \"\", CSMDoc::Message::Severity_Error);\n            if (effect->mAttribute < -1 || effect->mAttribute > 7)\n                messages.add(id, \"Effect #\" + number + \" affected attribute is invalid\", \"\", CSMDoc::Message::Severity_Error);\n            if (effect->mRange < 0 || effect->mRange > 2)\n                messages.add(id, \"Effect #\" + number + \" range is invalid\", \"\", CSMDoc::Message::Severity_Error);\n            if (effect->mArea < 0)\n                messages.add(id, \"Effect #\" + number + \" area is negative\", \"\", CSMDoc::Message::Severity_Error);\n            if (effect->mDuration < 0)\n                messages.add(id, \"Effect #\" + number + \" duration is negative\", \"\", CSMDoc::Message::Severity_Error);\n            if (effect->mMagnMin < 0)\n                messages.add(id, \"Effect #\" + number + \" minimum magnitude is negative\", \"\", CSMDoc::Message::Severity_Error);\n            if (effect->mMagnMax < 0)\n                messages.add(id, \"Effect #\" + number + \" maximum magnitude is negative\", \"\", CSMDoc::Message::Severity_Error);\n            if (effect->mMagnMin > effect->mMagnMax)\n                messages.add(id, \"Effect #\" + number + \" minimum magnitude is higher than maximum magnitude\", \"\", CSMDoc::Message::Severity_Error);\n            ++effect;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/enchantmentcheck.hpp",
    "content": "#ifndef CSM_TOOLS_ENCHANTMENTCHECK_H\n#define CSM_TOOLS_ENCHANTMENTCHECK_H\n\n#include <components/esm/loadench.hpp>\n\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief Make sure that enchantment records are correct\n    class EnchantmentCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::Enchantment>& mEnchantments;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            EnchantmentCheckStage (const CSMWorld::IdCollection<ESM::Enchantment>& enchantments);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/factioncheck.cpp",
    "content": "#include \"factioncheck.hpp\"\n\n#include <map>\n\n#include <components/esm/loadskil.hpp>\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nCSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions)\n: mFactions (factions)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::FactionCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mFactions.getSize();\n}\n\nvoid CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Faction>& record = mFactions.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::Faction& faction = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId);\n\n    // test for empty name\n    if (faction.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    // test for invalid attributes\n    if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1)\n    {\n        messages.add(id, \"Same attribute is listed twice\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    // test for non-unique skill\n    std::map<int, int> skills; // ID, number of occurrences\n\n    for (int i=0; i<7; ++i)\n        if (faction.mData.mSkills[i]!=-1)\n            ++skills[faction.mData.mSkills[i]];\n\n    for (auto &skill : skills)\n        if (skill.second>1)\n        {\n            messages.add(id, \"Skill \" + ESM::Skill::indexToId (skill.first) + \" is listed more than once\", \"\", CSMDoc::Message::Severity_Error);\n        }\n\n    /// \\todo check data members that can't be edited in the table view\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/factioncheck.hpp",
    "content": "#ifndef CSM_TOOLS_FACTIONCHECK_H\n#define CSM_TOOLS_FACTIONCHECK_H\n\n#include <components/esm/loadfact.hpp>\n\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that faction records are internally consistent\n    class FactionCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::Faction>& mFactions;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/gmstcheck.cpp",
    "content": "#include \"gmstcheck.hpp\"\n\n#include <sstream>\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/defaultgmsts.hpp\"\n\nCSMTools::GmstCheckStage::GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings)\n    : mGameSettings(gameSettings)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::GmstCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mGameSettings.getSize();\n}\n\nvoid CSMTools::GmstCheckStage::perform(int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::GameSetting>& record = mGameSettings.getRecord (stage);\n    \n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n    \n    const ESM::GameSetting& gmst = record.get();\n    \n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Gmst, gmst.mId);\n    \n    // Test for empty string\n    if (gmst.mValue.getType() == ESM::VT_String && gmst.mValue.getString().empty())\n        messages.add(id, gmst.mId + \" is an empty string\", \"\", CSMDoc::Message::Severity_Warning);\n    \n    // Checking type and limits\n    // optimization - compare it to lists based on naming convention (f-float,i-int,s-string)\n    if (gmst.mId[0] == 'f')\n    {\n        for (size_t i = 0; i < CSMWorld::DefaultGmsts::FloatCount; ++i)\n        {\n            if (gmst.mId == CSMWorld::DefaultGmsts::Floats[i])\n            {\n                if (gmst.mValue.getType() != ESM::VT_Float)\n                {\n                    std::ostringstream stream;\n                    stream << \"Expected float type for \" << gmst.mId << \" but found \"\n                        << varTypeToString(gmst.mValue.getType()) << \" type\";\n                    \n                    messages.add(id, stream.str(), \"\", CSMDoc::Message::Severity_Error);\n                }\n                \n                if (gmst.mValue.getFloat() < CSMWorld::DefaultGmsts::FloatLimits[i*2])\n                    messages.add(id, gmst.mId + \" is less than the suggested range\", \"\",\n                                 CSMDoc::Message::Severity_Warning);\n                \n                if (gmst.mValue.getFloat() > CSMWorld::DefaultGmsts::FloatLimits[i*2+1])\n                    messages.add(id, gmst.mId + \" is more than the suggested range\", \"\",\n                                 CSMDoc::Message::Severity_Warning);\n                \n                break; // for loop\n            }\n        }\n    }\n    else if (gmst.mId[0] == 'i')\n    {\n        for (size_t i = 0; i < CSMWorld::DefaultGmsts::IntCount; ++i)\n        {   \n            if (gmst.mId == CSMWorld::DefaultGmsts::Ints[i])\n            {\n                if (gmst.mValue.getType() != ESM::VT_Int)\n                {\n                    std::ostringstream stream;\n                    stream << \"Expected int type for \" << gmst.mId << \" but found \"\n                        << varTypeToString(gmst.mValue.getType()) << \" type\";\n                    \n                    messages.add(id, stream.str(), \"\", CSMDoc::Message::Severity_Error);\n                }\n                \n                if (gmst.mValue.getInteger() < CSMWorld::DefaultGmsts::IntLimits[i*2])\n                    messages.add(id, gmst.mId + \" is less than the suggested range\", \"\",\n                                 CSMDoc::Message::Severity_Warning);\n                \n                if (gmst.mValue.getInteger() > CSMWorld::DefaultGmsts::IntLimits[i*2+1])\n                    messages.add(id, gmst.mId + \" is more than the suggested range\", \"\",\n                                 CSMDoc::Message::Severity_Warning);\n                \n                break; // for loop\n            }\n        }\n    }\n    else if (gmst.mId[0] == 's')\n    {\n        for (size_t i = 0; i < CSMWorld::DefaultGmsts::StringCount; ++i)\n        {\n            if (gmst.mId == CSMWorld::DefaultGmsts::Strings[i])\n            {\n                ESM::VarType type = gmst.mValue.getType();\n                \n                if (type != ESM::VT_String && type != ESM::VT_None)\n                {\n                    std::ostringstream stream;\n                    stream << \"Expected string or none type for \" << gmst.mId << \" but found \"\n                        << varTypeToString(gmst.mValue.getType()) << \" type\";\n                    \n                    messages.add(id, stream.str(), \"\", CSMDoc::Message::Severity_Error);\n                }\n                \n                break; // for loop\n            }\n        }\n    }\n}\n\nstd::string CSMTools::GmstCheckStage::varTypeToString(ESM::VarType type)\n{\n    switch (type)\n    {\n        case ESM::VT_Unknown: return \"unknown\";\n        case ESM::VT_None: return \"none\";\n        case ESM::VT_Short: return \"short\";\n        case ESM::VT_Int: return \"int\";\n        case ESM::VT_Long: return \"long\";\n        case ESM::VT_Float: return \"float\";\n        case ESM::VT_String: return \"string\";\n        default: return \"unhandled\";\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/gmstcheck.hpp",
    "content": "#ifndef CSM_TOOLS_GMSTCHECK_H\n#define CSM_TOOLS_GMSTCHECK_H\n\n#include <components/esm/loadgmst.hpp>\n\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that GMSTs are alright\n    class GmstCheckStage : public CSMDoc::Stage\n    {\n    public:\n        \n        GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings);\n        \n        int setup() override;\n        ///< \\return number of steps\n\n        void perform(int stage, CSMDoc::Messages& messages) override;\n        ///< Messages resulting from this stage will be appended to \\a messages\n        \n    private:\n        \n        const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;\n        bool mIgnoreBaseRecords;\n        \n        std::string varTypeToString(ESM::VarType);\n        \n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/journalcheck.cpp",
    "content": "#include \"journalcheck.hpp\"\n\n#include <set>\n\n#include \"../prefs/state.hpp\"\n\nCSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals,\n    const CSMWorld::InfoCollection& journalInfos)\n    : mJournals(journals), mJournalInfos(journalInfos)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::JournalCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mJournals.getSize();\n}\n\nvoid CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && journalRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || journalRecord.isDeleted())\n        return;\n\n    const ESM::Dialogue &journal = journalRecord.get();\n    int statusNamedCount = 0;\n    int totalInfoCount = 0;\n    std::set<int> questIndices;\n\n    CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId);\n\n    for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it)\n    {\n        const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it);\n\n        if (infoRecord.isDeleted())\n            continue;\n\n        const CSMWorld::Info& journalInfo = infoRecord.get();\n\n        totalInfoCount += 1;\n\n        if (journalInfo.mQuestStatus == ESM::DialInfo::QS_Name)\n        {\n            statusNamedCount += 1;\n        }\n\n        // Skip \"Base\" records (setting!)\n        if (mIgnoreBaseRecords && infoRecord.mState == CSMWorld::RecordBase::State_BaseOnly)\n            continue;\n\n        if (journalInfo.mResponse.empty())\n        {\n            CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);\n            messages.add(id, \"Missing journal entry text\", \"\", CSMDoc::Message::Severity_Warning);\n        }\n\n        std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);\n\n        // Duplicate index\n        if (!result.second)\n        {\n            CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);\n            messages.add(id, \"Duplicated quest index \" + std::to_string(journalInfo.mData.mJournalIndex), \"\", CSMDoc::Message::Severity_Error);\n        }\n    }\n\n    if (totalInfoCount == 0)\n    {\n        CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);\n        messages.add(id, \"No related journal entry\", \"\", CSMDoc::Message::Severity_Warning);\n    }\n    else if (statusNamedCount > 1)\n    {\n        CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);\n        messages.add(id, \"Multiple entries with quest status 'Named'\", \"\", CSMDoc::Message::Severity_Error);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/journalcheck.hpp",
    "content": "#ifndef CSM_TOOLS_JOURNALCHECK_H\n#define CSM_TOOLS_JOURNALCHECK_H\n\n#include <components/esm/loaddial.hpp>\n\n#include \"../world/idcollection.hpp\"\n#include \"../world/infocollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that journal infos are good\n    class JournalCheckStage : public CSMDoc::Stage\n    {\n    public:\n\n        JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue>& journals,\n            const CSMWorld::InfoCollection& journalInfos);\n\n        int setup() override;\n        ///< \\return number of steps\n\n        void perform(int stage, CSMDoc::Messages& messages) override;\n        ///< Messages resulting from this stage will be appended to \\a messages\n\n    private:\n\n        const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;\n        const CSMWorld::InfoCollection& mJournalInfos;\n        bool mIgnoreBaseRecords;\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/magiceffectcheck.cpp",
    "content": "#include \"magiceffectcheck.hpp\"\n\n#include <components/misc/resourcehelpers.hpp>\n\n#include \"../prefs/state.hpp\"\n\nstd::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, \n                                                                const CSMWorld::UniversalId &type, \n                                                                const std::string &column) const\n{\n    CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id);\n    if (index.first == -1) \n        return (column + \" '\" + id + \"' does not exist\");\n    else if (index.second != type.getType()) \n        return (column + \" '\" + id + \"' does not have \" + type.getTypeName() + \" type\");\n    return std::string();\n}\n\nCSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,\n                                                       const CSMWorld::IdCollection<ESM::Sound> &sounds,\n                                                       const CSMWorld::RefIdCollection &objects,\n                                                       const CSMWorld::Resources &icons,\n                                                       const CSMWorld::Resources &textures)\n    : mMagicEffects(effects),\n      mSounds(sounds),\n      mObjects(objects),\n      mIcons(icons),\n      mTextures(textures)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::MagicEffectCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mMagicEffects.getSize();\n}\n\nvoid CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages)\n{\n    const CSMWorld::Record<ESM::MagicEffect> &record = mMagicEffects.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    ESM::MagicEffect effect = record.get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);\n\n    if (effect.mDescription.empty())\n    {\n        messages.add(id, \"Description is missing\", \"\", CSMDoc::Message::Severity_Warning);\n    }\n\n    if (effect.mData.mBaseCost < 0.0f)\n    {\n        messages.add(id, \"Base cost is negative\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (effect.mIcon.empty())\n    {\n        messages.add(id, \"Icon is missing\", \"\", CSMDoc::Message::Severity_Error);\n    }\n    else\n    {\n        if (mIcons.searchId(effect.mIcon) == -1)\n        {\n            std::string ddsIcon = effect.mIcon;\n            if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))\n                messages.add(id, \"Icon '\" + effect.mIcon + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        }\n    }\n\n    if (!effect.mParticle.empty())\n    {\n        if (mTextures.searchId(effect.mParticle) == -1)\n        {\n            std::string ddsParticle = effect.mParticle;\n            if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsParticle) && mTextures.searchId(ddsParticle) != -1))\n                messages.add(id, \"Particle texture '\" + effect.mParticle + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        }\n    }\n\n    if (!effect.mCasting.empty())\n    {\n        const std::string error = checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, \"Casting object\");\n        if (!error.empty())\n            messages.add(id, error, \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (!effect.mHit.empty())\n    {\n        const std::string error = checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, \"Hit object\");\n        if (!error.empty())\n            messages.add(id, error, \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (!effect.mArea.empty())\n    {\n        const std::string error = checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, \"Area object\");\n        if (!error.empty())\n            messages.add(id, error, \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (!effect.mBolt.empty())\n    {\n        const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, \"Bolt object\");\n        if (!error.empty())\n            messages.add(id, error, \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (!effect.mCastSound.empty() && mSounds.searchId(effect.mCastSound) == -1)\n        messages.add(id, \"Casting sound '\" + effect.mCastSound + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    if (!effect.mHitSound.empty() && mSounds.searchId(effect.mHitSound) == -1)\n        messages.add(id, \"Hit sound '\" + effect.mHitSound + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    if (!effect.mAreaSound.empty() && mSounds.searchId(effect.mAreaSound) == -1)\n        messages.add(id, \"Area sound '\" + effect.mAreaSound + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    if (!effect.mBoltSound.empty() && mSounds.searchId(effect.mBoltSound) == -1)\n        messages.add(id, \"Bolt sound '\" + effect.mBoltSound + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/magiceffectcheck.hpp",
    "content": "#ifndef CSM_TOOLS_MAGICEFFECTCHECK_HPP\n#define CSM_TOOLS_MAGICEFFECTCHECK_HPP\n\n#include <components/esm/loadmgef.hpp>\n#include <components/esm/loadsoun.hpp>\n\n#include \"../world/idcollection.hpp\"\n#include \"../world/refidcollection.hpp\"\n#include \"../world/resources.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that magic effect records are internally consistent\n    class MagicEffectCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::MagicEffect> &mMagicEffects;\n            const CSMWorld::IdCollection<ESM::Sound> &mSounds;\n            const CSMWorld::RefIdCollection &mObjects;\n            const CSMWorld::Resources &mIcons;\n            const CSMWorld::Resources &mTextures;\n            bool mIgnoreBaseRecords;\n\n        private:\n            std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const;\n\n        public:\n            MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,\n                                  const CSMWorld::IdCollection<ESM::Sound> &sounds,\n                                  const CSMWorld::RefIdCollection &objects,\n                                  const CSMWorld::Resources &icons,\n                                  const CSMWorld::Resources &textures);\n\n            int setup() override;\n            ///< \\return number of steps\n            void perform (int stage, CSMDoc::Messages &messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/mandatoryid.cpp",
    "content": "#include \"mandatoryid.hpp\"\n\n#include \"../world/collectionbase.hpp\"\n\n#include \"../world/record.hpp\"\n\nCSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::CollectionBase& idCollection,\n    const CSMWorld::UniversalId& collectionId, const std::vector<std::string>& ids)\n: mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids)\n{}\n\nint CSMTools::MandatoryIdStage::setup()\n{\n    return static_cast<int>(mIds.size());\n}\n\nvoid CSMTools::MandatoryIdStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    if (mIdCollection.searchId (mIds.at (stage))==-1 ||\n        mIdCollection.getRecord (mIds.at (stage)).isDeleted())\n        messages.add (mCollectionId, \"Missing mandatory record: \" + mIds.at (stage));\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/mandatoryid.hpp",
    "content": "#ifndef CSM_TOOLS_MANDATORYID_H\n#define CSM_TOOLS_MANDATORYID_H\n\n#include <string>\n#include <vector>\n\n#include \"../world/universalid.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMWorld\n{\n    class CollectionBase;\n}\n\nnamespace CSMTools\n{\n    /// \\brief Verify stage: make sure that records with specific IDs exist.\n    class MandatoryIdStage : public CSMDoc::Stage\n    {\n            const CSMWorld::CollectionBase& mIdCollection;\n            CSMWorld::UniversalId mCollectionId;\n            std::vector<std::string> mIds;\n\n        public:\n\n            MandatoryIdStage (const CSMWorld::CollectionBase& idCollection, const CSMWorld::UniversalId& collectionId,\n                const std::vector<std::string>& ids);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/mergeoperation.cpp",
    "content": "\n#include \"mergeoperation.hpp\"\n\n#include \"../doc/state.hpp\"\n#include \"../doc/document.hpp\"\n\n#include \"mergestages.hpp\"\n\nCSMTools::MergeOperation::MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding)\n: CSMDoc::Operation (CSMDoc::State_Merging, true), mState (document)\n{\n    appendStage (new StartMergeStage (mState));\n\n    appendStage (new MergeIdCollectionStage<ESM::Global> (mState, &CSMWorld::Data::getGlobals));\n    appendStage (new MergeIdCollectionStage<ESM::GameSetting> (mState, &CSMWorld::Data::getGmsts));\n    appendStage (new MergeIdCollectionStage<ESM::Skill> (mState, &CSMWorld::Data::getSkills));\n    appendStage (new MergeIdCollectionStage<ESM::Class> (mState, &CSMWorld::Data::getClasses));\n    appendStage (new MergeIdCollectionStage<ESM::Faction> (mState, &CSMWorld::Data::getFactions));\n    appendStage (new MergeIdCollectionStage<ESM::Race> (mState, &CSMWorld::Data::getRaces));\n    appendStage (new MergeIdCollectionStage<ESM::Sound> (mState, &CSMWorld::Data::getSounds));\n    appendStage (new MergeIdCollectionStage<ESM::Script> (mState, &CSMWorld::Data::getScripts));\n    appendStage (new MergeIdCollectionStage<ESM::Region> (mState, &CSMWorld::Data::getRegions));\n    appendStage (new MergeIdCollectionStage<ESM::BirthSign> (mState, &CSMWorld::Data::getBirthsigns));\n    appendStage (new MergeIdCollectionStage<ESM::Spell> (mState, &CSMWorld::Data::getSpells));\n    appendStage (new MergeIdCollectionStage<ESM::Dialogue> (mState, &CSMWorld::Data::getTopics));\n    appendStage (new MergeIdCollectionStage<ESM::Dialogue> (mState, &CSMWorld::Data::getJournals));\n    appendStage (new MergeIdCollectionStage<CSMWorld::Cell> (mState, &CSMWorld::Data::getCells));\n    appendStage (new MergeIdCollectionStage<ESM::Filter> (mState, &CSMWorld::Data::getFilters));\n    appendStage (new MergeIdCollectionStage<ESM::Enchantment> (mState, &CSMWorld::Data::getEnchantments));\n    appendStage (new MergeIdCollectionStage<ESM::BodyPart> (mState, &CSMWorld::Data::getBodyParts));\n    appendStage (new MergeIdCollectionStage<ESM::DebugProfile> (mState, &CSMWorld::Data::getDebugProfiles));\n    appendStage (new MergeIdCollectionStage<ESM::SoundGenerator> (mState, &CSMWorld::Data::getSoundGens));\n    appendStage (new MergeIdCollectionStage<ESM::MagicEffect> (mState, &CSMWorld::Data::getMagicEffects));\n    appendStage (new MergeIdCollectionStage<ESM::StartScript> (mState, &CSMWorld::Data::getStartScripts));\n    appendStage (new MergeIdCollectionStage<CSMWorld::Pathgrid, CSMWorld::SubCellCollection<CSMWorld::Pathgrid> > (mState, &CSMWorld::Data::getPathgrids));\n    appendStage (new MergeIdCollectionStage<CSMWorld::Info, CSMWorld::InfoCollection> (mState, &CSMWorld::Data::getTopicInfos));\n    appendStage (new MergeIdCollectionStage<CSMWorld::Info, CSMWorld::InfoCollection> (mState, &CSMWorld::Data::getJournalInfos));\n    appendStage (new MergeRefIdsStage (mState));\n    appendStage (new MergeReferencesStage (mState));\n    appendStage (new MergeReferencesStage (mState));\n    appendStage (new PopulateLandTexturesMergeStage (mState));\n    appendStage (new MergeLandStage (mState));\n    appendStage (new FixLandsAndLandTexturesMergeStage (mState));\n    appendStage (new CleanupLandTexturesMergeStage (mState));\n\n    appendStage (new FinishMergedDocumentStage (mState, encoding));\n}\n\nvoid CSMTools::MergeOperation::setTarget (std::unique_ptr<CSMDoc::Document> document)\n{\n    mState.mTarget = std::move(document);\n}\n\nvoid CSMTools::MergeOperation::operationDone()\n{\n    CSMDoc::Operation::operationDone();\n\n    if (mState.mCompleted)\n        emit mergeDone (mState.mTarget.release());\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/mergeoperation.hpp",
    "content": "#ifndef CSM_TOOLS_MERGEOPERATION_H\n#define CSM_TOOLS_MERGEOPERATION_H\n\n#include <memory>\n\n#include <components/to_utf8/to_utf8.hpp>\n\n#include \"../doc/operation.hpp\"\n\n#include \"mergestate.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMTools\n{\n    class MergeOperation : public CSMDoc::Operation\n    {\n            Q_OBJECT\n\n            MergeState mState;\n\n        public:\n\n            MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding);\n\n            /// \\attention Do not call this function while a merge is running.\n            void setTarget (std::unique_ptr<CSMDoc::Document> document);\n\n        protected slots:\n\n            void operationDone() override;\n\n        signals:\n\n            /// \\attention When this signal is emitted, *this hands over the ownership of the\n            /// document. This signal must be handled to avoid a leak.\n            void mergeDone (CSMDoc::Document *document);\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/mergestages.cpp",
    "content": "\n#include \"mergestages.hpp\"\n\n#include <sstream>\n\n#include <components/misc/stringops.hpp>\n\n#include \"mergestate.hpp\"\n\n#include \"../doc/document.hpp\"\n#include \"../world/commands.hpp\"\n#include \"../world/data.hpp\"\n#include \"../world/idtable.hpp\"\n\n\nCSMTools::StartMergeStage::StartMergeStage (MergeState& state)\n: mState (state)\n{}\n\nint CSMTools::StartMergeStage::setup()\n{\n    return 1;\n}\n\nvoid CSMTools::StartMergeStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    mState.mCompleted = false;\n    mState.mTextureIndices.clear();\n}\n\n\nCSMTools::FinishMergedDocumentStage::FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding)\n: mState (state), mEncoder (encoding)\n{}\n\nint CSMTools::FinishMergedDocumentStage::setup()\n{\n    return 1;\n}\n\nvoid CSMTools::FinishMergedDocumentStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    // We know that the content file list contains at least two entries and that the first one\n    // does exist on disc (otherwise it would have been impossible to initiate a merge on that\n    // document).\n    boost::filesystem::path path = mState.mSource.getContentFiles()[0];\n\n    ESM::ESMReader reader;\n    reader.setEncoder (&mEncoder);\n    reader.open (path.string());\n\n    CSMWorld::MetaData source;\n    source.mId = \"sys::meta\";\n    source.load (reader);\n\n    CSMWorld::MetaData target = mState.mTarget->getData().getMetaData();\n\n    target.mAuthor = source.mAuthor;\n    target.mDescription = source.mDescription;\n\n    mState.mTarget->getData().setMetaData (target);\n\n    mState.mCompleted = true;\n}\n\n\nCSMTools::MergeRefIdsStage::MergeRefIdsStage (MergeState& state) : mState (state) {}\n\nint CSMTools::MergeRefIdsStage::setup()\n{\n    return mState.mSource.getData().getReferenceables().getSize();\n}\n\nvoid CSMTools::MergeRefIdsStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    mState.mSource.getData().getReferenceables().copyTo (\n        stage, mState.mTarget->getData().getReferenceables());\n}\n\n\nCSMTools::MergeReferencesStage::MergeReferencesStage (MergeState& state)\n: mState (state)\n{}\n\nint CSMTools::MergeReferencesStage::setup()\n{\n    mIndex.clear();\n    return mState.mSource.getData().getReferences().getSize();\n}\n\nvoid CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<CSMWorld::CellRef>& record =\n        mState.mSource.getData().getReferences().getRecord (stage);\n\n    if (!record.isDeleted())\n    {\n        CSMWorld::CellRef ref = record.get();\n\n        ref.mOriginalCell = ref.mCell;\n\n        ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;\n        ref.mRefNum.mContentFile = 0;\n        ref.mNew = false;\n\n        CSMWorld::Record<CSMWorld::CellRef> newRecord (\n            CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &ref);\n\n        mState.mTarget->getData().getReferences().appendRecord (newRecord);\n    }\n}\n\n\nCSMTools::PopulateLandTexturesMergeStage::PopulateLandTexturesMergeStage (MergeState& state)\n    : mState (state)\n{\n}\n\nint CSMTools::PopulateLandTexturesMergeStage::setup()\n{\n    return mState.mSource.getData().getLandTextures().getSize();\n}\n\nvoid CSMTools::PopulateLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<CSMWorld::LandTexture>& record =\n        mState.mSource.getData().getLandTextures().getRecord (stage);\n\n    if (!record.isDeleted())\n    {\n        mState.mTarget->getData().getLandTextures().appendRecord(record);\n    }\n}\n\n\nCSMTools::MergeLandStage::MergeLandStage (MergeState& state)\n    : mState (state)\n{\n}\n\nint CSMTools::MergeLandStage::setup()\n{\n    return mState.mSource.getData().getLand().getSize();\n}\n\nvoid CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<CSMWorld::Land>& record =\n        mState.mSource.getData().getLand().getRecord (stage);\n\n    if (!record.isDeleted())\n    {\n        mState.mTarget->getData().getLand().appendRecord (record);\n    }\n}\n\n\nCSMTools::FixLandsAndLandTexturesMergeStage::FixLandsAndLandTexturesMergeStage (MergeState& state)\n    : mState (state)\n{\n}\n\nint CSMTools::FixLandsAndLandTexturesMergeStage::setup()\n{\n    // We will have no more than the source\n    return mState.mSource.getData().getLand().getSize();\n}\n\nvoid CSMTools::FixLandsAndLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    if (stage < mState.mTarget->getData().getLand().getSize())\n    {\n        CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&>(\n            *mState.mTarget->getData().getTableModel(CSMWorld::UniversalId::Type_Lands));\n\n        CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&>(\n            *mState.mTarget->getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));\n\n        std::string id = mState.mTarget->getData().getLand().getId(stage);\n\n        CSMWorld::TouchLandCommand cmd(landTable, ltexTable, id);\n        cmd.redo();\n\n        // Get rid of base data\n        const CSMWorld::Record<CSMWorld::Land>& oldRecord =\n            mState.mTarget->getData().getLand().getRecord (stage);\n\n        CSMWorld::Record<CSMWorld::Land> newRecord(CSMWorld::RecordBase::State_ModifiedOnly,\n            nullptr, &oldRecord.get());\n\n        mState.mTarget->getData().getLand().setRecord(stage, newRecord);\n    }\n}\n\nCSMTools::CleanupLandTexturesMergeStage::CleanupLandTexturesMergeStage (MergeState& state)\n    : mState (state)\n{\n}\n\nint CSMTools::CleanupLandTexturesMergeStage::setup()\n{\n    return 1;\n}\n\nvoid CSMTools::CleanupLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    auto& landTextures = mState.mTarget->getData().getLandTextures();\n    for (int i = 0; i < landTextures.getSize(); )\n    {\n        if (!landTextures.getRecord(i).isModified())\n            landTextures.removeRows(i, 1);\n        else\n            ++i;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/mergestages.hpp",
    "content": "#ifndef CSM_TOOLS_MERGESTAGES_H\n#define CSM_TOOLS_MERGESTAGES_H\n\n#include <algorithm>\n#include <map>\n\n#include <components/to_utf8/to_utf8.hpp>\n\n#include \"../doc/stage.hpp\"\n\n#include \"../world/data.hpp\"\n\n#include \"mergestate.hpp\"\n\nnamespace CSMTools\n{\n    class StartMergeStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n\n        public:\n\n            StartMergeStage (MergeState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    class FinishMergedDocumentStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n            ToUTF8::Utf8Encoder mEncoder;\n\n        public:\n\n            FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    template<typename RecordType, typename Collection = CSMWorld::IdCollection<RecordType> >\n    class MergeIdCollectionStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n            Collection& (CSMWorld::Data::*mAccessor)();\n\n        public:\n\n            MergeIdCollectionStage (MergeState& state, Collection& (CSMWorld::Data::*accessor)());\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    template<typename RecordType, typename Collection>\n    MergeIdCollectionStage<RecordType, Collection>::MergeIdCollectionStage (MergeState& state, Collection& (CSMWorld::Data::*accessor)())\n    : mState (state), mAccessor (accessor)\n    {}\n\n    template<typename RecordType, typename Collection>\n    int MergeIdCollectionStage<RecordType, Collection>::setup()\n    {\n        return (mState.mSource.getData().*mAccessor)().getSize();\n    }\n\n    template<typename RecordType, typename Collection>\n    void MergeIdCollectionStage<RecordType, Collection>::perform (int stage, CSMDoc::Messages& messages)\n    {\n        const Collection& source = (mState.mSource.getData().*mAccessor)();\n        Collection& target = (mState.mTarget->getData().*mAccessor)();\n\n        const CSMWorld::Record<RecordType>& record = source.getRecord (stage);\n\n        if (!record.isDeleted())\n            target.appendRecord (CSMWorld::Record<RecordType> (CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get()));\n    }\n\n    class MergeRefIdsStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n\n        public:\n\n            MergeRefIdsStage (MergeState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    class MergeReferencesStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n            std::map<std::string, int> mIndex;\n\n        public:\n\n            MergeReferencesStage (MergeState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    /// Adds all land texture records that could potentially be referenced when merging\n    class PopulateLandTexturesMergeStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n\n        public:\n\n            PopulateLandTexturesMergeStage (MergeState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    class MergeLandStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n\n        public:\n\n            MergeLandStage (MergeState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    /// During this stage, the complex process of combining LandTextures from\n    /// potentially multiple plugins is undertaken.\n    class FixLandsAndLandTexturesMergeStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n\n        public:\n\n            FixLandsAndLandTexturesMergeStage (MergeState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n\n    /// Removes base LandTexture records. This gets rid of the base records previously\n    /// needed in FixLandsAndLandTexturesMergeStage.\n    class CleanupLandTexturesMergeStage : public CSMDoc::Stage\n    {\n            MergeState& mState;\n\n        public:\n\n            CleanupLandTexturesMergeStage (MergeState& state);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/mergestate.hpp",
    "content": "#ifndef CSM_TOOLS_MERGESTATE_H\n#define CSM_TOOLS_MERGESTATE_H\n\n#include <stdint.h>\n\n#include <memory>\n#include <map>\n\n#include \"../doc/document.hpp\"\n\nnamespace CSMTools\n{\n    struct MergeState\n    {\n        std::unique_ptr<CSMDoc::Document> mTarget;\n        CSMDoc::Document& mSource;\n        bool mCompleted;\n        std::map<std::pair<uint16_t, int>, int> mTextureIndices; // (texture, content file) -> new texture\n\n        MergeState (CSMDoc::Document& source) : mSource (source), mCompleted (false) {}\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/pathgridcheck.cpp",
    "content": "#include \"pathgridcheck.hpp\"\n\n#include <sstream>\n#include <algorithm>\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n#include \"../world/idcollection.hpp\"\n#include \"../world/subcellcollection.hpp\"\n#include \"../world/pathgrid.hpp\"\n\nCSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids)\n: mPathgrids (pathgrids)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::PathgridCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mPathgrids.getSize();\n}\n\nvoid CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<CSMWorld::Pathgrid>& record = mPathgrids.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const CSMWorld::Pathgrid& pathgrid = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId);\n\n    // check the number of pathgrid points\n    if (pathgrid.mData.mS2 < static_cast<int>(pathgrid.mPoints.size()))\n        messages.add (id, \"Less points than expected\", \"\", CSMDoc::Message::Severity_Error);\n    else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))\n        messages.add (id, \"More points than expected\", \"\", CSMDoc::Message::Severity_Error);\n\n    std::vector<CSMTools::Point> pointList(pathgrid.mPoints.size());\n    std::vector<int> duplList;\n\n    for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i)\n    {\n        if (pathgrid.mEdges[i].mV0 < static_cast<int>(pathgrid.mPoints.size()) && pathgrid.mEdges[i].mV0 >= 0)\n        {\n            pointList[pathgrid.mEdges[i].mV0].mConnectionNum++;\n            // first check for duplicate edges\n            unsigned int j = 0;\n            for (; j < pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size(); ++j)\n            {\n                if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1)\n                {\n                    std::ostringstream ss;\n                    ss << \"Duplicate edge between points #\" << pathgrid.mEdges[i].mV0 << \" and #\" << pathgrid.mEdges[i].mV1;\n                    messages.add (id, ss.str(), \"\", CSMDoc::Message::Severity_Error);\n                    break;\n                }\n            }\n\n            // only add if not a duplicate\n            if (j == pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size())\n                pointList[pathgrid.mEdges[i].mV0].mOtherIndex.push_back(pathgrid.mEdges[i].mV1);\n        }\n        else\n        {\n            std::ostringstream ss;\n            ss << \"An edge is connected to a non-existent point #\" << pathgrid.mEdges[i].mV0;\n            messages.add (id, ss.str(), \"\", CSMDoc::Message::Severity_Error);\n        }\n    }\n\n    for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i)\n    {\n        // check that edges are bidirectional\n        bool foundReverse = false;\n        for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j)\n        {\n            for (unsigned int k = 0; k < pointList[pointList[i].mOtherIndex[j]].mOtherIndex.size(); ++k)\n            {\n                if (pointList[pointList[i].mOtherIndex[j]].mOtherIndex[k] == static_cast<int>(i))\n                {\n                    foundReverse = true;\n                    break;\n                }\n            }\n\n            if (!foundReverse)\n            {\n                std::ostringstream ss;\n                ss << \"Missing edge between points #\" << i << \" and #\" << pointList[i].mOtherIndex[j];\n                messages.add (id, ss.str(), \"\", CSMDoc::Message::Severity_Error);\n            }\n        }\n\n        // check duplicate points\n        // FIXME: how to do this efficiently?\n        for (unsigned int j = 0; j != i; ++j)\n        {\n            if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX &&\n                pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY &&\n                pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ)\n            {\n                std::vector<int>::const_iterator it = find(duplList.begin(), duplList.end(), static_cast<int>(i));\n                if (it == duplList.end())\n                {\n                    std::ostringstream ss;\n                    ss << \"Point #\" << i << \" duplicates point #\" << j\n                    << \" (\" << pathgrid.mPoints[i].mX << \", \" << pathgrid.mPoints[i].mY << \", \" << pathgrid.mPoints[i].mZ << \")\";\n                    messages.add (id, ss.str(), \"\", CSMDoc::Message::Severity_Warning);\n\n                    duplList.push_back(i);\n                    break;\n                }\n            }\n        }\n    }\n\n    // check pathgrid points that are not connected to anything\n    for (unsigned int i = 0; i < pointList.size(); ++i)\n    {\n        if (pointList[i].mConnectionNum == 0)\n        {\n            std::ostringstream ss;\n            ss << \"Point #\" << i << \" (\"\n            << pathgrid.mPoints[i].mX << \", \"\n            << pathgrid.mPoints[i].mY << \", \"\n            << pathgrid.mPoints[i].mZ << \") is disconnected from other points\";\n            messages.add (id, ss.str(), \"\", CSMDoc::Message::Severity_Warning);\n        }\n    }\n\n    // TODO: check whether there are disconnected graphs\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/pathgridcheck.hpp",
    "content": "#ifndef CSM_TOOLS_PATHGRIDCHECK_H\n#define CSM_TOOLS_PATHGRIDCHECK_H\n\n#include \"../world/collection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMWorld\n{\n    struct Pathgrid;\n    template<typename T, typename AT>\n    class SubCellCollection;\n}\n\nnamespace CSMTools\n{\n    struct Point\n    {\n        unsigned char mConnectionNum;\n        std::vector<int> mOtherIndex;\n        Point() : mConnectionNum(0), mOtherIndex(0) {}\n    };\n\n    class PathgridCheckStage : public CSMDoc::Stage\n    {\n        const CSMWorld::SubCellCollection<CSMWorld::Pathgrid,\n              CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& mPathgrids;\n        bool mIgnoreBaseRecords;\n\n    public:\n\n        PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid,\n            CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& pathgrids);\n\n        int setup() override;\n\n        void perform (int stage, CSMDoc::Messages& messages) override;\n    };\n}\n\n#endif // CSM_TOOLS_PATHGRIDCHECK_H\n"
  },
  {
    "path": "apps/opencs/model/tools/racecheck.cpp",
    "content": "#include \"racecheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nvoid CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Race>& record = mRaces.getRecord (stage);\n\n    if (record.isDeleted())\n        return;\n\n    const ESM::Race& race = record.get();\n\n    // Consider mPlayable flag even when \"Base\" records are ignored\n    if (race.mData.mFlags & 0x1)\n        mPlayable = true;\n\n    // Skip \"Base\" records (setting!)\n    if (mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly)\n        return;\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId);\n\n    // test for empty name and description\n    if (race.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", (race.mData.mFlags & 0x1) ? CSMDoc::Message::Severity_Error : CSMDoc::Message::Severity_Warning);\n\n    if (race.mDescription.empty())\n        messages.add(id, \"Description is missing\", \"\", CSMDoc::Message::Severity_Warning);\n\n    // test for positive height\n    if (race.mData.mHeight.mMale<=0)\n        messages.add(id, \"Male height is non-positive\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (race.mData.mHeight.mFemale<=0)\n        messages.add(id, \"Female height is non-positive\", \"\", CSMDoc::Message::Severity_Error);\n\n    // test for non-negative weight\n    if (race.mData.mWeight.mMale<0)\n        messages.add(id, \"Male weight is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (race.mData.mWeight.mFemale<0)\n        messages.add(id, \"Female weight is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    /// \\todo check data members that can't be edited in the table view\n}\n\nvoid CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages)\n{\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);\n\n    if (!mPlayable)\n        messages.add(id, \"No playable race\", \"\", CSMDoc::Message::Severity_SeriousError);\n}\n\nCSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)\n: mRaces (races), mPlayable (false)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::RaceCheckStage::setup()\n{\n    mPlayable = false;\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mRaces.getSize()+1;\n}\n\nvoid CSMTools::RaceCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    if (stage==mRaces.getSize())\n        performFinal (messages);\n    else\n        performPerRecord (stage, messages);\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/racecheck.hpp",
    "content": "#ifndef CSM_TOOLS_RACECHECK_H\n#define CSM_TOOLS_RACECHECK_H\n\n#include <components/esm/loadrace.hpp>\n\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that race records are internally consistent\n    class RaceCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::Race>& mRaces;\n            bool mPlayable;\n            bool mIgnoreBaseRecords;\n\n            void performPerRecord (int stage, CSMDoc::Messages& messages);\n\n            void performFinal (CSMDoc::Messages& messages);\n\n        public:\n\n            RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/referenceablecheck.cpp",
    "content": "#include \"referenceablecheck.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <components/misc/resourcehelpers.hpp>\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/record.hpp\"\n#include \"../world/universalid.hpp\"\n\nCSMTools::ReferenceableCheckStage::ReferenceableCheckStage(\n    const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,\n    const CSMWorld::IdCollection<ESM::Class>& classes,\n    const CSMWorld::IdCollection<ESM::Faction>& faction,\n    const CSMWorld::IdCollection<ESM::Script>& scripts,\n    const CSMWorld::Resources& models,\n    const CSMWorld::Resources& icons,\n    const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts)\n   :mReferencables(referenceable),\n    mRaces(races),\n    mClasses(classes),\n    mFactions(faction),\n    mScripts(scripts),\n    mModels(models),\n    mIcons(icons),\n    mBodyParts(bodyparts),\n    mPlayerPresent(false)\n{\n    mIgnoreBaseRecords = false;\n}\n\nvoid CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage.\n    const int bookSize(mReferencables.getBooks().getSize());\n\n    if (stage < bookSize)\n    {\n        bookCheck(stage, mReferencables.getBooks(), messages);\n        return;\n    }\n\n    stage -= bookSize;\n\n    const int activatorSize(mReferencables.getActivators().getSize());\n\n    if (stage < activatorSize)\n    {\n        activatorCheck(stage, mReferencables.getActivators(), messages);\n        return;\n    }\n\n    stage -= activatorSize;\n\n    const int potionSize(mReferencables.getPotions().getSize());\n\n    if (stage < potionSize)\n    {\n        potionCheck(stage, mReferencables.getPotions(), messages);\n        return;\n    }\n\n    stage -= potionSize;\n\n    const int apparatusSize(mReferencables.getApparati().getSize());\n\n    if (stage < apparatusSize)\n    {\n        apparatusCheck(stage, mReferencables.getApparati(), messages);\n        return;\n    }\n\n    stage -= apparatusSize;\n\n    const int armorSize(mReferencables.getArmors().getSize());\n\n    if (stage < armorSize)\n    {\n        armorCheck(stage, mReferencables.getArmors(), messages);\n        return;\n    }\n\n    stage -= armorSize;\n\n    const int clothingSize(mReferencables.getClothing().getSize());\n\n    if (stage < clothingSize)\n    {\n        clothingCheck(stage, mReferencables.getClothing(), messages);\n        return;\n    }\n\n    stage -= clothingSize;\n\n    const int containerSize(mReferencables.getContainers().getSize());\n\n    if (stage < containerSize)\n    {\n        containerCheck(stage, mReferencables.getContainers(), messages);\n        return;\n    }\n\n    stage -= containerSize;\n\n    const int doorSize(mReferencables.getDoors().getSize());\n\n    if (stage < doorSize)\n    {\n        doorCheck(stage, mReferencables.getDoors(), messages);\n        return;\n    }\n\n    stage -= doorSize;\n\n    const int ingredientSize(mReferencables.getIngredients().getSize());\n\n    if (stage < ingredientSize)\n    {\n        ingredientCheck(stage, mReferencables.getIngredients(), messages);\n        return;\n    }\n\n    stage -= ingredientSize;\n\n    const int creatureLevListSize(mReferencables.getCreatureLevelledLists().getSize());\n\n    if (stage < creatureLevListSize)\n    {\n        creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages);\n        return;\n    }\n\n    stage -= creatureLevListSize;\n\n    const int itemLevelledListSize(mReferencables.getItemLevelledList().getSize());\n\n    if (stage < itemLevelledListSize)\n    {\n        itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages);\n        return;\n    }\n\n    stage -= itemLevelledListSize;\n\n    const int lightSize(mReferencables.getLights().getSize());\n\n    if (stage < lightSize)\n    {\n        lightCheck(stage, mReferencables.getLights(), messages);\n        return;\n    }\n\n    stage -= lightSize;\n\n    const int lockpickSize(mReferencables.getLocpicks().getSize());\n\n    if (stage < lockpickSize)\n    {\n        lockpickCheck(stage, mReferencables.getLocpicks(), messages);\n        return;\n    }\n\n    stage -= lockpickSize;\n\n    const int miscSize(mReferencables.getMiscellaneous().getSize());\n\n    if (stage < miscSize)\n    {\n        miscCheck(stage, mReferencables.getMiscellaneous(), messages);\n        return;\n    }\n\n    stage -= miscSize;\n\n    const int npcSize(mReferencables.getNPCs().getSize());\n\n    if (stage < npcSize)\n    {\n        npcCheck(stage, mReferencables.getNPCs(), messages);\n        return;\n    }\n\n    stage -= npcSize;\n\n    const int weaponSize(mReferencables.getWeapons().getSize());\n\n    if (stage < weaponSize)\n    {\n        weaponCheck(stage, mReferencables.getWeapons(), messages);\n        return;\n    }\n\n    stage -= weaponSize;\n\n    const int probeSize(mReferencables.getProbes().getSize());\n\n    if (stage < probeSize)\n    {\n        probeCheck(stage, mReferencables.getProbes(), messages);\n        return;\n    }\n\n    stage -= probeSize;\n\n    const int repairSize(mReferencables.getRepairs().getSize());\n\n    if (stage < repairSize)\n    {\n        repairCheck(stage, mReferencables.getRepairs(), messages);\n        return;\n    }\n\n    stage -= repairSize;\n\n    const int staticSize(mReferencables.getStatics().getSize());\n\n    if (stage < staticSize)\n    {\n        staticCheck(stage, mReferencables.getStatics(), messages);\n        return;\n    }\n\n    stage -= staticSize;\n\n    const int creatureSize(mReferencables.getCreatures().getSize());\n\n    if (stage < creatureSize)\n    {\n        creatureCheck(stage, mReferencables.getCreatures(), messages);\n        return;\n    }\n// if we come that far, we are about to perform our last, final check.\n    finalCheck(messages);\n    return;\n}\n\nint CSMTools::ReferenceableCheckStage::setup()\n{\n    mPlayerPresent = false;\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mReferencables.getSize() + 1;\n}\n\nvoid CSMTools::ReferenceableCheckStage::bookCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Book >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Book& book = (dynamic_cast<const CSMWorld::Record<ESM::Book>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId);\n\n    inventoryItemCheck<ESM::Book>(book, messages, id.toString(), true);\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Book>(book, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::activatorCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Activator >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId);\n\n    if (activator.mModel.empty())\n        messages.add(id, \"Model is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mModels.searchId(activator.mModel) == -1)\n        messages.add(id, \"Model '\" + activator.mModel + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Activator>(activator, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::potionCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Potion >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Potion& potion = (dynamic_cast<const CSMWorld::Record<ESM::Potion>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId);\n\n    inventoryItemCheck<ESM::Potion>(potion, messages, id.toString());\n    /// \\todo Check magic effects for validity\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Potion>(potion, messages, id.toString());\n}\n\n\nvoid CSMTools::ReferenceableCheckStage::apparatusCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Apparatus >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Apparatus& apparatus = (dynamic_cast<const CSMWorld::Record<ESM::Apparatus>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, apparatus.mId);\n\n    inventoryItemCheck<ESM::Apparatus>(apparatus, messages, id.toString());\n\n    toolCheck<ESM::Apparatus>(apparatus, messages, id.toString());\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Apparatus>(apparatus, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::armorCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Armor >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Armor& armor = (dynamic_cast<const CSMWorld::Record<ESM::Armor>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, armor.mId);\n\n    inventoryItemCheck<ESM::Armor>(armor, messages, id.toString(), true);\n\n    // Armor should have positive armor class, but 0 class is not an error\n    if (armor.mData.mArmor < 0)\n        messages.add(id, \"Armor class is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    // Armor durability must be a positive number\n    if (armor.mData.mHealth <= 0)\n        messages.add(id, \"Durability is non-positive\", \"\", CSMDoc::Message::Severity_Error);\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Armor>(armor, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::clothingCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Clothing >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId);\n    inventoryItemCheck<ESM::Clothing>(clothing, messages, id.toString(), true);\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Clothing>(clothing, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::containerCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Container >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId);\n\n    //checking for name\n    if (container.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    //Checking for model\n    if (container.mModel.empty())\n        messages.add(id, \"Model is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mModels.searchId(container.mModel) == -1)\n        messages.add(id, \"Model '\" + container.mModel + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    //Checking for capacity (weight)\n    if (container.mWeight < 0) //0 is allowed\n        messages.add(id, \"Capacity is negative\", \"\", CSMDoc::Message::Severity_Error);\n    \n    //checking contained items\n    inventoryListCheck(container.mInventory.mList, messages, id.toString());\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Container>(container, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::creatureCheck (\n    int stage, const CSMWorld::RefIdDataContainer< ESM::Creature >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId);\n\n    if (creature.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (creature.mModel.empty())\n        messages.add(id, \"Model is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mModels.searchId(creature.mModel) == -1)\n        messages.add(id, \"Model '\" + creature.mModel + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    //stats checks\n    if (creature.mData.mLevel <= 0)\n        messages.add(id, \"Level is non-positive\", \"\", CSMDoc::Message::Severity_Warning);\n\n    if (creature.mData.mStrength < 0)\n        messages.add(id, \"Strength is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mIntelligence < 0)\n        messages.add(id, \"Intelligence is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mWillpower < 0)\n        messages.add(id, \"Willpower is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mAgility < 0)\n        messages.add(id, \"Agility is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mSpeed < 0)\n        messages.add(id, \"Speed is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mEndurance < 0)\n        messages.add(id, \"Endurance is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mPersonality < 0)\n        messages.add(id, \"Personality is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mLuck < 0)\n        messages.add(id, \"Luck is negative\", \"\", CSMDoc::Message::Severity_Warning);\n\n    if (creature.mData.mCombat < 0)\n        messages.add(id, \"Combat is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mMagic < 0)\n        messages.add(id, \"Magic is negative\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mData.mStealth < 0)\n        messages.add(id, \"Stealth is negative\", \"\", CSMDoc::Message::Severity_Warning);\n\n    if (creature.mData.mHealth < 0)\n        messages.add(id, \"Health is negative\", \"\", CSMDoc::Message::Severity_Error);\n    if (creature.mData.mMana < 0)\n        messages.add(id, \"Magicka is negative\", \"\", CSMDoc::Message::Severity_Error);\n    if (creature.mData.mFatigue < 0)\n        messages.add(id, \"Fatigue is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (creature.mData.mSoul < 0)\n        messages.add(id, \"Soul value is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (creature.mAiData.mAlarm > 100)\n        messages.add(id, \"Alarm rating is over 100\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mAiData.mFight > 100)\n        messages.add(id, \"Fight rating is over 100\", \"\", CSMDoc::Message::Severity_Warning);\n    if (creature.mAiData.mFlee > 100)\n        messages.add(id, \"Flee rating is over 100\", \"\", CSMDoc::Message::Severity_Warning);\n\n    for (int i = 0; i < 6; ++i)\n    {\n        if (creature.mData.mAttack[i] < 0)\n            messages.add(id, \"Attack \" + std::to_string(i/2 + 1) + \" has negative\" + (i % 2 == 0 ? \" minimum \" : \" maximum \") + \"damage\", \"\", CSMDoc::Message::Severity_Error);\n        if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1])\n            messages.add(id, \"Attack \" + std::to_string(i/2 + 1) + \" has minimum damage higher than maximum damage\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (creature.mData.mGold < 0)\n        messages.add(id, \"Gold count is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (creature.mScale == 0)\n        messages.add(id, \"Scale is equal to zero\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (!creature.mOriginal.empty())\n    {\n        CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(creature.mOriginal);\n        if (index.first == -1)\n            messages.add(id, \"Parent creature '\" + creature.mOriginal + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        else if (index.second != CSMWorld::UniversalId::Type_Creature)\n            messages.add(id, \"'\" + creature.mOriginal + \"' is not a creature\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    // Check inventory\n    inventoryListCheck(creature.mInventory.mList, messages, id.toString());\n \n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Creature>(creature, messages, id.toString());\n    /// \\todo Check spells, teleport table, AI data and AI packages for validity\n}\n\nvoid CSMTools::ReferenceableCheckStage::doorCheck(\n    int stage, const CSMWorld::RefIdDataContainer< ESM::Door >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Door& door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, door.mId);\n\n    //usual, name or model\n    if (door.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (door.mModel.empty())\n        messages.add(id, \"Model is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mModels.searchId(door.mModel) == -1)\n        messages.add(id, \"Model '\" + door.mModel + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Door>(door, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::ingredientCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Ingredient >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Ingredient& ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, ingredient.mId);\n\n    inventoryItemCheck<ESM::Ingredient>(ingredient, messages, id.toString());\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Ingredient>(ingredient, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::creaturesLevListCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::CreatureLevList& CreatureLevList = (dynamic_cast<const CSMWorld::Record<ESM::CreatureLevList>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/\n\n    listCheck<ESM::CreatureLevList>(CreatureLevList, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::itemLevelledListCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::ItemLevList& ItemLevList = (dynamic_cast<const CSMWorld::Record<ESM::ItemLevList>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId);\n\n    listCheck<ESM::ItemLevList>(ItemLevList, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::lightCheck(\n    int stage, const CSMWorld::RefIdDataContainer< ESM::Light >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Light& light = (dynamic_cast<const CSMWorld::Record<ESM::Light>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId);\n\n    if (light.mData.mRadius < 0)\n        messages.add(id, \"Light radius is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (light.mData.mFlags & ESM::Light::Carry)\n        inventoryItemCheck<ESM::Light>(light, messages, id.toString());\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Light>(light, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::lockpickCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Lockpick& lockpick = (dynamic_cast<const CSMWorld::Record<ESM::Lockpick>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, lockpick.mId);\n\n    inventoryItemCheck<ESM::Lockpick>(lockpick, messages, id.toString());\n\n    toolCheck<ESM::Lockpick>(lockpick, messages, id.toString(), true);\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Lockpick>(lockpick, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::miscCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Miscellaneous& miscellaneous = (dynamic_cast<const CSMWorld::Record<ESM::Miscellaneous>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId);\n\n    inventoryItemCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString());\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::npcCheck (\n    int stage, const CSMWorld::RefIdDataContainer< ESM::NPC >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    if (baseRecord.isDeleted())\n        return;\n\n    const ESM::NPC& npc = (dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(baseRecord)).get();\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Npc, npc.mId);\n\n    //Detect if player is present\n    if (Misc::StringUtils::ciEqual(npc.mId, \"player\")) //Happy now, scrawl?\n        mPlayerPresent = true;\n\n    // Skip \"Base\" records (setting!)\n    if (mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly)\n        return;\n\n    short level(npc.mNpdt.mLevel);\n    int gold(npc.mNpdt.mGold);\n\n    if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated\n    {\n        if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag\n        {\n            messages.add(id, \"NPC with autocalculated stats doesn't have autocalc flag turned on\", \"\", CSMDoc::Message::Severity_Error); //should not happen?\n            return;\n        }\n    }\n    else\n    {\n        if (npc.mNpdt.mStrength == 0)\n            messages.add(id, \"Strength is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n        if (npc.mNpdt.mIntelligence == 0)\n            messages.add(id, \"Intelligence is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n        if (npc.mNpdt.mWillpower == 0)\n            messages.add(id, \"Willpower is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n        if (npc.mNpdt.mAgility == 0)\n            messages.add(id, \"Agility is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n        if (npc.mNpdt.mSpeed == 0)\n            messages.add(id, \"Speed is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n        if (npc.mNpdt.mEndurance == 0)\n            messages.add(id, \"Endurance is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n        if (npc.mNpdt.mPersonality == 0)\n            messages.add(id, \"Personality is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n        if (npc.mNpdt.mLuck == 0)\n            messages.add(id, \"Luck is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n    }\n\n    if (level <= 0)\n        messages.add(id, \"Level is non-positive\", \"\", CSMDoc::Message::Severity_Warning);\n\n    if (npc.mAiData.mAlarm > 100)\n        messages.add(id, \"Alarm rating is over 100\", \"\", CSMDoc::Message::Severity_Warning);\n    if (npc.mAiData.mFight > 100)\n        messages.add(id, \"Fight rating is over 100\", \"\", CSMDoc::Message::Severity_Warning);\n    if (npc.mAiData.mFlee > 100)\n        messages.add(id, \"Flee rating is over 100\", \"\", CSMDoc::Message::Severity_Warning);\n\n    if (gold < 0)\n        messages.add(id, \"Gold count is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (npc.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (npc.mClass.empty())\n        messages.add(id, \"Class is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mClasses.searchId (npc.mClass) == -1)\n        messages.add(id, \"Class '\" + npc.mClass + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (npc.mRace.empty())\n        messages.add(id, \"Race is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mRaces.searchId (npc.mRace) == -1)\n        messages.add(id, \"Race '\" + npc.mRace + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (!npc.mFaction.empty() && mFactions.searchId(npc.mFaction) == -1)\n        messages.add(id, \"Faction '\" + npc.mFaction + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (npc.mHead.empty())\n        messages.add(id, \"Head is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else\n    {\n        if (mBodyParts.searchId(npc.mHead) == -1)\n            messages.add(id, \"Head body part '\" + npc.mHead + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        /// \\todo Check gender, race and other body parts stuff validity for the specific NPC\n    }\n\n    if (npc.mHair.empty())\n        messages.add(id, \"Hair is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else\n    {\n        if (mBodyParts.searchId(npc.mHair) == -1)\n            messages.add(id, \"Hair body part '\" + npc.mHair + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        /// \\todo Check gender, race and other body part stuff validity for the specific NPC\n    }\n\n    // Check inventory\n    inventoryListCheck(npc.mInventory.mList, messages, id.toString());\n \n    // Check that mentioned scripts exist\n    scriptCheck<ESM::NPC>(npc, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::weaponCheck(\n    int stage, const CSMWorld::RefIdDataContainer< ESM::Weapon >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Weapon& weapon = (dynamic_cast<const CSMWorld::Record<ESM::Weapon>& >(baseRecord)).get();\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Weapon, weapon.mId);\n\n    //TODO, It seems that this stuff for spellcasting is obligatory and In fact We should check if records are present\n    if\n    (   //THOSE ARE HARDCODED!\n        !(weapon.mId == \"VFX_Hands\" ||\n          weapon.mId == \"VFX_Absorb\" ||\n          weapon.mId == \"VFX_Reflect\" ||\n          weapon.mId == \"VFX_DefaultBolt\" ||\n          //TODO I don't know how to get full list of effects :/\n          //DANGER!, ACHTUNG! FIXME! The following is the list of the magical bolts, valid for Morrowind.esm. However those are not hardcoded.\n          weapon.mId == \"magic_bolt\" ||\n          weapon.mId == \"shock_bolt\" ||\n          weapon.mId == \"shield_bolt\" ||\n          weapon.mId == \"VFX_DestructBolt\" ||\n          weapon.mId == \"VFX_PoisonBolt\" ||\n          weapon.mId == \"VFX_RestoreBolt\" ||\n          weapon.mId == \"VFX_AlterationBolt\" ||\n          weapon.mId == \"VFX_ConjureBolt\" ||\n          weapon.mId == \"VFX_FrostBolt\" ||\n          weapon.mId == \"VFX_MysticismBolt\" ||\n          weapon.mId == \"VFX_IllusionBolt\" ||\n          weapon.mId == \"VFX_Multiple2\" ||\n          weapon.mId == \"VFX_Multiple3\" ||\n          weapon.mId == \"VFX_Multiple4\" ||\n          weapon.mId == \"VFX_Multiple5\" ||\n          weapon.mId == \"VFX_Multiple6\" ||\n          weapon.mId == \"VFX_Multiple7\" ||\n          weapon.mId == \"VFX_Multiple8\" ||\n          weapon.mId == \"VFX_Multiple9\"))\n    {\n        inventoryItemCheck<ESM::Weapon>(weapon, messages, id.toString(), true);\n\n        if (!(weapon.mData.mType == ESM::Weapon::MarksmanBow ||\n                weapon.mData.mType == ESM::Weapon::MarksmanCrossbow ||\n                weapon.mData.mType == ESM::Weapon::MarksmanThrown ||\n                weapon.mData.mType == ESM::Weapon::Arrow ||\n                weapon.mData.mType == ESM::Weapon::Bolt))\n        {\n            if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1])\n                messages.add(id, \"Minimum slash damage higher than maximum\", \"\", CSMDoc::Message::Severity_Warning);\n\n            if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1])\n                messages.add(id, \"Minimum thrust damage higher than maximum\", \"\", CSMDoc::Message::Severity_Warning);\n        }\n\n        if (weapon.mData.mChop[0] > weapon.mData.mChop[1])\n            messages.add(id, \"Minimum chop damage higher than maximum\", \"\", CSMDoc::Message::Severity_Warning);\n\n        if (!(weapon.mData.mType == ESM::Weapon::Arrow ||\n                weapon.mData.mType == ESM::Weapon::Bolt ||\n                weapon.mData.mType == ESM::Weapon::MarksmanThrown))\n        {\n            //checking of health\n            if (weapon.mData.mHealth == 0)\n                messages.add(id, \"Durability is equal to zero\", \"\", CSMDoc::Message::Severity_Warning);\n\n            if (weapon.mData.mReach < 0)\n                messages.add(id, \"Reach is negative\", \"\", CSMDoc::Message::Severity_Error);\n        }\n    }\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Weapon>(weapon, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::probeCheck(\n    int stage,\n    const CSMWorld::RefIdDataContainer< ESM::Probe >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Probe& probe = (dynamic_cast<const CSMWorld::Record<ESM::Probe>& >(baseRecord)).get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Probe, probe.mId);\n\n    inventoryItemCheck<ESM::Probe>(probe, messages, id.toString());\n    toolCheck<ESM::Probe>(probe, messages, id.toString(), true);\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Probe>(probe, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::repairCheck (\n    int stage, const CSMWorld::RefIdDataContainer< ESM::Repair >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Repair& repair = (dynamic_cast<const CSMWorld::Record<ESM::Repair>& >(baseRecord)).get();\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Repair, repair.mId);\n\n    inventoryItemCheck<ESM::Repair> (repair, messages, id.toString());\n    toolCheck<ESM::Repair> (repair, messages, id.toString(), true);\n\n    // Check that mentioned scripts exist\n    scriptCheck<ESM::Repair>(repair, messages, id.toString());\n}\n\nvoid CSMTools::ReferenceableCheckStage::staticCheck (\n    int stage, const CSMWorld::RefIdDataContainer< ESM::Static >& records,\n    CSMDoc::Messages& messages)\n{\n    const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())\n        return;\n\n    const ESM::Static& staticElement = (dynamic_cast<const CSMWorld::Record<ESM::Static>& >(baseRecord)).get();\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId);\n\n    if (staticElement.mModel.empty())\n        messages.add(id, \"Model is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mModels.searchId(staticElement.mModel) == -1)\n        messages.add(id, \"Model '\" + staticElement.mModel + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n}\n\n//final check\n\nvoid CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages)\n{\n    if (!mPlayerPresent)\n        messages.add(CSMWorld::UniversalId::Type_Referenceables, \"Player record is missing\", \"\", CSMDoc::Message::Severity_SeriousError);\n}\n\nvoid CSMTools::ReferenceableCheckStage::inventoryListCheck(\n    const std::vector<ESM::ContItem>& itemList, \n    CSMDoc::Messages& messages, \n    const std::string& id)\n{\n    for (size_t i = 0; i < itemList.size(); ++i)\n    {\n        std::string itemName = itemList[i].mItem;\n        CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName);\n\n        if (localIndex.first == -1)\n            messages.add(id, \"Item '\" + itemName + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        else\n        {\n            // Needs to accommodate containers, creatures, and NPCs\n            switch (localIndex.second)\n            {\n            case CSMWorld::UniversalId::Type_Potion:\n            case CSMWorld::UniversalId::Type_Apparatus:\n            case CSMWorld::UniversalId::Type_Armor:\n            case CSMWorld::UniversalId::Type_Book:\n            case CSMWorld::UniversalId::Type_Clothing:\n            case CSMWorld::UniversalId::Type_Ingredient:\n            case CSMWorld::UniversalId::Type_Light:\n            case CSMWorld::UniversalId::Type_Lockpick:\n            case CSMWorld::UniversalId::Type_Miscellaneous:\n            case CSMWorld::UniversalId::Type_Probe:\n            case CSMWorld::UniversalId::Type_Repair:\n            case CSMWorld::UniversalId::Type_Weapon:\n            case CSMWorld::UniversalId::Type_ItemLevelledList:\n                break;\n            default:\n                messages.add(id, \"'\" + itemName + \"' is not an item\", \"\", CSMDoc::Message::Severity_Error);\n            }\n        }\n    }\n}\n\n//Templates begins here\n\ntemplate<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemCheck (\n    const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable)\n{\n    if (someItem.mName.empty())\n        messages.add(someID, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    //Checking for weight\n    if (someItem.mData.mWeight < 0)\n        messages.add(someID, \"Weight is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    //Checking for value\n    if (someItem.mData.mValue < 0)\n        messages.add(someID, \"Value is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    //checking for model\n    if (someItem.mModel.empty())\n        messages.add(someID, \"Model is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mModels.searchId(someItem.mModel) == -1)\n        messages.add(someID, \"Model '\" + someItem.mModel + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    //checking for icon\n    if (someItem.mIcon.empty())\n        messages.add(someID, \"Icon is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mIcons.searchId(someItem.mIcon) == -1)\n    {\n        std::string ddsIcon = someItem.mIcon;\n        if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))\n            messages.add(someID, \"Icon '\" + someItem.mIcon + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (enchantable && someItem.mData.mEnchant < 0)\n        messages.add(someID, \"Enchantment points number is negative\", \"\", CSMDoc::Message::Severity_Error);\n}\n\ntemplate<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemCheck (\n    const Item& someItem, CSMDoc::Messages& messages, const std::string& someID)\n{\n    if (someItem.mName.empty())\n        messages.add(someID, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    //Checking for weight\n    if (someItem.mData.mWeight < 0)\n        messages.add(someID, \"Weight is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    //Checking for value\n    if (someItem.mData.mValue < 0)\n        messages.add(someID, \"Value is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    //checking for model\n    if (someItem.mModel.empty())\n        messages.add(someID, \"Model is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mModels.searchId(someItem.mModel) == -1)\n        messages.add(someID, \"Model '\" + someItem.mModel + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    //checking for icon\n    if (someItem.mIcon.empty())\n        messages.add(someID, \"Icon is missing\", \"\", CSMDoc::Message::Severity_Error);\n    else if (mIcons.searchId(someItem.mIcon) == -1)\n    {\n        std::string ddsIcon = someItem.mIcon;\n        if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))\n            messages.add(someID, \"Icon '\" + someItem.mIcon + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    }\n}\n\ntemplate<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck (\n    const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken)\n{\n    if (someTool.mData.mQuality <= 0)\n        messages.add(someID, \"Quality is non-positive\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (canBeBroken && someTool.mData.mUses<=0)\n        messages.add(someID, \"Number of uses is non-positive\", \"\", CSMDoc::Message::Severity_Error);\n}\n\ntemplate<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck (\n    const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID)\n{\n    if (someTool.mData.mQuality <= 0)\n        messages.add(someID, \"Quality is non-positive\", \"\", CSMDoc::Message::Severity_Error);\n}\n\ntemplate<typename List> void CSMTools::ReferenceableCheckStage::listCheck (\n    const List& someList, CSMDoc::Messages& messages, const std::string& someID)\n{\n    if (someList.mChanceNone > 100)\n    {\n        messages.add(someID, \"Chance that no object is used is over 100 percent\", \"\", CSMDoc::Message::Severity_Warning);\n    }\n\n    for (unsigned i = 0; i < someList.mList.size(); ++i)\n    {\n        if (mReferencables.searchId(someList.mList[i].mId).first == -1)\n            messages.add(someID, \"Object '\" + someList.mList[i].mId + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n        if (someList.mList[i].mLevel < 1)\n            messages.add(someID, \"Level of item '\" + someList.mList[i].mId + \"' is non-positive\", \"\", CSMDoc::Message::Severity_Error);\n    }\n}\n\ntemplate<typename Tool> void CSMTools::ReferenceableCheckStage::scriptCheck (\n    const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID)\n{\n    if (!someTool.mScript.empty())\n    {\n        if (mScripts.searchId(someTool.mScript) == -1)\n            messages.add(someID, \"Script '\" + someTool.mScript + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/referenceablecheck.hpp",
    "content": "#ifndef REFERENCEABLECHECKSTAGE_H\n#define REFERENCEABLECHECKSTAGE_H\n\n#include \"../world/universalid.hpp\"\n#include \"../doc/stage.hpp\"\n#include \"../world/data.hpp\"\n#include \"../world/refiddata.hpp\"\n#include \"../world/resources.hpp\"\n\nnamespace CSMTools\n{\n    class ReferenceableCheckStage : public CSMDoc::Stage\n    {\n        public:\n\n            ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable,\n                const CSMWorld::IdCollection<ESM::Race>& races,\n                const CSMWorld::IdCollection<ESM::Class>& classes,\n                const CSMWorld::IdCollection<ESM::Faction>& factions,\n                const CSMWorld::IdCollection<ESM::Script>& scripts,\n                const CSMWorld::Resources& models,\n                const CSMWorld::Resources& icons,\n                const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts);\n\n            void perform(int stage, CSMDoc::Messages& messages) override;\n            int setup() override;\n\n        private:\n            //CONCRETE CHECKS\n            void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, CSMDoc::Messages& messages);\n            void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, CSMDoc::Messages& messages);\n            void potionCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Potion>& records, CSMDoc::Messages& messages);\n            void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Apparatus>& records, CSMDoc::Messages& messages);\n            void armorCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Armor>& records, CSMDoc::Messages& messages);\n            void clothingCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Clothing>& records, CSMDoc::Messages& messages);\n            void containerCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Container>& records, CSMDoc::Messages& messages);\n            void creatureCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Creature>& records, CSMDoc::Messages& messages);\n            void doorCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Door>& records, CSMDoc::Messages& messages);\n            void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Ingredient>& records, CSMDoc::Messages& messages);\n            void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::CreatureLevList>& records, CSMDoc::Messages& messages);\n            void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::ItemLevList>& records, CSMDoc::Messages& messages);\n            void lightCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Light>& records, CSMDoc::Messages& messages);\n            void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Lockpick>& records, CSMDoc::Messages& messages);\n            void miscCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Miscellaneous>& records, CSMDoc::Messages& messages);\n            void npcCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::NPC>& records, CSMDoc::Messages& messages);\n            void weaponCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Weapon>& records, CSMDoc::Messages& messages);\n            void probeCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Probe>& records, CSMDoc::Messages& messages);\n            void repairCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Repair>& records, CSMDoc::Messages& messages);\n            void staticCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Static>& records, CSMDoc::Messages& messages);\n\n            //FINAL CHECK\n            void finalCheck (CSMDoc::Messages& messages);\n\n            //Convenience functions\n            void inventoryListCheck(const std::vector<ESM::ContItem>& itemList, CSMDoc::Messages& messages, const std::string& id);\n\n            template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,\n                                                            CSMDoc::Messages& messages,\n                                                            const std::string& someID,\n                                                            bool enchantable); //for all enchantable items.\n\n            template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,\n                                                            CSMDoc::Messages& messages,\n                                                            const std::string& someID); //for non-enchantable items.\n\n            template<typename TOOL> void toolCheck(const TOOL& someTool,\n                                                   CSMDoc::Messages& messages,\n                                                   const std::string& someID,\n                                                   bool canbebroken); //for tools with uses.\n\n            template<typename TOOL> void toolCheck(const TOOL& someTool,\n                                                   CSMDoc::Messages& messages,\n                                                   const std::string& someID); //for tools without uses.\n\n            template<typename LIST> void listCheck(const LIST& someList,\n                                                   CSMDoc::Messages& messages,\n                                                   const std::string& someID);\n\n            template<typename TOOL> void scriptCheck(const TOOL& someTool,\n                                                   CSMDoc::Messages& messages,\n                                                   const std::string& someID);\n\n            const CSMWorld::RefIdData& mReferencables;\n            const CSMWorld::IdCollection<ESM::Race>& mRaces;\n            const CSMWorld::IdCollection<ESM::Class>& mClasses;\n            const CSMWorld::IdCollection<ESM::Faction>& mFactions;\n            const CSMWorld::IdCollection<ESM::Script>& mScripts;\n            const CSMWorld::Resources& mModels;\n            const CSMWorld::Resources& mIcons;\n            const CSMWorld::IdCollection<ESM::BodyPart>& mBodyParts;\n            bool mPlayerPresent;\n            bool mIgnoreBaseRecords;\n    };\n}\n#endif // REFERENCEABLECHECKSTAGE_H\n"
  },
  {
    "path": "apps/opencs/model/tools/referencecheck.cpp",
    "content": "#include \"referencecheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\nCSMTools::ReferenceCheckStage::ReferenceCheckStage(\n    const CSMWorld::RefCollection& references,\n    const CSMWorld::RefIdCollection& referencables,\n    const CSMWorld::IdCollection<CSMWorld::Cell>& cells,\n    const CSMWorld::IdCollection<ESM::Faction>& factions)\n    :\n    mReferences(references),\n    mObjects(referencables),\n    mDataSet(referencables.getDataSet()),\n    mCells(cells),\n    mFactions(factions)\n{\n    mIgnoreBaseRecords = false;\n}\n\nvoid CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &messages)\n{\n    const CSMWorld::Record<CSMWorld::CellRef>& record = mReferences.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const CSMWorld::CellRef& cellRef = record.get();\n    const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId);\n\n    // Check reference id\n    if (cellRef.mRefID.empty())\n        messages.add(id, \"Instance is not based on an object\", \"\", CSMDoc::Message::Severity_Error);\n    else \n    {\n        // Check for non existing referenced object\n        if (mObjects.searchId(cellRef.mRefID) == -1)\n            messages.add(id, \"Instance of a non-existent object '\" + cellRef.mRefID + \"'\", \"\", CSMDoc::Message::Severity_Error);\n        else \n        {\n            // Check if reference charge is valid for it's proper referenced type\n            CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID);\n            bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light;\n            if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1))\n                messages.add(id, \"Invalid charge\", \"\", CSMDoc::Message::Severity_Error);\n        }\n    }\n\n    // If object have owner, check if that owner reference is valid\n    if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1)\n        messages.add(id, \"Owner object '\" + cellRef.mOwner + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    // If object have creature soul trapped, check if that creature reference is valid\n    if (!cellRef.mSoul.empty())\n        if (mObjects.searchId(cellRef.mSoul) == -1)\n            messages.add(id, \"Trapped soul object '\" + cellRef.mSoul + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (cellRef.mFaction.empty())\n    {\n        if (cellRef.mFactionRank != -2)\n            messages.add(id, \"Reference without a faction has a faction rank\", \"\", CSMDoc::Message::Severity_Error);\n    }\n    else\n    {\n        if (mFactions.searchId(cellRef.mFaction) == -1)\n            messages.add(id, \"Faction '\" + cellRef.mFaction + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        else if (cellRef.mFactionRank < -1)\n            messages.add(id, \"Invalid faction rank\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (!cellRef.mDestCell.empty() && mCells.searchId(cellRef.mDestCell) == -1)\n        messages.add(id, \"Destination cell '\" + cellRef.mDestCell + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n\n    if (cellRef.mScale < 0)\n        messages.add(id, \"Negative scale\", \"\", CSMDoc::Message::Severity_Error);\n\n    // Check if enchantement points aren't negative or are at full (-1)\n    if (cellRef.mEnchantmentCharge < -1)\n        messages.add(id, \"Negative number of enchantment points\", \"\", CSMDoc::Message::Severity_Error);\n\n    // Check if gold value isn't negative\n    if (cellRef.mGoldValue < 0)\n        messages.add(id, \"Negative gold value\", \"\", CSMDoc::Message::Severity_Error);\n}\n\nint CSMTools::ReferenceCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mReferences.getSize();\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/referencecheck.hpp",
    "content": "#ifndef CSM_TOOLS_REFERENCECHECK_H\n#define CSM_TOOLS_REFERENCECHECK_H\n\n#include \"../doc/state.hpp\"\n#include \"../doc/document.hpp\"\n\nnamespace CSMTools\n{\n    class ReferenceCheckStage : public CSMDoc::Stage\n    {\n        public:\n            ReferenceCheckStage (const CSMWorld::RefCollection& references,\n                const CSMWorld::RefIdCollection& referencables,\n                const CSMWorld::IdCollection<CSMWorld::Cell>& cells,\n                const CSMWorld::IdCollection<ESM::Faction>& factions);\n\n            void perform(int stage, CSMDoc::Messages& messages) override;\n            int setup() override;\n\n        private:\n            const CSMWorld::RefCollection& mReferences;\n            const CSMWorld::RefIdCollection& mObjects;\n            const CSMWorld::RefIdData& mDataSet;\n            const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;\n            const CSMWorld::IdCollection<ESM::Faction>& mFactions;\n            bool mIgnoreBaseRecords;\n    };\n}\n\n#endif // CSM_TOOLS_REFERENCECHECK_H\n"
  },
  {
    "path": "apps/opencs/model/tools/regioncheck.cpp",
    "content": "#include \"regioncheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nCSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions)\n: mRegions (regions)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::RegionCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mRegions.getSize();\n}\n\nvoid CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Region>& record = mRegions.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::Region& region = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId);\n\n    // test for empty name\n    if (region.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    /// \\todo test that the ID in mSleeplist exists\n\n    // test that chances add up to 100\n    int chances = region.mData.mClear + region.mData.mCloudy + region.mData.mFoggy + region.mData.mOvercast +\n        region.mData.mRain + region.mData.mThunder + region.mData.mAsh + region.mData.mBlight +\n        region.mData.mA + region.mData.mB;\n    if (chances != 100)\n        messages.add(id, \"Weather chances do not add up to 100\", \"\", CSMDoc::Message::Severity_Error);\n\n    for (const ESM::Region::SoundRef& sound : region.mSoundList)\n    {\n        if (sound.mChance > 100)\n            messages.add(id, \"Chance of '\" + sound.mSound + \"' sound to play is over 100 percent\", \"\", CSMDoc::Message::Severity_Warning);\n    }\n\n    /// \\todo check data members that can't be edited in the table view\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/regioncheck.hpp",
    "content": "#ifndef CSM_TOOLS_REGIONCHECK_H\n#define CSM_TOOLS_REGIONCHECK_H\n\n#include <components/esm/loadregn.hpp>\n\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that region records are internally consistent\n    class RegionCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::Region>& mRegions;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/reportmodel.cpp",
    "content": "#include \"reportmodel.hpp\"\n\n#include <stdexcept>\n#include <sstream>\n\n#include \"../world/columns.hpp\"\n\nCSMTools::ReportModel::ReportModel (bool fieldColumn, bool severityColumn)\n: mColumnField (-1), mColumnSeverity (-1)\n{\n    int index = 3;\n\n    if (severityColumn)\n        mColumnSeverity = index++;\n\n    if (fieldColumn)\n        mColumnField = index++;\n\n    mColumnDescription = index;\n}\n\nint CSMTools::ReportModel::rowCount (const QModelIndex & parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return static_cast<int>(mRows.size());\n}\n\nint CSMTools::ReportModel::columnCount (const QModelIndex & parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return mColumnDescription+1;\n}\n\nQVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const\n{\n    if (role!=Qt::DisplayRole && role!=Qt::UserRole)\n        return QVariant();\n\n    switch (index.column())\n    {\n        case Column_Type:\n\n            if(role == Qt::UserRole)\n                return QString::fromUtf8 (\n                    mRows.at (index.row()).mId.getTypeName().c_str());\n            else\n                return static_cast<int> (mRows.at (index.row()).mId.getType());\n\n        case Column_Id:\n        {\n            CSMWorld::UniversalId id = mRows.at (index.row()).mId;\n\n            if (id.getArgumentType()==CSMWorld::UniversalId::ArgumentType_Id)\n                return QString::fromUtf8 (id.getId().c_str());\n\n            return QString (\"-\");\n        }\n\n        case Column_Hint:\n\n            return QString::fromUtf8 (mRows.at (index.row()).mHint.c_str());\n    }\n\n    if (index.column()==mColumnDescription)\n        return QString::fromUtf8 (mRows.at (index.row()).mMessage.c_str());\n\n    if (index.column()==mColumnField)\n    {\n        std::string field;\n\n        std::istringstream stream (mRows.at (index.row()).mHint);\n\n        char type, ignore;\n        int fieldIndex;\n\n        if ((stream >> type >> ignore >> fieldIndex) && (type=='r' || type=='R'))\n        {\n            field = CSMWorld::Columns::getName (\n                static_cast<CSMWorld::Columns::ColumnId> (fieldIndex));\n        }\n\n        return QString::fromUtf8 (field.c_str());\n    }\n\n    if (index.column()==mColumnSeverity)\n    {\n        return QString::fromUtf8 (\n            CSMDoc::Message::toString (mRows.at (index.row()).mSeverity).c_str());\n    }\n\n    return QVariant();\n}\n\nQVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orientation, int role) const\n{\n    if (role!=Qt::DisplayRole)\n        return QVariant();\n\n    if (orientation==Qt::Vertical)\n        return QVariant();\n\n    switch (section)\n    {\n        case Column_Type: return \"Type\";\n        case Column_Id: return \"ID\";\n    }\n\n    if (section==mColumnDescription)\n        return \"Description\";\n\n    if (section==mColumnField)\n        return \"Field\";\n\n    if (section==mColumnSeverity)\n        return \"Severity\";\n\n    return \"-\";\n}\n\nbool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& parent)\n{\n    if (parent.isValid())\n        return false;\n\n    if (count>0)\n    {\n        beginRemoveRows (parent, row, row+count-1);\n\n        mRows.erase (mRows.begin()+row, mRows.begin()+row+count);\n\n        endRemoveRows();\n    }\n\n    return true;\n}\n\nvoid CSMTools::ReportModel::add (const CSMDoc::Message& message)\n{\n    beginInsertRows (QModelIndex(), static_cast<int>(mRows.size()), static_cast<int>(mRows.size()));\n\n    mRows.push_back (message);\n\n    endInsertRows();\n}\n\nvoid CSMTools::ReportModel::flagAsReplaced (int index)\n{\n    CSMDoc::Message& line = mRows.at (index);\n    std::string hint = line.mHint;\n\n    if (hint.empty() || hint[0]!='R')\n        throw std::logic_error (\"trying to flag message as replaced that is not replaceable\");\n\n    hint[0] = 'r';\n\n    line.mHint = hint;\n\n    emit dataChanged (this->index (index, 0), this->index (index, columnCount()));\n}\n\nconst CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const\n{\n    return mRows.at (row).mId;\n}\n\nstd::string CSMTools::ReportModel::getHint (int row) const\n{\n    return mRows.at (row).mHint;\n}\n\nvoid CSMTools::ReportModel::clear()\n{\n    if (!mRows.empty())\n    {\n        beginRemoveRows (QModelIndex(), 0, static_cast<int>(mRows.size())-1);\n        mRows.clear();\n        endRemoveRows();\n    }\n}\n\nint CSMTools::ReportModel::countErrors() const\n{\n    int count = 0;\n\n    for (std::vector<CSMDoc::Message>::const_iterator iter (mRows.begin());\n        iter!=mRows.end(); ++iter)\n        if (iter->mSeverity==CSMDoc::Message::Severity_Error ||\n            iter->mSeverity==CSMDoc::Message::Severity_SeriousError)\n            ++count;\n\n    return count;\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/reportmodel.hpp",
    "content": "#ifndef CSM_TOOLS_REPORTMODEL_H\n#define CSM_TOOLS_REPORTMODEL_H\n\n#include <vector>\n#include <string>\n\n#include <QAbstractTableModel>\n\n#include \"../doc/messages.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nnamespace CSMTools\n{\n    class ReportModel : public QAbstractTableModel\n    {\n            Q_OBJECT\n\n            std::vector<CSMDoc::Message> mRows;\n\n            // Fixed columns\n            enum Columns\n            {\n                Column_Type = 0, Column_Id = 1, Column_Hint = 2\n            };\n\n            // Configurable columns\n            int mColumnDescription;\n            int mColumnField;\n            int mColumnSeverity;\n\n        public:\n\n            ReportModel (bool fieldColumn = false, bool severityColumn = true);\n        \n            int rowCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            int columnCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const override;\n\n            QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;\n\n            bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()) override;\n            \n            void add (const CSMDoc::Message& message);\n\n            void flagAsReplaced (int index);\n                \n            const CSMWorld::UniversalId& getUniversalId (int row) const;\n\n            std::string getHint (int row) const;\n\n            void clear();\n\n            // Return number of messages with Error or SeriousError severity.\n            int countErrors() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/scriptcheck.cpp",
    "content": "#include \"scriptcheck.hpp\"\n\n#include <components/compiler/tokenloc.hpp>\n#include <components/compiler/scanner.hpp>\n#include <components/compiler/fileparser.hpp>\n#include <components/compiler/exception.hpp>\n#include <components/compiler/extensions0.hpp>\n\n#include \"../doc/document.hpp\"\n\n#include \"../world/data.hpp\"\n\n#include \"../prefs/state.hpp\"\n\nCSMDoc::Message::Severity CSMTools::ScriptCheckStage::getSeverity (Type type)\n{\n    switch (type)\n    {\n        case WarningMessage: return CSMDoc::Message::Severity_Warning;\n        case ErrorMessage: return CSMDoc::Message::Severity_Error;\n    }\n\n    return CSMDoc::Message::Severity_SeriousError;\n}\n\nvoid CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc,\n    Type type)\n{\n    std::ostringstream stream;\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);\n\n    stream << message << \" (\" << loc.mLiteral  << \")\" << \" @ line \" << loc.mLine+1 << \", column \" << loc.mColumn;\n\n    std::ostringstream hintStream;\n\n    hintStream << \"l:\" << loc.mLine+1 << \" \" << loc.mColumn;\n\n    mMessages->add (id, stream.str(), hintStream.str(), getSeverity (type));\n}\n\nvoid CSMTools::ScriptCheckStage::report (const std::string& message, Type type)\n{\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);\n\n    std::ostringstream stream;\n    stream << message;\n\n    mMessages->add (id, stream.str(), \"\", getSeverity (type));\n}\n\nCSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document)\n: mDocument (document), mContext (document.getData()), mMessages (nullptr), mWarningMode (Mode_Ignore)\n{\n    /// \\todo add an option to configure warning mode\n    setWarningsMode (0);\n\n    Compiler::registerExtensions (mExtensions);\n    mContext.setExtensions (&mExtensions);\n\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::ScriptCheckStage::setup()\n{\n    std::string warnings = CSMPrefs::get()[\"Scripts\"][\"warnings\"].toString();\n\n    if (warnings==\"Ignore\")\n        mWarningMode = Mode_Ignore;\n    else if (warnings==\"Normal\")\n        mWarningMode = Mode_Normal;\n    else if (warnings==\"Strict\")\n        mWarningMode = Mode_Strict;\n\n    mContext.clear();\n    mMessages = nullptr;\n    mId.clear();\n    Compiler::ErrorHandler::reset();\n\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mDocument.getData().getScripts().getSize();\n}\n\nvoid CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Script> &record = mDocument.getData().getScripts().getRecord(stage);\n\n    mId = mDocument.getData().getScripts().getId (stage);\n\n    if (mDocument.isBlacklisted (\n        CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId)))\n        return;\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    mMessages = &messages;\n\n    switch (mWarningMode)\n    {\n        case Mode_Ignore: setWarningsMode (0); break;\n        case Mode_Normal: setWarningsMode (1); break;\n        case Mode_Strict: setWarningsMode (2); break;\n    }\n\n    try\n    {\n        mFile = record.get().mId;\n        std::istringstream input (record.get().mScriptText);\n\n        Compiler::Scanner scanner (*this, input, mContext.getExtensions());\n\n        Compiler::FileParser parser (*this, mContext);\n\n        scanner.scan (parser);\n    }\n    catch (const Compiler::SourceException&)\n    {\n        // error has already been reported via error handler\n    }\n    catch (const std::exception& error)\n    {\n        CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);\n\n        std::ostringstream stream;\n        stream << error.what();\n\n        messages.add (id, stream.str(), \"\", CSMDoc::Message::Severity_SeriousError);\n    }\n\n    mMessages = nullptr;\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/scriptcheck.hpp",
    "content": "#ifndef CSM_TOOLS_SCRIPTCHECK_H\n#define CSM_TOOLS_SCRIPTCHECK_H\n\n#include <components/compiler/errorhandler.hpp>\n#include <components/compiler/extensions.hpp>\n\n#include \"../doc/stage.hpp\"\n\n#include \"../world/scriptcontext.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that scripts compile\n    class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler\n    {\n            enum WarningMode\n            {\n                Mode_Ignore,\n                Mode_Normal,\n                Mode_Strict\n            };\n\n            const CSMDoc::Document& mDocument;\n            Compiler::Extensions mExtensions;\n            CSMWorld::ScriptContext mContext;\n            std::string mId;\n            std::string mFile;\n            CSMDoc::Messages *mMessages;\n            WarningMode mWarningMode;\n            bool mIgnoreBaseRecords;\n\n            CSMDoc::Message::Severity getSeverity (Type type);\n\n            void report (const std::string& message, const Compiler::TokenLoc& loc, Type type) override;\n            ///< Report error to the user.\n\n            void report (const std::string& message, Type type) override;\n            ///< Report a file related error\n\n        public:\n\n            ScriptCheckStage (const CSMDoc::Document& document);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/search.cpp",
    "content": "#include \"search.hpp\"\n\n#include <stdexcept>\n#include <sstream>\n\n#include \"../doc/messages.hpp\"\n#include \"../doc/document.hpp\"\n\n#include \"../world/idtablebase.hpp\"\n#include \"../world/columnbase.hpp\"\n#include \"../world/universalid.hpp\"\n#include \"../world/commands.hpp\"\n\nvoid CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model,\n    const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable,\n    CSMDoc::Messages& messages) const\n{\n    // using QString here for easier handling of case folding.\n    \n    QString search = QString::fromUtf8 (mText.c_str());\n    QString text = model->data (index).toString();\n\n    int pos = 0;\n\n    Qt::CaseSensitivity caseSensitivity = mCase ? Qt::CaseSensitive : Qt::CaseInsensitive;\n    while ((pos = text.indexOf (search, pos, caseSensitivity))!=-1)\n    {\n        std::ostringstream hint;\n        hint\n            << (writable ? 'R' : 'r')\n            <<\": \"\n            << model->getColumnId (index.column())\n            << \" \" << pos\n            << \" \" << search.length();\n        \n        messages.add (id, formatDescription (text, pos, search.length()).toUtf8().data(), hint.str());\n\n        pos += search.length();\n    }\n}\n\nvoid CSMTools::Search::searchRegExCell (const CSMWorld::IdTableBase *model,\n    const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable,\n    CSMDoc::Messages& messages) const\n{\n    QString text = model->data (index).toString();\n\n    int pos = 0;\n\n    while ((pos = mRegExp.indexIn (text, pos))!=-1)\n    {\n        int length = mRegExp.matchedLength();\n        \n        std::ostringstream hint;\n        hint\n            << (writable ? 'R' : 'r')\n            <<\": \"\n            << model->getColumnId (index.column())\n            << \" \" << pos\n            << \" \" << length;\n        \n        messages.add (id, formatDescription (text, pos, length).toUtf8().data(), hint.str());\n\n        pos += length;\n    }\n}\n\nvoid CSMTools::Search::searchRecordStateCell (const CSMWorld::IdTableBase *model,\n    const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const\n{\n    if (writable)\n        throw std::logic_error (\"Record state can not be modified by search and replace\");\n        \n    int data = model->data (index).toInt();\n\n    if (data==mValue)\n    {\n        std::vector<std::pair<int,std::string>> states =\n            CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);\n\n        const std::string hint = \"r: \" + std::to_string(model->getColumnId(index.column()));\n        messages.add (id, states.at(data).second, hint);\n    }\n}\n\nQString CSMTools::Search::formatDescription (const QString& description, int pos, int length) const\n{\n    QString text (description);\n\n    // split\n    QString highlight = flatten (text.mid (pos, length));\n    QString before = flatten (mPaddingBefore>=pos ?\n        text.mid (0, pos) : text.mid (pos-mPaddingBefore, mPaddingBefore));\n    QString after = flatten (text.mid (pos+length, mPaddingAfter));\n\n    // compensate for Windows nonsense\n    text.remove ('\\r');\n\n    // join\n    text = before + \"<b>\" + highlight + \"</b>\" + after;\n\n    // improve layout for single line display\n    text.replace (\"\\n\", \"&lt;CR>\");\n    text.replace ('\\t', ' ');\n    \n    return text; \n}\n\nQString CSMTools::Search::flatten (const QString& text) const\n{\n    QString flat (text);\n\n    flat.replace (\"&\", \"&amp;\");\n    flat.replace (\"<\", \"&lt;\");\n\n    return flat;\n}\n\nCSMTools::Search::Search() : mType (Type_None), mValue (0), mCase (false), mIdColumn (0), mTypeColumn (0),\n    mPaddingBefore (10), mPaddingAfter (10) {}\n\nCSMTools::Search::Search (Type type, bool caseSensitive, const std::string& value)\n: mType (type), mText (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)\n{\n    if (type!=Type_Text && type!=Type_Id)\n        throw std::logic_error (\"Invalid search parameter (string)\");\n}\n\nCSMTools::Search::Search (Type type, bool caseSensitive, const QRegExp& value)\n: mType (type), mRegExp (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)\n{\n    mRegExp.setCaseSensitivity(mCase ? Qt::CaseSensitive : Qt::CaseInsensitive);\n    if (type!=Type_TextRegEx && type!=Type_IdRegEx)\n        throw std::logic_error (\"Invalid search parameter (RegExp)\");\n}\n\nCSMTools::Search::Search (Type type, bool caseSensitive, int value)\n: mType (type), mValue (value), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)\n{\n    if (type!=Type_RecordState)\n        throw std::logic_error (\"invalid search parameter (int)\");\n}\n\nvoid CSMTools::Search::configure (const CSMWorld::IdTableBase *model)\n{\n    mColumns.clear();\n\n    int columns = model->columnCount();\n\n    for (int i=0; i<columns; ++i)\n    {\n        CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> (\n            model->headerData (\n            i,  Qt::Horizontal, static_cast<int> (CSMWorld::ColumnBase::Role_Display)).toInt());\n\n        bool consider = false;\n\n        switch (mType)\n        {\n            case Type_Text:\n            case Type_TextRegEx:\n\n                if (CSMWorld::ColumnBase::isText (display) ||\n                    CSMWorld::ColumnBase::isScript (display))\n                {\n                    consider = true;\n                }\n\n                break;\n                \n            case Type_Id:\n            case Type_IdRegEx:\n\n                if (CSMWorld::ColumnBase::isId (display) ||\n                    CSMWorld::ColumnBase::isScript (display))\n                {\n                    consider = true;\n                }\n\n                break;\n            \n            case Type_RecordState:\n\n                if (display==CSMWorld::ColumnBase::Display_RecordState)\n                    consider = true;\n\n                break;\n\n            case Type_None:\n\n                break;\n        }\n\n        if (consider)\n            mColumns.insert (i);\n    }\n\n    mIdColumn = model->findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    mTypeColumn = model->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);\n}\n\nvoid CSMTools::Search::searchRow (const CSMWorld::IdTableBase *model, int row,\n    CSMDoc::Messages& messages) const\n{\n    for (std::set<int>::const_iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)\n    {\n        QModelIndex index = model->index (row, *iter);\n\n        CSMWorld::UniversalId::Type type = static_cast<CSMWorld::UniversalId::Type> (\n            model->data (model->index (row, mTypeColumn)).toInt());\n        \n        CSMWorld::UniversalId id (\n            type, model->data (model->index (row, mIdColumn)).toString().toUtf8().data());\n\n        bool writable = model->flags (index) & Qt::ItemIsEditable;\n            \n        switch (mType)\n        {\n            case Type_Text:\n            case Type_Id:\n\n                searchTextCell (model, index, id, writable, messages);\n                break;\n            \n            case Type_TextRegEx:\n            case Type_IdRegEx:\n\n                searchRegExCell (model, index, id, writable, messages);\n                break;\n            \n            case Type_RecordState:\n\n                searchRecordStateCell (model, index, id, writable, messages);\n                break;\n\n            case Type_None:\n\n                break;\n        }\n    }\n}\n\nvoid CSMTools::Search::setPadding (int before, int after)\n{\n    mPaddingBefore = before;\n    mPaddingAfter = after;\n}\n\nvoid CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model,\n    const CSMWorld::UniversalId& id, const std::string& messageHint,\n    const std::string& replaceText) const\n{\n    std::istringstream stream (messageHint.c_str());\n\n    char hint, ignore;\n    int columnId, pos, length;\n\n    if (stream >> hint >> ignore >> columnId >> pos >> length)\n    {\n        int column =\n            model->findColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (columnId));\n            \n        QModelIndex index = model->getModelIndex (id.getId(), column);\n\n        std::string text = model->data (index).toString().toUtf8().constData();\n\n        std::string before = text.substr (0, pos);\n        std::string after = text.substr (pos+length);\n\n        std::string newText = before + replaceText + after;\n        \n        document.getUndoStack().push (\n            new CSMWorld::ModifyCommand (*model, index, QString::fromUtf8 (newText.c_str())));\n    }\n}\n\nbool CSMTools::Search::verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model,\n    const CSMWorld::UniversalId& id, const std::string& messageHint) const\n{\n    CSMDoc::Messages messages (CSMDoc::Message::Severity_Info);\n\n    int row = model->getModelIndex (id.getId(),\n        model->findColumnIndex (CSMWorld::Columns::ColumnId_Id)).row();\n    \n    searchRow (model, row, messages);\n\n    for (CSMDoc::Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter)\n        if (iter->mHint==messageHint)\n            return true;\n\n    return false;\n}\n                \n"
  },
  {
    "path": "apps/opencs/model/tools/search.hpp",
    "content": "#ifndef CSM_TOOLS_SEARCH_H\n#define CSM_TOOLS_SEARCH_H\n\n#include <string>\n#include <set>\n\n#include <QRegExp>\n#include <QMetaType>\n\nclass QModelIndex;\n\nnamespace CSMDoc\n{\n    class Messages;\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class IdTableBase;\n    class UniversalId;\n}\n\nnamespace CSMTools\n{\n    class Search\n    {\n        public:\n\n            enum Type\n            {\n                Type_Text = 0,\n                Type_TextRegEx = 1,\n                Type_Id = 2,\n                Type_IdRegEx = 3,\n                Type_RecordState = 4,\n                Type_None\n            };\n\n        private:\n\n            Type mType;\n            std::string mText;\n            QRegExp mRegExp;\n            int mValue;\n            bool mCase;\n            std::set<int> mColumns;\n            int mIdColumn;\n            int mTypeColumn;\n            int mPaddingBefore;\n            int mPaddingAfter;\n\n            void searchTextCell (const CSMWorld::IdTableBase *model, const QModelIndex& index,\n                const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const;\n\n            void searchRegExCell (const CSMWorld::IdTableBase *model, const QModelIndex& index,\n                const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const;\n\n            void searchRecordStateCell (const CSMWorld::IdTableBase *model,\n                const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable,\n                CSMDoc::Messages& messages) const;\n\n            QString formatDescription (const QString& description, int pos, int length) const;\n\n            QString flatten (const QString& text) const;\n            \n        public:\n\n            Search();\n        \n            Search (Type type, bool caseSensitive, const std::string& value);\n\n            Search (Type type, bool caseSensitive, const QRegExp& value);\n\n            Search (Type type, bool caseSensitive, int value);\n\n            // Configure search for the specified model.\n            void configure (const CSMWorld::IdTableBase *model);\n\n            // Search row in \\a model and store results in \\a messages.\n            //\n            // \\attention *this needs to be configured for \\a model.\n            void searchRow (const CSMWorld::IdTableBase *model, int row,\n                CSMDoc::Messages& messages) const;\n\n            void setPadding (int before, int after);\n\n            // Configuring *this for the model is not necessary when calling this function.\n            void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model,\n                const CSMWorld::UniversalId& id, const std::string& messageHint,\n                const std::string& replaceText) const;\n\n            // Check if model still matches search results.\n            bool verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model,\n                const CSMWorld::UniversalId& id, const std::string& messageHint) const;\n    };\n}\n\nQ_DECLARE_METATYPE (CSMTools::Search)\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/searchoperation.cpp",
    "content": "#include \"searchoperation.hpp\"\n\n#include \"../doc/state.hpp\"\n#include \"../doc/document.hpp\"\n\n#include \"../world/data.hpp\"\n#include \"../world/idtablebase.hpp\"\n\n#include \"searchstage.hpp\"\n\nCSMTools::SearchOperation::SearchOperation (CSMDoc::Document& document)\n: CSMDoc::Operation (CSMDoc::State_Searching, false)\n{\n    std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listTypes (\n        CSMWorld::UniversalId::Class_RecordList |\n        CSMWorld::UniversalId::Class_ResourceList\n        );\n\n    for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());\n        iter!=types.end(); ++iter)\n        appendStage (new SearchStage (&dynamic_cast<CSMWorld::IdTableBase&> (\n            *document.getData().getTableModel (*iter))));\n\n    setDefaultSeverity (CSMDoc::Message::Severity_Info);\n}\n\nvoid CSMTools::SearchOperation::configure (const Search& search)\n{\n    mSearch = search;\n}\n\nvoid CSMTools::SearchOperation::appendStage (SearchStage *stage)\n{\n    CSMDoc::Operation::appendStage (stage);\n    stage->setOperation (this);\n}\n\nconst CSMTools::Search& CSMTools::SearchOperation::getSearch() const\n{\n    return mSearch;\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/searchoperation.hpp",
    "content": "#ifndef CSM_TOOLS_SEARCHOPERATION_H\n#define CSM_TOOLS_SEARCHOPERATION_H\n\n#include \"../doc/operation.hpp\"\n\n#include \"search.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMTools\n{\n    class SearchStage;\n    \n    class SearchOperation : public CSMDoc::Operation\n    {\n            Search mSearch;\n            \n        public:\n\n            SearchOperation (CSMDoc::Document& document);\n\n            /// \\attention Do not call this function while a search is running.\n            void configure (const Search& search);\n\n            void appendStage (SearchStage *stage);\n            ///< The ownership of \\a stage is transferred to *this.\n            ///\n            /// \\attention Do no call this function while this Operation is running.\n\n            const Search& getSearch() const;\n            \n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/searchstage.cpp",
    "content": "#include \"searchstage.hpp\"\n\n#include \"../world/idtablebase.hpp\"\n\n#include \"searchoperation.hpp\"\n\nCSMTools::SearchStage::SearchStage (const CSMWorld::IdTableBase *model)\n: mModel (model), mOperation (nullptr)\n{}\n\nint CSMTools::SearchStage::setup()\n{\n    if (mOperation)\n        mSearch = mOperation->getSearch();\n\n    mSearch.configure (mModel);\n        \n    return mModel->rowCount();\n}\n\nvoid CSMTools::SearchStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    mSearch.searchRow (mModel, stage, messages);\n}\n\nvoid CSMTools::SearchStage::setOperation (const SearchOperation *operation)\n{\n    mOperation = operation;\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/searchstage.hpp",
    "content": "#ifndef CSM_TOOLS_SEARCHSTAGE_H\n#define CSM_TOOLS_SEARCHSTAGE_H\n\n#include \"../doc/stage.hpp\"\n\n#include \"search.hpp\"\n\nnamespace CSMWorld\n{\n    class IdTableBase;\n}\n\nnamespace CSMTools\n{\n    class SearchOperation;\n    \n    class SearchStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdTableBase *mModel;\n            Search mSearch;\n            const SearchOperation *mOperation;\n\n        public:\n\n            SearchStage (const CSMWorld::IdTableBase *model);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n\n            void setOperation (const SearchOperation *operation);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/skillcheck.cpp",
    "content": "#include \"skillcheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nCSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills)\n: mSkills (skills)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::SkillCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mSkills.getSize();\n}\n\nvoid CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Skill>& record = mSkills.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::Skill& skill = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId);\n\n    if (skill.mDescription.empty())\n        messages.add(id, \"Description is missing\", \"\", CSMDoc::Message::Severity_Warning);\n\n    for (int i=0; i<4; ++i)\n        if (skill.mData.mUseValue[i]<0)\n        {\n            messages.add(id, \"Use value #\" + std::to_string(i) + \" is negative\", \"\", CSMDoc::Message::Severity_Error);\n        }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/skillcheck.hpp",
    "content": "#ifndef CSM_TOOLS_SKILLCHECK_H\n#define CSM_TOOLS_SKILLCHECK_H\n\n#include <components/esm/loadskil.hpp>\n\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that skill records are internally consistent\n    class SkillCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::Skill>& mSkills;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/soundcheck.cpp",
    "content": "#include \"soundcheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nCSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound> &sounds,\n                                            const CSMWorld::Resources &soundfiles)\n    : mSounds (sounds),\n      mSoundFiles (soundfiles)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::SoundCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mSounds.getSize();\n}\n\nvoid CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::Sound& sound = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);\n\n    if (sound.mData.mMinRange>sound.mData.mMaxRange)\n    {\n        messages.add(id, \"Minimum range is larger than maximum range\", \"\", CSMDoc::Message::Severity_Warning);\n    }\n\n    if (sound.mSound.empty())\n    {\n        messages.add(id, \"Sound file is missing\", \"\", CSMDoc::Message::Severity_Error);\n    }\n    else if (mSoundFiles.searchId(sound.mSound) == -1)\n    {\n        messages.add(id, \"Sound file '\" + sound.mSound + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/soundcheck.hpp",
    "content": "#ifndef CSM_TOOLS_SOUNDCHECK_H\n#define CSM_TOOLS_SOUNDCHECK_H\n\n#include <components/esm/loadsoun.hpp>\n\n#include \"../world/resources.hpp\"\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that sound records are internally consistent\n    class SoundCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::Sound>& mSounds;\n            const CSMWorld::Resources &mSoundFiles;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds,\n                             const CSMWorld::Resources &soundfiles);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/soundgencheck.cpp",
    "content": "#include \"soundgencheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/refiddata.hpp\"\n#include \"../world/universalid.hpp\"\n\nCSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,\n                                                 const CSMWorld::IdCollection<ESM::Sound> &sounds,\n                                                 const CSMWorld::RefIdCollection &objects)\n    : mSoundGens(soundGens),\n      mSounds(sounds),\n      mObjects(objects)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::SoundGenCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mSoundGens.getSize();\n}\n\nvoid CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages)\n{\n    const CSMWorld::Record<ESM::SoundGenerator> &record = mSoundGens.getRecord(stage);\n    \n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::SoundGenerator& soundGen = record.get();\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId);\n\n    if (!soundGen.mCreature.empty())\n    {\n        CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature);\n        if (creatureIndex.first == -1)\n        {\n            messages.add(id, \"Creature '\" + soundGen.mCreature + \"' doesn't exist\", \"\", CSMDoc::Message::Severity_Error);\n        }\n        else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature)\n        {\n            messages.add(id, \"'\" + soundGen.mCreature + \"' is not a creature\", \"\", CSMDoc::Message::Severity_Error);\n        }\n    }\n\n    if (soundGen.mSound.empty())\n    {\n        messages.add(id, \"Sound is missing\", \"\", CSMDoc::Message::Severity_Error);\n    }\n    else if (mSounds.searchId(soundGen.mSound) == -1)\n    {\n        messages.add(id, \"Sound '\" + soundGen.mSound + \"' doesn't exist\", \"\", CSMDoc::Message::Severity_Error);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/soundgencheck.hpp",
    "content": "#ifndef CSM_TOOLS_SOUNDGENCHECK_HPP\n#define CSM_TOOLS_SOUNDGENCHECK_HPP\n\n#include \"../world/data.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that sound gen records are internally consistent\n    class SoundGenCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens;\n            const CSMWorld::IdCollection<ESM::Sound> &mSounds;\n            const CSMWorld::RefIdCollection &mObjects;\n            bool mIgnoreBaseRecords;\n\n        public:\n            SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,\n                               const CSMWorld::IdCollection<ESM::Sound> &sounds,\n                               const CSMWorld::RefIdCollection &objects);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform(int stage, CSMDoc::Messages &messages) override;\n            ///< Messages resulting from this stage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/spellcheck.cpp",
    "content": "#include \"spellcheck.hpp\"\n\n#include <sstream>\n#include <map>\n\n#include <components/esm/loadspel.hpp>\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/universalid.hpp\"\n\nCSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells)\n: mSpells (spells)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::SpellCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mSpells.getSize();\n}\n\nvoid CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::Spell>& record = mSpells.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    const ESM::Spell& spell = record.get();\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);\n\n    // test for empty name\n    if (spell.mName.empty())\n        messages.add(id, \"Name is missing\", \"\", CSMDoc::Message::Severity_Error);\n\n    // test for invalid cost values\n    if (spell.mData.mCost<0)\n        messages.add(id, \"Spell cost is negative\", \"\", CSMDoc::Message::Severity_Error);\n\n    /// \\todo check data members that can't be edited in the table view\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/spellcheck.hpp",
    "content": "#ifndef CSM_TOOLS_SPELLCHECK_H\n#define CSM_TOOLS_SPELLCHECK_H\n\n#include <components/esm/loadspel.hpp>\n\n#include \"../world/idcollection.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: make sure that spell records are internally consistent\n    class SpellCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::Spell>& mSpells;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells);\n\n            int setup() override;\n            ///< \\return number of steps\n\n            void perform (int stage, CSMDoc::Messages& messages) override;\n            ///< Messages resulting from this tage will be appended to \\a messages.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/startscriptcheck.cpp",
    "content": "#include \"startscriptcheck.hpp\"\n\n#include \"../prefs/state.hpp\"\n\n#include <components/misc/stringops.hpp>\n\nCSMTools::StartScriptCheckStage::StartScriptCheckStage (\n    const CSMWorld::IdCollection<ESM::StartScript>& startScripts,\n    const CSMWorld::IdCollection<ESM::Script>& scripts)\n: mStartScripts (startScripts), mScripts (scripts)\n{\n    mIgnoreBaseRecords = false;\n}\n\nvoid CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<ESM::StartScript>& record = mStartScripts.getRecord (stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())\n        return;\n\n    std::string scriptId = record.get().mId;\n\n    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId);\n\n    if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1)\n        messages.add(id, \"Start script \" + scriptId + \" does not exist\", \"\", CSMDoc::Message::Severity_Error);\n}\n\nint CSMTools::StartScriptCheckStage::setup()\n{\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mStartScripts.getSize();\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/startscriptcheck.hpp",
    "content": "#ifndef CSM_TOOLS_STARTSCRIPTCHECK_H\n#define CSM_TOOLS_STARTSCRIPTCHECK_H\n\n#include <components/esm/loadsscr.hpp>\n#include <components/esm/loadscpt.hpp>\n\n#include \"../doc/stage.hpp\"\n\n#include \"../world/idcollection.hpp\"\n\nnamespace CSMTools\n{\n    class StartScriptCheckStage : public CSMDoc::Stage\n    {\n            const CSMWorld::IdCollection<ESM::StartScript>& mStartScripts;\n            const CSMWorld::IdCollection<ESM::Script>& mScripts;\n            bool mIgnoreBaseRecords;\n\n        public:\n\n            StartScriptCheckStage (const CSMWorld::IdCollection<ESM::StartScript>& startScripts,\n                const CSMWorld::IdCollection<ESM::Script>& scripts);\n\n            void perform(int stage, CSMDoc::Messages& messages) override;\n            int setup() override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/tools.cpp",
    "content": "#include \"tools.hpp\"\n\n#include <QThreadPool>\n\n#include \"../doc/state.hpp\"\n#include \"../doc/operation.hpp\"\n#include \"../doc/document.hpp\"\n\n#include \"../world/data.hpp\"\n#include \"../world/universalid.hpp\"\n\n#include \"reportmodel.hpp\"\n#include \"mandatoryid.hpp\"\n#include \"skillcheck.hpp\"\n#include \"classcheck.hpp\"\n#include \"factioncheck.hpp\"\n#include \"racecheck.hpp\"\n#include \"soundcheck.hpp\"\n#include \"regioncheck.hpp\"\n#include \"birthsigncheck.hpp\"\n#include \"spellcheck.hpp\"\n#include \"referenceablecheck.hpp\"\n#include \"scriptcheck.hpp\"\n#include \"bodypartcheck.hpp\"\n#include \"referencecheck.hpp\"\n#include \"startscriptcheck.hpp\"\n#include \"searchoperation.hpp\"\n#include \"pathgridcheck.hpp\"\n#include \"soundgencheck.hpp\"\n#include \"magiceffectcheck.hpp\"\n#include \"mergeoperation.hpp\"\n#include \"gmstcheck.hpp\"\n#include \"topicinfocheck.hpp\"\n#include \"journalcheck.hpp\"\n#include \"enchantmentcheck.hpp\"\n\nCSMDoc::OperationHolder *CSMTools::Tools::get (int type)\n{\n    switch (type)\n    {\n        case CSMDoc::State_Verifying: return &mVerifier;\n        case CSMDoc::State_Searching: return &mSearch;\n        case CSMDoc::State_Merging: return &mMerge;\n    }\n\n    return nullptr;\n}\n\nconst CSMDoc::OperationHolder *CSMTools::Tools::get (int type) const\n{\n    return const_cast<Tools *> (this)->get (type);\n}\n\nCSMDoc::OperationHolder *CSMTools::Tools::getVerifier()\n{\n    if (!mVerifierOperation)\n    {\n        mVerifierOperation = new CSMDoc::Operation (CSMDoc::State_Verifying, false);\n\n        connect (&mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));\n        connect (&mVerifier, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));\n        connect (&mVerifier, SIGNAL (reportMessage (const CSMDoc::Message&, int)),\n            this, SLOT (verifierMessage (const CSMDoc::Message&, int)));\n\n        std::vector<std::string> mandatoryIds {\"Day\", \"DaysPassed\", \"GameHour\", \"Month\", \"PCRace\"};\n\n        mVerifierOperation->appendStage (new MandatoryIdStage (mData.getGlobals(),\n            CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));\n\n        mVerifierOperation->appendStage (new SkillCheckStage (mData.getSkills()));\n\n        mVerifierOperation->appendStage (new ClassCheckStage (mData.getClasses()));\n\n        mVerifierOperation->appendStage (new FactionCheckStage (mData.getFactions()));\n\n        mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces()));\n\n        mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds(), mData.getResources (CSMWorld::UniversalId::Type_SoundsRes)));\n\n        mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions()));\n\n        mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns(), mData.getResources (CSMWorld::UniversalId::Type_Textures)));\n\n        mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells()));\n\n        mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(), \n                                                                      mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons),\n                                                                      mData.getBodyParts()));\n\n        mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));\n\n        mVerifierOperation->appendStage (new ScriptCheckStage (mDocument));\n\n        mVerifierOperation->appendStage (new StartScriptCheckStage (mData.getStartScripts(), mData.getScripts()));\n\n        mVerifierOperation->appendStage(\n            new BodyPartCheckStage(\n                mData.getBodyParts(),\n                mData.getResources(\n                    CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )),\n                mData.getRaces() ));\n\n        mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids()));\n\n        mVerifierOperation->appendStage (new SoundGenCheckStage (mData.getSoundGens(),\n                                                                 mData.getSounds(),\n                                                                 mData.getReferenceables()));\n\n        mVerifierOperation->appendStage (new MagicEffectCheckStage (mData.getMagicEffects(),\n                                                                    mData.getSounds(),\n                                                                    mData.getReferenceables(),\n                                                                    mData.getResources (CSMWorld::UniversalId::Type_Icons),\n                                                                    mData.getResources (CSMWorld::UniversalId::Type_Textures)));\n\n        mVerifierOperation->appendStage (new GmstCheckStage (mData.getGmsts()));\n\n        mVerifierOperation->appendStage (new TopicInfoCheckStage (mData.getTopicInfos(),\n                                                                  mData.getCells(),\n                                                                  mData.getClasses(),\n                                                                  mData.getFactions(),\n                                                                  mData.getGmsts(),\n                                                                  mData.getGlobals(),\n                                                                  mData.getJournals(),\n                                                                  mData.getRaces(),\n                                                                  mData.getRegions(),\n                                                                  mData.getTopics(),\n                                                                  mData.getReferenceables().getDataSet(),\n                                                                  mData.getResources (CSMWorld::UniversalId::Type_SoundsRes)));\n\n        mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos()));\n\n        mVerifierOperation->appendStage (new EnchantmentCheckStage(mData.getEnchantments()));\n\n        mVerifier.setOperation (mVerifierOperation);\n    }\n\n    return &mVerifier;\n}\n\nCSMTools::Tools::Tools (CSMDoc::Document& document, ToUTF8::FromType encoding)\n: mDocument (document), mData (document.getData()), mVerifierOperation (nullptr),\n  mSearchOperation (nullptr), mMergeOperation (nullptr), mNextReportNumber (0), mEncoding (encoding)\n{\n    // index 0: load error log\n    mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));\n    mActiveReports.insert (std::make_pair (CSMDoc::State_Loading, 0));\n\n    connect (&mSearch, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));\n    connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));\n    connect (&mSearch, SIGNAL (reportMessage (const CSMDoc::Message&, int)),\n        this, SLOT (verifierMessage (const CSMDoc::Message&, int)));\n\n    connect (&mMerge, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));\n    connect (&mMerge, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));\n    // don't need to connect report message, since there are no messages for merge\n}\n\nCSMTools::Tools::~Tools()\n{\n    if (mVerifierOperation)\n    {\n        mVerifier.abortAndWait();\n        delete mVerifierOperation;\n    }\n\n    if (mSearchOperation)\n    {\n        mSearch.abortAndWait();\n        delete mSearchOperation;\n    }\n\n    if (mMergeOperation)\n    {\n        mMerge.abortAndWait();\n        delete mMergeOperation;\n    }\n\n    for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)\n        delete iter->second;\n}\n\nCSMWorld::UniversalId CSMTools::Tools::runVerifier (const CSMWorld::UniversalId& reportId)\n{\n    int reportNumber = reportId.getType()==CSMWorld::UniversalId::Type_VerificationResults ?\n        reportId.getIndex() : mNextReportNumber++;\n\n    if (mReports.find (reportNumber)==mReports.end())\n        mReports.insert (std::make_pair (reportNumber, new ReportModel));\n\n    mActiveReports[CSMDoc::State_Verifying] = reportNumber;\n\n    getVerifier()->start();\n\n    return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, reportNumber);\n}\n\nCSMWorld::UniversalId CSMTools::Tools::newSearch()\n{\n    mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true, false)));\n\n    return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1);\n}\n\nvoid CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Search& search)\n{\n    mActiveReports[CSMDoc::State_Searching] = searchId.getIndex();\n\n    if (!mSearchOperation)\n    {\n        mSearchOperation = new SearchOperation (mDocument);\n        mSearch.setOperation (mSearchOperation);\n    }\n\n    mSearchOperation->configure (search);\n\n    mSearch.start();\n}\n\nvoid CSMTools::Tools::runMerge (std::unique_ptr<CSMDoc::Document> target)\n{\n    // not setting an active report, because merge does not produce messages\n\n    if (!mMergeOperation)\n    {\n        mMergeOperation = new MergeOperation (mDocument, mEncoding);\n        mMerge.setOperation (mMergeOperation);\n        connect (mMergeOperation, SIGNAL (mergeDone (CSMDoc::Document*)),\n            this, SIGNAL (mergeDone (CSMDoc::Document*)));\n    }\n\n    target->flagAsDirty();\n\n    mMergeOperation->setTarget (std::move(target));\n\n    mMerge.start();\n}\n\nvoid CSMTools::Tools::abortOperation (int type)\n{\n    if (CSMDoc::OperationHolder *operation = get (type))\n        operation->abort();\n}\n\nint CSMTools::Tools::getRunningOperations() const\n{\n    static const int sOperations[] =\n    {\n       CSMDoc::State_Verifying,\n       CSMDoc::State_Searching,\n       CSMDoc::State_Merging,\n        -1\n    };\n\n    int result = 0;\n\n    for (int i=0; sOperations[i]!=-1; ++i)\n        if (const CSMDoc::OperationHolder *operation = get (sOperations[i]))\n            if (operation->isRunning())\n                result |= sOperations[i];\n\n    return result;\n}\n\nCSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id)\n{\n    if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults &&\n        id.getType()!=CSMWorld::UniversalId::Type_LoadErrorLog &&\n        id.getType()!=CSMWorld::UniversalId::Type_Search)\n        throw std::logic_error (\"invalid request for report model: \" + id.toString());\n\n    return mReports.at (id.getIndex());\n}\n\nvoid CSMTools::Tools::verifierMessage (const CSMDoc::Message& message, int type)\n{\n    std::map<int, int>::iterator iter = mActiveReports.find (type);\n\n    if (iter!=mActiveReports.end())\n        mReports[iter->second]->add (message);\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/tools.hpp",
    "content": "#ifndef CSM_TOOLS_TOOLS_H\n#define CSM_TOOLS_TOOLS_H\n\n#include <memory>\n#include <map>\n\n#include <components/to_utf8/to_utf8.hpp>\n\n#include <QObject>\n\n#include <boost/filesystem/path.hpp>\n\n#include \"../doc/operationholder.hpp\"\n\nnamespace CSMWorld\n{\n    class Data;\n    class UniversalId;\n}\n\nnamespace CSMDoc\n{\n    class Operation;\n    class Document;\n}\n\nnamespace CSMTools\n{\n    class ReportModel;\n    class Search;\n    class SearchOperation;\n    class MergeOperation;\n\n    class Tools : public QObject\n    {\n            Q_OBJECT\n\n            CSMDoc::Document& mDocument;\n            CSMWorld::Data& mData;\n            CSMDoc::Operation *mVerifierOperation;\n            CSMDoc::OperationHolder mVerifier;\n            SearchOperation *mSearchOperation;\n            CSMDoc::OperationHolder mSearch;\n            MergeOperation *mMergeOperation;\n            CSMDoc::OperationHolder mMerge;\n            std::map<int, ReportModel *> mReports;\n            int mNextReportNumber;\n            std::map<int, int> mActiveReports; // type, report number\n            ToUTF8::FromType mEncoding;\n\n            // not implemented\n            Tools (const Tools&);\n            Tools& operator= (const Tools&);\n\n            CSMDoc::OperationHolder *getVerifier();\n\n            CSMDoc::OperationHolder *get (int type);\n            ///< Returns a 0-pointer, if operation hasn't been used yet.\n\n            const CSMDoc::OperationHolder *get (int type) const;\n            ///< Returns a 0-pointer, if operation hasn't been used yet.\n\n        public:\n\n            Tools (CSMDoc::Document& document, ToUTF8::FromType encoding);\n\n            virtual ~Tools();\n\n            /// \\param reportId If a valid VerificationResults ID, run verifier for the\n            /// specified report instead of creating a new one.\n            ///\n            /// \\return ID of the report for this verification run\n            CSMWorld::UniversalId runVerifier (const CSMWorld::UniversalId& reportId = CSMWorld::UniversalId());\n\n            /// Return ID of the report for this search.\n            CSMWorld::UniversalId newSearch();\n\n            void runSearch (const CSMWorld::UniversalId& searchId, const Search& search);\n\n            void runMerge (std::unique_ptr<CSMDoc::Document> target);\n\n            void abortOperation (int type);\n            ///< \\attention The operation is not aborted immediately.\n\n            int getRunningOperations() const;\n\n            ReportModel *getReport (const CSMWorld::UniversalId& id);\n            ///< The ownership of the returned report is not transferred.\n\n        private slots:\n\n            void verifierMessage (const CSMDoc::Message& message, int type);\n\n        signals:\n\n            void progress (int current, int max, int type);\n\n            void done (int type, bool failed);\n\n            /// \\attention When this signal is emitted, *this hands over the ownership of the\n            /// document. This signal must be handled to avoid a leak.\n            void mergeDone (CSMDoc::Document *document);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/tools/topicinfocheck.cpp",
    "content": "#include \"topicinfocheck.hpp\"\n\n#include <sstream>\n\n#include \"../prefs/state.hpp\"\n\n#include \"../world/infoselectwrapper.hpp\"\n\nCSMTools::TopicInfoCheckStage::TopicInfoCheckStage(\n    const CSMWorld::InfoCollection& topicInfos,\n    const CSMWorld::IdCollection<CSMWorld::Cell>& cells,\n    const CSMWorld::IdCollection<ESM::Class>& classes,\n    const CSMWorld::IdCollection<ESM::Faction>& factions,\n    const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,\n    const CSMWorld::IdCollection<ESM::Global>& globals,\n    const CSMWorld::IdCollection<ESM::Dialogue>& journals,\n    const CSMWorld::IdCollection<ESM::Race>& races,\n    const CSMWorld::IdCollection<ESM::Region>& regions,\n    const CSMWorld::IdCollection<ESM::Dialogue> &topics,\n    const CSMWorld::RefIdData& referencables,\n    const CSMWorld::Resources& soundFiles)\n    : mTopicInfos(topicInfos),\n      mCells(cells),\n      mClasses(classes),\n      mFactions(factions),\n      mGameSettings(gmsts),\n      mGlobals(globals),\n      mJournals(journals),\n      mRaces(races),\n      mRegions(regions),\n      mTopics(topics),\n      mReferencables(referencables),\n      mSoundFiles(soundFiles)\n{\n    mIgnoreBaseRecords = false;\n}\n\nint CSMTools::TopicInfoCheckStage::setup()\n{\n    // Generate list of cell names for reference checking\n\n    mCellNames.clear();\n    for (int i = 0; i < mCells.getSize(); ++i)\n    {\n        const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mCells.getRecord(i);\n\n        if (cellRecord.isDeleted())\n            continue;\n\n        mCellNames.insert(cellRecord.get().mName);\n    }\n    // Cell names can also include region names\n    for (int i = 0; i < mRegions.getSize(); ++i)\n    {\n        const CSMWorld::Record<ESM::Region>& regionRecord = mRegions.getRecord(i);\n\n        if (regionRecord.isDeleted())\n            continue;\n\n        mCellNames.insert(regionRecord.get().mName);\n    }\n    // Default cell name\n    int index = mGameSettings.searchId(\"sDefaultCellname\");\n    if (index != -1)\n    {\n        const CSMWorld::Record<ESM::GameSetting>& gmstRecord = mGameSettings.getRecord(index);\n\n        if (!gmstRecord.isDeleted() && gmstRecord.get().mValue.getType() == ESM::VT_String)\n        {\n            mCellNames.insert(gmstRecord.get().mValue.getString());\n        }\n    }\n\n    mIgnoreBaseRecords = CSMPrefs::get()[\"Reports\"][\"ignore-base-records\"].isTrue();\n\n    return mTopicInfos.getSize();\n}\n\nvoid CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& messages)\n{\n    const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage);\n\n    // Skip \"Base\" records (setting!) and \"Deleted\" records\n    if ((mIgnoreBaseRecords && infoRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || infoRecord.isDeleted())\n        return;\n\n    const CSMWorld::Info& topicInfo = infoRecord.get();\n\n    // There should always be a topic that matches\n    int topicIndex = mTopics.searchId(topicInfo.mTopicId);\n\n    const CSMWorld::Record<ESM::Dialogue>& topicRecord = mTopics.getRecord(topicIndex);\n\n    if (topicRecord.isDeleted())\n        return;\n\n    const ESM::Dialogue& topic = topicRecord.get();\n\n    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_TopicInfo, topicInfo.mId);\n\n    // Check fields\n\n    if (!topicInfo.mActor.empty())\n    {\n        verifyActor(topicInfo.mActor, id, messages);\n    }\n\n    if (!topicInfo.mClass.empty())\n    {\n        verifyId(topicInfo.mClass, mClasses, id, messages);\n    }\n\n    if (!topicInfo.mCell.empty())\n    {\n        verifyCell(topicInfo.mCell, id, messages);\n    }\n\n    if (!topicInfo.mFaction.empty() && !topicInfo.mFactionLess)\n    {\n        if (verifyId(topicInfo.mFaction, mFactions, id, messages))\n        {\n            verifyFactionRank(topicInfo.mFaction, topicInfo.mData.mRank, id, messages);\n        }\n    }\n\n    if (!topicInfo.mPcFaction.empty())\n    {\n        if (verifyId(topicInfo.mPcFaction, mFactions, id, messages))\n        {\n            verifyFactionRank(topicInfo.mPcFaction, topicInfo.mData.mPCrank, id, messages);\n        }\n    }\n\n    if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)\n    {\n        messages.add(id, \"Gender is invalid\", \"\", CSMDoc::Message::Severity_Error);\n    }\n\n    if (!topicInfo.mRace.empty())\n    {\n        verifyId(topicInfo.mRace, mRaces, id, messages);\n    }\n\n    if (!topicInfo.mSound.empty())\n    {\n        verifySound(topicInfo.mSound, id, messages);\n    }\n\n    if (topicInfo.mResponse.empty() && topic.mType != ESM::Dialogue::Voice)\n    {\n        messages.add(id, \"Response is empty\", \"\", CSMDoc::Message::Severity_Warning);\n    }\n\n    // Check info conditions\n\n    for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator it = topicInfo.mSelects.begin();\n         it != topicInfo.mSelects.end(); ++it)\n    {\n        verifySelectStruct((*it), id, messages);\n    }\n}\n\n// Verification functions\n\nbool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,\n    CSMDoc::Messages& messages)\n{\n    CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);\n\n    if (index.first == -1)\n    {\n        messages.add(id, \"Actor '\" + actor + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n    else if (mReferencables.getRecord(index).isDeleted())\n    {\n        messages.add(id, \"Deleted actor '\" + actor + \"' is being referenced\", \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n    else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature)\n    {\n        CSMWorld::UniversalId tempId(index.second, actor);\n        std::ostringstream stream;\n        stream << \"Object '\" << actor << \"' has invalid type \" << tempId.getTypeName() << \" (an actor must be an NPC or a creature)\"; \n        messages.add(id, stream.str(), \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n\n    return true;\n}\n\nbool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,\n    CSMDoc::Messages& messages)\n{\n    if (mCellNames.find(cell) == mCellNames.end())\n    {\n        messages.add(id, \"Cell '\" + cell + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n\n    return true;\n}\n\nbool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& factionName, int rank, const CSMWorld::UniversalId& id,\n    CSMDoc::Messages& messages)\n{\n    if (rank < -1)\n    {\n        std::ostringstream stream;\n        stream << \"Faction rank is set to \" << rank << \", but it should be set to -1 if there are no rank requirements\";\n        messages.add(id, stream.str(), \"\", CSMDoc::Message::Severity_Warning);\n        return false;\n    }\n\n    int index = mFactions.searchId(factionName);\n\n    const ESM::Faction &faction = mFactions.getRecord(index).get();\n\n    int limit = 0;\n    for (; limit < 10; ++limit)\n    {\n        if (faction.mRanks[limit].empty())\n            break;\n    }\n\n    if (rank >= limit)\n    {\n        std::ostringstream stream;\n        stream << \"Faction rank is set to \" << rank << \" which is more than the maximum of \" << limit - 1\n               << \" for the '\" << factionName << \"' faction\";\n\n        messages.add(id, stream.str(), \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n\n    return true;\n}\n\nbool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,\n    CSMDoc::Messages& messages)\n{\n    CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);\n\n    if (index.first == -1)\n    {\n        messages.add(id, (\"Item '\" + item + \"' does not exist\"), \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n    else if (mReferencables.getRecord(index).isDeleted())\n    {\n        messages.add(id, (\"Deleted item '\" + item + \"' is being referenced\"), \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n    else\n    {\n        switch (index.second)\n        {\n            case CSMWorld::UniversalId::Type_Potion:\n            case CSMWorld::UniversalId::Type_Apparatus:\n            case CSMWorld::UniversalId::Type_Armor:\n            case CSMWorld::UniversalId::Type_Book:\n            case CSMWorld::UniversalId::Type_Clothing:\n            case CSMWorld::UniversalId::Type_Ingredient:\n            case CSMWorld::UniversalId::Type_Light:\n            case CSMWorld::UniversalId::Type_Lockpick:\n            case CSMWorld::UniversalId::Type_Miscellaneous:\n            case CSMWorld::UniversalId::Type_Probe:\n            case CSMWorld::UniversalId::Type_Repair:\n            case CSMWorld::UniversalId::Type_Weapon:\n            case CSMWorld::UniversalId::Type_ItemLevelledList:\n                break;\n\n            default:\n            {\n                CSMWorld::UniversalId tempId(index.second, item);\n                std::ostringstream stream;\n                stream << \"Object '\" << item << \"' has invalid type \" << tempId.getTypeName() << \" (an item can be a potion, an armor piece, a book and so on)\"; \n                messages.add(id, stream.str(), \"\", CSMDoc::Message::Severity_Error);\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\nbool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::SelectStruct& select,\n    const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)\n{\n    CSMWorld::ConstInfoSelectWrapper infoCondition(select);\n\n    if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)\n    {\n        messages.add(id, \"Invalid condition '\" + infoCondition.toString() + \"'\", \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n    else if (!infoCondition.variantTypeIsValid())\n    {\n        std::ostringstream stream;\n        stream << \"Value of condition '\" << infoCondition.toString() << \"' has invalid \";\n\n        switch (select.mValue.getType())\n        {\n            case ESM::VT_None:   stream << \"None\"; break;\n            case ESM::VT_Short:  stream << \"Short\"; break;\n            case ESM::VT_Int:    stream << \"Int\"; break;\n            case ESM::VT_Long:   stream << \"Long\"; break;\n            case ESM::VT_Float:  stream << \"Float\"; break;\n            case ESM::VT_String: stream << \"String\"; break;\n            default:             stream << \"unknown\"; break;\n        }\n        stream << \" type\";\n\n        messages.add(id, stream.str(), \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n    else if (infoCondition.conditionIsAlwaysTrue())\n    {\n        messages.add(id, \"Condition '\" + infoCondition.toString() + \"' is always true\", \"\", CSMDoc::Message::Severity_Warning);\n        return false;\n    }\n    else if (infoCondition.conditionIsNeverTrue())\n    {\n        messages.add(id, \"Condition '\" + infoCondition.toString() + \"' is never true\", \"\", CSMDoc::Message::Severity_Warning);\n        return false;\n    }\n\n    // Id checks\n    if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global &&\n        !verifyId(infoCondition.getVariableName(), mGlobals, id, messages))\n    {\n        return false;\n    }\n    else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal &&\n        !verifyId(infoCondition.getVariableName(), mJournals, id, messages))\n    {\n        return false;\n    }\n    else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item &&\n        !verifyItem(infoCondition.getVariableName(), id, messages))\n    {\n        return false;\n    }\n    else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead &&\n        !verifyActor(infoCondition.getVariableName(), id, messages))\n    {\n        return false;\n    }\n    else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId &&\n        !verifyActor(infoCondition.getVariableName(), id, messages))\n    {\n        return false;\n    }\n    else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction &&\n        !verifyId(infoCondition.getVariableName(), mFactions, id, messages))\n    {\n        return false;\n    }\n    else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass &&\n        !verifyId(infoCondition.getVariableName(), mClasses, id, messages))\n    {\n        return false;\n    }\n    else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace &&\n        !verifyId(infoCondition.getVariableName(), mRaces, id, messages))\n    {\n        return false;\n    }\n    else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell &&\n        !verifyCell(infoCondition.getVariableName(), id, messages))\n    {\n        return false;\n    }\n\n    return true;\n}\n\nbool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,\n    CSMDoc::Messages& messages)\n{\n    if (mSoundFiles.searchId(sound) == -1)\n    {\n        messages.add(id, \"Sound file '\" + sound + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n\n    return true;\n}\n\ntemplate <typename T>\nbool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,\n    const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)\n{\n    int index = collection.searchId(name);\n\n    if (index == -1)\n    {\n        messages.add(id, T::getRecordType() + \" '\" + name + \"' does not exist\", \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n    else if (collection.getRecord(index).isDeleted())\n    {\n        messages.add(id, \"Deleted \" + T::getRecordType() + \" record '\" + name + \"' is being referenced\", \"\", CSMDoc::Message::Severity_Error);\n        return false;\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "apps/opencs/model/tools/topicinfocheck.hpp",
    "content": "#ifndef CSM_TOOLS_TOPICINFOCHECK_HPP\n#define CSM_TOOLS_TOPICINFOCHECK_HPP\n\n#include <set>\n\n#include <components/esm/loadclas.hpp>\n#include <components/esm/loaddial.hpp>\n#include <components/esm/loadfact.hpp>\n#include <components/esm/loadglob.hpp>\n#include <components/esm/loadgmst.hpp>\n#include <components/esm/loadrace.hpp>\n#include <components/esm/loadregn.hpp>\n\n#include \"../world/cell.hpp\"\n#include \"../world/idcollection.hpp\"\n#include \"../world/infocollection.hpp\"\n#include \"../world/refiddata.hpp\"\n#include \"../world/resources.hpp\"\n\n#include \"../doc/stage.hpp\"\n\nnamespace CSMTools\n{\n    /// \\brief VerifyStage: check topics\n    class TopicInfoCheckStage : public CSMDoc::Stage\n    {\n    public:\n\n        TopicInfoCheckStage(\n            const CSMWorld::InfoCollection& topicInfos,\n            const CSMWorld::IdCollection<CSMWorld::Cell>& cells,\n            const CSMWorld::IdCollection<ESM::Class>& classes,\n            const CSMWorld::IdCollection<ESM::Faction>& factions,\n            const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,\n            const CSMWorld::IdCollection<ESM::Global>& globals,\n            const CSMWorld::IdCollection<ESM::Dialogue>& journals,\n            const CSMWorld::IdCollection<ESM::Race>& races,\n            const CSMWorld::IdCollection<ESM::Region>& regions,\n            const CSMWorld::IdCollection<ESM::Dialogue>& topics,\n            const CSMWorld::RefIdData& referencables,\n            const CSMWorld::Resources& soundFiles);\n\n        int setup() override;\n        ///< \\return number of steps\n\n        void perform(int step, CSMDoc::Messages& messages) override;\n        ///< Messages resulting from this stage will be appended to \\a messages\n\n    private:\n\n        const CSMWorld::InfoCollection& mTopicInfos;\n\n        const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;\n        const CSMWorld::IdCollection<ESM::Class>& mClasses;\n        const CSMWorld::IdCollection<ESM::Faction>& mFactions;\n        const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;\n        const CSMWorld::IdCollection<ESM::Global>& mGlobals;\n        const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;\n        const CSMWorld::IdCollection<ESM::Race>& mRaces;\n        const CSMWorld::IdCollection<ESM::Region>& mRegions;\n        const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;\n\n        const CSMWorld::RefIdData& mReferencables;\n        const CSMWorld::Resources& mSoundFiles;\n\n        std::set<std::string> mCellNames;\n\n        bool mIgnoreBaseRecords;\n\n        // These return false when not successful and write an error\n        bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);\n        bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);\n        bool verifyFactionRank(const std::string& name, int rank, const CSMWorld::UniversalId& id,\n            CSMDoc::Messages& messages);\n        bool verifyItem(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);\n        bool verifySelectStruct(const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id,\n            CSMDoc::Messages& messages);\n        bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);\n\n        template <typename T>\n        bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,\n            const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/actoradapter.cpp",
    "content": "#include \"actoradapter.hpp\"\n\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadclot.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/loadrace.hpp>\n#include <components/esm/mappings.hpp>\n#include <components/sceneutil/actorutil.hpp>\n\n#include \"data.hpp\"\n\nnamespace CSMWorld\n{\n    const std::string& ActorAdapter::RaceData::getId() const\n    {\n        return mId;\n    }\n\n    bool ActorAdapter::RaceData::isBeast() const\n    {\n        return mIsBeast;\n    }\n\n    ActorAdapter::RaceData::RaceData()\n    {\n        mIsBeast = false;\n    }\n\n    bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const\n    {\n        switch (type)\n        {\n            case ESM::PRT_Skirt:\n            case ESM::PRT_Shield:\n            case ESM::PRT_RPauldron:\n            case ESM::PRT_LPauldron:\n            case ESM::PRT_Weapon:\n                return false;\n            default:\n                return true;\n        }\n    }\n\n    const std::string& ActorAdapter::RaceData::getFemalePart(ESM::PartReferenceType index) const\n    {\n        return mFemaleParts[ESM::getMeshPart(index)];\n    }\n\n    const std::string& ActorAdapter::RaceData::getMalePart(ESM::PartReferenceType index) const\n    {\n        return mMaleParts[ESM::getMeshPart(index)];\n    }\n\n    bool ActorAdapter::RaceData::hasDependency(const std::string& id) const\n    {\n        return mDependencies.find(id) != mDependencies.end();\n    }\n\n    void ActorAdapter::RaceData::setFemalePart(ESM::BodyPart::MeshPart index, const std::string& partId)\n    {\n        mFemaleParts[index] = partId;\n        addOtherDependency(partId);\n    }\n\n    void ActorAdapter::RaceData::setMalePart(ESM::BodyPart::MeshPart index, const std::string& partId)\n    {\n        mMaleParts[index] = partId;\n        addOtherDependency(partId);\n    }\n\n    void ActorAdapter::RaceData::addOtherDependency(const std::string& id)\n    {\n        if (!id.empty()) mDependencies.emplace(id);\n    }\n\n    void ActorAdapter::RaceData::reset_data(const std::string& id, bool isBeast)\n    {\n        mId = id;\n        mIsBeast = isBeast;\n        for (auto& str : mFemaleParts)\n            str.clear();\n        for (auto& str : mMaleParts)\n            str.clear();\n        mDependencies.clear();\n\n        // Mark self as a dependency\n        addOtherDependency(id);\n    }\n\n\n    ActorAdapter::ActorData::ActorData()\n    {\n        mCreature = false;\n        mFemale = false;\n    }\n\n    const std::string& ActorAdapter::ActorData::getId() const\n    {\n        return mId;\n    }\n\n    bool ActorAdapter::ActorData::isCreature() const\n    {\n        return mCreature;\n    }\n\n    bool ActorAdapter::ActorData::isFemale() const\n    {\n        return mFemale;\n    }\n\n    std::string ActorAdapter::ActorData::getSkeleton() const\n    {\n        if (mCreature || !mSkeletonOverride.empty())\n            return \"meshes\\\\\" + mSkeletonOverride;\n\n        bool firstPerson = false;\n        bool beast = mRaceData ? mRaceData->isBeast() : false;\n        bool werewolf = false;\n\n        return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf);\n    }\n\n    const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const\n    {\n        auto it = mParts.find(index);\n        if (it == mParts.end())\n        {\n            if (mRaceData && mRaceData->handlesPart(index))\n            {\n                if (mFemale)\n                {\n                    // Note: we should use male parts for females as fallback\n                    const std::string femalePart = mRaceData->getFemalePart(index);\n                    if (!femalePart.empty())\n                        return femalePart;\n                }\n\n                return mRaceData->getMalePart(index);\n            }\n\n            return \"\";\n        }\n\n        const std::string& partName = it->second.first;\n        return partName;\n    }\n\n    bool ActorAdapter::ActorData::hasDependency(const std::string& id) const\n    {\n        return mDependencies.find(id) != mDependencies.end();\n    }\n\n    void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId, int priority)\n    {\n        auto it = mParts.find(index);\n        if (it != mParts.end())\n        {\n            if (it->second.second >= priority)\n                return;\n        }\n\n        mParts[index] = std::make_pair(partId, priority);\n        addOtherDependency(partId);\n    }\n\n    void ActorAdapter::ActorData::addOtherDependency(const std::string& id)\n    {\n        if (!id.empty()) mDependencies.emplace(id);\n    }\n\n    void ActorAdapter::ActorData::reset_data(const std::string& id, const std::string& skeleton, bool isCreature, bool isFemale, RaceDataPtr raceData)\n    {\n        mId = id;\n        mCreature = isCreature;\n        mFemale = isFemale;\n        mSkeletonOverride = skeleton;\n        mRaceData = raceData;\n        mParts.clear();\n        mDependencies.clear();\n\n        // Mark self and race as a dependency\n        addOtherDependency(id);\n        if (raceData) addOtherDependency(raceData->getId());\n    }\n\n\n    ActorAdapter::ActorAdapter(Data& data)\n        : mReferenceables(data.getReferenceables())\n        , mRaces(data.getRaces())\n        , mBodyParts(data.getBodyParts())\n    {\n        // Setup qt slots and signals\n        QAbstractItemModel* refModel = data.getTableModel(UniversalId::Type_Referenceable);\n        connect(refModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)),\n                this, SLOT(handleReferenceablesInserted(const QModelIndex&, int, int)));\n        connect(refModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),\n                this, SLOT(handleReferenceableChanged(const QModelIndex&, const QModelIndex&)));\n        connect(refModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),\n                this, SLOT(handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int)));\n\n        QAbstractItemModel* raceModel = data.getTableModel(UniversalId::Type_Race);\n        connect(raceModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)),\n                this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int)));\n        connect(raceModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),\n                this, SLOT(handleRaceChanged(const QModelIndex&, const QModelIndex&)));\n        connect(raceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),\n                this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int)));\n\n        QAbstractItemModel* partModel = data.getTableModel(UniversalId::Type_BodyPart);\n        connect(partModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)),\n                this, SLOT(handleBodyPartsInserted(const QModelIndex&, int, int)));\n        connect(partModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),\n                this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&)));\n        connect(partModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),\n                this, SLOT(handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int)));\n    }\n\n    ActorAdapter::ActorDataPtr ActorAdapter::getActorData(const std::string& id)\n    {\n        // Return cached actor data if it exists\n        ActorDataPtr data = mCachedActors.get(id);\n        if (data)\n        {\n            return data;\n        }\n\n        // Create the actor data\n        data.reset(new ActorData());\n        setupActor(id, data);\n        mCachedActors.insert(id, data);\n        return data;\n    }\n\n    void ActorAdapter::handleReferenceablesInserted(const QModelIndex& parent, int start, int end)\n    {\n        // Only rows added at the top level are pertinent. Others are caught by dataChanged handler.\n        if (!parent.isValid())\n        {\n            for (int row = start; row <= end; ++row)\n            {\n                std::string refId = mReferenceables.getId(row);\n                markDirtyDependency(refId);\n            }\n        }\n\n        // Update affected\n        updateDirty();\n    }\n\n    void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight)\n    {\n        int start = getHighestIndex(topLeft).row();\n        int end = getHighestIndex(botRight).row();\n\n        // A change to record status (ex. Deleted) returns an invalid botRight\n        if (end == -1)\n            end = start;\n\n        // Handle each record\n        for (int row = start; row <= end; ++row)\n        {\n            std::string refId = mReferenceables.getId(row);\n            markDirtyDependency(refId);\n        }\n\n        // Update affected\n        updateDirty();\n    }\n\n    void ActorAdapter::handleReferenceablesAboutToBeRemoved(const QModelIndex& parent, int start, int end)\n    {\n        // Only rows at the top are pertinent.\n        if (!parent.isValid())\n        {\n            for (int row = start; row <= end; ++row)\n            {\n                std::string refId = mReferenceables.getId(row);\n                markDirtyDependency(refId);\n            }\n        }\n    }\n\n    void ActorAdapter::handleReferenceablesRemoved(const QModelIndex& parent, int start, int end)\n    {\n        // Changes specified in handleReferenceablesAboutToBeRemoved\n        updateDirty();\n    }\n\n    void ActorAdapter::handleRacesInserted(const QModelIndex& parent, int start, int end)\n    {\n        // Only rows added at the top are pertinent.\n        if (!parent.isValid())\n        {\n            for (int row = start; row <= end; ++row)\n            {\n                std::string raceId = mReferenceables.getId(row);\n                markDirtyDependency(raceId);\n            }\n        }\n\n        // Update affected\n        updateDirty();\n    }\n\n    void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight)\n    {\n        int start = getHighestIndex(topLeft).row();\n        int end = getHighestIndex(botRight).row();\n\n        // A change to record status (ex. Deleted) returns an invalid botRight\n        if (end == -1)\n            end = start;\n\n        for (int row = start; row <= end; ++row)\n        {\n            std::string raceId = mRaces.getId(row);\n            markDirtyDependency(raceId);\n        }\n\n        // Update affected\n        updateDirty();\n    }\n\n    void ActorAdapter::handleRacesAboutToBeRemoved(const QModelIndex& parent, int start, int end)\n    {\n        // Only changes at the top are pertinent.\n        if (!parent.isValid())\n        {\n            for (int row = start; row <= end; ++row)\n            {\n                std::string raceId = mRaces.getId(row);\n                markDirtyDependency(raceId);\n            }\n        }\n    }\n\n    void ActorAdapter::handleRacesRemoved(const QModelIndex& parent, int start, int end)\n    {\n        // Changes specified in handleRacesAboutToBeRemoved\n        updateDirty();\n    }\n\n    void ActorAdapter::handleBodyPartsInserted(const QModelIndex& parent, int start, int end)\n    {\n        // Only rows added at the top are pertinent.\n        if (!parent.isValid())\n        {\n            for (int row = start; row <= end; ++row)\n            {\n                // Race specified by part may need update\n                auto& record = mBodyParts.getRecord(row);\n                if (!record.isDeleted())\n                {\n                    markDirtyDependency(record.get().mRace);\n                }\n\n                std::string partId = mBodyParts.getId(row);\n                markDirtyDependency(partId);\n            }\n        }\n\n        // Update affected\n        updateDirty();\n    }\n\n    void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight)\n    {\n        int start = getHighestIndex(topLeft).row();\n        int end = getHighestIndex(botRight).row();\n\n        // A change to record status (ex. Deleted) returns an invalid botRight\n        if (end == -1)\n            end = start;\n\n        for (int row = start; row <= end; ++row)\n        {\n            // Race specified by part may need update\n            auto& record = mBodyParts.getRecord(row);\n            if (!record.isDeleted())\n            {\n                markDirtyDependency(record.get().mRace);\n            }\n\n            // Update entries with a tracked dependency\n            std::string partId = mBodyParts.getId(row);\n            markDirtyDependency(partId);\n        }\n\n        // Update affected\n        updateDirty();\n    }\n\n    void ActorAdapter::handleBodyPartsAboutToBeRemoved(const QModelIndex& parent, int start, int end)\n    {\n        // Only changes at the top are pertinent.\n        if (!parent.isValid())\n        {\n            for (int row = start; row <= end; ++row)\n            {\n                std::string partId = mBodyParts.getId(row);\n                markDirtyDependency(partId);\n            }\n        }\n    }\n\n    void ActorAdapter::handleBodyPartsRemoved(const QModelIndex& parent, int start, int end)\n    {\n        // Changes specified in handleBodyPartsAboutToBeRemoved\n        updateDirty();\n    }\n\n    QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const\n    {\n        while (index.parent().isValid())\n            index = index.parent();\n        return index;\n    }\n\n    bool ActorAdapter::is1stPersonPart(const std::string& name) const\n    {\n        return name.size() >= 4 && name.find(\".1st\", name.size() - 4) != std::string::npos;\n    }\n\n    ActorAdapter::RaceDataPtr ActorAdapter::getRaceData(const std::string& id)\n    {\n        // Return cached race data if it exists\n        RaceDataPtr data = mCachedRaces.get(id);\n        if (data) return data;\n\n        // Create the race data\n        data.reset(new RaceData());\n        setupRace(id, data);\n        mCachedRaces.insert(id, data);\n        return data;\n    }\n\n    void ActorAdapter::setupActor(const std::string& id, ActorDataPtr data)\n    {\n        int index = mReferenceables.searchId(id);\n        if (index == -1)\n        {\n            // Record does not exist\n            data->reset_data(id);\n            emit actorChanged(id);\n            return;\n        }\n\n        auto& record = mReferenceables.getRecord(index);\n        if (record.isDeleted())\n        {\n            // Record is deleted and therefore not accessible\n            data->reset_data(id);\n            emit actorChanged(id);\n            return;\n        }\n\n        const int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType);\n        int type = mReferenceables.getData(index, TypeColumn).toInt();\n        if (type == UniversalId::Type_Creature)\n        {\n            // Valid creature record\n            setupCreature(id, data);\n            emit actorChanged(id);\n        }\n        else if (type == UniversalId::Type_Npc)\n        {\n            // Valid npc record\n            setupNpc(id, data);\n            emit actorChanged(id);\n        }\n        else\n        {\n            // Wrong record type\n            data->reset_data(id);\n            emit actorChanged(id);\n        }\n    }\n\n    void ActorAdapter::setupRace(const std::string& id, RaceDataPtr data)\n    {\n        int index = mRaces.searchId(id);\n        if (index == -1)\n        {\n            // Record does not exist\n            data->reset_data(id);\n            return;\n        }\n\n        auto& raceRecord = mRaces.getRecord(index);\n        if (raceRecord.isDeleted())\n        {\n            // Record is deleted, so not accessible\n            data->reset_data(id);\n            return;\n        }\n\n        auto& race = raceRecord.get();\n        data->reset_data(id, race.mData.mFlags & ESM::Race::Beast);\n\n        // Setup body parts\n        for (int i = 0; i < mBodyParts.getSize(); ++i)\n        {\n            std::string partId = mBodyParts.getId(i);\n            auto& partRecord = mBodyParts.getRecord(i);\n\n            if (partRecord.isDeleted())\n            {\n                // Record is deleted, so not accessible.\n                continue;\n            }\n\n            auto& part = partRecord.get();\n            if (part.mRace == id && part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId))\n            {\n                auto type = (ESM::BodyPart::MeshPart) part.mData.mPart;\n                bool female = part.mData.mFlags & ESM::BodyPart::BPF_Female;\n                if (female) data->setFemalePart(type, part.mId);\n                else data->setMalePart(type, part.mId);\n            }\n        }\n    }\n\n    void ActorAdapter::setupNpc(const std::string& id, ActorDataPtr data)\n    {\n        // Common setup, record is known to exist and is not deleted\n        int index = mReferenceables.searchId(id);\n        auto& npc = dynamic_cast<const Record<ESM::NPC>&>(mReferenceables.getRecord(index)).get();\n\n        RaceDataPtr raceData = getRaceData(npc.mRace);\n        data->reset_data(id, \"\", false, !npc.isMale(), raceData);\n\n        // Add head and hair\n        data->setPart(ESM::PRT_Head, npc.mHead, 0);\n        data->setPart(ESM::PRT_Hair, npc.mHair, 0);\n\n        // Add inventory items\n        for (auto& item : npc.mInventory.mList)\n        {\n            if (item.mCount <= 0) continue;\n            std::string itemId = item.mItem;\n            addNpcItem(itemId, data);\n        }\n    }\n\n    void ActorAdapter::addNpcItem(const std::string& itemId, ActorDataPtr data)\n    {\n        int index = mReferenceables.searchId(itemId);\n        if (index == -1)\n        {\n            // Item does not exist yet\n            data->addOtherDependency(itemId);\n            return;\n        }\n\n        auto& record = mReferenceables.getRecord(index);\n        if (record.isDeleted())\n        {\n            // Item cannot be accessed yet\n            data->addOtherDependency(itemId);\n            return;\n        }\n\n        // Convenience function to add a parts list to actor data\n        auto addParts = [&](const std::vector<ESM::PartReference>& list, int priority) {\n            for (auto& part : list)\n            {\n                std::string partId;\n                auto partType = (ESM::PartReferenceType) part.mPart;\n\n                if (data->isFemale())\n                    partId = part.mFemale;\n                if (partId.empty())\n                    partId = part.mMale;\n\n                data->setPart(partType, partId, priority);\n\n                // An another vanilla quirk: hide hairs if an item replaces Head part\n                if (partType == ESM::PRT_Head)\n                    data->setPart(ESM::PRT_Hair, \"\", priority);\n            }\n        };\n\n        int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType);\n        int type = mReferenceables.getData(index, TypeColumn).toInt();\n        if (type == UniversalId::Type_Armor)\n        {\n            auto& armor = dynamic_cast<const Record<ESM::Armor>&>(record).get();\n            addParts(armor.mParts.mParts, 1);\n\n            // Changing parts could affect what is picked for rendering\n            data->addOtherDependency(itemId);\n        }\n        else if (type == UniversalId::Type_Clothing)\n        {\n            auto& clothing = dynamic_cast<const Record<ESM::Clothing>&>(record).get();\n\n            std::vector<ESM::PartReferenceType> parts;\n            if (clothing.mData.mType == ESM::Clothing::Robe)\n            {\n                parts = {\n                    ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,\n                    ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee,\n                    ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_Cuirass\n                };\n            }\n            else if (clothing.mData.mType == ESM::Clothing::Skirt)\n            {\n                parts = {ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg};\n            }\n\n            std::vector<ESM::PartReference> reservedList;\n            for (const auto& p : parts)\n            {\n                ESM::PartReference pr;\n                pr.mPart = p;\n                reservedList.emplace_back(pr);\n            }\n\n            int priority = parts.size();\n            addParts(clothing.mParts.mParts, priority);\n            addParts(reservedList, priority);\n\n            // Changing parts could affect what is picked for rendering\n            data->addOtherDependency(itemId);\n        }\n    }\n\n    void ActorAdapter::setupCreature(const std::string& id, ActorDataPtr data)\n    {\n        // Record is known to exist and is not deleted\n        int index = mReferenceables.searchId(id);\n        auto& creature = dynamic_cast<const Record<ESM::Creature>&>(mReferenceables.getRecord(index)).get();\n\n        data->reset_data(id, creature.mModel, true);\n    }\n\n    void ActorAdapter::markDirtyDependency(const std::string& dep)\n    {\n        for (auto raceIt : mCachedRaces)\n        {\n            if (raceIt->hasDependency(dep))\n                mDirtyRaces.emplace(raceIt->getId());\n        }\n        for (auto actorIt : mCachedActors)\n        {\n            if (actorIt->hasDependency(dep))\n                mDirtyActors.emplace(actorIt->getId());\n        }\n    }\n\n    void ActorAdapter::updateDirty()\n    {\n        // Handle races before actors, since actors are dependent on race\n        for (auto& race : mDirtyRaces)\n        {\n            RaceDataPtr data = mCachedRaces.get(race);\n            if (data)\n            {\n                setupRace(race, data);\n                // Race was changed. Need to mark actor dependencies as dirty.\n                // Cannot use markDirtyDependency because that would invalidate\n                // the current iterator.\n                for (auto actorIt : mCachedActors)\n                {\n                    if (actorIt->hasDependency(race))\n                        mDirtyActors.emplace(actorIt->getId());\n                }\n            }\n        }\n        mDirtyRaces.clear();\n\n        for (auto& actor : mDirtyActors)\n        {\n            ActorDataPtr data = mCachedActors.get(actor);\n            if (data)\n            {\n                setupActor(actor, data);\n            }\n        }\n        mDirtyActors.clear();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/actoradapter.hpp",
    "content": "#ifndef CSM_WOLRD_ACTORADAPTER_H\n#define CSM_WOLRD_ACTORADAPTER_H\n\n#include <array>\n#include <map>\n#include <unordered_set>\n\n#include <QObject>\n#include <QModelIndex>\n\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadbody.hpp>\n#include <components/misc/weakcache.hpp>\n\n#include \"refidcollection.hpp\"\n#include \"idcollection.hpp\"\n\nnamespace ESM\n{\n    struct Race;\n}\n\nnamespace CSMWorld\n{\n    class Data;\n\n    /// Adapts multiple collections to provide the data needed to render\n    /// an npc or creature.\n    class ActorAdapter : public QObject\n    {\n        Q_OBJECT\n    public:\n\n        /// A list indexed by ESM::PartReferenceType\n        using ActorPartList = std::map<ESM::PartReferenceType, std::pair<std::string, int>>;\n        /// A list indexed by ESM::BodyPart::MeshPart\n        using RacePartList = std::array<std::string, ESM::BodyPart::MP_Count>;\n        /// Tracks unique strings\n        using StringSet = std::unordered_set<std::string>;\n\n\n        /// Contains base race data shared between actors\n        class RaceData\n        {\n        public:\n            RaceData();\n\n            /// Retrieves the id of the race represented\n            const std::string& getId() const;\n            /// Checks if it's a beast race\n            bool isBeast() const;\n            /// Checks if a part could exist for the given type\n            bool handlesPart(ESM::PartReferenceType type) const;\n            /// Retrieves the associated body part\n            const std::string& getFemalePart(ESM::PartReferenceType index) const;\n            /// Retrieves the associated body part\n            const std::string& getMalePart(ESM::PartReferenceType index) const;\n            /// Checks if the race has a data dependency\n            bool hasDependency(const std::string& id) const;\n\n            /// Sets the associated part if it's empty and marks a dependency\n            void setFemalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId);\n            /// Sets the associated part if it's empty and marks a dependency\n            void setMalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId);\n            /// Marks an additional dependency\n            void addOtherDependency(const std::string& id);\n            /// Clears parts and dependencies\n            void reset_data(const std::string& raceId, bool isBeast=false);\n\n        private:\n            bool handles(ESM::PartReferenceType type) const;\n            std::string mId;\n            bool mIsBeast;\n            RacePartList mFemaleParts;\n            RacePartList mMaleParts;\n            StringSet mDependencies;\n        };\n        using RaceDataPtr = std::shared_ptr<RaceData>;\n\n        /// Contains all the data needed to render an actor. Tracks dependencies\n        /// so that pertinent data changes can be checked.\n        class ActorData\n        {\n        public:\n            ActorData();\n\n            /// Retrieves the id of the actor represented\n            const std::string& getId() const;\n            /// Checks if the actor is a creature\n            bool isCreature() const;\n            /// Checks if the actor is female\n            bool isFemale() const;\n            /// Returns the skeleton the actor should use for attaching parts to\n            std::string getSkeleton() const;\n            /// Retrieves the associated actor part\n            const std::string getPart(ESM::PartReferenceType index) const;\n            /// Checks if the actor has a data dependency\n            bool hasDependency(const std::string& id) const;\n\n            /// Sets the actor part used and marks a dependency\n            void setPart(ESM::PartReferenceType partIndex, const std::string& partId, int priority);\n            /// Marks an additional dependency for the actor\n            void addOtherDependency(const std::string& id);\n            /// Clears race, parts, and dependencies\n            void reset_data(const std::string& actorId, const std::string& skeleton=\"\", bool isCreature=false, bool female=true, RaceDataPtr raceData=nullptr);\n\n        private:\n            std::string mId;\n            bool mCreature;\n            bool mFemale;\n            std::string mSkeletonOverride;\n            RaceDataPtr mRaceData;\n            ActorPartList mParts;\n            StringSet mDependencies;\n        };\n        using ActorDataPtr = std::shared_ptr<ActorData>;\n\n\n        ActorAdapter(Data& data);\n\n        /// Obtains the shared data for a given actor\n        ActorDataPtr getActorData(const std::string& refId);\n\n    signals:\n\n        void actorChanged(const std::string& refId);\n\n    public slots:\n\n        void handleReferenceablesInserted(const QModelIndex&, int, int);\n        void handleReferenceableChanged(const QModelIndex&, const QModelIndex&);\n        void handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int);\n        void handleReferenceablesRemoved(const QModelIndex&, int, int);\n\n        void handleRacesInserted(const QModelIndex&, int, int);\n        void handleRaceChanged(const QModelIndex&, const QModelIndex&);\n        void handleRacesAboutToBeRemoved(const QModelIndex&, int, int);\n        void handleRacesRemoved(const QModelIndex&, int, int);\n\n        void handleBodyPartsInserted(const QModelIndex&, int, int);\n        void handleBodyPartChanged(const QModelIndex&, const QModelIndex&);\n        void handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int);\n        void handleBodyPartsRemoved(const QModelIndex&, int, int);\n\n    private:\n\n        ActorAdapter(const ActorAdapter&) = delete;\n        ActorAdapter& operator=(const ActorAdapter&) = delete;\n\n        QModelIndex getHighestIndex(QModelIndex) const;\n        bool is1stPersonPart(const std::string& id) const;\n\n        RaceDataPtr getRaceData(const std::string& raceId);\n\n        void setupActor(const std::string& id, ActorDataPtr data);\n        void setupRace(const std::string& id, RaceDataPtr data);\n\n        void setupNpc(const std::string& id, ActorDataPtr data);\n        void addNpcItem(const std::string& itemId, ActorDataPtr data);\n\n        void setupCreature(const std::string& id, ActorDataPtr data);\n\n        void markDirtyDependency(const std::string& dependency);\n        void updateDirty();\n\n        RefIdCollection& mReferenceables;\n        IdCollection<ESM::Race>& mRaces;\n        IdCollection<ESM::BodyPart>& mBodyParts;\n\n        Misc::WeakCache<std::string, ActorData> mCachedActors; // Key: referenceable id\n        Misc::WeakCache<std::string, RaceData> mCachedRaces; // Key: race id\n\n        StringSet mDirtyActors; // Actors that need updating\n        StringSet mDirtyRaces; // Races that need updating\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/cell.cpp",
    "content": "#include \"cell.hpp\"\n\n#include <sstream>\n\nvoid CSMWorld::Cell::load (ESM::ESMReader &esm, bool &isDeleted)\n{\n    ESM::Cell::load (esm, isDeleted, false);\n\n    mId = mName;\n    if (isExterior())\n    {\n        std::ostringstream stream;\n        stream << \"#\" << mData.mX << \" \" << mData.mY;\n        mId = stream.str();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/cell.hpp",
    "content": "#ifndef CSM_WOLRD_CELL_H\n#define CSM_WOLRD_CELL_H\n\n#include <vector>\n#include <string>\n\n#include <components/esm/loadcell.hpp>\n\nnamespace CSMWorld\n{\n    /// \\brief Wrapper for Cell record\n    ///\n    /// \\attention The mData.mX and mData.mY fields of the ESM::Cell struct are not used.\n    /// Exterior cell coordinates are encoded in the cell ID.\n    struct Cell : public ESM::Cell\n    {\n        std::string mId;\n\n        void load (ESM::ESMReader &esm, bool &isDeleted);\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/cellcoordinates.cpp",
    "content": "#include \"cellcoordinates.hpp\"\n\n#include <cmath>\n\n#include <ostream>\n#include <sstream>\n\n#include <components/esm/loadland.hpp>\n#include <components/misc/constants.hpp>\n\nCSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}\n\nCSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {}\n\nCSMWorld::CellCoordinates::CellCoordinates (const std::pair<int, int>& coordinates)\n: mX (coordinates.first), mY (coordinates.second) {}\n\nint CSMWorld::CellCoordinates::getX() const\n{\n    return mX;\n}\n\nint CSMWorld::CellCoordinates::getY() const\n{\n    return mY;\n}\n\nCSMWorld::CellCoordinates CSMWorld::CellCoordinates::move (int x, int y) const\n{\n    return CellCoordinates (mX + x, mY + y);\n}\n\nstd::string CSMWorld::CellCoordinates::getId (const std::string& worldspace) const\n{\n    // we ignore the worldspace for now, since there is only one (will change in 1.1)\n    return generateId(mX, mY);\n}\n\nstd::string CSMWorld::CellCoordinates::generateId (int x, int y)\n{\n    std::string cellId = \"#\" + std::to_string(x) + \" \" + std::to_string(y);\n    return cellId;\n}\n\nbool CSMWorld::CellCoordinates::isExteriorCell (const std::string& id)\n{\n    return (!id.empty() && id[0]=='#');\n}\n\nstd::pair<CSMWorld::CellCoordinates, bool> CSMWorld::CellCoordinates::fromId (\n    const std::string& id)\n{\n    // no worldspace for now, needs to be changed for 1.1\n    if (isExteriorCell(id))\n    {\n        int x, y;\n        char ignore;\n\n        std::istringstream stream (id);\n        if (stream >> ignore >> x >> y)\n            return std::make_pair (CellCoordinates (x, y), true);\n    }\n\n    return std::make_pair (CellCoordinates(), false);\n}\n\nstd::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y)\n{\n    return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits));\n}\n\nstd::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(const osg::Vec3d& worldPos)\n{\n    const auto xd = static_cast<float>(worldPos.x() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE - 0.25f);\n    const auto yd = static_cast<float>(worldPos.y() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE + 0.25f);\n\n    const auto x = static_cast<int>(std::floor(xd));\n    const auto y = static_cast<int>(std::floor(yd));\n\n    return std::make_pair(x, y);\n}\n\nstd::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(const osg::Vec3d& worldPos)\n{\n    const auto xd = static_cast<float>(worldPos.x() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);\n    const auto yd = static_cast<float>(worldPos.y() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);\n\n    const auto x = static_cast<int>(std::floor(xd));\n    const auto y = static_cast<int>(std::floor(yd));\n\n    return std::make_pair(x, y);\n}\n\nfloat CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(int textureGlobal)\n{\n    return ESM::Land::REAL_SIZE * (static_cast<float>(textureGlobal) + 0.25f) / ESM::Land::LAND_TEXTURE_SIZE;\n}\n\nfloat CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(int textureGlobal)\n{\n    return ESM::Land::REAL_SIZE * (static_cast<float>(textureGlobal) - 0.25f) / ESM::Land::LAND_TEXTURE_SIZE;\n}\n\nfloat CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(int vertexGlobal)\n{\n    return ESM::Land::REAL_SIZE * static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1);\n}\n\nint CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(int vertexGlobal)\n{\n    return static_cast<int>(vertexGlobal - std::floor(static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1)) * (ESM::Land::LAND_SIZE - 1));\n}\n\nstd::string CSMWorld::CellCoordinates::textureGlobalToCellId(const std::pair<int, int>& textureGlobal)\n{\n    int x = std::floor(static_cast<float>(textureGlobal.first) / ESM::Land::LAND_TEXTURE_SIZE);\n    int y = std::floor(static_cast<float>(textureGlobal.second) / ESM::Land::LAND_TEXTURE_SIZE);\n    return generateId(x, y);\n}\n\nstd::string CSMWorld::CellCoordinates::vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal)\n{\n    int x = std::floor(static_cast<float>(vertexGlobal.first) / (ESM::Land::LAND_SIZE - 1));\n    int y = std::floor(static_cast<float>(vertexGlobal.second) / (ESM::Land::LAND_SIZE - 1));\n    return generateId(x, y);\n}\n\nbool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)\n{\n    return left.getX()==right.getX() && left.getY()==right.getY();\n}\n\nbool CSMWorld::operator!= (const CellCoordinates& left, const CellCoordinates& right)\n{\n    return !(left==right);\n}\n\nbool CSMWorld::operator< (const CellCoordinates& left, const CellCoordinates& right)\n{\n    if (left.getX()<right.getX())\n        return true;\n\n    if (left.getX()>right.getX())\n        return false;\n\n    return left.getY()<right.getY();\n}\n\nstd::ostream& CSMWorld::operator<< (std::ostream& stream, const CellCoordinates& coordiantes)\n{\n    return stream << coordiantes.getX() << \", \" << coordiantes.getY();\n}\n"
  },
  {
    "path": "apps/opencs/model/world/cellcoordinates.hpp",
    "content": "#ifndef CSM_WOLRD_CELLCOORDINATES_H\n#define CSM_WOLRD_CELLCOORDINATES_H\n\n#include <iosfwd>\n#include <string>\n#include <utility>\n\n#include <QMetaType>\n\n#include <osg/Vec3d>\n\nnamespace CSMWorld\n{\n    class CellCoordinates\n    {\n            int mX;\n            int mY;\n\n        public:\n\n            CellCoordinates();\n\n            CellCoordinates (int x, int y);\n\n            CellCoordinates (const std::pair<int, int>& coordinates);\n\n            int getX() const;\n\n            int getY() const;\n\n            CellCoordinates move (int x, int y) const;\n            ///< Return a copy of *this, moved by the given offset.\n\n            ///Generate cell id string from x and y coordinates\n            static std::string generateId (int x, int y);\n\n            std::string getId (const std::string& worldspace) const;\n            ///< Return the ID for the cell at these coordinates.\n\n            static bool isExteriorCell (const std::string& id);\n\n            /// \\return first: CellCoordinates (or 0, 0 if cell does not have coordinates),\n            /// second: is cell paged?\n            ///\n            /// \\note The worldspace part of \\a id is ignored\n            static std::pair<CellCoordinates, bool> fromId (const std::string& id);\n\n            /// \\return cell coordinates such that given world coordinates are in it.\n            static std::pair<int, int> coordinatesToCellIndex (float x, float y);\n\n            ///Converts worldspace coordinates to global texture selection, taking in account the texture offset.\n            static std::pair<int, int> toTextureCoords(const osg::Vec3d& worldPos);\n\n            ///Converts worldspace coordinates to global vertex selection.\n            static std::pair<int, int> toVertexCoords(const osg::Vec3d& worldPos);\n\n            ///Converts global texture coordinate X to worldspace coordinate, offset by 0.25f.\n            static float textureGlobalXToWorldCoords(int textureGlobal);\n\n            ///Converts global texture coordinate Y to worldspace coordinate, offset by 0.25f.\n            static float textureGlobalYToWorldCoords(int textureGlobal);\n\n            ///Converts global vertex coordinate to worldspace coordinate\n            static float vertexGlobalToWorldCoords(int vertexGlobal);\n\n            ///Converts global vertex coordinate to local cell's heightmap coordinates\n            static int vertexGlobalToInCellCoords(int vertexGlobal);\n\n            ///Converts global texture coordinates to cell id\n            static std::string textureGlobalToCellId(const std::pair<int, int>& textureGlobal);\n\n            ///Converts global vertex coordinates to cell id\n            static std::string vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal);\n    };\n\n    bool operator== (const CellCoordinates& left, const CellCoordinates& right);\n    bool operator!= (const CellCoordinates& left, const CellCoordinates& right);\n    bool operator< (const CellCoordinates& left, const CellCoordinates& right);\n\n    std::ostream& operator<< (std::ostream& stream, const CellCoordinates& coordiantes);\n}\n\nQ_DECLARE_METATYPE (CSMWorld::CellCoordinates)\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/cellselection.cpp",
    "content": "#include \"cellselection.hpp\"\n\n#include <cmath>\n#include <stdexcept>\n#include <limits>\n\nCSMWorld::CellSelection::Iterator CSMWorld::CellSelection::begin() const\n{\n    return mCells.begin();\n}\n\nCSMWorld::CellSelection::Iterator CSMWorld::CellSelection::end() const\n{\n    return mCells.end();\n}\n\nbool CSMWorld::CellSelection::add (const CellCoordinates& coordinates)\n{\n    return mCells.insert (coordinates).second;\n}\n\nvoid CSMWorld::CellSelection::remove (const CellCoordinates& coordinates)\n{\n    mCells.erase (coordinates);\n}\n\nbool CSMWorld::CellSelection::has (const CellCoordinates& coordinates) const\n{\n    return mCells.find (coordinates)!=end();\n}\n\nint CSMWorld::CellSelection::getSize() const\n{\n    return mCells.size();\n}\n\nCSMWorld::CellCoordinates CSMWorld::CellSelection::getCentre() const\n{\n    if (mCells.empty())\n        throw std::logic_error (\"call of getCentre on empty cell selection\");\n\n    double x = 0;\n    double y = 0;\n\n    for (Iterator iter = begin(); iter!=end(); ++iter)\n    {\n        x += iter->getX();\n        y += iter->getY();\n    }\n\n    x /= mCells.size();\n    y /= mCells.size();\n\n    Iterator closest = begin();\n    double distance = std::numeric_limits<double>::max();\n\n    for (Iterator iter (begin()); iter!=end(); ++iter)\n    {\n        double deltaX = x - iter->getX();\n        double deltaY = y - iter->getY();\n\n        double delta = std::sqrt (deltaX * deltaX + deltaY * deltaY);\n\n        if (delta<distance)\n        {\n            distance = delta;\n            closest = iter;\n        }\n    }\n\n    return *closest;\n}\n\nvoid CSMWorld::CellSelection::move (int x, int y)\n{\n    Container moved;\n\n    for (Iterator iter = begin(); iter!=end(); ++iter)\n        moved.insert (iter->move (x, y));\n\n    mCells.swap (moved);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/cellselection.hpp",
    "content": "#ifndef CSM_WOLRD_CELLSELECTION_H\n#define CSM_WOLRD_CELLSELECTION_H\n\n#include <set>\n\n#include <QMetaType>\n\n#include \"cellcoordinates.hpp\"\n\nnamespace CSMWorld\n{\n    /// \\brief Selection of cells in a paged worldspace\n    ///\n    /// \\note The CellSelection does not specify the worldspace it applies to.\n    class CellSelection\n    {\n        public:\n\n            typedef std::set<CellCoordinates> Container;\n            typedef Container::const_iterator Iterator;\n\n        private:\n\n            Container mCells;\n\n        public:\n\n            Iterator begin() const;\n\n            Iterator end() const;\n\n            bool add (const CellCoordinates& coordinates);\n            ///< Ignored if the cell specified by \\a coordinates is already part of the selection.\n            ///\n            /// \\return Was a cell added to the collection?\n\n            void remove (const CellCoordinates& coordinates);\n            ///< ignored if the cell specified by \\a coordinates is not part of the selection.\n\n            bool has (const CellCoordinates& coordinates) const;\n            ///< \\return Is the cell specified by \\a coordinates part of the selection?\n\n            int getSize() const;\n            ///< Return number of cells.\n\n            CellCoordinates getCentre() const;\n            ///< Return the selected cell that is closest to the geometric centre of the selection.\n            ///\n            /// \\attention This function must not be called on selections that are empty.\n\n            void move (int x, int y);\n    };\n}\n\nQ_DECLARE_METATYPE (CSMWorld::CellSelection)\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/collection.hpp",
    "content": "#ifndef CSM_WOLRD_COLLECTION_H\n#define CSM_WOLRD_COLLECTION_H\n\n#include <vector>\n#include <map>\n#include <algorithm>\n#include <cctype>\n#include <stdexcept>\n#include <string>\n#include <functional>\n\n#include <QVariant>\n\n#include <components/misc/stringops.hpp>\n\n#include \"columnbase.hpp\"\n#include \"collectionbase.hpp\"\n#include \"land.hpp\"\n#include \"landtexture.hpp\"\n#include \"ref.hpp\"\n\nnamespace CSMWorld\n{\n    /// \\brief Access to ID field in records\n    template<typename ESXRecordT>\n    struct IdAccessor\n    {\n        void setId(ESXRecordT& record, const std::string& id) const;\n        const std::string getId (const ESXRecordT& record) const;\n    };\n\n    template<typename ESXRecordT>\n    void IdAccessor<ESXRecordT>::setId(ESXRecordT& record, const std::string& id) const\n    {\n        record.mId = id;\n    }\n\n    template<typename ESXRecordT>\n    const std::string IdAccessor<ESXRecordT>::getId (const ESXRecordT& record) const\n    {\n        return record.mId;\n    }\n\n    template<>\n    inline void IdAccessor<Land>::setId (Land& record, const std::string& id) const\n    {\n        int x=0, y=0;\n\n        Land::parseUniqueRecordId(id, x, y);\n        record.mX = x;\n        record.mY = y;\n    }\n\n    template<>\n    inline void IdAccessor<LandTexture>::setId (LandTexture& record, const std::string& id) const\n    {\n        int plugin = 0;\n        int index = 0;\n\n        LandTexture::parseUniqueRecordId(id, plugin, index);\n        record.mPluginIndex = plugin;\n        record.mIndex = index;\n    }\n\n    template<>\n    inline const std::string IdAccessor<Land>::getId (const Land& record) const\n    {\n        return Land::createUniqueRecordId(record.mX, record.mY);\n    }\n\n    template<>\n    inline const std::string IdAccessor<LandTexture>::getId (const LandTexture& record) const\n    {\n        return LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex);\n    }\n\n    /// \\brief Single-type record collection\n    template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >\n    class Collection : public CollectionBase\n    {\n        public:\n\n            typedef ESXRecordT ESXRecord;\n\n        private:\n\n            std::vector<Record<ESXRecordT> > mRecords;\n            std::map<std::string, int> mIndex;\n            std::vector<Column<ESXRecordT> *> mColumns;\n\n            // not implemented\n            Collection (const Collection&);\n            Collection& operator= (const Collection&);\n\n        protected:\n\n            const std::map<std::string, int>& getIdMap() const;\n\n            const std::vector<Record<ESXRecordT> >& getRecords() const;\n\n            bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);\n            ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices\n            /// given in \\a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).\n            ///\n            /// \\return Success?\n\n            int cloneRecordImp (const std::string& origin, const std::string& dest,\n                UniversalId::Type type);\n            ///< Returns the index of the clone.\n\n            int touchRecordImp (const std::string& id);\n            ///< Returns the index of the record on success, -1 on failure.\n\n        public:\n\n            Collection();\n\n            virtual ~Collection();\n\n            void add (const ESXRecordT& record);\n            ///< Add a new record (modified)\n\n            int getSize() const override;\n\n            std::string getId (int index) const override;\n\n            int getIndex (const std::string& id) const override;\n\n            int getColumns() const override;\n\n            QVariant getData (int index, int column) const override;\n\n            void setData (int index, int column, const QVariant& data) override;\n\n            const ColumnBase& getColumn (int column) const override;\n\n            virtual void merge();\n            ///< Merge modified into base.\n\n            virtual void purge();\n            ///< Remove records that are flagged as erased.\n\n            void removeRows (int index, int count) override;\n\n            void appendBlankRecord (const std::string& id,\n                UniversalId::Type type = UniversalId::Type_None) override;\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            void cloneRecord(const std::string& origin,\n                                     const std::string& destination,\n                                     const UniversalId::Type type) override;\n\n            bool touchRecord(const std::string& id) override;\n            ///< Change the state of a record from base to modified, if it is not already.\n            ///  \\return True if the record was changed.\n\n            int searchId (const std::string& id) const override;\n            ////< Search record with \\a id.\n            /// \\return index of record (if found) or -1 (not found)\n\n            void replace (int index, const RecordBase& record) override;\n            ///< If the record type does not match, an exception is thrown.\n            ///\n            /// \\attention \\a record must not change the ID.\n\n            void appendRecord (const RecordBase& record,\n                UniversalId::Type type = UniversalId::Type_None) override;\n            ///< If the record type does not match, an exception is thrown.\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            const Record<ESXRecordT>& getRecord (const std::string& id) const override;\n\n            const Record<ESXRecordT>& getRecord (int index) const override;\n\n            int getAppendIndex (const std::string& id,\n                UniversalId::Type type = UniversalId::Type_None) const override;\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            std::vector<std::string> getIds (bool listDeleted = true) const override;\n            ///< Return a sorted collection of all IDs\n            ///\n            /// \\param listDeleted include deleted record in the list\n\n            virtual void insertRecord (const RecordBase& record, int index,\n                UniversalId::Type type = UniversalId::Type_None);\n            ///< Insert record before index.\n            ///\n            /// If the record type does not match, an exception is thrown.\n            ///\n            /// If the index is invalid either generally (by being out of range) or for the particular\n            /// record, an exception is thrown.\n\n            bool reorderRows (int baseIndex, const std::vector<int>& newOrder) override;\n            ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices\n            /// given in \\a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).\n            ///\n            /// \\return Success?\n\n            void addColumn (Column<ESXRecordT> *column);\n\n            void setRecord (int index, const Record<ESXRecordT>& record);\n            ///< \\attention This function must not change the ID.\n\n            NestableColumn *getNestableColumn (int column) const;\n    };\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    const std::map<std::string, int>& Collection<ESXRecordT, IdAccessorT>::getIdMap() const\n    {\n        return mIndex;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    const std::vector<Record<ESXRecordT> >& Collection<ESXRecordT, IdAccessorT>::getRecords() const\n    {\n        return mRecords;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    bool Collection<ESXRecordT, IdAccessorT>::reorderRowsImp (int baseIndex,\n        const std::vector<int>& newOrder)\n    {\n        if (!newOrder.empty())\n        {\n            int size = static_cast<int> (newOrder.size());\n\n            // check that all indices are present\n            std::vector<int> test (newOrder);\n            std::sort (test.begin(), test.end());\n            if (*test.begin()!=0 || *--test.end()!=size-1)\n                return false;\n\n            // reorder records\n            std::vector<Record<ESXRecordT> > buffer (size);\n\n            for (int i=0; i<size; ++i)\n            {\n                buffer[newOrder[i]] = mRecords [baseIndex+i];\n                buffer[newOrder[i]].setModified (buffer[newOrder[i]].get());\n            }\n\n            std::copy (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);\n\n            // adjust index\n            for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();\n                 ++iter)\n                if (iter->second>=baseIndex && iter->second<baseIndex+size)\n                    iter->second = newOrder.at (iter->second-baseIndex)+baseIndex;\n        }\n\n        return true;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int Collection<ESXRecordT, IdAccessorT>::cloneRecordImp(const std::string& origin,\n        const std::string& destination, UniversalId::Type type)\n    {\n        Record<ESXRecordT> copy;\n        copy.mModified = getRecord(origin).get();\n        copy.mState = RecordBase::State_ModifiedOnly;\n        IdAccessorT().setId(copy.get(), destination);\n\n        if (type == UniversalId::Type_Reference) {\n            CSMWorld::CellRef* ptr = (CSMWorld::CellRef*) &copy.mModified;\n            ptr->mRefNum.mIndex = 0;\n        }\n\n        int index = getAppendIndex(destination, type);\n        insertRecord(copy, getAppendIndex(destination, type));\n\n        return index;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int Collection<ESXRecordT, IdAccessorT>::touchRecordImp(const std::string& id)\n    {\n        int index = getIndex(id);\n        Record<ESXRecordT>& record = mRecords.at(index);\n        if (record.isDeleted())\n        {\n            throw std::runtime_error(\"attempt to touch deleted record\");\n        }\n\n        if (!record.isModified())\n        {\n            record.setModified(record.get());\n            return index;\n        }\n\n        return -1;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,\n        const std::string& destination, const UniversalId::Type type)\n    {\n        cloneRecordImp(origin, destination, type);\n    }\n\n    template<>\n    inline void Collection<Land, IdAccessor<Land> >::cloneRecord(const std::string& origin,\n        const std::string& destination, const UniversalId::Type type)\n    {\n        int index = cloneRecordImp(origin, destination, type);\n        mRecords.at(index).get().mPlugin = 0;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    bool Collection<ESXRecordT, IdAccessorT>::touchRecord(const std::string& id)\n    {\n        return touchRecordImp(id) != -1;\n    }\n\n    template<>\n    inline bool Collection<Land, IdAccessor<Land> >::touchRecord(const std::string& id)\n    {\n        int index = touchRecordImp(id);\n        if (index >= 0)\n        {\n            mRecords.at(index).get().mPlugin = 0;\n            return true;\n        }\n\n        return false;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    Collection<ESXRecordT, IdAccessorT>::Collection()\n    {}\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    Collection<ESXRecordT, IdAccessorT>::~Collection()\n    {\n        for (typename std::vector<Column<ESXRecordT> *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)\n            delete *iter;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::add (const ESXRecordT& record)\n    {\n        std::string id = Misc::StringUtils::lowerCase (IdAccessorT().getId (record));\n\n        std::map<std::string, int>::iterator iter = mIndex.find (id);\n\n        if (iter==mIndex.end())\n        {\n            Record<ESXRecordT> record2;\n            record2.mState = Record<ESXRecordT>::State_ModifiedOnly;\n            record2.mModified = record;\n\n            insertRecord (record2, getAppendIndex (id));\n        }\n        else\n        {\n            mRecords[iter->second].setModified (record);\n        }\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int Collection<ESXRecordT, IdAccessorT>::getSize() const\n    {\n        return mRecords.size();\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const\n    {\n        return IdAccessorT().getId (mRecords.at (index).get());\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int  Collection<ESXRecordT, IdAccessorT>::getIndex (const std::string& id) const\n    {\n        int index = searchId (id);\n\n        if (index==-1)\n            throw std::runtime_error (\"invalid ID: \" + id);\n\n        return index;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int Collection<ESXRecordT, IdAccessorT>::getColumns() const\n    {\n        return mColumns.size();\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const\n    {\n        return mColumns.at (column)->get (mRecords.at (index));\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)\n    {\n        return mColumns.at (column)->set (mRecords.at (index), data);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    const ColumnBase& Collection<ESXRecordT, IdAccessorT>::getColumn (int column) const\n    {\n        return *mColumns.at (column);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    NestableColumn *Collection<ESXRecordT, IdAccessorT>::getNestableColumn (int column) const\n    {\n        if (column < 0 || column >= static_cast<int>(mColumns.size()))\n            throw std::runtime_error(\"column index out of range\");\n\n        return mColumns.at (column);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::addColumn (Column<ESXRecordT> *column)\n    {\n        mColumns.push_back (column);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::merge()\n    {\n        for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)\n            iter->merge();\n\n        purge();\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void  Collection<ESXRecordT, IdAccessorT>::purge()\n    {\n        int i = 0;\n\n        while (i<static_cast<int> (mRecords.size()))\n        {\n            if (mRecords[i].isErased())\n                removeRows (i, 1);\n            else\n                ++i;\n        }\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::removeRows (int index, int count)\n    {\n        mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count);\n\n        typename std::map<std::string, int>::iterator iter = mIndex.begin();\n\n        while (iter!=mIndex.end())\n        {\n            if (iter->second>=index)\n            {\n                if (iter->second>=index+count)\n                {\n                    iter->second -= count;\n                    ++iter;\n                }\n                else\n                {\n                    mIndex.erase (iter++);\n                }\n            }\n            else\n                ++iter;\n        }\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void  Collection<ESXRecordT, IdAccessorT>::appendBlankRecord (const std::string& id,\n        UniversalId::Type type)\n    {\n        ESXRecordT record;\n        IdAccessorT().setId(record, id);\n        record.blank();\n\n        Record<ESXRecordT> record2;\n        record2.mState = Record<ESXRecordT>::State_ModifiedOnly;\n        record2.mModified = record;\n\n        insertRecord (record2, getAppendIndex (id, type), type);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int Collection<ESXRecordT, IdAccessorT>::searchId (const std::string& id) const\n    {\n        std::string id2 = Misc::StringUtils::lowerCase(id);\n\n        std::map<std::string, int>::const_iterator iter = mIndex.find (id2);\n\n        if (iter==mIndex.end())\n            return -1;\n\n        return iter->second;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::replace (int index, const RecordBase& record)\n    {\n        mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,\n        UniversalId::Type type)\n    {\n        insertRecord (record,\n            getAppendIndex (IdAccessorT().getId (\n            dynamic_cast<const Record<ESXRecordT>&> (record).get()), type), type);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int Collection<ESXRecordT, IdAccessorT>::getAppendIndex (const std::string& id,\n        UniversalId::Type type) const\n    {\n        return static_cast<int> (mRecords.size());\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    std::vector<std::string> Collection<ESXRecordT, IdAccessorT>::getIds (bool listDeleted) const\n    {\n        std::vector<std::string> ids;\n\n        for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin();\n            iter!=mIndex.end(); ++iter)\n        {\n            if (listDeleted || !mRecords[iter->second].isDeleted())\n                ids.push_back (IdAccessorT().getId (mRecords[iter->second].get()));\n        }\n\n        return ids;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const\n    {\n        int index = getIndex (id);\n        return mRecords.at (index);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const\n    {\n        return mRecords.at (index);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::insertRecord (const RecordBase& record, int index,\n        UniversalId::Type type)\n    {\n        if (index<0 || index>static_cast<int> (mRecords.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);\n\n        mRecords.insert (mRecords.begin()+index, record2);\n\n        if (index<static_cast<int> (mRecords.size())-1)\n        {\n            for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();\n                ++iter)\n                 if (iter->second>=index)\n                     ++(iter->second);\n        }\n\n        mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (\n            record2.get())), index));\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)\n    {\n        if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index).get()))!=\n            Misc::StringUtils::lowerCase (IdAccessorT().getId (record.get())))\n            throw std::runtime_error (\"attempt to change the ID of a record\");\n\n        mRecords.at (index) = record;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    bool Collection<ESXRecordT, IdAccessorT>::reorderRows (int baseIndex, const std::vector<int>& newOrder)\n    {\n        return false;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/collectionbase.cpp",
    "content": "#include \"collectionbase.hpp\"\n\n#include <stdexcept>\n\n#include \"columnbase.hpp\"\n\nCSMWorld::CollectionBase::CollectionBase() {}\n\nCSMWorld::CollectionBase::~CollectionBase() {}\n\nint CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const\n{\n    int columns = getColumns();\n\n    for (int i=0; i<columns; ++i)\n        if (getColumn (i).mColumnId==id)\n            return i;\n\n    return -1;\n}\n\nint CSMWorld::CollectionBase::findColumnIndex (Columns::ColumnId id) const\n{\n    int index = searchColumnIndex (id);\n\n    if (index==-1)\n        throw std::logic_error (\"invalid column index\");\n\n    return index;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/collectionbase.hpp",
    "content": "#ifndef CSM_WOLRD_COLLECTIONBASE_H\n#define CSM_WOLRD_COLLECTIONBASE_H\n\n#include <string>\n#include <vector>\n\n#include \"universalid.hpp\"\n#include \"columns.hpp\"\n\nclass QVariant;\n\nnamespace CSMWorld\n{\n    struct ColumnBase;\n    struct RecordBase;\n\n    /// \\brief Base class for record collections\n    ///\n    /// \\attention Modifying records through the interface does not update connected views.\n    /// Such modifications should be done through the table model interface instead unless no views\n    /// are connected to the model or special precautions have been taken to send update signals\n    /// manually.\n    class CollectionBase\n    {\n            // not implemented\n            CollectionBase (const CollectionBase&);\n            CollectionBase& operator= (const CollectionBase&);\n\n        public:\n\n            CollectionBase();\n\n            virtual ~CollectionBase();\n\n            virtual int getSize() const = 0;\n\n            virtual std::string getId (int index) const = 0;\n\n            virtual int getIndex (const std::string& id) const = 0;\n\n            virtual int getColumns() const = 0;\n\n            virtual const ColumnBase& getColumn (int column) const = 0;\n\n            virtual QVariant getData (int index, int column) const = 0;\n\n            virtual void setData (int index, int column, const QVariant& data) = 0;\n\n// Not in use. Temporarily removed so that the implementation of RefIdCollection can continue without\n// these functions for now.\n//            virtual void merge() = 0;\n            ///< Merge modified into base.\n\n//            virtual void purge() = 0;\n            ///< Remove records that are flagged as erased.\n\n            virtual void removeRows (int index, int count) = 0;\n\n            virtual void appendBlankRecord (const std::string& id,\n                UniversalId::Type type = UniversalId::Type_None) = 0;\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            virtual int searchId (const std::string& id) const = 0;\n            ////< Search record with \\a id.\n            /// \\return index of record (if found) or -1 (not found)\n\n            virtual void replace (int index, const RecordBase& record) = 0;\n            ///< If the record type does not match, an exception is thrown.\n            ///\n            /// \\attention \\a record must not change the ID.\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            virtual void appendRecord (const RecordBase& record,\n                UniversalId::Type type = UniversalId::Type_None) = 0;\n            ///< If the record type does not match, an exception is thrown.\n\n            virtual void cloneRecord(const std::string& origin,\n                                     const std::string& destination,\n                                     const UniversalId::Type type) = 0;\n\n            virtual bool touchRecord(const std::string& id) = 0;\n\n            virtual const RecordBase& getRecord (const std::string& id) const = 0;\n\n            virtual const RecordBase& getRecord (int index) const = 0;\n\n            virtual int getAppendIndex (const std::string& id,\n                UniversalId::Type type = UniversalId::Type_None) const = 0;\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            virtual std::vector<std::string> getIds (bool listDeleted = true) const = 0;\n            ///< Return a sorted collection of all IDs\n            ///\n            /// \\param listDeleted include deleted record in the list\n\n            virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder) = 0;\n            ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices\n            /// given in \\a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).\n            ///\n            /// \\return Success?\n\n            int searchColumnIndex (Columns::ColumnId id) const;\n            ///< Return index of column with the given \\a id. If no such column exists, -1 is returned.\n\n            int findColumnIndex (Columns::ColumnId id) const;\n            ///< Return index of column with the given \\a id. If no such column exists, an exception is\n            /// thrown.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/columnbase.cpp",
    "content": "#include \"columnbase.hpp\"\n\n#include \"columns.hpp\"\n\nCSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags)\n    : mColumnId (columnId), mFlags (flags), mDisplayType (displayType)\n{}\n\nCSMWorld::ColumnBase::~ColumnBase() {}\n\nbool CSMWorld::ColumnBase::isUserEditable() const\n{\n    return isEditable();\n}\n\nstd::string CSMWorld::ColumnBase::getTitle() const\n{\n    return Columns::getName (static_cast<Columns::ColumnId> (mColumnId));\n}\n\nint  CSMWorld::ColumnBase::getId() const\n{\n    return mColumnId;\n}\n\nbool CSMWorld::ColumnBase::isId (Display display)\n{\n    static const Display ids[] =\n    {\n        Display_Skill,\n        Display_Class,\n        Display_Faction,\n        Display_Rank,\n        Display_Race,\n        Display_Sound,\n        Display_Region,\n        Display_Birthsign,\n        Display_Spell,\n        Display_Cell,\n        Display_Referenceable,\n        Display_Activator,\n        Display_Potion,\n        Display_Apparatus,\n        Display_Armor,\n        Display_Book,\n        Display_Clothing,\n        Display_Container,\n        Display_Creature,\n        Display_Door,\n        Display_Ingredient,\n        Display_CreatureLevelledList,\n        Display_ItemLevelledList,\n        Display_Light,\n        Display_Lockpick,\n        Display_Miscellaneous,\n        Display_Npc,\n        Display_Probe,\n        Display_Repair,\n        Display_Static,\n        Display_Weapon,\n        Display_Reference,\n        Display_Filter,\n        Display_Topic,\n        Display_Journal,\n        Display_TopicInfo,\n        Display_JournalInfo,\n        Display_Scene,\n        Display_GlobalVariable,\n        Display_BodyPart,\n        Display_Enchantment,\n        Display_Script,\n\n        Display_Mesh,\n        Display_Icon,\n        Display_Music,\n        Display_SoundRes,\n        Display_Texture,\n        Display_Video,\n\n        Display_Id,\n        Display_SkillId,\n        Display_EffectRange,\n        Display_EffectId,\n        Display_PartRefType,\n        Display_AiPackageType,\n        Display_InfoCondFunc,\n        Display_InfoCondVar,\n        Display_InfoCondComp,\n\n        Display_EffectSkill,\n        Display_EffectAttribute,\n        Display_IngredEffectId,\n\n        Display_None\n    };\n\n    for (int i=0; ids[i]!=Display_None; ++i)\n        if (ids[i]==display)\n            return true;\n\n    return false;\n}\n\nbool CSMWorld::ColumnBase::isText (Display display)\n{\n    return display==Display_String || display==Display_LongString ||\n        display==Display_String32 || display==Display_LongString256;\n}\n\nbool CSMWorld::ColumnBase::isScript (Display display)\n{\n    return display==Display_ScriptFile || display==Display_ScriptLines;\n}\n\nvoid CSMWorld::NestableColumn::addColumn(CSMWorld::NestableColumn *column)\n{\n    mNestedColumns.push_back(column);\n}\n\nconst CSMWorld::ColumnBase& CSMWorld::NestableColumn::nestedColumn(int subColumn) const\n{\n    if (mNestedColumns.empty())\n        throw std::logic_error(\"Tried to access nested column of the non-nest column\");\n\n    return *mNestedColumns.at(subColumn);\n}\n\nCSMWorld::NestableColumn::NestableColumn(int columnId, CSMWorld::ColumnBase::Display displayType,\n    int flag)\n    : CSMWorld::ColumnBase(columnId, displayType, flag)\n{}\n\nCSMWorld::NestableColumn::~NestableColumn()\n{\n    for (unsigned int i = 0; i < mNestedColumns.size(); ++i)\n    {\n        delete mNestedColumns[i];\n    }\n}\n\nbool CSMWorld::NestableColumn::hasChildren() const\n{\n    return !mNestedColumns.empty();\n}\n\nCSMWorld::NestedChildColumn::NestedChildColumn (int id,\n    CSMWorld::ColumnBase::Display display, int flags, bool isEditable)\n    : NestableColumn (id, display, flags) , mIsEditable(isEditable)\n{}\n\nbool CSMWorld::NestedChildColumn::isEditable () const\n{\n    return mIsEditable;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/columnbase.hpp",
    "content": "#ifndef CSM_WOLRD_COLUMNBASE_H\n#define CSM_WOLRD_COLUMNBASE_H\n\n#include <string>\n#include <vector>\n#include <stdexcept>\n\n#include <Qt>\n#include <QVariant>\n\n#include \"record.hpp\"\n\nnamespace CSMWorld\n{\n    struct ColumnBase\n    {\n        enum TableEditModes\n        {\n            TableEdit_None,      // no editing\n            TableEdit_Full,      // edit cells and add/remove rows\n            TableEdit_FixedRows  // edit cells only\n        };\n\n        enum Roles\n        {\n            Role_Flags = Qt::UserRole,\n            Role_Display = Qt::UserRole+1,\n            Role_ColumnId = Qt::UserRole+2\n        };\n\n        enum Flags\n        {\n            Flag_Table = 1, // column should be displayed in table view\n            Flag_Dialogue = 2, // column should be displayed in dialogue view\n            Flag_Dialogue_List = 4, // column should be diaplyed in dialogue view\n            Flag_Dialogue_Refresh = 8 // refresh dialogue view if this column is modified\n        };\n\n        enum Display\n        {\n            Display_None, //Do not use\n            Display_String,\n            Display_LongString,\n\n            //CONCRETE TYPES STARTS HERE (for drag and drop)\n            Display_Skill,\n            Display_Class,\n            Display_Faction,\n            Display_Rank,\n            Display_Race,\n            Display_Sound,\n            Display_Region,\n            Display_Birthsign,\n            Display_Spell,\n            Display_Cell,\n            Display_Referenceable,\n            Display_Activator,\n            Display_Potion,\n            Display_Apparatus,\n            Display_Armor,\n            Display_Book,\n            Display_Clothing,\n            Display_Container,\n            Display_Creature,\n            Display_Door,\n            Display_Ingredient,\n            Display_CreatureLevelledList,\n            Display_ItemLevelledList,\n            Display_Light,\n            Display_Lockpick,\n            Display_Miscellaneous,\n            Display_Npc,\n            Display_Probe,\n            Display_Repair,\n            Display_Static,\n            Display_Weapon,\n            Display_Reference,\n            Display_Filter,\n            Display_Topic,\n            Display_Journal,\n            Display_TopicInfo,\n            Display_JournalInfo,\n            Display_Scene,\n            Display_GlobalVariable,\n            Display_BodyPart,\n            Display_Enchantment,\n            //CONCRETE TYPES ENDS HERE\n\n            Display_SignedInteger8,\n            Display_SignedInteger16,\n            Display_UnsignedInteger8,\n            Display_UnsignedInteger16,\n            Display_Integer,\n            Display_Float,\n            Display_Double,\n            Display_Var,\n            Display_GmstVarType,\n            Display_GlobalVarType,\n            Display_Specialisation,\n            Display_Attribute,\n            Display_Boolean,\n            Display_SpellType,\n            Display_Script,\n            Display_ApparatusType,\n            Display_ArmorType,\n            Display_ClothingType,\n            Display_CreatureType,\n            Display_WeaponType,\n            Display_RecordState,\n            Display_RefRecordType,\n            Display_DialogueType,\n            Display_QuestStatusType,\n            Display_EnchantmentType,\n            Display_BodyPartType,\n            Display_MeshType,\n            Display_Gender,\n            Display_Mesh,\n            Display_Icon,\n            Display_Music,\n            Display_SoundRes,\n            Display_Texture,\n            Display_Video,\n            Display_Colour,\n            Display_ScriptFile,\n            Display_ScriptLines, // console context\n            Display_SoundGeneratorType,\n            Display_School,\n            Display_Id,\n            Display_SkillId,\n            Display_EffectRange,\n            Display_EffectId,\n            Display_PartRefType,\n            Display_AiPackageType,\n            Display_InfoCondFunc,\n            Display_InfoCondVar,\n            Display_InfoCondComp,\n            Display_String32,\n            Display_LongString256,\n            Display_BookType,\n            Display_BloodType,\n            Display_EmitterType,\n\n            Display_EffectSkill,     // must display at least one, unlike Display_Skill\n            Display_EffectAttribute, // must display at least one, unlike Display_Attribute\n            Display_IngredEffectId,  // display none allowed, unlike Display_EffectId\n            Display_GenderNpc,       // must display at least one, unlike Display_Gender\n\n            //top level columns that nest other columns\n            Display_NestedHeader\n        };\n\n        int mColumnId;\n        int mFlags;\n        Display mDisplayType;\n\n        ColumnBase (int columnId, Display displayType, int flag);\n\n        virtual ~ColumnBase();\n\n        virtual bool isEditable() const = 0;\n\n        virtual bool isUserEditable() const;\n        ///< Can this column be edited directly by the user?\n\n        virtual std::string getTitle() const;\n\n        virtual int getId() const;\n\n        static bool isId (Display display);\n\n        static bool isText (Display display);\n\n        static bool isScript (Display display);\n    };\n\n    class NestableColumn : public ColumnBase\n    {\n        std::vector<NestableColumn *> mNestedColumns;\n\n    public:\n\n        NestableColumn(int columnId, Display displayType, int flag);\n\n        ~NestableColumn();\n\n        void addColumn(CSMWorld::NestableColumn *column);\n\n        const ColumnBase& nestedColumn(int subColumn) const;\n\n        bool hasChildren() const;\n    };\n\n    template<typename ESXRecordT>\n    struct Column : public NestableColumn\n    {\n        Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue)\n        : NestableColumn (columnId, displayType, flags) {}\n\n        virtual QVariant get (const Record<ESXRecordT>& record) const = 0;\n\n        virtual void set (Record<ESXRecordT>& record, const QVariant& data)\n        {\n            throw std::logic_error (\"Column \" + getTitle() + \" is not editable\");\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct NestedParentColumn : public Column<ESXRecordT>\n    {\n        NestedParentColumn (int id, int flags = ColumnBase::Flag_Dialogue, bool fixedRows = false)\n            : Column<ESXRecordT> (id, ColumnBase::Display_NestedHeader, flags), mFixedRows(fixedRows)\n        {}\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            // There is nothing to do here.\n            // This prevents exceptions from parent's implementation\n        }\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            // by default editable; also see IdTree::hasChildren()\n            if (mFixedRows)\n                return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);\n            else\n                return QVariant::fromValue(ColumnBase::TableEdit_Full);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n    private:\n        bool mFixedRows;\n    };\n\n    struct NestedChildColumn : public NestableColumn\n    {\n        NestedChildColumn (int id,\n                Display display, int flags = ColumnBase::Flag_Dialogue, bool isEditable = true);\n\n        bool isEditable() const override;\n\n    private:\n        bool mIsEditable;\n    };\n}\n\nQ_DECLARE_METATYPE(CSMWorld::ColumnBase::TableEditModes)\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/columnimp.cpp",
    "content": "#include \"columnimp.hpp\"\n\n#include <stdexcept>\n#include <QVector>\n\nnamespace CSMWorld\n{\n    /* LandTextureNicknameColumn */\n    LandTextureNicknameColumn::LandTextureNicknameColumn()\n        : Column<LandTexture>(Columns::ColumnId_TextureNickname, ColumnBase::Display_String)\n    {\n    }\n\n    QVariant LandTextureNicknameColumn::get(const Record<LandTexture>& record) const\n    {\n        return QString::fromUtf8(record.get().mId.c_str());\n    }\n\n    void LandTextureNicknameColumn::set(Record<LandTexture>& record, const QVariant& data)\n    {\n        LandTexture copy = record.get();\n        copy.mId = data.toString().toUtf8().constData();\n        record.setModified(copy);\n    }\n\n    bool LandTextureNicknameColumn::isEditable() const\n    {\n        return true;\n    }\n\n    /* LandTextureIndexColumn */\n    LandTextureIndexColumn::LandTextureIndexColumn()\n        : Column<LandTexture>(Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer)\n    {\n    }\n\n    QVariant LandTextureIndexColumn::get(const Record<LandTexture>& record) const\n    {\n        return record.get().mIndex;\n    }\n\n    bool LandTextureIndexColumn::isEditable() const\n    {\n        return false;\n    }\n\n    /* LandPluginIndexColumn */\n    LandPluginIndexColumn::LandPluginIndexColumn()\n        : Column<Land>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)\n    {\n    }\n\n    QVariant LandPluginIndexColumn::get(const Record<Land>& record) const\n    {\n        return record.get().mPlugin;\n    }\n\n    bool LandPluginIndexColumn::isEditable() const\n    {\n        return false;\n    }\n\n    /* LandTexturePluginIndexColumn */\n    LandTexturePluginIndexColumn::LandTexturePluginIndexColumn()\n        : Column<LandTexture>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)\n    {\n    }\n\n    QVariant LandTexturePluginIndexColumn::get(const Record<LandTexture>& record) const\n    {\n        return record.get().mPluginIndex;\n    }\n\n    bool LandTexturePluginIndexColumn::isEditable() const\n    {\n        return false;\n    }\n\n    /* LandNormalsColumn */\n    LandNormalsColumn::LandNormalsColumn()\n        : Column<Land>(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0)\n    {\n    }\n\n    QVariant LandNormalsColumn::get(const Record<Land>& record) const\n    {\n        const int Size = Land::LAND_NUM_VERTS * 3;\n        const Land& land = record.get();\n\n        DataType values(Size, 0);\n\n        if (land.isDataLoaded(Land::DATA_VNML))\n        {\n            for (int i = 0; i < Size; ++i)\n                values[i] = land.getLandData()->mNormals[i];\n        }\n\n        QVariant variant;\n        variant.setValue(values);\n        return variant;\n    }\n\n    void LandNormalsColumn::set(Record<Land>& record, const QVariant& data)\n    {\n        DataType values = data.value<DataType>();\n\n        if (values.size() != Land::LAND_NUM_VERTS * 3)\n            throw std::runtime_error(\"invalid land normals data\");\n\n        Land copy = record.get();\n        copy.add(Land::DATA_VNML);\n\n        for (int i = 0; i < values.size(); ++i)\n        {\n            copy.getLandData()->mNormals[i] = values[i];\n        }\n\n        record.setModified(copy);\n    }\n\n    bool LandNormalsColumn::isEditable() const\n    {\n        return true;\n    }\n\n    /* LandHeightsColumn */\n    LandHeightsColumn::LandHeightsColumn()\n        : Column<Land>(Columns::ColumnId_LandHeightsIndex, ColumnBase::Display_String, 0)\n    {\n    }\n\n    QVariant LandHeightsColumn::get(const Record<Land>& record) const\n    {\n        const int Size = Land::LAND_NUM_VERTS;\n        const Land& land = record.get();\n\n        DataType values(Size, 0);\n\n        if (land.isDataLoaded(Land::DATA_VHGT))\n        {\n            for (int i = 0; i < Size; ++i)\n                values[i] = land.getLandData()->mHeights[i];\n        }\n\n        QVariant variant;\n        variant.setValue(values);\n        return variant;\n    }\n\n    void LandHeightsColumn::set(Record<Land>& record, const QVariant& data)\n    {\n        DataType values = data.value<DataType>();\n\n        if (values.size() != Land::LAND_NUM_VERTS)\n            throw std::runtime_error(\"invalid land heights data\");\n\n        Land copy = record.get();\n        copy.add(Land::DATA_VHGT);\n\n        for (int i = 0; i < values.size(); ++i)\n        {\n            copy.getLandData()->mHeights[i] = values[i];\n        }\n\n        record.setModified(copy);\n    }\n\n    bool LandHeightsColumn::isEditable() const\n    {\n        return true;\n    }\n\n    /* LandColoursColumn */\n    LandColoursColumn::LandColoursColumn()\n        : Column<Land>(Columns::ColumnId_LandColoursIndex, ColumnBase::Display_String, 0)\n    {\n    }\n\n    QVariant LandColoursColumn::get(const Record<Land>& record) const\n    {\n        const int Size = Land::LAND_NUM_VERTS * 3;\n        const Land& land = record.get();\n\n        DataType values(Size, 0);\n\n        if (land.isDataLoaded(Land::DATA_VCLR))\n        {\n            for (int i = 0; i < Size; ++i)\n                values[i] = land.getLandData()->mColours[i];\n        }\n\n        QVariant variant;\n        variant.setValue(values);\n        return variant;\n    }\n\n    void LandColoursColumn::set(Record<Land>& record, const QVariant& data)\n    {\n        DataType values = data.value<DataType>();\n\n        if (values.size() != Land::LAND_NUM_VERTS * 3)\n            throw std::runtime_error(\"invalid land colours data\");\n\n        Land copy = record.get();\n        copy.add(Land::DATA_VCLR);\n\n        for (int i = 0; i < values.size(); ++i)\n        {\n            copy.getLandData()->mColours[i] = values[i];\n        }\n\n        record.setModified(copy);\n    }\n\n    bool LandColoursColumn::isEditable() const\n    {\n        return true;\n    }\n\n    /* LandTexturesColumn */\n    LandTexturesColumn::LandTexturesColumn()\n        : Column<Land>(Columns::ColumnId_LandTexturesIndex, ColumnBase::Display_String, 0)\n    {\n    }\n\n    QVariant LandTexturesColumn::get(const Record<Land>& record) const\n    {\n        const int Size = Land::LAND_NUM_TEXTURES;\n        const Land& land = record.get();\n\n        DataType values(Size, 0);\n\n        if (land.isDataLoaded(Land::DATA_VTEX))\n        {\n            for (int i = 0; i < Size; ++i)\n                values[i] = land.getLandData()->mTextures[i];\n        }\n\n        QVariant variant;\n        variant.setValue(values);\n        return variant;\n    }\n\n    void LandTexturesColumn::set(Record<Land>& record, const QVariant& data)\n    {\n        DataType values = data.value<DataType>();\n\n        if (values.size() != Land::LAND_NUM_TEXTURES)\n            throw std::runtime_error(\"invalid land textures data\");\n\n        Land copy = record.get();\n        copy.add(Land::DATA_VTEX);\n\n        for (int i = 0; i < values.size(); ++i)\n        {\n            copy.getLandData()->mTextures[i] = values[i];\n        }\n\n        record.setModified(copy);\n    }\n\n    bool LandTexturesColumn::isEditable() const\n    {\n        return true;\n    }\n\n    /* BodyPartRaceColumn */\n    BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType)\n        : mMeshType(meshType)\n    {}\n\n    QVariant BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const\n    {\n        if (mMeshType != nullptr && mMeshType->get(record) == ESM::BodyPart::MT_Skin)\n        {\n            return QString::fromUtf8(record.get().mRace.c_str());\n        }\n        return QVariant(QVariant::UserType);\n    }\n\n    void BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVariant &data)\n    {\n        ESM::BodyPart record2 = record.get();\n\n        record2.mRace = data.toString().toUtf8().constData();\n\n        record.setModified(record2);\n    }\n\n    bool BodyPartRaceColumn::isEditable() const\n    {\n        return true;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/columnimp.hpp",
    "content": "#ifndef CSM_WOLRD_COLUMNIMP_H\n#define CSM_WOLRD_COLUMNIMP_H\n\n#include <cassert>\n#include <cstdint>\n#include <sstream>\n#include <stdexcept>\n\n#include <QColor>\n#include <QVector>\n\n#include <components/esm/loadbody.hpp>\n#include <components/esm/loadskil.hpp>\n#include <components/esm/loadrace.hpp>\n\n#include \"columnbase.hpp\"\n#include \"columns.hpp\"\n#include \"info.hpp\"\n\n#include \"land.hpp\"\n#include \"landtexture.hpp\"\n\nnamespace CSMWorld\n{\n    /// \\note Shares ID with VarValueColumn. A table can not have both.\n    template<typename ESXRecordT>\n    struct FloatValueColumn : public Column<ESXRecordT>\n    {\n        FloatValueColumn() : Column<ESXRecordT> (Columns::ColumnId_Value, ColumnBase::Display_Float) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mValue.getFloat();\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mValue.setFloat (data.toFloat());\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct StringIdColumn : public Column<ESXRecordT>\n    {\n        StringIdColumn (bool hidden = false)\n        : Column<ESXRecordT> (Columns::ColumnId_Id, ColumnBase::Display_Id,\n            hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mId.c_str());\n        }\n\n        bool isEditable() const override\n        {\n            return false;\n        }\n    };\n\n    template<>\n    inline QVariant StringIdColumn<Land>::get(const Record<Land>& record) const\n    {\n        const Land& land = record.get();\n        return QString::fromUtf8(Land::createUniqueRecordId(land.mX, land.mY).c_str());\n    }\n\n    template<>\n    inline QVariant StringIdColumn<LandTexture>::get(const Record<LandTexture>& record) const\n    {\n        const LandTexture& ltex = record.get();\n        return QString::fromUtf8(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str());\n    }\n\n    template<typename ESXRecordT>\n    struct RecordStateColumn : public Column<ESXRecordT>\n    {\n        RecordStateColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Modification, ColumnBase::Display_RecordState)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            if (record.mState==Record<ESXRecordT>::State_Erased)\n                return static_cast<int> (Record<ESXRecordT>::State_Deleted);\n\n            return static_cast<int> (record.mState);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            record.mState = static_cast<RecordBase::State> (data.toInt());\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n        bool isUserEditable() const override\n        {\n            return false;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct FixedRecordTypeColumn : public Column<ESXRecordT>\n    {\n        int mType;\n\n        FixedRecordTypeColumn (int type)\n        : Column<ESXRecordT> (Columns::ColumnId_RecordType, ColumnBase::Display_Integer, 0),\n          mType (type)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return mType;\n        }\n\n        bool isEditable() const override\n        {\n            return false;\n        }\n    };\n\n    /// \\attention A var type column must be immediately followed by a suitable value column.\n    template<typename ESXRecordT>\n    struct VarTypeColumn : public Column<ESXRecordT>\n    {\n        VarTypeColumn (ColumnBase::Display display)\n            : Column<ESXRecordT> (Columns::ColumnId_ValueType, display, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mValue.getType());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mValue.setType (static_cast<ESM::VarType> (data.toInt()));\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    /// \\note Shares ID with FloatValueColumn. A table can not have both.\n    template<typename ESXRecordT>\n    struct VarValueColumn : public Column<ESXRecordT>\n    {\n        VarValueColumn() : Column<ESXRecordT> (Columns::ColumnId_Value, ColumnBase::Display_Var, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            switch (record.get().mValue.getType())\n            {\n                case ESM::VT_String:\n\n                    return QString::fromUtf8 (record.get().mValue.getString().c_str());\n\n                case ESM::VT_Int:\n                case ESM::VT_Short:\n                case ESM::VT_Long:\n\n                    return record.get().mValue.getInteger();\n\n                case ESM::VT_Float:\n\n                    return record.get().mValue.getFloat();\n\n                default: return QVariant();\n            }\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            switch (record2.mValue.getType())\n            {\n                case ESM::VT_String:\n\n                    record2.mValue.setString (data.toString().toUtf8().constData());\n                    break;\n\n                case ESM::VT_Int:\n                case ESM::VT_Short:\n                case ESM::VT_Long:\n\n                    record2.mValue.setInteger (data.toInt());\n                    break;\n\n                case ESM::VT_Float:\n\n                    record2.mValue.setFloat (data.toFloat());\n                    break;\n\n                default: break;\n            }\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct DescriptionColumn : public Column<ESXRecordT>\n    {\n        DescriptionColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Description, ColumnBase::Display_LongString)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mDescription.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mDescription = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SpecialisationColumn : public Column<ESXRecordT>\n    {\n        SpecialisationColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Specialisation, ColumnBase::Display_Specialisation)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mSpecialization;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mSpecialization = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct UseValueColumn : public Column<ESXRecordT>\n    {\n        int mIndex;\n\n        UseValueColumn (int index)\n        : Column<ESXRecordT> (Columns::ColumnId_UseValue1 + index,  ColumnBase::Display_Float),\n          mIndex (index)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mUseValue[mIndex];\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mUseValue[mIndex] = data.toFloat();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct AttributeColumn : public Column<ESXRecordT>\n    {\n        AttributeColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mAttribute;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mAttribute = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct NameColumn : public Column<ESXRecordT>\n    {\n        NameColumn() : Column<ESXRecordT> (Columns::ColumnId_Name, ColumnBase::Display_String) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mName.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mName = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct AttributesColumn : public Column<ESXRecordT>\n    {\n        int mIndex;\n\n        AttributesColumn (int index)\n        : Column<ESXRecordT> (Columns::ColumnId_Attribute1 + index, ColumnBase::Display_Attribute),\n          mIndex (index)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mAttribute[mIndex];\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mAttribute[mIndex] = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SkillsColumn : public Column<ESXRecordT>\n    {\n        int mIndex;\n        bool mMajor;\n\n        SkillsColumn (int index, bool typePrefix = false, bool major = false)\n        : Column<ESXRecordT> ((typePrefix ? (\n            major ? Columns::ColumnId_MajorSkill1 : Columns::ColumnId_MinorSkill1) :\n            Columns::ColumnId_Skill1) + index, ColumnBase::Display_Skill),\n            mIndex (index), mMajor (major)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            int skill = record.get().mData.getSkill (mIndex, mMajor);\n\n            return QString::fromUtf8 (ESM::Skill::indexToId (skill).c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            std::istringstream stream (data.toString().toUtf8().constData());\n\n            int index = -1;\n            char c;\n\n            stream >> c >> index;\n\n            if (index!=-1)\n            {\n                ESXRecordT record2 = record.get();\n\n                record2.mData.getSkill (mIndex, mMajor) = index;\n\n                record.setModified (record2);\n            }\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct PlayableColumn : public Column<ESXRecordT>\n    {\n        PlayableColumn() : Column<ESXRecordT> (Columns::ColumnId_Playable, ColumnBase::Display_Boolean)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mIsPlayable!=0;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mIsPlayable = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct HiddenColumn : public Column<ESXRecordT>\n    {\n        HiddenColumn() : Column<ESXRecordT> (Columns::ColumnId_Hidden, ColumnBase::Display_Boolean) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mIsHidden!=0;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mIsHidden = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct FlagColumn : public Column<ESXRecordT>\n    {\n        int mMask;\n        bool mInverted;\n\n        FlagColumn (int columnId, int mask,\n                int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, bool inverted = false)\n        : Column<ESXRecordT> (columnId, ColumnBase::Display_Boolean, flags), mMask (mask),\n          mInverted (inverted)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            bool flag = (record.get().mData.mFlags & mMask)!=0;\n\n            if (mInverted)\n                flag = !flag;\n\n            return flag;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            int flags = record2.mData.mFlags & ~mMask;\n\n            if ((data.toInt()!=0)!=mInverted)\n                flags |= mMask;\n\n            record2.mData.mFlags = flags;\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct FlagColumn2 : public Column<ESXRecordT>\n    {\n        int mMask;\n        bool mInverted;\n\n        FlagColumn2 (int columnId, int mask, bool inverted = false)\n        : Column<ESXRecordT> (columnId, ColumnBase::Display_Boolean), mMask (mask),\n          mInverted (inverted)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            bool flag = (record.get().mFlags & mMask)!=0;\n\n            if (mInverted)\n                flag = !flag;\n\n            return flag;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            int flags = record2.mFlags & ~mMask;\n\n            if ((data.toInt()!=0)!=mInverted)\n                flags |= mMask;\n\n            record2.mFlags = flags;\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct WeightHeightColumn : public Column<ESXRecordT>\n    {\n        bool mMale;\n        bool mWeight;\n\n        WeightHeightColumn (bool male, bool weight)\n        : Column<ESXRecordT> (male ?\n          (weight ? Columns::ColumnId_MaleWeight : Columns::ColumnId_MaleHeight) :\n          (weight ? Columns::ColumnId_FemaleWeight : Columns::ColumnId_FemaleHeight),\n          ColumnBase::Display_Float),\n          mMale (male), mWeight (weight)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            const ESM::Race::MaleFemaleF& value =\n                mWeight ? record.get().mData.mWeight : record.get().mData.mHeight;\n\n            return mMale ? value.mMale : value.mFemale;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            ESM::Race::MaleFemaleF& value =\n                mWeight ? record2.mData.mWeight : record2.mData.mHeight;\n\n            (mMale ? value.mMale : value.mFemale) = data.toFloat();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SoundParamColumn : public Column<ESXRecordT>\n    {\n        enum Type\n        {\n            Type_Volume,\n            Type_MinRange,\n            Type_MaxRange\n        };\n\n        Type mType;\n\n        SoundParamColumn (Type type)\n        : Column<ESXRecordT> (type==Type_Volume ? Columns::ColumnId_Volume :\n          (type==Type_MinRange ? Columns::ColumnId_MinRange : Columns::ColumnId_MaxRange),\n          ColumnBase::Display_Integer),\n          mType (type)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            int value = 0;\n\n            switch (mType)\n            {\n                case Type_Volume: value = record.get().mData.mVolume; break;\n                case Type_MinRange: value = record.get().mData.mMinRange; break;\n                case Type_MaxRange: value = record.get().mData.mMaxRange; break;\n            }\n\n            return value;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            int value = data.toInt();\n\n            if (value<0)\n                value = 0;\n            else if (value>255)\n                value = 255;\n\n            ESXRecordT record2 = record.get();\n\n            switch (mType)\n            {\n                case Type_Volume: record2.mData.mVolume = static_cast<unsigned char> (value); break;\n                case Type_MinRange: record2.mData.mMinRange = static_cast<unsigned char> (value); break;\n                case Type_MaxRange: record2.mData.mMaxRange = static_cast<unsigned char> (value); break;\n            }\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SoundFileColumn : public Column<ESXRecordT>\n    {\n        SoundFileColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_SoundFile, ColumnBase::Display_SoundRes)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mSound.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mSound = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct MapColourColumn : public Column<ESXRecordT>\n    {\n        MapColourColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_MapColour, ColumnBase::Display_Colour)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mMapColor;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT copy = record.get();\n            copy.mMapColor = data.toInt();\n            record.setModified (copy);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SleepListColumn : public Column<ESXRecordT>\n    {\n        SleepListColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_SleepEncounter, ColumnBase::Display_CreatureLevelledList)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mSleepList.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mSleepList = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct TextureColumn : public Column<ESXRecordT>\n    {\n        TextureColumn() : Column<ESXRecordT> (Columns::ColumnId_Texture, ColumnBase::Display_Texture) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mTexture.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mTexture = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SpellTypeColumn : public Column<ESXRecordT>\n    {\n        SpellTypeColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_SpellType, ColumnBase::Display_SpellType)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mType;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mType = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct CostColumn : public Column<ESXRecordT>\n    {\n        CostColumn() : Column<ESXRecordT> (Columns::ColumnId_Cost, ColumnBase::Display_Integer) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mCost;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mData.mCost = data.toInt();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct ScriptColumn : public Column<ESXRecordT>\n    {\n        enum Type\n        {\n            Type_File, // regular script record\n            Type_Lines, // console context\n            Type_Info // dialogue context (not implemented yet)\n        };\n\n        ScriptColumn (Type type)\n        : Column<ESXRecordT> (Columns::ColumnId_ScriptText,\n            type==Type_File ? ColumnBase::Display_ScriptFile : ColumnBase::Display_ScriptLines,\n            type==Type_File ? 0 : ColumnBase::Flag_Dialogue)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mScriptText.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mScriptText = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct RegionColumn : public Column<ESXRecordT>\n    {\n        RegionColumn() : Column<ESXRecordT> (Columns::ColumnId_Region, ColumnBase::Display_Region) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mRegion.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mRegion = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct CellColumn : public Column<ESXRecordT>\n    {\n        bool mBlocked;\n\n        /// \\param blocked Do not allow user-modification\n        CellColumn (bool blocked = false)\n        : Column<ESXRecordT> (Columns::ColumnId_Cell, ColumnBase::Display_Cell),\n          mBlocked (blocked)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mCell.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mCell = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n        bool isUserEditable() const override\n        {\n            return !mBlocked;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct OriginalCellColumn : public Column<ESXRecordT>\n    {\n        OriginalCellColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_OriginalCell, ColumnBase::Display_Cell)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mOriginalCell.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mOriginalCell = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n        bool isUserEditable() const override\n        {\n            return false;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct IdColumn : public Column<ESXRecordT>\n    {\n        IdColumn() : Column<ESXRecordT> (Columns::ColumnId_ReferenceableId,\n            ColumnBase::Display_Referenceable) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mRefID.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mRefID = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct ScaleColumn : public Column<ESXRecordT>\n    {\n        ScaleColumn() : Column<ESXRecordT> (Columns::ColumnId_Scale, ColumnBase::Display_Float) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mScale;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mScale = data.toFloat();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct OwnerColumn : public Column<ESXRecordT>\n    {\n        OwnerColumn() : Column<ESXRecordT> (Columns::ColumnId_Owner, ColumnBase::Display_Npc) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mOwner.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mOwner = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SoulColumn : public Column<ESXRecordT>\n    {\n        SoulColumn() : Column<ESXRecordT> (Columns::ColumnId_Soul, ColumnBase::Display_Creature) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mSoul.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mSoul = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct FactionColumn : public Column<ESXRecordT>\n    {\n        FactionColumn() : Column<ESXRecordT> (Columns::ColumnId_Faction, ColumnBase::Display_Faction) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mFaction.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mFaction = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct FactionIndexColumn : public Column<ESXRecordT>\n    {\n        FactionIndexColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_FactionIndex, ColumnBase::Display_Integer)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mFactionRank;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mFactionRank = data.toInt();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct ChargesColumn : public Column<ESXRecordT>\n    {\n        ChargesColumn() : Column<ESXRecordT> (Columns::ColumnId_Charges, ColumnBase::Display_Integer) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mChargeInt;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mChargeInt = data.toInt();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct EnchantmentChargesColumn : public Column<ESXRecordT>\n    {\n        EnchantmentChargesColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Enchantment, ColumnBase::Display_Float)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mEnchantmentCharge;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mEnchantmentCharge = data.toFloat();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct GoldValueColumn : public Column<ESXRecordT>\n    {\n        GoldValueColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mGoldValue;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mGoldValue = data.toInt();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct TeleportColumn : public Column<ESXRecordT>\n    {\n        TeleportColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Teleport, ColumnBase::Display_Boolean)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mTeleport;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mTeleport = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct TeleportCellColumn : public Column<ESXRecordT>\n    {\n        TeleportCellColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_TeleportCell, ColumnBase::Display_Cell)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mDestCell.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mDestCell = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n        bool isUserEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct LockLevelColumn : public Column<ESXRecordT>\n    {\n        LockLevelColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_LockLevel, ColumnBase::Display_Integer)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mLockLevel;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mLockLevel = data.toInt();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct KeyColumn : public Column<ESXRecordT>\n    {\n        KeyColumn() : Column<ESXRecordT> (Columns::ColumnId_Key, ColumnBase::Display_Miscellaneous) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mKey.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mKey = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct TrapColumn : public Column<ESXRecordT>\n    {\n        TrapColumn() : Column<ESXRecordT> (Columns::ColumnId_Trap, ColumnBase::Display_Spell) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mTrap.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mTrap = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct FilterColumn : public Column<ESXRecordT>\n    {\n        FilterColumn() : Column<ESXRecordT> (Columns::ColumnId_Filter, ColumnBase::Display_Filter) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mFilter.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mFilter = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct PosColumn : public Column<ESXRecordT>\n    {\n        ESM::Position ESXRecordT::* mPosition;\n        int mIndex;\n\n        PosColumn (ESM::Position ESXRecordT::* position, int index, bool door)\n        : Column<ESXRecordT> (\n          (door ? Columns::ColumnId_DoorPositionXPos : Columns::ColumnId_PositionXPos)+index,\n          ColumnBase::Display_Float), mPosition (position), mIndex (index) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            const ESM::Position& position = record.get().*mPosition;\n            return position.pos[mIndex];\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            ESM::Position& position = record2.*mPosition;\n\n            position.pos[mIndex] = data.toFloat();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct RotColumn : public Column<ESXRecordT>\n    {\n        ESM::Position ESXRecordT::* mPosition;\n        int mIndex;\n\n        RotColumn (ESM::Position ESXRecordT::* position, int index, bool door)\n        : Column<ESXRecordT> (\n          (door ? Columns::ColumnId_DoorPositionXRot : Columns::ColumnId_PositionXRot)+index,\n          ColumnBase::Display_Double), mPosition (position), mIndex (index) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            const ESM::Position& position = record.get().*mPosition;\n            return osg::RadiansToDegrees(position.rot[mIndex]);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            ESM::Position& position = record2.*mPosition;\n\n            position.rot[mIndex] = osg::DegreesToRadians(data.toFloat());\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct DialogueTypeColumn : public Column<ESXRecordT>\n    {\n        DialogueTypeColumn (bool hidden = false)\n        : Column<ESXRecordT> (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType,\n            hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mType);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mType = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n        bool isUserEditable() const override\n        {\n            return false;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct QuestStatusTypeColumn : public Column<ESXRecordT>\n    {\n        QuestStatusTypeColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_QuestStatusType, ColumnBase::Display_QuestStatusType)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mQuestStatus);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mQuestStatus = static_cast<Info::QuestStatus> (data.toInt());\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct QuestDescriptionColumn : public Column<ESXRecordT>\n    {\n        QuestDescriptionColumn() : Column<ESXRecordT> (Columns::ColumnId_QuestDescription, ColumnBase::Display_LongString) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mResponse.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mResponse = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct QuestIndexColumn : public Column<ESXRecordT>\n    {\n        QuestIndexColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_QuestIndex, ColumnBase::Display_Integer)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mDisposition;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mData.mDisposition = data.toInt();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct TopicColumn : public Column<ESXRecordT>\n    {\n        TopicColumn (bool journal)\n        : Column<ESXRecordT> (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic,\n                              journal ? ColumnBase::Display_Journal : ColumnBase::Display_Topic)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mTopicId.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mTopicId = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n        bool isUserEditable() const override\n        {\n            return false;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct ActorColumn : public Column<ESXRecordT>\n    {\n        ActorColumn() : Column<ESXRecordT> (Columns::ColumnId_Actor, ColumnBase::Display_Npc) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mActor.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mActor = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct RaceColumn : public Column<ESXRecordT>\n    {\n        RaceColumn() : Column<ESXRecordT> (Columns::ColumnId_Race, ColumnBase::Display_Race) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mRace.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mRace = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct ClassColumn : public Column<ESXRecordT>\n    {\n        ClassColumn() : Column<ESXRecordT> (Columns::ColumnId_Class, ColumnBase::Display_Class) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mClass.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mClass = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct PcFactionColumn : public Column<ESXRecordT>\n    {\n        PcFactionColumn() : Column<ESXRecordT> (Columns::ColumnId_PcFaction, ColumnBase::Display_Faction) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mPcFaction.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mPcFaction = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct ResponseColumn : public Column<ESXRecordT>\n    {\n        ResponseColumn() : Column<ESXRecordT> (Columns::ColumnId_Response, ColumnBase::Display_LongString) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mResponse.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mResponse = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct DispositionColumn : public Column<ESXRecordT>\n    {\n        DispositionColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Disposition, ColumnBase::Display_Integer)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mDisposition;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mData.mDisposition = data.toInt();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct RankColumn : public Column<ESXRecordT>\n    {\n        RankColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Rank, ColumnBase::Display_Integer)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mData.mRank);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mData.mRank = static_cast<signed char> (data.toInt());\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct PcRankColumn : public Column<ESXRecordT>\n    {\n        PcRankColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_PcRank, ColumnBase::Display_Integer)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mData.mPCrank);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mData.mPCrank = static_cast<signed char> (data.toInt());\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct GenderColumn : public Column<ESXRecordT>\n    {\n        GenderColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Gender, ColumnBase::Display_Gender)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mData.mGender);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mGender = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct GenderNpcColumn : public Column<ESXRecordT>\n    {\n        GenderNpcColumn()\n            : Column<ESXRecordT>(Columns::ColumnId_Gender, ColumnBase::Display_GenderNpc)\n        {}\n\n        QVariant get(const Record<ESXRecordT>& record) const override\n        {\n            // Implemented this way to allow additional gender types in the future.\n            if ((record.get().mData.mFlags & ESM::BodyPart::BPF_Female) == ESM::BodyPart::BPF_Female)\n                return 1;\n\n            return 0;\n        }\n\n        void set(Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            // Implemented this way to allow additional gender types in the future.\n            if (data.toInt() == 1)\n                record2.mData.mFlags = (record2.mData.mFlags & ~ESM::BodyPart::BPF_Female) | ESM::BodyPart::BPF_Female;\n            else\n                record2.mData.mFlags = record2.mData.mFlags & ~ESM::BodyPart::BPF_Female;\n\n            record.setModified(record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct EnchantmentTypeColumn : public Column<ESXRecordT>\n    {\n        EnchantmentTypeColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_EnchantmentType, ColumnBase::Display_EnchantmentType)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mData.mType);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mType = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct ChargesColumn2 : public Column<ESXRecordT>\n    {\n        ChargesColumn2() : Column<ESXRecordT> (Columns::ColumnId_Charges, ColumnBase::Display_Integer) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mCharge;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mData.mCharge = data.toInt();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct AutoCalcColumn : public Column<ESXRecordT>\n    {\n        AutoCalcColumn() : Column<ESXRecordT> (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mAutocalc!=0;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mAutocalc = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct ModelColumn : public Column<ESXRecordT>\n    {\n        ModelColumn() : Column<ESXRecordT> (Columns::ColumnId_Model, ColumnBase::Display_Mesh) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mModel.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mModel = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct VampireColumn : public Column<ESXRecordT>\n    {\n        VampireColumn() : Column<ESXRecordT> (Columns::ColumnId_Vampire, ColumnBase::Display_Boolean)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mVampire!=0;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mVampire = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct BodyPartTypeColumn : public Column<ESXRecordT>\n    {\n        BodyPartTypeColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_BodyPartType, ColumnBase::Display_BodyPartType)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mData.mPart);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mPart = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct MeshTypeColumn : public Column<ESXRecordT>\n    {\n        MeshTypeColumn(int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)\n        : Column<ESXRecordT> (Columns::ColumnId_MeshType, ColumnBase::Display_MeshType, flags)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mData.mType);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mType = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct OwnerGlobalColumn : public Column<ESXRecordT>\n    {\n        OwnerGlobalColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_OwnerGlobal, ColumnBase::Display_GlobalVariable)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mGlobalVariable.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mGlobalVariable = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct RefNumCounterColumn : public Column<ESXRecordT>\n    {\n        RefNumCounterColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_RefNumCounter, ColumnBase::Display_Integer, 0)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mRefNumCounter);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mRefNumCounter = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n        bool isUserEditable() const override\n        {\n            return false;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct RefNumColumn : public Column<ESXRecordT>\n    {\n        RefNumColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_RefNum, ColumnBase::Display_Integer, 0)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mRefNum.mIndex);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mRefNum.mIndex = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n\n        bool isUserEditable() const override\n        {\n            return false;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SoundColumn : public Column<ESXRecordT>\n    {\n        SoundColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Sound, ColumnBase::Display_Sound)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mSound.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mSound = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct CreatureColumn : public Column<ESXRecordT>\n    {\n        CreatureColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Creature, ColumnBase::Display_Creature)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mCreature.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mCreature = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SoundGeneratorTypeColumn : public Column<ESXRecordT>\n    {\n        SoundGeneratorTypeColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_SoundGeneratorType, ColumnBase::Display_SoundGeneratorType)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int> (record.get().mType);\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mType = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct BaseCostColumn : public Column<ESXRecordT>\n    {\n        BaseCostColumn() : Column<ESXRecordT> (Columns::ColumnId_BaseCost, ColumnBase::Display_Float) {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mBaseCost;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n            record2.mData.mBaseCost = data.toFloat();\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct SchoolColumn : public Column<ESXRecordT>\n    {\n        SchoolColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_School, ColumnBase::Display_School)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mData.mSchool;\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mData.mSchool = data.toInt();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct EffectTextureColumn : public Column<ESXRecordT>\n    {\n        EffectTextureColumn (Columns::ColumnId columnId)\n        : Column<ESXRecordT> (columnId,\n                              columnId == Columns::ColumnId_Particle ? ColumnBase::Display_Texture\n                                                                     : ColumnBase::Display_Icon)\n        {\n            assert (this->mColumnId==Columns::ColumnId_Icon ||\n                this->mColumnId==Columns::ColumnId_Particle);\n        }\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (\n                (this->mColumnId==Columns::ColumnId_Icon ?\n                record.get().mIcon : record.get().mParticle).c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            (this->mColumnId==Columns::ColumnId_Icon ?\n                record2.mIcon : record2.mParticle)\n                = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct EffectObjectColumn : public Column<ESXRecordT>\n    {\n        EffectObjectColumn (Columns::ColumnId columnId)\n        : Column<ESXRecordT> (columnId, columnId==Columns::ColumnId_BoltObject ? ColumnBase::Display_Weapon : ColumnBase::Display_Static)\n        {\n            assert (this->mColumnId==Columns::ColumnId_CastingObject ||\n                this->mColumnId==Columns::ColumnId_HitObject ||\n                this->mColumnId==Columns::ColumnId_AreaObject ||\n                this->mColumnId==Columns::ColumnId_BoltObject);\n        }\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            const std::string *string = nullptr;\n\n            switch (this->mColumnId)\n            {\n                case Columns::ColumnId_CastingObject: string = &record.get().mCasting; break;\n                case Columns::ColumnId_HitObject: string = &record.get().mHit; break;\n                case Columns::ColumnId_AreaObject: string = &record.get().mArea; break;\n                case Columns::ColumnId_BoltObject: string = &record.get().mBolt; break;\n            }\n\n            if (!string)\n                throw std::logic_error (\"Unsupported column ID\");\n\n            return QString::fromUtf8 (string->c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            std::string *string = nullptr;\n\n            ESXRecordT record2 = record.get();\n\n            switch (this->mColumnId)\n            {\n                case Columns::ColumnId_CastingObject: string = &record2.mCasting; break;\n                case Columns::ColumnId_HitObject: string = &record2.mHit; break;\n                case Columns::ColumnId_AreaObject: string = &record2.mArea; break;\n                case Columns::ColumnId_BoltObject: string = &record2.mBolt; break;\n            }\n\n            if (!string)\n                throw std::logic_error (\"Unsupported column ID\");\n\n            *string = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct EffectSoundColumn : public Column<ESXRecordT>\n    {\n        EffectSoundColumn (Columns::ColumnId columnId)\n        : Column<ESXRecordT> (columnId, ColumnBase::Display_Sound)\n        {\n            assert (this->mColumnId==Columns::ColumnId_CastingSound ||\n                this->mColumnId==Columns::ColumnId_HitSound ||\n                this->mColumnId==Columns::ColumnId_AreaSound ||\n                this->mColumnId==Columns::ColumnId_BoltSound);\n        }\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            const std::string *string = nullptr;\n\n            switch (this->mColumnId)\n            {\n                case Columns::ColumnId_CastingSound: string = &record.get().mCastSound; break;\n                case Columns::ColumnId_HitSound: string = &record.get().mHitSound; break;\n                case Columns::ColumnId_AreaSound: string = &record.get().mAreaSound; break;\n                case Columns::ColumnId_BoltSound: string = &record.get().mBoltSound; break;\n            }\n\n            if (!string)\n                throw std::logic_error (\"Unsupported column ID\");\n\n            return QString::fromUtf8 (string->c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            std::string *string = nullptr;\n\n            ESXRecordT record2 = record.get();\n\n            switch (this->mColumnId)\n            {\n                case Columns::ColumnId_CastingSound: string = &record2.mCastSound; break;\n                case Columns::ColumnId_HitSound: string = &record2.mHitSound; break;\n                case Columns::ColumnId_AreaSound: string = &record2.mAreaSound; break;\n                case Columns::ColumnId_BoltSound: string = &record2.mBoltSound; break;\n            }\n\n            if (!string)\n                throw std::logic_error (\"Unsupported column ID\");\n\n            *string = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct FormatColumn : public Column<ESXRecordT>\n    {\n        FormatColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_FileFormat, ColumnBase::Display_Integer)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return record.get().mFormat;\n        }\n\n        bool isEditable() const override\n        {\n            return false;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct AuthorColumn : public Column<ESXRecordT>\n    {\n        AuthorColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_Author, ColumnBase::Display_String32)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mAuthor.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mAuthor = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    template<typename ESXRecordT>\n    struct FileDescriptionColumn : public Column<ESXRecordT>\n    {\n        FileDescriptionColumn()\n        : Column<ESXRecordT> (Columns::ColumnId_FileDescription, ColumnBase::Display_LongString256)\n        {}\n\n        QVariant get (const Record<ESXRecordT>& record) const override\n        {\n            return QString::fromUtf8 (record.get().mDescription.c_str());\n        }\n\n        void set (Record<ESXRecordT>& record, const QVariant& data) override\n        {\n            ESXRecordT record2 = record.get();\n\n            record2.mDescription = data.toString().toUtf8().constData();\n\n            record.setModified (record2);\n        }\n\n        bool isEditable() const override\n        {\n            return true;\n        }\n    };\n\n    struct LandTextureNicknameColumn : public Column<LandTexture>\n    {\n        LandTextureNicknameColumn();\n\n        QVariant get(const Record<LandTexture>& record) const override;\n        void set(Record<LandTexture>& record, const QVariant& data) override;\n        bool isEditable() const override;\n    };\n\n    struct LandTextureIndexColumn : public Column<LandTexture>\n    {\n        LandTextureIndexColumn();\n\n        QVariant get(const Record<LandTexture>& record) const override;\n        bool isEditable() const override;\n    };\n\n    struct LandPluginIndexColumn : public Column<Land>\n    {\n        LandPluginIndexColumn();\n\n        QVariant get(const Record<Land>& record) const override;\n        bool isEditable() const override;\n    };\n\n    struct LandTexturePluginIndexColumn : public Column<LandTexture>\n    {\n        LandTexturePluginIndexColumn();\n\n        QVariant get(const Record<LandTexture>& record) const override;\n        bool isEditable() const override;\n    };\n\n    struct LandNormalsColumn : public Column<Land>\n    {\n        using DataType = QVector<signed char>;\n\n        LandNormalsColumn();\n\n        QVariant get(const Record<Land>& record) const override;\n        void set(Record<Land>& record, const QVariant& data) override;\n        bool isEditable() const override;\n    };\n\n    struct LandHeightsColumn : public Column<Land>\n    {\n        using DataType = QVector<float>;\n\n        LandHeightsColumn();\n\n        QVariant get(const Record<Land>& record) const override;\n        void set(Record<Land>& record, const QVariant& data) override;\n        bool isEditable() const override;\n    };\n\n    struct LandColoursColumn : public Column<Land>\n    {\n        using DataType = QVector<unsigned char>;\n\n        LandColoursColumn();\n\n        QVariant get(const Record<Land>& record) const override;\n        void set(Record<Land>& record, const QVariant& data) override;\n        bool isEditable() const override;\n    };\n\n    struct LandTexturesColumn : public Column<Land>\n    {\n        using DataType = QVector<uint16_t>;\n\n        LandTexturesColumn();\n\n        QVariant get(const Record<Land>& record) const override;\n        void set(Record<Land>& record, const QVariant& data) override;\n        bool isEditable() const override;\n    };\n\n    struct BodyPartRaceColumn : public RaceColumn<ESM::BodyPart>\n    {\n        const MeshTypeColumn<ESM::BodyPart> *mMeshType;\n\n        BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType);\n\n        QVariant get(const Record<ESM::BodyPart> &record) const override;\n        void set(Record<ESM::BodyPart> &record, const QVariant &data) override;\n        bool isEditable() const override;\n    };\n}\n\n// This is required to access the type as a QVariant.\nQ_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType)\nQ_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType)\nQ_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType)\nQ_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType)\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/columns.cpp",
    "content": "#include \"columns.hpp\"\n\n#include <components/fallback/fallback.hpp>\n#include <components/misc/stringops.hpp>\n\n#include \"universalid.hpp\"\n#include \"infoselectwrapper.hpp\"\n\nnamespace CSMWorld\n{\n    namespace Columns\n    {\n        struct ColumnDesc\n        {\n            int mId;\n            const char *mName;\n        };\n\n        const ColumnDesc sNames[] =\n        {\n            { ColumnId_Value, \"Value\" },\n            { ColumnId_Id, \"ID\" },\n            { ColumnId_Modification, \"Modified\" },\n            { ColumnId_RecordType, \"Record Type\" },\n            { ColumnId_ValueType, \"Value Type\" },\n            { ColumnId_Description, \"Description\" },\n            { ColumnId_Specialisation, \"Specialisation\" },\n            { ColumnId_Attribute, \"Attribute\" },\n            { ColumnId_Name, \"Name\" },\n            { ColumnId_Playable, \"Playable\" },\n            { ColumnId_Hidden, \"Hidden\" },\n            { ColumnId_MaleWeight, \"Male Weight\" },\n            { ColumnId_FemaleWeight, \"Female Weight\" },\n            { ColumnId_MaleHeight, \"Male Height\" },\n            { ColumnId_FemaleHeight, \"Female Height\" },\n            { ColumnId_Volume, \"Volume\" },\n            { ColumnId_MinRange, \"Min Range\" },\n            { ColumnId_MaxRange, \"Max Range\" },\n            { ColumnId_MinMagnitude, \"Min Magnitude\" },\n            { ColumnId_MaxMagnitude, \"Max Magnitude\" },\n            { ColumnId_SoundFile, \"Sound File\" },\n            { ColumnId_MapColour, \"Map Colour\" },\n            { ColumnId_SleepEncounter, \"Sleep Encounter\" },\n            { ColumnId_Texture, \"Texture\" },\n            { ColumnId_SpellType, \"Spell Type\" },\n            { ColumnId_Cost, \"Cost\" },\n            { ColumnId_ScriptText, \"Script Text\" },\n            { ColumnId_Region, \"Region\" },\n            { ColumnId_Cell, \"Cell\" },\n            { ColumnId_Scale, \"Scale\" },\n            { ColumnId_Owner, \"Owner\" },\n            { ColumnId_Soul, \"Soul\" },\n            { ColumnId_Faction, \"Faction\" },\n            { ColumnId_FactionIndex, \"Faction Index\" },\n            { ColumnId_Charges, \"Charges\" },\n            { ColumnId_Enchantment, \"Enchantment\" },\n            { ColumnId_CoinValue, \"Coin Value\" },\n            { ColumnId_Teleport, \"Teleport\" },\n            { ColumnId_TeleportCell, \"Teleport Cell\" },\n            { ColumnId_LockLevel, \"Lock Level\" },\n            { ColumnId_Key, \"Key\" },\n            { ColumnId_Trap, \"Trap\" },\n            { ColumnId_BeastRace, \"Beast Race\" },\n            { ColumnId_AutoCalc, \"Auto Calc\" },\n            { ColumnId_StarterSpell, \"Starter Spell\" },\n            { ColumnId_AlwaysSucceeds, \"Always Succeeds\" },\n            { ColumnId_SleepForbidden, \"Sleep Forbidden\" },\n            { ColumnId_InteriorWater, \"Interior Water\" },\n            { ColumnId_InteriorSky, \"Interior Sky\" },\n            { ColumnId_Model, \"Model/Animation\" },\n            { ColumnId_Script, \"Script\" },\n            { ColumnId_Icon, \"Icon\" },\n            { ColumnId_Weight, \"Weight\" },\n            { ColumnId_EnchantmentPoints, \"Enchantment Points\" },\n            { ColumnId_Quality, \"Quality\" },\n            { ColumnId_AiHello, \"AI Hello\" },\n            { ColumnId_AiFlee, \"AI Flee\" },\n            { ColumnId_AiFight, \"AI Fight\" },\n            { ColumnId_AiAlarm, \"AI Alarm\" },\n            { ColumnId_BuysWeapons, \"Buys Weapons\" },\n            { ColumnId_BuysArmor, \"Buys Armor\" },\n            { ColumnId_BuysClothing, \"Buys Clothing\" },\n            { ColumnId_BuysBooks, \"Buys Books\" },\n            { ColumnId_BuysIngredients, \"Buys Ingredients\" },\n            { ColumnId_BuysLockpicks, \"Buys Lockpicks\" },\n            { ColumnId_BuysProbes, \"Buys Probes\" },\n            { ColumnId_BuysLights, \"Buys Lights\" },\n            { ColumnId_BuysApparati, \"Buys Apparati\" },\n            { ColumnId_BuysRepairItems, \"Buys Repair Items\" },\n            { ColumnId_BuysMiscItems, \"Buys Misc Items\" },\n            { ColumnId_BuysPotions, \"Buys Potions\" },\n            { ColumnId_BuysMagicItems, \"Buys Magic Items\" },\n            { ColumnId_SellsSpells, \"Sells Spells\" },\n            { ColumnId_Trainer, \"Trainer\" },\n            { ColumnId_Spellmaking, \"Spellmaking\" },\n            { ColumnId_EnchantingService, \"Enchanting Service\" },\n            { ColumnId_RepairService, \"Repair Service\" },\n            { ColumnId_ApparatusType, \"Apparatus Type\" },\n            { ColumnId_ArmorType, \"Armor Type\" },\n            { ColumnId_Health, \"Health\" },\n            { ColumnId_ArmorValue, \"Armor Value\" },\n            { ColumnId_BookType, \"Book Type\" },\n            { ColumnId_ClothingType, \"Clothing Type\" },\n            { ColumnId_WeightCapacity, \"Weight Capacity\" },\n            { ColumnId_OrganicContainer, \"Organic Container\" },\n            { ColumnId_Respawn, \"Respawn\" },\n            { ColumnId_CreatureType, \"Creature Type\" },\n            { ColumnId_SoulPoints, \"Soul Points\" },\n            { ColumnId_ParentCreature, \"Parent Creature\" },\n            { ColumnId_Biped, \"Biped\" },\n            { ColumnId_HasWeapon, \"Has Weapon\" },\n            { ColumnId_Swims, \"Swims\" },\n            { ColumnId_Flies, \"Flies\" },\n            { ColumnId_Walks, \"Walks\" },\n            { ColumnId_Essential, \"Essential\" },\n            { ColumnId_BloodType, \"Blood Type\" },\n\n            { ColumnId_OpenSound, \"Open Sound\" },\n            { ColumnId_CloseSound, \"Close Sound\" },\n            { ColumnId_Duration, \"Duration\" },\n            { ColumnId_Radius, \"Radius\" },\n            { ColumnId_Colour, \"Colour\" },\n            { ColumnId_Sound, \"Sound\" },\n            { ColumnId_Dynamic, \"Dynamic\" },\n            { ColumnId_Portable, \"Portable\" },\n            { ColumnId_NegativeLight, \"Negative Light\" },\n            { ColumnId_EmitterType, \"Emitter Type\" },\n\n            { ColumnId_Fire, \"Fire\" },\n            { ColumnId_OffByDefault, \"Off by default\" },\n            { ColumnId_IsKey, \"Is Key\" },\n            { ColumnId_Race, \"Race\" },\n            { ColumnId_Class, \"Class\" },\n            { Columnid_Hair, \"Hair\" },\n            { ColumnId_Head, \"Head\" },\n            { ColumnId_Female, \"Female\" },\n            { ColumnId_WeaponType, \"Weapon Type\" },\n            { ColumnId_WeaponSpeed, \"Weapon Speed\" },\n            { ColumnId_WeaponReach, \"Weapon Reach\" },\n            { ColumnId_MinChop, \"Min Chop\" },\n            { ColumnId_MaxChip, \"Max Chop\" },\n            { Columnid_MinSlash, \"Min Slash\" },\n            { ColumnId_MaxSlash, \"Max Slash\" },\n            { ColumnId_MinThrust, \"Min Thrust\" },\n            { ColumnId_MaxThrust, \"Max Thrust\" },\n            { ColumnId_Magical, \"Magical\" },\n            { ColumnId_Silver, \"Silver\" },\n            { ColumnId_Filter, \"Filter\" },\n            { ColumnId_PositionXPos, \"Pos X\" },\n            { ColumnId_PositionYPos, \"Pos Y\" },\n            { ColumnId_PositionZPos, \"Pos Z\" },\n            { ColumnId_PositionXRot, \"Rot X\" },\n            { ColumnId_PositionYRot, \"Rot Y\" },\n            { ColumnId_PositionZRot, \"Rot Z\" },\n            { ColumnId_DoorPositionXPos, \"Teleport Pos X\" },\n            { ColumnId_DoorPositionYPos, \"Teleport Pos Y\" },\n            { ColumnId_DoorPositionZPos, \"Teleport Pos Z\" },\n            { ColumnId_DoorPositionXRot, \"Teleport Rot X\" },\n            { ColumnId_DoorPositionYRot, \"Teleport Rot Y\" },\n            { ColumnId_DoorPositionZRot, \"Teleport Rot Z\" },\n            { ColumnId_DialogueType, \"Dialogue Type\" },\n            { ColumnId_QuestIndex, \"Quest Index\" },\n            { ColumnId_QuestStatusType, \"Quest Status\" },\n            { ColumnId_QuestDescription, \"Quest Description\" },\n            { ColumnId_Topic, \"Topic\" },\n            { ColumnId_Journal, \"Journal\" },\n            { ColumnId_Actor, \"Actor\" },\n            { ColumnId_PcFaction, \"PC Faction\" },\n            { ColumnId_Response, \"Response\" },\n            { ColumnId_Disposition, \"Disposition\" },\n            { ColumnId_Rank, \"Rank\" },\n            { ColumnId_Gender, \"Gender\" },\n            { ColumnId_PcRank, \"PC Rank\" },\n            { ColumnId_ReferenceableId, \"Object ID\" },\n\n            { ColumnId_ContainerContent, \"Content\" },\n            { ColumnId_ItemCount, \"Count\" },\n            { ColumnId_InventoryItemId, \"Item ID\"},\n\n            { ColumnId_CombatState, \"Combat\" },\n            { ColumnId_MagicState, \"Magic\" },\n            { ColumnId_StealthState, \"Stealth\" },\n            { ColumnId_EnchantmentType, \"Enchantment Type\" },\n            { ColumnId_Vampire, \"Vampire\" },\n            { ColumnId_BodyPartType, \"Bodypart Type\" },\n            { ColumnId_MeshType, \"Mesh Type\" },\n\n            { ColumnId_ActorInventory, \"Inventory\" },\n            { ColumnId_SpellList, \"Spells\" },\n            { ColumnId_SpellId, \"Spell ID\"},\n\n            { ColumnId_NpcDestinations, \"Destinations\" },\n            { ColumnId_DestinationCell, \"Dest Cell\"},\n            { ColumnId_PosX, \"Dest X\"},\n            { ColumnId_PosY, \"Dest Y\"},\n            { ColumnId_PosZ, \"Dest Z\"},\n            { ColumnId_RotX, \"Rotation X\"},\n            { ColumnId_RotY, \"Rotation Y\"},\n            { ColumnId_RotZ, \"Rotation Z\"},\n\n            { ColumnId_OwnerGlobal, \"Owner Global\" },\n            { ColumnId_DefaultProfile, \"Default Profile\" },\n            { ColumnId_BypassNewGame, \"Bypass New Game\" },\n            { ColumnId_GlobalProfile, \"Global Profile\" },\n            { ColumnId_RefNumCounter, \"RefNum Counter\" },\n            { ColumnId_RefNum, \"RefNum\" },\n            { ColumnId_Creature, \"Creature\" },\n            { ColumnId_SoundGeneratorType, \"Sound Generator Type\" },\n            { ColumnId_AllowSpellmaking, \"Allow Spellmaking\" },\n            { ColumnId_AllowEnchanting, \"Allow Enchanting\" },\n            { ColumnId_BaseCost, \"Base Cost\" },\n            { ColumnId_School, \"School\" },\n            { ColumnId_Particle, \"Particle\" },\n            { ColumnId_CastingObject, \"Casting Object\" },\n            { ColumnId_HitObject, \"Hit Object\" },\n            { ColumnId_AreaObject, \"Area Object\" },\n            { ColumnId_BoltObject, \"Bolt Object\" },\n            { ColumnId_CastingSound, \"Casting Sound\" },\n            { ColumnId_HitSound, \"Hit Sound\" },\n            { ColumnId_AreaSound, \"Area Sound\" },\n            { ColumnId_BoltSound, \"Bolt Sound\" },\n\n            { ColumnId_PathgridPoints, \"Points\" },\n            { ColumnId_PathgridIndex, \"pIndex\" },\n            { ColumnId_PathgridPosX, \"X\" },\n            { ColumnId_PathgridPosY, \"Y\" },\n            { ColumnId_PathgridPosZ, \"Z\" },\n            { ColumnId_PathgridEdges, \"Edges\" },\n            { ColumnId_PathgridEdgeIndex, \"eIndex\" },\n            { ColumnId_PathgridEdge0, \"Point 0\" },\n            { ColumnId_PathgridEdge1, \"Point 1\" },\n\n            { ColumnId_RegionSounds, \"Sounds\" },\n            { ColumnId_SoundName, \"Sound Name\" },\n            { ColumnId_SoundChance, \"Chance\" },\n\n            { ColumnId_FactionReactions, \"Reactions\" },\n            { ColumnId_FactionRanks, \"Ranks\" },\n            { ColumnId_FactionReaction, \"Reaction\" },\n\n            { ColumnId_FactionAttrib1, \"Attrib 1\" },\n            { ColumnId_FactionAttrib2, \"Attrib 2\" },\n            { ColumnId_FactionPrimSkill, \"Prim Skill\" },\n            { ColumnId_FactionFavSkill, \"Fav Skill\" },\n            { ColumnId_FactionRep, \"Fact Rep\" },\n            { ColumnId_RankName, \"Rank Name\" },\n\n            { ColumnId_EffectList, \"Effects\" },\n            { ColumnId_EffectId, \"Effect\" },\n            { ColumnId_EffectRange, \"Range\" },\n            { ColumnId_EffectArea, \"Area\" },\n\n            { ColumnId_AiPackageList, \"Ai Packages\" },\n            { ColumnId_AiPackageType, \"Package\" },\n            { ColumnId_AiWanderDist, \"Wander Dist\" },\n            { ColumnId_AiDuration, \"Ai Duration\" },\n            { ColumnId_AiWanderToD, \"Wander ToD\" },\n            { ColumnId_AiWanderRepeat, \"Wander Repeat\" },\n            { ColumnId_AiActivateName, \"Activate\" },\n            { ColumnId_AiTargetId, \"Target ID\" },\n            { ColumnId_AiTargetCell, \"Target Cell\" },\n\n            { ColumnId_PartRefList, \"Part Reference\" },\n            { ColumnId_PartRefType, \"Type\" },\n            { ColumnId_PartRefMale, \"Male Part\" },\n            { ColumnId_PartRefFemale, \"Female Part\" },\n\n            { ColumnId_LevelledList,\"Levelled List\" },\n            { ColumnId_LevelledItemId,\"Levelled Item\" },\n            { ColumnId_LevelledItemLevel,\"Item Level\" },\n            { ColumnId_LevelledItemType, \"Calculate all levels <= player\" },\n            { ColumnId_LevelledItemTypeEach, \"Select a new item each instance\" },\n            { ColumnId_LevelledItemChanceNone, \"Chance None\" },\n\n            { ColumnId_PowerList, \"Powers\" },\n            { ColumnId_Skill, \"Skill\" },\n\n            { ColumnId_InfoList, \"Info List\" },\n            { ColumnId_InfoCondition, \"Info Conditions\" },\n            { ColumnId_InfoCondFunc, \"Function\" },\n            { ColumnId_InfoCondVar, \"Variable/Object\" },\n            { ColumnId_InfoCondComp, \"Relation\" },\n            { ColumnId_InfoCondValue, \"Values\" },\n            { ColumnId_OriginalCell, \"Original Cell\" },\n\n            { ColumnId_NpcAttributes, \"NPC Attributes\" },\n            { ColumnId_NpcSkills, \"NPC Skill\" },\n            { ColumnId_UChar, \"Value [0..255]\" },\n            { ColumnId_NpcMisc, \"NPC Misc\" },\n            { ColumnId_Level, \"Level\" },\n            { ColumnId_Mana, \"Mana\" },\n            { ColumnId_Fatigue, \"Fatigue\" },\n            { ColumnId_NpcDisposition, \"NPC Disposition\" },\n            { ColumnId_NpcReputation, \"Reputation\" },\n            { ColumnId_NpcRank, \"NPC Rank\" },\n            { ColumnId_Gold, \"Gold\" },\n            { ColumnId_NpcPersistence, \"Persistent\" },\n\n            { ColumnId_RaceAttributes, \"Race Attributes\" },\n            { ColumnId_Male, \"Male\" },\n            { ColumnId_RaceSkillBonus, \"Skill Bonus\" },\n            { ColumnId_RaceBonus, \"Bonus\" },\n\n            { ColumnId_Interior, \"Interior\" },\n            { ColumnId_Ambient, \"Ambient\" },\n            { ColumnId_Sunlight, \"Sunlight\" },\n            { ColumnId_Fog, \"Fog\" },\n            { ColumnId_FogDensity, \"Fog Density\" },\n            { ColumnId_WaterLevel, \"Water Level\" },\n            { ColumnId_MapColor, \"Map Color\" },\n\n            { ColumnId_FileFormat, \"File Format\" },\n            { ColumnId_FileDescription, \"File Description\" },\n            { ColumnId_Author, \"Author\" },\n\n            { ColumnId_CreatureAttributes, \"Creature Attributes\" },\n            { ColumnId_AttributeValue, \"Attrib Value\" },\n            { ColumnId_CreatureAttack, \"Creature Attack\" },\n            { ColumnId_MinAttack, \"Min Attack\" },\n            { ColumnId_MaxAttack, \"Max Attack\" },\n            { ColumnId_CreatureMisc, \"Creature Misc\" },\n\n            { ColumnId_Idle1, \"Idle 1\" },\n            { ColumnId_Idle2, \"Idle 2\" },\n            { ColumnId_Idle3, \"Idle 3\" },\n            { ColumnId_Idle4, \"Idle 4\" },\n            { ColumnId_Idle5, \"Idle 5\" },\n            { ColumnId_Idle6, \"Idle 6\" },\n            { ColumnId_Idle7, \"Idle 7\" },\n            { ColumnId_Idle8, \"Idle 8\" },\n\n            { ColumnId_RegionWeather, \"Weather\" },\n            { ColumnId_WeatherName, \"Type\" },\n            { ColumnId_WeatherChance, \"Percent Chance\" },\n\n            { ColumnId_Text, \"Text\" },\n            { ColumnId_TextureNickname, \"Texture Nickname\" },\n            { ColumnId_PluginIndex, \"Plugin Index\" },\n            { ColumnId_TextureIndex, \"Texture Index\" },\n            { ColumnId_LandMapLodIndex, \"Land map height LOD\" },\n            { ColumnId_LandNormalsIndex, \"Land normals\" },\n            { ColumnId_LandHeightsIndex, \"Land heights\" },\n            { ColumnId_LandColoursIndex, \"Land colors\" },\n            { ColumnId_LandTexturesIndex, \"Land textures\" },\n\n            { ColumnId_UseValue1, \"Use value 1\" },\n            { ColumnId_UseValue2, \"Use value 2\" },\n            { ColumnId_UseValue3, \"Use value 3\" },\n            { ColumnId_UseValue4, \"Use value 4\" },\n\n            { ColumnId_Attribute1, \"Attribute 1\" },\n            { ColumnId_Attribute2, \"Attribute 2\" },\n\n            { ColumnId_MajorSkill1, \"Major Skill 1\" },\n            { ColumnId_MajorSkill2, \"Major Skill 2\" },\n            { ColumnId_MajorSkill3, \"Major Skill 3\" },\n            { ColumnId_MajorSkill4, \"Major Skill 4\" },\n            { ColumnId_MajorSkill5, \"Major Skill 5\" },\n\n            { ColumnId_MinorSkill1, \"Minor Skill 1\" },\n            { ColumnId_MinorSkill2, \"Minor Skill 2\" },\n            { ColumnId_MinorSkill3, \"Minor Skill 3\" },\n            { ColumnId_MinorSkill4, \"Minor Skill 4\" },\n            { ColumnId_MinorSkill5, \"Minor Skill 5\" },\n\n            { ColumnId_Skill1, \"Skill 1\" },\n            { ColumnId_Skill2, \"Skill 2\" },\n            { ColumnId_Skill3, \"Skill 3\" },\n            { ColumnId_Skill4, \"Skill 4\" },\n            { ColumnId_Skill5, \"Skill 5\" },\n            { ColumnId_Skill6, \"Skill 6\" },\n            { ColumnId_Skill7, \"Skill 7\" },\n\n            { -1, 0 } // end marker\n        };\n    }\n}\n\nstd::string CSMWorld::Columns::getName (ColumnId column)\n{\n    for (int i=0; sNames[i].mName; ++i)\n        if (column==sNames[i].mId)\n            return sNames[i].mName;\n\n    return \"\";\n}\n\nint CSMWorld::Columns::getId (const std::string& name)\n{\n    std::string name2 = Misc::StringUtils::lowerCase (name);\n\n    for (int i=0; sNames[i].mName; ++i)\n        if (Misc::StringUtils::ciEqual(sNames[i].mName, name2))\n            return sNames[i].mId;\n\n    return -1;\n}\n\nnamespace\n{\n    static const char *sSpecialisations[] =\n    {\n        \"Combat\", \"Magic\", \"Stealth\", 0\n    };\n\n    // see ESM::Attribute::AttributeID in <component/esm/attr.hpp>\n    static const char *sAttributes[] =\n    {\n        \"Strength\", \"Intelligence\", \"Willpower\", \"Agility\", \"Speed\", \"Endurance\", \"Personality\",\n        \"Luck\", 0\n    };\n\n    static const char *sSpellTypes[] =\n    {\n        \"Spell\", \"Ability\", \"Blight\", \"Disease\", \"Curse\", \"Power\", 0\n    };\n\n    static const char *sApparatusTypes[] =\n    {\n        \"Mortar & Pestle\", \"Alembic\", \"Calcinator\", \"Retort\", 0\n    };\n\n    static const char *sArmorTypes[] =\n    {\n        \"Helmet\", \"Cuirass\", \"Left Pauldron\", \"Right Pauldron\", \"Greaves\", \"Boots\", \"Left Gauntlet\",\n        \"Right Gauntlet\", \"Shield\", \"Left Bracer\", \"Right Bracer\", 0\n    };\n\n    static const char *sClothingTypes[] =\n    {\n        \"Pants\", \"Shoes\", \"Shirt\", \"Belt\", \"Robe\", \"Right Glove\", \"Left Glove\", \"Skirt\", \"Ring\",\n        \"Amulet\", 0\n    };\n\n    static const char *sCreatureTypes[] =\n    {\n        \"Creature\", \"Daedra\", \"Undead\", \"Humanoid\", 0\n    };\n\n    static const char *sWeaponTypes[] =\n    {\n        \"Short Blade 1H\", \"Long Blade 1H\", \"Long Blade 2H\", \"Blunt 1H\", \"Blunt 2H Close\",\n        \"Blunt 2H Wide\", \"Spear 2H\", \"Axe 1H\", \"Axe 2H\", \"Bow\", \"Crossbow\", \"Thrown\", \"Arrow\",\n        \"Bolt\", 0\n    };\n\n    static const char *sModificationEnums[] =\n    {\n        \"Base\", \"Modified\", \"Added\", \"Deleted\", \"Deleted\", 0\n    };\n\n    static const char *sVarTypeEnums[] =\n    {\n        \"unknown\", \"none\", \"short\", \"integer\", \"long\", \"float\", \"string\", 0\n    };\n\n    static const char *sDialogueTypeEnums[] =\n    {\n        \"Topic\", \"Voice\", \"Greeting\", \"Persuasion\", 0\n    };\n\n    static const char *sQuestStatusTypes[] =\n    {\n        \"None\", \"Name\", \"Finished\", \"Restart\", 0\n    };\n\n    static const char *sGenderEnums[] =\n    {\n        \"Male\", \"Female\", 0\n    };\n\n    static const char *sEnchantmentTypes[] =\n    {\n        \"Cast Once\", \"When Strikes\", \"When Used\", \"Constant Effect\", 0\n    };\n\n    static const char *sBodyPartTypes[] =\n    {\n        \"Head\", \"Hair\", \"Neck\", \"Chest\", \"Groin\", \"Hand\", \"Wrist\", \"Forearm\", \"Upper Arm\",\n        \"Foot\", \"Ankle\", \"Knee\", \"Upper Leg\", \"Clavicle\", \"Tail\", 0\n    };\n\n    static const char *sMeshTypes[] =\n    {\n        \"Skin\", \"Clothing\", \"Armour\", 0\n    };\n\n    static const char *sSoundGeneratorType[] =\n    {\n        \"Left Foot\", \"Right Foot\", \"Swim Left\", \"Swim Right\", \"Moan\", \"Roar\", \"Scream\",\n        \"Land\", 0\n    };\n\n    static const char *sSchools[] =\n    {\n        \"Alteration\", \"Conjuration\", \"Destruction\", \"Illusion\", \"Mysticism\", \"Restoration\", 0\n    };\n\n    // impact from magic effects, see ESM::Skill::SkillEnum in <component/esm/loadskil.hpp>\n    static const char *sSkills[] =\n    {\n        \"Block\", \"Armorer\", \"MediumArmor\", \"HeavyArmor\", \"BluntWeapon\",\n        \"LongBlade\", \"Axe\", \"Spear\", \"Athletics\", \"Enchant\",\n        \"Destruction\", \"Alteration\", \"Illusion\", \"Conjuration\", \"Mysticism\",\n        \"Restoration\", \"Alchemy\", \"Unarmored\", \"Security\", \"Sneak\",\n        \"Acrobatics\", \"LightArmor\", \"ShortBlade\", \"Marksman\", \"Mercantile\",\n        \"Speechcraft\", \"HandToHand\", 0\n    };\n\n    // range of magic effects, see ESM::RangeType in <component/esm/defs.hpp>\n    static const char *sEffectRange[] =\n    {\n        \"Self\", \"Touch\", \"Target\", 0\n    };\n\n    // magic effect names, see ESM::MagicEffect::Effects in <component/esm/loadmgef.hpp>\n    static const char *sEffectId[] =\n    {\n        \"WaterBreathing\", \"SwiftSwim\", \"WaterWalking\", \"Shield\", \"FireShield\",\n        \"LightningShield\", \"FrostShield\", \"Burden\", \"Feather\", \"Jump\",\n        \"Levitate\", \"SlowFall\", \"Lock\", \"Open\", \"FireDamage\",\n        \"ShockDamage\", \"FrostDamage\", \"DrainAttribute\", \"DrainHealth\", \"DrainMagicka\",\n        \"DrainFatigue\", \"DrainSkill\", \"DamageAttribute\", \"DamageHealth\", \"DamageMagicka\",\n        \"DamageFatigue\", \"DamageSkill\", \"Poison\", \"WeaknessToFire\", \"WeaknessToFrost\",\n        \"WeaknessToShock\", \"WeaknessToMagicka\", \"WeaknessToCommonDisease\", \"WeaknessToBlightDisease\", \"WeaknessToCorprusDisease\",\n        \"WeaknessToPoison\", \"WeaknessToNormalWeapons\", \"DisintegrateWeapon\", \"DisintegrateArmor\", \"Invisibility\",\n        \"Chameleon\", \"Light\", \"Sanctuary\", \"NightEye\", \"Charm\",\n        \"Paralyze\", \"Silence\", \"Blind\", \"Sound\", \"CalmHumanoid\",\n        \"CalmCreature\", \"FrenzyHumanoid\", \"FrenzyCreature\", \"DemoralizeHumanoid\", \"DemoralizeCreature\",\n        \"RallyHumanoid\", \"RallyCreature\", \"Dispel\", \"Soultrap\", \"Telekinesis\",\n        \"Mark\", \"Recall\", \"DivineIntervention\", \"AlmsiviIntervention\", \"DetectAnimal\",\n        \"DetectEnchantment\", \"DetectKey\", \"SpellAbsorption\", \"Reflect\", \"CureCommonDisease\",\n        \"CureBlightDisease\", \"CureCorprusDisease\", \"CurePoison\", \"CureParalyzation\", \"RestoreAttribute\",\n        \"RestoreHealth\", \"RestoreMagicka\", \"RestoreFatigue\", \"RestoreSkill\", \"FortifyAttribute\",\n        \"FortifyHealth\", \"FortifyMagicka\", \"FortifyFatigue\", \"FortifySkill\", \"FortifyMaximumMagicka\",\n        \"AbsorbAttribute\", \"AbsorbHealth\", \"AbsorbMagicka\", \"AbsorbFatigue\", \"AbsorbSkill\",\n        \"ResistFire\", \"ResistFrost\", \"ResistShock\", \"ResistMagicka\", \"ResistCommonDisease\",\n        \"ResistBlightDisease\", \"ResistCorprusDisease\", \"ResistPoison\", \"ResistNormalWeapons\", \"ResistParalysis\",\n        \"RemoveCurse\", \"TurnUndead\", \"SummonScamp\", \"SummonClannfear\", \"SummonDaedroth\",\n        \"SummonDremora\", \"SummonAncestralGhost\", \"SummonSkeletalMinion\", \"SummonBonewalker\", \"SummonGreaterBonewalker\",\n        \"SummonBonelord\", \"SummonWingedTwilight\", \"SummonHunger\", \"SummonGoldenSaint\", \"SummonFlameAtronach\",\n        \"SummonFrostAtronach\", \"SummonStormAtronach\", \"FortifyAttack\", \"CommandCreature\", \"CommandHumanoid\",\n        \"BoundDagger\", \"BoundLongsword\", \"BoundMace\", \"BoundBattleAxe\", \"BoundSpear\",\n        \"BoundLongbow\", \"ExtraSpell\", \"BoundCuirass\", \"BoundHelm\", \"BoundBoots\",\n        \"BoundShield\", \"BoundGloves\", \"Corprus\", \"Vampirism\", \"SummonCenturionSphere\",\n        \"SunDamage\", \"StuntedMagicka\", \"SummonFabricant\", \"SummonWolf\", \"SummonBear\",\n        \"SummonBonewolf\", \"SummonCreature04\", \"SummonCreature05\", 0\n    };\n\n    // see ESM::PartReferenceType in <component/esm/loadarmo.hpp>\n    static const char *sPartRefType[] =\n    {\n        \"Head\", \"Hair\", \"Neck\", \"Cuirass\", \"Groin\",\n        \"Skirt\", \"Right Hand\", \"Left Hand\", \"Right Wrist\", \"Left Wrist\",\n        \"Shield\", \"Right Forearm\", \"Left Forearm\", \"Right Upperarm\", \"Left Upperarm\",\n        \"Right Foot\", \"Left Foot\", \"Right Ankle\", \"Left Ankle\", \"Right Knee\",\n        \"Left Knee\", \"Right Leg\", \"Left Leg\", \"Right Pauldron\", \"Left Pauldron\",\n        \"Weapon\", \"Tail\", 0\n    };\n\n    // see the enums in <component/esm/aipackage.hpp>\n    static const char *sAiPackageType[] =\n    {\n        \"AI Wander\", \"AI Travel\", \"AI Follow\", \"AI Escort\", \"AI Activate\", 0\n    };\n\n    static const char *sBookType[] =\n    {\n        \"Book\", \"Scroll\", 0\n    };\n\n    static const char *sEmitterType[] =\n    {\n        \"<None>\", \"Flickering\", \"Flickering (Slow)\", \"Pulsing\", \"Pulsing (Slow)\", 0\n    };\n\n    const char **getEnumNames (CSMWorld::Columns::ColumnId column)\n    {\n        switch (column)\n        {\n            case CSMWorld::Columns::ColumnId_Specialisation: return sSpecialisations;\n            case CSMWorld::Columns::ColumnId_Attribute: return sAttributes;\n            case CSMWorld::Columns::ColumnId_SpellType: return sSpellTypes;\n            case CSMWorld::Columns::ColumnId_ApparatusType: return sApparatusTypes;\n            case CSMWorld::Columns::ColumnId_ArmorType: return sArmorTypes;\n            case CSMWorld::Columns::ColumnId_ClothingType: return sClothingTypes;\n            case CSMWorld::Columns::ColumnId_CreatureType: return sCreatureTypes;\n            case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes;\n            case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;\n            case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;\n            case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums;\n            case CSMWorld::Columns::ColumnId_QuestStatusType: return sQuestStatusTypes;\n            case CSMWorld::Columns::ColumnId_Gender: return sGenderEnums;\n            case CSMWorld::Columns::ColumnId_EnchantmentType: return sEnchantmentTypes;\n            case CSMWorld::Columns::ColumnId_BodyPartType: return sBodyPartTypes;\n            case CSMWorld::Columns::ColumnId_MeshType: return sMeshTypes;\n            case CSMWorld::Columns::ColumnId_SoundGeneratorType: return sSoundGeneratorType;\n            case CSMWorld::Columns::ColumnId_School: return sSchools;\n            case CSMWorld::Columns::ColumnId_Skill: return sSkills;\n            case CSMWorld::Columns::ColumnId_EffectRange: return sEffectRange;\n            case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;\n            case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;\n            case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;\n            case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;\n            case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;\n            case CSMWorld::Columns::ColumnId_BookType: return sBookType;\n            case CSMWorld::Columns::ColumnId_EmitterType: return sEmitterType;\n\n            default: return 0;\n        }\n    }\n}\n\nbool CSMWorld::Columns::hasEnums (ColumnId column)\n{\n    return getEnumNames (column)!=0 || column==ColumnId_RecordType;\n}\n\nstd::vector<std::pair<int,std::string>>CSMWorld::Columns::getEnums (ColumnId column)\n{\n    std::vector<std::pair<int,std::string>> enums;\n\n    if (const char **table = getEnumNames (column))\n        for (int i=0; table[i]; ++i)\n            enums.emplace_back(i, table[i]);\n    else if (column==ColumnId_BloodType)\n    {\n        for (int i=0; i<8; i++)\n        {\n            const std::string& bloodName = Fallback::Map::getString(\"Blood_Texture_Name_\" + std::to_string(i));\n            if (!bloodName.empty())\n                enums.emplace_back(i, bloodName);\n        }\n    }\n    else if (column==ColumnId_RecordType)\n    {\n        enums.emplace_back(UniversalId::Type_None, \"\"); // none\n\n        for (int i=UniversalId::Type_None+1; i<UniversalId::NumberOfTypes; ++i)\n            enums.emplace_back (i, UniversalId (static_cast<UniversalId::Type> (i)).getTypeName());\n    }\n\n    return enums;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/columns.hpp",
    "content": "#ifndef CSM_WOLRD_COLUMNS_H\n#define CSM_WOLRD_COLUMNS_H\n\n#include <string>\n#include <vector>\n\n#include \"columnbase.hpp\"\n\nnamespace CSMWorld\n{\n    namespace Columns\n    {\n        enum ColumnId\n        {\n            ColumnId_Value = 0,\n            ColumnId_Id = 1,\n            ColumnId_Modification = 2,\n            ColumnId_RecordType = 3,\n            ColumnId_ValueType = 4,\n            ColumnId_Description = 5,\n            ColumnId_Specialisation = 6,\n            ColumnId_Attribute = 7,\n            ColumnId_Name = 8,\n            ColumnId_Playable = 9,\n            ColumnId_Hidden = 10,\n            ColumnId_MaleWeight = 11,\n            ColumnId_FemaleWeight = 12,\n            ColumnId_MaleHeight = 13,\n            ColumnId_FemaleHeight = 14,\n            ColumnId_Volume = 15,\n            ColumnId_MinRange = 16,\n            ColumnId_MaxRange = 17,\n            ColumnId_SoundFile = 18,\n            ColumnId_MapColour = 19,\n            ColumnId_SleepEncounter = 20,\n            ColumnId_Texture = 21,\n            ColumnId_SpellType = 22,\n            ColumnId_Cost = 23,\n            ColumnId_ScriptText = 24,\n            ColumnId_Region = 25,\n            ColumnId_Cell = 26,\n            ColumnId_Scale = 27,\n            ColumnId_Owner = 28,\n            ColumnId_Soul = 29,\n            ColumnId_Faction = 30,\n            ColumnId_FactionIndex = 31,\n            ColumnId_Charges = 32,\n            ColumnId_Enchantment = 33,\n            ColumnId_CoinValue = 34,\n            ColumnId_Teleport = 35,\n            ColumnId_TeleportCell = 36,\n            ColumnId_LockLevel = 37,\n            ColumnId_Key = 38,\n            ColumnId_Trap = 39,\n            ColumnId_BeastRace = 40,\n            ColumnId_AutoCalc = 41,\n            ColumnId_StarterSpell = 42,\n            ColumnId_AlwaysSucceeds = 43,\n            ColumnId_SleepForbidden = 44,\n            ColumnId_InteriorWater = 45,\n            ColumnId_InteriorSky = 46,\n            ColumnId_Model = 47,\n            ColumnId_Script = 48,\n            ColumnId_Icon = 49,\n            ColumnId_Weight = 50,\n            ColumnId_EnchantmentPoints = 51,\n            ColumnId_Quality = 52,\n            // unused\n            ColumnId_AiHello = 54,\n            ColumnId_AiFlee = 55,\n            ColumnId_AiFight = 56,\n            ColumnId_AiAlarm = 57,\n            ColumnId_BuysWeapons = 58,\n            ColumnId_BuysArmor = 59,\n            ColumnId_BuysClothing = 60,\n            ColumnId_BuysBooks = 61,\n            ColumnId_BuysIngredients = 62,\n            ColumnId_BuysLockpicks = 63,\n            ColumnId_BuysProbes = 64,\n            ColumnId_BuysLights = 65,\n            ColumnId_BuysApparati = 66,\n            ColumnId_BuysRepairItems = 67,\n            ColumnId_BuysMiscItems = 68,\n            ColumnId_BuysPotions = 69,\n            ColumnId_BuysMagicItems = 70,\n            ColumnId_SellsSpells = 71,\n            ColumnId_Trainer = 72,\n            ColumnId_Spellmaking = 73,\n            ColumnId_EnchantingService = 74,\n            ColumnId_RepairService = 75,\n            ColumnId_ApparatusType = 76,\n            ColumnId_ArmorType = 77,\n            ColumnId_Health = 78,\n            ColumnId_ArmorValue = 79,\n            ColumnId_BookType = 80,\n            ColumnId_ClothingType = 81,\n            ColumnId_WeightCapacity = 82,\n            ColumnId_OrganicContainer = 83,\n            ColumnId_Respawn = 84,\n            ColumnId_CreatureType = 85,\n            ColumnId_SoulPoints = 86,\n            ColumnId_ParentCreature = 87,\n            ColumnId_Biped = 88,\n            ColumnId_HasWeapon = 89,\n            // unused\n            ColumnId_Swims = 91,\n            ColumnId_Flies = 92,\n            ColumnId_Walks = 93,\n            ColumnId_Essential = 94,\n            ColumnId_BloodType = 95,\n            // unused\n            ColumnId_OpenSound = 97,\n            ColumnId_CloseSound = 98,\n            ColumnId_Duration = 99,\n            ColumnId_Radius = 100,\n            ColumnId_Colour = 101,\n            ColumnId_Sound = 102,\n            ColumnId_Dynamic = 103,\n            ColumnId_Portable = 104,\n            ColumnId_NegativeLight = 105,\n            ColumnId_EmitterType = 106,\n            // unused (3x)\n            ColumnId_Fire = 110,\n            ColumnId_OffByDefault = 111,\n            ColumnId_IsKey = 112,\n            ColumnId_Race = 113,\n            ColumnId_Class = 114,\n            Columnid_Hair = 115,\n            ColumnId_Head = 116,\n            ColumnId_Female = 117,\n            ColumnId_WeaponType = 118,\n            ColumnId_WeaponSpeed = 119,\n            ColumnId_WeaponReach = 120,\n            ColumnId_MinChop = 121,\n            ColumnId_MaxChip = 122,\n            Columnid_MinSlash = 123,\n            ColumnId_MaxSlash = 124,\n            ColumnId_MinThrust = 125,\n            ColumnId_MaxThrust = 126,\n            ColumnId_Magical = 127,\n            ColumnId_Silver = 128,\n            ColumnId_Filter = 129,\n            ColumnId_PositionXPos = 130,\n            ColumnId_PositionYPos = 131,\n            ColumnId_PositionZPos = 132,\n            ColumnId_PositionXRot = 133,\n            ColumnId_PositionYRot = 134,\n            ColumnId_PositionZRot = 135,\n            ColumnId_DoorPositionXPos = 136,\n            ColumnId_DoorPositionYPos = 137,\n            ColumnId_DoorPositionZPos = 138,\n            ColumnId_DoorPositionXRot = 139,\n            ColumnId_DoorPositionYRot = 140,\n            ColumnId_DoorPositionZRot = 141,\n            ColumnId_DialogueType = 142,\n            ColumnId_QuestIndex = 143,\n            ColumnId_QuestStatusType = 144,\n            ColumnId_QuestDescription = 145,\n            ColumnId_Topic = 146,\n            ColumnId_Journal = 147,\n            ColumnId_Actor = 148,\n            ColumnId_PcFaction = 149,\n            ColumnId_Response = 150,\n            ColumnId_Disposition = 151,\n            ColumnId_Rank = 152,\n            ColumnId_Gender = 153,\n            ColumnId_PcRank = 154,\n            ColumnId_ReferenceableId = 155,\n            ColumnId_ContainerContent = 156,\n            ColumnId_ItemCount = 157,\n            ColumnId_InventoryItemId = 158,\n            ColumnId_CombatState = 159,\n            ColumnId_MagicState = 160,\n            ColumnId_StealthState = 161,\n            ColumnId_EnchantmentType = 162,\n            ColumnId_Vampire = 163,\n            ColumnId_BodyPartType = 164,\n            ColumnId_MeshType = 165,\n            ColumnId_ActorInventory = 166,\n            ColumnId_SpellList = 167,\n            ColumnId_SpellId = 168,\n            ColumnId_NpcDestinations = 169,\n            ColumnId_DestinationCell = 170,\n            ColumnId_PosX = 171, // these are float\n            ColumnId_PosY = 172, // these are float\n            ColumnId_PosZ = 173, // these are float\n            ColumnId_RotX = 174,\n            ColumnId_RotY = 175,\n            ColumnId_RotZ = 176,\n            // unused\n            ColumnId_OwnerGlobal = 178,\n            ColumnId_DefaultProfile = 179,\n            ColumnId_BypassNewGame = 180,\n            ColumnId_GlobalProfile = 181,\n            ColumnId_RefNumCounter = 182,\n            ColumnId_RefNum = 183,\n            ColumnId_Creature = 184,\n            ColumnId_SoundGeneratorType = 185,\n            ColumnId_AllowSpellmaking = 186,\n            ColumnId_AllowEnchanting = 187,\n            ColumnId_BaseCost = 188,\n            ColumnId_School = 189,\n            ColumnId_Particle = 190,\n            ColumnId_CastingObject = 191,\n            ColumnId_HitObject = 192,\n            ColumnId_AreaObject = 193,\n            ColumnId_BoltObject = 194,\n            ColumnId_CastingSound = 195,\n            ColumnId_HitSound = 196,\n            ColumnId_AreaSound = 197,\n            ColumnId_BoltSound = 198,\n\n            ColumnId_PathgridPoints = 199,\n            ColumnId_PathgridIndex = 200,\n            ColumnId_PathgridPosX = 201, // these are int\n            ColumnId_PathgridPosY = 202, // these are int\n            ColumnId_PathgridPosZ = 203, // these are int\n            ColumnId_PathgridEdges = 204,\n            ColumnId_PathgridEdgeIndex = 205,\n            ColumnId_PathgridEdge0 = 206,\n            ColumnId_PathgridEdge1 = 207,\n\n            ColumnId_RegionSounds = 208,\n            ColumnId_SoundName = 209,\n            ColumnId_SoundChance = 210,\n\n            ColumnId_FactionReactions = 211,\n            ColumnId_FactionReaction = 213,\n\n            ColumnId_EffectList = 214,\n            ColumnId_EffectId = 215,\n            ColumnId_EffectRange = 217,\n            ColumnId_EffectArea = 218,\n\n            ColumnId_AiPackageList = 219,\n            ColumnId_AiPackageType = 220,\n            ColumnId_AiWanderDist = 221,\n            ColumnId_AiDuration = 222,\n            ColumnId_AiWanderToD = 223,\n            // unused\n            ColumnId_AiWanderRepeat = 225,\n            ColumnId_AiActivateName = 226,\n            // use ColumnId_PosX, etc for AI destinations\n            ColumnId_AiTargetId = 227,\n            ColumnId_AiTargetCell = 228,\n\n            ColumnId_PartRefList = 229,\n            ColumnId_PartRefType = 230,\n            ColumnId_PartRefMale = 231,\n            ColumnId_PartRefFemale = 232,\n\n            ColumnId_LevelledList = 233,\n            ColumnId_LevelledItemId = 234,\n            ColumnId_LevelledItemLevel = 235,\n            ColumnId_LevelledItemType = 236,\n            ColumnId_LevelledItemTypeEach = 237,\n            ColumnId_LevelledItemChanceNone = 238,\n\n            ColumnId_PowerList = 239,\n            ColumnId_Skill = 240,\n\n            ColumnId_InfoList = 241,\n            ColumnId_InfoCondition = 242,\n            ColumnId_InfoCondFunc = 243,\n            ColumnId_InfoCondVar = 244,\n            ColumnId_InfoCondComp = 245,\n            ColumnId_InfoCondValue = 246,\n\n            ColumnId_OriginalCell = 247,\n\n            ColumnId_NpcAttributes = 248,\n            ColumnId_NpcSkills = 249,\n            ColumnId_UChar = 250,\n            ColumnId_NpcMisc = 251,\n            ColumnId_Level = 252,\n            // unused\n            ColumnId_Mana = 255,\n            ColumnId_Fatigue = 256,\n            ColumnId_NpcDisposition = 257,\n            ColumnId_NpcReputation = 258,\n            ColumnId_NpcRank = 259,\n            ColumnId_Gold = 260,\n            ColumnId_NpcPersistence = 261,\n\n            ColumnId_RaceAttributes = 262,\n            ColumnId_Male = 263,\n            // unused\n            ColumnId_RaceSkillBonus = 265,\n            // unused\n            ColumnId_RaceBonus = 267,\n\n            ColumnId_Interior = 268,\n            ColumnId_Ambient = 269,\n            ColumnId_Sunlight = 270,\n            ColumnId_Fog = 271,\n            ColumnId_FogDensity = 272,\n            ColumnId_WaterLevel = 273,\n            ColumnId_MapColor = 274,\n\n            ColumnId_FileFormat = 275,\n            ColumnId_FileDescription = 276,\n            ColumnId_Author = 277,\n\n            ColumnId_MinMagnitude = 278,\n            ColumnId_MaxMagnitude = 279,\n\n            ColumnId_CreatureAttributes = 280,\n            ColumnId_AttributeValue = 281,\n            ColumnId_CreatureAttack = 282,\n            ColumnId_MinAttack = 283,\n            ColumnId_MaxAttack = 284,\n            ColumnId_CreatureMisc = 285,\n\n            ColumnId_Idle1 = 286,\n            ColumnId_Idle2 = 287,\n            ColumnId_Idle3 = 288,\n            ColumnId_Idle4 = 289,\n            ColumnId_Idle5 = 290,\n            ColumnId_Idle6 = 291,\n            ColumnId_Idle7 = 292,\n            ColumnId_Idle8 = 293,\n\n            ColumnId_RegionWeather = 294,\n            ColumnId_WeatherName = 295,\n            ColumnId_WeatherChance = 296,\n\n            ColumnId_Text = 297,\n\n            ColumnId_TextureNickname = 298,\n            ColumnId_PluginIndex = 299,\n            ColumnId_TextureIndex = 300,\n            ColumnId_LandMapLodIndex = 301,\n            ColumnId_LandNormalsIndex = 302,\n            ColumnId_LandHeightsIndex = 303,\n            ColumnId_LandColoursIndex = 304,\n            ColumnId_LandTexturesIndex = 305,\n\n            ColumnId_RankName = 306,\n            ColumnId_FactionRanks = 307,\n            ColumnId_FactionPrimSkill = 308,\n            ColumnId_FactionFavSkill = 309,\n            ColumnId_FactionRep = 310,\n            ColumnId_FactionAttrib1 = 311,\n            ColumnId_FactionAttrib2 = 312,\n\n            // Allocated to a separate value range, so we don't get a collision should we ever need\n            // to extend the number of use values.\n            ColumnId_UseValue1 = 0x10000,\n            ColumnId_UseValue2 = 0x10001,\n            ColumnId_UseValue3 = 0x10002,\n            ColumnId_UseValue4 = 0x10003,\n\n            // Allocated to a separate value range, so we don't get a collision should we ever need\n            // to extend the number of attributes. Note that this is not the number of different\n            // attributes, but the number of attributes that can be references from a record.\n            ColumnId_Attribute1 = 0x20000,\n            ColumnId_Attribute2 = 0x20001,\n\n            // Allocated to a separate value range, so we don't get a collision should we ever need\n            // to extend the number of skills. Note that this is not the number of different\n            // skills, but the number of skills that can be references from a record.\n            ColumnId_MajorSkill1 = 0x30000,\n            ColumnId_MajorSkill2 = 0x30001,\n            ColumnId_MajorSkill3 = 0x30002,\n            ColumnId_MajorSkill4 = 0x30003,\n            ColumnId_MajorSkill5 = 0x30004,\n\n            ColumnId_MinorSkill1 = 0x40000,\n            ColumnId_MinorSkill2 = 0x40001,\n            ColumnId_MinorSkill3 = 0x40002,\n            ColumnId_MinorSkill4 = 0x40003,\n            ColumnId_MinorSkill5 = 0x40004,\n\n            ColumnId_Skill1 = 0x50000,\n            ColumnId_Skill2 = 0x50001,\n            ColumnId_Skill3 = 0x50002,\n            ColumnId_Skill4 = 0x50003,\n            ColumnId_Skill5 = 0x50004,\n            ColumnId_Skill6 = 0x50005,\n            ColumnId_Skill7 = 0x50006\n        };\n\n        std::string getName (ColumnId column);\n\n        int getId (const std::string& name);\n        ///< Will return -1 for an invalid name.\n\n        bool hasEnums (ColumnId column);\n\n        std::vector<std::pair<int,std::string>> getEnums (ColumnId column);\n        ///< Returns an empty vector, if \\a column isn't an enum type column.\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/commanddispatcher.cpp",
    "content": "#include \"commanddispatcher.hpp\"\n\n#include <algorithm>\n#include <memory>\n\n#include <components/misc/stringops.hpp>\n#include <components/misc/constants.hpp>\n\n#include \"../doc/document.hpp\"\n\n#include \"idtable.hpp\"\n#include \"record.hpp\"\n#include \"commands.hpp\"\n#include \"idtableproxymodel.hpp\"\n#include \"commandmacro.hpp\"\n\nstd::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const\n{\n    std::vector<std::string> result;\n\n    IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));\n\n    int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification);\n\n    for (std::vector<std::string>::const_iterator iter (mSelection.begin());\n        iter!=mSelection.end(); ++iter)\n    {\n        int row = model.getModelIndex (*iter, 0).row();\n\n        // check record state\n        RecordBase::State state = static_cast<RecordBase::State> (\n            model.data (model.index (row, stateColumnIndex)).toInt());\n\n        if (state==RecordBase::State_Deleted)\n            continue;\n\n        // check other columns (only relevant for a subset of the tables)\n        int dialogueTypeIndex = model.searchColumnIndex (Columns::ColumnId_DialogueType);\n\n        if (dialogueTypeIndex!=-1)\n        {\n            int type = model.data (model.index (row, dialogueTypeIndex)).toInt();\n\n            if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal)\n                continue;\n        }\n\n        result.push_back (*iter);\n    }\n\n    return result;\n}\n\nstd::vector<std::string> CSMWorld::CommandDispatcher::getRevertableRecords() const\n{\n    std::vector<std::string> result;\n\n    IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));\n\n    /// \\todo Reverting temporarily disabled on tables that support reordering, because\n    /// revert logic currently can not handle reordering.\n    if (model.getFeatures() & IdTable::Feature_ReorderWithinTopic)\n        return result;\n\n    int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification);\n\n    for (std::vector<std::string>::const_iterator iter (mSelection.begin());\n        iter!=mSelection.end(); ++iter)\n    {\n        int row = model.getModelIndex (*iter, 0).row();\n\n        // check record state\n        RecordBase::State state = static_cast<RecordBase::State> (\n            model.data (model.index (row, stateColumnIndex)).toInt());\n\n        if (state==RecordBase::State_BaseOnly)\n            continue;\n\n        result.push_back (*iter);\n    }\n\n    return result;\n}\n\nCSMWorld::CommandDispatcher::CommandDispatcher (CSMDoc::Document& document,\n    const CSMWorld::UniversalId& id, QObject *parent)\n: QObject (parent), mLocked (false), mDocument (document), mId (id)\n{}\n\nvoid CSMWorld::CommandDispatcher::setEditLock (bool locked)\n{\n    mLocked = locked;\n}\n\nvoid CSMWorld::CommandDispatcher::setSelection (const std::vector<std::string>& selection)\n{\n    mSelection = selection;\n    std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::lowerCaseInPlace);\n    std::sort (mSelection.begin(), mSelection.end());\n}\n\nvoid CSMWorld::CommandDispatcher::setExtendedTypes (const std::vector<UniversalId>& types)\n{\n    mExtendedTypes = types;\n}\n\nbool CSMWorld::CommandDispatcher::canDelete() const\n{\n    if (mLocked)\n        return false;\n\n    return getDeletableRecords().size()!=0;\n}\n\nbool CSMWorld::CommandDispatcher::canRevert() const\n{\n    if (mLocked)\n        return false;\n\n    return getRevertableRecords().size()!=0;\n}\n\nstd::vector<CSMWorld::UniversalId> CSMWorld::CommandDispatcher::getExtendedTypes() const\n{\n    std::vector<CSMWorld::UniversalId> tables;\n\n    if (mId==UniversalId::Type_Cells)\n    {\n        tables.push_back (mId);\n        tables.emplace_back(UniversalId::Type_References);\n        /// \\todo add other cell-specific types\n    }\n\n    return tables;\n}\n\nvoid CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_)\n{\n    if (mLocked)\n        return;\n\n    std::unique_ptr<CSMWorld::UpdateCellCommand> modifyCell;\n\n    std::unique_ptr<CSMWorld::ModifyCommand> modifyDataRefNum;\n\n    int columnId = model->data (index, ColumnBase::Role_ColumnId).toInt();\n\n    if (columnId==Columns::ColumnId_PositionXPos || columnId==Columns::ColumnId_PositionYPos)\n    {\n        const float oldPosition = model->data (index).toFloat();\n\n        // Modulate by cell size, update cell id if reference has been moved to a new cell\n        if (std::abs(std::fmod(oldPosition, Constants::CellSizeInUnits))\n            - std::abs(std::fmod(new_.toFloat(), Constants::CellSizeInUnits)) >= 0.5f)\n        {\n            IdTableProxyModel *proxy = dynamic_cast<IdTableProxyModel *> (model);\n\n            int row = proxy ? proxy->mapToSource (index).row() : index.row();\n\n            // This is not guaranteed to be the same as \\a model, since a proxy could be used.\n            IdTable& model2 = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));\n\n            int cellColumn = model2.searchColumnIndex (Columns::ColumnId_Cell);\n\n            if (cellColumn!=-1)\n            {\n                QModelIndex cellIndex = model2.index (row, cellColumn);\n\n                std::string cellId = model2.data (cellIndex).toString().toUtf8().data();\n\n                if (cellId.find ('#')!=std::string::npos)\n                {\n                    // Need to recalculate the cell and (if necessary) clear the instance's refNum\n                    modifyCell.reset (new UpdateCellCommand (model2, row));\n\n                    // Not sure which model this should be applied to\n                    int refNumColumn = model2.searchColumnIndex (Columns::ColumnId_RefNum);\n\n                    if (refNumColumn!=-1)\n                        modifyDataRefNum.reset (new ModifyCommand(*model, model->index(row, refNumColumn), 0));\n                }\n            }\n        }\n    }\n\n    std::unique_ptr<CSMWorld::ModifyCommand> modifyData (\n        new CSMWorld::ModifyCommand (*model, index, new_));\n\n    if (modifyCell.get())\n    {\n        CommandMacro macro (mDocument.getUndoStack());\n        macro.push (modifyData.release());\n        macro.push (modifyCell.release());\n        if (modifyDataRefNum.get())\n            macro.push (modifyDataRefNum.release());\n    }\n    else\n        mDocument.getUndoStack().push (modifyData.release());\n}\n\nvoid CSMWorld::CommandDispatcher::executeDelete()\n{\n    if (mLocked)\n        return;\n\n    std::vector<std::string> rows = getDeletableRecords();\n\n    if (rows.empty())\n        return;\n\n    IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));\n\n    int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);\n\n    CommandMacro macro (mDocument.getUndoStack(), rows.size()>1 ? \"Delete multiple records\" : \"\");\n    for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)\n    {\n        std::string id = model.data (model.getModelIndex (*iter, columnIndex)).\n            toString().toUtf8().constData();\n\n        if (mId.getType() == UniversalId::Type_Referenceables)\n        {\n            macro.push (new CSMWorld::DeleteCommand (model, id,\n                static_cast<CSMWorld::UniversalId::Type>(model.data (model.index (\n                    model.getModelIndex (id, columnIndex).row(),\n                    model.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType))).toInt())));\n        }\n        else\n            mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id));\n    }\n}\n\nvoid CSMWorld::CommandDispatcher::executeRevert()\n{\n    if (mLocked)\n        return;\n\n    std::vector<std::string> rows = getRevertableRecords();\n\n    if (rows.empty())\n        return;\n\n    IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));\n\n    int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);\n\n    CommandMacro macro (mDocument.getUndoStack(), rows.size()>1 ? \"Revert multiple records\" : \"\");\n    for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)\n    {\n        std::string id = model.data (model.getModelIndex (*iter, columnIndex)).\n            toString().toUtf8().constData();\n\n        macro.push (new CSMWorld::RevertCommand (model, id));\n    }\n}\n\nvoid CSMWorld::CommandDispatcher::executeExtendedDelete()\n{\n    CommandMacro macro (mDocument.getUndoStack(), mExtendedTypes.size()>1 ? tr (\"Extended delete of multiple records\") : \"\");\n\n    for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());\n        iter!=mExtendedTypes.end(); ++iter)\n    {\n        if (*iter==mId)\n            executeDelete();\n        else if (*iter==UniversalId::Type_References)\n        {\n            IdTable& model = dynamic_cast<IdTable&> (\n                *mDocument.getData().getTableModel (*iter));\n\n            const RefCollection& collection = mDocument.getData().getReferences();\n\n            int size = collection.getSize();\n\n            for (int i=size-1; i>=0; --i)\n            {\n                const Record<CellRef>& record = collection.getRecord (i);\n\n                if (record.mState==RecordBase::State_Deleted)\n                    continue;\n\n                if (!std::binary_search (mSelection.begin(), mSelection.end(),\n                    Misc::StringUtils::lowerCase (record.get().mCell)))\n                    continue;\n\n                macro.push (new CSMWorld::DeleteCommand (model, record.get().mId));\n            }\n        }\n    }\n}\n\nvoid CSMWorld::CommandDispatcher::executeExtendedRevert()\n{\n    CommandMacro macro (mDocument.getUndoStack(), mExtendedTypes.size()>1 ? tr (\"Extended revert of multiple records\") : \"\");\n\n    for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());\n        iter!=mExtendedTypes.end(); ++iter)\n    {\n        if (*iter==mId)\n            executeRevert();\n        else if (*iter==UniversalId::Type_References)\n        {\n            IdTable& model = dynamic_cast<IdTable&> (\n                *mDocument.getData().getTableModel (*iter));\n\n            const RefCollection& collection = mDocument.getData().getReferences();\n\n            int size = collection.getSize();\n\n            for (int i=size-1; i>=0; --i)\n            {\n                const Record<CellRef>& record = collection.getRecord (i);\n\n                if (!std::binary_search (mSelection.begin(), mSelection.end(),\n                    Misc::StringUtils::lowerCase (record.get().mCell)))\n                    continue;\n\n                macro.push (new CSMWorld::RevertCommand (model, record.get().mId));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/commanddispatcher.hpp",
    "content": "#ifndef CSM_WOLRD_COMMANDDISPATCHER_H\n#define CSM_WOLRD_COMMANDDISPATCHER_H\n\n#include <vector>\n\n#include <QObject>\n\n#include \"universalid.hpp\"\n\nclass QModelIndex;\nclass QAbstractItemModel;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class CommandDispatcher : public QObject\n    {\n            Q_OBJECT\n\n            bool mLocked;\n            CSMDoc::Document& mDocument;\n            UniversalId mId;\n            std::vector<std::string> mSelection;\n            std::vector<UniversalId> mExtendedTypes;\n\n            std::vector<std::string> getDeletableRecords() const;\n\n            std::vector<std::string> getRevertableRecords() const;\n\n        public:\n\n            CommandDispatcher (CSMDoc::Document& document, const CSMWorld::UniversalId& id,\n                QObject *parent = nullptr);\n            ///< \\param id ID of the table the commands should operate on primarily.\n\n            void setEditLock (bool locked);\n\n            void setSelection (const std::vector<std::string>& selection);\n\n            void setExtendedTypes (const std::vector<UniversalId>& types);\n            ///< Set record lists selected by the user for extended operations.\n\n            bool canDelete() const;\n\n            bool canRevert() const;\n\n            /// Return IDs of the record collection that can also be affected when\n            /// operating on the record collection this dispatcher is used for.\n            ///\n            /// \\note The returned collection contains the ID of the record collection this\n            /// dispatcher is used for. However if that record collection does not support\n            /// the extended mode, the returned vector will be empty instead.\n            std::vector<UniversalId> getExtendedTypes() const;\n\n            /// Add a modify command to the undo stack.\n            ///\n            /// \\attention model must either be a model for the table operated on by this\n            /// dispatcher or a proxy of it.\n            void executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_);\n\n        public slots:\n\n            void executeDelete();\n\n            void executeRevert();\n\n            void executeExtendedDelete();\n\n            void executeExtendedRevert();\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/commandmacro.cpp",
    "content": "\n#include \"commandmacro.hpp\"\n\n#include <QUndoStack>\n#include <QUndoCommand>\n\nCSMWorld::CommandMacro::CommandMacro (QUndoStack& undoStack, const QString& description)\n: mUndoStack (undoStack), mDescription (description), mStarted (false)\n{}\n\nCSMWorld::CommandMacro::~CommandMacro()\n{\n    if (mStarted)\n        mUndoStack.endMacro();\n}\n\nvoid CSMWorld::CommandMacro::push (QUndoCommand *command)\n{\n    if (!mStarted)\n    {\n        mUndoStack.beginMacro (mDescription.isEmpty() ? command->text() : mDescription);\n        mStarted = true;\n    }\n\n    mUndoStack.push (command);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/commandmacro.hpp",
    "content": "#ifndef CSM_WOLRD_COMMANDMACRO_H\n#define CSM_WOLRD_COMMANDMACRO_H\n\nclass QUndoStack;\nclass QUndoCommand;\n\n#include <QString>\n\nnamespace CSMWorld\n{\n    class CommandMacro\n    {\n            QUndoStack& mUndoStack;\n            QString mDescription;\n            bool mStarted;\n\n            /// not implemented\n            CommandMacro (const CommandMacro&);\n\n            /// not implemented\n            CommandMacro& operator= (const CommandMacro&);\n\n        public:\n\n            /// If \\a description is empty, the description of the first command is used.\n            CommandMacro (QUndoStack& undoStack, const QString& description = \"\");\n\n            ~CommandMacro();\n\n            void push (QUndoCommand *command);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/commands.cpp",
    "content": "#include \"commands.hpp\"\n\n#include <cmath>\n#include <sstream>\n#include <unordered_set>\n\n#include <components/misc/stringops.hpp>\n\n#include <QAbstractItemModel>\n#include <QAbstractProxyModel>\n\n#include \"cellcoordinates.hpp\"\n#include \"idcollection.hpp\"\n#include \"idtable.hpp\"\n#include \"idtree.hpp\"\n#include \"nestedtablewrapper.hpp\"\n#include \"pathgrid.hpp\"\n\nCSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUndoCommand* parent)\n    : QUndoCommand(parent)\n    , mTable(table)\n    , mId(id)\n    , mOld(nullptr)\n    , mChanged(false)\n{\n    setText((\"Touch \" + mId).c_str());\n    mOld.reset(mTable.getRecord(mId).clone());\n}\n\nvoid CSMWorld::TouchCommand::redo()\n{\n    mChanged = mTable.touchRecord(mId);\n}\n\nvoid CSMWorld::TouchCommand::undo()\n{\n    if (mChanged)\n    {\n        mTable.setRecord(mId, *mOld);\n        mChanged = false;\n    }\n}\n\nCSMWorld::ImportLandTexturesCommand::ImportLandTexturesCommand(IdTable& landTable,\n    IdTable& ltexTable, QUndoCommand* parent)\n    : QUndoCommand(parent)\n    , mLands(landTable)\n    , mLtexs(ltexTable)\n    , mOldState(0)\n{\n    setText(\"Copy land textures to current plugin\");\n}\n\nvoid CSMWorld::ImportLandTexturesCommand::redo()\n{\n    int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex);\n    int oldPlugin = mLands.data(mLands.getModelIndex(getOriginId(), pluginColumn)).toInt();\n\n    // Original data\n    int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);\n    mOld = mLands.data(mLands.getModelIndex(getOriginId(), textureColumn)).value<DataType>();\n\n    // Need to make a copy so the old values can be looked up\n    DataType copy(mOld);\n\n    // Perform touch/copy/etc...\n    onRedo();\n\n    // Find all indices used\n    std::unordered_set<int> texIndices;\n    for (int i = 0; i < mOld.size(); ++i)\n    {\n        // All indices are offset by 1 for a default texture\n        if (mOld[i] > 0)\n            texIndices.insert(mOld[i] - 1);\n    }\n\n    std::vector<std::string> oldTextures;\n    oldTextures.reserve(texIndices.size());\n    for (int index : texIndices)\n    {\n        oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));\n    }\n\n    // Import the textures, replace old values\n    LandTextureIdTable::ImportResults results = dynamic_cast<LandTextureIdTable&>(mLtexs).importTextures(oldTextures);\n    mCreatedTextures = std::move(results.createdRecords);\n    for (const auto& it : results.recordMapping)\n    {\n        int plugin = 0, newIndex = 0, oldIndex = 0;\n        LandTexture::parseUniqueRecordId(it.first, plugin, oldIndex);\n        LandTexture::parseUniqueRecordId(it.second, plugin, newIndex);\n\n        if (newIndex != oldIndex)\n        {\n            for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i)\n            {\n                // All indices are offset by 1 for a default texture\n                if (mOld[i] == oldIndex + 1)\n                    copy[i] = newIndex + 1;\n            }\n        }\n    }\n\n    // Apply modification\n    int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification);\n    mOldState = mLands.data(mLands.getModelIndex(getDestinationId(), stateColumn)).toInt();\n\n    QVariant variant;\n    variant.setValue(copy);\n    mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant);\n}\n\nvoid CSMWorld::ImportLandTexturesCommand::undo()\n{\n    // Restore to previous\n    int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);\n    QVariant variant;\n    variant.setValue(mOld);\n    mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant);\n\n    int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification);\n    mLands.setData(mLands.getModelIndex(getDestinationId(), stateColumn), mOldState);\n\n    // Undo copy/touch/etc...\n    onUndo();\n\n    for (const std::string& id : mCreatedTextures)\n    {\n        int row = mLtexs.getModelIndex(id, 0).row();\n        mLtexs.removeRows(row, 1);\n    }\n    mCreatedTextures.clear();\n}\n\nCSMWorld::CopyLandTexturesCommand::CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable,\n    const std::string& origin, const std::string& dest, QUndoCommand* parent)\n    : ImportLandTexturesCommand(landTable, ltexTable, parent)\n    , mOriginId(origin)\n    , mDestId(dest)\n{\n}\n\nconst std::string& CSMWorld::CopyLandTexturesCommand::getOriginId() const\n{\n    return mOriginId;\n}\n\nconst std::string& CSMWorld::CopyLandTexturesCommand::getDestinationId() const\n{\n    return mDestId;\n}\n\nCSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTable,\n    const std::string& id, QUndoCommand* parent)\n    : ImportLandTexturesCommand(landTable, ltexTable, parent)\n    , mId(id)\n    , mOld(nullptr)\n    , mChanged(false)\n{\n    setText((\"Touch \" + mId).c_str());\n    mOld.reset(mLands.getRecord(mId).clone());\n}\n\nconst std::string& CSMWorld::TouchLandCommand::getOriginId() const\n{\n    return mId;\n}\n\nconst std::string& CSMWorld::TouchLandCommand::getDestinationId() const\n{\n    return mId;\n}\n\nvoid CSMWorld::TouchLandCommand::onRedo()\n{\n    mChanged = mLands.touchRecord(mId);\n}\n\nvoid CSMWorld::TouchLandCommand::onUndo()\n{\n    if (mChanged)\n    {\n        mLands.setRecord(mId, *mOld);\n        mChanged = false;\n    }\n}\n\nCSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,\n                                        const QVariant& new_, QUndoCommand* parent)\n    : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)\n{\n    if (QAbstractProxyModel *proxy = dynamic_cast<QAbstractProxyModel *> (&model))\n    {\n        // Replace proxy with actual model\n        mIndex = proxy->mapToSource (index);\n        mModel = proxy->sourceModel();\n    }\n\n    if (mIndex.parent().isValid())\n    {\n        CSMWorld::IdTree* tree = &dynamic_cast<CSMWorld::IdTree&>(*mModel);\n        setText (\"Modify \" + tree->nestedHeaderData (\n                    mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());\n    }\n    else\n    {\n        setText (\"Modify \" + mModel->headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());\n    }\n\n    // Remember record state before the modification\n    if (CSMWorld::IdTable *table = dynamic_cast<IdTable *>(mModel))\n    {\n        mHasRecordState = true;\n        int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification);\n\n        int rowIndex = mIndex.row();\n        if (mIndex.parent().isValid())\n        {\n            rowIndex = mIndex.parent().row();\n        }\n\n        mRecordStateIndex = table->index(rowIndex, stateColumnIndex);\n        mOldRecordState = static_cast<CSMWorld::RecordBase::State>(table->data(mRecordStateIndex).toInt());\n    }\n}\n\nvoid CSMWorld::ModifyCommand::redo()\n{\n    mOld = mModel->data (mIndex, Qt::EditRole);\n    mModel->setData (mIndex, mNew);\n}\n\nvoid CSMWorld::ModifyCommand::undo()\n{\n    mModel->setData (mIndex, mOld);\n    if (mHasRecordState)\n    {\n        mModel->setData(mRecordStateIndex, mOldRecordState);\n    }\n}\n\n\nvoid CSMWorld::CreateCommand::applyModifications()\n{\n    if (!mNestedValues.empty())\n    {\n        CSMWorld::IdTree* tree = &dynamic_cast<CSMWorld::IdTree&>(mModel);\n        std::map<int, std::pair<int, QVariant> >::const_iterator current = mNestedValues.begin();\n        std::map<int, std::pair<int, QVariant> >::const_iterator end = mNestedValues.end();\n        for (; current != end; ++current)\n        {\n            QModelIndex index = tree->index(0,\n                                            current->second.first,\n                                            tree->getNestedModelIndex(mId, current->first));\n            tree->setData(index, current->second.second);\n        }\n    }\n}\n\nCSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent)\n: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)\n{\n    setText ((\"Create record \" + id).c_str());\n}\n\nvoid CSMWorld::CreateCommand::addValue (int column, const QVariant& value)\n{\n    mValues[column] = value;\n}\n\nvoid CSMWorld::CreateCommand::addNestedValue(int parentColumn, int nestedColumn, const QVariant &value)\n{\n    mNestedValues[parentColumn] = std::make_pair(nestedColumn, value);\n}\n\nvoid CSMWorld::CreateCommand::setType (UniversalId::Type type)\n{\n    mType = type;\n}\n\nvoid CSMWorld::CreateCommand::redo()\n{\n    mModel.addRecordWithData (mId, mValues, mType);\n    applyModifications();\n}\n\nvoid CSMWorld::CreateCommand::undo()\n{\n    mModel.removeRow (mModel.getModelIndex (mId, 0).row());\n}\n\nCSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)\n: QUndoCommand (parent), mModel (model), mId (id), mOld (nullptr)\n{\n    setText ((\"Revert record \" + id).c_str());\n\n    mOld = model.getRecord (id).clone();\n}\n\nCSMWorld::RevertCommand::~RevertCommand()\n{\n    delete mOld;\n}\n\nvoid CSMWorld::RevertCommand::redo()\n{\n    int column = mModel.findColumnIndex (Columns::ColumnId_Modification);\n\n    QModelIndex index = mModel.getModelIndex (mId, column);\n    RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());\n\n    if (state==RecordBase::State_ModifiedOnly)\n    {\n        mModel.removeRows (index.row(), 1);\n    }\n    else\n    {\n        mModel.setData (index, static_cast<int> (RecordBase::State_BaseOnly));\n    }\n}\n\nvoid CSMWorld::RevertCommand::undo()\n{\n    mModel.setRecord (mId, *mOld);\n}\n\nCSMWorld::DeleteCommand::DeleteCommand (IdTable& model,\n        const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent)\n: QUndoCommand (parent), mModel (model), mId (id), mOld (nullptr), mType(type)\n{\n    setText ((\"Delete record \" + id).c_str());\n\n    mOld = model.getRecord (id).clone();\n}\n\nCSMWorld::DeleteCommand::~DeleteCommand()\n{\n    delete mOld;\n}\n\nvoid CSMWorld::DeleteCommand::redo()\n{\n    int column = mModel.findColumnIndex (Columns::ColumnId_Modification);\n\n    QModelIndex index = mModel.getModelIndex (mId, column);\n    RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());\n\n    if (state==RecordBase::State_ModifiedOnly)\n    {\n        mModel.removeRows (index.row(), 1);\n    }\n    else\n    {\n        mModel.setData (index, static_cast<int> (RecordBase::State_Deleted));\n    }\n}\n\nvoid CSMWorld::DeleteCommand::undo()\n{\n    mModel.setRecord (mId, *mOld, mType);\n}\n\n\nCSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex,\n        const std::vector<int>& newOrder)\n: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder)\n{}\n\nvoid CSMWorld::ReorderRowsCommand::redo()\n{\n    mModel.reorderRows (mBaseIndex, mNewOrder);\n}\n\nvoid CSMWorld::ReorderRowsCommand::undo()\n{\n    int size = static_cast<int> (mNewOrder.size());\n    std::vector<int> reverse (size);\n\n    for (int i=0; i< size; ++i)\n        reverse.at (mNewOrder[i]) = i;\n\n    mModel.reorderRows (mBaseIndex, reverse);\n}\n\nCSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model,\n                                      const std::string& idOrigin,\n                                      const std::string& idDestination,\n                                      const CSMWorld::UniversalId::Type type,\n                                      QUndoCommand* parent)\n: CreateCommand (model, idDestination, parent), mIdOrigin (idOrigin)\n{\n    setType (type);\n    setText ( (\"Clone record \" + idOrigin + \" to the \" + idDestination).c_str());\n}\n\nvoid CSMWorld::CloneCommand::redo()\n{\n    mModel.cloneRecord (mIdOrigin, mId, mType);\n    applyModifications();\n    for (auto& value : mOverrideValues)\n    {\n        mModel.setData(mModel.getModelIndex (mId, value.first), value.second);\n    }\n}\n\nvoid CSMWorld::CloneCommand::undo()\n{\n    mModel.removeRow (mModel.getModelIndex (mId, 0).row());\n}\n\nvoid CSMWorld::CloneCommand::setOverrideValue(int column, QVariant value)\n{\n    mOverrideValues.emplace_back(std::make_pair(column, value));\n}\n\nCSMWorld::CreatePathgridCommand::CreatePathgridCommand(IdTable& model, const std::string& id, QUndoCommand *parent)\n    : CreateCommand(model, id, parent)\n{\n    setType(UniversalId::Type_Pathgrid);\n}\n\nvoid CSMWorld::CreatePathgridCommand::redo()\n{\n    CreateCommand::redo();\n\n    Record<Pathgrid> record = static_cast<const Record<Pathgrid>& >(mModel.getRecord(mId));\n    record.get().blank();\n    record.get().mCell = mId;\n\n    std::pair<CellCoordinates, bool> coords = CellCoordinates::fromId(mId);\n    if (coords.second)\n    {\n        record.get().mData.mX = coords.first.getX();\n        record.get().mData.mY = coords.first.getY();\n    }\n\n    mModel.setRecord(mId, record, mType);\n}\n\nCSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent)\n: QUndoCommand (parent), mModel (model), mRow (row)\n{\n    setText (\"Update cell ID\");\n}\n\nvoid CSMWorld::UpdateCellCommand::redo()\n{\n    if (!mNew.isValid())\n    {\n        int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell);\n        mIndex = mModel.index (mRow, cellColumn);\n\n        QModelIndex xIndex = mModel.index (\n            mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos));\n\n        QModelIndex yIndex = mModel.index (\n            mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos));\n\n        int x = std::floor (mModel.data (xIndex).toFloat() / Constants::CellSizeInUnits);\n        int y = std::floor (mModel.data (yIndex).toFloat() / Constants::CellSizeInUnits);\n\n        std::ostringstream stream;\n\n        stream << \"#\" << x << \" \" << y;\n\n        mNew = QString::fromUtf8 (stream.str().c_str());\n    }\n\n    mModel.setData (mIndex, mNew);\n}\n\nvoid CSMWorld::UpdateCellCommand::undo()\n{\n    mModel.setData (mIndex, mOld);\n}\n\n\nCSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model,\n                                                    const std::string& id,\n                                                    int nestedRow,\n                                                    int parentColumn,\n                                                    QUndoCommand* parent) :\n    QUndoCommand(parent),\n    NestedTableStoring(model, id, parentColumn),\n    mModel(model),\n    mId(id),\n    mParentColumn(parentColumn),\n    mNestedRow(nestedRow)\n{\n    std::string title =\n        model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData();\n    setText ((\"Delete row in \" + title + \" sub-table of \" + mId).c_str());\n\n    QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);\n    mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this);\n}\n\nvoid CSMWorld::DeleteNestedCommand::redo()\n{\n    QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);\n    mModel.removeRows (mNestedRow, 1, parentIndex);\n    mModifyParentCommand->redo();\n}\n\n\nvoid CSMWorld::DeleteNestedCommand::undo()\n{\n    QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);\n    mModel.setNestedTable(parentIndex, getOld());\n    mModifyParentCommand->undo();\n}\n\nCSMWorld::AddNestedCommand::AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent)\n    : QUndoCommand(parent),\n      NestedTableStoring(model, id, parentColumn),\n      mModel(model),\n      mId(id),\n      mNewRow(nestedRow),\n      mParentColumn(parentColumn)\n{\n    std::string title =\n        model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData();\n    setText ((\"Add row in \" + title + \" sub-table of \" + mId).c_str());\n\n    QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);\n    mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this);\n}\n\nvoid CSMWorld::AddNestedCommand::redo()\n{\n    QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);\n    mModel.addNestedRow (parentIndex, mNewRow);\n    mModifyParentCommand->redo();\n}\n\nvoid CSMWorld::AddNestedCommand::undo()\n{\n    QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);\n    mModel.setNestedTable(parentIndex, getOld());\n    mModifyParentCommand->undo();\n}\n\nCSMWorld::NestedTableStoring::NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn)\n    : mOld(model.nestedTable(model.getModelIndex(id, parentColumn))) {}\n\nCSMWorld::NestedTableStoring::~NestedTableStoring()\n{\n    delete mOld;\n}\n\nconst CSMWorld::NestedTableWrapperBase& CSMWorld::NestedTableStoring::getOld() const\n{\n    return *mOld;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/commands.hpp",
    "content": "#ifndef CSM_WOLRD_COMMANDS_H\n#define CSM_WOLRD_COMMANDS_H\n\n#include \"record.hpp\"\n\n#include <string>\n#include <map>\n#include <memory>\n#include <vector>\n\n#include <QVariant>\n#include <QUndoCommand>\n#include <QModelIndex>\n\n#include \"columnimp.hpp\"\n#include \"universalid.hpp\"\n#include \"nestedtablewrapper.hpp\"\n\nclass QModelIndex;\nclass QAbstractItemModel;\n\nnamespace CSMWorld\n{\n    class IdTable;\n    class IdTree;\n    struct RecordBase;\n    struct NestedTableWrapperBase;\n\n    class TouchCommand : public QUndoCommand\n    {\n        public:\n\n            TouchCommand(IdTable& model, const std::string& id, QUndoCommand* parent=nullptr);\n\n            void redo() override;\n            void undo() override;\n\n        private:\n\n            IdTable& mTable;\n            std::string mId;\n            std::unique_ptr<RecordBase> mOld;\n\n            bool mChanged;\n    };\n\n    /// \\brief Adds LandTexture records and modifies texture indices as needed.\n    ///\n    /// LandTexture records are different from other types of records, because\n    /// they only effect the current plugin. Thus, when modifying or copying\n    /// a Land record, all of the LandTexture records referenced need to be\n    /// added to the current plugin. Since these newly added LandTextures could\n    /// have indices that conflict with pre-existing LandTextures in the current\n    /// plugin, the indices might have to be changed, both for the newly added\n    /// LandRecord and within the Land record.\n    class ImportLandTexturesCommand : public QUndoCommand\n    {\n        public:\n\n            ImportLandTexturesCommand(IdTable& landTable, IdTable& ltexTable,\n                QUndoCommand* parent);\n\n            void redo() override;\n            void undo() override;\n\n        protected:\n\n            using DataType = LandTexturesColumn::DataType;\n\n            virtual const std::string& getOriginId() const = 0;\n            virtual const std::string& getDestinationId() const = 0;\n\n            virtual void onRedo() = 0;\n            virtual void onUndo() = 0;\n\n            IdTable& mLands;\n            IdTable& mLtexs;\n            DataType mOld;\n            int mOldState;\n            std::vector<std::string> mCreatedTextures;\n    };\n\n    /// \\brief This command is used to fix LandTexture records and texture\n    ///     indices after cloning a Land. See ImportLandTexturesCommand for\n    ///     details.\n    class CopyLandTexturesCommand : public ImportLandTexturesCommand\n    {\n        public:\n\n            CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, const std::string& origin,\n                const std::string& dest, QUndoCommand* parent = nullptr);\n\n        private:\n\n            const std::string& getOriginId() const override;\n            const std::string& getDestinationId() const override;\n\n            void onRedo() override {}\n            void onUndo() override {}\n\n            std::string mOriginId;\n            std::string mDestId;\n    };\n\n    /// \\brief This command brings a land record into the current plugin, adding\n    ///     LandTexture records and modifying texture indices as needed.\n    /// \\note See ImportLandTextures for more details.\n    class TouchLandCommand : public ImportLandTexturesCommand\n    {\n        public:\n\n            TouchLandCommand(IdTable& landTable, IdTable& ltexTable,\n                const std::string& id, QUndoCommand* parent = nullptr);\n\n        private:\n\n            const std::string& getOriginId() const override;\n            const std::string& getDestinationId() const override;\n\n            void onRedo() override;\n            void onUndo() override;\n\n            std::string mId;\n            std::unique_ptr<RecordBase> mOld;\n\n            bool mChanged;\n    };\n\n    class ModifyCommand : public QUndoCommand\n    {\n            QAbstractItemModel *mModel;\n            QModelIndex mIndex;\n            QVariant mNew;\n            QVariant mOld;\n\n            bool mHasRecordState;\n            QModelIndex mRecordStateIndex;\n            CSMWorld::RecordBase::State mOldRecordState;\n\n        public:\n\n            ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_,\n                QUndoCommand *parent = nullptr);\n\n            void redo() override;\n\n            void undo() override;\n    };\n\n    class CreateCommand : public QUndoCommand\n    {\n            std::map<int, QVariant> mValues;\n            std::map<int, std::pair<int, QVariant> > mNestedValues;\n            ///< Parameter order: a parent column, a nested column, a data.\n            ///< A nested row has index of 0.\n\n        protected:\n\n            IdTable& mModel;\n            std::string mId;\n            UniversalId::Type mType;\n\n        protected:\n\n            /// Apply modifications set via addValue.\n            void applyModifications();\n\n        public:\n\n            CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = nullptr);\n\n            void setType (UniversalId::Type type);\n\n            void addValue (int column, const QVariant& value);\n\n            void addNestedValue(int parentColumn, int nestedColumn, const QVariant &value);\n\n            void redo() override;\n\n            void undo() override;\n    };\n\n    class CloneCommand : public CreateCommand\n    {\n            std::string mIdOrigin;\n            std::vector<std::pair<int, QVariant>> mOverrideValues;\n\n        public:\n\n            CloneCommand (IdTable& model, const std::string& idOrigin,\n                          const std::string& IdDestination,\n                          const UniversalId::Type type,\n                          QUndoCommand* parent = nullptr);\n\n            void redo() override;\n\n            void undo() override;\n\n            void setOverrideValue(int column, QVariant value);\n    };\n\n    class RevertCommand : public QUndoCommand\n    {\n            IdTable& mModel;\n            std::string mId;\n            RecordBase *mOld;\n\n            // not implemented\n            RevertCommand (const RevertCommand&);\n            RevertCommand& operator= (const RevertCommand&);\n\n        public:\n\n            RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent = nullptr);\n\n            virtual ~RevertCommand();\n\n            void redo() override;\n\n            void undo() override;\n    };\n\n    class DeleteCommand : public QUndoCommand\n    {\n            IdTable& mModel;\n            std::string mId;\n            RecordBase *mOld;\n            UniversalId::Type mType;\n\n            // not implemented\n            DeleteCommand (const DeleteCommand&);\n            DeleteCommand& operator= (const DeleteCommand&);\n\n        public:\n\n            DeleteCommand (IdTable& model, const std::string& id,\n                    UniversalId::Type type = UniversalId::Type_None, QUndoCommand *parent = nullptr);\n\n            virtual ~DeleteCommand();\n\n            void redo() override;\n\n            void undo() override;\n    };\n\n    class ReorderRowsCommand : public QUndoCommand\n    {\n            IdTable& mModel;\n            int mBaseIndex;\n            std::vector<int> mNewOrder;\n\n        public:\n\n            ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector<int>& newOrder);\n\n            void redo() override;\n\n            void undo() override;\n    };\n\n    class CreatePathgridCommand : public CreateCommand\n    {\n        public:\n\n            CreatePathgridCommand(IdTable& model, const std::string& id, QUndoCommand *parent = nullptr);\n\n            void redo() override;\n    };\n\n    /// \\brief Update cell ID according to x/y-coordinates\n    ///\n    /// \\note The new value will be calculated in the first call to redo instead of the\n    /// constructor to accommodate multiple coordinate-affecting commands being executed\n    /// in a macro.\n    class UpdateCellCommand : public QUndoCommand\n    {\n            IdTable& mModel;\n            int mRow;\n            QModelIndex mIndex;\n            QVariant mNew; // invalid, if new cell ID has not been calculated yet\n            QVariant mOld;\n\n        public:\n\n            UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent = nullptr);\n\n            void redo() override;\n\n            void undo() override;\n    };\n\n\n    class NestedTableStoring\n    {\n        NestedTableWrapperBase* mOld;\n\n    public:\n        NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn);\n\n        ~NestedTableStoring();\n\n    protected:\n\n        const NestedTableWrapperBase& getOld() const;\n    };\n\n    class DeleteNestedCommand : public QUndoCommand, private NestedTableStoring\n    {\n            IdTree& mModel;\n\n            std::string mId;\n\n            int mParentColumn;\n\n            int mNestedRow;\n\n            // The command to redo/undo the Modified status of a record\n            ModifyCommand *mModifyParentCommand;\n\n        public:\n\n            DeleteNestedCommand (IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = nullptr);\n\n            void redo() override;\n\n            void undo() override;\n    };\n\n    class AddNestedCommand : public QUndoCommand, private NestedTableStoring\n    {\n            IdTree& mModel;\n\n            std::string mId;\n\n            int mNewRow;\n\n            int mParentColumn;\n\n            // The command to redo/undo the Modified status of a record\n            ModifyCommand *mModifyParentCommand;\n\n        public:\n\n            AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = nullptr);\n\n            void redo() override;\n\n            void undo() override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/data.cpp",
    "content": "#include \"data.hpp\"\n\n#include <stdexcept>\n#include <algorithm>\n\n#include <QAbstractItemModel>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/defs.hpp>\n#include <components/esm/loadglob.hpp>\n#include <components/esm/cellref.hpp>\n\n#include <components/resource/scenemanager.hpp>\n#include <components/sceneutil/shadow.hpp>\n#include <components/shader/shadermanager.hpp>\n#include <components/vfs/manager.hpp>\n#include <components/vfs/registerarchives.hpp>\n\n#include \"idtable.hpp\"\n#include \"idtree.hpp\"\n#include \"columnimp.hpp\"\n#include \"regionmap.hpp\"\n#include \"columns.hpp\"\n#include \"resourcesmanager.hpp\"\n#include \"resourcetable.hpp\"\n#include \"nestedcoladapterimp.hpp\"\n\nvoid CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update)\n{\n    mModels.push_back (model);\n    mModelIndex.insert (std::make_pair (type, model));\n\n    UniversalId::Type type2 = UniversalId::getParentType (type);\n\n    if (type2!=UniversalId::Type_None)\n        mModelIndex.insert (std::make_pair (type2, model));\n\n    if (update)\n    {\n        connect (model, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n            this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&)));\n        connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n            this, SLOT (rowsChanged (const QModelIndex&, int, int)));\n        connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),\n            this, SLOT (rowsChanged (const QModelIndex&, int, int)));\n    }\n}\n\nvoid CSMWorld::Data::appendIds (std::vector<std::string>& ids, const CollectionBase& collection,\n    bool listDeleted)\n{\n    std::vector<std::string> ids2 = collection.getIds (listDeleted);\n\n    ids.insert (ids.end(), ids2.begin(), ids2.end());\n}\n\nint CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collection)\n{\n    int number = 0;\n\n    for (int i=0; i<collection.getSize(); ++i)\n        if (collection.getRecord (i).mState==state)\n            ++number;\n\n    return number;\n}\n\nCSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,\n    const std::vector<std::string>& archives, const boost::filesystem::path& resDir)\n: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),\n  mReader (nullptr), mDialogue (nullptr), mReaderIndex(1),\n  mFsStrict(fsStrict), mDataPaths(dataPaths), mArchives(archives)\n{\n    mVFS.reset(new VFS::Manager(mFsStrict));\n    VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths, !mFsStrict), mArchives, true);\n\n    mResourcesManager.setVFS(mVFS.get());\n    mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));\n\n    Shader::ShaderManager::DefineMap defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();\n    Shader::ShaderManager::DefineMap shadowDefines = SceneUtil::ShadowManager::getShadowsDisabledDefines();\n    defines[\"forcePPL\"] = \"0\"; // Don't force per-pixel lighting\n    defines[\"clamp\"] = \"1\"; // Clamp lighting\n    defines[\"preLightEnv\"] = \"0\"; // Apply environment maps after lighting like Morrowind\n    defines[\"radialFog\"] = \"0\";\n    defines[\"lightingModel\"] = \"0\";\n    for (const auto& define : shadowDefines)\n        defines[define.first] = define.second;\n    mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);\n\n    mResourceSystem->getSceneManager()->setShaderPath((resDir / \"shaders\").string());\n\n    int index = 0;\n\n    mGlobals.addColumn (new StringIdColumn<ESM::Global>);\n    mGlobals.addColumn (new RecordStateColumn<ESM::Global>);\n    mGlobals.addColumn (new FixedRecordTypeColumn<ESM::Global> (UniversalId::Type_Global));\n    mGlobals.addColumn (new VarTypeColumn<ESM::Global> (ColumnBase::Display_GlobalVarType));\n    mGlobals.addColumn (new VarValueColumn<ESM::Global>);\n\n    mGmsts.addColumn (new StringIdColumn<ESM::GameSetting>);\n    mGmsts.addColumn (new RecordStateColumn<ESM::GameSetting>);\n    mGmsts.addColumn (new FixedRecordTypeColumn<ESM::GameSetting> (UniversalId::Type_Gmst));\n    mGmsts.addColumn (new VarTypeColumn<ESM::GameSetting> (ColumnBase::Display_GmstVarType));\n    mGmsts.addColumn (new VarValueColumn<ESM::GameSetting>);\n\n    mSkills.addColumn (new StringIdColumn<ESM::Skill>);\n    mSkills.addColumn (new RecordStateColumn<ESM::Skill>);\n    mSkills.addColumn (new FixedRecordTypeColumn<ESM::Skill> (UniversalId::Type_Skill));\n    mSkills.addColumn (new AttributeColumn<ESM::Skill>);\n    mSkills.addColumn (new SpecialisationColumn<ESM::Skill>);\n    for (int i=0; i<4; ++i)\n        mSkills.addColumn (new UseValueColumn<ESM::Skill> (i));\n    mSkills.addColumn (new DescriptionColumn<ESM::Skill>);\n\n    mClasses.addColumn (new StringIdColumn<ESM::Class>);\n    mClasses.addColumn (new RecordStateColumn<ESM::Class>);\n    mClasses.addColumn (new FixedRecordTypeColumn<ESM::Class> (UniversalId::Type_Class));\n    mClasses.addColumn (new NameColumn<ESM::Class>);\n    mClasses.addColumn (new AttributesColumn<ESM::Class> (0));\n    mClasses.addColumn (new AttributesColumn<ESM::Class> (1));\n    mClasses.addColumn (new SpecialisationColumn<ESM::Class>);\n    for (int i=0; i<5; ++i)\n        mClasses.addColumn (new SkillsColumn<ESM::Class> (i, true, true));\n    for (int i=0; i<5; ++i)\n        mClasses.addColumn (new SkillsColumn<ESM::Class> (i, true, false));\n    mClasses.addColumn (new PlayableColumn<ESM::Class>);\n    mClasses.addColumn (new DescriptionColumn<ESM::Class>);\n\n    mFactions.addColumn (new StringIdColumn<ESM::Faction>);\n    mFactions.addColumn (new RecordStateColumn<ESM::Faction>);\n    mFactions.addColumn (new FixedRecordTypeColumn<ESM::Faction> (UniversalId::Type_Faction));\n    mFactions.addColumn (new NameColumn<ESM::Faction>);\n    mFactions.addColumn (new AttributesColumn<ESM::Faction> (0));\n    mFactions.addColumn (new AttributesColumn<ESM::Faction> (1));\n    mFactions.addColumn (new HiddenColumn<ESM::Faction>);\n    for (int i=0; i<7; ++i)\n        mFactions.addColumn (new SkillsColumn<ESM::Faction> (i));\n    // Faction Reactions\n    mFactions.addColumn (new NestedParentColumn<ESM::Faction> (Columns::ColumnId_FactionReactions));\n    index = mFactions.getColumns()-1;\n    mFactions.addAdapter (std::make_pair(&mFactions.getColumn(index), new FactionReactionsAdapter ()));\n    mFactions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Faction, ColumnBase::Display_Faction));\n    mFactions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_FactionReaction, ColumnBase::Display_Integer));\n\n    // Faction Ranks\n    mFactions.addColumn (new NestedParentColumn<ESM::Faction> (Columns::ColumnId_FactionRanks));\n    index = mFactions.getColumns()-1;\n    mFactions.addAdapter (std::make_pair(&mFactions.getColumn(index), new FactionRanksAdapter ()));\n    mFactions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_RankName, ColumnBase::Display_Rank));\n    mFactions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_FactionAttrib1, ColumnBase::Display_Integer));\n    mFactions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_FactionAttrib2, ColumnBase::Display_Integer));\n    mFactions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_FactionPrimSkill, ColumnBase::Display_Integer));\n    mFactions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_FactionFavSkill, ColumnBase::Display_Integer));\n    mFactions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_FactionRep, ColumnBase::Display_Integer));\n\n    mRaces.addColumn (new StringIdColumn<ESM::Race>);\n    mRaces.addColumn (new RecordStateColumn<ESM::Race>);\n    mRaces.addColumn (new FixedRecordTypeColumn<ESM::Race> (UniversalId::Type_Race));\n    mRaces.addColumn (new NameColumn<ESM::Race>);\n    mRaces.addColumn (new DescriptionColumn<ESM::Race>);\n    mRaces.addColumn (new FlagColumn<ESM::Race> (Columns::ColumnId_Playable, 0x1));\n    mRaces.addColumn (new FlagColumn<ESM::Race> (Columns::ColumnId_BeastRace, 0x2));\n    mRaces.addColumn (new WeightHeightColumn<ESM::Race> (true, true));\n    mRaces.addColumn (new WeightHeightColumn<ESM::Race> (true, false));\n    mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, true));\n    mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, false));\n    // Race spells\n    mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_PowerList));\n    index = mRaces.getColumns()-1;\n    mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter<ESM::Race> ()));\n    mRaces.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_Spell));\n    // Race attributes\n    mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceAttributes,\n        ColumnBase::Flag_Dialogue, true)); // fixed rows table\n    index = mRaces.getColumns()-1;\n    mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceAttributeAdapter()));\n    mRaces.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute,\n            ColumnBase::Flag_Dialogue, false));\n    mRaces.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Male, ColumnBase::Display_Integer));\n    mRaces.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Female, ColumnBase::Display_Integer));\n    // Race skill bonus\n    mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceSkillBonus,\n        ColumnBase::Flag_Dialogue, true)); // fixed rows table\n    index = mRaces.getColumns()-1;\n    mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceSkillsBonusAdapter()));\n    mRaces.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));\n    mRaces.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_RaceBonus, ColumnBase::Display_Integer));\n\n    mSounds.addColumn (new StringIdColumn<ESM::Sound>);\n    mSounds.addColumn (new RecordStateColumn<ESM::Sound>);\n    mSounds.addColumn (new FixedRecordTypeColumn<ESM::Sound> (UniversalId::Type_Sound));\n    mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_Volume));\n    mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_MinRange));\n    mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_MaxRange));\n    mSounds.addColumn (new SoundFileColumn<ESM::Sound>);\n\n    mScripts.addColumn (new StringIdColumn<ESM::Script>);\n    mScripts.addColumn (new RecordStateColumn<ESM::Script>);\n    mScripts.addColumn (new FixedRecordTypeColumn<ESM::Script> (UniversalId::Type_Script));\n    mScripts.addColumn (new ScriptColumn<ESM::Script> (ScriptColumn<ESM::Script>::Type_File));\n\n    mRegions.addColumn (new StringIdColumn<ESM::Region>);\n    mRegions.addColumn (new RecordStateColumn<ESM::Region>);\n    mRegions.addColumn (new FixedRecordTypeColumn<ESM::Region> (UniversalId::Type_Region));\n    mRegions.addColumn (new NameColumn<ESM::Region>);\n    mRegions.addColumn (new MapColourColumn<ESM::Region>);\n    mRegions.addColumn (new SleepListColumn<ESM::Region>);\n    // Region Weather\n    mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionWeather));\n    index = mRegions.getColumns()-1;\n    mRegions.addAdapter (std::make_pair(&mRegions.getColumn(index), new RegionWeatherAdapter ()));\n    mRegions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_WeatherName, ColumnBase::Display_String, false));\n    mRegions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_WeatherChance, ColumnBase::Display_UnsignedInteger8));\n    // Region Sounds\n    mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionSounds));\n    index = mRegions.getColumns()-1;\n    mRegions.addAdapter (std::make_pair(&mRegions.getColumn(index), new RegionSoundListAdapter ()));\n    mRegions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_SoundName, ColumnBase::Display_Sound));\n    mRegions.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_SoundChance, ColumnBase::Display_UnsignedInteger8));\n\n    mBirthsigns.addColumn (new StringIdColumn<ESM::BirthSign>);\n    mBirthsigns.addColumn (new RecordStateColumn<ESM::BirthSign>);\n    mBirthsigns.addColumn (new FixedRecordTypeColumn<ESM::BirthSign> (UniversalId::Type_Birthsign));\n    mBirthsigns.addColumn (new NameColumn<ESM::BirthSign>);\n    mBirthsigns.addColumn (new TextureColumn<ESM::BirthSign>);\n    mBirthsigns.addColumn (new DescriptionColumn<ESM::BirthSign>);\n    // Birthsign spells\n    mBirthsigns.addColumn (new NestedParentColumn<ESM::BirthSign> (Columns::ColumnId_PowerList));\n    index = mBirthsigns.getColumns()-1;\n    mBirthsigns.addAdapter (std::make_pair(&mBirthsigns.getColumn(index),\n        new SpellListAdapter<ESM::BirthSign> ()));\n    mBirthsigns.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_Spell));\n\n    mSpells.addColumn (new StringIdColumn<ESM::Spell>);\n    mSpells.addColumn (new RecordStateColumn<ESM::Spell>);\n    mSpells.addColumn (new FixedRecordTypeColumn<ESM::Spell> (UniversalId::Type_Spell));\n    mSpells.addColumn (new NameColumn<ESM::Spell>);\n    mSpells.addColumn (new SpellTypeColumn<ESM::Spell>);\n    mSpells.addColumn (new CostColumn<ESM::Spell>);\n    mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AutoCalc, 0x1));\n    mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2));\n    mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4));\n    // Spell effects\n    mSpells.addColumn (new NestedParentColumn<ESM::Spell> (Columns::ColumnId_EffectList));\n    index = mSpells.getColumns()-1;\n    mSpells.addAdapter (std::make_pair(&mSpells.getColumn(index), new EffectsListAdapter<ESM::Spell> ()));\n    mSpells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));\n    mSpells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill));\n    mSpells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute));\n    mSpells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange));\n    mSpells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String));\n    mSpells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light\n    mSpells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_MinMagnitude, ColumnBase::Display_Integer));\n    mSpells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_MaxMagnitude, ColumnBase::Display_Integer));\n\n    mTopics.addColumn (new StringIdColumn<ESM::Dialogue>);\n    mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>);\n    mTopics.addColumn (new FixedRecordTypeColumn<ESM::Dialogue> (UniversalId::Type_Topic));\n    mTopics.addColumn (new DialogueTypeColumn<ESM::Dialogue>);\n\n    mJournals.addColumn (new StringIdColumn<ESM::Dialogue>);\n    mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);\n    mJournals.addColumn (new FixedRecordTypeColumn<ESM::Dialogue> (UniversalId::Type_Journal));\n    mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true));\n\n    mTopicInfos.addColumn (new StringIdColumn<Info> (true));\n    mTopicInfos.addColumn (new RecordStateColumn<Info>);\n    mTopicInfos.addColumn (new FixedRecordTypeColumn<Info> (UniversalId::Type_TopicInfo));\n    mTopicInfos.addColumn (new TopicColumn<Info> (false));\n    mTopicInfos.addColumn (new ActorColumn<Info>);\n    mTopicInfos.addColumn (new RaceColumn<Info>);\n    mTopicInfos.addColumn (new ClassColumn<Info>);\n    mTopicInfos.addColumn (new FactionColumn<Info>);\n    mTopicInfos.addColumn (new CellColumn<Info>);\n    mTopicInfos.addColumn (new DispositionColumn<Info>);\n    mTopicInfos.addColumn (new RankColumn<Info>);\n    mTopicInfos.addColumn (new GenderColumn<Info>);\n    mTopicInfos.addColumn (new PcFactionColumn<Info>);\n    mTopicInfos.addColumn (new PcRankColumn<Info>);\n    mTopicInfos.addColumn (new SoundFileColumn<Info>);\n    mTopicInfos.addColumn (new ResponseColumn<Info>);\n    // Result script\n    mTopicInfos.addColumn (new NestedParentColumn<Info> (Columns::ColumnId_InfoList,\n        ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List));\n    index = mTopicInfos.getColumns()-1;\n    mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoListAdapter ()));\n    mTopicInfos.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_ScriptText, ColumnBase::Display_ScriptLines));\n    // Special conditions\n    mTopicInfos.addColumn (new NestedParentColumn<Info> (Columns::ColumnId_InfoCondition));\n    index = mTopicInfos.getColumns()-1;\n    mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoConditionAdapter ()));\n    mTopicInfos.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc));\n    // FIXME: don't have dynamic value enum delegate, use Display_String for now\n    mTopicInfos.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_InfoCondVar));\n    mTopicInfos.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));\n    mTopicInfos.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Value, ColumnBase::Display_Var));\n\n    mJournalInfos.addColumn (new StringIdColumn<Info> (true));\n    mJournalInfos.addColumn (new RecordStateColumn<Info>);\n    mJournalInfos.addColumn (new FixedRecordTypeColumn<Info> (UniversalId::Type_JournalInfo));\n    mJournalInfos.addColumn (new TopicColumn<Info> (true));\n    mJournalInfos.addColumn (new QuestStatusTypeColumn<Info>);\n    mJournalInfos.addColumn (new QuestIndexColumn<Info>);\n    mJournalInfos.addColumn (new QuestDescriptionColumn<Info>);\n\n    mCells.addColumn (new StringIdColumn<Cell>);\n    mCells.addColumn (new RecordStateColumn<Cell>);\n    mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));\n    mCells.addColumn (new NameColumn<Cell>);\n    mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep));\n    mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater,\n        ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));\n    mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx,\n        ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));\n    mCells.addColumn (new RegionColumn<Cell>);\n    mCells.addColumn (new RefNumCounterColumn<Cell>);\n    // Misc Cell data\n    mCells.addColumn (new NestedParentColumn<Cell> (Columns::ColumnId_Cell,\n        ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List));\n    index = mCells.getColumns()-1;\n    mCells.addAdapter (std::make_pair(&mCells.getColumn(index), new CellListAdapter ()));\n    mCells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Interior, ColumnBase::Display_Boolean,\n        ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));\n    mCells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Ambient, ColumnBase::Display_Colour));\n    mCells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Sunlight, ColumnBase::Display_Colour));\n    mCells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Fog, ColumnBase::Display_Colour));\n    mCells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_FogDensity, ColumnBase::Display_Float));\n    mCells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_WaterLevel, ColumnBase::Display_Float));\n    mCells.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_MapColor, ColumnBase::Display_Colour));\n\n    mEnchantments.addColumn (new StringIdColumn<ESM::Enchantment>);\n    mEnchantments.addColumn (new RecordStateColumn<ESM::Enchantment>);\n    mEnchantments.addColumn (new FixedRecordTypeColumn<ESM::Enchantment> (UniversalId::Type_Enchantment));\n    mEnchantments.addColumn (new EnchantmentTypeColumn<ESM::Enchantment>);\n    mEnchantments.addColumn (new CostColumn<ESM::Enchantment>);\n    mEnchantments.addColumn (new ChargesColumn2<ESM::Enchantment>);\n    mEnchantments.addColumn (new FlagColumn<ESM::Enchantment> (Columns::ColumnId_AutoCalc, ESM::Enchantment::Autocalc));\n    // Enchantment effects\n    mEnchantments.addColumn (new NestedParentColumn<ESM::Enchantment> (Columns::ColumnId_EffectList));\n    index = mEnchantments.getColumns()-1;\n    mEnchantments.addAdapter (std::make_pair(&mEnchantments.getColumn(index),\n        new EffectsListAdapter<ESM::Enchantment> ()));\n    mEnchantments.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));\n    mEnchantments.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill));\n    mEnchantments.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute));\n    mEnchantments.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange));\n    mEnchantments.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String));\n    mEnchantments.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light\n    mEnchantments.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_MinMagnitude, ColumnBase::Display_Integer));\n    mEnchantments.getNestableColumn(index)->addColumn(\n        new NestedChildColumn (Columns::ColumnId_MaxMagnitude, ColumnBase::Display_Integer));\n\n    mBodyParts.addColumn (new StringIdColumn<ESM::BodyPart>);\n    mBodyParts.addColumn (new RecordStateColumn<ESM::BodyPart>);\n    mBodyParts.addColumn (new FixedRecordTypeColumn<ESM::BodyPart> (UniversalId::Type_BodyPart));\n    mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>);\n    mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>);\n    mBodyParts.addColumn(new GenderNpcColumn<ESM::BodyPart>);\n    mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable,\n        ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true));\n\n    int meshTypeFlags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh;\n    MeshTypeColumn<ESM::BodyPart> *meshTypeColumn = new MeshTypeColumn<ESM::BodyPart>(meshTypeFlags);\n    mBodyParts.addColumn (meshTypeColumn);\n    mBodyParts.addColumn (new ModelColumn<ESM::BodyPart>);\n    mBodyParts.addColumn (new BodyPartRaceColumn(meshTypeColumn));\n\n    mSoundGens.addColumn (new StringIdColumn<ESM::SoundGenerator>);\n    mSoundGens.addColumn (new RecordStateColumn<ESM::SoundGenerator>);\n    mSoundGens.addColumn (new FixedRecordTypeColumn<ESM::SoundGenerator> (UniversalId::Type_SoundGen));\n    mSoundGens.addColumn (new CreatureColumn<ESM::SoundGenerator>);\n    mSoundGens.addColumn (new SoundColumn<ESM::SoundGenerator>);\n    mSoundGens.addColumn (new SoundGeneratorTypeColumn<ESM::SoundGenerator>);\n\n    mMagicEffects.addColumn (new StringIdColumn<ESM::MagicEffect>);\n    mMagicEffects.addColumn (new RecordStateColumn<ESM::MagicEffect>);\n    mMagicEffects.addColumn (new FixedRecordTypeColumn<ESM::MagicEffect> (UniversalId::Type_MagicEffect));\n    mMagicEffects.addColumn (new SchoolColumn<ESM::MagicEffect>);\n    mMagicEffects.addColumn (new BaseCostColumn<ESM::MagicEffect>);\n    mMagicEffects.addColumn (new EffectTextureColumn<ESM::MagicEffect> (Columns::ColumnId_Icon));\n    mMagicEffects.addColumn (new EffectTextureColumn<ESM::MagicEffect> (Columns::ColumnId_Particle));\n    mMagicEffects.addColumn (new EffectObjectColumn<ESM::MagicEffect> (Columns::ColumnId_CastingObject));\n    mMagicEffects.addColumn (new EffectObjectColumn<ESM::MagicEffect> (Columns::ColumnId_HitObject));\n    mMagicEffects.addColumn (new EffectObjectColumn<ESM::MagicEffect> (Columns::ColumnId_AreaObject));\n    mMagicEffects.addColumn (new EffectObjectColumn<ESM::MagicEffect> (Columns::ColumnId_BoltObject));\n    mMagicEffects.addColumn (new EffectSoundColumn<ESM::MagicEffect> (Columns::ColumnId_CastingSound));\n    mMagicEffects.addColumn (new EffectSoundColumn<ESM::MagicEffect> (Columns::ColumnId_HitSound));\n    mMagicEffects.addColumn (new EffectSoundColumn<ESM::MagicEffect> (Columns::ColumnId_AreaSound));\n    mMagicEffects.addColumn (new EffectSoundColumn<ESM::MagicEffect> (Columns::ColumnId_BoltSound));\n    mMagicEffects.addColumn (new FlagColumn<ESM::MagicEffect> (\n        Columns::ColumnId_AllowSpellmaking, ESM::MagicEffect::AllowSpellmaking));\n    mMagicEffects.addColumn (new FlagColumn<ESM::MagicEffect> (\n        Columns::ColumnId_AllowEnchanting, ESM::MagicEffect::AllowEnchanting));\n    mMagicEffects.addColumn (new FlagColumn<ESM::MagicEffect> (\n        Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight));\n    mMagicEffects.addColumn (new DescriptionColumn<ESM::MagicEffect>);\n\n    mLand.addColumn (new StringIdColumn<Land>);\n    mLand.addColumn (new RecordStateColumn<Land>);\n    mLand.addColumn (new FixedRecordTypeColumn<Land>(UniversalId::Type_Land));\n    mLand.addColumn (new LandPluginIndexColumn);\n    mLand.addColumn (new LandNormalsColumn);\n    mLand.addColumn (new LandHeightsColumn);\n    mLand.addColumn (new LandColoursColumn);\n    mLand.addColumn (new LandTexturesColumn);\n\n    mLandTextures.addColumn (new StringIdColumn<LandTexture>(true));\n    mLandTextures.addColumn (new RecordStateColumn<LandTexture>);\n    mLandTextures.addColumn (new FixedRecordTypeColumn<LandTexture>(UniversalId::Type_LandTexture));\n    mLandTextures.addColumn (new LandTextureNicknameColumn);\n    mLandTextures.addColumn (new LandTexturePluginIndexColumn);\n    mLandTextures.addColumn (new LandTextureIndexColumn);\n    mLandTextures.addColumn (new TextureColumn<LandTexture>);\n\n    mPathgrids.addColumn (new StringIdColumn<Pathgrid>);\n    mPathgrids.addColumn (new RecordStateColumn<Pathgrid>);\n    mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (UniversalId::Type_Pathgrid));\n\n    // new object deleted in dtor of Collection<T,A>\n    mPathgrids.addColumn (new NestedParentColumn<Pathgrid> (Columns::ColumnId_PathgridPoints));\n    index = mPathgrids.getColumns()-1;\n    // new object deleted in dtor of NestedCollection<T,A>\n    mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridPointListAdapter ()));\n    // new objects deleted in dtor of NestableColumn\n    // WARNING: The order of the columns below are assumed in PathgridPointListAdapter\n    mPathgrids.getNestableColumn(index)->addColumn(\n            new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer,\n                ColumnBase::Flag_Dialogue, false));\n    mPathgrids.getNestableColumn(index)->addColumn(\n            new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer));\n    mPathgrids.getNestableColumn(index)->addColumn(\n            new NestedChildColumn (Columns::ColumnId_PathgridPosY, ColumnBase::Display_Integer));\n    mPathgrids.getNestableColumn(index)->addColumn(\n            new NestedChildColumn (Columns::ColumnId_PathgridPosZ, ColumnBase::Display_Integer));\n\n    mPathgrids.addColumn (new NestedParentColumn<Pathgrid> (Columns::ColumnId_PathgridEdges));\n    index = mPathgrids.getColumns()-1;\n    mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ()));\n    mPathgrids.getNestableColumn(index)->addColumn(\n            new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer,\n                ColumnBase::Flag_Dialogue, false));\n    mPathgrids.getNestableColumn(index)->addColumn(\n            new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer));\n    mPathgrids.getNestableColumn(index)->addColumn(\n            new NestedChildColumn (Columns::ColumnId_PathgridEdge1, ColumnBase::Display_Integer));\n\n    mStartScripts.addColumn (new StringIdColumn<ESM::StartScript>);\n    mStartScripts.addColumn (new RecordStateColumn<ESM::StartScript>);\n    mStartScripts.addColumn (new FixedRecordTypeColumn<ESM::StartScript> (UniversalId::Type_StartScript));\n\n    mRefs.addColumn (new StringIdColumn<CellRef> (true));\n    mRefs.addColumn (new RecordStateColumn<CellRef>);\n    mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference));\n    mRefs.addColumn (new CellColumn<CellRef> (true));\n    mRefs.addColumn (new OriginalCellColumn<CellRef>);\n    mRefs.addColumn (new IdColumn<CellRef>);\n    mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false));\n    mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 1, false));\n    mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 2, false));\n    mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 0, false));\n    mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 1, false));\n    mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 2, false));\n    mRefs.addColumn (new ScaleColumn<CellRef>);\n    mRefs.addColumn (new OwnerColumn<CellRef>);\n    mRefs.addColumn (new SoulColumn<CellRef>);\n    mRefs.addColumn (new FactionColumn<CellRef>);\n    mRefs.addColumn (new FactionIndexColumn<CellRef>);\n    mRefs.addColumn (new ChargesColumn<CellRef>);\n    mRefs.addColumn (new EnchantmentChargesColumn<CellRef>);\n    mRefs.addColumn (new GoldValueColumn<CellRef>);\n    mRefs.addColumn (new TeleportColumn<CellRef>);\n    mRefs.addColumn (new TeleportCellColumn<CellRef>);\n    mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 0, true));\n    mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 1, true));\n    mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 2, true));\n    mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 0, true));\n    mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 1, true));\n    mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 2, true));\n    mRefs.addColumn (new LockLevelColumn<CellRef>);\n    mRefs.addColumn (new KeyColumn<CellRef>);\n    mRefs.addColumn (new TrapColumn<CellRef>);\n    mRefs.addColumn (new OwnerGlobalColumn<CellRef>);\n    mRefs.addColumn (new RefNumColumn<CellRef>);\n\n    mFilters.addColumn (new StringIdColumn<ESM::Filter>);\n    mFilters.addColumn (new RecordStateColumn<ESM::Filter>);\n    mFilters.addColumn (new FixedRecordTypeColumn<ESM::Filter> (UniversalId::Type_Filter));\n    mFilters.addColumn (new FilterColumn<ESM::Filter>);\n    mFilters.addColumn (new DescriptionColumn<ESM::Filter>);\n\n    mDebugProfiles.addColumn (new StringIdColumn<ESM::DebugProfile>);\n    mDebugProfiles.addColumn (new RecordStateColumn<ESM::DebugProfile>);\n    mDebugProfiles.addColumn (new FixedRecordTypeColumn<ESM::DebugProfile> (UniversalId::Type_DebugProfile));\n    mDebugProfiles.addColumn (new FlagColumn2<ESM::DebugProfile> (\n        Columns::ColumnId_DefaultProfile, ESM::DebugProfile::Flag_Default));\n    mDebugProfiles.addColumn (new FlagColumn2<ESM::DebugProfile> (\n        Columns::ColumnId_BypassNewGame, ESM::DebugProfile::Flag_BypassNewGame));\n    mDebugProfiles.addColumn (new FlagColumn2<ESM::DebugProfile> (\n        Columns::ColumnId_GlobalProfile, ESM::DebugProfile::Flag_Global));\n    mDebugProfiles.addColumn (new DescriptionColumn<ESM::DebugProfile>);\n    mDebugProfiles.addColumn (new ScriptColumn<ESM::DebugProfile> (\n        ScriptColumn<ESM::DebugProfile>::Type_Lines));\n\n    mMetaData.appendBlankRecord (\"sys::meta\");\n\n    mMetaData.addColumn (new StringIdColumn<MetaData> (true));\n    mMetaData.addColumn (new RecordStateColumn<MetaData>);\n    mMetaData.addColumn (new FixedRecordTypeColumn<MetaData> (UniversalId::Type_MetaData));\n    mMetaData.addColumn (new FormatColumn<MetaData>);\n    mMetaData.addColumn (new AuthorColumn<MetaData>);\n    mMetaData.addColumn (new FileDescriptionColumn<MetaData>);\n\n    addModel (new IdTable (&mGlobals), UniversalId::Type_Global);\n    addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst);\n    addModel (new IdTable (&mSkills), UniversalId::Type_Skill);\n    addModel (new IdTable (&mClasses), UniversalId::Type_Class);\n    addModel (new IdTree (&mFactions, &mFactions), UniversalId::Type_Faction);\n    addModel (new IdTree (&mRaces, &mRaces), UniversalId::Type_Race);\n    addModel (new IdTable (&mSounds), UniversalId::Type_Sound);\n    addModel (new IdTable (&mScripts), UniversalId::Type_Script);\n    addModel (new IdTree (&mRegions, &mRegions), UniversalId::Type_Region);\n    addModel (new IdTree (&mBirthsigns, &mBirthsigns), UniversalId::Type_Birthsign);\n    addModel (new IdTree (&mSpells, &mSpells), UniversalId::Type_Spell);\n    addModel (new IdTable (&mTopics), UniversalId::Type_Topic);\n    addModel (new IdTable (&mJournals), UniversalId::Type_Journal);\n    addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic),\n        UniversalId::Type_TopicInfo);\n    addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo);\n    addModel (new IdTree (&mCells, &mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell);\n    addModel (new IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment);\n    addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart);\n    addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);\n    addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect);\n    addModel (new IdTable (&mLand, IdTable::Feature_AllowTouch), UniversalId::Type_Land);\n    addModel (new LandTextureIdTable (&mLandTextures), UniversalId::Type_LandTexture);\n    addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid);\n    addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript);\n    addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview),\n        UniversalId::Type_Referenceable);\n    addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference);\n    addModel (new IdTable (&mFilters), UniversalId::Type_Filter);\n    addModel (new IdTable (&mDebugProfiles), UniversalId::Type_DebugProfile);\n    addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Meshes)),\n        UniversalId::Type_Mesh);\n    addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Icons)),\n        UniversalId::Type_Icon);\n    addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Musics)),\n        UniversalId::Type_Music);\n    addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_SoundsRes)),\n        UniversalId::Type_SoundRes);\n    addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Textures)),\n        UniversalId::Type_Texture);\n    addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)),\n        UniversalId::Type_Video);\n    addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData);\n\n    mActorAdapter.reset(new ActorAdapter(*this));\n\n    mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files\n}\n\nCSMWorld::Data::~Data()\n{\n    for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)\n        delete *iter;\n\n    delete mReader;\n}\n\nstd::shared_ptr<Resource::ResourceSystem> CSMWorld::Data::getResourceSystem()\n{\n    return mResourceSystem;\n}\n\nstd::shared_ptr<const Resource::ResourceSystem> CSMWorld::Data::getResourceSystem() const\n{\n    return mResourceSystem;\n}\n\nconst CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const\n{\n    return mGlobals;\n}\n\nCSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals()\n{\n    return mGlobals;\n}\n\nconst CSMWorld::IdCollection<ESM::GameSetting>& CSMWorld::Data::getGmsts() const\n{\n    return mGmsts;\n}\n\nCSMWorld::IdCollection<ESM::GameSetting>& CSMWorld::Data::getGmsts()\n{\n    return mGmsts;\n}\n\nconst CSMWorld::IdCollection<ESM::Skill>& CSMWorld::Data::getSkills() const\n{\n    return mSkills;\n}\n\nCSMWorld::IdCollection<ESM::Skill>& CSMWorld::Data::getSkills()\n{\n    return mSkills;\n}\n\nconst CSMWorld::IdCollection<ESM::Class>& CSMWorld::Data::getClasses() const\n{\n    return mClasses;\n}\n\nCSMWorld::IdCollection<ESM::Class>& CSMWorld::Data::getClasses()\n{\n    return mClasses;\n}\n\nconst CSMWorld::IdCollection<ESM::Faction>& CSMWorld::Data::getFactions() const\n{\n    return mFactions;\n}\n\nCSMWorld::IdCollection<ESM::Faction>& CSMWorld::Data::getFactions()\n{\n    return mFactions;\n}\n\nconst CSMWorld::IdCollection<ESM::Race>& CSMWorld::Data::getRaces() const\n{\n    return mRaces;\n}\n\nCSMWorld::IdCollection<ESM::Race>& CSMWorld::Data::getRaces()\n{\n    return mRaces;\n}\n\nconst CSMWorld::IdCollection<ESM::Sound>& CSMWorld::Data::getSounds() const\n{\n    return mSounds;\n}\n\nCSMWorld::IdCollection<ESM::Sound>& CSMWorld::Data::getSounds()\n{\n    return mSounds;\n}\n\nconst CSMWorld::IdCollection<ESM::Script>& CSMWorld::Data::getScripts() const\n{\n    return mScripts;\n}\n\nCSMWorld::IdCollection<ESM::Script>& CSMWorld::Data::getScripts()\n{\n    return mScripts;\n}\n\nconst CSMWorld::IdCollection<ESM::Region>& CSMWorld::Data::getRegions() const\n{\n    return mRegions;\n}\n\nCSMWorld::IdCollection<ESM::Region>& CSMWorld::Data::getRegions()\n{\n    return mRegions;\n}\n\nconst CSMWorld::IdCollection<ESM::BirthSign>& CSMWorld::Data::getBirthsigns() const\n{\n    return mBirthsigns;\n}\n\nCSMWorld::IdCollection<ESM::BirthSign>& CSMWorld::Data::getBirthsigns()\n{\n    return mBirthsigns;\n}\n\nconst CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells() const\n{\n    return mSpells;\n}\n\nCSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells()\n{\n    return mSpells;\n}\n\n\nconst CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics() const\n{\n    return mTopics;\n}\n\nCSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics()\n{\n    return mTopics;\n}\n\nconst CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals() const\n{\n    return mJournals;\n}\n\nCSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals()\n{\n    return mJournals;\n}\n\nconst CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos() const\n{\n    return mTopicInfos;\n}\n\nCSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos()\n{\n    return mTopicInfos;\n}\n\nconst CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos() const\n{\n    return mJournalInfos;\n}\n\nCSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos()\n{\n    return mJournalInfos;\n}\n\nconst CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const\n{\n    return mCells;\n}\n\nCSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells()\n{\n    return mCells;\n}\n\nconst CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() const\n{\n    return mReferenceables;\n}\n\nCSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables()\n{\n    return mReferenceables;\n}\n\nconst CSMWorld::RefCollection& CSMWorld::Data::getReferences() const\n{\n    return mRefs;\n}\n\nCSMWorld::RefCollection& CSMWorld::Data::getReferences()\n{\n    return mRefs;\n}\n\nconst CSMWorld::IdCollection<ESM::Filter>& CSMWorld::Data::getFilters() const\n{\n    return mFilters;\n}\n\nCSMWorld::IdCollection<ESM::Filter>& CSMWorld::Data::getFilters()\n{\n    return mFilters;\n}\n\nconst CSMWorld::IdCollection<ESM::Enchantment>& CSMWorld::Data::getEnchantments() const\n{\n    return mEnchantments;\n}\n\nCSMWorld::IdCollection<ESM::Enchantment>& CSMWorld::Data::getEnchantments()\n{\n    return mEnchantments;\n}\n\nconst CSMWorld::IdCollection<ESM::BodyPart>& CSMWorld::Data::getBodyParts() const\n{\n    return mBodyParts;\n}\n\nCSMWorld::IdCollection<ESM::BodyPart>& CSMWorld::Data::getBodyParts()\n{\n    return mBodyParts;\n}\n\nconst CSMWorld::IdCollection<ESM::DebugProfile>& CSMWorld::Data::getDebugProfiles() const\n{\n    return mDebugProfiles;\n}\n\nCSMWorld::IdCollection<ESM::DebugProfile>& CSMWorld::Data::getDebugProfiles()\n{\n    return mDebugProfiles;\n}\n\nconst CSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand() const\n{\n    return mLand;\n}\n\nCSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand()\n{\n    return mLand;\n}\n\nconst CSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures() const\n{\n    return mLandTextures;\n}\n\nCSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures()\n{\n    return mLandTextures;\n}\n\nconst CSMWorld::IdCollection<ESM::SoundGenerator>& CSMWorld::Data::getSoundGens() const\n{\n    return mSoundGens;\n}\n\nCSMWorld::IdCollection<ESM::SoundGenerator>& CSMWorld::Data::getSoundGens()\n{\n    return mSoundGens;\n}\n\nconst CSMWorld::IdCollection<ESM::MagicEffect>& CSMWorld::Data::getMagicEffects() const\n{\n    return mMagicEffects;\n}\n\nCSMWorld::IdCollection<ESM::MagicEffect>& CSMWorld::Data::getMagicEffects()\n{\n    return mMagicEffects;\n}\n\nconst CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& CSMWorld::Data::getPathgrids() const\n{\n    return mPathgrids;\n}\n\nCSMWorld::SubCellCollection<CSMWorld::Pathgrid>& CSMWorld::Data::getPathgrids()\n{\n    return mPathgrids;\n}\n\nconst CSMWorld::IdCollection<ESM::StartScript>& CSMWorld::Data::getStartScripts() const\n{\n    return mStartScripts;\n}\n\nCSMWorld::IdCollection<ESM::StartScript>& CSMWorld::Data::getStartScripts()\n{\n    return mStartScripts;\n}\n\nconst CSMWorld::Resources& CSMWorld::Data::getResources (const UniversalId& id) const\n{\n    return mResourcesManager.get (id.getType());\n}\n\nconst CSMWorld::MetaData& CSMWorld::Data::getMetaData() const\n{\n    return mMetaData.getRecord (0).get();\n}\n\nvoid CSMWorld::Data::setMetaData (const MetaData& metaData)\n{\n    Record<MetaData> record (RecordBase::State_ModifiedOnly, nullptr, &metaData);\n    mMetaData.setRecord (0, record);\n}\n\nQAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)\n{\n    std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());\n\n    if (iter==mModelIndex.end())\n    {\n        // try creating missing (secondary) tables on the fly\n        //\n        // Note: We create these tables here so we don't have to deal with them during load/initial\n        // construction of the ESX data where no update signals are available.\n        if (id.getType()==UniversalId::Type_RegionMap)\n        {\n            RegionMap *table = nullptr;\n            addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, false);\n            return table;\n        }\n        throw std::logic_error (\"No table model available for \" + id.toString());\n    }\n\n    return iter->second;\n}\n\nconst CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() const\n{\n    return mActorAdapter.get();\n}\n\nCSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter()\n{\n    return mActorAdapter.get();\n}\n\nvoid CSMWorld::Data::merge()\n{\n    mGlobals.merge();\n}\n\nint CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)\n{\n    // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading\n    std::shared_ptr<ESM::ESMReader> ptr(mReader);\n    mReaders.push_back(ptr);\n    mReader = nullptr;\n\n    mDialogue = nullptr;\n\n    mReader = new ESM::ESMReader;\n    mReader->setEncoder (&mEncoder);\n    mReader->setIndex((project || !base) ? 0 : mReaderIndex++);\n    mReader->open (path.string());\n\n    mContentFileNames.insert(std::make_pair(path.filename().string(), mReader->getIndex()));\n\n    mBase = base;\n    mProject = project;\n\n    if (!mProject && !mBase)\n    {\n        MetaData metaData;\n        metaData.mId = \"sys::meta\";\n        metaData.load (*mReader);\n\n        mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, nullptr, &metaData));\n    }\n\n    return mReader->getRecordCount();\n}\n\nvoid CSMWorld::Data::loadFallbackEntries()\n{\n    // Load default marker definitions, if game files do not have them for some reason\n    std::pair<std::string, std::string> staticMarkers[] = {\n        std::make_pair(\"DivineMarker\", \"marker_divine.nif\"),\n        std::make_pair(\"DoorMarker\", \"marker_arrow.nif\"),\n        std::make_pair(\"NorthMarker\", \"marker_north.nif\"),\n        std::make_pair(\"TempleMarker\", \"marker_temple.nif\"),\n        std::make_pair(\"TravelMarker\", \"marker_travel.nif\")\n    };\n\n    std::pair<std::string, std::string> doorMarkers[] = {\n        std::make_pair(\"PrisonMarker\", \"marker_prison.nif\")\n    };\n\n    for (const auto &marker : staticMarkers)\n    {\n        if (mReferenceables.searchId (marker.first)==-1)\n        {\n            ESM::Static newMarker;\n            newMarker.mId = marker.first;\n            newMarker.mModel = marker.second;\n            CSMWorld::Record<ESM::Static> record;\n            record.mBase = newMarker;\n            record.mState = CSMWorld::RecordBase::State_BaseOnly;\n            mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Static);\n        }\n    }\n\n    for (const auto &marker : doorMarkers)\n    {\n        if (mReferenceables.searchId (marker.first)==-1)\n        {\n            ESM::Door newMarker;\n            newMarker.mId = marker.first;\n            newMarker.mModel = marker.second;\n            CSMWorld::Record<ESM::Door> record;\n            record.mBase = newMarker;\n            record.mState = CSMWorld::RecordBase::State_BaseOnly;\n            mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Door);\n        }\n    }\n}\n\nbool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)\n{\n    if (!mReader)\n        throw std::logic_error (\"can't continue loading, because no load has been started\");\n\n    if (!mReader->hasMoreRecs())\n    {\n        if (mBase)\n        {\n            // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading.\n            // We don't store non-base reader, because everything going into modified will be\n            // fully loaded during the initial loading process.\n            std::shared_ptr<ESM::ESMReader> ptr(mReader);\n            mReaders.push_back(ptr);\n        }\n        else\n            delete mReader;\n\n        mReader = nullptr;\n\n        mDialogue = nullptr;\n\n        loadFallbackEntries();\n\n        return true;\n    }\n\n    ESM::NAME n = mReader->getRecName();\n    mReader->getRecHeader();\n\n    bool unhandledRecord = false;\n\n    switch (n.intval)\n    {\n        case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break;\n        case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break;\n        case ESM::REC_SKIL: mSkills.load (*mReader, mBase); break;\n        case ESM::REC_CLAS: mClasses.load (*mReader, mBase); break;\n        case ESM::REC_FACT: mFactions.load (*mReader, mBase); break;\n        case ESM::REC_RACE: mRaces.load (*mReader, mBase); break;\n        case ESM::REC_SOUN: mSounds.load (*mReader, mBase); break;\n        case ESM::REC_SCPT: mScripts.load (*mReader, mBase); break;\n        case ESM::REC_REGN: mRegions.load (*mReader, mBase); break;\n        case ESM::REC_BSGN: mBirthsigns.load (*mReader, mBase); break;\n        case ESM::REC_SPEL: mSpells.load (*mReader, mBase); break;\n        case ESM::REC_ENCH: mEnchantments.load (*mReader, mBase); break;\n        case ESM::REC_BODY: mBodyParts.load (*mReader, mBase); break;\n        case ESM::REC_SNDG: mSoundGens.load (*mReader, mBase); break;\n        case ESM::REC_MGEF: mMagicEffects.load (*mReader, mBase); break;\n        case ESM::REC_PGRD: mPathgrids.load (*mReader, mBase); break;\n        case ESM::REC_SSCR: mStartScripts.load (*mReader, mBase); break;\n\n        case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break;\n\n        case ESM::REC_LAND: mLand.load(*mReader, mBase); break;\n\n        case ESM::REC_CELL:\n        {\n            int index = mCells.load (*mReader, mBase);\n            if (index < 0 || index >= mCells.getSize())\n            {\n                // log an error and continue loading the refs to the last loaded cell\n                CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_None);\n                messages.add (id, \"Logic error: cell index out of bounds\", \"\", CSMDoc::Message::Severity_Error);\n                index = mCells.getSize()-1;\n            }\n            std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (index));\n            mRefs.load (*mReader, index, mBase, mRefLoadCache[cellId], messages);\n            break;\n        }\n\n        case ESM::REC_ACTI: mReferenceables.load (*mReader, mBase, UniversalId::Type_Activator); break;\n        case ESM::REC_ALCH: mReferenceables.load (*mReader, mBase, UniversalId::Type_Potion); break;\n        case ESM::REC_APPA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Apparatus); break;\n        case ESM::REC_ARMO: mReferenceables.load (*mReader, mBase, UniversalId::Type_Armor); break;\n        case ESM::REC_BOOK: mReferenceables.load (*mReader, mBase, UniversalId::Type_Book); break;\n        case ESM::REC_CLOT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Clothing); break;\n        case ESM::REC_CONT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Container); break;\n        case ESM::REC_CREA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Creature); break;\n        case ESM::REC_DOOR: mReferenceables.load (*mReader, mBase, UniversalId::Type_Door); break;\n        case ESM::REC_INGR: mReferenceables.load (*mReader, mBase, UniversalId::Type_Ingredient); break;\n        case ESM::REC_LEVC:\n            mReferenceables.load (*mReader, mBase, UniversalId::Type_CreatureLevelledList); break;\n        case ESM::REC_LEVI:\n            mReferenceables.load (*mReader, mBase, UniversalId::Type_ItemLevelledList); break;\n        case ESM::REC_LIGH: mReferenceables.load (*mReader, mBase, UniversalId::Type_Light); break;\n        case ESM::REC_LOCK: mReferenceables.load (*mReader, mBase, UniversalId::Type_Lockpick); break;\n        case ESM::REC_MISC:\n            mReferenceables.load (*mReader, mBase, UniversalId::Type_Miscellaneous); break;\n        case ESM::REC_NPC_: mReferenceables.load (*mReader, mBase, UniversalId::Type_Npc); break;\n        case ESM::REC_PROB: mReferenceables.load (*mReader, mBase, UniversalId::Type_Probe); break;\n        case ESM::REC_REPA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Repair); break;\n        case ESM::REC_STAT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Static); break;\n        case ESM::REC_WEAP: mReferenceables.load (*mReader, mBase, UniversalId::Type_Weapon); break;\n\n        case ESM::REC_DIAL:\n        {\n            ESM::Dialogue record;\n            bool isDeleted = false;\n\n            record.load (*mReader, isDeleted);\n\n            if (isDeleted)\n            {\n                // record vector can be shuffled around which would make pointer to record invalid\n                mDialogue = nullptr;\n\n                if (mJournals.tryDelete (record.mId))\n                {\n                    mJournalInfos.removeDialogueInfos(record.mId);\n                }\n                else if (mTopics.tryDelete (record.mId))\n                {\n                    mTopicInfos.removeDialogueInfos(record.mId);\n                }\n                else\n                {\n                    messages.add (UniversalId::Type_None,\n                        \"Trying to delete dialogue record \" + record.mId + \" which does not exist\",\n                        \"\", CSMDoc::Message::Severity_Warning);\n                }\n            }\n            else\n            {\n                if (record.mType == ESM::Dialogue::Journal)\n                {\n                    mJournals.load (record, mBase);\n                    mDialogue = &mJournals.getRecord (record.mId).get();\n                }\n                else\n                {\n                    mTopics.load (record, mBase);\n                    mDialogue = &mTopics.getRecord (record.mId).get();\n                }\n            }\n\n            break;\n        }\n\n        case ESM::REC_INFO:\n        {\n            if (!mDialogue)\n            {\n                messages.add (UniversalId::Type_None,\n                    \"Found info record not following a dialogue record\", \"\", CSMDoc::Message::Severity_Error);\n\n                mReader->skipRecord();\n                break;\n            }\n\n            if (mDialogue->mType==ESM::Dialogue::Journal)\n                mJournalInfos.load (*mReader, mBase, *mDialogue);\n            else\n                mTopicInfos.load (*mReader, mBase, *mDialogue);\n\n            break;\n        }\n\n        case ESM::REC_FILT:\n\n            if (!mProject)\n            {\n                unhandledRecord = true;\n                break;\n            }\n\n            mFilters.load (*mReader, mBase);\n            break;\n\n        case ESM::REC_DBGP:\n\n            if (!mProject)\n            {\n                unhandledRecord = true;\n                break;\n            }\n\n            mDebugProfiles.load (*mReader, mBase);\n            break;\n\n        default:\n\n            unhandledRecord = true;\n    }\n\n    if (unhandledRecord)\n    {\n        messages.add (UniversalId::Type_None, \"Unsupported record type: \" + n.toString(), \"\",\n            CSMDoc::Message::Severity_Error);\n\n        mReader->skipRecord();\n    }\n\n    return false;\n}\n\nbool CSMWorld::Data::hasId (const std::string& id) const\n{\n    return\n        getGlobals().searchId (id)!=-1 ||\n        getGmsts().searchId (id)!=-1 ||\n        getSkills().searchId (id)!=-1 ||\n        getClasses().searchId (id)!=-1 ||\n        getFactions().searchId (id)!=-1 ||\n        getRaces().searchId (id)!=-1 ||\n        getSounds().searchId (id)!=-1 ||\n        getScripts().searchId (id)!=-1 ||\n        getRegions().searchId (id)!=-1 ||\n        getBirthsigns().searchId (id)!=-1 ||\n        getSpells().searchId (id)!=-1 ||\n        getTopics().searchId (id)!=-1 ||\n        getJournals().searchId (id)!=-1 ||\n        getCells().searchId (id)!=-1 ||\n        getEnchantments().searchId (id)!=-1 ||\n        getBodyParts().searchId (id)!=-1 ||\n        getSoundGens().searchId (id)!=-1 ||\n        getMagicEffects().searchId (id)!=-1 ||\n        getReferenceables().searchId (id)!=-1;\n}\n\nint CSMWorld::Data::count (RecordBase::State state) const\n{\n    return\n        count (state, mGlobals) +\n        count (state, mGmsts) +\n        count (state, mSkills) +\n        count (state, mClasses) +\n        count (state, mFactions) +\n        count (state, mRaces) +\n        count (state, mSounds) +\n        count (state, mScripts) +\n        count (state, mRegions) +\n        count (state, mBirthsigns) +\n        count (state, mSpells) +\n        count (state, mCells) +\n        count (state, mEnchantments) +\n        count (state, mBodyParts) +\n        count (state, mLand) +\n        count (state, mLandTextures) +\n        count (state, mSoundGens) +\n        count (state, mMagicEffects) +\n        count (state, mReferenceables) +\n        count (state, mPathgrids);\n}\n\nstd::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const\n{\n    std::vector<std::string> ids;\n\n    appendIds (ids, mGlobals, listDeleted);\n    appendIds (ids, mGmsts, listDeleted);\n    appendIds (ids, mClasses, listDeleted);\n    appendIds (ids, mFactions, listDeleted);\n    appendIds (ids, mRaces, listDeleted);\n    appendIds (ids, mSounds, listDeleted);\n    appendIds (ids, mScripts, listDeleted);\n    appendIds (ids, mRegions, listDeleted);\n    appendIds (ids, mBirthsigns, listDeleted);\n    appendIds (ids, mSpells, listDeleted);\n    appendIds (ids, mTopics, listDeleted);\n    appendIds (ids, mJournals, listDeleted);\n    appendIds (ids, mCells, listDeleted);\n    appendIds (ids, mEnchantments, listDeleted);\n    appendIds (ids, mBodyParts, listDeleted);\n    appendIds (ids, mSoundGens, listDeleted);\n    appendIds (ids, mMagicEffects, listDeleted);\n    appendIds (ids, mReferenceables, listDeleted);\n\n    std::sort (ids.begin(), ids.end());\n\n    return ids;\n}\n\nvoid CSMWorld::Data::assetsChanged()\n{\n    mVFS.get()->reset();\n    VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths, !mFsStrict), mArchives, true);\n\n    const UniversalId assetTableIds[] = {\n        UniversalId::Type_Meshes,\n        UniversalId::Type_Icons,\n        UniversalId::Type_Musics,\n        UniversalId::Type_SoundsRes,\n        UniversalId::Type_Textures,\n        UniversalId::Type_Videos\n    };\n\n    size_t numAssetTables = sizeof(assetTableIds) / sizeof(UniversalId);\n\n    for (size_t i = 0; i < numAssetTables; ++i)\n    {\n        ResourceTable* table = static_cast<ResourceTable*>(getTableModel(assetTableIds[i]));\n        table->beginReset();\n    }\n\n    // Trigger recreation\n    mResourcesManager.recreateResources();\n\n    for (size_t i = 0; i < numAssetTables; ++i)\n    {\n        ResourceTable* table = static_cast<ResourceTable*>(getTableModel(assetTableIds[i]));\n        table->endReset();\n    }\n\n    // Get rid of potentially old cached assets\n    mResourceSystem->clearCache();\n\n    emit assetTablesChanged();\n}\n\nvoid CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    if (topLeft.column()<=0)\n        emit idListChanged();\n}\n\nvoid CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end)\n{\n    emit idListChanged();\n}\n\nconst VFS::Manager* CSMWorld::Data::getVFS() const\n{\n    return mVFS.get();\n}\n"
  },
  {
    "path": "apps/opencs/model/world/data.hpp",
    "content": "#ifndef CSM_WOLRD_DATA_H\n#define CSM_WOLRD_DATA_H\n\n#include <map>\n#include <vector>\n\n#include <boost/filesystem/path.hpp>\n\n#include <QObject>\n#include <QModelIndex>\n\n#include <components/esm/loadglob.hpp>\n#include <components/esm/loadgmst.hpp>\n#include <components/esm/loadskil.hpp>\n#include <components/esm/loadclas.hpp>\n#include <components/esm/loadfact.hpp>\n#include <components/esm/loadrace.hpp>\n#include <components/esm/loadsoun.hpp>\n#include <components/esm/loadscpt.hpp>\n#include <components/esm/loadregn.hpp>\n#include <components/esm/loadbsgn.hpp>\n#include <components/esm/loadspel.hpp>\n#include <components/esm/loaddial.hpp>\n#include <components/esm/loadench.hpp>\n#include <components/esm/loadbody.hpp>\n#include <components/esm/loadsndg.hpp>\n#include <components/esm/loadmgef.hpp>\n#include <components/esm/loadsscr.hpp>\n#include <components/esm/debugprofile.hpp>\n#include <components/esm/filter.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n\n#include <components/files/multidircollection.hpp>\n#include <components/to_utf8/to_utf8.hpp>\n\n#include \"../doc/stage.hpp\"\n\n#include \"actoradapter.hpp\"\n#include \"idcollection.hpp\"\n#include \"nestedidcollection.hpp\"\n#include \"universalid.hpp\"\n#include \"cell.hpp\"\n#include \"land.hpp\"\n#include \"landtexture.hpp\"\n#include \"refidcollection.hpp\"\n#include \"refcollection.hpp\"\n#include \"infocollection.hpp\"\n#include \"nestedinfocollection.hpp\"\n#include \"pathgrid.hpp\"\n#include \"resourcesmanager.hpp\"\n#include \"metadata.hpp\"\n#ifndef Q_MOC_RUN\n#include \"subcellcollection.hpp\"\n#endif\n\nclass QAbstractItemModel;\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace Fallback\n{\n    class Map;\n}\n\nnamespace ESM\n{\n    class ESMReader;\n    struct Dialogue;\n}\n\nnamespace CSMWorld\n{\n    class ResourcesManager;\n    class Resources;\n\n    class Data : public QObject\n    {\n            Q_OBJECT\n\n            ToUTF8::Utf8Encoder mEncoder;\n            IdCollection<ESM::Global> mGlobals;\n            IdCollection<ESM::GameSetting> mGmsts;\n            IdCollection<ESM::Skill> mSkills;\n            IdCollection<ESM::Class> mClasses;\n            NestedIdCollection<ESM::Faction> mFactions;\n            NestedIdCollection<ESM::Race> mRaces;\n            IdCollection<ESM::Sound> mSounds;\n            IdCollection<ESM::Script> mScripts;\n            NestedIdCollection<ESM::Region> mRegions;\n            NestedIdCollection<ESM::BirthSign> mBirthsigns;\n            NestedIdCollection<ESM::Spell> mSpells;\n            IdCollection<ESM::Dialogue> mTopics;\n            IdCollection<ESM::Dialogue> mJournals;\n            NestedIdCollection<ESM::Enchantment> mEnchantments;\n            IdCollection<ESM::BodyPart> mBodyParts;\n            IdCollection<ESM::MagicEffect> mMagicEffects;\n            SubCellCollection<Pathgrid> mPathgrids;\n            IdCollection<ESM::DebugProfile> mDebugProfiles;\n            IdCollection<ESM::SoundGenerator> mSoundGens;\n            IdCollection<ESM::StartScript> mStartScripts;\n            NestedInfoCollection mTopicInfos;\n            InfoCollection mJournalInfos;\n            NestedIdCollection<Cell> mCells;\n            IdCollection<LandTexture> mLandTextures;\n            IdCollection<Land> mLand;\n            RefIdCollection mReferenceables;\n            RefCollection mRefs;\n            IdCollection<ESM::Filter> mFilters;\n            Collection<MetaData> mMetaData;\n            std::unique_ptr<ActorAdapter> mActorAdapter;\n            std::vector<QAbstractItemModel *> mModels;\n            std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;\n            ESM::ESMReader *mReader;\n            const ESM::Dialogue *mDialogue; // last loaded dialogue\n            bool mBase;\n            bool mProject;\n            std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;\n            int mReaderIndex;\n\n            bool mFsStrict;\n            Files::PathContainer mDataPaths;\n            std::vector<std::string> mArchives;\n            std::unique_ptr<VFS::Manager> mVFS;\n            ResourcesManager mResourcesManager;\n            std::shared_ptr<Resource::ResourceSystem> mResourceSystem;\n\n            std::vector<std::shared_ptr<ESM::ESMReader> > mReaders;\n\n            std::map<std::string, int> mContentFileNames;\n\n            // not implemented\n            Data (const Data&);\n            Data& operator= (const Data&);\n\n            void addModel (QAbstractItemModel *model, UniversalId::Type type,\n                bool update = true);\n\n            static void appendIds (std::vector<std::string>& ids, const CollectionBase& collection,\n                bool listDeleted);\n            ///< Append all IDs from collection to \\a ids.\n\n            static int count (RecordBase::State state, const CollectionBase& collection);\n\n            void loadFallbackEntries();\n\n        public:\n\n            Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,\n                const std::vector<std::string>& archives, const boost::filesystem::path& resDir);\n\n            virtual ~Data();\n\n            const VFS::Manager* getVFS() const;\n\n            std::shared_ptr<Resource::ResourceSystem> getResourceSystem();\n\n            std::shared_ptr<const Resource::ResourceSystem> getResourceSystem() const;\n\n            const IdCollection<ESM::Global>& getGlobals() const;\n\n            IdCollection<ESM::Global>& getGlobals();\n\n            const IdCollection<ESM::GameSetting>& getGmsts() const;\n\n            IdCollection<ESM::GameSetting>& getGmsts();\n\n            const IdCollection<ESM::Skill>& getSkills() const;\n\n            IdCollection<ESM::Skill>& getSkills();\n\n            const IdCollection<ESM::Class>& getClasses() const;\n\n            IdCollection<ESM::Class>& getClasses();\n\n            const IdCollection<ESM::Faction>& getFactions() const;\n\n            IdCollection<ESM::Faction>& getFactions();\n\n            const IdCollection<ESM::Race>& getRaces() const;\n\n            IdCollection<ESM::Race>& getRaces();\n\n            const IdCollection<ESM::Sound>& getSounds() const;\n\n            IdCollection<ESM::Sound>& getSounds();\n\n            const IdCollection<ESM::Script>& getScripts() const;\n\n            IdCollection<ESM::Script>& getScripts();\n\n            const IdCollection<ESM::Region>& getRegions() const;\n\n            IdCollection<ESM::Region>& getRegions();\n\n            const IdCollection<ESM::BirthSign>& getBirthsigns() const;\n\n            IdCollection<ESM::BirthSign>& getBirthsigns();\n\n            const IdCollection<ESM::Spell>& getSpells() const;\n\n            IdCollection<ESM::Spell>& getSpells();\n\n            const IdCollection<ESM::Dialogue>& getTopics() const;\n\n            IdCollection<ESM::Dialogue>& getTopics();\n\n            const IdCollection<ESM::Dialogue>& getJournals() const;\n\n            IdCollection<ESM::Dialogue>& getJournals();\n\n            const InfoCollection& getTopicInfos() const;\n\n            InfoCollection& getTopicInfos();\n\n            const InfoCollection& getJournalInfos() const;\n\n            InfoCollection& getJournalInfos();\n\n            const IdCollection<Cell>& getCells() const;\n\n            IdCollection<Cell>& getCells();\n\n            const RefIdCollection& getReferenceables() const;\n\n            RefIdCollection& getReferenceables();\n\n            const RefCollection& getReferences() const;\n\n            RefCollection& getReferences();\n\n            const IdCollection<ESM::Filter>& getFilters() const;\n\n            IdCollection<ESM::Filter>& getFilters();\n\n            const IdCollection<ESM::Enchantment>& getEnchantments() const;\n\n            IdCollection<ESM::Enchantment>& getEnchantments();\n\n            const IdCollection<ESM::BodyPart>& getBodyParts() const;\n\n            IdCollection<ESM::BodyPart>& getBodyParts();\n\n            const IdCollection<ESM::DebugProfile>& getDebugProfiles() const;\n\n            IdCollection<ESM::DebugProfile>& getDebugProfiles();\n\n            const IdCollection<CSMWorld::Land>& getLand() const;\n\n            IdCollection<CSMWorld::Land>& getLand();\n\n            const IdCollection<CSMWorld::LandTexture>& getLandTextures() const;\n\n            IdCollection<CSMWorld::LandTexture>& getLandTextures();\n\n            const IdCollection<ESM::SoundGenerator>& getSoundGens() const;\n\n            IdCollection<ESM::SoundGenerator>& getSoundGens();\n\n            const IdCollection<ESM::MagicEffect>& getMagicEffects() const;\n\n            IdCollection<ESM::MagicEffect>& getMagicEffects();\n\n            const SubCellCollection<Pathgrid>& getPathgrids() const;\n\n            SubCellCollection<Pathgrid>& getPathgrids();\n\n            const IdCollection<ESM::StartScript>& getStartScripts() const;\n\n            IdCollection<ESM::StartScript>& getStartScripts();\n\n            /// Throws an exception, if \\a id does not match a resources list.\n            const Resources& getResources (const UniversalId& id) const;\n\n            const MetaData& getMetaData() const;\n\n            void setMetaData (const MetaData& metaData);\n\n            QAbstractItemModel *getTableModel (const UniversalId& id);\n            ///< If no table model is available for \\a id, an exception is thrown.\n            ///\n            /// \\note The returned table may either be the model for the ID itself or the model that\n            /// contains the record specified by the ID.\n\n            const ActorAdapter* getActorAdapter() const;\n\n            ActorAdapter* getActorAdapter();\n\n            void merge();\n            ///< Merge modified into base.\n\n            int startLoading (const boost::filesystem::path& path, bool base, bool project);\n            ///< Begin merging content of a file into base or modified.\n            ///\n            /// \\param project load project file instead of content file\n            ///\n            ///< \\return estimated number of records\n\n            bool continueLoading (CSMDoc::Messages& messages);\n            ///< \\return Finished?\n\n            bool hasId (const std::string& id) const;\n\n            std::vector<std::string> getIds (bool listDeleted = true) const;\n            ///< Return a sorted collection of all IDs that are not internal to the editor.\n            ///\n            /// \\param listDeleted include deleted record in the list\n\n            int count (RecordBase::State state) const;\n            ///< Return number of top-level records with the given \\a state.\n\n        signals:\n\n            void idListChanged();\n\n            void assetTablesChanged();\n\n        private slots:\n\n            void assetsChanged();\n\n            void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void rowsChanged (const QModelIndex& parent, int start, int end);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/defaultgmsts.cpp",
    "content": "#include \"defaultgmsts.hpp\"\n\n#include <limits>\n\nconst float FInf = std::numeric_limits<float>::infinity();\nconst float FEps = std::numeric_limits<float>::epsilon();\n\nconst int IMax = std::numeric_limits<int>::max();\nconst int IMin = std::numeric_limits<int>::min();\n\nconst char* CSMWorld::DefaultGmsts::Floats[CSMWorld::DefaultGmsts::FloatCount] =\n{\n    \"fAIFleeFleeMult\",\n    \"fAIFleeHealthMult\",\n    \"fAIMagicSpellMult\",\n    \"fAIMeleeArmorMult\",\n    \"fAIMeleeSummWeaponMult\",\n    \"fAIMeleeWeaponMult\",\n    \"fAIRangeMagicSpellMult\",\n    \"fAIRangeMeleeWeaponMult\",\n    \"fAlarmRadius\",\n    \"fAthleticsRunBonus\",\n    \"fAudioDefaultMaxDistance\",\n    \"fAudioDefaultMinDistance\",\n    \"fAudioMaxDistanceMult\",\n    \"fAudioMinDistanceMult\",\n    \"fAudioVoiceDefaultMaxDistance\",\n    \"fAudioVoiceDefaultMinDistance\",\n    \"fAutoPCSpellChance\",\n    \"fAutoSpellChance\",\n    \"fBargainOfferBase\",\n    \"fBargainOfferMulti\",\n    \"fBarterGoldResetDelay\",\n    \"fBaseRunMultiplier\",\n    \"fBlockStillBonus\",\n    \"fBribe1000Mod\",\n    \"fBribe100Mod\",\n    \"fBribe10Mod\",\n    \"fCombatAngleXY\",\n    \"fCombatAngleZ\",\n    \"fCombatArmorMinMult\",\n    \"fCombatBlockLeftAngle\",\n    \"fCombatBlockRightAngle\",\n    \"fCombatCriticalStrikeMult\",\n    \"fCombatDelayCreature\",\n    \"fCombatDelayNPC\",\n    \"fCombatDistance\",\n    \"fCombatDistanceWerewolfMod\",\n    \"fCombatForceSideAngle\",\n    \"fCombatInvisoMult\",\n    \"fCombatKODamageMult\",\n    \"fCombatTorsoSideAngle\",\n    \"fCombatTorsoStartPercent\",\n    \"fCombatTorsoStopPercent\",\n    \"fConstantEffectMult\",\n    \"fCorpseClearDelay\",\n    \"fCorpseRespawnDelay\",\n    \"fCrimeGoldDiscountMult\",\n    \"fCrimeGoldTurnInMult\",\n    \"fCrimeStealing\",\n    \"fDamageStrengthBase\",\n    \"fDamageStrengthMult\",\n    \"fDifficultyMult\",\n    \"fDiseaseXferChance\",\n    \"fDispAttacking\",\n    \"fDispBargainFailMod\",\n    \"fDispBargainSuccessMod\",\n    \"fDispCrimeMod\",\n    \"fDispDiseaseMod\",\n    \"fDispFactionMod\",\n    \"fDispFactionRankBase\",\n    \"fDispFactionRankMult\",\n    \"fDispositionMod\",\n    \"fDispPersonalityBase\",\n    \"fDispPersonalityMult\",\n    \"fDispPickPocketMod\",\n    \"fDispRaceMod\",\n    \"fDispStealing\",\n    \"fDispWeaponDrawn\",\n    \"fEffectCostMult\",\n    \"fElementalShieldMult\",\n    \"fEnchantmentChanceMult\",\n    \"fEnchantmentConstantChanceMult\",\n    \"fEnchantmentConstantDurationMult\",\n    \"fEnchantmentMult\",\n    \"fEnchantmentValueMult\",\n    \"fEncumberedMoveEffect\",\n    \"fEncumbranceStrMult\",\n    \"fEndFatigueMult\",\n    \"fFallAcroBase\",\n    \"fFallAcroMult\",\n    \"fFallDamageDistanceMin\",\n    \"fFallDistanceBase\",\n    \"fFallDistanceMult\",\n    \"fFatigueAttackBase\",\n    \"fFatigueAttackMult\",\n    \"fFatigueBase\",\n    \"fFatigueBlockBase\",\n    \"fFatigueBlockMult\",\n    \"fFatigueJumpBase\",\n    \"fFatigueJumpMult\",\n    \"fFatigueMult\",\n    \"fFatigueReturnBase\",\n    \"fFatigueReturnMult\",\n    \"fFatigueRunBase\",\n    \"fFatigueRunMult\",\n    \"fFatigueSneakBase\",\n    \"fFatigueSneakMult\",\n    \"fFatigueSpellBase\",\n    \"fFatigueSpellCostMult\",\n    \"fFatigueSpellMult\",\n    \"fFatigueSwimRunBase\",\n    \"fFatigueSwimRunMult\",\n    \"fFatigueSwimWalkBase\",\n    \"fFatigueSwimWalkMult\",\n    \"fFightDispMult\",\n    \"fFightDistanceMultiplier\",\n    \"fFightStealing\",\n    \"fFleeDistance\",\n    \"fGreetDistanceReset\",\n    \"fHandtoHandHealthPer\",\n    \"fHandToHandReach\",\n    \"fHoldBreathEndMult\",\n    \"fHoldBreathTime\",\n    \"fIdleChanceMultiplier\",\n    \"fIngredientMult\",\n    \"fInteriorHeadTrackMult\",\n    \"fJumpAcrobaticsBase\",\n    \"fJumpAcroMultiplier\",\n    \"fJumpEncumbranceBase\",\n    \"fJumpEncumbranceMultiplier\",\n    \"fJumpMoveBase\",\n    \"fJumpMoveMult\",\n    \"fJumpRunMultiplier\",\n    \"fKnockDownMult\",\n    \"fLevelMod\",\n    \"fLevelUpHealthEndMult\",\n    \"fLightMaxMod\",\n    \"fLuckMod\",\n    \"fMagesGuildTravel\",\n    \"fMagicCreatureCastDelay\",\n    \"fMagicDetectRefreshRate\",\n    \"fMagicItemConstantMult\",\n    \"fMagicItemCostMult\",\n    \"fMagicItemOnceMult\",\n    \"fMagicItemPriceMult\",\n    \"fMagicItemRechargePerSecond\",\n    \"fMagicItemStrikeMult\",\n    \"fMagicItemUsedMult\",\n    \"fMagicStartIconBlink\",\n    \"fMagicSunBlockedMult\",\n    \"fMajorSkillBonus\",\n    \"fMaxFlySpeed\",\n    \"fMaxHandToHandMult\",\n    \"fMaxHeadTrackDistance\",\n    \"fMaxWalkSpeed\",\n    \"fMaxWalkSpeedCreature\",\n    \"fMedMaxMod\",\n    \"fMessageTimePerChar\",\n    \"fMinFlySpeed\",\n    \"fMinHandToHandMult\",\n    \"fMinorSkillBonus\",\n    \"fMinWalkSpeed\",\n    \"fMinWalkSpeedCreature\",\n    \"fMiscSkillBonus\",\n    \"fNPCbaseMagickaMult\",\n    \"fNPCHealthBarFade\",\n    \"fNPCHealthBarTime\",\n    \"fPCbaseMagickaMult\",\n    \"fPerDieRollMult\",\n    \"fPersonalityMod\",\n    \"fPerTempMult\",\n    \"fPickLockMult\",\n    \"fPickPocketMod\",\n    \"fPotionMinUsefulDuration\",\n    \"fPotionStrengthMult\",\n    \"fPotionT1DurMult\",\n    \"fPotionT1MagMult\",\n    \"fPotionT4BaseStrengthMult\",\n    \"fPotionT4EquipStrengthMult\",\n    \"fProjectileMaxSpeed\",\n    \"fProjectileMinSpeed\",\n    \"fProjectileThrownStoreChance\",\n    \"fRepairAmountMult\",\n    \"fRepairMult\",\n    \"fReputationMod\",\n    \"fRestMagicMult\",\n    \"fSeriousWoundMult\",\n    \"fSleepRandMod\",\n    \"fSleepRestMod\",\n    \"fSneakBootMult\",\n    \"fSneakDistanceBase\",\n    \"fSneakDistanceMultiplier\",\n    \"fSneakNoViewMult\",\n    \"fSneakSkillMult\",\n    \"fSneakSpeedMultiplier\",\n    \"fSneakUseDelay\",\n    \"fSneakUseDist\",\n    \"fSneakViewMult\",\n    \"fSoulGemMult\",\n    \"fSpecialSkillBonus\",\n    \"fSpellMakingValueMult\",\n    \"fSpellPriceMult\",\n    \"fSpellValueMult\",\n    \"fStromWalkMult\",\n    \"fStromWindSpeed\",\n    \"fSuffocationDamage\",\n    \"fSwimHeightScale\",\n    \"fSwimRunAthleticsMult\",\n    \"fSwimRunBase\",\n    \"fSwimWalkAthleticsMult\",\n    \"fSwimWalkBase\",\n    \"fSwingBlockBase\",\n    \"fSwingBlockMult\",\n    \"fTargetSpellMaxSpeed\",\n    \"fThrownWeaponMaxSpeed\",\n    \"fThrownWeaponMinSpeed\",\n    \"fTrapCostMult\",\n    \"fTravelMult\",\n    \"fTravelTimeMult\",\n    \"fUnarmoredBase1\",\n    \"fUnarmoredBase2\",\n    \"fVanityDelay\",\n    \"fVoiceIdleOdds\",\n    \"fWaterReflectUpdateAlways\",\n    \"fWaterReflectUpdateSeldom\",\n    \"fWeaponDamageMult\",\n    \"fWeaponFatigueBlockMult\",\n    \"fWeaponFatigueMult\",\n    \"fWereWolfAcrobatics\",\n    \"fWereWolfAgility\",\n    \"fWereWolfAlchemy\",\n    \"fWereWolfAlteration\",\n    \"fWereWolfArmorer\",\n    \"fWereWolfAthletics\",\n    \"fWereWolfAxe\",\n    \"fWereWolfBlock\",\n    \"fWereWolfBluntWeapon\",\n    \"fWereWolfConjuration\",\n    \"fWereWolfDestruction\",\n    \"fWereWolfEnchant\",\n    \"fWereWolfEndurance\",\n    \"fWereWolfFatigue\",\n    \"fWereWolfHandtoHand\",\n    \"fWereWolfHealth\",\n    \"fWereWolfHeavyArmor\",\n    \"fWereWolfIllusion\",\n    \"fWereWolfIntellegence\",\n    \"fWereWolfLightArmor\",\n    \"fWereWolfLongBlade\",\n    \"fWereWolfLuck\",\n    \"fWereWolfMagicka\",\n    \"fWereWolfMarksman\",\n    \"fWereWolfMediumArmor\",\n    \"fWereWolfMerchantile\",\n    \"fWereWolfMysticism\",\n    \"fWereWolfPersonality\",\n    \"fWereWolfRestoration\",\n    \"fWereWolfRunMult\",\n    \"fWereWolfSecurity\",\n    \"fWereWolfShortBlade\",\n    \"fWereWolfSilverWeaponDamageMult\",\n    \"fWereWolfSneak\",\n    \"fWereWolfSpear\",\n    \"fWereWolfSpeechcraft\",\n    \"fWereWolfSpeed\",\n    \"fWereWolfStrength\",\n    \"fWereWolfUnarmored\",\n    \"fWereWolfWillPower\",\n    \"fWortChanceValue\"\n};\n\nconst char * CSMWorld::DefaultGmsts::Ints[CSMWorld::DefaultGmsts::IntCount] =\n{\n    \"i1stPersonSneakDelta\",\n    \"iAlarmAttack\",\n    \"iAlarmKilling\",\n    \"iAlarmPickPocket\",\n    \"iAlarmStealing\",\n    \"iAlarmTresspass\",\n    \"iAlchemyMod\",\n    \"iAutoPCSpellMax\",\n    \"iAutoRepFacMod\",\n    \"iAutoRepLevMod\",\n    \"iAutoSpellAlterationMax\",\n    \"iAutoSpellAttSkillMin\",\n    \"iAutoSpellConjurationMax\",\n    \"iAutoSpellDestructionMax\",\n    \"iAutoSpellIllusionMax\",\n    \"iAutoSpellMysticismMax\",\n    \"iAutoSpellRestorationMax\",\n    \"iAutoSpellTimesCanCast\",\n    \"iBarterFailDisposition\",\n    \"iBarterSuccessDisposition\",\n    \"iBaseArmorSkill\",\n    \"iBlockMaxChance\",\n    \"iBlockMinChance\",\n    \"iBootsWeight\",\n    \"iCrimeAttack\",\n    \"iCrimeKilling\",\n    \"iCrimePickPocket\",\n    \"iCrimeThreshold\",\n    \"iCrimeThresholdMultiplier\",\n    \"iCrimeTresspass\",\n    \"iCuirassWeight\",\n    \"iDaysinPrisonMod\",\n    \"iDispAttackMod\",\n    \"iDispKilling\",\n    \"iDispTresspass\",\n    \"iFightAlarmMult\",\n    \"iFightAttack\",\n    \"iFightAttacking\",\n    \"iFightDistanceBase\",\n    \"iFightKilling\",\n    \"iFightPickpocket\",\n    \"iFightTrespass\",\n    \"iFlee\",\n    \"iGauntletWeight\",\n    \"iGreavesWeight\",\n    \"iGreetDistanceMultiplier\",\n    \"iGreetDuration\",\n    \"iHelmWeight\",\n    \"iKnockDownOddsBase\",\n    \"iKnockDownOddsMult\",\n    \"iLevelUp01Mult\",\n    \"iLevelUp02Mult\",\n    \"iLevelUp03Mult\",\n    \"iLevelUp04Mult\",\n    \"iLevelUp05Mult\",\n    \"iLevelUp06Mult\",\n    \"iLevelUp07Mult\",\n    \"iLevelUp08Mult\",\n    \"iLevelUp09Mult\",\n    \"iLevelUp10Mult\",\n    \"iLevelupMajorMult\",\n    \"iLevelupMajorMultAttribute\",\n    \"iLevelupMinorMult\",\n    \"iLevelupMinorMultAttribute\",\n    \"iLevelupMiscMultAttriubte\",\n    \"iLevelupSpecialization\",\n    \"iLevelupTotal\",\n    \"iMagicItemChargeConst\",\n    \"iMagicItemChargeOnce\",\n    \"iMagicItemChargeStrike\",\n    \"iMagicItemChargeUse\",\n    \"iMaxActivateDist\",\n    \"iMaxInfoDist\",\n    \"iMonthsToRespawn\",\n    \"iNumberCreatures\",\n    \"iPauldronWeight\",\n    \"iPerMinChance\",\n    \"iPerMinChange\",\n    \"iPickMaxChance\",\n    \"iPickMinChance\",\n    \"iShieldWeight\",\n    \"iSoulAmountForConstantEffect\",\n    \"iTrainingMod\",\n    \"iVoiceAttackOdds\",\n    \"iVoiceHitOdds\",\n    \"iWereWolfBounty\",\n    \"iWereWolfFightMod\",\n    \"iWereWolfFleeMod\",\n    \"iWereWolfLevelToAttack\"\n};\n\nconst char * CSMWorld::DefaultGmsts::Strings[CSMWorld::DefaultGmsts::StringCount] =\n{\n    \"s3dAudio\",\n    \"s3dHardware\",\n    \"s3dSoftware\",\n    \"sAbsorb\",\n    \"sAcrobat\",\n    \"sActivate\",\n    \"sActivateXbox\",\n    \"sActorInCombat\",\n    \"sAdmire\",\n    \"sAdmireFail\",\n    \"sAdmireSuccess\",\n    \"sAgent\",\n    \"sAgiDesc\",\n    \"sAIDistance\",\n    \"sAlembic\",\n    \"sAllTab\",\n    \"sAlways\",\n    \"sAlways_Run\",\n    \"sand\",\n    \"sApparatus\",\n    \"sApparelTab\",\n    \"sArcher\",\n    \"sArea\",\n    \"sAreaDes\",\n    \"sArmor\",\n    \"sArmorRating\",\n    \"sAsk\",\n    \"sAssassin\",\n    \"sAt\",\n    \"sAttack\",\n    \"sAttributeAgility\",\n    \"sAttributeEndurance\",\n    \"sAttributeIntelligence\",\n    \"sAttributeListTitle\",\n    \"sAttributeLuck\",\n    \"sAttributePersonality\",\n    \"sAttributesMenu1\",\n    \"sAttributeSpeed\",\n    \"sAttributeStrength\",\n    \"sAttributeWillpower\",\n    \"sAudio\",\n    \"sAuto_Run\",\n    \"sBack\",\n    \"sBackspace\",\n    \"sBackXbox\",\n    \"sBarbarian\",\n    \"sBard\",\n    \"sBarter\",\n    \"sBarterDialog1\",\n    \"sBarterDialog10\",\n    \"sBarterDialog11\",\n    \"sBarterDialog12\",\n    \"sBarterDialog2\",\n    \"sBarterDialog3\",\n    \"sBarterDialog4\",\n    \"sBarterDialog5\",\n    \"sBarterDialog6\",\n    \"sBarterDialog7\",\n    \"sBarterDialog8\",\n    \"sBarterDialog9\",\n    \"sBattlemage\",\n    \"sBestAttack\",\n    \"sBirthSign\",\n    \"sBirthsignmenu1\",\n    \"sBirthsignmenu2\",\n    \"sBlocks\",\n    \"sBonusSkillTitle\",\n    \"sBookPageOne\",\n    \"sBookPageTwo\",\n    \"sBookSkillMessage\",\n    \"sBounty\",\n    \"sBreath\",\n    \"sBribe 10 Gold\",\n    \"sBribe 100 Gold\",\n    \"sBribe 1000 Gold\",\n    \"sBribeFail\",\n    \"sBribeSuccess\",\n    \"sBuy\",\n    \"sBye\",\n    \"sCalcinator\",\n    \"sCancel\",\n    \"sCantEquipWeapWarning\",\n    \"sCastCost\",\n    \"sCaughtStealingMessage\",\n    \"sCenter\",\n    \"sChangedMastersMsg\",\n    \"sCharges\",\n    \"sChooseClassMenu1\",\n    \"sChooseClassMenu2\",\n    \"sChooseClassMenu3\",\n    \"sChooseClassMenu4\",\n    \"sChop\",\n    \"sClass\",\n    \"sClassChoiceMenu1\",\n    \"sClassChoiceMenu2\",\n    \"sClassChoiceMenu3\",\n    \"sClose\",\n    \"sCompanionShare\",\n    \"sCompanionWarningButtonOne\",\n    \"sCompanionWarningButtonTwo\",\n    \"sCompanionWarningMessage\",\n    \"sCondition\",\n    \"sConsoleTitle\",\n    \"sContainer\",\n    \"sContentsMessage1\",\n    \"sContentsMessage2\",\n    \"sContentsMessage3\",\n    \"sControlerVibration\",\n    \"sControls\",\n    \"sControlsMenu1\",\n    \"sControlsMenu2\",\n    \"sControlsMenu3\",\n    \"sControlsMenu4\",\n    \"sControlsMenu5\",\n    \"sControlsMenu6\",\n    \"sCostChance\",\n    \"sCostCharge\",\n    \"sCreate\",\n    \"sCreateClassMenu1\",\n    \"sCreateClassMenu2\",\n    \"sCreateClassMenu3\",\n    \"sCreateClassMenuHelp1\",\n    \"sCreateClassMenuHelp2\",\n    \"sCreateClassMenuWarning\",\n    \"sCreatedEffects\",\n    \"sCrimeHelp\",\n    \"sCrimeMessage\",\n    \"sCrouch_Sneak\",\n    \"sCrouchXbox\",\n    \"sCrusader\",\n    \"sCursorOff\",\n    \"sCustom\",\n    \"sCustomClassName\",\n    \"sDamage\",\n    \"sDark_Gamma\",\n    \"sDay\",\n    \"sDefaultCellname\",\n    \"sDelete\",\n    \"sDeleteGame\",\n    \"sDeleteNote\",\n    \"sDeleteSpell\",\n    \"sDeleteSpellError\",\n    \"sDetail_Level\",\n    \"sDialogMenu1\",\n    \"sDialogText1Xbox\",\n    \"sDialogText2Xbox\",\n    \"sDialogText3Xbox\",\n    \"sDifficulty\",\n    \"sDisposeCorpseFail\",\n    \"sDisposeofCorpse\",\n    \"sDone\",\n    \"sDoYouWantTo\",\n    \"sDrain\",\n    \"sDrop\",\n    \"sDuration\",\n    \"sDurationDes\",\n    \"sEasy\",\n    \"sEditNote\",\n    \"sEffectAbsorbAttribute\",\n    \"sEffectAbsorbFatigue\",\n    \"sEffectAbsorbHealth\",\n    \"sEffectAbsorbSkill\",\n    \"sEffectAbsorbSpellPoints\",\n    \"sEffectAlmsiviIntervention\",\n    \"sEffectBlind\",\n    \"sEffectBoundBattleAxe\",\n    \"sEffectBoundBoots\",\n    \"sEffectBoundCuirass\",\n    \"sEffectBoundDagger\",\n    \"sEffectBoundGloves\",\n    \"sEffectBoundHelm\",\n    \"sEffectBoundLongbow\",\n    \"sEffectBoundLongsword\",\n    \"sEffectBoundMace\",\n    \"sEffectBoundShield\",\n    \"sEffectBoundSpear\",\n    \"sEffectBurden\",\n    \"sEffectCalmCreature\",\n    \"sEffectCalmHumanoid\",\n    \"sEffectChameleon\",\n    \"sEffectCharm\",\n    \"sEffectCommandCreatures\",\n    \"sEffectCommandHumanoids\",\n    \"sEffectCorpus\",\n    \"sEffectCureBlightDisease\",\n    \"sEffectCureCommonDisease\",\n    \"sEffectCureCorprusDisease\",\n    \"sEffectCureParalyzation\",\n    \"sEffectCurePoison\",\n    \"sEffectDamageAttribute\",\n    \"sEffectDamageFatigue\",\n    \"sEffectDamageHealth\",\n    \"sEffectDamageMagicka\",\n    \"sEffectDamageSkill\",\n    \"sEffectDemoralizeCreature\",\n    \"sEffectDemoralizeHumanoid\",\n    \"sEffectDetectAnimal\",\n    \"sEffectDetectEnchantment\",\n    \"sEffectDetectKey\",\n    \"sEffectDisintegrateArmor\",\n    \"sEffectDisintegrateWeapon\",\n    \"sEffectDispel\",\n    \"sEffectDivineIntervention\",\n    \"sEffectDrainAttribute\",\n    \"sEffectDrainFatigue\",\n    \"sEffectDrainHealth\",\n    \"sEffectDrainSkill\",\n    \"sEffectDrainSpellpoints\",\n    \"sEffectExtraSpell\",\n    \"sEffectFeather\",\n    \"sEffectFireDamage\",\n    \"sEffectFireShield\",\n    \"sEffectFortifyAttackBonus\",\n    \"sEffectFortifyAttribute\",\n    \"sEffectFortifyFatigue\",\n    \"sEffectFortifyHealth\",\n    \"sEffectFortifyMagickaMultiplier\",\n    \"sEffectFortifySkill\",\n    \"sEffectFortifySpellpoints\",\n    \"sEffectFrenzyCreature\",\n    \"sEffectFrenzyHumanoid\",\n    \"sEffectFrostDamage\",\n    \"sEffectFrostShield\",\n    \"sEffectInvisibility\",\n    \"sEffectJump\",\n    \"sEffectLevitate\",\n    \"sEffectLight\",\n    \"sEffectLightningShield\",\n    \"sEffectLock\",\n    \"sEffectMark\",\n    \"sEffectNightEye\",\n    \"sEffectOpen\",\n    \"sEffectParalyze\",\n    \"sEffectPoison\",\n    \"sEffectRallyCreature\",\n    \"sEffectRallyHumanoid\",\n    \"sEffectRecall\",\n    \"sEffectReflect\",\n    \"sEffectRemoveCurse\",\n    \"sEffectResistBlightDisease\",\n    \"sEffectResistCommonDisease\",\n    \"sEffectResistCorprusDisease\",\n    \"sEffectResistFire\",\n    \"sEffectResistFrost\",\n    \"sEffectResistMagicka\",\n    \"sEffectResistNormalWeapons\",\n    \"sEffectResistParalysis\",\n    \"sEffectResistPoison\",\n    \"sEffectResistShock\",\n    \"sEffectRestoreAttribute\",\n    \"sEffectRestoreFatigue\",\n    \"sEffectRestoreHealth\",\n    \"sEffectRestoreSkill\",\n    \"sEffectRestoreSpellPoints\",\n    \"sEffects\",\n    \"sEffectSanctuary\",\n    \"sEffectShield\",\n    \"sEffectShockDamage\",\n    \"sEffectSilence\",\n    \"sEffectSlowFall\",\n    \"sEffectSoultrap\",\n    \"sEffectSound\",\n    \"sEffectSpellAbsorption\",\n    \"sEffectStuntedMagicka\",\n    \"sEffectSummonAncestralGhost\",\n    \"sEffectSummonBonelord\",\n    \"sEffectSummonCenturionSphere\",\n    \"sEffectSummonClannfear\",\n    \"sEffectSummonCreature01\",\n    \"sEffectSummonCreature02\",\n    \"sEffectSummonCreature03\",\n    \"sEffectSummonCreature04\",\n    \"sEffectSummonCreature05\",\n    \"sEffectSummonDaedroth\",\n    \"sEffectSummonDremora\",\n    \"sEffectSummonFabricant\",\n    \"sEffectSummonFlameAtronach\",\n    \"sEffectSummonFrostAtronach\",\n    \"sEffectSummonGoldensaint\",\n    \"sEffectSummonGreaterBonewalker\",\n    \"sEffectSummonHunger\",\n    \"sEffectSummonLeastBonewalker\",\n    \"sEffectSummonScamp\",\n    \"sEffectSummonSkeletalMinion\",\n    \"sEffectSummonStormAtronach\",\n    \"sEffectSummonWingedTwilight\",\n    \"sEffectSunDamage\",\n    \"sEffectSwiftSwim\",\n    \"sEffectTelekinesis\",\n    \"sEffectTurnUndead\",\n    \"sEffectVampirism\",\n    \"sEffectWaterBreathing\",\n    \"sEffectWaterWalking\",\n    \"sEffectWeaknessToBlightDisease\",\n    \"sEffectWeaknessToCommonDisease\",\n    \"sEffectWeaknessToCorprusDisease\",\n    \"sEffectWeaknessToFire\",\n    \"sEffectWeaknessToFrost\",\n    \"sEffectWeaknessToMagicka\",\n    \"sEffectWeaknessToNormalWeapons\",\n    \"sEffectWeaknessToPoison\",\n    \"sEffectWeaknessToShock\",\n    \"sEnableJoystick\",\n    \"sEnchanting\",\n    \"sEnchantItems\",\n    \"sEnchantmentHelp1\",\n    \"sEnchantmentHelp10\",\n    \"sEnchantmentHelp2\",\n    \"sEnchantmentHelp3\",\n    \"sEnchantmentHelp4\",\n    \"sEnchantmentHelp5\",\n    \"sEnchantmentHelp6\",\n    \"sEnchantmentHelp7\",\n    \"sEnchantmentHelp8\",\n    \"sEnchantmentHelp9\",\n    \"sEnchantmentMenu1\",\n    \"sEnchantmentMenu10\",\n    \"sEnchantmentMenu11\",\n    \"sEnchantmentMenu12\",\n    \"sEnchantmentMenu2\",\n    \"sEnchantmentMenu3\",\n    \"sEnchantmentMenu4\",\n    \"sEnchantmentMenu5\",\n    \"sEnchantmentMenu6\",\n    \"sEnchantmentMenu7\",\n    \"sEnchantmentMenu8\",\n    \"sEnchantmentMenu9\",\n    \"sEncumbrance\",\n    \"sEndDesc\",\n    \"sEquip\",\n    \"sExitGame\",\n    \"sExpelled\",\n    \"sExpelledMessage\",\n    \"sFace\",\n    \"sFaction\",\n    \"sFar\",\n    \"sFast\",\n    \"sFatDesc\",\n    \"sFatigue\",\n    \"sFavoriteSkills\",\n    \"sfeet\",\n    \"sFileSize\",\n    \"sfootarea\",\n    \"sFootsteps\",\n    \"sfor\",\n    \"sFortify\",\n    \"sForward\",\n    \"sForwardXbox\",\n    \"sFull\",\n    \"sGame\",\n    \"sGameWithoutLauncherXbox\",\n    \"sGamma_Correction\",\n    \"sGeneralMastPlugMismatchMsg\",\n    \"sGold\",\n    \"sGoodbye\",\n    \"sGoverningAttribute\",\n    \"sgp\",\n    \"sHair\",\n    \"sHard\",\n    \"sHeal\",\n    \"sHealer\",\n    \"sHealth\",\n    \"sHealthDesc\",\n    \"sHealthPerHourOfRest\",\n    \"sHealthPerLevel\",\n    \"sHeavy\",\n    \"sHigh\",\n    \"sin\",\n    \"sInfo\",\n    \"sInfoRefusal\",\n    \"sIngredients\",\n    \"sInPrisonTitle\",\n    \"sInputMenu1\",\n    \"sIntDesc\",\n    \"sIntimidate\",\n    \"sIntimidateFail\",\n    \"sIntimidateSuccess\",\n    \"sInvalidSaveGameMsg\",\n    \"sInvalidSaveGameMsgXBOX\",\n    \"sInventory\",\n    \"sInventoryMenu1\",\n    \"sInventoryMessage1\",\n    \"sInventoryMessage2\",\n    \"sInventoryMessage3\",\n    \"sInventoryMessage4\",\n    \"sInventoryMessage5\",\n    \"sInventorySelectNoIngredients\",\n    \"sInventorySelectNoItems\",\n    \"sInventorySelectNoSoul\",\n    \"sItem\",\n    \"sItemCastConstant\",\n    \"sItemCastOnce\",\n    \"sItemCastWhenStrikes\",\n    \"sItemCastWhenUsed\",\n    \"sItemName\",\n    \"sJournal\",\n    \"sJournalCmd\",\n    \"sJournalEntry\",\n    \"sJournalXbox\",\n    \"sJoystickHatShort\",\n    \"sJoystickNotFound\",\n    \"sJoystickShort\",\n    \"sJump\",\n    \"sJumpXbox\",\n    \"sKeyName_00\",\n    \"sKeyName_01\",\n    \"sKeyName_02\",\n    \"sKeyName_03\",\n    \"sKeyName_04\",\n    \"sKeyName_05\",\n    \"sKeyName_06\",\n    \"sKeyName_07\",\n    \"sKeyName_08\",\n    \"sKeyName_09\",\n    \"sKeyName_0A\",\n    \"sKeyName_0B\",\n    \"sKeyName_0C\",\n    \"sKeyName_0D\",\n    \"sKeyName_0E\",\n    \"sKeyName_0F\",\n    \"sKeyName_10\",\n    \"sKeyName_11\",\n    \"sKeyName_12\",\n    \"sKeyName_13\",\n    \"sKeyName_14\",\n    \"sKeyName_15\",\n    \"sKeyName_16\",\n    \"sKeyName_17\",\n    \"sKeyName_18\",\n    \"sKeyName_19\",\n    \"sKeyName_1A\",\n    \"sKeyName_1B\",\n    \"sKeyName_1C\",\n    \"sKeyName_1D\",\n    \"sKeyName_1E\",\n    \"sKeyName_1F\",\n    \"sKeyName_20\",\n    \"sKeyName_21\",\n    \"sKeyName_22\",\n    \"sKeyName_23\",\n    \"sKeyName_24\",\n    \"sKeyName_25\",\n    \"sKeyName_26\",\n    \"sKeyName_27\",\n    \"sKeyName_28\",\n    \"sKeyName_29\",\n    \"sKeyName_2A\",\n    \"sKeyName_2B\",\n    \"sKeyName_2C\",\n    \"sKeyName_2D\",\n    \"sKeyName_2E\",\n    \"sKeyName_2F\",\n    \"sKeyName_30\",\n    \"sKeyName_31\",\n    \"sKeyName_32\",\n    \"sKeyName_33\",\n    \"sKeyName_34\",\n    \"sKeyName_35\",\n    \"sKeyName_36\",\n    \"sKeyName_37\",\n    \"sKeyName_38\",\n    \"sKeyName_39\",\n    \"sKeyName_3A\",\n    \"sKeyName_3B\",\n    \"sKeyName_3C\",\n    \"sKeyName_3D\",\n    \"sKeyName_3E\",\n    \"sKeyName_3F\",\n    \"sKeyName_40\",\n    \"sKeyName_41\",\n    \"sKeyName_42\",\n    \"sKeyName_43\",\n    \"sKeyName_44\",\n    \"sKeyName_45\",\n    \"sKeyName_46\",\n    \"sKeyName_47\",\n    \"sKeyName_48\",\n    \"sKeyName_49\",\n    \"sKeyName_4A\",\n    \"sKeyName_4B\",\n    \"sKeyName_4C\",\n    \"sKeyName_4D\",\n    \"sKeyName_4E\",\n    \"sKeyName_4F\",\n    \"sKeyName_50\",\n    \"sKeyName_51\",\n    \"sKeyName_52\",\n    \"sKeyName_53\",\n    \"sKeyName_54\",\n    \"sKeyName_55\",\n    \"sKeyName_56\",\n    \"sKeyName_57\",\n    \"sKeyName_58\",\n    \"sKeyName_59\",\n    \"sKeyName_5A\",\n    \"sKeyName_5B\",\n    \"sKeyName_5C\",\n    \"sKeyName_5D\",\n    \"sKeyName_5E\",\n    \"sKeyName_5F\",\n    \"sKeyName_60\",\n    \"sKeyName_61\",\n    \"sKeyName_62\",\n    \"sKeyName_63\",\n    \"sKeyName_64\",\n    \"sKeyName_65\",\n    \"sKeyName_66\",\n    \"sKeyName_67\",\n    \"sKeyName_68\",\n    \"sKeyName_69\",\n    \"sKeyName_6A\",\n    \"sKeyName_6B\",\n    \"sKeyName_6C\",\n    \"sKeyName_6D\",\n    \"sKeyName_6E\",\n    \"sKeyName_6F\",\n    \"sKeyName_70\",\n    \"sKeyName_71\",\n    \"sKeyName_72\",\n    \"sKeyName_73\",\n    \"sKeyName_74\",\n    \"sKeyName_75\",\n    \"sKeyName_76\",\n    \"sKeyName_77\",\n    \"sKeyName_78\",\n    \"sKeyName_79\",\n    \"sKeyName_7A\",\n    \"sKeyName_7B\",\n    \"sKeyName_7C\",\n    \"sKeyName_7D\",\n    \"sKeyName_7E\",\n    \"sKeyName_7F\",\n    \"sKeyName_80\",\n    \"sKeyName_81\",\n    \"sKeyName_82\",\n    \"sKeyName_83\",\n    \"sKeyName_84\",\n    \"sKeyName_85\",\n    \"sKeyName_86\",\n    \"sKeyName_87\",\n    \"sKeyName_88\",\n    \"sKeyName_89\",\n    \"sKeyName_8A\",\n    \"sKeyName_8B\",\n    \"sKeyName_8C\",\n    \"sKeyName_8D\",\n    \"sKeyName_8E\",\n    \"sKeyName_8F\",\n    \"sKeyName_90\",\n    \"sKeyName_91\",\n    \"sKeyName_92\",\n    \"sKeyName_93\",\n    \"sKeyName_94\",\n    \"sKeyName_95\",\n    \"sKeyName_96\",\n    \"sKeyName_97\",\n    \"sKeyName_98\",\n    \"sKeyName_99\",\n    \"sKeyName_9A\",\n    \"sKeyName_9B\",\n    \"sKeyName_9C\",\n    \"sKeyName_9D\",\n    \"sKeyName_9E\",\n    \"sKeyName_9F\",\n    \"sKeyName_A0\",\n    \"sKeyName_A1\",\n    \"sKeyName_A2\",\n    \"sKeyName_A3\",\n    \"sKeyName_A4\",\n    \"sKeyName_A5\",\n    \"sKeyName_A6\",\n    \"sKeyName_A7\",\n    \"sKeyName_A8\",\n    \"sKeyName_A9\",\n    \"sKeyName_AA\",\n    \"sKeyName_AB\",\n    \"sKeyName_AC\",\n    \"sKeyName_AD\",\n    \"sKeyName_AE\",\n    \"sKeyName_AF\",\n    \"sKeyName_B0\",\n    \"sKeyName_B1\",\n    \"sKeyName_B2\",\n    \"sKeyName_B3\",\n    \"sKeyName_B4\",\n    \"sKeyName_B5\",\n    \"sKeyName_B6\",\n    \"sKeyName_B7\",\n    \"sKeyName_B8\",\n    \"sKeyName_B9\",\n    \"sKeyName_BA\",\n    \"sKeyName_BB\",\n    \"sKeyName_BC\",\n    \"sKeyName_BD\",\n    \"sKeyName_BE\",\n    \"sKeyName_BF\",\n    \"sKeyName_C0\",\n    \"sKeyName_C1\",\n    \"sKeyName_C2\",\n    \"sKeyName_C3\",\n    \"sKeyName_C4\",\n    \"sKeyName_C5\",\n    \"sKeyName_C6\",\n    \"sKeyName_C7\",\n    \"sKeyName_C8\",\n    \"sKeyName_C9\",\n    \"sKeyName_CA\",\n    \"sKeyName_CB\",\n    \"sKeyName_CC\",\n    \"sKeyName_CD\",\n    \"sKeyName_CE\",\n    \"sKeyName_CF\",\n    \"sKeyName_D0\",\n    \"sKeyName_D1\",\n    \"sKeyName_D2\",\n    \"sKeyName_D3\",\n    \"sKeyName_D4\",\n    \"sKeyName_D5\",\n    \"sKeyName_D6\",\n    \"sKeyName_D7\",\n    \"sKeyName_D8\",\n    \"sKeyName_D9\",\n    \"sKeyName_DA\",\n    \"sKeyName_DB\",\n    \"sKeyName_DC\",\n    \"sKeyName_DD\",\n    \"sKeyName_DE\",\n    \"sKeyName_DF\",\n    \"sKeyName_E0\",\n    \"sKeyName_E1\",\n    \"sKeyName_E2\",\n    \"sKeyName_E3\",\n    \"sKeyName_E4\",\n    \"sKeyName_E5\",\n    \"sKeyName_E6\",\n    \"sKeyName_E7\",\n    \"sKeyName_E8\",\n    \"sKeyName_E9\",\n    \"sKeyName_EA\",\n    \"sKeyName_EB\",\n    \"sKeyName_EC\",\n    \"sKeyName_ED\",\n    \"sKeyName_EE\",\n    \"sKeyName_EF\",\n    \"sKeyName_F0\",\n    \"sKeyName_F1\",\n    \"sKeyName_F2\",\n    \"sKeyName_F3\",\n    \"sKeyName_F4\",\n    \"sKeyName_F5\",\n    \"sKeyName_F6\",\n    \"sKeyName_F7\",\n    \"sKeyName_F8\",\n    \"sKeyName_F9\",\n    \"sKeyName_FA\",\n    \"sKeyName_FB\",\n    \"sKeyName_FC\",\n    \"sKeyName_FD\",\n    \"sKeyName_FE\",\n    \"sKeyName_FF\",\n    \"sKeyUsed\",\n    \"sKilledEssential\",\n    \"sKnight\",\n    \"sLeft\",\n    \"sLess\",\n    \"sLevel\",\n    \"sLevelProgress\",\n    \"sLevels\",\n    \"sLevelUp\",\n    \"sLevelUpMenu1\",\n    \"sLevelUpMenu2\",\n    \"sLevelUpMenu3\",\n    \"sLevelUpMenu4\",\n    \"sLevelUpMsg\",\n    \"sLevitateDisabled\",\n    \"sLight\",\n    \"sLight_Gamma\",\n    \"sLoadFailedMessage\",\n    \"sLoadGame\",\n    \"sLoadingErrorsMsg\",\n    \"sLoadingMessage1\",\n    \"sLoadingMessage14\",\n    \"sLoadingMessage15\",\n    \"sLoadingMessage2\",\n    \"sLoadingMessage3\",\n    \"sLoadingMessage4\",\n    \"sLoadingMessage5\",\n    \"sLoadingMessage9\",\n    \"sLoadLastSaveMsg\",\n    \"sLocal\",\n    \"sLockFail\",\n    \"sLockImpossible\",\n    \"sLockLevel\",\n    \"sLockSuccess\",\n    \"sLookDownXbox\",\n    \"sLookUpXbox\",\n    \"sLow\",\n    \"sLucDesc\",\n    \"sMagDesc\",\n    \"sMage\",\n    \"sMagic\",\n    \"sMagicAncestralGhostID\",\n    \"sMagicBonelordID\",\n    \"sMagicBoundBattleAxeID\",\n    \"sMagicBoundBootsID\",\n    \"sMagicBoundCuirassID\",\n    \"sMagicBoundDaggerID\",\n    \"sMagicBoundHelmID\",\n    \"sMagicBoundLeftGauntletID\",\n    \"sMagicBoundLongbowID\",\n    \"sMagicBoundLongswordID\",\n    \"sMagicBoundMaceID\",\n    \"sMagicBoundRightGauntletID\",\n    \"sMagicBoundShieldID\",\n    \"sMagicBoundSpearID\",\n    \"sMagicCannotRecast\",\n    \"sMagicCenturionSphereID\",\n    \"sMagicClannfearID\",\n    \"sMagicContractDisease\",\n    \"sMagicCorprusWorsens\",\n    \"sMagicCreature01ID\",\n    \"sMagicCreature02ID\",\n    \"sMagicCreature03ID\",\n    \"sMagicCreature04ID\",\n    \"sMagicCreature05ID\",\n    \"sMagicDaedrothID\",\n    \"sMagicDremoraID\",\n    \"sMagicEffects\",\n    \"sMagicFabricantID\",\n    \"sMagicFlameAtronachID\",\n    \"sMagicFrostAtronachID\",\n    \"sMagicGoldenSaintID\",\n    \"sMagicGreaterBonewalkerID\",\n    \"sMagicHungerID\",\n    \"sMagicInsufficientCharge\",\n    \"sMagicInsufficientSP\",\n    \"sMagicInvalidEffect\",\n    \"sMagicInvalidTarget\",\n    \"sMagicItem\",\n    \"sMagicLeastBonewalkerID\",\n    \"sMagicLockSuccess\",\n    \"sMagicMenu\",\n    \"sMagicOpenSuccess\",\n    \"sMagicPCResisted\",\n    \"sMagicScampID\",\n    \"sMagicSelectTitle\",\n    \"sMagicSkeletalMinionID\",\n    \"sMagicSkillFail\",\n    \"sMagicStormAtronachID\",\n    \"sMagicTab\",\n    \"sMagicTargetResisted\",\n    \"sMagicTargetResistsWeapons\",\n    \"sMagicWingedTwilightID\",\n    \"sMagnitude\",\n    \"sMagnitudeDes\",\n    \"sMake Enchantment\",\n    \"sMap\",\n    \"sMaster\",\n    \"sMastPlugMismatchMsg\",\n    \"sMaximumSaveGameMessage\",\n    \"sMaxSale\",\n    \"sMedium\",\n    \"sMenu_Help_Delay\",\n    \"sMenu_Mode\",\n    \"sMenuModeXbox\",\n    \"sMenuNextXbox\",\n    \"sMenuPrevXbox\",\n    \"sMenus\",\n    \"sMessage1\",\n    \"sMessage2\",\n    \"sMessage3\",\n    \"sMessage4\",\n    \"sMessage5\",\n    \"sMessageQuestionAnswer1\",\n    \"sMessageQuestionAnswer2\",\n    \"sMessageQuestionAnswer3\",\n    \"sMiscTab\",\n    \"sMissingMastersMsg\",\n    \"sMonk\",\n    \"sMonthEveningstar\",\n    \"sMonthFirstseed\",\n    \"sMonthFrostfall\",\n    \"sMonthHeartfire\",\n    \"sMonthLastseed\",\n    \"sMonthMidyear\",\n    \"sMonthMorningstar\",\n    \"sMonthRainshand\",\n    \"sMonthSecondseed\",\n    \"sMonthSunsdawn\",\n    \"sMonthSunsdusk\",\n    \"sMonthSunsheight\",\n    \"sMore\",\n    \"sMortar\",\n    \"sMouse\",\n    \"sMouseFlip\",\n    \"sMouseWheelDownShort\",\n    \"sMouseWheelUpShort\",\n    \"sMove\",\n    \"sMoveDownXbox\",\n    \"sMoveUpXbox\",\n    \"sMusic\",\n    \"sName\",\n    \"sNameTitle\",\n    \"sNear\",\n    \"sNeedOneSkill\",\n    \"sNeedTwoSkills\",\n    \"sNewGame\",\n    \"sNext\",\n    \"sNextRank\",\n    \"sNextSpell\",\n    \"sNextSpellXbox\",\n    \"sNextWeapon\",\n    \"sNextWeaponXbox\",\n    \"sNightblade\",\n    \"sNo\",\n    \"sNoName\",\n    \"sNone\",\n    \"sNotifyMessage1\",\n    \"sNotifyMessage10\",\n    \"sNotifyMessage11\",\n    \"sNotifyMessage12\",\n    \"sNotifyMessage13\",\n    \"sNotifyMessage14\",\n    \"sNotifyMessage15\",\n    \"sNotifyMessage16\",\n    \"sNotifyMessage16_a\",\n    \"sNotifyMessage17\",\n    \"sNotifyMessage18\",\n    \"sNotifyMessage19\",\n    \"sNotifyMessage2\",\n    \"sNotifyMessage20\",\n    \"sNotifyMessage21\",\n    \"sNotifyMessage22\",\n    \"sNotifyMessage23\",\n    \"sNotifyMessage24\",\n    \"sNotifyMessage25\",\n    \"sNotifyMessage26\",\n    \"sNotifyMessage27\",\n    \"sNotifyMessage28\",\n    \"sNotifyMessage29\",\n    \"sNotifyMessage3\",\n    \"sNotifyMessage30\",\n    \"sNotifyMessage31\",\n    \"sNotifyMessage32\",\n    \"sNotifyMessage33\",\n    \"sNotifyMessage34\",\n    \"sNotifyMessage35\",\n    \"sNotifyMessage36\",\n    \"sNotifyMessage37\",\n    \"sNotifyMessage38\",\n    \"sNotifyMessage39\",\n    \"sNotifyMessage4\",\n    \"sNotifyMessage40\",\n    \"sNotifyMessage41\",\n    \"sNotifyMessage42\",\n    \"sNotifyMessage43\",\n    \"sNotifyMessage44\",\n    \"sNotifyMessage45\",\n    \"sNotifyMessage46\",\n    \"sNotifyMessage47\",\n    \"sNotifyMessage48\",\n    \"sNotifyMessage49\",\n    \"sNotifyMessage4XBOX\",\n    \"sNotifyMessage5\",\n    \"sNotifyMessage50\",\n    \"sNotifyMessage51\",\n    \"sNotifyMessage52\",\n    \"sNotifyMessage53\",\n    \"sNotifyMessage54\",\n    \"sNotifyMessage55\",\n    \"sNotifyMessage56\",\n    \"sNotifyMessage57\",\n    \"sNotifyMessage58\",\n    \"sNotifyMessage59\",\n    \"sNotifyMessage6\",\n    \"sNotifyMessage60\",\n    \"sNotifyMessage61\",\n    \"sNotifyMessage62\",\n    \"sNotifyMessage63\",\n    \"sNotifyMessage64\",\n    \"sNotifyMessage65\",\n    \"sNotifyMessage66\",\n    \"sNotifyMessage67\",\n    \"sNotifyMessage6a\",\n    \"sNotifyMessage7\",\n    \"sNotifyMessage8\",\n    \"sNotifyMessage9\",\n    \"sOff\",\n    \"sOffer\",\n    \"sOfferMenuTitle\",\n    \"sOK\",\n    \"sOn\",\n    \"sOnce\",\n    \"sOneHanded\",\n    \"sOnetypeEffectMessage\",\n    \"sonword\",\n    \"sOptions\",\n    \"sOptionsMenuXbox\",\n    \"spercent\",\n    \"sPerDesc\",\n    \"sPersuasion\",\n    \"sPersuasionMenuTitle\",\n    \"sPickUp\",\n    \"sPilgrim\",\n    \"spoint\",\n    \"spoints\",\n    \"sPotionSuccess\",\n    \"sPowerAlreadyUsed\",\n    \"sPowers\",\n    \"sPreferences\",\n    \"sPrefs\",\n    \"sPrev\",\n    \"sPrevSpell\",\n    \"sPrevSpellXbox\",\n    \"sPrevWeapon\",\n    \"sPrevWeaponXbox\",\n    \"sProfitValue\",\n    \"sQuality\",\n    \"sQuanityMenuMessage01\",\n    \"sQuanityMenuMessage02\",\n    \"sQuestionDeleteSpell\",\n    \"sQuestionMark\",\n    \"sQuick0Xbox\",\n    \"sQuick10Cmd\",\n    \"sQuick1Cmd\",\n    \"sQuick2Cmd\",\n    \"sQuick3Cmd\",\n    \"sQuick4Cmd\",\n    \"sQuick4Xbox\",\n    \"sQuick5Cmd\",\n    \"sQuick5Xbox\",\n    \"sQuick6Cmd\",\n    \"sQuick6Xbox\",\n    \"sQuick7Cmd\",\n    \"sQuick7Xbox\",\n    \"sQuick8Cmd\",\n    \"sQuick8Xbox\",\n    \"sQuick9Cmd\",\n    \"sQuick9Xbox\",\n    \"sQuick_Save\",\n    \"sQuickLoadCmd\",\n    \"sQuickLoadXbox\",\n    \"sQuickMenu\",\n    \"sQuickMenu1\",\n    \"sQuickMenu2\",\n    \"sQuickMenu3\",\n    \"sQuickMenu4\",\n    \"sQuickMenu5\",\n    \"sQuickMenu6\",\n    \"sQuickMenuInstruc\",\n    \"sQuickMenuTitle\",\n    \"sQuickSaveCmd\",\n    \"sQuickSaveXbox\",\n    \"sRace\",\n    \"sRaceMenu1\",\n    \"sRaceMenu2\",\n    \"sRaceMenu3\",\n    \"sRaceMenu4\",\n    \"sRaceMenu5\",\n    \"sRaceMenu6\",\n    \"sRaceMenu7\",\n    \"sRacialTraits\",\n    \"sRange\",\n    \"sRangeDes\",\n    \"sRangeSelf\",\n    \"sRangeTarget\",\n    \"sRangeTouch\",\n    \"sReady_Magic\",\n    \"sReady_Weapon\",\n    \"sReadyItemXbox\",\n    \"sReadyMagicXbox\",\n    \"sRechargeEnchantment\",\n    \"sRender_Distance\",\n    \"sRepair\",\n    \"sRepairFailed\",\n    \"sRepairServiceTitle\",\n    \"sRepairSuccess\",\n    \"sReputation\",\n    \"sResChangeWarning\",\n    \"sRest\",\n    \"sRestIllegal\",\n    \"sRestKey\",\n    \"sRestMenu1\",\n    \"sRestMenu2\",\n    \"sRestMenu3\",\n    \"sRestMenu4\",\n    \"sRestMenuXbox\",\n    \"sRestore\",\n    \"sRetort\",\n    \"sReturnToGame\",\n    \"sRight\",\n    \"sRogue\",\n    \"sRun\",\n    \"sRunXbox\",\n    \"sSave\",\n    \"sSaveGame\",\n    \"sSaveGameDenied\",\n    \"sSaveGameFailed\",\n    \"sSaveGameNoMemory\",\n    \"sSaveGameTooBig\",\n    \"sSaveMenu1\",\n    \"sSaveMenuHelp01\",\n    \"sSaveMenuHelp02\",\n    \"sSaveMenuHelp03\",\n    \"sSaveMenuHelp04\",\n    \"sSaveMenuHelp05\",\n    \"sSaveMenuHelp06\",\n    \"sSchool\",\n    \"sSchoolAlteration\",\n    \"sSchoolConjuration\",\n    \"sSchoolDestruction\",\n    \"sSchoolIllusion\",\n    \"sSchoolMysticism\",\n    \"sSchoolRestoration\",\n    \"sScout\",\n    \"sScrolldown\",\n    \"sScrollup\",\n    \"ssecond\",\n    \"sseconds\",\n    \"sSeldom\",\n    \"sSelect\",\n    \"sSell\",\n    \"sSellerGold\",\n    \"sService\",\n    \"sServiceRefusal\",\n    \"sServiceRepairTitle\",\n    \"sServiceSpellsTitle\",\n    \"sServiceTrainingTitle\",\n    \"sServiceTrainingWords\",\n    \"sServiceTravelTitle\",\n    \"sSetValueMessage01\",\n    \"sSex\",\n    \"sShadows\",\n    \"sShadowText\",\n    \"sShift\",\n    \"sSkill\",\n    \"sSkillAcrobatics\",\n    \"sSkillAlchemy\",\n    \"sSkillAlteration\",\n    \"sSkillArmorer\",\n    \"sSkillAthletics\",\n    \"sSkillAxe\",\n    \"sSkillBlock\",\n    \"sSkillBluntweapon\",\n    \"sSkillClassMajor\",\n    \"sSkillClassMinor\",\n    \"sSkillClassMisc\",\n    \"sSkillConjuration\",\n    \"sSkillDestruction\",\n    \"sSkillEnchant\",\n    \"sSkillHandtohand\",\n    \"sSkillHeavyarmor\",\n    \"sSkillIllusion\",\n    \"sSkillLightarmor\",\n    \"sSkillLongblade\",\n    \"sSkillMarksman\",\n    \"sSkillMaxReached\",\n    \"sSkillMediumarmor\",\n    \"sSkillMercantile\",\n    \"sSkillMysticism\",\n    \"sSkillProgress\",\n    \"sSkillRestoration\",\n    \"sSkillSecurity\",\n    \"sSkillShortblade\",\n    \"sSkillsMenu1\",\n    \"sSkillsMenuReputationHelp\",\n    \"sSkillSneak\",\n    \"sSkillSpear\",\n    \"sSkillSpeechcraft\",\n    \"sSkillUnarmored\",\n    \"sSlash\",\n    \"sSleepInterrupt\",\n    \"sSlideLeftXbox\",\n    \"sSlideRightXbox\",\n    \"sSlow\",\n    \"sSorceror\",\n    \"sSoulGem\",\n    \"sSoulGemsWithSouls\",\n    \"sSoultrapSuccess\",\n    \"sSpace\",\n    \"sSpdDesc\",\n    \"sSpecialization\",\n    \"sSpecializationCombat\",\n    \"sSpecializationMagic\",\n    \"sSpecializationMenu1\",\n    \"sSpecializationStealth\",\n    \"sSpellmaking\",\n    \"sSpellmakingHelp1\",\n    \"sSpellmakingHelp2\",\n    \"sSpellmakingHelp3\",\n    \"sSpellmakingHelp4\",\n    \"sSpellmakingHelp5\",\n    \"sSpellmakingHelp6\",\n    \"sSpellmakingMenu1\",\n    \"sSpellmakingMenuTitle\",\n    \"sSpells\",\n    \"sSpellServiceTitle\",\n    \"sSpellsword\",\n    \"sStartCell\",\n    \"sStartCellError\",\n    \"sStartError\",\n    \"sStats\",\n    \"sStrafe\",\n    \"sStrDesc\",\n    \"sStrip\",\n    \"sSubtitles\",\n    \"sSystemMenuXbox\",\n    \"sTake\",\n    \"sTakeAll\",\n    \"sTargetCriticalStrike\",\n    \"sTaunt\",\n    \"sTauntFail\",\n    \"sTauntSuccess\",\n    \"sTeleportDisabled\",\n    \"sThief\",\n    \"sThrust\",\n    \"sTo\",\n    \"sTogglePOVCmd\",\n    \"sTogglePOVXbox\",\n    \"sToggleRunXbox\",\n    \"sTopics\",\n    \"sTotalCost\",\n    \"sTotalSold\",\n    \"sTraining\",\n    \"sTrainingServiceTitle\",\n    \"sTraits\",\n    \"sTransparency_Menu\",\n    \"sTrapFail\",\n    \"sTrapImpossible\",\n    \"sTrapped\",\n    \"sTrapSuccess\",\n    \"sTravel\",\n    \"sTravelServiceTitle\",\n    \"sTurn\",\n    \"sTurnLeftXbox\",\n    \"sTurnRightXbox\",\n    \"sTwoHanded\",\n    \"sType\",\n    \"sTypeAbility\",\n    \"sTypeBlightDisease\",\n    \"sTypeCurse\",\n    \"sTypeDisease\",\n    \"sTypePower\",\n    \"sTypeSpell\",\n    \"sUnequip\",\n    \"sUnlocked\",\n    \"sUntilHealed\",\n    \"sUse\",\n    \"sUserDefinedClass\",\n    \"sUses\",\n    \"sUseXbox\",\n    \"sValue\",\n    \"sVideo\",\n    \"sVideoWarning\",\n    \"sVoice\",\n    \"sWait\",\n    \"sWarrior\",\n    \"sWaterReflectUpdate\",\n    \"sWaterTerrainReflect\",\n    \"sWeaponTab\",\n    \"sWeight\",\n    \"sWerewolfAlarmMessage\",\n    \"sWerewolfPopup\",\n    \"sWerewolfRefusal\",\n    \"sWerewolfRestMessage\",\n    \"sWilDesc\",\n    \"sWitchhunter\",\n    \"sWorld\",\n    \"sWornTab\",\n    \"sXStrafe\",\n    \"sXTimes\",\n    \"sXTimesINT\",\n    \"sYes\",\n    \"sYourGold\"\n};\n\nconst char * CSMWorld::DefaultGmsts::OptionalFloats[CSMWorld::DefaultGmsts::OptionalFloatCount] =\n{\n    \"fCombatDistanceWerewolfMod\",\n    \"fFleeDistance\",\n    \"fWereWolfAcrobatics\",\n    \"fWereWolfAgility\",\n    \"fWereWolfAlchemy\",\n    \"fWereWolfAlteration\",\n    \"fWereWolfArmorer\",\n    \"fWereWolfAthletics\",\n    \"fWereWolfAxe\",\n    \"fWereWolfBlock\",\n    \"fWereWolfBluntWeapon\",\n    \"fWereWolfConjuration\",\n    \"fWereWolfDestruction\",\n    \"fWereWolfEnchant\",\n    \"fWereWolfEndurance\",\n    \"fWereWolfFatigue\",\n    \"fWereWolfHandtoHand\",\n    \"fWereWolfHealth\",\n    \"fWereWolfHeavyArmor\",\n    \"fWereWolfIllusion\",\n    \"fWereWolfIntellegence\",\n    \"fWereWolfLightArmor\",\n    \"fWereWolfLongBlade\",\n    \"fWereWolfLuck\",\n    \"fWereWolfMagicka\",\n    \"fWereWolfMarksman\",\n    \"fWereWolfMediumArmor\",\n    \"fWereWolfMerchantile\",\n    \"fWereWolfMysticism\",\n    \"fWereWolfPersonality\",\n    \"fWereWolfRestoration\",\n    \"fWereWolfRunMult\",\n    \"fWereWolfSecurity\",\n    \"fWereWolfShortBlade\",\n    \"fWereWolfSilverWeaponDamageMult\",\n    \"fWereWolfSneak\",\n    \"fWereWolfSpear\",\n    \"fWereWolfSpeechcraft\",\n    \"fWereWolfSpeed\",\n    \"fWereWolfStrength\",\n    \"fWereWolfUnarmored\",\n    \"fWereWolfWillPower\"\n};\n\nconst char * CSMWorld::DefaultGmsts::OptionalInts[CSMWorld::DefaultGmsts::OptionalIntCount] =\n{\n    \"iWereWolfBounty\",\n    \"iWereWolfFightMod\",\n    \"iWereWolfFleeMod\",\n    \"iWereWolfLevelToAttack\"\n};\n\nconst char * CSMWorld::DefaultGmsts::OptionalStrings[CSMWorld::DefaultGmsts::OptionalStringCount] =\n{\n    \"sCompanionShare\",\n    \"sCompanionWarningButtonOne\",\n    \"sCompanionWarningButtonTwo\",\n    \"sCompanionWarningMessage\",\n    \"sDeleteNote\",\n    \"sEditNote\",\n    \"sEffectSummonCreature01\",\n    \"sEffectSummonCreature02\",\n    \"sEffectSummonCreature03\",\n    \"sEffectSummonCreature04\",\n    \"sEffectSummonCreature05\",\n    \"sEffectSummonFabricant\",\n    \"sLevitateDisabled\",\n    \"sMagicCreature01ID\",\n    \"sMagicCreature02ID\",\n    \"sMagicCreature03ID\",\n    \"sMagicCreature04ID\",\n    \"sMagicCreature05ID\",\n    \"sMagicFabricantID\",\n    \"sMaxSale\",\n    \"sProfitValue\",\n    \"sTeleportDisabled\",\n    \"sWerewolfAlarmMessage\",\n    \"sWerewolfPopup\",\n    \"sWerewolfRefusal\",\n    \"sWerewolfRestMessage\"\n};\n\nconst float CSMWorld::DefaultGmsts::FloatsDefaultValues[CSMWorld::DefaultGmsts::FloatCount] =\n{\n    0.3f,    // fAIFleeFleeMult\n    7.0f,    // fAIFleeHealthMult\n    3.0f,    // fAIMagicSpellMult\n    1.0f,    // fAIMeleeArmorMult\n    1.0f,    // fAIMeleeSummWeaponMult\n    2.0f,    // fAIMeleeWeaponMult\n    5.0f,    // fAIRangeMagicSpellMult\n    5.0f,    // fAIRangeMeleeWeaponMult\n    2000.0f, // fAlarmRadius\n    1.0f,    // fAthleticsRunBonus\n    40.0f,   // fAudioDefaultMaxDistance\n    5.0f,    // fAudioDefaultMinDistance\n    50.0f,   // fAudioMaxDistanceMult\n    20.0f,   // fAudioMinDistanceMult\n    60.0f,   // fAudioVoiceDefaultMaxDistance\n    10.0f,   // fAudioVoiceDefaultMinDistance\n    50.0f,   // fAutoPCSpellChance\n    80.0f,   // fAutoSpellChance\n    50.0f,   // fBargainOfferBase\n    -4.0f,   // fBargainOfferMulti\n    24.0f,   // fBarterGoldResetDelay\n    1.75f,   // fBaseRunMultiplier\n    1.25f,   // fBlockStillBonus\n    150.0f,  // fBribe1000Mod\n    75.0f,   // fBribe100Mod\n    35.0f,   // fBribe10Mod\n    60.0f,   // fCombatAngleXY\n    60.0f,   // fCombatAngleZ\n    0.25f,   // fCombatArmorMinMult\n    -90.0f,  // fCombatBlockLeftAngle\n    30.0f,   // fCombatBlockRightAngle\n    4.0f,    // fCombatCriticalStrikeMult\n    0.1f,    // fCombatDelayCreature\n    0.1f,    // fCombatDelayNPC\n    128.0f,  // fCombatDistance\n    0.3f,    // fCombatDistanceWerewolfMod\n    30.0f,   // fCombatForceSideAngle\n    0.2f,    // fCombatInvisoMult\n    1.5f,    // fCombatKODamageMult\n    45.0f,   // fCombatTorsoSideAngle\n    0.3f,    // fCombatTorsoStartPercent\n    0.8f,    // fCombatTorsoStopPercent\n    15.0f,   // fConstantEffectMult\n    72.0f,   // fCorpseClearDelay\n    72.0f,   // fCorpseRespawnDelay\n    0.5f,    // fCrimeGoldDiscountMult\n    0.9f,    // fCrimeGoldTurnInMult\n    1.0f,    // fCrimeStealing\n    0.5f,    // fDamageStrengthBase\n    0.1f,    // fDamageStrengthMult\n    5.0f,    // fDifficultyMult\n    2.5f,    // fDiseaseXferChance\n    -10.0f,  // fDispAttacking\n    -1.0f,   // fDispBargainFailMod\n    1.0f,    // fDispBargainSuccessMod\n    0.0f,    // fDispCrimeMod\n    -10.0f,  // fDispDiseaseMod\n    3.0f,    // fDispFactionMod\n    1.0f,    // fDispFactionRankBase\n    0.5f,    // fDispFactionRankMult\n    1.0f,    // fDispositionMod\n    50.0f,   // fDispPersonalityBase\n    0.5f,    // fDispPersonalityMult\n    -25.0f,  // fDispPickPocketMod\n    5.0f,    // fDispRaceMod\n    -0.5f,   // fDispStealing\n    -5.0f,   // fDispWeaponDrawn\n    0.5f,    // fEffectCostMult\n    0.1f,    // fElementalShieldMult\n    3.0f,    // fEnchantmentChanceMult\n    0.5f,    // fEnchantmentConstantChanceMult\n    100.0f,  // fEnchantmentConstantDurationMult\n    0.1f,    // fEnchantmentMult\n    1000.0f, // fEnchantmentValueMult\n    0.3f,    // fEncumberedMoveEffect\n    5.0f,    // fEncumbranceStrMult\n    0.04f,   // fEndFatigueMult\n    0.25f,   // fFallAcroBase\n    0.01f,   // fFallAcroMult\n    400.0f,  // fFallDamageDistanceMin\n    0.0f,    // fFallDistanceBase\n    0.07f,   // fFallDistanceMult\n    2.0f,    // fFatigueAttackBase\n    0.0f,    // fFatigueAttackMult\n    1.25f,   // fFatigueBase\n    4.0f,    // fFatigueBlockBase\n    0.0f,    // fFatigueBlockMult\n    5.0f,    // fFatigueJumpBase\n    0.0f,    // fFatigueJumpMult\n    0.5f,    // fFatigueMult\n    2.5f,    // fFatigueReturnBase\n    0.02f,   // fFatigueReturnMult\n    5.0f,    // fFatigueRunBase\n    2.0f,    // fFatigueRunMult\n    1.5f,    // fFatigueSneakBase\n    1.5f,    // fFatigueSneakMult\n    0.0f,    // fFatigueSpellBase\n    0.0f,    // fFatigueSpellCostMult\n    0.0f,    // fFatigueSpellMult\n    7.0f,    // fFatigueSwimRunBase\n    0.0f,    // fFatigueSwimRunMult\n    2.5f,    // fFatigueSwimWalkBase\n    0.0f,    // fFatigueSwimWalkMult\n    0.2f,    // fFightDispMult\n    0.005f,  // fFightDistanceMultiplier\n    50.0f,   // fFightStealing\n    3000.0f, // fFleeDistance\n    512.0f,  // fGreetDistanceReset\n    0.1f,    // fHandtoHandHealthPer\n    1.0f,    // fHandToHandReach\n    0.5f,    // fHoldBreathEndMult\n    20.0f,   // fHoldBreathTime\n    0.75f,   // fIdleChanceMultiplier\n    1.0f,    // fIngredientMult\n    0.5f,    // fInteriorHeadTrackMult\n    128.0f,  // fJumpAcrobaticsBase\n    4.0f,    // fJumpAcroMultiplier\n    0.5f,    // fJumpEncumbranceBase\n    1.0f,    // fJumpEncumbranceMultiplier\n    0.5f,    // fJumpMoveBase\n    0.5f,    // fJumpMoveMult\n    1.0f,    // fJumpRunMultiplier\n    0.5f,    // fKnockDownMult\n    5.0f,    // fLevelMod\n    0.1f,    // fLevelUpHealthEndMult\n    0.6f,    // fLightMaxMod\n    10.0f,   // fLuckMod\n    10.0f,   // fMagesGuildTravel\n    1.5f,    // fMagicCreatureCastDelay\n    0.0167f, // fMagicDetectRefreshRate\n    1.0f,    // fMagicItemConstantMult\n    1.0f,    // fMagicItemCostMult\n    1.0f,    // fMagicItemOnceMult\n    1.0f,    // fMagicItemPriceMult\n    0.05f,   // fMagicItemRechargePerSecond\n    1.0f,    // fMagicItemStrikeMult\n    1.0f,    // fMagicItemUsedMult\n    3.0f,    // fMagicStartIconBlink\n    0.5f,    // fMagicSunBlockedMult\n    0.75f,   // fMajorSkillBonus\n    300.0f,  // fMaxFlySpeed\n    0.5f,    // fMaxHandToHandMult\n    400.0f,  // fMaxHeadTrackDistance\n    200.0f,  // fMaxWalkSpeed\n    300.0f,  // fMaxWalkSpeedCreature\n    0.9f,    // fMedMaxMod\n    0.1f,    // fMessageTimePerChar\n    5.0f,    // fMinFlySpeed\n    0.1f,    // fMinHandToHandMult\n    1.0f,    // fMinorSkillBonus\n    100.0f,  // fMinWalkSpeed\n    5.0f,    // fMinWalkSpeedCreature\n    1.25f,   // fMiscSkillBonus\n    2.0f,    // fNPCbaseMagickaMult\n    0.5f,    // fNPCHealthBarFade\n    3.0f,    // fNPCHealthBarTime\n    1.0f,    // fPCbaseMagickaMult\n    0.3f,    // fPerDieRollMult\n    5.0f,    // fPersonalityMod\n    1.0f,    // fPerTempMult\n    -1.0f,   // fPickLockMult\n    0.3f,    // fPickPocketMod\n    20.0f,   // fPotionMinUsefulDuration\n    0.5f,    // fPotionStrengthMult\n    0.5f,    // fPotionT1DurMult\n    1.5f,    // fPotionT1MagMult\n    20.0f,   // fPotionT4BaseStrengthMult\n    12.0f,   // fPotionT4EquipStrengthMult\n    3000.0f, // fProjectileMaxSpeed\n    400.0f,  // fProjectileMinSpeed\n    25.0f,   // fProjectileThrownStoreChance\n    3.0f,    // fRepairAmountMult\n    1.0f,    // fRepairMult\n    1.0f,    // fReputationMod\n    0.15f,   // fRestMagicMult\n    0.0f,    // fSeriousWoundMult\n    0.25f,   // fSleepRandMod\n    0.3f,    // fSleepRestMod\n    -1.0f,   // fSneakBootMult\n    0.5f,    // fSneakDistanceBase\n    0.002f,  // fSneakDistanceMultiplier\n    0.5f,    // fSneakNoViewMult\n    1.0f,    // fSneakSkillMult\n    0.75f,   // fSneakSpeedMultiplier\n    1.0f,    // fSneakUseDelay\n    500.0f,  // fSneakUseDist\n    1.5f,    // fSneakViewMult\n    3.0f,    // fSoulGemMult\n    0.8f,    // fSpecialSkillBonus\n    7.0f,    // fSpellMakingValueMult\n    2.0f,    // fSpellPriceMult\n    10.0f,   // fSpellValueMult\n    0.25f,   // fStromWalkMult\n    0.7f,    // fStromWindSpeed\n    3.0f,    // fSuffocationDamage\n    0.9f,    // fSwimHeightScale\n    0.1f,    // fSwimRunAthleticsMult\n    0.5f,    // fSwimRunBase\n    0.02f,   // fSwimWalkAthleticsMult\n    0.5f,    // fSwimWalkBase\n    1.0f,    // fSwingBlockBase\n    1.0f,    // fSwingBlockMult\n    1000.0f, // fTargetSpellMaxSpeed\n    1000.0f, // fThrownWeaponMaxSpeed\n    300.0f,  // fThrownWeaponMinSpeed\n    0.0f,    // fTrapCostMult\n    4000.0f, // fTravelMult\n    16000.0f,// fTravelTimeMult\n    0.1f,    // fUnarmoredBase1\n    0.065f,  // fUnarmoredBase2\n    30.0f,   // fVanityDelay\n    10.0f,   // fVoiceIdleOdds\n    0.0f,    // fWaterReflectUpdateAlways\n    10.0f,   // fWaterReflectUpdateSeldom\n    0.1f,    // fWeaponDamageMult\n    1.0f,    // fWeaponFatigueBlockMult\n    0.25f,   // fWeaponFatigueMult\n    150.0f,  // fWereWolfAcrobatics\n    150.0f,  // fWereWolfAgility\n    1.0f,    // fWereWolfAlchemy\n    1.0f,    // fWereWolfAlteration\n    1.0f,    // fWereWolfArmorer\n    150.0f,  // fWereWolfAthletics\n    1.0f,    // fWereWolfAxe\n    1.0f,    // fWereWolfBlock\n    1.0f,    // fWereWolfBluntWeapon\n    1.0f,    // fWereWolfConjuration\n    1.0f,    // fWereWolfDestruction\n    1.0f,    // fWereWolfEnchant\n    150.0f,  // fWereWolfEndurance\n    400.0f,  // fWereWolfFatigue\n    100.0f,  // fWereWolfHandtoHand\n    2.0f,    // fWereWolfHealth\n    1.0f,    // fWereWolfHeavyArmor\n    1.0f,    // fWereWolfIllusion\n    1.0f,    // fWereWolfIntellegence\n    1.0f,    // fWereWolfLightArmor\n    1.0f,    // fWereWolfLongBlade\n    1.0f,    // fWereWolfLuck\n    100.0f,  // fWereWolfMagicka\n    1.0f,    // fWereWolfMarksman\n    1.0f,    // fWereWolfMediumArmor\n    1.0f,    // fWereWolfMerchantile\n    1.0f,    // fWereWolfMysticism\n    1.0f,    // fWereWolfPersonality\n    1.0f,    // fWereWolfRestoration\n    1.5f,    // fWereWolfRunMult\n    1.0f,    // fWereWolfSecurity\n    1.0f,    // fWereWolfShortBlade\n    1.5f,    // fWereWolfSilverWeaponDamageMult\n    1.0f,    // fWereWolfSneak\n    1.0f,    // fWereWolfSpear\n    1.0f,    // fWereWolfSpeechcraft\n    150.0f,  // fWereWolfSpeed\n    150.0f,  // fWereWolfStrength\n    100.0f,  // fWereWolfUnarmored\n    1.0f,    // fWereWolfWillPower\n    15.0f   // fWortChanceValue\n};\n\nconst int CSMWorld::DefaultGmsts::IntsDefaultValues[CSMWorld::DefaultGmsts::IntCount] =\n{\n    10,     // i1stPersonSneakDelta\n    50,     // iAlarmAttack\n    90,     // iAlarmKilling\n    20,     // iAlarmPickPocket\n    1,      // iAlarmStealing\n    5,      // iAlarmTresspass\n    2,      // iAlchemyMod\n    100,    // iAutoPCSpellMax\n    2,      // iAutoRepFacMod\n    0,      // iAutoRepLevMod\n    5,      // iAutoSpellAlterationMax\n    70,     // iAutoSpellAttSkillMin\n    2,      // iAutoSpellConjurationMax\n    5,      // iAutoSpellDestructionMax\n    5,      // iAutoSpellIllusionMax\n    5,      // iAutoSpellMysticismMax\n    5,      // iAutoSpellRestorationMax\n    3,      // iAutoSpellTimesCanCast\n    -1,     // iBarterFailDisposition\n    1,      // iBarterSuccessDisposition\n    30,     // iBaseArmorSkill\n    50,     // iBlockMaxChance\n    10,     // iBlockMinChance\n    20,     // iBootsWeight\n    40,     // iCrimeAttack\n    1000,   // iCrimeKilling\n    25,     // iCrimePickPocket\n    1000,   // iCrimeThreshold\n    10,     // iCrimeThresholdMultiplier\n    5,      // iCrimeTresspass\n    30,     // iCuirassWeight\n    100,    // iDaysinPrisonMod\n    -50,    // iDispAttackMod\n    -50,    // iDispKilling\n    -20,    // iDispTresspass\n    1,      // iFightAlarmMult\n    100,    // iFightAttack\n    50,     // iFightAttacking\n    20,     // iFightDistanceBase\n    50,     // iFightKilling\n    25,     // iFightPickpocket\n    25,     // iFightTrespass\n    0,      // iFlee\n    5,      // iGauntletWeight\n    15,     // iGreavesWeight\n    6,      // iGreetDistanceMultiplier\n    4,      // iGreetDuration\n    5,      // iHelmWeight\n    50,     // iKnockDownOddsBase\n    50,     // iKnockDownOddsMult\n    2,      // iLevelUp01Mult\n    2,      // iLevelUp02Mult\n    2,      // iLevelUp03Mult\n    2,      // iLevelUp04Mult\n    3,      // iLevelUp05Mult\n    3,      // iLevelUp06Mult\n    3,      // iLevelUp07Mult\n    4,      // iLevelUp08Mult\n    4,      // iLevelUp09Mult\n    5,      // iLevelUp10Mult\n    1,      // iLevelupMajorMult\n    1,      // iLevelupMajorMultAttribute\n    1,      // iLevelupMinorMult\n    1,      // iLevelupMinorMultAttribute\n    1,      // iLevelupMiscMultAttriubte\n    1,      // iLevelupSpecialization\n    10,     // iLevelupTotal\n    10,     // iMagicItemChargeConst\n    1,      // iMagicItemChargeOnce\n    10,     // iMagicItemChargeStrike\n    5,      // iMagicItemChargeUse\n    192,    // iMaxActivateDist\n    192,    // iMaxInfoDist\n    4,      // iMonthsToRespawn\n    1,      // iNumberCreatures\n    10,     // iPauldronWeight\n    5,      // iPerMinChance\n    10,     // iPerMinChange\n    75,     // iPickMaxChance\n    5,      // iPickMinChance\n    15,     // iShieldWeight\n    400,    // iSoulAmountForConstantEffect\n    10,     // iTrainingMod\n    10,     // iVoiceAttackOdds\n    30,     // iVoiceHitOdds\n    10000,  // iWereWolfBounty\n    100,    // iWereWolfFightMod\n    100,    // iWereWolfFleeMod\n    20     // iWereWolfLevelToAttack\n};\n\nconst float CSMWorld::DefaultGmsts::FloatLimits[CSMWorld::DefaultGmsts::FloatCount * 2] =\n{\n    -FInf,      FInf,       // fAIFleeFleeMult\n    -FInf,      FInf,       // fAIFleeHealthMult\n    -FInf,      FInf,       // fAIMagicSpellMult\n    -FInf,      FInf,       // fAIMeleeArmorMult\n    -FInf,      FInf,       // fAIMeleeSummWeaponMult\n    -FInf,      FInf,       // fAIMeleeWeaponMult\n    -FInf,      FInf,       // fAIRangeMagicSpellMult\n    -FInf,      FInf,       // fAIRangeMeleeWeaponMult\n    0,          FInf,       // fAlarmRadius\n    -FInf,      FInf,       // fAthleticsRunBonus\n    0,          FInf,       // fAudioDefaultMaxDistance\n    0,          FInf,       // fAudioDefaultMinDistance\n    0,          FInf,       // fAudioMaxDistanceMult\n    0,          FInf,       // fAudioMinDistanceMult\n    0,          FInf,       // fAudioVoiceDefaultMaxDistance\n    0,          FInf,       // fAudioVoiceDefaultMinDistance\n    0,          FInf,       // fAutoPCSpellChance\n    0,          FInf,       // fAutoSpellChance\n    -FInf,      FInf,       // fBargainOfferBase\n    -FInf,      0,          // fBargainOfferMulti\n    -FInf,      FInf,       // fBarterGoldResetDelay\n    0,          FInf,       // fBaseRunMultiplier\n    -FInf,      FInf,       // fBlockStillBonus\n    0,          FInf,       // fBribe1000Mod\n    0,          FInf,       // fBribe100Mod\n    0,          FInf,       // fBribe10Mod\n    0,          FInf,       // fCombatAngleXY\n    0,          FInf,       // fCombatAngleZ\n    0,          1,          // fCombatArmorMinMult\n    -180,       0,          // fCombatBlockLeftAngle\n    0,          180,        // fCombatBlockRightAngle\n    0,          FInf,       // fCombatCriticalStrikeMult\n    0,          FInf,       // fCombatDelayCreature\n    0,          FInf,       // fCombatDelayNPC\n    0,          FInf,       // fCombatDistance\n    -FInf,      FInf,       // fCombatDistanceWerewolfMod\n    -FInf,      FInf,       // fCombatForceSideAngle\n    0,          FInf,       // fCombatInvisoMult\n    0,          FInf,       // fCombatKODamageMult\n    -FInf,      FInf,       // fCombatTorsoSideAngle\n    -FInf,      FInf,       // fCombatTorsoStartPercent\n    -FInf,      FInf,       // fCombatTorsoStopPercent\n    -FInf,      FInf,       // fConstantEffectMult\n    -FInf,      FInf,       // fCorpseClearDelay\n    -FInf,      FInf,       // fCorpseRespawnDelay\n    0,          1,          // fCrimeGoldDiscountMult\n    0,          FInf,       // fCrimeGoldTurnInMult\n    0,          FInf,       // fCrimeStealing\n    0,          FInf,       // fDamageStrengthBase\n    0,          FInf,       // fDamageStrengthMult\n    -FInf,      FInf,       // fDifficultyMult\n    0,          FInf,       // fDiseaseXferChance\n    -FInf,      0,          // fDispAttacking\n    -FInf,      FInf,       // fDispBargainFailMod\n    -FInf,      FInf,       // fDispBargainSuccessMod\n    -FInf,      0,          // fDispCrimeMod\n    -FInf,      0,          // fDispDiseaseMod\n    0,          FInf,       // fDispFactionMod\n    0,          FInf,       // fDispFactionRankBase\n    0,          FInf,       // fDispFactionRankMult\n    0,          FInf,       // fDispositionMod\n    0,          FInf,       // fDispPersonalityBase\n    0,          FInf,       // fDispPersonalityMult\n    -FInf,      0,          // fDispPickPocketMod\n    0,          FInf,       // fDispRaceMod\n    -FInf,      0,          // fDispStealing\n    -FInf,      0,          // fDispWeaponDrawn\n    0,          FInf,       // fEffectCostMult\n    0,          FInf,       // fElementalShieldMult\n    FEps,       FInf,       // fEnchantmentChanceMult\n    0,          FInf,       // fEnchantmentConstantChanceMult\n    0,          FInf,       // fEnchantmentConstantDurationMult\n    0,          FInf,       // fEnchantmentMult\n    0,          FInf,       // fEnchantmentValueMult\n    0,          FInf,       // fEncumberedMoveEffect\n    0,          FInf,       // fEncumbranceStrMult\n    0,          FInf,       // fEndFatigueMult\n    -FInf,      FInf,       // fFallAcroBase\n    0,          FInf,       // fFallAcroMult\n    0,          FInf,       // fFallDamageDistanceMin\n    -FInf,      FInf,       // fFallDistanceBase\n    0,          FInf,       // fFallDistanceMult\n    -FInf,      FInf,       // fFatigueAttackBase\n    0,          FInf,       // fFatigueAttackMult\n    0,          FInf,       // fFatigueBase\n    0,          FInf,       // fFatigueBlockBase\n    0,          FInf,       // fFatigueBlockMult\n    0,          FInf,       // fFatigueJumpBase\n    0,          FInf,       // fFatigueJumpMult\n    0,          FInf,       // fFatigueMult\n    -FInf,      FInf,       // fFatigueReturnBase\n    0,          FInf,       // fFatigueReturnMult\n    -FInf,      FInf,       // fFatigueRunBase\n    0,          FInf,       // fFatigueRunMult\n    -FInf,      FInf,       // fFatigueSneakBase\n    0,          FInf,       // fFatigueSneakMult\n    -FInf,      FInf,       // fFatigueSpellBase\n    -FInf,      FInf,       // fFatigueSpellCostMult\n    0,          FInf,       // fFatigueSpellMult\n    -FInf,      FInf,       // fFatigueSwimRunBase\n    0,          FInf,       // fFatigueSwimRunMult\n    -FInf,      FInf,       // fFatigueSwimWalkBase\n    0,          FInf,       // fFatigueSwimWalkMult\n    -FInf,      FInf,       // fFightDispMult\n    -FInf,      FInf,       // fFightDistanceMultiplier\n    -FInf,      FInf,       // fFightStealing\n    -FInf,      FInf,       // fFleeDistance\n    -FInf,      FInf,       // fGreetDistanceReset\n    0,          FInf,       // fHandtoHandHealthPer\n    0,          FInf,       // fHandToHandReach\n    -FInf,      FInf,       // fHoldBreathEndMult\n    0,          FInf,       // fHoldBreathTime\n    0,          FInf,       // fIdleChanceMultiplier\n    -FInf,      FInf,       // fIngredientMult\n    0,          FInf,       // fInteriorHeadTrackMult\n    -FInf,      FInf,       // fJumpAcrobaticsBase\n    0,          FInf,       // fJumpAcroMultiplier\n    -FInf,      FInf,       // fJumpEncumbranceBase\n    0,          FInf,       // fJumpEncumbranceMultiplier\n    -FInf,      FInf,       // fJumpMoveBase\n    0,          FInf,       // fJumpMoveMult\n    0,          FInf,       // fJumpRunMultiplier\n    -FInf,      FInf,       // fKnockDownMult\n    0,          FInf,       // fLevelMod\n    0,          FInf,       // fLevelUpHealthEndMult\n    0,          FInf,       // fLightMaxMod\n    0,          FInf,       // fLuckMod\n    0,          FInf,       // fMagesGuildTravel\n    -FInf,      FInf,       // fMagicCreatureCastDelay\n    -FInf,      FInf,       // fMagicDetectRefreshRate\n    -FInf,      FInf,       // fMagicItemConstantMult\n    -FInf,      FInf,       // fMagicItemCostMult\n    -FInf,      FInf,       // fMagicItemOnceMult\n    -FInf,      FInf,       // fMagicItemPriceMult\n    0,          FInf,       // fMagicItemRechargePerSecond\n    -FInf,      FInf,       // fMagicItemStrikeMult\n    -FInf,      FInf,       // fMagicItemUsedMult\n    0,          FInf,       // fMagicStartIconBlink\n    0,          FInf,       // fMagicSunBlockedMult\n    FEps,       FInf,       // fMajorSkillBonus\n    0,          FInf,       // fMaxFlySpeed\n    0,          FInf,       // fMaxHandToHandMult\n    0,          FInf,       // fMaxHeadTrackDistance\n    0,          FInf,       // fMaxWalkSpeed\n    0,          FInf,       // fMaxWalkSpeedCreature\n    0,          FInf,       // fMedMaxMod\n    0,          FInf,       // fMessageTimePerChar\n    0,          FInf,       // fMinFlySpeed\n    0,          FInf,       // fMinHandToHandMult\n    FEps,       FInf,       // fMinorSkillBonus\n    0,          FInf,       // fMinWalkSpeed\n    0,          FInf,       // fMinWalkSpeedCreature\n    FEps,       FInf,       // fMiscSkillBonus\n    0,          FInf,       // fNPCbaseMagickaMult\n    0,          FInf,       // fNPCHealthBarFade\n    0,          FInf,       // fNPCHealthBarTime\n    0,          FInf,       // fPCbaseMagickaMult\n    0,          FInf,       // fPerDieRollMult\n    0,          FInf,       // fPersonalityMod\n    0,          FInf,       // fPerTempMult\n    -FInf,      0,          // fPickLockMult\n    0,          FInf,       // fPickPocketMod\n    -FInf,      FInf,       // fPotionMinUsefulDuration\n    0,          FInf,       // fPotionStrengthMult\n    FEps,       FInf,       // fPotionT1DurMult\n    FEps,       FInf,       // fPotionT1MagMult\n    -FInf,      FInf,       // fPotionT4BaseStrengthMult\n    -FInf,      FInf,       // fPotionT4EquipStrengthMult\n    0,          FInf,       // fProjectileMaxSpeed\n    0,          FInf,       // fProjectileMinSpeed\n    0,          FInf,       // fProjectileThrownStoreChance\n    0,          FInf,       // fRepairAmountMult\n    0,          FInf,       // fRepairMult\n    0,          FInf,       // fReputationMod\n    0,          FInf,       // fRestMagicMult\n    -FInf,      FInf,       // fSeriousWoundMult\n    0,          FInf,       // fSleepRandMod\n    0,          FInf,       // fSleepRestMod\n    -FInf,      0,          // fSneakBootMult\n    -FInf,      FInf,       // fSneakDistanceBase\n    0,          FInf,       // fSneakDistanceMultiplier\n    0,          FInf,       // fSneakNoViewMult\n    0,          FInf,       // fSneakSkillMult\n    0,          FInf,       // fSneakSpeedMultiplier\n    0,          FInf,       // fSneakUseDelay\n    0,          FInf,       // fSneakUseDist\n    0,          FInf,       // fSneakViewMult\n    0,          FInf,       // fSoulGemMult\n    0,          FInf,       // fSpecialSkillBonus\n    0,          FInf,       // fSpellMakingValueMult\n    -FInf,      FInf,       // fSpellPriceMult\n    0,          FInf,       // fSpellValueMult\n    0,          FInf,       // fStromWalkMult\n    0,          FInf,       // fStromWindSpeed\n    0,          FInf,       // fSuffocationDamage\n    0,          FInf,       // fSwimHeightScale\n    0,          FInf,       // fSwimRunAthleticsMult\n    0,          FInf,       // fSwimRunBase\n    -FInf,      FInf,       // fSwimWalkAthleticsMult\n    -FInf,      FInf,       // fSwimWalkBase\n    0,          FInf,       // fSwingBlockBase\n    0,          FInf,       // fSwingBlockMult\n    0,          FInf,       // fTargetSpellMaxSpeed\n    0,          FInf,       // fThrownWeaponMaxSpeed\n    0,          FInf,       // fThrownWeaponMinSpeed\n    0,          FInf,       // fTrapCostMult\n    0,          FInf,       // fTravelMult\n    0,          FInf,       // fTravelTimeMult\n    0,          FInf,       // fUnarmoredBase1\n    0,          FInf,       // fUnarmoredBase2\n    0,          FInf,       // fVanityDelay\n    0,          FInf,       // fVoiceIdleOdds\n    -FInf,      FInf,       // fWaterReflectUpdateAlways\n    -FInf,      FInf,       // fWaterReflectUpdateSeldom\n    0,          FInf,       // fWeaponDamageMult\n    0,          FInf,       // fWeaponFatigueBlockMult\n    0,          FInf,       // fWeaponFatigueMult\n    0,          FInf,       // fWereWolfAcrobatics\n    -FInf,      FInf,       // fWereWolfAgility\n    -FInf,      FInf,       // fWereWolfAlchemy\n    -FInf,      FInf,       // fWereWolfAlteration\n    -FInf,      FInf,       // fWereWolfArmorer\n    -FInf,      FInf,       // fWereWolfAthletics\n    -FInf,      FInf,       // fWereWolfAxe\n    -FInf,      FInf,       // fWereWolfBlock\n    -FInf,      FInf,       // fWereWolfBluntWeapon\n    -FInf,      FInf,       // fWereWolfConjuration\n    -FInf,      FInf,       // fWereWolfDestruction\n    -FInf,      FInf,       // fWereWolfEnchant\n    -FInf,      FInf,       // fWereWolfEndurance\n    -FInf,      FInf,       // fWereWolfFatigue\n    -FInf,      FInf,       // fWereWolfHandtoHand\n    -FInf,      FInf,       // fWereWolfHealth\n    -FInf,      FInf,       // fWereWolfHeavyArmor\n    -FInf,      FInf,       // fWereWolfIllusion\n    -FInf,      FInf,       // fWereWolfIntellegence\n    -FInf,      FInf,       // fWereWolfLightArmor\n    -FInf,      FInf,       // fWereWolfLongBlade\n    -FInf,      FInf,       // fWereWolfLuck\n    -FInf,      FInf,       // fWereWolfMagicka\n    -FInf,      FInf,       // fWereWolfMarksman\n    -FInf,      FInf,       // fWereWolfMediumArmor\n    -FInf,      FInf,       // fWereWolfMerchantile\n    -FInf,      FInf,       // fWereWolfMysticism\n    -FInf,      FInf,       // fWereWolfPersonality\n    -FInf,      FInf,       // fWereWolfRestoration\n    0,          FInf,       // fWereWolfRunMult\n    -FInf,      FInf,       // fWereWolfSecurity\n    -FInf,      FInf,       // fWereWolfShortBlade\n    -FInf,      FInf,       // fWereWolfSilverWeaponDamageMult\n    -FInf,      FInf,       // fWereWolfSneak\n    -FInf,      FInf,       // fWereWolfSpear\n    -FInf,      FInf,       // fWereWolfSpeechcraft\n    -FInf,      FInf,       // fWereWolfSpeed\n    -FInf,      FInf,       // fWereWolfStrength\n    -FInf,      FInf,       // fWereWolfUnarmored\n    -FInf,      FInf,       // fWereWolfWillPower\n    0,          FInf        // fWortChanceValue\n};\n\nconst int CSMWorld::DefaultGmsts::IntLimits[CSMWorld::DefaultGmsts::IntCount * 2] =\n{\n    IMin,    IMax,  // i1stPersonSneakDelta\n    IMin,    IMax,  // iAlarmAttack\n    IMin,    IMax,  // iAlarmKilling\n    IMin,    IMax,  // iAlarmPickPocket\n    IMin,    IMax,  // iAlarmStealing\n    IMin,    IMax,  // iAlarmTresspass\n    IMin,    IMax,  // iAlchemyMod\n    0,       IMax,  // iAutoPCSpellMax\n    IMin,    IMax,  // iAutoRepFacMod\n    IMin,    IMax,  // iAutoRepLevMod\n    IMin,    IMax,  // iAutoSpellAlterationMax\n    0,       IMax,  // iAutoSpellAttSkillMin\n    IMin,    IMax,  // iAutoSpellConjurationMax\n    IMin,    IMax,  // iAutoSpellDestructionMax\n    IMin,    IMax,  // iAutoSpellIllusionMax\n    IMin,    IMax,  // iAutoSpellMysticismMax\n    IMin,    IMax,  // iAutoSpellRestorationMax\n    0,       IMax,  // iAutoSpellTimesCanCast\n    IMin,    0,     // iBarterFailDisposition\n    0,       IMax,  // iBarterSuccessDisposition\n    1,       IMax,  // iBaseArmorSkill\n    0,       IMax,  // iBlockMaxChance\n    0,       IMax,  // iBlockMinChance\n    0,       IMax,  // iBootsWeight\n    IMin,    IMax,  // iCrimeAttack\n    IMin,    IMax,  // iCrimeKilling\n    IMin,    IMax,  // iCrimePickPocket\n    0,       IMax,  // iCrimeThreshold\n    0,       IMax,  // iCrimeThresholdMultiplier\n    IMin,    IMax,  // iCrimeTresspass\n    0,       IMax,  // iCuirassWeight\n    1,       IMax,  // iDaysinPrisonMod\n    IMin,    0,     // iDispAttackMod\n    IMin,    0,     // iDispKilling\n    IMin,    0,     // iDispTresspass\n    IMin,    IMax,  // iFightAlarmMult\n    IMin,    IMax,  // iFightAttack\n    IMin,    IMax,  // iFightAttacking\n    0,       IMax,  // iFightDistanceBase\n    IMin,    IMax,  // iFightKilling\n    IMin,    IMax,  // iFightPickpocket\n    IMin,    IMax,  // iFightTrespass\n    IMin,    IMax,  // iFlee\n    0,       IMax,  // iGauntletWeight\n    0,       IMax,  // iGreavesWeight\n    0,       IMax,  // iGreetDistanceMultiplier\n    0,       IMax,  // iGreetDuration\n    0,       IMax,  // iHelmWeight\n    IMin,    IMax,  // iKnockDownOddsBase\n    IMin,    IMax,  // iKnockDownOddsMult\n    IMin,    IMax,  // iLevelUp01Mult\n    IMin,    IMax,  // iLevelUp02Mult\n    IMin,    IMax,  // iLevelUp03Mult\n    IMin,    IMax,  // iLevelUp04Mult\n    IMin,    IMax,  // iLevelUp05Mult\n    IMin,    IMax,  // iLevelUp06Mult\n    IMin,    IMax,  // iLevelUp07Mult\n    IMin,    IMax,  // iLevelUp08Mult\n    IMin,    IMax,  // iLevelUp09Mult\n    IMin,    IMax,  // iLevelUp10Mult\n    IMin,    IMax,  // iLevelupMajorMult\n    IMin,    IMax,  // iLevelupMajorMultAttribute\n    IMin,    IMax,  // iLevelupMinorMult\n    IMin,    IMax,  // iLevelupMinorMultAttribute\n    IMin,    IMax,  // iLevelupMiscMultAttriubte\n    IMin,    IMax,  // iLevelupSpecialization\n    IMin,    IMax,  // iLevelupTotal\n    IMin,    IMax,  // iMagicItemChargeConst\n    IMin,    IMax,  // iMagicItemChargeOnce\n    IMin,    IMax,  // iMagicItemChargeStrike\n    IMin,    IMax,  // iMagicItemChargeUse\n    IMin,    IMax,  // iMaxActivateDist\n    IMin,    IMax,  // iMaxInfoDist\n    0,       IMax,  // iMonthsToRespawn\n    0,       IMax,  // iNumberCreatures\n    0,       IMax,  // iPauldronWeight\n    0,       IMax,  // iPerMinChance\n    0,       IMax,  // iPerMinChange\n    0,       IMax,  // iPickMaxChance\n    0,       IMax,  // iPickMinChance\n    0,       IMax,  // iShieldWeight\n    0,       IMax,  // iSoulAmountForConstantEffect\n    0,       IMax,  // iTrainingMod\n    0,       IMax,  // iVoiceAttackOdds\n    0,       IMax,  // iVoiceHitOdds\n    IMin,    IMax,  // iWereWolfBounty\n    IMin,    IMax,  // iWereWolfFightMod\n    IMin,    IMax,  // iWereWolfFleeMod\n    IMin,    IMax   // iWereWolfLevelToAttack\n};\n"
  },
  {
    "path": "apps/opencs/model/world/defaultgmsts.hpp",
    "content": "#ifndef CSM_WORLD_DEFAULTGMSTS_H\n#define CSM_WORLD_DEFAULTGMSTS_H\n\n#include <cstddef>\n\nnamespace CSMWorld {\n    namespace DefaultGmsts {\n        \n        const size_t FloatCount = 258;\n        const size_t IntCount = 89;\n        const size_t StringCount = 1174;\n        \n        const size_t OptionalFloatCount = 42;\n        const size_t OptionalIntCount = 4;\n        const size_t OptionalStringCount = 26;\n        \n        extern const char* Floats[];\n        extern const char * Ints[];\n        extern const char * Strings[];\n        \n        extern const char * OptionalFloats[];\n        extern const char * OptionalInts[];\n        extern const char * OptionalStrings[];\n\n        extern const float FloatsDefaultValues[];\n        extern const int IntsDefaultValues[];\n\n        extern const float FloatLimits[];\n        extern const int IntLimits[];\n\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/idcollection.hpp",
    "content": "#ifndef CSM_WOLRD_IDCOLLECTION_H\n#define CSM_WOLRD_IDCOLLECTION_H\n\n#include <components/esm/esmreader.hpp>\n\n#include \"collection.hpp\"\n#include \"land.hpp\"\n\nnamespace CSMWorld\n{\n    /// \\brief Single type collection of top level records\n    template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >\n    class IdCollection : public Collection<ESXRecordT, IdAccessorT>\n    {\n            virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted);\n\n        public:\n\n            /// \\return Index of loaded record (-1 if no record was loaded)\n            int load (ESM::ESMReader& reader, bool base);\n\n            /// \\param index Index at which the record can be found.\n            /// Special values: -2 index unknown, -1 record does not exist yet and therefore\n            /// does not have an index\n            ///\n            /// \\return index\n            int load (const ESXRecordT& record, bool base, int index = -2);\n\n            bool tryDelete (const std::string& id);\n            ///< Try deleting \\a id. If the id does not exist or can't be deleted the call is ignored.\n            ///\n            /// \\return Has the ID been deleted?\n    };\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void IdCollection<ESXRecordT, IdAccessorT>::loadRecord (ESXRecordT& record,\n                                                            ESM::ESMReader& reader,\n                                                            bool& isDeleted)\n    {\n        record.load (reader, isDeleted);\n    }\n\n    template<>\n    inline void IdCollection<Land, IdAccessor<Land> >::loadRecord (Land& record,\n        ESM::ESMReader& reader, bool& isDeleted)\n    {\n        record.load (reader, isDeleted);\n\n        // Load all land data for now. A future optimisation may only load non-base data\n        // if a suitable mechanism for avoiding race conditions can be established.\n        int flags = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |\n            ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;\n        record.loadData (flags);\n\n        // Prevent data from being reloaded.\n        record.mContext.filename.clear();\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)\n    {\n        ESXRecordT record;\n        bool isDeleted = false;\n\n        loadRecord (record, reader, isDeleted);\n\n        std::string id = IdAccessorT().getId (record);\n        int index = this->searchId (id);\n\n        if (isDeleted)\n        {\n            if (index==-1)\n            {\n                // deleting a record that does not exist\n                // ignore it for now\n                /// \\todo report the problem to the user\n                return -1;\n            }\n\n            if (base)\n            {\n                this->removeRows (index, 1);\n                return -1;\n            }\n\n            Record<ESXRecordT> baseRecord = this->getRecord (index);\n            baseRecord.mState = RecordBase::State_Deleted;\n            this->setRecord (index, baseRecord);\n            return index;\n        }\n\n        return load (record, base, index);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,\n        int index)\n    {\n        if (index==-2)\n            index = this->searchId (IdAccessorT().getId (record));\n\n        if (index==-1)\n        {\n            // new record\n            Record<ESXRecordT> record2;\n            record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;\n            (base ? record2.mBase : record2.mModified) = record;\n\n            index = this->getSize();\n            this->appendRecord (record2);\n        }\n        else\n        {\n            // old record\n            Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);\n\n            if (base)\n                record2.mBase = record;\n            else\n                record2.setModified (record);\n\n            this->setRecord (index, record2);\n        }\n\n        return index;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    bool IdCollection<ESXRecordT, IdAccessorT>::tryDelete (const std::string& id)\n    {\n        int index = this->searchId (id);\n\n        if (index==-1)\n            return false;\n\n        Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);\n\n        if (record.isDeleted())\n            return false;\n\n        if (record.mState==RecordBase::State_ModifiedOnly)\n        {\n            Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);\n        }\n        else\n        {\n            record.mState = RecordBase::State_Deleted;\n            this->setRecord (index, record);\n        }\n\n        return true;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/idcompletionmanager.cpp",
    "content": "#include \"idcompletionmanager.hpp\"\n\n#include <QCompleter>\n\n#include \"../../view/widget/completerpopup.hpp\"\n\n#include \"data.hpp\"\n#include \"idtablebase.hpp\"\n\nnamespace\n{\n    std::map<CSMWorld::ColumnBase::Display, CSMWorld::UniversalId::Type> generateModelTypes()\n    {\n        std::map<CSMWorld::ColumnBase::Display, CSMWorld::UniversalId::Type> types;\n\n        types[CSMWorld::ColumnBase::Display_BodyPart            ] = CSMWorld::UniversalId::Type_BodyPart;\n        types[CSMWorld::ColumnBase::Display_Cell                ] = CSMWorld::UniversalId::Type_Cell;\n        types[CSMWorld::ColumnBase::Display_Class               ] = CSMWorld::UniversalId::Type_Class;\n        types[CSMWorld::ColumnBase::Display_CreatureLevelledList] = CSMWorld::UniversalId::Type_Referenceable;\n        types[CSMWorld::ColumnBase::Display_Creature            ] = CSMWorld::UniversalId::Type_Referenceable;\n        types[CSMWorld::ColumnBase::Display_Enchantment         ] = CSMWorld::UniversalId::Type_Enchantment;\n        types[CSMWorld::ColumnBase::Display_Faction             ] = CSMWorld::UniversalId::Type_Faction;\n        types[CSMWorld::ColumnBase::Display_GlobalVariable      ] = CSMWorld::UniversalId::Type_Global;\n        types[CSMWorld::ColumnBase::Display_Icon                ] = CSMWorld::UniversalId::Type_Icon;\n        types[CSMWorld::ColumnBase::Display_Journal             ] = CSMWorld::UniversalId::Type_Journal;\n        types[CSMWorld::ColumnBase::Display_Mesh                ] = CSMWorld::UniversalId::Type_Mesh;\n        types[CSMWorld::ColumnBase::Display_Miscellaneous       ] = CSMWorld::UniversalId::Type_Referenceable;\n        types[CSMWorld::ColumnBase::Display_Npc                 ] = CSMWorld::UniversalId::Type_Referenceable;\n        types[CSMWorld::ColumnBase::Display_Race                ] = CSMWorld::UniversalId::Type_Race;\n        types[CSMWorld::ColumnBase::Display_Region              ] = CSMWorld::UniversalId::Type_Region;\n        types[CSMWorld::ColumnBase::Display_Referenceable       ] = CSMWorld::UniversalId::Type_Referenceable;\n        types[CSMWorld::ColumnBase::Display_Script              ] = CSMWorld::UniversalId::Type_Script;\n        types[CSMWorld::ColumnBase::Display_Skill               ] = CSMWorld::UniversalId::Type_Skill;\n        types[CSMWorld::ColumnBase::Display_Sound               ] = CSMWorld::UniversalId::Type_Sound;\n        types[CSMWorld::ColumnBase::Display_SoundRes            ] = CSMWorld::UniversalId::Type_SoundRes;\n        types[CSMWorld::ColumnBase::Display_Spell               ] = CSMWorld::UniversalId::Type_Spell;\n        types[CSMWorld::ColumnBase::Display_Static              ] = CSMWorld::UniversalId::Type_Referenceable;\n        types[CSMWorld::ColumnBase::Display_Texture             ] = CSMWorld::UniversalId::Type_Texture;\n        types[CSMWorld::ColumnBase::Display_Topic               ] = CSMWorld::UniversalId::Type_Topic;\n        types[CSMWorld::ColumnBase::Display_Weapon              ] = CSMWorld::UniversalId::Type_Referenceable;\n\n        return types;\n    }\n\n    typedef std::map<CSMWorld::ColumnBase::Display, \n                     CSMWorld::UniversalId::Type>::const_iterator ModelTypeConstIterator;\n}\n\nconst std::map<CSMWorld::ColumnBase::Display, CSMWorld::UniversalId::Type>\n    CSMWorld::IdCompletionManager::sCompleterModelTypes = generateModelTypes();\n\nstd::vector<CSMWorld::ColumnBase::Display> CSMWorld::IdCompletionManager::getDisplayTypes()\n{\n    std::vector<CSMWorld::ColumnBase::Display> types;\n    ModelTypeConstIterator current = sCompleterModelTypes.begin();\n    ModelTypeConstIterator end = sCompleterModelTypes.end();\n    for (; current != end; ++current)\n    {\n        types.push_back(current->first);\n    }\n\n    // Hack for Display_InfoCondVar\n    types.push_back(CSMWorld::ColumnBase::Display_InfoCondVar);\n\n    return types;\n}\n\nCSMWorld::IdCompletionManager::IdCompletionManager(CSMWorld::Data &data)\n{\n    generateCompleters(data);\n}\n\nbool CSMWorld::IdCompletionManager::hasCompleterFor(CSMWorld::ColumnBase::Display display) const\n{\n    return mCompleters.find(display) != mCompleters.end();\n}\n\nstd::shared_ptr<QCompleter> CSMWorld::IdCompletionManager::getCompleter(CSMWorld::ColumnBase::Display display)\n{\n    if (!hasCompleterFor(display))\n    {\n        throw std::logic_error(\"This column doesn't have an ID completer\");\n    }\n    return mCompleters[display];\n}\n\nvoid CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data)\n{\n    ModelTypeConstIterator current = sCompleterModelTypes.begin();\n    ModelTypeConstIterator end = sCompleterModelTypes.end();\n    for (; current != end; ++current)\n    {\n        QAbstractItemModel *model = data.getTableModel(current->second);\n        CSMWorld::IdTableBase *table = dynamic_cast<CSMWorld::IdTableBase *>(model);\n        if (table != nullptr)\n        {\n            int idColumn = table->searchColumnIndex(CSMWorld::Columns::ColumnId_Id);\n            if (idColumn != -1)\n            {\n                std::shared_ptr<QCompleter> completer = std::make_shared<QCompleter>(table);\n                completer->setCompletionColumn(idColumn);\n                // The completion role must be Qt::DisplayRole to get the ID values from the model\n                completer->setCompletionRole(Qt::DisplayRole);\n                completer->setCaseSensitivity(Qt::CaseInsensitive);\n\n                QAbstractItemView *popup = new CSVWidget::CompleterPopup();\n                completer->setPopup(popup); // The completer takes ownership of the popup\n                completer->setMaxVisibleItems(10);\n\n                mCompleters[current->first] = completer;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/idcompletionmanager.hpp",
    "content": "#ifndef CSM_WORLD_IDCOMPLETIONMANAGER_HPP\n#define CSM_WORLD_IDCOMPLETIONMANAGER_HPP\n\n#include <vector>\n#include <map>\n#include <memory>\n\n#include \"columnbase.hpp\"\n#include \"universalid.hpp\"\n\nclass QCompleter;\n\nnamespace CSMWorld\n{\n    class Data;\n\n    /// \\brief Creates and stores all ID completers\n    class IdCompletionManager\n    {\n            static const std::map<ColumnBase::Display, UniversalId::Type> sCompleterModelTypes;\n\n            std::map<ColumnBase::Display, std::shared_ptr<QCompleter> > mCompleters;\n\n            // Don't allow copying\n            IdCompletionManager(const IdCompletionManager &);\n            IdCompletionManager &operator = (const IdCompletionManager &);\n\n            void generateCompleters(Data &data);\n\n        public:\n            static std::vector<ColumnBase::Display> getDisplayTypes();\n\n            IdCompletionManager(Data &data);\n\n            bool hasCompleterFor(ColumnBase::Display display) const;\n            std::shared_ptr<QCompleter> getCompleter(ColumnBase::Display display);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/idtable.cpp",
    "content": "#include \"idtable.hpp\"\n\n#include <algorithm>\n#include <cctype>\n#include <cstdint>\n#include <limits>\n#include <map>\n#include <stdexcept>\n\n#include <components/esm/cellid.hpp>\n\n#include \"collectionbase.hpp\"\n#include \"columnbase.hpp\"\n#include \"landtexture.hpp\"\n\nCSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)\n: IdTableBase (features), mIdCollection (idCollection)\n{}\n\nCSMWorld::IdTable::~IdTable()\n{}\n\nint CSMWorld::IdTable::rowCount (const QModelIndex & parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return mIdCollection->getSize();\n}\n\nint CSMWorld::IdTable::columnCount (const QModelIndex & parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return mIdCollection->getColumns();\n}\n\nQVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const\n{\n    if (index.row() < 0 || index.column() < 0)\n        return QVariant();\n\n    if (role==ColumnBase::Role_Display)\n        return QVariant(mIdCollection->getColumn(index.column()).mDisplayType);\n\n    if (role==ColumnBase::Role_ColumnId)\n        return QVariant (getColumnId (index.column()));\n\n    if ((role!=Qt::DisplayRole && role!=Qt::EditRole))\n        return QVariant();\n\n    if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())\n        return QVariant();\n\n    return mIdCollection->getData (index.row(), index.column());\n}\n\nQVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const\n{\n    if (orientation==Qt::Vertical)\n        return QVariant();\n\n    if (orientation != Qt::Horizontal)\n        throw std::logic_error(\"Unknown header orientation specified\");\n\n    if (role==Qt::DisplayRole)\n        return tr (mIdCollection->getColumn (section).getTitle().c_str());\n\n    if (role==ColumnBase::Role_Flags)\n        return mIdCollection->getColumn (section).mFlags;\n\n    if (role==ColumnBase::Role_Display)\n        return mIdCollection->getColumn (section).mDisplayType;\n\n    if (role==ColumnBase::Role_ColumnId)\n        return getColumnId (section);\n\n    return QVariant();\n}\n\nbool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value, int role)\n{\n    if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)\n    {\n        mIdCollection->setData (index.row(), index.column(), value);\n\n        int stateColumn = searchColumnIndex(Columns::ColumnId_Modification);\n        if (stateColumn != -1)\n        {\n            if (index.column() == stateColumn)\n            {\n                // modifying the state column can modify other values. we need to tell\n                // views that the whole row has changed.\n\n                emit dataChanged(this->index(index.row(), 0),\n                                 this->index(index.row(), columnCount(index.parent()) - 1));\n\n            } else\n            {\n                emit dataChanged(index, index);\n\n                // Modifying a value can also change the Modified status of a record.\n                QModelIndex stateIndex = this->index(index.row(), stateColumn);\n                emit dataChanged(stateIndex, stateIndex);\n            }\n        } else\n            emit dataChanged(index, index);\n\n        return true;\n    }\n\n    return false;\n}\n\nQt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const\n{\n    if (!index.isValid())\n        return Qt::ItemFlags();\n\n    Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;\n\n    if (mIdCollection->getColumn (index.column()).isUserEditable())\n        flags |= Qt::ItemIsEditable;\n\n    return flags;\n}\n\nbool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& parent)\n{\n    if (parent.isValid())\n        return false;\n\n    beginRemoveRows (parent, row, row+count-1);\n\n    mIdCollection->removeRows (row, count);\n\n    endRemoveRows();\n\n    return true;\n}\n\nQModelIndex CSMWorld::IdTable::index (int row, int column, const QModelIndex& parent) const\n{\n    if (parent.isValid())\n        return QModelIndex();\n\n    if (row<0 || row>=mIdCollection->getSize())\n        return QModelIndex();\n\n    if (column<0 || column>=mIdCollection->getColumns())\n        return QModelIndex();\n\n    return createIndex (row, column);\n}\n\nQModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const\n{\n    return QModelIndex();\n}\n\nvoid CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type)\n{\n    int index = mIdCollection->getAppendIndex (id, type);\n\n    beginInsertRows (QModelIndex(), index, index);\n\n    mIdCollection->appendBlankRecord (id, type);\n\n    endInsertRows();\n}\n\nvoid CSMWorld::IdTable::addRecordWithData (const std::string& id,\n    const std::map<int, QVariant>& data, UniversalId::Type type)\n{\n    int index = mIdCollection->getAppendIndex (id, type);\n\n    beginInsertRows (QModelIndex(), index, index);\n\n    mIdCollection->appendBlankRecord (id, type);\n\n    for (std::map<int, QVariant>::const_iterator iter (data.begin()); iter!=data.end(); ++iter)\n    {\n        mIdCollection->setData(index, iter->first, iter->second);\n    }\n\n    endInsertRows();\n}\n\nvoid CSMWorld::IdTable::cloneRecord(const std::string& origin,\n                                    const std::string& destination,\n                                    CSMWorld::UniversalId::Type type)\n{\n    int index = mIdCollection->getAppendIndex (destination);\n\n    beginInsertRows (QModelIndex(), index, index);\n    mIdCollection->cloneRecord(origin, destination, type);\n    endInsertRows();\n}\n\nbool CSMWorld::IdTable::touchRecord(const std::string& id)\n{\n    bool changed = mIdCollection->touchRecord(id);\n\n    int row = mIdCollection->getIndex(id);\n    int column = mIdCollection->searchColumnIndex(Columns::ColumnId_RecordType);\n    if (changed && column != -1)\n    {\n        QModelIndex modelIndex = index(row, column);\n        emit dataChanged(modelIndex, modelIndex);\n    }\n\n    return changed;\n}\n\nstd::string CSMWorld::IdTable::getId(int row) const\n{\n    return mIdCollection->getId(row);\n}\n\n///This method can return only indexes to the top level table cells\nQModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const\n{\n    int row = mIdCollection->searchId (id);\n    if (row != -1)\n        return index(row, column);\n\n    return QModelIndex();\n}\n\nvoid CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type)\n{\n    int index = mIdCollection->searchId (id);\n\n    if (index==-1)\n    {\n        index = mIdCollection->getAppendIndex (id, type);\n\n        beginInsertRows (QModelIndex(), index, index);\n\n        mIdCollection->appendRecord (record, type);\n\n        endInsertRows();\n    }\n    else\n    {\n        mIdCollection->replace (index, record);\n        emit dataChanged (CSMWorld::IdTable::index (index, 0),\n            CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));\n    }\n}\n\nconst CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const\n{\n    return mIdCollection->getRecord (id);\n}\n\nint CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const\n{\n    return mIdCollection->searchColumnIndex (id);\n}\n\nint CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const\n{\n    return mIdCollection->findColumnIndex (id);\n}\n\nvoid CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector<int>& newOrder)\n{\n    if (!newOrder.empty())\n        if (mIdCollection->reorderRows (baseIndex, newOrder))\n            emit dataChanged (index (baseIndex, 0),\n                index (baseIndex+static_cast<int>(newOrder.size())-1, mIdCollection->getColumns()-1));\n}\n\nstd::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row) const\n{\n    std::string id;\n    std::string hint;\n\n    if (getFeatures() & Feature_ViewCell)\n    {\n        int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell);\n        int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);\n\n        if (cellColumn!=-1 && idColumn!=-1)\n        {\n            id = mIdCollection->getData (row, cellColumn).toString().toUtf8().constData();\n            hint = \"r:\" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData());\n        }\n    }\n    else if (getFeatures() & Feature_ViewId)\n    {\n        int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);\n\n        if (column!=-1)\n        {\n            id = mIdCollection->getData (row, column).toString().toUtf8().constData();\n            hint = \"c:\" + id;\n        }\n    }\n\n    if (id.empty())\n        return std::make_pair (UniversalId::Type_None, \"\");\n\n    if (id[0]=='#')\n        id = ESM::CellId::sDefaultWorldspace;\n\n    return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint);\n}\n\n///For top level data/columns\nbool CSMWorld::IdTable::isDeleted (const std::string& id) const\n{\n    return getRecord (id).isDeleted();\n}\n\nint CSMWorld::IdTable::getColumnId(int column) const\n{\n    return mIdCollection->getColumn(column).getId();\n}\n\nCSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const\n{\n    return mIdCollection;\n}\n\nCSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, unsigned int features)\n    : IdTable(idCollection, features)\n{\n}\n\nCSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(const std::vector<std::string>& ids)\n{\n    ImportResults results;\n\n    // Map existing textures to ids\n    std::map<std::string, std::string> reverseLookupMap;\n    for (int i = 0; i < idCollection()->getSize(); ++i)\n    {\n        auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(i));\n        std::string texture = record.get().mTexture;\n        std::transform(texture.begin(), texture.end(), texture.begin(), tolower);\n        if (record.isModified())\n            reverseLookupMap.emplace(texture, idCollection()->getId(i));\n    }\n\n    for (const std::string& id : ids)\n    {\n        int plugin, index;\n\n        LandTexture::parseUniqueRecordId(id, plugin, index);\n        int oldRow = idCollection()->searchId(id);\n\n        // If it does not exist or it is in the current plugin, it can be skipped.\n        if (oldRow < 0 || plugin == 0)\n        {\n            results.recordMapping.emplace_back(id, id);\n            continue;\n        }\n\n        // Look for a pre-existing record\n        auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(oldRow));\n        std::string texture = record.get().mTexture;\n        std::transform(texture.begin(), texture.end(), texture.begin(), tolower);\n        auto searchIt = reverseLookupMap.find(texture);\n        if (searchIt != reverseLookupMap.end())\n        {\n            results.recordMapping.emplace_back(id, searchIt->second);\n            continue;\n        }\n\n        // Iterate until an unused index or found, or the index has completely wrapped around.\n        int startIndex = index;\n        do {\n            std::string newId = LandTexture::createUniqueRecordId(0, index);\n            int newRow = idCollection()->searchId(newId);\n\n            if (newRow < 0)\n            {\n                // Id not taken, clone it\n                cloneRecord(id, newId, UniversalId::Type_LandTexture);\n                results.createdRecords.push_back(newId);\n                results.recordMapping.emplace_back(id, newId);\n                reverseLookupMap.emplace(texture, newId);\n                break;\n            }\n\n            const size_t MaxIndex = std::numeric_limits<uint16_t>::max() - 1;\n            index = (index + 1) % MaxIndex;\n        } while (index != startIndex);\n    }\n\n    return results;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/idtable.hpp",
    "content": "#ifndef CSM_WOLRD_IDTABLE_H\n#define CSM_WOLRD_IDTABLE_H\n\n#include <vector>\n\n#include \"idtablebase.hpp\"\n#include \"universalid.hpp\"\n#include \"columns.hpp\"\n\nnamespace CSMWorld\n{\n    class CollectionBase;\n    struct RecordBase;\n\n    class IdTable : public IdTableBase\n    {\n            Q_OBJECT\n\n        private:\n\n            CollectionBase *mIdCollection;\n\n            // not implemented\n            IdTable (const IdTable&);\n            IdTable& operator= (const IdTable&);\n\n        public:\n\n            IdTable (CollectionBase *idCollection, unsigned int features = 0);\n            ///< The ownership of \\a idCollection is not transferred.\n\n            virtual ~IdTable();\n\n            int rowCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            int columnCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            QVariant data  (const QModelIndex & index, int role = Qt::DisplayRole) const override;\n\n            QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;\n\n            bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;\n\n            Qt::ItemFlags flags (const QModelIndex & index) const override;\n\n            bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()) override;\n\n            QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const override;\n\n            QModelIndex parent (const QModelIndex& index) const override;\n\n            void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            void addRecordWithData (const std::string& id, const std::map<int, QVariant>& data,\n                UniversalId::Type type = UniversalId::Type_None);\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            void cloneRecord(const std::string& origin,\n                             const std::string& destination,\n                             UniversalId::Type type = UniversalId::Type_None);\n\n            bool touchRecord(const std::string& id);\n            ///< Will change the record state to modified, if it is not already.\n\n            std::string getId(int row) const;\n\n            QModelIndex getModelIndex (const std::string& id, int column) const override;\n\n            void setRecord (const std::string& id, const RecordBase& record,\n                    UniversalId::Type type = UniversalId::Type_None);\n            ///< Add record or overwrite existing record.\n\n            const RecordBase& getRecord (const std::string& id) const;\n\n            int searchColumnIndex (Columns::ColumnId id) const override;\n            ///< Return index of column with the given \\a id. If no such column exists, -1 is returned.\n\n            int findColumnIndex (Columns::ColumnId id) const override;\n            ///< Return index of column with the given \\a id. If no such column exists, an exception is\n            /// thrown.\n\n            void reorderRows (int baseIndex, const std::vector<int>& newOrder);\n            ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices\n            /// given in \\a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).\n\n            std::pair<UniversalId, std::string> view (int row) const override;\n            ///< Return the UniversalId and the hint for viewing \\a row. If viewing is not\n            /// supported by this table, return (UniversalId::Type_None, \"\").\n\n            /// Is \\a id flagged as deleted?\n            bool isDeleted (const std::string& id) const override;\n\n            int getColumnId(int column) const override;\n\n        protected:\n\n            virtual CollectionBase *idCollection() const;\n    };\n\n    /// An IdTable customized to handle the more unique needs of LandTextureId's which behave\n    /// differently from other records. The major difference is that base records cannot be\n    /// modified.\n    class LandTextureIdTable : public IdTable\n    {\n        public:\n\n            struct ImportResults\n            {\n                using StringPair = std::pair<std::string,std::string>;\n\n                /// The newly added records\n                std::vector<std::string> createdRecords;\n                /// The 1st string is the original id, the 2nd is the mapped id\n                std::vector<StringPair> recordMapping;\n            };\n\n            LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0);\n\n            /// Finds and maps/recreates the specified ids.\n            ImportResults importTextures(const std::vector<std::string>& ids);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/idtablebase.cpp",
    "content": "#include \"idtablebase.hpp\"\n\nCSMWorld::IdTableBase::IdTableBase (unsigned int features) : mFeatures (features) {}\n\nunsigned int CSMWorld::IdTableBase::getFeatures() const\n{\n    return mFeatures;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/idtablebase.hpp",
    "content": "#ifndef CSM_WOLRD_IDTABLEBASE_H\n#define CSM_WOLRD_IDTABLEBASE_H\n\n#include <QAbstractItemModel>\n\n#include \"columns.hpp\"\n\nnamespace CSMWorld\n{\n    class UniversalId;\n\n    class IdTableBase : public QAbstractItemModel\n    {\n            Q_OBJECT\n\n        public:\n\n            enum Features\n            {\n                Feature_ReorderWithinTopic = 1,\n\n                /// Use ID column to generate view request (ID is transformed into\n                /// worldspace and original ID is passed as hint with c: prefix).\n                Feature_ViewId = 2,\n\n                /// Use cell column to generate view request (cell ID is transformed\n                /// into worldspace and record ID is passed as hint with r: prefix).\n                Feature_ViewCell = 4,\n\n                Feature_View = Feature_ViewId | Feature_ViewCell,\n\n                Feature_Preview = 8,\n\n                /// Table can not be modified through ordinary means.\n                Feature_Constant = 16,\n\n                Feature_AllowTouch = 32\n            };\n\n        private:\n\n            unsigned int mFeatures;\n\n        public:\n\n            IdTableBase (unsigned int features);\n\n            virtual QModelIndex getModelIndex (const std::string& id, int column) const = 0;\n\n            /// Return index of column with the given \\a id. If no such column exists, -1 is\n            /// returned.\n            virtual int searchColumnIndex (Columns::ColumnId id) const = 0;\n\n            /// Return index of column with the given \\a id. If no such column exists, an\n            /// exception is thrown.\n            virtual int findColumnIndex (Columns::ColumnId id) const = 0;\n\n            /// Return the UniversalId and the hint for viewing \\a row. If viewing is not\n            /// supported by this table, return (UniversalId::Type_None, \"\").\n            virtual std::pair<UniversalId, std::string> view (int row) const = 0;\n\n            /// Is \\a id flagged as deleted?\n            virtual bool isDeleted (const std::string& id) const = 0;\n\n            virtual int getColumnId (int column) const = 0;\n\n            unsigned int getFeatures() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/idtableproxymodel.cpp",
    "content": "#include \"idtableproxymodel.hpp\"\n\n#include <vector>\n\n#include \"idtablebase.hpp\"\n\nnamespace\n{\n    std::string getEnumValue(const std::vector<std::pair<int,std::string>> &values, int index)\n    {\n        if (index < 0 || index >= static_cast<int>(values.size()))\n        {\n            return \"\";\n        }\n        return values[index].second;\n    }\n}\n\nvoid CSMWorld::IdTableProxyModel::updateColumnMap()\n{\n    Q_ASSERT(mSourceModel != nullptr);\n\n    mColumnMap.clear();\n    if (mFilter)\n    {\n        std::vector<int> columns = mFilter->getReferencedColumns();\n        for (std::vector<int>::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter)\n            mColumnMap.insert (std::make_pair (*iter, \n                mSourceModel->searchColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (*iter))));\n    }\n}\n\nbool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent)\n    const\n{\n    Q_ASSERT(mSourceModel != nullptr);\n\n    // It is not possible to use filterAcceptsColumn() and check for\n    // sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags)\n    // because the sourceColumn parameter excludes the hidden columns, i.e. wrong columns can\n    // be rejected.  Workaround by disallowing tree branches (nested columns), which are not meant\n    // to be visible, from the filter.\n    if (sourceParent.isValid())\n        return false;\n\n    if (!mFilter)\n        return true;\n\n    return mFilter->test (*mSourceModel, sourceRow, mColumnMap);\n}\n\nCSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)\n    : QSortFilterProxyModel (parent), \n      mSourceModel(nullptr)\n{\n    setSortCaseSensitivity (Qt::CaseInsensitive);\n}\n\nQModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const\n{\n    Q_ASSERT(mSourceModel != nullptr);\n\n    return mapFromSource(mSourceModel->getModelIndex (id, column));\n}\n\nvoid CSMWorld::IdTableProxyModel::setSourceModel(QAbstractItemModel *model)\n{\n    QSortFilterProxyModel::setSourceModel(model);\n\n    mSourceModel = dynamic_cast<IdTableBase *>(sourceModel());\n    connect(mSourceModel, \n            SIGNAL(rowsInserted(const QModelIndex &, int, int)), \n            this, \n            SLOT(sourceRowsInserted(const QModelIndex &, int, int)));\n    connect(mSourceModel, \n            SIGNAL(rowsRemoved(const QModelIndex &, int, int)), \n            this,\n            SLOT(sourceRowsRemoved(const QModelIndex &, int, int)));\n    connect(mSourceModel, \n            SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), \n            this, \n            SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &)));\n}\n\nvoid CSMWorld::IdTableProxyModel::setFilter (const std::shared_ptr<CSMFilter::Node>& filter)\n{\n    beginResetModel();\n    mFilter = filter;\n    updateColumnMap();\n    endResetModel();\n}\n\nbool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    Columns::ColumnId id = static_cast<Columns::ColumnId>(left.data(ColumnBase::Role_ColumnId).toInt());\n    EnumColumnCache::const_iterator valuesIt = mEnumColumnCache.find(id);\n    if (valuesIt == mEnumColumnCache.end())\n    {\n        if (Columns::hasEnums(id))\n        {\n            valuesIt = mEnumColumnCache.insert(std::make_pair(id, Columns::getEnums(id))).first;\n        }\n    }\n\n    if (valuesIt != mEnumColumnCache.end())\n    {\n        std::string first = getEnumValue(valuesIt->second, left.data().toInt());\n        std::string second = getEnumValue(valuesIt->second, right.data().toInt());\n        return first < second;\n    }\n    return QSortFilterProxyModel::lessThan(left, right);\n}\n\nQString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const\n{\n    Q_ASSERT(mSourceModel != nullptr);\n\n    int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id);\n    return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString();\n}\n\nvoid CSMWorld::IdTableProxyModel::refreshFilter()\n{\n    updateColumnMap();\n    invalidateFilter();\n}\n\nvoid CSMWorld::IdTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end)\n{\n    refreshFilter();\n    if (!parent.isValid())\n    {\n        emit rowAdded(getRecordId(end).toUtf8().constData());\n    }\n}\n\nvoid CSMWorld::IdTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/)\n{\n    refreshFilter();\n}\n\nvoid CSMWorld::IdTableProxyModel::sourceDataChanged(const QModelIndex &/*topLeft*/, const QModelIndex &/*bottomRight*/)\n{\n    refreshFilter();\n}\n"
  },
  {
    "path": "apps/opencs/model/world/idtableproxymodel.hpp",
    "content": "#ifndef CSM_WOLRD_IDTABLEPROXYMODEL_H\n#define CSM_WOLRD_IDTABLEPROXYMODEL_H\n\n#include <string>\n\n#include <map>\n\n#include <QSortFilterProxyModel>\n\n#include \"../filter/node.hpp\"\n\n#include \"columns.hpp\"\n\nnamespace CSMWorld\n{\n    class IdTableProxyModel : public QSortFilterProxyModel\n    {\n            Q_OBJECT\n\n            std::shared_ptr<CSMFilter::Node> mFilter;\n            std::map<int, int> mColumnMap; // column ID, column index in this model (or -1)\n\n            // Cache of enum values for enum columns (e.g. Modified, Record Type).\n            // Used to speed up comparisons during the sort by such columns.\n            typedef std::map<Columns::ColumnId, std::vector<std::pair<int,std::string>> > EnumColumnCache;\n            mutable EnumColumnCache mEnumColumnCache;\n\n        protected:\n\n            IdTableBase *mSourceModel;\n\n        private:\n\n            void updateColumnMap();\n\n        public:\n\n            IdTableProxyModel (QObject *parent = nullptr);\n\n            virtual QModelIndex getModelIndex (const std::string& id, int column) const;\n\n            void setSourceModel(QAbstractItemModel *model) override;\n\n            void setFilter (const std::shared_ptr<CSMFilter::Node>& filter);\n\n            void refreshFilter();\n\n        protected:\n\n            bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n\n            bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const override;\n\n            QString getRecordId(int sourceRow) const;\n\n        protected slots:\n\n            virtual void sourceRowsInserted(const QModelIndex &parent, int start, int end);\n\n            virtual void sourceRowsRemoved(const QModelIndex &parent, int start, int end);\n\n            virtual void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);\n\n        signals:\n\n            void rowAdded(const std::string &id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/idtree.cpp",
    "content": "#include \"idtree.hpp\"\n\n#include \"nestedtablewrapper.hpp\"\n\n#include \"collectionbase.hpp\"\n#include \"nestedcollection.hpp\"\n#include \"columnbase.hpp\"\n\n// NOTE: parent class still needs idCollection\nCSMWorld::IdTree::IdTree (NestedCollection *nestedCollection, CollectionBase *idCollection, unsigned int features)\n: IdTable (idCollection, features), mNestedCollection (nestedCollection)\n{}\n\nCSMWorld::IdTree::~IdTree()\n{}\n\nint CSMWorld::IdTree::rowCount (const QModelIndex & parent) const\n{\n    if (hasChildren(parent))\n        return mNestedCollection->getNestedRowsCount(parent.row(), parent.column());\n\n    return IdTable::rowCount(parent);\n}\n\nint CSMWorld::IdTree::columnCount (const QModelIndex & parent) const\n{\n    if (hasChildren(parent))\n        return mNestedCollection->getNestedColumnsCount(parent.row(), parent.column());\n\n    return IdTable::columnCount(parent);\n}\n\nQVariant CSMWorld::IdTree::data  (const QModelIndex & index, int role) const\n{\n     if (!index.isValid())\n          return QVariant();\n\n    if (index.internalId() != 0)\n    {\n        std::pair<int, int> parentAddress(unfoldIndexAddress(index.internalId()));\n        const NestableColumn *parentColumn = mNestedCollection->getNestableColumn(parentAddress.second);\n\n        if (role == ColumnBase::Role_Display)\n            return parentColumn->nestedColumn(index.column()).mDisplayType;\n\n        if (role == ColumnBase::Role_ColumnId)\n            return parentColumn->nestedColumn(index.column()).mColumnId;\n\n        if (role == Qt::EditRole && !parentColumn->nestedColumn(index.column()).isEditable())\n            return QVariant();\n\n        if (role != Qt::DisplayRole && role != Qt::EditRole)\n            return QVariant();\n\n        return mNestedCollection->getNestedData(parentAddress.first,\n                                            parentAddress.second, index.row(), index.column());\n    }\n    else\n    {\n        return IdTable::data(index, role);\n    }\n}\n\nQVariant CSMWorld::IdTree::nestedHeaderData(int section, int subSection, Qt::Orientation orientation, int role) const\n{\n    if (section < 0 || section >= idCollection()->getColumns())\n        return QVariant();\n\n    const NestableColumn *parentColumn = mNestedCollection->getNestableColumn(section);\n\n    if (orientation==Qt::Vertical)\n        return QVariant();\n\n    if (role==Qt::DisplayRole)\n        return tr(parentColumn->nestedColumn(subSection).getTitle().c_str());\n\n    if (role==ColumnBase::Role_Flags)\n        return parentColumn->nestedColumn(subSection).mFlags;\n\n    if (role==ColumnBase::Role_Display)\n        return parentColumn->nestedColumn(subSection).mDisplayType;\n\n    if (role==ColumnBase::Role_ColumnId)\n        return parentColumn->nestedColumn(subSection).mColumnId;\n\n    return QVariant();\n}\n\nbool CSMWorld::IdTree::setData (const QModelIndex &index, const QVariant &value, int role)\n{\n    if (index.internalId() != 0)\n    {\n        if (idCollection()->getColumn(parent(index).column()).isEditable() && role==Qt::EditRole)\n        {\n            const std::pair<int, int>& parentAddress(unfoldIndexAddress(index.internalId()));\n\n            mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column());\n            emit dataChanged(index, index);\n\n            // Modifying a value can also change the Modified status of a record.\n            int stateColumn = searchColumnIndex(Columns::ColumnId_Modification);\n            if (stateColumn != -1)\n            {\n                QModelIndex stateIndex = this->index(index.parent().row(), stateColumn);\n                emit dataChanged(stateIndex, stateIndex);\n            }\n\n            return true;\n        }\n        else\n            return false;\n    }\n    return IdTable::setData(index, value, role);\n}\n\nQt::ItemFlags CSMWorld::IdTree::flags (const QModelIndex & index) const\n{\n    if (!index.isValid())\n        return Qt::ItemFlags();\n\n    if (index.internalId() != 0)\n    {\n        std::pair<int, int> parentAddress(unfoldIndexAddress(index.internalId()));\n\n        Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;\n\n        if (mNestedCollection->getNestableColumn(parentAddress.second)->nestedColumn(index.column()).isEditable())\n            flags |= Qt::ItemIsEditable;\n\n        return flags;\n    }\n    else\n        return IdTable::flags(index);\n}\n\nbool CSMWorld::IdTree::removeRows (int row, int count, const QModelIndex& parent)\n{\n    if (parent.isValid())\n    {\n        beginRemoveRows (parent, row, row+count-1);\n\n        for (int i = 0; i < count; ++i)\n        {\n            mNestedCollection->removeNestedRows(parent.row(), parent.column(), row+i);\n        }\n\n        endRemoveRows();\n\n        emit dataChanged (CSMWorld::IdTree::index (parent.row(), 0),\n                          CSMWorld::IdTree::index (parent.row(), idCollection()->getColumns()-1));\n\n        return true;\n    }\n    else\n        return IdTable::removeRows(row, count, parent);\n}\n\nvoid CSMWorld::IdTree::addNestedRow(const QModelIndex& parent, int position)\n{\n    if (!hasChildren(parent))\n        throw std::logic_error(\"Tried to set nested table, but index has no children\");\n\n    int row = parent.row();\n\n    beginInsertRows(parent, position, position);\n    mNestedCollection->addNestedRow(row, parent.column(), position);\n    endInsertRows();\n\n    emit dataChanged (CSMWorld::IdTree::index (row, 0),\n                      CSMWorld::IdTree::index (row, idCollection()->getColumns()-1));\n}\n\nQModelIndex CSMWorld::IdTree::index (int row, int column, const QModelIndex& parent) const\n{\n    unsigned int encodedId = 0;\n    if (parent.isValid())\n    {\n        encodedId = this->foldIndexAddress(parent);\n    }\n\n    if (row < 0 || row >= rowCount(parent))\n        return QModelIndex();\n\n    if (column < 0 || column >= columnCount(parent))\n        return QModelIndex();\n\n    return createIndex(row, column, encodedId); // store internal id\n}\n\nQModelIndex CSMWorld::IdTree::getNestedModelIndex (const std::string& id, int column) const\n{\n    return CSMWorld::IdTable::index(idCollection()->getIndex (id), column);\n}\n\nQModelIndex CSMWorld::IdTree::parent (const QModelIndex& index) const\n{\n    if (index.internalId() == 0) // 0 is used for indexs with invalid parent (top level data)\n        return QModelIndex();\n\n    unsigned int id = index.internalId();\n    const std::pair<int, int>& address(unfoldIndexAddress(id));\n\n    if (address.first >= this->rowCount() || address.second >= this->columnCount())\n        throw std::logic_error(\"Parent index is not present in the model\");\n\n    return createIndex(address.first, address.second);\n}\n\nunsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const\n{\n    unsigned int out = index.row() * this->columnCount();\n    out += index.column();\n    return ++out;\n}\n\nstd::pair< int, int > CSMWorld::IdTree::unfoldIndexAddress (unsigned int id) const\n{\n    if (id == 0)\n        throw std::runtime_error(\"Attempt to unfold index id of the top level data cell\");\n\n    --id;\n    int row = id / this->columnCount();\n    int column = id - row * this->columnCount();\n    return std::make_pair (row, column);\n}\n\n// FIXME: Not sure why this check is also needed?\n//\n// index.data().isValid() requires RefIdAdapter::getData() to return a valid QVariant for\n// nested columns (refidadapterimp.hpp)\n//\n// Also see comments in refidadapter.hpp and refidadapterimp.hpp.\nbool CSMWorld::IdTree::hasChildren(const QModelIndex& index) const\n{\n    return (index.isValid() &&\n            index.internalId() == 0 &&\n            mNestedCollection->getNestableColumn(index.column())->hasChildren() &&\n            index.data().isValid());\n}\n\nvoid CSMWorld::IdTree::setNestedTable(const QModelIndex& index, const CSMWorld::NestedTableWrapperBase& nestedTable)\n{\n    if (!hasChildren(index))\n        throw std::logic_error(\"Tried to set nested table, but index has no children\");\n\n    bool removeRowsMode = false;\n    if (nestedTable.size() != this->nestedTable(index)->size())\n    {\n        emit resetStart(this->index(index.row(), 0).data().toString());\n        removeRowsMode = true;\n    }\n\n    mNestedCollection->setNestedTable(index.row(), index.column(), nestedTable);\n\n    emit dataChanged (CSMWorld::IdTree::index (index.row(), 0),\n                      CSMWorld::IdTree::index (index.row(), idCollection()->getColumns()-1));\n\n    if (removeRowsMode)\n    {\n        emit resetEnd(this->index(index.row(), 0).data().toString());\n    }\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::IdTree::nestedTable(const QModelIndex& index) const\n{\n    if (!hasChildren(index))\n        throw std::logic_error(\"Tried to retrieve nested table, but index has no children\");\n\n    return mNestedCollection->nestedTable(index.row(), index.column());\n}\n\nint CSMWorld::IdTree::searchNestedColumnIndex(int parentColumn, Columns::ColumnId id)\n{\n    return mNestedCollection->searchNestedColumnIndex(parentColumn, id);\n}\n\nint CSMWorld::IdTree::findNestedColumnIndex(int parentColumn, Columns::ColumnId id)\n{\n    return mNestedCollection->findNestedColumnIndex(parentColumn, id);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/idtree.hpp",
    "content": "#ifndef CSM_WOLRD_IDTREE_H\n#define CSM_WOLRD_IDTREE_H\n\n#include \"idtable.hpp\"\n#include \"universalid.hpp\"\n#include \"columns.hpp\"\n\n/*! \\brief\n * Class for holding the model. Uses typical qt table abstraction/interface for granting access\n * to the individiual fields of the records, Some records are holding nested data (for instance\n * inventory list of the npc). In cases like this, table model offers interface to access\n * nested data in the qt way - that is specify parent. Since some of those nested data require\n * multiple columns to represent information, single int (default way to index model in the\n * qmodelindex) is not sufficiant. Therefore tablemodelindex class can hold two ints for the\n * sake of indexing two dimensions of the table. This model does not support multiple levels of\n * the nested data. Vast majority of methods makes sense only for the top level data.\n */\n\nnamespace CSMWorld\n{\n    class NestedCollection;\n    struct RecordBase;\n    struct NestedTableWrapperBase;\n\n    class IdTree : public IdTable\n    {\n            Q_OBJECT\n\n        private:\n\n            NestedCollection *mNestedCollection;\n\n            // not implemented\n            IdTree (const IdTree&);\n            IdTree& operator= (const IdTree&);\n\n            unsigned int foldIndexAddress(const QModelIndex& index) const;\n            std::pair<int, int> unfoldIndexAddress(unsigned int id) const;\n\n        public:\n\n            IdTree (NestedCollection *nestedCollection, CollectionBase *idCollection, unsigned int features = 0);\n            ///< The ownerships of \\a nestedCollecton and \\a idCollection are not transferred.\n\n            virtual ~IdTree();\n\n            int rowCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            int columnCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            QVariant data  (const QModelIndex & index, int role = Qt::DisplayRole) const override;\n\n            bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;\n\n            Qt::ItemFlags flags (const QModelIndex & index) const override;\n\n            bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()) override;\n\n            QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const override;\n\n            QModelIndex parent (const QModelIndex& index) const override;\n\n            QModelIndex getNestedModelIndex (const std::string& id, int column) const;\n\n            QVariant nestedHeaderData(int section, int subSection, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n\n            NestedTableWrapperBase* nestedTable(const QModelIndex &index) const;\n\n            void setNestedTable(const QModelIndex &index, const NestedTableWrapperBase& nestedTable);\n\n            void addNestedRow (const QModelIndex& parent, int position);\n\n            bool hasChildren (const QModelIndex& index) const override;\n\n            virtual int searchNestedColumnIndex(int parentColumn, Columns::ColumnId id);\n            ///< \\return the column index or -1 if the requested column wasn't found.\n\n            virtual int findNestedColumnIndex(int parentColumn, Columns::ColumnId id);\n            ///< \\return the column index or throws an exception if the requested column wasn't found.\n\n    signals:\n\n        void resetStart(const QString& id);\n\n        void resetEnd(const QString& id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/info.hpp",
    "content": "#ifndef CSM_WOLRD_INFO_H\n#define CSM_WOLRD_INFO_H\n\n#include <components/esm/loadinfo.hpp>\n\nnamespace CSMWorld\n{\n    struct Info : public ESM::DialInfo\n    {\n        std::string mTopicId;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/infocollection.cpp",
    "content": "#include \"infocollection.hpp\"\n\n#include <stdexcept>\n#include <iterator>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/loaddial.hpp>\n\n#include <components/misc/stringops.hpp>\n\nvoid CSMWorld::InfoCollection::load (const Info& record, bool base)\n{\n    int index = searchId (record.mId);\n\n    if (index==-1)\n    {\n        // new record\n        Record<Info> record2;\n        record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;\n        (base ? record2.mBase : record2.mModified) = record;\n\n        std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId);\n\n        if (!record2.get().mPrev.empty())\n        {\n            index = getInfoIndex (record2.get().mPrev, topic);\n\n            if (index!=-1)\n                ++index;\n        }\n\n        if (index==-1 && !record2.get().mNext.empty())\n        {\n            index = getInfoIndex (record2.get().mNext, topic);\n        }\n\n        if (index==-1)\n        {\n            Range range = getTopicRange (topic);\n\n            index = std::distance (getRecords().begin(), range.second);\n        }\n\n        insertRecord (record2, index);\n    }\n    else\n    {\n        // old record\n        Record<Info> record2 = getRecord (index);\n\n        if (base)\n            record2.mBase = record;\n        else\n            record2.setModified (record);\n\n        setRecord (index, record2);\n    }\n}\n\nint CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const\n{\n    std::string fullId = Misc::StringUtils::lowerCase (topic) + \"#\" +  id;\n\n    std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic);\n\n    for (; range.first!=range.second; ++range.first)\n        if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId))\n            return std::distance (getRecords().begin(), range.first);\n\n    return -1;\n}\n\nint CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const\n{\n    std::string::size_type separator = id.find_last_of ('#');\n\n    if (separator==std::string::npos)\n        throw std::runtime_error (\"invalid info ID: \" + id);\n\n    std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (id.substr (0, separator));\n\n    if (range.first==range.second)\n        return Collection<Info, IdAccessor<Info> >::getAppendIndex (id, type);\n\n    return std::distance (getRecords().begin(), range.second);\n}\n\nbool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)\n{\n    // check if the range is valid\n    int lastIndex = baseIndex + newOrder.size() -1;\n\n    if (lastIndex>=getSize())\n        return false;\n\n    // Check that topics match\n    if (!Misc::StringUtils::ciEqual(getRecord(baseIndex).get().mTopicId,\n                                    getRecord(lastIndex).get().mTopicId))\n        return false;\n\n    // reorder\n    return reorderRowsImp (baseIndex, newOrder);\n}\n\nvoid CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)\n{\n    Info info;\n    bool isDeleted = false;\n\n    info.load (reader, isDeleted);\n    std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + \"#\" + info.mId;\n\n    if (isDeleted)\n    {\n        int index = searchId (id);\n\n        if (index==-1)\n        {\n            // deleting a record that does not exist\n            // ignore it for now\n            /// \\todo report the problem to the user\n        }\n        else if (base)\n        {\n            removeRows (index, 1);\n        }\n        else\n        {\n            Record<Info> record = getRecord (index);\n            record.mState = RecordBase::State_Deleted;\n            setRecord (index, record);\n        }\n    }\n    else\n    {\n        info.mTopicId = dialogue.mId;\n        info.mId = id;\n        load (info, base);\n    }\n}\n\nCSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)\n    const\n{\n    std::string topic2 = Misc::StringUtils::lowerCase (topic);\n\n    std::map<std::string, int>::const_iterator iter = getIdMap().lower_bound (topic2);\n\n    // Skip invalid records: The beginning of a topic string could be identical to another topic\n    // string.\n    for (; iter!=getIdMap().end(); ++iter)\n    {\n        std::string testTopicId =\n            Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId);\n\n        if (testTopicId==topic2)\n            break;\n\n        std::size_t size = topic2.size();\n\n        if (testTopicId.size()<size || testTopicId.substr (0, size)!=topic2)\n            return Range (getRecords().end(), getRecords().end());\n    }\n\n    if (iter==getIdMap().end())\n        return Range (getRecords().end(), getRecords().end());\n\n    RecordConstIterator begin = getRecords().begin()+iter->second;\n\n    while (begin != getRecords().begin())\n    {\n        if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2))\n        {\n            // we've gone one too far, go back\n            ++begin;\n            break;\n        }\n        --begin;\n    }\n\n    // Find end\n    RecordConstIterator end = begin;\n\n    for (; end!=getRecords().end(); ++end)\n        if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2))\n            break;\n\n    return Range (begin, end);\n}\n\nvoid CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId)\n{\n    std::string id = Misc::StringUtils::lowerCase(dialogueId);\n    std::vector<int> erasedRecords;\n\n    std::map<std::string, int>::const_iterator current = getIdMap().lower_bound(id);\n    std::map<std::string, int>::const_iterator end = getIdMap().end();\n    for (; current != end; ++current)\n    {\n        Record<Info> record = getRecord(current->second);\n\n        if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))\n        {\n            if (record.mState == RecordBase::State_ModifiedOnly)\n            {\n                erasedRecords.push_back(current->second);\n            }\n            else\n            {\n                record.mState = RecordBase::State_Deleted;\n                setRecord(current->second, record);\n            }\n        }\n        else\n        {\n            break;\n        }\n    }\n\n    while (!erasedRecords.empty())\n    {\n        removeRows(erasedRecords.back(), 1);\n        erasedRecords.pop_back();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/infocollection.hpp",
    "content": "#ifndef CSM_WOLRD_INFOCOLLECTION_H\n#define CSM_WOLRD_INFOCOLLECTION_H\n\n#include \"collection.hpp\"\n#include \"info.hpp\"\n\nnamespace ESM\n{\n    struct Dialogue;\n}\n\nnamespace CSMWorld\n{\n    class InfoCollection : public Collection<Info, IdAccessor<Info> >\n    {\n        public:\n\n            typedef std::vector<Record<Info> >::const_iterator RecordConstIterator;\n            typedef std::pair<RecordConstIterator, RecordConstIterator> Range;\n\n        private:\n\n            void load (const Info& record, bool base);\n\n            int getInfoIndex (const std::string& id, const std::string& topic) const;\n            ///< Return index for record \\a id or -1 (if not present; deleted records are considered)\n            ///\n            /// \\param id info ID without topic prefix\n\n        public:\n\n            int getAppendIndex (const std::string& id,\n                UniversalId::Type type = UniversalId::Type_None) const override;\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            bool reorderRows (int baseIndex, const std::vector<int>& newOrder) override;\n            ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices\n            /// given in \\a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).\n            ///\n            /// \\return Success?\n\n            void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue);\n\n            Range getTopicRange (const std::string& topic) const;\n            ///< Return iterators that point to the beginning and past the end of the range for\n            /// the given topic.\n\n            void removeDialogueInfos(const std::string& dialogueId);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/infoselectwrapper.cpp",
    "content": "#include \"infoselectwrapper.hpp\"\n\n#include <limits>\n#include <sstream>\n#include <stdexcept>\n\nconst size_t CSMWorld::ConstInfoSelectWrapper::RuleMinSize = 5;\n\nconst size_t CSMWorld::ConstInfoSelectWrapper::FunctionPrefixOffset = 1;\nconst size_t CSMWorld::ConstInfoSelectWrapper::FunctionIndexOffset = 2;\nconst size_t CSMWorld::ConstInfoSelectWrapper::RelationIndexOffset = 4;\nconst size_t CSMWorld::ConstInfoSelectWrapper::VarNameOffset = 5;\n\nconst char* CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[] =\n{\n    \"Rank Low\",\n    \"Rank High\",\n    \"Rank Requirement\",\n    \"Reputation\",\n    \"Health Percent\",\n    \"PC Reputation\",\n    \"PC Level\",\n    \"PC Health Percent\",\n    \"PC Magicka\",\n    \"PC Fatigue\",\n    \"PC Strength\",\n    \"PC Block\",\n    \"PC Armorer\",\n    \"PC Medium Armor\",\n    \"PC Heavy Armor\",\n    \"PC Blunt Weapon\",\n    \"PC Long Blade\",\n    \"PC Axe\",\n    \"PC Spear\",\n    \"PC Athletics\",\n    \"PC Enchant\",\n    \"PC Detruction\",\n    \"PC Alteration\",\n    \"PC Illusion\",\n    \"PC Conjuration\",\n    \"PC Mysticism\",\n    \"PC Restoration\",\n    \"PC Alchemy\",\n    \"PC Unarmored\",\n    \"PC Security\",\n    \"PC Sneak\",\n    \"PC Acrobatics\",\n    \"PC Light Armor\",\n    \"PC Short Blade\",\n    \"PC Marksman\",\n    \"PC Merchantile\",\n    \"PC Speechcraft\",\n    \"PC Hand to Hand\",\n    \"PC Sex\",\n    \"PC Expelled\",\n    \"PC Common Disease\",\n    \"PC Blight Disease\",\n    \"PC Clothing Modifier\",\n    \"PC Crime Level\",\n    \"Same Sex\",\n    \"Same Race\",\n    \"Same Faction\",\n    \"Faction Rank Difference\",\n    \"Detected\",\n    \"Alarmed\",\n    \"Choice\",\n    \"PC Intelligence\",\n    \"PC Willpower\",\n    \"PC Agility\",\n    \"PC Speed\",\n    \"PC Endurance\",\n    \"PC Personality\",\n    \"PC Luck\",\n    \"PC Corpus\",\n    \"Weather\",\n    \"PC Vampire\",\n    \"Level\",\n    \"Attacked\",\n    \"Talked to PC\",\n    \"PC Health\",\n    \"Creature Target\",\n    \"Friend Hit\",\n    \"Fight\",\n    \"Hello\",\n    \"Alarm\",\n    \"Flee\",\n    \"Should Attack\",\n    \"Werewolf\",\n    \"PC Werewolf Kills\",\n    \"Global\",\n    \"Local\",\n    \"Journal\",\n    \"Item\",\n    \"Dead\",\n    \"Not Id\",\n    \"Not Faction\",\n    \"Not Class\",\n    \"Not Race\",\n    \"Not Cell\",\n    \"Not Local\",\n    0\n};\n\nconst char* CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings[] =\n{\n    \"=\",\n    \"!=\",\n    \">\",\n    \">=\",\n    \"<\",\n    \"<=\",\n    0\n};\n\nconst char* CSMWorld::ConstInfoSelectWrapper::ComparisonEnumStrings[] =\n{\n    \"Boolean\",\n    \"Integer\",\n    \"Numeric\",\n    0\n};\n\n// static functions\n\nstd::string CSMWorld::ConstInfoSelectWrapper::convertToString(FunctionName name)\n{\n    if (name < Function_None)\n        return FunctionEnumStrings[name];\n    else\n        return \"(Invalid Data: Function)\";\n}\n\nstd::string CSMWorld::ConstInfoSelectWrapper::convertToString(RelationType type)\n{\n    if (type < Relation_None)\n        return RelationEnumStrings[type];\n    else\n        return \"(Invalid Data: Relation)\";\n}\n\nstd::string CSMWorld::ConstInfoSelectWrapper::convertToString(ComparisonType type)\n{\n    if (type < Comparison_None)\n        return ComparisonEnumStrings[type];\n    else\n        return \"(Invalid Data: Comparison)\";\n}\n\n// ConstInfoSelectWrapper\n\nCSMWorld::ConstInfoSelectWrapper::ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select)\n    : mConstSelect(select)\n{\n    readRule();\n}\n\nCSMWorld::ConstInfoSelectWrapper::FunctionName CSMWorld::ConstInfoSelectWrapper::getFunctionName() const\n{\n    return mFunctionName;\n}\n\nCSMWorld::ConstInfoSelectWrapper::RelationType CSMWorld::ConstInfoSelectWrapper::getRelationType() const\n{\n    return mRelationType;\n}\n\nCSMWorld::ConstInfoSelectWrapper::ComparisonType CSMWorld::ConstInfoSelectWrapper::getComparisonType() const\n{\n    return mComparisonType;\n}\n\nbool CSMWorld::ConstInfoSelectWrapper::hasVariable() const\n{\n    return mHasVariable;\n}\n\nconst std::string& CSMWorld::ConstInfoSelectWrapper::getVariableName() const\n{\n    return mVariableName;\n}\n\nbool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue() const\n{\n    if (!variantTypeIsValid())\n        return false;\n\n    if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer)\n    {\n        if (mConstSelect.mValue.getType() == ESM::VT_Float)\n            return conditionIsAlwaysTrue(getConditionFloatRange(), getValidIntRange());\n        else\n            return conditionIsAlwaysTrue(getConditionIntRange(), getValidIntRange());\n    }\n    else if (mComparisonType == Comparison_Numeric)\n    {\n        if (mConstSelect.mValue.getType() == ESM::VT_Float)\n            return conditionIsAlwaysTrue(getConditionFloatRange(), getValidFloatRange());\n        else\n            return conditionIsAlwaysTrue(getConditionIntRange(), getValidFloatRange());\n    }\n\n    return false;\n}\n\nbool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue() const\n{\n    if (!variantTypeIsValid())\n        return false;\n\n    if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer)\n    {\n        if (mConstSelect.mValue.getType() == ESM::VT_Float)\n            return conditionIsNeverTrue(getConditionFloatRange(), getValidIntRange());\n        else\n            return conditionIsNeverTrue(getConditionIntRange(), getValidIntRange());\n    }\n    else if (mComparisonType == Comparison_Numeric)\n    {\n        if (mConstSelect.mValue.getType() == ESM::VT_Float)\n            return conditionIsNeverTrue(getConditionFloatRange(), getValidFloatRange());\n        else\n            return conditionIsNeverTrue(getConditionIntRange(), getValidFloatRange());\n    }\n\n    return false;\n}\n\nbool CSMWorld::ConstInfoSelectWrapper::variantTypeIsValid() const\n{\n    return (mConstSelect.mValue.getType() == ESM::VT_Int || mConstSelect.mValue.getType() == ESM::VT_Float);\n}\n\nconst ESM::Variant& CSMWorld::ConstInfoSelectWrapper::getVariant() const\n{\n    return mConstSelect.mValue;\n}\n\nstd::string CSMWorld::ConstInfoSelectWrapper::toString() const\n{\n    std::ostringstream stream;\n    stream << convertToString(mFunctionName) << \" \";\n\n    if (mHasVariable)\n        stream << mVariableName << \" \";\n\n    stream << convertToString(mRelationType) << \" \";\n\n    switch (mConstSelect.mValue.getType())\n    {\n        case ESM::VT_Int:\n            stream << mConstSelect.mValue.getInteger();\n            break;\n\n        case ESM::VT_Float:\n            stream << mConstSelect.mValue.getFloat();\n            break;\n\n        default:\n            stream << \"(Invalid value type)\";\n            break;\n    }\n\n    return stream.str();\n}\n\nvoid CSMWorld::ConstInfoSelectWrapper::readRule()\n{\n    if (mConstSelect.mSelectRule.size() < RuleMinSize)\n        throw std::runtime_error(\"InfoSelectWrapper: rule is to small\");\n\n    readFunctionName();\n    readRelationType();\n    readVariableName();\n    updateHasVariable();\n    updateComparisonType();\n}\n\nvoid CSMWorld::ConstInfoSelectWrapper::readFunctionName()\n{\n    char functionPrefix = mConstSelect.mSelectRule[FunctionPrefixOffset];\n    std::string functionIndex = mConstSelect.mSelectRule.substr(FunctionIndexOffset, 2);\n    int convertedIndex = -1;\n\n    // Read in function index, form ## from 00 .. 73, skip leading zero\n    if (functionIndex[0] == '0')\n        functionIndex = functionIndex[1];\n\n    std::stringstream stream;\n    stream << functionIndex;\n    stream >> convertedIndex;\n\n    switch (functionPrefix)\n    {\n        case '1':\n            if (convertedIndex >= 0 && convertedIndex <= 73)\n                mFunctionName = static_cast<FunctionName>(convertedIndex);\n            else\n                mFunctionName = Function_None;\n            break;\n\n        case '2': mFunctionName = Function_Global; break;\n        case '3': mFunctionName = Function_Local; break;\n        case '4': mFunctionName = Function_Journal; break;\n        case '5': mFunctionName = Function_Item; break;\n        case '6': mFunctionName = Function_Dead; break;\n        case '7': mFunctionName = Function_NotId; break;\n        case '8': mFunctionName = Function_NotFaction; break;\n        case '9': mFunctionName = Function_NotClass; break;\n        case 'A': mFunctionName = Function_NotRace; break;\n        case 'B': mFunctionName = Function_NotCell; break;\n        case 'C': mFunctionName = Function_NotLocal; break;\n        default:  mFunctionName = Function_None; break;\n    }\n}\n\nvoid CSMWorld::ConstInfoSelectWrapper::readRelationType()\n{\n    char relationIndex = mConstSelect.mSelectRule[RelationIndexOffset];\n\n    switch (relationIndex)\n    {\n        case '0': mRelationType = Relation_Equal; break;\n        case '1': mRelationType = Relation_NotEqual; break;\n        case '2': mRelationType = Relation_Greater; break;\n        case '3': mRelationType = Relation_GreaterOrEqual; break;\n        case '4': mRelationType = Relation_Less; break;\n        case '5': mRelationType = Relation_LessOrEqual; break;\n        default:  mRelationType = Relation_None;\n    }\n}\n\nvoid CSMWorld::ConstInfoSelectWrapper::readVariableName()\n{\n    if (mConstSelect.mSelectRule.size() >= VarNameOffset)\n        mVariableName = mConstSelect.mSelectRule.substr(VarNameOffset);\n    else\n        mVariableName.clear();\n}\n\nvoid CSMWorld::ConstInfoSelectWrapper::updateHasVariable()\n{\n    switch (mFunctionName)\n    {\n        case Function_Global:\n        case Function_Local:\n        case Function_Journal:\n        case Function_Item:\n        case Function_Dead:\n        case Function_NotId:\n        case Function_NotFaction:\n        case Function_NotClass:\n        case Function_NotRace:\n        case Function_NotCell:\n        case Function_NotLocal:\n            mHasVariable = true;\n            break;\n\n        default:\n            mHasVariable = false;\n            break;\n    }\n}\n\nvoid CSMWorld::ConstInfoSelectWrapper::updateComparisonType()\n{\n    switch (mFunctionName)\n    {\n        // Boolean\n        case Function_NotId:\n        case Function_NotFaction:\n        case Function_NotClass:\n        case Function_NotRace:\n        case Function_NotCell:\n        case Function_PcExpelled:\n        case Function_PcCommonDisease:\n        case Function_PcBlightDisease:\n        case Function_SameSex:\n        case Function_SameRace:\n        case Function_SameFaction:\n        case Function_Detected:\n        case Function_Alarmed:\n        case Function_PcCorpus:\n        case Function_PcVampire:\n        case Function_Attacked:\n        case Function_TalkedToPc:\n        case Function_ShouldAttack:\n        case Function_Werewolf:\n            mComparisonType = Comparison_Boolean;\n            break;\n\n        // Integer\n        case Function_Journal:\n        case Function_Item:\n        case Function_Dead:\n        case Function_RankLow:\n        case Function_RankHigh:\n        case Function_RankRequirement:\n        case Function_Reputation:\n        case Function_PcReputation:\n        case Function_PcLevel:\n        case Function_PcStrength:\n        case Function_PcBlock:\n        case Function_PcArmorer:\n        case Function_PcMediumArmor:\n        case Function_PcHeavyArmor:\n        case Function_PcBluntWeapon:\n        case Function_PcLongBlade:\n        case Function_PcAxe:\n        case Function_PcSpear:\n        case Function_PcAthletics:\n        case Function_PcEnchant:\n        case Function_PcDestruction:\n        case Function_PcAlteration:\n        case Function_PcIllusion:\n        case Function_PcConjuration:\n        case Function_PcMysticism:\n        case Function_PcRestoration:\n        case Function_PcAlchemy:\n        case Function_PcUnarmored:\n        case Function_PcSecurity:\n        case Function_PcSneak:\n        case Function_PcAcrobatics:\n        case Function_PcLightArmor:\n        case Function_PcShortBlade:\n        case Function_PcMarksman:\n        case Function_PcMerchantile:\n        case Function_PcSpeechcraft:\n        case Function_PcHandToHand:\n        case Function_PcGender:\n        case Function_PcClothingModifier:\n        case Function_PcCrimeLevel:\n        case Function_FactionRankDifference:\n        case Function_Choice:\n        case Function_PcIntelligence:\n        case Function_PcWillpower:\n        case Function_PcAgility:\n        case Function_PcSpeed:\n        case Function_PcEndurance:\n        case Function_PcPersonality:\n        case Function_PcLuck:\n        case Function_Weather:\n        case Function_Level:\n        case Function_CreatureTarget:\n        case Function_FriendHit:\n        case Function_Fight:\n        case Function_Hello:\n        case Function_Alarm:\n        case Function_Flee:\n        case Function_PcWerewolfKills:\n            mComparisonType = Comparison_Integer;\n            break;\n\n        // Numeric\n        case Function_Global:\n        case Function_Local:\n        case Function_NotLocal:\n\n        case Function_Health_Percent:\n        case Function_PcHealthPercent:\n        case Function_PcMagicka:\n        case Function_PcFatigue:\n        case Function_PcHealth:\n            mComparisonType = Comparison_Numeric;\n            break;\n\n        default:\n            mComparisonType = Comparison_None;\n            break;\n    }\n}\n\nstd::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() const\n{\n    const int IntMax = std::numeric_limits<int>::max();\n    const int IntMin = std::numeric_limits<int>::min();\n    const std::pair<int, int> InvalidRange(IntMax, IntMin);\n\n    int value = mConstSelect.mValue.getInteger();\n\n    switch (mRelationType)\n    {\n        case Relation_Equal:\n        case Relation_NotEqual:\n            return std::pair<int, int>(value, value);\n\n        case Relation_Greater:\n            if (value == IntMax)\n            {\n                return InvalidRange;\n            }\n            else\n            {\n                return std::pair<int, int>(value + 1, IntMax);\n            }\n            break;\n\n        case Relation_GreaterOrEqual:\n            return std::pair<int, int>(value, IntMax);\n\n        case Relation_Less:\n            if (value == IntMin)\n            {\n                return InvalidRange;\n            }\n            else\n            {\n                return std::pair<int, int>(IntMin, value - 1);\n            }\n\n        case Relation_LessOrEqual:\n            return std::pair<int, int>(IntMin, value);\n\n        default:\n            throw std::logic_error(\"InfoSelectWrapper: relation does not have a range\");\n    }\n}\n\nstd::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange() const\n{\n    const float FloatMax = std::numeric_limits<float>::infinity();\n    const float FloatMin = -std::numeric_limits<float>::infinity();\n    const float Epsilon = std::numeric_limits<float>::epsilon();\n\n    float value = mConstSelect.mValue.getFloat();\n\n    switch (mRelationType)\n    {\n        case Relation_Equal:\n        case Relation_NotEqual:\n            return std::pair<float, float>(value, value);\n\n        case Relation_Greater:\n            return std::pair<float, float>(value + Epsilon, FloatMax);\n\n        case Relation_GreaterOrEqual:\n            return std::pair<float, float>(value, FloatMax);\n\n        case Relation_Less:\n            return std::pair<float, float>(FloatMin, value - Epsilon);\n\n        case Relation_LessOrEqual:\n            return std::pair<float, float>(FloatMin, value);\n\n        default:\n            throw std::logic_error(\"InfoSelectWrapper: given relation does not have a range\");\n    }\n}\n\nstd::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getValidIntRange() const\n{\n    const int IntMax = std::numeric_limits<int>::max();\n    const int IntMin = std::numeric_limits<int>::min();\n\n    switch (mFunctionName)\n    {\n        // Boolean\n        case Function_NotId:\n        case Function_NotFaction:\n        case Function_NotClass:\n        case Function_NotRace:\n        case Function_NotCell:\n        case Function_PcExpelled:\n        case Function_PcCommonDisease:\n        case Function_PcBlightDisease:\n        case Function_SameSex:\n        case Function_SameRace:\n        case Function_SameFaction:\n        case Function_Detected:\n        case Function_Alarmed:\n        case Function_PcCorpus:\n        case Function_PcVampire:\n        case Function_Attacked:\n        case Function_TalkedToPc:\n        case Function_ShouldAttack:\n        case Function_Werewolf:\n            return std::pair<int, int>(0, 1);\n\n        // Integer\n        case Function_RankLow:\n        case Function_RankHigh:\n        case Function_Reputation:\n        case Function_PcReputation:\n        case Function_Journal:\n            return std::pair<int, int>(IntMin, IntMax);\n\n        case Function_Item:\n        case Function_Dead:\n        case Function_PcLevel:\n        case Function_PcStrength:\n        case Function_PcBlock:\n        case Function_PcArmorer:\n        case Function_PcMediumArmor:\n        case Function_PcHeavyArmor:\n        case Function_PcBluntWeapon:\n        case Function_PcLongBlade:\n        case Function_PcAxe:\n        case Function_PcSpear:\n        case Function_PcAthletics:\n        case Function_PcEnchant:\n        case Function_PcDestruction:\n        case Function_PcAlteration:\n        case Function_PcIllusion:\n        case Function_PcConjuration:\n        case Function_PcMysticism:\n        case Function_PcRestoration:\n        case Function_PcAlchemy:\n        case Function_PcUnarmored:\n        case Function_PcSecurity:\n        case Function_PcSneak:\n        case Function_PcAcrobatics:\n        case Function_PcLightArmor:\n        case Function_PcShortBlade:\n        case Function_PcMarksman:\n        case Function_PcMerchantile:\n        case Function_PcSpeechcraft:\n        case Function_PcHandToHand:\n        case Function_PcClothingModifier:\n        case Function_PcCrimeLevel:\n        case Function_Choice:\n        case Function_PcIntelligence:\n        case Function_PcWillpower:\n        case Function_PcAgility:\n        case Function_PcSpeed:\n        case Function_PcEndurance:\n        case Function_PcPersonality:\n        case Function_PcLuck:\n        case Function_Level:\n        case Function_PcWerewolfKills:\n            return std::pair<int, int>(0, IntMax);\n\n        case Function_Fight:\n        case Function_Hello:\n        case Function_Alarm:\n        case Function_Flee:\n            return std::pair<int, int>(0, 100);\n\n        case Function_Weather:\n            return std::pair<int, int>(0, 9);\n\n        case Function_FriendHit:\n            return std::pair<int, int>(0, 4);\n\n        case Function_RankRequirement:\n            return std::pair<int, int>(0, 3);\n\n        case Function_CreatureTarget:\n            return std::pair<int, int>(0, 2);\n\n        case Function_PcGender:\n            return std::pair<int, int>(0, 1);\n\n        case Function_FactionRankDifference:\n            return std::pair<int, int>(-9, 9);\n\n        // Numeric\n        case Function_Global:\n        case Function_Local:\n        case Function_NotLocal:\n            return std::pair<int, int>(IntMin, IntMax);\n\n        case Function_PcMagicka:\n        case Function_PcFatigue:\n        case Function_PcHealth:\n            return std::pair<int, int>(0, IntMax);\n\n        case Function_Health_Percent:\n        case Function_PcHealthPercent:\n            return std::pair<int, int>(0, 100);\n\n        default:\n            throw std::runtime_error(\"InfoSelectWrapper: function does not exist\");\n    }\n}\n\nstd::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getValidFloatRange() const\n{\n    const float FloatMax = std::numeric_limits<float>::infinity();\n    const float FloatMin = -std::numeric_limits<float>::infinity();\n\n    switch (mFunctionName)\n    {\n        // Numeric\n        case Function_Global:\n        case Function_Local:\n        case Function_NotLocal:\n            return std::pair<float, float>(FloatMin, FloatMax);\n\n        case Function_PcMagicka:\n        case Function_PcFatigue:\n        case Function_PcHealth:\n            return std::pair<float, float>(0, FloatMax);\n\n        case Function_Health_Percent:\n        case Function_PcHealthPercent:\n            return std::pair<float, float>(0, 100);\n\n        default:\n            throw std::runtime_error(\"InfoSelectWrapper: function does not exist or is not numeric\");\n    }\n}\n\ntemplate <typename T1, typename T2>\nbool CSMWorld::ConstInfoSelectWrapper::rangeContains(T1 value, std::pair<T2,T2> range) const\n{\n    return (value >= range.first && value <= range.second);\n}\n\ntemplate <typename T1, typename T2>\nbool CSMWorld::ConstInfoSelectWrapper::rangeFullyContains(std::pair<T1,T1> containingRange,\n    std::pair<T2,T2> testRange) const\n{\n    return (containingRange.first <= testRange.first) && (testRange.second <= containingRange.second);\n}\n\ntemplate <typename T1, typename T2>\nbool CSMWorld::ConstInfoSelectWrapper::rangesOverlap(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const\n{\n    // One of the bounds of either range should fall within the other range\n    return\n        (range1.first <= range2.first  && range2.first  <= range1.second) ||\n        (range1.first <= range2.second && range2.second <= range1.second) ||\n        (range2.first <= range1.first  && range1.first  <= range2.second) ||\n        (range2.first <= range1.second && range1.second <= range2.second);\n}\n\ntemplate <typename T1, typename T2>\nbool CSMWorld::ConstInfoSelectWrapper::rangesMatch(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const\n{\n    return (range1.first == range2.first  && range1.second == range2.second);\n}\n\ntemplate <typename T1, typename T2>\nbool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue(std::pair<T1,T1> conditionRange,\n    std::pair<T2,T2> validRange) const\n{\n    switch (mRelationType)\n    {\n        case Relation_Equal:\n            return false;\n\n        case Relation_NotEqual:\n            // If value is not within range, it will always be true\n            return !rangeContains(conditionRange.first, validRange);\n\n        case Relation_Greater:\n        case Relation_GreaterOrEqual:\n        case Relation_Less:\n        case Relation_LessOrEqual:\n            // If the valid range is completely within the condition range, it will always be true\n            return rangeFullyContains(conditionRange, validRange);\n\n        default:\n            throw std::logic_error(\"InfoCondition: operator can not be used to compare\");\n    }\n\n    return false;\n}\n\ntemplate <typename T1, typename T2>\nbool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue(std::pair<T1,T1> conditionRange,\n    std::pair<T2,T2> validRange) const\n{\n    switch (mRelationType)\n    {\n        case Relation_Equal:\n            return !rangeContains(conditionRange.first, validRange);\n\n        case Relation_NotEqual:\n            return false;\n\n        case Relation_Greater:\n        case Relation_GreaterOrEqual:\n        case Relation_Less:\n        case Relation_LessOrEqual:\n            // If ranges do not overlap, it will never be true\n            return !rangesOverlap(conditionRange, validRange);\n\n        default:\n            throw std::logic_error(\"InfoCondition: operator can not be used to compare\");\n    }\n\n    return false;\n}\n\n// InfoSelectWrapper\n\nCSMWorld::InfoSelectWrapper::InfoSelectWrapper(ESM::DialInfo::SelectStruct& select)\n    : CSMWorld::ConstInfoSelectWrapper(select), mSelect(select)\n{\n}\n\nvoid CSMWorld::InfoSelectWrapper::setFunctionName(FunctionName name)\n{\n    mFunctionName = name;\n    updateHasVariable();\n    updateComparisonType();\n}\n\nvoid CSMWorld::InfoSelectWrapper::setRelationType(RelationType type)\n{\n    mRelationType = type;\n}\n\nvoid CSMWorld::InfoSelectWrapper::setVariableName(const std::string& name)\n{\n    mVariableName = name;\n}\n\nvoid CSMWorld::InfoSelectWrapper::setDefaults()\n{\n    if (!variantTypeIsValid())\n        mSelect.mValue.setType(ESM::VT_Int);\n\n    switch (mComparisonType)\n    {\n        case Comparison_Boolean:\n            setRelationType(Relation_Equal);\n            mSelect.mValue.setInteger(1);\n            break;\n\n        case Comparison_Integer:\n        case Comparison_Numeric:\n            setRelationType(Relation_Greater);\n            mSelect.mValue.setInteger(0);\n            break;\n\n        default:\n            // Do nothing\n            break;\n    }\n\n    update();\n}\n\nvoid CSMWorld::InfoSelectWrapper::update()\n{\n    std::ostringstream stream;\n\n    // Leading 0\n    stream << '0';\n\n    // Write Function\n\n    bool writeIndex = false;\n    size_t functionIndex = static_cast<size_t>(mFunctionName);\n\n    switch (mFunctionName)\n    {\n        case Function_None:         stream << '0'; break;\n        case Function_Global:       stream << '2'; break;\n        case Function_Local:        stream << '3'; break;\n        case Function_Journal:      stream << '4'; break;\n        case Function_Item:         stream << '5'; break;\n        case Function_Dead:         stream << '6'; break;\n        case Function_NotId:        stream << '7'; break;\n        case Function_NotFaction:   stream << '8'; break;\n        case Function_NotClass:     stream << '9'; break;\n        case Function_NotRace:      stream << 'A'; break;\n        case Function_NotCell:      stream << 'B'; break;\n        case Function_NotLocal:     stream << 'C'; break;\n        default:                    stream << '1'; writeIndex = true; break;\n    }\n\n    if (writeIndex && functionIndex < 10) // leading 0\n        stream << '0' << functionIndex;\n    else if (writeIndex)\n        stream << functionIndex;\n    else\n        stream << \"00\";\n\n    // Write Relation\n    switch (mRelationType)\n    {\n        case Relation_Equal:            stream << '0'; break;\n        case Relation_NotEqual:         stream << '1'; break;\n        case Relation_Greater:          stream << '2'; break;\n        case Relation_GreaterOrEqual:   stream << '3'; break;\n        case Relation_Less:             stream << '4'; break;\n        case Relation_LessOrEqual:      stream << '5'; break;\n        default:                        stream << '0'; break;\n    }\n\n    if (mHasVariable)\n        stream << mVariableName;\n\n    mSelect.mSelectRule = stream.str();\n}\n\nESM::Variant& CSMWorld::InfoSelectWrapper::getVariant()\n{\n    return mSelect.mValue;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/infoselectwrapper.hpp",
    "content": "#ifndef CSM_WORLD_INFOSELECTWRAPPER_H\n#define CSM_WORLD_INFOSELECTWRAPPER_H\n\n#include <components/esm/loadinfo.hpp>\n\nnamespace CSMWorld\n{\n    // ESM::DialInfo::SelectStruct.mSelectRule\n    // 012345...\n    // ^^^ ^^\n    // ||| ||\n    // ||| |+------------- condition variable string\n    // ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc\n    // ||+---------------- function index (encoded, where function == '1')\n    // |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc\n    // +------------------ unknown\n    //\n\n    // Wrapper for DialInfo::SelectStruct\n    class ConstInfoSelectWrapper\n    {\n    public:\n\n        // Order matters\n        enum FunctionName\n        {\n            Function_RankLow=0,\n            Function_RankHigh,\n            Function_RankRequirement,\n            Function_Reputation,\n            Function_Health_Percent,\n            Function_PcReputation,\n            Function_PcLevel,\n            Function_PcHealthPercent,\n            Function_PcMagicka,\n            Function_PcFatigue,\n            Function_PcStrength,\n            Function_PcBlock,\n            Function_PcArmorer,\n            Function_PcMediumArmor,\n            Function_PcHeavyArmor,\n            Function_PcBluntWeapon,\n            Function_PcLongBlade,\n            Function_PcAxe,\n            Function_PcSpear,\n            Function_PcAthletics,\n            Function_PcEnchant,\n            Function_PcDestruction,\n            Function_PcAlteration,\n            Function_PcIllusion,\n            Function_PcConjuration,\n            Function_PcMysticism,\n            Function_PcRestoration,\n            Function_PcAlchemy,\n            Function_PcUnarmored,\n            Function_PcSecurity,\n            Function_PcSneak,\n            Function_PcAcrobatics,\n            Function_PcLightArmor,\n            Function_PcShortBlade,\n            Function_PcMarksman,\n            Function_PcMerchantile,\n            Function_PcSpeechcraft,\n            Function_PcHandToHand,\n            Function_PcGender,\n            Function_PcExpelled,\n            Function_PcCommonDisease,\n            Function_PcBlightDisease,\n            Function_PcClothingModifier,\n            Function_PcCrimeLevel,\n            Function_SameSex,\n            Function_SameRace,\n            Function_SameFaction,\n            Function_FactionRankDifference,\n            Function_Detected,\n            Function_Alarmed,\n            Function_Choice,\n            Function_PcIntelligence,\n            Function_PcWillpower,\n            Function_PcAgility,\n            Function_PcSpeed,\n            Function_PcEndurance,\n            Function_PcPersonality,\n            Function_PcLuck,\n            Function_PcCorpus,\n            Function_Weather,\n            Function_PcVampire,\n            Function_Level,\n            Function_Attacked,\n            Function_TalkedToPc,\n            Function_PcHealth,\n            Function_CreatureTarget,\n            Function_FriendHit,\n            Function_Fight,\n            Function_Hello,\n            Function_Alarm,\n            Function_Flee,\n            Function_ShouldAttack,\n            Function_Werewolf,\n            Function_PcWerewolfKills=73,\n\n            Function_Global,\n            Function_Local,\n            Function_Journal,\n            Function_Item,\n            Function_Dead,\n            Function_NotId,\n            Function_NotFaction,\n            Function_NotClass,\n            Function_NotRace,\n            Function_NotCell,\n            Function_NotLocal,\n\n            Function_None\n        };\n\n        enum RelationType\n        {\n            Relation_Equal,\n            Relation_NotEqual,\n            Relation_Greater,\n            Relation_GreaterOrEqual,\n            Relation_Less,\n            Relation_LessOrEqual,\n\n            Relation_None\n        };\n\n        enum ComparisonType\n        {\n            Comparison_Boolean,\n            Comparison_Integer,\n            Comparison_Numeric,\n\n            Comparison_None\n        };\n\n        static const size_t RuleMinSize;\n\n        static const size_t FunctionPrefixOffset;\n        static const size_t FunctionIndexOffset;\n        static const size_t RelationIndexOffset;\n        static const size_t VarNameOffset;\n\n        static const char* FunctionEnumStrings[];\n        static const char* RelationEnumStrings[];\n        static const char* ComparisonEnumStrings[];\n\n        static std::string convertToString(FunctionName name);\n        static std::string convertToString(RelationType type);\n        static std::string convertToString(ComparisonType type);\n\n        ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select);\n\n        FunctionName getFunctionName() const;\n        RelationType getRelationType() const;\n        ComparisonType getComparisonType() const;\n\n        bool hasVariable() const;\n        const std::string& getVariableName() const;\n\n        bool conditionIsAlwaysTrue() const;\n        bool conditionIsNeverTrue() const;\n        bool variantTypeIsValid() const;\n\n        const ESM::Variant& getVariant() const;\n\n        std::string toString() const;\n\n    protected:\n\n        void readRule();\n        void readFunctionName();\n        void readRelationType();\n        void readVariableName();\n        void updateHasVariable();\n        void updateComparisonType();\n\n        std::pair<int, int> getConditionIntRange() const;\n        std::pair<float, float> getConditionFloatRange() const;\n\n        std::pair<int, int> getValidIntRange() const;\n        std::pair<float, float> getValidFloatRange() const;\n\n        template <typename Type1, typename Type2>\n        bool rangeContains(Type1 value, std::pair<Type2,Type2> range) const;\n\n        template <typename Type1, typename Type2>\n        bool rangesOverlap(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;\n\n        template <typename Type1, typename Type2>\n        bool rangeFullyContains(std::pair<Type1,Type1> containing, std::pair<Type2,Type2> test) const;\n\n        template <typename Type1, typename Type2>\n        bool rangesMatch(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;\n\n        template <typename Type1, typename Type2>\n        bool conditionIsAlwaysTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;\n\n        template <typename Type1, typename Type2>\n        bool conditionIsNeverTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;\n\n        FunctionName mFunctionName;\n        RelationType mRelationType;\n        ComparisonType mComparisonType;\n\n        bool mHasVariable;\n        std::string mVariableName;\n\n    private:\n\n        const ESM::DialInfo::SelectStruct& mConstSelect;\n    };\n\n    // Wrapper for DialInfo::SelectStruct that can modify the wrapped select struct\n    class InfoSelectWrapper : public ConstInfoSelectWrapper\n    {\n    public:\n\n        InfoSelectWrapper(ESM::DialInfo::SelectStruct& select);\n\n        // Wrapped SelectStruct will not be modified until update() is called\n        void setFunctionName(FunctionName name);\n        void setRelationType(RelationType type);\n        void setVariableName(const std::string& name);\n\n        // Modified wrapped SelectStruct\n        void update();\n\n        // This sets properties based on the function name to its defaults and updates the wrapped object\n        void setDefaults();\n\n        ESM::Variant& getVariant();\n\n    private:\n\n        ESM::DialInfo::SelectStruct& mSelect;\n\n        void writeRule();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/infotableproxymodel.cpp",
    "content": "#include \"infotableproxymodel.hpp\"\n\n#include <components/misc/stringops.hpp>\n\n#include \"idtablebase.hpp\"\n#include \"columns.hpp\"\n\nnamespace\n{\n    QString toLower(const QString &str)\n    {\n        return QString::fromUtf8(Misc::StringUtils::lowerCase(str.toUtf8().constData()).c_str());\n    }\n}\n\nCSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type type, QObject *parent)\n    : IdTableProxyModel(parent),\n      mType(type),\n      mInfoColumnId(type == UniversalId::Type_TopicInfos ? Columns::ColumnId_Topic :\n                                                           Columns::ColumnId_Journal),\n      mInfoColumnIndex(-1),\n      mLastAddedSourceRow(-1)\n{\n    Q_ASSERT(type == UniversalId::Type_TopicInfos || type == UniversalId::Type_JournalInfos);\n}\n\nvoid CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceModel)\n{\n    IdTableProxyModel::setSourceModel(sourceModel);\n\n    if (mSourceModel != nullptr)\n    {\n        mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId);\n        mFirstRowCache.clear();\n    }\n}\n\nbool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    Q_ASSERT(mSourceModel != nullptr);\n\n    QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column());\n    QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column());\n\n    // If both indexes are belonged to the same Topic/Journal, compare their original rows only\n    if (first.row() == second.row())\n    {\n        return sortOrder() == Qt::AscendingOrder ? left.row() < right.row() : right.row() < left.row();\n    }\n    return IdTableProxyModel::lessThan(first, second);\n}\n\nint CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const\n{\n    Q_ASSERT(mSourceModel != nullptr);\n\n    int row = currentRow;\n    int column = mInfoColumnIndex;\n    QString info = toLower(mSourceModel->data(mSourceModel->index(row, column)).toString());\n\n    if (mFirstRowCache.contains(info))\n    {\n        return mFirstRowCache[info];\n    }\n\n    while (--row >= 0 && \n           toLower(mSourceModel->data(mSourceModel->index(row, column)).toString()) == info);\n    ++row;\n\n    mFirstRowCache[info] = row;\n    return row;\n}\n\nvoid CSMWorld::InfoTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/)\n{\n    refreshFilter();\n    mFirstRowCache.clear();\n}\n\nvoid CSMWorld::InfoTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end)\n{\n    refreshFilter();\n\n    if (!parent.isValid())\n    {\n        mFirstRowCache.clear();\n        // We can't re-sort the model here, because the topic of the added row isn't set yet.\n        // Store the row index for using in the first dataChanged() after this row insertion.\n        mLastAddedSourceRow = end;\n    }\n}\n\nvoid CSMWorld::InfoTableProxyModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)\n{\n    refreshFilter();\n\n    if (mLastAddedSourceRow != -1 && \n        topLeft.row() <= mLastAddedSourceRow && bottomRight.row() >= mLastAddedSourceRow)\n    {\n        // Now the topic of the last added row is set, \n        // so we can re-sort the model to ensure the corrent position of this row\n        int column = sortColumn();\n        Qt::SortOrder order = sortOrder();\n        sort(mInfoColumnIndex); // Restore the correct position of an added row\n        sort(column, order);    // Restore the original sort order\n        emit rowAdded(getRecordId(mLastAddedSourceRow).toUtf8().constData());\n\n        // Make sure that we perform a re-sorting only in the first dataChanged() after a row insertion\n        mLastAddedSourceRow = -1;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/infotableproxymodel.hpp",
    "content": "#ifndef CSM_WORLD_INFOTABLEPROXYMODEL_HPP\n#define CSM_WORLD_INFOTABLEPROXYMODEL_HPP\n\n#include <QHash>\n\n#include \"idtableproxymodel.hpp\"\n#include \"columns.hpp\"\n#include \"universalid.hpp\"\n\nnamespace CSMWorld\n{\n    class IdTableBase;\n\n    class InfoTableProxyModel : public IdTableProxyModel\n    {\n            Q_OBJECT\n\n            UniversalId::Type mType;\n            Columns::ColumnId mInfoColumnId;\n            ///< Contains ID for Topic or Journal ID\n            int mInfoColumnIndex;\n            int mLastAddedSourceRow;\n\n            mutable QHash<QString, int> mFirstRowCache;\n\n            int getFirstInfoRow(int currentRow) const;\n            ///< Finds the first row with the same topic (journal entry) as in \\a currentRow\n            ///< \\a currentRow is a row of the source model.\n\n        public:\n            InfoTableProxyModel(UniversalId::Type type, QObject *parent = nullptr);\n\n            void setSourceModel(QAbstractItemModel *sourceModel) override;\n\n        protected:\n            bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n\n        protected slots:\n            void sourceRowsInserted(const QModelIndex &parent, int start, int end) override;\n            void sourceRowsRemoved(const QModelIndex &parent, int start, int end) override;\n            void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/land.cpp",
    "content": "#include \"land.hpp\"\n\n#include <sstream>\n#include <stdexcept>\n\nnamespace CSMWorld\n{\n    void Land::load(ESM::ESMReader &esm, bool &isDeleted)\n    {\n        ESM::Land::load(esm, isDeleted);\n    }\n\n    std::string Land::createUniqueRecordId(int x, int y)\n    {\n        std::ostringstream stream;\n        stream << \"#\" << x << \" \" << y;\n        return stream.str();\n    }\n\n    void Land::parseUniqueRecordId(const std::string& id, int& x, int& y)\n    {\n        size_t mid = id.find(' ');\n\n        if (mid == std::string::npos || id[0] != '#')\n            throw std::runtime_error(\"Invalid Land ID\");\n\n        x = std::stoi(id.substr(1, mid - 1));\n        y = std::stoi(id.substr(mid + 1));\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/land.hpp",
    "content": "#ifndef CSM_WORLD_LAND_H\n#define CSM_WORLD_LAND_H\n\n#include <string>\n\n#include <components/esm/loadland.hpp>\n\nnamespace CSMWorld\n{\n    /// \\brief Wrapper for Land record. Encodes X and Y cell index in the ID.\n    ///\n    /// \\todo Add worldspace support to the Land record.\n    struct Land : public ESM::Land\n    {\n        /// Loads the metadata and ID\n        void load (ESM::ESMReader &esm, bool &isDeleted);\n\n        static std::string createUniqueRecordId(int x, int y);\n        static void parseUniqueRecordId(const std::string& id, int& x, int& y);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/landtexture.cpp",
    "content": "#include \"landtexture.hpp\"\n\n#include <sstream>\n#include <stdexcept>\n\n#include <components/esm/esmreader.hpp>\n\nnamespace CSMWorld\n{\n    void LandTexture::load(ESM::ESMReader &esm, bool &isDeleted)\n    {\n        ESM::LandTexture::load(esm, isDeleted);\n\n        mPluginIndex = esm.getIndex();\n    }\n\n    std::string LandTexture::createUniqueRecordId(int plugin, int index)\n    {\n        std::stringstream ss;\n        ss << 'L' << plugin << '#' << index;\n        return ss.str();\n    }\n\n    void LandTexture::parseUniqueRecordId(const std::string& id, int& plugin, int& index)\n    {\n        size_t middle = id.find('#');\n\n        if (middle == std::string::npos || id[0] != 'L')\n            throw std::runtime_error(\"Invalid LandTexture ID\");\n\n        plugin = std::stoi(id.substr(1,middle-1));\n        index = std::stoi(id.substr(middle+1));\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/landtexture.hpp",
    "content": "#ifndef CSM_WORLD_LANDTEXTURE_H\n#define CSM_WORLD_LANDTEXTURE_H\n\n#include <string>\n\n#include <components/esm/loadltex.hpp>\n\nnamespace CSMWorld\n{\n    /// \\brief Wrapper for LandTexture record, providing info which plugin the LandTexture was loaded from.\n    struct LandTexture : public ESM::LandTexture\n    {\n        int mPluginIndex;\n\n        void load (ESM::ESMReader &esm, bool &isDeleted);\n\n        /// Returns a string identifier that will be unique to any LandTexture.\n        static std::string createUniqueRecordId(int plugin, int index);\n        /// Deconstructs a unique string identifier into plugin and index.\n        static void parseUniqueRecordId(const std::string& id, int& plugin, int& index);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/landtexturetableproxymodel.cpp",
    "content": "#include \"landtexturetableproxymodel.hpp\"\n\n#include \"idtable.hpp\"\n\nnamespace CSMWorld\n{\n    LandTextureTableProxyModel::LandTextureTableProxyModel(QObject* parent)\n        : IdTableProxyModel(parent)\n    {\n    }\n\n    bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const\n    {\n        return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/landtexturetableproxymodel.hpp",
    "content": "#ifndef CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H\n#define CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H\n\n#include \"idtableproxymodel.hpp\"\n\nnamespace CSMWorld\n{\n    /// \\brief Removes base records from filtered results.\n    class LandTextureTableProxyModel : public IdTableProxyModel\n    {\n            Q_OBJECT\n        public:\n\n            LandTextureTableProxyModel(QObject* parent = nullptr);\n\n        protected:\n\n            bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/metadata.cpp",
    "content": "#include \"metadata.hpp\"\n\n#include <components/esm/loadtes3.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n\nvoid CSMWorld::MetaData::blank()\n{\n    mFormat = ESM::Header::CurrentFormat;\n    mAuthor.clear();\n    mDescription.clear();\n}\n\nvoid CSMWorld::MetaData::load (ESM::ESMReader& esm)\n{\n    mFormat = esm.getHeader().mFormat;\n    mAuthor = esm.getHeader().mData.author;\n    mDescription = esm.getHeader().mData.desc;\n}\n\nvoid CSMWorld::MetaData::save (ESM::ESMWriter& esm) const\n{\n    esm.setFormat (mFormat);\n    esm.setAuthor (mAuthor);\n    esm.setDescription (mDescription);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/metadata.hpp",
    "content": "#ifndef CSM_WOLRD_METADATA_H\n#define CSM_WOLRD_METADATA_H\n\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n}\n\nnamespace CSMWorld\n{\n    struct MetaData\n    {\n        std::string mId;\n\n        int mFormat;\n        std::string mAuthor;\n        std::string mDescription;\n\n        void blank();\n\n        void load (ESM::ESMReader& esm);\n        void save (ESM::ESMWriter& esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/nestedcoladapterimp.cpp",
    "content": "#include \"nestedcoladapterimp.hpp\"\n\n#include <components/esm/loadregn.hpp>\n#include <components/esm/loadfact.hpp>\n\n#include \"idcollection.hpp\"\n#include \"pathgrid.hpp\"\n#include \"info.hpp\"\n#include \"infoselectwrapper.hpp\"\n\nnamespace CSMWorld\n{\n    PathgridPointListAdapter::PathgridPointListAdapter () {}\n\n    void PathgridPointListAdapter::addRow(Record<Pathgrid>& record, int position) const\n    {\n        Pathgrid pathgrid = record.get();\n\n        ESM::Pathgrid::PointList& points = pathgrid.mPoints;\n\n        // blank row\n        ESM::Pathgrid::Point point;\n        point.mX = 0;\n        point.mY = 0;\n        point.mZ = 0;\n        point.mAutogenerated = 0;\n        point.mConnectionNum = 0;\n        point.mUnknown = 0;\n\n        points.insert(points.begin()+position, point);\n        pathgrid.mData.mS2 = pathgrid.mPoints.size();\n\n        record.setModified (pathgrid);\n    }\n\n    void PathgridPointListAdapter::removeRow(Record<Pathgrid>& record, int rowToRemove) const\n    {\n        Pathgrid pathgrid = record.get();\n\n        ESM::Pathgrid::PointList& points = pathgrid.mPoints;\n\n        if (rowToRemove < 0 || rowToRemove >= static_cast<int> (points.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        // Do not remove dangling edges, does not work with current undo mechanism\n        // Do not automatically adjust indices, what would be done with dangling edges?\n        points.erase(points.begin()+rowToRemove);\n        pathgrid.mData.mS2 = pathgrid.mPoints.size();\n\n        record.setModified (pathgrid);\n    }\n\n    void PathgridPointListAdapter::setTable(Record<Pathgrid>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        Pathgrid pathgrid = record.get();\n        pathgrid.mPoints = static_cast<const NestedTableWrapper<ESM::Pathgrid::PointList> &>(nestedTable).mNestedTable;\n        pathgrid.mData.mS2 = pathgrid.mPoints.size();\n\n        record.setModified (pathgrid);\n    }\n\n    NestedTableWrapperBase* PathgridPointListAdapter::table(const Record<Pathgrid>& record) const\n    {\n        // deleted by dtor of NestedTableStoring\n        return new NestedTableWrapper<ESM::Pathgrid::PointList>(record.get().mPoints);\n    }\n\n    QVariant PathgridPointListAdapter::getData(const Record<Pathgrid>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        ESM::Pathgrid::Point point = record.get().mPoints[subRowIndex];\n        switch (subColIndex)\n        {\n            case 0: return subRowIndex;\n            case 1: return point.mX;\n            case 2: return point.mY;\n            case 3: return point.mZ;\n            default: throw std::runtime_error(\"Pathgrid point subcolumn index out of range\");\n        }\n    }\n\n    void PathgridPointListAdapter::setData(Record<Pathgrid>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        Pathgrid pathgrid = record.get();\n        ESM::Pathgrid::Point point = pathgrid.mPoints[subRowIndex];\n        switch (subColIndex)\n        {\n            case 0: return; // return without saving\n            case 1: point.mX = value.toInt(); break;\n            case 2: point.mY = value.toInt(); break;\n            case 3: point.mZ = value.toInt(); break;\n            default: throw std::runtime_error(\"Pathgrid point subcolumn index out of range\");\n        }\n\n        pathgrid.mPoints[subRowIndex] = point;\n\n        record.setModified (pathgrid);\n    }\n\n    int PathgridPointListAdapter::getColumnsCount(const Record<Pathgrid>& record) const\n    {\n        return 4;\n    }\n\n    int PathgridPointListAdapter::getRowsCount(const Record<Pathgrid>& record) const\n    {\n        return static_cast<int>(record.get().mPoints.size());\n    }\n\n    PathgridEdgeListAdapter::PathgridEdgeListAdapter () {}\n\n    void PathgridEdgeListAdapter::addRow(Record<Pathgrid>& record, int position) const\n    {\n        Pathgrid pathgrid = record.get();\n\n        ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges;\n\n        // blank row\n        ESM::Pathgrid::Edge edge;\n        edge.mV0 = 0;\n        edge.mV1 = 0;\n\n        // NOTE: inserting a blank edge does not really make sense, perhaps this should be a\n        // logic_error exception\n        //\n        // Currently the code assumes that the end user to know what he/she is doing.\n        // e.g. Edges come in pairs, from points a->b and b->a\n        edges.insert(edges.begin()+position, edge);\n\n        record.setModified (pathgrid);\n    }\n\n    void PathgridEdgeListAdapter::removeRow(Record<Pathgrid>& record, int rowToRemove) const\n    {\n        Pathgrid pathgrid = record.get();\n\n        ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges;\n\n        if (rowToRemove < 0 || rowToRemove >= static_cast<int> (edges.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        edges.erase(edges.begin()+rowToRemove);\n\n        record.setModified (pathgrid);\n    }\n\n    void PathgridEdgeListAdapter::setTable(Record<Pathgrid>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        Pathgrid pathgrid = record.get();\n\n        pathgrid.mEdges =\n            static_cast<const NestedTableWrapper<ESM::Pathgrid::EdgeList> &>(nestedTable).mNestedTable;\n\n        record.setModified (pathgrid);\n    }\n\n    NestedTableWrapperBase* PathgridEdgeListAdapter::table(const Record<Pathgrid>& record) const\n    {\n        // deleted by dtor of NestedTableStoring\n        return new NestedTableWrapper<ESM::Pathgrid::EdgeList>(record.get().mEdges);\n    }\n\n    QVariant PathgridEdgeListAdapter::getData(const Record<Pathgrid>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        Pathgrid pathgrid = record.get();\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int> (pathgrid.mEdges.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex];\n        switch (subColIndex)\n        {\n            case 0: return subRowIndex;\n            case 1: return edge.mV0;\n            case 2: return edge.mV1;\n            default: throw std::runtime_error(\"Pathgrid edge subcolumn index out of range\");\n        }\n    }\n\n    void PathgridEdgeListAdapter::setData(Record<Pathgrid>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        Pathgrid pathgrid = record.get();\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int> (pathgrid.mEdges.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex];\n        switch (subColIndex)\n        {\n            case 0: return; // return without saving\n            case 1: edge.mV0 = value.toInt(); break;\n            case 2: edge.mV1 = value.toInt(); break;\n            default: throw std::runtime_error(\"Pathgrid edge subcolumn index out of range\");\n        }\n\n        pathgrid.mEdges[subRowIndex] = edge;\n\n        record.setModified (pathgrid);\n    }\n\n    int PathgridEdgeListAdapter::getColumnsCount(const Record<Pathgrid>& record) const\n    {\n        return 3;\n    }\n\n    int PathgridEdgeListAdapter::getRowsCount(const Record<Pathgrid>& record) const\n    {\n        return static_cast<int>(record.get().mEdges.size());\n    }\n\n    FactionReactionsAdapter::FactionReactionsAdapter () {}\n\n    void FactionReactionsAdapter::addRow(Record<ESM::Faction>& record, int position) const\n    {\n        ESM::Faction faction = record.get();\n\n        std::map<std::string, int>& reactions = faction.mReactions;\n\n        // blank row\n        reactions.insert(std::make_pair(\"\", 0));\n\n        record.setModified (faction);\n    }\n\n    void FactionReactionsAdapter::removeRow(Record<ESM::Faction>& record, int rowToRemove) const\n    {\n        ESM::Faction faction = record.get();\n\n        std::map<std::string, int>& reactions = faction.mReactions;\n\n        if (rowToRemove < 0 || rowToRemove >= static_cast<int> (reactions.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        // FIXME: how to ensure that the map entries correspond to table indicies?\n        // WARNING: Assumed that the table view has the same order as std::map\n        std::map<std::string, int>::iterator iter = reactions.begin();\n        for(int i = 0; i < rowToRemove; ++i)\n            ++iter;\n        reactions.erase(iter);\n\n        record.setModified (faction);\n    }\n\n    void FactionReactionsAdapter::setTable(Record<ESM::Faction>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        ESM::Faction faction = record.get();\n\n        faction.mReactions =\n            static_cast<const NestedTableWrapper<std::map<std::string, int> >&>(nestedTable).mNestedTable;\n\n        record.setModified (faction);\n    }\n\n    NestedTableWrapperBase* FactionReactionsAdapter::table(const Record<ESM::Faction>& record) const\n    {\n        // deleted by dtor of NestedTableStoring\n        return new NestedTableWrapper<std::map<std::string, int> >(record.get().mReactions);\n    }\n\n    QVariant FactionReactionsAdapter::getData(const Record<ESM::Faction>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        ESM::Faction faction = record.get();\n\n        std::map<std::string, int>& reactions = faction.mReactions;\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int> (reactions.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        // FIXME: how to ensure that the map entries correspond to table indicies?\n        // WARNING: Assumed that the table view has the same order as std::map\n        std::map<std::string, int>::const_iterator iter = reactions.begin();\n        for(int i = 0; i < subRowIndex; ++i)\n            ++iter;\n        switch (subColIndex)\n        {\n            case 0: return QString((*iter).first.c_str());\n            case 1: return (*iter).second;\n            default: throw std::runtime_error(\"Faction reactions subcolumn index out of range\");\n        }\n    }\n\n    void FactionReactionsAdapter::setData(Record<ESM::Faction>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        ESM::Faction faction = record.get();\n\n        std::map<std::string, int>& reactions = faction.mReactions;\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int> (reactions.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        // FIXME: how to ensure that the map entries correspond to table indicies?\n        // WARNING: Assumed that the table view has the same order as std::map\n        std::map<std::string, int>::iterator iter = reactions.begin();\n        for(int i = 0; i < subRowIndex; ++i)\n            ++iter;\n\n        std::string factionId = (*iter).first;\n        int reaction = (*iter).second;\n\n        switch (subColIndex)\n        {\n            case 0:\n            {\n                reactions.erase(iter);\n                reactions.insert(std::make_pair(value.toString().toUtf8().constData(), reaction));\n                break;\n            }\n            case 1:\n            {\n                reactions[factionId] = value.toInt();\n                break;\n            }\n            default: throw std::runtime_error(\"Faction reactions subcolumn index out of range\");\n        }\n\n        record.setModified (faction);\n    }\n\n    int FactionReactionsAdapter::getColumnsCount(const Record<ESM::Faction>& record) const\n    {\n        return 2;\n    }\n\n    int FactionReactionsAdapter::getRowsCount(const Record<ESM::Faction>& record) const\n    {\n        return static_cast<int>(record.get().mReactions.size());\n    }\n\n    RegionSoundListAdapter::RegionSoundListAdapter () {}\n\n    void RegionSoundListAdapter::addRow(Record<ESM::Region>& record, int position) const\n    {\n        ESM::Region region = record.get();\n\n        std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;\n\n        // blank row\n        ESM::Region::SoundRef soundRef;\n        soundRef.mSound.assign(\"\");\n        soundRef.mChance = 0;\n\n        soundList.insert(soundList.begin()+position, soundRef);\n\n        record.setModified (region);\n    }\n\n    void RegionSoundListAdapter::removeRow(Record<ESM::Region>& record, int rowToRemove) const\n    {\n        ESM::Region region = record.get();\n\n        std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;\n\n        if (rowToRemove < 0 || rowToRemove >= static_cast<int> (soundList.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        soundList.erase(soundList.begin()+rowToRemove);\n\n        record.setModified (region);\n    }\n\n    void RegionSoundListAdapter::setTable(Record<ESM::Region>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        ESM::Region region = record.get();\n\n        region.mSoundList =\n            static_cast<const NestedTableWrapper<std::vector<ESM::Region::SoundRef> >&>(nestedTable).mNestedTable;\n\n        record.setModified (region);\n    }\n\n    NestedTableWrapperBase* RegionSoundListAdapter::table(const Record<ESM::Region>& record) const\n    {\n        // deleted by dtor of NestedTableStoring\n        return new NestedTableWrapper<std::vector<ESM::Region::SoundRef> >(record.get().mSoundList);\n    }\n\n    QVariant RegionSoundListAdapter::getData(const Record<ESM::Region>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        ESM::Region region = record.get();\n\n        std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int> (soundList.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        ESM::Region::SoundRef soundRef = soundList[subRowIndex];\n        switch (subColIndex)\n        {\n            case 0: return QString(soundRef.mSound.c_str());\n            case 1: return soundRef.mChance;\n            default: throw std::runtime_error(\"Region sounds subcolumn index out of range\");\n        }\n    }\n\n    void RegionSoundListAdapter::setData(Record<ESM::Region>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        ESM::Region region = record.get();\n\n        std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int> (soundList.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        ESM::Region::SoundRef soundRef = soundList[subRowIndex];\n        switch (subColIndex)\n        {\n            case 0: soundRef.mSound.assign(value.toString().toUtf8().constData()); break;\n            case 1: soundRef.mChance = static_cast<unsigned char>(value.toInt()); break;\n            default: throw std::runtime_error(\"Region sounds subcolumn index out of range\");\n        }\n\n        region.mSoundList[subRowIndex] = soundRef;\n\n        record.setModified (region);\n    }\n\n    int RegionSoundListAdapter::getColumnsCount(const Record<ESM::Region>& record) const\n    {\n        return 2;\n    }\n\n    int RegionSoundListAdapter::getRowsCount(const Record<ESM::Region>& record) const\n    {\n        return static_cast<int>(record.get().mSoundList.size());\n    }\n\n    InfoListAdapter::InfoListAdapter () {}\n\n    void InfoListAdapter::addRow(Record<Info>& record, int position) const\n    {\n        throw std::logic_error (\"cannot add a row to a fixed table\");\n    }\n\n    void InfoListAdapter::removeRow(Record<Info>& record, int rowToRemove) const\n    {\n        throw std::logic_error (\"cannot remove a row to a fixed table\");\n    }\n\n    void InfoListAdapter::setTable(Record<Info>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        throw std::logic_error (\"table operation not supported\");\n    }\n\n    NestedTableWrapperBase* InfoListAdapter::table(const Record<Info>& record) const\n    {\n        throw std::logic_error (\"table operation not supported\");\n    }\n\n    QVariant InfoListAdapter::getData(const Record<Info>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        Info info = record.get();\n\n        if (subColIndex == 0)\n            return QString(info.mResultScript.c_str());\n        else\n            throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n    }\n\n    void InfoListAdapter::setData(Record<Info>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        Info info = record.get();\n\n        if (subColIndex == 0)\n            info.mResultScript = value.toString().toStdString();\n        else\n            throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n\n        record.setModified (info);\n    }\n\n    int InfoListAdapter::getColumnsCount(const Record<Info>& record) const\n    {\n        return 1;\n    }\n\n    int InfoListAdapter::getRowsCount(const Record<Info>& record) const\n    {\n        return 1; // fixed at size 1\n    }\n\n    InfoConditionAdapter::InfoConditionAdapter () {}\n\n    void InfoConditionAdapter::addRow(Record<Info>& record, int position) const\n    {\n        Info info = record.get();\n\n        std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;\n\n        // default row\n        ESM::DialInfo::SelectStruct condStruct;\n        condStruct.mSelectRule = \"01000\";\n        condStruct.mValue = ESM::Variant();\n        condStruct.mValue.setType(ESM::VT_Int);\n\n        conditions.insert(conditions.begin()+position, condStruct);\n\n        record.setModified (info);\n    }\n\n    void InfoConditionAdapter::removeRow(Record<Info>& record, int rowToRemove) const\n    {\n        Info info = record.get();\n\n        std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;\n\n        if (rowToRemove < 0 || rowToRemove >= static_cast<int> (conditions.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        conditions.erase(conditions.begin()+rowToRemove);\n\n        record.setModified (info);\n    }\n\n    void InfoConditionAdapter::setTable(Record<Info>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        Info info = record.get();\n\n        info.mSelects =\n            static_cast<const NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct> >&>(nestedTable).mNestedTable;\n\n        record.setModified (info);\n    }\n\n    NestedTableWrapperBase* InfoConditionAdapter::table(const Record<Info>& record) const\n    {\n        // deleted by dtor of NestedTableStoring\n        return new NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct> >(record.get().mSelects);\n    }\n\n    QVariant InfoConditionAdapter::getData(const Record<Info>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        Info info = record.get();\n\n        std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        ConstInfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);\n\n        switch (subColIndex)\n        {\n            case 0:\n            {\n                return infoSelectWrapper.getFunctionName();\n            }\n            case 1:\n            {\n                if (infoSelectWrapper.hasVariable())\n                    return QString(infoSelectWrapper.getVariableName().c_str());\n                else\n                    return \"\";\n            }\n            case 2:\n            {\n                return infoSelectWrapper.getRelationType();\n            }\n            case 3:\n            {\n                switch (infoSelectWrapper.getVariant().getType())\n                {\n                    case ESM::VT_Int:\n                    {\n                        return infoSelectWrapper.getVariant().getInteger();\n                    }\n                    case ESM::VT_Float:\n                    {\n                        return infoSelectWrapper.getVariant().getFloat();\n                    }\n                    default: return QVariant();\n                }\n            }\n            default: throw std::runtime_error(\"Info condition subcolumn index out of range\");\n        }\n    }\n\n    void InfoConditionAdapter::setData(Record<Info>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        Info info = record.get();\n\n        std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))\n            throw std::runtime_error (\"index out of range\");\n\n        InfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);\n        bool conversionResult = false;\n\n        switch (subColIndex)\n        {\n            case 0: // Function\n            {\n                infoSelectWrapper.setFunctionName(static_cast<ConstInfoSelectWrapper::FunctionName>(value.toInt()));\n\n                if (infoSelectWrapper.getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric &&\n                    infoSelectWrapper.getVariant().getType() != ESM::VT_Int)\n                {\n                    infoSelectWrapper.getVariant().setType(ESM::VT_Int);\n                }\n\n                infoSelectWrapper.update();\n                break;\n            }\n            case 1: // Variable\n            {\n                infoSelectWrapper.setVariableName(value.toString().toUtf8().constData());\n                infoSelectWrapper.update();\n                break;\n            }\n            case 2: // Relation\n            {\n                infoSelectWrapper.setRelationType(static_cast<ConstInfoSelectWrapper::RelationType>(value.toInt()));\n                infoSelectWrapper.update();\n                break;\n            }\n            case 3: // Value\n            {\n                switch (infoSelectWrapper.getComparisonType())\n                {\n                    case ConstInfoSelectWrapper::Comparison_Numeric:\n                    {\n                        // QVariant seems to have issues converting 0\n                        if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare(\"0\") == 0)\n                        {\n                            infoSelectWrapper.getVariant().setType(ESM::VT_Int);\n                            infoSelectWrapper.getVariant().setInteger(value.toInt());\n                        }\n                        else if (value.toFloat(&conversionResult) && conversionResult)\n                        {\n                            infoSelectWrapper.getVariant().setType(ESM::VT_Float);\n                            infoSelectWrapper.getVariant().setFloat(value.toFloat());\n                        }\n                        break;\n                    }\n                    case ConstInfoSelectWrapper::Comparison_Boolean:\n                    case ConstInfoSelectWrapper::Comparison_Integer:\n                    {\n                        if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare(\"0\") == 0)\n                        {\n                            infoSelectWrapper.getVariant().setType(ESM::VT_Int);\n                            infoSelectWrapper.getVariant().setInteger(value.toInt());\n                        }\n                        break;\n                    }\n                    default: break;\n                }\n                break;\n            }\n            default: throw std::runtime_error(\"Info condition subcolumn index out of range\");\n        }\n\n        record.setModified (info);\n    }\n\n    int InfoConditionAdapter::getColumnsCount(const Record<Info>& record) const\n    {\n        return 4;\n    }\n\n    int InfoConditionAdapter::getRowsCount(const Record<Info>& record) const\n    {\n        return static_cast<int>(record.get().mSelects.size());\n    }\n\n    RaceAttributeAdapter::RaceAttributeAdapter () {}\n\n    void RaceAttributeAdapter::addRow(Record<ESM::Race>& record, int position) const\n    {\n        // Do nothing, this table cannot be changed by the user\n    }\n\n    void RaceAttributeAdapter::removeRow(Record<ESM::Race>& record, int rowToRemove) const\n    {\n        // Do nothing, this table cannot be changed by the user\n    }\n\n    void RaceAttributeAdapter::setTable(Record<ESM::Race>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        ESM::Race race = record.get();\n\n        race.mData =\n            static_cast<const NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >&>(nestedTable).mNestedTable.at(0);\n\n        record.setModified (race);\n    }\n\n    NestedTableWrapperBase* RaceAttributeAdapter::table(const Record<ESM::Race>& record) const\n    {\n        std::vector<ESM::Race::RADTstruct> wrap;\n        wrap.push_back(record.get().mData);\n        // deleted by dtor of NestedTableStoring\n        return new NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >(wrap);\n    }\n\n    QVariant RaceAttributeAdapter::getData(const Record<ESM::Race>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        ESM::Race race = record.get();\n\n        if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length)\n            throw std::runtime_error (\"index out of range\");\n\n        switch (subColIndex)\n        {\n            case 0: return subRowIndex;\n            case 1: return race.mData.mAttributeValues[subRowIndex].mMale;\n            case 2: return race.mData.mAttributeValues[subRowIndex].mFemale;\n            default: throw std::runtime_error(\"Race Attribute subcolumn index out of range\");\n        }\n    }\n\n    void RaceAttributeAdapter::setData(Record<ESM::Race>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        ESM::Race race = record.get();\n\n        if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length)\n            throw std::runtime_error (\"index out of range\");\n\n        switch (subColIndex)\n        {\n            case 0: return; // throw an exception here?\n            case 1: race.mData.mAttributeValues[subRowIndex].mMale = value.toInt(); break;\n            case 2: race.mData.mAttributeValues[subRowIndex].mFemale = value.toInt(); break;\n            default: throw std::runtime_error(\"Race Attribute subcolumn index out of range\");\n        }\n\n        record.setModified (race);\n    }\n\n    int RaceAttributeAdapter::getColumnsCount(const Record<ESM::Race>& record) const\n    {\n        return 3; // attrib, male, female\n    }\n\n    int RaceAttributeAdapter::getRowsCount(const Record<ESM::Race>& record) const\n    {\n        return ESM::Attribute::Length; // there are 8 attributes\n    }\n\n    RaceSkillsBonusAdapter::RaceSkillsBonusAdapter () {}\n\n    void RaceSkillsBonusAdapter::addRow(Record<ESM::Race>& record, int position) const\n    {\n        // Do nothing, this table cannot be changed by the user\n    }\n\n    void RaceSkillsBonusAdapter::removeRow(Record<ESM::Race>& record, int rowToRemove) const\n    {\n        // Do nothing, this table cannot be changed by the user\n    }\n\n    void RaceSkillsBonusAdapter::setTable(Record<ESM::Race>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        ESM::Race race = record.get();\n\n        race.mData =\n            static_cast<const NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >&>(nestedTable).mNestedTable.at(0);\n\n        record.setModified (race);\n    }\n\n    NestedTableWrapperBase* RaceSkillsBonusAdapter::table(const Record<ESM::Race>& record) const\n    {\n        std::vector<ESM::Race::RADTstruct> wrap;\n        wrap.push_back(record.get().mData);\n        // deleted by dtor of NestedTableStoring\n        return new NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >(wrap);\n    }\n\n    QVariant RaceSkillsBonusAdapter::getData(const Record<ESM::Race>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        ESM::Race race = record.get();\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int>(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0])))\n            throw std::runtime_error (\"index out of range\");\n\n        switch (subColIndex)\n        {\n            case 0: return race.mData.mBonus[subRowIndex].mSkill; // can be -1\n            case 1: return race.mData.mBonus[subRowIndex].mBonus;\n            default: throw std::runtime_error(\"Race skill bonus subcolumn index out of range\");\n        }\n    }\n\n    void RaceSkillsBonusAdapter::setData(Record<ESM::Race>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        ESM::Race race = record.get();\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int>(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0])))\n            throw std::runtime_error (\"index out of range\");\n\n        switch (subColIndex)\n        {\n            case 0: race.mData.mBonus[subRowIndex].mSkill = value.toInt(); break; // can be -1\n            case 1: race.mData.mBonus[subRowIndex].mBonus = value.toInt(); break;\n            default: throw std::runtime_error(\"Race skill bonus subcolumn index out of range\");\n        }\n\n        record.setModified (race);\n    }\n\n    int RaceSkillsBonusAdapter::getColumnsCount(const Record<ESM::Race>& record) const\n    {\n        return 2; // skill, bonus\n    }\n\n    int RaceSkillsBonusAdapter::getRowsCount(const Record<ESM::Race>& record) const\n    {\n        // there are 7 skill bonuses\n        return static_cast<int>(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0]));\n    }\n\n    CellListAdapter::CellListAdapter () {}\n\n    void CellListAdapter::addRow(Record<CSMWorld::Cell>& record, int position) const\n    {\n        throw std::logic_error (\"cannot add a row to a fixed table\");\n    }\n\n    void CellListAdapter::removeRow(Record<CSMWorld::Cell>& record, int rowToRemove) const\n    {\n        throw std::logic_error (\"cannot remove a row to a fixed table\");\n    }\n\n    void CellListAdapter::setTable(Record<CSMWorld::Cell>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        throw std::logic_error (\"table operation not supported\");\n    }\n\n    NestedTableWrapperBase* CellListAdapter::table(const Record<CSMWorld::Cell>& record) const\n    {\n        throw std::logic_error (\"table operation not supported\");\n    }\n\n    QVariant CellListAdapter::getData(const Record<CSMWorld::Cell>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        CSMWorld::Cell cell = record.get();\n\n        bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0;\n        bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0;\n        bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0;\n\n        switch (subColIndex)\n        {\n            case 0: return isInterior;\n            // While the ambient information is not necessarily valid if the subrecord wasn't loaded,\n            // the user should still be allowed to edit it\n            case 1: return (isInterior && !behaveLikeExterior) ?\n                    cell.mAmbi.mAmbient : QVariant(QVariant::UserType);\n            case 2: return (isInterior && !behaveLikeExterior) ?\n                    cell.mAmbi.mSunlight : QVariant(QVariant::UserType);\n            case 3: return (isInterior && !behaveLikeExterior) ?\n                    cell.mAmbi.mFog : QVariant(QVariant::UserType);\n            case 4: return (isInterior && !behaveLikeExterior) ?\n                    cell.mAmbi.mFogDensity : QVariant(QVariant::UserType);\n            case 5:\n            {\n                if (isInterior && interiorWater)\n                    return cell.mWater;\n                else\n                    return QVariant(QVariant::UserType);\n            }\n            case 6: return isInterior ?\n                    QVariant(QVariant::UserType) : cell.mMapColor; // TODO: how to select?\n            //case 7: return isInterior ?\n                    //behaveLikeExterior : QVariant(QVariant::UserType);\n            default: throw std::runtime_error(\"Cell subcolumn index out of range\");\n        }\n    }\n\n    void CellListAdapter::setData(Record<CSMWorld::Cell>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        CSMWorld::Cell cell = record.get();\n\n        bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0;\n        bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0;\n        bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0;\n\n        switch (subColIndex)\n        {\n            case 0:\n            {\n                if (value.toBool())\n                    cell.mData.mFlags |= ESM::Cell::Interior;\n                else\n                    cell.mData.mFlags &= ~ESM::Cell::Interior;\n                break;\n            }\n            case 1:\n            {\n                if (isInterior && !behaveLikeExterior)\n                {\n                    cell.mAmbi.mAmbient = static_cast<int32_t>(value.toInt());\n                    cell.setHasAmbient(true);\n                }\n                else\n                    return; // return without saving\n                break;\n            }\n            case 2:\n            {\n                if (isInterior && !behaveLikeExterior)\n                {\n                    cell.mAmbi.mSunlight = static_cast<int32_t>(value.toInt());\n                    cell.setHasAmbient(true);\n                }\n                else\n                    return; // return without saving\n                break;\n            }\n            case 3:\n            {\n                if (isInterior && !behaveLikeExterior)\n                {\n                    cell.mAmbi.mFog = static_cast<int32_t>(value.toInt());\n                    cell.setHasAmbient(true);\n                }\n                else\n                    return; // return without saving\n                break;\n            }\n            case 4:\n            {\n                if (isInterior && !behaveLikeExterior)\n                {\n                    cell.mAmbi.mFogDensity = value.toFloat();\n                    cell.setHasAmbient(true);\n                }\n                else\n                    return; // return without saving\n                break;\n            }\n            case 5:\n            {\n                if (isInterior && interiorWater)\n                    cell.mWater = value.toFloat();\n                else\n                    return; // return without saving\n                break;\n            }\n            case 6:\n            {\n                if (!isInterior)\n                    cell.mMapColor = value.toInt();\n                else\n                    return; // return without saving\n                break;\n            }\n#if 0\n            // redundant since this flag is shown in the main table as \"Interior Sky\"\n            // keep here for documenting the logic based on vanilla\n            case 7:\n            {\n                if (isInterior)\n                {\n                    if (value.toBool())\n                        cell.mData.mFlags |= ESM::Cell::QuasiEx;\n                    else\n                        cell.mData.mFlags &= ~ESM::Cell::QuasiEx;\n                }\n                else\n                    return; // return without saving\n                break;\n            }\n#endif\n            default: throw std::runtime_error(\"Cell subcolumn index out of range\");\n        }\n\n        record.setModified (cell);\n    }\n\n    int CellListAdapter::getColumnsCount(const Record<CSMWorld::Cell>& record) const\n    {\n        return 7;\n    }\n\n    int CellListAdapter::getRowsCount(const Record<CSMWorld::Cell>& record) const\n    {\n        return 1; // fixed at size 1\n    }\n\n    RegionWeatherAdapter::RegionWeatherAdapter () {}\n\n    void RegionWeatherAdapter::addRow(Record<ESM::Region>& record, int position) const\n    {\n        throw std::logic_error (\"cannot add a row to a fixed table\");\n    }\n\n    void RegionWeatherAdapter::removeRow(Record<ESM::Region>& record, int rowToRemove) const\n    {\n        throw std::logic_error (\"cannot remove a row from a fixed table\");\n    }\n\n    void RegionWeatherAdapter::setTable(Record<ESM::Region>& record, const NestedTableWrapperBase& nestedTable) const\n    {\n        throw std::logic_error (\"table operation not supported\");\n    }\n\n    NestedTableWrapperBase* RegionWeatherAdapter::table(const Record<ESM::Region>& record) const\n    {\n        throw std::logic_error (\"table operation not supported\");\n    }\n\n    QVariant RegionWeatherAdapter::getData(const Record<ESM::Region>& record, int subRowIndex, int subColIndex) const\n    {\n        const char* WeatherNames[] = {\n            \"Clear\",\n            \"Cloudy\",\n            \"Fog\",\n            \"Overcast\",\n            \"Rain\",\n            \"Thunder\",\n            \"Ash\",\n            \"Blight\",\n            \"Snow\",\n            \"Blizzard\"\n        };\n\n        const ESM::Region& region = record.get();\n\n        if (subColIndex == 0 && subRowIndex >= 0 && subRowIndex < 10)\n        {\n            return WeatherNames[subRowIndex];\n        }\n        else if (subColIndex == 1)\n        {\n            switch (subRowIndex)\n            {\n                case 0: return region.mData.mClear;\n                case 1: return region.mData.mCloudy;\n                case 2: return region.mData.mFoggy;\n                case 3: return region.mData.mOvercast;\n                case 4: return region.mData.mRain;\n                case 5: return region.mData.mThunder;\n                case 6: return region.mData.mAsh;\n                case 7: return region.mData.mBlight;\n                case 8: return region.mData.mA; // Snow\n                case 9: return region.mData.mB; // Blizzard\n                default: break;\n            }\n        }\n\n        throw std::runtime_error(\"index out of range\");\n    }\n\n    void RegionWeatherAdapter::setData(Record<ESM::Region>& record, const QVariant& value, int subRowIndex,\n        int subColIndex) const\n    {\n        ESM::Region region = record.get();\n        unsigned char chance = static_cast<unsigned char>(value.toInt());\n\n        if (subColIndex == 1)\n        {\n            switch (subRowIndex)\n            {\n                case 0: region.mData.mClear = chance; break;\n                case 1: region.mData.mCloudy = chance; break;\n                case 2: region.mData.mFoggy = chance; break;\n                case 3: region.mData.mOvercast = chance; break;\n                case 4: region.mData.mRain = chance; break;\n                case 5: region.mData.mThunder = chance; break;\n                case 6: region.mData.mAsh = chance; break;\n                case 7: region.mData.mBlight = chance; break;\n                case 8: region.mData.mA = chance; break;\n                case 9: region.mData.mB = chance; break;\n                default: throw std::runtime_error(\"index out of range\");\n            }\n\n            record.setModified (region);\n        }\n    }\n\n    int RegionWeatherAdapter::getColumnsCount(const Record<ESM::Region>& record) const\n    {\n        return 2;\n    }\n\n    int RegionWeatherAdapter::getRowsCount(const Record<ESM::Region>& record) const\n    {\n        return 10;\n    }\n\n    FactionRanksAdapter::FactionRanksAdapter () {}\n\n    void FactionRanksAdapter::addRow(Record<ESM::Faction>& record, int position) const\n    {\n        throw std::logic_error (\"cannot add a row to a fixed table\");\n    }\n\n    void FactionRanksAdapter::removeRow(Record<ESM::Faction>& record, int rowToRemove) const\n    {\n        throw std::logic_error (\"cannot remove a row from a fixed table\");\n    }\n\n    void FactionRanksAdapter::setTable(Record<ESM::Faction>& record,\n            const NestedTableWrapperBase& nestedTable) const\n    {\n        throw std::logic_error (\"table operation not supported\");\n    }\n\n    NestedTableWrapperBase* FactionRanksAdapter::table(const Record<ESM::Faction>& record) const\n    {\n        throw std::logic_error (\"table operation not supported\");\n    }\n\n    QVariant FactionRanksAdapter::getData(const Record<ESM::Faction>& record,\n            int subRowIndex, int subColIndex) const\n    {\n        ESM::Faction faction = record.get();\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int>(sizeof(faction.mData.mRankData)/sizeof(faction.mData.mRankData[0])))\n            throw std::runtime_error (\"index out of range\");\n\n        auto& rankData = faction.mData.mRankData[subRowIndex];\n\n        switch (subColIndex)\n        {\n            case 0: return QString(faction.mRanks[subRowIndex].c_str());\n            case 1: return rankData.mAttribute1;\n            case 2: return rankData.mAttribute2;\n            case 3: return rankData.mPrimarySkill;\n            case 4: return rankData.mFavouredSkill;\n            case 5: return rankData.mFactReaction;\n            default: throw std::runtime_error(\"Rank subcolumn index out of range\");\n        }\n    }\n\n    void FactionRanksAdapter::setData(Record<ESM::Faction>& record,\n            const QVariant& value, int subRowIndex, int subColIndex) const\n    {\n        ESM::Faction faction = record.get();\n\n        if (subRowIndex < 0 || subRowIndex >= static_cast<int>(sizeof(faction.mData.mRankData)/sizeof(faction.mData.mRankData[0])))\n            throw std::runtime_error (\"index out of range\");\n\n        auto& rankData = faction.mData.mRankData[subRowIndex];\n\n        switch (subColIndex)\n        {\n            case 0: faction.mRanks[subRowIndex] = value.toString().toUtf8().constData(); break;\n            case 1: rankData.mAttribute1 = value.toInt(); break;\n            case 2: rankData.mAttribute2 = value.toInt(); break;\n            case 3: rankData.mPrimarySkill = value.toInt(); break;\n            case 4: rankData.mFavouredSkill = value.toInt(); break;\n            case 5: rankData.mFactReaction = value.toInt(); break;\n            default: throw std::runtime_error(\"Rank index out of range\");\n        }\n\n        record.setModified (faction);\n    }\n\n    int FactionRanksAdapter::getColumnsCount(const Record<ESM::Faction>& record) const\n    {\n        return 6;\n    }\n\n    int FactionRanksAdapter::getRowsCount(const Record<ESM::Faction>& record) const\n    {\n        return 10;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/nestedcoladapterimp.hpp",
    "content": "#ifndef CSM_WOLRD_NESTEDCOLADAPTERIMP_H\n#define CSM_WOLRD_NESTEDCOLADAPTERIMP_H\n\n#include <QVariant>\n\n#include <components/esm/loadpgrd.hpp>\n#include <components/esm/effectlist.hpp>\n#include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back\n#include <components/esm/loadskil.hpp> // for converting skill names\n#include <components/esm/attr.hpp>     // for converting attributes\n#include <components/esm/loadrace.hpp>\n\n#include \"nestedcolumnadapter.hpp\"\n#include \"nestedtablewrapper.hpp\"\n#include \"cell.hpp\"\n\nnamespace ESM\n{\n    struct Faction;\n    struct Region;\n}\n\nnamespace CSMWorld\n{\n    struct Pathgrid;\n    struct Info;\n\n    class PathgridPointListAdapter : public NestedColumnAdapter<Pathgrid>\n    {\n    public:\n        PathgridPointListAdapter ();\n\n        void addRow(Record<Pathgrid>& record, int position) const override;\n\n        void removeRow(Record<Pathgrid>& record, int rowToRemove) const override;\n\n        void setTable(Record<Pathgrid>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<Pathgrid>& record) const override;\n\n        QVariant getData(const Record<Pathgrid>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<Pathgrid>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<Pathgrid>& record) const override;\n\n        int getRowsCount(const Record<Pathgrid>& record) const override;\n    };\n\n    class PathgridEdgeListAdapter : public NestedColumnAdapter<Pathgrid>\n    {\n    public:\n        PathgridEdgeListAdapter ();\n\n        void addRow(Record<Pathgrid>& record, int position) const override;\n\n        void removeRow(Record<Pathgrid>& record, int rowToRemove) const override;\n\n        void setTable(Record<Pathgrid>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<Pathgrid>& record) const override;\n\n        QVariant getData(const Record<Pathgrid>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<Pathgrid>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<Pathgrid>& record) const override;\n\n        int getRowsCount(const Record<Pathgrid>& record) const override;\n    };\n\n    class FactionReactionsAdapter : public NestedColumnAdapter<ESM::Faction>\n    {\n    public:\n        FactionReactionsAdapter ();\n\n        void addRow(Record<ESM::Faction>& record, int position) const override;\n\n        void removeRow(Record<ESM::Faction>& record, int rowToRemove) const override;\n\n        void setTable(Record<ESM::Faction>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<ESM::Faction>& record) const override;\n\n        QVariant getData(const Record<ESM::Faction>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<ESM::Faction>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<ESM::Faction>& record) const override;\n\n        int getRowsCount(const Record<ESM::Faction>& record) const override;\n    };\n\n    class FactionRanksAdapter : public NestedColumnAdapter<ESM::Faction>\n    {\n    public:\n        FactionRanksAdapter ();\n\n        void addRow(Record<ESM::Faction>& record, int position) const override;\n\n        void removeRow(Record<ESM::Faction>& record, int rowToRemove) const override;\n\n        void setTable(Record<ESM::Faction>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<ESM::Faction>& record) const override;\n\n        QVariant getData(const Record<ESM::Faction>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<ESM::Faction>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<ESM::Faction>& record) const override;\n\n        int getRowsCount(const Record<ESM::Faction>& record) const override;\n    };\n\n    class RegionSoundListAdapter : public NestedColumnAdapter<ESM::Region>\n    {\n    public:\n        RegionSoundListAdapter ();\n\n        void addRow(Record<ESM::Region>& record, int position) const override;\n\n        void removeRow(Record<ESM::Region>& record, int rowToRemove) const override;\n\n        void setTable(Record<ESM::Region>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<ESM::Region>& record) const override;\n\n        QVariant getData(const Record<ESM::Region>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<ESM::Region>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<ESM::Region>& record) const override;\n\n        int getRowsCount(const Record<ESM::Region>& record) const override;\n    };\n\n    template<typename ESXRecordT>\n    class SpellListAdapter : public NestedColumnAdapter<ESXRecordT>\n    {\n    public:\n        SpellListAdapter () {}\n\n        void addRow(Record<ESXRecordT>& record, int position) const override\n        {\n            ESXRecordT raceOrBthSgn = record.get();\n\n            std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList;\n\n            // blank row\n            std::string spell = \"\";\n\n            spells.insert(spells.begin()+position, spell);\n\n            record.setModified (raceOrBthSgn);\n        }\n\n        void removeRow(Record<ESXRecordT>& record, int rowToRemove) const override\n        {\n            ESXRecordT raceOrBthSgn = record.get();\n\n            std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList;\n\n            if (rowToRemove < 0 || rowToRemove >= static_cast<int> (spells.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            spells.erase(spells.begin()+rowToRemove);\n\n            record.setModified (raceOrBthSgn);\n        }\n\n        void setTable(Record<ESXRecordT>& record, const NestedTableWrapperBase& nestedTable) const override\n        {\n            ESXRecordT raceOrBthSgn = record.get();\n\n            raceOrBthSgn.mPowers.mList =\n                static_cast<const NestedTableWrapper<std::vector<std::string> >&>(nestedTable).mNestedTable;\n\n            record.setModified (raceOrBthSgn);\n        }\n\n        NestedTableWrapperBase* table(const Record<ESXRecordT>& record) const override\n        {\n            // deleted by dtor of NestedTableStoring\n            return new NestedTableWrapper<std::vector<std::string> >(record.get().mPowers.mList);\n        }\n\n        QVariant getData(const Record<ESXRecordT>& record, int subRowIndex, int subColIndex) const override\n        {\n            ESXRecordT raceOrBthSgn = record.get();\n\n            std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (spells.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            std::string spell = spells[subRowIndex];\n            switch (subColIndex)\n            {\n                case 0: return QString(spell.c_str());\n                default: throw std::runtime_error(\"Spells subcolumn index out of range\");\n            }\n        }\n\n        void setData(Record<ESXRecordT>& record, const QVariant& value,\n                                    int subRowIndex, int subColIndex) const override\n        {\n            ESXRecordT raceOrBthSgn = record.get();\n\n            std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (spells.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            std::string spell = spells[subRowIndex];\n            switch (subColIndex)\n            {\n                case 0: spell = value.toString().toUtf8().constData(); break;\n                default: throw std::runtime_error(\"Spells subcolumn index out of range\");\n            }\n\n            raceOrBthSgn.mPowers.mList[subRowIndex] = spell;\n\n            record.setModified (raceOrBthSgn);\n        }\n\n        int getColumnsCount(const Record<ESXRecordT>& record) const override\n        {\n            return 1;\n        }\n\n        int getRowsCount(const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int>(record.get().mPowers.mList.size());\n        }\n    };\n\n    template<typename ESXRecordT>\n    class EffectsListAdapter : public NestedColumnAdapter<ESXRecordT>\n    {\n    public:\n        EffectsListAdapter () {}\n\n        void addRow(Record<ESXRecordT>& record, int position) const override\n        {\n            ESXRecordT magic = record.get();\n\n            std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;\n\n            // blank row\n            ESM::ENAMstruct effect;\n            effect.mEffectID = 0;\n            effect.mSkill = -1;\n            effect.mAttribute = -1;\n            effect.mRange = 0;\n            effect.mArea = 0;\n            effect.mDuration = 0;\n            effect.mMagnMin = 0;\n            effect.mMagnMax = 0;\n\n            effectsList.insert(effectsList.begin()+position, effect);\n\n            record.setModified (magic);\n        }\n\n        void removeRow(Record<ESXRecordT>& record, int rowToRemove) const override\n        {\n            ESXRecordT magic = record.get();\n\n            std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;\n\n            if (rowToRemove < 0 || rowToRemove >= static_cast<int> (effectsList.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            effectsList.erase(effectsList.begin()+rowToRemove);\n\n            record.setModified (magic);\n        }\n\n        void setTable(Record<ESXRecordT>& record, const NestedTableWrapperBase& nestedTable) const override\n        {\n            ESXRecordT magic = record.get();\n\n            magic.mEffects.mList =\n                static_cast<const NestedTableWrapper<std::vector<ESM::ENAMstruct> >&>(nestedTable).mNestedTable;\n\n            record.setModified (magic);\n        }\n\n        NestedTableWrapperBase* table(const Record<ESXRecordT>& record) const override\n        {\n            // deleted by dtor of NestedTableStoring\n            return new NestedTableWrapper<std::vector<ESM::ENAMstruct> >(record.get().mEffects.mList);\n        }\n\n        QVariant getData(const Record<ESXRecordT>& record, int subRowIndex, int subColIndex) const override\n        {\n            ESXRecordT magic = record.get();\n\n            std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (effectsList.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            ESM::ENAMstruct effect = effectsList[subRowIndex];\n            switch (subColIndex)\n            {\n                case 0:\n                {\n                    if (effect.mEffectID >=0 && effect.mEffectID < ESM::MagicEffect::Length)\n                        return effect.mEffectID;\n                    else\n                        throw std::runtime_error(\"Magic effects ID unexpected value\");\n                }\n                case 1:\n                {\n                    switch (effect.mEffectID)\n                    {\n                        case ESM::MagicEffect::DrainSkill:\n                        case ESM::MagicEffect::DamageSkill:\n                        case ESM::MagicEffect::RestoreSkill:\n                        case ESM::MagicEffect::FortifySkill:\n                        case ESM::MagicEffect::AbsorbSkill:\n                             return effect.mSkill;\n                        default:\n                            return QVariant();\n                    }\n                }\n                case 2:\n                {\n                    switch (effect.mEffectID)\n                    {\n                        case ESM::MagicEffect::DrainAttribute:\n                        case ESM::MagicEffect::DamageAttribute:\n                        case ESM::MagicEffect::RestoreAttribute:\n                        case ESM::MagicEffect::FortifyAttribute:\n                        case ESM::MagicEffect::AbsorbAttribute:\n                             return effect.mAttribute;\n                        default:\n                            return QVariant();\n                    }\n                }\n                case 3:\n                {\n                    if (effect.mRange >=0 && effect.mRange <=2)\n                        return effect.mRange;\n                    else\n                        throw std::runtime_error(\"Magic effects range unexpected value\");\n                }\n                case 4: return effect.mArea;\n                case 5: return effect.mDuration;\n                case 6: return effect.mMagnMin;\n                case 7: return effect.mMagnMax;\n                default: throw std::runtime_error(\"Magic Effects subcolumn index out of range\");\n            }\n        }\n\n        void setData(Record<ESXRecordT>& record, const QVariant& value,\n                                    int subRowIndex, int subColIndex) const override\n        {\n            ESXRecordT magic = record.get();\n\n            std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (effectsList.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            ESM::ENAMstruct effect = effectsList[subRowIndex];\n            switch (subColIndex)\n            {\n                case 0:\n                {\n                    effect.mEffectID = static_cast<short>(value.toInt());\n                    break;\n                }\n                case 1:\n                {\n                    effect.mSkill = static_cast<signed char>(value.toInt());\n                    break;\n                }\n                case 2:\n                {\n                    effect.mAttribute = static_cast<signed char>(value.toInt());\n                    break;\n                }\n                case 3:\n                {\n                    effect.mRange = value.toInt();\n                    break;\n                }\n                case 4: effect.mArea = value.toInt(); break;\n                case 5: effect.mDuration = value.toInt(); break;\n                case 6: effect.mMagnMin = value.toInt(); break;\n                case 7: effect.mMagnMax = value.toInt(); break;\n                default: throw std::runtime_error(\"Magic Effects subcolumn index out of range\");\n            }\n\n            magic.mEffects.mList[subRowIndex] = effect;\n\n            record.setModified (magic);\n        }\n\n        int getColumnsCount(const Record<ESXRecordT>& record) const override\n        {\n            return 8;\n        }\n\n        int getRowsCount(const Record<ESXRecordT>& record) const override\n        {\n            return static_cast<int>(record.get().mEffects.mList.size());\n        }\n    };\n\n    class InfoListAdapter : public NestedColumnAdapter<Info>\n    {\n    public:\n        InfoListAdapter ();\n\n        void addRow(Record<Info>& record, int position) const override;\n\n        void removeRow(Record<Info>& record, int rowToRemove) const override;\n\n        void setTable(Record<Info>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<Info>& record) const override;\n\n        QVariant getData(const Record<Info>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<Info>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<Info>& record) const override;\n\n        int getRowsCount(const Record<Info>& record) const override;\n    };\n\n    class InfoConditionAdapter : public NestedColumnAdapter<Info>\n    {\n    public:\n        InfoConditionAdapter ();\n\n        void addRow(Record<Info>& record, int position) const override;\n\n        void removeRow(Record<Info>& record, int rowToRemove) const override;\n\n        void setTable(Record<Info>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<Info>& record) const override;\n\n        QVariant getData(const Record<Info>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<Info>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<Info>& record) const override;\n\n        int getRowsCount(const Record<Info>& record) const override;\n    };\n\n    class RaceAttributeAdapter : public NestedColumnAdapter<ESM::Race>\n    {\n    public:\n        RaceAttributeAdapter ();\n\n        void addRow(Record<ESM::Race>& record, int position) const override;\n\n        void removeRow(Record<ESM::Race>& record, int rowToRemove) const override;\n\n        void setTable(Record<ESM::Race>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<ESM::Race>& record) const override;\n\n        QVariant getData(const Record<ESM::Race>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<ESM::Race>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<ESM::Race>& record) const override;\n\n        int getRowsCount(const Record<ESM::Race>& record) const override;\n    };\n\n    class RaceSkillsBonusAdapter : public NestedColumnAdapter<ESM::Race>\n    {\n    public:\n        RaceSkillsBonusAdapter ();\n\n        void addRow(Record<ESM::Race>& record, int position) const override;\n\n        void removeRow(Record<ESM::Race>& record, int rowToRemove) const override;\n\n        void setTable(Record<ESM::Race>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<ESM::Race>& record) const override;\n\n        QVariant getData(const Record<ESM::Race>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<ESM::Race>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<ESM::Race>& record) const override;\n\n        int getRowsCount(const Record<ESM::Race>& record) const override;\n    };\n\n    class CellListAdapter : public NestedColumnAdapter<CSMWorld::Cell>\n    {\n    public:\n        CellListAdapter ();\n\n        void addRow(Record<CSMWorld::Cell>& record, int position) const override;\n\n        void removeRow(Record<CSMWorld::Cell>& record, int rowToRemove) const override;\n\n        void setTable(Record<CSMWorld::Cell>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<CSMWorld::Cell>& record) const override;\n\n        QVariant getData(const Record<CSMWorld::Cell>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<CSMWorld::Cell>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<CSMWorld::Cell>& record) const override;\n\n        int getRowsCount(const Record<CSMWorld::Cell>& record) const override;\n    };\n\n    class RegionWeatherAdapter : public NestedColumnAdapter<ESM::Region>\n    {\n    public:\n        RegionWeatherAdapter ();\n\n        void addRow(Record<ESM::Region>& record, int position) const override;\n\n        void removeRow(Record<ESM::Region>& record, int rowToRemove) const override;\n\n        void setTable(Record<ESM::Region>& record,\n                const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* table(const Record<ESM::Region>& record) const override;\n\n        QVariant getData(const Record<ESM::Region>& record,\n                int subRowIndex, int subColIndex) const override;\n\n        void setData(Record<ESM::Region>& record,\n                const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getColumnsCount(const Record<ESM::Region>& record) const override;\n\n        int getRowsCount(const Record<ESM::Region>& record) const override;\n    };\n}\n\n#endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H\n"
  },
  {
    "path": "apps/opencs/model/world/nestedcollection.cpp",
    "content": "#include \"nestedcollection.hpp\"\n\nCSMWorld::NestedCollection::NestedCollection()\n{}\n\nCSMWorld::NestedCollection::~NestedCollection()\n{}\n\nint CSMWorld::NestedCollection::getNestedRowsCount(int row, int column) const\n{\n    return 0;\n}\n\nint CSMWorld::NestedCollection::getNestedColumnsCount(int row, int column) const\n{\n    return 0;\n}\n\nint CSMWorld::NestedCollection::searchNestedColumnIndex(int parentColumn, Columns::ColumnId id)\n{\n    // Assumed that the parentColumn is always a valid index\n    const NestableColumn *parent = getNestableColumn(parentColumn);\n    int nestedColumnCount = getNestedColumnsCount(0, parentColumn);\n    for (int i = 0; i < nestedColumnCount; ++i)\n    {\n        if (parent->nestedColumn(i).mColumnId == id)\n        {\n            return i;\n        }\n    }\n    return -1;\n}\n\nint CSMWorld::NestedCollection::findNestedColumnIndex(int parentColumn, Columns::ColumnId id)\n{\n    int index = searchNestedColumnIndex(parentColumn, id);\n    if (index == -1)\n    {\n        throw std::logic_error(\"CSMWorld::NestedCollection: No such nested column\");\n    }\n    return index;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/nestedcollection.hpp",
    "content": "#ifndef CSM_WOLRD_NESTEDCOLLECTION_H\n#define CSM_WOLRD_NESTEDCOLLECTION_H\n\n#include \"columns.hpp\"\n\nclass QVariant;\n\nnamespace CSMWorld\n{\n    class NestableColumn;\n    struct NestedTableWrapperBase;\n\n    class NestedCollection\n    {\n\n    public:\n\n        NestedCollection();\n        virtual ~NestedCollection();\n\n        virtual void addNestedRow(int row, int col, int position) = 0;\n\n        virtual void removeNestedRows(int row, int column, int subRow) = 0;\n\n        virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const = 0;\n\n        virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) = 0;\n\n        virtual NestedTableWrapperBase* nestedTable(int row, int column) const = 0;\n\n        virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable) = 0;\n\n        virtual int getNestedRowsCount(int row, int column) const;\n\n        virtual int getNestedColumnsCount(int row, int column) const;\n\n        virtual NestableColumn *getNestableColumn(int column) = 0;\n\n        virtual int searchNestedColumnIndex(int parentColumn, Columns::ColumnId id);\n        ///< \\return the column index or -1 if the requested column wasn't found.\n\n        virtual int findNestedColumnIndex(int parentColumn, Columns::ColumnId id);\n        ///< \\return the column index or throws an exception if the requested column wasn't found.\n    };\n}\n\n#endif // CSM_WOLRD_NESTEDCOLLECTION_H\n"
  },
  {
    "path": "apps/opencs/model/world/nestedcolumnadapter.hpp",
    "content": "#ifndef CSM_WOLRD_NESTEDCOLUMNADAPTER_H\n#define CSM_WOLRD_NESTEDCOLUMNADAPTER_H\n\nclass QVariant;\n\nnamespace CSMWorld\n{\n    struct NestedTableWrapperBase;\n\n    template <typename ESXRecordT>\n    struct Record;\n\n    template<typename ESXRecordT>\n    class NestedColumnAdapter\n    {\n    public:\n\n        NestedColumnAdapter() {}\n\n        virtual ~NestedColumnAdapter() {}\n\n        virtual void addRow(Record<ESXRecordT>& record, int position) const = 0;\n\n        virtual void removeRow(Record<ESXRecordT>& record, int rowToRemove) const = 0;\n\n        virtual void setTable(Record<ESXRecordT>& record, const NestedTableWrapperBase& nestedTable) const = 0;\n\n        virtual NestedTableWrapperBase* table(const Record<ESXRecordT>& record) const = 0;\n\n        virtual QVariant getData(const Record<ESXRecordT>& record, int subRowIndex, int subColIndex) const = 0;\n\n        virtual void setData(Record<ESXRecordT>& record, const QVariant& value, int subRowIndex, int subColIndex) const = 0;\n\n        virtual int getColumnsCount(const Record<ESXRecordT>& record) const = 0;\n\n        virtual int getRowsCount(const Record<ESXRecordT>& record) const = 0;\n    };\n}\n\n#endif // CSM_WOLRD_NESTEDCOLUMNADAPTER_H\n"
  },
  {
    "path": "apps/opencs/model/world/nestedidcollection.hpp",
    "content": "#ifndef CSM_WOLRD_NESTEDIDCOLLECTION_H\n#define CSM_WOLRD_NESTEDIDCOLLECTION_H\n\n#include <map>\n#include <stdexcept>\n\n#include \"nestedcollection.hpp\"\n#include \"nestedcoladapterimp.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace CSMWorld\n{\n    struct NestedTableWrapperBase;\n    struct Cell;\n\n    template<typename T, typename AT>\n    class IdCollection;\n\n    template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >\n    class NestedIdCollection : public IdCollection<ESXRecordT, IdAccessorT>, public NestedCollection\n    {\n            std::map<const ColumnBase*, NestedColumnAdapter<ESXRecordT>* > mAdapters;\n\n            const NestedColumnAdapter<ESXRecordT>& getAdapter(const ColumnBase &column) const;\n\n        public:\n\n            NestedIdCollection ();\n            ~NestedIdCollection();\n\n            void addNestedRow(int row, int column, int position) override;\n\n            void removeNestedRows(int row, int column, int subRow) override;\n\n            QVariant getNestedData(int row, int column, int subRow, int subColumn) const override;\n\n            void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) override;\n\n            NestedTableWrapperBase* nestedTable(int row, int column) const override;\n\n            void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable) override;\n\n            int getNestedRowsCount(int row, int column) const override;\n\n            int getNestedColumnsCount(int row, int column) const override;\n\n            // this method is inherited from NestedCollection, not from Collection<ESXRecordT>\n            NestableColumn *getNestableColumn(int column) override;\n\n            void addAdapter(std::pair<const ColumnBase*, NestedColumnAdapter<ESXRecordT>* > adapter);\n    };\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    NestedIdCollection<ESXRecordT, IdAccessorT>::NestedIdCollection ()\n    {}\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    NestedIdCollection<ESXRecordT, IdAccessorT>::~NestedIdCollection()\n    {\n        for (typename std::map<const ColumnBase *, NestedColumnAdapter<ESXRecordT>* >::iterator\n                iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter)\n        {\n            delete (*iter).second;\n        }\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void NestedIdCollection<ESXRecordT, IdAccessorT>::addAdapter(std::pair<const ColumnBase*,\n            NestedColumnAdapter<ESXRecordT>* > adapter)\n    {\n        mAdapters.insert(adapter);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    const NestedColumnAdapter<ESXRecordT>& NestedIdCollection<ESXRecordT, IdAccessorT>::getAdapter(const ColumnBase &column) const\n    {\n        typename std::map<const ColumnBase *, NestedColumnAdapter<ESXRecordT>* >::const_iterator iter =\n            mAdapters.find (&column);\n\n        if (iter==mAdapters.end())\n            throw std::logic_error(\"No such column in the nestedidadapter\");\n\n        return *iter->second;\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position)\n    {\n        Record<ESXRecordT> record;\n        record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));\n\n        getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(record, position);\n\n        Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow)\n    {\n        Record<ESXRecordT> record;\n        record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));\n\n        getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(record, subRow);\n\n        Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    QVariant NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedData (int row,\n            int column, int subRow, int subColumn) const\n    {\n        return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getData(\n                Collection<ESXRecordT, IdAccessorT>::getRecord(row), subRow, subColumn);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row,\n            int column, const QVariant& data, int subRow, int subColumn)\n    {\n        Record<ESXRecordT> record;\n        record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));\n\n        getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData(\n                record, data, subRow, subColumn);\n\n        Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    CSMWorld::NestedTableWrapperBase* NestedIdCollection<ESXRecordT, IdAccessorT>::nestedTable(int row,\n            int column) const\n    {\n        return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).table(\n                Collection<ESXRecordT, IdAccessorT>::getRecord(row));\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row,\n            int column, const CSMWorld::NestedTableWrapperBase& nestedTable)\n    {\n        Record<ESXRecordT> record;\n        record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));\n\n        getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable(\n                record, nestedTable);\n\n        Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedRowsCount(int row, int column) const\n    {\n        return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getRowsCount(\n                Collection<ESXRecordT, IdAccessorT>::getRecord(row));\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedColumnsCount(int row, int column) const\n    {\n        const ColumnBase &nestedColumn = Collection<ESXRecordT, IdAccessorT>::getColumn(column);\n        int numRecords = Collection<ESXRecordT, IdAccessorT>::getSize();\n        if (row >= 0 && row < numRecords)\n        {\n            const Record<ESXRecordT>& record = Collection<ESXRecordT, IdAccessorT>::getRecord(row);\n            return getAdapter(nestedColumn).getColumnsCount(record);\n        }\n        else\n        {\n            // If the row is invalid (or there no records), retrieve the column count using a blank record\n            const Record<ESXRecordT> record;\n            return getAdapter(nestedColumn).getColumnsCount(record);\n        }\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    CSMWorld::NestableColumn *NestedIdCollection<ESXRecordT, IdAccessorT>::getNestableColumn(int column)\n    {\n        return Collection<ESXRecordT, IdAccessorT>::getNestableColumn(column);\n    }\n}\n\n#endif // CSM_WOLRD_NESTEDIDCOLLECTION_H\n"
  },
  {
    "path": "apps/opencs/model/world/nestedinfocollection.cpp",
    "content": "#include \"nestedinfocollection.hpp\"\n\n#include \"nestedcoladapterimp.hpp\"\n\nnamespace CSMWorld\n{\n    NestedInfoCollection::NestedInfoCollection ()\n    {}\n\n    NestedInfoCollection::~NestedInfoCollection()\n    {\n        for (std::map<const ColumnBase *, NestedColumnAdapter<Info>* >::iterator\n                iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter)\n        {\n            delete (*iter).second;\n        }\n    }\n\n    void NestedInfoCollection::addAdapter(std::pair<const ColumnBase*,\n            NestedColumnAdapter<Info>* > adapter)\n    {\n        mAdapters.insert(adapter);\n    }\n\n    const NestedColumnAdapter<Info>& NestedInfoCollection::getAdapter(const ColumnBase &column) const\n    {\n        std::map<const ColumnBase *, NestedColumnAdapter<Info>* >::const_iterator iter =\n            mAdapters.find (&column);\n\n        if (iter==mAdapters.end())\n            throw std::logic_error(\"No such column in the nestedidadapter\");\n\n        return *iter->second;\n    }\n\n    void NestedInfoCollection::addNestedRow(int row, int column, int position)\n    {\n        Record<Info> record;\n        record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));\n\n        getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(record, position);\n\n        Collection<Info, IdAccessor<Info> >::setRecord(row, record);\n    }\n\n    void NestedInfoCollection::removeNestedRows(int row, int column, int subRow)\n    {\n        Record<Info> record;\n        record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));\n\n        getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(record, subRow);\n\n        Collection<Info, IdAccessor<Info> >::setRecord(row, record);\n    }\n\n    QVariant NestedInfoCollection::getNestedData (int row,\n            int column, int subRow, int subColumn) const\n    {\n        return getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).getData(\n                Collection<Info, IdAccessor<Info> >::getRecord(row), subRow, subColumn);\n    }\n\n    void NestedInfoCollection::setNestedData(int row,\n            int column, const QVariant& data, int subRow, int subColumn)\n    {\n        Record<Info> record;\n        record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));\n\n        getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData(\n                record, data, subRow, subColumn);\n\n        Collection<Info, IdAccessor<Info> >::setRecord(row, record);\n    }\n\n    CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row,\n            int column) const\n    {\n        return getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).table(\n                Collection<Info, IdAccessor<Info> >::getRecord(row));\n    }\n\n    void NestedInfoCollection::setNestedTable(int row,\n            int column, const CSMWorld::NestedTableWrapperBase& nestedTable)\n    {\n        Record<Info> record;\n        record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));\n\n        getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable(\n                record, nestedTable);\n\n        Collection<Info, IdAccessor<Info> >::setRecord(row, record);\n    }\n\n    int NestedInfoCollection::getNestedRowsCount(int row, int column) const\n    {\n        return getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).getRowsCount(\n                Collection<Info, IdAccessor<Info> >::getRecord(row));\n    }\n\n    int NestedInfoCollection::getNestedColumnsCount(int row, int column) const\n    {\n        return getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).getColumnsCount(\n                Collection<Info, IdAccessor<Info> >::getRecord(row));\n    }\n\n    CSMWorld::NestableColumn *NestedInfoCollection::getNestableColumn(int column)\n    {\n        return Collection<Info, IdAccessor<Info> >::getNestableColumn(column);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/nestedinfocollection.hpp",
    "content": "#ifndef CSM_WOLRD_NESTEDINFOCOLLECTION_H\n#define CSM_WOLRD_NESTEDINFOCOLLECTION_H\n\n#include <map>\n\n#include \"infocollection.hpp\"\n#include \"nestedcollection.hpp\"\n\nnamespace CSMWorld\n{\n    struct NestedTableWrapperBase;\n\n    template<typename ESXRecordT>\n    class NestedColumnAdapter;\n\n    class NestedInfoCollection : public InfoCollection, public NestedCollection\n    {\n            std::map<const ColumnBase*, NestedColumnAdapter<Info>* > mAdapters;\n\n            const NestedColumnAdapter<Info>& getAdapter(const ColumnBase &column) const;\n\n        public:\n\n            NestedInfoCollection ();\n            ~NestedInfoCollection();\n\n            void addNestedRow(int row, int column, int position) override;\n\n            void removeNestedRows(int row, int column, int subRow) override;\n\n            QVariant getNestedData(int row, int column, int subRow, int subColumn) const override;\n\n            void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) override;\n\n            NestedTableWrapperBase* nestedTable(int row, int column) const override;\n\n            void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable) override;\n\n            int getNestedRowsCount(int row, int column) const override;\n\n            int getNestedColumnsCount(int row, int column) const override;\n\n            // this method is inherited from NestedCollection, not from Collection<Info, IdAccessor<Info> >\n            NestableColumn *getNestableColumn(int column) override;\n\n            void addAdapter(std::pair<const ColumnBase*, NestedColumnAdapter<Info>* > adapter);\n    };\n}\n\n#endif // CSM_WOLRD_NESTEDINFOCOLLECTION_H\n"
  },
  {
    "path": "apps/opencs/model/world/nestedtableproxymodel.cpp",
    "content": "#include \"nestedtableproxymodel.hpp\"\n\n#include <cassert>\n#include \"idtree.hpp\"\n\nCSMWorld::NestedTableProxyModel::NestedTableProxyModel(const QModelIndex& parent,\n                                             ColumnBase::Display columnId,\n                                             CSMWorld::IdTree* parentModel)\n    : mParentColumn(parent.column()),\n      mMainModel(parentModel)\n{\n    const int parentRow = parent.row();\n\n    mId = std::string(parentModel->index(parentRow, 0).data().toString().toUtf8());\n\n    QAbstractProxyModel::setSourceModel(parentModel);\n\n    connect(mMainModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),\n            this, SLOT(forwardRowsAboutToInserted(const QModelIndex &, int, int)));\n\n    connect(mMainModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),\n            this, SLOT(forwardRowsInserted(const QModelIndex &, int, int)));\n\n    connect(mMainModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),\n            this, SLOT(forwardRowsAboutToRemoved(const QModelIndex &, int, int)));\n\n    connect(mMainModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),\n            this, SLOT(forwardRowsRemoved(const QModelIndex &, int, int)));\n\n    connect(mMainModel, SIGNAL(resetStart(const QString&)),\n            this, SLOT(forwardResetStart(const QString&)));\n\n    connect(mMainModel, SIGNAL(resetEnd(const QString&)),\n            this, SLOT(forwardResetEnd(const QString&)));\n\n    connect(mMainModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),\n            this, SLOT(forwardDataChanged(const QModelIndex &, const QModelIndex &)));\n}\n\nQModelIndex CSMWorld::NestedTableProxyModel::mapFromSource(const QModelIndex& sourceIndex) const\n{\n    const QModelIndex& testedParent = mMainModel->parent(sourceIndex);\n    const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn);\n    if (testedParent == parent)\n    {\n        return createIndex(sourceIndex.row(), sourceIndex.column());\n    }\n    else\n    {\n        return QModelIndex();\n    }\n}\n\nQModelIndex CSMWorld::NestedTableProxyModel::mapToSource(const QModelIndex& proxyIndex) const\n{\n    const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn);\n    return mMainModel->index(proxyIndex.row(), proxyIndex.column(), parent);\n}\n\nint CSMWorld::NestedTableProxyModel::rowCount(const QModelIndex& index) const\n{\n    assert (!index.isValid());\n\n    return mMainModel->rowCount(mMainModel->getModelIndex(mId, mParentColumn));\n}\n\nint CSMWorld::NestedTableProxyModel::columnCount(const QModelIndex& parent) const\n{\n    assert (!parent.isValid());\n\n    return mMainModel->columnCount(mMainModel->getModelIndex(mId, mParentColumn));\n}\n\nQModelIndex CSMWorld::NestedTableProxyModel::index(int row, int column, const QModelIndex& parent) const\n{\n    assert (!parent.isValid());\n\n    int numRows = rowCount(parent);\n    int numColumns = columnCount(parent);\n\n    if (row < 0 || row >= numRows || column < 0 || column >= numColumns)\n        return QModelIndex();\n\n    return createIndex(row, column);\n}\n\nQModelIndex CSMWorld::NestedTableProxyModel::parent(const QModelIndex& index) const\n{\n    return QModelIndex();\n}\n\nQVariant CSMWorld::NestedTableProxyModel::headerData(int section,\n                                                Qt::Orientation orientation,\n                                                int role) const\n{\n    return mMainModel->nestedHeaderData(mParentColumn, section, orientation, role);\n}\n\nQVariant CSMWorld::NestedTableProxyModel::data(const QModelIndex& index, int role) const\n{\n    return mMainModel->data(mapToSource(index), role);\n}\n\n// NOTE: Due to mapToSouce(index) the dataChanged() signal resulting from setData() will have the\n// source model's index values.  The indicies need to be converted to the proxy space values.\n// See forwardDataChanged()\nbool CSMWorld::NestedTableProxyModel::setData (const QModelIndex & index, const QVariant & value, int role)\n{\n    return mMainModel->setData(mapToSource(index), value, role);\n}\n\nQt::ItemFlags CSMWorld::NestedTableProxyModel::flags(const QModelIndex& index) const\n{\n    return mMainModel->flags(mapToSource(index));\n}\n\nstd::string CSMWorld::NestedTableProxyModel::getParentId() const\n{\n    return mId;\n}\n\nint CSMWorld::NestedTableProxyModel::getParentColumn() const\n{\n    return mParentColumn;\n}\n\nCSMWorld::IdTree* CSMWorld::NestedTableProxyModel::model() const\n{\n    return mMainModel;\n}\n\nvoid CSMWorld::NestedTableProxyModel::forwardRowsAboutToInserted(const QModelIndex& parent,\n        int first, int last)\n{\n    if (indexIsParent(parent))\n    {\n        beginInsertRows(QModelIndex(), first, last);\n    }\n}\n\nvoid CSMWorld::NestedTableProxyModel::forwardRowsInserted(const QModelIndex& parent, int first, int last)\n{\n    if (indexIsParent(parent))\n    {\n        endInsertRows();\n    }\n}\n\nbool CSMWorld::NestedTableProxyModel::indexIsParent(const QModelIndex& index)\n{\n    return (index.isValid() &&\n            index.column() == mParentColumn &&\n            mMainModel->data(mMainModel->index(index.row(), 0)).toString().toUtf8().constData() == mId);\n}\n\nvoid CSMWorld::NestedTableProxyModel::forwardRowsAboutToRemoved(const QModelIndex& parent,\n        int first, int last)\n{\n    if (indexIsParent(parent))\n    {\n        beginRemoveRows(QModelIndex(), first, last);\n    }\n}\n\nvoid CSMWorld::NestedTableProxyModel::forwardRowsRemoved(const QModelIndex& parent, int first, int last)\n{\n    if (indexIsParent(parent))\n    {\n        endRemoveRows();\n    }\n}\n\nvoid CSMWorld::NestedTableProxyModel::forwardResetStart(const QString& id)\n{\n    if (id.toUtf8() == mId.c_str())\n        beginResetModel();\n}\n\nvoid CSMWorld::NestedTableProxyModel::forwardResetEnd(const QString& id)\n{\n    if (id.toUtf8() == mId.c_str())\n        endResetModel();\n}\n\nvoid CSMWorld::NestedTableProxyModel::forwardDataChanged (const QModelIndex& topLeft,\n        const QModelIndex& bottomRight)\n{\n    const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn);\n\n    if (topLeft.column() <= parent.column() && bottomRight.column() >= parent.column())\n    {\n        emit dataChanged(index(0,0),\n                index(mMainModel->rowCount(parent)-1, mMainModel->columnCount(parent)-1));\n    }\n    else if (topLeft.parent() == parent && bottomRight.parent() == parent)\n    {\n        emit dataChanged(index(topLeft.row(), topLeft.column()), index(bottomRight.row(), bottomRight.column()));\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/nestedtableproxymodel.hpp",
    "content": "#ifndef CSM_WOLRD_NESTEDTABLEPROXYMODEL_H\n#define CSM_WOLRD_NESTEDTABLEPROXYMODEL_H\n\n#include <vector>\n\n#include <QAbstractProxyModel>\n\n#include \"universalid.hpp\"\n#include \"columns.hpp\"\n#include \"columnbase.hpp\"\n\n/*! \\brief\n * Proxy model used to connect view in the dialogue into the nested columns of the main model.\n */\n\nnamespace CSMWorld\n{\n    class CollectionBase;\n    struct RecordBase;\n    class IdTree;\n\n    class NestedTableProxyModel : public QAbstractProxyModel\n    {\n        Q_OBJECT\n\n        const int mParentColumn;\n        IdTree* mMainModel;\n        std::string mId;\n\n    public:\n        NestedTableProxyModel(const QModelIndex& parent,\n                         ColumnBase::Display displayType,\n                         IdTree* parentModel);\n        //parent is the parent of columns to work with. Columnid provides information about the column\n\n        std::string getParentId() const;\n\n        int getParentColumn() const;\n\n        CSMWorld::IdTree* model() const;\n\n        QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override;\n\n        QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;\n\n        int rowCount(const QModelIndex& parent) const override;\n\n        int columnCount(const QModelIndex& parent) const override;\n\n        QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;\n\n        QModelIndex parent(const QModelIndex& index) const override;\n\n        QVariant headerData (int section, Qt::Orientation orientation, int role) const override;\n\n        QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;\n\n        bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override;\n\n        Qt::ItemFlags flags(const QModelIndex& index) const override;\n\n    private:\n        void setupHeaderVectors(ColumnBase::Display columnId);\n\n        bool indexIsParent(const QModelIndex& index);\n\n    private slots:\n        void forwardRowsAboutToInserted(const QModelIndex & parent, int first, int last);\n\n        void forwardRowsInserted(const QModelIndex & parent, int first, int last);\n\n        void forwardRowsAboutToRemoved(const QModelIndex & parent, int first, int last);\n\n        void forwardRowsRemoved(const QModelIndex & parent, int first, int last);\n\n        void forwardResetStart(const QString& id);\n\n        void forwardResetEnd(const QString& id);\n\n        void forwardDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/nestedtablewrapper.cpp",
    "content": "#include \"nestedtablewrapper.hpp\"\n\nCSMWorld::NestedTableWrapperBase::NestedTableWrapperBase()\n{}\n\nCSMWorld::NestedTableWrapperBase::~NestedTableWrapperBase()\n{}\n\nint CSMWorld::NestedTableWrapperBase::size() const\n{\n    return -5;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/nestedtablewrapper.hpp",
    "content": "#ifndef CSM_WOLRD_NESTEDTABLEWRAPPER_H\n#define CSM_WOLRD_NESTEDTABLEWRAPPER_H\n\nnamespace CSMWorld\n{\n    struct NestedTableWrapperBase\n    {\n        virtual ~NestedTableWrapperBase();\n        \n        virtual int size() const;\n        \n        NestedTableWrapperBase();\n    };\n    \n    template<typename NestedTable>\n    struct NestedTableWrapper : public NestedTableWrapperBase\n    {\n        NestedTable mNestedTable;\n\n        NestedTableWrapper(const NestedTable& nestedTable)\n            : mNestedTable(nestedTable) {}\n\n        virtual ~NestedTableWrapper() {}\n\n        int size() const override\n        {\n            return mNestedTable.size(); //i hope that this will be enough\n        }\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/pathgrid.cpp",
    "content": "#include \"cell.hpp\"\n#include \"idcollection.hpp\"\n#include \"pathgrid.hpp\"\n\n#include <sstream>\n\nvoid CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection<Cell>& cells)\n{\n    load (esm, isDeleted);\n\n    // correct ID\n    if (!mId.empty() && mId[0]!='#' && cells.searchId (mId)==-1)\n    {\n        std::ostringstream stream;\n        stream << \"#\" << mData.mX << \" \" << mData.mY;\n        mId = stream.str();\n    }\n}\n\nvoid CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted)\n{\n    ESM::Pathgrid::load (esm, isDeleted);\n\n    mId = mCell;\n    if (mCell.empty())\n    {\n        std::ostringstream stream;\n        stream << \"#\" << mData.mX << \" \" << mData.mY;\n        mId = stream.str();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/model/world/pathgrid.hpp",
    "content": "#ifndef CSM_WOLRD_PATHGRID_H\n#define CSM_WOLRD_PATHGRID_H\n\n#include <vector>\n#include <string>\n\n#include <components/esm/loadpgrd.hpp>\n\nnamespace CSMWorld\n{\n    struct Cell;\n    template<typename T, typename AT>\n    class IdCollection;\n\n    /// \\brief Wrapper for Pathgrid record\n    ///\n    /// \\attention The mData.mX and mData.mY fields of the ESM::Pathgrid struct are not used.\n    /// Exterior cell coordinates are encoded in the pathgrid ID.\n    struct Pathgrid : public ESM::Pathgrid\n    {\n        std::string mId;\n\n        void load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection<Cell>& cells);\n        void load (ESM::ESMReader &esm, bool &isDeleted);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/record.cpp",
    "content": "#include \"record.hpp\"\n\nCSMWorld::RecordBase::~RecordBase() {}\n\nbool CSMWorld::RecordBase::isDeleted() const\n{\n    return mState==State_Deleted || mState==State_Erased;\n}\n\n\nbool CSMWorld::RecordBase::isErased() const\n{\n    return mState==State_Erased;\n}\n\n\nbool CSMWorld::RecordBase::isModified() const\n{\n    return mState==State_Modified || mState==State_ModifiedOnly;\n}"
  },
  {
    "path": "apps/opencs/model/world/record.hpp",
    "content": "#ifndef CSM_WOLRD_RECORD_H\n#define CSM_WOLRD_RECORD_H\n\n#include <stdexcept>\n\nnamespace CSMWorld\n{\n    struct RecordBase\n    {\n        enum State\n        {\n            State_BaseOnly = 0, // defined in base only\n            State_Modified = 1, // exists in base, but has been modified\n            State_ModifiedOnly = 2, // newly created in modified\n            State_Deleted = 3, // exists in base, but has been deleted\n            State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted)\n        };\n\n        State mState;\n\n        virtual ~RecordBase();\n\n        virtual RecordBase *clone() const = 0;\n\n        virtual RecordBase *modifiedCopy() const = 0;\n\n        virtual void assign (const RecordBase& record) = 0;\n        ///< Will throw an exception if the types don't match.\n\n        bool isDeleted() const;\n\n        bool isErased() const;\n\n        bool isModified() const;\n    };\n\n    template <typename ESXRecordT>\n    struct Record : public RecordBase\n    {\n        ESXRecordT mBase;\n        ESXRecordT mModified;\n\n        Record();\n\n        Record(State state,\n                const ESXRecordT *base = 0, const ESXRecordT *modified = 0);\n\n        RecordBase *clone() const override;\n\n        RecordBase *modifiedCopy() const override;\n\n        void assign (const RecordBase& record) override;\n\n        const ESXRecordT& get() const;\n        ///< Throws an exception, if the record is deleted.\n\n        ESXRecordT& get();\n        ///< Throws an exception, if the record is deleted.\n\n        const ESXRecordT& getBase() const;\n        ///< Throws an exception, if the record is deleted. Returns modified, if there is no base.\n\n        void setModified (const ESXRecordT& modified);\n        ///< Throws an exception, if the record is deleted.\n\n        void merge();\n        ///< Merge modified into base.\n    };\n\n    template <typename ESXRecordT>\n    Record<ESXRecordT>::Record()\n    : mBase(), mModified()\n    { }\n\n    template <typename ESXRecordT>\n    Record<ESXRecordT>::Record(State state, const ESXRecordT *base, const ESXRecordT *modified)\n    {\n        if(base)\n            mBase = *base;\n\n        if(modified)\n            mModified = *modified;\n\n        this->mState = state;\n    }\n\n    template <typename ESXRecordT>\n    RecordBase *Record<ESXRecordT>::modifiedCopy() const\n    {\n        return new Record<ESXRecordT> (State_ModifiedOnly, nullptr, &(this->get()));\n    }\n\n    template <typename ESXRecordT>\n    RecordBase *Record<ESXRecordT>::clone() const\n    {\n        return new Record<ESXRecordT> (*this);\n    }\n\n    template <typename ESXRecordT>\n    void Record<ESXRecordT>::assign (const RecordBase& record)\n    {\n        *this = dynamic_cast<const Record<ESXRecordT>& > (record);\n    }\n\n    template <typename ESXRecordT>\n    const ESXRecordT& Record<ESXRecordT>::get() const\n    {\n        if (mState==State_Erased)\n            throw std::logic_error (\"attempt to access a deleted record\");\n\n        return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified;\n    }\n\n    template <typename ESXRecordT>\n    ESXRecordT& Record<ESXRecordT>::get()\n    {\n        if (mState==State_Erased)\n            throw std::logic_error (\"attempt to access a deleted record\");\n\n        return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified;\n    }\n\n    template <typename ESXRecordT>\n    const ESXRecordT& Record<ESXRecordT>::getBase() const\n    {\n        if (mState==State_Erased)\n            throw std::logic_error (\"attempt to access a deleted record\");\n\n        return mState==State_ModifiedOnly ? mModified : mBase;\n    }\n\n    template <typename ESXRecordT>\n    void Record<ESXRecordT>::setModified (const ESXRecordT& modified)\n    {\n        if (mState==State_Erased)\n            throw std::logic_error (\"attempt to modify a deleted record\");\n\n        mModified = modified;\n\n        if (mState!=State_ModifiedOnly)\n            mState = State_Modified;\n    }\n\n    template <typename ESXRecordT>\n    void Record<ESXRecordT>::merge()\n    {\n        if (isModified())\n        {\n            mBase = mModified;\n            mState = State_BaseOnly;\n        }\n        else if (mState==State_Deleted)\n        {\n            mState = State_Erased;\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/ref.cpp",
    "content": "#include \"ref.hpp\"\n\n#include \"cellcoordinates.hpp\"\n\nCSMWorld::CellRef::CellRef() : mNew (true)\n{\n    mRefNum.mIndex = 0;\n    mRefNum.mContentFile = 0;\n}\n\nstd::pair<int, int> CSMWorld::CellRef::getCellIndex() const\n{\n    return CellCoordinates::coordinatesToCellIndex (mPos.pos[0], mPos.pos[1]);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/ref.hpp",
    "content": "#ifndef CSM_WOLRD_REF_H\n#define CSM_WOLRD_REF_H\n\n#include <utility>\n\n#include <components/esm/cellref.hpp>\n\nnamespace CSMWorld\n{\n    /// \\brief Wrapper for CellRef sub record\n    struct CellRef : public ESM::CellRef\n    {\n        std::string mId;\n        std::string mCell;\n        std::string mOriginalCell;\n        bool mNew; // new reference, not counted yet, ref num not assigned yet\n\n        CellRef();\n\n        /// Calculate cell index based on coordinates (x and y)\n        std::pair<int, int> getCellIndex() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/refcollection.cpp",
    "content": "#include \"refcollection.hpp\"\n\n#include <components/esm/loadcell.hpp>\n\n#include \"ref.hpp\"\n#include \"cell.hpp\"\n#include \"universalid.hpp\"\n#include \"record.hpp\"\n\nvoid CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,\n    std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages)\n{\n    Record<Cell> cell = mCells.getRecord (cellIndex);\n\n    Cell& cell2 = base ? cell.mBase : cell.mModified;\n\n    CellRef ref;\n    ref.mNew = false;\n    ESM::MovedCellRef mref;\n    mref.mRefNum.mIndex = 0;\n    bool isDeleted = false;\n\n    while (ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref))\n    {\n        // Keep mOriginalCell empty when in modified (as an indicator that the\n        // original cell will always be equal the current cell).\n        ref.mOriginalCell = base ? cell2.mId : \"\";\n\n        if (cell.get().isExterior())\n        {\n            // Autocalculate the cell index from coordinates first\n            std::pair<int, int> index = ref.getCellIndex();\n\n            ref.mCell = \"#\" + std::to_string(index.first) + \" \" + std::to_string(index.second);\n\n            // Handle non-base moved references\n            if (!base && mref.mRefNum.mIndex != 0)\n            {\n                // Moved references must have a link back to their original cell\n                // See discussion: https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30\n                ref.mOriginalCell = cell2.mId;\n\n                // Some mods may move references outside of the bounds, which often happens they are deleted.\n                // This results in nonsensical autocalculated cell IDs, so we must use the record target cell.\n\n                // Log a warning if the record target cell is different\n                if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1])\n                {\n                    std::string indexCell = ref.mCell;\n                    ref.mCell = \"#\" + std::to_string(mref.mTarget[0]) + \" \" + std::to_string(mref.mTarget[1]);\n\n                    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId (cellIndex));\n                    messages.add(id, \"The position of the moved reference \" + ref.mRefID + \" (cell \" + indexCell + \")\"\n                                     \" does not match the target cell (\" + ref.mCell + \")\",\n                                     std::string(), CSMDoc::Message::Severity_Warning);\n                }\n            }\n        }\n        else\n            ref.mCell = cell2.mId;\n\n        mref.mRefNum.mIndex = 0;\n\n        // ignore content file number\n        std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();\n        unsigned int thisIndex = ref.mRefNum.mIndex & 0x00ffffff;\n        if (ref.mRefNum.mContentFile != -1 && !base) ref.mRefNum.mContentFile = ref.mRefNum.mIndex >> 24;\n\n        for (; iter != cache.end(); ++iter)\n        {\n            if (thisIndex == iter->first.mIndex)\n                break;\n        }\n\n        if (isDeleted)\n        {\n            if (iter==cache.end())\n            {\n                CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,\n                    mCells.getId (cellIndex));\n\n                messages.add (id, \"Attempt to delete a non-existent reference\");\n                continue;\n            }\n\n            int index = getIndex (iter->second);\n\n            Record<CellRef> record = getRecord (index);\n\n            if (base)\n            {\n                removeRows (index, 1);\n                cache.erase (iter);\n            }\n            else\n            {\n                record.mState = RecordBase::State_Deleted;\n                setRecord (index, record);\n            }\n\n            continue;\n        }\n\n        if (iter==cache.end())\n        {\n            // new reference\n            ref.mId = getNewId();\n\n            Record<CellRef> record;\n            record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;\n            const ESM::RefNum refNum = ref.mRefNum;\n            std::string refId = ref.mId;\n            (base ? record.mBase : record.mModified) = std::move(ref);\n\n            appendRecord (record);\n\n            cache.emplace(refNum, std::move(refId));\n        }\n        else\n        {\n            // old reference -> merge\n            ref.mId = iter->second;\n\n            int index = getIndex (ref.mId);\n\n            Record<CellRef> record = getRecord (index);\n            record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;\n            (base ? record.mBase : record.mModified) = std::move(ref);\n\n            setRecord (index, record);\n        }\n    }\n}\n\nstd::string CSMWorld::RefCollection::getNewId()\n{\n    return \"ref#\" + std::to_string(mNextId++);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/refcollection.hpp",
    "content": "#ifndef CSM_WOLRD_REFCOLLECTION_H\n#define CSM_WOLRD_REFCOLLECTION_H\n\n#include <map>\n\n#include \"../doc/stage.hpp\"\n\n#include \"collection.hpp\"\n#include \"ref.hpp\"\n#include \"record.hpp\"\n\nnamespace CSMWorld\n{\n    struct Cell;\n    class UniversalId;\n\n    /// \\brief References in cells\n    class RefCollection : public Collection<CellRef>\n    {\n            Collection<Cell>& mCells;\n            int mNextId;\n\n        public:\n            // MSVC needs the constructor for a class inheriting a template to be defined in header\n            RefCollection (Collection<Cell>& cells)\n              : mCells (cells), mNextId (0)\n            {}\n\n            void load (ESM::ESMReader& reader, int cellIndex, bool base,\n                std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages);\n            ///< Load a sequence of references.\n\n            std::string getNewId();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/refidadapter.cpp",
    "content": "#include \"refidadapter.hpp\"\n\nCSMWorld::RefIdAdapter::RefIdAdapter() {}\n\nCSMWorld::RefIdAdapter::~RefIdAdapter() {}\n\nCSMWorld::NestedRefIdAdapterBase::NestedRefIdAdapterBase() {}\n\nCSMWorld::NestedRefIdAdapterBase::~NestedRefIdAdapterBase() {}\n"
  },
  {
    "path": "apps/opencs/model/world/refidadapter.hpp",
    "content": "#ifndef CSM_WOLRD_REFIDADAPTER_H\n#define CSM_WOLRD_REFIDADAPTER_H\n\n#include <string>\n#include <vector>\n\n/*! \\brief\n * Adapters acts as indirection layer, abstracting details of the record types (in the wrappers) from the higher levels of model.\n * Please notice that nested adaptor uses helper classes for actually performing any actions. Different record types require different helpers (needs to be created in the subclass and then fetched via member function).\n *\n * Important point: don't forget to make sure that getData on the nestedColumn returns true (otherwise code will not treat the index pointing to the column as having children!\n */\n\nclass QVariant;\n\nnamespace CSMWorld\n{\n    class RefIdColumn;\n    class RefIdData;\n    struct RecordBase;\n    struct NestedTableWrapperBase;\n    class HelperBase;\n\n    class RefIdAdapter\n    {\n            // not implemented\n            RefIdAdapter (const RefIdAdapter&);\n            RefIdAdapter& operator= (const RefIdAdapter&);\n\n        public:\n\n            RefIdAdapter();\n\n            virtual ~RefIdAdapter();\n\n            virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int idnex)\n                const = 0;\n            ///< If called on the nest column, should return QVariant(true).\n\n            virtual void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const = 0;\n            ///< If the data type does not match an exception is thrown.\n\n            virtual std::string getId (const RecordBase& record) const = 0;\n\n            virtual void setId(RecordBase& record, const std::string& id) = 0; // used by RefIdCollection::cloneRecord()\n    };\n\n    class NestedRefIdAdapterBase\n    {\n        public:\n            NestedRefIdAdapterBase();\n\n            virtual ~NestedRefIdAdapterBase();\n\n            virtual void setNestedData (const RefIdColumn *column,\n                    RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const = 0;\n\n            virtual QVariant getNestedData (const RefIdColumn *column,\n                    const RefIdData& data, int index, int subRowIndex, int subColIndex) const = 0;\n\n            virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const = 0;\n\n            virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const = 0;\n\n            virtual void removeNestedRow (const RefIdColumn *column,\n                    RefIdData& data, int index, int rowToRemove) const = 0;\n\n            virtual void addNestedRow (const RefIdColumn *column,\n                    RefIdData& data, int index, int position) const = 0;\n\n            virtual void setNestedTable (const RefIdColumn* column,\n                    RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const = 0;\n\n            virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                    const RefIdData& data, int index) const = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/refidadapterimp.cpp",
    "content": "#include \"refidadapterimp.hpp\"\n\n#include <cassert>\n#include <stdexcept>\n#include <utility>\n\n#include <components/esm/loadcont.hpp>\n#include <components/esm/loadmgef.hpp>\n\n#include \"nestedtablewrapper.hpp\"\n\nCSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)\n: InventoryColumns (columns), mEffects(nullptr) {}\n\nCSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const PotionColumns& columns,\n    const RefIdColumn *autoCalc)\n: InventoryRefIdAdapter<ESM::Potion> (UniversalId::Type_Potion, columns),\n  mColumns(columns), mAutoCalc (autoCalc)\n{}\n\nQVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,\n    int index) const\n{\n    const Record<ESM::Potion>& record = static_cast<const Record<ESM::Potion>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion)));\n\n    if (column==mAutoCalc)\n        return record.get().mData.mAutoCalc!=0;\n\n    // to show nested tables in dialogue subview, see IdTree::hasChildren()\n    if (column==mColumns.mEffects)\n        return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n    return InventoryRefIdAdapter<ESM::Potion>::getData (column, data, index);\n}\n\nvoid CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Potion>& record = static_cast<Record<ESM::Potion>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion)));\n\n    ESM::Potion potion = record.get();\n\n    if (column==mAutoCalc)\n        potion.mData.mAutoCalc = value.toInt();\n    else\n    {\n        InventoryRefIdAdapter<ESM::Potion>::setData (column, data, index, value);\n\n        return;\n    }\n\n    record.setModified(potion);\n}\n\n\nCSMWorld::IngredientColumns::IngredientColumns (const InventoryColumns& columns)\n: InventoryColumns (columns)\n, mEffects(nullptr)\n{}\n\nCSMWorld::IngredientRefIdAdapter::IngredientRefIdAdapter (const IngredientColumns& columns)\n: InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, columns),\n  mColumns(columns)\n{}\n\nQVariant CSMWorld::IngredientRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,\n    int index) const\n{\n    if (column==mColumns.mEffects)\n        return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);\n\n    return InventoryRefIdAdapter<ESM::Ingredient>::getData (column, data, index);\n}\n\nvoid CSMWorld::IngredientRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    InventoryRefIdAdapter<ESM::Ingredient>::setData (column, data, index, value);\n\n    return;\n}\n\n\nCSMWorld::IngredEffectRefIdAdapter::IngredEffectRefIdAdapter()\n: mType(UniversalId::Type_Ingredient)\n{}\n\nCSMWorld::IngredEffectRefIdAdapter::~IngredEffectRefIdAdapter()\n{}\n\nvoid CSMWorld::IngredEffectRefIdAdapter::addNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int position) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::IngredEffectRefIdAdapter::removeNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int rowToRemove) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::IngredEffectRefIdAdapter::setNestedTable (const RefIdColumn* column,\n        RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const\n{\n    Record<ESM::Ingredient>& record =\n        static_cast<Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n    ESM::Ingredient ingredient = record.get();\n\n    ingredient.mData =\n        static_cast<const NestedTableWrapper<std::vector<ESM::Ingredient::IRDTstruct> >&>(nestedTable).mNestedTable.at(0);\n\n    record.setModified (ingredient);\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::IngredEffectRefIdAdapter::nestedTable (const RefIdColumn* column,\n        const RefIdData& data, int index) const\n{\n    const Record<ESM::Ingredient>& record =\n        static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n    // return the whole struct\n    std::vector<ESM::Ingredient::IRDTstruct> wrap;\n    wrap.push_back(record.get().mData);\n\n    // deleted by dtor of NestedTableStoring\n    return new NestedTableWrapper<std::vector<ESM::Ingredient::IRDTstruct> >(wrap);\n}\n\nQVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData (const RefIdColumn *column,\n        const RefIdData& data, int index, int subRowIndex, int subColIndex) const\n{\n    const Record<ESM::Ingredient>& record =\n        static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n    if (subRowIndex < 0 || subRowIndex >= 4)\n        throw std::runtime_error (\"index out of range\");\n\n    switch (subColIndex)\n    {\n        case 0: return record.get().mData.mEffectID[subRowIndex];\n        case 1:\n        {\n            switch (record.get().mData.mEffectID[subRowIndex])\n            {\n                case ESM::MagicEffect::DrainSkill:\n                case ESM::MagicEffect::DamageSkill:\n                case ESM::MagicEffect::RestoreSkill:\n                case ESM::MagicEffect::FortifySkill:\n                case ESM::MagicEffect::AbsorbSkill:\n                    return record.get().mData.mSkills[subRowIndex];\n                default:\n                    return QVariant();\n            }\n        }\n        case 2:\n        {\n            switch (record.get().mData.mEffectID[subRowIndex])\n            {\n                case ESM::MagicEffect::DrainAttribute:\n                case ESM::MagicEffect::DamageAttribute:\n                case ESM::MagicEffect::RestoreAttribute:\n                case ESM::MagicEffect::FortifyAttribute:\n                case ESM::MagicEffect::AbsorbAttribute:\n                    return record.get().mData.mAttributes[subRowIndex];\n                default:\n                    return QVariant();\n            }\n        }\n        default:\n            throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n    }\n}\n\nvoid CSMWorld::IngredEffectRefIdAdapter::setNestedData (const RefIdColumn *column,\n        RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const\n{\n    Record<ESM::Ingredient>& record =\n        static_cast<Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n    ESM::Ingredient ingredient = record.get();\n\n    if (subRowIndex < 0 || subRowIndex >= 4)\n        throw std::runtime_error (\"index out of range\");\n\n    switch(subColIndex)\n    {\n        case 0: ingredient.mData.mEffectID[subRowIndex] = value.toInt(); break;\n        case 1: ingredient.mData.mSkills[subRowIndex] = value.toInt(); break;\n        case 2: ingredient.mData.mAttributes[subRowIndex] = value.toInt(); break;\n        default:\n            throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n    }\n\n    record.setModified (ingredient);\n}\n\nint CSMWorld::IngredEffectRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const\n{\n    return 3; // effect, skill, attribute\n}\n\nint CSMWorld::IngredEffectRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const\n{\n    return 4; // up to 4 effects\n}\n\n\nCSMWorld::ApparatusRefIdAdapter::ApparatusRefIdAdapter (const InventoryColumns& columns,\n    const RefIdColumn *type, const RefIdColumn *quality)\n: InventoryRefIdAdapter<ESM::Apparatus> (UniversalId::Type_Apparatus, columns),\n    mType (type), mQuality (quality)\n{}\n\nQVariant CSMWorld::ApparatusRefIdAdapter::getData (const RefIdColumn *column,\n    const RefIdData& data, int index) const\n{\n    const Record<ESM::Apparatus>& record = static_cast<const Record<ESM::Apparatus>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus)));\n\n    if (column==mType)\n        return record.get().mData.mType;\n\n    if (column==mQuality)\n        return record.get().mData.mQuality;\n\n    return InventoryRefIdAdapter<ESM::Apparatus>::getData (column, data, index);\n}\n\nvoid CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Apparatus>& record = static_cast<Record<ESM::Apparatus>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus)));\n\n    ESM::Apparatus apparatus = record.get();\n\n    if (column==mType)\n        apparatus.mData.mType = value.toInt();\n    else if (column==mQuality)\n        apparatus.mData.mQuality = value.toFloat();\n    else\n    {\n        InventoryRefIdAdapter<ESM::Apparatus>::setData (column, data, index, value);\n\n        return;\n    }\n    record.setModified(apparatus);\n}\n\n\nCSMWorld::ArmorRefIdAdapter::ArmorRefIdAdapter (const EnchantableColumns& columns,\n    const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor,\n    const RefIdColumn *partRef)\n: EnchantableRefIdAdapter<ESM::Armor> (UniversalId::Type_Armor, columns),\n    mType (type), mHealth (health), mArmor (armor), mPartRef(partRef)\n{}\n\nQVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column,\n    const RefIdData& data, int index) const\n{\n    const Record<ESM::Armor>& record = static_cast<const Record<ESM::Armor>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor)));\n\n    if (column==mType)\n        return record.get().mData.mType;\n\n    if (column==mHealth)\n        return record.get().mData.mHealth;\n\n    if (column==mArmor)\n        return record.get().mData.mArmor;\n\n    if (column==mPartRef)\n        return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n    return EnchantableRefIdAdapter<ESM::Armor>::getData (column, data, index);\n}\n\nvoid CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Armor>& record = static_cast<Record<ESM::Armor>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor)));\n\n    ESM::Armor armor = record.get();\n\n    if (column==mType)\n        armor.mData.mType = value.toInt();\n    else if (column==mHealth)\n        armor.mData.mHealth = value.toInt();\n    else if (column==mArmor)\n        armor.mData.mArmor = value.toInt();\n    else\n    {\n        EnchantableRefIdAdapter<ESM::Armor>::setData (column, data, index, value);\n\n        return;\n    }\n\n    record.setModified(armor);\n}\n\nCSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns,\n    const RefIdColumn *bookType, const RefIdColumn *skill, const RefIdColumn *text)\n: EnchantableRefIdAdapter<ESM::Book> (UniversalId::Type_Book, columns),\n    mBookType (bookType), mSkill (skill), mText (text)\n{}\n\nQVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column,\n    const RefIdData& data, int index) const\n{\n    const Record<ESM::Book>& record = static_cast<const Record<ESM::Book>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book)));\n\n    if (column==mBookType)\n        return record.get().mData.mIsScroll;\n\n    if (column==mSkill)\n        return record.get().mData.mSkillId;\n\n    if (column==mText)\n        return QString::fromUtf8 (record.get().mText.c_str());\n\n    return EnchantableRefIdAdapter<ESM::Book>::getData (column, data, index);\n}\n\nvoid CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Book>& record = static_cast<Record<ESM::Book>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book)));\n\n    ESM::Book book = record.get();\n\n    if (column==mBookType)\n        book.mData.mIsScroll = value.toInt();\n    else if (column==mSkill)\n        book.mData.mSkillId = value.toInt();\n    else if (column==mText)\n        book.mText = value.toString().toUtf8().data();\n    else\n    {\n        EnchantableRefIdAdapter<ESM::Book>::setData (column, data, index, value);\n\n        return;\n    }\n\n    record.setModified(book);\n}\n\nCSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns,\n    const RefIdColumn *type, const RefIdColumn *partRef)\n: EnchantableRefIdAdapter<ESM::Clothing> (UniversalId::Type_Clothing, columns), mType (type),\n  mPartRef(partRef)\n{}\n\nQVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column,\n    const RefIdData& data, int index) const\n{\n    const Record<ESM::Clothing>& record = static_cast<const Record<ESM::Clothing>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing)));\n\n    if (column==mType)\n        return record.get().mData.mType;\n\n    if (column==mPartRef)\n        return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n    return EnchantableRefIdAdapter<ESM::Clothing>::getData (column, data, index);\n}\n\nvoid CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Clothing>& record = static_cast<Record<ESM::Clothing>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing)));\n\n    ESM::Clothing clothing = record.get();\n\n    if (column==mType)\n        clothing.mData.mType = value.toInt();\n    else\n    {\n        EnchantableRefIdAdapter<ESM::Clothing>::setData (column, data, index, value);\n\n        return;\n    }\n\n    record.setModified(clothing);\n}\n\nCSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns,\n    const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn, const RefIdColumn *content)\n: NameRefIdAdapter<ESM::Container> (UniversalId::Type_Container, columns), mWeight (weight),\n  mOrganic (organic), mRespawn (respawn), mContent(content)\n{}\n\nQVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column,\n                                                   const RefIdData& data,\n                                                   int index) const\n{\n    const Record<ESM::Container>& record = static_cast<const Record<ESM::Container>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container)));\n\n    if (column==mWeight)\n        return record.get().mWeight;\n\n    if (column==mOrganic)\n        return (record.get().mFlags & ESM::Container::Organic)!=0;\n\n    if (column==mRespawn)\n        return (record.get().mFlags & ESM::Container::Respawn)!=0;\n\n    if (column==mContent)\n        return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n    return NameRefIdAdapter<ESM::Container>::getData (column, data, index);\n}\n\nvoid CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Container>& record = static_cast<Record<ESM::Container>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container)));\n\n    ESM::Container container = record.get();\n\n    if (column==mWeight)\n        container.mWeight = value.toFloat();\n    else if (column==mOrganic)\n    {\n        if (value.toInt())\n            container.mFlags |= ESM::Container::Organic;\n        else\n            container.mFlags &= ~ESM::Container::Organic;\n    }\n    else if (column==mRespawn)\n    {\n        if (value.toInt())\n            container.mFlags |= ESM::Container::Respawn;\n        else\n            container.mFlags &= ~ESM::Container::Respawn;\n    }\n    else\n    {\n        NameRefIdAdapter<ESM::Container>::setData (column, data, index, value);\n\n        return;\n    }\n\n    record.setModified(container);\n}\n\nCSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns)\n: ActorColumns (actorColumns),\n  mType(nullptr),\n  mScale(nullptr),\n  mOriginal(nullptr),\n  mAttributes(nullptr),\n  mAttacks(nullptr),\n  mMisc(nullptr),\n  mBloodType(nullptr)\n{}\n\nCSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns)\n: ActorRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature, columns), mColumns (columns)\n{}\n\nQVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,\n    int index) const\n{\n    const Record<ESM::Creature>& record = static_cast<const Record<ESM::Creature>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n\n    if (column==mColumns.mType)\n        return record.get().mData.mType;\n\n    if (column==mColumns.mScale)\n        return record.get().mScale;\n\n    if (column==mColumns.mOriginal)\n        return QString::fromUtf8 (record.get().mOriginal.c_str());\n\n    if (column==mColumns.mAttributes)\n        return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);\n\n    if (column==mColumns.mAttacks)\n        return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);\n\n    if (column==mColumns.mMisc)\n        return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n    if (column == mColumns.mBloodType)\n        return record.get().mBloodType;\n\n    std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n        mColumns.mFlags.find (column);\n\n    if (iter!=mColumns.mFlags.end())\n        return (record.get().mFlags & iter->second)!=0;\n\n    return ActorRefIdAdapter<ESM::Creature>::getData (column, data, index);\n}\n\nvoid CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Creature>& record = static_cast<Record<ESM::Creature>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n\n    ESM::Creature creature = record.get();\n\n    if (column==mColumns.mType)\n        creature.mData.mType = value.toInt();\n    else if (column==mColumns.mScale)\n        creature.mScale = value.toFloat();\n    else if (column==mColumns.mOriginal)\n        creature.mOriginal = value.toString().toUtf8().constData();\n    else if (column == mColumns.mBloodType)\n        creature.mBloodType = value.toInt();\n    else\n    {\n        std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n            mColumns.mFlags.find (column);\n\n        if (iter!=mColumns.mFlags.end())\n        {\n            if (value.toInt()!=0)\n                creature.mFlags |= iter->second;\n            else\n                creature.mFlags &= ~iter->second;\n        }\n        else\n        {\n            ActorRefIdAdapter<ESM::Creature>::setData (column, data, index, value);\n\n            return;\n        }\n    }\n\n    record.setModified(creature);\n}\n\nCSMWorld::DoorRefIdAdapter::DoorRefIdAdapter (const NameColumns& columns,\n    const RefIdColumn *openSound, const RefIdColumn *closeSound)\n: NameRefIdAdapter<ESM::Door> (UniversalId::Type_Door, columns), mOpenSound (openSound),\n  mCloseSound (closeSound)\n{}\n\nQVariant CSMWorld::DoorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,\n    int index) const\n{\n    const Record<ESM::Door>& record = static_cast<const Record<ESM::Door>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door)));\n\n    if (column==mOpenSound)\n        return QString::fromUtf8 (record.get().mOpenSound.c_str());\n\n    if (column==mCloseSound)\n        return QString::fromUtf8 (record.get().mCloseSound.c_str());\n\n    return NameRefIdAdapter<ESM::Door>::getData (column, data, index);\n}\n\nvoid CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Door>& record = static_cast<Record<ESM::Door>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door)));\n\n    ESM::Door door = record.get();\n\n    if (column==mOpenSound)\n        door.mOpenSound = value.toString().toUtf8().constData();\n    else if (column==mCloseSound)\n        door.mCloseSound = value.toString().toUtf8().constData();\n    else\n    {\n        NameRefIdAdapter<ESM::Door>::setData (column, data, index, value);\n\n        return;\n    }\n\n    record.setModified(door);\n}\n\nCSMWorld::LightColumns::LightColumns (const InventoryColumns& columns)\n: InventoryColumns (columns)\n, mTime(nullptr)\n, mRadius(nullptr)\n, mColor(nullptr)\n, mSound(nullptr)\n, mEmitterType(nullptr)\n{}\n\nCSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns)\n: InventoryRefIdAdapter<ESM::Light> (UniversalId::Type_Light, columns), mColumns (columns)\n{}\n\nQVariant CSMWorld::LightRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,\n    int index) const\n{\n    const Record<ESM::Light>& record = static_cast<const Record<ESM::Light>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light)));\n\n    if (column==mColumns.mTime)\n        return record.get().mData.mTime;\n\n    if (column==mColumns.mRadius)\n        return record.get().mData.mRadius;\n\n    if (column==mColumns.mColor)\n        return record.get().mData.mColor;\n\n    if (column==mColumns.mSound)\n        return QString::fromUtf8 (record.get().mSound.c_str());\n\n    if (column == mColumns.mEmitterType)\n    {\n        int mask = ESM::Light::Flicker | ESM::Light::FlickerSlow | ESM::Light::Pulse | ESM::Light::PulseSlow;\n\n        if ((record.get().mData.mFlags & mask) == ESM::Light::Flicker)\n            return 1;\n\n        if ((record.get().mData.mFlags & mask) == ESM::Light::FlickerSlow)\n            return 2;\n\n        if ((record.get().mData.mFlags & mask) == ESM::Light::Pulse)\n            return 3;\n\n        if ((record.get().mData.mFlags & mask) == ESM::Light::PulseSlow)\n            return 4;\n\n        return 0;\n    }\n\n    std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n        mColumns.mFlags.find (column);\n\n    if (iter!=mColumns.mFlags.end())\n        return (record.get().mData.mFlags & iter->second)!=0;\n\n    return InventoryRefIdAdapter<ESM::Light>::getData (column, data, index);\n}\n\nvoid CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Light>& record = static_cast<Record<ESM::Light>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light)));\n\n    ESM::Light light = record.get();\n\n    if (column==mColumns.mTime)\n        light.mData.mTime = value.toInt();\n    else if (column==mColumns.mRadius)\n        light.mData.mRadius = value.toInt();\n    else if (column==mColumns.mColor)\n        light.mData.mColor = value.toInt();\n    else if (column==mColumns.mSound)\n        light.mSound = value.toString().toUtf8().constData();\n    else if (column == mColumns.mEmitterType)\n    {\n        int mask = ~(ESM::Light::Flicker | ESM::Light::FlickerSlow | ESM::Light::Pulse | ESM::Light::PulseSlow);\n\n        if (value.toInt() == 0)\n            light.mData.mFlags = light.mData.mFlags & mask;\n        else if (value.toInt() == 1)\n            light.mData.mFlags = (light.mData.mFlags & mask) | ESM::Light::Flicker;\n        else if (value.toInt() == 2)\n            light.mData.mFlags = (light.mData.mFlags & mask) | ESM::Light::FlickerSlow;\n        else if (value.toInt() == 3)\n            light.mData.mFlags = (light.mData.mFlags & mask) | ESM::Light::Pulse;\n        else\n            light.mData.mFlags = (light.mData.mFlags & mask) | ESM::Light::PulseSlow;\n    }\n    else\n    {\n        std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n            mColumns.mFlags.find (column);\n\n        if (iter!=mColumns.mFlags.end())\n        {\n            if (value.toInt()!=0)\n                light.mData.mFlags |= iter->second;\n            else\n                light.mData.mFlags &= ~iter->second;\n        }\n        else\n        {\n            InventoryRefIdAdapter<ESM::Light>::setData (column, data, index, value);\n\n            return;\n        }\n    }\n\n    record.setModified (light);\n}\n\nCSMWorld::MiscRefIdAdapter::MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key)\n: InventoryRefIdAdapter<ESM::Miscellaneous> (UniversalId::Type_Miscellaneous, columns), mKey (key)\n{}\n\nQVariant CSMWorld::MiscRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,\n    int index) const\n{\n    const Record<ESM::Miscellaneous>& record = static_cast<const Record<ESM::Miscellaneous>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous)));\n\n    if (column==mKey)\n        return record.get().mData.mIsKey!=0;\n\n    return InventoryRefIdAdapter<ESM::Miscellaneous>::getData (column, data, index);\n}\n\nvoid CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Miscellaneous>& record = static_cast<Record<ESM::Miscellaneous>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous)));\n\n    ESM::Miscellaneous misc = record.get();\n\n    if (column==mKey)\n        misc.mData.mIsKey = value.toInt();\n    else\n    {\n        InventoryRefIdAdapter<ESM::Miscellaneous>::setData (column, data, index, value);\n\n        return;\n    }\n\n    record.setModified(misc);\n}\n\nCSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns)\n: ActorColumns (actorColumns),\n  mRace(nullptr),\n  mClass(nullptr),\n  mFaction(nullptr),\n  mHair(nullptr),\n  mHead(nullptr),\n  mAttributes(nullptr),\n  mSkills(nullptr),\n  mMisc(nullptr),\n  mBloodType(nullptr),\n  mGender(nullptr)\n{}\n\nCSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)\n: ActorRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc, columns), mColumns (columns)\n{}\n\nQVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index)\n    const\n{\n    const Record<ESM::NPC>& record = static_cast<const Record<ESM::NPC>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n\n    if (column==mColumns.mRace)\n        return QString::fromUtf8 (record.get().mRace.c_str());\n\n    if (column==mColumns.mClass)\n        return QString::fromUtf8 (record.get().mClass.c_str());\n\n    if (column==mColumns.mFaction)\n        return QString::fromUtf8 (record.get().mFaction.c_str());\n\n    if (column==mColumns.mHair)\n        return QString::fromUtf8 (record.get().mHair.c_str());\n\n    if (column==mColumns.mHead)\n        return QString::fromUtf8 (record.get().mHead.c_str());\n\n    if (column==mColumns.mAttributes || column==mColumns.mSkills)\n    {\n        if ((record.get().mFlags & ESM::NPC::Autocalc) != 0)\n            return QVariant::fromValue(ColumnBase::TableEdit_None);\n        else\n            return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);\n    }\n\n    if (column==mColumns.mMisc)\n        return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n    if (column == mColumns.mBloodType)\n        return record.get().mBloodType;\n\n    if (column == mColumns.mGender)\n    {\n        // Implemented this way to allow additional gender types in the future.\n        if ((record.get().mFlags & ESM::NPC::Female) == ESM::NPC::Female)\n            return 1;\n\n        return 0;\n    }\n\n    std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n        mColumns.mFlags.find (column);\n\n    if (iter!=mColumns.mFlags.end())\n        return (record.get().mFlags & iter->second)!=0;\n\n    return ActorRefIdAdapter<ESM::NPC>::getData (column, data, index);\n}\n\nvoid CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::NPC>& record = static_cast<Record<ESM::NPC>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n\n    ESM::NPC npc = record.get();\n\n    if (column==mColumns.mRace)\n        npc.mRace = value.toString().toUtf8().constData();\n    else if (column==mColumns.mClass)\n        npc.mClass = value.toString().toUtf8().constData();\n    else if (column==mColumns.mFaction)\n        npc.mFaction = value.toString().toUtf8().constData();\n    else if (column==mColumns.mHair)\n        npc.mHair = value.toString().toUtf8().constData();\n    else if (column==mColumns.mHead)\n        npc.mHead = value.toString().toUtf8().constData();\n    else if (column == mColumns.mBloodType)\n        npc.mBloodType = value.toInt();\n    else if (column == mColumns.mGender)\n    {\n        // Implemented this way to allow additional gender types in the future.\n        if (value.toInt() == 1)\n            npc.mFlags = (npc.mFlags & ~ESM::NPC::Female) | ESM::NPC::Female;\n        else\n            npc.mFlags = npc.mFlags & ~ESM::NPC::Female;\n    }\n    else\n    {\n        std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n            mColumns.mFlags.find (column);\n\n        if (iter!=mColumns.mFlags.end())\n        {\n            if (value.toInt()!=0)\n                npc.mFlags |= iter->second;\n            else\n                npc.mFlags &= ~iter->second;\n\n            if (iter->second == ESM::NPC::Autocalc)\n                npc.mNpdtType = (value.toInt() != 0) ? ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS\n                                                     : ESM::NPC::NPC_DEFAULT;\n        }\n        else\n        {\n            ActorRefIdAdapter<ESM::NPC>::setData (column, data, index, value);\n\n            return;\n        }\n    }\n\n    record.setModified (npc);\n}\n\nCSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter ()\n{}\n\nvoid CSMWorld::NpcAttributesRefIdAdapter::addNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int position) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::NpcAttributesRefIdAdapter::removeNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int rowToRemove) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::NpcAttributesRefIdAdapter::setNestedTable (const RefIdColumn* column,\n        RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const\n{\n    Record<ESM::NPC>& record =\n        static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n    ESM::NPC npc = record.get();\n\n    // store the whole struct\n    npc.mNpdt =\n        static_cast<const NestedTableWrapper<std::vector<ESM::NPC::NPDTstruct52> > &>(nestedTable).mNestedTable.at(0);\n\n    record.setModified (npc);\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::NpcAttributesRefIdAdapter::nestedTable (const RefIdColumn* column,\n        const RefIdData& data, int index) const\n{\n    const Record<ESM::NPC>& record =\n        static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n\n    // return the whole struct\n    std::vector<ESM::NPC::NPDTstruct52> wrap;\n    wrap.push_back(record.get().mNpdt);\n    // deleted by dtor of NestedTableStoring\n    return new NestedTableWrapper<std::vector<ESM::NPC::NPDTstruct52> >(wrap);\n}\n\nQVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *column,\n        const RefIdData& data, int index, int subRowIndex, int subColIndex) const\n{\n    const Record<ESM::NPC>& record =\n        static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n\n    const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt;\n\n    if (subColIndex == 0)\n        return subRowIndex;\n    else if (subColIndex == 1)\n        switch (subRowIndex)\n        {\n            case 0: return static_cast<int>(npcStruct.mStrength);\n            case 1: return static_cast<int>(npcStruct.mIntelligence);\n            case 2: return static_cast<int>(npcStruct.mWillpower);\n            case 3: return static_cast<int>(npcStruct.mAgility);\n            case 4: return static_cast<int>(npcStruct.mSpeed);\n            case 5: return static_cast<int>(npcStruct.mEndurance);\n            case 6: return static_cast<int>(npcStruct.mPersonality);\n            case 7: return static_cast<int>(npcStruct.mLuck);\n            default: return QVariant(); // throw an exception here?\n        }\n    else\n        return QVariant(); // throw an exception here?\n}\n\nvoid CSMWorld::NpcAttributesRefIdAdapter::setNestedData (const RefIdColumn *column,\n        RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const\n{\n    Record<ESM::NPC>& record =\n        static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc)));\n    ESM::NPC npc = record.get();\n    ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt;\n\n    if (subColIndex == 1)\n        switch(subRowIndex)\n        {\n            case 0: npcStruct.mStrength = static_cast<unsigned char>(value.toInt()); break;\n            case 1: npcStruct.mIntelligence = static_cast<unsigned char>(value.toInt()); break;\n            case 2: npcStruct.mWillpower = static_cast<unsigned char>(value.toInt()); break;\n            case 3: npcStruct.mAgility = static_cast<unsigned char>(value.toInt()); break;\n            case 4: npcStruct.mSpeed = static_cast<unsigned char>(value.toInt()); break;\n            case 5: npcStruct.mEndurance = static_cast<unsigned char>(value.toInt()); break;\n            case 6: npcStruct.mPersonality = static_cast<unsigned char>(value.toInt()); break;\n            case 7: npcStruct.mLuck = static_cast<unsigned char>(value.toInt()); break;\n            default: return; // throw an exception here?\n        }\n    else\n        return; // throw an exception here?\n\n    record.setModified (npc);\n}\n\nint CSMWorld::NpcAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const\n{\n    return 2;\n}\n\nint CSMWorld::NpcAttributesRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const\n{\n    // There are 8 attributes\n    return 8;\n}\n\nCSMWorld::NpcSkillsRefIdAdapter::NpcSkillsRefIdAdapter ()\n{}\n\nvoid CSMWorld::NpcSkillsRefIdAdapter::addNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int position) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::NpcSkillsRefIdAdapter::removeNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int rowToRemove) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::NpcSkillsRefIdAdapter::setNestedTable (const RefIdColumn* column,\n        RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const\n{\n    Record<ESM::NPC>& record =\n        static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n    ESM::NPC npc = record.get();\n\n    // store the whole struct\n    npc.mNpdt =\n        static_cast<const NestedTableWrapper<std::vector<ESM::NPC::NPDTstruct52> > &>(nestedTable).mNestedTable.at(0);\n\n    record.setModified (npc);\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::NpcSkillsRefIdAdapter::nestedTable (const RefIdColumn* column,\n        const RefIdData& data, int index) const\n{\n    const Record<ESM::NPC>& record =\n        static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n\n    // return the whole struct\n    std::vector<ESM::NPC::NPDTstruct52> wrap;\n    wrap.push_back(record.get().mNpdt);\n    // deleted by dtor of NestedTableStoring\n    return new NestedTableWrapper<std::vector<ESM::NPC::NPDTstruct52> >(wrap);\n}\n\nQVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *column,\n        const RefIdData& data, int index, int subRowIndex, int subColIndex) const\n{\n    const Record<ESM::NPC>& record =\n        static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n\n    const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt;\n\n    if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length)\n        throw std::runtime_error (\"index out of range\");\n\n    if (subColIndex == 0)\n        return subRowIndex;\n    else if (subColIndex == 1)\n        return static_cast<int>(npcStruct.mSkills[subRowIndex]);\n    else\n        return QVariant(); // throw an exception here?\n}\n\nvoid CSMWorld::NpcSkillsRefIdAdapter::setNestedData (const RefIdColumn *column,\n        RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const\n{\n    Record<ESM::NPC>& record =\n        static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc)));\n    ESM::NPC npc = record.get();\n    ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt;\n\n    if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length)\n        throw std::runtime_error (\"index out of range\");\n\n    if (subColIndex == 1)\n        npcStruct.mSkills[subRowIndex] = static_cast<unsigned char>(value.toInt());\n    else\n        return; // throw an exception here?\n\n    record.setModified (npc);\n}\n\nint CSMWorld::NpcSkillsRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const\n{\n    return 2;\n}\n\nint CSMWorld::NpcSkillsRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const\n{\n    // There are 27 skills\n    return ESM::Skill::Length;\n}\n\nCSMWorld::NpcMiscRefIdAdapter::NpcMiscRefIdAdapter ()\n{}\n\nCSMWorld::NpcMiscRefIdAdapter::~NpcMiscRefIdAdapter()\n{}\n\nvoid CSMWorld::NpcMiscRefIdAdapter::addNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int position) const\n{\n    throw std::logic_error (\"cannot add a row to a fixed table\");\n}\n\nvoid CSMWorld::NpcMiscRefIdAdapter::removeNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int rowToRemove) const\n{\n    throw std::logic_error (\"cannot remove a row to a fixed table\");\n}\n\nvoid CSMWorld::NpcMiscRefIdAdapter::setNestedTable (const RefIdColumn* column,\n        RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const\n{\n    throw std::logic_error (\"table operation not supported\");\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::NpcMiscRefIdAdapter::nestedTable (const RefIdColumn* column,\n        const RefIdData& data, int index) const\n{\n    throw std::logic_error (\"table operation not supported\");\n}\n\nQVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column,\n        const RefIdData& data, int index, int subRowIndex, int subColIndex) const\n{\n    const Record<ESM::NPC>& record =\n        static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));\n\n    bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0;\n\n    if (autoCalc)\n        switch (subColIndex)\n        {\n            case 0: return static_cast<int>(record.get().mNpdt.mLevel);\n            case 1: return QVariant(QVariant::UserType);\n            case 2: return QVariant(QVariant::UserType);\n            case 3: return QVariant(QVariant::UserType);\n            case 4: return static_cast<int>(record.get().mNpdt.mDisposition);\n            case 5: return static_cast<int>(record.get().mNpdt.mReputation);\n            case 6: return static_cast<int>(record.get().mNpdt.mRank);\n            case 7: return record.get().mNpdt.mGold;\n            case 8: return record.get().mPersistent == true;\n            default: return QVariant(); // throw an exception here?\n        }\n    else\n        switch (subColIndex)\n        {\n            case 0: return static_cast<int>(record.get().mNpdt.mLevel);\n            case 1: return static_cast<int>(record.get().mNpdt.mHealth);\n            case 2: return static_cast<int>(record.get().mNpdt.mMana);\n            case 3: return static_cast<int>(record.get().mNpdt.mFatigue);\n            case 4: return static_cast<int>(record.get().mNpdt.mDisposition);\n            case 5: return static_cast<int>(record.get().mNpdt.mReputation);\n            case 6: return static_cast<int>(record.get().mNpdt.mRank);\n            case 7: return record.get().mNpdt.mGold;\n            case 8: return record.get().mPersistent == true;\n            default: return QVariant(); // throw an exception here?\n        }\n}\n\nvoid CSMWorld::NpcMiscRefIdAdapter::setNestedData (const RefIdColumn *column,\n        RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const\n{\n    Record<ESM::NPC>& record =\n        static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc)));\n    ESM::NPC npc = record.get();\n\n    bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0;\n\n    if (autoCalc)\n        switch(subColIndex)\n        {\n            case 0: npc.mNpdt.mLevel = static_cast<short>(value.toInt()); break;\n            case 1: return;\n            case 2: return;\n            case 3: return;\n            case 4: npc.mNpdt.mDisposition = static_cast<signed char>(value.toInt()); break;\n            case 5: npc.mNpdt.mReputation = static_cast<signed char>(value.toInt()); break;\n            case 6: npc.mNpdt.mRank = static_cast<signed char>(value.toInt()); break;\n            case 7: npc.mNpdt.mGold = value.toInt(); break;\n            case 8: npc.mPersistent = value.toBool(); break;\n            default: return; // throw an exception here?\n        }\n    else\n        switch(subColIndex)\n        {\n            case 0: npc.mNpdt.mLevel = static_cast<short>(value.toInt()); break;\n            case 1: npc.mNpdt.mHealth = static_cast<unsigned short>(value.toInt()); break;\n            case 2: npc.mNpdt.mMana = static_cast<unsigned short>(value.toInt()); break;\n            case 3: npc.mNpdt.mFatigue = static_cast<unsigned short>(value.toInt()); break;\n            case 4: npc.mNpdt.mDisposition = static_cast<signed char>(value.toInt()); break;\n            case 5: npc.mNpdt.mReputation = static_cast<signed char>(value.toInt()); break;\n            case 6: npc.mNpdt.mRank = static_cast<signed char>(value.toInt()); break;\n            case 7: npc.mNpdt.mGold = value.toInt(); break;\n            case 8: npc.mPersistent = value.toBool(); break;\n            default: return; // throw an exception here?\n        }\n\n    record.setModified (npc);\n}\n\nint CSMWorld::NpcMiscRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const\n{\n    return 9; // Level, Health, Mana, Fatigue, Disposition, Reputation, Rank, Gold, Persist\n}\n\nint CSMWorld::NpcMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const\n{\n    return 1; // fixed at size 1\n}\n\nCSMWorld::CreatureAttributesRefIdAdapter::CreatureAttributesRefIdAdapter()\n{}\n\nvoid CSMWorld::CreatureAttributesRefIdAdapter::addNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int position) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::CreatureAttributesRefIdAdapter::removeNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int rowToRemove) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::CreatureAttributesRefIdAdapter::setNestedTable (const RefIdColumn* column,\n        RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const\n{\n    Record<ESM::Creature>& record =\n        static_cast<Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n    ESM::Creature creature = record.get();\n\n    // store the whole struct\n    creature.mData =\n        static_cast<const NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0);\n\n    record.setModified (creature);\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttributesRefIdAdapter::nestedTable (const RefIdColumn* column,\n        const RefIdData& data, int index) const\n{\n    const Record<ESM::Creature>& record =\n        static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n\n    // return the whole struct\n    std::vector<ESM::Creature::NPDTstruct> wrap;\n    wrap.push_back(record.get().mData);\n    // deleted by dtor of NestedTableStoring\n    return new NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> >(wrap);\n}\n\nQVariant CSMWorld::CreatureAttributesRefIdAdapter::getNestedData (const RefIdColumn *column,\n        const RefIdData& data, int index, int subRowIndex, int subColIndex) const\n{\n    const Record<ESM::Creature>& record =\n        static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n\n    const ESM::Creature& creature = record.get();\n\n    if (subColIndex == 0)\n        return subRowIndex;\n    else if (subColIndex == 1)\n        switch (subRowIndex)\n        {\n            case 0: return creature.mData.mStrength;\n            case 1: return creature.mData.mIntelligence;\n            case 2: return creature.mData.mWillpower;\n            case 3: return creature.mData.mAgility;\n            case 4: return creature.mData.mSpeed;\n            case 5: return creature.mData.mEndurance;\n            case 6: return creature.mData.mPersonality;\n            case 7: return creature.mData.mLuck;\n            default: return QVariant(); // throw an exception here?\n        }\n    else\n        return QVariant(); // throw an exception here?\n}\n\nvoid CSMWorld::CreatureAttributesRefIdAdapter::setNestedData (const RefIdColumn *column,\n        RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const\n{\n    Record<ESM::Creature>& record =\n        static_cast<Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Creature)));\n    ESM::Creature creature = record.get();\n\n    if (subColIndex == 1)\n        switch(subRowIndex)\n        {\n            case 0: creature.mData.mStrength = value.toInt(); break;\n            case 1: creature.mData.mIntelligence = value.toInt(); break;\n            case 2: creature.mData.mWillpower = value.toInt(); break;\n            case 3: creature.mData.mAgility = value.toInt(); break;\n            case 4: creature.mData.mSpeed = value.toInt(); break;\n            case 5: creature.mData.mEndurance = value.toInt(); break;\n            case 6: creature.mData.mPersonality = value.toInt(); break;\n            case 7: creature.mData.mLuck = value.toInt(); break;\n            default: return; // throw an exception here?\n        }\n    else\n        return; // throw an exception here?\n\n    record.setModified (creature);\n}\n\nint CSMWorld::CreatureAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const\n{\n    return 2;\n}\n\nint CSMWorld::CreatureAttributesRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const\n{\n    // There are 8 attributes\n    return 8;\n}\n\nCSMWorld::CreatureAttackRefIdAdapter::CreatureAttackRefIdAdapter()\n{}\n\nvoid CSMWorld::CreatureAttackRefIdAdapter::addNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int position) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::CreatureAttackRefIdAdapter::removeNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int rowToRemove) const\n{\n    // Do nothing, this table cannot be changed by the user\n}\n\nvoid CSMWorld::CreatureAttackRefIdAdapter::setNestedTable (const RefIdColumn* column,\n        RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const\n{\n    Record<ESM::Creature>& record =\n        static_cast<Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n    ESM::Creature creature = record.get();\n\n    // store the whole struct\n    creature.mData =\n        static_cast<const NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0);\n\n    record.setModified (creature);\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttackRefIdAdapter::nestedTable (const RefIdColumn* column,\n        const RefIdData& data, int index) const\n{\n    const Record<ESM::Creature>& record =\n        static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n\n    // return the whole struct\n    std::vector<ESM::Creature::NPDTstruct> wrap;\n    wrap.push_back(record.get().mData);\n    // deleted by dtor of NestedTableStoring\n    return new NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> >(wrap);\n}\n\nQVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn *column,\n        const RefIdData& data, int index, int subRowIndex, int subColIndex) const\n{\n    const Record<ESM::Creature>& record =\n        static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n\n    const ESM::Creature& creature = record.get();\n\n    if (subRowIndex < 0 || subRowIndex > 2)\n        throw std::runtime_error (\"index out of range\");\n\n    if (subColIndex == 0)\n        return subRowIndex + 1;\n    else if (subColIndex == 1 || subColIndex == 2)\n        return creature.mData.mAttack[(subRowIndex * 2) + (subColIndex - 1)];\n    else\n        throw std::runtime_error (\"index out of range\");\n}\n\nvoid CSMWorld::CreatureAttackRefIdAdapter::setNestedData (const RefIdColumn *column,\n        RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const\n{\n    Record<ESM::Creature>& record =\n        static_cast<Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Creature)));\n    ESM::Creature creature = record.get();\n\n    if (subRowIndex < 0 || subRowIndex > 2)\n        throw std::runtime_error (\"index out of range\");\n\n    if (subColIndex == 1 || subColIndex == 2)\n        creature.mData.mAttack[(subRowIndex * 2) + (subColIndex - 1)] = value.toInt();\n    else\n        return; // throw an exception here?\n\n    record.setModified (creature);\n}\n\nint CSMWorld::CreatureAttackRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const\n{\n    return 3;\n}\n\nint CSMWorld::CreatureAttackRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const\n{\n    // There are 3 attacks\n    return 3;\n}\n\nCSMWorld::CreatureMiscRefIdAdapter::CreatureMiscRefIdAdapter()\n{}\n\nCSMWorld::CreatureMiscRefIdAdapter::~CreatureMiscRefIdAdapter()\n{}\n\nvoid CSMWorld::CreatureMiscRefIdAdapter::addNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int position) const\n{\n    throw std::logic_error (\"cannot add a row to a fixed table\");\n}\n\nvoid CSMWorld::CreatureMiscRefIdAdapter::removeNestedRow (const RefIdColumn *column,\n        RefIdData& data, int index, int rowToRemove) const\n{\n    throw std::logic_error (\"cannot remove a row to a fixed table\");\n}\n\nvoid CSMWorld::CreatureMiscRefIdAdapter::setNestedTable (const RefIdColumn* column,\n        RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const\n{\n    throw std::logic_error (\"table operation not supported\");\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::CreatureMiscRefIdAdapter::nestedTable (const RefIdColumn* column,\n        const RefIdData& data, int index) const\n{\n    throw std::logic_error (\"table operation not supported\");\n}\n\nQVariant CSMWorld::CreatureMiscRefIdAdapter::getNestedData (const RefIdColumn *column,\n        const RefIdData& data, int index, int subRowIndex, int subColIndex) const\n{\n    const Record<ESM::Creature>& record =\n        static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));\n\n    const ESM::Creature& creature = record.get();\n\n    switch (subColIndex)\n    {\n        case 0: return creature.mData.mLevel;\n        case 1: return creature.mData.mHealth;\n        case 2: return creature.mData.mMana;\n        case 3: return creature.mData.mFatigue;\n        case 4: return creature.mData.mSoul;\n        case 5: return creature.mData.mCombat;\n        case 6: return creature.mData.mMagic;\n        case 7: return creature.mData.mStealth;\n        case 8: return creature.mData.mGold;\n        default: return QVariant(); // throw an exception here?\n    }\n}\n\nvoid CSMWorld::CreatureMiscRefIdAdapter::setNestedData (const RefIdColumn *column,\n        RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const\n{\n    Record<ESM::Creature>& record =\n        static_cast<Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Creature)));\n    ESM::Creature creature = record.get();\n\n    switch(subColIndex)\n    {\n        case 0: creature.mData.mLevel   = value.toInt(); break;\n        case 1: creature.mData.mHealth  = value.toInt(); break;\n        case 2: creature.mData.mMana    = value.toInt(); break;\n        case 3: creature.mData.mFatigue = value.toInt(); break;\n        case 4: creature.mData.mSoul    = value.toInt(); break;\n        case 5: creature.mData.mCombat  = value.toInt(); break;\n        case 6: creature.mData.mMagic   = value.toInt(); break;\n        case 7: creature.mData.mStealth = value.toInt(); break;\n        case 8: creature.mData.mGold    = value.toInt(); break;\n        default: return; // throw an exception here?\n    }\n\n    record.setModified (creature);\n}\n\nint CSMWorld::CreatureMiscRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const\n{\n    return 9; // Level, Health, Mana, Fatigue, Soul, Combat, Magic, Steath, Gold\n}\n\nint CSMWorld::CreatureMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const\n{\n    return 1; // fixed at size 1\n}\n\nCSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns)\n: EnchantableColumns (columns)\n, mType(nullptr)\n, mHealth(nullptr)\n, mSpeed(nullptr)\n, mReach(nullptr)\n, mChop{nullptr}\n, mSlash{nullptr}\n, mThrust{nullptr}\n{}\n\nCSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns)\n: EnchantableRefIdAdapter<ESM::Weapon> (UniversalId::Type_Weapon, columns), mColumns (columns)\n{}\n\nQVariant CSMWorld::WeaponRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,\n    int index) const\n{\n    const Record<ESM::Weapon>& record = static_cast<const Record<ESM::Weapon>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon)));\n\n    if (column==mColumns.mType)\n        return record.get().mData.mType;\n\n    if (column==mColumns.mHealth)\n        return record.get().mData.mHealth;\n\n    if (column==mColumns.mSpeed)\n        return record.get().mData.mSpeed;\n\n    if (column==mColumns.mReach)\n        return record.get().mData.mReach;\n\n    for (int i=0; i<2; ++i)\n    {\n        if (column==mColumns.mChop[i])\n            return record.get().mData.mChop[i];\n\n        if (column==mColumns.mSlash[i])\n            return record.get().mData.mSlash[i];\n\n        if (column==mColumns.mThrust[i])\n            return record.get().mData.mThrust[i];\n    }\n\n    std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n        mColumns.mFlags.find (column);\n\n    if (iter!=mColumns.mFlags.end())\n        return (record.get().mData.mFlags & iter->second)!=0;\n\n    return EnchantableRefIdAdapter<ESM::Weapon>::getData (column, data, index);\n}\n\nvoid CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,\n    const QVariant& value) const\n{\n    Record<ESM::Weapon>& record = static_cast<Record<ESM::Weapon>&> (\n        data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon)));\n\n    ESM::Weapon weapon = record.get();\n\n    if (column==mColumns.mType)\n        weapon.mData.mType = value.toInt();\n    else if (column==mColumns.mHealth)\n        weapon.mData.mHealth = value.toInt();\n    else if (column==mColumns.mSpeed)\n        weapon.mData.mSpeed = value.toFloat();\n    else if (column==mColumns.mReach)\n        weapon.mData.mReach = value.toFloat();\n    else if (column==mColumns.mChop[0])\n        weapon.mData.mChop[0] = value.toInt();\n    else if (column==mColumns.mChop[1])\n        weapon.mData.mChop[1] = value.toInt();\n    else if (column==mColumns.mSlash[0])\n        weapon.mData.mSlash[0] = value.toInt();\n    else if (column==mColumns.mSlash[1])\n        weapon.mData.mSlash[1] = value.toInt();\n    else if (column==mColumns.mThrust[0])\n        weapon.mData.mThrust[0] = value.toInt();\n    else if (column==mColumns.mThrust[1])\n        weapon.mData.mThrust[1] = value.toInt();\n    else\n    {\n        std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n            mColumns.mFlags.find (column);\n\n        if (iter!=mColumns.mFlags.end())\n        {\n            if (value.toInt()!=0)\n                weapon.mData.mFlags |= iter->second;\n            else\n                weapon.mData.mFlags &= ~iter->second;\n        }\n        else\n        {\n            EnchantableRefIdAdapter<ESM::Weapon>::setData (column, data, index, value);\n            return; // Don't overwrite changes made by base class\n        }\n    }\n\n    record.setModified(weapon);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/refidadapterimp.hpp",
    "content": "#ifndef CSM_WOLRD_REFIDADAPTERIMP_H\n#define CSM_WOLRD_REFIDADAPTERIMP_H\n\n#include <map>\n\n#include <QVariant>\n\n#include <components/esm/loadalch.hpp>\n#include <components/esm/loadench.hpp>\n#include <components/esm/loadappa.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/loadcrea.hpp>\n\n#include \"columnbase.hpp\"\n#include \"record.hpp\"\n#include \"refiddata.hpp\"\n#include \"universalid.hpp\"\n#include \"refidadapter.hpp\"\n#include \"nestedtablewrapper.hpp\"\n\nnamespace CSMWorld\n{\n    struct BaseColumns\n    {\n        const RefIdColumn *mId;\n        const RefIdColumn *mModified;\n        const RefIdColumn *mType;\n    };\n\n    /// \\brief Base adapter for all refereceable record types\n    /// Adapters that can handle nested tables, needs to return valid qvariant for parent columns\n    template<typename RecordT>\n    class BaseRefIdAdapter : public RefIdAdapter\n    {\n            UniversalId::Type mType;\n            BaseColumns mBase;\n\n        public:\n\n            BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base);\n\n            std::string getId (const RecordBase& record) const override;\n\n            void setId (RecordBase& record, const std::string& id) override;\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n\n            UniversalId::Type getType() const;\n    };\n\n    template<typename RecordT>\n    BaseRefIdAdapter<RecordT>::BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base)\n    : mType (type), mBase (base)\n    {}\n\n    template<typename RecordT>\n    void BaseRefIdAdapter<RecordT>::setId (RecordBase& record, const std::string& id)\n    {\n        (dynamic_cast<Record<RecordT>&> (record).get().mId) = id;\n    }\n\n    template<typename RecordT>\n    std::string BaseRefIdAdapter<RecordT>::getId (const RecordBase& record) const\n    {\n        return dynamic_cast<const Record<RecordT>&> (record).get().mId;\n    }\n\n    template<typename RecordT>\n    QVariant BaseRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,\n        int index) const\n    {\n        const Record<RecordT>& record = static_cast<const Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n        if (column==mBase.mId)\n            return QString::fromUtf8 (record.get().mId.c_str());\n\n        if (column==mBase.mModified)\n        {\n            if (record.mState==Record<RecordT>::State_Erased)\n                return static_cast<int> (Record<RecordT>::State_Deleted);\n\n            return static_cast<int> (record.mState);\n        }\n\n        if (column==mBase.mType)\n            return static_cast<int> (mType);\n\n        return QVariant();\n    }\n\n    template<typename RecordT>\n    void BaseRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,\n        const QVariant& value) const\n    {\n        Record<RecordT>& record = static_cast<Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n        if (column==mBase.mModified)\n            record.mState = static_cast<RecordBase::State> (value.toInt());\n    }\n\n    template<typename RecordT>\n    UniversalId::Type BaseRefIdAdapter<RecordT>::getType() const\n    {\n        return mType;\n    }\n\n\n    struct ModelColumns : public BaseColumns\n    {\n        const RefIdColumn *mModel;\n\n        ModelColumns (const BaseColumns& base) : BaseColumns (base), mModel(nullptr) {}\n    };\n\n    /// \\brief Adapter for IDs with models (all but levelled lists)\n    template<typename RecordT>\n    class ModelRefIdAdapter : public BaseRefIdAdapter<RecordT>\n    {\n            ModelColumns mModel;\n\n        public:\n\n            ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    template<typename RecordT>\n    ModelRefIdAdapter<RecordT>::ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns)\n    : BaseRefIdAdapter<RecordT> (type, columns), mModel (columns)\n    {}\n\n    template<typename RecordT>\n    QVariant ModelRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,\n        int index) const\n    {\n        const Record<RecordT>& record = static_cast<const Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        if (column==mModel.mModel)\n            return QString::fromUtf8 (record.get().mModel.c_str());\n\n        return BaseRefIdAdapter<RecordT>::getData (column, data, index);\n    }\n\n    template<typename RecordT>\n    void ModelRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,\n        const QVariant& value) const\n    {\n        Record<RecordT>& record = static_cast<Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        RecordT record2 = record.get();\n        if (column==mModel.mModel)\n            record2.mModel = value.toString().toUtf8().constData();\n        else\n        {\n            BaseRefIdAdapter<RecordT>::setData (column, data, index, value);\n            return;\n        }\n\n        record.setModified(record2);\n    }\n\n    struct NameColumns : public ModelColumns\n    {\n        const RefIdColumn *mName;\n        const RefIdColumn *mScript;\n\n        NameColumns (const ModelColumns& base)\n        : ModelColumns (base)\n        , mName(nullptr)\n        , mScript(nullptr)\n        {}\n    };\n\n    /// \\brief Adapter for IDs with names (all but levelled lists and statics)\n    template<typename RecordT>\n    class NameRefIdAdapter : public ModelRefIdAdapter<RecordT>\n    {\n            NameColumns mName;\n\n        public:\n\n            NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    template<typename RecordT>\n    NameRefIdAdapter<RecordT>::NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns)\n    : ModelRefIdAdapter<RecordT> (type, columns), mName (columns)\n    {}\n\n    template<typename RecordT>\n    QVariant NameRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,\n        int index) const\n    {\n        const Record<RecordT>& record = static_cast<const Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        if (column==mName.mName)\n            return QString::fromUtf8 (record.get().mName.c_str());\n\n        if (column==mName.mScript)\n            return QString::fromUtf8 (record.get().mScript.c_str());\n\n        return ModelRefIdAdapter<RecordT>::getData (column, data, index);\n    }\n\n    template<typename RecordT>\n    void NameRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,\n        const QVariant& value) const\n    {\n        Record<RecordT>& record = static_cast<Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        RecordT record2 = record.get();\n        if (column==mName.mName)\n            record2.mName = value.toString().toUtf8().constData();\n        else if (column==mName.mScript)\n            record2.mScript = value.toString().toUtf8().constData();\n        else\n        {\n            ModelRefIdAdapter<RecordT>::setData (column, data, index, value);\n            return;\n        }\n\n        record.setModified(record2);\n    }\n\n    struct InventoryColumns : public NameColumns\n    {\n        const RefIdColumn *mIcon;\n        const RefIdColumn *mWeight;\n        const RefIdColumn *mValue;\n\n        InventoryColumns (const NameColumns& base)\n        : NameColumns (base)\n        , mIcon(nullptr)\n        , mWeight(nullptr)\n        , mValue(nullptr)\n        {}\n    };\n\n    /// \\brief Adapter for IDs that can go into an inventory\n    template<typename RecordT>\n    class InventoryRefIdAdapter : public NameRefIdAdapter<RecordT>\n    {\n            InventoryColumns mInventory;\n\n        public:\n\n            InventoryRefIdAdapter (UniversalId::Type type, const InventoryColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    template<typename RecordT>\n    InventoryRefIdAdapter<RecordT>::InventoryRefIdAdapter (UniversalId::Type type,\n        const InventoryColumns& columns)\n    : NameRefIdAdapter<RecordT> (type, columns), mInventory (columns)\n    {}\n\n    template<typename RecordT>\n    QVariant InventoryRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,\n        int index) const\n    {\n        const Record<RecordT>& record = static_cast<const Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        if (column==mInventory.mIcon)\n            return QString::fromUtf8 (record.get().mIcon.c_str());\n\n        if (column==mInventory.mWeight)\n            return record.get().mData.mWeight;\n\n        if (column==mInventory.mValue)\n            return record.get().mData.mValue;\n\n        return NameRefIdAdapter<RecordT>::getData (column, data, index);\n    }\n\n    template<typename RecordT>\n    void InventoryRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,\n        const QVariant& value) const\n    {\n        Record<RecordT>& record = static_cast<Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        RecordT record2 = record.get();\n        if (column==mInventory.mIcon)\n            record2.mIcon = value.toString().toUtf8().constData();\n        else if (column==mInventory.mWeight)\n            record2.mData.mWeight = value.toFloat();\n        else if (column==mInventory.mValue)\n            record2.mData.mValue = value.toInt();\n        else\n        {\n            NameRefIdAdapter<RecordT>::setData (column, data, index, value);\n            return;\n        }\n\n        record.setModified(record2);\n    }\n\n    struct PotionColumns : public InventoryColumns\n    {\n        const RefIdColumn *mEffects;\n\n        PotionColumns (const InventoryColumns& columns);\n    };\n\n    class PotionRefIdAdapter : public InventoryRefIdAdapter<ESM::Potion>\n    {\n            PotionColumns mColumns;\n            const RefIdColumn *mAutoCalc;\n\n        public:\n\n            PotionRefIdAdapter (const PotionColumns& columns, const RefIdColumn *autoCalc);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    struct IngredientColumns : public InventoryColumns\n    {\n        const RefIdColumn *mEffects;\n\n        IngredientColumns (const InventoryColumns& columns);\n    };\n\n    class IngredientRefIdAdapter : public InventoryRefIdAdapter<ESM::Ingredient>\n    {\n            IngredientColumns mColumns;\n\n        public:\n\n            IngredientRefIdAdapter (const IngredientColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    class IngredEffectRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        IngredEffectRefIdAdapter (const IngredEffectRefIdAdapter&);\n        IngredEffectRefIdAdapter& operator= (const IngredEffectRefIdAdapter&);\n\n    public:\n\n        IngredEffectRefIdAdapter();\n\n        virtual ~IngredEffectRefIdAdapter();\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override;\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override;\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override;\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override;\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override;\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override;\n    };\n\n    struct EnchantableColumns : public InventoryColumns\n    {\n        const RefIdColumn *mEnchantment;\n        const RefIdColumn *mEnchantmentPoints;\n\n        EnchantableColumns (const InventoryColumns& base)\n        : InventoryColumns (base)\n        , mEnchantment(nullptr)\n        , mEnchantmentPoints(nullptr)\n        {}\n    };\n\n    /// \\brief Adapter for enchantable IDs\n    template<typename RecordT>\n    class EnchantableRefIdAdapter : public InventoryRefIdAdapter<RecordT>\n    {\n            EnchantableColumns mEnchantable;\n\n        public:\n\n            EnchantableRefIdAdapter (UniversalId::Type type, const EnchantableColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    template<typename RecordT>\n    EnchantableRefIdAdapter<RecordT>::EnchantableRefIdAdapter (UniversalId::Type type,\n        const EnchantableColumns& columns)\n    : InventoryRefIdAdapter<RecordT> (type, columns), mEnchantable (columns)\n    {}\n\n    template<typename RecordT>\n    QVariant EnchantableRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,\n        int index) const\n    {\n        const Record<RecordT>& record = static_cast<const Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        if (column==mEnchantable.mEnchantment)\n            return QString::fromUtf8 (record.get().mEnchant.c_str());\n\n        if (column==mEnchantable.mEnchantmentPoints)\n            return static_cast<int> (record.get().mData.mEnchant);\n\n        return InventoryRefIdAdapter<RecordT>::getData (column, data, index);\n    }\n\n    template<typename RecordT>\n    void EnchantableRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data,\n        int index, const QVariant& value) const\n    {\n        Record<RecordT>& record = static_cast<Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        RecordT record2 = record.get();\n        if (column==mEnchantable.mEnchantment)\n            record2.mEnchant = value.toString().toUtf8().constData();\n        else if (column==mEnchantable.mEnchantmentPoints)\n            record2.mData.mEnchant = value.toInt();\n        else\n        {\n            InventoryRefIdAdapter<RecordT>::setData (column, data, index, value);\n            return;\n        }\n\n        record.setModified(record2);\n    }\n\n    struct ToolColumns : public InventoryColumns\n    {\n        const RefIdColumn *mQuality;\n        const RefIdColumn *mUses;\n\n        ToolColumns (const InventoryColumns& base)\n        : InventoryColumns (base)\n        , mQuality(nullptr)\n        , mUses(nullptr)\n        {}\n    };\n\n    /// \\brief Adapter for tools with limited uses IDs (lockpick, repair, probes)\n    template<typename RecordT>\n    class ToolRefIdAdapter : public InventoryRefIdAdapter<RecordT>\n    {\n            ToolColumns mTools;\n\n        public:\n\n            ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    template<typename RecordT>\n    ToolRefIdAdapter<RecordT>::ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns)\n    : InventoryRefIdAdapter<RecordT> (type, columns), mTools (columns)\n    {}\n\n    template<typename RecordT>\n    QVariant ToolRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,\n        int index) const\n    {\n        const Record<RecordT>& record = static_cast<const Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        if (column==mTools.mQuality)\n            return record.get().mData.mQuality;\n\n        if (column==mTools.mUses)\n            return record.get().mData.mUses;\n\n        return InventoryRefIdAdapter<RecordT>::getData (column, data, index);\n    }\n\n    template<typename RecordT>\n    void ToolRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data,\n        int index, const QVariant& value) const\n    {\n        Record<RecordT>& record = static_cast<Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        RecordT record2 = record.get();\n        if (column==mTools.mQuality)\n            record2.mData.mQuality = value.toFloat();\n        else if (column==mTools.mUses)\n            record2.mData.mUses = value.toInt();\n        else\n        {\n            InventoryRefIdAdapter<RecordT>::setData (column, data, index, value);\n            return;\n        }\n\n        record.setModified(record2);\n    }\n\n    struct ActorColumns : public NameColumns\n    {\n        const RefIdColumn *mHello;\n        const RefIdColumn *mFlee;\n        const RefIdColumn *mFight;\n        const RefIdColumn *mAlarm;\n        const RefIdColumn *mInventory;\n        const RefIdColumn *mSpells;\n        const RefIdColumn *mDestinations;\n        const RefIdColumn *mAiPackages;\n        std::map<const RefIdColumn *, unsigned int> mServices;\n\n        ActorColumns (const NameColumns& base)\n            : NameColumns (base)\n            , mHello(nullptr)\n            , mFlee(nullptr)\n            , mFight(nullptr)\n            , mAlarm(nullptr)\n            , mInventory(nullptr)\n            , mSpells(nullptr)\n            , mDestinations(nullptr)\n            , mAiPackages(nullptr)\n            {}\n    };\n\n    /// \\brief Adapter for actor IDs (handles common AI functionality)\n    template<typename RecordT>\n    class ActorRefIdAdapter : public NameRefIdAdapter<RecordT>\n    {\n            ActorColumns mActors;\n\n        public:\n\n            ActorRefIdAdapter (UniversalId::Type type, const ActorColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    template<typename RecordT>\n    ActorRefIdAdapter<RecordT>::ActorRefIdAdapter (UniversalId::Type type,\n        const ActorColumns& columns)\n    : NameRefIdAdapter<RecordT> (type, columns), mActors (columns)\n    {}\n\n    template<typename RecordT>\n    QVariant ActorRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,\n        int index) const\n    {\n        const Record<RecordT>& record = static_cast<const Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        if (column==mActors.mHello)\n            return record.get().mAiData.mHello;\n\n        if (column==mActors.mFlee)\n            return record.get().mAiData.mFlee;\n\n        if (column==mActors.mFight)\n            return record.get().mAiData.mFight;\n\n        if (column==mActors.mAlarm)\n            return record.get().mAiData.mAlarm;\n\n        if (column==mActors.mInventory)\n            return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n        if (column==mActors.mSpells)\n            return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n        if (column==mActors.mDestinations)\n            return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n        if (column==mActors.mAiPackages)\n            return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n        std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n            mActors.mServices.find (column);\n\n        if (iter!=mActors.mServices.end())\n            return (record.get().mAiData.mServices & iter->second)!=0;\n\n        return NameRefIdAdapter<RecordT>::getData (column, data, index);\n    }\n\n    template<typename RecordT>\n    void ActorRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,\n        const QVariant& value) const\n    {\n        Record<RecordT>& record = static_cast<Record<RecordT>&> (\n            data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));\n\n        RecordT record2 = record.get();\n        if (column==mActors.mHello)\n            record2.mAiData.mHello = value.toInt();\n        else if (column==mActors.mFlee) // Flee, Fight and Alarm ratings are probabilities.\n            record2.mAiData.mFlee = std::min(100, value.toInt());\n        else if (column==mActors.mFight)\n            record2.mAiData.mFight = std::min(100, value.toInt());\n        else if (column==mActors.mAlarm)\n            record2.mAiData.mAlarm = std::min(100, value.toInt());\n        else\n        {\n            typename std::map<const RefIdColumn *, unsigned int>::const_iterator iter =\n                mActors.mServices.find (column);\n            if (iter!=mActors.mServices.end())\n            {\n                if (value.toInt()!=0)\n                    record2.mAiData.mServices |= iter->second;\n                else\n                    record2.mAiData.mServices &= ~iter->second;\n            }\n            else\n            {\n                NameRefIdAdapter<RecordT>::setData (column, data, index, value);\n                return;\n            }\n        }\n\n        record.setModified(record2);\n    }\n\n    class ApparatusRefIdAdapter : public InventoryRefIdAdapter<ESM::Apparatus>\n    {\n            const RefIdColumn *mType;\n            const RefIdColumn *mQuality;\n\n        public:\n\n            ApparatusRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *type,\n                const RefIdColumn *quality);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    class ArmorRefIdAdapter : public EnchantableRefIdAdapter<ESM::Armor>\n    {\n            const RefIdColumn *mType;\n            const RefIdColumn *mHealth;\n            const RefIdColumn *mArmor;\n            const RefIdColumn *mPartRef;\n\n        public:\n\n            ArmorRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type,\n                const RefIdColumn *health, const RefIdColumn *armor, const RefIdColumn *partRef);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    class BookRefIdAdapter : public EnchantableRefIdAdapter<ESM::Book>\n    {\n            const RefIdColumn *mBookType;\n            const RefIdColumn *mSkill;\n            const RefIdColumn *mText;\n\n        public:\n\n            BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *bookType,\n                const RefIdColumn *skill, const RefIdColumn *text);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    class ClothingRefIdAdapter : public EnchantableRefIdAdapter<ESM::Clothing>\n    {\n            const RefIdColumn *mType;\n            const RefIdColumn *mPartRef;\n\n        public:\n\n            ClothingRefIdAdapter (const EnchantableColumns& columns,\n                    const RefIdColumn *type, const RefIdColumn *partRef);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    class ContainerRefIdAdapter : public NameRefIdAdapter<ESM::Container>\n    {\n            const RefIdColumn *mWeight;\n            const RefIdColumn *mOrganic;\n            const RefIdColumn *mRespawn;\n            const RefIdColumn *mContent;\n\n        public:\n\n            ContainerRefIdAdapter (const NameColumns& columns, const RefIdColumn *weight,\n                                   const RefIdColumn *organic, const RefIdColumn *respawn, const RefIdColumn *content);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                                  const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    struct CreatureColumns : public ActorColumns\n    {\n        std::map<const RefIdColumn *, unsigned int> mFlags;\n        const RefIdColumn *mType;\n        const RefIdColumn *mScale;\n        const RefIdColumn *mOriginal;\n        const RefIdColumn *mAttributes;\n        const RefIdColumn *mAttacks;\n        const RefIdColumn *mMisc;\n        const RefIdColumn *mBloodType;\n\n        CreatureColumns (const ActorColumns& actorColumns);\n    };\n\n    class CreatureRefIdAdapter : public ActorRefIdAdapter<ESM::Creature>\n    {\n            CreatureColumns mColumns;\n\n        public:\n\n            CreatureRefIdAdapter (const CreatureColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    class DoorRefIdAdapter : public NameRefIdAdapter<ESM::Door>\n    {\n            const RefIdColumn *mOpenSound;\n            const RefIdColumn *mCloseSound;\n\n        public:\n\n            DoorRefIdAdapter (const NameColumns& columns, const RefIdColumn *openSound,\n                const RefIdColumn *closeSound);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    struct LightColumns : public InventoryColumns\n    {\n        const RefIdColumn *mTime;\n        const RefIdColumn *mRadius;\n        const RefIdColumn *mColor;\n        const RefIdColumn *mSound;\n        const RefIdColumn *mEmitterType;\n        std::map<const RefIdColumn *, unsigned int> mFlags;\n\n        LightColumns (const InventoryColumns& columns);\n    };\n\n    class LightRefIdAdapter : public InventoryRefIdAdapter<ESM::Light>\n    {\n            LightColumns mColumns;\n\n        public:\n\n            LightRefIdAdapter (const LightColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    class MiscRefIdAdapter : public InventoryRefIdAdapter<ESM::Miscellaneous>\n    {\n            const RefIdColumn *mKey;\n\n        public:\n\n            MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    struct NpcColumns : public ActorColumns\n    {\n        std::map<const RefIdColumn *, unsigned int> mFlags;\n        const RefIdColumn *mRace;\n        const RefIdColumn *mClass;\n        const RefIdColumn *mFaction;\n        const RefIdColumn *mHair;\n        const RefIdColumn *mHead;\n        const RefIdColumn *mAttributes; // depends on npc type\n        const RefIdColumn *mSkills;     // depends on npc type\n        const RefIdColumn *mMisc;       // may depend on npc type, e.g. FactionID\n        const RefIdColumn *mBloodType;\n        const RefIdColumn *mGender;\n\n        NpcColumns (const ActorColumns& actorColumns);\n    };\n\n    class NpcRefIdAdapter : public ActorRefIdAdapter<ESM::NPC>\n    {\n            NpcColumns mColumns;\n\n        public:\n\n            NpcRefIdAdapter (const NpcColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    struct WeaponColumns : public EnchantableColumns\n    {\n        const RefIdColumn *mType;\n        const RefIdColumn *mHealth;\n        const RefIdColumn *mSpeed;\n        const RefIdColumn *mReach;\n        const RefIdColumn *mChop[2];\n        const RefIdColumn *mSlash[2];\n        const RefIdColumn *mThrust[2];\n        std::map<const RefIdColumn *, unsigned int> mFlags;\n\n        WeaponColumns (const EnchantableColumns& columns);\n    };\n\n    class WeaponRefIdAdapter : public EnchantableRefIdAdapter<ESM::Weapon>\n    {\n            WeaponColumns mColumns;\n\n        public:\n\n            WeaponRefIdAdapter (const WeaponColumns& columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n\n    class NestedRefIdAdapterBase;\n\n    class NpcAttributesRefIdAdapter : public NestedRefIdAdapterBase\n    {\n    public:\n\n        NpcAttributesRefIdAdapter ();\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override;\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override;\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override;\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override;\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override;\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override;\n    };\n\n    class NpcSkillsRefIdAdapter : public NestedRefIdAdapterBase\n    {\n    public:\n\n        NpcSkillsRefIdAdapter ();\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override;\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override;\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override;\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override;\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override;\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override;\n    };\n\n    class NpcMiscRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        NpcMiscRefIdAdapter (const NpcMiscRefIdAdapter&);\n        NpcMiscRefIdAdapter& operator= (const NpcMiscRefIdAdapter&);\n\n    public:\n\n        NpcMiscRefIdAdapter ();\n        virtual ~NpcMiscRefIdAdapter();\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override;\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override;\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override;\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override;\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override;\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override;\n    };\n\n    class CreatureAttributesRefIdAdapter : public NestedRefIdAdapterBase\n    {\n    public:\n\n        CreatureAttributesRefIdAdapter ();\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override;\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override;\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override;\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override;\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override;\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override;\n    };\n\n    class CreatureAttackRefIdAdapter : public NestedRefIdAdapterBase\n    {\n    public:\n\n        CreatureAttackRefIdAdapter ();\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override;\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override;\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override;\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override;\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override;\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override;\n    };\n\n    class CreatureMiscRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        CreatureMiscRefIdAdapter (const CreatureMiscRefIdAdapter&);\n        CreatureMiscRefIdAdapter& operator= (const CreatureMiscRefIdAdapter&);\n\n    public:\n\n        CreatureMiscRefIdAdapter ();\n        virtual ~CreatureMiscRefIdAdapter();\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override;\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override;\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override;\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override;\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override;\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override;\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override;\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override;\n    };\n\n    template<typename ESXRecordT>\n    class EffectsListAdapter;\n\n    template<typename ESXRecordT>\n    class EffectsRefIdAdapter : public EffectsListAdapter<ESXRecordT>, public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        EffectsRefIdAdapter (const EffectsRefIdAdapter&);\n        EffectsRefIdAdapter& operator= (const EffectsRefIdAdapter&);\n\n    public:\n\n        EffectsRefIdAdapter(UniversalId::Type type) :mType(type) {}\n\n        virtual ~EffectsRefIdAdapter() {}\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            EffectsListAdapter<ESXRecordT>::addRow(record, position);\n        }\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            EffectsListAdapter<ESXRecordT>::removeRow(record, rowToRemove);\n        }\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            EffectsListAdapter<ESXRecordT>::setTable(record, nestedTable);\n        }\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            return EffectsListAdapter<ESXRecordT>::table(record);\n        }\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            return EffectsListAdapter<ESXRecordT>::getData(record, subRowIndex, subColIndex);\n        }\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n            EffectsListAdapter<ESXRecordT>::setData(record, value, subRowIndex, subColIndex);\n        }\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override\n        {\n            const Record<ESXRecordT> record; // not used, just a dummy\n            return EffectsListAdapter<ESXRecordT>::getColumnsCount(record);\n        }\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            return EffectsListAdapter<ESXRecordT>::getRowsCount(record);\n        }\n    };\n\n    template <typename ESXRecordT>\n    class NestedInventoryRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        NestedInventoryRefIdAdapter (const NestedInventoryRefIdAdapter&);\n        NestedInventoryRefIdAdapter& operator= (const NestedInventoryRefIdAdapter&);\n\n    public:\n\n        NestedInventoryRefIdAdapter(UniversalId::Type type) :mType(type) {}\n\n        virtual ~NestedInventoryRefIdAdapter() {}\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT container = record.get();\n\n            std::vector<ESM::ContItem>& list = container.mInventory.mList;\n\n            ESM::ContItem newRow = ESM::ContItem();\n\n            if (position >= (int)list.size())\n                list.push_back(newRow);\n            else\n                list.insert(list.begin()+position, newRow);\n\n            record.setModified (container);\n        }\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT container = record.get();\n\n            std::vector<ESM::ContItem>& list = container.mInventory.mList;\n\n            if (rowToRemove < 0 || rowToRemove >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            list.erase (list.begin () + rowToRemove);\n\n            record.setModified (container);\n        }\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT container = record.get();\n\n            container.mInventory.mList =\n                static_cast<const NestedTableWrapper<std::vector<typename ESM::ContItem> >&>(nestedTable).mNestedTable;\n\n            record.setModified (container);\n        }\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            // deleted by dtor of NestedTableStoring\n            return new NestedTableWrapper<std::vector<typename ESM::ContItem> >(record.get().mInventory.mList);\n        }\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            const std::vector<ESM::ContItem>& list = record.get().mInventory.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            const ESM::ContItem& content = list.at(subRowIndex);\n\n            switch (subColIndex)\n            {\n                case 0: return QString::fromUtf8(content.mItem.c_str());\n                case 1: return content.mCount;\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n        }\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n            ESXRecordT container = record.get();\n            std::vector<ESM::ContItem>& list = container.mInventory.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            switch(subColIndex)\n            {\n                case 0:\n                    list.at(subRowIndex).mItem.assign(std::string(value.toString().toUtf8().constData()));\n                    break;\n\n                case 1:\n                    list.at(subRowIndex).mCount = value.toInt();\n                    break;\n\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n\n            record.setModified (container);\n        }\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override\n        {\n            return 2;\n        }\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            return static_cast<int>(record.get().mInventory.mList.size());\n        }\n    };\n\n    template <typename ESXRecordT>\n    class NestedSpellRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        NestedSpellRefIdAdapter (const NestedSpellRefIdAdapter&);\n        NestedSpellRefIdAdapter& operator= (const NestedSpellRefIdAdapter&);\n\n    public:\n\n        NestedSpellRefIdAdapter(UniversalId::Type type) :mType(type) {}\n\n        virtual ~NestedSpellRefIdAdapter() {}\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT caster = record.get();\n\n            std::vector<std::string>& list = caster.mSpells.mList;\n\n            std::string newString;\n\n            if (position >= (int)list.size())\n                list.push_back(newString);\n            else\n                list.insert(list.begin()+position, newString);\n\n            record.setModified (caster);\n        }\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT caster = record.get();\n\n            std::vector<std::string>& list = caster.mSpells.mList;\n\n            if (rowToRemove < 0 || rowToRemove >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            list.erase (list.begin () + rowToRemove);\n\n            record.setModified (caster);\n        }\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT caster = record.get();\n\n            caster.mSpells.mList =\n                static_cast<const NestedTableWrapper<std::vector<typename std::string> >&>(nestedTable).mNestedTable;\n\n            record.setModified (caster);\n        }\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            // deleted by dtor of NestedTableStoring\n            return new NestedTableWrapper<std::vector<typename std::string> >(record.get().mSpells.mList);\n        }\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            const std::vector<std::string>& list = record.get().mSpells.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            const std::string& content = list.at(subRowIndex);\n\n            if (subColIndex == 0)\n                return QString::fromUtf8(content.c_str());\n            else\n                throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n        }\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n            ESXRecordT caster = record.get();\n            std::vector<std::string>& list = caster.mSpells.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            if (subColIndex == 0)\n                list.at(subRowIndex) = std::string(value.toString().toUtf8());\n            else\n                throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n\n            record.setModified (caster);\n        }\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override\n        {\n            return 1;\n        }\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            return static_cast<int>(record.get().mSpells.mList.size());\n        }\n    };\n\n    template <typename ESXRecordT>\n    class NestedTravelRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        NestedTravelRefIdAdapter (const NestedTravelRefIdAdapter&);\n        NestedTravelRefIdAdapter& operator= (const NestedTravelRefIdAdapter&);\n\n    public:\n\n        NestedTravelRefIdAdapter(UniversalId::Type type) :mType(type) {}\n\n        virtual ~NestedTravelRefIdAdapter() {}\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT traveller = record.get();\n\n            std::vector<ESM::Transport::Dest>& list = traveller.mTransport.mList;\n\n            ESM::Position newPos;\n            for (unsigned i = 0; i < 3; ++i)\n            {\n                newPos.pos[i] = 0;\n                newPos.rot[i] = 0;\n            }\n\n            ESM::Transport::Dest newRow;\n            newRow.mPos = newPos;\n            newRow.mCellName = \"\";\n\n            if (position >= (int)list.size())\n                list.push_back(newRow);\n            else\n                list.insert(list.begin()+position, newRow);\n\n            record.setModified (traveller);\n        }\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT traveller = record.get();\n\n            std::vector<ESM::Transport::Dest>& list = traveller.mTransport.mList;\n\n            if (rowToRemove < 0 || rowToRemove >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            list.erase (list.begin () + rowToRemove);\n\n            record.setModified (traveller);\n        }\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT traveller = record.get();\n\n            traveller.mTransport.mList =\n                static_cast<const NestedTableWrapper<std::vector<typename ESM::Transport::Dest> >&>(nestedTable).mNestedTable;\n\n            record.setModified (traveller);\n        }\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            // deleted by dtor of NestedTableStoring\n            return new NestedTableWrapper<std::vector<typename ESM::Transport::Dest> >(record.get().mTransport.mList);\n        }\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            const std::vector<ESM::Transport::Dest>& list = record.get().mTransport.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            const ESM::Transport::Dest& content = list.at(subRowIndex);\n\n            switch (subColIndex)\n            {\n                case 0: return QString::fromUtf8(content.mCellName.c_str());\n                case 1: return content.mPos.pos[0];\n                case 2: return content.mPos.pos[1];\n                case 3: return content.mPos.pos[2];\n                case 4: return content.mPos.rot[0];\n                case 5: return content.mPos.rot[1];\n                case 6: return content.mPos.rot[2];\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n        }\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n            ESXRecordT traveller = record.get();\n            std::vector<ESM::Transport::Dest>& list = traveller.mTransport.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            switch(subColIndex)\n            {\n                case 0: list.at(subRowIndex).mCellName = std::string(value.toString().toUtf8().constData()); break;\n                case 1: list.at(subRowIndex).mPos.pos[0] = value.toFloat(); break;\n                case 2: list.at(subRowIndex).mPos.pos[1] = value.toFloat(); break;\n                case 3: list.at(subRowIndex).mPos.pos[2] = value.toFloat(); break;\n                case 4: list.at(subRowIndex).mPos.rot[0] = value.toFloat(); break;\n                case 5: list.at(subRowIndex).mPos.rot[1] = value.toFloat(); break;\n                case 6: list.at(subRowIndex).mPos.rot[2] = value.toFloat(); break;\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n\n            record.setModified (traveller);\n        }\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override\n        {\n            return 7;\n        }\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            return static_cast<int>(record.get().mTransport.mList.size());\n        }\n    };\n\n    template <typename ESXRecordT>\n    class ActorAiRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        ActorAiRefIdAdapter (const ActorAiRefIdAdapter&);\n        ActorAiRefIdAdapter& operator= (const ActorAiRefIdAdapter&);\n\n    public:\n\n        ActorAiRefIdAdapter(UniversalId::Type type) :mType(type) {}\n\n        virtual ~ActorAiRefIdAdapter() {}\n\n        // FIXME: should check if the AI package type is already in the list and use a default\n        //        that wasn't used already (in extreme case do not add anything at all?\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT actor = record.get();\n\n            std::vector<ESM::AIPackage>& list = actor.mAiPackage.mList;\n\n            ESM::AIPackage newRow;\n            newRow.mType = ESM::AI_Wander;\n            newRow.mWander.mDistance = 0;\n            newRow.mWander.mDuration = 0;\n            newRow.mWander.mTimeOfDay = 0;\n            for (int i = 0; i < 8; ++i)\n                newRow.mWander.mIdle[i] = 0;\n            newRow.mWander.mShouldRepeat = 0;\n            newRow.mCellName = \"\";\n\n            if (position >= (int)list.size())\n                list.push_back(newRow);\n            else\n                list.insert(list.begin()+position, newRow);\n\n            record.setModified (actor);\n        }\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT actor = record.get();\n\n            std::vector<ESM::AIPackage>& list = actor.mAiPackage.mList;\n\n            if (rowToRemove < 0 || rowToRemove >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            list.erase (list.begin () + rowToRemove);\n\n            record.setModified (actor);\n        }\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT actor = record.get();\n\n            actor.mAiPackage.mList =\n                static_cast<const NestedTableWrapper<std::vector<typename ESM::AIPackage> >&>(nestedTable).mNestedTable;\n\n            record.setModified (actor);\n        }\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            // deleted by dtor of NestedTableStoring\n            return new NestedTableWrapper<std::vector<typename ESM::AIPackage> >(record.get().mAiPackage.mList);\n        }\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            const std::vector<ESM::AIPackage>& list = record.get().mAiPackage.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            const ESM::AIPackage& content = list.at(subRowIndex);\n\n            switch (subColIndex)\n            {\n                case 0:\n                    // FIXME: should more than one AI package type be allowed?  Check vanilla\n                    switch (content.mType)\n                    {\n                        case ESM::AI_Wander: return 0;\n                        case ESM::AI_Travel: return 1;\n                        case ESM::AI_Follow: return 2;\n                        case ESM::AI_Escort: return 3;\n                        case ESM::AI_Activate: return 4;\n                        case ESM::AI_CNDT:\n                        default: return QVariant();\n                    }\n                case 1: // wander dist\n                    if (content.mType == ESM::AI_Wander)\n                        return content.mWander.mDistance;\n                    else\n                        return QVariant();\n                case 2: // wander dur\n                    if (content.mType == ESM::AI_Wander ||\n                            content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        return content.mWander.mDuration;\n                    else\n                        return QVariant();\n                case 3: // wander ToD\n                    if (content.mType == ESM::AI_Wander)\n                        return content.mWander.mTimeOfDay; // FIXME: not sure of the format\n                    else\n                        return QVariant();\n                case 4: // wander idle\n                case 5:\n                case 6:\n                case 7:\n                case 8:\n                case 9:\n                case 10:\n                case 11:\n                    if (content.mType == ESM::AI_Wander)\n                        return static_cast<int>(content.mWander.mIdle[subColIndex-4]);\n                    else\n                        return QVariant();\n                case 12: // wander repeat\n                    if (content.mType == ESM::AI_Wander)\n                        return content.mWander.mShouldRepeat != 0;\n                    else\n                        return QVariant();\n                case 13: // activate name\n                    if (content.mType == ESM::AI_Activate)\n                        return QString(content.mActivate.mName.toString().c_str());\n                    else\n                        return QVariant();\n                case 14: // target id\n                    if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        return QString(content.mTarget.mId.toString().c_str());\n                    else\n                        return QVariant();\n                case 15: // target cell\n                    if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        return QString::fromUtf8(content.mCellName.c_str());\n                    else\n                        return QVariant();\n                case 16:\n                    if (content.mType == ESM::AI_Travel)\n                        return content.mTravel.mX;\n                    else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        return content.mTarget.mX;\n                    else\n                        return QVariant();\n                case 17:\n                    if (content.mType == ESM::AI_Travel)\n                        return content.mTravel.mY;\n                    else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        return content.mTarget.mY;\n                    else\n                        return QVariant();\n                case 18:\n                    if (content.mType == ESM::AI_Travel)\n                        return content.mTravel.mZ;\n                    else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        return content.mTarget.mZ;\n                    else\n                        return QVariant();\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n        }\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n            ESXRecordT actor = record.get();\n            std::vector<ESM::AIPackage>& list = actor.mAiPackage.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            ESM::AIPackage& content = list.at(subRowIndex);\n\n            switch(subColIndex)\n            {\n                case 0: // ai package type\n                    switch (value.toInt())\n                    {\n                        case 0: content.mType = ESM::AI_Wander; break;\n                        case 1: content.mType = ESM::AI_Travel; break;\n                        case 2: content.mType = ESM::AI_Follow; break;\n                        case 3: content.mType = ESM::AI_Escort; break;\n                        case 4: content.mType = ESM::AI_Activate; break;\n                        default: return; // return without saving\n                    }\n                    break; // always save\n\n                case 1:\n                    if (content.mType == ESM::AI_Wander)\n                        content.mWander.mDistance = static_cast<short>(value.toInt());\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 2:\n                    if (content.mType == ESM::AI_Wander ||\n                            content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        content.mWander.mDuration = static_cast<short>(value.toInt());\n                    else\n                        return; // return without saving\n                case 3:\n                    if (content.mType == ESM::AI_Wander)\n                        content.mWander.mTimeOfDay = static_cast<unsigned char>(value.toInt());\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 4:\n                case 5:\n                case 6:\n                case 7:\n                case 8:\n                case 9:\n                case 10:\n                case 11:\n                    if (content.mType == ESM::AI_Wander)\n                        content.mWander.mIdle[subColIndex-4] = static_cast<unsigned char>(value.toInt());\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 12:\n                    if (content.mType == ESM::AI_Wander)\n                        content.mWander.mShouldRepeat = static_cast<unsigned char>(value.toInt());\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 13: // NAME32\n                    if (content.mType == ESM::AI_Activate)\n                        content.mActivate.mName.assign(value.toString().toUtf8().constData());\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 14: // NAME32\n                    if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        content.mTarget.mId.assign(value.toString().toUtf8().constData());\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 15:\n                    if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        content.mCellName = std::string(value.toString().toUtf8().constData());\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 16:\n                    if (content.mType == ESM::AI_Travel)\n                        content.mTravel.mX = value.toFloat();\n                    else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        content.mTarget.mX = value.toFloat();\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 17:\n                    if (content.mType == ESM::AI_Travel)\n                        content.mTravel.mY = value.toFloat();\n                    else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        content.mTarget.mY = value.toFloat();\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                case 18:\n                    if (content.mType == ESM::AI_Travel)\n                        content.mTravel.mZ = value.toFloat();\n                    else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)\n                        content.mTarget.mZ = value.toFloat();\n                    else\n                        return; // return without saving\n\n                    break; // always save\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n\n            record.setModified (actor);\n        }\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override\n        {\n            return 19;\n        }\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            return static_cast<int>(record.get().mAiPackage.mList.size());\n        }\n    };\n\n\n    template <typename ESXRecordT>\n    class BodyPartRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        BodyPartRefIdAdapter (const BodyPartRefIdAdapter&);\n        BodyPartRefIdAdapter& operator= (const BodyPartRefIdAdapter&);\n\n    public:\n\n        BodyPartRefIdAdapter(UniversalId::Type type) :mType(type) {}\n\n        virtual ~BodyPartRefIdAdapter() {}\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT apparel = record.get();\n\n            std::vector<ESM::PartReference>& list = apparel.mParts.mParts;\n\n            ESM::PartReference newPart;\n            newPart.mPart = 0; // 0 == head\n            newPart.mMale = \"\";\n            newPart.mFemale = \"\";\n\n            if (position >= (int)list.size())\n                list.push_back(newPart);\n            else\n                list.insert(list.begin()+position, newPart);\n\n            record.setModified (apparel);\n        }\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT apparel = record.get();\n\n            std::vector<ESM::PartReference>& list = apparel.mParts.mParts;\n\n            if (rowToRemove < 0 || rowToRemove >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            list.erase (list.begin () + rowToRemove);\n\n            record.setModified (apparel);\n        }\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT apparel = record.get();\n\n            apparel.mParts.mParts =\n                static_cast<const NestedTableWrapper<std::vector<typename ESM::PartReference> >&>(nestedTable).mNestedTable;\n\n            record.setModified (apparel);\n        }\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            // deleted by dtor of NestedTableStoring\n            return new NestedTableWrapper<std::vector<typename ESM::PartReference> >(record.get().mParts.mParts);\n        }\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            const std::vector<ESM::PartReference>& list = record.get().mParts.mParts;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            const ESM::PartReference& content = list.at(subRowIndex);\n\n            switch (subColIndex)\n            {\n                case 0:\n                {\n                    if (content.mPart < ESM::PRT_Count)\n                        return content.mPart;\n                    else\n                        throw std::runtime_error(\"Part Reference Type unexpected value\");\n                }\n                case 1: return QString(content.mMale.c_str());\n                case 2: return QString(content.mFemale.c_str());\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n        }\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n            ESXRecordT apparel = record.get();\n            std::vector<ESM::PartReference>& list = apparel.mParts.mParts;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            switch(subColIndex)\n            {\n                case 0: list.at(subRowIndex).mPart = static_cast<unsigned char>(value.toInt()); break;\n                case 1: list.at(subRowIndex).mMale = value.toString().toStdString(); break;\n                case 2: list.at(subRowIndex).mFemale = value.toString().toStdString(); break;\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n\n            record.setModified (apparel);\n        }\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override\n        {\n            return 3;\n        }\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            return static_cast<int>(record.get().mParts.mParts.size());\n        }\n    };\n\n\n    struct LevListColumns : public BaseColumns\n    {\n        const RefIdColumn *mLevList;\n        const RefIdColumn *mNestedListLevList;\n\n        LevListColumns (const BaseColumns& base)\n        : BaseColumns (base)\n        , mLevList(nullptr)\n        , mNestedListLevList(nullptr)\n        {}\n    };\n\n    template<typename RecordT>\n    class LevelledListRefIdAdapter : public BaseRefIdAdapter<RecordT>\n    {\n            LevListColumns mLevList;\n\n        public:\n\n            LevelledListRefIdAdapter (UniversalId::Type type, const LevListColumns &columns);\n\n            QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)\n                const override;\n\n            void setData (const RefIdColumn *column, RefIdData& data, int index,\n                const QVariant& value) const override;\n            ///< If the data type does not match an exception is thrown.\n    };\n\n    template<typename RecordT>\n    LevelledListRefIdAdapter<RecordT>::LevelledListRefIdAdapter (UniversalId::Type type,\n            const LevListColumns &columns)\n    : BaseRefIdAdapter<RecordT> (type, columns), mLevList (columns)\n    {}\n\n    template<typename RecordT>\n    QVariant LevelledListRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,\n        int index) const\n    {\n        if (column==mLevList.mLevList || column == mLevList.mNestedListLevList)\n            return QVariant::fromValue(ColumnBase::TableEdit_Full);\n\n        return BaseRefIdAdapter<RecordT>::getData (column, data, index);\n    }\n\n    template<typename RecordT>\n    void LevelledListRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,\n        const QVariant& value) const\n    {\n        BaseRefIdAdapter<RecordT>::setData (column, data, index, value);\n        return;\n    }\n\n\n    // for non-tables\n    template <typename ESXRecordT>\n    class NestedListLevListRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        NestedListLevListRefIdAdapter (const NestedListLevListRefIdAdapter&);\n        NestedListLevListRefIdAdapter& operator= (const NestedListLevListRefIdAdapter&);\n\n    public:\n\n        NestedListLevListRefIdAdapter(UniversalId::Type type)\n                :mType(type) {}\n\n        virtual ~NestedListLevListRefIdAdapter() {}\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override\n        {\n            throw std::logic_error (\"cannot add a row to a fixed table\");\n        }\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override\n        {\n            throw std::logic_error (\"cannot remove a row to a fixed table\");\n        }\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override\n        {\n            throw std::logic_error (\"table operation not supported\");\n        }\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override\n        {\n            throw std::logic_error (\"table operation not supported\");\n        }\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            if (mType == UniversalId::Type_CreatureLevelledList)\n            {\n                switch (subColIndex)\n                {\n                    case 0: return QVariant(); // disable the checkbox editor\n                    case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels;\n                    case 2: return static_cast<int> (record.get().mChanceNone);\n                    default:\n                        throw std::runtime_error(\"Trying to access non-existing column in levelled creatues!\");\n                }\n            }\n            else\n            {\n                switch (subColIndex)\n                {\n                    case 0: return record.get().mFlags & ESM::ItemLevList::Each;\n                    case 1: return record.get().mFlags & ESM::ItemLevList::AllLevels;\n                    case 2: return static_cast<int> (record.get().mChanceNone);\n                    default:\n                        throw std::runtime_error(\"Trying to access non-existing column in levelled items!\");\n                }\n            }\n        }\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n            ESXRecordT leveled = record.get();\n\n            if (mType == UniversalId::Type_CreatureLevelledList)\n            {\n                switch(subColIndex)\n                {\n                    case 0: return; // return without saving\n                    case 1:\n                    {\n                        if(value.toBool())\n                        {\n                            leveled.mFlags |= ESM::CreatureLevList::AllLevels;\n                            break;\n                        }\n                        else\n                        {\n                            leveled.mFlags &= ~ESM::CreatureLevList::AllLevels;\n                            break;\n                        }\n                    }\n                    case 2: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break;\n                    default:\n                        throw std::runtime_error(\"Trying to set non-existing column in levelled creatures!\");\n                }\n            }\n            else\n            {\n                switch(subColIndex)\n                {\n                    case 0:\n                    {\n                        if(value.toBool())\n                        {\n                            leveled.mFlags |= ESM::ItemLevList::Each;\n                            break;\n                        }\n                        else\n                        {\n                            leveled.mFlags &= ~ESM::ItemLevList::Each;\n                            break;\n                        }\n                    }\n                    case 1:\n                    {\n                        if(value.toBool())\n                        {\n                            leveled.mFlags |= ESM::ItemLevList::AllLevels;\n                            break;\n                        }\n                        else\n                        {\n                            leveled.mFlags &= ~ESM::ItemLevList::AllLevels;\n                            break;\n                        }\n                    }\n                    case 2: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break;\n                    default:\n                        throw std::runtime_error(\"Trying to set non-existing column in levelled items!\");\n                }\n            }\n\n            record.setModified (leveled);\n        }\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override\n        {\n            return 3;\n        }\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override\n        {\n            return 1; // fixed at size 1\n        }\n    };\n\n    // for tables\n    template <typename ESXRecordT>\n    class NestedLevListRefIdAdapter : public NestedRefIdAdapterBase\n    {\n        UniversalId::Type mType;\n\n        // not implemented\n        NestedLevListRefIdAdapter (const NestedLevListRefIdAdapter&);\n        NestedLevListRefIdAdapter& operator= (const NestedLevListRefIdAdapter&);\n\n    public:\n\n        NestedLevListRefIdAdapter(UniversalId::Type type) :mType(type) {}\n\n        virtual ~NestedLevListRefIdAdapter() {}\n\n        void addNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int position) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT leveled = record.get();\n\n            std::vector<ESM::LevelledListBase::LevelItem>& list = leveled.mList;\n\n            ESM::LevelledListBase::LevelItem newItem;\n            newItem.mId = \"\";\n            newItem.mLevel = 0;\n\n            if (position >= (int)list.size())\n                list.push_back(newItem);\n            else\n                list.insert(list.begin()+position, newItem);\n\n            record.setModified (leveled);\n        }\n\n        void removeNestedRow (const RefIdColumn *column,\n                RefIdData& data, int index, int rowToRemove) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT leveled = record.get();\n\n            std::vector<ESM::LevelledListBase::LevelItem>& list = leveled.mList;\n\n            if (rowToRemove < 0 || rowToRemove >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            list.erase (list.begin () + rowToRemove);\n\n            record.setModified (leveled);\n        }\n\n        void setNestedTable (const RefIdColumn* column,\n                RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n            ESXRecordT leveled = record.get();\n\n            leveled.mList =\n                static_cast<const NestedTableWrapper<std::vector<typename ESM::LevelledListBase::LevelItem> >&>(nestedTable).mNestedTable;\n\n            record.setModified (leveled);\n        }\n\n        NestedTableWrapperBase* nestedTable (const RefIdColumn* column,\n                const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            // deleted by dtor of NestedTableStoring\n            return new NestedTableWrapper<std::vector<typename ESM::LevelledListBase::LevelItem> >(record.get().mList);\n        }\n\n        QVariant getNestedData (const RefIdColumn *column,\n                const RefIdData& data, int index, int subRowIndex, int subColIndex) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            const std::vector<ESM::LevelledListBase::LevelItem>& list = record.get().mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            const ESM::LevelledListBase::LevelItem& content = list.at(subRowIndex);\n\n            switch (subColIndex)\n            {\n                case 0: return QString(content.mId.c_str());\n                case 1: return content.mLevel;\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n        }\n\n        void setNestedData (const RefIdColumn *column,\n                RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override\n        {\n            Record<ESXRecordT>& record =\n                static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));\n            ESXRecordT leveled = record.get();\n            std::vector<ESM::LevelledListBase::LevelItem>& list = leveled.mList;\n\n            if (subRowIndex < 0 || subRowIndex >= static_cast<int> (list.size()))\n                throw std::runtime_error (\"index out of range\");\n\n            switch(subColIndex)\n            {\n                case 0: list.at(subRowIndex).mId = value.toString().toStdString(); break;\n                case 1: list.at(subRowIndex).mLevel = static_cast<short>(value.toInt()); break;\n                default:\n                    throw std::runtime_error(\"Trying to access non-existing column in the nested table!\");\n            }\n\n            record.setModified (leveled);\n        }\n\n        int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const override\n        {\n            return 2;\n        }\n\n        int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const override\n        {\n            const Record<ESXRecordT>& record =\n                static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));\n\n            return static_cast<int>(record.get().mList.size());\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/refidcollection.cpp",
    "content": "#include \"refidcollection.hpp\"\n\n#include <stdexcept>\n#include <memory>\n\n#include <components/esm/esmreader.hpp>\n\n#include \"refidadapter.hpp\"\n#include \"refidadapterimp.hpp\"\n#include \"columns.hpp\"\n#include \"nestedtablewrapper.hpp\"\n#include \"nestedcoladapterimp.hpp\"\n\nCSMWorld::RefIdColumn::RefIdColumn (int columnId, Display displayType, int flag,\n    bool editable, bool userEditable)\n    : NestableColumn (columnId, displayType, flag), mEditable (editable), mUserEditable (userEditable)\n{}\n\nbool CSMWorld::RefIdColumn::isEditable() const\n{\n    return mEditable;\n}\n\nbool CSMWorld::RefIdColumn::isUserEditable() const\n{\n    return mUserEditable;\n}\n\nconst CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdapter (UniversalId::Type type) const\n{\n    std::map<UniversalId::Type, RefIdAdapter *>::const_iterator iter = mAdapters.find (type);\n\n    if (iter==mAdapters.end())\n        throw std::logic_error (\"unsupported type in RefIdCollection\");\n\n    return *iter->second;\n}\n\nCSMWorld::RefIdCollection::RefIdCollection()\n{\n    BaseColumns baseColumns;\n\n    mColumns.emplace_back(Columns::ColumnId_Id, ColumnBase::Display_Id,\n        ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false);\n    baseColumns.mId = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_Modification, ColumnBase::Display_RecordState,\n        ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true, false);\n    baseColumns.mModified = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType,\n        ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false);\n    baseColumns.mType = &mColumns.back();\n\n    ModelColumns modelColumns (baseColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_Model, ColumnBase::Display_Mesh);\n    modelColumns.mModel = &mColumns.back();\n\n    NameColumns nameColumns (modelColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_Name, ColumnBase::Display_String);\n    nameColumns.mName = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_Script, ColumnBase::Display_Script);\n    nameColumns.mScript = &mColumns.back();\n\n    InventoryColumns inventoryColumns (nameColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_Icon, ColumnBase::Display_Icon);\n    inventoryColumns.mIcon = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_Weight, ColumnBase::Display_Float);\n    inventoryColumns.mWeight = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_CoinValue, ColumnBase::Display_Integer);\n    inventoryColumns.mValue = &mColumns.back();\n\n    IngredientColumns ingredientColumns (inventoryColumns);\n    mColumns.emplace_back(Columns::ColumnId_EffectList,\n        ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    ingredientColumns.mEffects = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> ingredientEffectsMap;\n    ingredientEffectsMap.insert(std::make_pair(UniversalId::Type_Ingredient,\n        new IngredEffectRefIdAdapter ()));\n    mNestedAdapters.emplace_back(&mColumns.back(), ingredientEffectsMap);\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_IngredEffectId));\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill));\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute));\n\n    // nested table\n    PotionColumns potionColumns (inventoryColumns);\n    mColumns.emplace_back(Columns::ColumnId_EffectList,\n        ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    potionColumns.mEffects = &mColumns.back(); // see refidadapterimp.hpp\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> effectsMap;\n    effectsMap.insert(std::make_pair(UniversalId::Type_Potion,\n        new EffectsRefIdAdapter<ESM::Potion> (UniversalId::Type_Potion)));\n    mNestedAdapters.emplace_back(&mColumns.back(), effectsMap);\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill));\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute));\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange));\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String));\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_MinMagnitude, ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n        new NestedChildColumn (Columns::ColumnId_MaxMagnitude, ColumnBase::Display_Integer));\n\n    EnchantableColumns enchantableColumns (inventoryColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_Enchantment, ColumnBase::Display_Enchantment);\n    enchantableColumns.mEnchantment = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_EnchantmentPoints, ColumnBase::Display_Integer);\n    enchantableColumns.mEnchantmentPoints = &mColumns.back();\n\n    ToolColumns toolsColumns (inventoryColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_Quality, ColumnBase::Display_Float);\n    toolsColumns.mQuality = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_Charges, ColumnBase::Display_Integer);\n    toolsColumns.mUses = &mColumns.back();\n\n    ActorColumns actorsColumns (nameColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_AiHello, ColumnBase::Display_UnsignedInteger16);\n    actorsColumns.mHello = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_AiFlee, ColumnBase::Display_UnsignedInteger8);\n    actorsColumns.mFlee = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_AiFight, ColumnBase::Display_UnsignedInteger8);\n    actorsColumns.mFight = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_AiAlarm, ColumnBase::Display_UnsignedInteger8);\n    actorsColumns.mAlarm = &mColumns.back();\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_ActorInventory,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    actorsColumns.mInventory = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> inventoryMap;\n    inventoryMap.insert(std::make_pair(UniversalId::Type_Npc,\n            new NestedInventoryRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc)));\n    inventoryMap.insert(std::make_pair(UniversalId::Type_Creature,\n            new NestedInventoryRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature)));\n    mNestedAdapters.emplace_back(&mColumns.back(), inventoryMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_Referenceable));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer));\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_SpellList,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    actorsColumns.mSpells = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> spellsMap;\n    spellsMap.insert(std::make_pair(UniversalId::Type_Npc,\n            new NestedSpellRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc)));\n    spellsMap.insert(std::make_pair(UniversalId::Type_Creature,\n            new NestedSpellRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature)));\n    mNestedAdapters.emplace_back(&mColumns.back(), spellsMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_SpellId, CSMWorld::ColumnBase::Display_Spell));\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_NpcDestinations,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    actorsColumns.mDestinations = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> destMap;\n    destMap.insert(std::make_pair(UniversalId::Type_Npc,\n            new NestedTravelRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc)));\n    destMap.insert(std::make_pair(UniversalId::Type_Creature,\n            new NestedTravelRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature)));\n    mNestedAdapters.emplace_back(&mColumns.back(), destMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_DestinationCell, CSMWorld::ColumnBase::Display_Cell));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_PosX, CSMWorld::ColumnBase::Display_Float));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_PosY, CSMWorld::ColumnBase::Display_Float));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_RotX, CSMWorld::ColumnBase::Display_Double));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_RotY, CSMWorld::ColumnBase::Display_Double));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Double));\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_AiPackageList,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    actorsColumns.mAiPackages = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> aiMap;\n    aiMap.insert(std::make_pair(UniversalId::Type_Npc,\n            new ActorAiRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc)));\n    aiMap.insert(std::make_pair(UniversalId::Type_Creature,\n            new ActorAiRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature)));\n    mNestedAdapters.emplace_back(&mColumns.back(), aiMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AiPackageType, CSMWorld::ColumnBase::Display_AiPackageType));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AiWanderDist, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AiDuration, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AiWanderToD, CSMWorld::ColumnBase::Display_Integer));\n\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Idle1, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Idle2, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Idle3, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Idle4, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Idle5, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Idle6, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Idle7, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Idle8, CSMWorld::ColumnBase::Display_Integer));\n\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_Boolean));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AiActivateName, CSMWorld::ColumnBase::Display_String));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AiTargetId, CSMWorld::ColumnBase::Display_String));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AiTargetCell, CSMWorld::ColumnBase::Display_String));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_PosX, CSMWorld::ColumnBase::Display_Float));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_PosY, CSMWorld::ColumnBase::Display_Float));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float));\n\n    static const struct\n    {\n        int mName;\n        unsigned int mFlag;\n    } sServiceTable[] =\n    {\n        { Columns::ColumnId_BuysWeapons, ESM::NPC::Weapon},\n        { Columns::ColumnId_BuysArmor, ESM::NPC::Armor},\n        { Columns::ColumnId_BuysClothing, ESM::NPC::Clothing},\n        { Columns::ColumnId_BuysBooks, ESM::NPC::Books},\n        { Columns::ColumnId_BuysIngredients, ESM::NPC::Ingredients},\n        { Columns::ColumnId_BuysLockpicks, ESM::NPC::Picks},\n        { Columns::ColumnId_BuysProbes, ESM::NPC::Probes},\n        { Columns::ColumnId_BuysLights, ESM::NPC::Lights},\n        { Columns::ColumnId_BuysApparati, ESM::NPC::Apparatus},\n        { Columns::ColumnId_BuysRepairItems, ESM::NPC::RepairItem},\n        { Columns::ColumnId_BuysMiscItems, ESM::NPC::Misc},\n        { Columns::ColumnId_BuysPotions, ESM::NPC::Potions},\n        { Columns::ColumnId_BuysMagicItems, ESM::NPC::MagicItems},\n        { Columns::ColumnId_SellsSpells, ESM::NPC::Spells},\n        { Columns::ColumnId_Trainer, ESM::NPC::Training},\n        { Columns::ColumnId_Spellmaking, ESM::NPC::Spellmaking},\n        { Columns::ColumnId_EnchantingService, ESM::NPC::Enchanting},\n        { Columns::ColumnId_RepairService, ESM::NPC::Repair},\n        { -1, 0 }\n    };\n\n    for (int i=0; sServiceTable[i].mName!=-1; ++i)\n    {\n        mColumns.emplace_back(sServiceTable[i].mName, ColumnBase::Display_Boolean);\n        actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag));\n    }\n\n    mColumns.emplace_back(Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean,\n            ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh);\n    const RefIdColumn *autoCalc = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_ApparatusType,\n        ColumnBase::Display_ApparatusType);\n    const RefIdColumn *apparatusType = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_ArmorType, ColumnBase::Display_ArmorType);\n    const RefIdColumn *armorType = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Health, ColumnBase::Display_Integer);\n    const RefIdColumn *health = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_ArmorValue, ColumnBase::Display_Integer);\n    const RefIdColumn *armor = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_BookType, ColumnBase::Display_BookType);\n    const RefIdColumn *bookType = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Skill, ColumnBase::Display_SkillId);\n    const RefIdColumn *skill = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Text, ColumnBase::Display_LongString);\n    const RefIdColumn *text = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_ClothingType, ColumnBase::Display_ClothingType);\n    const RefIdColumn *clothingType = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_WeightCapacity, ColumnBase::Display_Float);\n    const RefIdColumn *weightCapacity = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_OrganicContainer, ColumnBase::Display_Boolean);\n    const RefIdColumn *organic = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Respawn, ColumnBase::Display_Boolean);\n    const RefIdColumn *respawn = &mColumns.back();\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_ContainerContent,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    const RefIdColumn *content = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> contMap;\n    contMap.insert(std::make_pair(UniversalId::Type_Container,\n            new NestedInventoryRefIdAdapter<ESM::Container> (UniversalId::Type_Container)));\n    mNestedAdapters.emplace_back(&mColumns.back(), contMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_Referenceable));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer));\n\n    CreatureColumns creatureColumns (actorsColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_CreatureType, ColumnBase::Display_CreatureType);\n    creatureColumns.mType = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_Scale, ColumnBase::Display_Float);\n    creatureColumns.mScale = &mColumns.back();\n    mColumns.emplace_back(Columns::ColumnId_ParentCreature, ColumnBase::Display_Creature);\n    creatureColumns.mOriginal = &mColumns.back();\n\n    static const struct\n    {\n        int mName;\n        unsigned int mFlag;\n    } sCreatureFlagTable[] =\n    {\n        { Columns::ColumnId_Biped, ESM::Creature::Bipedal },\n        { Columns::ColumnId_HasWeapon, ESM::Creature::Weapon },\n        { Columns::ColumnId_Swims, ESM::Creature::Swims },\n        { Columns::ColumnId_Flies, ESM::Creature::Flies },\n        { Columns::ColumnId_Walks, ESM::Creature::Walks },\n        { Columns::ColumnId_Essential, ESM::Creature::Essential },\n        { -1, 0 }\n    };\n\n    // for re-use in NPC records\n    const RefIdColumn *essential = nullptr;\n\n    for (int i=0; sCreatureFlagTable[i].mName!=-1; ++i)\n    {\n        mColumns.emplace_back(sCreatureFlagTable[i].mName, ColumnBase::Display_Boolean);\n        creatureColumns.mFlags.insert (std::make_pair (&mColumns.back(), sCreatureFlagTable[i].mFlag));\n\n        switch (sCreatureFlagTable[i].mFlag)\n        {\n            case ESM::Creature::Essential: essential = &mColumns.back(); break;\n        }\n    }\n\n    mColumns.emplace_back(Columns::ColumnId_BloodType, ColumnBase::Display_BloodType);\n    // For re-use in NPC records.\n    const RefIdColumn *bloodType = &mColumns.back();\n    creatureColumns.mBloodType = bloodType;\n\n    creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn));\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_CreatureAttributes,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    creatureColumns.mAttributes = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> creaAttrMap;\n    creaAttrMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureAttributesRefIdAdapter()));\n    mNestedAdapters.emplace_back(&mColumns.back(), creaAttrMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Attribute, CSMWorld::ColumnBase::Display_Attribute, false, false));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_AttributeValue, CSMWorld::ColumnBase::Display_Integer));\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_CreatureAttack,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    creatureColumns.mAttacks = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> attackMap;\n    attackMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureAttackRefIdAdapter()));\n    mNestedAdapters.emplace_back(&mColumns.back(), attackMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_CreatureAttack, CSMWorld::ColumnBase::Display_Integer, false, false));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_MinAttack, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_MaxAttack, CSMWorld::ColumnBase::Display_Integer));\n\n    // Nested list\n    mColumns.emplace_back(Columns::ColumnId_CreatureMisc,\n        ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List);\n    creatureColumns.mMisc = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> creaMiscMap;\n    creaMiscMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureMiscRefIdAdapter()));\n    mNestedAdapters.emplace_back(&mColumns.back(), creaMiscMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Level, CSMWorld::ColumnBase::Display_Integer,\n            ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Health, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Mana, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Fatigue, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_SoulPoints, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_CombatState, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_MagicState, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_StealthState, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Gold, CSMWorld::ColumnBase::Display_Integer));\n\n    mColumns.emplace_back(Columns::ColumnId_OpenSound, ColumnBase::Display_Sound);\n    const RefIdColumn *openSound = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_CloseSound, ColumnBase::Display_Sound);\n    const RefIdColumn *closeSound = &mColumns.back();\n\n    LightColumns lightColumns (inventoryColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_Duration, ColumnBase::Display_Integer);\n    lightColumns.mTime = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Radius, ColumnBase::Display_Integer);\n    lightColumns.mRadius = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Colour, ColumnBase::Display_Colour);\n    lightColumns.mColor = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Sound, ColumnBase::Display_Sound);\n    lightColumns.mSound = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_EmitterType, ColumnBase::Display_EmitterType);\n    lightColumns.mEmitterType = &mColumns.back();\n\n    static const struct\n    {\n        int mName;\n        unsigned int mFlag;\n    } sLightFlagTable[] =\n    {\n        { Columns::ColumnId_Dynamic, ESM::Light::Dynamic },\n        { Columns::ColumnId_Portable, ESM::Light::Carry },\n        { Columns::ColumnId_NegativeLight, ESM::Light::Negative },\n        { Columns::ColumnId_Fire, ESM::Light::Fire },\n        { Columns::ColumnId_OffByDefault, ESM::Light::OffDefault },\n        { -1, 0 }\n    };\n\n    for (int i=0; sLightFlagTable[i].mName!=-1; ++i)\n    {\n        mColumns.emplace_back(sLightFlagTable[i].mName, ColumnBase::Display_Boolean);\n        lightColumns.mFlags.insert (std::make_pair (&mColumns.back(), sLightFlagTable[i].mFlag));\n    }\n\n    mColumns.emplace_back(Columns::ColumnId_IsKey, ColumnBase::Display_Boolean);\n    const RefIdColumn *key = &mColumns.back();\n\n    NpcColumns npcColumns (actorsColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_Race, ColumnBase::Display_Race);\n    npcColumns.mRace = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Class, ColumnBase::Display_Class);\n    npcColumns.mClass = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Faction, ColumnBase::Display_Faction);\n    npcColumns.mFaction = &mColumns.back();\n\n    mColumns.emplace_back(Columns::Columnid_Hair, ColumnBase::Display_BodyPart);\n    npcColumns.mHair = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Head, ColumnBase::Display_BodyPart);\n    npcColumns.mHead = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_Gender, ColumnBase::Display_GenderNpc);\n    npcColumns.mGender = &mColumns.back();\n\n    npcColumns.mFlags.insert (std::make_pair (essential, ESM::NPC::Essential));\n\n    npcColumns.mFlags.insert (std::make_pair (respawn, ESM::NPC::Respawn));\n\n    npcColumns.mFlags.insert (std::make_pair (autoCalc, ESM::NPC::Autocalc));\n\n    // Re-used from Creature records.\n    npcColumns.mBloodType = bloodType;\n\n    // Need a way to add a table of stats and values (rather than adding a long list of\n    // entries in the dialogue subview) E.g. attributes+stats(health, mana, fatigue), skills\n    // These needs to be driven from the autocalculated setting.\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_NpcAttributes,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    npcColumns.mAttributes = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> attrMap;\n    attrMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcAttributesRefIdAdapter()));\n    mNestedAdapters.emplace_back(&mColumns.back(), attrMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Attribute, CSMWorld::ColumnBase::Display_Attribute, false, false));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_UnsignedInteger8));\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_NpcSkills,\n            ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    npcColumns.mSkills = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> skillsMap;\n    skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter()));\n    mNestedAdapters.emplace_back(&mColumns.back(), skillsMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Skill, CSMWorld::ColumnBase::Display_SkillId, false, false));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_UnsignedInteger8));\n\n    // Nested list\n    mColumns.emplace_back(Columns::ColumnId_NpcMisc,\n        ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List);\n    npcColumns.mMisc = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> miscMap;\n    miscMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcMiscRefIdAdapter()));\n    mNestedAdapters.emplace_back(&mColumns.back(), miscMap);\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Level, CSMWorld::ColumnBase::Display_SignedInteger16));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Health, CSMWorld::ColumnBase::Display_UnsignedInteger16));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Mana, CSMWorld::ColumnBase::Display_UnsignedInteger16));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Fatigue, CSMWorld::ColumnBase::Display_UnsignedInteger16));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_NpcDisposition, CSMWorld::ColumnBase::Display_UnsignedInteger8));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_NpcReputation, CSMWorld::ColumnBase::Display_UnsignedInteger8));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_NpcRank, CSMWorld::ColumnBase::Display_UnsignedInteger8));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_Gold, CSMWorld::ColumnBase::Display_Integer));\n    mColumns.back().addColumn(\n            new RefIdColumn (Columns::ColumnId_NpcPersistence, CSMWorld::ColumnBase::Display_Boolean));\n\n    WeaponColumns weaponColumns (enchantableColumns);\n\n    mColumns.emplace_back(Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType);\n    weaponColumns.mType = &mColumns.back();\n\n    weaponColumns.mHealth = health;\n\n    mColumns.emplace_back(Columns::ColumnId_WeaponSpeed, ColumnBase::Display_Float);\n    weaponColumns.mSpeed = &mColumns.back();\n\n    mColumns.emplace_back(Columns::ColumnId_WeaponReach, ColumnBase::Display_Float);\n    weaponColumns.mReach = &mColumns.back();\n\n    for (int i=0; i<3; ++i)\n    {\n        const RefIdColumn **column =\n            i==0 ? weaponColumns.mChop : (i==1 ? weaponColumns.mSlash : weaponColumns.mThrust);\n\n        for (int j=0; j<2; ++j)\n        {\n            mColumns.emplace_back(Columns::ColumnId_MinChop+i*2+j, ColumnBase::Display_Integer);\n            column[j] = &mColumns.back();\n        }\n    }\n\n    static const struct\n    {\n        int mName;\n        unsigned int mFlag;\n    } sWeaponFlagTable[] =\n    {\n        { Columns::ColumnId_Magical, ESM::Weapon::Magical },\n        { Columns::ColumnId_Silver, ESM::Weapon::Silver },\n        { -1, 0 }\n    };\n\n    for (int i=0; sWeaponFlagTable[i].mName!=-1; ++i)\n    {\n        mColumns.emplace_back(sWeaponFlagTable[i].mName, ColumnBase::Display_Boolean);\n        weaponColumns.mFlags.insert (std::make_pair (&mColumns.back(), sWeaponFlagTable[i].mFlag));\n    }\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_PartRefList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    const RefIdColumn *partRef = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> partMap;\n    partMap.insert(std::make_pair(UniversalId::Type_Armor,\n        new BodyPartRefIdAdapter<ESM::Armor> (UniversalId::Type_Armor)));\n    partMap.insert(std::make_pair(UniversalId::Type_Clothing,\n        new BodyPartRefIdAdapter<ESM::Clothing> (UniversalId::Type_Clothing)));\n    mNestedAdapters.emplace_back(&mColumns.back(), partMap);\n    mColumns.back().addColumn(\n        new RefIdColumn (Columns::ColumnId_PartRefType, CSMWorld::ColumnBase::Display_PartRefType));\n    mColumns.back().addColumn(\n        new RefIdColumn (Columns::ColumnId_PartRefMale, CSMWorld::ColumnBase::Display_BodyPart));\n    mColumns.back().addColumn(\n        new RefIdColumn (Columns::ColumnId_PartRefFemale, CSMWorld::ColumnBase::Display_BodyPart));\n\n    LevListColumns levListColumns (baseColumns);\n\n    // Nested table\n    mColumns.emplace_back(Columns::ColumnId_LevelledList,\n        ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue);\n    levListColumns.mLevList = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> levListMap;\n    levListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList,\n        new NestedLevListRefIdAdapter<ESM::CreatureLevList> (UniversalId::Type_CreatureLevelledList)));\n    levListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList,\n        new NestedLevListRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList)));\n    mNestedAdapters.emplace_back(&mColumns.back(), levListMap);\n    mColumns.back().addColumn(\n        new RefIdColumn (Columns::ColumnId_LevelledItemId, CSMWorld::ColumnBase::Display_Referenceable));\n    mColumns.back().addColumn(\n        new RefIdColumn (Columns::ColumnId_LevelledItemLevel, CSMWorld::ColumnBase::Display_Integer));\n\n    // Nested list\n    mColumns.emplace_back(Columns::ColumnId_LevelledList,\n        ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List);\n    levListColumns.mNestedListLevList = &mColumns.back();\n    std::map<UniversalId::Type, NestedRefIdAdapterBase*> nestedListLevListMap;\n    nestedListLevListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList,\n        new NestedListLevListRefIdAdapter<ESM::CreatureLevList> (UniversalId::Type_CreatureLevelledList)));\n    nestedListLevListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList,\n        new NestedListLevListRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList)));\n    mNestedAdapters.emplace_back(&mColumns.back(), nestedListLevListMap);\n    mColumns.back().addColumn(\n        new RefIdColumn (Columns::ColumnId_LevelledItemTypeEach, CSMWorld::ColumnBase::Display_Boolean));\n    mColumns.back().addColumn(\n        new RefIdColumn (Columns::ColumnId_LevelledItemType, CSMWorld::ColumnBase::Display_Boolean));\n    mColumns.back().addColumn(\n        new RefIdColumn (Columns::ColumnId_LevelledItemChanceNone, CSMWorld::ColumnBase::Display_UnsignedInteger8));\n\n    mAdapters.insert (std::make_pair (UniversalId::Type_Activator,\n        new NameRefIdAdapter<ESM::Activator> (UniversalId::Type_Activator, nameColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Potion,\n        new PotionRefIdAdapter (potionColumns, autoCalc)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Apparatus,\n        new ApparatusRefIdAdapter (inventoryColumns, apparatusType, toolsColumns.mQuality)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Armor,\n        new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor, partRef)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Book,\n        new BookRefIdAdapter (enchantableColumns, bookType, skill, text)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Clothing,\n        new ClothingRefIdAdapter (enchantableColumns, clothingType, partRef)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Container,\n        new ContainerRefIdAdapter (nameColumns, weightCapacity, organic, respawn, content)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Creature,\n        new CreatureRefIdAdapter (creatureColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Door,\n        new DoorRefIdAdapter (nameColumns, openSound, closeSound)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient,\n        new IngredientRefIdAdapter (ingredientColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList,\n        new LevelledListRefIdAdapter<ESM::CreatureLevList> (\n        UniversalId::Type_CreatureLevelledList, levListColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_ItemLevelledList,\n        new LevelledListRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList, levListColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Light,\n        new LightRefIdAdapter (lightColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Lockpick,\n        new ToolRefIdAdapter<ESM::Lockpick> (UniversalId::Type_Lockpick, toolsColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Miscellaneous,\n        new MiscRefIdAdapter (inventoryColumns, key)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Npc,\n        new NpcRefIdAdapter (npcColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Probe,\n        new ToolRefIdAdapter<ESM::Probe> (UniversalId::Type_Probe, toolsColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Repair,\n        new ToolRefIdAdapter<ESM::Repair> (UniversalId::Type_Repair, toolsColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Static,\n        new ModelRefIdAdapter<ESM::Static> (UniversalId::Type_Static, modelColumns)));\n    mAdapters.insert (std::make_pair (UniversalId::Type_Weapon,\n        new WeaponRefIdAdapter (weaponColumns)));\n}\n\nCSMWorld::RefIdCollection::~RefIdCollection()\n{\n    for (std::map<UniversalId::Type, RefIdAdapter *>::iterator iter (mAdapters.begin());\n         iter!=mAdapters.end(); ++iter)\n         delete iter->second;\n\n    for (std::vector<std::pair<const ColumnBase*, std::map<UniversalId::Type, NestedRefIdAdapterBase*> > >::iterator iter (mNestedAdapters.begin());\n         iter!=mNestedAdapters.end(); ++iter)\n    {\n        for (std::map<UniversalId::Type, NestedRefIdAdapterBase *>::iterator it ((iter->second).begin());\n            it!=(iter->second).end(); ++it)\n            delete it->second;\n    }\n}\n\nint CSMWorld::RefIdCollection::getSize() const\n{\n    return mData.getSize();\n}\n\nstd::string CSMWorld::RefIdCollection::getId (int index) const\n{\n    return getData (index, 0).toString().toUtf8().constData();\n}\n\nint CSMWorld::RefIdCollection::getIndex (const std::string& id) const\n{\n    int index = searchId (id);\n\n    if (index==-1)\n        throw std::runtime_error (\"invalid ID: \" + id);\n\n    return index;\n}\n\nint CSMWorld::RefIdCollection::getColumns() const\n{\n    return mColumns.size();\n}\n\nconst CSMWorld::ColumnBase& CSMWorld::RefIdCollection::getColumn (int column) const\n{\n    return mColumns.at (column);\n}\n\nQVariant CSMWorld::RefIdCollection::getData (int index, int column) const\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);\n\n    const RefIdAdapter& adaptor = findAdapter (localIndex.second);\n\n    return adaptor.getData (&mColumns.at (column), mData, localIndex.first);\n}\n\nQVariant CSMWorld::RefIdCollection::getNestedData (int row, int column, int subRow, int subColumn) const\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex(row);\n    const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);\n\n    return nestedAdapter.getNestedData(&mColumns.at (column), mData, localIndex.first, subRow, subColumn);\n}\n\nvoid CSMWorld::RefIdCollection::setData (int index, int column, const QVariant& data)\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);\n\n    const RefIdAdapter& adaptor = findAdapter (localIndex.second);\n\n    adaptor.setData (&mColumns.at (column), mData, localIndex.first, data);\n}\n\nvoid CSMWorld::RefIdCollection::setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn)\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row);\n    const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);\n\n    nestedAdapter.setNestedData(&mColumns.at (column), mData, localIndex.first, data, subRow, subColumn);\n    return;\n}\n\nvoid CSMWorld::RefIdCollection::removeRows (int index, int count)\n{\n    mData.erase (index, count);\n}\n\nvoid CSMWorld::RefIdCollection::removeNestedRows(int row, int column, int subRow)\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row);\n    const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);\n\n    nestedAdapter.removeNestedRow(&mColumns.at (column), mData, localIndex.first, subRow);\n    return;\n}\n\nvoid CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)\n{\n    mData.appendRecord (type, id, false);\n}\n\nint CSMWorld::RefIdCollection::searchId (const std::string& id) const\n{\n    RefIdData::LocalIndex localIndex = mData.searchId (id);\n\n    if (localIndex.first==-1)\n        return -1;\n\n    return mData.localToGlobalIndex (localIndex);\n}\n\nvoid CSMWorld::RefIdCollection::replace (int index, const RecordBase& record)\n{\n    mData.getRecord (mData.globalToLocalIndex (index)).assign (record);\n}\n\nvoid CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,\n                                     const std::string& destination,\n                                     const CSMWorld::UniversalId::Type type)\n{\n        std::unique_ptr<RecordBase> newRecord(mData.getRecord(mData.searchId(origin)).modifiedCopy());\n        mAdapters.find(type)->second->setId(*newRecord, destination);\n        mData.insertRecord(*newRecord, type, destination);\n}\n\nbool CSMWorld::RefIdCollection::touchRecord(const std::string& id)\n{\n    throw std::runtime_error(\"RefIdCollection::touchRecord is unimplemented\");\n    return false;\n}\n\nvoid CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,\n    UniversalId::Type type)\n{\n    std::string id = findAdapter (type).getId (record);\n\n    int index = mData.getAppendIndex (type);\n\n    mData.appendRecord (type, id, false);\n\n    mData.getRecord (mData.globalToLocalIndex (index)).assign (record);\n}\n\nconst CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const\n{\n    return mData.getRecord (mData.searchId (id));\n}\n\nconst CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (int index) const\n{\n    return mData.getRecord (mData.globalToLocalIndex (index));\n}\n\nvoid CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type)\n{\n    mData.load(reader, base, type);\n}\n\nint CSMWorld::RefIdCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const\n{\n    return mData.getAppendIndex (type);\n}\n\nstd::vector<std::string> CSMWorld::RefIdCollection::getIds (bool listDeleted) const\n{\n    return mData.getIds (listDeleted);\n}\n\nbool CSMWorld::RefIdCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)\n{\n    return false;\n}\n\nvoid CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const\n{\n    mData.save (index, writer);\n}\n\nconst CSMWorld::RefIdData& CSMWorld::RefIdCollection::getDataSet() const\n{\n    return mData;\n}\n\nint CSMWorld::RefIdCollection::getNestedRowsCount(int row, int column) const\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row);\n    const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);\n\n    return nestedAdapter.getNestedRowsCount(&mColumns.at(column), mData, localIndex.first);\n}\n\nint CSMWorld::RefIdCollection::getNestedColumnsCount(int row, int column) const\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row);\n    const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);\n\n    return nestedAdapter.getNestedColumnsCount(&mColumns.at(column), mData);\n}\n\nCSMWorld::NestableColumn *CSMWorld::RefIdCollection::getNestableColumn(int column)\n{\n    return &mColumns.at(column);\n}\n\nvoid CSMWorld::RefIdCollection::addNestedRow(int row, int col, int position)\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row);\n    const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(col), localIndex.second);\n\n    nestedAdapter.addNestedRow(&mColumns.at(col), mData, localIndex.first, position);\n    return;\n}\n\nvoid CSMWorld::RefIdCollection::setNestedTable(int row, int column, const CSMWorld::NestedTableWrapperBase& nestedTable)\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row);\n    const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);\n\n    nestedAdapter.setNestedTable(&mColumns.at(column), mData, localIndex.first, nestedTable);\n    return;\n}\n\nCSMWorld::NestedTableWrapperBase* CSMWorld::RefIdCollection::nestedTable(int row, int column) const\n{\n    RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row);\n    const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);\n\n    return nestedAdapter.nestedTable(&mColumns.at(column), mData, localIndex.first);\n}\n\nconst CSMWorld::NestedRefIdAdapterBase& CSMWorld::RefIdCollection::getNestedAdapter(const CSMWorld::ColumnBase &column, UniversalId::Type type) const\n{\n    for (std::vector<std::pair<const ColumnBase*, std::map<UniversalId::Type, NestedRefIdAdapterBase*> > >::const_iterator iter (mNestedAdapters.begin());\n         iter!=mNestedAdapters.end(); ++iter)\n    {\n        if ((iter->first) == &column)\n        {\n            std::map<UniversalId::Type, NestedRefIdAdapterBase*>::const_iterator it =\n                (iter->second).find(type);\n\n            if (it == (iter->second).end())\n                throw std::runtime_error(\"No such type in the nestedadapters\");\n\n            return *it->second;\n        }\n    }\n    throw std::runtime_error(\"No such column in the nestedadapters\");\n}\n\nvoid CSMWorld::RefIdCollection::copyTo (int index, RefIdCollection& target) const\n{\n    mData.copyTo (index, target.mData);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/refidcollection.hpp",
    "content": "#ifndef CSM_WOLRD_REFIDCOLLECTION_H\n#define CSM_WOLRD_REFIDCOLLECTION_H\n\n#include <vector>\n#include <map>\n#include <deque>\n\n#include \"columnbase.hpp\"\n#include \"collectionbase.hpp\"\n#include \"nestedcollection.hpp\"\n#include \"refiddata.hpp\"\n\nnamespace ESM\n{\n    class ESMWriter;\n}\n\nnamespace CSMWorld\n{\n    class RefIdAdapter;\n    struct NestedTableWrapperBase;\n    class NestedRefIdAdapterBase;\n\n    class RefIdColumn : public NestableColumn\n    {\n            bool mEditable;\n            bool mUserEditable;\n\n        public:\n\n            RefIdColumn (int columnId, Display displayType,\n                         int flag = Flag_Table | Flag_Dialogue, bool editable = true,\n                         bool userEditable = true);\n\n            bool isEditable() const override;\n\n            bool isUserEditable() const override;\n    };\n\n    class RefIdCollection : public CollectionBase, public NestedCollection\n    {\n        private:\n\n            RefIdData mData;\n            std::deque<RefIdColumn> mColumns;\n            std::map<UniversalId::Type, RefIdAdapter *> mAdapters;\n\n            std::vector<std::pair<const ColumnBase*, std::map<UniversalId::Type, NestedRefIdAdapterBase*> > > mNestedAdapters;\n\n        private:\n\n            const RefIdAdapter& findAdapter (UniversalId::Type) const;\n            ///< Throws an exception if no adaptor for \\a Type can be found.\n\n            const NestedRefIdAdapterBase& getNestedAdapter(const ColumnBase &column, UniversalId::Type type) const;\n\n        public:\n\n            RefIdCollection();\n\n            virtual ~RefIdCollection();\n\n            int getSize() const override;\n\n            std::string getId (int index) const override;\n\n            int getIndex (const std::string& id) const override;\n\n            int getColumns() const override;\n\n            const ColumnBase& getColumn (int column) const override;\n\n            QVariant getData (int index, int column) const override;\n\n            void setData (int index, int column, const QVariant& data) override;\n\n            void removeRows (int index, int count) override;\n\n            void cloneRecord(const std::string& origin,\n                                     const std::string& destination,\n                                     const UniversalId::Type type) override;\n\n            bool touchRecord(const std::string& id) override;\n\n            void appendBlankRecord (const std::string& id, UniversalId::Type type) override;\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            int searchId (const std::string& id) const override;\n            ////< Search record with \\a id.\n            /// \\return index of record (if found) or -1 (not found)\n\n            void replace (int index, const RecordBase& record) override;\n            ///< If the record type does not match, an exception is thrown.\n            ///\n            /// \\attention \\a record must not change the ID.\n\n            void appendRecord (const RecordBase& record, UniversalId::Type type) override;\n            ///< If the record type does not match, an exception is thrown.\n            ///\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            const RecordBase& getRecord (const std::string& id) const override;\n\n            const RecordBase& getRecord (int index) const override;\n\n            void load (ESM::ESMReader& reader, bool base, UniversalId::Type type);\n\n            int getAppendIndex (const std::string& id, UniversalId::Type type) const override;\n            ///< \\param type Will be ignored, unless the collection supports multiple record types\n\n            std::vector<std::string> getIds (bool listDeleted) const override;\n            ///< Return a sorted collection of all IDs\n            ///\n            /// \\param listDeleted include deleted record in the list\n\n            bool reorderRows (int baseIndex, const std::vector<int>& newOrder) override;\n            ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices\n            /// given in \\a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).\n            ///\n            /// \\return Success?\n\n            QVariant getNestedData(int row, int column, int subRow, int subColumn) const override;\n\n            NestedTableWrapperBase* nestedTable(int row, int column) const override;\n\n            void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable) override;\n\n            int getNestedRowsCount(int row, int column) const override;\n\n            int getNestedColumnsCount(int row, int column) const override;\n\n            NestableColumn *getNestableColumn(int column) override;\n\n            void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) override;\n\n            void removeNestedRows(int row, int column, int subRow) override;\n\n            void addNestedRow(int row, int col, int position) override;\n\n            void save (int index, ESM::ESMWriter& writer) const;\n\n            const RefIdData& getDataSet() const; //I can't figure out a better name for this one :(\n            void copyTo (int index, RefIdCollection& target) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/refiddata.cpp",
    "content": "#include \"refiddata.hpp\"\n\n#include <cassert>\n#include <memory>\n\nCSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {}\n\n\nstd::string CSMWorld::RefIdData::getRecordId(const CSMWorld::RefIdData::LocalIndex &index) const\n{\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator found =\n        mRecordContainers.find (index.second);\n\n    if (found == mRecordContainers.end())\n        throw std::logic_error (\"invalid local index type\");\n\n    return found->second->getId(index.first);\n}\n\nCSMWorld::RefIdData::RefIdData()\n{\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Potion, &mPotions));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Apparatus, &mApparati));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Armor, &mArmors));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Book, &mBooks));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Clothing, &mClothing));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Container, &mContainers));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Creature, &mCreatures));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Door, &mDoors));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Ingredient, &mIngredients));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_CreatureLevelledList,\n        &mCreatureLevelledLists));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_ItemLevelledList, &mItemLevelledLists));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Light, &mLights));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Lockpick, &mLockpicks));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Miscellaneous, &mMiscellaneous));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Npc, &mNpcs));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Probe, &mProbes));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Repair, &mRepairs));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Static, &mStatics));\n    mRecordContainers.insert (std::make_pair (UniversalId::Type_Weapon, &mWeapons));\n}\n\nCSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const\n{\n    for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (\n        mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter)\n    {\n        if (index<iter->second->getSize())\n            return LocalIndex (index, iter->first);\n\n        index -= iter->second->getSize();\n    }\n\n    throw std::runtime_error (\"RefIdData index out of range\");\n}\n\nint CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index)\n    const\n{\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator end =\n        mRecordContainers.find (index.second);\n\n    if (end==mRecordContainers.end())\n        throw std::logic_error (\"invalid local index type\");\n\n    int globalIndex = index.first;\n\n    for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (\n        mRecordContainers.begin()); iter!=end; ++iter)\n        globalIndex += iter->second->getSize();\n\n    return globalIndex;\n}\n\nCSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId (\n    const std::string& id) const\n{\n    std::string id2 = Misc::StringUtils::lowerCase (id);\n\n    std::map<std::string, std::pair<int, UniversalId::Type> >::const_iterator iter = mIndex.find (id2);\n\n    if (iter==mIndex.end())\n        return std::make_pair (-1, CSMWorld::UniversalId::Type_None);\n\n    return iter->second;\n}\n\nvoid CSMWorld::RefIdData::erase (int index, int count)\n{\n    LocalIndex localIndex = globalToLocalIndex (index);\n\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =\n        mRecordContainers.find (localIndex.second);\n\n    while (count>0 && iter!=mRecordContainers.end())\n    {\n        int size = iter->second->getSize();\n\n        if (localIndex.first+count>size)\n        {\n            erase (localIndex, size-localIndex.first);\n            count -= size-localIndex.first;\n\n            ++iter;\n\n            if (iter==mRecordContainers.end())\n                throw std::runtime_error (\"invalid count value for erase operation\");\n\n            localIndex.first = 0;\n            localIndex.second = iter->first;\n        }\n        else\n        {\n            erase (localIndex, count);\n            count = 0;\n        }\n    }\n}\n\nconst CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const\n{\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =\n        mRecordContainers.find (index.second);\n\n    if (iter==mRecordContainers.end())\n        throw std::logic_error (\"invalid local index type\");\n\n    return iter->second->getRecord (index.first);\n}\n\nCSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index)\n{\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =\n        mRecordContainers.find (index.second);\n\n    if (iter==mRecordContainers.end())\n        throw std::logic_error (\"invalid local index type\");\n\n    return iter->second->getRecord (index.first);\n}\n\nvoid CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id, bool base)\n{\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =\n        mRecordContainers.find (type);\n\n    if (iter==mRecordContainers.end())\n        throw std::logic_error (\"invalid local index type\");\n\n    iter->second->appendRecord (id, base);\n\n    mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),\n        LocalIndex (iter->second->getSize()-1, type)));\n}\n\nint CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const\n{\n    int index = 0;\n\n    for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (\n        mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter)\n    {\n        index += iter->second->getSize();\n\n        if (type==iter->first)\n            break;\n    }\n\n    return index;\n}\n\nvoid CSMWorld::RefIdData::load (ESM::ESMReader& reader, bool base, CSMWorld::UniversalId::Type type)\n{\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator found =\n        mRecordContainers.find (type);\n\n    if (found == mRecordContainers.end())\n        throw std::logic_error (\"Invalid Referenceable ID type\");\n\n    int index = found->second->load(reader, base);\n    if (index != -1)\n    {\n        LocalIndex localIndex = LocalIndex(index, type);\n        if (base && getRecord(localIndex).mState == RecordBase::State_Deleted)\n        {\n            erase(localIndex, 1);\n        }\n        else\n        {\n            mIndex[Misc::StringUtils::lowerCase(getRecordId(localIndex))] = localIndex;\n        }\n    }\n}\n\nvoid CSMWorld::RefIdData::erase (const LocalIndex& index, int count)\n{\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =\n        mRecordContainers.find (index.second);\n    if (iter==mRecordContainers.end())\n        throw std::logic_error (\"invalid local index type\");\n\n    for (int i=index.first; i<index.first+count; ++i)\n    {\n        std::map<std::string, LocalIndex>::iterator result =\n            mIndex.find (Misc::StringUtils::lowerCase (iter->second->getId (i)));\n\n        if (result!=mIndex.end())\n            mIndex.erase (result);\n    }\n\n    // Adjust the local indexes to avoid gaps between them after removal of records\n    int recordIndex = index.first + count;\n    int recordCount = iter->second->getSize();\n    while (recordIndex < recordCount)\n    {\n        std::map<std::string, LocalIndex>::iterator recordIndexFound =\n            mIndex.find(Misc::StringUtils::lowerCase(iter->second->getId(recordIndex)));\n        if (recordIndexFound != mIndex.end())\n        {\n            recordIndexFound->second.first -= count;\n        }\n        ++recordIndex;\n    }\n\n    iter->second->erase (index.first, count);\n}\n\nint CSMWorld::RefIdData::getSize() const\n{\n    return mIndex.size();\n}\n\nstd::vector<std::string> CSMWorld::RefIdData::getIds (bool listDeleted) const\n{\n    std::vector<std::string> ids;\n\n    for (std::map<std::string, LocalIndex>::const_iterator iter (mIndex.begin()); iter!=mIndex.end();\n         ++iter)\n    {\n        if (listDeleted || !getRecord (iter->second).isDeleted())\n        {\n            std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator container =\n                mRecordContainers.find (iter->second.second);\n\n            if (container==mRecordContainers.end())\n                throw std::logic_error (\"Invalid referenceable ID type\");\n\n            ids.push_back (container->second->getId (iter->second.first));\n        }\n    }\n\n    return ids;\n}\n\nvoid CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const\n{\n    LocalIndex localIndex = globalToLocalIndex (index);\n\n    std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =\n        mRecordContainers.find (localIndex.second);\n\n    if (iter==mRecordContainers.end())\n        throw std::logic_error (\"invalid local index type\");\n\n    iter->second->save (localIndex.first, writer);\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const\n{\n    return mBooks;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivators() const\n{\n    return mActivators;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Potion >& CSMWorld::RefIdData::getPotions() const\n{\n    return mPotions;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Apparatus >& CSMWorld::RefIdData::getApparati() const\n{\n    return mApparati;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Armor >& CSMWorld::RefIdData::getArmors() const\n{\n    return mArmors;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Clothing >& CSMWorld::RefIdData::getClothing() const\n{\n    return mClothing;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Container >& CSMWorld::RefIdData::getContainers() const\n{\n    return mContainers;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Creature >& CSMWorld::RefIdData::getCreatures() const\n{\n    return mCreatures;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Door >& CSMWorld::RefIdData::getDoors() const\n{\n    return mDoors;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Ingredient >& CSMWorld::RefIdData::getIngredients() const\n{\n    return mIngredients;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& CSMWorld::RefIdData::getCreatureLevelledLists() const\n{\n    return mCreatureLevelledLists;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::ItemLevList >& CSMWorld::RefIdData::getItemLevelledList() const\n{\n    return mItemLevelledLists;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Light >& CSMWorld::RefIdData::getLights() const\n{\n    return mLights;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Lockpick >& CSMWorld::RefIdData::getLocpicks() const\n{\n    return mLockpicks;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& CSMWorld::RefIdData::getMiscellaneous() const\n{\n    return mMiscellaneous;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::NPC >& CSMWorld::RefIdData::getNPCs() const\n{\n    return mNpcs;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Weapon >& CSMWorld::RefIdData::getWeapons() const\n{\n    return mWeapons;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Probe >& CSMWorld::RefIdData::getProbes() const\n{\n    return mProbes;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Repair >& CSMWorld::RefIdData::getRepairs() const\n{\n    return mRepairs;\n}\n\nconst CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStatics() const\n{\n    return mStatics;\n}\n\nvoid CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id)\n{\n  std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =\n        mRecordContainers.find (type);\n\n    if (iter==mRecordContainers.end())\n        throw std::logic_error (\"invalid local index type\");\n\n    iter->second->insertRecord(record);\n\n    mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),\n        LocalIndex (iter->second->getSize()-1, type)));\n}\n\nvoid CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const\n{\n    LocalIndex localIndex = globalToLocalIndex (index);\n\n    RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;\n\n    std::string id = source->getId (localIndex.first);\n\n    std::unique_ptr<CSMWorld::RecordBase> newRecord (source->getRecord (localIndex.first).modifiedCopy());\n\n    target.insertRecord (*newRecord, localIndex.second, id);\n}\n"
  },
  {
    "path": "apps/opencs/model/world/refiddata.hpp",
    "content": "#ifndef CSM_WOLRD_REFIDDATA_H\n#define CSM_WOLRD_REFIDDATA_H\n\n#include <vector>\n#include <map>\n\n#include <components/esm/loadacti.hpp>\n#include <components/esm/loadalch.hpp>\n#include <components/esm/loadappa.hpp>\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadbook.hpp>\n#include <components/esm/loadclot.hpp>\n#include <components/esm/loadcont.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/loaddoor.hpp>\n#include <components/esm/loadingr.hpp>\n#include <components/esm/loadlevlist.hpp>\n#include <components/esm/loadligh.hpp>\n#include <components/esm/loadlock.hpp>\n#include <components/esm/loadprob.hpp>\n#include <components/esm/loadrepa.hpp>\n#include <components/esm/loadstat.hpp>\n#include <components/esm/loadweap.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/loadmisc.hpp>\n#include <components/esm/esmwriter.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include \"record.hpp\"\n#include \"universalid.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace CSMWorld\n{\n    struct RefIdDataContainerBase\n    {\n        virtual ~RefIdDataContainerBase();\n\n        virtual int getSize() const = 0;\n\n        virtual const RecordBase& getRecord (int index) const = 0;\n\n        virtual RecordBase& getRecord (int index)= 0;\n\n        virtual void appendRecord (const std::string& id, bool base) = 0;\n\n        virtual void insertRecord (RecordBase& record) = 0;\n\n        virtual int load (ESM::ESMReader& reader, bool base) = 0;\n        ///< \\return index of a loaded record or -1 if no record was loaded\n\n        virtual void erase (int index, int count) = 0;\n\n        virtual std::string getId (int index) const = 0;\n\n        virtual void save (int index, ESM::ESMWriter& writer) const = 0;\n    };\n\n    template<typename RecordT>\n    struct RefIdDataContainer : public RefIdDataContainerBase\n    {\n        std::vector<Record<RecordT> > mContainer;\n\n        int getSize() const override;\n\n        const RecordBase& getRecord (int index) const override;\n\n        RecordBase& getRecord (int index) override;\n\n        void appendRecord (const std::string& id, bool base) override;\n\n        void insertRecord (RecordBase& record) override;\n\n        int load (ESM::ESMReader& reader, bool base) override;\n        ///< \\return index of a loaded record or -1 if no record was loaded\n\n        void erase (int index, int count) override;\n\n        std::string getId (int index) const override;\n\n        void save (int index, ESM::ESMWriter& writer) const override;\n    };\n\n    template<typename RecordT>\n    void RefIdDataContainer<RecordT>::insertRecord(RecordBase& record)\n    {\n        Record<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record);\n        mContainer.push_back(newRecord);\n    }\n\n    template<typename RecordT>\n    int RefIdDataContainer<RecordT>::getSize() const\n    {\n        return static_cast<int> (mContainer.size());\n    }\n\n    template<typename RecordT>\n    const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const\n    {\n        return mContainer.at (index);\n    }\n\n    template<typename RecordT>\n    RecordBase& RefIdDataContainer<RecordT>::getRecord (int index)\n    {\n        return mContainer.at (index);\n    }\n\n    template<typename RecordT>\n    void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)\n    {\n        Record<RecordT> record;\n\n        record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;\n\n        record.mBase.mId = id;\n        record.mModified.mId = id;\n        (base ? record.mBase : record.mModified).blank();\n\n        mContainer.push_back (record);\n    }\n\n    template<typename RecordT>\n    int RefIdDataContainer<RecordT>::load (ESM::ESMReader& reader, bool base)\n    {\n        RecordT record;\n        bool isDeleted = false;\n\n        record.load(reader, isDeleted);\n\n        int index = 0;\n        int numRecords = static_cast<int>(mContainer.size());\n        for (; index < numRecords; ++index)\n        {\n            if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId))\n            {\n                break;\n            }\n        }\n\n        if (isDeleted)\n        {\n            if (index == numRecords)\n            {\n                // deleting a record that does not exist\n                // ignore it for now\n                /// \\todo report the problem to the user\n                return -1;\n            }\n\n            // Flag the record as Deleted even for a base content file.\n            // RefIdData is responsible for its erasure.\n            mContainer[index].mState = RecordBase::State_Deleted;\n        }\n        else\n        {\n            if (index == numRecords)\n            {\n                appendRecord(record.mId, base);\n                if (base)\n                {\n                    mContainer.back().mBase = record;\n                }\n                else\n                {\n                    mContainer.back().mModified = record;\n                }\n            }\n            else if (!base)\n            {\n                mContainer[index].setModified(record);\n            }\n            else\n            {\n                // Overwrite\n                mContainer[index].setModified(record);\n                mContainer[index].merge();\n            }\n        }\n\n        return index;\n    }\n\n    template<typename RecordT>\n    void RefIdDataContainer<RecordT>::erase (int index, int count)\n    {\n        if (index<0 || index+count>getSize())\n            throw std::runtime_error (\"invalid RefIdDataContainer index\");\n\n        mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count);\n    }\n\n    template<typename RecordT>\n    std::string RefIdDataContainer<RecordT>::getId (int index) const\n    {\n        return mContainer.at (index).get().mId;\n    }\n\n    template<typename RecordT>\n    void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const\n    {\n        Record<RecordT> record = mContainer.at(index);\n\n        if (record.isModified() || record.mState == RecordBase::State_Deleted)\n        {\n            RecordT esmRecord = record.get();\n            writer.startRecord(esmRecord.sRecordId);\n            esmRecord.save(writer, record.mState == RecordBase::State_Deleted);\n            writer.endRecord(esmRecord.sRecordId);\n        }\n    }\n\n\n    class RefIdData\n    {\n        public:\n\n            typedef std::pair<int, UniversalId::Type> LocalIndex;\n\n        private:\n\n            RefIdDataContainer<ESM::Activator> mActivators;\n            RefIdDataContainer<ESM::Potion> mPotions;\n            RefIdDataContainer<ESM::Apparatus> mApparati;\n            RefIdDataContainer<ESM::Armor> mArmors;\n            RefIdDataContainer<ESM::Book> mBooks;\n            RefIdDataContainer<ESM::Clothing> mClothing;\n            RefIdDataContainer<ESM::Container> mContainers;\n            RefIdDataContainer<ESM::Creature> mCreatures;\n            RefIdDataContainer<ESM::Door> mDoors;\n            RefIdDataContainer<ESM::Ingredient> mIngredients;\n            RefIdDataContainer<ESM::CreatureLevList> mCreatureLevelledLists;\n            RefIdDataContainer<ESM::ItemLevList> mItemLevelledLists;\n            RefIdDataContainer<ESM::Light> mLights;\n            RefIdDataContainer<ESM::Lockpick> mLockpicks;\n            RefIdDataContainer<ESM::Miscellaneous> mMiscellaneous;\n            RefIdDataContainer<ESM::NPC> mNpcs;\n            RefIdDataContainer<ESM::Probe> mProbes;\n            RefIdDataContainer<ESM::Repair> mRepairs;\n            RefIdDataContainer<ESM::Static> mStatics;\n            RefIdDataContainer<ESM::Weapon> mWeapons;\n\n            std::map<std::string, LocalIndex> mIndex;\n\n            std::map<UniversalId::Type, RefIdDataContainerBase *> mRecordContainers;\n\n            void erase (const LocalIndex& index, int count);\n            ///< Must not spill over into another type.\n\n            std::string getRecordId(const LocalIndex &index) const;\n\n        public:\n\n            RefIdData();\n\n            LocalIndex globalToLocalIndex (int index) const;\n\n            int localToGlobalIndex (const LocalIndex& index) const;\n\n            LocalIndex searchId (const std::string& id) const;\n\n            void erase (int index, int count);\n\n            void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type,\n                const std::string& id);\n\n            const RecordBase& getRecord (const LocalIndex& index) const;\n\n            RecordBase& getRecord (const LocalIndex& index);\n\n            void appendRecord (UniversalId::Type type, const std::string& id, bool base);\n\n            int getAppendIndex (UniversalId::Type type) const;\n\n            void load (ESM::ESMReader& reader, bool base, UniversalId::Type type);\n\n            int getSize() const;\n\n            std::vector<std::string> getIds (bool listDeleted = true) const;\n            ///< Return a sorted collection of all IDs\n            ///\n            /// \\param listDeleted include deleted record in the list\n\n            void save (int index, ESM::ESMWriter& writer) const;\n\n            //RECORD CONTAINERS ACCESS METHODS\n            const RefIdDataContainer<ESM::Book>& getBooks() const;\n            const RefIdDataContainer<ESM::Activator>& getActivators() const;\n            const RefIdDataContainer<ESM::Potion>& getPotions() const;\n            const RefIdDataContainer<ESM::Apparatus>& getApparati() const;\n            const RefIdDataContainer<ESM::Armor>& getArmors() const;\n            const RefIdDataContainer<ESM::Clothing>& getClothing() const;\n            const RefIdDataContainer<ESM::Container>& getContainers() const;\n            const RefIdDataContainer<ESM::Creature>& getCreatures() const;\n            const RefIdDataContainer<ESM::Door>& getDoors() const;\n            const RefIdDataContainer<ESM::Ingredient>& getIngredients() const;\n            const RefIdDataContainer<ESM::CreatureLevList>& getCreatureLevelledLists() const;\n            const RefIdDataContainer<ESM::ItemLevList>& getItemLevelledList() const;\n            const RefIdDataContainer<ESM::Light>& getLights() const;\n            const RefIdDataContainer<ESM::Lockpick>& getLocpicks() const;\n            const RefIdDataContainer<ESM::Miscellaneous>& getMiscellaneous() const;\n            const RefIdDataContainer<ESM::NPC>& getNPCs() const;\n            const RefIdDataContainer<ESM::Weapon >& getWeapons() const;\n            const RefIdDataContainer<ESM::Probe >& getProbes() const;\n            const RefIdDataContainer<ESM::Repair>& getRepairs() const;\n            const RefIdDataContainer<ESM::Static>& getStatics() const;\n\n            void copyTo (int index, RefIdData& target) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/regionmap.cpp",
    "content": "#include \"regionmap.hpp\"\n\n#include <cmath>\n#include <algorithm>\n\n#include <QBrush>\n\n#include <components/misc/stringops.hpp>\n\n#include \"data.hpp\"\n#include \"universalid.hpp\"\n\nCSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {}\n\nCSMWorld::RegionMap::CellDescription::CellDescription (const Record<Cell>& cell)\n{\n    const Cell& cell2 = cell.get();\n\n    if (!cell2.isExterior())\n        throw std::logic_error (\"Interior cell in region map\");\n\n    mDeleted = cell.isDeleted();\n\n    mRegion = cell2.mRegion;\n    mName = cell2.mName;\n}\n\nCSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex (const QModelIndex& index) const\n{\n    return mMin.move (index.column(), mMax.getY()-mMin.getY() - index.row()-1);\n}\n\nQModelIndex CSMWorld::RegionMap::getIndex (const CellCoordinates& index) const\n{\n    // I hate you, Qt API naming scheme!\n    return QAbstractTableModel::index (mMax.getY()-mMin.getY() - (index.getY()-mMin.getY())-1,\n        index.getX()-mMin.getX());\n}\n\nCSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex (const Cell& cell) const\n{\n    std::istringstream stream (cell.mId);\n\n    char ignore;\n    int x = 0;\n    int y = 0;\n    stream >> ignore >> x >> y;\n\n    return CellCoordinates (x, y);\n}\n\nvoid CSMWorld::RegionMap::buildRegions()\n{\n    const IdCollection<ESM::Region>& regions = mData.getRegions();\n\n    int size = regions.getSize();\n\n    for (int i=0; i<size; ++i)\n    {\n        const Record<ESM::Region>& region = regions.getRecord (i);\n\n        if (!region.isDeleted())\n            mColours.insert (std::make_pair (Misc::StringUtils::lowerCase (region.get().mId),\n                region.get().mMapColor));\n    }\n}\n\nvoid CSMWorld::RegionMap::buildMap()\n{\n    const IdCollection<Cell>& cells = mData.getCells();\n\n    int size = cells.getSize();\n\n    for (int i=0; i<size; ++i)\n    {\n        const Record<Cell>& cell = cells.getRecord (i);\n\n        const Cell& cell2 = cell.get();\n\n        if (cell2.isExterior())\n        {\n            CellDescription description (cell);\n\n            CellCoordinates index = getIndex (cell2);\n\n            mMap.insert (std::make_pair (index, description));\n        }\n    }\n\n    std::pair<CellCoordinates, CellCoordinates> mapSize = getSize();\n\n    mMin = mapSize.first;\n    mMax = mapSize.second;\n}\n\nvoid CSMWorld::RegionMap::addCell (const CellCoordinates& index, const CellDescription& description)\n{\n    std::map<CellCoordinates, CellDescription>::iterator cell = mMap.find (index);\n\n    if (cell!=mMap.end())\n    {\n        cell->second = description;\n    }\n    else\n    {\n        updateSize();\n\n        mMap.insert (std::make_pair (index, description));\n    }\n\n    QModelIndex index2 = getIndex (index);\n\n    dataChanged (index2, index2);\n}\n\nvoid CSMWorld::RegionMap::addCells (int start, int end)\n{\n    const IdCollection<Cell>& cells = mData.getCells();\n\n    for (int i=start; i<=end; ++i)\n    {\n        const Record<Cell>& cell = cells.getRecord (i);\n\n        const Cell& cell2 = cell.get();\n\n        if (cell2.isExterior())\n        {\n            CellCoordinates index = getIndex (cell2);\n\n            CellDescription description (cell);\n\n            addCell (index, description);\n        }\n    }\n}\n\nvoid CSMWorld::RegionMap::removeCell (const CellCoordinates& index)\n{\n    std::map<CellCoordinates, CellDescription>::iterator cell = mMap.find (index);\n\n    if (cell!=mMap.end())\n    {\n        mMap.erase (cell);\n\n        QModelIndex index2 = getIndex (index);\n\n        dataChanged (index2, index2);\n\n        updateSize();\n    }\n}\n\nvoid CSMWorld::RegionMap::addRegion (const std::string& region, unsigned int colour)\n{\n    mColours[Misc::StringUtils::lowerCase (region)] = colour;\n}\n\nvoid CSMWorld::RegionMap::removeRegion (const std::string& region)\n{\n    std::map<std::string, unsigned int>::iterator iter (\n        mColours.find (Misc::StringUtils::lowerCase (region)));\n\n    if (iter!=mColours.end())\n        mColours.erase (iter);\n}\n\nvoid CSMWorld::RegionMap::updateRegions (const std::vector<std::string>& regions)\n{\n    std::vector<std::string> regions2 (regions);\n\n    std::for_each (regions2.begin(), regions2.end(), Misc::StringUtils::lowerCaseInPlace);\n    std::sort (regions2.begin(), regions2.end());\n\n    for (std::map<CellCoordinates, CellDescription>::const_iterator iter (mMap.begin());\n         iter!=mMap.end(); ++iter)\n    {\n        if (!iter->second.mRegion.empty() &&\n            std::find (regions2.begin(), regions2.end(),\n            Misc::StringUtils::lowerCase (iter->second.mRegion))!=regions2.end())\n        {\n            QModelIndex index = getIndex (iter->first);\n\n            dataChanged (index, index);\n        }\n    }\n}\n\nvoid CSMWorld::RegionMap::updateSize()\n{\n    std::pair<CellCoordinates, CellCoordinates> size = getSize();\n\n    if (int diff = size.first.getX() - mMin.getX())\n    {\n        beginInsertColumns (QModelIndex(), 0, std::abs (diff)-1);\n        mMin = CellCoordinates (size.first.getX(), mMin.getY());\n        endInsertColumns();\n    }\n\n    if (int diff = size.first.getY() - mMin.getY())\n    {\n        beginInsertRows (QModelIndex(), 0, std::abs (diff)-1);\n        mMin = CellCoordinates (mMin.getX(), size.first.getY());\n        endInsertRows();\n    }\n\n    if (int diff = size.second.getX() - mMax.getX())\n    {\n        int columns = columnCount();\n\n        if (diff>0)\n            beginInsertColumns (QModelIndex(), columns, columns+diff-1);\n        else\n            beginRemoveColumns (QModelIndex(), columns+diff, columns-1);\n\n        mMax = CellCoordinates (size.second.getX(), mMax.getY());\n        endInsertColumns();\n    }\n\n    if (int diff = size.second.getY() - mMax.getY())\n    {\n        int rows = rowCount();\n\n        if (diff>0)\n            beginInsertRows (QModelIndex(), rows, rows+diff-1);\n        else\n            beginRemoveRows (QModelIndex(), rows+diff, rows-1);\n\n        mMax = CellCoordinates (mMax.getX(), size.second.getY());\n        endInsertRows();\n    }\n}\n\nstd::pair<CSMWorld::CellCoordinates, CSMWorld::CellCoordinates> CSMWorld::RegionMap::getSize() const\n{\n    const IdCollection<Cell>& cells = mData.getCells();\n\n    int size = cells.getSize();\n\n    CellCoordinates min (0, 0);\n    CellCoordinates max (0, 0);\n\n    for (int i=0; i<size; ++i)\n    {\n        const Record<Cell>& cell = cells.getRecord (i);\n\n        const Cell& cell2 = cell.get();\n\n        if (cell2.isExterior())\n        {\n            CellCoordinates index = getIndex (cell2);\n\n            if (min==max)\n            {\n                min = index;\n                max = min.move (1, 1);\n            }\n            else\n            {\n                if (index.getX()<min.getX())\n                    min = CellCoordinates (index.getX(), min.getY());\n                else if (index.getX()>=max.getX())\n                    max = CellCoordinates (index.getX()+1, max.getY());\n\n                if (index.getY()<min.getY())\n                    min = CellCoordinates (min.getX(), index.getY());\n                else if (index.getY()>=max.getY())\n                    max = CellCoordinates (max.getX(), index.getY() + 1);\n            }\n        }\n    }\n\n    return std::make_pair (min, max);\n}\n\nCSMWorld::RegionMap::RegionMap (Data& data) : mData (data)\n{\n    buildRegions();\n    buildMap();\n\n    QAbstractItemModel *regions = data.getTableModel (UniversalId (UniversalId::Type_Regions));\n\n    connect (regions, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int)));\n    connect (regions, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (regionsInserted (const QModelIndex&, int, int)));\n    connect (regions, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&)));\n\n    QAbstractItemModel *cells = data.getTableModel (UniversalId (UniversalId::Type_Cells));\n\n    connect (cells, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (cellsAboutToBeRemoved (const QModelIndex&, int, int)));\n    connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (cellsInserted (const QModelIndex&, int, int)));\n    connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (cellsChanged (const QModelIndex&, const QModelIndex&)));\n}\n\nint CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return mMax.getY()-mMin.getY();\n}\n\nint CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return mMax.getX()-mMin.getX();\n}\n\nQVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const\n{\n    if (role==Qt::SizeHintRole)\n        return QSize (16, 16);\n\n    if (role==Qt::BackgroundRole)\n    {\n        /// \\todo GUI class in non-GUI code. Needs to be addressed eventually.\n\n        std::map<CellCoordinates, CellDescription>::const_iterator cell =\n            mMap.find (getIndex (index));\n\n        if (cell!=mMap.end())\n        {\n            if (cell->second.mDeleted)\n                return QBrush (Qt::red, Qt::DiagCrossPattern);\n\n            std::map<std::string, unsigned int>::const_iterator iter =\n                mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion));\n\n            if (iter!=mColours.end())\n                return QBrush (QColor (iter->second & 0xff, \n                                       (iter->second >> 8) & 0xff, \n                                       (iter->second >> 16) & 0xff));\n\n            if (cell->second.mRegion.empty())\n                return QBrush (Qt::Dense6Pattern); // no region\n\n            return QBrush (Qt::red, Qt::Dense6Pattern); // invalid region\n        }\n\n        return QBrush (Qt::DiagCrossPattern);\n    }\n\n    if (role==Qt::ToolTipRole)\n    {\n        CellCoordinates cellIndex = getIndex (index);\n\n        std::ostringstream stream;\n\n        stream << cellIndex;\n\n        std::map<CellCoordinates, CellDescription>::const_iterator cell =\n            mMap.find (cellIndex);\n\n        if (cell!=mMap.end())\n        {\n            if (!cell->second.mName.empty())\n                stream << \" \" << cell->second.mName;\n\n            if (cell->second.mDeleted)\n                stream << \" (deleted)\";\n\n            if (!cell->second.mRegion.empty())\n            {\n                stream << \"<br>\";\n\n                std::map<std::string, unsigned int>::const_iterator iter =\n                    mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion));\n\n                if (iter!=mColours.end())\n                    stream << cell->second.mRegion;\n                else\n                    stream << \"<font color=red>\" << cell->second.mRegion << \"</font>\";\n            }\n        }\n        else\n            stream << \" (no cell)\";\n\n        return QString::fromUtf8 (stream.str().c_str());\n    }\n\n    if (role==Role_Region)\n    {\n        CellCoordinates cellIndex = getIndex (index);\n\n        std::map<CellCoordinates, CellDescription>::const_iterator cell =\n            mMap.find (cellIndex);\n\n        if (cell!=mMap.end() && !cell->second.mRegion.empty())\n            return QString::fromUtf8 (Misc::StringUtils::lowerCase (cell->second.mRegion).c_str());\n    }\n\n    if (role==Role_CellId)\n    {\n        CellCoordinates cellIndex = getIndex (index);\n\n        std::ostringstream stream;\n        stream << \"#\" << cellIndex.getX() << \" \" << cellIndex.getY();\n\n        return QString::fromUtf8 (stream.str().c_str());\n    }\n\n    return QVariant();\n}\n\nQt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const\n{\n    return Qt::ItemIsSelectable | Qt::ItemIsEnabled;\n}\n\nvoid CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    std::vector<std::string> update;\n\n    const IdCollection<ESM::Region>& regions = mData.getRegions();\n\n    for (int i=start; i<=end; ++i)\n    {\n        const Record<ESM::Region>& region = regions.getRecord (i);\n\n        update.push_back (region.get().mId);\n\n        removeRegion (region.get().mId);\n    }\n\n    updateRegions (update);\n}\n\nvoid CSMWorld::RegionMap::regionsInserted (const QModelIndex& parent, int start, int end)\n{\n    std::vector<std::string> update;\n\n    const IdCollection<ESM::Region>& regions = mData.getRegions();\n\n    for (int i=start; i<=end; ++i)\n    {\n        const Record<ESM::Region>& region = regions.getRecord (i);\n\n        if (!region.isDeleted())\n        {\n            update.push_back (region.get().mId);\n\n            addRegion (region.get().mId, region.get().mMapColor);\n        }\n    }\n\n    updateRegions (update);\n}\n\nvoid CSMWorld::RegionMap::regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    // Note: At this point an additional check could be inserted to see if there is any change to the\n    // columns we are interested in. If not we can exit the function here and avoid all updating.\n\n    std::vector<std::string> update;\n\n    const IdCollection<ESM::Region>& regions = mData.getRegions();\n\n    for (int i=topLeft.row(); i<=bottomRight.column(); ++i)\n    {\n        const Record<ESM::Region>& region = regions.getRecord (i);\n\n        update.push_back (region.get().mId);\n\n        if (!region.isDeleted())\n            addRegion (region.get().mId, region.get().mMapColor);\n        else\n            removeRegion (region.get().mId);\n    }\n\n    updateRegions (update);\n}\n\nvoid CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    const IdCollection<Cell>& cells = mData.getCells();\n\n    for (int i=start; i<=end; ++i)\n    {\n        const Record<Cell>& cell = cells.getRecord (i);\n\n        const Cell& cell2 = cell.get();\n\n        if (cell2.isExterior())\n            removeCell (getIndex (cell2));\n    }\n}\n\nvoid CSMWorld::RegionMap::cellsInserted (const QModelIndex& parent, int start, int end)\n{\n    addCells (start, end);\n}\n\nvoid CSMWorld::RegionMap::cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    // Note: At this point an additional check could be inserted to see if there is any change to the\n    // columns we are interested in. If not we can exit the function here and avoid all updating.\n\n    addCells (topLeft.row(), bottomRight.row());\n}\n"
  },
  {
    "path": "apps/opencs/model/world/regionmap.hpp",
    "content": "#ifndef CSM_WOLRD_REGIONMAP_H\n#define CSM_WOLRD_REGIONMAP_H\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include <QAbstractTableModel>\n\n#include \"record.hpp\"\n#include \"cell.hpp\"\n#include \"cellcoordinates.hpp\"\n\nnamespace CSMWorld\n{\n    class Data;\n\n    /// \\brief Model for the region map\n    ///\n    /// This class does not holds any record data (other than for the purpose of buffering).\n    class RegionMap : public QAbstractTableModel\n    {\n            Q_OBJECT\n\n        public:\n\n            enum Role\n            {\n                Role_Region = Qt::UserRole,\n                Role_CellId = Qt::UserRole+1\n            };\n\n        private:\n\n            struct CellDescription\n            {\n                bool mDeleted;\n                std::string mRegion;\n                std::string mName;\n\n                CellDescription();\n\n                CellDescription (const Record<Cell>& cell);\n            };\n\n            Data& mData;\n            std::map<CellCoordinates, CellDescription> mMap;\n            CellCoordinates mMin; ///< inclusive\n            CellCoordinates mMax; ///< exclusive\n            std::map<std::string, unsigned int> mColours; ///< region ID, colour (RGBA)\n\n            CellCoordinates getIndex (const QModelIndex& index) const;\n            ///< Translates a Qt model index into a cell index (which can contain negative components)\n\n            QModelIndex getIndex (const CellCoordinates& index) const;\n\n            CellCoordinates getIndex (const Cell& cell) const;\n\n            void buildRegions();\n\n            void buildMap();\n\n            void addCell (const CellCoordinates& index, const CellDescription& description);\n            ///< May be called on a cell that is already in the map (in which case an update is\n            // performed)\n\n            void addCells (int start, int end);\n\n            void removeCell (const CellCoordinates& index);\n            ///< May be called on a cell that is not in the map (in which case the call is ignored)\n\n            void addRegion (const std::string& region, unsigned int colour);\n            ///< May be called on a region that is already listed (in which case an update is\n            /// performed)\n            ///\n            /// \\note This function does not update the region map.\n\n            void removeRegion (const std::string& region);\n            ///< May be called on a region that is not listed (in which case the call is ignored)\n            ///\n            /// \\note This function does not update the region map.\n\n            void updateRegions (const std::vector<std::string>& regions);\n            ///< Update cells affected by the listed regions\n\n            void updateSize();\n\n            std::pair<CellCoordinates, CellCoordinates> getSize() const;\n\n        public:\n\n            RegionMap (Data& data);\n\n            int rowCount (const QModelIndex& parent = QModelIndex()) const override;\n\n            int columnCount (const QModelIndex& parent = QModelIndex()) const override;\n\n            QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const override;\n            ///< \\note Calling this function with role==Role_CellId may return the ID of a cell\n            /// that does not exist.\n\n            Qt::ItemFlags flags (const QModelIndex& index) const override;\n\n        private slots:\n\n            void regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void regionsInserted (const QModelIndex& parent, int start, int end);\n\n            void regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void cellsInserted (const QModelIndex& parent, int start, int end);\n\n            void cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/resources.cpp",
    "content": "#include \"resources.hpp\"\n\n#include <sstream>\n#include <stdexcept>\n#include <algorithm>\n\n#include <components/vfs/manager.hpp>\n\n#include <components/misc/stringops.hpp>\n\nCSMWorld::Resources::Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type,\n    const char * const *extensions)\n: mBaseDirectory (baseDirectory), mType (type)\n{\n    recreate(vfs, extensions);\n}\n\nvoid CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char * const *extensions)\n{\n    mFiles.clear();\n    mIndex.clear();\n\n    size_t baseSize = mBaseDirectory.size();\n\n    const std::map<std::string, VFS::File*>& index = vfs->getIndex();\n    for (std::map<std::string, VFS::File*>::const_iterator it = index.begin(); it != index.end(); ++it)\n    {\n        std::string filepath = it->first;\n        if (filepath.size()<baseSize+1 ||\n            filepath.substr (0, baseSize)!=mBaseDirectory ||\n            (filepath[baseSize]!='/' && filepath[baseSize]!='\\\\'))\n            continue;\n\n        if (extensions)\n        {\n            std::string::size_type extensionIndex = filepath.find_last_of ('.');\n\n            if (extensionIndex==std::string::npos)\n                continue;\n\n            std::string extension = filepath.substr (extensionIndex+1);\n\n            int i = 0;\n\n            for (; extensions[i]; ++i)\n                if (extensions[i]==extension)\n                    break;\n\n            if (!extensions[i])\n                continue;\n        }\n\n        std::string file = filepath.substr (baseSize+1);\n        mFiles.push_back (file);\n        std::replace (file.begin(), file.end(), '\\\\', '/');\n        mIndex.insert (std::make_pair (\n            Misc::StringUtils::lowerCase (file), static_cast<int> (mFiles.size())-1));\n    }\n}\n\nint CSMWorld::Resources::getSize() const\n{\n    return static_cast<int>(mFiles.size());\n}\n\nstd::string CSMWorld::Resources::getId (int index) const\n{\n    return mFiles.at (index);\n}\n\nint CSMWorld::Resources::getIndex (const std::string& id) const\n{\n    int index = searchId (id);\n\n    if (index==-1)\n    {\n        std::ostringstream stream;\n        stream << \"Invalid resource: \" << mBaseDirectory << '/' << id;\n\n        throw std::runtime_error (stream.str());\n    }\n\n    return index;\n}\n\nint CSMWorld::Resources::searchId (const std::string& id) const\n{\n    std::string id2 = Misc::StringUtils::lowerCase (id);\n\n    std::replace (id2.begin(), id2.end(), '\\\\', '/');\n\n    std::map<std::string, int>::const_iterator iter = mIndex.find (id2);\n\n    if (iter==mIndex.end())\n        return -1;\n\n    return iter->second;\n}\n\nCSMWorld::UniversalId::Type CSMWorld::Resources::getType() const\n{\n    return mType;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/resources.hpp",
    "content": "#ifndef CSM_WOLRD_RESOURCES_H\n#define CSM_WOLRD_RESOURCES_H\n\n#include <string>\n#include <map>\n#include <vector>\n\n#include \"universalid.hpp\"\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace CSMWorld\n{\n    class Resources\n    {\n            std::map<std::string, int> mIndex;\n            std::vector<std::string> mFiles;\n            std::string mBaseDirectory;\n            UniversalId::Type mType;\n\n        public:\n\n            /// \\param type Type of resources in this table.\n            Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type,\n                const char * const *extensions = nullptr);\n\n            void recreate(const VFS::Manager* vfs, const char * const *extensions = nullptr);\n\n            int getSize() const;\n\n            std::string getId (int index) const;\n\n            int getIndex (const std::string& id) const;\n\n            int searchId (const std::string& id) const;\n\n            UniversalId::Type getType() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/resourcesmanager.cpp",
    "content": "#include \"resourcesmanager.hpp\"\n\n#include <stdexcept>\n\nCSMWorld::ResourcesManager::ResourcesManager()\n    : mVFS(nullptr)\n{\n}\n\nvoid CSMWorld::ResourcesManager::addResources (const Resources& resources)\n{\n    mResources.insert (std::make_pair (resources.getType(), resources));\n    mResources.insert (std::make_pair (UniversalId::getParentType (resources.getType()),\n        resources));\n}\n\nconst char * const * CSMWorld::ResourcesManager::getMeshExtensions()\n{\n    // maybe we could go over the osgDB::Registry to list all supported node formats\n    static const char * const sMeshTypes[] = { \"nif\", \"osg\", \"osgt\", \"osgb\", \"osgx\", \"osg2\", \"dae\", 0 };\n    return sMeshTypes;\n}\n\nvoid CSMWorld::ResourcesManager::setVFS(const VFS::Manager *vfs)\n{\n    mVFS = vfs;\n    mResources.clear();\n\n    addResources (Resources (vfs, \"meshes\", UniversalId::Type_Mesh, getMeshExtensions()));\n    addResources (Resources (vfs, \"icons\", UniversalId::Type_Icon));\n    addResources (Resources (vfs, \"music\", UniversalId::Type_Music));\n    addResources (Resources (vfs, \"sound\", UniversalId::Type_SoundRes));\n    addResources (Resources (vfs, \"textures\", UniversalId::Type_Texture));\n    addResources (Resources (vfs, \"video\", UniversalId::Type_Video));\n}\n\nconst VFS::Manager* CSMWorld::ResourcesManager::getVFS() const\n{\n    return mVFS;\n}\n\nvoid CSMWorld::ResourcesManager::recreateResources()\n{\n    std::map<UniversalId::Type, Resources>::iterator it = mResources.begin();\n    for ( ; it != mResources.end(); ++it)\n    {\n        if (it->first == UniversalId::Type_Mesh)\n            it->second.recreate(mVFS, getMeshExtensions());\n        else\n            it->second.recreate(mVFS);\n    }\n}\n\nconst CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const\n{\n    std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type);\n\n    if (iter==mResources.end())\n        throw std::logic_error (\"Unknown resource type\");\n\n    return iter->second;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/resourcesmanager.hpp",
    "content": "#ifndef CSM_WOLRD_RESOURCESMANAGER_H\n#define CSM_WOLRD_RESOURCESMANAGER_H\n\n#include <map>\n\n#include \"universalid.hpp\"\n#include \"resources.hpp\"\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace CSMWorld\n{\n    class ResourcesManager\n    {\n            std::map<UniversalId::Type, Resources> mResources;\n            const VFS::Manager* mVFS;\n\n        private:\n\n            void addResources (const Resources& resources);\n\n            const char * const * getMeshExtensions();\n\n        public:\n\n            ResourcesManager();\n\n            const VFS::Manager* getVFS() const;\n\n            void setVFS(const VFS::Manager* vfs);\n\n            void recreateResources();\n\n            const Resources& get (UniversalId::Type type) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/resourcetable.cpp",
    "content": "#include \"resourcetable.hpp\"\n\n#include <stdexcept>\n\n#include \"resources.hpp\"\n#include \"columnbase.hpp\"\n#include \"universalid.hpp\"\n\nCSMWorld::ResourceTable::ResourceTable (const Resources *resources, unsigned int features)\n: IdTableBase (features | Feature_Constant), mResources (resources)\n{}\n\nCSMWorld::ResourceTable::~ResourceTable() {}\n\nint CSMWorld::ResourceTable::rowCount (const QModelIndex & parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return mResources->getSize();\n}\n\nint CSMWorld::ResourceTable::columnCount (const QModelIndex & parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return 2; // ID, type\n}\n\nQVariant CSMWorld::ResourceTable::data  (const QModelIndex & index, int role) const\n{\n    if (role!=Qt::DisplayRole)\n        return QVariant();\n\n    if (index.column()==0)\n        return QString::fromUtf8 (mResources->getId (index.row()).c_str());\n\n    if (index.column()==1)\n        return static_cast<int> (mResources->getType());\n\n    throw std::logic_error (\"Invalid column in resource table\");\n}\n\nQVariant CSMWorld::ResourceTable::headerData (int section, Qt::Orientation orientation,\n    int role ) const\n{\n    if (orientation==Qt::Vertical)\n        return QVariant();\n\n    if (role==ColumnBase::Role_Flags)\n        return section==0 ? ColumnBase::Flag_Table : 0;\n\n    switch (section)\n    {\n        case 0:\n\n            if (role==Qt::DisplayRole)\n                return Columns::getName (Columns::ColumnId_Id).c_str();\n\n            if (role==ColumnBase::Role_Display)\n                return ColumnBase::Display_Id;\n\n            break;\n\n        case 1:\n\n            if (role==Qt::DisplayRole)\n                return Columns::getName (Columns::ColumnId_RecordType).c_str();\n\n            if (role==ColumnBase::Role_Display)\n                return ColumnBase::Display_Integer;\n\n            break;\n    }\n\n    return QVariant();\n}\n\nbool CSMWorld::ResourceTable::setData ( const QModelIndex &index, const QVariant &value,\n    int role)\n{\n    return false;\n}\n\nQt::ItemFlags CSMWorld::ResourceTable::flags (const QModelIndex & index) const\n{\n    return Qt::ItemIsSelectable | Qt::ItemIsEnabled;\n}\n\nQModelIndex CSMWorld::ResourceTable::index (int row, int column, const QModelIndex& parent)\n    const\n{\n    if (parent.isValid())\n        return QModelIndex();\n\n    if (row<0 || row>=mResources->getSize())\n        return QModelIndex();\n\n    if (column<0 || column>1)\n        return QModelIndex();\n\n    return createIndex (row, column);\n}\n\nQModelIndex CSMWorld::ResourceTable::parent (const QModelIndex& index) const\n{\n    return QModelIndex();\n}\n\nQModelIndex CSMWorld::ResourceTable::getModelIndex (const std::string& id, int column) const\n{\n    int row = mResources->searchId(id);\n    if (row != -1)\n        return index (row, column);\n\n    return QModelIndex();\n}\n\nint CSMWorld::ResourceTable::searchColumnIndex (Columns::ColumnId id) const\n{\n    if (id==Columns::ColumnId_Id)\n        return 0;\n\n    if (id==Columns::ColumnId_RecordType)\n        return 1;\n\n    return -1;\n}\n\nint CSMWorld::ResourceTable::findColumnIndex (Columns::ColumnId id) const\n{\n    int index = searchColumnIndex (id);\n\n    if (index==-1)\n        throw std::logic_error (\"invalid column index\");\n\n    return index;\n}\n\nstd::pair<CSMWorld::UniversalId, std::string> CSMWorld::ResourceTable::view (int row) const\n{\n    return std::make_pair (UniversalId::Type_None, \"\");\n}\n\nbool CSMWorld::ResourceTable::isDeleted (const std::string& id) const\n{\n    return false;\n}\n\nint CSMWorld::ResourceTable::getColumnId (int column) const\n{\n    switch (column)\n    {\n        case 0: return Columns::ColumnId_Id;\n        case 1: return Columns::ColumnId_RecordType;\n    }\n\n    return -1;\n}\n\nvoid CSMWorld::ResourceTable::beginReset()\n{\n    beginResetModel();\n}\n\nvoid CSMWorld::ResourceTable::endReset()\n{\n    endResetModel();\n}\n"
  },
  {
    "path": "apps/opencs/model/world/resourcetable.hpp",
    "content": "#ifndef CSM_WOLRD_RESOURCETABLE_H\n#define CSM_WOLRD_RESOURCETABLE_H\n\n#include \"idtablebase.hpp\"\n\nnamespace CSMWorld\n{\n    class Resources;\n\n    class ResourceTable : public IdTableBase\n    {\n            const Resources *mResources;\n\n        public:\n\n            /// \\note The feature Feature_Constant will be added implicitly.\n            ResourceTable (const Resources *resources, unsigned int features = 0);\n\n            virtual ~ResourceTable();\n\n            int rowCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            int columnCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            QVariant data  (const QModelIndex & index, int role = Qt::DisplayRole) const override;\n\n            QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;\n\n            bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;\n\n            Qt::ItemFlags flags (const QModelIndex & index) const override;\n\n            QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const override;\n\n            QModelIndex parent (const QModelIndex& index) const override;\n\n            QModelIndex getModelIndex (const std::string& id, int column) const override;\n\n            /// Return index of column with the given \\a id. If no such column exists, -1 is\n            /// returned.\n            int searchColumnIndex (Columns::ColumnId id) const override;\n\n            /// Return index of column with the given \\a id. If no such column exists, an\n            /// exception is thrown.\n            int findColumnIndex (Columns::ColumnId id) const override;\n\n            /// Return the UniversalId and the hint for viewing \\a row. If viewing is not\n            /// supported by this table, return (UniversalId::Type_None, \"\").\n            std::pair<UniversalId, std::string> view (int row) const override;\n\n            /// Is \\a id flagged as deleted?\n            bool isDeleted (const std::string& id) const override;\n\n            int getColumnId (int column) const override;\n\n            /// Signal Qt that the data is about to change.\n            void beginReset();\n            /// Signal Qt that the data has been changed.\n            void endReset();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/scope.cpp",
    "content": "#include \"scope.hpp\"\n\n#include <stdexcept>\n\n#include <components/misc/stringops.hpp>\n\nCSMWorld::Scope CSMWorld::getScopeFromId (const std::string& id)\n{\n    // get root namespace\n    std::string namespace_;\n\n    std::string::size_type i = id.find (\"::\");\n\n    if (i!=std::string::npos)\n        namespace_ = Misc::StringUtils::lowerCase (id.substr (0, i));\n\n    if (namespace_==\"project\")\n        return Scope_Project;\n\n    if (namespace_==\"session\")\n        return Scope_Session;\n\n    return Scope_Content;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/scope.hpp",
    "content": "#ifndef CSM_WOLRD_SCOPE_H\n#define CSM_WOLRD_SCOPE_H\n\n#include <string>\n\nnamespace CSMWorld\n{\n    enum Scope\n    {\n        // record stored in content file\n        Scope_Content = 1,\n\n        // record stored in project file\n        Scope_Project = 2,\n\n        // record that exists only for the duration of one editing session\n        Scope_Session = 4\n    };\n\n    Scope getScopeFromId (const std::string& id);\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/scriptcontext.cpp",
    "content": "#include \"scriptcontext.hpp\"\n\n#include <algorithm>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/compiler/quickfileparser.hpp>\n#include <components/compiler/nullerrorhandler.hpp>\n#include <components/compiler/scanner.hpp>\n\n#include \"data.hpp\"\n\nCSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUpdated (false) {}\n\nbool CSMWorld::ScriptContext::canDeclareLocals() const\n{\n    return true;\n}\n\nchar CSMWorld::ScriptContext::getGlobalType (const std::string& name) const\n{\n    int index = mData.getGlobals().searchId (name);\n\n    if (index!=-1)\n    {\n        switch (mData.getGlobals().getRecord (index).get().mValue.getType())\n        {\n            case ESM::VT_Short: return 's';\n            case ESM::VT_Long: return 'l';\n            case ESM::VT_Float: return 'f';\n\n            default: return ' ';\n        }\n    }\n\n    return ' ';\n}\n\nstd::pair<char, bool> CSMWorld::ScriptContext::getMemberType (const std::string& name,\n    const std::string& id) const\n{\n    std::string id2 = Misc::StringUtils::lowerCase (id);\n\n    int index = mData.getScripts().searchId (id2);\n    bool reference = false;\n\n    if (index==-1)\n    {\n        // ID is not a script ID. Search for a matching referenceable instead.\n        index = mData.getReferenceables().searchId (id2);\n\n        if (index!=-1)\n        {\n            // Referenceable found.\n            int columnIndex = mData.getReferenceables().findColumnIndex (Columns::ColumnId_Script);\n\n            id2 = Misc::StringUtils::lowerCase (mData.getReferenceables().\n                getData (index, columnIndex).toString().toUtf8().constData());\n\n            if (!id2.empty())\n            {\n                // Referenceable has a script -> use it.\n                index = mData.getScripts().searchId (id2);\n                reference = true;\n            }\n        }\n    }\n\n    if (index==-1)\n        return std::make_pair (' ', false);\n\n    std::map<std::string, Compiler::Locals>::iterator iter = mLocals.find (id2);\n\n    if (iter==mLocals.end())\n    {\n        Compiler::Locals locals;\n\n        Compiler::NullErrorHandler errorHandler;\n        std::istringstream stream (mData.getScripts().getRecord (index).get().mScriptText);\n        Compiler::QuickFileParser parser (errorHandler, *this, locals);\n        Compiler::Scanner scanner (errorHandler, stream, getExtensions());\n        scanner.scan (parser);\n\n        iter = mLocals.insert (std::make_pair (id2, locals)).first;\n    }\n\n    return std::make_pair (iter->second.getType (Misc::StringUtils::lowerCase (name)), reference);\n}\n\nbool CSMWorld::ScriptContext::isId (const std::string& name) const\n{\n    if (!mIdsUpdated)\n    {\n        mIds = mData.getIds();\n\n        std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCaseInPlace);\n        std::sort (mIds.begin(), mIds.end());\n\n        mIdsUpdated = true;\n    }\n\n    return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name));\n}\n\nbool CSMWorld::ScriptContext::isJournalId (const std::string& name) const\n{\n    return mData.getJournals().searchId (name)!=-1;\n}\n\nvoid CSMWorld::ScriptContext::invalidateIds()\n{\n    mIdsUpdated = false;\n}\n\nvoid CSMWorld::ScriptContext::clear()\n{\n    mIds.clear();\n    mIdsUpdated = false;\n    mLocals.clear();\n}\n\nbool CSMWorld::ScriptContext::clearLocals (const std::string& script)\n{\n    std::map<std::string, Compiler::Locals>::iterator iter =\n        mLocals.find (Misc::StringUtils::lowerCase (script));\n\n    if (iter!=mLocals.end())\n    {\n        mLocals.erase (iter);\n        mIdsUpdated = false;\n        return true;\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/scriptcontext.hpp",
    "content": "#ifndef CSM_WORLD_SCRIPTCONTEXT_H\n#define CSM_WORLD_SCRIPTCONTEXT_H\n\n#include <string>\n#include <vector>\n#include <map>\n\n#include <components/compiler/context.hpp>\n#include <components/compiler/locals.hpp>\n\nnamespace CSMWorld\n{\n    class Data;\n\n    class ScriptContext : public Compiler::Context\n    {\n            const Data& mData;\n            mutable std::vector<std::string> mIds;\n            mutable bool mIdsUpdated;\n            mutable std::map<std::string, Compiler::Locals> mLocals;\n\n        public:\n\n            ScriptContext (const Data& data);\n\n            bool canDeclareLocals() const override;\n            ///< Is the compiler allowed to declare local variables?\n\n            char getGlobalType (const std::string& name) const override;\n            ///< 'l: long, 's': short, 'f': float, ' ': does not exist.\n\n            std::pair<char, bool> getMemberType (const std::string& name,\n                const std::string& id) const override;\n            ///< Return type of member variable \\a name in script \\a id or in script of reference of\n            /// \\a id\n            /// \\return first: 'l: long, 's': short, 'f': float, ' ': does not exist.\n            /// second: true: script of reference\n\n            bool isId (const std::string& name) const override;\n            ///< Does \\a name match an ID, that can be referenced?\n\n            bool isJournalId (const std::string& name) const override;\n            ///< Does \\a name match a journal ID?\n\n            void invalidateIds();\n\n            void clear();\n            ///< Remove all cached data.\n\n            /// \\return Were there any locals that needed clearing?\n            bool clearLocals (const std::string& script);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/subcellcollection.hpp",
    "content": "#ifndef CSM_WOLRD_SUBCOLLECTION_H\n#define CSM_WOLRD_SUBCOLLECTION_H\n\n#include \"nestedidcollection.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace CSMWorld\n{\n    struct Cell;\n    template<typename T, typename AT>\n    class IdCollection;\n\n    /// \\brief Single type collection of top level records that are associated with cells\n    template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >\n    class SubCellCollection : public NestedIdCollection<ESXRecordT, IdAccessorT>\n    {\n            const IdCollection<Cell>& mCells;\n\n            void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted) override;\n\n        public:\n\n            SubCellCollection (const IdCollection<Cell>& cells);\n    };\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    void SubCellCollection<ESXRecordT, IdAccessorT>::loadRecord (ESXRecordT& record,\n                                                                 ESM::ESMReader& reader,\n                                                                 bool& isDeleted)\n    {\n        record.load (reader, isDeleted, mCells);\n    }\n\n    template<typename ESXRecordT, typename IdAccessorT>\n    SubCellCollection<ESXRecordT, IdAccessorT>::SubCellCollection (\n        const IdCollection<Cell>& cells)\n    : mCells (cells)\n    {}\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/model/world/tablemimedata.cpp",
    "content": "#include \"tablemimedata.hpp\"\n\n#include <string>\n\n#include <QDebug>\n\n#include \"universalid.hpp\"\n#include \"columnbase.hpp\"\n\nCSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) :\nmDocument(document)\n{\n    mUniversalId.push_back (id);\n    mObjectsFormats << QString::fromUtf8 ((\"tabledata/\" + id.getTypeName()).c_str());\n}\n\nCSMWorld::TableMimeData::TableMimeData (const std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) :\n    mUniversalId (id), mDocument(document)\n{\n    for (std::vector<UniversalId>::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it)\n    {\n        mObjectsFormats << QString::fromUtf8 ((\"tabledata/\" + it->getTypeName()).c_str());\n    }\n}\n\nQStringList CSMWorld::TableMimeData::formats() const\n{\n    return mObjectsFormats;\n}\n\nCSMWorld::TableMimeData::~TableMimeData()\n{\n}\n\nstd::string CSMWorld::TableMimeData::getIcon() const\n{\n    if (mUniversalId.empty())\n    {\n        qDebug()<<\"TableMimeData object does not hold any records!\"; //because throwing in the event loop tends to be problematic\n        throw std::runtime_error (\"TableMimeData object does not hold any records!\");\n    }\n\n    std::string tmpIcon;\n    bool firstIteration = true;\n\n    for (unsigned i = 0; i < mUniversalId.size(); ++i)\n    {\n        if (firstIteration)\n        {\n            firstIteration = false;\n            tmpIcon = mUniversalId[i].getIcon();\n            continue;\n        }\n\n        if (tmpIcon != mUniversalId[i].getIcon())\n        {\n            return \":/multitype.png\"; //icon stolen from gnome TODO: get new icon\n        }\n\n        tmpIcon = mUniversalId[i].getIcon();\n    }\n\n    return mUniversalId.begin()->getIcon(); //All objects are of the same type;\n}\n\nstd::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const\n{\n    return mUniversalId;\n}\n\nbool CSMWorld::TableMimeData::isReferencable(CSMWorld::ColumnBase::Display type) const\n{\nreturn (  type == CSMWorld::ColumnBase::Display_Activator\n       || type == CSMWorld::ColumnBase::Display_Potion\n       || type == CSMWorld::ColumnBase::Display_Apparatus\n       || type == CSMWorld::ColumnBase::Display_Armor\n       || type == CSMWorld::ColumnBase::Display_Book\n       || type == CSMWorld::ColumnBase::Display_Clothing\n       || type == CSMWorld::ColumnBase::Display_Container\n       || type == CSMWorld::ColumnBase::Display_Creature\n       || type == CSMWorld::ColumnBase::Display_Door\n       || type == CSMWorld::ColumnBase::Display_Ingredient\n       || type == CSMWorld::ColumnBase::Display_CreatureLevelledList\n       || type == CSMWorld::ColumnBase::Display_ItemLevelledList\n       || type == CSMWorld::ColumnBase::Display_Light\n       || type == CSMWorld::ColumnBase::Display_Lockpick\n       || type == CSMWorld::ColumnBase::Display_Miscellaneous\n       || type == CSMWorld::ColumnBase::Display_Npc\n       || type == CSMWorld::ColumnBase::Display_Probe\n       || type == CSMWorld::ColumnBase::Display_Repair\n       || type == CSMWorld::ColumnBase::Display_Static\n       || type == CSMWorld::ColumnBase::Display_Weapon);\n}\nbool CSMWorld::TableMimeData::isReferencable(CSMWorld::UniversalId::Type type)\n{\n     return (  type == CSMWorld::UniversalId::Type_Activator\n            || type == CSMWorld::UniversalId::Type_Potion\n            || type == CSMWorld::UniversalId::Type_Apparatus\n            || type == CSMWorld::UniversalId::Type_Armor\n            || type == CSMWorld::UniversalId::Type_Book\n            || type == CSMWorld::UniversalId::Type_Clothing\n            || type == CSMWorld::UniversalId::Type_Container\n            || type == CSMWorld::UniversalId::Type_Creature\n            || type == CSMWorld::UniversalId::Type_Door\n            || type == CSMWorld::UniversalId::Type_Ingredient\n            || type == CSMWorld::UniversalId::Type_CreatureLevelledList\n            || type == CSMWorld::UniversalId::Type_ItemLevelledList\n            || type == CSMWorld::UniversalId::Type_Light\n            || type == CSMWorld::UniversalId::Type_Lockpick\n            || type == CSMWorld::UniversalId::Type_Miscellaneous\n            || type == CSMWorld::UniversalId::Type_Npc\n            || type == CSMWorld::UniversalId::Type_Probe\n            || type == CSMWorld::UniversalId::Type_Repair\n            || type == CSMWorld::UniversalId::Type_Static\n            || type == CSMWorld::UniversalId::Type_Weapon);\n}\n\nbool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const\n{\n    bool referencable = (type == CSMWorld::UniversalId::Type_Referenceable);\n    for (std::vector<UniversalId>::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it)\n    {\n        if (referencable)\n        {\n            if (isReferencable(it->getType()))\n            {\n                return true;\n            }\n        } else {\n        if (it->getType() == type)\n            {\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\nbool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const\n{\n    bool referencable = (type == CSMWorld::ColumnBase::Display_Referenceable);\n    for (std::vector<UniversalId>::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it)\n    {\n        if (referencable)\n        {\n            if (isReferencable(it->getType()))\n            {\n                return true;\n            }\n        } else {\n        if (it->getType() == convertEnums (type))\n            {\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\nCSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const\n{\n    bool referencable = (type == CSMWorld::UniversalId::Type_Referenceable);\n    for (std::vector<UniversalId>::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it)\n    {\n        if (referencable)\n        {\n            if (isReferencable(it->getType()))\n            {\n                return *it;\n            }\n        } else\n        {\n            if (it->getType() == type)\n            {\n                return *it;\n            }\n        }\n    }\n\n    throw std::runtime_error (\"TableMimeData object does not hold object of the sought type\");\n}\n\nCSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const\n{\n    bool referencable = (type == CSMWorld::ColumnBase::Display_Referenceable);\n    for (std::vector<UniversalId>::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it)\n    {\n        if (referencable)\n        {\n            if (isReferencable(it->getType()))\n            {\n                return *it;\n            }\n        } else {\n            if (it->getType() == convertEnums (type))\n            {\n                return *it;\n            }\n        }\n    }\n\n    throw std::runtime_error (\"TableMimeData object does not hold object of the sought type\");\n}\n\nbool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const\n{\n    return &document == &mDocument;\n}\n\nnamespace\n{\n    struct Mapping\n    {\n        CSMWorld::UniversalId::Type mUniversalIdType;\n        CSMWorld::ColumnBase::Display mDisplayType;\n    };\n\n    const Mapping mapping[] =\n    {\n        { CSMWorld::UniversalId::Type_Race, CSMWorld::ColumnBase::Display_Race },\n        { CSMWorld::UniversalId::Type_Skill, CSMWorld::ColumnBase::Display_Skill },\n        { CSMWorld::UniversalId::Type_Class, CSMWorld::ColumnBase::Display_Class },\n        { CSMWorld::UniversalId::Type_Faction, CSMWorld::ColumnBase::Display_Faction },\n        { CSMWorld::UniversalId::Type_Sound, CSMWorld::ColumnBase::Display_Sound },\n        { CSMWorld::UniversalId::Type_Region, CSMWorld::ColumnBase::Display_Region },\n        { CSMWorld::UniversalId::Type_Birthsign, CSMWorld::ColumnBase::Display_Birthsign },\n        { CSMWorld::UniversalId::Type_Spell, CSMWorld::ColumnBase::Display_Spell },\n        { CSMWorld::UniversalId::Type_Cell, CSMWorld::ColumnBase::Display_Cell },\n        { CSMWorld::UniversalId::Type_Referenceable, CSMWorld::ColumnBase::Display_Referenceable },\n        { CSMWorld::UniversalId::Type_Activator, CSMWorld::ColumnBase::Display_Activator },\n        { CSMWorld::UniversalId::Type_Potion, CSMWorld::ColumnBase::Display_Potion },\n        { CSMWorld::UniversalId::Type_Apparatus, CSMWorld::ColumnBase::Display_Apparatus },\n        { CSMWorld::UniversalId::Type_Armor, CSMWorld::ColumnBase::Display_Armor },\n        { CSMWorld::UniversalId::Type_Book, CSMWorld::ColumnBase::Display_Book },\n        { CSMWorld::UniversalId::Type_Clothing, CSMWorld::ColumnBase::Display_Clothing },\n        { CSMWorld::UniversalId::Type_Container, CSMWorld::ColumnBase::Display_Container },\n        { CSMWorld::UniversalId::Type_Creature, CSMWorld::ColumnBase::Display_Creature },\n        { CSMWorld::UniversalId::Type_Door, CSMWorld::ColumnBase::Display_Door },\n        { CSMWorld::UniversalId::Type_Ingredient, CSMWorld::ColumnBase::Display_Ingredient },\n        { CSMWorld::UniversalId::Type_CreatureLevelledList, CSMWorld::ColumnBase::Display_CreatureLevelledList },\n        { CSMWorld::UniversalId::Type_ItemLevelledList, CSMWorld::ColumnBase::Display_ItemLevelledList },\n        { CSMWorld::UniversalId::Type_Light, CSMWorld::ColumnBase::Display_Light },\n        { CSMWorld::UniversalId::Type_Lockpick, CSMWorld::ColumnBase::Display_Lockpick },\n        { CSMWorld::UniversalId::Type_Miscellaneous, CSMWorld::ColumnBase::Display_Miscellaneous },\n        { CSMWorld::UniversalId::Type_Npc, CSMWorld::ColumnBase::Display_Npc },\n        { CSMWorld::UniversalId::Type_Probe, CSMWorld::ColumnBase::Display_Probe },\n        { CSMWorld::UniversalId::Type_Repair, CSMWorld::ColumnBase::Display_Repair },\n        { CSMWorld::UniversalId::Type_Static, CSMWorld::ColumnBase::Display_Static },\n        { CSMWorld::UniversalId::Type_Weapon, CSMWorld::ColumnBase::Display_Weapon },\n        { CSMWorld::UniversalId::Type_Reference, CSMWorld::ColumnBase::Display_Reference },\n        { CSMWorld::UniversalId::Type_Filter, CSMWorld::ColumnBase::Display_Filter },\n        { CSMWorld::UniversalId::Type_Topic, CSMWorld::ColumnBase::Display_Topic },\n        { CSMWorld::UniversalId::Type_Journal, CSMWorld::ColumnBase::Display_Journal },\n        { CSMWorld::UniversalId::Type_TopicInfo, CSMWorld::ColumnBase::Display_TopicInfo },\n        { CSMWorld::UniversalId::Type_JournalInfo, CSMWorld::ColumnBase::Display_JournalInfo },\n        { CSMWorld::UniversalId::Type_Scene, CSMWorld::ColumnBase::Display_Scene },\n        { CSMWorld::UniversalId::Type_Script, CSMWorld::ColumnBase::Display_Script },\n        { CSMWorld::UniversalId::Type_Mesh, CSMWorld::ColumnBase::Display_Mesh },\n        { CSMWorld::UniversalId::Type_Icon, CSMWorld::ColumnBase::Display_Icon },\n        { CSMWorld::UniversalId::Type_Music, CSMWorld::ColumnBase::Display_Music },\n        { CSMWorld::UniversalId::Type_SoundRes, CSMWorld::ColumnBase::Display_SoundRes },\n        { CSMWorld::UniversalId::Type_Texture, CSMWorld::ColumnBase::Display_Texture },\n        { CSMWorld::UniversalId::Type_Video, CSMWorld::ColumnBase::Display_Video },\n        { CSMWorld::UniversalId::Type_Global, CSMWorld::ColumnBase::Display_GlobalVariable },\n        { CSMWorld::UniversalId::Type_BodyPart, CSMWorld::ColumnBase::Display_BodyPart },\n        { CSMWorld::UniversalId::Type_Enchantment, CSMWorld::ColumnBase::Display_Enchantment },\n\n        { CSMWorld::UniversalId::Type_None, CSMWorld::ColumnBase::Display_None } // end marker\n    };\n}\n\nCSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (ColumnBase::Display type)\n{\n    for (int i=0; mapping[i].mUniversalIdType!=CSMWorld::UniversalId::Type_None; ++i)\n        if (mapping[i].mDisplayType==type)\n            return mapping[i].mUniversalIdType;\n\n    return CSMWorld::UniversalId::Type_None;\n}\n\nCSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (UniversalId::Type type)\n{\n    for (int i=0; mapping[i].mUniversalIdType!=CSMWorld::UniversalId::Type_None; ++i)\n        if (mapping[i].mUniversalIdType==type)\n            return mapping[i].mDisplayType;\n\n    return CSMWorld::ColumnBase::Display_None;\n}\n\nconst CSMDoc::Document* CSMWorld::TableMimeData::getDocumentPtr() const\n{\n    return &mDocument;\n}\n"
  },
  {
    "path": "apps/opencs/model/world/tablemimedata.hpp",
    "content": "#ifndef TABLEMIMEDATA_H\n#define TABLEMIMEDATA_H\n\n#include <vector>\n\n#include <QtCore/QMimeData>\n#include <QStringList>\n\n#include \"universalid.hpp\"\n#include \"columnbase.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n\n/// \\brief Subclass of QmimeData, augmented to contain and transport UniversalIds.\n///\n/// This class provides way to construct mimedata object holding the universalid copy\n/// Universalid is used in the majority of the tables to store type, id, argument types.\n/// This way universalid grants a way to retrieve record from the concrete table.\n/// Please note, that tablemimedata object can hold multiple universalIds in the vector.\n\n    class TableMimeData : public QMimeData\n    {\n            std::vector<UniversalId> mUniversalId;\n            QStringList mObjectsFormats;\n            const CSMDoc::Document& mDocument;\n        public:\n            TableMimeData(UniversalId id, const CSMDoc::Document& document);\n\n            TableMimeData(const std::vector<UniversalId>& id, const CSMDoc::Document& document);\n\n            ~TableMimeData();\n\n            QStringList formats() const override;\n\n            std::string getIcon() const;\n\n            std::vector<UniversalId> getData() const;\n\n            bool holdsType(UniversalId::Type type) const;\n\n            bool holdsType(CSMWorld::ColumnBase::Display type) const;\n\n            bool fromDocument(const CSMDoc::Document& document) const;\n\n            UniversalId returnMatching(UniversalId::Type type) const;\n\n            const CSMDoc::Document* getDocumentPtr() const;\n\n            UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const;\n\n            static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type);\n\n            static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type);\n\n            static bool isReferencable(CSMWorld::UniversalId::Type type);\n        private:\n            bool isReferencable(CSMWorld::ColumnBase::Display type) const;\n\n    };\n}\n#endif // TABLEMIMEDATA_H\n"
  },
  {
    "path": "apps/opencs/model/world/universalid.cpp",
    "content": "#include \"universalid.hpp\"\n\n#include <ostream>\n#include <stdexcept>\n#include <sstream>\n#include <iostream>\n\nnamespace\n{\n    struct TypeData\n    {\n            CSMWorld::UniversalId::Class mClass;\n            CSMWorld::UniversalId::Type mType;\n            const char *mName;\n            const char *mIcon;\n    };\n\n    static const TypeData sNoArg[] =\n    {\n        { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, \"-\", 0 },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, \"Global Variables\", \":./global-variable.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, \"Game Settings\", \":./gmst.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, \"Skills\", \":./skill.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, \"Classes\", \":./class.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, \"Factions\", \":./faction.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, \"Races\", \":./race.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, \"Sounds\", \":./sound.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, \"Scripts\", \":./script.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, \"Regions\", \":./region.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, \"Birthsigns\", \":./birthsign.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, \"Spells\", \":./spell.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, \"Topics\", \":./dialogue-topics.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, \"Journals\", \":./journal-topics.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, \"Topic Infos\", \":./dialogue-topic-infos.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, \"Journal Infos\", \":./journal-topic-infos.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, \"Cells\", \":./cell.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, \"Enchantments\", \":./enchantment.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, \"Body Parts\", \":./body-part.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, \"Objects\", \":./object.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, \"Instances\", \":./instance.png\" },\n        { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, \"Region Map\", \":./region-map.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, \"Filters\", \":./filter.png\" },\n        { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, \"Meshes\", \":./resources-mesh\" },\n        { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, \"Icons\", \":./resources-icon\" },\n        { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, \"Music Files\", \":./resources-music\" },\n        { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, \"Sound Files\", \":resources-sound\" },\n        { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, \"Textures\", \":./resources-texture\" },\n        { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, \"Videos\", \":./resources-video\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, \"Debug Profiles\", \":./debug-profile.png\" },\n        { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, \"Run Log\", \":./run-log.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, \"Sound Generators\", \":./sound-generator.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, \"Magic Effects\", \":./magic-effect.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, \"Lands\", \":./land-heightmap.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, \"Land Textures\", \":./land-texture.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, \"Pathgrids\", \":./pathgrid.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, \"Start Scripts\", \":./start-script.png\" },\n        { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, \"Metadata\", \":./metadata.png\" },\n        { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker\n    };\n\n    static const TypeData sIdArg[] =\n    {\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, \"Global Variable\", \":./global-variable.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, \"Game Setting\", \":./gmst.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, \"Skill\", \":./skill.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, \"Class\", \":./class.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, \"Faction\", \":./faction.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, \"Race\", \":./race.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, \"Sound\", \":./sound.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, \"Script\", \":./script.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, \"Region\", \":./region.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, \"Birthsign\", \":./birthsign.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, \"Spell\", \":./spell.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, \"Topic\", \":./dialogue-topics.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, \"Journal\", \":./journal-topics.png\" },\n        { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_TopicInfo, \"TopicInfo\", \":./dialogue-topic-infos.png\" },\n        { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, \"JournalInfo\", \":./journal-topic-infos.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, \"Cell\", \":./cell.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, \"Cell\", \":./cell.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, \"Object\", \":./object.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, \"Activator\", \":./activator.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, \"Potion\", \":./potion.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, \"Apparatus\", \":./apparatus.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, \"Armor\", \":./armor.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, \"Book\", \":./book.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, \"Clothing\", \":./clothing.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, \"Container\", \":./container.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, \"Creature\", \":./creature.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, \"Door\", \":./door.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, \"Ingredient\", \":./ingredient.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList,\n            \"Creature Levelled List\", \":./levelled-creature.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList,\n            \"Item Levelled List\", \":./levelled-item.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, \"Light\", \":./light.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, \"Lockpick\", \":./lockpick.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous,\n            \"Miscellaneous\", \":./miscellaneous.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, \"NPC\", \":./npc.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, \"Probe\", \":./probe.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, \"Repair\", \":./repair.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, \"Static\", \":./static.png\" },\n        { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, \"Weapon\", \":./weapon.png\" },\n        { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, \"Instance\", \":./instance.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, \"Filter\", \":./filter.png\" },\n        { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, \"Scene\", \":./scene.png\" },\n        { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, \"Preview\", \":./record-preview.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, \"Enchantment\", \":./enchantment.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, \"Body Part\", \":./body-part.png\" },\n        { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, \"Mesh\", \":./resources-mesh\"},\n        { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, \"Icon\", \":./resources-icon\"},\n        { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, \"Music\", \":./resources-music\" },\n        { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, \"Sound File\", \":./resources-sound\" },\n        { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, \"Texture\", \":./resources-texture\" },\n        { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, \"Video\", \":./resources-video\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, \"Debug Profile\", \":./debug-profile.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, \"Sound Generator\", \":./sound-generator.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, \"Magic Effect\", \":./magic-effect.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, \"Land\", \":./land-heightmap.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, \"Land Texture\", \":./land-texture.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, \"Pathgrid\", \":./pathgrid.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, \"Start Script\", \":./start-script.png\" },\n        { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, \"Metadata\", \":./metadata.png\" },\n\n        { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker\n    };\n\n    static const TypeData sIndexArg[] =\n    {\n        { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, \"Verification Results\", \":./menu-verify.png\" },\n        { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, \"Load Error Log\", \":./error-log.png\" },\n        { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, \"Global Search\", \":./menu-search.png\" },\n        { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker\n    };\n}\n\nCSMWorld::UniversalId::UniversalId (const std::string& universalId)\n: mIndex(0)\n{\n    std::string::size_type index = universalId.find (':');\n\n    if (index!=std::string::npos)\n    {\n        std::string type = universalId.substr (0, index);\n\n        for (int i=0; sIdArg[i].mName; ++i)\n            if (type==sIdArg[i].mName)\n            {\n                mArgumentType = ArgumentType_Id;\n                mType = sIdArg[i].mType;\n                mClass = sIdArg[i].mClass;\n                mId = universalId.substr (index+2);\n                return;\n            }\n\n        for (int i=0; sIndexArg[i].mName; ++i)\n            if (type==sIndexArg[i].mName)\n            {\n                mArgumentType = ArgumentType_Index;\n                mType = sIndexArg[i].mType;\n                mClass = sIndexArg[i].mClass;\n\n                std::istringstream stream (universalId.substr (index+2));\n\n                if (stream >> mIndex)\n                    return;\n\n                break;\n            }\n    }\n    else\n    {\n        for (int i=0; sNoArg[i].mName; ++i)\n            if (universalId==sNoArg[i].mName)\n            {\n                mArgumentType = ArgumentType_None;\n                mType = sNoArg[i].mType;\n                mClass = sNoArg[i].mClass;\n                return;\n            }\n    }\n\n    throw std::runtime_error (\"invalid UniversalId: \" + universalId);\n}\n\nCSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_None), mType (type), mIndex (0)\n{\n    for (int i=0; sNoArg[i].mName; ++i)\n        if (type==sNoArg[i].mType)\n        {\n            mClass = sNoArg[i].mClass;\n            return;\n        }\n\n    for (int i=0; sIdArg[i].mName; ++i)\n        if (type==sIdArg[i].mType)\n        {\n            mArgumentType = ArgumentType_Id;\n            mClass = sIdArg[i].mClass;\n            return;\n        }\n\n    for (int i=0; sIndexArg[i].mName; ++i)\n        if (type==sIndexArg[i].mType)\n        {\n            mArgumentType = ArgumentType_Index;\n            mClass = sIndexArg[i].mClass;\n            return;\n        }\n\n    throw std::logic_error (\"invalid argument-less UniversalId type\");\n}\n\nCSMWorld::UniversalId::UniversalId (Type type, const std::string& id)\n: mArgumentType (ArgumentType_Id), mType (type), mId (id), mIndex (0)\n{\n    for (int i=0; sIdArg[i].mName; ++i)\n        if (type==sIdArg[i].mType)\n        {\n            mClass = sIdArg[i].mClass;\n            return;\n        }\n    throw std::logic_error (\"invalid ID argument UniversalId type\");\n}\n\nCSMWorld::UniversalId::UniversalId (Type type, int index)\n: mArgumentType (ArgumentType_Index), mType (type), mIndex (index)\n{\n    for (int i=0; sIndexArg[i].mName; ++i)\n        if (type==sIndexArg[i].mType)\n        {\n            mClass = sIndexArg[i].mClass;\n            return;\n        }\n\n    throw std::logic_error (\"invalid index argument UniversalId type\");\n}\n\nCSMWorld::UniversalId::Class CSMWorld::UniversalId::getClass() const\n{\n    return mClass;\n}\n\nCSMWorld::UniversalId::ArgumentType CSMWorld::UniversalId::getArgumentType() const\n{\n    return mArgumentType;\n}\n\nCSMWorld::UniversalId::Type CSMWorld::UniversalId::getType() const\n{\n    return mType;\n}\n\nconst std::string& CSMWorld::UniversalId::getId() const\n{\n    if (mArgumentType!=ArgumentType_Id)\n        throw std::logic_error (\"invalid access to ID of non-ID UniversalId\");\n\n    return mId;\n}\n\nint CSMWorld::UniversalId::getIndex() const\n{\n    if (mArgumentType!=ArgumentType_Index)\n        throw std::logic_error (\"invalid access to index of non-index UniversalId\");\n\n    return mIndex;\n}\n\nbool CSMWorld::UniversalId::isEqual (const UniversalId& universalId) const\n{\n    if (mClass!=universalId.mClass || mArgumentType!=universalId.mArgumentType || mType!=universalId.mType)\n            return false;\n\n    switch (mArgumentType)\n    {\n        case ArgumentType_Id: return mId==universalId.mId;\n        case ArgumentType_Index: return mIndex==universalId.mIndex;\n\n        default: return true;\n    }\n}\n\nbool CSMWorld::UniversalId::isLess (const UniversalId& universalId) const\n{\n    if (mType<universalId.mType)\n        return true;\n\n    if (mType>universalId.mType)\n        return false;\n\n    switch (mArgumentType)\n    {\n        case ArgumentType_Id: return mId<universalId.mId;\n        case ArgumentType_Index: return mIndex<universalId.mIndex;\n\n        default: return false;\n    }\n}\n\nstd::string CSMWorld::UniversalId::getTypeName() const\n{\n    const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg :\n        (mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg);\n\n    for (int i=0; typeData[i].mName; ++i)\n        if (typeData[i].mType==mType)\n            return typeData[i].mName;\n\n    throw std::logic_error (\"failed to retrieve UniversalId type name\");\n}\n\nstd::string CSMWorld::UniversalId::toString() const\n{\n    std::ostringstream stream;\n\n    stream << getTypeName();\n\n    switch (mArgumentType)\n    {\n        case ArgumentType_None: break;\n        case ArgumentType_Id: stream << \": \" << mId; break;\n        case ArgumentType_Index: stream << \": \" << mIndex; break;\n    }\n\n    return stream.str();\n}\n\nstd::string CSMWorld::UniversalId::getIcon() const\n{\n    const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg :\n        (mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg);\n\n    for (int i=0; typeData[i].mName; ++i)\n        if (typeData[i].mType==mType)\n            return typeData[i].mIcon ? typeData[i].mIcon : \":placeholder\";\n\n    throw std::logic_error (\"failed to retrieve UniversalId type icon\");\n}\n\nstd::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceableTypes()\n{\n    std::vector<CSMWorld::UniversalId::Type> list;\n\n    for (int i=0; sIdArg[i].mName; ++i)\n        if (sIdArg[i].mClass==Class_RefRecord)\n            list.push_back (sIdArg[i].mType);\n\n    return list;\n}\n\nstd::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listTypes (int classes)\n{\n    std::vector<CSMWorld::UniversalId::Type> list;\n\n    for (int i=0; sNoArg[i].mName; ++i)\n        if (sNoArg[i].mClass & classes)\n            list.push_back (sNoArg[i].mType);\n\n    for (int i=0; sIdArg[i].mName; ++i)\n        if (sIdArg[i].mClass & classes)\n            list.push_back (sIdArg[i].mType);\n\n    for (int i=0; sIndexArg[i].mName; ++i)\n        if (sIndexArg[i].mClass & classes)\n            list.push_back (sIndexArg[i].mType);\n\n    return list;\n}\n\nCSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type)\n{\n    for (int i=0; sIdArg[i].mType; ++i)\n        if (type==sIdArg[i].mType)\n        {\n            if (sIdArg[i].mClass==Class_RefRecord)\n                return Type_Referenceables;\n\n            if (sIdArg[i].mClass==Class_SubRecord || sIdArg[i].mClass==Class_Record ||\n                sIdArg[i].mClass==Class_Resource)\n            {\n                if (type==Type_Cell_Missing)\n                    return Type_Cells;\n\n                return static_cast<Type> (type-1);\n            }\n\n            break;\n        }\n\n    return Type_None;\n}\n\nbool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)\n{\n    return left.isEqual (right);\n}\n\nbool CSMWorld::operator!= (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)\n{\n    return !left.isEqual (right);\n}\n\nbool CSMWorld::operator< (const UniversalId& left, const UniversalId& right)\n{\n    return left.isLess (right);\n}\n\nstd::ostream& CSMWorld::operator< (std::ostream& stream, const CSMWorld::UniversalId& universalId)\n{\n    return stream << universalId.toString();\n}\n"
  },
  {
    "path": "apps/opencs/model/world/universalid.hpp",
    "content": "#ifndef CSM_WOLRD_UNIVERSALID_H\n#define CSM_WOLRD_UNIVERSALID_H\n\n#include <string>\n#include <iosfwd>\n#include <vector>\n\n#include <QMetaType>\n\nnamespace CSMWorld\n{\n    class UniversalId\n    {\n        public:\n\n            enum Class\n            {\n                Class_None = 0,\n                Class_Record = 1,\n                Class_RefRecord = 2, // referenceable record\n                Class_SubRecord = 4,\n                Class_RecordList = 8,\n                Class_Collection = 16, // multiple types of records combined\n                Class_Transient = 32, // not part of the world data or the project data\n                Class_NonRecord = 64, // record like data that is not part of the world\n                Class_Resource = 128, ///< \\attention Resource IDs are unique only within the\n                                /// respective collection\n                Class_ResourceList = 256\n            };\n\n            enum ArgumentType\n            {\n                ArgumentType_None,\n                ArgumentType_Id,\n                ArgumentType_Index\n            };\n\n            /// \\note A record list type must always be immediately followed by the matching\n            /// record type, if this type is of class SubRecord or Record.\n            enum Type\n            {\n                Type_None = 0,\n                Type_Globals,\n                Type_Global,\n                Type_VerificationResults,\n                Type_Gmsts,\n                Type_Gmst,\n                Type_Skills,\n                Type_Skill,\n                Type_Classes,\n                Type_Class,\n                Type_Factions,\n                Type_Faction,\n                Type_Races,\n                Type_Race,\n                Type_Sounds,\n                Type_Sound,\n                Type_Scripts,\n                Type_Script,\n                Type_Regions,\n                Type_Region,\n                Type_Birthsigns,\n                Type_Birthsign,\n                Type_Spells,\n                Type_Spell,\n                Type_Cells,\n                Type_Cell,\n                Type_Cell_Missing, //For cells that does not exist yet.\n                Type_Referenceables,\n                Type_Referenceable,\n                Type_Activator,\n                Type_Potion,\n                Type_Apparatus,\n                Type_Armor,\n                Type_Book,\n                Type_Clothing,\n                Type_Container,\n                Type_Creature,\n                Type_Door,\n                Type_Ingredient,\n                Type_CreatureLevelledList,\n                Type_ItemLevelledList,\n                Type_Light,\n                Type_Lockpick,\n                Type_Miscellaneous,\n                Type_Npc,\n                Type_Probe,\n                Type_Repair,\n                Type_Static,\n                Type_Weapon,\n                Type_References,\n                Type_Reference,\n                Type_RegionMap,\n                Type_Filters,\n                Type_Filter,\n                Type_Topics,\n                Type_Topic,\n                Type_Journals,\n                Type_Journal,\n                Type_TopicInfos,\n                Type_TopicInfo,\n                Type_JournalInfos,\n                Type_JournalInfo,\n                Type_Scene,\n                Type_Preview,\n                Type_LoadErrorLog,\n                Type_Enchantments,\n                Type_Enchantment,\n                Type_BodyParts,\n                Type_BodyPart,\n                Type_Meshes,\n                Type_Mesh,\n                Type_Icons,\n                Type_Icon,\n                Type_Musics,\n                Type_Music,\n                Type_SoundsRes,\n                Type_SoundRes,\n                Type_Textures,\n                Type_Texture,\n                Type_Videos,\n                Type_Video,\n                Type_DebugProfiles,\n                Type_DebugProfile,\n                Type_SoundGens,\n                Type_SoundGen,\n                Type_MagicEffects,\n                Type_MagicEffect,\n                Type_Lands,\n                Type_Land,\n                Type_LandTextures,\n                Type_LandTexture,\n                Type_Pathgrids,\n                Type_Pathgrid,\n                Type_StartScripts,\n                Type_StartScript,\n                Type_Search,\n                Type_MetaDatas,\n                Type_MetaData,\n                Type_RunLog\n            };\n\n            enum { NumberOfTypes = Type_RunLog+1 };\n\n        private:\n\n            Class mClass;\n            ArgumentType mArgumentType;\n            Type mType;\n            std::string mId;\n            int mIndex;\n\n        public:\n\n            UniversalId (const std::string& universalId);\n\n            UniversalId (Type type = Type_None);\n\n            UniversalId (Type type, const std::string& id);\n            ///< Using a type for a non-ID-argument UniversalId will throw an exception.\n\n            UniversalId (Type type, int index);\n            ///< Using a type for a non-index-argument UniversalId will throw an exception.\n\n            Class getClass() const;\n\n            ArgumentType getArgumentType() const;\n\n            Type getType() const;\n\n            const std::string& getId() const;\n            ///< Calling this function for a non-ID type will throw an exception.\n\n            int getIndex() const;\n            ///< Calling this function for a non-index type will throw an exception.\n\n            bool isEqual (const UniversalId& universalId) const;\n\n            bool isLess (const UniversalId& universalId) const;\n\n            std::string getTypeName() const;\n\n            std::string toString() const;\n\n            std::string getIcon() const;\n            ///< Will return an empty string, if no icon is available.\n\n            static std::vector<Type> listReferenceableTypes();\n\n            static std::vector<Type> listTypes (int classes);\n\n            /// If \\a type is a SubRecord, RefRecord or Record type return the type of the table\n            /// that contains records of type \\a type.\n            /// Otherwise return Type_None.\n            static Type getParentType (Type type);\n    };\n\n    bool operator== (const UniversalId& left, const UniversalId& right);\n    bool operator!= (const UniversalId& left, const UniversalId& right);\n\n    bool operator< (const UniversalId& left, const UniversalId& right);\n\n    std::ostream& operator< (std::ostream& stream, const UniversalId& universalId);\n}\n\nQ_DECLARE_METATYPE (CSMWorld::UniversalId)\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/adjusterwidget.cpp",
    "content": "#include \"adjusterwidget.hpp\"\n\n#include <components/misc/stringops.hpp>\n\n#include <boost/filesystem.hpp>\n\n#include <QHBoxLayout>\n#include <QLabel>\n#include <QStyle>\n\nCSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent)\n    : QWidget (parent), mValid (false), mAction (ContentAction_Undefined)\n{\n    QHBoxLayout *layout = new QHBoxLayout (this);\n\n    mIcon = new QLabel (this);\n\n    layout->addWidget (mIcon, 0);\n\n    mMessage = new QLabel (this);\n    mMessage->setWordWrap (true);\n    mMessage->setSizePolicy (QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Minimum));\n\n    layout->addWidget (mMessage, 1);\n\n    setName (\"\", false);\n\n    setLayout (layout);\n}\n\nvoid CSVDoc::AdjusterWidget::setAction (ContentAction action)\n{\n    mAction = action;\n}\n\nvoid CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData)\n{\n    mLocalData = localData;\n}\n\nboost::filesystem::path CSVDoc::AdjusterWidget::getPath() const\n{\n    if (!mValid)\n        throw std::logic_error (\"invalid content file path\");\n\n    return mResultPath;\n}\n\nbool CSVDoc::AdjusterWidget::isValid() const\n{\n    return mValid;\n}\n\nvoid CSVDoc::AdjusterWidget::setFilenameCheck (bool doCheck)\n{\n    mDoFilenameCheck = doCheck;\n}\n\nvoid CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)\n{\n    QString message;\n\n    mValid = (!name.isEmpty());\n    bool warning = false;\n\n    if (!mValid)\n    {\n        message = \"No name.\";\n    }\n    else\n    {\n        boost::filesystem::path path (name.toUtf8().data());\n\n        std::string extension = Misc::StringUtils::lowerCase(path.extension().string());\n\n        bool isLegacyPath = (extension == \".esm\" ||\n                             extension == \".esp\");\n\n        bool isFilePathChanged = (path.parent_path().string() != mLocalData.string());\n\n        if (isLegacyPath)\n            path.replace_extension (addon ? \".omwaddon\" : \".omwgame\");\n\n        //if the file came from data-local and is not a legacy file to be converted,\n        //don't worry about doing a file check.\n        if (!isFilePathChanged && !isLegacyPath)\n        {\n            // path already points to the local data directory\n            message = QString::fromUtf8 ((\"Will be saved as: \" + path.string()).c_str());\n            mResultPath = path;\n        }\n        //in all other cases, ensure the path points to data-local and do an existing file check\n        else\n        {\n            // path points somewhere else or is a leaf name.\n            if (isFilePathChanged)\n                path = mLocalData / path.filename();\n\n            message = QString::fromUtf8 ((\"Will be saved as: \" + path.string()).c_str());\n            mResultPath = path;\n\n            if (boost::filesystem::exists (path))\n            {\n                /// \\todo add an user setting to make this an error.\n                message += \"<p>A file with the same name already exists. If you continue, it will be overwritten.\";\n                warning = true;\n            }\n        }\n    }\n\n    mMessage->setText (message);\n    mIcon->setPixmap (style()->standardIcon (\n        mValid ? (warning ? QStyle::SP_MessageBoxWarning : QStyle::SP_MessageBoxInformation) : QStyle::SP_MessageBoxCritical).\n        pixmap (QSize (16, 16)));\n\n    emit stateChanged (mValid);\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/adjusterwidget.hpp",
    "content": "#ifndef CSV_DOC_ADJUSTERWIDGET_H\n#define CSV_DOC_ADJUSTERWIDGET_H\n\n#include <boost/filesystem/path.hpp>\n\n#include <QWidget>\n\nclass QLabel;\n\nnamespace CSVDoc\n{\n    enum ContentAction\n    {\n        ContentAction_New,\n        ContentAction_Edit,\n        ContentAction_Undefined\n    };\n\n    class AdjusterWidget : public QWidget\n    {\n            Q_OBJECT\n\n        public:\n\n            boost::filesystem::path mLocalData;\n            QLabel *mMessage;\n            QLabel *mIcon;\n            bool mValid;\n            boost::filesystem::path mResultPath;\n            ContentAction mAction;\n            bool mDoFilenameCheck;\n\n        public:\n\n            AdjusterWidget (QWidget *parent = nullptr);\n\n            void setLocalData (const boost::filesystem::path& localData);\n            void setAction (ContentAction action);\n\n            void setFilenameCheck (bool doCheck);\n            bool isValid() const;\n\n            boost::filesystem::path getPath() const;\n            ///< This function must not be called if there is no valid path.\n\n        public slots:\n\n            void setName (const QString& name, bool addon);\n\n        signals:\n\n            void stateChanged (bool valid);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/filedialog.cpp",
    "content": "#include \"filedialog.hpp\"\n\n#include <QCheckBox>\n#include <QPushButton>\n#include <QDialogButtonBox>\n#include <QSortFilterProxyModel>\n#include <QRegExpValidator>\n#include <QRegExp>\n#include <QSpacerItem>\n#include <QPushButton>\n#include <QLabel>\n#include <QGroupBox>\n\n#include \"components/contentselector/model/esmfile.hpp\"\n#include \"components/contentselector/view/contentselector.hpp\"\n\n#include \"filewidget.hpp\"\n#include \"adjusterwidget.hpp\"\n\nCSVDoc::FileDialog::FileDialog(QWidget *parent) :\n    QDialog(parent), mSelector (nullptr), mAction(ContentAction_Undefined), mFileWidget (nullptr), mAdjusterWidget (nullptr), mDialogBuilt(false)\n{\n    ui.setupUi (this);\n    resize(400, 400);\n\n    setObjectName (\"FileDialog\");\n    mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);\n    mAdjusterWidget = new AdjusterWidget (this);\n}\n\nvoid CSVDoc::FileDialog::addFiles(const QString &path)\n{\n    mSelector->addFiles(path);\n}\n\nvoid CSVDoc::FileDialog::setEncoding(const QString &encoding)\n{\n    mSelector->setEncoding(encoding);\n}\n\nvoid CSVDoc::FileDialog::clearFiles()\n{\n    mSelector->clearFiles();\n}\n\nQStringList CSVDoc::FileDialog::selectedFilePaths()\n{\n    QStringList filePaths;\n\n    for (ContentSelectorModel::EsmFile *file : mSelector->selectedFiles() )\n        filePaths.append(file->filePath());\n\n    return filePaths;\n}\n\nvoid CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData)\n{\n    mAdjusterWidget->setLocalData (localData);\n}\n\nvoid CSVDoc::FileDialog::showDialog (ContentAction action)\n{\n    mAction = action;\n\n    ui.projectGroupBoxLayout->insertWidget (0, mAdjusterWidget);\n\n    switch (mAction)\n    {\n    case ContentAction_New:\n        buildNewFileView();\n        break;\n\n    case ContentAction_Edit:\n        buildOpenFileView();\n        break;\n\n    default:\n        break;\n    }\n\n    mAdjusterWidget->setFilenameCheck (mAction == ContentAction_New);\n\n    if(!mDialogBuilt)\n    {\n        //connections common to both dialog view flavors\n        connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)),\n                 this, SLOT (slotUpdateAcceptButton (int)));\n\n        connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected()));\n        mDialogBuilt = true;\n    }\n\n    show();\n    raise();\n    activateWindow();\n}\n\nvoid CSVDoc::FileDialog::buildNewFileView()\n{\n    setWindowTitle(tr(\"Create a new addon\"));\n\n    QPushButton* createButton = ui.projectButtonBox->button (QDialogButtonBox::Ok);\n    createButton->setText (\"Create\");\n    createButton->setEnabled (false);\n\n    if(!mFileWidget)\n    {\n        mFileWidget = new FileWidget (this);\n\n        mFileWidget->setType (true);\n        mFileWidget->extensionLabelIsVisible(true);\n\n        connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)),\n            mAdjusterWidget, SLOT (setName (const QString&, bool)));\n\n        connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)),\n                this, SLOT (slotUpdateAcceptButton(const QString &, bool)));\n    }\n\n    ui.projectGroupBoxLayout->insertWidget (0, mFileWidget);\n\n    connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile()));\n}\n\nvoid CSVDoc::FileDialog::buildOpenFileView()\n{\n    setWindowTitle(tr(\"Open\"));\n    ui.projectGroupBox->setTitle (QString(\"\"));\n    ui.projectButtonBox->button(QDialogButtonBox::Ok)->setText (\"Open\");\n    if(mSelector->isGamefileSelected())\n        ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (true);\n    else\n        ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false);\n\n    if(!mDialogBuilt)\n    {\n        connect (mSelector, SIGNAL (signalAddonDataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (slotAddonDataChanged(const QModelIndex&, const QModelIndex&)));\n    }\n        connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile()));\n}\n\nvoid CSVDoc::FileDialog::slotAddonDataChanged(const QModelIndex &topleft, const QModelIndex &bottomright)\n{\n    slotUpdateAcceptButton(0);\n}\n\nvoid CSVDoc::FileDialog::slotUpdateAcceptButton(int)\n{\n    QString name = \"\";\n\n    if (mFileWidget && mAction == ContentAction_New)\n        name = mFileWidget->getName();\n\n    slotUpdateAcceptButton (name, true);\n}\n\nvoid CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool)\n{\n    bool success = !mSelector->selectedFiles().empty();\n\n    bool isNew = (mAction == ContentAction_New);\n\n    if (isNew)\n        success = success && !(name.isEmpty());\n    else if (success)\n    {\n        ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back();\n        mAdjusterWidget->setName (file->filePath(), !file->isGameFile());\n    }\n    else\n        mAdjusterWidget->setName (\"\", true);\n\n    ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success);\n}\n\nQString CSVDoc::FileDialog::filename() const\n{\n    if (mAction == ContentAction_New)\n        return \"\";\n\n    return mSelector->currentFile();\n}\n\nvoid CSVDoc::FileDialog::slotRejected()\n{\n    emit rejected();\n    disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile()));\n    disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile()));\n    if(mFileWidget)\n    {\n        delete mFileWidget;\n        mFileWidget = nullptr;\n    }\n    close();\n}\n\nvoid CSVDoc::FileDialog::slotNewFile()\n{\n    emit signalCreateNewFile (mAdjusterWidget->getPath());\n    if(mFileWidget)\n    {\n        delete mFileWidget;\n        mFileWidget = nullptr;\n    }\n    disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile()));\n    close();\n}\n\nvoid CSVDoc::FileDialog::slotOpenFile()\n{\n    ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back();\n\n    mAdjusterWidget->setName (file->filePath(), !file->isGameFile());\n\n    emit signalOpenFiles (mAdjusterWidget->getPath());\n    disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile()));\n    close();\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/filedialog.hpp",
    "content": "#ifndef FILEDIALOG_HPP\n#define FILEDIALOG_HPP\n\n#include <QDialog>\n#include <QModelIndex>\n\n#ifndef Q_MOC_RUN\n\n#include <boost/filesystem/path.hpp>\n#include \"adjusterwidget.hpp\"\n\n#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED\n#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED\nQ_DECLARE_METATYPE (boost::filesystem::path)\n#endif\n\n#endif\n\n#include \"ui_filedialog.h\"\n\nnamespace ContentSelectorView\n{\n    class ContentSelector;\n}\n\nnamespace CSVDoc\n{\n    class FileWidget;\n\n    class FileDialog : public QDialog\n    {\n        Q_OBJECT\n\n    private:\n\n        ContentSelectorView::ContentSelector *mSelector;\n        Ui::FileDialog ui;\n        ContentAction mAction;\n        FileWidget *mFileWidget;\n        AdjusterWidget *mAdjusterWidget;\n        bool mDialogBuilt;\n\n    public:\n\n        explicit FileDialog(QWidget *parent = nullptr);\n        void showDialog (ContentAction action);\n\n        void addFiles (const QString &path);\n        void setEncoding (const QString &encoding);\n        void clearFiles ();\n\n        QString filename() const;\n        QStringList selectedFilePaths();\n\n        void setLocalData (const boost::filesystem::path& localData);\n\n    private:\n\n        void buildNewFileView();\n        void buildOpenFileView();\n\n    signals:\n\n        void signalOpenFiles (const boost::filesystem::path &path);\n        void signalCreateNewFile (const boost::filesystem::path &path);\n\n        void signalUpdateAcceptButton (bool, int);\n\n    private slots:\n\n        void slotNewFile();\n        void slotOpenFile();\n        void slotUpdateAcceptButton (int);\n        void slotUpdateAcceptButton (const QString &, bool);\n        void slotRejected();\n        void slotAddonDataChanged(const QModelIndex& topleft, const QModelIndex& bottomright);\n    };\n}\n#endif // FILEDIALOG_HPP\n"
  },
  {
    "path": "apps/opencs/view/doc/filewidget.cpp",
    "content": "#include \"filewidget.hpp\"\n\n#include <QHBoxLayout>\n#include <QLineEdit>\n#include <QLabel>\n#include <QRegExpValidator>\n#include <QRegExp>\n\nQString CSVDoc::FileWidget::getExtension() const\n{\n    return mAddon ? \".omwaddon\" : \".omwgame\";\n}\n\nCSVDoc::FileWidget::FileWidget (QWidget *parent) : QWidget (parent), mAddon (false)\n{\n    QHBoxLayout *layout = new QHBoxLayout (this);\n\n    mInput = new QLineEdit (this);\n\n    layout->addWidget (mInput, 1);\n\n    mType = new QLabel (this);\n\n    layout ->addWidget (mType);\n\n    connect (mInput, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));\n\n    setLayout (layout);\n}\n\nvoid CSVDoc::FileWidget::setType (bool addon)\n{\n    mAddon = addon;\n\n    mType->setText (getExtension());\n}\n\nQString CSVDoc::FileWidget::getName() const\n{\n    QString text = mInput->text();\n\n    if (text.isEmpty())\n        return \"\";\n\n    return text + getExtension();\n}\n\nvoid CSVDoc::FileWidget::textChanged (const QString& text)\n{\n    emit nameChanged (getName(), mAddon);\n}\n\nvoid CSVDoc::FileWidget::extensionLabelIsVisible(bool visible)\n{\n    mType->setVisible(visible);\n}\n\nvoid CSVDoc::FileWidget::setName (const std::string& text)\n{\n    QString text2 = QString::fromUtf8 (text.c_str());\n\n    mInput->setText (text2);\n    textChanged (text2);\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/filewidget.hpp",
    "content": "#ifndef CSV_DOC_FILEWIDGET_H\n#define CSV_DOC_FILEWIDGET_H\n\n#include <QWidget>\n\n#include <string>\n\nclass QLabel;\nclass QString;\nclass QLineEdit;\n\nnamespace CSVDoc\n{\n    class FileWidget : public QWidget\n    {\n            Q_OBJECT\n\n            bool mAddon;\n            QLineEdit *mInput;\n            QLabel *mType;\n\n            QString getExtension() const;\n\n        public:\n\n            FileWidget (QWidget *parent = nullptr);\n\n            void setType (bool addon);\n\n            QString getName() const;\n\n            void extensionLabelIsVisible(bool visible);\n\n            void setName (const std::string& text);\n\n        private slots:\n\n            void textChanged (const QString& text);\n\n        signals:\n\n            void nameChanged (const QString& file, bool addon);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/globaldebugprofilemenu.cpp",
    "content": "#include \"globaldebugprofilemenu.hpp\"\n\n#include <vector>\n#include <algorithm>\n\n#include <QActionGroup>\n\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/record.hpp\"\n\nvoid CSVDoc::GlobalDebugProfileMenu::rebuild()\n{\n    clear();\n\n    delete mActions;\n    mActions = nullptr;\n\n    int idColumn = mDebugProfiles->findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    int stateColumn = mDebugProfiles->findColumnIndex (CSMWorld::Columns::ColumnId_Modification);\n    int globalColumn = mDebugProfiles->findColumnIndex (\n        CSMWorld::Columns::ColumnId_GlobalProfile);\n\n    int size = mDebugProfiles->rowCount();\n\n    std::vector<QString> ids;\n\n    for (int i=0; i<size; ++i)\n    {\n        int state = mDebugProfiles->data (mDebugProfiles->index (i, stateColumn)).toInt();\n\n        bool global = mDebugProfiles->data (mDebugProfiles->index (i, globalColumn)).toInt();\n\n        if (state!=CSMWorld::RecordBase::State_Deleted && global)\n            ids.push_back (\n                mDebugProfiles->data (mDebugProfiles->index (i, idColumn)).toString());\n    }\n\n    mActions = new QActionGroup (this);\n    connect (mActions, SIGNAL (triggered (QAction *)), this, SLOT (actionTriggered (QAction *)));\n\n    std::sort (ids.begin(), ids.end());\n\n    for (std::vector<QString>::const_iterator iter (ids.begin()); iter!=ids.end(); ++iter)\n    {\n        mActions->addAction (addAction (*iter));\n    }\n}\n\nCSVDoc::GlobalDebugProfileMenu::GlobalDebugProfileMenu (CSMWorld::IdTable *debugProfiles,\n    QWidget *parent)\n: QMenu (parent), mDebugProfiles (debugProfiles), mActions (nullptr)\n{\n    rebuild();\n\n    connect (mDebugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (profileAboutToBeRemoved (const QModelIndex&, int, int)));\n\n    connect (mDebugProfiles, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (profileInserted (const QModelIndex&, int, int)));\n\n    connect (mDebugProfiles, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (profileChanged (const QModelIndex&, const QModelIndex&)));\n}\n\nvoid CSVDoc::GlobalDebugProfileMenu::updateActions (bool running)\n{\n    if (mActions)\n        mActions->setEnabled (!running);\n}\n\nvoid CSVDoc::GlobalDebugProfileMenu::profileAboutToBeRemoved (const QModelIndex& parent,\n    int start, int end)\n{\n    rebuild();\n}\n\nvoid CSVDoc::GlobalDebugProfileMenu::profileInserted (const QModelIndex& parent, int start,\n    int end)\n{\n    rebuild();\n}\n\nvoid CSVDoc::GlobalDebugProfileMenu::profileChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    rebuild();\n}\n\nvoid CSVDoc::GlobalDebugProfileMenu::actionTriggered (QAction *action)\n{\n    emit triggered (std::string (action->text().toUtf8().constData()));\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/globaldebugprofilemenu.hpp",
    "content": "#ifndef CSV_DOC_GLOBALDEBUGPROFILEMENU_H\n#define CSV_DOC_GLOBALDEBUGPROFILEMENU_H\n\n#include <QMenu>\n\nclass QModelIndex;\nclass QActionGroup;\n\nnamespace CSMWorld\n{\n    class IdTable;\n}\n\nnamespace CSVDoc\n{\n    class GlobalDebugProfileMenu : public QMenu\n    {\n            Q_OBJECT\n\n            CSMWorld::IdTable *mDebugProfiles;\n            QActionGroup *mActions;\n\n        private:\n\n            void rebuild();\n\n        public:\n\n            GlobalDebugProfileMenu (CSMWorld::IdTable *debugProfiles, QWidget *parent = nullptr);\n\n            void updateActions (bool running);\n\n        private slots:\n\n            void profileAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void profileInserted (const QModelIndex& parent, int start, int end);\n\n            void profileChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void actionTriggered (QAction *action);\n\n        signals:\n\n            void triggered (const std::string& profile);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/loader.cpp",
    "content": "#include \"loader.hpp\"\n\n#include <QVBoxLayout>\n#include <QLabel>\n#include <QProgressBar>\n#include <QCursor>\n#include <QDialogButtonBox>\n#include <QCloseEvent>\n#include <QListWidget>\n\n#include \"../../model/doc/document.hpp\"\n\nvoid CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)\n{\n    event->ignore();\n    cancel();\n}\n\nCSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)\n: mDocument (document), mAborted (false), mMessages (nullptr), mTotalRecords (0)\n{\n    setWindowTitle (QString::fromUtf8((std::string(\"Opening \") + document->getSavePath().filename().string()).c_str()));\n\n    setMinimumWidth (400);\n\n    mLayout = new QVBoxLayout (this);\n\n    // file progress\n    mFile = new QLabel (this);\n\n    mLayout->addWidget (mFile);\n\n    mFileProgress = new QProgressBar (this);\n\n    mLayout->addWidget (mFileProgress);\n\n    int size = static_cast<int> (document->getContentFiles().size())+1;\n    if (document->isNew())\n        --size;\n\n    mFileProgress->setMinimum (0);\n    mFileProgress->setMaximum (size);\n    mFileProgress->setTextVisible (true);\n    mFileProgress->setValue (0);\n\n    // record progress\n    mLayout->addWidget (mRecords = new QLabel (\"Records\", this));\n\n    mRecordProgress = new QProgressBar (this);\n\n    mLayout->addWidget (mRecordProgress);\n\n    mRecordProgress->setMinimum (0);\n    mRecordProgress->setTextVisible (true);\n    mRecordProgress->setValue (0);\n\n    // error message\n    mError = new QLabel (this);\n    mError->setWordWrap (true);\n\n    mLayout->addWidget (mError);\n\n    // buttons\n    mButtons = new QDialogButtonBox (QDialogButtonBox::Cancel, Qt::Horizontal, this);\n\n    mLayout->addWidget (mButtons);\n\n    setLayout (mLayout);\n\n    move (QCursor::pos());\n\n    show();\n\n    connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));\n}\n\nvoid CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords)\n{\n    mFile->setText (QString::fromUtf8 ((\"Loading: \" + name).c_str()));\n\n    mFileProgress->setValue (mFileProgress->value()+1);\n\n    mRecordProgress->setValue (0);\n    mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1);\n\n    mTotalRecords = totalRecords;\n}\n\nvoid CSVDoc::LoadingDocument::nextRecord (int records)\n{\n    if (records<=mTotalRecords)\n    {\n        mRecordProgress->setValue (records);\n\n        std::ostringstream stream;\n\n        stream << \"Records: \" << records << \" of \" << mTotalRecords;\n\n        mRecords->setText (QString::fromUtf8 (stream.str().c_str()));\n    }\n}\n\nvoid CSVDoc::LoadingDocument::abort (const std::string& error)\n{\n    mAborted = true;\n    mError->setText (QString::fromUtf8 ((\"<font color=red>Loading failed: \" + error + \"</font>\").c_str()));\n    mButtons->setStandardButtons (QDialogButtonBox::Close);\n}\n\nvoid CSVDoc::LoadingDocument::addMessage (const std::string& message)\n{\n    if (!mMessages)\n    {\n        mMessages = new QListWidget (this);\n        mLayout->insertWidget (4, mMessages);\n    }\n\n    new QListWidgetItem (QString::fromUtf8 (message.c_str()), mMessages);\n}\n\nvoid CSVDoc::LoadingDocument::cancel()\n{\n    if (!mAborted)\n        emit cancel (mDocument);\n    else\n    {\n        emit close (mDocument);\n        deleteLater();\n    }\n}\n\n\nCSVDoc::Loader::Loader() {}\n\nCSVDoc::Loader::~Loader()\n{\n    for (std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter (mDocuments.begin());\n        iter!=mDocuments.end(); ++iter)\n        delete iter->second;\n}\n\nvoid CSVDoc::Loader::add (CSMDoc::Document *document)\n{\n    LoadingDocument *loading = new LoadingDocument (document);\n    mDocuments.insert (std::make_pair (document, loading));\n\n    connect (loading, SIGNAL (cancel (CSMDoc::Document *)),\n        this, SIGNAL (cancel (CSMDoc::Document *)));\n    connect (loading, SIGNAL (close (CSMDoc::Document *)),\n        this, SIGNAL (close (CSMDoc::Document *)));\n}\n\nvoid CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed,\n    const std::string& error)\n{\n    std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.begin();\n\n    for (; iter!=mDocuments.end(); ++iter)\n        if (iter->first==document)\n            break;\n\n    if (iter==mDocuments.end())\n        return;\n\n    if (completed || error.empty())\n    {\n        delete iter->second;\n        mDocuments.erase (iter);\n    }\n    else\n    {\n        iter->second->abort (error);\n        // Leave the window open for now (wait for the user to close it)\n        mDocuments.erase (iter);\n    }\n}\n\nvoid CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name,\n    int totalRecords)\n{\n    std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);\n\n    if (iter!=mDocuments.end())\n        iter->second->nextStage (name, totalRecords);\n}\n\nvoid CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records)\n{\n    std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);\n\n    if (iter!=mDocuments.end())\n        iter->second->nextRecord (records);\n}\n\nvoid CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string& message)\n{\n    std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);\n\n    if (iter!=mDocuments.end())\n        iter->second->addMessage (message);\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/loader.hpp",
    "content": "#ifndef CSV_DOC_LOADER_H\n#define CSV_DOC_LOADER_H\n\n#include <map>\n\n#include <QObject>\n#include <QWidget>\n#include <QSignalMapper>\n\nclass QLabel;\nclass QProgressBar;\nclass QDialogButtonBox;\nclass QListWidget;\nclass QVBoxLayout;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVDoc\n{\n    class LoadingDocument : public QWidget\n    {\n            Q_OBJECT\n\n            CSMDoc::Document *mDocument;\n            QLabel *mFile;\n            QLabel *mRecords;\n            QProgressBar *mFileProgress;\n            QProgressBar *mRecordProgress;\n            bool mAborted;\n            QDialogButtonBox *mButtons;\n            QLabel *mError;\n            QListWidget *mMessages;\n            QVBoxLayout *mLayout;\n            int mTotalRecords;\n\n        private:\n\n            void closeEvent (QCloseEvent *event) override;\n\n        public:\n\n            LoadingDocument (CSMDoc::Document *document);\n\n            void nextStage (const std::string& name, int totalRecords);\n\n            void nextRecord (int records);\n\n            void abort (const std::string& error);\n\n            void addMessage (const std::string& message);\n\n        private slots:\n\n            void cancel();\n\n        signals:\n\n            void cancel (CSMDoc::Document *document);\n            ///< Stop loading process.\n\n            void close (CSMDoc::Document *document);\n            ///< Close stopped loading process.\n    };\n\n    class Loader : public QObject\n    {\n            Q_OBJECT\n\n            std::map<CSMDoc::Document *, LoadingDocument *> mDocuments;\n\n        public:\n\n            Loader();\n\n            virtual ~Loader();\n\n        signals:\n\n            void cancel (CSMDoc::Document *document);\n\n            void close (CSMDoc::Document *document);\n\n        public slots:\n\n            void add (CSMDoc::Document *document);\n\n            void loadingStopped (CSMDoc::Document *document, bool completed,\n                const std::string& error);\n\n            void nextStage (CSMDoc::Document *document, const std::string& name, int totalRecords);\n\n            void nextRecord (CSMDoc::Document *document, int records);\n\n            void loadMessage (CSMDoc::Document *document, const std::string& message);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/newgame.cpp",
    "content": "#include \"newgame.hpp\"\n\n#include <QApplication>\n#include <QDesktopWidget>\n#include <QVBoxLayout>\n#include <QDialogButtonBox>\n#include <QPushButton>\n#include <QScreen>\n\n#include \"filewidget.hpp\"\n#include \"adjusterwidget.hpp\"\n\nCSVDoc::NewGameDialogue::NewGameDialogue()\n{\n    setWindowTitle (\"Create New Game\");\n\n    QVBoxLayout *layout = new QVBoxLayout (this);\n\n    mFileWidget = new FileWidget (this);\n    mFileWidget->setType (false);\n\n    layout->addWidget (mFileWidget, 1);\n\n    mAdjusterWidget = new AdjusterWidget (this);\n\n    layout->addWidget (mAdjusterWidget, 1);\n\n    QDialogButtonBox *buttons = new QDialogButtonBox (this);\n\n    mCreate = new QPushButton (\"Create\", this);\n    mCreate->setDefault (true);\n    mCreate->setEnabled (false);\n\n    buttons->addButton (mCreate, QDialogButtonBox::AcceptRole);\n\n    QPushButton *cancel = new QPushButton (\"Cancel\", this);\n\n    buttons->addButton (cancel, QDialogButtonBox::RejectRole);\n\n    layout->addWidget (buttons);\n\n    setLayout (layout);\n\n    connect (mAdjusterWidget, SIGNAL (stateChanged (bool)), this, SLOT (stateChanged (bool)));\n    connect (mCreate, SIGNAL (clicked()), this, SLOT (create()));\n    connect (cancel, SIGNAL (clicked()), this, SLOT (reject()));\n    connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)),\n        mAdjusterWidget, SLOT (setName (const QString&, bool)));\n\n    QRect scr = QGuiApplication::primaryScreen()->geometry();\n    QRect rect = geometry();\n    move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());\n}\n\nvoid CSVDoc::NewGameDialogue::setLocalData (const boost::filesystem::path& localData)\n{\n    mAdjusterWidget->setLocalData (localData);\n}\n\nvoid CSVDoc::NewGameDialogue::stateChanged (bool valid)\n{\n    mCreate->setEnabled (valid);\n}\n\nvoid CSVDoc::NewGameDialogue::create()\n{\n    emit createRequest (mAdjusterWidget->getPath());\n}\n\nvoid CSVDoc::NewGameDialogue::reject()\n{\n    emit cancelCreateGame ();\n    QDialog::reject();\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/newgame.hpp",
    "content": "#ifndef CSV_DOC_NEWGAME_H\n#define CSV_DOC_NEWGAME_H\n\n#include <boost/filesystem/path.hpp>\n\n#include <QDialog>\n#include <QMetaType>\n\n#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED\n#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED\nQ_DECLARE_METATYPE (boost::filesystem::path)\n#endif\n\nclass QPushButton;\n\nnamespace CSVDoc\n{\n    class FileWidget;\n    class AdjusterWidget;\n\n    class NewGameDialogue : public QDialog\n    {\n            Q_OBJECT\n\n            QPushButton *mCreate;\n            FileWidget *mFileWidget;\n            AdjusterWidget *mAdjusterWidget;\n\n        public:\n\n            NewGameDialogue();\n\n            void setLocalData (const boost::filesystem::path& localData);\n\n        signals:\n\n            void createRequest (const boost::filesystem::path& file);\n\n            void cancelCreateGame ();\n\n        private slots:\n\n            void stateChanged (bool valid);\n\n            void create();\n\n            void reject() override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/operation.cpp",
    "content": "#include \"operation.hpp\"\n\n#include <sstream>\n\n#include <QProgressBar>\n#include <QPushButton>\n#include <QVBoxLayout>\n\n#include \"../../model/doc/document.hpp\"\n\nvoid CSVDoc::Operation::updateLabel (int threads)\n{\n    if (threads==-1 || ((threads==0)!=mStalling))\n    {\n        std::string name (\"unknown operation\");\n\n        switch (mType)\n        {\n            case CSMDoc::State_Saving: name = \"saving\"; break;\n            case CSMDoc::State_Verifying: name = \"verifying\"; break;\n            case CSMDoc::State_Searching: name = \"searching\"; break;\n            case CSMDoc::State_Merging: name = \"merging\"; break;\n        }\n\n        std::ostringstream stream;\n\n        if ((mStalling = (threads<=0)))\n        {\n            stream << name << \" (waiting for a free worker thread)\";\n        }\n        else\n        {\n            stream << name << \" (%p%)\";\n        }\n\n        mProgressBar->setFormat (stream.str().c_str());\n    }\n}\n\nCSVDoc::Operation::Operation (int type, QWidget* parent) : mType (type), mStalling (false)\n{\n    /// \\todo Add a cancel button or a pop up menu with a cancel item\n    initWidgets();\n    setBarColor( type);\n    updateLabel();\n\n    /// \\todo assign different progress bar colours to allow the user to distinguish easily between operation types\n}\n\nCSVDoc::Operation::~Operation()\n{\n    delete mLayout;\n    delete mProgressBar;\n    delete mAbortButton;\n}\n\nvoid CSVDoc::Operation::initWidgets()\n{\n    mProgressBar = new QProgressBar ();\n    mAbortButton = new QPushButton(\"Abort\");\n    mLayout = new QHBoxLayout();\n\n    mLayout->addWidget (mProgressBar);\n    mLayout->addWidget (mAbortButton);\n\n    connect (mAbortButton, SIGNAL (clicked()), this, SLOT (abortOperation()));\n}\n\nvoid CSVDoc::Operation::setProgress (int current, int max, int threads)\n{\n    updateLabel (threads);\n    mProgressBar->setRange (0, max);\n    mProgressBar->setValue (current);\n}\n\nint CSVDoc::Operation::getType() const\n{\n    return mType;\n}\n\nvoid CSVDoc::Operation::setBarColor (int type)\n{\n    QString style =\"QProgressBar {\"\n            \"text-align: center;\"\n            \"}\"\n          \"QProgressBar::chunk {\"\n            \"background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 %1, stop:.50 %2 stop: .51 %3 stop:1 %4);\"\n            \"text-align: center;\"\n            \"margin: 2px 1px 1p 2px;\"\n          \"}\";\n\n    QString topColor = \"#F2F6F8\";\n    QString bottomColor = \"#E0EFF9\";\n    QString midTopColor = \"#D8E1E7\";\n    QString midBottomColor = \"#B5C6D0\";  // default gray gloss\n\n    // colors inspired by samples from:\n    // http://www.colorzilla.com/gradient-editor/\n\n    switch (type)\n    {\n    case CSMDoc::State_Saving:\n\n        topColor = \"#FECCB1\";\n        midTopColor = \"#F17432\";\n        midBottomColor = \"#EA5507\";\n        bottomColor = \"#FB955E\";  // red gloss #2\n        break;\n\n    case CSMDoc::State_Searching:\n\n        topColor = \"#EBF1F6\";\n        midTopColor = \"#ABD3EE\";\n        midBottomColor = \"#89C3EB\";\n        bottomColor = \"#D5EBFB\"; //blue gloss #4\n        break;\n\n    case CSMDoc::State_Verifying:\n\n        topColor = \"#BFD255\";\n        midTopColor = \"#8EB92A\";\n        midBottomColor = \"#72AA00\";\n        bottomColor = \"#9ECB2D\";  //green gloss\n        break;\n\n    case CSMDoc::State_Merging:\n\n        topColor = \"#F3E2C7\";\n        midTopColor = \"#C19E67\";\n        midBottomColor = \"#B68D4C\";\n        bottomColor = \"#E9D4B3\";  //l Brown 3D\n        break;\n\n    default:\n\n        topColor = \"#F2F6F8\";\n        bottomColor = \"#E0EFF9\";\n        midTopColor = \"#D8E1E7\";\n        midBottomColor = \"#B5C6D0\";  // gray gloss for undefined ops\n    }\n\n    mProgressBar->setStyleSheet(style.arg(topColor).arg(midTopColor).arg(midBottomColor).arg(bottomColor));\n}\n\nQHBoxLayout *CSVDoc::Operation::getLayout() const\n{\n    return mLayout;\n}\n\nvoid CSVDoc::Operation::abortOperation()\n{\n    emit abortOperation (mType);\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/operation.hpp",
    "content": "#ifndef CSV_DOC_OPERATION_H\n#define CSV_DOC_OPERATION_H\n\n#include <QObject>\n\nclass QHBoxLayout;\nclass QPushButton;\nclass QProgressBar;\n\nnamespace CSVDoc\n{\n    class Operation : public QObject\n    {\n            Q_OBJECT\n\n            int mType;\n            bool mStalling;\n            QProgressBar *mProgressBar;\n            QPushButton *mAbortButton;\n            QHBoxLayout *mLayout;\n\n            // not implemented\n            Operation (const Operation&);\n            Operation& operator= (const Operation&);\n\n            void updateLabel (int threads = -1);\n\n        public:\n\n            Operation (int type, QWidget *parent);\n            ~Operation();\n\n            void setProgress (int current, int max, int threads);\n\n            int getType() const;\n            QHBoxLayout *getLayout() const;\n\n        private:\n\n            void setBarColor (int type);\n            void initWidgets();\n\n        signals:\n\n            void abortOperation (int type);\n\n        private slots:\n\n            void abortOperation();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/operations.cpp",
    "content": "#include \"operations.hpp\"\n\n#include <QVBoxLayout>\n#include <QHBoxLayout>\n\n#include \"operation.hpp\"\n\nCSVDoc::Operations::Operations()\n{\n    /// \\todo make widget height fixed (exactly the height required to display all operations)\n\n    setFeatures (QDockWidget::NoDockWidgetFeatures);\n\n    QWidget *widgetContainer = new QWidget (this);\n    mLayout = new QVBoxLayout;\n\n    widgetContainer->setLayout (mLayout);\n    setWidget (widgetContainer);\n    setVisible (false);\n    setFixedHeight (widgetContainer->height());\n    setTitleBarWidget (new QWidget (this));\n}\n\nvoid CSVDoc::Operations::setProgress (int current, int max, int type, int threads)\n{\n    for (std::vector<Operation *>::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter)\n        if ((*iter)->getType()==type)\n        {\n            (*iter)->setProgress (current, max, threads);\n            return;\n        }\n\n    int oldCount = static_cast<int>(mOperations.size());\n    int newCount = oldCount + 1;\n\n    Operation *operation = new Operation (type, this);\n    connect (operation, SIGNAL (abortOperation (int)), this, SIGNAL (abortOperation (int)));\n\n    mLayout->addLayout (operation->getLayout());\n    mOperations.push_back (operation);\n    operation->setProgress (current, max, threads);\n\n    if ( oldCount > 0)\n        setFixedHeight (height()/oldCount * newCount);\n\n    setVisible (true);\n}\n\nvoid CSVDoc::Operations::quitOperation (int type)\n{\n    for (std::vector<Operation *>::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter)\n        if ((*iter)->getType()==type)\n        {\n            int oldCount = static_cast<int>(mOperations.size());\n            int newCount = oldCount - 1;\n\n            mLayout->removeItem ((*iter)->getLayout());\n\n            (*iter)->deleteLater();\n            mOperations.erase (iter);\n\n            if (oldCount > 1)\n                setFixedHeight (height() / oldCount * newCount);\n            else\n                setVisible (false);\n\n            break;\n        }\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/operations.hpp",
    "content": "#ifndef CSV_DOC_OPERATIONS_H\n#define CSV_DOC_OPERATIONS_H\n\n#include <vector>\n\n#include <QDockWidget>\n\nclass QVBoxLayout;\n\nnamespace CSVDoc\n{\n    class Operation;\n\n    class Operations : public QDockWidget\n    {\n            Q_OBJECT\n\n            QVBoxLayout *mLayout;\n            std::vector<Operation *> mOperations;\n\n            // not implemented\n            Operations (const Operations&);\n            Operations& operator= (const Operations&);\n\n        public:\n\n            Operations();\n\n            void setProgress (int current, int max, int type, int threads);\n            ///< Implicitly starts the operation, if it is not running already.\n\n            void quitOperation (int type);\n            ///< Calling this function for an operation that is not running is a no-op.\n\n        signals:\n\n            void abortOperation (int type);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/runlogsubview.cpp",
    "content": "#include \"runlogsubview.hpp\"\n\n#include <QTextEdit>\n\nCSVDoc::RunLogSubView::RunLogSubView (const CSMWorld::UniversalId& id,\n    CSMDoc::Document& document)\n: SubView (id)\n{\n    QTextEdit *edit = new QTextEdit (this);\n    edit->setDocument (document.getRunLog());\n    edit->setReadOnly (true);\n\n    setWidget (edit);\n}\n\nvoid CSVDoc::RunLogSubView::setEditLock (bool locked)\n{\n    // ignored since this SubView does not have editing\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/runlogsubview.hpp",
    "content": "#ifndef CSV_DOC_RUNLOGSUBVIEW_H\n#define CSV_DOC_RUNLOGSUBVIEW_H\n\n#include \"subview.hpp\"\n\nnamespace CSVDoc\n{\n    class RunLogSubView : public SubView\n    {\n            Q_OBJECT\n\n        public:\n\n            RunLogSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);\n\n            void setEditLock (bool locked) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/sizehint.cpp",
    "content": "#include \"sizehint.hpp\"\n\nCSVDoc::SizeHintWidget::SizeHintWidget(QWidget *parent) : QWidget(parent)\n{}\n\nCSVDoc::SizeHintWidget::~SizeHintWidget()\n{}\n\nQSize CSVDoc::SizeHintWidget::sizeHint() const\n{\n    return mSize;\n}\n\nvoid CSVDoc::SizeHintWidget::setSizeHint(const QSize &size)\n{\n    mSize = size;\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/sizehint.hpp",
    "content": "#ifndef CSV_DOC_SIZEHINT_H\n#define CSV_DOC_SIZEHINT_H\n\n#include <QWidget>\n#include <QSize>\n\nnamespace CSVDoc\n{\n    class SizeHintWidget : public QWidget\n    {\n            QSize mSize;\n\n        public:\n            SizeHintWidget(QWidget *parent = nullptr);\n            ~SizeHintWidget();\n\n            QSize sizeHint() const override;\n            void setSizeHint(const QSize &size);\n    };\n}\n\n#endif // CSV_DOC_SIZEHINT_H\n"
  },
  {
    "path": "apps/opencs/view/doc/startup.cpp",
    "content": "#include \"startup.hpp\"\n\n#include <QApplication>\n#include <QDesktopWidget>\n#include <QVBoxLayout>\n#include <QHBoxLayout>\n#include <QRect>\n#include <QGridLayout>\n#include <QLabel>\n#include <QIcon>\n#include <QPushButton>\n#include <QScreen>\n\nQPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon)\n{\n    int column = mColumn--;\n\n    QPushButton *button = new QPushButton (this);\n\n    button->setIcon (QIcon (icon));\n\n    button->setSizePolicy (QSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred));\n\n    mLayout->addWidget (button, 0, column);\n\n    mLayout->addWidget (new QLabel (label, this), 1, column, Qt::AlignCenter);\n\n    int width = mLayout->itemAtPosition (1, column)->widget()->sizeHint().width();\n\n    if (width>mWidth)\n        mWidth = width;\n\n    return button;\n}\n\n\nQWidget *CSVDoc::StartupDialogue::createButtons()\n{\n    QWidget *widget = new QWidget (this);\n\n    mLayout = new QGridLayout (widget);\n\n    /// \\todo add icons\n    QPushButton *loadDocument = addButton (\"Edit A Content File\", QIcon (\":startup/edit-content\"));\n    connect (loadDocument, SIGNAL (clicked()), this, SIGNAL (loadDocument()));\n\n    QPushButton *createAddon = addButton (\"Create A New Addon\", QIcon (\":startup/create-addon\"));\n    connect (createAddon, SIGNAL (clicked()), this, SIGNAL (createAddon()));\n\n    QPushButton *createGame = addButton (\"Create A New Game\", QIcon (\":startup/create-game\"));\n    connect (createGame, SIGNAL (clicked()), this, SIGNAL (createGame()));\n\n    for (int i=0; i<3; ++i)\n        mLayout->setColumnMinimumWidth (i, mWidth);\n\n    mLayout->setRowMinimumHeight (0, mWidth);\n\n    mLayout->setSizeConstraint (QLayout::SetMinimumSize);\n    mLayout->setHorizontalSpacing (32);\n\n    mLayout->setContentsMargins (16, 16, 16, 8);\n\n    loadDocument->setIconSize (QSize (mWidth, mWidth));\n    createGame->setIconSize (QSize (mWidth, mWidth));\n    createAddon->setIconSize (QSize (mWidth, mWidth));\n\n    widget->setLayout (mLayout);\n\n    return widget;\n}\n\nQWidget *CSVDoc::StartupDialogue::createTools()\n{\n    QWidget *widget = new QWidget (this);\n\n    QHBoxLayout *layout = new QHBoxLayout (widget);\n    layout->setDirection (QBoxLayout::RightToLeft);\n    layout->setContentsMargins (4, 4, 4, 4);\n\n    QPushButton *config = new QPushButton (widget);\n\n    config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));\n    config->setIcon (QIcon (\":startup/configure\"));\n    config->setToolTip (\"Open user settings\");\n\n    layout->addWidget (config);\n\n    layout->addWidget (new QWidget, 1); // dummy widget; stops buttons from taking all the space\n\n    widget->setLayout (layout);\n\n    connect (config, SIGNAL (clicked()), this, SIGNAL (editConfig()));\n\n    return widget;\n}\n\nCSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)\n{\n    setWindowTitle (\"OpenMW-CS\");\n\n    QVBoxLayout *layout = new QVBoxLayout (this);\n\n    layout->setContentsMargins (0, 0, 0, 0);\n\n    layout->addWidget (createButtons());\n    layout->addWidget (createTools());\n\n    /// \\todo remove this label once we are feature complete and convinced that this thing is\n    /// working properly.\n    QLabel *warning = new QLabel (\"<font color=Red>WARNING: OpenMW-CS is in alpha stage.<p>The editor is not feature complete and not sufficiently tested.<br>In theory your data should be safe. But we strongly advise to make backups regularly if you are working with live data.</font color>\");\n\n    QFont font;\n    font.setPointSize (12);\n    font.setBold (true);\n\n    warning->setFont (font);\n    warning->setWordWrap (true);\n\n    layout->addWidget (warning, 1);\n\n    setLayout (layout);\n\n    QRect scr = QGuiApplication::primaryScreen()->geometry();\n    QRect rect = geometry();\n    move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/startup.hpp",
    "content": "#ifndef CSV_DOC_STARTUP_H\n#define CSV_DOC_STARTUP_H\n\n#include <QWidget>\n\nclass QGridLayout;\nclass QString;\nclass QPushButton;\nclass QWidget;\nclass QIcon;\n\nnamespace CSVDoc\n{\n    class StartupDialogue : public QWidget\n    {\n        Q_OBJECT\n\n        private:\n\n            int mWidth;\n            int mColumn;\n            QGridLayout *mLayout;\n\n            QPushButton *addButton (const QString& label, const QIcon& icon);\n\n            QWidget *createButtons();\n\n            QWidget *createTools();\n\n        public:\n\n            StartupDialogue();\n\n        signals:\n\n            void createGame();\n\n            void createAddon();\n\n            void loadDocument();\n\n            void editConfig();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/subview.cpp",
    "content": "#include \"subview.hpp\"\n\n#include \"view.hpp\"\n\n#include <QShortcut>\n#include <QEvent>\n#include <QKeyEvent>\n\nbool CSVDoc::SubView::event (QEvent *event)\n{\n    if (event->type()==QEvent::ShortcutOverride)\n    {\n        QKeyEvent *keyEvent = static_cast<QKeyEvent *> (event);\n\n        if (keyEvent->key()==Qt::Key_W && keyEvent->modifiers()==(Qt::ShiftModifier | Qt::ControlModifier))\n            emit closeRequest();\n            return true;\n    }\n\n    return QDockWidget::event (event);\n}\n\nCSVDoc::SubView::SubView (const CSMWorld::UniversalId& id)\n : mUniversalId (id)\n{\n    /// \\todo  add a button to the title bar that clones this sub view\n\n    setWindowTitle (QString::fromUtf8 (mUniversalId.toString().c_str()));\n    setAttribute(Qt::WA_DeleteOnClose);\n}\n\nCSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const\n{\n    return mUniversalId;\n}\n\nvoid CSVDoc::SubView::setStatusBar (bool show) {}\n\nvoid CSVDoc::SubView::useHint (const std::string& hint) {}\n\nvoid CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id)\n{\n    mUniversalId = id;\n    setWindowTitle (QString::fromUtf8(mUniversalId.toString().c_str()));\n    emit universalIdChanged (mUniversalId);\n}\n\nvoid CSVDoc::SubView::closeEvent (QCloseEvent *event)\n{\n    emit updateSubViewIndices (this);\n}\n\nstd::string CSVDoc::SubView::getTitle() const\n{\n    return mUniversalId.toString();\n}\n\nvoid CSVDoc::SubView::closeRequest()\n{\n    emit closeRequest (this);\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/subview.hpp",
    "content": "#ifndef CSV_DOC_SUBVIEW_H\n#define CSV_DOC_SUBVIEW_H\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/universalid.hpp\"\n\n#include \"subviewfactory.hpp\"\n\n#include <QDockWidget>\n\nclass QUndoStack;\n\nnamespace CSMWorld\n{\n    class Data;\n}\n\nnamespace CSVDoc\n{\n    class View;\n\n    class SubView : public QDockWidget\n    {\n            Q_OBJECT\n\n            CSMWorld::UniversalId mUniversalId;\n\n            // not implemented\n            SubView (const SubView&);\n            SubView& operator= (SubView&);\n\n        protected:\n\n            void setUniversalId(const CSMWorld::UniversalId& id);\n\n            bool event (QEvent *event) override;\n\n        public:\n\n            SubView (const CSMWorld::UniversalId& id);\n\n            CSMWorld::UniversalId getUniversalId() const;\n\n            virtual void setEditLock (bool locked) = 0;\n\n            virtual void setStatusBar (bool show);\n            ///< Default implementation: ignored\n\n            virtual void useHint (const std::string& hint);\n            ///< Default implementation: ignored\n\n            virtual std::string getTitle() const;\n\n        private:\n\n            void closeEvent (QCloseEvent *event) override;\n\n        signals:\n\n            void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint);\n\n            void closeRequest (SubView *subView);\n\n            void updateTitle();\n\n            void updateSubViewIndices (SubView *view = nullptr);\n\n            void universalIdChanged (const CSMWorld::UniversalId& universalId);\n\n        protected slots:\n\n            void closeRequest();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/subviewfactory.cpp",
    "content": "#include \"subviewfactory.hpp\"\n\n#include <cassert>\n\n#include <stdexcept>\n\nCSVDoc::SubViewFactoryBase::SubViewFactoryBase() {}\n\nCSVDoc::SubViewFactoryBase::~SubViewFactoryBase() {}\n\n\nCSVDoc::SubViewFactoryManager::SubViewFactoryManager() {}\n\nCSVDoc::SubViewFactoryManager::~SubViewFactoryManager()\n{\n    for (std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *>::iterator iter (mSubViewFactories.begin());\n        iter!=mSubViewFactories.end(); ++iter)\n        delete iter->second;\n}\n\nvoid CSVDoc::SubViewFactoryManager::add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory)\n{\n    assert (mSubViewFactories.find (id)==mSubViewFactories.end());\n\n    mSubViewFactories.insert (std::make_pair (id, factory));\n}\n\nCSVDoc::SubView *CSVDoc::SubViewFactoryManager::makeSubView (const CSMWorld::UniversalId& id,\n    CSMDoc::Document& document)\n{\n    std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *>::iterator iter = mSubViewFactories.find (id.getType());\n\n    if (iter==mSubViewFactories.end())\n        throw std::runtime_error (\"Failed to create a sub view for: \" + id.toString());\n\n    return iter->second->makeSubView (id, document);\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/subviewfactory.hpp",
    "content": "#ifndef CSV_DOC_SUBVIEWFACTORY_H\n#define CSV_DOC_SUBVIEWFACTORY_H\n\n#include <map>\n\n#include \"../../model/world/universalid.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVDoc\n{\n    class SubView;\n\n    class SubViewFactoryBase\n    {\n            // not implemented\n            SubViewFactoryBase (const SubViewFactoryBase&);\n            SubViewFactoryBase& operator= (const SubViewFactoryBase&);\n\n        public:\n\n            SubViewFactoryBase();\n\n            virtual ~SubViewFactoryBase();\n\n            virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) = 0;\n            ///< The ownership of the returned sub view is not transferred.\n    };\n\n    class SubViewFactoryManager\n    {\n            std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *> mSubViewFactories;\n\n            // not implemented\n            SubViewFactoryManager (const SubViewFactoryManager&);\n            SubViewFactoryManager& operator= (const SubViewFactoryManager&);\n\n        public:\n\n            SubViewFactoryManager();\n\n            ~SubViewFactoryManager();\n\n            void add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory);\n            ///< The ownership of \\a factory is transferred to this.\n\n            SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);\n            ///< The ownership of the returned sub view is not transferred.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/subviewfactoryimp.hpp",
    "content": "#ifndef CSV_DOC_SUBVIEWFACTORYIMP_H\n#define CSV_DOC_SUBVIEWFACTORYIMP_H\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"subviewfactory.hpp\"\n\nnamespace CSVDoc\n{\n    template<class SubViewT>\n    class SubViewFactory : public SubViewFactoryBase\n    {\n        public:\n\n            CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) override;\n    };\n\n    template<class SubViewT>\n    CSVDoc::SubView *SubViewFactory<SubViewT>::makeSubView (const CSMWorld::UniversalId& id,\n        CSMDoc::Document& document)\n    {\n        return new SubViewT (id, document);\n    }\n\n\n    template<class SubViewT, class CreatorFactoryT>\n    class SubViewFactoryWithCreator : public SubViewFactoryBase\n    {\n            bool mSorting;\n\n        public:\n\n            SubViewFactoryWithCreator (bool sorting = true);\n\n            CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) override;\n    };\n\n    template<class SubViewT, class CreatorFactoryT>\n    SubViewFactoryWithCreator<SubViewT, CreatorFactoryT>::SubViewFactoryWithCreator (bool sorting)\n    : mSorting (sorting)\n    {}\n\n    template<class SubViewT, class CreatorFactoryT>\n    CSVDoc::SubView *SubViewFactoryWithCreator<SubViewT, CreatorFactoryT>::makeSubView (\n        const CSMWorld::UniversalId& id, CSMDoc::Document& document)\n    {\n        return new SubViewT (id, document, CreatorFactoryT(), mSorting);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/view.cpp",
    "content": "#include \"view.hpp\"\n\n#include <sstream>\n#include <stdexcept>\n\n#include <QCloseEvent>\n#include <QMenuBar>\n#include <QMessageBox>\n#include <QMdiArea>\n#include <QDockWidget>\n#include <QApplication>\n#include <QDesktopWidget>\n#include <QScrollArea>\n#include <QHBoxLayout>\n#include <QDesktopWidget>\n#include <QScrollBar>\n#include <QScreen>\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"../../model/world/idtable.hpp\"\n\n#include \"../world/subviews.hpp\"\n#include \"../world/scenesubview.hpp\"\n#include \"../world/tablesubview.hpp\"\n\n#include \"../tools/subviews.hpp\"\n\n#include <components/misc/helpviewer.hpp>\n#include <components/version/version.hpp>\n\n#include \"viewmanager.hpp\"\n#include \"operations.hpp\"\n#include \"subview.hpp\"\n#include \"globaldebugprofilemenu.hpp\"\n#include \"runlogsubview.hpp\"\n#include \"subviewfactoryimp.hpp\"\n\nvoid CSVDoc::View::closeEvent (QCloseEvent *event)\n{\n    if (!mViewManager.closeRequest (this))\n        event->ignore();\n    else\n    {\n        // closeRequest() returns true if last document\n        mViewManager.removeDocAndView(mDocument);\n    }\n}\n\nvoid CSVDoc::View::setupFileMenu()\n{\n    QMenu *file = menuBar()->addMenu (tr (\"File\"));\n\n    QAction* newGame = createMenuEntry(\"New Game\", \":./menu-new-game.png\", file, \"document-file-newgame\");\n    connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest()));\n\n    QAction* newAddon = createMenuEntry(\"New Addon\", \":./menu-new-addon.png\", file, \"document-file-newaddon\");\n    connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest()));\n\n    QAction* open = createMenuEntry(\"Open\", \":./menu-open.png\", file, \"document-file-open\");\n    connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest()));\n\n    QAction* save = createMenuEntry(\"Save\", \":./menu-save.png\", file, \"document-file-save\");\n    connect (save, SIGNAL (triggered()), this, SLOT (save()));\n    mSave = save;\n\n    QAction* verify = createMenuEntry(\"Verify\", \":./menu-verify.png\", file, \"document-file-verify\");\n    connect (verify, SIGNAL (triggered()), this, SLOT (verify()));\n    mVerify = verify;\n\n    QAction* merge = createMenuEntry(\"Merge\", \":./menu-merge.png\", file, \"document-file-merge\");\n    connect (merge, SIGNAL (triggered()), this, SLOT (merge()));\n    mMerge = merge;\n\n    QAction* loadErrors = createMenuEntry(\"Error Log\", \":./error-log.png\", file, \"document-file-errorlog\");\n    connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog()));\n\n    QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, \"document-file-metadata\");\n    connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView()));\n\n    QAction* close = createMenuEntry(\"Close\", \":./menu-close.png\", file, \"document-file-close\");\n    connect (close, SIGNAL (triggered()), this, SLOT (close()));\n\n    QAction* exit = createMenuEntry(\"Exit\", \":./menu-exit.png\", file, \"document-file-exit\");\n    connect (exit, SIGNAL (triggered()), this, SLOT (exit()));\n\n    connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *)));\n}\n\nnamespace\n{\n\n    void updateUndoRedoAction(QAction *action, const std::string &settingsKey)\n    {\n        QKeySequence seq;\n        CSMPrefs::State::get().getShortcutManager().getSequence(settingsKey, seq);\n        action->setShortcut(seq);\n    }\n\n}\n\nvoid CSVDoc::View::undoActionChanged()\n{\n    updateUndoRedoAction(mUndo, \"document-edit-undo\");\n}\n\nvoid CSVDoc::View::redoActionChanged()\n{\n    updateUndoRedoAction(mRedo, \"document-edit-redo\");\n}\n\nvoid CSVDoc::View::setupEditMenu()\n{\n    QMenu *edit = menuBar()->addMenu (tr (\"Edit\"));\n\n    mUndo = mDocument->getUndoStack().createUndoAction (this, tr(\"Undo\"));\n    setupShortcut(\"document-edit-undo\", mUndo);\n    connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));\n    mUndo->setIcon(QIcon(QString::fromStdString(\":./menu-undo.png\")));\n    edit->addAction (mUndo);\n\n    mRedo = mDocument->getUndoStack().createRedoAction (this, tr(\"Redo\"));\n    connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ()));\n    setupShortcut(\"document-edit-redo\", mRedo);\n    mRedo->setIcon(QIcon(QString::fromStdString(\":./menu-redo.png\")));\n    edit->addAction (mRedo);\n\n    QAction* userSettings = createMenuEntry(\"Preferences\", \":./menu-preferences.png\", edit, \"document-edit-preferences\");\n    connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest()));\n\n    QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, \"document-edit-search\");\n    connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView()));\n}\n\nvoid CSVDoc::View::setupViewMenu()\n{\n    QMenu *view = menuBar()->addMenu (tr (\"View\"));\n\n    QAction *newWindow = createMenuEntry(\"New View\", \":./menu-new-window.png\", view, \"document-view-newview\");\n    connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));\n\n    mShowStatusBar = createMenuEntry(\"Toggle Status Bar\", \":./menu-status-bar.png\", view, \"document-view-statusbar\");\n    connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool)));\n    mShowStatusBar->setCheckable (true);\n    mShowStatusBar->setChecked (CSMPrefs::get()[\"Windows\"][\"show-statusbar\"].isTrue());\n\n    view->addAction (mShowStatusBar);\n\n    QAction *filters = createMenuEntry(CSMWorld::UniversalId::Type_Filters, view, \"document-mechanics-filters\");\n    connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView()));\n}\n\nvoid CSVDoc::View::setupWorldMenu()\n{\n    QMenu *world = menuBar()->addMenu (tr (\"World\"));\n\n    QAction* regions = createMenuEntry(CSMWorld::UniversalId::Type_Regions, world, \"document-world-regions\");\n    connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));\n\n    QAction* cells = createMenuEntry(CSMWorld::UniversalId::Type_Cells, world, \"document-world-cells\");\n    connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));\n\n    QAction* referenceables = createMenuEntry(CSMWorld::UniversalId::Type_Referenceables, world, \"document-world-referencables\");\n    connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));\n\n    QAction* references = createMenuEntry(CSMWorld::UniversalId::Type_References, world, \"document-world-references\");\n    connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));\n\n    QAction *lands = createMenuEntry(CSMWorld::UniversalId::Type_Lands, world, \"document-world-lands\");\n    connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView()));\n\n    QAction *landTextures = createMenuEntry(CSMWorld::UniversalId::Type_LandTextures, world, \"document-world-landtextures\");\n    connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView()));\n\n    QAction *grid = createMenuEntry(CSMWorld::UniversalId::Type_Pathgrids, world, \"document-world-pathgrid\");\n    connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView()));\n\n    world->addSeparator(); // items that don't represent single record lists follow here\n\n    QAction *regionMap = createMenuEntry(CSMWorld::UniversalId::Type_RegionMap, world, \"document-world-regionmap\");\n    connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));\n}\n\nvoid CSVDoc::View::setupMechanicsMenu()\n{\n    QMenu *mechanics = menuBar()->addMenu (tr (\"Mechanics\"));\n\n    QAction* globals = createMenuEntry(CSMWorld::UniversalId::Type_Globals, mechanics, \"document-mechanics-globals\");\n    connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));\n\n    QAction* gmsts = createMenuEntry(CSMWorld::UniversalId::Type_Gmsts, mechanics, \"document-mechanics-gamesettings\");\n    connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));\n\n    QAction* scripts = createMenuEntry(CSMWorld::UniversalId::Type_Scripts, mechanics, \"document-mechanics-scripts\");\n    connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));\n\n    QAction* spells = createMenuEntry(CSMWorld::UniversalId::Type_Spells, mechanics, \"document-mechanics-spells\");\n    connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));\n\n    QAction* enchantments = createMenuEntry(CSMWorld::UniversalId::Type_Enchantments, mechanics, \"document-mechanics-enchantments\");\n    connect (enchantments, SIGNAL (triggered()), this, SLOT (addEnchantmentsSubView()));\n\n    QAction* magicEffects = createMenuEntry(CSMWorld::UniversalId::Type_MagicEffects, mechanics, \"document-mechanics-magiceffects\");\n    connect (magicEffects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView()));\n\n    QAction* startScripts = createMenuEntry(CSMWorld::UniversalId::Type_StartScripts, mechanics, \"document-mechanics-startscripts\");\n    connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView()));\n}\n\nvoid CSVDoc::View::setupCharacterMenu()\n{\n    QMenu *characters = menuBar()->addMenu (tr (\"Characters\"));\n\n    QAction* skills = createMenuEntry(CSMWorld::UniversalId::Type_Skills, characters, \"document-character-skills\");\n    connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView()));\n\n    QAction* classes = createMenuEntry(CSMWorld::UniversalId::Type_Classes, characters, \"document-character-classes\");\n    connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView()));\n\n    QAction* factions = createMenuEntry(CSMWorld::UniversalId::Type_Faction, characters, \"document-character-factions\");\n    connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView()));\n\n    QAction* races = createMenuEntry(CSMWorld::UniversalId::Type_Races, characters, \"document-character-races\");\n    connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView()));\n\n    QAction* birthsigns = createMenuEntry(CSMWorld::UniversalId::Type_Birthsigns, characters, \"document-character-birthsigns\");\n    connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView()));\n\n    QAction* topics = createMenuEntry(CSMWorld::UniversalId::Type_Topics, characters, \"document-character-topics\");\n    connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView()));\n\n    QAction* journals = createMenuEntry(CSMWorld::UniversalId::Type_Journals, characters, \"document-character-journals\");\n    connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));\n\n    QAction* topicInfos = createMenuEntry(CSMWorld::UniversalId::Type_TopicInfos, characters, \"document-character-topicinfos\");\n    connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView()));\n\n    QAction* journalInfos = createMenuEntry(CSMWorld::UniversalId::Type_JournalInfos, characters, \"document-character-journalinfos\");\n    connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView()));\n\n    QAction* bodyParts = createMenuEntry(CSMWorld::UniversalId::Type_BodyParts, characters, \"document-character-bodyparts\");\n    connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView()));\n}\n\nvoid CSVDoc::View::setupAssetsMenu()\n{\n    QMenu *assets = menuBar()->addMenu (tr (\"Assets\"));\n\n    QAction* reload = createMenuEntry(\"Reload\", \":./menu-reload.png\", assets, \"document-assets-reload\");\n    connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged()));\n\n    assets->addSeparator();\n\n    QAction* sounds = createMenuEntry(CSMWorld::UniversalId::Type_Sounds, assets, \"document-assets-sounds\");\n    connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));\n\n    QAction* soundGens = createMenuEntry(CSMWorld::UniversalId::Type_SoundGens, assets, \"document-assets-soundgens\");\n    connect (soundGens, SIGNAL (triggered()), this, SLOT (addSoundGensSubView()));\n\n    assets->addSeparator(); // resources follow here\n\n    QAction* meshes = createMenuEntry(CSMWorld::UniversalId::Type_Meshes, assets, \"document-assets-meshes\");\n    connect (meshes, SIGNAL (triggered()), this, SLOT (addMeshesSubView()));\n\n    QAction* icons = createMenuEntry(CSMWorld::UniversalId::Type_Icons, assets, \"document-assets-icons\");\n    connect (icons, SIGNAL (triggered()), this, SLOT (addIconsSubView()));\n\n    QAction* musics = createMenuEntry(CSMWorld::UniversalId::Type_Musics, assets, \"document-assets-musics\");\n    connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView()));\n\n    QAction* soundFiles = createMenuEntry(CSMWorld::UniversalId::Type_SoundsRes, assets, \"document-assets-soundres\");\n    connect (soundFiles, SIGNAL (triggered()), this, SLOT (addSoundsResSubView()));\n\n    QAction* textures = createMenuEntry(CSMWorld::UniversalId::Type_Textures, assets, \"document-assets-textures\");\n    connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView()));\n\n    QAction* videos = createMenuEntry(CSMWorld::UniversalId::Type_Videos, assets, \"document-assets-videos\");\n    connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView()));\n}\n\nvoid CSVDoc::View::setupDebugMenu()\n{\n    QMenu *debug = menuBar()->addMenu (tr (\"Debug\"));\n\n    QAction* profiles = createMenuEntry(CSMWorld::UniversalId::Type_DebugProfiles, debug, \"document-debug-profiles\");\n    connect (profiles, SIGNAL (triggered()), this, SLOT (addDebugProfilesSubView()));\n\n    debug->addSeparator();\n\n    mGlobalDebugProfileMenu = new GlobalDebugProfileMenu (\n        &dynamic_cast<CSMWorld::IdTable&> (*mDocument->getData().getTableModel (\n        CSMWorld::UniversalId::Type_DebugProfiles)), this);\n\n    connect (mGlobalDebugProfileMenu, SIGNAL (triggered (const std::string&)),\n        this, SLOT (run (const std::string&)));\n\n    QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu);\n    runDebug->setText (tr (\"Run OpenMW\"));\n    setupShortcut(\"document-debug-run\", runDebug);\n    runDebug->setIcon(QIcon(QString::fromStdString(\":./run-openmw.png\")));\n\n    QAction* stopDebug = createMenuEntry(\"Stop OpenMW\", \":./stop-openmw.png\", debug, \"document-debug-shutdown\");\n    connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop()));\n    mStopDebug = stopDebug;\n\n    QAction* runLog = createMenuEntry(CSMWorld::UniversalId::Type_RunLog, debug, \"document-debug-runlog\");\n    connect (runLog, SIGNAL (triggered()), this, SLOT (addRunLogSubView()));\n}\n\nvoid CSVDoc::View::setupHelpMenu()\n{\n    QMenu *help = menuBar()->addMenu (tr (\"Help\"));\n\n    QAction* helpInfo = createMenuEntry(\"Help\", \":/info.png\", help, \"document-help-help\");\n    connect (helpInfo, SIGNAL (triggered()), this, SLOT (openHelp()));\n\n    QAction* tutorial = createMenuEntry(\"Tutorial\", \":/info.png\", help, \"document-help-tutorial\");\n    connect (tutorial, SIGNAL (triggered()), this, SLOT (tutorial()));\n\n    QAction* about = createMenuEntry(\"About OpenMW-CS\", \":./info.png\", help, \"document-help-about\");\n    connect (about, SIGNAL (triggered()), this, SLOT (infoAbout()));\n\n    QAction* aboutQt = createMenuEntry(\"About Qt\", \":./qt.png\", help, \"document-help-qt\");\n    connect (aboutQt, SIGNAL (triggered()), this, SLOT (infoAboutQt()));\n}\n\nQAction* CSVDoc::View::createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName)\n{\n    const std::string title = CSMWorld::UniversalId (type).getTypeName();\n    QAction *entry = new QAction(QString::fromStdString(title), this);\n    setupShortcut(shortcutName, entry);\n    const std::string iconName = CSMWorld::UniversalId (type).getIcon();\n    if (!iconName.empty() && iconName != \":placeholder\")\n        entry->setIcon(QIcon(QString::fromStdString(iconName)));\n\n    menu->addAction (entry);\n\n    return entry;\n}\n\nQAction* CSVDoc::View::createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName)\n{\n    QAction *entry = new QAction(QString::fromStdString(title), this);\n    setupShortcut(shortcutName, entry);\n    if (!iconName.empty() && iconName != \":placeholder\")\n        entry->setIcon(QIcon(QString::fromStdString(iconName)));\n\n    menu->addAction (entry);\n\n    return entry;\n}\n\nvoid CSVDoc::View::setupUi()\n{\n    setupFileMenu();\n    setupEditMenu();\n    setupViewMenu();\n    setupWorldMenu();\n    setupMechanicsMenu();\n    setupCharacterMenu();\n    setupAssetsMenu();\n    setupDebugMenu();\n    setupHelpMenu();\n}\n\nvoid CSVDoc::View::setupShortcut(const char* name, QAction* action)\n{\n    CSMPrefs::Shortcut* shortcut = new CSMPrefs::Shortcut(name, this);\n    shortcut->associateAction(action);\n}\n\nvoid CSVDoc::View::updateTitle()\n{\n    std::ostringstream stream;\n\n    stream << mDocument->getSavePath().filename().string();\n\n    if (mDocument->getState() & CSMDoc::State_Modified)\n            stream << \" *\";\n\n    if (mViewTotal>1)\n        stream << \" [\" << (mViewIndex+1) << \"/\" << mViewTotal << \"]\";\n\n    CSMPrefs::Category& windows = CSMPrefs::State::get()[\"Windows\"];\n\n    bool hideTitle = windows[\"hide-subview\"].isTrue() &&\n        mSubViews.size()==1 && !mSubViews.at (0)->isFloating();\n\n    if (hideTitle)\n        stream << \" - \" << mSubViews.at (0)->getTitle();\n\n    setWindowTitle (QString::fromUtf8(stream.str().c_str()));\n}\n\nvoid CSVDoc::View::updateSubViewIndices(SubView *view)\n{\n    CSMPrefs::Category& windows = CSMPrefs::State::get()[\"Windows\"];\n\n    if(view && mSubViews.contains(view))\n    {\n        mSubViews.removeOne(view);\n\n        // adjust (reduce) the scroll area (even floating), except when it is \"Scrollbar Only\"\n        if (windows[\"mainwindow-scrollbar\"].toString() == \"Grow then Scroll\")\n            updateScrollbar();\n    }\n\n    bool hideTitle = windows[\"hide-subview\"].isTrue() &&\n        mSubViews.size()==1 && !mSubViews.at (0)->isFloating();\n\n    updateTitle();\n\n    for (SubView *subView : mSubViews)\n    {\n        if (!subView->isFloating())\n        {\n            if (hideTitle)\n            {\n                subView->setTitleBarWidget (new QWidget (this));\n                subView->setWindowTitle (QString::fromUtf8 (subView->getTitle().c_str()));\n            }\n            else\n            {\n                delete subView->titleBarWidget();\n                subView->setTitleBarWidget (nullptr);\n            }\n        }\n    }\n}\n\nvoid CSVDoc::View::updateActions()\n{\n    bool editing = !(mDocument->getState() & CSMDoc::State_Locked);\n    bool running = mDocument->getState() & CSMDoc::State_Running;\n\n    for (std::vector<QAction *>::iterator iter (mEditingActions.begin()); iter!=mEditingActions.end(); ++iter)\n        (*iter)->setEnabled (editing);\n\n    mUndo->setEnabled (editing && mDocument->getUndoStack().canUndo());\n    mRedo->setEnabled (editing && mDocument->getUndoStack().canRedo());\n\n    mSave->setEnabled (!(mDocument->getState() & CSMDoc::State_Saving) && !running);\n    mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying));\n\n    mGlobalDebugProfileMenu->updateActions (running);\n    mStopDebug->setEnabled (running);\n\n    mMerge->setEnabled (mDocument->getContentFiles().size()>1 &&\n        !(mDocument->getState() & CSMDoc::State_Merging));\n}\n\nCSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)\n    : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),\n      mViewTotal (totalViews), mScroll(nullptr), mScrollbarOnly(false)\n{\n    CSMPrefs::Category& windows = CSMPrefs::State::get()[\"Windows\"];\n\n    int width = std::max (windows[\"default-width\"].toInt(), 300);\n    int height = std::max (windows[\"default-height\"].toInt(), 300);\n\n    resize (width, height);\n\n    mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);\n\n    if (windows[\"mainwindow-scrollbar\"].toString() == \"Grow Only\")\n    {\n        setCentralWidget (&mSubViewWindow);\n    }\n    else\n    {\n        createScrollArea();\n    }\n\n    mOperations = new Operations;\n    addDockWidget (Qt::BottomDockWidgetArea, mOperations);\n\n    setContextMenuPolicy(Qt::NoContextMenu);\n\n    updateTitle();\n\n    setupUi();\n\n    updateActions();\n\n    CSVWorld::addSubViewFactories (mSubViewFactory);\n    CSVTools::addSubViewFactories (mSubViewFactory);\n\n    mSubViewFactory.add (CSMWorld::UniversalId::Type_RunLog, new SubViewFactory<RunLogSubView>);\n\n    connect (mOperations, SIGNAL (abortOperation (int)), this, SLOT (abortOperation (int)));\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n}\n\nCSVDoc::View::~View()\n{\n}\n\nconst CSMDoc::Document *CSVDoc::View::getDocument() const\n{\n        return mDocument;\n}\n\nCSMDoc::Document *CSVDoc::View::getDocument()\n{\n        return mDocument;\n}\n\nvoid CSVDoc::View::setIndex (int viewIndex, int totalViews)\n{\n    mViewIndex = viewIndex;\n    mViewTotal = totalViews;\n    updateTitle();\n}\n\nvoid CSVDoc::View::updateDocumentState()\n{\n    updateTitle();\n    updateActions();\n\n    static const int operations[] =\n    {\n        CSMDoc::State_Saving, CSMDoc::State_Verifying, CSMDoc::State_Searching,\n        CSMDoc::State_Merging,\n        -1 // end marker\n    };\n\n    int state = mDocument->getState() ;\n\n    for (int i=0; operations[i]!=-1; ++i)\n        if (!(state & operations[i]))\n           mOperations->quitOperation (operations[i]);\n\n    QList<CSVDoc::SubView *> subViews = findChildren<CSVDoc::SubView *>();\n\n    for (QList<CSVDoc::SubView *>::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter)\n        (*iter)->setEditLock (state & CSMDoc::State_Locked);\n}\n\nvoid CSVDoc::View::updateProgress (int current, int max, int type, int threads)\n{\n    mOperations->setProgress (current, max, type, threads);\n}\n\nvoid CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::string& hint)\n{\n    CSMPrefs::Category& windows = CSMPrefs::State::get()[\"Windows\"];\n\n    bool isReferenceable = id.getClass() == CSMWorld::UniversalId::Class_RefRecord;\n\n    // User setting to reuse sub views (on a per top level view basis)\n    if (windows[\"reuse\"].isTrue())\n    {\n        for (SubView *sb : mSubViews)\n        {\n            bool isSubViewReferenceable =\n                sb->getUniversalId().getType() == CSMWorld::UniversalId::Type_Referenceable;\n\n            if((isReferenceable && isSubViewReferenceable && id.getId() == sb->getUniversalId().getId())\n               ||\n               (!isReferenceable && id == sb->getUniversalId()))\n            {\n                sb->setFocus();\n                if (!hint.empty())\n                    sb->useHint (hint);\n                return;\n            }\n        }\n    }\n\n    if (mScroll)\n        QObject::connect(mScroll->horizontalScrollBar(),\n            SIGNAL(rangeChanged(int,int)), this, SLOT(moveScrollBarToEnd(int,int)));\n\n    // User setting for limiting the number of sub views per top level view.\n    // Automatically open a new top level view if this number is exceeded\n    //\n    // If the sub view limit setting is one, the sub view title bar is hidden and the\n    // text in the main title bar is adjusted accordingly\n    if(mSubViews.size() >= windows[\"max-subviews\"].toInt()) // create a new top level view\n    {\n        mViewManager.addView(mDocument, id, hint);\n\n        return;\n    }\n\n    SubView *view = nullptr;\n    if(isReferenceable)\n    {\n        view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument);\n    }\n    else\n    {\n        view = mSubViewFactory.makeSubView (id, *mDocument);\n    }\n    assert(view);\n    view->setParent(this);\n    view->setEditLock (mDocument->getState() & CSMDoc::State_Locked);\n    mSubViews.append(view); // only after assert\n\n    int minWidth = windows[\"minimum-width\"].toInt();\n    view->setMinimumWidth (minWidth);\n\n    view->setStatusBar (mShowStatusBar->isChecked());\n\n    // Work out how to deal with additional subviews\n    //\n    // Policy for \"Grow then Scroll\":\n    //\n    // - Increase the horizontal width of the mainwindow until it becomes greater than or equal\n    //   to the screen (monitor) width.\n    // - Move the mainwindow position sideways if necessary to fit within the screen.\n    // - Any more additions increases the size of the mSubViewWindow (horizontal scrollbar\n    //   should become visible)\n    // - Move the scroll bar to the newly added subview\n    //\n    mScrollbarOnly = windows[\"mainwindow-scrollbar\"].toString() == \"Scrollbar Only\";\n\n    updateWidth(windows[\"grow-limit\"].isTrue(), minWidth);\n\n    mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);\n\n    updateSubViewIndices();\n\n    connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this,\n        SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&)));\n\n    connect (view, SIGNAL (closeRequest (SubView *)), this, SLOT (closeRequest (SubView *)));\n\n    connect (view, SIGNAL (updateTitle()), this, SLOT (updateTitle()));\n\n    connect (view, SIGNAL (updateSubViewIndices (SubView *)),\n        this, SLOT (updateSubViewIndices (SubView *)));\n\n    CSVWorld::TableSubView* tableView = dynamic_cast<CSVWorld::TableSubView*>(view);\n    if (tableView)\n    {\n        connect (this, SIGNAL (requestFocus (const std::string&)),\n            tableView, SLOT (requestFocus (const std::string&)));\n    }\n\n    CSVWorld::SceneSubView* sceneView = dynamic_cast<CSVWorld::SceneSubView*>(view);\n    if (sceneView)\n    {\n        connect(sceneView, SIGNAL(requestFocus(const std::string&)),\n                this, SLOT(onRequestFocus(const std::string&)));\n    }\n\n    view->show();\n\n    if (!hint.empty())\n        view->useHint (hint);\n}\n\nvoid CSVDoc::View::moveScrollBarToEnd(int min, int max)\n{\n    if (mScroll)\n    {\n        mScroll->horizontalScrollBar()->setValue(max);\n\n        QObject::disconnect(mScroll->horizontalScrollBar(),\n            SIGNAL(rangeChanged(int,int)), this, SLOT(moveScrollBarToEnd(int,int)));\n    }\n}\n\nvoid CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==\"Windows/hide-subview\")\n        updateSubViewIndices (nullptr);\n    else if (*setting==\"Windows/mainwindow-scrollbar\")\n    {\n        if (setting->toString()!=\"Grow Only\")\n        {\n            if (mScroll)\n            {\n                if (setting->toString()==\"Scrollbar Only\")\n                {\n                    mScrollbarOnly = true;\n                    mSubViewWindow.setMinimumWidth(0);\n                }\n                else if (mScrollbarOnly)\n                {\n                    mScrollbarOnly = false;\n                    updateScrollbar();\n                }\n            }\n            else\n            {\n                createScrollArea();\n            }\n        }\n        else  if (mScroll)\n        {\n            mScroll->takeWidget();\n            setCentralWidget (&mSubViewWindow);\n            mScroll->deleteLater();\n            mScroll = nullptr;\n        }\n    }\n}\n\nvoid CSVDoc::View::newView()\n{\n    mViewManager.addView (mDocument);\n}\n\nvoid CSVDoc::View::save()\n{\n    mDocument->save();\n}\n\nvoid CSVDoc::View::openHelp()\n{\n    Misc::HelpViewer::openHelp(\"manuals/openmw-cs/index.html\");\n}\n\nvoid CSVDoc::View::tutorial()\n{\n    Misc::HelpViewer::openHelp(\"manuals/openmw-cs/tour.html\");\n}\n\nvoid CSVDoc::View::infoAbout()\n{\n    // Get current OpenMW version\n    QString versionInfo = (Version::getOpenmwVersionDescription(mDocument->getResourceDir().string())+\n#if defined(__x86_64__) || defined(_M_X64)\n    \" (64-bit)\").c_str();\n#else\n    \" (32-bit)\").c_str();\n#endif\n\n    // Get current year\n    time_t now = time(nullptr);\n    struct tm tstruct;\n    char copyrightInfo[40];\n    tstruct = *localtime(&now);\n    strftime(copyrightInfo, sizeof(copyrightInfo), \"Copyright © 2008-%Y OpenMW Team\", &tstruct);\n\n    QString aboutText = QString(\n    \"<p style=\\\"white-space: pre-wrap;\\\">\"\n    \"<b><h2>OpenMW Construction Set</h2></b>\"\n    \"%1\\n\\n\"\n    \"%2\\n\\n\"\n    \"%3\\n\\n\"\n    \"<table>\"\n    \"<tr><td>%4</td><td><a href=\\\"https://openmw.org\\\">https://openmw.org</a></td></tr>\"\n    \"<tr><td>%5</td><td><a href=\\\"https://forum.openmw.org\\\">https://forum.openmw.org</a></td></tr>\"\n    \"<tr><td>%6</td><td><a href=\\\"https://gitlab.com/OpenMW/openmw/issues\\\">https://gitlab.com/OpenMW/openmw/issues</a></td></tr>\"\n    \"<tr><td>%7</td><td><a href=\\\"https://web.libera.chat/#openmw\\\">ircs://irc.libera.chat/#openmw</a></td></tr>\"\n    \"</table>\"\n    \"</p>\")\n    .arg(versionInfo\n        , tr(\"OpenMW-CS is a content file editor for OpenMW, a modern, free and open source game engine.\")\n        , tr(copyrightInfo)\n        , tr(\"Home Page:\")\n        , tr(\"Forum:\")\n        , tr(\"Bug Tracker:\")\n        , tr(\"IRC:\"));\n\n    QMessageBox::about(this, \"About OpenMW-CS\", aboutText);\n}\n\nvoid CSVDoc::View::infoAboutQt()\n{\n    QMessageBox::aboutQt(this);\n}\n\nvoid CSVDoc::View::verify()\n{\n    addSubView (mDocument->verify());\n}\n\nvoid CSVDoc::View::addGlobalsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Globals);\n}\n\nvoid CSVDoc::View::addGmstsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Gmsts);\n}\n\nvoid CSVDoc::View::addSkillsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Skills);\n}\n\nvoid CSVDoc::View::addClassesSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Classes);\n}\n\nvoid CSVDoc::View::addFactionsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Factions);\n}\n\nvoid CSVDoc::View::addRacesSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Races);\n}\n\nvoid CSVDoc::View::addSoundsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Sounds);\n}\n\nvoid CSVDoc::View::addScriptsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Scripts);\n}\n\nvoid CSVDoc::View::addRegionsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Regions);\n}\n\nvoid CSVDoc::View::addBirthsignsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Birthsigns);\n}\n\nvoid CSVDoc::View::addSpellsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Spells);\n}\n\nvoid CSVDoc::View::addCellsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Cells);\n}\n\nvoid CSVDoc::View::addReferenceablesSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Referenceables);\n}\n\nvoid CSVDoc::View::addReferencesSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_References);\n}\n\nvoid CSVDoc::View::addRegionMapSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_RegionMap);\n}\n\nvoid CSVDoc::View::addFiltersSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Filters);\n}\n\nvoid CSVDoc::View::addTopicsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Topics);\n}\n\nvoid CSVDoc::View::addJournalsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Journals);\n}\n\nvoid CSVDoc::View::addTopicInfosSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_TopicInfos);\n}\n\nvoid CSVDoc::View::addJournalInfosSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_JournalInfos);\n}\n\nvoid CSVDoc::View::addEnchantmentsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Enchantments);\n}\n\nvoid CSVDoc::View::addBodyPartsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_BodyParts);\n}\n\nvoid CSVDoc::View::addSoundGensSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_SoundGens);\n}\n\nvoid CSVDoc::View::addMeshesSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Meshes);\n}\n\nvoid CSVDoc::View::addIconsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Icons);\n}\n\nvoid CSVDoc::View::addMusicsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Musics);\n}\n\nvoid CSVDoc::View::addSoundsResSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_SoundsRes);\n}\n\nvoid CSVDoc::View::addMagicEffectsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_MagicEffects);\n}\n\nvoid CSVDoc::View::addTexturesSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Textures);\n}\n\nvoid CSVDoc::View::addVideosSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Videos);\n}\n\nvoid CSVDoc::View::addDebugProfilesSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_DebugProfiles);\n}\n\nvoid CSVDoc::View::addRunLogSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_RunLog);\n}\n\nvoid CSVDoc::View::addLandsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Lands);\n}\n\nvoid CSVDoc::View::addLandTexturesSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_LandTextures);\n}\n\nvoid CSVDoc::View::addPathgridSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_Pathgrids);\n}\n\nvoid CSVDoc::View::addStartScriptsSubView()\n{\n    addSubView (CSMWorld::UniversalId::Type_StartScripts);\n}\n\nvoid CSVDoc::View::addSearchSubView()\n{\n    addSubView (mDocument->newSearch());\n}\n\nvoid CSVDoc::View::addMetaDataSubView()\n{\n    addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_MetaData, \"sys::meta\"));\n}\n\nvoid CSVDoc::View::abortOperation (int type)\n{\n    mDocument->abortOperation (type);\n    updateActions();\n}\n\nCSVDoc::Operations *CSVDoc::View::getOperations() const\n{\n    return mOperations;\n}\n\nvoid CSVDoc::View::exit()\n{\n    emit exitApplicationRequest (this);\n}\n\nvoid CSVDoc::View::resizeViewWidth (int width)\n{\n    if (width >= 0)\n        resize (width, geometry().height());\n}\n\nvoid CSVDoc::View::resizeViewHeight (int height)\n{\n    if (height >= 0)\n        resize (geometry().width(), height);\n}\n\nvoid CSVDoc::View::toggleShowStatusBar (bool show)\n{\n    for (QObject *view : mSubViewWindow.children())\n    {\n        if (CSVDoc::SubView *subView = dynamic_cast<CSVDoc::SubView *> (view))\n            subView->setStatusBar (show);\n    }\n}\n\nvoid CSVDoc::View::toggleStatusBar(bool checked)\n{\n    mShowStatusBar->setChecked(checked);\n}\n\nvoid CSVDoc::View::loadErrorLog()\n{\n    addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_LoadErrorLog, 0));\n}\n\nvoid CSVDoc::View::run (const std::string& profile, const std::string& startupInstruction)\n{\n    mDocument->startRunning (profile, startupInstruction);\n}\n\nvoid CSVDoc::View::stop()\n{\n    mDocument->stopRunning();\n}\n\nvoid CSVDoc::View::closeRequest (SubView *subView)\n{\n    CSMPrefs::Category& windows = CSMPrefs::State::get()[\"Windows\"];\n\n    if (mSubViews.size()>1 || mViewTotal<=1 || !windows[\"hide-subview\"].isTrue())\n    {\n        subView->deleteLater();\n        mSubViews.removeOne (subView);\n    }\n    else if (mViewManager.closeRequest (this))\n        mViewManager.removeDocAndView (mDocument);\n}\n\nvoid CSVDoc::View::updateScrollbar()\n{\n    QRect rect;\n    QWidget *topLevel = QApplication::topLevelAt(pos());\n    if (topLevel)\n        rect = topLevel->rect();\n    else\n        rect = this->rect();\n\n    int newWidth = 0;\n    for (int i = 0; i < mSubViews.size(); ++i)\n    {\n        newWidth += mSubViews[i]->width();\n    }\n\n    int frameWidth = frameGeometry().width() - width();\n\n    if ((newWidth+frameWidth) >= rect.width())\n        mSubViewWindow.setMinimumWidth(newWidth);\n    else\n        mSubViewWindow.setMinimumWidth(0);\n}\n\nvoid CSVDoc::View::merge()\n{\n    emit mergeDocument (mDocument);\n}\n\nvoid CSVDoc::View::updateWidth(bool isGrowLimit, int minSubViewWidth)\n{\n    QDesktopWidget *dw = QApplication::desktop();\n    QRect rect;\n    if (isGrowLimit)\n        rect = dw->screenGeometry(this);\n    else\n        rect = QGuiApplication::screens().at(dw->screenNumber(this))->geometry();\n\n    if (!mScrollbarOnly && mScroll && mSubViews.size() > 1)\n    {\n        int newWidth = width()+minSubViewWidth;\n        int frameWidth = frameGeometry().width() - width();\n        if (newWidth+frameWidth <= rect.width())\n        {\n            resize(newWidth, height());\n            // WARNING: below code assumes that new subviews are added to the right\n            if (x() > rect.width()-(newWidth+frameWidth))\n                move(rect.width()-(newWidth+frameWidth), y()); // shift left to stay within the screen\n        }\n        else\n        {\n            // full width\n            resize(rect.width()-frameWidth, height());\n            mSubViewWindow.setMinimumWidth(mSubViewWindow.width()+minSubViewWidth);\n            move(0, y());\n        }\n    }\n}\n\nvoid CSVDoc::View::createScrollArea()\n{\n    mScroll = new QScrollArea(this);\n    mScroll->setWidgetResizable(true);\n    mScroll->setWidget(&mSubViewWindow);\n    setCentralWidget(mScroll);\n}\n\nvoid CSVDoc::View::onRequestFocus (const std::string& id)\n{\n    if(CSMPrefs::get()[\"3D Scene Editing\"][\"open-list-view\"].isTrue())\n    {\n        addReferencesSubView();\n        emit requestFocus(id);\n    }\n    else\n    {\n        addSubView(CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Reference, id));\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/view.hpp",
    "content": "#ifndef CSV_DOC_VIEW_H\n#define CSV_DOC_VIEW_H\n\n#include <vector>\n#include <map>\n\n#include <QMainWindow>\n\n#include \"subviewfactory.hpp\"\n\nclass QAction;\nclass QDockWidget;\nclass QScrollArea;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class UniversalId;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVDoc\n{\n    class ViewManager;\n    class Operations;\n    class GlobalDebugProfileMenu;\n\n    class View : public QMainWindow\n    {\n            Q_OBJECT\n\n            ViewManager& mViewManager;\n            CSMDoc::Document *mDocument;\n            int mViewIndex;\n            int mViewTotal;\n            QList<SubView *> mSubViews;\n            QAction *mUndo;\n            QAction *mRedo;\n            QAction *mSave;\n            QAction *mVerify;\n            QAction *mShowStatusBar;\n            QAction *mStopDebug;\n            QAction *mMerge;\n            std::vector<QAction *> mEditingActions;\n            Operations *mOperations;\n            SubViewFactoryManager mSubViewFactory;\n            QMainWindow mSubViewWindow;\n            GlobalDebugProfileMenu *mGlobalDebugProfileMenu;\n            QScrollArea *mScroll;\n            bool mScrollbarOnly;\n\n\n            // not implemented\n            View (const View&);\n            View& operator= (const View&);\n\n        private:\n\n            void closeEvent (QCloseEvent *event) override;\n\n            QAction* createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName);\n            QAction* createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName);\n\n            void setupFileMenu();\n\n            void setupEditMenu();\n\n            void setupViewMenu();\n\n            void setupWorldMenu();\n\n            void setupMechanicsMenu();\n\n            void setupCharacterMenu();\n\n            void setupAssetsMenu();\n\n            void setupDebugMenu();\n\n            void setupHelpMenu();\n\n            void setupUi();\n\n            void setupShortcut(const char* name, QAction* action);\n\n            void updateActions();\n\n            void exitApplication();\n\n            /// User preference function\n            void resizeViewWidth (int width);\n\n            /// User preference function\n            void resizeViewHeight (int height);\n\n            void updateScrollbar();\n            void updateWidth(bool isGrowLimit, int minSubViewWidth);\n            void createScrollArea();\n        public:\n\n            View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews);\n\n            ///< The ownership of \\a document is not transferred to *this.\n\n            virtual ~View();\n\n            const CSMDoc::Document *getDocument() const;\n\n            CSMDoc::Document *getDocument();\n\n            void setIndex (int viewIndex, int totalViews);\n\n            void updateDocumentState();\n\n            void updateProgress (int current, int max, int type, int threads);\n\n            void toggleStatusBar(bool checked);\n\n            Operations *getOperations() const;\n\n        signals:\n\n            void newGameRequest();\n\n            void newAddonRequest();\n\n            void loadDocumentRequest();\n\n            void exitApplicationRequest (CSVDoc::View *view);\n\n            void editSettingsRequest();\n\n            void mergeDocument (CSMDoc::Document *document);\n\n            void requestFocus (const std::string& id);\n\n        public slots:\n\n            void addSubView (const CSMWorld::UniversalId& id, const std::string& hint = \"\");\n            ///< \\param hint Suggested view point (e.g. coordinates in a 3D scene or a line number\n            /// in a script).\n\n            void abortOperation (int type);\n\n            void updateTitle();\n\n            // called when subviews are added or removed\n            void updateSubViewIndices (SubView *view = nullptr);\n\n        private slots:\n\n            void settingChanged (const CSMPrefs::Setting *setting);\n\n            void undoActionChanged();\n\n            void redoActionChanged();\n\n            void newView();\n\n            void save();\n\n            void exit();\n\n            static void openHelp();\n\n            static void tutorial();\n\n            void infoAbout();\n\n            void infoAboutQt();\n\n            void verify();\n\n            void addGlobalsSubView();\n\n            void addGmstsSubView();\n\n            void addSkillsSubView();\n\n            void addClassesSubView();\n\n            void addFactionsSubView();\n\n            void addRacesSubView();\n\n            void addSoundsSubView();\n\n            void addScriptsSubView();\n\n            void addRegionsSubView();\n\n            void addBirthsignsSubView();\n\n            void addSpellsSubView();\n\n            void addCellsSubView();\n\n            void addReferenceablesSubView();\n\n            void addReferencesSubView();\n\n            void addRegionMapSubView();\n\n            void addFiltersSubView();\n\n            void addTopicsSubView();\n\n            void addJournalsSubView();\n\n            void addTopicInfosSubView();\n\n            void addJournalInfosSubView();\n\n            void addEnchantmentsSubView();\n\n            void addBodyPartsSubView();\n\n            void addSoundGensSubView();\n\n            void addMagicEffectsSubView();\n\n            void addMeshesSubView();\n\n            void addIconsSubView();\n\n            void addMusicsSubView();\n\n            void addSoundsResSubView();\n\n            void addTexturesSubView();\n\n            void addVideosSubView();\n\n            void addDebugProfilesSubView();\n\n            void addRunLogSubView();\n\n            void addLandsSubView();\n\n            void addLandTexturesSubView();\n\n            void addPathgridSubView();\n\n            void addStartScriptsSubView();\n\n            void addSearchSubView();\n\n            void addMetaDataSubView();\n\n            void toggleShowStatusBar (bool show);\n\n            void loadErrorLog();\n\n            void run (const std::string& profile, const std::string& startupInstruction = \"\");\n\n            void stop();\n\n            void closeRequest (SubView *subView);\n\n            void moveScrollBarToEnd(int min, int max);\n\n            void merge();\n\n            void onRequestFocus (const std::string& id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/doc/viewmanager.cpp",
    "content": "#include \"viewmanager.hpp\"\n\n#include <vector>\n#include <map>\n\n#include <QApplication>\n#include <QDesktopWidget>\n#include <QMessageBox>\n#include <QPushButton>\n\n#include \"../../model/doc/documentmanager.hpp\"\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/idcompletionmanager.hpp\"\n\n#include \"../../model/prefs/state.hpp\"\n\n#include \"../world/util.hpp\"\n#include \"../world/enumdelegate.hpp\"\n#include \"../world/vartypedelegate.hpp\"\n#include \"../world/recordstatusdelegate.hpp\"\n#include \"../world/idtypedelegate.hpp\"\n#include \"../world/idcompletiondelegate.hpp\"\n#include \"../world/colordelegate.hpp\"\n\n#include \"view.hpp\"\n\nvoid CSVDoc::ViewManager::updateIndices()\n{\n    std::map<CSMDoc::Document *, std::pair<int, int> > documents;\n\n    for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)\n    {\n        std::map<CSMDoc::Document *, std::pair<int, int> >::iterator document = documents.find ((*iter)->getDocument());\n\n        if (document==documents.end())\n            document =\n                documents.insert (\n                    std::make_pair ((*iter)->getDocument(), std::make_pair (0, countViews ((*iter)->getDocument())))).\n                first;\n\n        (*iter)->setIndex (document->second.first++, document->second.second);\n    }\n}\n\nCSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)\n    : mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false)\n{\n    mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection;\n\n    mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType,\n        new CSVWorld::VarTypeDelegateFactory (ESM::VT_None, ESM::VT_String, ESM::VT_Int, ESM::VT_Float));\n\n    mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType,\n        new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float));\n\n    mDelegateFactories->add (CSMWorld::ColumnBase::Display_RecordState,\n        new CSVWorld::RecordStatusDelegateFactory());\n\n    mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType,\n        new CSVWorld::IdTypeDelegateFactory());\n\n    mDelegateFactories->add (CSMWorld::ColumnBase::Display_Colour,\n        new CSVWorld::ColorDelegateFactory());\n\n    std::vector<CSMWorld::ColumnBase::Display> idCompletionColumns = CSMWorld::IdCompletionManager::getDisplayTypes();\n    for (std::vector<CSMWorld::ColumnBase::Display>::const_iterator current = idCompletionColumns.begin();\n         current != idCompletionColumns.end();\n         ++current)\n    {\n        mDelegateFactories->add(*current, new CSVWorld::IdCompletionDelegateFactory());\n    }\n\n    struct Mapping\n    {\n        CSMWorld::ColumnBase::Display mDisplay;\n        CSMWorld::Columns::ColumnId mColumnId;\n        bool mAllowNone;\n    };\n\n    static const Mapping sMapping[] =\n    {\n        { CSMWorld::ColumnBase::Display_Specialisation, CSMWorld::Columns::ColumnId_Specialisation, false },\n        { CSMWorld::ColumnBase::Display_Attribute, CSMWorld::Columns::ColumnId_Attribute, true },\n        { CSMWorld::ColumnBase::Display_SpellType, CSMWorld::Columns::ColumnId_SpellType, false },\n        { CSMWorld::ColumnBase::Display_ApparatusType, CSMWorld::Columns::ColumnId_ApparatusType, false },\n        { CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false },\n        { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false },\n        { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false },\n        { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false },\n        { CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false },\n        { CSMWorld::ColumnBase::Display_QuestStatusType, CSMWorld::Columns::ColumnId_QuestStatusType, false },\n        { CSMWorld::ColumnBase::Display_EnchantmentType, CSMWorld::Columns::ColumnId_EnchantmentType, false },\n        { CSMWorld::ColumnBase::Display_BodyPartType, CSMWorld::Columns::ColumnId_BodyPartType, false },\n        { CSMWorld::ColumnBase::Display_MeshType, CSMWorld::Columns::ColumnId_MeshType, false },\n        { CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true },\n        { CSMWorld::ColumnBase::Display_SoundGeneratorType, CSMWorld::Columns::ColumnId_SoundGeneratorType, false },\n        { CSMWorld::ColumnBase::Display_School, CSMWorld::Columns::ColumnId_School, false },\n        { CSMWorld::ColumnBase::Display_SkillId, CSMWorld::Columns::ColumnId_Skill, true },\n        { CSMWorld::ColumnBase::Display_EffectRange, CSMWorld::Columns::ColumnId_EffectRange, false },\n        { CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false },\n        { CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false },\n        { CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false },\n        { CSMWorld::ColumnBase::Display_InfoCondFunc, CSMWorld::Columns::ColumnId_InfoCondFunc, false },\n        { CSMWorld::ColumnBase::Display_InfoCondComp, CSMWorld::Columns::ColumnId_InfoCondComp, false },\n        { CSMWorld::ColumnBase::Display_IngredEffectId, CSMWorld::Columns::ColumnId_EffectId, true },\n        { CSMWorld::ColumnBase::Display_EffectSkill, CSMWorld::Columns::ColumnId_Skill, false },\n        { CSMWorld::ColumnBase::Display_EffectAttribute, CSMWorld::Columns::ColumnId_Attribute, false },\n        { CSMWorld::ColumnBase::Display_BookType, CSMWorld::Columns::ColumnId_BookType, false },\n        { CSMWorld::ColumnBase::Display_BloodType, CSMWorld::Columns::ColumnId_BloodType, false },\n        { CSMWorld::ColumnBase::Display_EmitterType, CSMWorld::Columns::ColumnId_EmitterType, false },\n        { CSMWorld::ColumnBase::Display_GenderNpc, CSMWorld::Columns::ColumnId_Gender, false }\n    };\n\n    for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)\n        mDelegateFactories->add (sMapping[i].mDisplay, new CSVWorld::EnumDelegateFactory (\n            CSMWorld::Columns::getEnums (sMapping[i].mColumnId), sMapping[i].mAllowNone));\n\n    connect (&mDocumentManager, SIGNAL (loadRequest (CSMDoc::Document *)),\n        &mLoader, SLOT (add (CSMDoc::Document *)));\n\n    connect (\n        &mDocumentManager, SIGNAL (loadingStopped (CSMDoc::Document *, bool, const std::string&)),\n        &mLoader, SLOT (loadingStopped (CSMDoc::Document *, bool, const std::string&)));\n\n    connect (\n        &mDocumentManager, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)),\n        &mLoader, SLOT (nextStage (CSMDoc::Document *, const std::string&, int)));\n\n    connect (\n        &mDocumentManager, SIGNAL (nextRecord (CSMDoc::Document *, int)),\n        &mLoader, SLOT (nextRecord (CSMDoc::Document *, int)));\n\n    connect (\n        &mDocumentManager, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)),\n        &mLoader, SLOT (loadMessage (CSMDoc::Document *, const std::string&)));\n\n    connect (\n        &mLoader, SIGNAL (cancel (CSMDoc::Document *)),\n        &mDocumentManager, SIGNAL (cancelLoading (CSMDoc::Document *)));\n\n    connect (\n        &mLoader, SIGNAL (close (CSMDoc::Document *)),\n        &mDocumentManager, SLOT (removeDocument (CSMDoc::Document *)));\n}\n\nCSVDoc::ViewManager::~ViewManager()\n{\n    delete mDelegateFactories;\n\n    for (std::vector<View *>::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)\n        delete *iter;\n}\n\nCSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document)\n{\n    if (countViews (document)==0)\n    {\n        // new document\n        connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)),\n            this, SLOT (documentStateChanged (int, CSMDoc::Document *)));\n\n        connect (document, SIGNAL (progress (int, int, int, int, CSMDoc::Document *)),\n            this, SLOT (progress (int, int, int, int, CSMDoc::Document *)));\n    }\n\n    View *view = new View (*this, document, countViews (document)+1);\n\n    mViews.push_back (view);\n\n    view->toggleStatusBar (CSMPrefs::get()[\"Windows\"][\"show-statusbar\"].isTrue());\n    view->show();\n\n    connect (view, SIGNAL (newGameRequest ()), this, SIGNAL (newGameRequest()));\n    connect (view, SIGNAL (newAddonRequest ()), this, SIGNAL (newAddonRequest()));\n    connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest()));\n    connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest()));\n    connect (view, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SIGNAL (mergeDocument (CSMDoc::Document *)));\n\n    updateIndices();\n\n    return view;\n}\n\nCSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document, const CSMWorld::UniversalId& id, const std::string& hint)\n{\n    View* view = addView(document);\n    view->addSubView(id, hint);\n\n    return view;\n}\n\nint CSVDoc::ViewManager::countViews (const CSMDoc::Document *document) const\n{\n    int count = 0;\n\n    for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)\n        if ((*iter)->getDocument()==document)\n            ++count;\n\n    return count;\n}\n\nbool CSVDoc::ViewManager::closeRequest (View *view)\n{\n    std::vector<View *>::iterator iter = std::find (mViews.begin(), mViews.end(), view);\n\n    bool continueWithClose = false;\n\n    if (iter!=mViews.end())\n    {\n        bool last = countViews (view->getDocument())<=1;\n\n        if (last)\n            continueWithClose = notifySaveOnClose (view);\n        else\n        {\n            (*iter)->deleteLater();\n            mViews.erase (iter);\n\n            updateIndices();\n        }\n    }\n\n    return continueWithClose;\n}\n\n// NOTE: This method assumes that it is called only if the last document\nvoid CSVDoc::ViewManager::removeDocAndView (CSMDoc::Document *document)\n{\n    for (std::vector<View *>::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)\n    {\n        // the first match should also be the only match\n        if((*iter)->getDocument() == document)\n        {\n            mDocumentManager.removeDocument(document);\n            (*iter)->deleteLater();\n            mViews.erase (iter);\n\n            updateIndices();\n            return;\n        }\n    }\n}\n\nbool CSVDoc::ViewManager::notifySaveOnClose (CSVDoc::View *view)\n{\n    bool result = true;\n    CSMDoc::Document *document = view->getDocument();\n\n    //notify user of saving in progress\n    if ( (document->getState() & CSMDoc::State_Saving) )\n        result = showSaveInProgressMessageBox (view);\n\n    //notify user of unsaved changes and process response\n    else if ( document->getState() & CSMDoc::State_Modified)\n        result = showModifiedDocumentMessageBox (view);\n\n    return result;\n}\n\nbool CSVDoc::ViewManager::showModifiedDocumentMessageBox (CSVDoc::View *view)\n{\n    emit closeMessageBox();\n\n    QMessageBox messageBox(view);\n    CSMDoc::Document *document = view->getDocument();\n\n    messageBox.setWindowTitle (QString::fromUtf8(document->getSavePath().filename().string().c_str()));\n    messageBox.setText (\"The document has been modified.\");\n    messageBox.setInformativeText (\"Do you want to save your changes?\");\n    messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);\n    messageBox.setDefaultButton (QMessageBox::Save);\n    messageBox.setWindowModality (Qt::NonModal);\n    messageBox.hide();\n    messageBox.show();\n\n    bool retVal = true;\n\n    connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close()));\n\n    connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));\n\n    mUserWarned = true;\n    int response = messageBox.exec();\n    mUserWarned = false;\n\n    switch (response)\n    {\n        case QMessageBox::Save:\n\n            document->save();\n            mExitOnSaveStateChange = true;\n            retVal = false;\n        break;\n\n        case QMessageBox::Discard:\n\n            disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));\n        break;\n\n        case QMessageBox::Cancel:\n\n            //disconnect to prevent unintended view closures\n            disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));\n            retVal = false;\n        break;\n\n        default:\n        break;\n\n    }\n\n    return retVal;\n}\n\nbool CSVDoc::ViewManager::showSaveInProgressMessageBox (CSVDoc::View *view)\n{\n    QMessageBox messageBox;\n    CSMDoc::Document *document = view->getDocument();\n\n    messageBox.setText (\"The document is currently being saved.\");\n    messageBox.setInformativeText(\"Do you want to close now and abort saving, or wait until saving has completed?\");\n\n    QPushButton* waitButton = messageBox.addButton (tr(\"Wait\"), QMessageBox::YesRole);\n    QPushButton* closeButton = messageBox.addButton (tr(\"Close Now\"), QMessageBox::RejectRole);\n    QPushButton* cancelButton = messageBox.addButton (tr(\"Cancel\"), QMessageBox::NoRole);\n\n    messageBox.setDefaultButton (waitButton);\n\n    bool retVal = true;\n\n    //Connections shut down message box if operation ends before user makes a decision.\n    connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));\n    connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close()));\n\n    //set / clear the user warned flag to indicate whether or not the message box is currently active.\n    mUserWarned = true;\n    messageBox.exec();\n    mUserWarned = false;\n\n    //if closed by the warning handler, defaults to the RejectRole button (closeButton)\n    if (messageBox.clickedButton() == waitButton)\n    {\n        //save the View iterator for shutdown after the save operation ends\n        mExitOnSaveStateChange = true;\n        retVal = false;\n    }\n\n    else if (messageBox.clickedButton() == closeButton)\n    {\n        //disconnect to avoid segmentation fault\n        disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));\n\n        view->abortOperation(CSMDoc::State_Saving);\n        mExitOnSaveStateChange = true;\n    }\n\n    else if (messageBox.clickedButton() == cancelButton)\n    {\n        //abort shutdown, allow save to complete\n        //disconnection to prevent unintended view closures\n        mExitOnSaveStateChange = false;\n        disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));\n        retVal = false;\n    }\n\n    return retVal;\n}\n\nvoid CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document)\n{\n    for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)\n            if ((*iter)->getDocument()==document)\n                (*iter)->updateDocumentState();\n}\n\nvoid CSVDoc::ViewManager::progress (int current, int max, int type, int threads, CSMDoc::Document *document)\n{\n    for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)\n            if ((*iter)->getDocument()==document)\n                (*iter)->updateProgress (current, max, type, threads);\n}\n\nvoid CSVDoc::ViewManager::onExitWarningHandler (int state, CSMDoc::Document *document)\n{\n    if ( !(state & CSMDoc::State_Saving) )\n    {\n        //if the user is being warned (message box is active), shut down the message box,\n        //as there is no save operation currently running\n        if ( mUserWarned )\n            emit closeMessageBox();\n\n        //otherwise, the user has closed the message box before the save operation ended.\n        //exit the application\n        else if (mExitOnSaveStateChange)\n            QApplication::instance()->exit();\n    }\n}\n\nbool CSVDoc::ViewManager::removeDocument (CSVDoc::View *view)\n{\n    if(!notifySaveOnClose(view))\n        return false;\n    else\n    {\n        // don't bother closing views or updating indicies, but remove from mViews\n        CSMDoc::Document * document = view->getDocument();\n        std::vector<View *> remainingViews;\n        std::vector<View *>::const_iterator iter = mViews.begin();\n        for (; iter!=mViews.end(); ++iter)\n        {\n            if(document == (*iter)->getDocument())\n                (*iter)->setVisible(false);\n            else\n                remainingViews.push_back(*iter);\n        }\n        mDocumentManager.removeDocument(document);\n        mViews = remainingViews;\n    }\n    return true;\n}\n\nvoid CSVDoc::ViewManager::exitApplication (CSVDoc::View *view)\n{\n    if(!removeDocument(view)) // close the current document first\n        return;\n\n    while(!mViews.empty()) // attempt to close all other documents\n    {\n        mViews.back()->activateWindow();\n        mViews.back()->raise(); // raise the window to alert the user\n        if(!removeDocument(mViews.back()))\n            return;\n    }\n    // Editor exits (via a signal) when the last document is deleted\n}\n"
  },
  {
    "path": "apps/opencs/view/doc/viewmanager.hpp",
    "content": "#ifndef CSV_DOC_VIEWMANAGER_H\n#define CSV_DOC_VIEWMANAGER_H\n\n#include <vector>\n\n#include <QObject>\n\n#include \"loader.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n    class DocumentManager;\n}\n\nnamespace CSVWorld\n{\n    class CommandDelegateFactoryCollection;\n}\n\nnamespace CSMWorld\n{\n    class UniversalId;\n}\n\nnamespace CSVDoc\n{\n    class View;\n\n    class ViewManager : public QObject\n    {\n            Q_OBJECT\n\n            CSMDoc::DocumentManager& mDocumentManager;\n            std::vector<View *> mViews;\n            CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories;\n            bool mExitOnSaveStateChange;\n            bool mUserWarned;\n            Loader mLoader;\n\n            // not implemented\n            ViewManager (const ViewManager&);\n            ViewManager& operator= (const ViewManager&);\n\n            void updateIndices();\n            bool notifySaveOnClose (View *view = nullptr);\n            bool showModifiedDocumentMessageBox (View *view);\n            bool showSaveInProgressMessageBox (View *view);\n            bool removeDocument(View *view);\n\n        public:\n\n            ViewManager (CSMDoc::DocumentManager& documentManager);\n\n            virtual ~ViewManager();\n\n            View *addView (CSMDoc::Document *document);\n            ///< The ownership of the returned view is not transferred.\n\n            View *addView (CSMDoc::Document *document, const CSMWorld::UniversalId& id, const std::string& hint);\n\n            int countViews (const CSMDoc::Document *document) const;\n            ///< Return number of views for \\a document.\n\n            bool closeRequest (View *view);\n            void removeDocAndView (CSMDoc::Document *document);\n\n        signals:\n\n            void newGameRequest();\n\n            void newAddonRequest();\n\n            void loadDocumentRequest();\n\n            void closeMessageBox();\n\n            void editSettingsRequest();\n\n            void mergeDocument (CSMDoc::Document *document);\n\n        public slots:\n\n            void exitApplication (CSVDoc::View *view);\n\n        private slots:\n\n            void documentStateChanged (int state, CSMDoc::Document *document);\n\n            void progress (int current, int max, int type, int threads, CSMDoc::Document *document);\n\n            void onExitWarningHandler(int state, CSMDoc::Document* document);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/filter/editwidget.cpp",
    "content": "#include \"editwidget.hpp\"\n\n#include <QAbstractItemModel>\n#include <QAction>\n#include <QContextMenuEvent>\n#include <QMenu>\n#include <QString>\n#include <QApplication>\n\n#include <components/misc/helpviewer.hpp>\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idtablebase.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/prefs/shortcut.hpp\"\n\nCSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent)\n: QLineEdit (parent), mParser (data), mIsEmpty(true)\n{\n    mPalette = palette();\n    connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));\n\n    const CSMWorld::IdTableBase *model =\n            static_cast<const CSMWorld::IdTableBase *> (data.getTableModel (CSMWorld::UniversalId::Type_Filters));\n\n    connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)),\n        this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)),\n        Qt::QueuedConnection);\n    connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),\n        this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)),\n        Qt::QueuedConnection);\n    connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (filterRowsInserted (const QModelIndex&, int, int)),\n        Qt::QueuedConnection);\n\n    mStateColumnIndex   = model->findColumnIndex(CSMWorld::Columns::ColumnId_Modification);\n    mDescColumnIndex    = model->findColumnIndex(CSMWorld::Columns::ColumnId_Description);\n\n    mHelpAction = new QAction (tr (\"Help\"), this);\n    connect (mHelpAction, SIGNAL (triggered()), this, SLOT (openHelp()));\n    mHelpAction->setIcon(QIcon(\":/info.png\"));\n    addAction (mHelpAction);\n    auto* openHelpShortcut = new CSMPrefs::Shortcut(\"help\", this);\n    openHelpShortcut->associateAction(mHelpAction);\n}\n\nvoid CSVFilter::EditWidget::textChanged (const QString& text)\n{\n    //no need to parse and apply filter if it was empty and now is empty too.\n    //e.g. - we modifiing content of filter with already opened some other (big) tables.\n    if (text.length() == 0){\n        if (mIsEmpty)\n            return;\n        else\n            mIsEmpty = true;\n    }else\n        mIsEmpty = false;\n\n    if (mParser.parse (text.toUtf8().constData()))\n    {\n        setPalette (mPalette);\n        emit filterChanged (mParser.getFilter());\n    }\n    else\n    {\n        QPalette palette (mPalette);\n        palette.setColor (QPalette::Text, Qt::red);\n        setPalette (palette);\n\n        /// \\todo improve error reporting; mark only the faulty part\n    }\n}\n\nvoid CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    for (int i = topLeft.column(); i <= bottomRight.column(); ++i)\n        if (i != mStateColumnIndex && i != mDescColumnIndex)\n            textChanged (text());\n}\n\nvoid CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end)\n{\n    textChanged (text());\n}\n\nvoid CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end)\n{\n    textChanged (text());\n}\n\nvoid CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource,\n        Qt::DropAction action)\n{\n    const unsigned count = filterSource.size();\n    bool multipleElements = false;\n\n    switch (count) //setting multipleElements;\n    {\n        case 0: //empty\n            return; //nothing to do here\n\n        case 1: //only single\n            multipleElements = false;\n            break;\n\n        default:\n            multipleElements = true;\n            break;\n    }\n\n    Qt::KeyboardModifiers key = QApplication::keyboardModifiers();\n    QString oldContent (text());\n\n    bool replaceMode = false;\n    std::string orAnd;\n\n    switch (key) //setting replaceMode and string used to glue expressions\n    {\n        case Qt::ShiftModifier:\n            orAnd = \"!or(\";\n            replaceMode = false;\n            break;\n\n        case Qt::ControlModifier:\n            orAnd = \"!and(\";\n            replaceMode = false;\n            break;\n\n        default:\n            replaceMode = true;\n            break;\n    }\n\n    if (oldContent.isEmpty() || !oldContent.contains (QRegExp (\"^!.*$\", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode\n    {\n        replaceMode = true;\n    }\n\n    if (!replaceMode)\n    {\n        oldContent.remove ('!');\n    }\n\n    std::stringstream ss;\n\n    if (multipleElements)\n    {\n        if (replaceMode)\n        {\n            ss<<\"!or(\";\n        } else {\n            ss << orAnd << oldContent.toUtf8().constData() << ',';\n        }\n\n        for (unsigned i = 0; i < count; ++i)\n        {\n            ss<<generateFilter (filterSource[i]);\n\n            if (i+1 != count)\n            {\n                ss<<\", \";\n            }\n        }\n\n        ss<<')';\n    } else {\n        if (!replaceMode)\n        {\n            ss << orAnd << oldContent.toUtf8().constData() <<',';\n        } else {\n            ss<<'!';\n        }\n\n        ss << generateFilter (filterSource[0]);\n\n        if (!replaceMode)\n        {\n            ss<<')';\n        }\n\n    }\n\n    if (ss.str().length() >4)\n    {\n        clear();\n        insert (QString::fromUtf8(ss.str().c_str()));\n    }\n}\n\nstd::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const\n{\n    const unsigned columns = seekedString.second.size();\n\n    bool multipleColumns = false;\n    switch (columns)\n    {\n    case 0: //empty\n        return \"\"; //no column to filter\n\n    case 1: //one column to look for\n        multipleColumns = false;\n        break;\n\n    default:\n        multipleColumns = true;\n        break;\n    }\n\n    std::stringstream ss;\n    if (multipleColumns)\n    {\n        ss<<\"or(\";\n        for (unsigned i = 0; i < columns; ++i)\n        {\n            ss<<\"string(\"<<'\"'<<seekedString.second[i]<<'\"'<<','<<'\"'<<seekedString.first<<'\"'<<')';\n            if (i+1 != columns)\n                ss<<',';\n        }\n        ss<<')';\n    } else {\n        ss<<\"string\"<<'('<<'\"'<<seekedString.second[0]<<\"\\\",\"<<'\"'<<seekedString.first<<\"\\\")\";\n    }\n\n    return ss.str();\n}\n\nvoid CSVFilter::EditWidget::contextMenuEvent(QContextMenuEvent *event)\n{\n    QMenu *menu = createStandardContextMenu();\n    menu->addAction(mHelpAction);\n    menu->exec(event->globalPos());\n    delete menu;\n}\n\nvoid CSVFilter::EditWidget::openHelp()\n{\n    Misc::HelpViewer::openHelp(\"manuals/openmw-cs/record-filters.html\");\n}\n\n"
  },
  {
    "path": "apps/opencs/view/filter/editwidget.hpp",
    "content": "#ifndef CSV_FILTER_EDITWIDGET_H\n#define CSV_FILTER_EDITWIDGET_H\n\n#include <QLineEdit>\n#include <QPalette>\n#include <QtCore/qnamespace.h>\n\n#include \"../../model/filter/parser.hpp\"\n#include \"../../model/filter/node.hpp\"\n\nclass QModelIndex;\n\nnamespace CSMWorld\n{\n    class Data;\n}\n\nnamespace CSVFilter\n{\n    class EditWidget : public QLineEdit\n    {\n            Q_OBJECT\n\n            CSMFilter::Parser mParser;\n            QPalette mPalette;\n            bool mIsEmpty;\n            int mStateColumnIndex;\n            int mDescColumnIndex;\n            QAction *mHelpAction;\n\n        public:\n\n            EditWidget (CSMWorld::Data& data, QWidget *parent = nullptr);\n\n            void createFilterRequest(std::vector<std::pair<std::string, std::vector<std::string> > >& filterSource,\n                                     Qt::DropAction action);\n\n        signals:\n\n            void filterChanged (std::shared_ptr<CSMFilter::Node> filter);\n\n    private:\n            std::string generateFilter(std::pair<std::string, std::vector<std::string> >& seekedString) const;\n            void contextMenuEvent (QContextMenuEvent *event) override;\n\n        private slots:\n\n            void textChanged (const QString& text);\n\n            void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void filterRowsRemoved (const QModelIndex& parent, int start, int end);\n\n            void filterRowsInserted (const QModelIndex& parent, int start, int end);\n\n            static void openHelp();\n\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/filter/filterbox.cpp",
    "content": "#include \"filterbox.hpp\"\n\n#include <QHBoxLayout>\n#include <QDragEnterEvent>\n\n#include \"recordfilterbox.hpp\"\n\n#include <apps/opencs/model/world/tablemimedata.hpp>\n\nCSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent)\n: QWidget (parent)\n{\n    QHBoxLayout *layout = new QHBoxLayout (this);\n\n    layout->setContentsMargins (0, 0, 0, 0);\n\n    mRecordFilterBox = new RecordFilterBox (data, this);\n\n    layout->addWidget (mRecordFilterBox);\n\n    setLayout (layout);\n\n    connect (mRecordFilterBox,\n        SIGNAL (filterChanged (std::shared_ptr<CSMFilter::Node>)),\n        this, SIGNAL (recordFilterChanged (std::shared_ptr<CSMFilter::Node>)));\n\n    setAcceptDrops(true);\n}\n\nvoid CSVFilter::FilterBox::setRecordFilter (const std::string& filter)\n{\n    mRecordFilterBox->setFilter (filter);\n}\n\nvoid CSVFilter::FilterBox::dropEvent (QDropEvent* event)\n{\n    const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n    if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped\n        return;\n\n    std::vector<CSMWorld::UniversalId> universalIdData = mime->getData();\n\n    emit recordDropped(universalIdData, event->proposedAction());\n}\n\nvoid CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event)\n{\n    event->acceptProposedAction();\n}\n\nvoid CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event)\n{\n    event->accept();\n}\n\nvoid CSVFilter::FilterBox::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource,\n                                                Qt::DropAction action)\n{\n    mRecordFilterBox->createFilterRequest(filterSource, action);\n}\n"
  },
  {
    "path": "apps/opencs/view/filter/filterbox.hpp",
    "content": "#ifndef CSV_FILTER_FILTERBOX_H\n#define CSV_FILTER_FILTERBOX_H\n\n#include <vector>\n\n#include <QWidget>\n#include <QtCore/qnamespace.h>\n\n#include \"../../model/filter/node.hpp\"\n#include \"../../model/world/universalid.hpp\"\n\nnamespace CSMWorld\n{\n    class Data;\n}\n\nnamespace CSVFilter\n{\n    class RecordFilterBox;\n\n    class FilterBox : public QWidget\n    {\n            Q_OBJECT\n\n            RecordFilterBox *mRecordFilterBox;\n\n        public:\n            FilterBox (CSMWorld::Data& data, QWidget *parent = nullptr);\n\n            void setRecordFilter (const std::string& filter);\n\n            void createFilterRequest(std::vector<std::pair<std::string, std::vector<std::string> > >& filterSource,\n                                     Qt::DropAction action);\n\n\n        private:\n            void dragEnterEvent (QDragEnterEvent* event) override;\n\n            void dropEvent (QDropEvent* event) override;\n\n            void dragMoveEvent(QDragMoveEvent *event) override;\n\n        signals:\n            void recordFilterChanged (std::shared_ptr<CSMFilter::Node> filter);\n            void recordDropped (std::vector<CSMWorld::UniversalId>& types, Qt::DropAction action);\n    };\n\n}\n\n#endif\n\n"
  },
  {
    "path": "apps/opencs/view/filter/recordfilterbox.cpp",
    "content": "#include \"recordfilterbox.hpp\"\n\n#include <QHBoxLayout>\n#include <QLabel>\n\n#include \"editwidget.hpp\"\n\nCSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent)\n: QWidget (parent)\n{\n    QHBoxLayout *layout = new QHBoxLayout (this);\n\n    layout->setContentsMargins (0, 6, 5, 0);\n\n    QLabel *label = new QLabel(\"Record Filter\", this);\n    label->setIndent(2);\n    layout->addWidget (label);\n\n    mEdit = new EditWidget (data, this);\n\n    layout->addWidget (mEdit);\n\n    setLayout (layout);\n\n    connect (\n        mEdit, SIGNAL (filterChanged (std::shared_ptr<CSMFilter::Node>)),\n        this, SIGNAL (filterChanged (std::shared_ptr<CSMFilter::Node>)));\n}\n\nvoid CSVFilter::RecordFilterBox::setFilter (const std::string& filter)\n{\n    mEdit->clear();\n    mEdit->setText (QString::fromUtf8 (filter.c_str()));\n}\n\nvoid CSVFilter::RecordFilterBox::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource,\n                                                      Qt::DropAction action)\n{\n    mEdit->createFilterRequest(filterSource, action);\n}\n"
  },
  {
    "path": "apps/opencs/view/filter/recordfilterbox.hpp",
    "content": "#ifndef CSV_FILTER_RECORDFILTERBOX_H\n#define CSV_FILTER_RECORDFILTERBOX_H\n\n#include <QWidget>\n#include <QtCore/qnamespace.h>\n\n#include <QHBoxLayout>\n\n#include \"../../model/filter/node.hpp\"\n\nnamespace CSMWorld\n{\n    class Data;\n}\n\nnamespace CSVFilter\n{\n    class EditWidget;\n\n    class RecordFilterBox : public QWidget\n    {\n            Q_OBJECT\n\n            EditWidget *mEdit;\n\n        public:\n\n            RecordFilterBox (CSMWorld::Data& data, QWidget *parent = nullptr);\n\n            void setFilter (const std::string& filter);\n\n            void useFilterRequest(const std::string& idOfFilter);\n\n            void createFilterRequest(std::vector<std::pair<std::string, std::vector<std::string> > >& filterSource,\n                                     Qt::DropAction action);\n\n        signals:\n\n            void filterChanged (std::shared_ptr<CSMFilter::Node> filter);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/prefs/contextmenulist.cpp",
    "content": "#include \"contextmenulist.hpp\"\n\n#include <QMenu>\n#include <QContextMenuEvent>\n#include <QMouseEvent>\n\n#include \"../../model/prefs/state.hpp\"\n\nCSVPrefs::ContextMenuList::ContextMenuList(QWidget* parent)\n    :QListWidget(parent)\n{\n}\n\nvoid CSVPrefs::ContextMenuList::contextMenuEvent(QContextMenuEvent* e)\n{\n    QMenu* menu = new QMenu();\n\n    menu->addAction(\"Reset category to default\", this, SLOT(resetCategory()));\n    menu->addAction(\"Reset all to default\", this, SLOT(resetAll()));\n\n    menu->exec(e->globalPos());\n    delete menu;\n}\n\nvoid CSVPrefs::ContextMenuList::mousePressEvent(QMouseEvent* e)\n{\n    // enable all buttons except right click\n    // This means that when right-clicking to enable the\n    // context menu, the page doesn't switch at the same time.\n    if (!(e->buttons() & Qt::RightButton))\n    {\n        QListWidget::mousePressEvent(e);\n    }\n}\n\nvoid CSVPrefs::ContextMenuList::resetCategory()\n{\n    CSMPrefs::State::get().resetCategory(currentItem()->text().toStdString());\n}\n\nvoid CSVPrefs::ContextMenuList::resetAll()\n{\n    CSMPrefs::State::get().resetAll();\n}\n"
  },
  {
    "path": "apps/opencs/view/prefs/contextmenulist.hpp",
    "content": "#ifndef CSV_PREFS_CONTEXTMENULIST_H\n#define CSV_PREFS_CONTEXTMENULIST_H\n\n#include <QListWidget>\n\nclass QContextMenuEvent;\nclass QMouseEvent;\n\nnamespace CSVPrefs\n{\n    class ContextMenuList : public QListWidget\n    {\n            Q_OBJECT\n    \n        public:\n    \n            ContextMenuList(QWidget* parent = nullptr);\n    \n        protected:\n    \n            void contextMenuEvent(QContextMenuEvent* e) override;\n\n            void mousePressEvent(QMouseEvent* e) override;\n    \n        private slots:\n    \n            void resetCategory();\n    \n            void resetAll();\n        };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/prefs/dialogue.cpp",
    "content": "#include \"dialogue.hpp\"\n\n#include <QApplication>\n#include <QDesktopWidget>\n#include <QSplitter>\n#include <QListWidget>\n#include <QStackedWidget>\n#include <QListWidgetItem>\n#include <QScreen>\n\n#include <components/debug/debuglog.hpp>\n\n#include \"../../model/prefs/state.hpp\"\n\n#include \"page.hpp\"\n#include \"keybindingpage.hpp\"\n#include \"contextmenulist.hpp\"\n\nvoid CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main)\n{\n    CSVPrefs::ContextMenuList* list = new CSVPrefs::ContextMenuList (main);\n    list->setMinimumWidth (50);\n    list->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);\n\n    list->setSelectionBehavior (QAbstractItemView::SelectItems);\n\n    main->addWidget (list);\n\n    QFontMetrics metrics (QApplication::font(list));\n\n    int maxWidth = 1;\n\n    for (CSMPrefs::State::Iterator iter = CSMPrefs::get().begin(); iter!=CSMPrefs::get().end();\n        ++iter)\n    {\n        QString label = QString::fromUtf8 (iter->second.getKey().c_str());\n\n        maxWidth = std::max (maxWidth, metrics.horizontalAdvance (label));\n\n        list->addItem (label);\n    }\n\n    list->setMaximumWidth (maxWidth + 10);\n\n    connect (list, SIGNAL (currentItemChanged (QListWidgetItem *, QListWidgetItem *)),\n        this, SLOT (selectionChanged (QListWidgetItem *, QListWidgetItem *)));\n}\n\nvoid CSVPrefs::Dialogue::buildContentArea (QSplitter *main)\n{\n    mContent = new QStackedWidget (main);\n    mContent->setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Expanding);\n\n    main->addWidget (mContent);\n}\n\nCSVPrefs::PageBase *CSVPrefs::Dialogue::makePage (const std::string& key)\n{\n    // special case page code goes here\n    if (key == \"Key Bindings\")\n        return new KeyBindingPage(CSMPrefs::get()[key], mContent);\n    else\n        return new Page (CSMPrefs::get()[key], mContent);\n}\n\nCSVPrefs::Dialogue::Dialogue()\n{\n    setWindowTitle (\"User Settings\");\n\n    setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);\n\n    setMinimumSize (600, 400);\n\n    QSplitter *main = new QSplitter (this);\n\n    setCentralWidget (main);\n    buildCategorySelector (main);\n    buildContentArea (main);\n}\n\nCSVPrefs::Dialogue::~Dialogue()\n{\n    try\n    {\n        if (isVisible())\n            CSMPrefs::State::get().save();\n    }\n    catch(const std::exception& e)\n    {\n        Log(Debug::Error) << \"Error in the destructor: \" << e.what();\n    }\n}\n\nvoid CSVPrefs::Dialogue::closeEvent (QCloseEvent *event)\n{\n    QMainWindow::closeEvent (event);\n    CSMPrefs::State::get().save();\n}\n\nvoid CSVPrefs::Dialogue::show()\n{\n    if (QWidget *active = QApplication::activeWindow())\n    {\n        // place at the centre of the window with focus\n        QSize size = active->size();\n        move (active->geometry().x()+(size.width() - frameGeometry().width())/2,\n            active->geometry().y()+(size.height() - frameGeometry().height())/2);\n    }\n    else\n    {\n        QRect scr = QGuiApplication::primaryScreen()->geometry();\n\n        // otherwise place at the centre of the screen\n        QPoint screenCenter = scr.center();\n\n        move (screenCenter - QPoint(frameGeometry().width()/2, frameGeometry().height()/2));\n    }\n\n    QWidget::show();\n}\n\nvoid CSVPrefs::Dialogue::selectionChanged (QListWidgetItem *current, QListWidgetItem *previous)\n{\n    if (current)\n    {\n        std::string key = current->text().toUtf8().data();\n\n        for (int i=0; i<mContent->count(); ++i)\n        {\n            PageBase& page = dynamic_cast<PageBase&> (*mContent->widget (i));\n\n            if (page.getCategory().getKey()==key)\n            {\n                mContent->setCurrentIndex (i);\n                return;\n            }\n        }\n\n        PageBase *page = makePage (key);\n        mContent->setCurrentIndex (mContent->addWidget (page));\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/prefs/dialogue.hpp",
    "content": "#ifndef CSV_PREFS_DIALOGUE_H\n#define CSV_PREFS_DIALOGUE_H\n\n#include <QMainWindow>\n\nclass QSplitter;\nclass QListWidget;\nclass QStackedWidget;\nclass QListWidgetItem;\n\nnamespace CSVPrefs\n{\n    class PageBase;\n\n    class Dialogue : public QMainWindow\n    {\n            Q_OBJECT\n\n            QStackedWidget *mContent;\n\n        private:\n\n            void buildCategorySelector (QSplitter *main);\n\n            void buildContentArea (QSplitter *main);\n\n            PageBase *makePage (const std::string& key);\n\n        public:\n\n            Dialogue();\n\n            virtual ~Dialogue();\n\n        protected:\n\n            void closeEvent (QCloseEvent *event) override;\n\n        public slots:\n\n            void show();\n\n        private slots:\n\n            void selectionChanged (QListWidgetItem *current, QListWidgetItem *previous);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/prefs/keybindingpage.cpp",
    "content": "#include \"keybindingpage.hpp\"\n\n#include <cassert>\n\n#include <QComboBox>\n#include <QGridLayout>\n#include <QPushButton>\n#include <QStackedLayout>\n#include <QVBoxLayout>\n\n#include \"../../model/prefs/setting.hpp\"\n#include \"../../model/prefs/category.hpp\"\n#include \"../../model/prefs/state.hpp\"\n\nnamespace CSVPrefs\n{\n    KeyBindingPage::KeyBindingPage(CSMPrefs::Category& category, QWidget* parent)\n        : PageBase(category, parent)\n        , mStackedLayout(nullptr)\n        , mPageLayout(nullptr)\n        , mPageSelector(nullptr)\n    {\n        // Need one widget for scroll area\n        QWidget* topWidget = new QWidget();\n        QVBoxLayout* topLayout = new QVBoxLayout(topWidget);\n\n        // Allows switching between \"pages\"\n        QWidget* stackedWidget = new QWidget();\n        mStackedLayout = new QStackedLayout(stackedWidget);\n\n        mPageSelector = new QComboBox();\n        connect(mPageSelector, SIGNAL(currentIndexChanged(int)), mStackedLayout, SLOT(setCurrentIndex(int)));\n\n        QFrame* lineSeparator = new QFrame(topWidget);\n        lineSeparator->setFrameShape(QFrame::HLine);\n        lineSeparator->setFrameShadow(QFrame::Sunken);\n\n        // Reset key bindings button\n        QPushButton* resetButton = new QPushButton (\"Reset to Defaults\", topWidget);\n        connect(resetButton, SIGNAL(clicked()), this, SLOT(resetKeyBindings()));\n\n        topLayout->addWidget(mPageSelector);\n        topLayout->addWidget(stackedWidget);\n        topLayout->addWidget(lineSeparator);\n        topLayout->addWidget(resetButton);\n        topLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);\n\n        // Add each option\n        for (CSMPrefs::Category::Iterator iter = category.begin(); iter!=category.end(); ++iter)\n            addSetting (*iter);\n\n        setWidgetResizable(true);\n        setWidget(topWidget);\n    }\n\n    void KeyBindingPage::addSetting(CSMPrefs::Setting *setting)\n    {\n        std::pair<QWidget*, QWidget*> widgets = setting->makeWidgets (this);\n\n        if (widgets.first)\n        {\n            // Label, Option widgets\n            assert(mPageLayout);\n\n            int next = mPageLayout->rowCount();\n            mPageLayout->addWidget(widgets.first, next, 0);\n            mPageLayout->addWidget(widgets.second, next, 1);\n        }\n        else if (widgets.second)\n        {\n            // Wide single widget\n            assert(mPageLayout);\n\n            int next = mPageLayout->rowCount();\n            mPageLayout->addWidget(widgets.second, next, 0, 1, 2);\n        }\n        else\n        {\n            if (setting->getLabel().empty())\n            {\n                // Insert empty space\n                assert(mPageLayout);\n\n                int next = mPageLayout->rowCount();\n                mPageLayout->addWidget(new QWidget(), next, 0);\n            }\n            else\n            {\n                // Create new page\n                QWidget* pageWidget = new QWidget();\n                mPageLayout = new QGridLayout(pageWidget);\n                mPageLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);\n\n                mStackedLayout->addWidget(pageWidget);\n\n                mPageSelector->addItem(QString::fromUtf8(setting->getLabel().c_str()));\n            }\n        }\n    }\n\n    void KeyBindingPage::resetKeyBindings()\n    {\n        CSMPrefs::State::get().resetCategory(\"Key Bindings\");\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/prefs/keybindingpage.hpp",
    "content": "#ifndef CSV_PREFS_KEYBINDINGPAGE_H\n#define CSV_PREFS_KEYBINDINGPAGE_H\n\n#include \"pagebase.hpp\"\n\nclass QComboBox;\nclass QGridLayout;\nclass QStackedLayout;\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVPrefs\n{\n    class KeyBindingPage : public PageBase\n    {\n            Q_OBJECT\n\n        public:\n\n            KeyBindingPage(CSMPrefs::Category& category, QWidget* parent);\n\n            void addSetting(CSMPrefs::Setting* setting);\n\n        private:\n\n            QStackedLayout* mStackedLayout;\n            QGridLayout* mPageLayout;\n            QComboBox* mPageSelector;\n\n        private slots:\n\n            void resetKeyBindings();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/prefs/page.cpp",
    "content": "\n#include \"page.hpp\"\n\n#include <QGridLayout>\n\n#include \"../../model/prefs/setting.hpp\"\n#include \"../../model/prefs/category.hpp\"\n\nCSVPrefs::Page::Page (CSMPrefs::Category& category, QWidget *parent)\n: PageBase (category, parent)\n{\n    QWidget *widget = new QWidget (parent);\n    mGrid = new QGridLayout (widget);\n\n    for (CSMPrefs::Category::Iterator iter = category.begin(); iter!=category.end(); ++iter)\n        addSetting (*iter);\n\n    setWidget (widget);\n}\n\nvoid CSVPrefs::Page::addSetting (CSMPrefs::Setting *setting)\n{\n    std::pair<QWidget *, QWidget *> widgets = setting->makeWidgets (this);\n\n    int next = mGrid->rowCount();\n\n    if (widgets.first)\n    {\n        mGrid->addWidget (widgets.first, next, 0);\n        mGrid->addWidget (widgets.second, next, 1);\n    }\n    else if (widgets.second)\n    {\n        mGrid->addWidget (widgets.second, next, 0, 1, 2);\n    }\n    else\n    {\n        mGrid->addWidget (new QWidget (this), next, 0);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/prefs/page.hpp",
    "content": "#ifndef CSV_PREFS_PAGE_H\n#define CSV_PREFS_PAGE_H\n\n#include \"pagebase.hpp\"\n\nclass QGridLayout;\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVPrefs\n{\n    class Page : public PageBase\n    {\n            Q_OBJECT\n\n            QGridLayout *mGrid;\n\n        public:\n\n            Page (CSMPrefs::Category& category, QWidget *parent);\n\n            void addSetting (CSMPrefs::Setting *setting);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/prefs/pagebase.cpp",
    "content": "\n#include \"pagebase.hpp\"\n\n#include <QMenu>\n#include <QContextMenuEvent>\n\n#include \"../../model/prefs/category.hpp\"\n#include \"../../model/prefs/state.hpp\"\n\nCSVPrefs::PageBase::PageBase (CSMPrefs::Category& category, QWidget *parent)\n: QScrollArea (parent), mCategory (category)\n{}\n\nCSMPrefs::Category& CSVPrefs::PageBase::getCategory()\n{\n    return mCategory;\n}\n\nvoid CSVPrefs::PageBase::contextMenuEvent(QContextMenuEvent* e)\n{\n    QMenu* menu = new QMenu();\n\n    menu->addAction(\"Reset category to default\", this, SLOT(resetCategory()));\n    menu->addAction(\"Reset all to default\", this, SLOT(resetAll()));\n\n    menu->exec(e->globalPos());\n    delete menu;\n}\n\nvoid CSVPrefs::PageBase::resetCategory()\n{\n    CSMPrefs::State::get().resetCategory(getCategory().getKey());\n}\n\nvoid CSVPrefs::PageBase::resetAll()\n{\n    CSMPrefs::State::get().resetAll();\n}\n"
  },
  {
    "path": "apps/opencs/view/prefs/pagebase.hpp",
    "content": "#ifndef CSV_PREFS_PAGEBASE_H\n#define CSV_PREFS_PAGEBASE_H\n\n#include <QScrollArea>\n\nclass QContextMenuEvent;\n\nnamespace CSMPrefs\n{\n    class Category;\n}\n\nnamespace CSVPrefs\n{\n    class PageBase : public QScrollArea\n    {\n            Q_OBJECT\n\n            CSMPrefs::Category& mCategory;\n\n        public:\n\n            PageBase (CSMPrefs::Category& category, QWidget *parent);\n\n            CSMPrefs::Category& getCategory();\n\n        protected:\n\n            void contextMenuEvent(QContextMenuEvent*) override;\n\n        private slots:\n\n            void resetCategory();\n\n            void resetAll();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/actor.cpp",
    "content": "#include \"actor.hpp\"\n\n#include <osg/Group>\n#include <osg/Node>\n\n#include <components/esm/mappings.hpp>\n#include <components/misc/resourcehelpers.hpp>\n#include <components/resource/resourcemanager.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/sceneutil/attach.hpp>\n#include <components/sceneutil/skeleton.hpp>\n\n#include \"../../model/world/data.hpp\"\n\nnamespace CSVRender\n{\n    const std::string Actor::MeshPrefix = \"meshes\\\\\";\n\n    Actor::Actor(const std::string& id, CSMWorld::Data& data)\n        : mId(id)\n        , mData(data)\n        , mBaseNode(new osg::Group())\n        , mSkeleton(nullptr)\n    {\n        mActorData = mData.getActorAdapter()->getActorData(mId);\n        connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)),\n                this, SLOT(handleActorChanged(const std::string&)));\n    }\n\n    osg::Group* Actor::getBaseNode()\n    {\n        return mBaseNode;\n    }\n\n    void Actor::update()\n    {\n        mBaseNode->removeChildren(0, mBaseNode->getNumChildren());\n\n        // Load skeleton\n        std::string skeletonModel = mActorData->getSkeleton();\n        skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());\n        loadSkeleton(skeletonModel);\n\n        if (!mActorData->isCreature())\n        {\n            // Get rid of the extra attachments\n            SceneUtil::CleanObjectRootVisitor cleanVisitor;\n            mSkeleton->accept(cleanVisitor);\n            cleanVisitor.remove();\n\n            // Attach parts to skeleton\n            loadBodyParts();\n        }\n        else\n        {\n            SceneUtil::RemoveTriBipVisitor removeTriBipVisitor;\n            mSkeleton->accept(removeTriBipVisitor);\n            removeTriBipVisitor.remove();\n        }\n\n        // Post setup\n        mSkeleton->markDirty();\n        mSkeleton->setActive(SceneUtil::Skeleton::Active);\n    }\n\n    void Actor::handleActorChanged(const std::string& refId)\n    {\n        if (mId == refId)\n        {\n            update();\n        }\n    }\n\n    void Actor::loadSkeleton(const std::string& model)\n    {\n        auto sceneMgr = mData.getResourceSystem()->getSceneManager();\n\n        osg::ref_ptr<osg::Node> temp = sceneMgr->getInstance(model);\n        mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(temp.get());\n        if (!mSkeleton)\n        {\n            mSkeleton = new SceneUtil::Skeleton();\n            mSkeleton->addChild(temp);\n        }\n        mBaseNode->addChild(mSkeleton);\n\n        // Map bone names to bones\n        mNodeMap.clear();\n        SceneUtil::NodeMapVisitor nmVisitor(mNodeMap);\n        mSkeleton->accept(nmVisitor);\n\n    }\n\n    void Actor::loadBodyParts()\n    {\n        for (int i = 0; i < ESM::PRT_Count; ++i)\n        {\n            auto type = (ESM::PartReferenceType) i;\n            std::string partId = mActorData->getPart(type);\n            attachBodyPart(type, getBodyPartMesh(partId));\n        }\n    }\n\n    void Actor::attachBodyPart(ESM::PartReferenceType type, const std::string& mesh)\n    {\n        auto sceneMgr = mData.getResourceSystem()->getSceneManager();\n\n        // Attach to skeleton\n        std::string boneName = ESM::getBoneName(type);\n        auto node = mNodeMap.find(boneName);\n        if (!mesh.empty() && node != mNodeMap.end())\n        {\n            auto instance = sceneMgr->getInstance(mesh);\n            SceneUtil::attach(instance, mSkeleton, boneName, node->second);\n        }\n    }\n\n    std::string Actor::getBodyPartMesh(const std::string& bodyPartId)\n    {\n        const auto& bodyParts = mData.getBodyParts();\n\n        int index = bodyParts.searchId(bodyPartId);\n        if (index != -1 && !bodyParts.getRecord(index).isDeleted())\n            return MeshPrefix + bodyParts.getRecord(index).get().mModel;\n        else\n            return \"\";\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/actor.hpp",
    "content": "#ifndef OPENCS_VIEW_RENDER_ACTOR_H\n#define OPENCS_VIEW_RENDER_ACTOR_H\n\n#include <string>\n\n#include <osg/ref_ptr>\n\n#include <QObject>\n\n#include <components/esm/loadarmo.hpp>\n#include <components/sceneutil/visitor.hpp>\n\n#include \"../../model/world/actoradapter.hpp\"\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace CSMWorld\n{\n    class Data;\n}\n\nnamespace SceneUtil\n{\n    class Skeleton;\n}\n\nnamespace CSVRender\n{\n    /// Handles loading an npc or creature\n    class Actor : public QObject\n    {\n        Q_OBJECT\n    public:\n        /// Creates an actor.\n        /// \\param id       The referenceable id\n        /// \\param type     The record type\n        /// \\param data     The data store\n        Actor(const std::string& id, CSMWorld::Data& data);\n\n        /// Retrieves the base node that meshes are attached to\n        osg::Group* getBaseNode();\n\n        /// (Re)creates the npc or creature renderable\n        void update();\n\n    private slots:\n        void handleActorChanged(const std::string& refId);\n\n    private:\n        void loadSkeleton(const std::string& model);\n        void loadBodyParts();\n        void attachBodyPart(ESM::PartReferenceType, const std::string& mesh);\n\n        std::string getBodyPartMesh(const std::string& bodyPartId);\n\n        static const std::string MeshPrefix;\n\n        std::string mId;\n        CSMWorld::Data& mData;\n        CSMWorld::ActorAdapter::ActorDataPtr mActorData;\n\n        osg::ref_ptr<osg::Group> mBaseNode;\n        SceneUtil::Skeleton* mSkeleton;\n        SceneUtil::NodeMapVisitor::NodeMap mNodeMap;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/brushdraw.cpp",
    "content": "#include \"brushdraw.hpp\"\n\n#include <limits>\n\n#include <osg/Group>\n#include <osg/Geometry>\n#include <osg/Array>\n\n#include <osgUtil/LineSegmentIntersector>\n\n#include \"../../model/world/cellcoordinates.hpp\"\n#include \"../widget/brushshapes.hpp\"\n#include \"mask.hpp\"\n\nCSVRender::BrushDraw::BrushDraw(osg::ref_ptr<osg::Group> parentNode, bool textureMode) :\n    mParentNode(parentNode), mTextureMode(textureMode)\n{\n    mBrushDrawNode = new osg::Group();\n    mGeometry = new osg::Geometry();\n    mBrushDrawNode->addChild(mGeometry);\n    mParentNode->addChild(mBrushDrawNode);\n    if (mTextureMode)\n        mLandSizeFactor = static_cast<float>(ESM::Land::REAL_SIZE) / static_cast<float>(ESM::Land::LAND_TEXTURE_SIZE);\n    else\n        mLandSizeFactor = static_cast<float>(ESM::Land::REAL_SIZE) / static_cast<float>(ESM::Land::LAND_SIZE - 1);\n}\n\nCSVRender::BrushDraw::~BrushDraw()\n{\n    mBrushDrawNode->removeChild(mGeometry);\n    mParentNode->removeChild(mBrushDrawNode);\n}\n\nfloat CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point)\n{\n    osg::Vec3d start = point;\n    osg::Vec3d end = point;\n    start.z() = std::numeric_limits<int>::max();\n    end.z() = std::numeric_limits<int>::lowest();\n    osg::Vec3d direction = end - start;\n\n    // Get intersection\n    osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(\n        osgUtil::Intersector::MODEL, start, end) );\n    intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);\n    osgUtil::IntersectionVisitor visitor(intersector);\n\n    visitor.setTraversalMask(Mask_Terrain);\n\n    mParentNode->accept(visitor);\n\n    for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();\n         it != intersector->getIntersections().end(); ++it)\n    {\n        osgUtil::LineSegmentIntersector::Intersection intersection = *it;\n\n        // reject back-facing polygons\n        if (direction * intersection.getWorldIntersectNormal() > 0)\n        {\n            continue;\n        }\n\n        return intersection.getWorldIntersectPoint().z();\n    }\n    return 0.0f;\n}\n\nvoid CSVRender::BrushDraw::buildPointGeometry(const osg::Vec3d& point)\n{\n    osg::ref_ptr<osg::Geometry> geom (new osg::Geometry());\n    osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array());\n    osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array());\n    const float brushOutlineHeight (1.0f);\n    const float crossHeadSize (8.0f);\n    osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f);\n\n    vertices->push_back(osg::Vec3d(\n        point.x() - crossHeadSize,\n        point.y() - crossHeadSize,\n        getIntersectionHeight(osg::Vec3d(\n            point.x() - crossHeadSize,\n            point.y() - crossHeadSize,\n            point.z()) ) + brushOutlineHeight));\n    colors->push_back(lineColor);\n    vertices->push_back(osg::Vec3d(\n        point.x() + crossHeadSize,\n        point.y() + crossHeadSize,\n        getIntersectionHeight(osg::Vec3d(\n            point.x() + crossHeadSize,\n            point.y() + crossHeadSize,\n            point.z()) ) + brushOutlineHeight));\n    colors->push_back(lineColor);\n    vertices->push_back(osg::Vec3d(\n        point.x() + crossHeadSize,\n        point.y() - crossHeadSize,\n        getIntersectionHeight(osg::Vec3d(\n            point.x() + crossHeadSize,\n            point.y() - crossHeadSize,\n            point.z()) ) + brushOutlineHeight));\n    colors->push_back(lineColor);\n    vertices->push_back(osg::Vec3d(\n        point.x() - crossHeadSize,\n        point.y() + crossHeadSize,\n        getIntersectionHeight(osg::Vec3d(\n            point.x() - crossHeadSize,\n            point.y() + crossHeadSize,\n            point.z()) ) + brushOutlineHeight));\n    colors->push_back(lineColor);\n\n    geom->setVertexArray(vertices);\n    geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);\n    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 4));\n    mGeometry = geom;\n}\n\nvoid CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::Vec3d& point)\n{\n    osg::ref_ptr<osg::Geometry> geom (new osg::Geometry());\n    osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array());\n    osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array());\n\n    const float brushOutlineHeight (1.0f);\n    float diameter = radius * 2;\n    int resolution = static_cast<int>(2.f * diameter / mLandSizeFactor); //half a vertex resolution\n    float resAdjustedLandSizeFactor = mLandSizeFactor / 2;\n    osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f);\n\n    for (int i = 0; i < resolution; i++)\n    {\n        int step = i * resAdjustedLandSizeFactor;\n        int step2 = (i + 1) * resAdjustedLandSizeFactor;\n\n        osg::Vec3d upHorizontalLinePoint1(\n            point.x() - radius + step,\n            point.y() - radius,\n            getIntersectionHeight(osg::Vec3d(\n                point.x() - radius + step,\n                point.y() - radius,\n                point.z())) + brushOutlineHeight);\n        osg::Vec3d upHorizontalLinePoint2(\n            point.x() - radius + step2,\n            point.y() - radius,\n            getIntersectionHeight(osg::Vec3d(\n                point.x() - radius + step2,\n                point.y() - radius,\n                point.z())) + brushOutlineHeight);\n        osg::Vec3d upVerticalLinePoint1(\n            point.x() - radius,\n            point.y() - radius + step,\n            getIntersectionHeight(osg::Vec3d(\n                point.x() - radius,\n                point.y() - radius + step,\n                point.z())) + brushOutlineHeight);\n        osg::Vec3d upVerticalLinePoint2(\n            point.x() - radius,\n            point.y() - radius + step2,\n            getIntersectionHeight(osg::Vec3d(\n                point.x() - radius,\n                point.y() - radius + step2,\n                point.z())) + brushOutlineHeight);\n        osg::Vec3d downHorizontalLinePoint1(\n            point.x() + radius - step,\n            point.y() + radius,\n            getIntersectionHeight(osg::Vec3d(\n                point.x() + radius - step,\n                point.y() + radius,\n                point.z())) + brushOutlineHeight);\n        osg::Vec3d downHorizontalLinePoint2(\n            point.x() + radius - step2,\n            point.y() + radius,\n            getIntersectionHeight(osg::Vec3d(\n                point.x() + radius - step2,\n                point.y() + radius,\n                point.z())) + brushOutlineHeight);\n        osg::Vec3d downVerticalLinePoint1(\n            point.x() + radius,\n            point.y() + radius - step,\n            getIntersectionHeight(osg::Vec3d(\n                point.x() + radius,\n                point.y() + radius - step,\n                point.z())) + brushOutlineHeight);\n        osg::Vec3d downVerticalLinePoint2(\n            point.x() + radius,\n            point.y() + radius - step2,\n            getIntersectionHeight(osg::Vec3d(\n                point.x() + radius,\n                point.y() + radius - step2,\n                point.z())) + brushOutlineHeight);\n        vertices->push_back(upHorizontalLinePoint1);\n        colors->push_back(lineColor);\n        vertices->push_back(upHorizontalLinePoint2);\n        colors->push_back(lineColor);\n        vertices->push_back(upVerticalLinePoint1);\n        colors->push_back(lineColor);\n        vertices->push_back(upVerticalLinePoint2);\n        colors->push_back(lineColor);\n        vertices->push_back(downHorizontalLinePoint1);\n        colors->push_back(lineColor);\n        vertices->push_back(downHorizontalLinePoint2);\n        colors->push_back(lineColor);\n        vertices->push_back(downVerticalLinePoint1);\n        colors->push_back(lineColor);\n        vertices->push_back(downVerticalLinePoint2);\n        colors->push_back(lineColor);\n    }\n\n    geom->setVertexArray(vertices);\n    geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);\n    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, resolution * 8));\n    mGeometry = geom;\n}\n\nvoid CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::Vec3d& point)\n{\n    osg::ref_ptr<osg::Geometry> geom (new osg::Geometry());\n    osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array());\n    osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array());\n    const int amountOfPoints = (osg::PI * 2.0f) * radius / 20;\n    const float step ((osg::PI * 2.0f) / static_cast<float>(amountOfPoints));\n    const float brushOutlineHeight (1.0f);\n    osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f);\n\n    for (int i = 0; i < amountOfPoints + 2; i++)\n    {\n        float angle (i * step);\n        vertices->push_back(osg::Vec3d(\n            point.x() + radius * cosf(angle),\n            point.y() + radius * sinf(angle),\n            getIntersectionHeight(osg::Vec3d(\n                point.x() + radius * cosf(angle),\n                point.y() + radius * sinf(angle),\n                point.z()) ) + brushOutlineHeight));\n        colors->push_back(lineColor);\n        angle = static_cast<float>(i + 1) * step;\n        vertices->push_back(osg::Vec3d(\n            point.x() + radius * cosf(angle),\n            point.y() + radius * sinf(angle),\n            getIntersectionHeight(osg::Vec3d(\n                point.x() + radius * cosf(angle),\n                point.y() + radius * sinf(angle),\n                point.z()) ) + brushOutlineHeight));\n        colors->push_back(lineColor);\n    }\n\n    geom->setVertexArray(vertices);\n    geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);\n    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, amountOfPoints * 2));\n    mGeometry = geom;\n}\n\nvoid CSVRender::BrushDraw::buildCustomGeometry(const float& radius, const osg::Vec3d& point)\n{\n    // Not implemented\n}\n\nvoid CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize, CSVWidget::BrushShape toolShape)\n{\n    if (mBrushDrawNode->containsNode(mGeometry))\n        mBrushDrawNode->removeChild(mGeometry);\n    float radius = (mLandSizeFactor * brushSize) / 2;\n    osg::Vec3d snapToGridPoint = point;\n    if (mTextureMode)\n    {\n        std::pair<int, int> snapToGridXY = CSMWorld::CellCoordinates::toTextureCoords(point);\n        float offsetToMiddle =  mLandSizeFactor * 0.5f;\n        snapToGridPoint = osg::Vec3d(\n            CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(snapToGridXY.first) + offsetToMiddle,\n            CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(snapToGridXY.second) + offsetToMiddle,\n            point.z());\n    }\n    else\n    {\n        std::pair<int, int> snapToGridXY = CSMWorld::CellCoordinates::toVertexCoords(point);\n        snapToGridPoint = osg::Vec3d(\n            CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(snapToGridXY.first),\n            CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(snapToGridXY.second),\n            point.z());\n    }\n\n\n    switch (toolShape)\n    {\n        case (CSVWidget::BrushShape_Point) :\n            buildPointGeometry(snapToGridPoint);\n            break;\n        case (CSVWidget::BrushShape_Square) :\n            buildSquareGeometry(radius, snapToGridPoint);\n            break;\n        case (CSVWidget::BrushShape_Circle) :\n            buildCircleGeometry(radius, snapToGridPoint);\n            break;\n        case (CSVWidget::BrushShape_Custom) :\n            buildSquareGeometry(1, snapToGridPoint);\n            //buildCustomGeometry\n            break;\n    }\n\n    mGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n\n    mBrushDrawNode->addChild(mGeometry);\n}\n\nvoid CSVRender::BrushDraw::hide()\n{\n    if (mBrushDrawNode->containsNode(mGeometry))\n        mBrushDrawNode->removeChild(mGeometry);\n}\n"
  },
  {
    "path": "apps/opencs/view/render/brushdraw.hpp",
    "content": "#ifndef CSV_RENDER_BRUSHDRAW_H\n#define CSV_RENDER_BRUSHDRAW_H\n\n#include <osg/Group>\n#include <osg/Geometry>\n\n#include <components/esm/loadland.hpp>\n#include \"../widget/brushshapes.hpp\"\n\nnamespace CSVRender\n{\n    class BrushDraw\n    {\n        public:\n            BrushDraw(osg::ref_ptr<osg::Group> parentNode, bool textureMode = false);\n            ~BrushDraw();\n\n            void update(osg::Vec3d point, int brushSize, CSVWidget::BrushShape toolShape);\n            void hide();\n\n        private:\n            void buildPointGeometry(const osg::Vec3d& point);\n            void buildSquareGeometry(const float& radius, const osg::Vec3d& point);\n            void buildCircleGeometry(const float& radius, const osg::Vec3d& point);\n            void buildCustomGeometry(const float& radius, const osg::Vec3d& point);\n            float getIntersectionHeight (const osg::Vec3d& point);\n\n            osg::ref_ptr<osg::Group> mParentNode;\n            osg::ref_ptr<osg::Group> mBrushDrawNode;\n            osg::ref_ptr<osg::Geometry> mGeometry;\n            bool mTextureMode;\n            float mLandSizeFactor;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/cameracontroller.cpp",
    "content": "#include \"cameracontroller.hpp\"\n\n#include <cmath>\n\n#include <QWidget>\n\n#include <osg/BoundingBox>\n#include <osg/Camera>\n#include <osg/ComputeBoundsVisitor>\n#include <osg/Drawable>\n#include <osg/Group>\n#include <osg/Matrixd>\n#include <osg/Quat>\n\n#include <osgUtil/LineSegmentIntersector>\n\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"scenewidget.hpp\"\n\nnamespace CSVRender\n{\n\n    /*\n    Camera Controller\n    */\n\n    const osg::Vec3d CameraController::WorldUp = osg::Vec3d(0, 0, 1);\n\n    const osg::Vec3d CameraController::LocalUp = osg::Vec3d(0, 1, 0);\n    const osg::Vec3d CameraController::LocalLeft = osg::Vec3d(1, 0, 0);\n    const osg::Vec3d CameraController::LocalForward = osg::Vec3d(0, 0, 1);\n\n    CameraController::CameraController(QObject* parent)\n        : QObject(parent)\n        , mActive(false)\n        , mInverted(false)\n        , mCameraSensitivity(1/650.f)\n        , mSecondaryMoveMult(50)\n        , mWheelMoveMult(8)\n        , mCamera(nullptr)\n    {\n    }\n\n    CameraController::~CameraController()\n    {\n    }\n\n    bool CameraController::isActive() const\n    {\n        return mActive;\n    }\n\n    osg::Camera* CameraController::getCamera() const\n    {\n        return mCamera;\n    }\n\n    double CameraController::getCameraSensitivity() const\n    {\n        return mCameraSensitivity;\n    }\n\n    bool CameraController::getInverted() const\n    {\n        return mInverted;\n    }\n\n    double CameraController::getSecondaryMovementMultiplier() const\n    {\n        return mSecondaryMoveMult;\n    }\n\n    double CameraController::getWheelMovementMultiplier() const\n    {\n        return mWheelMoveMult;\n    }\n\n    void CameraController::setCamera(osg::Camera* camera)\n    {\n        bool wasActive = mActive;\n\n        mCamera = camera;\n        mActive = (mCamera != nullptr);\n\n        if (mActive != wasActive)\n        {\n            for (std::vector<CSMPrefs::Shortcut*>::iterator it = mShortcuts.begin(); it != mShortcuts.end(); ++it)\n            {\n                CSMPrefs::Shortcut* shortcut = *it;\n                shortcut->enable(mActive);\n            }\n        }\n    }\n\n    void CameraController::setCameraSensitivity(double value)\n    {\n        mCameraSensitivity = value;\n    }\n\n    void CameraController::setInverted(bool value)\n    {\n        mInverted = value;\n    }\n\n    void CameraController::setSecondaryMovementMultiplier(double value)\n    {\n        mSecondaryMoveMult = value;\n    }\n\n    void CameraController::setWheelMovementMultiplier(double value)\n    {\n        mWheelMoveMult = value;\n    }\n\n    void CameraController::setup(osg::Group* root, unsigned int mask, const osg::Vec3d& up)\n    {\n        // Find World bounds\n        osg::ComputeBoundsVisitor boundsVisitor;\n        osg::BoundingBox& boundingBox = boundsVisitor.getBoundingBox();\n\n        boundsVisitor.setTraversalMask(mask);\n        root->accept(boundsVisitor);\n\n        if (!boundingBox.valid())\n        {\n            // Try again without any mask\n            boundsVisitor.reset();\n            boundsVisitor.setTraversalMask(~0u);\n            root->accept(boundsVisitor);\n\n            // Last resort, set a default\n            if (!boundingBox.valid())\n            {\n                boundingBox.set(-1, -1, -1, 1, 1, 1);\n            }\n        }\n\n        // Calculate a good starting position\n        osg::Vec3d minBounds = boundingBox.corner(0) - boundingBox.center();\n        osg::Vec3d maxBounds = boundingBox.corner(7) - boundingBox.center();\n\n        osg::Vec3d camOffset = up * maxBounds > 0 ? maxBounds : minBounds;\n        camOffset *= 2;\n\n        osg::Vec3d eye = camOffset + boundingBox.center();\n        osg::Vec3d center = boundingBox.center();\n\n        getCamera()->setViewMatrixAsLookAt(eye, center, up);\n    }\n\n    void CameraController::addShortcut(CSMPrefs::Shortcut* shortcut)\n    {\n        mShortcuts.push_back(shortcut);\n    }\n\n    /*\n    Free Camera Controller\n    */\n\n    FreeCameraController::FreeCameraController(QWidget* widget)\n        : CameraController(widget)\n        , mLockUpright(false)\n        , mModified(false)\n        , mNaviPrimary(false)\n        , mNaviSecondary(false)\n        , mFast(false)\n        , mFastAlternate(false)\n        , mLeft(false)\n        , mRight(false)\n        , mForward(false)\n        , mBackward(false)\n        , mRollLeft(false)\n        , mRollRight(false)\n        , mUp(LocalUp)\n        , mLinSpeed(1000)\n        , mRotSpeed(osg::PI / 2)\n        , mSpeedMult(8)\n    {\n        CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut(\"scene-navi-primary\", widget);\n        naviPrimaryShortcut->enable(false);\n        connect(naviPrimaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviPrimary(bool)));\n\n        addShortcut(naviPrimaryShortcut);\n\n        CSMPrefs::Shortcut* naviSecondaryShortcut = new CSMPrefs::Shortcut(\"scene-navi-secondary\", widget);\n        naviSecondaryShortcut->enable(false);\n        connect(naviSecondaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviSecondary(bool)));\n\n        addShortcut(naviSecondaryShortcut);\n\n        CSMPrefs::Shortcut* forwardShortcut = new CSMPrefs::Shortcut(\"free-forward\", \"scene-speed-modifier\",\n            CSMPrefs::Shortcut::SM_Detach, widget);\n        forwardShortcut->enable(false);\n        connect(forwardShortcut, SIGNAL(activated(bool)), this, SLOT(forward(bool)));\n        connect(forwardShortcut, SIGNAL(secondary(bool)), this, SLOT(alternateFast(bool)));\n\n        addShortcut(forwardShortcut);\n\n        CSMPrefs::Shortcut* leftShortcut = new CSMPrefs::Shortcut(\"free-left\", widget);\n        leftShortcut->enable(false);\n        connect(leftShortcut, SIGNAL(activated(bool)), this, SLOT(left(bool)));\n\n        addShortcut(leftShortcut);\n\n        CSMPrefs::Shortcut* backShortcut = new CSMPrefs::Shortcut(\"free-backward\", widget);\n        backShortcut->enable(false);\n        connect(backShortcut, SIGNAL(activated(bool)), this, SLOT(backward(bool)));\n\n        addShortcut(backShortcut);\n\n        CSMPrefs::Shortcut* rightShortcut = new CSMPrefs::Shortcut(\"free-right\", widget);\n        rightShortcut->enable(false);\n        connect(rightShortcut, SIGNAL(activated(bool)), this, SLOT(right(bool)));\n\n        addShortcut(rightShortcut);\n\n        CSMPrefs::Shortcut* rollLeftShortcut = new CSMPrefs::Shortcut(\"free-roll-left\", widget);\n        rollLeftShortcut->enable(false);\n        connect(rollLeftShortcut, SIGNAL(activated(bool)), this, SLOT(rollLeft(bool)));\n\n        addShortcut(rollLeftShortcut);\n\n        CSMPrefs::Shortcut* rollRightShortcut = new CSMPrefs::Shortcut(\"free-roll-right\", widget);\n        rollRightShortcut->enable(false);\n        connect(rollRightShortcut, SIGNAL(activated(bool)), this, SLOT(rollRight(bool)));\n\n        addShortcut(rollRightShortcut);\n\n        CSMPrefs::Shortcut* speedModeShortcut = new CSMPrefs::Shortcut(\"free-speed-mode\", widget);\n        speedModeShortcut->enable(false);\n        connect(speedModeShortcut, SIGNAL(activated()), this, SLOT(swapSpeedMode()));\n\n        addShortcut(speedModeShortcut);\n    }\n\n    double FreeCameraController::getLinearSpeed() const\n    {\n        return mLinSpeed;\n    }\n\n    double FreeCameraController::getRotationalSpeed() const\n    {\n        return mRotSpeed;\n    }\n\n    double FreeCameraController::getSpeedMultiplier() const\n    {\n        return mSpeedMult;\n    }\n\n    void FreeCameraController::setLinearSpeed(double value)\n    {\n        mLinSpeed = value;\n    }\n\n    void FreeCameraController::setRotationalSpeed(double value)\n    {\n        mRotSpeed = value;\n    }\n\n    void FreeCameraController::setSpeedMultiplier(double value)\n    {\n        mSpeedMult = value;\n    }\n\n    void FreeCameraController::fixUpAxis(const osg::Vec3d& up)\n    {\n        mLockUpright = true;\n        mUp = up;\n        mModified = true;\n    }\n\n    void FreeCameraController::unfixUpAxis()\n    {\n        mLockUpright = false;\n    }\n\n    void FreeCameraController::handleMouseMoveEvent(int x, int y)\n    {\n        if (!isActive())\n            return;\n\n        if (mNaviPrimary)\n        {\n            double scalar = getCameraSensitivity() * (getInverted() ? -1.0 : 1.0);\n            yaw(x * scalar);\n            pitch(y * scalar);\n        }\n        else if (mNaviSecondary)\n        {\n            osg::Vec3d movement;\n            movement += LocalLeft * -x * getSecondaryMovementMultiplier();\n            movement += LocalUp * y * getSecondaryMovementMultiplier();\n\n            translate(movement);\n        }\n    }\n\n    void FreeCameraController::handleMouseScrollEvent(int x)\n    {\n        if (!isActive())\n            return;\n\n        translate(LocalForward * x * ((mFast ^ mFastAlternate) ? getWheelMovementMultiplier() : 1));\n    }\n\n    void FreeCameraController::update(double dt)\n    {\n        if (!isActive())\n            return;\n\n        double linDist = mLinSpeed * dt;\n        double rotDist = mRotSpeed * dt;\n\n        if (mFast ^ mFastAlternate)\n            linDist *= mSpeedMult;\n\n        if (mLeft)\n            translate(LocalLeft * linDist);\n        if (mRight)\n            translate(LocalLeft * -linDist);\n        if (mForward)\n            translate(LocalForward * linDist);\n        if (mBackward)\n            translate(LocalForward * -linDist);\n\n        if (!mLockUpright)\n        {\n            if (mRollLeft)\n                roll(-rotDist);\n            if (mRollRight)\n                roll(rotDist);\n        }\n        else if(mModified)\n        {\n            stabilize();\n            mModified = false;\n        }\n\n        // Normalize the matrix to counter drift\n        getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix());\n    }\n\n    void FreeCameraController::yaw(double value)\n    {\n        getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalUp);\n        mModified = true;\n    }\n\n    void FreeCameraController::pitch(double value)\n    {\n        const double Constraint = osg::PI / 2 - 0.1;\n\n        if (mLockUpright)\n        {\n            osg::Vec3d eye, center, up;\n            getCamera()->getViewMatrixAsLookAt(eye, center, up);\n\n            osg::Vec3d forward = center - eye;\n            osg::Vec3d left = up ^ forward;\n\n            double pitchAngle = std::acos(up * mUp);\n            if ((mUp ^ up) * left < 0)\n                pitchAngle *= -1;\n\n            if (std::abs(pitchAngle + value) > Constraint)\n                value = (pitchAngle > 0 ? 1 : -1) * Constraint - pitchAngle;\n        }\n\n        getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalLeft);\n        mModified = true;\n    }\n\n    void FreeCameraController::roll(double value)\n    {\n        getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward);\n        mModified = true;\n    }\n\n    void FreeCameraController::translate(const osg::Vec3d& offset)\n    {\n        getCamera()->getViewMatrix() *= osg::Matrixd::translate(offset);\n        mModified = true;\n    }\n\n    void FreeCameraController::stabilize()\n    {\n        osg::Vec3d eye, center, up;\n        getCamera()->getViewMatrixAsLookAt(eye, center, up);\n        getCamera()->setViewMatrixAsLookAt(eye, center, mUp);\n    }\n\n    void FreeCameraController::naviPrimary(bool active)\n    {\n        mNaviPrimary = active;\n    }\n\n    void FreeCameraController::naviSecondary(bool active)\n    {\n        mNaviSecondary = active;\n    }\n\n    void FreeCameraController::forward(bool active)\n    {\n        mForward = active;\n    }\n\n    void FreeCameraController::left(bool active)\n    {\n        mLeft = active;\n    }\n\n    void FreeCameraController::backward(bool active)\n    {\n        mBackward = active;\n    }\n\n    void FreeCameraController::right(bool active)\n    {\n        mRight = active;\n    }\n\n    void FreeCameraController::rollLeft(bool active)\n    {\n        mRollLeft = active;\n    }\n\n    void FreeCameraController::rollRight(bool active)\n    {\n        mRollRight = active;\n    }\n\n    void FreeCameraController::alternateFast(bool active)\n    {\n        mFastAlternate = active;\n    }\n\n    void FreeCameraController::swapSpeedMode()\n    {\n        mFast = !mFast;\n    }\n\n    /*\n    Orbit Camera Controller\n    */\n\n    OrbitCameraController::OrbitCameraController(QWidget* widget)\n        : CameraController(widget)\n        , mInitialized(false)\n        , mNaviPrimary(false)\n        , mNaviSecondary(false)\n        , mFast(false)\n        , mFastAlternate(false)\n        , mLeft(false)\n        , mRight(false)\n        , mUp(false)\n        , mDown(false)\n        , mRollLeft(false)\n        , mRollRight(false)\n        , mPickingMask(~0u)\n        , mCenter(0,0,0)\n        , mDistance(0)\n        , mOrbitSpeed(osg::PI / 4)\n        , mOrbitSpeedMult(4)\n        , mConstRoll(false)\n    {\n        CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut(\"scene-navi-primary\", widget);\n        naviPrimaryShortcut->enable(false);\n        connect(naviPrimaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviPrimary(bool)));\n\n        addShortcut(naviPrimaryShortcut);\n\n        CSMPrefs::Shortcut* naviSecondaryShortcut = new CSMPrefs::Shortcut(\"scene-navi-secondary\", widget);\n        naviSecondaryShortcut->enable(false);\n        connect(naviSecondaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviSecondary(bool)));\n\n        addShortcut(naviSecondaryShortcut);\n\n        CSMPrefs::Shortcut* upShortcut = new CSMPrefs::Shortcut(\"orbit-up\", \"scene-speed-modifier\",\n            CSMPrefs::Shortcut::SM_Detach, widget);\n        upShortcut->enable(false);\n        connect(upShortcut, SIGNAL(activated(bool)), this, SLOT(up(bool)));\n        connect(upShortcut, SIGNAL(secondary(bool)), this, SLOT(alternateFast(bool)));\n\n        addShortcut(upShortcut);\n\n        CSMPrefs::Shortcut* leftShortcut = new CSMPrefs::Shortcut(\"orbit-left\", widget);\n        leftShortcut->enable(false);\n        connect(leftShortcut, SIGNAL(activated(bool)), this, SLOT(left(bool)));\n\n        addShortcut(leftShortcut);\n\n        CSMPrefs::Shortcut* downShortcut = new CSMPrefs::Shortcut(\"orbit-down\", widget);\n        downShortcut->enable(false);\n        connect(downShortcut, SIGNAL(activated(bool)), this, SLOT(down(bool)));\n\n        addShortcut(downShortcut);\n\n        CSMPrefs::Shortcut* rightShortcut = new CSMPrefs::Shortcut(\"orbit-right\", widget);\n        rightShortcut->enable(false);\n        connect(rightShortcut, SIGNAL(activated(bool)), this, SLOT(right(bool)));\n\n        addShortcut(rightShortcut);\n\n        CSMPrefs::Shortcut* rollLeftShortcut = new CSMPrefs::Shortcut(\"orbit-roll-left\", widget);\n        rollLeftShortcut->enable(false);\n        connect(rollLeftShortcut, SIGNAL(activated(bool)), this, SLOT(rollLeft(bool)));\n\n        addShortcut(rollLeftShortcut);\n\n        CSMPrefs::Shortcut* rollRightShortcut = new CSMPrefs::Shortcut(\"orbit-roll-right\", widget);\n        rollRightShortcut->enable(false);\n        connect(rollRightShortcut, SIGNAL(activated(bool)), this, SLOT(rollRight(bool)));\n\n        addShortcut(rollRightShortcut);\n\n        CSMPrefs::Shortcut* speedModeShortcut = new CSMPrefs::Shortcut(\"orbit-speed-mode\", widget);\n        speedModeShortcut->enable(false);\n        connect(speedModeShortcut, SIGNAL(activated()), this, SLOT(swapSpeedMode()));\n\n        addShortcut(speedModeShortcut);\n    }\n\n    osg::Vec3d OrbitCameraController::getCenter() const\n    {\n        return mCenter;\n    }\n\n    double OrbitCameraController::getOrbitSpeed() const\n    {\n        return mOrbitSpeed;\n    }\n\n    double OrbitCameraController::getOrbitSpeedMultiplier() const\n    {\n        return mOrbitSpeedMult;\n    }\n\n    unsigned int OrbitCameraController::getPickingMask() const\n    {\n        return mPickingMask;\n    }\n\n    void OrbitCameraController::setCenter(const osg::Vec3d& value)\n    {\n        osg::Vec3d eye, center, up;\n        getCamera()->getViewMatrixAsLookAt(eye, center, up);\n\n        mCenter = value;\n        mDistance = (eye - mCenter).length();\n\n        getCamera()->setViewMatrixAsLookAt(eye, mCenter, up);\n\n        mInitialized = true;\n    }\n\n    void OrbitCameraController::setOrbitSpeed(double value)\n    {\n        mOrbitSpeed = value;\n    }\n\n    void OrbitCameraController::setOrbitSpeedMultiplier(double value)\n    {\n        mOrbitSpeedMult = value;\n    }\n\n    void OrbitCameraController::setPickingMask(unsigned int value)\n    {\n        mPickingMask = value;\n    }\n\n    void OrbitCameraController::handleMouseMoveEvent(int x, int y)\n    {\n        if (!isActive())\n            return;\n\n        if (!mInitialized)\n            initialize();\n\n        if (mNaviPrimary)\n        {\n            double scalar = getCameraSensitivity() * (getInverted() ? -1.0 : 1.0);\n            rotateHorizontal(x * scalar);\n            rotateVertical(-y * scalar);\n        }\n        else if (mNaviSecondary)\n        {\n            osg::Vec3d movement;\n            movement += LocalLeft * x * getSecondaryMovementMultiplier();\n            movement += LocalUp * -y * getSecondaryMovementMultiplier();\n\n            translate(movement);\n        }\n    }\n\n    void OrbitCameraController::handleMouseScrollEvent(int x)\n    {\n        if (!isActive())\n            return;\n\n        zoom(-x * ((mFast ^ mFastAlternate) ? getWheelMovementMultiplier() : 1));\n    }\n\n    void OrbitCameraController::update(double dt)\n    {\n        if (!isActive())\n            return;\n\n        if (!mInitialized)\n            initialize();\n\n        double rotDist = mOrbitSpeed * dt;\n\n        if (mFast ^ mFastAlternate)\n            rotDist *= mOrbitSpeedMult;\n\n        if (mLeft)\n            rotateHorizontal(-rotDist);\n        if (mRight)\n            rotateHorizontal(rotDist);\n        if (mUp)\n            rotateVertical(rotDist);\n        if (mDown)\n            rotateVertical(-rotDist);\n\n        if (mRollLeft)\n            roll(-rotDist);\n        if (mRollRight)\n            roll(rotDist);\n\n        // Normalize the matrix to counter drift\n        getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix());\n    }\n\n    void OrbitCameraController::reset()\n    {\n        mInitialized = false;\n    }\n\n    void OrbitCameraController::initialize()\n    {\n        static const int DefaultStartDistance = 10000.f;\n\n        // Try to intelligently pick focus object\n        osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(\n            osgUtil::Intersector::PROJECTION, osg::Vec3d(0, 0, 0), LocalForward));\n\n        intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);\n        osgUtil::IntersectionVisitor visitor(intersector);\n\n        visitor.setTraversalMask(mPickingMask);\n\n        getCamera()->accept(visitor);\n\n        osg::Vec3d eye, center, up;\n        getCamera()->getViewMatrixAsLookAt(eye, center, up, DefaultStartDistance);\n\n        if (intersector->getIntersections().begin() != intersector->getIntersections().end())\n        {\n            mCenter = intersector->getIntersections().begin()->getWorldIntersectPoint();\n            mDistance = (eye - mCenter).length();\n        }\n        else\n        {\n            mCenter = center;\n            mDistance = DefaultStartDistance;\n        }\n\n        mInitialized = true;\n    }\n    \n    void OrbitCameraController::setConstRoll(bool enabled)\n    {\n        mConstRoll = enabled;\n    }\n\n    void OrbitCameraController::rotateHorizontal(double value)\n    {\n        osg::Vec3d eye, center, up;\n        getCamera()->getViewMatrixAsLookAt(eye, center, up);\n        osg::Vec3d absoluteUp = osg::Vec3(0,0,1);\n\n        osg::Quat rotation = osg::Quat(value, mConstRoll ? absoluteUp : up);\n        osg::Vec3d oldOffset = eye - mCenter;\n        osg::Vec3d newOffset = rotation * oldOffset;\n\n        if (mConstRoll)\n            up = rotation * up;\n\n        getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);\n    }\n\n    void OrbitCameraController::rotateVertical(double value)\n    {\n        osg::Vec3d eye, center, up;\n        getCamera()->getViewMatrixAsLookAt(eye, center, up);\n\n        osg::Vec3d forward = center - eye;\n        osg::Vec3d axis = up ^ forward;\n\n        osg::Quat rotation = osg::Quat(value,axis);\n        osg::Vec3d oldOffset = eye - mCenter;\n        osg::Vec3d newOffset = rotation * oldOffset;\n            \n        if (mConstRoll)\n            up = rotation * up;\n\n        getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);\n    }\n\n    void OrbitCameraController::roll(double value)\n    {\n        getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward);\n    }\n\n    void OrbitCameraController::translate(const osg::Vec3d& offset)\n    {\n        osg::Vec3d eye, center, up;\n        getCamera()->getViewMatrixAsLookAt(eye, center, up);\n\n        osg::Vec3d newOffset = getCamera()->getViewMatrix().getRotate().inverse() * offset;\n        mCenter += newOffset;\n        eye += newOffset;\n\n        getCamera()->setViewMatrixAsLookAt(eye, mCenter, up);\n    }\n\n    void OrbitCameraController::zoom(double value)\n    {\n        mDistance = std::max(10., mDistance + value);\n\n        osg::Vec3d eye, center, up;\n        getCamera()->getViewMatrixAsLookAt(eye, center, up, 1.f);\n\n        osg::Vec3d offset = (eye - center) * mDistance;\n\n        getCamera()->setViewMatrixAsLookAt(mCenter + offset, mCenter, up);\n    }\n\n    void OrbitCameraController::naviPrimary(bool active)\n    {\n        mNaviPrimary = active;\n    }\n\n    void OrbitCameraController::naviSecondary(bool active)\n    {\n        mNaviSecondary = active;\n    }\n\n    void OrbitCameraController::up(bool active)\n    {\n        mUp = active;\n    }\n\n    void OrbitCameraController::left(bool active)\n    {\n        mLeft = active;\n    }\n\n    void OrbitCameraController::down(bool active)\n    {\n        mDown = active;\n    }\n\n    void OrbitCameraController::right(bool active)\n    {\n        mRight = active;\n    }\n\n    void OrbitCameraController::rollLeft(bool active)\n    {\n        if (isActive())\n            mRollLeft = active;\n    }\n\n    void OrbitCameraController::rollRight(bool active)\n    {\n        mRollRight = active;\n    }\n\n    void OrbitCameraController::alternateFast(bool active)\n    {\n        mFastAlternate = active;\n    }\n\n    void OrbitCameraController::swapSpeedMode()\n    {\n        mFast = !mFast;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/cameracontroller.hpp",
    "content": "#ifndef OPENCS_VIEW_CAMERACONTROLLER_H\n#define OPENCS_VIEW_CAMERACONTROLLER_H\n\n#include <string>\n#include <vector>\n\n#include <QObject>\n\n#include <osg/ref_ptr>\n#include <osg/Vec3d>\n\nnamespace osg\n{\n    class Camera;\n    class Group;\n}\n\nnamespace CSMPrefs\n{\n    class Shortcut;\n}\n\nnamespace CSVRender\n{\n    class SceneWidget;\n\n    class CameraController : public QObject\n    {\n            Q_OBJECT\n\n        public:\n\n            static const osg::Vec3d WorldUp;\n\n            static const osg::Vec3d LocalUp;\n            static const osg::Vec3d LocalLeft;\n            static const osg::Vec3d LocalForward;\n\n            CameraController(QObject* parent);\n            virtual ~CameraController();\n\n            bool isActive() const;\n\n            osg::Camera* getCamera() const;\n            double getCameraSensitivity() const;\n            bool getInverted() const;\n            double getSecondaryMovementMultiplier() const;\n            double getWheelMovementMultiplier() const;\n\n            void setCamera(osg::Camera*);\n            void setCameraSensitivity(double value);\n            void setInverted(bool value);\n            void setSecondaryMovementMultiplier(double value);\n            void setWheelMovementMultiplier(double value);\n\n            // moves the camera to an intelligent position\n            void setup(osg::Group* root, unsigned int mask, const osg::Vec3d& up);\n\n            virtual void handleMouseMoveEvent(int x, int y) = 0;\n            virtual void handleMouseScrollEvent(int x) = 0;\n\n            virtual void update(double dt) = 0;\n\n        protected:\n\n            void addShortcut(CSMPrefs::Shortcut* shortcut);\n\n        private:\n\n            bool mActive, mInverted;\n            double mCameraSensitivity;\n            double mSecondaryMoveMult;\n            double mWheelMoveMult;\n\n            osg::Camera* mCamera;\n\n            std::vector<CSMPrefs::Shortcut*> mShortcuts;\n    };\n\n    class FreeCameraController : public CameraController\n    {\n            Q_OBJECT\n\n        public:\n\n            FreeCameraController(QWidget* parent);\n\n            double getLinearSpeed() const;\n            double getRotationalSpeed() const;\n            double getSpeedMultiplier() const;\n\n            void setLinearSpeed(double value);\n            void setRotationalSpeed(double value);\n            void setSpeedMultiplier(double value);\n\n            void fixUpAxis(const osg::Vec3d& up);\n            void unfixUpAxis();\n\n            void handleMouseMoveEvent(int x, int y) override;\n            void handleMouseScrollEvent(int x) override;\n\n            void update(double dt) override;\n\n        private:\n\n            void yaw(double value);\n            void pitch(double value);\n            void roll(double value);\n            void translate(const osg::Vec3d& offset);\n\n            void stabilize();\n\n            bool mLockUpright, mModified;\n            bool mNaviPrimary, mNaviSecondary;\n            bool mFast, mFastAlternate;\n            bool mLeft, mRight, mForward, mBackward, mRollLeft, mRollRight;\n            osg::Vec3d mUp;\n\n            double mLinSpeed;\n            double mRotSpeed;\n            double mSpeedMult;\n\n        private slots:\n\n            void naviPrimary(bool active);\n            void naviSecondary(bool active);\n            void forward(bool active);\n            void left(bool active);\n            void backward(bool active);\n            void right(bool active);\n            void rollLeft(bool active);\n            void rollRight(bool active);\n            void alternateFast(bool active);\n            void swapSpeedMode();\n    };\n\n    class OrbitCameraController : public CameraController\n    {\n            Q_OBJECT\n\n        public:\n\n            OrbitCameraController(QWidget* parent);\n\n            osg::Vec3d getCenter() const;\n            double getOrbitSpeed() const;\n            double getOrbitSpeedMultiplier() const;\n            unsigned int getPickingMask() const;\n\n            void setCenter(const osg::Vec3d& center);\n            void setOrbitSpeed(double value);\n            void setOrbitSpeedMultiplier(double value);\n            void setPickingMask(unsigned int value);\n\n            void handleMouseMoveEvent(int x, int y) override;\n            void handleMouseScrollEvent(int x) override;\n\n            void update(double dt) override;\n\n            /// \\brief Flag controller to be re-initialized.\n            void reset();\n\n            void setConstRoll(bool enable);\n\n        private:\n\n            void initialize();\n\n            void rotateHorizontal(double value);\n            void rotateVertical(double value);\n            void roll(double value);\n            void translate(const osg::Vec3d& offset);\n            void zoom(double value);\n\n            bool mInitialized;\n            bool mNaviPrimary, mNaviSecondary;\n            bool mFast, mFastAlternate;\n            bool mLeft, mRight, mUp, mDown, mRollLeft, mRollRight;\n            unsigned int mPickingMask;\n            osg::Vec3d mCenter;\n            double mDistance;\n\n            double mOrbitSpeed;\n            double mOrbitSpeedMult;\n\n            bool mConstRoll;\n\n        private slots:\n\n            void naviPrimary(bool active);\n            void naviSecondary(bool active);\n            void up(bool active);\n            void left(bool active);\n            void down(bool active);\n            void right(bool active);\n            void rollLeft(bool active);\n            void rollRight(bool active);\n            void alternateFast(bool active);\n            void swapSpeedMode();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/cell.cpp",
    "content": "#include \"cell.hpp\"\n\n#include <math.h>\n\n#include <osg/PositionAttitudeTransform>\n#include <osg/Geode>\n#include <osg/Geometry>\n#include <osg/Group>\n\n#include <components/misc/stringops.hpp>\n#include <components/esm/loadcell.hpp>\n#include <components/esm/loadland.hpp>\n#include <components/sceneutil/pathgridutil.hpp>\n#include <components/terrain/terraingrid.hpp>\n\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/refcollection.hpp\"\n#include \"../../model/world/cellcoordinates.hpp\"\n\n#include \"cellwater.hpp\"\n#include \"cellborder.hpp\"\n#include \"cellarrow.hpp\"\n#include \"cellmarker.hpp\"\n#include \"mask.hpp\"\n#include \"pathgrid.hpp\"\n#include \"terrainstorage.hpp\"\n#include \"object.hpp\"\n#include \"instancedragmodes.hpp\"\n\nnamespace CSVRender\n{\n    class CellNodeContainer : public osg::Referenced\n    {\n        public:\n\n            CellNodeContainer(Cell* cell) : mCell(cell) {}\n\n            Cell* getCell(){ return mCell; }\n\n        private:\n\n            Cell* mCell;\n    };\n\n    class CellNodeCallback : public osg::NodeCallback\n    {\n        public:\n\n            void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n            {\n                traverse(node, nv);\n                CellNodeContainer* container = static_cast<CellNodeContainer*>(node->getUserData());\n                container->getCell()->updateLand();\n            }\n    };\n}\n\nbool CSVRender::Cell::removeObject (const std::string& id)\n{\n    std::map<std::string, Object *>::iterator iter =\n        mObjects.find (Misc::StringUtils::lowerCase (id));\n\n    if (iter==mObjects.end())\n        return false;\n\n    removeObject (iter);\n    return true;\n}\n\nstd::map<std::string, CSVRender::Object *>::iterator CSVRender::Cell::removeObject (\n    std::map<std::string, Object *>::iterator iter)\n{\n    delete iter->second;\n    mObjects.erase (iter++);\n    return iter;\n}\n\nbool CSVRender::Cell::addObjects (int start, int end)\n{\n    bool modified = false;\n\n    const CSMWorld::RefCollection& collection = mData.getReferences();\n\n    for (int i=start; i<=end; ++i)\n    {\n        std::string cell = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mCell);\n\n        CSMWorld::RecordBase::State state = collection.getRecord (i).mState;\n\n        if (cell==mId && state!=CSMWorld::RecordBase::State_Deleted)\n        {\n            std::string id = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mId);\n\n            std::unique_ptr<Object> object (new Object (mData, mCellNode, id, false));\n\n            if (mSubModeElementMask & Mask_Reference)\n                object->setSubMode (mSubMode);\n\n            mObjects.insert (std::make_pair (id, object.release()));\n            modified = true;\n        }\n    }\n\n    return modified;\n}\n\nvoid CSVRender::Cell::updateLand()\n{\n    if (!mUpdateLand || mLandDeleted)\n        return;\n\n    mUpdateLand = false;\n\n    // Cell is deleted\n    if (mDeleted)\n    {\n        unloadLand();\n        return;\n    }\n\n    // Setup land if available\n    const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();\n    int landIndex = land.searchId(mId);\n    if (landIndex != -1 && !land.getRecord(mId).isDeleted())\n    {\n        const ESM::Land& esmLand = land.getRecord(mId).get();\n\n        if (esmLand.getLandData (ESM::Land::DATA_VHGT))\n        {\n            if (mTerrain)\n            {\n                mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());\n                mTerrain->clearAssociatedCaches();\n            }\n            else\n            {\n                mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode,\n                    mData.getResourceSystem().get(), mTerrainStorage, Mask_Terrain));\n            }\n\n            mTerrain->loadCell(esmLand.mX, esmLand.mY);\n\n            if (!mCellBorder)\n                mCellBorder.reset(new CellBorder(mCellNode, mCoordinates));\n\n            mCellBorder->buildShape(esmLand);\n\n            return;\n        }\n    }\n\n    // No land data\n    unloadLand();\n}\n\nvoid  CSVRender::Cell::unloadLand()\n{\n    if (mTerrain)\n        mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());\n\n    if (mCellBorder)\n        mCellBorder.reset();\n}\n\nCSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,\n    bool deleted)\n: mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0),\n  mSubModeElementMask (0), mUpdateLand(true), mLandDeleted(false)\n{\n    std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id);\n\n    mTerrainStorage = new TerrainStorage(mData);\n\n    if (result.second)\n        mCoordinates = result.first;\n\n    mCellNode = new osg::Group;\n    mCellNode->setUserData(new CellNodeContainer(this));\n    mCellNode->setUpdateCallback(new CellNodeCallback);\n    rootNode->addChild(mCellNode);\n\n    setCellMarker();\n\n    if (!mDeleted)\n    {\n        CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (\n            *mData.getTableModel (CSMWorld::UniversalId::Type_References));\n\n        int rows = references.rowCount();\n\n        addObjects (0, rows-1);\n\n        updateLand();\n\n        mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates));\n        mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates));\n    }\n}\n\nCSVRender::Cell::~Cell()\n{\n    for (std::map<std::string, Object *>::iterator iter (mObjects.begin());\n        iter!=mObjects.end(); ++iter)\n        delete iter->second;\n\n    mCellNode->getParent(0)->removeChild(mCellNode);\n}\n\nCSVRender::Pathgrid* CSVRender::Cell::getPathgrid() const\n{\n    return mPathgrid.get();\n}\n\nbool CSVRender::Cell::referenceableDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    bool modified = false;\n\n    for (std::map<std::string, Object *>::iterator iter (mObjects.begin());\n        iter!=mObjects.end(); ++iter)\n        if (iter->second->referenceableDataChanged (topLeft, bottomRight))\n            modified = true;\n\n    return modified;\n}\n\nbool CSVRender::Cell::referenceableAboutToBeRemoved (const QModelIndex& parent, int start,\n    int end)\n{\n    if (parent.isValid())\n        return false;\n\n    bool modified = false;\n\n    for (std::map<std::string, Object *>::iterator iter (mObjects.begin());\n        iter!=mObjects.end(); ++iter)\n        if (iter->second->referenceableAboutToBeRemoved (parent, start, end))\n            modified = true;\n\n    return modified;\n}\n\nbool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    if (mDeleted)\n        return false;\n\n    CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (\n        *mData.getTableModel (CSMWorld::UniversalId::Type_References));\n\n    int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);\n    int stateColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification);\n\n    // list IDs in cell\n    std::map<std::string, bool> ids; // id, deleted state\n\n    for (int i=topLeft.row(); i<=bottomRight.row(); ++i)\n    {\n        std::string cell = Misc::StringUtils::lowerCase (references.data (\n            references.index (i, cellColumn)).toString().toUtf8().constData());\n\n        if (cell==mId)\n        {\n            std::string id = Misc::StringUtils::lowerCase (references.data (\n                references.index (i, idColumn)).toString().toUtf8().constData());\n\n            int state = references.data (references.index (i, stateColumn)).toInt();\n\n            ids.insert (std::make_pair (id, state==CSMWorld::RecordBase::State_Deleted));\n        }\n    }\n\n    // perform update and remove where needed\n    bool modified = false;\n\n    std::map<std::string, Object *>::iterator iter = mObjects.begin();\n    while (iter!=mObjects.end())\n    {\n        if (iter->second->referenceDataChanged (topLeft, bottomRight))\n            modified = true;\n\n        std::map<std::string, bool>::iterator iter2 = ids.find (iter->first);\n\n        if (iter2!=ids.end())\n        {\n            bool deleted = iter2->second;\n            ids.erase (iter2);\n\n            if (deleted)\n            {\n                iter = removeObject (iter);\n                modified = true;\n                continue;\n            }\n        }\n\n        ++iter;\n    }\n\n    // add new objects\n    for (std::map<std::string, bool>::iterator mapIter (ids.begin()); mapIter!=ids.end(); ++mapIter)\n    {\n        if (!mapIter->second)\n        {\n            mObjects.insert (std::make_pair (\n                mapIter->first, new Object (mData, mCellNode, mapIter->first, false)));\n\n            modified = true;\n        }\n    }\n\n    return modified;\n}\n\nbool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int start,\n    int end)\n{\n    if (parent.isValid())\n        return false;\n\n    if (mDeleted)\n        return false;\n\n    CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (\n        *mData.getTableModel (CSMWorld::UniversalId::Type_References));\n\n    int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n\n    bool modified = false;\n\n    for (int row = start; row<=end; ++row)\n        if (removeObject (references.data (\n            references.index (row, idColumn)).toString().toUtf8().constData()))\n            modified = true;\n\n    return modified;\n}\n\nbool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int end)\n{\n    if (parent.isValid())\n        return false;\n\n    if (mDeleted)\n        return false;\n\n    return addObjects (start, end);\n}\n\nvoid CSVRender::Cell::setAlteredHeight(int inCellX, int inCellY, float height)\n{\n    mTerrainStorage->setAlteredHeight(inCellX, inCellY, height);\n    mUpdateLand = true;\n}\n\nfloat CSVRender::Cell::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY)\n{\n    return mTerrainStorage->getSumOfAlteredAndTrueHeight(cellX, cellY, inCellX, inCellY);\n}\n\nfloat* CSVRender::Cell::getAlteredHeight(int inCellX, int inCellY)\n{\n    return mTerrainStorage->getAlteredHeight(inCellX, inCellY);\n}\n\nvoid CSVRender::Cell::resetAlteredHeights()\n{\n    mTerrainStorage->resetHeights();\n    mUpdateLand = true;\n}\n\nvoid CSVRender::Cell::pathgridModified()\n{\n    if (mPathgrid)\n        mPathgrid->recreateGeometry();\n}\n\nvoid CSVRender::Cell::pathgridRemoved()\n{\n    if (mPathgrid)\n        mPathgrid->removeGeometry();\n}\n\nvoid CSVRender::Cell::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    mUpdateLand = true;\n}\n\nvoid CSVRender::Cell::landAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    mLandDeleted = true;\n    unloadLand();\n}\n\nvoid CSVRender::Cell::landAdded (const QModelIndex& parent, int start, int end)\n{\n    mUpdateLand = true;\n    mLandDeleted = false;\n}\n\nvoid CSVRender::Cell::landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    mUpdateLand = true;\n}\n\nvoid CSVRender::Cell::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    mUpdateLand = true;\n}\n\nvoid CSVRender::Cell::landTextureAdded (const QModelIndex& parent, int start, int end)\n{\n    mUpdateLand = true;\n}\n\nvoid CSVRender::Cell::reloadAssets()\n{\n    for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());\n        iter != mObjects.end(); ++iter)\n    {\n        iter->second->reloadAssets();\n    }\n\n    if (mTerrain)\n    {\n        mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());\n        mTerrain->loadCell(mCoordinates.getX(), mCoordinates.getY());\n    }\n\n    if (mCellWater)\n        mCellWater->reloadAssets();\n}\n\nvoid CSVRender::Cell::setSelection (int elementMask, Selection mode)\n{\n    if (elementMask & Mask_Reference)\n    {\n        for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());\n            iter!=mObjects.end(); ++iter)\n        {\n            bool selected = false;\n\n            switch (mode)\n            {\n                case Selection_Clear: selected = false; break;\n                case Selection_All: selected = true; break;\n                case Selection_Invert: selected = !iter->second->getSelected(); break;\n            }\n\n            iter->second->setSelected (selected);\n        }\n    }\n    if (mPathgrid && elementMask & Mask_Pathgrid)\n    {\n        // Only one pathgrid may be selected, so some operations will only have an effect\n        // if the pathgrid is already focused\n        switch (mode)\n        {\n            case Selection_Clear:\n                mPathgrid->clearSelected();\n                break;\n\n            case Selection_All:\n                if (mPathgrid->isSelected())\n                    mPathgrid->selectAll();\n                break;\n\n            case Selection_Invert:\n                if (mPathgrid->isSelected())\n                    mPathgrid->invertSelected();\n                break;\n        }\n    }\n}\n\nvoid CSVRender::Cell::selectAllWithSameParentId (int elementMask)\n{\n    std::set<std::string> ids;\n\n    for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());\n        iter!=mObjects.end(); ++iter)\n    {\n        if (iter->second->getSelected())\n            ids.insert (iter->second->getReferenceableId());\n    }\n\n    for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());\n        iter!=mObjects.end(); ++iter)\n    {\n        if (!iter->second->getSelected() &&\n            ids.find (iter->second->getReferenceableId())!=ids.end())\n        {\n            iter->second->setSelected (true);\n        }\n    }\n}\n\nvoid CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)\n{\n    if (dragMode == DragMode_Select_Only || dragMode == DragMode_Select_Add)\n        object->setSelected(true);\n\n    else if (dragMode == DragMode_Select_Remove)\n        object->setSelected(false);\n\n    else if (dragMode == DragMode_Select_Invert)\n        object->setSelected (!object->getSelected());\n}\n\nvoid CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode)\n{\n    for (auto& object : mObjects)\n    {\n        if (dragMode == DragMode_Select_Only) object.second->setSelected (false);\n\n        if ( ( object.second->getPosition().pos[0] > pointA[0] && object.second->getPosition().pos[0] < pointB[0] ) ||\n             ( object.second->getPosition().pos[0] > pointB[0] && object.second->getPosition().pos[0] < pointA[0] ))\n        {\n            if ( ( object.second->getPosition().pos[1] > pointA[1] && object.second->getPosition().pos[1] < pointB[1] ) ||\n                 ( object.second->getPosition().pos[1] > pointB[1] && object.second->getPosition().pos[1] < pointA[1] ))\n            {\n                if ( ( object.second->getPosition().pos[2] > pointA[2] && object.second->getPosition().pos[2] < pointB[2] ) ||\n                     ( object.second->getPosition().pos[2] > pointB[2] && object.second->getPosition().pos[2] < pointA[2] ))\n                    handleSelectDrag(object.second, dragMode);\n            }\n\n        }\n    }\n}\n\nvoid CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode)\n{\n    for (auto& object : mObjects)\n    {\n        if (dragMode == DragMode_Select_Only) object.second->setSelected (false);\n\n        float distanceFromObject = (point - object.second->getPosition().asVec3()).length();\n        if (distanceFromObject < distance) handleSelectDrag(object.second, dragMode);\n    }\n}\n\nvoid CSVRender::Cell::setCellArrows (int mask)\n{\n    for (int i=0; i<4; ++i)\n    {\n        CellArrow::Direction direction = static_cast<CellArrow::Direction> (1<<i);\n\n        bool enable = mask & direction;\n\n        if (enable!=(mCellArrows[i].get()!=nullptr))\n        {\n            if (enable)\n                mCellArrows[i].reset (new CellArrow (mCellNode, direction, mCoordinates));\n            else\n                mCellArrows[i].reset (nullptr);\n        }\n    }\n}\n\nvoid CSVRender::Cell::setCellMarker()\n{\n    bool cellExists = false;\n    bool isInteriorCell = false;\n\n    int cellIndex = mData.getCells().searchId(mId);\n    if (cellIndex > -1)\n    {\n        const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mData.getCells().getRecord(cellIndex);\n        cellExists = !cellRecord.isDeleted();\n        isInteriorCell = cellRecord.get().mData.mFlags & ESM::Cell::Interior;\n    }\n\n    if (!isInteriorCell) {\n        mCellMarker.reset(new CellMarker(mCellNode, mCoordinates, cellExists));\n    }\n}\n\nCSMWorld::CellCoordinates CSVRender::Cell::getCoordinates() const\n{\n    return mCoordinates;\n}\n\nbool CSVRender::Cell::isDeleted() const\n{\n    return mDeleted;\n}\n\nstd::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getSelection (unsigned int elementMask) const\n{\n    std::vector<osg::ref_ptr<TagBase> > result;\n\n    if (elementMask & Mask_Reference)\n        for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());\n            iter!=mObjects.end(); ++iter)\n            if (iter->second->getSelected())\n                result.push_back (iter->second->getTag());\n    if (mPathgrid && elementMask & Mask_Pathgrid)\n        if (mPathgrid->isSelected())\n            result.emplace_back(mPathgrid->getTag());\n\n    return result;\n}\n\nstd::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getEdited (unsigned int elementMask) const\n{\n    std::vector<osg::ref_ptr<TagBase> > result;\n\n    if (elementMask & Mask_Reference)\n        for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());\n            iter!=mObjects.end(); ++iter)\n            if (iter->second->isEdited())\n                result.push_back (iter->second->getTag());\n\n    return result;\n}\n\nvoid CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask)\n{\n    mSubMode = subMode;\n    mSubModeElementMask = elementMask;\n\n    if (elementMask & Mask_Reference)\n        for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());\n            iter!=mObjects.end(); ++iter)\n                iter->second->setSubMode (subMode);\n}\n\nvoid CSVRender::Cell::reset (unsigned int elementMask)\n{\n    if (elementMask & Mask_Reference)\n        for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());\n            iter!=mObjects.end(); ++iter)\n            iter->second->reset();\n    if (mPathgrid && elementMask & Mask_Pathgrid)\n        mPathgrid->resetIndicators();\n}\n"
  },
  {
    "path": "apps/opencs/view/render/cell.hpp",
    "content": "#ifndef OPENCS_VIEW_CELL_H\n#define OPENCS_VIEW_CELL_H\n\n#include <string>\n#include <map>\n#include <memory>\n#include <vector>\n\n#include <osg/ref_ptr>\n\n#include \"../../model/world/cellcoordinates.hpp\"\n#include \"terrainstorage.hpp\"\n#include \"instancedragmodes.hpp\"\n\nclass QModelIndex;\n\nnamespace osg\n{\n    class Group;\n    class Geometry;\n    class Geode;\n}\n\nnamespace CSMWorld\n{\n    class Data;\n}\n\nnamespace Terrain\n{\n    class TerrainGrid;\n}\n\nnamespace CSVRender\n{\n    class CellWater;\n    class Pathgrid;\n    class TagBase;\n    class Object;\n\n    class CellArrow;\n    class CellBorder;\n    class CellMarker;\n    class CellWater;\n\n    class Cell\n    {\n            CSMWorld::Data& mData;\n            std::string mId;\n            osg::ref_ptr<osg::Group> mCellNode;\n            std::map<std::string, Object *> mObjects;\n            std::unique_ptr<Terrain::TerrainGrid> mTerrain;\n            CSMWorld::CellCoordinates mCoordinates;\n            std::unique_ptr<CellArrow> mCellArrows[4];\n            std::unique_ptr<CellMarker> mCellMarker;\n            std::unique_ptr<CellBorder> mCellBorder;\n            std::unique_ptr<CellWater> mCellWater;\n            std::unique_ptr<Pathgrid> mPathgrid;\n            bool mDeleted;\n            int mSubMode;\n            unsigned int mSubModeElementMask;\n            bool mUpdateLand, mLandDeleted;\n            TerrainStorage *mTerrainStorage;\n\n            /// Ignored if cell does not have an object with the given ID.\n            ///\n            /// \\return Was the object deleted?\n            bool removeObject (const std::string& id);\n\n            // Remove object and return iterator to next object.\n            std::map<std::string, Object *>::iterator removeObject (\n                std::map<std::string, Object *>::iterator iter);\n\n            /// Add objects from reference table that are within this cell.\n            ///\n            /// \\return Have any objects been added?\n            bool addObjects (int start, int end);\n\n            void updateLand();\n            void unloadLand();\n\n        public:\n\n            enum Selection\n            {\n                Selection_Clear,\n                Selection_All,\n                Selection_Invert\n            };\n\n        public:\n\n            /// \\note Deleted covers both cells that are deleted and cells that don't exist in\n            /// the first place.\n            Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,\n                bool deleted = false);\n\n            ~Cell();\n\n            /// \\note Returns the pathgrid representation which will exist as long as the cell exists\n            Pathgrid* getPathgrid() const;\n\n            /// \\return Did this call result in a modification of the visual representation of\n            /// this cell?\n            bool referenceableDataChanged (const QModelIndex& topLeft,\n                const QModelIndex& bottomRight);\n\n            /// \\return Did this call result in a modification of the visual representation of\n            /// this cell?\n            bool referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            /// \\return Did this call result in a modification of the visual representation of\n            /// this cell?\n            bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            /// \\return Did this call result in a modification of the visual representation of\n            /// this cell?\n            bool referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            /// \\return Did this call result in a modification of the visual representation of\n            /// this cell?\n            bool referenceAdded (const QModelIndex& parent, int start, int end);\n\n            void setAlteredHeight(int inCellX, int inCellY, float height);\n\n            float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY);\n\n            float* getAlteredHeight(int inCellX, int inCellY);\n\n            void resetAlteredHeights();\n\n            void pathgridModified();\n\n            void pathgridRemoved();\n\n            void landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void landAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void landAdded (const QModelIndex& parent, int start, int end);\n\n            void landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void landTextureAdded (const QModelIndex& parent, int start, int end);\n\n            void reloadAssets();\n\n            void setSelection (int elementMask, Selection mode);\n\n            // Select everything that references the same ID as at least one of the elements\n            // already selected\n            void selectAllWithSameParentId (int elementMask);\n\n            void handleSelectDrag(Object* object, DragMode dragMode);\n\n            void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode);\n\n            void selectWithinDistance(const osg::Vec3d& pointA, float distance, DragMode dragMode);\n\n            void setCellArrows (int mask);\n\n            /// \\brief Set marker for this cell.\n            void setCellMarker();\n\n            /// Returns 0, 0 in case of an unpaged cell.\n            CSMWorld::CellCoordinates getCoordinates() const;\n\n            bool isDeleted() const;\n\n            std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) const;\n\n            std::vector<osg::ref_ptr<TagBase> > getEdited (unsigned int elementMask) const;\n\n            void setSubMode (int subMode, unsigned int elementMask);\n\n            /// Erase all overrides and restore the visual representation of the cell to its\n            /// true state.\n            void reset (unsigned int elementMask);\n\n            friend class CellNodeCallback;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/cellarrow.cpp",
    "content": "\n#include \"cellarrow.hpp\"\n\n#include <osg/Group>\n#include <osg/PositionAttitudeTransform>\n#include <osg/Geode>\n#include <osg/Geometry>\n#include <osg/PrimitiveSet>\n\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/prefs/shortcutmanager.hpp\"\n\n#include <components/misc/constants.hpp>\n\n#include \"mask.hpp\"\n\nCSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow)\n: TagBase (Mask_CellArrow), mArrow (arrow)\n{}\n\nCSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const\n{\n    return mArrow;\n}\n\nQString CSVRender::CellArrowTag::getToolTip (bool hideBasics) const\n{\n    QString text (\"Direction: \");\n\n    switch (mArrow->getDirection())\n    {\n        case CellArrow::Direction_North: text += \"North\"; break;\n        case CellArrow::Direction_West: text += \"West\"; break;\n        case CellArrow::Direction_South: text += \"South\"; break;\n        case CellArrow::Direction_East: text += \"East\"; break;\n    }\n\n    if (!hideBasics)\n    {\n        text +=\n            \"<p>\"\n            \"Modify which cells are shown\"\n            \"<ul><li>{scene-edit-primary}: Add cell in given direction</li>\"\n            \"<li>{scene-edit-secondary}: Add cell and remove old cell</li>\"\n            \"<li>{scene-select-primary}: Add cells in given direction</li>\"\n            \"<li>{scene-select-secondary}: Add cells and remove old cells</li>\"\n            \"<li>{scene-load-cam-cell}: Load cell where camera is located</li>\"\n            \"<li>{scene-load-cam-eastcell}: Load cell to east</li>\"\n            \"<li>{scene-load-cam-northcell}: Load cell to north</li>\"\n            \"<li>{scene-load-cam-westcell}: Load cell to west</li>\"\n            \"<li>{scene-load-cam-southcell}: Load cell to south</li>\"\n            \"</ul>\";\n    }\n\n    return CSMPrefs::State::get().getShortcutManager().processToolTip(text);\n}\n\n\nvoid CSVRender::CellArrow::adjustTransform()\n{\n    // position\n    const int cellSize = Constants::CellSizeInUnits;\n    const int offset = cellSize / 2 + 800;\n\n    int x = mCoordinates.getX()*cellSize + cellSize/2;\n    int y = mCoordinates.getY()*cellSize + cellSize/2;\n\n    float xr = 0;\n    float yr = 0;\n    float zr = 0;\n\n    float angle = osg::DegreesToRadians (90.0f);\n\n    switch (mDirection)\n    {\n        case Direction_North: y += offset; xr = -angle; zr = angle; break;\n        case Direction_West: x -= offset; yr = -angle; break;\n        case Direction_South: y -= offset; xr = angle; zr = angle; break;\n        case Direction_East: x += offset; yr = angle; break;\n    };\n\n    mBaseNode->setPosition (osg::Vec3f (x, y, 0));\n\n    // orientation\n    osg::Quat xr2 (xr, osg::Vec3f (1,0,0));\n    osg::Quat yr2 (yr, osg::Vec3f (0,1,0));\n    osg::Quat zr2 (zr, osg::Vec3f (0,0,1));\n    mBaseNode->setAttitude (zr2*yr2*xr2);\n}\n\nvoid CSVRender::CellArrow::buildShape()\n{\n    osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);\n\n    const int arrowWidth = 4000;\n    const int arrowLength = 1500;\n    const int arrowHeight = 500;\n\n    osg::Vec3Array *vertices = new osg::Vec3Array;\n    for (int i2=0; i2<2; ++i2)\n        for (int i=0; i<2; ++i)\n        {\n            float height = i ? -arrowHeight/2 : arrowHeight/2;\n            vertices->push_back (osg::Vec3f (height, -arrowWidth/2, 0));\n            vertices->push_back (osg::Vec3f (height, arrowWidth/2, 0));\n            vertices->push_back (osg::Vec3f (height, 0, arrowLength));\n        }\n\n    geometry->setVertexArray (vertices);\n\n    osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0);\n\n    // top\n    primitives->push_back (0);\n    primitives->push_back (1);\n    primitives->push_back (2);\n\n    // bottom\n    primitives->push_back (5);\n    primitives->push_back (4);\n    primitives->push_back (3);\n\n    // back\n    primitives->push_back (3+6);\n    primitives->push_back (4+6);\n    primitives->push_back (1+6);\n\n    primitives->push_back (3+6);\n    primitives->push_back (1+6);\n    primitives->push_back (0+6);\n\n    // sides\n    primitives->push_back (0+6);\n    primitives->push_back (2+6);\n    primitives->push_back (5+6);\n\n    primitives->push_back (0+6);\n    primitives->push_back (5+6);\n    primitives->push_back (3+6);\n\n    primitives->push_back (4+6);\n    primitives->push_back (5+6);\n    primitives->push_back (2+6);\n\n    primitives->push_back (4+6);\n    primitives->push_back (2+6);\n    primitives->push_back (1+6);\n\n    geometry->addPrimitiveSet (primitives);\n\n    osg::Vec4Array *colours = new osg::Vec4Array;\n\n    for (int i=0; i<6; ++i)\n        colours->push_back (osg::Vec4f (0.11f, 0.6f, 0.95f, 1.0f));\n    for (int i=0; i<6; ++i)\n        colours->push_back (osg::Vec4f (0.08f, 0.44f, 0.7f, 1.0f));\n\n    geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX);\n\n    geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF);\n\n    osg::ref_ptr<osg::Geode> geode (new osg::Geode);\n    geode->addDrawable (geometry);\n\n    mBaseNode->addChild (geode);\n}\n\nCSVRender::CellArrow::CellArrow (osg::Group *cellNode, Direction direction,\n    const CSMWorld::CellCoordinates& coordinates)\n: mDirection (direction), mParentNode (cellNode), mCoordinates (coordinates)\n{\n    mBaseNode = new osg::PositionAttitudeTransform;\n\n    mBaseNode->setUserData (new CellArrowTag (this));\n\n    mParentNode->addChild (mBaseNode);\n\n    mBaseNode->setNodeMask (Mask_CellArrow);\n\n    adjustTransform();\n    buildShape();\n}\n\nCSVRender::CellArrow::~CellArrow()\n{\n    mParentNode->removeChild (mBaseNode);\n}\n\nCSMWorld::CellCoordinates CSVRender::CellArrow::getCoordinates() const\n{\n    return mCoordinates;\n}\n\nCSVRender::CellArrow::Direction CSVRender::CellArrow::getDirection() const\n{\n    return mDirection;\n}\n"
  },
  {
    "path": "apps/opencs/view/render/cellarrow.hpp",
    "content": "#ifndef OPENCS_VIEW_CELLARROW_H\n#define OPENCS_VIEW_CELLARROW_H\n\n#include \"tagbase.hpp\"\n\n#include <osg/ref_ptr>\n\n#include \"../../model/world/cellcoordinates.hpp\"\n\nnamespace osg\n{\n    class PositionAttitudeTransform;\n    class Group;\n}\n\nnamespace CSVRender\n{\n    class CellArrow;\n\n    class CellArrowTag : public TagBase\n    {\n            CellArrow *mArrow;\n\n        public:\n\n            CellArrowTag (CellArrow *arrow);\n\n            CellArrow *getCellArrow() const;\n\n            QString getToolTip (bool hideBasics) const override;\n    };\n\n\n    class CellArrow\n    {\n        public:\n\n            enum Direction\n            {\n                Direction_North = 1,\n                Direction_West = 2,\n                Direction_South = 4,\n                Direction_East = 8\n            };\n\n        private:\n\n            // not implemented\n            CellArrow (const CellArrow&);\n            CellArrow& operator= (const CellArrow&);\n\n            Direction mDirection;\n            osg::Group* mParentNode;\n            osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;\n            CSMWorld::CellCoordinates mCoordinates;\n\n            void adjustTransform();\n\n            void buildShape();\n\n        public:\n\n            CellArrow (osg::Group *cellNode, Direction direction,\n                const CSMWorld::CellCoordinates& coordinates);\n\n            ~CellArrow();\n\n            CSMWorld::CellCoordinates getCoordinates() const;\n\n            Direction getDirection() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/cellborder.cpp",
    "content": "#include \"cellborder.hpp\"\n\n#include <osg/Group>\n#include <osg/PositionAttitudeTransform>\n#include <osg/Geometry>\n#include <osg/PrimitiveSet>\n\n#include <components/esm/loadland.hpp>\n\n#include \"mask.hpp\"\n\n#include \"../../model/world/cellcoordinates.hpp\"\n\nconst int CSVRender::CellBorder::CellSize = ESM::Land::REAL_SIZE;\n\n/*\n    The number of vertices per cell border is equal to the number of vertices per edge\n    minus the duplicated corner vertices. An additional vertex to close the loop is NOT needed.\n*/\nconst int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 4;\n\n\nCSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords)\n    : mParentNode(cellNode)\n{\n    mBorderGeometry = new osg::Geometry();\n    \n    mBaseNode = new osg::PositionAttitudeTransform();\n    mBaseNode->setNodeMask(Mask_CellBorder);\n    mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10));\n    mBaseNode->addChild(mBorderGeometry);\n\n    mParentNode->addChild(mBaseNode);\n}\n\nCSVRender::CellBorder::~CellBorder()\n{\n    mParentNode->removeChild(mBaseNode);\n}\n\nvoid CSVRender::CellBorder::buildShape(const ESM::Land& esmLand)\n{\n    const ESM::Land::LandData* landData = esmLand.getLandData(ESM::Land::DATA_VHGT);\n\n    if (!landData)\n        return;\n\n    mBaseNode->removeChild(mBorderGeometry);\n    mBorderGeometry = new osg::Geometry();\n\n    osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array();\n\n    int x = 0;\n    int y = 0;\n\n    /*\n        Traverse the cell border counter-clockwise starting at the SW corner vertex (0, 0).\n        Each loop starts at a corner vertex and ends right before the next corner vertex.\n    */\n    for (; x < ESM::Land::LAND_SIZE - 1; ++x)\n        vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));\n\n    x = ESM::Land::LAND_SIZE - 1;\n    for (; y < ESM::Land::LAND_SIZE - 1; ++y)\n        vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));\n\n    y = ESM::Land::LAND_SIZE - 1;\n    for (; x > 0; --x)\n        vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));\n\n    x = 0;\n    for (; y > 0; --y)\n        vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));\n\n    mBorderGeometry->setVertexArray(vertices);\n\n    osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array();\n    colors->push_back(osg::Vec4f(0.f, 0.5f, 0.f, 1.f));\n\n    mBorderGeometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET);\n\n    osg::ref_ptr<osg::DrawElementsUShort> primitives =\n        new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount + 1);\n\n    // Assign one primitive to each vertex.\n    for (size_t i = 0; i < VertexCount; ++i)\n        primitives->setElement(i, i);\n\n    // Assign the last primitive to the first vertex to close the loop.\n    primitives->setElement(VertexCount, 0);\n\n    mBorderGeometry->addPrimitiveSet(primitives);\n    mBorderGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n\n    mBaseNode->addChild(mBorderGeometry);\n}\n\nsize_t CSVRender::CellBorder::landIndex(int x, int y)\n{\n    return static_cast<size_t>(y) * ESM::Land::LAND_SIZE + x;\n}\n\nfloat CSVRender::CellBorder::scaleToWorld(int value)\n{\n    return (CellSize + 128) * (float)value / ESM::Land::LAND_SIZE;\n}\n"
  },
  {
    "path": "apps/opencs/view/render/cellborder.hpp",
    "content": "#ifndef OPENCS_VIEW_CELLBORDER_H\n#define OPENCS_VIEW_CELLBORDER_H\n\n#include <cstddef>\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class Geometry;\n    class Group;\n    class PositionAttitudeTransform;\n}\n\nnamespace ESM\n{\n    struct Land;\n}\n\nnamespace CSMWorld\n{\n    class CellCoordinates;\n}\n\nnamespace CSVRender\n{\n\n    class CellBorder\n    {\n    public:\n\n        CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords);\n        ~CellBorder();\n\n        void buildShape(const ESM::Land& esmLand);\n\n    private:\n\n        static const int CellSize;\n        static const int VertexCount;\n\n        size_t landIndex(int x, int y);\n        float scaleToWorld(int val);\n\n        // unimplemented\n        CellBorder(const CellBorder&);\n        CellBorder& operator=(const CellBorder&);\n\n        osg::Group* mParentNode;\n        osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;\n        osg::ref_ptr<osg::Geometry> mBorderGeometry;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/cellmarker.cpp",
    "content": "#include \"cellmarker.hpp\"\n\n#include <osg/AutoTransform>\n#include <osg/Material>\n#include <osg/Geode>\n#include <osgText/Text>\n\n#include <components/misc/constants.hpp>\n\nCSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker)\n: TagBase(Mask_CellMarker), mMarker(marker)\n{}\n\nCSVRender::CellMarker *CSVRender::CellMarkerTag::getCellMarker() const\n{\n    return mMarker;\n}\n\nvoid CSVRender::CellMarker::buildMarker()\n{\n    const int characterSize = 20;\n\n    // Set up attributes of marker text.\n    osg::ref_ptr<osgText::Text> markerText (new osgText::Text);\n    markerText->setLayout(osgText::Text::LEFT_TO_RIGHT);\n    markerText->setCharacterSize(characterSize);\n    markerText->setAlignment(osgText::Text::CENTER_CENTER);\n    markerText->setDrawMode(osgText::Text::TEXT | osgText::Text::FILLEDBOUNDINGBOX);\n\n    // If cell exists then show black bounding box otherwise show red.\n    if (mExists)\n    {\n        markerText->setBoundingBoxColor(osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f));\n    }\n    else\n    {\n        markerText->setBoundingBoxColor(osg::Vec4f(1.0f, 0.0f, 0.0f, 1.0f));\n    }\n\n    // Add text containing cell's coordinates.\n    std::string coordinatesText =\n        std::to_string(mCoordinates.getX()) + \",\" +\n        std::to_string(mCoordinates.getY());\n    markerText->setText(coordinatesText);\n\n    // Add text to marker node.\n    osg::ref_ptr<osg::Geode> geode (new osg::Geode);\n    geode->addDrawable(markerText);\n    mMarkerNode->addChild(geode);\n}\n\nvoid CSVRender::CellMarker::positionMarker()\n{\n    const int cellSize = Constants::CellSizeInUnits;\n    const int markerHeight = 0;\n\n    // Move marker to center of cell.\n    int x = (mCoordinates.getX() * cellSize) + (cellSize / 2);\n    int y = (mCoordinates.getY() * cellSize) + (cellSize / 2);\n    mMarkerNode->setPosition(osg::Vec3f(x, y, markerHeight));\n}\n\nCSVRender::CellMarker::CellMarker(\n    osg::Group *cellNode,\n    const CSMWorld::CellCoordinates& coordinates,\n    const bool cellExists\n) : mCellNode(cellNode),\n    mCoordinates(coordinates),\n    mExists(cellExists)\n{\n    // Set up node for cell marker.\n    mMarkerNode = new osg::AutoTransform();\n    mMarkerNode->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);\n    mMarkerNode->setAutoScaleToScreen(true);\n    mMarkerNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n    mMarkerNode->getOrCreateStateSet()->setRenderBinDetails(11, \"RenderBin\");\n    osg::ref_ptr<osg::Material> mat = new osg::Material;\n    mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);\n    mMarkerNode->getOrCreateStateSet()->setAttribute(mat);\n\n    mMarkerNode->setUserData(new CellMarkerTag(this));\n    mMarkerNode->setNodeMask(Mask_CellMarker);\n\n    mCellNode->addChild(mMarkerNode);\n\n    buildMarker();\n    positionMarker();\n}\n\nCSVRender::CellMarker::~CellMarker()\n{\n    mCellNode->removeChild(mMarkerNode);\n}\n"
  },
  {
    "path": "apps/opencs/view/render/cellmarker.hpp",
    "content": "#ifndef OPENCS_VIEW_CELLMARKER_H\n#define OPENCS_VIEW_CELLMARKER_H\n\n#include \"tagbase.hpp\"\n\n#include <osg/ref_ptr>\n\n#include \"../../model/world/cellcoordinates.hpp\"\n\nnamespace osg\n{\n    class AutoTransform;\n    class Group;\n}\n\nnamespace CSVRender\n{\n    class CellMarker;\n\n    class CellMarkerTag : public TagBase\n    {\n        private:\n\n            CellMarker *mMarker;\n\n        public:\n\n            CellMarkerTag(CellMarker *marker);\n\n            CellMarker *getCellMarker() const;\n    };\n\n    /// \\brief Marker to display cell coordinates.\n    class CellMarker\n    {\n        private:\n\n            osg::Group* mCellNode;\n            osg::ref_ptr<osg::AutoTransform> mMarkerNode;\n            CSMWorld::CellCoordinates mCoordinates;\n            bool mExists;\n\n            // Not implemented.\n            CellMarker(const CellMarker&);\n            CellMarker& operator=(const CellMarker&);\n\n            /// \\brief Build marker containing cell's coordinates.\n            void buildMarker();\n\n            /// \\brief Position marker at center of cell.\n            void positionMarker();\n\n        public:\n\n            /// \\brief Constructor.\n            /// \\param cellNode Cell to create marker for.\n            /// \\param coordinates Coordinates of cell.\n            /// \\param cellExists Whether or not cell exists.\n            CellMarker(\n                osg::Group *cellNode,\n                const CSMWorld::CellCoordinates& coordinates,\n                const bool cellExists);\n\n            ~CellMarker();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/cellwater.cpp",
    "content": "#include \"cellwater.hpp\"\n\n#include <osg/Geode>\n#include <osg/Geometry>\n#include <osg/Group>\n#include <osg/PositionAttitudeTransform>\n\n#include <components/esm/loadland.hpp>\n#include <components/fallback/fallback.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/resource/imagemanager.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/sceneutil/waterutil.hpp>\n\n#include \"../../model/world/cell.hpp\"\n#include \"../../model/world/cellcoordinates.hpp\"\n#include \"../../model/world/data.hpp\"\n\n#include \"mask.hpp\"\n\nnamespace CSVRender\n{\n    const int CellWater::CellSize = ESM::Land::REAL_SIZE;\n\n    CellWater::CellWater(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id,\n        const CSMWorld::CellCoordinates& cellCoords)\n        : mData(data)\n        , mId(id)\n        , mParentNode(cellNode)\n        , mWaterTransform(nullptr)\n        , mWaterNode(nullptr)\n        , mWaterGeometry(nullptr)\n        , mDeleted(false)\n        , mExterior(false)\n        , mHasWater(false)\n    {\n        mWaterTransform = new osg::PositionAttitudeTransform();\n        mWaterTransform->setPosition(osg::Vec3f(cellCoords.getX() * CellSize + CellSize / 2.f,\n            cellCoords.getY() * CellSize + CellSize / 2.f, 0));\n\n        mWaterTransform->setNodeMask(Mask_Water);\n        mParentNode->addChild(mWaterTransform);\n\n        mWaterNode = new osg::Geode();\n        mWaterTransform->addChild(mWaterNode);\n\n        int cellIndex = mData.getCells().searchId(mId);\n        if (cellIndex > -1)\n        {\n            updateCellData(mData.getCells().getRecord(cellIndex));\n        }\n\n        // Keep water existence/height up to date\n        QAbstractItemModel* cells = mData.getTableModel(CSMWorld::UniversalId::Type_Cells);\n        connect(cells, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),\n            this, SLOT(cellDataChanged(const QModelIndex&, const QModelIndex&)));\n    }\n\n    CellWater::~CellWater()\n    {\n        mParentNode->removeChild(mWaterTransform);\n    }\n\n    void CellWater::updateCellData(const CSMWorld::Record<CSMWorld::Cell>& cellRecord)\n    {\n        mDeleted = cellRecord.isDeleted();\n        if (!mDeleted)\n        {\n            const CSMWorld::Cell& cell = cellRecord.get();\n\n            if (mExterior != cell.isExterior() || mHasWater != cell.hasWater())\n            {\n                mExterior = cellRecord.get().isExterior();\n                mHasWater = cellRecord.get().hasWater();\n\n                recreate();\n            }\n\n            float waterHeight = -1;\n            if (!mExterior)\n            {\n                waterHeight = cellRecord.get().mWater;\n            }\n\n            osg::Vec3d pos = mWaterTransform->getPosition();\n            pos.z() = waterHeight;\n            mWaterTransform->setPosition(pos);\n        }\n        else\n        {\n            recreate();\n        }\n    }\n\n    void CellWater::reloadAssets()\n    {\n        recreate();\n    }\n\n    void CellWater::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)\n    {\n        const CSMWorld::Collection<CSMWorld::Cell>& cells = mData.getCells();\n\n        int rowStart = -1;\n        int rowEnd = -1;\n\n        if (topLeft.parent().isValid())\n        {\n            rowStart = topLeft.parent().row();\n            rowEnd = bottomRight.parent().row();\n        }\n        else\n        {\n            rowStart = topLeft.row();\n            rowEnd = bottomRight.row();\n        }\n\n        for (int row = rowStart; row <= rowEnd; ++row)\n        {\n            const CSMWorld::Record<CSMWorld::Cell>& cellRecord = cells.getRecord(row);\n\n            if (Misc::StringUtils::lowerCase(cellRecord.get().mId) == mId)\n                updateCellData(cellRecord);\n        }\n    }\n\n    void CellWater::recreate()\n    {\n        const int InteriorScalar = 20;\n        const int SegmentsPerCell = 1;\n        const int TextureRepeatsPerCell = 6;\n\n        const float Alpha = 0.5f;\n\n        const int RenderBin = osg::StateSet::TRANSPARENT_BIN - 1;\n\n        if (mWaterGeometry)\n        {\n            mWaterNode->removeDrawable(mWaterGeometry);\n            mWaterGeometry = nullptr;\n        }\n\n        if (mDeleted || !mHasWater)\n            return;\n\n        float size;\n        int segments;\n        float textureRepeats;\n\n        if (mExterior)\n        {\n            size = CellSize;\n            segments = SegmentsPerCell;\n            textureRepeats = TextureRepeatsPerCell;\n        }\n        else\n        {\n            size = CellSize * InteriorScalar;\n            segments = SegmentsPerCell * InteriorScalar;\n            textureRepeats = TextureRepeatsPerCell * InteriorScalar;\n        }\n\n        mWaterGeometry = SceneUtil::createWaterGeometry(size, segments, textureRepeats);\n        mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin));\n\n        // Add water texture\n        std::string textureName = Fallback::Map::getString(\"Water_SurfaceTexture\");\n        textureName = \"textures/water/\" + textureName + \"00.dds\";\n\n        Resource::ImageManager* imageManager = mData.getResourceSystem()->getImageManager();\n\n        osg::ref_ptr<osg::Texture2D> waterTexture = new osg::Texture2D();\n        waterTexture->setImage(imageManager->getImage(textureName));\n        waterTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n        waterTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n\n        mWaterGeometry->getStateSet()->setTextureAttributeAndModes(0, waterTexture, osg::StateAttribute::ON);\n\n\n        mWaterNode->addDrawable(mWaterGeometry);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/cellwater.hpp",
    "content": "#ifndef CSV_RENDER_CELLWATER_H\n#define CSV_RENDER_CELLWATER_H\n\n#include <string>\n\n#include <osg/ref_ptr>\n\n#include <QObject>\n#include <QModelIndex>\n\n#include \"../../model/world/record.hpp\"\n\nnamespace osg\n{\n    class Geode;\n    class Geometry;\n    class Group;\n    class PositionAttitudeTransform;\n}\n\nnamespace CSMWorld\n{\n    struct Cell;\n    class CellCoordinates;\n    class Data;\n}\n\nnamespace CSVRender\n{\n    /// For exterior cells, this adds a patch of water to fit the size of the cell. For interior cells with water, this\n    /// adds a large patch of water much larger than the typical size of a cell.\n    class CellWater : public QObject\n    {\n            Q_OBJECT\n\n        public:\n\n            CellWater(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id,\n                const CSMWorld::CellCoordinates& cellCoords);\n\n            ~CellWater();\n\n            void updateCellData(const CSMWorld::Record<CSMWorld::Cell>& cellRecord);\n\n            void reloadAssets();\n\n        private slots:\n\n            void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n        private:\n\n            void recreate();\n\n            static const int CellSize;\n\n            CSMWorld::Data& mData;\n            std::string mId;\n\n            osg::Group* mParentNode;\n\n            osg::ref_ptr<osg::PositionAttitudeTransform> mWaterTransform;\n            osg::ref_ptr<osg::Geode> mWaterNode;\n            osg::ref_ptr<osg::Geometry> mWaterGeometry;\n\n            bool mDeleted;\n            bool mExterior;\n            bool mHasWater;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/commands.cpp",
    "content": "#include \"commands.hpp\"\n\n#include <QPointer>\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/loadland.hpp>\n\n#include \"editmode.hpp\"\n#include \"terrainselection.hpp\"\n#include \"terrainshapemode.hpp\"\n#include \"terraintexturemode.hpp\"\n#include \"worldspacewidget.hpp\"\n\nCSVRender::DrawTerrainSelectionCommand::DrawTerrainSelectionCommand(WorldspaceWidget* worldspaceWidget, QUndoCommand* parent)\n    : mWorldspaceWidget(worldspaceWidget)\n{ }\n\nvoid CSVRender::DrawTerrainSelectionCommand::redo()\n{\n    tryUpdate();\n}\n\nvoid CSVRender::DrawTerrainSelectionCommand::undo()\n{\n    tryUpdate();\n}\n\nvoid CSVRender::DrawTerrainSelectionCommand::tryUpdate()\n{\n    if (!mWorldspaceWidget)\n    {\n        Log(Debug::Verbose) << \"Can't update terrain selection, no WorldspaceWidget found!\";\n        return;\n    }\n\n    auto terrainMode = dynamic_cast<CSVRender::TerrainShapeMode*>(mWorldspaceWidget->getEditMode());\n    if (!terrainMode)\n    {\n        Log(Debug::Verbose) << \"Can't update terrain selection in current EditMode\";\n        return;\n    }\n\n    terrainMode->getTerrainSelection()->update();\n}\n"
  },
  {
    "path": "apps/opencs/view/render/commands.hpp",
    "content": "#ifndef CSV_RENDER_COMMANDS_HPP\n#define CSV_RENDER_COMMANDS_HPP\n\n#include <QPointer>\n\n#include <QUndoCommand>\n\n#include \"worldspacewidget.hpp\"\n\nnamespace CSVRender\n{\n    class TerrainSelection;\n\n    /*\n        Current solution to force a redrawing of the terrain-selection grid\n        when undoing/redoing changes in the editor.\n        This only triggers a simple redraw of the grid, so only use it in\n        conjunction with actual data changes which deform the grid.\n\n        Please note that this command needs to be put onto the QUndoStack twice:\n        at the start and at the end of the related terrain manipulation.\n        This makes sure that the grid is always updated after all changes have\n        been undone or redone -- but it also means that the selection is redrawn\n        once at the beginning of either action. Future refinement may solve that.\n    */\n    class DrawTerrainSelectionCommand : public QUndoCommand\n    {\n\n    private:\n        QPointer<WorldspaceWidget> mWorldspaceWidget;\n\n    public:\n        DrawTerrainSelectionCommand(WorldspaceWidget* worldspaceWidget, QUndoCommand* parent = nullptr);\n\n        void redo() override;\n        void undo() override;\n\n        void tryUpdate();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/editmode.cpp",
    "content": "#include \"editmode.hpp\"\n\n#include \"tagbase.hpp\"\n#include \"worldspacewidget.hpp\"\n\nCSVRender::WorldspaceWidget& CSVRender::EditMode::getWorldspaceWidget()\n{\n    return *mWorldspaceWidget;\n}\n\nCSVRender::EditMode::EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon,\n    unsigned int mask, const QString& tooltip, QWidget *parent)\n: ModeButton (icon, tooltip, parent), mWorldspaceWidget (worldspaceWidget), mMask (mask)\n{}\n\nunsigned int CSVRender::EditMode::getInteractionMask() const\n{\n    return mMask;\n}\n\nvoid CSVRender::EditMode::activate (CSVWidget::SceneToolbar *toolbar)\n{\n    mWorldspaceWidget->setInteractionMask (mMask);\n    mWorldspaceWidget->clearSelection (~mMask);\n}\n\nvoid CSVRender::EditMode::setEditLock (bool locked)\n{\n\n}\n\nvoid CSVRender::EditMode::primaryOpenPressed (const WorldspaceHitResult& hit) {}\n\nvoid CSVRender::EditMode::primaryEditPressed (const WorldspaceHitResult& hit) {}\n\nvoid CSVRender::EditMode::secondaryEditPressed (const WorldspaceHitResult& hit) {}\n\nvoid CSVRender::EditMode::primarySelectPressed (const WorldspaceHitResult& hit) {}\n\nvoid CSVRender::EditMode::secondarySelectPressed (const WorldspaceHitResult& hit) {}\n\nbool CSVRender::EditMode::primaryEditStartDrag (const QPoint& pos)\n{\n    return false;\n}\n\nbool CSVRender::EditMode::secondaryEditStartDrag (const QPoint& pos)\n{\n    return false;\n}\n\nbool CSVRender::EditMode::primarySelectStartDrag (const QPoint& pos)\n{\n    return false;\n}\n\nbool CSVRender::EditMode::secondarySelectStartDrag (const QPoint& pos)\n{\n    return false;\n}\n\nvoid CSVRender::EditMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) {}\n\nvoid CSVRender::EditMode::dragCompleted(const QPoint& pos) {}\n\nvoid CSVRender::EditMode::dragAborted() {}\n\nvoid CSVRender::EditMode::dragWheel (int diff, double speedFactor) {}\n\nvoid CSVRender::EditMode::dragEnterEvent (QDragEnterEvent *event) {}\n\nvoid CSVRender::EditMode::dropEvent (QDropEvent *event) {}\n\nvoid CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {}\n\nvoid CSVRender::EditMode::mouseMoveEvent (QMouseEvent *event) {}\n\nint CSVRender::EditMode::getSubMode() const\n{\n    return -1;\n}\n"
  },
  {
    "path": "apps/opencs/view/render/editmode.hpp",
    "content": "#ifndef CSV_RENDER_EDITMODE_H\n#define CSV_RENDER_EDITMODE_H\n\n#include <osg/ref_ptr>\n\n#include \"../widget/modebutton.hpp\"\n\nclass QDragEnterEvent;\nclass QDropEvent;\nclass QDragMoveEvent;\nclass QPoint;\n\nnamespace CSVRender\n{\n    class WorldspaceWidget;\n    struct WorldspaceHitResult;\n    class TagBase;\n\n    class EditMode : public CSVWidget::ModeButton\n    {\n            Q_OBJECT\n\n            WorldspaceWidget *mWorldspaceWidget;\n            unsigned int mMask;\n\n        protected:\n\n            WorldspaceWidget& getWorldspaceWidget();\n\n        public:\n\n            EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon, unsigned int mask,\n                const QString& tooltip = \"\", QWidget *parent = nullptr);\n\n            unsigned int getInteractionMask() const;\n\n            void activate (CSVWidget::SceneToolbar *toolbar) override;\n\n            /// Default-implementation: Ignored.\n            virtual void setEditLock (bool locked);\n\n            /// Default-implementation: Ignored.\n            virtual void primaryOpenPressed (const WorldspaceHitResult& hit);\n\n            /// Default-implementation: Ignored.\n            virtual void primaryEditPressed (const WorldspaceHitResult& hit);\n\n            /// Default-implementation: Ignored.\n            virtual void secondaryEditPressed (const WorldspaceHitResult& hit);\n\n            /// Default-implementation: Ignored.\n            virtual void primarySelectPressed (const WorldspaceHitResult& hit);\n\n            /// Default-implementation: Ignored.\n            virtual void secondarySelectPressed (const WorldspaceHitResult& hit);\n\n            /// Default-implementation: ignore and return false\n            ///\n            /// \\return Drag accepted?\n            virtual bool primaryEditStartDrag (const QPoint& pos);\n\n            /// Default-implementation: ignore and return false\n            ///\n            /// \\return Drag accepted?\n            virtual bool secondaryEditStartDrag (const QPoint& pos);\n\n            /// Default-implementation: ignore and return false\n            ///\n            /// \\return Drag accepted?\n            virtual bool primarySelectStartDrag (const QPoint& pos);\n\n            /// Default-implementation: ignore and return false\n            ///\n            /// \\return Drag accepted?\n            virtual bool secondarySelectStartDrag (const QPoint& pos);\n\n            /// Default-implementation: ignored\n            virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor);\n\n            /// Default-implementation: ignored\n            virtual void dragCompleted(const QPoint& pos);\n\n            /// Default-implementation: ignored\n            ///\n            /// \\note dragAborted will not be called, if the drag is aborted via changing\n            /// editing mode\n            virtual void dragAborted();\n\n            /// Default-implementation: ignored\n            virtual void dragWheel (int diff, double speedFactor);\n\n            /// Default-implementation: ignored\n            void dragEnterEvent (QDragEnterEvent *event) override;\n\n            /// Default-implementation: ignored\n            void dropEvent (QDropEvent *event) override;\n\n            /// Default-implementation: ignored\n            void dragMoveEvent (QDragMoveEvent *event) override;\n\n            void mouseMoveEvent (QMouseEvent *event) override;\n\n            /// Default: return -1\n            virtual int getSubMode() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/instancedragmodes.hpp",
    "content": "#ifndef CSV_WIDGET_INSTANCEDRAGMODES_H\n#define CSV_WIDGET_INSTANCEDRAGMODES_H\n\nnamespace CSVRender\n{\n    enum DragMode\n    {\n        DragMode_None,\n        DragMode_Move,\n        DragMode_Rotate,\n        DragMode_Scale,\n        DragMode_Select_Only,\n        DragMode_Select_Add,\n        DragMode_Select_Remove,\n        DragMode_Select_Invert\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/instancemode.cpp",
    "content": "\n#include \"instancemode.hpp\"\n\n#include <QDragEnterEvent>\n#include <QPoint>\n#include <QString>\n\n#include \"../../model/prefs/state.hpp\"\n\n#include <osg/ComputeBoundsVisitor>\n#include <osg/Group>\n#include <osg/Vec3d>\n#include <osgUtil/LineSegmentIntersector>\n\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/idtree.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"../widget/scenetoolbar.hpp\"\n#include \"../widget/scenetoolmode.hpp\"\n\n#include \"mask.hpp\"\n\n#include \"object.hpp\"\n#include \"worldspacewidget.hpp\"\n#include \"pagedworldspacewidget.hpp\"\n#include \"instanceselectionmode.hpp\"\n#include \"instancemovemode.hpp\"\n\nint CSVRender::InstanceMode::getSubModeFromId (const std::string& id) const\n{\n    return id==\"move\" ? 0 : (id==\"rotate\" ? 1 : 2);\n}\n\nosg::Vec3f CSVRender::InstanceMode::quatToEuler(const osg::Quat& rot) const\n{\n    float x, y, z;\n    float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z());\n\n    if (std::abs(test) >= 1.f)\n    {\n        x = atan2(rot.x(), rot.w());\n        y = (test > 0) ? (osg::PI / 2) : (-osg::PI / 2);\n        z = 0;\n    }\n    else\n    {\n        x = std::atan2(2 * (rot.w() * rot.x() - rot.y() * rot.z()), 1 - 2 * (rot.x() * rot.x() + rot.y() * rot.y()));\n        y = std::asin(test);\n        z = std::atan2(2 * (rot.w() * rot.z() - rot.x() * rot.y()), 1 - 2 * (rot.y() * rot.y() + rot.z() * rot.z()));\n    }\n\n    return osg::Vec3f(-x, -y, -z);\n}\n\nosg::Quat CSVRender::InstanceMode::eulerToQuat(const osg::Vec3f& euler) const\n{\n    osg::Quat xr = osg::Quat(-euler[0], osg::Vec3f(1,0,0));\n    osg::Quat yr = osg::Quat(-euler[1], osg::Vec3f(0,1,0));\n    osg::Quat zr = osg::Quat(-euler[2], osg::Vec3f(0,0,1));\n\n    return zr * yr * xr;\n}\n\nosg::Vec3f CSVRender::InstanceMode::getSelectionCenter(const std::vector<osg::ref_ptr<TagBase> >& selection) const\n{\n    osg::Vec3f center = osg::Vec3f(0, 0, 0);\n    int objectCount = 0;\n\n    for (std::vector<osg::ref_ptr<TagBase> >::const_iterator iter (selection.begin()); iter!=selection.end(); ++iter)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))\n        {\n            const ESM::Position& position = objectTag->mObject->getPosition();\n            center += osg::Vec3f(position.pos[0], position.pos[1], position.pos[2]);\n\n            ++objectCount;\n        }\n    }\n\n    if (objectCount > 0)\n        center /= objectCount;\n\n    return center;\n}\n\nosg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos)\n{\n    osg::Matrix viewMatrix = getWorldspaceWidget().getCamera()->getViewMatrix();\n    osg::Matrix projMatrix = getWorldspaceWidget().getCamera()->getProjectionMatrix();\n    osg::Matrix windowMatrix = getWorldspaceWidget().getCamera()->getViewport()->computeWindowMatrix();\n    osg::Matrix combined = viewMatrix * projMatrix * windowMatrix;\n\n    return pos * combined;\n}\n\nosg::Vec3f CSVRender::InstanceMode::getProjectionSpaceCoords(const osg::Vec3f& pos)\n{\n    osg::Matrix viewMatrix = getWorldspaceWidget().getCamera()->getViewMatrix();\n    osg::Matrix projMatrix = getWorldspaceWidget().getCamera()->getProjectionMatrix();\n    osg::Matrix combined = viewMatrix * projMatrix;\n\n    return pos * combined;\n}\n\nosg::Vec3f CSVRender::InstanceMode::getMousePlaneCoords(const QPoint& point, const osg::Vec3d& dragStart)\n{\n    osg::Matrix viewMatrix;\n    viewMatrix.invert(getWorldspaceWidget().getCamera()->getViewMatrix());\n    osg::Matrix projMatrix;\n    projMatrix.invert(getWorldspaceWidget().getCamera()->getProjectionMatrix());\n    osg::Matrix combined = projMatrix * viewMatrix;\n\n    /* calculate viewport normalized coordinates\n       note: is there a reason to use getCamera()->getViewport()->computeWindowMatrix() instead? */\n    float x = (point.x() * 2) / getWorldspaceWidget().getCamera()->getViewport()->width() - 1.0f;\n    float y = 1.0f - (point.y() * 2) / getWorldspaceWidget().getCamera()->getViewport()->height();\n\n    osg::Vec3f mousePlanePoint = osg::Vec3f(x, y, dragStart.z()) * combined;\n\n    return mousePlanePoint;\n}\n\nCSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget,  osg::ref_ptr<osg::Group> parentNode,  QWidget *parent)\n: EditMode (worldspaceWidget, QIcon (\":scenetoolbar/editing-instance\"), Mask_Reference | Mask_Terrain, \"Instance editing\",\n  parent), mSubMode (nullptr), mSubModeId (\"move\"), mSelectionMode (nullptr), mDragMode (DragMode_None),\n  mDragAxis (-1), mLocked (false), mUnitScaleDist(1), mParentNode (parentNode)\n{\n    connect(this, SIGNAL(requestFocus(const std::string&)),\n            worldspaceWidget, SIGNAL(requestFocus(const std::string&)));\n\n    CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut(\"scene-delete\", worldspaceWidget);\n    connect(deleteShortcut, SIGNAL(activated(bool)), this, SLOT(deleteSelectedInstances(bool)));\n\n    // Following classes could be simplified by using QSignalMapper, which is obsolete in Qt5.10, but not in Qt4.8 and Qt5.14\n    CSMPrefs::Shortcut* dropToCollisionShortcut = new CSMPrefs::Shortcut(\"scene-instance-drop-collision\", worldspaceWidget);\n        connect(dropToCollisionShortcut, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToCollision()));\n    CSMPrefs::Shortcut* dropToTerrainLevelShortcut = new CSMPrefs::Shortcut(\"scene-instance-drop-terrain\", worldspaceWidget);\n        connect(dropToTerrainLevelShortcut, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToTerrain()));\n    CSMPrefs::Shortcut* dropToCollisionShortcut2 = new CSMPrefs::Shortcut(\"scene-instance-drop-collision-separately\", worldspaceWidget);\n        connect(dropToCollisionShortcut2, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToCollisionSeparately()));\n    CSMPrefs::Shortcut* dropToTerrainLevelShortcut2 = new CSMPrefs::Shortcut(\"scene-instance-drop-terrain-separately\", worldspaceWidget);\n        connect(dropToTerrainLevelShortcut2, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToTerrainSeparately()));\n}\n\nvoid CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar)\n{\n    if (!mSubMode)\n    {\n        mSubMode = new CSVWidget::SceneToolMode (toolbar, \"Edit Sub-Mode\");\n        mSubMode->addButton (new InstanceMoveMode (this), \"move\");\n        mSubMode->addButton (\":scenetoolbar/transform-rotate\", \"rotate\",\n            \"Rotate selected instances\"\n            \"<ul><li>Use {scene-edit-primary} to rotate instances freely</li>\"\n            \"<li>Use {scene-edit-secondary} to rotate instances within the grid</li>\"\n            \"<li>The center of the view acts as the axis of rotation</li>\"\n            \"</ul>\"\n            \"<font color=Red>Grid rotate not implemented yet</font color>\");\n        mSubMode->addButton (\":scenetoolbar/transform-scale\", \"scale\",\n            \"Scale selected instances\"\n            \"<ul><li>Use {scene-edit-primary} to scale instances freely</li>\"\n            \"<li>Use {scene-edit-secondary} to scale instances along the grid</li>\"\n            \"<li>The scaling rate is based on how close the start of a drag is to the center of the screen</li>\"\n            \"</ul>\"\n            \"<font color=Red>Grid scale not implemented yet</font color>\");\n\n        mSubMode->setButton (mSubModeId);\n\n        connect (mSubMode, SIGNAL (modeChanged (const std::string&)),\n            this, SLOT (subModeChanged (const std::string&)));\n    }\n\n    if (!mSelectionMode)\n        mSelectionMode = new InstanceSelectionMode (toolbar, getWorldspaceWidget(), mParentNode);\n\n    mDragMode = DragMode_None;\n\n    EditMode::activate (toolbar);\n\n    toolbar->addTool (mSubMode);\n    toolbar->addTool (mSelectionMode);\n\n    std::string subMode = mSubMode->getCurrentId();\n\n    getWorldspaceWidget().setSubMode (getSubModeFromId (subMode), Mask_Reference);\n}\n\nvoid CSVRender::InstanceMode::deactivate (CSVWidget::SceneToolbar *toolbar)\n{\n    mDragMode = DragMode_None;\n    getWorldspaceWidget().reset (Mask_Reference);\n\n    if (mSelectionMode)\n    {\n        toolbar->removeTool (mSelectionMode);\n        delete mSelectionMode;\n        mSelectionMode = nullptr;\n    }\n\n    if (mSubMode)\n    {\n        toolbar->removeTool (mSubMode);\n        delete mSubMode;\n        mSubMode = nullptr;\n    }\n\n    EditMode::deactivate (toolbar);\n}\n\nvoid CSVRender::InstanceMode::setEditLock (bool locked)\n{\n    mLocked = locked;\n\n    if (mLocked)\n        getWorldspaceWidget().abortDrag();\n}\n\nvoid CSVRender::InstanceMode::primaryEditPressed (const WorldspaceHitResult& hit)\n{\n    if (CSMPrefs::get()[\"3D Scene Input\"][\"context-select\"].isTrue())\n        primarySelectPressed (hit);\n}\n\nvoid CSVRender::InstanceMode::primaryOpenPressed (const WorldspaceHitResult& hit)\n{\n    if(hit.tag)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (hit.tag.get()))\n        {\n            const std::string refId = objectTag->mObject->getReferenceId();\n            emit requestFocus(refId);\n        }\n    }\n}\n\nvoid CSVRender::InstanceMode::secondaryEditPressed (const WorldspaceHitResult& hit)\n{\n    if (CSMPrefs::get()[\"3D Scene Input\"][\"context-select\"].isTrue())\n        secondarySelectPressed (hit);\n}\n\nvoid CSVRender::InstanceMode::primarySelectPressed (const WorldspaceHitResult& hit)\n{\n    getWorldspaceWidget().clearSelection (Mask_Reference);\n\n    if (hit.tag)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (hit.tag.get()))\n        {\n            // hit an Object, select it\n            CSVRender::Object* object = objectTag->mObject;\n            object->setSelected (true);\n            return;\n        }\n    }\n}\n\nvoid CSVRender::InstanceMode::secondarySelectPressed (const WorldspaceHitResult& hit)\n{\n    if (hit.tag)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (hit.tag.get()))\n        {\n            // hit an Object, toggle its selection state\n            CSVRender::Object* object = objectTag->mObject;\n            object->setSelected (!object->getSelected());\n            return;\n        }\n    }\n}\n\nbool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)\n{\n    if (mDragMode!=DragMode_None || mLocked)\n        return false;\n\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n\n    std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Reference);\n    if (selection.empty())\n    {\n        // Only change selection at the start of drag if no object is already selected\n        if (hit.tag && CSMPrefs::get()[\"3D Scene Input\"][\"context-select\"].isTrue())\n        {\n            getWorldspaceWidget().clearSelection (Mask_Reference);\n            if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (hit.tag.get()))\n            {\n                CSVRender::Object* object = objectTag->mObject;\n                object->setSelected (true);\n            }\n        }\n\n        selection = getWorldspaceWidget().getSelection (Mask_Reference);\n        if (selection.empty())\n            return false;\n    }\n\n    for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());\n        iter!=selection.end(); ++iter)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))\n        {\n            if (mSubModeId == \"move\")\n            {\n                objectTag->mObject->setEdited (Object::Override_Position);\n                mDragMode = DragMode_Move;\n            }\n            else if (mSubModeId == \"rotate\")\n            {\n                objectTag->mObject->setEdited (Object::Override_Rotation);\n                mDragMode = DragMode_Rotate;\n            }\n            else if (mSubModeId == \"scale\")\n            {\n                objectTag->mObject->setEdited (Object::Override_Scale);\n                mDragMode = DragMode_Scale;\n\n                // Calculate scale factor\n                std::vector<osg::ref_ptr<TagBase> > editedSelection = getWorldspaceWidget().getEdited (Mask_Reference);\n                osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection));\n\n                int widgetHeight = getWorldspaceWidget().height();\n\n                float dx = pos.x() - center.x();\n                float dy = (widgetHeight - pos.y()) - center.y();\n\n                mUnitScaleDist = std::sqrt(dx * dx + dy * dy);\n            }\n        }\n    }\n\n    if (CSVRender::ObjectMarkerTag *objectTag = dynamic_cast<CSVRender::ObjectMarkerTag *> (hit.tag.get()))\n    {\n        mDragAxis = objectTag->mAxis;\n    }\n    else\n        mDragAxis = -1;\n\n    return true;\n}\n\nbool CSVRender::InstanceMode::secondaryEditStartDrag (const QPoint& pos)\n{\n    if (mLocked)\n        return false;\n\n    return false;\n}\n\nbool CSVRender::InstanceMode::primarySelectStartDrag (const QPoint& pos)\n{\n    if (mDragMode!=DragMode_None || mLocked)\n        return false;\n\n    std::string primarySelectAction = CSMPrefs::get()[\"3D Scene Editing\"][\"primary-select-action\"].toString();\n\n    if ( primarySelectAction == \"Select only\" ) mDragMode = DragMode_Select_Only;\n    else if ( primarySelectAction == \"Add to selection\" ) mDragMode = DragMode_Select_Add;\n    else if ( primarySelectAction == \"Remove from selection\" ) mDragMode = DragMode_Select_Remove;\n    else if ( primarySelectAction == \"Invert selection\" ) mDragMode = DragMode_Select_Invert;\n\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n    mSelectionMode->setDragStart(hit.worldPos);\n\n    return true;\n}\n\nbool CSVRender::InstanceMode::secondarySelectStartDrag (const QPoint& pos)\n{\n    if (mDragMode!=DragMode_None || mLocked)\n        return false;\n\n    std::string secondarySelectAction = CSMPrefs::get()[\"3D Scene Editing\"][\"secondary-select-action\"].toString();\n\n    if ( secondarySelectAction == \"Select only\" ) mDragMode = DragMode_Select_Only;\n    else if ( secondarySelectAction == \"Add to selection\" ) mDragMode = DragMode_Select_Add;\n    else if ( secondarySelectAction == \"Remove from selection\" ) mDragMode = DragMode_Select_Remove;\n    else if ( secondarySelectAction == \"Invert selection\" ) mDragMode = DragMode_Select_Invert;\n\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n    mSelectionMode->setDragStart(hit.worldPos);\n\n    return true;\n}\n\nvoid CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)\n{\n    osg::Vec3f offset;\n    osg::Quat rotation;\n\n    std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getEdited (Mask_Reference);\n\n    if (mDragMode == DragMode_Move)\n    {\n        osg::Vec3f eye, centre, up;\n        getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up);\n\n        if (diffY)\n        {\n            offset += up * diffY * speedFactor;\n        }\n        if (diffX)\n        {\n            offset += ((centre-eye) ^ up) * diffX * speedFactor;\n        }\n\n        if (mDragAxis!=-1)\n        {\n            for (int i=0; i<3; ++i)\n            {\n                if (i!=mDragAxis)\n                    offset[i] = 0;\n            }\n        }\n    }\n    else if (mDragMode == DragMode_Rotate)\n    {\n        osg::Vec3f eye, centre, up;\n        getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up);\n\n        float angle;\n        osg::Vec3f axis;\n\n        if (mDragAxis == -1)\n        {\n            // Free rotate\n            float rotationFactor = CSMPrefs::get()[\"3D Scene Input\"][\"rotate-factor\"].toDouble() * speedFactor;\n\n            osg::Quat cameraRotation = getWorldspaceWidget().getCamera()->getInverseViewMatrix().getRotate();\n\n            osg::Vec3f camForward = centre - eye;\n            osg::Vec3f screenDir = cameraRotation * osg::Vec3f(diffX, diffY, 0);\n            screenDir.normalize();\n\n            angle = std::sqrt(diffX*diffX + diffY*diffY) * rotationFactor;\n            axis = screenDir ^ camForward;\n        }\n        else\n        {\n            // Global axis rotation\n            osg::Vec3f camBack = eye - centre;\n\n            for (int i = 0; i < 3; ++i)\n            {\n                if (i == mDragAxis)\n                    axis[i] = 1;\n                else\n                    axis[i] = 0;\n            }\n\n            // Flip axis if facing opposite side\n            if (camBack * axis < 0)\n                axis *= -1;\n\n            // Convert coordinate system\n            osg::Vec3f screenCenter = getScreenCoords(getSelectionCenter(selection));\n\n            int widgetHeight = getWorldspaceWidget().height();\n\n            float newX = pos.x() - screenCenter.x();\n            float newY = (widgetHeight - pos.y()) - screenCenter.y();\n\n            float oldX = newX - diffX;\n            float oldY = newY - diffY; // diffY appears to already be flipped\n\n            osg::Vec3f oldVec = osg::Vec3f(oldX, oldY, 0);\n            oldVec.normalize();\n\n            osg::Vec3f newVec = osg::Vec3f(newX, newY, 0);\n            newVec.normalize();\n\n            // Find angle and axis of rotation\n            angle = std::acos(oldVec * newVec) * speedFactor;\n            if (((oldVec ^ newVec) * camBack < 0) ^ (camBack.z() < 0))\n                angle *= -1;\n        }\n\n        rotation = osg::Quat(angle, axis);\n    }\n    else if (mDragMode == DragMode_Scale)\n    {\n        osg::Vec3f center = getScreenCoords(getSelectionCenter(selection));\n\n        // Calculate scaling distance/rate\n        int widgetHeight = getWorldspaceWidget().height();\n\n        float dx = pos.x() - center.x();\n        float dy = (widgetHeight - pos.y()) - center.y();\n\n        float dist = std::sqrt(dx * dx + dy * dy);\n        float scale = dist / mUnitScaleDist;\n\n        // Only uniform scaling is currently supported\n        offset = osg::Vec3f(scale, scale, scale);\n    }\n    else if (mSelectionMode->getCurrentId() == \"cube-centre\")\n    {\n        osg::Vec3f mousePlanePoint = getMousePlaneCoords(pos, getProjectionSpaceCoords(mSelectionMode->getDragStart()));\n        mSelectionMode->drawSelectionCubeCentre (mousePlanePoint);\n        return;\n    }\n    else if (mSelectionMode->getCurrentId() == \"cube-corner\")\n    {\n        osg::Vec3f mousePlanePoint = getMousePlaneCoords(pos, getProjectionSpaceCoords(mSelectionMode->getDragStart()));\n        mSelectionMode->drawSelectionCubeCorner (mousePlanePoint);\n        return;\n    }\n    else if (mSelectionMode->getCurrentId() == \"sphere\")\n    {\n        osg::Vec3f mousePlanePoint = getMousePlaneCoords(pos, getProjectionSpaceCoords(mSelectionMode->getDragStart()));\n        mSelectionMode->drawSelectionSphere (mousePlanePoint);\n        return;\n    }\n\n    // Apply\n    for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin()); iter!=selection.end(); ++iter)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))\n        {\n            if (mDragMode == DragMode_Move)\n            {\n                ESM::Position position = objectTag->mObject->getPosition();\n                for (int i=0; i<3; ++i)\n                {\n                    position.pos[i] += offset[i];\n                }\n\n                objectTag->mObject->setPosition(position.pos);\n            }\n            else if (mDragMode == DragMode_Rotate)\n            {\n                ESM::Position position = objectTag->mObject->getPosition();\n\n                osg::Quat currentRot = eulerToQuat(osg::Vec3f(position.rot[0], position.rot[1], position.rot[2]));\n                osg::Quat combined = currentRot * rotation;\n\n                osg::Vec3f euler = quatToEuler(combined);\n                // There appears to be a very rare rounding error that can cause asin to return NaN\n                if (!euler.isNaN())\n                {\n                    position.rot[0] = euler.x();\n                    position.rot[1] = euler.y();\n                    position.rot[2] = euler.z();\n                }\n\n                objectTag->mObject->setRotation(position.rot);\n            }\n            else if (mDragMode == DragMode_Scale)\n            {\n                // Reset scale\n                objectTag->mObject->setEdited(0);\n                objectTag->mObject->setEdited(Object::Override_Scale);\n\n                float scale = objectTag->mObject->getScale();\n                scale *= offset.x();\n\n                objectTag->mObject->setScale (scale);\n            }\n        }\n    }\n}\n\nvoid CSVRender::InstanceMode::dragCompleted(const QPoint& pos)\n{\n    std::vector<osg::ref_ptr<TagBase> > selection =\n        getWorldspaceWidget().getEdited (Mask_Reference);\n\n    QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();\n\n    QString description;\n\n    switch (mDragMode)\n    {\n        case DragMode_Move: description = \"Move Instances\"; break;\n        case DragMode_Rotate: description = \"Rotate Instances\"; break;\n        case DragMode_Scale: description = \"Scale Instances\"; break;\n        case DragMode_Select_Only :\n            handleSelectDrag(pos);\n            return;\n            break;\n        case DragMode_Select_Add :\n            handleSelectDrag(pos);\n            return;\n            break;\n        case DragMode_Select_Remove :\n            handleSelectDrag(pos);\n            return;\n            break;\n        case DragMode_Select_Invert :\n            handleSelectDrag(pos);\n            return;\n            break;\n\n        case DragMode_None: break;\n    }\n\n\n    CSMWorld::CommandMacro macro (undoStack, description);\n\n    for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());\n        iter!=selection.end(); ++iter)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))\n        {\n            objectTag->mObject->apply (macro);\n        }\n    }\n\n    mDragMode = DragMode_None;\n}\n\nvoid CSVRender::InstanceMode::dragAborted()\n{\n    getWorldspaceWidget().reset (Mask_Reference);\n    mDragMode = DragMode_None;\n}\n\nvoid CSVRender::InstanceMode::dragWheel (int diff, double speedFactor)\n{\n    if (mDragMode==DragMode_Move)\n    {\n        osg::Vec3f eye;\n        osg::Vec3f centre;\n        osg::Vec3f up;\n\n        getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up);\n\n        osg::Vec3f offset = centre - eye;\n        offset.normalize();\n        offset *= diff * speedFactor;\n\n        std::vector<osg::ref_ptr<TagBase> > selection =\n            getWorldspaceWidget().getEdited (Mask_Reference);\n\n        for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());\n            iter!=selection.end(); ++iter)\n        {\n            if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))\n            {\n                ESM::Position position = objectTag->mObject->getPosition();\n                for (int i=0; i<3; ++i)\n                    position.pos[i] += offset[i];\n                objectTag->mObject->setPosition (position.pos);\n            }\n        }\n    }\n}\n\nvoid CSVRender::InstanceMode::dragEnterEvent (QDragEnterEvent *event)\n{\n    if (const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()))\n    {\n        if (!mime->fromDocument (getWorldspaceWidget().getDocument()))\n            return;\n\n        if (mime->holdsType (CSMWorld::UniversalId::Type_Referenceable))\n            event->accept();\n    }\n}\n\nvoid CSVRender::InstanceMode::dropEvent (QDropEvent* event)\n{\n    if (const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()))\n    {\n        CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n\n        if (!mime->fromDocument (document))\n            return;\n\n        WorldspaceHitResult hit = getWorldspaceWidget().mousePick (event->pos(), getWorldspaceWidget().getInteractionMask());\n\n        std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos);\n\n        CSMWorld::IdTree& cellTable = dynamic_cast<CSMWorld::IdTree&> (\n            *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));\n\n        bool noCell = document.getData().getCells().searchId (cellId)==-1;\n\n        if (noCell)\n        {\n            std::string mode = CSMPrefs::get()[\"3D Scene Editing\"][\"outside-drop\"].toString();\n\n            // target cell does not exist\n            if (mode==\"Discard\")\n                return;\n\n            if (mode==\"Create cell and insert\")\n            {\n                std::unique_ptr<CSMWorld::CreateCommand> createCommand (\n                    new CSMWorld::CreateCommand (cellTable, cellId));\n\n                int parentIndex = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);\n                int index = cellTable.findNestedColumnIndex (parentIndex, CSMWorld::Columns::ColumnId_Interior);\n                createCommand->addNestedValue (parentIndex, index, false);\n\n                document.getUndoStack().push (createCommand.release());\n\n                if (CSVRender::PagedWorldspaceWidget *paged =\n                    dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n                {\n                    CSMWorld::CellSelection selection = paged->getCellSelection();\n                    selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);\n                    paged->setCellSelection (selection);\n                }\n            }\n        }\n        else if (CSVRender::PagedWorldspaceWidget *paged =\n            dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n        {\n            CSMWorld::CellSelection selection = paged->getCellSelection();\n            if (!selection.has (CSMWorld::CellCoordinates::fromId (cellId).first))\n            {\n                // target cell exists, but is not shown\n                std::string mode =\n                    CSMPrefs::get()[\"3D Scene Editing\"][\"outside-visible-drop\"].toString();\n\n                if (mode==\"Discard\")\n                    return;\n\n                if (mode==\"Show cell and insert\")\n                {\n                    selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);\n                    paged->setCellSelection (selection);\n                }\n            }\n        }\n\n        CSMWorld::IdTable& referencesTable = dynamic_cast<CSMWorld::IdTable&> (\n            *document.getData().getTableModel (CSMWorld::UniversalId::Type_References));\n\n        bool dropped = false;\n\n        std::vector<CSMWorld::UniversalId> ids = mime->getData();\n\n        for (std::vector<CSMWorld::UniversalId>::const_iterator iter (ids.begin());\n            iter!=ids.end(); ++iter)\n            if (mime->isReferencable (iter->getType()))\n            {\n                // create reference\n                std::unique_ptr<CSMWorld::CreateCommand> createCommand (\n                    new CSMWorld::CreateCommand (\n                    referencesTable, document.getData().getReferences().getNewId()));\n\n                 createCommand->addValue (referencesTable.findColumnIndex (\n                     CSMWorld::Columns::ColumnId_Cell), QString::fromUtf8 (cellId.c_str()));\n                 createCommand->addValue (referencesTable.findColumnIndex (\n                     CSMWorld::Columns::ColumnId_PositionXPos), hit.worldPos.x());\n                 createCommand->addValue (referencesTable.findColumnIndex (\n                     CSMWorld::Columns::ColumnId_PositionYPos), hit.worldPos.y());\n                 createCommand->addValue (referencesTable.findColumnIndex (\n                     CSMWorld::Columns::ColumnId_PositionZPos), hit.worldPos.z());\n                 createCommand->addValue (referencesTable.findColumnIndex (\n                     CSMWorld::Columns::ColumnId_ReferenceableId),\n                     QString::fromUtf8 (iter->getId().c_str()));\n\n                document.getUndoStack().push (createCommand.release());\n\n                dropped = true;\n            }\n\n        if (dropped)\n            event->accept();\n    }\n}\n\nint CSVRender::InstanceMode::getSubMode() const\n{\n    return mSubMode ? getSubModeFromId (mSubMode->getCurrentId()) : 0;\n}\n\nvoid CSVRender::InstanceMode::subModeChanged (const std::string& id)\n{\n    mSubModeId = id;\n    getWorldspaceWidget().abortDrag();\n    getWorldspaceWidget().setSubMode (getSubModeFromId (id), Mask_Reference);\n}\n\nvoid CSVRender::InstanceMode::handleSelectDrag(const QPoint& pos)\n{\n    osg::Vec3f mousePlanePoint = getMousePlaneCoords(pos, getProjectionSpaceCoords(mSelectionMode->getDragStart()));\n    mSelectionMode->dragEnded (mousePlanePoint, mDragMode);\n    mDragMode = DragMode_None;\n}\n\nvoid CSVRender::InstanceMode::deleteSelectedInstances(bool active)\n{\n    std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Reference);\n    if (selection.empty()) return;\n\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& referencesTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_References));\n    QUndoStack& undoStack = document.getUndoStack();\n\n    CSMWorld::CommandMacro macro (undoStack, \"Delete Instances\");\n    for(osg::ref_ptr<TagBase> tag: selection)\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))\n            macro.push(new CSMWorld::DeleteCommand(referencesTable, objectTag->mObject->getReferenceId()));\n\n    getWorldspaceWidget().clearSelection (Mask_Reference);\n}\n\nvoid CSVRender::InstanceMode::dropInstance(CSVRender::Object* object, float dropHeight)\n{\n    object->setEdited(Object::Override_Position);\n    ESM::Position position = object->getPosition();\n    position.pos[2] -= dropHeight;\n    object->setPosition(position.pos);\n}\n\nfloat CSVRender::InstanceMode::calculateDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight)\n{\n    osg::Vec3d point = object->getPosition().asVec3();\n\n    osg::Vec3d start = point;\n    start.z() += objectHeight;\n    osg::Vec3d end = point;\n    end.z() = std::numeric_limits<float>::lowest();\n\n    osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(\n        osgUtil::Intersector::MODEL, start, end) );\n    intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);\n    osgUtil::IntersectionVisitor visitor(intersector);\n\n    if (dropMode & Terrain)\n        visitor.setTraversalMask(Mask_Terrain);\n    if (dropMode & Collision)\n        visitor.setTraversalMask(Mask_Terrain | Mask_Reference);\n\n    mParentNode->accept(visitor);\n\n    osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();\n    if (it != intersector->getIntersections().end())\n    {\n        osgUtil::LineSegmentIntersector::Intersection intersection = *it;\n        float collisionLevel = intersection.getWorldIntersectPoint().z();\n        return point.z() - collisionLevel + objectHeight;\n    }\n\n    return 0.0f;\n}\n\nvoid CSVRender::InstanceMode::dropSelectedInstancesToCollision()\n{\n    handleDropMethod(Collision, \"Drop instances to next collision\");\n}\n\nvoid CSVRender::InstanceMode::dropSelectedInstancesToTerrain()\n{\n    handleDropMethod(Terrain, \"Drop instances to terrain level\");\n}\n\nvoid CSVRender::InstanceMode::dropSelectedInstancesToCollisionSeparately()\n{\n    handleDropMethod(CollisionSep, \"Drop instances to next collision level separately\");\n}\n\nvoid CSVRender::InstanceMode::dropSelectedInstancesToTerrainSeparately()\n{\n    handleDropMethod(TerrainSep, \"Drop instances to terrain level separately\");\n}\n\nvoid CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString commandMsg)\n{\n    std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Reference);\n    if (selection.empty())\n        return;\n\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    QUndoStack& undoStack = document.getUndoStack();\n\n    CSMWorld::CommandMacro macro (undoStack, commandMsg);\n\n    DropObjectHeightHandler dropObjectDataHandler(&getWorldspaceWidget());\n\n    if(dropMode & Separate)\n    {\n        int counter = 0;\n        for (osg::ref_ptr<TagBase> tag : selection)\n            if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(tag.get()))\n            {\n                float objectHeight = dropObjectDataHandler.mObjectHeights[counter];\n                float dropHeight = calculateDropHeight(dropMode, objectTag->mObject, objectHeight);\n                dropInstance(objectTag->mObject, dropHeight);\n                objectTag->mObject->apply(macro);\n                counter++;\n            }\n    }\n    else\n    {\n        float smallestDropHeight = std::numeric_limits<float>::max();\n        int counter = 0;\n        for (osg::ref_ptr<TagBase> tag : selection)\n            if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(tag.get()))\n            {\n                float objectHeight = dropObjectDataHandler.mObjectHeights[counter];\n                float thisDrop = calculateDropHeight(dropMode, objectTag->mObject, objectHeight);\n                if (thisDrop < smallestDropHeight)\n                    smallestDropHeight = thisDrop;\n                counter++;\n            }\n        for (osg::ref_ptr<TagBase> tag : selection)\n            if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(tag.get()))\n            {\n                dropInstance(objectTag->mObject, smallestDropHeight);\n                objectTag->mObject->apply(macro);\n            }\n    }\n}\n\nCSVRender::DropObjectHeightHandler::DropObjectHeightHandler(WorldspaceWidget* worldspacewidget)\n    : mWorldspaceWidget(worldspacewidget)\n{\n    std::vector<osg::ref_ptr<TagBase> > selection = mWorldspaceWidget->getSelection (Mask_Reference);\n    for(osg::ref_ptr<TagBase> tag: selection)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))\n        {\n            osg::ref_ptr<osg::Group> objectNodeWithGUI = objectTag->mObject->getRootNode();\n            osg::ref_ptr<osg::Group> objectNodeWithoutGUI = objectTag->mObject->getBaseNode();\n\n            osg::ComputeBoundsVisitor computeBounds;\n            computeBounds.setTraversalMask(Mask_Reference);\n            objectNodeWithoutGUI->accept(computeBounds);\n            osg::BoundingBox bounds = computeBounds.getBoundingBox();\n            float boundingBoxOffset = 0.0f;\n            if (bounds.valid())\n                boundingBoxOffset = bounds.zMin();\n\n            mObjectHeights.emplace_back(boundingBoxOffset);\n            mOldMasks.emplace_back(objectNodeWithGUI->getNodeMask());\n\n            objectNodeWithGUI->setNodeMask(0);\n        }\n    }\n}\n\nCSVRender::DropObjectHeightHandler::~DropObjectHeightHandler()\n{\n    std::vector<osg::ref_ptr<TagBase> > selection = mWorldspaceWidget->getSelection (Mask_Reference);\n    int counter = 0;\n    for(osg::ref_ptr<TagBase> tag: selection)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))\n        {\n            osg::ref_ptr<osg::Group> objectNodeWithGUI = objectTag->mObject->getRootNode();\n            objectNodeWithGUI->setNodeMask(mOldMasks[counter]);\n            counter++;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/instancemode.hpp",
    "content": "#ifndef CSV_RENDER_INSTANCEMODE_H\n#define CSV_RENDER_INSTANCEMODE_H\n\n#include <QString>\n\n#include <osg/ref_ptr>\n#include <osg/Group>\n#include <osg/Quat>\n#include <osg/Vec3f>\n\n#include \"editmode.hpp\"\n#include \"instancedragmodes.hpp\"\n\nnamespace CSVWidget\n{\n    class SceneToolMode;\n}\n\nnamespace CSVRender\n{\n    class TagBase;\n    class InstanceSelectionMode;\n    class Object;\n\n    class InstanceMode : public EditMode\n    {\n            Q_OBJECT\n\n            enum DropMode\n            {\n                Separate = 0b1,\n\n                Collision = 0b10,\n                Terrain = 0b100,\n\n                CollisionSep = Collision | Separate,\n                TerrainSep = Terrain | Separate,\n            };\n\n            CSVWidget::SceneToolMode *mSubMode;\n            std::string mSubModeId;\n            InstanceSelectionMode *mSelectionMode;\n            DragMode mDragMode;\n            int mDragAxis;\n            bool mLocked;\n            float mUnitScaleDist;\n            osg::ref_ptr<osg::Group> mParentNode;\n\n            int getSubModeFromId (const std::string& id) const;\n\n            osg::Vec3f quatToEuler(const osg::Quat& quat) const;\n            osg::Quat eulerToQuat(const osg::Vec3f& euler) const;\n\n            osg::Vec3f getSelectionCenter(const std::vector<osg::ref_ptr<TagBase> >& selection) const;\n            osg::Vec3f getScreenCoords(const osg::Vec3f& pos);\n            osg::Vec3f getProjectionSpaceCoords(const osg::Vec3f& pos);\n            osg::Vec3f getMousePlaneCoords(const QPoint& point, const osg::Vec3d& dragStart);\n            void handleSelectDrag(const QPoint& pos);\n            void dropInstance(CSVRender::Object* object, float dropHeight);\n            float calculateDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight);\n\n        public:\n\n            InstanceMode (WorldspaceWidget *worldspaceWidget, osg::ref_ptr<osg::Group> parentNode, QWidget *parent = nullptr);\n\n            void activate (CSVWidget::SceneToolbar *toolbar) override;\n\n            void deactivate (CSVWidget::SceneToolbar *toolbar) override;\n\n            void setEditLock (bool locked) override;\n\n            void primaryOpenPressed (const WorldspaceHitResult& hit) override;\n\n            void primaryEditPressed (const WorldspaceHitResult& hit) override;\n\n            void secondaryEditPressed (const WorldspaceHitResult& hit) override;\n\n            void primarySelectPressed (const WorldspaceHitResult& hit) override;\n\n            void secondarySelectPressed (const WorldspaceHitResult& hit) override;\n\n            bool primaryEditStartDrag (const QPoint& pos) override;\n\n            bool secondaryEditStartDrag (const QPoint& pos) override;\n\n            bool primarySelectStartDrag(const QPoint& pos) override;\n\n            bool secondarySelectStartDrag(const QPoint& pos) override;\n\n            void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) override;\n\n            void dragCompleted(const QPoint& pos) override;\n\n            /// \\note dragAborted will not be called, if the drag is aborted via changing\n            /// editing mode\n            void dragAborted() override;\n\n            void dragWheel (int diff, double speedFactor) override;\n\n            void dragEnterEvent (QDragEnterEvent *event) override;\n\n            void dropEvent (QDropEvent *event) override;\n\n            int getSubMode() const override;\n\n        signals:\n\n            void requestFocus (const std::string& id);\n\n        private slots:\n\n            void subModeChanged (const std::string& id);\n            void deleteSelectedInstances(bool active);\n            void dropSelectedInstancesToCollision();\n            void dropSelectedInstancesToTerrain();\n            void dropSelectedInstancesToCollisionSeparately();\n            void dropSelectedInstancesToTerrainSeparately();\n            void handleDropMethod(DropMode dropMode, QString commandMsg);\n    };\n\n    /// \\brief Helper class to handle object mask data in safe way\n    class DropObjectHeightHandler\n    {\n        public:\n            DropObjectHeightHandler(WorldspaceWidget* worldspacewidget);\n            ~DropObjectHeightHandler();\n            std::vector<float> mObjectHeights;\n\n        private:\n            WorldspaceWidget* mWorldspaceWidget;\n            std::vector<osg::Node::NodeMask> mOldMasks;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/instancemovemode.cpp",
    "content": "\n#include \"instancemovemode.hpp\"\n\nCSVRender::InstanceMoveMode::InstanceMoveMode (QWidget *parent)\n: ModeButton (QIcon (QPixmap (\":scenetoolbar/transform-move\")),\n  \"Move selected instances\"\n  \"<ul><li>Use {scene-edit-primary} to move instances around freely</li>\"\n  \"<li>Use {scene-edit-secondary} to move instances around within the grid</li>\"\n  \"</ul>\"\n  \"<font color=Red>Grid move not implemented yet</font color>\",\n  parent)\n{}\n"
  },
  {
    "path": "apps/opencs/view/render/instancemovemode.hpp",
    "content": "#ifndef CSV_RENDER_INSTANCEMOVEMODE_H\n#define CSV_RENDER_INSTANCEMOVEMODE_H\n\n#include \"../widget/modebutton.hpp\"\n\nnamespace CSVRender\n{\n    class InstanceMoveMode : public CSVWidget::ModeButton\n    {\n            Q_OBJECT\n\n        public:\n\n            InstanceMoveMode (QWidget *parent = nullptr);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/instanceselectionmode.cpp",
    "content": "#include \"instanceselectionmode.hpp\"\n\n#include <QMenu>\n#include <QAction>\n#include <QPoint>\n\n#include <osg/Group>\n#include <osg/PositionAttitudeTransform>\n#include <osg/ref_ptr>\n#include <osg/Vec3d>\n\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/commands.hpp\"\n\n#include \"instancedragmodes.hpp\"\n#include \"worldspacewidget.hpp\"\n#include \"object.hpp\"\n\nnamespace CSVRender\n{\n    InstanceSelectionMode::InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget, osg::Group *cellNode)\n        : SelectionMode(parent, worldspaceWidget, Mask_Reference), mParentNode(cellNode)\n    {\n        mSelectSame = new QAction(\"Extend selection to instances with same object ID\", this);\n        mDeleteSelection = new QAction(\"Delete selected instances\", this);\n\n        connect(mSelectSame, SIGNAL(triggered()), this, SLOT(selectSame()));\n        connect(mDeleteSelection, SIGNAL(triggered()), this, SLOT(deleteSelection()));\n    }\n\n    InstanceSelectionMode::~InstanceSelectionMode()\n    {\n        mParentNode->removeChild(mBaseNode);\n    }\n\n    void InstanceSelectionMode::setDragStart(const osg::Vec3d& dragStart)\n    {\n        mDragStart = dragStart;\n    }\n\n    const osg::Vec3d& InstanceSelectionMode::getDragStart()\n    {\n        return mDragStart;\n    }\n\n    void InstanceSelectionMode::dragEnded(const osg::Vec3d& dragEndPoint, DragMode dragMode)\n    {\n        float dragDistance = (mDragStart - dragEndPoint).length();\n        if (mBaseNode) mParentNode->removeChild (mBaseNode);\n        if (getCurrentId() == \"cube-centre\")\n        {\n            osg::Vec3d pointA(mDragStart[0] - dragDistance, mDragStart[1] - dragDistance, mDragStart[2] - dragDistance);\n            osg::Vec3d pointB(mDragStart[0] + dragDistance, mDragStart[1] + dragDistance, mDragStart[2] + dragDistance);\n            getWorldspaceWidget().selectInsideCube(pointA, pointB, dragMode);\n        }\n        else if (getCurrentId() == \"cube-corner\")\n        {\n            getWorldspaceWidget().selectInsideCube(mDragStart, dragEndPoint, dragMode);\n        }\n        else if (getCurrentId() == \"sphere\")\n        {\n            getWorldspaceWidget().selectWithinDistance(mDragStart, dragDistance, dragMode);\n        }\n    }\n\n    void InstanceSelectionMode::drawSelectionCubeCentre(const osg::Vec3f& mousePlanePoint)\n    {\n        float dragDistance = (mDragStart - mousePlanePoint).length();\n        drawSelectionCube(mDragStart, dragDistance);\n    }\n\n    void InstanceSelectionMode::drawSelectionCubeCorner(const osg::Vec3f& mousePlanePoint)\n    {\n        drawSelectionBox(mDragStart, mousePlanePoint);\n    }\n\n    void InstanceSelectionMode::drawSelectionBox(const osg::Vec3d& pointA, const osg::Vec3d& pointB)\n    {\n        if (mBaseNode) mParentNode->removeChild (mBaseNode);\n        mBaseNode = new osg::PositionAttitudeTransform;\n        mBaseNode->setPosition(pointA);\n\n        osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);\n\n        osg::Vec3Array *vertices = new osg::Vec3Array;\n        vertices->push_back (osg::Vec3f (0.0f, 0.0f, 0.0f));\n        vertices->push_back (osg::Vec3f (0.0f, 0.0f, pointB[2] - pointA[2]));\n        vertices->push_back (osg::Vec3f (0.0f, pointB[1] - pointA[1], 0.0f));\n        vertices->push_back (osg::Vec3f (0.0f, pointB[1] - pointA[1], pointB[2] - pointA[2]));\n\n        vertices->push_back (osg::Vec3f (pointB[0] - pointA[0], 0.0f, 0.0f));\n        vertices->push_back (osg::Vec3f (pointB[0] - pointA[0], 0.0f, pointB[2] - pointA[2]));\n        vertices->push_back (osg::Vec3f (pointB[0] - pointA[0], pointB[1] - pointA[1], 0.0f));\n        vertices->push_back (osg::Vec3f (pointB[0] - pointA[0], pointB[1] - pointA[1], pointB[2] - pointA[2]));\n\n        geometry->setVertexArray (vertices);\n\n        osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0);\n\n        // top\n        primitives->push_back (2);\n        primitives->push_back (1);\n        primitives->push_back (0);\n\n        primitives->push_back (3);\n        primitives->push_back (1);\n        primitives->push_back (2);\n\n        // bottom\n        primitives->push_back (4);\n        primitives->push_back (5);\n        primitives->push_back (6);\n\n        primitives->push_back (6);\n        primitives->push_back (5);\n        primitives->push_back (7);\n\n        // sides\n        primitives->push_back (1);\n        primitives->push_back (4);\n        primitives->push_back (0);\n\n        primitives->push_back (4);\n        primitives->push_back (1);\n        primitives->push_back (5);\n\n        primitives->push_back (4);\n        primitives->push_back (2);\n        primitives->push_back (0);\n\n        primitives->push_back (6);\n        primitives->push_back (2);\n        primitives->push_back (4);\n\n        primitives->push_back (6);\n        primitives->push_back (3);\n        primitives->push_back (2);\n\n        primitives->push_back (7);\n        primitives->push_back (3);\n        primitives->push_back (6);\n\n        primitives->push_back (1);\n        primitives->push_back (3);\n        primitives->push_back (5);\n\n        primitives->push_back (5);\n        primitives->push_back (3);\n        primitives->push_back (7);\n\n        geometry->addPrimitiveSet (primitives);\n\n        osg::Vec4Array *colours = new osg::Vec4Array;\n\n        colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.5f, 0.2f));\n        colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f));\n        colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f));\n        colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f));\n        colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f));\n        colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f));\n        colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f));\n        colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f));\n\n        geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX);\n\n        geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF);\n        geometry->getOrCreateStateSet()->setMode (GL_BLEND, osg::StateAttribute::ON);\n        geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n        geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n\n        mBaseNode->addChild (geometry);\n        mParentNode->addChild(mBaseNode);\n    }\n\n    void InstanceSelectionMode::drawSelectionCube(const osg::Vec3d& point, float radius)\n    {\n        if (mBaseNode) mParentNode->removeChild (mBaseNode);\n        mBaseNode = new osg::PositionAttitudeTransform;\n        mBaseNode->setPosition(point);\n\n        osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);\n\n        osg::Vec3Array *vertices = new osg::Vec3Array;\n        for (int i = 0; i < 2; ++i)\n        {\n            float height = i ? -radius : radius;\n            vertices->push_back (osg::Vec3f (height, -radius, -radius));\n            vertices->push_back (osg::Vec3f (height, -radius,  radius));\n            vertices->push_back (osg::Vec3f (height,  radius, -radius));\n            vertices->push_back (osg::Vec3f (height,  radius,  radius));\n        }\n\n        geometry->setVertexArray (vertices);\n\n        osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0);\n\n        // top\n        primitives->push_back (2);\n        primitives->push_back (1);\n        primitives->push_back (0);\n\n        primitives->push_back (3);\n        primitives->push_back (1);\n        primitives->push_back (2);\n\n        // bottom\n        primitives->push_back (4);\n        primitives->push_back (5);\n        primitives->push_back (6);\n\n        primitives->push_back (6);\n        primitives->push_back (5);\n        primitives->push_back (7);\n\n        // sides\n        primitives->push_back (1);\n        primitives->push_back (4);\n        primitives->push_back (0);\n\n        primitives->push_back (4);\n        primitives->push_back (1);\n        primitives->push_back (5);\n\n        primitives->push_back (4);\n        primitives->push_back (2);\n        primitives->push_back (0);\n\n        primitives->push_back (6);\n        primitives->push_back (2);\n        primitives->push_back (4);\n\n        primitives->push_back (6);\n        primitives->push_back (3);\n        primitives->push_back (2);\n\n        primitives->push_back (7);\n        primitives->push_back (3);\n        primitives->push_back (6);\n\n        primitives->push_back (1);\n        primitives->push_back (3);\n        primitives->push_back (5);\n\n        primitives->push_back (5);\n        primitives->push_back (3);\n        primitives->push_back (7);\n\n        geometry->addPrimitiveSet (primitives);\n\n        osg::Vec4Array *colours = new osg::Vec4Array;\n\n        colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.5f, 0.2f));\n        colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f));\n        colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f));\n        colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f));\n        colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f));\n        colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f));\n        colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f));\n        colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f));\n\n        geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX);\n\n        geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF);\n        geometry->getOrCreateStateSet()->setMode (GL_BLEND, osg::StateAttribute::ON);\n        geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n        geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n\n        mBaseNode->addChild (geometry);\n        mParentNode->addChild(mBaseNode);\n    }\n\n    void InstanceSelectionMode::drawSelectionSphere(const osg::Vec3f& mousePlanePoint)\n    {\n        float dragDistance = (mDragStart - mousePlanePoint).length();\n        drawSelectionSphere(mDragStart, dragDistance);\n    }\n\n    void InstanceSelectionMode::drawSelectionSphere(const osg::Vec3d& point, float radius)\n    {\n        if (mBaseNode) mParentNode->removeChild (mBaseNode);\n        mBaseNode = new osg::PositionAttitudeTransform;\n        mBaseNode->setPosition(point);\n\n        osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);\n\n        osg::Vec3Array *vertices = new osg::Vec3Array;\n        int resolution = 32;\n        float radiusPerResolution = radius / resolution;\n        float reciprocalResolution = 1.0f / resolution;\n        float doubleReciprocalRes = reciprocalResolution * 2;\n\n        osg::Vec4Array *colours = new osg::Vec4Array;\n\n        for (float i = 0.0; i <= resolution; i += 2)\n        {\n            float iShifted = (static_cast<float>(i) - resolution / 2.0f); // i - 16 = -16 ... 16\n            float xPercentile = iShifted * doubleReciprocalRes;\n            float x = xPercentile * radius;\n            float thisRadius = sqrt (radius * radius - x * x);\n\n            //the next row\n            float iShifted2 = (static_cast<float>(i + 1) - resolution / 2.0f);\n            float xPercentile2 = iShifted2 * doubleReciprocalRes;\n            float x2 = xPercentile2 * radius;\n            float thisRadius2 = sqrt (radius * radius - x2 * x2);\n\n            for (int j = 0; j < resolution; ++j)\n            {\n                float vertexX = thisRadius * sin(j * reciprocalResolution * osg::PI * 2);\n                float vertexY = i * radiusPerResolution * 2 - radius;\n                float vertexZ = thisRadius * cos(j * reciprocalResolution * osg::PI * 2);\n                float heightPercentage = (vertexZ + radius) / (radius * 2);\n                vertices->push_back (osg::Vec3f (vertexX, vertexY, vertexZ));\n                colours->push_back (osg::Vec4f (heightPercentage, heightPercentage, heightPercentage, 0.3f));\n\n                float vertexNextRowX = thisRadius2 * sin(j * reciprocalResolution * osg::PI * 2);\n                float vertexNextRowY = (i + 1) * radiusPerResolution * 2 - radius;\n                float vertexNextRowZ = thisRadius2 * cos(j * reciprocalResolution * osg::PI * 2);\n                float heightPercentageNextRow = (vertexZ + radius) / (radius * 2);\n                vertices->push_back (osg::Vec3f (vertexNextRowX, vertexNextRowY, vertexNextRowZ));\n                colours->push_back (osg::Vec4f (heightPercentageNextRow, heightPercentageNextRow, heightPercentageNextRow, 0.3f));\n            }\n        }\n\n        geometry->setVertexArray (vertices);\n\n        osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLE_STRIP, 0);\n\n        for (int i = 0; i < resolution; ++i)\n        {\n            //Even\n            for (int j = 0; j < resolution * 2; ++j)\n            {\n                if (i * resolution * 2 + j > static_cast<int>(vertices->size()) - 1) continue;\n                primitives->push_back (i * resolution * 2 + j);\n            }\n            if (i * resolution * 2 > static_cast<int>(vertices->size()) - 1) continue;\n            primitives->push_back (i * resolution * 2);\n            primitives->push_back (i * resolution * 2 + 1);\n\n            //Odd\n            for (int j = 1; j < resolution * 2 - 2; j += 2)\n            {\n                if ((i + 1) * resolution * 2 + j - 1 > static_cast<int>(vertices->size()) - 1) continue;\n                primitives->push_back ((i + 1) * resolution * 2 + j - 1);\n                primitives->push_back (i * resolution * 2 + j + 2);\n            }\n            if ((i + 2) * resolution * 2 - 2 > static_cast<int>(vertices->size()) - 1) continue;\n            primitives->push_back ((i + 2) * resolution * 2 - 2);\n            primitives->push_back (i * resolution * 2 + 1);\n            primitives->push_back ((i + 1) * resolution * 2);\n        }\n\n        geometry->addPrimitiveSet (primitives);\n\n        geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX);\n\n        geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF);\n        geometry->getOrCreateStateSet()->setMode (GL_BLEND, osg::StateAttribute::ON);\n        geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n        geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n\n        mBaseNode->addChild (geometry);\n        mParentNode->addChild(mBaseNode);\n    }\n\n    bool InstanceSelectionMode::createContextMenu(QMenu* menu)\n    {\n        if (menu)\n        {\n            SelectionMode::createContextMenu(menu);\n\n            menu->addAction(mSelectSame);\n            menu->addAction(mDeleteSelection);\n        }\n\n        return true;\n    }\n\n    void InstanceSelectionMode::selectSame()\n    {\n        getWorldspaceWidget().selectAllWithSameParentId(Mask_Reference);\n    }\n\n    void InstanceSelectionMode::deleteSelection()\n    {\n        std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection(Mask_Reference);\n\n        CSMWorld::IdTable& referencesTable = dynamic_cast<CSMWorld::IdTable&>(\n            *getWorldspaceWidget().getDocument().getData().getTableModel(CSMWorld::UniversalId::Type_References));\n\n        for (std::vector<osg::ref_ptr<TagBase> >::iterator iter = selection.begin(); iter != selection.end(); ++iter)\n        {\n            CSMWorld::DeleteCommand* command = new CSMWorld::DeleteCommand(referencesTable,\n                static_cast<ObjectTag*>(iter->get())->mObject->getReferenceId());\n\n            getWorldspaceWidget().getDocument().getUndoStack().push(command);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/instanceselectionmode.hpp",
    "content": "#ifndef CSV_RENDER_INSTANCE_SELECTION_MODE_H\n#define CSV_RENDER_INSTANCE_SELECTION_MODE_H\n\n#include <QPoint>\n\n#include <osg/PositionAttitudeTransform>\n#include <osg/Vec3d>\n\n#include \"selectionmode.hpp\"\n#include \"instancedragmodes.hpp\"\n\nnamespace CSVRender\n{\n    class InstanceSelectionMode : public SelectionMode\n    {\n            Q_OBJECT\n\n        public:\n\n            InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget, osg::Group *cellNode);\n\n            ~InstanceSelectionMode();\n\n            /// Store the worldspace-coordinate when drag begins\n            void setDragStart(const osg::Vec3d& dragStart);\n\n            /// Store the worldspace-coordinate when drag begins\n            const osg::Vec3d& getDragStart();\n\n            /// Store the screen-coordinate when drag begins\n            void setScreenDragStart(const QPoint& dragStartPoint);\n\n            /// Apply instance selection changes\n            void dragEnded(const osg::Vec3d& dragEndPoint, DragMode dragMode);\n\n            void drawSelectionCubeCentre(const osg::Vec3f& mousePlanePoint );\n            void drawSelectionCubeCorner(const osg::Vec3f& mousePlanePoint );\n            void drawSelectionSphere(const osg::Vec3f& mousePlanePoint );\n        protected:\n\n            /// Add context menu items to \\a menu.\n            ///\n            /// \\attention menu can be a 0-pointer\n            ///\n            /// \\return Have there been any menu items to be added (if menu is 0 and there\n            /// items to be added, the function must return true anyway.\n            bool createContextMenu(QMenu* menu) override;\n\n        private:\n\n            void drawSelectionBox(const osg::Vec3d& pointA, const osg::Vec3d& pointB);\n            void drawSelectionCube(const osg::Vec3d& point, float radius);\n            void drawSelectionSphere(const osg::Vec3d& point, float radius);\n\n            QAction* mDeleteSelection;\n            QAction* mSelectSame;\n            osg::Vec3d mDragStart;\n            osg::Group* mParentNode;\n            osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;\n\n        private slots:\n\n            void deleteSelection();\n            void selectSame();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/lighting.cpp",
    "content": "#include \"lighting.hpp\"\n\n#include <osg/LightSource>\n#include <osg/NodeVisitor>\n#include <osg/Switch>\n\n#include <components/misc/constants.hpp>\n\nclass DayNightSwitchVisitor : public osg::NodeVisitor\n{\npublic:\n    DayNightSwitchVisitor(int index)\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        , mIndex(index)\n    { }\n\n    void apply(osg::Switch &switchNode) override\n    {\n        if (switchNode.getName() == Constants::NightDayLabel)\n            switchNode.setSingleChildOn(mIndex);\n\n        traverse(switchNode);\n    }\n\nprivate:\n    int mIndex;\n};\n\nCSVRender::Lighting::~Lighting() {}\n\nvoid CSVRender::Lighting::updateDayNightMode(int index)\n{\n    if (mRootNode == nullptr)\n        return;\n\n    DayNightSwitchVisitor visitor(index);\n    mRootNode->accept(visitor);\n}\n"
  },
  {
    "path": "apps/opencs/view/render/lighting.hpp",
    "content": "#ifndef OPENCS_VIEW_LIGHTING_H\n#define OPENCS_VIEW_LIGHTING_H\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class Vec4f;\n    class LightSource;\n    class Group;\n}\n\nnamespace CSVRender\n{\n    class Lighting\n    {\n        public:\n\n            Lighting() : mRootNode(nullptr) {}\n            virtual ~Lighting();\n\n            virtual void activate (osg::Group* rootNode, bool isExterior) = 0;\n\n            virtual void deactivate() = 0;\n\n            virtual osg::Vec4f getAmbientColour(osg::Vec4f* defaultAmbient) = 0;\n\n        protected:\n\n            void updateDayNightMode(int index);\n\n            osg::ref_ptr<osg::LightSource> mLightSource;\n            osg::Group* mRootNode;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/lightingbright.cpp",
    "content": "#include \"lightingbright.hpp\"\n\n#include <osg/LightSource>\n\nCSVRender::LightingBright::LightingBright() {}\n\nvoid CSVRender::LightingBright::activate (osg::Group* rootNode, bool /*isExterior*/)\n{\n    mRootNode = rootNode;\n\n    mLightSource = (new osg::LightSource);\n\n    osg::ref_ptr<osg::Light> light (new osg::Light);\n    light->setAmbient(osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n    light->setPosition(osg::Vec4f(0.f, 0.f, 1.f, 0.f));\n    light->setDiffuse(osg::Vec4f(1.f, 1.f, 1.f, 1.f));\n    light->setSpecular(osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n    light->setConstantAttenuation(1.f);\n\n    mLightSource->setLight(light);\n\n    mRootNode->addChild(mLightSource);\n\n    updateDayNightMode(0);\n}\n\nvoid CSVRender::LightingBright::deactivate()\n{\n    if (mRootNode && mLightSource.get())\n        mRootNode->removeChild(mLightSource);\n}\n\nosg::Vec4f CSVRender::LightingBright::getAmbientColour(osg::Vec4f* /*defaultAmbient*/)\n{\n    return osg::Vec4f(1.f, 1.f, 1.f, 1.f);\n}\n"
  },
  {
    "path": "apps/opencs/view/render/lightingbright.hpp",
    "content": "#ifndef OPENCS_VIEW_LIGHTING_BRIGHT_H\n#define OPENCS_VIEW_LIGHTING_BRIGHT_H\n\n#include \"lighting.hpp\"\n\nnamespace osg\n{\n    class Light;\n    class Group;\n}\n\nnamespace CSVRender\n{\n    class LightingBright : public Lighting\n    {\n        public:\n\n            LightingBright();\n\n            void activate (osg::Group* rootNode, bool /*isExterior*/) override;\n\n            void deactivate() override;\n\n            osg::Vec4f getAmbientColour(osg::Vec4f* defaultAmbient) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/lightingday.cpp",
    "content": "#include \"lightingday.hpp\"\n\n#include <osg/LightSource>\n\nCSVRender::LightingDay::LightingDay(){}\n\nvoid CSVRender::LightingDay::activate (osg::Group* rootNode, bool /*isExterior*/)\n{\n    mRootNode = rootNode;\n\n    mLightSource = new osg::LightSource;\n\n    osg::ref_ptr<osg::Light> light (new osg::Light);\n    light->setPosition(osg::Vec4f(0.f, 0.f, 1.f, 0.f));\n    light->setAmbient(osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n    light->setDiffuse(osg::Vec4f(1.f, 1.f, 1.f, 1.f));\n    light->setSpecular(osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n    light->setConstantAttenuation(1.f);\n\n    mLightSource->setLight(light);\n    mRootNode->addChild(mLightSource);\n\n    updateDayNightMode(0);\n}\n\nvoid CSVRender::LightingDay::deactivate()\n{\n    if (mRootNode && mLightSource.get())\n        mRootNode->removeChild(mLightSource);\n}\n\nosg::Vec4f CSVRender::LightingDay::getAmbientColour(osg::Vec4f *defaultAmbient)\n{\n    if (defaultAmbient)\n        return *defaultAmbient;\n    else\n        return osg::Vec4f(0.7f, 0.7f, 0.7f, 1.f);\n}\n"
  },
  {
    "path": "apps/opencs/view/render/lightingday.hpp",
    "content": "#ifndef OPENCS_VIEW_LIGHTING_DAY_H\n#define OPENCS_VIEW_LIGHTING_DAY_H\n\n#include \"lighting.hpp\"\n\nnamespace CSVRender\n{\n    class LightingDay : public Lighting\n    {\n        public:\n\n            LightingDay();\n\n            void activate (osg::Group* rootNode, bool /*isExterior*/) override;\n\n            void deactivate() override;\n\n            osg::Vec4f getAmbientColour(osg::Vec4f *defaultAmbient) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/lightingnight.cpp",
    "content": "#include \"lightingnight.hpp\"\n\n#include <osg/LightSource>\n\nCSVRender::LightingNight::LightingNight() {}\n\nvoid CSVRender::LightingNight::activate (osg::Group* rootNode, bool isExterior)\n{\n    mRootNode = rootNode;\n\n    mLightSource = new osg::LightSource;\n\n    osg::ref_ptr<osg::Light> light (new osg::Light);\n    light->setPosition(osg::Vec4f(0.f, 0.f, 1.f, 0.f));\n    light->setAmbient(osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n    light->setDiffuse(osg::Vec4f(0.2f, 0.2f, 0.2f, 1.f));\n    light->setSpecular(osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n    light->setConstantAttenuation(1.f);\n\n    mLightSource->setLight(light);\n\n    mRootNode->addChild(mLightSource);\n\n    updateDayNightMode(isExterior ? 1 : 0);\n}\n\nvoid CSVRender::LightingNight::deactivate()\n{\n    if (mRootNode && mLightSource.get())\n        mRootNode->removeChild(mLightSource);\n}\n\nosg::Vec4f CSVRender::LightingNight::getAmbientColour(osg::Vec4f *defaultAmbient)\n{\n    if (defaultAmbient)\n        return *defaultAmbient;\n    else\n        return osg::Vec4f(0.2f, 0.2f, 0.2f, 1.f);\n}\n"
  },
  {
    "path": "apps/opencs/view/render/lightingnight.hpp",
    "content": "#ifndef OPENCS_VIEW_LIGHTING_NIGHT_H\n#define OPENCS_VIEW_LIGHTING_NIGHT_H\n\n#include \"lighting.hpp\"\n\nnamespace CSVRender\n{\n    class LightingNight : public Lighting\n    {\n        public:\n\n            LightingNight();\n\n            void activate (osg::Group* rootNode, bool isExterior) override;\n            void deactivate() override;\n\n            osg::Vec4f getAmbientColour(osg::Vec4f *defaultAmbient) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/mask.hpp",
    "content": "#ifndef CSV_RENDER_ELEMENTS_H\n#define CSV_RENDER_ELEMENTS_H\n\nnamespace CSVRender\n{\n\n    /// Node masks used on the OSG scene graph in OpenMW-CS.\n    /// @note See the respective file in OpenMW (apps/openmw/mwrender/vismask.hpp)\n    /// for general usage hints about node masks.\n    /// @copydoc MWRender::VisMask\n    enum Mask : unsigned int\n    {\n        // elements that are part of the actual scene\n        Mask_Reference = 0x2,\n        Mask_Pathgrid = 0x4,\n        Mask_Water = 0x8,\n        Mask_Fog = 0x10,\n        Mask_Terrain = 0x20,\n\n        // used within models\n        Mask_ParticleSystem = 0x100,\n\n        Mask_Lighting = 0x200,\n\n        // control elements\n        Mask_CellMarker = 0x10000,\n        Mask_CellArrow = 0x20000,\n        Mask_CellBorder = 0x40000\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/object.cpp",
    "content": "#include \"object.hpp\"\n\n#include <stdexcept>\n#include <string>\n#include <iostream>\n\n#include <osg/Depth>\n#include <osg/Group>\n#include <osg/PositionAttitudeTransform>\n\n#include <osg/ShapeDrawable>\n#include <osg/Shape>\n#include <osg/Geode>\n#include <osg/Geometry>\n#include <osg/PrimitiveSet>\n\n#include <osgFX/Scribe>\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/ref.hpp\"\n#include \"../../model/world/refidcollection.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n#include \"../../model/world/cellcoordinates.hpp\"\n#include \"../../model/prefs/state.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/sceneutil/lightutil.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n\n#include \"actor.hpp\"\n#include \"mask.hpp\"\n\n\nconst float CSVRender::Object::MarkerShaftWidth = 30;\nconst float CSVRender::Object::MarkerShaftBaseLength = 70;\nconst float CSVRender::Object::MarkerHeadWidth = 50;\nconst float CSVRender::Object::MarkerHeadLength = 50;\n\n\nnamespace\n{\n\n    osg::ref_ptr<osg::Geode> createErrorCube()\n    {\n        osg::ref_ptr<osg::Box> shape(new osg::Box(osg::Vec3f(0,0,0), 50.f));\n        osg::ref_ptr<osg::ShapeDrawable> shapedrawable(new osg::ShapeDrawable);\n        shapedrawable->setShape(shape);\n\n        osg::ref_ptr<osg::Geode> geode (new osg::Geode);\n        geode->addDrawable(shapedrawable);\n        return geode;\n    }\n\n}\n\n\nCSVRender::ObjectTag::ObjectTag (Object* object)\n: TagBase (Mask_Reference), mObject (object)\n{}\n\nQString CSVRender::ObjectTag::getToolTip (bool hideBasics) const\n{\n    return QString::fromUtf8 (mObject->getReferenceableId().c_str());\n}\n\n\nCSVRender::ObjectMarkerTag::ObjectMarkerTag (Object* object, int axis)\n: ObjectTag (object), mAxis (axis)\n{}\n\n\nvoid CSVRender::Object::clear()\n{\n}\n\nvoid CSVRender::Object::update()\n{\n    clear();\n\n    const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();\n    const int TypeIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType);\n    const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model);\n\n    int index = referenceables.searchId (mReferenceableId);\n    const ESM::Light* light = nullptr;\n\n    mBaseNode->removeChildren(0, mBaseNode->getNumChildren());\n\n    if (index == -1)\n    {\n        mBaseNode->addChild(createErrorCube());\n        return;\n    }\n\n    /// \\todo check for Deleted state (error 1)\n\n    int recordType = referenceables.getData(index, TypeIndex).toInt();\n    std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData();\n\n    if (recordType == CSMWorld::UniversalId::Type_Light)\n    {\n        light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();\n        if (model.empty())\n            model = \"marker_light.nif\";\n    }\n\n    if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)\n    {\n        if (model.empty())\n            model = \"marker_creature.nif\";\n    }\n\n    try\n    {\n        if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature)\n        {\n            if (!mActor) mActor.reset(new Actor(mReferenceableId, mData));\n            mActor->update();\n            mBaseNode->addChild(mActor->getBaseNode());\n        }\n        else if (!model.empty())\n        {\n            std::string path = \"meshes\\\\\" + model;\n            mResourceSystem->getSceneManager()->getInstance(path, mBaseNode);\n        }\n        else\n        {\n            throw std::runtime_error(mReferenceableId + \" has no model\");\n        }\n    }\n    catch (std::exception& e)\n    {\n        // TODO: use error marker mesh\n        Log(Debug::Error) << e.what();\n    }\n\n    if (light)\n    {\n        bool isExterior = false; // FIXME\n        SceneUtil::addLight(mBaseNode, light, Mask_ParticleSystem, Mask_Lighting, isExterior);\n    }\n}\n\nvoid CSVRender::Object::adjustTransform()\n{\n    if (mReferenceId.empty())\n        return;\n\n    ESM::Position position = getPosition();\n\n    // position\n    mRootNode->setPosition(mForceBaseToZero ? osg::Vec3() : osg::Vec3f(position.pos[0], position.pos[1], position.pos[2]));\n\n    // orientation\n    osg::Quat xr (-position.rot[0], osg::Vec3f(1,0,0));\n    osg::Quat yr (-position.rot[1], osg::Vec3f(0,1,0));\n    osg::Quat zr (-position.rot[2], osg::Vec3f(0,0,1));\n    mBaseNode->setAttitude(zr*yr*xr);\n\n    float scale = getScale();\n\n    mBaseNode->setScale(osg::Vec3(scale, scale, scale));\n}\n\nconst CSMWorld::CellRef& CSVRender::Object::getReference() const\n{\n    if (mReferenceId.empty())\n        throw std::logic_error (\"object does not represent a reference\");\n\n    return mData.getReferences().getRecord (mReferenceId).get();\n}\n\nvoid CSVRender::Object::updateMarker()\n{\n    for (int i=0; i<3; ++i)\n    {\n        if (mMarker[i])\n        {\n            mRootNode->removeChild (mMarker[i]);\n            mMarker[i] = osg::ref_ptr<osg::Node>();\n        }\n\n        if (mSelected)\n        {\n            if (mSubMode==0)\n            {\n                mMarker[i] = makeMoveOrScaleMarker (i);\n                mMarker[i]->setUserData(new ObjectMarkerTag (this, i));\n\n                mRootNode->addChild (mMarker[i]);\n            }\n            else if (mSubMode==1)\n            {\n                mMarker[i] = makeRotateMarker (i);\n                mMarker[i]->setUserData(new ObjectMarkerTag (this, i));\n\n                mRootNode->addChild (mMarker[i]);\n            }\n            else if (mSubMode==2)\n            {\n                mMarker[i] = makeMoveOrScaleMarker (i);\n                mMarker[i]->setUserData(new ObjectMarkerTag (this, i));\n\n                mRootNode->addChild (mMarker[i]);\n            }\n        }\n    }\n}\n\nosg::ref_ptr<osg::Node> CSVRender::Object::makeMoveOrScaleMarker (int axis)\n{\n    osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);\n\n    float shaftLength = MarkerShaftBaseLength + mBaseNode->getBound().radius();\n\n    // shaft\n    osg::Vec3Array *vertices = new osg::Vec3Array;\n\n    for (int i=0; i<2; ++i)\n    {\n        float length = i ? shaftLength : MarkerShaftWidth;\n\n        vertices->push_back (getMarkerPosition (-MarkerShaftWidth/2, -MarkerShaftWidth/2, length, axis));\n        vertices->push_back (getMarkerPosition (-MarkerShaftWidth/2, MarkerShaftWidth/2, length, axis));\n        vertices->push_back (getMarkerPosition (MarkerShaftWidth/2, MarkerShaftWidth/2, length, axis));\n        vertices->push_back (getMarkerPosition (MarkerShaftWidth/2, -MarkerShaftWidth/2, length, axis));\n    }\n\n    // head backside\n    vertices->push_back (getMarkerPosition (-MarkerHeadWidth/2, -MarkerHeadWidth/2, shaftLength, axis));\n    vertices->push_back (getMarkerPosition (-MarkerHeadWidth/2, MarkerHeadWidth/2, shaftLength, axis));\n    vertices->push_back (getMarkerPosition (MarkerHeadWidth/2, MarkerHeadWidth/2, shaftLength, axis));\n    vertices->push_back (getMarkerPosition (MarkerHeadWidth/2, -MarkerHeadWidth/2, shaftLength, axis));\n\n    // head\n    vertices->push_back (getMarkerPosition (0, 0, shaftLength+MarkerHeadLength, axis));\n\n    geometry->setVertexArray (vertices);\n\n    osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0);\n\n    // shaft\n    for (int i=0; i<4; ++i)\n    {\n        int i2 = i==3 ? 0 : i+1;\n        primitives->push_back (i);\n        primitives->push_back (4+i);\n        primitives->push_back (i2);\n\n        primitives->push_back (4+i);\n        primitives->push_back (4+i2);\n        primitives->push_back (i2);\n    }\n\n    // cap\n    primitives->push_back (0);\n    primitives->push_back (1);\n    primitives->push_back (2);\n\n    primitives->push_back (2);\n    primitives->push_back (3);\n    primitives->push_back (0);\n\n    // head, backside\n    primitives->push_back (0+8);\n    primitives->push_back (1+8);\n    primitives->push_back (2+8);\n\n    primitives->push_back (2+8);\n    primitives->push_back (3+8);\n    primitives->push_back (0+8);\n\n    for (int i=0; i<4; ++i)\n    {\n        primitives->push_back (12);\n        primitives->push_back (8+(i==3 ? 0 : i+1));\n        primitives->push_back (8+i);\n    }\n\n    geometry->addPrimitiveSet (primitives);\n\n    osg::Vec4Array *colours = new osg::Vec4Array;\n\n    for (int i=0; i<8; ++i)\n        colours->push_back (osg::Vec4f (axis==0 ? 1.0f : 0.2f, axis==1 ? 1.0f : 0.2f,\n            axis==2 ? 1.0f : 0.2f, mMarkerTransparency));\n\n    for (int i=8; i<8+4+1; ++i)\n        colours->push_back (osg::Vec4f (axis==0 ? 1.0f : 0.0f, axis==1 ? 1.0f : 0.0f,\n            axis==2 ? 1.0f : 0.0f, mMarkerTransparency));\n\n    geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX);\n\n    setupCommonMarkerState(geometry);\n\n    osg::ref_ptr<osg::Geode> geode (new osg::Geode);\n    geode->addDrawable (geometry);\n\n    return geode;\n}\n\nosg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)\n{\n    const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius());\n    const float OuterRadius = InnerRadius + MarkerShaftWidth;\n\n    const float SegmentDistance = 100.f;\n    const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * osg::PI / SegmentDistance)));\n    const size_t VerticesPerSegment = 4;\n    const size_t IndicesPerSegment = 24;\n\n    const size_t VertexCount = SegmentCount * VerticesPerSegment;\n    const size_t IndexCount = SegmentCount * IndicesPerSegment;\n\n    const float Angle = 2 * osg::PI / SegmentCount;\n\n    const unsigned short IndexPattern[IndicesPerSegment] =\n    {\n        0, 4, 5, 0, 5, 1,\n        2, 6, 4, 2, 4, 0,\n        3, 7, 6, 3, 6, 2,\n        1, 5, 7, 1, 7, 3\n    };\n\n\n    osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();\n\n    osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(VertexCount);\n    osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);\n    osg::ref_ptr<osg::DrawElementsUShort> primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES,\n        IndexCount);\n\n    // prevent some depth collision issues from overlaps\n    osg::Vec3f offset = getMarkerPosition(0, MarkerShaftWidth/4, 0, axis);\n\n    for (size_t i = 0; i < SegmentCount; ++i)\n    {\n        size_t index = i * VerticesPerSegment;\n\n        float innerX = InnerRadius * std::cos(i * Angle);\n        float innerY = InnerRadius * std::sin(i * Angle);\n\n        float outerX = OuterRadius * std::cos(i * Angle);\n        float outerY = OuterRadius * std::sin(i * Angle);\n\n        vertices->at(index++) = getMarkerPosition(innerX, innerY,  MarkerShaftWidth / 2, axis) + offset;\n        vertices->at(index++) = getMarkerPosition(innerX, innerY, -MarkerShaftWidth / 2, axis) + offset;\n        vertices->at(index++) = getMarkerPosition(outerX, outerY,  MarkerShaftWidth / 2, axis) + offset;\n        vertices->at(index++) = getMarkerPosition(outerX, outerY, -MarkerShaftWidth / 2, axis) + offset;\n    }\n\n    colors->at(0) = osg::Vec4f (\n        axis==0 ? 1.0f : 0.2f,\n        axis==1 ? 1.0f : 0.2f,\n        axis==2 ? 1.0f : 0.2f,\n        mMarkerTransparency);\n\n    for (size_t i = 0; i < SegmentCount; ++i)\n    {\n        size_t indices[IndicesPerSegment];\n        for (size_t j = 0; j < IndicesPerSegment; ++j)\n        {\n            indices[j] = i * VerticesPerSegment + j;\n\n            if (indices[j] >= VertexCount)\n                indices[j] -= VertexCount;\n        }\n\n        size_t elementOffset = i * IndicesPerSegment;\n        for (size_t j = 0; j < IndicesPerSegment; ++j)\n        {\n            primitives->setElement(elementOffset++, indices[IndexPattern[j]]);\n        }\n    }\n\n    geometry->setVertexArray(vertices);\n    geometry->setColorArray(colors, osg::Array::BIND_OVERALL);\n    geometry->addPrimitiveSet(primitives);\n\n    setupCommonMarkerState(geometry);\n\n    osg::ref_ptr<osg::Geode> geode = new osg::Geode();\n    geode->addDrawable (geometry);\n\n    return geode;\n}\n\nvoid CSVRender::Object::setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry)\n{\n    osg::ref_ptr<osg::StateSet> state = geometry->getOrCreateStateSet();\n    state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n    state->setMode(GL_BLEND, osg::StateAttribute::ON);\n\n    state->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n}\n\nosg::Vec3f CSVRender::Object::getMarkerPosition (float x, float y, float z, int axis)\n{\n    switch (axis)\n    {\n        case 2: return osg::Vec3f (x, y, z);\n        case 0: return osg::Vec3f (z, x, y);\n        case 1: return osg::Vec3f (y, z, x);\n\n        default:\n\n            throw std::logic_error (\"invalid axis for marker geometry\");\n    }\n}\n\nCSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode,\n    const std::string& id, bool referenceable, bool forceBaseToZero)\n: mData (data), mBaseNode(nullptr), mSelected(false), mParentNode(parentNode), mResourceSystem(data.getResourceSystem().get()), mForceBaseToZero (forceBaseToZero),\n  mScaleOverride (1), mOverrideFlags (0), mSubMode (-1), mMarkerTransparency(0.5f)\n{\n    mRootNode = new osg::PositionAttitudeTransform;\n\n    mBaseNode = new osg::PositionAttitudeTransform;\n    mBaseNode->addCullCallback(new SceneUtil::LightListCallback);\n\n    mOutline = new osgFX::Scribe;\n\n    mBaseNode->setUserData(new ObjectTag(this));\n\n    mRootNode->addChild (mBaseNode);\n\n    parentNode->addChild (mRootNode);\n\n    mRootNode->setNodeMask(Mask_Reference);\n\n    if (referenceable)\n    {\n        mReferenceableId = id;\n    }\n    else\n    {\n        mReferenceId = id;\n        mReferenceableId = getReference().mRefID;\n    }\n\n    adjustTransform();\n    update();\n    updateMarker();\n}\n\nCSVRender::Object::~Object()\n{\n    clear();\n\n    mParentNode->removeChild (mRootNode);\n}\n\nvoid CSVRender::Object::setSelected(bool selected)\n{\n    mSelected = selected;\n\n    mOutline->removeChild(mBaseNode);\n    mRootNode->removeChild(mOutline);\n    mRootNode->removeChild(mBaseNode);\n    if (selected)\n    {\n        mOutline->addChild(mBaseNode);\n        mRootNode->addChild(mOutline);\n    }\n    else\n        mRootNode->addChild(mBaseNode);\n\n    mMarkerTransparency = CSMPrefs::get()[\"Rendering\"][\"object-marker-alpha\"].toDouble();\n    updateMarker();\n}\n\nbool CSVRender::Object::getSelected() const\n{\n    return mSelected;\n}\n\nosg::ref_ptr<osg::Group> CSVRender::Object::getRootNode()\n{\n    return mRootNode;\n}\n\nosg::ref_ptr<osg::Group> CSVRender::Object::getBaseNode()\n{\n    return mBaseNode;\n}\n\nbool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();\n\n    int index = referenceables.searchId (mReferenceableId);\n\n    if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row())\n    {\n        adjustTransform();\n        update();\n        updateMarker();\n        return true;\n    }\n\n    return false;\n}\n\nbool CSVRender::Object::referenceableAboutToBeRemoved (const QModelIndex& parent, int start,\n    int end)\n{\n    const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();\n\n    int index = referenceables.searchId (mReferenceableId);\n\n    if (index!=-1 && index>=start && index<=end)\n    {\n        // Deletion of referenceable-type objects is handled outside of Object.\n        if (!mReferenceId.empty())\n        {\n            adjustTransform();\n            update();\n            return true;\n        }\n    }\n\n    return false;\n}\n\nbool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    if (mReferenceId.empty())\n        return false;\n\n    const CSMWorld::RefCollection& references = mData.getReferences();\n\n    int index = references.searchId (mReferenceId);\n\n    if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row())\n    {\n        int columnIndex =\n            references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId);\n\n        adjustTransform();\n\n        if (columnIndex>=topLeft.column() && columnIndex<=bottomRight.row())\n        {\n            mReferenceableId =\n                references.getData (index, columnIndex).toString().toUtf8().constData();\n\n            update();\n            updateMarker();\n        }\n\n        return true;\n    }\n\n    return false;\n}\n\nvoid CSVRender::Object::reloadAssets()\n{\n    update();\n    updateMarker();\n}\n\nstd::string CSVRender::Object::getReferenceId() const\n{\n    return mReferenceId;\n}\n\nstd::string CSVRender::Object::getReferenceableId() const\n{\n    return mReferenceableId;\n}\n\nosg::ref_ptr<CSVRender::TagBase> CSVRender::Object::getTag() const\n{\n    return static_cast<CSVRender::TagBase *> (mBaseNode->getUserData());\n}\n\nbool CSVRender::Object::isEdited() const\n{\n    return mOverrideFlags;\n}\n\nvoid CSVRender::Object::setEdited (int flags)\n{\n    bool discard = mOverrideFlags & ~flags;\n    int added = flags & ~mOverrideFlags;\n\n    mOverrideFlags = flags;\n\n    if (added & Override_Position)\n        for (int i=0; i<3; ++i)\n            mPositionOverride.pos[i] = getReference().mPos.pos[i];\n\n    if (added & Override_Rotation)\n        for (int i=0; i<3; ++i)\n            mPositionOverride.rot[i] = getReference().mPos.rot[i];\n\n    if (added & Override_Scale)\n        mScaleOverride = getReference().mScale;\n\n    if (discard)\n        adjustTransform();\n}\n\nESM::Position CSVRender::Object::getPosition() const\n{\n    ESM::Position position = getReference().mPos;\n\n    if (mOverrideFlags & Override_Position)\n        for (int i=0; i<3; ++i)\n            position.pos[i] = mPositionOverride.pos[i];\n\n    if (mOverrideFlags & Override_Rotation)\n        for (int i=0; i<3; ++i)\n            position.rot[i] = mPositionOverride.rot[i];\n\n    return position;\n}\n\nfloat CSVRender::Object::getScale() const\n{\n    return (mOverrideFlags & Override_Scale) ? mScaleOverride : getReference().mScale;\n}\n\nvoid CSVRender::Object::setPosition (const float position[3])\n{\n    mOverrideFlags |= Override_Position;\n\n    for (int i=0; i<3; ++i)\n        mPositionOverride.pos[i] = position[i];\n\n    adjustTransform();\n}\n\nvoid CSVRender::Object::setRotation (const float rotation[3])\n{\n    mOverrideFlags |= Override_Rotation;\n\n    for (int i=0; i<3; ++i)\n        mPositionOverride.rot[i] = rotation[i];\n\n    adjustTransform();\n}\n\nvoid CSVRender::Object::setScale (float scale)\n{\n    mOverrideFlags |= Override_Scale;\n\n    mScaleOverride = scale;\n\n    adjustTransform();\n}\n\nvoid CSVRender::Object::setMarkerTransparency(float value)\n{\n    mMarkerTransparency = value;\n    updateMarker();\n}\n\nvoid CSVRender::Object::apply (CSMWorld::CommandMacro& commands)\n{\n    const CSMWorld::RefCollection& collection = mData.getReferences();\n    QAbstractItemModel *model = mData.getTableModel (CSMWorld::UniversalId::Type_References);\n\n    int recordIndex = collection.getIndex (mReferenceId);\n\n    if (mOverrideFlags & Override_Position)\n    {\n        //Do cell check first so positions can be compared\n        const CSMWorld::CellRef& ref = collection.getRecord(recordIndex).get();\n\n        if (CSMWorld::CellCoordinates::isExteriorCell(ref.mCell))\n        {\n            // Find cell index at new position\n            std::pair<int, int> cellIndex = CSMWorld::CellCoordinates::coordinatesToCellIndex(\n                mPositionOverride.pos[0], mPositionOverride.pos[1]);\n            std::pair<int, int> originalIndex = ref.getCellIndex();\n\n            int cellColumn = collection.findColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (\n                CSMWorld::Columns::ColumnId_Cell));\n            int refNumColumn = collection.findColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (\n                CSMWorld::Columns::ColumnId_RefNum));\n\n            if (cellIndex != originalIndex)\n            {\n                /// \\todo figure out worldspace (not important until multiple worldspaces are supported)\n                std::string cellId = CSMWorld::CellCoordinates (cellIndex).getId (\"\");\n\n                commands.push (new CSMWorld::ModifyCommand (*model,\n                    model->index (recordIndex, cellColumn), QString::fromUtf8 (cellId.c_str())));\n                commands.push (new CSMWorld::ModifyCommand( *model,\n                    model->index (recordIndex, refNumColumn), 0));\n            }\n        }\n\n        for (int i=0; i<3; ++i)\n        {\n            int column = collection.findColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (\n                CSMWorld::Columns::ColumnId_PositionXPos+i));\n\n            commands.push (new CSMWorld::ModifyCommand (*model,\n                model->index (recordIndex, column), mPositionOverride.pos[i]));\n        }\n    }\n\n    if (mOverrideFlags & Override_Rotation)\n    {\n        for (int i=0; i<3; ++i)\n        {\n            int column = collection.findColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (\n                CSMWorld::Columns::ColumnId_PositionXRot+i));\n\n            commands.push (new CSMWorld::ModifyCommand (*model,\n                model->index (recordIndex, column), osg::RadiansToDegrees(mPositionOverride.rot[i])));\n        }\n    }\n\n    if (mOverrideFlags & Override_Scale)\n    {\n        int column = collection.findColumnIndex (CSMWorld::Columns::ColumnId_Scale);\n\n        commands.push (new CSMWorld::ModifyCommand (*model,\n            model->index (recordIndex, column), mScaleOverride));\n    }\n\n    mOverrideFlags = 0;\n}\n\nvoid CSVRender::Object::setSubMode (int subMode)\n{\n    if (subMode!=mSubMode)\n    {\n        mSubMode = subMode;\n        updateMarker();\n    }\n}\n\nvoid CSVRender::Object::reset()\n{\n    mOverrideFlags = 0;\n    adjustTransform();\n    updateMarker();\n}\n"
  },
  {
    "path": "apps/opencs/view/render/object.hpp",
    "content": "#ifndef OPENCS_VIEW_OBJECT_H\n#define OPENCS_VIEW_OBJECT_H\n\n#include <memory>\n#include <string>\n\n#include <osg/ref_ptr>\n#include <osg/Geometry>\n#include <osg/Referenced>\n\n#include <components/esm/defs.hpp>\n\n#include \"tagbase.hpp\"\n\nclass QModelIndex;\nclass QUndoStack;\n\nnamespace osg\n{\n    class PositionAttitudeTransform;\n    class Group;\n    class Node;\n    class Geode;\n}\n\nnamespace osgFX\n{\n    class Scribe;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace CSMWorld\n{\n    class Data;\n    struct CellRef;\n    class CommandMacro;\n}\n\nnamespace CSVRender\n{\n    class Actor;\n    class Object;\n\n    // An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query\n    class ObjectTag : public TagBase\n    {\n        public:\n\n            ObjectTag (Object* object);\n\n            Object* mObject;\n\n            QString getToolTip (bool hideBasics) const override;\n    };\n\n    class ObjectMarkerTag : public ObjectTag\n    {\n        public:\n\n            ObjectMarkerTag (Object* object, int axis);\n\n            int mAxis;\n    };\n\n    class Object\n    {\n        public:\n\n            enum OverrideFlags\n            {\n                Override_Position = 1,\n                Override_Rotation = 2,\n                Override_Scale = 4\n            };\n\n        private:\n\n            static const float MarkerShaftWidth;\n            static const float MarkerShaftBaseLength;\n            static const float MarkerHeadWidth;\n            static const float MarkerHeadLength;\n\n            CSMWorld::Data& mData;\n            std::string mReferenceId;\n            std::string mReferenceableId;\n            osg::ref_ptr<osg::PositionAttitudeTransform> mRootNode;\n            osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;\n            osg::ref_ptr<osgFX::Scribe> mOutline;\n            bool mSelected;\n            osg::Group* mParentNode;\n            Resource::ResourceSystem* mResourceSystem;\n            bool mForceBaseToZero;\n            ESM::Position mPositionOverride;\n            float mScaleOverride;\n            int mOverrideFlags;\n            osg::ref_ptr<osg::Node> mMarker[3];\n            int mSubMode;\n            float mMarkerTransparency;\n            std::unique_ptr<Actor> mActor;\n\n            /// Not implemented\n            Object (const Object&);\n\n            /// Not implemented\n            Object& operator= (const Object&);\n\n            /// Remove object from node (includes deleting)\n            void clear();\n\n            /// Update model\n            /// @note Make sure adjustTransform() was called first so world space particles get positioned correctly\n            void update();\n\n            /// Adjust position, orientation and scale\n            void adjustTransform();\n\n            /// Throws an exception if *this was constructed with referenceable\n            const CSMWorld::CellRef& getReference() const;\n\n            void updateMarker();\n\n            osg::ref_ptr<osg::Node> makeMoveOrScaleMarker (int axis);\n            osg::ref_ptr<osg::Node> makeRotateMarker (int axis);\n\n            /// Sets up a stateset with properties common to all marker types.\n            void setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry);\n\n            osg::Vec3f getMarkerPosition (float x, float y, float z, int axis);\n\n        public:\n\n            Object (CSMWorld::Data& data, osg::Group *cellNode,\n                const std::string& id, bool referenceable,\n                bool forceBaseToZero = false);\n            /// \\param forceBaseToZero If this is a reference ignore the coordinates and place\n            /// it at 0, 0, 0 instead.\n\n            ~Object();\n\n            /// Mark the object as selected, selected objects show an outline effect\n            void setSelected(bool selected);\n\n            bool getSelected() const;\n\n            /// Get object node with GUI graphics\n            osg::ref_ptr<osg::Group> getRootNode();\n\n            /// Get object node without GUI graphics\n            osg::ref_ptr<osg::Group> getBaseNode();\n\n            /// \\return Did this call result in a modification of the visual representation of\n            /// this object?\n            bool referenceableDataChanged (const QModelIndex& topLeft,\n                const QModelIndex& bottomRight);\n\n            /// \\return Did this call result in a modification of the visual representation of\n            /// this object?\n            bool referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            /// \\return Did this call result in a modification of the visual representation of\n            /// this object?\n            bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            /// Reloads the underlying asset\n            void reloadAssets();\n\n            /// Returns an empty string if this is a refereceable-type object.\n            std::string getReferenceId() const;\n\n            std::string getReferenceableId() const;\n\n            osg::ref_ptr<TagBase> getTag() const;\n\n            /// Is there currently an editing operation running on this object?\n            bool isEdited() const;\n\n            void setEdited (int flags);\n\n            ESM::Position getPosition() const;\n\n            float getScale() const;\n\n            /// Set override position.\n            void setPosition (const float position[3]);\n\n            /// Set override rotation\n            void setRotation (const float rotation[3]);\n\n            /// Set override scale\n            void setScale (float scale);\n\n            void setMarkerTransparency(float value);\n\n            /// Apply override changes via command and end edit mode\n            void apply (CSMWorld::CommandMacro& commands);\n\n            void setSubMode (int subMode);\n\n            /// Erase all overrides and restore the visual representation of the object to its\n            /// true state.\n            void reset();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/orbitcameramode.cpp",
    "content": "#include \"orbitcameramode.hpp\"\n\n#include <QMenu>\n\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"worldspacewidget.hpp\"\n\nnamespace CSVRender\n{\n    OrbitCameraMode::OrbitCameraMode(WorldspaceWidget* worldspaceWidget, const QIcon& icon, const QString& tooltip,\n        QWidget* parent)\n        : ModeButton(icon, tooltip, parent)\n        , mWorldspaceWidget(worldspaceWidget)\n        , mCenterOnSelection(nullptr)\n    {\n        mCenterShortcut = new CSMPrefs::Shortcut(\"orbit-center-selection\", worldspaceWidget);\n        mCenterShortcut->enable(false);\n        connect(mCenterShortcut, SIGNAL(activated()), this, SLOT(centerSelection()));\n    }\n\n    OrbitCameraMode::~OrbitCameraMode()\n    {\n    }\n\n    void OrbitCameraMode::activate(CSVWidget::SceneToolbar* toolbar)\n    {\n        mCenterOnSelection = new QAction(\"Center on selected object\", this);\n        mCenterShortcut->associateAction(mCenterOnSelection);\n        connect(mCenterOnSelection, SIGNAL(triggered()), this, SLOT(centerSelection()));\n\n        mCenterShortcut->enable(true);\n    }\n\n    void OrbitCameraMode::deactivate(CSVWidget::SceneToolbar* toolbar)\n    {\n        mCenterShortcut->associateAction(nullptr);\n        mCenterShortcut->enable(false);\n    }\n\n    bool OrbitCameraMode::createContextMenu(QMenu* menu)\n    {\n        if (menu)\n        {\n            menu->addAction(mCenterOnSelection);\n        }\n\n        return true;\n    }\n\n    void OrbitCameraMode::centerSelection()\n    {\n        mWorldspaceWidget->centerOrbitCameraOnSelection();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/orbitcameramode.hpp",
    "content": "#ifndef CSV_RENDER_ORBITCAMERAPICKMODE_H\n#define CSV_RENDER_ORBITCAMERAPICKMODE_H\n\n#include <memory>\n\n#include \"../widget/modebutton.hpp\"\n\nnamespace CSMPrefs\n{\n    class Shortcut;\n}\n\nnamespace CSVRender\n{\n    class WorldspaceWidget;\n\n    class OrbitCameraMode : public CSVWidget::ModeButton\n    {\n            Q_OBJECT\n\n        public:\n\n            OrbitCameraMode(WorldspaceWidget* worldspaceWidget, const QIcon& icon, const QString& tooltip = \"\",\n                QWidget* parent = nullptr);\n            ~OrbitCameraMode();\n\n            void activate(CSVWidget::SceneToolbar* toolbar) override;\n            void deactivate(CSVWidget::SceneToolbar* toolbar) override;\n            bool createContextMenu(QMenu* menu) override;\n\n        private:\n\n            WorldspaceWidget* mWorldspaceWidget;\n            QAction* mCenterOnSelection;\n            CSMPrefs::Shortcut* mCenterShortcut;\n\n        private slots:\n\n            void centerSelection();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/pagedworldspacewidget.cpp",
    "content": "#include \"pagedworldspacewidget.hpp\"\n\n#include <memory>\n#include <sstream>\n#include <string>\n\n#include <QMouseEvent>\n#include <QApplication>\n\n#include <components/misc/constants.hpp>\n\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"../../model/world/idtable.hpp\"\n\n#include \"../widget/scenetooltoggle2.hpp\"\n#include \"../widget/scenetoolmode.hpp\"\n\n#include \"editmode.hpp\"\n#include \"mask.hpp\"\n#include \"cameracontroller.hpp\"\n#include \"cellarrow.hpp\"\n#include \"terraintexturemode.hpp\"\n#include \"terrainshapemode.hpp\"\n\nbool CSVRender::PagedWorldspaceWidget::adjustCells()\n{\n    bool modified = false;\n\n    const CSMWorld::IdCollection<CSMWorld::Cell>& cells = mDocument.getData().getCells();\n\n    {\n        // remove/update\n        std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());\n\n        while (iter!=mCells.end())\n        {\n            if (!mSelection.has (iter->first))\n            {\n                // remove\n                delete iter->second;\n                mCells.erase (iter++);\n\n                modified = true;\n            }\n            else\n            {\n                // update\n                int index = cells.searchId (iter->first.getId (mWorldspace));\n\n                bool deleted = index==-1 ||\n                    cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted;\n\n                if (deleted!=iter->second->isDeleted())\n                {\n                    modified = true;\n\n                    std::unique_ptr<Cell> cell (new Cell (mDocument.getData(), mRootNode,\n                        iter->first.getId (mWorldspace), deleted));\n\n                    delete iter->second;\n                    iter->second = cell.release();\n                }\n                else if (!deleted)\n                {\n                    // delete state has not changed -> just update\n\n                    // TODO check if name or region field has changed (cell marker)\n                    // FIXME: config setting\n                    //std::string name = cells.getRecord(index).get().mName;\n                    //std::string region = cells.getRecord(index).get().mRegion;\n\n                    modified = true;\n                }\n\n                ++iter;\n            }\n        }\n    }\n\n    // add\n    for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end();\n        ++iter)\n    {\n        if (mCells.find (*iter)==mCells.end())\n        {\n            addCellToScene (*iter);\n            modified = true;\n        }\n    }\n\n    if (modified)\n    {\n        for (std::map<CSMWorld::CellCoordinates, Cell *>::const_iterator iter (mCells.begin());\n            iter!=mCells.end(); ++iter)\n        {\n            int mask = 0;\n\n            for (int i=CellArrow::Direction_North; i<=CellArrow::Direction_East; i *= 2)\n            {\n                CSMWorld::CellCoordinates coordinates (iter->second->getCoordinates());\n\n                switch (i)\n                {\n                    case CellArrow::Direction_North: coordinates = coordinates.move (0, 1); break;\n                    case CellArrow::Direction_West: coordinates = coordinates.move (-1, 0); break;\n                    case CellArrow::Direction_South: coordinates = coordinates.move (0, -1); break;\n                    case CellArrow::Direction_East: coordinates = coordinates.move (1, 0); break;\n                }\n\n                if (!mSelection.has (coordinates))\n                    mask |= i;\n            }\n\n            iter->second->setCellArrows (mask);\n        }\n    }\n\n    return modified;\n}\n\nvoid CSVRender::PagedWorldspaceWidget::addVisibilitySelectorButtons (\n    CSVWidget::SceneToolToggle2 *tool)\n{\n    WorldspaceWidget::addVisibilitySelectorButtons (tool);\n    tool->addButton (Button_Terrain, Mask_Terrain, \"Terrain\");\n    tool->addButton (Button_Fog, Mask_Fog, \"Fog\", \"\", true);\n}\n\nvoid CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (\n    CSVWidget::SceneToolMode *tool)\n{\n    WorldspaceWidget::addEditModeSelectorButtons (tool);\n\n    /// \\todo replace EditMode with suitable subclasses\n    tool->addButton (\n        new TerrainShapeMode (this, mRootNode, tool), \"terrain-shape\");\n    tool->addButton (\n        new TerrainTextureMode (this, mRootNode, tool), \"terrain-texture\");\n    tool->addButton (\n        new EditMode (this, QIcon (\":placeholder\"), Mask_Reference, \"Terrain vertex paint editing\"),\n        \"terrain-vertex\");\n    tool->addButton (\n        new EditMode (this, QIcon (\":placeholder\"), Mask_Reference, \"Terrain movement\"),\n        \"terrain-move\");\n}\n\nvoid CSVRender::PagedWorldspaceWidget::handleInteractionPress (const WorldspaceHitResult& hit, InteractionType type)\n{\n    if (hit.tag && hit.tag->getMask()==Mask_CellArrow)\n    {\n        if (CellArrowTag *cellArrowTag = dynamic_cast<CSVRender::CellArrowTag *> (hit.tag.get()))\n        {\n            CellArrow *arrow = cellArrowTag->getCellArrow();\n\n            CSMWorld::CellCoordinates coordinates = arrow->getCoordinates();\n\n            CellArrow::Direction direction = arrow->getDirection();\n\n            int x = 0;\n            int y = 0;\n\n            switch (direction)\n            {\n                case CellArrow::Direction_North: y = 1; break;\n                case CellArrow::Direction_West: x = -1; break;\n                case CellArrow::Direction_South: y = -1; break;\n                case CellArrow::Direction_East: x = 1; break;\n            }\n\n            bool modified = false;\n\n            if (type == InteractionType_PrimarySelect)\n            {\n                addCellSelection (x, y);\n                modified = true;\n            }\n            else if (type == InteractionType_SecondarySelect)\n            {\n                moveCellSelection (x, y);\n                modified = true;\n            }\n            else // Primary/SecondaryEdit\n            {\n                CSMWorld::CellCoordinates newCoordinates = coordinates.move (x, y);\n\n                if (mCells.find (newCoordinates)==mCells.end())\n                {\n                    addCellToScene (newCoordinates);\n                    mSelection.add (newCoordinates);\n                    modified = true;\n                }\n\n                if (type == InteractionType_SecondaryEdit)\n                {\n                    if (mCells.find (coordinates)!=mCells.end())\n                    {\n                        removeCellFromScene (coordinates);\n                        mSelection.remove (coordinates);\n                        modified = true;\n                    }\n                }\n            }\n\n            if (modified)\n                adjustCells();\n\n            return;\n        }\n    }\n\n    WorldspaceWidget::handleInteractionPress (hit, type);\n}\n\nvoid CSVRender::PagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());\n        iter!=mCells.end(); ++iter)\n        if (iter->second->referenceableDataChanged (topLeft, bottomRight))\n            flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::referenceableAboutToBeRemoved (\n    const QModelIndex& parent, int start, int end)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());\n        iter!=mCells.end(); ++iter)\n        if (iter->second->referenceableAboutToBeRemoved (parent, start, end))\n            flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent,\n    int start, int end)\n{\n    CSMWorld::IdTable& referenceables = dynamic_cast<CSMWorld::IdTable&> (\n        *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables));\n\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());\n        iter!=mCells.end(); ++iter)\n    {\n        QModelIndex topLeft = referenceables.index (start, 0);\n        QModelIndex bottomRight =\n            referenceables.index (end, referenceables.columnCount());\n\n        if (iter->second->referenceableDataChanged (topLeft, bottomRight))\n            flagAsModified();\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());\n        iter!=mCells.end(); ++iter)\n        if (iter->second->referenceDataChanged (topLeft, bottomRight))\n            flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent,\n    int start, int end)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());\n        iter!=mCells.end(); ++iter)\n        if (iter->second->referenceAboutToBeRemoved (parent, start, end))\n            flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start,\n    int end)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());\n        iter!=mCells.end(); ++iter)\n        if (iter->second->referenceAdded (parent, start, end))\n            flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();\n\n    int rowStart = -1;\n    int rowEnd = -1;\n\n    if (topLeft.parent().isValid())\n    {\n        rowStart = topLeft.parent().row();\n        rowEnd = bottomRight.parent().row();\n    }\n    else\n    {\n        rowStart = topLeft.row();\n        rowEnd = bottomRight.row();\n    }\n\n    for (int row = rowStart; row <= rowEnd; ++row)\n    {\n        const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get();\n        CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY);\n\n        std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);\n        if (searchResult != mCells.end())\n        {\n            searchResult->second->pathgridModified();\n            flagAsModified();\n        }\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();\n\n    if (!parent.isValid())\n    {\n        // Pathgrid going to be deleted\n        for (int row = start; row <= end; ++row)\n        {\n            const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get();\n            CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY);\n\n            std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);\n            if (searchResult != mCells.end())\n            {\n                searchResult->second->pathgridRemoved();\n                flagAsModified();\n            }\n        }\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent, int start, int end)\n{\n   const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();\n\n    if (!parent.isValid())\n    {\n        for (int row = start; row <= end; ++row)\n        {\n            const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get();\n            CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY);\n\n            std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);\n            if (searchResult != mCells.end())\n            {\n                searchResult->second->pathgridModified();\n                flagAsModified();\n            }\n        }\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    for (int r = topLeft.row(); r <= bottomRight.row(); ++r)\n    {\n        std::string id = mDocument.getData().getLand().getId(r);\n\n        auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);\n        if (cellIt != mCells.end())\n        {\n            cellIt->second->landDataChanged(topLeft, bottomRight);\n            flagAsModified();\n        }\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::landAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    for (int r = start; r <= end; ++r)\n    {\n        std::string id = mDocument.getData().getLand().getId(r);\n\n        auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);\n        if (cellIt != mCells.end())\n        {\n            cellIt->second->landAboutToBeRemoved(parent, start, end);\n            flagAsModified();\n        }\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::landAdded (const QModelIndex& parent, int start, int end)\n{\n    for (int r = start; r <= end; ++r)\n    {\n        std::string id = mDocument.getData().getLand().getId(r);\n\n        auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);\n        if (cellIt != mCells.end())\n        {\n            cellIt->second->landAdded(parent, start, end);\n            flagAsModified();\n        }\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    for (auto cellIt : mCells)\n        cellIt.second->landTextureChanged(topLeft, bottomRight);\n    flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    for (auto cellIt : mCells)\n        cellIt.second->landTextureAboutToBeRemoved(parent, start, end);\n    flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::landTextureAdded (const QModelIndex& parent, int start, int end)\n{\n    for (auto cellIt : mCells)\n        cellIt.second->landTextureAdded(parent, start, end);\n    flagAsModified();\n}\n\n\nstd::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()\n{\n    osg::Vec3d eye, center, up;\n    mView->getCamera()->getViewMatrixAsLookAt(eye, center, up);\n    osg::Vec3d position = eye;\n\n    std::ostringstream stream;\n\n    stream\n        << \"player->position \"\n        << position.x() << \", \" << position.y() << \", \" << position.z()\n        << \", 0\";\n\n    return stream.str();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::addCellToScene (\n    const CSMWorld::CellCoordinates& coordinates)\n{\n    const CSMWorld::IdCollection<CSMWorld::Cell>& cells = mDocument.getData().getCells();\n\n    int index = cells.searchId (coordinates.getId (mWorldspace));\n\n    bool deleted = index==-1 ||\n        cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted;\n\n    std::unique_ptr<Cell> cell (\n        new Cell (mDocument.getData(), mRootNode, coordinates.getId (mWorldspace),\n        deleted));\n    EditMode *editMode = getEditMode();\n    cell->setSubMode (editMode->getSubMode(), editMode->getInteractionMask());\n\n    mCells.insert (std::make_pair (coordinates, cell.release()));\n}\n\nvoid CSVRender::PagedWorldspaceWidget::removeCellFromScene (\n    const CSMWorld::CellCoordinates& coordinates)\n{\n    std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.find (coordinates);\n\n    if (iter!=mCells.end())\n    {\n        delete iter->second;\n        mCells.erase (iter);\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::addCellSelection (int x, int y)\n{\n    CSMWorld::CellSelection newSelection = mSelection;\n    newSelection.move (x, y);\n\n    for (CSMWorld::CellSelection::Iterator iter (newSelection.begin()); iter!=newSelection.end();\n        ++iter)\n    {\n        if (mCells.find (*iter)==mCells.end())\n        {\n            addCellToScene (*iter);\n            mSelection.add (*iter);\n        }\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::moveCellSelection (int x, int y)\n{\n    CSMWorld::CellSelection newSelection = mSelection;\n    newSelection.move (x, y);\n\n    for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end();\n        ++iter)\n    {\n        if (!newSelection.has (*iter))\n            removeCellFromScene (*iter);\n    }\n\n    for (CSMWorld::CellSelection::Iterator iter (newSelection.begin()); iter!=newSelection.end();\n        ++iter)\n    {\n        if (!mSelection.has (*iter))\n            addCellToScene (*iter);\n    }\n\n    mSelection = newSelection;\n}\n\nvoid CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, int offsetY)\n{\n    osg::Vec3f eye, center, up;\n    getCamera()->getViewMatrixAsLookAt(eye, center, up);\n\n    int cellX = (int)std::floor(center.x() / Constants::CellSizeInUnits) + offsetX;\n    int cellY = (int)std::floor(center.y() / Constants::CellSizeInUnits) + offsetY;\n\n    CSMWorld::CellCoordinates cellCoordinates(cellX, cellY);\n\n    if (!mSelection.has(cellCoordinates))\n    {\n        addCellToScene(cellCoordinates);\n        mSelection.add(cellCoordinates);\n\n        adjustCells();\n    }\n}\n\nCSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document)\n: WorldspaceWidget (document, parent), mDocument (document), mWorldspace (\"std::default\"),\n  mControlElements(nullptr), mDisplayCellCoord(true)\n{\n    QAbstractItemModel *cells =\n        document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells);\n\n    connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (cells, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),\n        this, SLOT (cellRemoved (const QModelIndex&, int, int)));\n    connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (cellAdded (const QModelIndex&, int, int)));\n\n    connect (&document.getData(), SIGNAL (assetTablesChanged ()),\n        this, SLOT (assetTablesChanged ()));\n\n    QAbstractItemModel *lands = document.getData().getTableModel (CSMWorld::UniversalId::Type_Lands);\n\n    connect (lands, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (landDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (lands, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (landAboutToBeRemoved (const QModelIndex&, int, int)));\n    connect (lands, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (landAdded (const QModelIndex&, int, int)));\n\n    QAbstractItemModel *ltexs = document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures);\n\n    connect (ltexs, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (landTextureDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (ltexs, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (landTextureAboutToBeRemoved (const QModelIndex&, int, int)));\n    connect (ltexs, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (landTextureAdded (const QModelIndex&, int, int)));\n\n    // Shortcuts\n    CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut(\"scene-load-cam-cell\", this);\n    connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell()));\n\n    CSMPrefs::Shortcut* loadCameraEastCellShortcut = new CSMPrefs::Shortcut(\"scene-load-cam-eastcell\", this);\n    connect(loadCameraEastCellShortcut, SIGNAL(activated()), this, SLOT(loadEastCell()));\n\n    CSMPrefs::Shortcut* loadCameraNorthCellShortcut = new CSMPrefs::Shortcut(\"scene-load-cam-northcell\", this);\n    connect(loadCameraNorthCellShortcut, SIGNAL(activated()), this, SLOT(loadNorthCell()));\n\n    CSMPrefs::Shortcut* loadCameraWestCellShortcut = new CSMPrefs::Shortcut(\"scene-load-cam-westcell\", this);\n    connect(loadCameraWestCellShortcut, SIGNAL(activated()), this, SLOT(loadWestCell()));\n\n    CSMPrefs::Shortcut* loadCameraSouthCellShortcut = new CSMPrefs::Shortcut(\"scene-load-cam-southcell\", this);\n    connect(loadCameraSouthCellShortcut, SIGNAL(activated()), this, SLOT(loadSouthCell()));\n}\n\nCSVRender::PagedWorldspaceWidget::~PagedWorldspaceWidget()\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());\n        iter!=mCells.end(); ++iter)\n    {\n        delete iter->second;\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint)\n{\n    if (!hint.empty())\n    {\n        CSMWorld::CellSelection selection;\n\n        if (hint[0]=='c')\n        {\n            // syntax: c:#x1 y1; #x2 y2 (number of coordinate pairs can be 0 or larger)\n            char ignore;\n\n            std::istringstream stream (hint.c_str());\n            if (stream >> ignore)\n            {\n                char ignore1; // : or ;\n                char ignore2; // #\n                // Current coordinate\n                int x, y;\n\n                // Loop through all the coordinates to add them to selection\n                while (stream >> ignore1 >> ignore2 >> x >> y)\n                    selection.add (CSMWorld::CellCoordinates (x, y));\n\n                // Mark that camera needs setup\n                mCamPositionSet=false;\n            }\n        }\n        else if (hint[0]=='r')\n        {\n            // syntax r:ref#number (e.g. r:ref#100)\n            char ignore;\n\n            std::istringstream stream (hint.c_str());\n            if (stream >> ignore) // ignore r\n            {\n                char ignore1; // : or ;\n\n                std::string refCode; // ref#number (e.g. ref#100)\n\n                while (stream >> ignore1 >> refCode) {}\n\n                //Find out cell coordinate\n                CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (\n                    *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References));\n                int cellColumn = references.findColumnIndex(CSMWorld::Columns::ColumnId_Cell);\n                QVariant cell = references.data(references.getModelIndex(refCode, cellColumn)).value<QVariant>();\n                QString cellqs = cell.toString();\n                std::istringstream streamCellCoord (cellqs.toStdString().c_str());\n\n                if (streamCellCoord >> ignore) //ignore #\n                {\n                    // Current coordinate\n                    int x, y;\n\n                    // Loop through all the coordinates to add them to selection\n                    while (streamCellCoord >> x >> y)\n                        selection.add (CSMWorld::CellCoordinates (x, y));\n\n                    // Mark that camera needs setup\n                    mCamPositionSet=false;\n                }\n            }\n        }\n\n        setCellSelection (selection);\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSelection& selection)\n{\n    mSelection = selection;\n\n    if (adjustCells())\n        flagAsModified();\n\n    emit cellSelectionChanged (mSelection);\n}\n\nconst CSMWorld::CellSelection& CSVRender::PagedWorldspaceWidget::getCellSelection() const\n{\n    return mSelection;\n}\n\nstd::pair< int, int > CSVRender::PagedWorldspaceWidget::getCoordinatesFromId (const std::string& record) const\n{\n    std::istringstream stream (record.c_str());\n    char ignore;\n    int x, y;\n    stream >> ignore >> x >> y;\n    return std::make_pair(x, y);\n}\n\nbool CSVRender::PagedWorldspaceWidget::handleDrop (\n    const std::vector< CSMWorld::UniversalId >& universalIdData, DropType type)\n{\n    if (WorldspaceWidget::handleDrop (universalIdData, type))\n        return true;\n\n    if (type!=Type_CellsExterior)\n        return false;\n\n    bool selectionChanged = false;\n    for (unsigned i = 0; i < universalIdData.size(); ++i)\n    {\n        std::pair<int, int> coordinates(getCoordinatesFromId(universalIdData[i].getId()));\n        if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second)))\n        {\n            selectionChanged = true;\n        }\n    }\n    if (selectionChanged)\n    {\n        if (adjustCells())\n            flagAsModified();\n\n        emit cellSelectionChanged(mSelection);\n    }\n\n    return true;\n}\n\nCSVRender::WorldspaceWidget::dropRequirments CSVRender::PagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::DropType type) const\n{\n    dropRequirments requirements = WorldspaceWidget::getDropRequirements (type);\n\n    if (requirements!=ignored)\n        return requirements;\n\n    switch (type)\n    {\n        case Type_CellsExterior:\n            return canHandle;\n\n        case Type_CellsInterior:\n            return needUnpaged;\n\n        default:\n            return ignored;\n    }\n}\n\nunsigned int CSVRender::PagedWorldspaceWidget::getVisibilityMask() const\n{\n    return WorldspaceWidget::getVisibilityMask() | mControlElements->getSelectionMask();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::clearSelection (int elementMask)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();\n        iter!=mCells.end(); ++iter)\n        iter->second->setSelection (elementMask, Cell::Selection_Clear);\n\n    flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::invertSelection (int elementMask)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();\n        iter!=mCells.end(); ++iter)\n        iter->second->setSelection (elementMask, Cell::Selection_Invert);\n\n    flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::selectAll (int elementMask)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();\n        iter!=mCells.end(); ++iter)\n        iter->second->setSelection (elementMask, Cell::Selection_All);\n\n    flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::selectAllWithSameParentId (int elementMask)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();\n        iter!=mCells.end(); ++iter)\n        iter->second->selectAllWithSameParentId (elementMask);\n\n    flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode)\n{\n    for (auto& cell : mCells)\n    {\n        cell.second->selectInsideCube (pointA, pointB, dragMode);\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode)\n{\n    for (auto& cell : mCells)\n    {\n        cell.second->selectWithinDistance (point, distance, dragMode);\n    }\n}\n\nstd::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const\n{\n    CSMWorld::CellCoordinates cellCoordinates (\n        static_cast<int> (std::floor (point.x() / Constants::CellSizeInUnits)),\n        static_cast<int> (std::floor (point.y() / Constants::CellSizeInUnits)));\n\n    return cellCoordinates.getId (mWorldspace);\n}\n\nCSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const\n{\n    CSMWorld::CellCoordinates coords(\n        static_cast<int> (std::floor (point.x() / Constants::CellSizeInUnits)),\n        static_cast<int> (std::floor (point.y() / Constants::CellSizeInUnits)));\n\n    std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator searchResult = mCells.find(coords);\n    if (searchResult != mCells.end())\n        return searchResult->second;\n    else\n        return nullptr;\n}\n\nCSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const CSMWorld::CellCoordinates& coords) const\n{\n    std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator searchResult = mCells.find(coords);\n    if (searchResult != mCells.end())\n        return searchResult->second;\n    else\n        return nullptr;\n}\n\nvoid CSVRender::PagedWorldspaceWidget::setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height)\n{\n    std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);\n    if (searchResult != mCells.end())\n        searchResult->second->setAlteredHeight(inCellX, inCellY, height);\n}\n\nfloat* CSVRender::PagedWorldspaceWidget::getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY)\n{\n    std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);\n    if (searchResult != mCells.end())\n        return searchResult->second->getAlteredHeight(inCellX, inCellY);\n    return nullptr;\n}\n\nvoid CSVRender::PagedWorldspaceWidget::resetAllAlteredHeights()\n{\n    for (const auto& cell : mCells)\n        cell.second->resetAlteredHeights();\n}\n\nstd::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::PagedWorldspaceWidget::getSelection (\n    unsigned int elementMask) const\n{\n    std::vector<osg::ref_ptr<CSVRender::TagBase> > result;\n\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::const_iterator iter = mCells.begin();\n        iter!=mCells.end(); ++iter)\n    {\n        std::vector<osg::ref_ptr<CSVRender::TagBase> > cellResult =\n            iter->second->getSelection (elementMask);\n\n        result.insert (result.end(), cellResult.begin(), cellResult.end());\n    }\n\n    return result;\n}\n\nstd::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::PagedWorldspaceWidget::getEdited (\n    unsigned int elementMask) const\n{\n    std::vector<osg::ref_ptr<CSVRender::TagBase> > result;\n\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::const_iterator iter = mCells.begin();\n        iter!=mCells.end(); ++iter)\n    {\n        std::vector<osg::ref_ptr<CSVRender::TagBase> > cellResult =\n            iter->second->getEdited (elementMask);\n\n        result.insert (result.end(), cellResult.begin(), cellResult.end());\n    }\n\n    return result;\n}\n\nvoid CSVRender::PagedWorldspaceWidget::setSubMode (int subMode, unsigned int elementMask)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::const_iterator iter = mCells.begin();\n        iter!=mCells.end(); ++iter)\n        iter->second->setSubMode (subMode, elementMask);\n}\n\nvoid CSVRender::PagedWorldspaceWidget::reset (unsigned int elementMask)\n{\n    for (std::map<CSMWorld::CellCoordinates, Cell *>::const_iterator iter = mCells.begin();\n        iter!=mCells.end(); ++iter)\n        iter->second->reset (elementMask);\n}\n\nCSVWidget::SceneToolToggle2 *CSVRender::PagedWorldspaceWidget::makeControlVisibilitySelector (\n    CSVWidget::SceneToolbar *parent)\n{\n    mControlElements = new CSVWidget::SceneToolToggle2 (parent,\n        \"Controls & Guides Visibility\", \":scenetoolbar/scene-view-marker-c\", \":scenetoolbar/scene-view-marker-\");\n\n    mControlElements->addButton (1, Mask_CellMarker, \"Cell Marker\");\n    mControlElements->addButton (2, Mask_CellArrow, \"Cell Arrows\");\n    mControlElements->addButton (4, Mask_CellBorder, \"Cell Border\");\n\n    mControlElements->setSelectionMask (0xffffffff);\n\n    connect (mControlElements, SIGNAL (selectionChanged()),\n        this, SLOT (elementSelectionChanged()));\n\n    return mControlElements;\n}\n\nvoid CSVRender::PagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    /// \\todo check if no selected cell is affected and do not update, if that is the case\n    if (adjustCells())\n        flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::cellRemoved (const QModelIndex& parent, int start,\n    int end)\n{\n    if (adjustCells())\n        flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int start,\n    int end)\n{\n    /// \\todo check if no selected cell is affected and do not update, if that is the case\n    if (adjustCells())\n        flagAsModified();\n}\n\nvoid CSVRender::PagedWorldspaceWidget::assetTablesChanged()\n{\n    std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();\n    for ( ; iter != mCells.end(); ++iter)\n    {\n        iter->second->reloadAssets();\n    }\n}\n\nvoid CSVRender::PagedWorldspaceWidget::loadCameraCell()\n{\n    addCellToSceneFromCamera(0, 0);\n}\n\nvoid CSVRender::PagedWorldspaceWidget::loadEastCell()\n{\n    addCellToSceneFromCamera(1, 0);\n}\n\nvoid CSVRender::PagedWorldspaceWidget::loadNorthCell()\n{\n    addCellToSceneFromCamera(0, 1);\n}\n\nvoid CSVRender::PagedWorldspaceWidget::loadWestCell()\n{\n    addCellToSceneFromCamera(-1, 0);\n}\n\nvoid CSVRender::PagedWorldspaceWidget::loadSouthCell()\n{\n    addCellToSceneFromCamera(0, -1);\n}\n"
  },
  {
    "path": "apps/opencs/view/render/pagedworldspacewidget.hpp",
    "content": "#ifndef OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H\n#define OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H\n\n#include <map>\n\n#include \"../../model/world/cellselection.hpp\"\n\n#include \"worldspacewidget.hpp\"\n#include \"cell.hpp\"\n#include \"instancedragmodes.hpp\"\n\nnamespace CSVWidget\n{\n   class SceneToolToggle;\n   class SceneToolToggle2;\n}\n\nnamespace CSVRender\n{\n    class TextOverlay;\n    class OverlayMask;\n\n    class PagedWorldspaceWidget : public WorldspaceWidget\n    {\n            Q_OBJECT\n\n            CSMDoc::Document& mDocument;\n            CSMWorld::CellSelection mSelection;\n            std::map<CSMWorld::CellCoordinates, Cell *> mCells;\n            std::string mWorldspace;\n            CSVWidget::SceneToolToggle2 *mControlElements;\n            bool mDisplayCellCoord;\n\n        private:\n\n            std::pair<int, int> getCoordinatesFromId(const std::string& record) const;\n\n            /// Bring mCells into sync with mSelection again.\n            ///\n            /// \\return Any cells added or removed?\n            bool adjustCells();\n\n            void referenceableDataChanged (const QModelIndex& topLeft,\n                const QModelIndex& bottomRight) override;\n\n            void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) override;\n\n            void referenceableAdded (const QModelIndex& index, int start, int end) override;\n\n            void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) override;\n\n            void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) override;\n\n            void referenceAdded (const QModelIndex& index, int start, int end) override;\n\n            void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) override;\n\n            void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end) override;\n\n            void pathgridAdded (const QModelIndex& parent, int start, int end) override;\n\n            std::string getStartupInstruction() override;\n\n            /// \\note Does not update the view or any cell marker\n            void addCellToScene (const CSMWorld::CellCoordinates& coordinates);\n\n            /// \\note Does not update the view or any cell marker\n            ///\n            /// \\note Calling this function for a cell that is not in the selection is a no-op.\n            void removeCellFromScene (const CSMWorld::CellCoordinates& coordinates);\n\n            /// \\note Does not update the view or any cell marker\n            void addCellSelection (int x, int y);\n\n            /// \\note Does not update the view or any cell marker\n            void moveCellSelection (int x, int y);\n\n            void addCellToSceneFromCamera (int offsetX, int offsetY);\n\n        public:\n\n            PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document);\n            ///< \\note Sets the cell area selection to an invalid value to indicate that currently\n            /// no cells are displayed. The cells to be displayed will be specified later through\n            /// hint system.\n\n            virtual ~PagedWorldspaceWidget();\n\n            /// Decodes the the hint string to set of cell that are rendered.\n            void useViewHint (const std::string& hint) override;\n\n            void setCellSelection(const CSMWorld::CellSelection& selection);\n\n            const CSMWorld::CellSelection& getCellSelection() const;\n\n            /// \\return Drop handled?\n            bool handleDrop (const std::vector<CSMWorld::UniversalId>& data,\n                DropType type) override;\n\n            dropRequirments getDropRequirements(DropType type) const override;\n\n            /// \\attention The created tool is not added to the toolbar (via addTool). Doing\n            /// that is the responsibility of the calling function.\n            virtual CSVWidget::SceneToolToggle2 *makeControlVisibilitySelector (\n                CSVWidget::SceneToolbar *parent);\n\n            unsigned int getVisibilityMask() const override;\n\n            /// \\param elementMask Elements to be affected by the clear operation\n            void clearSelection (int elementMask) override;\n\n            /// \\param elementMask Elements to be affected by the select operation\n            void invertSelection (int elementMask) override;\n\n            /// \\param elementMask Elements to be affected by the select operation\n            void selectAll (int elementMask) override;\n\n            // Select everything that references the same ID as at least one of the elements\n            // already selected\n            //\n            /// \\param elementMask Elements to be affected by the select operation\n            void selectAllWithSameParentId (int elementMask) override;\n\n            void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) override;\n\n            void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) override;\n\n            std::string getCellId (const osg::Vec3f& point) const override;\n\n            Cell* getCell(const osg::Vec3d& point) const override;\n\n            Cell* getCell(const CSMWorld::CellCoordinates& coords) const override;\n\n            void setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height);\n\n            float* getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY);\n\n            void resetAllAlteredHeights();\n\n            std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)\n                const override;\n\n            std::vector<osg::ref_ptr<TagBase> > getEdited (unsigned int elementMask)\n                const override;\n\n            void setSubMode (int subMode, unsigned int elementMask) override;\n\n            /// Erase all overrides and restore the visual representation to its true state.\n            void reset (unsigned int elementMask) override;\n\n        protected:\n\n            void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool) override;\n\n            void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool) override;\n\n            void handleInteractionPress (const WorldspaceHitResult& hit, InteractionType type) override;\n\n        signals:\n\n            void cellSelectionChanged (const CSMWorld::CellSelection& selection);\n\n        private slots:\n\n            virtual void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            virtual void cellRemoved (const QModelIndex& parent, int start, int end);\n\n            virtual void cellAdded (const QModelIndex& index, int start, int end);\n\n            virtual void landDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight);\n            virtual void landAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n            virtual void landAdded (const QModelIndex& parent, int start, int end);\n\n            virtual void landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight);\n            virtual void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n            virtual void landTextureAdded (const QModelIndex& parent, int start, int end);\n\n            void assetTablesChanged ();\n\n            void loadCameraCell();\n\n            void loadEastCell();\n\n            void loadNorthCell();\n\n            void loadWestCell();\n\n            void loadSouthCell();\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/pathgrid.cpp",
    "content": "#include \"pathgrid.hpp\"\n\n#include <algorithm>\n\n#include <osg/Array>\n#include <osg/Geode>\n#include <osg/Geometry>\n#include <osg/Group>\n#include <osg/PositionAttitudeTransform>\n#include <osg/Vec3>\n\n#include <components/sceneutil/pathgridutil.hpp>\n\n#include \"../../model/world/cell.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idtree.hpp\"\n\nnamespace CSVRender\n{\n    class PathgridNodeCallback : public osg::NodeCallback\n    {\n        public:\n\n            void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n            {\n                PathgridTag* tag = static_cast<PathgridTag*>(node->getUserData());\n                tag->getPathgrid()->update();\n            }\n    };\n\n    PathgridTag::PathgridTag(Pathgrid* pathgrid)\n        : TagBase(Mask_Pathgrid), mPathgrid(pathgrid)\n    {\n    }\n\n    Pathgrid* PathgridTag::getPathgrid() const\n    {\n        return mPathgrid;\n    }\n\n    QString PathgridTag::getToolTip(bool hideBasics) const\n    {\n        QString text(\"Pathgrid: \");\n        text += mPathgrid->getId().c_str();\n\n        return text;\n    }\n\n    Pathgrid::Pathgrid(CSMWorld::Data& data, osg::Group* parent, const std::string& pathgridId,\n        const CSMWorld::CellCoordinates& coordinates)\n        : mData(data)\n        , mPathgridCollection(mData.getPathgrids())\n        , mId(pathgridId)\n        , mCoords(coordinates)\n        , mInterior(false)\n        , mDragOrigin(0)\n        , mChangeGeometry(true)\n        , mRemoveGeometry(false)\n        , mUseOffset(true)\n        , mParent(parent)\n        , mPathgridGeometry(nullptr)\n        , mDragGeometry(nullptr)\n        , mTag(new PathgridTag(this))\n    {\n        const float CoordScalar = ESM::Land::REAL_SIZE;\n\n        mBaseNode = new osg::PositionAttitudeTransform ();\n        mBaseNode->setPosition(osg::Vec3f(mCoords.getX() * CoordScalar, mCoords.getY() * CoordScalar, 0.f));\n        mBaseNode->setUserData(mTag);\n        mBaseNode->setUpdateCallback(new PathgridNodeCallback());\n        mBaseNode->setNodeMask(Mask_Pathgrid);\n        mParent->addChild(mBaseNode);\n\n        mPathgridGeode = new osg::Geode();\n        mBaseNode->addChild(mPathgridGeode);\n\n        recreateGeometry();\n\n        int index = mData.getCells().searchId(mId);\n        if (index != -1)\n        {\n            const CSMWorld::Cell& cell = mData.getCells().getRecord(index).get();\n            mInterior = cell.mData.mFlags & ESM::Cell::Interior;\n        }\n    }\n\n    Pathgrid::~Pathgrid()\n    {\n        mParent->removeChild(mBaseNode);\n    }\n\n    const CSMWorld::CellCoordinates& Pathgrid::getCoordinates() const\n    {\n        return mCoords;\n    }\n\n    const std::string& Pathgrid::getId() const\n    {\n        return mId;\n    }\n\n    bool Pathgrid::isSelected() const\n    {\n        return !mSelected.empty();\n    }\n\n    const Pathgrid::NodeList& Pathgrid::getSelected() const\n    {\n        return mSelected;\n    }\n\n    void Pathgrid::selectAll()\n    {\n        mSelected.clear();\n\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            for (unsigned short i = 0; i < static_cast<unsigned short>(source->mPoints.size()); ++i)\n                mSelected.push_back(i);\n\n            createSelectedGeometry(*source);\n        }\n        else\n        {\n            removeSelectedGeometry();\n        }\n    }\n\n    void Pathgrid::toggleSelected(unsigned short node)\n    {\n        NodeList::iterator searchResult = std::find(mSelected.begin(), mSelected.end(), node);\n        if (searchResult != mSelected.end())\n        {\n            mSelected.erase(searchResult);\n        }\n        else\n        {\n            mSelected.push_back(node);\n        }\n\n        createSelectedGeometry();\n    }\n\n    void Pathgrid::invertSelected()\n    {\n        NodeList temp = NodeList(mSelected);\n        mSelected.clear();\n\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            for (unsigned short i = 0; i < static_cast<unsigned short>(source->mPoints.size()); ++i)\n            {\n                if (std::find(temp.begin(), temp.end(), i) == temp.end())\n                    mSelected.push_back(i);\n            }\n\n            createSelectedGeometry(*source);\n        }\n        else\n        {\n            removeSelectedGeometry();\n        }\n    }\n\n    void Pathgrid::clearSelected()\n    {\n        mSelected.clear();\n        removeSelectedGeometry();\n    }\n\n    void Pathgrid::moveSelected(const osg::Vec3d& offset)\n    {\n        mUseOffset = true;\n        mMoveOffset += offset;\n\n        recreateGeometry();\n    }\n\n    void Pathgrid::setDragOrigin(unsigned short node)\n    {\n        mDragOrigin = node;\n    }\n\n    void Pathgrid::setDragEndpoint(unsigned short node)\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            const CSMWorld::Pathgrid::Point& pointA = source->mPoints[mDragOrigin];\n            const CSMWorld::Pathgrid::Point& pointB = source->mPoints[node];\n\n            osg::Vec3f start = osg::Vec3f(pointA.mX, pointA.mY, pointA.mZ + SceneUtil::DiamondHalfHeight);\n            osg::Vec3f end = osg::Vec3f(pointB.mX, pointB.mY, pointB.mZ + SceneUtil::DiamondHalfHeight);\n\n            createDragGeometry(start, end, true);\n        }\n    }\n\n    void Pathgrid::setDragEndpoint(const osg::Vec3d& pos)\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            const CSMWorld::Pathgrid::Point& point = source->mPoints[mDragOrigin];\n\n            osg::Vec3f start = osg::Vec3f(point.mX, point.mY, point.mZ + SceneUtil::DiamondHalfHeight);\n            osg::Vec3f end = pos - mBaseNode->getPosition();\n            createDragGeometry(start, end, false);\n        }\n    }\n\n    void Pathgrid::resetIndicators()\n    {\n        mUseOffset = false;\n        mMoveOffset.set(0, 0, 0);\n\n        mPathgridGeode->removeDrawable(mDragGeometry);\n        mDragGeometry = nullptr;\n    }\n\n    void Pathgrid::applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos)\n    {\n        CSMWorld::IdTree* model = &dynamic_cast<CSMWorld::IdTree&>(*mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids));\n\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            osg::Vec3d localCoords = worldPos - mBaseNode->getPosition();\n\n            int posX = clampToCell(static_cast<int>(localCoords.x()));\n            int posY = clampToCell(static_cast<int>(localCoords.y()));\n            int posZ = clampToCell(static_cast<int>(localCoords.z()));\n\n            int recordIndex = mPathgridCollection.getIndex (mId);\n            int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints);\n\n            int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n                CSMWorld::Columns::ColumnId_PathgridPosX);\n\n            int posYColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n                CSMWorld::Columns::ColumnId_PathgridPosY);\n\n            int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n                CSMWorld::Columns::ColumnId_PathgridPosZ);\n\n            QModelIndex parent = model->index(recordIndex, parentColumn);\n            int row = static_cast<int>(source->mPoints.size());\n\n            // Add node to end of list\n            commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn));\n            commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), posX));\n            commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), posY));\n            commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), posZ));\n        }\n        else\n        {\n            int index = mPathgridCollection.searchId(mId);\n            if (index == -1)\n            {\n                // Does not exist\n                commands.push(new CSMWorld::CreatePathgridCommand(*model, mId));\n            }\n            else\n            {\n                source = &mPathgridCollection.getRecord(index).get();\n\n                // Deleted, so revert and remove all data\n                commands.push(new CSMWorld::RevertCommand(*model, mId));\n\n                int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints);\n                for (int row = source->mPoints.size() - 1; row >= 0; --row)\n                {\n                    commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, row, parentColumn));\n                }\n\n                parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges);\n                for (int row = source->mEdges.size() - 1; row >= 0; --row)\n                {\n                    commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, row, parentColumn));\n                }\n            }\n        }\n    }\n\n    void Pathgrid::applyPosition(CSMWorld::CommandMacro& commands)\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            osg::Vec3d localCoords = mMoveOffset;\n\n            int offsetX = static_cast<int>(localCoords.x());\n            int offsetY = static_cast<int>(localCoords.y());\n            int offsetZ = static_cast<int>(localCoords.z());\n\n            QAbstractItemModel* model = mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids);\n\n            int recordIndex = mPathgridCollection.getIndex(mId);\n            int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints);\n\n            int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n                CSMWorld::Columns::ColumnId_PathgridPosX);\n\n            int posYColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n                CSMWorld::Columns::ColumnId_PathgridPosY);\n\n            int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n                CSMWorld::Columns::ColumnId_PathgridPosZ);\n\n            QModelIndex parent = model->index(recordIndex, parentColumn);\n\n            for (size_t i = 0; i < mSelected.size(); ++i)\n            {\n                const CSMWorld::Pathgrid::Point& point = source->mPoints[mSelected[i]];\n                int row = static_cast<int>(mSelected[i]);\n\n                commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent),\n                    clampToCell(point.mX + offsetX)));\n\n                commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent),\n                    clampToCell(point.mY + offsetY)));\n\n                commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent),\n                    clampToCell(point.mZ + offsetZ)));\n            }\n        }\n    }\n\n    void Pathgrid::applyEdge(CSMWorld::CommandMacro& commands, unsigned short node1, unsigned short node2)\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            addEdge(commands, *source, node1, node2);\n        }\n    }\n\n    void Pathgrid::applyEdges(CSMWorld::CommandMacro& commands, unsigned short node)\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            for (size_t i = 0; i < mSelected.size(); ++i)\n            {\n                addEdge(commands, *source, node, mSelected[i]);\n            }\n        }\n    }\n\n    void Pathgrid::applyRemoveNodes(CSMWorld::CommandMacro& commands)\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            CSMWorld::IdTree* model = &dynamic_cast<CSMWorld::IdTree&>(*mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids));\n\n            // Want to remove nodes from end of list first\n            std::sort(mSelected.begin(), mSelected.end(), std::greater<int>());\n\n            int recordIndex = mPathgridCollection.getIndex(mId);\n            int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints);\n\n            for (std::vector<unsigned short>::iterator row = mSelected.begin(); row != mSelected.end(); ++row)\n            {\n                commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, static_cast<int>(*row), parentColumn));\n            }\n\n            // Fix/remove edges\n            std::set<int, std::greater<int> > edgeRowsToRemove;\n\n            parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges);\n\n            int edge0Column = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n                CSMWorld::Columns::ColumnId_PathgridEdge0);\n\n            int edge1Column = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n                CSMWorld::Columns::ColumnId_PathgridEdge1);\n\n            QModelIndex parent = model->index(recordIndex, parentColumn);\n\n            for (size_t edge = 0; edge < source->mEdges.size(); ++edge)\n            {\n                int adjustment0 = 0;\n                int adjustment1 = 0;\n\n                // Determine necessary adjustment\n                for (std::vector<unsigned short>::iterator point = mSelected.begin(); point != mSelected.end(); ++point)\n                {\n                    if (source->mEdges[edge].mV0 == *point || source->mEdges[edge].mV1 == *point)\n                    {\n                        edgeRowsToRemove.insert(static_cast<int>(edge));\n\n                        adjustment0 = 0; // No need to adjust, its getting removed\n                        adjustment1 = 0;\n                        break;\n                    }\n\n                    if (source->mEdges[edge].mV0 > *point)\n                        --adjustment0;\n\n                    if (source->mEdges[edge].mV1 > *point)\n                        --adjustment1;\n                }\n\n                if (adjustment0 != 0)\n                {\n                    int adjustedEdge = source->mEdges[edge].mV0 + adjustment0;\n                    commands.push(new CSMWorld::ModifyCommand(*model, model->index(edge, edge0Column, parent),\n                        adjustedEdge));\n                }\n\n                if (adjustment1 != 0)\n                {\n                    int adjustedEdge = source->mEdges[edge].mV1 + adjustment1;\n                    commands.push(new CSMWorld::ModifyCommand(*model, model->index(edge, edge1Column, parent),\n                        adjustedEdge));\n                }\n            }\n\n            std::set<int, std::greater<int> >::iterator row;\n            for (row = edgeRowsToRemove.begin(); row != edgeRowsToRemove.end(); ++row)\n            {\n                commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, *row, parentColumn));\n            }\n        }\n\n        clearSelected();\n    }\n\n    void Pathgrid::applyRemoveEdges(CSMWorld::CommandMacro& commands)\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            // Want to remove from end of row first\n            std::set<int, std::greater<int> > rowsToRemove;\n            for (size_t i = 0; i <= mSelected.size(); ++i)\n            {\n                for (size_t j = i + 1; j < mSelected.size(); ++j)\n                {\n                    int row = edgeExists(*source, mSelected[i], mSelected[j]);\n                    if (row != -1)\n                    {\n                        rowsToRemove.insert(row);\n                    }\n\n                    row = edgeExists(*source, mSelected[j], mSelected[i]);\n                    if (row != -1)\n                    {\n                        rowsToRemove.insert(row);\n                    }\n                }\n            }\n\n            CSMWorld::IdTree* model = &dynamic_cast<CSMWorld::IdTree&>(*mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids));\n            int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges);\n\n            std::set<int, std::greater<int> >::iterator row;\n            for (row = rowsToRemove.begin(); row != rowsToRemove.end(); ++row)\n            {\n                commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, *row, parentColumn));\n            }\n        }\n    }\n\n    osg::ref_ptr<PathgridTag> Pathgrid::getTag() const\n    {\n        return mTag;\n    }\n\n    void Pathgrid::recreateGeometry()\n    {\n        mChangeGeometry = true;\n    }\n\n    void Pathgrid::removeGeometry()\n    {\n        mRemoveGeometry = true;\n    }\n\n    void Pathgrid::update()\n    {\n        if (mRemoveGeometry)\n        {\n            removePathgridGeometry();\n            removeSelectedGeometry();\n        }\n        else if (mChangeGeometry)\n        {\n            createGeometry();\n        }\n\n        mChangeGeometry = false;\n        mRemoveGeometry = false;\n    }\n\n    void Pathgrid::createGeometry()\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            CSMWorld::Pathgrid temp;\n            if (mUseOffset)\n            {\n                temp = *source;\n\n                for (NodeList::iterator it = mSelected.begin(); it != mSelected.end(); ++it)\n                {\n                    temp.mPoints[*it].mX += mMoveOffset.x();\n                    temp.mPoints[*it].mY += mMoveOffset.y();\n                    temp.mPoints[*it].mZ += mMoveOffset.z();\n                }\n\n                source = &temp;\n            }\n\n            removePathgridGeometry();\n            mPathgridGeometry = SceneUtil::createPathgridGeometry(*source);\n            mPathgridGeode->addDrawable(mPathgridGeometry);\n\n            createSelectedGeometry(*source);\n        }\n        else\n        {\n            removePathgridGeometry();\n            removeSelectedGeometry();\n        }\n    }\n\n    void Pathgrid::createSelectedGeometry()\n    {\n        const CSMWorld::Pathgrid* source = getPathgridSource();\n        if (source)\n        {\n            createSelectedGeometry(*source);\n        }\n        else\n        {\n            removeSelectedGeometry();\n        }\n    }\n\n    void Pathgrid::createSelectedGeometry(const CSMWorld::Pathgrid& source)\n    {\n        removeSelectedGeometry();\n\n        mSelectedGeometry = SceneUtil::createPathgridSelectedWireframe(source, mSelected);\n        mPathgridGeode->addDrawable(mSelectedGeometry);\n    }\n\n    void Pathgrid::removePathgridGeometry()\n    {\n        if (mPathgridGeometry)\n        {\n            mPathgridGeode->removeDrawable(mPathgridGeometry);\n            mPathgridGeometry = nullptr;\n        }\n    }\n\n    void Pathgrid::removeSelectedGeometry()\n    {\n        if (mSelectedGeometry)\n        {\n            mPathgridGeode->removeDrawable(mSelectedGeometry);\n            mSelectedGeometry = nullptr;\n        }\n    }\n\n    void Pathgrid::createDragGeometry(const osg::Vec3f& start, const osg::Vec3f& end, bool valid)\n    {\n        if (mDragGeometry)\n            mPathgridGeode->removeDrawable(mDragGeometry);\n\n        mDragGeometry = new osg::Geometry();\n\n        osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(2);\n        osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);\n        osg::ref_ptr<osg::DrawElementsUShort> indices = new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, 2);\n\n        (*vertices)[0] = start;\n        (*vertices)[1] = end;\n\n        if (valid)\n        {\n            (*colors)[0] = osg::Vec4f(0.91f, 0.66f, 1.f, 1.f);\n        }\n        else\n        {\n            (*colors)[0] = osg::Vec4f(1.f, 0.f, 0.f, 1.f);\n        }\n\n        indices->setElement(0, 0);\n        indices->setElement(1, 1);\n\n        mDragGeometry->setVertexArray(vertices);\n        mDragGeometry->setColorArray(colors, osg::Array::BIND_OVERALL);\n        mDragGeometry->addPrimitiveSet(indices);\n        mDragGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n\n        mPathgridGeode->addDrawable(mDragGeometry);\n    }\n\n    const CSMWorld::Pathgrid* Pathgrid::getPathgridSource()\n    {\n        int index = mPathgridCollection.searchId(mId);\n        if (index != -1 && !mPathgridCollection.getRecord(index).isDeleted())\n        {\n            return &mPathgridCollection.getRecord(index).get();\n        }\n\n        return nullptr;\n    }\n\n    int Pathgrid::edgeExists(const CSMWorld::Pathgrid& source, unsigned short node1, unsigned short node2)\n    {\n        for (size_t i = 0; i < source.mEdges.size(); ++i)\n        {\n            if (source.mEdges[i].mV0 == node1 && source.mEdges[i].mV1 == node2)\n                return static_cast<int>(i);\n        }\n\n        return -1;\n    }\n\n    void Pathgrid::addEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1,\n        unsigned short node2)\n    {\n        CSMWorld::IdTree* model = &dynamic_cast<CSMWorld::IdTree&>(*mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids));\n\n        int recordIndex = mPathgridCollection.getIndex(mId);\n        int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges);\n\n        int edge0Column = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n            CSMWorld::Columns::ColumnId_PathgridEdge0);\n\n        int edge1Column = mPathgridCollection.searchNestedColumnIndex(parentColumn,\n            CSMWorld::Columns::ColumnId_PathgridEdge1);\n\n        QModelIndex parent = model->index(recordIndex, parentColumn);\n        int row = static_cast<int>(source.mEdges.size());\n\n        if (edgeExists(source, node1, node2) == -1)\n        {\n            commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn));\n            commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge0Column, parent), node1));\n            commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node2));\n            ++row;\n        }\n\n        if (edgeExists(source, node2, node1) == -1)\n        {\n            commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn));\n            commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge0Column, parent), node2));\n            commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node1));\n        }\n    }\n\n    int Pathgrid::clampToCell(int v)\n    {\n        const int CellExtent = ESM::Land::REAL_SIZE;\n\n        if (mInterior)\n            return v;\n        else if (v > CellExtent)\n            return CellExtent;\n        else if (v < 0)\n            return 0;\n        else\n            return v;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/pathgrid.hpp",
    "content": "#ifndef CSV_RENDER_PATHGRID_H\n#define CSV_RENDER_PATHGRID_H\n\n#include <vector>\n\n#include <QString>\n#include <osg/ref_ptr>\n#include <osg/Vec3d>\n\n#include \"../../model/world/cellcoordinates.hpp\"\n#include \"../../model/world/idcollection.hpp\"\n#include \"../../model/world/subcellcollection.hpp\"\n\n#include \"tagbase.hpp\"\n\nnamespace osg\n{\n    class Geode;\n    class Geometry;\n    class Group;\n    class PositionAttitudeTransform;\n}\n\nnamespace CSMWorld\n{\n    class CommandMacro;\n    class Data;\n    struct Pathgrid;\n}\n\nnamespace CSVRender\n{\n    class Pathgrid;\n\n    class PathgridTag : public TagBase\n    {\n        public:\n\n            PathgridTag (Pathgrid* pathgrid);\n\n            Pathgrid* getPathgrid () const;\n\n            QString getToolTip (bool hideBasics) const override;\n\n        private:\n\n            Pathgrid* mPathgrid;\n    };\n\n    class Pathgrid\n    {\n        public:\n\n            typedef std::vector<unsigned short> NodeList;\n\n            Pathgrid(CSMWorld::Data& data, osg::Group* parent, const std::string& pathgridId,\n                const CSMWorld::CellCoordinates& coordinates);\n\n            ~Pathgrid();\n\n            const CSMWorld::CellCoordinates& getCoordinates() const;\n            const std::string& getId() const;\n\n            bool isSelected() const;\n            const NodeList& getSelected() const;\n            void selectAll();\n            void toggleSelected(unsigned short node); // Adds to end of vector\n            void invertSelected();\n            void clearSelected();\n\n            void moveSelected(const osg::Vec3d& offset);\n            void setDragOrigin(unsigned short node);\n            void setDragEndpoint(unsigned short node);\n            void setDragEndpoint(const osg::Vec3d& pos);\n\n            void resetIndicators();\n\n            void applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos);\n            void applyPosition(CSMWorld::CommandMacro& commands);\n            void applyEdge(CSMWorld::CommandMacro& commands, unsigned short node1, unsigned short node2);\n            void applyEdges(CSMWorld::CommandMacro& commands, unsigned short node);\n            void applyRemoveNodes(CSMWorld::CommandMacro& commands);\n            void applyRemoveEdges(CSMWorld::CommandMacro& commands);\n\n            osg::ref_ptr<PathgridTag> getTag() const;\n\n            void recreateGeometry();\n            void removeGeometry();\n\n            void update();\n\n        private:\n\n            CSMWorld::Data& mData;\n            CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& mPathgridCollection;\n            std::string mId;\n            CSMWorld::CellCoordinates mCoords;\n            bool mInterior;\n\n            NodeList mSelected;\n            osg::Vec3d mMoveOffset;\n            unsigned short mDragOrigin;\n\n            bool mChangeGeometry;\n            bool mRemoveGeometry;\n            bool mUseOffset;\n\n            osg::Group* mParent;\n            osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;\n            osg::ref_ptr<osg::Geode> mPathgridGeode;\n            osg::ref_ptr<osg::Geometry> mPathgridGeometry;\n            osg::ref_ptr<osg::Geometry> mSelectedGeometry;\n            osg::ref_ptr<osg::Geometry> mDragGeometry;\n\n            osg::ref_ptr<PathgridTag> mTag;\n\n            void createGeometry();\n            void createSelectedGeometry();\n            void createSelectedGeometry(const CSMWorld::Pathgrid& source);\n            void removePathgridGeometry();\n            void removeSelectedGeometry();\n\n            void createDragGeometry(const osg::Vec3f& start, const osg::Vec3f& end, bool valid);\n\n            const CSMWorld::Pathgrid* getPathgridSource();\n\n            int edgeExists(const CSMWorld::Pathgrid& source, unsigned short node1, unsigned short node2);\n            void addEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1,\n                unsigned short node2);\n            void removeEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1,\n                unsigned short node2);\n\n            int clampToCell(int v);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/pathgridmode.cpp",
    "content": "#include \"pathgridmode.hpp\"\n\n#include <QMenu>\n#include <QPoint>\n\n#include <components/sceneutil/pathgridutil.hpp>\n\n#include \"../../model/prefs/state.hpp\"\n\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n\n#include \"../widget/scenetoolbar.hpp\"\n\n#include \"cell.hpp\"\n#include \"mask.hpp\"\n#include \"pathgrid.hpp\"\n#include \"pathgridselectionmode.hpp\"\n#include \"worldspacewidget.hpp\"\n\nnamespace CSVRender\n{\n    PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent)\n        : EditMode(worldspaceWidget, QIcon(\":placeholder\"), Mask_Pathgrid | Mask_Terrain | Mask_Reference,\n            getTooltip(), parent)\n        , mDragMode(DragMode_None)\n        , mFromNode(0)\n        , mSelectionMode(nullptr)\n    {\n    }\n\n    QString PathgridMode::getTooltip()\n    {\n        return QString(\n            \"Pathgrid editing\"\n            \"<ul><li>Press {scene-edit-primary} to add a node to the cursor location</li>\"\n            \"<li>Press {scene-edit-secondary} to connect the selected nodes to the node beneath the cursor</li>\"\n            \"<li>Press {scene-edit-primary} and drag to move selected nodes</li>\"\n            \"<li>Press {scene-edit-secondary} and drag to connect one node to another</li>\"\n            \"</ul><p>Note: Only a single cell's pathgrid may be edited at a time\");\n    }\n\n    void PathgridMode::activate(CSVWidget::SceneToolbar* toolbar)\n    {\n        if (!mSelectionMode)\n        {\n            mSelectionMode = new PathgridSelectionMode(toolbar, getWorldspaceWidget());\n        }\n\n        EditMode::activate(toolbar);\n        toolbar->addTool(mSelectionMode);\n    }\n\n    void PathgridMode::deactivate(CSVWidget::SceneToolbar* toolbar)\n    {\n        if (mSelectionMode)\n        {\n            toolbar->removeTool (mSelectionMode);\n            delete mSelectionMode;\n            mSelectionMode = nullptr;\n        }\n    }\n\n    void PathgridMode::primaryOpenPressed(const WorldspaceHitResult& hitResult)\n    {\n    }\n\n    void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hitResult)\n    {\n        if (CSMPrefs::get()[\"3D Scene Input\"][\"context-select\"].isTrue() &&\n            dynamic_cast<PathgridTag*>(hitResult.tag.get()))\n        {\n            primarySelectPressed(hitResult);\n        }\n        else if (Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos))\n        {\n            if (cell->getPathgrid())\n            {\n                // Add node\n                QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();\n                QString description = \"Add node\";\n\n                CSMWorld::CommandMacro macro(undoStack, description);\n                cell->getPathgrid()->applyPoint(macro, hitResult.worldPos);\n            }\n        }\n    }\n\n    void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit)\n    {\n        if (hit.tag)\n        {\n            if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))\n            {\n                if (tag->getPathgrid()->isSelected())\n                {\n                    unsigned short node = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));\n\n                    QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();\n                    QString description = \"Connect node to selected nodes\";\n\n                    CSMWorld::CommandMacro macro(undoStack, description);\n                    tag->getPathgrid()->applyEdges(macro, node);\n                }\n            }\n        }\n    }\n\n    void PathgridMode::primarySelectPressed(const WorldspaceHitResult& hit)\n    {\n        getWorldspaceWidget().clearSelection(Mask_Pathgrid);\n\n        if (hit.tag)\n        {\n            if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))\n            {\n                mLastId = tag->getPathgrid()->getId();\n                unsigned short node = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));\n                tag->getPathgrid()->toggleSelected(node);\n            }\n        }\n    }\n\n    void PathgridMode::secondarySelectPressed(const WorldspaceHitResult& hit)\n    {\n        if (hit.tag)\n        {\n            if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))\n            {\n                if (tag->getPathgrid()->getId() != mLastId)\n                {\n                    getWorldspaceWidget().clearSelection(Mask_Pathgrid);\n                    mLastId = tag->getPathgrid()->getId();\n                }\n\n                unsigned short node = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));\n                tag->getPathgrid()->toggleSelected(node);\n\n                return;\n            }\n        }\n\n        getWorldspaceWidget().clearSelection(Mask_Pathgrid);\n    }\n\n    bool PathgridMode::primaryEditStartDrag(const QPoint& pos)\n    {\n        std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);\n\n        if (CSMPrefs::get()[\"3D Scene Input\"][\"context-select\"].isTrue())\n        {\n            WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n\n            if (dynamic_cast<PathgridTag*>(hit.tag.get()))\n            {\n                primarySelectPressed(hit);\n                selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);\n            }\n        }\n\n        if (!selection.empty())\n        {\n            mDragMode = DragMode_Move;\n            return true;\n        }\n\n        return false;\n    }\n\n    bool PathgridMode::secondaryEditStartDrag(const QPoint& pos)\n    {\n        WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n        if (hit.tag)\n        {\n            if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))\n            {\n                mDragMode = DragMode_Edge;\n                mEdgeId = tag->getPathgrid()->getId();\n                mFromNode = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));\n\n                tag->getPathgrid()->setDragOrigin(mFromNode);\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    void PathgridMode::drag(const QPoint& pos, int diffX, int diffY, double speedFactor)\n    {\n        if (mDragMode == DragMode_Move)\n        {\n            std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection(Mask_Pathgrid);\n\n            for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it != selection.end(); ++it)\n            {\n                if (PathgridTag* tag = dynamic_cast<PathgridTag*>(it->get()))\n                {\n                    osg::Vec3d eye, center, up, offset;\n                    getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, center, up);\n\n                    offset = (up * diffY * speedFactor) + (((center - eye) ^ up) * diffX * speedFactor);\n\n                    tag->getPathgrid()->moveSelected(offset);\n                }\n            }\n        }\n        else if (mDragMode == DragMode_Edge)\n        {\n            WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n\n            Cell* cell = getWorldspaceWidget().getCell(hit.worldPos);\n            if (cell && cell->getPathgrid())\n            {\n                PathgridTag* tag = nullptr;\n                if (hit.tag && (tag = dynamic_cast<PathgridTag*>(hit.tag.get())) && tag->getPathgrid()->getId() == mEdgeId)\n                {\n                    unsigned short node = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));\n                    cell->getPathgrid()->setDragEndpoint(node);\n                }\n                else\n                {\n                    cell->getPathgrid()->setDragEndpoint(hit.worldPos);\n                }\n\n            }\n        }\n    }\n\n    void PathgridMode::dragCompleted(const QPoint& pos)\n    {\n        if (mDragMode == DragMode_Move)\n        {\n            std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);\n            for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it != selection.end(); ++it)\n            {\n                if (PathgridTag* tag = dynamic_cast<PathgridTag*>(it->get()))\n                {\n                    QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();\n                    QString description = \"Move pathgrid node(s)\";\n\n                    CSMWorld::CommandMacro macro(undoStack, description);\n                    tag->getPathgrid()->applyPosition(macro);\n                }\n            }\n        }\n        else if (mDragMode == DragMode_Edge)\n        {\n            WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());\n\n            if (hit.tag)\n            {\n                if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))\n                {\n                    if (tag->getPathgrid()->getId() == mEdgeId)\n                    {\n                        unsigned short toNode = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));\n\n                        QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();\n                        QString description = \"Add edge between nodes\";\n\n                        CSMWorld::CommandMacro macro(undoStack, description);\n                        tag->getPathgrid()->applyEdge(macro, mFromNode, toNode);\n                    }\n                }\n            }\n\n            mEdgeId.clear();\n            mFromNode = 0;\n        }\n\n        mDragMode = DragMode_None;\n        getWorldspaceWidget().reset(Mask_Pathgrid);\n    }\n\n    void PathgridMode::dragAborted()\n    {\n        getWorldspaceWidget().reset(Mask_Pathgrid);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/pathgridmode.hpp",
    "content": "#ifndef CSV_RENDER_PATHGRIDMODE_H\n#define CSV_RENDER_PATHGRIDMODE_H\n\n#include <string>\n\n#include \"editmode.hpp\"\n\nnamespace CSVRender\n{\n    class PathgridSelectionMode;\n\n    class PathgridMode : public EditMode\n    {\n            Q_OBJECT\n\n        public:\n\n            PathgridMode(WorldspaceWidget* worldspace, QWidget* parent=nullptr);\n\n            void activate(CSVWidget::SceneToolbar* toolbar) override;\n\n            void deactivate(CSVWidget::SceneToolbar* toolbar) override;\n\n            void primaryOpenPressed(const WorldspaceHitResult& hit) override;\n\n            void primaryEditPressed(const WorldspaceHitResult& hit) override;\n\n            void secondaryEditPressed(const WorldspaceHitResult& hit) override;\n\n            void primarySelectPressed(const WorldspaceHitResult& hit) override;\n\n            void secondarySelectPressed(const WorldspaceHitResult& hit) override;\n\n            bool primaryEditStartDrag (const QPoint& pos) override;\n\n            bool secondaryEditStartDrag (const QPoint& pos) override;\n\n            void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) override;\n\n            void dragCompleted(const QPoint& pos) override;\n\n            /// \\note dragAborted will not be called, if the drag is aborted via changing\n            /// editing mode\n            void dragAborted() override;\n\n        private:\n\n            enum DragMode\n            {\n                DragMode_None,\n                DragMode_Move,\n                DragMode_Edge\n            };\n\n            DragMode mDragMode;\n            std::string mLastId, mEdgeId;\n            unsigned short mFromNode;\n\n            PathgridSelectionMode* mSelectionMode;\n\n            QString getTooltip();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/pathgridselectionmode.cpp",
    "content": "#include \"pathgridselectionmode.hpp\"\n\n#include <QMenu>\n#include <QAction>\n\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n\n#include \"worldspacewidget.hpp\"\n#include \"pathgrid.hpp\"\n\nnamespace CSVRender\n{\n    PathgridSelectionMode::PathgridSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget)\n        : SelectionMode(parent, worldspaceWidget, Mask_Pathgrid)\n    {\n        mRemoveSelectedNodes = new QAction(\"Remove selected nodes\", this);\n        mRemoveSelectedEdges = new QAction(\"Remove edges between selected nodes\", this);\n\n        connect(mRemoveSelectedNodes, SIGNAL(triggered()), this, SLOT(removeSelectedNodes()));\n        connect(mRemoveSelectedEdges, SIGNAL(triggered()), this, SLOT(removeSelectedEdges()));\n    }\n\n    bool PathgridSelectionMode::createContextMenu(QMenu* menu)\n    {\n        if (menu)\n        {\n            SelectionMode::createContextMenu(menu);\n\n            menu->addAction(mRemoveSelectedNodes);\n            menu->addAction(mRemoveSelectedEdges);\n        }\n\n        return true;\n    }\n\n    void PathgridSelectionMode::removeSelectedNodes()\n    {\n        std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);\n\n        for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it != selection.end(); ++it)\n        {\n            if (PathgridTag* tag = dynamic_cast<PathgridTag*>(it->get()))\n            {\n                QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();\n                QString description = \"Remove selected nodes\";\n\n                CSMWorld::CommandMacro macro(undoStack, description);\n                tag->getPathgrid()->applyRemoveNodes(macro);\n            }\n        }\n    }\n\n    void PathgridSelectionMode::removeSelectedEdges()\n    {\n        std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);\n\n        for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it != selection.end(); ++it)\n        {\n            if (PathgridTag* tag = dynamic_cast<PathgridTag*>(it->get()))\n            {\n                QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();\n                QString description = \"Remove edges between selected nodes\";\n\n                CSMWorld::CommandMacro macro(undoStack, description);\n                tag->getPathgrid()->applyRemoveEdges(macro);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/pathgridselectionmode.hpp",
    "content": "#ifndef CSV_RENDER_PATHGRID_SELECTION_MODE_H\n#define CSV_RENDER_PATHGRID_SELECTION_MODE_H\n\n#include \"selectionmode.hpp\"\n\nnamespace CSVRender\n{\n    class PathgridSelectionMode : public SelectionMode\n    {\n            Q_OBJECT\n\n        public:\n\n            PathgridSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget);\n\n        protected:\n\n            /// Add context menu items to \\a menu.\n            ///\n            /// \\attention menu can be a 0-pointer\n            ///\n            /// \\return Have there been any menu items to be added (if menu is 0 and there\n            /// items to be added, the function must return true anyway.\n            bool createContextMenu(QMenu* menu) override;\n\n        private:\n\n            QAction* mRemoveSelectedNodes;\n            QAction* mRemoveSelectedEdges;\n\n        private slots:\n\n            void removeSelectedNodes();\n            void removeSelectedEdges();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/previewwidget.cpp",
    "content": "#include \"previewwidget.hpp\"\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idtable.hpp\"\n\nCSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data,\n    const std::string& id, bool referenceable, QWidget *parent)\n: SceneWidget (data.getResourceSystem(), parent), mData (data), mObject(data, mRootNode, id, referenceable)\n{\n    selectNavigationMode(\"orbit\");\n\n    QAbstractItemModel *referenceables =\n        mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables);\n\n    connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (referenceableDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int)));\n\n    connect (&mData, SIGNAL (assetTablesChanged ()),\n        this, SLOT (assetTablesChanged ()));\n\n    setExterior(false);\n\n    if (!referenceable)\n    {\n        QAbstractItemModel *references =\n            mData.getTableModel (CSMWorld::UniversalId::Type_References);\n\n        connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n            this, SLOT (referenceDataChanged (const QModelIndex&, const QModelIndex&)));\n        connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n            this, SLOT (referenceAboutToBeRemoved (const QModelIndex&, int, int)));\n    }\n}\n\nvoid CSVRender::PreviewWidget::referenceableDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    if (mObject.referenceableDataChanged (topLeft, bottomRight))\n        flagAsModified();\n\n    if (mObject.getReferenceId().empty())\n    {\n        CSMWorld::IdTable& referenceables = dynamic_cast<CSMWorld::IdTable&> (\n            *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables));\n\n        QModelIndex index = referenceables.getModelIndex (mObject.getReferenceableId(),\n            referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Modification));\n\n        if (referenceables.data (index).toInt()==CSMWorld::RecordBase::State_Deleted)\n            emit closeRequest();\n    }\n}\n\nvoid CSVRender::PreviewWidget::referenceableAboutToBeRemoved (const QModelIndex& parent, int start,\n    int end)\n{\n    if (mObject.referenceableAboutToBeRemoved (parent, start, end))\n        flagAsModified();\n\n    if (mObject.getReferenceableId().empty())\n        return;\n\n    CSMWorld::IdTable& referenceables = dynamic_cast<CSMWorld::IdTable&> (\n        *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables));\n\n    QModelIndex index = referenceables.getModelIndex (mObject.getReferenceableId(), 0);\n\n    if (index.row()>=start && index.row()<=end)\n    {\n        if (mObject.getReferenceId().empty())\n        {\n            // this is a preview for a referenceble\n            emit closeRequest();\n        }\n    }\n}\n\nvoid CSVRender::PreviewWidget::referenceDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    if (mObject.referenceDataChanged (topLeft, bottomRight))\n        flagAsModified();\n\n    if (mObject.getReferenceId().empty())\n        return;\n\n    CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (\n        *mData.getTableModel (CSMWorld::UniversalId::Type_References));\n\n    // check for deleted state\n    {\n        QModelIndex index = references.getModelIndex (mObject.getReferenceId(),\n            references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification));\n\n        if (references.data (index).toInt()==CSMWorld::RecordBase::State_Deleted)\n        {\n            emit closeRequest();\n            return;\n        }\n    }\n\n    int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId);\n\n    QModelIndex index = references.getModelIndex (mObject.getReferenceId(), columnIndex);\n\n    if (index.row()>=topLeft.row() && index.row()<=bottomRight.row())\n        if (index.column()>=topLeft.column() && index.column()<=bottomRight.row())\n            emit referenceableIdChanged (mObject.getReferenceableId());\n}\n\nvoid CSVRender::PreviewWidget::referenceAboutToBeRemoved (const QModelIndex& parent, int start,\n    int end)\n{\n    if (mObject.getReferenceId().empty())\n        return;\n\n    CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (\n        *mData.getTableModel (CSMWorld::UniversalId::Type_References));\n\n    QModelIndex index = references.getModelIndex (mObject.getReferenceId(), 0);\n\n    if (index.row()>=start && index.row()<=end)\n        emit closeRequest();\n}\n\nvoid CSVRender::PreviewWidget::assetTablesChanged ()\n{\n    mObject.reloadAssets();\n}\n"
  },
  {
    "path": "apps/opencs/view/render/previewwidget.hpp",
    "content": "#ifndef OPENCS_VIEW_PREVIEWWIDGET_H\n#define OPENCS_VIEW_PREVIEWWIDGET_H\n\n#include \"scenewidget.hpp\"\n\n#include \"object.hpp\"\n\nclass QModelIndex;\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace CSMWorld\n{\n    class Data;\n}\n\nnamespace CSVRender\n{\n    class PreviewWidget : public SceneWidget\n    {\n            Q_OBJECT\n\n            CSMWorld::Data& mData;\n            CSVRender::Object mObject;\n\n        public:\n\n            PreviewWidget (CSMWorld::Data& data, const std::string& id, bool referenceable,\n                QWidget *parent = nullptr);\n\n        signals:\n\n            void closeRequest();\n\n            void referenceableIdChanged (const std::string& id);\n\n        private slots:\n\n            void referenceableDataChanged (const QModelIndex& topLeft,\n                const QModelIndex& bottomRight);\n\n            void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void assetTablesChanged ();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/scenewidget.cpp",
    "content": "#include \"scenewidget.hpp\"\n\n#include <chrono>\n#include <thread>\n\n#include <QEvent>\n#include <QResizeEvent>\n#include <QTimer>\n#include <QLayout>\n\n#include <extern/osgQt/GraphicsWindowQt>\n#include <osg/GraphicsContext>\n#include <osgViewer/CompositeViewer>\n#include <osgViewer/ViewerEventHandlers>\n#include <osg/LightModel>\n#include <osg/Material>\n#include <osg/Version>\n\n#include <components/debug/debuglog.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n\n#include \"../widget/scenetoolmode.hpp\"\n\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"lighting.hpp\"\n#include \"mask.hpp\"\n#include \"cameracontroller.hpp\"\n\nnamespace CSVRender\n{\n\nRenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)\n    : QWidget(parent, f)\n    , mRootNode(nullptr)\n{\n\n    osgViewer::CompositeViewer& viewer = CompositeViewer::get();\n\n    osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();\n    //ds->setNumMultiSamples(8);\n\n    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;\n    traits->windowName = \"\";\n    traits->windowDecoration = true;\n    traits->x = 0;\n    traits->y = 0;\n    traits->width = width();\n    traits->height = height();\n    traits->doubleBuffer = true;\n    traits->alpha = ds->getMinimumNumAlphaBits();\n    traits->stencil = ds->getMinimumNumStencilBits();\n    traits->sampleBuffers = ds->getMultiSamples();\n    traits->samples = ds->getNumMultiSamples();\n    // Doesn't make much sense as we're running on demand updates, and there seems to be a bug with the refresh rate when running multiple QGLWidgets\n    traits->vsync = false;\n\n    mView = new osgViewer::View;\n    updateCameraParameters( traits->width / static_cast<double>(traits->height) );\n\n    osg::ref_ptr<osgQt::GraphicsWindowQt> window = new osgQt::GraphicsWindowQt(traits.get());\n    QLayout* layout = new QHBoxLayout(this);\n    layout->setContentsMargins(0, 0, 0, 0);\n    layout->addWidget(window->getGLWidget());\n    setLayout(layout);\n\n    mView->getCamera()->setGraphicsContext(window);\n    mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );\n\n    SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager;\n    lightMgr->setStartLight(1);\n    lightMgr->setLightingMask(Mask_Lighting);\n    mRootNode = lightMgr;\n\n    mView->getCamera()->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);\n    mView->getCamera()->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);\n    osg::ref_ptr<osg::Material> defaultMat (new osg::Material);\n    defaultMat->setColorMode(osg::Material::OFF);\n    defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n    defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n    defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n    mView->getCamera()->getOrCreateStateSet()->setAttribute(defaultMat);\n\n    mView->setSceneData(mRootNode);\n\n    // Add ability to signal osg to show its statistics for debugging purposes\n    mView->addEventHandler(new osgViewer::StatsHandler);\n\n    viewer.addView(mView);\n    viewer.setDone(false);\n    viewer.realize();\n}\n\nRenderWidget::~RenderWidget()\n{\n    try\n    {\n        CompositeViewer::get().removeView(mView);\n\n#if OSG_VERSION_LESS_THAN(3,6,5)\n        // before OSG 3.6.4, the default font was a static object, and if it wasn't attached to the scene when a graphics context was destroyed, it's program wouldn't be released.\n        // 3.6.4 moved it into the object cache, which meant it usually got released, but not here.\n        // 3.6.5 improved cleanup with osgViewer::CompositeViewer::removeView so it more reliably released associated state for objects in the object cache.\n        osg::ref_ptr<osg::GraphicsContext> graphicsContext = mView->getCamera()->getGraphicsContext();\n        osgText::Font::getDefaultFont()->releaseGLObjects(graphicsContext->getState());\n#endif\n    }\n    catch(const std::exception& e)\n    {\n        Log(Debug::Error) << \"Error in the destructor: \" << e.what();\n    }\n}\n\nvoid RenderWidget::flagAsModified()\n{\n    mView->requestRedraw();\n}\n\nvoid RenderWidget::setVisibilityMask(unsigned int mask)\n{\n    mView->getCamera()->setCullMask(mask | Mask_ParticleSystem | Mask_Lighting);\n}\n\nosg::Camera *RenderWidget::getCamera()\n{\n    return mView->getCamera();\n}\n\nvoid RenderWidget::toggleRenderStats()\n{\n    osgViewer::GraphicsWindow* window =\n        static_cast<osgViewer::GraphicsWindow*>(mView->getCamera()->getGraphicsContext());\n\n    window->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_S);\n    window->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_S);\n}\n\n\n// --------------------------------------------------\n\nCompositeViewer::CompositeViewer()\n    : mSimulationTime(0.0)\n{\n    // TODO: Upgrade osgQt to support osgViewer::ViewerBase::DrawThreadPerContext\n    // https://gitlab.com/OpenMW/openmw/-/issues/5481\n    setThreadingModel(osgViewer::ViewerBase::SingleThreaded);\n\n#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)\n    setUseConfigureAffinity(false);\n#endif\n\n    // disable the default setting of viewer.done() by pressing Escape.\n    setKeyEventSetsDone(0);\n\n    // Only render when the camera position changed, or content flagged dirty\n    //setRunFrameScheme(osgViewer::ViewerBase::ON_DEMAND);\n    setRunFrameScheme(osgViewer::ViewerBase::CONTINUOUS);\n\n    connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );\n    mTimer.start( 10 );\n\n    int frameRateLimit = CSMPrefs::get()[\"Rendering\"][\"framerate-limit\"].toInt();\n    setRunMaxFrameRate(frameRateLimit);\n}\n\nCompositeViewer &CompositeViewer::get()\n{\n    static CompositeViewer sThis;\n    return sThis;\n}\n\nvoid CompositeViewer::update()\n{\n    double dt = mFrameTimer.time_s();\n    mFrameTimer.setStartTick();\n\n    emit simulationUpdated(dt);\n\n    mSimulationTime += dt;\n    frame(mSimulationTime);\n\n    double minFrameTime = _runMaxFrameRate > 0.0 ? 1.0 / _runMaxFrameRate : 0.0;\n    if (dt < minFrameTime)\n    {\n        std::this_thread::sleep_for(std::chrono::duration<double>(minFrameTime - dt));\n    }\n}\n\n// ---------------------------------------------------\n\nSceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSystem, QWidget *parent, Qt::WindowFlags f,\n    bool retrieveInput)\n    : RenderWidget(parent, f)\n    , mResourceSystem(resourceSystem)\n    , mLighting(nullptr)\n    , mHasDefaultAmbient(false)\n    , mIsExterior(true)\n    , mPrevMouseX(0)\n    , mPrevMouseY(0)\n    , mCamPositionSet(false)\n{\n    mFreeCamControl = new FreeCameraController(this);\n    mOrbitCamControl = new OrbitCameraController(this);\n    mCurrentCamControl = mFreeCamControl;\n\n    mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain);\n\n    mOrbitCamControl->setConstRoll( CSMPrefs::get()[\"3D Scene Input\"][\"navi-orbit-const-roll\"].isTrue() );\n\n    // set up gradient view or configured clear color\n    QColor bgColour = CSMPrefs::get()[\"Rendering\"][\"scene-day-background-colour\"].toColor();\n\n    if (CSMPrefs::get()[\"Rendering\"][\"scene-use-gradient\"].isTrue()) {\n        QColor gradientColour = CSMPrefs::get()[\"Rendering\"][\"scene-day-gradient-colour\"].toColor();\n        mGradientCamera = createGradientCamera(bgColour, gradientColour);\n\n        mView->getCamera()->setClearMask(0);\n        mView->getCamera()->addChild(mGradientCamera.get());\n    }\n    else {\n        mView->getCamera()->setClearColor(osg::Vec4(\n            bgColour.redF(),\n            bgColour.greenF(),\n            bgColour.blueF(),\n            1.0f\n        ));\n    }\n\n    // we handle lighting manually\n    mView->setLightingMode(osgViewer::View::NO_LIGHT);\n\n    setLighting(&mLightingDay);\n\n    mResourceSystem->getSceneManager()->setParticleSystemMask(Mask_ParticleSystem);\n\n    // Recieve mouse move event even if mouse button is not pressed\n    setMouseTracking(true);\n    setFocusPolicy(Qt::ClickFocus);\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n\n    // TODO update this outside of the constructor where virtual methods can be used\n    if (retrieveInput)\n    {\n        CSMPrefs::get()[\"3D Scene Input\"].update();\n        CSMPrefs::get()[\"Tooltips\"].update();\n    }\n\n    connect (&CompositeViewer::get(), SIGNAL (simulationUpdated(double)), this, SLOT (update(double)));\n\n    // Shortcuts\n    CSMPrefs::Shortcut* focusToolbarShortcut = new CSMPrefs::Shortcut(\"scene-focus-toolbar\", this);\n    connect(focusToolbarShortcut, SIGNAL(activated()), this, SIGNAL(focusToolbarRequest()));\n\n    CSMPrefs::Shortcut* renderStatsShortcut = new CSMPrefs::Shortcut(\"scene-render-stats\", this);\n    connect(renderStatsShortcut, SIGNAL(activated()), this, SLOT(toggleRenderStats()));\n}\n\nSceneWidget::~SceneWidget()\n{\n    // Since we're holding on to the resources past the existence of this graphics context, we'll need to manually release the created objects\n    mResourceSystem->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());\n}\n\n\nosg::ref_ptr<osg::Geometry> SceneWidget::createGradientRectangle(QColor bgColour, QColor gradientColour)\n{\n    osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;\n\n    osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;\n\n    vertices->push_back(osg::Vec3(0.0f, 0.0f, -1.0f));\n    vertices->push_back(osg::Vec3(1.0f, 0.0f, -1.0f));\n    vertices->push_back(osg::Vec3(0.0f, 1.0f, -1.0f));\n    vertices->push_back(osg::Vec3(1.0f, 1.0f, -1.0f));\n\n    geometry->setVertexArray(vertices);\n\n    osg::ref_ptr<osg::DrawElementsUShort> primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0);\n\n    // triangle 1\n    primitives->push_back (0);\n    primitives->push_back (1);\n    primitives->push_back (2);\n\n    // triangle 2\n    primitives->push_back (2);\n    primitives->push_back (1);\n    primitives->push_back (3);\n\n    geometry->addPrimitiveSet(primitives);\n\n    osg::ref_ptr <osg::Vec4ubArray> colours = new osg::Vec4ubArray;\n    colours->push_back(osg::Vec4ub(gradientColour.red(), gradientColour.green(), gradientColour.blue(), 1.0f));\n    colours->push_back(osg::Vec4ub(gradientColour.red(), gradientColour.green(), gradientColour.blue(), 1.0f));\n    colours->push_back(osg::Vec4ub(bgColour.red(), bgColour.green(), bgColour.blue(), 1.0f));\n    colours->push_back(osg::Vec4ub(bgColour.red(), bgColour.green(), bgColour.blue(), 1.0f));\n\n    geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX);\n\n    geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n    geometry->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n\n    return geometry;\n}\n\n\nosg::ref_ptr<osg::Camera> SceneWidget::createGradientCamera(QColor bgColour, QColor gradientColour)\n{\n    osg::ref_ptr<osg::Camera> camera = new osg::Camera();\n    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);\n    camera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1.0f, 0, 1.0f));\n    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);\n    camera->setViewMatrix(osg::Matrix::identity());\n\n    camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);\n    camera->setAllowEventFocus(false);\n\n    // draw subgraph before main camera view.\n    camera->setRenderOrder(osg::Camera::PRE_RENDER);\n\n    camera->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n\n    osg::ref_ptr<osg::Geometry> gradientQuad = createGradientRectangle(bgColour, gradientColour);\n\n    camera->addChild(gradientQuad);\n    return camera;\n}\n\n\nvoid SceneWidget::updateGradientCamera(QColor bgColour, QColor gradientColour)\n{\n    osg::ref_ptr<osg::Geometry> gradientRect = createGradientRectangle(bgColour, gradientColour);\n    // Replaces previous rectangle\n    mGradientCamera->setChild(0, gradientRect.get());\n}\n\nvoid SceneWidget::setLighting(Lighting *lighting)\n{\n    if (mLighting)\n        mLighting->deactivate();\n\n    mLighting = lighting;\n    mLighting->activate (mRootNode, mIsExterior);\n\n    osg::Vec4f ambient = mLighting->getAmbientColour(mHasDefaultAmbient ? &mDefaultAmbient : nullptr);\n    setAmbient(ambient);\n\n    flagAsModified();\n}\n\nvoid SceneWidget::setAmbient(const osg::Vec4f& ambient)\n{\n    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;\n    osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;\n    lightmodel->setAmbientIntensity(ambient);\n    stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);\n    stateset->setMode(GL_LIGHT0, osg::StateAttribute::ON);\n    stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON);\n    mRootNode->setStateSet(stateset);\n}\n\nvoid SceneWidget::selectLightingMode (const std::string& mode)\n{\n    QColor backgroundColour;\n    QColor gradientColour;\n    if (mode == \"day\")\n    {\n        backgroundColour = CSMPrefs::get()[\"Rendering\"][\"scene-day-background-colour\"].toColor();\n        gradientColour = CSMPrefs::get()[\"Rendering\"][\"scene-day-gradient-colour\"].toColor();\n        setLighting(&mLightingDay);\n    }\n    else if (mode == \"night\")\n    {\n        backgroundColour = CSMPrefs::get()[\"Rendering\"][\"scene-night-background-colour\"].toColor();\n        gradientColour = CSMPrefs::get()[\"Rendering\"][\"scene-night-gradient-colour\"].toColor();\n        setLighting(&mLightingNight);\n    }\n    else if (mode == \"bright\")\n    {\n        backgroundColour = CSMPrefs::get()[\"Rendering\"][\"scene-bright-background-colour\"].toColor();\n        gradientColour = CSMPrefs::get()[\"Rendering\"][\"scene-bright-gradient-colour\"].toColor();\n        setLighting(&mLightingBright);\n    }\n    if (CSMPrefs::get()[\"Rendering\"][\"scene-use-gradient\"].isTrue()) {\n        if (mGradientCamera.get() != nullptr) {\n            // we can go ahead and update since this camera still exists\n            updateGradientCamera(backgroundColour, gradientColour);\n\n            if (!mView->getCamera()->containsNode(mGradientCamera.get()))\n            {\n                // need to re-attach the gradient camera\n                mView->getCamera()->setClearMask(0);\n                mView->getCamera()->addChild(mGradientCamera.get());\n            }\n        } \n        else {\n            // need to create the gradient camera\n            mGradientCamera = createGradientCamera(backgroundColour, gradientColour);\n            mView->getCamera()->setClearMask(0);\n            mView->getCamera()->addChild(mGradientCamera.get());\n        }\n    }\n    else {\n        // Fall back to using the clear color for the camera\n        mView->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        mView->getCamera()->setClearColor(osg::Vec4(\n            backgroundColour.redF(),\n            backgroundColour.greenF(),\n            backgroundColour.blueF(),\n            1.0f\n        ));\n        if (mGradientCamera.get() != nullptr && mView->getCamera()->containsNode(mGradientCamera.get())) {\n            // Remove the child to prevent the gradient from rendering\n            mView->getCamera()->removeChild(mGradientCamera.get());\n        }\n    }\n}\n\nCSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent)\n{\n    CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, \"Lighting Mode\");\n\n    /// \\todo replace icons\n    tool->addButton (\":scenetoolbar/day\", \"day\",\n        \"Day\"\n        \"<ul><li>Cell specific ambient in interiors</li>\"\n        \"<li>Low ambient in exteriors</li>\"\n        \"<li>Strong directional light source</li>\"\n        \"<li>This mode closely resembles day time in-game</li></ul>\");\n    tool->addButton (\":scenetoolbar/night\", \"night\",\n        \"Night\"\n        \"<ul><li>Cell specific ambient in interiors</li>\"\n        \"<li>Low ambient in exteriors</li>\"\n        \"<li>Weak directional light source</li>\"\n        \"<li>This mode closely resembles night time in-game</li></ul>\");\n    tool->addButton (\":scenetoolbar/bright\", \"bright\",\n        \"Bright\"\n        \"<ul><li>Maximum ambient</li>\"\n        \"<li>Strong directional light source</li></ul>\");\n\n    connect (tool, SIGNAL (modeChanged (const std::string&)),\n        this, SLOT (selectLightingMode (const std::string&)));\n\n    return tool;\n}\n\nvoid SceneWidget::setDefaultAmbient (const osg::Vec4f& colour)\n{\n    mDefaultAmbient = colour;\n    mHasDefaultAmbient = true;\n\n    setAmbient(mLighting->getAmbientColour(&mDefaultAmbient));\n}\n\nvoid SceneWidget::setExterior (bool isExterior)\n{\n    mIsExterior = isExterior;\n}\n\nvoid SceneWidget::mouseMoveEvent (QMouseEvent *event)\n{\n    mCurrentCamControl->handleMouseMoveEvent(event->x() - mPrevMouseX, event->y() - mPrevMouseY);\n\n    mPrevMouseX = event->x();\n    mPrevMouseY = event->y();\n}\n\nvoid SceneWidget::wheelEvent(QWheelEvent *event)\n{\n    mCurrentCamControl->handleMouseScrollEvent(event->angleDelta().y());\n}\n\nvoid SceneWidget::update(double dt)\n{\n    if (mCamPositionSet)\n    {\n        mCurrentCamControl->update(dt);\n    }\n    else\n    {\n        mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp);\n        mCamPositionSet = true;\n    }\n}\n\nvoid SceneWidget::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==\"3D Scene Input/p-navi-free-sensitivity\")\n    {\n        mFreeCamControl->setCameraSensitivity(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/p-navi-orbit-sensitivity\")\n    {\n        mOrbitCamControl->setCameraSensitivity(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/p-navi-free-invert\")\n    {\n        mFreeCamControl->setInverted(setting->isTrue());\n    }\n    else if (*setting==\"3D Scene Input/p-navi-orbit-invert\")\n    {\n        mOrbitCamControl->setInverted(setting->isTrue());\n    }\n    else if (*setting==\"3D Scene Input/s-navi-sensitivity\")\n    {\n        mFreeCamControl->setSecondaryMovementMultiplier(setting->toDouble());\n        mOrbitCamControl->setSecondaryMovementMultiplier(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/navi-wheel-factor\")\n    {\n        mFreeCamControl->setWheelMovementMultiplier(setting->toDouble());\n        mOrbitCamControl->setWheelMovementMultiplier(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/navi-free-lin-speed\")\n    {\n        mFreeCamControl->setLinearSpeed(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/navi-free-rot-speed\")\n    {\n        mFreeCamControl->setRotationalSpeed(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/navi-free-speed-mult\")\n    {\n        mFreeCamControl->setSpeedMultiplier(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/navi-orbit-rot-speed\")\n    {\n        mOrbitCamControl->setOrbitSpeed(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/navi-orbit-speed-mult\")\n    {\n        mOrbitCamControl->setOrbitSpeedMultiplier(setting->toDouble());\n    }\n    else if (*setting==\"3D Scene Input/navi-orbit-const-roll\")\n    {\n        mOrbitCamControl->setConstRoll(setting->isTrue());\n    }\n    else if (*setting==\"Rendering/framerate-limit\")\n    {\n        CompositeViewer::get().setRunMaxFrameRate(setting->toInt());\n    }\n    else if (*setting==\"Rendering/camera-fov\" ||\n             *setting==\"Rendering/camera-ortho\" ||\n             *setting==\"Rendering/camera-ortho-size\")\n    {\n        updateCameraParameters();\n    }\n}\n\nvoid RenderWidget::updateCameraParameters(double overrideAspect)\n{\n    const float nearDist = 1.0;\n    const float farDist = 1000.0;\n\n    if (CSMPrefs::get()[\"Rendering\"][\"camera-ortho\"].isTrue())\n    {\n        const float size = CSMPrefs::get()[\"Rendering\"][\"camera-ortho-size\"].toInt();\n        const float aspect = overrideAspect >= 0.0 ? overrideAspect : (width() / static_cast<double>(height()));\n        const float halfH = size * 10.0;\n        const float halfW = halfH * aspect;\n\n        mView->getCamera()->setProjectionMatrixAsOrtho(\n            -halfW, halfW, -halfH, halfH, nearDist, farDist);\n    }\n    else\n    { \n        mView->getCamera()->setProjectionMatrixAsPerspective(\n            CSMPrefs::get()[\"Rendering\"][\"camera-fov\"].toInt(),\n            static_cast<double>(width())/static_cast<double>(height()),\n            nearDist, farDist);\n    }\n}\n\nvoid SceneWidget::selectNavigationMode (const std::string& mode)\n{\n    if (mode==\"1st\")\n    {\n        mCurrentCamControl->setCamera(nullptr);\n        mCurrentCamControl = mFreeCamControl;\n        mFreeCamControl->setCamera(getCamera());\n        mFreeCamControl->fixUpAxis(CameraController::WorldUp);\n    }\n    else if (mode==\"free\")\n    {\n        mCurrentCamControl->setCamera(nullptr);\n        mCurrentCamControl = mFreeCamControl;\n        mFreeCamControl->setCamera(getCamera());\n        mFreeCamControl->unfixUpAxis();\n    }\n    else if (mode==\"orbit\")\n    {\n        mCurrentCamControl->setCamera(nullptr);\n        mCurrentCamControl = mOrbitCamControl;\n        mOrbitCamControl->setCamera(getCamera());\n        mOrbitCamControl->reset();\n    }\n}\n\n}\n"
  },
  {
    "path": "apps/opencs/view/render/scenewidget.hpp",
    "content": "#ifndef OPENCS_VIEW_SCENEWIDGET_H\n#define OPENCS_VIEW_SCENEWIDGET_H\n\n#include <map>\n#include <memory>\n\n#include <QWidget>\n#include <QTimer>\n\n#include <osgViewer/View>\n#include <osgViewer/CompositeViewer>\n\n#include \"lightingday.hpp\"\n#include \"lightingnight.hpp\"\n#include \"lightingbright.hpp\"\n\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace osg\n{\n    class Group;\n    class Camera;\n}\n\nnamespace CSVWidget\n{\n    class SceneToolMode;\n    class SceneToolbar;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVRender\n{\n    class CameraController;\n    class FreeCameraController;\n    class OrbitCameraController;\n    class Lighting;\n\n    class RenderWidget : public QWidget\n    {\n            Q_OBJECT\n\n        public:\n            RenderWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());\n            virtual ~RenderWidget();\n\n            /// Initiates a request to redraw the view\n            void flagAsModified();\n\n            void setVisibilityMask(unsigned int mask);\n\n            osg::Camera *getCamera();\n\n        protected:\n\n            osg::ref_ptr<osgViewer::View> mView;\n            osg::ref_ptr<osg::Group> mRootNode;\n\n            void updateCameraParameters(double overrideAspect = -1.0);\n\n            QTimer mTimer;\n\n        protected slots:\n\n            void toggleRenderStats();\n    };\n\n    /// Extension of RenderWidget to support lighting mode selection & toolbar\n    class SceneWidget : public RenderWidget\n    {\n            Q_OBJECT\n        public:\n            SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSystem, QWidget* parent = nullptr,\n                        Qt::WindowFlags f = Qt::WindowFlags(), bool retrieveInput = true);\n            virtual ~SceneWidget();\n\n            CSVWidget::SceneToolMode *makeLightingSelector (CSVWidget::SceneToolbar *parent);\n            ///< \\attention The created tool is not added to the toolbar (via addTool). Doing that\n            /// is the responsibility of the calling function.\n\n            void setDefaultAmbient (const osg::Vec4f& colour);\n            ///< \\note The actual ambient colour may differ based on lighting settings.\n\n            void setExterior (bool isExterior);\n\n        protected:\n            void setLighting (Lighting *lighting);\n            ///< \\attention The ownership of \\a lighting is not transferred to *this.\n\n            void setAmbient(const osg::Vec4f& ambient);\n\n            void mouseMoveEvent (QMouseEvent *event) override;\n            void wheelEvent (QWheelEvent *event) override;\n\n            osg::ref_ptr<osg::Geometry> createGradientRectangle(QColor bgColour, QColor gradientColour);\n            osg::ref_ptr<osg::Camera> createGradientCamera(QColor bgColour, QColor gradientColour);\n            void updateGradientCamera(QColor bgColour, QColor gradientColour);\n\n            std::shared_ptr<Resource::ResourceSystem> mResourceSystem;\n\n            Lighting* mLighting;\n            \n            osg::ref_ptr<osg::Camera> mGradientCamera;\n            osg::Vec4f mDefaultAmbient;\n            bool mHasDefaultAmbient;\n            bool mIsExterior;\n            LightingDay mLightingDay;\n            LightingNight mLightingNight;\n            LightingBright mLightingBright;\n\n            int mPrevMouseX, mPrevMouseY;\n            \n            /// Tells update that camera isn't set\n            bool mCamPositionSet;\n\n            FreeCameraController* mFreeCamControl;\n            OrbitCameraController* mOrbitCamControl;\n            CameraController* mCurrentCamControl;\n\n        public slots:\n            void update(double dt);\n\n        protected slots:\n\n            virtual void settingChanged (const CSMPrefs::Setting *setting);\n\n            void selectNavigationMode (const std::string& mode);\n\n        private slots:\n\n            void selectLightingMode (const std::string& mode);\n\n        signals:\n\n            void focusToolbarRequest();\n    };\n\n\n    // There are rendering glitches when using multiple Viewer instances, work around using CompositeViewer with multiple views\n    class CompositeViewer : public QObject, public osgViewer::CompositeViewer\n    {\n            Q_OBJECT\n        public:\n            CompositeViewer();\n\n            static CompositeViewer& get();\n\n            QTimer mTimer;\n\n        private:\n            osg::Timer mFrameTimer;\n            double mSimulationTime;\n\n        public slots:\n            void update();\n\n        signals:\n            void simulationUpdated(double dt);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/selectionmode.cpp",
    "content": "#include \"selectionmode.hpp\"\n\n#include <QMenu>\n#include <QAction>\n\n#include \"worldspacewidget.hpp\"\n\nnamespace CSVRender\n{\n    SelectionMode::SelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget,\n        unsigned int interactionMask)\n        : SceneToolMode(parent, \"Selection mode\")\n        , mWorldspaceWidget(worldspaceWidget)\n        , mInteractionMask(interactionMask)\n    {\n        addButton(\":scenetoolbar/selection-mode-cube\", \"cube-centre\",\n            \"Centred cube\"\n            \"<ul><li>Drag with {scene-select-primary} for primary select or {scene-select-secondary} for secondary select \"\n                \"from the centre of the selection cube outwards.</li>\"\n            \"<li>The selection cube is aligned to the word space axis</li>\"\n            \"<li>If context selection mode is enabled, a drag with {scene-edit-primary} or {scene-edit-secondary} not \"\n                \"starting on an instance will have the same effect</li>\"\n            \"</ul>\");\n        addButton(\":scenetoolbar/selection-mode-cube-corner\", \"cube-corner\",\n            \"Cube corner to corner\"\n            \"<ul><li>Drag with {scene-select-primary} for primary select or {scene-select-secondary} for secondary select \"\n                \"from one corner of the selection cube to the opposite corner</li>\"\n            \"<li>The selection cube is aligned to the word space axis</li>\"\n            \"<li>If context selection mode is enabled, a drag with {scene-edit-primary} or {scene-edit-secondary} not \"\n                \"starting on an instance will have the same effect</li>\"\n            \"</ul>\");\n        addButton(\":scenetoolbar/selection-mode-cube-sphere\", \"sphere\",\n            \"Centred sphere\"\n            \"<ul><li>Drag with {scene-select-primary} for primary select or {scene-select-secondary} for secondary select  \"\n                \"from the centre of the selection sphere outwards</li>\"\n            \"<li>If context selection mode is enabled, a drag with {scene-edit-primary} or {scene-edit-secondary} not \"\n                \"starting on an instance will have the same effect</li>\"\n            \"</ul>\");\n\n        mSelectAll = new QAction(\"Select all\", this);\n        mDeselectAll = new QAction(\"Clear selection\", this);\n        mInvertSelection = new QAction(\"Invert selection\", this);\n\n        connect(mSelectAll, SIGNAL(triggered()), this, SLOT(selectAll()));\n        connect(mDeselectAll, SIGNAL(triggered()), this, SLOT(clearSelection()));\n        connect(mInvertSelection, SIGNAL(triggered()), this, SLOT(invertSelection()));\n    }\n\n    WorldspaceWidget& SelectionMode::getWorldspaceWidget()\n    {\n        return mWorldspaceWidget;\n    }\n\n    bool SelectionMode::createContextMenu (QMenu* menu)\n    {\n        if (menu)\n        {\n            menu->addAction(mSelectAll);\n            menu->addAction(mDeselectAll);\n            menu->addAction(mInvertSelection);\n        }\n\n        return true;\n    }\n\n    void SelectionMode::selectAll()\n    {\n        getWorldspaceWidget().selectAll(mInteractionMask);\n    }\n\n    void SelectionMode::clearSelection()\n    {\n        getWorldspaceWidget().clearSelection(mInteractionMask);\n    }\n\n    void SelectionMode::invertSelection()\n    {\n        getWorldspaceWidget().invertSelection(mInteractionMask);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/selectionmode.hpp",
    "content": "#ifndef CSV_RENDER_SELECTION_MODE_H\n#define CSV_RENDER_SELECTION_MODE_H\n\n#include \"../widget/scenetoolmode.hpp\"\n\n#include \"mask.hpp\"\n\nclass QAction;\n\nnamespace CSVRender\n{\n    class WorldspaceWidget;\n\n    class SelectionMode : public CSVWidget::SceneToolMode\n    {\n            Q_OBJECT\n\n        public:\n\n            SelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget,\n                unsigned int interactionMask);\n\n        protected:\n\n            WorldspaceWidget& getWorldspaceWidget();\n\n            /// Add context menu items to \\a menu.\n            ///\n            /// \\attention menu can be a 0-pointer\n            ///\n            /// \\return Have there been any menu items to be added (if menu is 0 and there\n            /// items to be added, the function must return true anyway.\n            bool createContextMenu (QMenu* menu) override;\n\n        private:\n\n            WorldspaceWidget& mWorldspaceWidget;\n            unsigned int mInteractionMask;\n            QAction* mSelectAll;\n            QAction* mDeselectAll;\n            QAction* mInvertSelection;\n\n        protected slots:\n\n            virtual void selectAll();\n            virtual void clearSelection();\n            virtual void invertSelection();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/tagbase.cpp",
    "content": "\n#include \"tagbase.hpp\"\n\nCSVRender::TagBase::TagBase (Mask mask) : mMask (mask) {}\n\nCSVRender::Mask CSVRender::TagBase::getMask() const\n{\n    return mMask;\n}\n\nQString CSVRender::TagBase::getToolTip (bool hideBasics) const\n{\n    return \"\";\n}\n"
  },
  {
    "path": "apps/opencs/view/render/tagbase.hpp",
    "content": "#ifndef OPENCS_VIEW_TAGBASE_H\n#define OPENCS_VIEW_TAGBASE_H\n\n#include <osg/Referenced>\n\n#include <QString>\n\n#include \"mask.hpp\"\n\nnamespace CSVRender\n{\n    class TagBase : public osg::Referenced\n    {\n            Mask mMask;\n\n        public:\n\n            TagBase (Mask mask);\n\n            Mask getMask() const;\n\n            virtual QString getToolTip (bool hideBasics) const;\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/terrainselection.cpp",
    "content": "#include \"terrainselection.hpp\"\n\n#include <algorithm>\n\n#include <osg/Group>\n#include <osg/Geometry>\n#include <osg/PositionAttitudeTransform>\n\n#include <components/esm/loadland.hpp>\n\n#include \"../../model/world/cellcoordinates.hpp\"\n#include \"../../model/world/columnimp.hpp\"\n#include \"../../model/world/idtable.hpp\"\n\n#include \"cell.hpp\"\n#include \"worldspacewidget.hpp\"\n\nCSVRender::TerrainSelection::TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type):\nmParentNode(parentNode), mWorldspaceWidget (worldspaceWidget), mDraggedOperationFlag(false), mSelectionType(type)\n{\n    mGeometry = new osg::Geometry();\n\n    mSelectionNode = new osg::Group();\n    mSelectionNode->addChild(mGeometry);\n\n    activate();\n}\n\nCSVRender::TerrainSelection::~TerrainSelection()\n{\n    deactivate();\n}\n\nstd::vector<std::pair<int, int>> CSVRender::TerrainSelection::getTerrainSelection() const\n{\n    return mSelection;\n}\n\nvoid CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, int>> &localPositions)\n{\n    mSelection = localPositions;\n\n    update();\n}\n\nvoid CSVRender::TerrainSelection::addSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress)\n{\n    handleSelection(localPositions, toggleInProgress, SelectionMethod::AddSelect);\n}\n\nvoid CSVRender::TerrainSelection::removeSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress)\n{\n    handleSelection(localPositions, toggleInProgress, SelectionMethod::RemoveSelect);\n}\n\nvoid CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress)\n{\n    handleSelection(localPositions, toggleInProgress, SelectionMethod::ToggleSelect);\n}\n\nvoid CSVRender::TerrainSelection::activate()\n{\n    if (!mParentNode->containsNode(mSelectionNode)) mParentNode->addChild(mSelectionNode);\n}\n\nvoid CSVRender::TerrainSelection::deactivate()\n{\n    mParentNode->removeChild(mSelectionNode);\n}\n\nvoid CSVRender::TerrainSelection::update()\n{\n    mSelectionNode->removeChild(mGeometry);\n    mGeometry = new osg::Geometry();\n\n    const osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array);\n\n    switch (mSelectionType)\n    {\n        case TerrainSelectionType::Texture : drawTextureSelection(vertices);\n        break;\n        case TerrainSelectionType::Shape : drawShapeSelection(vertices);\n        break;\n    }\n\n    mGeometry->setVertexArray(vertices);\n    osg::ref_ptr<osg::DrawArrays> drawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES);\n    drawArrays->setCount(vertices->size());\n    if (vertices->size() != 0) mGeometry->addPrimitiveSet(drawArrays);\n    mSelectionNode->addChild(mGeometry);\n}\n\nvoid CSVRender::TerrainSelection::drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices)\n{\n    if (!mSelection.empty())\n    {\n        for (std::pair<int, int> &localPos : mSelection)\n        {\n            int x (localPos.first);\n            int y (localPos.second);\n\n            float xWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x));\n            float yWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y));\n\n            osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y) + 2);\n\n            vertices->push_back(pointXY);\n            vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y - 1), calculateLandHeight(x, y - 1) + 2));\n            vertices->push_back(pointXY);\n            vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x - 1), yWorldCoord, calculateLandHeight(x - 1, y) + 2));\n\n            const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));\n            if (north == mSelection.end())\n            {\n                vertices->push_back(pointXY);\n                vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1), calculateLandHeight(x, y + 1) + 2));\n            }\n\n            const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));\n            if (east == mSelection.end())\n            {\n                vertices->push_back(pointXY);\n                vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x + 1), yWorldCoord, calculateLandHeight(x + 1, y) + 2));\n            }\n        }\n    }\n}\n\nvoid CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices)\n{\n    if (!mSelection.empty())\n    {\n        const int landHeightsNudge = (ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE) / (ESM::Land::LAND_SIZE - 1); // Does this work with all land size configurations?\n\n        const int textureSizeToLandSizeModifier = (ESM::Land::LAND_SIZE - 1) / ESM::Land::LAND_TEXTURE_SIZE;\n\n        for (std::pair<int, int> &localPos : mSelection)\n        {\n            int x (localPos.first);\n            int y (localPos.second);\n\n            // convert texture selection to global vertex coordinates at selection box corners\n            int x1 = x * textureSizeToLandSizeModifier + landHeightsNudge;\n            int x2 = x * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier + landHeightsNudge;\n            int y1 = y * textureSizeToLandSizeModifier - landHeightsNudge;\n            int y2 = y * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier - landHeightsNudge;\n\n            // Draw edges (check all sides, draw lines between vertices, +1 height to keep lines above ground)\n            // Check adjancent selections, draw lines only to edges of the selection\n            const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));\n            if (north == mSelection.end())\n            {\n                for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)\n                {\n                    float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));\n                    float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));\n                    vertices->push_back(osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+(i-1), y2)+2));\n                    vertices->push_back(osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+i, y2)+2));\n                }\n            }\n\n            const auto south = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y - 1));\n            if (south == mSelection.end())\n            {\n                for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)\n                {\n                    float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + (i - 1) *(ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));\n                    float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));\n                    vertices->push_back(osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+(i-1), y1)+2));\n                    vertices->push_back(osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+i, y1)+2));\n                }\n            }\n\n            const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));\n            if (east == mSelection.end())\n            {\n                for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)\n                {\n                    float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));\n                    float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));\n                    vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1), drawPreviousY, calculateLandHeight(x2, y1+(i-1))+2));\n                    vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1), drawCurrentY, calculateLandHeight(x2, y1+i)+2));\n                }\n            }\n\n            const auto west = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x - 1, y));\n            if (west == mSelection.end())\n            {\n                for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)\n                {\n                    float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));\n                    float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));\n                    vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x), drawPreviousY, calculateLandHeight(x1, y1+(i-1))+2));\n                    vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x), drawCurrentY, calculateLandHeight(x1, y1+i)+2));\n                }\n            }\n        }\n    }\n}\n\nvoid CSVRender::TerrainSelection::handleSelection(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod)\n{\n    if (toggleInProgress)\n    {\n        for (auto const& localPos : localPositions)\n        {\n            auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);\n            mDraggedOperationFlag = true;\n\n            if (iterTemp == mTemporarySelection.end())\n            {\n                auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);\n\n                switch (selectionMethod)\n                {\n                case SelectionMethod::AddSelect:\n                    if (iter == mSelection.end())\n                    {\n                        mSelection.emplace_back(localPos);\n                    }\n\n                    break;\n                case SelectionMethod::RemoveSelect:\n                    if (iter != mSelection.end())\n                    {\n                        mSelection.erase(iter);\n                    }\n\n                    break;\n                case SelectionMethod::ToggleSelect:\n                    if (iter == mSelection.end())\n                    {\n                        mSelection.emplace_back(localPos);\n                    }\n                    else\n                    {\n                        mSelection.erase(iter);\n                    }\n\n                    break;\n                default: break;\n                }\n            }\n\n            mTemporarySelection.push_back(localPos);\n        }\n    }\n    else if (mDraggedOperationFlag == false)\n    {\n        for (auto const& localPos : localPositions)\n        {\n            const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);\n\n            switch (selectionMethod)\n            {\n            case SelectionMethod::AddSelect:\n                if (iter == mSelection.end())\n                {\n                    mSelection.emplace_back(localPos);\n                }\n\n                break;\n            case SelectionMethod::RemoveSelect:\n                if (iter != mSelection.end())\n                {\n                    mSelection.erase(iter);\n                }\n\n                break;\n            case SelectionMethod::ToggleSelect:\n                if (iter == mSelection.end())\n                {\n                    mSelection.emplace_back(localPos);\n                }\n                else\n                {\n                    mSelection.erase(iter);\n                }\n\n                break;\n            default: break;\n            }\n        }\n    }\n    else\n    {\n        mDraggedOperationFlag = false;\n\n        mTemporarySelection.clear();\n    }\n\n    update();\n}\n\nbool CSVRender::TerrainSelection::noCell(const std::string& cellId)\n{\n    CSMDoc::Document& document = mWorldspaceWidget->getDocument();\n    const CSMWorld::IdCollection<CSMWorld::Cell>& cellCollection = document.getData().getCells();\n    return cellCollection.searchId (cellId) == -1;\n}\n\nbool CSVRender::TerrainSelection::noLand(const std::string& cellId)\n{\n    CSMDoc::Document& document = mWorldspaceWidget->getDocument();\n    const CSMWorld::IdCollection<CSMWorld::Land>& landCollection = document.getData().getLand();\n    return landCollection.searchId (cellId) == -1;\n}\n\nbool CSVRender::TerrainSelection::noLandLoaded(const std::string& cellId)\n{\n    CSMDoc::Document& document = mWorldspaceWidget->getDocument();\n    const CSMWorld::IdCollection<CSMWorld::Land>& landCollection = document.getData().getLand();\n    return !landCollection.getRecord(cellId).get().isDataLoaded(ESM::Land::DATA_VNML);\n}\n\nbool CSVRender::TerrainSelection::isLandLoaded(const std::string& cellId)\n{\n    if (!noCell(cellId) && !noLand(cellId) && !noLandLoaded(cellId)) return true;\n    return false;\n}\n\nint CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global vertex coordinates\n{\n    int cellX = std::floor(static_cast<float>(x) / (ESM::Land::LAND_SIZE - 1));\n    int cellY = std::floor(static_cast<float>(y) / (ESM::Land::LAND_SIZE - 1));\n    int localX = x - cellX * (ESM::Land::LAND_SIZE - 1);\n    int localY = y - cellY * (ESM::Land::LAND_SIZE - 1);\n\n    CSMWorld::CellCoordinates coords (cellX, cellY);\n\n    float landHeight = 0.f;\n    if (CSVRender::Cell* cell = dynamic_cast<CSVRender::Cell*>(mWorldspaceWidget->getCell(coords)))\n    {\n        landHeight = cell->getSumOfAlteredAndTrueHeight(cellX, cellY, localX, localY);\n    }\n    else if (isLandLoaded(CSMWorld::CellCoordinates::generateId(cellX, cellY)))\n    {\n        CSMDoc::Document& document = mWorldspaceWidget->getDocument();\n        CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n        std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);\n        int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n        const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n        return mPointer[localY*ESM::Land::LAND_SIZE + localX];\n    }\n\n    return landHeight;\n}\n"
  },
  {
    "path": "apps/opencs/view/render/terrainselection.hpp",
    "content": "#ifndef CSV_RENDER_TERRAINSELECTION_H\n#define CSV_RENDER_TERRAINSELECTION_H\n\n#include <utility>\n#include <vector>\n\n#include <osg/Vec3d>\n#include <osg/ref_ptr>\n#include <osg/PositionAttitudeTransform>\n\n#include <components/esm/loadland.hpp>\n#include \"../../model/world/cellcoordinates.hpp\"\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace CSVRender\n{\n    struct WorldspaceHitResult;\n    class WorldspaceWidget;\n\n    enum class TerrainSelectionType\n    {\n        Texture,\n        Shape\n    };\n\n    enum class SelectionMethod\n    {\n        OnlySelect,\n        AddSelect,\n        RemoveSelect,\n        ToggleSelect\n    };\n\n    /// \\brief Class handling the terrain selection data and rendering\n    class TerrainSelection\n    {\n        public:\n\n            TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type);\n            ~TerrainSelection();\n\n            void onlySelect(const std::vector<std::pair<int, int>> &localPositions);\n            void addSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress);\n            void removeSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress);\n            void toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress);\n\n            void activate();\n            void deactivate();\n\n            std::vector<std::pair<int, int>> getTerrainSelection() const;\n\n            void update();\n\n        protected:\n\n            void drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices);\n            void drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices);\n\n            int calculateLandHeight(int x, int y);\n\n        private:\n\n            void handleSelection(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod);\n\n            bool noCell(const std::string& cellId);\n\n            bool noLand(const std::string& cellId);\n\n            bool noLandLoaded(const std::string& cellId);\n\n            bool isLandLoaded(const std::string& cellId);\n            \n            osg::Group* mParentNode;\n            WorldspaceWidget *mWorldspaceWidget;\n            osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;\n            osg::ref_ptr<osg::Geometry> mGeometry;\n            osg::ref_ptr<osg::Group> mSelectionNode;\n            std::vector<std::pair<int, int>> mSelection; // Global terrain selection coordinate in either vertex or texture units\n            std::vector<std::pair<int, int>> mTemporarySelection; // Used during toggle to compare the most recent drag operation\n            bool mDraggedOperationFlag; //true during drag operation, false when click-operation\n            TerrainSelectionType mSelectionType;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/terrainshapemode.cpp",
    "content": "#include \"terrainshapemode.hpp\"\n\n#include <algorithm>\n#include <string>\n#include <sstream>\n#include <memory>\n\n#include <QWidget>\n#include <QIcon>\n#include <QEvent>\n#include <QDropEvent>\n#include <QDragEnterEvent>\n#include <QDrag>\n\n#include <osg/Group>\n#include <osg/Vec3f>\n\n#include <components/esm/loadland.hpp>\n#include <components/debug/debuglog.hpp>\n\n#include \"../widget/brushshapes.hpp\"\n#include \"../widget/modebutton.hpp\"\n#include \"../widget/scenetoolbar.hpp\"\n#include \"../widget/scenetoolshapebrush.hpp\"\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/idtree.hpp\"\n#include \"../../model/world/land.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n#include \"../../model/world/universalid.hpp\"\n\n#include \"brushdraw.hpp\"\n#include \"commands.hpp\"\n#include \"editmode.hpp\"\n#include \"pagedworldspacewidget.hpp\"\n#include \"mask.hpp\"\n#include \"tagbase.hpp\"\n#include \"terrainselection.hpp\"\n#include \"worldspacewidget.hpp\"\n\nCSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)\n: EditMode (worldspaceWidget, QIcon {\":scenetoolbar/editing-terrain-shape\"}, Mask_Terrain, \"Terrain land editing\", parent),\n    mParentNode(parentNode)\n{\n}\n\nvoid CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar)\n{\n    if (!mTerrainShapeSelection)\n    {\n        mTerrainShapeSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Shape));\n    }\n\n    if(!mShapeBrushScenetool)\n    {\n        mShapeBrushScenetool = new CSVWidget::SceneToolShapeBrush (toolbar, \"scenetoolshapebrush\", getWorldspaceWidget().getDocument());\n        connect(mShapeBrushScenetool, SIGNAL (clicked()), mShapeBrushScenetool, SLOT (activate()));\n        connect(mShapeBrushScenetool->mShapeBrushWindow, SIGNAL(passBrushSize(int)), this, SLOT(setBrushSize(int)));\n        connect(mShapeBrushScenetool->mShapeBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setBrushShape(CSVWidget::BrushShape)));\n        connect(mShapeBrushScenetool->mShapeBrushWindow->mSizeSliders->mBrushSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBrushSize(int)));\n        connect(mShapeBrushScenetool->mShapeBrushWindow->mToolSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(setShapeEditTool(int)));\n        connect(mShapeBrushScenetool->mShapeBrushWindow->mToolStrengthSlider, SIGNAL(valueChanged(int)), this, SLOT(setShapeEditToolStrength(int)));\n    }\n\n    if (!mBrushDraw)\n        mBrushDraw.reset(new BrushDraw(mParentNode));\n\n    EditMode::activate(toolbar);\n    toolbar->addTool (mShapeBrushScenetool);\n}\n\nvoid CSVRender::TerrainShapeMode::deactivate(CSVWidget::SceneToolbar* toolbar)\n{\n    if(mShapeBrushScenetool)\n    {\n        toolbar->removeTool (mShapeBrushScenetool);\n    }\n\n    if (mTerrainShapeSelection)\n    {\n        mTerrainShapeSelection.reset();\n    }\n\n    if (mBrushDraw)\n        mBrushDraw.reset();\n\n    EditMode::deactivate(toolbar);\n}\n\nvoid CSVRender::TerrainShapeMode::primaryOpenPressed (const WorldspaceHitResult& hit) // Apply changes here\n{\n}\n\nvoid CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult& hit)\n{\n    if (hit.hit && hit.tag == nullptr)\n    {\n        if (mShapeEditTool == ShapeEditTool_Flatten)\n            setFlattenToolTargetHeight(hit);\n        if (mDragMode == InteractionType_PrimaryEdit && mShapeEditTool != ShapeEditTool_Drag)\n        {\n            editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true);\n            applyTerrainEditChanges();\n        }\n\n        if (mDragMode == InteractionType_PrimarySelect)\n        {\n            selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true);\n        }\n\n        if (mDragMode == InteractionType_SecondarySelect)\n        {\n            selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true);\n        }\n    }\n    clearTransientEdits();\n}\n\nvoid CSVRender::TerrainShapeMode::primarySelectPressed(const WorldspaceHitResult& hit)\n{\n    if(hit.hit && hit.tag == nullptr)\n    {\n        selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, false);\n    }\n}\n\nvoid CSVRender::TerrainShapeMode::secondarySelectPressed(const WorldspaceHitResult& hit)\n{\n    if(hit.hit && hit.tag == nullptr)\n    {\n        selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, false);\n    }\n}\n\nbool CSVRender::TerrainShapeMode::primaryEditStartDrag (const QPoint& pos)\n{\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n\n    mDragMode = InteractionType_PrimaryEdit;\n\n    if (hit.hit && hit.tag == nullptr)\n    {\n        mEditingPos = hit.worldPos;\n        mIsEditing = true;\n        if (mShapeEditTool == ShapeEditTool_Flatten)\n            setFlattenToolTargetHeight(hit);\n    }\n\n    return true;\n}\n\nbool CSVRender::TerrainShapeMode::secondaryEditStartDrag (const QPoint& pos)\n{\n    return false;\n}\n\nbool CSVRender::TerrainShapeMode::primarySelectStartDrag (const QPoint& pos)\n{\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n    mDragMode = InteractionType_PrimarySelect;\n    if (!hit.hit || hit.tag != nullptr)\n    {\n        mDragMode = InteractionType_None;\n        return false;\n    }\n    selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true);\n    return false;\n}\n\nbool CSVRender::TerrainShapeMode::secondarySelectStartDrag (const QPoint& pos)\n{\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n    mDragMode = InteractionType_SecondarySelect;\n    if (!hit.hit || hit.tag != nullptr)\n    {\n        mDragMode = InteractionType_None;\n        return false;\n    }\n    selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true);\n    return false;\n}\n\nvoid CSVRender::TerrainShapeMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)\n{\n    if (mDragMode == InteractionType_PrimaryEdit)\n    {\n        WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n        mTotalDiffY += diffY;\n        if (mIsEditing)\n        {\n            if (mShapeEditTool == ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(mEditingPos), true);\n                else editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true);\n        }\n    }\n\n    if (mDragMode == InteractionType_PrimarySelect)\n    {\n        WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n        if (hit.hit && hit.tag == nullptr) selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true);\n    }\n\n    if (mDragMode == InteractionType_SecondarySelect)\n    {\n        WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n        if (hit.hit && hit.tag == nullptr) selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true);\n    }\n}\n\nvoid CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos)\n{\n    if (mDragMode == InteractionType_PrimaryEdit)\n    {\n        applyTerrainEditChanges();\n        clearTransientEdits();\n    }\n}\n\n\nvoid CSVRender::TerrainShapeMode::dragAborted()\n{\n     clearTransientEdits();\n}\n\nvoid CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor)\n{\n}\n\nvoid CSVRender::TerrainShapeMode::sortAndLimitAlteredCells()\n{\n    bool passing = false;\n    int passes = 0;\n\n    std::sort(mAlteredCells.begin(), mAlteredCells.end());\n    mAlteredCells.erase(std::unique(mAlteredCells.begin(), mAlteredCells.end()), mAlteredCells.end());\n\n    while (!passing) // Multiple passes are needed when steepness problems arise for both x and y axis simultaneously\n    {\n        passing = true;\n        for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)\n        {\n            limitAlteredHeights(cellCoordinates);\n        }\n        std::reverse(mAlteredCells.begin(), mAlteredCells.end()); //Instead of alphabetical order, this should be fixed to sort cells by cell coordinates\n        for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)\n        {\n            if (!limitAlteredHeights(cellCoordinates, true)) passing = false;\n        }\n        ++passes;\n        if (passes > 2)\n        {\n            Log(Debug::Warning) << \"Warning: User edit exceeds accepted slope steepness. Automatic limiting has failed, edit has been discarded.\";\n            clearTransientEdits();\n            return;\n        }\n    }\n}\n\nvoid CSVRender::TerrainShapeMode::clearTransientEdits()\n{\n    mTotalDiffY = 0;\n    mIsEditing = false;\n    mAlteredCells.clear();\n    if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n        paged->resetAllAlteredHeights();\n    mTerrainShapeSelection->update();\n}\n\nvoid CSVRender::TerrainShapeMode::applyTerrainEditChanges()\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));\n\n    int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n    int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex);\n\n    QUndoStack& undoStack = document.getUndoStack();\n\n    sortAndLimitAlteredCells();\n\n    undoStack.beginMacro (\"Edit shape and normal records\");\n\n    // One command at the beginning of the macro for redrawing the terrain-selection grid when undoing the changes.\n    undoStack.push(new DrawTerrainSelectionCommand(&getWorldspaceWidget()));\n\n    for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)\n    {\n        std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY());\n        undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));\n        const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n        CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer);\n        CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget());\n\n        // Generate land height record\n        for(int i = 0; i < ESM::Land::LAND_SIZE; ++i)\n        {\n            for(int j = 0; j < ESM::Land::LAND_SIZE; ++j)\n            {\n                if (paged && paged->getCellAlteredHeight(cellCoordinates, i, j))\n                    landShapeNew[j * ESM::Land::LAND_SIZE + i] = landShapePointer[j * ESM::Land::LAND_SIZE + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j);\n                else\n                    landShapeNew[j * ESM::Land::LAND_SIZE + i] = 0;\n            }\n        }\n\n        pushEditToCommand(landShapeNew, document, landTable, cellId);\n    }\n\n    for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)\n    {\n        std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY());\n        const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n        const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY()), landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n        const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1), landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n        const CSMWorld::LandNormalsColumn::DataType landNormalsPointer = landTable.data(landTable.getModelIndex(cellId, landnormalsColumn)).value<CSMWorld::LandNormalsColumn::DataType>();\n        CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer);\n\n        // Generate land normals record\n        for(int i = 0; i < ESM::Land::LAND_SIZE; ++i)\n        {\n            for(int j = 0; j < ESM::Land::LAND_SIZE; ++j)\n            {\n                osg::Vec3f v1(128, 0, 0);\n                osg::Vec3f v2(0, 128, 0);\n\n                if (i < ESM::Land::LAND_SIZE - 1) v1.z() = landShapePointer[j * ESM::Land::LAND_SIZE + i + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i];\n                else\n                {\n                    std::string shiftedCellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY());\n                    if (isLandLoaded(shiftedCellId))\n                        v1.z() = landRightShapePointer[j * ESM::Land::LAND_SIZE + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i];\n                }\n\n                if (j < ESM::Land::LAND_SIZE - 1) v2.z() = landShapePointer[(j + 1) * ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i];\n                else\n                {\n                    std::string shiftedCellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1);\n                    if (isLandLoaded(shiftedCellId))\n                        v2.z() = landDownShapePointer[ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i];\n                }\n\n                osg::Vec3f normal = v1 ^ v2;\n                const float hyp = normal.length() / 127.0f;\n\n                normal /= hyp;\n\n                landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 0] = normal.x();\n                landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 1] = normal.y();\n                landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = normal.z();\n            }\n        }\n        pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId);\n    }\n    // One command at the end of the macro for redrawing the terrain-selection grid when redoing the changes.\n    undoStack.push(new DrawTerrainSelectionCommand(&getWorldspaceWidget()));\n\n    undoStack.endMacro();\n    clearTransientEdits();\n}\n\nfloat CSVRender::TerrainShapeMode::calculateBumpShape(float distance, int radius, float height)\n{\n    float distancePerRadius = distance / radius;\n    return height - height * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius);\n}\n\nvoid CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair<int, int>& vertexCoords, bool dragOperation)\n{\n    int r = mBrushSize / 2;\n    if (r == 0) r = 1; // Prevent division by zero later, which might happen when mBrushSize == 1\n\n    if (CSVRender::PagedWorldspaceWidget *paged =\n        dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        if (mShapeEditTool == ShapeEditTool_Drag) paged->resetAllAlteredHeights();\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Point)\n    {\n        std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords);\n        CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first;\n        int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first);\n        int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second);\n        if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY);\n        if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower)\n        {\n            alterHeight(cellCoords, x, y, mShapeEditToolStrength);\n            float smoothMultiplier = static_cast<float>(CSMPrefs::get()[\"3D Scene Editing\"][\"landedit-post-smoothstrength\"].toDouble());\n            if (CSMPrefs::get()[\"3D Scene Editing\"][\"landedit-post-smoothpainting\"].isTrue()) smoothHeight(cellCoords, x, y, mShapeEditToolStrength * smoothMultiplier);\n        }\n        if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength);\n        if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight);\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Square)\n    {\n        for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i)\n        {\n            for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j)\n            {\n                std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j));\n                CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first;\n                int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i);\n                int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j);\n                if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY);\n                if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower)\n                {\n                    alterHeight(cellCoords, x, y, mShapeEditToolStrength);\n                    float smoothMultiplier = static_cast<float>(CSMPrefs::get()[\"3D Scene Editing\"][\"landedit-post-smoothstrength\"].toDouble());\n                    if (CSMPrefs::get()[\"3D Scene Editing\"][\"landedit-post-smoothpainting\"].isTrue()) smoothHeight(cellCoords, x, y, mShapeEditToolStrength * smoothMultiplier);\n                }\n                if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength);\n                if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight);\n            }\n        }\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Circle)\n    {\n        for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i)\n        {\n            for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j)\n            {\n                std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j));\n                CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first;\n                int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i);\n                int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j);\n                int distanceX = abs(i - vertexCoords.first);\n                int distanceY = abs(j - vertexCoords.second);\n                float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2));\n                float smoothedByDistance = 0.0f;\n                if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = calculateBumpShape(distance, r, mTotalDiffY);\n                if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = calculateBumpShape(distance, r, r + mShapeEditToolStrength);\n\n                // Using floating-point radius here to prevent selecting too few vertices.\n                if (distance <= mBrushSize / 2.0f)\n                {\n                    if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, smoothedByDistance);\n                    if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower)\n                    {\n                        alterHeight(cellCoords, x, y, smoothedByDistance);\n                        float smoothMultiplier = static_cast<float>(CSMPrefs::get()[\"3D Scene Editing\"][\"landedit-post-smoothstrength\"].toDouble());\n                        if (CSMPrefs::get()[\"3D Scene Editing\"][\"landedit-post-smoothpainting\"].isTrue()) smoothHeight(cellCoords, x, y, mShapeEditToolStrength * smoothMultiplier);\n                    }\n                    if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength);\n                    if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight);\n                }\n            }\n        }\n    }\n    if (mBrushShape == CSVWidget::BrushShape_Custom)\n    {\n        if(!mCustomBrushShape.empty())\n        {\n            for(auto const& value: mCustomBrushShape)\n            {\n                std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(vertexCoords.first + value.first, vertexCoords.second + value.second));\n                CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first;\n                int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first + value.first);\n                int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second + value.second);\n                if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY);\n                if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower)\n                {\n                    alterHeight(cellCoords, x, y, mShapeEditToolStrength);\n                    float smoothMultiplier = static_cast<float>(CSMPrefs::get()[\"3D Scene Editing\"][\"landedit-post-smoothstrength\"].toDouble());\n                    if (CSMPrefs::get()[\"3D Scene Editing\"][\"landedit-post-smoothpainting\"].isTrue()) smoothHeight(cellCoords, x, y, mShapeEditToolStrength * smoothMultiplier);\n                }\n                if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength);\n                if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight);\n            }\n        }\n    }\n    mTerrainShapeSelection->update();\n}\n\nvoid CSVRender::TerrainShapeMode::setFlattenToolTargetHeight(const WorldspaceHitResult& hit)\n{\n    std::pair<int, int> vertexCoords = CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos);\n    std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords);\n    int inCellX = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first);\n    int inCellY = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second);\n\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n    const CSMWorld::LandHeightsColumn::DataType landShapePointer =\n        landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n    mTargetHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX];\n}\n\n\nvoid CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight, bool useTool)\n{\n    std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());\n\n    if (!(allowLandShapeEditing(cellId, useTool) && (useTool || (isLandLoaded(cellId)))))\n        return;\n    CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget());\n    if (!paged)\n        return;\n\n    std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY());\n    std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY());\n    std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1);\n    std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1);\n    std::string cellUpLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() - 1);\n    std::string cellUpRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY() - 1);\n    std::string cellDownLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() + 1);\n    std::string cellDownRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY() + 1);\n\n    if (useTool)\n    {\n        mAlteredCells.emplace_back(cellCoords);\n        if (mShapeEditTool == ShapeEditTool_Drag)\n        {\n            // Get distance from modified land, alter land change based on zoom\n            osg::Vec3d eye, center, up;\n            paged->getCamera()->getViewMatrixAsLookAt(eye, center, up);\n            osg::Vec3d distance = eye - mEditingPos;\n            alteredHeight = alteredHeight * (distance.length() / 500);\n        }\n        if (mShapeEditTool == ShapeEditTool_PaintToRaise) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight;\n        if (mShapeEditTool == ShapeEditTool_PaintToLower) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) - alteredHeight;\n        if (mShapeEditTool == ShapeEditTool_Smooth) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight;\n    }\n\n    if (inCellX != 0 && inCellY != 0 && inCellX != ESM::Land::LAND_SIZE - 1 && inCellY != ESM::Land::LAND_SIZE - 1)\n    paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);\n\n    // Change values of cornering cells\n    if ((inCellX == 0 && inCellY == 0) && (useTool || isLandLoaded(cellUpLeftId)))\n    {\n        if(allowLandShapeEditing(cellUpLeftId, useTool) && allowLandShapeEditing(cellLeftId, useTool) && allowLandShapeEditing(cellUpId, useTool))\n        {\n            CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, -1);\n            if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end())\n            mAlteredCells.emplace_back(cornerCellCoords);\n            paged->setCellAlteredHeight(cornerCellCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE - 1, alteredHeight);\n        } else return;\n    }\n    else if ((inCellX == 0 && inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownLeftId)))\n    {\n        if (allowLandShapeEditing(cellDownLeftId, useTool) && allowLandShapeEditing(cellLeftId, useTool) && allowLandShapeEditing(cellDownId, useTool))\n        {\n            CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, 1);\n            if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end())\n            mAlteredCells.emplace_back(cornerCellCoords);\n            paged->setCellAlteredHeight(cornerCellCoords, ESM::Land::LAND_SIZE - 1, 0, alteredHeight);\n        } else return;\n    }\n    else if ((inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == 0) && (useTool || isLandLoaded(cellUpRightId)))\n    {\n        if (allowLandShapeEditing(cellUpRightId, useTool) && allowLandShapeEditing(cellRightId, useTool) && allowLandShapeEditing(cellUpId, useTool))\n        {\n            CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, -1);\n            if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end())\n            mAlteredCells.emplace_back(cornerCellCoords);\n            paged->setCellAlteredHeight(cornerCellCoords, 0, ESM::Land::LAND_SIZE - 1, alteredHeight);\n        } else return;\n    }\n    else if ((inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownRightId)))\n    {\n        if(allowLandShapeEditing(cellDownRightId, useTool) && allowLandShapeEditing(cellRightId, useTool) && allowLandShapeEditing(cellDownId, useTool))\n        {\n            CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, 1);\n            if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end())\n            mAlteredCells.emplace_back(cornerCellCoords);\n            paged->setCellAlteredHeight(cornerCellCoords, 0, 0, alteredHeight);\n        } else return;\n    }\n\n    // Change values of edging cells\n    if ((inCellX == 0) && (useTool || isLandLoaded(cellLeftId)))\n    {\n        if(allowLandShapeEditing(cellLeftId, useTool))\n        {\n            CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(-1, 0);\n            if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())\n            mAlteredCells.emplace_back(edgeCellCoords);\n            paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);\n            paged->setCellAlteredHeight(edgeCellCoords, ESM::Land::LAND_SIZE - 1, inCellY, alteredHeight);\n        }\n    }\n    if ((inCellY == 0) && (useTool || isLandLoaded(cellUpId)))\n    {\n        if(allowLandShapeEditing(cellUpId, useTool))\n        {\n            CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, -1);\n            if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())\n            mAlteredCells.emplace_back(edgeCellCoords);\n            paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);\n            paged->setCellAlteredHeight(edgeCellCoords, inCellX, ESM::Land::LAND_SIZE - 1, alteredHeight);\n        }\n    }\n\n    if ((inCellX == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellRightId)))\n    {\n        if(allowLandShapeEditing(cellRightId, useTool))\n        {\n            CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(1, 0);\n            if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())\n            mAlteredCells.emplace_back(edgeCellCoords);\n            paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);\n            paged->setCellAlteredHeight(edgeCellCoords, 0, inCellY, alteredHeight);\n        }\n    }\n    if ((inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownId)))\n    {\n        if(allowLandShapeEditing(cellDownId, useTool))\n        {\n            CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, 1);\n            if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())\n            mAlteredCells.emplace_back(edgeCellCoords);\n            paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);\n            paged->setCellAlteredHeight(edgeCellCoords, inCellX, 0, alteredHeight);\n        }\n    }\n\n}\n\nvoid CSVRender::TerrainShapeMode::smoothHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength)\n{\n    if (CSVRender::PagedWorldspaceWidget *paged =\n        dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n        CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n            *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n        int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n\n        std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());\n        const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n        // ### Variable naming key ###\n        // Variables here hold either the real value, or the altered value of current edit.\n        // this = this Cell\n        // left = x - 1, up = y - 1, right = x + 1, down = y + 1\n        // Altered = transient edit (in current edited)\n        float thisAlteredHeight = 0.0f;\n        if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) != nullptr)\n            thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY);\n        float thisHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX];\n        float leftHeight = 0.0f;\n        float leftAlteredHeight = 0.0f;\n        float upAlteredHeight = 0.0f;\n        float rightHeight = 0.0f;\n        float rightAlteredHeight = 0.0f;\n        float downHeight = 0.0f;\n        float downAlteredHeight = 0.0f;\n        float upHeight = 0.0f;\n\n        if(allowLandShapeEditing(cellId))\n        {\n            //Get key values for calculating average, handle cell edges, check for null pointers\n            if (inCellX == 0)\n            {\n                cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY());\n                const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n                leftHeight = landLeftShapePointer[inCellY * ESM::Land::LAND_SIZE + (ESM::Land::LAND_SIZE - 2)];\n                if (paged->getCellAlteredHeight(cellCoords.move(-1, 0), inCellX, ESM::Land::LAND_SIZE - 2))\n                    leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY);\n            }\n            if (inCellY == 0)\n            {\n                cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1);\n                const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n                upHeight = landUpShapePointer[(ESM::Land::LAND_SIZE - 2) * ESM::Land::LAND_SIZE + inCellX];\n                if (paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, ESM::Land::LAND_SIZE - 2))\n                    upAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, ESM::Land::LAND_SIZE - 2);\n            }\n            if (inCellX > 0)\n            {\n                leftHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX - 1];\n                leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY);\n            }\n            if (inCellY > 0)\n            {\n                upHeight = landShapePointer[(inCellY - 1) * ESM::Land::LAND_SIZE + inCellX];\n                upAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1);\n            }\n            if (inCellX == ESM::Land::LAND_SIZE - 1)\n            {\n                cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY());\n                const CSMWorld::LandHeightsColumn::DataType landRightShapePointer =\n                    landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n                rightHeight = landRightShapePointer[inCellY * ESM::Land::LAND_SIZE + 1];\n                if (paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY))\n                {\n                    rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY);\n                }\n            }\n            if (inCellY == ESM::Land::LAND_SIZE - 1)\n            {\n                cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1);\n                const CSMWorld::LandHeightsColumn::DataType landDownShapePointer =\n                    landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n                downHeight = landDownShapePointer[1 * ESM::Land::LAND_SIZE + inCellX];\n                if (paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1))\n                {\n                    downAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1);\n                }\n            }\n            if (inCellX < ESM::Land::LAND_SIZE - 1)\n            {\n                rightHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX + 1];\n                if(paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY))\n                    rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY);\n            }\n            if (inCellY < ESM::Land::LAND_SIZE - 1)\n            {\n                downHeight = landShapePointer[(inCellY + 1) * ESM::Land::LAND_SIZE + inCellX];\n                if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1))\n                    downAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1);\n            }\n\n            float averageHeight = (upHeight + downHeight + rightHeight + leftHeight +\n                upAlteredHeight + downAlteredHeight + rightAlteredHeight + leftAlteredHeight) / 4;\n            if ((thisHeight + thisAlteredHeight) != averageHeight) mAlteredCells.emplace_back(cellCoords);\n            if (toolStrength > abs(thisHeight + thisAlteredHeight - averageHeight)) toolStrength = abs(thisHeight + thisAlteredHeight - averageHeight);\n            if (thisHeight + thisAlteredHeight > averageHeight) alterHeight(cellCoords, inCellX, inCellY, - toolStrength);\n            if (thisHeight + thisAlteredHeight < averageHeight) alterHeight(cellCoords, inCellX, inCellY, + toolStrength);\n        }\n    }\n}\n\nvoid CSVRender::TerrainShapeMode::flattenHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength, int targetHeight)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n\n    float thisHeight = 0.0f;\n    float thisAlteredHeight = 0.0f;\n\n    std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());\n\n    if (CSVRender::PagedWorldspaceWidget *paged =\n        dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        if (!noCell(cellId) && !noLand(cellId))\n        {\n            const CSMWorld::LandHeightsColumn::DataType landShapePointer =\n                landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n            if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY))\n                thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY);\n            thisHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX];\n        }\n    }\n\n    if (toolStrength > abs(thisHeight - targetHeight) && toolStrength > 8.0f) toolStrength =\n        abs(thisHeight - targetHeight); //Cut down excessive changes\n    if (thisHeight + thisAlteredHeight > targetHeight) alterHeight(cellCoords, inCellX, inCellY, thisAlteredHeight - toolStrength);\n    if (thisHeight + thisAlteredHeight < targetHeight) alterHeight(cellCoords, inCellX, inCellY, thisAlteredHeight + toolStrength);\n}\n\nvoid CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight,\n    float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight, float* rightHeight,\n    float* rightAlteredHeight, float* downHeight, float* downAlteredHeight)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n\n    std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());\n    std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY());\n    std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1);\n    std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY());\n    std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1);\n\n    *thisHeight = 0.0f; // real + altered height\n    *thisAlteredHeight = 0.0f;  // only altered height\n    *leftHeight = 0.0f;\n    *leftAlteredHeight = 0.0f;\n    *upHeight = 0.0f;\n    *upAlteredHeight = 0.0f;\n    *rightHeight = 0.0f;\n    *rightAlteredHeight = 0.0f;\n    *downHeight = 0.0f;\n    *downAlteredHeight = 0.0f;\n\n    if (CSVRender::PagedWorldspaceWidget *paged =\n        dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        if (!noCell(cellId) && !noLand(cellId))\n        {\n            const CSMWorld::LandHeightsColumn::DataType landShapePointer =\n                landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n            if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY))\n                *thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY);\n            *thisHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX] + *thisAlteredHeight;\n\n            // Default to the same value as thisHeight, which happens in the case of cell edge where next cell/land is not found,\n            // which is to prevent unnecessary action at limitHeightChange().\n            *leftHeight = *thisHeight;\n            *upHeight = *thisHeight;\n            *rightHeight = *thisHeight;\n            *downHeight = *thisHeight;\n\n            //If at edge, get values from neighboring cell\n            if (inCellX == 0)\n            {\n                if(isLandLoaded(cellLeftId))\n                {\n                    const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer =\n                        landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n                    *leftHeight = landLeftShapePointer[inCellY * ESM::Land::LAND_SIZE + (ESM::Land::LAND_SIZE - 2)];\n                    if (paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY))\n                    {\n                        *leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY);\n                        *leftHeight += *leftAlteredHeight;\n                    }\n                }\n            }\n            if (inCellY == 0)\n            {\n                if(isLandLoaded(cellUpId))\n                {\n                    const CSMWorld::LandHeightsColumn::DataType landUpShapePointer =\n                        landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n                    *upHeight = landUpShapePointer[(ESM::Land::LAND_SIZE - 2) * ESM::Land::LAND_SIZE + inCellX];\n                    if (paged->getCellAlteredHeight(cellCoords.move(0,-1), inCellX, ESM::Land::LAND_SIZE - 2))\n                    {\n                        *upAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, ESM::Land::LAND_SIZE - 2);\n                        *upHeight += *upAlteredHeight;\n                    }\n                }\n            }\n            if (inCellX == ESM::Land::LAND_SIZE - 1)\n            {\n                if(isLandLoaded(cellRightId))\n                {\n                    const CSMWorld::LandHeightsColumn::DataType landRightShapePointer =\n                        landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n                    *rightHeight = landRightShapePointer[inCellY * ESM::Land::LAND_SIZE + 1];\n                    if (paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY))\n                    {\n                        *rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY);\n                        *rightHeight += *rightAlteredHeight;\n                    }\n                }\n            }\n            if (inCellY == ESM::Land::LAND_SIZE - 1)\n            {\n                if(isLandLoaded(cellDownId))\n                {\n                    const CSMWorld::LandHeightsColumn::DataType landDownShapePointer =\n                        landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n                    *downHeight = landDownShapePointer[ESM::Land::LAND_SIZE + inCellX];\n                    if (paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1))\n                    {\n                        *downAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1);\n                        *downHeight += *downAlteredHeight;\n                    }\n                }\n            }\n\n           //If not at edge, get values from the same cell\n            if (inCellX != 0)\n            {\n                *leftHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX - 1];\n                if (paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY))\n                    *leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY);\n                *leftHeight += *leftAlteredHeight;\n            }\n            if (inCellY != 0)\n            {\n                *upHeight = landShapePointer[(inCellY - 1) * ESM::Land::LAND_SIZE + inCellX];\n                if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1))\n                    *upAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1);\n                *upHeight += *upAlteredHeight;\n            }\n            if (inCellX != ESM::Land::LAND_SIZE - 1)\n            {\n                *rightHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX + 1];\n                if (paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY))\n                    *rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY);\n                *rightHeight += *rightAlteredHeight;\n            }\n            if (inCellY != ESM::Land::LAND_SIZE - 1)\n            {\n                *downHeight = landShapePointer[(inCellY + 1) * ESM::Land::LAND_SIZE + inCellX];\n                if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1))\n                    *downAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1);\n                *downHeight += *downAlteredHeight;\n            }\n\n        }\n    }\n}\n\nvoid CSVRender::TerrainShapeMode::compareAndLimit(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* limitedAlteredHeightXAxis, float* limitedAlteredHeightYAxis, bool* steepnessIsWithinLimits)\n{\n    if (limitedAlteredHeightXAxis)\n    {\n        if (limitedAlteredHeightYAxis)\n        {\n            if(std::abs(*limitedAlteredHeightXAxis) >= std::abs(*limitedAlteredHeightYAxis))\n            {\n                alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false);\n                *steepnessIsWithinLimits = false;\n            }\n            else\n            {\n                alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false);\n                *steepnessIsWithinLimits = false;\n            }\n        }\n        else\n        {\n            alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false);\n            *steepnessIsWithinLimits = false;\n        }\n    }\n    else if (limitedAlteredHeightYAxis)\n    {\n        alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false);\n        *steepnessIsWithinLimits = false;\n    }\n}\n\nbool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (*document.getData().getTableModel(CSMWorld::UniversalId::Type_Land));\n    int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n\n    std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());\n\n    int limitHeightChange = 1016.0f; // Limited by save format\n    bool steepnessIsWithinLimits = true;\n\n    if (isLandLoaded(cellId))\n    {\n        const CSMWorld::LandHeightsColumn::DataType landShapePointer =\n            landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n        float thisHeight = 0.0f;\n        float thisAlteredHeight = 0.0f;\n        float leftHeight = 0.0f;\n        float leftAlteredHeight = 0.0f;\n        float upHeight = 0.0f;\n        float upAlteredHeight = 0.0f;\n        float rightHeight = 0.0f;\n        float rightAlteredHeight = 0.0f;\n        float downHeight = 0.0f;\n        float downAlteredHeight = 0.0f;\n\n        if (!reverseMode)\n        {\n            for(int inCellY = 0; inCellY < ESM::Land::LAND_SIZE; ++inCellY)\n            {\n                for(int inCellX = 0; inCellX < ESM::Land::LAND_SIZE; ++inCellX)\n                {\n                    std::unique_ptr<float> limitedAlteredHeightXAxis(nullptr);\n                    std::unique_ptr<float> limitedAlteredHeightYAxis(nullptr);\n                    updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, &leftAlteredHeight,\n                        &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight, &downAlteredHeight);\n\n                    // Check for height limits on x-axis\n                    if (leftHeight - thisHeight > limitHeightChange)\n                        limitedAlteredHeightXAxis.reset(new float(leftHeight - limitHeightChange - (thisHeight - thisAlteredHeight)));\n                    else if (leftHeight - thisHeight < -limitHeightChange)\n                        limitedAlteredHeightXAxis.reset(new float(leftHeight + limitHeightChange - (thisHeight - thisAlteredHeight)));\n\n                    // Check for height limits on y-axis\n                    if (upHeight - thisHeight > limitHeightChange)\n                        limitedAlteredHeightYAxis.reset(new float(upHeight - limitHeightChange - (thisHeight - thisAlteredHeight)));\n                    else if (upHeight - thisHeight < -limitHeightChange)\n                        limitedAlteredHeightYAxis.reset(new float(upHeight + limitHeightChange - (thisHeight - thisAlteredHeight)));\n\n                    // Limit altered height value based on x or y, whichever is the smallest\n                    compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis.get(), limitedAlteredHeightYAxis.get(), &steepnessIsWithinLimits);\n                }\n            }\n        }\n\n        if (reverseMode)\n        {\n            for(int inCellY = ESM::Land::LAND_SIZE - 1; inCellY >= 0; --inCellY)\n            {\n                for(int inCellX = ESM::Land::LAND_SIZE - 1; inCellX >= 0; --inCellX)\n                {\n                    std::unique_ptr<float> limitedAlteredHeightXAxis(nullptr);\n                    std::unique_ptr<float> limitedAlteredHeightYAxis(nullptr);\n                    updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, &leftAlteredHeight,\n                        &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight, &downAlteredHeight);\n\n                    // Check for height limits on x-axis\n                    if (rightHeight - thisHeight > limitHeightChange)\n                        limitedAlteredHeightXAxis.reset(new float(rightHeight - limitHeightChange - (thisHeight - thisAlteredHeight)));\n                    else if (rightHeight - thisHeight < -limitHeightChange)\n                        limitedAlteredHeightXAxis.reset(new float(rightHeight + limitHeightChange - (thisHeight - thisAlteredHeight)));\n\n                    // Check for height limits on y-axis\n                    if (downHeight - thisHeight > limitHeightChange)\n                        limitedAlteredHeightYAxis.reset(new float(downHeight - limitHeightChange - (thisHeight - thisAlteredHeight)));\n                    else if (downHeight - thisHeight < -limitHeightChange)\n                        limitedAlteredHeightYAxis.reset(new float(downHeight + limitHeightChange - (thisHeight - thisAlteredHeight)));\n\n                    // Limit altered height value based on x or y, whichever is the smallest\n                    compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis.get(), limitedAlteredHeightYAxis.get(), &steepnessIsWithinLimits);\n                }\n            }\n        }\n    }\n    return steepnessIsWithinLimits;\n}\n\nbool CSVRender::TerrainShapeMode::isInCellSelection(int globalSelectionX, int globalSelectionY)\n{\n    if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        std::pair<int, int> vertexCoords = std::make_pair(globalSelectionX, globalSelectionY);\n        std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords);\n        return paged->getCellSelection().has(CSMWorld::CellCoordinates::fromId(cellId).first) && isLandLoaded(cellId);\n    }\n    return false;\n}\n\nvoid CSVRender::TerrainShapeMode::handleSelection(int globalSelectionX, int globalSelectionY, std::vector<std::pair<int, int>>* selections)\n{\n    if (isInCellSelection(globalSelectionX, globalSelectionY)) selections->emplace_back(globalSelectionX, globalSelectionY);\n    else\n    {\n        int moduloX = globalSelectionX % (ESM::Land::LAND_SIZE - 1);\n        int moduloY = globalSelectionY % (ESM::Land::LAND_SIZE - 1);\n        bool xIsAtCellBorder = moduloX == 0;\n        bool yIsAtCellBorder = moduloY == 0;\n        if (!xIsAtCellBorder && !yIsAtCellBorder)\n            return;\n        int selectionX = globalSelectionX;\n        int selectionY = globalSelectionY;\n\n        /*\n            The northern and eastern edges don't belong to the current cell.\n            If the corresponding adjacent cell is not loaded, some special handling is necessary to select border vertices.\n        */\n        if (xIsAtCellBorder && yIsAtCellBorder)\n        {\n            /*\n                Handle the NW, NE, and SE corner vertices.\n                NW corner: (+1, -1) offset to reach current cell.\n                NE corner: (-1, -1) offset to reach current cell.\n                SE corner: (-1, +1) offset to reach current cell.\n            */\n            if (isInCellSelection(globalSelectionX - 1, globalSelectionY - 1)\n                || isInCellSelection(globalSelectionX + 1, globalSelectionY - 1)\n                || isInCellSelection(globalSelectionX - 1, globalSelectionY + 1))\n            {\n                selections->emplace_back(globalSelectionX, globalSelectionY);\n            }\n        }\n        else if (xIsAtCellBorder)\n        {\n            selectionX--;\n        }\n        else if (yIsAtCellBorder)\n        {\n            selectionY--;\n        }\n\n        if (isInCellSelection(selectionX, selectionY))\n            selections->emplace_back(globalSelectionX, globalSelectionY);\n    }\n}\n\nvoid CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>& vertexCoords, unsigned char selectMode, bool dragOperation)\n{\n    int r = mBrushSize / 2;\n    std::vector<std::pair<int, int>> selections;\n\n    if (mBrushShape == CSVWidget::BrushShape_Point)\n    {\n        handleSelection(vertexCoords.first, vertexCoords.second, &selections);\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Square)\n    {\n        for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i)\n        {\n            for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j)\n            {\n                handleSelection(i, j, &selections);\n            }\n        }\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Circle)\n    {\n        for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i)\n        {\n            for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j)\n            {\n                int distanceX = abs(i - vertexCoords.first);\n                int distanceY = abs(j - vertexCoords.second);\n                float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2));\n\n                // Using floating-point radius here to prevent selecting too few vertices.\n                if (distance <= mBrushSize / 2.0f)\n                    handleSelection(i, j, &selections);\n            }\n        }\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Custom)\n    {\n        if(!mCustomBrushShape.empty())\n        {\n            for(auto const& value: mCustomBrushShape)\n            {\n                std::pair<int, int> localVertexCoords (vertexCoords.first + value.first, vertexCoords.second + value.second);\n                handleSelection(localVertexCoords.first, localVertexCoords.second, &selections);\n            }\n        }\n    }\n\n    std::string selectAction;\n\n    if (selectMode == 0)\n        selectAction = CSMPrefs::get()[\"3D Scene Editing\"][\"primary-select-action\"].toString();\n    else\n        selectAction = CSMPrefs::get()[\"3D Scene Editing\"][\"secondary-select-action\"].toString();\n\n    if (selectAction == \"Select only\")\n        mTerrainShapeSelection->onlySelect(selections);\n    else if (selectAction == \"Add to selection\")\n        mTerrainShapeSelection->addSelect(selections, dragOperation);\n    else if (selectAction == \"Remove from selection\")\n        mTerrainShapeSelection->removeSelect(selections, dragOperation);\n    else if (selectAction == \"Invert selection\")\n        mTerrainShapeSelection->toggleSelect(selections, dragOperation);\n}\n\nvoid CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,\n    CSMWorld::IdTable& landTable, const std::string& cellId)\n{\n    QVariant changedLand;\n    changedLand.setValue(newLandGrid);\n\n    QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandHeightsIndex)));\n\n    QUndoStack& undoStack = document.getUndoStack();\n    undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));\n}\n\nvoid CSVRender::TerrainShapeMode::pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document,\n    CSMWorld::IdTable& landTable, const std::string& cellId)\n{\n    QVariant changedLand;\n    changedLand.setValue(newLandGrid);\n\n    QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandNormalsIndex)));\n\n    QUndoStack& undoStack = document.getUndoStack();\n    undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));\n}\n\nbool CSVRender::TerrainShapeMode::noCell(const std::string& cellId)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    const CSMWorld::IdCollection<CSMWorld::Cell>& cellCollection = document.getData().getCells();\n    return cellCollection.searchId (cellId) == -1;\n}\n\nbool CSVRender::TerrainShapeMode::noLand(const std::string& cellId)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    const CSMWorld::IdCollection<CSMWorld::Land>& landCollection = document.getData().getLand();\n    return landCollection.searchId (cellId) == -1;\n}\n\nbool CSVRender::TerrainShapeMode::noLandLoaded(const std::string& cellId)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    const CSMWorld::IdCollection<CSMWorld::Land>& landCollection = document.getData().getLand();\n    return !landCollection.getRecord(cellId).get().isDataLoaded(ESM::Land::DATA_VNML);\n}\n\nbool CSVRender::TerrainShapeMode::isLandLoaded(const std::string& cellId)\n{\n    if (!noCell(cellId) && !noLand(cellId) && !noLandLoaded(cellId)) return true;\n    return false;\n}\n\nvoid CSVRender::TerrainShapeMode::createNewLandData(const CSMWorld::CellCoordinates& cellCoords)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));\n    int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n    int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex);\n\n    float defaultHeight = 0.f;\n    int averageDivider = 0;\n    CSMWorld::CellCoordinates cellLeftCoords = cellCoords.move(-1, 0);\n    CSMWorld::CellCoordinates cellRightCoords = cellCoords.move(1, 0);\n    CSMWorld::CellCoordinates cellUpCoords = cellCoords.move(0, -1);\n    CSMWorld::CellCoordinates cellDownCoords = cellCoords.move(0, 1);\n\n    std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());\n    std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellLeftCoords.getX(), cellLeftCoords.getY());\n    std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellRightCoords.getX(), cellRightCoords.getY());\n    std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellUpCoords.getX(), cellUpCoords.getY());\n    std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellDownCoords.getX(), cellDownCoords.getY());\n\n    float leftCellSampleHeight = 0.0f;\n    float rightCellSampleHeight = 0.0f;\n    float upCellSampleHeight = 0.0f;\n    float downCellSampleHeight = 0.0f;\n\n    const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n    const CSMWorld::LandNormalsColumn::DataType landNormalsPointer = landTable.data(landTable.getModelIndex(cellId, landnormalsColumn)).value<CSMWorld::LandNormalsColumn::DataType>();\n    CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer);\n    CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer);\n\n    if (CSVRender::PagedWorldspaceWidget *paged =\n        dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        if (isLandLoaded(cellLeftId))\n        {\n            const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer =\n                landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n            ++averageDivider;\n            leftCellSampleHeight = landLeftShapePointer[(ESM::Land::LAND_SIZE / 2) * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1];\n            if(paged->getCellAlteredHeight(cellLeftCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE / 2))\n                leftCellSampleHeight += *paged->getCellAlteredHeight(cellLeftCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE / 2);\n        }\n        if (isLandLoaded(cellRightId))\n        {\n            const CSMWorld::LandHeightsColumn::DataType landRightShapePointer =\n                landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n            ++averageDivider;\n            rightCellSampleHeight = landRightShapePointer[(ESM::Land::LAND_SIZE / 2) * ESM::Land::LAND_SIZE];\n            if(paged->getCellAlteredHeight(cellRightCoords, 0, ESM::Land::LAND_SIZE / 2))\n                rightCellSampleHeight += *paged->getCellAlteredHeight(cellRightCoords, 0, ESM::Land::LAND_SIZE / 2);\n        }\n        if (isLandLoaded(cellUpId))\n        {\n            const CSMWorld::LandHeightsColumn::DataType landUpShapePointer =\n                landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n            ++averageDivider;\n            upCellSampleHeight = landUpShapePointer[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + (ESM::Land::LAND_SIZE / 2)];\n            if(paged->getCellAlteredHeight(cellUpCoords, ESM::Land::LAND_SIZE / 2, ESM::Land::LAND_SIZE - 1))\n                upCellSampleHeight += *paged->getCellAlteredHeight(cellUpCoords, ESM::Land::LAND_SIZE / 2, ESM::Land::LAND_SIZE - 1);\n        }\n        if (isLandLoaded(cellDownId))\n        {\n            const CSMWorld::LandHeightsColumn::DataType landDownShapePointer =\n                landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n            ++averageDivider;\n            downCellSampleHeight = landDownShapePointer[ESM::Land::LAND_SIZE / 2];\n            if(paged->getCellAlteredHeight(cellDownCoords, ESM::Land::LAND_SIZE / 2, 0))\n                downCellSampleHeight += *paged->getCellAlteredHeight(cellDownCoords, ESM::Land::LAND_SIZE / 2, 0);\n        }\n    }\n    if (averageDivider > 0) defaultHeight = (leftCellSampleHeight + rightCellSampleHeight + upCellSampleHeight + downCellSampleHeight) / averageDivider;\n\n    for(int i = 0; i < ESM::Land::LAND_SIZE; ++i)\n    {\n        for(int j = 0; j < ESM::Land::LAND_SIZE; ++j)\n        {\n            landShapeNew[j * ESM::Land::LAND_SIZE + i] = defaultHeight;\n            landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 0] = 0;\n            landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 1] = 0;\n            landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = 127;\n        }\n    }\n    QVariant changedShape;\n    changedShape.setValue(landShapeNew);\n    QVariant changedNormals;\n    changedNormals.setValue(landNormalsNew);\n    QModelIndex indexShape(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandHeightsIndex)));\n    QModelIndex indexNormal(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandNormalsIndex)));\n    document.getUndoStack().push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));\n    document.getUndoStack().push (new CSMWorld::ModifyCommand(landTable, indexShape, changedShape));\n    document.getUndoStack().push (new CSMWorld::ModifyCommand(landTable, indexNormal, changedNormals));\n}\n\nbool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellId, bool useTool)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    CSMWorld::IdTree& cellTable = dynamic_cast<CSMWorld::IdTree&> (\n            *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));\n\n    if (noCell(cellId))\n    {\n        std::string mode = CSMPrefs::get()[\"3D Scene Editing\"][\"outside-landedit\"].toString();\n\n        // target cell does not exist\n        if (mode==\"Discard\")\n            return false;\n\n        if (mode==\"Create cell and land, then edit\" && useTool)\n        {\n            std::unique_ptr<CSMWorld::CreateCommand> createCommand (\n                new CSMWorld::CreateCommand (cellTable, cellId));\n            int parentIndex = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);\n            int index = cellTable.findNestedColumnIndex (parentIndex, CSMWorld::Columns::ColumnId_Interior);\n            createCommand->addNestedValue (parentIndex, index, false);\n            document.getUndoStack().push (createCommand.release());\n\n            if (CSVRender::PagedWorldspaceWidget *paged =\n                dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n            {\n                CSMWorld::CellSelection selection = paged->getCellSelection();\n                selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);\n                paged->setCellSelection (selection);\n            }\n        }\n    }\n    else if (CSVRender::PagedWorldspaceWidget *paged =\n        dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        CSMWorld::CellSelection selection = paged->getCellSelection();\n        if (!selection.has (CSMWorld::CellCoordinates::fromId (cellId).first))\n        {\n            // target cell exists, but is not shown\n            std::string mode =\n                CSMPrefs::get()[\"3D Scene Editing\"][\"outside-visible-landedit\"].toString();\n\n            if (mode==\"Discard\")\n                return false;\n\n            if (mode==\"Show cell and edit\" && useTool)\n            {\n                selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);\n                paged->setCellSelection (selection);\n            }\n        }\n    }\n\n    if (noLand(cellId))\n    {\n        std::string mode = CSMPrefs::get()[\"3D Scene Editing\"][\"outside-landedit\"].toString();\n\n        // target cell does not exist\n        if (mode==\"Discard\")\n            return false;\n\n        if (mode==\"Create cell and land, then edit\" && useTool)\n        {\n            document.getUndoStack().push (new CSMWorld::CreateCommand (landTable, cellId));\n            createNewLandData(CSMWorld::CellCoordinates::fromId(cellId).first);\n            fixEdges(CSMWorld::CellCoordinates::fromId(cellId).first);\n            sortAndLimitAlteredCells();\n        }\n    }\n    else if (noLandLoaded(cellId))\n    {\n        std::string mode = CSMPrefs::get()[\"3D Scene Editing\"][\"outside-landedit\"].toString();\n\n        if (mode==\"Discard\")\n            return false;\n\n        if (mode==\"Create cell and land, then edit\" && useTool)\n        {\n            createNewLandData(CSMWorld::CellCoordinates::fromId(cellId).first);\n            fixEdges(CSMWorld::CellCoordinates::fromId(cellId).first);\n            sortAndLimitAlteredCells();\n        }\n    }\n\n    if (useTool && (noCell(cellId) || noLand(cellId) || noLandLoaded(cellId)))\n    {\n        Log(Debug::Warning) << \"Land creation failed at cell id: \" << cellId;\n        return false;\n    }\n    return true;\n}\n\nvoid CSVRender::TerrainShapeMode::fixEdges(CSMWorld::CellCoordinates cellCoords)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);\n    std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());\n    std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY());\n    std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY());\n    std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1);\n    std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1);\n\n    const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n    const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n    const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n    const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n    const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();\n\n    CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer);\n    for(int i = 0; i < ESM::Land::LAND_SIZE; ++i)\n    {\n        if (isLandLoaded(cellLeftId) &&\n            landShapePointer[i * ESM::Land::LAND_SIZE] != landLeftShapePointer[i * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1])\n            landShapeNew[i * ESM::Land::LAND_SIZE] = landLeftShapePointer[i * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1];\n        if (isLandLoaded(cellRightId) &&\n            landShapePointer[i * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1] != landRightShapePointer[i * ESM::Land::LAND_SIZE])\n            landShapeNew[i * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1] = landRightShapePointer[i * ESM::Land::LAND_SIZE];\n        if (isLandLoaded(cellUpId) &&\n            landShapePointer[i] != landUpShapePointer[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + i])\n            landShapeNew[i] = landUpShapePointer[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + i];\n        if (isLandLoaded(cellDownId) &&\n            landShapePointer[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + i] != landDownShapePointer[i])\n            landShapeNew[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + i] = landDownShapePointer[i];\n    }\n\n    QVariant changedLand;\n    changedLand.setValue(landShapeNew);\n\n    QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandHeightsIndex)));\n    QUndoStack& undoStack = document.getUndoStack();\n    undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));\n}\n\nvoid CSVRender::TerrainShapeMode::dragMoveEvent (QDragMoveEvent *event)\n{\n}\n\nvoid CSVRender::TerrainShapeMode::mouseMoveEvent (QMouseEvent *event)\n{\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask());\n    if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing))\n        mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape);\n    if (!hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing))\n        mBrushDraw->hide();\n}\n\nstd::shared_ptr<CSVRender::TerrainSelection> CSVRender::TerrainShapeMode::getTerrainSelection()\n{\n    return mTerrainShapeSelection;\n}\n\nvoid CSVRender::TerrainShapeMode::setBrushSize(int brushSize)\n{\n    mBrushSize = brushSize;\n}\n\nvoid CSVRender::TerrainShapeMode::setBrushShape(CSVWidget::BrushShape brushShape)\n{\n    mBrushShape = brushShape;\n\n    //Set custom brush shape\n    if (mBrushShape == CSVWidget::BrushShape_Custom && !mTerrainShapeSelection->getTerrainSelection().empty())\n    {\n        auto terrainSelection = mTerrainShapeSelection->getTerrainSelection();\n        int selectionCenterX = 0;\n        int selectionCenterY = 0;\n        int selectionAmount = 0;\n\n        for(auto const& value: terrainSelection)\n        {\n            selectionCenterX = selectionCenterX + value.first;\n            selectionCenterY = selectionCenterY + value.second;\n            ++selectionAmount;\n        }\n\n        if (selectionAmount != 0)\n        {\n            selectionCenterX /= selectionAmount;\n            selectionCenterY /= selectionAmount;\n        }\n\n        mCustomBrushShape.clear();\n        std::pair<int, int> differentialPos {};\n        for(auto const& value: terrainSelection)\n        {\n            differentialPos.first = value.first - selectionCenterX;\n            differentialPos.second = value.second - selectionCenterY;\n            mCustomBrushShape.push_back(differentialPos);\n        }\n    }\n}\n\nvoid CSVRender::TerrainShapeMode::setShapeEditTool(int shapeEditTool)\n{\n    mShapeEditTool = shapeEditTool;\n}\n\nvoid CSVRender::TerrainShapeMode::setShapeEditToolStrength(int shapeEditToolStrength)\n{\n    mShapeEditToolStrength = shapeEditToolStrength;\n}\n\nCSVRender::PagedWorldspaceWidget& CSVRender::TerrainShapeMode::getPagedWorldspaceWidget()\n{\n    return dynamic_cast<PagedWorldspaceWidget&>(getWorldspaceWidget());\n}\n"
  },
  {
    "path": "apps/opencs/view/render/terrainshapemode.hpp",
    "content": "#ifndef CSV_RENDER_TERRAINSHAPEMODE_H\n#define CSV_RENDER_TERRAINSHAPEMODE_H\n\n#include \"editmode.hpp\"\n\n#include <string>\n#include <memory>\n\n#include <QWidget>\n#include <QEvent>\n\n#ifndef Q_MOC_RUN\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/land.hpp\"\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/landtexture.hpp\"\n#include \"../widget/brushshapes.hpp\"\n#endif\n\n#include \"brushdraw.hpp\"\n#include \"terrainselection.hpp\"\n\nnamespace CSVWidget\n{\n    class SceneToolShapeBrush;\n}\n\nnamespace CSVRender\n{\n    class PagedWorldspaceWidget;\n\n    /// \\brief EditMode for handling the terrain shape editing\n    class TerrainShapeMode : public EditMode\n    {\n        Q_OBJECT\n\n        public:\n\n            enum InteractionType\n            {\n                InteractionType_PrimaryEdit,\n                InteractionType_PrimarySelect,\n                InteractionType_SecondaryEdit,\n                InteractionType_SecondarySelect,\n                InteractionType_None\n            };\n\n            enum ShapeEditTool\n            {\n                ShapeEditTool_Drag = 0,\n                ShapeEditTool_PaintToRaise = 1,\n                ShapeEditTool_PaintToLower = 2,\n                ShapeEditTool_Smooth = 3,\n                ShapeEditTool_Flatten = 4\n            };\n\n            /// Editmode for terrain shape grid\n            TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);\n\n            void primaryOpenPressed (const WorldspaceHitResult& hit) override;\n\n            /// Create single command for one-click shape editing\n            void primaryEditPressed (const WorldspaceHitResult& hit) override;\n\n            /// Open brush settings window\n            void primarySelectPressed(const WorldspaceHitResult&) override;\n\n            void secondarySelectPressed(const WorldspaceHitResult&) override;\n\n            void activate(CSVWidget::SceneToolbar*) override;\n            void deactivate(CSVWidget::SceneToolbar*) override;\n\n            /// Start shape editing command macro\n            bool primaryEditStartDrag (const QPoint& pos) override;\n\n            bool secondaryEditStartDrag (const QPoint& pos) override;\n            bool primarySelectStartDrag (const QPoint& pos) override;\n            bool secondarySelectStartDrag (const QPoint& pos) override;\n\n            /// Handle shape edit behavior during dragging\n            void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) override;\n\n            /// End shape editing command macro\n            void dragCompleted(const QPoint& pos) override;\n\n            /// Cancel shape editing, and reset all pending changes\n            void dragAborted() override;\n\n            void dragWheel (int diff, double speedFactor) override;\n            void dragMoveEvent (QDragMoveEvent *event) override;\n            void mouseMoveEvent (QMouseEvent *event) override;\n\n            std::shared_ptr<TerrainSelection> getTerrainSelection();\n\n        private:\n\n            /// Remove duplicates and sort mAlteredCells, then limitAlteredHeights forward and reverse\n            void sortAndLimitAlteredCells();\n\n            /// Reset everything in the current edit\n            void clearTransientEdits();\n\n            /// Move pending alteredHeights changes to omwgame/omwaddon -data\n            void applyTerrainEditChanges();\n\n            /// Handle brush mechanics for shape editing\n            void editTerrainShapeGrid (const std::pair<int, int>& vertexCoords, bool dragOperation);\n\n            /// Calculate height, when aiming for bump-shaped terrain change\n            float calculateBumpShape(float distance, int radius, float height);\n\n            /// set the target height for flatten tool\n            void setFlattenToolTargetHeight(const WorldspaceHitResult& hit);\n\n            /// Do a single height alteration for transient shape edit map\n            void alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight, bool useTool = true);\n\n            /// Do a single smoothing height alteration for transient shape edit map\n            void smoothHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength);\n\n            /// Do a single flattening height alteration for transient shape edit map\n            void flattenHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength, int targetHeight);\n\n            /// Get altered height values around one vertex\n            void updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight,\n                float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight,\n                float* rightHeight, float* rightAlteredHeight, float* downHeight, float* downAlteredHeight);\n\n            ///Limit steepness based on either X or Y and return false if steepness is limited\n            void compareAndLimit(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* limitedAlteredHeightXAxis,\n                float* limitedAlteredHeightYAxis, bool* steepnessIsWithinLimits);\n\n            /// Check that the edit doesn't break save format limits, fix if necessary, return true if slope steepness is within limits\n            bool limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode = false);\n\n            /// Check if global selection coordinate belongs to cell in view\n            bool isInCellSelection(int globalSelectionX, int globalSelectionY);\n\n            /// Select vertex at global selection coordinate\n            void handleSelection(int globalSelectionX, int globalSelectionY, std::vector<std::pair<int, int>>* selections);\n\n            /// Handle brush mechanics for terrain shape selection\n            void selectTerrainShapes (const std::pair<int, int>& vertexCoords, unsigned char selectMode, bool dragOperation);\n\n            /// Push terrain shape edits to command macro\n            void pushEditToCommand (const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,\n                CSMWorld::IdTable& landTable, const std::string& cellId);\n\n            /// Push land normals edits to command macro\n            void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document,\n                CSMWorld::IdTable& landTable, const std::string& cellId);\n\n            bool noCell(const std::string& cellId);\n\n            bool noLand(const std::string& cellId);\n\n            bool noLandLoaded(const std::string& cellId);\n\n            bool isLandLoaded(const std::string& cellId);\n\n            /// Create new blank height record and new normals, if there are valid adjancent cell, take sample points and set the average height based on that\n            void createNewLandData(const CSMWorld::CellCoordinates& cellCoords);\n\n            /// Create new cell and land if needed, only user tools may ask for opening new cells (useTool == false is for automated land changes)\n            bool allowLandShapeEditing(const std::string& textureFileName, bool useTool = true);\n\n            /// Bind the edging vertice to the values of the adjancent cells\n            void fixEdges(CSMWorld::CellCoordinates cellCoords);\n\n            std::string mBrushTexture;\n            int mBrushSize = 1;\n            CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point;\n            std::unique_ptr<BrushDraw> mBrushDraw;\n            std::vector<std::pair<int, int>> mCustomBrushShape;\n            CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool = nullptr;\n            int mDragMode = InteractionType_None;\n            osg::Group* mParentNode;\n            bool mIsEditing = false;\n            std::shared_ptr<TerrainSelection> mTerrainShapeSelection;\n            int mTotalDiffY = 0;\n            std::vector<CSMWorld::CellCoordinates> mAlteredCells;\n            osg::Vec3d mEditingPos;\n            int mShapeEditTool = ShapeEditTool_Drag;\n            int mShapeEditToolStrength = 8;\n            int mTargetHeight = 0;\n\n            PagedWorldspaceWidget& getPagedWorldspaceWidget();\n\n        public slots:\n            void setBrushSize(int brushSize);\n            void setBrushShape(CSVWidget::BrushShape brushShape);\n            void setShapeEditTool(int shapeEditTool);\n            void setShapeEditToolStrength(int shapeEditToolStrength);\n    };\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/terrainstorage.cpp",
    "content": "#include \"terrainstorage.hpp\"\n\n#include \"../../model/world/land.hpp\"\n#include \"../../model/world/landtexture.hpp\"\n\n#include <components/esmterrain/storage.hpp>\n\nnamespace CSVRender\n{\n    TerrainStorage::TerrainStorage(const CSMWorld::Data &data)\n        : ESMTerrain::Storage(data.getResourceSystem()->getVFS())\n        , mData(data)\n    {\n        resetHeights();\n    }\n\n    osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)\n    {\n        // The cell isn't guaranteed to have Land. This is because the terrain implementation\n        // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell\n        int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY));\n        if (index == -1)\n            return nullptr;\n\n        const ESM::Land& land = mData.getLand().getRecord(index).get();\n        return new ESMTerrain::LandObject(&land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX);\n    }\n\n    const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)\n    {\n        int row = mData.getLandTextures().searchId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index));\n        if (row == -1)\n            return nullptr;\n\n        return &mData.getLandTextures().getRecord(row).get();\n    }\n\n    void TerrainStorage::setAlteredHeight(int inCellX, int inCellY, float height)\n    {\n        mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] = height - fmod(height, 8); //Limit to divisible by 8 to avoid cell seam breakage\n    }\n\n    void TerrainStorage::resetHeights()\n    {\n        std::fill(std::begin(mAlteredHeight), std::end(mAlteredHeight), 0);\n    }\n\n    float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY)\n    {\n        float height = 0.f;\n        osg::ref_ptr<const ESMTerrain::LandObject> land = getLand (cellX, cellY);\n        if (land)\n        {\n            const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;\n            if (data) height = getVertexHeight(data, inCellX, inCellY);\n        }\n        else return height;\n        return mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] + height;\n\n    }\n\n    float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY)\n    {\n        return &mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX];\n    }\n\n    void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY)\n    {\n        // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells\n        throw std::runtime_error(\"getBounds not implemented\");\n    }\n\n    int TerrainStorage::getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return heightData->mHeights[col*ESM::Land::LAND_SIZE + row] +\n            mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)];\n    }\n\n    int TerrainStorage::getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] +\n            mAlteredHeight[static_cast<unsigned int>((col)*ESM::Land::LAND_SIZE + row - 1)];\n    }\n\n    int TerrainStorage::getRightHeight(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return heightData->mHeights[col*ESM::Land::LAND_SIZE + row + 1] +\n            mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row + 1)];\n    }\n\n    int TerrainStorage::getUpHeight(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] +\n            mAlteredHeight[static_cast<unsigned int>((col - 1)*ESM::Land::LAND_SIZE + row)];\n    }\n\n    int TerrainStorage::getDownHeight(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] +\n            mAlteredHeight[static_cast<unsigned int>((col + 1)*ESM::Land::LAND_SIZE + row)];\n    }\n\n    int TerrainStorage::getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return abs(getThisHeight(col, row, heightData) - getLeftHeight(col, row, heightData));\n    }\n\n    int TerrainStorage::getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return abs(getThisHeight(col, row, heightData) - getRightHeight(col, row, heightData));\n    }\n\n    int TerrainStorage::getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return abs(getThisHeight(col, row, heightData) - getUpHeight(col, row, heightData));\n    }\n\n    int TerrainStorage::getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const\n    {\n        return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData));\n    }\n\n    bool TerrainStorage::leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const\n    {\n        return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit ||\n            getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit;\n    }\n\n    bool TerrainStorage::rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const\n    {\n        return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit ||\n            getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit;\n    }\n\n    void TerrainStorage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const\n    {\n        // Highlight broken height changes\n        int heightWarningLimit = 1024;\n        if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData)) ||\n            ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData)))\n        {\n            color.r() = 255;\n            color.g() = 0;\n            color.b() = 0;\n        }\n    }\n\n    float TerrainStorage::getAlteredHeight(int col, int row) const\n    {\n        return mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)];\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/terrainstorage.hpp",
    "content": "#ifndef OPENCS_RENDER_TERRAINSTORAGE_H\n#define OPENCS_RENDER_TERRAINSTORAGE_H\n\n#include <array>\n\n#include <components/esmterrain/storage.hpp>\n\n#include \"../../model/world/data.hpp\"\n\nnamespace CSVRender\n{\n    /**\n     * @brief A bridge between the terrain component and OpenCS's terrain data storage.\n     */\n    class TerrainStorage : public ESMTerrain::Storage\n    {\n    public:\n        TerrainStorage(const CSMWorld::Data& data);\n        void setAlteredHeight(int inCellX, int inCellY, float heightMap);\n        void resetHeights();\n\n        bool useAlteration() const override { return true; }\n        float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY);\n        float* getAlteredHeight(int inCellX, int inCellY);\n\n    private:\n        const CSMWorld::Data& mData;\n        std::array<float, ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE> mAlteredHeight;\n\n        osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) override;\n        const ESM::LandTexture* getLandTexture(int index, short plugin) override;\n\n        void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;\n\n        int getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const;\n        int getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const;\n        int getRightHeight(int col, int row, const ESM::Land::LandData *heightData) const;\n        int getUpHeight(int col, int row, const ESM::Land::LandData *heightData) const;\n        int getDownHeight(int col, int row, const ESM::Land::LandData *heightData) const;\n        int getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData *heightData) const;\n        int getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const;\n        int getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const;\n        int getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const;\n        bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const;\n        bool rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const;\n\n        void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const override;\n        float getAlteredHeight(int col, int row) const override;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/terraintexturemode.cpp",
    "content": "#include \"terraintexturemode.hpp\"\n\n#include <string>\n#include <sstream>\n\n#include <QWidget>\n#include <QIcon>\n#include <QEvent>\n#include <QDropEvent>\n#include <QDragEnterEvent>\n#include <QDrag>\n\n#include <osg/Group>\n\n#include \"../widget/modebutton.hpp\"\n#include \"../widget/scenetoolbar.hpp\"\n#include \"../widget/scenetooltexturebrush.hpp\"\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/idtree.hpp\"\n#include \"../../model/world/landtexture.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#include \"../widget/brushshapes.hpp\"\n\n#include \"brushdraw.hpp\"\n#include \"editmode.hpp\"\n#include \"pagedworldspacewidget.hpp\"\n#include \"mask.hpp\"\n#include \"object.hpp\" // Something small needed regarding pointers from here ()\n#include \"worldspacewidget.hpp\"\n\nCSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)\n: EditMode (worldspaceWidget, QIcon {\":scenetoolbar/editing-terrain-texture\"}, Mask_Terrain | Mask_Reference, \"Terrain texture editing\", parent),\n    mBrushTexture(\"L0#0\"),\n    mBrushSize(1),\n    mBrushShape(CSVWidget::BrushShape_Point),\n    mTextureBrushScenetool(nullptr),\n    mDragMode(InteractionType_None),\n    mParentNode(parentNode),\n    mIsEditing(false)\n{\n}\n\nvoid CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar)\n{\n    if(!mTextureBrushScenetool)\n    {\n        mTextureBrushScenetool = new CSVWidget::SceneToolTextureBrush (toolbar, \"scenetooltexturebrush\", getWorldspaceWidget().getDocument());\n        connect(mTextureBrushScenetool, SIGNAL (clicked()), mTextureBrushScenetool, SLOT (activate()));\n        connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushSize(int)), this, SLOT(setBrushSize(int)));\n        connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setBrushShape(CSVWidget::BrushShape)));\n        connect(mTextureBrushScenetool->mTextureBrushWindow->mSizeSliders->mBrushSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBrushSize(int)));\n        connect(mTextureBrushScenetool, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string)));\n        connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string)));\n\n        connect(mTextureBrushScenetool, SIGNAL(passEvent(QDropEvent*)), this, SLOT(handleDropEvent(QDropEvent*)));\n        connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool->mTextureBrushWindow, SLOT(setBrushTexture(std::string)));\n        connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string)));\n    }\n\n    if (!mTerrainTextureSelection)\n    {\n        mTerrainTextureSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Texture));\n    }\n\n    if (!mBrushDraw)\n        mBrushDraw.reset(new BrushDraw(mParentNode, true));\n\n    EditMode::activate(toolbar);\n    toolbar->addTool (mTextureBrushScenetool);\n}\n\nvoid CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)\n{\n    if(mTextureBrushScenetool)\n    {\n        toolbar->removeTool (mTextureBrushScenetool);\n        delete mTextureBrushScenetool;\n        mTextureBrushScenetool = nullptr;\n    }\n\n    if (mTerrainTextureSelection)\n    {\n        mTerrainTextureSelection.reset();\n    }\n\n    if (mBrushDraw)\n        mBrushDraw.reset();\n\n    EditMode::deactivate(toolbar);\n}\n\nvoid CSVRender::TerrainTextureMode::primaryOpenPressed(const WorldspaceHitResult& hit) // Apply changes here\n{\n}\n\nvoid CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult& hit) // Apply changes here\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));\n\n    mCellId = getWorldspaceWidget().getCellId (hit.worldPos);\n\n    QUndoStack& undoStack = document.getUndoStack();\n    CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();\n    int index = landtexturesCollection.searchId(mBrushTexture);\n\n    if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == nullptr)\n    {\n        undoStack.beginMacro (\"Edit texture records\");\n        if(allowLandTextureEditing(mCellId))\n        {\n            undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));\n            editTerrainTextureGrid(hit);\n        }\n        undoStack.endMacro();\n    }\n}\n\nvoid CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit)\n{\n    if(hit.hit && hit.tag == nullptr)\n    {\n        selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, false);\n    }\n}\n\nvoid CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit)\n{\n    if(hit.hit && hit.tag == nullptr)\n    {\n        selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, false);\n    }\n}\n\nbool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)\n{\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));\n\n    mCellId = getWorldspaceWidget().getCellId (hit.worldPos);\n\n    QUndoStack& undoStack = document.getUndoStack();\n\n    mDragMode = InteractionType_PrimaryEdit;\n\n    CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();\n    int index = landtexturesCollection.searchId(mBrushTexture);\n\n    if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == nullptr)\n    {\n        undoStack.beginMacro (\"Edit texture records\");\n        mIsEditing = true;\n        if(allowLandTextureEditing(mCellId))\n        {\n            undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));\n            editTerrainTextureGrid(hit);\n        }\n    }\n\n    return true;\n}\n\nbool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos)\n{\n    return false;\n}\n\nbool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos)\n{\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n    mDragMode = InteractionType_PrimarySelect;\n    if (!hit.hit || hit.tag != nullptr)\n    {\n        mDragMode = InteractionType_None;\n        return false;\n    }\n    selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);\n    return false;\n}\n\nbool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)\n{\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n    mDragMode = InteractionType_SecondarySelect;\n    if (!hit.hit || hit.tag != nullptr)\n    {\n        mDragMode = InteractionType_None;\n        return false;\n    }\n    selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);\n    return false;\n}\n\nvoid CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)\n{\n    if (mDragMode == InteractionType_PrimaryEdit)\n    {\n        WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n        std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos);\n        CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n\n        CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();\n        int index = landtexturesCollection.searchId(mBrushTexture);\n\n        if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == nullptr)\n        {\n            editTerrainTextureGrid(hit);\n        }\n    }\n\n    if (mDragMode == InteractionType_PrimarySelect)\n    {\n        WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n        if (hit.hit && hit.tag == nullptr) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);\n    }\n\n    if (mDragMode == InteractionType_SecondarySelect)\n    {\n        WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());\n        if (hit.hit && hit.tag == nullptr) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);\n    }\n}\n\nvoid CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)\n{\n    if (mDragMode == InteractionType_PrimaryEdit && mIsEditing)\n    {\n        CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n        QUndoStack& undoStack = document.getUndoStack();\n\n        CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();\n        int index = landtexturesCollection.searchId(mBrushTexture);\n\n        if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())\n        {\n             undoStack.endMacro();\n             mIsEditing = false;\n        }\n    }\n}\n\nvoid CSVRender::TerrainTextureMode::dragAborted()\n{\n}\n\nvoid CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor)\n{\n}\n\nvoid CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event)\n{\n  const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n\n  if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped\n      return;\n\n  if (mime->holdsType (CSMWorld::UniversalId::Type_LandTexture))\n  {\n      const std::vector<CSMWorld::UniversalId> ids = mime->getData();\n\n      for (const CSMWorld::UniversalId& uid : ids)\n      {\n          mBrushTexture = uid.getId();\n          emit passBrushTexture(mBrushTexture);\n      }\n  }\n  if (mime->holdsType (CSMWorld::UniversalId::Type_Texture))\n  {\n      const std::vector<CSMWorld::UniversalId> ids = mime->getData();\n\n      for (const CSMWorld::UniversalId& uid : ids)\n      {\n          std::string textureFileName = uid.toString();\n          createTexture(textureFileName);\n          emit passBrushTexture(mBrushTexture);\n      }\n  }\n}\n\nvoid CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitResult& hit)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n\n    mCellId = getWorldspaceWidget().getCellId (hit.worldPos);\n    if(allowLandTextureEditing(mCellId)) {}\n\n    std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId);\n\n    int cellX = cellCoordinates_pair.first.getX();\n    int cellY = cellCoordinates_pair.first.getY();\n\n    // The coordinates of hit in mCellId\n    int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.25));\n    int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.25));\n    if (xHitInCell < 0)\n    {\n        xHitInCell = xHitInCell + landTextureSize;\n        cellX = cellX - 1;\n    }\n    if (yHitInCell > 15)\n    {\n        yHitInCell = yHitInCell - landTextureSize;\n        cellY = cellY + 1;\n    }\n\n    mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);\n    if(allowLandTextureEditing(mCellId)) {}\n\n    std::string iteratedCellId;\n\n    int textureColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandTexturesIndex);\n\n    std::size_t hashlocation = mBrushTexture.find('#');\n    std::string mBrushTextureInt = mBrushTexture.substr (hashlocation+1);\n    int brushInt = stoi(mBrushTexture.substr (hashlocation+1))+1; // All indices are offset by +1\n\n    int r = static_cast<float>(mBrushSize) / 2;\n\n    if (mBrushShape == CSVWidget::BrushShape_Point)\n    {\n        CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();\n        CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);\n\n        if(allowLandTextureEditing(mCellId))\n        {\n            newTerrain[yHitInCell*landTextureSize+xHitInCell] = brushInt;\n            pushEditToCommand(newTerrain, document, landTable, mCellId);\n        }\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Square)\n    {\n        int upperLeftCellX  = cellX - std::floor(r / landTextureSize);\n        int upperLeftCellY  = cellY - std::floor(r / landTextureSize);\n        if (xHitInCell - (r % landTextureSize) < 0) upperLeftCellX--;\n        if (yHitInCell - (r % landTextureSize) < 0) upperLeftCellY--;\n\n        int lowerrightCellX = cellX + std::floor(r / landTextureSize);\n        int lowerrightCellY = cellY + std::floor(r / landTextureSize);\n        if (xHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellX++;\n        if (yHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellY++;\n\n        for(int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++)\n        {\n            for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)\n            {\n                iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);\n                if(allowLandTextureEditing(iteratedCellId))\n                {\n                    CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();\n                    CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);\n                    for(int i = 0; i < landTextureSize; i++)\n                    {\n                        for(int j = 0; j < landTextureSize; j++)\n                        {\n\n                            if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)\n                            {\n                                newTerrain[j*landTextureSize+i] = brushInt;\n                            }\n                            else\n                            {\n                                int distanceX(0);\n                                int distanceY(0);\n                                if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i;\n                                if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j;\n                                if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize * abs(i_cell-cellX) + i;\n                                if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;\n                                if (i_cell == cellX) distanceX = abs(i-xHitInCell);\n                                if (j_cell == cellY) distanceY = abs(j-yHitInCell);\n                                if (distanceX < r && distanceY < r) newTerrain[j*landTextureSize+i] = brushInt;\n                            }\n                        }\n                    }\n                    pushEditToCommand(newTerrain, document, landTable, iteratedCellId);\n                }\n            }\n        }\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Circle)\n    {\n        int upperLeftCellX  = cellX - std::floor(r / landTextureSize);\n        int upperLeftCellY  = cellY - std::floor(r / landTextureSize);\n        if (xHitInCell - (r % landTextureSize) < 0) upperLeftCellX--;\n        if (yHitInCell - (r % landTextureSize) < 0) upperLeftCellY--;\n\n        int lowerrightCellX = cellX + std::floor(r / landTextureSize);\n        int lowerrightCellY = cellY + std::floor(r / landTextureSize);\n        if (xHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellX++;\n        if (yHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellY++;\n\n        for(int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++)\n        {\n            for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)\n            {\n                iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);\n                if(allowLandTextureEditing(iteratedCellId))\n                {\n                    CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();\n                    CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);\n                    for(int i = 0; i < landTextureSize; i++)\n                    {\n                        for(int j = 0; j < landTextureSize; j++)\n                        {\n                            if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)\n                            {\n                                int distanceX = abs(i-xHitInCell);\n                                int distanceY = abs(j-yHitInCell);\n                                float distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));\n                                float rf = static_cast<float>(mBrushSize) / 2;\n                                if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;\n                            }\n                            else\n                            {\n                                int distanceX(0);\n                                int distanceY(0);\n                                if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i;\n                                if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j;\n                                if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize * abs(i_cell-cellX) + i;\n                                if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;\n                                if (i_cell == cellX) distanceX = abs(i-xHitInCell);\n                                if (j_cell == cellY) distanceY = abs(j-yHitInCell);\n                                float distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));\n                                float rf = static_cast<float>(mBrushSize) / 2;\n                                if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;\n                            }\n                        }\n                    }\n                    pushEditToCommand(newTerrain, document, landTable, iteratedCellId);\n                }\n            }\n        }\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Custom)\n    {\n        CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();\n        CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);\n\n        if(allowLandTextureEditing(mCellId) && !mCustomBrushShape.empty())\n        {\n            for(auto const& value: mCustomBrushShape)\n            {\n                if(yHitInCell + value.second >= 0 && yHitInCell + value.second <= 15 && xHitInCell + value.first >= 0 && xHitInCell + value.first <= 15)\n                {\n                    newTerrain[(yHitInCell+value.second)*landTextureSize+xHitInCell+value.first] = brushInt;\n                }\n                else\n                {\n                    int cellXDifference = std::floor(1.0f*(xHitInCell + value.first)/landTextureSize);\n                    int cellYDifference = std::floor(1.0f*(yHitInCell + value.second)/landTextureSize);\n                    int xInOtherCell = xHitInCell + value.first - cellXDifference * landTextureSize;\n                    int yInOtherCell = yHitInCell + value.second - cellYDifference * landTextureSize;\n\n                    std::string cellId = CSMWorld::CellCoordinates::generateId(cellX+cellXDifference, cellY+cellYDifference);\n                    if (allowLandTextureEditing(cellId))\n                    {\n                        CSMWorld::LandTexturesColumn::DataType newTerrainPointerOtherCell = landTable.data(landTable.getModelIndex(cellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();\n                        CSMWorld::LandTexturesColumn::DataType newTerrainOtherCell(newTerrainPointerOtherCell);\n                        newTerrainOtherCell[yInOtherCell*landTextureSize+xInOtherCell] = brushInt;\n                        pushEditToCommand(newTerrainOtherCell, document, landTable, cellId);\n                    }\n                }\n            }\n            pushEditToCommand(newTerrain, document, landTable, mCellId);\n        }\n    }\n}\n\nbool CSVRender::TerrainTextureMode::isInCellSelection(int globalSelectionX, int globalSelectionY)\n{\n    if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        std::pair<int, int> textureCoords = std::make_pair(globalSelectionX, globalSelectionY);\n        std::string cellId = CSMWorld::CellCoordinates::textureGlobalToCellId(textureCoords);\n        return paged->getCellSelection().has(CSMWorld::CellCoordinates::fromId(cellId).first);\n    }\n    return false;\n}\n\n\nvoid CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation)\n{\n    int r = mBrushSize / 2;\n    std::vector<std::pair<int, int>> selections;\n\n    if (mBrushShape == CSVWidget::BrushShape_Point)\n    {\n        if (isInCellSelection(texCoords.first, texCoords.second)) selections.emplace_back(texCoords);\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Square)\n    {\n        for (int i = -r; i <= r; i++)\n        {\n            for (int j = -r; j <= r; j++)\n            {\n                int x = i + texCoords.first;\n                int y = j + texCoords.second;\n                if (isInCellSelection(x, y))\n                {\n                    selections.emplace_back(x, y);\n                }\n            }\n        }\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Circle)\n    {\n        for (int i = -r; i <= r; i++)\n        {\n            for (int j = -r; j <= r; j++)\n            {\n                osg::Vec2f coords(i,j);\n                float rf = static_cast<float>(mBrushSize) / 2;\n                if (std::round(coords.length()) < rf)\n                {\n                    int x = i + texCoords.first;\n                    int y = j + texCoords.second;\n                    if (isInCellSelection(x, y))\n                    {\n                        selections.emplace_back(x, y);\n                    }\n                }\n            }\n        }\n    }\n\n    if (mBrushShape == CSVWidget::BrushShape_Custom)\n    {\n        if(!mCustomBrushShape.empty())\n        {\n            for(auto const& value: mCustomBrushShape)\n            {\n                int x = texCoords.first + value.first;\n                int y = texCoords.second + value.second;\n                if (isInCellSelection(x, y))\n                {\n                    selections.emplace_back(x, y);\n                }\n            }\n        }\n    }\n\n    if(selectMode == 0) mTerrainTextureSelection->onlySelect(selections);\n    if(selectMode == 1) mTerrainTextureSelection->toggleSelect(selections, dragOperation);\n}\n\nvoid CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,\n    CSMWorld::IdTable& landTable, std::string cellId)\n{\n    CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));\n\n    QVariant changedLand;\n    changedLand.setValue(newLandGrid);\n\n    QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex)));\n\n    QUndoStack& undoStack = document.getUndoStack();\n    undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));\n    undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));\n}\n\nvoid CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n\n    CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));\n\n    QUndoStack& undoStack = document.getUndoStack();\n\n    std::string newId;\n\n    int counter=0;\n    bool freeIndexFound = false;\n    do\n    {\n        const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;\n        try\n        {\n            newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);\n            if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;\n        }\n        catch (const std::exception&)\n        {\n            newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);\n            freeIndexFound = true;\n        }\n    } while (freeIndexFound == false);\n\n    std::size_t idlocation = textureFileName.find(\"Texture: \");\n    textureFileName = textureFileName.substr (idlocation + 9);\n\n    QVariant textureNameVariant;\n\n    QVariant textureFileNameVariant;\n    textureFileNameVariant.setValue(QString::fromStdString(textureFileName));\n\n    undoStack.beginMacro (\"Add land texture record\");\n\n    undoStack.push (new CSMWorld::CreateCommand (ltexTable, newId));\n    QModelIndex index(ltexTable.getModelIndex (newId, ltexTable.findColumnIndex (CSMWorld::Columns::ColumnId_Texture)));\n    undoStack.push (new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant));\n    undoStack.endMacro();\n    mBrushTexture = newId;\n}\n\nbool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId)\n{\n    CSMDoc::Document& document = getWorldspaceWidget().getDocument();\n    CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));\n    CSMWorld::IdTree& cellTable = dynamic_cast<CSMWorld::IdTree&> (\n            *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));\n\n    bool noCell = document.getData().getCells().searchId (cellId)==-1;\n    bool noLand = document.getData().getLand().searchId (cellId)==-1;\n\n    if (noCell)\n    {\n        std::string mode = CSMPrefs::get()[\"3D Scene Editing\"][\"outside-landedit\"].toString();\n\n        // target cell does not exist\n        if (mode==\"Discard\")\n            return false;\n\n        if (mode==\"Create cell and land, then edit\")\n        {\n            std::unique_ptr<CSMWorld::CreateCommand> createCommand (\n                new CSMWorld::CreateCommand (cellTable, cellId));\n            int parentIndex = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);\n            int index = cellTable.findNestedColumnIndex (parentIndex, CSMWorld::Columns::ColumnId_Interior);\n            createCommand->addNestedValue (parentIndex, index, false);\n            document.getUndoStack().push (createCommand.release());\n\n            if (CSVRender::PagedWorldspaceWidget *paged =\n                dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n            {\n                CSMWorld::CellSelection selection = paged->getCellSelection();\n                selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);\n                paged->setCellSelection (selection);\n            }\n        }\n    }\n    else if (CSVRender::PagedWorldspaceWidget *paged =\n        dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))\n    {\n        CSMWorld::CellSelection selection = paged->getCellSelection();\n        if (!selection.has (CSMWorld::CellCoordinates::fromId (cellId).first))\n        {\n            // target cell exists, but is not shown\n            std::string mode =\n                CSMPrefs::get()[\"3D Scene Editing\"][\"outside-visible-landedit\"].toString();\n\n            if (mode==\"Discard\")\n                return false;\n\n            if (mode==\"Show cell and edit\")\n            {\n                selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);\n                paged->setCellSelection (selection);\n            }\n        }\n    }\n\n    if (noLand)\n    {\n        std::string mode = CSMPrefs::get()[\"3D Scene Editing\"][\"outside-landedit\"].toString();\n\n        // target cell does not exist\n        if (mode==\"Discard\")\n            return false;\n\n        if (mode==\"Create cell and land, then edit\")\n        {\n            document.getUndoStack().push (new CSMWorld::CreateCommand (landTable, cellId));\n        }\n    }\n\n    return true;\n}\n\nvoid CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event)\n{\n}\n\nvoid CSVRender::TerrainTextureMode::mouseMoveEvent (QMouseEvent *event)\n{\n    WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask());\n    if (hit.hit && mBrushDraw)\n        mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape);\n    if (!hit.hit && mBrushDraw)\n        mBrushDraw->hide();\n}\n\nstd::shared_ptr<CSVRender::TerrainSelection> CSVRender::TerrainTextureMode::getTerrainSelection()\n{\n    return mTerrainTextureSelection;\n}\n\n\nvoid CSVRender::TerrainTextureMode::setBrushSize(int brushSize)\n{\n    mBrushSize = brushSize;\n}\n\nvoid CSVRender::TerrainTextureMode::setBrushShape(CSVWidget::BrushShape brushShape)\n{\n    mBrushShape = brushShape;\n\n    //Set custom brush shape\n    if (mBrushShape == CSVWidget::BrushShape_Custom && !mTerrainTextureSelection->getTerrainSelection().empty())\n    {\n        auto terrainSelection = mTerrainTextureSelection->getTerrainSelection();\n        int selectionCenterX = 0;\n        int selectionCenterY = 0;\n        int selectionAmount = 0;\n\n        for(auto const& value: terrainSelection)\n        {\n            selectionCenterX += value.first;\n            selectionCenterY += value.second;\n            ++selectionAmount;\n        }\n\n        if (selectionAmount != 0)\n        {\n            selectionCenterX /= selectionAmount;\n            selectionCenterY /= selectionAmount;\n        }\n\n        mCustomBrushShape.clear();\n        for (auto const& value: terrainSelection)\n            mCustomBrushShape.emplace_back(value.first - selectionCenterX, value.second - selectionCenterY);\n    }\n}\n\nvoid CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture)\n{\n    mBrushTexture = brushTexture;\n}\n"
  },
  {
    "path": "apps/opencs/view/render/terraintexturemode.hpp",
    "content": "#ifndef CSV_RENDER_TERRAINTEXTUREMODE_H\n#define CSV_RENDER_TERRAINTEXTUREMODE_H\n\n#include \"editmode.hpp\"\n\n#include <string>\n#include <memory>\n\n#include <QWidget>\n#include <QEvent>\n\n#ifndef Q_MOC_RUN\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/land.hpp\"\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/landtexture.hpp\"\n#include \"../widget/brushshapes.hpp\"\n#include \"brushdraw.hpp\"\n#endif\n\n#include \"terrainselection.hpp\"\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace CSVWidget\n{\n    class SceneToolTextureBrush;\n}\n\nnamespace CSVRender\n{\n    class TerrainTextureMode : public EditMode\n    {\n        Q_OBJECT\n\n        public:\n\n            enum InteractionType\n            {\n                InteractionType_PrimaryEdit,\n                InteractionType_PrimarySelect,\n                InteractionType_SecondaryEdit,\n                InteractionType_SecondarySelect,\n                InteractionType_None\n            };\n\n            /// \\brief Editmode for terrain texture grid\n            TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);\n\n            void primaryOpenPressed (const WorldspaceHitResult& hit) override;\n\n            /// \\brief Create single command for one-click texture editing\n            void primaryEditPressed (const WorldspaceHitResult& hit) override;\n\n            /// \\brief Open brush settings window\n            void primarySelectPressed(const WorldspaceHitResult&) override;\n\n            void secondarySelectPressed(const WorldspaceHitResult&) override;\n\n            void activate(CSVWidget::SceneToolbar*) override;\n            void deactivate(CSVWidget::SceneToolbar*) override;\n\n            /// \\brief Start texture editing command macro\n            bool primaryEditStartDrag (const QPoint& pos) override;\n\n            bool secondaryEditStartDrag (const QPoint& pos) override;\n            bool primarySelectStartDrag (const QPoint& pos) override;\n            bool secondarySelectStartDrag (const QPoint& pos) override;\n\n            /// \\brief Handle texture edit behavior during dragging\n            void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) override;\n\n            /// \\brief End texture editing command macro\n            void dragCompleted(const QPoint& pos) override;\n\n            void dragAborted() override;\n            void dragWheel (int diff, double speedFactor) override;\n            void dragMoveEvent (QDragMoveEvent *event) override;\n\n            void mouseMoveEvent (QMouseEvent *event) override;\n\n            std::shared_ptr<TerrainSelection> getTerrainSelection();\n\n        private:\n            /// \\brief Handle brush mechanics, maths regarding worldspace hit etc.\n            void editTerrainTextureGrid (const WorldspaceHitResult& hit);\n\n            /// \\brief Check if global selection coordinate belongs to cell in view\n            bool isInCellSelection(int globalSelectionX, int globalSelectionY);\n\n            /// \\brief Handle brush mechanics for texture selection\n            void selectTerrainTextures (const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation);\n\n            /// \\brief Push texture edits to command macro\n            void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,\n                CSMWorld::IdTable& landTable, std::string cellId);\n\n            /// \\brief Create new land texture record from texture asset\n            void createTexture(std::string textureFileName);\n\n            /// \\brief Create new cell and land if needed\n            bool allowLandTextureEditing(std::string textureFileName);\n\n            std::string mCellId;\n            std::string mBrushTexture;\n            int mBrushSize;\n            CSVWidget::BrushShape mBrushShape;\n            std::unique_ptr<BrushDraw> mBrushDraw;\n            std::vector<std::pair<int, int>> mCustomBrushShape;\n            CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool;\n            int mDragMode;\n            osg::Group* mParentNode;\n            bool mIsEditing;\n            std::shared_ptr<TerrainSelection> mTerrainTextureSelection;\n\n            const int cellSize {ESM::Land::REAL_SIZE};\n            const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};\n\n        signals:\n            void passBrushTexture(std::string brushTexture);\n\n        public slots:\n            void handleDropEvent(QDropEvent *event);\n            void setBrushSize(int brushSize);\n            void setBrushShape(CSVWidget::BrushShape brushShape);\n            void setBrushTexture(std::string brushShape);\n    };\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/unpagedworldspacewidget.cpp",
    "content": "#include \"unpagedworldspacewidget.hpp\"\n\n#include <sstream>\n\n#include <QEvent>\n\n#include <components/sceneutil/util.hpp>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n\n#include \"../widget/scenetooltoggle2.hpp\"\n\n#include \"cameracontroller.hpp\"\n#include \"mask.hpp\"\n#include \"tagbase.hpp\"\n\nvoid CSVRender::UnpagedWorldspaceWidget::update()\n{\n    const CSMWorld::Record<CSMWorld::Cell>& record =\n        dynamic_cast<const CSMWorld::Record<CSMWorld::Cell>&> (mCellsModel->getRecord (mCellId));\n\n    osg::Vec4f colour = SceneUtil::colourFromRGB(record.get().mAmbi.mAmbient);\n\n    setDefaultAmbient (colour);\n\n    bool isInterior = (record.get().mData.mFlags & ESM::Cell::Interior) != 0;\n    bool behaveLikeExterior = (record.get().mData.mFlags & ESM::Cell::QuasiEx) != 0;\n\n    setExterior(behaveLikeExterior || !isInterior);\n\n    /// \\todo deal with mSunlight and mFog/mForDensity\n\n    flagAsModified();\n}\n\nCSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget* parent)\n: WorldspaceWidget (document, parent), mDocument(document), mCellId (cellId)\n{\n    mCellsModel = &dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));\n\n    mReferenceablesModel = &dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables));\n\n    connect (mCellsModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int)));\n\n    connect (&document.getData(), SIGNAL (assetTablesChanged ()),\n        this, SLOT (assetTablesChanged ()));\n\n    update();\n\n    mCell.reset (new Cell (document.getData(), mRootNode, mCellId));\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    int index = mCellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification);\n    QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, index);\n\n    if (cellIndex.row()>=topLeft.row() && cellIndex.row()<=bottomRight.row())\n    {\n        if (mCellsModel->data (cellIndex).toInt()==CSMWorld::RecordBase::State_Deleted)\n        {\n            emit closeRequest();\n        }\n        else\n        {\n            /// \\todo possible optimisation: check columns and update only if relevant columns have\n            /// changed\n            update();\n        }\n    }\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelIndex& parent,\n    int start, int end)\n{\n    QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, 0);\n\n    if (cellIndex.row()>=start && cellIndex.row()<=end)\n        emit closeRequest();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::assetTablesChanged()\n{\n    if (mCell)\n        mCell->reloadAssets();\n}\n\nbool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& universalIdData, DropType type)\n{\n    if (WorldspaceWidget::handleDrop (universalIdData, type))\n        return true;\n\n    if (type!=Type_CellsInterior)\n        return false;\n\n    mCellId = universalIdData.begin()->getId();\n\n    mCell.reset (new Cell (getDocument().getData(), mRootNode, mCellId));\n    mCamPositionSet = false;\n    mOrbitCamControl->reset();\n\n    update();\n    emit cellChanged(*universalIdData.begin());\n\n    return true;\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::clearSelection (int elementMask)\n{\n    mCell->setSelection (elementMask, Cell::Selection_Clear);\n    flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::invertSelection (int elementMask)\n{\n    mCell->setSelection (elementMask, Cell::Selection_Invert);\n    flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::selectAll (int elementMask)\n{\n    mCell->setSelection (elementMask, Cell::Selection_All);\n    flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::selectAllWithSameParentId (int elementMask)\n{\n    mCell->selectAllWithSameParentId (elementMask);\n    flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode)\n{\n    mCell->selectInsideCube (pointA, pointB, dragMode);\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode)\n{\n    mCell->selectWithinDistance (point, distance, dragMode);\n}\n\nstd::string CSVRender::UnpagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const\n{\n    return mCellId;\n}\n\nCSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const osg::Vec3d& point) const\n{\n    return mCell.get();\n}\n\nCSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const CSMWorld::CellCoordinates& coords) const\n{\n    return mCell.get();\n}\n\nstd::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::UnpagedWorldspaceWidget::getSelection (\n    unsigned int elementMask) const\n{\n    return mCell->getSelection (elementMask);\n}\n\nstd::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::UnpagedWorldspaceWidget::getEdited (\n    unsigned int elementMask) const\n{\n    return mCell->getEdited (elementMask);\n}\n\nvoid  CSVRender::UnpagedWorldspaceWidget::setSubMode (int subMode, unsigned int elementMask)\n{\n    mCell->setSubMode (subMode, elementMask);\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::reset (unsigned int elementMask)\n{\n    mCell->reset (elementMask);\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    if (mCell.get())\n        if (mCell.get()->referenceableDataChanged (topLeft, bottomRight))\n            flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::referenceableAboutToBeRemoved (\n    const QModelIndex& parent, int start, int end)\n{\n    if (mCell.get())\n        if (mCell.get()->referenceableAboutToBeRemoved (parent, start, end))\n            flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent,\n    int start, int end)\n{\n    if (mCell.get())\n    {\n        QModelIndex topLeft = mReferenceablesModel->index (start, 0);\n        QModelIndex bottomRight =\n            mReferenceablesModel->index (end, mReferenceablesModel->columnCount());\n\n        if (mCell.get()->referenceableDataChanged (topLeft, bottomRight))\n            flagAsModified();\n    }\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    if (mCell.get())\n        if (mCell.get()->referenceDataChanged (topLeft, bottomRight))\n            flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent,\n    int start, int end)\n{\n    if (mCell.get())\n        if (mCell.get()->referenceAboutToBeRemoved (parent, start, end))\n            flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start,\n    int end)\n{\n    if (mCell.get())\n        if (mCell.get()->referenceAdded (parent, start, end))\n            flagAsModified();\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();\n\n    int rowStart = -1;\n    int rowEnd = -1;\n\n    if (topLeft.parent().isValid())\n    {\n        rowStart = topLeft.parent().row();\n        rowEnd = bottomRight.parent().row();\n    }\n    else\n    {\n        rowStart = topLeft.row();\n        rowEnd = bottomRight.row();\n    }\n\n    for (int row = rowStart; row <= rowEnd; ++row)\n    {\n        const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get();\n        if (mCellId == pathgrid.mId)\n        {\n            mCell->pathgridModified();\n            flagAsModified();\n            return;\n        }\n    }\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();\n\n    if (!parent.isValid())\n    {\n        // Pathgrid going to be deleted\n        for (int row = start; row <= end; ++row)\n        {\n            const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get();\n            if (mCellId == pathgrid.mId)\n            {\n                mCell->pathgridRemoved();\n                flagAsModified();\n                return;\n            }\n        }\n    }\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::pathgridAdded (const QModelIndex& parent, int start, int end)\n{\n    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();\n\n    if (!parent.isValid())\n    {\n        for (int row = start; row <= end; ++row)\n        {\n            const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get();\n            if (mCellId == pathgrid.mId)\n            {\n                mCell->pathgridModified();\n                flagAsModified();\n                return;\n            }\n        }\n    }\n}\n\nvoid CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons (\n    CSVWidget::SceneToolToggle2 *tool)\n{\n    WorldspaceWidget::addVisibilitySelectorButtons (tool);\n    tool->addButton (Button_Terrain, Mask_Terrain, \"Terrain\", \"\", true);\n    tool->addButton (Button_Fog, Mask_Fog, \"Fog\");\n}\n\nstd::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction()\n{\n    osg::Vec3d eye, center, up;\n    mView->getCamera()->getViewMatrixAsLookAt(eye, center, up);\n    osg::Vec3d position = eye;\n\n    std::ostringstream stream;\n\n    stream\n        << \"player->positionCell \"\n        << position.x() << \", \" << position.y() << \", \" << position.z()\n        << \", 0, \\\"\" << mCellId << \"\\\"\";\n\n    return stream.str();\n}\n\nCSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::DropType type) const\n{\n    dropRequirments requirements = WorldspaceWidget::getDropRequirements (type);\n\n    if (requirements!=ignored)\n        return requirements;\n\n    switch(type)\n    {\n        case Type_CellsInterior:\n            return canHandle;\n\n        case Type_CellsExterior:\n            return needPaged;\n\n        default:\n            return ignored;\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/unpagedworldspacewidget.hpp",
    "content": "#ifndef OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H\n#define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H\n\n#include <string>\n#include <memory>\n\n#include \"worldspacewidget.hpp\"\n#include \"cell.hpp\"\n\nclass QModelIndex;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class IdTable;\n    class CellCoordinates;\n}\n\nnamespace CSVRender\n{\n    class UnpagedWorldspaceWidget : public WorldspaceWidget\n    {\n            Q_OBJECT\n\n            CSMDoc::Document& mDocument;\n            std::string mCellId;\n            CSMWorld::IdTable *mCellsModel;\n            CSMWorld::IdTable *mReferenceablesModel;\n            std::unique_ptr<Cell> mCell;\n\n            void update();\n\n        public:\n\n            UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document,\n                                     QWidget *parent);\n\n            dropRequirments getDropRequirements(DropType type) const override;\n\n            /// \\return Drop handled?\n            bool handleDrop (const std::vector<CSMWorld::UniversalId>& data,\n                DropType type) override;\n\n            /// \\param elementMask Elements to be affected by the clear operation\n            void clearSelection (int elementMask) override;\n\n            /// \\param elementMask Elements to be affected by the select operation\n            void invertSelection (int elementMask) override;\n\n            /// \\param elementMask Elements to be affected by the select operation\n            void selectAll (int elementMask) override;\n\n            // Select everything that references the same ID as at least one of the elements\n            // already selected\n            //\n            /// \\param elementMask Elements to be affected by the select operation\n            void selectAllWithSameParentId (int elementMask) override;\n\n            void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) override;\n\n            void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) override;\n\n            std::string getCellId (const osg::Vec3f& point) const override;\n\n            Cell* getCell(const osg::Vec3d& point) const override;\n\n            Cell* getCell(const CSMWorld::CellCoordinates& coords) const override;\n\n            std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)\n                const override;\n\n            std::vector<osg::ref_ptr<TagBase> > getEdited (unsigned int elementMask)\n                const override;\n\n            void setSubMode (int subMode, unsigned int elementMask) override;\n\n            /// Erase all overrides and restore the visual representation to its true state.\n            void reset (unsigned int elementMask) override;\n\n        private:\n\n            void referenceableDataChanged (const QModelIndex& topLeft,\n                const QModelIndex& bottomRight) override;\n\n            void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) override;\n\n            void referenceableAdded (const QModelIndex& index, int start, int end) override;\n\n            void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) override;\n\n            void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) override;\n\n            void referenceAdded (const QModelIndex& index, int start, int end) override;\n\n            void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) override;\n\n            void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end) override;\n\n            void pathgridAdded (const QModelIndex& parent, int start, int end) override;\n\n            std::string getStartupInstruction()  override;\n\n        protected:\n\n            void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool) override;\n\n        private slots:\n\n            void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void assetTablesChanged ();\n\n        signals:\n\n            void cellChanged(const CSMWorld::UniversalId& id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/render/worldspacewidget.cpp",
    "content": "#include \"worldspacewidget.hpp\"\n\n#include <algorithm>\n\n#include <QEvent>\n#include <QDragEnterEvent>\n#include <QDragMoveEvent>\n#include <QDropEvent>\n#include <QMouseEvent>\n#include <QKeyEvent>\n#include <QApplication>\n#include <QToolTip>\n\n#include <osgUtil/LineSegmentIntersector>\n\n#include \"../../model/world/universalid.hpp\"\n#include \"../../model/world/idtable.hpp\"\n\n#include \"../../model/prefs/shortcut.hpp\"\n#include \"../../model/prefs/state.hpp\"\n\n#include \"../render/orbitcameramode.hpp\"\n\n#include \"../widget/scenetoolmode.hpp\"\n#include \"../widget/scenetooltoggle2.hpp\"\n#include \"../widget/scenetoolrun.hpp\"\n\n#include \"object.hpp\"\n#include \"mask.hpp\"\n#include \"instancemode.hpp\"\n#include \"pathgridmode.hpp\"\n#include \"cameracontroller.hpp\"\n\nCSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)\n    : SceneWidget (document.getData().getResourceSystem(), parent, Qt::WindowFlags(), false)\n    , mSceneElements(nullptr)\n    , mRun(nullptr)\n    , mDocument(document)\n    , mInteractionMask (0)\n    , mEditMode (nullptr)\n    , mLocked (false)\n    , mDragMode(InteractionType_None)\n    , mDragging (false)\n    , mDragX(0)\n    , mDragY(0)\n    , mSpeedMode(false)\n    , mDragFactor(0)\n    , mDragWheelFactor(0)\n    , mDragShiftFactor(0)\n    , mToolTipPos (-1, -1)\n    , mShowToolTips(false)\n    , mToolTipDelay(0)\n    , mInConstructor(true)\n{\n    setAcceptDrops(true);\n\n    QAbstractItemModel *referenceables =\n        document.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables);\n\n    connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (referenceableDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int)));\n    connect (referenceables, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (referenceableAdded (const QModelIndex&, int, int)));\n\n    QAbstractItemModel *references =\n        document.getData().getTableModel (CSMWorld::UniversalId::Type_References);\n\n    connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (referenceDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (referenceAboutToBeRemoved (const QModelIndex&, int, int)));\n    connect (references, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (referenceAdded (const QModelIndex&, int, int)));\n\n    QAbstractItemModel *pathgrids = document.getData().getTableModel (CSMWorld::UniversalId::Type_Pathgrids);\n\n    connect (pathgrids, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (pathgridDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (pathgrids, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (pathgridAboutToBeRemoved (const QModelIndex&, int, int)));\n    connect (pathgrids, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (pathgridAdded (const QModelIndex&, int, int)));\n\n    QAbstractItemModel *debugProfiles =\n        document.getData().getTableModel (CSMWorld::UniversalId::Type_DebugProfiles);\n\n    connect (debugProfiles, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (debugProfileDataChanged (const QModelIndex&, const QModelIndex&)));\n    connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (debugProfileAboutToBeRemoved (const QModelIndex&, int, int)));\n\n    mToolTipDelayTimer.setSingleShot (true);\n    connect (&mToolTipDelayTimer, SIGNAL (timeout()), this, SLOT (showToolTip()));\n\n    CSMPrefs::get()[\"3D Scene Input\"].update();\n    CSMPrefs::get()[\"Tooltips\"].update();\n\n    // Shortcuts\n    CSMPrefs::Shortcut* primaryEditShortcut = new CSMPrefs::Shortcut(\"scene-edit-primary\", \"scene-speed-modifier\",\n            CSMPrefs::Shortcut::SM_Detach, this);\n    CSMPrefs::Shortcut* primaryOpenShortcut = new CSMPrefs::Shortcut(\"scene-open-primary\", this);\n\n    connect(primaryOpenShortcut, SIGNAL(activated(bool)), this, SLOT(primaryOpen(bool)));\n    connect(primaryEditShortcut, SIGNAL(activated(bool)), this, SLOT(primaryEdit(bool)));\n    connect(primaryEditShortcut, SIGNAL(secondary(bool)), this, SLOT(speedMode(bool)));\n\n    CSMPrefs::Shortcut* secondaryEditShortcut = new CSMPrefs::Shortcut(\"scene-edit-secondary\", this);\n    connect(secondaryEditShortcut, SIGNAL(activated(bool)), this, SLOT(secondaryEdit(bool)));\n\n    CSMPrefs::Shortcut* primarySelectShortcut = new CSMPrefs::Shortcut(\"scene-select-primary\", this);\n    connect(primarySelectShortcut, SIGNAL(activated(bool)), this, SLOT(primarySelect(bool)));\n\n    CSMPrefs::Shortcut* secondarySelectShortcut = new CSMPrefs::Shortcut(\"scene-select-secondary\", this);\n    connect(secondarySelectShortcut, SIGNAL(activated(bool)), this, SLOT(secondarySelect(bool)));\n\n    CSMPrefs::Shortcut* abortShortcut = new CSMPrefs::Shortcut(\"scene-edit-abort\", this);\n    connect(abortShortcut, SIGNAL(activated()), this, SLOT(abortDrag()));\n\n    mInConstructor = false;\n}\n\nCSVRender::WorldspaceWidget::~WorldspaceWidget ()\n{\n}\n\nvoid CSVRender::WorldspaceWidget::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==\"3D Scene Input/drag-factor\")\n        mDragFactor = setting->toDouble();\n    else if (*setting==\"3D Scene Input/drag-wheel-factor\")\n        mDragWheelFactor = setting->toDouble();\n    else if (*setting==\"3D Scene Input/drag-shift-factor\")\n        mDragShiftFactor = setting->toDouble();\n    else if (*setting==\"Rendering/object-marker-alpha\" && !mInConstructor)\n    {\n        float alpha = setting->toDouble();\n        // getSelection is virtual, thus this can not be called from the constructor\n        auto selection = getSelection(Mask_Reference);\n        for (osg::ref_ptr<TagBase> tag : selection)\n        {\n            if (auto objTag = dynamic_cast<ObjectTag*>(tag.get()))\n                objTag->mObject->setMarkerTransparency(alpha);\n        }\n    }\n    else if (*setting==\"Tooltips/scene-delay\")\n        mToolTipDelay = setting->toInt();\n    else if (*setting==\"Tooltips/scene\")\n        mShowToolTips = setting->isTrue();\n    else\n        SceneWidget::settingChanged(setting);\n}\n\n\nvoid CSVRender::WorldspaceWidget::useViewHint (const std::string& hint) {}\n\nvoid CSVRender::WorldspaceWidget::selectDefaultNavigationMode()\n{\n    selectNavigationMode(\"1st\");\n}\n\nvoid CSVRender::WorldspaceWidget::centerOrbitCameraOnSelection()\n{\n    std::vector<osg::ref_ptr<TagBase> > selection = getSelection(~0u);\n\n    for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it!=selection.end(); ++it)\n    {\n        if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag*> (it->get()))\n        {\n            mOrbitCamControl->setCenter(objectTag->mObject->getPosition().asVec3());\n        }\n    }\n}\n\nCSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector (\n    CSVWidget::SceneToolbar *parent)\n{\n    CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, \"Camera Mode\");\n\n    /// \\todo replace icons\n    /// \\todo consider user-defined button-mapping\n    tool->addButton (\":scenetoolbar/1st-person\", \"1st\",\n        \"First Person\"\n        \"<ul><li>Camera is held upright</li>\"\n        \"<li>Mouse-Look while holding {scene-navi-primary}</li>\"\n        \"<li>Movement keys: {free-forward}(forward), {free-left}(left), {free-backward}(back), {free-right}(right)</li>\"\n        \"<li>Strafing (also vertically) by holding {scene-navi-secondary}</li>\"\n        \"<li>Mouse wheel moves the camera forward/backward</li>\"\n        \"<li>Hold {scene-speed-modifier} to speed up movement</li>\"\n        \"</ul>\");\n    tool->addButton (\":scenetoolbar/free-camera\", \"free\",\n        \"Free Camera\"\n        \"<ul><li>Mouse-Look while holding {scene-navi-primary}</li>\"\n        \"<li>Movement keys: {free-forward}(forward), {free-left}(left), {free-backward}(back), {free-right}(right)</li>\"\n        \"<li>Roll camera with {free-roll-left} and {free-roll-right} keys</li>\"\n        \"<li>Strafing (also vertically) by holding {scene-navi-secondary}</li>\"\n        \"<li>Mouse wheel moves the camera forward/backward</li>\"\n        \"<li>Hold {free-forward:mod} to speed up movement</li>\"\n        \"</ul>\");\n    tool->addButton(\n        new CSVRender::OrbitCameraMode(this, QIcon(\":scenetoolbar/orbiting-camera\"),\n            \"Orbiting Camera\"\n            \"<ul><li>Always facing the centre point</li>\"\n            \"<li>Rotate around the centre point via {orbit-up}, {orbit-left}, {orbit-down}, {orbit-right} or by moving \"\n                \"the mouse while holding {scene-navi-primary}</li>\"\n            \"<li>Roll camera with {orbit-roll-left} and {orbit-roll-right} keys</li>\"\n            \"<li>Strafing (also vertically) by holding {scene-navi-secondary} (includes relocation of the centre point)</li>\"\n            \"<li>Mouse wheel moves camera away or towards centre point but can not pass through it</li>\"\n            \"<li>Hold {scene-speed-modifier} to speed up movement</li>\"\n            \"</ul>\", tool),\n        \"orbit\");\n\n    connect (tool, SIGNAL (modeChanged (const std::string&)),\n        this, SLOT (selectNavigationMode (const std::string&)));\n\n    return tool;\n}\n\nCSVWidget::SceneToolToggle2 *CSVRender::WorldspaceWidget::makeSceneVisibilitySelector (CSVWidget::SceneToolbar *parent)\n{\n    mSceneElements = new CSVWidget::SceneToolToggle2 (parent,\n        \"Scene Element Visibility\", \":scenetoolbar/scene-view-c\", \":scenetoolbar/scene-view-\");\n\n    addVisibilitySelectorButtons (mSceneElements);\n\n    mSceneElements->setSelectionMask (0xffffffff);\n\n    connect (mSceneElements, SIGNAL (selectionChanged()),\n        this, SLOT (elementSelectionChanged()));\n\n    return mSceneElements;\n}\n\nCSVWidget::SceneToolRun *CSVRender::WorldspaceWidget::makeRunTool (\n    CSVWidget::SceneToolbar *parent)\n{\n    CSMWorld::IdTable& debugProfiles = dynamic_cast<CSMWorld::IdTable&> (\n        *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_DebugProfiles));\n\n    std::vector<std::string> profiles;\n\n    int idColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    int stateColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Modification);\n    int defaultColumn = debugProfiles.findColumnIndex (\n        CSMWorld::Columns::ColumnId_DefaultProfile);\n\n    int size = debugProfiles.rowCount();\n\n    for (int i=0; i<size; ++i)\n    {\n        int state = debugProfiles.data (debugProfiles.index (i, stateColumn)).toInt();\n\n        bool default_ = debugProfiles.data (debugProfiles.index (i, defaultColumn)).toInt();\n\n        if (state!=CSMWorld::RecordBase::State_Deleted && default_)\n            profiles.emplace_back(debugProfiles.data (debugProfiles.index (i, idColumn)).\n                toString().toUtf8().constData());\n    }\n\n    std::sort (profiles.begin(), profiles.end());\n\n    mRun = new CSVWidget::SceneToolRun (parent, \"Run OpenMW from the current camera position\",\n        \":scenetoolbar/play\", profiles);\n\n    connect (mRun, SIGNAL (runRequest (const std::string&)),\n        this, SLOT (runRequest (const std::string&)));\n\n    return mRun;\n}\n\nCSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeEditModeSelector (\n    CSVWidget::SceneToolbar *parent)\n{\n    mEditMode = new CSVWidget::SceneToolMode (parent, \"Edit Mode\");\n\n    addEditModeSelectorButtons (mEditMode);\n\n    connect (mEditMode, SIGNAL (modeChanged (const std::string&)),\n        this, SLOT (editModeChanged (const std::string&)));\n\n    return mEditMode;\n}\n\nCSVRender::WorldspaceWidget::DropType CSVRender::WorldspaceWidget::getDropType (\n    const std::vector< CSMWorld::UniversalId >& data)\n{\n    DropType output = Type_Other;\n\n    for (std::vector<CSMWorld::UniversalId>::const_iterator iter (data.begin());\n        iter!=data.end(); ++iter)\n    {\n        DropType type = Type_Other;\n\n        if (iter->getType()==CSMWorld::UniversalId::Type_Cell ||\n            iter->getType()==CSMWorld::UniversalId::Type_Cell_Missing)\n        {\n            type = iter->getId().substr (0, 1)==\"#\" ? Type_CellsExterior : Type_CellsInterior;\n        }\n        else if (iter->getType()==CSMWorld::UniversalId::Type_DebugProfile)\n            type = Type_DebugProfile;\n\n        if (iter==data.begin())\n            output = type;\n        else if  (output!=type) // mixed types -> ignore\n            return Type_Other;\n    }\n\n    return output;\n}\n\nCSVRender::WorldspaceWidget::dropRequirments\n    CSVRender::WorldspaceWidget::getDropRequirements (DropType type) const\n{\n    if (type==Type_DebugProfile)\n        return canHandle;\n\n    return ignored;\n}\n\nbool CSVRender::WorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& universalIdData,\n    DropType type)\n{\n    if (type==Type_DebugProfile)\n    {\n        if (mRun)\n        {\n            for (std::vector<CSMWorld::UniversalId>::const_iterator iter (universalIdData.begin());\n                iter!=universalIdData.end(); ++iter)\n                mRun->addProfile (iter->getId());\n        }\n\n        return true;\n    }\n\n    return false;\n}\n\nunsigned int CSVRender::WorldspaceWidget::getVisibilityMask() const\n{\n    return mSceneElements->getSelectionMask();\n}\n\nvoid CSVRender::WorldspaceWidget::setInteractionMask (unsigned int mask)\n{\n    mInteractionMask = mask | Mask_CellMarker | Mask_CellArrow;\n}\n\nunsigned int CSVRender::WorldspaceWidget::getInteractionMask() const\n{\n    return mInteractionMask & getVisibilityMask();\n}\n\nvoid CSVRender::WorldspaceWidget::setEditLock (bool locked)\n{\n    dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent()).setEditLock (locked);\n}\n\nvoid CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (\n    CSVWidget::SceneToolToggle2 *tool)\n{\n    tool->addButton (Button_Reference, Mask_Reference, \"Instances\");\n    tool->addButton (Button_Water, Mask_Water, \"Water\");\n    tool->addButton (Button_Pathgrid, Mask_Pathgrid, \"Pathgrid\");\n}\n\nvoid CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool)\n{\n    /// \\todo replace EditMode with suitable subclasses\n    tool->addButton (new InstanceMode (this, mRootNode, tool), \"object\");\n    tool->addButton (new PathgridMode (this, tool), \"pathgrid\");\n}\n\nCSMDoc::Document& CSVRender::WorldspaceWidget::getDocument()\n{\n    return mDocument;\n}\n\nCSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPoint& localPos,\n    unsigned int interactionMask) const\n{\n    // (0,0) is considered the lower left corner of an OpenGL window\n    int x = localPos.x();\n    int y = height() - localPos.y();\n\n    // Convert from screen space to world space\n    osg::Matrixd wpvMat;\n    wpvMat.preMult (mView->getCamera()->getViewport()->computeWindowMatrix());\n    wpvMat.preMult (mView->getCamera()->getProjectionMatrix());\n    wpvMat.preMult (mView->getCamera()->getViewMatrix());\n    wpvMat = osg::Matrixd::inverse (wpvMat);\n\n    osg::Vec3d start = wpvMat.preMult (osg::Vec3d(x, y, 0));\n    osg::Vec3d end = wpvMat.preMult (osg::Vec3d(x, y, 1));\n    osg::Vec3d direction = end - start;\n\n    // Get intersection\n    osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(\n        osgUtil::Intersector::MODEL, start, end));\n\n    intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);\n    osgUtil::IntersectionVisitor visitor(intersector);\n\n    visitor.setTraversalMask(interactionMask);\n\n    mView->getCamera()->accept(visitor);\n\n    // Get relevant data\n    for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();\n         it != intersector->getIntersections().end(); ++it)\n    {\n        osgUtil::LineSegmentIntersector::Intersection intersection = *it;\n\n        // reject back-facing polygons\n        if (direction * intersection.getWorldIntersectNormal() > 0)\n        {\n            continue;\n        }\n\n        for (std::vector<osg::Node*>::iterator nodeIter = intersection.nodePath.begin(); nodeIter != intersection.nodePath.end(); ++nodeIter)\n        {\n            osg::Node* node = *nodeIter;\n            if (osg::ref_ptr<CSVRender::TagBase> tag = dynamic_cast<CSVRender::TagBase *>(node->getUserData()))\n            {\n                WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() };\n                if (intersection.indexList.size() >= 3)\n                {\n                    hit.index0 = intersection.indexList[0];\n                    hit.index1 = intersection.indexList[1];\n                    hit.index2 = intersection.indexList[2];\n                }\n                return hit;\n            }\n        }\n\n        // Something untagged, probably terrain\n        WorldspaceHitResult hit = { true, nullptr, 0, 0, 0, intersection.getWorldIntersectPoint() };\n        if (intersection.indexList.size() >= 3)\n        {\n            hit.index0 = intersection.indexList[0];\n            hit.index1 = intersection.indexList[1];\n            hit.index2 = intersection.indexList[2];\n        }\n        return hit;\n    }\n\n    // Default placement\n    direction.normalize();\n    direction *= CSMPrefs::get()[\"3D Scene Editing\"][\"distance\"].toInt();\n\n    WorldspaceHitResult hit = { false, nullptr, 0, 0, 0, start + direction };\n    return hit;\n}\n\nCSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode()\n{\n    return dynamic_cast<CSVRender::EditMode *> (mEditMode->getCurrent());\n}\n\nvoid CSVRender::WorldspaceWidget::abortDrag()\n{\n    if (mDragging)\n    {\n        EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());\n\n        editMode.dragAborted();\n        mDragging = false;\n        mDragMode = InteractionType_None;\n    }\n}\n\nvoid CSVRender::WorldspaceWidget::dragEnterEvent (QDragEnterEvent* event)\n{\n    const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n    if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped\n        return;\n\n    if (mime->fromDocument (mDocument))\n    {\n        if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) ||\n            mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) ||\n            mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile))\n        {\n            // These drops are handled through the subview object.\n            event->accept();\n        }\n        else\n            dynamic_cast<EditMode&> (*mEditMode->getCurrent()).dragEnterEvent (event);\n    }\n}\n\nvoid CSVRender::WorldspaceWidget::dragMoveEvent(QDragMoveEvent *event)\n{\n    const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n    if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped\n        return;\n\n    if (mime->fromDocument (mDocument))\n    {\n        if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) ||\n            mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) ||\n            mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile))\n        {\n            // These drops are handled through the subview object.\n            event->accept();\n        }\n        else\n            dynamic_cast<EditMode&> (*mEditMode->getCurrent()).dragMoveEvent (event);\n    }\n}\n\nvoid CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event)\n{\n    const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n    if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped\n        return;\n\n    if (mime->fromDocument (mDocument))\n    {\n        if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) ||\n            mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) ||\n            mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile))\n        {\n            emit dataDropped(mime->getData());\n        }\n        else\n            dynamic_cast<EditMode&> (*mEditMode->getCurrent()).dropEvent (event);\n    }\n}\n\nvoid CSVRender::WorldspaceWidget::runRequest (const std::string& profile)\n{\n    mDocument.startRunning (profile, getStartupInstruction());\n}\n\nvoid CSVRender::WorldspaceWidget::debugProfileDataChanged (const QModelIndex& topLeft,\n    const QModelIndex& bottomRight)\n{\n    if (!mRun)\n        return;\n\n    CSMWorld::IdTable& debugProfiles = dynamic_cast<CSMWorld::IdTable&> (\n        *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_DebugProfiles));\n\n    int idColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    int stateColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Modification);\n\n    for (int i=topLeft.row(); i<=bottomRight.row(); ++i)\n    {\n        int state = debugProfiles.data (debugProfiles.index (i, stateColumn)).toInt();\n\n        // As of version 0.33 this case can not happen because debug profiles exist only in\n        // project or session scope, which means they will never be in deleted state. But we\n        // are adding the code for the sake of completeness and to avoid surprises if debug\n        // profile ever get extended to content scope.\n        if (state==CSMWorld::RecordBase::State_Deleted)\n            mRun->removeProfile (debugProfiles.data (\n                debugProfiles.index (i, idColumn)).toString().toUtf8().constData());\n    }\n}\n\nvoid CSVRender::WorldspaceWidget::debugProfileAboutToBeRemoved (const QModelIndex& parent,\n    int start, int end)\n{\n    if (parent.isValid())\n        return;\n\n    if (!mRun)\n        return;\n\n    CSMWorld::IdTable& debugProfiles = dynamic_cast<CSMWorld::IdTable&> (\n        *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_DebugProfiles));\n\n    int idColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n\n    for (int i=start; i<=end; ++i)\n    {\n        mRun->removeProfile (debugProfiles.data (\n            debugProfiles.index (i, idColumn)).toString().toUtf8().constData());\n    }\n}\n\nvoid CSVRender::WorldspaceWidget::editModeChanged (const std::string& id)\n{\n    dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent()).setEditLock (mLocked);\n    mDragging = false;\n    mDragMode = InteractionType_None;\n}\n\nvoid CSVRender::WorldspaceWidget::showToolTip()\n{\n    if (mShowToolTips)\n    {\n        QPoint pos = QCursor::pos();\n\n        WorldspaceHitResult hit = mousePick (mapFromGlobal (pos), getInteractionMask());\n        if (hit.tag)\n        {\n            bool hideBasics = CSMPrefs::get()[\"Tooltips\"][\"scene-hide-basic\"].isTrue();\n            QToolTip::showText (pos, hit.tag->getToolTip (hideBasics), this);\n        }\n    }\n}\n\nvoid CSVRender::WorldspaceWidget::elementSelectionChanged()\n{\n    setVisibilityMask (getVisibilityMask());\n    flagAsModified();\n    updateOverlay();\n}\n\nvoid CSVRender::WorldspaceWidget::updateOverlay()\n{\n}\n\nvoid CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)\n{\n    dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent()).mouseMoveEvent (event);\n\n    if (mDragging)\n    {\n        int diffX = event->x() - mDragX;\n        int diffY = (height() - event->y()) - mDragY;\n\n        mDragX = event->x();\n        mDragY = height() - event->y();\n\n        double factor = mDragFactor;\n\n        if (mSpeedMode)\n            factor *= mDragShiftFactor;\n\n        EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());\n\n        editMode.drag (event->pos(), diffX, diffY, factor);\n    }\n    else if (mDragMode != InteractionType_None)\n    {\n        EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());\n\n        if (mDragMode == InteractionType_PrimaryEdit)\n            mDragging = editMode.primaryEditStartDrag (event->pos());\n        else if (mDragMode == InteractionType_SecondaryEdit)\n            mDragging = editMode.secondaryEditStartDrag (event->pos());\n        else if (mDragMode == InteractionType_PrimarySelect)\n            mDragging = editMode.primarySelectStartDrag (event->pos());\n        else if (mDragMode == InteractionType_SecondarySelect)\n            mDragging = editMode.secondarySelectStartDrag (event->pos());\n\n        if (mDragging)\n        {\n            mDragX = event->localPos().x();\n            mDragY = height() - event->localPos().y();\n        }\n    }\n    else\n    {\n        if (event->globalPos()!=mToolTipPos)\n        {\n            mToolTipPos = event->globalPos();\n\n            if (mShowToolTips)\n            {\n                QToolTip::hideText();\n                mToolTipDelayTimer.start (mToolTipDelay);\n            }\n        }\n\n        SceneWidget::mouseMoveEvent(event);\n    }\n}\n\nvoid CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event)\n{\n    if (mDragging)\n    {\n        double factor = mDragWheelFactor;\n\n        if (mSpeedMode)\n            factor *= mDragShiftFactor;\n\n        EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());\n        editMode.dragWheel (event->angleDelta().y(), factor);\n    }\n    else\n        SceneWidget::wheelEvent(event);\n}\n\nvoid CSVRender::WorldspaceWidget::handleInteractionPress (const WorldspaceHitResult& hit, InteractionType type)\n{\n    EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());\n\n    if (type == InteractionType_PrimaryEdit)\n        editMode.primaryEditPressed (hit);\n    else if (type == InteractionType_SecondaryEdit)\n        editMode.secondaryEditPressed (hit);\n    else if (type == InteractionType_PrimarySelect)\n        editMode.primarySelectPressed (hit);\n    else if (type == InteractionType_SecondarySelect)\n        editMode.secondarySelectPressed (hit);\n    else if (type == InteractionType_PrimaryOpen)\n        editMode.primaryOpenPressed (hit);\n}\n\nvoid CSVRender::WorldspaceWidget::primaryOpen(bool activate)\n{\n    handleInteraction(InteractionType_PrimaryOpen, activate);\n}\n\nvoid CSVRender::WorldspaceWidget::primaryEdit(bool activate)\n{\n    handleInteraction(InteractionType_PrimaryEdit, activate);\n}\n\nvoid CSVRender::WorldspaceWidget::secondaryEdit(bool activate)\n{\n    handleInteraction(InteractionType_SecondaryEdit, activate);\n}\n\nvoid CSVRender::WorldspaceWidget::primarySelect(bool activate)\n{\n    handleInteraction(InteractionType_PrimarySelect, activate);\n}\n\nvoid CSVRender::WorldspaceWidget::secondarySelect(bool activate)\n{\n    handleInteraction(InteractionType_SecondarySelect, activate);\n}\n\nvoid CSVRender::WorldspaceWidget::speedMode(bool activate)\n{\n    mSpeedMode = activate;\n}\n\nvoid CSVRender::WorldspaceWidget::handleInteraction(InteractionType type, bool activate)\n{\n    if (activate)\n    {\n        if (!mDragging)\n            mDragMode = type;\n    }\n    else\n    {\n        mDragMode = InteractionType_None;\n\n        if (mDragging)\n        {\n            EditMode* editMode = getEditMode();\n            editMode->dragCompleted(mapFromGlobal(QCursor::pos()));\n            mDragging = false;\n        }\n        else\n        {\n            WorldspaceHitResult hit = mousePick(mapFromGlobal(QCursor::pos()), getInteractionMask());\n            handleInteractionPress(hit, type);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/render/worldspacewidget.hpp",
    "content": "#ifndef OPENCS_VIEW_WORLDSPACEWIDGET_H\n#define OPENCS_VIEW_WORLDSPACEWIDGET_H\n\n#include <QTimer>\n#include <osg/Vec3>\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n\n#include \"instancedragmodes.hpp\"\n#include \"scenewidget.hpp\"\n#include \"mask.hpp\"\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSMWorld\n{\n    class CellCoordinates;\n    class UniversalId;\n}\n\nnamespace CSVWidget\n{\n    class SceneToolMode;\n    class SceneToolToggle2;\n    class SceneToolbar;\n    class SceneToolRun;\n}\n\nnamespace CSVRender\n{\n    class TagBase;\n    class Cell;\n    class CellArrow;\n    class EditMode;\n\n    struct WorldspaceHitResult\n    {\n        bool hit;\n        osg::ref_ptr<TagBase> tag;\n        unsigned int index0, index1, index2; // indices of mesh vertices\n        osg::Vec3d worldPos;\n    };\n\n    class WorldspaceWidget : public SceneWidget\n    {\n            Q_OBJECT\n\n            CSVWidget::SceneToolToggle2 *mSceneElements;\n            CSVWidget::SceneToolRun *mRun;\n            CSMDoc::Document& mDocument;\n            unsigned int mInteractionMask;\n            CSVWidget::SceneToolMode *mEditMode;\n            bool mLocked;\n            int mDragMode;\n            bool mDragging;\n            int mDragX;\n            int mDragY;\n            bool mSpeedMode;\n            double mDragFactor;\n            double mDragWheelFactor;\n            double mDragShiftFactor;\n            QTimer mToolTipDelayTimer;\n            QPoint mToolTipPos;\n            bool mShowToolTips;\n            int mToolTipDelay;\n            bool mInConstructor;\n\n        public:\n\n            enum DropType\n            {\n                Type_CellsInterior,\n                Type_CellsExterior,\n                Type_Other,\n                Type_DebugProfile\n            };\n\n            enum dropRequirments\n            {\n                canHandle,\n                needPaged,\n                needUnpaged,\n                ignored //either mixed cells, or not cells\n            };\n\n            enum InteractionType\n            {\n                InteractionType_PrimaryEdit,\n                InteractionType_PrimarySelect,\n                InteractionType_SecondaryEdit,\n                InteractionType_SecondarySelect,\n                InteractionType_PrimaryOpen,\n                InteractionType_None\n            };\n\n            WorldspaceWidget (CSMDoc::Document& document, QWidget *parent = nullptr);\n            ~WorldspaceWidget ();\n\n            CSVWidget::SceneToolMode *makeNavigationSelector (CSVWidget::SceneToolbar *parent);\n            ///< \\attention The created tool is not added to the toolbar (via addTool). Doing that\n            /// is the responsibility of the calling function.\n\n            /// \\attention The created tool is not added to the toolbar (via addTool). Doing\n            /// that is the responsibility of the calling function.\n            CSVWidget::SceneToolToggle2 *makeSceneVisibilitySelector (\n                CSVWidget::SceneToolbar *parent);\n\n            /// \\attention The created tool is not added to the toolbar (via addTool). Doing\n            /// that is the responsibility of the calling function.\n            CSVWidget::SceneToolRun *makeRunTool (CSVWidget::SceneToolbar *parent);\n\n            /// \\attention The created tool is not added to the toolbar (via addTool). Doing\n            /// that is the responsibility of the calling function.\n            CSVWidget::SceneToolMode *makeEditModeSelector (CSVWidget::SceneToolbar *parent);\n\n            void selectDefaultNavigationMode();\n\n            void centerOrbitCameraOnSelection();\n\n            static DropType getDropType(const std::vector<CSMWorld::UniversalId>& data);\n\n            virtual dropRequirments getDropRequirements(DropType type) const;\n\n            virtual void useViewHint (const std::string& hint);\n            ///< Default-implementation: ignored.\n\n            /// \\return Drop handled?\n            virtual bool handleDrop (const std::vector<CSMWorld::UniversalId>& data,\n                DropType type);\n\n            virtual unsigned int getVisibilityMask() const;\n\n            /// \\note This function will implicitly add elements that are independent of the\n            /// selected edit mode.\n            virtual void setInteractionMask (unsigned int mask);\n\n            /// \\note This function will only return those elements that are both visible and\n            /// marked for interaction.\n            unsigned int getInteractionMask() const;\n\n            virtual void setEditLock (bool locked);\n\n            CSMDoc::Document& getDocument();\n\n            /// \\param elementMask Elements to be affected by the clear operation\n            virtual void clearSelection (int elementMask) = 0;\n\n            /// \\param elementMask Elements to be affected by the select operation\n            virtual void invertSelection (int elementMask) = 0;\n\n            /// \\param elementMask Elements to be affected by the select operation\n            virtual void selectAll (int elementMask) = 0;\n\n            // Select everything that references the same ID as at least one of the elements\n            // already selected\n            //\n            /// \\param elementMask Elements to be affected by the select operation\n            virtual void selectAllWithSameParentId (int elementMask) = 0;\n\n            virtual void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) = 0;\n\n            virtual void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) = 0;\n\n            /// Return the next intersection with scene elements matched by\n            /// \\a interactionMask based on \\a localPos and the camera vector.\n            /// If there is no such intersection, instead a point \"in front\" of \\a localPos will be\n            /// returned.\n            WorldspaceHitResult mousePick (const QPoint& localPos, unsigned int interactionMask) const;\n\n            virtual std::string getCellId (const osg::Vec3f& point) const = 0;\n\n            /// \\note Returns the cell if it exists, otherwise a null pointer\n            virtual Cell* getCell(const osg::Vec3d& point) const = 0;\n\n            virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const = 0;\n\n            virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)\n                const = 0;\n\n            virtual std::vector<osg::ref_ptr<TagBase> > getEdited (unsigned int elementMask)\n                const = 0;\n\n            virtual void setSubMode (int subMode, unsigned int elementMask) = 0;\n\n            /// Erase all overrides and restore the visual representation to its true state.\n            virtual void reset (unsigned int elementMask) = 0;\n\n            EditMode *getEditMode();\n\n        protected:\n\n            /// Visual elements in a scene\n            /// @note do not change the enumeration values, they are used in pre-existing button file names!\n            enum ButtonId\n            {\n                Button_Reference = 0x1,\n                Button_Pathgrid = 0x2,\n                Button_Water = 0x4,\n                Button_Fog = 0x8,\n                Button_Terrain = 0x10\n            };\n\n            virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool);\n\n            virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool);\n\n            virtual void updateOverlay();\n\n            void mouseMoveEvent (QMouseEvent *event) override;\n            void wheelEvent (QWheelEvent *event) override;\n\n            virtual void handleInteractionPress (const WorldspaceHitResult& hit, InteractionType type);\n\n            void settingChanged (const CSMPrefs::Setting *setting) override;\n\n            bool getSpeedMode();\n\n        private:\n\n            void dragEnterEvent(QDragEnterEvent *event) override;\n\n            void dropEvent(QDropEvent* event) override;\n\n            void dragMoveEvent(QDragMoveEvent *event) override;\n\n            virtual std::string getStartupInstruction() = 0;\n\n            void handleInteraction(InteractionType type, bool activate);\n\n        public slots:\n\n            /// \\note Drags will be automatically aborted when the aborting is triggered\n            /// (either explicitly or implicitly) from within this class. This function only\n            /// needs to be called, when the drag abort is triggered externally (e.g. from\n            /// an edit mode).\n            void abortDrag();\n\n        private slots:\n\n            virtual void referenceableDataChanged (const QModelIndex& topLeft,\n                const QModelIndex& bottomRight) = 0;\n\n            virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0;\n\n            virtual void referenceableAdded (const QModelIndex& index, int start, int end) = 0;\n\n            virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0;\n\n            virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0;\n\n            virtual void referenceAdded (const QModelIndex& index, int start, int end) = 0;\n\n            virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0;\n\n            virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0;\n\n            virtual void pathgridAdded (const QModelIndex& parent, int start, int end) = 0;\n\n\n            virtual void runRequest (const std::string& profile);\n\n            void debugProfileDataChanged (const QModelIndex& topLeft,\n                const QModelIndex& bottomRight);\n\n            void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n            void editModeChanged (const std::string& id);\n\n            void showToolTip();\n\n            void primaryOpen(bool activate);\n\n            void primaryEdit(bool activate);\n\n            void secondaryEdit(bool activate);\n\n            void primarySelect(bool activate);\n\n            void secondarySelect(bool activate);\n\n            void speedMode(bool activate);\n\n        protected slots:\n\n            void elementSelectionChanged();\n\n        signals:\n\n            void closeRequest();\n\n            void dataDropped(const std::vector<CSMWorld::UniversalId>& data);\n\n            void requestFocus (const std::string& id);\n\n        friend class MouseState;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/tools/merge.cpp",
    "content": "\n#include \"merge.hpp\"\n\n#include <QVBoxLayout>\n#include <QDialogButtonBox>\n#include <QSplitter>\n#include <QPushButton>\n#include <QListWidget>\n#include <QLabel>\n#include <QKeyEvent>\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/doc/documentmanager.hpp\"\n\n#include \"../doc/filewidget.hpp\"\n#include \"../doc/adjusterwidget.hpp\"\n\nvoid CSVTools::Merge::keyPressEvent (QKeyEvent *event)\n{\n    if (event->key()==Qt::Key_Escape)\n    {\n        event->accept();\n        cancel();\n    }\n    else\n        QWidget::keyPressEvent (event);\n}\n\nCSVTools::Merge::Merge (CSMDoc::DocumentManager& documentManager, QWidget *parent)\n: QWidget (parent), mDocument (nullptr), mDocumentManager (documentManager)\n{\n    setWindowTitle (\"Merge Content Files into a new Game File\");\n\n    QVBoxLayout *mainLayout = new QVBoxLayout;\n    setLayout (mainLayout);\n\n    QSplitter *splitter = new QSplitter (Qt::Horizontal, this);\n\n    mainLayout->addWidget (splitter, 1);\n\n    // left panel (files to be merged)\n    QWidget *left = new QWidget (this);\n    left->setContentsMargins (0, 0, 0, 0);\n    splitter->addWidget (left);\n\n    QVBoxLayout *leftLayout = new QVBoxLayout;\n    left->setLayout (leftLayout);\n\n    leftLayout->addWidget (new QLabel (\"Files to be merged\", this));\n\n    mFiles = new QListWidget (this);\n    leftLayout->addWidget (mFiles, 1);\n\n    // right panel (new game file)\n    QWidget *right = new QWidget (this);\n    right->setContentsMargins (0, 0, 0, 0);\n    splitter->addWidget (right);\n\n    QVBoxLayout *rightLayout = new QVBoxLayout;\n    rightLayout->setAlignment (Qt::AlignTop);\n    right->setLayout (rightLayout);\n\n    rightLayout->addWidget (new QLabel (\"New game file\", this));\n\n    mNewFile = new CSVDoc::FileWidget (this);\n    mNewFile->setType (false);\n    mNewFile->extensionLabelIsVisible (true);\n    rightLayout->addWidget (mNewFile);\n\n    mAdjuster = new CSVDoc::AdjusterWidget (this);\n\n    rightLayout->addWidget (mAdjuster);\n\n    connect (mNewFile, SIGNAL (nameChanged (const QString&, bool)),\n        mAdjuster, SLOT (setName (const QString&, bool)));\n    connect (mAdjuster, SIGNAL (stateChanged (bool)), this, SLOT (stateChanged (bool)));\n\n    // buttons\n    QDialogButtonBox *buttons = new QDialogButtonBox (QDialogButtonBox::Cancel, Qt::Horizontal, this);\n\n    connect (buttons->button (QDialogButtonBox::Cancel), SIGNAL (clicked()), this, SLOT (cancel()));\n\n    mOkay = new QPushButton (\"Merge\", this);\n    connect (mOkay, SIGNAL (clicked()), this, SLOT (accept()));\n    mOkay->setDefault (true);\n    buttons->addButton (mOkay, QDialogButtonBox::AcceptRole);\n\n    mainLayout->addWidget (buttons);\n}\n\nvoid CSVTools::Merge::configure (CSMDoc::Document *document)\n{\n    mDocument = document;\n\n    mNewFile->setName (\"\");\n\n    // content files\n    while (mFiles->count())\n        delete mFiles->takeItem (0);\n\n    std::vector<boost::filesystem::path> files = document->getContentFiles();\n\n    for (std::vector<boost::filesystem::path>::const_iterator iter (files.begin());\n        iter!=files.end(); ++iter)\n        mFiles->addItem (QString::fromUtf8 (iter->filename().string().c_str()));\n}\n\nvoid CSVTools::Merge::setLocalData (const boost::filesystem::path& localData)\n{\n    mAdjuster->setLocalData (localData);\n}\n\nCSMDoc::Document *CSVTools::Merge::getDocument() const\n{\n    return mDocument;\n}\n\nvoid CSVTools::Merge::cancel()\n{\n    mDocument = nullptr;\n    hide();\n}\n\nvoid CSVTools::Merge::accept()\n{\n    if ((mDocument->getState() & CSMDoc::State_Merging)==0)\n    {\n        std::vector< boost::filesystem::path > files (1, mAdjuster->getPath());\n\n        std::unique_ptr<CSMDoc::Document> target (\n            mDocumentManager.makeDocument (files, files[0], true));\n\n        mDocument->runMerge (std::move(target));\n\n        hide();\n    }\n}\n\nvoid CSVTools::Merge::stateChanged (bool valid)\n{\n    mOkay->setEnabled (valid);\n}\n"
  },
  {
    "path": "apps/opencs/view/tools/merge.hpp",
    "content": "#ifndef CSV_TOOLS_REPORTTABLE_H\n#define CSV_TOOLS_REPORTTABLE_H\n\n#include <QWidget>\n\n#ifndef Q_MOC_RUN\n#include <boost/filesystem/path.hpp>\n#endif\n\nclass QPushButton;\nclass QListWidget;\n\nnamespace CSMDoc\n{\n    class Document;\n    class DocumentManager;\n}\n\nnamespace CSVDoc\n{\n    class FileWidget;\n    class AdjusterWidget;\n}\n\nnamespace CSVTools\n{\n    class Merge : public QWidget\n    {\n            Q_OBJECT\n\n            CSMDoc::Document *mDocument;\n            QPushButton *mOkay;\n            QListWidget *mFiles;\n            CSVDoc::FileWidget *mNewFile;\n            CSVDoc::AdjusterWidget *mAdjuster;\n            CSMDoc::DocumentManager& mDocumentManager;\n\n            void keyPressEvent (QKeyEvent *event) override;\n\n        public:\n\n            Merge (CSMDoc::DocumentManager& documentManager, QWidget *parent = nullptr);\n\n            /// Configure dialogue for a new merge\n            void configure (CSMDoc::Document *document);\n\n            void setLocalData (const boost::filesystem::path& localData);\n\n            CSMDoc::Document *getDocument() const;\n\n        public slots:\n\n            void cancel();\n\n        private slots:\n\n            void accept();\n\n            void stateChanged (bool valid);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/tools/reportsubview.cpp",
    "content": "#include \"reportsubview.hpp\"\n\n#include \"reporttable.hpp\"\n\nCSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)\n: CSVDoc::SubView (id), mDocument (document), mRefreshState (0)\n{\n    if (id.getType()==CSMWorld::UniversalId::Type_VerificationResults)\n        mRefreshState = CSMDoc::State_Verifying;\n\n    setWidget (mTable = new ReportTable (document, id, false, mRefreshState, this));\n\n    connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)),\n        SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)));\n\n    if (mRefreshState==CSMDoc::State_Verifying)\n    {\n        connect (mTable, SIGNAL (refreshRequest()), this, SLOT (refreshRequest()));\n\n        connect (&document, SIGNAL (stateChanged (int, CSMDoc::Document *)),\n            mTable, SLOT (stateChanged (int, CSMDoc::Document *)));\n    }\n}\n\nvoid CSVTools::ReportSubView::setEditLock (bool locked)\n{\n    // ignored. We don't change document state anyway.\n}\n\nvoid CSVTools::ReportSubView::refreshRequest()\n{\n    if (!(mDocument.getState() & mRefreshState))\n    {\n        if (mRefreshState==CSMDoc::State_Verifying)\n        {\n            mTable->clear();\n            mDocument.verify (getUniversalId());\n        }\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/tools/reportsubview.hpp",
    "content": "#ifndef CSV_TOOLS_REPORTSUBVIEW_H\n#define CSV_TOOLS_REPORTSUBVIEW_H\n\n#include \"../doc/subview.hpp\"\n\nclass QTableView;\nclass QModelIndex;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVTools\n{\n    class ReportTable;\n\n    class ReportSubView : public CSVDoc::SubView\n    {\n            Q_OBJECT\n\n            ReportTable *mTable;\n            CSMDoc::Document& mDocument;\n            int mRefreshState;\n\n        public:\n\n            ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);\n\n            void setEditLock (bool locked) override;\n\n        private slots:\n\n            void refreshRequest();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/tools/reporttable.cpp",
    "content": "#include \"reporttable.hpp\"\n\n#include <algorithm>\n\n#include <QHeaderView>\n#include <QAction>\n#include <QMenu>\n#include <QStyledItemDelegate>\n#include <QTextDocument>\n#include <QPainter>\n#include <QContextMenuEvent>\n#include <QMouseEvent>\n#include <QSortFilterProxyModel>\n\n#include \"../../model/tools/reportmodel.hpp\"\n\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"../../view/world/idtypedelegate.hpp\"\n\nnamespace CSVTools\n{\n    class RichTextDelegate : public QStyledItemDelegate\n    {\n        public:\n\n            RichTextDelegate (QObject *parent = nullptr);\n\n            void paint(QPainter *painter, const QStyleOptionViewItem& option,\n                const QModelIndex& index) const override;\n    };\n}\n\nCSVTools::RichTextDelegate::RichTextDelegate (QObject *parent) : QStyledItemDelegate (parent)\n{}\n\nvoid CSVTools::RichTextDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option,\n    const QModelIndex& index) const\n{\n    QTextDocument document;\n    QVariant value = index.data (Qt::DisplayRole);\n    if (value.isValid() && !value.isNull())\n    {\n        document.setHtml (value.toString());\n        painter->translate (option.rect.topLeft());\n        document.drawContents (painter);\n        painter->translate (-option.rect.topLeft());\n    }\n}\n\n\nvoid CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)\n{\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    // create context menu\n    QMenu menu (this);\n\n    if (!selectedRows.empty())\n    {\n        menu.addAction (mShowAction);\n        menu.addAction (mRemoveAction);\n\n        bool found = false;\n        for (QModelIndexList::const_iterator iter (selectedRows.begin());\n            iter!=selectedRows.end(); ++iter)\n        {\n            QString hint = mProxyModel->data (mProxyModel->index (iter->row(), 2)).toString();\n\n            if (!hint.isEmpty() && hint[0]=='R')\n            {\n                found = true;\n                break;\n            }\n        }\n\n        if (found)\n            menu.addAction (mReplaceAction);\n    }\n\n    if (mRefreshAction)\n        menu.addAction (mRefreshAction);\n\n    menu.exec (event->globalPos());\n}\n\nvoid CSVTools::ReportTable::mouseMoveEvent (QMouseEvent *event)\n{\n    if (event->buttons() & Qt::LeftButton)\n        startDragFromTable (*this);\n}\n\nvoid CSVTools::ReportTable::mouseDoubleClickEvent (QMouseEvent *event)\n{\n    Qt::KeyboardModifiers modifiers =\n        event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier);\n\n    QModelIndex index = currentIndex();\n\n    selectionModel()->select (index,\n        QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows);\n\n    std::map<Qt::KeyboardModifiers, DoubleClickAction>::iterator iter =\n        mDoubleClickActions.find (modifiers);\n\n    if (iter==mDoubleClickActions.end())\n    {\n        event->accept();\n        return;\n    }\n\n    switch (iter->second)\n    {\n        case Action_None:\n\n            event->accept();\n            break;\n\n        case Action_Edit:\n\n            event->accept();\n            showSelection();\n            break;\n\n        case Action_Remove:\n\n            event->accept();\n            removeSelection();\n            break;\n\n        case Action_EditAndRemove:\n\n            event->accept();\n            showSelection();\n            removeSelection();\n            break;\n    }\n}\n\nCSVTools::ReportTable::ReportTable (CSMDoc::Document& document,\n    const CSMWorld::UniversalId& id, bool richTextDescription, int refreshState,\n    QWidget *parent)\n: CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)),\n  mRefreshAction (nullptr), mRefreshState (refreshState)\n{\n    horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);\n    horizontalHeader()->setStretchLastSection (true);\n    verticalHeader()->hide();\n    setSortingEnabled (true);\n    setSelectionBehavior (QAbstractItemView::SelectRows);\n    setSelectionMode (QAbstractItemView::ExtendedSelection);\n\n    mProxyModel = new QSortFilterProxyModel (this);\n    mProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);\n    mProxyModel->setSourceModel (mModel);\n    mProxyModel->setSortRole(Qt::UserRole);\n\n    setModel (mProxyModel);\n    setColumnHidden (2, true);\n\n    mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (nullptr,\n        mDocument, this);\n\n    setItemDelegateForColumn (0, mIdTypeDelegate);\n\n    if (richTextDescription)\n        setItemDelegateForColumn (mModel->columnCount()-1, new RichTextDelegate (this));\n\n    mShowAction = new QAction (tr (\"Show\"), this);\n    connect (mShowAction, SIGNAL (triggered()), this, SLOT (showSelection()));\n    addAction (mShowAction);\n    CSMPrefs::Shortcut* showShortcut = new CSMPrefs::Shortcut(\"reporttable-show\", this);\n    showShortcut->associateAction(mShowAction);\n\n    mRemoveAction = new QAction (tr (\"Remove from list\"), this);\n    connect (mRemoveAction, SIGNAL (triggered()), this, SLOT (removeSelection()));\n    addAction (mRemoveAction);\n    CSMPrefs::Shortcut* removeShortcut = new CSMPrefs::Shortcut(\"reporttable-remove\", this);\n    removeShortcut->associateAction(mRemoveAction);\n\n    mReplaceAction = new QAction (tr (\"Replace\"), this);\n    connect (mReplaceAction, SIGNAL (triggered()), this, SIGNAL (replaceRequest()));\n    addAction (mReplaceAction);\n    CSMPrefs::Shortcut* replaceShortcut = new CSMPrefs::Shortcut(\"reporttable-replace\", this);\n    replaceShortcut->associateAction(mReplaceAction);\n\n    if (mRefreshState)\n    {\n        mRefreshAction = new QAction (tr (\"Refresh\"), this);\n        mRefreshAction->setEnabled (!(mDocument.getState() & mRefreshState));\n        connect (mRefreshAction, SIGNAL (triggered()), this, SIGNAL (refreshRequest()));\n        addAction (mRefreshAction);\n        CSMPrefs::Shortcut* refreshShortcut = new CSMPrefs::Shortcut(\"reporttable-refresh\", this);\n        refreshShortcut->associateAction(mRefreshAction);\n    }\n\n    mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_Edit));\n    mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_Remove));\n    mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove));\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n    CSMPrefs::get()[\"Reports\"].update();\n}\n\nstd::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() const\n{\n    std::vector<CSMWorld::UniversalId> ids;\n\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();\n        ++iter)\n    {\n        ids.push_back (mModel->getUniversalId (mProxyModel->mapToSource (*iter).row()));\n    }\n\n    return ids;\n}\n\nstd::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const\n{\n    std::vector<int> indices;\n\n    if (selection)\n    {\n        QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n        std::vector<int> rows;\n\n        for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();\n            ++iter)\n        {\n            rows.push_back (mProxyModel->mapToSource (*iter).row());\n        }\n\n        std::sort (rows.begin(), rows.end());\n\n        for (std::vector<int>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)\n        {\n            QString hint = mModel->data (mModel->index (*iter, 2)).toString();\n\n            if (!hint.isEmpty() && hint[0]=='R')\n                indices.push_back (*iter);\n        }\n    }\n    else\n    {\n        for (int i=0; i<mModel->rowCount(); ++i)\n        {\n            QString hint = mModel->data (mModel->index (i, 2)).toString();\n\n            if (!hint.isEmpty() && hint[0]=='R')\n                indices.push_back (i);\n        }\n    }\n\n    return indices;\n}\n\nvoid CSVTools::ReportTable::flagAsReplaced (int index)\n{\n    mModel->flagAsReplaced (index);\n}\n\nvoid CSVTools::ReportTable::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (setting->getParent()->getKey()==\"Reports\")\n    {\n        QString base (\"double\");\n        QString key = setting->getKey().c_str();\n        if (key.startsWith (base))\n        {\n            QString modifierString = key.mid (base.size());\n            Qt::KeyboardModifiers modifiers;\n\n            if (modifierString==\"-s\")\n                modifiers = Qt::ShiftModifier;\n            else if (modifierString==\"-c\")\n                modifiers = Qt::ControlModifier;\n            else if (modifierString==\"-sc\")\n                modifiers = Qt::ShiftModifier | Qt::ControlModifier;\n\n            DoubleClickAction action = Action_None;\n\n            std::string value = setting->toString();\n\n            if (value==\"Edit\")\n                action = Action_Edit;\n            else if (value==\"Remove\")\n                action = Action_Remove;\n            else if (value==\"Edit And Remove\")\n                action = Action_EditAndRemove;\n\n            mDoubleClickActions[modifiers] = action;\n\n            return;\n        }\n    }\n    else if (*setting==\"Records/type-format\")\n        mIdTypeDelegate->settingChanged (setting);\n}\n\nvoid CSVTools::ReportTable::showSelection()\n{\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();\n        ++iter)\n    {\n        int row = mProxyModel->mapToSource (*iter).row();\n        emit editRequest (mModel->getUniversalId (row), mModel->getHint (row));\n    }\n}\n\nvoid CSVTools::ReportTable::removeSelection()\n{\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    std::vector<int> rows;\n\n    for (QModelIndexList::iterator iter (selectedRows.begin()); iter!=selectedRows.end();\n        ++iter)\n    {\n        rows.push_back (mProxyModel->mapToSource (*iter).row());\n    }\n\n    std::sort (rows.begin(), rows.end());\n\n    for (std::vector<int>::const_reverse_iterator iter (rows.rbegin()); iter!=rows.rend(); ++iter)\n        mProxyModel->removeRows (*iter, 1);\n\n    selectionModel()->clear();\n}\n\nvoid CSVTools::ReportTable::clear()\n{\n    mModel->clear();\n}\n\nvoid CSVTools::ReportTable::stateChanged (int state, CSMDoc::Document *document)\n{\n    if (mRefreshAction)\n        mRefreshAction->setEnabled (!(state & mRefreshState));\n}\n"
  },
  {
    "path": "apps/opencs/view/tools/reporttable.hpp",
    "content": "#ifndef CSV_TOOLS_REPORTTABLE_H\n#define CSV_TOOLS_REPORTTABLE_H\n\n#include <map>\n\n#include \"../world/dragrecordtable.hpp\"\n\nclass QAction;\nclass QSortFilterProxyModel;\n\nnamespace CSMTools\n{\n    class ReportModel;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWorld\n{\n    class CommandDelegate;\n}\n\nnamespace CSVTools\n{\n    class ReportTable : public CSVWorld::DragRecordTable\n    {\n            Q_OBJECT\n\n            enum DoubleClickAction\n            {\n                Action_None,\n                Action_Edit,\n                Action_Remove,\n                Action_EditAndRemove\n            };\n\n            QSortFilterProxyModel *mProxyModel;\n            CSMTools::ReportModel *mModel;\n            CSVWorld::CommandDelegate *mIdTypeDelegate;\n            QAction *mShowAction;\n            QAction *mRemoveAction;\n            QAction *mReplaceAction;\n            QAction *mRefreshAction;\n            std::map<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions;\n            int mRefreshState;\n\n        private:\n\n            void contextMenuEvent (QContextMenuEvent *event) override;\n\n            void mouseMoveEvent (QMouseEvent *event) override;\n\n            void mouseDoubleClickEvent (QMouseEvent *event) override;\n\n        public:\n\n            /// \\param richTextDescription Use rich text in the description column.\n            /// \\param refreshState Document state to check for refresh function. If value is\n            /// 0 no refresh function exists. If the document current has the specified state\n            /// the refresh function is disabled.\n            ReportTable (CSMDoc::Document& document, const CSMWorld::UniversalId& id,\n                bool richTextDescription, int refreshState = 0, QWidget *parent = nullptr);\n\n            std::vector<CSMWorld::UniversalId> getDraggedRecords() const override;\n\n            void clear();\n\n            /// Return indices of rows that are suitable for replacement.\n            ///\n            /// \\param selection Only list selected rows.\n            ///\n            /// \\return rows in the original model\n            std::vector<int> getReplaceIndices (bool selection) const;\n\n            /// \\param index row in the original model\n            void flagAsReplaced (int index);\n\n        private slots:\n\n            void settingChanged (const CSMPrefs::Setting *setting);\n\n            void showSelection();\n\n            void removeSelection();\n\n        public slots:\n\n            void stateChanged (int state, CSMDoc::Document *document);\n\n        signals:\n\n            void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);\n\n            void replaceRequest();\n\n            void refreshRequest();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/tools/searchbox.cpp",
    "content": "#include \"searchbox.hpp\"\n\n#include <stdexcept>\n\n#include <QGridLayout>\n#include <QComboBox>\n#include <QPushButton>\n\n#include \"../../model/world/columns.hpp\"\n\n#include \"../../model/tools/search.hpp\"\n\nvoid CSVTools::SearchBox::updateSearchButton()\n{\n    if (!mSearchEnabled)\n        mSearch.setEnabled (false);\n    else\n    {\n        switch (mMode.currentIndex())\n        {\n            case 0:\n            case 1:\n            case 2:\n            case 3:\n\n                mSearch.setEnabled (!mText.text().isEmpty());\n                break;\n\n            case 4:\n\n                mSearch.setEnabled (true);\n                break;\n        }\n    }\n}\n\nCSVTools::SearchBox::SearchBox (QWidget *parent)\n: QWidget (parent), mSearch (tr(\"Search\")), mSearchEnabled (false), mReplace (tr(\"Replace All\"))\n{\n    mLayout = new QGridLayout (this);\n\n    // search panel\n    std::vector<std::pair<int,std::string>> states =\n        CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);\n    states.resize (states.size()-1); // ignore erased state\n\n    for (std::vector<std::pair<int,std::string>>::const_iterator iter (states.begin()); iter!=states.end();\n        ++iter)\n        mRecordState.addItem (QString::fromUtf8 (iter->second.c_str()));\n        \n    mMode.addItem (tr(\"Text\"));\n    mMode.addItem (tr(\"Text (RegEx)\"));\n    mMode.addItem (tr(\"ID\"));\n    mMode.addItem (tr(\"ID (RegEx)\"));\n    mMode.addItem (tr(\"Record State\"));\n    connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));\n    mLayout->addWidget (&mMode, 0, 0);\n\n    connect (&mText, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));\n    connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch()));\n    mInput.insertWidget (0, &mText);\n\n    mInput.insertWidget (1, &mRecordState);\n    mLayout->addWidget (&mInput, 0, 1);\n\n    mCaseSensitive.setText (tr (\"Case\"));\n    mLayout->addWidget (&mCaseSensitive, 0, 2);\n\n    connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool)));\n    mLayout->addWidget (&mSearch, 0, 3);\n\n    // replace panel\n    mReplaceInput.insertWidget (0, &mReplaceText);\n    mReplaceInput.insertWidget (1, &mReplacePlaceholder);\n\n    mLayout->addWidget (&mReplaceInput, 1, 1);\n\n    mLayout->addWidget (&mReplace, 1, 3);\n    \n    // layout adjustments\n    mLayout->setColumnMinimumWidth (2, 50);\n    mLayout->setColumnStretch (1, 1);\n\n    mLayout->setContentsMargins (0, 0, 0, 0);\n\n    connect (&mReplace, (SIGNAL (clicked (bool))), this, SLOT (replaceAll (bool)));\n    \n    // update\n    modeSelected (0);\n\n    updateSearchButton();\n}\n\nvoid CSVTools::SearchBox::setSearchMode (bool enabled)\n{\n    mSearchEnabled = enabled;\n    updateSearchButton();\n}\n\nCSMTools::Search CSVTools::SearchBox::getSearch() const\n{\n    CSMTools::Search::Type type = static_cast<CSMTools::Search::Type> (mMode.currentIndex());    \n    bool caseSensitive = mCaseSensitive.isChecked();\n\n    switch (type)\n    {\n        case CSMTools::Search::Type_Text:\n        case CSMTools::Search::Type_Id:\n\n            return CSMTools::Search (type, caseSensitive, std::string (mText.text().toUtf8().data()));\n        \n        case CSMTools::Search::Type_TextRegEx:\n        case CSMTools::Search::Type_IdRegEx:\n\n            return CSMTools::Search (type, caseSensitive, QRegExp (mText.text().toUtf8().data(), Qt::CaseInsensitive));\n        \n        case CSMTools::Search::Type_RecordState:\n\n            return CSMTools::Search (type, caseSensitive, mRecordState.currentIndex());\n\n        case CSMTools::Search::Type_None:\n\n            break;\n    }\n\n    throw std::logic_error (\"invalid search mode index\");\n}\n\nstd::string CSVTools::SearchBox::getReplaceText() const\n{\n    CSMTools::Search::Type type = static_cast<CSMTools::Search::Type> (mMode.currentIndex());\n    \n    switch (type)\n    {\n        case CSMTools::Search::Type_Text:\n        case CSMTools::Search::Type_TextRegEx:\n        case CSMTools::Search::Type_Id:\n        case CSMTools::Search::Type_IdRegEx:\n\n            return mReplaceText.text().toUtf8().data();\n\n        default:\n\n            throw std::logic_error (\"Invalid search mode for replace\");\n    }\n}\n\nvoid CSVTools::SearchBox::setEditLock (bool locked)\n{\n    mReplace.setEnabled (!locked);\n}\n\nvoid CSVTools::SearchBox::focus()\n{\n    mInput.currentWidget()->setFocus();\n}\n\nvoid CSVTools::SearchBox::modeSelected (int index)\n{\n    switch (index)\n    {\n        case CSMTools::Search::Type_Text:\n        case CSMTools::Search::Type_TextRegEx:\n        case CSMTools::Search::Type_Id:\n        case CSMTools::Search::Type_IdRegEx:\n\n            mInput.setCurrentIndex (0);\n            mReplaceInput.setCurrentIndex (0);\n            break;\n\n        case CSMTools::Search::Type_RecordState:\n            mInput.setCurrentIndex (1);\n            mReplaceInput.setCurrentIndex (1);\n            break;\n    }\n\n    mInput.currentWidget()->setFocus();\n    \n    updateSearchButton();\n}\n\nvoid CSVTools::SearchBox::textChanged (const QString& text)\n{\n    updateSearchButton();\n}\n\nvoid CSVTools::SearchBox::startSearch (bool checked)\n{\n    if (mSearch.isEnabled())\n        emit startSearch (getSearch());\n}\n\nvoid CSVTools::SearchBox::replaceAll (bool checked)\n{\n    emit replaceAll();\n}\n"
  },
  {
    "path": "apps/opencs/view/tools/searchbox.hpp",
    "content": "#ifndef CSV_TOOLS_SEARCHBOX_H\n#define CSV_TOOLS_SEARCHBOX_H\n\n#include <QWidget>\n#include <QLineEdit>\n#include <QComboBox>\n#include <QCheckBox>\n#include <QStackedWidget>\n#include <QPushButton>\n#include <QLabel>\n\nclass QGridLayout;\n\nnamespace CSMTools\n{\n    class Search;\n}\n\nnamespace CSVTools\n{\n    class SearchBox : public QWidget\n    {\n            Q_OBJECT\n\n            QStackedWidget mInput;\n            QLineEdit mText;\n            QComboBox mRecordState;\n            QCheckBox mCaseSensitive;\n            QPushButton mSearch;\n            QGridLayout *mLayout;\n            QComboBox mMode;\n            bool mSearchEnabled;\n            QStackedWidget mReplaceInput;\n            QLineEdit mReplaceText;\n            QLabel mReplacePlaceholder;\n            QPushButton mReplace;\n\n        private:\n\n            void updateSearchButton();\n            \n        public:\n\n            SearchBox (QWidget *parent = nullptr);\n\n            void setSearchMode (bool enabled);\n\n            CSMTools::Search getSearch() const;\n\n            std::string getReplaceText() const;\n\n            void setEditLock (bool locked);\n\n            void focus();\n\n        private slots:\n\n            void modeSelected (int index);\n\n            void textChanged (const QString& text);\n\n            void startSearch (bool checked = true);\n\n            void replaceAll (bool checked);\n\n        signals:\n\n            void startSearch (const CSMTools::Search& search);\n\n            void replaceAll();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/tools/searchsubview.cpp",
    "content": "#include \"searchsubview.hpp\"\n\n#include <QVBoxLayout>\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/doc/state.hpp\"\n#include \"../../model/tools/search.hpp\"\n#include \"../../model/tools/reportmodel.hpp\"\n#include \"../../model/world/idtablebase.hpp\"\n#include \"../../model/prefs/state.hpp\"\n\n#include \"../world/tablebottombox.hpp\"\n#include \"../world/creator.hpp\"\n\n#include \"reporttable.hpp\"\n#include \"searchbox.hpp\"\n\nvoid CSVTools::SearchSubView::replace (bool selection)\n{\n    if (mLocked)\n        return;\n\n    std::vector<int> indices = mTable->getReplaceIndices (selection);\n\n    std::string replace = mSearchBox.getReplaceText();\n\n    const CSMTools::ReportModel& model =\n        dynamic_cast<const CSMTools::ReportModel&> (*mTable->model());\n\n    bool autoDelete = CSMPrefs::get()[\"Search & Replace\"][\"auto-delete\"].isTrue();\n\n    CSMTools::Search search (mSearch);\n    CSMWorld::IdTableBase *currentTable = nullptr;\n\n    // We are running through the indices in reverse order to avoid messing up multiple results\n    // in a single string.\n    for (std::vector<int>::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter)\n    {\n        CSMWorld::UniversalId id = model.getUniversalId (*iter);\n\n        CSMWorld::UniversalId::Type type = CSMWorld::UniversalId::getParentType (id.getType());\n\n        CSMWorld::IdTableBase *table = &dynamic_cast<CSMWorld::IdTableBase&> (\n            *mDocument.getData().getTableModel (type));\n\n        if (table!=currentTable)\n        {\n            search.configure (table);\n            currentTable = table;\n        }\n\n        std::string hint = model.getHint (*iter);\n\n        if (search.verify (mDocument, table, id, hint))\n        {\n            search.replace (mDocument, table, id, hint, replace);\n            mTable->flagAsReplaced (*iter);\n\n            if (autoDelete)\n                mTable->model()->removeRows (*iter, 1);\n        }\n    }\n}\n\nvoid CSVTools::SearchSubView::showEvent (QShowEvent *event)\n{\n    CSVDoc::SubView::showEvent (event);\n    mSearchBox.focus();\n}\n\nCSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)\n: CSVDoc::SubView (id), mDocument (document), mLocked (false)\n{\n    QVBoxLayout *layout = new QVBoxLayout;\n\n    layout->addWidget (&mSearchBox);\n\n    layout->addWidget (mTable = new ReportTable (document, id, true), 2);\n\n    layout->addWidget (mBottom =\n        new CSVWorld::TableBottomBox (CSVWorld::NullCreatorFactory(), document, id, this), 0);\n\n    QWidget *widget = new QWidget;\n\n    widget->setLayout (layout);\n\n    setWidget (widget);\n\n    stateChanged (document.getState(), &document);\n\n    connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)),\n        SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)));\n\n    connect (mTable, SIGNAL (replaceRequest()), this, SLOT (replaceRequest()));\n\n    connect (&document, SIGNAL (stateChanged (int, CSMDoc::Document *)),\n        this, SLOT (stateChanged (int, CSMDoc::Document *)));\n\n    connect (&mSearchBox, SIGNAL (startSearch (const CSMTools::Search&)),\n        this, SLOT (startSearch (const CSMTools::Search&)));\n\n    connect (&mSearchBox, SIGNAL (replaceAll()), this, SLOT (replaceAllRequest()));\n\n    connect (document.getReport (id), SIGNAL (rowsRemoved (const QModelIndex&, int, int)),\n        this, SLOT (tableSizeUpdate()));\n\n    connect (document.getReport (id), SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (tableSizeUpdate()));\n\n    connect (&document, SIGNAL (operationDone (int, bool)),\n        this, SLOT (operationDone (int, bool)));\n}\n\nvoid CSVTools::SearchSubView::setEditLock (bool locked)\n{\n    mLocked = locked;\n    mSearchBox.setEditLock (locked);\n}\n\nvoid CSVTools::SearchSubView::setStatusBar (bool show)\n{\n    mBottom->setStatusBar(show);\n}\n\nvoid CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *document)\n{\n    mSearchBox.setSearchMode (!(state & CSMDoc::State_Searching));\n}\n\nvoid CSVTools::SearchSubView::startSearch (const CSMTools::Search& search)\n{\n    CSMPrefs::Category& settings = CSMPrefs::get()[\"Search & Replace\"];\n\n    mSearch = search;\n    mSearch.setPadding (settings[\"char-before\"].toInt(), settings[\"char-after\"].toInt());\n\n    mTable->clear();\n    mDocument.runSearch (getUniversalId(), mSearch);\n}\n\nvoid CSVTools::SearchSubView::replaceRequest()\n{\n    replace (true);\n}\n\nvoid CSVTools::SearchSubView::replaceAllRequest()\n{\n    replace (false);\n}\n\nvoid CSVTools::SearchSubView::tableSizeUpdate()\n{\n    mBottom->tableSizeChanged (mDocument.getReport (getUniversalId())->rowCount(), 0, 0);\n}\n\nvoid CSVTools::SearchSubView::operationDone (int type, bool failed)\n{\n    if (type==CSMDoc::State_Searching && !failed &&\n        !mDocument.getReport (getUniversalId())->rowCount())\n    {\n        mBottom->setStatusMessage (\"No Results\");\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/tools/searchsubview.hpp",
    "content": "#ifndef CSV_TOOLS_SEARCHSUBVIEW_H\n#define CSV_TOOLS_SEARCHSUBVIEW_H\n\n#include \"../../model/tools/search.hpp\"\n\n#include \"../doc/subview.hpp\"\n\n#include \"searchbox.hpp\"\n\nclass QTableView;\nclass QModelIndex;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVWorld\n{\n    class TableBottomBox;\n}\n\nnamespace CSVTools\n{\n    class ReportTable;\n\n    class SearchSubView : public CSVDoc::SubView\n    {\n            Q_OBJECT\n\n            ReportTable *mTable;\n            SearchBox mSearchBox;\n            CSMDoc::Document& mDocument;\n            CSMTools::Search mSearch;\n            bool mLocked;\n            CSVWorld::TableBottomBox *mBottom;\n\n        private:\n\n            void replace (bool selection);\n\n        protected:\n\n            void showEvent (QShowEvent *event) override;\n\n        public:\n\n            SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);\n\n            void setEditLock (bool locked) override;\n\n            void setStatusBar (bool show) override;\n\n        private slots:\n\n            void stateChanged (int state, CSMDoc::Document *document);\n\n            void startSearch (const CSMTools::Search& search);\n\n            void replaceRequest();\n\n            void replaceAllRequest();\n\n            void tableSizeUpdate();\n\n            void operationDone (int type, bool failed);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/tools/subviews.cpp",
    "content": "#include \"subviews.hpp\"\n\n#include \"../doc/subviewfactoryimp.hpp\"\n\n#include \"reportsubview.hpp\"\n#include \"searchsubview.hpp\"\n\nvoid CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)\n{\n    manager.add (CSMWorld::UniversalId::Type_VerificationResults,\n        new CSVDoc::SubViewFactory<ReportSubView>);\n    manager.add (CSMWorld::UniversalId::Type_LoadErrorLog,\n        new CSVDoc::SubViewFactory<ReportSubView>);\n    manager.add (CSMWorld::UniversalId::Type_Search,\n        new CSVDoc::SubViewFactory<SearchSubView>);\n}\n"
  },
  {
    "path": "apps/opencs/view/tools/subviews.hpp",
    "content": "#ifndef CSV_TOOLS_SUBVIEWS_H\n#define CSV_TOOLS_SUBVIEWS_H\n\nnamespace CSVDoc\n{\n    class SubViewFactoryManager;\n}\n\nnamespace CSVTools\n{\n    void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager);\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/brushshapes.hpp",
    "content": "#ifndef CSV_WIDGET_BRUSHSHAPES_H\n#define CSV_WIDGET_BRUSHSHAPES_H\n\nnamespace CSVWidget\n{\n    enum BrushShape\n    {\n        BrushShape_Point,\n        BrushShape_Square,\n        BrushShape_Circle,\n        BrushShape_Custom\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/coloreditor.cpp",
    "content": "#include \"coloreditor.hpp\"\n\n#include <QApplication>\n#include <QColorDialog>\n#include <QDesktopWidget>\n#include <QPainter>\n#include <QShowEvent>\n#include <QScreen>\n\n#include \"colorpickerpopup.hpp\"\n\nCSVWidget::ColorEditor::ColorEditor(const QColor &color, QWidget *parent, const bool popupOnStart)\n    : ColorEditor(parent, popupOnStart)\n{\n    setColor(color);\n}\n\nCSVWidget::ColorEditor::ColorEditor(const int colorInt, QWidget *parent, const bool popupOnStart)\n    : ColorEditor(parent, popupOnStart)\n{\n    setColor(colorInt);\n}\n\nCSVWidget::ColorEditor::ColorEditor(QWidget *parent, const bool popupOnStart)\n    : QPushButton(parent),\n      mColorPicker(new ColorPickerPopup(this)),\n      mPopupOnStart(popupOnStart)\n{\n    connect(this, SIGNAL(clicked()), this, SLOT(showPicker()));\n    connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &)));\n}\n\nvoid CSVWidget::ColorEditor::paintEvent(QPaintEvent *event)\n{\n    QPushButton::paintEvent(event);\n\n    QRect buttonRect = rect();\n    QRect coloredRect(buttonRect.x() + qRound(buttonRect.width() / 4.0),\n                      buttonRect.y() + qRound(buttonRect.height() / 4.0),\n                      buttonRect.width() / 2,\n                      buttonRect.height() / 2);\n    QPainter painter(this);\n    painter.fillRect(coloredRect, mColor);\n    painter.setPen(Qt::black);\n    painter.drawRect(coloredRect);\n}\n\nvoid CSVWidget::ColorEditor::showEvent(QShowEvent *event)\n{\n    QPushButton::showEvent(event);\n    if (isVisible() && mPopupOnStart)\n    {\n        setChecked(true);\n        showPicker();\n        mPopupOnStart = false;\n    }\n}\n\nQColor CSVWidget::ColorEditor::color() const\n{\n    return mColor;\n}\n\nint CSVWidget::ColorEditor::colorInt() const\n{\n    return (mColor.blue() << 16) | (mColor.green() << 8) | (mColor.red());\n}\n\nvoid CSVWidget::ColorEditor::setColor(const QColor &color)\n{\n    mColor = color;\n    update();\n}\n\nvoid CSVWidget::ColorEditor::setColor(const int colorInt)\n{\n    // Color RGB values are stored in given integer.\n    // First byte is red, second byte is green, third byte is blue.\n    QColor color = QColor(colorInt & 0xff, (colorInt >> 8) & 0xff, (colorInt >> 16) & 0xff);\n    setColor(color);\n}\n\nvoid CSVWidget::ColorEditor::showPicker()\n{\n    mColorPicker->showPicker(calculatePopupPosition(), mColor);\n    emit pickingFinished();\n}\n\nvoid CSVWidget::ColorEditor::pickerColorChanged(const QColor &color)\n{\n    mColor = color;\n    update();\n}\n\nQPoint CSVWidget::ColorEditor::calculatePopupPosition()\n{\n    QRect editorGeometry = geometry();\n    QRect popupGeometry = mColorPicker->geometry();\n    QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();\n\n    // Center the popup horizontally relative to the editor\n    int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2;\n    // Popup position need to be specified in global coords\n    QPoint popupPosition = mapToGlobal(QPoint(localPopupX, editorGeometry.height()));\n\n    // Make sure that the popup isn't out of the screen\n    if (popupPosition.x() < screenGeometry.left())\n    {\n        popupPosition.setX(screenGeometry.left() + 1);\n    }\n    else if (popupPosition.x() + popupGeometry.width() > screenGeometry.right())\n    {\n        popupPosition.setX(screenGeometry.right() - popupGeometry.width() - 1);\n    }\n    if (popupPosition.y() + popupGeometry.height() > screenGeometry.bottom())\n    {\n        // Place the popup above the editor\n        popupPosition.setY(popupPosition.y() - popupGeometry.height() - editorGeometry.height() - 1);\n    }\n\n    return popupPosition;\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/coloreditor.hpp",
    "content": "#ifndef CSV_WIDGET_COLOREDITOR_HPP\n#define CSV_WIDGET_COLOREDITOR_HPP\n\n#include <QPushButton>\n\nclass QColor;\nclass QPoint;\nclass QSize;\n\nnamespace CSVWidget\n{\n    class ColorPickerPopup;\n\n    class ColorEditor : public QPushButton\n    {\n            Q_OBJECT\n\n            QColor mColor;\n            ColorPickerPopup *mColorPicker;\n            bool mPopupOnStart;\n\n            QPoint calculatePopupPosition();\n\n        public:\n            ColorEditor(const QColor &color, QWidget *parent = nullptr, const bool popupOnStart = false);\n            ColorEditor(const int colorInt, QWidget *parent = nullptr, const bool popupOnStart = false);\n\n            QColor color() const;\n\n            /// \\return Color RGB value encoded in an int.\n            int colorInt() const;\n\n            void setColor(const QColor &color);\n\n            /// \\brief Set color using given int value.\n            /// \\param colorInt RGB color value encoded as an integer.\n            void setColor(const int colorInt);\n\n        protected:\n            void paintEvent(QPaintEvent *event) override;\n            void showEvent(QShowEvent *event) override;\n\n        private:\n            ColorEditor(QWidget *parent = nullptr, const bool popupOnStart = false);\n\n        private slots:\n            void showPicker();\n            void pickerColorChanged(const QColor &color);\n\n        signals:\n            void pickingFinished();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/colorpickerpopup.cpp",
    "content": "#include \"colorpickerpopup.hpp\"\n\n#include <QColorDialog>\n#include <QPushButton>\n#include <QEvent>\n#include <QKeyEvent>\n#include <QLayout>\n#include <QStyleOption>\n\nCSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent) \n    : QFrame(parent)\n{\n    setWindowFlags(Qt::Popup);\n    setFrameStyle(QFrame::Box | QFrame::Plain);\n    hide();\n\n    mColorPicker = new QColorDialog(this);\n    mColorPicker->setWindowFlags(Qt::Widget);\n    mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog);\n    mColorPicker->installEventFilter(this);\n    connect(mColorPicker,\n            SIGNAL(currentColorChanged(const QColor &)),\n            this,\n            SIGNAL(colorChanged(const QColor &)));\n\n    QVBoxLayout *layout = new QVBoxLayout(this);\n    layout->addWidget(mColorPicker);\n    layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);\n    layout->setContentsMargins(0, 0, 0, 0);\n    setLayout(layout);\n    setFixedSize(mColorPicker->size());\n}\n\nvoid CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColor &initialColor)\n{\n    QRect geometry = this->geometry();\n    geometry.moveTo(position);\n    setGeometry(geometry);\n\n    // Calling getColor() creates a blocking dialog that will continue execution once the user chooses OK or Cancel\n    QColor color = mColorPicker->getColor(initialColor);\n    if (color.isValid())\n        mColorPicker->setCurrentColor(color);\n}\n\nvoid CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event)\n{\n    QPushButton *button = qobject_cast<QPushButton *>(parentWidget());\n    if (button != nullptr)\n    {\n        QStyleOptionButton option;\n        option.init(button);\n        QRect buttonRect = option.rect;\n        buttonRect.moveTo(button->mapToGlobal(buttonRect.topLeft()));\n\n        // If the mouse is pressed above the pop-up parent,\n        // the pop-up will be hidden and the pressed signal won't be repeated for the parent\n        if (buttonRect.contains(event->globalPos()) || buttonRect.contains(event->pos()))\n        {\n            setAttribute(Qt::WA_NoMouseReplay);\n        }\n    }\n    QFrame::mousePressEvent(event);\n}\n\nbool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event)\n{\n    if (object == mColorPicker && event->type() == QEvent::KeyPress)\n    {\n        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n        // Prevent QColorDialog from closing when Escape is pressed.\n        // Instead, hide the popup.\n        if (keyEvent->key() == Qt::Key_Escape)\n        {\n            hide();\n            return true;\n        }\n    }\n    return QFrame::eventFilter(object, event);\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/colorpickerpopup.hpp",
    "content": "#ifndef CSVWIDGET_COLORPICKERPOPUP_HPP\n#define CSVWIDGET_COLORPICKERPOPUP_HPP\n\n#include <QFrame>\n\nclass QColorDialog;\n\nnamespace CSVWidget\n{\n    class ColorPickerPopup : public QFrame\n    {\n        Q_OBJECT\n\n        QColorDialog *mColorPicker;\n\n    public:\n        explicit ColorPickerPopup(QWidget *parent);\n    \n        void showPicker(const QPoint &position, const QColor &initialColor);\n\n    protected:\n        void mousePressEvent(QMouseEvent *event) override;\n        bool eventFilter(QObject *object, QEvent *event) override;\n\n    signals:\n        void colorChanged(const QColor &color);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/completerpopup.cpp",
    "content": "#include \"completerpopup.hpp\"\n\nCSVWidget::CompleterPopup::CompleterPopup(QWidget *parent)\n    : QListView(parent)\n{\n    setEditTriggers(QAbstractItemView::NoEditTriggers);\n    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n    setSelectionBehavior(QAbstractItemView::SelectRows);\n    setSelectionMode(QAbstractItemView::SingleSelection);\n}\n\nint CSVWidget::CompleterPopup::sizeHintForRow(int row) const\n{\n    if (model() == nullptr)\n    {\n        return -1;\n    }\n    if (row < 0 || row >= model()->rowCount())\n    {\n        return -1;\n    }\n\n    ensurePolished();\n    QModelIndex index = model()->index(row, modelColumn());\n    QStyleOptionViewItem option = viewOptions();\n    QAbstractItemDelegate *delegate = itemDelegate(index);\n    return delegate->sizeHint(option, index).height();\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/completerpopup.hpp",
    "content": "#ifndef CSV_WIDGET_COMPLETERPOPUP_HPP\n#define CSV_WIDGET_COMPLETERPOPUP_HPP\n\n#include <QListView>\n\nnamespace CSVWidget\n{\n    class CompleterPopup : public QListView\n    {\n        public:\n            CompleterPopup(QWidget *parent = nullptr);\n\n            int sizeHintForRow(int row) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/droplineedit.cpp",
    "content": "#include \"droplineedit.hpp\"\n\n#include <QDropEvent>\n\n#include \"../../model/world/tablemimedata.hpp\"\n#include \"../../model/world/universalid.hpp\"\n\n#include \"../world/dragdroputils.hpp\"\n\nCSVWidget::DropLineEdit::DropLineEdit(CSMWorld::ColumnBase::Display type, QWidget *parent)\n    : QLineEdit(parent),\n      mDropType(type)\n{\n    setAcceptDrops(true);\n}\n\nvoid CSVWidget::DropLineEdit::dragEnterEvent(QDragEnterEvent *event)\n{\n    if (CSVWorld::DragDropUtils::canAcceptData(*event, mDropType))\n    {\n        event->acceptProposedAction();\n    }\n}\n\nvoid CSVWidget::DropLineEdit::dragMoveEvent(QDragMoveEvent *event)\n{\n    if (CSVWorld::DragDropUtils::canAcceptData(*event, mDropType))\n    {\n        event->accept();\n    }\n}\n\nvoid CSVWidget::DropLineEdit::dropEvent(QDropEvent *event)\n{\n    if (CSVWorld::DragDropUtils::canAcceptData(*event, mDropType))\n    {\n        CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, mDropType);\n        setText(QString::fromUtf8(id.getId().c_str()));\n        emit tableMimeDataDropped(id, CSVWorld::DragDropUtils::getTableMimeData(*event)->getDocumentPtr());\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/droplineedit.hpp",
    "content": "#ifndef CSV_WIDGET_DROPLINEEDIT_HPP\n#define CSV_WIDGET_DROPLINEEDIT_HPP\n\n#include <QLineEdit>\n\n#include \"../../model/world/columnbase.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class TableMimeData;\n    class UniversalId;\n}\n\nnamespace CSVWidget\n{\n    class DropLineEdit : public QLineEdit\n    {\n            Q_OBJECT\n\n            CSMWorld::ColumnBase::Display mDropType;\n            ///< The accepted Display type for this LineEdit.\n\n        public:\n            DropLineEdit(CSMWorld::ColumnBase::Display type, QWidget *parent = nullptr);\n\n        protected:\n            void dragEnterEvent(QDragEnterEvent *event) override;\n            void dragMoveEvent(QDragMoveEvent *event) override;\n            void dropEvent(QDropEvent *event) override;\n\n        signals:\n            void tableMimeDataDropped(const CSMWorld::UniversalId &id, const CSMDoc::Document *document);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/modebutton.cpp",
    "content": "#include \"modebutton.hpp\"\n\nCSVWidget::ModeButton::ModeButton (const QIcon& icon, const QString& tooltip, QWidget *parent)\n: PushButton (icon, Type_Mode, tooltip, parent)\n{}\n\nvoid CSVWidget::ModeButton::activate (SceneToolbar *toolbar) {}\n\nvoid CSVWidget::ModeButton::deactivate (SceneToolbar *toolbar) {}\n\nbool CSVWidget::ModeButton::createContextMenu (QMenu *menu)\n{\n    return false;\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/modebutton.hpp",
    "content": "#ifndef CSV_WIDGET_MODEBUTTON_H\n#define CSV_WIDGET_MODEBUTTON_H\n\n#include \"pushbutton.hpp\"\n\nclass QMenu;\n\nnamespace CSVWidget\n{\n    class SceneToolbar;\n\n    /// \\brief Specialist PushButton of Type_Mode for use in SceneToolMode\n    class ModeButton : public PushButton\n    {\n            Q_OBJECT\n\n        public:\n\n            ModeButton (const QIcon& icon, const QString& tooltip = \"\",\n                QWidget *parent = nullptr);\n\n            /// Default-Implementation: do nothing\n            virtual void activate (SceneToolbar *toolbar);\n\n            /// Default-Implementation: do nothing\n            virtual void deactivate (SceneToolbar *toolbar);\n\n            /// Add context menu items to \\a menu. Default-implementation: return false\n            ///\n            /// \\attention menu can be a 0-pointer\n            ///\n            /// \\return Have there been any menu items to be added (if menu is 0 and there\n            /// items to be added, the function must return true anyway.\n            virtual bool createContextMenu (QMenu *menu);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/pushbutton.cpp",
    "content": "#include \"pushbutton.hpp\"\n\n#include <QMouseEvent>\n#include <QKeyEvent>\n\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/prefs/shortcutmanager.hpp\"\n\nvoid CSVWidget::PushButton::processShortcuts()\n{\n    mProcessedToolTip = CSMPrefs::State::get().getShortcutManager().processToolTip(mToolTip);\n}\n\nvoid CSVWidget::PushButton::setExtendedToolTip()\n{\n    QString tooltip = mProcessedToolTip;\n\n    if (tooltip.isEmpty())\n        tooltip = \"(Tool tip not implemented yet)\";\n\n    switch (mType)\n    {\n        case Type_TopMode:\n\n            tooltip +=\n                \"<p>(left click to change mode)\";\n\n            break;\n\n        case Type_TopAction:\n\n            break;\n\n        case Type_Mode:\n\n            tooltip +=\n                \"<p>(left click to activate,\"\n                \"<br>shift-left click to activate and keep panel open)\";\n\n            break;\n\n        case Type_Toggle:\n\n            tooltip += \"<p>(left click to \";\n            tooltip += isChecked() ? \"disable\" : \"enable\";\n            tooltip += \"<p>shift-left click to \";\n            tooltip += isChecked() ? \"disable\" : \"enable\";\n            tooltip += \" and keep panel open)\";\n\n            break;\n    }\n\n    setToolTip (tooltip);\n}\n\nvoid CSVWidget::PushButton::keyPressEvent (QKeyEvent *event)\n{\n    if (event->key()!=Qt::Key_Shift)\n        mKeepOpen = false;\n\n    QPushButton::keyPressEvent (event);\n}\n\nvoid CSVWidget::PushButton::keyReleaseEvent (QKeyEvent *event)\n{\n    if (event->key()==Qt::Key_Space)\n        mKeepOpen = event->modifiers() & Qt::ShiftModifier;\n\n    QPushButton::keyReleaseEvent (event);\n}\n\nvoid CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event)\n{\n    mKeepOpen = event->button()==Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier);\n    QPushButton::mouseReleaseEvent (event);\n}\n\nCSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const QString& tooltip,\n    QWidget *parent)\n: QPushButton (icon, \"\", parent), mKeepOpen (false), mType (type), mToolTip (tooltip)\n{\n    if (type==Type_Mode || type==Type_Toggle)\n    {\n        setCheckable (true);\n        connect (this, SIGNAL (toggled (bool)), this, SLOT (checkedStateChanged (bool)));\n    }\n    setCheckable (type==Type_Mode || type==Type_Toggle);\n    processShortcuts();\n    setExtendedToolTip();\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n}\n\nCSVWidget::PushButton::PushButton (Type type, const QString& tooltip, QWidget *parent)\n: QPushButton (parent), mKeepOpen (false), mType (type), mToolTip (tooltip)\n{\n    setCheckable (type==Type_Mode || type==Type_Toggle);\n    processShortcuts();\n    setExtendedToolTip();\n}\n\nbool CSVWidget::PushButton::hasKeepOpen() const\n{\n    return mKeepOpen;\n}\n\nQString CSVWidget::PushButton::getBaseToolTip() const\n{\n    return mProcessedToolTip;\n}\n\nCSVWidget::PushButton::Type CSVWidget::PushButton::getType() const\n{\n    return mType;\n}\n\nvoid CSVWidget::PushButton::checkedStateChanged (bool checked)\n{\n    setExtendedToolTip();\n}\n\nvoid CSVWidget::PushButton::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (setting->getParent()->getKey() == \"Key Bindings\")\n    {\n        processShortcuts();\n        setExtendedToolTip();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/pushbutton.hpp",
    "content": "#ifndef CSV_WIDGET_PUSHBUTTON_H\n#define CSV_WIDGET_PUSHBUTTON_H\n\n#include <QPushButton>\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWidget\n{\n    class PushButton : public QPushButton\n    {\n            Q_OBJECT\n\n        public:\n\n            enum Type\n            {\n                Type_TopMode, // top level button for mode selector panel\n                Type_TopAction, // top level button that triggers an action\n                Type_Mode, // mode button\n                Type_Toggle\n            };\n\n        private:\n\n            bool mKeepOpen;\n            Type mType;\n            QString mToolTip;\n            QString mProcessedToolTip;\n\n        private:\n\n            void processShortcuts();\n            void setExtendedToolTip();\n\n        protected:\n\n            void keyPressEvent (QKeyEvent *event) override;\n\n            void keyReleaseEvent (QKeyEvent *event) override;\n\n            void mouseReleaseEvent (QMouseEvent *event) override;\n\n        public:\n\n            /// \\param push Do not maintain a toggle state\n            PushButton (const QIcon& icon, Type type, const QString& tooltip = \"\",\n                QWidget *parent = nullptr);\n\n            /// \\param push Do not maintain a toggle state\n            PushButton (Type type, const QString& tooltip = \"\",\n                QWidget *parent = nullptr);\n\n            bool hasKeepOpen() const;\n\n            /// Return tooltip used at construction (without any button-specific modifications)\n            QString getBaseToolTip() const;\n\n            Type getType() const;\n\n        private slots:\n\n            void checkedStateChanged (bool checked);\n            void settingChanged (const CSMPrefs::Setting *setting);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetool.cpp",
    "content": "#include \"scenetool.hpp\"\n\n#include <QMouseEvent>\n\n#include \"scenetoolbar.hpp\"\n\nCSVWidget::SceneTool::SceneTool (SceneToolbar *parent, Type type)\n: PushButton (type, \"\", parent)\n{\n    setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));\n    setIconSize (QSize (parent->getIconSize(), parent->getIconSize()));\n    setFixedSize (parent->getButtonSize(), parent->getButtonSize());\n\n    connect (this, SIGNAL (clicked()), this, SLOT (openRequest()));\n}\n\nvoid CSVWidget::SceneTool::activate() {}\n\nvoid CSVWidget::SceneTool::mouseReleaseEvent (QMouseEvent *event)\n{\n    if (getType()==Type_TopAction && event->button()==Qt::RightButton)\n        showPanel (parentWidget()->mapToGlobal (pos()));\n    else\n        PushButton::mouseReleaseEvent (event);\n}\n\nvoid CSVWidget::SceneTool::openRequest()\n{\n    if (getType()==Type_TopAction)\n        activate();\n    else\n        showPanel (parentWidget()->mapToGlobal (pos()));\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetool.hpp",
    "content": "#ifndef CSV_WIDGET_SCENETOOL_H\n#define CSV_WIDGET_SCENETOOL_H\n\n#include \"pushbutton.hpp\"\n\nnamespace CSVWidget\n{\n    class SceneToolbar;\n\n    ///< \\brief Tool base class\n    class SceneTool : public PushButton\n    {\n            Q_OBJECT\n\n        public:\n\n            SceneTool (SceneToolbar *parent, Type type = Type_TopMode);\n\n            virtual void showPanel (const QPoint& position) = 0;\n\n            /// This function will only called for buttons of type Type_TopAction. The default\n            /// implementation is empty.\n            virtual void activate();\n\n        protected:\n\n            void mouseReleaseEvent (QMouseEvent *event) override;\n\n        private slots:\n\n            void openRequest();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetoolbar.cpp",
    "content": "#include \"scenetoolbar.hpp\"\n\n#include <QVBoxLayout>\n\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"scenetool.hpp\"\n\nvoid CSVWidget::SceneToolbar::focusInEvent (QFocusEvent *event)\n{\n    QWidget::focusInEvent (event);\n\n    if (mLayout->count())\n        dynamic_cast<QWidgetItem&> (*mLayout->itemAt (0)).widget()->setFocus();\n}\n\nCSVWidget::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent)\n: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6)\n{\n    setFixedWidth (mButtonSize);\n\n    mLayout = new QVBoxLayout (this);\n    mLayout->setAlignment (Qt::AlignTop);\n\n    mLayout->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    setLayout (mLayout);\n\n    CSMPrefs::Shortcut* focusSceneShortcut = new CSMPrefs::Shortcut(\"scene-focus-toolbar\", this);\n    connect(focusSceneShortcut, SIGNAL(activated()), this, SIGNAL(focusSceneRequest()));\n}\n\nvoid CSVWidget::SceneToolbar::addTool (SceneTool *tool, SceneTool *insertPoint)\n{\n    if (!insertPoint)\n        mLayout->addWidget (tool, 0, Qt::AlignTop);\n    else\n    {\n        int index = mLayout->indexOf (insertPoint);\n        mLayout->insertWidget (index+1, tool, 0, Qt::AlignTop);\n    }\n}\n\nvoid CSVWidget::SceneToolbar::removeTool (SceneTool *tool)\n{\n    mLayout->removeWidget (tool);\n}\n\nint CSVWidget::SceneToolbar::getButtonSize() const\n{\n    return mButtonSize;\n}\n\nint CSVWidget::SceneToolbar::getIconSize() const\n{\n    return mIconSize;\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetoolbar.hpp",
    "content": "#ifndef CSV_WIDGET_SCENETOOLBAR_H\n#define CSV_WIDGET_SCENETOOLBAR_H\n\n#include <QWidget>\n\nclass QVBoxLayout;\n\nnamespace CSVWidget\n{\n    class SceneTool;\n\n    class SceneToolbar : public QWidget\n    {\n            Q_OBJECT\n\n            QVBoxLayout *mLayout;\n            int mButtonSize;\n            int mIconSize;\n\n        protected:\n\n            void focusInEvent (QFocusEvent *event) override;\n\n        public:\n\n            SceneToolbar (int buttonSize, QWidget *parent = nullptr);\n\n            /// If insertPoint==0, insert \\a tool at the end of the scrollbar. Otherwise\n            /// insert tool after \\a insertPoint.\n            void addTool (SceneTool *tool, SceneTool *insertPoint = nullptr);\n\n            void removeTool (SceneTool *tool);\n\n            int getButtonSize() const;\n\n            int getIconSize() const;\n\n        signals:\n\n            void focusSceneRequest();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetoolmode.cpp",
    "content": "#include \"scenetoolmode.hpp\"\n\n#include <QHBoxLayout>\n#include <QFrame>\n#include <QSignalMapper>\n#include <QMenu>\n#include <QContextMenuEvent>\n#include <QEvent>\n\n#include \"scenetoolbar.hpp\"\n#include \"modebutton.hpp\"\n\nvoid CSVWidget::SceneToolMode::contextMenuEvent (QContextMenuEvent *event)\n{\n    QMenu menu (this);\n    if (createContextMenu (&menu))\n        menu.exec (event->globalPos());\n}\n\nbool CSVWidget::SceneToolMode::createContextMenu (QMenu *menu)\n{\n    if (mCurrent)\n        return mCurrent->createContextMenu (menu);\n\n    return false;\n}\n\nvoid CSVWidget::SceneToolMode::adjustToolTip (const ModeButton *activeMode)\n{\n    QString toolTip = mToolTip;\n\n    toolTip += \"<p>Currently selected: \" + activeMode->getBaseToolTip();\n\n    toolTip += \"<p>(left click to change mode)\";\n\n    if (createContextMenu (nullptr))\n        toolTip += \"<br>(right click to access context menu)\";\n\n    setToolTip (toolTip);\n}\n\nvoid CSVWidget::SceneToolMode::setButton (std::map<ModeButton *, std::string>::iterator iter)\n{\n    for (std::map<ModeButton *, std::string>::const_iterator iter2 = mButtons.begin();\n        iter2!=mButtons.end(); ++iter2)\n        iter2->first->setChecked (iter2==iter);\n\n    setIcon (iter->first->icon());\n    adjustToolTip (iter->first);\n\n    if (mCurrent!=iter->first)\n    {\n        if (mCurrent)\n            mCurrent->deactivate (mToolbar);\n\n        mCurrent = iter->first;\n        mCurrent->activate (mToolbar);\n    }\n\n    emit modeChanged (iter->second);\n}\n\nCSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent, const QString& toolTip)\n: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()),\n  mToolTip (toolTip), mFirst (nullptr), mCurrent (nullptr), mToolbar (parent)\n{\n    mPanel = new QFrame (this, Qt::Popup);\n\n    mLayout = new QHBoxLayout (mPanel);\n\n    mLayout->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    mPanel->setLayout (mLayout);\n}\n\nvoid CSVWidget::SceneToolMode::showPanel (const QPoint& position)\n{\n    mPanel->move (position);\n    mPanel->show();\n\n    if (mFirst)\n        mFirst->setFocus (Qt::OtherFocusReason);\n}\n\nvoid CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id,\n    const QString& tooltip)\n{\n    ModeButton *button = new ModeButton (QIcon (QPixmap (icon.c_str())), tooltip, mPanel);\n    addButton (button, id);\n}\n\nvoid CSVWidget::SceneToolMode::addButton (ModeButton *button, const std::string& id)\n{\n    button->setParent (mPanel);\n\n    button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));\n    button->setIconSize (QSize (mIconSize, mIconSize));\n    button->setFixedSize (mButtonSize, mButtonSize);\n\n    mLayout->addWidget (button);\n\n    mButtons.insert (std::make_pair (button, id));\n\n    connect (button, SIGNAL (clicked()), this, SLOT (selected()));\n\n    if (mButtons.size()==1)\n    {\n        mFirst = mCurrent = button;\n        setIcon (button->icon());\n        button->setChecked (true);\n        adjustToolTip (button);\n        mCurrent->activate (mToolbar);\n    }\n}\n\nCSVWidget::ModeButton *CSVWidget::SceneToolMode::getCurrent()\n{\n    return mCurrent;\n}\n\nstd::string CSVWidget::SceneToolMode::getCurrentId() const\n{\n    return mButtons.find (mCurrent)->second;\n}\n\nvoid CSVWidget::SceneToolMode::setButton (const std::string& id)\n{\n    for (std::map<ModeButton *, std::string>::iterator iter = mButtons.begin();\n        iter!=mButtons.end(); ++iter)\n        if (iter->second==id)\n        {\n            setButton (iter);\n            break;\n        }\n}\n\nbool CSVWidget::SceneToolMode::event(QEvent* event)\n{\n    if (event->type() == QEvent::ToolTip)\n    {\n        adjustToolTip(mCurrent);\n    }\n\n    return SceneTool::event(event);\n}\n\nvoid CSVWidget::SceneToolMode::selected()\n{\n    std::map<ModeButton *, std::string>::iterator iter =\n        mButtons.find (dynamic_cast<ModeButton *> (sender()));\n\n    if (iter!=mButtons.end())\n    {\n        if (!iter->first->hasKeepOpen())\n            mPanel->hide();\n\n        setButton (iter);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetoolmode.hpp",
    "content": "#ifndef CSV_WIDGET_SCENETOOL_MODE_H\n#define CSV_WIDGET_SCENETOOL_MODE_H\n\n#include \"scenetool.hpp\"\n\n#include <map>\n\nclass QHBoxLayout;\nclass QMenu;\nclass QEvent;\n\nnamespace CSVWidget\n{\n    class SceneToolbar;\n    class ModeButton;\n\n    ///< \\brief Mode selector tool\n    class SceneToolMode : public SceneTool\n    {\n            Q_OBJECT\n\n            QWidget *mPanel;\n            QHBoxLayout *mLayout;\n            std::map<ModeButton *, std::string> mButtons; // widget, id\n            int mButtonSize;\n            int mIconSize;\n            QString mToolTip;\n            PushButton *mFirst;\n            ModeButton *mCurrent;\n            SceneToolbar *mToolbar;\n\n            void adjustToolTip (const ModeButton *activeMode);\n\n            void contextMenuEvent (QContextMenuEvent *event) override;\n\n            /// Add context menu items to \\a menu. Default-implementation: Pass on request to\n            /// current mode button or return false, if there is no current mode button.\n            ///\n            /// \\attention menu can be a 0-pointer\n            ///\n            /// \\return Have there been any menu items to be added (if menu is 0 and there\n            /// items to be added, the function must return true anyway.\n            virtual bool createContextMenu (QMenu *menu);\n\n            void setButton (std::map<ModeButton *, std::string>::iterator iter);\n\n        protected:\n\n            bool event(QEvent* event) override;\n\n        public:\n\n            SceneToolMode (SceneToolbar *parent, const QString& toolTip);\n\n            void showPanel (const QPoint& position) override;\n\n            void addButton (const std::string& icon, const std::string& id,\n                const QString& tooltip = \"\");\n\n            /// The ownership of \\a button is transferred to *this.\n            void addButton (ModeButton *button, const std::string& id);\n\n            /// Will return a 0-pointer only if the mode does not have any buttons yet.\n            ModeButton *getCurrent();\n\n            /// Must not be called if there aren't any buttons yet.\n            std::string getCurrentId() const;\n\n            /// Manually change the current mode\n            void setButton (const std::string& id);\n\n        signals:\n\n            void modeChanged (const std::string& id);\n\n        private slots:\n\n            void selected();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetoolrun.cpp",
    "content": "#include \"scenetoolrun.hpp\"\n\n#include <iterator>\n\n#include <QFrame>\n#include <QTableWidget>\n#include <QHBoxLayout>\n#include <QHeaderView>\n#include <QApplication>\n\nvoid CSVWidget::SceneToolRun::adjustToolTips()\n{\n    QString toolTip = mToolTip;\n\n    if (mSelected==mProfiles.end())\n        toolTip += \"<p>No debug profile selected (function disabled)\";\n    else\n    {\n        toolTip += \"<p>Debug profile: \" + QString::fromUtf8 (mSelected->c_str());\n        toolTip += \"<p>(right click to switch to a different profile)\";\n    }\n\n    setToolTip (toolTip);\n}\n\nvoid CSVWidget::SceneToolRun::updateIcon()\n{\n    setDisabled (mSelected==mProfiles.end());\n}\n\nvoid CSVWidget::SceneToolRun::updatePanel()\n{\n    mTable->setRowCount (static_cast<int>(mProfiles.size()));\n\n    int i = 0;\n\n    for (std::set<std::string>::const_iterator iter (mProfiles.begin()); iter!=mProfiles.end();\n        ++iter, ++i)\n    {\n        mTable->setItem (i, 0, new QTableWidgetItem (QString::fromUtf8 (iter->c_str())));\n\n        mTable->setItem (i, 1, new QTableWidgetItem (\n            QApplication::style()->standardIcon (QStyle::SP_TitleBarCloseButton), \"\"));\n    }\n}\n\nCSVWidget::SceneToolRun::SceneToolRun (SceneToolbar *parent, const QString& toolTip,\n    const QString& icon, const std::vector<std::string>& profiles)\n: SceneTool (parent, Type_TopAction), mProfiles (profiles.begin(), profiles.end()),\n  mSelected (mProfiles.begin()), mToolTip (toolTip)\n{\n    setIcon (QIcon (icon));\n    updateIcon();\n    adjustToolTips();\n\n    mPanel = new QFrame (this, Qt::Popup);\n\n    QHBoxLayout *layout = new QHBoxLayout (mPanel);\n\n    layout->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    mTable = new QTableWidget (0, 2, this);\n\n    mTable->setShowGrid (false);\n    mTable->verticalHeader()->hide();\n    mTable->horizontalHeader()->hide();\n    mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);\n    mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents);\n    mTable->setSelectionMode (QAbstractItemView::NoSelection);\n\n    layout->addWidget (mTable);\n\n    connect (mTable, SIGNAL (clicked (const QModelIndex&)),\n        this, SLOT (clicked (const QModelIndex&)));\n}\n\nvoid CSVWidget::SceneToolRun::showPanel (const QPoint& position)\n{\n    updatePanel();\n\n    mPanel->move (position);\n    mPanel->show();\n}\n\nvoid CSVWidget::SceneToolRun::activate()\n{\n    if (mSelected!=mProfiles.end())\n        emit runRequest (*mSelected);\n}\n\nvoid CSVWidget::SceneToolRun::removeProfile (const std::string& profile)\n{\n    std::set<std::string>::iterator iter = mProfiles.find (profile);\n\n    if (iter!=mProfiles.end())\n    {\n        if (iter==mSelected)\n        {\n            if (iter!=mProfiles.begin())\n                --mSelected;\n            else\n                ++mSelected;\n        }\n\n        mProfiles.erase (iter);\n\n        if (mSelected==mProfiles.end())\n            updateIcon();\n\n        adjustToolTips();\n    }\n}\n\nvoid CSVWidget::SceneToolRun::addProfile (const std::string& profile)\n{\n    std::set<std::string>::iterator iter = mProfiles.find (profile);\n\n    if (iter==mProfiles.end())\n    {\n        mProfiles.insert (profile);\n\n        if (mSelected==mProfiles.end())\n        {\n            mSelected = mProfiles.begin();\n            updateIcon();\n        }\n\n        adjustToolTips();\n    }\n}\n\nvoid CSVWidget::SceneToolRun::clicked (const QModelIndex& index)\n{\n    if (index.column()==0)\n    {\n        // select profile\n        mSelected = mProfiles.begin();\n        std::advance (mSelected, index.row());\n        mPanel->hide();\n        adjustToolTips();\n    }\n    else if (index.column()==1)\n    {\n        // remove profile from list\n        std::set<std::string>::iterator iter = mProfiles.begin();\n        std::advance (iter, index.row());\n        removeProfile (*iter);\n        updatePanel();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetoolrun.hpp",
    "content": "#ifndef CSV_WIDGET_SCENETOOLRUN_H\n#define CSV_WIDGET_SCENETOOLRUN_H\n\n#include <set>\n#include <string>\n\n#include \"scenetool.hpp\"\n\nclass QFrame;\nclass QTableWidget;\nclass QModelIndex;\n\nnamespace CSVWidget\n{\n    class SceneToolRun : public SceneTool\n    {\n            Q_OBJECT\n\n            std::set<std::string> mProfiles;\n            std::set<std::string>::iterator mSelected;\n            QString mToolTip;\n            QFrame *mPanel;\n            QTableWidget *mTable;\n\n        private:\n\n            void adjustToolTips();\n\n            void updateIcon();\n\n            void updatePanel();\n\n        public:\n\n            SceneToolRun (SceneToolbar *parent, const QString& toolTip, const QString& icon,\n                const std::vector<std::string>& profiles);\n\n            void showPanel (const QPoint& position) override;\n\n            void activate() override;\n\n            /// \\attention This function does not remove the profile from the profile selection\n            /// panel.\n            void removeProfile (const std::string& profile);\n\n            /// \\attention This function doe not add the profile to the profile selection\n            /// panel. This only happens when the panel is re-opened.\n            ///\n            /// \\note Adding profiles that are already listed is a no-op.\n            void addProfile (const std::string& profile);\n\n        private slots:\n\n            void clicked (const QModelIndex& index);\n\n        signals:\n\n            void runRequest (const std::string& profile);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetoolshapebrush.cpp",
    "content": "#include \"scenetoolshapebrush.hpp\"\n\n#include <QFrame>\n#include <QIcon>\n#include <QTableWidget>\n#include <QHBoxLayout>\n\n#include <QWidget>\n#include <QComboBox>\n#include <QSpinBox>\n#include <QGroupBox>\n#include <QSlider>\n#include <QEvent>\n#include <QDropEvent>\n#include <QButtonGroup>\n#include <QVBoxLayout>\n#include <QDragEnterEvent>\n#include <QDrag>\n#include <QTableWidget>\n#include <QHeaderView>\n#include <QApplication>\n#include <QSizePolicy>\n\n#include \"brushshapes.hpp\"\n#include \"scenetool.hpp\"\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/world/commands.hpp\"\n\n\nCSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, QWidget *parent)\n    : QGroupBox(title, parent)\n{\n    mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides);\n    mBrushSizeSlider->setTickInterval(10);\n    mBrushSizeSlider->setRange(1, CSMPrefs::get()[\"3D Scene Editing\"][\"shapebrush-maximumsize\"].toInt());\n    mBrushSizeSlider->setSingleStep(1);\n\n    mBrushSizeSpinBox->setRange(1, CSMPrefs::get()[\"3D Scene Editing\"][\"shapebrush-maximumsize\"].toInt());\n    mBrushSizeSpinBox->setSingleStep(1);\n\n    QHBoxLayout *layoutSliderSize = new QHBoxLayout;\n    layoutSliderSize->addWidget(mBrushSizeSlider);\n    layoutSliderSize->addWidget(mBrushSizeSpinBox);\n\n    connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int)));\n    connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int)));\n\n    setLayout(layoutSliderSize);\n}\n\nCSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent)\n    : QFrame(parent, Qt::Popup),\n    mDocument(document)\n{\n    mButtonPoint = new QPushButton(QIcon (QPixmap (\":scenetoolbar/brush-point\")), \"\", this);\n    mButtonSquare = new QPushButton(QIcon (QPixmap (\":scenetoolbar/brush-square\")), \"\", this);\n    mButtonCircle = new QPushButton(QIcon (QPixmap (\":scenetoolbar/brush-circle\")), \"\", this);\n    mButtonCustom = new QPushButton(QIcon (QPixmap (\":scenetoolbar/brush-custom\")), \"\", this);\n\n    mSizeSliders = new ShapeBrushSizeControls(\"Brush size\", this);\n\n    QVBoxLayout *layoutMain = new QVBoxLayout;\n    layoutMain->setSpacing(0);\n    layoutMain->setContentsMargins(4,0,4,4);\n\n    QHBoxLayout *layoutHorizontal = new QHBoxLayout;\n    layoutHorizontal->setSpacing(0);\n    layoutHorizontal->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    configureButtonInitialSettings(mButtonPoint);\n    configureButtonInitialSettings(mButtonSquare);\n    configureButtonInitialSettings(mButtonCircle);\n    configureButtonInitialSettings(mButtonCustom);\n\n    mButtonPoint->setToolTip (toolTipPoint);\n    mButtonSquare->setToolTip (toolTipSquare);\n    mButtonCircle->setToolTip (toolTipCircle);\n    mButtonCustom->setToolTip (toolTipCustom);\n\n    QButtonGroup* brushButtonGroup = new QButtonGroup(this);\n    brushButtonGroup->addButton(mButtonPoint);\n    brushButtonGroup->addButton(mButtonSquare);\n    brushButtonGroup->addButton(mButtonCircle);\n    brushButtonGroup->addButton(mButtonCustom);\n\n    brushButtonGroup->setExclusive(true);\n\n    layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop);\n    layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop);\n    layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop);\n    layoutHorizontal->addWidget(mButtonCustom, 0, Qt::AlignTop);\n\n    mHorizontalGroupBox = new QGroupBox(tr(\"\"));\n    mHorizontalGroupBox->setLayout(layoutHorizontal);\n\n    mToolSelector = new QComboBox(this);\n    mToolSelector->addItem(tr(\"Height (drag)\"));\n    mToolSelector->addItem(tr(\"Height, raise (paint)\"));\n    mToolSelector->addItem(tr(\"Height, lower (paint)\"));\n    mToolSelector->addItem(tr(\"Smooth (paint)\"));\n    mToolSelector->addItem(tr(\"Flatten (paint)\"));\n\n    QLabel *brushStrengthLabel = new QLabel(this);\n    brushStrengthLabel->setText(\"Brush strength:\");\n\n    mToolStrengthSlider = new QSlider(Qt::Horizontal);\n    mToolStrengthSlider->setTickPosition(QSlider::TicksBothSides);\n    mToolStrengthSlider->setTickInterval(8);\n    mToolStrengthSlider->setRange(8, 128);\n    mToolStrengthSlider->setSingleStep(8);\n    mToolStrengthSlider->setValue(8);\n\n    layoutMain->addWidget(mHorizontalGroupBox);\n    layoutMain->addWidget(mSizeSliders);\n    layoutMain->addWidget(mToolSelector);\n    layoutMain->addWidget(brushStrengthLabel);\n    layoutMain->addWidget(mToolStrengthSlider);\n\n    setLayout(layoutMain);\n\n    connect(mButtonPoint, SIGNAL(clicked()), this, SLOT(setBrushShape()));\n    connect(mButtonSquare, SIGNAL(clicked()), this, SLOT(setBrushShape()));\n    connect(mButtonCircle, SIGNAL(clicked()), this, SLOT(setBrushShape()));\n    connect(mButtonCustom, SIGNAL(clicked()), this, SLOT(setBrushShape()));\n}\n\nvoid CSVWidget::ShapeBrushWindow::configureButtonInitialSettings(QPushButton *button)\n{\n  button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));\n  button->setContentsMargins (QMargins (0, 0, 0, 0));\n  button->setIconSize (QSize (48-6, 48-6));\n  button->setFixedSize (48, 48);\n  button->setCheckable(true);\n}\n\nvoid CSVWidget::ShapeBrushWindow::setBrushSize(int brushSize)\n{\n    mBrushSize = brushSize;\n    emit passBrushSize(mBrushSize);\n}\n\nvoid CSVWidget::ShapeBrushWindow::setBrushShape()\n{\n    if(mButtonPoint->isChecked()) mBrushShape = BrushShape_Point;\n    if(mButtonSquare->isChecked()) mBrushShape = BrushShape_Square;\n    if(mButtonCircle->isChecked()) mBrushShape = BrushShape_Circle;\n    if(mButtonCustom->isChecked()) mBrushShape = BrushShape_Custom;\n    emit passBrushShape(mBrushShape);\n}\n\nvoid CSVWidget::SceneToolShapeBrush::adjustToolTips()\n{\n}\n\nCSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document)\n: SceneTool (parent, Type_TopAction),\n    mToolTip (toolTip),\n    mDocument (document),\n    mShapeBrushWindow(new ShapeBrushWindow(document, this))\n{\n    setAcceptDrops(true);\n    connect(mShapeBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setButtonIcon(CSVWidget::BrushShape)));\n    setButtonIcon(mShapeBrushWindow->mBrushShape);\n\n    mPanel = new QFrame (this, Qt::Popup);\n\n    QHBoxLayout *layout = new QHBoxLayout (mPanel);\n\n    layout->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    mTable = new QTableWidget (0, 2, this);\n\n    mTable->setShowGrid (true);\n    mTable->verticalHeader()->hide();\n    mTable->horizontalHeader()->hide();\n    mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);\n    mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch);\n    mTable->setSelectionMode (QAbstractItemView::NoSelection);\n\n    layout->addWidget (mTable);\n\n    connect (mTable, SIGNAL (clicked (const QModelIndex&)),\n        this, SLOT (clicked (const QModelIndex&)));\n\n}\n\nvoid CSVWidget::SceneToolShapeBrush::setButtonIcon (CSVWidget::BrushShape brushShape)\n{\n    QString tooltip = \"Change brush settings <p>Currently selected: \";\n\n    switch (brushShape)\n    {\n        case BrushShape_Point:\n\n            setIcon (QIcon (QPixmap (\":scenetoolbar/brush-point\")));\n            tooltip += mShapeBrushWindow->toolTipPoint;\n            break;\n\n        case BrushShape_Square:\n\n            setIcon (QIcon (QPixmap (\":scenetoolbar/brush-square\")));\n            tooltip += mShapeBrushWindow->toolTipSquare;\n            break;\n\n        case BrushShape_Circle:\n\n            setIcon (QIcon (QPixmap (\":scenetoolbar/brush-circle\")));\n            tooltip += mShapeBrushWindow->toolTipCircle;\n            break;\n\n        case BrushShape_Custom:\n\n            setIcon (QIcon (QPixmap (\":scenetoolbar/brush-custom\")));\n            tooltip += mShapeBrushWindow->toolTipCustom;\n            break;\n    }\n\n    setToolTip (tooltip);\n}\n\nvoid CSVWidget::SceneToolShapeBrush::showPanel (const QPoint& position)\n{\n}\n\nvoid CSVWidget::SceneToolShapeBrush::updatePanel ()\n{\n}\n\nvoid CSVWidget::SceneToolShapeBrush::clicked (const QModelIndex& index)\n{\n}\n\nvoid CSVWidget::SceneToolShapeBrush::activate ()\n{\n    QPoint position = QCursor::pos();\n    mShapeBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()[\"3D Scene Editing\"][\"shapebrush-maximumsize\"].toInt());\n    mShapeBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()[\"3D Scene Editing\"][\"shapebrush-maximumsize\"].toInt());\n    mShapeBrushWindow->move (position);\n    mShapeBrushWindow->show();\n}\n\nvoid CSVWidget::SceneToolShapeBrush::dragEnterEvent (QDragEnterEvent *event)\n{\n    emit passEvent(event);\n    event->accept();\n}\nvoid CSVWidget::SceneToolShapeBrush::dropEvent (QDropEvent *event)\n{\n    emit passEvent(event);\n    event->accept();\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetoolshapebrush.hpp",
    "content": "#ifndef CSV_WIDGET_SCENETOOLSHAPEBRUSH_H\n#define CSV_WIDGET_SCENETOOLSHAPEBRUSH_H\n\n#include <QIcon>\n#include <QFrame>\n#include <QModelIndex>\n\n#include <QWidget>\n#include <QLabel>\n#include <QComboBox>\n#include <QSpinBox>\n#include <QGroupBox>\n#include <QSlider>\n#include <QEvent>\n#include <QHBoxLayout>\n#include <QPushButton>\n\n#ifndef Q_MOC_RUN\n#include \"brushshapes.hpp\"\n#include \"scenetool.hpp\"\n\n#include \"../../model/doc/document.hpp\"\n#endif\n\nclass QTableWidget;\n\nnamespace CSVRender\n{\n    class TerrainShapeMode;\n}\n\nnamespace CSVWidget\n{\n    /// \\brief Layout-box for some brush button settings\n    class ShapeBrushSizeControls : public QGroupBox\n    {\n        Q_OBJECT\n\n        public:\n            ShapeBrushSizeControls(const QString &title, QWidget *parent);\n\n        private:\n            QSlider *mBrushSizeSlider = new QSlider(Qt::Horizontal);\n            QSpinBox *mBrushSizeSpinBox = new QSpinBox;\n\n        friend class SceneToolShapeBrush;\n        friend class CSVRender::TerrainShapeMode;\n    };\n\n    /// \\brief Brush settings window\n    class ShapeBrushWindow : public QFrame\n    {\n        Q_OBJECT\n\n        public:\n\n            ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent = nullptr);\n            void configureButtonInitialSettings(QPushButton *button);\n\n            const QString toolTipPoint = \"Paint single point\";\n            const QString toolTipSquare = \"Paint with square brush\";\n            const QString toolTipCircle = \"Paint with circle brush\";\n            const QString toolTipCustom = \"Paint with custom brush, defined by terrain selection\";\n\n        private:\n            CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point;\n            int mBrushSize = 1;\n            CSMDoc::Document& mDocument;\n            QGroupBox *mHorizontalGroupBox;\n            QComboBox *mToolSelector;\n            QSlider *mToolStrengthSlider;\n            QPushButton *mButtonPoint;\n            QPushButton *mButtonSquare;\n            QPushButton *mButtonCircle;\n            QPushButton *mButtonCustom;\n            ShapeBrushSizeControls* mSizeSliders;\n\n        friend class SceneToolShapeBrush;\n        friend class CSVRender::TerrainShapeMode;\n\n        public slots:\n            void setBrushShape();\n            void setBrushSize(int brushSize);\n\n        signals:\n            void passBrushSize (int brushSize);\n            void passBrushShape(CSVWidget::BrushShape brushShape);\n    };\n\n    class SceneToolShapeBrush : public SceneTool\n    {\n            Q_OBJECT\n\n            QString mToolTip;\n            CSMDoc::Document& mDocument;\n            QFrame *mPanel;\n            QTableWidget *mTable;\n            ShapeBrushWindow *mShapeBrushWindow;\n\n        private:\n\n            void adjustToolTips();\n\n        public:\n\n            SceneToolShapeBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document);\n\n            void showPanel (const QPoint& position) override;\n            void updatePanel ();\n\n            void dropEvent (QDropEvent *event) override;\n            void dragEnterEvent (QDragEnterEvent *event) override;\n\n        friend class CSVRender::TerrainShapeMode;\n\n        public slots:\n            void setButtonIcon(CSVWidget::BrushShape brushShape);\n            void clicked (const QModelIndex& index);\n            void activate() override;\n\n        signals:\n            void passEvent(QDropEvent *event);\n            void passEvent(QDragEnterEvent *event);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetooltexturebrush.cpp",
    "content": "#include \"scenetooltexturebrush.hpp\"\n\n#include <QFrame>\n#include <QIcon>\n#include <QTableWidget>\n#include <QHBoxLayout>\n\n#include <QWidget>\n#include <QSpinBox>\n#include <QGroupBox>\n#include <QSlider>\n#include <QEvent>\n#include <QDropEvent>\n#include <QButtonGroup>\n#include <QVBoxLayout>\n#include <QDragEnterEvent>\n#include <QDrag>\n#include <QTableWidget>\n#include <QHeaderView>\n#include <QApplication>\n#include <QSizePolicy>\n\n#include \"scenetool.hpp\"\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idcollection.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/landtexture.hpp\"\n#include \"../../model/world/universalid.hpp\"\n\n\nCSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *parent)\n    : QGroupBox(title, parent),\n    mLayoutSliderSize(new QHBoxLayout),\n    mBrushSizeSlider(new QSlider(Qt::Horizontal)),\n    mBrushSizeSpinBox(new QSpinBox)\n{\n    mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides);\n    mBrushSizeSlider->setTickInterval(10);\n    mBrushSizeSlider->setRange(1, CSMPrefs::get()[\"3D Scene Editing\"][\"texturebrush-maximumsize\"].toInt());\n    mBrushSizeSlider->setSingleStep(1);\n\n    mBrushSizeSpinBox->setRange(1, CSMPrefs::get()[\"3D Scene Editing\"][\"texturebrush-maximumsize\"].toInt());\n    mBrushSizeSpinBox->setSingleStep(1);\n\n    mLayoutSliderSize->addWidget(mBrushSizeSlider);\n    mLayoutSliderSize->addWidget(mBrushSizeSpinBox);\n\n    connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int)));\n    connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int)));\n\n    setLayout(mLayoutSliderSize);\n}\n\nCSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent)\n    : QFrame(parent, Qt::Popup),\n    mDocument(document)\n{\n    mBrushTextureLabel = \"Selected texture: \" + mBrushTexture + \" \";\n\n    CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();\n\n    int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);\n    int index = landtexturesCollection.searchId(mBrushTexture);\n\n    if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())\n    {\n        mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());\n    } else\n    {\n        mBrushTextureLabel = \"No selected texture or invalid texture\";\n        mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel));\n    }\n\n    mButtonPoint = new QPushButton(QIcon (QPixmap (\":scenetoolbar/brush-point\")), \"\", this);\n    mButtonSquare = new QPushButton(QIcon (QPixmap (\":scenetoolbar/brush-square\")), \"\", this);\n    mButtonCircle = new QPushButton(QIcon (QPixmap (\":scenetoolbar/brush-circle\")), \"\", this);\n    mButtonCustom = new QPushButton(QIcon (QPixmap (\":scenetoolbar/brush-custom\")), \"\", this);\n\n    mSizeSliders = new BrushSizeControls(\"Brush size\", this);\n\n    QVBoxLayout *layoutMain = new QVBoxLayout;\n    layoutMain->setSpacing(0);\n    layoutMain->setContentsMargins(4,0,4,4);\n\n    QHBoxLayout *layoutHorizontal = new QHBoxLayout;\n    layoutHorizontal->setSpacing(0);\n    layoutHorizontal->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    configureButtonInitialSettings(mButtonPoint);\n    configureButtonInitialSettings(mButtonSquare);\n    configureButtonInitialSettings(mButtonCircle);\n    configureButtonInitialSettings(mButtonCustom);\n\n    mButtonPoint->setToolTip (toolTipPoint);\n    mButtonSquare->setToolTip (toolTipSquare);\n    mButtonCircle->setToolTip (toolTipCircle);\n    mButtonCustom->setToolTip (toolTipCustom);\n\n    QButtonGroup* brushButtonGroup = new QButtonGroup(this);\n    brushButtonGroup->addButton(mButtonPoint);\n    brushButtonGroup->addButton(mButtonSquare);\n    brushButtonGroup->addButton(mButtonCircle);\n    brushButtonGroup->addButton(mButtonCustom);\n\n    brushButtonGroup->setExclusive(true);\n\n    layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop);\n    layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop);\n    layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop);\n    layoutHorizontal->addWidget(mButtonCustom, 0, Qt::AlignTop);\n\n    mHorizontalGroupBox = new QGroupBox(tr(\"\"));\n    mHorizontalGroupBox->setLayout(layoutHorizontal);\n\n    layoutMain->addWidget(mHorizontalGroupBox);\n    layoutMain->addWidget(mSizeSliders);\n    layoutMain->addWidget(mSelectedBrush);\n\n    setLayout(layoutMain);\n\n    connect(mButtonPoint, SIGNAL(clicked()), this, SLOT(setBrushShape()));\n    connect(mButtonSquare, SIGNAL(clicked()), this, SLOT(setBrushShape()));\n    connect(mButtonCircle, SIGNAL(clicked()), this, SLOT(setBrushShape()));\n    connect(mButtonCustom, SIGNAL(clicked()), this, SLOT(setBrushShape()));\n}\n\nvoid CSVWidget::TextureBrushWindow::configureButtonInitialSettings(QPushButton *button)\n{\n  button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));\n  button->setContentsMargins (QMargins (0, 0, 0, 0));\n  button->setIconSize (QSize (48-6, 48-6));\n  button->setFixedSize (48, 48);\n  button->setCheckable(true);\n}\n\nvoid CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)\n{\n    CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (\n        *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));\n    QUndoStack& undoStack = mDocument.getUndoStack();\n\n    CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();\n    int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);\n\n    int index = 0;\n    int pluginInDragged = 0;\n    CSMWorld::LandTexture::parseUniqueRecordId(brushTexture, pluginInDragged, index);\n    std::string newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, index);\n    int rowInBase = landtexturesCollection.searchId(brushTexture);\n    int rowInNew = landtexturesCollection.searchId(newBrushTextureId);\n\n    // Check if texture exists in current plugin, and clone if id found in base, otherwise reindex the texture\n    // TO-DO: Handle case when texture is not found in neither base or plugin properly (finding new index is not enough)\n    // TO-DO: Handle conflicting plugins properly\n    if (rowInNew == -1)\n    {\n        if (rowInBase == -1)\n        {\n            int counter=0;\n            bool freeIndexFound = false;\n            const int maxCounter = std::numeric_limits<uint16_t>::max() - 1;\n            do {\n                newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);\n                if (landtexturesCollection.searchId(brushTexture) != -1 &&\n                    landtexturesCollection.getRecord(brushTexture).isDeleted() == 0 &&\n                    landtexturesCollection.searchId(newBrushTextureId) != -1 &&\n                    landtexturesCollection.getRecord(newBrushTextureId).isDeleted() == 0)\n                        counter = (counter + 1) % maxCounter;\n                else freeIndexFound = true;\n            } while (freeIndexFound == false || counter < maxCounter);\n        }\n\n        undoStack.beginMacro (\"Add land texture record\");\n        undoStack.push (new CSMWorld::CloneCommand (ltexTable, brushTexture, newBrushTextureId, CSMWorld::UniversalId::Type_LandTexture));\n        undoStack.endMacro();\n    }\n\n    if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())\n    {\n        mBrushTextureLabel = \"Selected texture: \" + newBrushTextureId + \" \";\n        mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());\n    } else\n    {\n        newBrushTextureId = \"\";\n        mBrushTextureLabel = \"No selected texture or invalid texture\";\n        mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));\n    }\n\n    mBrushTexture = newBrushTextureId;\n\n    emit passTextureId(mBrushTexture);\n    emit passBrushShape(mBrushShape); // updates the icon tooltip\n}\n\nvoid CSVWidget::TextureBrushWindow::setBrushSize(int brushSize)\n{\n    mBrushSize = brushSize;\n    emit passBrushSize(mBrushSize);\n}\n\nvoid CSVWidget::TextureBrushWindow::setBrushShape()\n{\n    if (mButtonPoint->isChecked())\n        mBrushShape = CSVWidget::BrushShape_Point;\n    if (mButtonSquare->isChecked())\n        mBrushShape = CSVWidget::BrushShape_Square;\n    if (mButtonCircle->isChecked())\n        mBrushShape = CSVWidget::BrushShape_Circle;\n    if (mButtonCustom->isChecked())\n        mBrushShape = CSVWidget::BrushShape_Custom;\n    emit passBrushShape(mBrushShape);\n}\n\nvoid CSVWidget::SceneToolTextureBrush::adjustToolTips()\n{\n}\n\nCSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document)\n: SceneTool (parent, Type_TopAction),\n    mToolTip (toolTip),\n    mDocument (document),\n    mTextureBrushWindow(new TextureBrushWindow(document, this))\n{\n    mBrushHistory.resize(1);\n    mBrushHistory[0] = \"L0#0\";\n\n    setAcceptDrops(true);\n    connect(mTextureBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setButtonIcon(CSVWidget::BrushShape)));\n    setButtonIcon(mTextureBrushWindow->mBrushShape);\n\n    mPanel = new QFrame (this, Qt::Popup);\n\n    QHBoxLayout *layout = new QHBoxLayout (mPanel);\n\n    layout->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    mTable = new QTableWidget (0, 2, this);\n\n    mTable->setShowGrid (true);\n    mTable->verticalHeader()->hide();\n    mTable->horizontalHeader()->hide();\n    mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);\n    mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch);\n    mTable->setSelectionMode (QAbstractItemView::NoSelection);\n\n    layout->addWidget (mTable);\n\n    connect (mTable, SIGNAL (clicked (const QModelIndex&)),\n        this, SLOT (clicked (const QModelIndex&)));\n\n}\n\nvoid CSVWidget::SceneToolTextureBrush::setButtonIcon (CSVWidget::BrushShape brushShape)\n{\n    QString tooltip = \"Change brush settings <p>Currently selected: \";\n\n    switch (brushShape)\n    {\n        case BrushShape_Point:\n\n            setIcon (QIcon (QPixmap (\":scenetoolbar/brush-point\")));\n            tooltip += mTextureBrushWindow->toolTipPoint;\n            break;\n\n        case BrushShape_Square:\n\n            setIcon (QIcon (QPixmap (\":scenetoolbar/brush-square\")));\n            tooltip += mTextureBrushWindow->toolTipSquare;\n            break;\n\n        case BrushShape_Circle:\n\n            setIcon (QIcon (QPixmap (\":scenetoolbar/brush-circle\")));\n            tooltip += mTextureBrushWindow->toolTipCircle;\n            break;\n\n        case BrushShape_Custom:\n\n            setIcon (QIcon (QPixmap (\":scenetoolbar/brush-custom\")));\n            tooltip += mTextureBrushWindow->toolTipCustom;\n            break;\n    }\n\n    tooltip += \"<p>(right click to access of previously used brush settings)\";\n\n\n    CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();\n\n    int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);\n    int index = landtexturesCollection.searchId(mTextureBrushWindow->mBrushTexture);\n\n    if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())\n    {\n        tooltip += \"<p>Selected texture: \" + QString::fromStdString(mTextureBrushWindow->mBrushTexture) + \" \";\n\n        tooltip += landtexturesCollection.getData(index, landTextureFilename).value<QString>();\n    } else\n    {\n        tooltip += \"<p>No selected texture or invalid texture\";\n    }\n\n    tooltip += \"<br>(drop texture here to change)\";\n    setToolTip (tooltip);\n}\n\nvoid CSVWidget::SceneToolTextureBrush::showPanel (const QPoint& position)\n{\n    updatePanel();\n    mPanel->move (position);\n    mPanel->show();\n}\n\nvoid CSVWidget::SceneToolTextureBrush::updatePanel()\n{\n    mTable->setRowCount (mBrushHistory.size());\n\n    for (int i = mBrushHistory.size()-1; i >= 0; --i)\n    {\n        CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();\n        int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);\n        int index = landtexturesCollection.searchId(mBrushHistory[i]);\n\n        if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())\n        {\n            mTable->setItem (i, 1, new QTableWidgetItem (landtexturesCollection.getData(index, landTextureFilename).value<QString>()));\n            mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i])));\n        } else\n        {\n            mTable->setItem (i, 1, new QTableWidgetItem (\"Invalid/deleted texture\"));\n            mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i])));\n        }\n    }\n}\n\nvoid CSVWidget::SceneToolTextureBrush::updateBrushHistory (const std::string& brushTexture)\n{\n    mBrushHistory.insert(mBrushHistory.begin(), brushTexture);\n    if(mBrushHistory.size() > 5) mBrushHistory.pop_back();\n}\n\nvoid CSVWidget::SceneToolTextureBrush::clicked (const QModelIndex& index)\n{\n    if (index.column()==0 || index.column()==1)\n    {\n        std::string brushTexture = mBrushHistory[index.row()];\n        std::swap(mBrushHistory[index.row()], mBrushHistory[0]);\n        mTextureBrushWindow->setBrushTexture(brushTexture);\n        emit passTextureId(brushTexture);\n        updatePanel();\n        mPanel->hide();\n    }\n}\n\nvoid CSVWidget::SceneToolTextureBrush::activate ()\n{\n    QPoint position = QCursor::pos();\n    mTextureBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()[\"3D Scene Editing\"][\"texturebrush-maximumsize\"].toInt());\n    mTextureBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()[\"3D Scene Editing\"][\"texturebrush-maximumsize\"].toInt());\n    mTextureBrushWindow->move (position);\n    mTextureBrushWindow->show();\n}\n\nvoid CSVWidget::SceneToolTextureBrush::dragEnterEvent (QDragEnterEvent *event)\n{\n    emit passEvent(event);\n    event->accept();\n}\nvoid CSVWidget::SceneToolTextureBrush::dropEvent (QDropEvent *event)\n{\n    emit passEvent(event);\n    event->accept();\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetooltexturebrush.hpp",
    "content": "#ifndef CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H\n#define CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H\n\n#include <QIcon>\n#include <QFrame>\n#include <QModelIndex>\n\n#include <QWidget>\n#include <QLabel>\n#include <QSpinBox>\n#include <QGroupBox>\n#include <QSlider>\n#include <QEvent>\n#include <QHBoxLayout>\n#include <QPushButton>\n\n#ifndef Q_MOC_RUN\n#include \"brushshapes.hpp\"\n#include \"scenetool.hpp\"\n\n#include \"../../model/doc/document.hpp\"\n#endif\n\nclass QTableWidget;\n\nnamespace CSVRender\n{\n    class TerrainTextureMode;\n}\n\nnamespace CSVWidget\n{\n    class SceneToolTextureBrush;\n\n    /// \\brief Layout-box for some brush button settings\n    class BrushSizeControls : public QGroupBox\n    {\n        Q_OBJECT\n\n        public:\n            BrushSizeControls(const QString &title, QWidget *parent);\n\n        private:\n            QHBoxLayout *mLayoutSliderSize;\n            QSlider *mBrushSizeSlider;\n            QSpinBox *mBrushSizeSpinBox;\n\n        friend class SceneToolTextureBrush;\n        friend class CSVRender::TerrainTextureMode;\n    };\n\n    class SceneToolTextureBrush;\n\n    /// \\brief Brush settings window\n    class TextureBrushWindow : public QFrame\n    {\n        Q_OBJECT\n\n        public:\n            TextureBrushWindow(CSMDoc::Document& document, QWidget *parent = nullptr);\n            void configureButtonInitialSettings(QPushButton *button);\n\n            const QString toolTipPoint = \"Paint single point\";\n            const QString toolTipSquare = \"Paint with square brush\";\n            const QString toolTipCircle = \"Paint with circle brush\";\n            const QString toolTipCustom = \"Paint custom selection (not implemented yet)\";\n\n        private:\n            CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point;\n            int mBrushSize = 1;\n            std::string mBrushTexture = \"L0#0\";\n            CSMDoc::Document& mDocument;\n            QLabel *mSelectedBrush;\n            QGroupBox *mHorizontalGroupBox;\n            std::string mBrushTextureLabel;\n            QPushButton *mButtonPoint;\n            QPushButton *mButtonSquare;\n            QPushButton *mButtonCircle;\n            QPushButton *mButtonCustom;\n            BrushSizeControls* mSizeSliders;\n\n        friend class SceneToolTextureBrush;\n        friend class CSVRender::TerrainTextureMode;\n\n        public slots:\n            void setBrushTexture(std::string brushTexture);\n            void setBrushShape();\n            void setBrushSize(int brushSize);\n\n        signals:\n            void passBrushSize (int brushSize);\n            void passBrushShape(CSVWidget::BrushShape brushShape);\n            void passTextureId(std::string brushTexture);\n    };\n\n    class SceneToolTextureBrush : public SceneTool\n    {\n            Q_OBJECT\n\n            QString mToolTip;\n            CSMDoc::Document& mDocument;\n            QFrame *mPanel;\n            QTableWidget *mTable;\n            std::vector<std::string> mBrushHistory;\n            TextureBrushWindow *mTextureBrushWindow;\n\n        private:\n\n            void adjustToolTips();\n\n        public:\n\n            SceneToolTextureBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document);\n\n            void showPanel (const QPoint& position) override;\n            void updatePanel ();\n\n            void dropEvent (QDropEvent *event) override;\n            void dragEnterEvent (QDragEnterEvent *event) override;\n\n        friend class CSVRender::TerrainTextureMode;\n\n        public slots:\n            void setButtonIcon(CSVWidget::BrushShape brushShape);\n            void updateBrushHistory (const std::string& mBrushTexture);\n            void clicked (const QModelIndex& index);\n            void activate() override;\n\n        signals:\n            void passEvent(QDropEvent *event);\n            void passEvent(QDragEnterEvent *event);\n            void passTextureId(std::string brushTexture);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetooltoggle.cpp",
    "content": "#include \"scenetooltoggle.hpp\"\n\n#include <stdexcept>\n\n#include <QHBoxLayout>\n#include <QFrame>\n#include <QIcon>\n#include <QPainter>\n\n#include \"scenetoolbar.hpp\"\n#include \"pushbutton.hpp\"\n\nvoid CSVWidget::SceneToolToggle::adjustToolTip()\n{\n    QString toolTip = mToolTip;\n\n    toolTip += \"<p>Currently enabled: \";\n\n    bool first = true;\n\n    for (std::map<PushButton *, ButtonDesc>::const_iterator iter (mButtons.begin());\n        iter!=mButtons.end(); ++iter)\n        if (iter->first->isChecked())\n        {\n            if (!first)\n                toolTip += \", \";\n            else\n                first = false;\n\n            toolTip += iter->second.mName;\n        }\n\n    if (first)\n        toolTip += \"none\";\n\n    toolTip += \"<p>(left click to alter selection)\";\n\n    setToolTip (toolTip);\n}\n\nvoid CSVWidget::SceneToolToggle::adjustIcon()\n{\n    unsigned int selection = getSelectionMask();\n    if (!selection)\n        setIcon (QIcon (QString::fromUtf8 (mEmptyIcon.c_str())));\n    else\n    {\n        QPixmap pixmap (48, 48);\n        pixmap.fill (QColor (0, 0, 0, 0));\n\n        {\n            QPainter painter (&pixmap);\n\n            for (std::map<PushButton *, ButtonDesc>::const_iterator iter (mButtons.begin());\n                iter!=mButtons.end(); ++iter)\n                if (iter->first->isChecked())\n                {\n                    painter.drawImage (getIconBox (iter->second.mIndex),\n                        QImage (QString::fromUtf8 (iter->second.mSmallIcon.c_str())));\n                }\n        }\n\n        setIcon (pixmap);\n    }\n}\n\nQRect CSVWidget::SceneToolToggle::getIconBox (int index) const\n{\n    // layout for a 3x3 grid\n    int xMax = 3;\n    int yMax = 3;\n\n    // icon size\n    int xBorder = 1;\n    int yBorder = 1;\n\n    int iconXSize = (mIconSize-xBorder*(xMax+1))/xMax;\n    int iconYSize = (mIconSize-yBorder*(yMax+1))/yMax;\n\n    int y = index / xMax;\n    int x = index % xMax;\n\n    int total = static_cast<int>(mButtons.size());\n\n    int actualYIcons = total/xMax;\n\n    if (total % xMax)\n        ++actualYIcons;\n\n    if (actualYIcons!=yMax)\n    {\n        // space out icons vertically, if there aren't enough to populate all rows\n        int diff = yMax - actualYIcons;\n        yBorder += (diff*(yBorder+iconXSize)) / (actualYIcons+1);\n    }\n\n    if (y==actualYIcons-1)\n    {\n        // generating the last row of icons\n        int actualXIcons = total % xMax;\n\n        if (actualXIcons)\n        {\n            // space out icons horizontally, if there aren't enough to fill the last row\n            int diff = xMax - actualXIcons;\n\n            xBorder += (diff*(xBorder+iconXSize)) / (actualXIcons+1);\n        }\n    }\n\n    return QRect ((iconXSize+xBorder)*x+xBorder, (iconYSize+yBorder)*y+yBorder,\n        iconXSize, iconYSize);\n}\n\nCSVWidget::SceneToolToggle::SceneToolToggle (SceneToolbar *parent, const QString& toolTip,\n    const std::string& emptyIcon)\n: SceneTool (parent), mEmptyIcon (emptyIcon), mButtonSize (parent->getButtonSize()),\n  mIconSize (parent->getIconSize()), mToolTip (toolTip), mFirst (nullptr)\n{\n    mPanel = new QFrame (this, Qt::Popup);\n\n    mLayout = new QHBoxLayout (mPanel);\n\n    mLayout->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    mPanel->setLayout (mLayout);\n}\n\nvoid CSVWidget::SceneToolToggle::showPanel (const QPoint& position)\n{\n    mPanel->move (position);\n    mPanel->show();\n\n    if (mFirst)\n        mFirst->setFocus (Qt::OtherFocusReason);\n}\n\nvoid CSVWidget::SceneToolToggle::addButton (const std::string& icon, unsigned int mask,\n    const std::string& smallIcon, const QString& name, const QString& tooltip)\n{\n    if (mButtons.size()>=9)\n        throw std::runtime_error (\"Exceeded number of buttons in toggle type tool\");\n\n    PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())),\n        PushButton::Type_Toggle, tooltip.isEmpty() ? name: tooltip, mPanel);\n\n    button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));\n    button->setIconSize (QSize (mIconSize, mIconSize));\n    button->setFixedSize (mButtonSize, mButtonSize);\n\n    mLayout->addWidget (button);\n\n    ButtonDesc desc;\n    desc.mMask = mask;\n    desc.mSmallIcon = smallIcon;\n    desc.mName = name;\n    desc.mIndex = static_cast<int>(mButtons.size());\n\n    mButtons.insert (std::make_pair (button, desc));\n\n    connect (button, SIGNAL (clicked()), this, SLOT (selected()));\n\n    if (mButtons.size()==1)\n        mFirst = button;\n}\n\nunsigned int CSVWidget::SceneToolToggle::getSelectionMask() const\n{\n    unsigned int selection = 0;\n\n    for (std::map<PushButton *, ButtonDesc>::const_iterator iter (mButtons.begin());\n        iter!=mButtons.end(); ++iter)\n        if (iter->first->isChecked())\n            selection |= iter->second.mMask;\n\n    return selection;\n}\n\nvoid CSVWidget::SceneToolToggle::setSelectionMask (unsigned int selection)\n{\n    for (std::map<PushButton *, ButtonDesc>::iterator iter (mButtons.begin());\n        iter!=mButtons.end(); ++iter)\n        iter->first->setChecked (selection & iter->second.mMask);\n\n    adjustToolTip();\n    adjustIcon();\n}\n\nvoid CSVWidget::SceneToolToggle::selected()\n{\n    std::map<PushButton *, ButtonDesc>::const_iterator iter =\n        mButtons.find (dynamic_cast<PushButton *> (sender()));\n\n    if (iter!=mButtons.end())\n    {\n        if (!iter->first->hasKeepOpen())\n            mPanel->hide();\n\n        adjustToolTip();\n        adjustIcon();\n\n        emit selectionChanged();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetooltoggle.hpp",
    "content": "#ifndef CSV_WIDGET_SCENETOOL_TOGGLE_H\n#define CSV_WIDGET_SCENETOOL_TOGGLE_H\n\n#include \"scenetool.hpp\"\n\n#include <map>\n\nclass QHBoxLayout;\nclass QRect;\n\nnamespace CSVWidget\n{\n    class SceneToolbar;\n    class PushButton;\n\n    ///< \\brief Multi-Toggle tool\n    class SceneToolToggle : public SceneTool\n    {\n            Q_OBJECT\n\n            struct ButtonDesc\n            {\n                unsigned int mMask;\n                std::string mSmallIcon;\n                QString mName;\n                int mIndex;\n            };\n\n            std::string mEmptyIcon;\n            QWidget *mPanel;\n            QHBoxLayout *mLayout;\n            std::map<PushButton *, ButtonDesc> mButtons; // widget, id\n            int mButtonSize;\n            int mIconSize;\n            QString mToolTip;\n            PushButton *mFirst;\n\n            void adjustToolTip();\n\n            void adjustIcon();\n\n            QRect getIconBox (int index) const;\n\n        public:\n\n            SceneToolToggle (SceneToolbar *parent, const QString& toolTip,\n                const std::string& emptyIcon);\n\n            void showPanel (const QPoint& position) override;\n\n            /// \\attention After the last button has been added, setSelection must be called at\n            /// least once to finalise the layout.\n            ///\n            /// \\note The layout algorithm can not handle more than 9 buttons. To prevent this An\n            /// attempt to add more will result in an exception being thrown.\n            /// The small icons will be sized at (x-4)/3 (where x is the main icon size).\n            void addButton (const std::string& icon, unsigned int mask,\n                const std::string& smallIcon, const QString& name, const QString& tooltip = \"\");\n\n            unsigned int getSelectionMask() const;\n\n            /// \\param or'ed button masks. buttons that do not exist will be ignored.\n            void setSelectionMask (unsigned int selection);\n\n        signals:\n\n            void selectionChanged();\n\n        private slots:\n\n            void selected();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetooltoggle2.cpp",
    "content": "#include \"scenetooltoggle2.hpp\"\n\n#include <stdexcept>\n#include <sstream>\n\n#include <QHBoxLayout>\n#include <QFrame>\n#include <QIcon>\n#include <QPainter>\n\n#include \"scenetoolbar.hpp\"\n#include \"pushbutton.hpp\"\n\nvoid CSVWidget::SceneToolToggle2::adjustToolTip()\n{\n    QString toolTip = mToolTip;\n\n    toolTip += \"<p>Currently enabled: \";\n\n    bool first = true;\n\n    for (std::map<PushButton *, ButtonDesc>::const_iterator iter (mButtons.begin());\n        iter!=mButtons.end(); ++iter)\n        if (iter->first->isChecked())\n        {\n            if (!first)\n                toolTip += \", \";\n            else\n                first = false;\n\n            toolTip += iter->second.mName;\n        }\n\n    if (first)\n        toolTip += \"none\";\n\n    toolTip += \"<p>(left click to alter selection)\";\n\n    setToolTip (toolTip);\n}\n\nvoid CSVWidget::SceneToolToggle2::adjustIcon()\n{\n    unsigned int buttonIds = 0;\n\n    for (std::map<PushButton *, ButtonDesc>::const_iterator iter (mButtons.begin());\n        iter!=mButtons.end(); ++iter)\n        if (iter->first->isChecked())\n            buttonIds |= iter->second.mButtonId;\n\n    std::ostringstream stream;\n    stream << mCompositeIcon << buttonIds;\n    setIcon (QIcon (QString::fromUtf8 (stream.str().c_str())));\n}\n\nCSVWidget::SceneToolToggle2::SceneToolToggle2 (SceneToolbar *parent, const QString& toolTip,\n    const std::string& compositeIcon, const std::string& singleIcon)\n: SceneTool (parent), mCompositeIcon (compositeIcon), mSingleIcon (singleIcon),\n  mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()), mToolTip (toolTip),\n  mFirst (nullptr)\n{\n    mPanel = new QFrame (this, Qt::Popup);\n\n    mLayout = new QHBoxLayout (mPanel);\n\n    mLayout->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    mPanel->setLayout (mLayout);\n}\n\nvoid CSVWidget::SceneToolToggle2::showPanel (const QPoint& position)\n{\n    mPanel->move (position);\n    mPanel->show();\n\n    if (mFirst)\n        mFirst->setFocus (Qt::OtherFocusReason);\n}\n\nvoid CSVWidget::SceneToolToggle2::addButton (unsigned int id, unsigned int mask,\n    const QString& name, const QString& tooltip, bool disabled)\n{\n    std::ostringstream stream;\n    stream << mSingleIcon << id;\n\n    PushButton *button = new PushButton (QIcon (QPixmap (stream.str().c_str())),\n        PushButton::Type_Toggle, tooltip.isEmpty() ? name: tooltip, mPanel);\n\n    button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));\n    button->setIconSize (QSize (mIconSize, mIconSize));\n    button->setFixedSize (mButtonSize, mButtonSize);\n\n    if (disabled)\n        button->setDisabled (true);\n\n    mLayout->addWidget (button);\n\n    ButtonDesc desc;\n    desc.mButtonId = id;\n    desc.mMask = mask;\n    desc.mName = name;\n    desc.mIndex = static_cast<int>(mButtons.size());\n\n    mButtons.insert (std::make_pair (button, desc));\n\n    connect (button, SIGNAL (clicked()), this, SLOT (selected()));\n\n    if (mButtons.size()==1 && !disabled)\n        mFirst = button;\n}\n\nunsigned int CSVWidget::SceneToolToggle2::getSelectionMask() const\n{\n    unsigned int selection = 0;\n\n    for (std::map<PushButton *, ButtonDesc>::const_iterator iter (mButtons.begin());\n        iter!=mButtons.end(); ++iter)\n        if (iter->first->isChecked())\n            selection |= iter->second.mMask;\n\n    return selection;\n}\n\nvoid CSVWidget::SceneToolToggle2::setSelectionMask (unsigned int selection)\n{\n    for (std::map<PushButton *, ButtonDesc>::iterator iter (mButtons.begin());\n        iter!=mButtons.end(); ++iter)\n        iter->first->setChecked (selection & iter->second.mMask);\n\n    adjustToolTip();\n    adjustIcon();\n}\n\nvoid CSVWidget::SceneToolToggle2::selected()\n{\n    std::map<PushButton *, ButtonDesc>::const_iterator iter =\n        mButtons.find (dynamic_cast<PushButton *> (sender()));\n\n    if (iter!=mButtons.end())\n    {\n        if (!iter->first->hasKeepOpen())\n            mPanel->hide();\n\n        adjustToolTip();\n        adjustIcon();\n\n        emit selectionChanged();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/widget/scenetooltoggle2.hpp",
    "content": "#ifndef CSV_WIDGET_SCENETOOL_TOGGLE2_H\n#define CSV_WIDGET_SCENETOOL_TOGGLE2_H\n\n#include \"scenetool.hpp\"\n\n#include <map>\n\nclass QHBoxLayout;\nclass QRect;\n\nnamespace CSVWidget\n{\n    class SceneToolbar;\n    class PushButton;\n\n    ///< \\brief Multi-Toggle tool\n    ///\n    /// Top level button is using predefined icons instead building a composite icon.\n    class SceneToolToggle2 : public SceneTool\n    {\n            Q_OBJECT\n\n            struct ButtonDesc\n            {\n                unsigned int mButtonId;\n                unsigned int mMask;\n                QString mName;\n                int mIndex;\n            };\n\n            std::string mCompositeIcon;\n            std::string mSingleIcon;\n            QWidget *mPanel;\n            QHBoxLayout *mLayout;\n            std::map<PushButton *, ButtonDesc> mButtons; // widget, id\n            int mButtonSize;\n            int mIconSize;\n            QString mToolTip;\n            PushButton *mFirst;\n\n            void adjustToolTip();\n\n            void adjustIcon();\n\n        public:\n\n            /// The top level icon is compositeIcon + sum of bitpatterns for active buttons (in\n            /// decimal)\n            ///\n            /// The icon for individual toggle buttons is signleIcon + bitmask for button (in\n            /// decimal)\n            SceneToolToggle2 (SceneToolbar *parent, const QString& toolTip,\n                const std::string& compositeIcon, const std::string& singleIcon);\n\n            void showPanel (const QPoint& position) override;\n\n            /// \\param buttonId used to compose the icon filename\n            /// \\param mask used for the reported getSelectionMask() / setSelectionMask()\n            /// \\attention After the last button has been added, setSelection must be called at\n            /// least once to finalise the layout.\n            void addButton (unsigned int buttonId, unsigned int mask,\n                const QString& name, const QString& tooltip = \"\", bool disabled = false);\n\n            unsigned int getSelectionMask() const;\n\n            /// \\param or'ed button masks. buttons that do not exist will be ignored.\n            void setSelectionMask (unsigned int selection);\n\n        signals:\n\n            void selectionChanged();\n\n        private slots:\n\n            void selected();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/bodypartcreator.cpp",
    "content": "#include \"bodypartcreator.hpp\"\n\n#include <QCheckBox>\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/universalid.hpp\"\n\nstd::string CSVWorld::BodyPartCreator::getId() const\n{\n    std::string id = CSVWorld::GenericCreator::getId();\n\n    if (mFirstPerson->isChecked())\n    {\n        id += \".1st\";\n    }\n\n    return id;\n}\n\nCSVWorld::BodyPartCreator::BodyPartCreator(\n    CSMWorld::Data& data,\n    QUndoStack& undoStack,\n    const CSMWorld::UniversalId& id\n) : GenericCreator(data, undoStack, id)\n{\n    mFirstPerson = new QCheckBox(\"First Person\", this);\n    insertBeforeButtons(mFirstPerson, false);\n\n    connect(mFirstPerson, SIGNAL(clicked(bool)), this, SLOT(checkboxClicked()));\n}\n\nstd::string CSVWorld::BodyPartCreator::getErrors() const\n{\n    std::string errors;\n\n    std::string id = getId();\n    if (getData().hasId(id))\n    {\n        errors = \"ID is already in use\";\n    }\n\n    return errors;\n}\n\nvoid CSVWorld::BodyPartCreator::reset()\n{\n    CSVWorld::GenericCreator::reset();\n    mFirstPerson->setChecked(false);\n}\n\nvoid CSVWorld::BodyPartCreator::checkboxClicked()\n{\n    update();\n}\n"
  },
  {
    "path": "apps/opencs/view/world/bodypartcreator.hpp",
    "content": "#ifndef BODYPARTCREATOR_HPP\n#define BODYPARTCREATOR_HPP\n\nclass QCheckBox;\n\n#include \"genericcreator.hpp\"\n\nnamespace CSMWorld\n{\n    class Data;\n    class UniversalId;\n}\n\nnamespace CSVWorld\n{\n    /// \\brief Record creator for body parts.\n    class BodyPartCreator : public GenericCreator\n    {\n        Q_OBJECT\n\n        QCheckBox *mFirstPerson;\n\n        private:\n\n            /// \\return ID entered by user.\n            std::string getId() const override;\n\n        public:\n\n            BodyPartCreator(\n                CSMWorld::Data& data,\n                QUndoStack& undoStack,\n                const CSMWorld::UniversalId& id);\n\n            /// \\return Error description for current user input.\n            std::string getErrors() const override;\n\n            /// \\brief Clear ID and checkbox input widgets.\n            void reset() override;\n\n        private slots:\n\n            void checkboxClicked();\n    };\n}\n\n#endif // BODYPARTCREATOR_HPP\n"
  },
  {
    "path": "apps/opencs/view/world/cellcreator.cpp",
    "content": "#include \"cellcreator.hpp\"\n\n#include <limits>\n#include <sstream>\n\n#include <QComboBox>\n#include <QSpinBox>\n#include <QLabel>\n\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/idtree.hpp\"\n\nstd::string CSVWorld::CellCreator::getId() const\n{\n    if (mType->currentIndex()==0)\n        return GenericCreator::getId();\n\n    std::ostringstream stream;\n\n    stream << \"#\" << mX->value() << \" \" << mY->value();\n\n    return stream.str();\n}\n\nvoid CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const\n{\n    CSMWorld::IdTree* model = &dynamic_cast<CSMWorld::IdTree&>(*getData().getTableModel(getCollectionId()));\n\n    int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell);\n    int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior);\n    command.addNestedValue(parentIndex, index, mType->currentIndex() == 0);\n}\n\nCSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n    const CSMWorld::UniversalId& id)\n: GenericCreator (data, undoStack, id)\n{\n    mY = new QSpinBox (this);\n    mY->setVisible (false);\n    mY->setMinimum (std::numeric_limits<int>::min());\n    mY->setMaximum (std::numeric_limits<int>::max());\n    connect (mY, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));\n    insertAtBeginning (mY, true);\n\n    mYLabel = new QLabel (\"Y\", this);\n    mYLabel->setVisible (false);\n    insertAtBeginning (mYLabel, false);\n\n    mX = new QSpinBox (this);\n    mX->setVisible (false);\n    mX->setMinimum (std::numeric_limits<int>::min());\n    mX->setMaximum (std::numeric_limits<int>::max());\n    connect (mX, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));\n    insertAtBeginning (mX, true);\n\n    mXLabel = new QLabel (\"X\", this);\n    mXLabel->setVisible (false);\n    insertAtBeginning (mXLabel, false);\n\n    mType = new QComboBox (this);\n\n    mType->addItem (\"Interior Cell\");\n    mType->addItem (\"Exterior Cell\");\n\n    connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int)));\n\n    insertAtBeginning (mType, false);\n}\n\nvoid CSVWorld::CellCreator::reset()\n{\n    mX->setValue (0);\n    mY->setValue (0);\n    mType->setCurrentIndex (0);\n    setType(0);\n    GenericCreator::reset();\n}\n\nvoid CSVWorld::CellCreator::setType (int index)\n{\n    setManualEditing (index==0);\n    mXLabel->setVisible (index==1);\n    mX->setVisible (index==1);\n    mYLabel->setVisible (index==1);\n    mY->setVisible (index==1);\n\n    update();\n}\n\nvoid CSVWorld::CellCreator::valueChanged (int index)\n{\n    update();\n}\n\nvoid CSVWorld::CellCreator::cloneMode(const std::string& originId, \n                                      const CSMWorld::UniversalId::Type type)\n{\n    CSVWorld::GenericCreator::cloneMode(originId, type);\n    if (*(originId.begin()) == '#') //if originid points to the exterior cell\n    {\n        setType(1); //enable x and y controls\n        mType->setCurrentIndex(1);\n    } else {\n        setType(0);\n        mType->setCurrentIndex(0);\n    }\n}\n\nstd::string CSVWorld::CellCreator::getErrors() const\n{\n    std::string errors;\n    if (mType->currentIndex() == 0)\n    {\n        errors = GenericCreator::getErrors();\n    }\n    else if (getData().hasId(getId()))\n    {\n        errors = \"The Exterior Cell is already exist\";\n    }\n    return errors;\n}\n"
  },
  {
    "path": "apps/opencs/view/world/cellcreator.hpp",
    "content": "#ifndef CSV_WORLD_CELLCREATOR_H\n#define CSV_WORLD_CELLCREATOR_H\n\nclass QLabel;\nclass QSpinBox;\nclass QComboBox;\n\n#include \"genericcreator.hpp\"\n\nnamespace CSVWorld\n{\n    class CellCreator : public GenericCreator\n    {\n            Q_OBJECT\n\n            QComboBox *mType;\n            QLabel *mXLabel;\n            QSpinBox *mX;\n            QLabel *mYLabel;\n            QSpinBox *mY;\n\n        protected:\n\n            std::string getId() const override;\n\n            /// Allow subclasses to add additional data to \\a command.\n            void configureCreateCommand(CSMWorld::CreateCommand& command) const override;\n\n        public:\n\n            CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);\n\n            void reset() override;\n\n            void cloneMode(const std::string& originId, \n                                   const CSMWorld::UniversalId::Type type) override;\n\n            std::string getErrors() const override;\n            ///< Return formatted error descriptions for the current state of the creator. if an empty\n            /// string is returned, there is no error.\n\n        private slots:\n\n            void setType (int index);\n\n            void valueChanged (int index);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/colordelegate.cpp",
    "content": "#include \"colordelegate.hpp\"\n\n#include <QPainter>\n#include <QPushButton>\n\nCSVWorld::ColorDelegate::ColorDelegate(CSMWorld::CommandDispatcher *dispatcher,\n                                       CSMDoc::Document& document,\n                                       QObject *parent)\n    : CommandDelegate(dispatcher, document, parent)\n{}\n\nvoid CSVWorld::ColorDelegate::paint(QPainter *painter,\n                                    const QStyleOptionViewItem &option,\n                                    const QModelIndex &index) const\n{\n    int colorInt = index.data().toInt();\n    QColor color(colorInt & 0xff, (colorInt >> 8) & 0xff, (colorInt >> 16) & 0xff);\n\n    QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0),\n                      option.rect.y() + qRound(option.rect.height() / 4.0),\n                      option.rect.width() / 2,\n                      option.rect.height() / 2);\n    painter->save();\n    painter->fillRect(coloredRect, color);\n    painter->setPen(Qt::black);\n    painter->drawRect(coloredRect);\n    painter->restore();\n}\n\nCSVWorld::CommandDelegate *CSVWorld::ColorDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher,\n                                                                        CSMDoc::Document &document,\n                                                                        QObject *parent) const\n{\n    return new ColorDelegate(dispatcher, document, parent);\n}\n\n\n"
  },
  {
    "path": "apps/opencs/view/world/colordelegate.hpp",
    "content": "#ifndef CSV_WORLD_COLORDELEGATE_HPP\n#define CSV_WORLD_COLORDELEGATE_HPP\n\n#include \"util.hpp\"\n\nclass QRect;\n\nnamespace CSVWidget\n{\n    class ColorEditButton;\n}\n\nnamespace CSVWorld\n{\n    class ColorDelegate : public CommandDelegate\n    {\n        public:\n            ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, \n                          CSMDoc::Document& document, \n                          QObject *parent);\n\n            void paint(QPainter *painter, \n                               const QStyleOptionViewItem &option,\n                               const QModelIndex &index) const override;\n    };\n\n    class ColorDelegateFactory : public CommandDelegateFactory\n    {\n        public:\n            CommandDelegate *makeDelegate(CSMWorld::CommandDispatcher *dispatcher, \n                                                  CSMDoc::Document &document, \n                                                  QObject *parent) const override;\n            ///< The ownership of the returned CommandDelegate is transferred to the caller.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/creator.cpp",
    "content": "#include \"creator.hpp\"\n\n#include <stdexcept>\n\nCSVWorld::Creator::~Creator() {}\n\nvoid CSVWorld::Creator::setScope (unsigned int scope)\n{\n    if (scope!=CSMWorld::Scope_Content)\n        throw std::logic_error (\"Invalid scope in creator\");\n}\n\n\nCSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {}\n\n\nCSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMDoc::Document& document, \n                                                              const CSMWorld::UniversalId& id) const\n{\n    return nullptr;\n}\n"
  },
  {
    "path": "apps/opencs/view/world/creator.hpp",
    "content": "#ifndef CSV_WORLD_CREATOR_H\n#define CSV_WORLD_CREATOR_H\n\n#include <memory>\n\n#include <QWidget>\n\n#ifndef Q_MOC_RUN\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/scope.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#endif\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVWorld\n{\n    /// \\brief Record creator UI base class\n    class Creator : public QWidget\n    {\n            Q_OBJECT\n\n        public:\n\n            virtual ~Creator();\n\n            virtual void reset() = 0;\n\n            virtual void cloneMode(const std::string& originId,\n                                   const CSMWorld::UniversalId::Type type) = 0;\n\n            /// Touches a record, if the creator supports it.\n            virtual void touch(const std::vector<CSMWorld::UniversalId>& ids) = 0;\n\n            virtual void setEditLock (bool locked) = 0;\n\n            virtual void toggleWidgets(bool active = true) = 0;\n\n            /// Default implementation: Throw an exception if scope!=Scope_Content.\n            virtual void setScope (unsigned int scope);\n\n            /// Focus main input widget\n            virtual void focus() = 0;\n\n        signals:\n\n            void done();\n\n            void requestFocus (const std::string& id);\n            ///< Request owner of this creator to focus the just created \\a id. The owner may\n            /// ignore this request.\n    };\n\n    /// \\brief Base class for Creator factory\n    class CreatorFactoryBase\n    {\n        public:\n\n            virtual ~CreatorFactoryBase();\n\n            virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const = 0;\n            ///< The ownership of the returned Creator is transferred to the caller.\n            ///\n            /// \\note The function can return a 0-pointer, which means no UI for creating/deleting\n            /// records should be provided.\n    };\n\n    /// \\brief Creator factory that does not produces any creator\n    class NullCreatorFactory : public CreatorFactoryBase\n    {\n        public:\n\n            Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const override;\n            ///< The ownership of the returned Creator is transferred to the caller.\n            ///\n            /// \\note The function always returns 0.\n    };\n\n    template<class CreatorT, unsigned int scope = CSMWorld::Scope_Content>\n    class CreatorFactory : public CreatorFactoryBase\n    {\n        public:\n\n            Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const override;\n            ///< The ownership of the returned Creator is transferred to the caller.\n            ///\n            /// \\note The function can return a 0-pointer, which means no UI for creating/deleting\n            /// records should be provided.\n    };\n\n    template<class CreatorT, unsigned int scope>\n    Creator *CreatorFactory<CreatorT, scope>::makeCreator (CSMDoc::Document& document,\n                                                           const CSMWorld::UniversalId& id) const\n    {\n        std::unique_ptr<CreatorT> creator (new CreatorT (document.getData(), document.getUndoStack(), id));\n\n        creator->setScope (scope);\n\n        return creator.release();\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/datadisplaydelegate.cpp",
    "content": "#include \"datadisplaydelegate.hpp\"\n\n#include \"../../model/prefs/state.hpp\"\n\n#include <QApplication>\n#include <QPainter>\n\nCSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values,\n                                                   const IconList &icons,\n                                                   CSMWorld::CommandDispatcher *dispatcher,\n                                                   CSMDoc::Document& document,\n                                                   const std::string &pageName,\n                                                   const std::string &settingName,\n                                                   QObject *parent)\n    : EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly),\n      mIcons (icons), mIconSize (QSize(16, 16)),\n      mHorizontalMargin(QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1),\n      mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName)\n{\n    buildPixmaps();\n\n    if (!pageName.empty())\n        updateDisplayMode (CSMPrefs::get()[pageName][settingName].toString());\n}\n\nvoid CSVWorld::DataDisplayDelegate::buildPixmaps ()\n{\n    if (!mPixmaps.empty())\n        mPixmaps.clear();\n\n    IconList::iterator it = mIcons.begin();\n\n    while (it != mIcons.end())\n    {\n        mPixmaps.emplace_back (it->mValue, it->mIcon.pixmap (mIconSize) );\n        ++it;\n    }\n}\n\nvoid CSVWorld::DataDisplayDelegate::setIconSize(const QSize& size)\n{\n    mIconSize = size;\n    buildPixmaps();\n}\n\nvoid CSVWorld::DataDisplayDelegate::setTextLeftOffset(int offset)\n{\n    mTextLeftOffset = offset;\n}\n\nQSize CSVWorld::DataDisplayDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const\n{\n    QSize size = EnumDelegate::sizeHint(option, index);\n\n    int valueIndex = getValueIndex(index);\n    if (valueIndex != -1)\n    {\n        if (mDisplayMode == Mode_IconOnly)\n        {\n            size.setWidth(mIconSize.width() + 2 * mHorizontalMargin);\n        }\n        else if (mDisplayMode == Mode_IconAndText)\n        {\n            size.setWidth(size.width() + mIconSize.width() + mTextLeftOffset);\n        }\n\n        if (mDisplayMode != Mode_TextOnly)\n        {\n            size.setHeight(qMax(size.height(), mIconSize.height()));\n        }\n    }\n    return size;\n}\n\nvoid CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const\n{\n    painter->save();\n\n    //default to enum delegate's paint method for text-only conditions\n    if (mDisplayMode == Mode_TextOnly)\n        EnumDelegate::paint(painter, option, index);\n    else\n    {\n        int valueIndex = getValueIndex(index);\n        if (valueIndex != -1)\n        {\n            paintIcon(painter, option, valueIndex);\n        }\n    }\n\n    painter->restore();\n}\n\nvoid CSVWorld::DataDisplayDelegate::paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int index) const\n{\n    QRect iconRect = option.rect;\n    QRect textRect = iconRect;\n\n    iconRect.setLeft(iconRect.left() + mHorizontalMargin);\n    iconRect.setRight(option.rect.right() - mHorizontalMargin);\n    if (mDisplayMode == Mode_IconAndText)\n    {\n        iconRect.setWidth(mIconSize.width());\n        textRect.setLeft(iconRect.right() + mTextLeftOffset);\n        textRect.setRight(option.rect.right() - mHorizontalMargin);\n\n        QString text = option.fontMetrics.elidedText(mValues.at(index).second,\n                                                     option.textElideMode,\n                                                     textRect.width());\n        QApplication::style()->drawItemText(painter,\n                                            textRect,\n                                            Qt::AlignLeft | Qt::AlignVCenter,\n                                            option.palette,\n                                            true,\n                                            text);\n    }\n    QApplication::style()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, mPixmaps.at(index).second);\n}\n\nvoid CSVWorld::DataDisplayDelegate::updateDisplayMode (const std::string &mode)\n{\n    if (mode == \"Icon and Text\")\n        mDisplayMode = Mode_IconAndText;\n\n    else if (mode == \"Icon Only\")\n        mDisplayMode = Mode_IconOnly;\n\n    else if (mode == \"Text Only\")\n        mDisplayMode = Mode_TextOnly;\n}\n\nCSVWorld::DataDisplayDelegate::~DataDisplayDelegate()\n{\n}\n\nvoid CSVWorld::DataDisplayDelegate::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==mSettingKey)\n        updateDisplayMode (setting->toString());\n}\n\n\nvoid CSVWorld::DataDisplayDelegateFactory::add (int enumValue, const QString& enumName, const QString& iconFilename)\n{\n    EnumDelegateFactory::add(enumValue, enumName);\n\n    Icon icon;\n    icon.mValue = enumValue;\n    icon.mName = enumName;\n    icon.mIcon = QIcon(iconFilename);\n\n    for (auto it=mIcons.begin(); it!=mIcons.end(); ++it)\n    {\n        if (it->mName > enumName)\n        {\n            mIcons.insert(it, icon);\n            return;\n        }\n    }\n\n    mIcons.push_back(icon);\n}\n\nCSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate (\n    CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const\n{\n    return new DataDisplayDelegate (mValues, mIcons, dispatcher, document, \"\", \"\", parent);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/datadisplaydelegate.hpp",
    "content": "#ifndef DATADISPLAYDELEGATE_HPP\n#define DATADISPLAYDELEGATE_HPP\n\n#include <QTextOption>\n#include \"enumdelegate.hpp\"\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWorld\n{\n    struct Icon\n    {\n        int mValue;\n        QIcon mIcon;\n        QString mName;\n    };\n\n    class DataDisplayDelegate : public EnumDelegate\n    {\n    public:\n\n        typedef std::vector<Icon> IconList;\n        typedef std::vector<std::pair<int, QString> > ValueList;\n\n    protected:\n\n        enum DisplayMode\n        {\n            Mode_TextOnly,\n            Mode_IconOnly,\n            Mode_IconAndText\n        };\n\n        DisplayMode mDisplayMode;\n        IconList mIcons;\n\n    private:\n\n        std::vector <std::pair <int, QPixmap> > mPixmaps;\n        QSize mIconSize;\n        int mHorizontalMargin;\n        int mTextLeftOffset;\n\n        std::string mSettingKey;\n\n    public:\n        DataDisplayDelegate (const ValueList & values, const IconList & icons,\n            CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document,\n            const std::string& pageName, const std::string& settingName, QObject *parent);\n\n        ~DataDisplayDelegate();\n\n        void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;\n\n        QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;\n\n        /// pass a QSize defining height / width of icon. Default is QSize (16,16).\n        void setIconSize (const QSize& icon);\n\n        /// offset the horizontal position of the text from the right edge of the icon.  Default is 8 pixels.\n        void setTextLeftOffset (int offset);\n\n    private:\n\n        /// update the display mode based on a passed string\n        void updateDisplayMode (const std::string &);\n\n        /// custom paint function for painting the icon.  Mode_IconAndText and Mode_Icon only.\n        void paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int i) const;\n\n        /// rebuild the list of pixmaps from the provided icons (called when icon size is changed)\n        void buildPixmaps();\n\n        void settingChanged (const CSMPrefs::Setting *setting) override;\n    };\n\n    class DataDisplayDelegateFactory : public EnumDelegateFactory\n    {\n    protected:\n\n        DataDisplayDelegate::IconList mIcons;\n\n    public:\n\n        CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const override;\n        ///< The ownership of the returned CommandDelegate is transferred to the caller.\n\n    protected:\n\n       void add (int enumValue, const QString& enumName, const QString& iconFilename);\n\n    };\n\n}\n\n#endif // DATADISPLAYDELEGATE_HPP\n"
  },
  {
    "path": "apps/opencs/view/world/dialoguecreator.cpp",
    "content": "#include \"dialoguecreator.hpp\"\n\n#include <components/esm/loaddial.hpp>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/idtable.hpp\"\n\nvoid CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const\n{\n    int index =\n        dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).\n        findColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);\n\n    command.addValue (index, mType);\n}\n\nCSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n    const CSMWorld::UniversalId& id, int type)\n: GenericCreator (data, undoStack, id, true), mType (type)\n{}\n\nCSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMDoc::Document& document, \n                                                               const CSMWorld::UniversalId& id) const\n{\n    return new DialogueCreator (document.getData(), document.getUndoStack(), id, ESM::Dialogue::Topic);\n}\n\nCSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMDoc::Document& document, \n                                                                 const CSMWorld::UniversalId& id) const\n{\n    return new DialogueCreator (document.getData(), document.getUndoStack(), id, ESM::Dialogue::Journal);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/dialoguecreator.hpp",
    "content": "#ifndef CSV_WORLD_DIALOGUECREATOR_H\n#define CSV_WORLD_DIALOGUECREATOR_H\n\n#include \"genericcreator.hpp\"\n\nnamespace CSVWorld\n{\n    class DialogueCreator : public GenericCreator\n    {\n            int mType;\n\n        protected:\n\n            void configureCreateCommand (CSMWorld::CreateCommand& command) const override;\n\n        public:\n\n            DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n                const CSMWorld::UniversalId& id, int type);\n    };\n\n    class TopicCreatorFactory : public CreatorFactoryBase\n    {\n        public:\n\n            Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const override;\n            ///< The ownership of the returned Creator is transferred to the caller.\n    };\n\n    class JournalCreatorFactory : public CreatorFactoryBase\n    {\n        public:\n\n            Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const override;\n            ///< The ownership of the returned Creator is transferred to the caller.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/dialoguespinbox.cpp",
    "content": "#include \"dialoguespinbox.hpp\"\n\n#include <QWheelEvent>\n\nCSVWorld::DialogueSpinBox::DialogueSpinBox(QWidget *parent) : QSpinBox(parent)\n{\n    setFocusPolicy(Qt::StrongFocus);\n}\n\nvoid CSVWorld::DialogueSpinBox::focusInEvent(QFocusEvent *event)\n{\n    setFocusPolicy(Qt::WheelFocus);\n    QSpinBox::focusInEvent(event);\n}\n\nvoid CSVWorld::DialogueSpinBox::focusOutEvent(QFocusEvent *event)\n{\n    setFocusPolicy(Qt::StrongFocus);\n    QSpinBox::focusOutEvent(event);\n}\n\nvoid CSVWorld::DialogueSpinBox::wheelEvent(QWheelEvent *event)\n{\n    if (!hasFocus())\n        event->ignore();\n    else\n        QSpinBox::wheelEvent(event);\n}\n\nCSVWorld::DialogueDoubleSpinBox::DialogueDoubleSpinBox(QWidget *parent) : QDoubleSpinBox(parent)\n{\n    setFocusPolicy(Qt::StrongFocus);\n}\n\nvoid CSVWorld::DialogueDoubleSpinBox::focusInEvent(QFocusEvent *event)\n{\n    setFocusPolicy(Qt::WheelFocus);\n    QDoubleSpinBox::focusInEvent(event);\n}\n\nvoid CSVWorld::DialogueDoubleSpinBox::focusOutEvent(QFocusEvent *event)\n{\n    setFocusPolicy(Qt::StrongFocus);\n    QDoubleSpinBox::focusOutEvent(event);\n}\n\nvoid CSVWorld::DialogueDoubleSpinBox::wheelEvent(QWheelEvent *event)\n{\n    if (!hasFocus())\n        event->ignore();\n    else\n        QDoubleSpinBox::wheelEvent(event);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/dialoguespinbox.hpp",
    "content": "#ifndef CSV_WORLD_DIALOGUESPINBOX_H\n#define CSV_WORLD_DIALOGUESPINBOX_H\n\n#include <QSpinBox>\n#include <QDoubleSpinBox>\n\nnamespace CSVWorld\n{\n    class DialogueSpinBox : public QSpinBox\n    {\n        Q_OBJECT\n\n        public:\n\n            DialogueSpinBox (QWidget *parent = nullptr);\n\n        protected:\n\n            void focusInEvent(QFocusEvent *event) override;\n            void focusOutEvent(QFocusEvent *event) override;\n            void wheelEvent(QWheelEvent *event) override;\n    };\n\n    class DialogueDoubleSpinBox : public QDoubleSpinBox\n    {\n        Q_OBJECT\n\n        public:\n\n            DialogueDoubleSpinBox (QWidget *parent = nullptr);\n\n        protected:\n\n            void focusInEvent(QFocusEvent *event) override;\n            void focusOutEvent(QFocusEvent *event) override;\n            void wheelEvent(QWheelEvent *event) override;\n    };\n}\n\n#endif // CSV_WORLD_DIALOGUESPINBOX_H\n"
  },
  {
    "path": "apps/opencs/view/world/dialoguesubview.cpp",
    "content": "#include \"dialoguesubview.hpp\"\n\n#include <utility>\n#include <memory>\n#include <stdexcept>\n\n#include <QGridLayout>\n#include <QLabel>\n#include <QSize>\n#include <QAbstractItemModel>\n#include <QDoubleSpinBox>\n#include <QSpinBox>\n#include <QLineEdit>\n#include <QEvent>\n#include <QDataWidgetMapper>\n#include <QCheckBox>\n#include <QLineEdit>\n#include <QPlainTextEdit>\n#include <QComboBox>\n#include <QHeaderView>\n#include <QScrollBar>\n#include <QMenu>\n\n#include \"../../model/world/nestedtableproxymodel.hpp\"\n#include \"../../model/world/columnbase.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/idtree.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/record.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/prefs/state.hpp\"\n\n#include \"../widget/coloreditor.hpp\"\n#include \"../widget/droplineedit.hpp\"\n\n#include \"recordstatusdelegate.hpp\"\n#include \"util.hpp\"\n#include \"tablebottombox.hpp\"\n#include \"nestedtable.hpp\"\n#include \"recordbuttonbar.hpp\"\n/*\n==============================NotEditableSubDelegate==========================================\n*/\nCSVWorld::NotEditableSubDelegate::NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject * parent) :\nQAbstractItemDelegate(parent),\nmTable(table)\n{}\n\nvoid CSVWorld::NotEditableSubDelegate::setEditorData (QWidget* editor, const QModelIndex& index) const\n{\n    QLabel* label = qobject_cast<QLabel*>(editor);\n    if(!label)\n        return;\n\n    QVariant v = index.data(Qt::EditRole);\n    if (!v.isValid())\n    {\n        v = index.data(Qt::DisplayRole);\n        if (!v.isValid())\n        {\n            return;\n        }\n    }\n\n    CSMWorld::Columns::ColumnId columnId = static_cast<CSMWorld::Columns::ColumnId> (\n        mTable->getColumnId (index.column()));\n\n    if (QVariant::String == v.type())\n    {\n        label->setText(v.toString());\n    }\n    else if (CSMWorld::Columns::hasEnums (columnId))\n    {\n        int data = v.toInt();\n        std::vector<std::pair<int,std::string>> enumNames (CSMWorld::Columns::getEnums (columnId));\n\n        label->setText(QString::fromUtf8(enumNames.at(data).second.c_str()));\n    }\n    else\n    {\n        label->setText (v.toString());\n    }\n}\n\nvoid CSVWorld::NotEditableSubDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const\n{\n    //not editable widgets will not save model data\n}\n\nvoid CSVWorld::NotEditableSubDelegate::paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const\n{\n    //does nothing\n}\n\nQSize CSVWorld::NotEditableSubDelegate::sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const\n{\n    return QSize();\n}\n\nQWidget* CSVWorld::NotEditableSubDelegate::createEditor (QWidget *parent,\n                                const QStyleOptionViewItem& option,\n                                const QModelIndex& index) const\n{\n    QLabel *label = new QLabel(parent);\n    label->setTextInteractionFlags (Qt::TextSelectableByMouse);\n    return label;\n}\n\n/*\n==============================DialogueDelegateDispatcherProxy==========================================\n*/\nCSVWorld::DialogueDelegateDispatcherProxy::refWrapper::refWrapper(const QModelIndex& index) :\nmIndex(index)\n{}\n\nCSVWorld::DialogueDelegateDispatcherProxy::DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display) :\nmEditor(editor),\nmDisplay(display),\nmIndexWrapper(nullptr)\n{\n}\n\nvoid CSVWorld::DialogueDelegateDispatcherProxy::editorDataCommited()\n{\n    if (mIndexWrapper.get())\n    {\n        emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay);\n    }\n}\n\nvoid CSVWorld::DialogueDelegateDispatcherProxy::setIndex(const QModelIndex& index)\n{\n    mIndexWrapper.reset(new refWrapper(index));\n}\n\nQWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const\n{\n    return mEditor;\n}\n\n/*\n==============================DialogueDelegateDispatcher==========================================\n*/\n\nCSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent,\n        CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher,\n        CSMDoc::Document& document, QAbstractItemModel *model) :\nmParent(parent),\nmTable(model ? model : table),\nmCommandDispatcher (commandDispatcher), mDocument (document),\nmNotEditableDelegate(table, parent)\n{\n}\n\nCSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display)\n{\n    CommandDelegate *delegate = nullptr;\n    std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));\n    if (delegateIt == mDelegates.end())\n    {\n        delegate = CommandDelegateFactoryCollection::get().makeDelegate (\n                                    display, &mCommandDispatcher, mDocument, mParent);\n        mDelegates.insert(std::make_pair(display, delegate));\n    } else\n    {\n        delegate = delegateIt->second;\n    }\n    return delegate;\n}\n\nvoid CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor,\n        const QModelIndex& index, CSMWorld::ColumnBase::Display display)\n{\n    setModelData(editor, mTable, index, display);\n}\n\nvoid CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QModelIndex& index) const\n{\n    CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None;\n    if (index.parent().isValid())\n    {\n        display = static_cast<CSMWorld::ColumnBase::Display>\n        (static_cast<CSMWorld::IdTree *>(mTable)->nestedHeaderData (index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());\n    }\n    else\n    {\n        display = static_cast<CSMWorld::ColumnBase::Display>\n        (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());\n    }\n\n    QLabel* label = qobject_cast<QLabel*>(editor);\n    if(label)\n    {\n        mNotEditableDelegate.setEditorData(label, index);\n        return;\n    }\n\n    std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));\n    if (delegateIt != mDelegates.end())\n    {\n        delegateIt->second->setEditorData(editor, index, true);\n    }\n\n    for (unsigned i = 0; i < mProxys.size(); ++i)\n    {\n       if (mProxys[i]->getEditor() == editor)\n        {\n            mProxys[i]->setIndex(index);\n        }\n    }\n}\n\nvoid CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor,\n        QAbstractItemModel* model, const QModelIndex& index) const\n{\n    setModelData(editor, model, index, CSMWorld::ColumnBase::Display_None);\n}\n\nvoid CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor,\n        QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const\n{\n    std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));\n    if (delegateIt != mDelegates.end())\n    {\n        delegateIt->second->setModelData(editor, model, index);\n    }\n}\n\nvoid CSVWorld::DialogueDelegateDispatcher::paint (QPainter* painter,\n        const QStyleOptionViewItem& option, const QModelIndex& index) const\n{\n    //Does nothing\n}\n\nQSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem& option,\n        const QModelIndex& index) const\n{\n    return QSize(); //silencing warning, otherwise does nothing\n}\n\nQWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display,\n        const QModelIndex& index)\n{\n    QVariant variant = index.data();\n    if (!variant.isValid())\n    {\n        variant = index.data(Qt::DisplayRole);\n        if (!variant.isValid())\n        {\n            return nullptr;\n        }\n    }\n\n    QWidget* editor = nullptr;\n    if (! (mTable->flags (index) & Qt::ItemIsEditable))\n    {\n        return mNotEditableDelegate.createEditor(qobject_cast<QWidget*>(mParent),\n                QStyleOptionViewItem(), index);\n    }\n\n    std::map<int, CommandDelegate*>::iterator delegateIt(mDelegates.find(display));\n\n    if (delegateIt != mDelegates.end())\n    {\n        editor = delegateIt->second->createEditor(qobject_cast<QWidget*>(mParent),\n                QStyleOptionViewItem(), index, display);\n\n        DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display);\n\n        // NOTE: For each entry in CSVWorld::CommandDelegate::createEditor() a corresponding entry\n        // is required here\n        if (qobject_cast<CSVWidget::DropLineEdit*>(editor))\n        {\n            connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited()));\n\n            connect(editor, SIGNAL(tableMimeDataDropped(const CSMWorld::UniversalId&, const CSMDoc::Document*)),\n                    proxy, SLOT(editorDataCommited()));\n        }\n        else if (qobject_cast<QCheckBox*>(editor))\n        {\n            connect(editor, SIGNAL(stateChanged(int)), proxy, SLOT(editorDataCommited()));\n        }\n        else if (qobject_cast<QPlainTextEdit*>(editor))\n        {\n            connect(editor, SIGNAL(textChanged()), proxy, SLOT(editorDataCommited()));\n        }\n        else if (qobject_cast<QComboBox*>(editor))\n        {\n            connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited()));\n        }\n        else if (qobject_cast<QAbstractSpinBox*>(editor) || qobject_cast<QLineEdit*>(editor))\n        {\n            connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited()));\n        }\n        else if (qobject_cast<CSVWidget::ColorEditor *>(editor))\n        {\n            connect(editor, SIGNAL(pickingFinished()), proxy, SLOT(editorDataCommited()));\n        }\n        else // throw an exception because this is a coding error\n            throw std::logic_error (\"Dialogue editor type missing\");\n\n        connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)),\n                this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)));\n\n        mProxys.push_back(proxy); //deleted in the destructor\n    }\n    return editor;\n}\n\nCSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher()\n{\n    for (unsigned i = 0; i < mProxys.size(); ++i)\n    {\n        delete mProxys[i]; //unique_ptr could be handy\n    }\n}\n\n\nCSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Display display)\n    : QObject(widget),\n      mWidget(widget),\n      mIdType(CSMWorld::TableMimeData::convertEnums(display))\n{\n    Q_ASSERT(mWidget != nullptr);\n    Q_ASSERT(CSMWorld::ColumnBase::isId(display));\n    Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None);\n\n    mWidget->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(mWidget,\n            SIGNAL(customContextMenuRequested(const QPoint &)),\n            this,\n            SLOT(showContextMenu(const QPoint &)));\n\n    mEditIdAction = new QAction(this);\n    connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest()));\n\n    QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget);\n    if (lineEdit != nullptr)\n    {\n        mContextMenu = lineEdit->createStandardContextMenu();\n    }\n    else\n    {\n        mContextMenu = new QMenu(mWidget);\n    }\n}\n\nvoid CSVWorld::IdContextMenu::excludeId(const std::string &id)\n{\n    mExcludedIds.insert(id);\n}\n\nQString CSVWorld::IdContextMenu::getWidgetValue() const\n{\n    QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget);\n    QLabel *label = qobject_cast<QLabel *>(mWidget);\n\n    QString value = \"\";\n    if (lineEdit != nullptr)\n    {\n        value = lineEdit->text();\n    }\n    else if (label != nullptr)\n    {\n        value = label->text();\n    }\n    return value;\n}\n\nvoid CSVWorld::IdContextMenu::addEditIdActionToMenu(const QString &text)\n{\n    mEditIdAction->setText(text);\n    if (mContextMenu->actions().isEmpty())\n    {\n        mContextMenu->addAction(mEditIdAction);\n    }\n    else if (mContextMenu->actions().first() != mEditIdAction)\n    {\n        QAction *action = mContextMenu->actions().first();\n        mContextMenu->insertAction(action, mEditIdAction);\n        mContextMenu->insertSeparator(action);\n    }\n}\n\nvoid CSVWorld::IdContextMenu::removeEditIdActionFromMenu()\n{\n    if (mContextMenu->actions().isEmpty())\n    {\n        return;\n    }\n\n    if (mContextMenu->actions().first() == mEditIdAction)\n    {\n        mContextMenu->removeAction(mEditIdAction);\n        if (!mContextMenu->actions().isEmpty() && mContextMenu->actions().first()->isSeparator())\n        {\n            mContextMenu->removeAction(mContextMenu->actions().first());\n        }\n    }\n}\n\nvoid CSVWorld::IdContextMenu::showContextMenu(const QPoint &pos)\n{\n    QString value = getWidgetValue();\n    bool isExcludedId = mExcludedIds.find(value.toUtf8().constData()) != mExcludedIds.end();\n    if (!value.isEmpty() && !isExcludedId)\n    {\n        addEditIdActionToMenu(\"Edit '\" + value + \"'\");\n    }\n    else\n    {\n        removeEditIdActionFromMenu();\n    }\n\n    if (!mContextMenu->actions().isEmpty())\n    {\n        mContextMenu->exec(mWidget->mapToGlobal(pos));\n    }\n}\n\nvoid CSVWorld::IdContextMenu::editIdRequest()\n{\n    CSMWorld::UniversalId editId(mIdType, getWidgetValue().toUtf8().constData());\n    emit editIdRequest(editId, \"\");\n}\n\n/*\n=============================================================EditWidget=====================================================\n*/\n\nvoid CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor,\n                                                   CSMWorld::ColumnBase::Display display,\n                                                   int currentRow) const\n{\n    Q_ASSERT(editor != nullptr);\n\n    if (CSMWorld::ColumnBase::isId(display) &&\n        CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None)\n    {\n        int idColumn = mTable->findColumnIndex(CSMWorld::Columns::ColumnId_Id);\n        QString id = mTable->data(mTable->index(currentRow, idColumn)).toString();\n\n        IdContextMenu *menu = new IdContextMenu(editor, display);\n        // Current ID is already opened, so no need to create Edit 'ID' action for it\n        menu->excludeId(id.toUtf8().constData());\n        connect(menu,\n                SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)),\n                this,\n                SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)));\n    }\n}\n\nCSVWorld::EditWidget::~EditWidget()\n{\n    for (unsigned i = 0; i < mNestedModels.size(); ++i)\n        delete mNestedModels[i];\n\n    if (mDispatcher)\n        delete mDispatcher;\n\n    if (mNestedTableDispatcher)\n        delete mNestedTableDispatcher;\n}\n\nCSVWorld::EditWidget::EditWidget(QWidget *parent,\n        int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher,\n        CSMDoc::Document& document, bool createAndDelete) :\nQScrollArea(parent),\nmWidgetMapper(nullptr),\nmNestedTableMapper(nullptr),\nmDispatcher(nullptr),\nmNestedTableDispatcher(nullptr),\nmMainWidget(nullptr),\nmTable(table),\nmCommandDispatcher (commandDispatcher),\nmDocument (document)\n{\n    remake (row);\n}\n\nvoid CSVWorld::EditWidget::remake(int row)\n{\n    if (mMainWidget)\n    {\n        QWidget *del = this->takeWidget();\n        del->deleteLater();\n    }\n    mMainWidget = new QWidget (this);\n\n    for (unsigned i = 0; i < mNestedModels.size(); ++i)\n        delete mNestedModels[i];\n\n    mNestedModels.clear();\n\n    if (mDispatcher)\n        delete mDispatcher;\n    mDispatcher = new DialogueDelegateDispatcher(nullptr/*this*/, mTable, mCommandDispatcher, mDocument);\n\n    if (mNestedTableDispatcher)\n        delete mNestedTableDispatcher;\n\n    //not sure if widget mapper can handle deleting the widgets that were mapped\n    if (mWidgetMapper)\n        delete mWidgetMapper;\n\n    mWidgetMapper = new QDataWidgetMapper (this);\n    mWidgetMapper->setModel(mTable);\n    mWidgetMapper->setItemDelegate(mDispatcher);\n\n    if (mNestedTableMapper)\n        delete mNestedTableMapper;\n\n\n    QFrame* line = new QFrame(mMainWidget);\n    line->setObjectName(QString::fromUtf8(\"line\"));\n    line->setGeometry(QRect(320, 150, 118, 3));\n    line->setFrameShape(QFrame::HLine);\n    line->setFrameShadow(QFrame::Sunken);\n\n    QFrame* line2 = new QFrame(mMainWidget);\n    line2->setObjectName(QString::fromUtf8(\"line\"));\n    line2->setGeometry(QRect(320, 150, 118, 3));\n    line2->setFrameShape(QFrame::HLine);\n    line2->setFrameShadow(QFrame::Sunken);\n\n    QVBoxLayout *mainLayout = new QVBoxLayout(mMainWidget);\n    QGridLayout *lockedLayout = new QGridLayout();\n    QGridLayout *unlockedLayout = new QGridLayout();\n    QVBoxLayout *tablesLayout = new QVBoxLayout();\n\n    mainLayout->addLayout(lockedLayout, QSizePolicy::Fixed);\n    mainLayout->addWidget(line, 1);\n    mainLayout->addLayout(unlockedLayout, QSizePolicy::Preferred);\n    mainLayout->addWidget(line2, 1);\n    mainLayout->addLayout(tablesLayout, QSizePolicy::Preferred);\n    mainLayout->addStretch(1);\n\n    int unlocked = 0;\n    int locked = 0;\n    const int columns = mTable->columnCount();\n\n    for (int i=0; i<columns; ++i)\n    {\n        int flags = mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();\n\n        if (flags & CSMWorld::ColumnBase::Flag_Dialogue)\n        {\n            CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display>\n                (mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());\n\n            if (mTable->hasChildren(mTable->index(row, i)) &&\n                    !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List))\n            {\n                CSMWorld::IdTree* innerTable = &dynamic_cast<CSMWorld::IdTree&>(*mTable);\n                mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (mTable->index(row, i), display, innerTable));\n\n                int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n                int typeColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);\n\n                CSMWorld::UniversalId id = CSMWorld::UniversalId(\n                    static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (row, typeColumn)).toInt()),\n                    mTable->data (mTable->index (row, idColumn)).toString().toUtf8().constData());\n\n                bool editable = true;\n                bool fixedRows = false;\n                QVariant v = mTable->index(row, i).data();\n                if (v.canConvert<CSMWorld::ColumnBase::TableEditModes>())\n                {\n                    assert (QString(v.typeName()) == \"CSMWorld::ColumnBase::TableEditModes\");\n\n                    if (v.value<CSMWorld::ColumnBase::TableEditModes>() == CSMWorld::ColumnBase::TableEdit_None)\n                        editable = false;\n                    else if (v.value<CSMWorld::ColumnBase::TableEditModes>() == CSMWorld::ColumnBase::TableEdit_FixedRows)\n                        fixedRows = true;\n                }\n\n                // Create and display nested table only if it's editable.\n                if (editable)\n                {\n                    NestedTable* table =\n                        new NestedTable(mDocument, id, mNestedModels.back(), this, editable, fixedRows);\n                    table->resizeColumnsToContents();\n\n                    int rows = mTable->rowCount(mTable->index(row, i));\n                    int rowHeight = (rows == 0) ? table->horizontalHeader()->height() : table->rowHeight(0);\n                    int headerHeight = table->horizontalHeader()->height();\n                    int tableMaxHeight = (5 * rowHeight) + headerHeight + (2 * table->frameWidth());\n                    table->setMinimumHeight(tableMaxHeight);\n\n                    QString headerText = mTable->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString();\n                    QLabel* label = new QLabel (headerText, mMainWidget);\n                    label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);\n\n                    tablesLayout->addWidget(label);\n                    tablesLayout->addWidget(table);\n\n                    connect(table,\n                            SIGNAL(editRequest(const CSMWorld::UniversalId &, const std::string &)),\n                            this,\n                            SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)));\n                }\n            }\n            else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List))\n            {\n                mDispatcher->makeDelegate (display);\n                QWidget* editor = mDispatcher->makeEditor (display, (mTable->index (row, i)));\n\n                if (editor)\n                {\n                    mWidgetMapper->addMapping (editor, i);\n\n                    QLabel* label = new QLabel (mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget);\n\n                    label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);\n                    editor->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);\n\n                    if (! (mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable))\n                    {\n                        lockedLayout->addWidget (label, locked, 0);\n                        lockedLayout->addWidget (editor, locked, 1);\n                        ++locked;\n                    }\n                    else\n                    {\n                        unlockedLayout->addWidget (label, unlocked, 0);\n                        unlockedLayout->addWidget (editor, unlocked, 1);\n                        ++unlocked;\n                    }\n\n                    if(mTable->index(row, i).data().type() == QVariant::UserType)\n                    {\n                        editor->setEnabled(false);\n                        label->setEnabled(false);\n                    }\n\n                    createEditorContextMenu(editor, display, row);\n                }\n            }\n            else\n            {\n                CSMWorld::IdTree *tree = static_cast<CSMWorld::IdTree *>(mTable);\n                mNestedTableMapper = new QDataWidgetMapper (this);\n\n                mNestedTableMapper->setModel(tree);\n                // FIXME: lack MIME support?\n                mNestedTableDispatcher =\n                        new DialogueDelegateDispatcher (nullptr/*this*/, mTable, mCommandDispatcher, mDocument, tree);\n                mNestedTableMapper->setRootIndex (tree->index(row, i));\n                mNestedTableMapper->setItemDelegate(mNestedTableDispatcher);\n\n                int columnCount = tree->columnCount(tree->index(row, i));\n                for (int col = 0; col < columnCount; ++col)\n                {\n                    int displayRole = tree->nestedHeaderData (i, col,\n                            Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt();\n\n                    display = static_cast<CSMWorld::ColumnBase::Display> (displayRole);\n\n                   mNestedTableDispatcher->makeDelegate (display);\n\n                    // FIXME: assumed all columns are editable\n                    QWidget* editor =\n                        mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i)));\n                    if (editor)\n                    {\n                        mNestedTableMapper->addMapping (editor, col);\n\n                        // Need to use Qt::DisplayRole in order to get the  correct string\n                        // from CSMWorld::Columns\n                        QLabel* label = new QLabel (tree->nestedHeaderData (i, col,\n                                    Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget);\n\n                        label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);\n                        editor->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);\n\n                        unlockedLayout->addWidget (label, unlocked, 0);\n                        unlockedLayout->addWidget (editor, unlocked, 1);\n                        ++unlocked;\n\n                        if(tree->index(0, col, tree->index(row, i)).data().type() == QVariant::UserType)\n                        {\n                            editor->setEnabled(false);\n                            label->setEnabled(false);\n                        }\n\n                        createEditorContextMenu(editor, display, row);\n                    }\n                }\n                mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i)));\n            }\n        }\n    }\n\n    mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0));\n\n    if (unlocked == 0)\n        mainLayout->removeWidget(line);\n\n    this->setWidget(mMainWidget);\n    this->setWidgetResizable(true);\n}\n\n\nQVBoxLayout& CSVWorld::SimpleDialogueSubView::getMainLayout()\n{\n    return *mMainLayout;\n}\n\nCSMWorld::IdTable& CSVWorld::SimpleDialogueSubView::getTable()\n{\n    return *mTable;\n}\n\nCSMWorld::CommandDispatcher& CSVWorld::SimpleDialogueSubView::getCommandDispatcher()\n{\n    return mCommandDispatcher;\n}\n\nCSVWorld::EditWidget& CSVWorld::SimpleDialogueSubView::getEditWidget()\n{\n    return *mEditWidget;\n}\n\nbool CSVWorld::SimpleDialogueSubView::isLocked() const\n{\n    return mLocked;\n}\n\nCSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) :\n    SubView (id),\n    mEditWidget(nullptr),\n    mMainLayout(nullptr),\n    mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))),\n    mLocked(false),\n    mDocument(document),\n    mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType()))\n{\n    connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&)));\n    connect(mTable, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex&, int, int)));\n\n    updateCurrentId();\n\n    QWidget *mainWidget = new QWidget(this);\n\n    mMainLayout = new QVBoxLayout(mainWidget);\n    setWidget (mainWidget);\n\n    int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n\n    mEditWidget = new EditWidget(mainWidget,\n            mTable->getModelIndex(getUniversalId().getId(), idColumn).row(), mTable, mCommandDispatcher, document, false);\n\n    mMainLayout->addWidget(mEditWidget);\n    mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);\n\n    dataChanged(mTable->getModelIndex (getUniversalId().getId(), idColumn));\n\n    connect(mEditWidget,\n            SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)),\n            this,\n            SIGNAL(focusId(const CSMWorld::UniversalId &, const std::string &)));\n}\n\nvoid CSVWorld::SimpleDialogueSubView::setEditLock (bool locked)\n{\n    if (!mEditWidget) // hack to indicate that getUniversalId().getId() is no longer valid\n        return;\n\n    int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    mLocked = locked;\n    QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn));\n\n    if (currentIndex.isValid())\n    {\n        CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (currentIndex.row(), 1)).toInt());\n\n        mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || locked);\n\n        mCommandDispatcher.setEditLock (locked);\n    }\n\n}\n\nvoid CSVWorld::SimpleDialogueSubView::dataChanged (const QModelIndex & index)\n{\n    int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn));\n\n    if (currentIndex.isValid() &&\n            (index.parent().isValid() ? index.parent().row() : index.row()) == currentIndex.row())\n    {\n        CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (currentIndex.row(), 1)).toInt());\n\n        mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked);\n\n        // Check if the changed data should force refresh (rebuild) the dialogue subview\n        int flags = 0;\n        if (index.parent().isValid()) // TODO: check that index is topLeft\n        {\n            flags = static_cast<CSMWorld::IdTree *>(mTable)->nestedHeaderData (index.parent().column(),\n                    index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();\n        }\n        else\n        {\n            flags = mTable->headerData (index.column(),\n                    Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();\n        }\n\n        if (flags & CSMWorld::ColumnBase::Flag_Dialogue_Refresh)\n        {\n            int y = mEditWidget->verticalScrollBar()->value();\n            mEditWidget->remake (index.parent().isValid() ? index.parent().row() : index.row());\n            mEditWidget->verticalScrollBar()->setValue(y);\n        }\n    }\n}\n\nvoid CSVWorld::SimpleDialogueSubView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)\n{\n    int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn));\n\n    if (!currentIndex.isValid())\n    {\n        return;\n    }\n\n    if (currentIndex.parent() == parent && currentIndex.row() >= start && currentIndex.row() <= end)\n    {\n        if(mEditWidget)\n        {\n            delete mEditWidget;\n            mEditWidget = nullptr;\n        }\n        emit closeRequest(this);\n    }\n}\n\nvoid CSVWorld::SimpleDialogueSubView::updateCurrentId()\n{\n    std::vector<std::string> selection;\n    selection.push_back (getUniversalId().getId());\n    mCommandDispatcher.setSelection(selection);\n}\n\n\nvoid CSVWorld::DialogueSubView::addButtonBar()\n{\n    if (mButtons)\n        return;\n\n    mButtons = new RecordButtonBar (getUniversalId(), getTable(), mBottom,\n        &getCommandDispatcher(), this);\n\n    getMainLayout().insertWidget (1, mButtons);\n\n    // connections\n    connect (mButtons, SIGNAL (showPreview()), this, SLOT (showPreview()));\n    connect (mButtons, SIGNAL (viewRecord()), this, SLOT (viewRecord()));\n    connect (mButtons, SIGNAL (switchToRow (int)), this, SLOT (switchToRow (int)));\n\n    connect (this, SIGNAL (universalIdChanged (const CSMWorld::UniversalId&)),\n        mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&)));\n}\n\nCSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id,\n    CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting)\n: SimpleDialogueSubView (id, document), mButtons (nullptr)\n{\n    // bottom box\n    mBottom = new TableBottomBox (creatorFactory, document, id, this);\n\n    connect (mBottom, SIGNAL (requestFocus (const std::string&)),\n        this, SLOT (requestFocus (const std::string&)));\n\n    // layout\n    getMainLayout().addWidget (mBottom);\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n    CSMPrefs::get()[\"ID Dialogues\"].update();\n}\n\nvoid CSVWorld::DialogueSubView::setEditLock (bool locked)\n{\n    SimpleDialogueSubView::setEditLock (locked);\n\n    if (mButtons)\n        mButtons->setEditLock (locked);\n}\n\nvoid CSVWorld::DialogueSubView::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==\"ID Dialogues/toolbar\")\n    {\n        if (setting->isTrue())\n        {\n            addButtonBar();\n        }\n        else if (mButtons)\n        {\n            getMainLayout().removeWidget (mButtons);\n            delete mButtons;\n            mButtons = nullptr;\n        }\n    }\n}\n\nvoid CSVWorld::DialogueSubView::showPreview ()\n{\n    int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    QModelIndex currentIndex (getTable().getModelIndex (getUniversalId().getId(), idColumn));\n\n    if (currentIndex.isValid() &&\n        getTable().getFeatures() & CSMWorld::IdTable::Feature_Preview &&\n        currentIndex.row() < getTable().rowCount())\n    {\n        emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, getUniversalId().getId()), \"\");\n    }\n}\n\nvoid CSVWorld::DialogueSubView::viewRecord ()\n{\n    int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    QModelIndex currentIndex (getTable().getModelIndex (getUniversalId().getId(), idColumn));\n\n    if (currentIndex.isValid() &&\n        currentIndex.row() < getTable().rowCount())\n    {\n        std::pair<CSMWorld::UniversalId, std::string> params = getTable().view (currentIndex.row());\n\n        if (params.first.getType()!=CSMWorld::UniversalId::Type_None)\n            emit focusId (params.first, params.second);\n    }\n}\n\nvoid CSVWorld::DialogueSubView::switchToRow (int row)\n{\n    int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    std::string id = getTable().data (getTable().index (row, idColumn)).toString().toUtf8().constData();\n\n    int typeColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);\n    CSMWorld::UniversalId::Type type = static_cast<CSMWorld::UniversalId::Type> (\n        getTable().data (getTable().index (row, typeColumn)).toInt());\n\n    setUniversalId (CSMWorld::UniversalId (type, id));\n    updateCurrentId();\n\n    getEditWidget().remake (row);\n\n    int stateColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Modification);\n    CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State> (\n        getTable().data (getTable().index (row, stateColumn)).toInt());\n\n    getEditWidget().setDisabled (isLocked() || state==CSMWorld::RecordBase::State_Deleted);\n}\n\nvoid CSVWorld::DialogueSubView::requestFocus (const std::string& id)\n{\n    int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    QModelIndex index = getTable().getModelIndex (id, idColumn);\n\n    if (index.isValid())\n        switchToRow (index.row());\n}\n"
  },
  {
    "path": "apps/opencs/view/world/dialoguesubview.hpp",
    "content": "#ifndef CSV_WORLD_DIALOGUESUBVIEW_H\n#define CSV_WORLD_DIALOGUESUBVIEW_H\n\n#include <set>\n#include <map>\n#include <memory>\n\n#include <QAbstractItemDelegate>\n#include <QScrollArea>\n\n#ifndef Q_MOC_RUN\n#include \"../doc/subview.hpp\"\n\n#include \"../../model/world/columnbase.hpp\"\n#include \"../../model/world/commanddispatcher.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#endif\n\nclass QDataWidgetMapper;\nclass QSize;\nclass QEvent;\nclass QLabel;\nclass QVBoxLayout;\nclass QMenu;\n\nnamespace CSMWorld\n{\n    class IdTable;\n    class NestedTableProxyModel;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVWorld\n{\n    class CommandDelegate;\n    class CreatorFactoryBase;\n    class TableBottomBox;\n\n    class NotEditableSubDelegate : public QAbstractItemDelegate\n    {\n        const CSMWorld::IdTable* mTable;\n    public:\n        NotEditableSubDelegate(const CSMWorld::IdTable* table,\n                               QObject * parent = nullptr);\n\n        void setEditorData (QWidget* editor, const QModelIndex& index) const override;\n\n        void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;\n\n        void paint (QPainter* painter,\n                            const QStyleOptionViewItem& option,\n                            const QModelIndex& index) const override;\n        ///< does nothing\n\n        QSize sizeHint (const QStyleOptionViewItem& option,\n                                const QModelIndex& index) const override;\n        ///< does nothing\n\n        QWidget *createEditor (QWidget *parent,\n                                const QStyleOptionViewItem& option,\n                                const QModelIndex& index) const override;\n    };\n\n    //this can't be nested into the DialogueDelegateDispatcher, because it needs to emit signals\n    class DialogueDelegateDispatcherProxy : public QObject\n    {\n        Q_OBJECT\n        class refWrapper\n        {\n        public:\n            refWrapper(const QModelIndex& index);\n\n            const QModelIndex& mIndex;\n        };\n\n        QWidget* mEditor;\n\n        CSMWorld::ColumnBase::Display mDisplay;\n\n        std::unique_ptr<refWrapper> mIndexWrapper;\n\n    public:\n        DialogueDelegateDispatcherProxy(QWidget* editor,\n                                        CSMWorld::ColumnBase::Display display);\n        QWidget* getEditor() const;\n\n    public slots:\n        void editorDataCommited();\n        void setIndex(const QModelIndex& index);\n\n    signals:\n        void editorDataCommited(QWidget* editor,\n                                const QModelIndex& index,\n                                CSMWorld::ColumnBase::Display display);\n\n    };\n\n    class DialogueDelegateDispatcher : public QAbstractItemDelegate\n    {\n        Q_OBJECT\n        std::map<int, CommandDelegate*> mDelegates;\n\n        QObject* mParent;\n\n        QAbstractItemModel* mTable;\n\n        CSMWorld::CommandDispatcher& mCommandDispatcher;\n        CSMDoc::Document& mDocument;\n\n        NotEditableSubDelegate mNotEditableDelegate;\n\n        std::vector<DialogueDelegateDispatcherProxy*> mProxys;\n        //once we move to the C++11 we should use unique_ptr\n\n    public:\n        DialogueDelegateDispatcher(QObject* parent,\n                                   CSMWorld::IdTable* table,\n                                   CSMWorld::CommandDispatcher& commandDispatcher,\n                                   CSMDoc::Document& document,\n                                   QAbstractItemModel* model = nullptr);\n\n        ~DialogueDelegateDispatcher();\n\n        CSVWorld::CommandDelegate* makeDelegate(CSMWorld::ColumnBase::Display display);\n\n        QWidget* makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index);\n        ///< will return null if delegate is not present, parent of the widget is\n        //same as for dispatcher itself\n\n        void setEditorData (QWidget* editor, const QModelIndex& index) const override;\n\n        void setModelData (QWidget* editor, QAbstractItemModel* model,\n                                   const QModelIndex& index) const override;\n\n        virtual void setModelData (QWidget* editor,\n                                   QAbstractItemModel* model, const QModelIndex& index,\n                                   CSMWorld::ColumnBase::Display display) const;\n\n        void paint (QPainter* painter,\n                            const QStyleOptionViewItem& option,\n                            const QModelIndex& index) const override;\n        ///< does nothing\n\n        QSize sizeHint (const QStyleOptionViewItem& option,\n                                const QModelIndex& index) const override;\n        ///< does nothing\n\n    private slots:\n        void editorDataCommited(QWidget* editor, const QModelIndex& index,\n                                CSMWorld::ColumnBase::Display display);\n    };\n\n    /// A context menu with \"Edit 'ID'\" action for editors in the dialogue subview\n    class IdContextMenu : public QObject\n    {\n            Q_OBJECT\n\n            QWidget *mWidget;\n            CSMWorld::UniversalId::Type mIdType;\n            std::set<std::string> mExcludedIds;\n            ///< A list of IDs that should not have the Edit 'ID' action.\n\n            QMenu *mContextMenu;\n            QAction *mEditIdAction;\n\n            QString getWidgetValue() const;\n            void addEditIdActionToMenu(const QString &text);\n            void removeEditIdActionFromMenu();\n\n        public:\n            IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Display display);\n\n            void excludeId(const std::string &id);\n\n        private slots:\n            void showContextMenu(const QPoint &pos);\n            void editIdRequest();\n\n        signals:\n            void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint);\n    };\n\n    class EditWidget : public QScrollArea\n    {\n        Q_OBJECT\n            QDataWidgetMapper *mWidgetMapper;\n            QDataWidgetMapper *mNestedTableMapper;\n            DialogueDelegateDispatcher *mDispatcher;\n            DialogueDelegateDispatcher *mNestedTableDispatcher;\n            QWidget* mMainWidget;\n            CSMWorld::IdTable* mTable;\n            CSMWorld::CommandDispatcher& mCommandDispatcher;\n            CSMDoc::Document& mDocument;\n            std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor\n\n            void createEditorContextMenu(QWidget *editor,\n                                         CSMWorld::ColumnBase::Display display,\n                                         int currentRow) const;\n        public:\n\n            EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table,\n                        CSMWorld::CommandDispatcher& commandDispatcher,\n                        CSMDoc::Document& document, bool createAndDelete = false);\n\n            virtual ~EditWidget();\n\n            void remake(int row);\n\n        signals:\n            void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint);\n    };\n\n    class SimpleDialogueSubView : public CSVDoc::SubView\n    {\n            Q_OBJECT\n\n            EditWidget* mEditWidget;\n            QVBoxLayout* mMainLayout;\n            CSMWorld::IdTable* mTable;\n            bool mLocked;\n            const CSMDoc::Document& mDocument;\n            CSMWorld::CommandDispatcher mCommandDispatcher;\n\n        protected:\n\n            QVBoxLayout& getMainLayout();\n\n            CSMWorld::IdTable& getTable();\n\n            CSMWorld::CommandDispatcher& getCommandDispatcher();\n\n            EditWidget& getEditWidget();\n\n            void updateCurrentId();\n\n            bool isLocked() const;\n\n        public:\n\n            SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);\n\n            void setEditLock (bool locked) override;\n\n        private slots:\n\n            void dataChanged(const QModelIndex & index);\n            ///\\brief we need to care for deleting currently edited record\n\n            void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);\n    };\n\n    class RecordButtonBar;\n\n    class DialogueSubView : public SimpleDialogueSubView\n    {\n            Q_OBJECT\n\n            TableBottomBox* mBottom;\n            RecordButtonBar *mButtons;\n\n        private:\n\n            void addButtonBar();\n\n        public:\n\n            DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,\n                const CreatorFactoryBase& creatorFactory, bool sorting = false);\n\n            void setEditLock (bool locked) override;\n\n        private slots:\n\n            void settingChanged (const CSMPrefs::Setting *setting);\n\n            void showPreview();\n\n            void viewRecord();\n\n            void switchToRow (int row);\n\n            void requestFocus (const std::string& id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/dragdroputils.cpp",
    "content": "#include \"dragdroputils.hpp\"\n\n#include <QDropEvent>\n\n#include \"../../model/world/tablemimedata.hpp\"\n\nconst CSMWorld::TableMimeData *CSVWorld::DragDropUtils::getTableMimeData(const QDropEvent &event)\n{\n    return dynamic_cast<const CSMWorld::TableMimeData *>(event.mimeData());\n}\n\nbool CSVWorld::DragDropUtils::canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type)\n{\n    const CSMWorld::TableMimeData *data = getTableMimeData(event);\n    return data != nullptr && data->holdsType(type);\n}\n\nbool CSVWorld::DragDropUtils::isInfo(const QDropEvent &event, CSMWorld::ColumnBase::Display type)\n{\n    const CSMWorld::TableMimeData *data = getTableMimeData(event);\n    return data != nullptr && (\n        data->holdsType(CSMWorld::UniversalId::Type_TopicInfo) ||\n        data->holdsType(CSMWorld::UniversalId::Type_JournalInfo) );\n}\n\nCSMWorld::UniversalId CSVWorld::DragDropUtils::getAcceptedData(const QDropEvent &event,\n                                                               CSMWorld::ColumnBase::Display type)\n{\n    if (canAcceptData(event, type))\n    {\n        if (const CSMWorld::TableMimeData *data = getTableMimeData(event))\n            return data->returnMatching(type);\n    }\n    return CSMWorld::UniversalId::Type_None;\n}\n"
  },
  {
    "path": "apps/opencs/view/world/dragdroputils.hpp",
    "content": "#ifndef CSV_WORLD_DRAGDROPUTILS_HPP\n#define CSV_WORLD_DRAGDROPUTILS_HPP\n\n#include \"../../model/world/columnbase.hpp\"\n\nclass QDropEvent;\n\nnamespace CSMWorld\n{\n    class TableMimeData;\n    class UniversalId;\n}\n\nnamespace CSVWorld\n{\n    namespace DragDropUtils\n    {\n        const CSMWorld::TableMimeData *getTableMimeData(const QDropEvent &event);\n\n        bool canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type);\n        ///< Checks whether the \\a event contains a valid CSMWorld::TableMimeData that holds the \\a type\n\n        bool isInfo(const QDropEvent &event, CSMWorld::ColumnBase::Display type);\n        ///< Info types can be dragged to sort the info table\n\n        CSMWorld::UniversalId getAcceptedData(const QDropEvent &event, CSMWorld::ColumnBase::Display type);\n        ///< Gets the accepted data from the \\a event using the \\a type\n        ///< \\return Type_None if the \\a event data doesn't holds the \\a type\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/dragrecordtable.cpp",
    "content": "#include \"dragrecordtable.hpp\"\n\n#include <QDrag>\n#include <QDragEnterEvent>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/tablemimedata.hpp\"\n#include \"../../model/world/commands.hpp\"\n\n#include \"dragdroputils.hpp\"\n\nvoid CSVWorld::DragRecordTable::startDragFromTable (const CSVWorld::DragRecordTable& table)\n{\n    std::vector<CSMWorld::UniversalId> records = table.getDraggedRecords();\n    if (records.empty())\n    {\n        return;\n    }\n\n    CSMWorld::TableMimeData* mime = new CSMWorld::TableMimeData (records, mDocument);\n    QDrag* drag = new QDrag (this);\n    drag->setMimeData (mime);\n    drag->setPixmap (QString::fromUtf8 (mime->getIcon().c_str()));\n    drag->exec (Qt::CopyAction);\n}\n\nCSVWorld::DragRecordTable::DragRecordTable (CSMDoc::Document& document, QWidget* parent) :\nQTableView(parent),\nmDocument(document),\nmEditLock(false)\n{\n    setAcceptDrops(true);\n}\n\nvoid CSVWorld::DragRecordTable::setEditLock (bool locked)\n{\n    mEditLock = locked;\n}\n\nvoid CSVWorld::DragRecordTable::dragEnterEvent(QDragEnterEvent *event)\n{\n    event->acceptProposedAction();\n}\n\nvoid CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent *event)\n{\n    QModelIndex index = indexAt(event->pos());\n    if (CSVWorld::DragDropUtils::canAcceptData(*event, getIndexDisplayType(index)) ||\n        CSVWorld::DragDropUtils::isInfo(*event, getIndexDisplayType(index)) )\n    {\n        if (index.flags() & Qt::ItemIsEditable)\n        {\n            event->accept();\n            return;\n        }\n    }\n    event->ignore();\n}\n\nvoid CSVWorld::DragRecordTable::dropEvent(QDropEvent *event)\n{\n    QModelIndex index = indexAt(event->pos());\n    CSMWorld::ColumnBase::Display display = getIndexDisplayType(index);\n    if (CSVWorld::DragDropUtils::canAcceptData(*event, display))\n    {\n        const CSMWorld::TableMimeData *tableMimeData = CSVWorld::DragDropUtils::getTableMimeData(*event);\n        if (tableMimeData->fromDocument(mDocument))\n        {\n            CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, display);\n            QVariant newIndexData = QString::fromUtf8(id.getId().c_str());\n            QVariant oldIndexData = index.data(Qt::EditRole);\n            if (newIndexData != oldIndexData)\n            {\n                mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*model(), index, newIndexData));\n            }\n        }\n    }\n    else if (CSVWorld::DragDropUtils::isInfo(*event, display) && event->source() == this)\n    {\n        emit moveRecordsFromSameTable(event);\n    }\n}\n\nCSMWorld::ColumnBase::Display CSVWorld::DragRecordTable::getIndexDisplayType(const QModelIndex &index) const\n{\n    Q_ASSERT(model() != nullptr);\n\n    if (index.isValid())\n    {\n        QVariant display = model()->headerData(index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display);\n        if (display.isValid())\n        {\n            return static_cast<CSMWorld::ColumnBase::Display>(display.toInt());\n        }\n    }\n    return CSMWorld::ColumnBase::Display_None;\n}\n"
  },
  {
    "path": "apps/opencs/view/world/dragrecordtable.hpp",
    "content": "#ifndef CSV_WORLD_DRAGRECORDTABLE_H\n#define CSV_WORLD_DRAGRECORDTABLE_H\n\n#include <QTableView>\n#include <QEvent>\n\n#include \"../../model/world/columnbase.hpp\"\n\nclass QWidget;\nclass QAction;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class UniversalId;\n}\n\nnamespace CSVWorld\n{\n    class DragRecordTable : public QTableView\n    {\n        Q_OBJECT\n\n        protected:\n            CSMDoc::Document& mDocument;\n            bool mEditLock;\n\n        public:\n            DragRecordTable(CSMDoc::Document& document, QWidget* parent = nullptr);\n\n            virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const = 0;\n\n            void setEditLock(bool locked);\n\n        protected:\n            void startDragFromTable(const DragRecordTable& table);\n\n            void dragEnterEvent(QDragEnterEvent *event) override;\n\n            void dragMoveEvent(QDragMoveEvent *event) override;\n\n            void dropEvent(QDropEvent *event) override;\n\n        private:\n            CSMWorld::ColumnBase::Display getIndexDisplayType(const QModelIndex &index) const;\n\n        signals:\n            void moveRecordsFromSameTable(QDropEvent *event);\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "apps/opencs/view/world/enumdelegate.cpp",
    "content": "#include \"enumdelegate.hpp\"\n\n#include <cassert>\n#include <stdexcept>\n\n#include <QComboBox>\n#include <QApplication>\n#include <QUndoStack>\n\n#include \"../../model/world/commands.hpp\"\n\nint CSVWorld::EnumDelegate::getValueIndex(const QModelIndex &index, int role) const\n{\n    if (index.isValid() && index.data(role).isValid())\n    {\n        int value = index.data(role).toInt();\n\n        int size = static_cast<int>(mValues.size());\n        for (int i = 0; i < size; ++i)\n        {\n            if (value == mValues.at(i).first)\n            {\n                return i;\n            }\n        }\n    }\n    return -1;\n}\n\nvoid CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model,\n    const QModelIndex& index) const\n{\n    if (QComboBox *comboBox = dynamic_cast<QComboBox *> (editor))\n    {\n        QString value = comboBox->currentText();\n\n        for (std::vector<std::pair<int, QString> >::const_iterator iter (mValues.begin());\n            iter!=mValues.end(); ++iter)\n            if (iter->second==value)\n            {\n                // do nothing if the value has not changed\n                if (model->data(index).toInt() != iter->first)\n                    addCommands (model, index, iter->first);\n                break;\n            }\n    }\n}\n\nvoid CSVWorld::EnumDelegate::addCommands (QAbstractItemModel *model,\n    const QModelIndex& index, int type) const\n{\n    getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, type));\n}\n\n\nCSVWorld::EnumDelegate::EnumDelegate (const std::vector<std::pair<int, QString> >& values,\n    CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)\n: CommandDelegate (dispatcher, document, parent), mValues (values)\n{\n\n}\n\nQWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent,\n                                              const QStyleOptionViewItem& option,\n                                              const QModelIndex& index) const\n{\n    return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_None);\n}\n\nQWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option,\n    const QModelIndex& index, CSMWorld::ColumnBase::Display display) const\n{\n    if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid())\n        return nullptr;\n\n    QComboBox *comboBox = new QComboBox (parent);\n\n    for (std::vector<std::pair<int, QString> >::const_iterator iter (mValues.begin());\n         iter!=mValues.end(); ++iter)\n         comboBox->addItem (iter->second);\n\n    return comboBox;\n}\n\nvoid CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const\n{\n    if (QComboBox *comboBox = dynamic_cast<QComboBox *>(editor))\n    {\n        int role = Qt::EditRole;\n        if (tryDisplay && !index.data(role).isValid())\n        {\n            role = Qt::DisplayRole;\n            if (!index.data(role).isValid())\n            {\n                return;\n            }\n        }\n\n        int valueIndex = getValueIndex(index, role);\n        if (valueIndex != -1)\n        {\n            comboBox->setCurrentIndex(valueIndex);\n        }\n    }\n}\n\nvoid CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewItem& option,\n    const QModelIndex& index) const\n{\n    int valueIndex = getValueIndex(index);\n    if (valueIndex != -1)\n    {\n        QStyleOptionViewItem itemOption(option);\n        itemOption.text = mValues.at(valueIndex).second;\n        QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter);\n    }\n}\n\nQSize CSVWorld::EnumDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const\n{\n    int valueIndex = getValueIndex(index);\n    if (valueIndex != -1)\n    {\n        // Calculate the size hint as for a combobox.\n        // So, the whole text is visible (isn't elided) when the editor is created\n        QStyleOptionComboBox itemOption;\n        itemOption.fontMetrics = option.fontMetrics;\n        itemOption.palette = option.palette;\n        itemOption.rect = option.rect;\n        itemOption.state = option.state;\n\n        const QString &valueText = mValues.at(valueIndex).second;\n        QSize valueSize = QSize(itemOption.fontMetrics.horizontalAdvance(valueText), itemOption.fontMetrics.height());\n        itemOption.currentText = valueText;\n        return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize);\n    }\n    return option.rect.size();\n}\n\nCSVWorld::EnumDelegateFactory::EnumDelegateFactory() {}\n\nCSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names, bool allowNone)\n{\n    assert (names);\n\n    if (allowNone)\n        add (-1, \"\");\n\n    for (int i=0; names[i]; ++i)\n        add (i, names[i]);\n}\n\nCSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::pair<int,std::string>>& names,\n    bool allowNone)\n{\n    if (allowNone)\n        add (-1, \"\");\n\n    int size = static_cast<int> (names.size());\n\n    for (int i=0; i<size; ++i)\n        add (names[i].first, names[i].second.c_str());\n}\n\nCSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (\n    CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const\n{\n    return new EnumDelegate (mValues, dispatcher, document, parent);\n}\n\nvoid CSVWorld::EnumDelegateFactory::add (int value, const QString& name)\n{\n    auto pair = std::make_pair (value, name);\n\n    for (auto it=mValues.begin(); it!=mValues.end(); ++it)\n    {\n        if (it->second > name)\n        {\n            mValues.insert(it, pair);\n            return;\n        }\n    }\n\n    mValues.emplace_back (value, name);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/enumdelegate.hpp",
    "content": "#ifndef CSV_WORLD_ENUMDELEGATE_H\n#define CSV_WORLD_ENUMDELEGATE_H\n\n#include <vector>\n\n#include <QString>\n#include <QStyledItemDelegate>\n\n#include <components/esm/defs.hpp>\n\n#include \"util.hpp\"\n\nnamespace CSVWorld\n{\n    /// \\brief Integer value that represents an enum and is interacted with via a combobox\n    class EnumDelegate : public CommandDelegate\n    {\n        protected:\n\n            std::vector<std::pair<int, QString> > mValues;\n\n            int getValueIndex(const QModelIndex &index, int role = Qt::DisplayRole) const;\n\n        private:\n\n            void setModelDataImp (QWidget *editor, QAbstractItemModel *model,\n                const QModelIndex& index) const override;\n\n            virtual void addCommands (QAbstractItemModel *model,\n                const QModelIndex& index, int type) const;\n\n        public:\n\n            EnumDelegate (const std::vector<std::pair<int, QString> >& values,\n                CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent);\n\n            QWidget *createEditor(QWidget *parent,\n                                          const QStyleOptionViewItem& option,\n                                          const QModelIndex& index) const override;\n\n            QWidget *createEditor(QWidget *parent,\n                                          const QStyleOptionViewItem& option,\n                                          const QModelIndex& index,\n                                          CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const override;\n\n            void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const override;\n\n            void paint (QPainter *painter, const QStyleOptionViewItem& option,\n                const QModelIndex& index) const override;\n\n            QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;\n\n    };\n\n    class EnumDelegateFactory : public CommandDelegateFactory\n    {\n        protected:\n            std::vector<std::pair<int, QString> > mValues;\n\n        public:\n\n            EnumDelegateFactory();\n\n            EnumDelegateFactory (const char **names, bool allowNone = false);\n            ///< \\param names Array of char pointer with a 0-pointer as end mark\n            /// \\param allowNone Use value of -1 for \"none selected\" (empty string)\n\n            EnumDelegateFactory (const std::vector<std::pair<int,std::string>>& names, bool allowNone = false);\n            /// \\param allowNone Use value of -1 for \"none selected\" (empty string)\n\n            CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const override;\n            ///< The ownership of the returned CommandDelegate is transferred to the caller.\n\n            void add (int value, const QString& name);\n    };\n\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/extendedcommandconfigurator.cpp",
    "content": "#include \"extendedcommandconfigurator.hpp\"\n\n#include <algorithm>\n\n#include <QPushButton>\n#include <QGroupBox>\n#include <QCheckBox>\n#include <QLabel>\n#include <QLayout>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/commanddispatcher.hpp\"\n#include \"../../model/world/data.hpp\"\n\nCSVWorld::ExtendedCommandConfigurator::ExtendedCommandConfigurator(CSMDoc::Document &document,\n                                                                   const CSMWorld::UniversalId &id,\n                                                                   QWidget *parent)\n    : QWidget(parent),\n      mNumUsedCheckBoxes(0),\n      mNumChecked(0),\n      mMode(Mode_None),\n      mData(document.getData()),\n      mEditLock(false)\n{\n    mCommandDispatcher = new CSMWorld::CommandDispatcher(document, id, this);\n\n    connect(&mData, SIGNAL(idListChanged()), this, SLOT(dataIdListChanged()));\n\n    mPerformButton = new QPushButton(this);\n    mPerformButton->setDefault(true);\n    mPerformButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);\n    connect(mPerformButton, SIGNAL(clicked(bool)), this, SLOT(performExtendedCommand()));\n\n    mCancelButton = new QPushButton(\"Cancel\", this);\n    mCancelButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);\n    connect(mCancelButton, SIGNAL(clicked(bool)), this, SIGNAL(done()));\n\n    mTypeGroup = new QGroupBox(this);\n\n    QGridLayout *groupLayout = new QGridLayout(mTypeGroup);\n    groupLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);\n    mTypeGroup->setLayout(groupLayout);\n\n    QHBoxLayout *mainLayout = new QHBoxLayout(this);\n    mainLayout->setSizeConstraint(QLayout::SetNoConstraint);\n    mainLayout->setContentsMargins(0, 0, 0, 0);\n    mainLayout->addWidget(mTypeGroup);\n    mainLayout->addWidget(mPerformButton);\n    mainLayout->addWidget(mCancelButton);\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::configure(CSVWorld::ExtendedCommandConfigurator::Mode mode,\n                                                      const std::vector<std::string> &selectedIds)\n{\n    mMode = mode;\n    if (mMode != Mode_None)\n    {\n        mPerformButton->setText((mMode == Mode_Delete) ? \"Extended Delete\" : \"Extended Revert\");\n        mSelectedIds = selectedIds;\n        mCommandDispatcher->setSelection(mSelectedIds);\n\n        setupCheckBoxes(mCommandDispatcher->getExtendedTypes());\n        setupGroupLayout();\n        lockWidgets(mEditLock);\n    }\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::setEditLock(bool locked)\n{\n    if (mEditLock != locked)\n    {\n        mEditLock = locked;\n        lockWidgets(mEditLock);\n    }\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::resizeEvent(QResizeEvent *event)\n{\n    QWidget::resizeEvent(event);\n    setupGroupLayout();\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::setupGroupLayout()\n{\n    if (mMode == Mode_None)\n    {\n        return;\n    }\n\n    int groupWidth = mTypeGroup->geometry().width();\n    QGridLayout *layout = qobject_cast<QGridLayout *>(mTypeGroup->layout());\n\n    // Find the optimal number of rows to place the checkboxes within the available space\n    int divider = 1;\n    do\n    {\n        while (layout->itemAt(0) != nullptr)\n        {\n            layout->removeItem(layout->itemAt(0));\n        }\n\n        int counter = 0;\n        int itemsPerRow = mNumUsedCheckBoxes / divider;\n        CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();\n        CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();\n        for (; current != end; ++current)\n        {\n            if (counter < mNumUsedCheckBoxes)\n            {\n                int row = counter / itemsPerRow;\n                int column = counter - (counter / itemsPerRow) * itemsPerRow;\n                layout->addWidget(current->first, row, column);\n            }\n            ++counter;\n        }\n        divider *= 2;\n    }\n    while (groupWidth < mTypeGroup->sizeHint().width() && divider <= mNumUsedCheckBoxes);\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::setupCheckBoxes(const std::vector<CSMWorld::UniversalId> &types)\n{\n    // Make sure that we have enough checkboxes\n    int numTypes =  static_cast<int>(types.size());\n    int numCheckBoxes = static_cast<int>(mTypeCheckBoxes.size());\n    if (numTypes > numCheckBoxes)\n    {\n        for (int i = numTypes - numCheckBoxes; i > 0; --i)\n        {\n            QCheckBox *checkBox = new QCheckBox(mTypeGroup);\n            connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxStateChanged(int)));\n            mTypeCheckBoxes.insert(std::make_pair(checkBox, CSMWorld::UniversalId::Type_None));\n        }\n    }\n\n    // Set up the checkboxes\n    int counter = 0;\n    CheckBoxMap::iterator current = mTypeCheckBoxes.begin();\n    CheckBoxMap::iterator end = mTypeCheckBoxes.end();\n    for (; current != end; ++current)\n    {\n        if (counter < numTypes)\n        {\n            CSMWorld::UniversalId type = types[counter];\n            current->first->setText(QString::fromUtf8(type.getTypeName().c_str()));\n            current->first->setChecked(true);\n            current->second = type;\n            ++counter;\n        }\n        else\n        {\n            current->first->hide();\n        }\n    }\n    mNumChecked = mNumUsedCheckBoxes = numTypes;\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::lockWidgets(bool locked)\n{\n    mPerformButton->setEnabled(!mEditLock && mNumChecked > 0);\n\n    CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();\n    CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();\n    for (int i = 0; current != end && i < mNumUsedCheckBoxes; ++current, ++i)\n    {\n        current->first->setEnabled(!mEditLock);\n    }\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::performExtendedCommand()\n{\n    std::vector<CSMWorld::UniversalId> types;\n    \n    CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();\n    CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();\n    for (; current != end; ++current)\n    {\n        if (current->first->isChecked())\n        {\n            types.push_back(current->second);\n        }\n    }\n\n    mCommandDispatcher->setExtendedTypes(types);\n    if (mMode == Mode_Delete)\n    {\n        mCommandDispatcher->executeExtendedDelete();\n    }\n    else\n    {\n        mCommandDispatcher->executeExtendedRevert();\n    }\n    emit done();\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::checkBoxStateChanged(int state)\n{\n    switch (state)\n    {\n        case Qt::Unchecked:\n            --mNumChecked;\n            break;\n        case Qt::Checked:\n            ++mNumChecked;\n            break;\n        case Qt::PartiallyChecked: // Not used\n            break;\n    }\n\n    mPerformButton->setEnabled(mNumChecked > 0);\n}\n\nvoid CSVWorld::ExtendedCommandConfigurator::dataIdListChanged()\n{\n    bool idsRemoved = false;\n    for (int i = 0; i < static_cast<int>(mSelectedIds.size()); ++i)\n    {\n        if (!mData.hasId(mSelectedIds[i]))\n        {\n            std::swap(mSelectedIds[i], mSelectedIds.back());\n            mSelectedIds.pop_back();\n            idsRemoved = true;\n            --i;\n        }\n    }\n\n    // If all selected IDs were removed, cancel the configurator\n    if (mSelectedIds.empty())\n    {\n        emit done();\n        return;\n    }\n\n    if (idsRemoved)\n    {\n        mCommandDispatcher->setSelection(mSelectedIds);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/extendedcommandconfigurator.hpp",
    "content": "#ifndef CSVWORLD_EXTENDEDCOMMANDCONFIGURATOR_HPP\n#define CSVWORLD_EXTENDEDCOMMANDCONFIGURATOR_HPP\n\n#include <map>\n\n#include <QWidget>\n\n#include \"../../model/world/universalid.hpp\"\n\nclass QPushButton;\nclass QGroupBox;\nclass QCheckBox;\nclass QLabel;\nclass QHBoxLayout;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class CommandDispatcher;\n    class Data;\n}\n\nnamespace CSVWorld\n{\n    class ExtendedCommandConfigurator : public QWidget\n    {\n            Q_OBJECT\n\n        public:\n            enum Mode { Mode_None, Mode_Delete, Mode_Revert };\n        \n        private:\n            typedef std::map<QCheckBox *, CSMWorld::UniversalId> CheckBoxMap;\n\n            QPushButton *mPerformButton;\n            QPushButton *mCancelButton;\n            QGroupBox *mTypeGroup;\n            CheckBoxMap mTypeCheckBoxes;\n            int mNumUsedCheckBoxes;\n            int mNumChecked;\n\n            Mode mMode;\n            CSMWorld::CommandDispatcher *mCommandDispatcher;\n            CSMWorld::Data &mData;\n            std::vector<std::string> mSelectedIds;\n            \n            bool mEditLock;\n\n            void setupGroupLayout();\n            void setupCheckBoxes(const std::vector<CSMWorld::UniversalId> &types);\n            void lockWidgets(bool locked);\n\n        public:\n            ExtendedCommandConfigurator(CSMDoc::Document &document,\n                                        const CSMWorld::UniversalId &id,\n                                        QWidget *parent = nullptr);\n\n            void configure(Mode mode, const std::vector<std::string> &selectedIds);\n            void setEditLock(bool locked);\n\n        protected:\n            void resizeEvent(QResizeEvent *event) override;\n\n        private slots:\n            void performExtendedCommand();\n            void checkBoxStateChanged(int state);\n            void dataIdListChanged();\n\n        signals:\n            void done();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/genericcreator.cpp",
    "content": "#include \"genericcreator.hpp\"\n\n#include <memory>\n\n#include <QHBoxLayout>\n#include <QPushButton>\n#include <QLineEdit>\n#include <QUndoStack>\n#include <QLabel>\n#include <QComboBox>\n\n#include <components/misc/stringops.hpp>\n\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idtable.hpp\"\n\n#include \"idvalidator.hpp\"\n\nvoid CSVWorld::GenericCreator::update()\n{\n    mErrors = getErrors();\n\n    mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str()));\n    mId->setToolTip (QString::fromUtf8 (mErrors.c_str()));\n\n    mCreate->setEnabled (mErrors.empty() && !mLocked);\n}\n\nvoid CSVWorld::GenericCreator::setManualEditing (bool enabled)\n{\n    mId->setVisible (enabled);\n}\n\nvoid CSVWorld::GenericCreator::insertAtBeginning (QWidget *widget, bool stretched)\n{\n    mLayout->insertWidget (0, widget, stretched ? 1 : 0);\n}\n\nvoid CSVWorld::GenericCreator::insertBeforeButtons (QWidget *widget, bool stretched)\n{\n    mLayout->insertWidget (mLayout->count()-2, widget, stretched ? 1 : 0);\n\n    // Reset tab order relative to buttons.\n    setTabOrder(widget, mCreate);\n    setTabOrder(mCreate, mCancel);\n}\n\nstd::string CSVWorld::GenericCreator::getId() const\n{\n    return mId->text().toUtf8().constData();\n}\n\nstd::string CSVWorld::GenericCreator::getClonedId() const\n{\n    return mClonedId;\n}\n\nstd::string CSVWorld::GenericCreator::getIdValidatorResult() const\n{\n    std::string errors;\n\n    if (!mId->hasAcceptableInput())\n        errors = mValidator->getError();\n\n    return errors;\n}\n\nvoid CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {}\n\nvoid CSVWorld::GenericCreator::pushCommand (std::unique_ptr<CSMWorld::CreateCommand> command,\n    const std::string& id)\n{\n    mUndoStack.push (command.release());\n}\n\nCSMWorld::Data& CSVWorld::GenericCreator::getData() const\n{\n    return mData;\n}\n\nQUndoStack& CSVWorld::GenericCreator::getUndoStack()\n{\n    return mUndoStack;\n}\n\nconst CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const\n{\n    return mListId;\n}\n\nstd::string CSVWorld::GenericCreator::getNamespace() const\n{\n    CSMWorld::Scope scope = CSMWorld::Scope_Content;\n\n    if (mScope)\n    {\n        scope = static_cast<CSMWorld::Scope> (mScope->itemData (mScope->currentIndex()).toInt());\n    }\n    else\n    {\n        if (mScopes & CSMWorld::Scope_Project)\n            scope = CSMWorld::Scope_Project;\n        else if (mScopes & CSMWorld::Scope_Session)\n            scope = CSMWorld::Scope_Session;\n    }\n\n    switch (scope)\n    {\n        case CSMWorld::Scope_Content: return \"\";\n        case CSMWorld::Scope_Project: return \"project::\";\n        case CSMWorld::Scope_Session: return \"session::\";\n    }\n\n    return \"\";\n}\n\nvoid CSVWorld::GenericCreator::updateNamespace()\n{\n    std::string namespace_ = getNamespace();\n\n    mValidator->setNamespace (namespace_);\n\n    int index = mId->text().indexOf (\"::\");\n\n    if (index==-1)\n    {\n        // no namespace in old text\n        mId->setText (QString::fromUtf8 (namespace_.c_str()) + mId->text());\n    }\n    else\n    {\n        std::string oldNamespace =\n            Misc::StringUtils::lowerCase (mId->text().left (index).toUtf8().constData());\n\n        if (oldNamespace==\"project\" || oldNamespace==\"session\")\n            mId->setText (QString::fromUtf8 (namespace_.c_str()) + mId->text().mid (index+2));\n    }\n}\n\nvoid CSVWorld::GenericCreator::addScope (const QString& name, CSMWorld::Scope scope,\n    const QString& tooltip)\n{\n    mScope->addItem (name, static_cast<int> (scope));\n    mScope->setItemData (mScope->count()-1, tooltip, Qt::ToolTipRole);\n}\n\nCSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n    const CSMWorld::UniversalId& id, bool relaxedIdRules)\n: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false),\n  mClonedType (CSMWorld::UniversalId::Type_None), mScopes (CSMWorld::Scope_Content), mScope (nullptr),\n  mScopeLabel (nullptr), mCloneMode (false)\n{\n    // If the collection ID has a parent type, use it instead.\n    // It will change IDs with Record/SubRecord class (used for creators in Dialogue subviews)\n    // to IDs with general RecordList class (used for creators in Table subviews).\n    CSMWorld::UniversalId::Type listParentType = CSMWorld::UniversalId::getParentType(mListId.getType());\n    if (listParentType != CSMWorld::UniversalId::Type_None)\n    {\n        mListId = listParentType;\n    }\n\n    mLayout = new QHBoxLayout;\n    mLayout->setContentsMargins (0, 0, 0, 0);\n\n    mId = new QLineEdit;\n    mId->setValidator (mValidator = new IdValidator (relaxedIdRules, this));\n    mLayout->addWidget (mId, 1);\n\n    mCreate = new QPushButton (\"Create\");\n    mLayout->addWidget (mCreate);\n\n    mCancel = new QPushButton(\"Cancel\");\n    mLayout->addWidget(mCancel);\n\n    setLayout (mLayout);\n\n    connect (mCancel, SIGNAL (clicked (bool)), this, SIGNAL (done()));\n    connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create()));\n\n    connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));\n    connect (mId, SIGNAL (returnPressed()), this, SLOT (inputReturnPressed()));\n\n    connect (&mData, SIGNAL (idListChanged()), this, SLOT (dataIdListChanged()));\n}\n\nvoid CSVWorld::GenericCreator::setEditLock (bool locked)\n{\n    mLocked = locked;\n    update();\n}\n\nvoid CSVWorld::GenericCreator::reset()\n{\n    mCloneMode = false;\n    mId->setText (\"\");\n    update();\n    updateNamespace();\n}\n\nstd::string CSVWorld::GenericCreator::getErrors() const\n{\n    std::string errors;\n\n    if (!mId->hasAcceptableInput())\n        errors = mValidator->getError();\n    else if (mData.hasId (getId()))\n        errors = \"ID is already in use\";\n\n    return errors;\n}\n\nvoid CSVWorld::GenericCreator::textChanged (const QString& text)\n{\n    update();\n}\n\nvoid CSVWorld::GenericCreator::inputReturnPressed()\n{\n    if (mCreate->isEnabled())\n    {\n        create();\n    }\n}\n\nvoid CSVWorld::GenericCreator::create()\n{\n    if (!mLocked)\n    {\n        std::string id = getId();\n\n        std::unique_ptr<CSMWorld::CreateCommand> command;\n\n        if (mCloneMode)\n        {\n            command.reset (new CSMWorld::CloneCommand (\n                dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel(mListId)), mClonedId, id, mClonedType));\n        }\n        else\n        {\n            command.reset (new CSMWorld::CreateCommand (\n                dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel (mListId)), id));\n\n        }\n\n        configureCreateCommand (*command);\n        pushCommand (std::move(command), id);\n\n        emit done();\n        emit requestFocus(id);\n    }\n}\n\nvoid CSVWorld::GenericCreator::cloneMode(const std::string& originId,\n                                         const CSMWorld::UniversalId::Type type)\n{\n    mCloneMode = true;\n    mClonedId = originId;\n    mClonedType = type;\n}\n\nvoid CSVWorld::GenericCreator::touch(const std::vector<CSMWorld::UniversalId>& ids)\n{\n    // Combine multiple touch commands into one \"macro\" command\n    mUndoStack.beginMacro(\"Touch Records\");\n\n    CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*mData.getTableModel(mListId));\n    for (const CSMWorld::UniversalId& uid : ids)\n    {\n        CSMWorld::TouchCommand* touchCmd = new CSMWorld::TouchCommand(table, uid.getId());\n        mUndoStack.push(touchCmd);\n    }\n\n    // Execute\n    mUndoStack.endMacro();\n}\n\nvoid CSVWorld::GenericCreator::toggleWidgets(bool active)\n{\n}\n\nvoid CSVWorld::GenericCreator::focus()\n{\n    mId->setFocus();\n}\n\nvoid CSVWorld::GenericCreator::setScope (unsigned int scope)\n{\n    mScopes = scope;\n    int count = (mScopes & CSMWorld::Scope_Content) + (mScopes & CSMWorld::Scope_Project) +\n        (mScopes & CSMWorld::Scope_Session);\n\n    // scope selector widget\n    if (count>1)\n    {\n        mScope = new QComboBox (this);\n        insertAtBeginning (mScope, false);\n\n        if (mScopes & CSMWorld::Scope_Content)\n            addScope (\"Content\", CSMWorld::Scope_Content,\n                \"Record will be stored in the currently edited content file.\");\n\n        if (mScopes & CSMWorld::Scope_Project)\n            addScope (\"Project\", CSMWorld::Scope_Project,\n                \"Record will be stored in a local project file.<p>\"\n                \"Record will be created in the reserved namespace \\\"project\\\".<p>\"\n                \"Record is available when running OpenMW via OpenCS.\");\n\n        if (mScopes & CSMWorld::Scope_Session)\n            addScope (\"Session\", CSMWorld::Scope_Session,\n                \"Record exists only for the duration of the current editing session.<p>\"\n                \"Record will be created in the reserved namespace \\\"session\\\".<p>\"\n                \"Record is not available when running OpenMW via OpenCS.\");\n\n        connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (scopeChanged (int)));\n\n        mScopeLabel = new QLabel (\"Scope\", this);\n        insertAtBeginning (mScopeLabel, false);\n\n        mScope->setCurrentIndex (0);\n    }\n    else\n    {\n        delete mScope;\n        mScope = nullptr;\n\n        delete mScopeLabel;\n        mScopeLabel = nullptr;\n    }\n\n    updateNamespace();\n}\n\nvoid CSVWorld::GenericCreator::scopeChanged (int index)\n{\n    update();\n    updateNamespace();\n}\n\nvoid CSVWorld::GenericCreator::dataIdListChanged()\n{\n    // If the original ID of cloned record was removed, cancel the creator\n    if (mCloneMode && !mData.hasId(mClonedId))\n    {\n        emit done();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/genericcreator.hpp",
    "content": "#ifndef CSV_WORLD_GENERICCREATOR_H\n#define CSV_WORLD_GENERICCREATOR_H\n\n#include <memory>\n\n#include \"../../model/world/universalid.hpp\"\n\n#include \"creator.hpp\"\n\nclass QString;\nclass QPushButton;\nclass QLineEdit;\nclass QHBoxLayout;\nclass QComboBox;\nclass QLabel;\nclass QUndoStack;\n\nnamespace CSMWorld\n{\n    class CreateCommand;\n    class Data;\n}\n\nnamespace CSVWorld\n{\n    class IdValidator;\n\n    class GenericCreator : public Creator\n    {\n            Q_OBJECT\n\n            CSMWorld::Data& mData;\n            QUndoStack& mUndoStack;\n            CSMWorld::UniversalId mListId;\n            QPushButton *mCreate;\n            QPushButton *mCancel;\n            QLineEdit *mId;\n            std::string mErrors;\n            QHBoxLayout *mLayout;\n            bool mLocked;\n            std::string mClonedId;\n            CSMWorld::UniversalId::Type mClonedType;\n            unsigned int mScopes;\n            QComboBox *mScope;\n            QLabel *mScopeLabel;\n            IdValidator *mValidator;\n\n        protected:\n            bool mCloneMode;\n\n        protected:\n\n            void update();\n\n            virtual void setManualEditing (bool enabled);\n            ///< Enable/disable manual ID editing (enabled by default).\n\n            void insertAtBeginning (QWidget *widget, bool stretched);\n\n            /// \\brief Insert given widget before Create and Cancel buttons.\n            /// \\param widget Widget to add to layout.\n            /// \\param stretched Whether widget should be streched or not.\n            void insertBeforeButtons (QWidget *widget, bool stretched);\n\n            virtual std::string getId() const;\n\n            std::string getClonedId() const;\n\n            virtual std::string getIdValidatorResult() const;\n\n            /// Allow subclasses to add additional data to \\a command.\n            virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;\n\n            /// Allow subclasses to wrap the create command together with additional commands\n            /// into a macro.\n            virtual void pushCommand (std::unique_ptr<CSMWorld::CreateCommand> command,\n                const std::string& id);\n\n            CSMWorld::Data& getData() const;\n\n            QUndoStack& getUndoStack();\n\n            const CSMWorld::UniversalId& getCollectionId() const;\n\n            std::string getNamespace() const;\n\n        private:\n\n            void updateNamespace();\n\n            void addScope (const QString& name, CSMWorld::Scope scope,\n                const QString& tooltip);\n\n        public:\n\n            GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n                const CSMWorld::UniversalId& id, bool relaxedIdRules = false);\n\n            void setEditLock (bool locked) override;\n\n            void reset() override;\n\n            void toggleWidgets (bool active = true) override;\n\n            void cloneMode(const std::string& originId,\n                                   const CSMWorld::UniversalId::Type type) override;\n\n            void touch(const std::vector<CSMWorld::UniversalId>& ids) override;\n\n            virtual std::string getErrors() const;\n            ///< Return formatted error descriptions for the current state of the creator. if an empty\n            /// string is returned, there is no error.\n\n            void setScope (unsigned int scope) override;\n\n            /// Focus main input widget\n            void focus() override;\n\n        private slots:\n\n            void textChanged (const QString& text);\n\n            /// \\brief Create record if able to after Return key is pressed on input.\n            void inputReturnPressed();\n\n            void create();\n\n            void scopeChanged (int index);\n\n            void dataIdListChanged();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/globalcreator.cpp",
    "content": "#include \"globalcreator.hpp\"\n\n#include <components/esm/variant.hpp>\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/idtable.hpp\"\n\nnamespace CSVWorld\n{\n    void GlobalCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const\n    {\n        CSMWorld::IdTable* table = static_cast<CSMWorld::IdTable*>(getData().getTableModel(getCollectionId()));\n\n        int index = table->findColumnIndex(CSMWorld::Columns::ColumnId_ValueType);\n        int type = (int)ESM::VT_Float;\n\n        command.addValue(index, type);\n    }\n\n    GlobalCreator::GlobalCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id)\n        : GenericCreator (data, undoStack, id, true)\n    {\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/globalcreator.hpp",
    "content": "#ifndef CSV_WORLD_GLOBALCREATOR_H\n#define CSV_WORLD_GLOBALCREATOR_H\n\n#include \"genericcreator.hpp\"\n\nnamespace CSVWorld\n{\n    class GlobalCreator : public GenericCreator\n    {\n            Q_OBJECT\n\n        public:\n\n            GlobalCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);\n\n        protected:\n\n            void configureCreateCommand(CSMWorld::CreateCommand& command) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/idcompletiondelegate.cpp",
    "content": "#include \"idcompletiondelegate.hpp\"\n\n#include \"../../model/world/idcompletionmanager.hpp\"\n#include \"../../model/world/infoselectwrapper.hpp\"\n\n#include \"../widget/droplineedit.hpp\"\n\nCSVWorld::IdCompletionDelegate::IdCompletionDelegate(CSMWorld::CommandDispatcher *dispatcher,\n                                                     CSMDoc::Document& document,\n                                                     QObject *parent)\n    : CommandDelegate(dispatcher, document, parent)\n{}\n\nQWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent,\n                                                      const QStyleOptionViewItem &option,\n                                                      const QModelIndex &index) const\n{\n    return createEditor(parent, option, index, getDisplayTypeFromIndex(index));\n}\n\nQWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent,\n                                                      const QStyleOptionViewItem &option,\n                                                      const QModelIndex &index,\n                                                      CSMWorld::ColumnBase::Display display) const\n{\n    if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid())\n    {\n        return nullptr;\n    }\n\n    // The completer for InfoCondVar needs to return a completer based on the first column\n    if (display == CSMWorld::ColumnBase::Display_InfoCondVar)\n    {\n        QModelIndex sibling = index.sibling(index.row(), 0);\n        int conditionFunction = sibling.model()->data(sibling, Qt::EditRole).toInt();\n\n        switch (conditionFunction)\n        {\n            case CSMWorld::ConstInfoSelectWrapper::Function_Global:\n            {\n                return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_GlobalVariable);\n            }\n            case CSMWorld::ConstInfoSelectWrapper::Function_Journal:\n            {\n                return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Journal);\n            }\n            case CSMWorld::ConstInfoSelectWrapper::Function_Item:\n            {\n                return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Referenceable);\n            }\n            case CSMWorld::ConstInfoSelectWrapper::Function_Dead:\n            case CSMWorld::ConstInfoSelectWrapper::Function_NotId:\n            {\n                return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Referenceable);\n            }\n            case CSMWorld::ConstInfoSelectWrapper::Function_NotFaction:\n            {\n                return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Faction);\n            }\n            case CSMWorld::ConstInfoSelectWrapper::Function_NotClass:\n            {\n                return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Class);\n            }\n            case CSMWorld::ConstInfoSelectWrapper::Function_NotRace:\n            {\n                return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Race);\n            }\n            case CSMWorld::ConstInfoSelectWrapper::Function_NotCell:\n            {\n                return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Cell);\n            }\n            case CSMWorld::ConstInfoSelectWrapper::Function_Local:\n            case CSMWorld::ConstInfoSelectWrapper::Function_NotLocal:\n            {\n                return new CSVWidget::DropLineEdit(display, parent);\n            }\n            default: return nullptr; // The rest of them can't be edited anyway\n        }\n    }\n\n    CSMWorld::IdCompletionManager &completionManager = getDocument().getIdCompletionManager();\n    CSVWidget::DropLineEdit *editor = new CSVWidget::DropLineEdit(display, parent);\n    editor->setCompleter(completionManager.getCompleter(display).get());\n    return editor;\n}\n\nCSVWorld::CommandDelegate *CSVWorld::IdCompletionDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher,\n                                                                               CSMDoc::Document& document, \n                                                                               QObject *parent) const\n{\n    return new IdCompletionDelegate(dispatcher, document, parent);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/idcompletiondelegate.hpp",
    "content": "#ifndef CSV_WORLD_IDCOMPLETIONDELEGATE_HPP\n#define CSV_WORLD_IDCOMPLETIONDELEGATE_HPP\n\n#include \"util.hpp\"\n\nnamespace CSVWorld\n{\n    /// \\brief Enables the Id completion for a column\n    class IdCompletionDelegate : public CommandDelegate\n    {\n        public:\n            IdCompletionDelegate(CSMWorld::CommandDispatcher *dispatcher, \n                                 CSMDoc::Document& document, \n                                 QObject *parent);\n\n            QWidget *createEditor (QWidget *parent,\n                                           const QStyleOptionViewItem &option,\n                                           const QModelIndex &index) const override;\n\n            QWidget *createEditor (QWidget *parent,\n                                           const QStyleOptionViewItem &option,\n                                           const QModelIndex &index,\n                                           CSMWorld::ColumnBase::Display display) const override;\n    };\n\n    class IdCompletionDelegateFactory : public CommandDelegateFactory\n    {\n        public:\n            CommandDelegate *makeDelegate(CSMWorld::CommandDispatcher *dispatcher, \n                                                  CSMDoc::Document& document, \n                                                  QObject *parent) const override;\n            ///< The ownership of the returned CommandDelegate is transferred to the caller.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/idtypedelegate.cpp",
    "content": "#include \"idtypedelegate.hpp\"\n\n#include \"../../model/world/universalid.hpp\"\n\nCSVWorld::IdTypeDelegate::IdTypeDelegate\n    (const ValueList &values, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)\n    : DataDisplayDelegate (values, icons, dispatcher, document,\n                           \"Records\", \"type-format\",\n                           parent)\n{}\n\nCSVWorld::IdTypeDelegateFactory::IdTypeDelegateFactory()\n{\n    for (int i=0; i<CSMWorld::UniversalId::NumberOfTypes; ++i)\n    {\n        CSMWorld::UniversalId id (static_cast<CSMWorld::UniversalId::Type> (i));\n\n        DataDisplayDelegateFactory::add (id.getType(), QString::fromUtf8 (id.getTypeName().c_str()),\n            QString::fromUtf8 (id.getIcon().c_str()));\n    }\n}\n\nCSVWorld::CommandDelegate *CSVWorld::IdTypeDelegateFactory::makeDelegate (\n    CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const\n{\n    return new IdTypeDelegate (mValues, mIcons, dispatcher, document, parent);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/idtypedelegate.hpp",
    "content": "#ifndef IDTYPEDELEGATE_HPP\n#define IDTYPEDELEGATE_HPP\n\n#include \"enumdelegate.hpp\"\n#include \"util.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#include \"datadisplaydelegate.hpp\"\n\nnamespace CSVWorld\n{\n    class IdTypeDelegate : public DataDisplayDelegate\n    {\n        public:\n            IdTypeDelegate (const ValueList &mValues, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent);\n    };\n\n    class IdTypeDelegateFactory : public DataDisplayDelegateFactory\n    {\n        public:\n\n            IdTypeDelegateFactory();\n\n            CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const override;\n            ///< The ownership of the returned CommandDelegate is transferred to the caller.\n    };\n}\n\n#endif // REFIDTYPEDELEGATE_HPP\n"
  },
  {
    "path": "apps/opencs/view/world/idvalidator.cpp",
    "content": "#include \"idvalidator.hpp\"\n\n#include <components/misc/stringops.hpp>\n\nbool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const\n{\n    if (c.isLetter() || c=='_')\n        return true;\n\n    if (!first && (c.isDigit()  || c.isSpace()))\n        return true;\n\n    return false;\n}\n\nCSVWorld::IdValidator::IdValidator (bool relaxed, QObject *parent)\n: QValidator (parent), mRelaxed (relaxed)\n{}\n\nQValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const\n{\n    mError.clear();\n\n    if (mRelaxed)\n    {\n        if (input.indexOf ('\"')!=-1 || input.indexOf (\"::\")!=-1 || input.indexOf (\"#\")!=-1)\n            return QValidator::Invalid;\n    }\n    else\n    {\n        if (input.isEmpty())\n        {\n            mError = \"Missing ID\";\n            return QValidator::Intermediate;\n        }\n\n        bool first = true;\n        bool scope = false;\n        bool prevScope = false;\n\n        QString::const_iterator iter = input.begin();\n\n        if (!mNamespace.empty())\n        {\n            std::string namespace_ = input.left (static_cast<int>(mNamespace.size())).toUtf8().constData();\n\n            if (Misc::StringUtils::lowerCase (namespace_)!=mNamespace)\n                return QValidator::Invalid; // incorrect namespace\n\n            iter += namespace_.size();\n            first = false;\n            prevScope = true;\n        }\n        else\n        {\n            int index = input.indexOf (\":\");\n\n            if (index!=-1)\n            {\n                QString namespace_ = input.left (index);\n\n                if (namespace_==\"project\" || namespace_==\"session\")\n                    return QValidator::Invalid; // reserved namespace\n            }\n        }\n\n        for (; iter!=input.end(); ++iter, first = false)\n        {\n            if (*iter==':')\n            {\n                if (first)\n                    return QValidator::Invalid; // scope operator at the beginning\n\n                if (scope)\n                {\n                    scope = false;\n                    prevScope = true;\n                }\n                else\n                {\n                    if (prevScope)\n                        return QValidator::Invalid; // sequence of two scope operators\n\n                    scope = true;\n                }\n            }\n            else if (scope)\n                return QValidator::Invalid; // incomplete scope operator\n            else\n            {\n                prevScope = false;\n\n                if (!isValid (*iter, first))\n                    return QValidator::Invalid;\n            }\n        }\n\n        if (scope)\n        {\n            mError = \"ID ending with incomplete scope operator\";\n            return QValidator::Intermediate;\n        }\n\n        if (prevScope)\n        {\n            mError = \"ID ending with scope operator\";\n            return QValidator::Intermediate;\n        }\n    }\n\n    return QValidator::Acceptable;\n}\n\nvoid CSVWorld::IdValidator::setNamespace (const std::string& namespace_)\n{\n    mNamespace = Misc::StringUtils::lowerCase (namespace_);\n}\n\nstd::string CSVWorld::IdValidator::getError() const\n{\n    return mError;\n}\n"
  },
  {
    "path": "apps/opencs/view/world/idvalidator.hpp",
    "content": "#ifndef CSV_WORLD_IDVALIDATOR_H\n#define CSV_WORLD_IDVALIDATOR_H\n\n#include <string>\n\n#include <QValidator>\n\nnamespace CSVWorld\n{\n    class IdValidator : public QValidator\n    {\n            bool mRelaxed;\n            std::string mNamespace;\n            mutable std::string mError;\n\n        private:\n\n            bool isValid (const QChar& c, bool first) const;\n\n        public:\n\n            IdValidator (bool relaxed = false, QObject *parent = nullptr);\n            ///< \\param relaxed Relaxed rules for IDs that also functino as user visible text\n\n            State validate (QString& input, int& pos) const override;\n\n            void setNamespace (const std::string& namespace_);\n\n            /// Return a description of the error that resulted in the last call of validate\n            /// returning QValidator::Intermediate. If the last call to validate returned\n            /// a different value (or if there was no such call yet), an empty string is\n            /// returned.\n            std::string getError() const;\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/infocreator.cpp",
    "content": "#include \"infocreator.hpp\"\n\n#include <algorithm>\n\n#include <QLabel>\n#include <QUuid>\n\n#include <components/misc/stringops.hpp>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/idcompletionmanager.hpp\"\n\n#include \"../widget/droplineedit.hpp\"\n\nstd::string CSVWorld::InfoCreator::getId() const\n{\n    std::string id = Misc::StringUtils::lowerCase (mTopic->text().toUtf8().constData());\n\n    std::string unique = QUuid::createUuid().toByteArray().data();\n\n    unique.erase (std::remove (unique.begin(), unique.end(), '-'), unique.end());\n\n    unique = unique.substr (1, unique.size()-2);\n\n    return id + '#' + unique;\n}\n\nvoid CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const\n{\n    CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId()));\n\n    CSMWorld::CloneCommand* cloneCommand = dynamic_cast<CSMWorld::CloneCommand*> (&command);\n    if (getCollectionId() == CSMWorld::UniversalId::Type_TopicInfos)\n    {\n        if (!cloneCommand)\n        {\n            command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Topic), mTopic->text());\n            command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Rank), -1);\n            command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Gender), -1);\n            command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_PcRank), -1);\n        }\n        else\n        {\n            cloneCommand->setOverrideValue(table.findColumnIndex(CSMWorld::Columns::ColumnId_Topic), mTopic->text());\n        }\n    }\n    else\n    {\n        if (!cloneCommand)\n        {\n            command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Journal), mTopic->text());\n        }\n        else\n            cloneCommand->setOverrideValue(table.findColumnIndex(CSMWorld::Columns::ColumnId_Journal), mTopic->text());\n    }\n}\n\nCSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n    const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager& completionManager)\n: GenericCreator (data, undoStack, id)\n{\n    // Determine if we're dealing with topics or journals.\n    CSMWorld::ColumnBase::Display displayType = CSMWorld::ColumnBase::Display_Topic;\n    QString labelText = \"Topic\";\n    if (getCollectionId().getType() == CSMWorld::UniversalId::Type_JournalInfos)\n    {\n        displayType = CSMWorld::ColumnBase::Display_Journal;\n        labelText = \"Journal\";\n    }\n\n    QLabel *label = new QLabel (labelText, this);\n    insertBeforeButtons (label, false);\n\n    // Add topic/journal ID input with auto-completion.\n    // Only existing topic/journal IDs are accepted so no ID validation is performed.\n    mTopic = new CSVWidget::DropLineEdit(displayType, this);\n    mTopic->setCompleter(completionManager.getCompleter(displayType).get());\n    insertBeforeButtons (mTopic, true);\n\n    setManualEditing (false);\n\n    connect (mTopic, SIGNAL (textChanged (const QString&)), this, SLOT (topicChanged()));\n    connect (mTopic, SIGNAL (returnPressed()), this, SLOT (inputReturnPressed()));\n}\n\nvoid CSVWorld::InfoCreator::cloneMode (const std::string& originId,\n    const CSMWorld::UniversalId::Type type)\n{\n    CSMWorld::IdTable& infoTable =\n        dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId()));\n\n    int topicColumn = infoTable.findColumnIndex (\n        getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ?\n        CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal);\n\n    mTopic->setText (\n        infoTable.data (infoTable.getModelIndex (originId, topicColumn)).toString());\n\n    GenericCreator::cloneMode (originId, type);\n}\n\nvoid CSVWorld::InfoCreator::reset()\n{\n    mTopic->setText (\"\");\n    GenericCreator::reset();\n}\n\nstd::string CSVWorld::InfoCreator::getErrors() const\n{\n    // We ignore errors from GenericCreator here, because they can never happen in an InfoCreator.\n    std::string errors;\n\n    std::string topic = mTopic->text().toUtf8().constData();\n\n    if ((getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ?\n        getData().getTopics() : getData().getJournals()).searchId (topic)==-1)\n    {\n        errors += \"Invalid Topic ID\";\n    }\n\n    return errors;\n}\n\nvoid CSVWorld::InfoCreator::focus()\n{\n    mTopic->setFocus();\n}\n\nvoid CSVWorld::InfoCreator::topicChanged()\n{\n    update();\n}\n\nCSVWorld::Creator *CSVWorld::InfoCreatorFactory::makeCreator(CSMDoc::Document& document,\n                                                             const CSMWorld::UniversalId& id) const\n{\n    return new InfoCreator(document.getData(),\n                           document.getUndoStack(),\n                           id,\n                           document.getIdCompletionManager());\n}\n"
  },
  {
    "path": "apps/opencs/view/world/infocreator.hpp",
    "content": "#ifndef CSV_WORLD_INFOCREATOR_H\n#define CSV_WORLD_INFOCREATOR_H\n\n#include \"genericcreator.hpp\"\n\nnamespace CSMWorld\n{\n    class InfoCollection;\n    class IdCompletionManager;\n}\n\nnamespace CSVWidget\n{\n    class DropLineEdit;\n}\n\nnamespace CSVWorld\n{\n    class InfoCreator : public GenericCreator\n    {\n            Q_OBJECT\n\n            CSVWidget::DropLineEdit *mTopic;\n\n            std::string getId() const override;\n\n            void configureCreateCommand (CSMWorld::CreateCommand& command) const override;\n\n        public:\n\n            InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n                const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager& completionManager);\n\n            void cloneMode (const std::string& originId,\n                const CSMWorld::UniversalId::Type type) override;\n\n            void reset() override;\n\n            std::string getErrors() const override;\n            ///< Return formatted error descriptions for the current state of the creator. if an empty\n            /// string is returned, there is no error.\n            \n            /// Focus main input widget\n            void focus() override;\n            \n        private slots:\n\n            void topicChanged();\n    };\n\n    class InfoCreatorFactory : public CreatorFactoryBase\n    {\n        public:\n\n            Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const override;\n            ///< The ownership of the returned Creator is transferred to the caller.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/landcreator.cpp",
    "content": "#include \"landcreator.hpp\"\n\n#include <limits>\n\n#include <QLabel>\n#include <QSpinBox>\n\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/land.hpp\"\n\nnamespace CSVWorld\n{\n    LandCreator::LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id)\n        : GenericCreator(data, undoStack, id)\n        , mXLabel(nullptr)\n        , mYLabel(nullptr)\n        , mX(nullptr)\n        , mY(nullptr)\n    {\n        const int MaxInt = std::numeric_limits<int>::max();\n        const int MinInt = std::numeric_limits<int>::min();\n\n        setManualEditing(false);\n\n        mXLabel = new QLabel(\"X: \");\n        mX = new QSpinBox();\n        mX->setMinimum(MinInt);\n        mX->setMaximum(MaxInt);\n        insertBeforeButtons(mXLabel, false);\n        insertBeforeButtons(mX, true);\n\n        mYLabel = new QLabel(\"Y: \");\n        mY = new QSpinBox();\n        mY->setMinimum(MinInt);\n        mY->setMaximum(MaxInt);\n        insertBeforeButtons(mYLabel, false);\n        insertBeforeButtons(mY, true);\n\n        connect (mX, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int)));\n        connect (mY, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int)));\n    }\n\n    void LandCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type)\n    {\n        GenericCreator::cloneMode(originId, type);\n\n        int x = 0, y = 0;\n        CSMWorld::Land::parseUniqueRecordId(originId, x, y);\n\n        mX->setValue(x);\n        mY->setValue(y);\n    }\n\n    void LandCreator::touch(const std::vector<CSMWorld::UniversalId>& ids)\n    {\n        // Combine multiple touch commands into one \"macro\" command\n        getUndoStack().beginMacro(\"Touch records\");\n\n        CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));\n        CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));\n        for (const CSMWorld::UniversalId& uid : ids)\n        {\n            CSMWorld::TouchLandCommand* touchCmd = new CSMWorld::TouchLandCommand(lands, ltexs, uid.getId());\n            getUndoStack().push(touchCmd);\n        }\n\n        // Execute\n        getUndoStack().endMacro();\n    }\n\n    void LandCreator::focus()\n    {\n        mX->setFocus();\n    }\n\n    void LandCreator::reset()\n    {\n        GenericCreator::reset();\n        mX->setValue(0);\n        mY->setValue(0);\n    }\n\n    std::string LandCreator::getErrors() const\n    {\n        if (getData().getLand().searchId(getId()) >= 0)\n            return \"A land with that name already exists.\";\n\n        return \"\";\n    }\n\n    std::string LandCreator::getId() const\n    {\n        return CSMWorld::Land::createUniqueRecordId(mX->value(), mY->value());\n    }\n\n    void LandCreator::pushCommand(std::unique_ptr<CSMWorld::CreateCommand> command, const std::string& id)\n    {\n        if (mCloneMode)\n        {\n            CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));\n            CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));\n\n            getUndoStack().beginMacro((\"Clone \" + id).c_str());\n            getUndoStack().push(command.release());\n\n            CSMWorld::CopyLandTexturesCommand* ltexCopy = new CSMWorld::CopyLandTexturesCommand(lands, ltexs, getClonedId(), getId());\n            getUndoStack().push(ltexCopy);\n\n            getUndoStack().endMacro();\n        }\n        else\n            getUndoStack().push (command.release());\n    }\n\n    void LandCreator::coordChanged(int value)\n    {\n        update();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/landcreator.hpp",
    "content": "#ifndef CSV_WORLD_LANDCREATOR_H\n#define CSV_WORLD_LANDCREATOR_H\n\n#include \"genericcreator.hpp\"\n\nclass QLabel;\nclass QSpinBox;\n\nnamespace CSVWorld\n{\n    class LandCreator : public GenericCreator\n    {\n            Q_OBJECT\n\n            QLabel* mXLabel;\n            QLabel* mYLabel;\n            QSpinBox* mX;\n            QSpinBox* mY;\n\n        public:\n\n            LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);\n\n            void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;\n\n            void touch(const std::vector<CSMWorld::UniversalId>& ids) override;\n\n            void focus() override;\n\n            void reset() override;\n\n            std::string getErrors() const override;\n\n        protected:\n\n            std::string getId() const override;\n\n            void pushCommand(std::unique_ptr<CSMWorld::CreateCommand> command,\n                const std::string& id) override;\n\n        private slots:\n\n            void coordChanged(int value);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/landtexturecreator.cpp",
    "content": "#include \"landtexturecreator.hpp\"\n\n#include <cstdint>\n#include <limits>\n\n#include <QLabel>\n#include <QLineEdit>\n#include <QSpinBox>\n\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/landtexture.hpp\"\n\nnamespace CSVWorld\n{\n    LandTextureCreator::LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id)\n        : GenericCreator(data, undoStack, id)\n    {\n        // One index is reserved for a default texture\n        const size_t MaxIndex = std::numeric_limits<uint16_t>::max() - 1;\n\n        setManualEditing(false);\n\n        QLabel* nameLabel = new QLabel(\"Name\");\n        insertBeforeButtons(nameLabel, false);\n\n        mNameEdit = new QLineEdit(this);\n        insertBeforeButtons(mNameEdit, true);\n\n        QLabel* indexLabel = new QLabel(\"Index\");\n        insertBeforeButtons(indexLabel, false);\n\n        mIndexBox = new QSpinBox(this);\n        mIndexBox->setMinimum(0);\n        mIndexBox->setMaximum(MaxIndex);\n        insertBeforeButtons(mIndexBox, true);\n\n        connect(mNameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(nameChanged(const QString&)));\n        connect(mIndexBox, SIGNAL(valueChanged(int)), this, SLOT(indexChanged(int)));\n    }\n\n    void LandTextureCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type)\n    {\n        GenericCreator::cloneMode(originId, type);\n\n        CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(getCollectionId()));\n\n        int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname);\n        mNameEdit->setText((table.data(table.getModelIndex(originId, column)).toString()));\n\n        column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureIndex);\n        mIndexBox->setValue((table.data(table.getModelIndex(originId, column)).toInt()));\n    }\n\n    void LandTextureCreator::focus()\n    {\n        mIndexBox->setFocus();\n    }\n\n    void LandTextureCreator::reset()\n    {\n        GenericCreator::reset();\n        mNameEdit->setText(\"\");\n        mIndexBox->setValue(0);\n    }\n\n    std::string LandTextureCreator::getErrors() const\n    {\n        if (getData().getLandTextures().searchId(getId()) >= 0)\n        {\n            return \"Index is already in use\";\n        }\n\n        return \"\";\n    }\n\n    void LandTextureCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const\n    {\n        GenericCreator::configureCreateCommand(command);\n\n        CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(getCollectionId()));\n        int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname);\n        command.addValue(column, mName.c_str());\n    }\n\n    std::string LandTextureCreator::getId() const\n    {\n        return CSMWorld::LandTexture::createUniqueRecordId(0, mIndexBox->value());\n    }\n\n    void LandTextureCreator::nameChanged(const QString& value)\n    {\n        mName = value.toUtf8().constData();\n        update();\n    }\n\n    void LandTextureCreator::indexChanged(int value)\n    {\n        update();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/landtexturecreator.hpp",
    "content": "#ifndef CSV_WORLD_LANDTEXTURECREATOR_H\n#define CSV_WORLD_LANDTEXTURECREATOR_H\n\n#include <string>\n\n#include \"genericcreator.hpp\"\n\nclass QLineEdit;\nclass QSpinBox;\n\nnamespace CSVWorld\n{\n    class LandTextureCreator : public GenericCreator\n    {\n            Q_OBJECT\n\n        public:\n\n            LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);\n\n            void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;\n\n            void focus() override;\n\n            void reset() override;\n\n            std::string getErrors() const override;\n\n        protected:\n\n            void configureCreateCommand(CSMWorld::CreateCommand& command) const override;\n\n            std::string getId() const override;\n\n        private slots:\n\n            void nameChanged(const QString& val);\n            void indexChanged(int val);\n\n        private:\n\n            QLineEdit* mNameEdit;\n            QSpinBox* mIndexBox;\n\n            std::string mName;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/nestedtable.cpp",
    "content": "#include \"nestedtable.hpp\"\n\n#include <QHeaderView>\n#include <QContextMenuEvent>\n#include <QMenu>\n#include <QDebug>\n\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"../../model/world/nestedtableproxymodel.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/commanddispatcher.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n\n#include \"tableeditidaction.hpp\"\n#include \"util.hpp\"\n\nCSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,\n                                   CSMWorld::UniversalId id,\n                                   CSMWorld::NestedTableProxyModel* model,\n                                   QWidget* parent,\n                                   bool editable,\n                                   bool fixedRows)\n    : DragRecordTable(document, parent),\n      mAddNewRowAction(nullptr),\n      mRemoveRowAction(nullptr),\n      mEditIdAction(nullptr),\n      mModel(model)\n{\n    mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);\n\n    setSelectionBehavior (QAbstractItemView::SelectRows);\n    setSelectionMode (QAbstractItemView::ExtendedSelection);\n\n    horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);\n    verticalHeader()->hide();\n\n    int columns = model->columnCount(QModelIndex());\n\n    for(int i = 0 ; i < columns; ++i)\n    {\n        CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> (\n            model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());\n\n        CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display,\n                                                                                         mDispatcher,\n                                                                                         document,\n                                                                                         this);\n\n        setItemDelegateForColumn(i, delegate);\n    }\n\n    setModel(model);\n\n    if (editable)\n    {\n        if (!fixedRows)\n        {\n            mAddNewRowAction = new QAction (tr (\"Add new row\"), this);\n            connect(mAddNewRowAction, SIGNAL(triggered()),\n                    this, SLOT(addNewRowActionTriggered()));\n            CSMPrefs::Shortcut* addRowShortcut = new CSMPrefs::Shortcut(\"table-add\", this);\n            addRowShortcut->associateAction(mAddNewRowAction);\n\n            mRemoveRowAction = new QAction (tr (\"Remove rows\"), this);\n            connect(mRemoveRowAction, SIGNAL(triggered()),\n                    this, SLOT(removeRowActionTriggered()));\n            CSMPrefs::Shortcut* removeRowShortcut = new CSMPrefs::Shortcut(\"table-remove\", this);\n            removeRowShortcut->associateAction(mRemoveRowAction);\n        }\n\n        mEditIdAction = new TableEditIdAction(*this, this);\n        connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editCell()));\n    }\n}\n\nstd::vector<CSMWorld::UniversalId> CSVWorld::NestedTable::getDraggedRecords() const\n{\n    // No drag support for nested tables\n    return std::vector<CSMWorld::UniversalId>();\n}\n\nvoid CSVWorld::NestedTable::contextMenuEvent (QContextMenuEvent *event)\n{\n    if (!mEditIdAction)\n        return;\n\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    QMenu menu(this);\n\n    int currentRow = rowAt(event->y());\n    int currentColumn = columnAt(event->x());\n    if (mEditIdAction->isValidIdCell(currentRow, currentColumn))\n    {\n        mEditIdAction->setCell(currentRow, currentColumn);\n        menu.addAction(mEditIdAction);\n        menu.addSeparator();\n    }\n\n    if (mAddNewRowAction && mRemoveRowAction)\n    {\n        menu.addAction(mAddNewRowAction);\n        menu.addAction(mRemoveRowAction);\n    }\n\n    menu.exec (event->globalPos());\n}\n\nvoid CSVWorld::NestedTable::removeRowActionTriggered()\n{\n    CSMWorld::CommandMacro macro(mDocument.getUndoStack(),\n        selectionModel()->selectedRows().size() > 1 ? tr(\"Remove rows\") : \"\");\n\n    // Remove rows in reverse order\n    for (int i = selectionModel()->selectedRows().size() - 1; i >= 0; --i)\n    {\n        macro.push(new CSMWorld::DeleteNestedCommand(*(mModel->model()), mModel->getParentId(),\n            selectionModel()->selectedRows()[i].row(), mModel->getParentColumn()));\n    }\n}\n\nvoid CSVWorld::NestedTable::addNewRowActionTriggered()\n{\n    int row = 0;\n\n    if (!selectionModel()->selectedRows().empty())\n        row = selectionModel()->selectedRows().back().row() + 1;\n\n    mDocument.getUndoStack().push(new CSMWorld::AddNestedCommand(*(mModel->model()),\n                                                                 mModel->getParentId(),\n                                                                 row,\n                                                                 mModel->getParentColumn()));\n}\n\nvoid CSVWorld::NestedTable::editCell()\n{\n    emit editRequest(mEditIdAction->getCurrentId(), \"\");\n}\n"
  },
  {
    "path": "apps/opencs/view/world/nestedtable.hpp",
    "content": "#ifndef CSV_WORLD_NESTEDTABLE_H\n#define CSV_WORLD_NESTEDTABLE_H\n\n#include <QEvent>\n\n#include \"dragrecordtable.hpp\"\n\nclass QAction;\nclass QContextMenuEvent;\n\nnamespace CSMWorld\n{\n    class NestedTableProxyModel;\n    class UniversalId;\n    class CommandDispatcher;\n}\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVWorld\n{\n    class TableEditIdAction;\n\n    class NestedTable : public DragRecordTable\n    {\n        Q_OBJECT\n\n        QAction *mAddNewRowAction;\n        QAction *mRemoveRowAction;\n        TableEditIdAction *mEditIdAction;\n        CSMWorld::NestedTableProxyModel* mModel;\n        CSMWorld::CommandDispatcher *mDispatcher;\n\n    public:\n        NestedTable(CSMDoc::Document& document,\n                    CSMWorld::UniversalId id,\n                    CSMWorld::NestedTableProxyModel* model,\n                    QWidget* parent = nullptr,\n                    bool editable = true,\n                    bool fixedRows = false);\n\n        std::vector<CSMWorld::UniversalId> getDraggedRecords() const override;\n\n    private:\n        void contextMenuEvent (QContextMenuEvent *event) override;\n\n    private slots:\n        void removeRowActionTriggered();\n\n        void addNewRowActionTriggered();\n\n        void editCell();\n\n    signals:\n        void editRequest(const CSMWorld::UniversalId &id, const std::string &hint);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/pathgridcreator.cpp",
    "content": "#include \"pathgridcreator.hpp\"\n\n#include <QLabel>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idcompletionmanager.hpp\"\n#include \"../../model/world/idtable.hpp\"\n\n#include \"../widget/droplineedit.hpp\"\n\nstd::string CSVWorld::PathgridCreator::getId() const\n{\n    return mCell->text().toUtf8().constData();\n}\n\nCSMWorld::IdTable& CSVWorld::PathgridCreator::getPathgridsTable() const\n{\n    return dynamic_cast<CSMWorld::IdTable&> (\n        *getData().getTableModel(getCollectionId())\n    );\n}\n\nCSVWorld::PathgridCreator::PathgridCreator(\n    CSMWorld::Data& data,\n    QUndoStack& undoStack,\n    const CSMWorld::UniversalId& id,\n    CSMWorld::IdCompletionManager& completionManager\n) : GenericCreator(data, undoStack, id)\n{\n    setManualEditing(false);\n\n    QLabel *label = new QLabel(\"Cell\", this);\n    insertBeforeButtons(label, false);\n\n    // Add cell ID input with auto-completion.\n    // Only existing cell IDs are accepted so no ID validation is performed.\n    CSMWorld::ColumnBase::Display displayType = CSMWorld::ColumnBase::Display_Cell;\n    mCell = new CSVWidget::DropLineEdit(displayType, this);\n    mCell->setCompleter(completionManager.getCompleter(displayType).get());\n    insertBeforeButtons(mCell, true);\n\n    connect(mCell, SIGNAL (textChanged(const QString&)), this, SLOT (cellChanged()));\n    connect(mCell, SIGNAL (returnPressed()), this, SLOT (inputReturnPressed()));\n}\n\nvoid CSVWorld::PathgridCreator::cloneMode(\n    const std::string& originId,\n    const CSMWorld::UniversalId::Type type)\n{\n    CSVWorld::GenericCreator::cloneMode(originId, type);\n\n    // Look up cloned record in pathgrids table and set cell ID text.\n    CSMWorld::IdTable& table = getPathgridsTable();\n    int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_Id);\n    mCell->setText(table.data(table.getModelIndex(originId, column)).toString());\n}\n\nstd::string CSVWorld::PathgridCreator::getErrors() const\n{\n    std::string cellId = getId();\n\n    // Check user input for any errors.\n    std::string errors;\n    if (cellId.empty())\n    {\n        errors = \"No cell ID selected\";\n    }\n    else if (getData().getPathgrids().searchId(cellId) > -1)\n    {\n        errors = \"Pathgrid for selected cell ID already exists\";\n    }\n    else if (getData().getCells().searchId(cellId) == -1)\n    {\n        errors = \"Cell with selected cell ID does not exist\";\n    }\n\n    return errors;\n}\n\nvoid CSVWorld::PathgridCreator::focus()\n{\n    mCell->setFocus();\n}\n\nvoid CSVWorld::PathgridCreator::reset()\n{\n    CSVWorld::GenericCreator::reset();\n    mCell->setText(\"\");\n}\n\nvoid CSVWorld::PathgridCreator::cellChanged()\n{\n    update();\n}\n\nCSVWorld::Creator *CSVWorld::PathgridCreatorFactory::makeCreator(\n    CSMDoc::Document& document,\n    const CSMWorld::UniversalId& id) const\n{\n    return new PathgridCreator(\n        document.getData(),\n        document.getUndoStack(),\n        id,\n        document.getIdCompletionManager()\n    );\n}\n"
  },
  {
    "path": "apps/opencs/view/world/pathgridcreator.hpp",
    "content": "#ifndef PATHGRIDCREATOR_HPP\n#define PATHGRIDCREATOR_HPP\n\n#include \"genericcreator.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class Data;\n    class IdCompletionManager;\n    class IdTable;\n    class UniversalId;\n}\n\nnamespace CSVWidget\n{\n    class DropLineEdit;\n}\n\nnamespace CSVWorld\n{\n    /// \\brief Record creator for pathgrids.\n    class PathgridCreator : public GenericCreator\n    {\n        Q_OBJECT\n\n        CSVWidget::DropLineEdit *mCell;\n\n        private:\n\n            /// \\return Cell ID entered by user.\n            std::string getId() const override;\n\n            /// \\return reference to table containing pathgrids.\n            CSMWorld::IdTable& getPathgridsTable() const;\n\n        public:\n\n            PathgridCreator(\n                CSMWorld::Data& data,\n                QUndoStack& undoStack,\n                const CSMWorld::UniversalId& id,\n                CSMWorld::IdCompletionManager& completionManager);\n\n            /// \\brief Set cell ID input widget to ID of record to be cloned.\n            /// \\param originId Cell ID to be cloned.\n            /// \\param type Type of record to be cloned.\n            void cloneMode(\n                const std::string& originId,\n                const CSMWorld::UniversalId::Type type) override;\n\n            /// \\return Error description for current user input.\n            std::string getErrors() const override;\n\n            /// \\brief Set focus to cell ID input widget.\n            void focus() override;\n\n            /// \\brief Clear cell ID input widget.\n            void reset() override;\n\n        private slots:\n\n            /// \\brief Check user input for errors.\n            void cellChanged();\n    };\n\n    /// \\brief Creator factory for pathgrid record creator.\n    class PathgridCreatorFactory : public CreatorFactoryBase\n    {\n        public:\n\n            Creator *makeCreator(\n                CSMDoc::Document& document,\n                const CSMWorld::UniversalId& id) const override;\n    };\n}\n\n#endif // PATHGRIDCREATOR_HPP\n"
  },
  {
    "path": "apps/opencs/view/world/previewsubview.cpp",
    "content": "#include \"previewsubview.hpp\"\n\n#include <QHBoxLayout>\n\n#include \"../render/previewwidget.hpp\"\n\n#include \"../widget/scenetoolbar.hpp\"\n#include \"../widget/scenetoolmode.hpp\"\n\nCSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)\n: SubView (id), mTitle (id.toString().c_str())\n{\n    QHBoxLayout *layout = new QHBoxLayout;\n\n    if (document.getData().getReferenceables().searchId (id.getId())==-1)\n    {\n        std::string referenceableId =\n            document.getData().getReferences().getRecord (id.getId()).get().mRefID;\n\n        referenceableIdChanged (referenceableId);\n\n        mScene =\n            new CSVRender::PreviewWidget (document.getData(), id.getId(), false, this);\n    }\n    else\n        mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this);\n\n    CSVWidget::SceneToolbar *toolbar = new CSVWidget::SceneToolbar (48+6, this);\n\n    CSVWidget::SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar);\n    toolbar->addTool (lightingTool);\n\n    layout->addWidget (toolbar, 0);\n\n    layout->addWidget (mScene, 1);\n\n    QWidget *widget = new QWidget;\n\n    widget->setLayout (layout);\n\n    setWidget (widget);\n\n    connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest()));\n    connect (mScene, SIGNAL (referenceableIdChanged (const std::string&)),\n        this, SLOT (referenceableIdChanged (const std::string&)));\n    connect (mScene, SIGNAL (focusToolbarRequest()), toolbar, SLOT (setFocus()));\n    connect (toolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus()));\n}\n\nvoid CSVWorld::PreviewSubView::setEditLock (bool locked) {}\n\nstd::string CSVWorld::PreviewSubView::getTitle() const\n{\n    return mTitle;\n}\n\nvoid CSVWorld::PreviewSubView::referenceableIdChanged (const std::string& id)\n{\n    if (id.empty())\n        mTitle = \"Preview: Reference to <nothing>\";\n    else\n        mTitle = \"Preview: Reference to \" + id;\n\n    setWindowTitle (QString::fromUtf8 (mTitle.c_str()));\n\n    emit updateTitle();\n}\n"
  },
  {
    "path": "apps/opencs/view/world/previewsubview.hpp",
    "content": "#ifndef CSV_WORLD_PREVIEWSUBVIEW_H\n#define CSV_WORLD_PREVIEWSUBVIEW_H\n\n#include \"../doc/subview.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVRender\n{\n    class PreviewWidget;\n}\n\nnamespace CSVWorld\n{\n    class PreviewSubView : public CSVDoc::SubView\n    {\n            Q_OBJECT\n\n            CSVRender::PreviewWidget *mScene;\n            std::string mTitle;\n\n        public:\n\n            PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);\n\n            void setEditLock (bool locked) override;\n\n            std::string getTitle() const override;\n\n        private slots:\n\n            void referenceableIdChanged (const std::string& id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/recordbuttonbar.cpp",
    "content": "#include \"recordbuttonbar.hpp\"\n\n#include <QHBoxLayout>\n#include <QToolButton>\n\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/commanddispatcher.hpp\"\n\n#include \"../../model/prefs/state.hpp\"\n\n#include \"../world/tablebottombox.hpp\"\n\nvoid CSVWorld::RecordButtonBar::updateModificationButtons()\n{\n    bool createAndDeleteDisabled = !mBottom || !mBottom->canCreateAndDelete() || mLocked;\n\n    mCloneButton->setDisabled (createAndDeleteDisabled);\n    mAddButton->setDisabled (createAndDeleteDisabled);\n\n    bool commandDisabled = !mCommandDispatcher || mLocked;\n\n    mRevertButton->setDisabled (commandDisabled);\n    mDeleteButton->setDisabled (commandDisabled || createAndDeleteDisabled);\n}\n\nvoid CSVWorld::RecordButtonBar::updatePrevNextButtons()\n{\n    int rows = mTable.rowCount();\n\n    if (rows<=1)\n    {\n        mPrevButton->setDisabled (true);\n        mNextButton->setDisabled (true);\n    }\n    else if (CSMPrefs::get()[\"General Input\"][\"cycle\"].isTrue())\n    {\n        mPrevButton->setDisabled (false);\n        mNextButton->setDisabled (false);\n    }\n    else\n    {\n        int row = mTable.getModelIndex (mId.getId(), 0).row();\n\n        mPrevButton->setDisabled (row<=0);\n        mNextButton->setDisabled (row>=rows-1);\n    }\n}\n\nCSVWorld::RecordButtonBar::RecordButtonBar (const CSMWorld::UniversalId& id,\n    CSMWorld::IdTable& table, TableBottomBox *bottomBox,\n    CSMWorld::CommandDispatcher *commandDispatcher, QWidget *parent)\n: QWidget (parent), mId (id), mTable (table), mBottom (bottomBox),\n  mCommandDispatcher (commandDispatcher), mLocked (false)\n{\n    QHBoxLayout *buttonsLayout = new QHBoxLayout;\n    buttonsLayout->setContentsMargins (0, 0, 0, 0);\n\n    // left section\n    mPrevButton = new QToolButton (this);\n    mPrevButton->setIcon(QIcon(\":record-previous\"));\n    mPrevButton->setToolTip (\"Switch to previous record\");\n    buttonsLayout->addWidget (mPrevButton, 0);\n\n    mNextButton = new QToolButton (this);\n    mNextButton->setIcon(QIcon(\":/record-next\"));\n    mNextButton->setToolTip (\"Switch to next record\");\n    buttonsLayout->addWidget (mNextButton, 1);\n\n    buttonsLayout->addStretch(2);\n\n    // optional buttons of the right section\n    if (mTable.getFeatures() & CSMWorld::IdTable::Feature_Preview)\n    {\n        QToolButton* previewButton = new QToolButton (this);\n        previewButton->setIcon(QIcon(\":edit-preview\"));\n        previewButton->setToolTip (\"Open a preview of this record\");\n        buttonsLayout->addWidget(previewButton);\n        connect (previewButton, SIGNAL(clicked()), this, SIGNAL (showPreview()));\n    }\n\n    if (mTable.getFeatures() & CSMWorld::IdTable::Feature_View)\n    {\n        QToolButton* viewButton = new QToolButton (this);\n        viewButton->setIcon(QIcon(\":/cell.png\"));\n        viewButton->setToolTip (\"Open a scene view of the cell this record is located in\");\n        buttonsLayout->addWidget(viewButton);\n        connect (viewButton, SIGNAL(clicked()), this, SIGNAL (viewRecord()));\n    }\n\n    // right section\n    mCloneButton = new QToolButton (this);\n    mCloneButton->setIcon(QIcon(\":edit-clone\"));\n    mCloneButton->setToolTip (\"Clone record\");\n    buttonsLayout->addWidget(mCloneButton);\n\n    mAddButton = new QToolButton (this);\n    mAddButton->setIcon(QIcon(\":edit-add\"));\n    mAddButton->setToolTip (\"Add new record\");\n    buttonsLayout->addWidget(mAddButton);\n\n    mDeleteButton = new QToolButton (this);\n    mDeleteButton->setIcon(QIcon(\":edit-delete\"));\n    mDeleteButton->setToolTip (\"Delete record\");\n    buttonsLayout->addWidget(mDeleteButton);\n\n    mRevertButton = new QToolButton (this);\n    mRevertButton->setIcon(QIcon(\":edit-undo\"));\n    mRevertButton->setToolTip (\"Revert record\");\n    buttonsLayout->addWidget(mRevertButton);\n\n    setLayout (buttonsLayout);\n\n    // connections\n    if(mBottom && mBottom->canCreateAndDelete())\n    {\n        connect (mAddButton, SIGNAL (clicked()), mBottom, SLOT (createRequest()));\n        connect (mCloneButton, SIGNAL (clicked()), this, SLOT (cloneRequest()));\n    }\n\n    connect (mNextButton, SIGNAL (clicked()), this, SLOT (nextId()));\n    connect (mPrevButton, SIGNAL (clicked()), this, SLOT (prevId()));\n\n    if (mCommandDispatcher)\n    {\n        connect (mRevertButton, SIGNAL (clicked()), mCommandDispatcher, SLOT (executeRevert()));\n        connect (mDeleteButton, SIGNAL (clicked()), mCommandDispatcher, SLOT (executeDelete()));\n    }\n\n    connect (&mTable, SIGNAL (rowsInserted (const QModelIndex&, int, int)),\n        this, SLOT (rowNumberChanged (const QModelIndex&, int, int)));\n    connect (&mTable, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),\n        this, SLOT (rowNumberChanged (const QModelIndex&, int, int)));\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n\n    updateModificationButtons();\n    updatePrevNextButtons();\n}\n\nvoid CSVWorld::RecordButtonBar::setEditLock (bool locked)\n{\n    mLocked = locked;\n    updateModificationButtons();\n}\n\nvoid CSVWorld::RecordButtonBar::universalIdChanged (const CSMWorld::UniversalId& id)\n{\n    mId = id;\n    updatePrevNextButtons();\n}\n\nvoid CSVWorld::RecordButtonBar::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==\"General Input/cycle\")\n        updatePrevNextButtons();\n}\n\nvoid CSVWorld::RecordButtonBar::cloneRequest()\n{\n    if (mBottom)\n    {\n        int typeColumn = mTable.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);\n\n        QModelIndex typeIndex = mTable.getModelIndex (mId.getId(), typeColumn);\n        CSMWorld::UniversalId::Type type = static_cast<CSMWorld::UniversalId::Type> (\n            mTable.data (typeIndex).toInt());\n\n        mBottom->cloneRequest (mId.getId(), type);\n    }\n}\n\nvoid CSVWorld::RecordButtonBar::nextId()\n{\n    int newRow = mTable.getModelIndex (mId.getId(), 0).row() + 1;\n\n    if (newRow >= mTable.rowCount())\n    {\n        if (CSMPrefs::get()[\"General Input\"][\"cycle\"].isTrue())\n            newRow = 0;\n        else\n            return;\n    }\n\n    emit switchToRow (newRow);\n}\n\nvoid CSVWorld::RecordButtonBar::prevId()\n{\n    int newRow = mTable.getModelIndex (mId.getId(), 0).row() - 1;\n\n    if (newRow < 0)\n    {\n        if (CSMPrefs::get()[\"General Input\"][\"cycle\"].isTrue())\n            newRow = mTable.rowCount()-1;\n        else\n            return;\n    }\n\n    emit switchToRow (newRow);\n}\n\nvoid CSVWorld::RecordButtonBar::rowNumberChanged (const QModelIndex& parent, int start, int end)\n{\n    updatePrevNextButtons();\n}\n"
  },
  {
    "path": "apps/opencs/view/world/recordbuttonbar.hpp",
    "content": "#ifndef CSV_WORLD_RECORDBUTTONBAR_H\n#define CSV_WORLD_RECORDBUTTONBAR_H\n\n#include <QWidget>\n\n#include \"../../model/world/universalid.hpp\"\n\nclass QToolButton;\nclass QModelIndex;\n\nnamespace CSMWorld\n{\n    class IdTable;\n    class CommandDispatcher;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWorld\n{\n    class TableBottomBox;\n\n    /// \\brief Button bar for use in dialogue-type subviews\n    ///\n    /// Contains the following buttons:\n    /// - next/prev\n    /// - clone\n    /// - add\n    /// - delete\n    /// - revert\n    /// - preview (optional)\n    /// - view (optional)\n    class RecordButtonBar : public QWidget\n    {\n            Q_OBJECT\n\n            CSMWorld::UniversalId mId;\n            CSMWorld::IdTable& mTable;\n            TableBottomBox *mBottom;\n            CSMWorld::CommandDispatcher *mCommandDispatcher;\n            QToolButton *mPrevButton;\n            QToolButton *mNextButton;\n            QToolButton *mCloneButton;\n            QToolButton *mAddButton;\n            QToolButton *mDeleteButton;\n            QToolButton *mRevertButton;\n            bool mLocked;\n\n        private:\n\n            void updateModificationButtons();\n\n            void updatePrevNextButtons();\n\n        public:\n\n            RecordButtonBar (const CSMWorld::UniversalId& id,\n                CSMWorld::IdTable& table, TableBottomBox *bottomBox = nullptr,\n                CSMWorld::CommandDispatcher *commandDispatcher = nullptr, QWidget *parent = nullptr);\n\n            void setEditLock (bool locked);\n\n        public slots:\n\n            void universalIdChanged (const CSMWorld::UniversalId& id);\n\n        private slots:\n\n            void settingChanged (const CSMPrefs::Setting *setting);\n\n            void cloneRequest();\n\n            void nextId();\n\n            void prevId();\n\n            void rowNumberChanged (const QModelIndex& parent, int start, int end);\n\n        signals:\n\n            void showPreview();\n\n            void viewRecord();\n\n            void switchToRow (int row);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/recordstatusdelegate.cpp",
    "content": "#include \"recordstatusdelegate.hpp\"\n\n#include <QPainter>\n#include <QApplication>\n#include <QUndoStack>\n\n#include \"../../model/world/columns.hpp\"\n\nCSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values,\n                                                     const IconList & icons,\n                                                     CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)\n    : DataDisplayDelegate (values, icons, dispatcher, document,\n                           \"Records\", \"status-format\",\n                           parent)\n{}\n\nCSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate (\n    CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const\n{\n    return new RecordStatusDelegate (mValues, mIcons, dispatcher, document, parent);\n}\n\nCSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()\n{\n    std::vector<std::pair<int,std::string>> enums =\n        CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);\n\n    static const char *sIcons[] =\n    {\n        \":list-base\", \":list-modified\", \":list-added\", \":list-removed\", \":list-removed\", 0\n    };\n\n    for (int i=0; sIcons[i]; ++i)\n    {\n        auto& enumPair = enums.at(i);\n        add (enumPair.first, enumPair.second.c_str(), sIcons[i]);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/recordstatusdelegate.hpp",
    "content": "#ifndef RECORDSTATUSDELEGATE_H\n#define RECORDSTATUSDELEGATE_H\n\n#include \"util.hpp\"\n#include <QTextOption>\n#include <QFont>\n\n#include \"datadisplaydelegate.hpp\"\n#include \"../../model/world/record.hpp\"\n\nclass QIcon;\nclass QFont;\n\nnamespace CSVWorld\n{\n    class RecordStatusDelegate : public DataDisplayDelegate\n    {\n    public:\n\n        RecordStatusDelegate (const ValueList& values, const IconList& icons,\n            CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document,\n            QObject *parent = nullptr);\n    };\n\n    class RecordStatusDelegateFactory : public DataDisplayDelegateFactory\n    {\n        public:\n\n            RecordStatusDelegateFactory();\n\n            CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const override;\n            ///< The ownership of the returned CommandDelegate is transferred to the caller.\n\n    };\n}\n#endif // RECORDSTATUSDELEGATE_HPP\n\n"
  },
  {
    "path": "apps/opencs/view/world/referenceablecreator.cpp",
    "content": "#include \"referenceablecreator.hpp\"\n\n#include <QComboBox>\n#include <QLabel>\n\n#include \"../../model/world/universalid.hpp\"\n#include \"../../model/world/commands.hpp\"\n\nvoid CSVWorld::ReferenceableCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const\n{\n    command.setType (\n        static_cast<CSMWorld::UniversalId::Type> (mType->itemData (mType->currentIndex()).toInt()));\n}\n\nCSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n    const CSMWorld::UniversalId& id)\n: GenericCreator (data, undoStack, id)\n{\n    QLabel *label = new QLabel (\"Type\", this);\n    insertBeforeButtons (label, false);\n\n    std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();\n\n    mType = new QComboBox (this);\n\n    for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());\n         iter!=types.end(); ++iter)\n    {\n        CSMWorld::UniversalId id2 (*iter, \"\");\n\n        mType->addItem (QIcon (id2.getIcon().c_str()), id2.getTypeName().c_str(),\n            static_cast<int> (id2.getType()));\n    }\n\n    insertBeforeButtons (mType, false);\n}\n\nvoid CSVWorld::ReferenceableCreator::reset()\n{\n    mType->setCurrentIndex (0);\n    GenericCreator::reset();\n}\n\nvoid CSVWorld::ReferenceableCreator::cloneMode (const std::string& originId,\n    const CSMWorld::UniversalId::Type type)\n{\n    GenericCreator::cloneMode (originId, type);\n    mType->setCurrentIndex (mType->findData (static_cast<int> (type)));\n}\n\nvoid CSVWorld::ReferenceableCreator::toggleWidgets(bool active)\n{\n    CSVWorld::GenericCreator::toggleWidgets(active);\n    mType->setEnabled(active);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/referenceablecreator.hpp",
    "content": "#ifndef CSV_WORLD_REFERENCEABLECREATOR_H\n#define CSV_WORLD_REFERENCEABLECREATOR_H\n\nclass QComboBox;\n\n#include \"genericcreator.hpp\"\n\nnamespace CSVWorld\n{\n    class ReferenceableCreator : public GenericCreator\n    {\n            Q_OBJECT\n\n            QComboBox *mType;\n\n        private:\n\n            void configureCreateCommand (CSMWorld::CreateCommand& command) const override;\n\n        public:\n\n            ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n                const CSMWorld::UniversalId& id);\n\n            void reset() override;\n\n            void cloneMode (const std::string& originId,\n                const CSMWorld::UniversalId::Type type) override;\n\n            void toggleWidgets(bool active = true) override;\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/referencecreator.cpp",
    "content": "#include \"referencecreator.hpp\"\n\n#include <QLabel>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/idcompletionmanager.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n\n#include \"../widget/droplineedit.hpp\"\n\nstd::string CSVWorld::ReferenceCreator::getId() const\n{\n    return mId;\n}\n\nvoid CSVWorld::ReferenceCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const\n{\n    // Set cellID\n    int cellIdColumn =\n        dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).\n        findColumnIndex (CSMWorld::Columns::ColumnId_Cell);\n\n    command.addValue (cellIdColumn, mCell->text());\n}\n\nCSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n    const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager &completionManager)\n: GenericCreator (data, undoStack, id)\n{\n    QLabel *label = new QLabel (\"Cell\", this);\n    insertBeforeButtons (label, false);\n\n    // Add cell ID input with auto-completion.\n    // Only existing cell IDs are accepted so no ID validation is performed.\n    mCell = new CSVWidget::DropLineEdit(CSMWorld::ColumnBase::Display_Cell, this);\n    mCell->setCompleter(completionManager.getCompleter(CSMWorld::ColumnBase::Display_Cell).get());\n    insertBeforeButtons (mCell, true);\n\n    setManualEditing (false);\n\n    connect (mCell, SIGNAL (textChanged (const QString&)), this, SLOT (cellChanged()));\n    connect (mCell, SIGNAL (returnPressed()), this, SLOT (inputReturnPressed()));\n}\n\nvoid CSVWorld::ReferenceCreator::reset()\n{\n    GenericCreator::reset();\n    mCell->setText (\"\");\n    mId = getData().getReferences().getNewId();\n}\n\nstd::string CSVWorld::ReferenceCreator::getErrors() const\n{\n    // We are ignoring errors coming from GenericCreator here, because the ID of the new\n    // record is internal and requires neither user input nor verification.\n    std::string errors;\n\n    std::string cell = mCell->text().toUtf8().constData();\n\n    if (cell.empty())\n        errors += \"Missing Cell ID\";\n    else if (getData().getCells().searchId (cell)==-1)\n        errors += \"Invalid Cell ID\";\n\n    return errors;\n}\n\nvoid CSVWorld::ReferenceCreator::focus()\n{\n    mCell->setFocus();\n}\n\nvoid CSVWorld::ReferenceCreator::cellChanged()\n{\n    update();\n}\n\nvoid CSVWorld::ReferenceCreator::cloneMode(const std::string& originId,\n                                           const CSMWorld::UniversalId::Type type)\n{\n    CSMWorld::IdTable& referenceTable = dynamic_cast<CSMWorld::IdTable&> (\n        *getData().getTableModel (CSMWorld::UniversalId::Type_References));\n\n    int cellIdColumn = referenceTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);\n\n    mCell->setText (\n        referenceTable.data (referenceTable.getModelIndex (originId, cellIdColumn)).toString());\n\n    CSVWorld::GenericCreator::cloneMode(originId, type);\n    cellChanged(); //otherwise ok button will remain disabled\n}\n\nCSVWorld::Creator *CSVWorld::ReferenceCreatorFactory::makeCreator (CSMDoc::Document& document,\n                                                                   const CSMWorld::UniversalId& id) const\n{\n    return new ReferenceCreator(document.getData(),\n                                document.getUndoStack(),\n                                id,\n                                document.getIdCompletionManager());\n}\n"
  },
  {
    "path": "apps/opencs/view/world/referencecreator.hpp",
    "content": "#ifndef CSV_WORLD_REFERENCECREATOR_H\n#define CSV_WORLD_REFERENCECREATOR_H\n\n#include \"genericcreator.hpp\"\n\nnamespace CSMWorld\n{\n    class IdCompletionManager;\n}\n\nnamespace CSVWidget\n{\n    class DropLineEdit;\n}\n\nnamespace CSVWorld\n{\n\n    class ReferenceCreator : public GenericCreator\n    {\n            Q_OBJECT\n\n            CSVWidget::DropLineEdit *mCell;\n            std::string mId;\n\n        private:\n\n            std::string getId() const override;\n\n            void configureCreateCommand (CSMWorld::CreateCommand& command) const override;\n\n        public:\n\n            ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack,\n                const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager &completionManager);\n\n            void cloneMode(const std::string& originId,\n                                   const CSMWorld::UniversalId::Type type) override;\n\n            void reset() override;\n\n            std::string getErrors() const override;\n            ///< Return formatted error descriptions for the current state of the creator. if an empty\n            /// string is returned, there is no error.\n\n            /// Focus main input widget\n            void focus() override;\n\n        private slots:\n\n            void cellChanged();\n    };\n\n    class ReferenceCreatorFactory : public CreatorFactoryBase\n    {\n        public:\n\n            Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const override;\n            ///< The ownership of the returned Creator is transferred to the caller.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/regionmap.cpp",
    "content": "#include \"regionmap.hpp\"\n\n#include <algorithm>\n#include <set>\n#include <sstream>\n\n#include <QHeaderView>\n#include <QContextMenuEvent>\n#include <QMenu>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/regionmap.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n\nvoid CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event)\n{\n    QMenu menu (this);\n\n    if (getUnselectedCells().size()>0)\n        menu.addAction (mSelectAllAction);\n\n    if (selectionModel()->selectedIndexes().size()>0)\n        menu.addAction (mClearSelectionAction);\n\n    if (getMissingRegionCells().size()>0)\n        menu.addAction (mSelectRegionsAction);\n\n    int selectedNonExistentCells = getSelectedCells (false, true).size();\n\n    if (selectedNonExistentCells>0)\n    {\n        if (selectedNonExistentCells==1)\n            mCreateCellsAction->setText (\"Create one Cell\");\n        else\n        {\n            std::ostringstream stream;\n            stream << \"Create \" << selectedNonExistentCells << \" cells\";\n            mCreateCellsAction->setText (QString::fromUtf8 (stream.str().c_str()));\n        }\n\n        menu.addAction (mCreateCellsAction);\n    }\n\n    if (getSelectedCells().size()>0)\n    {\n        if (!mRegionId.empty())\n        {\n            mSetRegionAction->setText (QString::fromUtf8 ((\"Set Region to \" + mRegionId).c_str()));\n            menu.addAction (mSetRegionAction);\n        }\n\n        menu.addAction (mUnsetRegionAction);\n\n        menu.addAction (mViewInTableAction);\n    }\n\n    if (selectionModel()->selectedIndexes().size()>0)\n        menu.addAction (mViewAction);\n\n    menu.exec (event->globalPos());\n}\n\nQModelIndexList CSVWorld::RegionMap::getUnselectedCells() const\n{\n    const QAbstractItemModel *model = QTableView::model();\n\n    int rows = model->rowCount();\n    int columns = model->columnCount();\n\n    QModelIndexList selected = selectionModel()->selectedIndexes();\n    std::sort (selected.begin(), selected.end());\n\n    QModelIndexList all;\n\n    for (int y=0; y<rows; ++y)\n        for (int x=0; x<columns; ++x)\n        {\n            QModelIndex index = model->index (y, x);\n            if (model->data (index, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern))\n                all.push_back (index);\n        }\n\n    std::sort (all.begin(), all.end());\n\n    QModelIndexList list;\n\n    std::set_difference (all.begin(), all.end(), selected.begin(), selected.end(),\n        std::back_inserter (list));\n\n    return list;\n}\n\nQModelIndexList CSVWorld::RegionMap::getSelectedCells (bool existent, bool nonExistent) const\n{\n    const QAbstractItemModel *model = QTableView::model();\n\n    QModelIndexList selected = selectionModel()->selectedIndexes();\n\n    QModelIndexList list;\n\n    for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter)\n    {\n        bool exists = model->data (*iter, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern);\n\n        if ((exists && existent) || (!exists && nonExistent))\n             list.push_back (*iter);\n    }\n\n    return list;\n}\n\nQModelIndexList CSVWorld::RegionMap::getMissingRegionCells() const\n{\n    const QAbstractItemModel *model = QTableView::model();\n\n    QModelIndexList selected = selectionModel()->selectedIndexes();\n\n    std::set<std::string> regions;\n\n    for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter)\n    {\n        std::string region =\n            model->data (*iter, CSMWorld::RegionMap::Role_Region).toString().toUtf8().constData();\n\n        if (!region.empty())\n            regions.insert (region);\n    }\n\n    QModelIndexList list;\n\n    QModelIndexList unselected = getUnselectedCells();\n\n    for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter)\n    {\n        std::string region =\n            model->data (*iter, CSMWorld::RegionMap::Role_Region).toString().toUtf8().constData();\n\n        if (!region.empty() && regions.find (region)!=regions.end())\n            list.push_back (*iter);\n    }\n\n    return list;\n}\n\nvoid CSVWorld::RegionMap::setRegion (const std::string& regionId)\n{\n    QModelIndexList selected = getSelectedCells();\n\n    QAbstractItemModel *regionModel = model();\n\n    CSMWorld::IdTable *cellsModel = &dynamic_cast<CSMWorld::IdTable&> (*\n        mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));\n\n    QString regionId2 = QString::fromUtf8 (regionId.c_str());\n\n    CSMWorld::CommandMacro macro (mDocument.getUndoStack(), selected.size()>1 ? tr (\"Set Region\") : \"\");\n\n    for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter)\n    {\n        std::string cellId = regionModel->data (*iter, CSMWorld::RegionMap::Role_CellId).\n            toString().toUtf8().constData();\n\n        QModelIndex index = cellsModel->getModelIndex (cellId,\n            cellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Region));\n\n        macro.push (new CSMWorld::ModifyCommand (*cellsModel, index, regionId2));\n    }\n}\n\nCSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId,\n    CSMDoc::Document& document, QWidget *parent)\n:  DragRecordTable(document, parent)\n{\n    verticalHeader()->hide();\n    horizontalHeader()->hide();\n\n    setSelectionMode (QAbstractItemView::ExtendedSelection);\n\n    setModel (document.getData().getTableModel (universalId));\n\n    resizeColumnsToContents();\n    resizeRowsToContents();\n\n    mSelectAllAction = new QAction (tr (\"Select All\"), this);\n    connect (mSelectAllAction, SIGNAL (triggered()), this, SLOT (selectAll()));\n    addAction (mSelectAllAction);\n\n    mClearSelectionAction = new QAction (tr (\"Clear Selection\"), this);\n    connect (mClearSelectionAction, SIGNAL (triggered()), this, SLOT (clearSelection()));\n    addAction (mClearSelectionAction);\n\n    mSelectRegionsAction = new QAction (tr (\"Select Regions\"), this);\n    connect (mSelectRegionsAction, SIGNAL (triggered()), this, SLOT (selectRegions()));\n    addAction (mSelectRegionsAction);\n\n    mCreateCellsAction = new QAction (tr (\"Create Cells Action\"), this);\n    connect (mCreateCellsAction, SIGNAL (triggered()), this, SLOT (createCells()));\n    addAction (mCreateCellsAction);\n\n    mSetRegionAction = new QAction (tr (\"Set Region\"), this);\n    connect (mSetRegionAction, SIGNAL (triggered()), this, SLOT (setRegion()));\n    addAction (mSetRegionAction);\n\n    mUnsetRegionAction = new QAction (tr (\"Unset Region\"), this);\n    connect (mUnsetRegionAction, SIGNAL (triggered()), this, SLOT (unsetRegion()));\n    addAction (mUnsetRegionAction);\n\n    mViewAction = new QAction (tr (\"View Cells\"), this);\n    connect (mViewAction, SIGNAL (triggered()), this, SLOT (view()));\n    addAction (mViewAction);\n\n    mViewInTableAction = new QAction (tr (\"View Cells in Table\"), this);\n    connect (mViewInTableAction, SIGNAL (triggered()), this, SLOT (viewInTable()));\n    addAction (mViewInTableAction);\n\n    setAcceptDrops(true);\n}\n\nvoid CSVWorld::RegionMap::selectAll()\n{\n    QModelIndexList unselected = getUnselectedCells();\n\n    for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter)\n        selectionModel()->select (*iter, QItemSelectionModel::Select);\n}\n\nvoid CSVWorld::RegionMap::clearSelection()\n{\n    selectionModel()->clearSelection();\n}\n\nvoid CSVWorld::RegionMap::selectRegions()\n{\n    QModelIndexList unselected = getMissingRegionCells();\n\n    for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter)\n        selectionModel()->select (*iter, QItemSelectionModel::Select);\n}\n\nvoid CSVWorld::RegionMap::createCells()\n{\n    if (mEditLock)\n        return;\n\n    QModelIndexList selected = getSelectedCells (false, true);\n\n    CSMWorld::IdTable *cellsModel = &dynamic_cast<CSMWorld::IdTable&> (*\n        mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));\n\n    CSMWorld::CommandMacro macro (mDocument.getUndoStack(), selected.size()>1 ? tr (\"Create cells\"): \"\");\n\n    for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter)\n    {\n        std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId).\n            toString().toUtf8().constData();\n\n        macro.push (new CSMWorld::CreateCommand (*cellsModel, cellId));\n    }\n}\n\nvoid CSVWorld::RegionMap::setRegion()\n{\n    if (mEditLock)\n        return;\n\n    setRegion (mRegionId);\n}\n\nvoid CSVWorld::RegionMap::unsetRegion()\n{\n    if (mEditLock)\n        return;\n\n    setRegion (\"\");\n}\n\nvoid CSVWorld::RegionMap::view()\n{\n    std::ostringstream hint;\n    hint << \"c:\";\n\n    QModelIndexList selected = selectionModel()->selectedIndexes();\n\n    bool first = true;\n\n    for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter)\n    {\n        std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId).\n            toString().toUtf8().constData();\n\n        if (first)\n            first = false;\n        else\n            hint << \"; \";\n\n        hint << cellId;\n    }\n\n    emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Scene, ESM::CellId::sDefaultWorldspace),\n        hint.str());\n}\n\nvoid CSVWorld::RegionMap::viewInTable()\n{\n    std::ostringstream hint;\n    hint << \"f:!or(\";\n\n    QModelIndexList selected = getSelectedCells();\n\n    bool first = true;\n\n    for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter)\n    {\n        std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId).\n            toString().toUtf8().constData();\n\n        if (first)\n            first = false;\n        else\n            hint << \",\";\n\n        hint << \"string(ID,\\\"\" << cellId << \"\\\")\";\n    }\n\n    hint << \")\";\n\n    emit editRequest (CSMWorld::UniversalId::Type_Cells, hint.str());\n}\n\nvoid CSVWorld::RegionMap::mouseMoveEvent (QMouseEvent* event)\n{\n    startDragFromTable(*this);\n}\n\nstd::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() const\n{\n    QModelIndexList selected(getSelectedCells(true, false));\n    std::vector<CSMWorld::UniversalId> ids;\n    for (const QModelIndex& it : selected)\n    {\n        ids.emplace_back(\n                CSMWorld::UniversalId::Type_Cell,\n                model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData());\n    }\n    selected = getSelectedCells(false, true);\n    for (const QModelIndex& it : selected)\n    {\n        ids.emplace_back(\n                CSMWorld::UniversalId::Type_Cell_Missing,\n                model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData());\n    }\n    return ids;\n}\n\nvoid CSVWorld::RegionMap::dropEvent (QDropEvent* event)\n{\n    QModelIndex index = indexAt (event->pos());\n\n    bool exists = QTableView::model()->data(index, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern);\n\n    if (!index.isValid() || !exists)\n    {\n        return;\n    }\n\n    const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n    if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped\n        return;\n\n    if (mime->fromDocument(mDocument) && mime->holdsType(CSMWorld::UniversalId::Type_Region))\n    {\n        CSMWorld::UniversalId record (mime->returnMatching (CSMWorld::UniversalId::Type_Region));\n\n        QAbstractItemModel *regionModel = model();\n\n        CSMWorld::IdTable *cellsModel = &dynamic_cast<CSMWorld::IdTable&> (*\n            mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));\n\n        std::string cellId(regionModel->data (index, CSMWorld::RegionMap::Role_CellId).\n            toString().toUtf8().constData());\n\n        QModelIndex index2(cellsModel->getModelIndex (cellId,\n            cellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Region)));\n\n        mDocument.getUndoStack().push(new CSMWorld::ModifyCommand\n                                        (*cellsModel, index2, QString::fromUtf8(record.getId().c_str())));\n\n        mRegionId = record.getId();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/regionmap.hpp",
    "content": "#ifndef CSV_WORLD_REGIONMAP_H\n#define CSV_WORLD_REGIONMAP_H\n\n#include <cstddef>\n#include <vector>\n\n#include <QObject>\n#include <QTableView>\n\n#include \"./dragrecordtable.hpp\"\n\nclass QAction;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class UniversalId;\n}\n\nnamespace CSVWorld\n{\n    class RegionMap : public DragRecordTable\n    {\n            Q_OBJECT\n\n            QAction *mSelectAllAction;\n            QAction *mClearSelectionAction;\n            QAction *mSelectRegionsAction;\n            QAction *mCreateCellsAction;\n            QAction *mSetRegionAction;\n            QAction *mUnsetRegionAction;\n            QAction *mViewAction;\n            QAction *mViewInTableAction;\n            std::string mRegionId;\n\n        private:\n\n            void contextMenuEvent (QContextMenuEvent *event) override;\n\n            QModelIndexList getUnselectedCells() const;\n            ///< \\note Non-existent cells are not listed.\n\n            QModelIndexList getSelectedCells (bool existent = true, bool nonExistent = false) const;\n            ///< \\param existent Include existent cells.\n            /// \\param nonExistent Include non-existent cells.\n\n            QModelIndexList getMissingRegionCells() const;\n            ///< Unselected cells within all regions that have at least one selected cell.\n\n            void setRegion (const std::string& regionId);\n            ///< Set region Id of selected cells.\n\n            void mouseMoveEvent(QMouseEvent *event) override;\n\n            void dropEvent(QDropEvent* event) override;\n\n        public:\n\n            RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document,\n                QWidget *parent = nullptr);\n\n            std::vector<CSMWorld::UniversalId> getDraggedRecords() const override;\n\n        signals:\n\n            void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);\n\n        private slots:\n\n            void selectAll() override;\n\n            void clearSelection();\n\n            void selectRegions();\n\n            void createCells();\n\n            void setRegion();\n\n            void unsetRegion();\n\n            void view();\n\n            void viewInTable();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/regionmapsubview.cpp",
    "content": "#include \"regionmapsubview.hpp\"\n\n#include \"regionmap.hpp\"\n\nCSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId,\n    CSMDoc::Document& document)\n: CSVDoc::SubView (universalId)\n{\n    mRegionMap = new RegionMap (universalId, document, this);\n\n    setWidget (mRegionMap);\n\n    connect (mRegionMap, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)),\n        this, SLOT (editRequest (const CSMWorld::UniversalId&, const std::string&)));\n}\n\nvoid CSVWorld::RegionMapSubView::setEditLock (bool locked)\n{\n    mRegionMap->setEditLock (locked);\n}\n\nvoid CSVWorld::RegionMapSubView::editRequest (const CSMWorld::UniversalId& id,\n    const std::string& hint)\n{\n    focusId (id, hint);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/regionmapsubview.hpp",
    "content": "#ifndef CSV_WORLD_REGIONMAPSUBVIEW_H\n#define CSV_WORLD_REGIONMAPSUBVIEW_H\n\n#include \"../doc/subview.hpp\"\n\nclass QAction;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVWorld\n{\n    class RegionMap;\n\n    class RegionMapSubView : public CSVDoc::SubView\n    {\n            Q_OBJECT\n\n            RegionMap *mRegionMap;\n\n        public:\n\n            RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document);\n\n            void setEditLock (bool locked) override;\n\n        private slots:\n\n            void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/scenesubview.cpp",
    "content": "#include \"scenesubview.hpp\"\n\n#include <sstream>\n\n#include <QVBoxLayout>\n#include <QHBoxLayout>\n#include <QLabel>\n#include <cassert>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/cellselection.hpp\"\n\n#include \"../filter/filterbox.hpp\"\n\n#include \"../render/pagedworldspacewidget.hpp\"\n#include \"../render/unpagedworldspacewidget.hpp\"\n\n#include \"../widget/scenetoolbar.hpp\"\n#include \"../widget/scenetoolmode.hpp\"\n#include \"../widget/scenetooltoggle.hpp\"\n#include \"../widget/scenetooltoggle2.hpp\"\n#include \"../widget/scenetoolrun.hpp\"\n\n#include \"tablebottombox.hpp\"\n#include \"creator.hpp\"\n\nCSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)\n: SubView (id), mScene(nullptr), mLayout(new QHBoxLayout), mDocument(document), mToolbar(nullptr)\n{\n    QVBoxLayout *layout = new QVBoxLayout;\n\n    layout->addWidget (mBottom = new TableBottomBox (NullCreatorFactory(), document, id, this), 0);\n\n    mLayout->setContentsMargins (QMargins (0, 0, 0, 0));\n\n    CSVRender::WorldspaceWidget* worldspaceWidget = nullptr;\n    widgetType whatWidget;\n\n    if (id.getId()==ESM::CellId::sDefaultWorldspace)\n    {\n        whatWidget = widget_Paged;\n\n        CSVRender::PagedWorldspaceWidget *newWidget = new CSVRender::PagedWorldspaceWidget (this, document);\n\n        worldspaceWidget = newWidget;\n\n        makeConnections(newWidget);\n    }\n    else\n    {\n        whatWidget = widget_Unpaged;\n\n        CSVRender::UnpagedWorldspaceWidget *newWidget = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this);\n\n        worldspaceWidget = newWidget;\n\n        makeConnections(newWidget);\n    }\n\n    replaceToolbarAndWorldspace(worldspaceWidget, makeToolbar(worldspaceWidget, whatWidget));\n\n    layout->insertLayout (0, mLayout, 1);\n\n    CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this);\n\n    layout->insertWidget (0, filterBox);\n\n    QWidget *widget = new QWidget;\n\n    widget->setLayout (layout);\n\n    setWidget (widget);\n}\n\nvoid CSVWorld::SceneSubView::makeConnections (CSVRender::UnpagedWorldspaceWidget* widget)\n{\n    connect (widget, SIGNAL (closeRequest()), this, SLOT (closeRequest()));\n\n    connect(widget, SIGNAL(dataDropped(const std::vector<CSMWorld::UniversalId>&)),\n            this, SLOT(handleDrop(const std::vector<CSMWorld::UniversalId>&)));\n\n    connect(widget, SIGNAL(cellChanged(const CSMWorld::UniversalId&)),\n            this, SLOT(cellSelectionChanged(const CSMWorld::UniversalId&)));\n\n    connect(widget, SIGNAL(requestFocus (const std::string&)),\n            this, SIGNAL(requestFocus (const std::string&)));\n}\n\nvoid CSVWorld::SceneSubView::makeConnections (CSVRender::PagedWorldspaceWidget* widget)\n{\n    connect (widget, SIGNAL (closeRequest()), this, SLOT (closeRequest()));\n\n    connect(widget, SIGNAL(dataDropped(const std::vector<CSMWorld::UniversalId>&)),\n            this, SLOT(handleDrop(const std::vector<CSMWorld::UniversalId>&)));\n\n    connect (widget, SIGNAL (cellSelectionChanged (const CSMWorld::CellSelection&)),\n             this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&)));\n\n    connect(widget, SIGNAL(requestFocus (const std::string&)),\n            this, SIGNAL(requestFocus (const std::string&)));\n}\n\nCSVWidget::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type)\n{\n    CSVWidget::SceneToolbar* toolbar = new CSVWidget::SceneToolbar (48+6, this);\n\n    CSVWidget::SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar);\n    toolbar->addTool (navigationTool);\n\n    CSVWidget::SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar);\n    toolbar->addTool (lightingTool);\n\n    CSVWidget::SceneToolToggle2 *sceneVisibilityTool =\n        widget->makeSceneVisibilitySelector (toolbar);\n    toolbar->addTool (sceneVisibilityTool);\n\n    if (type==widget_Paged)\n    {\n        CSVWidget::SceneToolToggle2 *controlVisibilityTool =\n            dynamic_cast<CSVRender::PagedWorldspaceWidget&> (*widget).\n            makeControlVisibilitySelector (toolbar);\n\n        toolbar->addTool (controlVisibilityTool);\n    }\n\n    CSVWidget::SceneToolRun *runTool = widget->makeRunTool (toolbar);\n    toolbar->addTool (runTool);\n\n    toolbar->addTool (widget->makeEditModeSelector (toolbar), runTool);\n\n    return toolbar;\n}\n\nvoid CSVWorld::SceneSubView::setEditLock (bool locked)\n{\n    mScene->setEditLock (locked);\n}\n\nvoid CSVWorld::SceneSubView::setStatusBar (bool show)\n{\n    mBottom->setStatusBar (show);\n}\n\nvoid CSVWorld::SceneSubView::useHint (const std::string& hint)\n{\n    mScene->useViewHint (hint);\n}\n\nstd::string CSVWorld::SceneSubView::getTitle() const\n{\n    return mTitle;\n}\n\nvoid CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId& id)\n{\n    setUniversalId(id);\n\n    mTitle = \"Scene: \" + getUniversalId().getId();\n    setWindowTitle (QString::fromUtf8 (mTitle.c_str()));\n    emit updateTitle();\n}\n\nvoid CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection& selection)\n{\n    setUniversalId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, ESM::CellId::sDefaultWorldspace));\n    int size = selection.getSize();\n\n    std::ostringstream stream;\n    stream << \"Scene: \" << getUniversalId().getId();\n\n    if (size==0)\n        stream << \" (empty)\";\n    else if (size==1)\n    {\n        stream << \" (\" << *selection.begin() << \")\";\n    }\n    else\n    {\n        stream << \" (\" << selection.getCentre() << \" and \" << size-1 << \" more \";\n\n        if (size>1)\n            stream << \"cells around it)\";\n        else\n            stream << \"cell around it)\";\n    }\n\n    mTitle = stream.str();\n    setWindowTitle (QString::fromUtf8 (mTitle.c_str()));\n    emit updateTitle();\n}\n\nvoid CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& universalIdData)\n{\n    CSVRender::PagedWorldspaceWidget* pagedNewWidget = nullptr;\n    CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = nullptr;\n    CSVWidget::SceneToolbar* toolbar = nullptr;\n\n    CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (universalIdData);\n\n    switch (mScene->getDropRequirements (type))\n    {\n        case CSVRender::WorldspaceWidget::canHandle:\n            mScene->handleDrop (universalIdData, type);\n            break;\n\n        case CSVRender::WorldspaceWidget::needPaged:\n            pagedNewWidget = new CSVRender::PagedWorldspaceWidget(this, mDocument);\n            toolbar = makeToolbar(pagedNewWidget, widget_Paged);\n            makeConnections(pagedNewWidget);\n            replaceToolbarAndWorldspace(pagedNewWidget, toolbar);\n            mScene->handleDrop (universalIdData, type);\n            break;\n\n        case CSVRender::WorldspaceWidget::needUnpaged:\n            unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(universalIdData.begin()->getId(), mDocument, this);\n            toolbar = makeToolbar(unPagedNewWidget, widget_Unpaged);\n            makeConnections(unPagedNewWidget);\n            replaceToolbarAndWorldspace(unPagedNewWidget, toolbar);\n            cellSelectionChanged(*(universalIdData.begin()));\n            break;\n\n        case CSVRender::WorldspaceWidget::ignored:\n            return;\n    }\n}\n\nvoid CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar)\n{\n    assert(mLayout);\n\n    if (mScene)\n    {\n        mLayout->removeWidget(mScene);\n        mScene->deleteLater();\n    }\n\n    if (mToolbar)\n    {\n        mLayout->removeWidget(mToolbar);\n        mToolbar->deleteLater();\n    }\n\n    mScene = widget;\n    mToolbar = toolbar;\n\n    connect (mScene, SIGNAL (focusToolbarRequest()), mToolbar, SLOT (setFocus()));\n    connect (mToolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus()));\n\n    mLayout->addWidget (mToolbar, 0);\n    mLayout->addWidget (mScene, 1);\n\n    mScene->selectDefaultNavigationMode();\n    setFocusProxy (mScene);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/scenesubview.hpp",
    "content": "#ifndef CSV_WORLD_SCENESUBVIEW_H\n#define CSV_WORLD_SCENESUBVIEW_H\n\n#include <QHBoxLayout>\n\n#include \"../doc/subview.hpp\"\n\nclass QModelIndex;\n\nnamespace CSMWorld\n{\n    class CellSelection;\n}\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVRender\n{\n    class WorldspaceWidget;\n    class PagedWorldspaceWidget;\n    class UnpagedWorldspaceWidget;\n}\n\nnamespace CSVWidget\n{\n    class SceneToolbar;\n    class SceneToolMode;\n}\n\nnamespace CSVWorld\n{\n    class Table;\n    class TableBottomBox;\n    class CreatorFactoryBase;\n\n    class SceneSubView : public CSVDoc::SubView\n    {\n            Q_OBJECT\n\n            TableBottomBox *mBottom;\n            CSVRender::WorldspaceWidget *mScene;\n            QHBoxLayout* mLayout;\n            CSMDoc::Document& mDocument;\n            CSVWidget::SceneToolbar* mToolbar;\n            std::string mTitle;\n\n        public:\n\n            SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);\n\n            void setEditLock (bool locked) override;\n\n            void setStatusBar (bool show) override;\n\n            void useHint (const std::string& hint) override;\n\n            std::string getTitle() const override;\n\n        private:\n\n            void makeConnections(CSVRender::PagedWorldspaceWidget* widget);\n\n            void makeConnections(CSVRender::UnpagedWorldspaceWidget* widget);\n\n            void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar);\n\n            enum widgetType\n            {\n                widget_Paged,\n                widget_Unpaged\n            };\n\n            CSVWidget::SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type);\n\n        private slots:\n\n            void cellSelectionChanged (const CSMWorld::CellSelection& selection);\n\n            void cellSelectionChanged (const CSMWorld::UniversalId& id);\n\n            void handleDrop(const std::vector<CSMWorld::UniversalId>& data);\n\n        signals:\n\n            void requestFocus (const std::string& id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/scriptedit.cpp",
    "content": "#include \"scriptedit.hpp\"\n\n#include <algorithm>\n\n#include <QDragEnterEvent>\n#include <QRegExp>\n#include <QString>\n#include <QPainter>\n#include <QTextDocumentFragment>\n#include <QMenu>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/universalid.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/prefs/shortcut.hpp\"\n\nCSVWorld::ScriptEdit::ChangeLock::ChangeLock (ScriptEdit& edit) : mEdit (edit)\n{\n    ++mEdit.mChangeLocked;\n}\n\nCSVWorld::ScriptEdit::ChangeLock::~ChangeLock()\n{\n    --mEdit.mChangeLocked;\n}\n\nbool CSVWorld::ScriptEdit::event (QEvent *event)\n{\n    // ignore undo and redo shortcuts\n    if (event->type()==QEvent::ShortcutOverride)\n    {\n        QKeyEvent *keyEvent = static_cast<QKeyEvent *> (event);\n\n        if (keyEvent->matches (QKeySequence::Undo) || keyEvent->matches (QKeySequence::Redo))\n            return true;\n    }\n\n    return QPlainTextEdit::event (event);\n}\n\nCSVWorld::ScriptEdit::ScriptEdit(\n    const CSMDoc::Document& document,\n    ScriptHighlighter::Mode mode,\n    QWidget* parent\n) : QPlainTextEdit(parent),\n    mChangeLocked(0),\n    mShowLineNum(false),\n    mLineNumberArea(nullptr),\n    mDefaultFont(font()),\n    mMonoFont(QFont(\"Monospace\")),\n    mTabCharCount(4),\n    mMarkOccurrences(true),\n    mDocument(document),\n    mWhiteListQoutes(\"^[a-z|_]{1}[a-z|0-9|_]{0,}$\", Qt::CaseInsensitive)\n{\n    wrapLines(false);\n    setTabWidth();\n    setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead\n\n    mAllowedTypes <<CSMWorld::UniversalId::Type_Journal\n                  <<CSMWorld::UniversalId::Type_Global\n                  <<CSMWorld::UniversalId::Type_Topic\n                  <<CSMWorld::UniversalId::Type_Sound\n                  <<CSMWorld::UniversalId::Type_Spell\n                  <<CSMWorld::UniversalId::Type_Cell\n                  <<CSMWorld::UniversalId::Type_Referenceable\n                  <<CSMWorld::UniversalId::Type_Activator\n                  <<CSMWorld::UniversalId::Type_Potion\n                  <<CSMWorld::UniversalId::Type_Apparatus\n                  <<CSMWorld::UniversalId::Type_Armor\n                  <<CSMWorld::UniversalId::Type_Book\n                  <<CSMWorld::UniversalId::Type_Clothing\n                  <<CSMWorld::UniversalId::Type_Container\n                  <<CSMWorld::UniversalId::Type_Creature\n                  <<CSMWorld::UniversalId::Type_Door\n                  <<CSMWorld::UniversalId::Type_Ingredient\n                  <<CSMWorld::UniversalId::Type_CreatureLevelledList\n                  <<CSMWorld::UniversalId::Type_ItemLevelledList\n                  <<CSMWorld::UniversalId::Type_Light\n                  <<CSMWorld::UniversalId::Type_Lockpick\n                  <<CSMWorld::UniversalId::Type_Miscellaneous\n                  <<CSMWorld::UniversalId::Type_Npc\n                  <<CSMWorld::UniversalId::Type_Probe\n                  <<CSMWorld::UniversalId::Type_Repair\n                  <<CSMWorld::UniversalId::Type_Static\n                  <<CSMWorld::UniversalId::Type_Weapon\n                  <<CSMWorld::UniversalId::Type_Script\n                  <<CSMWorld::UniversalId::Type_Region;\n    \n    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(markOccurrences()));\n\n    mCommentAction = new QAction (tr (\"Comment Selection\"), this);\n    connect(mCommentAction, SIGNAL (triggered()), this, SLOT (commentSelection()));\n    CSMPrefs::Shortcut *commentShortcut = new CSMPrefs::Shortcut(\"script-editor-comment\", this);\n    commentShortcut->associateAction(mCommentAction);\n\n    mUncommentAction = new QAction (tr (\"Uncomment Selection\"), this);\n    connect(mUncommentAction, SIGNAL (triggered()), this, SLOT (uncommentSelection()));\n    CSMPrefs::Shortcut *uncommentShortcut = new CSMPrefs::Shortcut(\"script-editor-uncomment\", this);\n    uncommentShortcut->associateAction(mUncommentAction);\n\n    mHighlighter = new ScriptHighlighter (document.getData(), mode, ScriptEdit::document());\n\n    connect (&document.getData(), SIGNAL (idListChanged()), this, SLOT (idListChanged()));\n\n    connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting()));\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n    {\n        ChangeLock lock (*this);\n        CSMPrefs::get()[\"Scripts\"].update();\n    }\n\n    mUpdateTimer.setSingleShot (true);\n\n    // TODO: provide a font selector dialogue\n    mMonoFont.setStyleHint(QFont::TypeWriter);\n\n    mLineNumberArea = new LineNumberArea(this);\n    updateLineNumberAreaWidth(0);\n\n    connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));\n    connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));\n    updateHighlighting();\n}\n\nvoid CSVWorld::ScriptEdit::showLineNum(bool show)\n{\n    if(show!=mShowLineNum)\n    {\n        mShowLineNum = show;\n        updateLineNumberAreaWidth(0);\n    }\n}\n\nbool CSVWorld::ScriptEdit::isChangeLocked() const\n{\n    return mChangeLocked!=0;\n}\n\nvoid CSVWorld::ScriptEdit::dragEnterEvent (QDragEnterEvent* event)\n{\n    const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n    if (!mime)\n        QPlainTextEdit::dragEnterEvent(event);\n    else\n    {\n        setTextCursor (cursorForPosition (event->pos()));\n        event->acceptProposedAction();\n    }\n}\n\nvoid CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event)\n{\n    const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n    if (!mime)\n        QPlainTextEdit::dragMoveEvent(event);\n    else\n    {\n        setTextCursor (cursorForPosition (event->pos()));\n        event->accept();\n    }\n}\n\nvoid CSVWorld::ScriptEdit::dropEvent (QDropEvent* event)\n{\n    const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());\n    if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped\n    {\n        QPlainTextEdit::dropEvent(event);\n        return;\n    }\n\n    setTextCursor (cursorForPosition (event->pos()));\n\n    if (mime->fromDocument (mDocument))\n    {\n        std::vector<CSMWorld::UniversalId> records (mime->getData());\n\n        for (std::vector<CSMWorld::UniversalId>::iterator it = records.begin(); it != records.end(); ++it)\n        {\n            if (mAllowedTypes.contains (it->getType()))\n            {\n                if (stringNeedsQuote(it->getId()))\n                {\n                    insertPlainText(QString::fromUtf8 (('\"' + it->getId() + '\"').c_str()));\n                } else {\n                    insertPlainText(QString::fromUtf8 (it->getId().c_str()));\n                }\n            }\n        }\n    }\n}\n\nbool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const\n{\n    const QString string(QString::fromUtf8(id.c_str())); //<regex> is only for c++11, so let's use qregexp for now.\n    //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than…\n    return !(string.contains(mWhiteListQoutes));\n}\n\nvoid CSVWorld::ScriptEdit::setTabWidth()\n{\n    // Set tab width to specified number of characters using current font.\n    setTabStopDistance(mTabCharCount * fontMetrics().horizontalAdvance(' '));\n}\n\nvoid CSVWorld::ScriptEdit::wrapLines(bool wrap)\n{\n    if (wrap)\n    {\n        setLineWrapMode(QPlainTextEdit::WidgetWidth);\n    }\n    else\n    {\n        setLineWrapMode(QPlainTextEdit::NoWrap);\n    }\n}\n\nvoid CSVWorld::ScriptEdit::settingChanged(const CSMPrefs::Setting *setting)\n{\n    // Determine which setting was changed.\n    if (mHighlighter->settingChanged(setting))\n    {\n        updateHighlighting();\n    }\n    else if (*setting == \"Scripts/mono-font\")\n    {\n        setFont(setting->isTrue() ? mMonoFont : mDefaultFont);\n        setTabWidth();\n    }\n    else if (*setting == \"Scripts/show-linenum\")\n    {\n        showLineNum(setting->isTrue());\n    }\n    else if (*setting == \"Scripts/wrap-lines\")\n    {\n        wrapLines(setting->isTrue());\n    }\n    else if (*setting == \"Scripts/tab-width\")\n    {\n        mTabCharCount = setting->toInt();\n        setTabWidth();\n    }\n    else if (*setting == \"Scripts/highlight-occurrences\")\n    {\n        mMarkOccurrences = setting->isTrue();\n        mHighlighter->setMarkedWord(\"\");\n        updateHighlighting();\n        mHighlighter->setMarkOccurrences(mMarkOccurrences);\n    }\n}\n\nvoid CSVWorld::ScriptEdit::idListChanged()\n{\n    mHighlighter->invalidateIds();\n\n    if (!mUpdateTimer.isActive())\n        mUpdateTimer.start (0);\n}\n\nvoid CSVWorld::ScriptEdit::updateHighlighting()\n{\n    if (isChangeLocked())\n        return;\n\n    ChangeLock lock (*this);\n\n    mHighlighter->rehighlight();\n}\n\nint CSVWorld::ScriptEdit::lineNumberAreaWidth()\n{\n    if(!mShowLineNum)\n        return 0;\n\n    int digits = 1;\n    int max = qMax(1, blockCount());\n    while (max >= 10)\n    {\n        max /= 10;\n        ++digits;\n    }\n\n    int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;\n    return space;\n}\n\nvoid CSVWorld::ScriptEdit::updateLineNumberAreaWidth(int /* newBlockCount */)\n{\n    setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);\n}\n\nvoid CSVWorld::ScriptEdit::updateLineNumberArea(const QRect &rect, int dy)\n{\n    if (dy)\n        mLineNumberArea->scroll(0, dy);\n    else\n        mLineNumberArea->update(0, rect.y(), mLineNumberArea->width(), rect.height());\n\n    if (rect.contains(viewport()->rect()))\n        updateLineNumberAreaWidth(0);\n}\n\nvoid CSVWorld::ScriptEdit::markOccurrences()\n{\n    if (mMarkOccurrences)\n    {\n        QTextCursor cursor = textCursor();\n\n        // prevent infinite recursion with cursor.select(),\n        // which ends up calling this function again\n        // could be fixed with blockSignals, but mDocument is const\n        disconnect(this, SIGNAL(cursorPositionChanged()), this, nullptr);\n        cursor.select(QTextCursor::WordUnderCursor);\n        connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(markOccurrences()));\n\n        QString word = cursor.selectedText();\n        mHighlighter->setMarkedWord(word.toStdString());\n        mHighlighter->rehighlight();\n    }\n}\n  \nvoid CSVWorld::ScriptEdit::commentSelection()\n{\n    QTextCursor begin = textCursor();\n    QTextCursor end = begin;\n    begin.setPosition(begin.selectionStart());\n    begin.movePosition(QTextCursor::StartOfLine);\n\n    end.setPosition(end.selectionEnd());\n    end.movePosition(QTextCursor::EndOfLine);\n\n    begin.beginEditBlock();\n\n    for (; begin < end; begin.movePosition(QTextCursor::EndOfLine), begin.movePosition(QTextCursor::Right))\n    {\n        begin.insertText(\";\");\n    }\n\n    begin.endEditBlock();\n}\n\nvoid CSVWorld::ScriptEdit::uncommentSelection()\n{\n    QTextCursor begin = textCursor();\n    QTextCursor end = begin;\n    begin.setPosition(begin.selectionStart());\n    begin.movePosition(QTextCursor::StartOfLine);\n\n    end.setPosition(end.selectionEnd());\n    end.movePosition(QTextCursor::EndOfLine);\n\n    begin.beginEditBlock();\n\n    for (; begin < end; begin.movePosition(QTextCursor::EndOfLine), begin.movePosition(QTextCursor::Right)) {\n        begin.select(QTextCursor::LineUnderCursor);\n        QString line = begin.selectedText();\n\n        if (line.size() == 0)\n            continue;\n\n        // get first nonspace character in line\n        int index;\n        for (index = 0; index != line.size(); ++index)\n        {\n            if (!line[index].isSpace())\n                break;\n        }\n\n        if (index != line.size() && line[index] == ';')\n        {\n            // remove the semicolon\n            line.remove(index, 1);\n            // put the line back\n            begin.insertText(line);\n        }\n    }\n\n    begin.endEditBlock();\n}\n\nvoid CSVWorld::ScriptEdit::resizeEvent(QResizeEvent *e)\n{\n    QPlainTextEdit::resizeEvent(e);\n\n    QRect cr = contentsRect();\n    mLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));\n}\n\nvoid CSVWorld::ScriptEdit::contextMenuEvent(QContextMenuEvent *event)\n{\n    QMenu *menu = createStandardContextMenu();\n\n    // remove redo/undo since they are disabled\n    QList<QAction*> menuActions = menu->actions();\n    for (QList<QAction*>::iterator i = menuActions.begin(); i < menuActions.end(); ++i)\n    {\n        if ((*i)->text().contains(\"Undo\") || (*i)->text().contains(\"Redo\"))\n        {\n            (*i)->setVisible(false);\n        }\n    }\n    menu->addAction(mCommentAction);\n    menu->addAction(mUncommentAction);\n\n    menu->exec(event->globalPos());\n    delete menu;\n}\n\nvoid CSVWorld::ScriptEdit::lineNumberAreaPaintEvent(QPaintEvent *event)\n{\n    QPainter painter(mLineNumberArea);\n\n    QTextBlock block = firstVisibleBlock();\n    int blockNumber = block.blockNumber();\n    int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();\n    int bottom = top + (int) blockBoundingRect(block).height();\n\n    int startBlock = textCursor().blockNumber();\n    int endBlock = textCursor().blockNumber();\n    if(textCursor().hasSelection())\n    {\n        QString str = textCursor().selection().toPlainText();\n        int offset = str.count(\"\\n\");\n        if(textCursor().position() < textCursor().anchor())\n            endBlock += offset;\n        else\n            startBlock -= offset;\n    }\n    painter.setBackgroundMode(Qt::OpaqueMode);\n    QFont font = painter.font();\n    QBrush background = painter.background();\n\n    while (block.isValid() && top <= event->rect().bottom())\n    {\n        if (block.isVisible() && bottom >= event->rect().top())\n        {\n            QFont newFont = painter.font();\n            QString number = QString::number(blockNumber + 1);\n            if(blockNumber >= startBlock && blockNumber <= endBlock)\n            {\n                painter.setBackground(Qt::cyan);\n                painter.setPen(Qt::darkMagenta);\n                newFont.setBold(true);\n            }\n            else\n            {\n                painter.setBackground(background);\n                painter.setPen(Qt::black);\n            }\n            painter.setFont(newFont);\n            painter.drawText(0, top, mLineNumberArea->width(), fontMetrics().height(),\n                             Qt::AlignRight, number);\n            painter.setFont(font);\n        }\n\n        block = block.next();\n        top = bottom;\n        bottom = top + (int) blockBoundingRect(block).height();\n        ++blockNumber;\n    }\n}\n\nCSVWorld::LineNumberArea::LineNumberArea(ScriptEdit *editor) : QWidget(editor), mScriptEdit(editor)\n{}\n\nQSize CSVWorld::LineNumberArea::sizeHint() const\n{\n    return QSize(mScriptEdit->lineNumberAreaWidth(), 0);\n}\n\nvoid CSVWorld::LineNumberArea::paintEvent(QPaintEvent *event)\n{\n    mScriptEdit->lineNumberAreaPaintEvent(event);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/scriptedit.hpp",
    "content": "#ifndef SCRIPTEDIT_H\n#define SCRIPTEDIT_H\n\n#include <QPlainTextEdit>\n#include <QWidget>\n#include <QVector>\n#include <QTimer>\n#include <QFont>\n#include <QAction>\n\n#include \"../../model/world/universalid.hpp\"\n\n#include \"scripthighlighter.hpp\"\n\nclass QRegExp;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVWorld\n{\n    class LineNumberArea;\n\n    /// \\brief Editor for scripts.\n    class ScriptEdit : public QPlainTextEdit\n    {\n            Q_OBJECT\n\n        public:\n\n            class ChangeLock\n            {\n                    ScriptEdit& mEdit;\n\n                    ChangeLock (const ChangeLock&);\n                    ChangeLock& operator= (const ChangeLock&);\n\n                public:\n\n                    ChangeLock (ScriptEdit& edit);\n                    ~ChangeLock();\n            };\n\n            friend class ChangeLock;\n\n        private:\n\n            int mChangeLocked;\n            ScriptHighlighter *mHighlighter;\n            QTimer mUpdateTimer;\n            bool mShowLineNum;\n            LineNumberArea *mLineNumberArea;\n            QFont mDefaultFont;\n            QFont mMonoFont;\n            int mTabCharCount;\n            bool mMarkOccurrences;\n            QAction *mCommentAction;\n            QAction *mUncommentAction;\n\n        protected:\n\n            bool event (QEvent *event) override;\n\n        public:\n\n            ScriptEdit (const CSMDoc::Document& document, ScriptHighlighter::Mode mode,\n                QWidget* parent);\n\n            /// Should changes to the data be ignored (i.e. not cause updated)?\n            ///\n            /// \\note This mechanism is used to avoid infinite update recursions\n            bool isChangeLocked() const;\n\n            void lineNumberAreaPaintEvent(QPaintEvent *event);\n            int lineNumberAreaWidth();\n            void showLineNum(bool show);\n\n        protected:\n\n            void resizeEvent(QResizeEvent *e) override;\n\n            void contextMenuEvent(QContextMenuEvent *event) override;\n\n        private:\n\n            QVector<CSMWorld::UniversalId::Type> mAllowedTypes;\n            const CSMDoc::Document& mDocument;\n            const QRegExp mWhiteListQoutes;\n\n            void dragEnterEvent (QDragEnterEvent* event) override;\n\n            void dropEvent (QDropEvent* event) override;\n\n            void dragMoveEvent (QDragMoveEvent* event) override;\n\n            bool stringNeedsQuote(const std::string& id) const;\n\n            /// \\brief Set tab width for script editor.\n            void setTabWidth();\n\n            /// \\brief Turn line wrapping in script editor on or off.\n            /// \\param wrap Whether or not to wrap lines.\n            void wrapLines(bool wrap);\n\n        private slots:\n\n            /// \\brief Update editor when related setting is changed.\n            /// \\param setting Setting that was changed.\n            void settingChanged(const CSMPrefs::Setting *setting);\n\n            void idListChanged();\n\n            void updateHighlighting();\n\n            void updateLineNumberAreaWidth(int newBlockCount);\n\n            void updateLineNumberArea(const QRect &, int);\n\n            void markOccurrences();\n            \n            void commentSelection();\n\n            void uncommentSelection();\n            \n    };\n\n    class LineNumberArea : public QWidget\n    {\n            ScriptEdit *mScriptEdit;\n\n        public:\n\n            LineNumberArea(ScriptEdit *editor);\n            QSize sizeHint() const override;\n\n        protected:\n\n            void paintEvent(QPaintEvent *event) override;\n    };\n}\n#endif // SCRIPTEDIT_H\n"
  },
  {
    "path": "apps/opencs/view/world/scripterrortable.cpp",
    "content": "#include \"scripterrortable.hpp\"\n\n#include <QHeaderView>\n\n#include <components/compiler/tokenloc.hpp>\n#include <components/compiler/scanner.hpp>\n#include <components/compiler/fileparser.hpp>\n#include <components/compiler/exception.hpp>\n#include <components/compiler/extensions0.hpp>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/prefs/state.hpp\"\n\nvoid CSVWorld::ScriptErrorTable::report (const std::string& message, const Compiler::TokenLoc& loc, Type type)\n{\n    std::ostringstream stream;\n    stream << message << \" (\" << loc.mLiteral << \")\";\n\n    addMessage (stream.str(), type==Compiler::ErrorHandler::WarningMessage ?\n        CSMDoc::Message::Severity_Warning : CSMDoc::Message::Severity_Error,\n        loc.mLine, loc.mColumn-loc.mLiteral.length());\n}\n\nvoid CSVWorld::ScriptErrorTable::report (const std::string& message, Type type)\n{\n    addMessage (message, type==Compiler::ErrorHandler::WarningMessage ?\n        CSMDoc::Message::Severity_Warning : CSMDoc::Message::Severity_Error);\n}\n\nvoid CSVWorld::ScriptErrorTable::addMessage (const std::string& message,\n    CSMDoc::Message::Severity severity, int line, int column)\n{\n    int row = rowCount();\n\n    setRowCount (row+1);\n\n    QTableWidgetItem *severityItem = new QTableWidgetItem (\n        QString::fromUtf8 (CSMDoc::Message::toString (severity).c_str()));\n    severityItem->setFlags (severityItem->flags() ^ Qt::ItemIsEditable);\n    setItem (row, 0, severityItem);\n\n    if (line!=-1)\n    {\n        QTableWidgetItem *lineItem = new QTableWidgetItem;\n        lineItem->setData (Qt::DisplayRole, line+1);\n        lineItem->setFlags (lineItem->flags() ^ Qt::ItemIsEditable);\n        setItem (row, 1, lineItem);\n\n        QTableWidgetItem *columnItem = new QTableWidgetItem;\n        columnItem->setData (Qt::DisplayRole, column);\n        columnItem->setFlags (columnItem->flags() ^ Qt::ItemIsEditable);\n        setItem (row, 3, columnItem);\n    }\n    else\n    {\n        QTableWidgetItem *lineItem = new QTableWidgetItem;\n        lineItem->setData (Qt::DisplayRole, \"-\");\n        lineItem->setFlags (lineItem->flags() ^ Qt::ItemIsEditable);\n        setItem (row, 1, lineItem);\n    }\n\n    QTableWidgetItem *messageItem = new QTableWidgetItem (QString::fromUtf8 (message.c_str()));\n    messageItem->setFlags (messageItem->flags() ^ Qt::ItemIsEditable);\n    setItem (row, 2, messageItem);\n}\n\nvoid CSVWorld::ScriptErrorTable::setWarningsMode (const std::string& value)\n{\n    if (value==\"Ignore\")\n        Compiler::ErrorHandler::setWarningsMode (0);\n    else if (value==\"Normal\")\n        Compiler::ErrorHandler::setWarningsMode (1);\n    else if (value==\"Strict\")\n        Compiler::ErrorHandler::setWarningsMode (2);\n}\n\nCSVWorld::ScriptErrorTable::ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent)\n: QTableWidget (parent), mContext (document.getData())\n{\n    setColumnCount (4);\n\n    QStringList headers;\n    headers << \"Severity\" << \"Line\" << \"Description\";\n    setHorizontalHeaderLabels (headers);\n    horizontalHeader()->setSectionResizeMode (0, QHeaderView::ResizeToContents);\n    horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents);\n    horizontalHeader()->setStretchLastSection (true);\n    verticalHeader()->hide();\n    setColumnHidden (3, true);\n\n    setSelectionMode (QAbstractItemView::NoSelection);\n\n    Compiler::registerExtensions (mExtensions);\n    mContext.setExtensions (&mExtensions);\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n    CSMPrefs::get()[\"Scripts\"].update();\n\n    connect (this, SIGNAL (cellClicked (int, int)), this, SLOT (cellClicked (int, int)));\n}\n\nvoid CSVWorld::ScriptErrorTable::update (const std::string& source)\n{\n    clear();\n\n    try\n    {\n        std::istringstream input (source);\n\n        Compiler::Scanner scanner (*this, input, mContext.getExtensions());\n\n        Compiler::FileParser parser (*this, mContext);\n\n        scanner.scan (parser);\n    }\n    catch (const Compiler::SourceException&)\n    {\n        // error has already been reported via error handler\n    }\n    catch (const std::exception& error)\n    {\n        addMessage (error.what(), CSMDoc::Message::Severity_SeriousError);\n    }\n}\n\nvoid CSVWorld::ScriptErrorTable::clear()\n{\n    setRowCount (0);\n}\n\nbool CSVWorld::ScriptErrorTable::clearLocals (const std::string& script)\n{\n    return mContext.clearLocals (script);\n}\n\nvoid CSVWorld::ScriptErrorTable::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==\"Scripts/warnings\")\n        setWarningsMode (setting->toString());\n}\n\nvoid CSVWorld::ScriptErrorTable::cellClicked (int row, int column)\n{\n    if (item (row, 3))\n    {\n        int scriptLine = item (row, 1)->data (Qt::DisplayRole).toInt();\n        int scriptColumn = item (row, 3)->data (Qt::DisplayRole).toInt();\n        emit highlightError (scriptLine-1, scriptColumn);\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/scripterrortable.hpp",
    "content": "#ifndef CSV_WORLD_SCRIPTERRORTABLE_H\n#define CSV_WORLD_SCRIPTERRORTABLE_H\n\n#include <QTableWidget>\n\n#include <components/compiler/errorhandler.hpp>\n#include <components/compiler/extensions.hpp>\n\n#include \"../../model/world/scriptcontext.hpp\"\n#include \"../../model/doc/messages.hpp\"\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWorld\n{\n    class ScriptErrorTable : public QTableWidget, private Compiler::ErrorHandler\n    {\n            Q_OBJECT\n\n            Compiler::Extensions mExtensions;\n            CSMWorld::ScriptContext mContext;\n\n            void report (const std::string& message, const Compiler::TokenLoc& loc, Type type) override;\n            ///< Report error to the user.\n\n            void report (const std::string& message, Type type) override;\n            ///< Report a file related error\n\n            void addMessage (const std::string& message, CSMDoc::Message::Severity severity,\n                int line = -1, int column = -1);\n\n            void setWarningsMode (const std::string& value);\n\n        public:\n\n            ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent = nullptr);\n\n            void update (const std::string& source);\n\n            void clear();\n\n            /// Clear local variable cache for \\a script.\n            ///\n            /// \\return Were there any locals that needed clearing?\n            bool clearLocals (const std::string& script);\n\n        private slots:\n\n            void settingChanged (const CSMPrefs::Setting *setting);\n\n            void cellClicked (int row, int column);\n\n        signals:\n\n            void highlightError (int line, int column);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/scripthighlighter.cpp",
    "content": "#include \"scripthighlighter.hpp\"\n\n#include <sstream>\n\n#include <components/compiler/scanner.hpp>\n#include <components/compiler/extensions0.hpp>\n\n#include \"../../model/prefs/setting.hpp\"\n#include \"../../model/prefs/category.hpp\"\n\nbool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc,\n    Compiler::Scanner& scanner)\n{\n    highlight (loc, Type_Int);\n    return true;\n}\n\nbool CSVWorld::ScriptHighlighter::parseFloat (float value, const Compiler::TokenLoc& loc,\n    Compiler::Scanner& scanner)\n{\n    highlight (loc, Type_Float);\n    return true;\n}\n\nbool CSVWorld::ScriptHighlighter::parseName (const std::string& name, const Compiler::TokenLoc& loc,\n    Compiler::Scanner& scanner)\n{\n    highlight (loc, mContext.isId (name) ? Type_Id : Type_Name);\n    return true;\n}\n\nbool CSVWorld::ScriptHighlighter::parseKeyword (int keyword, const Compiler::TokenLoc& loc,\n    Compiler::Scanner& scanner)\n{\n    if (((mMode==Mode_Console || mMode==Mode_Dialogue) &&\n        (keyword==Compiler::Scanner::K_begin || keyword==Compiler::Scanner::K_end ||\n        keyword==Compiler::Scanner::K_short || keyword==Compiler::Scanner::K_long ||\n        keyword==Compiler::Scanner::K_float))\n        || (mMode==Mode_Console && (keyword==Compiler::Scanner::K_if ||\n        keyword==Compiler::Scanner::K_endif || keyword==Compiler::Scanner::K_else ||\n        keyword==Compiler::Scanner::K_elseif || keyword==Compiler::Scanner::K_while ||\n        keyword==Compiler::Scanner::K_endwhile)))\n        return parseName (loc.mLiteral, loc, scanner);\n\n    highlight (loc, Type_Keyword);\n    return true;\n}\n\nbool CSVWorld::ScriptHighlighter::parseSpecial (int code, const Compiler::TokenLoc& loc,\n    Compiler::Scanner& scanner)\n{\n    highlight (loc, Type_Special);\n    return true;\n}\n\nbool CSVWorld::ScriptHighlighter::parseComment (const std::string& comment,\n    const Compiler::TokenLoc& loc, Compiler::Scanner& scanner)\n{\n    highlight (loc, Type_Comment);\n    return true;\n}\n\nvoid CSVWorld::ScriptHighlighter::parseEOF (Compiler::Scanner& scanner)\n{}\n\nvoid CSVWorld::ScriptHighlighter::highlight (const Compiler::TokenLoc& loc, Type type)\n{\n    // We should take in account multibyte characters\n    int length = 0;\n    const char* token = loc.mLiteral.c_str();\n    while (*token) length += (*token++ & 0xc0) != 0x80;\n\n    int index = loc.mColumn;\n\n    // compensate for bug in Compiler::Scanner (position of token is the character after the token)\n    index -= length;\n\n    QTextCharFormat scheme = mScheme[type];\n    if (mMarkOccurrences && type == Type_Name && loc.mLiteral == mMarkedWord)\n        scheme.merge(mScheme[Type_Highlight]);\n\n    setFormat (index, length, scheme);\n}\n\nCSVWorld::ScriptHighlighter::ScriptHighlighter (const CSMWorld::Data& data, Mode mode,\n    QTextDocument *parent)\n    : QSyntaxHighlighter (parent)\n    , Compiler::Parser (mErrorHandler, mContext)\n    , mContext (data)\n    , mMode (mode)\n    , mMarkOccurrences (false)\n{\n    QColor color (\"black\");\n    QTextCharFormat format;\n    format.setForeground (color);\n\n    for (int i=0; i<=Type_Id; ++i)\n        mScheme.insert (std::make_pair (static_cast<Type> (i), format));\n\n    // configure compiler\n    Compiler::registerExtensions (mExtensions);\n    mContext.setExtensions (&mExtensions);\n}\n\nvoid CSVWorld::ScriptHighlighter::highlightBlock (const QString& text)\n{\n    std::istringstream stream (text.toUtf8().constData());\n\n    Compiler::Scanner scanner (mErrorHandler, stream, mContext.getExtensions());\n\n    try\n    {\n        scanner.scan (*this);\n    }\n    catch (...) {} // ignore syntax errors\n}\n\nvoid CSVWorld::ScriptHighlighter::setMarkOccurrences(bool flag)\n{\n    mMarkOccurrences = flag;\n}\n\nvoid CSVWorld::ScriptHighlighter::setMarkedWord(const std::string& name)\n{\n    mMarkedWord = name;\n}\n\nvoid CSVWorld::ScriptHighlighter::invalidateIds()\n{\n    mContext.invalidateIds();\n}\n\nbool CSVWorld::ScriptHighlighter::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (setting->getParent()->getKey()==\"Scripts\")\n    {\n        static const char *const colours[Type_Id+2] =\n        {\n            \"colour-int\", \"colour-float\", \"colour-name\", \"colour-keyword\",\n            \"colour-special\", \"colour-comment\", \"colour-highlight\", \"colour-id\",\n            0\n        };\n\n        for (int i=0; colours[i]; ++i)\n            if (setting->getKey()==colours[i])\n            {\n                QTextCharFormat format;\n                if (i == Type_Highlight)\n                    format.setBackground (setting->toColor());\n                else\n                    format.setForeground (setting->toColor());\n                mScheme[static_cast<Type> (i)] = format;\n                return true;\n            }\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "apps/opencs/view/world/scripthighlighter.hpp",
    "content": "#ifndef CSV_WORLD_SCRIPTHIGHLIGHTER_H\n#define CSV_WORLD_SCRIPTHIGHLIGHTER_H\n\n#include <map>\n#include <string>\n\n#include <QSyntaxHighlighter>\n\n#include <components/compiler/nullerrorhandler.hpp>\n#include <components/compiler/parser.hpp>\n#include <components/compiler/extensions.hpp>\n\n#include \"../../model/world/scriptcontext.hpp\"\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWorld\n{\n    class ScriptHighlighter : public QSyntaxHighlighter, private Compiler::Parser\n    {\n        public:\n\n            enum Type\n            {\n                Type_Int = 0,\n                Type_Float = 1,\n                Type_Name = 2,\n                Type_Keyword = 3,\n                Type_Special = 4,\n                Type_Comment = 5,\n                Type_Highlight = 6,\n                Type_Id = 7\n            };\n\n            enum Mode\n            {\n                Mode_General,\n                Mode_Console,\n                Mode_Dialogue\n            };\n\n        private:\n\n            Compiler::NullErrorHandler mErrorHandler;\n            Compiler::Extensions mExtensions;\n            CSMWorld::ScriptContext mContext;\n            std::map<Type, QTextCharFormat> mScheme;\n            Mode mMode;\n            bool mMarkOccurrences;\n            std::string mMarkedWord;\n\n        private:\n\n            bool parseInt (int value, const Compiler::TokenLoc& loc,\n                Compiler::Scanner& scanner) override;\n            ///< Handle an int token.\n            /// \\return fetch another token?\n\n            bool parseFloat (float value, const Compiler::TokenLoc& loc,\n                Compiler::Scanner& scanner) override;\n            ///< Handle a float token.\n            /// \\return fetch another token?\n\n            bool parseName (const std::string& name,\n                const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const Compiler::TokenLoc& loc,\n                Compiler::Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const Compiler::TokenLoc& loc,\n                Compiler::Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            bool parseComment (const std::string& comment, const Compiler::TokenLoc& loc,\n                Compiler::Scanner& scanner) override;\n            ///< Handle comment token.\n            /// \\return fetch another token?\n\n            void parseEOF (Compiler::Scanner& scanner) override;\n            ///< Handle EOF token.\n\n            void highlight (const Compiler::TokenLoc& loc, Type type);\n\n        public:\n\n            ScriptHighlighter (const CSMWorld::Data& data, Mode mode, QTextDocument *parent);\n\n            void highlightBlock (const QString& text) override;\n\n            void setMarkOccurrences(bool);\n\n            void setMarkedWord(const std::string& name);\n\n            void invalidateIds();\n\n            bool settingChanged (const CSMPrefs::Setting *setting);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/scriptsubview.cpp",
    "content": "#include \"scriptsubview.hpp\"\n\n#include <stdexcept>\n\n#include <QStatusBar>\n#include <QStackedLayout>\n#include <QSplitter>\n#include <QTimer>\n\n#include <components/debug/debuglog.hpp>\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/prefs/state.hpp\"\n\n#include \"scriptedit.hpp\"\n#include \"recordbuttonbar.hpp\"\n#include \"tablebottombox.hpp\"\n#include \"genericcreator.hpp\"\n#include \"scripterrortable.hpp\"\n\nvoid CSVWorld::ScriptSubView::addButtonBar()\n{\n    if (mButtons)\n        return;\n\n    mButtons = new RecordButtonBar (getUniversalId(), *mModel, mBottom, &mCommandDispatcher, this);\n\n    mLayout.insertWidget (1, mButtons);\n\n    connect (mButtons, SIGNAL (switchToRow (int)), this, SLOT (switchToRow (int)));\n\n    connect (this, SIGNAL (universalIdChanged (const CSMWorld::UniversalId&)),\n        mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&)));\n}\n\nvoid CSVWorld::ScriptSubView::recompile()\n{\n    if (!mCompileDelay->isActive() && !isDeleted())\n        mCompileDelay->start (CSMPrefs::get()[\"Scripts\"][\"compile-delay\"].toInt());\n}\n\nbool CSVWorld::ScriptSubView::isDeleted() const\n{\n    return mModel->data (mModel->getModelIndex (getUniversalId().getId(), mStateColumn)).toInt()\n        ==CSMWorld::RecordBase::State_Deleted;\n}\n\nvoid CSVWorld::ScriptSubView::updateDeletedState()\n{\n    if (isDeleted())\n    {\n        mErrors->clear();\n        adjustSplitter();\n        mEditor->setEnabled (false);\n    }\n    else\n    {\n        mEditor->setEnabled (true);\n        recompile();\n    }\n}\n\nvoid CSVWorld::ScriptSubView::adjustSplitter()\n{\n    QList<int> sizes;\n\n    if (mErrors->rowCount())\n    {\n        if (mErrors->height())\n            return; // keep old height if the error panel was already open\n\n        sizes << (mMain->height()-mErrorHeight-mMain->handleWidth()) << mErrorHeight;\n    }\n    else\n    {\n        if (mErrors->height())\n            mErrorHeight = mErrors->height();\n\n        sizes << 1 << 0;\n    }\n\n    mMain->setSizes (sizes);\n}\n\nCSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)\n: SubView (id), mDocument (document), mColumn (-1), mBottom(nullptr), mButtons (nullptr),\n  mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())),\n  mErrorHeight (CSMPrefs::get()[\"Scripts\"][\"error-height\"].toInt())\n{\n    std::vector<std::string> selection (1, id.getId());\n    mCommandDispatcher.setSelection (selection);\n\n    mMain = new QSplitter (this);\n    mMain->setOrientation (Qt::Vertical);\n    mLayout.addWidget (mMain, 2);\n\n    mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this);\n    mMain->addWidget (mEditor);\n    mMain->setCollapsible (0, false);\n\n    mErrors = new ScriptErrorTable (document, this);\n    mMain->addWidget (mErrors);\n\n    QList<int> sizes;\n    sizes << 1 << 0;\n    mMain->setSizes (sizes);\n\n    QWidget *widget = new QWidget (this);;\n    widget->setLayout (&mLayout);\n    setWidget (widget);\n\n    mModel = &dynamic_cast<CSMWorld::IdTable&> (\n        *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts));\n\n    mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText);\n    mIdColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification);\n\n    QString source = mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString();\n\n    mEditor->setPlainText (source);\n    // bottom box and buttons\n    mBottom = new TableBottomBox (CreatorFactory<GenericCreator>(), document, id, this);\n\n    connect (mBottom, SIGNAL (requestFocus (const std::string&)),\n        this, SLOT (switchToId (const std::string&)));\n\n    mLayout.addWidget (mBottom);\n\n    // signals\n    connect (mEditor, SIGNAL (textChanged()), this, SLOT (textChanged()));\n\n    connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&)));\n\n    connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),\n        this, SLOT (rowsAboutToBeRemoved (const QModelIndex&, int, int)));\n\n    updateStatusBar();\n    connect(mEditor, SIGNAL(cursorPositionChanged()), this, SLOT(updateStatusBar()));\n\n    mErrors->update (source.toUtf8().constData());\n\n    connect (mErrors, SIGNAL (highlightError (int, int)),\n        this, SLOT (highlightError (int, int)));\n\n    mCompileDelay = new QTimer (this);\n    mCompileDelay->setSingleShot (true);\n    connect (mCompileDelay, SIGNAL (timeout()), this, SLOT (updateRequest()));\n\n    updateDeletedState();\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n    CSMPrefs::get()[\"Scripts\"].update();\n}\n\nvoid CSVWorld::ScriptSubView::setStatusBar (bool show)\n{\n    mBottom->setStatusBar (show);\n}\n\nvoid CSVWorld::ScriptSubView::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==\"Scripts/toolbar\")\n    {\n        if (setting->isTrue())\n        {\n            addButtonBar();\n        }\n        else if (mButtons)\n        {\n            mLayout.removeWidget (mButtons);\n            delete mButtons;\n            mButtons = nullptr;\n        }\n    }\n    else if (*setting==\"Scripts/compile-delay\")\n    {\n        mCompileDelay->setInterval (setting->toInt());\n    }\n    else  if (*setting==\"Scripts/warnings\")\n        recompile();\n}\n\nvoid CSVWorld::ScriptSubView::updateStatusBar ()\n{\n    mBottom->positionChanged (mEditor->textCursor().blockNumber() + 1,\n        mEditor->textCursor().columnNumber() + 1);\n}\n\nvoid CSVWorld::ScriptSubView::setEditLock (bool locked)\n{\n    mEditor->setReadOnly (locked);\n\n    if (mButtons)\n        mButtons->setEditLock (locked);\n\n    mCommandDispatcher.setEditLock (locked);\n}\n\nvoid CSVWorld::ScriptSubView::useHint (const std::string& hint)\n{\n    if (hint.empty())\n        return;\n\n    unsigned line = 0, column = 0;\n    char c;\n    std::istringstream stream (hint.c_str()+1);\n    switch(hint[0])\n    {\n        case 'R':\n        case 'r':\n        {\n            QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);\n            QString source = mModel->data (index).toString();\n            unsigned stringSize = source.length();\n            unsigned pos, dummy;\n            if (!(stream >> c >> dummy >> pos) )\n                return;\n\n            if (pos > stringSize)\n            {\n                Log(Debug::Warning) << \"CSVWorld::ScriptSubView: requested position is higher than actual string length\";\n                pos = stringSize;\n            }\n\n            for (unsigned i = 0; i <= pos; ++i)\n            {\n                if (source[i] == '\\n')\n                {\n                    ++line;\n                    column = i+1;\n                }\n            }\n            column = pos - column;\n            break;\n        }\n        case 'l':\n            if (!(stream >> c >> line >> column))\n                return;\n    }\n\n    QTextCursor cursor = mEditor->textCursor();\n\n    cursor.movePosition (QTextCursor::Start);\n    if (cursor.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, line))\n        cursor.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, column);\n\n    mEditor->setFocus();\n    mEditor->setTextCursor (cursor);\n}\n\nvoid CSVWorld::ScriptSubView::textChanged()\n{\n    if (mEditor->isChangeLocked())\n        return;\n\n    ScriptEdit::ChangeLock lock (*mEditor);\n\n    QString source = mEditor->toPlainText();\n\n    mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel,\n        mModel->getModelIndex (getUniversalId().getId(), mColumn), source));\n\n    recompile();\n}\n\nvoid CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)\n{\n    if (mEditor->isChangeLocked())\n        return;\n\n    ScriptEdit::ChangeLock lock (*mEditor);\n\n    bool updateRequired = false;\n\n    for (int i=topLeft.row(); i<=bottomRight.row(); ++i)\n    {\n        std::string id = mModel->data (mModel->index (i, mIdColumn)).toString().toUtf8().constData();\n        if (mErrors->clearLocals (id))\n            updateRequired = true;\n    }\n\n    QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);\n\n    if (index.row()>=topLeft.row() && index.row()<=bottomRight.row())\n    {\n        if (mStateColumn>=topLeft.column() && mStateColumn<=bottomRight.column())\n            updateDeletedState();\n\n        if (mColumn>=topLeft.column() && mColumn<=bottomRight.column())\n        {\n            QString source = mModel->data (index).toString();\n\n            QTextCursor cursor = mEditor->textCursor();\n            mEditor->setPlainText (source);\n            mEditor->setTextCursor (cursor);\n\n            updateRequired = true;\n        }\n    }\n\n    if (updateRequired)\n        recompile();\n}\n\nvoid CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end)\n{\n    bool updateRequired = false;\n\n    for (int i=start; i<=end; ++i)\n    {\n        std::string id = mModel->data (mModel->index (i, mIdColumn)).toString().toUtf8().constData();\n        if (mErrors->clearLocals (id))\n            updateRequired = true;\n    }\n\n    if (updateRequired)\n        recompile();\n\n    QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);\n\n    if (!parent.isValid() && index.row()>=start && index.row()<=end)\n        emit closeRequest();\n}\n\nvoid CSVWorld::ScriptSubView::switchToRow (int row)\n{\n    int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    std::string id = mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData();\n    setUniversalId (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, id));\n\n    bool oldSignalsState =  mEditor->blockSignals( true );\n    mEditor->setPlainText( mModel->data(mModel->index(row, mColumn)).toString() );\n    mEditor->blockSignals( oldSignalsState );\n\n    std::vector<std::string> selection (1, id);\n    mCommandDispatcher.setSelection (selection);\n\n    updateDeletedState();\n}\n\nvoid CSVWorld::ScriptSubView::switchToId (const std::string& id)\n{\n    switchToRow (mModel->getModelIndex (id, 0).row());\n}\n\nvoid CSVWorld::ScriptSubView::highlightError (int line, int column)\n{\n    QTextCursor cursor = mEditor->textCursor();\n\n    cursor.movePosition (QTextCursor::Start);\n    if (cursor.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, line))\n        cursor.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, column);\n\n    mEditor->setFocus();\n    mEditor->setTextCursor (cursor);\n}\n\nvoid CSVWorld::ScriptSubView::updateRequest()\n{\n    QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);\n\n    QString source = mModel->data (index).toString();\n\n    mErrors->update (source.toUtf8().constData());\n\n    adjustSplitter();\n}\n"
  },
  {
    "path": "apps/opencs/view/world/scriptsubview.hpp",
    "content": "#ifndef CSV_WORLD_SCRIPTSUBVIEW_H\n#define CSV_WORLD_SCRIPTSUBVIEW_H\n\n#include <QVBoxLayout>\n\n#include \"../../model/world/commanddispatcher.hpp\"\n\n#include \"../doc/subview.hpp\"\n\nclass QModelIndex;\nclass QLabel;\nclass QVBoxLayout;\nclass QSplitter;\nclass QTime;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class IdTable;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWorld\n{\n    class ScriptEdit;\n    class RecordButtonBar;\n    class TableBottomBox;\n    class ScriptErrorTable;\n\n    class ScriptSubView : public CSVDoc::SubView\n    {\n            Q_OBJECT\n\n            ScriptEdit *mEditor;\n            CSMDoc::Document& mDocument;\n            CSMWorld::IdTable *mModel;\n            int mColumn;\n            int mIdColumn;\n            int mStateColumn;\n            TableBottomBox *mBottom;\n            RecordButtonBar *mButtons;\n            CSMWorld::CommandDispatcher mCommandDispatcher;\n            QVBoxLayout mLayout;\n            QSplitter *mMain;\n            ScriptErrorTable *mErrors;\n            QTimer *mCompileDelay;\n            int mErrorHeight;\n\n        private:\n\n            void addButtonBar();\n\n            void recompile();\n\n            bool isDeleted() const;\n\n            void updateDeletedState();\n\n            void adjustSplitter();\n\n        public:\n\n            ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);\n\n            void setEditLock (bool locked) override;\n\n            void useHint (const std::string& hint) override;\n\n            void setStatusBar (bool show) override;\n\n        public slots:\n\n            void textChanged();\n\n            void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);\n\n            void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);\n\n        private slots:\n\n            void settingChanged (const CSMPrefs::Setting *setting);\n\n            void updateStatusBar();\n\n            void switchToRow (int row);\n\n            void switchToId (const std::string& id);\n\n            void highlightError (int line, int column);\n\n            void updateRequest();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/startscriptcreator.cpp",
    "content": "#include \"startscriptcreator.hpp\"\n\n#include <QLabel>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/data.hpp\"\n#include \"../../model/world/idcompletionmanager.hpp\"\n#include \"../../model/world/idtable.hpp\"\n\n#include \"../widget/droplineedit.hpp\"\n\nstd::string CSVWorld::StartScriptCreator::getId() const\n{\n    return mScript->text().toUtf8().constData();\n}\n\nCSMWorld::IdTable& CSVWorld::StartScriptCreator::getStartScriptsTable() const\n{\n    return dynamic_cast<CSMWorld::IdTable&> (\n        *getData().getTableModel(getCollectionId())\n    );\n}\n\nCSVWorld::StartScriptCreator::StartScriptCreator(\n    CSMWorld::Data &data,\n    QUndoStack &undoStack,\n    const CSMWorld::UniversalId &id,\n    CSMWorld::IdCompletionManager& completionManager\n) : GenericCreator(data, undoStack, id)\n{\n    setManualEditing(false);\n\n    // Add script ID input label.\n    QLabel *label = new QLabel(\"Script\", this);\n    insertBeforeButtons(label, false);\n\n    // Add script ID input with auto-completion.\n    // Only existing script IDs are accepted so no ID validation is performed.\n    CSMWorld::ColumnBase::Display displayType = CSMWorld::ColumnBase::Display_Script;\n    mScript = new CSVWidget::DropLineEdit(displayType, this);\n    mScript->setCompleter(completionManager.getCompleter(displayType).get());\n    insertBeforeButtons(mScript, true);\n\n    connect(mScript, SIGNAL (textChanged(const QString&)), this, SLOT (scriptChanged()));\n    connect(mScript, SIGNAL (returnPressed()), this, SLOT (inputReturnPressed()));\n}\n\nvoid CSVWorld::StartScriptCreator::cloneMode(\n    const std::string& originId,\n    const CSMWorld::UniversalId::Type type)\n{\n    CSVWorld::GenericCreator::cloneMode(originId, type);\n\n    // Look up cloned record in start scripts table and set script ID text.\n    CSMWorld::IdTable& table = getStartScriptsTable();\n    int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_Id);\n    mScript->setText(table.data(table.getModelIndex(originId, column)).toString());\n}\n\nstd::string CSVWorld::StartScriptCreator::getErrors() const\n{\n    std::string scriptId = getId();\n\n    // Check user input for any errors.\n    std::string errors;\n    if (scriptId.empty())\n    {\n        errors = \"No Script ID entered\";\n    }\n    else if (getData().getScripts().searchId(scriptId) == -1)\n    {\n        errors = \"Script ID not found\";\n    }\n    else if (getData().getStartScripts().searchId(scriptId) > -1)\n    {\n        errors = \"Script with this ID already registered as Start Script\";\n    }\n\n    return errors;\n}\n\nvoid CSVWorld::StartScriptCreator::focus()\n{\n    mScript->setFocus();\n}\n\nvoid CSVWorld::StartScriptCreator::reset()\n{\n    CSVWorld::GenericCreator::reset();\n    mScript->setText(\"\");\n}\n\nvoid CSVWorld::StartScriptCreator::scriptChanged()\n{\n    update();\n}\n\nCSVWorld::Creator *CSVWorld::StartScriptCreatorFactory::makeCreator(\n    CSMDoc::Document& document,\n    const CSMWorld::UniversalId& id) const\n{\n    return new StartScriptCreator(\n        document.getData(),\n        document.getUndoStack(),\n        id,\n        document.getIdCompletionManager()\n    );\n}\n"
  },
  {
    "path": "apps/opencs/view/world/startscriptcreator.hpp",
    "content": "#ifndef STARTSCRIPTCREATOR_HPP\n#define STARTSCRIPTCREATOR_HPP\n\n#include \"genericcreator.hpp\"\n\nnamespace CSMWorld\n{\n    class IdCompletionManager;\n    class IdTable;\n}\n\nnamespace CSVWidget\n{\n    class DropLineEdit;\n}\n\nnamespace CSVWorld\n{\n    /// \\brief Record creator for start scripts.\n    class StartScriptCreator : public GenericCreator\n    {\n        Q_OBJECT\n\n        CSVWidget::DropLineEdit *mScript;\n\n        private:\n\n            /// \\return script ID entered by user.\n            std::string getId() const override;\n\n            /// \\return reference to table containing start scripts.\n            CSMWorld::IdTable& getStartScriptsTable() const;\n\n        public:\n\n            StartScriptCreator(\n                CSMWorld::Data& data,\n                QUndoStack& undoStack,\n                const CSMWorld::UniversalId& id,\n                CSMWorld::IdCompletionManager& completionManager);\n\n            /// \\brief Set script ID input widget to ID of record to be cloned.\n            /// \\param originId Script ID to be cloned.\n            /// \\param type Type of record to be cloned.\n            void cloneMode(\n                const std::string& originId,\n                const CSMWorld::UniversalId::Type type) override;\n\n            /// \\return Error description for current user input.\n            std::string getErrors() const override;\n\n            /// \\brief Set focus to script ID input widget.\n            void focus() override;\n\n            /// \\brief Clear script ID input widget.\n            void reset() override;\n\n        private slots:\n\n            /// \\brief Check user input for any errors.\n            void scriptChanged();\n     };\n\n     /// \\brief Creator factory for start script record creator.\n     class StartScriptCreatorFactory : public CreatorFactoryBase\n     {\n        public:\n\n            Creator *makeCreator(\n                CSMDoc::Document& document,\n                const CSMWorld::UniversalId& id) const override;\n     };\n}\n\n#endif // STARTSCRIPTCREATOR_HPP\n"
  },
  {
    "path": "apps/opencs/view/world/subviews.cpp",
    "content": "#include \"subviews.hpp\"\n\n#include \"../doc/subviewfactoryimp.hpp\"\n\n#include \"tablesubview.hpp\"\n#include \"dialoguesubview.hpp\"\n#include \"scriptsubview.hpp\"\n#include \"regionmapsubview.hpp\"\n#include \"genericcreator.hpp\"\n#include \"globalcreator.hpp\"\n#include \"cellcreator.hpp\"\n#include \"referenceablecreator.hpp\"\n#include \"referencecreator.hpp\"\n#include \"startscriptcreator.hpp\"\n#include \"scenesubview.hpp\"\n#include \"dialoguecreator.hpp\"\n#include \"infocreator.hpp\"\n#include \"pathgridcreator.hpp\"\n#include \"previewsubview.hpp\"\n#include \"bodypartcreator.hpp\"\n#include \"landcreator.hpp\"\n#include \"landtexturecreator.hpp\"\n\nvoid CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)\n{\n    // Regular record tables (including references which are actually sub-records, but are promoted\n    // to top-level records within the editor)\n    manager.add (CSMWorld::UniversalId::Type_Gmsts,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n\n    manager.add (CSMWorld::UniversalId::Type_Skills,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n\n    manager.add (CSMWorld::UniversalId::Type_MagicEffects,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n\n    static const CSMWorld::UniversalId::Type sTableTypes[] =\n    {\n        CSMWorld::UniversalId::Type_Classes,\n        CSMWorld::UniversalId::Type_Factions,\n        CSMWorld::UniversalId::Type_Races,\n        CSMWorld::UniversalId::Type_Sounds,\n        CSMWorld::UniversalId::Type_Regions,\n        CSMWorld::UniversalId::Type_Birthsigns,\n        CSMWorld::UniversalId::Type_Spells,\n        CSMWorld::UniversalId::Type_Enchantments,\n        CSMWorld::UniversalId::Type_SoundGens,\n\n        CSMWorld::UniversalId::Type_None // end marker\n    };\n\n    for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i)\n        manager.add (sTableTypes[i],\n            new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GenericCreator> >);\n\n    manager.add (CSMWorld::UniversalId::Type_BodyParts,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<BodyPartCreator> >);\n\n    manager.add (CSMWorld::UniversalId::Type_StartScripts,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, StartScriptCreatorFactory>);\n\n    manager.add (CSMWorld::UniversalId::Type_Cells,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<CellCreator> >);\n\n    manager.add (CSMWorld::UniversalId::Type_Referenceables,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceableCreator> >);\n\n    manager.add (CSMWorld::UniversalId::Type_References,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, ReferenceCreatorFactory>);\n\n    manager.add (CSMWorld::UniversalId::Type_Topics,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, TopicCreatorFactory>);\n\n    manager.add (CSMWorld::UniversalId::Type_Journals,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, JournalCreatorFactory>);\n\n    manager.add (CSMWorld::UniversalId::Type_TopicInfos,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, InfoCreatorFactory>(false));\n\n    manager.add (CSMWorld::UniversalId::Type_JournalInfos,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, InfoCreatorFactory>(false));\n\n    manager.add (CSMWorld::UniversalId::Type_Pathgrids,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, PathgridCreatorFactory>);\n\n    manager.add (CSMWorld::UniversalId::Type_Lands,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandCreator> >);\n\n    manager.add (CSMWorld::UniversalId::Type_LandTextures,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandTextureCreator> >);\n\n    manager.add (CSMWorld::UniversalId::Type_Globals,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GlobalCreator> >);\n\n    // Subviews for resources tables\n    manager.add (CSMWorld::UniversalId::Type_Meshes,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n    manager.add (CSMWorld::UniversalId::Type_Icons,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n    manager.add (CSMWorld::UniversalId::Type_Musics,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n    manager.add (CSMWorld::UniversalId::Type_SoundsRes,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n    manager.add (CSMWorld::UniversalId::Type_Textures,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n    manager.add (CSMWorld::UniversalId::Type_Videos,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);\n\n\n    // Subviews for editing/viewing individual records\n    manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>);\n\n    // Other stuff (combined record tables)\n    manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory<RegionMapSubView>);\n\n    manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory<SceneSubView>);\n\n    // More other stuff\n    manager.add (CSMWorld::UniversalId::Type_Filters,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView,\n        CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> >);\n\n    manager.add (CSMWorld::UniversalId::Type_DebugProfiles,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView,\n        CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> >);\n\n    manager.add (CSMWorld::UniversalId::Type_Scripts,\n        new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GenericCreator,\n            CSMWorld::Scope_Project | CSMWorld::Scope_Content> >);\n\n    // Dialogue subviews\n    static const CSMWorld::UniversalId::Type sTableTypes2[] =\n    {\n        CSMWorld::UniversalId::Type_Region,\n        CSMWorld::UniversalId::Type_Spell,\n        CSMWorld::UniversalId::Type_Birthsign,\n        CSMWorld::UniversalId::Type_Global,\n        CSMWorld::UniversalId::Type_Race,\n        CSMWorld::UniversalId::Type_Class,\n        CSMWorld::UniversalId::Type_Sound,\n        CSMWorld::UniversalId::Type_Faction,\n        CSMWorld::UniversalId::Type_Enchantment,\n        CSMWorld::UniversalId::Type_SoundGen,\n\n        CSMWorld::UniversalId::Type_None // end marker\n    };\n\n    for (int i=0; sTableTypes2[i]!=CSMWorld::UniversalId::Type_None; ++i)\n        manager.add (sTableTypes2[i],\n            new CSVDoc::SubViewFactoryWithCreator<DialogueSubView,\n            CreatorFactory<GenericCreator> > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_BodyPart,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<BodyPartCreator> > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_StartScript,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, StartScriptCreatorFactory>(false));\n\n    manager.add (CSMWorld::UniversalId::Type_Skill,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_MagicEffect,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_Gmst,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_Referenceable,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceableCreator> > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_Reference,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, ReferenceCreatorFactory> (false));\n\n    manager.add (CSMWorld::UniversalId::Type_Cell,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<CellCreator> > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_JournalInfo,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, InfoCreatorFactory> (false));\n\n    manager.add (CSMWorld::UniversalId::Type_TopicInfo,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, InfoCreatorFactory>(false));\n\n    manager.add (CSMWorld::UniversalId::Type_Topic,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, TopicCreatorFactory> (false));\n\n    manager.add (CSMWorld::UniversalId::Type_Journal,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, JournalCreatorFactory> (false));\n\n    manager.add (CSMWorld::UniversalId::Type_Pathgrid,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, PathgridCreatorFactory> (false));\n\n    manager.add (CSMWorld::UniversalId::Type_Land,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandCreator> >(false));\n\n    manager.add (CSMWorld::UniversalId::Type_LandTexture,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandTextureCreator> >(false));\n\n    manager.add (CSMWorld::UniversalId::Type_DebugProfile,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_Filter,\n        new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> > (false));\n\n    manager.add (CSMWorld::UniversalId::Type_MetaData,\n        new CSVDoc::SubViewFactory<SimpleDialogueSubView >);\n\n    //preview\n    manager.add (CSMWorld::UniversalId::Type_Preview, new CSVDoc::SubViewFactory<PreviewSubView>);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/subviews.hpp",
    "content": "#ifndef CSV_WORLD_SUBVIEWS_H\n#define CSV_WORLD_SUBVIEWS_H\n\nnamespace CSVDoc\n{\n    class SubViewFactoryManager;\n}\n\nnamespace CSVWorld\n{\n    void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager);\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/table.cpp",
    "content": "#include \"table.hpp\"\n\n#include <QHeaderView>\n#include <QAction>\n#include <QMenu>\n#include <QContextMenuEvent>\n#include <QString>\n#include <QtCore/qnamespace.h>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/helpviewer.hpp>\n#include <components/misc/stringops.hpp>\n\n#include \"../../model/doc/document.hpp\"\n\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/infotableproxymodel.hpp\"\n#include \"../../model/world/idtableproxymodel.hpp\"\n#include \"../../model/world/idtablebase.hpp\"\n#include \"../../model/world/idtable.hpp\"\n#include \"../../model/world/landtexturetableproxymodel.hpp\"\n#include \"../../model/world/record.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/commanddispatcher.hpp\"\n\n#include \"../../model/prefs/state.hpp\"\n#include \"../../model/prefs/shortcut.hpp\"\n\n#include \"tableeditidaction.hpp\"\n#include \"util.hpp\"\n\nvoid CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)\n{\n    // configure dispatcher\n    mDispatcher->setSelection (getSelectedIds());\n\n    std::vector<CSMWorld::UniversalId> extendedTypes = mDispatcher->getExtendedTypes();\n\n    mDispatcher->setExtendedTypes (extendedTypes);\n\n    // create context menu\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n    QMenu menu (this);\n\n    ///  \\todo add menu items for select all and clear selection\n\n    int currentRow = rowAt(event->y());\n    int currentColumn = columnAt(event->x());\n    if (mEditIdAction->isValidIdCell(currentRow, currentColumn))\n    {\n        mEditIdAction->setCell(currentRow, currentColumn);\n        menu.addAction(mEditIdAction);\n        menu.addSeparator();\n    }\n\n    if (!mEditLock && !(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))\n    {\n        if (selectedRows.size()==1)\n        {\n            menu.addAction (mEditAction);\n\n            if (mCreateAction)\n                menu.addAction(mCloneAction);\n        }\n\n        if (mTouchAction)\n            menu.addAction (mTouchAction);\n\n        if (mCreateAction)\n            menu.addAction (mCreateAction);\n\n        if (mDispatcher->canRevert())\n        {\n            menu.addAction (mRevertAction);\n\n            if (!extendedTypes.empty())\n                menu.addAction (mExtendedRevertAction);\n        }\n\n        if (mDispatcher->canDelete())\n        {\n            menu.addAction (mDeleteAction);\n\n            if (!extendedTypes.empty())\n                menu.addAction (mExtendedDeleteAction);\n        }\n\n        if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_ReorderWithinTopic)\n        {\n            /// \\todo allow reordering of multiple rows\n            if (selectedRows.size()==1)\n            {\n                int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic);\n\n                if (column==-1)\n                    column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal);\n\n                if (column!=-1)\n                {\n                    int row = mProxyModel->mapToSource (\n                        mProxyModel->index (selectedRows.begin()->row(), 0)).row();\n                    QString curData = mModel->data(mModel->index(row, column)).toString();\n\n                    if (row > 0)\n                    {\n                        QString prevData = mModel->data(mModel->index(row - 1, column)).toString();\n                        if (Misc::StringUtils::ciEqual(curData.toStdString(), prevData.toStdString()))\n                        {\n                            menu.addAction(mMoveUpAction);\n                        }\n                    }\n\n                    if (row < mModel->rowCount() - 1)\n                    {\n                        QString nextData = mModel->data(mModel->index(row + 1, column)).toString();\n                        if (Misc::StringUtils::ciEqual(curData.toStdString(), nextData.toStdString()))\n                        {\n                            menu.addAction(mMoveDownAction);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    if (selectedRows.size()==1)\n    {\n        int row = selectedRows.begin()->row();\n\n        row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();\n\n        if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_View)\n        {\n            CSMWorld::UniversalId id = mModel->view (row).first;\n\n            int index = mDocument.getData().getCells().searchId (id.getId());\n            // index==-1: the ID references a worldspace instead of a cell (ignore for now and go\n            // ahead)\n\n            if (index==-1 || !mDocument.getData().getCells().getRecord (index).isDeleted())\n                menu.addAction (mViewAction);\n        }\n\n        if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Preview)\n        {\n            const CSMWorld::UniversalId id = getUniversalId(currentRow);\n            const CSMWorld::UniversalId::Type type = id.getType();\n\n            QModelIndex index = mModel->index (row,\n                mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification));\n\n            CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State> (\n                mModel->data (index).toInt());\n\n            if (state!=CSMWorld::RecordBase::State_Deleted && type != CSMWorld::UniversalId::Type_ItemLevelledList)\n                menu.addAction (mPreviewAction);\n        }\n    }\n\n    if (mHelpAction)\n        menu.addAction (mHelpAction);\n\n    menu.exec (event->globalPos());\n}\n\nvoid CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event)\n{\n    Qt::KeyboardModifiers modifiers =\n        event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier);\n\n    QModelIndex index = currentIndex();\n\n    selectionModel()->select (index,\n        QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows);\n\n    std::map<Qt::KeyboardModifiers, DoubleClickAction>::iterator iter =\n        mDoubleClickActions.find (modifiers);\n\n    if (iter==mDoubleClickActions.end())\n    {\n        event->accept();\n        return;\n    }\n\n    switch (iter->second)\n    {\n        case Action_None:\n\n            event->accept();\n            break;\n\n        case Action_InPlaceEdit:\n\n            DragRecordTable::mouseDoubleClickEvent (event);\n            break;\n\n        case Action_EditRecord:\n\n            event->accept();\n            editRecord();\n            break;\n\n        case Action_View:\n\n            event->accept();\n            viewRecord();\n            break;\n\n        case Action_Revert:\n\n            event->accept();\n            if (mDispatcher->canRevert())\n                mDispatcher->executeRevert();\n            break;\n\n        case Action_Delete:\n\n            event->accept();\n            if (mDispatcher->canDelete())\n                mDispatcher->executeDelete();\n            break;\n\n        case Action_EditRecordAndClose:\n\n            event->accept();\n            editRecord();\n            emit closeRequest();\n            break;\n\n        case Action_ViewAndClose:\n\n            event->accept();\n            viewRecord();\n            emit closeRequest();\n            break;\n    }\n}\n\nCSVWorld::Table::Table (const CSMWorld::UniversalId& id,\n    bool createAndDelete, bool sorting, CSMDoc::Document& document)\n    : DragRecordTable(document), mCreateAction (nullptr), mCloneAction(nullptr), mTouchAction(nullptr),\n    mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false)\n{\n    mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id));\n\n    bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos ||\n                       id.getType() == CSMWorld::UniversalId::Type_JournalInfos;\n    bool isLtexTable = (id.getType() == CSMWorld::UniversalId::Type_LandTextures);\n    if (isInfoTable)\n    {\n        mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this);\n        connect (this, &CSVWorld::DragRecordTable::moveRecordsFromSameTable, this, &CSVWorld::Table::moveRecords);\n    }\n    else if (isLtexTable)\n    {\n        mProxyModel = new CSMWorld::LandTextureTableProxyModel (this);\n    }\n    else\n    {\n        mProxyModel = new CSMWorld::IdTableProxyModel (this);\n    }\n    mProxyModel->setSourceModel (mModel);\n\n    mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);\n\n    setModel (mProxyModel);\n    horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);\n    verticalHeader()->hide();\n    setSelectionBehavior (QAbstractItemView::SelectRows);\n    setSelectionMode (QAbstractItemView::ExtendedSelection);\n\n    setSortingEnabled (sorting);\n    if (sorting)\n    {\n        sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder);\n    }\n\n    int columns = mModel->columnCount();\n    for (int i=0; i<columns; ++i)\n    {\n        int flags = mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();\n\n        if (flags & CSMWorld::ColumnBase::Flag_Table)\n        {\n            CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> (\n                mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());\n\n            CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display,\n                mDispatcher, document, this);\n\n            mDelegates.push_back (delegate);\n            setItemDelegateForColumn (i, delegate);\n        }\n        else\n            hideColumn (i);\n    }\n\n    mEditAction = new QAction (tr (\"Edit Record\"), this);\n    connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));\n    mEditAction->setIcon(QIcon(\":edit-edit\"));\n    addAction (mEditAction);\n    CSMPrefs::Shortcut* editShortcut = new CSMPrefs::Shortcut(\"table-edit\", this);\n    editShortcut->associateAction(mEditAction);\n\n    if (createAndDelete)\n    {\n        mCreateAction = new QAction (tr (\"Add Record\"), this);\n        connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest()));\n        mCreateAction->setIcon(QIcon(\":edit-add\"));\n        addAction (mCreateAction);\n        CSMPrefs::Shortcut* createShortcut = new CSMPrefs::Shortcut(\"table-add\", this);\n        createShortcut->associateAction(mCreateAction);\n\n        mCloneAction = new QAction (tr (\"Clone Record\"), this);\n        connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord()));\n        mCloneAction->setIcon(QIcon(\":edit-clone\"));\n        addAction(mCloneAction);\n        CSMPrefs::Shortcut* cloneShortcut = new CSMPrefs::Shortcut(\"table-clone\", this);\n        cloneShortcut->associateAction(mCloneAction);\n    }\n\n    if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch)\n    {\n        mTouchAction = new QAction(tr(\"Touch Record\"), this);\n        connect(mTouchAction, SIGNAL(triggered()), this, SLOT(touchRecord()));\n        mTouchAction->setIcon(QIcon(\":edit-touch\"));\n        addAction(mTouchAction);\n        CSMPrefs::Shortcut* touchShortcut = new CSMPrefs::Shortcut(\"table-touch\", this);\n        touchShortcut->associateAction(mTouchAction);\n    }\n\n    mRevertAction = new QAction (tr (\"Revert Record\"), this);\n    connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert()));\n    mRevertAction->setIcon(QIcon(\":edit-undo\"));\n    addAction (mRevertAction);\n    CSMPrefs::Shortcut* revertShortcut = new CSMPrefs::Shortcut(\"table-revert\", this);\n    revertShortcut->associateAction(mRevertAction);\n\n    mDeleteAction = new QAction (tr (\"Delete Record\"), this);\n    connect (mDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeDelete()));\n    mDeleteAction->setIcon(QIcon(\":edit-delete\"));\n    addAction (mDeleteAction);\n    CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut(\"table-remove\", this);\n    deleteShortcut->associateAction(mDeleteAction);\n\n    mMoveUpAction = new QAction (tr (\"Move Up\"), this);\n    connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord()));\n    mMoveUpAction->setIcon(QIcon(\":record-up\"));\n    addAction (mMoveUpAction);\n    CSMPrefs::Shortcut* moveUpShortcut = new CSMPrefs::Shortcut(\"table-moveup\", this);\n    moveUpShortcut->associateAction(mMoveUpAction);\n\n    mMoveDownAction = new QAction (tr (\"Move Down\"), this);\n    connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord()));\n    mMoveDownAction->setIcon(QIcon(\":record-down\"));\n    addAction (mMoveDownAction);\n    CSMPrefs::Shortcut* moveDownShortcut = new CSMPrefs::Shortcut(\"table-movedown\", this);\n    moveDownShortcut->associateAction(mMoveDownAction);\n\n    mViewAction = new QAction (tr (\"View\"), this);\n    connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord()));\n    mViewAction->setIcon(QIcon(\":/cell.png\"));\n    addAction (mViewAction);\n    CSMPrefs::Shortcut* viewShortcut = new CSMPrefs::Shortcut(\"table-view\", this);\n    viewShortcut->associateAction(mViewAction);\n\n    mPreviewAction = new QAction (tr (\"Preview\"), this);\n    connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord()));\n    mPreviewAction->setIcon(QIcon(\":edit-preview\"));\n    addAction (mPreviewAction);\n    CSMPrefs::Shortcut* previewShortcut = new CSMPrefs::Shortcut(\"table-preview\", this);\n    previewShortcut->associateAction(mPreviewAction);\n\n    mExtendedDeleteAction = new QAction (tr (\"Extended Delete Record\"), this);\n    connect (mExtendedDeleteAction, SIGNAL (triggered()), this, SLOT (executeExtendedDelete()));\n    mExtendedDeleteAction->setIcon(QIcon(\":edit-delete\"));\n    addAction (mExtendedDeleteAction);\n    CSMPrefs::Shortcut* extendedDeleteShortcut = new CSMPrefs::Shortcut(\"table-extendeddelete\", this);\n    extendedDeleteShortcut->associateAction(mExtendedDeleteAction);\n\n    mExtendedRevertAction = new QAction (tr (\"Extended Revert Record\"), this);\n    connect (mExtendedRevertAction, SIGNAL (triggered()), this, SLOT (executeExtendedRevert()));\n    mExtendedRevertAction->setIcon(QIcon(\":edit-undo\"));\n    addAction (mExtendedRevertAction);\n    CSMPrefs::Shortcut* extendedRevertShortcut = new CSMPrefs::Shortcut(\"table-extendedrevert\", this);\n    extendedRevertShortcut->associateAction(mExtendedRevertAction);\n\n    mEditIdAction = new TableEditIdAction (*this, this);\n    connect (mEditIdAction, SIGNAL (triggered()), this, SLOT (editCell()));\n    addAction (mEditIdAction);\n\n    mHelpAction = new QAction (tr (\"Help\"), this);\n    connect (mHelpAction, SIGNAL (triggered()), this, SLOT (openHelp()));\n    mHelpAction->setIcon(QIcon(\":/info.png\"));\n    addAction (mHelpAction);\n    CSMPrefs::Shortcut* openHelpShortcut = new CSMPrefs::Shortcut(\"help\", this);\n    openHelpShortcut->associateAction(mHelpAction);\n\n    connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),\n        this, SLOT (tableSizeUpdate()));\n\n    connect (mProxyModel, SIGNAL (rowAdded (const std::string &)),\n        this, SLOT (rowAdded (const std::string &)));\n\n    /// \\note This signal could instead be connected to a slot that filters out changes not affecting\n    /// the records status column (for permanence reasons)\n    connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),\n        this, SLOT (tableSizeUpdate()));\n\n    connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),\n        this, SLOT (selectionSizeUpdate ()));\n\n    setAcceptDrops(true);\n\n    mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_InPlaceEdit));\n    mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_EditRecord));\n    mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_View));\n    mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier | Qt::ControlModifier, Action_EditRecordAndClose));\n\n    connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),\n        this, SLOT (settingChanged (const CSMPrefs::Setting *)));\n    CSMPrefs::get()[\"ID Tables\"].update();\n}\n\nvoid CSVWorld::Table::setEditLock (bool locked)\n{\n    for (std::vector<CommandDelegate *>::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter)\n        (*iter)->setEditLock (locked);\n\n    DragRecordTable::setEditLock(locked);\n    mDispatcher->setEditLock (locked);\n}\n\nCSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const\n{\n    row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();\n\n    int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n    int typeColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);\n\n    return CSMWorld::UniversalId (\n        static_cast<CSMWorld::UniversalId::Type> (mModel->data (mModel->index (row, typeColumn)).toInt()),\n        mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData());\n}\n\nstd::vector<std::string> CSVWorld::Table::getSelectedIds() const\n{\n    std::vector<std::string> ids;\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n    int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);\n\n    for (QModelIndexList::const_iterator iter (selectedRows.begin());\n         iter != selectedRows.end();\n         ++iter)\n    {\n        int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row();\n        ids.emplace_back(mModel->data (mModel->index (row, columnIndex)).toString().toUtf8().constData());\n    }\n    return ids;\n}\n\nvoid CSVWorld::Table::editRecord()\n{\n    if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))\n    {\n        QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n        if (selectedRows.size()==1)\n            emit editRequest (getUniversalId (selectedRows.begin()->row()), \"\");\n    }\n}\n\nvoid CSVWorld::Table::cloneRecord()\n{\n    if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))\n    {\n        QModelIndexList selectedRows = selectionModel()->selectedRows();\n        const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row());\n        if (selectedRows.size() == 1)\n        {\n            emit cloneRequest (toClone);\n        }\n    }\n}\n\nvoid CSVWorld::Table::touchRecord()\n{\n    if (!mEditLock && mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch)\n    {\n        std::vector<CSMWorld::UniversalId> touchIds;\n\n        QModelIndexList selectedRows = selectionModel()->selectedRows();\n        for (auto it = selectedRows.begin(); it != selectedRows.end(); ++it)\n        {\n            touchIds.push_back(getUniversalId(it->row()));\n        }\n\n        emit touchRequest(touchIds);\n    }\n}\n\nvoid CSVWorld::Table::moveUpRecord()\n{\n    if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))\n        return;\n\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    if (selectedRows.size()==1)\n    {\n        int row2 =selectedRows.begin()->row();\n\n        if (row2>0)\n        {\n            int row = row2-1;\n\n            row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();\n            row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row();\n\n            if (row2<=row)\n                throw std::runtime_error (\"Inconsistent row order\");\n\n            std::vector<int> newOrder (row2-row+1);\n            newOrder[0] = row2-row;\n            newOrder[row2-row] = 0;\n            for (int i=1; i<row2-row; ++i)\n                newOrder[i] = i;\n\n            mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (\n                dynamic_cast<CSMWorld::IdTable&> (*mModel), row, newOrder));\n        }\n    }\n}\n\nvoid CSVWorld::Table::moveDownRecord()\n{\n    if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))\n        return;\n\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    if (selectedRows.size()==1)\n    {\n        int row =selectedRows.begin()->row();\n\n        if (row<mProxyModel->rowCount()-1)\n        {\n            int row2 = row+1;\n\n            row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();\n            row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row();\n\n            if (row2<=row)\n                throw std::runtime_error (\"Inconsistent row order\");\n\n            std::vector<int> newOrder (row2-row+1);\n            newOrder[0] = row2-row;\n            newOrder[row2-row] = 0;\n            for (int i=1; i<row2-row; ++i)\n                newOrder[i] = i;\n\n            mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (\n                dynamic_cast<CSMWorld::IdTable&> (*mModel), row, newOrder));\n        }\n    }\n}\n\nvoid CSVWorld::Table::moveRecords(QDropEvent *event)\n{\n    if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))\n        return;\n\n    QModelIndex targedIndex = indexAt(event->pos());\n\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n    int targetRowRaw = targedIndex.row();\n    int targetRow = mProxyModel->mapToSource (mProxyModel->index (targetRowRaw, 0)).row();\n    int baseRowRaw = targedIndex.row() - 1;\n    int baseRow = mProxyModel->mapToSource (mProxyModel->index (baseRowRaw, 0)).row();\n    int highestDifference = 0;\n\n    for (const auto& thisRowData : selectedRows)\n    {\n        int thisRow = mProxyModel->mapToSource (mProxyModel->index (thisRowData.row(), 0)).row();\n        if (std::abs(targetRow - thisRow) > highestDifference) highestDifference = std::abs(targetRow - thisRow);\n        if (thisRow - 1 < baseRow) baseRow = thisRow - 1;\n    }\n\n    std::vector<int> newOrder (highestDifference + 1);\n\n    for (long unsigned int i = 0; i < newOrder.size(); ++i)\n    {\n        newOrder[i] = i;\n    }\n\n    if (selectedRows.size() > 1)\n    {\n        Log(Debug::Warning) << \"Move operation failed: Moving multiple selections isn't implemented.\";\n        return;\n    }\n\n    for (const auto& thisRowData : selectedRows)\n    {\n        /*\n            Moving algorithm description\n            a) Remove the (ORIGIN + 1)th list member.\n            b) Add (ORIGIN+1)th list member with value TARGET\n            c) If ORIGIN > TARGET,d_INC; ELSE d_DEC\n            d_INC) increase all members after (and including) the TARGET by one, stop before hitting ORIGINth address\n            d_DEC)  decrease all members after the ORIGIN by one, stop after hitting address TARGET\n        */\n\n        int originRowRaw = thisRowData.row();\n        int originRow = mProxyModel->mapToSource (mProxyModel->index (originRowRaw, 0)).row();\n\n        newOrder.erase(newOrder.begin() +  originRow - baseRow - 1);\n        newOrder.emplace(newOrder.begin() + originRow - baseRow - 1, targetRow - baseRow - 1);\n\n        if (originRow > targetRow)\n        {\n            for (int i = targetRow - baseRow - 1; i < originRow - baseRow - 1; ++i)\n            {\n                ++newOrder[i];\n            }\n        }\n        else\n        {\n            for (int i = originRow - baseRow; i <= targetRow - baseRow - 1; ++i)\n            {\n                --newOrder[i];\n            }\n        }\n\n    }\n    mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (\n        dynamic_cast<CSMWorld::IdTable&> (*mModel), baseRow + 1, newOrder));\n}\n\nvoid CSVWorld::Table::editCell()\n{\n    emit editRequest(mEditIdAction->getCurrentId(), \"\");\n}\n\nvoid CSVWorld::Table::openHelp()\n{\n    Misc::HelpViewer::openHelp(\"manuals/openmw-cs/tables.html\");\n}\n\nvoid CSVWorld::Table::viewRecord()\n{\n    if (!(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_View))\n        return;\n\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    if (selectedRows.size()==1)\n    {\n        int row = selectedRows.begin()->row();\n\n        row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();\n\n        std::pair<CSMWorld::UniversalId, std::string> params = mModel->view (row);\n\n        if (params.first.getType()!=CSMWorld::UniversalId::Type_None)\n            emit editRequest (params.first, params.second);\n    }\n}\n\nvoid CSVWorld::Table::previewRecord()\n{\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n\n    if (selectedRows.size()==1)\n    {\n        std::string id = getUniversalId (selectedRows.begin()->row()).getId();\n\n        QModelIndex index = mModel->getModelIndex (id,\n            mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification));\n\n        if (mModel->data (index)!=CSMWorld::RecordBase::State_Deleted)\n            emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id),\n                \"\");\n    }\n}\n\nvoid CSVWorld::Table::executeExtendedDelete()\n{\n    if (CSMPrefs::get()[\"ID Tables\"][\"extended-config\"].isTrue())\n    {\n        emit extendedDeleteConfigRequest(getSelectedIds());\n    }\n    else\n    {\n        QMetaObject::invokeMethod(mDispatcher, \"executeExtendedDelete\", Qt::QueuedConnection);\n    }\n}\n\nvoid CSVWorld::Table::executeExtendedRevert()\n{\n    if (CSMPrefs::get()[\"ID Tables\"][\"extended-config\"].isTrue())\n    {\n        emit extendedRevertConfigRequest(getSelectedIds());\n    }\n    else\n    {\n        QMetaObject::invokeMethod(mDispatcher, \"executeExtendedRevert\", Qt::QueuedConnection);\n    }\n}\n\nvoid CSVWorld::Table::settingChanged (const CSMPrefs::Setting *setting)\n{\n    if (*setting==\"ID Tables/jump-to-added\")\n    {\n        if (setting->toString()==\"Jump and Select\")\n        {\n            mJumpToAddedRecord = true;\n            mUnselectAfterJump = false;\n        }\n        else if (setting->toString()==\"Jump Only\")\n        {\n            mJumpToAddedRecord = true;\n            mUnselectAfterJump = true;\n        }\n        else // No Jump\n        {\n            mJumpToAddedRecord = false;\n            mUnselectAfterJump = false;\n        }\n    }\n    else if (*setting==\"Records/type-format\" || *setting==\"Records/status-format\")\n    {\n        int columns = mModel->columnCount();\n\n        for (int i=0; i<columns; ++i)\n            if (QAbstractItemDelegate *delegate = itemDelegateForColumn (i))\n            {\n                dynamic_cast<CommandDelegate&> (*delegate).settingChanged (setting);\n                emit dataChanged (mModel->index (0, i),\n                    mModel->index (mModel->rowCount()-1, i));\n            }\n    }\n    else if (setting->getParent()->getKey()==\"ID Tables\" &&\n        setting->getKey().substr (0, 6)==\"double\")\n    {\n        std::string modifierString = setting->getKey().substr (6);\n\n        Qt::KeyboardModifiers modifiers;\n\n        if (modifierString==\"-s\")\n            modifiers = Qt::ShiftModifier;\n        else if (modifierString==\"-c\")\n            modifiers = Qt::ControlModifier;\n        else if (modifierString==\"-sc\")\n            modifiers = Qt::ShiftModifier | Qt::ControlModifier;\n\n        DoubleClickAction action = Action_None;\n\n        std::string value = setting->toString();\n\n        if (value==\"Edit in Place\")\n            action = Action_InPlaceEdit;\n        else if (value==\"Edit Record\")\n            action = Action_EditRecord;\n        else if (value==\"View\")\n            action = Action_View;\n        else if (value==\"Revert\")\n            action = Action_Revert;\n        else if (value==\"Delete\")\n            action = Action_Delete;\n        else if (value==\"Edit Record and Close\")\n            action = Action_EditRecordAndClose;\n        else if (value==\"View and Close\")\n            action = Action_ViewAndClose;\n\n        mDoubleClickActions[modifiers] = action;\n    }\n}\n\nvoid CSVWorld::Table::tableSizeUpdate()\n{\n    int size = 0;\n    int deleted = 0;\n    int modified = 0;\n\n    if (mProxyModel->columnCount()>0)\n    {\n        int rows = mProxyModel->rowCount();\n\n        int columnIndex = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Modification);\n\n        if (columnIndex!=-1)\n        {\n            for (int i=0; i<rows; ++i)\n            {\n                QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (i, 0));\n\n                int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt();\n\n                switch (state)\n                {\n                    case CSMWorld::RecordBase::State_BaseOnly: ++size; break;\n                    case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break;\n                    case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break;\n                    case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break;\n                }\n            }\n        }\n        else\n            size = rows;\n    }\n\n    emit tableSizeChanged (size, deleted, modified);\n}\n\nvoid CSVWorld::Table::selectionSizeUpdate()\n{\n    emit selectionSizeChanged (selectionModel()->selectedRows().size());\n}\n\nvoid CSVWorld::Table::requestFocus (const std::string& id)\n{\n    QModelIndex index = mProxyModel->getModelIndex (id, 0);\n\n    if (index.isValid())\n    {\n        // This will scroll to the row.\n        selectRow (index.row());\n        // This will actually select it.\n        selectionModel()->select (index, QItemSelectionModel::Select | QItemSelectionModel::Rows);\n    }\n}\n\nvoid CSVWorld::Table::recordFilterChanged (std::shared_ptr<CSMFilter::Node> filter)\n{\n    mProxyModel->setFilter (filter);\n    tableSizeUpdate();\n    selectionSizeUpdate();\n}\n\nvoid CSVWorld::Table::mouseMoveEvent (QMouseEvent* event)\n{\n    if (event->buttons() & Qt::LeftButton)\n    {\n        startDragFromTable(*this);\n    }\n}\n\nstd::vector<std::string> CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const\n{\n    const int count = mModel->columnCount();\n\n    std::vector<std::string> titles;\n    for (int i = 0; i < count; ++i)\n    {\n        CSMWorld::ColumnBase::Display columndisplay = static_cast<CSMWorld::ColumnBase::Display>\n                                                     (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());\n\n        if (display == columndisplay)\n        {\n            titles.emplace_back(mModel->headerData (i, Qt::Horizontal).toString().toUtf8().constData());\n        }\n    }\n    return titles;\n}\n\nstd::vector< CSMWorld::UniversalId > CSVWorld::Table::getDraggedRecords() const\n{\n    QModelIndexList selectedRows = selectionModel()->selectedRows();\n    std::vector<CSMWorld::UniversalId> idToDrag;\n\n    for (QModelIndex& it : selectedRows)\n        idToDrag.push_back (getUniversalId (it.row()));\n\n    return idToDrag;\n}\n\nvoid CSVWorld::Table::rowAdded(const std::string &id)\n{\n    tableSizeUpdate();\n    if(mJumpToAddedRecord)\n    {\n        int idColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);\n        selectRow(mProxyModel->getModelIndex(id, idColumn).row());\n\n        if(mUnselectAfterJump)\n            clearSelection();\n    }\n}\n"
  },
  {
    "path": "apps/opencs/view/world/table.hpp",
    "content": "#ifndef CSV_WORLD_TABLE_H\n#define CSV_WORLD_TABLE_H\n\n#include <vector>\n#include <string>\n\n#include <QEvent>\n\n#include \"../../model/filter/node.hpp\"\n#include \"../../model/world/columnbase.hpp\"\n#include \"../../model/world/universalid.hpp\"\n#include \"dragrecordtable.hpp\"\n\nclass QAction;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSMWorld\n{\n    class IdTableProxyModel;\n    class IdTableBase;\n    class CommandDispatcher;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWorld\n{\n    class CommandDelegate;\n    class TableEditIdAction;\n\n    ///< Table widget\n    class Table : public DragRecordTable\n    {\n            Q_OBJECT\n\n            enum DoubleClickAction\n            {\n                Action_None,\n                Action_InPlaceEdit,\n                Action_EditRecord,\n                Action_View,\n                Action_Revert,\n                Action_Delete,\n                Action_EditRecordAndClose,\n                Action_ViewAndClose\n            };\n\n            std::vector<CommandDelegate *> mDelegates;\n            QAction *mEditAction;\n            QAction *mCreateAction;\n            QAction *mCloneAction;\n            QAction *mTouchAction;\n            QAction *mRevertAction;\n            QAction *mDeleteAction;\n            QAction *mMoveUpAction;\n            QAction *mMoveDownAction;\n            QAction *mViewAction;\n            QAction *mPreviewAction;\n            QAction *mExtendedDeleteAction;\n            QAction *mExtendedRevertAction;\n            QAction *mHelpAction;\n            TableEditIdAction *mEditIdAction;\n            CSMWorld::IdTableProxyModel *mProxyModel;\n            CSMWorld::IdTableBase *mModel;\n            int mRecordStatusDisplay;\n            CSMWorld::CommandDispatcher *mDispatcher;\n            std::map<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions;\n            bool mJumpToAddedRecord;\n            bool mUnselectAfterJump;\n\n        private:\n\n            void contextMenuEvent (QContextMenuEvent *event) override;\n\n            void mouseMoveEvent(QMouseEvent *event) override;\n\n        protected:\n\n            void mouseDoubleClickEvent (QMouseEvent *event) override;\n\n        public:\n\n            Table (const CSMWorld::UniversalId& id, bool createAndDelete,\n                bool sorting, CSMDoc::Document& document);\n            ///< \\param createAndDelete Allow creation and deletion of records.\n            /// \\param sorting Allow changing order of rows in the view via column headers.\n\n            virtual void setEditLock (bool locked);\n\n            CSMWorld::UniversalId getUniversalId (int row) const;\n\n            std::vector<std::string> getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const;\n\n            std::vector<std::string> getSelectedIds() const;\n\n            std::vector<CSMWorld::UniversalId> getDraggedRecords() const override;\n\n        signals:\n\n            void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);\n\n            void selectionSizeChanged (int size);\n\n            void tableSizeChanged (int size, int deleted, int modified);\n            ///< \\param size Number of not deleted records\n            /// \\param deleted Number of deleted records\n            /// \\param modified Number of added and modified records\n\n            void createRequest();\n\n            void cloneRequest(const CSMWorld::UniversalId&);\n\n            void touchRequest(const std::vector<CSMWorld::UniversalId>& ids);\n\n            void closeRequest();\n\n            void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);\n\n            void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);\n\n        private slots:\n\n            void editCell();\n\n            static void openHelp();\n\n            void editRecord();\n\n            void cloneRecord();\n\n            void touchRecord();\n\n            void moveUpRecord();\n\n            void moveDownRecord();\n\n            void moveRecords(QDropEvent *event);\n\n            void viewRecord();\n\n            void previewRecord();\n\n            void executeExtendedDelete();\n\n            void executeExtendedRevert();\n\n        public slots:\n\n            void settingChanged (const CSMPrefs::Setting *setting);\n\n            void tableSizeUpdate();\n\n            void selectionSizeUpdate();\n\n            void requestFocus (const std::string& id);\n\n            void recordFilterChanged (std::shared_ptr<CSMFilter::Node> filter);\n\n            void rowAdded(const std::string &id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/tablebottombox.cpp",
    "content": "#include \"tablebottombox.hpp\"\n\n#include <sstream>\n\n#include <QStatusBar>\n#include <QStackedLayout>\n#include <QLabel>\n#include <QEvent>\n#include <QKeyEvent>\n\n#include \"creator.hpp\"\n\nvoid CSVWorld::TableBottomBox::updateSize()\n{\n    // Make sure that the size of the bottom box is determined by the currently visible widget\n    for (int i = 0; i < mLayout->count(); ++i)\n    {\n        QSizePolicy::Policy verPolicy = QSizePolicy::Ignored;\n        if (mLayout->widget(i) == mLayout->currentWidget())\n        {\n            verPolicy = QSizePolicy::Expanding;\n        }\n        mLayout->widget(i)->setSizePolicy(QSizePolicy::Expanding, verPolicy);\n    }\n}\n\nvoid CSVWorld::TableBottomBox::updateStatus()\n{\n    if (mShowStatusBar)\n    {\n        if (!mStatusMessage.isEmpty())\n        {\n            mStatus->setText (mStatusMessage);\n            return;\n        }\n\n        static const char *sLabels[4] = { \"record\", \"deleted\", \"touched\", \"selected\" };\n        static const char *sLabelsPlural[4] = { \"records\", \"deleted\", \"touched\", \"selected\" };\n\n        std::ostringstream stream;\n\n        bool first = true;\n\n        for (int i=0; i<4; ++i)\n        {\n            if (mStatusCount[i]>0)\n            {\n                if (first)\n                    first = false;\n                else\n                    stream << \", \";\n\n                stream\n                    << mStatusCount[i] << ' '\n                    << (mStatusCount[i]==1 ? sLabels[i] : sLabelsPlural[i]);\n            }\n        }\n\n        if (mHasPosition)\n        {\n            if (!first)\n                stream << \" -- \";\n\n            stream << \"(\" << mRow << \", \" << mColumn << \")\";\n        }\n\n        mStatus->setText (QString::fromUtf8 (stream.str().c_str()));\n    }\n}\n\nvoid CSVWorld::TableBottomBox::extendedConfigRequest(CSVWorld::ExtendedCommandConfigurator::Mode mode,\n                                                     const std::vector<std::string> &selectedIds)\n{\n    mExtendedConfigurator->configure (mode, selectedIds);\n    mLayout->setCurrentWidget (mExtendedConfigurator);\n    mEditMode = EditMode_ExtendedConfig;\n    setVisible (true);\n    mExtendedConfigurator->setFocus();\n}\n\nCSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory,\n                                          CSMDoc::Document& document,\n                                          const CSMWorld::UniversalId& id,\n                                          QWidget *parent)\n: QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition(false), mRow(0), mColumn(0)\n{\n    for (int i=0; i<4; ++i)\n        mStatusCount[i] = 0;\n\n    setVisible (false);\n\n    mLayout = new QStackedLayout;\n    mLayout->setContentsMargins (0, 0, 0, 0);\n    connect (mLayout, SIGNAL (currentChanged (int)), this, SLOT (currentWidgetChanged (int)));\n\n    mStatus = new QLabel;\n\n    mStatusBar = new QStatusBar(this);\n\n    mStatusBar->addWidget (mStatus);\n\n    mLayout->addWidget (mStatusBar);\n\n    setLayout (mLayout);\n\n    mCreator = creatorFactory.makeCreator (document, id);\n\n    if (mCreator)\n    {\n        mCreator->installEventFilter(this);\n        mLayout->addWidget (mCreator);\n\n        connect (mCreator, SIGNAL (done()), this, SLOT (requestDone()));\n\n        connect (mCreator, SIGNAL (requestFocus (const std::string&)),\n            this, SIGNAL (requestFocus (const std::string&)));\n    }\n\n    mExtendedConfigurator = new ExtendedCommandConfigurator (document, id, this);\n    mExtendedConfigurator->installEventFilter(this);\n    mLayout->addWidget (mExtendedConfigurator);\n    connect (mExtendedConfigurator, SIGNAL (done()), this, SLOT (requestDone()));\n\n    updateSize();\n}\n\nvoid CSVWorld::TableBottomBox::setEditLock (bool locked)\n{\n    if (mCreator)\n        mCreator->setEditLock (locked);\n    mExtendedConfigurator->setEditLock (locked);\n}\n\nCSVWorld::TableBottomBox::~TableBottomBox()\n{\n    delete mCreator;\n}\n\nbool CSVWorld::TableBottomBox::eventFilter(QObject *object, QEvent *event)\n{\n    if (event->type() == QEvent::KeyPress)\n    {\n        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n        if (keyEvent->key() == Qt::Key_Escape)\n        {\n            requestDone();\n            return true;\n        }\n    }\n    return QWidget::eventFilter(object, event);\n}\n\nvoid CSVWorld::TableBottomBox::setStatusBar (bool show)\n{\n    if (show!=mShowStatusBar)\n    {\n        setVisible (show || (mEditMode != EditMode_None));\n\n        mShowStatusBar = show;\n\n        if (show)\n            updateStatus();\n    }\n}\n\nbool CSVWorld::TableBottomBox::canCreateAndDelete() const\n{\n    return mCreator;\n}\n\nvoid CSVWorld::TableBottomBox::requestDone()\n{\n    if (!mShowStatusBar)\n        setVisible (false);\n    else\n        updateStatus();\n\n    mLayout->setCurrentWidget (mStatusBar);\n    mEditMode = EditMode_None;\n}\n\nvoid CSVWorld::TableBottomBox::currentWidgetChanged(int /*index*/)\n{\n    updateSize();\n}\n\nvoid CSVWorld::TableBottomBox::setStatusMessage (const QString& message)\n{\n    mStatusMessage = message;\n    updateStatus();\n}\n\nvoid CSVWorld::TableBottomBox::selectionSizeChanged (int size)\n{\n    if (mStatusCount[3]!=size)\n    {\n        mStatusMessage = \"\";\n        mStatusCount[3] = size;\n        updateStatus();\n    }\n}\n\nvoid CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modified)\n{\n    bool changed = false;\n\n    if (mStatusCount[0]!=size)\n    {\n        mStatusCount[0] = size;\n        changed = true;\n    }\n\n    if (mStatusCount[1]!=deleted)\n    {\n        mStatusCount[1] = deleted;\n        changed = true;\n    }\n\n    if (mStatusCount[2]!=modified)\n    {\n        mStatusCount[2] = modified;\n        changed = true;\n    }\n\n    if (changed)\n    {\n        mStatusMessage = \"\";\n        updateStatus();\n    }\n}\n\nvoid CSVWorld::TableBottomBox::positionChanged (int row, int column)\n{\n    mRow = row;\n    mColumn = column;\n    mHasPosition = true;\n    updateStatus();\n}\n\nvoid CSVWorld::TableBottomBox::noMorePosition()\n{\n    mHasPosition = false;\n    updateStatus();\n}\n\nvoid CSVWorld::TableBottomBox::createRequest()\n{\n    mCreator->reset();\n    mCreator->toggleWidgets(true);\n    mLayout->setCurrentWidget (mCreator);\n    setVisible (true);\n    mEditMode = EditMode_Creation;\n    mCreator->focus();\n}\n\nvoid CSVWorld::TableBottomBox::cloneRequest(const std::string& id,\n                                            const CSMWorld::UniversalId::Type type)\n{\n    mCreator->reset();\n    mCreator->cloneMode(id, type);\n    mLayout->setCurrentWidget(mCreator);\n    mCreator->toggleWidgets(false);\n    setVisible (true);\n    mEditMode = EditMode_Creation;\n    mCreator->focus();\n}\n\nvoid CSVWorld::TableBottomBox::touchRequest(const std::vector<CSMWorld::UniversalId>& ids)\n{\n    mCreator->touch(ids);\n}\n\nvoid CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds)\n{\n    extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds);\n}\n\nvoid CSVWorld::TableBottomBox::extendedRevertConfigRequest(const std::vector<std::string> &selectedIds)\n{\n    extendedConfigRequest(ExtendedCommandConfigurator::Mode_Revert, selectedIds);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/tablebottombox.hpp",
    "content": "#ifndef CSV_WORLD_BOTTOMBOX_H\n#define CSV_WORLD_BOTTOMBOX_H\n\n#include <QWidget>\n#include <apps/opencs/model/world/universalid.hpp>\n\n#include \"extendedcommandconfigurator.hpp\"\n\nclass QLabel;\nclass QStackedLayout;\nclass QStatusBar;\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVWorld\n{\n    class CreatorFactoryBase;\n    class Creator;\n\n    class TableBottomBox : public QWidget\n    {\n            Q_OBJECT\n\n            enum EditMode { EditMode_None, EditMode_Creation, EditMode_ExtendedConfig };\n\n            bool mShowStatusBar;\n            QLabel *mStatus;\n            QStatusBar *mStatusBar;\n            int mStatusCount[4];\n\n            EditMode mEditMode;\n            Creator *mCreator;\n            ExtendedCommandConfigurator *mExtendedConfigurator;\n\n            QStackedLayout *mLayout;\n            bool mHasPosition;\n            int mRow;\n            int mColumn;\n            QString mStatusMessage;\n\n        private:\n\n            // not implemented\n            TableBottomBox (const TableBottomBox&);\n            TableBottomBox& operator= (const TableBottomBox&);\n\n            void updateSize();\n\n            void updateStatus();\n\n            void extendedConfigRequest(ExtendedCommandConfigurator::Mode mode,\n                                       const std::vector<std::string> &selectedIds);\n\n        public:\n\n            TableBottomBox (const CreatorFactoryBase& creatorFactory,\n                            CSMDoc::Document& document,\n                            const CSMWorld::UniversalId& id,\n                            QWidget *parent = nullptr);\n\n            virtual ~TableBottomBox();\n\n            bool eventFilter(QObject *object, QEvent *event) override;\n\n            void setEditLock (bool locked);\n\n            void setStatusBar (bool show);\n\n            bool canCreateAndDelete() const;\n            ///< Is record creation and deletion supported?\n            ///\n            /// \\note The BotomBox does not partake in the deletion of records.\n\n            void setStatusMessage (const QString& message);\n\n        signals:\n\n            void requestFocus (const std::string& id);\n            ///< Request owner of this box to focus the just created \\a id. The owner may\n            /// ignore this request.\n\n        private slots:\n\n            void requestDone();\n            ///< \\note This slot being called does not imply success.\n\n            void currentWidgetChanged(int index);\n\n        public slots:\n\n            void selectionSizeChanged (int size);\n\n            void tableSizeChanged (int size, int deleted, int modified);\n            ///< \\param size Number of not deleted records\n            /// \\param deleted Number of deleted records\n            /// \\param modified Number of added and modified records\n\n            void positionChanged (int row, int column);\n\n            void noMorePosition();\n\n            void createRequest();\n            void cloneRequest(const std::string& id,\n                              const CSMWorld::UniversalId::Type type);\n            void touchRequest(const std::vector<CSMWorld::UniversalId>&);\n\n            void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);\n            void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/tableeditidaction.cpp",
    "content": "#include \"tableeditidaction.hpp\"\n\n#include <QTableView>\n\n#include \"../../model/world/tablemimedata.hpp\"\n\nCSVWorld::TableEditIdAction::CellData CSVWorld::TableEditIdAction::getCellData(int row, int column) const\n{\n    QModelIndex index = mTable.model()->index(row, column);\n    if (index.isValid())\n    {\n        QVariant display = mTable.model()->data(index, CSMWorld::ColumnBase::Role_Display);\n        QString value = mTable.model()->data(index).toString();\n        return std::make_pair(static_cast<CSMWorld::ColumnBase::Display>(display.toInt()), value);\n    }\n    return std::make_pair(CSMWorld::ColumnBase::Display_None, \"\");\n}\n\nCSVWorld::TableEditIdAction::TableEditIdAction(const QTableView &table, QWidget *parent)\n    : QAction(parent),\n      mTable(table),\n      mCurrentId(CSMWorld::UniversalId::Type_None)\n{}\n\nvoid CSVWorld::TableEditIdAction::setCell(int row, int column)\n{\n    CellData data = getCellData(row, column);\n    CSMWorld::UniversalId::Type idType = CSMWorld::TableMimeData::convertEnums(data.first);\n\n    if (idType != CSMWorld::UniversalId::Type_None)\n    {\n        mCurrentId = CSMWorld::UniversalId(idType, data.second.toUtf8().constData());\n        setText(\"Edit '\" + data.second + \"'\");\n    }\n}\n\nCSMWorld::UniversalId CSVWorld::TableEditIdAction::getCurrentId() const\n{\n    return mCurrentId;\n}\n\nbool CSVWorld::TableEditIdAction::isValidIdCell(int row, int column) const\n{\n    CellData data = getCellData(row, column);\n    CSMWorld::UniversalId::Type idType = CSMWorld::TableMimeData::convertEnums(data.first);\n    return CSMWorld::ColumnBase::isId(data.first) && \n           idType != CSMWorld::UniversalId::Type_None &&\n           !data.second.isEmpty();\n}\n"
  },
  {
    "path": "apps/opencs/view/world/tableeditidaction.hpp",
    "content": "#ifndef CSVWORLD_TABLEEDITIDACTION_HPP\n#define CSVWORLD_TABLEEDITIDACTION_HPP\n\n#include <QAction>\n\n#include \"../../model/world/columnbase.hpp\"\n#include \"../../model/world/universalid.hpp\"\n\nclass QTableView;\n\nnamespace CSVWorld\n{\n    class TableEditIdAction : public QAction\n    {\n            const QTableView &mTable;\n            CSMWorld::UniversalId mCurrentId;\n\n            typedef std::pair<CSMWorld::ColumnBase::Display, QString> CellData;\n            CellData getCellData(int row, int column) const;\n\n        public:\n            TableEditIdAction(const QTableView &table, QWidget *parent = nullptr);\n\n            void setCell(int row, int column);\n\n            CSMWorld::UniversalId getCurrentId() const;\n            bool isValidIdCell(int row, int column) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/tablesubview.cpp",
    "content": "#include \"tablesubview.hpp\"\n\n#include <QVBoxLayout>\n#include <QEvent>\n#include <QHeaderView>\n#include <QApplication>\n#include <QDesktopWidget>\n#include <QDropEvent>\n\n#include \"../../model/doc/document.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n\n#include \"../doc/sizehint.hpp\"\n#include \"../filter/filterbox.hpp\"\n#include \"table.hpp\"\n#include \"tablebottombox.hpp\"\n#include \"creator.hpp\"\n\nCSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,\n    const CreatorFactoryBase& creatorFactory, bool sorting)\n: SubView (id)\n{\n    QVBoxLayout *layout = new QVBoxLayout;\n\n    layout->addWidget (mBottom =\n        new TableBottomBox (creatorFactory, document, id, this), 0);\n\n    layout->insertWidget (0, mTable =\n        new Table (id, mBottom->canCreateAndDelete(), sorting, document), 2);\n\n    mFilterBox = new CSVFilter::FilterBox (document.getData(), this);\n\n    layout->insertWidget (0, mFilterBox);\n\n    CSVDoc::SizeHintWidget *widget = new CSVDoc::SizeHintWidget;\n\n    widget->setLayout (layout);\n\n    setWidget (widget);\n    // prefer height of the screen and full width of the table\n    const QRect rect = QApplication::desktop()->screenGeometry(this);\n    int frameHeight = 40; // set a reasonable default\n    QWidget *topLevel = QApplication::topLevelAt(pos());\n    if (topLevel)\n        frameHeight = topLevel->frameGeometry().height() - topLevel->height();\n    widget->setSizeHint(QSize(mTable->horizontalHeader()->length(), rect.height()-frameHeight));\n\n    connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)),\n        this, SLOT (editRequest (const CSMWorld::UniversalId&, const std::string&)));\n\n    connect (mTable, SIGNAL (selectionSizeChanged (int)),\n        mBottom, SLOT (selectionSizeChanged (int)));\n    connect (mTable, SIGNAL (tableSizeChanged (int, int, int)),\n        mBottom, SLOT (tableSizeChanged (int, int, int)));\n\n    mTable->tableSizeUpdate();\n    mTable->selectionSizeUpdate();\n    mTable->viewport()->installEventFilter(this);\n    mBottom->installEventFilter(this);\n    mFilterBox->installEventFilter(this);\n\n    if (mBottom->canCreateAndDelete())\n    {\n        connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest()));\n\n        connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this,\n                 SLOT(cloneRequest(const CSMWorld::UniversalId&)));\n\n        connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)),\n                mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)));\n\n        connect (mTable, SIGNAL(touchRequest(const std::vector<CSMWorld::UniversalId>&)),\n            mBottom, SLOT(touchRequest(const std::vector<CSMWorld::UniversalId>&)));\n\n        connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector<std::string> &)),\n            mBottom, SLOT(extendedDeleteConfigRequest(const std::vector<std::string> &)));\n        connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector<std::string> &)),\n            mBottom, SLOT(extendedRevertConfigRequest(const std::vector<std::string> &)));\n    }\n    connect (mBottom, SIGNAL (requestFocus (const std::string&)),\n        mTable, SLOT (requestFocus (const std::string&)));\n\n    connect (mFilterBox,\n        SIGNAL (recordFilterChanged (std::shared_ptr<CSMFilter::Node>)),\n        mTable, SLOT (recordFilterChanged (std::shared_ptr<CSMFilter::Node>)));\n\n    connect(mFilterBox, SIGNAL(recordDropped(std::vector<CSMWorld::UniversalId>&, Qt::DropAction)),\n        this, SLOT(createFilterRequest(std::vector<CSMWorld::UniversalId>&, Qt::DropAction)));\n\n    connect (mTable, SIGNAL (closeRequest()), this, SLOT (closeRequest()));\n}\n\nvoid CSVWorld::TableSubView::setEditLock (bool locked)\n{\n    mTable->setEditLock (locked);\n    mBottom->setEditLock (locked);\n}\n\nvoid CSVWorld::TableSubView::editRequest (const CSMWorld::UniversalId& id, const std::string& hint)\n{\n    focusId (id, hint);\n}\n\nvoid CSVWorld::TableSubView::setStatusBar (bool show)\n{\n    mBottom->setStatusBar (show);\n}\n\nvoid CSVWorld::TableSubView::useHint (const std::string& hint)\n{\n    if (hint.empty())\n        return;\n\n    if (hint[0]=='f' && hint.size()>=2)\n        mFilterBox->setRecordFilter (hint.substr (2));\n}\n\nvoid CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone)\n{\n    emit cloneRequest(toClone.getId(), toClone.getType());\n}\n\nvoid CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action)\n{\n    std::vector<std::pair<std::string, std::vector<std::string> > > filterSource;\n\n    std::vector<std::string> refIdColumns = mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(CSMWorld::UniversalId::Type_Referenceable));\n    bool hasRefIdDisplay = !refIdColumns.empty();\n\n    for (std::vector<CSMWorld::UniversalId>::iterator it(types.begin()); it != types.end(); ++it)\n    {\n        CSMWorld::UniversalId::Type type = it->getType();\n        std::vector<std::string> col = mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(type));\n        if(!col.empty())\n        {\n            filterSource.emplace_back(it->getId(), col);\n        }\n\n        if(hasRefIdDisplay && CSMWorld::TableMimeData::isReferencable(type))\n        {\n            filterSource.emplace_back(it->getId(), refIdColumns);\n        }\n    }\n\n    mFilterBox->createFilterRequest(filterSource, action);\n}\n\nbool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event)\n{\n    if (event->type() == QEvent::Drop)\n    {\n        if (QDropEvent* drop = dynamic_cast<QDropEvent*>(event))\n        {\n            const CSMWorld::TableMimeData* tableMimeData = dynamic_cast<const CSMWorld::TableMimeData*>(drop->mimeData());\n            if (!tableMimeData) // May happen when non-records (e.g. plain text) are dragged and dropped\n                return false;\n\n            bool handled = tableMimeData->holdsType(CSMWorld::UniversalId::Type_Filter);\n            if (handled)\n            {\n                mFilterBox->setRecordFilter(tableMimeData->returnMatching(CSMWorld::UniversalId::Type_Filter).getId());\n            }\n            return handled;\n        }\n    }\n    return false;\n}\n\nvoid CSVWorld::TableSubView::requestFocus (const std::string& id)\n{\n    mTable->requestFocus(id);\n}\n"
  },
  {
    "path": "apps/opencs/view/world/tablesubview.hpp",
    "content": "#ifndef CSV_WORLD_TABLESUBVIEW_H\n#define CSV_WORLD_TABLESUBVIEW_H\n\n#include \"../doc/subview.hpp\"\n\n#include <QtCore/qnamespace.h>\n\nclass QModelIndex;\n\nnamespace CSMWorld\n{\n    class IdTable;\n}\n\nnamespace CSMDoc\n{\n    class Document;\n}\n\nnamespace CSVFilter\n{\n    class FilterBox;\n}\n\nnamespace CSVWorld\n{\n    class Table;\n    class TableBottomBox;\n    class CreatorFactoryBase;\n\n    class TableSubView : public CSVDoc::SubView\n    {\n            Q_OBJECT\n\n            Table *mTable;\n            TableBottomBox *mBottom;\n            CSVFilter::FilterBox *mFilterBox;\n\n        public:\n\n            TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,\n                const CreatorFactoryBase& creatorFactory, bool sorting);\n\n            void setEditLock (bool locked) override;\n\n            void setStatusBar (bool show) override;\n\n            void useHint (const std::string& hint) override;\n\n        protected:\n            bool eventFilter(QObject* object, QEvent *event) override;\n\n        signals:\n            void cloneRequest(const std::string&,\n                              const CSMWorld::UniversalId::Type);\n\n        private slots:\n\n            void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);\n            void cloneRequest (const CSMWorld::UniversalId& toClone);\n            void createFilterRequest(std::vector< CSMWorld::UniversalId >& types,\n                                     Qt::DropAction action);\n\n        public slots:\n\n            void requestFocus (const std::string& id);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/util.cpp",
    "content": "#include \"util.hpp\"\n\n#include <limits>\n#include <stdexcept>\n\n#include <QUndoStack>\n#include <QMetaProperty>\n#include <QStyledItemDelegate>\n#include <QLineEdit>\n#include <QComboBox>\n#include <QCheckBox>\n#include <QPlainTextEdit>\n#include <QEvent>\n#include <QItemEditorFactory>\n\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/tablemimedata.hpp\"\n#include \"../../model/world/commanddispatcher.hpp\"\n\n#include \"../widget/coloreditor.hpp\"\n#include \"../widget/droplineedit.hpp\"\n\n#include \"dialoguespinbox.hpp\"\n#include \"scriptedit.hpp\"\n\nCSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model)\n: mModel (model)\n{}\n\nint CSVWorld::NastyTableModelHack::rowCount (const QModelIndex & parent) const\n{\n    return mModel.rowCount (parent);\n}\n\nint CSVWorld::NastyTableModelHack::columnCount (const QModelIndex & parent) const\n{\n    return mModel.columnCount (parent);\n}\n\nQVariant CSVWorld::NastyTableModelHack::data  (const QModelIndex & index, int role) const\n{\n    return mModel.data (index, role);\n}\n\nbool CSVWorld::NastyTableModelHack::setData ( const QModelIndex &index, const QVariant &value, int role)\n{\n    mData = value;\n    return true;\n}\n\nQVariant CSVWorld::NastyTableModelHack::getData() const\n{\n    return mData;\n}\n\n\nCSVWorld::CommandDelegateFactory::~CommandDelegateFactory() {}\n\n\nCSVWorld::CommandDelegateFactoryCollection *CSVWorld::CommandDelegateFactoryCollection::sThis = nullptr;\n\nCSVWorld::CommandDelegateFactoryCollection::CommandDelegateFactoryCollection()\n{\n    if (sThis)\n        throw std::logic_error (\"multiple instances of CSVWorld::CommandDelegateFactoryCollection\");\n\n    sThis = this;\n}\n\nCSVWorld::CommandDelegateFactoryCollection::~CommandDelegateFactoryCollection()\n{\n    sThis = nullptr;\n\n    for (std::map<CSMWorld::ColumnBase::Display, CommandDelegateFactory *>::iterator iter (\n        mFactories.begin());\n        iter!=mFactories.end(); ++iter)\n         delete iter->second;\n}\n\nvoid CSVWorld::CommandDelegateFactoryCollection::add (CSMWorld::ColumnBase::Display display,\n    CommandDelegateFactory *factory)\n{\n    mFactories.insert (std::make_pair (display, factory));\n}\n\nCSVWorld::CommandDelegate *CSVWorld::CommandDelegateFactoryCollection::makeDelegate (\n    CSMWorld::ColumnBase::Display display, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const\n{\n    std::map<CSMWorld::ColumnBase::Display, CommandDelegateFactory *>::const_iterator iter =\n        mFactories.find (display);\n\n    if (iter!=mFactories.end())\n        return iter->second->makeDelegate (dispatcher, document, parent);\n\n    return new CommandDelegate (dispatcher, document, parent);\n}\n\nconst CSVWorld::CommandDelegateFactoryCollection& CSVWorld::CommandDelegateFactoryCollection::get()\n{\n    if (!sThis)\n        throw std::logic_error (\"no instance of CSVWorld::CommandDelegateFactoryCollection\");\n\n    return *sThis;\n}\n\n\nQUndoStack& CSVWorld::CommandDelegate::getUndoStack() const\n{\n    return mDocument.getUndoStack();\n}\n\nCSMDoc::Document& CSVWorld::CommandDelegate::getDocument() const\n{\n    return mDocument;\n}\n\nCSMWorld::ColumnBase::Display CSVWorld::CommandDelegate::getDisplayTypeFromIndex(const QModelIndex &index) const\n{\n    int rawDisplay = index.data(CSMWorld::ColumnBase::Role_Display).toInt();\n    return static_cast<CSMWorld::ColumnBase::Display>(rawDisplay);\n}\n\nvoid CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model,\n    const QModelIndex& index) const\n{\n    if (!mCommandDispatcher)\n        return;\n\n    QVariant variant;\n\n    // Color columns use a custom editor, so we need to fetch selected color from it.\n    CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor);\n    if (colorEditor != nullptr)\n    {\n        variant = colorEditor->colorInt();\n    }\n    else\n    {\n        NastyTableModelHack hack (*model);\n        QStyledItemDelegate::setModelData (editor, &hack, index);\n        variant = hack.getData();\n    }\n\n    if ((model->data (index)!=variant) && (model->flags(index) & Qt::ItemIsEditable))\n        mCommandDispatcher->executeModify (model, index, variant);\n}\n\nCSVWorld::CommandDelegate::CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher,\n    CSMDoc::Document& document, QObject *parent)\n: QStyledItemDelegate (parent), mEditLock (false),\n  mCommandDispatcher (commandDispatcher), mDocument (document)\n{}\n\nvoid CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model,\n        const QModelIndex& index) const\n{\n    if (!mEditLock)\n    {\n        setModelDataImp (editor, model, index);\n    }\n\n    ///< \\todo provide some kind of feedback to the user, indicating that editing is currently not possible.\n}\n\nQWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option,\n    const QModelIndex& index) const\n{\n    CSMWorld::ColumnBase::Display display = getDisplayTypeFromIndex(index);\n\n    // This createEditor() method is called implicitly from tables.\n    // For boolean values in tables use the default editor (combobox).\n    // Checkboxes is looking ugly in the table view.\n    // TODO: Find a better solution?\n    if (display == CSMWorld::ColumnBase::Display_Boolean)\n    {\n        return QItemEditorFactory::defaultFactory()->createEditor(QVariant::Bool, parent);\n    }\n    // For tables the pop-up of the color editor should appear immediately after the editor creation\n    // (the third parameter of ColorEditor's constructor)\n    else if (display == CSMWorld::ColumnBase::Display_Colour)\n    {\n        return new CSVWidget::ColorEditor(index.data().toInt(), parent, true);\n    }\n    return createEditor (parent, option, index, display);\n}\n\nQWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option,\n    const QModelIndex& index, CSMWorld::ColumnBase::Display display) const\n{\n    QVariant variant = index.data();\n    if (!variant.isValid())\n    {\n        variant = index.data(Qt::DisplayRole);\n        if (!variant.isValid())\n        {\n            return nullptr;\n        }\n    }\n\n    // NOTE: for each editor type (e.g. QLineEdit) there needs to be a corresponding\n    // entry in CSVWorld::DialogueDelegateDispatcher::makeEditor()\n    switch (display)\n    {\n        case CSMWorld::ColumnBase::Display_Colour:\n        {\n            return new CSVWidget::ColorEditor(variant.toInt(), parent);\n        }\n        case CSMWorld::ColumnBase::Display_Integer:\n        {\n            DialogueSpinBox *sb = new DialogueSpinBox(parent);\n            sb->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());\n            return sb;\n        }\n\n        case CSMWorld::ColumnBase::Display_SignedInteger8:\n        {\n            DialogueSpinBox *sb = new DialogueSpinBox(parent);\n            sb->setRange(std::numeric_limits<signed char>::min(), std::numeric_limits<signed char>::max());\n            return sb;\n        }\n        case CSMWorld::ColumnBase::Display_SignedInteger16:\n        {\n            DialogueSpinBox *sb = new DialogueSpinBox(parent);\n            sb->setRange(std::numeric_limits<short>::min(), std::numeric_limits<short>::max());\n            return sb;\n        }\n\n        case CSMWorld::ColumnBase::Display_UnsignedInteger8:\n        {\n            DialogueSpinBox *sb = new DialogueSpinBox(parent);\n            sb->setRange(0, std::numeric_limits<unsigned char>::max());\n            return sb;\n        }\n\n        case CSMWorld::ColumnBase::Display_UnsignedInteger16:\n        {\n            DialogueSpinBox *sb = new DialogueSpinBox(parent);\n            sb->setRange(0, std::numeric_limits<unsigned short>::max());\n            return sb;\n        }\n\n        case CSMWorld::ColumnBase::Display_Var:\n\n            return new QLineEdit(parent);\n\n        case CSMWorld::ColumnBase::Display_Float:\n        {\n            DialogueDoubleSpinBox *dsb = new DialogueDoubleSpinBox(parent);\n            dsb->setRange(-std::numeric_limits<float>::max(), std::numeric_limits<float>::max());\n            dsb->setSingleStep(0.01f);\n            dsb->setDecimals(3);\n            return dsb;\n        }\n\n        case CSMWorld::ColumnBase::Display_Double:\n        {\n            DialogueDoubleSpinBox *dsb = new DialogueDoubleSpinBox(parent);\n            dsb->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max());\n            dsb->setSingleStep(0.01f);\n            dsb->setDecimals(6);\n            return dsb;\n        }\n\n        /// \\todo implement size limit. QPlainTextEdit does not support a size limit.\n        case CSMWorld::ColumnBase::Display_LongString:\n        case CSMWorld::ColumnBase::Display_LongString256:\n        {\n            QPlainTextEdit *edit = new QPlainTextEdit(parent);\n            edit->setUndoRedoEnabled (false);\n            return edit;\n        }\n\n        case CSMWorld::ColumnBase::Display_Boolean:\n\n            return new QCheckBox(parent);\n\n        case CSMWorld::ColumnBase::Display_ScriptLines:\n\n            return new ScriptEdit (mDocument, ScriptHighlighter::Mode_Console, parent);\n\n        case CSMWorld::ColumnBase::Display_String:\n        // For other Display types (that represent record IDs) with drop support IdCompletionDelegate is used\n\n            return new CSVWidget::DropLineEdit(display, parent);\n\n        case CSMWorld::ColumnBase::Display_String32:\n        {\n        // For other Display types (that represent record IDs) with drop support IdCompletionDelegate is used\n            CSVWidget::DropLineEdit *widget = new CSVWidget::DropLineEdit(display, parent);\n            widget->setMaxLength (32);\n            return widget;\n        }\n\n        default:\n\n            return QStyledItemDelegate::createEditor (parent, option, index);\n    }\n}\n\nvoid CSVWorld::CommandDelegate::setEditLock (bool locked)\n{\n    mEditLock = locked;\n}\n\nbool CSVWorld::CommandDelegate::isEditLocked() const\n{\n    return mEditLock;\n}\n\nvoid CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index) const\n{\n    setEditorData (editor, index, false);\n}\n\nvoid CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const\n{\n    QVariant variant = index.data(Qt::EditRole);\n    if (tryDisplay)\n    {\n        if (!variant.isValid())\n        {\n            variant = index.data(Qt::DisplayRole);\n            if (!variant.isValid())\n            {\n                return;\n            }\n        }\n        QPlainTextEdit* plainTextEdit = qobject_cast<QPlainTextEdit*>(editor);\n        if(plainTextEdit) //for some reason it is easier to brake the loop here\n        {\n            if (plainTextEdit->toPlainText() == variant.toString())\n            {\n                return;\n            }\n        }\n    }\n\n    // Color columns use a custom editor, so we need explicitly set a data for it\n    CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor);\n    if (colorEditor != nullptr)\n    {\n        colorEditor->setColor(variant.toInt());\n        return;\n    }\n\n    QByteArray n = editor->metaObject()->userProperty().name();\n\n    if (n == \"dateTime\")\n    {\n        if (editor->inherits(\"QTimeEdit\"))\n            n = \"time\";\n        else if (editor->inherits(\"QDateEdit\"))\n            n = \"date\";\n    }\n\n    if (!n.isEmpty())\n    {\n        if (!variant.isValid())\n            variant = QVariant(editor->property(n).userType(), (const void *)nullptr);\n        editor->setProperty(n, variant);\n    }\n\n}\n\nvoid CSVWorld::CommandDelegate::settingChanged (const CSMPrefs::Setting *setting) {}\n"
  },
  {
    "path": "apps/opencs/view/world/util.hpp",
    "content": "#ifndef CSV_WORLD_UTIL_H\n#define CSV_WORLD_UTIL_H\n\n#include <map>\n\n#include <QAbstractTableModel>\n#include <QStyledItemDelegate>\n\n\n#ifndef Q_MOC_RUN\n#include \"../../model/world/columnbase.hpp\"\n#include \"../../model/doc/document.hpp\"\n#endif\n\nclass QUndoStack;\n\nnamespace CSMWorld\n{\n    class TableMimeData;\n    class UniversalId;\n    class CommandDispatcher;\n}\n\nnamespace CSMPrefs\n{\n    class Setting;\n}\n\nnamespace CSVWorld\n{\n    ///< \\brief Getting the data out of an editor widget\n    ///\n    /// Really, Qt? Really?\n    class NastyTableModelHack : public QAbstractTableModel\n    {\n            QAbstractItemModel& mModel;\n            QVariant mData;\n\n        public:\n\n            NastyTableModelHack (QAbstractItemModel& model);\n\n            int rowCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            int columnCount (const QModelIndex & parent = QModelIndex()) const override;\n\n            QVariant data  (const QModelIndex & index, int role = Qt::DisplayRole) const override;\n\n            bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;\n\n            QVariant getData() const;\n    };\n\n    class CommandDelegate;\n\n    class CommandDelegateFactory\n    {\n        public:\n\n            virtual ~CommandDelegateFactory();\n\n            virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher,\n                CSMDoc::Document& document, QObject *parent)\n                const = 0;\n            ///< The ownership of the returned CommandDelegate is transferred to the caller.\n    };\n\n    class CommandDelegateFactoryCollection\n    {\n            static CommandDelegateFactoryCollection *sThis;\n            std::map<CSMWorld::ColumnBase::Display, CommandDelegateFactory *> mFactories;\n\n        private:\n\n            // not implemented\n            CommandDelegateFactoryCollection (const CommandDelegateFactoryCollection&);\n            CommandDelegateFactoryCollection& operator= (const CommandDelegateFactoryCollection&);\n\n        public:\n\n            CommandDelegateFactoryCollection();\n\n            ~CommandDelegateFactoryCollection();\n\n            void add (CSMWorld::ColumnBase::Display display, CommandDelegateFactory *factory);\n            ///< The ownership of \\a factory is transferred to *this.\n            ///\n            /// This function must not be called more than once per value of \\a display.\n\n            CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display,\n                CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document,\n                QObject *parent) const;\n            ///< The ownership of the returned CommandDelegate is transferred to the caller.\n            ///\n            /// If no factory is registered for \\a display, a CommandDelegate will be returned.\n\n            static const CommandDelegateFactoryCollection& get();\n\n    };\n\n    ///< \\brief Use commands instead of manipulating the model directly\n    class CommandDelegate : public QStyledItemDelegate\n    {\n            Q_OBJECT\n\n            bool mEditLock;\n            CSMWorld::CommandDispatcher *mCommandDispatcher;\n            CSMDoc::Document& mDocument;\n\n        protected:\n\n            QUndoStack& getUndoStack() const;\n\n            CSMDoc::Document& getDocument() const;\n\n            CSMWorld::ColumnBase::Display getDisplayTypeFromIndex(const QModelIndex &index) const;\n\n            virtual void setModelDataImp (QWidget *editor, QAbstractItemModel *model,\n                const QModelIndex& index) const;\n\n        public:\n\n            /// \\param commandDispatcher If CommandDelegate will be only be used on read-only\n            /// cells, a 0-pointer can be passed here.\n            CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher, CSMDoc::Document& document, QObject *parent);\n\n            void setModelData (QWidget *editor, QAbstractItemModel *model,\n                const QModelIndex& index) const override;\n\n            QWidget *createEditor (QWidget *parent,\n                                           const QStyleOptionViewItem& option,\n                                           const QModelIndex& index) const override;\n\n            virtual QWidget *createEditor (QWidget *parent,\n                                           const QStyleOptionViewItem& option,\n                                           const QModelIndex& index,\n                                           CSMWorld::ColumnBase::Display display) const;\n\n            void setEditLock (bool locked);\n\n            bool isEditLocked() const;\n\n            ///< \\return Does column require update?\n\n            void setEditorData (QWidget *editor, const QModelIndex& index) const override;\n\n            virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const;\n\n            /// \\attention This is not a slot. For ordering reasons this function needs to be\n            /// called manually from the parent object's settingChanged function.\n            virtual void settingChanged (const CSMPrefs::Setting *setting);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/opencs/view/world/vartypedelegate.cpp",
    "content": "#include \"vartypedelegate.hpp\"\n\n#include <QUndoStack>\n\n#include \"../../model/world/commands.hpp\"\n#include \"../../model/world/columns.hpp\"\n#include \"../../model/world/commandmacro.hpp\"\n\nvoid CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QModelIndex& index, int type)\n    const\n{\n    QModelIndex next = model->index (index.row(), index.column()+1);\n\n    QVariant old = model->data (next);\n\n    QVariant value;\n\n    switch (type)\n    {\n        case ESM::VT_Short:\n        case ESM::VT_Int:\n        case ESM::VT_Long:\n\n            value = old.toInt();\n            break;\n\n        case ESM::VT_Float:\n\n            value = old.toFloat();\n            break;\n\n        case ESM::VT_String:\n\n            value = old.toString();\n            break;\n\n        default: break; // ignore the rest\n    }\n\n    CSMWorld::CommandMacro macro (getUndoStack(), \"Modify \" + model->headerData (index.column(), Qt::Horizontal, Qt::DisplayRole).toString());\n\n    macro.push (new CSMWorld::ModifyCommand (*model, index, type));\n    macro.push (new CSMWorld::ModifyCommand (*model, next, value));\n}\n\nCSVWorld::VarTypeDelegate::VarTypeDelegate (const std::vector<std::pair<int, QString> >& values,\n    CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)\n: EnumDelegate (values, dispatcher, document, parent)\n{}\n\n\nCSVWorld::VarTypeDelegateFactory::VarTypeDelegateFactory (ESM::VarType type0,\n    ESM::VarType type1, ESM::VarType type2, ESM::VarType type3)\n{\n    if (type0!=ESM::VT_Unknown)\n        add (type0);\n\n    if (type1!=ESM::VT_Unknown)\n        add (type1);\n\n    if (type2!=ESM::VT_Unknown)\n        add (type2);\n\n    if (type3!=ESM::VT_Unknown)\n        add (type3);\n}\n\nCSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate (\n    CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const\n{\n    return new VarTypeDelegate (mValues, dispatcher, document, parent);\n}\n\nvoid CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type)\n{\n    std::vector<std::pair<int,std::string>> enums =\n        CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_ValueType);\n\n    if (static_cast<size_t>(type) >= enums.size())\n        throw std::logic_error (\"Unsupported variable type\");\n\n    mValues.emplace_back(type, QString::fromUtf8 (enums[type].second.c_str()));\n}\n"
  },
  {
    "path": "apps/opencs/view/world/vartypedelegate.hpp",
    "content": "#ifndef CSV_WORLD_VARTYPEDELEGATE_H\n#define CSV_WORLD_VARTYPEDELEGATE_H\n\n#include <components/esm/variant.hpp>\n\n#include \"enumdelegate.hpp\"\n\nnamespace CSVWorld\n{\n    class VarTypeDelegate : public EnumDelegate\n    {\n        private:\n\n            void addCommands (QAbstractItemModel *model,\n                const QModelIndex& index, int type) const override;\n\n        public:\n\n            VarTypeDelegate (const std::vector<std::pair<int, QString> >& values,\n                CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent);\n    };\n\n    class VarTypeDelegateFactory : public CommandDelegateFactory\n    {\n            std::vector<std::pair<int, QString> > mValues;\n\n        public:\n\n            VarTypeDelegateFactory (ESM::VarType type0 = ESM::VT_Unknown,\n                ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown,\n                ESM::VarType type3 = ESM::VT_Unknown);\n\n            CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher,\n                CSMDoc::Document& document, QObject *parent) const override;\n            ///< The ownership of the returned CommandDelegate is transferred to the caller.\n\n            void add (ESM::VarType type);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/CMakeLists.txt",
    "content": "# local files\nset(GAME\n    main.cpp\n    engine.cpp\n\n    ${CMAKE_SOURCE_DIR}/files/tes3mp/tes3mp.rc\n    ${CMAKE_SOURCE_DIR}/files/tes3mp/tes3mp.exe.manifest\n)\n\nif (ANDROID)\n    set(GAME ${GAME} android_main.cpp)\nendif()\n\nset(GAME_HEADER\n    engine.hpp\n)\n\nsource_group(game FILES ${GAME} ${GAME_HEADER})\n\nadd_openmw_dir (mwrender\n    actors objects renderingmanager animation rotatecontroller sky npcanimation vismask\n    creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager\n    bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation\n    renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover\n    )\n\nadd_openmw_dir (mwinput\n    actions actionmanager bindingsmanager controllermanager controlswitch\n    inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager\n    )\n\nadd_openmw_dir (mwgui\n    layout textinput widgets race class birth review windowmanagerimp console dialogue\n    windowbase statswindow messagebox journalwindow charactercreation\n    mapwindow windowpinnablebase tooltips scrollwindow bookwindow resourceskin\n    formatting inventorywindow container hud countdialog tradewindow settingswindow\n    confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu\n    itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog\n    enchantingdialog trainingwindow travelwindow exposedwindow cursor spellicons\n    merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks\n    itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview\n    tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog\n    recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview\n    draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher\n    )\n\nadd_openmw_dir (mwdialogue\n    dialoguemanagerimp journalimp journalentry quest topic filter selectwrapper hypertextparser keywordsearch scripttest\n    )\n\nadd_openmw_dir (mwscript\n    locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions\n    guiextensions soundextensions skyextensions statsextensions containerextensions\n    aiextensions controlextensions extensions globalscripts ref dialogueextensions\n    animationextensions transformationextensions consoleextensions userextensions\n    )\n\nadd_openmw_dir (mwsound\n    soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output\n    loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater volumesettings\n    )\n\nadd_openmw_dir (mwworld\n    refdata worldimp scene globals class action nullaction actionteleport\n    containerstore actiontalk actiontake manualref player cellvisitors failedaction\n    cells localscripts customdata inventorystore ptr actionopen actionread actionharvest\n    actionequip timestamp actionalchemy cellstore actionapply actioneat\n    store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor\n    contentloader esmloader actiontrap cellreflist cellref weather projectilemanager\n    cellpreloader datetimemanager\n    )\n\nadd_openmw_dir (mwphysics\n    physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback\n    contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile\n    actorconvexcallback raycasting mtphysics contacttestwrapper projectileconvexcallback\n    )\n\nadd_openmw_dir (mwclass\n    classes activator creature npc weapon armor potion apparatus book clothing container door\n    ingredient creaturelevlist itemlevlist light lockpick misc probe repair static actor bodypart\n    )\n\nadd_openmw_dir (mwmechanics\n    mechanicsmanagerimp stat creaturestats magiceffects movement actorutil spelllist\n    drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe\n    aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance\n    disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning\n    character actors objects aistate trading weaponpriority spellpriority weapontype spellutil tickableeffects\n    spellabsorption linkedeffects\n    )\n\nadd_openmw_dir (mwstate\n    statemanagerimp charactermanager character quicksavemanager\n    )\n\nadd_openmw_dir (mwbase\n    environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager\n    inputmanager windowmanager statemanager\n    )\n\nadd_openmw_dir (mwmp Main Networking LocalSystem LocalPlayer DedicatedPlayer PlayerList LocalActor DedicatedActor ActorList\n    ObjectList Worldstate Cell CellController GUIController MechanicsHelper RecordHelper ScriptController\n    )\n\nadd_openmw_dir (mwmp/GUI GUIChat GUILogin PlayerMarkerCollection GUIDialogList TextInputDialog\n    )\n\nadd_openmw_dir(mwmp/processors BaseClientPacketProcessor SystemProcessor PlayerProcessor ObjectProcessor ActorProcessor\n    WorldstateProcessor ProcessorInitializer\n    )\n\nadd_openmw_dir (mwmp/processors/system ProcessorSystemHandshake\n    )\n\nadd_openmw_dir (mwmp/processors/actor ProcessorActorAI ProcessorActorAnimFlags ProcessorActorAnimPlay ProcessorActorAttack\n    ProcessorActorAuthority ProcessorActorCast ProcessorActorCellChange ProcessorActorDeath ProcessorActorEquipment\n    ProcessorActorList ProcessorActorPosition ProcessorActorSpeech ProcessorActorSpellsActive ProcessorActorStatsDynamic\n    ProcessorActorTest\n    )\n\nadd_openmw_dir (mwmp/processors/player ProcessorChatMessage ProcessorGUIMessageBox ProcessorUserDisconnected\n    ProcessorUserMyID ProcessorGameSettings ProcessorPlayerAlly ProcessorPlayerAnimFlags ProcessorPlayerAnimPlay\n    ProcessorPlayerAttack ProcessorPlayerAttribute ProcessorPlayerBaseInfo ProcessorPlayerBehavior ProcessorPlayerBook\n    ProcessorPlayerBounty ProcessorPlayerCast ProcessorPlayerCellChange ProcessorPlayerCellState ProcessorPlayerCharClass\n    ProcessorPlayerCharGen ProcessorPlayerCooldowns ProcessorPlayerDeath ProcessorPlayerDisposition ProcessorPlayerEquipment\n    ProcessorPlayerFaction ProcessorPlayerInput ProcessorPlayerInventory ProcessorPlayerItemUse ProcessorPlayerJail\n    ProcessorPlayerJournal ProcessorPlayerLevel ProcessorPlayerMiscellaneous ProcessorPlayerMomentum ProcessorPlayerPosition \n    ProcessorPlayerQuickKeys ProcessorPlayerReputation ProcessorPlayerResurrect ProcessorPlayerShapeshift\n    ProcessorPlayerSkill ProcessorPlayerSpeech ProcessorPlayerSpellbook ProcessorPlayerSpellsActive\n    ProcessorPlayerStatsDynamic ProcessorPlayerTopic\n    )\n\nadd_openmw_dir (mwmp/processors/object BaseObjectProcessor\n\n    ProcessorConsoleCommand ProcessorContainer ProcessorDoorDestination ProcessorDoorState ProcessorMusicPlay\n    ProcessorVideoPlay\n\n    ProcessorObjectActivate ProcessorObjectAnimPlay ProcessorObjectAttach ProcessorObjectDelete\n    ProcessorObjectDialogueChoice ProcessorObjectHit ProcessorObjectLock ProcessorObjectMove ProcessorObjectPlace\n    ProcessorObjectRestock ProcessorObjectRotate ProcessorObjectScale ProcessorObjectSound ProcessorObjectSpawn\n    ProcessorObjectState ProcessorObjectTrap ProcessorClientScriptLocal ProcessorScriptMemberShort\n    ProcessorObjectMiscellaneous\n    )\n\nadd_openmw_dir (mwmp/processors/worldstate ProcessorCellReset ProcessorClientScriptGlobal ProcessorClientScriptSettings\n    ProcessorRecordDynamic ProcessorWorldCollisionOverride ProcessorWorldDestinationOverride ProcessorWorldKillCount\n    ProcessorWorldMap ProcessorWorldRegionAuthority ProcessorWorldTime ProcessorWorldWeather\n    )\n\n# Main executable\n\nif (NOT ANDROID)\n    openmw_add_executable(tes3mp\n        ${OPENMW_FILES}\n        ${GAME} ${GAME_HEADER}\n        ${APPLE_BUNDLE_RESOURCES}\n    )\nelse ()\n    add_library(tes3mp\n        SHARED\n        ${OPENMW_FILES}\n        ${GAME} ${GAME_HEADER}\n    )\nendif ()\n\n# Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING\n# when we change the backend.\ninclude_directories(\n    ${FFmpeg_INCLUDE_DIRS}\n)\n\ntarget_link_libraries(tes3mp\n    # CMake's built-in OSG finder does not use pkgconfig, so we have to\n    # manually ensure the order is correct for inter-library dependencies.\n    # This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.\n    # https://gitlab.kitware.com/cmake/cmake/-/issues/21701\n    ${OSGPARTICLE_LIBRARIES}\n    ${OSGVIEWER_LIBRARIES}\n    ${OSGGA_LIBRARIES}\n    ${OSGSHADOW_LIBRARIES}\n    ${OSGDB_LIBRARIES}\n    ${OSGUTIL_LIBRARIES}\n    ${OSG_LIBRARIES}\n\n    ${Boost_SYSTEM_LIBRARY}\n    ${Boost_THREAD_LIBRARY}\n    ${Boost_FILESYSTEM_LIBRARY}\n    ${Boost_PROGRAM_OPTIONS_LIBRARY}\n    ${OPENAL_LIBRARY}\n    ${FFmpeg_LIBRARIES}\n    ${MyGUI_LIBRARIES}\n    ${SDL2_LIBRARY}\n    ${RecastNavigation_LIBRARIES}\n    \"osg-ffmpeg-videoplayer\"\n    \"oics\"\n    components\n    ${RakNet_LIBRARY}\n)\n\nif(OSG_STATIC)\n    unset(_osg_plugins_static_files)\n    add_library(openmw_osg_plugins INTERFACE)\n    foreach(_plugin ${USED_OSG_PLUGINS})\n        string(TOUPPER ${_plugin} _plugin_uc)\n        if(OPENMW_USE_SYSTEM_OSG)\n            list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY})\n        else()\n            list(APPEND _osg_plugins_static_files $<TARGET_FILE:${${_plugin_uc}_LIBRARY}>)\n            target_link_libraries(openmw_osg_plugins INTERFACE $<TARGET_PROPERTY:${${_plugin_uc}_LIBRARY},LINK_LIBRARIES>)\n            add_dependencies(openmw_osg_plugins ${${_plugin_uc}_LIBRARY})\n        endif()\n    endforeach()\n    # We use --whole-archive because OSG plugins use registration.\n    get_whole_archive_options(_opts ${_osg_plugins_static_files})\n    target_link_options(openmw_osg_plugins INTERFACE ${_opts})\n    target_link_libraries(tes3mp openmw_osg_plugins)\n\n    if(OPENMW_USE_SYSTEM_OSG)\n        # OSG plugin pkgconfig files are missing these dependencies.\n        # https://github.com/openscenegraph/OpenSceneGraph/issues/1052\n        target_link_libraries(tes3mp freetype jpeg png)\n    endif()\nendif(OSG_STATIC)\n\nif (ANDROID)\n    target_link_libraries(tes3mp EGL android log z)\nendif (ANDROID)\n\nif (USE_SYSTEM_TINYXML)\n    target_link_libraries(tes3mp ${TinyXML_LIBRARIES})\nendif()\n\nif (NOT UNIX)\ntarget_link_libraries(tes3mp ${SDL2MAIN_LIBRARY})\nendif()\n\n# Fix for not visible pthreads functions for linker with glibc 2.15\nif (UNIX AND NOT APPLE)\ntarget_link_libraries(tes3mp ${CMAKE_THREAD_LIBS_INIT})\nendif()\n\nif(APPLE)\n    set(BUNDLE_RESOURCES_DIR \"${APP_BUNDLE_DIR}/Contents/Resources\")\n\n    set(OPENMW_MYGUI_FILES_ROOT ${BUNDLE_RESOURCES_DIR})\n    set(OPENMW_SHADERS_ROOT ${BUNDLE_RESOURCES_DIR})\n\n    add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)\n\n    configure_file(\"${OpenMW_BINARY_DIR}/defaults.bin\" ${BUNDLE_RESOURCES_DIR} COPYONLY)\n    configure_file(\"${OpenMW_BINARY_DIR}/openmw.cfg\" ${BUNDLE_RESOURCES_DIR} COPYONLY)\n    configure_file(\"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt\" ${BUNDLE_RESOURCES_DIR} COPYONLY)\n\n    add_custom_command(TARGET tes3mp\n        POST_BUILD\n        COMMAND cp \"${OpenMW_BINARY_DIR}/resources/version\" \"${BUNDLE_RESOURCES_DIR}/resources\")\n\n    find_library(COCOA_FRAMEWORK Cocoa)\n    find_library(IOKIT_FRAMEWORK IOKit)\n    target_link_libraries(tes3mp ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK})\n\n    if (FFmpeg_FOUND)\n        find_library(COREVIDEO_FRAMEWORK CoreVideo)\n        find_library(VDA_FRAMEWORK VideoDecodeAcceleration)\n        target_link_libraries(tes3mp z ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK})\n    endif()\nendif(APPLE)\n\nif (BUILD_WITH_CODE_COVERAGE)\n  add_definitions (--coverage)\n  target_link_libraries(tes3mp gcov)\nendif()\n\nif (MSVC)\n    # Debug version needs increased number of sections beyond 2^16\n    if (CMAKE_CL_64)\n        set (CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} /bigobj\")\n    endif (CMAKE_CL_64)\nendif (MSVC)\n\nif (WIN32)\n    INSTALL(TARGETS tes3mp RUNTIME DESTINATION \".\")\nendif (WIN32)\n"
  },
  {
    "path": "apps/openmw/android_main.cpp",
    "content": "int stderr = 0; // Hack: fix linker error\n\n#include \"SDL_main.h\"\n#include <SDL_gamecontroller.h>\n#include <SDL_mouse.h>\n#include <SDL_events.h>\n\n/*******************************************************************************\n Functions called by JNI\n *******************************************************************************/\n#include <jni.h>\n\n/* Called before  to initialize JNI bindings  */\n\nextern void SDL_Android_Init(JNIEnv* env, jclass cls);\nextern int argcData;\nextern const char **argvData;\nvoid releaseArgv();\n\n\nextern \"C\" int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) {\n    int ret = 0;\n    SDL_GetMouseState(&ret, nullptr);\n    return ret;\n}\n\n\nextern \"C\" int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) {\n    int ret = 0;\n    SDL_GetMouseState(nullptr, &ret);\n    return ret;\n}\n\nextern \"C\" int Java_org_libsdl_app_SDLActivity_isMouseShown(JNIEnv *env, jclass cls, jobject obj) {\n    return SDL_ShowCursor(SDL_QUERY);\n}\n\nextern SDL_Window *Android_Window;\nextern \"C\" int SDL_SendMouseMotion(SDL_Window * window, int mouseID, int relative, int x, int y);\nextern \"C\" void Java_org_libsdl_app_SDLActivity_sendRelativeMouseMotion(JNIEnv *env, jclass cls, int x, int y) {\n    SDL_SendMouseMotion(Android_Window, 0, 1, x, y);\n}\n\nextern \"C\" int SDL_SendMouseButton(SDL_Window * window, int mouseID, Uint8 state, Uint8 button);\nextern \"C\" void Java_org_libsdl_app_SDLActivity_sendMouseButton(JNIEnv *env, jclass cls, int state, int button) {\n    SDL_SendMouseButton(Android_Window, 0, state, button);\n}\n\nextern \"C\" int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) {\n    setenv(\"OPENMW_DECOMPRESS_TEXTURES\", \"1\", 1);\n\n    // On Android, we use a virtual controller with guid=\"Virtual\"\n    SDL_GameControllerAddMapping(\"5669727475616c000000000000000000,Virtual,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4\");\n\n    return 0;\n}\n"
  },
  {
    "path": "apps/openmw/doc.hpp",
    "content": "// Note: This is not a regular source file.\n\n/// \\ingroup apps\n/// \\defgroup openmw OpenMW\n\n/// \\namespace OMW\n/// \\ingroup openmw\n/// \\brief Integration of OpenMW-subsystems\n\n/// \\namespace MWDialogue\n/// \\ingroup openmw\n/// \\brief NPC dialogues\n\n/// \\namespace MWMechanics\n/// \\ingroup openmw\n/// \\brief Game mechanics and NPC-AI\n\n/// \\namespace MWSound\n/// \\ingroup openmw\n/// \\brief Sound & music\n\n/// \\namespace MWGUI\n/// \\ingroup openmw\n/// \\brief HUD and windows\n\n/// \\namespace MWRender\n/// \\ingroup openmw\n/// \\brief Rendering\n\n/// \\namespace MWWorld\n/// \\ingroup openmw\n/// \\brief World data\n\n/// \\namespace MWClass\n/// \\ingroup openmw\n/// \\brief Workaround for non-OOP design of the record system\n\n/// \\namespace MWInput\n/// \\ingroup openmw\n/// \\brief User input and character controls\n\n/// \\namespace MWScript\n/// \\ingroup openmw\n/// \\brief MW-specific script extensions and integration of the script system into OpenMW\n"
  },
  {
    "path": "apps/openmw/engine.cpp",
    "content": "#include \"engine.hpp\"\n\n#include <iomanip>\n#include <fstream>\n#include <chrono>\n#include <thread>\n\n#include <boost/filesystem/fstream.hpp>\n\n#include <osgViewer/ViewerEventHandlers>\n#include <osgDB/ReadFile>\n#include <osgDB/WriteFile>\n\n#include <SDL.h>\n\n#include <components/debug/debuglog.hpp>\n#include <components/debug/gldebug.hpp>\n\n#include <components/misc/rng.hpp>\n\n#include <components/vfs/manager.hpp>\n#include <components/vfs/registerarchives.hpp>\n\n#include <components/sdlutil/sdlgraphicswindow.hpp>\n#include <components/sdlutil/imagetosurface.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/stats.hpp>\n\n#include <components/compiler/extensions0.hpp>\n\n#include <components/sceneutil/workqueue.hpp>\n\n#include <components/files/configurationmanager.hpp>\n\n#include <components/version/version.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"mwmp/Main.hpp\"\n#include \"mwmp/GUIController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/detournavigator/navigator.hpp>\n\n#include <components/misc/frameratelimiter.hpp>\n\n#include \"mwinput/inputmanagerimp.hpp\"\n\n#include \"mwgui/windowmanagerimp.hpp\"\n\n#include \"mwscript/scriptmanagerimp.hpp\"\n#include \"mwscript/interpretercontext.hpp\"\n\n#include \"mwsound/soundmanagerimp.hpp\"\n\n#include \"mwworld/class.hpp\"\n#include \"mwworld/player.hpp\"\n#include \"mwworld/worldimp.hpp\"\n\n#include \"mwrender/vismask.hpp\"\n\n#include \"mwclass/classes.hpp\"\n\n#include \"mwdialogue/dialoguemanagerimp.hpp\"\n#include \"mwdialogue/journalimp.hpp\"\n#include \"mwdialogue/scripttest.hpp\"\n\n#include \"mwmechanics/mechanicsmanagerimp.hpp\"\n\n#include \"mwstate/statemanagerimp.hpp\"\n\nnamespace\n{\n    void checkSDLError(int ret)\n    {\n        if (ret != 0)\n            Log(Debug::Error) << \"SDL error: \" << SDL_GetError();\n    }\n\n    struct UserStats\n    {\n        const std::string mLabel;\n        const std::string mBegin;\n        const std::string mEnd;\n        const std::string mTaken;\n\n        UserStats(const std::string& label, const std::string& prefix)\n            : mLabel(label),\n              mBegin(prefix + \"_time_begin\"),\n              mEnd(prefix + \"_time_end\"),\n              mTaken(prefix + \"_time_taken\")\n        {}\n    };\n\n    enum class UserStatsType : std::size_t\n    {\n        Input,\n        Sound,\n        State,\n        Script,\n        Mechanics,\n        Physics,\n        PhysicsWorker,\n        World,\n        Gui,\n\n        Number,\n    };\n\n    template <UserStatsType type>\n    struct UserStatsValue\n    {\n        static const UserStats sValue;\n    };\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::Input>::sValue {\"Input\", \"input\"};\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::Sound>::sValue {\"Sound\", \"sound\"};\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::State>::sValue {\"State\", \"state\"};\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::Script>::sValue {\"Script\", \"script\"};\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::Mechanics>::sValue {\"Mech\", \"mechanics\"};\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::Physics>::sValue {\"Phys\", \"physics\"};\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::PhysicsWorker>::sValue {\" -Async\", \"physicsworker\"};\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::World>::sValue {\"World\", \"world\"};\n\n    template <>\n    const UserStats UserStatsValue<UserStatsType::Gui>::sValue {\"Gui\", \"gui\"};\n\n    template <UserStatsType type>\n    struct ForEachUserStatsValue\n    {\n        template <class F>\n        static void apply(F&& f)\n        {\n            f(UserStatsValue<type>::sValue);\n            using Next = ForEachUserStatsValue<static_cast<UserStatsType>(static_cast<std::size_t>(type) + 1)>;\n            Next::apply(std::forward<F>(f));\n        }\n    };\n\n    template <>\n    struct ForEachUserStatsValue<UserStatsType::Number>\n    {\n        template <class F>\n        static void apply(F&&) {}\n    };\n\n    template <class F>\n    void forEachUserStatsValue(F&& f)\n    {\n        ForEachUserStatsValue<static_cast<UserStatsType>(0)>::apply(std::forward<F>(f));\n    }\n\n    template <UserStatsType sType>\n    class ScopedProfile\n    {\n        public:\n            ScopedProfile(osg::Timer_t frameStart, unsigned int frameNumber, const osg::Timer& timer, osg::Stats& stats)\n                : mScopeStart(timer.tick()),\n                  mFrameStart(frameStart),\n                  mFrameNumber(frameNumber),\n                  mTimer(timer),\n                  mStats(stats)\n            {\n            }\n\n            ScopedProfile(const ScopedProfile&) = delete;\n            ScopedProfile& operator=(const ScopedProfile&) = delete;\n\n            ~ScopedProfile()\n            {\n                if (!mStats.collectStats(\"engine\"))\n                    return;\n                const osg::Timer_t end = mTimer.tick();\n                const UserStats& stats = UserStatsValue<sType>::sValue;\n\n                mStats.setAttribute(mFrameNumber, stats.mBegin, mTimer.delta_s(mFrameStart, mScopeStart));\n                mStats.setAttribute(mFrameNumber, stats.mTaken, mTimer.delta_s(mScopeStart, end));\n                mStats.setAttribute(mFrameNumber, stats.mEnd, mTimer.delta_s(mFrameStart, end));\n            }\n\n        private:\n            const osg::Timer_t mScopeStart;\n            const osg::Timer_t mFrameStart;\n            const unsigned int mFrameNumber;\n            const osg::Timer& mTimer;\n            osg::Stats& mStats;\n    };\n\n    void initStatsHandler(Resource::Profiler& profiler)\n    {\n        const osg::Vec4f textColor(1.f, 1.f, 1.f, 1.f);\n        const osg::Vec4f barColor(1.f, 1.f, 1.f, 1.f);\n        const float multiplier = 1000;\n        const bool average = true;\n        const bool averageInInverseSpace = false;\n        const float maxValue = 10000;\n\n        forEachUserStatsValue([&] (const UserStats& v)\n        {\n            profiler.addUserStatsLine(v.mLabel, textColor, barColor, v.mTaken, multiplier,\n                                      average, averageInInverseSpace, v.mBegin, v.mEnd, maxValue);\n        });\n        // the forEachUserStatsValue loop is \"run\" at compile time, hence the settings manager is not available.\n        // Unconditionnally add the async physics stats, and then remove it at runtime if necessary\n        if (Settings::Manager::getInt(\"async num threads\", \"Physics\") == 0)\n            profiler.removeUserStatsLine(\" -Async\");\n    }\n}\n\nvoid OMW::Engine::executeLocalScripts()\n{\n    MWWorld::LocalScripts& localScripts = mEnvironment.getWorld()->getLocalScripts();\n\n    localScripts.startIteration();\n    std::pair<std::string, MWWorld::Ptr> script;\n    while (localScripts.getNext(script))\n    {\n        MWScript::InterpreterContext interpreterContext (\n            &script.second.getRefData().getLocals(), script.second);\n\n        /*\n            Start of tes3mp addition\n\n            By comparing its name with a list of script names, check if this script\n            is allowed to send packets about its value changes\n\n            If it is, set a tes3mp-only boolean to true in its interpreterContext\n        */\n        if (mwmp::Main::isValidPacketScript(script.first))\n        {\n            interpreterContext.sendPackets = true;\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Mark this InterpreterContext as having a SCRIPT_LOCAL context\n            and as currently running the script with this name, so that\n            packets sent by the Interpreter can have their\n            origin determined by serverside scripts\n        */\n        interpreterContext.trackContextType(Interpreter::Context::SCRIPT_LOCAL);\n        interpreterContext.trackCurrentScriptName(script.first);\n        /*\n            End of tes3mp addition\n        */\n\n        mEnvironment.getScriptManager()->run (script.first, interpreterContext);\n    }\n}\n\nbool OMW::Engine::frame(float frametime)\n{\n    try\n    {\n        const osg::Timer_t frameStart = mViewer->getStartTick();\n        const unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();\n        const osg::Timer* const timer = osg::Timer::instance();\n        osg::Stats* const stats = mViewer->getViewerStats();\n\n        mEnvironment.setFrameDuration(frametime);\n\n        // update input\n        {\n            ScopedProfile<UserStatsType::Input> profile(frameStart, frameNumber, *timer, *stats);\n            mEnvironment.getInputManager()->update(frametime, false);\n        }\n\n        // When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug.\n        // If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2),\n        // and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21)\n        {\n            ScopedProfile<UserStatsType::Sound> profile(frameStart, frameNumber, *timer, *stats);\n\n            if (!mEnvironment.getWindowManager()->isWindowVisible())\n            {\n                mEnvironment.getSoundManager()->pausePlayback();\n                /*\n                    Start of tes3mp change (major)\n\n                    The game cannot be paused in multiplayer, so prevent that from happening even here\n                */\n                //return false;\n                /*\n                    End of tes3mp change (major)\n                */\n            }\n            else\n                mEnvironment.getSoundManager()->resumePlayback();\n\n            // sound\n            if (mUseSound)\n                mEnvironment.getSoundManager()->update(frametime);\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Update multiplayer processing for the current frame\n        */\n        mwmp::Main::frame(frametime);\n        /*\n            End of tes3mp addition\n        */\n\n        // Main menu opened? Then scripts are also paused.\n        bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu);\n        \n        /*\n            Start of tes3mp change (major)\n\n            Time should not be frozen in multiplayer, so the paused boolean is always set to\n            false instead\n        */\n        paused = false;\n        /*\n            End of tes3mp change (major)\n        */\n\n        // update game state\n        {\n            ScopedProfile<UserStatsType::State> profile(frameStart, frameNumber, *timer, *stats);\n            mEnvironment.getStateManager()->update (frametime);\n        }\n\n        /*\n            Start of tes3mp change (major)\n\n            Whether the GUI is active should have no relevance in multiplayer, so the guiActive\n            boolean is always set to false instead\n        */\n        //bool guiActive = mEnvironment.getWindowManager()->isGuiMode();\n        bool guiActive = false;\n        /*\n            End of tes3mp change (major)\n        */\n\n        {\n            ScopedProfile<UserStatsType::Script> profile(frameStart, frameNumber, *timer, *stats);\n\n            if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)\n            {\n                if (!paused)\n                {\n                    if (mEnvironment.getWorld()->getScriptsEnabled())\n                    {\n                        // local scripts\n                        executeLocalScripts();\n\n                        // global scripts\n                        mEnvironment.getScriptManager()->getGlobalScripts().run();\n                    }\n\n                    mEnvironment.getWorld()->markCellAsUnchanged();\n                }\n\n                if (!guiActive)\n                {\n                    double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;\n                    mEnvironment.getWorld()->advanceTime(hours, true);\n                    mEnvironment.getWorld()->rechargeItems(frametime, true);\n                }\n            }\n        }\n\n        // update mechanics\n        {\n            ScopedProfile<UserStatsType::Mechanics> profile(frameStart, frameNumber, *timer, *stats);\n\n            if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)\n            {\n                mEnvironment.getMechanicsManager()->update(frametime, guiActive);\n            }\n\n            if (mEnvironment.getStateManager()->getState() == MWBase::StateManager::State_Running)\n            {\n                MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();\n                /*\n                    Start of tes3mp change (major)\n\n                    In multiplayer, the game should not end when the player dies,\n                    so the code here has been commented out\n                */\n                //if(!guiActive && player.getClass().getCreatureStats(player).isDead())\n                //    mEnvironment.getStateManager()->endGame();\n                /*\n                    End of tes3mp change (major)\n                */\n            }\n        }\n\n        // update physics\n        {\n            ScopedProfile<UserStatsType::Physics> profile(frameStart, frameNumber, *timer, *stats);\n\n            if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)\n            {\n                mEnvironment.getWorld()->updatePhysics(frametime, guiActive, frameStart, frameNumber, *stats);\n            }\n        }\n\n        // update world\n        {\n            ScopedProfile<UserStatsType::World> profile(frameStart, frameNumber, *timer, *stats);\n\n            if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)\n            {\n                mEnvironment.getWorld()->update(frametime, guiActive);\n            }\n        }\n\n        // update GUI\n        {\n            ScopedProfile<UserStatsType::Gui> profile(frameStart, frameNumber, *timer, *stats);\n            mEnvironment.getWindowManager()->update(frametime);\n        }\n\n        if (stats->collectStats(\"resource\"))\n        {\n            stats->setAttribute(frameNumber, \"FrameNumber\", frameNumber);\n\n            mResourceSystem->reportStats(frameNumber, stats);\n\n            stats->setAttribute(frameNumber, \"WorkQueue\", mWorkQueue->getNumItems());\n            stats->setAttribute(frameNumber, \"WorkThread\", mWorkQueue->getNumActiveThreads());\n\n            mEnvironment.reportStats(frameNumber, *stats);\n        }\n    }\n    catch (const std::exception& e)\n    {\n        Log(Debug::Error) << \"Error in frame: \" << e.what();\n    }\n    return true;\n}\n\nOMW::Engine::Engine(Files::ConfigurationManager& configurationManager)\n  : mWindow(nullptr)\n  , mEncoding(ToUTF8::WINDOWS_1252)\n  , mEncoder(nullptr)\n  , mScreenCaptureOperation(nullptr)\n  , mSkipMenu (false)\n  , mUseSound (true)\n  , mCompileAll (false)\n  , mCompileAllDialogue (false)\n  , mWarningsMode (1)\n  , mScriptConsoleMode (false)\n  , mActivationDistanceOverride(-1)\n  , mGrab(true)\n  , mExportFonts(false)\n  , mRandomSeed(0)\n  , mScriptContext (nullptr)\n  , mFSStrict (false)\n  , mScriptBlacklistUse (true)\n  , mNewGame (false)\n  , mCfgMgr(configurationManager)\n{\n    SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, \"0\"); // We use only gamepads\n\n    Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE|SDL_INIT_GAMECONTROLLER|SDL_INIT_JOYSTICK|SDL_INIT_SENSOR;\n    if(SDL_WasInit(flags) == 0)\n    {\n        SDL_SetMainReady();\n        if(SDL_Init(flags) != 0)\n        {\n            throw std::runtime_error(\"Could not initialize SDL! \" + std::string(SDL_GetError()));\n        }\n    }\n}\n\nOMW::Engine::~Engine()\n{\n    /*\n        Start of tes3mp addition\n\n        Free up memory allocated by multiplayer's GUIController, but make sure\n        mwmp::Main has actually been initialized\n    */\n    if (mwmp::Main::isInitialized())\n        mwmp::Main::get().getGUIController()->cleanUp();\n    /*\n        End of tes3mp addition\n    */\n\n    mEnvironment.cleanup();\n\n    /*\n        Start of tes3mp addition\n\n        Free up memory allocated by multiplayer's Main class\n    */\n    mwmp::Main::destroy();\n    /*\n        End of tes3mp addition\n    */\n\n    delete mScriptContext;\n    mScriptContext = nullptr;\n\n    mWorkQueue = nullptr;\n\n    mViewer = nullptr;\n\n    mResourceSystem.reset();\n\n    delete mEncoder;\n    mEncoder = nullptr;\n\n    if (mWindow)\n    {\n        SDL_DestroyWindow(mWindow);\n        mWindow = nullptr;\n    }\n\n    SDL_Quit();\n\n    /*\n        Start of tes3mp addition\n\n        Free up memory allocated by multiplayer's logger\n    */\n    LOG_QUIT();\n    /*\n        End of tes3mp addition\n    */\n}\n\nvoid OMW::Engine::enableFSStrict(bool fsStrict)\n{\n    mFSStrict = fsStrict;\n}\n\n// Set data dir\n\nvoid OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs)\n{\n    mDataDirs = dataDirs;\n    mDataDirs.insert(mDataDirs.begin(), (mResDir / \"vfs\"));\n    mFileCollections = Files::Collections (mDataDirs, !mFSStrict);\n}\n\n// Add BSA archive\nvoid OMW::Engine::addArchive (const std::string& archive) {\n    mArchives.push_back(archive);\n}\n\n// Set resource dir\nvoid OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir)\n{\n    mResDir = parResDir;\n}\n\n// Set start cell name\nvoid OMW::Engine::setCell (const std::string& cellName)\n{\n    mCellName = cellName;\n}\n\nvoid OMW::Engine::addContentFile(const std::string& file)\n{\n    mContentFiles.push_back(file);\n}\n\nvoid OMW::Engine::addGroundcoverFile(const std::string& file)\n{\n    mGroundcoverFiles.emplace_back(file);\n}\n\nvoid OMW::Engine::setSkipMenu (bool skipMenu, bool newGame)\n{\n    mSkipMenu = skipMenu;\n    mNewGame = newGame;\n}\n\nstd::string OMW::Engine::loadSettings (Settings::Manager & settings)\n{\n    // Create the settings manager and load default settings file\n    const std::string localdefault = (mCfgMgr.getLocalPath() / \"defaults.bin\").string();\n    const std::string globaldefault = (mCfgMgr.getGlobalPath() / \"defaults.bin\").string();\n\n    // prefer local\n    if (boost::filesystem::exists(localdefault))\n        settings.loadDefault(localdefault);\n    else if (boost::filesystem::exists(globaldefault))\n        settings.loadDefault(globaldefault);\n    else\n        throw std::runtime_error (\"No default settings file found! Make sure the file \\\"defaults.bin\\\" was properly installed.\");\n\n    // load user settings if they exist\n    const std::string settingspath = (mCfgMgr.getUserConfigPath() / \"settings.cfg\").string();\n    if (boost::filesystem::exists(settingspath))\n        settings.loadUser(settingspath);\n\n    return settingspath;\n}\n\nvoid OMW::Engine::createWindow(Settings::Manager& settings)\n{\n    int screen = settings.getInt(\"screen\", \"Video\");\n    int width = settings.getInt(\"resolution x\", \"Video\");\n    int height = settings.getInt(\"resolution y\", \"Video\");\n    bool fullscreen = settings.getBool(\"fullscreen\", \"Video\");\n    bool windowBorder = settings.getBool(\"window border\", \"Video\");\n    bool vsync = settings.getBool(\"vsync\", \"Video\");\n    unsigned int antialiasing = std::max(0, settings.getInt(\"antialiasing\", \"Video\"));\n\n    int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(screen),\n        pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(screen);\n\n    if(fullscreen)\n    {\n        pos_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(screen);\n        pos_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(screen);\n    }\n\n    Uint32 flags = SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE;\n    if(fullscreen)\n        flags |= SDL_WINDOW_FULLSCREEN;\n\n    if (!windowBorder)\n        flags |= SDL_WINDOW_BORDERLESS;\n\n    SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,\n                settings.getBool(\"minimize on focus loss\", \"Video\") ? \"1\" : \"0\");\n\n    checkSDLError(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8));\n    checkSDLError(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8));\n    checkSDLError(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8));\n    checkSDLError(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0));\n    checkSDLError(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24));\n    if (Debug::shouldDebugOpenGL())\n        checkSDLError(SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG));\n\n    if (antialiasing > 0)\n    {\n        checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1));\n        checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));\n    }\n\n    osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> graphicsWindow;\n    while (!graphicsWindow || !graphicsWindow->valid())\n    {\n        while (!mWindow)\n        {\n            /*\n                Start of tes3mp change (major)\n\n                Rename the window into TES3MP\n            */\n            mWindow = SDL_CreateWindow(\"TES3MP\", pos_x, pos_y, width, height, flags);\n            /*\n                End of tes3mp change (major)\n            */\n            if (!mWindow)\n            {\n                // Try with a lower AA\n                if (antialiasing > 0)\n                {\n                    Log(Debug::Warning) << \"Warning: \" << antialiasing << \"x antialiasing not supported, trying \" << antialiasing/2;\n                    antialiasing /= 2;\n                    Settings::Manager::setInt(\"antialiasing\", \"Video\", antialiasing);\n                    checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));\n                    continue;\n                }\n                else\n                {\n                    std::stringstream error;\n                    error << \"Failed to create SDL window: \" << SDL_GetError();\n                    throw std::runtime_error(error.str());\n                }\n            }\n        }\n\n        setWindowIcon();\n\n        osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;\n        SDL_GetWindowPosition(mWindow, &traits->x, &traits->y);\n        SDL_GetWindowSize(mWindow, &traits->width, &traits->height);\n        traits->windowName = SDL_GetWindowTitle(mWindow);\n        traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);\n        traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);\n        traits->vsync = vsync;\n        traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);\n\n        graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits);\n        if (!graphicsWindow->valid()) throw std::runtime_error(\"Failed to create GraphicsContext\");\n\n        if (traits->samples < antialiasing)\n        {\n            Log(Debug::Warning) << \"Warning: Framebuffer MSAA level is only \" << traits->samples << \"x instead of \" << antialiasing << \"x. Trying \" << antialiasing / 2 << \"x instead.\";\n            graphicsWindow->closeImplementation();\n            SDL_DestroyWindow(mWindow);\n            mWindow = nullptr;\n            antialiasing /= 2;\n            Settings::Manager::setInt(\"antialiasing\", \"Video\", antialiasing);\n            checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));\n            continue;\n        }\n\n        if (traits->red < 8)\n            Log(Debug::Warning) << \"Warning: Framebuffer only has a \" << traits->red << \" bit red channel.\";\n        if (traits->green < 8)\n            Log(Debug::Warning) << \"Warning: Framebuffer only has a \" << traits->green << \" bit green channel.\";\n        if (traits->blue < 8)\n            Log(Debug::Warning) << \"Warning: Framebuffer only has a \" << traits->blue << \" bit blue channel.\";\n        if (traits->depth < 24)\n            Log(Debug::Warning) << \"Warning: Framebuffer only has \" << traits->depth << \" bits of depth precision.\";\n\n        traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel\n    }\n\n    osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();\n    camera->setGraphicsContext(graphicsWindow);\n    camera->setViewport(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height);\n\n    if (Debug::shouldDebugOpenGL())\n        mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation());\n\n    mViewer->realize();\n\n    mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height);\n}\n\nvoid OMW::Engine::setWindowIcon()\n{\n    boost::filesystem::ifstream windowIconStream;\n    /*\n        Start of tes3mp change (major)\n\n        Use TES3MP's logo for the window icon\n    */\n    std::string windowIcon = (mResDir / \"mygui\" / \"tes3mp_logo.png\").string();\n    /*\n        End of tes3mp change (major)\n    */\n    windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary);\n    if (windowIconStream.fail())\n        Log(Debug::Error) << \"Error: Failed to open \" << windowIcon;\n    osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n    if (!reader)\n    {\n        Log(Debug::Error) << \"Error: Failed to read window icon, no png readerwriter found\";\n        return;\n    }\n    osgDB::ReaderWriter::ReadResult result = reader->readImage(windowIconStream);\n    if (!result.success())\n        Log(Debug::Error) << \"Error: Failed to read \" << windowIcon << \": \" << result.message() << \" code \" << result.status();\n    else\n    {\n        osg::ref_ptr<osg::Image> image = result.getImage();\n        auto surface = SDLUtil::imageToSurface(image, true);\n        SDL_SetWindowIcon(mWindow, surface.get());\n    }\n}\n\nvoid OMW::Engine::prepareEngine (Settings::Manager & settings)\n{\n    mEnvironment.setStateManager (\n        new MWState::StateManager (mCfgMgr.getUserDataPath() / \"saves\", mContentFiles.at (0)));\n\n    createWindow(settings);\n\n    osg::ref_ptr<osg::Group> rootNode (new osg::Group);\n    mViewer->setSceneData(rootNode);\n\n    mVFS.reset(new VFS::Manager(mFSStrict));\n\n    VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);\n\n    mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));\n    mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(false); // keep to Off for now to allow better state sharing\n    mResourceSystem->getSceneManager()->setFilterSettings(\n        Settings::Manager::getString(\"texture mag filter\", \"General\"),\n        Settings::Manager::getString(\"texture min filter\", \"General\"),\n        Settings::Manager::getString(\"texture mipmap\", \"General\"),\n        Settings::Manager::getInt(\"anisotropy\", \"General\")\n    );\n\n    int numThreads = Settings::Manager::getInt(\"preload num threads\", \"Cells\");\n    if (numThreads <= 0)\n        throw std::runtime_error(\"Invalid setting: 'preload num threads' must be >0\");\n    mWorkQueue = new SceneUtil::WorkQueue(numThreads);\n\n    // Create input and UI first to set up a bootstrapping environment for\n    // showing a loading screen and keeping the window responsive while doing so\n\n    std::string keybinderUser = (mCfgMgr.getUserConfigPath() / \"input_v3.xml\").string();\n    bool keybinderUserExists = boost::filesystem::exists(keybinderUser);\n    if(!keybinderUserExists)\n    {\n        std::string input2 = (mCfgMgr.getUserConfigPath() / \"input_v2.xml\").string();\n        if(boost::filesystem::exists(input2)) {\n            boost::filesystem::copy_file(input2, keybinderUser);\n            keybinderUserExists = boost::filesystem::exists(keybinderUser);\n            Log(Debug::Info) << \"Loading keybindings file: \" << keybinderUser;\n        }\n    }\n    else\n        Log(Debug::Info) << \"Loading keybindings file: \" << keybinderUser;\n\n    const std::string userdefault = mCfgMgr.getUserConfigPath().string() + \"/gamecontrollerdb.txt\";\n    const std::string localdefault = mCfgMgr.getLocalPath().string() + \"/gamecontrollerdb.txt\";\n    const std::string globaldefault = mCfgMgr.getGlobalPath().string() + \"/gamecontrollerdb.txt\";\n\n    std::string userGameControllerdb;\n    if (boost::filesystem::exists(userdefault)){\n        userGameControllerdb = userdefault;\n    }\n    else\n        userGameControllerdb = \"\";\n\n    std::string gameControllerdb;\n    if (boost::filesystem::exists(localdefault))\n        gameControllerdb = localdefault;\n    else if (boost::filesystem::exists(globaldefault))\n        gameControllerdb = globaldefault;\n    else\n        gameControllerdb = \"\"; //if it doesn't exist, pass in an empty string\n\n    std::string myguiResources = (mResDir / \"mygui\").string();\n    osg::ref_ptr<osg::Group> guiRoot = new osg::Group;\n    guiRoot->setName(\"GUI Root\");\n    guiRoot->setNodeMask(MWRender::Mask_GUI);\n    rootNode->addChild(guiRoot);\n    MWGui::WindowManager* window = new MWGui::WindowManager(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),\n                mCfgMgr.getLogPath().string() + std::string(\"/\"), myguiResources,\n                mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts,\n                Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());\n    mEnvironment.setWindowManager (window);\n\n    MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);\n    mEnvironment.setInputManager (input);\n\n    // Create sound system\n    mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));\n\n    if (!mSkipMenu)\n    {\n        const std::string& logo = Fallback::Map::getString(\"Movies_Company_Logo\");\n        if (!logo.empty())\n            window->playVideo(logo, true);\n    }\n\n    // Create the world\n    mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),\n        mFileCollections, mContentFiles, mGroundcoverFiles, mEncoder, mActivationDistanceOverride, mCellName,\n        mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));\n    mEnvironment.getWorld()->setupPlayer();\n\n    window->setStore(mEnvironment.getWorld()->getStore());\n    window->initUI();\n\n    //Load translation data\n    mTranslationDataStorage.setEncoder(mEncoder);\n    for (size_t i = 0; i < mContentFiles.size(); i++)\n      mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);\n\n    Compiler::registerExtensions (mExtensions);\n\n    // Create script system\n    mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full);\n    mScriptContext->setExtensions (&mExtensions);\n\n    mEnvironment.setScriptManager (new MWScript::ScriptManager (mEnvironment.getWorld()->getStore(), *mScriptContext, mWarningsMode,\n        mScriptBlacklistUse ? mScriptBlacklist : std::vector<std::string>()));\n\n    // Create game mechanics system\n    MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager;\n    mEnvironment.setMechanicsManager (mechanics);\n\n    // Create dialog system\n    mEnvironment.setJournal (new MWDialogue::Journal);\n    mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mTranslationDataStorage));\n    mEnvironment.setResourceSystem(mResourceSystem.get());\n\n    // scripts\n    if (mCompileAll)\n    {\n        std::pair<int, int> result = mEnvironment.getScriptManager()->compileAll();\n        if (result.first)\n            Log(Debug::Info)\n                << \"compiled \" << result.second << \" of \" << result.first << \" scripts (\"\n                << 100*static_cast<double> (result.second)/result.first\n                << \"%)\";\n    }\n    if (mCompileAllDialogue)\n    {\n        std::pair<int, int> result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode);\n        if (result.first)\n            Log(Debug::Info)\n                << \"compiled \" << result.second << \" of \" << result.first << \" dialogue script/actor combinations a(\"\n                << 100*static_cast<double> (result.second)/result.first\n                << \"%)\";\n    }\n}\n\nclass WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation\n{\npublic:\n    WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat)\n        : mScreenshotPath(screenshotPath)\n        , mScreenshotFormat(screenshotFormat)\n    {\n    }\n\n    void operator()(const osg::Image& image, const unsigned int context_id) override\n    {\n        // Count screenshots.\n        int shotCount = 0;\n\n        // Find the first unused filename with a do-while\n        std::ostringstream stream;\n        do\n        {\n            // Reset the stream\n            stream.str(\"\");\n            stream.clear();\n\n            stream << mScreenshotPath << \"/screenshot\" << std::setw(3) << std::setfill('0') << shotCount++ << \".\" << mScreenshotFormat;\n\n        } while (boost::filesystem::exists(stream.str()));\n\n        boost::filesystem::ofstream outStream;\n        outStream.open(boost::filesystem::path(stream.str()), std::ios::binary);\n\n        osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat);\n        if (!readerwriter)\n        {\n            Log(Debug::Error) << \"Error: Can't write screenshot, no '\" << mScreenshotFormat << \"' readerwriter found\";\n            return;\n        }\n\n        osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream);\n        if (!result.success())\n        {\n            Log(Debug::Error) << \"Error: Can't write screenshot: \" << result.message() << \" code \" << result.status();\n        }\n    }\n\nprivate:\n    std::string mScreenshotPath;\n    std::string mScreenshotFormat;\n};\n\n// Initialise and enter main loop.\nvoid OMW::Engine::go()\n{\n    assert (!mContentFiles.empty());\n\n    /*\n        Start of tes3mp change (major)\n\n        Attempt multiplayer initialization and proceed no further if it fails\n    */\n    if (!mwmp::Main::init(mContentFiles, mFileCollections))\n        return;\n    /*\n        End of tes3mp change (major)\n    */\n\n    Log(Debug::Info) << \"OSG version: \" << osgGetVersion();\n    SDL_version sdlVersion;\n    SDL_GetVersion(&sdlVersion);\n    Log(Debug::Info) << \"SDL version: \" << (int)sdlVersion.major << \".\" << (int)sdlVersion.minor << \".\" << (int)sdlVersion.patch;\n\n    Misc::Rng::init(mRandomSeed);\n\n    // Load settings\n    Settings::Manager settings;\n    std::string settingspath;\n    settingspath = loadSettings (settings);\n\n    MWClass::registerClasses();\n\n    // Create encoder\n    mEncoder = new ToUTF8::Utf8Encoder(mEncoding);\n\n    // Setup viewer\n    mViewer = new osgViewer::Viewer;\n    mViewer->setReleaseContextAtEndOfFrameHint(false);\n\n#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)\n    // Do not try to outsmart the OS thread scheduler (see bug #4785).\n    mViewer->setUseConfigureAffinity(false);\n#endif\n\n    mScreenCaptureOperation = new WriteScreenshotToFileOperation(\n        mCfgMgr.getScreenshotPath().string(),\n        Settings::Manager::getString(\"screenshot format\", \"General\"));\n\n    mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);\n\n    mViewer->addEventHandler(mScreenCaptureHandler);\n\n    mEnvironment.setFrameRateLimit(Settings::Manager::getFloat(\"framerate limit\", \"Video\"));\n\n    prepareEngine (settings);\n\n    std::ofstream stats;\n    if (const auto path = std::getenv(\"OPENMW_OSG_STATS_FILE\"))\n    {\n        stats.open(path, std::ios_base::out);\n        if (stats.is_open())\n            Log(Debug::Info) << \"Stats will be written to: \" << path;\n        else\n            Log(Debug::Warning) << \"Failed to open file for stats: \" << path;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Handle post-initialization for multiplayer classes\n    */\n    mwmp::Main::postInit();\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp change (major)\n\n        Always skip the main menu in multiplayer\n    */\n    mSkipMenu = true;\n    /*\n        End of tes3mp change (major)\n    */\n\n    // Setup profiler\n    osg::ref_ptr<Resource::Profiler> statshandler = new Resource::Profiler(stats.is_open());\n\n    initStatsHandler(*statshandler);\n\n    mViewer->addEventHandler(statshandler);\n\n    osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler(stats.is_open());\n    mViewer->addEventHandler(resourceshandler);\n\n    if (stats.is_open())\n        Resource::CollectStatistics(mViewer);\n\n    // Start the game\n    if (!mSaveGameFile.empty())\n    {\n        mEnvironment.getStateManager()->loadGame(mSaveGameFile);\n    }\n    else if (!mSkipMenu)\n    {\n        // start in main menu\n        mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);\n        mEnvironment.getSoundManager()->playTitleMusic();\n        const std::string& logo = Fallback::Map::getString(\"Movies_Morrowind_Logo\");\n        if (!logo.empty())\n            mEnvironment.getWindowManager()->playVideo(logo, true);\n    }\n    else\n    {\n        mEnvironment.getStateManager()->newGame (!mNewGame);\n    }\n\n    if (!mStartupScript.empty() && mEnvironment.getStateManager()->getState() == MWState::StateManager::State_Running)\n    {\n        mEnvironment.getWindowManager()->executeInConsole(mStartupScript);\n    }\n\n    // Start the main rendering loop\n    double simulationTime = 0.0;\n    Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(mEnvironment.getFrameRateLimit());\n    const std::chrono::steady_clock::duration maxSimulationInterval(std::chrono::milliseconds(200));\n    while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest())\n    {\n        const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(std::min(\n            frameRateLimiter.getLastFrameDuration(),\n            maxSimulationInterval\n        )).count();\n\n        mViewer->advance(simulationTime);\n\n        if (!frame(dt))\n        {\n            std::this_thread::sleep_for(std::chrono::milliseconds(5));\n            continue;\n        }\n        else\n        {\n            mViewer->eventTraversal();\n            mViewer->updateTraversal();\n\n            mEnvironment.getWorld()->updateWindowManager();\n\n            mViewer->renderingTraversals();\n\n            bool guiActive = mEnvironment.getWindowManager()->isGuiMode();\n\n            /*\n                Start of tes3mp change (major)\n\n                Whether the GUI is active should have no relevance in multiplayer, so the guiActive\n                boolean is always set to false instead\n            */\n            guiActive = false;\n            /*\n                End of tes3mp change (major)\n            */\n\n            if (!guiActive)\n                simulationTime += dt;\n        }\n\n        if (stats)\n        {\n            const auto frameNumber = mViewer->getFrameStamp()->getFrameNumber();\n            if (frameNumber >= 2)\n            {\n                mViewer->getViewerStats()->report(stats, frameNumber - 2);\n                osgViewer::Viewer::Cameras cameras;\n                mViewer->getCameras(cameras);\n                for (auto camera : cameras)\n                    camera->getStats()->report(stats, frameNumber - 2);\n            }\n        }\n\n        frameRateLimiter.limit();\n    }\n\n    // Save user settings\n    settings.saveUser(settingspath);\n\n    mViewer->stopThreading();\n\n    Log(Debug::Info) << \"Quitting peacefully.\";\n}\n\nvoid OMW::Engine::setCompileAll (bool all)\n{\n    mCompileAll = all;\n}\n\nvoid OMW::Engine::setCompileAllDialogue (bool all)\n{\n    mCompileAllDialogue = all;\n}\n\nvoid OMW::Engine::setSoundUsage(bool soundUsage)\n{\n    mUseSound = soundUsage;\n}\n\nvoid OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)\n{\n    mEncoding = encoding;\n}\n\nvoid OMW::Engine::setScriptConsoleMode (bool enabled)\n{\n    mScriptConsoleMode = enabled;\n}\n\nvoid OMW::Engine::setStartupScript (const std::string& path)\n{\n    mStartupScript = path;\n}\n\nvoid OMW::Engine::setActivationDistanceOverride (int distance)\n{\n    mActivationDistanceOverride = distance;\n}\n\nvoid OMW::Engine::setWarningsMode (int mode)\n{\n    mWarningsMode = mode;\n}\n\nvoid OMW::Engine::setScriptBlacklist (const std::vector<std::string>& list)\n{\n    mScriptBlacklist = list;\n}\n\nvoid OMW::Engine::setScriptBlacklistUse (bool use)\n{\n    mScriptBlacklistUse = use;\n}\n\nvoid OMW::Engine::enableFontExport(bool exportFonts)\n{\n    mExportFonts = exportFonts;\n}\n\nvoid OMW::Engine::setSaveGameFile(const std::string &savegame)\n{\n    mSaveGameFile = savegame;\n}\n\nvoid OMW::Engine::setRandomSeed(unsigned int seed)\n{\n    mRandomSeed = seed;\n}\n"
  },
  {
    "path": "apps/openmw/engine.hpp",
    "content": "#ifndef ENGINE_H\n#define ENGINE_H\n\n#include <components/compiler/extensions.hpp>\n#include <components/files/collections.hpp>\n#include <components/translation/translation.hpp>\n#include <components/settings/settings.hpp>\n\n#include <osgViewer/Viewer>\n#include <osgViewer/ViewerEventHandlers>\n\n#include \"mwbase/environment.hpp\"\n\n#include \"mwworld/ptr.hpp\"\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace SceneUtil\n{\n    class WorkQueue;\n}\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace Compiler\n{\n    class Context;\n}\n\nnamespace Files\n{\n    struct ConfigurationManager;\n}\n\nnamespace osgViewer\n{\n    class ScreenCaptureHandler;\n}\n\nstruct SDL_Window;\n\nnamespace OMW\n{\n    /// \\brief Main engine class, that brings together all the components of OpenMW\n    class Engine\n    {\n            SDL_Window* mWindow;\n            std::unique_ptr<VFS::Manager> mVFS;\n            std::unique_ptr<Resource::ResourceSystem> mResourceSystem;\n            osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;\n            MWBase::Environment mEnvironment;\n            ToUTF8::FromType mEncoding;\n            ToUTF8::Utf8Encoder* mEncoder;\n            Files::PathContainer mDataDirs;\n            std::vector<std::string> mArchives;\n            boost::filesystem::path mResDir;\n            osg::ref_ptr<osgViewer::Viewer> mViewer;\n            osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;\n            osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;\n            std::string mCellName;\n            std::vector<std::string> mContentFiles;\n            std::vector<std::string> mGroundcoverFiles;\n            bool mSkipMenu;\n            bool mUseSound;\n            bool mCompileAll;\n            bool mCompileAllDialogue;\n            int mWarningsMode;\n            std::string mFocusName;\n            bool mScriptConsoleMode;\n            std::string mStartupScript;\n            int mActivationDistanceOverride;\n            std::string mSaveGameFile;\n            // Grab mouse?\n            bool mGrab;\n\n            bool mExportFonts;\n            unsigned int mRandomSeed;\n\n            Compiler::Extensions mExtensions;\n            Compiler::Context *mScriptContext;\n\n            Files::Collections mFileCollections;\n            bool mFSStrict;\n            Translation::Storage mTranslationDataStorage;\n            std::vector<std::string> mScriptBlacklist;\n            bool mScriptBlacklistUse;\n            bool mNewGame;\n\n            // not implemented\n            Engine (const Engine&);\n            Engine& operator= (const Engine&);\n\n            void executeLocalScripts();\n\n            bool frame (float dt);\n\n            /// Load settings from various files, returns the path to the user settings file\n            std::string loadSettings (Settings::Manager & settings);\n\n            /// Prepare engine for game play\n            void prepareEngine (Settings::Manager & settings);\n\n            void createWindow(Settings::Manager& settings);\n            void setWindowIcon();\n\n        public:\n            Engine(Files::ConfigurationManager& configurationManager);\n            virtual ~Engine();\n\n            /// Enable strict filesystem mode (do not fold case)\n            ///\n            /// \\attention The strict mode must be specified before any path-related settings\n            /// are given to the engine.\n            void enableFSStrict(bool fsStrict);\n\n            /// Set data dirs\n            void setDataDirs(const Files::PathContainer& dataDirs);\n\n            /// Add BSA archive\n            void addArchive(const std::string& archive);\n\n            /// Set resource dir\n            void setResourceDir(const boost::filesystem::path& parResDir);\n\n            /// Set start cell name\n            void setCell(const std::string& cellName);\n\n            /**\n             * @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container.\n             * @param file - filename (extension is required)\n             */\n            void addContentFile(const std::string& file);\n            void addGroundcoverFile(const std::string& file);\n\n            /// Disable or enable all sounds\n            void setSoundUsage(bool soundUsage);\n\n            /// Skip main menu and go directly into the game\n            ///\n            /// \\param newGame Start a new game instead off dumping the player into the game\n            /// (ignored if !skipMenu).\n            void setSkipMenu (bool skipMenu, bool newGame);\n\n            void setGrabMouse(bool grab) { mGrab = grab; }\n\n            /// Initialise and enter main loop.\n            void go();\n\n            /// Compile all scripts (excludign dialogue scripts) at startup?\n            void setCompileAll (bool all);\n\n            /// Compile all dialogue scripts at startup?\n            void setCompileAllDialogue (bool all);\n\n            /// Font encoding\n            void setEncoding(const ToUTF8::FromType& encoding);\n\n            /// Enable console-only script functionality\n            void setScriptConsoleMode (bool enabled);\n\n            /// Set path for a script that is run on startup in the console.\n            void setStartupScript (const std::string& path);\n\n            /// Override the game setting specified activation distance.\n            void setActivationDistanceOverride (int distance);\n\n            void setWarningsMode (int mode);\n\n            void setScriptBlacklist (const std::vector<std::string>& list);\n\n            void setScriptBlacklistUse (bool use);\n\n            void enableFontExport(bool exportFonts);\n\n            /// Set the save game file to load after initialising the engine.\n            void setSaveGameFile(const std::string& savegame);\n\n            void setRandomSeed(unsigned int seed);\n\n        private:\n            Files::ConfigurationManager& mCfgMgr;\n    };\n}\n\n#endif /* ENGINE_H */\n"
  },
  {
    "path": "apps/openmw/main.cpp",
    "content": "#include <components/version/version.hpp>\n#include <components/files/configurationmanager.hpp>\n#include <components/files/escape.hpp>\n#include <components/fallback/fallback.hpp>\n#include <components/fallback/validate.hpp>\n#include <components/debug/debugging.hpp>\n#include <components/misc/rng.hpp>\n\n#include \"engine.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include the header of the multiplayer's Main class\n*/\n#include \"mwmp/Main.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#if defined(_WIN32)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n// makes __argc and __argv available on windows\n#include <cstdlib>\n#endif\n\n#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))\n#include <unistd.h>\n#endif\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/ErrorMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Utils.hpp>\n#include <components/openmw-mp/Version.hpp>\n/*\n    End of tes3mp addition\n*/\n\n\nusing namespace Fallback;\n\n/**\n * \\brief Parses application command line and calls \\ref Cfg::ConfigurationManager\n * to parse configuration files.\n *\n * Results are directly written to \\ref Engine class.\n *\n * \\retval true - Everything goes OK\n * \\retval false - Error\n */\nbool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::ConfigurationManager& cfgMgr)\n{\n    // Create a local alias for brevity\n    namespace bpo = boost::program_options;\n    typedef std::vector<std::string> StringsVector;\n\n    bpo::options_description desc(\"Syntax: openmw <options>\\nAllowed options\");\n\n    desc.add_options()\n        (\"help\", \"print help message\")\n        (\"version\", \"print version information and quit\")\n\n        (\"replace\", bpo::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), \"\")\n            ->multitoken()->composing(), \"settings where the values from the current source should replace those from lower-priority sources instead of being appended\")\n\n        (\"data\", bpo::value<Files::EscapePathContainer>()->default_value(Files::EscapePathContainer(), \"data\")\n            ->multitoken()->composing(), \"set data directories (later directories have higher priority)\")\n\n        (\"data-local\", bpo::value<Files::EscapePath>()->default_value(Files::EscapePath(), \"\"),\n            \"set local data directory (highest priority)\")\n\n        (\"fallback-archive\", bpo::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), \"fallback-archive\")\n            ->multitoken()->composing(), \"set fallback BSA archives (later archives have higher priority)\")\n\n        (\"resources\", bpo::value<Files::EscapePath>()->default_value(Files::EscapePath(), \"resources\"),\n            \"set resources directory\")\n\n        (\"start\", bpo::value<Files::EscapeHashString>()->default_value(\"\"),\n            \"set initial cell\")\n\n        (\"content\", bpo::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), \"\")\n            ->multitoken()->composing(), \"content file(s): esm/esp, or omwgame/omwaddon\")\n\n        (\"groundcover\", bpo::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), \"\")\n            ->multitoken()->composing(), \"groundcover content file(s): esm/esp, or omwgame/omwaddon\")\n\n        (\"no-sound\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(false), \"disable all sounds\")\n\n        (\"script-all\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(false), \"compile all scripts (excluding dialogue scripts) at startup\")\n\n        (\"script-all-dialogue\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(false), \"compile all dialogue scripts at startup\")\n\n        (\"script-console\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(false), \"enable console-only script functionality\")\n\n        (\"script-run\", bpo::value<Files::EscapeHashString>()->default_value(\"\"),\n            \"select a file containing a list of console commands that is executed on startup\")\n\n        (\"script-warn\", bpo::value<int>()->implicit_value (1)\n            ->default_value (1),\n            \"handling of warnings when compiling scripts\\n\"\n            \"\\t0 - ignore warning\\n\"\n            \"\\t1 - show warning but consider script as correctly compiled anyway\\n\"\n            \"\\t2 - treat warnings as errors\")\n\n        (\"script-blacklist\", bpo::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), \"\")\n            ->multitoken()->composing(), \"ignore the specified script (if the use of the blacklist is enabled)\")\n\n        (\"script-blacklist-use\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(true), \"enable script blacklisting\")\n\n        (\"load-savegame\", bpo::value<Files::EscapePath>()->default_value(Files::EscapePath(), \"\"),\n            \"load a save game file on game startup (specify an absolute filename or a filename relative to the current working directory)\")\n\n        (\"skip-menu\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(false), \"skip main menu on game startup\")\n\n        (\"new-game\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(false), \"run new game sequence (ignored if skip-menu=0)\")\n\n        (\"fs-strict\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(false), \"strict file system handling (no case folding)\")\n\n        (\"encoding\", bpo::value<Files::EscapeHashString>()->\n            default_value(\"win1252\"),\n            \"Character encoding used in OpenMW game messages:\\n\"\n            \"\\n\\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\\n\"\n            \"\\n\\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\\n\"\n            \"\\n\\twin1252 - Western European (Latin) alphabet, used by default\")\n\n        (\"fallback\", bpo::value<FallbackMap>()->default_value(FallbackMap(), \"\")\n            ->multitoken()->composing(), \"fallback values\")\n\n        (\"no-grab\", bpo::value<bool>()->implicit_value(true)->default_value(false), \"Don't grab mouse cursor\")\n\n        (\"export-fonts\", bpo::value<bool>()->implicit_value(true)\n            ->default_value(false), \"Export Morrowind .fnt fonts to PNG image and XML file in current directory\")\n\n        (\"activate-dist\", bpo::value <int> ()->default_value (-1), \"activation distance override\")\n\n        (\"random-seed\", bpo::value <unsigned int> ()\n            ->default_value(Misc::Rng::generateDefaultSeed()),\n            \"seed value for random number generator\")\n    ;\n\n    /*\n        Start of tes3mp addition\n\n        Parse options added by multiplayer\n    */\n    mwmp::Main::optionsDesc(&desc);\n    /*\n        End of tes3mp addition\n    */\n\n    bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)\n        .options(desc).allow_unregistered().run();\n\n    bpo::variables_map variables;\n\n    // Runtime options override settings from all configs\n    bpo::store(valid_opts, variables);\n    bpo::notify(variables);\n\n    if (variables.count (\"help\"))\n    {\n        getRawStdout() << desc << std::endl;\n        return false;\n    }\n\n    if (variables.count (\"version\"))\n    {\n        cfgMgr.readConfiguration(variables, desc, true);\n\n        Version::Version v = Version::getOpenmwVersion(variables[\"resources\"].as<Files::EscapePath>().mPath.string());\n        getRawStdout() << v.describe() << std::endl;\n        return false;\n    }\n\n    bpo::variables_map composingVariables = cfgMgr.separateComposingVariables(variables, desc);\n    cfgMgr.readConfiguration(variables, desc);\n    cfgMgr.mergeComposingVariables(variables, composingVariables, desc);\n\n    Version::Version v = Version::getOpenmwVersion(variables[\"resources\"].as<Files::EscapePath>().mPath.string());\n\n    /*\n        Start of tes3mp addition\n\n        Print the multiplayer version first\n    */\n    Log(Debug::Info) << Utils::getVersionInfo(\"TES3MP client\", TES3MP_VERSION, v.mCommitHash, TES3MP_PROTO_VERSION);\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp change (minor)\n\n        Because there is no need to print the commit hash again, only print OpenMW's version\n    */\n    Log(Debug::Info) << \"OpenMW version \" << v.mVersion;\n    /*\n        End of tes3mp change (minor)\n    */\n\n    engine.setGrabMouse(!variables[\"no-grab\"].as<bool>());\n\n    // Font encoding settings\n    std::string encoding(variables[\"encoding\"].as<Files::EscapeHashString>().toStdString());\n    Log(Debug::Info) << ToUTF8::encodingUsingMessage(encoding);\n    engine.setEncoding(ToUTF8::calculateEncoding(encoding));\n\n    // directory settings\n    engine.enableFSStrict(variables[\"fs-strict\"].as<bool>());\n\n    Files::PathContainer dataDirs(Files::EscapePath::toPathContainer(variables[\"data\"].as<Files::EscapePathContainer>()));\n\n    Files::PathContainer::value_type local(variables[\"data-local\"].as<Files::EscapePath>().mPath);\n    if (!local.empty())\n        dataDirs.push_back(local);\n\n    cfgMgr.processPaths(dataDirs);\n\n    engine.setResourceDir(variables[\"resources\"].as<Files::EscapePath>().mPath);\n    engine.setDataDirs(dataDirs);\n\n    // fallback archives\n    StringsVector archives = variables[\"fallback-archive\"].as<Files::EscapeStringVector>().toStdStringVector();\n    for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); ++it)\n    {\n        engine.addArchive(*it);\n    }\n\n    StringsVector content = variables[\"content\"].as<Files::EscapeStringVector>().toStdStringVector();\n    if (content.empty())\n    {\n        Log(Debug::Error) << \"No content file given (esm/esp, nor omwgame/omwaddon). Aborting...\";\n        return false;\n    }\n    std::set<std::string> contentDedupe;\n    for (const auto& contentFile : content)\n    {\n        if (!contentDedupe.insert(contentFile).second)\n        {\n            Log(Debug::Error) << \"Content file specified more than once: \" << contentFile << \". Aborting...\";\n            return false;\n        }\n    }\n\n    for (auto& file : content)\n    {\n        engine.addContentFile(file);\n    }\n\n    StringsVector groundcover = variables[\"groundcover\"].as<Files::EscapeStringVector>().toStdStringVector();\n    for (auto& file : groundcover)\n    {\n        engine.addGroundcoverFile(file);\n    }\n\n    // startup-settings\n    engine.setCell(variables[\"start\"].as<Files::EscapeHashString>().toStdString());\n    engine.setSkipMenu (variables[\"skip-menu\"].as<bool>(), variables[\"new-game\"].as<bool>());\n    if (!variables[\"skip-menu\"].as<bool>() && variables[\"new-game\"].as<bool>())\n        Log(Debug::Warning) << \"Warning: new-game used without skip-menu -> ignoring it\";\n\n    // scripts\n    engine.setCompileAll(variables[\"script-all\"].as<bool>());\n    engine.setCompileAllDialogue(variables[\"script-all-dialogue\"].as<bool>());\n    engine.setScriptConsoleMode (variables[\"script-console\"].as<bool>());\n    \n    /*\n        Start of tes3mp change (major)\n\n        Clients should not be allowed to set any of these unilaterally in multiplayer, so\n        disable them\n    */\n    /*\n    engine.setStartupScript (variables[\"script-run\"].as<Files::EscapeHashString>().toStdString());\n    engine.setWarningsMode (variables[\"script-warn\"].as<int>());\n    engine.setScriptBlacklist (variables[\"script-blacklist\"].as<Files::EscapeStringVector>().toStdStringVector());\n    engine.setScriptBlacklistUse (variables[\"script-blacklist-use\"].as<bool>());\n    engine.setSaveGameFile (variables[\"load-savegame\"].as<Files::EscapePath>().mPath.string());\n    */\n    /*\n        End of tes3mp change (major)\n    */\n\n    // other settings\n    Fallback::Map::init(variables[\"fallback\"].as<FallbackMap>().mMap);\n    engine.setSoundUsage(!variables[\"no-sound\"].as<bool>());\n    engine.setActivationDistanceOverride (variables[\"activate-dist\"].as<int>());\n    engine.enableFontExport(variables[\"export-fonts\"].as<bool>());\n    engine.setRandomSeed(variables[\"random-seed\"].as<unsigned int>());\n\n    /*\n        Start of tes3mp addition\n\n        Configure multiplayer using parsed variables\n    */\n    mwmp::Main::configure(&variables);\n    /*\n        End of tes3mp addition\n    */\n\n    return true;\n}\n\nnamespace\n{\n    class OSGLogHandler : public osg::NotifyHandler\n    {\n        void notify(osg::NotifySeverity severity, const char* msg) override\n        {\n            // Copy, because osg logging is not thread safe.\n            std::string msgCopy(msg);\n            if (msgCopy.empty())\n                return;\n\n            Debug::Level level;\n            switch (severity)\n            {\n            case osg::ALWAYS:\n            case osg::FATAL:\n                level = Debug::Error;\n                break;\n            case osg::WARN:\n            case osg::NOTICE:\n                level = Debug::Warning;\n                break;\n            case osg::INFO:\n                level = Debug::Info;\n                break;\n            case osg::DEBUG_INFO:\n            case osg::DEBUG_FP:\n            default:\n                level = Debug::Debug;\n            }\n            std::string_view s(msgCopy);\n            if (s.size() < 1024)\n                Log(level) << (s.back() == '\\n' ? s.substr(0, s.size() - 1) : s);\n            else\n            {\n                while (!s.empty())\n                {\n                    size_t lineSize = 1;\n                    while (lineSize < s.size() && s[lineSize - 1] != '\\n')\n                        lineSize++;\n                    Log(level) << s.substr(0, s[lineSize - 1] == '\\n' ? lineSize - 1 : lineSize);\n                    s = s.substr(lineSize);\n                }\n            }\n        }\n    };\n}\n\nint runApplication(int argc, char *argv[])\n{\n#ifdef __APPLE__\n    boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));\n    boost::filesystem::current_path(binary_path.parent_path());\n    setenv(\"OSG_GL_TEXTURE_STORAGE\", \"OFF\", 0);\n#endif\n\n    osg::setNotifyHandler(new OSGLogHandler());\n    Files::ConfigurationManager cfgMgr;\n    std::unique_ptr<OMW::Engine> engine;\n    engine.reset(new OMW::Engine(cfgMgr));\n\n    if (parseOptions(argc, argv, *engine, cfgMgr))\n    {\n        engine->go();\n    }\n\n    return 0;\n}\n\n#ifdef ANDROID\nextern \"C\" int SDL_main(int argc, char**argv)\n#else\nint main(int argc, char**argv)\n#endif\n{\n    /*\n        Start of tes3mp addition\n\n        Initialize the logger added for multiplayer\n    */\n    LOG_INIT(TimedLog::LOG_INFO);\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp change (major)\n\n        Instead of logging information in openmw.log, use a more descriptive filename\n        that includes a timestamp\n    */\n    return wrapApplication(&runApplication, argc, argv, \"/tes3mp-client-\" + TimedLog::getFilenameTimestamp());\n    /*\n        End of tes3mp change (major)\n    */\n}\n\n// Platform specific for Windows when there is no console built into the executable.\n// Windows will call the WinMain function instead of main in this case, the normal\n// main function is then called with the __argc and __argv parameters.\n#if defined(_WIN32) && !defined(_CONSOLE)\nint WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)\n{\n    return main(__argc, __argv);\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/dialoguemanager.hpp",
    "content": "#ifndef GAME_MWBASE_DIALOGUEMANAGER_H\n#define GAME_MWBASE_DIALOGUEMANAGER_H\n\n#include <string>\n#include <vector>\n#include <list>\n\n#include <stdint.h>\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWBase\n{\n    /// \\brief Interface for dialogue manager (implemented in MWDialogue)\n    class DialogueManager\n    {\n            DialogueManager (const DialogueManager&);\n            ///< not implemented\n\n            DialogueManager& operator= (const DialogueManager&);\n            ///< not implemented\n\n        public:\n\n            class ResponseCallback\n            {\n            public:\n                virtual ~ResponseCallback() = default;\n                virtual void addResponse(const std::string& title, const std::string& text) = 0;\n            };\n\n            DialogueManager() {}\n\n            virtual void clear() = 0;\n\n            virtual ~DialogueManager() {}\n\n            virtual bool isInChoice() const = 0;\n\n            virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) = 0;\n\n            virtual bool inJournal (const std::string& topicId, const std::string& infoId) = 0;\n\n            virtual void addTopic (const std::string& topic) = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a topic is known by the player from elsewhere\n                in the code\n            */\n            virtual bool isNewTopic(const std::string& topic) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual void addChoice (const std::string& text,int choice) = 0;\n            virtual const std::vector<std::pair<std::string, int> >& getChoices() = 0;\n\n            virtual bool isGoodbye() = 0;\n\n            virtual void goodbye() = 0;\n\n            virtual void say(const MWWorld::Ptr &actor, const std::string &topic) = 0;\n\n            virtual void keywordSelected (const std::string& keyword, ResponseCallback* callback) = 0;\n            virtual void goodbyeSelected() = 0;\n            virtual void questionAnswered (int answer, ResponseCallback* callback) = 0;\n\n            enum TopicType\n            {\n                Specific = 1,\n                Exhausted = 2\n            };\n\n            enum ServiceType\n            {\n                Any = -1,\n                Barter = 1,\n                Repair = 2,\n                Spells = 3,\n                Training = 4,\n                Travel = 5,\n                Spellmaking = 6,\n                Enchanting = 7\n            };\n\n            virtual std::list<std::string> getAvailableTopics() = 0;\n            virtual int getTopicFlag(const std::string&) = 0;\n\n            virtual bool checkServiceRefused (ResponseCallback* callback, ServiceType service = ServiceType::Any) = 0;\n\n            virtual void persuade (int type, ResponseCallback* callback) = 0;\n            virtual int getTemporaryDispositionChange () const = 0;\n\n            /// @note Controlled by an option, gets discarded when dialogue ends by default\n            virtual void applyBarterDispositionChange (int delta) = 0;\n\n            virtual int countSavedGameRecords() const = 0;\n\n            virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0;\n\n            virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0;\n\n            /// Changes faction1's opinion of faction2 by \\a diff.\n            virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff) = 0;\n\n            virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute) = 0;\n\n            /// @return faction1's opinion of faction2\n            virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0;\n\n            /// Removes the last added topic response for the given actor from the journal\n            virtual void clearInfoActor (const MWWorld::Ptr& actor) const = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Declare this method here so it can be used from outside of MWDialogue::DialogueManager\n            */\n            virtual void updateActorKnownTopics() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the caption of a voice dialogue\n            */\n            virtual std::string getVoiceCaption(const std::string& sound) const = 0;\n            /*\n                End of tes3mp addition\n            */\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/environment.cpp",
    "content": "#include \"environment.hpp\"\n\n#include <cassert>\n\n#include <components/resource/resourcesystem.hpp>\n\n#include \"world.hpp\"\n#include \"scriptmanager.hpp\"\n#include \"dialoguemanager.hpp\"\n#include \"journal.hpp\"\n#include \"soundmanager.hpp\"\n#include \"mechanicsmanager.hpp\"\n#include \"inputmanager.hpp\"\n#include \"windowmanager.hpp\"\n#include \"statemanager.hpp\"\n\nMWBase::Environment *MWBase::Environment::sThis = nullptr;\n\nMWBase::Environment::Environment()\n: mWorld (nullptr), mSoundManager (nullptr), mScriptManager (nullptr), mWindowManager (nullptr),\n  mMechanicsManager (nullptr),  mDialogueManager (nullptr), mJournal (nullptr), mInputManager (nullptr),\n    mStateManager (nullptr), mResourceSystem (nullptr),  mFrameDuration (0), mFrameRateLimit(0.f)\n{\n    assert (!sThis);\n    sThis = this;\n}\n\nMWBase::Environment::~Environment()\n{\n    cleanup();\n    sThis = nullptr;\n}\n\nvoid MWBase::Environment::setWorld (World *world)\n{\n    mWorld = world;\n}\n\nvoid MWBase::Environment::setSoundManager (SoundManager *soundManager)\n{\n    mSoundManager = soundManager;\n}\n\nvoid MWBase::Environment::setScriptManager (ScriptManager *scriptManager)\n{\n    mScriptManager = scriptManager;\n}\n\nvoid MWBase::Environment::setWindowManager (WindowManager *windowManager)\n{\n    mWindowManager = windowManager;\n}\n\nvoid MWBase::Environment::setMechanicsManager (MechanicsManager *mechanicsManager)\n{\n    mMechanicsManager = mechanicsManager;\n}\n\nvoid MWBase::Environment::setDialogueManager (DialogueManager *dialogueManager)\n{\n    mDialogueManager = dialogueManager;\n}\n\nvoid MWBase::Environment::setJournal (Journal *journal)\n{\n    mJournal = journal;\n}\n\nvoid MWBase::Environment::setInputManager (InputManager *inputManager)\n{\n    mInputManager = inputManager;\n}\n\nvoid MWBase::Environment::setStateManager (StateManager *stateManager)\n{\n    mStateManager = stateManager;\n}\n\nvoid MWBase::Environment::setResourceSystem (Resource::ResourceSystem *resourceSystem)\n{\n    mResourceSystem = resourceSystem;\n}\n\nvoid MWBase::Environment::setFrameDuration (float duration)\n{\n    mFrameDuration = duration;\n}\n\nvoid MWBase::Environment::setFrameRateLimit(float limit)\n{\n    mFrameRateLimit = limit;\n}\n\nfloat MWBase::Environment::getFrameRateLimit() const\n{\n    return mFrameRateLimit;\n}\n\nMWBase::World *MWBase::Environment::getWorld() const\n{\n    assert (mWorld);\n    return mWorld;\n}\n\nMWBase::SoundManager *MWBase::Environment::getSoundManager() const\n{\n    assert (mSoundManager);\n    return mSoundManager;\n}\n\nMWBase::ScriptManager *MWBase::Environment::getScriptManager() const\n{\n    assert (mScriptManager);\n    return mScriptManager;\n}\n\nMWBase::WindowManager *MWBase::Environment::getWindowManager() const\n{\n    assert (mWindowManager);\n    return mWindowManager;\n}\n\nMWBase::MechanicsManager *MWBase::Environment::getMechanicsManager() const\n{\n    assert (mMechanicsManager);\n    return mMechanicsManager;\n}\n\nMWBase::DialogueManager *MWBase::Environment::getDialogueManager() const\n{\n    assert (mDialogueManager);\n    return mDialogueManager;\n}\n\nMWBase::Journal *MWBase::Environment::getJournal() const\n{\n    assert (mJournal);\n    return mJournal;\n}\n\nMWBase::InputManager *MWBase::Environment::getInputManager() const\n{\n    assert (mInputManager);\n    return mInputManager;\n}\n\nMWBase::StateManager *MWBase::Environment::getStateManager() const\n{\n    assert (mStateManager);\n    return mStateManager;\n}\n\nResource::ResourceSystem *MWBase::Environment::getResourceSystem() const\n{\n    return mResourceSystem;\n}\n\nfloat MWBase::Environment::getFrameDuration() const\n{\n    return mFrameDuration;\n}\n\nvoid MWBase::Environment::cleanup()\n{\n    delete mMechanicsManager;\n    mMechanicsManager = nullptr;\n\n    delete mDialogueManager;\n    mDialogueManager = nullptr;\n\n    delete mJournal;\n    mJournal = nullptr;\n\n    delete mScriptManager;\n    mScriptManager = nullptr;\n\n    delete mWindowManager;\n    mWindowManager = nullptr;\n\n    delete mWorld;\n    mWorld = nullptr;\n\n    delete mSoundManager;\n    mSoundManager = nullptr;\n\n    delete mInputManager;\n    mInputManager = nullptr;\n\n    delete mStateManager;\n    mStateManager = nullptr;\n}\n\nconst MWBase::Environment& MWBase::Environment::get()\n{\n    assert (sThis);\n    return *sThis;\n}\n\nvoid MWBase::Environment::reportStats(unsigned int frameNumber, osg::Stats& stats) const\n{\n    mMechanicsManager->reportStats(frameNumber, stats);\n    mWorld->reportStats(frameNumber, stats);\n}\n"
  },
  {
    "path": "apps/openmw/mwbase/environment.hpp",
    "content": "#ifndef GAME_BASE_ENVIRONMENT_H\n#define GAME_BASE_ENVIRONMENT_H\n\nnamespace osg\n{\n    class Stats;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWBase\n{\n    class World;\n    class ScriptManager;\n    class DialogueManager;\n    class Journal;\n    class SoundManager;\n    class MechanicsManager;\n    class InputManager;\n    class WindowManager;\n    class StateManager;\n\n    /// \\brief Central hub for mw-subsystems\n    ///\n    /// This class allows each mw-subsystem to access any others subsystem's top-level manager class.\n    ///\n    /// \\attention Environment takes ownership of the manager class instances it is handed over in\n    /// the set* functions.\n    class Environment\n    {\n            static Environment *sThis;\n\n            World *mWorld;\n            SoundManager *mSoundManager;\n            ScriptManager *mScriptManager;\n            WindowManager *mWindowManager;\n            MechanicsManager *mMechanicsManager;\n            DialogueManager *mDialogueManager;\n            Journal *mJournal;\n            InputManager *mInputManager;\n            StateManager *mStateManager;\n            Resource::ResourceSystem *mResourceSystem;\n            float mFrameDuration;\n            float mFrameRateLimit;\n\n            Environment (const Environment&);\n            ///< not implemented\n\n            Environment& operator= (const Environment&);\n            ///< not implemented\n\n        public:\n\n            Environment();\n\n            ~Environment();\n\n            void setWorld (World *world);\n\n            void setSoundManager (SoundManager *soundManager);\n\n            void setScriptManager (MWBase::ScriptManager *scriptManager);\n\n            void setWindowManager (WindowManager *windowManager);\n\n            void setMechanicsManager (MechanicsManager *mechanicsManager);\n\n            void setDialogueManager (DialogueManager *dialogueManager);\n\n            void setJournal (Journal *journal);\n\n            void setInputManager (InputManager *inputManager);\n\n            void setStateManager (StateManager *stateManager);\n\n            void setResourceSystem (Resource::ResourceSystem *resourceSystem);\n\n            void setFrameDuration (float duration);\n            ///< Set length of current frame in seconds.\n\n            void setFrameRateLimit(float frameRateLimit);\n            float getFrameRateLimit() const;\n\n            World *getWorld() const;\n\n            SoundManager *getSoundManager() const;\n\n            ScriptManager *getScriptManager() const;\n\n            WindowManager *getWindowManager() const;\n\n            MechanicsManager *getMechanicsManager() const;\n\n            DialogueManager *getDialogueManager() const;\n\n            Journal *getJournal() const;\n\n            InputManager *getInputManager() const;\n\n            StateManager *getStateManager() const;\n\n            Resource::ResourceSystem *getResourceSystem() const;\n\n            float getFrameDuration() const;\n\n            void cleanup();\n            ///< Delete all mw*-subsystems.\n\n            static const Environment& get();\n            ///< Return instance of this class.\n\n            void reportStats(unsigned int frameNumber, osg::Stats& stats) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/inputmanager.hpp",
    "content": "#ifndef GAME_MWBASE_INPUTMANAGER_H\n#define GAME_MWBASE_INPUTMANAGER_H\n\n#include <string>\n#include <set>\n#include <vector>\n\n#include <stdint.h>\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n}\n\nnamespace MWBase\n{\n    /// \\brief Interface for input manager (implemented in MWInput)\n    class InputManager\n    {\n            InputManager (const InputManager&);\n            ///< not implemented\n\n            InputManager& operator= (const InputManager&);\n            ///< not implemented\n\n        public:\n\n            InputManager() {}\n\n            /// Clear all savegame-specific data\n            virtual void clear() = 0;\n\n            virtual ~InputManager() {}\n\n            virtual void update(float dt, bool disableControls, bool disableEvents=false) = 0;\n\n            virtual void changeInputMode(bool guiMode) = 0;\n\n            virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) = 0;\n\n            virtual void setDragDrop(bool dragDrop) = 0;\n            virtual void setGamepadGuiCursorEnabled(bool enabled) = 0;\n            virtual void setAttemptJump(bool jumping) = 0;\n\n            virtual void toggleControlSwitch (const std::string& sw, bool value) = 0;\n            virtual bool getControlSwitch (const std::string& sw) = 0;\n\n            virtual std::string getActionDescription (int action) = 0;\n            virtual std::string getActionKeyBindingName (int action) = 0;\n            virtual std::string getActionControllerBindingName (int action) = 0;\n            ///Actions available for binding to keyboard buttons\n            virtual std::vector<int> getActionKeySorting() = 0;\n            ///Actions available for binding to controller buttons\n            virtual std::vector<int> getActionControllerSorting() = 0;\n            virtual int getNumActions() = 0;\n            ///If keyboard is true, only pay attention to keyboard events. If false, only pay attention to controller events (excluding esc)\n            virtual void enableDetectingBindingMode (int action, bool keyboard) = 0;\n            virtual void resetToDefaultKeyBindings() = 0;\n            virtual void resetToDefaultControllerBindings() = 0;\n\n            /// Returns if the last used input device was a joystick or a keyboard\n            /// @return true if joystick, false otherwise\n            virtual bool joystickLastUsed() = 0;\n            virtual void setJoystickLastUsed(bool enabled) = 0;\n\n            virtual int countSavedGameRecords() const = 0;\n            virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0;\n            virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0;\n\n            virtual void resetIdleTime() = 0;\n\n            virtual void executeAction(int action) = 0;\n\n            virtual bool controlsDisabled() = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/journal.hpp",
    "content": "#ifndef GAME_MWBASE_JOURNAL_H\n#define GAME_MWBASE_JOURNAL_H\n\n#include <string>\n#include <deque>\n#include <map>\n\n#include <stdint.h>\n\n#include \"../mwdialogue/journalentry.hpp\"\n#include \"../mwdialogue/topic.hpp\"\n#include \"../mwdialogue/quest.hpp\"\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n}\n\nnamespace MWBase\n{\n    /// \\brief Interface for the player's journal (implemented in MWDialogue)\n    class Journal\n    {\n            Journal (const Journal&);\n            ///< not implemented\n\n            Journal& operator= (const Journal&);\n            ///< not implemented\n\n        public:\n\n            typedef std::deque<MWDialogue::StampedJournalEntry> TEntryContainer;\n            typedef TEntryContainer::const_iterator TEntryIter;\n            typedef std::map<std::string, MWDialogue::Quest> TQuestContainer; // topic, quest\n            typedef TQuestContainer::const_iterator TQuestIter;\n            typedef std::map<std::string, MWDialogue::Topic> TTopicContainer; // topic-id, topic-content\n            typedef TTopicContainer::const_iterator TTopicIter;\n\n        public:\n\n            Journal() {}\n\n            virtual void clear() = 0;\n\n            virtual ~Journal() {}\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a journal entry already exists from elsewhere in the code\n            */\n            virtual bool hasEntry(const std::string& id, int index) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp change (minor)\n\n                Make it possible to override current time when adding journal entries, by adding\n                optional timestamp override arguments\n            */\n            virtual void addEntry (const std::string& id, int index, const MWWorld::Ptr& actor, int daysPassed = -1, int month = -1, int day = -1) = 0;\n            ///< Add a journal entry.\n            /// @param actor Used as context for replacing of escape sequences (%name, etc).\n            /*\n                End of tes3mp change (major)\n            */\n\n            virtual void setJournalIndex (const std::string& id, int index) = 0;\n            ///< Set the journal index without adding an entry.\n\n            virtual int getJournalIndex (const std::string& id) const = 0;\n            ///< Get the journal index.\n\n            virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor) = 0;\n            /// \\note topicId must be lowercase\n\n            virtual void removeLastAddedTopicResponse (const std::string& topicId, const std::string& actorName) = 0;\n            ///< Removes the last topic response added for the given topicId and actor name.\n            /// \\note topicId must be lowercase\n\n            virtual TEntryIter begin() const = 0;\n            ///< Iterator pointing to the begin of the main journal.\n            ///\n            /// \\note Iterators to main journal entries will never become invalid.\n\n            virtual TEntryIter end() const = 0;\n            ///< Iterator pointing past the end of the main journal.\n\n            virtual TQuestIter questBegin() const = 0;\n            ///< Iterator pointing to the first quest (sorted by topic ID)\n\n            virtual TQuestIter questEnd() const = 0;\n            ///< Iterator pointing past the last quest.\n\n            virtual TTopicIter topicBegin() const = 0;\n            ///< Iterator pointing to the first topic (sorted by topic ID)\n            ///\n            /// \\note The topic ID is identical with the user-visible topic string.\n\n            virtual TTopicIter topicEnd() const = 0;\n            ///< Iterator pointing past the last topic.\n\n            virtual int countSavedGameRecords() const = 0;\n\n            virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0;\n\n            virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/mechanicsmanager.hpp",
    "content": "#ifndef GAME_MWBASE_MECHANICSMANAGER_H\n#define GAME_MWBASE_MECHANICSMANAGER_H\n\n#include <string>\n#include <vector>\n#include <list>\n#include <set>\n#include <stdint.h>\n\n#include \"../mwmechanics/actorutil.hpp\"\n// For MWMechanics::GreetingState\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace osg\n{\n    class Stats;\n    class Vec3f;\n}\n\nnamespace ESM\n{\n    struct Class;\n\n    class ESMReader;\n    class ESMWriter;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n    class CellStore;\n    class CellRef;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWBase\n{\n    /// \\brief Interface for game mechanics manager (implemented in MWMechanics)\n    class MechanicsManager\n    {\n            MechanicsManager (const MechanicsManager&);\n            ///< not implemented\n\n            MechanicsManager& operator= (const MechanicsManager&);\n            ///< not implemented\n\n        public:\n\n            MechanicsManager() {}\n\n            virtual ~MechanicsManager() {}\n\n            virtual void add (const MWWorld::Ptr& ptr) = 0;\n            ///< Register an object for management\n\n            virtual void remove (const MWWorld::Ptr& ptr) = 0;\n            ///< Deregister an object for management\n\n            virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) = 0;\n            ///< Moves an object to a new cell\n\n            virtual void drop (const MWWorld::CellStore *cellStore) = 0;\n            ///< Deregister all objects in the given cell.\n\n            virtual void update (float duration, bool paused) = 0;\n            ///< Update objects\n            ///\n            /// \\param paused In game type does not currently advance (this usually means some GUI\n            /// component is up).\n\n            virtual void setPlayerName (const std::string& name) = 0;\n            ///< Set player name.\n\n            virtual void setPlayerRace (const std::string& id, bool male, const std::string &head, const std::string &hair) = 0;\n            ///< Set player race.\n\n            virtual void setPlayerBirthsign (const std::string& id) = 0;\n            ///< Set player birthsign.\n\n            virtual void setPlayerClass (const std::string& id) = 0;\n            ///< Set player class to stock class.\n\n            virtual void setPlayerClass (const ESM::Class& class_) = 0;\n            ///< Set player class to custom class.\n\n            virtual void restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep) = 0;\n\n            virtual void rest(double hours, bool sleep) = 0;\n            ///< If the player is sleeping or waiting, this should be called every hour.\n            /// @param sleep is the player sleeping or waiting?\n\n            virtual int getHoursToRest() const = 0;\n            ///< Calculate how many hours the player needs to rest in order to be fully healed\n\n            virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0;\n            ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC.\n\n            virtual int getDerivedDisposition(const MWWorld::Ptr& ptr, bool addTemporaryDispositionChange = true) = 0;\n            ///< Calculate the diposition of an NPC toward the player.\n\n            virtual int countDeaths (const std::string& id) const = 0;\n            ///< Return the number of deaths for actors with the given ID.\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the number of deaths for an actor with the given refId\n            */\n            virtual void setDeaths(const std::string& refId, int number) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /// Check if \\a observer is potentially aware of \\a ptr. Does not do a line of sight check!\n            virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0;\n\n            /// Makes \\a ptr fight \\a target. Also shouts a combat taunt.\n            virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;\n\n            enum OffenseType\n            {\n                OT_Theft, // Taking items owned by an NPC or a faction you are not a member of\n                OT_Assault, // Attacking a peaceful NPC\n                OT_Murder, // Murdering a peaceful NPC\n                OT_Trespassing, // Picking the lock of an owned door/chest\n                OT_SleepingInOwnedBed, // Sleeping in a bed owned by an NPC or a faction you are not a member of\n                OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft)\n            };\n            /**\n             * @note victim may be empty\n             * @param arg Depends on \\a type, e.g. for Theft, the value of the item that was stolen.\n             * @param victimAware Is the victim already aware of the crime?\n             *                    If this parameter is false, it will be determined by a line-of-sight and awareness check.\n             * @return was the crime seen?\n             */\n            virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type,\n                                      const std::string& factionId=\"\", int arg=0, bool victimAware=false) = 0;\n            /// @return false if the attack was considered a \"friendly hit\" and forgiven\n            virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;\n\n            /// Notify that actor was killed, add a murder bounty if applicable\n            /// @note No-op for non-player attackers\n            virtual void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;\n\n            /// Utility to check if taking this item is illegal and calling commitCrime if so\n            /// @param container The container the item is in; may be empty for an item in the world\n            virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container,\n                                    int count, bool alarm = true) = 0;\n            /// Utility to check if unlocking this object is illegal and calling commitCrime if so\n            virtual void unlockAttempted (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0;\n            /// Attempt sleeping in a bed. If this is illegal, call commitCrime.\n            /// @return was it illegal, and someone saw you doing it?\n            virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0;\n\n            enum PersuasionType\n            {\n                PT_Admire,\n                PT_Intimidate,\n                PT_Taunt,\n                PT_Bribe10,\n                PT_Bribe100,\n                PT_Bribe1000\n            };\n            virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, bool& success, float& tempChange, float& permChange) = 0;\n            ///< Perform a persuasion action on NPC\n\n            virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;\n            ///< Forces an object to refresh its animation state.\n\n            virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1, bool persist=false) = 0;\n            ///< Run animation for a MW-reference. Calls to this function for references that are currently not\n            /// in the scene should be ignored.\n            ///\n            /// \\param mode 0 normal, 1 immediate start, 2 immediate loop\n            /// \\param count How many times the animation should be run\n            /// \\param persist Whether the animation state should be stored in saved games\n            ///                and persist after cell unload.\n            /// \\return Success or error\n\n            virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;\n            ///< Skip the animation for the given MW-reference for one frame. Calls to this function for\n            /// references that are currently not in the scene should be ignored.\n\n            virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;\n\n            /// Save the current animation state of managed references to their RefData.\n            virtual void persistAnimationStates() = 0;\n\n            /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently\n            /// paused we may want to do it manually (after equipping permanent enchantment)\n            virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;\n\n            virtual bool toggleAI() = 0;\n            virtual bool isAIActive() = 0;\n\n            virtual void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& objects) = 0;\n            virtual void getActorsInRange(const osg::Vec3f &position, float radius, std::vector<MWWorld::Ptr> &objects) = 0;\n\n            /// Check if there are actors in selected range\n            virtual bool isAnyActorInRange(const osg::Vec3f &position, float radius) = 0;\n\n            ///Returns the list of actors which are siding with the given actor in fights\n            /**ie AiFollow or AiEscort is active and the target is the actor **/\n            virtual std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) = 0;\n            virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0;\n            virtual std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor) = 0;\n            virtual std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor) = 0;\n\n            ///Returns a list of actors who are fighting the given actor within the fAlarmDistance\n            /** ie AiCombat is active and the target is the actor **/\n            virtual std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor) = 0;\n\n            virtual std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor) = 0;\n\n            /// Recursive versions of above methods\n            virtual void getActorsFollowing(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) = 0;\n            virtual void getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) = 0;\n\n            virtual void playerLoaded() = 0;\n\n            virtual int countSavedGameRecords() const = 0;\n\n            virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0;\n\n            virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0;\n\n            virtual void clear() = 0;\n\n            virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;\n\n            /// Resurrects the player if necessary\n            virtual void resurrect(const MWWorld::Ptr& ptr) = 0;\n\n            virtual bool isCastingSpell (const MWWorld::Ptr& ptr) const = 0;\n            virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;\n            virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the attackingOrSpell state from elsewhere in the code\n            */\n            virtual void setAttackingOrSpell(const MWWorld::Ptr &ptr, bool state) const = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell) = 0;\n\n            virtual void processChangedSettings (const std::set< std::pair<std::string, std::string> >& settings) = 0;\n\n            virtual float getActorsProcessingRange() const = 0;\n\n            virtual void notifyDied(const MWWorld::Ptr& actor) = 0;\n\n            virtual bool onOpen(const MWWorld::Ptr& ptr) = 0;\n            virtual void onClose(const MWWorld::Ptr& ptr) = 0;\n\n            /// Check if the target actor was detected by an observer\n            /// If the observer is a non-NPC, check all actors in AI processing distance as observers\n            virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0;\n\n            virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0;\n\n            /// List the owners that the player has stolen this item from (the owner can be an NPC or a faction).\n            /// <Owner, item count>\n            virtual std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid) = 0;\n\n            /// Has the player stolen this item from the given owner?\n            virtual bool isItemStolenFrom(const std::string& itemid, const MWWorld::Ptr& ptr) = 0;\n\n            virtual bool isBoundItem(const MWWorld::Ptr& item) = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check if an itemId corresponds to a bound item\n            */\n            virtual bool isBoundItem(std::string itemId) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) = 0;\n\n            /// Turn actor into werewolf or normal form.\n            virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0;\n\n            /// Sets the NPC's Acrobatics skill to match the fWerewolfAcrobatics GMST.\n            /// It only applies to the current form the NPC is in.\n            virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0;\n\n            virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0;\n\n            virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) = 0;\n            virtual bool isAttackPreparing(const MWWorld::Ptr& ptr) = 0;\n            virtual bool isRunning(const MWWorld::Ptr& ptr) = 0;\n            virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0;\n\n            virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;\n\n            virtual int getGreetingTimer(const MWWorld::Ptr& ptr) const = 0;\n            virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const  = 0;\n            virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0;\n            virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0;\n\n            virtual void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/rotationflags.hpp",
    "content": "#ifndef GAME_MWBASE_ROTATIONFLAGS_H\n#define GAME_MWBASE_ROTATIONFLAGS_H\n\nnamespace MWBase\n{\n    using RotationFlags = unsigned short;\n\n    enum RotationFlag : RotationFlags\n    {\n        RotationFlag_none = 0,\n        RotationFlag_adjust = 1,\n        RotationFlag_inverseOrder = 1 << 1,\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/scriptmanager.hpp",
    "content": "#ifndef GAME_MWBASE_SCRIPTMANAGER_H\n#define GAME_MWBASE_SCRIPTMANAGER_H\n\n#include <string>\n\nnamespace Interpreter\n{\n    class Context;\n}\n\nnamespace Compiler\n{\n    class Locals;\n}\n\nnamespace MWScript\n{\n    class GlobalScripts;\n}\n\nnamespace MWBase\n{\n    /// \\brief Interface for script manager (implemented in MWScript)\n    class ScriptManager\n    {\n            ScriptManager (const ScriptManager&);\n            ///< not implemented\n\n            ScriptManager& operator= (const ScriptManager&);\n            ///< not implemented\n\n        public:\n\n            ScriptManager() {}\n\n            virtual ~ScriptManager() {}\n\n            virtual void clear() = 0;\n\n            virtual bool run (const std::string& name, Interpreter::Context& interpreterContext) = 0;\n            ///< Run the script with the given name (compile first, if not compiled yet)\n\n            virtual bool compile (const std::string& name) = 0;\n            ///< Compile script with the given namen\n            /// \\return Success?\n\n            virtual std::pair<int, int> compileAll() = 0;\n            ///< Compile all scripts\n            /// \\return count, success\n\n            virtual const Compiler::Locals& getLocals (const std::string& name) = 0;\n            ///< Return locals for script \\a name.\n\n            virtual MWScript::GlobalScripts& getGlobalScripts() = 0;\n   };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/soundmanager.hpp",
    "content": "#ifndef GAME_MWBASE_SOUNDMANAGER_H\n#define GAME_MWBASE_SOUNDMANAGER_H\n\n#include <memory>\n#include <string>\n#include <set>\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwsound/type.hpp\"\n\nnamespace MWWorld\n{\n    class CellStore;\n}\n\nnamespace MWSound\n{\n    // Each entry excepts of MaxCount should be used only in one place\n    enum BlockerType\n    {\n        VideoPlayback,\n\n        MaxCount\n    };\n\n    class Sound;\n    class Stream;\n    struct Sound_Decoder;\n    typedef std::shared_ptr<Sound_Decoder> DecoderPtr;\n\n    /* These must all fit together */\n    enum class PlayMode {\n        Normal  = 0, /* non-looping, affected by environment */\n        Loop    = 1<<0, /* Sound will continually loop until explicitly stopped */\n        NoEnv   = 1<<1, /* Do not apply environment effects (eg, underwater filters) */\n        RemoveAtDistance = 1<<2, /* (3D only) If the listener gets further than 2000 units away\n                                  * from the sound source, the sound is removed.\n                                  * This is weird stuff but apparently how vanilla works for sounds\n                                  * played by the PlayLoopSound family of script functions. Perhaps\n                                  * we can make this cut off a more subtle fade later, but have to\n                                  * be careful to not change the overall volume of areas by too\n                                  * much. */\n        NoPlayerLocal = 1<<3, /* (3D only) Don't play the sound local to the listener even if the\n                               * player is making it. */\n        LoopNoEnv = Loop | NoEnv,\n        LoopRemoveAtDistance = Loop | RemoveAtDistance\n    };\n\n    // Used for creating a type mask for SoundManager::pauseSounds and resumeSounds\n    inline int operator~(Type a) { return ~static_cast<int>(a); }\n    inline int operator&(Type a, Type b) { return static_cast<int>(a) & static_cast<int>(b); }\n    inline int operator&(int a, Type b) { return a & static_cast<int>(b); }\n    inline int operator|(Type a, Type b) { return static_cast<int>(a) | static_cast<int>(b); }\n}\n\nnamespace MWBase\n{\n    using Sound = MWSound::Sound;\n    using SoundStream = MWSound::Stream;\n\n    /// \\brief Interface for sound manager (implemented in MWSound)\n    class SoundManager\n    {\n            SoundManager (const SoundManager&);\n            ///< not implemented\n\n            SoundManager& operator= (const SoundManager&);\n            ///< not implemented\n\n        protected:\n            using PlayMode = MWSound::PlayMode;\n            using Type = MWSound::Type;\n\n        public:\n            SoundManager() {}\n            virtual ~SoundManager() {}\n\n            virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& settings) = 0;\n\n            virtual void stopMusic() = 0;\n            ///< Stops music if it's playing\n\n            virtual void streamMusic(const std::string& filename) = 0;\n            ///< Play a soundifle\n            /// \\param filename name of a sound file in \"Music/\" in the data directory.\n\n            virtual bool isMusicPlaying() = 0;\n            ///< Returns true if music is playing\n\n            virtual void playPlaylist(const std::string &playlist) = 0;\n            ///< Start playing music from the selected folder\n            /// \\param name of the folder that contains the playlist\n\n            virtual void playTitleMusic() = 0;\n            ///< Start playing title music\n\n            virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename) = 0;\n            ///< Make an actor say some text.\n            /// \\param filename name of a sound file in \"Sound/\" in the data directory.\n\n            virtual void say(const std::string& filename) = 0;\n            ///< Say some text, without an actor ref\n            /// \\param filename name of a sound file in \"Sound/\" in the data directory.\n\n            virtual bool sayActive(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const = 0;\n            ///< Is actor not speaking?\n\n            virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const = 0;\n            ///< For scripting backward compatibility\n\n            virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) = 0;\n            ///< Stop an actor speaking\n\n            virtual float getSaySoundLoudness(const MWWorld::ConstPtr& reference) const = 0;\n            ///< Check the currently playing say sound for this actor\n            /// and get an average loudness value (scale [0,1]) at the current time position.\n            /// If the actor is not saying anything, returns 0.\n\n            virtual SoundStream *playTrack(const MWSound::DecoderPtr& decoder, Type type) = 0;\n            ///< Play a 2D audio track, using a custom decoder. The caller is expected to call\n            /// stopTrack with the returned handle when done.\n\n            virtual void stopTrack(SoundStream *stream) = 0;\n            ///< Stop the given audio track from playing\n\n            virtual double getTrackTimeDelay(SoundStream *stream) = 0;\n            ///< Retives the time delay, in seconds, of the audio track (must be a sound\n            /// returned by \\ref playTrack). Only intended to be called by the track\n            /// decoder's read method.\n\n            virtual Sound *playSound(const std::string& soundId, float volume, float pitch,\n                                     Type type=Type::Sfx, PlayMode mode=PlayMode::Normal,\n                                     float offset=0) = 0;\n            ///< Play a sound, independently of 3D-position\n            ///< @param offset Number of seconds into the sound to start playback.\n\n            virtual Sound *playSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId,\n                                       float volume, float pitch, Type type=Type::Sfx,\n                                       PlayMode mode=PlayMode::Normal, float offset=0) = 0;\n            ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified.\n            ///< @param offset Number of seconds into the sound to start playback.\n\n            virtual Sound *playSound3D(const osg::Vec3f& initialPos, const std::string& soundId,\n                                       float volume, float pitch, Type type=Type::Sfx,\n                                       PlayMode mode=PlayMode::Normal, float offset=0) = 0;\n            ///< Play a 3D sound at \\a initialPos. If the sound should be moving, it must be updated using Sound::setPosition.\n\n            virtual void stopSound(Sound *sound) = 0;\n            ///< Stop the given sound from playing\n\n            virtual void stopSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId) = 0;\n            ///< Stop the given object from playing the given sound,\n\n            virtual void stopSound3D(const MWWorld::ConstPtr &reference) = 0;\n            ///< Stop the given object from playing all sounds.\n\n            virtual void stopSound(const MWWorld::CellStore *cell) = 0;\n            ///< Stop all sounds for the given cell.\n\n            virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration) = 0;\n            ///< Fade out given sound (that is already playing) of given object\n            ///< @param reference Reference to object, whose sound is faded out\n            ///< @param soundId ID of the sound to fade out.\n            ///< @param duration Time until volume reaches 0.\n\n            virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const = 0;\n            ///< Is the given sound currently playing on the given object?\n            ///  If you want to check if sound played with playSound is playing, use empty Ptr\n\n            virtual void pauseSounds(MWSound::BlockerType blocker, int types=int(Type::Mask)) = 0;\n            ///< Pauses all currently playing sounds, including music.\n\n            virtual void resumeSounds(MWSound::BlockerType blocker) = 0;\n            ///< Resumes all previously paused sounds.\n\n            virtual void pausePlayback() = 0;\n            virtual void resumePlayback() = 0;\n\n            virtual void update(float duration) = 0;\n\n            virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater) = 0;\n\n            virtual void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) = 0;\n\n            virtual void clear() = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/statemanager.hpp",
    "content": "#ifndef GAME_MWSTATE_STATEMANAGER_H\n#define GAME_MWSTATE_STATEMANAGER_H\n\n#include <list>\n#include <string>\n\nnamespace MWState\n{\n    struct Slot;\n    class Character;\n}\n\nnamespace MWBase\n{\n    /// \\brief Interface for game state manager (implemented in MWState)\n    class StateManager\n    {\n        public:\n\n            enum State\n            {\n                State_NoGame,\n                State_Ended,\n                State_Running\n            };\n\n            typedef std::list<MWState::Character>::const_iterator CharacterIterator;\n\n        private:\n\n            StateManager (const StateManager&);\n            ///< not implemented\n\n            StateManager& operator= (const StateManager&);\n            ///< not implemented\n\n        public:\n\n            StateManager() {}\n\n            virtual ~StateManager() {}\n\n            virtual void requestQuit() = 0;\n\n            virtual bool hasQuitRequest() const = 0;\n\n            virtual void askLoadRecent() = 0;\n\n            virtual State getState() const = 0;\n\n            virtual void newGame (bool bypass = false) = 0;\n            ///< Start a new game.\n            ///\n            /// \\param bypass Skip new game mechanics.\n\n            virtual void endGame() = 0;\n\n            virtual void resumeGame() = 0;\n\n            virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot) = 0;\n\n            virtual void saveGame (const std::string& description, const MWState::Slot *slot = nullptr) = 0;\n            ///< Write a saved game to \\a slot or create a new slot if \\a slot == 0.\n            ///\n            /// \\note Slot must belong to the current character.\n\n            virtual void loadGame (const std::string& filepath) = 0;\n            ///< Load a saved game directly from the given file path. This will search the CharacterManager\n            /// for a Character containing this save file, and set this Character current if one was found.\n            /// Otherwise, a new Character will be created.\n\n            virtual void loadGame (const MWState::Character *character, const std::string& filepath) = 0;\n            ///< Load a saved game file belonging to the given character.\n\n            ///Simple saver, writes over the file if already existing\n            /** Used for quick save and autosave **/\n            virtual void quickSave(std::string = \"Quicksave\")=0;\n\n            ///Simple loader, loads the last saved file\n            /** Used for quickload **/\n            virtual void quickLoad()=0;\n\n            virtual MWState::Character *getCurrentCharacter () = 0;\n            ///< @note May return null.\n\n            virtual CharacterIterator characterBegin() = 0;\n            ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned\n            /// iterator.\n\n            virtual CharacterIterator characterEnd() = 0;\n\n            virtual void update (float duration) = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/windowmanager.hpp",
    "content": "#ifndef GAME_MWBASE_WINDOWMANAGER_H\n#define GAME_MWBASE_WINDOWMANAGER_H\n\n#include <stdint.h>\n#include <string>\n#include <vector>\n#include <map>\n#include <set>\n\n#include <MyGUI_KeyCode.h>\n\n#include \"../mwgui/mode.hpp\"\n\n#include <components/sdlutil/events.hpp>\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace Translation\n{\n    class Storage;\n}\n\nnamespace MyGUI\n{\n    class Gui;\n    class Widget;\n    class UString;\n}\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n    struct CellId;\n}\n\nnamespace MWMechanics\n{\n    class AttributeValue;\n    template<typename T>\n    class DynamicStat;\n    class SkillValue;\n}\n\nnamespace MWWorld\n{\n    class CellStore;\n    class Ptr;\n}\n\nnamespace MWGui\n{\n    class Layout;\n\n    class Console;\n    class SpellWindow;\n    class TradeWindow;\n    class TravelWindow;\n    class SpellBuyingWindow;\n    class ConfirmationDialog;\n    class CountDialog;\n    class ScrollWindow;\n    class BookWindow;\n    class InventoryWindow;\n    class ContainerWindow;\n    class DialogueWindow;\n    class WindowModal;\n    class JailScreen;\n\n    enum ShowInDialogueMode {\n        ShowInDialogueMode_IfPossible,\n        ShowInDialogueMode_Only,\n        ShowInDialogueMode_Never\n    };\n\n    struct TextColours;\n}\n\nnamespace SFO\n{\n    class CursorManager;\n}\n\nnamespace MWBase\n{\n    /// \\brief Interface for widnow manager (implemented in MWGui)\n    class WindowManager : public SDLUtil::WindowListener\n    {\n            WindowManager (const WindowManager&);\n            ///< not implemented\n\n            WindowManager& operator= (const WindowManager&);\n            ///< not implemented\n\n        public:\n\n            typedef std::vector<int> SkillList;\n\n            WindowManager() {}\n\n            virtual ~WindowManager() {}\n\n            /// @note This method will block until the video finishes playing\n            /// (and will continually update the window while doing so)\n            virtual void playVideo(const std::string& name, bool allowSkipping) = 0;\n\n            virtual void setNewGame(bool newgame) = 0;\n\n            virtual void pushGuiMode (MWGui::GuiMode mode, const MWWorld::Ptr& arg) = 0;\n            virtual void pushGuiMode (MWGui::GuiMode mode) = 0;\n            virtual void popGuiMode(bool noSound=false) = 0;\n\n            virtual void removeGuiMode (MWGui::GuiMode mode, bool noSound=false) = 0;\n            ///< can be anywhere in the stack\n\n            virtual void goToJail(int days) = 0;\n\n            virtual void updatePlayer() = 0;\n\n            virtual MWGui::GuiMode getMode() const = 0;\n            virtual bool containsMode(MWGui::GuiMode) const = 0;\n\n            virtual bool isGuiMode() const = 0;\n\n            virtual bool isConsoleMode() const = 0;\n\n            virtual void toggleVisible (MWGui::GuiWindow wnd) = 0;\n\n            virtual void forceHide(MWGui::GuiWindow wnd) = 0;\n            virtual void unsetForceHide(MWGui::GuiWindow wnd) = 0;\n\n            /// Disallow all inventory mode windows\n            virtual void disallowAll() = 0;\n\n            /// Allow one or more windows\n            virtual void allow (MWGui::GuiWindow wnd) = 0;\n\n            virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0;\n\n            /// \\todo investigate, if we really need to expose every single lousy UI element to the outside world\n            virtual MWGui::InventoryWindow* getInventoryWindow() = 0;\n            virtual MWGui::CountDialog* getCountDialog() = 0;\n            virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;\n            virtual MWGui::TradeWindow* getTradeWindow() = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the ContainerWindow from elsewhere\n                in the code\n            */\n            virtual MWGui::ContainerWindow* getContainerWindow() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the DialogueWindow from elsewhere\n            */\n            virtual MWGui::DialogueWindow* getDialogueWindow() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /// Make the player use an item, while updating GUI state accordingly\n            virtual void useItem(const MWWorld::Ptr& item, bool force=false) = 0;\n\n            virtual void updateSpellWindow() = 0;\n\n            virtual void setConsoleSelectedObject(const MWWorld::Ptr& object) = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Allow the direct setting of a console's Ptr, without the assumption that an object\n                was clicked and that key focus should be restored to the console window, for console\n                commands executed via server scripts\n            */\n            virtual void setConsolePtr(const MWWorld::Ptr& object) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Allow the clearing of the console's Ptr from elsewhere in the code, so that\n                Ptrs used in console commands run from server scripts do not stay selected\n            */\n            virtual void clearConsolePtr() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /// Set time left for the player to start drowning (update the drowning bar)\n            /// @param time time left to start drowning\n            /// @param maxTime how long we can be underwater (in total) until drowning starts\n            virtual void setDrowningTimeLeft (float time, float maxTime) = 0;\n\n            virtual void changeCell(const MWWorld::CellStore* cell) = 0;\n            ///< change the active cell\n\n            /*\n                Start of tes3mp addition\n\n                Allow the setting of the image data for a global map tile from elsewhere\n                in the code\n            */\n            virtual void setGlobalMapImage(int cellX, int cellY, const std::vector<char>& imageData) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual void setFocusObject(const MWWorld::Ptr& focus) = 0;\n            virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0;\n\n            virtual void setCursorVisible(bool visible) = 0;\n            virtual void setCursorActive(bool active) = 0;\n            virtual void getMousePosition(int &x, int &y) = 0;\n            virtual void getMousePosition(float &x, float &y) = 0;\n            virtual void setDragDrop(bool dragDrop) = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Allow the completion of a drag and drop from elsewhere in the code\n            */\n            virtual void finishDragDrop() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual bool getWorldMouseOver() = 0;\n\n            virtual float getScalingFactor() = 0;\n\n            virtual bool toggleFogOfWar() = 0;\n\n            virtual bool toggleFullHelp() = 0;\n            ///< show extra info in item tooltips (owner, script)\n\n            virtual bool getFullHelp() const = 0;\n\n            virtual void setActiveMap(int x, int y, bool interior) = 0;\n            ///< set the indices of the map texture that should be used\n\n            /// sets the visibility of the drowning bar\n            virtual void setDrowningBarVisibility(bool visible) = 0;\n\n            /// sets the visibility of the hud health/magicka/stamina bars\n            virtual void setHMSVisibility(bool visible) = 0;\n\n            /// sets the visibility of the hud minimap\n            virtual void setMinimapVisibility(bool visible) = 0;\n            virtual void setWeaponVisibility(bool visible) = 0;\n            virtual void setSpellVisibility(bool visible) = 0;\n            virtual void setSneakVisibility(bool visible) = 0;\n\n            /// activate selected quick key\n            virtual void activateQuickKey (int index) = 0;\n            /// update activated quick key state (if action executing was delayed for some reason)\n            virtual void updateActivatedQuickKey () = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to add quickKeys from elsewhere in the code\n            */\n            virtual void setQuickKey(int slot, int quickKeyType, MWWorld::Ptr item, const std::string& spellId = \"\") = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual std::string getSelectedSpell() = 0;\n            virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0;\n            virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0;\n            virtual const MWWorld::Ptr& getSelectedEnchantItem() const = 0;\n            virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0;\n            virtual const MWWorld::Ptr& getSelectedWeapon() const = 0;\n            virtual int getFontHeight() const = 0;\n            virtual void unsetSelectedSpell() = 0;\n            virtual void unsetSelectedWeapon() = 0;\n\n            virtual void showCrosshair(bool show) = 0;\n            virtual bool getSubtitlesEnabled() = 0;\n            virtual bool toggleHud() = 0;\n\n            virtual void disallowMouse() = 0;\n            virtual void allowMouse() = 0;\n            virtual void notifyInputActionBound() = 0;\n\n            virtual void addVisitedLocation(const std::string& name, int x, int y) = 0;\n\n            /// Hides dialog and schedules dialog to be deleted.\n            virtual void removeDialog(MWGui::Layout* dialog) = 0;\n\n            ///Gracefully attempts to exit the topmost GUI mode\n            /** No guarantee of actually closing the window **/\n            virtual void exitCurrentGuiMode() = 0;\n\n            virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0;\n            virtual void staticMessageBox(const std::string& message) = 0;\n            virtual void removeStaticMessageBox() = 0;\n            /*\n                Start of tes3mp change (major)\n\n                Add a hasServerOrigin boolean to the list of arguments so those messageboxes\n                can be differentiated from client-only ones\n            */\n            virtual void interactiveMessageBox (const std::string& message,\n                                                const std::vector<std::string>& buttons = std::vector<std::string>(), bool block=false, bool hasServerOrigin=false) = 0;\n            /*\n                End of tes3mp change (major)\n            */\n\n            /// returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)\n            virtual int readPressedButton() = 0;\n\n            virtual void update (float duration) = 0;\n\n            virtual void updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr) = 0;\n\n            /**\n             * Fetches a GMST string from the store, if there is no setting with the given\n             * ID or it is not a string the default string is returned.\n             *\n             * @param id Identifier for the GMST setting, e.g. \"aName\"\n             * @param default Default value if the GMST setting cannot be used.\n             */\n            virtual std::string getGameSettingString(const std::string &id, const std::string &default_) = 0;\n\n            virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) = 0;\n\n            virtual void executeInConsole (const std::string& path) = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Allow the execution of console commands from elsewhere in the code\n            */\n            virtual void executeCommandInConsole(const std::string& command) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual void enableRest() = 0;\n            virtual bool getRestEnabled() = 0;\n            virtual bool getJournalAllowed() = 0; \n\n            virtual bool getPlayerSleeping() = 0;\n            virtual void wakeUpPlayer() = 0;\n\n            virtual void showSoulgemDialog (MWWorld::Ptr item) = 0;\n\n            virtual void changePointer (const std::string& name) = 0;\n\n            virtual void setEnemy (const MWWorld::Ptr& enemy) = 0;\n\n            virtual int getMessagesCount() const = 0;\n\n            virtual const Translation::Storage& getTranslationDataStorage() const = 0;\n\n            /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.\n            virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;\n\n            virtual void loadUserFonts() = 0;\n\n            virtual Loading::Listener* getLoadingScreen() = 0;\n\n            /// Should the cursor be visible?\n            virtual bool getCursorVisible() = 0;\n\n            /// Clear all savegame-specific data\n            virtual void clear() = 0;\n\n            virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) = 0;\n            virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0;\n            virtual int countSavedGameRecords() const = 0;\n\n            /// Does the current stack of GUI-windows permit saving?\n            virtual bool isSavingAllowed() const = 0;\n\n            /// Send exit command to active Modal window\n            virtual void exitCurrentModal() = 0;\n\n            /// Sets the current Modal\n            /** Used to send exit command to active Modal when Esc is pressed **/\n            virtual void addCurrentModal(MWGui::WindowModal* input) = 0;\n\n            /// Removes the top Modal\n            /** Used when one Modal adds another Modal\n                \\param input Pointer to the current modal, to ensure proper modal is removed **/\n            virtual void removeCurrentModal(MWGui::WindowModal* input) = 0;\n\n            virtual void pinWindow (MWGui::GuiWindow window) = 0;\n            virtual void toggleMaximized(MWGui::Layout *layout) = 0;\n\n            /// Fade the screen in, over \\a time seconds\n            virtual void fadeScreenIn(const float time, bool clearQueue=true, float delay=0.f) = 0;\n            /// Fade the screen out to black, over \\a time seconds\n            virtual void fadeScreenOut(const float time, bool clearQueue=true, float delay=0.f) = 0;\n            /// Fade the screen to a specified percentage of black, over \\a time seconds\n            virtual void fadeScreenTo(const int percent, const float time, bool clearQueue=true, float delay=0.f) = 0;\n            /// Darken the screen to a specified percentage\n            virtual void setBlindness(const int percent) = 0;\n\n            virtual void activateHitOverlay(bool interrupt=true) = 0;\n            virtual void setWerewolfOverlay(bool set) = 0;\n\n            virtual void toggleConsole() = 0;\n            virtual void toggleDebugWindow() = 0;\n\n            /// Cycle to next or previous spell\n            virtual void cycleSpell(bool next) = 0;\n            /// Cycle to next or previous weapon\n            virtual void cycleWeapon(bool next) = 0;\n\n            virtual void playSound(const std::string& soundId, float volume = 1.f, float pitch = 1.f) = 0;\n\n            // In WindowManager for now since there isn't a VFS singleton\n            virtual std::string correctIconPath(const std::string& path) = 0;\n            virtual std::string correctBookartPath(const std::string& path, int width, int height, bool* exists = nullptr) = 0;\n            virtual std::string correctTexturePath(const std::string& path) = 0;\n            virtual bool textureExists(const std::string& path) = 0;\n\n            virtual void addCell(MWWorld::CellStore* cell) = 0;\n            virtual void removeCell(MWWorld::CellStore* cell) = 0;\n            virtual void writeFog(MWWorld::CellStore* cell) = 0;\n\n            virtual const MWGui::TextColours& getTextColours() = 0;\n\n            virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0;\n            virtual bool injectKeyRelease(MyGUI::KeyCode key) = 0;\n\n            void windowVisibilityChange(bool visible) override = 0;\n            void windowResized(int x, int y) override = 0;\n            void windowClosed() override = 0;\n            virtual bool isWindowVisible() = 0;\n\n            virtual void watchActor(const MWWorld::Ptr& ptr) = 0;\n            virtual MWWorld::Ptr getWatchedActor() const = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwbase/world.hpp",
    "content": "#ifndef GAME_MWBASE_WORLD_H\n#define GAME_MWBASE_WORLD_H\n\n#include \"rotationflags.hpp\"\n\n#include <vector>\n#include <map>\n#include <set>\n#include <deque>\n\n#include <components/esm/cellid.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/esm/variant.hpp>\n/*\n    End of tes3mp addition\n*/\n\n#include <osg/Timer>\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/doorstate.hpp\"\n\n#include \"../mwrender/rendermode.hpp\"\n\nnamespace osg\n{\n    class Vec3f;\n    class Matrixf;\n    class Quat;\n    class Image;\n    class Stats;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n    struct Position;\n    struct Cell;\n    struct Class;\n    struct Container;\n    struct Creature;\n    struct Potion;\n    struct Spell;\n    struct NPC;\n    struct Armor;\n    struct Weapon;\n    struct Clothing;\n    struct Enchantment;\n    struct Book;\n    struct EffectList;\n    struct CreatureLevList;\n    struct ItemLevList;\n    struct TimeStamp;\n}\n\nnamespace MWPhysics\n{\n    class RayCastingInterface;\n}\n\nnamespace MWRender\n{\n    class Animation;\n}\n\nnamespace MWMechanics\n{\n    struct Movement;\n}\n\nnamespace DetourNavigator\n{\n    struct Navigator;\n}\n\nnamespace MWWorld\n{\n    class CellStore;\n    class Player;\n    class LocalScripts;\n    class TimeStamp;\n    class ESMStore;\n    class RefData;\n\n    typedef std::vector<std::pair<MWWorld::Ptr,MWMechanics::Movement> > PtrMovementList;\n}\n\nnamespace MWBase\n{\n    /// \\brief Interface for the World (implemented in MWWorld)\n    class World\n    {\n            World (const World&);\n            ///< not implemented\n\n            World& operator= (const World&);\n            ///< not implemented\n\n        public:\n\n            struct DoorMarker\n            {\n                std::string name;\n                float x, y; // world position\n                ESM::CellId dest;\n            };\n\n            World() {}\n\n            virtual ~World() {}\n\n            virtual void startNewGame (bool bypass) = 0;\n            ///< \\param bypass Bypass regular game start.\n\n            virtual void clear() = 0;\n\n            virtual int countSavedGameRecords() const = 0;\n            virtual int countSavedGameCells() const = 0;\n\n            virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0;\n\n            virtual void readRecord (ESM::ESMReader& reader, uint32_t type,\n                const std::map<int, int>& contentFileMap) = 0;\n\n            virtual MWWorld::CellStore *getExterior (int x, int y) = 0;\n\n            virtual MWWorld::CellStore *getInterior (const std::string& name) = 0;\n\n            virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0;\n\n            virtual void testExteriorCells() = 0;\n            virtual void testInteriorCells() = 0;\n\n            virtual void useDeathCamera() = 0;\n\n            virtual void setWaterHeight(const float height) = 0;\n\n            virtual bool toggleWater() = 0;\n            virtual bool toggleWorld() = 0;\n            virtual bool toggleBorders() = 0;\n\n            virtual void adjustSky() = 0;\n\n            virtual MWWorld::Player& getPlayer() = 0;\n            virtual MWWorld::Ptr getPlayerPtr() = 0;\n            virtual MWWorld::ConstPtr getPlayerConstPtr() const = 0;\n\n            virtual const MWWorld::ESMStore& getStore() const = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the World's ESMStore as a non-const\n            */\n            virtual MWWorld::ESMStore& getModifiableStore() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual std::vector<ESM::ESMReader>& getEsmReader() = 0;\n\n            virtual MWWorld::LocalScripts& getLocalScripts() = 0;\n\n            virtual bool hasCellChanged() const = 0;\n            ///< Has the set of active cells changed, since the last frame?\n\n            virtual bool isCellExterior() const = 0;\n\n            virtual bool isCellQuasiExterior() const = 0;\n\n            virtual osg::Vec2f getNorthVector (const MWWorld::CellStore* cell) = 0;\n            ///< get north vector for given interior cell\n\n            virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector<DoorMarker>& out) = 0;\n            ///< get a list of teleport door markers for a given cell, to be displayed on the local map\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether global variables exist and to create\n                new ones\n            */\n            virtual bool hasGlobal(const std::string& name) = 0;\n\n            virtual void createGlobal(const std::string& name, ESM::VarType varType) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual void setGlobalInt (const std::string& name, int value) = 0;\n            ///< Set value independently from real type.\n\n            virtual void setGlobalFloat (const std::string& name, float value) = 0;\n            ///< Set value independently from real type.\n\n            virtual int getGlobalInt (const std::string& name) const = 0;\n            ///< Get value independently from real type.\n\n            virtual float getGlobalFloat (const std::string& name) const = 0;\n            ///< Get value independently from real type.\n\n            virtual char getGlobalVariableType (const std::string& name) const = 0;\n            ///< Return ' ', if there is no global variable with this name.\n\n            virtual std::string getCellName (const MWWorld::CellStore *cell = nullptr) const = 0;\n            ///< Return name of the cell.\n            ///\n            /// \\note If cell==0, the cell the player is currently in will be used instead to\n            /// generate a name.\n            virtual std::string getCellName(const ESM::Cell* cell) const = 0;\n\n            virtual void removeRefScript (MWWorld::RefData *ref) = 0;\n            //< Remove the script attached to ref from mLocalScripts\n\n            virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0;\n            ///< Return a pointer to a liveCellRef with the given name.\n            /// \\param activeOnly do non search inactive cells.\n\n            virtual MWWorld::Ptr searchPtr (const std::string& name, bool activeOnly, bool searchInContainers = true) = 0;\n            ///< Return a pointer to a liveCellRef with the given name.\n            /// \\param activeOnly do non search inactive cells.\n\n            virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0;\n            ///< Search is limited to the active cells.\n\n            virtual MWWorld::Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to find a Ptr in any active cell based on its refNum and mpNum\n            */\n            virtual MWWorld::Ptr searchPtrViaUniqueIndex(int refNum, int mpNum) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to update all Ptrs in active cells that have a certain refId\n            */\n            virtual void updatePtrsWithRefId(std::string refId) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0;\n            ///< Return a pointer to a liveCellRef which contains \\a ptr.\n            /// \\note Search is limited to the active cells.\n\n            virtual void enable (const MWWorld::Ptr& ptr) = 0;\n\n            virtual void disable (const MWWorld::Ptr& ptr) = 0;\n\n            virtual void advanceTime (double hours, bool incremental = false) = 0;\n            ///< Advance in-game time.\n\n            virtual std::string getMonthName (int month = -1) const = 0;\n            ///< Return name of month (-1: current month)\n\n            virtual MWWorld::TimeStamp getTimeStamp() const = 0;\n            ///< Return current in-game time and number of day since new game start.\n\n            virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0;\n            ///< Return current in-game date and time.\n\n            virtual bool toggleSky() = 0;\n            ///< \\return Resulting mode\n\n            virtual void changeWeather(const std::string& region, const unsigned int id) = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set a specific weather state for a region from elsewhere\n                in the code\n            */\n            virtual void setRegionWeather(const std::string& region, const unsigned int currentWeather, const unsigned int nextWeather,\n                const unsigned int queuedWeather, const float transitionFactor, bool force) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether the local WeatherManager has the\n                ability to create weather changes\n            */\n            virtual bool getWeatherCreationState() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to enable and disable the local WeatherManager's ability\n                to create weather changes\n            */\n            virtual void setWeatherCreationState(bool state) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to send the current weather in a WorldWeather packet\n                when requested from elsewhere in the code\n            */\n            virtual void sendWeather() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual int getCurrentWeather() const = 0;\n\n            virtual unsigned int getNightDayMode() const = 0;\n\n            virtual int getMasserPhase() const = 0;\n\n            virtual int getSecundaPhase() const = 0;\n\n            virtual void setMoonColour (bool red) = 0;\n\n            virtual void modRegion(const std::string &regionid, const std::vector<char> &chances) = 0;\n\n            virtual float getTimeScaleFactor() const = 0;\n\n            virtual void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true) = 0;\n            ///< Move to interior cell.\n            ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes\n\n            virtual void changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true) = 0;\n            ///< Move to exterior cell.\n            ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes\n\n            virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true) = 0;\n            ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes\n\n            virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;\n            ///< Return a cell matching the given name or a 0-pointer, if there is no such cell.\n\n            virtual void markCellAsUnchanged() = 0;\n\n            virtual MWWorld::Ptr  getFacedObject() = 0;\n            ///< Return pointer to the object the player is looking at, if it is within activation range\n\n            /*\n                Start of tes3mp addition\n\n                This has been declared here so it can be accessed from places\n                other than MWWorld::World\n            */\n            virtual void updateWeather(float duration, bool paused = false) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                This has been declared here so it can be accessed from places\n                other than MWWorld::World \n            */\n            virtual void PCDropped(const MWWorld::Ptr& item) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual float getDistanceToFacedObject() = 0;\n\n            virtual float getMaxActivationDistance() = 0;\n\n            /// Returns a pointer to the object the provided object would hit (if within the\n            /// specified distance), and the point where the hit occurs. This will attempt to\n            /// use the \"Head\" node, or alternatively the \"Bip01 Head\" node as a basis.\n            virtual std::pair<MWWorld::Ptr,osg::Vec3f> getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector<MWWorld::Ptr> &targets) = 0;\n\n            virtual void adjustPosition (const MWWorld::Ptr& ptr, bool force) = 0;\n            ///< Adjust position after load to be on ground. Must be called after model load.\n            /// @param force do this even if the ptr is flying\n\n            virtual void fixPosition () = 0;\n            ///< Attempt to fix position so that the player is not stuck inside the geometry.\n\n            /// @note No-op for items in containers. Use ContainerStore::removeItem instead.\n            virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;\n            virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0;\n\n            virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) = 0;\n            ///< @return an updated Ptr in case the Ptr's cell changes\n\n            virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0;\n            ///< @return an updated Ptr\n\n            virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec, bool moveToActive, bool ignoreCollisions) = 0;\n            ///< @return an updated Ptr\n\n            virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;\n\n            virtual void rotateObject(const MWWorld::Ptr& ptr, float x, float y, float z,\n                RotationFlags flags = RotationFlag_inverseOrder) = 0;\n\n            virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0;\n            ///< Place an object. Makes a copy of the Ptr.\n\n            virtual MWWorld::Ptr safePlaceObject (const MWWorld::ConstPtr& ptr, const MWWorld::ConstPtr& referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance) = 0;\n            ///< Place an object in a safe place next to \\a referenceObject. \\a direction and \\a distance specify the wanted placement\n            /// relative to \\a referenceObject (but the object may be placed somewhere else if the wanted location is obstructed).\n\n            virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false)\n                const = 0;\n            ///< Convert cell numbers to position.\n\n            virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0;\n            ///< Convert position to cell numbers\n\n            virtual void queueMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity) = 0;\n            ///< Queues movement for \\a ptr (in local space), to be applied in the next call to\n            /// doPhysics.\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the inertial force of a Ptr directly\n            */\n            virtual void setInertialForce(const MWWorld::Ptr& ptr, const osg::Vec3f &force) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set whether a Ptr is on the ground or not, needed for proper\n                synchronization in multiplayer\n            */\n            virtual void setOnGround(const MWWorld::Ptr& ptr, bool onGround) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the physics framerate from elsewhere\n            */\n            virtual void setPhysicsFramerate(float physFramerate) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual void updateAnimatedCollisionShape(const MWWorld::Ptr &ptr) = 0;\n\n            virtual const MWPhysics::RayCastingInterface* getRayCasting() const = 0;\n\n            virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) = 0;\n            ///< cast a Ray and return true if there is an object in the ray path.\n\n            virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;\n\n            virtual bool castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore) = 0;\n\n            virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0;\n            virtual bool isActorCollisionEnabled(const MWWorld::Ptr& ptr) = 0;\n\n            virtual bool toggleCollisionMode() = 0;\n            ///< Toggle collision mode for player. If disabled player object should ignore\n            /// collisions and gravity.\n            /// \\return Resulting mode\n\n            virtual bool toggleRenderMode (MWRender::RenderMode mode) = 0;\n            ///< Toggle a render mode.\n            ///< \\return Resulting mode\n\n            virtual const ESM::Potion *createRecord (const ESM::Potion& record) = 0;\n            ///< Create a new record (of type potion) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Spell *createRecord (const ESM::Spell& record) = 0;\n            ///< Create a new record (of type spell) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Class *createRecord (const ESM::Class& record) = 0;\n            ///< Create a new record (of type class) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Cell *createRecord (const ESM::Cell& record) = 0;\n            ///< Create a new record (of type cell) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::NPC *createRecord(const ESM::NPC &record) = 0;\n            ///< Create a new record (of type npc) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Creature *createRecord (const ESM::Creature &record) = 0;\n            ///< Create a new record (of type creature) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Armor *createRecord (const ESM::Armor& record) = 0;\n            ///< Create a new record (of type armor) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Weapon *createRecord (const ESM::Weapon& record) = 0;\n            ///< Create a new record (of type weapon) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Clothing *createRecord (const ESM::Clothing& record) = 0;\n            ///< Create a new record (of type clothing) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Enchantment *createRecord (const ESM::Enchantment& record) = 0;\n            ///< Create a new record (of type enchantment) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::Book *createRecord (const ESM::Book& record) = 0;\n            ///< Create a new record (of type book) in the ESM store.\n            /// \\return pointer to created record\n\n            virtual const ESM::CreatureLevList *createOverrideRecord (const ESM::CreatureLevList& record) = 0;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            virtual const ESM::ItemLevList *createOverrideRecord (const ESM::ItemLevList& record) = 0;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            virtual const ESM::Creature *createOverrideRecord (const ESM::Creature& record) = 0;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            virtual const ESM::NPC *createOverrideRecord (const ESM::NPC& record) = 0;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            virtual const ESM::Container *createOverrideRecord (const ESM::Container& record) = 0;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            virtual void update (float duration, bool paused) = 0;\n            virtual void updatePhysics (float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) = 0;\n\n            virtual void updateWindowManager () = 0;\n\n            virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) = 0;\n            ///< copy and place an object into the gameworld at the specified cursor position\n            /// @param object\n            /// @param cursor X (relative 0-1)\n            /// @param cursor Y (relative 0-1)\n            /// @param number of objects to place\n\n            virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) = 0;\n            ///< copy and place an object into the gameworld at the given actor's position\n            /// @param actor giving the dropped object position\n            /// @param object\n            /// @param number of objects to place\n\n            virtual bool canPlaceObject (float cursorX, float cursorY) = 0;\n            ///< @return true if it is possible to place on object at specified cursor location\n\n            virtual void processChangedSettings (const std::set< std::pair<std::string, std::string> >& settings) = 0;\n\n            virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0;\n            virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0;\n            virtual bool isSwimming(const MWWorld::ConstPtr &object) const = 0;\n            virtual bool isWading(const MWWorld::ConstPtr &object) const = 0;\n            ///Is the head of the creature underwater?\n            virtual bool isSubmerged(const MWWorld::ConstPtr &object) const = 0;\n            virtual bool isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const = 0;\n            virtual bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const = 0;\n            virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const = 0;\n            virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;\n\n            virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0;\n\n            virtual void togglePOV(bool force = false) = 0;\n            virtual bool isFirstPerson() const = 0;\n            virtual bool isPreviewModeEnabled() const = 0;\n            virtual void togglePreviewMode(bool enable) = 0;\n            virtual bool toggleVanityMode(bool enable) = 0;\n            virtual void allowVanityMode(bool allow) = 0;\n            virtual bool vanityRotateCamera(float * rot) = 0;\n            virtual void adjustCameraDistance(float dist) = 0;\n            virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0;\n            virtual void disableDeferredPreviewRotation() = 0;\n\n            virtual void saveLoaded() = 0;\n\n            virtual void setupPlayer() = 0;\n            virtual void renderPlayer() = 0;\n\n            /// open or close a non-teleport door (depending on current state)\n            virtual void activateDoor(const MWWorld::Ptr& door) = 0;\n            /// update movement state of a non-teleport door as specified\n            /// @param state see MWClass::setDoorState\n            /// @note throws an exception when invoked on a teleport door\n            virtual void activateDoor(const MWWorld::Ptr& door, MWWorld::DoorState state) = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Useful self-contained method for saving door states\n            */\n            virtual void saveDoorState(const MWWorld::Ptr& door, MWWorld::DoorState state) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a cell is active\n            */\n            virtual bool isCellActive(const ESM::Cell& cell) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to unload a cell from elsewhere\n            */\n            virtual void unloadCell(const ESM::Cell& cell) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to unload all active cells from elsewhere\n            */\n            virtual void unloadActiveCells() = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Clear the CellStore for a specific Cell from elsewhere\n            */\n            virtual void clearCellStore(const ESM::Cell& cell) = 0;\n            /*\n                End of tes3mp addition\n            */\n\n            virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) = 0; ///< get a list of actors standing on \\a object\n            virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \\a object\n            virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \\a object\n            virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \\a object\n            virtual bool getActorCollidingWith (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is colliding with \\a object\n            virtual void hurtStandingActors (const MWWorld::ConstPtr& object, float dmgPerSecond) = 0;\n            ///< Apply a health difference to any actors standing on \\a object.\n            /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed.\n            virtual void hurtCollidingActors (const MWWorld::ConstPtr& object, float dmgPerSecond) = 0;\n            ///< Apply a health difference to any actors colliding with \\a object.\n            /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed.\n\n            virtual float getWindSpeed() = 0;\n\n            virtual void getContainersOwnedBy (const MWWorld::ConstPtr& npc, std::vector<MWWorld::Ptr>& out) = 0;\n            ///< get all containers in active cells owned by this Npc\n            virtual void getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector<MWWorld::Ptr>& out) = 0;\n            ///< get all items in active cells owned by this Npc\n\n            virtual bool getLOS(const MWWorld::ConstPtr& actor,const MWWorld::ConstPtr& targetActor) = 0;\n            ///< get Line of Sight (morrowind stupid implementation)\n\n            virtual float getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist, bool includeWater = false) = 0;\n\n            virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0;\n\n            enum RestPermitted\n            {\n                Rest_Allowed = 0,\n                Rest_OnlyWaiting = 1,\n                Rest_PlayerIsInAir = 2,\n                Rest_PlayerIsUnderwater = 3,\n                Rest_EnemiesAreNearby = 4\n            };\n\n            /// check if the player is allowed to rest\n            virtual RestPermitted canRest() const = 0;\n\n            /// \\todo Probably shouldn't be here\n            virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0;\n            virtual const MWRender::Animation* getAnimation(const MWWorld::ConstPtr &ptr) const = 0;\n            virtual void reattachPlayerCamera() = 0;\n\n            /// \\todo this does not belong here\n            virtual void screenshot (osg::Image* image, int w, int h) = 0;\n            virtual bool screenshot360 (osg::Image* image) = 0;\n\n            /// Find default position inside exterior cell specified by name\n            /// \\return false if exterior with given name not exists, true otherwise\n            virtual bool findExteriorPosition(const std::string &name, ESM::Position &pos) = 0;\n\n            /// Find default position inside interior cell specified by name\n            /// \\return false if interior with given name not exists, true otherwise\n            virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos) = 0;\n\n            /// Enables or disables use of teleport spell effects (recall, intervention, etc).\n            virtual void enableTeleporting(bool enable) = 0;\n\n            /// Returns true if teleport spell effects are allowed.\n            virtual bool isTeleportingEnabled() const = 0;\n\n            /// Enables or disables use of levitation spell effect.\n            virtual void enableLevitation(bool enable) = 0;\n\n            /// Returns true if levitation spell effect is allowed.\n            virtual bool isLevitationEnabled() const = 0;\n\n            virtual bool getGodModeState() const = 0;\n\n            virtual bool toggleGodMode() = 0;\n\n            virtual bool toggleScripts() = 0;\n            virtual bool getScriptsEnabled() const = 0;\n\n            /**\n             * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met.\n             * @param actor\n             * @return true if the spell can be casted (i.e. the animation should start)\n             */\n            virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0;\n\n            virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0;\n\n            virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;\n            virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,\n                                           const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0;\n            virtual void updateProjectilesCasters() = 0;\n\n            virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0;\n\n            virtual const std::vector<std::string>& getContentFiles() const = 0;\n\n            virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0;\n\n            // Allow NPCs to use torches?\n            virtual bool useTorches() const = 0;\n\n            virtual bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) = 0;\n\n            /// Teleports \\a ptr to the closest reference of \\a id (e.g. DivineMarker, PrisonMarker, TempleMarker)\n            /// @note id must be lower case\n            virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr,\n                                                  const std::string& id) = 0;\n\n            enum DetectionType\n            {\n                Detect_Enchantment,\n                Detect_Key,\n                Detect_Creature\n            };\n            /// List all references (filtered by \\a type) detected by \\a ptr. The range\n            /// is determined by the current magnitude of the \"Detect X\" magic effect belonging to \\a type.\n            /// @note This also works for references in containers.\n            virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector<MWWorld::Ptr>& out,\n                                                  DetectionType type) = 0;\n\n            /// Update the value of some globals according to the world state, which may be used by dialogue entries.\n            /// This should be called when initiating a dialogue.\n            virtual void updateDialogueGlobals() = 0;\n\n            /// Moves all stolen items from \\a ptr to the closest evidence chest.\n            virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0;\n\n            virtual void goToJail () = 0;\n\n            /// Spawn a random creature from a levelled list next to the player\n            virtual void spawnRandomCreature(const std::string& creatureList) = 0;\n\n            /// Spawn a blood effect for \\a ptr at \\a worldPosition\n            virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;\n\n            virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) = 0;\n\n            virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster,\n                                      const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id,\n                                      const std::string& sourceName, const bool fromProjectile=false) = 0;\n\n            virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;\n\n            /// @see MWWorld::WeatherManager::isInStorm\n            virtual bool isInStorm() const = 0;\n\n            /// @see MWWorld::WeatherManager::getStormDirection\n            virtual osg::Vec3f getStormDirection() const = 0;\n\n            /// Resets all actors in the current active cells to their original location within that cell.\n            virtual void resetActors() = 0;\n\n            virtual bool isWalkingOnWater (const MWWorld::ConstPtr& actor) const = 0;\n\n            /// Return a vector aiming the actor's weapon towards a target.\n            /// @note The length of the vector is the distance between actor and target.\n            virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat) = 0;\n\n            /// Return the distance between actor's weapon and target's collision box.\n            virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0;\n\n            virtual void addContainerScripts(const MWWorld::Ptr& reference, MWWorld::CellStore* cell) = 0;\n            virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0;\n\n            virtual bool isPlayerInJail() const = 0;\n\n            virtual void rest(double hours) = 0;\n            virtual void rechargeItems(double duration, bool activeOnly) = 0;\n\n            virtual void setPlayerTraveling(bool traveling) = 0;\n            virtual bool isPlayerTraveling() const = 0;\n\n            virtual void rotateWorldObject (const MWWorld::Ptr& ptr, osg::Quat rotate) = 0;\n\n            /// Return terrain height at \\a worldPos position.\n            virtual float getTerrainHeightAt(const osg::Vec3f& worldPos) const = 0;\n\n            /// Return physical or rendering half extents of the given actor.\n            virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering=false) const = 0;\n\n            /// Export scene graph to a file and return the filename.\n            /// \\param ptr object to export scene graph for (if empty, export entire scene graph)\n            virtual std::string exportSceneGraph(const MWWorld::Ptr& ptr) = 0;\n\n            /// Preload VFX associated with this effect list\n            virtual void preloadEffects(const ESM::EffectList* effectList) = 0;\n\n            virtual DetourNavigator::Navigator* getNavigator() const = 0;\n\n            virtual void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,\n                    const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const = 0;\n\n            virtual void removeActorPath(const MWWorld::ConstPtr& actor) const = 0;\n\n            virtual void setNavMeshNumberToRender(const std::size_t value) = 0;\n\n            /// Return physical half extents of the given actor to be used in pathfinding\n            virtual osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const = 0;\n\n            virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;\n\n            virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0;\n\n            virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;\n\n            virtual std::vector<MWWorld::Ptr> getAll(const std::string& id) = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/activator.cpp",
    "content": "#include \"activator.hpp\"\n\n#include <components/esm/loadacti.hpp>\n#include <components/misc/rng.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/action.hpp\"\n#include \"../mwworld/failedaction.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n\n#include \"../mwphysics/physicssystem.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n#include \"../mwrender/vismask.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n\n\nnamespace MWClass\n{\n\n    void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty())\n        {\n            renderingInterface.getObjects().insertModel(ptr, model, true);\n            ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static);\n        }\n    }\n\n    void Activator::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        if(!model.empty())\n            physics.addObject(ptr, model);\n    }\n\n    std::string Activator::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    bool Activator::isActivator() const\n    {\n        return true;\n    }\n\n    bool Activator::useAnim() const\n    {\n        return true;\n    }\n\n    std::string Activator::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();\n\n        return ref->mBase->mName;\n    }\n\n    std::string Activator::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Activator> *ref =\n            ptr.get<ESM::Activator>();\n\n        return ref->mBase->mScript;\n    }\n\n    void Activator::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Activator);\n\n        registerClass (typeid (ESM::Activator).name(), instance);\n    }\n\n    bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const\n    {\n        return !getName(ptr).empty();\n    }\n\n    bool Activator::allowTelekinesis(const MWWorld::ConstPtr &ptr) const {\n        return false;\n    }\n\n    MWGui::ToolTipInfo Activator::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n\n        std::string text;\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp())\n        {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n        info.text = text;\n\n        return info;\n    }\n\n    std::shared_ptr<MWWorld::Action> Activator::activate(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor) const\n    {\n        if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfActivator\");\n\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(\"#{sWerewolfRefusal}\"));\n            if(sound) action->setSound(sound->mId);\n\n            return action;\n        }\n        return std::shared_ptr<MWWorld::Action>(new MWWorld::NullAction);\n    }\n\n\n    MWWorld::Ptr Activator::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const\n    {\n        const std::string model = getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); \n        std::string creatureId;\n\n        for (const ESM::Creature &iter : store.get<ESM::Creature>())\n        {\n            if (!iter.mModel.empty() && Misc::StringUtils::ciEqual(model, \"meshes\\\\\" + iter.mModel))\n            {\n                creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId;\n                break;\n            }\n        }\n\n        int type = getSndGenTypeFromName(name);\n\n        std::vector<const ESM::SoundGenerator*> fallbacksounds;\n        if (!creatureId.empty())\n        {\n            std::vector<const ESM::SoundGenerator*> sounds;\n            for (auto sound = store.get<ESM::SoundGenerator>().begin(); sound != store.get<ESM::SoundGenerator>().end(); ++sound)\n            {\n                if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature)))\n                    sounds.push_back(&*sound);\n                if (type == sound->mType && sound->mCreature.empty())\n                    fallbacksounds.push_back(&*sound);\n            }\n\n            if (!sounds.empty())\n                return sounds[Misc::Rng::rollDice(sounds.size())]->mSound;\n            if (!fallbacksounds.empty())\n                return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound;\n        }\n        else\n        {\n            // The activator doesn't have a corresponding creature ID, but we can try to use the defaults\n            for (auto sound = store.get<ESM::SoundGenerator>().begin(); sound != store.get<ESM::SoundGenerator>().end(); ++sound)\n                if (type == sound->mType && sound->mCreature.empty())\n                    fallbacksounds.push_back(&*sound);\n\n            if (!fallbacksounds.empty())\n                return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound;\n        }\n\n        return std::string();\n    }\n\n    int Activator::getSndGenTypeFromName(const std::string &name)\n    {\n        if (name == \"left\")\n            return ESM::SoundGenerator::LeftFoot;\n        if (name == \"right\")\n            return ESM::SoundGenerator::RightFoot;\n        if (name == \"swimleft\")\n            return ESM::SoundGenerator::SwimLeft;\n        if (name == \"swimright\")\n            return ESM::SoundGenerator::SwimRight;\n        if (name == \"moan\")\n            return ESM::SoundGenerator::Moan;\n        if (name == \"roar\")\n            return ESM::SoundGenerator::Roar;\n        if (name == \"scream\")\n            return ESM::SoundGenerator::Scream;\n        if (name == \"land\")\n            return ESM::SoundGenerator::Land;\n\n        throw std::runtime_error(std::string(\"Unexpected soundgen type: \")+name);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/activator.hpp",
    "content": "#ifndef GAME_MWCLASS_ACTIVATOR_H\n#define GAME_MWCLASS_ACTIVATOR_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Activator : public MWWorld::Class\n    {\n\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n            static int getSndGenTypeFromName(const std::string &name);\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            bool hasToolTip (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const override;\n            ///< Return whether this class of object can be activated with telekinesis\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            static void registerSelf();\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            bool useAnim() const override;\n            ///< Whether or not to use animated variant of model (default false)\n\n            bool isActivator() const override;\n\n            std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/actor.cpp",
    "content": "#include \"actor.hpp\"\n\n#include <components/esm/loadmgef.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n#include \"../mwmechanics/magiceffects.hpp\"\n\n#include \"../mwphysics/physicssystem.hpp\"\n\n#include \"../mwworld/inventorystore.hpp\"\n\nnamespace MWClass\n{\n    Actor::Actor() {}\n\n    Actor::~Actor() {}\n\n    void Actor::adjustPosition(const MWWorld::Ptr& ptr, bool force) const\n    {\n        MWBase::Environment::get().getWorld()->adjustPosition(ptr, force);\n    }\n\n    void Actor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        if (!model.empty())\n        {\n            physics.addActor(ptr, model);\n            if (getCreatureStats(ptr).isDead() && getCreatureStats(ptr).isDeathAnimationFinished())\n                MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false);\n        }\n    }\n\n    bool Actor::useAnim() const\n    {\n        return true;\n    }\n\n    void Actor::block(const MWWorld::Ptr &ptr) const\n    {\n        const MWWorld::InventoryStore& inv = getInventoryStore(ptr);\n        MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n        if (shield == inv.end())\n            return;\n\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        switch (shield->getClass().getEquipmentSkill(*shield))\n        {\n            case ESM::Skill::LightArmor:\n                sndMgr->playSound3D(ptr, \"Light Armor Hit\", 1.0f, 1.0f);\n                break;\n            case ESM::Skill::MediumArmor:\n                sndMgr->playSound3D(ptr, \"Medium Armor Hit\", 1.0f, 1.0f);\n                break;\n            case ESM::Skill::HeavyArmor:\n                sndMgr->playSound3D(ptr, \"Heavy Armor Hit\", 1.0f, 1.0f);\n                break;\n            default:\n                return;\n        }\n    }\n\n    osg::Vec3f Actor::getRotationVector(const MWWorld::Ptr& ptr) const\n    {\n        MWMechanics::Movement &movement = getMovementSettings(ptr);\n        osg::Vec3f vec(movement.mRotation[0], movement.mRotation[1], movement.mRotation[2]);\n        movement.mRotation[0] = 0.0f;\n        movement.mRotation[1] = 0.0f;\n        movement.mRotation[2] = 0.0f;\n        return vec;\n    }\n\n    float Actor::getEncumbrance(const MWWorld::Ptr& ptr) const\n    {\n        float weight = getContainerStore(ptr).getWeight();\n        const MWMechanics::MagicEffects& effects = getCreatureStats(ptr).getMagicEffects();\n        weight -= effects.get(MWMechanics::EffectKey(ESM::MagicEffect::Feather)).getMagnitude();\n        if (ptr != MWMechanics::getPlayer() || !MWBase::Environment::get().getWorld()->getGodModeState())\n            weight += effects.get(MWMechanics::EffectKey(ESM::MagicEffect::Burden)).getMagnitude();\n        return (weight < 0) ? 0.0f : weight;\n    }\n\n    bool Actor::allowTelekinesis(const MWWorld::ConstPtr &ptr) const {\n        return false;\n    }\n\n    bool Actor::isActor() const\n    {\n        return true;\n    }\n\n    float Actor::getCurrentSpeed(const MWWorld::Ptr& ptr) const\n    {\n        const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);\n        float moveSpeed = this->getMaxSpeed(ptr) * movementSettings.mSpeedFactor;\n        if (movementSettings.mIsStrafing)\n            moveSpeed *= 0.75f;\n        return moveSpeed;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/actor.hpp",
    "content": "#ifndef GAME_MWCLASS_MOBILE_H\n#define GAME_MWCLASS_MOBILE_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace ESM\n{\n    struct GameSetting;\n}\n\nnamespace MWClass\n{\n    /// \\brief Class holding functionality common to Creature and NPC\n    class Actor : public MWWorld::Class\n    {\n    protected:\n\n        Actor();\n\n    public:\n        virtual ~Actor();\n\n        void adjustPosition(const MWWorld::Ptr& ptr, bool force) const override;\n        ///< Adjust position to stand on ground. Must be called post model load\n        /// @param force do this even if the ptr is flying\n\n        void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n        bool useAnim() const override;\n\n        void block(const MWWorld::Ptr &ptr) const override;\n\n        osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const override;\n        ///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.\n\n        float getEncumbrance(const MWWorld::Ptr& ptr) const override;\n        ///< Returns total weight of objects inside this object (including modifications from magic\n        /// effects). Throws an exception, if the object can't hold other objects.\n\n        bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const override;\n        ///< Return whether this class of object can be activated with telekinesis\n\n        bool isActor() const override;\n\n        /// Return current movement speed.\n        float getCurrentSpeed(const MWWorld::Ptr& ptr) const override;\n        \n        // not implemented\n        Actor(const Actor&);\n        Actor& operator= (const Actor&);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/apparatus.cpp",
    "content": "#include \"apparatus.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadappa.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionalchemy.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\nnamespace MWClass\n{\n\n    void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Apparatus::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision || Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Apparatus::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Apparatus::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Apparatus::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();\n\n        return ref->mBase->mScript;\n    }\n\n    int Apparatus::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Apparatus::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Apparatus);\n\n        registerClass (typeid (ESM::Apparatus).name(), instance);\n    }\n\n    std::string Apparatus::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Apparatus Up\");\n    }\n\n    std::string Apparatus::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Apparatus Down\");\n    }\n\n    std::string Apparatus::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n        text += \"\\n#{sQuality}: \" + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n        info.text = text;\n\n        return info;\n    }\n\n    std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionAlchemy(force));\n    }\n\n    MWWorld::Ptr Apparatus::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    bool Apparatus::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Apparatus) != 0;\n    }\n\n    float Apparatus::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/apparatus.hpp",
    "content": "#ifndef GAME_MWCLASS_APPARATUS_H\n#define GAME_MWCLASS_APPARATUS_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Apparatus : public MWWorld::Class\n    {\n\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/armor.cpp",
    "content": "#include \"armor.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/Worldstate.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadskil.hpp>\n#include <components/esm/loadgmst.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\nnamespace MWClass\n{\n\n    void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Armor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision || Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Armor::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Armor::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    bool Armor::hasItemHealth (const MWWorld::ConstPtr& ptr) const\n    {\n        return true;\n    }\n\n    int Armor::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        return ref->mBase->mData.mHealth;\n    }\n\n    std::string Armor::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        return ref->mBase->mScript;\n    }\n\n    std::pair<std::vector<int>, bool> Armor::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        std::vector<int> slots_;\n\n        const int size = 11;\n\n        static const int sMapping[size][2] =\n        {\n            { ESM::Armor::Helmet, MWWorld::InventoryStore::Slot_Helmet },\n            { ESM::Armor::Cuirass, MWWorld::InventoryStore::Slot_Cuirass },\n            { ESM::Armor::LPauldron, MWWorld::InventoryStore::Slot_LeftPauldron },\n            { ESM::Armor::RPauldron, MWWorld::InventoryStore::Slot_RightPauldron },\n            { ESM::Armor::Greaves, MWWorld::InventoryStore::Slot_Greaves },\n            { ESM::Armor::Boots, MWWorld::InventoryStore::Slot_Boots },\n            { ESM::Armor::LGauntlet, MWWorld::InventoryStore::Slot_LeftGauntlet },\n            { ESM::Armor::RGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet },\n            { ESM::Armor::Shield, MWWorld::InventoryStore::Slot_CarriedLeft },\n            { ESM::Armor::LBracer, MWWorld::InventoryStore::Slot_LeftGauntlet },\n            { ESM::Armor::RBracer, MWWorld::InventoryStore::Slot_RightGauntlet }\n        };\n\n        for (int i=0; i<size; ++i)\n            if (sMapping[i][0]==ref->mBase->mData.mType)\n            {\n                slots_.push_back (int (sMapping[i][1]));\n                break;\n            }\n\n        return std::make_pair (slots_, false);\n    }\n\n    int Armor::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        std::string typeGmst;\n\n        switch (ref->mBase->mData.mType)\n        {\n            case ESM::Armor::Helmet: typeGmst = \"iHelmWeight\"; break;\n            case ESM::Armor::Cuirass: typeGmst = \"iCuirassWeight\"; break;\n            case ESM::Armor::LPauldron:\n            case ESM::Armor::RPauldron: typeGmst = \"iPauldronWeight\"; break;\n            case ESM::Armor::Greaves: typeGmst = \"iGreavesWeight\"; break;\n            case ESM::Armor::Boots: typeGmst = \"iBootsWeight\"; break;\n            case ESM::Armor::LGauntlet:\n            case ESM::Armor::RGauntlet: typeGmst = \"iGauntletWeight\"; break;\n            case ESM::Armor::Shield: typeGmst = \"iShieldWeight\"; break;\n            case ESM::Armor::LBracer:\n            case ESM::Armor::RBracer: typeGmst = \"iGauntletWeight\"; break;\n        }\n\n        if (typeGmst.empty())\n            return -1;\n\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        float iWeight = floor(gmst.find(typeGmst)->mValue.getFloat());\n\n        float epsilon = 0.0005f;\n\n        if (ref->mBase->mData.mWeight <= iWeight * gmst.find (\"fLightMaxMod\")->mValue.getFloat() + epsilon)\n            return ESM::Skill::LightArmor;\n\n        if (ref->mBase->mData.mWeight <= iWeight * gmst.find (\"fMedMaxMod\")->mValue.getFloat() + epsilon)\n            return ESM::Skill::MediumArmor;\n\n        else\n            return ESM::Skill::HeavyArmor;\n    }\n\n    int Armor::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Armor::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Armor);\n\n        registerClass (typeid (ESM::Armor).name(), instance);\n    }\n\n    std::string Armor::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        int es = getEquipmentSkill(ptr);\n        if (es == ESM::Skill::LightArmor)\n            return std::string(\"Item Armor Light Up\");\n        else if (es == ESM::Skill::MediumArmor)\n            return std::string(\"Item Armor Medium Up\");\n        else\n            return std::string(\"Item Armor Heavy Up\");\n    }\n\n    std::string Armor::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        int es = getEquipmentSkill(ptr);\n        if (es == ESM::Skill::LightArmor)\n            return std::string(\"Item Armor Light Down\");\n        else if (es == ESM::Skill::MediumArmor)\n            return std::string(\"Item Armor Medium Down\");\n        else\n            return std::string(\"Item Armor Heavy Down\");\n    }\n\n    std::string Armor::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        // get armor type string (light/medium/heavy)\n        std::string typeText;\n        if (ref->mBase->mData.mWeight == 0)\n            typeText = \"\";\n        else\n        {\n            int armorType = getEquipmentSkill(ptr);       \n            if (armorType == ESM::Skill::LightArmor)\n                typeText = \"#{sLight}\";\n            else if (armorType == ESM::Skill::MediumArmor)\n                typeText = \"#{sMedium}\";\n            else\n                typeText = \"#{sHeavy}\";\n        }\n\n        text += \"\\n#{sArmorRating}: \" + MWGui::ToolTips::toString(static_cast<int>(getEffectiveArmorRating(ptr,\n            MWMechanics::getPlayer())));\n\n        int remainingHealth = getItemHealth(ptr);\n        text += \"\\n#{sCondition}: \" + MWGui::ToolTips::toString(remainingHealth) + \"/\"\n                + MWGui::ToolTips::toString(ref->mBase->mData.mHealth);\n\n        if (typeText != \"\")\n            text += \"\\n#{sWeight}: \" + MWGui::ToolTips::toString(ref->mBase->mData.mWeight) + \" (\" + typeText + \")\";\n\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.enchant = ref->mBase->mEnchant;\n        if (!info.enchant.empty())\n            info.remainingEnchantCharge = static_cast<int>(ptr.getCellRef().getEnchantmentCharge());\n\n        info.text = text;\n\n        return info;\n    }\n\n    std::string Armor::getEnchantment (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        return ref->mBase->mEnchant;\n    }\n\n    std::string Armor::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        ESM::Armor newItem = *ref->mBase;\n        newItem.mId=\"\";\n        newItem.mName=newName;\n        newItem.mData.mEnchant=enchCharge;\n        newItem.mEnchant=enchId;\n\n        /*\n            Start of tes3mp addition\n\n            Send the newly created record to the server and expect it to be\n            returned with a server-set id\n        */\n        mwmp::Main::get().getNetworking()->getWorldstate()->sendArmorRecord(&newItem, ref->mBase->mId);\n        /*\n            End of tes3mp addition\n        */\n\n        const ESM::Armor *record = MWBase::Environment::get().getWorld()->createRecord (newItem);\n        return record->mId;\n    }\n\n    float Armor::getEffectiveArmorRating(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &actor) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        int armorSkillType = getEquipmentSkill(ptr);\n        float armorSkill = actor.getClass().getSkill(actor, armorSkillType);\n\n        const MWBase::World *world = MWBase::Environment::get().getWorld();\n        int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find(\"iBaseArmorSkill\")->mValue.getInteger();\n\n        if(ref->mBase->mData.mWeight == 0)\n            return ref->mBase->mData.mArmor;\n        else\n            return ref->mBase->mData.mArmor * armorSkill / static_cast<float>(iBaseArmorSkill);\n    }\n\n    std::pair<int, std::string> Armor::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const\n    {\n        const MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc);\n\n        if (getItemHealth(ptr) == 0)\n            return std::make_pair(0, \"#{sInventoryMessage1}\");\n\n        // slots that this item can be equipped in\n        std::pair<std::vector<int>, bool> slots_ = getEquipmentSlots(ptr);\n\n        if (slots_.first.empty())\n            return std::make_pair(0, \"\");\n\n        if (npc.getClass().isNpc())\n        {\n            std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace;\n\n            // Beast races cannot equip shoes / boots, or full helms (head part vs hair part)\n            const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);\n            if(race->mData.mFlags & ESM::Race::Beast)\n            {\n                std::vector<ESM::PartReference> parts = ptr.get<ESM::Armor>()->mBase->mParts.mParts;\n\n                for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr)\n                {\n                    if((*itr).mPart == ESM::PRT_Head)\n                        return std::make_pair(0, \"#{sNotifyMessage13}\");\n                    if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot)\n                        return std::make_pair(0, \"#{sNotifyMessage14}\");\n                }\n            }\n        }\n\n        for (std::vector<int>::const_iterator slot=slots_.first.begin();\n            slot!=slots_.first.end(); ++slot)\n        {\n            // If equipping a shield, check if there's a twohanded weapon conflicting with it\n            if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft)\n            {\n                MWWorld::ConstContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n                if(weapon != invStore.end() && weapon->getTypeName() == typeid(ESM::Weapon).name())\n                {\n                    const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();\n                    if (MWMechanics::getWeaponType(ref->mBase->mData.mType)->mFlags & ESM::WeaponType::TwoHanded)\n                        return std::make_pair(3,\"\");\n                }\n\n                return std::make_pair(1,\"\");\n            }\n        }\n        return std::make_pair(1,\"\");\n    }\n\n    std::shared_ptr<MWWorld::Action> Armor::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));\n\n        action->setSound(getUpSoundId(ptr));\n\n        return action;\n    }\n\n    MWWorld::Ptr Armor::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    int Armor::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n\n        return ref->mBase->mData.mEnchant;\n    }\n\n    bool Armor::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Armor)\n                || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty());\n    }\n\n    float Armor::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/armor.hpp",
    "content": "#ifndef GAME_MWCLASS_ARMOR_H\n#define GAME_MWCLASS_ARMOR_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Armor : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            bool hasItemHealth (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return Item health data available?\n\n            int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return item max health or throw an exception, if class does not have item health\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return first: Return IDs of the slot this object can be equipped in; second: can object\n            /// stay stacked when equipped?\n\n            int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const override;\n            /// Return the index of the skill this item corresponds to when equipped or -1, if there is\n            /// no such skill.\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::string getEnchantment (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return the enchantment ID if the object is enchanted, otherwise an empty string\n\n            std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const override;\n            ///< Creates a new record using \\a ptr as template, with the given name and the given enchantment applied to it.\n\n            std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const override;\n            ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \\n\n            ///  Second item in the pair specifies the error message\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n\n            /// Get the effective armor rating, factoring in the actor's skills, for the given armor.\n            float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/bodypart.cpp",
    "content": "#include \"bodypart.hpp\"\n\n#include \"../mwrender/renderinginterface.hpp\"\n#include \"../mwrender/objects.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n\nnamespace MWClass\n{\n\n    MWWorld::Ptr BodyPart::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::BodyPart> *ref = ptr.get<ESM::BodyPart>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    void BodyPart::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string &model, MWRender::RenderingInterface &renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void BodyPart::insertObject(const MWWorld::Ptr &ptr, const std::string &model, MWPhysics::PhysicsSystem &physics) const\n    {\n    }\n\n    std::string BodyPart::getName(const MWWorld::ConstPtr &ptr) const\n    {\n        return std::string();\n    }\n\n    bool BodyPart::hasToolTip(const MWWorld::ConstPtr& ptr) const\n    {\n        return false;\n    }\n\n    void BodyPart::registerSelf()\n    {\n        std::shared_ptr<MWWorld::Class> instance (new BodyPart);\n\n        registerClass (typeid (ESM::BodyPart).name(), instance);\n    }\n\n    std::string BodyPart::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::BodyPart> *ref = ptr.get<ESM::BodyPart>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/bodypart.hpp",
    "content": "#ifndef GAME_MWCLASS_BODYPART_H\n#define GAME_MWCLASS_BODYPART_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n\n    class BodyPart : public MWWorld::Class\n    {\n        MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n    public:\n\n        void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n        ///< Add reference into a cell for rendering\n\n        void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n        std::string getName (const MWWorld::ConstPtr& ptr) const override;\n        ///< \\return name or ID; can return an empty string.\n\n        bool hasToolTip (const MWWorld::ConstPtr& ptr) const override;\n        ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n        static void registerSelf();\n\n        std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/book.cpp",
    "content": "#include \"book.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadbook.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionread.hpp\"\n#include \"../mwworld/failedaction.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n\nnamespace MWClass\n{\n\n    void Book::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Book::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision || Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Book::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Book::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Book::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfItem\");\n\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(\"#{sWerewolfRefusal}\"));\n            if(sound) action->setSound(sound->mId);\n\n            return action;\n        }\n\n        return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRead(ptr));\n    }\n\n    std::string Book::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        return ref->mBase->mScript;\n    }\n\n    int Book::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Book::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Book);\n\n        registerClass (typeid (ESM::Book).name(), instance);\n    }\n\n    std::string Book::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Book Up\");\n    }\n\n    std::string Book::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Book Down\");\n    }\n\n    std::string Book::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.enchant = ref->mBase->mEnchant;\n\n        info.text = text;\n\n        return info;\n    }\n\n    std::string Book::getEnchantment (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        return ref->mBase->mEnchant;\n    }\n\n    std::string Book::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        ESM::Book newItem = *ref->mBase;\n        newItem.mId=\"\";\n        newItem.mName=newName;\n        newItem.mData.mIsScroll = 1;\n        newItem.mData.mEnchant=enchCharge;\n        newItem.mEnchant=enchId;\n\n        /*\n            Start of tes3mp addition\n\n            Send the newly created record to the server and expect it to be\n            returned with a server-set id\n        */\n        mwmp::Main::get().getNetworking()->getWorldstate()->sendBookRecord(&newItem, ref->mBase->mId);\n        /*\n            End of tes3mp addition\n        */\n\n        const ESM::Book *record = MWBase::Environment::get().getWorld()->createRecord (newItem);\n        return record->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Book::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRead(ptr));\n    }\n\n    MWWorld::Ptr Book::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    int Book::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n\n        return ref->mBase->mData.mEnchant;\n    }\n\n    bool Book::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Books)\n                || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty());\n    }\n\n    float Book::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/book.hpp",
    "content": "#ifndef GAME_MWCLASS_BOOK_H\n#define GAME_MWCLASS_BOOK_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Book : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::string getEnchantment (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return the enchantment ID if the object is enchanted, otherwise an empty string\n\n            std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const override;\n            ///< Creates a new record using \\a ptr as template, with the given name and the given enchantment applied to it.\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const override;\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/classes.cpp",
    "content": "#include \"classes.hpp\"\n\n#include \"activator.hpp\"\n#include \"creature.hpp\"\n#include \"npc.hpp\"\n#include \"weapon.hpp\"\n#include \"armor.hpp\"\n#include \"potion.hpp\"\n#include \"apparatus.hpp\"\n#include \"book.hpp\"\n#include \"clothing.hpp\"\n#include \"container.hpp\"\n#include \"door.hpp\"\n#include \"ingredient.hpp\"\n#include \"creaturelevlist.hpp\"\n#include \"itemlevlist.hpp\"\n#include \"light.hpp\"\n#include \"lockpick.hpp\"\n#include \"misc.hpp\"\n#include \"probe.hpp\"\n#include \"repair.hpp\"\n#include \"static.hpp\"\n#include \"bodypart.hpp\"\n\nnamespace MWClass\n{\n    void registerClasses()\n    {\n        Activator::registerSelf();\n        Creature::registerSelf();\n        Npc::registerSelf();\n        Weapon::registerSelf();\n        Armor::registerSelf();\n        Potion::registerSelf();\n        Apparatus::registerSelf();\n        Book::registerSelf();\n        Clothing::registerSelf();\n        Container::registerSelf();\n        Door::registerSelf();\n        Ingredient::registerSelf();\n        CreatureLevList::registerSelf();\n        ItemLevList::registerSelf();\n        Light::registerSelf();\n        Lockpick::registerSelf();\n        Miscellaneous::registerSelf();\n        Probe::registerSelf();\n        Repair::registerSelf();\n        Static::registerSelf();\n        BodyPart::registerSelf();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/classes.hpp",
    "content": "#ifndef GAME_MWCLASS_CLASSES_H\n#define GAME_MWCLASS_CLASSES_H\n\nnamespace MWClass\n{\n    void registerClasses();\n    ///< register all known classes\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/clothing.cpp",
    "content": "#include \"clothing.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadclot.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\nnamespace MWClass\n{\n\n    void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Clothing::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision || Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Clothing::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Clothing::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr,\n            const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Clothing::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        return ref->mBase->mScript;\n    }\n\n    std::pair<std::vector<int>, bool> Clothing::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        std::vector<int> slots_;\n\n        if (ref->mBase->mData.mType==ESM::Clothing::Ring)\n        {\n            slots_.push_back (int (MWWorld::InventoryStore::Slot_LeftRing));\n            slots_.push_back (int (MWWorld::InventoryStore::Slot_RightRing));\n        }\n        else\n        {\n            const int size = 9;\n\n            static const int sMapping[size][2] =\n            {\n                { ESM::Clothing::Shirt, MWWorld::InventoryStore::Slot_Shirt },\n                { ESM::Clothing::Belt, MWWorld::InventoryStore::Slot_Belt },\n                { ESM::Clothing::Robe, MWWorld::InventoryStore::Slot_Robe },\n                { ESM::Clothing::Pants, MWWorld::InventoryStore::Slot_Pants },\n                { ESM::Clothing::Shoes, MWWorld::InventoryStore::Slot_Boots },\n                { ESM::Clothing::LGlove, MWWorld::InventoryStore::Slot_LeftGauntlet },\n                { ESM::Clothing::RGlove, MWWorld::InventoryStore::Slot_RightGauntlet },\n                { ESM::Clothing::Skirt, MWWorld::InventoryStore::Slot_Skirt },\n                { ESM::Clothing::Amulet, MWWorld::InventoryStore::Slot_Amulet }\n            };\n\n            for (int i=0; i<size; ++i)\n                if (sMapping[i][0]==ref->mBase->mData.mType)\n                {\n                    slots_.push_back (int (sMapping[i][1]));\n                    break;\n                }\n        }\n\n        return std::make_pair (slots_, false);\n    }\n\n    int Clothing::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        if (ref->mBase->mData.mType==ESM::Clothing::Shoes)\n            return ESM::Skill::Unarmored;\n\n        return -1;\n    }\n\n    int Clothing::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Clothing::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Clothing);\n\n        registerClass (typeid (ESM::Clothing).name(), instance);\n    }\n\n    std::string Clothing::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        if (ref->mBase->mData.mType == 8)\n        {\n            return std::string(\"Item Ring Up\");\n        }\n        return std::string(\"Item Clothes Up\");\n    }\n\n    std::string Clothing::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        if (ref->mBase->mData.mType == 8)\n        {\n            return std::string(\"Item Ring Down\");\n        }\n        return std::string(\"Item Clothes Down\");\n    }\n\n    std::string Clothing::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.enchant = ref->mBase->mEnchant;\n        if (!info.enchant.empty())\n            info.remainingEnchantCharge = static_cast<int>(ptr.getCellRef().getEnchantmentCharge());\n\n        info.text = text;\n\n        return info;\n    }\n\n    std::string Clothing::getEnchantment (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        return ref->mBase->mEnchant;\n    }\n\n    std::string Clothing::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        ESM::Clothing newItem = *ref->mBase;\n        newItem.mId=\"\";\n        newItem.mName=newName;\n        newItem.mData.mEnchant=enchCharge;\n        newItem.mEnchant=enchId;\n\n        /*\n            Start of tes3mp addition\n\n            Send the newly created record to the server and expect it to be\n            returned with a server-set id\n        */\n        mwmp::Main::get().getNetworking()->getWorldstate()->sendClothingRecord(&newItem, ref->mBase->mId);\n        /*\n            End of tes3mp addition\n        */\n\n        const ESM::Clothing *record = MWBase::Environment::get().getWorld()->createRecord (newItem);\n        return record->mId;\n    }\n\n    std::pair<int, std::string> Clothing::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const\n    {\n        // slots that this item can be equipped in\n        std::pair<std::vector<int>, bool> slots_ = getEquipmentSlots(ptr);\n\n        if (slots_.first.empty())\n            return std::make_pair(0, \"\");\n\n        if (npc.getClass().isNpc())\n        {\n            std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace;\n\n            // Beast races cannot equip shoes / boots, or full helms (head part vs hair part)\n            const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);\n            if(race->mData.mFlags & ESM::Race::Beast)\n            {\n                std::vector<ESM::PartReference> parts = ptr.get<ESM::Clothing>()->mBase->mParts.mParts;\n\n                for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr)\n                {\n                    if((*itr).mPart == ESM::PRT_Head)\n                        return std::make_pair(0, \"#{sNotifyMessage13}\");\n                    if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot)\n                        return std::make_pair(0, \"#{sNotifyMessage15}\");\n                }\n            }\n        }\n\n        return std::make_pair (1, \"\");\n    }\n\n    std::shared_ptr<MWWorld::Action> Clothing::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));\n\n        action->setSound(getUpSoundId(ptr));\n\n        return action;\n    }\n\n    MWWorld::Ptr Clothing::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    int Clothing::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n\n        return ref->mBase->mData.mEnchant;\n    }\n\n    bool Clothing::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Clothing)\n                || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty());\n    }\n\n    float Clothing::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/clothing.hpp",
    "content": "#ifndef GAME_MWCLASS_CLOTHING_H\n#define GAME_MWCLASS_CLOTHING_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Clothing : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return first: Return IDs of the slot this object can be equipped in; second: can object\n            /// stay stacked when equipped?\n\n            int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const override;\n            /// Return the index of the skill this item corresponds to when equipped or -1, if there is\n            /// no such skill.\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::string getEnchantment (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return the enchantment ID if the object is enchanted, otherwise an empty string\n\n            std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const override;\n            ///< Creates a new record using \\a ptr as template, with the given name and the given enchantment applied to it.\n\n            std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const override;\n            ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.\n            ///  Second item in the pair specifies the error message\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const override;\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/container.cpp",
    "content": "#include \"container.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadcont.hpp>\n#include <components/esm/containerstate.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/failedaction.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/customdata.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/actionharvest.hpp\"\n#include \"../mwworld/actionopen.hpp\"\n#include \"../mwworld/actiontrap.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n\nnamespace MWClass\n{\n    ContainerCustomData::ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell)\n    {\n        unsigned int seed = Misc::Rng::rollDice(std::numeric_limits<int>::max());\n        // setting ownership not needed, since taking items from a container inherits the\n        // container's owner automatically\n        mStore.fillNonRandom(container.mInventory, \"\", seed);\n    }\n\n    ContainerCustomData::ContainerCustomData(const ESM::InventoryState& inventory)\n    {\n        mStore.readState(inventory);\n    }\n\n    ContainerCustomData& ContainerCustomData::asContainerCustomData()\n    {\n        return *this;\n    }\n    const ContainerCustomData& ContainerCustomData::asContainerCustomData() const\n    {\n        return *this;\n    }\n\n    Container::Container()\n    {\n        mHarvestEnabled = Settings::Manager::getBool(\"graphic herbalism\", \"Game\");\n    }\n\n    void Container::ensureCustomData (const MWWorld::Ptr& ptr) const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();\n\n            // store\n            ptr.getRefData().setCustomData (std::make_unique<ContainerCustomData>(*ref->mBase, ptr.getCell()));\n\n            MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell());\n        }\n    }\n\n    bool Container::canBeHarvested(const MWWorld::ConstPtr& ptr) const\n    {\n        if (!mHarvestEnabled)\n            return false;\n        const MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);\n        if (animation == nullptr)\n            return false;\n\n        return animation->canBeHarvested();\n    }\n\n    void Container::respawn(const MWWorld::Ptr &ptr) const\n    {\n        MWWorld::LiveCellRef<ESM::Container> *ref =\n            ptr.get<ESM::Container>();\n        if (ref->mBase->mFlags & ESM::Container::Respawn)\n        {\n            // Container was not touched, there is no need to modify its content.\n            if (ptr.getRefData().getCustomData() == nullptr)\n                return;\n\n            MWBase::Environment::get().getWorld()->removeContainerScripts(ptr);\n            ptr.getRefData().setCustomData(nullptr);\n        }\n    }\n\n    void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model, true);\n        }\n    }\n\n    void Container::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        if(!model.empty())\n            physics.addObject(ptr, model);\n    }\n\n    std::string Container::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    bool Container::useAnim() const\n    {\n        return true;\n    }\n\n    std::shared_ptr<MWWorld::Action> Container::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))\n            return std::shared_ptr<MWWorld::Action> (new MWWorld::NullAction ());\n\n        if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfContainer\");\n\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(\"#{sWerewolfRefusal}\"));\n            if(sound) action->setSound(sound->mId);\n\n            return action;\n        }\n\n        const std::string lockedSound = \"LockedChest\";\n        const std::string trapActivationSound = \"Disarm Trap Fail\";\n\n        MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        MWWorld::InventoryStore& invStore = player.getClass().getInventoryStore(player);\n\n        bool isLocked = ptr.getCellRef().getLockLevel() > 0;\n        bool isTrapped = !ptr.getCellRef().getTrap().empty();\n        bool hasKey = false;\n        std::string keyName;\n\n        const std::string keyId = ptr.getCellRef().getKey();\n        if (!keyId.empty())\n        {\n            MWWorld::Ptr keyPtr = invStore.search(keyId);\n            if (!keyPtr.isEmpty())\n            {\n                hasKey = true;\n                keyName = keyPtr.getClass().getName(keyPtr);\n            }\n        }\n\n        if (isLocked && hasKey)\n        {\n            MWBase::Environment::get().getWindowManager ()->messageBox (keyName + \" #{sKeyUsed}\");\n\n            /*\n                Start of tes3mp change (major)\n\n                Disable unilateral unlocking on this client and expect the server's reply to our\n                packet to do it instead\n            */\n            //ptr.getCellRef().unlock();\n            /*\n                End of tes3mp change (major)\n            */\n\n            // using a key disarms the trap\n            if(isTrapped)\n            {\n                /*\n                    Start of tes3mp change (major)\n\n                    Disable unilateral trap disarming on this client and expect the server's reply to our\n                    packet to do it instead\n                */\n                //ptr.getCellRef().setTrap(\"\");\n                //MWBase::Environment::get().getSoundManager()->playSound3D(ptr, \"Disarm Trap\", 1.0f, 1.0f);\n                /*\n                    End of tes3mp change (major)\n                */\n\n                isTrapped = false;\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_OBJECT_TRAP packet every time a trap is disarmed\n                */\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectTrap(ptr, ptr.getRefData().getPosition(), true);\n                objectList->sendObjectTrap();\n                /*\n                    End of tes3mp addition\n                */\n            }\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_OBJECT_LOCK packet every time a container is unlocked here\n            */\n            if (isLocked)\n            {\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectLock(ptr, 0);\n                objectList->sendObjectLock();\n            }\n            /*\n                End of tes3mp addition\n            */\n        }\n\n\n        if (!isLocked || hasKey)\n        {\n            if(!isTrapped)\n            {\n                if (canBeHarvested(ptr))\n                {\n                    std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionHarvest(ptr));\n                    return action;\n                }\n\n                std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionOpen(ptr));\n                return action;\n            }\n            else\n            {\n                // Activate trap\n                std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTrap(ptr.getCellRef().getTrap(), ptr));\n                action->setSound(trapActivationSound);\n                return action;\n            }\n        }\n        else\n        {\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(std::string(), ptr));\n            action->setSound(lockedSound);\n            return action;\n        }\n    }\n\n    std::string Container::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) const\n    {\n        ensureCustomData (ptr);\n        auto& data = ptr.getRefData().getCustomData()->asContainerCustomData();\n        data.mStore.mPtr = ptr;\n        return data.mStore;\n    }\n\n    std::string Container::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();\n\n        return ref->mBase->mScript;\n    }\n\n    void Container::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Container);\n\n        registerClass (typeid (ESM::Container).name(), instance);\n    }\n\n    bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const\n    {\n        if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())\n            return !canBeHarvested(ptr) || data->asContainerCustomData().mStore.hasVisibleItems();\n        return true;\n    }\n\n    MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));\n\n        std::string text;\n        int lockLevel = ptr.getCellRef().getLockLevel();\n        if (lockLevel > 0 && lockLevel != ESM::UnbreakableLock)\n            text += \"\\n#{sLockLevel}: \" + MWGui::ToolTips::toString(lockLevel);\n        else if (lockLevel < 0)\n            text += \"\\n#{sUnlocked}\";\n        if (ptr.getCellRef().getTrap() != \"\")\n            text += \"\\n#{sTrapped}\";\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp())\n        {   text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n            if (Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), \"stolen_goods\"))\n                text += \"\\nYou can not use evidence chests\";\n        }\n\n        info.text = text;\n\n        return info;\n    }\n\n    float Container::getCapacity (const MWWorld::Ptr& ptr) const\n    {\n        MWWorld::LiveCellRef<ESM::Container> *ref =\n            ptr.get<ESM::Container>();\n\n        return ref->mBase->mWeight;\n    }\n\n    float Container::getEncumbrance (const MWWorld::Ptr& ptr) const\n    {\n        return getContainerStore (ptr).getWeight();\n    }\n\n    bool Container::canLock(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();\n        return !(ref->mBase->mFlags & ESM::Container::Organic);\n    }\n\n    void Container::modifyBaseInventory(const std::string& containerId, const std::string& itemId, int amount) const\n    {\n        MWMechanics::modifyBaseInventory<ESM::Container>(containerId, itemId, amount);\n    }\n\n    MWWorld::Ptr Container::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const\n    {\n        if (!state.mHasCustomState)\n            return;\n\n        const ESM::ContainerState& containerState = state.asContainerState();\n        ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(containerState.mInventory));\n    }\n\n    void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            state.mHasCustomState = false;\n            return;\n        }\n\n        const ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();\n        if (!customData.mStore.isResolved())\n        {\n            state.mHasCustomState = false;\n            return;\n        }\n\n        ESM::ContainerState& containerState = state.asContainerState();\n        customData.mStore.writeState (containerState.mInventory);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/container.hpp",
    "content": "#ifndef GAME_MWCLASS_CONTAINER_H\n#define GAME_MWCLASS_CONTAINER_H\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/customdata.hpp\"\n\nnamespace ESM\n{\n    struct Container;\n    struct InventoryState;\n}\n\nnamespace MWClass\n{\n    class ContainerCustomData : public MWWorld::TypedCustomData<ContainerCustomData>\n    {\n        MWWorld::ContainerStore mStore;\n    public:\n        ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell);\n        ContainerCustomData(const ESM::InventoryState& inventory);\n\n        ContainerCustomData& asContainerCustomData() override;\n        const ContainerCustomData& asContainerCustomData() const override;\n\n        friend class Container;\n    };\n\n    class Container : public MWWorld::Class\n    {\n            bool mHarvestEnabled;\n\n            void ensureCustomData (const MWWorld::Ptr& ptr) const;\n\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n            bool canBeHarvested(const MWWorld::ConstPtr& ptr) const;\n\n        public:\n            Container();\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            bool hasToolTip (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const override;\n            ///< Return container store\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a class has a container store\n            */\n            virtual bool hasContainerStore(const MWWorld::Ptr &ptr) const { return true; }\n            /*\n                End of tes3mp addition\n            */\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            float getCapacity (const MWWorld::Ptr& ptr) const override;\n            ///< Return total weight that fits into the object. Throws an exception, if the object can't\n            /// hold other objects.\n\n            float getEncumbrance (const MWWorld::Ptr& ptr) const override;\n            ///< Returns total weight of objects inside this object (including modifications from magic\n            /// effects). Throws an exception, if the object can't hold other objects.\n\n            bool canLock(const MWWorld::ConstPtr &ptr) const override;\n\n            void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)\n                const override;\n            ///< Read additional state from \\a state into \\a ptr.\n\n            void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const override;\n            ///< Write additional state from \\a ptr into \\a state.\n\n            static void registerSelf();\n\n            void respawn (const MWWorld::Ptr& ptr) const override;\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            bool useAnim() const override;\n\n            void modifyBaseInventory(const std::string& containerId, const std::string& itemId, int amount) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/creature.cpp",
    "content": "#include \"creature.hpp\"\n\n#include <components/misc/rng.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/creaturestate.hpp>\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/magiceffects.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n#include \"../mwmechanics/disease.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n#include \"../mwmechanics/difficultyscaling.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actiontalk.hpp\"\n#include \"../mwworld/actionopen.hpp\"\n#include \"../mwworld/failedaction.hpp\"\n#include \"../mwworld/customdata.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/localscripts.hpp\"\n\n#include \"../mwrender/renderinginterface.hpp\"\n#include \"../mwrender/objects.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/combat.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace\n{\n    bool isFlagBitSet(const MWWorld::ConstPtr &ptr, ESM::Creature::Flags bitMask)\n    {\n        return (ptr.get<ESM::Creature>()->mBase->mFlags & bitMask) != 0;\n    }\n}\n\nnamespace MWClass\n{\n\n    class CreatureCustomData : public MWWorld::TypedCustomData<CreatureCustomData>\n    {\n    public:\n        MWMechanics::CreatureStats mCreatureStats;\n        std::unique_ptr<MWWorld::ContainerStore> mContainerStore; // may be InventoryStore for some creatures\n        MWMechanics::Movement mMovement;\n\n        CreatureCustomData() = default;\n        CreatureCustomData(const CreatureCustomData& other);\n        CreatureCustomData(CreatureCustomData&& other) = default;\n\n        CreatureCustomData& asCreatureCustomData() override\n        {\n            return *this;\n        }\n        const CreatureCustomData& asCreatureCustomData() const override\n        {\n            return *this;\n        }\n    };\n\n    CreatureCustomData::CreatureCustomData(const CreatureCustomData& other)\n        : mCreatureStats(other.mCreatureStats),\n          mContainerStore(other.mContainerStore->clone()),\n          mMovement(other.mMovement)\n    {\n    }\n\n    const Creature::GMST& Creature::getGmst()\n    {\n        static GMST gmst;\n        static bool inited = false;\n        if (!inited)\n        {\n            const MWBase::World *world = MWBase::Environment::get().getWorld();\n            const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();\n            gmst.fMinWalkSpeedCreature = store.find(\"fMinWalkSpeedCreature\");\n            gmst.fMaxWalkSpeedCreature = store.find(\"fMaxWalkSpeedCreature\");\n            gmst.fEncumberedMoveEffect = store.find(\"fEncumberedMoveEffect\");\n            gmst.fSneakSpeedMultiplier = store.find(\"fSneakSpeedMultiplier\");\n            gmst.fAthleticsRunBonus = store.find(\"fAthleticsRunBonus\");\n            gmst.fBaseRunMultiplier = store.find(\"fBaseRunMultiplier\");\n            gmst.fMinFlySpeed = store.find(\"fMinFlySpeed\");\n            gmst.fMaxFlySpeed = store.find(\"fMaxFlySpeed\");\n            gmst.fSwimRunBase = store.find(\"fSwimRunBase\");\n            gmst.fSwimRunAthleticsMult = store.find(\"fSwimRunAthleticsMult\");\n            gmst.fKnockDownMult = store.find(\"fKnockDownMult\");\n            gmst.iKnockDownOddsMult = store.find(\"iKnockDownOddsMult\");\n            gmst.iKnockDownOddsBase = store.find(\"iKnockDownOddsBase\");\n            inited = true;\n        }\n        return gmst;\n    }\n\n    void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            std::unique_ptr<CreatureCustomData> data (new CreatureCustomData);\n\n            MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();\n\n            // creature stats\n            data->mCreatureStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mData.mStrength);\n            data->mCreatureStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mData.mIntelligence);\n            data->mCreatureStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mData.mWillpower);\n            data->mCreatureStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mData.mAgility);\n            data->mCreatureStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mData.mSpeed);\n            data->mCreatureStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mData.mEndurance);\n            data->mCreatureStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mData.mPersonality);\n            data->mCreatureStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mData.mLuck);\n            data->mCreatureStats.setHealth(static_cast<float>(ref->mBase->mData.mHealth));\n            data->mCreatureStats.setMagicka(static_cast<float>(ref->mBase->mData.mMana));\n            data->mCreatureStats.setFatigue(static_cast<float>(ref->mBase->mData.mFatigue));\n\n            data->mCreatureStats.setLevel(ref->mBase->mData.mLevel);\n\n            data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage);\n\n            data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello);\n            data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, ref->mBase->mAiData.mFight);\n            data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee);\n            data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm);\n\n            // Persistent actors with 0 health do not play death animation\n            if (data->mCreatureStats.isDead())\n                data->mCreatureStats.setDeathAnimationFinished(isPersistent(ptr));\n\n            // spells\n            bool spellsInitialised = data->mCreatureStats.getSpells().setSpells(ref->mBase->mId);\n            if (!spellsInitialised)\n                data->mCreatureStats.getSpells().addAllToInstance(ref->mBase->mSpells.mList);\n\n            // inventory\n            bool hasInventory = hasInventoryStore(ptr);\n            if (hasInventory)\n                data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();\n            else\n                data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();\n\n            data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);\n\n            data->mCreatureStats.setNeedRecalcDynamicStats(false);\n\n            // store\n            ptr.getRefData().setCustomData(std::move(data));\n\n            getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());\n\n            if (hasInventory)\n                getInventoryStore(ptr).autoEquip(ptr);\n        }\n    }\n\n    void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        MWRender::Objects& objects = renderingInterface.getObjects();\n        objects.insertCreature(ptr, model, hasInventoryStore(ptr));\n    }\n\n    std::string Creature::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    void Creature::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const\n    {\n        std::string model = getModel(ptr);\n        if (!model.empty())\n            models.push_back(model);\n\n        // FIXME: use const version of InventoryStore functions once they are available\n        if (hasInventoryStore(ptr))\n        {\n            const MWWorld::InventoryStore& invStore = getInventoryStore(ptr);\n            for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)\n            {\n                MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);\n                if (equipped != invStore.end())\n                {\n                    model = equipped->getClass().getModel(*equipped);\n                    if (!model.empty())\n                        models.push_back(model);\n                }\n            }\n        }\n    }\n\n    std::string Creature::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    MWMechanics::CreatureStats& Creature::getCreatureStats (const MWWorld::Ptr& ptr) const\n    {\n        ensureCustomData (ptr);\n\n        return ptr.getRefData().getCustomData()->asCreatureCustomData().mCreatureStats;\n    }\n\n\n    void Creature::hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const\n    {\n        /*\n            Start of tes3mp addition\n\n            Ignore hit calculations on this client from DedicatedPlayers and DedicatedActors\n        */\n        if (mwmp::PlayerList::isDedicatedPlayer(ptr) || mwmp::Main::get().getCellController()->isDedicatedActor(ptr))\n        {\n            return;\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        MWWorld::LiveCellRef<ESM::Creature> *ref =\n            ptr.get<ESM::Creature>();\n        const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        MWMechanics::CreatureStats &stats = getCreatureStats(ptr);\n\n        if (stats.getDrawState() != MWMechanics::DrawState_Weapon)\n            return;\n\n        // Get the weapon used (if hand-to-hand, weapon = inv.end())\n        MWWorld::Ptr weapon;\n        if (hasInventoryStore(ptr))\n        {\n            MWWorld::InventoryStore &inv = getInventoryStore(ptr);\n            MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n            if (weaponslot != inv.end() && weaponslot->getTypeName() == typeid(ESM::Weapon).name())\n                weapon = *weaponslot;\n        }\n\n        MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);\n\n        float dist = gmst.find(\"fCombatDistance\")->mValue.getFloat();\n        if (!weapon.isEmpty())\n            dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach;\n\n        // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.\n        std::vector<MWWorld::Ptr> targetActors;\n        stats.getAiSequence().getCombatTargets(targetActors);\n\n        std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors);\n        if (result.first.isEmpty())\n            return; // Didn't hit anything\n\n        MWWorld::Ptr victim = result.first;\n\n        /*\n            Start of tes3mp change (major)\n\n            Send an ID_OBJECT_HIT packet when hitting non-actors instead of\n            just returning\n        */\n        if (!victim.getClass().isActor())\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectHit(victim, ptr);\n            objectList->sendObjectHit();\n            return;\n        }\n        /*\n            End of tes3mp change (major)\n        */\n\n        osg::Vec3f hitPosition (result.second);\n\n        float hitchance = MWMechanics::getHitChance(ptr, victim, ref->mBase->mData.mCombat);\n\n        /*\n            Start of tes3mp addition\n\n            If the attacker is a LocalPlayer or LocalActor, get their Attack to assign its\n            hit position and target\n        */\n        mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(ptr);\n\n        if (localAttack)\n        {\n            localAttack->isHit = true;\n            localAttack->success = true;\n            localAttack->hitPosition = MechanicsHelper::getPositionFromVector(hitPosition);\n            MechanicsHelper::assignAttackTarget(localAttack, victim);\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        if(Misc::Rng::roll0to99() >= hitchance)\n        {\n            /*\n                Start of tes3mp addition\n\n                If this was a failed attack by the LocalPlayer or LocalActor, send a\n                packet about it\n\n                Send an ID_OBJECT_HIT about it as well\n            */\n            if (localAttack)\n            {\n                localAttack->pressed = false;\n                localAttack->success = false;\n                localAttack->shouldSend = true;\n\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectHit(victim, ptr, *localAttack);\n                objectList->sendObjectHit();\n            }\n            /*\n                End of tes3mp addition\n            */\n\n            victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false);\n            MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);\n            return;\n        }\n\n        int min,max;\n        switch (type)\n        {\n        case 0:\n            min = ref->mBase->mData.mAttack[0];\n            max = ref->mBase->mData.mAttack[1];\n            break;\n        case 1:\n            min = ref->mBase->mData.mAttack[2];\n            max = ref->mBase->mData.mAttack[3];\n            break;\n        case 2:\n        default:\n            min = ref->mBase->mData.mAttack[4];\n            max = ref->mBase->mData.mAttack[5];\n            break;\n        }\n\n        float damage = min + (max - min) * attackStrength;\n        bool healthdmg = true;\n        if (!weapon.isEmpty())\n        {\n            const unsigned char *attack = nullptr;\n            if(type == ESM::Weapon::AT_Chop)\n                attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;\n            else if(type == ESM::Weapon::AT_Slash)\n                attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;\n            else if(type == ESM::Weapon::AT_Thrust)\n                attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;\n            if(attack)\n            {\n                damage = attack[0] + ((attack[1]-attack[0])*attackStrength);\n                MWMechanics::adjustWeaponDamage(damage, weapon, ptr);\n                MWMechanics::resistNormalWeapon(victim, ptr, weapon, damage);\n                MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);\n            }\n\n            // Apply \"On hit\" enchanted weapons\n\n            /*\n                Start of tes3mp change (minor)\n\n                Track whether the strike enchantment is successful for attacks by the\n                LocalPlayer or LocalActors\n            */\n            bool appliedEnchantment = MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition);\n\n            if (localAttack)\n                localAttack->applyWeaponEnchantment = appliedEnchantment;\n            /*\n                End of tes3mp change (minor)\n            */\n        }\n        else if (isBipedal(ptr))\n        {\n            MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg, attackStrength);\n        }\n\n        MWMechanics::applyElementalShields(ptr, victim);\n\n        if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))\n            damage = 0;\n\n        MWMechanics::diseaseContact(victim, ptr);\n\n        victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);\n    }\n\n    void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const\n    {\n        MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n\n        // NOTE: 'object' and/or 'attacker' may be empty.        \n        if (!attacker.isEmpty() && attacker.getClass().isActor() && !stats.getAiSequence().isInCombat(attacker))\n            stats.setAttacked(true);\n\n        // Self defense\n        bool setOnPcHitMe = true; // Note OnPcHitMe is not set for friendly hits.\n        \n        // No retaliation for totally static creatures (they have no movement or attacks anyway)\n        if (isMobile(ptr) && !attacker.isEmpty())\n            setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);\n\n        // Attacker and target store each other as hitattemptactor if they have no one stored yet\n        if (!attacker.isEmpty() && attacker.getClass().isActor())\n        {\n            MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker);\n\n            /*\n                Start of tes3mp change (minor)\n\n                Instead of only checking whether an attacker is the LocalPlayer, also\n                check if they are a DedicatedPlayer\n\n                Additionally, if the two players are on each other's team, don't track\n                their hits\n            */\n\n            // First handle the attacked actor\n            if ((stats.getHitAttemptActorId() == -1)\n                && (statsAttacker.getAiSequence().isInCombat(ptr)\n                    || attacker == MWMechanics::getPlayer()\n                    || mwmp::PlayerList::isDedicatedPlayer(attacker))\n                && !MechanicsHelper::isTeamMember(attacker, ptr))\n                stats.setHitAttemptActorId(statsAttacker.getActorId());\n\n            // Next handle the attacking actor\n            if ((statsAttacker.getHitAttemptActorId() == -1)\n                && (statsAttacker.getAiSequence().isInCombat(ptr)\n                    || attacker == MWMechanics::getPlayer()\n                    || mwmp::PlayerList::isDedicatedPlayer(attacker))\n                && !MechanicsHelper::isTeamMember(ptr, attacker))\n                statsAttacker.setHitAttemptActorId(stats.getActorId());\n\n            /*\n                End of tes3mp change (minor)\n            */\n        }\n\n        if (!object.isEmpty())\n            stats.setLastHitAttemptObject(object.getCellRef().getRefId());\n\n        if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())\n        {\n            const std::string &script = ptr.get<ESM::Creature>()->mBase->mScript;\n            /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */\n            if(!script.empty())\n                ptr.getRefData().getLocals().setVarByInt(script, \"onpchitme\", 1);\n        }\n\n        if (!successful)\n        {\n            // Missed\n            if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())\n                MWBase::Environment::get().getSoundManager()->playSound3D(ptr, \"miss\", 1.0f, 1.0f);\n            return;\n        }\n\n        if (!object.isEmpty())\n            stats.setLastHitObject(object.getCellRef().getRefId());\n\n        if (damage < 0.001f)\n            damage = 0;\n\n        if (damage > 0.f)\n        {\n            if (!attacker.isEmpty())\n            {\n                // Check for knockdown\n                float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->mValue.getFloat();\n                float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()\n                    * getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f + getGmst().iKnockDownOddsBase->mValue.getInteger();\n\n                /*\n                    Start of tes3mp change (major)\n\n                    If the attacker is a DedicatedPlayer or DedicatedActor with a successful knockdown, apply the knockdown\n\n                    If the attacker is neither of those, then it must be a LocalPlayer or a LocalActor, so calculate the\n                    knockdown probability on our client\n\n                    Default to hit recovery if no knockdown has taken place, like in regular OpenMW\n                */\n                mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(attacker);\n\n                if (dedicatedAttack)\n                {\n                    if (dedicatedAttack->knockdown)\n                        stats.setKnockedDown(true);\n                }\n\n                if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99())\n                    stats.setKnockedDown(true);\n                else\n                {\n                    if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99())\n                        stats.setKnockedDown(true);\n                }\n\n                if (!stats.getKnockedDown())\n                    stats.setHitRecovery(true); // Is this supposed to always occur?\n                /*\n                    End of tes3mp change (major)\n                */\n            }\n\n            if(ishealth)\n            {\n                damage *= damage / (damage + getArmorRating(ptr));\n                damage = std::max(1.f, damage);\n                if (!attacker.isEmpty())\n                {\n                    damage = scaleDamage(damage, attacker, ptr);\n                    MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition);\n                }\n\n                MWBase::Environment::get().getSoundManager()->playSound3D(ptr, \"Health Damage\", 1.0f, 1.0f);\n\n                MWMechanics::DynamicStat<float> health(stats.getHealth());\n                health.setCurrent(health.getCurrent() - damage);\n                stats.setHealth(health);\n            }\n            else\n            {\n                MWMechanics::DynamicStat<float> fatigue(stats.getFatigue());\n                fatigue.setCurrent(fatigue.getCurrent() - damage, true);\n                stats.setFatigue(fatigue);\n            }\n        }\n\n        /*\n            Start of tes3mp addition\n\n            If the attacker was the LocalPlayer or LocalActor, record their target and send an\n            attack packet about it\n\n            Send an ID_OBJECT_HIT about it as well\n\n            If the victim was a LocalActor who died, record their attacker as the killer\n        */\n        mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);\n\n        if (localAttack)\n        {\n            localAttack->pressed = false;\n            localAttack->damage = damage;\n            localAttack->knockdown = getCreatureStats(ptr).getKnockedDown();\n\n            MechanicsHelper::assignAttackTarget(localAttack, ptr);\n\n            localAttack->shouldSend = true;\n\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectHit(ptr, attacker, *localAttack);\n            objectList->sendObjectHit();\n        }\n\n        if (mwmp::Main::get().getCellController()->isLocalActor(ptr))\n        {\n            if (getCreatureStats(ptr).isDead())\n            {\n                mwmp::Main::get().getCellController()->getLocalActor(ptr)->killer = MechanicsHelper::getTarget(attacker);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfCreature\");\n\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(\"#{sWerewolfRefusal}\"));\n            if(sound) action->setSound(sound->mId);\n\n            return action;\n        }\n\n        const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n\n        if(stats.isDead())\n        {\n            bool canLoot = Settings::Manager::getBool (\"can loot during death animation\", \"Game\");\n\n            // by default user can loot friendly actors during death animation\n            if (canLoot && !stats.getAiSequence().isInCombat())\n                return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));\n\n            // otherwise wait until death animation\n            if(stats.isDeathAnimationFinished())\n                return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));\n        }\n        else if (!stats.getAiSequence().isInCombat() && !stats.getKnockedDown())\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));\n\n        // Tribunal and some mod companions oddly enough must use open action as fallback\n        if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), \"companion\"))\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));\n\n        return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(\"\"));\n    }\n\n    MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const\n    {\n        ensureCustomData (ptr);\n\n        return *ptr.getRefData().getCustomData()->asCreatureCustomData().mContainerStore;\n    }\n\n    MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const\n    {\n        if (hasInventoryStore(ptr))\n            return dynamic_cast<MWWorld::InventoryStore&>(getContainerStore(ptr));\n        else\n            throw std::runtime_error(\"this creature has no inventory store\");\n    }\n\n    bool Creature::hasInventoryStore(const MWWorld::Ptr &ptr) const\n    {\n        return isFlagBitSet(ptr, ESM::Creature::Weapon);\n    }\n\n    std::string Creature::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();\n\n        return ref->mBase->mScript;\n    }\n\n    bool Creature::isEssential (const MWWorld::ConstPtr& ptr) const\n    {\n        return isFlagBitSet(ptr, ESM::Creature::Essential);\n    }\n\n    void Creature::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Creature);\n\n        registerClass (typeid (ESM::Creature).name(), instance);\n    }\n\n    float Creature::getMaxSpeed(const MWWorld::Ptr &ptr) const\n    {\n        const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n\n        if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())\n            return 0.f;\n\n        const GMST& gmst = getGmst();\n\n        const MWBase::World *world = MWBase::Environment::get().getWorld();\n        const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();\n\n        float moveSpeed;\n\n        if(getEncumbrance(ptr) > getCapacity(ptr))\n            moveSpeed = 0.0f;\n        else if(canFly(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).getMagnitude() > 0 &&\n                world->isLevitationEnabled()))\n        {\n            float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +\n                                    mageffects.get(ESM::MagicEffect::Levitate).getMagnitude());\n            flySpeed = gmst.fMinFlySpeed->mValue.getFloat() + flySpeed*(gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat());\n            const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);\n            flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance;\n            flySpeed = std::max(0.0f, flySpeed);\n            moveSpeed = flySpeed;\n        }\n        else if(world->isSwimming(ptr))\n            moveSpeed = getSwimSpeed(ptr);\n        else\n            moveSpeed = getWalkSpeed(ptr);\n\n        return moveSpeed;\n    }\n\n    MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const\n    {\n        ensureCustomData (ptr);\n\n        return ptr.getRefData().getCustomData()->asCreatureCustomData().mMovement;\n    }\n\n    bool Creature::hasToolTip(const MWWorld::ConstPtr& ptr) const\n    {\n        if (!ptr.getRefData().getCustomData() || MWBase::Environment::get().getWindowManager()->isGuiMode())\n            return true;\n\n        const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();\n\n        if (customData.mCreatureStats.isDead() && customData.mCreatureStats.isDeathAnimationFinished())\n            return true;\n\n        return !customData.mCreatureStats.getAiSequence().isInCombat();\n    }\n\n    MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));\n\n        std::string text;\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp())\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        info.text = text;\n\n        return info;\n    }\n\n    float Creature::getArmorRating (const MWWorld::Ptr& ptr) const\n    {\n        // Equipment armor rating is deliberately ignored.\n        return getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).getMagnitude();\n    }\n\n    float Creature::getCapacity (const MWWorld::Ptr& ptr) const\n    {\n        const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);\n        return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5;\n    }\n\n    int Creature::getServices(const MWWorld::ConstPtr &actor) const\n    {\n        return actor.get<ESM::Creature>()->mBase->mAiData.mServices;\n    }\n\n    bool Creature::isPersistent(const MWWorld::ConstPtr &actor) const\n    {\n        const MWWorld::LiveCellRef<ESM::Creature>* ref = actor.get<ESM::Creature>();\n        return ref->mBase->mPersistent;\n    }\n\n    std::string Creature::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const\n    {\n        int type = getSndGenTypeFromName(ptr, name);\n        if (type < 0)\n            return std::string();\n\n        std::vector<const ESM::SoundGenerator*> sounds;\n        std::vector<const ESM::SoundGenerator*> fallbacksounds;\n\n        MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();\n\n        const std::string& ourId = (ref->mBase->mOriginal.empty()) ? ptr.getCellRef().getRefId() : ref->mBase->mOriginal;\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        auto sound = store.get<ESM::SoundGenerator>().begin();\n        while (sound != store.get<ESM::SoundGenerator>().end())\n        {\n            if (type == sound->mType && !sound->mCreature.empty() && Misc::StringUtils::ciEqual(ourId, sound->mCreature))\n                sounds.push_back(&*sound);\n            if (type == sound->mType && sound->mCreature.empty())\n                fallbacksounds.push_back(&*sound);\n            ++sound;\n        }\n\n        if (sounds.empty())\n        {\n            const std::string model = getModel(ptr);\n            if (!model.empty())\n            {\n                for (const ESM::Creature &creature : store.get<ESM::Creature>())\n                {\n                    if (creature.mId != ourId && creature.mOriginal != ourId && !creature.mModel.empty()\n                     && Misc::StringUtils::ciEqual(model, \"meshes\\\\\" + creature.mModel))\n                    {\n                        const std::string& fallbackId = !creature.mOriginal.empty() ? creature.mOriginal : creature.mId;\n                        sound = store.get<ESM::SoundGenerator>().begin();\n                        while (sound != store.get<ESM::SoundGenerator>().end())\n                        {\n                            if (type == sound->mType && !sound->mCreature.empty()\n                             && Misc::StringUtils::ciEqual(fallbackId, sound->mCreature))\n                                sounds.push_back(&*sound);\n                            ++sound;\n                        }\n                        break;\n                    }\n                }\n            }\n        }\n\n        if (!sounds.empty())\n            return sounds[Misc::Rng::rollDice(sounds.size())]->mSound;\n        if (!fallbacksounds.empty())\n            return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound;\n\n        return std::string();\n    }\n\n    MWWorld::Ptr Creature::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    bool Creature::isBipedal(const MWWorld::ConstPtr &ptr) const\n    {\n        return isFlagBitSet(ptr, ESM::Creature::Bipedal);\n    }\n\n    bool Creature::canFly(const MWWorld::ConstPtr &ptr) const\n    {\n        return isFlagBitSet(ptr, ESM::Creature::Flies);\n    }\n\n    bool Creature::canSwim(const MWWorld::ConstPtr &ptr) const\n    {\n        return isFlagBitSet(ptr, static_cast<ESM::Creature::Flags>(ESM::Creature::Swims | ESM::Creature::Bipedal));\n    }\n\n    bool Creature::canWalk(const MWWorld::ConstPtr &ptr) const\n    {\n        return isFlagBitSet(ptr, static_cast<ESM::Creature::Flags>(ESM::Creature::Walks | ESM::Creature::Bipedal));\n    }\n\n    int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)\n    {\n        if(name == \"left\")\n        {\n            MWBase::World *world = MWBase::Environment::get().getWorld();\n            if(world->isFlying(ptr))\n                return -1;\n            osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());\n            if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))\n                return ESM::SoundGenerator::SwimLeft;\n            if(world->isOnGround(ptr))\n                return ESM::SoundGenerator::LeftFoot;\n            return -1;\n        }\n        if(name == \"right\")\n        {\n            MWBase::World *world = MWBase::Environment::get().getWorld();\n            if(world->isFlying(ptr))\n                return -1;\n            osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());\n            if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))\n                return ESM::SoundGenerator::SwimRight;\n            if(world->isOnGround(ptr))\n                return ESM::SoundGenerator::RightFoot;\n            return -1;\n        }\n        if(name == \"swimleft\")\n            return ESM::SoundGenerator::SwimLeft;\n        if(name == \"swimright\")\n            return ESM::SoundGenerator::SwimRight;\n        if(name == \"moan\")\n            return ESM::SoundGenerator::Moan;\n        if(name == \"roar\")\n            return ESM::SoundGenerator::Roar;\n        if(name == \"scream\")\n            return ESM::SoundGenerator::Scream;\n        if(name == \"land\")\n            return ESM::SoundGenerator::Land;\n\n        throw std::runtime_error(std::string(\"Unexpected soundgen type: \")+name);\n    }\n\n    float Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const\n    {\n        MWWorld::LiveCellRef<ESM::Creature> *ref =\n            ptr.get<ESM::Creature>();\n\n        const ESM::Skill* skillRecord = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(skill);\n\n        switch (skillRecord->mData.mSpecialization)\n        {\n        case ESM::Class::Combat:\n            return ref->mBase->mData.mCombat;\n        case ESM::Class::Magic:\n            return ref->mBase->mData.mMagic;\n        case ESM::Class::Stealth:\n            return ref->mBase->mData.mStealth;\n        default:\n            throw std::runtime_error(\"invalid specialisation\");\n        }\n    }\n\n    int Creature::getBloodTexture(const MWWorld::ConstPtr &ptr) const\n    {\n        return ptr.get<ESM::Creature>()->mBase->mBloodType;\n    }\n\n    void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)\n        const\n    {\n        if (!state.mHasCustomState)\n            return;\n\n        if (state.mVersion > 0)\n        {\n            if (!ptr.getRefData().getCustomData())\n            {\n                // Create a CustomData, but don't fill it from ESM records (not needed)\n                std::unique_ptr<CreatureCustomData> data (new CreatureCustomData);\n\n                if (hasInventoryStore(ptr))\n                    data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();\n                else\n                    data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();\n\n                ptr.getRefData().setCustomData (std::move(data));\n            }\n        }\n        else\n            ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless.\n\n        CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();\n        const ESM::CreatureState& creatureState = state.asCreatureState();\n        customData.mContainerStore->readState (creatureState.mInventory);\n        bool spellsInitialised = customData.mCreatureStats.getSpells().setSpells(ptr.get<ESM::Creature>()->mBase->mId);\n        if(spellsInitialised)\n            customData.mCreatureStats.getSpells().clear();\n        customData.mCreatureStats.readState (creatureState.mCreatureStats);\n    }\n\n    void Creature::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state)\n        const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            state.mHasCustomState = false;\n            return;\n        }\n\n        if (ptr.getRefData().getCount() <= 0)\n        {\n            state.mHasCustomState = false;\n            return;\n        }\n\n        const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();\n        ESM::CreatureState& creatureState = state.asCreatureState();\n        customData.mContainerStore->writeState (creatureState.mInventory);\n        customData.mCreatureStats.writeState (creatureState.mCreatureStats);\n    }\n\n    int Creature::getBaseGold(const MWWorld::ConstPtr& ptr) const\n    {\n        return ptr.get<ESM::Creature>()->mBase->mData.mGold;\n    }\n\n    void Creature::respawn(const MWWorld::Ptr &ptr) const\n    {\n        const MWMechanics::CreatureStats& creatureStats = getCreatureStats(ptr);\n        if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead())\n            return;\n\n        if (!creatureStats.isDeathAnimationFinished())\n            return;\n\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        static const float fCorpseRespawnDelay = gmst.find(\"fCorpseRespawnDelay\")->mValue.getFloat();\n        static const float fCorpseClearDelay = gmst.find(\"fCorpseClearDelay\")->mValue.getFloat();\n\n        float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay);\n\n        if (isFlagBitSet(ptr, ESM::Creature::Respawn)\n                && creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp())\n        {\n            if (ptr.getCellRef().hasContentFile())\n            {\n                if (ptr.getRefData().getCount() == 0)\n                {\n                    ptr.getRefData().setCount(1);\n                    const std::string& script = getScript(ptr);\n                    if(!script.empty())\n                        MWBase::Environment::get().getWorld()->getLocalScripts().add(script, ptr);\n                }\n\n                MWBase::Environment::get().getWorld()->removeContainerScripts(ptr);\n                ptr.getRefData().setCustomData(nullptr);\n\n                // Reset to original position\n                MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0],\n                        ptr.getCellRef().getPosition().pos[1],\n                        ptr.getCellRef().getPosition().pos[2]);\n            }\n        }\n    }\n\n    int Creature::getBaseFightRating(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();\n        return ref->mBase->mAiData.mFight;\n    }\n\n    void Creature::adjustScale(const MWWorld::ConstPtr &ptr, osg::Vec3f &scale, bool /* rendering */) const\n    {\n        const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();\n        scale *= ref->mBase->mScale;\n    }\n\n    void Creature::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const\n    {\n        MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value);\n    }\n\n    void Creature::modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const\n    {\n        MWMechanics::modifyBaseInventory<ESM::Creature>(actorId, itemId, amount);\n    }\n\n    float Creature::getWalkSpeed(const MWWorld::Ptr& ptr) const\n    {\n        const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n        const GMST& gmst = getGmst();\n\n        return gmst.fMinWalkSpeedCreature->mValue.getFloat()\n                + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()\n                * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());\n    }\n\n    float Creature::getRunSpeed(const MWWorld::Ptr& ptr) const\n    {\n        return getWalkSpeed(ptr);\n    }\n\n    float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const\n    {\n        const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n        const GMST& gmst = getGmst();\n        const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();\n\n        return getWalkSpeed(ptr)\n            * (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude())\n            * (gmst.fSwimRunBase->mValue.getFloat()\n               + 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat());\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/creature.hpp",
    "content": "#ifndef GAME_MWCLASS_CREATURE_H\n#define GAME_MWCLASS_CREATURE_H\n\n#include \"actor.hpp\"\n\nnamespace ESM\n{\n    struct GameSetting;\n}\n\nnamespace MWClass\n{\n    class Creature : public Actor\n    {\n            void ensureCustomData (const MWWorld::Ptr& ptr) const;\n\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n            static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name);\n\n            // cached GMSTs\n            struct GMST\n            {\n                const ESM::GameSetting *fMinWalkSpeedCreature;\n                const ESM::GameSetting *fMaxWalkSpeedCreature;\n                const ESM::GameSetting *fEncumberedMoveEffect;\n                const ESM::GameSetting *fSneakSpeedMultiplier;\n                const ESM::GameSetting *fAthleticsRunBonus;\n                const ESM::GameSetting *fBaseRunMultiplier;\n                const ESM::GameSetting *fMinFlySpeed;\n                const ESM::GameSetting *fMaxFlySpeed;\n                const ESM::GameSetting *fSwimRunBase;\n                const ESM::GameSetting *fSwimRunAthleticsMult;\n                const ESM::GameSetting *fKnockDownMult;\n                const ESM::GameSetting *iKnockDownOddsMult;\n                const ESM::GameSetting *iKnockDownOddsBase;\n            };\n\n            static const GMST& getGmst();\n\n        public:\n\n             void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const override;\n            ///< Return creature stats\n\n            void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const override;\n\n            void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const override;\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWWorld::ContainerStore& getContainerStore (\n                const MWWorld::Ptr& ptr) const override;\n            ///< Return container store\n\n            MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const override;\n            ///< Return inventory store\n\n            bool hasInventoryStore (const MWWorld::Ptr &ptr) const override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a class has a container store\n            */\n            virtual bool hasContainerStore(const MWWorld::Ptr &ptr) const { return true; }\n            /*\n                End of tes3mp addition\n            */\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            float getCapacity (const MWWorld::Ptr& ptr) const override;\n            ///< Return total weight that fits into the object. Throws an exception, if the object can't\n            /// hold other objects.\n\n            float getArmorRating (const MWWorld::Ptr& ptr) const override;\n            ///< @return combined armor rating of this actor\n\n            bool isEssential (const MWWorld::ConstPtr& ptr) const override;\n            ///< Is \\a ptr essential? (i.e. may losing \\a ptr make the game unwinnable)\n\n            int getServices (const MWWorld::ConstPtr& actor) const override;\n\n            bool isPersistent (const MWWorld::ConstPtr& ptr) const override;\n\n            std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const override;\n\n            MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const override;\n            ///< Return desired movement.\n\n            float getMaxSpeed (const MWWorld::Ptr& ptr) const override;\n\n            static void registerSelf();\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;\n            ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().\n\n            bool isBipedal (const MWWorld::ConstPtr &ptr) const override;\n            bool canFly (const MWWorld::ConstPtr &ptr) const override;\n            bool canSwim (const MWWorld::ConstPtr &ptr) const override;\n            bool canWalk (const MWWorld::ConstPtr &ptr) const override;\n\n            float getSkill(const MWWorld::Ptr &ptr, int skill) const override;\n\n            /// Get a blood texture suitable for \\a ptr (see Blood Texture 0-2 in Morrowind.ini)\n            int getBloodTexture (const MWWorld::ConstPtr& ptr) const override;\n\n            void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const override;\n            ///< Read additional state from \\a state into \\a ptr.\n\n            void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const override;\n            ///< Write additional state from \\a ptr into \\a state.\n\n            int getBaseGold(const MWWorld::ConstPtr& ptr) const override;\n\n            void respawn (const MWWorld::Ptr& ptr) const override;\n\n            int getBaseFightRating(const MWWorld::ConstPtr &ptr) const override;\n\n            void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const override;\n            /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh\n\n            void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const override;\n\n            void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const override;\n\n            float getWalkSpeed(const MWWorld::Ptr& ptr) const override;\n\n            float getRunSpeed(const MWWorld::Ptr& ptr) const override;\n\n            float getSwimSpeed(const MWWorld::Ptr& ptr) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/creaturelevlist.cpp",
    "content": "#include \"creaturelevlist.hpp\"\n\n#include <components/esm/loadlevlist.hpp>\n#include <components/esm/creaturelevliststate.hpp>\n\n#include \"../mwmechanics/levelledlist.hpp\"\n\n#include \"../mwworld/customdata.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n\nnamespace MWClass\n{\n    class CreatureLevListCustomData : public MWWorld::TypedCustomData<CreatureLevListCustomData>\n    {\n    public:\n        // actorId of the creature we spawned\n        int mSpawnActorId;\n        bool mSpawn; // Should a new creature be spawned?\n\n        CreatureLevListCustomData& asCreatureLevListCustomData() override\n        {\n            return *this;\n        }\n        const CreatureLevListCustomData& asCreatureLevListCustomData() const override\n        {\n            return *this;\n        }\n    };\n\n    std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        return \"\";\n    }\n\n    bool CreatureLevList::hasToolTip(const MWWorld::ConstPtr& ptr) const\n    {\n        return false;\n    }\n\n    void CreatureLevList::respawn(const MWWorld::Ptr &ptr) const\n    {\n        ensureCustomData(ptr);\n\n        CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();\n        if (customData.mSpawn)\n            return;\n\n        MWWorld::Ptr creature = (customData.mSpawnActorId == -1) ? MWWorld::Ptr() : MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);\n        if (!creature.isEmpty())\n        {\n            const MWMechanics::CreatureStats& creatureStats = creature.getClass().getCreatureStats(creature);\n            if (creature.getRefData().getCount() == 0)\n                customData.mSpawn = true;\n            else if (creatureStats.isDead())\n            {\n                const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n                static const float fCorpseRespawnDelay = gmst.find(\"fCorpseRespawnDelay\")->mValue.getFloat();\n                static const float fCorpseClearDelay = gmst.find(\"fCorpseClearDelay\")->mValue.getFloat();\n\n                float delay = std::min(fCorpseRespawnDelay, fCorpseClearDelay);\n                if (creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp())\n                    customData.mSpawn = true;\n            }\n        }\n        else\n            customData.mSpawn = true;\n    }\n\n    void CreatureLevList::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new CreatureLevList);\n\n        registerClass (typeid (ESM::CreatureLevList).name(), instance);\n    }\n\n    void CreatureLevList::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const\n    {\n        // disable for now, too many false positives\n        /*\n        const MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = ptr.get<ESM::CreatureLevList>();\n        for (std::vector<ESM::LevelledListBase::LevelItem>::const_iterator it = ref->mBase->mList.begin(); it != ref->mBase->mList.end(); ++it)\n        {\n            MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n            if (it->mLevel > player.getClass().getCreatureStats(player).getLevel())\n                continue;\n\n            const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n            MWWorld::ManualRef ref(store, it->mId);\n            ref.getPtr().getClass().getModelsToPreload(ref.getPtr(), models);\n        }\n        */\n    }\n\n    void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string& model, MWRender::RenderingInterface &renderingInterface) const\n    {\n        ensureCustomData(ptr);\n\n        CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();\n        if (!customData.mSpawn)\n            return;\n\n        MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =\n            ptr.get<ESM::CreatureLevList>();\n\n        std::string id = MWMechanics::getLevelledItem(ref->mBase, true);\n\n        if (!id.empty())\n        {\n            // Delete the previous creature\n            if (customData.mSpawnActorId != -1)\n            {\n                MWWorld::Ptr creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);\n                if (!creature.isEmpty())\n                    MWBase::Environment::get().getWorld()->deleteObject(creature);\n                customData.mSpawnActorId = -1;\n            }\n\n            const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n            MWWorld::ManualRef manualRef(store, id);\n            manualRef.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition());\n\n            /*\n                Start of tes3mp change (major)\n\n                Don't spawn leveled creatures in multiplayer; they'll be spawned when the server requests them\n            */\n            /*\n            manualRef.getPtr().getCellRef().setScale(ptr.getCellRef().getScale());\n            MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(manualRef.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition());\n            customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId();\n            */\n            /*\n                End of tes3mp change (major)\n            */\n            customData.mSpawn = false;\n        }\n        else\n            customData.mSpawn = false;\n    }\n\n    void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            std::unique_ptr<CreatureLevListCustomData> data = std::make_unique<CreatureLevListCustomData>();\n            data->mSpawnActorId = -1;\n            data->mSpawn = true;\n\n            ptr.getRefData().setCustomData(std::move(data));\n        }\n    }\n\n    void CreatureLevList::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)\n        const\n    {\n        if (!state.mHasCustomState)\n            return;\n\n        ensureCustomData(ptr);\n        CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();\n        const ESM::CreatureLevListState& levListState = state.asCreatureLevListState();\n        customData.mSpawnActorId = levListState.mSpawnActorId;\n        customData.mSpawn = levListState.mSpawn;\n    }\n\n    void CreatureLevList::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state)\n        const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            state.mHasCustomState = false;\n            return;\n        }\n\n        const CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();\n        ESM::CreatureLevListState& levListState = state.asCreatureLevListState();\n        levListState.mSpawnActorId = customData.mSpawnActorId;\n        levListState.mSpawn = customData.mSpawn;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/creaturelevlist.hpp",
    "content": "#ifndef GAME_MWCLASS_CREATURELEVLIST_H\n#define GAME_MWCLASS_CREATURELEVLIST_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class CreatureLevList : public MWWorld::Class\n    {\n        void ensureCustomData (const MWWorld::Ptr& ptr) const;\n\n        public:\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            bool hasToolTip (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            static void registerSelf();\n\n            void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;\n            ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const override;\n            ///< Read additional state from \\a state into \\a ptr.\n\n            void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const override;\n            ///< Write additional state from \\a ptr into \\a state.\n\n            void respawn (const MWWorld::Ptr& ptr) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/door.cpp",
    "content": "#include \"door.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loaddoor.hpp>\n#include <components/esm/doorstate.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/failedaction.hpp\"\n#include \"../mwworld/actionteleport.hpp\"\n#include \"../mwworld/actiondoor.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/actiontrap.hpp\"\n#include \"../mwworld/customdata.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n#include \"../mwrender/animation.hpp\"\n#include \"../mwrender/vismask.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWClass\n{\n    class DoorCustomData : public MWWorld::TypedCustomData<DoorCustomData>\n    {\n    public:\n        MWWorld::DoorState mDoorState = MWWorld::DoorState::Idle;\n\n        DoorCustomData& asDoorCustomData() override\n        {\n            return *this;\n        }\n        const DoorCustomData& asDoorCustomData() const override\n        {\n            return *this;\n        }\n    };\n\n    void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty())\n        {\n            renderingInterface.getObjects().insertModel(ptr, model, true);\n            ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static);\n        }\n    }\n\n    void Door::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        if(!model.empty())\n            physics.addObject(ptr, model, MWPhysics::CollisionType_Door);\n\n        // Resume the door's opening/closing animation if it wasn't finished\n        if (ptr.getRefData().getCustomData())\n        {\n            const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();\n            if (customData.mDoorState != MWWorld::DoorState::Idle)\n            {\n                MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState);\n            }\n        }\n    }\n\n    bool Door::isDoor() const\n    {\n        return true;\n    }\n\n    bool Door::useAnim() const\n    {\n        return true;\n    }\n\n    std::string Door::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Door::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Door::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();\n\n        const std::string &openSound = ref->mBase->mOpenSound;\n        const std::string &closeSound = ref->mBase->mCloseSound;\n        const std::string lockedSound = \"LockedDoor\";\n        const std::string trapActivationSound = \"Disarm Trap Fail\";\n\n        MWWorld::ContainerStore &invStore = actor.getClass().getContainerStore(actor);\n\n        bool isLocked = ptr.getCellRef().getLockLevel() > 0;\n        bool isTrapped = !ptr.getCellRef().getTrap().empty();\n        bool hasKey = false;\n        std::string keyName;\n\n        // FIXME: If NPC activate teleporting door, it can lead to crash due to iterator invalidation in the Actors update.\n        // Make such activation a no-op for now, like how it is in the vanilla game.\n        if (actor != MWMechanics::getPlayer() && ptr.getCellRef().getTeleport())\n        {\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(std::string(), ptr));\n            action->setSound(lockedSound);\n            return action;\n        }\n\n        // make door glow if player activates it with telekinesis\n        if (actor == MWMechanics::getPlayer() &&\n            MWBase::Environment::get().getWorld()->getDistanceToFacedObject() >\n            MWBase::Environment::get().getWorld()->getMaxActivationDistance())\n        {\n            MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);\n            if(animation)\n            {\n                const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n                int index = ESM::MagicEffect::effectStringToId(\"sEffectTelekinesis\");\n                const ESM::MagicEffect *effect = store.get<ESM::MagicEffect>().find(index);\n\n                animation->addSpellCastGlow(effect, 1); // 1 second glow to match the time taken for a door opening or closing\n            }\n        }\n\n        const std::string keyId = ptr.getCellRef().getKey();\n        if (!keyId.empty())\n        {\n            MWWorld::Ptr keyPtr = invStore.search(keyId);\n            if (!keyPtr.isEmpty())\n            {\n                hasKey = true;\n                keyName = keyPtr.getClass().getName(keyPtr);\n            }\n        }\n\n        if (isLocked && hasKey)\n        {\n            if(actor == MWMechanics::getPlayer())\n                MWBase::Environment::get().getWindowManager()->messageBox(keyName + \" #{sKeyUsed}\");\n\n            /*\n                Start of tes3mp change (major)\n\n                Disable unilateral unlocking on this client and expect the server's reply to our\n                packet to do it instead\n            */\n            //ptr.getCellRef().unlock(); //Call the function here. because that makes sense.\n            /*\n                End of tes3mp change (major)\n            */\n\n            // using a key disarms the trap\n            if(isTrapped)\n            {\n                /*\n                    Start of tes3mp change (major)\n\n                    Disable unilateral trap disarming on this client and expect the server's reply to our\n                    packet to do it instead\n                */\n                //ptr.getCellRef().setTrap(\"\");\n                //MWBase::Environment::get().getSoundManager()->playSound3D(ptr, \"Disarm Trap\", 1.0f, 1.0f);\n                /*\n                    End of tes3mp change (major)\n                */\n\n                isTrapped = false;\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_OBJECT_TRAP packet every time a trap is disarmed\n                */\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectTrap(ptr, ptr.getRefData().getPosition(), true);\n                objectList->sendObjectTrap();\n                /*\n                    End of tes3mp addition\n                */\n            }\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_OBJECT_LOCK packet every time a door is unlocked here\n            */\n            if (isLocked)\n            {\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectLock(ptr, 0);\n                objectList->sendObjectLock();\n            }\n            /*\n                End of tes3mp addition\n            */\n        }\n\n        if (!isLocked || hasKey)\n        {\n            if(isTrapped)\n            {\n                // Trap activation\n                std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTrap(ptr.getCellRef().getTrap(), ptr));\n                action->setSound(trapActivationSound);\n                return action;\n            }\n\n            if (ptr.getCellRef().getTeleport())\n            {\n                if (actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getDistanceToFacedObject() > MWBase::Environment::get().getWorld()->getMaxActivationDistance())\n                {\n                    // player activated teleport door with telekinesis\n                    std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction);\n                    return action;\n                }\n                else\n                {\n                    /*\n                        Start of tes3mp change (major)\n\n                        If there is a destination override in the mwmp::Worldstate for this door's original\n                        destination, use it\n                    */\n                    std::string destinationCell = ptr.getCellRef().getDestCell();\n\n                    if (mwmp::Main::get().getNetworking()->getWorldstate()->destinationOverrides.count(destinationCell) != 0)\n                        destinationCell = mwmp::Main::get().getNetworking()->getWorldstate()->destinationOverrides[destinationCell];\n\n                    std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport(destinationCell, ptr.getCellRef().getDoorDest(), true));\n                    /*\n                        End of tes3mp change (major)\n                    */\n\n                    action->setSound(openSound);\n                    return action;\n                }\n            }\n            else\n            {\n                // animated door\n                std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));\n                const auto doorState = getDoorState(ptr);\n                bool opening = true;\n                float doorRot = ptr.getRefData().getPosition().rot[2] - ptr.getCellRef().getPosition().rot[2];\n                if (doorState == MWWorld::DoorState::Opening)\n                    opening = false;\n                if (doorState == MWWorld::DoorState::Idle && doorRot != 0)\n                    opening = false;\n\n                if (opening)\n                {\n                    MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr,\n                            closeSound, 0.5f);\n                    // Doors rotate at 90 degrees per second, so start the sound at\n                    // where it would be at the current rotation.\n                    float offset = doorRot/(osg::PI * 0.5f);\n                    action->setSoundOffset(offset);\n                    action->setSound(openSound);\n                }\n                else\n                {\n                    MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr,\n                                                openSound, 0.5f);\n                    float offset = 1.0f - doorRot/(osg::PI * 0.5f);\n                    action->setSoundOffset(std::max(offset, 0.0f));\n                    action->setSound(closeSound);\n                }\n\n                return action;\n            }\n        }\n        else\n        {\n            // locked, and we can't open.\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(std::string(), ptr));\n            action->setSound(lockedSound);\n            return action;\n        }\n    }\n\n    bool Door::canLock(const MWWorld::ConstPtr &ptr) const\n    {\n        return true;\n    }\n\n    bool Door::allowTelekinesis(const MWWorld::ConstPtr &ptr) const\n    {\n        if (ptr.getCellRef().getTeleport() && ptr.getCellRef().getLockLevel() <= 0 && ptr.getCellRef().getTrap().empty())\n            return false;\n        else\n            return true;\n    }\n\n    std::string Door::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();\n\n        return ref->mBase->mScript;\n    }\n\n    void Door::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Door);\n\n        registerClass (typeid (ESM::Door).name(), instance);\n    }\n\n    MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));\n\n        std::string text;\n\n        if (ptr.getCellRef().getTeleport())\n        {\n            text += \"\\n#{sTo}\";\n            text += \"\\n\" + getDestination(*ref);\n        }\n\n        int lockLevel = ptr.getCellRef().getLockLevel();\n        if (lockLevel > 0 && lockLevel != ESM::UnbreakableLock)\n            text += \"\\n#{sLockLevel}: \" + MWGui::ToolTips::toString(ptr.getCellRef().getLockLevel());\n        else if (ptr.getCellRef().getLockLevel() < 0)\n            text += \"\\n#{sUnlocked}\";\n        if (ptr.getCellRef().getTrap() != \"\")\n            text += \"\\n#{sTrapped}\";\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp())\n        {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n        info.text = text;\n\n        return info;\n    }\n\n    std::string Door::getDestination(const MWWorld::LiveCellRef<ESM::Door>& door)\n    {\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n\n        std::string dest = door.mRef.getDestCell();\n        if (dest.empty())\n        {\n            // door leads to exterior, use cell name (if any), otherwise translated region name\n            int x, y;\n            auto world = MWBase::Environment::get().getWorld();\n            world->positionToIndex(door.mRef.getDoorDest().pos[0], door.mRef.getDoorDest().pos[1], x, y);\n            const ESM::Cell* cell = world->getStore().get<ESM::Cell>().search(x, y);\n            dest = world->getCellName(cell);\n        }\n        /*\n            Start of tes3mp addition\n\n            If there is a destination override in the mwmp::Worldstate for this door's original\n            destination, use it\n        */\n        else if (mwmp::Main::get().getNetworking()->getWorldstate()->destinationOverrides.count(dest) != 0)\n            dest = mwmp::Main::get().getNetworking()->getWorldstate()->destinationOverrides[dest];\n        /*\n            End of tes3mp addition\n        */\n\n        return \"#{sCell=\" + dest + \"}\";\n    }\n\n    MWWorld::Ptr Door::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    void Door::ensureCustomData(const MWWorld::Ptr &ptr) const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            ptr.getRefData().setCustomData(std::make_unique<DoorCustomData>());\n        }\n    }\n\n    MWWorld::DoorState Door::getDoorState (const MWWorld::ConstPtr &ptr) const\n    {\n        if (!ptr.getRefData().getCustomData())\n            return MWWorld::DoorState::Idle;\n        const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();\n        return customData.mDoorState;\n    }\n\n    void Door::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const\n    {\n        if (ptr.getCellRef().getTeleport())\n            throw std::runtime_error(\"load doors can't be moved\");\n\n        ensureCustomData(ptr);\n        DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();\n        customData.mDoorState = state;\n    }\n\n    void Door::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const\n    {\n        if (!state.mHasCustomState)\n            return;\n\n        ensureCustomData(ptr);\n        DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();\n        const ESM::DoorState& doorState = state.asDoorState();\n        customData.mDoorState = MWWorld::DoorState(doorState.mDoorState);\n    }\n\n    void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            state.mHasCustomState = false;\n            return;\n        }\n\n        const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();\n        ESM::DoorState& doorState = state.asDoorState();\n        doorState.mDoorState = int(customData.mDoorState);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/door.hpp",
    "content": "#ifndef GAME_MWCLASS_DOOR_H\n#define GAME_MWCLASS_DOOR_H\n\n#include <components/esm/loaddoor.hpp>\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Door : public MWWorld::Class\n    {\n            void ensureCustomData (const MWWorld::Ptr& ptr) const;\n\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            bool isDoor() const override;\n\n            bool useAnim() const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            static std::string getDestination (const MWWorld::LiveCellRef<ESM::Door>& door);\n            ///< @return destination cell name or token\n\n            bool canLock(const MWWorld::ConstPtr &ptr) const override;\n\n            bool allowTelekinesis(const MWWorld::ConstPtr &ptr) const override;\n            ///< Return whether this class of object can be activated with telekinesis\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            static void registerSelf();\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            MWWorld::DoorState getDoorState (const MWWorld::ConstPtr &ptr) const override;\n            /// This does not actually cause the door to move. Use World::activateDoor instead.\n            void setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const override;\n\n\n            void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const override;\n            ///< Read additional state from \\a state into \\a ptr.\n\n            void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const override;\n            ///< Write additional state from \\a ptr into \\a state.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/ingredient.cpp",
    "content": "#include \"ingredient.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadingr.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/actioneat.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\nnamespace MWClass\n{\n\n    void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Ingredient::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision || Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Ingredient::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Ingredient::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Ingredient::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Ingredient::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();\n\n        return ref->mBase->mScript;\n    }\n\n    int Ingredient::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n\n    std::shared_ptr<MWWorld::Action> Ingredient::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionEat (ptr));\n\n        action->setSound (\"Swallow\");\n\n        return action;\n    }\n\n    void Ingredient::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Ingredient);\n\n        registerClass (typeid (ESM::Ingredient).name(), instance);\n    }\n\n    std::string Ingredient::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Ingredient Up\");\n    }\n\n    std::string Ingredient::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Ingredient Down\");\n    }\n\n    std::string Ingredient::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        float alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);\n\n        static const float fWortChanceValue =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fWortChanceValue\")->mValue.getFloat();\n\n        MWGui::Widgets::SpellEffectList list;\n        for (int i=0; i<4; ++i)\n        {\n            if (ref->mBase->mData.mEffectID[i] < 0)\n                continue;\n            MWGui::Widgets::SpellEffectParams params;\n            params.mEffectID = ref->mBase->mData.mEffectID[i];\n            params.mAttribute = ref->mBase->mData.mAttributes[i];\n            params.mSkill = ref->mBase->mData.mSkills[i];\n\n            params.mKnown = ( (i == 0 && alchemySkill >= fWortChanceValue)\n                 || (i == 1 && alchemySkill >= fWortChanceValue*2)\n                 || (i == 2 && alchemySkill >= fWortChanceValue*3)\n                 || (i == 3 && alchemySkill >= fWortChanceValue*4));\n\n            list.push_back(params);\n        }\n        info.effects = list;\n\n        info.text = text;\n        info.isIngredient = true;\n\n        return info;\n    }\n\n    MWWorld::Ptr Ingredient::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    bool Ingredient::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Ingredients) != 0;\n    }\n\n\n    float Ingredient::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/ingredient.hpp",
    "content": "#ifndef GAME_MWCLASS_INGREDIENT_H\n#define GAME_MWCLASS_INGREDIENT_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Ingredient : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n            \n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/itemlevlist.cpp",
    "content": "#include \"itemlevlist.hpp\"\n\n#include <components/esm/loadlevlist.hpp>\n\nnamespace MWClass\n{\n\n    std::string ItemLevList::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        return \"\";\n    }\n\n    bool ItemLevList::hasToolTip(const MWWorld::ConstPtr& ptr) const\n    {\n        return false;\n    }\n\n    void ItemLevList::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new ItemLevList);\n\n        registerClass (typeid (ESM::ItemLevList).name(), instance);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/itemlevlist.hpp",
    "content": "#ifndef GAME_MWCLASS_ITEMLEVLIST_H\n#define GAME_MWCLASS_ITEMLEVLIST_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class ItemLevList : public MWWorld::Class\n    {\n        public:\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            bool hasToolTip (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            static void registerSelf();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/light.cpp",
    "content": "#include \"light.hpp\"\n\n#include <components/esm/loadligh.hpp>\n#include <components/esm/objectstate.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n#include \"../mwworld/failedaction.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\nnamespace MWClass\n{\n\n    void Light::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        MWWorld::LiveCellRef<ESM::Light> *ref =\n            ptr.get<ESM::Light>();\n\n        // Insert even if model is empty, so that the light is added\n        renderingInterface.getObjects().insertModel(ptr, model, true, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault));\n    }\n\n    void Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        MWWorld::LiveCellRef<ESM::Light> *ref =\n            ptr.get<ESM::Light>();\n        assert (ref->mBase != nullptr);\n\n        // TODO: add option somewhere to enable collision for placeable objects\n        if (!model.empty() && (ref->mBase->mData.mFlags & ESM::Light::Carry) == 0)\n            physics.addObject(ptr, model);\n\n        if (!ref->mBase->mSound.empty() && !(ref->mBase->mData.mFlags & ESM::Light::OffDefault))\n            MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0,\n                                                                      MWSound::Type::Sfx,\n                                                                      MWSound::PlayMode::Loop);\n    }\n\n    bool Light::useAnim() const\n    {\n        return true;\n    }\n\n    std::string Light::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Light::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n\n        if (ref->mBase->mModel.empty())\n            return std::string();\n\n        const std::string& name = ref->mBase->mName;\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Light::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        if(!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::NullAction());\n\n        MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n        if(!(ref->mBase->mData.mFlags&ESM::Light::Carry))\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction());\n\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Light::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n\n        return ref->mBase->mScript;\n    }\n\n    std::pair<std::vector<int>, bool> Light::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n\n        std::vector<int> slots_;\n\n        if (ref->mBase->mData.mFlags & ESM::Light::Carry)\n            slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedLeft));\n\n        return std::make_pair (slots_, false);\n    }\n\n    int Light::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Light::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Light);\n\n        registerClass (typeid (ESM::Light).name(), instance);\n    }\n\n    std::string Light::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Misc Up\");\n    }\n\n    std::string Light::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Misc Down\");\n    }\n\n\n    std::string Light::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n\n        return ref->mBase->mIcon;\n    }\n\n    bool Light::hasToolTip (const MWWorld::ConstPtr& ptr) const\n    {\n        return showsInInventory(ptr);\n    }\n\n    MWGui::ToolTipInfo Light::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        // Don't show duration for infinite light sources.\n        if (Settings::Manager::getBool(\"show effect duration\",\"Game\") && ptr.getClass().getRemainingUsageTime(ptr) != -1)\n            text += MWGui::ToolTips::getDurationString(ptr.getClass().getRemainingUsageTime(ptr), \"\\n#{sDuration}\");\n\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.text = text;\n\n        return info;\n    }\n\n    bool Light::showsInInventory (const MWWorld::ConstPtr& ptr) const\n    {\n        const ESM::Light* light = ptr.get<ESM::Light>()->mBase;\n\n        if (!(light->mData.mFlags & ESM::Light::Carry))\n            return false;\n\n        return Class::showsInInventory(ptr);\n    }\n\n    std::shared_ptr<MWWorld::Action> Light::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));\n\n        action->setSound(getUpSoundId(ptr));\n\n        return action;\n    }\n\n    void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const\n    {\n        ptr.getCellRef().setChargeFloat(duration);\n    }\n\n    float Light::getRemainingUsageTime (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n        if (ptr.getCellRef().getCharge() == -1)\n            return static_cast<float>(ref->mBase->mData.mTime);\n        else\n            return ptr.getCellRef().getChargeFloat();\n    }\n\n    MWWorld::Ptr Light::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    bool Light::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Lights) != 0;\n    }\n\n    float Light::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n        return ref->mBase->mData.mWeight;\n    }\n\n    std::pair<int, std::string> Light::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const\n    {\n        const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();\n        if (!(ref->mBase->mData.mFlags & ESM::Light::Carry))\n            return std::make_pair(0,\"\");\n\n        return std::make_pair(1,\"\");\n    }\n\n    std::string Light::getSound(const MWWorld::ConstPtr& ptr) const\n    {\n      return ptr.get<ESM::Light>()->mBase->mSound;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/light.hpp",
    "content": "#ifndef GAME_MWCLASS_LIGHT_H\n#define GAME_MWCLASS_LIGHT_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Light : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            bool useAnim() const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            bool hasToolTip (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            bool showsInInventory (const MWWorld::ConstPtr& ptr) const override;\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return first: Return IDs of the slot this object can be equipped in; second: can object\n            /// stay stacked when equipped?\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const override;\n            ///< Sets the remaining duration of the object.\n\n            float getRemainingUsageTime (const MWWorld::ConstPtr& ptr) const override;\n            ///< Returns the remaining duration of the object.\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n\n            std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const override;\n\n            std::string getSound(const MWWorld::ConstPtr& ptr) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/lockpick.cpp",
    "content": "#include \"lockpick.hpp\"\n\n#include <components/esm/loadlock.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\nnamespace MWClass\n{\n\n    void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Lockpick::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n    }\n\n    std::string Lockpick::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Lockpick::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Lockpick::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n\n        return ref->mBase->mScript;\n    }\n\n    std::pair<std::vector<int>, bool> Lockpick::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const\n    {\n        std::vector<int> slots_;\n\n        slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));\n\n        return std::make_pair (slots_, false);\n    }\n\n    int Lockpick::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Lockpick::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Lockpick);\n\n        registerClass (typeid (ESM::Lockpick).name(), instance);\n    }\n\n    std::string Lockpick::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Lockpick Up\");\n    }\n\n    std::string Lockpick::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Lockpick Down\");\n    }\n\n    std::string Lockpick::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        int remainingUses = getItemHealth(ptr);\n\n        text += \"\\n#{sUses}: \" + MWGui::ToolTips::toString(remainingUses);\n        text += \"\\n#{sQuality}: \" + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.text = text;\n\n        return info;\n    }\n\n    std::shared_ptr<MWWorld::Action> Lockpick::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));\n\n        action->setSound(getUpSoundId(ptr));\n\n        return action;\n    }\n\n    MWWorld::Ptr Lockpick::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    std::pair<int, std::string> Lockpick::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const\n    {\n        // Do not allow equip tools from inventory during attack\n        if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)\n            && MWBase::Environment::get().getWindowManager()->isGuiMode())\n            return std::make_pair(0, \"#{sCantEquipWeapWarning}\");\n\n        return std::make_pair(1, \"\");\n    }\n\n    bool Lockpick::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Picks) != 0;\n    }\n\n    int Lockpick::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n\n        return ref->mBase->mData.mUses;\n    }\n\n    float Lockpick::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/lockpick.hpp",
    "content": "#ifndef GAME_MWCLASS_LOCKPICK_H\n#define GAME_MWCLASS_LOCKPICK_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Lockpick : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return first: Return IDs of the slot this object can be equipped in; second: can object\n            /// stay stacked when equipped?\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const override;\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return item max health or throw an exception, if class does not have item health\n\n            bool hasItemHealth (const MWWorld::ConstPtr& ptr) const override { return true; }\n            ///< \\return Item health data available? (default implementation: false)\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/misc.cpp",
    "content": "#include \"misc.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadmisc.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n#include \"../mwworld/actionsoulgem.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\nnamespace MWClass\n{\n    bool Miscellaneous::isGold (const MWWorld::ConstPtr& ptr) const\n    {\n        return Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), \"gold_001\")\n                        || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), \"gold_005\")\n                        || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), \"gold_010\")\n                        || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), \"gold_025\")\n                        || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), \"gold_100\");\n    }\n\n    void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision || Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Miscellaneous::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Miscellaneous::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Miscellaneous::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();\n\n        return ref->mBase->mScript;\n    }\n\n    int Miscellaneous::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();\n\n        int value = ref->mBase->mData.mValue;\n        if (ptr.getCellRef().getGoldValue() > 1 && ptr.getRefData().getCount() == 1)\n            value = ptr.getCellRef().getGoldValue();\n\n        if (ptr.getCellRef().getSoul() != \"\")\n        {\n            const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(ref->mRef.getSoul());\n            if (creature)\n            {\n                int soul = creature->mData.mSoul;\n                if (Settings::Manager::getBool(\"rebalance soul gem values\", \"Game\"))\n                {\n                    // use the 'soul gem value rebalance' formula from the Morrowind Code Patch \n                    float soulValue = 0.0001 * pow(soul, 3) + 2 * soul;\n                    \n                    // for Azura's star add the unfilled value\n                    if (Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), \"Misc_SoulGem_Azura\"))\n                        value += soulValue;\n                    else\n                        value = soulValue;\n                }\n                else\n                    value *= soul;\n            }\n        }\n\n        return value;\n    }\n\n    void Miscellaneous::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Miscellaneous);\n\n        registerClass (typeid (ESM::Miscellaneous).name(), instance);\n    }\n\n    std::string Miscellaneous::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        if (isGold(ptr))\n            return std::string(\"Item Gold Up\");\n        return std::string(\"Item Misc Up\");\n    }\n\n    std::string Miscellaneous::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        if (isGold(ptr))\n            return std::string(\"Item Gold Down\");\n        return std::string(\"Item Misc Down\");\n    }\n\n    std::string Miscellaneous::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();\n\n        MWGui::ToolTipInfo info;\n\n        bool gold = isGold(ptr);\n        if (gold)\n            count *= getValue(ptr);\n\n        std::string countString;\n        if (!gold)\n            countString = MWGui::ToolTips::getCountString(count);\n        else // gold displays its count also if it's 1.\n            countString = \" (\" + std::to_string(count) + \")\";\n\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + countString + MWGui::ToolTips::getSoulString(ptr.getCellRef());\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        if (!gold && !ref->mBase->mData.mIsKey)\n            text += MWGui::ToolTips::getValueString(getValue(ptr), \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.text = text;\n\n        return info;\n    }\n\n    MWWorld::Ptr Miscellaneous::copyToCell(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell, int count) const\n    {\n        MWWorld::Ptr newPtr;\n\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        if (isGold(ptr)) {\n            int goldAmount = getValue(ptr) * count;\n\n            std::string base = \"Gold_001\";\n            if (goldAmount >= 100)\n                base = \"Gold_100\";\n            else if (goldAmount >= 25)\n                base = \"Gold_025\";\n            else if (goldAmount >= 10)\n                base = \"Gold_010\";\n            else if (goldAmount >= 5)\n                base = \"Gold_005\";\n\n            // Really, I have no idea why moving ref out of conditional\n            // scope causes list::push_back throwing std::bad_alloc\n            MWWorld::ManualRef newRef(store, base);\n            const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref =\n                newRef.getPtr().get<ESM::Miscellaneous>();\n\n            newPtr = MWWorld::Ptr(cell.insert(ref), &cell);\n            newPtr.getCellRef().setGoldValue(goldAmount);\n            newPtr.getRefData().setCount(1);\n        } else {\n            const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref =\n                ptr.get<ESM::Miscellaneous>();\n            newPtr = MWWorld::Ptr(cell.insert(ref), &cell);\n            newPtr.getRefData().setCount(count);\n        }\n        newPtr.getCellRef().unsetRefNum();\n\n        return newPtr;\n    }\n\n    std::shared_ptr<MWWorld::Action> Miscellaneous::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        if (ptr.getCellRef().getSoul().empty() || !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(ptr.getCellRef().getSoul()))\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::NullAction());\n        else\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionSoulgem(ptr));\n    }\n\n    bool Miscellaneous::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = item.get<ESM::Miscellaneous>();\n\n        return !ref->mBase->mData.mIsKey && (npcServices & ESM::NPC::Misc) && !isGold(item);\n    }\n\n    float Miscellaneous::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();\n        return ref->mBase->mData.mWeight;\n    }\n\n    bool Miscellaneous::isKey(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();\n        return ref->mBase->mData.mIsKey != 0;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/misc.hpp",
    "content": "#ifndef GAME_MWCLASS_MISC_H\n#define GAME_MWCLASS_MISC_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Miscellaneous : public MWWorld::Class\n    {\n        public:\n\n            MWWorld::Ptr copyToCell(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell, int count) const override;\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n\n            bool isKey (const MWWorld::ConstPtr &ptr) const override;\n\n            bool isGold (const MWWorld::ConstPtr& ptr) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/npc.cpp",
    "content": "﻿#include \"npc.hpp\"\n\n#include <memory>\n\n#include <components/misc/constants.hpp>\n#include <components/misc/rng.hpp>\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/loadmgef.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/npcstate.hpp>\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n#include \"../mwmechanics/disease.hpp\"\n#include \"../mwmechanics/combat.hpp\"\n#include \"../mwmechanics/autocalcspell.hpp\"\n#include \"../mwmechanics/difficultyscaling.hpp\"\n#include \"../mwmechanics/weapontype.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actiontalk.hpp\"\n#include \"../mwworld/actionopen.hpp\"\n#include \"../mwworld/failedaction.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/customdata.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/localscripts.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n#include \"../mwrender/npcanimation.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\nnamespace\n{\n\n    int is_even(double d) {\n        double int_part;\n        modf(d / 2.0, &int_part);\n        return 2.0 * int_part == d;\n    }\n\n    int round_ieee_754(double d) {\n        double i = floor(d);\n        d -= i;\n        if(d < 0.5)\n            return static_cast<int>(i);\n        if(d > 0.5)\n            return static_cast<int>(i) + 1;\n        if(is_even(i))\n            return static_cast<int>(i);\n        return static_cast<int>(i) + 1;\n    }\n\n    void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats)\n    {\n        // race bonus\n        const ESM::Race *race =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc->mRace);\n\n        bool male = (npc->mFlags & ESM::NPC::Female) == 0;\n\n        int level = creatureStats.getLevel();\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n        {\n            const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i];\n            creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale);\n        }\n\n        // class bonus\n        const ESM::Class *class_ =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);\n\n        for (int i=0; i<2; ++i)\n        {\n            int attribute = class_->mData.mAttribute[i];\n            if (attribute>=0 && attribute<8)\n            {\n                creatureStats.setAttribute(attribute,\n                    creatureStats.getAttribute(attribute).getBase() + 10);\n            }\n        }\n\n        // skill bonus\n        for (int attribute=0; attribute < ESM::Attribute::Length; ++attribute)\n        {\n            float modifierSum = 0;\n\n            for (int j=0; j<ESM::Skill::Length; ++j)\n            {\n                const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(j);\n\n                if (skill->mData.mAttribute != attribute)\n                    continue;\n\n                // is this a minor or major skill?\n                float add=0.2f;\n                for (int k=0; k<5; ++k)\n                {\n                    if (class_->mData.mSkills[k][0] == j)\n                        add=0.5;\n                }\n                for (int k=0; k<5; ++k)\n                {\n                    if (class_->mData.mSkills[k][1] == j)\n                        add=1.0;\n                }\n                modifierSum += add;\n            }\n            creatureStats.setAttribute(attribute, std::min(\n                                           round_ieee_754(creatureStats.getAttribute(attribute).getBase()\n                + (level-1) * modifierSum), 100) );\n        }\n\n        // initial health\n        float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();\n        float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();\n\n        int multiplier = 3;\n\n        if (class_->mData.mSpecialization == ESM::Class::Combat)\n            multiplier += 2;\n        else if (class_->mData.mSpecialization == ESM::Class::Stealth)\n            multiplier += 1;\n\n        if (class_->mData.mAttribute[0] == ESM::Attribute::Endurance\n            || class_->mData.mAttribute[1] == ESM::Attribute::Endurance)\n            multiplier += 1;\n\n        creatureStats.setHealth(floor(0.5f * (strength + endurance)) + multiplier * (creatureStats.getLevel() - 1));\n    }\n\n    /**\n     * @brief autoCalculateSkills\n     *\n     * Skills are calculated with following formulae ( http://www.uesp.net/wiki/Morrowind:NPCs#Skills ):\n     *\n     * Skills: (Level - 1) × (Majority Multiplier + Specialization Multiplier)\n     *\n     *         The Majority Multiplier is 1.0 for a Major or Minor Skill, or 0.1 for a Miscellaneous Skill.\n     *\n     *         The Specialization Multiplier is 0.5 for a Skill in the same Specialization as the class,\n     *         zero for other Skills.\n     *\n     * and by adding class, race, specialization bonus.\n     */\n    void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats, const MWWorld::Ptr& ptr, bool spellsInitialised)\n    {\n        const ESM::Class *class_ =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);\n\n        unsigned int level = npcStats.getLevel();\n\n        const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc->mRace);\n\n\n        for (int i = 0; i < 2; ++i)\n        {\n            int bonus = (i==0) ? 10 : 25;\n\n            for (int i2 = 0; i2 < 5; ++i2)\n            {\n                int index = class_->mData.mSkills[i2][i];\n                if (index >= 0 && index < ESM::Skill::Length)\n                {\n                    npcStats.getSkill(index).setBase (npcStats.getSkill(index).getBase() + bonus);\n                }\n            }\n        }\n\n        for (int skillIndex = 0; skillIndex < ESM::Skill::Length; ++skillIndex)\n        {\n            float majorMultiplier = 0.1f;\n            float specMultiplier = 0.0f;\n\n            int raceBonus = 0;\n            int specBonus = 0;\n\n            for (int raceSkillIndex = 0; raceSkillIndex < 7; ++raceSkillIndex)\n            {\n                if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex)\n                {\n                    raceBonus = race->mData.mBonus[raceSkillIndex].mBonus;\n                    break;\n                }\n            }\n\n            for (int k = 0; k < 5; ++k)\n            {\n                // is this a minor or major skill?\n                if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex))\n                {\n                    majorMultiplier = 1.0f;\n                    break;\n                }\n            }\n\n            // is this skill in the same Specialization as the class?\n            const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(skillIndex);\n            if (skill->mData.mSpecialization == class_->mData.mSpecialization)\n            {\n                specMultiplier = 0.5f;\n                specBonus = 5;\n            }\n\n            npcStats.getSkill(skillIndex).setBase(\n                  std::min(\n                    round_ieee_754(\n                            npcStats.getSkill(skillIndex).getBase()\n                    + 5\n                    + raceBonus\n                    + specBonus\n                    +(int(level)-1) * (majorMultiplier + specMultiplier)), 100)); // Must gracefully handle level 0\n        }\n\n        int skills[ESM::Skill::Length];\n        for (int i=0; i<ESM::Skill::Length; ++i)\n            skills[i] = npcStats.getSkill(i).getBase();\n\n        int attributes[ESM::Attribute::Length];\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            attributes[i] = npcStats.getAttribute(i).getBase();\n\n        if (!spellsInitialised)\n        {\n            std::vector<std::string> spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race);\n            npcStats.getSpells().addAllToInstance(spells);\n        }\n    }\n}\n\nnamespace MWClass\n{\n\n    class NpcCustomData : public MWWorld::TypedCustomData<NpcCustomData>\n    {\n    public:\n        MWMechanics::NpcStats mNpcStats;\n        MWMechanics::Movement mMovement;\n        MWWorld::InventoryStore mInventoryStore;\n\n        NpcCustomData& asNpcCustomData() override\n        {\n            return *this;\n        }\n        const NpcCustomData& asNpcCustomData() const override\n        {\n            return *this;\n        }\n    };\n\n    const Npc::GMST& Npc::getGmst()\n    {\n        static GMST gmst;\n        static bool inited = false;\n        if(!inited)\n        {\n            const MWBase::World *world = MWBase::Environment::get().getWorld();\n            const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();\n\n            gmst.fMinWalkSpeed = store.find(\"fMinWalkSpeed\");\n            gmst.fMaxWalkSpeed = store.find(\"fMaxWalkSpeed\");\n            gmst.fEncumberedMoveEffect = store.find(\"fEncumberedMoveEffect\");\n            gmst.fSneakSpeedMultiplier = store.find(\"fSneakSpeedMultiplier\");\n            gmst.fAthleticsRunBonus = store.find(\"fAthleticsRunBonus\");\n            gmst.fBaseRunMultiplier = store.find(\"fBaseRunMultiplier\");\n            gmst.fMinFlySpeed = store.find(\"fMinFlySpeed\");\n            gmst.fMaxFlySpeed = store.find(\"fMaxFlySpeed\");\n            gmst.fSwimRunBase = store.find(\"fSwimRunBase\");\n            gmst.fSwimRunAthleticsMult = store.find(\"fSwimRunAthleticsMult\");\n            gmst.fJumpEncumbranceBase = store.find(\"fJumpEncumbranceBase\");\n            gmst.fJumpEncumbranceMultiplier = store.find(\"fJumpEncumbranceMultiplier\");\n            gmst.fJumpAcrobaticsBase = store.find(\"fJumpAcrobaticsBase\");\n            gmst.fJumpAcroMultiplier = store.find(\"fJumpAcroMultiplier\");\n            gmst.fJumpRunMultiplier = store.find(\"fJumpRunMultiplier\");\n            gmst.fWereWolfRunMult = store.find(\"fWereWolfRunMult\");\n            gmst.fKnockDownMult = store.find(\"fKnockDownMult\");\n            gmst.iKnockDownOddsMult = store.find(\"iKnockDownOddsMult\");\n            gmst.iKnockDownOddsBase = store.find(\"iKnockDownOddsBase\");\n            gmst.fCombatArmorMinMult = store.find(\"fCombatArmorMinMult\");\n\n            inited = true;\n        }\n        return gmst;\n    }\n\n    void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            std::unique_ptr<NpcCustomData> data(new NpcCustomData);\n\n            MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n\n            bool spellsInitialised = data->mNpcStats.getSpells().setSpells(ref->mBase->mId);\n\n            // creature stats\n            int gold=0;\n            if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)\n            {\n                gold = ref->mBase->mNpdt.mGold;\n\n                for (unsigned int i=0; i< ESM::Skill::Length; ++i)\n                    data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt.mSkills[i]);\n\n                data->mNpcStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mNpdt.mStrength);\n                data->mNpcStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mNpdt.mIntelligence);\n                data->mNpcStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mNpdt.mWillpower);\n                data->mNpcStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mNpdt.mAgility);\n                data->mNpcStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mNpdt.mSpeed);\n                data->mNpcStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mNpdt.mEndurance);\n                data->mNpcStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mNpdt.mPersonality);\n                data->mNpcStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mNpdt.mLuck);\n\n                data->mNpcStats.setHealth (ref->mBase->mNpdt.mHealth);\n                data->mNpcStats.setMagicka (ref->mBase->mNpdt.mMana);\n                data->mNpcStats.setFatigue (ref->mBase->mNpdt.mFatigue);\n\n                data->mNpcStats.setLevel(ref->mBase->mNpdt.mLevel);\n                data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt.mDisposition);\n                data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation);\n\n                data->mNpcStats.setNeedRecalcDynamicStats(false);\n            }\n            else\n            {\n                gold = ref->mBase->mNpdt.mGold;\n\n                for (int i=0; i<3; ++i)\n                    data->mNpcStats.setDynamic (i, 10);\n\n                data->mNpcStats.setLevel(ref->mBase->mNpdt.mLevel);\n                data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt.mDisposition);\n                data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation);\n\n                autoCalculateAttributes(ref->mBase, data->mNpcStats);\n                autoCalculateSkills(ref->mBase, data->mNpcStats, ptr, spellsInitialised);\n\n                data->mNpcStats.setNeedRecalcDynamicStats(true);\n            }\n\n            // Persistent actors with 0 health do not play death animation\n            if (data->mNpcStats.isDead())\n                data->mNpcStats.setDeathAnimationFinished(isPersistent(ptr));\n\n            // race powers\n            const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);\n            data->mNpcStats.getSpells().addAllToInstance(race->mPowers.mList);\n\n            if (!ref->mBase->mFaction.empty())\n            {\n                static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                        .find(\"iAutoRepFacMod\")->mValue.getInteger();\n                static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                        .find(\"iAutoRepLevMod\")->mValue.getInteger();\n                int rank = ref->mBase->getFactionRank();\n\n                data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1));\n            }\n\n            data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage);\n\n            data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello);\n            data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, ref->mBase->mAiData.mFight);\n            data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee);\n            data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm);\n\n            // spells\n            if (!spellsInitialised)\n                data->mNpcStats.getSpells().addAllToInstance(ref->mBase->mSpells.mList);\n\n            // inventory\n            // setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items\n            data->mInventoryStore.fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());\n\n            data->mNpcStats.setGoldPool(gold);\n\n            // store\n            ptr.getRefData().setCustomData(std::move(data));\n\n            getInventoryStore(ptr).autoEquip(ptr);\n        }\n    }\n\n    void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        renderingInterface.getObjects().insertNPC(ptr);\n    }\n\n    bool Npc::isPersistent(const MWWorld::ConstPtr &actor) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC>* ref = actor.get<ESM::NPC>();\n        return ref->mBase->mPersistent;\n    }\n\n    std::string Npc::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n\n        std::string model = Settings::Manager::getString(\"baseanim\", \"Models\");\n        const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);\n        if(race->mData.mFlags & ESM::Race::Beast)\n            model = Settings::Manager::getString(\"baseanimkna\", \"Models\");\n\n        return model;\n    }\n\n    void Npc::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *npc = ptr.get<ESM::NPC>();\n        const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().search(npc->mBase->mRace);\n        if(race && race->mData.mFlags & ESM::Race::Beast)\n            models.emplace_back(Settings::Manager::getString(\"baseanimkna\", \"Models\"));\n\n        // keep these always loaded just in case\n        models.emplace_back(Settings::Manager::getString(\"xargonianswimkna\", \"Models\"));\n        models.emplace_back(Settings::Manager::getString(\"xbaseanimfemale\", \"Models\"));\n        models.emplace_back(Settings::Manager::getString(\"xbaseanim\", \"Models\"));\n\n        if (!npc->mBase->mModel.empty())\n            models.push_back(\"meshes/\"+npc->mBase->mModel);\n\n        if (!npc->mBase->mHead.empty())\n        {\n            const ESM::BodyPart* head = MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>().search(npc->mBase->mHead);\n            if (head)\n                models.push_back(\"meshes/\"+head->mModel);\n        }\n        if (!npc->mBase->mHair.empty())\n        {\n            const ESM::BodyPart* hair = MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>().search(npc->mBase->mHair);\n            if (hair)\n                models.push_back(\"meshes/\"+hair->mModel);\n        }\n\n        bool female = (npc->mBase->mFlags & ESM::NPC::Female);\n\n        // FIXME: use const version of InventoryStore functions once they are available\n        // preload equipped items\n        const MWWorld::InventoryStore& invStore = getInventoryStore(ptr);\n        for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)\n        {\n            MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);\n            if (equipped != invStore.end())\n            {\n                std::vector<ESM::PartReference> parts;\n                if(equipped->getTypeName() == typeid(ESM::Clothing).name())\n                {\n                    const ESM::Clothing *clothes = equipped->get<ESM::Clothing>()->mBase;\n                    parts = clothes->mParts.mParts;\n                }\n                else if(equipped->getTypeName() == typeid(ESM::Armor).name())\n                {\n                    const ESM::Armor *armor = equipped->get<ESM::Armor>()->mBase;\n                    parts = armor->mParts.mParts;\n                }\n                else\n                {\n                    std::string model = equipped->getClass().getModel(*equipped);\n                    if (!model.empty())\n                        models.push_back(model);\n                }\n\n                for (std::vector<ESM::PartReference>::const_iterator it = parts.begin(); it != parts.end(); ++it)\n                {\n                    std::string partname = female ? it->mFemale : it->mMale;\n                    if (partname.empty())\n                        partname = female ? it->mMale : it->mFemale;\n                    const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>().search(partname);\n                    if (part && !part->mModel.empty())\n                        models.push_back(\"meshes/\"+part->mModel);\n                }\n            }\n        }\n\n        // preload body parts\n        if (race)\n        {\n            const std::vector<const ESM::BodyPart*>& parts = MWRender::NpcAnimation::getBodyParts(Misc::StringUtils::lowerCase(race->mId), female, false, false);\n            for (std::vector<const ESM::BodyPart*>::const_iterator it = parts.begin(); it != parts.end(); ++it)\n            {\n                const ESM::BodyPart* part = *it;\n                if (part && !part->mModel.empty())\n                    models.push_back(\"meshes/\"+part->mModel);\n            }\n        }\n\n    }\n\n    std::string Npc::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        if(ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf())\n        {\n            const MWBase::World *world = MWBase::Environment::get().getWorld();\n            const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();\n\n            return store.find(\"sWerewolfPopup\")->mValue.getString();\n        }\n\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const\n    {\n        ensureCustomData (ptr);\n\n        return ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats;\n    }\n\n    MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const\n    {\n        ensureCustomData (ptr);\n\n        return ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats;\n    }\n\n\n    void Npc::hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const\n    {\n        /*\n            Start of tes3mp addition\n\n            Ignore hit calculations on this client from DedicatedPlayers and DedicatedActors\n        */\n        if (mwmp::PlayerList::isDedicatedPlayer(ptr) || mwmp::Main::get().getCellController()->isDedicatedActor(ptr))\n        {\n            return;\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n\n        const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();\n\n        // Get the weapon used (if hand-to-hand, weapon = inv.end())\n        MWWorld::InventoryStore &inv = getInventoryStore(ptr);\n        MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n        MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr());\n        if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())\n            weapon = MWWorld::Ptr();\n\n        MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);\n\n        const float fCombatDistance = store.find(\"fCombatDistance\")->mValue.getFloat();\n        float dist = fCombatDistance * (!weapon.isEmpty() ?\n                               weapon.get<ESM::Weapon>()->mBase->mData.mReach :\n                               store.find(\"fHandToHandReach\")->mValue.getFloat());\n\n        // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.\n        std::vector<MWWorld::Ptr> targetActors;\n        if (ptr != MWMechanics::getPlayer())\n            getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors);\n\n        // TODO: Use second to work out the hit angle\n        std::pair<MWWorld::Ptr, osg::Vec3f> result = world->getHitContact(ptr, dist, targetActors);\n        MWWorld::Ptr victim = result.first;\n        osg::Vec3f hitPosition (result.second);\n        if(victim.isEmpty()) // Didn't hit anything\n            return;\n\n        const MWWorld::Class &othercls = victim.getClass();\n        /*\n            Start of tes3mp change (major)\n\n            Send an ID_OBJECT_HIT packet when hitting non-actors instead of\n            just returning\n        */\n        if(!othercls.isActor())\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectHit(victim, ptr);\n            objectList->sendObjectHit();\n            return;\n        }\n        /*\n            End of tes3mp change (major)\n        */\n        MWMechanics::CreatureStats &otherstats = othercls.getCreatureStats(victim);\n        if(otherstats.isDead()) // Can't hit dead actors\n            return;\n\n        if(ptr == MWMechanics::getPlayer())\n            MWBase::Environment::get().getWindowManager()->setEnemy(victim);\n\n        int weapskill = ESM::Skill::HandToHand;\n        if(!weapon.isEmpty())\n            weapskill = weapon.getClass().getEquipmentSkill(weapon);\n\n        float hitchance = MWMechanics::getHitChance(ptr, victim, getSkill(ptr, weapskill));\n\n        /*\n            Start of tes3mp addition\n\n            If the attacker is a LocalPlayer or LocalActor, get their Attack to assign its\n            hit position and target\n        */\n        mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(ptr);\n\n        if (localAttack)\n        {\n            localAttack->isHit = true;\n            localAttack->success = true;\n            localAttack->hitPosition = MechanicsHelper::getPositionFromVector(hitPosition);\n            MechanicsHelper::assignAttackTarget(localAttack, victim);\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        if (Misc::Rng::roll0to99() >= hitchance)\n        {\n            /*\n                Start of tes3mp addition\n\n                If this was a failed attack by the LocalPlayer or LocalActor, send a\n                packet about it\n\n                Send an ID_OBJECT_HIT about it as well\n            */\n            if (localAttack)\n            {\n                localAttack->pressed = false;\n                localAttack->success = false;\n                localAttack->shouldSend = true;\n\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectHit(victim, ptr, *localAttack);\n                objectList->sendObjectHit();\n            }\n            /*\n                End of tes3mp addition\n            */\n\n            othercls.onHit(victim, 0.0f, false, weapon, ptr, osg::Vec3f(), false);\n            MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);\n            return;\n        }\n\n        bool healthdmg;\n        float damage = 0.0f;\n        if(!weapon.isEmpty())\n        {\n            const unsigned char *attack = nullptr;\n            if(type == ESM::Weapon::AT_Chop)\n                attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;\n            else if(type == ESM::Weapon::AT_Slash)\n                attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;\n            else if(type == ESM::Weapon::AT_Thrust)\n                attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;\n            if(attack)\n            {\n                damage  = attack[0] + ((attack[1]-attack[0])*attackStrength);\n            }\n            MWMechanics::adjustWeaponDamage(damage, weapon, ptr);\n            MWMechanics::resistNormalWeapon(victim, ptr, weapon, damage);\n            MWMechanics::applyWerewolfDamageMult(victim, weapon, damage);\n            MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);\n            healthdmg = true;\n        }\n        else\n        {\n            MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg, attackStrength);\n        }\n        if(ptr == MWMechanics::getPlayer())\n        {\n            skillUsageSucceeded(ptr, weapskill, 0);\n\n            const MWMechanics::AiSequence& seq = victim.getClass().getCreatureStats(victim).getAiSequence();\n\n            bool unaware = !seq.isInCombat()\n                    && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);\n            if(unaware)\n            {\n                damage *= store.find(\"fCombatCriticalStrikeMult\")->mValue.getFloat();\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sTargetCriticalStrike}\");\n                MWBase::Environment::get().getSoundManager()->playSound3D(victim, \"critical damage\", 1.0f, 1.0f);\n            }\n        }\n\n        if (othercls.getCreatureStats(victim).getKnockedDown())\n            damage *= store.find(\"fCombatKODamageMult\")->mValue.getFloat();\n\n        // Apply \"On hit\" enchanted weapons\n\n        /*\n            Start of tes3mp change (minor)\n\n            Track whether the strike enchantment is successful for attacks by the\n            LocalPlayer or LocalActors\n        */\n        bool appliedEnchantment = MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition);\n\n        if (localAttack)\n            localAttack->applyWeaponEnchantment = appliedEnchantment;\n        /*\n            End of tes3mp change (minor)\n        */\n\n        MWMechanics::applyElementalShields(ptr, victim);\n\n        if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))\n            damage = 0;\n\n        if (victim == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())\n            damage = 0;\n\n        MWMechanics::diseaseContact(victim, ptr);\n\n        othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);\n    }\n\n    void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const\n    {\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n        bool wasDead = stats.isDead();\n\n        // Note OnPcHitMe is not set for friendly hits.\n        bool setOnPcHitMe = true;\n\n        // NOTE: 'object' and/or 'attacker' may be empty.\n        if (!attacker.isEmpty() && attacker.getClass().isActor() && !stats.getAiSequence().isInCombat(attacker))\n        {\n            stats.setAttacked(true);\n            setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);\n        }\n\n        // Attacker and target store each other as hitattemptactor if they have no one stored yet\n        if (!attacker.isEmpty() && attacker.getClass().isActor())\n        {\n            MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker);\n\n            /*\n                Start of tes3mp change (minor)\n\n                Instead of only checking whether an attacker is the LocalPlayer, also\n                check if they are a DedicatedPlayer\n\n                Additionally, if the two players are on each other's team, don't track\n                their hits\n            */\n\n            // First handle the attacked actor\n            if ((stats.getHitAttemptActorId() == -1)\n                && (statsAttacker.getAiSequence().isInCombat(ptr)\n                    || attacker == MWMechanics::getPlayer()\n                    || mwmp::PlayerList::isDedicatedPlayer(attacker))\n                && !MechanicsHelper::isTeamMember(attacker, ptr))\n                stats.setHitAttemptActorId(statsAttacker.getActorId());\n\n            // Next handle the attacking actor\n            if ((statsAttacker.getHitAttemptActorId() == -1)\n                && (statsAttacker.getAiSequence().isInCombat(ptr)\n                    || attacker == MWMechanics::getPlayer()\n                    || mwmp::PlayerList::isDedicatedPlayer(attacker))\n                && !MechanicsHelper::isTeamMember(ptr, attacker))\n                statsAttacker.setHitAttemptActorId(stats.getActorId());\n\n            /*\n                End of tes3mp change (minor)\n            */\n        }\n\n        if (!object.isEmpty())\n            stats.setLastHitAttemptObject(object.getCellRef().getRefId());\n\n        if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())\n        {\n            const std::string &script = getScript(ptr);\n            /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */\n            if(!script.empty())\n                ptr.getRefData().getLocals().setVarByInt(script, \"onpchitme\", 1);\n        }\n\n        if (!successful)\n        {\n            // Missed\n            if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())\n                sndMgr->playSound3D(ptr, \"miss\", 1.0f, 1.0f);\n            return;\n        }\n\n        if (!object.isEmpty())\n            stats.setLastHitObject(object.getCellRef().getRefId());\n\n\n        if (damage > 0.0f && !object.isEmpty())\n            MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);\n\n        if (damage < 0.001f)\n            damage = 0;\n\n        bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n\n        if (godmode)\n            damage = 0;\n\n        if (damage > 0.0f && !attacker.isEmpty())\n        {\n            // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying\n            // something, alert the character controller, scripts, etc.\n\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            const GMST& gmst = getGmst();\n\n            int chance = store.get<ESM::GameSetting>().find(\"iVoiceHitOdds\")->mValue.getInteger();\n            if (Misc::Rng::roll0to99() < chance)\n                MWBase::Environment::get().getDialogueManager()->say(ptr, \"hit\");\n\n            // Check for knockdown\n            float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->mValue.getFloat();\n            float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()\n                    * gmst.iKnockDownOddsMult->mValue.getInteger() * 0.01f + gmst.iKnockDownOddsBase->mValue.getInteger();\n\n            /*\n                Start of tes3mp change (major)\n\n                If the attacker is a DedicatedPlayer or DedicatedActor with a successful knockdown, apply the knockdown\n\n                If the attacker is neither of those, then it must be a LocalPlayer or a LocalActor, so calculate the\n                knockdown probability on our client\n\n                Default to hit recovery if no knockdown has taken place, like in regular OpenMW\n            */\n            mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(attacker);\n\n            if (dedicatedAttack)\n            {\n                if (dedicatedAttack->knockdown)\n                    stats.setKnockedDown(true);\n            }\n            else\n            {\n                if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99())\n                    stats.setKnockedDown(true);\n            }\n\n            if (!stats.getKnockedDown())\n                stats.setHitRecovery(true); // Is this supposed to always occur?\n            /*\n                End of tes3mp change (major)\n            */\n\n            if (damage > 0 && ishealth)\n            {\n                // Hit percentages:\n                // cuirass = 30%\n                // shield, helmet, greaves, boots, pauldrons = 10% each\n                // guantlets = 5% each\n                static const int hitslots[20] = {\n                    MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,\n                    MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,\n                    MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,\n                    MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft,\n                    MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet,\n                    MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves,\n                    MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots,\n                    MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron,\n                    MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron,\n                    MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet\n                };\n                int hitslot = hitslots[Misc::Rng::rollDice(20)];\n\n                float unmitigatedDamage = damage;\n                float x = damage / (damage + getArmorRating(ptr));\n                damage *= std::max(gmst.fCombatArmorMinMult->mValue.getFloat(), x);\n                int damageDiff = static_cast<int>(unmitigatedDamage - damage);\n                damage = std::max(1.f, damage);\n                damageDiff = std::max(1, damageDiff);\n\n                MWWorld::InventoryStore &inv = getInventoryStore(ptr);\n                MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);\n                MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());\n                bool hasArmor = !armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name();\n                // If there's no item in the carried left slot or if it is not a shield redistribute the hit.\n                if (!hasArmor && hitslot == MWWorld::InventoryStore::Slot_CarriedLeft)\n                {\n                    if (Misc::Rng::rollDice(2) == 0)\n                        hitslot = MWWorld::InventoryStore::Slot_Cuirass;\n                    else\n                        hitslot = MWWorld::InventoryStore::Slot_LeftPauldron;\n                    armorslot = inv.getSlot(hitslot);\n                    if (armorslot != inv.end())\n                    {\n                        armor = *armorslot;\n                        hasArmor = !armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name();\n                    }\n                }\n                if (hasArmor)\n                {\n                    if (!object.isEmpty() || attacker.isEmpty() || attacker.getClass().isNpc()) // Unarmed creature attacks don't affect armor condition\n                    {\n                        int armorhealth = armor.getClass().getItemHealth(armor);\n                        armorhealth -= std::min(damageDiff, armorhealth);\n                        armor.getCellRef().setCharge(armorhealth);\n\n                        // Armor broken? unequip it\n                        if (armorhealth == 0)\n                            armor = *inv.unequipItem(armor, ptr);\n                    }\n\n                    if (ptr == MWMechanics::getPlayer())\n                        skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0);\n\n                    switch(armor.getClass().getEquipmentSkill(armor))\n                    {\n                        case ESM::Skill::LightArmor:\n                            sndMgr->playSound3D(ptr, \"Light Armor Hit\", 1.0f, 1.0f);\n                            break;\n                        case ESM::Skill::MediumArmor:\n                            sndMgr->playSound3D(ptr, \"Medium Armor Hit\", 1.0f, 1.0f);\n                            break;\n                        case ESM::Skill::HeavyArmor:\n                            sndMgr->playSound3D(ptr, \"Heavy Armor Hit\", 1.0f, 1.0f);\n                            break;\n                    }\n                }\n                else if(ptr == MWMechanics::getPlayer())\n                    skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0);\n            }\n        }\n\n        if (ishealth)\n        {\n            if (!attacker.isEmpty() && !godmode)\n                damage = scaleDamage(damage, attacker, ptr);\n\n            if (damage > 0.0f)\n            {\n                sndMgr->playSound3D(ptr, \"Health Damage\", 1.0f, 1.0f);\n                if (ptr == MWMechanics::getPlayer())\n                    MWBase::Environment::get().getWindowManager()->activateHitOverlay();\n                if (!attacker.isEmpty())\n                    MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition);\n            }\n            MWMechanics::DynamicStat<float> health(getCreatureStats(ptr).getHealth());\n            health.setCurrent(health.getCurrent() - damage);\n            stats.setHealth(health);\n        }\n        else\n        {\n            MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());\n            fatigue.setCurrent(fatigue.getCurrent() - damage, true);\n            stats.setFatigue(fatigue);\n        }\n\n        if (!wasDead && getCreatureStats(ptr).isDead())\n        {\n            // NPC was killed\n            if (!attacker.isEmpty() && attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf())\n            {\n                attacker.getClass().getNpcStats(attacker).addWerewolfKill();\n            }\n\n            MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, attacker);\n        }\n\n        /*\n            Start of tes3mp addition\n\n            If the attacker was the LocalPlayer or LocalActor, record their target and send an\n            attack packet about it\n\n            Send an ID_OBJECT_HIT about it as well\n\n            If the victim was the LocalPlayer, check whether packets should be sent about\n            their new dynamic stats and position\n\n            If the victim was a LocalActor who died, record their attacker as the killer\n        */\n        mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);\n\n        if (localAttack)\n        {\n            localAttack->pressed = false;\n            localAttack->damage = damage;\n            localAttack->knockdown = getCreatureStats(ptr).getKnockedDown();\n\n            MechanicsHelper::assignAttackTarget(localAttack, ptr);\n\n            localAttack->shouldSend = true;\n\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectHit(ptr, attacker, *localAttack);\n            objectList->sendObjectHit();\n        }\n        \n        if (ptr == MWMechanics::getPlayer())\n        {\n            // Record the attacker as the LocalPlayer's death reason\n            if (getCreatureStats(ptr).isDead())\n            {\n                mwmp::Main::get().getLocalPlayer()->killer = MechanicsHelper::getTarget(attacker);\n            }\n\n            mwmp::Main::get().getLocalPlayer()->updateStatsDynamic(true);\n            mwmp::Main::get().getLocalPlayer()->updatePosition(true); // fix position after getting damage;\n        }\n        else if (mwmp::Main::get().getCellController()->isLocalActor(ptr))\n        {\n            if (getCreatureStats(ptr).isDead())\n            {\n                mwmp::Main::get().getCellController()->getLocalActor(ptr)->killer = MechanicsHelper::getTarget(attacker);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        /*\n            Start of tes3mp addition\n\n            Don't display a dialogue screen for two players interacting with each other\n        */\n        if (actor == MWMechanics::getPlayer() && mwmp::PlayerList::isDedicatedPlayer(ptr))\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(\"\"));\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Avoid returning an ActionTalk when a non-player NPC activates another\n            non-player NPC, because it will always pop up a dialogue screen for\n            the local player\n        */\n        if (ptr != MWMechanics::getPlayer() && actor != MWMechanics::getPlayer())\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(\"\"));\n        /*\n            End of tes3mp addition\n        */\n\n        // player got activated by another NPC\n        if(ptr == MWMechanics::getPlayer())\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(actor));\n\n        // Werewolfs can't activate NPCs\n        if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfNPC\");\n\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(\"#{sWerewolfRefusal}\"));\n            if(sound) action->setSound(sound->mId);\n\n            return action;\n        }\n\n        const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n\n        if(stats.isDead())\n        {\n            bool canLoot = Settings::Manager::getBool (\"can loot during death animation\", \"Game\");\n\n            // by default user can loot friendly actors during death animation\n            if (canLoot && !stats.getAiSequence().isInCombat())\n                return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));\n\n            // otherwise wait until death animation\n            if(stats.isDeathAnimationFinished())\n                return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));\n        }\n        else if (!stats.getAiSequence().isInCombat())\n        {\n            if (stats.getKnockedDown() || MWBase::Environment::get().getMechanicsManager()->isSneaking(actor))\n                return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing\n\n            // Can't talk to werewolves\n            if (!getNpcStats(ptr).isWerewolf())\n                return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));\n        }\n        else // In combat\n        {\n            const bool stealingInCombat = Settings::Manager::getBool (\"always allow stealing from knocked out actors\", \"Game\");\n            if (stealingInCombat && stats.getKnockedDown())\n                return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing\n        }\n\n        // Tribunal and some mod companions oddly enough must use open action as fallback\n        if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), \"companion\"))\n            return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));\n\n        return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(\"\"));\n    }\n\n    MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr)\n        const\n    {\n        ensureCustomData (ptr);\n\n        return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;\n    }\n\n    MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr)\n        const\n    {\n        ensureCustomData (ptr);\n\n        return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore;\n    }\n\n    std::string Npc::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n\n        return ref->mBase->mScript;\n    }\n\n    float Npc::getMaxSpeed(const MWWorld::Ptr& ptr) const\n    {\n        // TODO: This function is called several times per frame for each NPC.\n        // It would be better to calculate it only once per frame for each NPC and save the result in CreatureStats.\n        const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n        bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n        if ((!godmode && stats.isParalyzed()) || stats.getKnockedDown() || stats.isDead())\n            return 0.f;\n\n        const MWBase::World *world = MWBase::Environment::get().getWorld();\n        const GMST& gmst = getGmst();\n\n        const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());\n        const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();\n\n        const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);\n\n        bool swimming = world->isSwimming(ptr);\n        bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);\n        bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);\n        bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);\n        running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));\n\n        float moveSpeed;\n        if(getEncumbrance(ptr) > getCapacity(ptr))\n            moveSpeed = 0.0f;\n        else if(mageffects.get(ESM::MagicEffect::Levitate).getMagnitude() > 0 &&\n                world->isLevitationEnabled())\n        {\n            float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() +\n                                    mageffects.get(ESM::MagicEffect::Levitate).getMagnitude());\n            flySpeed = gmst.fMinFlySpeed->mValue.getFloat() + flySpeed*(gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat());\n            flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance;\n            flySpeed = std::max(0.0f, flySpeed);\n            moveSpeed = flySpeed;\n        }\n        else if (swimming)\n            moveSpeed = getSwimSpeed(ptr);\n        else if (running && !sneaking)\n            moveSpeed = getRunSpeed(ptr);\n        else\n            moveSpeed = getWalkSpeed(ptr);\n\n        if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)\n            moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat();\n\n        return moveSpeed;\n    }\n\n    float Npc::getJump(const MWWorld::Ptr &ptr) const\n    {\n        if(getEncumbrance(ptr) > getCapacity(ptr))\n            return 0.f;\n\n        const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n        bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n        if ((!godmode && stats.isParalyzed()) || stats.getKnockedDown() || stats.isDead())\n            return 0.f;\n\n        const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());\n        const GMST& gmst = getGmst();\n        const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();\n        const float encumbranceTerm = gmst.fJumpEncumbranceBase->mValue.getFloat() +\n                                          gmst.fJumpEncumbranceMultiplier->mValue.getFloat() *\n                                          (1.0f - Npc::getNormalizedEncumbrance(ptr));\n\n        float a = getSkill(ptr, ESM::Skill::Acrobatics);\n        float b = 0.0f;\n        if(a > 50.0f)\n        {\n            b = a - 50.0f;\n            a = 50.0f;\n        }\n\n        float x = gmst.fJumpAcrobaticsBase->mValue.getFloat() +\n                  std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->mValue.getFloat());\n        x += 3.0f * b * gmst.fJumpAcroMultiplier->mValue.getFloat();\n        x += mageffects.get(ESM::MagicEffect::Jump).getMagnitude() * 64;\n        x *= encumbranceTerm;\n\n        if(stats.getStance(MWMechanics::CreatureStats::Stance_Run))\n            x *= gmst.fJumpRunMultiplier->mValue.getFloat();\n        x *= npcdata->mNpcStats.getFatigueTerm();\n        x -= -Constants::GravityConst * Constants::UnitsPerMeter;\n        x /= 3.0f;\n\n        return x;\n    }\n\n    MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const\n    {\n        ensureCustomData (ptr);\n\n        return ptr.getRefData().getCustomData()->asNpcCustomData().mMovement;\n    }\n\n    bool Npc::isEssential (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n\n        return (ref->mBase->mFlags & ESM::NPC::Essential) != 0;\n    }\n\n    void Npc::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Npc);\n        registerClass (typeid (ESM::NPC).name(), instance);\n    }\n\n    bool Npc::hasToolTip(const MWWorld::ConstPtr& ptr) const\n    {\n        if (!ptr.getRefData().getCustomData() || MWBase::Environment::get().getWindowManager()->isGuiMode())\n            return true;\n\n        const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();\n\n        if (customData.mNpcStats.isDead() && customData.mNpcStats.isDeathAnimationFinished())\n            return true;\n\n        if (!customData.mNpcStats.getAiSequence().isInCombat())\n            return true;\n\n        const bool stealingInCombat = Settings::Manager::getBool (\"always allow stealing from knocked out actors\", \"Game\");\n        if (stealingInCombat && customData.mNpcStats.getKnockedDown())\n            return true;\n\n        return false;\n    }\n\n    MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n\n        bool fullHelp = MWBase::Environment::get().getWindowManager()->getFullHelp();\n        MWGui::ToolTipInfo info;\n\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));\n        if(fullHelp && !ref->mBase->mName.empty() && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf())\n        {\n            info.caption += \" (\";\n            info.caption += MyGUI::TextIterator::toTagsString(ref->mBase->mName);\n            info.caption += \")\";\n        }\n\n        if(fullHelp)\n            info.text = MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n\n        return info;\n    }\n\n    float Npc::getCapacity (const MWWorld::Ptr& ptr) const\n    {\n        const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);\n        static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fEncumbranceStrMult\")->mValue.getFloat();\n        return stats.getAttribute(ESM::Attribute::Strength).getModified()*fEncumbranceStrMult;\n    }\n\n    float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const\n    {\n        // According to UESP, inventory weight is ignored in werewolf form. Does that include\n        // feather and burden effects?\n        return getNpcStats(ptr).isWerewolf() ? 0.0f : Actor::getEncumbrance(ptr);\n    }\n\n    bool Npc::apply (const MWWorld::Ptr& ptr, const std::string& id,\n        const MWWorld::Ptr& actor) const\n    {\n        MWMechanics::CastSpell cast(ptr, ptr);\n        return cast.cast(id);\n    }\n\n    void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const\n    {\n        MWMechanics::NpcStats& stats = getNpcStats (ptr);\n\n        if (stats.isWerewolf())\n            return;\n\n        MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n\n        const ESM::Class *class_ =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find (\n                ref->mBase->mClass\n            );\n\n        stats.useSkill (skill, *class_, usageType, extraFactor);\n    }\n\n    float Npc::getArmorRating (const MWWorld::Ptr& ptr) const\n    {\n        const MWBase::World *world = MWBase::Environment::get().getWorld();\n        const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();\n\n        MWMechanics::NpcStats &stats = getNpcStats(ptr);\n        const MWWorld::InventoryStore &invStore = getInventoryStore(ptr);\n\n        float fUnarmoredBase1 = store.find(\"fUnarmoredBase1\")->mValue.getFloat();\n        float fUnarmoredBase2 = store.find(\"fUnarmoredBase2\")->mValue.getFloat();\n        float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored);\n\n        float ratings[MWWorld::InventoryStore::Slots];\n        for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)\n        {\n            MWWorld::ConstContainerStoreIterator it = invStore.getSlot(i);\n            if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name())\n            {\n                // unarmored\n                ratings[i] = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);\n            }\n            else\n            {\n                ratings[i] = it->getClass().getEffectiveArmorRating(*it, ptr);\n\n                // Take in account armor condition\n                const bool hasHealth = it->getClass().hasItemHealth(*it);\n                if (hasHealth)\n                {\n                    ratings[i] *= it->getClass().getItemNormalizedHealth(*it);\n                }\n            }\n        }\n\n        float shield = stats.getMagicEffects().get(ESM::MagicEffect::Shield).getMagnitude();\n\n        return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3f\n                + (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet]\n                    + ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots]\n                    + ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron]\n                    ) * 0.1f\n                + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + ratings[MWWorld::InventoryStore::Slot_RightGauntlet])\n                    * 0.05f\n                + shield;\n    }\n\n    void Npc::adjustScale(const MWWorld::ConstPtr &ptr, osg::Vec3f&scale, bool rendering) const\n    {\n        if (!rendering)\n            return; // collision meshes are not scaled based on race height\n                    // having the same collision extents for all races makes the environments easier to test\n\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n\n        const ESM::Race* race =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);\n\n        // Race weight should not affect 1st-person meshes, otherwise it will change hand proportions and can break aiming.\n        if (ptr == MWMechanics::getPlayer() && ptr.isInCell() && MWBase::Environment::get().getWorld()->isFirstPerson())\n        {\n            if (ref->mBase->isMale())\n                scale *= race->mData.mHeight.mMale;\n            else\n                scale *= race->mData.mHeight.mFemale;\n\n            return;\n        }\n\n        if (ref->mBase->isMale())\n        {\n            scale.x() *= race->mData.mWeight.mMale;\n            scale.y() *= race->mData.mWeight.mMale;\n            scale.z() *= race->mData.mHeight.mMale;\n        }\n        else\n        {\n            scale.x() *= race->mData.mWeight.mFemale;\n            scale.y() *= race->mData.mWeight.mFemale;\n            scale.z() *= race->mData.mHeight.mFemale;\n        }\n    }\n\n    int Npc::getServices(const MWWorld::ConstPtr &actor) const\n    {\n        return actor.get<ESM::NPC>()->mBase->mAiData.mServices;\n    }\n\n\n    std::string Npc::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const\n    {\n        if(name == \"left\" || name == \"right\")\n        {\n            MWBase::World *world = MWBase::Environment::get().getWorld();\n            if(world->isFlying(ptr))\n                return std::string();\n            osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());\n            if(world->isSwimming(ptr))\n                return (name == \"left\") ? \"Swim Left\" : \"Swim Right\";\n            if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))\n                return (name == \"left\") ? \"FootWaterLeft\" : \"FootWaterRight\";\n            if(world->isOnGround(ptr))\n            {\n                if (getNpcStats(ptr).isWerewolf()\n                        && getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))\n                {\n                    int weaponType = ESM::Weapon::None;\n                    MWMechanics::getActiveWeapon(ptr, &weaponType);\n                    if (weaponType == ESM::Weapon::None)\n                        return std::string();\n                }\n\n                const MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);\n                MWWorld::ConstContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);\n                if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name())\n                    return (name == \"left\") ? \"FootBareLeft\" : \"FootBareRight\";\n\n                switch(boots->getClass().getEquipmentSkill(*boots))\n                {\n                    case ESM::Skill::LightArmor:\n                        return (name == \"left\") ? \"FootLightLeft\" : \"FootLightRight\";\n                    case ESM::Skill::MediumArmor:\n                        return (name == \"left\") ? \"FootMedLeft\" : \"FootMedRight\";\n                    case ESM::Skill::HeavyArmor:\n                        return (name == \"left\") ? \"FootHeavyLeft\" : \"FootHeavyRight\";\n                }\n            }\n            return std::string();\n        }\n\n        // Morrowind ignores land soundgen for NPCs\n        if(name == \"land\")\n            return std::string();\n        if(name == \"swimleft\")\n            return \"Swim Left\";\n        if(name == \"swimright\")\n            return \"Swim Right\";\n        // TODO: I have no idea what these are supposed to do for NPCs since they use\n        // voiced dialog for various conditions like health loss and combat taunts. Maybe\n        // only for biped creatures?\n\n        if(name == \"moan\")\n            return std::string();\n        if(name == \"roar\")\n            return std::string();\n        if(name == \"scream\")\n            return std::string();\n\n        throw std::runtime_error(std::string(\"Unexpected soundgen type: \")+name);\n    }\n\n    MWWorld::Ptr Npc::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const\n    {\n        return getNpcStats(ptr).getSkill(skill).getModified();\n    }\n\n    int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const\n    {\n        return ptr.get<ESM::NPC>()->mBase->mBloodType;\n    }\n\n    void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)\n        const\n    {\n        if (!state.mHasCustomState)\n            return;\n\n        if (state.mVersion > 0)\n        {\n            if (!ptr.getRefData().getCustomData())\n            {\n                // Create a CustomData, but don't fill it from ESM records (not needed)\n                ptr.getRefData().setCustomData(std::make_unique<NpcCustomData>());\n            }\n        }\n        else\n            ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless.\n\n        NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();\n        const ESM::NpcState& npcState = state.asNpcState();\n        customData.mInventoryStore.readState (npcState.mInventory);\n        customData.mNpcStats.readState (npcState.mNpcStats);\n        bool spellsInitialised = customData.mNpcStats.getSpells().setSpells(ptr.get<ESM::NPC>()->mBase->mId);\n        if(spellsInitialised)\n            customData.mNpcStats.getSpells().clear();\n        customData.mNpcStats.readState (npcState.mCreatureStats);\n    }\n\n    void Npc::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state)\n        const\n    {\n        if (!ptr.getRefData().getCustomData())\n        {\n            state.mHasCustomState = false;\n            return;\n        }\n\n        if (ptr.getRefData().getCount() <= 0)\n        {\n            state.mHasCustomState = false;\n            return;\n        }\n\n        const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();\n        ESM::NpcState& npcState = state.asNpcState();\n        customData.mInventoryStore.writeState (npcState.mInventory);\n        customData.mNpcStats.writeState (npcState.mNpcStats);\n        customData.mNpcStats.writeState (npcState.mCreatureStats);\n    }\n\n    int Npc::getBaseGold(const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n        return ref->mBase->mNpdt.mGold;\n    }\n\n    bool Npc::isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const\n    {\n        return Misc::StringUtils::ciEqual(ptr.get<ESM::NPC>()->mBase->mClass, className);\n    }\n\n    bool Npc::canSwim(const MWWorld::ConstPtr &ptr) const\n    {\n        return true;\n    }\n\n    bool Npc::canWalk(const MWWorld::ConstPtr &ptr) const\n    {\n        return true;\n    }\n\n    void Npc::respawn(const MWWorld::Ptr &ptr) const\n    {\n        const MWMechanics::CreatureStats& creatureStats = getCreatureStats(ptr);\n        if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead())\n            return;\n\n        if (!creatureStats.isDeathAnimationFinished())\n            return;\n\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        static const float fCorpseRespawnDelay = gmst.find(\"fCorpseRespawnDelay\")->mValue.getFloat();\n        static const float fCorpseClearDelay = gmst.find(\"fCorpseClearDelay\")->mValue.getFloat();\n\n        float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay);\n\n        if (ptr.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Respawn\n                && creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp())\n        {\n            if (ptr.getCellRef().hasContentFile())\n            {\n                if (ptr.getRefData().getCount() == 0)\n                {\n                    ptr.getRefData().setCount(1);\n                    const std::string& script = getScript(ptr);\n                    if (!script.empty())\n                        MWBase::Environment::get().getWorld()->getLocalScripts().add(script, ptr);\n                }\n\n                MWBase::Environment::get().getWorld()->removeContainerScripts(ptr);\n                ptr.getRefData().setCustomData(nullptr);\n\n                // Reset to original position\n                MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0],\n                        ptr.getCellRef().getPosition().pos[1],\n                        ptr.getCellRef().getPosition().pos[2]);\n            }\n        }\n    }\n\n    int Npc::getBaseFightRating (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n        return ref->mBase->mAiData.mFight;\n    }\n\n    bool Npc::isBipedal(const MWWorld::ConstPtr &ptr) const\n    {\n        return true;\n    }\n\n    std::string Npc::getPrimaryFaction (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n        return ref->mBase->mFaction;\n    }\n\n    int Npc::getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const\n    {\n        std::string factionID = ptr.getClass().getPrimaryFaction(ptr);\n        if(factionID.empty())\n            return -1;\n\n        // Search in the NPC data first\n        if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())\n        {\n            int rank = data->asNpcCustomData().mNpcStats.getFactionRank(factionID);\n            if (rank >= 0)\n                return rank;\n        }\n\n        // Use base NPC record as a fallback\n        const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();\n        return ref->mBase->getFactionRank();\n    }\n\n    void Npc::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const\n    {\n        MWMechanics::setBaseAISetting<ESM::NPC>(id, setting, value);\n    }\n\n    void Npc::modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const\n    {\n        MWMechanics::modifyBaseInventory<ESM::NPC>(actorId, itemId, amount);\n    }\n\n    float Npc::getWalkSpeed(const MWWorld::Ptr& ptr) const\n    {\n        const GMST& gmst = getGmst();\n        const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());\n        const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);\n        const bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);\n\n        float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat()\n                + 0.01f * npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()\n                * (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat());\n        walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance;\n        walkSpeed = std::max(0.0f, walkSpeed);\n        if(sneaking)\n            walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat();\n\n        return walkSpeed;\n    }\n\n    float Npc::getRunSpeed(const MWWorld::Ptr& ptr) const\n    {\n        const GMST& gmst = getGmst();\n        return getWalkSpeed(ptr)\n                * (0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat()\n                   + gmst.fBaseRunMultiplier->mValue.getFloat());\n    }\n\n    float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const\n    {\n        const GMST& gmst = getGmst();\n        const MWBase::World* world = MWBase::Environment::get().getWorld();\n        const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);\n        const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());\n        const MWMechanics::MagicEffects& mageffects = npcdata->mNpcStats.getMagicEffects();\n        const bool swimming = world->isSwimming(ptr);\n        const bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);\n        const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run)\n                && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));\n\n        float swimSpeed;\n\n        if (running)\n            swimSpeed = getRunSpeed(ptr);\n        else\n            swimSpeed = getWalkSpeed(ptr);\n\n        swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();\n        swimSpeed *= gmst.fSwimRunBase->mValue.getFloat()\n                + 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat();\n\n        return swimSpeed;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/npc.hpp",
    "content": "#ifndef GAME_MWCLASS_NPC_H\n#define GAME_MWCLASS_NPC_H\n\n#include \"actor.hpp\"\n\nnamespace ESM\n{\n    struct GameSetting;\n}\n\nnamespace MWClass\n{\n    class Npc : public Actor\n    {\n            void ensureCustomData (const MWWorld::Ptr& ptr) const;\n\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n            struct GMST\n            {\n                const ESM::GameSetting *fMinWalkSpeed;\n                const ESM::GameSetting *fMaxWalkSpeed;\n                const ESM::GameSetting *fEncumberedMoveEffect;\n                const ESM::GameSetting *fSneakSpeedMultiplier;\n                const ESM::GameSetting *fAthleticsRunBonus;\n                const ESM::GameSetting *fBaseRunMultiplier;\n                const ESM::GameSetting *fMinFlySpeed;\n                const ESM::GameSetting *fMaxFlySpeed;\n                const ESM::GameSetting *fSwimRunBase;\n                const ESM::GameSetting *fSwimRunAthleticsMult;\n                const ESM::GameSetting *fJumpEncumbranceBase;\n                const ESM::GameSetting *fJumpEncumbranceMultiplier;\n                const ESM::GameSetting *fJumpAcrobaticsBase;\n                const ESM::GameSetting *fJumpAcroMultiplier;\n                const ESM::GameSetting *fJumpRunMultiplier;\n                const ESM::GameSetting *fWereWolfRunMult;\n                const ESM::GameSetting *fKnockDownMult;\n                const ESM::GameSetting *iKnockDownOddsMult;\n                const ESM::GameSetting *iKnockDownOddsBase;\n                const ESM::GameSetting *fCombatArmorMinMult;\n            };\n\n            static const GMST& getGmst();\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const override;\n            ///< Return creature stats\n\n            MWMechanics::NpcStats& getNpcStats (const MWWorld::Ptr& ptr) const override;\n            ///< Return NPC stats\n\n            MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const override;\n            ///< Return container store\n\n            bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const override;\n            ///< Return inventory store\n\n            bool hasInventoryStore(const MWWorld::Ptr &ptr) const override { return true; }\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a class has a container store\n            */\n            virtual bool hasContainerStore(const MWWorld::Ptr &ptr) const { return true; }\n            /*\n                End of tes3mp addition\n            */\n\n            void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const override;\n\n            void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const override;\n\n            void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;\n            ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            float getMaxSpeed (const MWWorld::Ptr& ptr) const override;\n            ///< Return maximal movement speed.\n\n            float getJump(const MWWorld::Ptr &ptr) const override;\n            ///< Return jump velocity (not accounting for movement)\n\n            MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const override;\n            ///< Return desired movement.\n\n            float getCapacity (const MWWorld::Ptr& ptr) const override;\n            ///< Return total weight that fits into the object. Throws an exception, if the object can't\n            /// hold other objects.\n\n            float getEncumbrance (const MWWorld::Ptr& ptr) const override;\n            ///< Returns total weight of objects inside this object (including modifications from magic\n            /// effects). Throws an exception, if the object can't hold other objects.\n\n            float getArmorRating (const MWWorld::Ptr& ptr) const override;\n            ///< @return combined armor rating of this actor\n\n            bool apply (const MWWorld::Ptr& ptr, const std::string& id,\n                const MWWorld::Ptr& actor) const override;\n            ///< Apply \\a id on \\a ptr.\n            /// \\param actor Actor that is resposible for the ID being applied to \\a ptr.\n            /// \\return Any effect?\n\n            void adjustScale (const MWWorld::ConstPtr &ptr, osg::Vec3f &scale, bool rendering) const override;\n            /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh\n\n            void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor=1.f) const override;\n            ///< Inform actor \\a ptr that a skill use has succeeded.\n\n            bool isEssential (const MWWorld::ConstPtr& ptr) const override;\n            ///< Is \\a ptr essential? (i.e. may losing \\a ptr make the game unwinnable)\n\n            int getServices (const MWWorld::ConstPtr& actor) const override;\n\n            bool isPersistent (const MWWorld::ConstPtr& ptr) const override;\n\n            std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const override;\n\n            static void registerSelf();\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            float getSkill(const MWWorld::Ptr& ptr, int skill) const override;\n\n            /// Get a blood texture suitable for \\a ptr (see Blood Texture 0-2 in Morrowind.ini)\n            int getBloodTexture (const MWWorld::ConstPtr& ptr) const override;\n\n            bool isNpc() const override\n            {\n                return true;\n            }\n\n            void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const override;\n            ///< Read additional state from \\a state into \\a ptr.\n\n            void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const override;\n            ///< Write additional state from \\a ptr into \\a state.\n\n            int getBaseGold(const MWWorld::ConstPtr& ptr) const override;\n\n            bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const override;\n\n            bool canSwim (const MWWorld::ConstPtr &ptr) const override;\n\n            bool canWalk (const MWWorld::ConstPtr &ptr) const override;\n\n            bool isBipedal (const MWWorld::ConstPtr &ptr) const override;\n\n            void respawn (const MWWorld::Ptr& ptr) const override;\n\n            int getBaseFightRating (const MWWorld::ConstPtr& ptr) const override;\n\n            std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const override;\n            int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const override;\n\n            void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const override;\n\n            void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const override;\n\n            float getWalkSpeed(const MWWorld::Ptr& ptr) const override;\n\n            float getRunSpeed(const MWWorld::Ptr& ptr) const override;\n\n            float getSwimSpeed(const MWWorld::Ptr& ptr) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/potion.cpp",
    "content": "#include \"potion.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadalch.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionapply.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\n#include \"../mwmechanics/alchemy.hpp\"\n\nnamespace MWClass\n{\n\n    void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Potion::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision || Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Potion::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Potion::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Potion::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Potion> *ref =\n            ptr.get<ESM::Potion>();\n\n        return ref->mBase->mScript;\n    }\n\n    int Potion::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Potion::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Potion);\n\n        registerClass (typeid (ESM::Potion).name(), instance);\n    }\n\n    std::string Potion::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Potion Up\");\n    }\n\n    std::string Potion::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Potion Down\");\n    }\n\n    std::string Potion::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        text += \"\\n#{sWeight}: \" + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects);\n\n        // hide effects the player doesn't know about\n        MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        for (unsigned int i=0; i<info.effects.size(); ++i)\n            info.effects[i].mKnown = MWMechanics::Alchemy::knownEffect(i, player);\n\n        info.isPotion = true;\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.text = text;\n\n        return info;\n    }\n\n    std::shared_ptr<MWWorld::Action> Potion::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        MWWorld::LiveCellRef<ESM::Potion> *ref =\n            ptr.get<ESM::Potion>();\n\n        std::shared_ptr<MWWorld::Action> action (\n            new MWWorld::ActionApply (ptr, ref->mBase->mId));\n\n        action->setSound (\"Drink\");\n\n        return action;\n    }\n\n    MWWorld::Ptr Potion::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    bool Potion::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Potions) != 0;\n    }\n\n    float Potion::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/potion.hpp",
    "content": "#ifndef GAME_MWCLASS_POTION_H\n#define GAME_MWCLASS_POTION_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Potion : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/probe.cpp",
    "content": "#include \"probe.hpp\"\n\n#include <components/esm/loadprob.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\nnamespace MWClass\n{\n\n    void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Probe::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n    }\n\n    std::string Probe::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Probe::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n    std::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Probe::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref =\n            ptr.get<ESM::Probe>();\n\n        return ref->mBase->mScript;\n    }\n\n    std::pair<std::vector<int>, bool> Probe::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const\n    {\n        std::vector<int> slots_;\n\n        slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));\n\n        return std::make_pair (slots_, false);\n    }\n\n    int Probe::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Probe::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Probe);\n\n        registerClass (typeid (ESM::Probe).name(), instance);\n    }\n\n    std::string Probe::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Probe Up\");\n    }\n\n    std::string Probe::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Probe Down\");\n    }\n\n    std::string Probe::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        int remainingUses = getItemHealth(ptr);\n\n        text += \"\\n#{sUses}: \" + MWGui::ToolTips::toString(remainingUses);\n        text += \"\\n#{sQuality}: \" + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.text = text;\n\n        return info;\n    }\n\n    std::shared_ptr<MWWorld::Action> Probe::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));\n\n        action->setSound(getUpSoundId(ptr));\n\n        return action;\n    }\n\n    MWWorld::Ptr Probe::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    std::pair<int, std::string> Probe::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const\n    {\n        // Do not allow equip tools from inventory during attack\n        if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)\n            && MWBase::Environment::get().getWindowManager()->isGuiMode())\n            return std::make_pair(0, \"#{sCantEquipWeapWarning}\");\n\n        return std::make_pair(1, \"\");\n    }\n\n    bool Probe::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Probes) != 0;\n    }\n\n    int Probe::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();\n\n        return ref->mBase->mData.mUses;\n    }\n\n    float Probe::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/probe.hpp",
    "content": "#ifndef GAME_MWCLASS_PROBE_H\n#define GAME_MWCLASS_PROBE_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Probe : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return first: Return IDs of the slot this object can be equipped in; second: can object\n            /// stay stacked when equipped?\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const override;\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return item max health or throw an exception, if class does not have item health\n\n            bool hasItemHealth (const MWWorld::ConstPtr& ptr) const override { return true; }\n            ///< \\return Item health data available? (default implementation: false)\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/repair.cpp",
    "content": "#include \"repair.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadrepa.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/actionrepair.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\nnamespace MWClass\n{\n\n    void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Repair::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision || Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Repair::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Repair::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    std::string Repair::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref =\n            ptr.get<ESM::Repair>();\n\n        return ref->mBase->mScript;\n    }\n\n    int Repair::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Repair::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Repair);\n\n        registerClass (typeid (ESM::Repair).name(), instance);\n    }\n\n    std::string Repair::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Repair Up\");\n    }\n\n    std::string Repair::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string(\"Item Repair Down\");\n    }\n\n    std::string Repair::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();\n\n        return ref->mBase->mIcon;\n    }\n\n    bool Repair::hasItemHealth (const MWWorld::ConstPtr& ptr) const\n    {\n        return true;\n    }\n\n    int Repair::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();\n\n        return ref->mBase->mData.mUses;\n    }\n\n    MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        std::string text;\n\n        int remainingUses = getItemHealth(ptr);\n\n        text += \"\\n#{sUses}: \" + MWGui::ToolTips::toString(remainingUses);\n        text += \"\\n#{sQuality}: \" + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.text = text;\n\n        return info;\n    }\n\n    MWWorld::Ptr Repair::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    std::shared_ptr<MWWorld::Action> Repair::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRepair(ptr, force));\n    }\n\n    bool Repair::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::RepairItem) != 0;\n    }\n\n    float Repair::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/repair.hpp",
    "content": "#ifndef GAME_MWCLASS_REPAIR_H\n#define GAME_MWCLASS_REPAIR_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Repair : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu (default implementation: return a\n            /// null action).\n\n            bool hasItemHealth (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return Item health data available? (default implementation: false)\n\n            int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return item max health or throw an exception, if class does not have item health\n            /// (default implementation: throw an exception)\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/static.cpp",
    "content": "#include \"static.hpp\"\n\n#include <components/esm/loadstat.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n#include \"../mwrender/vismask.hpp\"\n\nnamespace MWClass\n{\n\n    void Static::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty())\n        {\n            renderingInterface.getObjects().insertModel(ptr, model);\n            ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static);\n        }\n    }\n\n    void Static::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        if(!model.empty())\n            physics.addObject(ptr, model);\n    }\n\n    std::string Static::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Static> *ref = ptr.get<ESM::Static>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Static::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        return \"\";\n    }\n\n    bool Static::hasToolTip(const MWWorld::ConstPtr& ptr) const\n    {\n        return false;\n    }\n\n    void Static::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Static);\n\n        registerClass (typeid (ESM::Static).name(), instance);\n    }\n\n    MWWorld::Ptr Static::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Static> *ref = ptr.get<ESM::Static>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/static.hpp",
    "content": "#ifndef GAME_MWCLASS_STATIC_H\n#define GAME_MWCLASS_STATIC_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Static : public MWWorld::Class\n    {\n            MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            bool hasToolTip (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            static void registerSelf();\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwclass/weapon.cpp",
    "content": "#include \"weapon.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadweap.hpp>\n#include <components/misc/constants.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwrender/objects.hpp\"\n#include \"../mwrender/renderinginterface.hpp\"\n\nnamespace MWClass\n{\n\n    void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const\n    {\n        if (!model.empty()) {\n            renderingInterface.getObjects().insertModel(ptr, model);\n        }\n    }\n\n    void Weapon::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const\n    {\n        // TODO: add option somewhere to enable collision for placeable objects\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable collision for this object class from a packet\n        */\n        if (!model.empty())\n        {\n            mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n            if (worldstate->hasPlacedObjectCollision ||\n                Utils::vectorContains(worldstate->enforcedCollisionRefIds, ptr.getCellRef().getRefId()))\n            {\n                if (worldstate->useActorCollisionForPlacedObjects)\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);\n                else\n                    physics.addObject(ptr, model, MWPhysics::CollisionType_World);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    std::string Weapon::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n\n        const std::string &model = ref->mBase->mModel;\n        if (!model.empty()) {\n            return \"meshes\\\\\" + model;\n        }\n        return \"\";\n    }\n\n    std::string Weapon::getName (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n        const std::string& name = ref->mBase->mName;\n\n        return !name.empty() ? name : ref->mBase->mId;\n    }\n\n    std::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr,\n        const MWWorld::Ptr& actor) const\n    {\n        return defaultItemActivate(ptr, actor);\n    }\n\n    bool Weapon::hasItemHealth (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n        int type = ref->mBase->mData.mType;\n\n        return MWMechanics::getWeaponType(type)->mFlags & ESM::WeaponType::HasHealth;\n    }\n\n    int Weapon::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n\n        return ref->mBase->mData.mHealth;\n    }\n\n    std::string Weapon::getScript (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref =\n            ptr.get<ESM::Weapon>();\n\n        return ref->mBase->mScript;\n    }\n\n    std::pair<std::vector<int>, bool> Weapon::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n        ESM::WeaponType::Class weapClass = MWMechanics::getWeaponType(ref->mBase->mData.mType)->mWeaponClass;\n\n        std::vector<int> slots_;\n        bool stack = false;\n\n        if (weapClass == ESM::WeaponType::Ammo)\n        {\n            slots_.push_back (int (MWWorld::InventoryStore::Slot_Ammunition));\n            stack = true;\n        }\n        else if (weapClass == ESM::WeaponType::Thrown)\n        {\n            slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));\n            stack = true;\n        }\n        else\n            slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));\n\n        return std::make_pair (slots_, stack);\n    }\n\n    int Weapon::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n        int type = ref->mBase->mData.mType;\n\n        return MWMechanics::getWeaponType(type)->mSkill;\n    }\n\n    int Weapon::getValue (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n\n        return ref->mBase->mData.mValue;\n    }\n\n    void Weapon::registerSelf()\n    {\n        std::shared_ptr<Class> instance (new Weapon);\n\n        registerClass (typeid (ESM::Weapon).name(), instance);\n    }\n\n    std::string Weapon::getUpSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n        int type = ref->mBase->mData.mType;\n        std::string soundId = MWMechanics::getWeaponType(type)->mSoundId;\n        return soundId + \" Up\";\n    }\n\n    std::string Weapon::getDownSoundId (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n        int type = ref->mBase->mData.mType;\n        std::string soundId = MWMechanics::getWeaponType(type)->mSoundId;\n        return soundId + \" Down\";\n    }\n\n    std::string Weapon::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n\n        return ref->mBase->mIcon;\n    }\n\n    MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n        const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType);\n\n        MWGui::ToolTipInfo info;\n        info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);\n        info.icon = ref->mBase->mIcon;\n\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n\n        std::string text;\n\n        // weapon type & damage\n        if (weaponType->mWeaponClass != ESM::WeaponType::Ammo || Settings::Manager::getBool(\"show projectile damage\", \"Game\"))\n        {\n            text += \"\\n#{sType} \";\n\n            int skill = MWMechanics::getWeaponType(ref->mBase->mData.mType)->mSkill;\n            const std::string type = ESM::Skill::sSkillNameIds[skill];\n            std::string oneOrTwoHanded;\n            if (weaponType->mWeaponClass == ESM::WeaponType::Melee)\n            {\n                if (weaponType->mFlags & ESM::WeaponType::TwoHanded)\n                    oneOrTwoHanded = \"sTwoHanded\";\n                else\n                    oneOrTwoHanded = \"sOneHanded\";\n            }\n\n            text += store.get<ESM::GameSetting>().find(type)->mValue.getString() +\n                ((oneOrTwoHanded != \"\") ? \", \" + store.get<ESM::GameSetting>().find(oneOrTwoHanded)->mValue.getString() : \"\");\n\n            // weapon damage\n            if (weaponType->mWeaponClass == ESM::WeaponType::Thrown)\n            {\n                // Thrown weapons have 2x real damage applied\n                // as they're both the weapon and the ammo\n                text += \"\\n#{sAttack}: \"\n                    + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[0] * 2))\n                    + \" - \" + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[1] * 2));\n            }\n            else if (weaponType->mWeaponClass == ESM::WeaponType::Melee)\n            {\n                // Chop\n                text += \"\\n#{sChop}: \"\n                    + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[0]))\n                    + \" - \" + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[1]));\n                // Slash\n                text += \"\\n#{sSlash}: \"\n                    + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mSlash[0]))\n                    + \" - \" + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mSlash[1]));\n                // Thrust\n                text += \"\\n#{sThrust}: \"\n                    + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mThrust[0]))\n                    + \" - \" + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mThrust[1]));\n            }\n            else\n            {\n                // marksman\n                text += \"\\n#{sAttack}: \"\n                    + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[0]))\n                    + \" - \" + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[1]));\n            }\n        }\n\n        if (hasItemHealth(ptr))\n        {\n            int remainingHealth = getItemHealth(ptr);\n            text += \"\\n#{sCondition}: \" + MWGui::ToolTips::toString(remainingHealth) + \"/\"\n                    + MWGui::ToolTips::toString(ref->mBase->mData.mHealth);\n        }\n\n        const bool verbose = Settings::Manager::getBool(\"show melee info\", \"Game\");\n        // add reach for melee weapon\n        if (weaponType->mWeaponClass == ESM::WeaponType::Melee && verbose)\n        {\n            // display value in feet\n            const float combatDistance = store.get<ESM::GameSetting>().find(\"fCombatDistance\")->mValue.getFloat() * ref->mBase->mData.mReach;\n            text += MWGui::ToolTips::getWeightString(combatDistance / Constants::UnitsPerFoot, \"#{sRange}\");\n            text += \" #{sFeet}\";\n        }\n\n        // add attack speed for any weapon excepts arrows and bolts\n        if (weaponType->mWeaponClass != ESM::WeaponType::Ammo && verbose)\n        {\n            text += MWGui::ToolTips::getPercentString(ref->mBase->mData.mSpeed, \"#{sAttributeSpeed}\");\n        }\n\n        text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, \"#{sWeight}\");\n        text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, \"#{sValue}\");\n\n        info.enchant = ref->mBase->mEnchant;\n\n        if (!info.enchant.empty())\n            info.remainingEnchantCharge = static_cast<int>(ptr.getCellRef().getEnchantmentCharge());\n\n        if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {\n            text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());\n            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, \"Script\");\n        }\n\n        info.text = text;\n\n        return info;\n    }\n\n    std::string Weapon::getEnchantment (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n\n        return ref->mBase->mEnchant;\n    }\n\n    std::string Weapon::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n\n        ESM::Weapon newItem = *ref->mBase;\n        newItem.mId=\"\";\n        newItem.mName=newName;\n        newItem.mData.mEnchant=enchCharge;\n        newItem.mEnchant=enchId;\n        newItem.mData.mFlags |= ESM::Weapon::Magical;\n\n        /*\n            Start of tes3mp addition\n\n            Send the newly created record to the server and expect it to be\n            returned with a server-set id\n        */\n        unsigned int quantity = mwmp::Main::get().getLocalPlayer()->lastEnchantmentQuantity;\n\n        mwmp::Main::get().getNetworking()->getWorldstate()->sendWeaponRecord(&newItem, ref->mBase->mId, quantity);\n        /*\n            End of tes3mp addition\n        */\n\n        const ESM::Weapon *record = MWBase::Environment::get().getWorld()->createRecord (newItem);\n        return record->mId;\n    }\n\n    std::pair<int, std::string> Weapon::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const\n    {\n        if (hasItemHealth(ptr) && getItemHealth(ptr) == 0)\n            return std::make_pair(0, \"#{sInventoryMessage1}\");\n\n        // Do not allow equip weapons from inventory during attack\n        if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)\n            && MWBase::Environment::get().getWindowManager()->isGuiMode())\n            return std::make_pair(0, \"#{sCantEquipWeapWarning}\");\n\n        std::pair<std::vector<int>, bool> slots_ = getEquipmentSlots(ptr);\n\n        if (slots_.first.empty())\n            return std::make_pair (0, \"\");\n\n        int type = ptr.get<ESM::Weapon>()->mBase->mData.mType;\n        if(MWMechanics::getWeaponType(type)->mFlags & ESM::WeaponType::TwoHanded)\n        {\n            return std::make_pair (2, \"\");\n        }\n\n        return std::make_pair(1, \"\");\n    }\n\n    std::shared_ptr<MWWorld::Action> Weapon::use (const MWWorld::Ptr& ptr, bool force) const\n    {\n        std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));\n\n        action->setSound(getUpSoundId(ptr));\n\n        return action;\n    }\n\n    MWWorld::Ptr Weapon::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n\n        return MWWorld::Ptr(cell.insert(ref), &cell);\n    }\n\n    int Weapon::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n\n        return ref->mBase->mData.mEnchant;\n    }\n\n    bool Weapon::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return (npcServices & ESM::NPC::Weapon)\n                || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty());\n    }\n\n    float Weapon::getWeight(const MWWorld::ConstPtr &ptr) const\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();\n        return ref->mBase->mData.mWeight;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwclass/weapon.hpp",
    "content": "#ifndef GAME_MWCLASS_WEAPON_H\n#define GAME_MWCLASS_WEAPON_H\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWClass\n{\n    class Weapon : public MWWorld::Class\n    {\n            MWWorld::Ptr\n            copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;\n\n        public:\n\n            void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;\n            ///< Add reference into a cell for rendering\n\n            void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;\n\n            std::string getName (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return name or ID; can return an empty string.\n\n            std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,\n                const MWWorld::Ptr& actor) const override;\n            ///< Generate action for activation\n\n            MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const override;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            bool hasItemHealth (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return Item health data available?\n\n            int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return item max health or throw an exception, if class does not have item health\n\n            std::string getScript (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of the script attached to ptr\n\n            std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const override;\n            ///< \\return first: Return IDs of the slot this object can be equipped in; second: can object\n            /// stay stacked when equipped?\n\n            int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const override;\n            /// Return the index of the skill this item corresponds to when equipped or -1, if there is\n            /// no such skill.\n\n            int getValue (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n\n            static void registerSelf();\n\n            std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the pick up sound Id\n\n            std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return the put down sound Id\n\n            std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const override;\n            ///< Return name of inventory icon.\n\n            std::string getEnchantment (const MWWorld::ConstPtr& ptr) const override;\n            ///< @return the enchantment ID if the object is enchanted, otherwise an empty string\n\n            std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const override;\n            ///< Creates a new record using \\a ptr as template, with the given name and the given enchantment applied to it.\n\n            std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const override;\n            ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.\n            ///  Second item in the pair specifies the error message\n\n            std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const override;\n            ///< Generate action for using via inventory menu\n\n            std::string getModel(const MWWorld::ConstPtr &ptr) const override;\n\n            bool canSell (const MWWorld::ConstPtr& item, int npcServices) const override;\n\n            float getWeight (const MWWorld::ConstPtr& ptr) const override;\n\n            int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/dialoguemanagerimp.cpp",
    "content": "#include \"dialoguemanagerimp.hpp\"\n\n#include <algorithm>\n#include <list>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/esm/loaddial.hpp>\n#include <components/esm/loadinfo.hpp>\n#include <components/esm/dialoguestate.hpp>\n#include <components/esm/esmwriter.hpp>\n\n#include <components/compiler/exception.hpp>\n#include <components/compiler/errorhandler.hpp>\n#include <components/compiler/scanner.hpp>\n#include <components/compiler/locals.hpp>\n#include <components/compiler/output.hpp>\n#include <components/compiler/scriptparser.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/defines.hpp>\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/LocalActor.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/journal.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwscript/compilercontext.hpp\"\n#include \"../mwscript/interpretercontext.hpp\"\n#include \"../mwscript/extensions.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"filter.hpp\"\n#include \"hypertextparser.hpp\"\n\nnamespace MWDialogue\n{\n    DialogueManager::DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage) :\n      mTranslationDataStorage(translationDataStorage)\n      , mCompilerContext (MWScript::CompilerContext::Type_Dialogue)\n      , mErrorHandler()\n      , mTalkedTo(false)\n      , mTemporaryDispositionChange(0.f)\n      , mPermanentDispositionChange(0.f)\n    {\n        mChoice = -1;\n        mIsInChoice = false;\n        mGoodbye = false;\n        mCompilerContext.setExtensions (&extensions);\n    }\n\n    void DialogueManager::clear()\n    {\n        mKnownTopics.clear();\n        mTalkedTo = false;\n        mTemporaryDispositionChange = 0;\n        mPermanentDispositionChange = 0;\n    }\n\n    void DialogueManager::addTopic (const std::string& topic)\n    {\n        mKnownTopics.insert( Misc::StringUtils::lowerCase(topic) );\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check whether a topic is known by the player from elsewhere\n        in the code\n    */\n    bool DialogueManager::isNewTopic(const std::string& topic)\n    {\n        return (!mKnownTopics.count(topic));\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void DialogueManager::parseText (const std::string& text)\n    {\n        updateActorKnownTopics();\n        std::vector<HyperTextParser::Token> hypertext = HyperTextParser::parseHyperText(text);\n\n        for (std::vector<HyperTextParser::Token>::iterator tok = hypertext.begin(); tok != hypertext.end(); ++tok)\n        {\n            std::string topicId = Misc::StringUtils::lowerCase(tok->mText);\n\n            if (tok->isExplicitLink())\n            {\n                // calculation of standard form for all hyperlinks\n                size_t asterisk_count = HyperTextParser::removePseudoAsterisks(topicId);\n                for(; asterisk_count > 0; --asterisk_count)\n                    topicId.append(\"*\");\n\n                topicId = mTranslationDataStorage.topicStandardForm(topicId);\n            }\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_PLAYER_TOPIC packet every time a new topic becomes known\n            */\n            if (mActorKnownTopics.count(topicId) && isNewTopic(topicId))\n                mwmp::Main::get().getLocalPlayer()->sendTopic(topicId);\n            /*\n                End of tes3mp addition\n            */\n\n            if (mActorKnownTopics.count( topicId ))\n                mKnownTopics.insert( topicId );\n        }\n    }\n\n    bool DialogueManager::startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback)\n    {\n        updateGlobals();\n\n        // Dialogue with dead actor (e.g. through script) should not be allowed.\n        if (actor.getClass().getCreatureStats(actor).isDead())\n            return false;\n\n        mLastTopic = \"\";\n        mPermanentDispositionChange = 0;\n        mTemporaryDispositionChange = 0;\n\n        mChoice = -1;\n        mIsInChoice = false;\n        mGoodbye = false;\n        mChoices.clear();\n\n        mActor = actor;\n\n        MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats (actor);\n        mTalkedTo = creatureStats.hasTalkedToPlayer();\n\n        mActorKnownTopics.clear();\n        mActorKnownTopicsFlag.clear();\n\n        //greeting\n        const MWWorld::Store<ESM::Dialogue> &dialogs =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();\n\n        Filter filter (actor, mChoice, mTalkedTo);\n\n        for (MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin(); it != dialogs.end(); ++it)\n        {\n            if(it->mType == ESM::Dialogue::Greeting)\n            {\n                // Search a response (we do not accept a fallback to \"Info refusal\" here)\n                if (const ESM::DialInfo *info = filter.search (*it, false))\n                {\n                    creatureStats.talkedToPlayer();\n\n                    if (!info->mSound.empty())\n                    {\n                        // TODO play sound\n                    }\n\n                    MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);\n                    callback->addResponse(\"\", Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));\n                    executeScript (info->mResultScript, mActor);\n                    mLastTopic = it->mId;\n\n                    parseText (info->mResponse);\n\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    bool DialogueManager::compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor)\n    {\n        bool success = true;\n\n        try\n        {\n            mErrorHandler.reset();\n\n            mErrorHandler.setContext(\"[dialogue script]\");\n\n            std::istringstream input (cmd + \"\\n\");\n\n            Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions());\n\n            Compiler::Locals locals;\n\n            std::string actorScript = actor.getClass().getScript (actor);\n\n            if (!actorScript.empty())\n            {\n                // grab local variables from actor's script, if available.\n                locals = MWBase::Environment::get().getScriptManager()->getLocals (actorScript);\n            }\n\n            Compiler::ScriptParser parser(mErrorHandler,mCompilerContext, locals, false);\n\n            scanner.scan (parser);\n\n            if (!mErrorHandler.isGood())\n                success = false;\n\n            if (success)\n                parser.getCode (code);\n        }\n        catch (const Compiler::SourceException& /* error */)\n        {\n            // error has already been reported via error handler\n            success = false;\n        }\n        catch (const std::exception& error)\n        {\n            Log(Debug::Error) << std::string (\"Dialogue error: An exception has been thrown: \") + error.what();\n            success = false;\n        }\n\n        if (!success)\n        {\n            Log(Debug::Error) << \"Error: compiling failed (dialogue script): \\n\" << cmd << \"\\n\";\n        }\n\n        return success;\n    }\n\n    void DialogueManager::executeScript (const std::string& script, const MWWorld::Ptr& actor)\n    {\n        std::vector<Interpreter::Type_Code> code;\n        if(compile(script, code, actor))\n        {\n            try\n            {\n                MWScript::InterpreterContext interpreterContext(&actor.getRefData().getLocals(), actor);\n\n                /*\n                    Start of tes3mp addition\n\n                    Mark this InterpreterContext as having a DIALOGUE context,\n                    so that packets sent by the Interpreter can have their\n                    origin determined by serverside scripts\n                */\n                interpreterContext.trackContextType(Interpreter::Context::DIALOGUE);\n                /*\n                    End of tes3mp addition\n                */\n\n                Interpreter::Interpreter interpreter;\n                MWScript::installOpcodes (interpreter);\n                interpreter.run (&code[0], code.size(), interpreterContext);\n            }\n            catch (const std::exception& error)\n            {\n               Log(Debug::Error) << std::string (\"Dialogue error: An exception has been thrown: \") + error.what();\n            }\n        }\n    }\n\n    bool DialogueManager::inJournal (const std::string& topicId, const std::string& infoId)\n    {\n        const MWDialogue::Topic *topicHistory = nullptr;\n        MWBase::Journal *journal = MWBase::Environment::get().getJournal();\n        for (auto it = journal->topicBegin(); it != journal->topicEnd(); ++it)\n        {\n            if (it->first == topicId)\n            {\n                topicHistory = &it->second;\n                break;\n            }\n        }\n\n        if (!topicHistory)\n            return false;\n\n        for(const auto& topic : *topicHistory)\n        {\n            if (topic.mInfoId == infoId)\n                return true;\n        }\n        return false;\n    }\n\n    void DialogueManager::executeTopic (const std::string& topic, ResponseCallback* callback)\n    {\n        Filter filter (mActor, mChoice, mTalkedTo);\n\n        const MWWorld::Store<ESM::Dialogue> &dialogues =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();\n\n        const ESM::Dialogue& dialogue = *dialogues.find (topic);\n\n        const ESM::DialInfo* info = filter.search(dialogue, true);\n        if (info)\n        {\n            std::string title;\n            if (dialogue.mType==ESM::Dialogue::Persuasion)\n            {\n                // Determine GMST from dialogue topic. GMSTs are:\n                // sAdmireSuccess, sAdmireFail, sIntimidateSuccess, sIntimidateFail,\n                // sTauntSuccess, sTauntFail, sBribeSuccess, sBribeFail\n                std::string modifiedTopic = \"s\" + topic;\n\n                modifiedTopic.erase (std::remove (modifiedTopic.begin(), modifiedTopic.end(), ' '), modifiedTopic.end());\n\n                const MWWorld::Store<ESM::GameSetting>& gmsts =\n                    MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n                title = gmsts.find (modifiedTopic)->mValue.getString();\n            }\n            else\n                title = topic;\n\n            MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);\n            callback->addResponse(title, Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));\n\n            if (dialogue.mType == ESM::Dialogue::Topic)\n            {\n                // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group,\n                // in which case it should not be added to the journal.\n                for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin();\n                    iter!=dialogue.mInfo.end(); ++iter)\n                {\n                    if (iter->mId == info->mId)\n                    {\n                        MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(topic), info->mId, mActor);\n                        break;\n                    }\n                }\n            }\n\n            mLastTopic = topic;\n\n            executeScript (info->mResultScript, mActor);\n\n            parseText (info->mResponse);\n        }\n    }\n\n    const ESM::Dialogue *DialogueManager::searchDialogue(const std::string& id)\n    {\n        return MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().search(id);\n    }\n\n    void DialogueManager::updateGlobals()\n    {\n        MWBase::Environment::get().getWorld()->updateDialogueGlobals();\n    }\n\n    void DialogueManager::updateActorKnownTopics()\n    {\n        updateGlobals();\n\n        mActorKnownTopics.clear();\n        mActorKnownTopicsFlag.clear();\n\n        const auto& dialogs = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();\n\n        Filter filter (mActor, -1, mTalkedTo);\n\n        for (const auto& dialog : dialogs)\n        {\n            if (dialog.mType == ESM::Dialogue::Topic)\n            {\n                const auto* answer = filter.search(dialog, true);\n                auto topicId = Misc::StringUtils::lowerCase(dialog.mId);\n\n                if (answer != nullptr)\n                {\n                    int flag = 0;\n                    if(!inJournal(topicId, answer->mId))\n                    {\n                        // Does this dialogue contains some actor-specific answer?\n                        if (Misc::StringUtils::ciEqual(answer->mActor, mActor.getCellRef().getRefId()))\n                            flag |= MWBase::DialogueManager::TopicType::Specific;\n                    }\n                    else\n                        flag |= MWBase::DialogueManager::TopicType::Exhausted;\n                    mActorKnownTopics.insert (dialog.mId);\n                    mActorKnownTopicsFlag[dialog.mId] = flag;\n                }\n\n            }\n        }\n    }\n\n    std::list<std::string> DialogueManager::getAvailableTopics()\n    {\n        updateActorKnownTopics();\n\n        std::list<std::string> keywordList;\n\n        for (const std::string& topic : mActorKnownTopics)\n        {\n            //does the player know the topic?\n            if (mKnownTopics.count(topic))\n                keywordList.push_back(topic);\n        }\n\n        // sort again, because the previous sort was case-sensitive\n        keywordList.sort(Misc::StringUtils::ciLess);\n        return keywordList;\n    }\n\n    int DialogueManager::getTopicFlag(const std::string& topicId)\n    {\n        return mActorKnownTopicsFlag[topicId];\n    }\n\n    void DialogueManager::keywordSelected (const std::string& keyword, ResponseCallback* callback)\n    {\n        if(!mIsInChoice)\n        {\n            const ESM::Dialogue* dialogue = searchDialogue(keyword);\n            if (dialogue && dialogue->mType == ESM::Dialogue::Topic)\n            {\n                executeTopic (keyword, callback);\n            }\n        }\n    }\n\n    bool DialogueManager::isInChoice() const\n    {\n        return mIsInChoice;\n    }\n\n    void DialogueManager::goodbyeSelected()\n    {\n        // Apply disposition change to NPC's base disposition\n        if (mActor.getClass().isNpc())\n        {\n            // Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate)       \n            float curDisp = static_cast<float>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false));\n            if (curDisp + mPermanentDispositionChange < 0)\n                mPermanentDispositionChange = -curDisp;\n\n            MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor);\n            npcStats.setBaseDisposition(static_cast<int>(npcStats.getBaseDisposition() + mPermanentDispositionChange));\n        }\n        mPermanentDispositionChange = 0;\n        mTemporaryDispositionChange = 0;\n    }\n\n    void DialogueManager::questionAnswered (int answer, ResponseCallback* callback)\n    {\n        mChoice = answer;\n\n        const ESM::Dialogue* dialogue = searchDialogue(mLastTopic);\n        if (dialogue)\n        {\n            Filter filter (mActor, mChoice, mTalkedTo);\n\n            if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting)\n            {\n                if (const ESM::DialInfo *info = filter.search (*dialogue, true))\n                {\n                    std::string text = info->mResponse;\n                    parseText (text);\n\n                    mChoice = -1;\n                    mIsInChoice = false;\n                    mChoices.clear();\n\n                    MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);\n                    callback->addResponse(\"\", Interpreter::fixDefinesDialog(text, interpreterContext));\n\n                    if (dialogue->mType == ESM::Dialogue::Topic)\n                    {\n                        // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group,\n                        // in which case it should not be added to the journal\n                        for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin();\n                            iter!=dialogue->mInfo.end(); ++iter)\n                        {\n                            if (iter->mId == info->mId)\n                            {\n                                MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(mLastTopic), info->mId, mActor);\n                                break;\n                            }\n                        }\n                    }\n\n                    executeScript (info->mResultScript, mActor);\n                }\n                else\n                {\n                    mChoice = -1;\n                    mIsInChoice = false;\n                    mChoices.clear();\n                }\n            }\n        }\n\n        updateActorKnownTopics();\n    }\n\n    void DialogueManager::addChoice (const std::string& text, int choice)\n    {\n        mIsInChoice = true;\n        mChoices.emplace_back(text, choice);\n    }\n\n    const std::vector<std::pair<std::string, int> >& DialogueManager::getChoices()\n    {\n        return mChoices;\n    }\n\n    bool DialogueManager::isGoodbye()\n    {\n        return mGoodbye;\n    }\n\n    void DialogueManager::goodbye()\n    {\n        mIsInChoice = false;\n        mGoodbye = true;\n    }\n\n    void DialogueManager::persuade(int type, ResponseCallback* callback)\n    {\n        bool success;\n        float temp, perm;\n        MWBase::Environment::get().getMechanicsManager()->getPersuasionDispositionChange(\n                    mActor, MWBase::MechanicsManager::PersuasionType(type),\n                    success, temp, perm);\n        mTemporaryDispositionChange += temp;\n        mPermanentDispositionChange += perm;\n\n        // change temp disposition so that final disposition is between 0...100\n        float curDisp = static_cast<float>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false));\n        if (curDisp + mTemporaryDispositionChange < 0)\n            mTemporaryDispositionChange = -curDisp;\n        else if (curDisp + mTemporaryDispositionChange > 100)\n            mTemporaryDispositionChange = 100 - curDisp;\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        player.getClass().skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1);\n\n        if (success)\n        {\n            int gold=0;\n            if (type == MWBase::MechanicsManager::PT_Bribe10)\n                gold = 10;\n            else if (type == MWBase::MechanicsManager::PT_Bribe100)\n                gold = 100;\n            else if (type == MWBase::MechanicsManager::PT_Bribe1000)\n                gold = 1000;\n\n            if (gold)\n            {\n                player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, gold, player);\n                mActor.getClass().getContainerStore(mActor).add(MWWorld::ContainerStore::sGoldId, gold, mActor);\n            }\n        }\n\n        std::string text;\n\n        if (type == MWBase::MechanicsManager::PT_Admire)\n            text = \"Admire\";\n        else if (type == MWBase::MechanicsManager::PT_Taunt)\n            text = \"Taunt\";\n        else if (type == MWBase::MechanicsManager::PT_Intimidate)\n            text = \"Intimidate\";\n        else{\n            text = \"Bribe\";\n        }\n\n        executeTopic (text + (success ? \" Success\" : \" Fail\"), callback);\n    }\n\n    int DialogueManager::getTemporaryDispositionChange() const\n    {\n        return static_cast<int>(mTemporaryDispositionChange);\n    }\n\n    void DialogueManager::applyBarterDispositionChange(int delta)\n    {\n        mTemporaryDispositionChange += delta;\n        if (Settings::Manager::getBool(\"barter disposition change is permanent\", \"Game\"))\n            mPermanentDispositionChange += delta;\n    }\n\n    bool DialogueManager::checkServiceRefused(ResponseCallback* callback, ServiceType service)\n    {\n        Filter filter (mActor, service, mTalkedTo);\n\n        const MWWorld::Store<ESM::Dialogue> &dialogues =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();\n\n        const ESM::Dialogue& dialogue = *dialogues.find (\"Service Refusal\");\n\n        std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, false, false, true);\n        if (!infos.empty())\n        {\n            const ESM::DialInfo* info = infos[0];\n\n            parseText (info->mResponse);\n\n            const MWWorld::Store<ESM::GameSetting>& gmsts =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n            MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);\n\n            callback->addResponse(gmsts.find (\"sServiceRefusal\")->mValue.getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));\n\n            executeScript (info->mResultScript, mActor);\n            return true;\n        }\n        return false;\n    }\n\n    void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic)\n    {\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        if(sndMgr->sayActive(actor))\n        {\n            // Actor is already saying something.\n            return;\n        }\n\n        if (actor.getClass().isNpc() && MWBase::Environment::get().getWorld()->isSwimming(actor))\n        {\n            // NPCs don't talk while submerged\n            return;\n        }\n\n        if (actor.getClass().getCreatureStats(actor).getKnockedDown())\n        {\n            // Unconscious actors can not speak\n            return;\n        }\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        const ESM::Dialogue *dial = store.get<ESM::Dialogue>().find(topic);\n\n        const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);\n        Filter filter(actor, 0, creatureStats.hasTalkedToPlayer());\n        const ESM::DialInfo *info = filter.search(*dial, false);\n        if(info != nullptr)\n        {\n            MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n            if (winMgr->getSubtitlesEnabled())\n            /*\n                Start of tes3mp change (minor)\n\n                Prevent subtitles for NPC sounds from being added to a currently open dialogue window,\n                which wasn't a problem in regular OpenMW because time was frozen during dialogue\n            */\n                winMgr->messageBox(info->mResponse, MWGui::ShowInDialogueMode_Never);\n            /*\n                End of tes3mp change (minor)\n            */\n            if (!info->mSound.empty())\n                sndMgr->say(actor, info->mSound);\n            if (!info->mResultScript.empty())\n                executeScript(info->mResultScript, actor);\n\n            /*\n                Start of tes3mp addition\n\n                If we are the cell authority over this actor, we need to record this new\n                sound for it\n            */\n            if (mwmp::Main::get().getCellController()->isLocalActor(actor))\n            {\n                mwmp::LocalActor *localActor = mwmp::Main::get().getCellController()->getLocalActor(actor);\n                localActor->sound = info->mSound;\n            }\n            /*\n                End of tes3mp addition\n            */\n        }\n    }\n\n    int DialogueManager::countSavedGameRecords() const\n    {\n        return 1; // known topics\n    }\n\n    void DialogueManager::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n    {\n        ESM::DialogueState state;\n\n        for (std::set<std::string>::const_iterator iter (mKnownTopics.begin());\n            iter!=mKnownTopics.end(); ++iter)\n        {\n            state.mKnownTopics.push_back (*iter);\n        }\n\n        state.mChangedFactionReaction = mChangedFactionReaction;\n\n        writer.startRecord (ESM::REC_DIAS);\n        state.save (writer);\n        writer.endRecord (ESM::REC_DIAS);\n    }\n\n    void DialogueManager::readRecord (ESM::ESMReader& reader, uint32_t type)\n    {\n        if (type==ESM::REC_DIAS)\n        {\n            const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n\n            ESM::DialogueState state;\n            state.load (reader);\n\n            for (std::vector<std::string>::const_iterator iter (state.mKnownTopics.begin());\n                iter!=state.mKnownTopics.end(); ++iter)\n                if (store.get<ESM::Dialogue>().search (*iter))\n                    mKnownTopics.insert (*iter);\n\n            mChangedFactionReaction = state.mChangedFactionReaction;\n        }\n    }\n\n    void DialogueManager::modFactionReaction(const std::string &faction1, const std::string &faction2, int diff)\n    {\n        std::string fact1 = Misc::StringUtils::lowerCase(faction1);\n        std::string fact2 = Misc::StringUtils::lowerCase(faction2);\n\n        // Make sure the factions exist\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2);\n\n        int newValue = getFactionReaction(faction1, faction2) + diff;\n\n        std::map<std::string, int>& map = mChangedFactionReaction[fact1];\n        map[fact2] = newValue;\n    }\n\n    void DialogueManager::setFactionReaction(const std::string &faction1, const std::string &faction2, int absolute)\n    {\n        std::string fact1 = Misc::StringUtils::lowerCase(faction1);\n        std::string fact2 = Misc::StringUtils::lowerCase(faction2);\n\n        // Make sure the factions exist\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2);\n\n        std::map<std::string, int>& map = mChangedFactionReaction[fact1];\n        map[fact2] = absolute;\n    }\n\n    int DialogueManager::getFactionReaction(const std::string &faction1, const std::string &faction2) const\n    {\n        std::string fact1 = Misc::StringUtils::lowerCase(faction1);\n        std::string fact2 = Misc::StringUtils::lowerCase(faction2);\n\n        ModFactionReactionMap::const_iterator map = mChangedFactionReaction.find(fact1);\n        if (map != mChangedFactionReaction.end() && map->second.find(fact2) != map->second.end())\n            return map->second.at(fact2);\n\n        const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);\n\n        std::map<std::string, int>::const_iterator it = faction->mReactions.begin();\n        for (; it != faction->mReactions.end(); ++it)\n        {\n            if (Misc::StringUtils::ciEqual(it->first, fact2))\n                    return it->second;\n        }\n        return 0;\n    }\n\n    void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const\n    {\n        if (actor == mActor && !mLastTopic.empty())\n        {\n            MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse(\n                        Misc::StringUtils::lowerCase(mLastTopic), actor.getClass().getName(actor));\n        }\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the caption of a voice dialogue\n    */\n    std::string DialogueManager::getVoiceCaption(const std::string& sound) const\n    {\n        const MWWorld::Store<ESM::Dialogue>& dialogues = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();\n\n        for (MWWorld::Store<ESM::Dialogue>::iterator dialogueIter = dialogues.begin(); dialogueIter != dialogues.end(); ++dialogueIter)\n        {\n            if (dialogueIter->mType == ESM::Dialogue::Voice)\n            {\n                for (ESM::Dialogue::InfoContainer::const_iterator infoIter = dialogueIter->mInfo.begin();\n                    infoIter != dialogueIter->mInfo.end(); ++infoIter)\n                {\n                    if (!infoIter->mSound.empty() && Misc::StringUtils::ciEqual(sound, infoIter->mSound))\n                        return infoIter->mResponse;\n                }\n            }\n        }\n\n        return \"???\";\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/dialoguemanagerimp.hpp",
    "content": "#ifndef GAME_MWDIALOG_DIALOGUEMANAGERIMP_H\n#define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H\n\n#include \"../mwbase/dialoguemanager.hpp\"\n\n#include <map>\n#include <set>\n#include <unordered_map>\n\n#include <components/compiler/streamerrorhandler.hpp>\n#include <components/translation/translation.hpp>\n#include <components/misc/stringops.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n\n#include \"../mwscript/compilercontext.hpp\"\n\nnamespace ESM\n{\n    struct Dialogue;\n}\n\nnamespace MWDialogue\n{\n    class DialogueManager : public MWBase::DialogueManager\n    {\n            std::set<std::string, Misc::StringUtils::CiComp> mKnownTopics;// Those are the topics the player knows.\n\n            // Modified faction reactions. <Faction1, <Faction2, Difference> >\n            typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap;\n            ModFactionReactionMap mChangedFactionReaction;\n\n            std::set<std::string, Misc::StringUtils::CiComp> mActorKnownTopics;\n            std::unordered_map<std::string, int> mActorKnownTopicsFlag;\n\n            Translation::Storage& mTranslationDataStorage;\n            MWScript::CompilerContext mCompilerContext;\n            Compiler::StreamErrorHandler mErrorHandler;\n\n            MWWorld::Ptr mActor;\n            bool mTalkedTo;\n\n            int mChoice;\n            std::string mLastTopic; // last topic ID, lowercase\n            bool mIsInChoice;\n            bool mGoodbye;\n\n            std::vector<std::pair<std::string, int> > mChoices;\n\n            float mTemporaryDispositionChange;\n            float mPermanentDispositionChange;\n\n            void parseText (const std::string& text);\n\n            void updateActorKnownTopics();\n            void updateGlobals();\n\n            bool compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor);\n            void executeScript (const std::string& script, const MWWorld::Ptr& actor);\n\n            void executeTopic (const std::string& topic, ResponseCallback* callback);\n\n            const ESM::Dialogue* searchDialogue(const std::string& id);\n\n        public:\n\n            DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage);\n\n            void clear() override;\n\n            bool isInChoice() const override;\n\n            bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) override;\n\n            std::list<std::string> getAvailableTopics() override;\n            int getTopicFlag(const std::string& topicId) override;\n\n            bool inJournal (const std::string& topicId, const std::string& infoId) override;\n\n            void addTopic (const std::string& topic) override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a topic is known by the player from elsewhere\n                in the code\n            */\n            virtual bool isNewTopic(const std::string& topic);\n            /*\n                End of tes3mp addition\n            */\n\n            void addChoice (const std::string& text,int choice) override;\n            const std::vector<std::pair<std::string, int> >& getChoices() override;\n\n            bool isGoodbye() override;\n\n            void goodbye() override;\n\n            bool checkServiceRefused (ResponseCallback* callback, ServiceType service = ServiceType::Any) override;\n\n            void say(const MWWorld::Ptr &actor, const std::string &topic) override;\n\n            //calbacks for the GUI\n            void keywordSelected (const std::string& keyword, ResponseCallback* callback) override;\n            void goodbyeSelected() override;\n            void questionAnswered (int answer, ResponseCallback* callback) override;\n\n            void persuade (int type, ResponseCallback* callback) override;\n            int getTemporaryDispositionChange () const override;\n\n            /// @note Controlled by an option, gets discarded when dialogue ends by default\n            void applyBarterDispositionChange (int delta) override;\n\n            int countSavedGameRecords() const override;\n\n            void write (ESM::ESMWriter& writer, Loading::Listener& progress) const override;\n\n            void readRecord (ESM::ESMReader& reader, uint32_t type) override;\n\n            /// Changes faction1's opinion of faction2 by \\a diff.\n            void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff) override;\n\n            void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute) override;\n\n            /// @return faction1's opinion of faction2\n            int getFactionReaction (const std::string& faction1, const std::string& faction2) const override;\n\n            /// Removes the last added topic response for the given actor from the journal\n            void clearInfoActor(const MWWorld::Ptr & actor) const override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the caption of a voice dialogue\n            */\n            virtual std::string getVoiceCaption(const std::string& sound) const;\n            /*\n                End of tes3mp addition\n            */\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/filter.cpp",
    "content": "#include \"filter.hpp\"\n\n#include <components/compiler/locals.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/journal.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/magiceffects.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"selectwrapper.hpp\"\n\nbool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const\n{\n    bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name());\n\n    // actor id\n    if (!info.mActor.empty())\n    {\n        if ( !Misc::StringUtils::ciEqual(info.mActor, mActor.getCellRef().getRefId()))\n            return false;\n    }\n    else if (isCreature)\n    {\n        // Creatures must not have topics aside of those specific to their id\n        return false;\n    }\n\n    // NPC race\n    if (!info.mRace.empty())\n    {\n        if (isCreature)\n            return true;\n\n        MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>();\n\n        if (!Misc::StringUtils::ciEqual(info.mRace, cellRef->mBase->mRace))\n            return false;\n    }\n\n    // NPC class\n    if (!info.mClass.empty())\n    {\n        if (isCreature)\n            return true;\n\n        MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>();\n\n        if ( !Misc::StringUtils::ciEqual(info.mClass, cellRef->mBase->mClass))\n            return false;\n    }\n\n    // NPC faction\n    if (info.mFactionLess)\n    {\n        if (isCreature)\n            return true;\n\n        if (!mActor.getClass().getPrimaryFaction(mActor).empty())\n            return false;\n    }\n    else if (!info.mFaction.empty())\n    {\n        if (isCreature)\n            return true;\n\n        if (!Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), info.mFaction))\n            return false;\n\n        // check rank\n        if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)\n            return false;\n    }\n    else if (info.mData.mRank != -1)\n    {\n        if (isCreature)\n            return true;\n\n        // Rank requirement, but no faction given. Use the actor's faction, if there is one.\n        // check rank\n        if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)\n            return false;\n    }\n\n    // Gender\n    if (!isCreature)\n    {\n        MWWorld::LiveCellRef<ESM::NPC>* npc = mActor.get<ESM::NPC>();\n        if (info.mData.mGender==(npc->mBase->mFlags & npc->mBase->Female ? 0 : 1))\n            return false;\n    }\n\n    return true;\n}\n\nbool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const\n{\n    const MWWorld::Ptr player = MWMechanics::getPlayer();\n    MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player);\n\n    // check player faction and rank\n    if (!info.mPcFaction.empty())\n    {\n        std::map<std::string,int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction));\n\n        if(iter==stats.getFactionRanks().end())\n            return false;\n\n        // check rank\n        if (iter->second < info.mData.mPCrank)\n            return false;\n    }\n    else if (info.mData.mPCrank != -1)\n    {\n        // required PC faction is not specified but PC rank is; use speaker's faction\n        std::map<std::string,int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (mActor.getClass().getPrimaryFaction(mActor)));\n\n        if(iter==stats.getFactionRanks().end())\n            return false;\n\n        // check rank\n        if (iter->second < info.mData.mPCrank)\n            return false;\n    }\n\n    // check cell\n    if (!info.mCell.empty())\n    {\n        // supports partial matches, just like getPcCell\n        const std::string& playerCell = MWBase::Environment::get().getWorld()->getCellName(player.getCell());\n        bool match = playerCell.length()>=info.mCell.length() &&\n            Misc::StringUtils::ciEqual(playerCell.substr (0, info.mCell.length()), info.mCell);\n        if (!match)\n            return false;\n    }\n\n    return true;\n}\n\nbool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const\n{\n    for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.mSelects.begin());\n        iter != info.mSelects.end(); ++iter)\n        if (!testSelectStruct (*iter))\n            return false;\n\n    return true;\n}\n\nbool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert) const\n{\n    bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name());\n\n    if (isCreature)\n        return true;\n\n    int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor);\n    // For service refusal, the disposition check is inverted. However, a value of 0 still means \"always succeed\".\n    return invert ? (info.mData.mDisposition == 0 || actorDisposition < info.mData.mDisposition)\n                  : (actorDisposition >= info.mData.mDisposition);\n}\n\nbool MWDialogue::Filter::testFunctionLocal(const MWDialogue::SelectWrapper& select) const\n{\n    std::string scriptName = mActor.getClass().getScript (mActor);\n\n    if (scriptName.empty())\n        return false; // no script\n\n    std::string name = Misc::StringUtils::lowerCase (select.getName());\n\n    const Compiler::Locals& localDefs =\n        MWBase::Environment::get().getScriptManager()->getLocals (scriptName);\n\n    char type = localDefs.getType (name);\n\n    if (type==' ')\n        return false; // script does not have a variable of this name.\n\n    int index = localDefs.getIndex (name);\n    if (index < 0)\n        return false; // shouldn't happen, we checked that variable has a type above, so must exist\n\n    const MWScript::Locals& locals = mActor.getRefData().getLocals();\n    if (locals.isEmpty())\n        return select.selectCompare(0);\n    switch (type)\n    {\n        case 's': return select.selectCompare (static_cast<int> (locals.mShorts[index]));\n        case 'l': return select.selectCompare (locals.mLongs[index]);\n        case 'f': return select.selectCompare (locals.mFloats[index]);\n    }\n\n    throw std::logic_error (\"unknown local variable type in dialogue filter\");\n}\n\nbool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const\n{\n    if (select.isNpcOnly() && (mActor.getTypeName() != typeid (ESM::NPC).name()))\n        // If the actor is a creature, we pass all conditions only applicable to NPCs.\n        return true;\n\n    if (select.getFunction() == SelectWrapper::Function_Choice && mChoice == -1)\n        // If not currently in a choice, we reject all conditions that test against choices.\n        return false;\n\n    if (select.getFunction() == SelectWrapper::Function_Weather && !(MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()))\n        // Reject weather conditions in interior cells\n        // Note that the original engine doesn't include the \"|| isCellQuasiExterior()\" check, which could be considered a bug.\n        return false;\n\n    switch (select.getType())\n    {\n        case SelectWrapper::Type_None: return true;\n        case SelectWrapper::Type_Integer: return select.selectCompare (getSelectStructInteger (select));\n        case SelectWrapper::Type_Numeric: return testSelectStructNumeric (select);\n        case SelectWrapper::Type_Boolean: return select.selectCompare (getSelectStructBoolean (select));\n\n        // We must not do the comparison for inverted functions (eg. Function_NotClass)\n        case SelectWrapper::Type_Inverted: return getSelectStructBoolean (select);\n    }\n\n    return true;\n}\n\nbool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) const\n{\n    switch (select.getFunction())\n    {\n        case SelectWrapper::Function_Global:\n\n            // internally all globals are float :(\n            return select.selectCompare (\n                MWBase::Environment::get().getWorld()->getGlobalFloat (select.getName()));\n\n        case SelectWrapper::Function_Local:\n        {\n            return testFunctionLocal(select);\n        }\n\n        case SelectWrapper::Function_NotLocal:\n        {\n            return !testFunctionLocal(select);\n        }\n\n        case SelectWrapper::Function_PcHealthPercent:\n        {\n            MWWorld::Ptr player = MWMechanics::getPlayer();\n\n            float ratio = player.getClass().getCreatureStats (player).getHealth().getCurrent() /\n                player.getClass().getCreatureStats (player).getHealth().getModified();\n\n            return select.selectCompare (static_cast<int>(ratio*100));\n        }\n\n        case SelectWrapper::Function_PcDynamicStat:\n        {\n            MWWorld::Ptr player = MWMechanics::getPlayer();\n\n            float value = player.getClass().getCreatureStats (player).\n                getDynamic (select.getArgument()).getCurrent();\n\n            return select.selectCompare (value);\n        }\n\n        case SelectWrapper::Function_HealthPercent:\n        {\n            float ratio = mActor.getClass().getCreatureStats (mActor).getHealth().getCurrent() /\n                mActor.getClass().getCreatureStats (mActor).getHealth().getModified();\n\n            return select.selectCompare (static_cast<int>(ratio*100));\n        }\n\n        default:\n\n            throw std::runtime_error (\"unknown numeric select function\");\n    }\n}\n\nint MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) const\n{\n    MWWorld::Ptr player = MWMechanics::getPlayer();\n\n    switch (select.getFunction())\n    {\n        case SelectWrapper::Function_Journal:\n\n            return MWBase::Environment::get().getJournal()->getJournalIndex (select.getName());\n\n        case SelectWrapper::Function_Item:\n        {\n            MWWorld::ContainerStore& store = player.getClass().getContainerStore (player);\n\n            return store.count(select.getName());\n        }\n\n        case SelectWrapper::Function_Dead:\n\n            return MWBase::Environment::get().getMechanicsManager()->countDeaths (select.getName());\n\n        case SelectWrapper::Function_Choice:\n\n            return mChoice;\n\n        case SelectWrapper::Function_AiSetting:\n\n            return mActor.getClass().getCreatureStats (mActor).getAiSetting (\n                        (MWMechanics::CreatureStats::AiSetting)select.getArgument()).getModified(false);\n\n        case SelectWrapper::Function_PcAttribute:\n\n            return player.getClass().getCreatureStats (player).\n                getAttribute (select.getArgument()).getModified();\n\n        case SelectWrapper::Function_PcSkill:\n\n            return static_cast<int> (player.getClass().\n                getNpcStats (player).getSkill (select.getArgument()).getModified());\n\n        case SelectWrapper::Function_FriendlyHit:\n        {\n            int hits = mActor.getClass().getCreatureStats (mActor).getFriendlyHits();\n\n            return hits>4 ? 4 : hits;\n        }\n\n        case SelectWrapper::Function_PcLevel:\n\n            return player.getClass().getCreatureStats (player).getLevel();\n\n        case SelectWrapper::Function_PcGender:\n\n            return player.get<ESM::NPC>()->mBase->isMale() ? 0 : 1;\n\n        case SelectWrapper::Function_PcClothingModifier:\n        {\n            const MWWorld::InventoryStore& store = player.getClass().getInventoryStore (player);\n\n            int value = 0;\n\n            for (int i=0; i<=15; ++i) // everything except things held in hands and ammunition\n            {\n                MWWorld::ConstContainerStoreIterator slot = store.getSlot (i);\n\n                if (slot!=store.end())\n                    value += slot->getClass().getValue (*slot);\n            }\n\n            return value;\n        }\n\n        case SelectWrapper::Function_PcCrimeLevel:\n\n            return player.getClass().getNpcStats (player).getBounty();\n\n        case SelectWrapper::Function_RankRequirement:\n        {\n            std::string faction = mActor.getClass().getPrimaryFaction(mActor);\n            if (faction.empty())\n                return 0;\n\n            int rank = getFactionRank (player, faction);\n\n            if (rank>=9)\n                return 0; // max rank\n\n            int result = 0;\n\n            if (hasFactionRankSkillRequirements (player, faction, rank+1))\n                result += 1;\n\n            if (hasFactionRankReputationRequirements (player, faction, rank+1))\n                result += 2;\n\n            return result;\n        }\n\n        case SelectWrapper::Function_Level:\n\n            return mActor.getClass().getCreatureStats (mActor).getLevel();\n\n        case SelectWrapper::Function_PCReputation:\n\n            return player.getClass().getNpcStats (player).getReputation();\n\n        case SelectWrapper::Function_Weather:\n\n            return MWBase::Environment::get().getWorld()->getCurrentWeather();\n\n        case SelectWrapper::Function_Reputation:\n\n            return mActor.getClass().getNpcStats (mActor).getReputation();\n\n        case SelectWrapper::Function_FactionRankDiff:\n        {\n            std::string faction = mActor.getClass().getPrimaryFaction(mActor);\n\n            if (faction.empty())\n                return 0;\n\n            int rank = getFactionRank (player, faction);\n            int npcRank = mActor.getClass().getPrimaryFactionRank(mActor);\n            return rank-npcRank;\n        }\n\n        case SelectWrapper::Function_WerewolfKills:\n\n            return player.getClass().getNpcStats (player).getWerewolfKills();\n\n        case SelectWrapper::Function_RankLow:\n        case SelectWrapper::Function_RankHigh:\n        {\n            bool low = select.getFunction()==SelectWrapper::Function_RankLow;\n\n            std::string factionId = mActor.getClass().getPrimaryFaction(mActor);\n\n            if (factionId.empty())\n                return 0;\n\n            int value = 0;\n\n            MWMechanics::NpcStats& playerStats = player.getClass().getNpcStats (player);\n\n            std::map<std::string, int>::const_iterator playerFactionIt = playerStats.getFactionRanks().begin();\n            for (; playerFactionIt != playerStats.getFactionRanks().end(); ++playerFactionIt)\n            {\n                int reaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(factionId, playerFactionIt->first);\n                if (low ? reaction < value : reaction > value)\n                    value = reaction;\n            }\n\n            return value;\n        }\n\n        case SelectWrapper::Function_CreatureTargetted:\n\n            {\n                MWWorld::Ptr target;\n                mActor.getClass().getCreatureStats(mActor).getAiSequence().getCombatTarget(target);\n                if (target)\n                {\n                    if (target.getClass().isNpc() && target.getClass().getNpcStats(target).isWerewolf())\n                        return 2;\n                    if (target.getTypeName() == typeid(ESM::Creature).name())\n                        return 1;\n                }\n            }\n            return 0;\n\n        default:\n\n            throw std::runtime_error (\"unknown integer select function\");\n    }\n}\n\nbool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) const\n{\n    MWWorld::Ptr player = MWMechanics::getPlayer();\n\n    switch (select.getFunction())\n    {\n        case SelectWrapper::Function_False:\n\n            return false;\n\n        case SelectWrapper::Function_NotId:\n\n            return !Misc::StringUtils::ciEqual(mActor.getCellRef().getRefId(), select.getName());\n\n        case SelectWrapper::Function_NotFaction:\n\n            return !Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), select.getName());\n\n        case SelectWrapper::Function_NotClass:\n\n            return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mClass, select.getName());\n\n        case SelectWrapper::Function_NotRace:\n\n            return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mRace, select.getName());\n\n        case SelectWrapper::Function_NotCell:\n            {\n                const std::string& actorCell = MWBase::Environment::get().getWorld()->getCellName(mActor.getCell());\n                return !(actorCell.length() >= select.getName().length()\n                      && Misc::StringUtils::ciEqual(actorCell.substr(0, select.getName().length()), select.getName()));\n            }\n        case SelectWrapper::Function_SameGender:\n\n            return (player.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female)==\n                (mActor.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female);\n\n        case SelectWrapper::Function_SameRace:\n\n            return Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mRace, player.get<ESM::NPC>()->mBase->mRace);\n\n        case SelectWrapper::Function_SameFaction:\n\n            return player.getClass().getNpcStats (player).isInFaction(mActor.getClass().getPrimaryFaction(mActor));\n\n        case SelectWrapper::Function_PcCommonDisease:\n\n            return player.getClass().getCreatureStats (player).hasCommonDisease();\n\n        case SelectWrapper::Function_PcBlightDisease:\n\n            return player.getClass().getCreatureStats (player).hasBlightDisease();\n\n        case SelectWrapper::Function_PcCorprus:\n\n            return player.getClass().getCreatureStats (player).\n                getMagicEffects().get (ESM::MagicEffect::Corprus).getMagnitude()!=0;\n\n        case SelectWrapper::Function_PcExpelled:\n        {\n            std::string faction = mActor.getClass().getPrimaryFaction(mActor);\n\n            if (faction.empty())\n                return false;\n\n            return player.getClass().getNpcStats(player).getExpelled(faction);\n        }\n\n        case SelectWrapper::Function_PcVampire:\n\n            return player.getClass().getCreatureStats(player).getMagicEffects().\n                    get(ESM::MagicEffect::Vampirism).getMagnitude() > 0;\n\n        case SelectWrapper::Function_TalkedToPc:\n\n            return mTalkedToPlayer;\n\n        case SelectWrapper::Function_Alarmed:\n\n            return mActor.getClass().getCreatureStats (mActor).isAlarmed();\n\n        case SelectWrapper::Function_Detected:\n\n            return MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, mActor);\n\n        case SelectWrapper::Function_Attacked:\n\n            return mActor.getClass().getCreatureStats (mActor).getAttacked();\n\n        case SelectWrapper::Function_ShouldAttack:\n\n            return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor,\n                    MWMechanics::getPlayer());\n\n        case SelectWrapper::Function_Werewolf:\n\n            return mActor.getClass().getNpcStats (mActor).isWerewolf();\n\n        default:\n\n            throw std::runtime_error (\"unknown boolean select function\");\n    }\n}\n\nint MWDialogue::Filter::getFactionRank (const MWWorld::Ptr& actor, const std::string& factionId) const\n{\n    MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor);\n\n    std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase(factionId));\n\n    if (iter==stats.getFactionRanks().end())\n        return -1;\n\n    return iter->second;\n}\n\nbool MWDialogue::Filter::hasFactionRankSkillRequirements (const MWWorld::Ptr& actor,\n    const std::string& factionId, int rank) const\n{\n    if (rank<0 || rank>=10)\n        throw std::runtime_error (\"rank index out of range\");\n\n    if (!actor.getClass().getNpcStats (actor).hasSkillsForRank (factionId, rank))\n        return false;\n\n    const ESM::Faction& faction =\n        *MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find (factionId);\n\n    MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats (actor);\n\n    return stats.getAttribute (faction.mData.mAttribute[0]).getBase()>=faction.mData.mRankData[rank].mAttribute1 &&\n        stats.getAttribute (faction.mData.mAttribute[1]).getBase()>=faction.mData.mRankData[rank].mAttribute2;\n}\n\nbool MWDialogue::Filter::hasFactionRankReputationRequirements (const MWWorld::Ptr& actor,\n    const std::string& factionId, int rank) const\n{\n    if (rank<0 || rank>=10)\n        throw std::runtime_error (\"rank index out of range\");\n\n    MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor);\n\n    const ESM::Faction& faction =\n        *MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find (factionId);\n\n    return stats.getFactionReputation (factionId)>=faction.mData.mRankData[rank].mFactReaction;\n}\n\nMWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer)\n: mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer)\n{}\n\nconst ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const\n{\n    std::vector<const ESM::DialInfo *> suitableInfos = list (dialogue, fallbackToInfoRefusal, false);\n\n    if (suitableInfos.empty())\n        return nullptr;\n    else\n        return suitableInfos[0];\n}\n\nstd::vector<const ESM::DialInfo *> MWDialogue::Filter::listAll (const ESM::Dialogue& dialogue) const\n{\n    std::vector<const ESM::DialInfo *> infos;\n    for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter)\n    {\n        if (testActor (*iter))\n            infos.push_back(&*iter);\n    }\n    return infos;\n}\n\nstd::vector<const ESM::DialInfo *> MWDialogue::Filter::list (const ESM::Dialogue& dialogue,\n    bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const\n{\n    std::vector<const ESM::DialInfo *> infos;\n\n    bool infoRefusal = false;\n\n    // Iterate over topic responses to find a matching one\n    for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin();\n        iter!=dialogue.mInfo.end(); ++iter)\n    {\n        if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter))\n        {\n            if (testDisposition (*iter, invertDisposition)) {\n                infos.push_back(&*iter);\n                if (!searchAll)\n                    break;\n            }\n            else\n                infoRefusal = true;\n        }\n    }\n\n    if (infos.empty() && infoRefusal && fallbackToInfoRefusal)\n    {\n        // No response is valid because of low NPC disposition,\n        // search a response in the topic \"Info Refusal\"\n\n        const MWWorld::Store<ESM::Dialogue> &dialogues =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();\n\n        const ESM::Dialogue& infoRefusalDialogue = *dialogues.find (\"Info Refusal\");\n\n        for (ESM::Dialogue::InfoContainer::const_iterator iter = infoRefusalDialogue.mInfo.begin();\n            iter!=infoRefusalDialogue.mInfo.end(); ++iter)\n            if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter, invertDisposition)) {\n                infos.push_back(&*iter);\n                if (!searchAll)\n                    break;\n            }\n    }\n\n    return infos;\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/filter.hpp",
    "content": "#ifndef GAME_MWDIALOGUE_FILTER_H\n#define GAME_MWDIALOGUE_FILTER_H\n\n#include <vector>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace ESM\n{\n    struct DialInfo;\n    struct Dialogue;\n}\n\nnamespace MWDialogue\n{\n    class SelectWrapper;\n\n    class Filter\n    {\n            MWWorld::Ptr mActor;\n            int mChoice;\n            bool mTalkedToPlayer;\n\n            bool testActor (const ESM::DialInfo& info) const;\n            ///< Is this the right actor for this \\a info?\n\n            bool testPlayer (const ESM::DialInfo& info) const;\n            ///< Do the player and the cell the player is currently in match \\a info?\n\n            bool testSelectStructs (const ESM::DialInfo& info) const;\n            ///< Are all select structs matching?\n\n            bool testDisposition (const ESM::DialInfo& info, bool invert=false) const;\n            ///< Is the actor disposition toward the player high enough (or low enough, if \\a invert is true)?\n\n            bool testFunctionLocal(const SelectWrapper& select) const;\n\n            bool testSelectStruct (const SelectWrapper& select) const;\n\n            bool testSelectStructNumeric (const SelectWrapper& select) const;\n\n            int getSelectStructInteger (const SelectWrapper& select) const;\n\n            bool getSelectStructBoolean (const SelectWrapper& select) const;\n\n            int getFactionRank (const MWWorld::Ptr& actor, const std::string& factionId) const;\n\n            bool hasFactionRankSkillRequirements (const MWWorld::Ptr& actor, const std::string& factionId,\n                int rank) const;\n\n            bool hasFactionRankReputationRequirements (const MWWorld::Ptr& actor, const std::string& factionId,\n                int rank) const;\n\n        public:\n\n            Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer);\n\n            std::vector<const ESM::DialInfo *> list (const ESM::Dialogue& dialogue,\n                bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition=false) const;\n            ///< List all infos that could be used on the given actor, using the current runtime state of the actor.\n            /// \\note If fallbackToInfoRefusal is used, the returned DialInfo might not be from the supplied ESM::Dialogue.\n\n            std::vector<const ESM::DialInfo *> listAll (const ESM::Dialogue& dialogue) const;\n            ///< List all infos that could possibly be used on the given actor, ignoring runtime state filters and ignoring player filters.\n\n            const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;\n            ///< Get a matching response for the requested dialogue.\n            ///  Redirect to \"Info Refusal\" topic if a response fulfills all conditions but disposition.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/hypertextparser.cpp",
    "content": "#include <components/esm/loaddial.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/store.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"keywordsearch.hpp\"\n\n#include \"hypertextparser.hpp\"\n\nnamespace MWDialogue\n{\n    namespace HyperTextParser\n    {\n        std::vector<Token> parseHyperText(const std::string & text)\n        {\n            std::vector<Token> result;\n            size_t pos_end = std::string::npos, iteration_pos = 0;\n            for(;;)\n            {\n                size_t pos_begin = text.find('@', iteration_pos);\n                if (pos_begin != std::string::npos)\n                    pos_end = text.find('#', pos_begin);\n\n                if (pos_begin != std::string::npos && pos_end != std::string::npos)\n                {\n                    if (pos_begin != iteration_pos)\n                        tokenizeKeywords(text.substr(iteration_pos, pos_begin - iteration_pos), result);\n\n                    std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1);\n                    result.emplace_back(link, Token::ExplicitLink);\n\n                    iteration_pos = pos_end + 1;\n                }\n                else\n                {\n                    if (iteration_pos != text.size())\n                        tokenizeKeywords(text.substr(iteration_pos), result);\n                    break;\n                }\n            }\n\n            return result;\n        }\n\n        void tokenizeKeywords(const std::string & text, std::vector<Token> & tokens)\n        {\n            const MWWorld::Store<ESM::Dialogue> & dialogs =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();\n\n            std::list<std::string> keywordList;\n            for (MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin(); it != dialogs.end(); ++it)\n                keywordList.push_back(Misc::StringUtils::lowerCase(it->mId));\n            keywordList.sort(Misc::StringUtils::ciLess);\n\n            KeywordSearch<std::string, int /*unused*/> keywordSearch;\n\n            for (std::list<std::string>::const_iterator it = keywordList.begin(); it != keywordList.end(); ++it)\n                keywordSearch.seed(*it, 0 /*unused*/);\n\n            std::vector<KeywordSearch<std::string, int /*unused*/>::Match> matches;\n            keywordSearch.highlightKeywords(text.begin(), text.end(), matches);\n\n            for (std::vector<KeywordSearch<std::string, int /*unused*/>::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it)\n            {\n                tokens.emplace_back(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword);\n            }\n        }\n\n        size_t removePseudoAsterisks(std::string & phrase)\n        {\n            size_t pseudoAsterisksCount = 0;\n\n            if( !phrase.empty() )\n            {\n                std::string::reverse_iterator rit = phrase.rbegin();\n\n                const char specialPseudoAsteriskCharacter = 127;\n                while( rit != phrase.rend() && *rit == specialPseudoAsteriskCharacter )\n                {\n                    pseudoAsterisksCount++;\n                    ++rit;\n                }\n            }\n\n            phrase = phrase.substr(0, phrase.length() - pseudoAsterisksCount);\n\n            return pseudoAsterisksCount;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/hypertextparser.hpp",
    "content": "#ifndef GAME_MWDIALOGUE_HYPERTEXTPARSER_H\n#define GAME_MWDIALOGUE_HYPERTEXTPARSER_H\n\n#include <string>\n#include <vector>\n\nnamespace MWDialogue\n{\n    namespace HyperTextParser\n    {\n        struct Token\n        {\n            enum Type\n            {\n                ExplicitLink, // enclosed in @#\n                ImplicitKeyword\n            };\n\n            Token(const std::string & text, Type type) : mText(text), mType(type) {}\n\n            bool isExplicitLink() { return mType == ExplicitLink; }\n\n            std::string mText;\n            Type mType;\n        };\n\n        // In translations (at least Russian) the links are marked with @#, so\n        // it should be a function to parse it\n        std::vector<Token> parseHyperText(const std::string & text);\n        void tokenizeKeywords(const std::string & text, std::vector<Token> & tokens);\n        size_t removePseudoAsterisks(std::string & phrase);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/journalentry.cpp",
    "content": "#include \"journalentry.hpp\"\n\n#include <stdexcept>\n\n#include <components/esm/journalentry.hpp>\n\n#include <components/interpreter/defines.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwscript/interpretercontext.hpp\"\n\n\nnamespace MWDialogue\n{\n    Entry::Entry() {}\n\n    Entry::Entry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor)\n    : mInfoId (infoId)\n    {\n        const ESM::Dialogue *dialogue =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic);\n\n        for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin());\n            iter!=dialogue->mInfo.end(); ++iter)\n            if (iter->mId == mInfoId)\n            {\n                if (actor.isEmpty())\n                {\n                    MWScript::InterpreterContext interpreterContext(nullptr, MWWorld::Ptr());\n                    mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext);\n                }\n                else\n                {\n                    MWScript::InterpreterContext interpreterContext(&actor.getRefData().getLocals(),actor);\n                    mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext);\n                }\n\n                return;\n            }\n\n        throw std::runtime_error (\"unknown info ID \" + mInfoId + \" for topic \" + topic);\n    }\n\n    Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText), mActorName(record.mActorName) {}\n\n    std::string Entry::getText() const\n    {\n        return mText;\n    }\n\n    void Entry::write (ESM::JournalEntry& entry) const\n    {\n        entry.mInfo = mInfoId;\n        entry.mText = mText;\n        entry.mActorName = mActorName;\n    }\n\n\n    JournalEntry::JournalEntry() {}\n\n    JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor)\n        : Entry (topic, infoId, actor), mTopic (topic)\n    {}\n\n    JournalEntry::JournalEntry (const ESM::JournalEntry& record)\n        : Entry (record), mTopic (record.mTopic)\n    {}\n\n    void JournalEntry::write (ESM::JournalEntry& entry) const\n    {\n        Entry::write (entry);\n        entry.mTopic = mTopic;\n    }\n\n    JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index)\n    {\n        return JournalEntry (topic, idFromIndex (topic, index), MWWorld::Ptr());\n    }\n\n    std::string JournalEntry::idFromIndex (const std::string& topic, int index)\n    {\n        const ESM::Dialogue *dialogue =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic);\n\n        for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin());\n            iter!=dialogue->mInfo.end(); ++iter)\n            if (iter->mData.mJournalIndex==index)\n            {\n                return iter->mId;\n            }\n\n        throw std::runtime_error (\"unknown journal index for topic \" + topic);\n    }\n\n\n    StampedJournalEntry::StampedJournalEntry()\n    : mDay (0), mMonth (0), mDayOfMonth (0)\n    {}\n\n    StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId,\n        int day, int month, int dayOfMonth, const MWWorld::Ptr& actor)\n    : JournalEntry (topic, infoId, actor), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)\n    {}\n\n    StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record)\n    : JournalEntry (record), mDay (record.mDay), mMonth (record.mMonth),\n      mDayOfMonth (record.mDayOfMonth)\n    {}\n\n    void StampedJournalEntry::write (ESM::JournalEntry& entry) const\n    {\n        JournalEntry::write (entry);\n        entry.mDay = mDay;\n        entry.mMonth = mMonth;\n        entry.mDayOfMonth = mDayOfMonth;\n    }\n\n    StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index, const MWWorld::Ptr& actor)\n    {\n        int day = MWBase::Environment::get().getWorld()->getGlobalInt (\"dayspassed\");\n        int month = MWBase::Environment::get().getWorld()->getGlobalInt (\"month\");\n        int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalInt (\"day\");\n\n        return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth, actor);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/journalentry.hpp",
    "content": "#ifndef GAME_MWDIALOGUE_JOURNALENTRY_H\n#define GAME_MWDIALOGUE_JOURNALENTRY_H\n\n#include <string>\n\nnamespace ESM\n{\n    struct JournalEntry;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWDialogue\n{\n    /// \\brief Basic quest/dialogue/topic entry\n    struct Entry\n    {\n        std::string mInfoId;\n        std::string mText;\n        std::string mActorName; // optional\n\n        Entry();\n\n        /// actor is optional\n        Entry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor);\n\n        Entry (const ESM::JournalEntry& record);\n\n        std::string getText() const;\n\n        void write (ESM::JournalEntry& entry) const;\n    };\n\n    /// \\brief A dialogue entry\n    ///\n    /// Same as entry, but store TopicID\n    struct JournalEntry : public Entry\n    {\n        std::string mTopic;\n\n        JournalEntry();\n\n        JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor);\n\n        JournalEntry (const ESM::JournalEntry& record);\n\n        void write (ESM::JournalEntry& entry) const;\n\n        static JournalEntry makeFromQuest (const std::string& topic, int index);\n\n        static std::string idFromIndex (const std::string& topic, int index);\n    };\n\n    /// \\brief A quest entry with a timestamp.\n    struct StampedJournalEntry : public JournalEntry\n    {\n        int mDay;\n        int mMonth;\n        int mDayOfMonth;\n\n        StampedJournalEntry();\n\n        StampedJournalEntry (const std::string& topic, const std::string& infoId,\n            int day, int month, int dayOfMonth, const MWWorld::Ptr& actor);\n\n        StampedJournalEntry (const ESM::JournalEntry& record);\n\n        void write (ESM::JournalEntry& entry) const;\n\n        static StampedJournalEntry makeFromQuest (const std::string& topic, int index, const MWWorld::Ptr& actor);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/journalimp.cpp",
    "content": "#include \"journalimp.hpp\"\n\n#include <iterator>\n\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/queststate.hpp>\n#include <components/esm/journalentry.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwgui/messagebox.hpp\"\n\nnamespace MWDialogue\n{\n    Quest& Journal::getQuest (const std::string& id)\n    {\n        TQuestContainer::iterator iter = mQuests.find (id);\n\n        if (iter==mQuests.end())\n        {\n            std::pair<TQuestContainer::iterator, bool> result =\n                mQuests.insert (std::make_pair (id, Quest (id)));\n\n            iter = result.first;\n        }\n\n        return iter->second;\n    }\n\n    Topic& Journal::getTopic (const std::string& id)\n    {\n        TTopicContainer::iterator iter = mTopics.find (id);\n\n        if (iter==mTopics.end())\n        {\n            std::pair<TTopicContainer::iterator, bool> result\n                = mTopics.insert (std::make_pair (id, Topic (id)));\n\n            iter = result.first;\n        }\n\n        return iter->second;\n    }\n\n    bool Journal::isThere (const std::string& topicId, const std::string& infoId) const\n    {\n        if (const ESM::Dialogue *dialogue =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().search (topicId))\n        {\n            if (infoId.empty())\n                return true;\n\n            for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin());\n                iter!=dialogue->mInfo.end(); ++iter)\n                if (iter->mId == infoId)\n                    return true;\n        }\n\n        return false;\n    }\n\n    Journal::Journal()\n    {}\n\n    void Journal::clear()\n    {\n        mJournal.clear();\n        mQuests.clear();\n        mTopics.clear();\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check whether a journal entry already exists from elsewhere in the code\n    */\n    bool Journal::hasEntry(const std::string& id, int index)\n    {\n        std::string infoId = JournalEntry::idFromIndex(id, index);\n        for (TEntryIter i = mJournal.begin(); i != mJournal.end(); ++i)\n            if (i->mTopic == id && i->mInfoId == infoId)\n                return true;\n\n        return false;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp change (minor)\n\n        Make it possible to override current time when adding journal entries, by adding\n        optional timestamp override arguments\n    */\n    void Journal::addEntry (const std::string& id, int index, const MWWorld::Ptr& actor, int daysPassed, int month, int day)\n    /*\n        End of tes3mp change (major)\n    */\n    {\n        // bail out if we already have heard this...\n        std::string infoId = JournalEntry::idFromIndex (id, index);\n        for (TEntryIter i = mJournal.begin (); i != mJournal.end (); ++i)\n            if (i->mTopic == id && i->mInfoId == infoId)\n            {\n                if (getJournalIndex(id) < index)\n                {\n                    setJournalIndex(id, index);\n                    MWBase::Environment::get().getWindowManager()->messageBox (\"#{sJournalEntry}\");\n                }\n                return;\n            }\n\n        StampedJournalEntry entry = StampedJournalEntry::makeFromQuest (id, index, actor);\n\n        /*\n            Start of tes3mp addition\n\n            Override the entry's timestamp if provided with valid time arguments\n        */\n        if (daysPassed != -1 && month != -1 && day != -1)\n        {\n            entry.mDay = daysPassed;\n            entry.mMonth = month;\n            entry.mDayOfMonth = day;\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        Quest& quest = getQuest (id);\n        quest.addEntry (entry); // we are doing slicing on purpose here\n\n        // there is no need to show empty entries in journal\n        if (!entry.getText().empty())\n        {\n            mJournal.push_back (entry);\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sJournalEntry}\");\n        }\n    }\n\n    void Journal::setJournalIndex (const std::string& id, int index)\n    {\n        Quest& quest = getQuest (id);\n\n        quest.setIndex (index);\n    }\n\n    void Journal::addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor)\n    {\n        Topic& topic = getTopic (topicId);\n\n        JournalEntry entry(topicId, infoId, actor);\n        entry.mActorName = actor.getClass().getName(actor);\n        topic.addEntry (entry);\n    }\n\n    void Journal::removeLastAddedTopicResponse(const std::string &topicId, const std::string &actorName)\n    {\n        Topic& topic = getTopic (topicId);\n\n        topic.removeLastAddedResponse(actorName);\n\n        if (topic.begin() == topic.end())\n            mTopics.erase(mTopics.find(topicId)); // All responses removed -> remove topic\n    }\n\n    int Journal::getJournalIndex (const std::string& id) const\n    {\n        TQuestContainer::const_iterator iter = mQuests.find (id);\n\n        if (iter==mQuests.end())\n            return 0;\n\n        return iter->second.getIndex();\n    }\n\n    Journal::TEntryIter Journal::begin() const\n    {\n        return mJournal.begin();\n    }\n\n    Journal::TEntryIter Journal::end() const\n    {\n        return mJournal.end();\n    }\n\n    Journal::TQuestIter Journal::questBegin() const\n    {\n        return mQuests.begin();\n    }\n\n    Journal::TQuestIter Journal::questEnd() const\n    {\n        return mQuests.end();\n    }\n\n    Journal::TTopicIter Journal::topicBegin() const\n    {\n        return mTopics.begin();\n    }\n\n    Journal::TTopicIter Journal::topicEnd() const\n    {\n        return mTopics.end();\n    }\n\n    int Journal::countSavedGameRecords() const\n    {\n        int count = static_cast<int> (mQuests.size());\n\n        for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)\n            count += std::distance (iter->second.begin(), iter->second.end());\n\n        count += std::distance (mJournal.begin(), mJournal.end());\n\n        for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)\n            count += std::distance (iter->second.begin(), iter->second.end());\n\n        return count;\n    }\n\n    void Journal::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n    {\n        for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)\n        {\n            const Quest& quest = iter->second;\n\n            ESM::QuestState state;\n            quest.write (state);\n            writer.startRecord (ESM::REC_QUES);\n            state.save (writer);\n            writer.endRecord (ESM::REC_QUES);\n\n            for (Topic::TEntryIter entryIter (quest.begin()); entryIter!=quest.end(); ++entryIter)\n            {\n                ESM::JournalEntry entry;\n                entry.mType = ESM::JournalEntry::Type_Quest;\n                entry.mTopic = quest.getTopic();\n                entryIter->write (entry);\n                writer.startRecord (ESM::REC_JOUR);\n                entry.save (writer);\n                writer.endRecord (ESM::REC_JOUR);\n            }\n        }\n\n        for (TEntryIter iter (mJournal.begin()); iter!=mJournal.end(); ++iter)\n        {\n            ESM::JournalEntry entry;\n            entry.mType = ESM::JournalEntry::Type_Journal;\n            iter->write (entry);\n            writer.startRecord (ESM::REC_JOUR);\n            entry.save (writer);\n            writer.endRecord (ESM::REC_JOUR);\n        }\n\n        for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)\n        {\n            const Topic& topic = iter->second;\n\n            for (Topic::TEntryIter entryIter (topic.begin()); entryIter!=topic.end(); ++entryIter)\n            {\n                ESM::JournalEntry entry;\n                entry.mType = ESM::JournalEntry::Type_Topic;\n                entry.mTopic = topic.getTopic();\n                entryIter->write (entry);\n                writer.startRecord (ESM::REC_JOUR);\n                entry.save (writer);\n                writer.endRecord (ESM::REC_JOUR);\n            }\n        }\n    }\n\n    void Journal::readRecord (ESM::ESMReader& reader, uint32_t type)\n    {\n        if (type==ESM::REC_JOUR || type==ESM::REC_JOUR_LEGACY)\n        {\n            ESM::JournalEntry record;\n            record.load (reader);\n\n            if (isThere (record.mTopic, record.mInfo))\n                switch (record.mType)\n                {\n                    case ESM::JournalEntry::Type_Quest:\n\n                        getQuest (record.mTopic).insertEntry (record);\n                        break;\n\n                    case ESM::JournalEntry::Type_Journal:\n\n                        mJournal.push_back (record);\n                        break;\n\n                    case ESM::JournalEntry::Type_Topic:\n\n                        getTopic (record.mTopic).insertEntry (record);\n                        break;\n                }\n        }\n        else if (type==ESM::REC_QUES)\n        {\n            ESM::QuestState record;\n            record.load (reader);\n\n            if (isThere (record.mTopic))\n            {\n                std::pair<TQuestContainer::iterator, bool> result = mQuests.insert (std::make_pair (record.mTopic, record));\n                // reapply quest index, this is to handle users upgrading from only\n                // Morrowind.esm (no quest states) to Morrowind.esm + Tribunal.esm\n                result.first->second.setIndex(record.mState);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/journalimp.hpp",
    "content": "#ifndef GAME_MWDIALOG_JOURNAL_H\n#define GAME_MWDIALOG_JOURNAL_H\n\n#include \"../mwbase/journal.hpp\"\n\n#include \"quest.hpp\"\n\nnamespace MWDialogue\n{\n    /// \\brief The player's journal\n    class Journal : public MWBase::Journal\n    {\n            TEntryContainer mJournal;\n            TQuestContainer mQuests;\n            TTopicContainer mTopics;\n\n        private:\n\n            Quest& getQuest (const std::string& id);\n\n            Topic& getTopic (const std::string& id);\n\n            bool isThere (const std::string& topicId, const std::string& infoId = \"\") const;\n\n        public:\n\n            Journal();\n\n            void clear() override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a journal entry already exists from elsewhere in the code\n            */\n            virtual bool hasEntry(const std::string& id, int index);\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp change (minor)\n\n                Make it possible to override current time when adding journal entries, by adding\n                optional timestamp override arguments\n            */\n            void addEntry (const std::string& id, int index, const MWWorld::Ptr& actor, int daysPassed = -1, int month = -1, int day = -1) override;\n            ///< Add a journal entry.\n            /// @param actor Used as context for replacing of escape sequences (%name, etc).\n            /*\n                End of tes3mp change (major)\n            */\n\n            void setJournalIndex (const std::string& id, int index) override;\n            ///< Set the journal index without adding an entry.\n\n            int getJournalIndex (const std::string& id) const override;\n            ///< Get the journal index.\n\n            void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor) override;\n            /// \\note topicId must be lowercase\n\n            void removeLastAddedTopicResponse (const std::string& topicId, const std::string& actorName) override;\n            ///< Removes the last topic response added for the given topicId and actor name.\n            /// \\note topicId must be lowercase\n\n            TEntryIter begin() const override;\n            ///< Iterator pointing to the begin of the main journal.\n            ///\n            /// \\note Iterators to main journal entries will never become invalid.\n\n            TEntryIter end() const override;\n            ///< Iterator pointing past the end of the main journal.\n\n            TQuestIter questBegin() const override;\n            ///< Iterator pointing to the first quest (sorted by topic ID)\n\n            TQuestIter questEnd() const override;\n            ///< Iterator pointing past the last quest.\n\n            TTopicIter topicBegin() const override;\n            ///< Iterator pointing to the first topic (sorted by topic ID)\n            ///\n            /// \\note The topic ID is identical with the user-visible topic string.\n\n            TTopicIter topicEnd() const override;\n            ///< Iterator pointing past the last topic.\n\n            int countSavedGameRecords() const override;\n\n            void write (ESM::ESMWriter& writer, Loading::Listener& progress) const override;\n\n            void readRecord (ESM::ESMReader& reader, uint32_t type) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/keywordsearch.cpp",
    "content": ""
  },
  {
    "path": "apps/openmw/mwdialogue/keywordsearch.hpp",
    "content": "#ifndef GAME_MWDIALOGUE_KEYWORDSEARCH_H\n#define GAME_MWDIALOGUE_KEYWORDSEARCH_H\n\n#include <map>\n#include <cctype>\n#include <stdexcept>\n#include <vector>\n#include <algorithm>    // std::reverse\n\n#include <components/misc/stringops.hpp>\n\nnamespace MWDialogue\n{\n\ntemplate <typename string_t, typename value_t>\nclass KeywordSearch\n{\npublic:\n\n    typedef typename string_t::const_iterator Point;\n\n    struct Match\n    {\n        Point mBeg;\n        Point mEnd;\n        value_t mValue;\n    };\n\n    void seed (string_t keyword, value_t value)\n    {\n        if (keyword.empty())\n            return;\n        seed_impl  (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot);\n    }\n\n    void clear ()\n    {\n        mRoot.mChildren.clear ();\n        mRoot.mKeyword.clear ();\n    }\n\n    bool containsKeyword (string_t keyword, value_t& value)\n    {\n        typename Entry::childen_t::iterator current;\n        typename Entry::childen_t::iterator next;\n\n        current = mRoot.mChildren.find (Misc::StringUtils::toLower (*keyword.begin()));\n        if (current == mRoot.mChildren.end())\n            return false;\n        else if (current->second.mKeyword.size() && Misc::StringUtils::ciEqual(current->second.mKeyword, keyword))\n        {\n            value = current->second.mValue;\n            return true;\n        }\n\n        for (Point i = ++keyword.begin(); i != keyword.end(); ++i)\n        {\n            next = current->second.mChildren.find(Misc::StringUtils::toLower (*i));\n            if (next == current->second.mChildren.end())\n                return false;\n            if (Misc::StringUtils::ciEqual(next->second.mKeyword, keyword))\n            {\n                value = next->second.mValue;\n                return true;\n            }\n            current = next;\n        }\n        return false;\n    }\n\n    static bool sortMatches(const Match& left, const Match& right)\n    {\n        return left.mBeg < right.mBeg;\n    }\n\n    void highlightKeywords (Point beg, Point end, std::vector<Match>& out)\n    {\n        std::vector<Match> matches;\n        for (Point i = beg; i != end; ++i)\n        {\n            // check if previous character marked start of new word\n            if (i != beg)\n            {\n                Point prev = i;\n                --prev;\n                if(isalpha(*prev))\n                    continue;\n            }\n\n\n            // check first character\n            typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (Misc::StringUtils::toLower (*i));\n\n            // no match, on to next character\n            if (candidate == mRoot.mChildren.end ())\n                continue;\n\n            // see how far the match goes\n            Point j = i;\n\n            // some keywords might be longer variations of other keywords, so we definitely need a list of candidates\n            // the first element in the pair is length of the match, i.e. depth from the first character on\n            std::vector< typename std::pair<int, typename Entry::childen_t::iterator> > candidates;\n\n            while ((j + 1) != end)\n            {\n                typename Entry::childen_t::iterator next = candidate->second.mChildren.find (Misc::StringUtils::toLower (*++j));\n\n                if (next == candidate->second.mChildren.end ())\n                {\n                    if (candidate->second.mKeyword.size() > 0)\n                        candidates.push_back(std::make_pair((j-i), candidate));\n                    break;\n                }\n\n                candidate = next;\n\n                if (candidate->second.mKeyword.size() > 0)\n                    candidates.push_back(std::make_pair((j-i), candidate));\n            }\n\n            if (candidates.empty())\n                continue; // didn't match enough to disambiguate, on to next character\n\n            // shorter candidates will be added to the vector first. however, we want to check against longer candidates first\n            std::reverse(candidates.begin(), candidates.end());\n\n            for (typename std::vector< std::pair<int, typename Entry::childen_t::iterator> >::iterator it = candidates.begin();\n                 it != candidates.end(); ++it)\n            {\n                candidate = it->second;\n                // try to match the rest of the keyword\n                Point k = i + it->first;\n                typename string_t::const_iterator t = candidate->second.mKeyword.begin () + (k - i);\n\n\n                while (k != end && t != candidate->second.mKeyword.end ())\n                {\n                    if (Misc::StringUtils::toLower (*k) != Misc::StringUtils::toLower (*t))\n                        break;\n\n                    ++k, ++t;\n                }\n\n                // didn't match full keyword, try the next candidate\n                if (t != candidate->second.mKeyword.end ())\n                    continue;\n\n                // found a keyword, but there might still be longer keywords that start somewhere _within_ this keyword\n                // we will resolve these overlapping keywords later, choosing the longest one in case of conflict\n                Match match;\n                match.mValue = candidate->second.mValue;\n                match.mBeg = i;\n                match.mEnd = k;\n                matches.push_back(match);\n                break;\n            }\n        }\n\n        // resolve overlapping keywords\n        while (!matches.empty())\n        {\n            int longestKeywordSize = 0;\n            typename std::vector<Match>::iterator longestKeyword = matches.begin();\n            for (typename std::vector<Match>::iterator it = matches.begin(); it != matches.end(); ++it)\n            {\n                int size = it->mEnd - it->mBeg;\n                if (size > longestKeywordSize)\n                {\n                    longestKeywordSize = size;\n                    longestKeyword = it;\n                }\n\n                typename std::vector<Match>::iterator next = it;\n                ++next;\n\n                if (next == matches.end())\n                    break;\n\n                if (it->mEnd <= next->mBeg)\n                {\n                    break; // no overlap\n                }\n            }\n\n            Match keyword = *longestKeyword;\n            matches.erase(longestKeyword);\n            out.push_back(keyword);\n            // erase anything that overlaps with the keyword we just added to the output\n            for (typename std::vector<Match>::iterator it = matches.begin(); it != matches.end();)\n            {\n                if (it->mBeg < keyword.mEnd && it->mEnd > keyword.mBeg)\n                    it = matches.erase(it);\n                else\n                    ++it;\n            }\n        }\n\n        std::sort(out.begin(), out.end(), sortMatches);\n    }\n\nprivate:\n\n    struct Entry\n    {\n        typedef std::map <wchar_t, Entry> childen_t;\n\n        string_t mKeyword;\n        value_t mValue;\n        childen_t mChildren;\n    };\n\n    void seed_impl (string_t keyword, value_t value, size_t depth, Entry  & entry)\n    {\n        int ch = Misc::StringUtils::toLower (keyword.at (depth));\n\n        typename Entry::childen_t::iterator j = entry.mChildren.find (ch);\n\n        if (j == entry.mChildren.end ())\n        {\n            entry.mChildren [ch].mValue = /*std::move*/ (value);\n            entry.mChildren [ch].mKeyword = /*std::move*/ (keyword);\n        }\n        else\n        {\n            if (j->second.mKeyword.size () > 0)\n            {\n                if (keyword == j->second.mKeyword)\n                    throw std::runtime_error (\"duplicate keyword inserted\");\n\n                value_t pushValue = /*std::move*/ (j->second.mValue);\n                string_t pushKeyword = /*std::move*/ (j->second.mKeyword);\n\n                if (depth >= pushKeyword.size ())\n                    throw std::runtime_error (\"unexpected\");\n\n                if (depth+1 < pushKeyword.size())\n                {\n                    seed_impl (/*std::move*/ (pushKeyword), /*std::move*/ (pushValue), depth+1, j->second);\n                    j->second.mKeyword.clear ();\n                }\n            }\n            if (depth+1 == keyword.size())\n                j->second.mKeyword = value;\n            else // depth+1 < keyword.size()\n                seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), depth+1, j->second);\n        }\n\n    }\n\n    Entry mRoot;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/quest.cpp",
    "content": "#include \"quest.hpp\"\n\n#include <components/esm/queststate.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\nnamespace MWDialogue\n{\n    Quest::Quest()\n    : Topic(), mIndex (0), mFinished (false)\n    {}\n\n    Quest::Quest (const std::string& topic)\n    : Topic (topic), mIndex (0), mFinished (false)\n    {}\n\n    Quest::Quest (const ESM::QuestState& state)\n    : Topic (state.mTopic), mIndex (state.mState), mFinished (state.mFinished!=0)\n    {}\n\n    std::string Quest::getName() const\n    {\n        const ESM::Dialogue *dialogue =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic);\n\n        for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin());\n            iter!=dialogue->mInfo.end(); ++iter)\n            if (iter->mQuestStatus==ESM::DialInfo::QS_Name)\n                return iter->mResponse;\n\n        return \"\";\n    }\n\n    int Quest::getIndex() const\n    {\n        return mIndex;\n    }\n\n    void Quest::setIndex (int index)\n    {\n        // The index must be set even if no related journal entry was found\n        mIndex = index;\n    }\n\n    bool Quest::isFinished() const\n    {\n        return mFinished;\n    }\n\n    void Quest::addEntry (const JournalEntry& entry)\n    {\n        int index = -1;\n\n        const ESM::Dialogue *dialogue =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (entry.mTopic);\n\n        for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin());\n            iter!=dialogue->mInfo.end(); ++iter)\n            if (iter->mId == entry.mInfoId)\n            {\n                index = iter->mData.mJournalIndex;\n                break;\n            }\n\n        if (index==-1)\n            throw std::runtime_error (\"unknown journal entry for topic \" + mTopic);\n\n        for (auto &info : dialogue->mInfo)\n        {\n            if (info.mData.mJournalIndex == index\n            && (info.mQuestStatus == ESM::DialInfo::QS_Finished || info.mQuestStatus == ESM::DialInfo::QS_Restart))\n            {\n                mFinished = (info.mQuestStatus == ESM::DialInfo::QS_Finished);\n                break;\n            }\n        }\n\n        if (index > mIndex)\n            mIndex = index;\n\n        for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter)\n            if (iter->mInfoId==entry.mInfoId)\n                return;\n\n        mEntries.push_back (entry); // we want slicing here\n    }\n\n    void Quest::write (ESM::QuestState& state) const\n    {\n        state.mTopic = getTopic();\n        state.mState = mIndex;\n        state.mFinished = mFinished;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/quest.hpp",
    "content": "#ifndef GAME_MWDIALOG_QUEST_H\n#define GAME_MWDIALOG_QUEST_H\n\n#include \"topic.hpp\"\n\nnamespace ESM\n{\n    struct QuestState;\n}\n\nnamespace MWDialogue\n{\n    /// \\brief A quest in progress or a completed quest\n    class Quest : public Topic\n    {\n            int mIndex;\n            bool mFinished;\n\n        public:\n\n            Quest();\n\n            Quest (const std::string& topic);\n\n            Quest (const ESM::QuestState& state);\n\n            std::string getName() const override;\n            ///< May be an empty string\n\n            int getIndex() const;\n\n            void setIndex (int index);\n            ///< Calling this function with a non-existent index will throw an exception.\n\n            bool isFinished() const;\n\n            void addEntry (const JournalEntry& entry) override;\n            ///< Add entry and adjust index accordingly.\n            ///\n            /// \\note Redundant entries are ignored, but the index is still adjusted.\n\n            void write (ESM::QuestState& state) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/scripttest.cpp",
    "content": "#include \"scripttest.hpp\"\n\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n\n#include \"../mwscript/compilercontext.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/compiler/exception.hpp>\n#include <components/compiler/streamerrorhandler.hpp>\n#include <components/compiler/scanner.hpp>\n#include <components/compiler/locals.hpp>\n#include <components/compiler/scriptparser.hpp>\n\n#include \"filter.hpp\"\n\nnamespace\n{\n\nvoid test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler::Extensions* extensions, int warningsMode)\n{\n    MWDialogue::Filter filter(actor, 0, false);\n\n    MWScript::CompilerContext compilerContext(MWScript::CompilerContext::Type_Dialogue);\n    compilerContext.setExtensions(extensions);\n    Compiler::StreamErrorHandler errorHandler;\n    errorHandler.setWarningsMode (warningsMode);\n\n    const MWWorld::Store<ESM::Dialogue>& dialogues = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();\n    for (MWWorld::Store<ESM::Dialogue>::iterator it = dialogues.begin(); it != dialogues.end(); ++it)\n    {\n        std::vector<const ESM::DialInfo*> infos = filter.listAll(*it);\n\n        for (std::vector<const ESM::DialInfo*>::iterator iter = infos.begin(); iter != infos.end(); ++iter)\n        {\n            const ESM::DialInfo* info = *iter;\n            if (!info->mResultScript.empty())\n            {\n                bool success = true;\n                ++total;\n                try\n                {\n                    errorHandler.reset();\n\n                    std::istringstream input (info->mResultScript + \"\\n\");\n\n                    Compiler::Scanner scanner (errorHandler, input, extensions);\n\n                    Compiler::Locals locals;\n\n                    std::string actorScript = actor.getClass().getScript(actor);\n\n                    if (!actorScript.empty())\n                    {\n                        // grab local variables from actor's script, if available.\n                        locals = MWBase::Environment::get().getScriptManager()->getLocals (actorScript);\n                    }\n\n                    Compiler::ScriptParser parser(errorHandler, compilerContext, locals, false);\n\n                    scanner.scan (parser);\n\n                    if (!errorHandler.isGood())\n                        success = false;\n\n                    ++compiled;\n                }\n                catch (const Compiler::SourceException& /* error */)\n                {\n                    // error has already been reported via error handler\n                    success = false;\n                }\n                catch (const std::exception& error)\n                {\n                    Log(Debug::Error) << std::string (\"Dialogue error: An exception has been thrown: \") + error.what();\n                    success = false;\n                }\n\n                if (!success)\n                {\n                    Log(Debug::Error) << \"Error: compiling failed (dialogue script): \\n\" << info->mResultScript << \"\\n\";\n                }\n            }\n        }\n    }\n}\n\n}\n\nnamespace MWDialogue\n{\n\nnamespace ScriptTest\n{\n\n    std::pair<int, int> compileAll(const Compiler::Extensions *extensions, int warningsMode)\n    {\n        int compiled = 0, total = 0;\n        const MWWorld::Store<ESM::NPC>& npcs = MWBase::Environment::get().getWorld()->getStore().get<ESM::NPC>();\n        for (MWWorld::Store<ESM::NPC>::iterator it = npcs.begin(); it != npcs.end(); ++it)\n        {\n            MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->mId);\n            test(ref.getPtr(), compiled, total, extensions, warningsMode);\n        }\n\n        const MWWorld::Store<ESM::Creature>& creatures = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>();\n        for (MWWorld::Store<ESM::Creature>::iterator it = creatures.begin(); it != creatures.end(); ++it)\n        {\n            MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->mId);\n            test(ref.getPtr(), compiled, total, extensions, warningsMode);\n        }\n        return std::make_pair(total, compiled);\n    }\n\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/scripttest.hpp",
    "content": "#ifndef OPENMW_MWDIALOGUE_SCRIPTTEST_H\n#define OPENMW_MWDIALOGUE_SCRIPTTEST_H\n\n#include <components/compiler/extensions.hpp>\n\nnamespace MWDialogue\n{\n\nnamespace ScriptTest\n{\n\n/// Attempt to compile all dialogue scripts, use for verification purposes\n/// @return A pair containing <total number of scripts, number of successfully compiled scripts>\nstd::pair<int, int> compileAll(const Compiler::Extensions* extensions, int warningsMode);\n\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/selectwrapper.cpp",
    "content": "#include \"selectwrapper.hpp\"\n\n#include <stdexcept>\n#include <sstream>\n#include <iterator>\n\n#include <components/misc/stringops.hpp>\n\nnamespace\n{\n    template<typename T1, typename T2>\n    bool selectCompareImp (char comp, T1 value1, T2 value2)\n    {\n        switch (comp)\n        {\n            case '0': return value1==value2;\n            case '1': return value1!=value2;\n            case '2': return value1>value2;\n            case '3': return value1>=value2;\n            case '4': return value1<value2;\n            case '5': return value1<=value2;\n        }\n\n        throw std::runtime_error (\"unknown compare type in dialogue info select\");\n    }\n\n    template<typename T>\n    bool selectCompareImp (const ESM::DialInfo::SelectStruct& select, T value1)\n    {\n        if (select.mValue.getType()==ESM::VT_Int)\n        {\n            return selectCompareImp (select.mSelectRule[4], value1, select.mValue.getInteger());\n        }\n        else if (select.mValue.getType()==ESM::VT_Float)\n        {\n            return selectCompareImp (select.mSelectRule[4], value1, select.mValue.getFloat());\n        }\n        else\n            throw std::runtime_error (\n                \"unsupported variable type in dialogue info select\");\n    }\n}\n\nMWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::decodeFunction() const\n{\n    int index = 0;\n\n    std::istringstream (mSelect.mSelectRule.substr(2,2)) >> index;\n\n    switch (index)\n    {\n        case  0: return Function_RankLow;\n        case  1: return Function_RankHigh;\n        case  2: return Function_RankRequirement;\n        case  3: return Function_Reputation;\n        case  4: return Function_HealthPercent;\n        case  5: return Function_PCReputation;\n        case  6: return Function_PcLevel;\n        case  7: return Function_PcHealthPercent;\n        case  8: case  9: return Function_PcDynamicStat;\n        case 10: return Function_PcAttribute;\n        case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20:\n        case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30:\n        case 31: case 32: case 33: case 34: case 35: case 36: case 37: return Function_PcSkill;\n        case 38: return Function_PcGender;\n        case 39: return Function_PcExpelled;\n        case 40: return Function_PcCommonDisease;\n        case 41: return Function_PcBlightDisease;\n        case 42: return Function_PcClothingModifier;\n        case 43: return Function_PcCrimeLevel;\n        case 44: return Function_SameGender;\n        case 45: return Function_SameRace;\n        case 46: return Function_SameFaction;\n        case 47: return Function_FactionRankDiff;\n        case 48: return Function_Detected;\n        case 49: return Function_Alarmed;\n        case 50: return Function_Choice;\n        case 51: case 52: case 53: case 54: case 55: case 56: case 57: return Function_PcAttribute;\n        case 58: return Function_PcCorprus;\n        case 59: return Function_Weather;\n        case 60: return Function_PcVampire;\n        case 61: return Function_Level;\n        case 62: return Function_Attacked;\n        case 63: return Function_TalkedToPc;\n        case 64: return Function_PcDynamicStat;\n        case 65: return Function_CreatureTargetted;\n        case 66: return Function_FriendlyHit;\n        case 67: case 68: case 69: case 70: return Function_AiSetting;\n        case 71: return Function_ShouldAttack;\n        case 72: return Function_Werewolf;\n        case 73: return Function_WerewolfKills;\n    }\n\n    return Function_False;\n}\n\nMWDialogue::SelectWrapper::SelectWrapper (const ESM::DialInfo::SelectStruct& select) : mSelect (select) {}\n\nMWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::getFunction() const\n{\n    char type = mSelect.mSelectRule[1];\n\n    switch (type)\n    {\n        case '1': return decodeFunction();\n        case '2': return Function_Global;\n        case '3': return Function_Local;\n        case '4': return Function_Journal;\n        case '5': return Function_Item;\n        case '6': return Function_Dead;\n        case '7': return Function_NotId;\n        case '8': return Function_NotFaction;\n        case '9': return Function_NotClass;\n        case 'A': return Function_NotRace;\n        case 'B': return Function_NotCell;\n        case 'C': return Function_NotLocal;\n    }\n\n    return Function_None;\n}\n\nint MWDialogue::SelectWrapper::getArgument() const\n{\n    if (mSelect.mSelectRule[1]!='1')\n        return 0;\n\n    int index = 0;\n\n    std::istringstream (mSelect.mSelectRule.substr(2,2)) >> index;\n\n    switch (index)\n    {\n        // AI settings\n        case 67: return 1;\n        case 68: return 0;\n        case 69: return 3;\n        case 70: return 2;\n\n        // attributes\n        case 10: return 0;\n        case 51: return 1;\n        case 52: return 2;\n        case 53: return 3;\n        case 54: return 4;\n        case 55: return 5;\n        case 56: return 6;\n        case 57: return 7;\n\n        // skills\n        case 11: return 0;\n        case 12: return 1;\n        case 13: return 2;\n        case 14: return 3;\n        case 15: return 4;\n        case 16: return 5;\n        case 17: return 6;\n        case 18: return 7;\n        case 19: return 8;\n        case 20: return 9;\n        case 21: return 10;\n        case 22: return 11;\n        case 23: return 12;\n        case 24: return 13;\n        case 25: return 14;\n        case 26: return 15;\n        case 27: return 16;\n        case 28: return 17;\n        case 29: return 18;\n        case 30: return 19;\n        case 31: return 20;\n        case 32: return 21;\n        case 33: return 22;\n        case 34: return 23;\n        case 35: return 24;\n        case 36: return 25;\n        case 37: return 26;\n\n        // dynamic stats\n        case  8: return 1;\n        case  9: return 2;\n        case 64: return 0;\n    }\n\n    return 0;\n}\n\nMWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const\n{\n    static const Function integerFunctions[] =\n    {\n        Function_Journal, Function_Item, Function_Dead,\n        Function_Choice,\n        Function_AiSetting,\n        Function_PcAttribute, Function_PcSkill,\n        Function_FriendlyHit,\n        Function_PcLevel, Function_PcGender, Function_PcClothingModifier,\n        Function_PcCrimeLevel,\n        Function_RankRequirement,\n        Function_Level, Function_PCReputation,\n        Function_Weather,\n        Function_Reputation, Function_FactionRankDiff,\n        Function_WerewolfKills,\n        Function_RankLow, Function_RankHigh,\n        Function_CreatureTargetted,\n        Function_None // end marker\n    };\n\n    static const Function numericFunctions[] =\n    {\n        Function_Global, Function_Local, Function_NotLocal,\n        Function_PcDynamicStat, Function_PcHealthPercent,\n        Function_HealthPercent,\n        Function_None // end marker\n    };\n\n    static const Function booleanFunctions[] =\n    {\n        Function_False,\n        Function_SameGender, Function_SameRace, Function_SameFaction,\n        Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus,\n        Function_PcExpelled,\n        Function_PcVampire, Function_TalkedToPc,\n        Function_Alarmed, Function_Detected,\n        Function_Attacked, Function_ShouldAttack,\n        Function_Werewolf,\n        Function_None // end marker\n    };\n\n    static const Function invertedBooleanFunctions[] =\n    {\n        Function_NotId, Function_NotFaction, Function_NotClass,\n        Function_NotRace, Function_NotCell,\n        Function_None // end marker\n    };\n\n    Function function = getFunction();\n\n    for (int i=0; integerFunctions[i]!=Function_None; ++i)\n        if (integerFunctions[i]==function)\n            return Type_Integer;\n\n    for (int i=0; numericFunctions[i]!=Function_None; ++i)\n        if (numericFunctions[i]==function)\n            return Type_Numeric;\n\n    for (int i=0; booleanFunctions[i]!=Function_None; ++i)\n        if (booleanFunctions[i]==function)\n            return Type_Boolean;\n\n    for (int i=0; invertedBooleanFunctions[i]!=Function_None; ++i)\n        if (invertedBooleanFunctions[i]==function)\n            return Type_Inverted;\n\n    return Type_None;\n}\n\nbool MWDialogue::SelectWrapper::isNpcOnly() const\n{\n    static const Function functions[] =\n    {\n        Function_NotFaction, Function_NotClass, Function_NotRace,\n        Function_SameGender, Function_SameRace, Function_SameFaction,\n        Function_RankRequirement,\n        Function_Reputation, Function_FactionRankDiff,\n        Function_Werewolf, Function_WerewolfKills,\n        Function_RankLow, Function_RankHigh,\n        Function_None // end marker\n    };\n\n    Function function = getFunction();\n\n    for (int i=0; functions[i]!=Function_None; ++i)\n        if (functions[i]==function)\n            return true;\n\n    return false;\n}\n\nbool MWDialogue::SelectWrapper::selectCompare (int value) const\n{\n    return selectCompareImp (mSelect, value);\n}\n\nbool MWDialogue::SelectWrapper::selectCompare (float value) const\n{\n    return selectCompareImp (mSelect, value);\n}\n\nbool MWDialogue::SelectWrapper::selectCompare (bool value) const\n{\n    return selectCompareImp (mSelect, static_cast<int> (value));\n}\n\nstd::string MWDialogue::SelectWrapper::getName() const\n{\n    return Misc::StringUtils::lowerCase (mSelect.mSelectRule.substr (5));\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/selectwrapper.hpp",
    "content": "#ifndef GAME_MWDIALOGUE_SELECTWRAPPER_H\n#define GAME_MWDIALOGUE_SELECTWRAPPER_H\n\n#include <components/esm/loadinfo.hpp>\n\nnamespace MWDialogue\n{\n    class SelectWrapper\n    {\n            const ESM::DialInfo::SelectStruct& mSelect;\n\n        public:\n\n            enum Function\n            {\n                Function_None, Function_False,\n                Function_Journal,\n                Function_Item,\n                Function_Dead,\n                Function_NotId,\n                Function_NotFaction,\n                Function_NotClass,\n                Function_NotRace,\n                Function_NotCell,\n                Function_NotLocal,\n                Function_Local,\n                Function_Global,\n                Function_SameGender, Function_SameRace, Function_SameFaction,\n                Function_Choice,\n                Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus,\n                Function_AiSetting,\n                Function_PcAttribute, Function_PcSkill,\n                Function_PcExpelled,\n                Function_PcVampire,\n                Function_FriendlyHit,\n                Function_TalkedToPc,\n                Function_PcLevel, Function_PcHealthPercent, Function_PcDynamicStat,\n                Function_PcGender, Function_PcClothingModifier, Function_PcCrimeLevel,\n                Function_RankRequirement,\n                Function_HealthPercent, Function_Level, Function_PCReputation,\n                Function_Weather,\n                Function_Reputation, Function_Alarmed, Function_FactionRankDiff, Function_Detected,\n                Function_Attacked, Function_ShouldAttack,\n                Function_CreatureTargetted,\n                Function_Werewolf, Function_WerewolfKills,\n                Function_RankLow, Function_RankHigh\n            };\n\n            enum Type\n            {\n                Type_None,\n                Type_Integer,\n                Type_Numeric,\n                Type_Boolean,\n                Type_Inverted\n            };\n\n        private:\n\n            Function decodeFunction() const;\n\n        public:\n\n            SelectWrapper (const ESM::DialInfo::SelectStruct& select);\n\n            Function getFunction() const;\n\n            int getArgument() const;\n\n            Type getType() const;\n\n            bool isNpcOnly() const;\n            ///< \\attention Do not call any of the select functions for this select struct!\n\n            bool selectCompare (int value) const;\n\n            bool selectCompare (float value) const;\n\n            bool selectCompare (bool value) const;\n\n            std::string getName() const;\n            ///< Return case-smashed name.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwdialogue/topic.cpp",
    "content": "#include \"topic.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace MWDialogue\n{\n    Topic::Topic()\n    {}\n\n    Topic::Topic (const std::string& topic)\n    : mTopic (topic), mName (\n      MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic)->mId)\n    {}\n\n    Topic::~Topic()\n    {}\n\n    void Topic::addEntry (const JournalEntry& entry)\n    {\n        if (entry.mTopic!=mTopic)\n            throw std::runtime_error (\"topic does not match: \" + mTopic);\n\n        // bail out if we already have heard this\n        for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it)\n        {\n            if (it->mInfoId == entry.mInfoId)\n                return;\n        }\n\n        mEntries.push_back (entry); // we want slicing here\n    }\n\n    void Topic::insertEntry (const ESM::JournalEntry& entry)\n    {\n        mEntries.push_back (entry);\n    }\n\n    std::string Topic::getTopic() const\n    {\n        return mTopic;\n    }\n\n    std::string Topic::getName() const\n    {\n        return mName;\n    }\n\n    Topic::TEntryIter Topic::begin() const\n    {\n        return mEntries.begin();\n    }\n\n    Topic::TEntryIter Topic::end() const\n    {\n        return mEntries.end();\n    }\n\n    void Topic::removeLastAddedResponse (const std::string& actorName)\n    {\n        for (std::vector<MWDialogue::Entry>::reverse_iterator it = mEntries.rbegin();\n             it != mEntries.rend(); ++it)\n        {\n            if (it->mActorName == actorName)\n            {\n                mEntries.erase( (++it).base() ); // erase doesn't take a reverse_iterator\n                return;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwdialogue/topic.hpp",
    "content": "#ifndef GAME_MWDIALOG_TOPIC_H\n#define GAME_MWDIALOG_TOPIC_H\n\n#include <string>\n#include <vector>\n\n#include \"journalentry.hpp\"\n\nnamespace ESM\n{\n    struct JournalEntry;\n}\n\nnamespace MWDialogue\n{\n    /// \\brief Collection of seen responses for a topic\n    class Topic\n    {\n        public:\n\n            typedef std::vector<Entry> TEntryContainer;\n            typedef TEntryContainer::const_iterator TEntryIter;\n\n        protected:\n\n            std::string mTopic;\n            std::string mName;\n            TEntryContainer mEntries;\n\n        public:\n\n            Topic();\n\n            Topic (const std::string& topic);\n\n            virtual ~Topic();\n\n            virtual void addEntry (const JournalEntry& entry);\n            ///< Add entry\n            ///\n            /// \\note Redundant entries are ignored.\n\n            void insertEntry (const ESM::JournalEntry& entry);\n            ///< Add entry without checking for redundant entries or modifying the state of the\n            /// topic otherwise\n\n            std::string getTopic() const;\n\n            virtual std::string getName() const;\n\n            void removeLastAddedResponse (const std::string& actorName);\n\n            TEntryIter begin() const;\n            ///< Iterator pointing to the begin of the journal for this topic.\n\n            TEntryIter end() const;\n            ///< Iterator pointing past the end of the journal for this topic.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/alchemywindow.cpp",
    "content": "#include \"alchemywindow.hpp\"\n\n#include <MyGUI_Gui.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_EditBox.h>\n#include <MyGUI_ComboBox.h>\n#include <MyGUI_ControllerManager.h>\n#include <MyGUI_ControllerRepeatClick.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/magiceffects.hpp\"\n#include \"../mwmechanics/alchemy.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include <MyGUI_Macros.h>\n#include <components/esm/records.hpp>\n\n#include \"inventoryitemmodel.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n#include \"itemview.hpp\"\n#include \"itemwidget.hpp\"\n#include \"widgets.hpp\"\n\nnamespace MWGui\n{\n    AlchemyWindow::AlchemyWindow()\n        : WindowBase(\"openmw_alchemy_window.layout\")\n        , mCurrentFilter(FilterType::ByName)\n        , mModel(nullptr)\n        , mSortModel(nullptr)\n        , mAlchemy(new MWMechanics::Alchemy())\n        , mApparatus (4)\n        , mIngredients (4)\n    {\n        getWidget(mCreateButton, \"CreateButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mIngredients[0], \"Ingredient1\");\n        getWidget(mIngredients[1], \"Ingredient2\");\n        getWidget(mIngredients[2], \"Ingredient3\");\n        getWidget(mIngredients[3], \"Ingredient4\");\n        getWidget(mApparatus[0], \"Apparatus1\");\n        getWidget(mApparatus[1], \"Apparatus2\");\n        getWidget(mApparatus[2], \"Apparatus3\");\n        getWidget(mApparatus[3], \"Apparatus4\");\n        getWidget(mEffectsBox, \"CreatedEffects\");\n        getWidget(mBrewCountEdit, \"BrewCount\");\n        getWidget(mIncreaseButton, \"IncreaseButton\");\n        getWidget(mDecreaseButton, \"DecreaseButton\");\n        getWidget(mNameEdit, \"NameEdit\");\n        getWidget(mItemView, \"ItemView\");\n        getWidget(mFilterValue, \"FilterValue\");\n        getWidget(mFilterType, \"FilterType\");\n\n        mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged);\n        mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);\n        mBrewCountEdit->setMinValue(1);\n        mBrewCountEdit->setValue(1);\n\n        mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onIncreaseButtonPressed);\n        mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased);\n        mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onDecreaseButtonPressed);\n        mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased);\n\n        mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem);\n\n        mIngredients[0]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);\n        mIngredients[1]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);\n        mIngredients[2]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);\n        mIngredients[3]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);\n\n        mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked);\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);\n\n        mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);\n        mFilterValue->eventComboChangePosition += MyGUI::newDelegate(this, &AlchemyWindow::onFilterChanged);\n        mFilterValue->eventEditTextChange += MyGUI::newDelegate(this, &AlchemyWindow::onFilterEdited);\n        mFilterType->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::switchFilterType);\n\n        center();\n    }\n\n    void AlchemyWindow::onAccept(MyGUI::EditBox* sender)\n    {\n        onCreateButtonClicked(sender);\n\n        // To do not spam onAccept() again and again\n        MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);\n    }\n\n    void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy);\n    }\n\n    void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender)\n    {\n        mAlchemy->setPotionName(mNameEdit->getCaption());\n        int count = mAlchemy->countPotionsToBrew();\n        count = std::min(count, mBrewCountEdit->getValue());\n        createPotions(count);\n    }\n\n    void AlchemyWindow::createPotions(int count)\n    {\n        MWMechanics::Alchemy::Result result = mAlchemy->create(mNameEdit->getCaption(), count);\n        MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n\n        /*\n            Start of tes3mp addition\n\n            Declare objectList here so we can use it below\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        /*\n            End of tes3mp addition\n        */\n\n        switch (result)\n        {\n        case MWMechanics::Alchemy::Result_NoName:\n            winMgr->messageBox(\"#{sNotifyMessage37}\");\n            break;\n        case MWMechanics::Alchemy::Result_NoMortarAndPestle:\n            winMgr->messageBox(\"#{sNotifyMessage45}\");\n            break;\n        case MWMechanics::Alchemy::Result_LessThanTwoIngredients:\n            winMgr->messageBox(\"#{sNotifyMessage6a}\");\n            break;\n        case MWMechanics::Alchemy::Result_Success:\n            winMgr->playSound(\"potion success\");\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_OBJECT_SOUND packet every time the player makes a sound here\n            */\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectSound(MWMechanics::getPlayer(), \"potion success\", 1.0, 1.0);\n            objectList->sendObjectSound();\n            /*\n                End of tes3mp addition\n            */\n\n            if (count == 1)\n                winMgr->messageBox(\"#{sPotionSuccess}\");\n            else\n                winMgr->messageBox(\"#{sPotionSuccess} \"+mNameEdit->getCaption()+\" (\"+std::to_string(count)+\")\");\n            break;\n        case MWMechanics::Alchemy::Result_NoEffects:\n        case MWMechanics::Alchemy::Result_RandomFailure:\n            winMgr->messageBox(\"#{sNotifyMessage8}\");\n            winMgr->playSound(\"potion fail\");\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_OBJECT_SOUND packet every time the player makes a sound here\n            */\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectSound(MWMechanics::getPlayer(), \"potion fail\", 1.0, 1.0);\n            objectList->sendObjectSound();\n            /*\n                End of tes3mp addition\n            */\n\n            break;\n        }\n\n        // remove ingredient slots that have been fully used up\n        for (int i=0; i<4; ++i)\n            if (mIngredients[i]->isUserString(\"ToolTipType\"))\n            {\n                MWWorld::Ptr ingred = *mIngredients[i]->getUserData<MWWorld::Ptr>();\n                if (ingred.getRefData().getCount() == 0)\n                    removeIngredient(mIngredients[i]);\n            }\n\n        updateFilters();\n        update();\n    }\n\n    void AlchemyWindow::initFilter()\n    {\n        auto const& wm = MWBase::Environment::get().getWindowManager();\n        auto const ingredient  = wm->getGameSettingString(\"sIngredients\", \"Ingredients\");\n        auto const effect = wm->getGameSettingString(\"sMagicEffects\", \"Magic Effects\");\n\n        if (mFilterType->getCaption() == ingredient)\n            mCurrentFilter = FilterType::ByName;\n        else\n            mCurrentFilter = FilterType::ByEffect;\n        updateFilters();\n        mFilterValue->clearIndexSelected();\n        updateFilters();\n    }\n\n    void AlchemyWindow::switchFilterType(MyGUI::Widget* _sender)\n    {\n        auto const& wm = MWBase::Environment::get().getWindowManager();\n        auto const ingredient  = wm->getGameSettingString(\"sIngredients\", \"Ingredients\");\n        auto const effect = wm->getGameSettingString(\"sMagicEffects\", \"Magic Effects\");\n        auto *button = _sender->castType<MyGUI::Button>();\n\n        if (button->getCaption() == ingredient)\n        {\n            button->setCaption(effect);\n            mCurrentFilter = FilterType::ByEffect;\n        }\n        else\n        {\n            button->setCaption(ingredient);\n            mCurrentFilter = FilterType::ByName;\n        }\n        mSortModel->setNameFilter({});\n        mSortModel->setEffectFilter({});\n        mFilterValue->clearIndexSelected();\n        updateFilters();\n        mItemView->update();\n    }\n\n    void AlchemyWindow::updateFilters()\n    {\n        std::set<std::string> itemNames, itemEffects;\n        for (size_t i = 0; i < mModel->getItemCount(); ++i)\n        {\n            MWWorld::Ptr item = mModel->getItem(i).mBase;\n            if (item.getTypeName() != typeid(ESM::Ingredient).name())\n                continue;\n\n            itemNames.insert(item.getClass().getName(item));\n\n            MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n            auto const alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);\n\n            auto const effects = MWMechanics::Alchemy::effectsDescription(item, alchemySkill);\n            itemEffects.insert(effects.begin(), effects.end());\n        }\n\n        mFilterValue->removeAllItems();\n        auto const addItems = [&](auto const& container)\n        {\n            for (auto const& item : container)\n                mFilterValue->addItem(item);\n        };\n        switch (mCurrentFilter)\n        {\n            case FilterType::ByName: addItems(itemNames); break;\n            case FilterType::ByEffect: addItems(itemEffects); break;\n        }\n    }\n\n    void AlchemyWindow::applyFilter(const std::string& filter)\n    {\n        switch (mCurrentFilter)\n        {\n            case FilterType::ByName: mSortModel->setNameFilter(filter); break;\n            case FilterType::ByEffect: mSortModel->setEffectFilter(filter); break;\n        }\n        mItemView->update();\n    }\n\n    void AlchemyWindow::onFilterChanged(MyGUI::ComboBox* _sender, size_t _index)\n    {\n        // ignore spurious event fired when one edit the content after selection.\n        // onFilterEdited will handle it.\n        if (_index != MyGUI::ITEM_NONE)\n            applyFilter(_sender->getItemNameAt(_index));\n    }\n\n    void AlchemyWindow::onFilterEdited(MyGUI::EditBox* _sender)\n    {\n        applyFilter(_sender->getCaption());\n    }\n\n    void AlchemyWindow::onOpen()\n    {\n        mAlchemy->clear();\n        mAlchemy->setAlchemist (MWMechanics::getPlayer());\n\n        mModel = new InventoryItemModel(MWMechanics::getPlayer());\n        mSortModel = new SortFilterItemModel(mModel);\n        mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients);\n        mItemView->setModel (mSortModel);\n        mItemView->resetScrollBars();\n\n        mNameEdit->setCaption(\"\");\n        mBrewCountEdit->setValue(1);\n\n        int index = 0;\n        for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools());\n            iter!=mAlchemy->endTools() && index<static_cast<int> (mApparatus.size()); ++iter, ++index)\n        {\n            mApparatus.at (index)->setItem(*iter);\n            mApparatus.at (index)->clearUserStrings();\n            if (!iter->isEmpty())\n            {\n                mApparatus.at (index)->setUserString (\"ToolTipType\", \"ItemPtr\");\n                mApparatus.at (index)->setUserData (MWWorld::Ptr(*iter));\n            }\n        }\n\n        update();\n        initFilter();\n\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);\n    }\n\n    void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender)\n    {\n        removeIngredient(_sender);\n        update();\n    }\n\n    void AlchemyWindow::onSelectedItem(int index)\n    {\n        MWWorld::Ptr item = mSortModel->getItem(index).mBase;\n        int res = mAlchemy->addIngredient(item);\n\n        if (res != -1)\n        {\n            update();\n\n            std::string sound = item.getClass().getUpSoundId(item);\n            MWBase::Environment::get().getWindowManager()->playSound(sound);\n        }\n    }\n\n    void AlchemyWindow::update()\n    {\n        std::string suggestedName = mAlchemy->suggestPotionName();\n        if (suggestedName != mSuggestedPotionName)\n            mNameEdit->setCaptionWithReplacing(suggestedName);\n        mSuggestedPotionName = suggestedName;\n\n        mSortModel->clearDragItems();\n\n        MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy->beginIngredients ();\n        for (int i=0; i<4; ++i)\n        {\n            ItemWidget* ingredient = mIngredients[i];\n\n            MWWorld::Ptr item;\n            if (it != mAlchemy->endIngredients ())\n            {\n                item = *it;\n                ++it;\n            }\n\n            if (!item.isEmpty())\n                mSortModel->addDragItem(item, item.getRefData().getCount());\n\n            if (ingredient->getChildCount())\n                MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0));\n\n            ingredient->clearUserStrings ();\n\n            ingredient->setItem(item);\n\n            if (item.isEmpty ())\n                continue;\n\n            ingredient->setUserString(\"ToolTipType\", \"ItemPtr\");\n            ingredient->setUserData(MWWorld::Ptr(item));\n\n            ingredient->setCount(item.getRefData().getCount());\n        }\n\n        mItemView->update();\n\n        std::set<MWMechanics::EffectKey> effectIds = mAlchemy->listEffects();\n        Widgets::SpellEffectList list;\n        unsigned int effectIndex=0;\n        for (const MWMechanics::EffectKey& effectKey : effectIds)\n        {\n            Widgets::SpellEffectParams params;\n            params.mEffectID = effectKey.mId;\n            const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectKey.mId);\n            if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)\n                params.mSkill = effectKey.mArg;\n            else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)\n                params.mAttribute = effectKey.mArg;\n            params.mIsConstant = true;\n            params.mNoTarget = true;\n            params.mNoMagnitude = true;\n\n            params.mKnown = mAlchemy->knownEffect(effectIndex, MWBase::Environment::get().getWorld()->getPlayerPtr());\n\n            list.push_back(params);\n            ++effectIndex;\n        }\n\n        while (mEffectsBox->getChildCount())\n            MyGUI::Gui::getInstance().destroyWidget(mEffectsBox->getChildAt(0));\n\n        MyGUI::IntCoord coord(0, 0, mEffectsBox->getWidth(), 24);\n        Widgets::MWEffectListPtr effectsWidget = mEffectsBox->createWidget<Widgets::MWEffectList>\n            (\"MW_StatName\", coord, MyGUI::Align::Left | MyGUI::Align::Top);\n\n        effectsWidget->setEffectList(list);\n\n        std::vector<MyGUI::Widget*> effectItems;\n        effectsWidget->createEffectWidgets(effectItems, mEffectsBox, coord, false, 0);\n        effectsWidget->setCoord(coord);\n    }\n\n    void AlchemyWindow::removeIngredient(MyGUI::Widget* ingredient)\n    {\n        for (int i=0; i<4; ++i)\n            if (mIngredients[i] == ingredient)\n                mAlchemy->removeIngredient (i);\n\n        update();\n    }\n\n    void AlchemyWindow::addRepeatController(MyGUI::Widget *widget)\n    {\n        MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(MyGUI::ControllerRepeatClick::getClassTypeName());\n        MyGUI::ControllerRepeatClick* controller = static_cast<MyGUI::ControllerRepeatClick*>(item);\n        controller->eventRepeatClick += newDelegate(this, &AlchemyWindow::onRepeatClick);\n        MyGUI::ControllerManager::getInstance().addItem(widget, controller);\n    }\n\n    void AlchemyWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)\n    {\n        addRepeatController(_sender);\n        onIncreaseButtonTriggered();\n    }\n\n    void AlchemyWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)\n    {\n        addRepeatController(_sender);\n        onDecreaseButtonTriggered();\n    }\n\n    void AlchemyWindow::onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller)\n    {\n        if (widget == mIncreaseButton)\n            onIncreaseButtonTriggered();\n        else if (widget == mDecreaseButton)\n            onDecreaseButtonTriggered();\n    }\n\n    void AlchemyWindow::onCountButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id)\n    {\n        MyGUI::ControllerManager::getInstance().removeItem(_sender);\n    }\n\n    void AlchemyWindow::onCountValueChanged(int value)\n    {\n        mBrewCountEdit->setValue(std::abs(value));\n    }\n\n    void AlchemyWindow::onIncreaseButtonTriggered()\n    {\n        int currentCount = mBrewCountEdit->getValue();\n\n        // prevent overflows\n        if (currentCount == std::numeric_limits<int>::max())\n            return;\n\n        mBrewCountEdit->setValue(currentCount+1);\n    }\n\n    void AlchemyWindow::onDecreaseButtonTriggered()\n    {\n        int currentCount = mBrewCountEdit->getValue();\n        if (currentCount > 1)\n            mBrewCountEdit->setValue(currentCount-1);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/alchemywindow.hpp",
    "content": "#ifndef MWGUI_ALCHEMY_H\n#define MWGUI_ALCHEMY_H\n\n#include <memory>\n#include <vector>\n\n#include <MyGUI_ControllerItem.h>\n#include <MyGUI_ComboBox.h>\n\n#include <components/widgets/box.hpp>\n#include <components/widgets/numericeditbox.hpp>\n\n#include \"windowbase.hpp\"\n\nnamespace MWMechanics\n{\n    class Alchemy;\n}\n\nnamespace MWGui\n{\n    class ItemView;\n    class ItemWidget;\n    class InventoryItemModel;\n    class SortFilterItemModel;\n\n    class AlchemyWindow : public WindowBase\n    {\n    public:\n        AlchemyWindow();\n\n        void onOpen() override;\n\n        void onResChange(int, int) override { center(); }\n\n    private:\n\n        static const float sCountChangeInitialPause; // in seconds\n        static const float sCountChangeInterval; // in seconds\n\n        std::string mSuggestedPotionName;\n        enum class FilterType { ByName, ByEffect };\n        FilterType mCurrentFilter;\n\n        ItemView* mItemView;\n        InventoryItemModel* mModel;\n        SortFilterItemModel* mSortModel;\n\n        MyGUI::Button* mCreateButton;\n        MyGUI::Button* mCancelButton;\n\n        MyGUI::Widget* mEffectsBox;\n\n        MyGUI::Button* mIncreaseButton;\n        MyGUI::Button* mDecreaseButton;\n        Gui::AutoSizedButton* mFilterType;\n        MyGUI::ComboBox* mFilterValue;\n        MyGUI::EditBox* mNameEdit;\n        Gui::NumericEditBox* mBrewCountEdit;\n\n        void onCancelButtonClicked(MyGUI::Widget* _sender);\n        void onCreateButtonClicked(MyGUI::Widget* _sender);\n        void onIngredientSelected(MyGUI::Widget* _sender);\n        void onAccept(MyGUI::EditBox*);\n        void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);\n        void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);\n        void onCountButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);\n        void onCountValueChanged(int value);\n        void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller);\n\n        void applyFilter(const std::string& filter);\n        void initFilter();\n        void onFilterChanged(MyGUI::ComboBox* _sender, size_t _index);\n        void onFilterEdited(MyGUI::EditBox* _sender);\n        void switchFilterType(MyGUI::Widget* _sender);\n        void updateFilters();\n\n        void addRepeatController(MyGUI::Widget* widget);\n\n        void onIncreaseButtonTriggered();\n        void onDecreaseButtonTriggered();\n\n        void onSelectedItem(int index);\n\n        void removeIngredient(MyGUI::Widget* ingredient);\n\n        void createPotions(int count);\n\n        void update();\n\n        std::unique_ptr<MWMechanics::Alchemy> mAlchemy;\n\n        std::vector<ItemWidget*> mApparatus;\n        std::vector<ItemWidget*> mIngredients;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/backgroundimage.cpp",
    "content": "#include \"backgroundimage.hpp\"\n\n#include <MyGUI_Gui.h>\n\nnamespace MWGui\n{\n\nvoid BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool stretch)\n{\n    if (mChild)\n    {\n        MyGUI::Gui::getInstance().destroyWidget(mChild);\n        mChild = nullptr;\n    }\n    if (!stretch)\n    {\n        setImageTexture(\"black\");\n\n        if (fixedRatio)\n            mAspect = 4.0/3.0;\n        else\n            mAspect = 0; // TODO\n\n        mChild = createWidgetReal<MyGUI::ImageBox>(\"ImageBox\",\n            MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);\n        mChild->setImageTexture(image);\n\n        adjustSize();\n    }\n    else\n    {\n        mAspect = 0;\n        setImageTexture(image);\n    }\n}\n\nvoid BackgroundImage::adjustSize()\n{\n    if (mAspect == 0)\n        return;\n\n    MyGUI::IntSize screenSize = getSize();\n\n    int leftPadding = std::max(0, static_cast<int>(screenSize.width - screenSize.height * mAspect) / 2);\n    int topPadding = std::max(0, static_cast<int>(screenSize.height - screenSize.width / mAspect) / 2);\n\n    mChild->setCoord(leftPadding, topPadding, screenSize.width - leftPadding*2, screenSize.height - topPadding*2);\n}\n\nvoid BackgroundImage::setSize (const MyGUI::IntSize& _value)\n{\n    MyGUI::Widget::setSize (_value);\n    adjustSize();\n}\n\nvoid BackgroundImage::setCoord (const MyGUI::IntCoord& _value)\n{\n    MyGUI::Widget::setCoord (_value);\n    adjustSize();\n}\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/backgroundimage.hpp",
    "content": "#ifndef OPENMW_MWGUI_BACKGROUNDIMAGE_H\n#define OPENMW_MWGUI_BACKGROUNDIMAGE_H\n\n#include <MyGUI_ImageBox.h>\n\nnamespace MWGui\n{\n\n    /**\n     * @brief A variant of MyGUI::ImageBox with aspect ratio correction using black bars\n     */\n    class BackgroundImage final : public MyGUI::ImageBox\n    {\n    MYGUI_RTTI_DERIVED(BackgroundImage)\n\n    public:\n        BackgroundImage() : mChild(nullptr), mAspect(0) {}\n\n        /**\n         * @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions\n         * @param stretch Stretch to fill the whole screen, or add black bars?\n         */\n        void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool stretch=true);\n\n        void setSize (const MyGUI::IntSize &_value) override;\n        void setCoord (const MyGUI::IntCoord &_value) override;\n\n    private:\n        MyGUI::ImageBox* mChild;\n        double mAspect;\n\n        void adjustSize();\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/birth.cpp",
    "content": "#include \"birth.hpp\"\n\n#include <MyGUI_ListBox.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_ScrollView.h>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"widgets.hpp\"\n\nnamespace\n{\n\n    bool sortBirthSigns(const std::pair<std::string, const ESM::BirthSign*>& left, const std::pair<std::string, const ESM::BirthSign*>& right)\n    {\n        return left.second->mName.compare (right.second->mName) < 0;\n    }\n\n}\n\nnamespace MWGui\n{\n\n    BirthDialog::BirthDialog()\n      : WindowModal(\"openmw_chargen_birth.layout\")\n    {\n        // Centre dialog\n        center();\n\n        getWidget(mSpellArea, \"SpellArea\");\n\n        getWidget(mBirthImage, \"BirthsignImage\");\n\n        getWidget(mBirthList, \"BirthsignList\");\n        mBirthList->setScrollVisible(true);\n        mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onAccept);\n        mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);\n\n        MyGUI::Button* backButton;\n        getWidget(backButton, \"BackButton\");\n        backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked);\n\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOK\", \"\"));\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked);\n\n        updateBirths();\n        updateSpells();\n    }\n\n    void BirthDialog::setNextButtonShow(bool shown)\n    {\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n\n        if (shown)\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sNext\", \"\"));\n        else\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOK\", \"\"));\n    }\n\n    void BirthDialog::onOpen()\n    {\n        WindowModal::onOpen();\n        updateBirths();\n        updateSpells();\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mBirthList);\n\n        // Show the current birthsign by default\n        const std::string &signId =\n            MWBase::Environment::get().getWorld()->getPlayer().getBirthSign();\n\n        if (!signId.empty())\n            setBirthId(signId);\n    }\n\n    void BirthDialog::setBirthId(const std::string &birthId)\n    {\n        mCurrentBirthId = birthId;\n        mBirthList->setIndexSelected(MyGUI::ITEM_NONE);\n        size_t count = mBirthList->getItemCount();\n        for (size_t i = 0; i < count; ++i)\n        {\n            if (Misc::StringUtils::ciEqual(*mBirthList->getItemDataAt<std::string>(i), birthId))\n            {\n                mBirthList->setIndexSelected(i);\n                break;\n            }\n        }\n\n        updateSpells();\n    }\n\n    // widget controls\n\n    void BirthDialog::onOkClicked(MyGUI::Widget* _sender)\n    {\n        if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE)\n            return;\n        eventDone(this);\n    }\n\n    void BirthDialog::onAccept(MyGUI::ListBox *_sender, size_t _index)\n    {\n        onSelectBirth(_sender, _index);\n        if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE)\n            return;\n        eventDone(this);\n    }\n\n    void BirthDialog::onBackClicked(MyGUI::Widget* _sender)\n    {\n        eventBack();\n    }\n\n    void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index)\n    {\n        if (_index == MyGUI::ITEM_NONE)\n            return;\n\n        const std::string *birthId = mBirthList->getItemDataAt<std::string>(_index);\n        if (Misc::StringUtils::ciEqual(mCurrentBirthId, *birthId))\n            return;\n\n        mCurrentBirthId = *birthId;\n        updateSpells();\n    }\n\n    // update widget content\n\n    void BirthDialog::updateBirths()\n    {\n        mBirthList->removeAllItems();\n\n        const MWWorld::Store<ESM::BirthSign> &signs =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>();\n\n        // sort by name\n        std::vector < std::pair<std::string, const ESM::BirthSign*> > birthSigns;\n\n        for (const ESM::BirthSign& sign : signs)\n        {\n            birthSigns.emplace_back(sign.mId, &sign);\n        }\n        std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns);\n\n        int index = 0;\n        for (auto& birthsignPair : birthSigns)\n        {\n            mBirthList->addItem(birthsignPair.second->mName, birthsignPair.first);\n            if (mCurrentBirthId.empty())\n            {\n                mBirthList->setIndexSelected(index);\n                mCurrentBirthId = birthsignPair.first;\n            }\n            else if (Misc::StringUtils::ciEqual(birthsignPair.first, mCurrentBirthId))\n            {\n                mBirthList->setIndexSelected(index);\n            }\n\n            index++;\n        }\n    }\n\n    void BirthDialog::updateSpells()\n    {\n        for (MyGUI::Widget* widget : mSpellItems)\n        {\n            MyGUI::Gui::getInstance().destroyWidget(widget);\n        }\n        mSpellItems.clear();\n\n        if (mCurrentBirthId.empty())\n            return;\n\n        Widgets::MWSpellPtr spellWidget;\n        const int lineHeight = 18;\n        MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18);\n\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        const ESM::BirthSign *birth =\n            store.get<ESM::BirthSign>().find(mCurrentBirthId);\n\n        mBirthImage->setImageTexture(MWBase::Environment::get().getWindowManager()->correctTexturePath(birth->mTexture));\n\n        std::vector<std::string> abilities, powers, spells;\n\n        std::vector<std::string>::const_iterator it = birth->mPowers.mList.begin();\n        std::vector<std::string>::const_iterator end = birth->mPowers.mList.end();\n        for (; it != end; ++it)\n        {\n            const std::string &spellId = *it;\n            const ESM::Spell *spell = store.get<ESM::Spell>().search(spellId);\n            if (!spell)\n                continue; // Skip spells which cannot be found\n            ESM::Spell::SpellType type = static_cast<ESM::Spell::SpellType>(spell->mData.mType);\n            if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power)\n                continue; // We only want spell, ability and powers.\n\n            if (type == ESM::Spell::ST_Ability)\n                abilities.push_back(spellId);\n            else if (type == ESM::Spell::ST_Power)\n                powers.push_back(spellId);\n            else if (type == ESM::Spell::ST_Spell)\n                spells.push_back(spellId);\n        }\n\n        int i = 0;\n\n        struct {\n            const std::vector<std::string> &spells;\n            const char *label;\n        }\n        categories[3] = {\n            {abilities, \"sBirthsignmenu1\"},\n            {powers,    \"sPowers\"},\n            {spells,    \"sBirthsignmenu2\"}\n        };\n\n        for (int category = 0; category < 3; ++category)\n        {\n            if (!categories[category].spells.empty())\n            {\n                MyGUI::TextBox* label = mSpellArea->createWidget<MyGUI::TextBox>(\"SandBrightText\", coord, MyGUI::Align::Default, std::string(\"Label\"));\n                label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, \"\"));\n                mSpellItems.push_back(label);\n                coord.top += lineHeight;\n\n                end = categories[category].spells.end();\n                for (it = categories[category].spells.begin(); it != end; ++it)\n                {\n                    const std::string &spellId = *it;\n                    spellWidget = mSpellArea->createWidget<Widgets::MWSpell>(\"MW_StatName\", coord, MyGUI::Align::Default, std::string(\"Spell\") + MyGUI::utility::toString(i));\n                    spellWidget->setSpellId(spellId);\n\n                    mSpellItems.push_back(spellWidget);\n                    coord.top += lineHeight;\n\n                    MyGUI::IntCoord spellCoord = coord;\n                    spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template?\n                    spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? Widgets::MWEffectList::EF_Constant : 0);\n                    coord.top = spellCoord.top;\n\n                    ++i;\n                }\n            }\n        }\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mSpellArea->setVisibleVScroll(false);\n        mSpellArea->setCanvasSize(MyGUI::IntSize(mSpellArea->getWidth(), std::max(mSpellArea->getHeight(), coord.top)));\n        mSpellArea->setVisibleVScroll(true);\n        mSpellArea->setViewOffset(MyGUI::IntPoint(0, 0));\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/birth.hpp",
    "content": "#ifndef MWGUI_BIRTH_H\n#define MWGUI_BIRTH_H\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n    class BirthDialog : public WindowModal\n    {\n    public:\n        BirthDialog();\n\n        enum Gender\n        {\n            GM_Male,\n            GM_Female\n        };\n\n        const std::string &getBirthId() const { return mCurrentBirthId; }\n        void setBirthId(const std::string &raceId);\n\n        void setNextButtonShow(bool shown);\n        void onOpen() override;\n\n        bool exit() override { return false; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        /** Event : Back button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventBack;\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n    protected:\n        void onSelectBirth(MyGUI::ListBox* _sender, size_t _index);\n\n        void onAccept(MyGUI::ListBox* _sender, size_t index);\n        void onOkClicked(MyGUI::Widget* _sender);\n        void onBackClicked(MyGUI::Widget* _sender);\n\n    private:\n        void updateBirths();\n        void updateSpells();\n\n        MyGUI::ListBox* mBirthList;\n        MyGUI::ScrollView* mSpellArea;\n        MyGUI::ImageBox* mBirthImage;\n        std::vector<MyGUI::Widget*> mSpellItems;\n\n        std::string mCurrentBirthId;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/bookpage.cpp",
    "content": "#include \"bookpage.hpp\"\n\n#include <optional>\n\n#include \"MyGUI_RenderItem.h\"\n#include \"MyGUI_RenderManager.h\"\n#include \"MyGUI_TextureUtility.h\"\n#include \"MyGUI_FactoryManager.h\"\n\n#include <components/misc/utf8stream.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\nnamespace MWGui\n{\nstruct TypesetBookImpl;\nclass PageDisplay;\nclass BookPageImpl;\n\nstatic bool ucsSpace (int codePoint);\nstatic bool ucsLineBreak (int codePoint);\nstatic bool ucsCarriageReturn (int codePoint);\nstatic bool ucsBreakingSpace (int codePoint);\n\nstruct BookTypesetter::Style { virtual ~Style () {} };\n\nstruct TypesetBookImpl : TypesetBook\n{\n    typedef std::vector <uint8_t> Content;\n    typedef std::list <Content> Contents;\n    typedef Utf8Stream::Point Utf8Point;\n    typedef std::pair <Utf8Point, Utf8Point> Range;\n\n    struct StyleImpl : BookTypesetter::Style\n    {\n        MyGUI::IFont*         mFont;\n        MyGUI::Colour         mHotColour;\n        MyGUI::Colour         mActiveColour;\n        MyGUI::Colour         mNormalColour;\n        InteractiveId mInteractiveId;\n\n        bool match (MyGUI::IFont* tstFont, const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour,\n                    const MyGUI::Colour& tstNormalColour, intptr_t tstInteractiveId)\n        {\n            return (mFont == tstFont) &&\n                   partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId);\n        }\n\n        bool match (char const * tstFont, const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour,\n                    const MyGUI::Colour& tstNormalColour, intptr_t tstInteractiveId)\n        {\n            return (mFont->getResourceName ()   == tstFont) &&\n                   partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId);\n        }\n\n        bool partal_match (const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour,\n                           const MyGUI::Colour& tstNormalColour, intptr_t tstInteractiveId)\n        {\n            return\n                (mHotColour                  == tstHotColour     ) &&\n                (mActiveColour               == tstActiveColour  ) &&\n                (mNormalColour               == tstNormalColour  ) &&\n                (mInteractiveId              == tstInteractiveId ) ;\n        }\n    };\n\n    typedef std::list <StyleImpl> Styles;\n\n    struct Run\n    {\n        StyleImpl*  mStyle;\n        Range       mRange;\n        int         mLeft, mRight;\n        int         mPrintableChars;\n    };\n\n    typedef std::vector <Run> Runs;\n\n    struct Line\n    {\n        Runs mRuns;\n        MyGUI::IntRect mRect;\n    };\n\n    typedef std::vector <Line> Lines;\n\n    struct Section\n    {\n        Lines mLines;\n        MyGUI::IntRect mRect;\n    };\n\n    typedef std::vector <Section> Sections;\n\n    // Holds \"top\" and \"bottom\" vertical coordinates in the source text.\n    // A page is basically a \"window\" into a portion of the source text, similar to a ScrollView.\n    typedef std::pair <int, int> Page;\n\n    typedef std::vector <Page> Pages;\n\n    Pages mPages;\n    Sections mSections;\n    Contents mContents;\n    Styles mStyles;\n    MyGUI::IntRect mRect;\n\n    virtual ~TypesetBookImpl () {}\n\n    Range addContent (BookTypesetter::Utf8Span text)\n    {\n        Contents::iterator i = mContents.insert (mContents.end (), Content (text.first, text.second));\n\n        if (i->empty())\n            return Range (Utf8Point (nullptr), Utf8Point (nullptr));\n\n        return Range (i->data(), i->data() + i->size());\n    }\n\n    size_t pageCount () const override { return mPages.size (); }\n\n    std::pair <unsigned int, unsigned int> getSize () const override\n    {\n        return std::make_pair (mRect.width (), mRect.height ());\n    }\n\n    template <typename Visitor>\n    void visitRuns (int top, int bottom, MyGUI::IFont* Font, Visitor const & visitor) const\n    {\n        for (Sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i)\n        {\n            if (top >= mRect.bottom || bottom <= i->mRect.top)\n                continue;\n\n            for (Lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j)\n            {\n                if (top >= j->mRect.bottom || bottom <= j->mRect.top)\n                    continue;\n\n                for (Runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k)\n                    if (!Font || k->mStyle->mFont == Font)\n                        visitor (*i, *j, *k);\n            }\n        }\n    }\n\n    template <typename Visitor>\n    void visitRuns (int top, int bottom, Visitor const & visitor) const\n    {\n        visitRuns (top, bottom, nullptr, visitor);\n    }\n\n    /// hit test with a margin for error. only hits on interactive text fragments are reported.\n    StyleImpl * hitTestWithMargin (int left, int top)\n    {\n        StyleImpl * hit = hitTest(left, top);\n        if (hit && hit->mInteractiveId != 0)\n            return hit;\n\n        const int maxMargin = 10;\n        for (int margin=1; margin < maxMargin; ++margin)\n        {\n            for (int i=0; i<4; ++i)\n            {\n                if (i==0)\n                    hit = hitTest(left, top-margin);\n                else if (i==1)\n                    hit = hitTest(left, top+margin);\n                else if (i==2)\n                    hit = hitTest(left-margin, top);\n                else\n                    hit = hitTest(left+margin, top);\n\n                if (hit && hit->mInteractiveId != 0)\n                    return hit;\n            }\n        }\n        return nullptr;\n    }\n\n    StyleImpl * hitTest (int left, int top) const\n    {\n        for (Sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i)\n        {\n            if (top < i->mRect.top || top >= i->mRect.bottom)\n                continue;\n\n            int left1 = left - i->mRect.left;\n\n            for (Lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j)\n            {\n                if (top < j->mRect.top || top >= j->mRect.bottom)\n                    continue;\n\n                int left2 = left1 - j->mRect.left;\n\n                for (Runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k)\n                {\n                    if (left2 < k->mLeft || left2 >= k->mRight)\n                        continue;\n\n                    return k->mStyle;\n                }\n            }\n        }\n\n        return nullptr;\n    }\n\n    MyGUI::IFont* affectedFont (StyleImpl* style)\n    {\n        for (Styles::iterator i = mStyles.begin (); i != mStyles.end (); ++i)\n            if (&*i == style)\n                return i->mFont;\n        return nullptr;\n    }\n\n    struct Typesetter;\n};\n\nstruct TypesetBookImpl::Typesetter : BookTypesetter\n{\n    struct PartialText {\n        StyleImpl *mStyle;\n        Utf8Stream::Point mBegin;\n        Utf8Stream::Point mEnd;\n        int mWidth;\n\n        PartialText( StyleImpl *style, Utf8Stream::Point begin, Utf8Stream::Point end, int width) :\n            mStyle(style), mBegin(begin), mEnd(end), mWidth(width)\n        {}\n    };\n\n    typedef TypesetBookImpl Book;\n    typedef std::shared_ptr <Book> BookPtr;\n    typedef std::vector<PartialText>::const_iterator PartialTextConstIterator;\n\n    int mPageWidth;\n    int mPageHeight;\n\n    BookPtr mBook;\n    Section * mSection;\n    Line * mLine;\n    Run * mRun;\n\n    std::vector <Alignment> mSectionAlignment;\n    std::vector <PartialText> mPartialWhitespace;\n    std::vector <PartialText> mPartialWord;\n\n    Book::Content const * mCurrentContent;\n    Alignment mCurrentAlignment;\n\n    Typesetter (size_t width, size_t height) :\n        mPageWidth (width), mPageHeight(height),\n        mSection (nullptr), mLine (nullptr), mRun (nullptr),\n        mCurrentContent (nullptr),\n        mCurrentAlignment (AlignLeft)\n    {\n        mBook = std::make_shared <Book> ();\n    }\n\n    virtual ~Typesetter ()\n    {\n    }\n\n    Style * createStyle (const std::string& fontName, const Colour& fontColour, bool useBookFont) override\n    {\n        std::string fullFontName;\n        if (fontName.empty())\n            fullFontName = MyGUI::FontManager::getInstance().getDefaultFont();\n        else\n            fullFontName = fontName;\n\n        if (useBookFont)\n            fullFontName = \"Journalbook \" + fullFontName;\n\n        for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i)\n            if (i->match (fullFontName.c_str(), fontColour, fontColour, fontColour, 0))\n                return &*i;\n\n        MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fullFontName);\n        if (!font)\n            throw std::runtime_error(std::string(\"can't find font \") + fullFontName);\n\n        StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ());\n        style.mFont = font;\n        style.mHotColour = fontColour;\n        style.mActiveColour = fontColour;\n        style.mNormalColour = fontColour;\n        style.mInteractiveId = 0;\n\n        return &style;\n    }\n\n    Style* createHotStyle (Style* baseStyle, const Colour& normalColour, const Colour& hoverColour,\n                           const Colour& activeColour, InteractiveId id, bool unique) override\n    {\n        StyleImpl* BaseStyle = static_cast <StyleImpl*> (baseStyle);\n\n        if (!unique)\n            for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i)\n                if (i->match (BaseStyle->mFont, hoverColour, activeColour, normalColour, id))\n                    return &*i;\n\n        StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ());\n\n        style.mFont = BaseStyle->mFont;\n        style.mHotColour = hoverColour;\n        style.mActiveColour = activeColour;\n        style.mNormalColour = normalColour;\n        style.mInteractiveId = id;\n\n        return &style;\n    }\n\n    void write (Style * style, Utf8Span text) override\n    {\n        Range range = mBook->addContent (text);\n\n        writeImpl (static_cast <StyleImpl*> (style), range.first, range.second);\n    }\n\n    intptr_t addContent (Utf8Span text, bool select) override\n    {\n        add_partial_text();\n\n        Contents::iterator i = mBook->mContents.insert (mBook->mContents.end (), Content (text.first, text.second));\n\n        if (select)\n            mCurrentContent = &(*i);\n\n        return reinterpret_cast <intptr_t> (&(*i));\n    }\n\n    void selectContent (intptr_t contentHandle) override\n    {\n        add_partial_text();\n\n        mCurrentContent = reinterpret_cast <Content const *> (contentHandle);\n    }\n\n    void write (Style * style, size_t begin, size_t end) override\n    {\n        assert (mCurrentContent != nullptr);\n        assert (end <= mCurrentContent->size ());\n        assert (begin <= mCurrentContent->size ());\n\n        Utf8Point begin_ = mCurrentContent->data() + begin;\n        Utf8Point end_   = mCurrentContent->data() + end;\n\n        writeImpl (static_cast <StyleImpl*> (style), begin_, end_);\n    }\n\n    void lineBreak (float margin) override\n    {\n        assert (margin == 0); //TODO: figure out proper behavior here...\n\n        add_partial_text();\n\n        mRun = nullptr;\n        mLine = nullptr;\n    }\n\n    void sectionBreak (int margin) override\n    {\n        add_partial_text();\n\n        if (mBook->mSections.size () > 0)\n        {\n            mRun = nullptr;\n            mLine = nullptr;\n            mSection = nullptr;\n\n            if (mBook->mRect.bottom < (mBook->mSections.back ().mRect.bottom + margin))\n                mBook->mRect.bottom = (mBook->mSections.back ().mRect.bottom + margin);\n        }\n    }\n\n    void setSectionAlignment (Alignment sectionAlignment) override\n    {\n        add_partial_text();\n\n        if (mSection != nullptr)\n            mSectionAlignment.back () = sectionAlignment;\n        mCurrentAlignment = sectionAlignment;\n    }\n\n    TypesetBook::Ptr complete () override\n    {\n        int curPageStart = 0;\n        int curPageStop  = 0;\n\n        add_partial_text();\n\n        std::vector <Alignment>::iterator sa = mSectionAlignment.begin ();\n        for (Sections::iterator i = mBook->mSections.begin (); i != mBook->mSections.end (); ++i, ++sa)\n        {\n            // apply alignment to individual lines...\n            for (Lines::iterator j = i->mLines.begin (); j != i->mLines.end (); ++j)\n            {\n                int width = j->mRect.width ();\n                int excess = mPageWidth - width;\n\n                switch (*sa)\n                {\n                default:\n                case AlignLeft:   j->mRect.left = 0;        break;\n                case AlignCenter: j->mRect.left = excess/2; break;\n                case AlignRight:  j->mRect.left = excess;   break;\n                }\n\n                j->mRect.right = j->mRect.left + width;\n            }\n\n            if (curPageStop == curPageStart)\n            {\n                curPageStart = i->mRect.top;\n                curPageStop  = i->mRect.top;\n            }\n\n            int spaceLeft = mPageHeight - (curPageStop - curPageStart);\n            int sectionHeight = i->mRect.height ();\n\n            // This is NOT equal to i->mRect.height(), which doesn't account for section breaks.\n            int spaceRequired = (i->mRect.bottom - curPageStop);\n            if (curPageStart == curPageStop) // If this is a new page, the section break is not needed\n                spaceRequired = i->mRect.height();\n\n            if (spaceRequired <= mPageHeight)\n            {\n                if (spaceRequired > spaceLeft)\n                {\n                    // The section won't completely fit on the current page. Finish the current page and start a new one.\n                    assert (curPageStart != curPageStop);\n\n                    mBook->mPages.push_back (Page (curPageStart, curPageStop));\n\n                    curPageStart = i->mRect.top;\n                    curPageStop = i->mRect.bottom;\n                }\n                else\n                    curPageStop = i->mRect.bottom;\n            }\n            else\n            {\n                // The section won't completely fit on the current page. Finish the current page and start a new one.\n                mBook->mPages.push_back (Page (curPageStart, curPageStop));\n\n                curPageStart = i->mRect.top;\n                curPageStop = i->mRect.bottom;\n\n                //split section\n                int sectionHeightLeft = sectionHeight;\n                while (sectionHeightLeft >= mPageHeight)\n                {\n                    // Adjust to the top of the first line that does not fit on the current page anymore\n                    int splitPos = curPageStop;\n                    for (Lines::iterator j = i->mLines.begin (); j != i->mLines.end (); ++j)\n                    {\n                        if (j->mRect.bottom > curPageStart + mPageHeight)\n                        {\n                            splitPos = j->mRect.top;\n                            break;\n                        }\n                    }\n\n                    mBook->mPages.push_back (Page (curPageStart, splitPos));\n                    curPageStart = splitPos;\n                    curPageStop = splitPos;\n\n                    sectionHeightLeft = (i->mRect.bottom - splitPos);\n                }\n                curPageStop = i->mRect.bottom;\n            }\n        }\n\n        if (curPageStart != curPageStop)\n            mBook->mPages.push_back (Page (curPageStart, curPageStop));\n\n        return mBook;\n    }\n\n    void writeImpl (StyleImpl * style, Utf8Stream::Point _begin, Utf8Stream::Point _end)\n    {\n        Utf8Stream stream (_begin, _end);\n\n        while (!stream.eof ())\n        {\n            if (ucsLineBreak (stream.peek ()))\n            {\n                add_partial_text();\n                stream.consume ();\n                mLine = nullptr, mRun = nullptr;\n                continue;\n            }\n\n            if (ucsBreakingSpace (stream.peek ()) && !mPartialWord.empty())\n                add_partial_text();\n\n            int word_width = 0;\n            int space_width = 0;\n\n            Utf8Stream::Point lead = stream.current ();\n\n            while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ()))\n            {\n                MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek());\n                if (info.charFound)\n                    space_width += static_cast<int>(info.advance + info.bearingX);\n                stream.consume ();\n            }\n\n            Utf8Stream::Point origin = stream.current ();\n\n            while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ()))\n            {\n                MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek());\n                if (info.charFound)\n                    word_width += static_cast<int>(info.advance + info.bearingX);\n                stream.consume ();\n            }\n\n            Utf8Stream::Point extent = stream.current ();\n\n            if (lead == extent)\n                break;\n\n            if ( lead != origin )\n                mPartialWhitespace.emplace_back(style, lead, origin, space_width);\n            if ( origin != extent )\n                mPartialWord.emplace_back(style, origin, extent, word_width);\n        }\n    }\n\n    void add_partial_text ()\n    {\n        if (mPartialWhitespace.empty() && mPartialWord.empty())\n            return;\n\n        int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight();\n        int space_width = 0;\n        int word_width  = 0;\n\n        for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i)\n            space_width += i->mWidth;\n        for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i)\n            word_width += i->mWidth;\n\n        int left = mLine ? mLine->mRect.right : 0;\n\n        if (left + space_width + word_width > mPageWidth)\n        {\n            mLine = nullptr, mRun = nullptr, left = 0;\n        }\n        else\n        {\n            for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i)\n            {\n                int top = mLine ? mLine->mRect.top : mBook->mRect.bottom;\n\n                append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + fontHeight);\n\n                left = mLine->mRect.right;\n            }\n        }\n\n        for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i)\n        {\n            int top = mLine ? mLine->mRect.top : mBook->mRect.bottom;\n\n            append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + fontHeight);\n\n            left = mLine->mRect.right;\n        }\n\n        mPartialWhitespace.clear();\n        mPartialWord.clear();\n    }\n\n    void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom)\n    {\n        if (mSection == nullptr)\n        {\n            mBook->mSections.push_back (Section ());\n            mSection = &mBook->mSections.back ();\n            mSection->mRect = MyGUI::IntRect (0, mBook->mRect.bottom, 0, mBook->mRect.bottom);\n            mSectionAlignment.push_back (mCurrentAlignment);\n        }\n\n        if (mLine == nullptr)\n        {\n            mSection->mLines.push_back (Line ());\n            mLine = &mSection->mLines.back ();\n            mLine->mRect = MyGUI::IntRect (0, mSection->mRect.bottom, 0, mBook->mRect.bottom);\n        }\n\n        if (mBook->mRect.right < right)\n            mBook->mRect.right = right;\n\n        if (mBook->mRect.bottom < bottom)\n            mBook->mRect.bottom = bottom;\n\n        if (mSection->mRect.right < right)\n            mSection->mRect.right = right;\n\n        if (mSection->mRect.bottom < bottom)\n            mSection->mRect.bottom = bottom;\n\n        if (mLine->mRect.right < right)\n            mLine->mRect.right = right;\n\n        if (mLine->mRect.bottom < bottom)\n            mLine->mRect.bottom = bottom;\n\n        if (mRun == nullptr || mRun->mStyle != style || mRun->mRange.second != begin)\n        {\n            int left = mRun ? mRun->mRight : mLine->mRect.left;\n\n            mLine->mRuns.push_back (Run ());\n            mRun = &mLine->mRuns.back ();\n            mRun->mStyle = style;\n            mRun->mLeft = left;\n            mRun->mRight = right;\n            mRun->mRange.first = begin;\n            mRun->mRange.second = end;\n            mRun->mPrintableChars = pc;\n          //Run->Locale = Locale;\n        }\n        else\n        {\n            mRun->mRight = right;\n            mRun->mRange.second = end;\n            mRun->mPrintableChars += pc;\n        }\n    }\n};\n\nBookTypesetter::Ptr BookTypesetter::create (int pageWidth, int pageHeight)\n{\n    return std::make_shared <TypesetBookImpl::Typesetter> (pageWidth, pageHeight);\n}\n\nnamespace\n{\n    struct RenderXform\n    {\n    public:\n\n        float clipTop;\n        float clipLeft;\n        float clipRight;\n        float clipBottom;\n\n        float absoluteLeft;\n        float absoluteTop;\n        float leftOffset;\n        float topOffset;\n\n        float pixScaleX;\n        float pixScaleY;\n        float hOffset;\n        float vOffset;\n\n        RenderXform (MyGUI::ICroppedRectangle* croppedParent, MyGUI::RenderTargetInfo const & renderTargetInfo)\n        {\n            clipTop    = static_cast<float>(croppedParent->_getMarginTop());\n            clipLeft   = static_cast<float>(croppedParent->_getMarginLeft ());\n            clipRight  = static_cast<float>(croppedParent->getWidth () - croppedParent->_getMarginRight ());\n            clipBottom = static_cast<float>(croppedParent->getHeight() - croppedParent->_getMarginBottom());\n\n            absoluteLeft = static_cast<float>(croppedParent->getAbsoluteLeft());\n            absoluteTop  = static_cast<float>(croppedParent->getAbsoluteTop());\n            leftOffset   = static_cast<float>(renderTargetInfo.leftOffset);\n            topOffset    = static_cast<float>(renderTargetInfo.topOffset);\n\n            pixScaleX   = renderTargetInfo.pixScaleX;\n            pixScaleY   = renderTargetInfo.pixScaleY;\n            hOffset     = renderTargetInfo.hOffset;\n            vOffset     = renderTargetInfo.vOffset;\n        }\n\n        bool clip (MyGUI::FloatRect & vr, MyGUI::FloatRect & tr)\n        {\n            if (vr.bottom <= clipTop    || vr.right  <= clipLeft   ||\n                vr.left   >= clipRight  || vr.top    >= clipBottom )\n                return false;\n\n            if (vr.top < clipTop)\n            {\n                tr.top += tr.height () * (clipTop - vr.top) / vr.height ();\n                vr.top = clipTop;\n            }\n\n            if (vr.left < clipLeft)\n            {\n                tr.left += tr.width () * (clipLeft - vr.left) / vr.width ();\n                vr.left = clipLeft;\n            }\n\n            if (vr.right > clipRight)\n            {\n                tr.right -= tr.width () * (vr.right - clipRight) / vr.width ();\n                vr.right = clipRight;\n            }\n\n            if (vr.bottom > clipBottom)\n            {\n                tr.bottom -= tr.height () * (vr.bottom - clipBottom) / vr.height ();\n                vr.bottom = clipBottom;\n            }\n\n            return true;\n        }\n\n        MyGUI::FloatPoint operator () (MyGUI::FloatPoint pt)\n        {\n            pt.left = absoluteLeft - leftOffset + pt.left;\n            pt.top  = absoluteTop  - topOffset  + pt.top;\n\n            pt.left = +(((pixScaleX * pt.left + hOffset) * 2.0f) - 1.0f);\n            pt.top  = -(((pixScaleY * pt.top  + vOffset) * 2.0f) - 1.0f);\n\n            return pt;\n        }\n    };\n\n    struct GlyphStream\n    {\n        float mZ;\n        uint32_t mC;\n        MyGUI::IFont* mFont;\n        MyGUI::FloatPoint mOrigin;\n        MyGUI::FloatPoint mCursor;\n        MyGUI::Vertex* mVertices;\n        RenderXform mRenderXform;\n        MyGUI::VertexColourType mVertexColourType;\n\n        GlyphStream (MyGUI::IFont* font, float left, float top, float Z,\n                      MyGUI::Vertex* vertices, RenderXform const & renderXform) :\n            mZ(Z),\n            mC(0), mFont (font), mOrigin (left, top),\n            mVertices (vertices),\n            mRenderXform (renderXform)\n        {\n            assert(font != nullptr);\n            mVertexColourType = MyGUI::RenderManager::getInstance().getVertexFormat();\n        }\n\n        ~GlyphStream ()\n        {\n        }\n\n        MyGUI::Vertex* end () const { return mVertices; }\n\n        void reset (float left, float top, MyGUI::Colour colour)\n        {\n            mC = MyGUI::texture_utility::toColourARGB(colour) | 0xFF000000;\n            MyGUI::texture_utility::convertColour(mC, mVertexColourType);\n\n            mCursor.left = mOrigin.left + left;\n            mCursor.top = mOrigin.top + top;\n        }\n\n        void emitGlyph (wchar_t ch)\n        {\n            MWGui::GlyphInfo info = GlyphInfo(mFont, ch);\n\n            if (!info.charFound)\n                return;\n\n            MyGUI::FloatRect vr;\n\n            vr.left = mCursor.left + info.bearingX;\n            vr.top = mCursor.top + info.bearingY;\n            vr.right = vr.left + info.width;\n            vr.bottom = vr.top + info.height;\n\n            MyGUI::FloatRect tr = info.uvRect;\n\n            if (mRenderXform.clip (vr, tr))\n                quad (vr, tr);\n\n            mCursor.left += static_cast<int>(info.bearingX + info.advance);\n        }\n\n        void emitSpace (wchar_t ch)\n        {\n            MWGui::GlyphInfo info = GlyphInfo(mFont, ch);\n\n            if (info.charFound)\n                mCursor.left += static_cast<int>(info.bearingX + info.advance);\n        }\n\n    private:\n\n        void quad (const MyGUI::FloatRect& vr, const MyGUI::FloatRect& tr)\n        {\n            vertex (vr.left, vr.top, tr.left, tr.top);\n            vertex (vr.right, vr.top, tr.right, tr.top);\n            vertex (vr.left, vr.bottom, tr.left, tr.bottom);\n            vertex (vr.right, vr.top, tr.right, tr.top);\n            vertex (vr.left, vr.bottom, tr.left, tr.bottom);\n            vertex (vr.right, vr.bottom, tr.right, tr.bottom);\n        }\n\n        void vertex (float x, float y, float u, float v)\n        {\n            MyGUI::FloatPoint pt = mRenderXform (MyGUI::FloatPoint (x, y));\n\n            mVertices->x = pt.left;\n            mVertices->y = pt.top ;\n            mVertices->z = mZ;\n            mVertices->u = u;\n            mVertices->v = v;\n            mVertices->colour = mC;\n\n            ++mVertices;\n        }\n    };\n}\n\nclass PageDisplay final : public MyGUI::ISubWidgetText\n{\n    MYGUI_RTTI_DERIVED(PageDisplay)\nprotected:\n\n    typedef TypesetBookImpl::Section Section;\n    typedef TypesetBookImpl::Line    Line;\n    typedef TypesetBookImpl::Run     Run;\n    bool mIsPageReset;\n    size_t mPage;\n\n    struct TextFormat : ISubWidget\n    {\n        typedef MyGUI::IFont* Id;\n\n        Id mFont;\n        int mCountVertex;\n        MyGUI::ITexture* mTexture;\n        MyGUI::RenderItem* mRenderItem;\n        PageDisplay * mDisplay;\n\n        TextFormat (MyGUI::IFont* id, PageDisplay * display) :\n            mFont (id),\n            mCountVertex (0),\n            mTexture (nullptr),\n            mRenderItem (nullptr),\n            mDisplay (display)\n        {\n        }\n\n        void createDrawItem (MyGUI::ILayerNode* node)\n        {\n            assert (mRenderItem == nullptr);\n\n            if (mTexture != nullptr)\n            {\n                mRenderItem = node->addToRenderItem(mTexture, false, false);\n                mRenderItem->addDrawItem(this, mCountVertex);\n            }\n        }\n\n        void destroyDrawItem (MyGUI::ILayerNode* node)\n        {\n            assert (mTexture != nullptr ? mRenderItem != nullptr : mRenderItem == nullptr);\n\n            if (mTexture != nullptr)\n            {\n                mRenderItem->removeDrawItem (this);\n                mRenderItem = nullptr;\n            }\n        }\n\n        void doRender() override { mDisplay->doRender (*this); }\n\n        // this isn't really a sub-widget, its just a \"drawitem\" which\n        // should have its own interface\n        void createDrawItem(MyGUI::ITexture* _texture, MyGUI::ILayerNode* _node) override {}\n        void destroyDrawItem() override {}\n    };\n\n    void resetPage()\n    {\n       mIsPageReset = true;\n       mPage = 0;\n    }\n\n    void setPage(size_t page)\n    {\n       mIsPageReset = false;\n       mPage = page;\n    }\n\n    bool isPageDifferent(size_t page)\n    {\n       return mIsPageReset || (mPage != page);\n    }\n\n    std::optional<MyGUI::IntPoint> getAdjustedPos(int left, int top, bool move = false)\n    {\n        if (!mBook)\n            return {};\n\n        if (mPage >= mBook->mPages.size())\n            return {};\n\n        MyGUI::IntPoint pos (left, top);\n#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)\n        // work around inconsistency in MyGUI where the mouse press coordinates aren't\n        // transformed by the current Layer (even though mouse *move* events are).\n        if(!move)\n            pos = mNode->getLayer()->getPosition(left, top);\n#endif\n        pos.left -= mCroppedParent->getAbsoluteLeft ();\n        pos.top  -= mCroppedParent->getAbsoluteTop  ();\n        pos.top += mViewTop;\n        return pos;\n    }\n\npublic:\n\n    typedef TypesetBookImpl::StyleImpl Style;\n    typedef std::map <TextFormat::Id, std::unique_ptr<TextFormat>> ActiveTextFormats;\n\n    int mViewTop;\n    int mViewBottom;\n\n    Style* mFocusItem;\n    bool mItemActive;\n    MyGUI::MouseButton mLastDown;\n    std::function <void (intptr_t)> mLinkClicked;\n\n\n    std::shared_ptr <TypesetBookImpl> mBook;\n\n    MyGUI::ILayerNode* mNode;\n    ActiveTextFormats mActiveTextFormats;\n\n    PageDisplay ()\n    {\n        resetPage ();\n        mViewTop = 0;\n        mViewBottom = 0;\n        mFocusItem = nullptr;\n        mItemActive = false;\n        mNode = nullptr;\n    }\n\n    void dirtyFocusItem ()\n    {\n        if (mFocusItem != nullptr)\n        {\n            MyGUI::IFont* Font = mBook->affectedFont (mFocusItem);\n\n            ActiveTextFormats::iterator i = mActiveTextFormats.find (Font);\n\n            if (mNode)\n                mNode->outOfDate (i->second->mRenderItem);\n        }\n    }\n\n    void onMouseLostFocus ()\n    {\n        if (!mBook)\n            return;\n\n        if (mPage >= mBook->mPages.size())\n            return;\n\n        dirtyFocusItem ();\n\n        mFocusItem = nullptr;\n        mItemActive = false;\n    }\n\n    void onMouseMove (int left, int top)\n    {\n        Style * hit = nullptr;\n        if(auto pos = getAdjustedPos(left, top, true))\n            if(pos->top <= mViewBottom)\n                hit = mBook->hitTestWithMargin (pos->left, pos->top);\n\n        if (mLastDown == MyGUI::MouseButton::None)\n        {\n            if (hit != mFocusItem)\n            {\n                dirtyFocusItem ();\n\n                mFocusItem = hit;\n                mItemActive = false;\n\n                dirtyFocusItem ();\n            }\n        }\n        else\n        if (mFocusItem != nullptr)\n        {\n            bool newItemActive = hit == mFocusItem;\n\n            if (newItemActive != mItemActive)\n            {\n                mItemActive = newItemActive;\n\n                dirtyFocusItem ();\n            }\n        }\n    }\n\n    void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id)\n    {\n        auto pos = getAdjustedPos(left, top);\n\n        if (pos && mLastDown == MyGUI::MouseButton::None)\n        {\n            mFocusItem = pos->top <= mViewBottom ? mBook->hitTestWithMargin (pos->left, pos->top) : nullptr;\n            mItemActive = true;\n\n            dirtyFocusItem ();\n\n            mLastDown = id;\n        }\n    }\n\n    void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id)\n    {\n        auto pos = getAdjustedPos(left, top);\n\n        if (pos && mLastDown == id)\n        {\n            Style * item = pos->top <= mViewBottom ? mBook->hitTestWithMargin (pos->left, pos->top) : nullptr;\n\n            bool clicked = mFocusItem == item;\n\n            mItemActive = false;\n\n            dirtyFocusItem ();\n\n            mLastDown = MyGUI::MouseButton::None;\n\n            if (clicked && mLinkClicked && item && item->mInteractiveId != 0)\n                mLinkClicked (item->mInteractiveId);\n        }\n    }\n\n    void showPage (TypesetBook::Ptr book, size_t newPage)\n    {\n        std::shared_ptr <TypesetBookImpl> newBook = std::dynamic_pointer_cast <TypesetBookImpl> (book);\n\n        if (mBook != newBook)\n        {\n            mFocusItem = nullptr;\n            mItemActive = 0;\n\n            for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)\n            {\n                if (mNode != nullptr)\n                    i->second->destroyDrawItem (mNode);\n                i->second.reset();\n            }\n\n            mActiveTextFormats.clear ();\n\n            if (newBook != nullptr)\n            {\n                createActiveFormats (newBook);\n\n                mBook = newBook;\n                setPage (newPage);\n\n                if (newPage < mBook->mPages.size ())\n                {\n                    mViewTop = mBook->mPages [newPage].first;\n                    mViewBottom = mBook->mPages [newPage].second;\n                }\n                else\n                {\n                    mViewTop = 0;\n                    mViewBottom = 0;\n                }\n            }\n            else\n            {\n                mBook.reset ();\n                resetPage ();\n                mViewTop = 0;\n                mViewBottom = 0;\n            }\n        }\n        else\n        if (mBook && isPageDifferent (newPage))\n        {\n            if (mNode != nullptr)\n                for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)\n                    mNode->outOfDate(i->second->mRenderItem);\n\n            setPage (newPage);\n\n            if (newPage < mBook->mPages.size ())\n            {\n                mViewTop = mBook->mPages [newPage].first;\n                mViewBottom = mBook->mPages [newPage].second;\n            }\n            else\n            {\n                mViewTop = 0;\n                mViewBottom = 0;\n            }\n        }\n    }\n\n    struct CreateActiveFormat\n    {\n        PageDisplay * this_;\n\n        CreateActiveFormat (PageDisplay * this_) : this_ (this_) {}\n\n        void operator () (Section const & section, Line const & line, Run const & run) const\n        {\n            MyGUI::IFont* Font = run.mStyle->mFont;\n\n            ActiveTextFormats::iterator j = this_->mActiveTextFormats.find (Font);\n\n            if (j == this_->mActiveTextFormats.end ())\n            {\n                std::unique_ptr<TextFormat> textFormat(new TextFormat (Font, this_));\n\n                textFormat->mTexture = Font->getTextureFont ();\n\n                j = this_->mActiveTextFormats.insert (std::make_pair (Font, std::move(textFormat))).first;\n            }\n\n            j->second->mCountVertex += run.mPrintableChars * 6;\n        }\n    };\n\n    void createActiveFormats (std::shared_ptr <TypesetBookImpl> newBook)\n    {\n        newBook->visitRuns (0, 0x7FFFFFFF, CreateActiveFormat (this));\n\n        if (mNode != nullptr)\n            for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)\n                i->second->createDrawItem (mNode);\n    }\n\n    void setVisible (bool newVisible) override\n    {\n        if (mVisible == newVisible)\n            return;\n\n        mVisible = newVisible;\n\n        if (mVisible)\n        {\n            // reset input state\n            mLastDown = MyGUI::MouseButton::None;\n            mFocusItem = nullptr;\n            mItemActive = 0;\n        }\n\n        if (nullptr != mNode)\n        {\n            for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)\n                mNode->outOfDate(i->second->mRenderItem);\n        }\n    }\n\n    void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) override\n    {\n        mNode = node;\n\n        for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)\n            i->second->createDrawItem (node);\n    }\n\n    struct RenderRun\n    {\n        PageDisplay * this_;\n        GlyphStream &glyphStream;\n\n        RenderRun (PageDisplay * this_, GlyphStream &glyphStream) :\n            this_(this_), glyphStream (glyphStream)\n        {\n        }\n\n        void operator () (Section const & section, Line const & line, Run const & run) const\n        {\n            bool isActive = run.mStyle->mInteractiveId && (run.mStyle == this_->mFocusItem);\n\n            MyGUI::Colour colour = isActive ? (this_->mItemActive ? run.mStyle->mActiveColour: run.mStyle->mHotColour) : run.mStyle->mNormalColour;\n\n            glyphStream.reset(static_cast<float>(section.mRect.left + line.mRect.left + run.mLeft), static_cast<float>(line.mRect.top), colour);\n\n            Utf8Stream stream (run.mRange);\n\n            while (!stream.eof ())\n            {\n                Utf8Stream::UnicodeChar code_point = stream.consume ();\n\n                if (ucsCarriageReturn (code_point))\n                    continue;\n\n                if (!ucsSpace (code_point))\n                    glyphStream.emitGlyph (code_point);\n                else\n                    glyphStream.emitSpace (code_point);\n            }\n        }\n    };\n\n    /*\n        queue up rendering operations for this text format\n    */\n    void doRender(TextFormat & textFormat)\n    {\n        if (!mVisible)\n            return;\n\n        MyGUI::Vertex* vertices = textFormat.mRenderItem->getCurrentVertexBuffer();\n\n        RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo());\n\n        GlyphStream glyphStream(textFormat.mFont, static_cast<float>(mCoord.left), static_cast<float>(mCoord.top - mViewTop),\n                                  -1 /*mNode->getNodeDepth()*/, vertices, renderXform);\n\n        int visit_top    = (std::max) (mViewTop,    mViewTop + int (renderXform.clipTop   ));\n        int visit_bottom = (std::min) (mViewBottom, mViewTop + int (renderXform.clipBottom));\n\n        mBook->visitRuns (visit_top, visit_bottom, textFormat.mFont, RenderRun (this, glyphStream));\n\n        textFormat.mRenderItem->setLastVertexCount(glyphStream.end () - vertices);\n    }\n\n    // ISubWidget should not necessarily be a drawitem\n    // in this case, it is not...\n    void doRender() override { }\n\n    void _updateView () override\n    {\n        _checkMargin();\n\n        if (mNode != nullptr)\n            for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)\n                mNode->outOfDate (i->second->mRenderItem);\n    }\n\n    void _correctView() override\n    {\n        _checkMargin ();\n\n        if (mNode != nullptr)\n            for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)\n                mNode->outOfDate (i->second->mRenderItem);\n\n    }\n\n    void destroyDrawItem() override\n    {\n        for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)\n            i->second->destroyDrawItem (mNode);\n\n        mNode = nullptr;\n    }\n};\n\n\nclass BookPageImpl final : public BookPage\n{\nMYGUI_RTTI_DERIVED(BookPage)\npublic:\n\n    BookPageImpl()\n        : mPageDisplay(nullptr)\n    {\n    }\n\n    void showPage (TypesetBook::Ptr book, size_t page) override\n    {\n        mPageDisplay->showPage (book, page);\n    }\n\n    void adviseLinkClicked (std::function <void (InteractiveId)> linkClicked) override\n    {\n        mPageDisplay->mLinkClicked = linkClicked;\n    }\n\n    void unadviseLinkClicked () override\n    {\n        mPageDisplay->mLinkClicked = std::function <void (InteractiveId)> ();\n    }\n\nprotected:\n\n    void initialiseOverride() override\n    {\n        Base::initialiseOverride();\n\n        if (getSubWidgetText())\n        {\n            mPageDisplay = getSubWidgetText()->castType<PageDisplay>();\n        }\n        else\n        {\n            throw std::runtime_error(\"BookPage unable to find page display sub widget\");\n        }\n    }\n\n    void onMouseLostFocus(Widget* _new) override\n    {\n        // NOTE: MyGUI also fires eventMouseLostFocus for widgets that are about to be destroyed (if they had focus).\n        // Child widgets may already be destroyed! So be careful.\n        mPageDisplay->onMouseLostFocus ();\n    }\n\n    void onMouseMove(int left, int top) override\n    {\n        mPageDisplay->onMouseMove (left, top);\n    }\n\n    void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) override\n    {\n        mPageDisplay->onMouseButtonPressed (left, top, id);\n    }\n\n    void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) override\n    {\n        mPageDisplay->onMouseButtonReleased (left, top, id);\n    }\n\n    PageDisplay* mPageDisplay;\n};\n\nvoid BookPage::registerMyGUIComponents ()\n{\n    MyGUI::FactoryManager & factory = MyGUI::FactoryManager::getInstance();\n\n    factory.registerFactory<BookPageImpl>(\"Widget\");\n    factory.registerFactory<PageDisplay>(\"BasisSkin\");\n}\n\nstatic bool ucsLineBreak (int codePoint)\n{\n    return codePoint == '\\n';\n}\n\nstatic bool ucsCarriageReturn (int codePoint)\n{\n    return codePoint == '\\r';\n}\n\nstatic bool ucsSpace (int codePoint)\n{\n    switch (codePoint)\n    {\n    case 0x0020: // SPACE\n    case 0x00A0: // NO-BREAK SPACE\n    case 0x1680: // OGHAM SPACE MARK\n    case 0x180E: // MONGOLIAN VOWEL SEPARATOR\n    case 0x2000: // EN QUAD\n    case 0x2001: // EM QUAD\n    case 0x2002: // EN SPACE\n    case 0x2003: // EM SPACE\n    case 0x2004: // THREE-PER-EM SPACE\n    case 0x2005: // FOUR-PER-EM SPACE\n    case 0x2006: // SIX-PER-EM SPACE\n    case 0x2007: // FIGURE SPACE\n    case 0x2008: // PUNCTUATION SPACE\n    case 0x2009: // THIN SPACE\n    case 0x200A: // HAIR SPACE\n    case 0x200B: // ZERO WIDTH SPACE\n    case 0x202F: // NARROW NO-BREAK SPACE\n    case 0x205F: // MEDIUM MATHEMATICAL SPACE\n    case 0x3000: // IDEOGRAPHIC SPACE\n    case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE\n        return true;\n    default:\n        return false;\n    }\n}\n\nstatic bool ucsBreakingSpace (int codePoint)\n{\n    switch (codePoint)\n    {\n    case 0x0020: // SPACE\n  //case 0x00A0: // NO-BREAK SPACE\n    case 0x1680: // OGHAM SPACE MARK\n    case 0x180E: // MONGOLIAN VOWEL SEPARATOR\n    case 0x2000: // EN QUAD\n    case 0x2001: // EM QUAD\n    case 0x2002: // EN SPACE\n    case 0x2003: // EM SPACE\n    case 0x2004: // THREE-PER-EM SPACE\n    case 0x2005: // FOUR-PER-EM SPACE\n    case 0x2006: // SIX-PER-EM SPACE\n    case 0x2007: // FIGURE SPACE\n    case 0x2008: // PUNCTUATION SPACE\n    case 0x2009: // THIN SPACE\n    case 0x200A: // HAIR SPACE\n    case 0x200B: // ZERO WIDTH SPACE\n    case 0x202F: // NARROW NO-BREAK SPACE\n    case 0x205F: // MEDIUM MATHEMATICAL SPACE\n    case 0x3000: // IDEOGRAPHIC SPACE\n  //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE\n        return true;\n    default:\n        return false;\n    }\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/bookpage.hpp",
    "content": "#ifndef MWGUI_BOOKPAGE_HPP\n#define MWGUI_BOOKPAGE_HPP\n\n#include \"MyGUI_Colour.h\"\n#include \"MyGUI_Widget.h\"\n#include \"MyGUI_FontManager.h\"\n\n#include <functional>\n#include <memory>\n#include <stdint.h>\n\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\nnamespace MWGui\n{\n    /// A formatted and paginated document to be used with\n    /// the book page widget.\n    struct TypesetBook\n    {\n        typedef std::shared_ptr <TypesetBook> Ptr;\n        typedef intptr_t InteractiveId;\n\n        /// Returns the number of pages in the document.\n        virtual size_t pageCount () const = 0;\n\n        /// Return the area covered by the document. The first\n        /// integer is the maximum with of any line. This is not\n        /// the largest coordinate of the right edge of any line,\n        /// it is the largest distance from the left edge to the\n        /// right edge. The second integer is the height of all\n        /// text combined prior to pagination.\n        virtual std::pair <unsigned int, unsigned int> getSize () const = 0;\n\n        virtual ~TypesetBook() = default;\n    };\n\n    struct GlyphInfo\n    {\n        char codePoint;\n        float width;\n        float height;\n        float advance;\n        float bearingX;\n        float bearingY;\n        bool charFound;\n        MyGUI::FloatRect uvRect;\n\n        GlyphInfo(MyGUI::IFont* font, MyGUI::Char ch)\n        {\n            static const int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight();\n\n            const MyGUI::GlyphInfo* gi = font->getGlyphInfo(ch);\n            if (gi)\n            {\n                const float scale = font->getDefaultHeight() / (float) fontHeight;\n\n                codePoint = gi->codePoint;\n                bearingX = (int) gi->bearingX / scale;\n                bearingY = (int) gi->bearingY / scale;\n                width = (int) gi->width / scale;\n                height = (int) gi->height / scale;\n                advance = (int) gi->advance / scale;\n                uvRect = gi->uvRect;\n                charFound = true;\n            }\n            else\n            {\n                codePoint = 0;\n                bearingX = 0;\n                bearingY = 0;\n                width = 0;\n                height = 0;\n                advance = 0;\n                charFound = false;\n            }\n        }\n    };\n\n    /// A factory class for creating a typeset book instance.\n    struct BookTypesetter\n    {\n        typedef std::shared_ptr <BookTypesetter> Ptr;\n        typedef TypesetBook::InteractiveId InteractiveId;\n        typedef MyGUI::Colour Colour;\n        typedef uint8_t const * Utf8Point;\n        typedef std::pair <Utf8Point, Utf8Point> Utf8Span;\n\n        virtual ~BookTypesetter() = default;\n\n        enum Alignment {\n            AlignLeft   = -1,\n            AlignCenter = 0,\n            AlignRight  = +1\n        };\n\n        /// Styles are used to control the character level formatting\n        /// of text added to a typeset book. Their lifetime is equal\n        /// to the lifetime of the book-typesetter instance that created\n        /// them.\n        struct Style;\n\n        /// A factory function for creating the default implementation of a book typesetter\n        static Ptr create (int pageWidth, int pageHeight);\n\n        /// Create a simple text style consisting of a font and a text color.\n        virtual Style* createStyle (const std::string& fontName, const Colour& colour, bool useBookFont=true) = 0;\n\n        /// Create a hyper-link style with a user-defined identifier based on an\n        /// existing style. The unique flag forces a new instance of this style\n        /// to be created even if an existing instance is present.\n        virtual Style* createHotStyle (Style * BaseStyle, const Colour& NormalColour, const Colour& HoverColour,\n                                       const Colour& ActiveColour, InteractiveId Id, bool Unique = true) = 0;\n\n        /// Insert a line break into the document. Newline characters in the input\n        /// text have the same affect. The margin parameter adds additional space\n        /// before the next line of text.\n        virtual void lineBreak (float margin = 0) = 0;\n\n        /// Insert a section  break into the document. This causes a new section\n        /// to begin when additional text is inserted. Pagination attempts to keep\n        /// sections together on a single page. The margin parameter adds additional space\n        /// before the next line of text.\n        virtual void sectionBreak (int margin = 0) = 0;\n\n        /// Changes the alignment for the current section of text.\n        virtual void setSectionAlignment (Alignment sectionAlignment) = 0;\n\n        // Layout a block of text with the specified style into the document.\n        virtual void write (Style * Style, Utf8Span Text) = 0;\n\n        /// Adds a content block to the document without laying it out. An\n        /// identifier is returned that can be used to refer to it. If select\n        /// is true, the block is activated to be references by future writes.\n        virtual intptr_t addContent (Utf8Span Text, bool Select = true) = 0;\n\n        /// Select a previously created content block for future writes.\n        virtual void selectContent (intptr_t contentHandle) = 0;\n\n        /// Layout a span of the selected content block into the document\n        /// using the specified style.\n        virtual void write (Style * Style, size_t Begin, size_t End) = 0;\n\n        /// Finalize the document layout, and return a pointer to it.\n        virtual TypesetBook::Ptr complete () = 0;\n    };\n\n    /// An interface to the BookPage widget.\n    class BookPage : public MyGUI::Widget\n    {\n    MYGUI_RTTI_DERIVED(BookPage)\n    public:\n\n        typedef TypesetBook::InteractiveId InteractiveId;\n        typedef std::function <void (InteractiveId)> ClickCallback;\n\n        /// Make the widget display the specified page from the specified book.\n        virtual void showPage (TypesetBook::Ptr Book, size_t Page) = 0;\n\n        /// Set the callback for a clicking a hyper-link in the document.\n        virtual void adviseLinkClicked (ClickCallback callback) = 0;\n\n        /// Clear the hyper-link click callback.\n        virtual void unadviseLinkClicked () = 0;\n\n        /// Register the widget and associated sub-widget with MyGUI. Should be\n        /// called once near the beginning of the program.\n        static void registerMyGUIComponents ();\n    };\n}\n\n#endif // MWGUI_BOOKPAGE_HPP\n"
  },
  {
    "path": "apps/openmw/mwgui/bookwindow.cpp",
    "content": "#include \"bookwindow.hpp\"\n\n#include <MyGUI_TextBox.h>\n#include <MyGUI_InputManager.h>\n\n#include <components/esm/loadbook.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/actiontake.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"formatting.hpp\"\n\nnamespace MWGui\n{\n\n    BookWindow::BookWindow ()\n        : BookWindowBase(\"openmw_book.layout\")\n        , mCurrentPage(0)\n        , mTakeButtonShow(true)\n        , mTakeButtonAllowed(true)\n    {\n        getWidget(mCloseButton, \"CloseButton\");\n        mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked);\n\n        getWidget(mTakeButton, \"TakeButton\");\n        mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked);\n\n        getWidget(mNextPageButton, \"NextPageBTN\");\n        mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked);\n\n        getWidget(mPrevPageButton, \"PrevPageBTN\");\n        mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked);\n\n        getWidget(mLeftPageNumber, \"LeftPageNumber\");\n        getWidget(mRightPageNumber, \"RightPageNumber\");\n\n        getWidget(mLeftPage, \"LeftPage\");\n        getWidget(mRightPage, \"RightPage\");\n\n        adjustButton(\"CloseButton\");\n        adjustButton(\"TakeButton\");\n        adjustButton(\"PrevPageBTN\");\n        float scale = adjustButton(\"NextPageBTN\");\n\n        mLeftPage->setNeedMouseFocus(true);\n        mLeftPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel);\n        mRightPage->setNeedMouseFocus(true);\n        mRightPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel);\n\n        mNextPageButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed);\n        mPrevPageButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed);\n        mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed);\n        mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed);\n\n        if (mNextPageButton->getSize().width == 64)\n        {\n            // english button has a 7 pixel wide strip of garbage on its right edge\n            mNextPageButton->setSize(64-7, mNextPageButton->getSize().height);\n            mNextPageButton->setImageCoord(MyGUI::IntCoord(0,0,(64-7)*scale,mNextPageButton->getSize().height*scale));\n        }\n\n        center();\n    }\n\n    void BookWindow::onMouseWheel(MyGUI::Widget *_sender, int _rel)\n    {\n        if (_rel < 0)\n            nextPage();\n        else\n            prevPage();\n    }\n\n    void BookWindow::clearPages()\n    {\n        mPages.clear();\n    }\n\n    void BookWindow::setPtr (const MWWorld::Ptr& book)\n    {\n        mBook = book;\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        bool showTakeButton = book.getContainerStore() != &player.getClass().getContainerStore(player);\n\n        clearPages();\n        mCurrentPage = 0;\n\n        MWWorld::LiveCellRef<ESM::Book> *ref = mBook.get<ESM::Book>();\n\n        Formatting::BookFormatter formatter;\n        mPages = formatter.markupToWidget(mLeftPage, ref->mBase->mText);\n        formatter.markupToWidget(mRightPage, ref->mBase->mText);\n\n        updatePages();\n\n        setTakeButtonShow(showTakeButton);\n\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);\n    }\n\n    void BookWindow::setTakeButtonShow(bool show)\n    {\n        mTakeButtonShow = show;\n        mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);\n    }\n\n    void BookWindow::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character)\n    {\n        if (key == MyGUI::KeyCode::ArrowUp)\n            prevPage();\n        else if (key == MyGUI::KeyCode::ArrowDown)\n            nextPage();\n    }\n\n    void BookWindow::setInventoryAllowed(bool allowed)\n    {\n        mTakeButtonAllowed = allowed;\n        mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);\n    }\n\n    void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);\n    }\n\n    void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender)\n    {\n        MWBase::Environment::get().getWindowManager()->playSound(\"Item Book Up\");\n\n        MWWorld::ActionTake take(mBook);\n        take.execute (MWMechanics::getPlayer());\n\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);\n    }\n\n    void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender)\n    {\n        nextPage();\n    }\n\n    void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender)\n    {\n        prevPage();\n    }\n\n    void BookWindow::updatePages()\n    {\n        mLeftPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 1) );\n        mRightPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 2) );\n\n        MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n        bool nextPageVisible = (mCurrentPage+1)*2 < mPages.size();\n        mNextPageButton->setVisible(nextPageVisible);\n        bool prevPageVisible = mCurrentPage != 0;\n        mPrevPageButton->setVisible(prevPageVisible);\n\n        if (focus == mNextPageButton && !nextPageVisible && prevPageVisible)\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mPrevPageButton);\n        else if (focus == mPrevPageButton && !prevPageVisible && nextPageVisible)\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNextPageButton);\n\n        if (mPages.empty())\n            return;\n\n        MyGUI::Widget * paper;\n\n        paper = mLeftPage->getChildAt(0);\n        paper->setCoord(paper->getPosition().left, -mPages[mCurrentPage*2].first,\n                paper->getWidth(), mPages[mCurrentPage*2].second);\n\n        paper = mRightPage->getChildAt(0);\n        if ((mCurrentPage+1)*2 <= mPages.size())\n        {\n            paper->setCoord(paper->getPosition().left, -mPages[mCurrentPage*2+1].first,\n                    paper->getWidth(), mPages[mCurrentPage*2+1].second);\n            paper->setVisible(true);\n        }\n        else\n        {\n            paper->setVisible(false);\n        }\n    }\n\n    void BookWindow::nextPage()\n    {\n        if ((mCurrentPage+1)*2 < mPages.size())\n        {\n            MWBase::Environment::get().getWindowManager()->playSound(\"book page2\");\n\n            ++mCurrentPage;\n\n            updatePages();\n        }\n    }\n    void BookWindow::prevPage()\n    {\n        if (mCurrentPage > 0)\n        {\n            MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n\n            --mCurrentPage;\n\n            updatePages();\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/bookwindow.hpp",
    "content": "#ifndef MWGUI_BOOKWINDOW_H\n#define MWGUI_BOOKWINDOW_H\n\n#include \"windowbase.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n\n#include <components/widgets/imagebutton.hpp>\n\nnamespace MWGui\n{\n    class BookWindow : public BookWindowBase\n    {\n        public:\n            BookWindow();\n\n            void setPtr(const MWWorld::Ptr& book) override;\n            void setInventoryAllowed(bool allowed);\n\n            void onResChange(int, int) override { center(); }\n\n        protected:\n            void onNextPageButtonClicked (MyGUI::Widget* sender);\n            void onPrevPageButtonClicked (MyGUI::Widget* sender);\n            void onCloseButtonClicked (MyGUI::Widget* sender);\n            void onTakeButtonClicked (MyGUI::Widget* sender);\n            void onMouseWheel(MyGUI::Widget* _sender, int _rel);\n            void setTakeButtonShow(bool show);\n\n            void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character);\n\n            void nextPage();\n            void prevPage();\n\n            void updatePages();\n            void clearPages();\n\n        private:\n            typedef std::pair<int, int> Page;\n            typedef std::vector<Page> Pages;\n\n            Gui::ImageButton* mCloseButton;\n            Gui::ImageButton* mTakeButton;\n            Gui::ImageButton* mNextPageButton;\n            Gui::ImageButton* mPrevPageButton;\n\n            MyGUI::TextBox* mLeftPageNumber;\n            MyGUI::TextBox* mRightPageNumber;\n            MyGUI::Widget* mLeftPage;\n            MyGUI::Widget* mRightPage;\n\n            unsigned int mCurrentPage; // 0 is first page\n            Pages mPages;\n\n            MWWorld::Ptr mBook;\n\n            bool mTakeButtonShow;\n            bool mTakeButtonAllowed;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/charactercreation.cpp",
    "content": "#include \"charactercreation.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/fallback/fallback.hpp>\n#include <components/misc/rng.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/player.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"textinput.hpp\"\n#include \"race.hpp\"\n#include \"class.hpp\"\n#include \"birth.hpp\"\n#include \"review.hpp\"\n#include \"inventorywindow.hpp\"\n\nnamespace\n{\n    struct Response\n    {\n        const std::string mText;\n        const ESM::Class::Specialization mSpecialization;\n    };\n\n    struct Step\n    {\n        const std::string mText;\n        const Response mResponses[3];\n        const std::string mSound;\n    };\n\n    Step sGenerateClassSteps(int number)\n    {\n        number++;\n\n        std::string question = Fallback::Map::getString(\"Question_\" + MyGUI::utility::toString(number) + \"_Question\");\n        std::string answer0 = Fallback::Map::getString(\"Question_\" + MyGUI::utility::toString(number) + \"_AnswerOne\");\n        std::string answer1 = Fallback::Map::getString(\"Question_\" + MyGUI::utility::toString(number) + \"_AnswerTwo\");\n        std::string answer2 = Fallback::Map::getString(\"Question_\" + MyGUI::utility::toString(number) + \"_AnswerThree\");\n        std::string sound = \"vo\\\\misc\\\\chargen qa\" + MyGUI::utility::toString(number) + \".wav\";\n\n        Response r0 = {answer0, ESM::Class::Combat};\n        Response r1 = {answer1, ESM::Class::Magic};\n        Response r2 = {answer2, ESM::Class::Stealth};\n\n        // randomize order in which responses are displayed\n        int order = Misc::Rng::rollDice(6);\n\n        switch (order)\n        {\n            case 0:\n                return {question, {r0, r1, r2}, sound};\n            case 1:\n                return {question, {r0, r2, r1}, sound};\n            case 2:\n                return {question, {r1, r0, r2}, sound};\n            case 3:\n                return {question, {r1, r2, r0}, sound};\n            case 4:\n                return {question, {r2, r0, r1}, sound};\n            default:\n                return {question, {r2, r1, r0}, sound};\n        }\n    }\n\n    void updatePlayerHealth()\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player);\n        npcStats.updateHealth();\n    }\n}\n\nnamespace MWGui\n{\n\n    CharacterCreation::CharacterCreation(osg::Group* parent, Resource::ResourceSystem* resourceSystem)\n        : mParent(parent)\n        , mResourceSystem(resourceSystem)\n        , mNameDialog(nullptr)\n        , mRaceDialog(nullptr)\n        , mClassChoiceDialog(nullptr)\n        , mGenerateClassQuestionDialog(nullptr)\n        , mGenerateClassResultDialog(nullptr)\n        , mPickClassDialog(nullptr)\n        , mCreateClassDialog(nullptr)\n        , mBirthSignDialog(nullptr)\n        , mReviewDialog(nullptr)\n        , mGenerateClassStep(0)\n    {\n        mCreationStage = CSE_NotStarted;\n        mGenerateClassResponses[0] = ESM::Class::Combat;\n        mGenerateClassResponses[1] = ESM::Class::Magic;\n        mGenerateClassResponses[2] = ESM::Class::Stealth;\n        mGenerateClassSpecializations[0] = 0;\n        mGenerateClassSpecializations[1] = 0;\n        mGenerateClassSpecializations[2] = 0;\n\n        // Setup player stats\n        for (int i = 0; i < ESM::Attribute::Length; ++i)\n            mPlayerAttributes.emplace(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue());\n\n        for (int i = 0; i < ESM::Skill::Length; ++i)\n            mPlayerSkillValues.emplace(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue());\n    }\n\n    void CharacterCreation::setValue (const std::string& id, const MWMechanics::AttributeValue& value)\n    {\n        static const char *ids[] =\n        {\n            \"AttribVal1\", \"AttribVal2\", \"AttribVal3\", \"AttribVal4\",\n            \"AttribVal5\", \"AttribVal6\", \"AttribVal7\", \"AttribVal8\", 0\n        };\n\n        for (int i=0; ids[i]; ++i)\n        {\n            if (ids[i]==id)\n            {\n                mPlayerAttributes[static_cast<ESM::Attribute::AttributeID>(i)] = value;\n                if (mReviewDialog)\n                    mReviewDialog->setAttribute(static_cast<ESM::Attribute::AttributeID>(i), value);\n\n                break;\n            }\n        }\n    }\n\n    void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value)\n    {\n        if (mReviewDialog)\n        {\n            if (id == \"HBar\")\n            {\n                mReviewDialog->setHealth (value);\n            }\n            else if (id == \"MBar\")\n            {\n                mReviewDialog->setMagicka (value);\n            }\n            else if (id == \"FBar\")\n            {\n                mReviewDialog->setFatigue (value);\n            }\n        }\n    }\n\n    void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)\n    {\n        mPlayerSkillValues[parSkill] = value;\n        if (mReviewDialog)\n            mReviewDialog->setSkillValue(parSkill, value);\n    }\n\n    void CharacterCreation::configureSkills (const SkillList& major, const SkillList& minor)\n    {\n        if (mReviewDialog)\n            mReviewDialog->configureSkills(major, minor);\n\n        mPlayerMajorSkills = major;\n        mPlayerMinorSkills = minor;\n    }\n\n    void CharacterCreation::onFrame(float duration)\n    {\n        if (mReviewDialog)\n            mReviewDialog->onFrame(duration);\n    }\n\n    void CharacterCreation::spawnDialog(const char id)\n    {\n        try\n        {\n            switch (id)\n            {\n                case GM_Name:\n                    MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog);\n                    mNameDialog = nullptr;\n                    mNameDialog = new TextInputDialog();\n                    mNameDialog->setTextLabel(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sName\", \"Name\"));\n                    mNameDialog->setTextInput(mPlayerName);\n                    mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen);\n                    mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone);\n                    mNameDialog->setVisible(true);\n                    break;\n\n                case GM_Race:\n                    MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog);\n                    mRaceDialog = nullptr;\n                    mRaceDialog = new RaceDialog(mParent, mResourceSystem);\n                    mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen);\n                    mRaceDialog->setRaceId(mPlayerRaceId);\n                    mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone);\n                    mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack);\n                    mRaceDialog->setVisible(true);\n                    if (mCreationStage < CSE_NameChosen)\n                        mCreationStage = CSE_NameChosen;\n                    break;\n\n                case GM_Class:\n                    MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog);\n                    mClassChoiceDialog = nullptr;\n                    mClassChoiceDialog = new ClassChoiceDialog();\n                    mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice);\n                    mClassChoiceDialog->setVisible(true);\n                    if (mCreationStage < CSE_RaceChosen)\n                        mCreationStage = CSE_RaceChosen;\n                    break;\n\n                case GM_ClassPick:\n                    MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog);\n                    mPickClassDialog = nullptr;\n                    mPickClassDialog = new PickClassDialog();\n                    mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen);\n                    mPickClassDialog->setClassId(mPlayerClass.mId);\n                    mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone);\n                    mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack);\n                    mPickClassDialog->setVisible(true);\n                    if (mCreationStage < CSE_RaceChosen)\n                        mCreationStage = CSE_RaceChosen;\n                    break;\n\n                case GM_Birth:\n                    MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog);\n                    mBirthSignDialog = nullptr;\n                    mBirthSignDialog = new BirthDialog();\n                    mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen);\n                    mBirthSignDialog->setBirthId(mPlayerBirthSignId);\n                    mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone);\n                    mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack);\n                    mBirthSignDialog->setVisible(true);\n                    if (mCreationStage < CSE_ClassChosen)\n                        mCreationStage = CSE_ClassChosen;\n                    break;\n\n                case GM_ClassCreate:\n                    if (!mCreateClassDialog)\n                    {\n                        mCreateClassDialog = new CreateClassDialog();\n                        mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone);\n                        mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack);\n                    }\n                    mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen);\n                    mCreateClassDialog->setVisible(true);\n                    if (mCreationStage < CSE_RaceChosen)\n                        mCreationStage = CSE_RaceChosen;\n                    break;\n                case GM_ClassGenerate:\n                    mGenerateClassStep = 0;\n                    mGenerateClass = \"\";\n                    mGenerateClassSpecializations[0] = 0;\n                    mGenerateClassSpecializations[1] = 0;\n                    mGenerateClassSpecializations[2] = 0;\n                    showClassQuestionDialog();\n                    if (mCreationStage < CSE_RaceChosen)\n                        mCreationStage = CSE_RaceChosen;\n                    break;\n                case GM_Review:\n                    MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog);\n                    mReviewDialog = nullptr;\n                    mReviewDialog = new ReviewDialog();\n\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n                    const ESM::NPC *playerNpc = world->getPlayerPtr().get<ESM::NPC>()->mBase;\n\n                    const MWWorld::Player player = world->getPlayer();\n\n                    const ESM::Class *playerClass = world->getStore().get<ESM::Class>().find(playerNpc->mClass);\n\n                    mReviewDialog->setPlayerName(playerNpc->mName);\n                    mReviewDialog->setRace(playerNpc->mRace);\n                    mReviewDialog->setClass(*playerClass);\n                    mReviewDialog->setBirthSign(player.getBirthSign());\n\n                    MWWorld::Ptr playerPtr = MWMechanics::getPlayer();\n                    const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr);\n\n                    mReviewDialog->setHealth(stats.getHealth());\n                    mReviewDialog->setMagicka(stats.getMagicka());\n                    mReviewDialog->setFatigue(stats.getFatigue());\n                    for (auto& attributePair : mPlayerAttributes)\n                    {\n                        mReviewDialog->setAttribute(static_cast<ESM::Attribute::AttributeID> (attributePair.first), attributePair.second);\n                    }\n                    for (auto& skillPair : mPlayerSkillValues)\n                    {\n                        mReviewDialog->setSkillValue(static_cast<ESM::Skill::SkillEnum> (skillPair.first), skillPair.second);\n                    }\n                    mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills);\n\n                    mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone);\n                    mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack);\n                    mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog);\n                    mReviewDialog->setVisible(true);\n                    if (mCreationStage < CSE_BirthSignChosen)\n                        mCreationStage = CSE_BirthSignChosen;\n                    break;\n            }\n        }\n        catch (std::exception& e)\n        {\n            Log(Debug::Error) << \"Error: Failed to create chargen window: \" << e.what();\n        }\n    }\n\n    void CharacterCreation::onReviewDialogDone(WindowBase* parWindow)\n    {\n        MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog);\n        mReviewDialog = nullptr;\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n    }\n\n    void CharacterCreation::onReviewDialogBack()\n    {\n        MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog);\n        mReviewDialog = nullptr;\n        mCreationStage = CSE_ReviewBack;\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth);\n\n        /*\n            Start of tes3mp addition\n\n            Decrease the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage--;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void CharacterCreation::onReviewActivateDialog(int parDialog)\n    {\n        MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog);\n        mReviewDialog = nullptr;\n        mCreationStage = CSE_ReviewNext;\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n\n        switch(parDialog)\n        {\n            case ReviewDialog::NAME_DIALOG:\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name);\n                break;\n            case ReviewDialog::RACE_DIALOG:\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race);\n                break;\n            case ReviewDialog::CLASS_DIALOG:\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);\n                break;\n            case ReviewDialog::BIRTHSIGN_DIALOG:\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth);\n        };\n    }\n\n    void CharacterCreation::selectPickedClass()\n    {\n        if (mPickClassDialog)\n        {\n            const std::string &classId = mPickClassDialog->getClassId();\n            if (!classId.empty())\n                MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId);\n\n            const ESM::Class *klass =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(classId);\n            if (klass)\n            {\n                mPlayerClass = *klass;\n            }\n            MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog);\n            mPickClassDialog = nullptr;\n        }\n\n        updatePlayerHealth();\n    }\n\n    void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow)\n    {\n        selectPickedClass();\n\n        handleDialogDone(CSE_ClassChosen, GM_Birth);\n\n        /*\n            Start of tes3mp addition\n\n            Increase the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage++;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void CharacterCreation::onPickClassDialogBack()\n    {\n        selectPickedClass();\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);\n    }\n\n    void CharacterCreation::onClassChoice(int _index)\n    {\n        MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog);\n        mClassChoiceDialog = nullptr;\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n\n        switch(_index)\n        {\n            case ClassChoiceDialog::Class_Generate:\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassGenerate);\n                break;\n            case ClassChoiceDialog::Class_Pick:\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassPick);\n                break;\n            case ClassChoiceDialog::Class_Create:\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassCreate);\n                break;\n            case ClassChoiceDialog::Class_Back:\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race);\n\n                /*\n                    Start of tes3mp addition\n\n                    Decrease the character generation stage tracked for the LocalPlayer\n                */\n                mwmp::Main::get().getLocalPlayer()->charGenState.currentStage--;\n                /*\n                    End of tes3mp addition\n                */\n                break;\n\n        };\n    }\n\n    void CharacterCreation::onNameDialogDone(WindowBase* parWindow)\n    {\n        if (mNameDialog)\n        {\n            mPlayerName = mNameDialog->getTextInput();\n\n            /*\n                Start of tes3mp change (major)\n\n                Ensure names are not longer than the original game's 31 character maximum\n            */\n            if (mPlayerName.length() > 31)\n                mPlayerName = mPlayerName.substr(0, 31);\n            /*\n                End of tes3mp change (major)\n            */\n\n            MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName);\n            MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog);\n            mNameDialog = nullptr;\n        }\n\n        handleDialogDone(CSE_NameChosen, GM_Race);\n\n        /*\n            Start of tes3mp addition\n\n            Increase the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage++;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void CharacterCreation::selectRace()\n    {\n        if (mRaceDialog)\n        {\n            const ESM::NPC &data = mRaceDialog->getResult();\n            mPlayerRaceId = data.mRace;\n            if (!mPlayerRaceId.empty()) {\n                MWBase::Environment::get().getMechanicsManager()->setPlayerRace(\n                    data.mRace,\n                    data.isMale(),\n                    data.mHead,\n                    data.mHair\n                );\n            }\n            MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar();\n\n            MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog);\n            mRaceDialog = nullptr;\n        }\n\n        updatePlayerHealth();\n    }\n\n    void CharacterCreation::onRaceDialogBack()\n    {\n        selectRace();\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name);\n    }\n\n    void CharacterCreation::onRaceDialogDone(WindowBase* parWindow)\n    {\n        selectRace();\n\n        handleDialogDone(CSE_RaceChosen, GM_Class);\n\n        /*\n            Start of tes3mp addition\n\n            Increase the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage++;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void CharacterCreation::selectBirthSign()\n    {\n        if (mBirthSignDialog)\n        {\n            mPlayerBirthSignId = mBirthSignDialog->getBirthId();\n            if (!mPlayerBirthSignId.empty())\n                MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId);\n            MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog);\n            mBirthSignDialog = nullptr;\n        }\n\n        updatePlayerHealth();\n    }\n\n    void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow)\n    {\n        selectBirthSign();\n\n        handleDialogDone(CSE_BirthSignChosen, GM_Review);\n\n        /*\n            Start of tes3mp addition\n\n            Increase the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage++;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void CharacterCreation::onBirthSignDialogBack()\n    {\n        selectBirthSign();\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);\n\n        /*\n            Start of tes3mp addition\n\n            Decrease the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage--;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void CharacterCreation::selectCreatedClass()\n    {\n        if (mCreateClassDialog)\n        {\n            ESM::Class klass;\n            klass.mName = mCreateClassDialog->getName();\n            klass.mDescription = mCreateClassDialog->getDescription();\n            klass.mData.mSpecialization = mCreateClassDialog->getSpecializationId();\n            klass.mData.mIsPlayable = 0x1;\n\n            std::vector<int> attributes = mCreateClassDialog->getFavoriteAttributes();\n            assert(attributes.size() == 2);\n            klass.mData.mAttribute[0] = attributes[0];\n            klass.mData.mAttribute[1] = attributes[1];\n\n            std::vector<ESM::Skill::SkillEnum> majorSkills = mCreateClassDialog->getMajorSkills();\n            std::vector<ESM::Skill::SkillEnum> minorSkills = mCreateClassDialog->getMinorSkills();\n            assert(majorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]));\n            assert(minorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]));\n            for (size_t i = 0; i < sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]); ++i)\n            {\n                klass.mData.mSkills[i][1] = majorSkills[i];\n                klass.mData.mSkills[i][0] = minorSkills[i];\n            }\n\n            MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass);\n            mPlayerClass = klass;\n\n            // Do not delete dialog, so that choices are remembered in case we want to go back and adjust them later\n            mCreateClassDialog->setVisible(false);\n        }\n        updatePlayerHealth();\n    }\n\n    void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow)\n    {\n        selectCreatedClass();\n\n        handleDialogDone(CSE_ClassChosen, GM_Birth);\n\n        /*\n            Start of tes3mp addition\n\n            Increase the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage++;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void CharacterCreation::onCreateClassDialogBack()\n    {\n        // not done in MW, but we do it for consistency with the other dialogs\n        selectCreatedClass();\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);\n\n        /*\n            Start of tes3mp addition\n\n            Decrease the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage--;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void CharacterCreation::onClassQuestionChosen(int _index)\n    {\n        MWBase::Environment::get().getSoundManager()->stopSay();\n\n        MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog);\n        mGenerateClassQuestionDialog = nullptr;\n\n        if (_index < 0 || _index >= 3)\n        {\n            MWBase::Environment::get().getWindowManager()->popGuiMode();\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);\n            return;\n        }\n\n        ESM::Class::Specialization specialization = mGenerateClassResponses[_index];\n        if (specialization == ESM::Class::Combat)\n            ++mGenerateClassSpecializations[0];\n        else if (specialization == ESM::Class::Magic)\n            ++mGenerateClassSpecializations[1];\n        else if (specialization == ESM::Class::Stealth)\n            ++mGenerateClassSpecializations[2];\n        ++mGenerateClassStep;\n        showClassQuestionDialog();\n    }\n\n    void CharacterCreation::showClassQuestionDialog()\n    {\n        if (mGenerateClassStep == 10)\n        {\n            unsigned combat = mGenerateClassSpecializations[0];\n            unsigned magic = mGenerateClassSpecializations[1];\n            unsigned stealth = mGenerateClassSpecializations[2];\n\n            if (combat > 7)\n            {\n                mGenerateClass = \"Warrior\";\n            }\n            else if (magic > 7)\n            {\n                mGenerateClass = \"Mage\";\n            }\n            else if (stealth > 7)\n            {\n                mGenerateClass = \"Thief\";\n            }\n            else\n            {\n                switch (combat)\n                {\n                    case 4:\n                        mGenerateClass = \"Rogue\";\n                        break;\n                    case 5:\n                        if (stealth == 3)\n                            mGenerateClass = \"Scout\";\n                        else\n                            mGenerateClass = \"Archer\";\n                        break;\n                    case 6:\n                        if (stealth == 1)\n                            mGenerateClass = \"Barbarian\";\n                        else if (stealth == 3)\n                            mGenerateClass = \"Crusader\";\n                        else\n                            mGenerateClass = \"Knight\";\n                        break;\n                    case 7:\n                        mGenerateClass = \"Warrior\";\n                        break;\n                    default:\n                        switch (magic)\n                        {\n                            case 4:\n                                mGenerateClass = \"Spellsword\";\n                                break;\n                            case 5:\n                                mGenerateClass = \"Witchhunter\";\n                                break;\n                            case 6:\n                                if (combat == 2)\n                                    mGenerateClass = \"Sorcerer\";\n                                else if (combat == 3)\n                                    mGenerateClass = \"Healer\";\n                                else\n                                    mGenerateClass = \"Battlemage\";\n                                break;\n                            case 7:\n                                mGenerateClass = \"Mage\";\n                                break;\n                            default:\n                                switch (stealth)\n                                {\n                                    case 3:\n                                        if (magic == 3)\n                                            mGenerateClass = \"Bard\"; // unreachable\n                                        else\n                                            mGenerateClass = \"Warrior\";\n                                        break;\n                                    case 5:\n                                        if (magic == 3)\n                                            mGenerateClass = \"Monk\";\n                                        else\n                                            mGenerateClass = \"Pilgrim\";\n                                        break;\n                                    case 6:\n                                        if (magic == 1)\n                                            mGenerateClass = \"Agent\";\n                                        else if (magic == 3)\n                                            mGenerateClass = \"Assassin\";\n                                        else\n                                            mGenerateClass = \"Acrobat\";\n                                        break;\n                                    case 7:\n                                        mGenerateClass = \"Thief\";\n                                        break;\n                                    default:\n                                        mGenerateClass = \"Warrior\";\n                                }\n                        }\n                }\n            }\n\n            MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog);\n            mGenerateClassResultDialog = nullptr;\n\n            mGenerateClassResultDialog = new GenerateClassResultDialog();\n            mGenerateClassResultDialog->setClassId(mGenerateClass);\n            mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack);\n            mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone);\n            mGenerateClassResultDialog->setVisible(true);\n            return;\n        }\n\n        if (mGenerateClassStep > 10)\n        {\n            MWBase::Environment::get().getWindowManager()->popGuiMode();\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);\n            return;\n        }\n\n        MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog);\n        mGenerateClassQuestionDialog = nullptr;\n\n        mGenerateClassQuestionDialog = new InfoBoxDialog();\n\n        Step step = sGenerateClassSteps(mGenerateClassStep);\n        mGenerateClassResponses[0] = step.mResponses[0].mSpecialization;\n        mGenerateClassResponses[1] = step.mResponses[1].mSpecialization;\n        mGenerateClassResponses[2] = step.mResponses[2].mSpecialization;\n\n        InfoBoxDialog::ButtonList buttons;\n        mGenerateClassQuestionDialog->setText(step.mText);\n        buttons.push_back(step.mResponses[0].mText);\n        buttons.push_back(step.mResponses[1].mText);\n        buttons.push_back(step.mResponses[2].mText);\n        mGenerateClassQuestionDialog->setButtons(buttons);\n        mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen);\n        mGenerateClassQuestionDialog->setVisible(true);\n\n        MWBase::Environment::get().getSoundManager()->say(step.mSound);\n    }\n\n    void CharacterCreation::selectGeneratedClass()\n    {\n        MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog);\n        mGenerateClassResultDialog = nullptr;\n\n        MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass);\n\n        const ESM::Class *klass =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(mGenerateClass);\n\n        mPlayerClass = *klass;\n\n        updatePlayerHealth();\n    }\n\n    void CharacterCreation::onGenerateClassBack()\n    {\n        selectGeneratedClass();\n\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);\n    }\n\n    void CharacterCreation::onGenerateClassDone(WindowBase* parWindow)\n    {\n        selectGeneratedClass();\n\n        handleDialogDone(CSE_ClassChosen, GM_Birth);\n\n        /*\n            Start of tes3mp addition\n\n            Increase the character generation stage tracked for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->charGenState.currentStage++;\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    CharacterCreation::~CharacterCreation()\n    {\n        delete mNameDialog;\n        delete mRaceDialog;\n        delete mClassChoiceDialog;\n        delete mGenerateClassQuestionDialog;\n        delete mGenerateClassResultDialog;\n        delete mPickClassDialog;\n        delete mCreateClassDialog;\n        delete mBirthSignDialog;\n        delete mReviewDialog;\n    }\n\n    void CharacterCreation::handleDialogDone(CSE currentStage, int nextMode)\n    {\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n        if (mCreationStage == CSE_ReviewNext)\n        {\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review);\n        }\n        /*\n            Start of tes3mp change (major)\n\n            Servers have control over character generation in multiplayer, which is why\n            the automatic transition to the next character generation menu has been\n            commented out here\n        */\n        /*\n        else if (mCreationStage >= currentStage)\n        {\n            MWBase::Environment::get().getWindowManager()->pushGuiMode((GuiMode)nextMode);\n        }\n        */\n        /*\n            End of tes3mp change (major)\n        */\n        else\n        {\n            mCreationStage = currentStage;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/charactercreation.hpp",
    "content": "#ifndef CHARACTER_CREATION_HPP\n#define CHARACTER_CREATION_HPP\n\n#include <components/esm/loadclas.hpp>\n\n#include <map>\n#include <vector>\n\n#include \"statswatcher.hpp\"\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWGui\n{\n    class WindowBase;\n\n    class TextInputDialog;\n    class InfoBoxDialog;\n    class RaceDialog;\n    class DialogueWindow;\n    class ClassChoiceDialog;\n    class GenerateClassResultDialog;\n    class PickClassDialog;\n    class CreateClassDialog;\n    class BirthDialog;\n    class ReviewDialog;\n    class MessageBoxManager;\n\n    class CharacterCreation : public StatsListener\n    {\n    public:\n    typedef std::vector<int> SkillList;\n\n    CharacterCreation(osg::Group* parent, Resource::ResourceSystem* resourceSystem);\n    virtual ~CharacterCreation();\n\n    //Show a dialog\n    void spawnDialog(const char id);\n\n    void setValue (const std::string& id, const MWMechanics::AttributeValue& value) override;\n    void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value) override;\n    void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override;\n    void configureSkills(const SkillList& major, const SkillList& minor) override;\n\n    void onFrame(float duration);\n\n    private:\n    osg::Group* mParent;\n    Resource::ResourceSystem* mResourceSystem;\n\n    SkillList mPlayerMajorSkills, mPlayerMinorSkills;\n    std::map<int, MWMechanics::AttributeValue> mPlayerAttributes;\n    std::map<int, MWMechanics::SkillValue> mPlayerSkillValues;\n\n    //Dialogs\n    TextInputDialog* mNameDialog;\n    RaceDialog* mRaceDialog;\n    ClassChoiceDialog* mClassChoiceDialog;\n    InfoBoxDialog* mGenerateClassQuestionDialog;\n    GenerateClassResultDialog* mGenerateClassResultDialog;\n    PickClassDialog* mPickClassDialog;\n    CreateClassDialog* mCreateClassDialog;\n    BirthDialog* mBirthSignDialog;\n    ReviewDialog* mReviewDialog;\n\n    //Player data\n    std::string mPlayerName;\n    std::string mPlayerRaceId;\n    std::string mPlayerBirthSignId;\n    ESM::Class mPlayerClass;\n\n    //Class generation vars\n    unsigned mGenerateClassStep;                 // Keeps track of current step in Generate Class dialog\n    ESM::Class::Specialization mGenerateClassResponses[3];\n    unsigned mGenerateClassSpecializations[3];   // A counter for each specialization which is increased when an answer is chosen\n    std::string mGenerateClass;                  // In order: Combat, Magic, Stealth\n\n    ////Dialog events\n    //Name dialog\n    void onNameDialogDone(WindowBase* parWindow);\n\n    //Race dialog\n    void onRaceDialogDone(WindowBase* parWindow);\n    void onRaceDialogBack();\n    void selectRace();\n\n    //Class dialogs\n    void onClassChoice(int _index);\n    void onPickClassDialogDone(WindowBase* parWindow);\n    void onPickClassDialogBack();\n    void onCreateClassDialogDone(WindowBase* parWindow);\n    void onCreateClassDialogBack();\n    void showClassQuestionDialog();\n    void onClassQuestionChosen(int _index);\n    void onGenerateClassBack();\n    void onGenerateClassDone(WindowBase* parWindow);\n    void selectGeneratedClass();\n    void selectCreatedClass();\n    void selectPickedClass();\n\n    //Birthsign dialog\n    void onBirthSignDialogDone(WindowBase* parWindow);\n    void onBirthSignDialogBack();\n    void selectBirthSign();\n\n    //Review dialog\n    void onReviewDialogDone(WindowBase* parWindow);\n    void onReviewDialogBack();\n    void onReviewActivateDialog(int parDialog);\n\n    enum CSE    //Creation Stage Enum\n    {\n        CSE_NotStarted,\n        CSE_NameChosen,\n        CSE_RaceChosen,\n        CSE_ClassChosen,\n        CSE_BirthSignChosen,\n        CSE_ReviewBack,\n        CSE_ReviewNext\n    };\n\n    CSE mCreationStage; // Which state the character creating is in, controls back/next/ok buttons\n\n    void handleDialogDone(CSE currentStage, int nextMode);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/class.cpp",
    "content": "#include \"class.hpp\"\n\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_ListBox.h>\n#include <MyGUI_Gui.h>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include \"tooltips.hpp\"\n\nnamespace\n{\n\n    bool sortClasses(const std::pair<std::string, std::string>& left, const std::pair<std::string, std::string>& right)\n    {\n        return left.second.compare(right.second) < 0;\n    }\n\n}\n\nnamespace MWGui\n{\n\n    /* GenerateClassResultDialog */\n\n    GenerateClassResultDialog::GenerateClassResultDialog()\n      : WindowModal(\"openmw_chargen_generate_class_result.layout\")\n    {\n        setText(\"ReflectT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sMessageQuestionAnswer1\", \"\"));\n\n        getWidget(mClassImage, \"ClassImage\");\n        getWidget(mClassName, \"ClassName\");\n\n        MyGUI::Button* backButton;\n        getWidget(backButton, \"BackButton\");\n        backButton->setCaptionWithReplacing(\"#{sMessageQuestionAnswer3}\");\n        backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked);\n\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->setCaptionWithReplacing(\"#{sMessageQuestionAnswer2}\");\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked);\n\n        center();\n    }\n\n    std::string GenerateClassResultDialog::getClassId() const\n    {\n        return mClassName->getCaption();\n    }\n\n    void GenerateClassResultDialog::setClassId(const std::string &classId)\n    {\n        mCurrentClassId = classId;\n\n        setClassImage(mClassImage, mCurrentClassId);\n\n        mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(mCurrentClassId)->mName);\n\n        center();\n    }\n\n    // widget controls\n\n    void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender)\n    {\n        eventDone(this);\n    }\n\n    void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender)\n    {\n        eventBack();\n    }\n\n    /* PickClassDialog */\n\n    PickClassDialog::PickClassDialog()\n      : WindowModal(\"openmw_chargen_class.layout\")\n    {\n        // Centre dialog\n        center();\n\n        getWidget(mSpecializationName, \"SpecializationName\");\n\n        getWidget(mFavoriteAttribute[0], \"FavoriteAttribute0\");\n        getWidget(mFavoriteAttribute[1], \"FavoriteAttribute1\");\n\n        for(int i = 0; i < 5; i++)\n        {\n            char theIndex = '0'+i;\n            getWidget(mMajorSkill[i], std::string(\"MajorSkill\").append(1, theIndex));\n            getWidget(mMinorSkill[i], std::string(\"MinorSkill\").append(1, theIndex));\n        }\n\n        getWidget(mClassList, \"ClassList\");\n        mClassList->setScrollVisible(true);\n        mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onAccept);\n        mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);\n\n        getWidget(mClassImage, \"ClassImage\");\n\n        MyGUI::Button* backButton;\n        getWidget(backButton, \"BackButton\");\n        backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked);\n\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked);\n\n        updateClasses();\n        updateStats();\n    }\n\n    void PickClassDialog::setNextButtonShow(bool shown)\n    {\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n\n        if (shown)\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sNext\", \"\"));\n        else\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOK\", \"\"));\n    }\n\n    void PickClassDialog::onOpen()\n    {\n        WindowModal::onOpen ();\n        updateClasses();\n        updateStats();\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mClassList);\n\n        // Show the current class by default\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n\n        const std::string &classId =\n            player.get<ESM::NPC>()->mBase->mClass;\n\n        if (!classId.empty())\n            setClassId(classId);\n    }\n\n    void PickClassDialog::setClassId(const std::string &classId)\n    {\n        mCurrentClassId = classId;\n        mClassList->setIndexSelected(MyGUI::ITEM_NONE);\n        size_t count = mClassList->getItemCount();\n        for (size_t i = 0; i < count; ++i)\n        {\n            if (Misc::StringUtils::ciEqual(*mClassList->getItemDataAt<std::string>(i), classId))\n            {\n                mClassList->setIndexSelected(i);\n                break;\n            }\n        }\n\n        updateStats();\n    }\n\n    // widget controls\n\n    void PickClassDialog::onOkClicked(MyGUI::Widget* _sender)\n    {\n        if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE)\n            return;\n        eventDone(this);\n    }\n\n    void PickClassDialog::onBackClicked(MyGUI::Widget* _sender)\n    {\n        eventBack();\n    }\n\n    void PickClassDialog::onAccept(MyGUI::ListBox* _sender, size_t _index)\n    {\n        onSelectClass(_sender, _index);\n        if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE)\n            return;\n        eventDone(this);\n    }\n\n    void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index)\n    {\n        if (_index == MyGUI::ITEM_NONE)\n            return;\n\n        const std::string *classId = mClassList->getItemDataAt<std::string>(_index);\n        if (Misc::StringUtils::ciEqual(mCurrentClassId, *classId))\n            return;\n\n        mCurrentClassId = *classId;\n        updateStats();\n    }\n\n    // update widget content\n\n    void PickClassDialog::updateClasses()\n    {\n        mClassList->removeAllItems();\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n\n        std::vector<std::pair<std::string, std::string> > items; // class id, class name\n        for (const ESM::Class& classInfo : store.get<ESM::Class>())\n        {\n            bool playable = (classInfo.mData.mIsPlayable != 0);\n            if (!playable) // Only display playable classes\n                continue;\n\n            if (store.get<ESM::Class>().isDynamic(classInfo.mId))\n                continue; // custom-made class not relevant for this dialog\n\n            items.emplace_back(classInfo.mId, classInfo.mName);\n        }\n        std::sort(items.begin(), items.end(), sortClasses);\n\n        int index = 0;\n        for (auto& itemPair : items)\n        {\n            const std::string &id = itemPair.first;\n            mClassList->addItem(itemPair.second, id);\n            if (mCurrentClassId.empty())\n            {\n                mCurrentClassId = id;\n                mClassList->setIndexSelected(index);\n            }\n            else if (Misc::StringUtils::ciEqual(id, mCurrentClassId))\n            {\n                mClassList->setIndexSelected(index);\n            }\n            ++index;\n        }\n    }\n\n    void PickClassDialog::updateStats()\n    {\n        if (mCurrentClassId.empty())\n            return;\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        const ESM::Class *klass = store.get<ESM::Class>().search(mCurrentClassId);\n        if (!klass)\n            return;\n\n        ESM::Class::Specialization specialization = static_cast<ESM::Class::Specialization>(klass->mData.mSpecialization);\n\n        static const char *specIds[3] = {\n            \"sSpecializationCombat\",\n            \"sSpecializationMagic\",\n            \"sSpecializationStealth\"\n        };\n        std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[specialization], specIds[specialization]);\n        mSpecializationName->setCaption(specName);\n        ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization);\n\n        mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]);\n        mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]);\n        ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId());\n        ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId());\n\n        for (int i = 0; i < 5; ++i)\n        {\n            mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]);\n            mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]);\n            ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]);\n            ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]);\n        }\n\n        setClassImage(mClassImage, mCurrentClassId);\n    }\n\n    /* InfoBoxDialog */\n\n    void InfoBoxDialog::fitToText(MyGUI::TextBox* widget)\n    {\n        MyGUI::IntCoord inner = widget->getTextRegion();\n        MyGUI::IntCoord outer = widget->getCoord();\n        MyGUI::IntSize size = widget->getTextSize();\n        size.width += outer.width - inner.width;\n        size.height += outer.height - inner.height;\n        widget->setSize(size);\n    }\n\n    void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin)\n    {\n        size_t count = widget->getChildCount();\n        int pos = 0;\n        pos += margin;\n        int width = 0;\n        for (unsigned i = 0; i < count; ++i)\n        {\n            MyGUI::Widget* child = widget->getChildAt(i);\n            if (!child->getVisible())\n                continue;\n\n            child->setPosition(child->getLeft(), pos);\n            width = std::max(width, child->getWidth());\n            pos += child->getHeight() + margin;\n        }\n        width += margin*2;\n        widget->setSize(width, pos);\n    }\n\n    InfoBoxDialog::InfoBoxDialog()\n        : WindowModal(\"openmw_infobox.layout\")\n    {\n        getWidget(mTextBox, \"TextBox\");\n        getWidget(mText, \"Text\");\n        mText->getSubWidgetText()->setWordWrap(true);\n        getWidget(mButtonBar, \"ButtonBar\");\n\n        center();\n    }\n\n    void InfoBoxDialog::setText(const std::string &str)\n    {\n        mText->setCaption(str);\n        mTextBox->setVisible(!str.empty());\n        fitToText(mText);\n    }\n\n    std::string InfoBoxDialog::getText() const\n    {\n        return mText->getCaption();\n    }\n\n    void InfoBoxDialog::setButtons(ButtonList &buttons)\n    {\n        for (MyGUI::Button* button : this->mButtons)\n        {\n            MyGUI::Gui::getInstance().destroyWidget(button);\n        }\n        this->mButtons.clear();\n\n        // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget\n        MyGUI::Button* button;\n        MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10);\n        for (const std::string &text : buttons)\n        {\n            button = mButtonBar->createWidget<MyGUI::Button>(\"MW_Button\", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, \"\");\n            button->getSubWidgetText()->setWordWrap(true);\n            button->setCaption(text);\n            fitToText(button);\n            button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked);\n            coord.top += button->getHeight();\n            this->mButtons.push_back(button);\n        }\n    }\n\n    void InfoBoxDialog::onOpen()\n    {\n        WindowModal::onOpen();\n        // Fix layout\n        layoutVertically(mTextBox, 4);\n        layoutVertically(mButtonBar, 6);\n        layoutVertically(mMainWidget, 4 + 6);\n\n        center();\n    }\n\n    void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender)\n    {\n        int i = 0;\n        for (MyGUI::Button* button : mButtons)\n        {\n            if (button == _sender)\n            {\n                eventButtonSelected(i);\n                return;\n            }\n            ++i;\n        }\n    }\n\n    /* ClassChoiceDialog */\n\n    ClassChoiceDialog::ClassChoiceDialog()\n        : InfoBoxDialog()\n    {\n        setText(\"\");\n        ButtonList buttons;\n        buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sClassChoiceMenu1\", \"\"));\n        buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sClassChoiceMenu2\", \"\"));\n        buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sClassChoiceMenu3\", \"\"));\n        buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sBack\", \"\"));\n        setButtons(buttons);\n    }\n\n    /* CreateClassDialog */\n\n    CreateClassDialog::CreateClassDialog()\n      : WindowModal(\"openmw_chargen_create_class.layout\")\n      , mSpecDialog(nullptr)\n      , mAttribDialog(nullptr)\n      , mSkillDialog(nullptr)\n      , mDescDialog(nullptr)\n      , mAffectedAttribute(nullptr)\n      , mAffectedSkill(nullptr)\n    {\n        // Centre dialog\n        center();\n\n        setText(\"SpecializationT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sChooseClassMenu1\", \"Specialization\"));\n        getWidget(mSpecializationName, \"SpecializationName\");\n        mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked);\n\n        setText(\"FavoriteAttributesT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sChooseClassMenu2\", \"Favorite Attributes:\"));\n        getWidget(mFavoriteAttribute0, \"FavoriteAttribute0\");\n        getWidget(mFavoriteAttribute1, \"FavoriteAttribute1\");\n        mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked);\n        mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked);\n\n        setText(\"MajorSkillT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sSkillClassMajor\", \"\"));\n        setText(\"MinorSkillT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sSkillClassMinor\", \"\"));\n        for(int i = 0; i < 5; i++)\n        {\n            char theIndex = '0'+i;\n            getWidget(mMajorSkill[i], std::string(\"MajorSkill\").append(1, theIndex));\n            getWidget(mMinorSkill[i], std::string(\"MinorSkill\").append(1, theIndex));\n            mSkills.push_back(mMajorSkill[i]);\n            mSkills.push_back(mMinorSkill[i]);\n        }\n\n        for (Widgets::MWSkillPtr& skill : mSkills)\n        {\n            skill->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked);\n        }\n\n        setText(\"LabelT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sName\", \"\"));\n        getWidget(mEditName, \"EditName\");\n\n        // Make sure the edit box has focus\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mEditName);\n\n        MyGUI::Button* descriptionButton;\n        getWidget(descriptionButton, \"DescriptionButton\");\n        descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked);\n\n        MyGUI::Button* backButton;\n        getWidget(backButton, \"BackButton\");\n        backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked);\n\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked);\n\n        // Set default skills, attributes\n\n        mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength);\n        mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility);\n\n        mMajorSkill[0]->setSkillId(ESM::Skill::Block);\n        mMajorSkill[1]->setSkillId(ESM::Skill::Armorer);\n        mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor);\n        mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor);\n        mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon);\n\n        mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade);\n        mMinorSkill[1]->setSkillId(ESM::Skill::Axe);\n        mMinorSkill[2]->setSkillId(ESM::Skill::Spear);\n        mMinorSkill[3]->setSkillId(ESM::Skill::Athletics);\n        mMinorSkill[4]->setSkillId(ESM::Skill::Enchant);\n\n        setSpecialization(0);\n        update();\n    }\n\n    CreateClassDialog::~CreateClassDialog()\n    {\n        delete mSpecDialog;\n        delete mAttribDialog;\n        delete mSkillDialog;\n        delete mDescDialog;\n    }\n\n    void CreateClassDialog::update()\n    {\n        for (int i = 0; i < 5; ++i)\n        {\n            ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId());\n            ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId());\n        }\n\n        ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId());\n        ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId());\n    }\n\n    std::string CreateClassDialog::getName() const\n    {\n        return mEditName->getCaption();\n    }\n\n    std::string CreateClassDialog::getDescription() const\n    {\n        return mDescription;\n    }\n\n    ESM::Class::Specialization CreateClassDialog::getSpecializationId() const\n    {\n        return mSpecializationId;\n    }\n\n    std::vector<int> CreateClassDialog::getFavoriteAttributes() const\n    {\n        std::vector<int> v;\n        v.push_back(mFavoriteAttribute0->getAttributeId());\n        v.push_back(mFavoriteAttribute1->getAttributeId());\n        return v;\n    }\n\n    std::vector<ESM::Skill::SkillEnum> CreateClassDialog::getMajorSkills() const\n    {\n        std::vector<ESM::Skill::SkillEnum> v;\n        for(int i = 0; i < 5; i++)\n        {\n            v.push_back(mMajorSkill[i]->getSkillId());\n        }\n        return v;\n    }\n\n    std::vector<ESM::Skill::SkillEnum> CreateClassDialog::getMinorSkills() const\n    {\n        std::vector<ESM::Skill::SkillEnum> v;\n        for(int i=0; i < 5; i++)\n        {\n            v.push_back(mMinorSkill[i]->getSkillId());\n        }\n        return v;\n    }\n\n    void CreateClassDialog::setNextButtonShow(bool shown)\n    {\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n\n        if (shown)\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sNext\", \"\"));\n        else\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOK\", \"\"));\n    }\n\n    // widget controls\n\n    void CreateClassDialog::onDialogCancel()\n    {\n        MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog);\n        mSpecDialog = nullptr;\n\n        MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog);\n        mAttribDialog = nullptr;\n\n        MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog);\n        mSkillDialog = nullptr;\n\n        MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog);\n        mDescDialog = nullptr;\n    }\n\n    void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender)\n    {\n        delete mSpecDialog;\n        mSpecDialog = new SelectSpecializationDialog();\n        mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);\n        mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected);\n        mSpecDialog->setVisible(true);\n    }\n\n    void CreateClassDialog::onSpecializationSelected()\n    {\n        mSpecializationId = mSpecDialog->getSpecializationId();\n        setSpecialization(mSpecializationId);\n\n        MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog);\n        mSpecDialog = nullptr;\n    }\n\n    void CreateClassDialog::setSpecialization(int id)\n    {\n        mSpecializationId = (ESM::Class::Specialization) id;\n        static const char *specIds[3] = {\n            \"sSpecializationCombat\",\n            \"sSpecializationMagic\",\n            \"sSpecializationStealth\"\n        };\n        std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]);\n        mSpecializationName->setCaption(specName);\n        ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId);\n    }\n\n    void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender)\n    {\n        delete mAttribDialog;\n        mAttribDialog = new SelectAttributeDialog();\n        mAffectedAttribute = _sender;\n        mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);\n        mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected);\n        mAttribDialog->setVisible(true);\n    }\n\n    void CreateClassDialog::onAttributeSelected()\n    {\n        ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId();\n        if (mAffectedAttribute == mFavoriteAttribute0)\n        {\n            if (mFavoriteAttribute1->getAttributeId() == id)\n                mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId());\n        }\n        else if (mAffectedAttribute == mFavoriteAttribute1)\n        {\n            if (mFavoriteAttribute0->getAttributeId() == id)\n                mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId());\n        }\n        mAffectedAttribute->setAttributeId(id);\n        MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog);\n        mAttribDialog = nullptr;\n\n        update();\n    }\n\n    void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender)\n    {\n        delete mSkillDialog;\n        mSkillDialog = new SelectSkillDialog();\n        mAffectedSkill = _sender;\n        mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);\n        mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected);\n        mSkillDialog->setVisible(true);\n    }\n\n    void CreateClassDialog::onSkillSelected()\n    {\n        ESM::Skill::SkillEnum id = mSkillDialog->getSkillId();\n\n        // Avoid duplicate skills by swapping any skill field that matches the selected one\n        for (Widgets::MWSkillPtr& skill : mSkills)\n        {\n            if (skill == mAffectedSkill)\n                continue;\n            if (skill->getSkillId() == id)\n            {\n                skill->setSkillId(mAffectedSkill->getSkillId());\n                break;\n            }\n        }\n\n        mAffectedSkill->setSkillId(mSkillDialog->getSkillId());\n        MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog);\n        mSkillDialog = nullptr;\n        update();\n    }\n\n    void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender)\n    {\n        mDescDialog = new DescriptionDialog();\n        mDescDialog->setTextInput(mDescription);\n        mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered);\n        mDescDialog->setVisible(true);\n    }\n\n    void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow)\n    {\n        mDescription = mDescDialog->getTextInput();\n        MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog);\n        mDescDialog = nullptr;\n    }\n\n    void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender)\n    {\n        if(getName().size() <= 0)\n            return;\n        eventDone(this);\n    }\n\n    void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender)\n    {\n        eventBack();\n    }\n\n    /* SelectSpecializationDialog */\n\n    SelectSpecializationDialog::SelectSpecializationDialog()\n      : WindowModal(\"openmw_chargen_select_specialization.layout\")\n    {\n        // Centre dialog\n        center();\n\n        getWidget(mSpecialization0, \"Specialization0\");\n        getWidget(mSpecialization1, \"Specialization1\");\n        getWidget(mSpecialization2, \"Specialization2\");\n        std::string combat = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Combat], \"\");\n        std::string magic = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Magic], \"\");\n        std::string stealth = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Stealth], \"\");\n\n        mSpecialization0->setCaption(combat);\n        mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);\n        mSpecialization1->setCaption(magic);\n        mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);\n        mSpecialization2->setCaption(stealth);\n        mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);\n        mSpecializationId = ESM::Class::Combat;\n\n        ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat);\n        ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic);\n        ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth);\n\n        MyGUI::Button* cancelButton;\n        getWidget(cancelButton, \"CancelButton\");\n        cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked);\n    }\n\n    SelectSpecializationDialog::~SelectSpecializationDialog()\n    {\n    }\n\n    // widget controls\n\n    void SelectSpecializationDialog::onSpecializationClicked(MyGUI::Widget* _sender)\n    {\n        if (_sender == mSpecialization0)\n            mSpecializationId = ESM::Class::Combat;\n        else if (_sender == mSpecialization1)\n            mSpecializationId = ESM::Class::Magic;\n        else if (_sender == mSpecialization2)\n            mSpecializationId = ESM::Class::Stealth;\n        else\n            return;\n\n        eventItemSelected();\n    }\n\n    void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender)\n    {\n        exit();\n    }\n\n    bool SelectSpecializationDialog::exit()\n    {\n        eventCancel();\n        return true;\n    }\n\n    /* SelectAttributeDialog */\n\n    SelectAttributeDialog::SelectAttributeDialog()\n      : WindowModal(\"openmw_chargen_select_attribute.layout\")\n      , mAttributeId(ESM::Attribute::Strength)\n    {\n        // Centre dialog\n        center();\n\n        for (int i = 0; i < 8; ++i)\n        {\n            Widgets::MWAttributePtr attribute;\n            char theIndex = '0'+i;\n\n            getWidget(attribute,  std::string(\"Attribute\").append(1, theIndex));\n            attribute->setAttributeId(ESM::Attribute::sAttributeIds[i]);\n            attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked);\n            ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId());\n        }\n\n        MyGUI::Button* cancelButton;\n        getWidget(cancelButton, \"CancelButton\");\n        cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked);\n    }\n\n    SelectAttributeDialog::~SelectAttributeDialog()\n    {\n    }\n\n    // widget controls\n\n    void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender)\n    {\n        // TODO: Change MWAttribute to set and get AttributeID enum instead of int\n        mAttributeId = static_cast<ESM::Attribute::AttributeID>(_sender->getAttributeId());\n        eventItemSelected();\n    }\n\n    void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender)\n    {\n        exit();\n    }\n\n    bool SelectAttributeDialog::exit()\n    {\n        eventCancel();\n        return true;\n    }\n\n\n    /* SelectSkillDialog */\n\n    SelectSkillDialog::SelectSkillDialog()\n      : WindowModal(\"openmw_chargen_select_skill.layout\")\n      , mSkillId(ESM::Skill::Block)\n    {\n        // Centre dialog\n        center();\n\n        for(int i = 0; i < 9; i++)\n        {\n            char theIndex = '0'+i;\n            getWidget(mCombatSkill[i],  std::string(\"CombatSkill\").append(1, theIndex));\n            getWidget(mMagicSkill[i],   std::string(\"MagicSkill\").append(1, theIndex));\n            getWidget(mStealthSkill[i], std::string(\"StealthSkill\").append(1, theIndex));\n        }\n\n        struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = {\n            {\n                {mCombatSkill[0], ESM::Skill::Block},\n                {mCombatSkill[1], ESM::Skill::Armorer},\n                {mCombatSkill[2], ESM::Skill::MediumArmor},\n                {mCombatSkill[3], ESM::Skill::HeavyArmor},\n                {mCombatSkill[4], ESM::Skill::BluntWeapon},\n                {mCombatSkill[5], ESM::Skill::LongBlade},\n                {mCombatSkill[6], ESM::Skill::Axe},\n                {mCombatSkill[7], ESM::Skill::Spear},\n                {mCombatSkill[8], ESM::Skill::Athletics}\n            },\n            {\n                {mMagicSkill[0], ESM::Skill::Enchant},\n                {mMagicSkill[1], ESM::Skill::Destruction},\n                {mMagicSkill[2], ESM::Skill::Alteration},\n                {mMagicSkill[3], ESM::Skill::Illusion},\n                {mMagicSkill[4], ESM::Skill::Conjuration},\n                {mMagicSkill[5], ESM::Skill::Mysticism},\n                {mMagicSkill[6], ESM::Skill::Restoration},\n                {mMagicSkill[7], ESM::Skill::Alchemy},\n                {mMagicSkill[8], ESM::Skill::Unarmored}\n            },\n            {\n                {mStealthSkill[0], ESM::Skill::Security},\n                {mStealthSkill[1], ESM::Skill::Sneak},\n                {mStealthSkill[2], ESM::Skill::Acrobatics},\n                {mStealthSkill[3], ESM::Skill::LightArmor},\n                {mStealthSkill[4], ESM::Skill::ShortBlade},\n                {mStealthSkill[5] ,ESM::Skill::Marksman},\n                {mStealthSkill[6] ,ESM::Skill::Mercantile},\n                {mStealthSkill[7] ,ESM::Skill::Speechcraft},\n                {mStealthSkill[8] ,ESM::Skill::HandToHand}\n            }\n        };\n\n        for (int spec = 0; spec < 3; ++spec)\n        {\n            for (int i = 0; i < 9; ++i)\n            {\n                mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId);\n                mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked);\n                ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId());\n            }\n        }\n\n        MyGUI::Button* cancelButton;\n        getWidget(cancelButton, \"CancelButton\");\n        cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked);\n    }\n\n    SelectSkillDialog::~SelectSkillDialog()\n    {\n    }\n\n    // widget controls\n\n    void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender)\n    {\n        mSkillId = _sender->getSkillId();\n        eventItemSelected();\n    }\n\n    void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender)\n    {\n        exit();\n    }\n\n    bool SelectSkillDialog::exit()\n    {\n        eventCancel();\n        return true;\n    }\n\n    /* DescriptionDialog */\n\n    DescriptionDialog::DescriptionDialog()\n      : WindowModal(\"openmw_chargen_class_description.layout\")\n    {\n        // Centre dialog\n        center();\n\n        getWidget(mTextEdit, \"TextEdit\");\n\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked);\n        okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sInputMenu1\", \"\"));\n\n        // Make sure the edit box has focus\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);\n    }\n\n    DescriptionDialog::~DescriptionDialog()\n    {\n    }\n\n    // widget controls\n\n    void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender)\n    {\n        eventDone(this);\n    }\n\n    void setClassImage(MyGUI::ImageBox* imageBox, const std::string &classId)\n    {\n        std::string classImage = std::string(\"textures\\\\levelup\\\\\") + classId + \".dds\";\n        if (!MWBase::Environment::get().getWindowManager()->textureExists(classImage))\n        {\n            Log(Debug::Warning) << \"No class image for \" << classId << \", falling back to default\";\n            classImage = \"textures\\\\levelup\\\\warrior.dds\";\n        }\n        imageBox->setImageTexture(classImage);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/class.hpp",
    "content": "#ifndef MWGUI_CLASS_H\n#define MWGUI_CLASS_H\n\n#include <components/esm/attr.hpp>\n#include <components/esm/loadclas.hpp>\n#include \"widgets.hpp\"\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n    void setClassImage(MyGUI::ImageBox* imageBox, const std::string& classId);\n\n    class InfoBoxDialog : public WindowModal\n    {\n    public:\n        InfoBoxDialog();\n\n        typedef std::vector<std::string> ButtonList;\n\n        void setText(const std::string &str);\n        std::string getText() const;\n        void setButtons(ButtonList &buttons);\n\n        void onOpen() override;\n\n        bool exit() override { return false; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;\n\n        /** Event : Button was clicked.\\n\n            signature : void method(int index)\\n\n        */\n        EventHandle_Int eventButtonSelected;\n\n    protected:\n        void onButtonClicked(MyGUI::Widget* _sender);\n\n    private:\n\n        void fitToText(MyGUI::TextBox* widget);\n        void layoutVertically(MyGUI::Widget* widget, int margin);\n        MyGUI::Widget* mTextBox;\n        MyGUI::TextBox* mText;\n        MyGUI::Widget* mButtonBar;\n        std::vector<MyGUI::Button*> mButtons;\n    };\n\n    // Lets the player choose between 3 ways of creating a class\n    class ClassChoiceDialog : public InfoBoxDialog\n    {\n    public:\n        // Corresponds to the buttons that can be clicked\n        enum ClassChoice\n        {\n            Class_Generate = 0,\n            Class_Pick = 1,\n            Class_Create = 2,\n            Class_Back = 3\n        };\n        ClassChoiceDialog();\n    };\n\n    class GenerateClassResultDialog : public WindowModal\n    {\n    public:\n        GenerateClassResultDialog();\n\n        std::string getClassId() const;\n        void setClassId(const std::string &classId);\n\n        bool exit() override { return false; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        /** Event : Back button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventBack;\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n    protected:\n        void onOkClicked(MyGUI::Widget* _sender);\n        void onBackClicked(MyGUI::Widget* _sender);\n\n    private:\n        MyGUI::ImageBox* mClassImage;\n        MyGUI::TextBox*  mClassName;\n\n        std::string mCurrentClassId;\n    };\n\n    class PickClassDialog : public WindowModal\n    {\n    public:\n        PickClassDialog();\n\n        const std::string &getClassId() const { return mCurrentClassId; }\n        void setClassId(const std::string &classId);\n\n        void setNextButtonShow(bool shown);\n        void onOpen() override;\n\n        bool exit() override { return false; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        /** Event : Back button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventBack;\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n    protected:\n        void onSelectClass(MyGUI::ListBox* _sender, size_t _index);\n        void onAccept(MyGUI::ListBox* _sender, size_t _index);\n\n        void onOkClicked(MyGUI::Widget* _sender);\n        void onBackClicked(MyGUI::Widget* _sender);\n\n    private:\n        void updateClasses();\n        void updateStats();\n\n        MyGUI::ImageBox* mClassImage;\n        MyGUI::ListBox*  mClassList;\n        MyGUI::TextBox*  mSpecializationName;\n        Widgets::MWAttributePtr mFavoriteAttribute[2];\n        Widgets::MWSkillPtr   mMajorSkill[5];\n        Widgets::MWSkillPtr   mMinorSkill[5];\n\n        std::string mCurrentClassId;\n    };\n\n    class SelectSpecializationDialog : public WindowModal\n    {\n    public:\n        SelectSpecializationDialog();\n        ~SelectSpecializationDialog();\n\n        bool exit() override;\n\n        ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        /** Event : Cancel button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventCancel;\n\n        /** Event : Dialog finished, specialization selected.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventItemSelected;\n\n    protected:\n        void onSpecializationClicked(MyGUI::Widget* _sender);\n        void onCancelClicked(MyGUI::Widget* _sender);\n\n    private:\n        MyGUI::TextBox *mSpecialization0, *mSpecialization1, *mSpecialization2;\n\n        ESM::Class::Specialization mSpecializationId;\n    };\n\n    class SelectAttributeDialog : public WindowModal\n    {\n    public:\n        SelectAttributeDialog();\n        ~SelectAttributeDialog();\n\n        bool exit() override;\n\n        ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        /** Event : Cancel button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventCancel;\n\n        /** Event : Dialog finished, attribute selected.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventItemSelected;\n\n    protected:\n        void onAttributeClicked(Widgets::MWAttributePtr _sender);\n        void onCancelClicked(MyGUI::Widget* _sender);\n\n    private:\n        ESM::Attribute::AttributeID mAttributeId;\n    };\n\n    class SelectSkillDialog : public WindowModal\n    {\n    public:\n        SelectSkillDialog();\n        ~SelectSkillDialog();\n\n        bool exit() override;\n\n        ESM::Skill::SkillEnum getSkillId() const { return mSkillId; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        /** Event : Cancel button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventCancel;\n\n        /** Event : Dialog finished, skill selected.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventItemSelected;\n\n    protected:\n        void onSkillClicked(Widgets::MWSkillPtr _sender);\n        void onCancelClicked(MyGUI::Widget* _sender);\n\n    private:\n        Widgets::MWSkillPtr mCombatSkill[9];\n        Widgets::MWSkillPtr mMagicSkill[9];\n        Widgets::MWSkillPtr mStealthSkill[9];\n\n        ESM::Skill::SkillEnum mSkillId;\n    };\n\n    class DescriptionDialog : public WindowModal\n    {\n    public:\n        DescriptionDialog();\n        ~DescriptionDialog();\n\n        std::string getTextInput() const { return mTextEdit->getCaption(); }\n        void setTextInput(const std::string &text) { mTextEdit->setCaption(text); }\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n    protected:\n        void onOkClicked(MyGUI::Widget* _sender);\n\n    private:\n        MyGUI::EditBox* mTextEdit;\n    };\n\n    class CreateClassDialog : public WindowModal\n    {\n    public:\n        CreateClassDialog();\n        virtual ~CreateClassDialog();\n\n        bool exit() override { return false; }\n\n        std::string getName() const;\n        std::string getDescription() const;\n        ESM::Class::Specialization getSpecializationId() const;\n        std::vector<int> getFavoriteAttributes() const;\n        std::vector<ESM::Skill::SkillEnum> getMajorSkills() const;\n        std::vector<ESM::Skill::SkillEnum> getMinorSkills() const;\n\n        void setNextButtonShow(bool shown);\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        /** Event : Back button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventBack;\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n    protected:\n        void onOkClicked(MyGUI::Widget* _sender);\n        void onBackClicked(MyGUI::Widget* _sender);\n\n        void onSpecializationClicked(MyGUI::Widget* _sender);\n        void onSpecializationSelected();\n        void onAttributeClicked(Widgets::MWAttributePtr _sender);\n        void onAttributeSelected();\n        void onSkillClicked(Widgets::MWSkillPtr _sender);\n        void onSkillSelected();\n        void onDescriptionClicked(MyGUI::Widget* _sender);\n        void onDescriptionEntered(WindowBase* parWindow);\n        void onDialogCancel();\n\n        void setSpecialization(int id);\n\n        void update();\n\n    private:\n        MyGUI::EditBox*                   mEditName;\n        MyGUI::TextBox*                  mSpecializationName;\n        Widgets::MWAttributePtr          mFavoriteAttribute0, mFavoriteAttribute1;\n        Widgets::MWSkillPtr              mMajorSkill[5];\n        Widgets::MWSkillPtr              mMinorSkill[5];\n        std::vector<Widgets::MWSkillPtr> mSkills;\n        std::string                      mDescription;\n\n        SelectSpecializationDialog       *mSpecDialog;\n        SelectAttributeDialog            *mAttribDialog;\n        SelectSkillDialog                *mSkillDialog;\n        DescriptionDialog                *mDescDialog;\n\n        ESM::Class::Specialization       mSpecializationId;\n\n        Widgets::MWAttributePtr              mAffectedAttribute;\n        Widgets::MWSkillPtr              mAffectedSkill;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/companionitemmodel.cpp",
    "content": "#include \"companionitemmodel.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\nnamespace\n{\n\n    void modifyProfit(const MWWorld::Ptr& actor, int diff)\n    {\n        std::string script = actor.getClass().getScript(actor);\n        if (!script.empty())\n        {\n            int profit = actor.getRefData().getLocals().getIntVar(script, \"minimumprofit\");\n            profit += diff;\n            actor.getRefData().getLocals().setVarByInt(script, \"minimumprofit\", profit);\n        }\n    }\n\n}\n\nnamespace MWGui\n{\n    CompanionItemModel::CompanionItemModel(const MWWorld::Ptr &actor)\n        : InventoryItemModel(actor)\n    {\n    }\n\n    MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)\n    {\n        if (hasProfit(mActor))\n            modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count);\n\n        return InventoryItemModel::copyItem(item, count, allowAutoEquip);\n    }\n\n    void CompanionItemModel::removeItem (const ItemStack& item, size_t count)\n    {\n        if (hasProfit(mActor))\n            modifyProfit(mActor, -item.mBase.getClass().getValue(item.mBase) * count);\n\n        InventoryItemModel::removeItem(item, count);\n    }\n\n    bool CompanionItemModel::hasProfit(const MWWorld::Ptr &actor)\n    {\n        std::string script = actor.getClass().getScript(actor);\n        if (script.empty())\n            return false;\n        return actor.getRefData().getLocals().hasVar(script, \"minimumprofit\");\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/companionitemmodel.hpp",
    "content": "#ifndef MWGUI_COMPANION_ITEM_MODEL_H\n#define MWGUI_COMPANION_ITEM_MODEL_H\n\n#include \"inventoryitemmodel.hpp\"\n\nnamespace MWGui\n{\n\n    /// @brief The companion item model keeps track of the companion's profit by\n    /// monitoring which items are being added to and removed from the model.\n    class CompanionItemModel : public InventoryItemModel\n    {\n    public:\n        CompanionItemModel (const MWWorld::Ptr& actor);\n\n        MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true) override;\n        void removeItem (const ItemStack& item, size_t count) override;\n\n        bool hasProfit(const MWWorld::Ptr& actor);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/companionwindow.cpp",
    "content": "#include \"companionwindow.hpp\"\n\n#include <cmath>\n\n#include <MyGUI_InputManager.h>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"messagebox.hpp\"\n#include \"itemview.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n#include \"companionitemmodel.hpp\"\n#include \"draganddrop.hpp\"\n#include \"countdialog.hpp\"\n#include \"widgets.hpp\"\n#include \"tooltips.hpp\"\n\nnamespace\n{\n\n    int getProfit(const MWWorld::Ptr& actor)\n    {\n        std::string script = actor.getClass().getScript(actor);\n        if (!script.empty())\n        {\n            return actor.getRefData().getLocals().getIntVar(script, \"minimumprofit\");\n        }\n        return 0;\n    }\n\n}\n\nnamespace MWGui\n{\n\nCompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* manager)\n    : WindowBase(\"openmw_companion_window.layout\")\n    , mSortModel(nullptr)\n    , mModel(nullptr)\n    , mSelectedItem(-1)\n    , mDragAndDrop(dragAndDrop)\n    , mMessageBoxManager(manager)\n{\n    getWidget(mCloseButton, \"CloseButton\");\n    getWidget(mProfitLabel, \"ProfitLabel\");\n    getWidget(mEncumbranceBar, \"EncumbranceBar\");\n    getWidget(mFilterEdit, \"FilterEdit\");\n    getWidget(mItemView, \"ItemView\");\n    mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &CompanionWindow::onBackgroundSelected);\n    mItemView->eventItemClicked += MyGUI::newDelegate(this, &CompanionWindow::onItemSelected);\n    mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &CompanionWindow::onNameFilterChanged);\n\n    mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CompanionWindow::onCloseButtonClicked);\n\n    setCoord(200,0,600,300);\n}\n\nvoid CompanionWindow::onItemSelected(int index)\n{\n    if (mDragAndDrop->mIsOnDragAndDrop)\n    {\n        mDragAndDrop->drop(mModel, mItemView);\n        updateEncumbranceBar();\n        return;\n    }\n\n    const ItemStack& item = mSortModel->getItem(index);\n\n    // We can't take conjured items from a companion NPC\n    if (item.mFlags & ItemStack::Flag_Bound)\n    {\n        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sBarterDialog12}\");\n        return;\n    }\n\n    MWWorld::Ptr object = item.mBase;\n    int count = item.mCount;\n    bool shift = MyGUI::InputManager::getInstance().isShiftPressed();\n    if (MyGUI::InputManager::getInstance().isControlPressed())\n        count = 1;\n\n    mSelectedItem = mSortModel->mapToSource(index);\n\n    if (count > 1 && !shift)\n    {\n        CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();\n        std::string name = object.getClass().getName(object) + MWGui::ToolTips::getSoulString(object.getCellRef());\n        dialog->openCountDialog(name, \"#{sTake}\", count);\n        dialog->eventOkClicked.clear();\n        dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem);\n    }\n    else\n        dragItem (nullptr, count);\n}\n\nvoid CompanionWindow::onNameFilterChanged(MyGUI::EditBox* _sender)\n    {\n        mSortModel->setNameFilter(_sender->getCaption());\n        mItemView->update();\n    }\n\nvoid CompanionWindow::dragItem(MyGUI::Widget* sender, int count)\n{\n    mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);\n}\n\nvoid CompanionWindow::onBackgroundSelected()\n{\n    if (mDragAndDrop->mIsOnDragAndDrop)\n    {\n        mDragAndDrop->drop(mModel, mItemView);\n        updateEncumbranceBar();\n    }\n}\n\nvoid CompanionWindow::setPtr(const MWWorld::Ptr& npc)\n{\n    mPtr = npc;\n    updateEncumbranceBar();\n\n    mModel = new CompanionItemModel(npc);\n    mSortModel = new SortFilterItemModel(mModel);\n    mFilterEdit->setCaption(std::string());\n    mItemView->setModel(mSortModel);\n    mItemView->resetScrollBars();\n\n    setTitle(npc.getClass().getName(npc));\n}\n\nvoid CompanionWindow::onFrame(float dt)\n{\n    checkReferenceAvailable();\n    updateEncumbranceBar();\n}\n\nvoid CompanionWindow::updateEncumbranceBar()\n{\n    if (mPtr.isEmpty())\n        return;\n    float capacity = mPtr.getClass().getCapacity(mPtr);\n    float encumbrance = mPtr.getClass().getEncumbrance(mPtr);\n    mEncumbranceBar->setValue(std::ceil(encumbrance), static_cast<int>(capacity));\n\n    if (mModel && mModel->hasProfit(mPtr))\n    {\n        mProfitLabel->setCaptionWithReplacing(\"#{sProfitValue} \" + MyGUI::utility::toString(getProfit(mPtr)));\n    }\n    else\n        mProfitLabel->setCaption(\"\");\n}\n\nvoid CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender)\n{\n    if (exit())\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);\n}\n\nbool CompanionWindow::exit()\n{\n    if (mModel && mModel->hasProfit(mPtr) && getProfit(mPtr) < 0)\n    {\n        std::vector<std::string> buttons;\n        buttons.emplace_back(\"#{sCompanionWarningButtonOne}\");\n        buttons.emplace_back(\"#{sCompanionWarningButtonTwo}\");\n        mMessageBoxManager->createInteractiveMessageBox(\"#{sCompanionWarningMessage}\", buttons);\n        mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked);\n        return false;\n    }\n    return true;\n}\n\nvoid CompanionWindow::onMessageBoxButtonClicked(int button)\n{\n    if (button == 0)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);\n        // Important for Calvus' contract script to work properly\n        MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n    }\n}\n\nvoid CompanionWindow::onReferenceUnavailable()\n{\n    MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);\n}\n\nvoid CompanionWindow::resetReference()\n{\n    ReferenceInterface::resetReference();\n    mItemView->setModel(nullptr);\n    mModel = nullptr;\n    mSortModel = nullptr;\n}\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/companionwindow.hpp",
    "content": "#ifndef OPENMW_MWGUI_COMPANIONWINDOW_H\n#define OPENMW_MWGUI_COMPANIONWINDOW_H\n\n#include \"windowbase.hpp\"\n#include \"referenceinterface.hpp\"\n\nnamespace MWGui\n{\n    namespace Widgets\n    {\n        class MWDynamicStat;\n    }\n\n    class MessageBoxManager;\n    class ItemView;\n    class DragAndDrop;\n    class SortFilterItemModel;\n    class CompanionItemModel;\n\n    class CompanionWindow : public WindowBase, public ReferenceInterface\n    {\n    public:\n        CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager);\n\n        bool exit() override;\n\n        void resetReference() override;\n\n        void setPtr(const MWWorld::Ptr& npc) override;\n        void onFrame (float dt) override;\n        void clear() override { resetReference(); }\n\n    private:\n        ItemView* mItemView;\n        SortFilterItemModel* mSortModel;\n        CompanionItemModel* mModel;\n        int mSelectedItem;\n\n        DragAndDrop* mDragAndDrop;\n\n        MyGUI::Button* mCloseButton;\n        MyGUI::EditBox* mFilterEdit;\n        MyGUI::TextBox* mProfitLabel;\n        Widgets::MWDynamicStat* mEncumbranceBar;\n        MessageBoxManager* mMessageBoxManager;\n\n        void onItemSelected(int index);\n        void onNameFilterChanged(MyGUI::EditBox* _sender);\n        void onBackgroundSelected();\n        void dragItem(MyGUI::Widget* sender, int count);\n\n        void onMessageBoxButtonClicked(int button);\n\n        void updateEncumbranceBar();\n\n        void onCloseButtonClicked(MyGUI::Widget* _sender);\n\n        void onReferenceUnavailable() override;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/confirmationdialog.cpp",
    "content": "#include \"confirmationdialog.hpp\"\n\n#include <MyGUI_Button.h>\n#include <MyGUI_EditBox.h>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\nnamespace MWGui\n{\n    ConfirmationDialog::ConfirmationDialog() :\n        WindowModal(\"openmw_confirmation_dialog.layout\")\n    {\n        getWidget(mMessage, \"Message\");\n        getWidget(mOkButton, \"OkButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onCancelButtonClicked);\n        mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked);\n    }\n\n    void ConfirmationDialog::askForConfirmation(const std::string& message)\n    {\n        setVisible(true);\n\n        mMessage->setCaptionWithReplacing(message);\n\n        int height = mMessage->getTextSize().height + 60;\n\n        int width = mMessage->getTextSize().width + 24;\n\n        mMainWidget->setSize(width, height);\n\n        mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height + 24);\n\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);\n\n        center();\n    }\n\n    bool ConfirmationDialog::exit()\n    {\n        setVisible(false);\n        eventCancelClicked();\n        return true;\n    }\n\n    void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender)\n    {\n        exit();\n    }\n\n    void ConfirmationDialog::onOkButtonClicked(MyGUI::Widget* _sender)\n    {\n        setVisible(false);\n\n        eventOkClicked();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/confirmationdialog.hpp",
    "content": "#ifndef MWGUI_CONFIRMATIONDIALOG_H\n#define MWGUI_CONFIRMATIONDIALOG_H\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n    class ConfirmationDialog : public WindowModal\n    {\n        public:\n            ConfirmationDialog();\n            void askForConfirmation(const std::string& message);\n            bool exit() override;\n\n            typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n            /** Event : Ok button was clicked.\\n\n                signature : void method()\\n\n            */\n            EventHandle_Void eventOkClicked;\n            EventHandle_Void eventCancelClicked;\n\n        private:\n            MyGUI::EditBox* mMessage;\n            MyGUI::Button* mOkButton;\n            MyGUI::Button* mCancelButton;\n\n            void onCancelButtonClicked(MyGUI::Widget* _sender);\n            void onOkButtonClicked(MyGUI::Widget* _sender);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/console.cpp",
    "content": "#include \"console.hpp\"\n\n#include <MyGUI_EditBox.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_LayerManager.h>\n\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/compiler/exception.hpp>\n#include <components/compiler/extensions0.hpp>\n#include <components/compiler/lineparser.hpp>\n#include <components/compiler/scanner.hpp>\n#include <components/compiler/locals.hpp>\n#include <components/interpreter/interpreter.hpp>\n\n#include \"../mwscript/extensions.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/class.hpp\"\n\nnamespace MWGui\n{\n    class ConsoleInterpreterContext : public MWScript::InterpreterContext\n    {\n            Console& mConsole;\n\n        public:\n\n            ConsoleInterpreterContext (Console& console, MWWorld::Ptr reference);\n\n            void report (const std::string& message) override;\n    };\n\n    ConsoleInterpreterContext::ConsoleInterpreterContext (Console& console,\n        MWWorld::Ptr reference)\n    : MWScript::InterpreterContext (\n        reference.isEmpty() ? nullptr : &reference.getRefData().getLocals(), reference),\n      mConsole (console)\n    {}\n\n    void ConsoleInterpreterContext::report (const std::string& message)\n    {\n        mConsole.printOK (message);\n    }\n\n    bool Console::compile (const std::string& cmd, Compiler::Output& output)\n    {\n        try\n        {\n            ErrorHandler::reset();\n\n            std::istringstream input (cmd + '\\n');\n\n            Compiler::Scanner scanner (*this, input, mCompilerContext.getExtensions());\n\n            Compiler::LineParser parser (*this, mCompilerContext, output.getLocals(),\n                output.getLiterals(), output.getCode(), true);\n\n            scanner.scan (parser);\n\n            return isGood();\n        }\n        catch (const Compiler::SourceException&)\n        {\n            // error has already been reported via error handler\n        }\n        catch (const std::exception& error)\n        {\n            printError (std::string (\"Error: \") + error.what());\n        }\n\n        return false;\n    }\n\n    void Console::report (const std::string& message, const Compiler::TokenLoc& loc, Type type)\n    {\n        std::ostringstream error;\n        error << \"column \" << loc.mColumn << \" (\" << loc.mLiteral << \"):\";\n\n        printError (error.str());\n        printError ((type==ErrorMessage ? \"error: \" : \"warning: \") + message);\n    }\n\n    void Console::report (const std::string& message, Type type)\n    {\n        printError ((type==ErrorMessage ? \"error: \" : \"warning: \") + message);\n    }\n\n    void Console::listNames()\n    {\n        if (mNames.empty())\n        {\n            // keywords\n            std::istringstream input (\"\");\n\n            Compiler::Scanner scanner (*this, input, mCompilerContext.getExtensions());\n\n            scanner.listKeywords (mNames);\n\n            // identifier\n            const MWWorld::ESMStore& store =\n                MWBase::Environment::get().getWorld()->getStore();\n\n            for (MWWorld::ESMStore::iterator it = store.begin(); it != store.end(); ++it)\n            {\n                it->second->listIdentifier (mNames);\n            }\n\n            // exterior cell names aren't technically identifiers, but since the COC function accepts them,\n            // we should list them too\n            for (MWWorld::Store<ESM::Cell>::iterator it = store.get<ESM::Cell>().extBegin();\n                 it != store.get<ESM::Cell>().extEnd(); ++it)\n            {\n                if (!it->mName.empty())\n                    mNames.push_back(it->mName);\n            }\n\n            // sort\n            std::sort (mNames.begin(), mNames.end());\n\n            // remove duplicates\n            mNames.erase( std::unique( mNames.begin(), mNames.end() ), mNames.end() );\n        }\n    }\n\n    Console::Console(int w, int h, bool consoleOnlyScripts)\n      : WindowBase(\"openmw_console.layout\"),\n        mCompilerContext (MWScript::CompilerContext::Type_Console),\n        mConsoleOnlyScripts (consoleOnlyScripts)\n    {\n        setCoord(10,10, w-10, h/2);\n\n        getWidget(mCommandLine, \"edit_Command\");\n        getWidget(mHistory, \"list_History\");\n\n        // Set up the command line box\n        mCommandLine->eventEditSelectAccept +=\n            newDelegate(this, &Console::acceptCommand);\n        mCommandLine->eventKeyButtonPressed +=\n            newDelegate(this, &Console::keyPress);\n\n        // Set up the log window\n        mHistory->setOverflowToTheLeft(true);\n\n        // compiler\n        Compiler::registerExtensions (mExtensions, mConsoleOnlyScripts);\n        mCompilerContext.setExtensions (&mExtensions);\n    }\n\n    void Console::onOpen()\n    {\n        // Give keyboard focus to the combo box whenever the console is\n        // turned on and place it over other widgets\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine);\n        MyGUI::LayerManager::getInstance().upLayerItem(mMainWidget);\n    }\n\n    void Console::print(const std::string &msg, const std::string& color)\n    {\n        mHistory->addText(color + MyGUI::TextIterator::toTagsString(msg));\n    }\n\n    void Console::printOK(const std::string &msg)\n    {\n        print(msg + \"\\n\", \"#FF00FF\");\n    }\n\n    void Console::printError(const std::string &msg)\n    {\n        print(msg + \"\\n\", \"#FF2222\");\n    }\n\n    void Console::execute (const std::string& command)\n    {\n        // Log the command\n        print(\"> \" + command + \"\\n\");\n\n        Compiler::Locals locals;\n        if (!mPtr.isEmpty())\n        {\n            std::string script = mPtr.getClass().getScript(mPtr);\n            if (!script.empty())\n                locals = MWBase::Environment::get().getScriptManager()->getLocals(script);\n        }\n        Compiler::Output output (locals);\n\n        if (compile (command + \"\\n\", output))\n        {\n            try\n            {\n                ConsoleInterpreterContext interpreterContext (*this, mPtr);\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_CONSOLE_COMMAND packet to the server with the\n                    command and target used\n\n                    Mark this InterpreterContext as having a CONSOLE context,\n                    so that packets sent by the Interpreter can have their\n                    origin determined by serverside scripts\n                */\n                interpreterContext.trackContextType(Interpreter::Context::CONSOLE);\n\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_CONSOLE;\n                objectList->consoleCommand = command;\n                \n                if (mPtr.isEmpty())\n                    objectList->cell = mwmp::Main::get().getLocalPlayer()->cell;\n                else\n                {\n                    objectList->addObjectGeneric(mPtr);\n                }\n\n                objectList->sendConsoleCommand();\n                /*\n                    End of tes3mp addition\n                */\n\n                Interpreter::Interpreter interpreter;\n                MWScript::installOpcodes (interpreter, mConsoleOnlyScripts);\n                std::vector<Interpreter::Type_Code> code;\n                output.getCode (code);\n                interpreter.run (&code[0], code.size(), interpreterContext);\n            }\n            catch (const std::exception& error)\n            {\n                printError (std::string (\"Error: \") + error.what());\n            }\n        }\n    }\n\n    void Console::executeFile (const std::string& path)\n    {\n        namespace bfs = boost::filesystem;\n        bfs::ifstream stream ((bfs::path(path)));\n\n        if (!stream.is_open())\n            printError (\"failed to open file: \" + path);\n        else\n        {\n            std::string line;\n\n            while (std::getline (stream, line))\n                execute (line);\n        }\n    }\n\n    void Console::clear()\n    {\n        resetReference();\n    }\n\n    bool isWhitespace(char c)\n    {\n        return c == ' ' || c == '\\t';\n    }\n\n    void Console::keyPress(MyGUI::Widget* _sender,\n                  MyGUI::KeyCode key,\n                  MyGUI::Char _char)\n    {\n        if(MyGUI::InputManager::getInstance().isControlPressed())\n        {\n            if(key == MyGUI::KeyCode::W)\n            {\n                const auto& caption = mCommandLine->getCaption();\n                if(caption.empty())\n                    return;\n                size_t max = mCommandLine->getTextCursor();\n                while(max > 0 && (isWhitespace(caption[max - 1]) || caption[max - 1] == '>'))\n                    max--;\n                while(max > 0 && !isWhitespace(caption[max - 1]) && caption[max - 1] != '>')\n                    max--;\n                size_t length = mCommandLine->getTextCursor() - max;\n                if(length > 0)\n                {\n                    auto text = caption;\n                    text.erase(max, length);\n                    mCommandLine->setCaption(text);\n                    mCommandLine->setTextCursor(max);\n                }\n            }\n            else if(key == MyGUI::KeyCode::U)\n            {\n                if(mCommandLine->getTextCursor() > 0)\n                {\n                    auto text = mCommandLine->getCaption();\n                    text.erase(0, mCommandLine->getTextCursor());\n                    mCommandLine->setCaption(text);\n                    mCommandLine->setTextCursor(0);\n                }\n            }\n        }\n        else if(key == MyGUI::KeyCode::Tab)\n        {\n            std::vector<std::string> matches;\n            listNames();\n            std::string oldCaption = mCommandLine->getCaption();\n            std::string newCaption = complete( mCommandLine->getOnlyText(), matches );\n            mCommandLine->setCaption(newCaption);\n\n            // List candidates if repeatedly pressing tab\n            if (oldCaption == newCaption && !matches.empty())\n            {\n                int i = 0;\n                printOK(\"\");\n                for(std::string& match : matches)\n                {\n                    if(i == 50)\n                        break;\n\n                    printOK(match);\n                    i++;\n                }\n            }\n        }\n\n        if(mCommandHistory.empty()) return;\n\n        // Traverse history with up and down arrows\n        if(key == MyGUI::KeyCode::ArrowUp)\n        {\n            // If the user was editing a string, store it for later\n            if(mCurrent == mCommandHistory.end())\n                mEditString = mCommandLine->getOnlyText();\n\n            if(mCurrent != mCommandHistory.begin())\n            {\n                --mCurrent;\n                mCommandLine->setCaption(*mCurrent);\n            }\n        }\n        else if(key == MyGUI::KeyCode::ArrowDown)\n        {\n            if(mCurrent != mCommandHistory.end())\n            {\n                ++mCurrent;\n\n                if(mCurrent != mCommandHistory.end())\n                    mCommandLine->setCaption(*mCurrent);\n                else\n                    // Restore the edit string\n                    mCommandLine->setCaption(mEditString);\n            }\n        }\n    }\n\n    void Console::acceptCommand(MyGUI::EditBox* _sender)\n    {\n        const std::string &cm = mCommandLine->getOnlyText();\n        if(cm.empty()) return;\n\n        // Add the command to the history, and set the current pointer to\n        // the end of the list\n        if (mCommandHistory.empty() || mCommandHistory.back() != cm)\n            mCommandHistory.push_back(cm);\n        mCurrent = mCommandHistory.end();\n        mEditString.clear();\n\n        // Reset the command line before the command execution.\n        // It prevents the re-triggering of the acceptCommand() event for the same command \n        // during the actual command execution\n        mCommandLine->setCaption(\"\");\n\n        execute (cm);\n    }\n\n    std::string Console::complete( std::string input, std::vector<std::string> &matches )\n    {\n        std::string output = input;\n        std::string tmp = input;\n        bool has_front_quote = false;\n\n        /* Does the input string contain things that don't have to be completed? If yes erase them. */\n\n        /* Erase a possible call to an explicit reference. */\n        size_t explicitPos = tmp.find(\"->\");\n        if (explicitPos != std::string::npos)\n        {\n            tmp.erase(0, explicitPos+2);\n        }\n\n        /* Are there quotation marks? */\n        if( tmp.find('\"') != std::string::npos ) {\n            int numquotes=0;\n            for(std::string::iterator it=tmp.begin(); it < tmp.end(); ++it) {\n                if( *it == '\"' )\n                    numquotes++;\n            }\n\n            /* Is it terminated?*/\n            if( numquotes % 2 ) {\n                tmp.erase( 0, tmp.rfind('\"')+1 );\n                has_front_quote = true;\n            }\n            else {\n                size_t pos;\n                if( ( ((pos = tmp.rfind(' ')) != std::string::npos ) ) && ( pos > tmp.rfind('\"') ) ) {\n                    tmp.erase( 0, tmp.rfind(' ')+1);\n                }\n                else {\n                    tmp.clear();\n                }\n                has_front_quote = false;\n            }\n        }\n        /* No quotation marks. Are there spaces?*/\n        else {\n            size_t rpos;\n            if( (rpos=tmp.rfind(' ')) != std::string::npos ) {\n                if( rpos == 0 ) {\n                    tmp.clear();\n                }\n                else {\n                    tmp.erase(0, rpos+1);\n                }\n            }\n        }\n\n        /* Erase the input from the output string so we can easily append the completed form later. */\n        output.erase(output.end()-tmp.length(), output.end());\n\n        /* Is there still something in the input string? If not just display all commands and return the unchanged input. */\n        if( tmp.length() == 0 ) {\n                matches=mNames;\n            return input;\n        }\n\n        /* Iterate through the vector. */\n        for(std::string& name : mNames)\n        {\n            bool string_different=false;\n\n            /* Is the string shorter than the input string? If yes skip it. */\n            if(name.length() < tmp.length())\n                continue;\n\n            /* Is the beginning of the string different from the input string? If yes skip it. */\n            for( std::string::iterator iter=tmp.begin(), iter2=name.begin(); iter < tmp.end();++iter, ++iter2) {\n                if( Misc::StringUtils::toLower(*iter) != Misc::StringUtils::toLower(*iter2) ) {\n                    string_different=true;\n                    break;\n                }\n            }\n\n            if( string_different )\n                continue;\n\n            /* The beginning of the string matches the input string, save it for the next test. */\n            matches.push_back(name);\n        }\n\n        /* There are no matches. Return the unchanged input. */\n        if( matches.empty() )\n        {\n            return input;\n        }\n\n        /* Only one match. We're done. */\n        if( matches.size() == 1 ) {\n            /* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/\n            if( ( matches.front().find(' ') != std::string::npos )  ) {\n                if( !has_front_quote )\n                    output.append(std::string(\"\\\"\"));\n                return output.append(matches.front() + std::string(\"\\\" \"));\n            }\n            else if( has_front_quote ) {\n                return  output.append(matches.front() + std::string(\"\\\" \"));\n            }\n            else {\n                return output.append(matches.front() + std::string(\" \"));\n            }\n        }\n\n        /* Check if all matching strings match further than input. If yes complete to this match. */\n        int i = tmp.length();\n\n        for(std::string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); ++iter, ++i)\n        {\n            for(std::string& match : matches)\n            {\n                if(Misc::StringUtils::toLower(match[i]) != Misc::StringUtils::toLower(*iter))\n                {\n                    /* Append the longest match to the end of the output string*/\n                    output.append(matches.front().substr(0, i));\n                    return output;\n                }\n            }\n        }\n\n        /* All keywords match with the shortest. Append it to the output string and return it. */\n        return output.append(matches.front());\n    }\n\n    void Console::onResChange(int width, int height)\n    {\n        setCoord(10,10, width-10, height/2);\n    }\n\n    void Console::updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)\n    {\n        if (mPtr == currentPtr)\n            mPtr = newPtr;\n    }\n\n    void Console::setSelectedObject(const MWWorld::Ptr& object)\n    {\n        if (!object.isEmpty())\n        {\n            if (object == mPtr)\n            {\n                setTitle(\"#{sConsoleTitle}\");\n                mPtr=MWWorld::Ptr();\n            }\n            else\n            {\n                /*\n                    Start of tes3mp change (major)\n\n                    Display the selected object's refNum and mpNum alongside its refId in the\n                    title of the console window, for easier debugging of almost everything\n                */\n                setTitle(\"#{sConsoleTitle} (\" + object.getCellRef().getRefId() + \", \" +\n                    std::to_string(object.getCellRef().getRefNum().mIndex) + \"-\" +\n                    std::to_string(object.getCellRef().getMpNum()) + \")\");\n                /*\n                    End of tes3mp change (major)\n                */\n                mPtr = object;\n            }\n            // User clicked on an object. Restore focus to the console command line.\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine);\n        }\n        else\n        {\n            setTitle(\"#{sConsoleTitle}\");\n            mPtr = MWWorld::Ptr();\n        }\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the direct setting of a console's Ptr, without the assumption that an object\n        was clicked and that key focus should be restored to the console window, for console\n        commands executed via server scripts\n    */\n    void Console::setPtr(const MWWorld::Ptr& object)\n    {\n        mPtr = object;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void Console::onReferenceUnavailable()\n    {\n        setSelectedObject(MWWorld::Ptr());\n    }\n\n    void Console::resetReference()\n    {\n        ReferenceInterface::resetReference();\n        setSelectedObject(MWWorld::Ptr());\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/console.hpp",
    "content": "#ifndef MWGUI_CONSOLE_H\n#define MWGUI_CONSOLE_H\n\n#include <list>\n#include <string>\n#include <vector>\n\n#include <components/compiler/errorhandler.hpp>\n#include <components/compiler/output.hpp>\n#include <components/compiler/extensions.hpp>\n\n#include \"../mwscript/compilercontext.hpp\"\n#include \"../mwscript/interpretercontext.hpp\"\n\n#include \"referenceinterface.hpp\"\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n    class Console : public WindowBase, private Compiler::ErrorHandler, public ReferenceInterface\n    {\n        public:\n            /// Set the implicit object for script execution\n            void setSelectedObject(const MWWorld::Ptr& object);\n\n            /*\n                Start of tes3mp addition\n\n                Allow the direct setting of a console's Ptr, without the assumption that an object\n                was clicked and that key focus should be restored to the console window, for console\n                commands executed via server scripts\n            */\n            void setPtr(const MWWorld::Ptr& object);\n            /*\n                End of tes3mp addition\n            */\n\n            MyGUI::EditBox* mCommandLine;\n            MyGUI::EditBox* mHistory;\n\n            typedef std::list<std::string> StringList;\n\n            // History of previous entered commands\n            StringList mCommandHistory;\n            StringList::iterator mCurrent;\n            std::string mEditString;\n\n            Console(int w, int h, bool consoleOnlyScripts);\n\n            void onOpen() override;\n\n            void onResChange(int width, int height) override;\n\n            // Print a message to the console, in specified color.\n            void print(const std::string &msg, const std::string& color = \"#FFFFFF\");\n\n            // These are pre-colored versions that you should use.\n\n            /// Output from successful console command\n            void printOK(const std::string &msg);\n\n            /// Error message\n            void printError(const std::string &msg);\n\n            void execute (const std::string& command);\n\n            void executeFile (const std::string& path);\n\n            void updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr);\n\n            void clear() override;\n\n            void resetReference () override;\n\n        protected:\n\n            void onReferenceUnavailable() override;\n\n        private:\n\n            void keyPress(MyGUI::Widget* _sender,\n                          MyGUI::KeyCode key,\n                          MyGUI::Char _char);\n\n            void acceptCommand(MyGUI::EditBox* _sender);\n\n            std::string complete( std::string input, std::vector<std::string> &matches );\n\n            Compiler::Extensions mExtensions;\n            MWScript::CompilerContext mCompilerContext;\n            std::vector<std::string> mNames;\n            bool mConsoleOnlyScripts;\n\n            bool compile (const std::string& cmd, Compiler::Output& output);\n\n            /// Report error to the user.\n            void report (const std::string& message, const Compiler::TokenLoc& loc, Type type) override;\n\n            /// Report a file related error\n            void report (const std::string& message, Type type) override;\n\n            /// Write all valid identifiers and keywords into mNames and sort them.\n            /// \\note If mNames is not empty, this function is a no-op.\n            /// \\note The list may contain duplicates (if a name is a keyword and an identifier at the same\n            /// time).\n            void listNames();\n  };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/container.cpp",
    "content": "#include \"container.hpp\"\n\n#include <MyGUI_InputManager.h>\n#include <MyGUI_Button.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwmechanics/aipackage.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/summoning.hpp\"\n\n#include \"../mwscript/interpretercontext.hpp\"\n\n#include \"countdialog.hpp\"\n#include \"inventorywindow.hpp\"\n\n#include \"itemview.hpp\"\n#include \"inventoryitemmodel.hpp\"\n#include \"containeritemmodel.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n#include \"pickpocketitemmodel.hpp\"\n#include \"draganddrop.hpp\"\n#include \"tooltips.hpp\"\n\nnamespace MWGui\n{\n\n    ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop)\n        : WindowBase(\"openmw_container_window.layout\")\n        , mDragAndDrop(dragAndDrop)\n        , mSortModel(nullptr)\n        , mModel(nullptr)\n        , mSelectedItem(-1)\n    {\n        getWidget(mDisposeCorpseButton, \"DisposeCorpseButton\");\n        getWidget(mTakeButton, \"TakeButton\");\n        getWidget(mCloseButton, \"CloseButton\");\n\n        getWidget(mItemView, \"ItemView\");\n        mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &ContainerWindow::onBackgroundSelected);\n        mItemView->eventItemClicked += MyGUI::newDelegate(this, &ContainerWindow::onItemSelected);\n\n        mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked);\n        mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked);\n        mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked);\n\n        setCoord(200,0,600,300);\n    }\n\n    void ContainerWindow::onItemSelected(int index)\n    {\n        if (mDragAndDrop->mIsOnDragAndDrop)\n        {\n            dropItem();\n            return;\n        }\n\n        const ItemStack& item = mSortModel->getItem(index);\n\n        // We can't take a conjured item from a container (some NPC we're pickpocketing, a box, etc)\n        if (item.mFlags & ItemStack::Flag_Bound)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sContentsMessage1}\");\n            return;\n        }\n\n        MWWorld::Ptr object = item.mBase;\n        int count = item.mCount;\n        bool shift = MyGUI::InputManager::getInstance().isShiftPressed();\n        if (MyGUI::InputManager::getInstance().isControlPressed())\n            count = 1;\n\n        mSelectedItem = mSortModel->mapToSource(index);\n\n        if (count > 1 && !shift)\n        {\n            CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();\n            std::string name = object.getClass().getName(object) + MWGui::ToolTips::getSoulString(object.getCellRef());\n            dialog->openCountDialog(name, \"#{sTake}\", count);\n            dialog->eventOkClicked.clear();\n            dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem);\n        }\n        else\n            dragItem (nullptr, count);\n    }\n\n    void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)\n    {\n        if (!mModel)\n            return;\n\n        if (!onTakeItem(mModel->getItem(mSelectedItem), count))\n            return;\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CONTAINER packet every time an item starts being dragged\n            from a container\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->cell = *mPtr.getCell()->getCell();\n        objectList->action = mwmp::BaseObjectList::REMOVE;\n        objectList->containerSubAction = mwmp::BaseObjectList::DRAG;\n\n        mwmp::BaseObject baseObject = objectList->getBaseObjectFromPtr(mPtr);\n        MWWorld::Ptr itemPtr = mModel->getItem(mSelectedItem).mBase;\n        objectList->addContainerItem(baseObject, itemPtr, itemPtr.getRefData().getCount(), count);\n        objectList->addBaseObject(baseObject);\n        objectList->sendContainer();\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp change (major)\n\n            Avoid running any of the original code for dragging items, to prevent possibilities\n            for item duping or interaction with restricted containers\n        */\n        return;\n        /*\n            End of tes3mp change (major)\n        */\n\n        mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);\n    }\n\n    void ContainerWindow::dropItem()\n    {\n        if (!mModel)\n            return;\n\n        bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount);\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CONTAINER packet every time an item is dropped in a container\n        */\n        if (success)\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->cell = *mPtr.getCell()->getCell();\n            objectList->action = mwmp::BaseObjectList::ADD;\n            objectList->containerSubAction = mwmp::BaseObjectList::DROP;\n\n            mwmp::BaseObject baseObject = objectList->getBaseObjectFromPtr(mPtr);\n            MWWorld::Ptr itemPtr = mDragAndDrop->mItem.mBase;\n            objectList->addContainerItem(baseObject, itemPtr, mDragAndDrop->mDraggedCount, 0);\n            objectList->addBaseObject(baseObject);\n            objectList->sendContainer();\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp change (major)\n\n            For valid drops, avoid running the original code for the item transfer, to prevent unilateral\n            item duping or interaction on this client\n\n            Instead, finish the drag in a way that removes the items in it, and let the server's reply handle\n            the rest\n        */\n        if (success)\n            // mDragAndDrop->drop(mModel, mItemView);\n            mDragAndDrop->finish(true);\n        /*\n            End of tes3mp change (major)\n        */\n    }\n\n    void ContainerWindow::onBackgroundSelected()\n    {\n        if (mDragAndDrop->mIsOnDragAndDrop)\n            dropItem();\n    }\n\n    void ContainerWindow::setPtr(const MWWorld::Ptr& container)\n    {\n        /*\n            Start of tes3mp addition\n\n            Mark this container as open for multiplayer logic purposes\n        */\n        mwmp::Main::get().getLocalPlayer()->storeCurrentContainer(container);\n        /*\n            End of tes3mp addition\n        */\n\n        mPtr = container;\n\n        bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead();\n\n        if (mPtr.getClass().hasInventoryStore(mPtr))\n        {\n            if (mPtr.getClass().isNpc() && !loot)\n            {\n                // we are stealing stuff\n                mModel = new PickpocketItemModel(mPtr, new InventoryItemModel(container),\n                                                 !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown());\n            }\n            else\n                mModel = new InventoryItemModel(container);\n        }\n        else\n        {\n            mModel = new ContainerItemModel(container);\n        }\n\n        mDisposeCorpseButton->setVisible(loot);\n\n        mSortModel = new SortFilterItemModel(mModel);\n\n        mItemView->setModel (mSortModel);\n        mItemView->resetScrollBars();\n\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);\n\n        setTitle(container.getClass().getName(container));\n    }\n\n    void ContainerWindow::resetReference()\n    {\n        ReferenceInterface::resetReference();\n        mItemView->setModel(nullptr);\n        mModel = nullptr;\n        mSortModel = nullptr;\n    }\n\n    void ContainerWindow::onClose()\n    {\n        /*\n            Start of tes3mp addition\n\n            Mark this container as closed for multiplayer logic purposes\n        */\n        mwmp::Main::get().getLocalPlayer()->clearCurrentContainer();\n        /*\n            End of tes3mp addition\n        */\n\n        WindowBase::onClose();\n\n        // Make sure the window was actually closed and not temporarily hidden.\n        if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Container))\n            return;\n\n        if (mModel)\n            mModel->onClose();\n\n        if (!mPtr.isEmpty())\n            MWBase::Environment::get().getMechanicsManager()->onClose(mPtr);\n        resetReference();\n    }\n\n    void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);\n    }\n\n    void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender)\n    {\n        if(mDragAndDrop != nullptr && mDragAndDrop->mIsOnDragAndDrop)\n            return;\n\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CONTAINER packet every time the Take All button is used on\n            a container\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->cell = *mPtr.getCell()->getCell();\n        objectList->action = mwmp::BaseObjectList::REMOVE;\n        objectList->containerSubAction = mwmp::BaseObjectList::TAKE_ALL;\n        mwmp::BaseObject baseObject = objectList->getBaseObjectFromPtr(mPtr);\n\n        for (size_t i = 0; i < mModel->getItemCount(); ++i)\n        {\n            const ItemStack& item = mModel->getItem(i);\n\n            // Trigger crimes related to the attempted taking of these items, if applicable\n            if (!onTakeItem(item, item.mCount))\n                break;\n\n            objectList->addContainerItem(baseObject, item, item.mCount, item.mCount);\n        }\n\n        if (baseObject.containerItems.size() > 0)\n        {\n            objectList->addBaseObject(baseObject);\n            objectList->sendContainer();\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp change (major)\n\n            Avoid running any of the original code for taking all items, to prevent\n            possibilities for item duping or interaction with restricted containers\n        */\n        return;\n        /*\n            End of tes3mp change (major)\n        */\n\n        // transfer everything into the player's inventory\n        ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel();\n        assert(mModel);\n        mModel->update();\n\n        // unequip all items to avoid unequipping/reequipping\n        if (mPtr.getClass().hasInventoryStore(mPtr))\n        {\n            MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);\n            for (size_t i=0; i<mModel->getItemCount(); ++i)\n            {\n                const ItemStack& item = mModel->getItem(i);\n                if (invStore.isEquipped(item.mBase) == false)\n                    continue;\n\n                invStore.unequipItem(item.mBase, mPtr);\n            }\n        }\n\n        mModel->update();\n\n        for (size_t i=0; i<mModel->getItemCount(); ++i)\n        {\n            if (i==0)\n            {\n                // play the sound of the first object\n                MWWorld::Ptr item = mModel->getItem(i).mBase;\n                std::string sound = item.getClass().getUpSoundId(item);\n                MWBase::Environment::get().getWindowManager()->playSound(sound);\n            }\n\n            const ItemStack& item = mModel->getItem(i);\n\n            if (!onTakeItem(item, item.mCount))\n                break;\n\n            mModel->moveItem(item, item.mCount, playerModel);\n        }\n\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);\n    }\n\n    void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender)\n    {\n        if(mDragAndDrop == nullptr || !mDragAndDrop->mIsOnDragAndDrop)\n        {\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);\n\n            // Copy mPtr because onTakeAllButtonClicked closes the window which resets the reference\n            MWWorld::Ptr ptr = mPtr;\n            onTakeAllButtonClicked(mTakeButton);\n            \n            if (ptr.getClass().isPersistent(ptr))\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sDisposeCorpseFail}\");\n            else\n            {\n                /*\n                    Start of tes3mp change (major)\n\n                    Instead of deleting the corpse on this client, increasing the death count and\n                    running the dead actor's script, simply send an ID_OBJECT_DELETE packet to the server\n                    as a request for the deletion\n                */\n\n                /*\n                MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);\n\n                // If we dispose corpse before end of death animation, we should update death counter counter manually.\n                // Also we should run actor's script - it may react on actor's death.\n                if (creatureStats.isDead() && !creatureStats.isDeathAnimationFinished())\n                {\n                    creatureStats.setDeathAnimationFinished(true);\n                    MWBase::Environment::get().getMechanicsManager()->notifyDied(ptr);\n\n                    const std::string script = ptr.getClass().getScript(ptr);\n                    if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled())\n                    {\n                        MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr);\n                        MWBase::Environment::get().getScriptManager()->run (script, interpreterContext);\n                    }\n\n                    // Clean up summoned creatures as well\n                    std::map<ESM::SummonKey, int>& creatureMap = creatureStats.getSummonedCreatureMap();\n                    for (const auto& creature : creatureMap)\n                        MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(ptr, creature.second);\n                    creatureMap.clear();\n\n                    // Check if we are a summon and inform our master we've bit the dust\n                    for(const auto& package : creatureStats.getAiSequence())\n                    {\n                        if(package->followTargetThroughDoors() && !package->getTarget().isEmpty())\n                        {\n                            const auto& summoner = package->getTarget();\n                            auto& summons = summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap();\n                            auto it = std::find_if(summons.begin(), summons.end(), [&] (const auto& entry) { return entry.second == creatureStats.getActorId(); });\n                            if(it != summons.end())\n                            {\n                                MWMechanics::purgeSummonEffect(summoner, *it);\n                                summons.erase(it);\n                                break;\n                            }\n                        }\n                    }\n                }\n\n                MWBase::Environment::get().getWorld()->deleteObject(ptr);\n                */\n\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectGeneric(ptr);\n                objectList->sendObjectDelete();\n                /*\n                    End of tes3mp change (major)\n                */\n            }\n        }\n    }\n\n    void ContainerWindow::onReferenceUnavailable()\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);\n    }\n\n    bool ContainerWindow::onTakeItem(const ItemStack &item, int count)\n    {\n        return mModel->onTakeItem(item.mBase, count);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check from elsewhere whether there is currently an\n        item being dragged in the container window\n    */\n    bool ContainerWindow::isOnDragAndDrop()\n    {\n        return mDragAndDrop->mIsOnDragAndDrop;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to drag a specific item Ptr instead of having to rely\n        on an index that may have changed in the meantime, for drags that\n        require approval from the server\n    */\n    bool ContainerWindow::dragItemByPtr(const MWWorld::Ptr& itemPtr, int dragCount)\n    {\n        ItemModel::ModelIndex newIndex = -1;\n        for (unsigned int i = 0; i < mModel->getItemCount(); ++i)\n        {\n            if (mModel->getItem(i).mBase == itemPtr)\n            {\n                newIndex = i;\n                break;\n            }\n        }\n\n        if (newIndex != -1)\n        {\n            mDragAndDrop->startDrag(newIndex, mSortModel, mModel, mItemView, dragCount);\n            return true;\n        }\n\n        return false;\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/container.hpp",
    "content": "#ifndef MGUI_CONTAINER_H\n#define MGUI_CONTAINER_H\n\n#include \"windowbase.hpp\"\n#include \"referenceinterface.hpp\"\n\n#include \"itemmodel.hpp\"\n\nnamespace MyGUI\n{\n    class Gui;\n    class Widget;\n}\n\nnamespace MWGui\n{\n    class ContainerWindow;\n    class ItemView;\n    class SortFilterItemModel;\n}\n\n\nnamespace MWGui\n{\n    class ContainerWindow : public WindowBase, public ReferenceInterface\n    {\n    public:\n        ContainerWindow(DragAndDrop* dragAndDrop);\n\n        void setPtr(const MWWorld::Ptr& container) override;\n        void onClose() override;\n        void clear() override { resetReference(); }\n\n        void onFrame(float dt) override { checkReferenceAvailable(); }\n\n        void resetReference() override;\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to check from elsewhere whether there is currently an\n            item being dragged in the container window\n        */\n        bool isOnDragAndDrop();\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to drag a specific item Ptr instead of having to rely\n            on an index that may have changed in the meantime, for drags that\n            require approval from the server\n        */\n        bool dragItemByPtr(const MWWorld::Ptr& itemPtr, int dragCount);\n        /*\n            End of tes3mp addition\n        */\n\n    private:\n        DragAndDrop* mDragAndDrop;\n\n        MWGui::ItemView* mItemView;\n        SortFilterItemModel* mSortModel;\n        ItemModel* mModel;\n        int mSelectedItem;\n\n        MyGUI::Button* mDisposeCorpseButton;\n        MyGUI::Button* mTakeButton;\n        MyGUI::Button* mCloseButton;\n\n        void onItemSelected(int index);\n        void onBackgroundSelected();\n        void dragItem(MyGUI::Widget* sender, int count);\n        void dropItem();\n        void onCloseButtonClicked(MyGUI::Widget* _sender);\n        void onTakeAllButtonClicked(MyGUI::Widget* _sender);\n        void onDisposeCorpseButtonClicked(MyGUI::Widget* sender);\n\n        /// @return is taking the item allowed?\n        bool onTakeItem(const ItemStack& item, int count);\n\n        void onReferenceUnavailable() override;\n    };\n}\n#endif // CONTAINER_H\n"
  },
  {
    "path": "apps/openmw/mwgui/containeritemmodel.cpp",
    "content": "#include \"containeritemmodel.hpp\"\n\n#include <algorithm>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\nnamespace\n{\n\n    bool stacks (const MWWorld::Ptr& left, const MWWorld::Ptr& right)\n    {\n        if (left == right)\n            return true;\n\n        // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure\n        if (left.getContainerStore() && right.getContainerStore())\n            return left.getContainerStore()->stacks(left, right)\n                    && right.getContainerStore()->stacks(left, right);\n\n        if (left.getContainerStore())\n            return left.getContainerStore()->stacks(left, right);\n        if (right.getContainerStore())\n            return right.getContainerStore()->stacks(left, right);\n\n        MWWorld::ContainerStore store;\n        return store.stacks(left, right);\n    }\n\n}\n\nnamespace MWGui\n{\nContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSources, const std::vector<MWWorld::Ptr>& worldItems)\n    : mWorldItems(worldItems)\n    , mTrading(true)\n{\n    assert (!itemSources.empty());\n    // Tie resolution lifetimes to the ItemModel\n    mItemSources.reserve(itemSources.size());\n    for(const MWWorld::Ptr& source : itemSources)\n    {\n        MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);\n        mItemSources.emplace_back(source, store.resolveTemporarily());\n    }\n}\n\nContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) : mTrading(false)\n{\n    MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);\n    mItemSources.emplace_back(source, store.resolveTemporarily());\n}\n\nbool ContainerItemModel::allowedToUseItems() const\n{\n    if (mItemSources.empty())\n        return true;\n\n    MWWorld::Ptr ptr = MWMechanics::getPlayer();\n    MWWorld::Ptr victim;\n\n    // Check if the player is allowed to use items from opened container\n    MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();\n    return mm->isAllowedToUse(ptr, mItemSources[0].first, victim);\n}\n\nItemStack ContainerItemModel::getItem (ModelIndex index)\n{\n    if (index < 0)\n        throw std::runtime_error(\"Invalid index supplied\");\n    if (mItems.size() <= static_cast<size_t>(index))\n        throw std::runtime_error(\"Item index out of range\");\n    return mItems[index];\n}\n\nsize_t ContainerItemModel::getItemCount()\n{\n    return mItems.size();\n}\n\nItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)\n{\n    size_t i = 0;\n    for (ItemStack& itemStack : mItems)\n    {\n        if (itemStack == item)\n            return i;\n        ++i;\n    }\n    return -1;\n}\n\nMWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)\n{\n    auto& source = mItemSources[0];\n    MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);\n    if (item.mBase.getContainerStore() == &store)\n        throw std::runtime_error(\"Item to copy needs to be from a different container!\");\n\n    /*\n        Start of tes3mp addition\n\n        Send an ID_CONTAINER packet every time an item is added to a container here\n    */\n    mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n    objectList->reset();\n    objectList->packetOrigin = mwmp::PACKET_ORIGIN::CLIENT_GAMEPLAY;\n    objectList->cell = *source.first.getCell()->getCell();\n    objectList->action = mwmp::BaseObjectList::ADD;\n    objectList->containerSubAction = mwmp::BaseObjectList::NONE;\n    mwmp::BaseObject baseObject = objectList->getBaseObjectFromPtr(source.first);\n    objectList->addContainerItem(baseObject, item.mBase, count, 0);\n    objectList->addBaseObject(baseObject);\n    objectList->sendContainer();\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp change (major)\n\n        Instead of unilaterally adding the item to this source's ContainerStore on this\n        client and returning the resulting Ptr, rely on the server to handle the item\n        transfer and just return the original item Ptr as a placeholder return value\n    */\n    return item.mBase;\n    /*\n        End of tes3mp change (major)\n    */\n}\n\nvoid ContainerItemModel::removeItem (const ItemStack& item, size_t count)\n{\n    int toRemove = count;\n\n    for (auto& source : mItemSources)\n    {\n        MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);\n\n        for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n        {\n            if (stacks(*it, item.mBase))\n            {\n                /*\n                    Start of tes3mp change (major)\n\n                    Send an ID_CONTAINER packet every time an item is removed here and prevent any\n                    unilateral item removal on this client, as long as this isn't the player's\n                    currently open container and doesn't require the drag and drop logic dealt with\n                    in MWGui::ContainerWindow instead\n                */\n                mwmp::CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer;\n\n                if (currentContainer->refNum != source.first.getCellRef().getRefNum().mIndex ||\n                    currentContainer->mpNum != source.first.getCellRef().getMpNum())\n                {\n                    mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::PACKET_ORIGIN::CLIENT_GAMEPLAY;\n                    objectList->cell = *source.first.getCell()->getCell();\n                    objectList->action = mwmp::BaseObjectList::REMOVE;\n                    objectList->containerSubAction = mwmp::BaseObjectList::NONE;\n                    mwmp::BaseObject baseObject = objectList->getBaseObjectFromPtr(source.first);\n                    objectList->addContainerItem(baseObject, *it, it->getRefData().getCount(), toRemove);\n                    objectList->addBaseObject(baseObject);\n                    objectList->sendContainer();\n                    \n                    toRemove -= it->getRefData().getCount();\n                }\n                else\n                {\n                    int quantity = it->mRef->mData.getCount(false);\n                    // If this is a restocking quantity, just don't remove it\n                    if (quantity < 0 && mTrading)\n                        toRemove += quantity;\n                    else\n                        toRemove -= store.remove(*it, toRemove, source.first);\n                }\n                /*\n                    End of tes3mp change (major)\n                */\n\n                if (toRemove <= 0)\n                    return;\n            }\n        }\n    }\n    for (MWWorld::Ptr& source : mWorldItems)\n    {\n        if (stacks(source, item.mBase))\n        {\n            int refCount = source.getRefData().getCount();\n            if (refCount - toRemove <= 0)\n            {\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_OBJECT_DELETE packet every time an item is removed from the world\n                    because it has been purchased from its owner\n                */\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectGeneric(source);\n                objectList->sendObjectDelete();\n                /*\n                    End of tes3mp addition\n                */\n\n                MWBase::Environment::get().getWorld()->deleteObject(source);\n            }\n            else\n                source.getRefData().setCount(std::max(0, refCount - toRemove));\n            toRemove -= refCount;\n            if (toRemove <= 0)\n                return;\n        }\n    }\n\n    throw std::runtime_error(\"Not enough items to remove could be found\");\n}\n\nvoid ContainerItemModel::update()\n{\n    mItems.clear();\n    for (auto& source : mItemSources)\n    {\n        MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);\n\n        for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n        {\n            if (!(*it).getClass().showsInInventory(*it))\n                continue;\n\n            bool found = false;\n            for (ItemStack& itemStack : mItems)\n            {\n                if (stacks(*it, itemStack.mBase))\n                {\n                    // we already have an item stack of this kind, add to it\n                    itemStack.mCount += it->getRefData().getCount();\n                    found = true;\n                    break;\n                }\n            }\n\n            if (!found)\n            {\n                // no stack yet, create one\n                ItemStack newItem (*it, this, it->getRefData().getCount());\n                mItems.push_back(newItem);\n            }\n        }\n    }\n    for (MWWorld::Ptr& source : mWorldItems)\n    {\n        bool found = false;\n        for (ItemStack& itemStack : mItems)\n        {\n            if (stacks(source, itemStack.mBase))\n            {\n                // we already have an item stack of this kind, add to it\n                itemStack.mCount += source.getRefData().getCount();\n                found = true;\n                break;\n            }\n        }\n\n        if (!found)\n        {\n            // no stack yet, create one\n            ItemStack newItem (source, this, source.getRefData().getCount());\n            mItems.push_back(newItem);\n        }\n    }\n}\nbool ContainerItemModel::onDropItem(const MWWorld::Ptr &item, int count)\n{\n    if (mItemSources.empty())\n        return false;\n\n    MWWorld::Ptr target = mItemSources[0].first;\n\n    if (target.getTypeName() != typeid(ESM::Container).name())\n        return true;\n\n    // check container organic flag\n    MWWorld::LiveCellRef<ESM::Container>* ref = target.get<ESM::Container>();\n    if (ref->mBase->mFlags & ESM::Container::Organic)\n    {\n        MWBase::Environment::get().getWindowManager()->\n            messageBox(\"#{sContentsMessage2}\");\n        return false;\n    }\n\n    // check that we don't exceed container capacity\n    float weight = item.getClass().getWeight(item) * count;\n    if (target.getClass().getCapacity(target) < target.getClass().getEncumbrance(target) + weight)\n    {\n        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sContentsMessage3}\");\n        return false;\n    }\n\n    return true;\n}\n\nbool ContainerItemModel::onTakeItem(const MWWorld::Ptr &item, int count)\n{\n    if (mItemSources.empty())\n        return false;\n\n    MWWorld::Ptr target = mItemSources[0].first;\n\n    // Looting a dead corpse is considered OK\n    if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())\n        return true;\n\n    MWWorld::Ptr player = MWMechanics::getPlayer();\n    MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item, target, count);\n\n    return true;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/containeritemmodel.hpp",
    "content": "#ifndef MWGUI_CONTAINER_ITEM_MODEL_H\n#define MWGUI_CONTAINER_ITEM_MODEL_H\n\n#include <utility>\n#include <vector>\n\n#include \"itemmodel.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n\nnamespace MWGui\n{\n\n    /// @brief The container item model supports multiple item sources, which are needed for\n    /// making NPCs sell items from containers owned by them\n    class ContainerItemModel : public ItemModel\n    {\n    public:\n        ContainerItemModel (const std::vector<MWWorld::Ptr>& itemSources, const std::vector<MWWorld::Ptr>& worldItems);\n        ///< @note The order of elements \\a itemSources matters here. The first element has the highest priority for removal,\n        ///  while the last element will be used to add new items to.\n\n        ContainerItemModel (const MWWorld::Ptr& source);\n\n        bool allowedToUseItems() const override;\n\n        bool onDropItem(const MWWorld::Ptr &item, int count) override;\n        bool onTakeItem(const MWWorld::Ptr &item, int count) override;\n\n        ItemStack getItem (ModelIndex index) override;\n        ModelIndex getIndex (ItemStack item) override;\n        size_t getItemCount() override;\n\n        MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true) override;\n        void removeItem (const ItemStack& item, size_t count) override;\n\n        void update() override;\n\n    private:\n        std::vector<std::pair<MWWorld::Ptr, MWWorld::ResolutionHandle>> mItemSources;\n        std::vector<MWWorld::Ptr> mWorldItems;\n        const bool mTrading;\n        std::vector<ItemStack> mItems;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/controllers.cpp",
    "content": "#include \"controllers.hpp\"\n\n#include <MyGUI_InputManager.h>\n#include <MyGUI_Widget.h>\n\nnamespace MWGui\n{\n    namespace Controllers\n    {\n        void ControllerFollowMouse::prepareItem(MyGUI::Widget *_widget)\n        {\n        }\n\n        bool ControllerFollowMouse::addTime(MyGUI::Widget *_widget, float _time)\n        {\n            _widget->setPosition(MyGUI::InputManager::getInstance().getMousePosition());\n            return true;\n        }\n\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/controllers.hpp",
    "content": "#ifndef MWGUI_CONTROLLERS_H\n#define MWGUI_CONTROLLERS_H\n\n#include <string>\n#include <MyGUI_ControllerItem.h>\n\nnamespace MyGUI\n{\n    class Widget;\n}\n\nnamespace MWGui::Controllers\n    {\n        /// Automatically positions a widget below the mouse cursor.\n        class ControllerFollowMouse final : public MyGUI::ControllerItem\n        {\n            MYGUI_RTTI_DERIVED( ControllerFollowMouse )\n\n        private:\n            bool addTime(MyGUI::Widget* _widget, float _time) override;\n            void prepareItem(MyGUI::Widget* _widget) override;\n        };\n    }\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/countdialog.cpp",
    "content": "#include \"countdialog.hpp\"\n\n#include <MyGUI_Button.h>\n#include <MyGUI_ScrollBar.h>\n#include <MyGUI_RenderManager.h>\n\n#include <components/widgets/numericeditbox.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\nnamespace MWGui\n{\n    CountDialog::CountDialog() :\n        WindowModal(\"openmw_count_window.layout\")\n    {\n        getWidget(mSlider, \"CountSlider\");\n        getWidget(mItemEdit, \"ItemEdit\");\n        getWidget(mItemText, \"ItemText\");\n        getWidget(mLabelText, \"LabelText\");\n        getWidget(mOkButton, \"OkButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CountDialog::onCancelButtonClicked);\n        mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CountDialog::onOkButtonClicked);\n        mItemEdit->eventValueChanged += MyGUI::newDelegate(this, &CountDialog::onEditValueChanged);\n        mSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &CountDialog::onSliderMoved);\n        // make sure we read the enter key being pressed to accept multiple items\n        mItemEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &CountDialog::onEnterKeyPressed);\n    }\n\n    void CountDialog::openCountDialog(const std::string& item, const std::string& message, const int maxCount)\n    {\n        setVisible(true);\n\n        mLabelText->setCaptionWithReplacing(message);\n\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n\n        mSlider->setScrollRange(maxCount);\n        mItemText->setCaption(item);\n\n        int width = std::max(mItemText->getTextSize().width + 128, 320);\n        setCoord(viewSize.width/2 - width/2,\n                viewSize.height/2 - mMainWidget->getHeight()/2,\n                width,\n                mMainWidget->getHeight());\n\n        // by default, the text edit field has the focus of the keyboard\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mItemEdit);\n\n        mSlider->setScrollPosition(maxCount-1);\n\n        mItemEdit->setMinValue(1);\n        mItemEdit->setMaxValue(maxCount);\n        mItemEdit->setValue(maxCount);\n    }\n\n    void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender)\n    {\n        setVisible(false);\n    }\n\n    void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender)\n    {\n        eventOkClicked(nullptr, mSlider->getScrollPosition()+1);\n\n        setVisible(false);\n    }\n\n    // essentially duplicating what the OK button does if user presses\n    // Enter key\n    void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender)\n    {\n        eventOkClicked(nullptr, mSlider->getScrollPosition()+1);\n        setVisible(false);\n\n        // To do not spam onEnterKeyPressed() again and again\n        MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);\n    }\n\n    void CountDialog::onEditValueChanged(int value)\n    {\n        mSlider->setScrollPosition(value-1);\n    }\n\n    void CountDialog::onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position)\n    {\n        mItemEdit->setValue(_position+1);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/countdialog.hpp",
    "content": "#ifndef MWGUI_COUNTDIALOG_H\n#define MWGUI_COUNTDIALOG_H\n\n#include \"windowbase.hpp\"\n\nnamespace Gui\n{\n    class NumericEditBox;\n}\n\nnamespace MWGui\n{\n    class CountDialog : public WindowModal\n    {\n        public:\n            CountDialog();\n            void openCountDialog(const std::string& item, const std::string& message, const int maxCount);\n\n            typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt;\n\n            /** Event : Ok button was clicked.\\n\n                signature : void method(MyGUI::Widget* _sender, int _count)\\n\n            */\n            EventHandle_WidgetInt eventOkClicked;\n\n        private:\n            MyGUI::ScrollBar* mSlider;\n            Gui::NumericEditBox* mItemEdit;\n            MyGUI::TextBox* mItemText;\n            MyGUI::TextBox* mLabelText;\n            MyGUI::Button* mOkButton;\n            MyGUI::Button* mCancelButton;\n\n            void onCancelButtonClicked(MyGUI::Widget* _sender);\n            void onOkButtonClicked(MyGUI::Widget* _sender);\n            void onEditValueChanged(int value);\n            void onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position);\n            void onEnterKeyPressed(MyGUI::EditBox* _sender);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/cursor.cpp",
    "content": "#include \"cursor.hpp\"\n\n#include <MyGUI_PointerManager.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_RotatingSkin.h>\n#include <MyGUI_Gui.h>\n\nnamespace MWGui\n{\n\n\n    ResourceImageSetPointerFix::ResourceImageSetPointerFix()\n        : mImageSet(nullptr)\n        , mRotation(0)\n    {\n    }\n\n    ResourceImageSetPointerFix::~ResourceImageSetPointerFix()\n    {\n    }\n\n    void ResourceImageSetPointerFix::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version)\n    {\n        Base::deserialization(_node, _version);\n\n        MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator();\n        while (info.next(\"Property\"))\n        {\n            const std::string& key = info->findAttribute(\"key\");\n            const std::string& value = info->findAttribute(\"value\");\n\n            if (key == \"Point\")\n                mPoint = MyGUI::IntPoint::parse(value);\n            else if (key == \"Size\")\n                mSize = MyGUI::IntSize::parse(value);\n            else if (key == \"Rotation\")\n                mRotation = MyGUI::utility::parseInt(value);\n            else if (key == \"Resource\")\n                mImageSet = MyGUI::ResourceManager::getInstance().getByName(value)->castType<MyGUI::ResourceImageSet>();\n        }\n    }\n\n    int ResourceImageSetPointerFix::getRotation()\n    {\n        return mRotation;\n    }\n\n    void ResourceImageSetPointerFix::setImage(MyGUI::ImageBox* _image)\n    {\n        if (mImageSet != nullptr)\n            _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0));\n    }\n\n    void ResourceImageSetPointerFix::setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point)\n    {\n        _image->setCoord(_point.left - mPoint.left, _point.top - mPoint.top, mSize.width, mSize.height);\n    }\n\n    MyGUI::ResourceImageSetPtr ResourceImageSetPointerFix:: getImageSet()\n    {\n        return mImageSet;\n    }\n\n    MyGUI::IntPoint ResourceImageSetPointerFix::getHotSpot()\n    {\n        return mPoint;\n    }\n\n    MyGUI::IntSize ResourceImageSetPointerFix::getSize()\n    {\n        return mSize;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/cursor.hpp",
    "content": "#ifndef MWGUI_CURSOR_H\n#define MWGUI_CURSOR_H\n\n#include <MyGUI_IPointer.h>\n#include <MyGUI_ResourceImageSet.h>\n\nnamespace MWGui\n{\n\n    /// \\brief Allows us to get the members of\n    ///        ResourceImageSetPointer that we need.\n    /// \\example MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>(\"Resource\", \"ResourceImageSetPointer\");\n    ///          MyGUI::ResourceManager::getInstance().load(\"core.xml\");\n    class ResourceImageSetPointerFix final :\n        public MyGUI::IPointer\n    {\n        MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix )\n\n    public:\n        ResourceImageSetPointerFix();\n        virtual ~ResourceImageSetPointerFix();\n\n        void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) override;\n\n        void setImage(MyGUI::ImageBox* _image) override;\n        void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point) override;\n\n        //and now for the whole point of this class, allow us to get\n        //the hot spot, the image and the size of the cursor.\n        MyGUI::ResourceImageSetPtr getImageSet();\n        MyGUI::IntPoint getHotSpot();\n        MyGUI::IntSize getSize();\n        int getRotation();\n\n    private:\n        MyGUI::IntPoint mPoint;\n        MyGUI::IntSize mSize;\n        MyGUI::ResourceImageSetPtr mImageSet;\n        int mRotation; // rotation in degrees\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/debugwindow.cpp",
    "content": "#include \"debugwindow.hpp\"\n\n#include <MyGUI_TabControl.h>\n#include <MyGUI_TabItem.h>\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_EditBox.h>\n\n#include <LinearMath/btQuickprof.h>\n\n#ifndef BT_NO_PROFILE\n\nnamespace\n{\n    void bulletDumpRecursive(CProfileIterator* pit, int spacing, std::stringstream& os)\n    {\n        pit->First();\n        if (pit->Is_Done())\n            return;\n\n        float accumulated_time=0,parent_time = pit->Is_Root() ? CProfileManager::Get_Time_Since_Reset() : pit->Get_Current_Parent_Total_Time();\n        int i,j;\n        int frames_since_reset = CProfileManager::Get_Frame_Count_Since_Reset();\n        for (i=0;i<spacing;i++) os << \".\";\n        os << \"----------------------------------\\n\";\n        for (i=0;i<spacing;i++) os << \".\";\n        std::string s = \"Profiling: \"+\n                std::string(pit->Get_Current_Parent_Name())+\" (total running time: \"+MyGUI::utility::toString(parent_time,3)+\" ms) ---\\n\";\n        os << s;\n        //float totalTime = 0.f;\n\n        int numChildren = 0;\n\n        for (i = 0; !pit->Is_Done(); i++,pit->Next())\n        {\n            numChildren++;\n            float current_total_time = pit->Get_Current_Total_Time();\n            accumulated_time += current_total_time;\n            float fraction = parent_time > SIMD_EPSILON ? (current_total_time / parent_time) * 100 : 0.f;\n\n            for (j=0;j<spacing;j++) os << \".\";\n            double ms = (current_total_time / (double)frames_since_reset);\n            s = MyGUI::utility::toString(i)+\" -- \"+pit->Get_Current_Name()+\" (\"+MyGUI::utility::toString(fraction,2)+\" %) :: \"+MyGUI::utility::toString(ms,3)+\" ms / frame (\"+MyGUI::utility::toString(pit->Get_Current_Total_Calls())+\" calls)\\n\";\n            os << s;\n            //totalTime += current_total_time;\n            //recurse into children\n        }\n\n        if (parent_time < accumulated_time)\n        {\n            os << \"what's wrong\\n\";\n        }\n        for (i=0;i<spacing;i++) os << \".\";\n        double unaccounted=  parent_time > SIMD_EPSILON ? ((parent_time - accumulated_time) / parent_time) * 100 : 0.f;\n        s = \"Unaccounted: (\"+MyGUI::utility::toString(unaccounted,3)+\" %) :: \"+MyGUI::utility::toString(parent_time - accumulated_time,3)+\" ms\\n\";\n        os << s;\n\n        for (i=0;i<numChildren;i++)\n        {\n            pit->Enter_Child(i);\n            bulletDumpRecursive(pit, spacing+3, os);\n            pit->Enter_Parent();\n        }\n    }\n\n    void bulletDumpAll(std::stringstream& os)\n    {\n        CProfileIterator* profileIterator = 0;\n        profileIterator = CProfileManager::Get_Iterator();\n\n        bulletDumpRecursive(profileIterator, 0, os);\n\n        CProfileManager::Release_Iterator(profileIterator);\n    }\n}\n\n#endif // BT_NO_PROFILE\n\nnamespace MWGui\n{\n\n    DebugWindow::DebugWindow()\n        : WindowBase(\"openmw_debug_window.layout\")\n    {\n        getWidget(mTabControl, \"TabControl\");\n\n        // Ideas for other tabs:\n        // - Texture / compositor texture viewer\n        // - Log viewer\n        // - Material editor\n        // - Shader editor\n\n        MyGUI::TabItem* item = mTabControl->addItem(\"Physics Profiler\");\n        mBulletProfilerEdit = item->createWidgetReal<MyGUI::EditBox>\n                (\"LogEdit\", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Stretch);\n\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        mMainWidget->setSize(viewSize);\n\n\n    }\n\n    void DebugWindow::onFrame(float dt)\n    {\n#ifndef BT_NO_PROFILE\n        if (!isVisible())\n            return;\n\n        static float timer = 0;\n        timer -= dt;\n\n        if (timer > 0)\n            return;\n        timer = 1;\n\n        std::stringstream stream;\n        bulletDumpAll(stream);\n\n        if (mBulletProfilerEdit->isTextSelection()) // pause updating while user is trying to copy text\n            return;\n\n        size_t previousPos = mBulletProfilerEdit->getVScrollPosition();\n        mBulletProfilerEdit->setCaption(stream.str());\n        mBulletProfilerEdit->setVScrollPosition(std::min(previousPos, mBulletProfilerEdit->getVScrollRange()-1));\n#endif\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/debugwindow.hpp",
    "content": "#ifndef OPENMW_MWGUI_DEBUGWINDOW_H\n#define OPENMW_MWGUI_DEBUGWINDOW_H\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n\n    class DebugWindow : public WindowBase\n    {\n    public:\n        DebugWindow();\n\n        void onFrame(float dt) override;\n\n    private:\n        MyGUI::TabControl* mTabControl;\n\n        MyGUI::EditBox* mBulletProfilerEdit;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/dialogue.cpp",
    "content": "#include \"dialogue.hpp\"\n\n#include <MyGUI_LanguageManager.h>\n#include <MyGUI_Window.h>\n#include <MyGUI_ProgressBar.h>\n#include <MyGUI_ScrollBar.h>\n#include <MyGUI_Button.h>\n\n#include <components/debug/debuglog.hpp>\n#include <components/widgets/list.hpp>\n#include <components/translation/translation.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include <components/openmw-mp/TimedLog.hpp>\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"bookpage.hpp\"\n#include \"textcolours.hpp\"\n\n#include \"journalbooks.hpp\" // to_utf8_span\n\nnamespace MWGui\n{\n\n    class ResponseCallback : public MWBase::DialogueManager::ResponseCallback\n    {\n    public:\n        ResponseCallback(DialogueWindow* win, bool needMargin=true)\n            : mWindow(win)\n            , mNeedMargin(needMargin)\n        {\n\n        }\n\n        void addResponse(const std::string& title, const std::string& text) override\n        {\n            mWindow->addResponse(title, text, mNeedMargin);\n        }\n\n        void updateTopics()\n        {\n            mWindow->updateTopics();\n        }\n\n    private:\n        DialogueWindow* mWindow;\n        bool mNeedMargin;\n    };\n\n    PersuasionDialog::PersuasionDialog(ResponseCallback* callback)\n        : WindowModal(\"openmw_persuasion_dialog.layout\")\n        , mCallback(callback)\n    {\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mAdmireButton, \"AdmireButton\");\n        getWidget(mIntimidateButton, \"IntimidateButton\");\n        getWidget(mTauntButton, \"TauntButton\");\n        getWidget(mBribe10Button, \"Bribe10Button\");\n        getWidget(mBribe100Button, \"Bribe100Button\");\n        getWidget(mBribe1000Button, \"Bribe1000Button\");\n        getWidget(mGoldLabel, \"GoldLabel\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel);\n        mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);\n        mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);\n        mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);\n        mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);\n        mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);\n        mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);\n    }\n\n    void PersuasionDialog::onCancel(MyGUI::Widget *sender)\n    {\n        setVisible(false);\n    }\n\n    void PersuasionDialog::onPersuade(MyGUI::Widget *sender)\n    {\n        MWBase::MechanicsManager::PersuasionType type;\n        if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire;\n        else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate;\n        else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt;\n        else if (sender == mBribe10Button)\n            type = MWBase::MechanicsManager::PT_Bribe10;\n        else if (sender == mBribe100Button)\n            type = MWBase::MechanicsManager::PT_Bribe100;\n        else /*if (sender == mBribe1000Button)*/\n            type = MWBase::MechanicsManager::PT_Bribe1000;\n\n        MWBase::Environment::get().getDialogueManager()->persuade(type, mCallback.get());\n        mCallback->updateTopics();\n\n        setVisible(false);\n    }\n\n    void PersuasionDialog::onOpen()\n    {\n        center();\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        mBribe10Button->setEnabled (playerGold >= 10);\n        mBribe100Button->setEnabled (playerGold >= 100);\n        mBribe1000Button->setEnabled (playerGold >= 1000);\n\n        mGoldLabel->setCaptionWithReplacing(\"#{sGold}: \" + MyGUI::utility::toString(playerGold));\n        WindowModal::onOpen();\n    }\n\n    MyGUI::Widget* PersuasionDialog::getDefaultKeyFocus()\n    {\n        return mAdmireButton;\n    }\n\n    // --------------------------------------------------------------------------------------------------\n\n    Response::Response(const std::string &text, const std::string &title, bool needMargin)\n        : mTitle(title), mNeedMargin(needMargin)\n    {\n        mText = text;\n    }\n\n    void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const\n    {\n        typesetter->sectionBreak(mNeedMargin ? 9 : 0);\n\n        if (mTitle != \"\")\n        {\n            const MyGUI::Colour& headerColour = MWBase::Environment::get().getWindowManager()->getTextColours().header;\n            BookTypesetter::Style* title = typesetter->createStyle(\"\", headerColour, false);\n            typesetter->write(title, to_utf8_span(mTitle.c_str()));\n            typesetter->sectionBreak();\n        }\n\n        typedef std::pair<size_t, size_t> Range;\n        std::map<Range, intptr_t> hyperLinks;\n\n        // We need this copy for when @# hyperlinks are replaced\n        std::string text = mText;\n\n        size_t pos_end = std::string::npos;\n        for(;;)\n        {\n            size_t pos_begin = text.find('@');\n            if (pos_begin != std::string::npos)\n                pos_end = text.find('#', pos_begin);\n\n            if (pos_begin != std::string::npos && pos_end != std::string::npos)\n            {\n                std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1);\n                const char specialPseudoAsteriskCharacter = 127;\n                std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*');\n                std::string topicName = MWBase::Environment::get().getWindowManager()->\n                        getTranslationDataStorage().topicStandardForm(link);\n\n                std::string displayName = link;\n                while (displayName[displayName.size()-1] == '*')\n                    displayName.erase(displayName.size()-1, 1);\n\n                text.replace(pos_begin, pos_end+1-pos_begin, displayName);\n\n                if (topicLinks.find(Misc::StringUtils::lowerCase(topicName)) != topicLinks.end())\n                    hyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = intptr_t(topicLinks[Misc::StringUtils::lowerCase(topicName)]);\n            }\n            else\n                break;\n        }\n\n        typesetter->addContent(to_utf8_span(text.c_str()));\n\n        if (hyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())\n        {\n            const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();\n\n            BookTypesetter::Style* style = typesetter->createStyle(\"\", textColours.normal, false);\n            size_t formatted = 0; // points to the first character that is not laid out yet\n            for (auto& hyperLink : hyperLinks)\n            {\n                intptr_t topicId = hyperLink.second;\n                BookTypesetter::Style* hotStyle = typesetter->createHotStyle (style, textColours.link,\n                                                                              textColours.linkOver, textColours.linkPressed,\n                                                                              topicId);\n                if (formatted < hyperLink.first.first)\n                    typesetter->write(style, formatted, hyperLink.first.first);\n                typesetter->write(hotStyle, hyperLink.first.first, hyperLink.first.second);\n                formatted = hyperLink.first.second;\n            }\n            if (formatted < text.size())\n                typesetter->write(style, formatted, text.size());\n        }\n        else\n        {\n            std::vector<KeywordSearchT::Match> matches;\n            keywordSearch->highlightKeywords(text.begin(), text.end(), matches);\n\n            std::string::const_iterator i = text.begin ();\n            for (KeywordSearchT::Match& match : matches)\n            {\n                if (i != match.mBeg)\n                    addTopicLink (typesetter, 0, i - text.begin (), match.mBeg - text.begin ());\n\n                addTopicLink (typesetter, match.mValue, match.mBeg - text.begin (), match.mEnd - text.begin ());\n\n                i = match.mEnd;\n            }\n            if (i != text.end ())\n                addTopicLink (typesetter, 0, i - text.begin (), text.size ());\n        }\n    }\n\n    void Response::addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const\n    {\n        const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();\n\n        BookTypesetter::Style* style = typesetter->createStyle(\"\", textColours.normal, false);\n\n\n        if (topicId)\n            style = typesetter->createHotStyle (style, textColours.link, textColours.linkOver, textColours.linkPressed, topicId);\n        typesetter->write (style, begin, end);\n    }\n\n    Message::Message(const std::string& text)\n    {\n        mText = text;\n    }\n\n    void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const\n    {\n        const MyGUI::Colour& textColour = MWBase::Environment::get().getWindowManager()->getTextColours().notify;\n        BookTypesetter::Style* title = typesetter->createStyle(\"\", textColour, false);\n        typesetter->sectionBreak(9);\n        typesetter->write(title, to_utf8_span(mText.c_str()));\n    }\n\n    // --------------------------------------------------------------------------------------------------\n\n    void Choice::activated()\n    {\n        MWBase::Environment::get().getWindowManager()->playSound(\"Menu Click\");\n        eventChoiceActivated(mChoiceId);\n    }\n\n    void Topic::activated()\n    {\n        MWBase::Environment::get().getWindowManager()->playSound(\"Menu Click\");\n        eventTopicActivated(mTopicId);\n    }\n\n    void Goodbye::activated()\n    {\n        MWBase::Environment::get().getWindowManager()->playSound(\"Menu Click\");\n        eventActivated();\n    }\n\n    // --------------------------------------------------------------------------------------------------\n\n    DialogueWindow::DialogueWindow()\n        : WindowBase(\"openmw_dialogue_window.layout\")\n        , mIsCompanion(false)\n        , mGoodbye(false)\n        , mPersuasionDialog(new ResponseCallback(this))\n        , mCallback(new ResponseCallback(this))\n        , mGreetingCallback(new ResponseCallback(this, false))\n    {\n        // Centre dialog\n        center();\n\n        mPersuasionDialog.setVisible(false);\n\n        //History view\n        getWidget(mHistory, \"History\");\n\n        //Topics list\n        getWidget(mTopicsList, \"TopicsList\");\n        mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectListItem);\n\n        getWidget(mGoodbyeButton, \"ByeButton\");\n        mGoodbyeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);\n\n        getWidget(mDispositionBar, \"Disposition\");\n        getWidget(mDispositionText,\"DispositionText\");\n        getWidget(mScrollBar, \"VScroll\");\n\n        mScrollBar->eventScrollChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onScrollbarMoved);\n        mHistory->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel);\n\n        BookPage::ClickCallback callback = std::bind (&DialogueWindow::notifyLinkClicked, this, std::placeholders::_1);\n        mHistory->adviseLinkClicked(callback);\n\n        mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);\n    }\n\n    DialogueWindow::~DialogueWindow()\n    {\n        deleteLater();\n        for (Link* link : mLinks)\n            delete link;\n        for (const auto& link : mTopicLinks)\n            delete link.second;\n        for (auto history : mHistoryContents)\n            delete history;\n    }\n\n    void DialogueWindow::onTradeComplete()\n    {\n        addResponse(\"\", MyGUI::LanguageManager::getInstance().replaceTags(\"#{sBarterDialog5}\"));\n    }\n\n    bool DialogueWindow::exit()\n    {\n        if ((MWBase::Environment::get().getDialogueManager()->isInChoice()))\n        {\n            return false;\n        }\n        else\n        {\n            resetReference();\n            MWBase::Environment::get().getDialogueManager()->goodbyeSelected();\n            mTopicsList->scrollToTop();\n            return true;\n        }\n    }\n\n    void DialogueWindow::onWindowResize(MyGUI::Window* _sender)\n    {\n        // if the window has only been moved, not resized, we don't need to update\n        if (mCurrentWindowSize == _sender->getSize()) return;\n\n        mTopicsList->adjustSize();\n        updateHistory();\n        updateTopicFormat();\n        mCurrentWindowSize = _sender->getSize();\n    }\n\n    void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)\n    {\n        if (!mScrollBar->getVisible())\n            return;\n        mScrollBar->setScrollPosition(std::min(static_cast<int>(mScrollBar->getScrollRange()-1),\n                                               std::max(0, static_cast<int>(mScrollBar->getScrollPosition() - _rel*0.3))));\n        onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition());\n    }\n\n    void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)\n    {\n        if (exit())\n        {\n            MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);\n        }\n    }\n\n    void DialogueWindow::onSelectListItem(const std::string& topic, int id)\n    {\n        /*\n            Start of tes3mp change (major)\n\n            Instead of activating a list item here, send an ObjectDialogueChoice packet to the server\n            and let it decide whether the list item gets activated\n        */\n        sendDialogueChoicePacket(topic);\n        return;\n        /*\n            End of tes3mp change (major)\n        */\n\n        MWBase::DialogueManager* dialogueManager = MWBase::Environment::get().getDialogueManager();\n\n        if (mGoodbye || dialogueManager->isInChoice())\n            return;\n\n        const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        const std::string sPersuasion = gmst.find(\"sPersuasion\")->mValue.getString();\n        const std::string sCompanionShare = gmst.find(\"sCompanionShare\")->mValue.getString();\n        const std::string sBarter = gmst.find(\"sBarter\")->mValue.getString();\n        const std::string sSpells = gmst.find(\"sSpells\")->mValue.getString();\n        const std::string sTravel = gmst.find(\"sTravel\")->mValue.getString();\n        const std::string sSpellMakingMenuTitle = gmst.find(\"sSpellMakingMenuTitle\")->mValue.getString();\n        const std::string sEnchanting = gmst.find(\"sEnchanting\")->mValue.getString();\n        const std::string sServiceTrainingTitle = gmst.find(\"sServiceTrainingTitle\")->mValue.getString();\n        const std::string sRepair = gmst.find(\"sRepair\")->mValue.getString();\n\n        if (topic != sPersuasion && topic != sCompanionShare && topic != sBarter \n         && topic != sSpells && topic != sTravel && topic != sSpellMakingMenuTitle \n         && topic != sEnchanting && topic != sServiceTrainingTitle && topic != sRepair)\n        {\n            onTopicActivated(topic);\n            if (mGoodbyeButton->getEnabled())\n                MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);\n        }\n        else if (topic == sPersuasion)\n            mPersuasionDialog.setVisible(true);\n        else if (topic == sCompanionShare)\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr);\n        else if (!dialogueManager->checkServiceRefused(mCallback.get()))\n        {\n            if (topic == sBarter && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Barter))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);\n            else if (topic == sSpells && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Spells))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr);\n            else if (topic == sTravel && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Travel))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr);\n            else if (topic == sSpellMakingMenuTitle && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Spellmaking))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr);\n            else if (topic == sEnchanting && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Enchanting))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr);\n            else if (topic == sServiceTrainingTitle && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Training))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr);\n            else if (topic == sRepair && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Repair))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr);\n        }\n        else\n            updateTopics();\n    }\n\n    /*\n        Start of tes3mp addition\n\n        A different event that should be used in multiplayer when clicking on choices\n        in the dialogue screen, sending DialogueChoice packets to the server so they can\n        be approved or denied\n    */\n    void DialogueWindow::sendDialogueChoicePacket(const std::string& topic)\n    {\n        mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectDialogueChoice(mPtr, topic);\n        objectList->sendObjectDialogueChoice();\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to activate any dialogue choice from elsewhere in the code\n    */\n    void DialogueWindow::activateDialogueChoice(unsigned char dialogueChoiceType, std::string topic)\n    {\n        if (dialogueChoiceType == mwmp::DialogueChoiceType::TOPIC)\n        {\n            onTopicActivated(topic);\n        }\n        else if (dialogueChoiceType == mwmp::DialogueChoiceType::PERSUASION)\n            mPersuasionDialog.setVisible(true);\n        else if (dialogueChoiceType == mwmp::DialogueChoiceType::COMPANION_SHARE)\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr);\n        else\n        {\n            MWBase::DialogueManager* dialogueManager = MWBase::Environment::get().getDialogueManager();\n\n            if (dialogueChoiceType == mwmp::DialogueChoiceType::BARTER && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Barter))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);\n            else if (dialogueChoiceType == mwmp::DialogueChoiceType::SPELLS && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Spells))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr);\n            else if (dialogueChoiceType == mwmp::DialogueChoiceType::TRAVEL && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Travel))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr);\n            else if (dialogueChoiceType == mwmp::DialogueChoiceType::SPELLMAKING && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Spellmaking))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr);\n            else if (dialogueChoiceType == mwmp::DialogueChoiceType::ENCHANTING && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Enchanting))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr);\n            else if (dialogueChoiceType == mwmp::DialogueChoiceType::TRAINING && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Training))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr);\n            else if (dialogueChoiceType == mwmp::DialogueChoiceType::REPAIR && !dialogueManager->checkServiceRefused(mCallback.get(), MWBase::DialogueManager::Repair))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr);\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the Ptr of the actor involved in the dialogue\n    */\n    MWWorld::Ptr DialogueWindow::getPtr()\n    {\n        return mPtr;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void DialogueWindow::setPtr(const MWWorld::Ptr& actor)\n    {\n        if (!actor.getClass().isActor())\n        {\n            Log(Debug::Warning) << \"Warning: can not talk with non-actor object.\";\n            return;\n        }\n\n        bool sameActor = (mPtr == actor);\n        if (!sameActor)\n        {\n            // The history is not reset here\n            mKeywords.clear();\n            mTopicsList->clear();\n            for (Link* link : mLinks)\n                mDeleteLater.push_back(link); // Links are not deleted right away to prevent issues with event handlers\n            mLinks.clear();\n        }\n\n        mPtr = actor;\n        mGoodbye = false;\n        mTopicsList->setEnabled(true);\n\n        if (!MWBase::Environment::get().getDialogueManager()->startDialogue(actor, mGreetingCallback.get()))\n        {\n            // No greetings found. The dialogue window should not be shown.\n            // If this is a companion, we must show the companion window directly (used by BM_bear_be_unique).\n            MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);\n            mPtr = MWWorld::Ptr();\n            if (isCompanion(actor))\n                MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion, actor);\n            return;\n        }\n\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);\n\n        setTitle(mPtr.getClass().getName(mPtr));\n\n        updateTopics();\n        updateTopicsPane(); // force update for new services\n\n        updateDisposition();\n        restock();\n    }\n\n    void DialogueWindow::restock()\n    {\n        MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr);\n        float delay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fBarterGoldResetDelay\")->mValue.getFloat();\n\n        // Gold is restocked every 24h\n        if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay)\n        {\n            /*\n                Start of tes3mp change (major)\n\n                Instead of restocking the NPC's gold pool or last restock time here, send a packet about them to the server\n            */\n            /*\n            sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr));\n\n            sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp());\n            */\n            mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectMiscellaneous(mPtr, mPtr.getClass().getBaseGold(mPtr), MWBase::Environment::get().getWorld()->getTimeStamp().getHour(),\n                MWBase::Environment::get().getWorld()->getTimeStamp().getDay());\n            objectList->sendObjectMiscellaneous();\n            /*\n                End of tes3mp change (major)\n            */\n        }\n    }\n\n    void DialogueWindow::deleteLater()\n    {\n        for (Link* link : mDeleteLater)\n            delete link;\n        mDeleteLater.clear();\n    }\n\n    void DialogueWindow::onClose()\n    {\n        if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Dialogue))\n            return;\n        // Reset history\n        for (DialogueText* text : mHistoryContents)\n            delete text;\n        mHistoryContents.clear();\n    }\n\n    bool DialogueWindow::setKeywords(std::list<std::string> keyWords)\n    {\n        if (mKeywords == keyWords && isCompanion() == mIsCompanion)\n            return false;\n        mIsCompanion = isCompanion();\n        mKeywords = keyWords;\n        updateTopicsPane();\n        return true;\n    }\n\n    void DialogueWindow::updateTopicsPane()\n    {\n        mTopicsList->clear();\n        for (auto& linkPair : mTopicLinks)\n            mDeleteLater.push_back(linkPair.second);\n        mTopicLinks.clear();\n        mKeywordSearch.clear();\n\n        int services = mPtr.getClass().getServices(mPtr);\n\n        bool travel = (mPtr.getTypeName() == typeid(ESM::NPC).name() && !mPtr.get<ESM::NPC>()->mBase->getTransport().empty())\n                || (mPtr.getTypeName() == typeid(ESM::Creature).name() && !mPtr.get<ESM::Creature>()->mBase->getTransport().empty());\n\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        if (mPtr.getTypeName() == typeid(ESM::NPC).name())\n            mTopicsList->addItem(gmst.find(\"sPersuasion\")->mValue.getString());\n\n        if (services & ESM::NPC::AllItems)\n            mTopicsList->addItem(gmst.find(\"sBarter\")->mValue.getString());\n\n        if (services & ESM::NPC::Spells)\n            mTopicsList->addItem(gmst.find(\"sSpells\")->mValue.getString());\n\n        if (travel)\n            mTopicsList->addItem(gmst.find(\"sTravel\")->mValue.getString());\n\n        if (services & ESM::NPC::Spellmaking)\n            mTopicsList->addItem(gmst.find(\"sSpellmakingMenuTitle\")->mValue.getString());\n\n        if (services & ESM::NPC::Enchanting)\n            mTopicsList->addItem(gmst.find(\"sEnchanting\")->mValue.getString());\n\n        if (services & ESM::NPC::Training)\n            mTopicsList->addItem(gmst.find(\"sServiceTrainingTitle\")->mValue.getString());\n\n        if (services & ESM::NPC::Repair)\n            mTopicsList->addItem(gmst.find(\"sRepair\")->mValue.getString());\n\n        if (isCompanion())\n            mTopicsList->addItem(gmst.find(\"sCompanionShare\")->mValue.getString());\n\n        if (mTopicsList->getItemCount() > 0)\n            mTopicsList->addSeparator();\n\n\n        for(const auto& keyword : mKeywords)\n        {\n            std::string topicId = Misc::StringUtils::lowerCase(keyword);\n            mTopicsList->addItem(keyword);\n\n            Topic* t = new Topic(keyword);\n            /*\n                Start of tes3mp change (major)\n\n                Instead of running DialogueWindow::onSelectListItem() when clicking a highlighted topic, run\n                onSendDialoguePacket() so the server can approve or deny a dialogue choice\n            */\n            //t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);\n            t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::sendDialogueChoicePacket);\n            /*\n                End of tes3mp change (major)\n            */\n            \n            mTopicLinks[topicId] = t;\n\n            mKeywordSearch.seed(topicId, intptr_t(t));\n        }\n        mTopicsList->adjustSize();\n\n        updateHistory();\n        // The topics list has been regenerated so topic formatting needs to be updated\n        updateTopicFormat();\n    }\n\n    void DialogueWindow::updateHistory(bool scrollbar)\n    {\n        if (!scrollbar && mScrollBar->getVisible())\n        {\n            mHistory->setSize(mHistory->getSize()+MyGUI::IntSize(mScrollBar->getWidth(),0));\n            mScrollBar->setVisible(false);\n        }\n        if (scrollbar && !mScrollBar->getVisible())\n        {\n            mHistory->setSize(mHistory->getSize()-MyGUI::IntSize(mScrollBar->getWidth(),0));\n            mScrollBar->setVisible(true);\n        }\n\n        BookTypesetter::Ptr typesetter = BookTypesetter::create (mHistory->getWidth(), std::numeric_limits<int>::max());\n\n        for (DialogueText* text : mHistoryContents)\n            text->write(typesetter, &mKeywordSearch, mTopicLinks);\n\n        BookTypesetter::Style* body = typesetter->createStyle(\"\", MyGUI::Colour::White, false);\n\n        typesetter->sectionBreak(9);\n        // choices\n        const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();\n        mChoices = MWBase::Environment::get().getDialogueManager()->getChoices();\n        for (std::pair<std::string, int>& choice : mChoices)\n        {\n            Choice* link = new Choice(choice.second);\n            link->eventChoiceActivated += MyGUI::newDelegate(this, &DialogueWindow::onChoiceActivated);\n            mLinks.push_back(link);\n\n            typesetter->lineBreak();\n            BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver,\n                                                                              textColours.answerPressed,\n                                                                              TypesetBook::InteractiveId(link));\n            typesetter->write(questionStyle, to_utf8_span(choice.first.c_str()));\n        }\n\n        mGoodbye = MWBase::Environment::get().getDialogueManager()->isGoodbye();\n        if (mGoodbye)\n        {\n            Goodbye* link = new Goodbye();\n            link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated);\n            mLinks.push_back(link);\n            std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sGoodbye\")->mValue.getString();\n            BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver,\n                                                                              textColours.answerPressed,\n                                                                              TypesetBook::InteractiveId(link));\n            typesetter->lineBreak();\n            typesetter->write(questionStyle, to_utf8_span(goodbye.c_str()));\n        }\n\n        TypesetBook::Ptr book = typesetter->complete();\n        mHistory->showPage(book, 0);\n        size_t viewHeight = mHistory->getParent()->getHeight();\n        if (!scrollbar && book->getSize().second > viewHeight)\n            updateHistory(true);\n        else if (scrollbar)\n        {\n            mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second));\n            size_t range = book->getSize().second - viewHeight;\n            mScrollBar->setScrollRange(range);\n            mScrollBar->setScrollPosition(range-1);\n            mScrollBar->setTrackSize(static_cast<int>(viewHeight / static_cast<float>(book->getSize().second) * mScrollBar->getLineSize()));\n            onScrollbarMoved(mScrollBar, range-1);\n        }\n        else\n        {\n            // no scrollbar\n            onScrollbarMoved(mScrollBar, 0);\n        }\n\n        bool goodbyeEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() || mGoodbye;\n        bool goodbyeWasEnabled = mGoodbyeButton->getEnabled();\n        mGoodbyeButton->setEnabled(goodbyeEnabled);\n        if (goodbyeEnabled && !goodbyeWasEnabled)\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);\n\n        bool topicsEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() && !mGoodbye;\n        mTopicsList->setEnabled(topicsEnabled);\n    }\n\n    void DialogueWindow::notifyLinkClicked (TypesetBook::InteractiveId link)\n    {\n        reinterpret_cast<Link*>(link)->activated();\n    }\n\n    void DialogueWindow::onTopicActivated(const std::string &topicId)\n    {\n        if (mGoodbye)\n            return;\n\n        MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId, mCallback.get());\n        updateTopics();\n    }\n\n    void DialogueWindow::onChoiceActivated(int id)\n    {\n        if (mGoodbye)\n        {\n            onGoodbyeActivated();\n            return;\n        }\n        MWBase::Environment::get().getDialogueManager()->questionAnswered(id, mCallback.get());\n        updateTopics();\n    }\n\n    void DialogueWindow::onGoodbyeActivated()\n    {\n        MWBase::Environment::get().getDialogueManager()->goodbyeSelected();\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);\n        resetReference();\n    }\n\n    void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos)\n    {\n        mHistory->setPosition(0, static_cast<int>(pos) * -1);\n    }\n\n    void DialogueWindow::addResponse(const std::string &title, const std::string &text, bool needMargin)\n    {\n        mHistoryContents.push_back(new Response(text, title, needMargin));\n        updateHistory();\n    }\n\n    void DialogueWindow::addMessageBox(const std::string& text)\n    {\n        mHistoryContents.push_back(new Message(text));\n        updateHistory();\n    }\n\n    void DialogueWindow::updateDisposition()\n    {\n        bool dispositionVisible = false;\n        if (!mPtr.isEmpty() && mPtr.getClass().isNpc())\n        {\n            dispositionVisible = true;\n            mDispositionBar->setProgressRange(100);\n            mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr));\n            mDispositionText->setCaption(MyGUI::utility::toString(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string(\"/100\"));\n        }\n\n        bool dispositionWasVisible = mDispositionBar->getVisible();\n\n        if (dispositionVisible && !dispositionWasVisible)\n        {\n            mDispositionBar->setVisible(true);\n            int offset = mDispositionBar->getHeight()+5;\n            mTopicsList->setCoord(mTopicsList->getCoord() + MyGUI::IntCoord(0,offset,0,-offset));\n            mTopicsList->adjustSize();\n        }\n        else if (!dispositionVisible && dispositionWasVisible)\n        {\n            mDispositionBar->setVisible(false);\n            int offset = mDispositionBar->getHeight()+5;\n            mTopicsList->setCoord(mTopicsList->getCoord() - MyGUI::IntCoord(0,offset,0,-offset));\n            mTopicsList->adjustSize();\n        }\n    }\n\n    void DialogueWindow::onReferenceUnavailable()\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);\n    }\n\n    void DialogueWindow::onFrame(float dt)\n    {\n        checkReferenceAvailable();\n        if (mPtr.isEmpty())\n            return;\n\n        updateDisposition();\n        deleteLater();\n\n        if (mChoices != MWBase::Environment::get().getDialogueManager()->getChoices()\n                || mGoodbye != MWBase::Environment::get().getDialogueManager()->isGoodbye())\n            updateHistory();\n    }\n\n    void DialogueWindow::updateTopicFormat()\n    {\n        if (!Settings::Manager::getBool(\"color topic enable\", \"GUI\"))\n            return;\n\n        std::string specialColour = Settings::Manager::getString(\"color topic specific\", \"GUI\");\n        std::string oldColour = Settings::Manager::getString(\"color topic exhausted\", \"GUI\");\n\n        for (const std::string& keyword : mKeywords)\n        {\n            int flag = MWBase::Environment::get().getDialogueManager()->getTopicFlag(keyword);\n            MyGUI::Button* button = mTopicsList->getItemWidget(keyword);\n\n            if (!specialColour.empty() && flag & MWBase::DialogueManager::TopicType::Specific)\n                button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(specialColour));\n            else if (!oldColour.empty() && flag & MWBase::DialogueManager::TopicType::Exhausted)\n                button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(oldColour));\n        }\n    }\n\n    void DialogueWindow::updateTopics()\n    {\n        // Topic formatting needs to be updated regardless of whether the topic list has changed\n        if (!setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()))\n            updateTopicFormat();\n    }\n\n    bool DialogueWindow::isCompanion()\n    {\n        return isCompanion(mPtr);\n    }\n\n    bool DialogueWindow::isCompanion(const MWWorld::Ptr& actor)\n    {\n        if (actor.isEmpty())\n            return false;\n\n        return !actor.getClass().getScript(actor).empty()\n                && actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), \"companion\");\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/dialogue.hpp",
    "content": "#ifndef MWGUI_DIALOGE_H\n#define MWGUI_DIALOGE_H\n\n#include \"windowbase.hpp\"\n#include \"referenceinterface.hpp\"\n\n#include \"bookpage.hpp\"\n\n#include \"../mwdialogue/keywordsearch.hpp\"\n\n#include <MyGUI_Delegate.h>\n\nnamespace Gui\n{\n    class MWList;\n}\n\nnamespace MWGui\n{\n    class ResponseCallback;\n\n    class PersuasionDialog : public WindowModal\n    {\n    public:\n        PersuasionDialog(ResponseCallback* callback);\n\n        void onOpen() override;\n\n        MyGUI::Widget* getDefaultKeyFocus() override;\n\n    private:\n        std::unique_ptr<ResponseCallback> mCallback;\n\n        MyGUI::Button* mCancelButton;\n        MyGUI::Button* mAdmireButton;\n        MyGUI::Button* mIntimidateButton;\n        MyGUI::Button* mTauntButton;\n        MyGUI::Button* mBribe10Button;\n        MyGUI::Button* mBribe100Button;\n        MyGUI::Button* mBribe1000Button;\n        MyGUI::TextBox* mGoldLabel;\n\n        void onCancel (MyGUI::Widget* sender);\n        void onPersuade (MyGUI::Widget* sender);\n    };\n\n\n    struct Link\n    {\n        virtual ~Link() {}\n        virtual void activated () = 0;\n    };\n\n    struct Topic : Link\n    {\n        typedef MyGUI::delegates::CMultiDelegate1<const std::string&> EventHandle_TopicId;\n        EventHandle_TopicId eventTopicActivated;\n        Topic(const std::string& id) : mTopicId(id) {}\n        std::string mTopicId;\n        void activated () override;\n    };\n\n    struct Choice : Link\n    {\n        typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_ChoiceId;\n        EventHandle_ChoiceId eventChoiceActivated;\n        Choice(int id) : mChoiceId(id) {}\n        int mChoiceId;\n        void activated () override;\n    };\n\n    struct Goodbye : Link\n    {\n        typedef MyGUI::delegates::CMultiDelegate0 Event_Activated;\n        Event_Activated eventActivated;\n        void activated () override;\n    };\n\n    typedef MWDialogue::KeywordSearch <std::string, intptr_t> KeywordSearchT;\n\n    struct DialogueText\n    {\n        virtual ~DialogueText() {}\n        virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const = 0;\n        std::string mText;\n    };\n\n    struct Response : DialogueText\n    {\n        Response(const std::string& text, const std::string& title = \"\", bool needMargin = true);\n        void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const override;\n        void addTopicLink (BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const;\n        std::string mTitle;\n        bool mNeedMargin;\n    };\n\n    struct Message : DialogueText\n    {\n        Message(const std::string& text);\n        void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const override;\n    };\n\n    class DialogueWindow: public WindowBase, public ReferenceInterface\n    {\n    public:\n        DialogueWindow();\n        ~DialogueWindow();\n\n        void onTradeComplete();\n\n        bool exit() override;\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        void notifyLinkClicked (TypesetBook::InteractiveId link);\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to activate any dialogue choice from elsewhere in the code\n        */\n        void activateDialogueChoice(unsigned char dialogueChoiceType, std::string topic = \"\");\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to get the Ptr of the actor involved in the dialogue\n        */\n        MWWorld::Ptr getPtr();\n        /*\n            End of tes3mp addition\n        */\n\n        void setPtr(const MWWorld::Ptr& actor) override;\n\n        /// @return true if stale keywords were updated successfully\n        bool setKeywords(std::list<std::string> keyWord);\n\n        void addResponse (const std::string& title, const std::string& text, bool needMargin = true);\n\n        void addMessageBox(const std::string& text);\n\n        void onFrame(float dt) override;\n        void clear() override { resetReference(); }\n\n        void updateTopics();\n\n        void onClose() override;\n\n    protected:\n        void updateTopicsPane();\n        bool isCompanion(const MWWorld::Ptr& actor);\n        bool isCompanion();\n\n        /*\n            Start of tes3mp addition\n\n            A different event that should be used in multiplayer when clicking on choices\n            in the dialogue screen, sending DialogueChoice packets to the server so they can\n            be approved or denied\n        */\n        void sendDialogueChoicePacket(const std::string& topic);\n        /*\n            End of tes3mp addition\n        */\n\n        void onSelectListItem(const std::string& topic, int id);\n        void onByeClicked(MyGUI::Widget* _sender);\n        void onMouseWheel(MyGUI::Widget* _sender, int _rel);\n        void onWindowResize(MyGUI::Window* _sender);\n        void onTopicActivated(const std::string& topicId);\n        void onChoiceActivated(int id);\n        void onGoodbyeActivated();\n\n        void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos);\n\n        void updateHistory(bool scrollbar=false);\n\n        void onReferenceUnavailable() override;\n\n    private:\n        void updateDisposition();\n        void restock();\n        void deleteLater();\n\n        bool mIsCompanion;\n        std::list<std::string> mKeywords;\n\n        std::vector<DialogueText*> mHistoryContents;\n        std::vector<std::pair<std::string, int> > mChoices;\n        bool mGoodbye;\n\n        std::vector<Link*> mLinks;\n        std::map<std::string, Link*> mTopicLinks;\n\n        std::vector<Link*> mDeleteLater;\n\n        KeywordSearchT mKeywordSearch;\n\n        BookPage* mHistory;\n        Gui::MWList*   mTopicsList;\n        MyGUI::ScrollBar* mScrollBar;\n        MyGUI::ProgressBar* mDispositionBar;\n        MyGUI::TextBox*     mDispositionText;\n        MyGUI::Button* mGoodbyeButton;\n\n        PersuasionDialog mPersuasionDialog;\n\n        MyGUI::IntSize mCurrentWindowSize;\n\n        std::unique_ptr<ResponseCallback> mCallback;\n        std::unique_ptr<ResponseCallback> mGreetingCallback;\n\n        void updateTopicFormat();\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/draganddrop.cpp",
    "content": "#include \"draganddrop.hpp\"\n\n#include <MyGUI_Gui.h>\n#include <MyGUI_ControllerManager.h>\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"sortfilteritemmodel.hpp\"\n#include \"inventorywindow.hpp\"\n#include \"itemwidget.hpp\"\n#include \"itemview.hpp\"\n#include \"controllers.hpp\"\n\nnamespace MWGui\n{\n\n\nDragAndDrop::DragAndDrop()\n    : mIsOnDragAndDrop(false)\n    , mDraggedWidget(nullptr)\n    , mSourceModel(nullptr)\n    , mSourceView(nullptr)\n    , mSourceSortModel(nullptr)\n    , mDraggedCount(0)\n{\n}\n\nvoid DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count)\n{\n    mItem = sourceModel->getItem(index);\n    mDraggedCount = count;\n    mSourceModel = sourceModel;\n    mSourceView = sourceView;\n    mSourceSortModel = sortModel;\n\n    // If picking up an item that isn't from the player's inventory, the item gets added to player inventory backend\n    // immediately, even though it's still floating beneath the mouse cursor. A bit counterintuitive,\n    // but this is how it works in vanilla, and not doing so would break quests (BM_beasts for instance).\n    ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel();\n    if (mSourceModel != playerModel)\n    {\n        MWWorld::Ptr item = mSourceModel->moveItem(mItem, mDraggedCount, playerModel);\n\n        playerModel->update();\n\n        ItemModel::ModelIndex newIndex = -1;\n        for (unsigned int i=0; i<playerModel->getItemCount(); ++i)\n        {\n            if (playerModel->getItem(i).mBase == item)\n            {\n                newIndex = i;\n                break;\n            }\n        }\n        mItem = playerModel->getItem(newIndex);\n        mSourceModel = playerModel;\n\n        SortFilterItemModel* playerFilterModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getSortFilterModel();\n        mSourceSortModel = playerFilterModel;\n    }\n\n    std::string sound = mItem.mBase.getClass().getUpSoundId(mItem.mBase);\n    MWBase::Environment::get().getWindowManager()->playSound (sound);\n\n    if (mSourceSortModel)\n    {\n        mSourceSortModel->clearDragItems();\n        mSourceSortModel->addDragItem(mItem.mBase, count);\n    }\n\n    ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget<ItemWidget>(\"MW_ItemIcon\", 0, 0, 42, 42, MyGUI::Align::Default, \"DragAndDrop\");\n\n    Controllers::ControllerFollowMouse* controller =\n            MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerFollowMouse::getClassTypeName())\n            ->castType<Controllers::ControllerFollowMouse>();\n    MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller);\n\n    mDraggedWidget = baseWidget;\n    baseWidget->setItem(mItem.mBase);\n    baseWidget->setNeedMouseFocus(false);\n    baseWidget->setCount(count);\n\n    sourceView->update();\n\n    MWBase::Environment::get().getWindowManager()->setDragDrop(true);\n\n    mIsOnDragAndDrop = true;\n}\n\nvoid DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView)\n{\n    std::string sound = mItem.mBase.getClass().getDownSoundId(mItem.mBase);\n    MWBase::Environment::get().getWindowManager()->playSound(sound);\n\n    // We can't drop a conjured item to the ground; the target container should always be the source container\n    if (mItem.mFlags & ItemStack::Flag_Bound && targetModel != mSourceModel)\n    {\n        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sBarterDialog12}\");\n        return;\n    }\n\n    // If item is dropped where it was taken from, we don't need to do anything -\n    // otherwise, do the transfer\n    if (targetModel != mSourceModel)\n    {\n        mSourceModel->moveItem(mItem, mDraggedCount, targetModel);\n    }\n\n    mSourceModel->update();\n\n    finish();\n    if (targetView)\n        targetView->update();\n\n    MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();\n\n    // We need to update the view since an other item could be auto-equipped.\n    mSourceView->update();\n}\n\nvoid DragAndDrop::onFrame()\n{\n    if (mIsOnDragAndDrop && mItem.mBase.getRefData().getCount() == 0)\n        finish();\n}\n\n/*\n    Start of tes3mp change (minor)\n\n    Add a deleteDragItems argument that allows the deletion of the\n    items in the drag as oppposed to the regular behavior of returning\n    them to their source model\n\n    This is required to reduce unpredictable behavior for drags approved\n    or rejected by the server\n*/\nvoid DragAndDrop::finish(bool deleteDragItems)\n/*\n    End of tes3mp change (minor)\n*/\n{\n    mIsOnDragAndDrop = false;\n    mSourceSortModel->clearDragItems();\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to entirely delete the items in the drag\n    */\n    if (deleteDragItems)\n        mSourceModel->removeItem(mItem, mDraggedCount);\n    /*\n        End of tes3mp addition\n    */\n\n    // since mSourceView doesn't get updated in drag()\n    MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();\n\n    MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget);\n    mDraggedWidget = nullptr;\n    MWBase::Environment::get().getWindowManager()->setDragDrop(false);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/draganddrop.hpp",
    "content": "#ifndef OPENMW_MWGUI_DRAGANDDROP_H\n#define OPENMW_MWGUI_DRAGANDDROP_H\n\n#include \"itemmodel.hpp\"\n\nnamespace MyGUI\n{\n    class Widget;\n}\n\nnamespace MWGui\n{\n\n    class ItemView;\n    class SortFilterItemModel;\n\n    class DragAndDrop\n    {\n    public:\n        bool mIsOnDragAndDrop;\n        MyGUI::Widget* mDraggedWidget;\n        ItemModel* mSourceModel;\n        ItemView* mSourceView;\n        SortFilterItemModel* mSourceSortModel;\n        ItemStack mItem;\n        int mDraggedCount;\n\n        DragAndDrop();\n\n        void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count);\n        void drop (ItemModel* targetModel, ItemView* targetView);\n        void onFrame();\n\n        /*\n            Start of tes3mp change (minor)\n\n            Add a deleteDragItems argument that allows the deletion of the\n            items in the drag as oppposed to the regular behavior of returning\n            them to their source model\n\n            This is required to reduce unpredictable behavior for drags approved\n            or rejected by the server\n        */\n        void finish(bool deleteDragItems = false);\n        /*\n            End of tes3mp change (minor)\n        */\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/enchantingdialog.cpp",
    "content": "#include \"enchantingdialog.hpp\"\n\n#include <iomanip>\n\n#include <MyGUI_Button.h>\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_EditBox.h>\n\n#include <components/widgets/list.hpp>\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"itemselection.hpp\"\n#include \"itemwidget.hpp\"\n\n#include \"sortfilteritemmodel.hpp\"\n\nnamespace MWGui\n{\n\n\n    EnchantingDialog::EnchantingDialog()\n        : WindowBase(\"openmw_enchanting_dialog.layout\")\n        , EffectEditorBase(EffectEditorBase::Enchanting)\n        , mItemSelectionDialog(nullptr)\n    {\n        getWidget(mName, \"NameEdit\");\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mAvailableEffectsList, \"AvailableEffects\");\n        getWidget(mUsedEffectsView, \"UsedEffects\");\n        getWidget(mItemBox, \"ItemBox\");\n        getWidget(mSoulBox, \"SoulBox\");\n        getWidget(mEnchantmentPoints, \"Enchantment\");\n        getWidget(mCastCost, \"CastCost\");\n        getWidget(mCharge, \"Charge\");\n        getWidget(mSuccessChance, \"SuccessChance\");\n        getWidget(mChanceLayout, \"ChanceLayout\");\n        getWidget(mTypeButton, \"TypeButton\");\n        getWidget(mBuyButton, \"BuyButton\");\n        getWidget(mPrice, \"PriceLabel\");\n        getWidget(mPriceText, \"PriceTextLabel\");\n\n        setWidgets(mAvailableEffectsList, mUsedEffectsView);\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onCancelButtonClicked);\n        mItemBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectItem);\n        mSoulBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectSoul);\n        mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked);\n        mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked);\n        mName->eventEditSelectAccept += MyGUI::newDelegate(this, &EnchantingDialog::onAccept);\n    }\n\n    EnchantingDialog::~EnchantingDialog()\n    {\n        delete mItemSelectionDialog;\n    }\n\n    void EnchantingDialog::onOpen()\n    {\n        center();\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mName);\n    }\n\n    void EnchantingDialog::setSoulGem(const MWWorld::Ptr &gem)\n    {\n        if (gem.isEmpty())\n        {\n            mSoulBox->setItem(MWWorld::Ptr());\n            mSoulBox->clearUserStrings();\n            mEnchanting.setSoulGem(MWWorld::Ptr());\n        }\n        else\n        {\n            mSoulBox->setItem(gem);\n            mSoulBox->setUserString (\"ToolTipType\", \"ItemPtr\");\n            mSoulBox->setUserData(MWWorld::Ptr(gem));\n            mEnchanting.setSoulGem(gem);\n        }\n    }\n\n    void EnchantingDialog::setItem(const MWWorld::Ptr &item)\n    {\n        if (item.isEmpty())\n        {\n            mItemBox->setItem(MWWorld::Ptr());\n            mItemBox->clearUserStrings();\n            mEnchanting.setOldItem(MWWorld::Ptr());\n        }\n        else\n        {\n            mName->setCaption(item.getClass().getName(item));\n            mItemBox->setItem(item);\n            mItemBox->setUserString (\"ToolTipType\", \"ItemPtr\");\n            mItemBox->setUserData(MWWorld::Ptr(item));\n            mEnchanting.setOldItem(item);\n        }\n    }\n\n    void EnchantingDialog::updateLabels()\n    {\n        mEnchantmentPoints->setCaption(std::to_string(static_cast<int>(mEnchanting.getEnchantPoints(false))) + \" / \" + std::to_string(mEnchanting.getMaxEnchantValue()));\n        mCharge->setCaption(std::to_string(mEnchanting.getGemCharge()));\n        mSuccessChance->setCaption(std::to_string(std::max(0, std::min(100, mEnchanting.getEnchantChance()))));\n        mCastCost->setCaption(std::to_string(mEnchanting.getEffectiveCastCost()));\n        mPrice->setCaption(std::to_string(mEnchanting.getEnchantPrice()));\n\n        switch(mEnchanting.getCastStyle())\n        {\n            case ESM::Enchantment::CastOnce:\n                mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sItemCastOnce\",\"Cast Once\"));\n                setConstantEffect(false);\n                break;\n            case ESM::Enchantment::WhenStrikes:\n                mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sItemCastWhenStrikes\", \"When Strikes\"));\n                setConstantEffect(false);\n                break;\n            case ESM::Enchantment::WhenUsed:\n                mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sItemCastWhenUsed\", \"When Used\"));\n                setConstantEffect(false);\n                break;\n            case ESM::Enchantment::ConstantEffect:\n                mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sItemCastConstant\", \"Cast Constant\"));\n                setConstantEffect(true);\n                break;\n        }\n    }\n\n    void EnchantingDialog::setPtr (const MWWorld::Ptr& ptr)\n    {\n        mName->setCaption(\"\");\n\n        if (ptr.getClass().isActor())\n        {\n            mEnchanting.setSelfEnchanting(false);\n            mEnchanting.setEnchanter(ptr);\n            mBuyButton->setCaptionWithReplacing(\"#{sBuy}\");\n            mChanceLayout->setVisible(false);\n            mPtr = ptr;\n            setSoulGem(MWWorld::Ptr());\n            mPrice->setVisible(true);\n            mPriceText->setVisible(true);\n        }\n        else\n        {\n            mEnchanting.setSelfEnchanting(true);\n            mEnchanting.setEnchanter(MWMechanics::getPlayer());\n            mBuyButton->setCaptionWithReplacing(\"#{sCreate}\");\n            bool enabled = Settings::Manager::getBool(\"show enchant chance\",\"Game\");\n            mChanceLayout->setVisible(enabled);\n            mPtr = MWMechanics::getPlayer();\n            setSoulGem(ptr);\n            mPrice->setVisible(false);\n            mPriceText->setVisible(false);\n        }\n\n        setItem(MWWorld::Ptr());\n        startEditing ();\n        updateLabels();\n    }\n\n    void EnchantingDialog::onReferenceUnavailable ()\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);\n        resetReference();\n    }\n\n    void EnchantingDialog::resetReference()\n    {\n        ReferenceInterface::resetReference();\n        setItem(MWWorld::Ptr());\n        setSoulGem(MWWorld::Ptr());\n        mPtr = MWWorld::Ptr();\n        mEnchanting.setEnchanter(MWWorld::Ptr());\n    }\n\n    void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Enchanting);\n    }\n\n    void EnchantingDialog::onSelectItem(MyGUI::Widget *sender)\n    {\n        if (mEnchanting.getOldItem().isEmpty())\n        {\n            delete mItemSelectionDialog;\n            mItemSelectionDialog = new ItemSelectionDialog(\"#{sEnchantItems}\");\n            mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onItemSelected);\n            mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onItemCancel);\n            mItemSelectionDialog->setVisible(true);\n            mItemSelectionDialog->openContainer(MWMechanics::getPlayer());\n            mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyEnchantable);\n        }\n        else\n        {\n            setItem(MWWorld::Ptr());\n            updateLabels();\n        }\n    }\n\n    void EnchantingDialog::onItemSelected(MWWorld::Ptr item)\n    {\n        mItemSelectionDialog->setVisible(false);\n\n        setItem(item);\n        MWBase::Environment::get().getWindowManager()->playSound(item.getClass().getDownSoundId(item));\n        mEnchanting.nextCastStyle();\n        updateLabels();\n    }\n\n    void EnchantingDialog::onItemCancel()\n    {\n        mItemSelectionDialog->setVisible(false);\n    }\n\n    void EnchantingDialog::onSoulSelected(MWWorld::Ptr item)\n    {\n        mItemSelectionDialog->setVisible(false);\n\n        mEnchanting.setSoulGem(item);\n        if(mEnchanting.getGemCharge()==0)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage32}\");\n            return;\n        }\n\n        setSoulGem(item);\n        MWBase::Environment::get().getWindowManager()->playSound(item.getClass().getDownSoundId(item));\n        updateLabels();\n    }\n\n    void EnchantingDialog::onSoulCancel()\n    {\n        mItemSelectionDialog->setVisible(false);\n    }\n\n    void EnchantingDialog::onSelectSoul(MyGUI::Widget *sender)\n    {\n        if (mEnchanting.getGem().isEmpty())\n        {\n            delete mItemSelectionDialog;\n            mItemSelectionDialog = new ItemSelectionDialog(\"#{sSoulGemsWithSouls}\");\n            mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onSoulSelected);\n            mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onSoulCancel);\n            mItemSelectionDialog->setVisible(true);\n            mItemSelectionDialog->openContainer(MWMechanics::getPlayer());\n            mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones);\n\n            //MWBase::Environment::get().getWindowManager()->messageBox(\"#{sInventorySelectNoSoul}\");\n        }\n        else\n        {\n            setSoulGem(MWWorld::Ptr());\n            mEnchanting.nextCastStyle();\n            updateLabels();\n            updateEffectsView();\n        }\n    }\n\n    void EnchantingDialog::notifyEffectsChanged ()\n    {\n        mEffectList.mList = mEffects;\n        mEnchanting.setEffect(mEffectList);\n        updateLabels();\n    }\n\n    void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender)\n    {\n        mEnchanting.nextCastStyle();\n        updateLabels();\n        updateEffectsView();\n    }\n\n    void EnchantingDialog::onAccept(MyGUI::EditBox *sender)\n    {\n        onBuyButtonClicked(sender);\n\n        // To do not spam onAccept() again and again\n        MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);\n    }\n\n    void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender)\n    {\n        if (mEffects.size() <= 0)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sEnchantmentMenu11}\");\n            return;\n        }\n\n        if (mName->getCaption ().empty())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage10}\");\n            return;\n        }\n\n        if (mEnchanting.soulEmpty())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage52}\");\n            return;\n        }\n\n        if (mEnchanting.itemEmpty())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage11}\");\n            return;\n        }\n\n        if (static_cast<int>(mEnchanting.getEnchantPoints(false)) > mEnchanting.getMaxEnchantValue())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage29}\");\n            return;\n        }\n\n        mEnchanting.setNewItemName(mName->getCaption());\n        mEnchanting.setEffect(mEffectList);\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n        if (mPtr != player && mEnchanting.getEnchantPrice() > playerGold)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage18}\");\n            return;\n        }\n\n        // check if the player is attempting to use a soulstone or item that was stolen from this actor\n        if (mPtr != player)\n        {\n            for (int i=0; i<2; ++i)\n            {\n                MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem();\n                if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), mPtr))\n                {\n                    std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sNotifyMessage49\")->mValue.getString();\n                    msg = Misc::StringUtils::format(msg, item.getClass().getName(item));\n                    MWBase::Environment::get().getWindowManager()->messageBox(msg);\n\n                    MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, item, mPtr, 1);\n\n                    MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);\n                    MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n                    return;\n                }\n            }\n        }\n\n        int result = mEnchanting.create();\n\n        if(result==1)\n        {\n            MWBase::Environment::get().getWindowManager()->playSound(\"enchant success\");\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sEnchantmentMenu12}\");\n            MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_OBJECT_SOUND packet every time the player makes a sound here\n            */\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectSound(MWMechanics::getPlayer(), \"enchant success\", 1.0, 1.0);\n            objectList->sendObjectSound();\n            /*\n                End of tes3mp addition\n            */\n        }\n        else\n        {\n            MWBase::Environment::get().getWindowManager()->playSound(\"enchant fail\");\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage34}\");\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_OBJECT_SOUND packet every time the player makes a sound here\n            */\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectSound(MWMechanics::getPlayer(), \"enchant fail\", 1.0, 1.0);\n            objectList->sendObjectSound();\n            /*\n                End of tes3mp addition\n            */\n\n            if (!mEnchanting.getGem().isEmpty() && !mEnchanting.getGem().getRefData().getCount())\n            {\n                setSoulGem(MWWorld::Ptr());\n                mEnchanting.nextCastStyle();\n                updateLabels();\n                updateEffectsView();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/enchantingdialog.hpp",
    "content": "#ifndef MWGUI_ENCHANTINGDIALOG_H\n#define MWGUI_ENCHANTINGDIALOG_H\n\n#include \"spellcreationdialog.hpp\"\n\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/enchanting.hpp\"\n\nnamespace MWGui\n{\n\n    class ItemSelectionDialog;\n    class ItemWidget;\n\n    class EnchantingDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase\n    {\n    public:\n        EnchantingDialog();\n        virtual ~EnchantingDialog();\n\n        void onOpen() override;\n\n        void onFrame(float dt) override { checkReferenceAvailable(); }\n        void clear() override { resetReference(); }\n\n        void setSoulGem (const MWWorld::Ptr& gem);\n        void setItem (const MWWorld::Ptr& item);\n\n        /// Actor Ptr: buy enchantment from this actor\n        /// Soulgem Ptr: player self-enchant\n        void setPtr(const MWWorld::Ptr& ptr) override;\n\n        void resetReference() override;\n\n    protected:\n        void onReferenceUnavailable() override;\n        void notifyEffectsChanged() override;\n\n        void onCancelButtonClicked(MyGUI::Widget* sender);\n        void onSelectItem (MyGUI::Widget* sender);\n        void onSelectSoul (MyGUI::Widget* sender);\n\n        void onItemSelected(MWWorld::Ptr item);\n        void onItemCancel();\n        void onSoulSelected(MWWorld::Ptr item);\n        void onSoulCancel();\n        void onBuyButtonClicked(MyGUI::Widget* sender);\n        void updateLabels();\n        void onTypeButtonClicked(MyGUI::Widget* sender);\n        void onAccept(MyGUI::EditBox* sender);\n\n        ItemSelectionDialog* mItemSelectionDialog;\n\n        MyGUI::Widget* mChanceLayout;\n\n        MyGUI::Button* mCancelButton;\n        ItemWidget* mItemBox;\n        ItemWidget* mSoulBox;\n\n        MyGUI::Button* mTypeButton;\n        MyGUI::Button* mBuyButton;\n\n        MyGUI::EditBox* mName;\n        MyGUI::TextBox* mEnchantmentPoints;\n        MyGUI::TextBox* mCastCost;\n        MyGUI::TextBox* mCharge;\n        MyGUI::TextBox* mSuccessChance;\n        MyGUI::TextBox* mPrice;\n        MyGUI::TextBox* mPriceText;\n\n        MWMechanics::Enchanting mEnchanting;\n        ESM::EffectList mEffectList;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/exposedwindow.cpp",
    "content": "#include \"exposedwindow.hpp\"\n\nnamespace MWGui\n{\n    MyGUI::VectorWidgetPtr Window::getSkinWidgetsByName (const std::string &name)\n    {\n        return MyGUI::Widget::getSkinWidgetsByName (name);\n    }\n\n    MyGUI::Widget* Window::getSkinWidget(const std::string & _name, bool _throw)\n    {\n        MyGUI::VectorWidgetPtr widgets = getSkinWidgetsByName (_name);\n\n        if (widgets.empty())\n        {\n            MYGUI_ASSERT( ! _throw, \"widget name '\" << _name << \"' not found in skin of layout '\" << getName() << \"'\");\n            return nullptr;\n        }\n        else\n        {\n            return widgets[0];\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/exposedwindow.hpp",
    "content": "#ifndef MWGUI_EXPOSEDWINDOW_H\n#define MWGUI_EXPOSEDWINDOW_H\n\n#include <MyGUI_Window.h>\n\nnamespace MWGui\n{\n\n    /**\n     * @brief subclass to provide access to some Widget internals.\n     */\n    class Window : public MyGUI::Window\n    {\n        MYGUI_RTTI_DERIVED(Window)\n\n    public:\n        MyGUI::VectorWidgetPtr getSkinWidgetsByName (const std::string &name);\n\n        MyGUI::Widget* getSkinWidget(const std::string & _name, bool _throw = true);\n        ///< Get a widget defined in the inner skin of this window.\n    };\n\n}\n\n#endif\n\n"
  },
  {
    "path": "apps/openmw/mwgui/formatting.cpp",
    "content": "#include \"formatting.hpp\"\n\n#include <MyGUI_EditText.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_EditBox.h>\n#include <MyGUI_ImageBox.h>\n\n// correctBookartPath\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/interpreter/defines.hpp>\n#include <components/misc/stringops.hpp>\n\n#include \"../mwscript/interpretercontext.hpp\"\n\nnamespace MWGui\n{\n    namespace Formatting\n    {\n        /* BookTextParser */\n        BookTextParser::BookTextParser(const std::string & text)\n            : mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true), mClosingTag(false)\n        {\n            MWScript::InterpreterContext interpreterContext(nullptr, MWWorld::Ptr()); // empty arguments, because there is no locals or actor\n            mText = Interpreter::fixDefinesBook(mText, interpreterContext);\n\n            Misc::StringUtils::replaceAll(mText, \"\\r\", \"\");\n\n            // vanilla game does not show any text after the last EOL tag.\n            const std::string lowerText = Misc::StringUtils::lowerCase(mText);\n            size_t brIndex = lowerText.rfind(\"<br>\");\n            size_t pIndex = lowerText.rfind(\"<p>\");\n            mPlainTextEnd = 0;\n            if (brIndex != pIndex)\n            {\n                if (brIndex != std::string::npos && pIndex != std::string::npos)\n                    mPlainTextEnd = std::max(brIndex, pIndex);\n                else if (brIndex != std::string::npos)\n                    mPlainTextEnd = brIndex;\n                else\n                    mPlainTextEnd = pIndex;\n            }\n\n            registerTag(\"br\", Event_BrTag);\n            registerTag(\"p\", Event_PTag);\n            registerTag(\"img\", Event_ImgTag);\n            registerTag(\"div\", Event_DivTag);\n            registerTag(\"font\", Event_FontTag);\n        }\n\n        void BookTextParser::registerTag(const std::string & tag, BookTextParser::Events type)\n        {\n            mTagTypes[tag] = type;\n        }\n\n        std::string BookTextParser::getReadyText() const\n        {\n            return mReadyText;\n        }\n\n        BookTextParser::Events BookTextParser::next()\n        {\n            while (mIndex < mText.size())\n            {\n                char ch = mText[mIndex];\n                if (ch == '<')\n                {\n                    const size_t tagStart = mIndex + 1;\n                    const size_t tagEnd = mText.find('>', tagStart);\n                    if (tagEnd == std::string::npos)\n                        throw std::runtime_error(\"BookTextParser Error: Tag is not terminated\");\n                    parseTag(mText.substr(tagStart, tagEnd - tagStart));\n                    mIndex = tagEnd;\n\n                    if (mTagTypes.find(mTag) != mTagTypes.end())\n                    {\n                        Events type = mTagTypes.at(mTag);\n\n                        if (type == Event_BrTag || type == Event_PTag)\n                        {\n                            if (!mIgnoreNewlineTags)\n                            {\n                                if (type == Event_BrTag)\n                                    mBuffer.push_back('\\n');\n                                else\n                                {\n                                    mBuffer.append(\"\\n\\n\");\n                                }\n                            }\n                            mIgnoreLineEndings = true;\n                        }\n                        else\n                            flushBuffer();\n\n                        if (type == Event_ImgTag)\n                        {\n                            mIgnoreNewlineTags = false;\n                        }\n\n                        ++mIndex;\n                        return type;\n                    }\n                }\n                else\n                {\n                    if (!mIgnoreLineEndings || ch != '\\n')\n                    {\n                        if (mIndex < mPlainTextEnd)\n                            mBuffer.push_back(ch);\n                        mIgnoreLineEndings = false;\n                        mIgnoreNewlineTags = false;\n                    }\n                }\n\n                ++mIndex;\n            }\n\n            flushBuffer();\n            return Event_EOF;\n        }\n\n        void BookTextParser::flushBuffer()\n        {\n            mReadyText = mBuffer;\n            mBuffer.clear();\n        }\n\n        const BookTextParser::Attributes & BookTextParser::getAttributes() const\n        {\n            return mAttributes;\n        }\n\n        bool BookTextParser::isClosingTag() const\n        {\n            return mClosingTag;\n        }\n\n        void BookTextParser::parseTag(std::string tag)\n        {\n            size_t tagNameEndPos = tag.find(' ');\n            mAttributes.clear();\n            mTag = tag.substr(0, tagNameEndPos);\n            Misc::StringUtils::lowerCaseInPlace(mTag);\n            if (mTag.empty())\n                return;\n\n            mClosingTag = (mTag[0] == '/');\n            if (mClosingTag)\n            {\n                mTag.erase(mTag.begin());\n                return;\n            }\n\n            if (tagNameEndPos == std::string::npos)\n                return;\n            tag.erase(0, tagNameEndPos+1);\n\n            while (!tag.empty())\n            {\n                size_t sepPos = tag.find('=');\n                if (sepPos == std::string::npos)\n                    return;\n\n                std::string key = tag.substr(0, sepPos);\n                Misc::StringUtils::lowerCaseInPlace(key);\n                tag.erase(0, sepPos+1);\n\n                std::string value;\n\n                if (tag.empty())\n                    return;\n\n                if (tag[0] == '\"')\n                {\n                    size_t quoteEndPos = tag.find('\"', 1);\n                    if (quoteEndPos == std::string::npos)\n                        throw std::runtime_error(\"BookTextParser Error: Missing end quote in tag\");\n                    value = tag.substr(1, quoteEndPos-1);\n                    tag.erase(0, quoteEndPos+2);\n                }\n                else\n                {\n                    size_t valEndPos = tag.find(' ');\n                    if (valEndPos == std::string::npos)\n                    {\n                        value = tag;\n                        tag.erase();\n                    }\n                    else\n                    {\n                        value = tag.substr(0, valEndPos);\n                        tag.erase(0, valEndPos+1);\n                    }\n                }\n\n                mAttributes[key] = value;\n            }\n        }\n\n        /* BookFormatter */\n        Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight)\n        {\n            Paginator pag(pageWidth, pageHeight);\n\n            while (parent->getChildCount())\n            {\n                MyGUI::Gui::getInstance().destroyWidget(parent->getChildAt(0));\n            }\n\n            mTextStyle = TextStyle();\n            mBlockStyle = BlockStyle();\n\n            MyGUI::Widget * paper = parent->createWidget<MyGUI::Widget>(\"Widget\", MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top);\n            paper->setNeedMouseFocus(false);\n\n            BookTextParser parser(markup);\n\n            bool brBeforeLastTag = false;\n            bool isPrevImg = false;\n            for (;;)\n            {\n                BookTextParser::Events event = parser.next();\n                if (event == BookTextParser::Event_BrTag || event == BookTextParser::Event_PTag)\n                    continue;\n\n                std::string plainText = parser.getReadyText();\n\n                // for cases when linebreaks are used to cause a shift to the next page\n                // if the split text block ends in an empty line, proceeding text block(s) should have leading empty lines removed\n                if (pag.getIgnoreLeadingEmptyLines())\n                {\n                    while (!plainText.empty())\n                    {\n                        if (plainText[0] == '\\n')\n                            plainText.erase(plainText.begin());\n                        else\n                        {\n                            pag.setIgnoreLeadingEmptyLines(false);\n                            break;\n                        }\n                    }\n                }\n\n                if (plainText.empty())\n                    brBeforeLastTag = true;\n                else\n                {\n                    // Each block of text (between two tags / boundary and tag) will be displayed in a separate editbox widget,\n                    // which means an additional linebreak will be created between them.\n                    // ^ This is not what vanilla MW assumes, so we must deal with line breaks around tags appropriately.\n                    bool brAtStart = (plainText[0] == '\\n');\n                    bool brAtEnd = (plainText[plainText.size()-1] == '\\n');\n\n                    if (brAtStart && !brBeforeLastTag && !isPrevImg)\n                        plainText.erase(plainText.begin());\n\n                    if (plainText.size() && brAtEnd)\n                        plainText.erase(plainText.end()-1);\n\n                    if (!plainText.empty() || brBeforeLastTag || isPrevImg)\n                    {\n                        TextElement elem(paper, pag, mBlockStyle,\n                                         mTextStyle, plainText);\n                        elem.paginate();\n                    }\n\n                    brBeforeLastTag = brAtEnd;\n                }\n\n                if (event == BookTextParser::Event_EOF)\n                    break;\n\n                isPrevImg = (event == BookTextParser::Event_ImgTag);\n\n                switch (event)\n                {\n                    case BookTextParser::Event_ImgTag:\n                    {\n                        const BookTextParser::Attributes & attr = parser.getAttributes();\n\n                        if (attr.find(\"src\") == attr.end() || attr.find(\"width\") == attr.end() || attr.find(\"height\") == attr.end())\n                            continue;\n\n                        std::string src = attr.at(\"src\");\n                        int width = MyGUI::utility::parseInt(attr.at(\"width\"));\n                        int height = MyGUI::utility::parseInt(attr.at(\"height\"));\n\n                        bool exists;\n                        std::string correctedSrc = MWBase::Environment::get().getWindowManager()->correctBookartPath(src, width, height, &exists);\n\n                        if (!exists)\n                        {\n                            Log(Debug::Warning) << \"Warning: Could not find \\\"\" << src << \"\\\" referenced by an <img> tag.\";\n                            break;\n                        }\n\n                        pag.setIgnoreLeadingEmptyLines(false);\n\n                        ImageElement elem(paper, pag, mBlockStyle,\n                                          correctedSrc, width, height);\n                        elem.paginate();\n                        break;\n                    }\n                    case BookTextParser::Event_FontTag:\n                        if (parser.isClosingTag())\n                            resetFontProperties();\n                        else\n                            handleFont(parser.getAttributes());\n                        break;\n                    case BookTextParser::Event_DivTag:\n                        handleDiv(parser.getAttributes());\n                        break;\n                    default:\n                        break;\n                }\n            }\n\n            // insert last page\n            if (pag.getStartTop() != pag.getCurrentTop())\n                pag << Paginator::Page(pag.getStartTop(), pag.getStartTop() + pag.getPageHeight());\n\n            paper->setSize(paper->getWidth(), pag.getCurrentTop());\n\n            return pag.getPages();\n        }\n\n        Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, const std::string & markup)\n        {\n            return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight());\n        }\n\n        void BookFormatter::resetFontProperties()\n        {\n            mTextStyle = TextStyle();\n        }\n\n        void BookFormatter::handleDiv(const BookTextParser::Attributes & attr)\n        {\n            if (attr.find(\"align\") == attr.end())\n                return;\n\n            std::string align = attr.at(\"align\");\n\n            if (Misc::StringUtils::ciEqual(align, \"center\"))\n                mBlockStyle.mAlign = MyGUI::Align::HCenter;\n            else if (Misc::StringUtils::ciEqual(align, \"left\"))\n                mBlockStyle.mAlign = MyGUI::Align::Left;\n            else if (Misc::StringUtils::ciEqual(align, \"right\"))\n                mBlockStyle.mAlign = MyGUI::Align::Right;\n        }\n\n        void BookFormatter::handleFont(const BookTextParser::Attributes & attr)\n        {\n            if (attr.find(\"color\") != attr.end())\n            {\n                unsigned int color;\n                std::stringstream ss;\n                ss << attr.at(\"color\");\n                ss >> std::hex >> color;\n\n                mTextStyle.mColour = MyGUI::Colour(\n                    (color>>16 & 0xFF) / 255.f,\n                    (color>>8 & 0xFF) / 255.f,\n                    (color & 0xFF) / 255.f);\n            }\n            if (attr.find(\"face\") != attr.end())\n            {\n                std::string face = attr.at(\"face\");\n                mTextStyle.mFont = \"Journalbook \"+face;\n            }\n            if (attr.find(\"size\") != attr.end())\n            {\n                /// \\todo\n            }\n        }\n\n        /* GraphicElement */\n        GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle)\n            : mParent(parent), mPaginator(pag), mBlockStyle(blockStyle)\n        {\n        }\n\n        void GraphicElement::paginate()\n        {\n            int newTop = mPaginator.getCurrentTop() + getHeight();\n            while (newTop-mPaginator.getStartTop() > mPaginator.getPageHeight())\n            {\n                int newStartTop = pageSplit();\n                mPaginator << Paginator::Page(mPaginator.getStartTop(), newStartTop);\n                mPaginator.setStartTop(newStartTop);\n            }\n\n            mPaginator.setCurrentTop(newTop);\n        }\n\n        int GraphicElement::pageSplit()\n        {\n            return mPaginator.getStartTop() + mPaginator.getPageHeight();\n        }\n\n        /* TextElement */\n        TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,\n                                 const TextStyle & textStyle, const std::string & text)\n            : GraphicElement(parent, pag, blockStyle),\n              mTextStyle(textStyle)\n        {\n            Gui::EditBox* box = parent->createWidget<Gui::EditBox>(\"NormalText\",\n                MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top,\n                parent->getName() + MyGUI::utility::toString(parent->getChildCount()));\n            box->setEditStatic(true);\n            box->setEditMultiLine(true);\n            box->setEditWordWrap(true);\n            box->setNeedMouseFocus(false);\n            box->setNeedKeyFocus(false);\n            box->setMaxTextLength(text.size());\n            box->setTextAlign(mBlockStyle.mAlign);\n            box->setTextColour(mTextStyle.mColour);\n            box->setFontName(mTextStyle.mFont);\n            box->setCaption(MyGUI::TextIterator::toTagsString(text));\n            box->setSize(box->getSize().width, box->getTextSize().height);\n            mEditBox = box;\n        }\n\n        int TextElement::getHeight()\n        {\n            return mEditBox->getTextSize().height;\n        }\n\n        int TextElement::pageSplit()\n        {\n            // split lines\n            const int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight();\n            unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop());\n            if (lineHeight > 0)\n                lastLine /= lineHeight;\n            int ret = mPaginator.getCurrentTop() + lastLine * lineHeight;\n\n            // first empty lines that would go to the next page should be ignored\n            mPaginator.setIgnoreLeadingEmptyLines(true);\n\n            const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType<MyGUI::EditText>()->getLineInfo();\n            for (unsigned int i = lastLine; i < lines.size(); ++i)\n            {\n                if (lines[i].width == 0)\n                    ret += lineHeight;\n                else\n                {\n                    mPaginator.setIgnoreLeadingEmptyLines(false);\n                    break;\n                }\n            }\n            return ret;\n        }\n\n        /* ImageElement */\n        ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,\n                                   const std::string & src, int width, int height)\n            : GraphicElement(parent, pag, blockStyle),\n              mImageHeight(height)\n        {\n            int left = 0;\n            if (mBlockStyle.mAlign.isHCenter())\n                left += (pag.getPageWidth() - width) / 2;\n            else if (mBlockStyle.mAlign.isLeft())\n                left = 0;\n            else if (mBlockStyle.mAlign.isRight())\n                left += pag.getPageWidth() - width;\n\n            mImageBox = parent->createWidget<MyGUI::ImageBox> (\"ImageBox\",\n                MyGUI::IntCoord(left, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top,\n                parent->getName() + MyGUI::utility::toString(parent->getChildCount()));\n\n            mImageBox->setImageTexture(src);\n            mImageBox->setProperty(\"NeedMouse\", \"false\");\n        }\n\n        int ImageElement::getHeight()\n        {\n            return mImageHeight;\n        }\n\n        int ImageElement::pageSplit()\n        {\n            // if the image is larger than the page, fall back to the default pageSplit implementation\n            if (mImageHeight > mPaginator.getPageHeight())\n                return GraphicElement::pageSplit();\n            return mPaginator.getCurrentTop();\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/formatting.hpp",
    "content": "#ifndef MWGUI_FORMATTING_H\n#define MWGUI_FORMATTING_H\n\n#include <MyGUI_Colour.h>\n#include <map>\n\n#include <components/widgets/box.hpp>\n\nnamespace MWGui\n{\n    namespace Formatting\n    {\n        struct TextStyle\n        {\n            TextStyle() :\n                mColour(0,0,0)\n                , mFont(\"Journalbook Magic Cards\")\n                , mTextSize(16)\n            {\n            }\n\n            MyGUI::Colour mColour;\n            std::string mFont;\n            int mTextSize;\n        };\n\n        struct BlockStyle\n        {\n            BlockStyle() :\n                mAlign(MyGUI::Align::Left | MyGUI::Align::Top)\n            {\n            }\n\n            MyGUI::Align mAlign;\n        };\n\n        class BookTextParser\n        {\n            public:\n                typedef std::map<std::string, std::string> Attributes;\n                enum Events\n                {\n                    Event_None = -2,\n                    Event_EOF = -1,\n                    Event_BrTag,\n                    Event_PTag,\n                    Event_ImgTag,\n                    Event_DivTag,\n                    Event_FontTag\n                };\n\n                BookTextParser(const std::string & text);\n\n                Events next();\n\n                const Attributes & getAttributes() const;\n                std::string getReadyText() const;\n                bool isClosingTag() const;\n\n            private:\n                void registerTag(const std::string & tag, Events type);\n                void flushBuffer();\n                void parseTag(std::string tag);\n\n                size_t mIndex;\n                std::string mText;\n                std::string mReadyText;\n\n                bool mIgnoreNewlineTags;\n                bool mIgnoreLineEndings;\n                Attributes mAttributes;\n                std::string mTag;\n                bool mClosingTag;\n                std::map<std::string, Events> mTagTypes;\n                std::string mBuffer;\n\n                size_t mPlainTextEnd;\n        };\n\n        class Paginator\n        {\n            public:\n                typedef std::pair<int, int> Page;\n                typedef std::vector<Page> Pages;\n\n                Paginator(int pageWidth, int pageHeight)\n                    : mStartTop(0), mCurrentTop(0),\n                      mPageWidth(pageWidth), mPageHeight(pageHeight),\n                      mIgnoreLeadingEmptyLines(false)\n                {\n                }\n\n                int getStartTop() const { return mStartTop; }\n                int getCurrentTop() const { return mCurrentTop; }\n                int getPageWidth() const { return mPageWidth; }\n                int getPageHeight() const { return mPageHeight; }\n                bool getIgnoreLeadingEmptyLines() const { return mIgnoreLeadingEmptyLines; }\n                Pages getPages() const { return mPages; }\n\n                void setStartTop(int top) { mStartTop = top; }\n                void setCurrentTop(int top) { mCurrentTop = top; }\n                void setIgnoreLeadingEmptyLines(bool ignore) { mIgnoreLeadingEmptyLines = ignore; }\n\n                Paginator & operator<<(const Page & page)\n                {\n                    mPages.push_back(page);\n                    return *this;\n                }\n\n            private:\n                int mStartTop, mCurrentTop;\n                int mPageWidth, mPageHeight;\n                bool mIgnoreLeadingEmptyLines;\n                Pages mPages;\n        };\n\n        /// \\brief utilities for parsing book/scroll text as mygui widgets\n        class BookFormatter\n        {\n            public:\n                Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight);\n                Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup);\n\n            private:\n                void resetFontProperties();\n\n                void handleDiv(const BookTextParser::Attributes & attr);\n                void handleFont(const BookTextParser::Attributes & attr);\n\n                TextStyle mTextStyle;\n                BlockStyle mBlockStyle;\n        };\n\n        class GraphicElement\n        {\n            public:\n                GraphicElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle);\n                virtual int getHeight() = 0;\n                virtual void paginate();\n                virtual int pageSplit();\n\n            protected:\n                virtual ~GraphicElement() {}\n                MyGUI::Widget * mParent;\n                Paginator & mPaginator;\n                BlockStyle mBlockStyle;\n        };\n\n        class TextElement : public GraphicElement\n        {\n            public:\n                TextElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,\n                            const TextStyle & textStyle, const std::string & text);\n                int getHeight() override;\n                int pageSplit() override;\n            private:\n                int currentFontHeight() const;\n                TextStyle mTextStyle;\n                Gui::EditBox * mEditBox;\n        };\n\n        class ImageElement : public GraphicElement\n        {\n            public:\n                ImageElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,\n                             const std::string & src, int width, int height);\n                int getHeight() override;\n                int pageSplit() override;\n\n            private:\n                int mImageHeight;\n                MyGUI::ImageBox * mImageBox;\n        };\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/hud.cpp",
    "content": "#include \"hud.hpp\"\n\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_ProgressBar.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_ScrollView.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/settings/settings.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"inventorywindow.hpp\"\n#include \"spellicons.hpp\"\n#include \"itemmodel.hpp\"\n#include \"draganddrop.hpp\"\n\n#include \"itemwidget.hpp\"\n\nnamespace MWGui\n{\n\n    /**\n     * Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world.\n     */\n    class WorldItemModel : public ItemModel\n    {\n    public:\n        WorldItemModel(float left, float top) : mLeft(left), mTop(top) {}\n        virtual ~WorldItemModel() override {}\n        MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override\n        {\n            MWBase::World* world = MWBase::Environment::get().getWorld();\n\n            MWWorld::Ptr dropped;\n            if (world->canPlaceObject(mLeft, mTop))\n                dropped = world->placeObject(item.mBase, mLeft, mTop, count);\n            else\n                dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count);\n            dropped.getCellRef().setOwner(\"\");\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_OBJECT_PLACE packet every time an object is dropped into the world from\n                the inventory screen\n            */\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectPlace(dropped, true);\n            objectList->sendObjectPlace();\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp change (major)\n\n                Instead of actually keeping this object as is, delete it after sending the packet\n                and wait for the server to send it back with a unique mpNum of its own\n            */\n            MWBase::Environment::get().getWorld()->deleteObject(dropped);\n            /*\n                End of tes3mp change (major)\n            */\n\n            return dropped;\n        }\n\n        void removeItem (const ItemStack& item, size_t count) override { throw std::runtime_error(\"removeItem not implemented\"); }\n        ModelIndex getIndex (ItemStack item) override { throw std::runtime_error(\"getIndex not implemented\"); }\n        void update() override {}\n        size_t getItemCount() override { return 0; }\n        ItemStack getItem (ModelIndex index) override { throw std::runtime_error(\"getItem not implemented\"); }\n\n    private:\n        // Where to drop the item\n        float mLeft;\n        float mTop;\n    };\n\n\n    HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender)\n        : WindowBase(\"openmw_hud.layout\")\n        , LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool(\"local map hud fog of war\", \"Map\"))\n        , mHealth(nullptr)\n        , mMagicka(nullptr)\n        , mStamina(nullptr)\n        , mDrowning(nullptr)\n        , mWeapImage(nullptr)\n        , mSpellImage(nullptr)\n        , mWeapStatus(nullptr)\n        , mSpellStatus(nullptr)\n        , mEffectBox(nullptr)\n        , mMinimap(nullptr)\n        , mCrosshair(nullptr)\n        , mCellNameBox(nullptr)\n        , mDrowningFrame(nullptr)\n        , mDrowningFlash(nullptr)\n        , mHealthManaStaminaBaseLeft(0)\n        , mWeapBoxBaseLeft(0)\n        , mSpellBoxBaseLeft(0)\n        , mMinimapBoxBaseRight(0)\n        , mEffectBoxBaseRight(0)\n        , mDragAndDrop(dragAndDrop)\n        , mCellNameTimer(0.0f)\n        , mWeaponSpellTimer(0.f)\n        , mMapVisible(true)\n        , mWeaponVisible(true)\n        , mSpellVisible(true)\n        , mWorldMouseOver(false)\n        , mEnemyActorId(-1)\n        , mEnemyHealthTimer(-1)\n        , mIsDrowning(false)\n        , mDrowningFlashTheta(0.f)\n    {\n        mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());\n\n        // Energy bars\n        getWidget(mHealthFrame, \"HealthFrame\");\n        getWidget(mHealth, \"Health\");\n        getWidget(mMagicka, \"Magicka\");\n        getWidget(mStamina, \"Stamina\");\n        getWidget(mEnemyHealth, \"EnemyHealth\");\n        mHealthManaStaminaBaseLeft = mHealthFrame->getLeft();\n\n        MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame;\n        getWidget(healthFrame, \"HealthFrame\");\n        getWidget(magickaFrame, \"MagickaFrame\");\n        getWidget(fatigueFrame, \"FatigueFrame\");\n        healthFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked);\n        magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked);\n        fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked);\n\n        //Drowning bar\n        getWidget(mDrowningFrame, \"DrowningFrame\");\n        getWidget(mDrowning, \"Drowning\");\n        getWidget(mDrowningFlash, \"Flash\");\n        mDrowning->setProgressRange(200);\n\n        const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n\n        // Item and spell images and status bars\n        getWidget(mWeapBox, \"WeapBox\");\n        getWidget(mWeapImage, \"WeapImage\");\n        getWidget(mWeapStatus, \"WeapStatus\");\n        mWeapBoxBaseLeft = mWeapBox->getLeft();\n        mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked);\n\n        getWidget(mSpellBox, \"SpellBox\");\n        getWidget(mSpellImage, \"SpellImage\");\n        getWidget(mSpellStatus, \"SpellStatus\");\n        mSpellBoxBaseLeft = mSpellBox->getLeft();\n        mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked);\n\n        getWidget(mSneakBox, \"SneakBox\");\n        mSneakBoxBaseLeft = mSneakBox->getLeft();\n\n        getWidget(mEffectBox, \"EffectBox\");\n        mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight();\n\n        getWidget(mMinimapBox, \"MiniMapBox\");\n        mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight();\n        getWidget(mMinimap, \"MiniMap\");\n        getWidget(mCompass, \"Compass\");\n        getWidget(mMinimapButton, \"MiniMapButton\");\n        mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);\n\n        getWidget(mCellNameBox, \"CellName\");\n        getWidget(mWeaponSpellBox, \"WeaponSpellName\");\n\n        getWidget(mCrosshair, \"Crosshair\");\n\n        LocalMapBase::init(mMinimap, mCompass);\n\n        mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked);\n        mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver);\n        mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus);\n\n        mSpellIcons = new SpellIcons();\n    }\n\n    HUD::~HUD()\n    {\n        mMainWidget->eventMouseLostFocus.clear();\n        mMainWidget->eventMouseMove.clear();\n        mMainWidget->eventMouseButtonClick.clear();\n\n        delete mSpellIcons;\n    }\n\n    void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value)\n    {\n        int current = static_cast<int>(value.getCurrent());\n        int modified = static_cast<int>(value.getModified());\n\n        // Fatigue can be negative\n        if (id != \"FBar\")\n            current = std::max(0, current);\n\n        MyGUI::Widget* w;\n        std::string valStr = MyGUI::utility::toString(current) + \" / \" + MyGUI::utility::toString(modified);\n        if (id == \"HBar\")\n        {\n            mHealth->setProgressRange(std::max(0, modified));\n            mHealth->setProgressPosition(std::max(0, current));\n            getWidget(w, \"HealthFrame\");\n            w->setUserString(\"Caption_HealthDescription\", \"#{sHealthDesc}\\n\" + valStr);\n        }\n        else if (id == \"MBar\")\n        {\n            mMagicka->setProgressRange(std::max(0, modified));\n            mMagicka->setProgressPosition(std::max(0, current));\n            getWidget(w, \"MagickaFrame\");\n            w->setUserString(\"Caption_HealthDescription\", \"#{sMagDesc}\\n\" + valStr);\n        }\n        else if (id == \"FBar\")\n        {\n            mStamina->setProgressRange(std::max(0, modified));\n            mStamina->setProgressPosition(std::max(0, current));\n            getWidget(w, \"FatigueFrame\");\n            w->setUserString(\"Caption_HealthDescription\", \"#{sFatDesc}\\n\" + valStr);\n        }\n    }\n\n    void HUD::setDrowningTimeLeft(float time, float maxTime)\n    {\n        size_t progress = static_cast<size_t>(time / maxTime * 200);\n        mDrowning->setProgressPosition(progress);\n\n        bool isDrowning = (progress == 0);\n        if (isDrowning && !mIsDrowning) // Just started drowning\n            mDrowningFlashTheta = 0.0f; // Start out on bright red every time.\n\n        mDrowningFlash->setVisible(isDrowning);\n        mIsDrowning = isDrowning;\n    }\n\n    void HUD::setDrowningBarVisible(bool visible)\n    {\n        mDrowningFrame->setVisible(visible);\n    }\n\n    void HUD::onWorldClicked(MyGUI::Widget* _sender)\n    {\n        if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ())\n            return;\n\n        MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n        if (mDragAndDrop->mIsOnDragAndDrop)\n        {\n            // drop item into the gameworld\n            MWBase::Environment::get().getWorld()->breakInvisibility(\n                        MWMechanics::getPlayer());\n\n            MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n            MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition();\n            float mouseX = cursorPosition.left / float(viewSize.width);\n            float mouseY = cursorPosition.top / float(viewSize.height);\n\n            WorldItemModel drop (mouseX, mouseY);\n            mDragAndDrop->drop(&drop, nullptr);\n\n            winMgr->changePointer(\"arrow\");\n        }\n        else\n        {\n            GuiMode mode = winMgr->getMode();\n\n            if (!winMgr->isConsoleMode() && (mode != GM_Container) && (mode != GM_Inventory))\n                return;\n\n            MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject();\n\n            if (winMgr->isConsoleMode())\n                winMgr->setConsoleSelectedObject(object);\n            else //if ((mode == GM_Container) || (mode == GM_Inventory))\n            {\n                // pick up object\n                if (!object.isEmpty())\n                /*\n                    Start of tes3mp change (major)\n\n                    Disable unilateral picking up of objects on this client\n\n                    Instead, send an ID_OBJECT_ACTIVATE packet every time an item is made to pick up\n                    an item here, and expect the server's reply to our packet to cause the actual\n                    picking up of items\n                */\n                    //winMgr->getInventoryWindow()->pickUpObject(object);\n                {\n                    mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                    objectList->addObjectActivate(object, MWMechanics::getPlayer());\n                    objectList->sendObjectActivate();\n                }\n                /*\n                    End of tes3mp change (major)\n                */\n            }\n        }\n    }\n\n    void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y)\n    {\n        if (mDragAndDrop->mIsOnDragAndDrop)\n        {\n            mWorldMouseOver = false;\n\n            MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n            MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition();\n            float mouseX = cursorPosition.left / float(viewSize.width);\n            float mouseY = cursorPosition.top / float(viewSize.height);\n\n            MWBase::World* world = MWBase::Environment::get().getWorld();\n\n            // if we can't drop the object at the wanted position, show the \"drop on ground\" cursor.\n            bool canDrop = world->canPlaceObject(mouseX, mouseY);\n\n            if (!canDrop)\n                MWBase::Environment::get().getWindowManager()->changePointer(\"drop_ground\");\n            else\n                MWBase::Environment::get().getWindowManager()->changePointer(\"arrow\");\n\n        }\n        else\n        {\n            MWBase::Environment::get().getWindowManager()->changePointer(\"arrow\");\n            mWorldMouseOver = true;\n        }\n    }\n\n    void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new)\n    {\n        MWBase::Environment::get().getWindowManager()->changePointer(\"arrow\");\n        mWorldMouseOver = false;\n    }\n\n    void HUD::onHMSClicked(MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats);\n    }\n\n    void HUD::onMapClicked(MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map);\n    }\n\n    void HUD::onWeaponClicked(MyGUI::Widget* _sender)\n    {\n        const MWWorld::Ptr& player = MWMechanics::getPlayer();\n        if (player.getClass().getNpcStats(player).isWerewolf())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sWerewolfRefusal}\");\n            return;\n        }\n\n        MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);\n    }\n\n    void HUD::onMagicClicked(MyGUI::Widget* _sender)\n    {\n        const MWWorld::Ptr& player = MWMechanics::getPlayer();\n        if (player.getClass().getNpcStats(player).isWerewolf())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sWerewolfRefusal}\");\n            return;\n        }\n\n        MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic);\n    }\n\n    void HUD::setCellName(const std::string& cellName)\n    {\n        if (mCellName != cellName)\n        {\n            mCellNameTimer = 5.0f;\n            mCellName = cellName;\n\n            mCellNameBox->setCaptionWithReplacing(\"#{sCell=\" + mCellName + \"}\");\n            mCellNameBox->setVisible(mMapVisible);\n        }\n    }\n\n    void HUD::onFrame(float dt)\n    {\n        LocalMapBase::onFrame(dt);\n\n        mCellNameTimer -= dt;\n        mWeaponSpellTimer -= dt;\n        if (mCellNameTimer < 0)\n            mCellNameBox->setVisible(false);\n        if (mWeaponSpellTimer < 0)\n            mWeaponSpellBox->setVisible(false);\n\n        mEnemyHealthTimer -= dt;\n        if (mEnemyHealth->getVisible() && mEnemyHealthTimer < 0)\n        {\n            mEnemyHealth->setVisible(false);\n            mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20));\n        }\n\n        if (mIsDrowning)\n            mDrowningFlashTheta += dt * osg::PI*2;\n\n        mSpellIcons->updateWidgets(mEffectBox, true);\n\n        if (mEnemyActorId != -1 && mEnemyHealth->getVisible())\n        {\n            updateEnemyHealthBar();\n        }\n\n        if (mIsDrowning)\n        {\n            float intensity = (cos(mDrowningFlashTheta) + 2.0f) / 3.0f;\n\n            mDrowningFlash->setAlpha(intensity);\n        }\n    }\n\n    void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent)\n    {\n        const ESM::Spell* spell =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);\n\n        std::string spellName = spell->mName;\n        if (spellName != mSpellName && mSpellVisible)\n        {\n            mWeaponSpellTimer = 5.0f;\n            mSpellName = spellName;\n            mWeaponSpellBox->setCaption(mSpellName);\n            mWeaponSpellBox->setVisible(true);\n        }\n\n        mSpellStatus->setProgressRange(100);\n        mSpellStatus->setProgressPosition(successChancePercent);\n\n        mSpellBox->setUserString(\"ToolTipType\", \"Spell\");\n        mSpellBox->setUserString(\"Spell\", spellId);\n\n        // use the icon of the first effect\n        const ESM::MagicEffect* effect =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(spell->mEffects.mList.front().mEffectID);\n\n        std::string icon = effect->mIcon;\n        int slashPos = icon.rfind('\\\\');\n        icon.insert(slashPos+1, \"b_\");\n        icon = MWBase::Environment::get().getWindowManager()->correctIconPath(icon);\n\n        mSpellImage->setSpellIcon(icon);\n    }\n\n    void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent)\n    {\n        std::string itemName = item.getClass().getName(item);\n        if (itemName != mSpellName && mSpellVisible)\n        {\n            mWeaponSpellTimer = 5.0f;\n            mSpellName = itemName;\n            mWeaponSpellBox->setCaption(mSpellName);\n            mWeaponSpellBox->setVisible(true);\n        }\n\n        mSpellStatus->setProgressRange(100);\n        mSpellStatus->setProgressPosition(chargePercent);\n\n        mSpellBox->setUserString(\"ToolTipType\", \"ItemPtr\");\n        mSpellBox->setUserData(MWWorld::Ptr(item));\n\n        mSpellImage->setItem(item);\n    }\n\n    void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent)\n    {\n        std::string itemName = item.getClass().getName(item);\n        if (itemName != mWeaponName && mWeaponVisible)\n        {\n            mWeaponSpellTimer = 5.0f;\n            mWeaponName = itemName;\n            mWeaponSpellBox->setCaption(mWeaponName);\n            mWeaponSpellBox->setVisible(true);\n        }\n\n        mWeapBox->clearUserStrings();\n        mWeapBox->setUserString(\"ToolTipType\", \"ItemPtr\");\n        mWeapBox->setUserData(MWWorld::Ptr(item));\n\n        mWeapStatus->setProgressRange(100);\n        mWeapStatus->setProgressPosition(durabilityPercent);\n\n        mWeapImage->setItem(item);\n    }\n\n    void HUD::unsetSelectedSpell()\n    {\n        std::string spellName = \"#{sNone}\";\n        if (spellName != mSpellName && mSpellVisible)\n        {\n            mWeaponSpellTimer = 5.0f;\n            mSpellName = spellName;\n            mWeaponSpellBox->setCaptionWithReplacing(mSpellName);\n            mWeaponSpellBox->setVisible(true);\n        }\n\n        mSpellStatus->setProgressRange(100);\n        mSpellStatus->setProgressPosition(0);\n        mSpellImage->setItem(MWWorld::Ptr());\n        mSpellBox->clearUserStrings();\n    }\n\n    void HUD::unsetSelectedWeapon()\n    {\n        std::string itemName = \"#{sSkillHandtohand}\";\n        if (itemName != mWeaponName && mWeaponVisible)\n        {\n            mWeaponSpellTimer = 5.0f;\n            mWeaponName = itemName;\n            mWeaponSpellBox->setCaptionWithReplacing(mWeaponName);\n            mWeaponSpellBox->setVisible(true);\n        }\n\n        mWeapStatus->setProgressRange(100);\n        mWeapStatus->setProgressPosition(0);\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        MWWorld::Ptr player = world->getPlayerPtr();\n\n        mWeapImage->setItem(MWWorld::Ptr());\n        std::string icon = (player.getClass().getNpcStats(player).isWerewolf()) ? \"icons\\\\k\\\\tx_werewolf_hand.dds\" : \"icons\\\\k\\\\stealth_handtohand.dds\";\n        mWeapImage->setIcon(icon);\n\n        mWeapBox->clearUserStrings();\n        mWeapBox->setUserString(\"ToolTipType\", \"Layout\");\n        mWeapBox->setUserString(\"ToolTipLayout\", \"HandToHandToolTip\");\n        mWeapBox->setUserString(\"Caption_HandToHandText\", itemName);\n        mWeapBox->setUserString(\"ImageTexture_HandToHandImage\", icon);\n    }\n\n    void HUD::setCrosshairVisible(bool visible)\n    {\n        mCrosshair->setVisible (visible);\n    }\n    \n    void HUD::setCrosshairOwned(bool owned)\n    {\n        if(owned)\n        {\n            mCrosshair->changeWidgetSkin(\"HUD_Crosshair_Owned\");\n        }\n        else\n        {\n            mCrosshair->changeWidgetSkin(\"HUD_Crosshair\");\n        }\n    }\n    \n    void HUD::setHmsVisible(bool visible)\n    {\n        mHealth->setVisible(visible);\n        mMagicka->setVisible(visible);\n        mStamina->setVisible(visible);\n        updatePositions();\n    }\n\n    void HUD::setWeapVisible(bool visible)\n    {\n        mWeapBox->setVisible(visible);\n        updatePositions();\n    }\n\n    void HUD::setSpellVisible(bool visible)\n    {\n        mSpellBox->setVisible(visible);\n        updatePositions();\n    }\n\n    void HUD::setSneakVisible(bool visible)\n    {\n        mSneakBox->setVisible(visible);\n        updatePositions();\n    }\n\n    void HUD::setEffectVisible(bool visible)\n    {\n        mEffectBox->setVisible (visible);\n        updatePositions();\n    }\n\n    void HUD::setMinimapVisible(bool visible)\n    {\n        mMinimapBox->setVisible (visible);\n        updatePositions();\n    }\n\n    void HUD::updatePositions()\n    {\n        int weapDx = 0, spellDx = 0, sneakDx = 0;\n        if (!mHealth->getVisible())\n            sneakDx = spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft;\n\n        if (!mWeapBox->getVisible())\n        {\n            spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft;\n            sneakDx = spellDx;\n        }\n\n        if (!mSpellBox->getVisible())\n            sneakDx += mSneakBoxBaseLeft - mSpellBoxBaseLeft;\n\n        mWeaponVisible = mWeapBox->getVisible();\n        mSpellVisible = mSpellBox->getVisible();\n        if (!mWeaponVisible && !mSpellVisible)\n            mWeaponSpellBox->setVisible(false);\n\n        mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop());\n        mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop());\n        mSneakBox->setPosition(mSneakBoxBaseLeft - sneakDx, mSneakBox->getTop());\n\n        const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n\n        // effect box can have variable width -> variable left coordinate\n        int effectsDx = 0;\n        if (!mMinimapBox->getVisible ())\n            effectsDx = mEffectBoxBaseRight - mMinimapBoxBaseRight;\n\n        mMapVisible = mMinimapBox->getVisible ();\n        if (!mMapVisible)\n            mCellNameBox->setVisible(false);\n\n        mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop());\n    }\n\n    void HUD::updateEnemyHealthBar()\n    {\n        MWWorld::Ptr enemy = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mEnemyActorId);\n        if (enemy.isEmpty())\n            return;\n        MWMechanics::CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);\n        mEnemyHealth->setProgressRange(100);\n        // Health is usually cast to int before displaying. Actors die whenever they are < 1 health.\n        // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :)\n        mEnemyHealth->setProgressPosition(static_cast<size_t>(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100));\n\n        static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fNPCHealthBarFade\")->mValue.getFloat();\n        if (fNPCHealthBarFade > 0.f)\n            mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade)));\n\n    }\n\n    void HUD::setEnemy(const MWWorld::Ptr &enemy)\n    {\n        mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId();\n        mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fNPCHealthBarTime\")->mValue.getFloat();\n        if (!mEnemyHealth->getVisible())\n            mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));\n        mEnemyHealth->setVisible(true);\n        updateEnemyHealthBar();\n    }\n\n    void HUD::resetEnemy()\n    {\n        mEnemyActorId = -1;\n        mEnemyHealthTimer = -1;\n    }\n\n    void HUD::clear()\n    {\n        unsetSelectedSpell();\n        unsetSelectedWeapon();\n        resetEnemy();\n    }\n\n    void HUD::customMarkerCreated(MyGUI::Widget *marker)\n    {\n        marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);\n    }\n\n    void HUD::doorMarkerCreated(MyGUI::Widget *marker)\n    {\n        marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/hud.hpp",
    "content": "#ifndef OPENMW_GAME_MWGUI_HUD_H\n#define OPENMW_GAME_MWGUI_HUD_H\n\n#include \"mapwindow.hpp\"\n#include \"statswatcher.hpp\"\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWGui\n{\n    class DragAndDrop;\n    class SpellIcons;\n    class ItemWidget;\n    class SpellWidget;\n\n    class HUD : public WindowBase, public LocalMapBase, public StatsListener\n    {\n    public:\n        HUD(CustomMarkerCollection& customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender);\n        virtual ~HUD();\n        void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value) override;\n\n        /// Set time left for the player to start drowning\n        /// @param time time left to start drowning\n        /// @param maxTime how long we can be underwater (in total) until drowning starts\n        void setDrowningTimeLeft(float time, float maxTime);\n        void setDrowningBarVisible(bool visible);\n\n        void setHmsVisible(bool visible);\n        void setWeapVisible(bool visible);\n        void setSpellVisible(bool visible);\n        void setSneakVisible(bool visible);\n\n        void setEffectVisible(bool visible);\n        void setMinimapVisible(bool visible);\n\n        void setSelectedSpell(const std::string& spellId, int successChancePercent);\n        void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent);\n        const MWWorld::Ptr& getSelectedEnchantItem();\n        void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent);\n        void unsetSelectedSpell();\n        void unsetSelectedWeapon();\n\n        void setCrosshairVisible(bool visible);\n        void setCrosshairOwned(bool owned);\n\n        void onFrame(float dt) override;\n\n        void setCellName(const std::string& cellName);\n\n        bool getWorldMouseOver() { return mWorldMouseOver; }\n\n        MyGUI::Widget* getEffectBox() { return mEffectBox; }\n\n        void setEnemy(const MWWorld::Ptr& enemy);\n        void resetEnemy();\n\n        void clear() override;\n\n    private:\n        MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning;\n        MyGUI::Widget* mHealthFrame;\n        MyGUI::Widget *mWeapBox, *mSpellBox, *mSneakBox;\n        ItemWidget *mWeapImage;\n        SpellWidget *mSpellImage;\n        MyGUI::ProgressBar *mWeapStatus, *mSpellStatus;\n        MyGUI::Widget *mEffectBox, *mMinimapBox;\n        MyGUI::Button* mMinimapButton;\n        MyGUI::ScrollView* mMinimap;\n        MyGUI::ImageBox* mCrosshair;\n        MyGUI::TextBox* mCellNameBox;\n        MyGUI::TextBox* mWeaponSpellBox;\n        MyGUI::Widget *mDrowningFrame, *mDrowningFlash;\n\n        // bottom left elements\n        int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft, mSneakBoxBaseLeft;\n        // bottom right elements\n        int mMinimapBoxBaseRight, mEffectBoxBaseRight;\n\n        DragAndDrop* mDragAndDrop;\n\n        std::string mCellName;\n        float mCellNameTimer;\n\n        std::string mWeaponName;\n        std::string mSpellName;\n        float mWeaponSpellTimer;\n\n        bool mMapVisible;\n        bool mWeaponVisible;\n        bool mSpellVisible;\n\n        bool mWorldMouseOver;\n\n        SpellIcons* mSpellIcons;\n\n        int mEnemyActorId;\n        float mEnemyHealthTimer;\n\n        bool  mIsDrowning;\n        float mDrowningFlashTheta;\n\n        void onWorldClicked(MyGUI::Widget* _sender);\n        void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y);\n        void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new);\n        void onHMSClicked(MyGUI::Widget* _sender);\n        void onWeaponClicked(MyGUI::Widget* _sender);\n        void onMagicClicked(MyGUI::Widget* _sender);\n        void onMapClicked(MyGUI::Widget* _sender);\n\n        // LocalMapBase\n        void customMarkerCreated(MyGUI::Widget* marker) override;\n        void doorMarkerCreated(MyGUI::Widget* marker) override;\n\n        void updateEnemyHealthBar();\n\n        void updatePositions();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/inventoryitemmodel.cpp",
    "content": "#include \"inventoryitemmodel.hpp\"\n\n#include <sstream>\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\nnamespace MWGui\n{\n\nInventoryItemModel::InventoryItemModel(const MWWorld::Ptr &actor)\n    : mActor(actor)\n{\n}\n\nItemStack InventoryItemModel::getItem (ModelIndex index)\n{\n    if (index < 0)\n        throw std::runtime_error(\"Invalid index supplied\");\n    if (mItems.size() <= static_cast<size_t>(index))\n        throw std::runtime_error(\"Item index out of range\");\n    return mItems[index];\n}\n\nsize_t InventoryItemModel::getItemCount()\n{\n    return mItems.size();\n}\n\nItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)\n{\n    size_t i = 0;\n    for (ItemStack& itemStack : mItems)\n    {\n        if (itemStack == item)\n            return i;\n        ++i;\n    }\n    return -1;\n}\n\nMWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)\n{\n    if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor))\n        throw std::runtime_error(\"Item to copy needs to be from a different container!\");\n    return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, allowAutoEquip);\n}\n\nvoid InventoryItemModel::removeItem (const ItemStack& item, size_t count)\n{\n    int removed = 0;\n    // Re-equipping makes sense only if a target has inventory\n    if (mActor.getClass().hasInventoryStore(mActor))\n    {\n        MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor);\n        removed = store.remove(item.mBase, count, mActor, true);\n    }\n    else\n    {\n        MWWorld::ContainerStore& store = mActor.getClass().getContainerStore(mActor);\n        removed = store.remove(item.mBase, count, mActor);\n    }\n\n    std::stringstream error;\n\n    if (removed == 0)\n    {\n        error << \"Item '\" << item.mBase.getCellRef().getRefId() << \"' was not found in container store to remove\";\n        throw std::runtime_error(error.str());\n    }\n    else if (removed < static_cast<int>(count))\n    {\n        error << \"Not enough items '\" << item.mBase.getCellRef().getRefId() << \"' in the stack to remove (\" << static_cast<int>(count) << \" requested, \" << removed << \" found)\";\n        throw std::runtime_error(error.str());\n    }\n}\n\nMWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)\n{\n    // Can't move conjured items: This is a general fix that also takes care of issues with taking conjured items via the 'Take All' button.\n    if (item.mFlags & ItemStack::Flag_Bound)\n        return MWWorld::Ptr();\n\n    MWWorld::Ptr ret = otherModel->copyItem(item, count);\n    removeItem(item, count);\n    return ret;\n}\n\nvoid InventoryItemModel::update()\n{\n    MWWorld::ContainerStore& store = mActor.getClass().getContainerStore(mActor);\n\n    mItems.clear();\n\n    for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n    {\n        MWWorld::Ptr item = *it;\n\n        if (!item.getClass().showsInInventory(item))\n            continue;\n\n        ItemStack newItem (item, this, item.getRefData().getCount());\n\n        if (mActor.getClass().hasInventoryStore(mActor))\n        {\n            MWWorld::InventoryStore& invStore = mActor.getClass().getInventoryStore(mActor);\n            if (invStore.isEquipped(newItem.mBase))\n                newItem.mType = ItemStack::Type_Equipped;\n        }\n\n        mItems.push_back(newItem);\n    }\n}\n\nbool InventoryItemModel::onTakeItem(const MWWorld::Ptr &item, int count)\n{\n    // Looting a dead corpse is considered OK\n    if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead())\n        return true;\n\n    MWWorld::Ptr player = MWMechanics::getPlayer();\n    MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item, mActor, count);\n\n    return true;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/inventoryitemmodel.hpp",
    "content": "#ifndef MWGUI_INVENTORY_ITEM_MODEL_H\n#define MWGUI_INVENTORY_ITEM_MODEL_H\n\n#include \"itemmodel.hpp\"\n\nnamespace MWGui\n{\n\n    class InventoryItemModel : public ItemModel\n    {\n    public:\n        InventoryItemModel (const MWWorld::Ptr& actor);\n\n        ItemStack getItem (ModelIndex index) override;\n        ModelIndex getIndex (ItemStack item) override;\n        size_t getItemCount() override;\n\n        bool onTakeItem(const MWWorld::Ptr &item, int count) override;\n\n        MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true) override;\n        void removeItem (const ItemStack& item, size_t count) override;\n\n        /// Move items from this model to \\a otherModel.\n        MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel) override;\n\n        void update() override;\n\n    protected:\n        MWWorld::Ptr mActor;\n    private:\n        std::vector<ItemStack> mItems;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/inventorywindow.cpp",
    "content": "#include \"inventorywindow.hpp\"\n\n#include <cmath>\n#include <stdexcept>\n\n#include <MyGUI_Window.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_EditBox.h>\n\n#include <osg/Texture2D>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/myguiplatform/myguitexture.hpp>\n\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n\n#include \"itemview.hpp\"\n#include \"inventoryitemmodel.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n#include \"tradeitemmodel.hpp\"\n#include \"countdialog.hpp\"\n#include \"tradewindow.hpp\"\n#include \"draganddrop.hpp\"\n#include \"widgets.hpp\"\n#include \"tooltips.hpp\"\n\nnamespace\n{\n\n    bool isRightHandWeapon(const MWWorld::Ptr& item)\n    {\n        if (item.getClass().getTypeName() != typeid(ESM::Weapon).name())\n            return false;\n        std::vector<int> equipmentSlots = item.getClass().getEquipmentSlots(item).first;\n        return (!equipmentSlots.empty() && equipmentSlots.front() == MWWorld::InventoryStore::Slot_CarriedRight);\n    }\n\n}\n\nnamespace MWGui\n{\n\n    InventoryWindow::InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem)\n        : WindowPinnableBase(\"openmw_inventory_window.layout\")\n        , mDragAndDrop(dragAndDrop)\n        , mSelectedItem(-1)\n        , mSortModel(nullptr)\n        , mTradeModel(nullptr)\n        , mGuiMode(GM_Inventory)\n        , mLastXSize(0)\n        , mLastYSize(0)\n        , mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer()))\n        , mTrading(false)\n        , mUpdateTimer(0.f)\n    {\n        mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));\n        mPreview->rebuild();\n\n        mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize);\n\n        getWidget(mAvatar, \"Avatar\");\n        getWidget(mAvatarImage, \"AvatarImage\");\n        getWidget(mEncumbranceBar, \"EncumbranceBar\");\n        getWidget(mFilterAll, \"AllButton\");\n        getWidget(mFilterWeapon, \"WeaponButton\");\n        getWidget(mFilterApparel, \"ApparelButton\");\n        getWidget(mFilterMagic, \"MagicButton\");\n        getWidget(mFilterMisc, \"MiscButton\");\n        getWidget(mLeftPane, \"LeftPane\");\n        getWidget(mRightPane, \"RightPane\");\n        getWidget(mArmorRating, \"ArmorRating\");\n        getWidget(mFilterEdit, \"FilterEdit\");\n\n        mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked);\n        mAvatarImage->setRenderItemTexture(mPreviewTexture.get());\n        mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));\n\n        getWidget(mItemView, \"ItemView\");\n        mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected);\n        mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &InventoryWindow::onBackgroundSelected);\n\n        mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);\n        mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);\n        mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);\n        mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);\n        mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);\n        mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &InventoryWindow::onNameFilterChanged);\n\n        mFilterAll->setStateSelected(true);\n\n        setGuiMode(mGuiMode);\n\n        adjustPanes();\n    }\n\n    void InventoryWindow::adjustPanes()\n    {\n        const float aspect = 0.5; // fixed aspect ratio for the avatar image\n        int leftPaneWidth = static_cast<int>((mMainWidget->getSize().height - 44 - mArmorRating->getHeight()) * aspect);\n        mLeftPane->setSize( leftPaneWidth, mMainWidget->getSize().height-44 );\n        mRightPane->setCoord( mLeftPane->getPosition().left + leftPaneWidth + 4,\n                              mRightPane->getPosition().top,\n                              mMainWidget->getSize().width - 12 - leftPaneWidth - 15,\n                              mMainWidget->getSize().height-44 );\n    }\n\n    void InventoryWindow::updatePlayer()\n    {\n        mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr());\n\n        if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings\n            mSortModel->setSourceModel(mTradeModel);\n        else\n            mSortModel = new SortFilterItemModel(mTradeModel);\n\n        mSortModel->setNameFilter(mFilterEdit->getCaption());\n\n        mItemView->setModel(mSortModel);\n\n        mFilterAll->setStateSelected(true);\n        mFilterWeapon->setStateSelected(false);\n        mFilterApparel->setStateSelected(false);\n        mFilterMagic->setStateSelected(false);\n        mFilterMisc->setStateSelected(false);\n\n        mPreview->updatePtr(mPtr);\n        mPreview->rebuild();\n        mPreview->update();\n\n        dirtyPreview();\n\n        updatePreviewSize();\n\n        updateEncumbranceBar();\n        mItemView->update();\n        notifyContentChanged();\n    }\n\n    void InventoryWindow::clear()\n    {\n        mPtr = MWWorld::Ptr();\n        mTradeModel = nullptr;\n        mSortModel = nullptr;\n        mItemView->setModel(nullptr);\n    }\n\n    void InventoryWindow::toggleMaximized()\n    {\n        std::string setting = getModeSetting();\n\n        bool maximized = !Settings::Manager::getBool(setting + \" maximized\", \"Windows\");\n        if (maximized)\n            setting += \" maximized\";\n\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        float x = Settings::Manager::getFloat(setting + \" x\", \"Windows\") * float(viewSize.width);\n        float y = Settings::Manager::getFloat(setting + \" y\", \"Windows\") * float(viewSize.height);\n        float w = Settings::Manager::getFloat(setting + \" w\", \"Windows\") * float(viewSize.width);\n        float h = Settings::Manager::getFloat(setting + \" h\", \"Windows\") * float(viewSize.height);\n        MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();\n        window->setCoord(x, y, w, h);\n\n        if (maximized)\n            Settings::Manager::setBool(setting, \"Windows\", maximized);\n        else\n            Settings::Manager::setBool(setting + \" maximized\", \"Windows\", maximized);\n\n        adjustPanes();\n        updatePreviewSize();\n    }\n\n    void InventoryWindow::setGuiMode(GuiMode mode)\n    {\n        mGuiMode = mode;\n        std::string setting = getModeSetting();\n        setPinButtonVisible(mode == GM_Inventory);\n\n        if (Settings::Manager::getBool(setting + \" maximized\", \"Windows\"))\n            setting += \" maximized\";\n\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        MyGUI::IntPoint pos(static_cast<int>(Settings::Manager::getFloat(setting + \" x\", \"Windows\") * viewSize.width),\n                            static_cast<int>(Settings::Manager::getFloat(setting + \" y\", \"Windows\") * viewSize.height));\n        MyGUI::IntSize size(static_cast<int>(Settings::Manager::getFloat(setting + \" w\", \"Windows\") * viewSize.width),\n                            static_cast<int>(Settings::Manager::getFloat(setting + \" h\", \"Windows\") * viewSize.height));\n\n        bool needUpdate = (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight());\n\n        mMainWidget->setPosition(pos);\n        mMainWidget->setSize(size);\n\n        adjustPanes();\n\n        if (needUpdate)\n            updatePreviewSize();\n    }\n\n    SortFilterItemModel* InventoryWindow::getSortFilterModel()\n    {\n        return mSortModel;\n    }\n\n    TradeItemModel* InventoryWindow::getTradeModel()\n    {\n        return mTradeModel;\n    }\n\n    ItemModel* InventoryWindow::getModel()\n    {\n        return mTradeModel;\n    }\n\n    void InventoryWindow::onBackgroundSelected()\n    {\n        if (mDragAndDrop->mIsOnDragAndDrop)\n            mDragAndDrop->drop(mTradeModel, mItemView);\n    }\n\n    void InventoryWindow::onItemSelected (int index)\n    {\n        onItemSelectedFromSourceModel (mSortModel->mapToSource(index));\n    }\n\n    void InventoryWindow::onItemSelectedFromSourceModel (int index)\n    {\n        if (mDragAndDrop->mIsOnDragAndDrop)\n        {\n            mDragAndDrop->drop(mTradeModel, mItemView);\n            return;\n        }\n\n        const ItemStack& item = mTradeModel->getItem(index);\n        std::string sound = item.mBase.getClass().getDownSoundId(item.mBase);\n\n        MWWorld::Ptr object = item.mBase;\n        int count = item.mCount;\n        bool shift = MyGUI::InputManager::getInstance().isShiftPressed();\n\n        if (MyGUI::InputManager::getInstance().isControlPressed())\n            count = 1;\n\n        if (mTrading)\n        {\n            // Can't give conjured items to a merchant\n            if (item.mFlags & ItemStack::Flag_Bound)\n            {\n                MWBase::Environment::get().getWindowManager()->playSound(sound);\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sBarterDialog9}\");\n                return;\n            }\n\n            // check if merchant accepts item\n            int services = MWBase::Environment::get().getWindowManager()->getTradeWindow()->getMerchantServices();\n            if (!object.getClass().canSell(object, services))\n            {\n                MWBase::Environment::get().getWindowManager()->playSound(sound);\n                MWBase::Environment::get().getWindowManager()->\n                        messageBox(\"#{sBarterDialog4}\");\n                return;\n            }\n        }\n\n        // If we unequip weapon during attack, it can lead to unexpected behaviour\n        if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPtr))\n        {\n            bool isWeapon = item.mBase.getTypeName() == typeid(ESM::Weapon).name();\n            MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);\n\n            if (isWeapon && invStore.isEquipped(item.mBase))\n            {\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sCantEquipWeapWarning}\");\n                return;\n            }\n        }\n\n        if (count > 1 && !shift)\n        {\n            CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();\n            std::string message = mTrading ? \"#{sQuanityMenuMessage01}\" : \"#{sTake}\";\n            std::string name = object.getClass().getName(object) + MWGui::ToolTips::getSoulString(object.getCellRef());\n            dialog->openCountDialog(name, message, count);\n            dialog->eventOkClicked.clear();\n            if (mTrading)\n                dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem);\n            else\n                dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dragItem);\n            mSelectedItem = index;\n        }\n        else\n        {\n            mSelectedItem = index;\n            if (mTrading)\n                sellItem (nullptr, count);\n            else\n                dragItem (nullptr, count);\n        }\n    }\n\n    void InventoryWindow::ensureSelectedItemUnequipped(int count)\n    {\n        const ItemStack& item = mTradeModel->getItem(mSelectedItem);\n        if (item.mType == ItemStack::Type_Equipped)\n        {\n            MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);\n            MWWorld::Ptr newStack = *invStore.unequipItemQuantity(item.mBase, mPtr, count);\n\n            // The unequipped item was re-stacked. We have to update the index\n            // since the item pointed does not exist anymore.\n            if (item.mBase != newStack)\n            {\n                updateItemView();  // Unequipping can produce a new stack, not yet in the window...\n\n                // newIndex will store the index of the ItemStack the item was stacked on\n                int newIndex = -1;\n                for (size_t i=0; i < mTradeModel->getItemCount(); ++i)\n                {\n                    if (mTradeModel->getItem(i).mBase == newStack)\n                    {\n                        newIndex = i;\n                        break;\n                    }\n                }\n\n                if (newIndex == -1)\n                    throw std::runtime_error(\"Can't find restacked item\");\n\n                mSelectedItem = newIndex;\n            }\n        }\n    }\n\n    void InventoryWindow::dragItem(MyGUI::Widget* sender, int count)\n    {\n        ensureSelectedItemUnequipped(count);\n        mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count);\n        notifyContentChanged();\n    }\n\n    void InventoryWindow::sellItem(MyGUI::Widget* sender, int count)\n    {\n        ensureSelectedItemUnequipped(count);\n        const ItemStack& item = mTradeModel->getItem(mSelectedItem);\n        std::string sound = item.mBase.getClass().getUpSoundId(item.mBase);\n        MWBase::Environment::get().getWindowManager()->playSound(sound);\n\n        if (item.mType == ItemStack::Type_Barter)\n        {\n            // this was an item borrowed to us by the merchant\n            mTradeModel->returnItemBorrowedToUs(mSelectedItem, count);\n            MWBase::Environment::get().getWindowManager()->getTradeWindow()->returnItem(mSelectedItem, count);\n        }\n        else\n        {\n            // borrow item to the merchant\n            mTradeModel->borrowItemFromUs(mSelectedItem, count);\n            MWBase::Environment::get().getWindowManager()->getTradeWindow()->borrowItem(mSelectedItem, count);\n        }\n\n        mItemView->update();\n        notifyContentChanged();\n    }\n\n    void InventoryWindow::updateItemView()\n    {\n        MWBase::Environment::get().getWindowManager()->updateSpellWindow();\n\n        mItemView->update();\n\n        dirtyPreview();\n    }\n\n    void InventoryWindow::onOpen()\n    {\n        // Reset the filter focus when opening the window\n        MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n        if (focus == mFilterEdit)\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr);\n\n        if (!mPtr.isEmpty())\n        {\n            updateEncumbranceBar();\n            mItemView->update();\n            notifyContentChanged();\n        }\n        adjustPanes();\n    }\n\n    std::string InventoryWindow::getModeSetting() const\n    {\n        std::string setting = \"inventory\";\n        switch(mGuiMode)\n        {\n            case GM_Container:\n                setting += \" container\";\n                break;\n            case GM_Companion:\n                setting += \" companion\";\n                break;\n            case GM_Barter:\n                setting += \" barter\";\n                break;\n            default:\n                break;\n        }\n\n        return setting;\n    }\n\n    void InventoryWindow::onWindowResize(MyGUI::Window* _sender)\n    {\n        adjustPanes();\n        std::string setting = getModeSetting();\n\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        float x = _sender->getPosition().left / float(viewSize.width);\n        float y = _sender->getPosition().top / float(viewSize.height);\n        float w = _sender->getSize().width / float(viewSize.width);\n        float h = _sender->getSize().height / float(viewSize.height);\n        Settings::Manager::setFloat(setting + \" x\", \"Windows\", x);\n        Settings::Manager::setFloat(setting + \" y\", \"Windows\", y);\n        Settings::Manager::setFloat(setting + \" w\", \"Windows\", w);\n        Settings::Manager::setFloat(setting + \" h\", \"Windows\", h);\n        bool maximized = Settings::Manager::getBool(setting + \" maximized\", \"Windows\");\n        if (maximized)\n            Settings::Manager::setBool(setting + \" maximized\", \"Windows\", false);\n\n        if (mMainWidget->getSize().width != mLastXSize || mMainWidget->getSize().height != mLastYSize)\n        {\n            mLastXSize = mMainWidget->getSize().width;\n            mLastYSize = mMainWidget->getSize().height;\n\n            updatePreviewSize();\n            updateArmorRating();\n        }\n    }\n\n    void InventoryWindow::updateArmorRating()\n    {\n        mArmorRating->setCaptionWithReplacing (\"#{sArmor}: \"\n            + MyGUI::utility::toString(static_cast<int>(mPtr.getClass().getArmorRating(mPtr))));\n        if (mArmorRating->getTextSize().width > mArmorRating->getSize().width)\n            mArmorRating->setCaptionWithReplacing (MyGUI::utility::toString(static_cast<int>(mPtr.getClass().getArmorRating(mPtr))));\n    }\n\n    void InventoryWindow::updatePreviewSize()\n    {\n        MyGUI::IntSize size = mAvatarImage->getSize();\n        int width = std::min(mPreview->getTextureWidth(), size.width);\n        int height = std::min(mPreview->getTextureHeight(), size.height);\n        float scalingFactor = MWBase::Environment::get().getWindowManager()->getScalingFactor();\n        mPreview->setViewport(int(width*scalingFactor), int(height*scalingFactor));\n\n        mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f,\n                                                                     width*scalingFactor/float(mPreview->getTextureWidth()), height*scalingFactor/float(mPreview->getTextureHeight())));\n    }\n\n    void InventoryWindow::onNameFilterChanged(MyGUI::EditBox* _sender)\n    {\n        mSortModel->setNameFilter(_sender->getCaption());\n        mItemView->update();\n    }\n\n    void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender)\n    {\n        if (_sender == mFilterAll)\n            mSortModel->setCategory(SortFilterItemModel::Category_All);\n        else if (_sender == mFilterWeapon)\n            mSortModel->setCategory(SortFilterItemModel::Category_Weapon);\n        else if (_sender == mFilterApparel)\n            mSortModel->setCategory(SortFilterItemModel::Category_Apparel);\n        else if (_sender == mFilterMagic)\n            mSortModel->setCategory(SortFilterItemModel::Category_Magic);\n        else if (_sender == mFilterMisc)\n            mSortModel->setCategory(SortFilterItemModel::Category_Misc);\n        mFilterAll->setStateSelected(false);\n        mFilterWeapon->setStateSelected(false);\n        mFilterApparel->setStateSelected(false);\n        mFilterMagic->setStateSelected(false);\n        mFilterMisc->setStateSelected(false);\n\n        mItemView->update();\n\n        _sender->castType<MyGUI::Button>()->setStateSelected(true);\n    }\n\n    void InventoryWindow::onPinToggled()\n    {\n        Settings::Manager::setBool(\"inventory pin\", \"Windows\", mPinned);\n\n        MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned);\n    }\n\n    void InventoryWindow::onTitleDoubleClicked()\n    {\n        if (MyGUI::InputManager::getInstance().isShiftPressed())\n            toggleMaximized();\n        else if (!mPinned)\n            MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);\n    }\n\n    void InventoryWindow::useItem(const MWWorld::Ptr &ptr, bool force)\n    {\n        const std::string& script = ptr.getClass().getScript(ptr);\n        if (!script.empty())\n        {\n            // Don't try to equip the item if PCSkipEquip is set to 1\n            if (ptr.getRefData().getLocals().getIntVar(script, \"pcskipequip\") == 1)\n            {\n                ptr.getRefData().getLocals().setVarByInt(script, \"onpcequip\", 1);\n                return;\n            }\n            ptr.getRefData().getLocals().setVarByInt(script, \"onpcequip\", 0);\n        }\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n\n        // early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that case\n        if (!ptr.getClass().getEquipmentSlots(ptr).first.empty())\n        {\n            if (ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)\n            {\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sInventoryMessage1}\");\n                updateItemView();\n                return;\n            }\n\n            if (!force)\n            {\n                std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player);\n\n                if (canEquip.first == 0)\n                {\n                    MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);\n                    updateItemView();\n                    return;\n                }\n            }\n        }\n\n        // If the item has a script, set OnPCEquip or PCSkipEquip to 1\n        if (!script.empty())\n        {\n            // Ingredients, books and repair hammers must not have OnPCEquip set to 1 here\n            const std::string& type = ptr.getTypeName();\n            bool isBook = type == typeid(ESM::Book).name();\n            if (!isBook && type != typeid(ESM::Ingredient).name() && type != typeid(ESM::Repair).name())\n                ptr.getRefData().getLocals().setVarByInt(script, \"onpcequip\", 1);\n            // Books must have PCSkipEquip set to 1 instead\n            else if (isBook)\n                ptr.getRefData().getLocals().setVarByInt(script, \"pcskipequip\", 1);\n        }\n\n        std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);\n        action->execute(player);\n\n        if (isVisible())\n        {\n            mItemView->update();\n\n            notifyContentChanged();\n        }\n        // else: will be updated in open()\n    }\n\n    void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender)\n    {\n        if (mDragAndDrop->mIsOnDragAndDrop)\n        {\n            MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase;\n\n            mDragAndDrop->finish();\n\n            if (mDragAndDrop->mSourceModel != mTradeModel)\n            {\n                // Move item to the player's inventory\n                ptr = mDragAndDrop->mSourceModel->moveItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount, mTradeModel);\n            }\n\n            /*\n                Start of tes3mp change (major)\n\n                Instead of unilaterally using an item, send an ID_PLAYER_ITEM_USE packet and let the server\n                decide if the item actually gets used\n            */\n            //useItem(ptr);\n            mwmp::Main::get().getLocalPlayer()->sendItemUse(ptr);\n            /*\n                End of tes3mp change (major)\n            */\n\n            // If item is ingredient or potion don't stop drag and drop to simplify action of taking more than one 1 item\n            if ((ptr.getTypeName() == typeid(ESM::Potion).name() ||\n                 ptr.getTypeName() == typeid(ESM::Ingredient).name())\n                && mDragAndDrop->mDraggedCount > 1)\n            {\n                // Item can be provided from other window for example container.\n                // But after DragAndDrop::startDrag item automaticly always gets to player inventory.\n                mSelectedItem = getModel()->getIndex(mDragAndDrop->mItem);\n                dragItem(nullptr, mDragAndDrop->mDraggedCount - 1);\n            }\n        }\n        else\n        {\n            MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance ().getLastPressedPosition (MyGUI::MouseButton::Left);\n            MyGUI::IntPoint relPos = mousePos - mAvatarImage->getAbsolutePosition ();\n\n            MWWorld::Ptr itemSelected = getAvatarSelectedItem (relPos.left, relPos.top);\n            if (itemSelected.isEmpty ())\n                return;\n\n            for (size_t i=0; i < mTradeModel->getItemCount (); ++i)\n            {\n                if (mTradeModel->getItem(i).mBase == itemSelected)\n                {\n                    onItemSelectedFromSourceModel(i);\n                    return;\n                }\n            }\n            throw std::runtime_error(\"Can't find clicked item\");\n        }\n    }\n\n    MWWorld::Ptr InventoryWindow::getAvatarSelectedItem(int x, int y)\n    {\n        // convert to OpenGL lower-left origin\n        y = (mAvatarImage->getHeight()-1) - y;\n\n        // Scale coordinates\n        float scalingFactor = MWBase::Environment::get().getWindowManager()->getScalingFactor();\n        x = static_cast<int>(x*scalingFactor);\n        y = static_cast<int>(y*scalingFactor);\n\n        int slot = mPreview->getSlotSelected (x, y);\n\n        if (slot == -1)\n            return MWWorld::Ptr();\n\n        MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);\n        if(invStore.getSlot(slot) != invStore.end())\n        {\n            MWWorld::Ptr item = *invStore.getSlot(slot);\n            if (!item.getClass().showsInInventory(item))\n                return MWWorld::Ptr();\n            return item;\n        }\n\n        return MWWorld::Ptr();\n    }\n\n    void InventoryWindow::updateEncumbranceBar()\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n\n        float capacity = player.getClass().getCapacity(player);\n        float encumbrance = player.getClass().getEncumbrance(player);\n        mTradeModel->adjustEncumbrance(encumbrance);\n        mEncumbranceBar->setValue(std::ceil(encumbrance), static_cast<int>(capacity));\n    }\n\n    void InventoryWindow::onFrame(float dt)\n    {\n        updateEncumbranceBar();\n\n        if (mPinned)\n        {\n            mUpdateTimer += dt;\n            if (0.1f < mUpdateTimer)\n            {\n                mUpdateTimer = 0;\n\n                // Update pinned inventory in-game\n                if (!MWBase::Environment::get().getWindowManager()->isGuiMode())\n                {\n                    mItemView->update();\n                    notifyContentChanged();\n                }\n            }\n        }\n    }\n\n    void InventoryWindow::setTrading(bool trading)\n    {\n        mTrading = trading;\n    }\n\n    void InventoryWindow::dirtyPreview()\n    {\n        mPreview->update();\n\n        updateArmorRating();\n    }\n\n    void InventoryWindow::notifyContentChanged()\n    {\n        // update the spell window just in case new enchanted items were added to inventory\n        MWBase::Environment::get().getWindowManager()->updateSpellWindow();\n\n        MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(\n                    MWMechanics::getPlayer());\n\n        dirtyPreview();\n    }\n\n    void InventoryWindow::pickUpObject (MWWorld::Ptr object)\n    {\n        // If the inventory is not yet enabled, don't pick anything up\n        if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory))\n            return;\n        // make sure the object is of a type that can be picked up\n        std::string type = object.getTypeName();\n        if ( (type != typeid(ESM::Apparatus).name())\n            && (type != typeid(ESM::Armor).name())\n            && (type != typeid(ESM::Book).name())\n            && (type != typeid(ESM::Clothing).name())\n            && (type != typeid(ESM::Ingredient).name())\n            && (type != typeid(ESM::Light).name())\n            && (type != typeid(ESM::Miscellaneous).name())\n            && (type != typeid(ESM::Lockpick).name())\n            && (type != typeid(ESM::Probe).name())\n            && (type != typeid(ESM::Repair).name())\n            && (type != typeid(ESM::Weapon).name())\n            && (type != typeid(ESM::Potion).name()))\n            return;\n\n        // An object that can be picked up must have a tooltip.\n        if (!object.getClass().hasToolTip(object))\n            return;\n\n        int count = object.getRefData().getCount();\n        if (object.getClass().isGold(object))\n            count *= object.getClass().getValue(object);\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWBase::Environment::get().getWorld()->breakInvisibility(player);\n        \n        if (!object.getRefData().activate())\n            return;\n\n        MWBase::Environment::get().getMechanicsManager()->itemTaken(player, object, MWWorld::Ptr(), count);\n\n        // add to player inventory\n        // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object\n        MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player);\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_OBJECT_DELETE packet every time an item from the world is picked up\n            by the player through the inventory HUD\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectGeneric(object);\n        objectList->sendObjectDelete();\n        /*\n            End of tes3mp addition\n        */\n\n        // remove from world\n        MWBase::Environment::get().getWorld()->deleteObject (object);\n\n        // get ModelIndex to the item\n        mTradeModel->update();\n        size_t i=0;\n        for (; i<mTradeModel->getItemCount(); ++i)\n        {\n            if (mTradeModel->getItem(i).mBase == newObject)\n                break;\n        }\n        if (i == mTradeModel->getItemCount())\n            throw std::runtime_error(\"Added item not found\");\n        mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count);\n\n        MWBase::Environment::get().getWindowManager()->updateSpellWindow();\n    }\n\n    void InventoryWindow::cycle(bool next)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n\n        if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))\n            return;\n\n        const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);\n        bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();\n        if ((!godmode && stats.isParalyzed()) || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())\n            return;\n\n        ItemModel::ModelIndex selected = -1;\n        // not using mSortFilterModel as we only need sorting, not filtering\n        SortFilterItemModel model(new InventoryItemModel(player));\n        model.setSortByType(false);\n        model.update();\n        if (model.getItemCount() == 0)\n            return;\n\n        for (ItemModel::ModelIndex i=0; i<int(model.getItemCount()); ++i)\n        {\n            MWWorld::Ptr item = model.getItem(i).mBase;\n            if (model.getItem(i).mType & ItemStack::Type_Equipped && isRightHandWeapon(item))\n                selected = i;\n        }\n\n        int incr = next ? 1 : -1;\n        bool found = false;\n        std::string lastId;\n        if (selected != -1)\n            lastId = model.getItem(selected).mBase.getCellRef().getRefId();\n        ItemModel::ModelIndex cycled = selected;\n        for (unsigned int i=0; i<model.getItemCount(); ++i)\n        {\n            cycled += incr;\n            cycled = (cycled + model.getItemCount()) % model.getItemCount();\n\n            MWWorld::Ptr item = model.getItem(cycled).mBase;\n\n            // skip different stacks of the same item, or we will get stuck as stacking/unstacking them may change their relative ordering\n            if (Misc::StringUtils::ciEqual(lastId, item.getCellRef().getRefId()))\n                continue;\n\n            lastId = item.getCellRef().getRefId();\n\n            if (item.getClass().getTypeName() == typeid(ESM::Weapon).name() &&\n                isRightHandWeapon(item) &&\n                item.getClass().canBeEquipped(item, player).first)\n            {\n                found = true;\n                break;\n            }\n        }\n\n        if (!found || selected == cycled)\n            return;\n\n        /*\n            Start of tes3mp change (major)\n\n            Instead of unilaterally using an item, send an ID_PLAYER_ITEM_USE packet and let the server\n            decide if the item actually gets used\n        */\n        //useItem(model.getItem(cycled).mBase);\n        mwmp::Main::get().getLocalPlayer()->sendItemUse(model.getItem(cycled).mBase);\n        /*\n            End of tes3mp change (major)\n        */\n    }\n\n    void InventoryWindow::rebuildAvatar()\n    {\n        mPreview->rebuild();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/inventorywindow.hpp",
    "content": "#ifndef MGUI_Inventory_H\n#define MGUI_Inventory_H\n\n#include \"windowpinnablebase.hpp\"\n#include \"mode.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwrender/characterpreview.hpp\"\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWGui\n{\n    namespace Widgets\n    {\n        class MWDynamicStat;\n    }\n\n    class ItemView;\n    class SortFilterItemModel;\n    class TradeItemModel;\n    class DragAndDrop;\n    class ItemModel;\n\n    class InventoryWindow : public WindowPinnableBase\n    {\n        public:\n            InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem);\n\n            void onOpen() override;\n\n            /// start trading, disables item drag&drop\n            void setTrading(bool trading);\n\n            void onFrame(float dt) override;\n\n            void pickUpObject (MWWorld::Ptr object);\n\n            MWWorld::Ptr getAvatarSelectedItem(int x, int y);\n\n            void rebuildAvatar();\n\n            SortFilterItemModel* getSortFilterModel();\n            TradeItemModel* getTradeModel();\n            ItemModel* getModel();\n\n            void updateItemView();\n\n            void updatePlayer();\n\n            void clear() override;\n\n            void useItem(const MWWorld::Ptr& ptr, bool force=false);\n\n            void setGuiMode(GuiMode mode);\n\n            /// Cycle to previous/next weapon\n            void cycle(bool next);\n\n        protected:\n            void onTitleDoubleClicked() override;\n\n        private:\n            DragAndDrop* mDragAndDrop;\n\n            int mSelectedItem;\n\n            MWWorld::Ptr mPtr;\n\n            MWGui::ItemView* mItemView;\n            SortFilterItemModel* mSortModel;\n            TradeItemModel* mTradeModel;\n\n            MyGUI::Widget* mAvatar;\n            MyGUI::ImageBox* mAvatarImage;\n            MyGUI::TextBox* mArmorRating;\n            Widgets::MWDynamicStat* mEncumbranceBar;\n\n            MyGUI::Widget* mLeftPane;\n            MyGUI::Widget* mRightPane;\n\n            MyGUI::Button* mFilterAll;\n            MyGUI::Button* mFilterWeapon;\n            MyGUI::Button* mFilterApparel;\n            MyGUI::Button* mFilterMagic;\n            MyGUI::Button* mFilterMisc;\n            \n            MyGUI::EditBox* mFilterEdit;\n\n            GuiMode mGuiMode;\n\n            int mLastXSize;\n            int mLastYSize;\n\n            std::unique_ptr<MyGUI::ITexture> mPreviewTexture;\n            std::unique_ptr<MWRender::InventoryPreview> mPreview;\n\n            bool mTrading;\n            float mUpdateTimer;\n\n            void toggleMaximized();\n\n            void onItemSelected(int index);\n            void onItemSelectedFromSourceModel(int index);\n\n            void onBackgroundSelected();\n\n            std::string getModeSetting() const;\n\n            void sellItem(MyGUI::Widget* sender, int count);\n            void dragItem(MyGUI::Widget* sender, int count);\n\n            void onWindowResize(MyGUI::Window* _sender);\n            void onFilterChanged(MyGUI::Widget* _sender);\n            void onNameFilterChanged(MyGUI::EditBox* _sender);\n            void onAvatarClicked(MyGUI::Widget* _sender);\n            void onPinToggled() override;\n\n            void updateEncumbranceBar();\n            void notifyContentChanged();\n            void dirtyPreview();\n            void updatePreviewSize();\n            void updateArmorRating();\n\n            void adjustPanes();\n\n            /// Unequips count items from mSelectedItem, if it is equipped, and then updates mSelectedItem in case the items were re-stacked\n            void ensureSelectedItemUnequipped(int count);\n    };\n}\n\n#endif // Inventory_H\n"
  },
  {
    "path": "apps/openmw/mwgui/itemchargeview.cpp",
    "content": "#include \"itemchargeview.hpp\"\n\n#include <set>\n\n#include <MyGUI_Gui.h>\n#include <MyGUI_TextBox.h>\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_FactoryManager.h>\n\n#include <components/esm/loadench.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"itemmodel.hpp\"\n#include \"itemwidget.hpp\"\n\nnamespace MWGui\n{\n    ItemChargeView::ItemChargeView()\n        : mScrollView(nullptr),\n          mDisplayMode(DisplayMode_Health)\n    {\n    }\n\n    void ItemChargeView::registerComponents()\n    {\n        MyGUI::FactoryManager::getInstance().registerFactory<ItemChargeView>(\"Widget\");\n    }\n\n    void ItemChargeView::initialiseOverride()\n    {\n        Base::initialiseOverride();\n\n        assignWidget(mScrollView, \"ScrollView\");\n        if (mScrollView == nullptr)\n            throw std::runtime_error(\"Item charge view needs a scroll view\");\n\n        mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top);\n    }\n\n    void ItemChargeView::setModel(ItemModel* model)\n    {\n        mModel.reset(model);\n    }\n\n    void ItemChargeView::setDisplayMode(ItemChargeView::DisplayMode type)\n    {\n        mDisplayMode = type;\n        update();\n    }\n\n    void ItemChargeView::update()\n    {\n        if (!mModel.get())\n        {\n            layoutWidgets();\n            return;\n        }\n\n        mModel->update();\n\n        Lines lines;\n        std::set<Lines::const_iterator> visitedLines;\n\n        for (size_t i = 0; i < mModel->getItemCount(); ++i)\n        {\n            ItemStack stack = mModel->getItem(static_cast<ItemModel::ModelIndex>(i));\n\n            bool found = false;\n            for (Lines::const_iterator iter = mLines.begin(); iter != mLines.end(); ++iter)\n            {\n                if (iter->mItemPtr == stack.mBase)\n                {\n                    found = true;\n                    visitedLines.insert(iter);\n\n                    // update line widgets\n                    updateLine(*iter);\n                    lines.push_back(*iter);\n                    break;\n                }\n            }\n\n            if (!found)\n            {\n                // add line widgets\n                Line line;\n                line.mItemPtr = stack.mBase;\n\n                line.mText = mScrollView->createWidget<MyGUI::TextBox>(\"SandText\", MyGUI::IntCoord(), MyGUI::Align::Default);\n                line.mText->setNeedMouseFocus(false);\n\n                line.mIcon = mScrollView->createWidget<ItemWidget>(\"MW_ItemIconSmall\", MyGUI::IntCoord(), MyGUI::Align::Default);\n                line.mIcon->setItem(line.mItemPtr);\n                line.mIcon->setUserString(\"ToolTipType\", \"ItemPtr\");\n                line.mIcon->setUserData(MWWorld::Ptr(line.mItemPtr));\n                line.mIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemChargeView::onIconClicked);\n                line.mIcon->eventMouseWheel += MyGUI::newDelegate(this, &ItemChargeView::onMouseWheelMoved);\n\n                line.mCharge = mScrollView->createWidget<Widgets::MWDynamicStat>(\"MW_ChargeBar\", MyGUI::IntCoord(), MyGUI::Align::Default);\n                line.mCharge->setNeedMouseFocus(false);\n\n                updateLine(line);\n\n                lines.push_back(line);\n            }\n        }\n\n        for (Lines::iterator iter = mLines.begin(); iter != mLines.end(); ++iter)\n        {\n            if (visitedLines.count(iter))\n                continue;\n\n            // remove line widgets\n            MyGUI::Gui::getInstance().destroyWidget(iter->mText);\n            MyGUI::Gui::getInstance().destroyWidget(iter->mIcon);\n            MyGUI::Gui::getInstance().destroyWidget(iter->mCharge);\n        }\n\n        mLines.swap(lines);\n\n        layoutWidgets();\n    }\n\n    void ItemChargeView::layoutWidgets()\n    {\n        int currentY = 0;\n\n        for (Line& line : mLines)\n        {\n            line.mText->setCoord(8, currentY, mScrollView->getWidth()-8, 18);\n            currentY += 19;\n\n            line.mIcon->setCoord(16, currentY, 32, 32);\n            line.mCharge->setCoord(72, currentY+2, std::max(199, mScrollView->getWidth()-72-38), 20);\n            currentY += 32 + 4;\n        }\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mScrollView->setVisibleVScroll(false);\n        mScrollView->setCanvasSize(MyGUI::IntSize(mScrollView->getWidth(), std::max(mScrollView->getHeight(), currentY)));\n        mScrollView->setVisibleVScroll(true);\n    }\n\n    void ItemChargeView::resetScrollbars()\n    {\n        mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));\n    }\n\n    void ItemChargeView::setSize(const MyGUI::IntSize& value)\n    {\n        bool changed = (value.width != getWidth() || value.height != getHeight());\n        Base::setSize(value);\n        if (changed)\n            layoutWidgets();\n    }\n\n    void ItemChargeView::setCoord(const MyGUI::IntCoord& value)\n    {\n        bool changed = (value.width != getWidth() || value.height != getHeight());\n        Base::setCoord(value);\n        if (changed)\n            layoutWidgets();\n    }\n\n    void ItemChargeView::updateLine(const ItemChargeView::Line& line)\n    {\n        line.mText->setCaption(line.mItemPtr.getClass().getName(line.mItemPtr));\n\n        line.mCharge->setVisible(false);\n        switch (mDisplayMode)\n        {\n            case DisplayMode_Health:\n                if (!line.mItemPtr.getClass().hasItemHealth(line.mItemPtr))\n                    break;\n\n                line.mCharge->setVisible(true);\n                line.mCharge->setValue(line.mItemPtr.getClass().getItemHealth(line.mItemPtr),\n                                       line.mItemPtr.getClass().getItemMaxHealth(line.mItemPtr));\n                break;\n            case DisplayMode_EnchantmentCharge:\n                std::string enchId = line.mItemPtr.getClass().getEnchantment(line.mItemPtr);\n                if (enchId.empty())\n                    break;\n                const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchId);\n                if (!ench)\n                    break;\n\n                line.mCharge->setVisible(true);\n                line.mCharge->setValue(static_cast<int>(line.mItemPtr.getCellRef().getEnchantmentCharge()),\n                                       ench->mData.mCharge);\n                break;\n        }\n    }\n\n    void ItemChargeView::onIconClicked(MyGUI::Widget* sender)\n    {\n        eventItemClicked(this, *sender->getUserData<MWWorld::Ptr>());\n    }\n\n    void ItemChargeView::onMouseWheelMoved(MyGUI::Widget* /*sender*/, int rel)\n    {\n        if (mScrollView->getViewOffset().top + rel*0.3f > 0)\n            mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));\n        else\n            mScrollView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mScrollView->getViewOffset().top + rel*0.3f)));\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/itemchargeview.hpp",
    "content": "#ifndef MWGUI_ITEMCHARGEVIEW_H\n#define MWGUI_ITEMCHARGEVIEW_H\n\n#include <vector>\n#include <memory>\n\n#include <MyGUI_Widget.h>\n\n#include \"../mwworld/ptr.hpp\"\n\n#include \"widgets.hpp\"\n\nnamespace MyGUI\n{\n    class TextBox;\n    class ScrollView;\n}\n\nnamespace MWGui\n{\n    class ItemModel;\n    class ItemWidget;\n\n    class ItemChargeView final : public MyGUI::Widget\n    {\n        MYGUI_RTTI_DERIVED(ItemChargeView)\n        public:\n            enum DisplayMode\n            {\n                DisplayMode_Health,\n                DisplayMode_EnchantmentCharge\n            };\n\n            ItemChargeView();\n\n            /// Register needed components with MyGUI's factory manager\n            static void registerComponents();\n\n            void initialiseOverride() override;\n\n            /// Takes ownership of \\a model\n            void setModel(ItemModel* model);\n\n            void setDisplayMode(DisplayMode type);\n\n            void update();\n            void layoutWidgets();\n            void resetScrollbars();\n\n            void setSize(const MyGUI::IntSize& value) override;\n            void setCoord(const MyGUI::IntCoord& value) override;\n\n            MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, const MWWorld::Ptr&> eventItemClicked;\n\n        private:\n            struct Line\n            {\n                MWWorld::Ptr mItemPtr;\n                MyGUI::TextBox* mText;\n                ItemWidget* mIcon;\n                Widgets::MWDynamicStatPtr mCharge;\n            };\n\n            void updateLine(const Line& line);\n\n            void onIconClicked(MyGUI::Widget* sender);\n            void onMouseWheelMoved(MyGUI::Widget* sender, int rel);\n\n            typedef std::vector<Line> Lines;\n            Lines mLines;\n\n            std::unique_ptr<ItemModel> mModel;\n            MyGUI::ScrollView* mScrollView;\n            DisplayMode mDisplayMode;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/itemmodel.cpp",
    "content": "#include \"itemmodel.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\nnamespace MWGui\n{\n\n    ItemStack::ItemStack(const MWWorld::Ptr &base, ItemModel *creator, size_t count)\n        : mType(Type_Normal)\n        , mFlags(0)\n        , mCreator(creator)\n        , mCount(count)\n        , mBase(base)\n    {\n        if (base.getClass().getEnchantment(base) != \"\")\n            mFlags |= Flag_Enchanted;\n\n        if (MWBase::Environment::get().getMechanicsManager()->isBoundItem(base))\n            mFlags |= Flag_Bound;\n    }\n\n    ItemStack::ItemStack()\n        : mType(Type_Normal)\n        , mFlags(0)\n        , mCreator(nullptr)\n        , mCount(0)\n    {\n    }\n\n    bool operator == (const ItemStack& left, const ItemStack& right)\n    {\n        if (left.mType != right.mType)\n            return false;\n\n        if(left.mBase == right.mBase)\n            return true;\n\n        // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure\n        if (left.mBase.getContainerStore() && right.mBase.getContainerStore())\n            return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase)\n                    && right.mBase.getContainerStore()->stacks(left.mBase, right.mBase);\n\n        if (left.mBase.getContainerStore())\n            return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase);\n        if (right.mBase.getContainerStore())\n            return right.mBase.getContainerStore()->stacks(left.mBase, right.mBase);\n\n        MWWorld::ContainerStore store;\n        return store.stacks(left.mBase, right.mBase);\n    }\n\n    ItemModel::ItemModel()\n    {\n    }\n\n    MWWorld::Ptr ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)\n    {\n        MWWorld::Ptr ret = otherModel->copyItem(item, count);\n        removeItem(item, count);\n        return ret;\n    }\n\n    bool ItemModel::allowedToUseItems() const\n    {\n        return true;\n    }\n\n    bool ItemModel::onDropItem(const MWWorld::Ptr &item, int count)\n    {\n        return true;\n    }\n\n    bool ItemModel::onTakeItem(const MWWorld::Ptr &item, int count)\n    {\n        return true;\n    }\n\n\n    ProxyItemModel::ProxyItemModel()\n        : mSourceModel(nullptr)\n    {\n    }\n\n    ProxyItemModel::~ProxyItemModel()\n    {\n        delete mSourceModel;\n    }\n\n    bool ProxyItemModel::allowedToUseItems() const\n    {\n        return mSourceModel->allowedToUseItems();\n    }\n\n    MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)\n    {\n        return mSourceModel->copyItem (item, count, allowAutoEquip);\n    }\n\n    void ProxyItemModel::removeItem (const ItemStack& item, size_t count)\n    {\n        mSourceModel->removeItem (item, count);\n    }\n\n    ItemModel::ModelIndex ProxyItemModel::mapToSource (ModelIndex index)\n    {\n        const ItemStack& itemToSearch = getItem(index);\n        for (size_t i=0; i<mSourceModel->getItemCount(); ++i)\n        {\n            const ItemStack& item = mSourceModel->getItem(i);\n            if (item.mBase == itemToSearch.mBase)\n                return i;\n        }\n        return -1;\n    }\n\n    ItemModel::ModelIndex ProxyItemModel::mapFromSource (ModelIndex index)\n    {\n        const ItemStack& itemToSearch = mSourceModel->getItem(index);\n        for (size_t i=0; i<getItemCount(); ++i)\n        {\n            const ItemStack& item = getItem(i);\n            if (item.mBase == itemToSearch.mBase)\n                return i;\n        }\n        return -1;\n    }\n\n    ItemModel::ModelIndex ProxyItemModel::getIndex (ItemStack item)\n    {\n        return mSourceModel->getIndex(item);\n    }\n\n    void ProxyItemModel::setSourceModel(ItemModel *sourceModel)\n    {\n        if (mSourceModel == sourceModel)\n            return;\n\n        if (mSourceModel)\n        {\n            delete mSourceModel;\n            mSourceModel = nullptr;\n        }\n\n        mSourceModel = sourceModel;\n    }\n\n    void ProxyItemModel::onClose()\n    {\n        mSourceModel->onClose();\n    }\n\n    bool ProxyItemModel::onDropItem(const MWWorld::Ptr &item, int count)\n    {\n        return mSourceModel->onDropItem(item, count);\n    }\n\n    bool ProxyItemModel::onTakeItem(const MWWorld::Ptr &item, int count)\n    {\n        return mSourceModel->onTakeItem(item, count);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/itemmodel.hpp",
    "content": "#ifndef MWGUI_ITEM_MODEL_H\n#define MWGUI_ITEM_MODEL_H\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWGui\n{\n\n    class ItemModel;\n\n    /// @brief A single item stack managed by an item model\n    struct ItemStack\n    {\n        ItemStack (const MWWorld::Ptr& base, ItemModel* creator, size_t count);\n        ItemStack();\n        ///< like operator==, only without checking mType\n\n        enum Type\n        {\n            Type_Barter,\n            Type_Equipped,\n            Type_Normal\n        };\n        Type mType;\n\n        enum Flags\n        {\n            Flag_Enchanted = (1<<0),\n            Flag_Bound = (1<<1)\n        };\n        int mFlags;\n\n        ItemModel* mCreator;\n        size_t mCount;\n        MWWorld::Ptr mBase;\n    };\n\n    bool operator == (const ItemStack& left, const ItemStack& right);\n\n\n    /// @brief The base class that all item models should derive from.\n    class ItemModel\n    {\n    public:\n        ItemModel();\n        virtual ~ItemModel() {}\n\n        typedef int ModelIndex; // -1 means invalid index\n\n        /// Throws for invalid index or out of range index\n        virtual ItemStack getItem (ModelIndex index) = 0;\n\n        /// The number of items in the model, this specifies the range of indices you can pass to\n        /// the getItem function (but this range is only valid until the next call to update())\n        virtual size_t getItemCount() = 0;\n\n        /// Returns an invalid index if the item was not found\n        virtual ModelIndex getIndex (ItemStack item) = 0;\n\n        /// Rebuild the item model, this will invalidate existing model indices\n        virtual void update() = 0;\n\n        /// Move items from this model to \\a otherModel.\n        /// @note Derived implementations may return an empty Ptr if the move was unsuccessful.\n        virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);\n\n        virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0;\n        virtual void removeItem (const ItemStack& item, size_t count) = 0;\n\n        /// Is the player allowed to use items from this item model? (default true)\n        virtual bool allowedToUseItems() const;\n        virtual void onClose()\n        {\n        }\n        virtual bool onDropItem(const MWWorld::Ptr &item, int count);\n        virtual bool onTakeItem(const MWWorld::Ptr &item, int count);\n\n    private:\n        ItemModel(const ItemModel&);\n        ItemModel& operator=(const ItemModel&);\n    };\n\n    /// @brief A proxy item model can be used to filter or rearrange items from a source model (or even add new items to it).\n    /// The neat thing is that this does not actually alter the source model.\n    class ProxyItemModel : public ItemModel\n    {\n    public:\n        ProxyItemModel();\n        virtual ~ProxyItemModel();\n\n        bool allowedToUseItems() const override;\n\n        void onClose() override;\n        bool onDropItem(const MWWorld::Ptr &item, int count) override;\n        bool onTakeItem(const MWWorld::Ptr &item, int count) override;\n\n        MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true) override;\n        void removeItem (const ItemStack& item, size_t count) override;\n        ModelIndex getIndex (ItemStack item) override;\n\n        /// @note Takes ownership of the passed pointer.\n        void setSourceModel(ItemModel* sourceModel);\n\n        ModelIndex mapToSource (ModelIndex index);\n        ModelIndex mapFromSource (ModelIndex index);\n    protected:\n        ItemModel* mSourceModel;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/itemselection.cpp",
    "content": "#include \"itemselection.hpp\"\n\n#include <MyGUI_TextBox.h>\n#include <MyGUI_Button.h>\n\n#include \"itemview.hpp\"\n#include \"inventoryitemmodel.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n\nnamespace MWGui\n{\n\n    ItemSelectionDialog::ItemSelectionDialog(const std::string &label)\n        : WindowModal(\"openmw_itemselection_dialog.layout\")\n        , mSortModel(nullptr)\n        , mModel(nullptr)\n    {\n        getWidget(mItemView, \"ItemView\");\n        mItemView->eventItemClicked += MyGUI::newDelegate(this, &ItemSelectionDialog::onSelectedItem);\n\n        MyGUI::TextBox* l;\n        getWidget(l, \"Label\");\n        l->setCaptionWithReplacing (label);\n\n        MyGUI::Button* cancelButton;\n        getWidget(cancelButton, \"CancelButton\");\n        cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked);\n\n        center();\n    }\n\n    bool ItemSelectionDialog::exit()\n    {\n        eventDialogCanceled();\n        return true;\n    }\n\n    void ItemSelectionDialog::openContainer(const MWWorld::Ptr& container)\n    {\n        mModel = new InventoryItemModel(container);\n        mSortModel = new SortFilterItemModel(mModel);\n        mItemView->setModel(mSortModel);\n        mItemView->resetScrollBars();\n    }\n\n    void ItemSelectionDialog::setCategory(int category)\n    {\n        mSortModel->setCategory(category);\n        mItemView->update();\n    }\n\n    void ItemSelectionDialog::setFilter(int filter)\n    {\n        mSortModel->setFilter(filter);\n        mItemView->update();\n    }\n\n    void ItemSelectionDialog::onSelectedItem(int index)\n    {\n        ItemStack item = mSortModel->getItem(index);\n        eventItemSelected(item.mBase);\n    }\n\n    void ItemSelectionDialog::onCancelButtonClicked(MyGUI::Widget* sender)\n    {\n        exit();\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/itemselection.hpp",
    "content": "#ifndef OPENMW_GAME_MWGUI_ITEMSELECTION_H\n#define OPENMW_GAME_MWGUI_ITEMSELECTION_H\n\n#include <MyGUI_Delegate.h>\n\n#include \"windowbase.hpp\"\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWGui\n{\n    class ItemView;\n    class SortFilterItemModel;\n    class InventoryItemModel;\n\n    class ItemSelectionDialog : public WindowModal\n    {\n    public:\n        ItemSelectionDialog(const std::string& label);\n\n        bool exit() override;\n\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n        typedef MyGUI::delegates::CMultiDelegate1<MWWorld::Ptr> EventHandle_Item;\n\n        EventHandle_Item eventItemSelected;\n        EventHandle_Void eventDialogCanceled;\n\n        void openContainer (const MWWorld::Ptr& container);\n        void setCategory(int category);\n        void setFilter(int filter);\n\n    private:\n        ItemView* mItemView;\n        SortFilterItemModel* mSortModel;\n        InventoryItemModel* mModel;\n\n        void onSelectedItem(int index);\n\n        void onCancelButtonClicked(MyGUI::Widget* sender);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/itemview.cpp",
    "content": "#include \"itemview.hpp\"\n\n#include <cmath>\n\n#include <MyGUI_FactoryManager.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_ScrollView.h>\n\n#include \"../mwworld/class.hpp\"\n\n#include \"itemmodel.hpp\"\n#include \"itemwidget.hpp\"\n\nnamespace MWGui\n{\n\nItemView::ItemView()\n    : mModel(nullptr)\n    , mScrollView(nullptr)\n{\n}\n\nItemView::~ItemView()\n{\n    delete mModel;\n}\n\nvoid ItemView::setModel(ItemModel *model)\n{\n    if (mModel == model)\n        return;\n\n    delete mModel;\n    mModel = model;\n\n    update();\n}\n\nvoid ItemView::initialiseOverride()\n{\n    Base::initialiseOverride();\n\n    assignWidget(mScrollView, \"ScrollView\");\n    if (mScrollView == nullptr)\n        throw std::runtime_error(\"Item view needs a scroll view\");\n\n    mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top);\n}\n\nvoid ItemView::layoutWidgets()\n{\n    if (!mScrollView->getChildCount())\n        return;\n\n    int x = 0;\n    int y = 0;\n    MyGUI::Widget* dragArea = mScrollView->getChildAt(0);\n    int maxHeight = mScrollView->getHeight();\n\n    int rows = maxHeight/42;\n    rows = std::max(rows, 1);\n    bool showScrollbar = int(std::ceil(dragArea->getChildCount()/float(rows))) > mScrollView->getWidth()/42;\n    if (showScrollbar)\n        maxHeight -= 18;\n\n    for (unsigned int i=0; i<dragArea->getChildCount(); ++i)\n    {\n        MyGUI::Widget* w = dragArea->getChildAt(i);\n\n        w->setPosition(x, y);\n\n        y += 42;\n\n        if (y > maxHeight-42 && i < dragArea->getChildCount()-1)\n        {\n            x += 42;\n            y = 0;\n        }\n    }\n    x += 42;\n\n    MyGUI::IntSize size = MyGUI::IntSize(std::max(mScrollView->getSize().width, x), mScrollView->getSize().height);\n\n    // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n    mScrollView->setVisibleVScroll(false);\n    mScrollView->setVisibleHScroll(false);\n    mScrollView->setCanvasSize(size);\n    mScrollView->setVisibleVScroll(true);\n    mScrollView->setVisibleHScroll(true);\n    dragArea->setSize(size);\n}\n\nvoid ItemView::update()\n{\n    while (mScrollView->getChildCount())\n        MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0));\n\n    if (!mModel)\n        return;\n\n    mModel->update();\n\n    MyGUI::Widget* dragArea = mScrollView->createWidget<MyGUI::Widget>(\"\",0,0,mScrollView->getWidth(),mScrollView->getHeight(),\n                                                                       MyGUI::Align::Stretch);\n    dragArea->setNeedMouseFocus(true);\n    dragArea->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedBackground);\n    dragArea->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheelMoved);\n\n    for (ItemModel::ModelIndex i=0; i<static_cast<int>(mModel->getItemCount()); ++i)\n    {\n        const ItemStack& item = mModel->getItem(i);\n\n        ItemWidget* itemWidget = dragArea->createWidget<ItemWidget>(\"MW_ItemIcon\",\n            MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default);\n        itemWidget->setUserString(\"ToolTipType\", \"ItemModelIndex\");\n        itemWidget->setUserData(std::make_pair(i, mModel));\n        ItemWidget::ItemState state = ItemWidget::None;\n        if (item.mType == ItemStack::Type_Barter)\n            state = ItemWidget::Barter;\n        if (item.mType == ItemStack::Type_Equipped)\n            state = ItemWidget::Equip;\n        itemWidget->setItem(item.mBase, state);\n        itemWidget->setCount(item.mCount);\n\n        itemWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedItem);\n        itemWidget->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheelMoved);\n    }\n\n    layoutWidgets();\n}\n\nvoid ItemView::resetScrollBars()\n{\n    mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));\n}\n\nvoid ItemView::onSelectedItem(MyGUI::Widget *sender)\n{\n    ItemModel::ModelIndex index = (*sender->getUserData<std::pair<ItemModel::ModelIndex, ItemModel*> >()).first;\n    eventItemClicked(index);\n}\n\nvoid ItemView::onSelectedBackground(MyGUI::Widget *sender)\n{\n    eventBackgroundClicked();\n}\n\nvoid ItemView::onMouseWheelMoved(MyGUI::Widget *_sender, int _rel)\n{\n    if (mScrollView->getViewOffset().left + _rel*0.3f > 0)\n        mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));\n    else\n        mScrollView->setViewOffset(MyGUI::IntPoint(static_cast<int>(mScrollView->getViewOffset().left + _rel*0.3f), 0));\n}\n\nvoid ItemView::setSize(const MyGUI::IntSize &_value)\n{\n    bool changed = (_value.width != getWidth() || _value.height != getHeight());\n    Base::setSize(_value);\n    if (changed)\n        layoutWidgets();\n}\n\nvoid ItemView::setCoord(const MyGUI::IntCoord &_value)\n{\n    bool changed = (_value.width != getWidth() || _value.height != getHeight());\n    Base::setCoord(_value);\n    if (changed)\n        layoutWidgets();\n}\n\nvoid ItemView::registerComponents()\n{\n    MyGUI::FactoryManager::getInstance().registerFactory<MWGui::ItemView>(\"Widget\");\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/itemview.hpp",
    "content": "#ifndef MWGUI_ITEMVIEW_H\n#define MWGUI_ITEMVIEW_H\n\n#include <MyGUI_Widget.h>\n\n#include \"itemmodel.hpp\"\n\nnamespace MWGui\n{\n\n    class ItemView final : public MyGUI::Widget\n    {\n    MYGUI_RTTI_DERIVED(ItemView)\n    public:\n        ItemView();\n        ~ItemView() override;\n\n        /// Register needed components with MyGUI's factory manager\n        static void registerComponents ();\n\n        /// Takes ownership of \\a model\n        void setModel (ItemModel* model);\n\n        typedef MyGUI::delegates::CMultiDelegate1<ItemModel::ModelIndex> EventHandle_ModelIndex;\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n        /// Fired when an item was clicked\n        EventHandle_ModelIndex eventItemClicked;\n        /// Fired when the background was clicked (useful for drag and drop)\n        EventHandle_Void eventBackgroundClicked;\n\n        void update();\n\n        void resetScrollBars();\n\n    private:\n        void initialiseOverride() override;\n\n        void layoutWidgets();\n\n        void setSize(const MyGUI::IntSize& _value) override;\n        void setCoord(const MyGUI::IntCoord& _value) override;\n\n        void onSelectedItem (MyGUI::Widget* sender);\n        void onSelectedBackground (MyGUI::Widget* sender);\n        void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel);\n\n        ItemModel* mModel;\n        MyGUI::ScrollView* mScrollView;\n\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/itemwidget.cpp",
    "content": "#include \"itemwidget.hpp\"\n\n#include <MyGUI_FactoryManager.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_TextBox.h>\n\n#include <components/debug/debuglog.hpp>\n// correctIconPath\n#include <components/resource/resourcesystem.hpp>\n#include <components/vfs/manager.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\nnamespace\n{\n    std::string getCountString(int count)\n    {\n        if (count == 1)\n            return \"\";\n\n        if (count > 999999999)\n            return MyGUI::utility::toString(count/1000000000) + \"b\";\n        else if (count > 999999)\n            return MyGUI::utility::toString(count/1000000) + \"m\";\n        else if (count > 9999)\n            return MyGUI::utility::toString(count/1000) + \"k\";\n        else\n            return MyGUI::utility::toString(count);\n    }\n}\n\nnamespace MWGui\n{\n    std::map<std::string, float> ItemWidget::mScales;\n\n    ItemWidget::ItemWidget()\n        : mItem(nullptr)\n        , mItemShadow(nullptr)\n        , mFrame(nullptr)\n        , mText(nullptr)\n    {\n\n    }\n\n    void ItemWidget::registerComponents()\n    {\n        MyGUI::FactoryManager::getInstance().registerFactory<ItemWidget>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<SpellWidget>(\"Widget\");\n    }\n\n    void ItemWidget::initialiseOverride()\n    {\n        assignWidget(mItem, \"Item\");\n        if (mItem)\n            mItem->setNeedMouseFocus(false);\n        assignWidget(mItemShadow, \"ItemShadow\");\n        if (mItemShadow)\n            mItemShadow->setNeedMouseFocus(false);\n        assignWidget(mFrame, \"Frame\");\n        if (mFrame)\n            mFrame->setNeedMouseFocus(false);\n        assignWidget(mText, \"Text\");\n        if (mText)\n            mText->setNeedMouseFocus(false);\n\n        Base::initialiseOverride();\n    }\n\n    void ItemWidget::setCount(int count)\n    {\n        if (!mText)\n            return;\n        mText->setCaption(getCountString(count));\n    }\n\n    void ItemWidget::setIcon(const std::string &icon)\n    {\n        if (mCurrentIcon != icon)\n        {\n            mCurrentIcon = icon;\n\n            if (mItemShadow)\n                mItemShadow->setImageTexture(icon);\n            if (mItem)\n                mItem->setImageTexture(icon);\n        }\n    }\n\n    void ItemWidget::setFrame(const std::string &frame, const MyGUI::IntCoord &coord)\n    {\n        if (mFrame)\n        {\n            mFrame->setImageTile(MyGUI::IntSize(coord.width, coord.height)); // Why is this needed? MyGUI bug?\n            mFrame->setImageCoord(coord);\n        }\n\n        if (mCurrentFrame != frame)\n        {\n            mCurrentFrame = frame;\n            mFrame->setImageTexture(frame);\n        }\n    }\n\n    void ItemWidget::setIcon(const MWWorld::Ptr &ptr)\n    {\n        std::string invIcon = ptr.getClass().getInventoryIcon(ptr);\n        if (invIcon.empty())\n            invIcon = \"default icon.tga\";\n        invIcon = MWBase::Environment::get().getWindowManager()->correctIconPath(invIcon);\n        if (!MWBase::Environment::get().getResourceSystem()->getVFS()->exists(invIcon))\n        {\n            Log(Debug::Error) << \"Failed to open image: '\" << invIcon << \"' not found, falling back to 'default-icon.tga'\";\n            invIcon = MWBase::Environment::get().getWindowManager()->correctIconPath(\"default icon.tga\");\n        }\n        setIcon(invIcon);\n    }\n\n\n    void ItemWidget::setItem(const MWWorld::Ptr &ptr, ItemState state)\n    {\n        if (!mItem)\n            return;\n\n        if (ptr.isEmpty())\n        {\n            if (mFrame)\n                mFrame->setImageTexture(\"\");\n            if (mItemShadow)\n                mItemShadow->setImageTexture(\"\");\n            mItem->setImageTexture(\"\");\n            mText->setCaption(\"\");\n            mCurrentIcon.clear();\n            mCurrentFrame.clear();\n            return;\n        }\n\n        bool isMagic = !ptr.getClass().getEnchantment(ptr).empty();\n\n        std::string backgroundTex = \"textures\\\\menu_icon\";\n        if (isMagic)\n            backgroundTex += \"_magic\";\n        if (state == None)\n        {\n            if (!isMagic)\n                backgroundTex = \"\";\n        }\n        else if (state == Equip)\n        {\n            backgroundTex += \"_equip\";\n        }\n        else if (state == Barter)\n            backgroundTex += \"_barter\";\n\n        if (backgroundTex != \"\")\n            backgroundTex += \".dds\";\n\n        float scale = 1.f;\n        if (!backgroundTex.empty())\n        {\n            auto found = mScales.find(backgroundTex);\n            if (found == mScales.end())\n            {\n                // By default, background icons are supposed to use the 42x42 part of 64x64 image.\n                // If the image has a different size, we should use a different chunk size\n                // Cache result to do not retrieve background texture every frame.\n                MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(backgroundTex);\n                if (texture)\n                    scale = texture->getHeight() / 64.f;\n\n                mScales[backgroundTex] = scale;\n            }\n            else\n                scale = found->second;\n        }\n\n        if (state == Barter && !isMagic)\n            setFrame(backgroundTex, MyGUI::IntCoord(2*scale,2*scale,44*scale,44*scale));\n        else\n            setFrame(backgroundTex, MyGUI::IntCoord(0,0,44*scale,44*scale));\n\n        setIcon(ptr);\n    }\n\n    void SpellWidget::setSpellIcon(const std::string& icon)\n    {\n        if (mFrame && !mCurrentFrame.empty())\n        {\n            mCurrentFrame.clear();\n            mFrame->setImageTexture(\"\");\n        }\n        if (mCurrentIcon != icon)\n        {\n            mCurrentIcon = icon;\n            if (mItemShadow)\n                mItemShadow->setImageTexture(icon);\n            if (mItem)\n                mItem->setImageTexture(icon);\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/itemwidget.hpp",
    "content": "#ifndef OPENMW_MWGUI_ITEMWIDGET_H\n#define OPENMW_MWGUI_ITEMWIDGET_H\n\n#include <MyGUI_Widget.h>\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWGui\n{\n\n    /// @brief A widget that shows an icon for an MWWorld::Ptr\n    class ItemWidget : public MyGUI::Widget\n    {\n    MYGUI_RTTI_DERIVED(ItemWidget)\n    public:\n        ItemWidget();\n\n        /// Register needed components with MyGUI's factory manager\n        static void registerComponents ();\n\n        enum ItemState\n        {\n            None,\n            Equip,\n            Barter,\n            Magic\n        };\n\n        /// Set count to be displayed in a textbox over the item\n        void setCount(int count);\n\n        /// \\a ptr may be empty\n        void setItem (const MWWorld::Ptr& ptr, ItemState state = None);\n\n        // Set icon and frame manually\n        void setIcon (const std::string& icon);\n        void setIcon (const MWWorld::Ptr& ptr);\n        void setFrame (const std::string& frame, const MyGUI::IntCoord& coord);\n\n    protected:\n        void initialiseOverride() override;\n\n        MyGUI::ImageBox* mItem;\n        MyGUI::ImageBox* mItemShadow;\n        MyGUI::ImageBox* mFrame;\n        MyGUI::TextBox* mText;\n\n        std::string mCurrentIcon;\n        std::string mCurrentFrame;\n\n        static std::map<std::string, float> mScales;\n    };\n\n    class SpellWidget : public ItemWidget\n    {\n    MYGUI_RTTI_DERIVED(SpellWidget)\n    public:\n\n        void setSpellIcon (const std::string& icon);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/jailscreen.cpp",
    "content": "#include <MyGUI_ScrollBar.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/store.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"jailscreen.hpp\"\n\nnamespace MWGui\n{\n    JailScreen::JailScreen()\n        : WindowBase(\"openmw_jail_screen.layout\"),\n          mDays(1),\n          mFadeTimeRemaining(0),\n          mTimeAdvancer(0.01f)\n    {\n        getWidget(mProgressBar, \"ProgressBar\");\n\n        mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &JailScreen::onJailProgressChanged);\n        mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &JailScreen::onJailFinished);\n\n        center();\n    }\n\n    void JailScreen::goToJail(int days)\n    {\n        mDays = days;\n\n        MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5);\n        mFadeTimeRemaining = 0.5;\n\n        setVisible(false);\n        mProgressBar->setScrollRange(100+1);\n        mProgressBar->setScrollPosition(0);\n        mProgressBar->setTrackSize(0);\n\n        /*\n            Start of tes3mp addition\n\n            If we've received a packet overriding the default jail progress text, use the new text\n        */\n        if (!mwmp::Main::get().getLocalPlayer()->jailProgressText.empty())\n            setText(\"LoadingText\", mwmp::Main::get().getLocalPlayer()->jailProgressText);\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void JailScreen::onFrame(float dt)\n    {\n        mTimeAdvancer.onFrame(dt);\n\n        if (mFadeTimeRemaining <= 0)\n            return;\n\n        mFadeTimeRemaining -= dt;\n\n        if (mFadeTimeRemaining <= 0)\n        {\n            MWWorld::Ptr player = MWMechanics::getPlayer();\n\n            /*\n                Start of tes3mp change (minor)\n\n                Prevent teleportation to jail if specified\n            */\n            if (!mwmp::Main::get().getLocalPlayer()->ignoreJailTeleportation)\n            {\n                MWBase::Environment::get().getWorld()->teleportToClosestMarker(player, \"prisonmarker\");\n                MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.f); // override fade-in caused by cell transition\n            }\n            /*\n                End of tes3mp change (minor)\n            */\n\n            setVisible(true);\n            mTimeAdvancer.run(100);\n        }\n    }\n\n    void JailScreen::onJailProgressChanged(int cur, int /*total*/)\n    {\n        mProgressBar->setScrollPosition(0);\n        mProgressBar->setTrackSize(static_cast<int>(cur / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize()));\n    }\n\n    void JailScreen::onJailFinished()\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Jail);\n        MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n\n        /*\n            Start of tes3mp addition\n\n            Declare pointer to LocalPlayer for use in other additions\n        */\n        mwmp::LocalPlayer* localPlayer = mwmp::Main::get().getLocalPlayer();\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getMechanicsManager()->rest(mDays * 24, true);\n\n        /*\n            Start of tes3mp change (major)\n\n            Multiplayer requires that time not get advanced here\n        */\n        //MWBase::Environment::get().getWorld()->advanceTime(mDays * 24);\n        /*\n            End of tes3mp change (major)\n        */\n\n        // We should not worsen corprus when in prison\n        for (auto& spell : player.getClass().getCreatureStats(player).getCorprusSpells())\n        {\n            spell.second.mNextWorsening += mDays * 24;\n        }\n\n        std::set<int> skills;\n        for (int day=0; day<mDays; ++day)\n        {\n            int skill = Misc::Rng::rollDice(ESM::Skill::Length);\n            skills.insert(skill);\n\n            MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill);\n\n            /*\n                Start of tes3mp change (minor)\n\n                Disable increases for Security and Sneak when using ignoreJailSkillIncreases\n            */\n            if (localPlayer->ignoreJailSkillIncreases)\n                value.setBase(std::max(0.f, value.getBase()-1));\n            else if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak)\n            /*\n                End of tes3mp change (minor)\n            */\n                value.setBase(std::min(100.f, value.getBase() + 1));\n            else\n                value.setBase(std::max(0.f, value.getBase()-1));\n        }\n\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        std::string message;\n        if (mDays == 1)\n            message = gmst.find(\"sNotifyMessage42\")->mValue.getString();\n        else\n            message = gmst.find(\"sNotifyMessage43\")->mValue.getString();\n\n        /*\n            Start of tes3mp addition\n\n            If we've received a packet overriding the default jail end text, use the new text\n        */\n        if (!localPlayer->jailEndText.empty())\n            message = mwmp::Main::get().getLocalPlayer()->jailEndText;\n        /*\n            End of tes3mp addition\n        */\n\n        message = Misc::StringUtils::format(message, mDays);\n\n        for (const int& skill : skills)\n        {\n            std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[skill])->mValue.getString();\n            int skillValue = player.getClass().getNpcStats(player).getSkill(skill).getBase();\n            std::string skillMsg = gmst.find(\"sNotifyMessage44\")->mValue.getString();\n\n            /*\n                Start of tes3mp change (minor)\n\n                Account for usage of ignoreJailSkillIncreases\n            */\n            if (!localPlayer->ignoreJailSkillIncreases &&\n                (skill == ESM::Skill::Sneak || skill == ESM::Skill::Security))\n            /*\n                End of tes3mp change (minor)\n            */\n                skillMsg = gmst.find(\"sNotifyMessage39\")->mValue.getString();\n\n            skillMsg = Misc::StringUtils::format(skillMsg, skillName, skillValue);\n            message += \"\\n\" + skillMsg;\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Reset all PlayerJail-related overrides\n        */\n        localPlayer->ignoreJailTeleportation = false;\n        localPlayer->ignoreJailSkillIncreases = false;\n        localPlayer->jailProgressText = \"\";\n        localPlayer->jailEndText = \"\";\n        /*\n            End of tes3mp addition\n        */\n\n        std::vector<std::string> buttons;\n        buttons.emplace_back(\"#{sOk}\");\n        MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/jailscreen.hpp",
    "content": "#ifndef MWGUI_JAILSCREEN_H\n#define MWGUI_JAILSCREEN_H\n\n#include \"windowbase.hpp\"\n#include \"timeadvancer.hpp\"\n\nnamespace MWGui\n{\n    class JailScreen : public WindowBase\n    {\n        public:\n            JailScreen();\n            void goToJail(int days);\n\n            void onFrame(float dt) override;\n\n            bool exit() override { return false; }\n\n        private:\n            int mDays;\n\n            float mFadeTimeRemaining;\n\n            MyGUI::ScrollBar* mProgressBar;\n\n            void onJailProgressChanged(int cur, int total);\n            void onJailFinished();\n\n            TimeAdvancer mTimeAdvancer;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/journalbooks.cpp",
    "content": "#include \"journalbooks.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include <components/fontloader/fontloader.hpp>\n#include <components/misc/utf8stream.hpp>\n\n#include \"textcolours.hpp\"\n\nnamespace\n{\n    struct AddContent\n    {\n        MWGui::BookTypesetter::Ptr mTypesetter;\n        MWGui::BookTypesetter::Style* mBodyStyle;\n\n        AddContent (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) :\n            mTypesetter (typesetter), mBodyStyle (body_style)\n        {\n        }\n    };\n\n    struct AddSpan : AddContent\n    {\n        AddSpan (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) :\n            AddContent (typesetter, body_style)\n        {\n        }\n\n        void operator () (intptr_t topicId, size_t begin, size_t end)\n        {\n            MWGui::BookTypesetter::Style* style = mBodyStyle;\n\n            const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();\n            if (topicId)\n                style = mTypesetter->createHotStyle (mBodyStyle, textColours.journalLink, textColours.journalLinkOver,\n                                                     textColours.journalLinkPressed, topicId);\n\n            mTypesetter->write (style, begin, end);\n        }\n    };\n\n    struct AddEntry\n    {\n        MWGui::BookTypesetter::Ptr mTypesetter;\n        MWGui::BookTypesetter::Style* mBodyStyle;\n\n        AddEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) :\n            mTypesetter (typesetter), mBodyStyle (body_style)\n        {\n        }\n\n        void operator () (MWGui::JournalViewModel::Entry const & entry)\n        {\n            mTypesetter->addContent (entry.body ());\n\n            entry.visitSpans (AddSpan (mTypesetter, mBodyStyle));\n        }\n    };\n\n    struct AddJournalEntry : AddEntry\n    {\n        bool mAddHeader;\n        MWGui::BookTypesetter::Style* mHeaderStyle;\n\n        AddJournalEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style,\n                            MWGui::BookTypesetter::Style* header_style, bool add_header) :\n            AddEntry (typesetter, body_style),\n            mAddHeader (add_header),\n            mHeaderStyle (header_style)\n        {\n        }\n\n        void operator () (MWGui::JournalViewModel::JournalEntry const & entry)\n        {\n            if (mAddHeader)\n            {\n                mTypesetter->write (mHeaderStyle, entry.timestamp ());\n                mTypesetter->lineBreak ();\n            }\n\n            AddEntry::operator () (entry);\n\n            mTypesetter->sectionBreak (30);\n        }\n    };\n\n    struct AddTopicEntry : AddEntry\n    {\n        intptr_t mContentId;\n        MWGui::BookTypesetter::Style* mHeaderStyle;\n\n        AddTopicEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style,\n                        MWGui::BookTypesetter::Style* header_style, intptr_t contentId) :\n            AddEntry (typesetter, body_style), mContentId (contentId), mHeaderStyle (header_style)\n        {\n        }\n\n        void operator () (MWGui::JournalViewModel::TopicEntry const & entry)\n        {\n            mTypesetter->write (mBodyStyle, entry.source ());\n            mTypesetter->write (mBodyStyle, 0, 3);// begin\n\n            AddEntry::operator() (entry);\n\n            mTypesetter->selectContent (mContentId);\n            mTypesetter->write (mBodyStyle, 2, 3);// end quote\n\n            mTypesetter->sectionBreak (30);\n        }\n    };\n\n    struct AddTopicName : AddContent\n    {\n        AddTopicName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) :\n            AddContent (typesetter, style)\n        {\n        }\n\n        void operator () (MWGui::JournalViewModel::Utf8Span topicName)\n        {\n            mTypesetter->write (mBodyStyle, topicName);\n            mTypesetter->sectionBreak ();\n        }\n    };\n\n    struct AddQuestName : AddContent\n    {\n        AddQuestName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) :\n            AddContent (typesetter, style)\n        {\n        }\n\n        void operator () (MWGui::JournalViewModel::Utf8Span topicName)\n        {\n            mTypesetter->write (mBodyStyle, topicName);\n            mTypesetter->sectionBreak ();\n        }\n    };\n}\n\nnamespace MWGui\n{\n\nMWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)\n{\n    typedef MWGui::BookTypesetter::Utf8Point point;\n\n    point begin = reinterpret_cast <point> (text);\n\n    return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));\n}\n\ntypedef TypesetBook::Ptr book;\n\nJournalBooks::JournalBooks (JournalViewModel::Ptr model, ToUTF8::FromType encoding) :\n    mModel (model), mEncoding(encoding), mIndexPagesCount(0)\n{\n}\n\nbook JournalBooks::createEmptyJournalBook ()\n{\n    BookTypesetter::Ptr typesetter = createTypesetter ();\n\n    BookTypesetter::Style* header = typesetter->createStyle (\"\", MyGUI::Colour (0.60f, 0.00f, 0.00f));\n    BookTypesetter::Style* body   = typesetter->createStyle (\"\", MyGUI::Colour::Black);\n\n    typesetter->write (header, to_utf8_span (\"You have no journal entries!\"));\n    typesetter->lineBreak ();\n    typesetter->write (body, to_utf8_span (\"You should have gone though the starting quest and got an initial quest.\"));\n\n    return typesetter->complete ();\n}\n\nbook JournalBooks::createJournalBook ()\n{\n    BookTypesetter::Ptr typesetter = createTypesetter ();\n\n    BookTypesetter::Style* header = typesetter->createStyle (\"\", MyGUI::Colour (0.60f, 0.00f, 0.00f));\n    BookTypesetter::Style* body   = typesetter->createStyle (\"\", MyGUI::Colour::Black);\n\n    mModel->visitJournalEntries (\"\", AddJournalEntry (typesetter, body, header, true));\n\n    return typesetter->complete ();\n}\n\nbook JournalBooks::createTopicBook (uintptr_t topicId)\n{\n    BookTypesetter::Ptr typesetter = createTypesetter ();\n\n    BookTypesetter::Style* header = typesetter->createStyle (\"\", MyGUI::Colour (0.60f, 0.00f, 0.00f));\n    BookTypesetter::Style* body   = typesetter->createStyle (\"\", MyGUI::Colour::Black);\n\n    mModel->visitTopicName (topicId, AddTopicName (typesetter, header));\n\n    intptr_t contentId = typesetter->addContent (to_utf8_span (\", \\\"\"));\n\n    mModel->visitTopicEntries (topicId, AddTopicEntry (typesetter, body, header, contentId));\n\n    return typesetter->complete ();\n}\n\nbook JournalBooks::createQuestBook (const std::string& questName)\n{\n    BookTypesetter::Ptr typesetter = createTypesetter ();\n\n    BookTypesetter::Style* header = typesetter->createStyle (\"\", MyGUI::Colour (0.60f, 0.00f, 0.00f));\n    BookTypesetter::Style* body   = typesetter->createStyle (\"\", MyGUI::Colour::Black);\n\n    AddQuestName addName (typesetter, header);\n    addName(to_utf8_span(questName.c_str()));\n\n    mModel->visitJournalEntries (questName, AddJournalEntry (typesetter, body, header, false));\n\n    return typesetter->complete ();\n}\n\nbook JournalBooks::createTopicIndexBook ()\n{\n    bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251);\n\n    BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex();\n\n    return typesetter->complete ();\n}\n\nBookTypesetter::Ptr JournalBooks::createLatinJournalIndex ()\n{\n    BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260);\n\n    typesetter->setSectionAlignment (BookTypesetter::AlignCenter);\n\n    // Latin journal index always has two columns for now.\n    mIndexPagesCount = 2;\n\n    char ch = 'A';\n\n    BookTypesetter::Style* body = typesetter->createStyle (\"\", MyGUI::Colour::Black);\n    for (int i = 0; i < 26; ++i)\n    {\n        char buffer [32];\n        sprintf (buffer, \"( %c )\", ch);\n\n        const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();\n        BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic,\n                                                                   textColours.journalTopicOver,\n                                                                   textColours.journalTopicPressed, (Utf8Stream::UnicodeChar) ch);\n\n        if (i == 13)\n            typesetter->sectionBreak ();\n\n        typesetter->write (style, to_utf8_span (buffer));\n        typesetter->lineBreak ();\n\n        ch++;\n    }\n\n    return typesetter;\n}\n\nBookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex ()\n{\n    BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260);\n\n    typesetter->setSectionAlignment (BookTypesetter::AlignCenter);\n\n    BookTypesetter::Style* body = typesetter->createStyle (\"\", MyGUI::Colour::Black);\n\n    int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight();\n\n    // for small font size split alphabet to two columns (2x15 characers), for big font size split it to three colums (3x10 characters).\n    int sectionBreak = 10;\n    mIndexPagesCount = 3;\n    if (fontHeight < 18)\n    {\n        sectionBreak = 15;\n        mIndexPagesCount = 2;\n    }\n\n    unsigned char ch[3] = {0xd0, 0x90, 0x00}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8\n\n    for (int i = 0; i < 32; ++i)\n    {\n        char buffer [32];\n        sprintf(buffer, \"( %c%c )\", ch[0], ch[1]);\n\n        Utf8Stream stream ((char*) ch);\n        Utf8Stream::UnicodeChar first = stream.peek();\n\n        const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();\n        BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic,\n                                                                   textColours.journalTopicOver,\n                                                                   textColours.journalTopicPressed, first);\n\n        ch[1]++;\n\n        // Words can not be started with these characters\n        if (i == 26 || i == 28)\n            continue;\n\n        if (i % sectionBreak == 0)\n            typesetter->sectionBreak ();\n\n        typesetter->write (style, to_utf8_span (buffer));\n        typesetter->lineBreak ();\n    }\n\n    return typesetter;\n}\n\nBookTypesetter::Ptr JournalBooks::createTypesetter ()\n{\n    //TODO: determine page size from layout...\n    return BookTypesetter::create (240, 320);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/journalbooks.hpp",
    "content": "#ifndef MWGUI_JOURNALBOOKS_HPP\n#define MWGUI_JOURNALBOOKS_HPP\n\n#include \"bookpage.hpp\"\n#include \"journalviewmodel.hpp\"\n\n#include <components/to_utf8/to_utf8.hpp>\n\nnamespace MWGui\n{\n    MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text);\n\n    struct JournalBooks\n    {\n        typedef TypesetBook::Ptr Book;\n        JournalViewModel::Ptr mModel;\n\n        JournalBooks (JournalViewModel::Ptr model, ToUTF8::FromType encoding);\n\n        Book createEmptyJournalBook ();\n        Book createJournalBook ();\n        Book createTopicBook (uintptr_t topicId);\n        Book createTopicBook (const std::string& topicId);\n        Book createQuestBook (const std::string& questName);\n        Book createTopicIndexBook ();\n\n        ToUTF8::FromType mEncoding;\n        int mIndexPagesCount;\n\n    private:\n        BookTypesetter::Ptr createTypesetter ();\n        BookTypesetter::Ptr createLatinJournalIndex ();\n        BookTypesetter::Ptr createCyrillicJournalIndex ();\n    };\n}\n\n#endif // MWGUI_JOURNALBOOKS_HPP\n"
  },
  {
    "path": "apps/openmw/mwgui/journalviewmodel.cpp",
    "content": "#include \"journalviewmodel.hpp\"\n\n#include <map>\n#include <sstream>\n\n#include <MyGUI_LanguageManager.h>\n\n#include <components/translation/translation.hpp>\n#include <components/misc/stringops.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/journal.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwdialogue/keywordsearch.hpp\"\n\nnamespace MWGui {\n\nstruct JournalViewModelImpl;\n\nstruct JournalViewModelImpl : JournalViewModel\n{\n    typedef MWDialogue::KeywordSearch <std::string, intptr_t> KeywordSearchT;\n\n    mutable bool             mKeywordSearchLoaded;\n    mutable KeywordSearchT mKeywordSearch;\n\n    JournalViewModelImpl ()\n    {\n        mKeywordSearchLoaded = false;\n    }\n\n    virtual ~JournalViewModelImpl ()\n    {\n    }\n\n    /// \\todo replace this nasty BS\n    static Utf8Span toUtf8Span (std::string const & str)\n    {\n        if (str.size () == 0)\n            return Utf8Span (Utf8Point (nullptr), Utf8Point (nullptr));\n\n        Utf8Point point = reinterpret_cast <Utf8Point> (str.c_str ());\n\n        return Utf8Span (point, point + str.size ());\n    }\n\n    void load () override\n    {\n    }\n\n    void unload () override\n    {\n        mKeywordSearch.clear ();\n        mKeywordSearchLoaded = false;\n    }\n\n    void ensureKeyWordSearchLoaded () const\n    {\n        if (!mKeywordSearchLoaded)\n        {\n            MWBase::Journal * journal = MWBase::Environment::get().getJournal();\n\n            for(MWBase::Journal::TTopicIter i = journal->topicBegin(); i != journal->topicEnd (); ++i)\n                mKeywordSearch.seed (i->first, intptr_t (&i->second));\n\n            mKeywordSearchLoaded = true;\n        }\n    }\n\n    bool isEmpty () const override\n    {\n        MWBase::Journal * journal = MWBase::Environment::get().getJournal();\n\n        return journal->begin () == journal->end ();\n    }\n\n    template <typename t_iterator, typename Interface>\n    struct BaseEntry : Interface\n    {\n        typedef t_iterator iterator_t;\n\n        iterator_t                      itr;\n        JournalViewModelImpl const *    mModel;\n\n        BaseEntry (JournalViewModelImpl const * model, iterator_t itr) :\n            itr (itr), mModel (model), loaded (false)\n        {}\n\n        virtual ~BaseEntry () {}\n\n        mutable bool loaded;\n        mutable std::string utf8text;\n\n        typedef std::pair<size_t, size_t> Range;\n\n        // hyperlinks in @link# notation\n        mutable std::map<Range, intptr_t> mHyperLinks;\n\n        virtual std::string getText () const = 0;\n\n        void ensureLoaded () const\n        {\n            if (!loaded)\n            {\n                mModel->ensureKeyWordSearchLoaded ();\n\n                utf8text = getText ();\n\n                size_t pos_end = 0;\n                for(;;)\n                {\n                    size_t pos_begin = utf8text.find('@');\n                    if (pos_begin != std::string::npos)\n                        pos_end = utf8text.find('#', pos_begin);\n\n                    if (pos_begin != std::string::npos && pos_end != std::string::npos)\n                    {\n                        std::string link = utf8text.substr(pos_begin + 1, pos_end - pos_begin - 1);\n                        const char specialPseudoAsteriskCharacter = 127;\n                        std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*');\n                        std::string topicName = MWBase::Environment::get().getWindowManager()->\n                                getTranslationDataStorage().topicStandardForm(link);\n\n                        std::string displayName = link;\n                        while (displayName[displayName.size()-1] == '*')\n                            displayName.erase(displayName.size()-1, 1);\n\n                        utf8text.replace(pos_begin, pos_end+1-pos_begin, displayName);\n\n                        intptr_t value = 0;\n                        if (mModel->mKeywordSearch.containsKeyword(topicName, value))\n                            mHyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = value;\n                    }\n                    else\n                        break;\n                }\n\n                loaded = true;\n            }\n        }\n\n        Utf8Span body () const override\n        {\n            ensureLoaded ();\n\n            return toUtf8Span (utf8text);\n        }\n\n        void visitSpans (std::function < void (TopicId, size_t, size_t)> visitor) const override\n        {\n            ensureLoaded ();\n            mModel->ensureKeyWordSearchLoaded ();\n\n            if (mHyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())\n            {\n                size_t formatted = 0; // points to the first character that is not laid out yet\n                for (std::map<Range, intptr_t>::const_iterator it = mHyperLinks.begin(); it != mHyperLinks.end(); ++it)\n                {\n                    intptr_t topicId = it->second;\n                    if (formatted < it->first.first)\n                        visitor (0, formatted, it->first.first);\n                    visitor (topicId, it->first.first, it->first.second);\n                    formatted = it->first.second;\n                }\n                if (formatted < utf8text.size())\n                    visitor (0, formatted, utf8text.size());\n            }\n            else\n            {\n                std::vector<KeywordSearchT::Match> matches;\n                mModel->mKeywordSearch.highlightKeywords(utf8text.begin(), utf8text.end(), matches);\n\n                std::string::const_iterator i = utf8text.begin ();\n                for (std::vector<KeywordSearchT::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it)\n                {\n                    const KeywordSearchT::Match& match = *it;\n\n                    if (i != match.mBeg)\n                        visitor (0, i - utf8text.begin (), match.mBeg - utf8text.begin ());\n\n                    visitor (match.mValue, match.mBeg - utf8text.begin (), match.mEnd - utf8text.begin ());\n\n                    i = match.mEnd;\n                }\n\n                if (i != utf8text.end ())\n                    visitor (0, i - utf8text.begin (), utf8text.size ());\n            }\n        }\n\n    };\n\n    void visitQuestNames (bool active_only, std::function <void (const std::string&, bool)> visitor) const override\n    {\n        MWBase::Journal * journal = MWBase::Environment::get ().getJournal ();\n\n        std::set<std::string> visitedQuests;\n\n        // Note that for purposes of the journal GUI, quests are identified by the name, not the ID, so several\n        // different quest IDs can end up in the same quest log. A quest log should be considered finished\n        // when any quest ID in that log is finished.\n        for (MWBase::Journal::TQuestIter i = journal->questBegin (); i != journal->questEnd (); ++i)\n        {\n            const MWDialogue::Quest& quest = i->second;\n\n            bool isFinished = false;\n            for (MWBase::Journal::TQuestIter j = journal->questBegin (); j != journal->questEnd (); ++j)\n            {\n                if (quest.getName() == j->second.getName() && j->second.isFinished())\n                    isFinished = true;\n            }\n\n            if (active_only && isFinished)\n                continue;\n\n            // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal.\n            // Note that even with Tribunal, some quests still don't have quest names. I'm assuming those are not supposed\n            // to appear in the quest book.\n            if (!quest.getName().empty())\n            {\n                // Don't list the same quest name twice\n                if (visitedQuests.find(quest.getName()) != visitedQuests.end())\n                    continue;\n\n                visitor (quest.getName(), isFinished);\n\n                visitedQuests.insert(quest.getName());\n            }\n        }\n    }\n\n    template <typename iterator_t>\n    struct JournalEntryImpl : BaseEntry <iterator_t, JournalEntry>\n    {\n        using BaseEntry <iterator_t, JournalEntry>::itr;\n\n        mutable std::string timestamp_buffer;\n\n        JournalEntryImpl (JournalViewModelImpl const * model, iterator_t itr) :\n            BaseEntry <iterator_t, JournalEntry> (model, itr)\n        {}\n\n        std::string getText () const override\n        {\n            return itr->getText();\n        }\n\n        Utf8Span timestamp () const override\n        {\n            if (timestamp_buffer.empty ())\n            {\n                std::string dayStr = MyGUI::LanguageManager::getInstance().replaceTags(\"#{sDay}\");\n\n                std::ostringstream os;\n\n                os\n                    << itr->mDayOfMonth << ' '\n                    << MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth)\n                    << \" (\" << dayStr << \" \" << (itr->mDay) << ')';\n\n                timestamp_buffer = os.str ();\n            }\n\n            return toUtf8Span (timestamp_buffer);\n        }\n    };\n\n    void visitJournalEntries (const std::string& questName, std::function <void (JournalEntry const &)> visitor) const override\n    {\n        MWBase::Journal * journal = MWBase::Environment::get().getJournal();\n\n        if (!questName.empty())\n        {\n            std::vector<MWDialogue::Quest const*> quests;\n            for (MWBase::Journal::TQuestIter questIt = journal->questBegin(); questIt != journal->questEnd(); ++questIt)\n            {\n                if (Misc::StringUtils::ciEqual(questIt->second.getName(), questName))\n                    quests.push_back(&questIt->second);\n            }\n\n            for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i)\n            {\n                for (std::vector<MWDialogue::Quest const*>::iterator questIt = quests.begin(); questIt != quests.end(); ++questIt)\n                {\n                    MWDialogue::Quest const* quest = *questIt;\n                    for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j)\n                    {\n                        if (i->mInfoId == j->mInfoId)\n                            visitor (JournalEntryImpl <MWBase::Journal::TEntryIter> (this, i));\n                    }\n                }\n            }\n        }\n        else\n        {\n            for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i)\n                visitor (JournalEntryImpl <MWBase::Journal::TEntryIter> (this, i));\n        }\n    }\n\n    void visitTopicName (TopicId topicId, std::function <void (Utf8Span)> visitor) const override\n    {\n        MWDialogue::Topic const & topic = * reinterpret_cast <MWDialogue::Topic const *> (topicId);\n        visitor (toUtf8Span (topic.getName()));\n    }\n\n    void visitTopicNamesStartingWith (Utf8Stream::UnicodeChar character, std::function < void (const std::string&) > visitor) const override\n    {\n        MWBase::Journal * journal = MWBase::Environment::get().getJournal();\n\n        for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i)\n        {\n            Utf8Stream stream (i->first.c_str());\n            Utf8Stream::UnicodeChar first = Misc::StringUtils::toLowerUtf8(stream.peek());\n\n            if (first != Misc::StringUtils::toLowerUtf8(character))\n                continue;\n\n            visitor (i->second.getName());\n        }\n    }\n\n    struct TopicEntryImpl : BaseEntry <MWDialogue::Topic::TEntryIter, TopicEntry>\n    {\n        MWDialogue::Topic const & mTopic;\n\n        TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) :\n            BaseEntry (model, itr), mTopic (topic)\n        {}\n\n        std::string getText () const override\n        {\n            return  itr->getText();\n        }\n\n        Utf8Span source () const override\n        {\n            return toUtf8Span (itr->mActorName);\n        }\n\n    };\n\n    void visitTopicEntries (TopicId topicId, std::function <void (TopicEntry const &)> visitor) const override\n    {\n        typedef MWDialogue::Topic::TEntryIter iterator_t;\n\n        MWDialogue::Topic const & topic = * reinterpret_cast <MWDialogue::Topic const *> (topicId);\n\n        for (iterator_t i = topic.begin (); i != topic.end (); ++i)\n            visitor (TopicEntryImpl (this, topic, i));\n    }\n};\n\nJournalViewModel::Ptr JournalViewModel::create ()\n{\n    return std::make_shared <JournalViewModelImpl> ();\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/journalviewmodel.hpp",
    "content": "#ifndef MWGUI_JOURNALVIEWMODEL_HPP\n#define MWGUI_JOURNALVIEWMODEL_HPP\n\n#include <string>\n#include <memory>\n#include <functional>\n#include <stdint.h>\n\n#include <components/misc/utf8stream.hpp>\n\nnamespace MWGui\n{\n    /// View-Model for the journal GUI\n    ///\n    /// This interface defines an abstract data model suited\n    ///  specifically to the needs of the journal GUI. It isolates\n    /// the journal GUI from the implementation details of the\n    /// game data store.\n    struct JournalViewModel\n    {\n        typedef std::shared_ptr <JournalViewModel> Ptr;\n\n        typedef intptr_t QuestId;\n        typedef intptr_t TopicId;\n        typedef uint8_t const * Utf8Point;\n        typedef std::pair <Utf8Point, Utf8Point> Utf8Span;\n\n        /// The base interface for both journal entries and topics.\n        struct Entry\n        {\n            /// returns the body text for the journal entry\n            ///\n            /// This function returns a borrowed reference to the body of the\n            /// journal entry. The returned reference becomes invalid when the\n            /// entry is destroyed.\n            virtual Utf8Span body () const = 0;\n\n            /// Visits each subset of text in the body, delivering the beginning\n            /// and end of the span relative to the body, and a valid topic ID if\n            /// the span represents a keyword, or zero if not.\n            virtual void visitSpans (std::function <void (TopicId, size_t, size_t)> visitor) const = 0;\n\n            virtual ~Entry() = default;\n        };\n\n        /// An interface to topic data.\n        struct TopicEntry : Entry\n        {\n            /// Returns a pre-formatted span of UTF8 encoded text representing\n            /// the name of the NPC this portion of dialog was heard from.\n            virtual Utf8Span source () const = 0;\n\n            virtual ~TopicEntry() = default;\n        };\n\n        /// An interface to journal data.\n        struct JournalEntry : Entry\n        {\n            /// Returns a pre-formatted span of UTF8 encoded text representing\n            /// the in-game date this entry was added to the journal.\n            virtual Utf8Span timestamp () const = 0;\n\n            virtual ~JournalEntry() = default;\n        };\n\n        /// called prior to journal opening\n        virtual void load () = 0;\n\n        /// called prior to journal closing\n        virtual void unload () = 0;\n\n        /// returns true if their are no journal entries to display\n        virtual bool isEmpty () const = 0;\n\n        /// walks the active and optionally completed, quests providing the name and completed status\n        virtual void visitQuestNames (bool active_only, std::function <void (const std::string&, bool)> visitor) const = 0;\n\n        /// walks over the journal entries related to all quests with the given name\n        /// If \\a questName is empty, simply visits all journal entries\n        virtual void visitJournalEntries (const std::string& questName, std::function <void (JournalEntry const &)> visitor) const = 0;\n\n        /// provides the name of the topic specified by its id\n        virtual void visitTopicName (TopicId topicId, std::function <void (Utf8Span)> visitor) const = 0;\n\n        /// walks over the topics whose names start with the character\n        virtual void visitTopicNamesStartingWith (Utf8Stream::UnicodeChar character, std::function < void (const std::string&) > visitor) const = 0;\n\n        /// walks over the topic entries for the topic specified by its identifier\n        virtual void visitTopicEntries (TopicId topicId, std::function <void (TopicEntry const &)> visitor) const = 0;\n\n        // create an instance of the default journal view model implementation\n        static Ptr create ();\n\n        virtual ~JournalViewModel() = default;\n    };\n}\n\n#endif // MWGUI_JOURNALVIEWMODEL_HPP\n"
  },
  {
    "path": "apps/openmw/mwgui/journalwindow.cpp",
    "content": "#include \"journalwindow.hpp\"\n\n#include <set>\n#include <stack>\n#include <string>\n#include <utility>\n\n#include <MyGUI_TextBox.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_InputManager.h>\n\n#include <components/misc/stringops.hpp>\n#include <components/widgets/imagebutton.hpp>\n#include <components/widgets/list.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/journal.hpp\"\n\n#include \"bookpage.hpp\"\n#include \"windowbase.hpp\"\n#include \"journalviewmodel.hpp\"\n#include \"journalbooks.hpp\"\n\nnamespace\n{\n    static char const OptionsOverlay [] = \"OptionsOverlay\";\n    static char const OptionsBTN [] = \"OptionsBTN\";\n    static char const PrevPageBTN [] = \"PrevPageBTN\";\n    static char const NextPageBTN [] = \"NextPageBTN\";\n    static char const CloseBTN [] = \"CloseBTN\";\n    static char const JournalBTN [] = \"JournalBTN\";\n    static char const TopicsBTN [] = \"TopicsBTN\";\n    static char const QuestsBTN [] = \"QuestsBTN\";\n    static char const CancelBTN [] = \"CancelBTN\";\n    static char const ShowAllBTN [] = \"ShowAllBTN\";\n    static char const ShowActiveBTN [] = \"ShowActiveBTN\";\n    static char const PageOneNum [] = \"PageOneNum\";\n    static char const PageTwoNum [] = \"PageTwoNum\";\n    static char const TopicsList [] = \"TopicsList\";\n    static char const QuestsList [] = \"QuestsList\";\n    static char const LeftBookPage [] = \"LeftBookPage\";\n    static char const RightBookPage [] = \"RightBookPage\";\n    static char const LeftTopicIndex [] = \"LeftTopicIndex\";\n    static char const CenterTopicIndex [] = \"CenterTopicIndex\";\n    static char const RightTopicIndex [] = \"RightTopicIndex\";\n\n    struct JournalWindowImpl : MWGui::JournalBooks, MWGui::JournalWindow\n    {\n        struct DisplayState\n        {\n            unsigned int mPage;\n            Book mBook;\n        };\n\n        typedef std::stack <DisplayState> DisplayStateStack;\n\n        DisplayStateStack mStates;\n        Book mTopicIndexBook;\n        bool mQuestMode;\n        bool mOptionsMode;\n        bool mTopicsMode;\n        bool mAllQuests;\n\n        template <typename T>\n        T * getWidget (char const * name)\n        {\n            T * widget;\n            WindowBase::getWidget (widget, name);\n            return widget;\n        }\n\n        template <typename value_type>\n        void setText (char const * name, value_type const & value)\n        {\n            getWidget <MyGUI::TextBox> (name) ->\n                setCaption (MyGUI::utility::toString (value));\n        }\n\n        void setVisible (char const * name, bool visible)\n        {\n            getWidget <MyGUI::Widget> (name) ->\n                setVisible (visible);\n        }\n\n        void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender))\n        {\n            getWidget <MyGUI::Widget> (name) ->\n                eventMouseButtonClick += newDelegate(this, Handler);\n        }\n\n        void adviseKeyPress (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character))\n        {\n            getWidget <MyGUI::Widget> (name) ->\n                eventKeyButtonPressed += newDelegate(this, Handler);\n        }\n\n        MWGui::BookPage* getPage (char const * name)\n        {\n            return getWidget <MWGui::BookPage> (name);\n        }\n\n        JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList, ToUTF8::FromType encoding)\n            : JournalBooks (Model, encoding), JournalWindow()\n        {\n            center();\n\n            adviseButtonClick (OptionsBTN,    &JournalWindowImpl::notifyOptions   );\n            adviseButtonClick (PrevPageBTN,   &JournalWindowImpl::notifyPrevPage  );\n            adviseButtonClick (NextPageBTN,   &JournalWindowImpl::notifyNextPage  );\n            adviseButtonClick (CloseBTN,      &JournalWindowImpl::notifyClose     );\n            adviseButtonClick (JournalBTN,    &JournalWindowImpl::notifyJournal   );\n\n            adviseButtonClick (TopicsBTN,     &JournalWindowImpl::notifyTopics    );\n            adviseButtonClick (QuestsBTN,     &JournalWindowImpl::notifyQuests    );\n            adviseButtonClick (CancelBTN,     &JournalWindowImpl::notifyCancel    );\n\n            adviseButtonClick (ShowAllBTN,    &JournalWindowImpl::notifyShowAll   );\n            adviseButtonClick (ShowActiveBTN, &JournalWindowImpl::notifyShowActive);\n\n            adviseKeyPress (OptionsBTN, &JournalWindowImpl::notifyKeyPress);\n            adviseKeyPress (PrevPageBTN, &JournalWindowImpl::notifyKeyPress);\n            adviseKeyPress (NextPageBTN, &JournalWindowImpl::notifyKeyPress);\n            adviseKeyPress (CloseBTN, &JournalWindowImpl::notifyKeyPress);\n            adviseKeyPress (JournalBTN, &JournalWindowImpl::notifyKeyPress);\n\n            Gui::MWList* list = getWidget<Gui::MWList>(QuestsList);\n            list->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyQuestClicked);\n\n            Gui::MWList* topicsList = getWidget<Gui::MWList>(TopicsList);\n            topicsList->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyTopicSelected);\n\n            {\n                MWGui::BookPage::ClickCallback callback;\n                \n                callback = std::bind (&JournalWindowImpl::notifyTopicClicked, this, std::placeholders::_1);\n\n                getPage (LeftBookPage)->adviseLinkClicked (callback);\n                getPage (RightBookPage)->adviseLinkClicked (callback);\n\n                getPage (LeftBookPage)->eventMouseWheel += MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);\n                getPage (RightBookPage)->eventMouseWheel += MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);\n            }\n\n            {\n                MWGui::BookPage::ClickCallback callback;\n                \n                callback = std::bind(&JournalWindowImpl::notifyIndexLinkClicked, this, std::placeholders::_1);\n\n                getPage (LeftTopicIndex)->adviseLinkClicked (callback);\n                getPage (CenterTopicIndex)->adviseLinkClicked (callback);\n                getPage (RightTopicIndex)->adviseLinkClicked (callback);\n            }\n\n            adjustButton(PrevPageBTN);\n            float nextButtonScale = adjustButton(NextPageBTN);\n            adjustButton(CloseBTN);\n            adjustButton(CancelBTN);\n            adjustButton(JournalBTN);\n\n            Gui::ImageButton* optionsButton = getWidget<Gui::ImageButton>(OptionsBTN);\n            Gui::ImageButton* showActiveButton = getWidget<Gui::ImageButton>(ShowActiveBTN);\n            Gui::ImageButton* showAllButton = getWidget<Gui::ImageButton>(ShowAllBTN);\n            Gui::ImageButton* questsButton = getWidget<Gui::ImageButton>(QuestsBTN);\n\n            Gui::ImageButton* nextButton = getWidget<Gui::ImageButton>(NextPageBTN);\n            if (nextButton->getSize().width == 64)\n            {\n                // english button has a 7 pixel wide strip of garbage on its right edge\n                nextButton->setSize(64-7, nextButton->getSize().height);\n                nextButton->setImageCoord(MyGUI::IntCoord(0,0,(64-7)*nextButtonScale,nextButton->getSize().height*nextButtonScale));\n            }\n\n            if (!questList)\n            {\n                // If tribunal is not installed (-> no options button), we still want the Topics button available,\n                // so place it where the options button would have been\n                Gui::ImageButton* topicsButton = getWidget<Gui::ImageButton>(TopicsBTN);\n                topicsButton->detachFromWidget();\n                topicsButton->attachToWidget(optionsButton->getParent());\n                topicsButton->setPosition(optionsButton->getPosition());\n                topicsButton->eventMouseButtonClick.clear();\n                topicsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &JournalWindowImpl::notifyOptions);\n\n                optionsButton->setVisible(false);\n                showActiveButton->setVisible(false);\n                showAllButton->setVisible(false);\n                questsButton->setVisible(false);\n\n                adjustButton(TopicsBTN);\n            }\n            else\n            {\n                optionsButton->setImage(\"textures/tx_menubook_options.dds\");\n                showActiveButton->setImage(\"textures/tx_menubook_quests_active.dds\");\n                showAllButton->setImage(\"textures/tx_menubook_quests_all.dds\");\n                questsButton->setImage(\"textures/tx_menubook_quests.dds\");\n\n                adjustButton(ShowAllBTN);\n                adjustButton(ShowActiveBTN);\n                adjustButton(OptionsBTN);\n                adjustButton(QuestsBTN);\n                adjustButton(TopicsBTN);\n                int topicsWidth = getWidget<MyGUI::Widget>(TopicsBTN)->getSize().width;\n                int cancelLeft = getWidget<MyGUI::Widget>(CancelBTN)->getPosition().left;\n                int cancelRight = getWidget<MyGUI::Widget>(CancelBTN)->getPosition().left + getWidget<MyGUI::Widget>(CancelBTN)->getSize().width;\n\n                getWidget<MyGUI::Widget>(QuestsBTN)->setPosition(cancelRight, getWidget<MyGUI::Widget>(QuestsBTN)->getPosition().top);\n\n                // Usually Topics, Quests, and Cancel buttons have the 64px width, so we can place the Topics left-up from the Cancel button, and the Quests right-up from the Cancel button.\n                // But in some installations, e.g. German one, the Topics button has the 128px width, so we should place it exactly left from the Quests button.\n                if (topicsWidth == 64)\n                {\n                    getWidget<MyGUI::Widget>(TopicsBTN)->setPosition(cancelLeft - topicsWidth, getWidget<MyGUI::Widget>(TopicsBTN)->getPosition().top);\n                }\n                else\n                {\n                    int questLeft = getWidget<MyGUI::Widget>(QuestsBTN)->getPosition().left;\n                    getWidget<MyGUI::Widget>(TopicsBTN)->setPosition(questLeft - topicsWidth, getWidget<MyGUI::Widget>(TopicsBTN)->getPosition().top);\n                }\n            }\n\n            mQuestMode = false;\n            mAllQuests = false;\n            mOptionsMode = false;\n            mTopicsMode = false;\n        }\n\n        void onOpen() override\n        {\n            if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ())\n            {\n                MWBase::Environment::get().getWindowManager()->popGuiMode ();\n            }\n            mModel->load ();\n\n            setBookMode ();\n\n            Book journalBook;\n            if (mModel->isEmpty ())\n                journalBook = createEmptyJournalBook ();\n            else\n                journalBook = createJournalBook ();\n\n            pushBook (journalBook, 0);\n\n            // fast forward to the last page\n            if (!mStates.empty ())\n            {\n                unsigned int  & page = mStates.top ().mPage;\n                page = mStates.top().mBook->pageCount()-1;\n                if (page%2)\n                    --page;\n            }\n            updateShowingPages();\n\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(getWidget<MyGUI::Widget>(CloseBTN));\n        }\n\n        void onClose() override\n        {\n            mModel->unload ();\n\n            getPage (LeftBookPage)->showPage (Book (), 0);\n            getPage (RightBookPage)->showPage (Book (), 0);\n\n            while (!mStates.empty ())\n                mStates.pop ();\n\n            mTopicIndexBook.reset ();\n        }\n\n        void setVisible (bool newValue) override\n        {\n            WindowBase::setVisible (newValue);\n        }\n\n        void setBookMode ()\n        {\n            mOptionsMode = false;\n            mTopicsMode = false;\n            setVisible (OptionsBTN, true);\n            setVisible (OptionsOverlay, false);\n\n            updateShowingPages ();\n            updateCloseJournalButton ();\n        }\n\n        void setOptionsMode ()\n        {\n            mOptionsMode = true;\n            mTopicsMode = false;\n\n            setVisible (OptionsBTN, false);\n            setVisible (OptionsOverlay, true);\n\n            setVisible (PrevPageBTN, false);\n            setVisible (NextPageBTN, false);\n            setVisible (CloseBTN, false);\n            setVisible (JournalBTN, false);\n\n            setVisible (TopicsList, false);\n            setVisible (QuestsList, mQuestMode);\n            setVisible (LeftTopicIndex, !mQuestMode);\n            setVisible (CenterTopicIndex, !mQuestMode);\n            setVisible (RightTopicIndex, !mQuestMode);\n            setVisible (ShowAllBTN, mQuestMode && !mAllQuests);\n            setVisible (ShowActiveBTN, mQuestMode && mAllQuests);\n\n            //TODO: figure out how to make \"options\" page overlay book page\n            //      correctly, so that text may show underneath\n            getPage (RightBookPage)->showPage (Book (), 0);\n\n            // If in quest mode, ensure the quest list is updated\n            if (mQuestMode)\n                notifyQuests(getWidget<MyGUI::Widget>(QuestsList));\n            else\n                notifyTopics(getWidget<MyGUI::Widget>(TopicsList));\n        }\n\n        void pushBook (Book book, unsigned int page)\n        {\n            DisplayState bs;\n            bs.mPage = page;\n            bs.mBook = book;\n            mStates.push (bs);\n            updateShowingPages ();\n            updateCloseJournalButton ();\n        }\n\n        void replaceBook (Book book, unsigned int page)\n        {\n            assert (!mStates.empty ());\n            mStates.top ().mBook = book;\n            mStates.top ().mPage = page;\n            updateShowingPages ();\n        }\n\n        void popBook ()\n        {\n            mStates.pop ();\n            updateShowingPages ();\n            updateCloseJournalButton ();\n        }\n\n        void updateCloseJournalButton ()\n        {\n            setVisible (CloseBTN, mStates.size () < 2);\n            setVisible (JournalBTN, mStates.size () >= 2);\n        }\n\n        void updateShowingPages ()\n        {\n            Book book;\n            unsigned int page;\n            unsigned int relPages;\n\n            if (!mStates.empty ())\n            {\n                book = mStates.top ().mBook;\n                page = mStates.top ().mPage;\n                relPages = book->pageCount () - page;\n            }\n            else\n            {\n                page = 0;\n                relPages = 0;\n            }\n\n            MyGUI::Widget* nextPageBtn = getWidget<MyGUI::Widget>(NextPageBTN);\n            MyGUI::Widget* prevPageBtn = getWidget<MyGUI::Widget>(PrevPageBTN);\n\n            MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n            bool nextPageVisible = relPages > 2;\n            nextPageBtn->setVisible(nextPageVisible);\n            bool prevPageVisible = page > 0;\n            prevPageBtn->setVisible(prevPageVisible);\n\n            if (focus == nextPageBtn && !nextPageVisible && prevPageVisible)\n                MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(prevPageBtn);\n            else if (focus == prevPageBtn && !prevPageVisible && nextPageVisible)\n                MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nextPageBtn);\n\n            setVisible (PageOneNum, relPages > 0);\n            setVisible (PageTwoNum, relPages > 1);\n\n            getPage (LeftBookPage)->showPage ((relPages > 0) ? book : Book (), page+0);\n            getPage (RightBookPage)->showPage ((relPages > 0) ? book : Book (), page+1);\n\n            setText (PageOneNum, page + 1);\n            setText (PageTwoNum, page + 2);\n        }\n\n        void notifyKeyPress(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character)\n        {\n            if (key == MyGUI::KeyCode::ArrowUp)\n                notifyPrevPage(sender);\n            else if (key == MyGUI::KeyCode::ArrowDown)\n                notifyNextPage(sender);\n        }\n\n        void notifyTopicClicked (intptr_t linkId)\n        {\n            Book topicBook = createTopicBook (linkId);\n\n            if (mStates.size () > 1)\n                replaceBook (topicBook, 0);\n            else\n                pushBook (topicBook, 0);\n\n            setVisible (OptionsOverlay, false);\n            setVisible (OptionsBTN, true);\n            setVisible (JournalBTN, true);\n\n            mOptionsMode = false;\n            mTopicsMode = false;\n\n            MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n        }\n\n        void notifyTopicSelected (const std::string& topic, int id)\n        {\n            const MWBase::Journal* journal = MWBase::Environment::get().getJournal();\n            intptr_t topicId = 0; /// \\todo get rid of intptr ids\n            for(MWBase::Journal::TTopicIter i = journal->topicBegin(); i != journal->topicEnd (); ++i)\n            {\n                if (Misc::StringUtils::ciEqual(i->first, topic))\n                    topicId = intptr_t (&i->second);\n            }\n\n            notifyTopicClicked(topicId);\n        }\n\n        void notifyQuestClicked (const std::string& name, int id)\n        {\n            Book book = createQuestBook (name);\n\n            if (mStates.size () > 1)\n                replaceBook (book, 0);\n            else\n                pushBook (book, 0);\n\n            setVisible (OptionsOverlay, false);\n            setVisible (OptionsBTN, true);\n            setVisible (JournalBTN, true);\n\n            mOptionsMode = false;\n\n            MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n        }\n\n        void notifyOptions(MyGUI::Widget* _sender)\n        {\n            setOptionsMode ();\n\n            if (!mTopicIndexBook)\n                mTopicIndexBook = createTopicIndexBook ();\n\n            if (mIndexPagesCount == 3)\n            {\n                getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0);\n                getPage (CenterTopicIndex)->showPage (mTopicIndexBook, 1);\n                getPage (RightTopicIndex)->showPage (mTopicIndexBook, 2);\n            }\n            else\n            {\n                getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0);\n                getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1);\n            }\n        }\n\n        void notifyJournal(MyGUI::Widget* _sender)\n        {\n            assert (mStates.size () > 1);\n            popBook ();\n\n            MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n        }\n\n        void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId index)\n        {\n            setVisible (LeftTopicIndex, false);\n            setVisible (CenterTopicIndex, false);\n            setVisible (RightTopicIndex, false);\n            setVisible (TopicsList, true);\n\n            mTopicsMode = true;\n\n            Gui::MWList* list = getWidget<Gui::MWList>(TopicsList);\n            list->clear();\n\n            AddNamesToList add(list);\n\n            mModel->visitTopicNamesStartingWith(index, add);\n\n            list->adjustSize();\n\n            MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n        }\n\n        void notifyTopics(MyGUI::Widget* _sender)\n        {\n            mQuestMode = false;\n            mTopicsMode = false;\n            setVisible (LeftTopicIndex, true);\n            setVisible (CenterTopicIndex, true);\n            setVisible (RightTopicIndex, true);\n            setVisible (TopicsList, false);\n            setVisible (QuestsList, false);\n            setVisible (ShowAllBTN, false);\n            setVisible (ShowActiveBTN, false);\n\n            MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n        }\n\n        struct AddNamesToList\n        {\n            AddNamesToList(Gui::MWList* list) : mList(list) {}\n\n            Gui::MWList* mList;\n            void operator () (const std::string& name, bool finished=false)\n            {\n                mList->addItem(name);\n            }\n        };\n        struct SetNamesInactive\n        {\n            SetNamesInactive(Gui::MWList* list) : mList(list) {}\n\n            Gui::MWList* mList;\n            void operator () (const std::string& name, bool finished)\n            {\n                if (finished)\n                {\n                    mList->getItemWidget(name)->setStateSelected(true);\n                }\n            }\n        };\n\n        void notifyQuests(MyGUI::Widget* _sender)\n        {\n            mQuestMode = true;\n\n            setVisible (LeftTopicIndex, false);\n            setVisible (CenterTopicIndex, false);\n            setVisible (RightTopicIndex, false);\n            setVisible (TopicsList, false);\n            setVisible (QuestsList, true);\n            setVisible (ShowAllBTN, !mAllQuests);\n            setVisible (ShowActiveBTN, mAllQuests);\n\n            Gui::MWList* list = getWidget<Gui::MWList>(QuestsList);\n            list->clear();\n\n            AddNamesToList add(list);\n\n            mModel->visitQuestNames(!mAllQuests, add);\n\n            list->adjustSize();\n\n            if (mAllQuests)\n            {\n                SetNamesInactive setInactive(list);\n                mModel->visitQuestNames(false, setInactive);\n            }\n\n            MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n        }\n\n        void notifyShowAll(MyGUI::Widget* _sender)\n        {\n            mAllQuests = true;\n            notifyQuests(_sender);\n        }\n\n        void notifyShowActive(MyGUI::Widget* _sender)\n        {\n            mAllQuests = false;\n            notifyQuests(_sender);\n        }\n\n        void notifyCancel(MyGUI::Widget* _sender)\n        {\n            if (mTopicsMode)\n            {\n                notifyTopics(_sender);\n            }\n            else\n            {\n                setBookMode();\n                MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n            }\n\n        }\n\n        void notifyClose(MyGUI::Widget* _sender)\n        {\n            MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n            winMgr->playSound(\"book close\");\n            winMgr->popGuiMode();\n        }\n\n        void notifyMouseWheel(MyGUI::Widget* sender, int rel)\n        {\n            if (rel < 0)\n                notifyNextPage(sender);\n            else\n                notifyPrevPage(sender);\n        }\n\n        void notifyNextPage(MyGUI::Widget* _sender)\n        {\n            if (mOptionsMode)\n                return;\n            if (!mStates.empty ())\n            {\n                unsigned int  & page = mStates.top ().mPage;\n                Book   book = mStates.top ().mBook;\n\n                if (page+2 < book->pageCount())\n                {\n                    MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n\n                    page += 2;\n                    updateShowingPages ();\n                }\n            }\n        }\n\n        void notifyPrevPage(MyGUI::Widget* _sender)\n        {\n            if (mOptionsMode)\n                return;\n            if (!mStates.empty ())\n            {\n                unsigned int & page = mStates.top ().mPage;\n\n                if(page >= 2)\n                {\n                    MWBase::Environment::get().getWindowManager()->playSound(\"book page\");\n\n                    page -= 2;\n                    updateShowingPages ();\n                }\n            }\n        }\n    };\n}\n\n// glue the implementation to the interface\nMWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model, bool questList, ToUTF8::FromType encoding)\n{\n    return new JournalWindowImpl (Model, questList, encoding);\n}\n\nMWGui::JournalWindow::JournalWindow()\n    : BookWindowBase(\"openmw_journal.layout\")\n{\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/journalwindow.hpp",
    "content": "#ifndef MWGUI_JOURNAL_H\n#define MWGUI_JOURNAL_H\n\n#include \"windowbase.hpp\"\n\n#include <components/to_utf8/to_utf8.hpp>\n\n#include <memory>\n\nnamespace MWBase { class WindowManager; }\n\nnamespace MWGui\n{\n    struct JournalViewModel;\n\n    struct JournalWindow : public BookWindowBase\n    {\n        JournalWindow();\n\n        /// construct a new instance of the one JournalWindow implementation\n        static JournalWindow * create (std::shared_ptr <JournalViewModel> Model, bool questList, ToUTF8::FromType encoding);\n\n        /// destroy this instance of the JournalWindow implementation\n        virtual ~JournalWindow () {}\n\n        /// show/hide the journal window\n        void setVisible (bool newValue) override = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/keyboardnavigation.cpp",
    "content": "#include \"keyboardnavigation.hpp\"\n\n#include <MyGUI_InputManager.h>\n#include <MyGUI_WidgetManager.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_Window.h>\n\n#include <components/debug/debuglog.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/GUIController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n\nnamespace MWGui\n{\n\nbool shouldAcceptKeyFocus(MyGUI::Widget* w)\n{\n    return w && !w->castType<MyGUI::Window>(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled();\n}\n\n/// Recursively get all child widgets that accept keyboard input\nvoid getKeyFocusWidgets(MyGUI::Widget* parent, std::vector<MyGUI::Widget*>& results)\n{\n    assert(parent != nullptr);\n\n    if (!parent->getVisible() || !parent->getEnabled())\n        return;\n\n    MyGUI::EnumeratorWidgetPtr enumerator = parent->getEnumerator();\n    while (enumerator.next())\n    {\n        MyGUI::Widget* w = enumerator.current();\n        if (!w->getVisible() || !w->getEnabled())\n            continue;\n        if (w->getNeedKeyFocus() && shouldAcceptKeyFocus(w))\n            results.push_back(w);\n        else\n            getKeyFocusWidgets(w, results);\n    }\n}\n\nKeyboardNavigation::KeyboardNavigation()\n    : mCurrentFocus(nullptr)\n    , mModalWindow(nullptr)\n    , mEnabled(true)\n{\n    MyGUI::WidgetManager::getInstance().registerUnlinker(this);\n}\n\nKeyboardNavigation::~KeyboardNavigation()\n{\n    try\n    {\n        MyGUI::WidgetManager::getInstance().unregisterUnlinker(this);\n    }\n    catch(const MyGUI::Exception& e)\n    {\n        Log(Debug::Error) << \"Error in the destructor: \" << e.what();\n    }\n}\n\nvoid KeyboardNavigation::saveFocus(int mode)\n{\n    MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n    if (shouldAcceptKeyFocus(focus))\n    {\n        mKeyFocus[mode] = focus;\n    }\n    else if(shouldAcceptKeyFocus(mCurrentFocus))\n    {\n        mKeyFocus[mode] = mCurrentFocus;\n    }\n}\n\nvoid KeyboardNavigation::restoreFocus(int mode)\n{\n    std::map<int, MyGUI::Widget*>::const_iterator found = mKeyFocus.find(mode);\n    if (found != mKeyFocus.end())\n    {\n        MyGUI::Widget* w = found->second;\n        if (w && w->getVisible() && w->getEnabled())\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(found->second);\n    }\n}\n\nvoid KeyboardNavigation::_unlinkWidget(MyGUI::Widget *widget)\n{\n    for (std::pair<const int, MyGUI::Widget*>& w : mKeyFocus)\n        if (w.second == widget)\n            w.second = nullptr;\n    if (widget == mCurrentFocus)\n        mCurrentFocus = nullptr;\n}\n\n#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)\nvoid styleFocusedButton(MyGUI::Widget* w)\n{\n    if (w)\n    {\n        if (MyGUI::Button* b = w->castType<MyGUI::Button>(false))\n        {\n            b->_setWidgetState(\"highlighted\");\n        }\n    }\n}\n#endif\n\nbool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root)\n{\n    while (widget && widget->getParent())\n        widget = widget->getParent();\n    return widget == root;\n}\n\nvoid KeyboardNavigation::onFrame()\n{\n    if (!mEnabled)\n        return;\n\n    /*\n        Start of tes3mp change (major)\n\n        Don't clear key focus widget when not in menus if the chat is currently focused\n    */\n    if (!MWBase::Environment::get().getWindowManager()->isGuiMode() && !mwmp::Main::get().getGUIController()->getChatEditState())\n    /*\n        End of tes3mp change (major)\n    */\n    {\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr);\n        return;\n    }\n\n    MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n\n    if (focus == mCurrentFocus)\n    {\n#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)\n        styleFocusedButton(mCurrentFocus);\n#endif\n        return;\n    }\n\n    // workaround incorrect key focus resets (fix in MyGUI TBD)\n    if (!shouldAcceptKeyFocus(focus) && shouldAcceptKeyFocus(mCurrentFocus) && (!mModalWindow || isRootParent(mCurrentFocus, mModalWindow)))\n    {\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCurrentFocus);\n        focus = mCurrentFocus;\n    }\n\n    if (focus != mCurrentFocus)\n    {\n#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)\n        if (mCurrentFocus)\n        {\n            if (MyGUI::Button* b = mCurrentFocus->castType<MyGUI::Button>(false))\n                b->_setWidgetState(\"normal\");\n        }\n#endif\n        mCurrentFocus = focus;\n    }\n\n#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)\n    styleFocusedButton(mCurrentFocus);\n#endif\n}\n\nvoid KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *defaultFocus)\n{\n    MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n    if (!focus || !shouldAcceptKeyFocus(focus))\n    {\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(defaultFocus);\n    }\n    else\n    {\n        if (!isRootParent(focus, window))\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(defaultFocus);\n    }\n}\n\nvoid KeyboardNavigation::setModalWindow(MyGUI::Widget *window)\n{\n    mModalWindow = window;\n}\n\nvoid KeyboardNavigation::setEnabled(bool enabled)\n{\n    mEnabled = enabled;\n}\n\nenum Direction\n{\n    D_Left,\n    D_Up,\n    D_Right,\n    D_Down,\n    D_Next,\n    D_Prev\n};\n\nbool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat)\n{\n    if (!mEnabled)\n        return false;\n\n    switch (key.getValue())\n    {\n    case MyGUI::KeyCode::ArrowLeft:\n        return switchFocus(D_Left, false);\n    case MyGUI::KeyCode::ArrowRight:\n        return switchFocus(D_Right, false);\n    case MyGUI::KeyCode::ArrowUp:\n        return switchFocus(D_Up, false);\n    case MyGUI::KeyCode::ArrowDown:\n        return switchFocus(D_Down, false);\n    case MyGUI::KeyCode::Tab:\n        return switchFocus(MyGUI::InputManager::getInstance().isShiftPressed() ? D_Prev : D_Next, true);\n    case MyGUI::KeyCode::Return:\n    case MyGUI::KeyCode::NumpadEnter:\n    case MyGUI::KeyCode::Space:\n    {\n        // We should disable repeating for activation keys\n        MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::None);\n        if (repeat)\n            return true;\n\n        return accept();\n    }\n    default:\n        return false;\n    }\n}\n\nbool KeyboardNavigation::switchFocus(int direction, bool wrap)\n{\n    if (!MWBase::Environment::get().getWindowManager()->isGuiMode())\n        return false;\n\n    MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n\n    bool isCycle = (direction == D_Prev || direction == D_Next);\n\n    if ((focus && focus->getTypeName().find(\"Button\") == std::string::npos) && !isCycle)\n        return false;\n\n    if (focus && isCycle && focus->getUserString(\"AcceptTab\") == \"true\")\n        return false;\n\n    if ((!focus || !focus->getNeedKeyFocus()) && isCycle)\n    {\n        // if nothing is selected, select the first widget\n        return selectFirstWidget();\n    }\n    if (!focus)\n        return false;\n\n    MyGUI::Widget* window = focus;\n    while (window && window->getParent())\n        window = window->getParent();\n    MyGUI::VectorWidgetPtr keyFocusList;\n    getKeyFocusWidgets(window, keyFocusList);\n\n    if (keyFocusList.empty())\n        return false;\n\n    MyGUI::VectorWidgetPtr::iterator found = std::find(keyFocusList.begin(), keyFocusList.end(), focus);\n    if (found == keyFocusList.end())\n    {\n        if (isCycle)\n            return selectFirstWidget();\n        else\n            return false;\n    }\n\n    bool forward = (direction == D_Next || direction == D_Right || direction == D_Down);\n\n    int index = found - keyFocusList.begin();\n    index = forward ? (index+1) : (index-1);\n    if (wrap)\n        index = (index + keyFocusList.size())%keyFocusList.size();\n    else\n        index = std::min(std::max(0, index), static_cast<int>(keyFocusList.size())-1);\n\n    MyGUI::Widget* next = keyFocusList[index];\n    int vertdiff = next->getTop() - focus->getTop();\n    int horizdiff = next->getLeft() - focus->getLeft();\n    bool isVertical = std::abs(vertdiff) > std::abs(horizdiff);\n    if (direction == D_Right && (horizdiff <= 0 || isVertical))\n        return false;\n    else if (direction == D_Left && (horizdiff >= 0 || isVertical))\n        return false;\n    else if (direction == D_Down && (vertdiff <= 0 || !isVertical))\n        return false;\n    else if (direction == D_Up && (vertdiff >= 0 || !isVertical))\n        return false;\n\n    MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[index]);\n    return true;\n}\n\nbool KeyboardNavigation::selectFirstWidget()\n{\n    MyGUI::VectorWidgetPtr keyFocusList;\n    MyGUI::EnumeratorWidgetPtr enumerator = MyGUI::Gui::getInstance().getEnumerator();\n    if (mModalWindow)\n        enumerator = mModalWindow->getEnumerator();\n    while (enumerator.next())\n        getKeyFocusWidgets(enumerator.current(), keyFocusList);\n\n    if (!keyFocusList.empty())\n    {\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[0]);\n        return true;\n    }\n    return false;\n}\n\nbool KeyboardNavigation::accept()\n{\n    MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n    if (!focus)\n        return false;\n    //MyGUI::Button* button = focus->castType<MyGUI::Button>(false);\n    //if (button && button->getEnabled())\n    if (focus->getTypeName().find(\"Button\") != std::string::npos && focus->getEnabled())\n    {\n        focus->eventMouseButtonClick(focus);\n        return true;\n    }\n    return false;\n}\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/keyboardnavigation.hpp",
    "content": "#ifndef OPENMW_MWGUI_KEYBOARDNAVIGATION_H\n#define OPENMW_MWGUI_KEYBOARDNAVIGATION_H\n\n#include <MyGUI_KeyCode.h>\n#include <MyGUI_IUnlinkWidget.h>\n\nnamespace MWGui\n{\n\n    class KeyboardNavigation : public MyGUI::IUnlinkWidget\n    {\n    public:\n        KeyboardNavigation();\n        ~KeyboardNavigation();\n\n        /// @return Was the key handled by this class?\n        bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat);\n\n        void saveFocus(int mode);\n        void restoreFocus(int mode);\n\n        void _unlinkWidget(MyGUI::Widget* widget) override;\n\n        void onFrame();\n\n        /// Set a key focus widget for this window, if one isn't already set.\n        void setDefaultFocus(MyGUI::Widget* window, MyGUI::Widget* defaultFocus);\n\n        void setModalWindow(MyGUI::Widget* window);\n\n        void setEnabled(bool enabled);\n\n    private:\n        bool switchFocus(int direction, bool wrap);\n\n        bool selectFirstWidget();\n\n        /// Send button press event to focused button\n        bool accept();\n\n        std::map<int, MyGUI::Widget*> mKeyFocus;\n\n        MyGUI::Widget* mCurrentFocus;\n        MyGUI::Widget* mModalWindow;\n\n        bool mEnabled;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/layout.cpp",
    "content": "#include \"layout.hpp\"\n\n#include <MyGUI_LayoutManager.h>\n#include <MyGUI_Widget.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_TextBox.h>\n#include <MyGUI_Window.h>\n\nnamespace MWGui\n{\n    void Layout::initialise(const std::string& _layout, MyGUI::Widget* _parent)\n    {\n        const std::string MAIN_WINDOW = \"_Main\";\n        mLayoutName = _layout;\n\n        if (mLayoutName.empty())\n            mMainWidget = _parent;\n        else\n        {\n            mPrefix = MyGUI::utility::toString(this, \"_\");\n            mListWindowRoot = MyGUI::LayoutManager::getInstance().loadLayout(mLayoutName, mPrefix, _parent);\n\n            const std::string main_name = mPrefix + MAIN_WINDOW;\n            for (MyGUI::Widget* widget : mListWindowRoot)\n            {\n                if (widget->getName() == main_name)\n                {\n                    mMainWidget = widget;\n                    break;\n                }\n            }\n            MYGUI_ASSERT(mMainWidget, \"root widget name '\" << MAIN_WINDOW << \"' in layout '\" << mLayoutName << \"' not found.\");\n        }\n    }\n\n    void Layout::shutdown()\n    {\n        setVisible(false);\n        MyGUI::Gui::getInstance().destroyWidget(mMainWidget);\n        mListWindowRoot.clear();\n    }\n\n    void Layout::setCoord(int x, int y, int w, int h)\n    {\n        mMainWidget->setCoord(x,y,w,h);\n    }\n\n    void Layout::setVisible(bool b)\n    {\n        mMainWidget->setVisible(b);\n    }\n\n    void Layout::setText(const std::string &name, const std::string &caption)\n    {\n        MyGUI::Widget* pt;\n        getWidget(pt, name);\n        static_cast<MyGUI::TextBox*>(pt)->setCaption(caption);\n    }\n\n    void Layout::setTitle(const std::string& title)\n    {\n        MyGUI::Window* window = static_cast<MyGUI::Window*>(mMainWidget);\n\n        if (window->getCaption() != title)\n            window->setCaptionWithReplacing(title);\n    }\n\n    MyGUI::Widget* Layout::getWidget(const std::string &_name)\n    {\n        for (MyGUI::Widget* widget : mListWindowRoot)\n        {\n            MyGUI::Widget* find = widget->findWidget(mPrefix + _name);\n            if (nullptr != find)\n            {\n                return find;\n            }\n        }\n        MYGUI_EXCEPT(\"widget name '\" << _name << \"' in layout '\" << mLayoutName << \"' not found.\");\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/layout.hpp",
    "content": "#ifndef OPENMW_MWGUI_LAYOUT_H\n#define OPENMW_MWGUI_LAYOUT_H\n\n#include <string>\n#include <MyGUI_WidgetDefines.h>\n#include <MyGUI_Widget.h>\n\n#include <components/debug/debuglog.hpp>\n\nnamespace MWGui\n{\n  /** The Layout class is an utility class used to load MyGUI layouts\n      from xml files, and to manipulate member widgets.\n   */\n  class Layout\n  {\n  public:\n    Layout(const std::string & _layout, MyGUI::Widget* _parent = nullptr)\n      : mMainWidget(nullptr)\n    { initialise(_layout, _parent); }\n    virtual ~Layout()\n    {\n        try\n        {\n            shutdown();\n        }\n        catch(const MyGUI::Exception& e)\n        {\n            Log(Debug::Error) << \"Error in the destructor: \" << e.what();\n        }\n    }\n\n    MyGUI::Widget* getWidget(const std::string& _name);\n\n    template <typename T>\n    void getWidget(T * & _widget, const std::string & _name)\n    {\n        MyGUI::Widget* w = getWidget(_name);\n        T* cast = w->castType<T>(false);\n        if (!cast)\n        {\n            MYGUI_EXCEPT(\"Error cast : dest type = '\" << T::getClassTypeName()\n                         << \"' source name = '\" << w->getName()\n                         << \"' source type = '\" << w->getTypeName() << \"' in layout '\" << mLayoutName << \"'\");\n        }\n        else\n            _widget = cast;\n    }\n\n  private:\n    void initialise(const std::string & _layout,\n                    MyGUI::Widget* _parent = nullptr);\n\n    void shutdown();\n\n  public:\n    void setCoord(int x, int y, int w, int h);\n\n    virtual void setVisible(bool b);\n\n    void setText(const std::string& name, const std::string& caption);\n\n    // NOTE: this assume that mMainWidget is of type Window.\n    void setTitle(const std::string& title);\n\n    MyGUI::Widget* mMainWidget;\n\n  protected:\n\n    std::string mPrefix;\n    std::string mLayoutName;\n    MyGUI::VectorWidgetPtr mListWindowRoot;\n  };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/levelupdialog.cpp",
    "content": "#include \"levelupdialog.hpp\"\n\n#include <MyGUI_Button.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_EditBox.h>\n\n#include <components/fallback/fallback.hpp>\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"class.hpp\"\n\nnamespace MWGui\n{\n    const unsigned int LevelupDialog::sMaxCoins = 3;\n    LevelupDialog::LevelupDialog()\n        : WindowBase(\"openmw_levelup_dialog.layout\"),\n          mCoinCount(sMaxCoins)\n    {\n        getWidget(mOkButton, \"OkButton\");\n        getWidget(mClassImage, \"ClassImage\");\n        getWidget(mLevelText, \"LevelText\");\n        getWidget(mLevelDescription, \"LevelDescription\");\n        getWidget(mCoinBox, \"Coins\");\n        getWidget(mAssignWidget, \"AssignWidget\");\n\n        mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onOkButtonClicked);\n\n        for (int i=1; i<9; ++i)\n        {\n            MyGUI::TextBox* t;\n            getWidget(t, \"AttribVal\" + MyGUI::utility::toString(i));\n            mAttributeValues.push_back(t);\n\n            MyGUI::Button* b;\n            getWidget(b, \"Attrib\" + MyGUI::utility::toString(i));\n            b->setUserData (i-1);\n            b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked);\n            mAttributes.push_back(b);\n\n            getWidget(t, \"AttribMultiplier\" + MyGUI::utility::toString(i));\n            mAttributeMultipliers.push_back(t);\n        }\n\n        for (unsigned int i = 0; i < mCoinCount; ++i)\n        {\n            MyGUI::ImageBox* image = mCoinBox->createWidget<MyGUI::ImageBox>(\"ImageBox\", MyGUI::IntCoord(0,0,16,16), MyGUI::Align::Default);\n            image->setImageTexture (\"icons\\\\tx_goldicon.dds\");\n            mCoins.push_back(image);\n        }\n\n        center();\n    }\n\n    void LevelupDialog::setAttributeValues()\n    {\n        MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player);\n        MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);\n\n        for (int i = 0; i < 8; ++i)\n        {\n            int val = creatureStats.getAttribute(i).getBase();\n            if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end())\n            {\n                val += pcStats.getLevelupAttributeMultiplier(i);\n            }\n\n            if (val >= 100)\n                val = 100;\n\n            mAttributeValues[i]->setCaption(MyGUI::utility::toString(val));\n        }\n    }\n\n\n    void LevelupDialog::resetCoins()\n    {\n        const int coinSpacing = 33;\n        int curX = mCoinBox->getWidth()/2 - (coinSpacing*(mCoinCount - 1) + 16*mCoinCount)/2;\n        for (unsigned int i=0; i<sMaxCoins; ++i)\n        {\n            MyGUI::ImageBox* image = mCoins[i];\n            image->detachFromWidget();\n            image->attachToWidget(mCoinBox);\n            if (i < mCoinCount)\n            {\n                mCoins[i]->setVisible(true);\n                image->setCoord(MyGUI::IntCoord(curX,0,16,16));\n                curX += 16+coinSpacing;\n            }\n            else\n                mCoins[i]->setVisible(false);\n        }\n    }\n\n    void LevelupDialog::assignCoins()\n    {\n        resetCoins();\n        for (unsigned int i=0; i<mSpentAttributes.size(); ++i)\n        {\n            MyGUI::ImageBox* image = mCoins[i];\n            image->detachFromWidget();\n            image->attachToWidget(mAssignWidget);\n\n            int attribute = mSpentAttributes[i];\n\n            int xdiff = mAttributeMultipliers[attribute]->getCaption() == \"\" ? 0 : 20;\n\n            MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mAssignWidget->getAbsolutePosition() - MyGUI::IntPoint(22+xdiff,0);\n            pos.top += (mAttributes[attribute]->getHeight() - image->getHeight())/2;\n            image->setPosition(pos);\n        }\n\n        setAttributeValues();\n    }\n\n    void LevelupDialog::onOpen()\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        MWWorld::Ptr player = world->getPlayerPtr();\n        MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player);\n        MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);\n\n        setClassImage(mClassImage, getLevelupClassImage(pcStats.getSkillIncreasesForSpecialization(0),\n                                                        pcStats.getSkillIncreasesForSpecialization(1),\n                                                        pcStats.getSkillIncreasesForSpecialization(2)));\n\n        int level = creatureStats.getLevel ()+1;\n        mLevelText->setCaptionWithReplacing(\"#{sLevelUpMenu1} \" + MyGUI::utility::toString(level));\n\n        std::string levelupdescription;\n        levelupdescription = Fallback::Map::getString(\"Level_Up_Level\"+MyGUI::utility::toString(level));\n\n        if (levelupdescription == \"\")\n            levelupdescription = Fallback::Map::getString(\"Level_Up_Default\");\n\n        mLevelDescription->setCaption (levelupdescription);\n\n        unsigned int availableAttributes = 0;\n        for (int i = 0; i < 8; ++i)\n        {\n            MyGUI::TextBox* text = mAttributeMultipliers[i];\n            if (pcStats.getAttribute(i).getBase() < 100)\n            {\n                mAttributes[i]->setEnabled(true);\n                mAttributeValues[i]->setEnabled(true);\n                availableAttributes++;\n\n                float mult = pcStats.getLevelupAttributeMultiplier (i);\n                mult = std::min(mult, 100-pcStats.getAttribute(i).getBase());\n                text->setCaption(mult <= 1 ? \"\" : \"x\" + MyGUI::utility::toString(mult));\n            }\n            else\n            {\n                mAttributes[i]->setEnabled(false);\n                mAttributeValues[i]->setEnabled(false);\n\n                text->setCaption(\"\");\n            }\n        }\n\n        mCoinCount = std::min(sMaxCoins, availableAttributes);\n\n        mSpentAttributes.clear();\n        resetCoins();\n\n        setAttributeValues();\n\n        center();\n\n        // Play LevelUp Music\n        MWBase::Environment::get().getSoundManager()->streamMusic(\"Special/MW_Triumph.mp3\");\n    }\n\n    void LevelupDialog::onOkButtonClicked(MyGUI::Widget* sender)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);\n\n        if (mSpentAttributes.size() < mCoinCount)\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sNotifyMessage36}\");\n        else\n        {\n            // increase attributes\n            for (unsigned int i = 0; i < mCoinCount; ++i)\n            {\n                MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]);\n                attribute.setBase(attribute.getBase() + pcStats.getLevelupAttributeMultiplier(mSpentAttributes[i]));\n\n                if (attribute.getBase() >= 100)\n                    attribute.setBase(100);\n                pcStats.setAttribute(mSpentAttributes[i], attribute);\n            }\n\n            pcStats.levelUp();\n\n            MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Levelup);\n        }\n\n    }\n\n    void LevelupDialog::onAttributeClicked(MyGUI::Widget *sender)\n    {\n        int attribute = *sender->getUserData<int>();\n\n        std::vector<int>::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute);\n        if (found != mSpentAttributes.end())\n            mSpentAttributes.erase(found);\n        else\n        {\n            if (mSpentAttributes.size() == mCoinCount)\n                mSpentAttributes[mCoinCount - 1] = attribute;\n            else\n                mSpentAttributes.push_back(attribute);\n        }\n        assignCoins();\n    }\n\n    std::string LevelupDialog::getLevelupClassImage(const int combatIncreases, const int magicIncreases, const int stealthIncreases)\n    {\n        std::string ret = \"acrobat\";\n\n        int total = combatIncreases + magicIncreases + stealthIncreases;\n        if (total == 0)\n            return ret;\n\n        int combatFraction = static_cast<int>(static_cast<float>(combatIncreases) / total * 10.f);\n        int magicFraction = static_cast<int>(static_cast<float>(magicIncreases) / total * 10.f);\n        int stealthFraction = static_cast<int>(static_cast<float>(stealthIncreases) / total * 10.f);\n\n        if (combatFraction > 7)\n            ret = \"warrior\";\n        else if (magicFraction > 7)\n            ret = \"mage\";\n        else if (stealthFraction > 7)\n            ret = \"thief\";\n\n        switch (combatFraction)\n        {\n            case 7:\n                ret = \"warrior\";\n                break;\n            case 6:\n                if (stealthFraction == 1)\n                    ret = \"barbarian\";\n                else if (stealthFraction == 3)\n                    ret = \"crusader\";\n                else\n                    ret = \"knight\";\n                break;\n            case 5:\n                if (stealthFraction == 3)\n                    ret = \"scout\";\n                else\n                    ret = \"archer\";\n                break;\n            case 4:\n                ret = \"rogue\";\n                break;\n            default:\n                break;\n        }\n\n        switch (magicFraction)\n        {\n            case 7:\n                ret = \"mage\";\n                break;\n            case 6:\n                if (combatFraction == 2)\n                    ret = \"sorcerer\";\n                else if (combatIncreases == 3)\n                    ret = \"healer\";\n                else\n                    ret = \"battlemage\";\n                break;\n            case 5:\n                ret = \"witchhunter\";\n                break;\n            case 4:\n                ret = \"spellsword\";\n                // In vanilla there's also code for \"nightblade\", however it seems to be unreachable.\n                break;\n            default:\n                break;\n        }\n\n        switch (stealthFraction)\n        {\n            case 7:\n                ret = \"thief\";\n                break;\n            case 6:\n                if (magicFraction == 1)\n                    ret = \"agent\";\n                else if (magicIncreases == 3)\n                    ret = \"assassin\";\n                else\n                    ret = \"acrobat\";\n                break;\n            case 5:\n                if (magicIncreases == 3)\n                    ret = \"monk\";\n                else\n                    ret = \"pilgrim\";\n                break;\n            case 3:\n                if (magicFraction == 3)\n                    ret = \"bard\";\n                break;\n            default:\n                break;\n        }\n\n        return ret;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/levelupdialog.hpp",
    "content": "#ifndef MWGUI_LEVELUPDIALOG_H\n#define MWGUI_LEVELUPDIALOG_H\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n\n    class LevelupDialog : public WindowBase\n    {\n    public:\n        LevelupDialog();\n\n        void onOpen() override;\n\n    private:\n        MyGUI::Button* mOkButton;\n        MyGUI::ImageBox* mClassImage;\n        MyGUI::TextBox* mLevelText;\n        MyGUI::EditBox* mLevelDescription;\n\n        MyGUI::Widget* mCoinBox;\n        MyGUI::Widget* mAssignWidget;\n\n        std::vector<MyGUI::Button*> mAttributes;\n        std::vector<MyGUI::TextBox*> mAttributeValues;\n        std::vector<MyGUI::TextBox*> mAttributeMultipliers;\n        std::vector<MyGUI::ImageBox*> mCoins;\n\n        std::vector<int> mSpentAttributes;\n\n        unsigned int mCoinCount;\n        static const unsigned int sMaxCoins;\n\n        void onOkButtonClicked(MyGUI::Widget* sender);\n        void onAttributeClicked(MyGUI::Widget* sender);\n\n        void assignCoins();\n        void resetCoins();\n\n        void setAttributeValues();\n\n        std::string getLevelupClassImage(const int combatIncreases, const int magicIncreases, const int stealthIncreases);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/loadingscreen.cpp",
    "content": "#include \"loadingscreen.hpp\"\n\n#include <array>\n#include <condition_variable>\n\n#include <osgViewer/Viewer>\n\n#include <osg/Texture2D>\n#include <osg/Version>\n\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_ScrollBar.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_TextBox.h>\n\n#include <components/misc/rng.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/myguiplatform/myguitexture.hpp>\n#include <components/settings/settings.hpp>\n#include <components/vfs/manager.hpp>\n#include <components/resource/resourcesystem.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n\n#include \"backgroundimage.hpp\"\n\nnamespace MWGui\n{\n\n    LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer)\n        : WindowBase(\"openmw_loading_screen.layout\")\n        , mResourceSystem(resourceSystem)\n        , mViewer(viewer)\n        , mTargetFrameRate(120.0)\n        , mLastWallpaperChangeTime(0.0)\n        , mLastRenderTime(0.0)\n        , mLoadingOnTime(0.0)\n        , mImportantLabel(false)\n        , mVisible(false)\n        , mNestedLoadingCount(0)\n        , mProgress(0)\n        , mShowWallpaper(true)\n    {\n        mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());\n\n        getWidget(mLoadingText, \"LoadingText\");\n        getWidget(mProgressBar, \"ProgressBar\");\n        getWidget(mLoadingBox, \"LoadingBox\");\n\n        mProgressBar->setScrollViewPage(1);\n\n        mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>(\"ImageBox\", 0,0,1,1,\n            MyGUI::Align::Stretch, \"Menu\");\n        mSceneImage = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>(\"ImageBox\", 0,0,1,1,\n            MyGUI::Align::Stretch, \"Scene\");\n\n        findSplashScreens();\n    }\n\n    LoadingScreen::~LoadingScreen()\n    {\n    }\n\n    void LoadingScreen::findSplashScreens()\n    {\n        const std::map<std::string, VFS::File*>& index = mResourceSystem->getVFS()->getIndex();\n        std::string pattern = \"Splash/\";\n        mResourceSystem->getVFS()->normalizeFilename(pattern);\n\n        /* priority given to the left */\n        const std::array<std::string, 7> supported_extensions {{\".tga\", \".dds\", \".ktx\", \".png\", \".bmp\", \".jpeg\", \".jpg\"}};\n\n        auto found = index.lower_bound(pattern);\n        while (found != index.end())\n        {\n            const std::string& name = found->first;\n            if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern)\n            {\n                size_t pos = name.find_last_of('.');\n                if (pos != std::string::npos)\n                {\n                    for(auto const& extension: supported_extensions)\n                    {\n                        if (name.compare(pos, name.size() - pos, extension) == 0)\n                        {\n                            mSplashScreens.push_back(found->first);\n                            break;  /* based on priority */\n                        }\n                    }\n                }\n            }\n            else\n                break;\n            ++found;\n        }\n        if (mSplashScreens.empty())\n            Log(Debug::Warning) << \"Warning: no splash screens found!\";\n    }\n\n    void LoadingScreen::setLabel(const std::string &label, bool important)\n    {\n        mImportantLabel = important;\n\n        mLoadingText->setCaptionWithReplacing(label);\n        int padding = mLoadingBox->getWidth() - mLoadingText->getWidth();\n        MyGUI::IntSize size(mLoadingText->getTextSize().width+padding, mLoadingBox->getHeight());\n        size.width = std::max(300, size.width);\n        mLoadingBox->setSize(size);\n\n        if (MWBase::Environment::get().getWindowManager()->getMessagesCount() > 0)\n            mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mMainWidget->getHeight()/2 - mLoadingBox->getHeight()/2);\n        else\n            mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mMainWidget->getHeight() - mLoadingBox->getHeight() - 8);\n    }\n\n    void LoadingScreen::setVisible(bool visible)\n    {\n        WindowBase::setVisible(visible);\n        mBackgroundImage->setVisible(visible);\n        mSceneImage->setVisible(visible);\n    }\n\n    double LoadingScreen::getTargetFrameRate() const\n    {\n        double frameRateLimit = MWBase::Environment::get().getFrameRateLimit();\n        if (frameRateLimit > 0)\n            return std::min(frameRateLimit, mTargetFrameRate);\n        else\n            return mTargetFrameRate;\n    }\n\n    class CopyFramebufferToTextureCallback : public osg::Camera::DrawCallback\n    {\n    public:\n        CopyFramebufferToTextureCallback(osg::Texture2D* texture)\n            : mOneshot(true)\n            , mTexture(texture)\n        {\n        }\n\n        void operator () (osg::RenderInfo& renderInfo) const override\n        {\n            int w = renderInfo.getCurrentCamera()->getViewport()->width();\n            int h = renderInfo.getCurrentCamera()->getViewport()->height();\n            mTexture->copyTexImage2D(*renderInfo.getState(), 0, 0, w, h);\n\n            mOneshot = false;\n        }\n\n        void reset()\n        {\n            mOneshot = true;\n        }\n\n    private:\n        mutable bool mOneshot;\n        osg::ref_ptr<osg::Texture2D> mTexture;\n    };\n\n    class DontComputeBoundCallback : public osg::Node::ComputeBoundingSphereCallback\n    {\n    public:\n        osg::BoundingSphere computeBound(const osg::Node&) const override { return osg::BoundingSphere(); }\n    };\n\n    void LoadingScreen::loadingOn(bool visible)\n    {\n        // Early-out if already on\n        if (mNestedLoadingCount++ > 0 && mMainWidget->getVisible())\n            return;\n\n        mLoadingOnTime = mTimer.time_m();\n\n        // Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading\n        // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()\n        mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);\n\n        if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) {\n            mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame();\n            mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame();\n        }\n\n        mVisible = visible;\n        mLoadingBox->setVisible(mVisible);\n        setVisible(true);\n\n        if (!mVisible)\n        {\n            mShowWallpaper = false;\n            draw();\n            return;\n        }\n\n        mShowWallpaper = MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame;\n\n        if (mShowWallpaper)\n        {\n            changeWallpaper();\n        }\n\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading);\n    }\n\n    void LoadingScreen::loadingOff()\n    {\n        if (--mNestedLoadingCount > 0)\n            return;\n        mLoadingBox->setVisible(true);   // restore\n\n        if (mLastRenderTime < mLoadingOnTime)\n        {\n            // the loading was so fast that we didn't show loading screen at all\n            // we may still want to show the label if the caller requested it\n            if (mImportantLabel)\n            {\n                MWBase::Environment::get().getWindowManager()->messageBox(mLoadingText->getCaption());\n                mImportantLabel = false;\n            }\n        }\n        else\n            mImportantLabel = false; // label was already shown on loading screen\n\n        mViewer->getSceneData()->setComputeBoundingSphereCallback(nullptr);\n        mViewer->getSceneData()->dirtyBound();\n\n        //std::cout << \"loading took \" << mTimer.time_m() - mLoadingOnTime << std::endl;\n        setVisible(false);\n\n        if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation())\n        {\n            ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(mOldIcoMin);\n            ico->setMaximumNumOfObjectsToCompilePerFrame(mOldIcoMax);\n        }\n\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading);\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper);\n    }\n\n    void LoadingScreen::changeWallpaper ()\n    {\n        if (!mSplashScreens.empty())\n        {\n            std::string const & randomSplash = mSplashScreens.at(Misc::Rng::rollDice(mSplashScreens.size()));\n\n            // TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3\n            // we can't do this by default, because the Morrowind splash screens are 1024x1024, but should be displayed as 4:3\n            bool stretch = Settings::Manager::getBool(\"stretch menu background\", \"GUI\");\n            mBackgroundImage->setVisible(true);\n            mBackgroundImage->setBackgroundImage(randomSplash, true, stretch);\n        }\n        mSceneImage->setBackgroundImage(\"\");\n        mSceneImage->setVisible(false);\n    }\n\n    void LoadingScreen::setProgressRange (size_t range)\n    {\n        mProgressBar->setScrollRange(range+1);\n        mProgressBar->setScrollPosition(0);\n        mProgressBar->setTrackSize(0);\n        mProgress = 0;\n    }\n\n    void LoadingScreen::setProgress (size_t value)\n    {\n        // skip expensive update if there isn't enough visible progress\n        if (mProgressBar->getWidth() <= 0 || value - mProgress < mProgressBar->getScrollRange()/mProgressBar->getWidth())\n            return;\n        value = std::min(value, mProgressBar->getScrollRange()-1);\n        mProgress = value;\n        mProgressBar->setScrollPosition(0);\n        mProgressBar->setTrackSize(static_cast<int>(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize()));\n        draw();\n    }\n\n    void LoadingScreen::increaseProgress (size_t increase)\n    {\n        mProgressBar->setScrollPosition(0);\n        size_t value = mProgress + increase;\n        value = std::min(value, mProgressBar->getScrollRange()-1);\n        mProgress = value;\n        mProgressBar->setTrackSize(static_cast<int>(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize()));\n        draw();\n    }\n\n    bool LoadingScreen::needToDrawLoadingScreen()\n    {\n        if ( mTimer.time_m() <= mLastRenderTime + (1.0/getTargetFrameRate()) * 1000.0)\n            return false;\n\n        // the minimal delay before a loading screen shows\n        const float initialDelay = 0.05;\n\n        bool alreadyShown = (mLastRenderTime > mLoadingOnTime);\n        float diff = (mTimer.time_m() - mLoadingOnTime);\n\n        if (!alreadyShown)\n        {\n            // bump the delay by the current progress - i.e. if during the initial delay the loading\n            // has almost finished, no point showing the loading screen now\n            diff -= mProgress / static_cast<float>(mProgressBar->getScrollRange()) * 100.f;\n        }\n\n        if (!mShowWallpaper && diff < initialDelay*1000)\n            return false;\n        return true;\n    }\n\n    void LoadingScreen::setupCopyFramebufferToTextureCallback()\n    {\n        // Copy the current framebuffer onto a texture and display that texture as the background image\n        // Note, we could also set the camera to disable clearing and have the background image transparent,\n        // but then we get shaking effects on buffer swaps.\n\n        if (!mTexture)\n        {\n            mTexture = new osg::Texture2D;\n            mTexture->setInternalFormat(GL_RGB);\n            mTexture->setResizeNonPowerOfTwoHint(false);\n        }\n\n        if (!mGuiTexture.get())\n        {\n            mGuiTexture.reset(new osgMyGUI::OSGTexture(mTexture));\n        }\n\n        if (!mCopyFramebufferToTextureCallback)\n        {\n            mCopyFramebufferToTextureCallback = new CopyFramebufferToTextureCallback(mTexture);\n        }\n\n#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10)\n        mViewer->getCamera()->removeInitialDrawCallback(mCopyFramebufferToTextureCallback);\n        mViewer->getCamera()->addInitialDrawCallback(mCopyFramebufferToTextureCallback);\n#else\n        mViewer->getCamera()->setInitialDrawCallback(mCopyFramebufferToTextureCallback);\n#endif\n        mCopyFramebufferToTextureCallback->reset();\n\n        mBackgroundImage->setBackgroundImage(\"\");\n        mBackgroundImage->setVisible(false);\n\n        mSceneImage->setRenderItemTexture(mGuiTexture.get());\n        mSceneImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));\n        mSceneImage->setVisible(true);\n    }\n\n    void LoadingScreen::draw()\n    {\n        if (mVisible && !needToDrawLoadingScreen())\n            return;\n\n        if (mShowWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000*1)\n        {\n            mLastWallpaperChangeTime = mTimer.time_m();\n            changeWallpaper();\n        }\n\n        if (!mShowWallpaper && mLastRenderTime < mLoadingOnTime)\n        {\n            setupCopyFramebufferToTextureCallback();\n        }\n\n        MWBase::Environment::get().getInputManager()->update(0, true, true);\n\n        mResourceSystem->reportStats(mViewer->getFrameStamp()->getFrameNumber(), mViewer->getViewerStats());\n        if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation())\n        {\n            ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1.f/getTargetFrameRate());\n            ico->setMaximumNumOfObjectsToCompilePerFrame(1000);\n        }\n\n        // at the time this function is called we are in the middle of a frame,\n        // so out of order calls are necessary to get a correct frameNumber for the next frame.\n        // refer to the advance() and frame() order in Engine::go()\n        mViewer->eventTraversal();\n        mViewer->updateTraversal();\n        mViewer->renderingTraversals();\n        mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());\n\n        mLastRenderTime = mTimer.time_m();\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/loadingscreen.hpp",
    "content": "#ifndef MWGUI_LOADINGSCREEN_H\n#define MWGUI_LOADINGSCREEN_H\n\n#include <memory>\n\n#include <osg/Timer>\n#include <osg/ref_ptr>\n\n#include \"windowbase.hpp\"\n\n#include <components/loadinglistener/loadinglistener.hpp>\n\nnamespace osgViewer\n{\n    class Viewer;\n}\n\nnamespace osg\n{\n    class Texture2D;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWGui\n{\n    class BackgroundImage;\n    class CopyFramebufferToTextureCallback;\n\n    class LoadingScreen : public WindowBase, public Loading::Listener\n    {\n    public:\n        LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer);\n        virtual ~LoadingScreen();\n\n        /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details\n        void setLabel (const std::string& label, bool important) override;\n        void loadingOn(bool visible=true) override;\n        void loadingOff() override;\n        void setProgressRange (size_t range) override;\n        void setProgress (size_t value) override;\n        void increaseProgress (size_t increase=1) override;\n\n        void setVisible(bool visible) override;\n\n        double getTargetFrameRate() const;\n\n    private:\n        void findSplashScreens();\n        bool needToDrawLoadingScreen();\n\n        void setupCopyFramebufferToTextureCallback();\n\n        Resource::ResourceSystem* mResourceSystem;\n        osg::ref_ptr<osgViewer::Viewer> mViewer;\n\n        double mTargetFrameRate;\n\n        double mLastWallpaperChangeTime;\n        double mLastRenderTime;\n        osg::Timer mTimer;\n        double mLoadingOnTime;\n\n        bool mImportantLabel;\n\n        bool mVisible;\n        int mNestedLoadingCount;\n\n        size_t mProgress;\n\n        bool mShowWallpaper;\n        float mOldIcoMin = 0.f;\n        unsigned int mOldIcoMax = 0;\n\n        MyGUI::Widget* mLoadingBox;\n\n        MyGUI::TextBox* mLoadingText;\n        MyGUI::ScrollBar* mProgressBar;\n        BackgroundImage* mBackgroundImage;\n        BackgroundImage* mSceneImage;\n\n        std::vector<std::string> mSplashScreens;\n\n        osg::ref_ptr<osg::Texture2D> mTexture;\n        osg::ref_ptr<CopyFramebufferToTextureCallback> mCopyFramebufferToTextureCallback;\n        std::unique_ptr<MyGUI::ITexture> mGuiTexture;\n\n        void changeWallpaper();\n\n        void draw();\n    };\n\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/mainmenu.cpp",
    "content": "#include \"mainmenu.hpp\"\n\n#include <MyGUI_TextBox.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_RenderManager.h>\n\n#include <components/widgets/imagebutton.hpp>\n#include <components/settings/settings.hpp>\n#include <components/vfs/manager.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n\n#include \"savegamedialog.hpp\"\n#include \"confirmationdialog.hpp\"\n#include \"backgroundimage.hpp\"\n#include \"videowidget.hpp\"\n\nnamespace MWGui\n{\n\n    MainMenu::MainMenu(int w, int h, const VFS::Manager* vfs, const std::string& versionDescription)\n        : WindowBase(\"openmw_mainmenu.layout\")\n        , mWidth (w), mHeight (h)\n        , mVFS(vfs), mButtonBox(nullptr)\n        , mBackground(nullptr)\n        , mVideoBackground(nullptr)\n        , mVideo(nullptr)\n        , mSaveGameDialog(nullptr)\n    {\n        getWidget(mVersionText, \"VersionText\");\n        mVersionText->setCaption(versionDescription);\n\n        mHasAnimatedMenu = mVFS->exists(\"video/menu_background.bik\");\n\n        updateMenu();\n    }\n\n    MainMenu::~MainMenu()\n    {\n        delete mSaveGameDialog;\n    }\n\n    void MainMenu::onResChange(int w, int h)\n    {\n        mWidth = w;\n        mHeight = h;\n\n        updateMenu();\n    }\n\n    void MainMenu::setVisible (bool visible)\n    {\n        if (visible)\n            updateMenu();\n\n        bool isMainMenu =\n                MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) &&\n                MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame;\n\n        showBackground(isMainMenu);\n\n        if (visible)\n        {\n            if (isMainMenu)\n            {\n                if (mButtons[\"loadgame\"]->getVisible())\n                    MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons[\"loadgame\"]);\n                else\n                    MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons[\"newgame\"]);\n            }\n            else\n                MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons[\"return\"]);\n        }\n\n        Layout::setVisible (visible);\n    }\n\n    void MainMenu::onNewGameConfirmed()\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu);\n        MWBase::Environment::get().getStateManager()->newGame();\n    }\n\n    void MainMenu::onExitConfirmed()\n    {\n        MWBase::Environment::get().getStateManager()->requestQuit();\n    }\n\n    void MainMenu::onButtonClicked(MyGUI::Widget *sender)\n    {\n        MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n\n        std::string name = *sender->getUserData<std::string>();\n        winMgr->playSound(\"Menu Click\");\n        if (name == \"return\")\n        {\n            winMgr->removeGuiMode (GM_MainMenu);\n        }\n        else if (name == \"options\")\n            winMgr->pushGuiMode (GM_Settings);\n        else if (name == \"credits\")\n            winMgr->playVideo(\"mw_credits.bik\", true);\n        else if (name == \"exitgame\")\n        {\n            if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)\n                onExitConfirmed();\n            else\n            {\n                ConfirmationDialog* dialog = winMgr->getConfirmationDialog();\n                dialog->askForConfirmation(\"#{sMessage2}\");\n                dialog->eventOkClicked.clear();\n                dialog->eventOkClicked += MyGUI::newDelegate(this, &MainMenu::onExitConfirmed);\n                dialog->eventCancelClicked.clear();\n            }\n        }\n        else if (name == \"newgame\")\n        {\n            if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)\n                onNewGameConfirmed();\n            else\n            {\n                ConfirmationDialog* dialog = winMgr->getConfirmationDialog();\n                dialog->askForConfirmation(\"#{sNotifyMessage54}\");\n                dialog->eventOkClicked.clear();\n                dialog->eventOkClicked += MyGUI::newDelegate(this, &MainMenu::onNewGameConfirmed);\n                dialog->eventCancelClicked.clear();\n            }\n        }\n\n        else\n        {\n            if (!mSaveGameDialog)\n                mSaveGameDialog = new SaveGameDialog();\n            if (name == \"loadgame\")\n                mSaveGameDialog->setLoadOrSave(true);\n            else if (name == \"savegame\")\n                mSaveGameDialog->setLoadOrSave(false);\n            mSaveGameDialog->setVisible(true);\n        }\n    }\n\n    void MainMenu::showBackground(bool show)\n    {\n        if (mVideo && !show)\n        {\n            MyGUI::Gui::getInstance().destroyWidget(mVideoBackground);\n            mVideoBackground = nullptr;\n            mVideo = nullptr;\n        }\n        if (mBackground && !show)\n        {\n            MyGUI::Gui::getInstance().destroyWidget(mBackground);\n            mBackground = nullptr;\n        }\n\n        if (!show)\n            return;\n\n        bool stretch = Settings::Manager::getBool(\"stretch menu background\", \"GUI\");\n\n        if (mHasAnimatedMenu)\n        {\n            if (!mVideo)\n            {\n                // Use black background to correct aspect ratio\n                mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal<MyGUI::ImageBox>(\"ImageBox\", 0,0,1,1,\n                    MyGUI::Align::Default, \"Menu\");\n                mVideoBackground->setImageTexture(\"black\");\n\n                mVideo = mVideoBackground->createWidget<VideoWidget>(\"ImageBox\", 0,0,1,1,\n                    MyGUI::Align::Stretch, \"Menu\");\n                mVideo->setVFS(mVFS);\n\n                mVideo->playVideo(\"video\\\\menu_background.bik\");\n            }\n\n            MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n            int screenWidth = viewSize.width;\n            int screenHeight = viewSize.height;\n            mVideoBackground->setSize(screenWidth, screenHeight);\n\n            mVideo->autoResize(stretch);\n\n            mVideo->setVisible(true);\n        }\n        else\n        {\n            if (!mBackground)\n            {\n                mBackground = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>(\"ImageBox\", 0,0,1,1,\n                    MyGUI::Align::Stretch, \"Menu\");\n                mBackground->setBackgroundImage(\"textures\\\\menu_morrowind.dds\", true, stretch);\n            }\n            mBackground->setVisible(true);\n        }\n    }\n\n    void MainMenu::onFrame(float dt)\n    {\n        if (mVideo)\n        {\n            if (!mVideo->update())\n            {\n                // If finished playing, start again\n                mVideo->playVideo(\"video\\\\menu_background.bik\");\n            }\n        }\n    }\n\n    bool MainMenu::exit()\n    {\n        return MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running;\n    }\n\n    void MainMenu::updateMenu()\n    {\n        setCoord(0,0, mWidth, mHeight);\n\n        if (!mButtonBox)\n            mButtonBox = mMainWidget->createWidget<MyGUI::Widget>(\"\", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);\n\n        int curH = 0;\n\n        MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState();\n\n        mVersionText->setVisible(state == MWBase::StateManager::State_NoGame);\n\n        std::vector<std::string> buttons;\n\n        if (state==MWBase::StateManager::State_Running)\n            buttons.emplace_back(\"return\");\n\n        /*\n            Start of tes3mp change (major)\n\n            In multiplayer, the main menu should not have options for starting or loading the game,\n            so they have been removed\n\n            Saving the game should still be possible, as long as it's clear that the resulting\n            save is singleplayer-only; this will prevent players from completely losing their\n            characters and houses on servers if those servers ever go down\n        */\n\n        //buttons.emplace_back(\"newgame\");\n\n        if (state==MWBase::StateManager::State_Running &&\n            MWBase::Environment::get().getWorld()->getGlobalInt (\"chargenstate\")==-1 &&\n                MWBase::Environment::get().getWindowManager()->isSavingAllowed())\n            buttons.emplace_back(\"savegame\");\n\n        /*\n        if (MWBase::Environment::get().getStateManager()->characterBegin()!=\n            MWBase::Environment::get().getStateManager()->characterEnd())\n            buttons.emplace_back(\"loadgame\");\n        */\n\n        /*\n            End of tes3mp change (major)\n        */\n\n        buttons.emplace_back(\"options\");\n\n        if (state==MWBase::StateManager::State_NoGame)\n            buttons.emplace_back(\"credits\");\n\n        buttons.emplace_back(\"exitgame\");\n\n        // Create new buttons if needed\n        std::vector<std::string> allButtons { \"return\", \"newgame\", \"savegame\", \"loadgame\", \"options\", \"credits\", \"exitgame\"};\n        for (std::string& buttonId : allButtons)\n        {\n            if (mButtons.find(buttonId) == mButtons.end())\n            {\n                Gui::ImageButton* button = mButtonBox->createWidget<Gui::ImageButton>\n                        (\"ImageBox\", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default);\n                button->setProperty(\"ImageHighlighted\", \"textures\\\\menu_\" + buttonId + \"_over.dds\");\n                button->setProperty(\"ImageNormal\", \"textures\\\\menu_\" + buttonId + \".dds\");\n                button->setProperty(\"ImagePushed\", \"textures\\\\menu_\" + buttonId + \"_pressed.dds\");\n                button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked);\n                button->setUserData(std::string(buttonId));\n                mButtons[buttonId] = button;\n            }\n        }\n\n        // Start by hiding all buttons\n        int maxwidth = 0;\n        for (auto& buttonPair : mButtons)\n        {\n            buttonPair.second->setVisible(false);\n            MyGUI::IntSize requested = buttonPair.second->getRequestedSize();\n            if (requested.width > maxwidth)\n                maxwidth = requested.width;\n        }\n\n        // Now show and position the ones we want\n        for (std::string& buttonId : buttons)\n        {\n            assert(mButtons.find(buttonId) != mButtons.end());\n            Gui::ImageButton* button = mButtons[buttonId];\n            button->setVisible(true);\n\n            // By default, assume that all menu buttons textures should have 64 height.\n            // If they have a different resolution, scale them.\n            MyGUI::IntSize requested = button->getRequestedSize();\n            float scale = requested.height / 64.f;\n\n            button->setImageCoord(MyGUI::IntCoord(0, 0, requested.width, requested.height));\n            // Trim off some of the excessive padding\n            // TODO: perhaps do this within ImageButton?\n            int height = requested.height;\n            button->setImageTile(MyGUI::IntSize(requested.width, requested.height-16*scale));\n            button->setCoord((maxwidth-requested.width/scale) / 2, curH, requested.width/scale, height/scale-16);\n            curH += height/scale-16;\n        }\n\n        if (state == MWBase::StateManager::State_NoGame)\n        {\n            // Align with the background image\n            int bottomPadding=24;\n            mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight - curH - bottomPadding, maxwidth, curH);\n        }\n        else\n            mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH);\n\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/mainmenu.hpp",
    "content": "#ifndef OPENMW_GAME_MWGUI_MAINMENU_H\n#define OPENMW_GAME_MWGUI_MAINMENU_H\n\n#include \"windowbase.hpp\"\n\nnamespace Gui\n{\n    class ImageButton;\n}\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace MWGui\n{\n\n    class BackgroundImage;\n    class SaveGameDialog;\n    class VideoWidget;\n\n    class MainMenu : public WindowBase\n    {\n            int mWidth;\n            int mHeight;\n\n            bool mHasAnimatedMenu;\n\n        public:\n\n            MainMenu(int w, int h, const VFS::Manager* vfs, const std::string& versionDescription);\n            ~MainMenu();\n\n            void onResChange(int w, int h) override;\n\n            void setVisible (bool visible) override;\n\n            void onFrame(float dt) override;\n\n            bool exit() override;\n\n        private:\n            const VFS::Manager* mVFS;\n\n            MyGUI::Widget* mButtonBox;\n            MyGUI::TextBox* mVersionText;\n\n            BackgroundImage* mBackground;\n\n            MyGUI::ImageBox* mVideoBackground;\n            VideoWidget* mVideo; // For animated main menus\n\n            std::map<std::string, Gui::ImageButton*> mButtons;\n\n            void onButtonClicked (MyGUI::Widget* sender);\n            void onNewGameConfirmed();\n            void onExitConfirmed();\n\n            void showBackground(bool show);\n\n            void updateMenu();\n\n            SaveGameDialog* mSaveGameDialog;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/mapwindow.cpp",
    "content": "#include \"mapwindow.hpp\"\n\n#include <osg/Texture2D>\n\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_LanguageManager.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_RotatingSkin.h>\n#include <MyGUI_FactoryManager.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/GUIController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/globalmap.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/settings/settings.hpp>\n#include <components/myguiplatform/myguitexture.hpp>\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwrender/globalmap.hpp\"\n#include \"../mwrender/localmap.hpp\"\n\n#include \"confirmationdialog.hpp\"\n#include \"tooltips.hpp\"\n\nnamespace\n{\n\n    const int cellSize = Constants::CellSizeInUnits;\n\n    enum LocalMapWidgetDepth\n    {\n        Local_MarkerAboveFogLayer = 0,\n        Local_CompassLayer = 1,\n        Local_FogLayer = 2,\n        Local_MarkerLayer = 3,\n        Local_MapLayer = 4\n    };\n\n    enum GlobalMapWidgetDepth\n    {\n        Global_CompassLayer = 0,\n        Global_MarkerLayer = 1,\n        Global_ExploreOverlayLayer = 2,\n        Global_MapLayer = 3\n    };\n\n\n    /// @brief A widget that changes its color when hovered.\n    class MarkerWidget final : public MyGUI::Widget\n    {\n        MYGUI_RTTI_DERIVED(MarkerWidget)\n\n    public:\n        void setNormalColour(const MyGUI::Colour& colour)\n        {\n            mNormalColour = colour;\n            setColour(colour);\n        }\n\n        void setHoverColour(const MyGUI::Colour& colour)\n        {\n            mHoverColour = colour;\n        }\n\n    private:\n        MyGUI::Colour mNormalColour;\n        MyGUI::Colour mHoverColour;\n\n        void onMouseLostFocus(MyGUI::Widget* _new) override\n        {\n            setColour(mNormalColour);\n        }\n\n        void onMouseSetFocus(MyGUI::Widget* _old) override\n        {\n            setColour(mHoverColour);\n        }\n    };\n}\n\nnamespace MWGui\n{\n\n    void CustomMarkerCollection::addMarker(const ESM::CustomMarker &marker, bool triggerEvent)\n    {\n        mMarkers.insert(std::make_pair(marker.mCell, marker));\n        if (triggerEvent)\n            eventMarkersChanged();\n    }\n\n    void CustomMarkerCollection::deleteMarker(const ESM::CustomMarker &marker)\n    {\n        std::pair<ContainerType::iterator, ContainerType::iterator> range = mMarkers.equal_range(marker.mCell);\n\n        for (ContainerType::iterator it = range.first; it != range.second; ++it)\n        {\n            if (it->second == marker)\n            {\n                mMarkers.erase(it);\n                eventMarkersChanged();\n                return;\n            }\n        }\n        throw std::runtime_error(\"can't find marker to delete\");\n    }\n\n    void CustomMarkerCollection::updateMarker(const ESM::CustomMarker &marker, const std::string &newNote)\n    {\n        std::pair<ContainerType::iterator, ContainerType::iterator> range = mMarkers.equal_range(marker.mCell);\n\n        for (ContainerType::iterator it = range.first; it != range.second; ++it)\n        {\n            if (it->second == marker)\n            {\n                it->second.mNote = newNote;\n                eventMarkersChanged();\n                return;\n            }\n        }\n        throw std::runtime_error(\"can't find marker to update\");\n    }\n\n    void CustomMarkerCollection::clear()\n    {\n        mMarkers.clear();\n        eventMarkersChanged();\n    }\n\n    CustomMarkerCollection::ContainerType::const_iterator CustomMarkerCollection::begin() const\n    {\n        return mMarkers.begin();\n    }\n\n    CustomMarkerCollection::ContainerType::const_iterator CustomMarkerCollection::end() const\n    {\n        return mMarkers.end();\n    }\n\n    CustomMarkerCollection::RangeType CustomMarkerCollection::getMarkers(const ESM::CellId &cellId) const\n    {\n        return mMarkers.equal_range(cellId);\n    }\n\n    size_t CustomMarkerCollection::size() const\n    {\n        return mMarkers.size();\n    }\n\n    // ------------------------------------------------------\n\n    LocalMapBase::LocalMapBase(CustomMarkerCollection &markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled)\n        : mLocalMapRender(localMapRender)\n        , mCurX(0)\n        , mCurY(0)\n        , mInterior(false)\n        , mLocalMap(nullptr)\n        , mCompass(nullptr)\n        , mChanged(true)\n        , mFogOfWarToggled(true)\n        , mFogOfWarEnabled(fogOfWarEnabled)\n        , mMapWidgetSize(0)\n        , mNumCells(0)\n        , mCellDistance(0)\n        , mCustomMarkers(markers)\n        , mMarkerUpdateTimer(0.0f)\n        , mLastDirectionX(0.0f)\n        , mLastDirectionY(0.0f)\n        , mNeedDoorMarkersUpdate(false)\n    {\n        mCustomMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers);\n        \n        /*\n            Start of tes3mp addition\n\n            Add a MyGUI delegate for updating player markers\n        */\n        mwmp::Main::get().getGUIController()->mPlayerMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updatePlayerMarkers);\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    LocalMapBase::~LocalMapBase()\n    {\n        mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers);\n\n        /*\n            Start of tes3mp addition\n\n            Remove a MyGUI delegate for updating player markers\n        */\n        mwmp::Main::get().getGUIController()->mPlayerMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updatePlayerMarkers);\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass)\n    {\n        mLocalMap = widget;\n        mCompass = compass;\n        mMapWidgetSize = std::max(1, Settings::Manager::getInt(\"local map widget size\", \"Map\"));\n        mCellDistance = Constants::CellGridRadius;\n        mNumCells = mCellDistance * 2 + 1;\n\n        mLocalMap->setCanvasSize(mMapWidgetSize*mNumCells, mMapWidgetSize*mNumCells);\n\n        mCompass->setDepth(Local_CompassLayer);\n        mCompass->setNeedMouseFocus(false);\n\n        for (int mx=0; mx<mNumCells; ++mx)\n        {\n            for (int my=0; my<mNumCells; ++my)\n            {\n                MyGUI::ImageBox* map = mLocalMap->createWidget<MyGUI::ImageBox>(\"ImageBox\",\n                    MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize),\n                    MyGUI::Align::Top | MyGUI::Align::Left);\n                map->setDepth(Local_MapLayer);\n\n                MyGUI::ImageBox* fog = mLocalMap->createWidget<MyGUI::ImageBox>(\"ImageBox\",\n                    MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize),\n                    MyGUI::Align::Top | MyGUI::Align::Left);\n                fog->setDepth(Local_FogLayer);\n                fog->setColour(MyGUI::Colour(0, 0, 0));\n\n                map->setNeedMouseFocus(false);\n                fog->setNeedMouseFocus(false);\n\n                mMaps.emplace_back(map, fog);\n            }\n        }\n    }\n\n    void LocalMapBase::setCellPrefix(const std::string& prefix)\n    {\n        mPrefix = prefix;\n        mChanged = true;\n    }\n\n    bool LocalMapBase::toggleFogOfWar()\n    {\n        mFogOfWarToggled = !mFogOfWarToggled;\n        applyFogOfWar();\n        return mFogOfWarToggled;\n    }\n\n    void LocalMapBase::applyFogOfWar()\n    {\n        for (int mx=0; mx<mNumCells; ++mx)\n        {\n            for (int my=0; my<mNumCells; ++my)\n            {\n                MapEntry& entry = mMaps[my + mNumCells*mx];\n                MyGUI::ImageBox* fog = entry.mFogWidget;\n\n                if (!mFogOfWarToggled || !mFogOfWarEnabled)\n                {\n                    fog->setImageTexture(\"\");\n                    entry.mFogTexture.reset();\n                    continue;\n                }\n            }\n        }\n\n        redraw();\n    }\n\n    MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos)\n    {\n        MyGUI::IntPoint widgetPos;\n        // normalized cell coordinates\n        float nX,nY;\n\n        if (!mInterior)\n        {\n            int cellX, cellY;\n            MWBase::Environment::get().getWorld()->positionToIndex(worldX, worldY, cellX, cellY);\n            nX = (worldX - cellSize * cellX) / cellSize;\n            // Image space is -Y up, cells are Y up\n            nY = 1 - (worldY - cellSize * cellY) / cellSize;\n\n            float cellDx = static_cast<float>(cellX - mCurX);\n            float cellDy = static_cast<float>(cellY - mCurY);\n\n            markerPos.cellX = cellX;\n            markerPos.cellY = cellY;\n\n            widgetPos = MyGUI::IntPoint(static_cast<int>(nX * mMapWidgetSize + (mCellDistance + cellDx) * mMapWidgetSize),\n                                        static_cast<int>(nY * mMapWidgetSize + (mCellDistance - cellDy) * mMapWidgetSize));\n        }\n        else\n        {\n            int cellX, cellY;\n            osg::Vec2f worldPos (worldX, worldY);\n            mLocalMapRender->worldToInteriorMapPosition(worldPos, nX, nY, cellX, cellY);\n\n            markerPos.cellX = cellX;\n            markerPos.cellY = cellY;\n\n            // Image space is -Y up, cells are Y up\n            widgetPos = MyGUI::IntPoint(static_cast<int>(nX * mMapWidgetSize + (mCellDistance + (cellX - mCurX)) * mMapWidgetSize),\n                                        static_cast<int>(nY * mMapWidgetSize + (mCellDistance - (cellY - mCurY)) * mMapWidgetSize));\n        }\n\n        markerPos.nX = nX;\n        markerPos.nY = nY;\n        return widgetPos;\n    }\n\n    void LocalMapBase::updateCustomMarkers()\n    {\n        for (MyGUI::Widget* widget : mCustomMarkerWidgets)\n            MyGUI::Gui::getInstance().destroyWidget(widget);\n        mCustomMarkerWidgets.clear();\n\n        for (int dX = -mCellDistance; dX <= mCellDistance; ++dX)\n        {\n            for (int dY =-mCellDistance; dY <= mCellDistance; ++dY)\n            {\n                ESM::CellId cellId;\n                cellId.mPaged = !mInterior;\n                cellId.mWorldspace = (mInterior ? mPrefix : ESM::CellId::sDefaultWorldspace);\n                cellId.mIndex.mX = mCurX+dX;\n                cellId.mIndex.mY = mCurY+dY;\n\n                CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellId);\n                for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; ++it)\n                {\n                    const ESM::CustomMarker& marker = it->second;\n\n                    MarkerUserData markerPos (mLocalMapRender);\n                    MyGUI::IntPoint widgetPos = getMarkerPosition(marker.mWorldX, marker.mWorldY, markerPos);\n\n                    MyGUI::IntCoord widgetCoord(widgetPos.left - 8,\n                                                widgetPos.top - 8,\n                                                16, 16);\n                    MarkerWidget* markerWidget = mLocalMap->createWidget<MarkerWidget>(\"CustomMarkerButton\",\n                        widgetCoord, MyGUI::Align::Default);\n                    markerWidget->setDepth(Local_MarkerAboveFogLayer);\n                    markerWidget->setUserString(\"ToolTipType\", \"Layout\");\n                    markerWidget->setUserString(\"ToolTipLayout\", \"TextToolTipOneLine\");\n                    markerWidget->setUserString(\"Caption_TextOneLine\", MyGUI::TextIterator::toTagsString(marker.mNote));\n                    markerWidget->setNormalColour(MyGUI::Colour(0.6f, 0.6f, 0.6f));\n                    markerWidget->setHoverColour(MyGUI::Colour(1.0f, 1.0f, 1.0f));\n                    markerWidget->setUserData(marker);\n                    markerWidget->setNeedMouseFocus(true);\n                    customMarkerCreated(markerWidget);\n                    mCustomMarkerWidgets.push_back(markerWidget);\n                }\n            }\n        }\n\n        redraw();\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Send the LocalMapBase to our GUIController when updating player markers\n    */\n    void LocalMapBase::updatePlayerMarkers()\n    {\n        mwmp::Main::get().getGUIController()->updatePlayersMarkers(this);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Send the MapWindow to our GUIController when updating player markers\n    */\n    void MapWindow::updatePlayerMarkers()\n    {\n        LocalMapBase::updatePlayerMarkers();\n\n        mwmp::Main::get().getGUIController()->updateGlobalMapMarkerTooltips(this);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void LocalMapBase::setActiveCell(const int x, const int y, bool interior)\n    {\n        if (x==mCurX && y==mCurY && mInterior==interior && !mChanged)\n            return; // don't do anything if we're still in the same cell\n\n        mCurX = x;\n        mCurY = y;\n        mInterior = interior;\n        mChanged = false;\n\n        for (int mx=0; mx<mNumCells; ++mx)\n        {\n            for (int my=0; my<mNumCells; ++my)\n            {\n                MapEntry& entry = mMaps[my + mNumCells*mx];\n                entry.mMapWidget->setRenderItemTexture(nullptr);\n                entry.mFogWidget->setRenderItemTexture(nullptr);\n                entry.mMapTexture.reset();\n                entry.mFogTexture.reset();\n\n                entry.mCellX = x + (mx - mCellDistance);\n                entry.mCellY = y - (my - mCellDistance);\n            }\n        }\n\n        // Delay the door markers update until scripts have been given a chance to run.\n        // If we don't do this, door markers that should be disabled will still appear on the map.\n        mNeedDoorMarkersUpdate = true;\n\n        updateMagicMarkers();\n        updateCustomMarkers();\n    }\n\n    void LocalMapBase::requestMapRender(const MWWorld::CellStore *cell)\n    {\n        mLocalMapRender->requestMap(cell);\n    }\n\n    void LocalMapBase::redraw()\n    {\n        // Redraw children in proper order\n        mLocalMap->getParent()->_updateChilds();\n    }\n\n    void LocalMapBase::setPlayerPos(int cellX, int cellY, const float nx, const float ny)\n    {\n        MyGUI::IntPoint pos(static_cast<int>(mMapWidgetSize * mCellDistance + nx*mMapWidgetSize - 16), static_cast<int>(mMapWidgetSize * mCellDistance + ny*mMapWidgetSize - 16));\n        pos.left += (cellX - mCurX) * mMapWidgetSize;\n        pos.top -= (cellY - mCurY) * mMapWidgetSize;\n\n        if (pos != mCompass->getPosition())\n        {\n            notifyPlayerUpdate ();\n\n            mCompass->setPosition(pos);\n            MyGUI::IntPoint middle (pos.left+16, pos.top+16);\n                    MyGUI::IntCoord viewsize = mLocalMap->getCoord();\n            MyGUI::IntPoint viewOffset((viewsize.width / 2) - middle.left, (viewsize.height / 2) - middle.top);\n            mLocalMap->setViewOffset(viewOffset);\n        }\n    }\n\n    void LocalMapBase::setPlayerDir(const float x, const float y)\n    {\n        if (x == mLastDirectionX && y == mLastDirectionY)\n            return;\n\n        notifyPlayerUpdate ();\n\n        MyGUI::ISubWidget* main = mCompass->getSubWidgetMain();\n        MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();\n        rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));\n        float angle = std::atan2(x,y);\n        rotatingSubskin->setAngle(angle);\n\n        mLastDirectionX = x;\n        mLastDirectionY = y;\n    }\n\n    void LocalMapBase::addDetectionMarkers(int type)\n    {\n        std::vector<MWWorld::Ptr> markers;\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        world->listDetectedReferences(\n                    world->getPlayerPtr(),\n                    markers, MWBase::World::DetectionType(type));\n        if (markers.empty())\n            return;\n\n        std::string markerTexture;\n        if (type == MWBase::World::Detect_Creature)\n        {\n            markerTexture = \"textures\\\\detect_animal_icon.dds\";\n        }\n        if (type == MWBase::World::Detect_Key)\n        {\n            markerTexture = \"textures\\\\detect_key_icon.dds\";\n        }\n        if (type == MWBase::World::Detect_Enchantment)\n        {\n            markerTexture = \"textures\\\\detect_enchantment_icon.dds\";\n        }\n\n        int counter = 0;\n        for (const MWWorld::Ptr& ptr : markers)\n        {\n            const ESM::Position& worldPos = ptr.getRefData().getPosition();\n            MarkerUserData markerPos (mLocalMapRender);\n            MyGUI::IntPoint widgetPos = getMarkerPosition(worldPos.pos[0], worldPos.pos[1], markerPos);\n            MyGUI::IntCoord widgetCoord(widgetPos.left - 4,\n                                        widgetPos.top - 4,\n                                        8, 8);\n            ++counter;\n            MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>(\"ImageBox\",\n                widgetCoord, MyGUI::Align::Default);\n            markerWidget->setDepth(Local_MarkerAboveFogLayer);\n            markerWidget->setImageTexture(markerTexture);\n            markerWidget->setImageCoord(MyGUI::IntCoord(0,0,8,8));\n            markerWidget->setNeedMouseFocus(false);\n            mMagicMarkerWidgets.push_back(markerWidget);\n        }\n    }\n\n    void LocalMapBase::onFrame(float dt)\n    {\n        if (mNeedDoorMarkersUpdate)\n        {\n            updateDoorMarkers();\n            mNeedDoorMarkersUpdate = false;\n        }\n\n        mMarkerUpdateTimer += dt;\n\n        if (mMarkerUpdateTimer >= 0.25)\n        {\n            mMarkerUpdateTimer = 0;\n            updateMagicMarkers();\n        }\n\n        updateRequiredMaps();\n    }\n\n    bool widgetCropped(MyGUI::Widget* widget, MyGUI::Widget* cropTo)\n    {\n        MyGUI::IntRect coord = widget->getAbsoluteRect();\n        MyGUI::IntRect croppedCoord = cropTo->getAbsoluteRect();\n        if (coord.left < croppedCoord.left && coord.right < croppedCoord.left)\n            return true;\n        if (coord.left > croppedCoord.right && coord.right > croppedCoord.right)\n            return true;\n        if (coord.top < croppedCoord.top && coord.bottom < croppedCoord.top)\n            return true;\n        if (coord.top > croppedCoord.bottom && coord.bottom > croppedCoord.bottom)\n            return true;\n        return false;\n    }\n\n    void LocalMapBase::updateRequiredMaps()\n    {\n        bool needRedraw = false;\n        for (MapEntry& entry : mMaps)\n        {\n            if (widgetCropped(entry.mMapWidget, mLocalMap))\n                continue;\n\n            if (!entry.mMapTexture)\n            {\n                if (!mInterior)\n                    requestMapRender(MWBase::Environment::get().getWorld()->getExterior (entry.mCellX, entry.mCellY));\n\n                osg::ref_ptr<osg::Texture2D> texture = mLocalMapRender->getMapTexture(entry.mCellX, entry.mCellY);\n                if (texture)\n                {\n                    entry.mMapTexture.reset(new osgMyGUI::OSGTexture(texture));\n                    entry.mMapWidget->setRenderItemTexture(entry.mMapTexture.get());\n                    entry.mMapWidget->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));\n                    needRedraw = true;\n                }\n                else\n                    entry.mMapTexture.reset(new osgMyGUI::OSGTexture(\"\", nullptr));\n            }\n            if (!entry.mFogTexture && mFogOfWarToggled && mFogOfWarEnabled)\n            {\n                osg::ref_ptr<osg::Texture2D> tex = mLocalMapRender->getFogOfWarTexture(entry.mCellX, entry.mCellY);\n                if (tex)\n                {\n                    entry.mFogTexture.reset(new osgMyGUI::OSGTexture(tex));\n                    entry.mFogWidget->setRenderItemTexture(entry.mFogTexture.get());\n                    entry.mFogWidget->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));\n                }\n                else\n                {\n                    entry.mFogWidget->setImageTexture(\"black\");\n                    entry.mFogTexture.reset(new osgMyGUI::OSGTexture(\"\", nullptr));\n                }\n                needRedraw = true;\n            }\n        }\n        if (needRedraw)\n            redraw();\n    }\n\n    void LocalMapBase::updateDoorMarkers()\n    {\n        // clear all previous door markers\n        for (MyGUI::Widget* widget : mDoorMarkerWidgets)\n            MyGUI::Gui::getInstance().destroyWidget(widget);\n        mDoorMarkerWidgets.clear();\n\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n\n        // Retrieve the door markers we want to show\n        std::vector<MWBase::World::DoorMarker> doors;\n        if (mInterior)\n        {\n            MWWorld::CellStore* cell = world->getInterior (mPrefix);\n            world->getDoorMarkers(cell, doors);\n        }\n        else\n        {\n            for (int dX=-mCellDistance; dX<=mCellDistance; ++dX)\n            {\n                for (int dY=-mCellDistance; dY<=mCellDistance; ++dY)\n                {\n                    MWWorld::CellStore* cell = world->getExterior (mCurX+dX, mCurY+dY);\n                    world->getDoorMarkers(cell, doors);\n                }\n            }\n        }\n\n        // Create a widget for each marker\n        int counter = 0;\n        for (MWBase::World::DoorMarker& marker : doors)\n        {\n            std::vector<std::string> destNotes;\n            CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(marker.dest);\n            for (CustomMarkerCollection::ContainerType::const_iterator iter = markers.first; iter != markers.second; ++iter)\n                destNotes.push_back(iter->second.mNote);\n\n            MarkerUserData data (mLocalMapRender);\n            data.notes = destNotes;\n            data.caption = marker.name;\n            MyGUI::IntPoint widgetPos = getMarkerPosition(marker.x, marker.y, data);\n            MyGUI::IntCoord widgetCoord(widgetPos.left - 4,\n                                        widgetPos.top - 4,\n                                        8, 8);\n            ++counter;\n            MarkerWidget* markerWidget = mLocalMap->createWidget<MarkerWidget>(\"MarkerButton\",\n                widgetCoord, MyGUI::Align::Default);\n            markerWidget->setNormalColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags(\"#{fontcolour=normal}\")));\n            markerWidget->setHoverColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags(\"#{fontcolour=normal_over}\")));\n            markerWidget->setDepth(Local_MarkerLayer);\n            markerWidget->setNeedMouseFocus(true);\n            // Used by tooltips to not show the tooltip if marker is hidden by fog of war\n            markerWidget->setUserString(\"ToolTipType\", \"MapMarker\");\n\n            markerWidget->setUserData(data);\n            doorMarkerCreated(markerWidget);\n\n            mDoorMarkerWidgets.push_back(markerWidget);\n        }\n    }\n\n    void LocalMapBase::updateMagicMarkers()\n    {\n        // clear all previous markers\n        for (MyGUI::Widget* widget : mMagicMarkerWidgets)\n            MyGUI::Gui::getInstance().destroyWidget(widget);\n        mMagicMarkerWidgets.clear();\n\n        addDetectionMarkers(MWBase::World::Detect_Creature);\n        addDetectionMarkers(MWBase::World::Detect_Key);\n        addDetectionMarkers(MWBase::World::Detect_Enchantment);\n\n        // Add marker for the spot marked with Mark magic effect\n        MWWorld::CellStore* markedCell = nullptr;\n        ESM::Position markedPosition;\n        MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition);\n        if (markedCell && markedCell->isExterior() == !mInterior\n                && (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->mName, mPrefix)))\n        {\n            MarkerUserData markerPos (mLocalMapRender);\n            MyGUI::IntPoint widgetPos = getMarkerPosition(markedPosition.pos[0], markedPosition.pos[1], markerPos);\n            MyGUI::IntCoord widgetCoord(widgetPos.left - 4,\n                                        widgetPos.top - 4,\n                                        8, 8);\n            MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>(\"ImageBox\",\n                widgetCoord, MyGUI::Align::Default);\n            markerWidget->setDepth(Local_MarkerAboveFogLayer);\n            markerWidget->setImageTexture(\"textures\\\\menu_map_smark.dds\");\n            markerWidget->setNeedMouseFocus(false);\n            mMagicMarkerWidgets.push_back(markerWidget);\n        }\n\n        redraw();\n    }\n\n    // ------------------------------------------------------------------------------------------\n\n    MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue)\n        : WindowPinnableBase(\"openmw_map_window.layout\")\n        , LocalMapBase(customMarkers, localMapRender)\n        , NoDrop(drag, mMainWidget)\n        , mGlobalMap(nullptr)\n        , mGlobalMapImage(nullptr)\n        , mGlobalMapOverlay(nullptr)\n        , mGlobal(Settings::Manager::getBool(\"global\", \"Map\"))\n        , mEventBoxGlobal(nullptr)\n        , mEventBoxLocal(nullptr)\n        , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue))\n        , mEditNoteDialog()\n    {\n        static bool registered = false;\n        if (!registered)\n        {\n            MyGUI::FactoryManager::getInstance().registerFactory<MarkerWidget>(\"Widget\");\n            registered = true;\n        }\n\n        mEditNoteDialog.setVisible(false);\n        mEditNoteDialog.eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditOk);\n        mEditNoteDialog.eventDeleteClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditDelete);\n\n        setCoord(500,0,320,300);\n\n        getWidget(mLocalMap, \"LocalMap\");\n        getWidget(mGlobalMap, \"GlobalMap\");\n        getWidget(mGlobalMapImage, \"GlobalMapImage\");\n        getWidget(mGlobalMapOverlay, \"GlobalMapOverlay\");\n        getWidget(mPlayerArrowLocal, \"CompassLocal\");\n        getWidget(mPlayerArrowGlobal, \"CompassGlobal\");\n\n        mPlayerArrowGlobal->setDepth(Global_CompassLayer);\n        mPlayerArrowGlobal->setNeedMouseFocus(false);\n        mGlobalMapImage->setDepth(Global_MapLayer);\n        mGlobalMapOverlay->setDepth(Global_ExploreOverlayLayer);\n\n        mLastScrollWindowCoordinates = mLocalMap->getCoord();\n        mLocalMap->eventChangeCoord += MyGUI::newDelegate(this, &MapWindow::onChangeScrollWindowCoord);\n\n        mGlobalMap->setVisible (false);\n\n        getWidget(mButton, \"WorldButton\");\n        mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);\n        mButton->setCaptionWithReplacing( mGlobal ? \"#{sLocal}\" : \"#{sWorld}\");\n\n        getWidget(mEventBoxGlobal, \"EventBoxGlobal\");\n        mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);\n        mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);\n        mEventBoxGlobal->setDepth(Global_ExploreOverlayLayer);\n\n        getWidget(mEventBoxLocal, \"EventBoxLocal\");\n        mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);\n        mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);\n        mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked);\n\n        LocalMapBase::init(mLocalMap, mPlayerArrowLocal);\n\n        mGlobalMap->setVisible(mGlobal);\n        mLocalMap->setVisible(!mGlobal);\n    }\n\n    void MapWindow::onNoteEditOk()\n    {\n        if (mEditNoteDialog.getDeleteButtonShown())\n            mCustomMarkers.updateMarker(mEditingMarker, mEditNoteDialog.getText());\n        else\n        {\n            mEditingMarker.mNote = mEditNoteDialog.getText();\n            mCustomMarkers.addMarker(mEditingMarker);\n        }\n\n        mEditNoteDialog.setVisible(false);\n    }\n\n    void MapWindow::onNoteEditDelete()\n    {\n        ConfirmationDialog* confirmation = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();\n        confirmation->askForConfirmation(\"#{sDeleteNote}\");\n        confirmation->eventCancelClicked.clear();\n        confirmation->eventOkClicked.clear();\n        confirmation->eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditDeleteConfirm);\n    }\n\n    void MapWindow::onNoteEditDeleteConfirm()\n    {\n        mCustomMarkers.deleteMarker(mEditingMarker);\n\n        mEditNoteDialog.setVisible(false);\n    }\n\n    void MapWindow::onCustomMarkerDoubleClicked(MyGUI::Widget *sender)\n    {\n        mEditingMarker = *sender->getUserData<ESM::CustomMarker>();\n        mEditNoteDialog.setText(mEditingMarker.mNote);\n        mEditNoteDialog.showDeleteButton(true);\n        mEditNoteDialog.setVisible(true);\n    }\n\n    void MapWindow::onMapDoubleClicked(MyGUI::Widget *sender)\n    {\n        MyGUI::IntPoint clickedPos = MyGUI::InputManager::getInstance().getMousePosition();\n\n        MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition();\n        int x = int(widgetPos.left/float(mMapWidgetSize))-mCellDistance;\n        int y = (int(widgetPos.top/float(mMapWidgetSize))-mCellDistance)*-1;\n        float nX = widgetPos.left/float(mMapWidgetSize) - int(widgetPos.left/float(mMapWidgetSize));\n        float nY = widgetPos.top/float(mMapWidgetSize) - int(widgetPos.top/float(mMapWidgetSize));\n        x += mCurX;\n        y += mCurY;\n\n        osg::Vec2f worldPos;\n        if (mInterior)\n        {\n            worldPos = mLocalMapRender->interiorMapToWorldPosition(nX, nY, x, y);\n        }\n        else\n        {\n            worldPos.x() = (x + nX) * cellSize;\n            worldPos.y() = (y + (1.0f-nY)) * cellSize;\n        }\n\n        mEditingMarker.mWorldX = worldPos.x();\n        mEditingMarker.mWorldY = worldPos.y();\n\n        mEditingMarker.mCell.mPaged = !mInterior;\n        if (mInterior)\n            mEditingMarker.mCell.mWorldspace = LocalMapBase::mPrefix;\n        else\n        {\n            mEditingMarker.mCell.mWorldspace = ESM::CellId::sDefaultWorldspace;\n            mEditingMarker.mCell.mIndex.mX = x;\n            mEditingMarker.mCell.mIndex.mY = y;\n        }\n\n        mEditNoteDialog.setVisible(true);\n        mEditNoteDialog.showDeleteButton(false);\n        mEditNoteDialog.setText(\"\");\n    }\n\n    void MapWindow::onChangeScrollWindowCoord(MyGUI::Widget* sender)\n    {\n        MyGUI::IntCoord currentCoordinates = sender->getCoord();\n\n        MyGUI::IntPoint currentViewPortCenter = MyGUI::IntPoint(currentCoordinates.width / 2, currentCoordinates.height / 2);\n        MyGUI::IntPoint lastViewPortCenter = MyGUI::IntPoint(mLastScrollWindowCoordinates.width / 2, mLastScrollWindowCoordinates.height / 2);\n        MyGUI::IntPoint viewPortCenterDiff = currentViewPortCenter - lastViewPortCenter;\n\n        mLocalMap->setViewOffset(mLocalMap->getViewOffset() + viewPortCenterDiff);\n        mGlobalMap->setViewOffset(mGlobalMap->getViewOffset() + viewPortCenterDiff);\n\n        mLastScrollWindowCoordinates = currentCoordinates;\n    }\n\n    void MapWindow::setVisible(bool visible)\n    {\n        WindowBase::setVisible(visible);\n        mButton->setVisible(visible && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_None);\n    }\n\n    void MapWindow::renderGlobalMap()\n    {\n        mGlobalMapRender->render();\n        mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());\n        mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());\n    }\n\n    MapWindow::~MapWindow()\n    {\n        delete mGlobalMapRender;\n    }\n\n    void MapWindow::setCellName(const std::string& cellName)\n    {\n        setTitle(\"#{sCell=\" + cellName + \"}\");\n    }\n\n    void MapWindow::addVisitedLocation(const std::string& name, int x, int y)\n    {\n        CellId cell;\n        cell.first = x;\n        cell.second = y;\n        if (mMarkers.insert(cell).second)\n        {\n            float worldX, worldY;\n            mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY);\n\n            int markerSize = 12;\n            int offset = mGlobalMapRender->getCellSize()/2 - markerSize/2;\n            MyGUI::IntCoord widgetCoord(\n                        static_cast<int>(worldX * mGlobalMapRender->getWidth()+offset),\n                        static_cast<int>(worldY * mGlobalMapRender->getHeight() + offset),\n                        markerSize, markerSize);\n\n            MyGUI::Widget* markerWidget = mGlobalMap->createWidget<MyGUI::Widget>(\"MarkerButton\",\n                widgetCoord, MyGUI::Align::Default);\n\n            markerWidget->setUserString(\"Caption_TextOneLine\", \"#{sCell=\" + name + \"}\");\n\n            setGlobalMapMarkerTooltip(markerWidget, x, y);\n\n            markerWidget->setUserString(\"ToolTipLayout\", \"TextToolTipOneLine\");\n\n            markerWidget->setNeedMouseFocus(true);\n            markerWidget->setColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags(\"#{fontcolour=normal}\")));\n            markerWidget->setDepth(Global_MarkerLayer);\n            markerWidget->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);\n            markerWidget->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);\n            mGlobalMapMarkers[std::make_pair(x,y)] = markerWidget;\n        }\n    }\n\n    void MapWindow::cellExplored(int x, int y)\n    {\n        mGlobalMapRender->cleanupCameras();\n        mGlobalMapRender->exploreCell(x, y, mLocalMapRender->getMapTexture(x, y));\n    }\n\n    void MapWindow::onFrame(float dt)\n    {\n        LocalMapBase::onFrame(dt);\n        NoDrop::onFrame(dt);\n    }\n\n    void MapWindow::setGlobalMapMarkerTooltip(MyGUI::Widget* markerWidget, int x, int y)\n    {\n        ESM::CellId cellId;\n        cellId.mIndex.mX = x;\n        cellId.mIndex.mY = y;\n        cellId.mWorldspace = ESM::CellId::sDefaultWorldspace;\n        cellId.mPaged = true;\n        CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellId);\n        std::vector<std::string> destNotes;\n        for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; ++it)\n            destNotes.push_back(it->second.mNote);\n\n        if (!destNotes.empty())\n        {\n            MarkerUserData data (nullptr);\n            data.notes = destNotes;\n            data.caption = markerWidget->getUserString(\"Caption_TextOneLine\");\n            markerWidget->setUserData(data);\n            markerWidget->setUserString(\"ToolTipType\", \"MapMarker\");\n        }\n        else\n        {\n            markerWidget->setUserString(\"ToolTipType\", \"Layout\");\n        }\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the setting of the image data for a global map tile from elsewhere\n        in the code\n    */\n    void MapWindow::setGlobalMapImage(int cellX, int cellY, const std::vector<char>& imageData)\n    {\n        mGlobalMapRender->setImage(cellX, cellY, imageData);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void MapWindow::updateCustomMarkers()\n    {\n        LocalMapBase::updateCustomMarkers();\n\n        for (auto& widgetPair : mGlobalMapMarkers)\n        {\n            int x = widgetPair.first.first;\n            int y = widgetPair.first.second;\n            MyGUI::Widget* markerWidget = widgetPair.second;\n            setGlobalMapMarkerTooltip(markerWidget, x, y);\n        }\n    }\n\n    void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)\n    {\n        if (_id!=MyGUI::MouseButton::Left) return;\n        mLastDragPos = MyGUI::IntPoint(_left, _top);\n    }\n\n    void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)\n    {\n        if (_id!=MyGUI::MouseButton::Left) return;\n\n        MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos;\n\n        if (!mGlobal)\n            mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff );\n        else\n            mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff );\n\n        mLastDragPos = MyGUI::IntPoint(_left, _top);\n    }\n\n    void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)\n    {\n        mGlobal = !mGlobal;\n        mGlobalMap->setVisible(mGlobal);\n        mLocalMap->setVisible(!mGlobal);\n\n        Settings::Manager::setBool(\"global\", \"Map\", mGlobal);\n\n        mButton->setCaptionWithReplacing( mGlobal ? \"#{sLocal}\" :\n                \"#{sWorld}\");\n\n        if (mGlobal)\n            globalMapUpdatePlayer ();\n    }\n\n    void MapWindow::onPinToggled()\n    {\n        Settings::Manager::setBool(\"map pin\", \"Windows\", mPinned);\n\n        MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned);\n    }\n\n    void MapWindow::onTitleDoubleClicked()\n    {\n        if (MyGUI::InputManager::getInstance().isShiftPressed())\n            MWBase::Environment::get().getWindowManager()->toggleMaximized(this);\n        else if (!mPinned)\n            MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map);\n    }\n\n    void MapWindow::onOpen()\n    {\n        ensureGlobalMapLoaded();\n\n        globalMapUpdatePlayer();\n    }\n\n    void MapWindow::globalMapUpdatePlayer ()\n    {\n        // For interiors, position is set by WindowManager via setGlobalMapPlayerPosition\n        if (MWBase::Environment::get().getWorld ()->isCellExterior ())\n        {\n            osg::Vec3f pos = MWBase::Environment::get().getWorld ()->getPlayerPtr().getRefData().getPosition().asVec3();\n            setGlobalMapPlayerPosition(pos.x(), pos.y());\n        }\n    }\n\n    void MapWindow::notifyPlayerUpdate ()\n    {\n        globalMapUpdatePlayer ();\n\n        setGlobalMapPlayerDir(mLastDirectionX, mLastDirectionY);\n    }\n\n    void MapWindow::setGlobalMapPlayerPosition(float worldX, float worldY)\n    {\n        float x, y;\n        mGlobalMapRender->worldPosToImageSpace (worldX, worldY, x, y);\n        x *= mGlobalMapRender->getWidth();\n        y *= mGlobalMapRender->getHeight();\n\n        mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(static_cast<int>(x - 16), static_cast<int>(y - 16)));\n\n        // set the view offset so that player is in the center\n        MyGUI::IntSize viewsize = mGlobalMap->getSize();\n        MyGUI::IntPoint viewoffs(static_cast<int>(viewsize.width * 0.5f - x), static_cast<int>(viewsize.height *0.5 - y));\n        mGlobalMap->setViewOffset(viewoffs);\n    }\n\n    void MapWindow::setGlobalMapPlayerDir(const float x, const float y)\n    {\n        MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain();\n        MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();\n        rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));\n        float angle = std::atan2(x,y);\n        rotatingSubskin->setAngle(angle);\n    }\n\n    void MapWindow::ensureGlobalMapLoaded()\n    {\n        if (!mGlobalMapTexture.get())\n        {\n            mGlobalMapTexture.reset(new osgMyGUI::OSGTexture(mGlobalMapRender->getBaseTexture()));\n            mGlobalMapImage->setRenderItemTexture(mGlobalMapTexture.get());\n            mGlobalMapImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));\n\n            mGlobalMapOverlayTexture.reset(new osgMyGUI::OSGTexture(mGlobalMapRender->getOverlayTexture()));\n            mGlobalMapOverlay->setRenderItemTexture(mGlobalMapOverlayTexture.get());\n            mGlobalMapOverlay->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));\n\n            // Redraw children in proper order\n            mGlobalMap->getParent()->_updateChilds();\n        }\n    }\n\n    void MapWindow::clear()\n    {\n        mMarkers.clear();\n\n        mGlobalMapRender->clear();\n        mChanged = true;\n\n        for (auto& widgetPair : mGlobalMapMarkers)\n            MyGUI::Gui::getInstance().destroyWidget(widgetPair.second);\n        mGlobalMapMarkers.clear();\n    }\n\n    void MapWindow::write(ESM::ESMWriter &writer, Loading::Listener& progress)\n    {\n        ESM::GlobalMap map;\n        mGlobalMapRender->write(map);\n\n        map.mMarkers = mMarkers;\n\n        writer.startRecord(ESM::REC_GMAP);\n        map.save(writer);\n        writer.endRecord(ESM::REC_GMAP);\n    }\n\n    void MapWindow::readRecord(ESM::ESMReader &reader, uint32_t type)\n    {\n        if (type == ESM::REC_GMAP)\n        {\n            ESM::GlobalMap map;\n            map.load(reader);\n\n            mGlobalMapRender->read(map);\n\n            for (const ESM::GlobalMap::CellId& cellId : map.mMarkers)\n            {\n                const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(cellId.first, cellId.second);\n                if (cell && !cell->mName.empty())\n                    addVisitedLocation(cell->mName, cellId.first, cellId.second);\n            }\n        }\n    }\n\n    void MapWindow::setAlpha(float alpha)\n    {\n        NoDrop::setAlpha(alpha);\n        // can't allow showing map with partial transparency, as the fog of war will also go transparent\n        // and reveal parts of the map you shouldn't be able to see\n        for (MapEntry& entry : mMaps)\n            entry.mMapWidget->setVisible(alpha == 1);\n    }\n\n    void MapWindow::customMarkerCreated(MyGUI::Widget *marker)\n    {\n        marker->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);\n        marker->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);\n        marker->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onCustomMarkerDoubleClicked);\n    }\n\n    void MapWindow::doorMarkerCreated(MyGUI::Widget *marker)\n    {\n        marker->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);\n        marker->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);\n    }\n\n    // -------------------------------------------------------------------\n\n    EditNoteDialog::EditNoteDialog()\n        : WindowModal(\"openmw_edit_note.layout\")\n    {\n        getWidget(mOkButton, \"OkButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mDeleteButton, \"DeleteButton\");\n        getWidget(mTextEdit, \"TextEdit\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onCancelButtonClicked);\n        mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onOkButtonClicked);\n        mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onDeleteButtonClicked);\n    }\n\n    void EditNoteDialog::showDeleteButton(bool show)\n    {\n        mDeleteButton->setVisible(show);\n    }\n\n    bool EditNoteDialog::getDeleteButtonShown()\n    {\n        return mDeleteButton->getVisible();\n    }\n\n    void EditNoteDialog::setText(const std::string &text)\n    {\n        mTextEdit->setCaption(MyGUI::TextIterator::toTagsString(text));\n    }\n\n    std::string EditNoteDialog::getText()\n    {\n        return MyGUI::TextIterator::getOnlyText(mTextEdit->getCaption());\n    }\n\n    void EditNoteDialog::onOpen()\n    {\n        WindowModal::onOpen();\n        center();\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);\n    }\n\n    void EditNoteDialog::onCancelButtonClicked(MyGUI::Widget *sender)\n    {\n        setVisible(false);\n    }\n\n    void EditNoteDialog::onOkButtonClicked(MyGUI::Widget *sender)\n    {\n        eventOkClicked();\n    }\n\n    void EditNoteDialog::onDeleteButtonClicked(MyGUI::Widget *sender)\n    {\n        eventDeleteClicked();\n    }\n\n    bool LocalMapBase::MarkerUserData::isPositionExplored() const\n    {\n        if (!mLocalMapRender)\n            return true;\n        return mLocalMapRender->isPositionExplored(nX, nY, cellX, cellY);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/mapwindow.hpp",
    "content": "#ifndef MWGUI_MAPWINDOW_H\n#define MWGUI_MAPWINDOW_H\n\n#include <stdint.h>\n#include <memory>\n\n#include \"windowpinnablebase.hpp\"\n\n#include <components/esm/cellid.hpp>\n\n#include <components/esm/custommarkerstate.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Declare GUIController here so we can use it for delegates\n*/\nnamespace mwmp\n{\n    class GUIController;\n}\n/*\n    End of tes3mp addition\n*/\n\nnamespace MWRender\n{\n    class GlobalMap;\n    class LocalMap;\n}\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n}\n\nnamespace MWWorld\n{\n    class CellStore;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace SceneUtil\n{\n    class WorkQueue;\n}\n\nnamespace MWGui\n{\n\n    class CustomMarkerCollection\n    {\n    public:\n        void addMarker(const ESM::CustomMarker& marker, bool triggerEvent=true);\n        void deleteMarker (const ESM::CustomMarker& marker);\n        void updateMarker(const ESM::CustomMarker& marker, const std::string& newNote);\n\n        void clear();\n\n        size_t size() const;\n\n        typedef std::multimap<ESM::CellId, ESM::CustomMarker> ContainerType;\n\n        typedef std::pair<ContainerType::const_iterator, ContainerType::const_iterator> RangeType;\n\n        ContainerType::const_iterator begin() const;\n        ContainerType::const_iterator end() const;\n\n        RangeType getMarkers(const ESM::CellId& cellId) const;\n\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n        EventHandle_Void eventMarkersChanged;\n\n    private:\n        ContainerType mMarkers;\n    };\n\n    class LocalMapBase\n    {\n        /*\n            Start of tes3mp addition\n\n            Allow the use of GUIController by declaring it as a friend class\n        */\n        friend class mwmp::GUIController;\n        /*\n            End of tes3mp addition\n        */\n    public:\n        LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled = true);\n        virtual ~LocalMapBase();\n        void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass);\n\n        void setCellPrefix(const std::string& prefix);\n        void setActiveCell(const int x, const int y, bool interior=false);\n        void requestMapRender(const MWWorld::CellStore* cell);\n        void setPlayerDir(const float x, const float y);\n        void setPlayerPos(int cellX, int cellY, const float nx, const float ny);\n\n        void onFrame(float dt);\n\n        bool toggleFogOfWar();\n\n        struct MarkerUserData\n        {\n            MarkerUserData(MWRender::LocalMap* map)\n                : mLocalMapRender(map)\n                , cellX(0)\n                , cellY(0)\n                , nX(0.f)\n                , nY(0.f)\n            {\n            }\n\n            bool isPositionExplored() const;\n\n            MWRender::LocalMap* mLocalMapRender;\n            int cellX;\n            int cellY;\n            float nX;\n            float nY;\n            std::vector<std::string> notes;\n            std::string caption;\n        };\n\n    protected:\n        MWRender::LocalMap* mLocalMapRender;\n\n        int mCurX, mCurY;\n        bool mInterior;\n        MyGUI::ScrollView* mLocalMap;\n        MyGUI::ImageBox* mCompass;\n        std::string mPrefix;\n        bool mChanged;\n        bool mFogOfWarToggled;\n        bool mFogOfWarEnabled;\n\n        int mMapWidgetSize;\n\n        int mNumCells; // for convenience, mCellDistance * 2 + 1\n        int mCellDistance;\n\n        // Stores markers that were placed by a player. May be shared between multiple map views.\n        CustomMarkerCollection& mCustomMarkers;\n\n        struct MapEntry\n        {\n            MapEntry(MyGUI::ImageBox* mapWidget, MyGUI::ImageBox* fogWidget)\n                : mMapWidget(mapWidget), mFogWidget(fogWidget), mCellX(0), mCellY(0) {}\n\n            MyGUI::ImageBox* mMapWidget;\n            MyGUI::ImageBox* mFogWidget;\n            std::shared_ptr<MyGUI::ITexture> mMapTexture;\n            std::shared_ptr<MyGUI::ITexture> mFogTexture;\n            int mCellX;\n            int mCellY;\n        };\n        std::vector<MapEntry> mMaps;\n\n        // Keep track of created marker widgets, just to easily remove them later.\n        std::vector<MyGUI::Widget*> mDoorMarkerWidgets;\n        std::vector<MyGUI::Widget*> mMagicMarkerWidgets;\n        std::vector<MyGUI::Widget*> mCustomMarkerWidgets;\n\n        /*\n            Start of tes3mp addition\n\n            Add a new group of Widgets for player markers\n        */\n        std::vector<MyGUI::Widget*> mPlayerMarkerWidgets;\n        /*\n            End of tes3mp addition\n        */\n\n        virtual void updateCustomMarkers();\n\n        /*\n            Start of tes3mp addition\n\n            Send the LocalMapBase to our GUIController when updating player markers\n        */\n        virtual void updatePlayerMarkers();\n        /*\n            End of tes3mp addition\n        */\n\n        void applyFogOfWar();\n\n        MyGUI::IntPoint getMarkerPosition (float worldX, float worldY, MarkerUserData& markerPos);\n\n        virtual void notifyPlayerUpdate() {}\n        virtual void notifyMapChanged() {}\n\n        virtual void customMarkerCreated(MyGUI::Widget* marker) {}\n        virtual void doorMarkerCreated(MyGUI::Widget* marker) {}\n\n        void updateRequiredMaps();\n\n        void updateMagicMarkers();\n        void addDetectionMarkers(int type);\n\n        void redraw();\n\n        float mMarkerUpdateTimer;\n\n        float mLastDirectionX;\n        float mLastDirectionY;\n\n    private:\n        void updateDoorMarkers();\n        bool mNeedDoorMarkersUpdate;\n    };\n\n    class EditNoteDialog : public MWGui::WindowModal\n    {\n    public:\n        EditNoteDialog();\n\n        void onOpen() override;\n\n        void showDeleteButton(bool show);\n        bool getDeleteButtonShown();\n        void setText(const std::string& text);\n        std::string getText();\n\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        EventHandle_Void eventDeleteClicked;\n        EventHandle_Void eventOkClicked;\n\n    private:\n        void onCancelButtonClicked(MyGUI::Widget* sender);\n        void onOkButtonClicked(MyGUI::Widget* sender);\n        void onDeleteButtonClicked(MyGUI::Widget* sender);\n\n        MyGUI::TextBox* mTextEdit;\n        MyGUI::Button* mOkButton;\n        MyGUI::Button* mCancelButton;\n        MyGUI::Button* mDeleteButton;\n    };\n\n    class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop\n    {\n        /*\n            Start of tes3mp addition\n\n            Allow the use of GUIController by declaring it as a friend class\n        */\n        friend class mwmp::GUIController;\n        /*\n            End of tes3mp addition\n        */\n    public:\n        MapWindow(CustomMarkerCollection& customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue);\n        virtual ~MapWindow();\n\n        void setCellName(const std::string& cellName);\n\n        void setAlpha(float alpha) override;\n        void setVisible(bool visible) override;\n\n        void renderGlobalMap();\n\n        /// adds the marker to the global map\n        /// @param name The ESM::Cell::mName\n        void addVisitedLocation(const std::string& name, int x, int y);\n\n        // reveals this cell's map on the global map\n        void cellExplored(int x, int y);\n\n        void setGlobalMapPlayerPosition (float worldX, float worldY);\n        void setGlobalMapPlayerDir(const float x, const float y);\n\n        /*\n            Start of tes3mp addition\n\n            Allow the setting of the image data for a global map tile from elsewhere\n            in the code\n        */\n        void setGlobalMapImage(int cellX, int cellY, const std::vector<char>& imageData);\n        /*\n            End of tes3mp addition\n        */\n\n        void ensureGlobalMapLoaded();\n\n        void onOpen() override;\n\n        void onFrame(float dt) override;\n\n        void updateCustomMarkers() override;\n\n        /*\n            Start of tes3mp addition\n\n            Send the MapWindow to our GUIController when updating player markers\n        */\n        virtual void updatePlayerMarkers();\n        /*\n            End of tes3mp addition\n        */\n\n        /// Clear all savegame-specific data\n        void clear() override;\n\n        void write (ESM::ESMWriter& writer, Loading::Listener& progress);\n        void readRecord (ESM::ESMReader& reader, uint32_t type);\n\n    private:\n        void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);\n        void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);\n        void onWorldButtonClicked(MyGUI::Widget* _sender);\n        void onMapDoubleClicked(MyGUI::Widget* sender);\n        void onCustomMarkerDoubleClicked(MyGUI::Widget* sender);\n        void onNoteEditOk();\n        void onNoteEditDelete();\n        void onNoteEditDeleteConfirm();\n        void onNoteDoubleClicked(MyGUI::Widget* sender);\n        void onChangeScrollWindowCoord(MyGUI::Widget* sender);\n        void globalMapUpdatePlayer();\n        void setGlobalMapMarkerTooltip(MyGUI::Widget* widget, int x, int y);\n\n        MyGUI::ScrollView* mGlobalMap;\n        std::unique_ptr<MyGUI::ITexture> mGlobalMapTexture;\n        std::unique_ptr<MyGUI::ITexture> mGlobalMapOverlayTexture;\n        MyGUI::ImageBox* mGlobalMapImage;\n        MyGUI::ImageBox* mGlobalMapOverlay;\n        MyGUI::ImageBox* mPlayerArrowLocal;\n        MyGUI::ImageBox* mPlayerArrowGlobal;\n        MyGUI::Button* mButton;\n        MyGUI::IntPoint mLastDragPos;\n        bool mGlobal;\n\n        MyGUI::IntCoord mLastScrollWindowCoordinates;\n\n        // Markers on global map\n        typedef std::pair<int, int> CellId;\n        std::set<CellId> mMarkers;\n\n        MyGUI::Button* mEventBoxGlobal;\n        MyGUI::Button* mEventBoxLocal;\n\n        MWRender::GlobalMap* mGlobalMapRender;\n\n        std::map<std::pair<int, int>, MyGUI::Widget*> mGlobalMapMarkers;\n\n        EditNoteDialog mEditNoteDialog;\n        ESM::CustomMarker mEditingMarker;\n\n        void onPinToggled() override;\n        void onTitleDoubleClicked() override;\n\n        void doorMarkerCreated(MyGUI::Widget* marker) override;\n        void customMarkerCreated(MyGUI::Widget *marker) override;\n\n        void notifyPlayerUpdate() override;\n\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/merchantrepair.cpp",
    "content": "#include \"merchantrepair.hpp\"\n\n#include <components/esm/loadgmst.hpp>\n\n#include <MyGUI_Button.h>\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_Gui.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace MWGui\n{\nMerchantRepair::MerchantRepair()\n    : WindowBase(\"openmw_merchantrepair.layout\")\n{\n    getWidget(mList, \"RepairView\");\n    getWidget(mOkButton, \"OkButton\");\n    getWidget(mGoldLabel, \"PlayerGold\");\n\n    mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onOkButtonClick);\n}\n\nvoid MerchantRepair::setPtr(const MWWorld::Ptr &actor)\n{\n    mActor = actor;\n\n    while (mList->getChildCount())\n        MyGUI::Gui::getInstance().destroyWidget(mList->getChildAt(0));\n\n    int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n    int currentY = 0;\n\n    MWWorld::Ptr player = MWMechanics::getPlayer();\n    int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n    MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);\n    int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor;\n    for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); iter!=store.end(); ++iter)\n    {\n        if (iter->getClass().hasItemHealth(*iter))\n        {\n            int maxDurability = iter->getClass().getItemMaxHealth(*iter);\n            int durability = iter->getClass().getItemHealth(*iter);\n            if (maxDurability == durability || maxDurability == 0)\n                continue;\n\n            int basePrice = iter->getClass().getValue(*iter);\n            float fRepairMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                    .find(\"fRepairMult\")->mValue.getFloat();\n\n            float p = static_cast<float>(std::max(1, basePrice));\n            float r = static_cast<float>(std::max(1, static_cast<int>(maxDurability / p)));\n\n            int x = static_cast<int>((maxDurability - durability) / r);\n            x = static_cast<int>(fRepairMult * x);\n            x = std::max(1, x);\n\n            int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true);\n\n            std::string name = iter->getClass().getName(*iter)\n                    + \" - \" + MyGUI::utility::toString(price)\n                    + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                    .find(\"sgp\")->mValue.getString();\n\n            MyGUI::Button* button =\n                mList->createWidget<MyGUI::Button>(price <= playerGold ? \"SandTextButton\" : \"SandTextButtonDisabled\", // can't use setEnabled since that removes tooltip\n                    0,\n                    currentY,\n                    0,\n                    lineHeight,\n                    MyGUI::Align::Default\n                );\n\n            currentY += lineHeight;\n\n            button->setUserString(\"Price\", MyGUI::utility::toString(price));\n            button->setUserData(MWWorld::Ptr(*iter));\n            button->setCaptionWithReplacing(name);\n            button->setSize(mList->getWidth(), lineHeight);\n            button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel);\n            button->setUserString(\"ToolTipType\", \"ItemPtr\");\n            button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick);\n        }\n    }\n    // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n    mList->setVisibleVScroll(false);\n    mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY)));\n    mList->setVisibleVScroll(true);\n\n    mGoldLabel->setCaptionWithReplacing(\"#{sGold}: \"\n        + MyGUI::utility::toString(playerGold));\n}\n\nvoid MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel)\n{\n    if (mList->getViewOffset().top + _rel*0.3f > 0)\n        mList->setViewOffset(MyGUI::IntPoint(0, 0));\n    else\n        mList->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mList->getViewOffset().top + _rel*0.3f)));\n}\n\nvoid MerchantRepair::onOpen()\n{\n    center();\n    // Reset scrollbars\n    mList->setViewOffset(MyGUI::IntPoint(0, 0));\n}\n\nvoid MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender)\n{\n    MWWorld::Ptr player = MWMechanics::getPlayer();\n\n    int price = MyGUI::utility::parseInt(sender->getUserString(\"Price\"));\n    if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId))\n        return;\n\n    // repair\n    MWWorld::Ptr item = *sender->getUserData<MWWorld::Ptr>();\n    item.getCellRef().setCharge(item.getClass().getItemMaxHealth(item));\n\n    player.getClass().getContainerStore(player).restack(item);\n\n    MWBase::Environment::get().getWindowManager()->playSound(\"Repair\");\n\n    player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);\n\n    // add gold to NPC trading gold pool\n    MWMechanics::CreatureStats& actorStats = mActor.getClass().getCreatureStats(mActor);\n\n    /*\n        Start of tes3mp change (major)\n\n        Don't unilaterally change the merchant's gold pool on our client and instead let the server do it\n    */\n    //actorStats.setGoldPool(actorStats.getGoldPool() + price);\n\n    mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n    objectList->reset();\n    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n    objectList->addObjectMiscellaneous(mActor, actorStats.getGoldPool() + price, actorStats.getLastRestockTime().getHour(),\n        actorStats.getLastRestockTime().getDay());\n    objectList->sendObjectMiscellaneous();\n    /*\n        End of tes3mp change (major)\n    */\n\n    setPtr(mActor);\n}\n\nvoid MerchantRepair::onOkButtonClick(MyGUI::Widget *sender)\n{\n    MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_MerchantRepair);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/merchantrepair.hpp",
    "content": "#ifndef OPENMW_MWGUI_MERCHANTREPAIR_H\n#define OPENMW_MWGUI_MERCHANTREPAIR_H\n\n#include \"windowbase.hpp\"\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWGui\n{\n\nclass MerchantRepair : public WindowBase\n{\npublic:\n    MerchantRepair();\n\n    void onOpen() override;\n\n    void setPtr(const MWWorld::Ptr& actor) override;\n\nprivate:\n    MyGUI::ScrollView* mList;\n    MyGUI::Button* mOkButton;\n    MyGUI::TextBox* mGoldLabel;\n\n    MWWorld::Ptr mActor;\n\nprotected:\n    void onMouseWheel(MyGUI::Widget* _sender, int _rel);\n    void onRepairButtonClick(MyGUI::Widget* sender);\n    void onOkButtonClick(MyGUI::Widget* sender);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/messagebox.cpp",
    "content": "#include \"messagebox.hpp\"\n\n#include <MyGUI_LanguageManager.h>\n#include <MyGUI_EditBox.h>\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_Button.h>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/GUIController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#undef MessageBox\n\nnamespace MWGui\n{\n\n    MessageBoxManager::MessageBoxManager (float timePerChar)\n    {\n        mInterMessageBoxe = nullptr;\n        mStaticMessageBox = nullptr;\n        mLastButtonPressed = -1;\n        mMessageBoxSpeed = timePerChar;\n    }\n\n    MessageBoxManager::~MessageBoxManager ()\n    {\n        MessageBoxManager::clear();\n    }\n\n    int MessageBoxManager::getMessagesCount()\n    {\n        return mMessageBoxes.size();\n    }\n\n    void MessageBoxManager::clear()\n    {\n        if (mInterMessageBoxe)\n        {\n            mInterMessageBoxe->setVisible(false);\n\n            delete mInterMessageBoxe;\n            mInterMessageBoxe = nullptr;\n        }\n\n        for (MessageBox* messageBox : mMessageBoxes)\n        {\n            if (messageBox == mStaticMessageBox)\n                mStaticMessageBox = nullptr;\n            delete messageBox;\n        }\n        mMessageBoxes.clear();\n\n        mLastButtonPressed = -1;\n    }\n\n    void MessageBoxManager::onFrame (float frameDuration)\n    {\n        std::vector<MessageBox*>::iterator it;\n        for(it = mMessageBoxes.begin(); it != mMessageBoxes.end();)\n        {\n            (*it)->mCurrentTime += frameDuration;\n            if((*it)->mCurrentTime >= (*it)->mMaxTime && *it != mStaticMessageBox)\n            {\n                delete *it;\n                it = mMessageBoxes.erase(it);\n            }\n            else\n                ++it;\n        }\n\n        float height = 0;\n        it = mMessageBoxes.begin();\n        while(it != mMessageBoxes.end())\n        {\n            (*it)->update(static_cast<int>(height));\n            height += (*it)->getHeight();\n            ++it;\n        }\n\n        if(mInterMessageBoxe != nullptr && mInterMessageBoxe->mMarkedToDelete) {\n            mLastButtonPressed = mInterMessageBoxe->readPressedButton();\n\n            /*\n                Start of tes3mp addition\n\n                If this message box was created by the server, send the input back to it\n            */\n            if (mInterMessageBoxe->mHasServerOrigin)\n                mwmp::Main::get().getGUIController()->processCustomMessageBoxInput(mLastButtonPressed);\n            /*\n                End of tes3mp addition\n            */\n\n            mInterMessageBoxe->setVisible(false);\n            delete mInterMessageBoxe;\n            mInterMessageBoxe = nullptr;\n            MWBase::Environment::get().getInputManager()->changeInputMode(\n                        MWBase::Environment::get().getWindowManager()->isGuiMode());\n        }\n    }\n\n    void MessageBoxManager::createMessageBox (const std::string& message, bool stat)\n    {\n        MessageBox *box = new MessageBox(*this, message);\n        box->mCurrentTime = 0;\n        std::string realMessage = MyGUI::LanguageManager::getInstance().replaceTags(message);\n        box->mMaxTime = realMessage.length()*mMessageBoxSpeed;\n\n        if(stat)\n            mStaticMessageBox = box;\n\n        mMessageBoxes.push_back(box);\n\n        if(mMessageBoxes.size() > 3) {\n            delete *mMessageBoxes.begin();\n            mMessageBoxes.erase(mMessageBoxes.begin());\n        }\n\n        int height = 0;\n        for (MessageBox* messageBox : mMessageBoxes)\n        {\n            messageBox->update(height);\n            height += messageBox->getHeight();\n        }\n    }\n\n    void MessageBoxManager::removeStaticMessageBox ()\n    {\n        removeMessageBox(mStaticMessageBox);\n        mStaticMessageBox = nullptr;\n    }\n\n    /*\n        Start of tes3mp change (major)\n\n        Add a hasServerOrigin boolean to the list of arguments so those messageboxes\n        can be differentiated from client-only ones\n    */\n    bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons, bool hasServerOrigin)\n    /*\n        End of tes3mp change (major)\n    */\n    {\n        if (mInterMessageBoxe != nullptr)\n        {\n            Log(Debug::Warning) << \"Warning: replacing an interactive message box that was not answered yet\";\n            mInterMessageBoxe->setVisible(false);\n            delete mInterMessageBoxe;\n            mInterMessageBoxe = nullptr;\n        }\n\n        mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons);\n        /*\n            Start of tes3mp addition\n\n            Track whether the message box has a server origin\n        */\n        mInterMessageBoxe->mHasServerOrigin = hasServerOrigin;\n        /*\n            End of tes3mp addition\n        */\n        mLastButtonPressed = -1;\n\n        return true;\n    }\n\n    bool MessageBoxManager::isInteractiveMessageBox ()\n    {\n        return mInterMessageBoxe != nullptr;\n    }\n\n\n    bool MessageBoxManager::removeMessageBox (MessageBox *msgbox)\n    {\n        std::vector<MessageBox*>::iterator it;\n        for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)\n        {\n            if((*it) == msgbox)\n            {\n                delete (*it);\n                mMessageBoxes.erase(it);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    int MessageBoxManager::readPressedButton (bool reset)\n    {\n        int pressed = mLastButtonPressed;\n        if (reset)\n            mLastButtonPressed = -1;\n        return pressed;\n    }\n\n\n\n\n    MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message)\n      : Layout(\"openmw_messagebox.layout\")\n      , mCurrentTime(0)\n      , mMaxTime(0)\n      , mMessageBoxManager(parMessageBoxManager)\n      , mMessage(message)\n    {\n        // defines\n        mBottomPadding = 48;\n        mNextBoxPadding = 4;\n\n        getWidget(mMessageWidget, \"message\");\n\n        mMessageWidget->setCaptionWithReplacing(mMessage);\n    }\n\n    void MessageBox::update (int height)\n    {\n        MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();\n        MyGUI::IntPoint pos;\n        pos.left = (gameWindowSize.width - mMainWidget->getWidth())/2;\n        pos.top = (gameWindowSize.height - mMainWidget->getHeight() - height - mBottomPadding);\n\n        mMainWidget->setPosition(pos);\n    }\n\n    int MessageBox::getHeight ()\n    {\n        return mMainWidget->getHeight()+mNextBoxPadding;\n    }\n\n\n\n    InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons)\n        : WindowModal(MWBase::Environment::get().getWindowManager()->isGuiMode() ? \"openmw_interactive_messagebox_notransp.layout\" : \"openmw_interactive_messagebox.layout\")\n      , mMessageBoxManager(parMessageBoxManager)\n      , mButtonPressed(-1)\n    {\n        int textPadding = 10; // padding between text-widget and main-widget\n        int textButtonPadding = 10; // padding between the text-widget und the button-widget\n        int buttonLeftPadding = 10; // padding between the buttons if horizontal\n        int buttonTopPadding = 10; // ^-- if vertical\n        int buttonLabelLeftPadding = 12; // padding between button label and button itself, from left\n        int buttonLabelTopPadding = 4; // padding between button label and button itself, from top\n        int buttonMainPadding = 10; // padding between buttons and bottom of the main widget\n\n        mMarkedToDelete = false;\n\n\n        getWidget(mMessageWidget, \"message\");\n        getWidget(mButtonsWidget, \"buttons\");\n\n        mMessageWidget->setSize(400, mMessageWidget->getHeight());\n        mMessageWidget->setCaptionWithReplacing(message);\n\n        MyGUI::IntSize textSize = mMessageWidget->getTextSize();\n\n        MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();\n\n        int biggestButtonWidth = 0;\n        int buttonsWidth = 0;\n        int buttonsHeight = 0;\n        int buttonHeight = 0;\n        MyGUI::IntCoord dummyCoord(0, 0, 0, 0);\n\n        for(const std::string& buttonId : buttons)\n        {\n            MyGUI::Button* button = mButtonsWidget->createWidget<MyGUI::Button>(\n                MyGUI::WidgetStyle::Child,\n                std::string(\"MW_Button\"),\n                dummyCoord,\n                MyGUI::Align::Default);\n            button->setCaptionWithReplacing(buttonId);\n\n            button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed);\n\n            mButtons.push_back(button);\n\n            if (buttonsWidth != 0)\n                buttonsWidth += buttonLeftPadding;\n\n            int buttonWidth = button->getTextSize().width + 2*buttonLabelLeftPadding;\n            buttonsWidth += buttonWidth;\n\n            buttonHeight = button->getTextSize().height + 2*buttonLabelTopPadding;\n\n            if (buttonsHeight != 0)\n                buttonsHeight += buttonTopPadding;\n            buttonsHeight += buttonHeight;\n\n            if(buttonWidth > biggestButtonWidth)\n            {\n                biggestButtonWidth = buttonWidth;\n            }\n        }\n\n        MyGUI::IntSize mainWidgetSize;\n        if(buttonsWidth < textSize.width)\n        {\n            // on one line\n            mainWidgetSize.width = textSize.width + 3*textPadding;\n            mainWidgetSize.height = textPadding + textSize.height + textButtonPadding + buttonHeight + buttonMainPadding;\n\n            MyGUI::IntSize realSize = mainWidgetSize +\n                    // To account for borders\n                    (mMainWidget->getSize() - mMainWidget->getClientWidget()->getSize());\n\n            MyGUI::IntPoint absPos;\n            absPos.left = (gameWindowSize.width - realSize.width)/2;\n            absPos.top = (gameWindowSize.height - realSize.height)/2;\n\n            mMainWidget->setPosition(absPos);\n            mMainWidget->setSize(realSize);\n\n            MyGUI::IntCoord messageWidgetCoord;\n            messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;\n            messageWidgetCoord.top = textPadding;\n            mMessageWidget->setCoord(messageWidgetCoord);\n\n            mMessageWidget->setSize(textSize);\n\n            MyGUI::IntCoord buttonCord;\n            MyGUI::IntSize buttonSize(0, buttonHeight);\n            int left = (mainWidgetSize.width - buttonsWidth)/2;\n\n            for(MyGUI::Button* button : mButtons)\n            {\n                buttonCord.left = left;\n                buttonCord.top = messageWidgetCoord.top + textSize.height + textButtonPadding;\n\n                buttonSize.width = button->getTextSize().width + 2*buttonLabelLeftPadding;\n                buttonSize.height = button->getTextSize().height + 2*buttonLabelTopPadding;\n\n                button->setCoord(buttonCord);\n                button->setSize(buttonSize);\n\n                left += buttonSize.width + buttonLeftPadding;\n            }\n        }\n        else\n        {\n            // among each other\n            if(biggestButtonWidth > textSize.width) {\n                mainWidgetSize.width = biggestButtonWidth + buttonTopPadding*2;\n            }\n            else {\n                mainWidgetSize.width = textSize.width + 3*textPadding;\n            }\n\n            MyGUI::IntCoord buttonCord;\n            MyGUI::IntSize buttonSize(0, buttonHeight);\n\n            int top = textPadding + textSize.height + textButtonPadding;\n\n            for(MyGUI::Button* button : mButtons)\n            {\n                buttonSize.width = button->getTextSize().width + buttonLabelLeftPadding*2;\n                buttonSize.height = button->getTextSize().height + buttonLabelTopPadding*2;\n\n                buttonCord.top = top;\n                buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2;\n\n                button->setCoord(buttonCord);\n                button->setSize(buttonSize);\n\n                top += buttonSize.height + buttonTopPadding;\n            }\n\n            mainWidgetSize.height = textPadding + textSize.height + textButtonPadding + buttonsHeight + buttonMainPadding;\n            mMainWidget->setSize(mainWidgetSize +\n                                 // To account for borders\n                                 (mMainWidget->getSize() - mMainWidget->getClientWidget()->getSize()));\n\n            MyGUI::IntPoint absPos;\n            absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2;\n            absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2;\n\n            mMainWidget->setPosition(absPos);\n\n            MyGUI::IntCoord messageWidgetCoord;\n            messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;\n            messageWidgetCoord.top = textPadding;\n            messageWidgetCoord.width = textSize.width;\n            messageWidgetCoord.height = textSize.height;\n            mMessageWidget->setCoord(messageWidgetCoord);\n        }\n\n        setVisible(true);\n    }\n\n    MyGUI::Widget* InteractiveMessageBox::getDefaultKeyFocus()\n    {\n        std::vector<std::string> keywords { \"sOk\", \"sYes\" };\n        for(MyGUI::Button* button : mButtons)\n        {\n            for (const std::string& keyword : keywords)\n            {\n                if(Misc::StringUtils::ciEqual(MyGUI::LanguageManager::getInstance().replaceTags(\"#{\" + keyword + \"}\"), button->getCaption()))\n                {\n                    return button;\n                }\n            }\n        }\n        return nullptr;\n    }\n\n    void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)\n    {\n        buttonActivated (pressed);\n    }\n\n    void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed)\n    {\n        mMarkedToDelete = true;\n        int index = 0;\n        for(const MyGUI::Button* button : mButtons)\n        {\n            if(button == pressed)\n            {\n                mButtonPressed = index;\n                mMessageBoxManager.onButtonPressed(mButtonPressed);\n                return;\n            }\n            index++;\n        }\n    }\n\n    int InteractiveMessageBox::readPressedButton ()\n    {\n        return mButtonPressed;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/messagebox.hpp",
    "content": "#ifndef MWGUI_MESSAGE_BOX_H\n#define MWGUI_MESSAGE_BOX_H\n\n#include \"windowbase.hpp\"\n\n#undef MessageBox\n\nnamespace MyGUI\n{\n    class Widget;\n    class Button;\n    class EditBox;\n}\n\nnamespace MWGui\n{\n    class InteractiveMessageBox;\n    class MessageBoxManager;\n    class MessageBox;\n    class MessageBoxManager\n    {\n        public:\n            MessageBoxManager (float timePerChar);\n            ~MessageBoxManager ();\n            void onFrame (float frameDuration);\n            void createMessageBox (const std::string& message, bool stat = false);\n            void removeStaticMessageBox ();\n            /*\n                Start of tes3mp change (major)\n\n                Add a hasServerOrigin boolean to the list of arguments so those messageboxes\n                can be differentiated from client-only ones\n            */\n            bool createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons, bool hasServerOrigin = false);\n            /*\n                End of tes3mp change (major)\n            */\n            bool isInteractiveMessageBox ();\n\n            int getMessagesCount();\n\n            const InteractiveMessageBox* getInteractiveMessageBox() const { return mInterMessageBoxe; }\n\n            /// Remove all message boxes\n            void clear();\n\n            bool removeMessageBox (MessageBox *msgbox);\n\n            /// @param reset Reset the pressed button to -1 after reading it.\n            int readPressedButton (bool reset=true);\n\n            typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;\n\n            // Note: this delegate unassigns itself after it was fired, i.e. works once.\n            EventHandle_Int eventButtonPressed;\n\n            void onButtonPressed(int button) { eventButtonPressed(button); eventButtonPressed.clear(); }\n\n        private:\n            std::vector<MessageBox*> mMessageBoxes;\n            InteractiveMessageBox* mInterMessageBoxe;\n            MessageBox* mStaticMessageBox;\n            float mMessageBoxSpeed;\n            int mLastButtonPressed;\n    };\n\n    class MessageBox : public Layout\n    {\n        public:\n            MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message);\n            void setMessage (const std::string& message);\n            int getHeight ();\n            void update (int height);\n\n            float mCurrentTime;\n            float mMaxTime;\n\n        protected:\n            MessageBoxManager& mMessageBoxManager;\n            const std::string& mMessage;\n            MyGUI::EditBox* mMessageWidget;\n            int mBottomPadding;\n            int mNextBoxPadding;\n    };\n\n    class InteractiveMessageBox : public WindowModal\n    {\n        public:\n            InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons);\n            void mousePressed (MyGUI::Widget* _widget);\n            int readPressedButton ();\n\n            MyGUI::Widget* getDefaultKeyFocus() override;\n\n            bool exit() override { return false; }\n\n            bool mMarkedToDelete;\n\n            /*\n                Start of tes3mp addition\n\n                Track whether the message box has a server origin\n            */\n            bool mHasServerOrigin = false;\n            /*\n                End of tes3mp addition\n            */\n\n        private:\n            void buttonActivated (MyGUI::Widget* _widget);\n\n            MessageBoxManager& mMessageBoxManager;\n            MyGUI::EditBox* mMessageWidget;\n            MyGUI::Widget* mButtonsWidget;\n            std::vector<MyGUI::Button*> mButtons;\n\n            int mButtonPressed;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/mode.hpp",
    "content": "#ifndef MWGUI_MODE_H\n#define MWGUI_MODE_H\n\nnamespace MWGui\n{\n  enum GuiMode\n    {\n      GM_None,\n      GM_Settings,      // Settings window\n      GM_Inventory,     // Inventory mode\n      GM_Container,\n      GM_Companion,\n      GM_MainMenu,      // Main menu mode\n\n      GM_Journal,       // Journal mode\n\n      GM_Scroll,        // Read scroll\n      GM_Book,          // Read book\n      GM_Alchemy,       // Make potions\n      GM_Repair,\n\n      GM_Dialogue,      // NPC interaction\n      GM_Barter,\n      GM_Rest,\n      GM_SpellBuying,\n      GM_Travel,\n      GM_SpellCreation,\n      GM_Enchanting,\n      GM_Recharge,\n      GM_Training,\n      GM_MerchantRepair,\n\n      GM_Levelup,\n\n      // Startup character creation dialogs\n      GM_Name,\n      GM_Race,\n      GM_Birth,\n      GM_Class,\n      GM_ClassGenerate,\n      GM_ClassPick,\n      GM_ClassCreate,\n      GM_Review,\n      \n      GM_Loading,\n      GM_LoadingWallpaper,\n      GM_Jail,\n\n      GM_QuickKeysMenu\n    };\n\n  // Windows shown in inventory mode\n  enum GuiWindow\n    {\n      GW_None           = 0,\n\n      GW_Map            = 0x01,\n      GW_Inventory      = 0x02,\n      GW_Magic          = 0x04,\n      GW_Stats          = 0x08,\n\n      GW_ALL            = 0xFF\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/pickpocketitemmodel.cpp",
    "content": "#include \"pickpocketitemmodel.hpp\"\n\n#include <components/misc/rng.hpp>\n#include <components/esm/loadskil.hpp>\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/pickpocket.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\nnamespace MWGui\n{\n\n    PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& actor, ItemModel *sourceModel, bool hideItems)\n        : mActor(actor), mPickpocketDetected(false)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        mSourceModel = sourceModel;\n        float chance = player.getClass().getSkill(player, ESM::Skill::Sneak);\n\n        mSourceModel->update();\n\n        // build list of items that player is unable to find when attempts to pickpocket.\n        if (hideItems)\n        {\n            for (size_t i = 0; i<mSourceModel->getItemCount(); ++i)\n            {\n                if (Misc::Rng::roll0to99() > chance)\n                    mHiddenItems.push_back(mSourceModel->getItem(i));\n            }\n        }\n    }\n\n    bool PickpocketItemModel::allowedToUseItems() const\n    {\n        return false;\n    }\n\n    ItemStack PickpocketItemModel::getItem (ModelIndex index)\n    {\n        if (index < 0)\n            throw std::runtime_error(\"Invalid index supplied\");\n        if (mItems.size() <= static_cast<size_t>(index))\n            throw std::runtime_error(\"Item index out of range\");\n        return mItems[index];\n    }\n\n    size_t PickpocketItemModel::getItemCount()\n    {\n        return mItems.size();\n    }\n\n    void PickpocketItemModel::update()\n    {\n        mSourceModel->update();\n        mItems.clear();\n        for (size_t i = 0; i<mSourceModel->getItemCount(); ++i)\n        {\n            const ItemStack& item = mSourceModel->getItem(i);\n\n            // Bound items may not be stolen\n            if (item.mFlags & ItemStack::Flag_Bound)\n                continue;\n\n            if (std::find(mHiddenItems.begin(), mHiddenItems.end(), item) == mHiddenItems.end()\n                    && item.mType != ItemStack::Type_Equipped)\n                mItems.push_back(item);\n        }\n    }\n\n    void PickpocketItemModel::removeItem (const ItemStack &item, size_t count)\n    {\n        ProxyItemModel::removeItem(item, count);\n    }\n\n    bool PickpocketItemModel::onDropItem(const MWWorld::Ptr &item, int count)\n    {\n        // don't allow \"reverse pickpocket\" (it will be handled by scripts after 1.0)\n        return false;\n    }\n\n    void PickpocketItemModel::onClose()\n    {\n        // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened)\n        if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Container)\n        // If it was already detected while taking an item, no need to check now\n                || mPickpocketDetected)\n            return;\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWMechanics::Pickpocket pickpocket(player, mActor);\n        if (pickpocket.finish())\n        {\n            MWBase::Environment::get().getMechanicsManager()->commitCrime(\n                        player, mActor, MWBase::MechanicsManager::OT_Pickpocket, std::string(), 0, true);\n            MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);\n            mPickpocketDetected = true;\n        }\n    }\n\n    bool PickpocketItemModel::onTakeItem(const MWWorld::Ptr &item, int count)\n    {\n        if (mActor.getClass().getCreatureStats(mActor).getKnockedDown())\n            return mSourceModel->onTakeItem(item, count);\n\n        bool success = stealItem(item, count);\n        if (success)\n        {\n            MWWorld::Ptr player = MWMechanics::getPlayer();\n            MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item, mActor, count, false);\n        }\n\n        return success;\n    }\n\n    bool PickpocketItemModel::stealItem(const MWWorld::Ptr &item, int count)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWMechanics::Pickpocket pickpocket(player, mActor);\n        if (pickpocket.pick(item, count))\n        {\n            MWBase::Environment::get().getMechanicsManager()->commitCrime(\n                        player, mActor, MWBase::MechanicsManager::OT_Pickpocket, std::string(), 0, true);\n            MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);\n            mPickpocketDetected = true;\n            return false;\n        }\n        else\n            player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1);\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/pickpocketitemmodel.hpp",
    "content": "#ifndef MWGUI_PICKPOCKET_ITEM_MODEL_H\n#define MWGUI_PICKPOCKET_ITEM_MODEL_H\n\n#include \"itemmodel.hpp\"\n\nnamespace MWGui\n{\n\n    /// @brief The pickpocket item model randomly hides item stacks based on a specified chance. Equipped items are always hidden.\n    class PickpocketItemModel : public ProxyItemModel\n    {\n    public:\n        PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel, bool hideItems=true);\n\n        bool allowedToUseItems() const override;\n        ItemStack getItem (ModelIndex index) override;\n        size_t getItemCount() override;\n        void update() override;\n        void removeItem (const ItemStack& item, size_t count) override;\n        void onClose() override;\n        bool onDropItem(const MWWorld::Ptr &item, int count) override;\n        bool onTakeItem(const MWWorld::Ptr &item, int count) override;\n\n    protected:\n        MWWorld::Ptr mActor;\n        bool mPickpocketDetected;\n        bool stealItem(const MWWorld::Ptr &item, int count);\n\n    private:\n        std::vector<ItemStack> mHiddenItems;\n        std::vector<ItemStack> mItems;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/quickkeysmenu.cpp",
    "content": "#include \"quickkeysmenu.hpp\"\n\n#include <MyGUI_EditBox.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_RenderManager.h>\n\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/quickkeys.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/spellutil.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"itemselection.hpp\"\n#include \"spellview.hpp\"\n#include \"itemwidget.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n\n\nnamespace MWGui\n{\n\n    QuickKeysMenu::QuickKeysMenu()\n        : WindowBase(\"openmw_quickkeys_menu.layout\")\n        , mKey(std::vector<keyData>(10))\n        , mSelected(nullptr)\n        , mActivated(nullptr)\n        , mAssignDialog(nullptr)\n        , mItemSelectionDialog(nullptr)\n        , mMagicSelectionDialog(nullptr)\n\n    {\n        getWidget(mOkButton, \"OKButton\");\n        getWidget(mInstructionLabel, \"InstructionLabel\");\n\n        mMainWidget->setSize(mMainWidget->getWidth(),\n                             mMainWidget->getHeight() +\n                             (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight()));\n\n        mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked);\n        center();\n\n        for (int i = 0; i < 10; ++i)\n        {\n            mKey[i].index = i+1;\n            getWidget(mKey[i].button, \"QuickKey\" + MyGUI::utility::toString(i+1));\n            mKey[i].button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);\n\n            unassign(&mKey[i]);\n        }\n    }\n\n    void QuickKeysMenu::clear()\n    {\n        mActivated = nullptr;\n\n        for (int i=0; i<10; ++i)\n        {\n            unassign(&mKey[i]);\n        }\n    }\n\n    QuickKeysMenu::~QuickKeysMenu()\n    {\n        delete mAssignDialog;\n        delete mItemSelectionDialog;\n        delete mMagicSelectionDialog;\n    }\n\n    void QuickKeysMenu::onOpen()\n    {\n        WindowBase::onOpen();\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);\n\n        // Check if quick keys are still valid\n        for (int i=0; i<10; ++i)\n        {\n            switch (mKey[i].type)\n            {\n                case Type_Unassigned:\n                case Type_HandToHand:\n                case Type_Magic:\n                    break;\n                case Type_Item:\n                case Type_MagicItem:\n                {\n                    MWWorld::Ptr item = *mKey[i].button->getUserData<MWWorld::Ptr>();\n                    // Make sure the item is available and is not broken\n                    if (!item || item.getRefData().getCount() < 1 ||\n                        (item.getClass().hasItemHealth(item) &&\n                        item.getClass().getItemHealth(item) <= 0))\n                    {\n                        // Try searching for a compatible replacement\n                        item = store.findReplacement(mKey[i].id);\n\n                        if (item)\n                            mKey[i].button->setUserData(MWWorld::Ptr(item));\n\n                        break;\n                    }\n                }\n            }\n        }\n    }\n\n    void QuickKeysMenu::unassign(keyData* key)\n    {\n        key->button->clearUserStrings();\n        key->button->setItem(MWWorld::Ptr());\n\n        while (key->button->getChildCount()) // Destroy number label\n            MyGUI::Gui::getInstance().destroyWidget(key->button->getChildAt(0));\n\n        if (key->index == 10)\n        {\n            key->type = Type_HandToHand;\n\n            MyGUI::ImageBox* image = key->button->createWidget<MyGUI::ImageBox>(\"ImageBox\",\n                MyGUI::IntCoord(14, 13, 32, 32), MyGUI::Align::Default);\n\n            image->setImageTexture(\"icons\\\\k\\\\stealth_handtohand.dds\");\n            image->setNeedMouseFocus(false);\n        }\n        else\n        {\n            key->type = Type_Unassigned;\n            key->id = \"\";\n            key->name = \"\";\n\n            MyGUI::TextBox* textBox = key->button->createWidgetReal<MyGUI::TextBox>(\"SandText\",\n                MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);\n\n            textBox->setTextAlign(MyGUI::Align::Center);\n            textBox->setCaption(MyGUI::utility::toString(key->index));\n            textBox->setNeedMouseFocus(false);\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Send a PLAYER_QUICKKEYS packet whenever a key is unassigned, but only if the player\n            is logged in on the server, so as to avoid doing anything doing at startup when all\n            quick keys get unassigned by default\n        */\n        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && !mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys)\n        {\n            mwmp::Main::get().getLocalPlayer()->sendQuickKey(key->index, Type_Unassigned);\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow unassigning an index directly from elsewhere in the code\n    */\n    void QuickKeysMenu::unassignIndex(int index)\n    {\n        unassign(&mKey[index]);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void QuickKeysMenu::onQuickKeyButtonClicked(MyGUI::Widget* sender)\n    {\n        int index = -1;\n        for (int i = 0; i < 10; ++i)\n        {\n            if (sender == mKey[i].button || sender->getParent() == mKey[i].button)\n            {\n                index = i;\n                break;\n            }\n        }\n        assert(index != -1);\n        if (index < 0)\n        {\n            mSelected = nullptr;\n            return;\n        }\n\n        mSelected = &mKey[index];\n\n        // prevent reallocation of zero key from Type_HandToHand\n        if(mSelected->index == 10)\n            return;\n\n        // open assign dialog\n        if (!mAssignDialog)\n            mAssignDialog = new QuickKeysMenuAssign(this);\n\n        mAssignDialog->setVisible(true);\n    }\n\n    void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu);\n    }\n\n    void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender)\n    {\n        if (!mItemSelectionDialog)\n        {\n            mItemSelectionDialog = new ItemSelectionDialog(\"#{sQuickMenu6}\");\n            mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem);\n            mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel);\n        }\n        mItemSelectionDialog->setVisible(true);\n        mItemSelectionDialog->openContainer(MWMechanics::getPlayer());\n        mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems);\n\n        mAssignDialog->setVisible(false);\n    }\n\n    void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender)\n    {\n        if (!mMagicSelectionDialog)\n        {\n            mMagicSelectionDialog = new MagicSelectionDialog(this);\n        }\n        mMagicSelectionDialog->setVisible(true);\n\n        mAssignDialog->setVisible(false);\n    }\n\n    void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender)\n    {\n        unassign(mSelected);\n        mAssignDialog->setVisible(false);\n    }\n\n    void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender)\n    {\n        mAssignDialog->setVisible(false);\n    }\n\n    void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)\n    {\n        assert(mSelected);\n\n        while (mSelected->button->getChildCount()) // Destroy number label\n            MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));\n\n        mSelected->type = Type_Item;\n        mSelected->id = item.getCellRef().getRefId();\n        mSelected->name = item.getClass().getName(item);\n\n        mSelected->button->setItem(item, ItemWidget::Barter);\n        mSelected->button->setUserString(\"ToolTipType\", \"ItemPtr\");\n        mSelected->button->setUserData(item);\n\n        if (mItemSelectionDialog)\n            mItemSelectionDialog->setVisible(false);\n\n        /*\n            Start of tes3mp addition\n\n            Send a PlayerQuickKeys packet whenever a key is assigned to an item\n            by a player, not by a packet received from the server\n        */\n        if (!mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys)\n            mwmp::Main::get().getLocalPlayer()->sendQuickKey(mSelected->index, Type_Item, item.getCellRef().getRefId());\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void QuickKeysMenu::onAssignItemCancel()\n    {\n        mItemSelectionDialog->setVisible(false);\n    }\n\n    void QuickKeysMenu::onAssignMagicItem(MWWorld::Ptr item)\n    {\n        assert(mSelected);\n\n        while (mSelected->button->getChildCount()) // Destroy number label\n            MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));\n\n        mSelected->type = Type_MagicItem;\n        mSelected->id = item.getCellRef().getRefId();\n        mSelected->name = item.getClass().getName(item);\n\n        float scale = 1.f;\n        MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(\"textures\\\\menu_icon_select_magic_magic.dds\");\n        if (texture)\n            scale = texture->getHeight() / 64.f;\n\n        mSelected->button->setFrame(\"textures\\\\menu_icon_select_magic_magic.dds\", MyGUI::IntCoord(0, 0, 44*scale, 44*scale));\n        mSelected->button->setIcon(item);\n\n        mSelected->button->setUserString(\"ToolTipType\", \"ItemPtr\");\n        mSelected->button->setUserData(MWWorld::Ptr(item));\n\n        if (mMagicSelectionDialog)\n            mMagicSelectionDialog->setVisible(false);\n\n        /*\n            Start of tes3mp addition\n\n            Send a PLAYER_QUICKKEYS packet whenever a key is assigned to an item's magic\n        */\n        if (!mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys)\n            mwmp::Main::get().getLocalPlayer()->sendQuickKey(mSelected->index, Type_MagicItem, item.getCellRef().getRefId());\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void QuickKeysMenu::onAssignMagic(const std::string& spellId)\n    {\n        assert(mSelected);\n        while (mSelected->button->getChildCount()) // Destroy number label\n            MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));\n\n        const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore();\n        const ESM::Spell* spell = esmStore.get<ESM::Spell>().find(spellId);\n\n        mSelected->type = Type_Magic;\n        mSelected->id = spellId;\n        mSelected->name = spell->mName;\n\n        mSelected->button->setItem(MWWorld::Ptr());\n        mSelected->button->setUserString(\"ToolTipType\", \"Spell\");\n        mSelected->button->setUserString(\"Spell\", spellId);\n\n        // use the icon of the first effect\n        const ESM::MagicEffect* effect = esmStore.get<ESM::MagicEffect>().find(spell->mEffects.mList.front().mEffectID);\n\n        std::string path = effect->mIcon;\n        int slashPos = path.rfind('\\\\');\n        path.insert(slashPos+1, \"b_\");\n        path = MWBase::Environment::get().getWindowManager()->correctIconPath(path);\n\n        float scale = 1.f;\n        MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(\"textures\\\\menu_icon_select_magic.dds\");\n        if (texture)\n            scale = texture->getHeight() / 64.f;\n\n        mSelected->button->setFrame(\"textures\\\\menu_icon_select_magic.dds\", MyGUI::IntCoord(0, 0, 44*scale, 44*scale));\n        mSelected->button->setIcon(path);\n\n        if (mMagicSelectionDialog)\n            mMagicSelectionDialog->setVisible(false);\n\n        /*\n            Start of tes3mp addition\n\n            Send a PLAYER_QUICKKEYS packet whenever a key is assigned to a spell\n        */\n        if (!mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys)\n            mwmp::Main::get().getLocalPlayer()->sendQuickKey(mSelected->index, Type_Magic, spellId);\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void QuickKeysMenu::onAssignMagicCancel()\n    {\n        mMagicSelectionDialog->setVisible(false);\n    }\n\n    void QuickKeysMenu::updateActivatedQuickKey()\n    {\n        // there is no delayed action, nothing to do.\n        if (!mActivated)\n            return;\n\n        activateQuickKey(mActivated->index);\n    }\n\n    void QuickKeysMenu::activateQuickKey(int index)\n    {\n        assert(index >= 1 && index <= 10);\n\n        keyData *key = &mKey[index-1];\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);\n        const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player);\n\n        // Delay action executing,\n        // if player is busy for now (casting a spell, attacking someone, etc.)\n        bool isDelayNeeded = MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player)\n                || playerStats.getKnockedDown()\n                || playerStats.getHitRecovery();\n\n        bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();\n        bool isReturnNeeded = (!godmode && playerStats.isParalyzed()) || playerStats.isDead();\n\n        if (isReturnNeeded && key->type != Type_Item)\n        {\n            return;\n        }\n        else if (isDelayNeeded && key->type != Type_Item)\n        {\n            mActivated = key;\n            return;\n        }\n        else\n        {\n            mActivated = nullptr;\n        }\n\n        if (key->type == Type_Item || key->type == Type_MagicItem)\n        {\n            MWWorld::Ptr item = *key->button->getUserData<MWWorld::Ptr>();\n\n            MWWorld::ContainerStoreIterator it = store.begin();\n            for (; it != store.end(); ++it)\n            {\n                if (*it == item)\n                    break;\n            }\n            if (it == store.end())\n                item = nullptr;\n\n            // check the item is available and not broken\n            if (!item || item.getRefData().getCount() < 1 ||\n               (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0))\n            {\n                item = store.findReplacement(key->id);\n\n                if (!item || item.getRefData().getCount() < 1)\n                {\n                    MWBase::Environment::get().getWindowManager()->messageBox(\n                        \"#{sQuickMenu5} \" + key->name);\n\n                    return;\n                }\n            }\n\n            if (key->type == Type_Item)\n            {\n                bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();\n                bool isTool = item.getTypeName() == typeid(ESM::Probe).name() ||\n                    item.getTypeName() == typeid(ESM::Lockpick).name();\n\n                // delay weapon switching if player is busy\n                if (isDelayNeeded && (isWeapon || isTool))\n                {\n                    mActivated = key;\n                    return;\n                }\n                else if (isReturnNeeded && (isWeapon || isTool))\n                {\n                    return;\n                }\n\n                /*\n                    Start of tes3mp change (major)\n\n                    Instead of unilaterally using an item, send an ID_PLAYER_ITEM_USE packet and let the server\n                    decide if the item actually gets used\n                */\n\n                /*\n                if (!store.isEquipped(item))\n                    MWBase::Environment::get().getWindowManager()->useItem(item);\n                MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n                // change draw state only if the item is in player's right hand\n                if (rightHand != store.end() && item == *rightHand)\n                {\n                    MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);\n                }\n                */\n\n                bool shouldDraw = isWeapon || isTool;\n                \n                if (!store.isEquipped(item))\n                {\n                    mwmp::Main::get().getLocalPlayer()->sendItemUse(item, false, shouldDraw ? MWMechanics::DrawState_Weapon : MWMechanics::DrawState_Nothing);\n                }\n                /*\n                    End of tes3mp change (major)\n                */\n            }\n            else if (key->type == Type_MagicItem)\n            {\n                // equip, if it can be equipped and isn't yet equipped\n\n                /*\n                    Start of tes3mp change (major)\n\n                    Instead of unilaterally using an item, send an ID_PLAYER_ITEM_USE packet and let the server\n                    decide if the item actually gets used\n                */\n                /*\n                if (!item.getClass().getEquipmentSlots(item).first.empty() && !store.isEquipped(item))\n                {\n                    MWBase::Environment::get().getWindowManager()->useItem(item);\n\n                    // make sure that item was successfully equipped\n                    if (!store.isEquipped(item))\n                        return;\n                }\n                \n                store.setSelectedEnchantItem(it);\n                MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);\n                */\n\n                mwmp::Main::get().getLocalPlayer()->sendItemUse(item, true, MWMechanics::DrawState_Spell);\n                /*\n                    End of tes3mp change (major)\n                */\n            }\n        }\n        else if (key->type == Type_Magic)\n        {\n            std::string spellId = key->id;\n\n            // Make sure the player still has this spell\n            MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n            MWMechanics::Spells& spells = stats.getSpells();\n\n            if (!spells.hasSpell(spellId))\n            {\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sQuickMenu5} \" + key->name);\n                return;\n            }\n\n            store.setSelectedEnchantItem(store.end());\n            MWBase::Environment::get().getWindowManager()\n                ->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));\n            MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);\n\n            /*\n                Start of tes3mp addition\n\n                Send a PlayerMiscellaneous packet with the player's new selected spell\n            */\n            mwmp::Main::get().getLocalPlayer()->sendSelectedSpell(spellId);\n            /*\n                End of tes3mp addition\n            */\n        }\n        else if (key->type == Type_HandToHand)\n        {\n            store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, player);\n            MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);\n        }\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to add quickKeys from elsewhere in the code\n    */\n    void QuickKeysMenu::setSelectedIndex(int index)\n    {\n        mSelected = &mKey[index];\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    // ---------------------------------------------------------------------------------------------------------\n\n    QuickKeysMenuAssign::QuickKeysMenuAssign (QuickKeysMenu* parent)\n        : WindowModal(\"openmw_quickkeys_menu_assign.layout\")\n        , mParent(parent)\n    {\n        getWidget(mLabel, \"Label\");\n        getWidget(mItemButton, \"ItemButton\");\n        getWidget(mMagicButton, \"MagicButton\");\n        getWidget(mUnassignButton, \"UnassignButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n\n        mItemButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onItemButtonClicked);\n        mMagicButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onMagicButtonClicked);\n        mUnassignButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onUnassignButtonClicked);\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onCancelButtonClicked);\n\n\n        int maxWidth = mLabel->getTextSize ().width + 24;\n        maxWidth = std::max(maxWidth, mItemButton->getTextSize ().width + 24);\n        maxWidth = std::max(maxWidth, mMagicButton->getTextSize ().width + 24);\n        maxWidth = std::max(maxWidth, mUnassignButton->getTextSize ().width + 24);\n        maxWidth = std::max(maxWidth, mCancelButton->getTextSize ().width + 24);\n\n        mMainWidget->setSize(maxWidth + 24, mMainWidget->getHeight());\n        mLabel->setSize(maxWidth, mLabel->getHeight());\n\n        mItemButton->setCoord((maxWidth - mItemButton->getTextSize().width-24)/2 + 8,\n                              mItemButton->getTop(),\n                              mItemButton->getTextSize().width + 24,\n                              mItemButton->getHeight());\n        mMagicButton->setCoord((maxWidth - mMagicButton->getTextSize().width-24)/2 + 8,\n                              mMagicButton->getTop(),\n                              mMagicButton->getTextSize().width + 24,\n                              mMagicButton->getHeight());\n        mUnassignButton->setCoord((maxWidth - mUnassignButton->getTextSize().width-24)/2 + 8,\n                              mUnassignButton->getTop(),\n                              mUnassignButton->getTextSize().width + 24,\n                              mUnassignButton->getHeight());\n        mCancelButton->setCoord((maxWidth - mCancelButton->getTextSize().width-24)/2 + 8,\n                              mCancelButton->getTop(),\n                              mCancelButton->getTextSize().width + 24,\n                              mCancelButton->getHeight());\n\n        center();\n    }\n\n    void QuickKeysMenu::write(ESM::ESMWriter &writer)\n    {\n        writer.startRecord(ESM::REC_KEYS);\n\n        ESM::QuickKeys keys;\n\n        // NB: The quick key with index 9 always has Hand-to-Hand type and must not be saved\n        for (int i=0; i<9; ++i)\n        {\n            ItemWidget* button = mKey[i].button;\n\n            int type = mKey[i].type;\n\n            ESM::QuickKeys::QuickKey key;\n            key.mType = type;\n\n            switch (type)\n            {\n                case Type_Unassigned:\n                case Type_HandToHand:\n                    break;\n                case Type_Item:\n                case Type_MagicItem:\n                {\n                    MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();\n                    key.mId = item.getCellRef().getRefId();\n                    break;\n                }\n                case Type_Magic:\n                    std::string spellId = button->getUserString(\"Spell\");\n                    key.mId = spellId;\n                    break;\n            }\n\n            keys.mKeys.push_back(key);\n        }\n\n        keys.save(writer);\n\n        writer.endRecord(ESM::REC_KEYS);\n    }\n\n    void QuickKeysMenu::readRecord(ESM::ESMReader &reader, uint32_t type)\n    {\n        if (type != ESM::REC_KEYS)\n            return;\n\n        ESM::QuickKeys keys;\n        keys.load(reader);\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);\n\n        int i=0;\n        for (ESM::QuickKeys::QuickKey& quickKey : keys.mKeys)\n        {\n            // NB: The quick key with index 9 always has Hand-to-Hand type and must not be loaded\n            if (i >= 9)\n                return;\n\n            mSelected = &mKey[i];\n\n            switch (quickKey.mType)\n            {\n            case Type_Magic:\n                if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(quickKey.mId))\n                    onAssignMagic(quickKey.mId);\n                break;\n            case Type_Item:\n            case Type_MagicItem:\n            {\n                // Find the item by id\n                MWWorld::Ptr item = store.findReplacement(quickKey.mId);\n\n                if (item.isEmpty())\n                    unassign(mSelected);\n                else\n                {\n                    if (quickKey.mType == Type_Item)\n                        onAssignItem(item);\n                    else // if (quickKey.mType == Type_MagicItem)\n                        onAssignMagicItem(item);\n                }\n\n                break;\n            }\n            case Type_Unassigned:\n            case Type_HandToHand:\n                unassign(mSelected);\n                break;\n            }\n\n            ++i;\n        }\n    }\n\n    // ---------------------------------------------------------------------------------------------------------\n\n    MagicSelectionDialog::MagicSelectionDialog(QuickKeysMenu* parent)\n        : WindowModal(\"openmw_magicselection_dialog.layout\")\n        , mParent(parent)\n    {\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mMagicList, \"MagicList\");\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked);\n\n        mMagicList->setShowCostColumn(false);\n        mMagicList->setHighlightSelected(false);\n        mMagicList->eventSpellClicked += MyGUI::newDelegate(this, &MagicSelectionDialog::onModelIndexSelected);\n\n        center();\n    }\n\n    void MagicSelectionDialog::onCancelButtonClicked (MyGUI::Widget *sender)\n    {\n        exit();\n    }\n\n    bool MagicSelectionDialog::exit()\n    {\n        mParent->onAssignMagicCancel();\n        return true;\n    }\n\n    void MagicSelectionDialog::onOpen ()\n    {\n        WindowModal::onOpen();\n\n        mMagicList->setModel(new SpellModel(MWMechanics::getPlayer()));\n        mMagicList->resetScrollbars();\n    }\n\n    void MagicSelectionDialog::onModelIndexSelected(SpellModel::ModelIndex index)\n    {\n        const Spell& spell = mMagicList->getModel()->getItem(index);\n        if (spell.mType == Spell::Type_EnchantedItem)\n            mParent->onAssignMagicItem(spell.mItem);\n        else\n            mParent->onAssignMagic(spell.mId);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/quickkeysmenu.hpp",
    "content": "#ifndef MWGUI_QUICKKEYS_H\n#define MWGUI_QUICKKEYS_H\n\n#include \"windowbase.hpp\"\n\n#include \"spellmodel.hpp\"\n\nnamespace MWGui\n{\n\n    class QuickKeysMenuAssign;\n    class ItemSelectionDialog;\n    class MagicSelectionDialog;\n    class ItemWidget;\n    class SpellView;\n\n    class QuickKeysMenu : public WindowBase\n    {\n    public:\n        QuickKeysMenu();\n        ~QuickKeysMenu();\n\n        void onResChange(int, int) override { center(); }\n\n        void onItemButtonClicked(MyGUI::Widget* sender);\n        void onMagicButtonClicked(MyGUI::Widget* sender);\n        void onUnassignButtonClicked(MyGUI::Widget* sender);\n        void onCancelButtonClicked(MyGUI::Widget* sender);\n\n        void onAssignItem (MWWorld::Ptr item);\n        void onAssignItemCancel ();\n        void onAssignMagicItem (MWWorld::Ptr item);\n        void onAssignMagic (const std::string& spellId);\n        void onAssignMagicCancel ();\n        void onOpen() override;\n\n        void activateQuickKey(int index);\n        void updateActivatedQuickKey();\n\n        /*\n            Start of tes3mp addition\n\n            Allow the setting of the selected index from elsewhere in the code\n        */\n        void setSelectedIndex(int index);\n        /*\n            End of tes3mp addition\n        */\n\n        /// @note This enum is serialized, so don't move the items around!\n        enum QuickKeyType\n        {\n            Type_Item,\n            Type_Magic,\n            Type_MagicItem,\n            Type_Unassigned,\n            Type_HandToHand\n        };\n\n        void write (ESM::ESMWriter& writer);\n        void readRecord (ESM::ESMReader& reader, uint32_t type);\n        void clear() override;\n\n        /*\n            Start of tes3mp addition\n\n            Allow unassigning an index directly from elsewhere in the code\n        */\n        void unassignIndex(int index);\n        /*\n            End of tes3mp addition\n        */\n\n\n    private:\n\n        struct keyData {\n            int index;\n            ItemWidget* button;\n            QuickKeysMenu::QuickKeyType type;\n            std::string id;\n            std::string name;\n            keyData(): index(-1), button(nullptr), type(Type_Unassigned), id(\"\"), name(\"\") {}\n        };\n\n        std::vector<keyData> mKey;\n        keyData* mSelected;\n        keyData* mActivated;\n\n        MyGUI::EditBox* mInstructionLabel;\n        MyGUI::Button* mOkButton;\n\n        QuickKeysMenuAssign* mAssignDialog;\n        ItemSelectionDialog* mItemSelectionDialog;\n        MagicSelectionDialog* mMagicSelectionDialog;\n\n        void onQuickKeyButtonClicked(MyGUI::Widget* sender);\n        void onOkButtonClicked(MyGUI::Widget* sender);\n\n        void unassign(keyData* key);\n    };\n\n    class QuickKeysMenuAssign : public WindowModal\n    {\n    public:\n        QuickKeysMenuAssign(QuickKeysMenu* parent);\n\n    private:\n        MyGUI::TextBox* mLabel;\n        MyGUI::Button* mItemButton;\n        MyGUI::Button* mMagicButton;\n        MyGUI::Button* mUnassignButton;\n        MyGUI::Button* mCancelButton;\n\n        QuickKeysMenu* mParent;\n    };\n\n    class MagicSelectionDialog : public WindowModal\n    {\n    public:\n        MagicSelectionDialog(QuickKeysMenu* parent);\n\n        void onOpen() override;\n        bool exit() override;\n\n    private:\n        MyGUI::Button* mCancelButton;\n        SpellView* mMagicList;\n\n        QuickKeysMenu* mParent;\n\n        void onCancelButtonClicked (MyGUI::Widget* sender);\n        void onModelIndexSelected(SpellModel::ModelIndex index);\n    };\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/race.cpp",
    "content": "#include \"race.hpp\"\n\n#include <MyGUI_ListBox.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_Gui.h>\n\n#include <osg/Texture2D>\n\n#include <components/debug/debuglog.hpp>\n#include <components/myguiplatform/myguitexture.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwrender/characterpreview.hpp\"\n\n#include \"tooltips.hpp\"\n\nnamespace\n{\n    int wrap(int index, int max)\n    {\n        if (index < 0)\n            return max - 1;\n        else if (index >= max)\n            return 0;\n        else\n            return index;\n    }\n\n    bool sortRaces(const std::pair<std::string, std::string>& left, const std::pair<std::string, std::string>& right)\n    {\n        return left.second.compare(right.second) < 0;\n    }\n\n}\n\nnamespace MWGui\n{\n\n    RaceDialog::RaceDialog(osg::Group* parent, Resource::ResourceSystem* resourceSystem)\n      : WindowModal(\"openmw_chargen_race.layout\")\n      , mParent(parent)\n      , mResourceSystem(resourceSystem)\n      , mGenderIndex(0)\n      , mFaceIndex(0)\n      , mHairIndex(0)\n      , mCurrentAngle(0)\n      , mPreviewDirty(true)\n    {\n        // Centre dialog\n        center();\n\n        setText(\"AppearanceT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRaceMenu1\", \"Appearance\"));\n        getWidget(mPreviewImage, \"PreviewImage\");\n\n        getWidget(mHeadRotate, \"HeadRotate\");\n\n        mHeadRotate->setScrollRange(1000);\n        mHeadRotate->setScrollPosition(500);\n        mHeadRotate->setScrollViewPage(50);\n        mHeadRotate->setScrollPage(50);\n        mHeadRotate->setScrollWheelPage(50);\n        mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate);\n\n        // Set up next/previous buttons\n        MyGUI::Button *prevButton, *nextButton;\n\n        setText(\"GenderChoiceT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRaceMenu2\", \"Change Sex\"));\n        getWidget(prevButton, \"PrevGenderButton\");\n        getWidget(nextButton, \"NextGenderButton\");\n        prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender);\n        nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender);\n\n        setText(\"FaceChoiceT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRaceMenu3\", \"Change Face\"));\n        getWidget(prevButton, \"PrevFaceButton\");\n        getWidget(nextButton, \"NextFaceButton\");\n        prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace);\n        nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace);\n\n        setText(\"HairChoiceT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRaceMenu4\", \"Change Hair\"));\n        getWidget(prevButton, \"PrevHairButton\");\n        getWidget(nextButton, \"NextHairButton\");\n        prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair);\n        nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair);\n\n        setText(\"RaceT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRaceMenu5\", \"Race\"));\n        getWidget(mRaceList, \"RaceList\");\n        mRaceList->setScrollVisible(true);\n        mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onAccept);\n        mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);\n\n        setText(\"SkillsT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sBonusSkillTitle\", \"Skill Bonus\"));\n        getWidget(mSkillList, \"SkillList\");\n        setText(\"SpellPowerT\", MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRaceMenu7\", \"Specials\"));\n        getWidget(mSpellPowerList, \"SpellPowerList\");\n\n        MyGUI::Button* backButton;\n        getWidget(backButton, \"BackButton\");\n        backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked);\n\n        /*\n            Start of tes3mp change (major)\n\n            Disable back button here so players can't change their names after logging into\n            their server accounts\n        */\n        backButton->setVisible(false);\n        /*\n            End of tes3mp change (major)\n        */\n\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOK\", \"\"));\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked);\n\n        updateRaces();\n        updateSkills();\n        updateSpellPowers();\n    }\n\n    void RaceDialog::setNextButtonShow(bool shown)\n    {\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n\n        if (shown)\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sNext\", \"\"));\n        else\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOK\", \"\"));\n    }\n\n    void RaceDialog::onOpen()\n    {\n        WindowModal::onOpen();\n\n        updateRaces();\n        updateSkills();\n        updateSpellPowers();\n\n        mPreviewImage->setRenderItemTexture(nullptr);\n\n        mPreview.reset(nullptr);\n        mPreviewTexture.reset(nullptr);\n\n        mPreview.reset(new MWRender::RaceSelectionPreview(mParent, mResourceSystem));\n        mPreview->rebuild();\n        mPreview->setAngle (mCurrentAngle);\n\n        mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));\n        mPreviewImage->setRenderItemTexture(mPreviewTexture.get());\n        mPreviewImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));\n\n        const ESM::NPC& proto = mPreview->getPrototype();\n        setRaceId(proto.mRace);\n        setGender(proto.isMale() ? GM_Male : GM_Female);\n        recountParts();\n\n        for (unsigned int i=0; i<mAvailableHeads.size(); ++i)\n        {\n            if (Misc::StringUtils::ciEqual(mAvailableHeads[i], proto.mHead))\n                mFaceIndex = i;\n        }\n\n        for (unsigned int i=0; i<mAvailableHairs.size(); ++i)\n        {\n            if (Misc::StringUtils::ciEqual(mAvailableHairs[i], proto.mHair))\n                mHairIndex = i;\n        }\n\n        mPreviewDirty = true;\n\n        size_t initialPos = mHeadRotate->getScrollRange()/2+mHeadRotate->getScrollRange()/10;\n        mHeadRotate->setScrollPosition(initialPos);\n        onHeadRotate(mHeadRotate, initialPos);\n\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mRaceList);\n    }\n\n    void RaceDialog::setRaceId(const std::string &raceId)\n    {\n        mCurrentRaceId = raceId;\n        mRaceList->setIndexSelected(MyGUI::ITEM_NONE);\n        size_t count = mRaceList->getItemCount();\n        for (size_t i = 0; i < count; ++i)\n        {\n            if (Misc::StringUtils::ciEqual(*mRaceList->getItemDataAt<std::string>(i), raceId))\n            {\n                mRaceList->setIndexSelected(i);\n                break;\n            }\n        }\n\n        updateSkills();\n        updateSpellPowers();\n    }\n\n    void RaceDialog::onClose()\n    {\n        WindowModal::onClose();\n\n        mPreviewImage->setRenderItemTexture(nullptr);\n\n        mPreviewTexture.reset(nullptr);\n        mPreview.reset(nullptr);\n    }\n\n    // widget controls\n\n    void RaceDialog::onOkClicked(MyGUI::Widget* _sender)\n    {\n        if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE)\n            return;\n        eventDone(this);\n    }\n\n    void RaceDialog::onBackClicked(MyGUI::Widget* _sender)\n    {\n        eventBack();\n    }\n\n    void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position)\n    {\n        float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5f) * osg::PI * 2;\n        mPreview->setAngle (angle);\n\n        mCurrentAngle = angle;\n    }\n\n    void RaceDialog::onSelectPreviousGender(MyGUI::Widget*)\n    {\n        mGenderIndex = wrap(mGenderIndex - 1, 2);\n\n        recountParts();\n        updatePreview();\n    }\n\n    void RaceDialog::onSelectNextGender(MyGUI::Widget*)\n    {\n        mGenderIndex = wrap(mGenderIndex + 1, 2);\n\n        recountParts();\n        updatePreview();\n    }\n\n    void RaceDialog::onSelectPreviousFace(MyGUI::Widget*)\n    {\n        mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size());\n        updatePreview();\n    }\n\n    void RaceDialog::onSelectNextFace(MyGUI::Widget*)\n    {\n        mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size());\n        updatePreview();\n    }\n\n    void RaceDialog::onSelectPreviousHair(MyGUI::Widget*)\n    {\n        mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size());\n        updatePreview();\n    }\n\n    void RaceDialog::onSelectNextHair(MyGUI::Widget*)\n    {\n        mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size());\n        updatePreview();\n    }\n\n    void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index)\n    {\n        if (_index == MyGUI::ITEM_NONE)\n            return;\n\n        const std::string *raceId = mRaceList->getItemDataAt<std::string>(_index);\n        if (Misc::StringUtils::ciEqual(mCurrentRaceId, *raceId))\n            return;\n\n        mCurrentRaceId = *raceId;\n\n        recountParts();\n\n        updatePreview();\n        updateSkills();\n        updateSpellPowers();\n    }\n\n    void RaceDialog::onAccept(MyGUI::ListBox *_sender, size_t _index)\n    {\n        onSelectRace(_sender, _index);\n        if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE)\n            return;\n        eventDone(this);\n    }\n\n    void RaceDialog::getBodyParts (int part, std::vector<std::string>& out)\n    {\n        out.clear();\n        const MWWorld::Store<ESM::BodyPart> &store =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();\n\n        for (const ESM::BodyPart& bodypart : store)\n        {\n            if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)\n                continue;\n            if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)\n                continue;\n            if (bodypart.mData.mPart != static_cast<ESM::BodyPart::MeshPart>(part))\n                continue;\n            if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))\n                continue;\n            bool firstPerson = (bodypart.mId.size() >= 3)\n                    && bodypart.mId[bodypart.mId.size()-3] == '1'\n                    && bodypart.mId[bodypart.mId.size()-2] == 's'\n                    && bodypart.mId[bodypart.mId.size()-1] == 't';\n            if (firstPerson)\n                continue;\n            if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId))\n                out.push_back(bodypart.mId);\n        }\n    }\n\n    void RaceDialog::recountParts()\n    {\n        getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs);\n        getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads);\n\n        mFaceIndex = 0;\n        mHairIndex = 0;\n    }\n\n    // update widget content\n\n    void RaceDialog::updatePreview()\n    {\n        ESM::NPC record = mPreview->getPrototype();\n        record.mRace = mCurrentRaceId;\n        record.setIsMale(mGenderIndex == 0);\n\n        if (mFaceIndex >= 0 && mFaceIndex < int(mAvailableHeads.size()))\n            record.mHead = mAvailableHeads[mFaceIndex];\n\n        if (mHairIndex >= 0 && mHairIndex < int(mAvailableHairs.size()))\n            record.mHair = mAvailableHairs[mHairIndex];\n\n        try\n        {\n            mPreview->setPrototype(record);\n        }\n        catch (std::exception& e)\n        {\n            Log(Debug::Error) << \"Error creating preview: \" << e.what();\n        }\n    }\n\n    void RaceDialog::updateRaces()\n    {\n        mRaceList->removeAllItems();\n\n        const MWWorld::Store<ESM::Race> &races =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>();\n\n        std::vector<std::pair<std::string, std::string> > items; // ID, name\n        for (const ESM::Race& race : races)\n        {\n            bool playable = race.mData.mFlags & ESM::Race::Playable;\n            if (!playable) // Only display playable races\n                continue;\n\n            items.emplace_back(race.mId, race.mName);\n        }\n        std::sort(items.begin(), items.end(), sortRaces);\n\n        int index = 0;\n        for (auto& item : items)\n        {\n            mRaceList->addItem(item.second, item.first);\n            if (Misc::StringUtils::ciEqual(item.first, mCurrentRaceId))\n                mRaceList->setIndexSelected(index);\n            ++index;\n        }\n    }\n\n    void RaceDialog::updateSkills()\n    {\n        for (MyGUI::Widget* widget : mSkillItems)\n        {\n            MyGUI::Gui::getInstance().destroyWidget(widget);\n        }\n        mSkillItems.clear();\n\n        if (mCurrentRaceId.empty())\n            return;\n\n        Widgets::MWSkillPtr skillWidget;\n        const int lineHeight = 18;\n        MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18);\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        const ESM::Race *race = store.get<ESM::Race>().find(mCurrentRaceId);\n        int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE?\n        for (int i = 0; i < count; ++i)\n        {\n            int skillId = race->mData.mBonus[i].mSkill;\n            if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes\n                continue;\n\n            skillWidget = mSkillList->createWidget<Widgets::MWSkill>(\"MW_StatNameValue\", coord1, MyGUI::Align::Default,\n                                                           std::string(\"Skill\") + MyGUI::utility::toString(i));\n            skillWidget->setSkillNumber(skillId);\n            skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(static_cast<float>(race->mData.mBonus[i].mBonus)));\n            ToolTips::createSkillToolTip(skillWidget, skillId);\n\n\n            mSkillItems.push_back(skillWidget);\n\n            coord1.top += lineHeight;\n        }\n    }\n\n    void RaceDialog::updateSpellPowers()\n    {\n        for (MyGUI::Widget* widget : mSpellPowerItems)\n        {\n            MyGUI::Gui::getInstance().destroyWidget(widget);\n        }\n        mSpellPowerItems.clear();\n\n        if (mCurrentRaceId.empty())\n            return;\n\n        const int lineHeight = 18;\n        MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18);\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        const ESM::Race *race = store.get<ESM::Race>().find(mCurrentRaceId);\n\n        int i = 0;\n        for (const std::string& spellpower : race->mPowers.mList)\n        {\n            Widgets::MWSpellPtr spellPowerWidget = mSpellPowerList->createWidget<Widgets::MWSpell>(\"MW_StatName\", coord, MyGUI::Align::Default, std::string(\"SpellPower\") + MyGUI::utility::toString(i));\n            spellPowerWidget->setSpellId(spellpower);\n            spellPowerWidget->setUserString(\"ToolTipType\", \"Spell\");\n            spellPowerWidget->setUserString(\"Spell\", spellpower);\n\n            mSpellPowerItems.push_back(spellPowerWidget);\n\n            coord.top += lineHeight;\n            ++i;\n        }\n    }\n\n    const ESM::NPC& RaceDialog::getResult() const\n    {\n        return mPreview->getPrototype();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/race.hpp",
    "content": "#ifndef MWGUI_RACE_H\n#define MWGUI_RACE_H\n\n#include <memory>\n\n#include \"windowbase.hpp\"\n#include <MyGUI_RenderManager.h>\n\n\nnamespace MWRender\n{\n    class RaceSelectionPreview;\n}\n\nnamespace ESM\n{\n    struct NPC;\n}\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWGui\n{\n    class RaceDialog : public WindowModal\n    {\n    public:\n        RaceDialog(osg::Group* parent, Resource::ResourceSystem* resourceSystem);\n\n        enum Gender\n        {\n            GM_Male,\n            GM_Female\n        };\n\n        const ESM::NPC &getResult() const;\n        const std::string &getRaceId() const { return mCurrentRaceId; }\n        Gender getGender() const { return mGenderIndex == 0 ? GM_Male : GM_Female; }\n\n        void setRaceId(const std::string &raceId);\n        void setGender(Gender gender) { mGenderIndex = gender == GM_Male ? 0 : 1; }\n\n        void setNextButtonShow(bool shown);\n        void onOpen() override;\n        void onClose() override;\n\n        bool exit() override { return false; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n\n        /** Event : Back button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_Void eventBack;\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n    protected:\n        void onHeadRotate(MyGUI::ScrollBar* _sender, size_t _position);\n\n        void onSelectPreviousGender(MyGUI::Widget* _sender);\n        void onSelectNextGender(MyGUI::Widget* _sender);\n\n        void onSelectPreviousFace(MyGUI::Widget* _sender);\n        void onSelectNextFace(MyGUI::Widget* _sender);\n\n        void onSelectPreviousHair(MyGUI::Widget* _sender);\n        void onSelectNextHair(MyGUI::Widget* _sender);\n\n        void onSelectRace(MyGUI::ListBox* _sender, size_t _index);\n        void onAccept(MyGUI::ListBox* _sender, size_t _index);\n\n        void onOkClicked(MyGUI::Widget* _sender);\n        void onBackClicked(MyGUI::Widget* _sender);\n\n    private:\n        void updateRaces();\n        void updateSkills();\n        void updateSpellPowers();\n        void updatePreview();\n        void recountParts();\n\n        void getBodyParts (int part, std::vector<std::string>& out);\n\n        osg::Group* mParent;\n        Resource::ResourceSystem* mResourceSystem;\n\n        std::vector<std::string> mAvailableHeads;\n        std::vector<std::string> mAvailableHairs;\n\n        MyGUI::ImageBox*  mPreviewImage;\n        MyGUI::ListBox*   mRaceList;\n        MyGUI::ScrollBar* mHeadRotate;\n\n        MyGUI::Widget* mSkillList;\n        std::vector<MyGUI::Widget*> mSkillItems;\n\n        MyGUI::Widget* mSpellPowerList;\n        std::vector<MyGUI::Widget*> mSpellPowerItems;\n\n        int mGenderIndex, mFaceIndex, mHairIndex;\n\n        std::string mCurrentRaceId;\n\n        float mCurrentAngle;\n\n        std::unique_ptr<MWRender::RaceSelectionPreview> mPreview;\n        std::unique_ptr<MyGUI::ITexture> mPreviewTexture;\n\n        bool mPreviewDirty;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/recharge.cpp",
    "content": "#include \"recharge.hpp\"\n\n#include <MyGUI_ScrollView.h>\n\n#include <components/widgets/box.hpp>\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/recharge.hpp\"\n\n#include \"itemselection.hpp\"\n#include \"itemwidget.hpp\"\n#include \"itemchargeview.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n#include \"inventoryitemmodel.hpp\"\n\nnamespace MWGui\n{\n\nRecharge::Recharge()\n    : WindowBase(\"openmw_recharge_dialog.layout\")\n    , mItemSelectionDialog(nullptr)\n{\n    getWidget(mBox, \"Box\");\n    getWidget(mGemBox, \"GemBox\");\n    getWidget(mGemIcon, \"GemIcon\");\n    getWidget(mChargeLabel, \"ChargeLabel\");\n    getWidget(mCancelButton, \"CancelButton\");\n\n    mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onCancel);\n    mBox->eventItemClicked += MyGUI::newDelegate(this, &Recharge::onItemClicked);\n\n    mBox->setDisplayMode(ItemChargeView::DisplayMode_EnchantmentCharge);\n\n    mGemIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onSelectItem);\n}\n\nvoid Recharge::onOpen()\n{\n    center();\n\n    SortFilterItemModel * model = new SortFilterItemModel(new InventoryItemModel(MWMechanics::getPlayer()));\n    model->setFilter(SortFilterItemModel::Filter_OnlyRechargable);\n    mBox->setModel(model);\n\n    // Reset scrollbars\n    mBox->resetScrollbars();\n}\n\nvoid Recharge::setPtr (const MWWorld::Ptr &item)\n{\n    mGemIcon->setItem(item);\n    mGemIcon->setUserString(\"ToolTipType\", \"ItemPtr\");\n    mGemIcon->setUserData(MWWorld::Ptr(item));\n\n    updateView();\n}\n\nvoid Recharge::updateView()\n{\n    MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>();\n\n    std::string soul = gem.getCellRef().getSoul();\n    const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul);\n\n    mChargeLabel->setCaptionWithReplacing(\"#{sCharges} \" + MyGUI::utility::toString(creature->mData.mSoul));\n\n    bool toolBoxVisible = (gem.getRefData().getCount() != 0);\n    mGemBox->setVisible(toolBoxVisible);\n    mGemBox->setUserString(\"Hidden\", toolBoxVisible ? \"false\" : \"true\");\n\n    if (!toolBoxVisible)\n    {\n        mGemIcon->setItem(MWWorld::Ptr());\n        mGemIcon->clearUserStrings();\n    }\n\n    mBox->update();\n\n    Gui::Box* box = dynamic_cast<Gui::Box*>(mMainWidget);\n    if (box == nullptr)\n        throw std::runtime_error(\"main widget must be a box\");\n\n    box->notifyChildrenSizeChanged();\n    center();\n}\n\nvoid Recharge::onCancel(MyGUI::Widget *sender)\n{\n    MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Recharge);\n}\n\nvoid Recharge::onSelectItem(MyGUI::Widget *sender)\n{\n    delete mItemSelectionDialog;\n    mItemSelectionDialog = new ItemSelectionDialog(\"#{sSoulGemsWithSouls}\");\n    mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &Recharge::onItemSelected);\n    mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &Recharge::onItemCancel);\n    mItemSelectionDialog->setVisible(true);\n    mItemSelectionDialog->openContainer(MWMechanics::getPlayer());\n    mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones);\n}\n\nvoid Recharge::onItemSelected(MWWorld::Ptr item)\n{\n    mItemSelectionDialog->setVisible(false);\n\n    mGemIcon->setItem(item);\n    mGemIcon->setUserString (\"ToolTipType\", \"ItemPtr\");\n    mGemIcon->setUserData(item);\n\n    MWBase::Environment::get().getWindowManager()->playSound(item.getClass().getDownSoundId(item));\n    updateView();\n}\n\nvoid Recharge::onItemCancel()\n{\n    mItemSelectionDialog->setVisible(false);\n}\n\nvoid Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)\n{\n    MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>();\n    if (!MWMechanics::rechargeItem(item, gem))\n        return;\n\n    updateView();\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/recharge.hpp",
    "content": "#ifndef OPENMW_MWGUI_RECHARGE_H\n#define OPENMW_MWGUI_RECHARGE_H\n\n#include \"windowbase.hpp\"\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWGui\n{\n\nclass ItemSelectionDialog;\nclass ItemWidget;\nclass ItemChargeView;\n\nclass Recharge : public WindowBase\n{\npublic:\n    Recharge();\n\n    void onOpen() override;\n\n    void setPtr (const MWWorld::Ptr& gem) override;\n\nprotected:\n    ItemChargeView* mBox;\n\n    MyGUI::Widget* mGemBox;\n\n    ItemWidget* mGemIcon;\n\n    ItemSelectionDialog* mItemSelectionDialog;\n\n    MyGUI::TextBox* mChargeLabel;\n\n    MyGUI::Button* mCancelButton;\n\n    void updateView();\n\n    void onSelectItem(MyGUI::Widget* sender);\n\n    void onItemSelected(MWWorld::Ptr item);\n    void onItemCancel();\n\n    void onItemClicked (MyGUI::Widget* sender, const MWWorld::Ptr& item);\n    void onCancel (MyGUI::Widget* sender);\n    void onMouseWheel(MyGUI::Widget* _sender, int _rel);\n\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/referenceinterface.cpp",
    "content": "#include \"referenceinterface.hpp\"\n\nnamespace MWGui\n{\n    ReferenceInterface::ReferenceInterface()\n    {\n    }\n\n    ReferenceInterface::~ReferenceInterface()\n    {\n    }\n\n    void ReferenceInterface::checkReferenceAvailable()\n    {\n        // check if count of the reference has become 0\n        if (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)\n        {\n            mPtr = MWWorld::Ptr();\n            onReferenceUnavailable();\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/referenceinterface.hpp",
    "content": "#ifndef MWGUI_REFERENCEINTERFACE_H\n#define MWGUI_REFERENCEINTERFACE_H\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWGui\n{\n    /// \\brief this class is intended for GUI interfaces that access an MW-Reference\n    /// for example dialogue window accesses an NPC, or Container window accesses a Container\n    /// these classes have to be automatically closed if the reference becomes unavailable\n    /// make sure that checkReferenceAvailable() is called every frame and that onReferenceUnavailable() has been overridden\n    class ReferenceInterface\n    {\n    public:\n        ReferenceInterface();\n        virtual ~ReferenceInterface();\n\n        void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable\n\n        virtual void resetReference() { mPtr = MWWorld::Ptr(); }\n\n    protected:\n        virtual void onReferenceUnavailable() = 0; ///< called when reference has become unavailable\n\n        MWWorld::Ptr mPtr;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/repair.cpp",
    "content": "#include \"repair.hpp\"\n\n#include <iomanip>\n\n#include <MyGUI_ScrollView.h>\n\n#include <components/widgets/box.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"itemselection.hpp\"\n#include \"itemwidget.hpp\"\n#include \"itemchargeview.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n#include \"inventoryitemmodel.hpp\"\n\nnamespace MWGui\n{\n\nRepair::Repair()\n    : WindowBase(\"openmw_repair.layout\")\n    , mItemSelectionDialog(nullptr)\n{\n    getWidget(mRepairBox, \"RepairBox\");\n    getWidget(mToolBox, \"ToolBox\");\n    getWidget(mToolIcon, \"ToolIcon\");\n    getWidget(mUsesLabel, \"UsesLabel\");\n    getWidget(mQualityLabel, \"QualityLabel\");\n    getWidget(mCancelButton, \"CancelButton\");\n\n    mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onCancel);\n\n    mRepairBox->eventItemClicked += MyGUI::newDelegate(this, &Repair::onRepairItem);\n    mRepairBox->setDisplayMode(ItemChargeView::DisplayMode_Health);\n\n    mToolIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onSelectItem);\n}\n\nvoid Repair::onOpen()\n{\n    center();\n\n    SortFilterItemModel * model = new SortFilterItemModel(new InventoryItemModel(MWMechanics::getPlayer()));\n    model->setFilter(SortFilterItemModel::Filter_OnlyRepairable);\n    mRepairBox->setModel(model);\n\n    // Reset scrollbars\n    mRepairBox->resetScrollbars();\n}\n\nvoid Repair::setPtr(const MWWorld::Ptr &item)\n{\n    MWBase::Environment::get().getWindowManager()->playSound(\"Item Repair Up\");\n\n    mRepair.setTool(item);\n\n    mToolIcon->setItem(item);\n    mToolIcon->setUserString(\"ToolTipType\", \"ItemPtr\");\n    mToolIcon->setUserData(MWWorld::Ptr(item));\n\n    updateRepairView();\n}\n\nvoid Repair::updateRepairView()\n{\n    MWWorld::LiveCellRef<ESM::Repair> *ref =\n        mRepair.getTool().get<ESM::Repair>();\n\n    int uses = mRepair.getTool().getClass().getItemHealth(mRepair.getTool());\n\n    float quality = ref->mBase->mData.mQuality;\n\n    mToolIcon->setUserData(mRepair.getTool());\n\n    std::stringstream qualityStr;\n    qualityStr << std::setprecision(3) << quality;\n\n    mUsesLabel->setCaptionWithReplacing(\"#{sUses} \" + MyGUI::utility::toString(uses));\n    mQualityLabel->setCaptionWithReplacing(\"#{sQuality} \" + qualityStr.str());\n\n    bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0);\n    mToolBox->setVisible(toolBoxVisible);\n    mToolBox->setUserString(\"Hidden\", toolBoxVisible ? \"false\" : \"true\");\n\n    if (!toolBoxVisible)\n    {\n        mToolIcon->setItem(MWWorld::Ptr());\n        mToolIcon->clearUserStrings();\n    }\n\n    mRepairBox->update();\n\n    Gui::Box* box = dynamic_cast<Gui::Box*>(mMainWidget);\n    if (box == nullptr)\n        throw std::runtime_error(\"main widget must be a box\");\n\n    box->notifyChildrenSizeChanged();\n    center();\n}\n\nvoid Repair::onSelectItem(MyGUI::Widget *sender)\n{\n    delete mItemSelectionDialog;\n    mItemSelectionDialog = new ItemSelectionDialog(\"#{sRepair}\");\n    mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &Repair::onItemSelected);\n    mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &Repair::onItemCancel);\n    mItemSelectionDialog->setVisible(true);\n    mItemSelectionDialog->openContainer(MWMechanics::getPlayer());\n    mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyRepairTools);\n}\n\nvoid Repair::onItemSelected(MWWorld::Ptr item)\n{\n    mItemSelectionDialog->setVisible(false);\n\n    mToolIcon->setItem(item);\n    mToolIcon->setUserString (\"ToolTipType\", \"ItemPtr\");\n    mToolIcon->setUserData(item);\n\n    mRepair.setTool(item);\n\n    MWBase::Environment::get().getWindowManager()->playSound(item.getClass().getDownSoundId(item));\n    updateRepairView();\n}\n\nvoid Repair::onItemCancel()\n{\n    mItemSelectionDialog->setVisible(false);\n}\n\nvoid Repair::onCancel(MyGUI::Widget* /*sender*/)\n{\n    MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Repair);\n}\n\nvoid Repair::onRepairItem(MyGUI::Widget* /*sender*/, const MWWorld::Ptr& ptr)\n{\n    if (!mRepair.getTool().getRefData().getCount())\n        return;\n\n    mRepair.repair(ptr);\n\n    updateRepairView();\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/repair.hpp",
    "content": "#ifndef OPENMW_MWGUI_REPAIR_H\n#define OPENMW_MWGUI_REPAIR_H\n\n#include \"windowbase.hpp\"\n\n#include \"../mwmechanics/repair.hpp\"\n\nnamespace MWGui\n{\n\nclass ItemSelectionDialog;\nclass ItemWidget;\nclass ItemChargeView;\n\nclass Repair : public WindowBase\n{\npublic:\n    Repair();\n\n    void onOpen() override;\n\n    void setPtr (const MWWorld::Ptr& item) override;\n\nprotected:\n    ItemChargeView* mRepairBox;\n\n    MyGUI::Widget* mToolBox;\n\n    ItemWidget* mToolIcon;\n\n    ItemSelectionDialog* mItemSelectionDialog;\n\n    MyGUI::TextBox* mUsesLabel;\n    MyGUI::TextBox* mQualityLabel;\n\n    MyGUI::Button* mCancelButton;\n\n    MWMechanics::Repair mRepair;\n\n    void updateRepairView();\n\n    void onSelectItem(MyGUI::Widget* sender);\n\n    void onItemSelected(MWWorld::Ptr item);\n    void onItemCancel();\n\n    void onRepairItem(MyGUI::Widget* sender, const MWWorld::Ptr& ptr);\n    void onCancel(MyGUI::Widget* sender);\n\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/resourceskin.cpp",
    "content": "#include \"resourceskin.hpp\"\n\n#include <MyGUI_RenderManager.h>\n\n#include <components/misc/stringops.hpp>\n\nnamespace MWGui\n{\n    void resizeSkin(MyGUI::xml::ElementPtr _node)\n    {\n        _node->setAttribute(\"type\", \"ResourceSkin\");\n        const std::string size = _node->findAttribute(\"size\");\n        if (!size.empty())\n            return;\n\n        const std::string textureName = _node->findAttribute(\"texture\");\n        if (textureName.empty())\n            return;\n\n        MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(textureName);\n        if (!texture)\n            return;\n\n        MyGUI::IntCoord coord(0, 0, texture->getWidth(), texture->getHeight());\n        MyGUI::xml::ElementEnumerator basis = _node->getElementEnumerator();\n        const std::string textureSize = std::to_string(coord.width) + \" \" +  std::to_string(coord.height);\n        _node->addAttribute(\"size\", textureSize);\n        while (basis.next())\n        {\n            if (basis->getName() != \"BasisSkin\")\n                continue;\n\n            const std::string basisSkinType = basis->findAttribute(\"type\");\n            if (Misc::StringUtils::ciEqual(basisSkinType, \"SimpleText\"))\n                continue;\n\n            const std::string offset = basis->findAttribute(\"offset\");\n            if (!offset.empty())\n                continue;\n\n            basis->addAttribute(\"offset\", coord);\n\n            MyGUI::xml::ElementEnumerator state = basis->getElementEnumerator();\n            while (state.next())\n            {\n                if (state->getName() == \"State\")\n                {\n                    const std::string stateOffset = state->findAttribute(\"offset\");\n                    if (!stateOffset.empty())\n                        continue;\n\n                    state->addAttribute(\"offset\", coord);\n                    if (Misc::StringUtils::ciEqual(basisSkinType, \"TileRect\"))\n                    {\n                        MyGUI::xml::ElementEnumerator property = state->getElementEnumerator();\n                        bool hasTileSize = false;\n                        while (property.next(\"Property\"))\n                        {\n                            const std::string key = property->findAttribute(\"key\");\n                            if (key != \"TileSize\")\n                                continue;\n\n                            hasTileSize = true;\n                        }\n\n                        if (!hasTileSize)\n                        {\n                            MyGUI::xml::ElementPtr tileSizeProperty = state->createChild(\"Property\");\n                            tileSizeProperty->addAttribute(\"key\", \"TileSize\");\n                            tileSizeProperty->addAttribute(\"value\", textureSize);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    void AutoSizedResourceSkin::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version)\n    {\n        resizeSkin(_node);\n        Base::deserialization(_node, _version);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/resourceskin.hpp",
    "content": "#ifndef MWGUI_RESOURCESKIN_H\n#define MWGUI_RESOURCESKIN_H\n\n#include <MyGUI_ResourceSkin.h>\n\nnamespace MWGui\n{\n    class AutoSizedResourceSkin final : public MyGUI::ResourceSkin\n    {\n        MYGUI_RTTI_DERIVED( AutoSizedResourceSkin )\n\n    public:\n        void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) override;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/review.cpp",
    "content": "#include \"review.hpp\"\n\n#include <cmath>\n\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_Gui.h>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwmechanics/autocalcspell.hpp\"\n\n#include \"tooltips.hpp\"\n\nnamespace\n{\n    void adjustButtonSize(MyGUI::Button *button)\n    {\n        // adjust size of button to fit its text\n        MyGUI::IntSize size = button->getTextSize();\n        button->setSize(size.width + 24, button->getSize().height);\n    }\n}\n\nnamespace MWGui\n{\n    ReviewDialog::ReviewDialog()\n        : WindowModal(\"openmw_chargen_review.layout\"),\n          mUpdateSkillArea(false)\n    {\n        // Centre dialog\n        center();\n\n        // Setup static stats\n        MyGUI::Button* button;\n        getWidget(mNameWidget, \"NameText\");\n        getWidget(button, \"NameButton\");\n        adjustButtonSize(button);\n        button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);\n\n        getWidget(mRaceWidget, \"RaceText\");\n        getWidget(button, \"RaceButton\");\n        adjustButtonSize(button);\n        button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);\n\n        getWidget(mClassWidget, \"ClassText\");\n        getWidget(button, \"ClassButton\");\n        adjustButtonSize(button);\n        button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);\n\n        getWidget(mBirthSignWidget, \"SignText\");\n        getWidget(button, \"SignButton\");\n        adjustButtonSize(button);\n        button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);\n\n        // Setup dynamic stats\n        getWidget(mHealth, \"Health\");\n        mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sHealth\", \"\"));\n        mHealth->setValue(45, 45);\n\n        getWidget(mMagicka, \"Magicka\");\n        mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sMagic\", \"\"));\n        mMagicka->setValue(50, 50);\n\n        getWidget(mFatigue, \"Fatigue\");\n        mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sFatigue\", \"\"));\n        mFatigue->setValue(160, 160);\n\n        // Setup attributes\n\n        Widgets::MWAttributePtr attribute;\n        for (int idx = 0; idx < ESM::Attribute::Length; ++idx)\n        {\n            getWidget(attribute, std::string(\"Attribute\") + MyGUI::utility::toString(idx));\n            mAttributeWidgets.insert(std::make_pair(static_cast<int>(ESM::Attribute::sAttributeIds[idx]), attribute));\n            attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]);\n            attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue());\n        }\n\n        // Setup skills\n        getWidget(mSkillView, \"SkillView\");\n        mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);\n\n        for (int i = 0; i < ESM::Skill::Length; ++i)\n        {\n            mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue()));\n            mSkillWidgetMap.insert(std::make_pair(i, static_cast<MyGUI::TextBox*> (nullptr)));\n        }\n\n        MyGUI::Button* backButton;\n        getWidget(backButton, \"BackButton\");\n        backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked);\n\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked);\n    }\n\n    void ReviewDialog::onOpen()\n    {\n        WindowModal::onOpen();\n        mUpdateSkillArea = true;\n    }\n\n    void ReviewDialog::onFrame(float /*duration*/)\n    {\n        if (mUpdateSkillArea)\n        {\n            updateSkillArea();\n            mUpdateSkillArea = false;\n        }\n    }\n\n    void ReviewDialog::setPlayerName(const std::string &name)\n    {\n        mNameWidget->setCaption(name);\n    }\n\n    void ReviewDialog::setRace(const std::string &raceId)\n    {\n        mRaceId = raceId;\n\n        const ESM::Race *race =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().search(mRaceId);\n        if (race)\n        {\n            ToolTips::createRaceToolTip(mRaceWidget, race);\n            mRaceWidget->setCaption(race->mName);\n        }\n\n        mUpdateSkillArea = true;\n    }\n\n    void ReviewDialog::setClass(const ESM::Class& class_)\n    {\n        mKlass = class_;\n        mClassWidget->setCaption(mKlass.mName);\n        ToolTips::createClassToolTip(mClassWidget, mKlass);\n    }\n\n    void ReviewDialog::setBirthSign(const std::string& signId)\n    {\n        mBirthSignId = signId;\n\n        const ESM::BirthSign *sign =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().search(mBirthSignId);\n        if (sign)\n        {\n            mBirthSignWidget->setCaption(sign->mName);\n            ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId);\n        }\n\n        mUpdateSkillArea = true;\n    }\n\n    void ReviewDialog::setHealth(const MWMechanics::DynamicStat<float>& value)\n    {\n        int current = std::max(0, static_cast<int>(value.getCurrent()));\n        int modified = static_cast<int>(value.getModified());\n\n        mHealth->setValue(current, modified);\n        std::string valStr =  MyGUI::utility::toString(current) + \" / \" + MyGUI::utility::toString(modified);\n        mHealth->setUserString(\"Caption_HealthDescription\", \"#{sHealthDesc}\\n\" + valStr);\n    }\n\n    void ReviewDialog::setMagicka(const MWMechanics::DynamicStat<float>& value)\n    {\n        int current = std::max(0, static_cast<int>(value.getCurrent()));\n        int modified = static_cast<int>(value.getModified());\n\n        mMagicka->setValue(current, modified);\n        std::string valStr =  MyGUI::utility::toString(current) + \" / \" + MyGUI::utility::toString(modified);\n        mMagicka->setUserString(\"Caption_HealthDescription\", \"#{sMagDesc}\\n\" + valStr);\n    }\n\n    void ReviewDialog::setFatigue(const MWMechanics::DynamicStat<float>& value)\n    {\n        int current = static_cast<int>(value.getCurrent());\n        int modified = static_cast<int>(value.getModified());\n\n        mFatigue->setValue(current, modified);\n        std::string valStr =  MyGUI::utility::toString(current) + \" / \" + MyGUI::utility::toString(modified);\n        mFatigue->setUserString(\"Caption_HealthDescription\", \"#{sFatDesc}\\n\" + valStr);\n    }\n\n    void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value)\n    {\n        std::map<int, Widgets::MWAttributePtr>::iterator attr = mAttributeWidgets.find(static_cast<int>(attributeId));\n        if (attr == mAttributeWidgets.end())\n            return;\n\n        if (attr->second->getAttributeValue() != value)\n        {\n            attr->second->setAttributeValue(value);\n            mUpdateSkillArea = true;\n        }\n    }\n\n    void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value)\n    {\n        mSkillValues[skillId] = value;\n        MyGUI::TextBox* widget = mSkillWidgetMap[skillId];\n        if (widget)\n        {\n            float modified = static_cast<float>(value.getModified()), base = static_cast<float>(value.getBase());\n            std::string text = MyGUI::utility::toString(std::floor(modified));\n            std::string state = \"normal\";\n            if (modified > base)\n                state = \"increased\";\n            else if (modified < base)\n                state = \"decreased\";\n\n            widget->setCaption(text);\n            widget->_setWidgetState(state);\n        }\n\n        mUpdateSkillArea = true;\n    }\n\n    void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)\n    {\n        mMajorSkills = major;\n        mMinorSkills = minor;\n\n        // Update misc skills with the remaining skills not in major or minor\n        std::set<int> skillSet;\n        std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin()));\n        std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin()));\n        mMiscSkills.clear();\n        for (const int skill : ESM::Skill::sSkillIds)\n        {\n            if (skillSet.find(skill) == skillSet.end())\n                mMiscSkills.push_back(skill);\n        }\n\n        mUpdateSkillArea = true;\n    }\n\n    void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        MyGUI::ImageBox* separator = mSkillView->createWidget<MyGUI::ImageBox>(\"MW_HLine\", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default);\n        separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);\n\n        mSkillWidgets.push_back(separator);\n\n        coord1.top += separator->getHeight();\n        coord2.top += separator->getHeight();\n    }\n\n    void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        MyGUI::TextBox* groupWidget = mSkillView->createWidget<MyGUI::TextBox>(\"SandBrightText\", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default);\n        groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);\n        groupWidget->setCaption(label);\n        mSkillWidgets.push_back(groupWidget);\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n        coord1.top += lineHeight;\n        coord2.top += lineHeight;\n    }\n\n    MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        MyGUI::TextBox* skillNameWidget;\n        MyGUI::TextBox* skillValueWidget;\n\n        skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>(\"SandText\", coord1, MyGUI::Align::Default);\n        skillNameWidget->setCaption(text);\n        skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);\n\n        skillValueWidget = mSkillView->createWidget<MyGUI::TextBox>(\"SandTextRight\", coord2, MyGUI::Align::Default);\n        skillValueWidget->setCaption(value);\n        skillValueWidget->_setWidgetState(state);\n        skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);\n\n        mSkillWidgets.push_back(skillNameWidget);\n        mSkillWidgets.push_back(skillValueWidget);\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n        coord1.top += lineHeight;\n        coord2.top += lineHeight;\n\n        return skillValueWidget;\n    }\n\n    void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        MyGUI::TextBox* skillNameWidget;\n\n        skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>(\"SandText\", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);\n        skillNameWidget->setCaption(text);\n        skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);\n\n        mSkillWidgets.push_back(skillNameWidget);\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n        coord1.top += lineHeight;\n        coord2.top += lineHeight;\n    }\n\n    void ReviewDialog::addItem(const ESM::Spell* spell, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2)\n    {\n        Widgets::MWSpellPtr widget = mSkillView->createWidget<Widgets::MWSpell>(\"MW_StatName\", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);\n        widget->setSpellId(spell->mId);\n        widget->setUserString(\"ToolTipType\", \"Spell\");\n        widget->setUserString(\"Spell\", spell->mId);\n        widget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);\n\n        mSkillWidgets.push_back(widget);\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n        coord1.top += lineHeight;\n        coord2.top += lineHeight;\n    }\n\n    void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        // Add a line separator if there are items above\n        if (!mSkillWidgets.empty())\n        {\n            addSeparator(coord1, coord2);\n        }\n\n        addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2);\n\n        for (const int& skillId : skills)\n        {\n            if (skillId < 0 || skillId >= ESM::Skill::Length) // Skip unknown skill indexes\n                continue;\n            assert(skillId >= 0 && skillId < ESM::Skill::Length);\n            const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId];\n            const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second;\n            int base = stat.getBase();\n            int modified = stat.getModified();\n\n            std::string state = \"normal\";\n            if (modified > base)\n                state = \"increased\";\n            else if (modified < base)\n                state = \"decreased\";\n            MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), MyGUI::utility::toString(static_cast<int>(modified)), state, coord1, coord2);\n\n            for (int i=0; i<2; ++i)\n            {\n                ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId);\n            }\n\n            mSkillWidgetMap[skillId] = widget;\n        }\n    }\n\n    void ReviewDialog::updateSkillArea()\n    {\n        for (MyGUI::Widget* skillWidget : mSkillWidgets)\n        {\n            MyGUI::Gui::getInstance().destroyWidget(skillWidget);\n        }\n        mSkillWidgets.clear();\n\n        const int valueSize = 40;\n        MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18);\n        MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height);\n\n        if (!mMajorSkills.empty())\n            addSkills(mMajorSkills, \"sSkillClassMajor\", \"Major Skills\", coord1, coord2);\n\n        if (!mMinorSkills.empty())\n            addSkills(mMinorSkills, \"sSkillClassMinor\", \"Minor Skills\", coord1, coord2);\n\n        if (!mMiscSkills.empty())\n            addSkills(mMiscSkills, \"sSkillClassMisc\", \"Misc Skills\", coord1, coord2);\n\n        // starting spells\n        std::vector<std::string> spells;\n\n        const ESM::Race* race = nullptr;\n        if (!mRaceId.empty())\n            race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(mRaceId);\n\n        int skills[ESM::Skill::Length];\n        for (int i=0; i<ESM::Skill::Length; ++i)\n            skills[i] = mSkillValues.find(i)->second.getBase();\n\n        int attributes[ESM::Attribute::Length];\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            attributes[i] = mAttributeWidgets[i]->getAttributeValue().getBase();\n\n        std::vector<std::string> selectedSpells = MWMechanics::autoCalcPlayerSpells(skills, attributes, race);\n        for (std::string& spellId : selectedSpells)\n        {\n            std::string lower = Misc::StringUtils::lowerCase(spellId);\n            if (std::find(spells.begin(), spells.end(), lower) == spells.end())\n                spells.push_back(lower);\n        }\n\n        if (race)\n        {\n            for (const std::string& spellId : race->mPowers.mList)\n            {\n                std::string lower = Misc::StringUtils::lowerCase(spellId);\n                if (std::find(spells.begin(), spells.end(), lower) == spells.end())\n                    spells.push_back(lower);\n            }\n        }\n\n        if (!mBirthSignId.empty())\n        {\n            const ESM::BirthSign* sign = MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().find(mBirthSignId);\n            for (const std::string& spellId : sign->mPowers.mList)\n            {\n                std::string lower = Misc::StringUtils::lowerCase(spellId);\n                if (std::find(spells.begin(), spells.end(), lower) == spells.end())\n                    spells.push_back(lower);\n            }\n        }\n\n        if (!mSkillWidgets.empty())\n            addSeparator(coord1, coord2);\n        addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sTypeAbility\", \"Abilities\"), coord1, coord2);\n        for (std::string& spellId : spells)\n        {\n            const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);\n            if (spell->mData.mType == ESM::Spell::ST_Ability)\n                addItem(spell, coord1, coord2);\n        }\n\n        addSeparator(coord1, coord2);\n        addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sTypePower\", \"Powers\"), coord1, coord2);\n        for (std::string& spellId : spells)\n        {\n            const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);\n            if (spell->mData.mType == ESM::Spell::ST_Power)\n                addItem(spell, coord1, coord2);\n        }\n\n        addSeparator(coord1, coord2);\n        addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sTypeSpell\", \"Spells\"), coord1, coord2);\n        for (std::string& spellId : spells)\n        {\n            const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);\n            if (spell->mData.mType == ESM::Spell::ST_Spell)\n                addItem(spell, coord1, coord2);\n        }\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mSkillView->setVisibleVScroll(false);\n        mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top));\n        mSkillView->setVisibleVScroll(true);\n    }\n\n    // widget controls\n\n    void ReviewDialog::onOkClicked(MyGUI::Widget* _sender)\n    {\n        eventDone(this);\n    }\n\n    void ReviewDialog::onBackClicked(MyGUI::Widget* _sender)\n    {\n        eventBack();\n    }\n\n    void ReviewDialog::onNameClicked(MyGUI::Widget* _sender)\n    {\n        eventActivateDialog(NAME_DIALOG);\n    }\n\n    void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender)\n    {\n        eventActivateDialog(RACE_DIALOG);\n    }\n\n    void ReviewDialog::onClassClicked(MyGUI::Widget* _sender)\n    {\n        eventActivateDialog(CLASS_DIALOG);\n    }\n\n    void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender)\n    {\n        eventActivateDialog(BIRTHSIGN_DIALOG);\n    }\n\n    void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel)\n    {\n        if (mSkillView->getViewOffset().top + _rel*0.3 > 0)\n            mSkillView->setViewOffset(MyGUI::IntPoint(0, 0));\n        else\n            mSkillView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mSkillView->getViewOffset().top + _rel*0.3)));\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/review.hpp",
    "content": "#ifndef MWGUI_REVIEW_H\n#define MWGUI_REVIEW_H\n\n#include <components/esm/attr.hpp>\n#include <components/esm/loadclas.hpp>\n#include \"windowbase.hpp\"\n#include \"widgets.hpp\"\n\nnamespace ESM\n{\n    struct Spell;\n}\n\nnamespace MWGui\n{\n    class ReviewDialog : public WindowModal\n    {\n    public:\n        enum Dialogs {\n            NAME_DIALOG,\n            RACE_DIALOG,\n            CLASS_DIALOG,\n            BIRTHSIGN_DIALOG\n        };\n        typedef std::vector<int> SkillList;\n\n        ReviewDialog();\n\n        bool exit() override { return false; }\n\n        void setPlayerName(const std::string &name);\n        void setRace(const std::string &raceId);\n        void setClass(const ESM::Class& class_);\n        void setBirthSign (const std::string &signId);\n\n        void setHealth(const MWMechanics::DynamicStat<float>& value);\n        void setMagicka(const MWMechanics::DynamicStat<float>& value);\n        void setFatigue(const MWMechanics::DynamicStat<float>& value);\n\n        void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value);\n\n        void configureSkills(const SkillList& major, const SkillList& minor);\n        void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value);\n\n        void onOpen() override;\n\n        void onFrame(float duration) override;\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n        typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;\n\n        /** Event : Back button clicked.\\n\n        signature : void method()\\n\n        */\n        EventHandle_Void eventBack;\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n        EventHandle_Int eventActivateDialog;\n\n    protected:\n        void onOkClicked(MyGUI::Widget* _sender);\n        void onBackClicked(MyGUI::Widget* _sender);\n\n        void onNameClicked(MyGUI::Widget* _sender);\n        void onRaceClicked(MyGUI::Widget* _sender);\n        void onClassClicked(MyGUI::Widget* _sender);\n        void onBirthSignClicked(MyGUI::Widget* _sender);\n\n        void onMouseWheel(MyGUI::Widget* _sender, int _rel);\n\n    private:\n        void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n        void addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n        void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n        MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n        void addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n        void addItem(const ESM::Spell* spell, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n        void updateSkillArea();\n\n        MyGUI::TextBox *mNameWidget, *mRaceWidget, *mClassWidget, *mBirthSignWidget;\n        MyGUI::ScrollView* mSkillView;\n\n        Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue;\n\n        std::map<int, Widgets::MWAttributePtr> mAttributeWidgets;\n\n        SkillList mMajorSkills, mMinorSkills, mMiscSkills;\n        std::map<int, MWMechanics::SkillValue > mSkillValues;\n        std::map<int, MyGUI::TextBox*> mSkillWidgetMap;\n        std::string mName, mRaceId, mBirthSignId;\n        ESM::Class mKlass;\n        std::vector<MyGUI::Widget*> mSkillWidgets; //< Skills and other information\n\n        bool mUpdateSkillArea;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/savegamedialog.cpp",
    "content": "#include \"savegamedialog.hpp\"\n\n#include <sstream>\n#include <iomanip>\n\n#include <MyGUI_ComboBox.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_ListBox.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_LanguageManager.h>\n\n#include <osgDB/ReadFile>\n#include <osg/Texture2D>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/myguiplatform/myguitexture.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include <components/files/memorystream.hpp>\n\n#include \"../mwbase/statemanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwstate/character.hpp\"\n\n#include \"confirmationdialog.hpp\"\n\nnamespace MWGui\n{\n    SaveGameDialog::SaveGameDialog()\n        : WindowModal(\"openmw_savegame_dialog.layout\")\n        , mSaving(true)\n        , mCurrentCharacter(nullptr)\n        , mCurrentSlot(nullptr)\n    {\n        getWidget(mScreenshot, \"Screenshot\");\n        getWidget(mCharacterSelection, \"SelectCharacter\");\n        getWidget(mInfoText, \"InfoText\");\n        getWidget(mOkButton, \"OkButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mDeleteButton, \"DeleteButton\");\n        getWidget(mSaveList, \"SaveList\");\n        getWidget(mSaveNameEdit, \"SaveNameEdit\");\n        getWidget(mSpacer, \"Spacer\");\n        mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);\n        mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked);\n        mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);\n        mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterAccept);\n        mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);\n        mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick);\n        mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);\n        mSaveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &SaveGameDialog::onKeyButtonPressed);\n        mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept);\n        mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged);\n\n        // To avoid accidental deletions\n        mDeleteButton->setNeedKeyFocus(false);\n    }\n\n    void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos)\n    {\n        onSlotSelected(sender, pos);\n        accept();\n    }\n\n    void SaveGameDialog::onSlotMouseClick(MyGUI::ListBox* sender, size_t pos)\n    {\n        onSlotSelected(sender, pos);\n\n        if (pos != MyGUI::ITEM_NONE && MyGUI::InputManager::getInstance().isShiftPressed())\n            confirmDeleteSave();\n    }\n\n    void SaveGameDialog::confirmDeleteSave()\n    {\n        ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();\n        dialog->askForConfirmation(\"#{sMessage3}\");\n        dialog->eventOkClicked.clear();\n        dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotConfirmed);\n        dialog->eventCancelClicked.clear();\n        dialog->eventCancelClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotCancel);\n    }\n\n    void SaveGameDialog::onDeleteSlotConfirmed()\n    {\n        MWBase::Environment::get().getStateManager()->deleteGame (mCurrentCharacter, mCurrentSlot);\n        mSaveList->removeItemAt(mSaveList->getIndexSelected());\n        onSlotSelected(mSaveList, mSaveList->getIndexSelected());\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);\n\n        if (mSaveList->getItemCount() == 0)\n        {\n            size_t previousIndex = mCharacterSelection->getIndexSelected();\n            mCurrentCharacter = nullptr;\n            mCharacterSelection->removeItemAt(previousIndex);\n            if (mCharacterSelection->getItemCount())\n            {\n                size_t nextCharacter = std::min(previousIndex, mCharacterSelection->getItemCount()-1);\n                mCharacterSelection->setIndexSelected(nextCharacter);\n                onCharacterSelected(mCharacterSelection, nextCharacter);\n            }\n            else\n                fillSaveList();\n        }\n    }\n\n    void SaveGameDialog::onDeleteSlotCancel()\n    {\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);\n    }\n\n    void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender)\n    {\n        // This might have previously been a save slot from the list. If so, that is no longer the case\n        mSaveList->setIndexSelected(MyGUI::ITEM_NONE);\n        onSlotSelected(mSaveList, MyGUI::ITEM_NONE);\n    }\n\n    void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender)\n    {\n        accept();\n\n        // To do not spam onEditSelectAccept() again and again\n        MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);\n    }\n\n    void SaveGameDialog::onClose()\n    {\n        mSaveList->setIndexSelected(MyGUI::ITEM_NONE);\n\n        WindowModal::onClose();\n    }\n\n    void SaveGameDialog::onOpen()\n    {\n        WindowModal::onOpen();\n\n        mSaveNameEdit->setCaption (\"\");\n        if (mSaving)\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit);\n        else\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);\n\n        center();\n\n        mCharacterSelection->setCaption(\"\");\n        mCharacterSelection->removeAllItems();\n        mCurrentCharacter = nullptr;\n        mCurrentSlot = nullptr;\n        mSaveList->removeAllItems();\n        onSlotSelected(mSaveList, MyGUI::ITEM_NONE);\n\n        MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();\n        if (mgr->characterBegin() == mgr->characterEnd())\n            return;\n\n        mCurrentCharacter = mgr->getCurrentCharacter();\n\n        std::string directory =\n            Misc::StringUtils::lowerCase (Settings::Manager::getString (\"character\", \"Saves\"));\n\n        size_t selectedIndex = MyGUI::ITEM_NONE;\n\n        for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it)\n        {\n            if (it->begin()!=it->end())\n            {\n                std::stringstream title;\n                title << it->getSignature().mPlayerName;\n\n                // For a custom class, we will not find it in the store (unless we loaded the savegame first).\n                // Fall back to name stored in savegame header in that case.\n                std::string className;\n                if (it->getSignature().mPlayerClassId.empty())\n                    className = it->getSignature().mPlayerClassName;\n                else\n                {\n                    // Find the localised name for this class from the store\n                    const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().search(\n                                it->getSignature().mPlayerClassId);\n                    if (class_)\n                        className = class_->mName;\n                    else\n                        className = \"?\"; // From an older savegame format that did not support custom classes properly.\n                }\n\n                title << \" (#{sLevel} \" << it->getSignature().mPlayerLevel << \" \" << MyGUI::TextIterator::toTagsString(className) << \")\";\n\n                mCharacterSelection->addItem (MyGUI::LanguageManager::getInstance().replaceTags(title.str()));\n\n                if (mCurrentCharacter == &*it ||\n                    (!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase (\n                    it->begin()->mPath.parent_path().filename().string())))\n                {\n                    mCurrentCharacter = &*it;\n                    selectedIndex = mCharacterSelection->getItemCount()-1;\n                }\n            }\n        }\n\n        mCharacterSelection->setIndexSelected(selectedIndex);\n        if (selectedIndex == MyGUI::ITEM_NONE)\n            mCharacterSelection->setCaption(\"Select Character ...\");\n\n        fillSaveList();\n\n    }\n\n    void SaveGameDialog::setLoadOrSave(bool load)\n    {\n        mSaving = !load;\n        mSaveNameEdit->setVisible(!load);\n        mCharacterSelection->setUserString(\"Hidden\", load ? \"false\" : \"true\");\n        mCharacterSelection->setVisible(load);\n        mSpacer->setUserString(\"Hidden\", load ? \"false\" : \"true\");\n\n        mDeleteButton->setUserString(\"Hidden\", load ? \"false\" : \"true\");\n        mDeleteButton->setVisible(load);\n\n        if (!load)\n        {\n            mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter();\n        }\n\n        center();\n    }\n\n    void SaveGameDialog::onCancelButtonClicked(MyGUI::Widget *sender)\n    {\n        setVisible(false);\n    }\n\n    void SaveGameDialog::onDeleteButtonClicked(MyGUI::Widget *sender)\n    {\n        if (mCurrentSlot)\n            confirmDeleteSave();\n    }\n\n    void SaveGameDialog::onConfirmationGiven()\n    {\n        accept(true);\n    }\n\n    void SaveGameDialog::onConfirmationCancel()\n    {\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);\n    }\n\n    void SaveGameDialog::accept(bool reallySure)\n    {\n        if (mSaving)\n        {\n            // If overwriting an existing slot, ask for confirmation first\n            if (mCurrentSlot != nullptr && !reallySure)\n            {\n                ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();\n                dialog->askForConfirmation(\"#{sMessage4}\");\n                dialog->eventOkClicked.clear();\n                dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven);\n                dialog->eventCancelClicked.clear();\n                dialog->eventCancelClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationCancel);\n                return;\n            }\n            if (mSaveNameEdit->getCaption().empty())\n            {\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sNotifyMessage65}\");\n                return;\n            }\n        }\n        else\n        {\n            MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState();\n\n            // If game is running, ask for confirmation first\n            if (state == MWBase::StateManager::State_Running && !reallySure)\n            {\n                ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();\n                dialog->askForConfirmation(\"#{sMessage1}\");\n                dialog->eventOkClicked.clear();\n                dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven);\n                dialog->eventCancelClicked.clear();\n                dialog->eventCancelClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationCancel);\n                return;\n            }\n        }\n\n        setVisible(false);\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu);\n\n        if (mSaving)\n        {\n            MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), mCurrentSlot);\n        }\n        else\n        {\n            assert (mCurrentCharacter && mCurrentSlot);\n            MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot->mPath.string());\n        }\n    }\n\n    void SaveGameDialog::onKeyButtonPressed(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character)\n    {\n        if (key == MyGUI::KeyCode::Delete && mCurrentSlot)\n            confirmDeleteSave();\n    }\n\n    void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender)\n    {\n        accept();\n    }\n\n    void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos)\n    {\n        MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();\n\n        unsigned int i=0;\n        const MWState::Character* character = nullptr;\n        for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i)\n        {\n            if (i == pos)\n                character = &*it;\n        }\n        assert(character && \"Can't find selected character\");\n\n        mCurrentCharacter = character;\n        mCurrentSlot = nullptr;\n        fillSaveList();\n    }\n\n    void SaveGameDialog::onCharacterAccept(MyGUI::ComboBox* sender, size_t pos)\n    {\n        // Give key focus to save list so we can confirm the selection with Enter\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);\n    }\n\n    void SaveGameDialog::fillSaveList()\n    {\n        mSaveList->removeAllItems();\n        if (!mCurrentCharacter)\n            return;\n        for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it)\n        {\n            mSaveList->addItem(it->mProfile.mDescription);\n        }\n        // When loading, Auto-select the first save, if there is one\n        if (mSaveList->getItemCount() && !mSaving)\n        {\n            mSaveList->setIndexSelected(0);\n            onSlotSelected(mSaveList, 0);\n        }\n        else\n            onSlotSelected(mSaveList, MyGUI::ITEM_NONE);\n    }\n\n    std::string formatTimeplayed(const double timeInSeconds)\n    {\n        int timePlayed = (int)floor(timeInSeconds);\n        int days = timePlayed / 60 / 60 / 24;\n        int hours = (timePlayed / 60 / 60) % 24;\n        int minutes = (timePlayed / 60) % 60;\n        int seconds = timePlayed % 60;\n\n        std::stringstream stream;\n        stream << std::setfill('0') << std::setw(2) << days << \":\";\n        stream << std::setfill('0') << std::setw(2) << hours << \":\";\n        stream << std::setfill('0') << std::setw(2) << minutes << \":\";\n        stream << std::setfill('0') << std::setw(2) << seconds;\n        return stream.str();\n    }\n\n    void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos)\n    {\n        mOkButton->setEnabled(pos != MyGUI::ITEM_NONE || mSaving);\n        mDeleteButton->setEnabled(pos != MyGUI::ITEM_NONE);\n\n        if (pos == MyGUI::ITEM_NONE || !mCurrentCharacter)\n        {\n            mCurrentSlot = nullptr;\n            mInfoText->setCaption(\"\");\n            mScreenshot->setImageTexture(\"\");\n            return;\n        }\n\n        if (mSaving)\n            mSaveNameEdit->setCaption(sender->getItemNameAt(pos));\n\n        mCurrentSlot = nullptr;\n        unsigned int i=0;\n        for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i)\n        {\n            if (i == pos)\n                mCurrentSlot = &*it;\n        }\n        if (!mCurrentSlot)\n            throw std::runtime_error(\"Can't find selected slot\");\n\n        std::stringstream text;\n        time_t time = mCurrentSlot->mTimeStamp;\n        struct tm* timeinfo;\n        timeinfo = localtime(&time);\n\n        text << std::put_time(timeinfo, \"%Y.%m.%d %T\") << \"\\n\";\n\n        text << \"#{sLevel} \" << mCurrentSlot->mProfile.mPlayerLevel << \"\\n\";\n        text << \"#{sCell=\" << mCurrentSlot->mProfile.mPlayerCell << \"}\\n\";\n\n        int hour = int(mCurrentSlot->mProfile.mInGameTime.mGameHour);\n        bool pm = hour >= 12;\n        if (hour >= 13) hour -= 12;\n        if (hour == 0) hour = 12;\n\n        text\n            << mCurrentSlot->mProfile.mInGameTime.mDay << \" \"\n            << MWBase::Environment::get().getWorld()->getMonthName(mCurrentSlot->mProfile.mInGameTime.mMonth)\n            <<  \" \" << hour << \" \" << (pm ? \"#{sSaveMenuHelp05}\" : \"#{sSaveMenuHelp04}\");\n\n        if (Settings::Manager::getBool(\"timeplayed\",\"Saves\"))\n        {\n            text << \"\\n\" << \"Time played: \" << formatTimeplayed(mCurrentSlot->mProfile.mTimePlayed);\n        }\n\n        mInfoText->setCaptionWithReplacing(text.str());\n\n\n        // Decode screenshot\n        const std::vector<char>& data = mCurrentSlot->mProfile.mScreenshot;\n        Files::IMemStream instream (&data[0], data.size());\n\n        osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"jpg\");\n        if (!readerwriter)\n        {\n            Log(Debug::Error) << \"Error: Can't open savegame screenshot, no jpg readerwriter found\";\n            return;\n        }\n\n        osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(instream);\n        if (!result.success())\n        {\n            Log(Debug::Error) << \"Error: Failed to read savegame screenshot: \" << result.message() << \" code \" << result.status();\n            return;\n        }\n\n        osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);\n        texture->setImage(result.getImage());\n        texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n        texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n        texture->setResizeNonPowerOfTwoHint(false);\n        texture->setUnRefImageDataAfterApply(true);\n\n        mScreenshotTexture.reset(new osgMyGUI::OSGTexture(texture));\n\n        mScreenshot->setRenderItemTexture(mScreenshotTexture.get());\n        mScreenshot->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/savegamedialog.hpp",
    "content": "#ifndef OPENMW_MWGUI_SAVEGAMEDIALOG_H\n#define OPENMW_MWGUI_SAVEGAMEDIALOG_H\n\n#include <memory>\n\n#include \"windowbase.hpp\"\n\nnamespace MWState\n{\n    class Character;\n    struct Slot;\n}\n\nnamespace MWGui\n{\n\n    class SaveGameDialog : public MWGui::WindowModal\n    {\n    public:\n        SaveGameDialog();\n\n        void onOpen() override;\n        void onClose() override;\n\n        void setLoadOrSave(bool load);\n\n    private:\n        void confirmDeleteSave();\n\n        void onKeyButtonPressed(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character);\n        void onCancelButtonClicked (MyGUI::Widget* sender);\n        void onOkButtonClicked (MyGUI::Widget* sender);\n        void onDeleteButtonClicked (MyGUI::Widget* sender);\n        void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);\n        void onCharacterAccept(MyGUI::ComboBox* sender, size_t pos);\n        // Slot selected (mouse click or arrow keys)\n        void onSlotSelected (MyGUI::ListBox* sender, size_t pos);\n        // Slot activated (double click or enter key)\n        void onSlotActivated (MyGUI::ListBox* sender, size_t pos);\n        // Slot clicked with mouse\n        void onSlotMouseClick(MyGUI::ListBox* sender, size_t pos);\n\n        void onDeleteSlotConfirmed();\n        void onDeleteSlotCancel();\n\n        void onEditSelectAccept (MyGUI::EditBox* sender);\n        void onSaveNameChanged (MyGUI::EditBox* sender);\n        void onConfirmationGiven();\n        void onConfirmationCancel();\n\n        void accept(bool reallySure=false);\n\n        void fillSaveList();\n\n        std::unique_ptr<MyGUI::ITexture> mScreenshotTexture;\n        MyGUI::ImageBox* mScreenshot;\n        bool mSaving;\n\n        MyGUI::ComboBox* mCharacterSelection;\n        MyGUI::EditBox* mInfoText;\n        MyGUI::Button* mOkButton;\n        MyGUI::Button* mCancelButton;\n        MyGUI::Button* mDeleteButton;\n        MyGUI::ListBox* mSaveList;\n        MyGUI::EditBox* mSaveNameEdit;\n        MyGUI::Widget* mSpacer;\n\n        const MWState::Character* mCurrentCharacter;\n        const MWState::Slot* mCurrentSlot;\n\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/screenfader.cpp",
    "content": "#include \"screenfader.hpp\"\n\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_Gui.h>\n\nnamespace MWGui\n{\n\n    FadeOp::FadeOp(ScreenFader * fader, float time, float targetAlpha, float delay)\n        : mFader(fader),\n          mRemainingTime(time+delay),\n          mTargetTime(time),\n          mTargetAlpha(targetAlpha),\n          mStartAlpha(0.f),\n          mDelay(delay),\n          mRunning(false)\n    {\n    }\n\n    bool FadeOp::isRunning()\n    {\n        return mRunning;\n    }\n\n    void FadeOp::start()\n    {\n        if (mRunning)\n            return;\n\n        mRemainingTime = mTargetTime + mDelay;\n        mStartAlpha = mFader->getCurrentAlpha();\n        mRunning = true;\n    }\n\n    void FadeOp::update(float dt)\n    {\n        if (!mRunning)\n            return;\n\n        if (mStartAlpha == mTargetAlpha)\n        {\n            finish();\n            return;\n        }\n\n        if (mRemainingTime <= 0)\n        {\n            // Make sure the target alpha is applied\n            mFader->notifyAlphaChanged(mTargetAlpha);\n            finish();\n            return;\n        }\n\n        if (mRemainingTime > mTargetTime)\n        {\n            mRemainingTime -= dt;\n            return;\n        }\n\n        float currentAlpha = mFader->getCurrentAlpha();\n        if (mStartAlpha > mTargetAlpha)\n        {\n            currentAlpha -= dt/mTargetTime * (mStartAlpha-mTargetAlpha);\n            if (currentAlpha < mTargetAlpha)\n                currentAlpha = mTargetAlpha;\n        }\n        else\n        {\n            currentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha);\n            if (currentAlpha > mTargetAlpha)\n                currentAlpha = mTargetAlpha;\n        }\n\n        mFader->notifyAlphaChanged(currentAlpha);\n\n        mRemainingTime -= dt;\n    }\n\n    void FadeOp::finish()\n    {\n        mRunning = false;\n        mFader->notifyOperationFinished();\n    }\n\n    ScreenFader::ScreenFader(const std::string & texturePath, const std::string& layout, const MyGUI::FloatCoord& texCoordOverride)\n        : WindowBase(layout)\n        , mCurrentAlpha(0.f)\n        , mFactor(1.f)\n        , mRepeat(false)\n    {\n        MyGUI::Gui::getInstance().eventFrameStart += MyGUI::newDelegate(this, &ScreenFader::onFrameStart);\n\n        mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());\n\n        MyGUI::ImageBox* imageBox = mMainWidget->castType<MyGUI::ImageBox>(false);\n        if (imageBox)\n        {\n            imageBox->setImageTexture(texturePath);\n            const MyGUI::IntSize imageSize = imageBox->getImageSize();\n            imageBox->setImageCoord(MyGUI::IntCoord(texCoordOverride.left * imageSize.width,\n                                                    texCoordOverride.top * imageSize.height,\n                                                    texCoordOverride.width * imageSize.width,\n                                                    texCoordOverride.height * imageSize.height));\n        }\n    }\n\n    ScreenFader::~ScreenFader()\n    {\n        try\n        {\n            MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart);\n        }\n        catch(const MyGUI::Exception& e)\n        {\n            Log(Debug::Error) << \"Error in the destructor: \" << e.what();\n        }\n    }\n\n    void ScreenFader::onFrameStart(float dt)\n    {\n        if (!mQueue.empty())\n        {\n            if (!mQueue.front()->isRunning())\n                mQueue.front()->start();\n            mQueue.front()->update(dt);\n        }\n    }\n\n    void ScreenFader::applyAlpha()\n    {\n        setVisible(true);\n        mMainWidget->setAlpha(1.f-((1.f-mCurrentAlpha) * mFactor));\n    }\n\n    void ScreenFader::fadeIn(float time, float delay)\n    {\n        queue(time, 1.f, delay);\n    }\n\n    void ScreenFader::fadeOut(const float time, float delay)\n    {\n        queue(time, 0.f, delay);\n    }\n\n    void ScreenFader::fadeTo(const int percent, const float time, float delay)\n    {\n        queue(time, percent/100.f, delay);\n    }\n\n    void ScreenFader::clear()\n    {\n        clearQueue();\n        notifyAlphaChanged(0.f);\n    }\n\n    void ScreenFader::setFactor(float factor)\n    {\n        mFactor = factor;\n        applyAlpha();\n    }\n\n    void ScreenFader::setRepeat(bool repeat)\n    {\n        mRepeat = repeat;\n    }\n\n    void ScreenFader::queue(float time, float targetAlpha, float delay)\n    {\n        if (time < 0.f)\n            return;\n\n        if (time == 0.f && delay == 0.f)\n        {\n            mCurrentAlpha = targetAlpha;\n            applyAlpha();\n            return;\n        }\n\n        mQueue.push_back(FadeOp::Ptr(new FadeOp(this, time, targetAlpha, delay)));\n    }\n\n    bool ScreenFader::isEmpty()\n    {\n        return mQueue.empty();\n    }\n\n    void ScreenFader::clearQueue()\n    {\n        mQueue.clear();\n    }\n\n    void ScreenFader::notifyAlphaChanged(float alpha)\n    {\n        if (mCurrentAlpha == alpha)\n            return;\n\n        mCurrentAlpha = alpha;\n\n        if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f)\n            mMainWidget->setVisible(false);\n        else\n            applyAlpha();\n    }\n\n    void ScreenFader::notifyOperationFinished()\n    {\n        FadeOp::Ptr op = mQueue.front();\n        mQueue.pop_front();\n\n        if (mRepeat)\n            mQueue.push_back(op);\n    }\n\n    float ScreenFader::getCurrentAlpha()\n    {\n        return mCurrentAlpha;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/screenfader.hpp",
    "content": "#ifndef OPENMW_MWGUI_SCREENFADER_H\n#define OPENMW_MWGUI_SCREENFADER_H\n\n#include <deque>\n#include <memory>\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n    class ScreenFader;\n\n    class FadeOp\n    {\n    public:\n        typedef std::shared_ptr<FadeOp> Ptr;\n\n        FadeOp(ScreenFader * fader, float time, float targetAlpha, float delay);\n\n        bool isRunning();\n\n        void start();\n        void update(float dt);\n        void finish();\n\n    private:\n        ScreenFader * mFader;\n        float mRemainingTime;\n        float mTargetTime;\n        float mTargetAlpha;\n        float mStartAlpha;\n        float mDelay;\n        bool mRunning;\n    };\n\n    class ScreenFader : public WindowBase\n    {\n    public:\n        ScreenFader(const std::string & texturePath, const std::string& layout = \"openmw_screen_fader.layout\", const MyGUI::FloatCoord& texCoordOverride = MyGUI::FloatCoord(0,0,1,1));\n        ~ScreenFader();\n\n        void onFrameStart(float dt);\n\n        void fadeIn(const float time, float delay=0);\n        void fadeOut(const float time, float delay=0);\n        void fadeTo(const int percent, const float time, float delay=0);\n\n        void clear() override;\n\n        void setFactor (float factor);\n        void setRepeat(bool repeat);\n\n        void queue(float time, float targetAlpha, float delay);\n        bool isEmpty();\n        void clearQueue();\n\n        void notifyAlphaChanged(float alpha);\n        void notifyOperationFinished();\n        float getCurrentAlpha();\n\n    private:\n        void applyAlpha();\n\n        float mCurrentAlpha;\n        float mFactor;\n\n        bool mRepeat; // repeat queued operations without removing them\n        std::deque<FadeOp::Ptr> mQueue;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/scrollwindow.cpp",
    "content": "#include \"scrollwindow.hpp\"\n\n#include <MyGUI_ScrollView.h>\n\n#include <components/esm/loadbook.hpp>\n#include <components/widgets/imagebutton.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/actiontake.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"formatting.hpp\"\n\nnamespace MWGui\n{\n\n    ScrollWindow::ScrollWindow ()\n        : BookWindowBase(\"openmw_scroll.layout\")\n        , mTakeButtonShow(true)\n        , mTakeButtonAllowed(true)\n    {\n        getWidget(mTextView, \"TextView\");\n\n        getWidget(mCloseButton, \"CloseButton\");\n        mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked);\n\n        getWidget(mTakeButton, \"TakeButton\");\n        mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked);\n\n        adjustButton(\"CloseButton\");\n        adjustButton(\"TakeButton\");\n\n        mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed);\n        mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed);\n\n        center();\n    }\n\n    void ScrollWindow::setPtr (const MWWorld::Ptr& scroll)\n    {\n        mScroll = scroll;\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        bool showTakeButton = scroll.getContainerStore() != &player.getClass().getContainerStore(player);\n\n        MWWorld::LiveCellRef<ESM::Book> *ref = mScroll.get<ESM::Book>();\n\n        Formatting::BookFormatter formatter;\n        formatter.markupToWidget(mTextView, ref->mBase->mText, 390, mTextView->getHeight());\n        MyGUI::IntSize size = mTextView->getChildAt(0)->getSize();\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mTextView->setVisibleVScroll(false);\n        if (size.height > mTextView->getSize().height)\n            mTextView->setCanvasSize(mTextView->getWidth(), size.height);\n        else\n            mTextView->setCanvasSize(mTextView->getWidth(), mTextView->getSize().height);\n        mTextView->setVisibleVScroll(true);\n\n        mTextView->setViewOffset(MyGUI::IntPoint(0,0));\n\n        setTakeButtonShow(showTakeButton);\n\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);\n    }\n\n    void ScrollWindow::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character)\n    {\n        int scroll = 0;\n        if (key == MyGUI::KeyCode::ArrowUp)\n            scroll = 40;\n        else if (key == MyGUI::KeyCode::ArrowDown)\n            scroll = -40;\n\n        if (scroll != 0)\n            mTextView->setViewOffset(mTextView->getViewOffset() + MyGUI::IntPoint(0, scroll));\n    }\n\n    void ScrollWindow::setTakeButtonShow(bool show)\n    {\n        mTakeButtonShow = show;\n        mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);\n    }\n\n    void ScrollWindow::setInventoryAllowed(bool allowed)\n    {\n        mTakeButtonAllowed = allowed;\n        mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);\n    }\n\n    void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll);\n    }\n\n    void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->playSound(\"Item Book Up\");\n\n        MWWorld::ActionTake take(mScroll);\n        take.execute (MWMechanics::getPlayer());\n\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll, true);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/scrollwindow.hpp",
    "content": "#ifndef MWGUI_SCROLLWINDOW_H\n#define MWGUI_SCROLLWINDOW_H\n\n#include \"windowbase.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace Gui\n{\n    class ImageButton;\n}\n\nnamespace MWGui\n{\n    class ScrollWindow : public BookWindowBase\n    {\n        public:\n            ScrollWindow ();\n\n            void setPtr (const MWWorld::Ptr& scroll) override;\n            void setInventoryAllowed(bool allowed);\n\n            void onResChange(int, int) override { center(); }\n\n        protected:\n            void onCloseButtonClicked (MyGUI::Widget* _sender);\n            void onTakeButtonClicked (MyGUI::Widget* _sender);\n            void setTakeButtonShow(bool show);\n            void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character);\n\n        private:\n            Gui::ImageButton* mCloseButton;\n            Gui::ImageButton* mTakeButton;\n            MyGUI::ScrollView* mTextView;\n\n            MWWorld::Ptr mScroll;\n\n            bool mTakeButtonShow;\n            bool mTakeButtonAllowed;\n\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/settingswindow.cpp",
    "content": "#include \"settingswindow.hpp\"\n\n#include <MyGUI_ScrollBar.h>\n#include <MyGUI_Window.h>\n#include <MyGUI_ComboBox.h>\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_TabControl.h>\n\n#include <SDL_video.h>\n\n#include <iomanip>\n#include <numeric>\n#include <array>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/misc/constants.hpp>\n#include <components/widgets/sharedstatebutton.hpp>\n#include <components/settings/settings.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"confirmationdialog.hpp\"\n\nnamespace\n{\n\n    std::string textureMipmappingToStr(const std::string& val)\n    {\n        if (val == \"linear\")  return \"Trilinear\";\n        if (val == \"nearest\") return \"Bilinear\";\n        if (val != \"none\")\n            Log(Debug::Warning) << \"Warning: Invalid texture mipmap option: \"<< val;\n\n        return \"Other\";\n    }\n\n    void parseResolution (int &x, int &y, const std::string& str)\n    {\n        std::vector<std::string> split;\n        Misc::StringUtils::split (str, split, \"@(x\");\n        assert (split.size() >= 2);\n        Misc::StringUtils::trim(split[0]);\n        Misc::StringUtils::trim(split[1]);\n        x = MyGUI::utility::parseInt (split[0]);\n        y = MyGUI::utility::parseInt (split[1]);\n    }\n\n    bool sortResolutions (std::pair<int, int> left, std::pair<int, int> right)\n    {\n        if (left.first == right.first)\n            return left.second > right.second;\n        return left.first > right.first;\n    }\n\n    std::string getAspect (int x, int y)\n    {\n        int gcd = std::gcd (x, y);\n        if (gcd == 0)\n            return std::string();\n\n        int xaspect = x / gcd;\n        int yaspect = y / gcd;\n        // special case: 8 : 5 is usually referred to as 16:10\n        if (xaspect == 8 && yaspect == 5)\n            return \"16 : 10\";\n        return MyGUI::utility::toString(xaspect) + \" : \" + MyGUI::utility::toString(yaspect);\n    }\n\n    const char* checkButtonType = \"CheckButton\";\n    const char* sliderType = \"Slider\";\n\n    std::string getSettingType(MyGUI::Widget* widget)\n    {\n        return widget->getUserString(\"SettingType\");\n    }\n\n    std::string getSettingName(MyGUI::Widget* widget)\n    {\n        return widget->getUserString(\"SettingName\");\n    }\n\n    std::string getSettingCategory(MyGUI::Widget* widget)\n    {\n        return widget->getUserString(\"SettingCategory\");\n    }\n\n    std::string getSettingValueType(MyGUI::Widget* widget)\n    {\n        return widget->getUserString(\"SettingValueType\");\n    }\n\n    void getSettingMinMax(MyGUI::Widget* widget, float& min, float& max)\n    {\n        const char* settingMin = \"SettingMin\";\n        const char* settingMax = \"SettingMax\";\n        min = 0.f;\n        max = 1.f;\n        if (!widget->getUserString(settingMin).empty())\n            min = MyGUI::utility::parseFloat(widget->getUserString(settingMin));\n        if (!widget->getUserString(settingMax).empty())\n            max = MyGUI::utility::parseFloat(widget->getUserString(settingMax));\n    }\n\n    void updateMaxLightsComboBox(MyGUI::ComboBox* box)\n    {\n        constexpr int min = 8;\n        constexpr int max = 32;\n        constexpr int increment = 8;\n        int maxLights = Settings::Manager::getInt(\"max lights\", \"Shaders\");\n        // show increments of 8 in dropdown\n        if (maxLights >= min && maxLights <= max && !(maxLights % increment))\n            box->setIndexSelected((maxLights / increment)-1);\n        else\n            box->setIndexSelected(MyGUI::ITEM_NONE);\n    }\n}\n\nnamespace MWGui\n{\n    void SettingsWindow::configureWidgets(MyGUI::Widget* widget, bool init)\n    {\n        MyGUI::EnumeratorWidgetPtr widgets = widget->getEnumerator();\n        while (widgets.next())\n        {\n            MyGUI::Widget* current = widgets.current();\n\n            std::string type = getSettingType(current);\n            if (type == checkButtonType)\n            {\n                std::string initialValue = Settings::Manager::getBool(getSettingName(current),\n                                                                      getSettingCategory(current))\n                        ? \"#{sOn}\" : \"#{sOff}\";\n                current->castType<MyGUI::Button>()->setCaptionWithReplacing(initialValue);\n                if (init)\n                    current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);\n            }\n            if (type == sliderType)\n            {\n                /*\n                    Start of tes3mp addition\n\n                    Hide difficulty widget because it has no use in multiplayer, with\n                    the difficulty being set by the server instead\n                */\n                if (getSettingName(current) == \"difficulty\")\n                {\n                    widget->setEnabled(false);\n                    widget->setVisible(false);\n                }\n                /*\n                    End of tes3mp addition\n                */\n\n                MyGUI::ScrollBar* scroll = current->castType<MyGUI::ScrollBar>();\n                std::string valueStr;\n                std::string valueType = getSettingValueType(current);\n                if (valueType == \"Float\" || valueType == \"Integer\" || valueType == \"Cell\")\n                {\n                    // TODO: ScrollBar isn't meant for this. should probably use a dedicated FloatSlider widget\n                    float min,max;\n                    getSettingMinMax(scroll, min, max);\n                    float value = Settings::Manager::getFloat(getSettingName(current), getSettingCategory(current));\n\n                    if (valueType == \"Cell\")\n                    {\n                        std::stringstream ss;\n                        ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits;\n                        valueStr = ss.str();\n                    }\n                    else if (valueType == \"Float\")\n                    {\n                        std::stringstream ss;\n                        ss << std::fixed << std::setprecision(2) << value;\n                        valueStr = ss.str();\n                    }\n                    else\n                        valueStr = MyGUI::utility::toString(int(value));\n\n                    value = std::max(min, std::min(value, max));\n                    value = (value-min)/(max-min);\n\n                    scroll->setScrollPosition(static_cast<size_t>(value * (scroll->getScrollRange() - 1)));\n                }\n                else\n                {\n                    int value = Settings::Manager::getInt(getSettingName(current), getSettingCategory(current));\n                    valueStr = MyGUI::utility::toString(value);\n                    scroll->setScrollPosition(value);\n                }\n                if (init)\n                    scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);\n                if (scroll->getVisible())\n                    updateSliderLabel(scroll, valueStr);\n            }\n\n            configureWidgets(current, init);\n        }\n    }\n\n    void SettingsWindow::updateSliderLabel(MyGUI::ScrollBar *scroller, const std::string& value)\n    {\n        std::string labelWidgetName = scroller->getUserString(\"SettingLabelWidget\");\n        if (!labelWidgetName.empty())\n        {\n            MyGUI::TextBox* textBox;\n            getWidget(textBox, labelWidgetName);\n            std::string labelCaption = scroller->getUserString(\"SettingLabelCaption\");\n            labelCaption = Misc::StringUtils::format(labelCaption, value);\n            textBox->setCaptionWithReplacing(labelCaption);\n        }\n    }\n\n    SettingsWindow::SettingsWindow() :\n        WindowBase(\"openmw_settings_window.layout\"),\n        mKeyboardMode(true)\n    {\n        bool terrain = Settings::Manager::getBool(\"distant terrain\", \"Terrain\");\n        const std::string widgetName = terrain ? \"RenderingDistanceSlider\" : \"LargeRenderingDistanceSlider\";\n        MyGUI::Widget* unusedSlider;\n        getWidget(unusedSlider, widgetName);\n        unusedSlider->setVisible(false);\n\n        configureWidgets(mMainWidget, true);\n\n        setTitle(\"#{sOptions}\");\n\n        getWidget(mSettingsTab, \"SettingsTab\");\n        getWidget(mOkButton, \"OkButton\");\n        getWidget(mResolutionList, \"ResolutionList\");\n        getWidget(mFullscreenButton, \"FullscreenButton\");\n        getWidget(mWindowBorderButton, \"WindowBorderButton\");\n        getWidget(mTextureFilteringButton, \"TextureFilteringButton\");\n        getWidget(mAnisotropyBox, \"AnisotropyBox\");\n        getWidget(mControlsBox, \"ControlsBox\");\n        getWidget(mResetControlsButton, \"ResetControlsButton\");\n        getWidget(mKeyboardSwitch, \"KeyboardButton\");\n        getWidget(mControllerSwitch, \"ControllerButton\");\n        getWidget(mWaterTextureSize, \"WaterTextureSize\");\n        getWidget(mWaterReflectionDetail, \"WaterReflectionDetail\");\n        getWidget(mLightingMethodButton, \"LightingMethodButton\");\n        getWidget(mLightsResetButton, \"LightsResetButton\");\n        getWidget(mMaxLights, \"MaxLights\");\n\n#ifndef WIN32\n        // hide gamma controls since it currently does not work under Linux\n        MyGUI::ScrollBar *gammaSlider;\n        getWidget(gammaSlider, \"GammaSlider\");\n        gammaSlider->setVisible(false);\n        MyGUI::TextBox *textBox;\n        getWidget(textBox, \"GammaText\");\n        textBox->setVisible(false);\n        getWidget(textBox, \"GammaTextDark\");\n        textBox->setVisible(false);\n        getWidget(textBox, \"GammaTextLight\");\n        textBox->setVisible(false);\n#endif\n\n        mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &SettingsWindow::onWindowResize);\n\n        mSettingsTab->eventTabChangeSelect += MyGUI::newDelegate(this, &SettingsWindow::onTabChanged);\n        mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked);\n        mTextureFilteringButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onTextureFilteringChanged);\n        mResolutionList->eventListChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onResolutionSelected);\n\n        mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);\n        mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged);\n\n        mLightingMethodButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onLightingMethodButtonChanged);\n        mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked);\n        mMaxLights->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onMaxLightsChanged);\n\n        mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);\n        mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);\n\n        center();\n\n        mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings);\n\n        // fill resolution list\n        int screen = Settings::Manager::getInt(\"screen\", \"Video\");\n        int numDisplayModes = SDL_GetNumDisplayModes(screen);\n        std::vector < std::pair<int, int> > resolutions;\n        for (int i = 0; i < numDisplayModes; i++)\n        {\n            SDL_DisplayMode mode;\n            SDL_GetDisplayMode(screen, i, &mode);\n            resolutions.emplace_back(mode.w, mode.h);\n        }\n        std::sort(resolutions.begin(), resolutions.end(), sortResolutions);\n        for (std::pair<int, int>& resolution : resolutions)\n        {\n            std::string str = MyGUI::utility::toString(resolution.first) + \" x \" + MyGUI::utility::toString(resolution.second);\n            std::string aspect = getAspect(resolution.first, resolution.second);\n            if (!aspect.empty())\n                 str = str + \" (\" + aspect + \")\";\n\n            if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE)\n                mResolutionList->addItem(str);\n        }\n        highlightCurrentResolution();\n\n        std::string tmip = Settings::Manager::getString(\"texture mipmap\", \"General\");\n        mTextureFilteringButton->setCaption(textureMipmappingToStr(tmip));\n\n        int waterTextureSize = Settings::Manager::getInt(\"rtt size\", \"Water\");\n        if (waterTextureSize >= 512)\n            mWaterTextureSize->setIndexSelected(0);\n        if (waterTextureSize >= 1024)\n            mWaterTextureSize->setIndexSelected(1);\n        if (waterTextureSize >= 2048)\n            mWaterTextureSize->setIndexSelected(2);\n\n        int waterReflectionDetail = Settings::Manager::getInt(\"reflection detail\", \"Water\");\n        waterReflectionDetail = std::min(5, std::max(0, waterReflectionDetail));\n        mWaterReflectionDetail->setIndexSelected(waterReflectionDetail);\n\n        updateMaxLightsComboBox(mMaxLights);\n\n        mWindowBorderButton->setEnabled(!Settings::Manager::getBool(\"fullscreen\", \"Video\"));\n\n        mKeyboardSwitch->setStateSelected(true);\n        mControllerSwitch->setStateSelected(false);\n    }\n\n    void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/)\n    {\n        resetScrollbars();\n    }\n\n    void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Settings);\n    }\n\n    void SettingsWindow::onResolutionSelected(MyGUI::ListBox* _sender, size_t index)\n    {\n        if (index == MyGUI::ITEM_NONE)\n            return;\n\n        ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();\n        dialog->askForConfirmation(\"#{sNotifyMessage67}\");\n        dialog->eventOkClicked.clear();\n        dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept);\n        dialog->eventCancelClicked.clear();\n        dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel);\n    }\n\n    void SettingsWindow::onResolutionAccept()\n    {\n        std::string resStr = mResolutionList->getItemNameAt(mResolutionList->getIndexSelected());\n        int resX, resY;\n        parseResolution (resX, resY, resStr);\n\n        Settings::Manager::setInt(\"resolution x\", \"Video\", resX);\n        Settings::Manager::setInt(\"resolution y\", \"Video\", resY);\n\n        apply();\n    }\n\n    void SettingsWindow::onResolutionCancel()\n    {\n        highlightCurrentResolution();\n    }\n\n    void SettingsWindow::highlightCurrentResolution()\n    {\n        mResolutionList->setIndexSelected(MyGUI::ITEM_NONE);\n\n        int currentX = Settings::Manager::getInt(\"resolution x\", \"Video\");\n        int currentY = Settings::Manager::getInt(\"resolution y\", \"Video\");\n\n        for (size_t i=0; i<mResolutionList->getItemCount(); ++i)\n        {\n            int resX, resY;\n            parseResolution (resX, resY, mResolutionList->getItemNameAt(i));\n\n            if (resX == currentX && resY == currentY)\n            {\n                mResolutionList->setIndexSelected(i);\n                break;\n            }\n        }\n    }\n\n    void SettingsWindow::onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos)\n    {\n        int size = 0;\n        if (pos == 0)\n            size = 512;\n        else if (pos == 1)\n            size = 1024;\n        else if (pos == 2)\n            size = 2048;\n        Settings::Manager::setInt(\"rtt size\", \"Water\", size);\n        apply();\n    }\n\n    void SettingsWindow::onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos)\n    {\n        unsigned int level = std::min((unsigned int)5, (unsigned int)pos);\n        Settings::Manager::setInt(\"reflection detail\", \"Water\", level);\n        apply();\n    }\n\n    void SettingsWindow::onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos)\n    {\n        if (pos == MyGUI::ITEM_NONE)\n            return;\n\n        std::string message = \"This change requires a restart to take effect.\";\n        MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, {\"#{sOK}\"}, true);\n\n        Settings::Manager::setString(\"lighting method\", \"Shaders\", _sender->getItemNameAt(pos));\n        apply();\n    }\n\n    void SettingsWindow::onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos)\n    {\n        int count = 8 * (pos + 1);\n\n        Settings::Manager::setInt(\"max lights\", \"Shaders\", count);\n        apply();\n        configureWidgets(mMainWidget, false);\n    }\n\n    void SettingsWindow::onLightsResetButtonClicked(MyGUI::Widget* _sender)\n    {\n        std::vector<std::string> buttons = {\"#{sYes}\", \"#{sNo}\"};\n        std::string message = \"Resets to default values, would you like to continue? Changes to lighting method will require a restart.\";\n        MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons, true);\n        int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton();\n        if (selectedButton == 1 || selectedButton == -1)\n            return;\n\n        constexpr std::array<const char*, 6> settings = {\n            \"light bounds multiplier\",\n            \"maximum light distance\",\n            \"light fade start\",\n            \"minimum interior brightness\",\n            \"max lights\",\n            \"lighting method\",\n        };\n        for (const auto& setting : settings)\n            Settings::Manager::setString(setting, \"Shaders\", Settings::Manager::mDefaultSettings[{\"Shaders\", setting}]);\n\n        mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(Settings::Manager::mDefaultSettings[{\"Shaders\", \"lighting method\"}]));\n        updateMaxLightsComboBox(mMaxLights);\n\n        apply();\n        configureWidgets(mMainWidget, false);\n    }\n\n    void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender)\n    {\n        std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOn\", \"On\");\n        std::string off = MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOff\", \"On\");\n        bool newState;\n        if (_sender->castType<MyGUI::Button>()->getCaption() == on)\n        {\n            _sender->castType<MyGUI::Button>()->setCaption(off);\n            newState = false;\n        }\n        else\n        {\n            _sender->castType<MyGUI::Button>()->setCaption(on);\n            newState = true;\n        }\n\n        if (_sender == mFullscreenButton)\n        {\n            // check if this resolution is supported in fullscreen\n            if (mResolutionList->getIndexSelected() != MyGUI::ITEM_NONE)\n            {\n                std::string resStr = mResolutionList->getItemNameAt(mResolutionList->getIndexSelected());\n                int resX, resY;\n                parseResolution (resX, resY, resStr);\n                Settings::Manager::setInt(\"resolution x\", \"Video\", resX);\n                Settings::Manager::setInt(\"resolution y\", \"Video\", resY);\n            }\n\n            bool supported = false;\n            int fallbackX = 0, fallbackY = 0;\n            for (unsigned int i=0; i<mResolutionList->getItemCount(); ++i)\n            {\n                std::string resStr = mResolutionList->getItemNameAt(i);\n                int resX, resY;\n                parseResolution (resX, resY, resStr);\n\n                if (i == 0)\n                {\n                    fallbackX = resX;\n                    fallbackY = resY;\n                }\n\n                if (resX == Settings::Manager::getInt(\"resolution x\", \"Video\")\n                    && resY  == Settings::Manager::getInt(\"resolution y\", \"Video\"))\n                    supported = true;\n            }\n\n            if (!supported && mResolutionList->getItemCount())\n            {\n                if (fallbackX != 0 && fallbackY != 0)\n                {\n                    Settings::Manager::setInt(\"resolution x\", \"Video\", fallbackX);\n                    Settings::Manager::setInt(\"resolution y\", \"Video\", fallbackY);\n                }\n            }\n\n            mWindowBorderButton->setEnabled(!newState);\n        }\n\n        if (getSettingType(_sender) == checkButtonType)\n        {\n            Settings::Manager::setBool(getSettingName(_sender), getSettingCategory(_sender), newState);\n            apply();\n            return;\n        }\n    }\n\n    void SettingsWindow::onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos)\n    {\n        if(pos == 0)\n            Settings::Manager::setString(\"texture mipmap\", \"General\", \"nearest\");\n        else if(pos == 1)\n            Settings::Manager::setString(\"texture mipmap\", \"General\", \"linear\");\n        else\n            Log(Debug::Warning) << \"Unexpected option pos \" << pos;\n        apply();\n    }\n\n    void SettingsWindow::onSliderChangePosition(MyGUI::ScrollBar* scroller, size_t pos)\n    {\n        if (getSettingType(scroller) == \"Slider\")\n        {\n            std::string valueStr;\n            std::string valueType = getSettingValueType(scroller);\n            if (valueType == \"Float\" || valueType == \"Integer\" || valueType == \"Cell\")\n            {\n                float value = pos / float(scroller->getScrollRange()-1);\n\n                float min,max;\n                getSettingMinMax(scroller, min, max);\n                value = min + (max-min) * value;\n                if (valueType == \"Float\")\n                    Settings::Manager::setFloat(getSettingName(scroller), getSettingCategory(scroller), value);\n                else\n                    Settings::Manager::setInt(getSettingName(scroller), getSettingCategory(scroller), (int)value);\n\n                if (valueType == \"Cell\")\n                {\n                    std::stringstream ss;\n                    ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits;\n                    valueStr = ss.str();\n                }\n                else if (valueType == \"Float\")\n                {\n                    std::stringstream ss;\n                    ss << std::fixed << std::setprecision(2) << value;\n                    valueStr = ss.str();\n                }\n                else\n                    valueStr = MyGUI::utility::toString(int(value));\n            }\n            else\n            {\n                Settings::Manager::setInt(getSettingName(scroller), getSettingCategory(scroller), pos);\n                valueStr = MyGUI::utility::toString(pos);\n            }\n            updateSliderLabel(scroller, valueStr);\n\n            apply();\n        }\n    }\n\n    void SettingsWindow::apply()\n    {\n        const Settings::CategorySettingVector changed = Settings::Manager::getPendingChanges();\n        MWBase::Environment::get().getWorld()->processChangedSettings(changed);\n        MWBase::Environment::get().getSoundManager()->processChangedSettings(changed);\n        MWBase::Environment::get().getWindowManager()->processChangedSettings(changed);\n        MWBase::Environment::get().getInputManager()->processChangedSettings(changed);\n        MWBase::Environment::get().getMechanicsManager()->processChangedSettings(changed);\n        Settings::Manager::resetPendingChanges();\n    }\n\n    void SettingsWindow::onKeyboardSwitchClicked(MyGUI::Widget* _sender)\n    {\n        if(mKeyboardMode)\n            return;\n        mKeyboardMode = true;\n        mKeyboardSwitch->setStateSelected(true);\n        mControllerSwitch->setStateSelected(false);\n        updateControlsBox();\n        resetScrollbars();\n    }\n\n    void SettingsWindow::onControllerSwitchClicked(MyGUI::Widget* _sender)\n    {\n        if(!mKeyboardMode)\n            return;\n        mKeyboardMode = false;\n        mKeyboardSwitch->setStateSelected(false);\n        mControllerSwitch->setStateSelected(true);\n        updateControlsBox();\n        resetScrollbars();\n    }\n\n    void SettingsWindow::updateControlsBox()\n    {\n        while (mControlsBox->getChildCount())\n            MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0));\n\n        MWBase::Environment::get().getWindowManager()->removeStaticMessageBox();\n        std::vector<int> actions;\n        if(mKeyboardMode)\n            actions = MWBase::Environment::get().getInputManager()->getActionKeySorting();\n        else\n            actions = MWBase::Environment::get().getInputManager()->getActionControllerSorting();\n\n        for (const int& action : actions)\n        {\n            std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (action);\n            if (desc == \"\")\n                continue;\n\n            std::string binding;\n            if(mKeyboardMode)\n                binding = MWBase::Environment::get().getInputManager()->getActionKeyBindingName(action);\n            else\n                binding = MWBase::Environment::get().getInputManager()->getActionControllerBindingName(action);\n\n            Gui::SharedStateButton* leftText = mControlsBox->createWidget<Gui::SharedStateButton>(\"SandTextButton\", MyGUI::IntCoord(), MyGUI::Align::Default);\n            leftText->setCaptionWithReplacing(desc);\n\n            Gui::SharedStateButton* rightText = mControlsBox->createWidget<Gui::SharedStateButton>(\"SandTextButton\", MyGUI::IntCoord(), MyGUI::Align::Default);\n            rightText->setCaptionWithReplacing(binding);\n            rightText->setTextAlign (MyGUI::Align::Right);\n            rightText->setUserData(action); // save the action id for callbacks\n            rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction);\n            rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel);\n\n            Gui::ButtonGroup group;\n            group.push_back(leftText);\n            group.push_back(rightText);\n            Gui::SharedStateButton::createButtonGroup(group);\n        }\n\n        layoutControlsBox();\n    }\n\n    void SettingsWindow::updateLightSettings()\n    {\n        auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod();\n        std::string lightingMethodStr = SceneUtil::LightManager::getLightingMethodString(lightingMethod);\n\n        mLightingMethodButton->removeAllItems();\n\n        std::array<SceneUtil::LightingMethod, 3> methods = {\n            SceneUtil::LightingMethod::FFP,\n            SceneUtil::LightingMethod::PerObjectUniform,\n            SceneUtil::LightingMethod::SingleUBO,\n        };\n\n        for (const auto& method : methods)\n        {\n            if (!MWBase::Environment::get().getResourceSystem()->getSceneManager()->isSupportedLightingMethod(method))\n                continue;\n\n            mLightingMethodButton->addItem(SceneUtil::LightManager::getLightingMethodString(method));\n        }\n\n        mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(lightingMethodStr));\n    }\n\n    void SettingsWindow::layoutControlsBox()\n    {\n        const int h = 18;\n        const int w = mControlsBox->getWidth() - 28;\n        const int noWidgetsInRow = 2;\n        const int totalH = mControlsBox->getChildCount() / noWidgetsInRow * h;\n\n        for (size_t i = 0; i < mControlsBox->getChildCount(); i++)\n        {\n            MyGUI::Widget * widget = mControlsBox->getChildAt(i);\n            widget->setCoord(0, i / noWidgetsInRow * h, w, h);\n        }\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mControlsBox->setVisibleVScroll(false);\n        mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(totalH, mControlsBox->getHeight()));\n        mControlsBox->setVisibleVScroll(true);\n    }\n\n    void SettingsWindow::onRebindAction(MyGUI::Widget* _sender)\n    {\n        int actionId = *_sender->getUserData<int>();\n\n        _sender->castType<MyGUI::Button>()->setCaptionWithReplacing(\"#{sNone}\");\n\n        MWBase::Environment::get().getWindowManager ()->staticMessageBox (\"#{sControlsMenu3}\");\n        MWBase::Environment::get().getWindowManager ()->disallowMouse();\n\n        MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId, mKeyboardMode);\n\n    }\n\n    void SettingsWindow::onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel)\n    {\n        if (mControlsBox->getViewOffset().top + _rel*0.3f > 0)\n            mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0));\n        else\n            mControlsBox->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mControlsBox->getViewOffset().top + _rel*0.3f)));\n    }\n\n    void SettingsWindow::onResetDefaultBindings(MyGUI::Widget* _sender)\n    {\n        ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();\n        dialog->askForConfirmation(\"#{sNotifyMessage66}\");\n        dialog->eventOkClicked.clear();\n        dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindingsAccept);\n        dialog->eventCancelClicked.clear();\n    }\n\n    void SettingsWindow::onResetDefaultBindingsAccept()\n    {\n        if(mKeyboardMode)\n            MWBase::Environment::get().getInputManager ()->resetToDefaultKeyBindings ();\n        else\n            MWBase::Environment::get().getInputManager()->resetToDefaultControllerBindings();\n        updateControlsBox ();\n    }\n\n    void SettingsWindow::onOpen()\n    {\n        highlightCurrentResolution();\n        updateControlsBox();\n        updateLightSettings();\n        resetScrollbars();\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);\n    }\n\n    void SettingsWindow::onWindowResize(MyGUI::Window *_sender)\n    {\n        layoutControlsBox();\n    }\n\n    void SettingsWindow::resetScrollbars()\n    {\n        mResolutionList->setScrollPosition(0);\n        mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0));\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/settingswindow.hpp",
    "content": "#ifndef MWGUI_SETTINGS_H\n#define MWGUI_SETTINGS_H\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n    class SettingsWindow : public WindowBase\n    {\n        public:\n            SettingsWindow();\n\n            void onOpen() override;\n\n            void updateControlsBox();\n\n            void updateLightSettings();\n\n            void onResChange(int, int) override { center(); }\n\n    protected:\n            MyGUI::TabControl* mSettingsTab;\n            MyGUI::Button* mOkButton;\n\n            // graphics\n            MyGUI::ListBox* mResolutionList;\n            MyGUI::Button* mFullscreenButton;\n            MyGUI::Button* mWindowBorderButton;\n            MyGUI::ComboBox* mTextureFilteringButton;\n            MyGUI::Widget* mAnisotropyBox;\n\n            MyGUI::ComboBox* mWaterTextureSize;\n            MyGUI::ComboBox* mWaterReflectionDetail;\n\n            MyGUI::ComboBox* mMaxLights;\n            MyGUI::ComboBox* mLightingMethodButton;\n            MyGUI::Button* mLightsResetButton;\n\n            // controls\n            MyGUI::ScrollView* mControlsBox;\n            MyGUI::Button* mResetControlsButton;\n            MyGUI::Button* mKeyboardSwitch;\n            MyGUI::Button* mControllerSwitch;\n            bool mKeyboardMode; //if true, setting up the keyboard. Otherwise, it's controller\n\n            void onTabChanged(MyGUI::TabControl* _sender, size_t index);\n            void onOkButtonClicked(MyGUI::Widget* _sender);\n            void onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos);\n            void onSliderChangePosition(MyGUI::ScrollBar* scroller, size_t pos);\n            void onButtonToggled(MyGUI::Widget* _sender);\n            void onResolutionSelected(MyGUI::ListBox* _sender, size_t index);\n            void onResolutionAccept();\n            void onResolutionCancel();\n            void highlightCurrentResolution();\n\n            void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);\n            void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);\n\n            void onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos);\n            void onLightsResetButtonClicked(MyGUI::Widget* _sender);\n            void onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos);\n\n            void onRebindAction(MyGUI::Widget* _sender);\n            void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);\n            void onResetDefaultBindings(MyGUI::Widget* _sender);\n            void onResetDefaultBindingsAccept ();\n            void onKeyboardSwitchClicked(MyGUI::Widget* _sender);\n            void onControllerSwitchClicked(MyGUI::Widget* _sender);\n\n            void onWindowResize(MyGUI::Window* _sender);\n\n            void apply();\n\n            void configureWidgets(MyGUI::Widget* widget, bool init);\n            void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value);\n\n            void layoutControlsBox();\n        \n        private:\n            void resetScrollbars();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/sortfilteritemmodel.cpp",
    "content": "#include \"sortfilteritemmodel.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/esm/loadalch.hpp>\n#include <components/esm/loadappa.hpp>\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadbook.hpp>\n#include <components/esm/loadclot.hpp>\n#include <components/esm/loadingr.hpp>\n#include <components/esm/loadlock.hpp>\n#include <components/esm/loadligh.hpp>\n#include <components/esm/loadmisc.hpp>\n#include <components/esm/loadprob.hpp>\n#include <components/esm/loadrepa.hpp>\n#include <components/esm/loadweap.hpp>\n#include <components/esm/loadench.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/nullaction.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/alchemy.hpp\"\n\nnamespace\n{\n    bool compareType(const std::string& type1, const std::string& type2)\n    {\n        // this defines the sorting order of types. types that are first in the vector appear before other types.\n        std::vector<std::string> mapping;\n        mapping.emplace_back(typeid(ESM::Weapon).name() );\n        mapping.emplace_back(typeid(ESM::Armor).name() );\n        mapping.emplace_back(typeid(ESM::Clothing).name() );\n        mapping.emplace_back(typeid(ESM::Potion).name() );\n        mapping.emplace_back(typeid(ESM::Ingredient).name() );\n        mapping.emplace_back(typeid(ESM::Apparatus).name() );\n        mapping.emplace_back(typeid(ESM::Book).name() );\n        mapping.emplace_back(typeid(ESM::Light).name() );\n        mapping.emplace_back(typeid(ESM::Miscellaneous).name() );\n        mapping.emplace_back(typeid(ESM::Lockpick).name() );\n        mapping.emplace_back(typeid(ESM::Repair).name() );\n        mapping.emplace_back(typeid(ESM::Probe).name() );\n\n        assert( std::find(mapping.begin(), mapping.end(), type1) != mapping.end() );\n        assert( std::find(mapping.begin(), mapping.end(), type2) != mapping.end() );\n\n        return std::find(mapping.begin(), mapping.end(), type1) < std::find(mapping.begin(), mapping.end(), type2);\n    }\n\n    struct Compare\n    {\n        bool mSortByType;\n        Compare() : mSortByType(true) {}\n        bool operator() (const MWGui::ItemStack& left, const MWGui::ItemStack& right)\n        {\n            if (mSortByType && left.mType != right.mType)\n                return left.mType < right.mType;\n\n            float result = 0;\n\n            // compare items by type\n            std::string leftName = left.mBase.getTypeName();\n            std::string rightName = right.mBase.getTypeName();\n\n            if (leftName != rightName)\n                return compareType(leftName, rightName);\n\n            // compare items by name\n            leftName = Misc::StringUtils::lowerCaseUtf8(left.mBase.getClass().getName(left.mBase));\n            rightName = Misc::StringUtils::lowerCaseUtf8(right.mBase.getClass().getName(right.mBase));\n\n            result = leftName.compare(rightName);\n            if (result != 0)\n                return result < 0;\n\n            // compare items by enchantment:\n            // 1. enchanted items showed before non-enchanted\n            // 2. item with lesser charge percent comes after items with more charge percent\n            // 3. item with constant effect comes before items with non-constant effects\n            int leftChargePercent = -1;\n            int rightChargePercent = -1;\n            leftName = left.mBase.getClass().getEnchantment(left.mBase);\n            rightName = right.mBase.getClass().getEnchantment(right.mBase);\n\n            if (!leftName.empty())\n            {\n                const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(leftName);\n                if (ench)\n                {\n                    if (ench->mData.mType == ESM::Enchantment::ConstantEffect)\n                        leftChargePercent = 101;\n                    else\n                        leftChargePercent = static_cast<int>(left.mBase.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100);\n                }\n            }\n\n            if (!rightName.empty())\n            {\n                const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(rightName);\n                if (ench)\n                {\n                    if (ench->mData.mType == ESM::Enchantment::ConstantEffect)\n                        rightChargePercent = 101;\n                    else\n                        rightChargePercent = static_cast<int>(right.mBase.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100);\n                }\n            }\n\n            result = leftChargePercent - rightChargePercent;\n            if (result != 0)\n                return result > 0;\n\n            // compare items by condition\n            if (left.mBase.getClass().hasItemHealth(left.mBase) && right.mBase.getClass().hasItemHealth(right.mBase))\n            {\n                result = left.mBase.getClass().getItemHealth(left.mBase) - right.mBase.getClass().getItemHealth(right.mBase);\n                if (result != 0)\n                    return result > 0;\n            }\n\n            // compare items by remaining usage time\n            result = left.mBase.getClass().getRemainingUsageTime(left.mBase) - right.mBase.getClass().getRemainingUsageTime(right.mBase);\n            if (result != 0)\n                return result > 0;\n\n            // compare items by value\n            result = left.mBase.getClass().getValue(left.mBase) - right.mBase.getClass().getValue(right.mBase);\n            if (result != 0)\n                return result > 0;\n\n            // compare items by weight\n            result = left.mBase.getClass().getWeight(left.mBase) - right.mBase.getClass().getWeight(right.mBase);\n            if (result != 0)\n                return result > 0;\n\n            // compare items by Id\n            leftName = left.mBase.getCellRef().getRefId();\n            rightName = right.mBase.getCellRef().getRefId();\n\n            result = leftName.compare(rightName);\n            return result < 0;\n        }\n    };\n}\n\nnamespace MWGui\n{\n\n    SortFilterItemModel::SortFilterItemModel(ItemModel *sourceModel)\n        : mCategory(Category_All)\n        , mFilter(0)\n        , mSortByType(true)\n        , mNameFilter(\"\")\n        , mEffectFilter(\"\")\n    {\n        mSourceModel = sourceModel;\n    }\n\n    bool SortFilterItemModel::allowedToUseItems() const\n    {\n        return mSourceModel->allowedToUseItems();\n    }\n\n    void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count)\n    {\n        mDragItems.emplace_back(dragItem, count);\n    }\n\n    void SortFilterItemModel::clearDragItems()\n    {\n        mDragItems.clear();\n    }\n\n    bool SortFilterItemModel::filterAccepts (const ItemStack& item)\n    {\n        MWWorld::Ptr base = item.mBase;\n\n        int category = 0;\n        if (base.getTypeName() == typeid(ESM::Armor).name()\n                || base.getTypeName() == typeid(ESM::Clothing).name())\n            category = Category_Apparel;\n        else if (base.getTypeName() == typeid(ESM::Weapon).name())\n            category = Category_Weapon;\n        else if (base.getTypeName() == typeid(ESM::Ingredient).name()\n                     || base.getTypeName() == typeid(ESM::Potion).name())\n            category = Category_Magic;\n        else if (base.getTypeName() == typeid(ESM::Miscellaneous).name()\n                 || base.getTypeName() == typeid(ESM::Ingredient).name()\n                 || base.getTypeName() == typeid(ESM::Repair).name()\n                 || base.getTypeName() == typeid(ESM::Lockpick).name()\n                 || base.getTypeName() == typeid(ESM::Light).name()\n                 || base.getTypeName() == typeid(ESM::Apparatus).name()\n                 || base.getTypeName() == typeid(ESM::Book).name()\n                 || base.getTypeName() == typeid(ESM::Probe).name())\n            category = Category_Misc;\n\n        if (item.mFlags & ItemStack::Flag_Enchanted)\n            category |= Category_Magic;\n\n        if (!(category & mCategory))\n            return false;\n\n        if (mFilter & Filter_OnlyIngredients)\n        {\n            if (base.getTypeName() != typeid(ESM::Ingredient).name())\n                return false;\n\n            if (!mNameFilter.empty() && !mEffectFilter.empty())\n                throw std::logic_error(\"name and magic effect filter are mutually exclusive\");\n\n            if (!mNameFilter.empty())\n            {\n                const auto itemName = Misc::StringUtils::lowerCaseUtf8(base.getClass().getName(base));\n                return itemName.find(mNameFilter) != std::string::npos;\n            }\n\n            if (!mEffectFilter.empty())\n            {\n                MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n                const auto alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);\n\n                const auto effects = MWMechanics::Alchemy::effectsDescription(base, alchemySkill);\n\n                for (const auto& effect : effects)\n                {\n                    const auto ciEffect = Misc::StringUtils::lowerCaseUtf8(effect);\n\n                    if (ciEffect.find(mEffectFilter) != std::string::npos)\n                        return true;\n                }\n                return false;\n            }\n            return true;\n        }\n\n        if ((mFilter & Filter_OnlyEnchanted) && !(item.mFlags & ItemStack::Flag_Enchanted))\n            return false;\n        if ((mFilter & Filter_OnlyChargedSoulstones) && (base.getTypeName() != typeid(ESM::Miscellaneous).name()\n                                                     || base.getCellRef().getSoul() == \"\" || !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(base.getCellRef().getSoul())))\n            return false;\n        if ((mFilter & Filter_OnlyRepairTools) && (base.getTypeName() != typeid(ESM::Repair).name()))\n            return false;\n        if ((mFilter & Filter_OnlyEnchantable) && (item.mFlags & ItemStack::Flag_Enchanted\n                                               || (base.getTypeName() != typeid(ESM::Armor).name()\n                                                   && base.getTypeName() != typeid(ESM::Clothing).name()\n                                                   && base.getTypeName() != typeid(ESM::Weapon).name()\n                                                   && base.getTypeName() != typeid(ESM::Book).name())))\n            return false;\n        if ((mFilter & Filter_OnlyEnchantable) && base.getTypeName() == typeid(ESM::Book).name()\n                && !base.get<ESM::Book>()->mBase->mData.mIsScroll)\n            return false;\n\n        if ((mFilter & Filter_OnlyUsableItems) && base.getClass().getScript(base).empty())\n        {\n            std::shared_ptr<MWWorld::Action> actionOnUse = base.getClass().use(base);\n            if (!actionOnUse || actionOnUse->isNullAction())\n                return false;\n        }\n\n        if ((mFilter & Filter_OnlyRepairable) && (\n                    !base.getClass().hasItemHealth(base)\n                    || (base.getClass().getItemHealth(base) == base.getClass().getItemMaxHealth(base))\n                    || (base.getTypeName() != typeid(ESM::Weapon).name()\n                        && base.getTypeName() != typeid(ESM::Armor).name())))\n            return false;\n\n        if (mFilter & Filter_OnlyRechargable)\n        {\n            if (!(item.mFlags & ItemStack::Flag_Enchanted))\n                return false;\n\n            std::string enchId = base.getClass().getEnchantment(base);\n            const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchId);\n            if (!ench)\n            {\n                Log(Debug::Warning) << \"Warning: Can't find enchantment '\" << enchId << \"' on item \" << base.getCellRef().getRefId();\n                return false;\n            }\n\n            if (base.getCellRef().getEnchantmentCharge() >= ench->mData.mCharge\n                    || base.getCellRef().getEnchantmentCharge() == -1)\n                return false;\n        }\n\n        std::string compare = Misc::StringUtils::lowerCaseUtf8(item.mBase.getClass().getName(item.mBase));\n        if(compare.find(mNameFilter) == std::string::npos)\n            return false;\n\n        return true;\n    }\n\n    ItemStack SortFilterItemModel::getItem (ModelIndex index)\n    {\n        if (index < 0)\n            throw std::runtime_error(\"Invalid index supplied\");\n        if (mItems.size() <= static_cast<size_t>(index))\n            throw std::runtime_error(\"Item index out of range\");\n        return mItems[index];\n    }\n\n    size_t SortFilterItemModel::getItemCount()\n    {\n        return mItems.size();\n    }\n\n    void SortFilterItemModel::setCategory (int category)\n    {\n        mCategory = category;\n    }\n\n    void SortFilterItemModel::setFilter (int filter)\n    {\n        mFilter = filter;\n    }\n\n    void SortFilterItemModel::setNameFilter (const std::string& filter)\n    {\n        mNameFilter = Misc::StringUtils::lowerCaseUtf8(filter);\n    }\n\n    void SortFilterItemModel::setEffectFilter (const std::string& filter)\n    {\n        mEffectFilter = Misc::StringUtils::lowerCaseUtf8(filter);\n    }\n\n    void SortFilterItemModel::update()\n    {\n        mSourceModel->update();\n\n        size_t count = mSourceModel->getItemCount();\n\n        mItems.clear();\n        for (size_t i=0; i<count; ++i)\n        {\n            ItemStack item = mSourceModel->getItem(i);\n\n            for (std::vector<std::pair<MWWorld::Ptr, size_t> >::iterator it = mDragItems.begin(); it != mDragItems.end(); ++it)\n            {\n                if (item.mBase == it->first)\n                {\n                    if (item.mCount < it->second)\n                        throw std::runtime_error(\"Dragging more than present in the model\");\n                    item.mCount -= it->second;\n                }\n            }\n\n            if (item.mCount > 0 && filterAccepts(item))\n                mItems.push_back(item);\n        }\n\n        Compare cmp;\n        cmp.mSortByType = mSortByType;\n        std::sort(mItems.begin(), mItems.end(), cmp);\n    }\n\n    void SortFilterItemModel::onClose()\n    {\n        mSourceModel->onClose();\n    }\n\n    bool SortFilterItemModel::onDropItem(const MWWorld::Ptr &item, int count)\n    {\n        return mSourceModel->onDropItem(item, count);\n    }\n\n    bool SortFilterItemModel::onTakeItem(const MWWorld::Ptr &item, int count)\n    {\n        return mSourceModel->onTakeItem(item, count);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/sortfilteritemmodel.hpp",
    "content": "#ifndef MWGUI_SORT_FILTER_ITEM_MODEL_H\n#define MWGUI_SORT_FILTER_ITEM_MODEL_H\n\n#include \"itemmodel.hpp\"\n\nnamespace MWGui\n{\n\n    class SortFilterItemModel : public ProxyItemModel\n    {\n    public:\n        SortFilterItemModel (ItemModel* sourceModel);\n\n        void update() override;\n\n        bool filterAccepts (const ItemStack& item);\n\n        bool allowedToUseItems() const override;\n        ItemStack getItem (ModelIndex index) override;\n        size_t getItemCount() override;\n\n        /// Dragged items are not displayed.\n        void addDragItem (const MWWorld::Ptr& dragItem, size_t count);\n        void clearDragItems();\n\n        void setCategory (int category);\n        void setFilter (int filter);\n        void setNameFilter (const std::string& filter);\n        void setEffectFilter (const std::string& filter);\n\n        /// Use ItemStack::Type for sorting?\n        void setSortByType(bool sort) { mSortByType = sort; }\n\n        void onClose() override;\n        bool onDropItem(const MWWorld::Ptr &item, int count) override;\n        bool onTakeItem(const MWWorld::Ptr &item, int count) override;\n\n        static constexpr int Category_Weapon = (1<<1);\n        static constexpr int Category_Apparel = (1<<2);\n        static constexpr int Category_Misc = (1<<3);\n        static constexpr int Category_Magic = (1<<4);\n        static constexpr int Category_All = 255;\n\n        static constexpr int Filter_OnlyIngredients = (1<<0);\n        static constexpr int Filter_OnlyEnchanted = (1<<1);\n        static constexpr int Filter_OnlyEnchantable = (1<<2);\n        static constexpr int Filter_OnlyChargedSoulstones = (1<<3);\n        static constexpr int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action\n        static constexpr int Filter_OnlyRepairable = (1<<5);\n        static constexpr int Filter_OnlyRechargable = (1<<6);\n        static constexpr int Filter_OnlyRepairTools = (1<<7);\n\n\n    private:\n        std::vector<ItemStack> mItems;\n\n        std::vector<std::pair<MWWorld::Ptr, size_t> > mDragItems;\n\n        int mCategory;\n        int mFilter;\n        bool mSortByType;\n\n        std::string mNameFilter; // filter by item name\n        std::string mEffectFilter; // filter by magic effect\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/soulgemdialog.cpp",
    "content": "#include \"soulgemdialog.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"messagebox.hpp\"\n\nnamespace MWGui\n{\n\n    void SoulgemDialog::show(const MWWorld::Ptr &soulgem)\n    {\n        mSoulgem = soulgem;\n        std::vector<std::string> buttons;\n        buttons.emplace_back(\"#{sRechargeEnchantment}\");\n        buttons.emplace_back(\"#{sMake Enchantment}\");\n        mManager->createInteractiveMessageBox(\"#{sDoYouWantTo}\", buttons);\n        mManager->eventButtonPressed += MyGUI::newDelegate(this, &SoulgemDialog::onButtonPressed);\n    }\n\n    void SoulgemDialog::onButtonPressed(int button)\n    {\n        if (button == 0)\n        {\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Recharge, mSoulgem);\n        }\n        else\n        {\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mSoulgem);\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/soulgemdialog.hpp",
    "content": "#ifndef OPENMW_MWGUI_SOULGEMDIALOG_H\n#define OPENMW_MWGUI_SOULGEMDIALOG_H\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWGui\n{\n\n    class MessageBoxManager;\n\n    class SoulgemDialog\n    {\n    public:\n        SoulgemDialog (MessageBoxManager* manager)\n            : mManager(manager) {}\n\n        void show (const MWWorld::Ptr& soulgem);\n\n        void onButtonPressed(int button);\n\n    private:\n        MessageBoxManager* mManager;\n        MWWorld::Ptr mSoulgem;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/spellbuyingwindow.cpp",
    "content": "#include \"spellbuyingwindow.hpp\"\n\n#include <MyGUI_Gui.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_ScrollView.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWGui\n{\n    SpellBuyingWindow::SpellBuyingWindow() :\n        WindowBase(\"openmw_spell_buying_window.layout\")\n        , mCurrentY(0)\n    {\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mPlayerGold, \"PlayerGold\");\n        getWidget(mSpellsView, \"SpellsView\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked);\n    }\n\n    bool SpellBuyingWindow::sortSpells (const ESM::Spell* left, const ESM::Spell* right)\n    {\n        std::string leftName = Misc::StringUtils::lowerCase(left->mName);\n        std::string rightName = Misc::StringUtils::lowerCase(right->mName);\n\n        return leftName.compare(rightName) < 0;\n    }\n\n    void SpellBuyingWindow::addSpell(const ESM::Spell& spell)\n    {\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        int price = std::max(1, static_cast<int>(spell.mData.mCost*store.get<ESM::GameSetting>().find(\"fSpellValueMult\")->mValue.getFloat()));\n        price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true);\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        // TODO: refactor to use MyGUI::ListBox\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n\n        MyGUI::Button* toAdd =\n            mSpellsView->createWidget<MyGUI::Button>(\n                price <= playerGold ? \"SandTextButton\" : \"SandTextButtonDisabled\", // can't use setEnabled since that removes tooltip\n                0,\n                mCurrentY,\n                200,\n                lineHeight,\n                MyGUI::Align::Default\n            );\n\n        mCurrentY += lineHeight;\n\n        toAdd->setUserData(price);\n        toAdd->setCaptionWithReplacing(spell.mName+\"   -   \"+MyGUI::utility::toString(price)+\"#{sgp}\");\n        toAdd->setSize(mSpellsView->getWidth(), lineHeight);\n        toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel);\n        toAdd->setUserString(\"ToolTipType\", \"Spell\");\n        toAdd->setUserString(\"Spell\", spell.mId);\n        toAdd->setUserString(\"SpellCost\", std::to_string(spell.mData.mCost));\n        toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick);\n        mSpellsWidgetMap.insert(std::make_pair (toAdd, spell.mId));\n    }\n\n    void SpellBuyingWindow::clearSpells()\n    {\n        mSpellsView->setViewOffset(MyGUI::IntPoint(0,0));\n        mCurrentY = 0;\n        while (mSpellsView->getChildCount())\n            MyGUI::Gui::getInstance().destroyWidget(mSpellsView->getChildAt(0));\n        mSpellsWidgetMap.clear();\n    }\n\n    void SpellBuyingWindow::setPtr(const MWWorld::Ptr &actor)\n    {\n        setPtr(actor, 0);\n    }\n\n    void SpellBuyingWindow::setPtr(const MWWorld::Ptr& actor, int startOffset)\n    {\n        center();\n        mPtr = actor;\n        clearSpells();\n\n        MWMechanics::Spells& merchantSpells = actor.getClass().getCreatureStats (actor).getSpells();\n\n        std::vector<const ESM::Spell*> spellsToSort;\n\n        for (MWMechanics::Spells::TIterator iter = merchantSpells.begin(); iter!=merchantSpells.end(); ++iter)\n        {\n            const ESM::Spell* spell = iter->first;\n\n            if (spell->mData.mType!=ESM::Spell::ST_Spell)\n                continue; // don't try to sell diseases, curses or powers\n\n            if (actor.getClass().isNpc())\n            {\n                const ESM::Race* race =\n                        MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(\n                        actor.get<ESM::NPC>()->mBase->mRace);\n                if (race->mPowers.exists(spell->mId))\n                    continue;\n            }\n\n            if (playerHasSpell(iter->first->mId))\n                continue;\n\n            spellsToSort.push_back(iter->first);\n        }\n\n        std::stable_sort(spellsToSort.begin(), spellsToSort.end(), sortSpells);\n\n        for (const ESM::Spell* spell : spellsToSort)\n        {\n            addSpell(*spell);\n        }\n\n        spellsToSort.clear();\n\n        updateLabels();\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mSpellsView->setVisibleVScroll(false);\n        mSpellsView->setCanvasSize (MyGUI::IntSize(mSpellsView->getWidth(), std::max(mSpellsView->getHeight(), mCurrentY)));\n        mSpellsView->setVisibleVScroll(true);\n        mSpellsView->setViewOffset(MyGUI::IntPoint(0, startOffset));\n    }\n\n    bool SpellBuyingWindow::playerHasSpell(const std::string &id)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        return player.getClass().getCreatureStats(player).getSpells().hasSpell(id);\n    }\n\n    void SpellBuyingWindow::onSpellButtonClick(MyGUI::Widget* _sender)\n    {\n        int price = *_sender->getUserData<int>();\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId))\n            return;\n\n        MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n        MWMechanics::Spells& spells = stats.getSpells();\n        spells.add (mSpellsWidgetMap.find(_sender)->second);\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_PLAYER_SPELLBOOK packet every time a player buys a spell\n        */\n        mwmp::Main::get().getLocalPlayer()->sendSpellChange(mSpellsWidgetMap.find(_sender)->second, mwmp::SpellbookChanges::ADD);\n        /*\n            End of tes3mp addition\n        */\n\n        player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);\n\n        // add gold to NPC trading gold pool\n        MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't unilaterally change the merchant's gold pool on our client and instead let the server do it\n        */\n        //npcStats.setGoldPool(npcStats.getGoldPool() + price);\n\n        mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectMiscellaneous(mPtr, npcStats.getGoldPool() + price, npcStats.getLastRestockTime().getHour(),\n            npcStats.getLastRestockTime().getDay());\n        objectList->sendObjectMiscellaneous();\n        /*\n            End of tes3mp change (major)\n        */\n\n        setPtr(mPtr, mSpellsView->getViewOffset().top);\n\n        MWBase::Environment::get().getWindowManager()->playSound(\"Item Gold Up\");\n    }\n\n    void SpellBuyingWindow::onCancelButtonClicked(MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_SpellBuying);\n    }\n\n    void SpellBuyingWindow::updateLabels()\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        mPlayerGold->setCaptionWithReplacing(\"#{sGold}: \" + MyGUI::utility::toString(playerGold));\n        mPlayerGold->setCoord(8,\n                              mPlayerGold->getTop(),\n                              mPlayerGold->getTextSize().width,\n                              mPlayerGold->getHeight());\n    }\n\n    void SpellBuyingWindow::onReferenceUnavailable()\n    {\n        // remove both Spells and Dialogue (since you always trade with the NPC/creature that you have previously talked to)\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying);\n        MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n    }\n\n    void SpellBuyingWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)\n    {\n        if (mSpellsView->getViewOffset().top + _rel*0.3 > 0)\n            mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0));\n        else\n            mSpellsView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mSpellsView->getViewOffset().top + _rel*0.3f)));\n    }\n}\n\n"
  },
  {
    "path": "apps/openmw/mwgui/spellbuyingwindow.hpp",
    "content": "#ifndef MWGUI_SpellBuyingWINDOW_H\n#define MWGUI_SpellBuyingWINDOW_H\n\n#include \"windowbase.hpp\"\n#include \"referenceinterface.hpp\"\n\nnamespace ESM\n{\n    struct Spell;\n}\n\nnamespace MyGUI\n{\n  class Gui;\n  class Widget;\n}\n\nnamespace MWGui\n{\n    class SpellBuyingWindow : public ReferenceInterface, public WindowBase\n    {\n        public:\n            SpellBuyingWindow();\n\n            void setPtr(const MWWorld::Ptr& actor) override;\n            void setPtr(const MWWorld::Ptr& actor, int startOffset);\n\n            void onFrame(float dt) override { checkReferenceAvailable(); }\n            void clear() override { resetReference(); }\n\n            void onResChange(int, int) override { center(); }\n\n        protected:\n            MyGUI::Button* mCancelButton;\n            MyGUI::TextBox* mPlayerGold;\n\n            MyGUI::ScrollView* mSpellsView;\n\n            std::map<MyGUI::Widget*, std::string> mSpellsWidgetMap;\n\n            void onCancelButtonClicked(MyGUI::Widget* _sender);\n            void onSpellButtonClick(MyGUI::Widget* _sender);\n            void onMouseWheel(MyGUI::Widget* _sender, int _rel);\n            void addSpell(const ESM::Spell& spell);\n            void clearSpells();\n            int mCurrentY;\n\n            void updateLabels();\n\n            void onReferenceUnavailable() override;\n\n            bool playerHasSpell (const std::string& id);\n\n        private:\n            static bool sortSpells (const ESM::Spell* left, const ESM::Spell* right);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/spellcreationdialog.cpp",
    "content": "#include \"spellcreationdialog.hpp\"\n\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_Gui.h>\n\n#include <components/esm/records.hpp>\n#include <components/widgets/list.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/Worldstate.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/spells.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/spellutil.hpp\"\n\n#include \"tooltips.hpp\"\n#include \"class.hpp\"\n#include \"widgets.hpp\"\n\nnamespace\n{\n\n    bool sortMagicEffects (short id1, short id2)\n    {\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        return gmst.find(ESM::MagicEffect::effectIdToString (id1))->mValue.getString()\n                < gmst.find(ESM::MagicEffect::effectIdToString  (id2))->mValue.getString();\n    }\n\n    void init(ESM::ENAMstruct& effect)\n    {\n        effect.mArea = 0;\n        effect.mDuration = 0;\n        effect.mEffectID = -1;\n        effect.mMagnMax = 0;\n        effect.mMagnMin = 0;\n        effect.mRange = 0;\n        effect.mSkill = -1;\n        effect.mAttribute = -1;\n    }\n}\n\nnamespace MWGui\n{\n\n    EditEffectDialog::EditEffectDialog()\n        : WindowModal(\"openmw_edit_effect.layout\")\n        , mEditing(false)\n        , mMagicEffect(nullptr)\n        , mConstantEffect(false)\n    {\n        init(mEffect);\n        init(mOldEffect);\n\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mOkButton, \"OkButton\");\n        getWidget(mDeleteButton, \"DeleteButton\");\n        getWidget(mRangeButton, \"RangeButton\");\n        getWidget(mMagnitudeMinValue, \"MagnitudeMinValue\");\n        getWidget(mMagnitudeMaxValue, \"MagnitudeMaxValue\");\n        getWidget(mDurationValue, \"DurationValue\");\n        getWidget(mAreaValue, \"AreaValue\");\n        getWidget(mMagnitudeMinSlider, \"MagnitudeMinSlider\");\n        getWidget(mMagnitudeMaxSlider, \"MagnitudeMaxSlider\");\n        getWidget(mDurationSlider, \"DurationSlider\");\n        getWidget(mAreaSlider, \"AreaSlider\");\n        getWidget(mEffectImage, \"EffectImage\");\n        getWidget(mEffectName, \"EffectName\");\n        getWidget(mAreaText, \"AreaText\");\n        getWidget(mDurationBox, \"DurationBox\");\n        getWidget(mAreaBox, \"AreaBox\");\n        getWidget(mMagnitudeBox, \"MagnitudeBox\");\n\n        mRangeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onRangeButtonClicked);\n        mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onOkButtonClicked);\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onCancelButtonClicked);\n        mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onDeleteButtonClicked);\n\n        mMagnitudeMinSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMinChanged);\n        mMagnitudeMaxSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged);\n        mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged);\n        mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged);\n    }\n\n    void EditEffectDialog::setConstantEffect(bool constant)\n    {\n        mConstantEffect = constant;\n    }\n\n    void EditEffectDialog::onOpen()\n    {\n        WindowModal::onOpen();\n        center();\n    }\n\n    bool EditEffectDialog::exit()\n    {\n        if(mEditing)\n            eventEffectModified(mOldEffect);\n        else\n            eventEffectRemoved(mEffect);\n        return true;\n    }\n\n    void EditEffectDialog::newEffect (const ESM::MagicEffect *effect)\n    {\n        bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0;\n        bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect;\n        bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect;\n\n        if (!allowSelf && !allowTouch && !allowTarget)\n            return; // TODO: Show an error message popup?\n\n        setMagicEffect(effect);\n        mEditing = false;\n\n        mDeleteButton->setVisible (false);\n\n        mEffect.mRange = ESM::RT_Self;\n        if (!allowSelf)\n            mEffect.mRange = ESM::RT_Touch;\n        if (!allowTouch)\n            mEffect.mRange = ESM::RT_Target;\n        mEffect.mMagnMin = 1;\n        mEffect.mMagnMax = 1;\n        mEffect.mDuration = 1;\n        mEffect.mArea = 0;\n        mEffect.mSkill = -1;\n        mEffect.mAttribute = -1;\n        eventEffectAdded(mEffect);\n\n        onRangeButtonClicked(mRangeButton);\n\n        mMagnitudeMinSlider->setScrollPosition (0);\n        mMagnitudeMaxSlider->setScrollPosition (0);\n        mAreaSlider->setScrollPosition (0);\n        mDurationSlider->setScrollPosition (0);\n\n        mDurationValue->setCaption(\"1\");\n        mMagnitudeMinValue->setCaption(\"1\");\n        const std::string to = MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sTo\", \"-\");\n\n        mMagnitudeMaxValue->setCaption(to + \" 1\");\n        mAreaValue->setCaption(\"0\");\n\n        setVisible(true);\n    }\n\n    void EditEffectDialog::editEffect (ESM::ENAMstruct effect)\n    {\n        const ESM::MagicEffect* magicEffect =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);\n\n        setMagicEffect(magicEffect);\n        mOldEffect = effect;\n        mEffect = effect;\n        mEditing = true;\n\n        mDeleteButton->setVisible (true);\n\n        mMagnitudeMinSlider->setScrollPosition (effect.mMagnMin-1);\n        mMagnitudeMaxSlider->setScrollPosition (effect.mMagnMax-1);\n        mAreaSlider->setScrollPosition (effect.mArea);\n        mDurationSlider->setScrollPosition (effect.mDuration-1);\n\n        if (mEffect.mRange == ESM::RT_Self)\n            mRangeButton->setCaptionWithReplacing (\"#{sRangeSelf}\");\n        else if (mEffect.mRange == ESM::RT_Target)\n            mRangeButton->setCaptionWithReplacing (\"#{sRangeTarget}\");\n        else if (mEffect.mRange == ESM::RT_Touch)\n            mRangeButton->setCaptionWithReplacing (\"#{sRangeTouch}\");\n\n        onMagnitudeMinChanged (mMagnitudeMinSlider, effect.mMagnMin-1);\n        onMagnitudeMaxChanged (mMagnitudeMinSlider, effect.mMagnMax-1);\n        onAreaChanged (mAreaSlider, effect.mArea);\n        onDurationChanged (mDurationSlider, effect.mDuration-1);\n        eventEffectModified(mEffect);\n\n        updateBoxes();\n    }\n\n    void EditEffectDialog::setMagicEffect (const ESM::MagicEffect *effect)\n    {\n        mEffectImage->setImageTexture(MWBase::Environment::get().getWindowManager()->correctIconPath(effect->mIcon));\n\n        mEffectName->setCaptionWithReplacing(\"#{\"+ESM::MagicEffect::effectIdToString  (effect->mIndex)+\"}\");\n\n        mEffect.mEffectID = effect->mIndex;\n\n        mMagicEffect = effect;\n\n        updateBoxes();\n    }\n\n    void EditEffectDialog::updateBoxes()\n    {\n        static int startY = mMagnitudeBox->getPosition().top;\n        int curY = startY;\n\n        mMagnitudeBox->setVisible (false);\n        mDurationBox->setVisible (false);\n        mAreaBox->setVisible (false);\n\n        if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))\n        {\n            mMagnitudeBox->setPosition(mMagnitudeBox->getPosition().left, curY);\n            mMagnitudeBox->setVisible (true);\n            curY += mMagnitudeBox->getSize().height;\n        }\n        if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)&&mConstantEffect==false)\n        {\n            mDurationBox->setPosition(mDurationBox->getPosition().left, curY);\n            mDurationBox->setVisible (true);\n            curY += mDurationBox->getSize().height;\n        }\n        if (mEffect.mRange != ESM::RT_Self)\n        {\n            mAreaBox->setPosition(mAreaBox->getPosition().left, curY);\n            mAreaBox->setVisible (true);\n            //curY += mAreaBox->getSize().height;\n        }\n    }\n\n    void EditEffectDialog::onRangeButtonClicked (MyGUI::Widget* sender)\n    {\n        mEffect.mRange = (mEffect.mRange+1)%3;\n\n        // cycle through range types until we find something that's allowed\n        // does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect dialog)\n        bool allowSelf = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0;\n        bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect;\n        bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect;\n        if (mEffect.mRange == ESM::RT_Self && !allowSelf)\n            mEffect.mRange = (mEffect.mRange+1)%3;\n        if (mEffect.mRange == ESM::RT_Touch && !allowTouch)\n            mEffect.mRange = (mEffect.mRange+1)%3;\n        if (mEffect.mRange == ESM::RT_Target && !allowTarget)\n            mEffect.mRange = (mEffect.mRange+1)%3;\n\n        if(mEffect.mRange == ESM::RT_Self)\n        {\n            mAreaSlider->setScrollPosition(0);\n            onAreaChanged(mAreaSlider,0);\n        }\n\n        if (mEffect.mRange == ESM::RT_Self)\n            mRangeButton->setCaptionWithReplacing (\"#{sRangeSelf}\");\n        else if (mEffect.mRange == ESM::RT_Target)\n            mRangeButton->setCaptionWithReplacing (\"#{sRangeTarget}\");\n        else if (mEffect.mRange == ESM::RT_Touch)\n            mRangeButton->setCaptionWithReplacing (\"#{sRangeTouch}\");\n\n        updateBoxes();\n        eventEffectModified(mEffect);\n    }\n\n    void EditEffectDialog::onDeleteButtonClicked (MyGUI::Widget* sender)\n    {\n        setVisible(false);\n\n        eventEffectRemoved(mEffect);\n    }\n\n    void EditEffectDialog::onOkButtonClicked (MyGUI::Widget* sender)\n    {\n        setVisible(false);\n    }\n\n    void EditEffectDialog::onCancelButtonClicked (MyGUI::Widget* sender)\n    {\n        setVisible(false);\n        exit();\n    }\n\n    void EditEffectDialog::setSkill (int skill)\n    {\n        mEffect.mSkill = skill;\n        eventEffectModified(mEffect);\n    }\n\n    void EditEffectDialog::setAttribute (int attribute)\n    {\n        mEffect.mAttribute = attribute;\n        eventEffectModified(mEffect);\n    }\n\n    void EditEffectDialog::onMagnitudeMinChanged (MyGUI::ScrollBar* sender, size_t pos)\n    {\n        mMagnitudeMinValue->setCaption(MyGUI::utility::toString(pos+1));\n        mEffect.mMagnMin = pos+1;\n\n        // trigger the check again (see below)\n        onMagnitudeMaxChanged(mMagnitudeMaxSlider, mMagnitudeMaxSlider->getScrollPosition ());\n        eventEffectModified(mEffect);\n    }\n\n    void EditEffectDialog::onMagnitudeMaxChanged (MyGUI::ScrollBar* sender, size_t pos)\n    {\n        // make sure the max value is actually larger or equal than the min value\n        size_t magnMin = std::abs(mEffect.mMagnMin); // should never be < 0, this is just here to avoid the compiler warning\n        if (pos+1 < magnMin)\n        {\n            pos = mEffect.mMagnMin-1;\n            sender->setScrollPosition (pos);\n        }\n\n        mEffect.mMagnMax = pos+1;\n        const std::string to = MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sTo\", \"-\");\n\n        mMagnitudeMaxValue->setCaption(to + \" \" + MyGUI::utility::toString(pos+1));\n\n        eventEffectModified(mEffect);\n    }\n\n    void EditEffectDialog::onDurationChanged (MyGUI::ScrollBar* sender, size_t pos)\n    {\n        mDurationValue->setCaption(MyGUI::utility::toString(pos+1));\n        mEffect.mDuration = pos+1;\n        eventEffectModified(mEffect);\n    }\n\n    void EditEffectDialog::onAreaChanged (MyGUI::ScrollBar* sender, size_t pos)\n    {\n        mAreaValue->setCaption(MyGUI::utility::toString(pos));\n        mEffect.mArea = pos;\n        eventEffectModified(mEffect);\n    }\n\n    // ------------------------------------------------------------------------------------------------\n\n    SpellCreationDialog::SpellCreationDialog()\n        : WindowBase(\"openmw_spellcreation_dialog.layout\")\n        , EffectEditorBase(EffectEditorBase::Spellmaking)\n    {\n        getWidget(mNameEdit, \"NameEdit\");\n        getWidget(mMagickaCost, \"MagickaCost\");\n        getWidget(mSuccessChance, \"SuccessChance\");\n        getWidget(mAvailableEffectsList, \"AvailableEffects\");\n        getWidget(mUsedEffectsView, \"UsedEffects\");\n        getWidget(mPriceLabel, \"PriceLabel\");\n        getWidget(mBuyButton, \"BuyButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onCancelButtonClicked);\n        mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onBuyButtonClicked);\n        mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept);\n\n        setWidgets(mAvailableEffectsList, mUsedEffectsView);\n    }\n\n    void SpellCreationDialog::setPtr (const MWWorld::Ptr& actor)\n    {\n        mPtr = actor;\n        mNameEdit->setCaption(\"\");\n\n        startEditing();\n    }\n\n    void SpellCreationDialog::onCancelButtonClicked (MyGUI::Widget* sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_SpellCreation);\n    }\n\n    void SpellCreationDialog::onBuyButtonClicked (MyGUI::Widget* sender)\n    {\n        if (mEffects.size() <= 0)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage30}\");\n            return;\n        }\n\n        if (mNameEdit->getCaption () == \"\")\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage10}\");\n            return;\n        }\n\n        if (mMagickaCost->getCaption() == \"0\")\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sEnchantmentMenu8}\");\n            return;\n        }\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        int price = MyGUI::utility::parseInt(mPriceLabel->getCaption());\n        if (price > playerGold)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage18}\");\n            return;\n        }\n\n        mSpell.mName = mNameEdit->getCaption();\n\n        player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);\n\n        // add gold to NPC trading gold pool\n        MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't unilaterally change the merchant's gold pool on our client and instead let the server do it\n        */\n        //npcStats.setGoldPool(npcStats.getGoldPool() + price);\n\n        mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectMiscellaneous(mPtr, npcStats.getGoldPool() + price, npcStats.getLastRestockTime().getHour(),\n            npcStats.getLastRestockTime().getDay());\n        objectList->sendObjectMiscellaneous();\n        /*\n            End of tes3mp change (major)\n        */\n\n        MWBase::Environment::get().getWindowManager()->playSound (\"Mysticism Hit\");\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't create a record and don't add the spell to the player's spellbook;\n            instead just send its record to the server and expect the server to add it\n            to the player's spellbook\n        */\n        /*\n        const ESM::Spell* spell = MWBase::Environment::get().getWorld()->createRecord(mSpell);\n\n        MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n        MWMechanics::Spells& spells = stats.getSpells();\n        spells.add(spell->mId);\n        */\n\n        mwmp::Main::get().getNetworking()->getWorldstate()->sendSpellRecord(&mSpell);\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation);\n    }\n\n    void SpellCreationDialog::onAccept(MyGUI::EditBox *sender)\n    {\n        onBuyButtonClicked(sender);\n\n        // To do not spam onAccept() again and again\n        MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);\n    }\n\n    void SpellCreationDialog::onOpen()\n    {\n        center();\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);\n    }\n\n    void SpellCreationDialog::onReferenceUnavailable ()\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation);\n    }\n\n    void SpellCreationDialog::notifyEffectsChanged ()\n    {\n        if (mEffects.empty())\n        {\n            mMagickaCost->setCaption(\"0\");\n            mPriceLabel->setCaption(\"0\");\n            mSuccessChance->setCaption(\"0\");\n            return;\n        }\n\n        float y = 0;\n\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        for (const ESM::ENAMstruct& effect : mEffects)\n        {\n            y += std::max(1.f, MWMechanics::calcEffectCost(effect));\n\n            if (effect.mRange == ESM::RT_Target)\n                y *= 1.5;\n        }\n\n        ESM::EffectList effectList;\n        effectList.mList = mEffects;\n        mSpell.mEffects = effectList;\n        mSpell.mData.mCost = int(y);\n        mSpell.mData.mType = ESM::Spell::ST_Spell;\n        mSpell.mData.mFlags = 0;\n\n        mMagickaCost->setCaption(MyGUI::utility::toString(int(y)));\n\n        float fSpellMakingValueMult =\n            store.get<ESM::GameSetting>().find(\"fSpellMakingValueMult\")->mValue.getFloat();\n\n        int price = std::max(1, static_cast<int>(y * fSpellMakingValueMult));\n        price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);\n\n        mPriceLabel->setCaption(MyGUI::utility::toString(int(price)));\n\n        float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), nullptr);\n\n        int intChance = std::min(100, int(chance));\n        mSuccessChance->setCaption(MyGUI::utility::toString(intChance));\n    }\n\n    // ------------------------------------------------------------------------------------------------\n\n\n    EffectEditorBase::EffectEditorBase(Type type)\n        : mAvailableEffectsList(nullptr)\n        , mUsedEffectsView(nullptr)\n        , mAddEffectDialog()\n        , mSelectAttributeDialog(nullptr)\n        , mSelectSkillDialog(nullptr)\n        , mSelectedEffect(0)\n        , mSelectedKnownEffectId(0)\n        , mConstantEffect(false)\n        , mType(type)\n    {\n        mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded);\n        mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified);\n        mAddEffectDialog.eventEffectRemoved += MyGUI::newDelegate(this, &EffectEditorBase::onEffectRemoved);\n\n        mAddEffectDialog.setVisible (false);\n    }\n\n    EffectEditorBase::~EffectEditorBase()\n    {\n    }\n\n    void EffectEditorBase::startEditing ()\n    {\n        // get the list of magic effects that are known to the player\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n        MWMechanics::Spells& spells = stats.getSpells();\n\n        std::vector<short> knownEffects;\n\n        for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)\n        {\n            const ESM::Spell* spell = it->first;\n\n            // only normal spells count\n            if (spell->mData.mType != ESM::Spell::ST_Spell)\n                continue;\n\n            for (const ESM::ENAMstruct& effectInfo : spell->mEffects.mList)\n            {\n                const ESM::MagicEffect * effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectInfo.mEffectID);\n\n                // skip effects that do not allow spellmaking/enchanting\n                int requiredFlags = (mType == Spellmaking) ? ESM::MagicEffect::AllowSpellmaking : ESM::MagicEffect::AllowEnchanting;\n                if (!(effect->mData.mFlags & requiredFlags))\n                    continue;\n\n                if (std::find(knownEffects.begin(), knownEffects.end(), effectInfo.mEffectID) == knownEffects.end())\n                    knownEffects.push_back(effectInfo.mEffectID);\n            }\n        }\n\n        std::sort(knownEffects.begin(), knownEffects.end(), sortMagicEffects);\n\n        mAvailableEffectsList->clear ();\n\n        int i=0;\n        for (const short effectId : knownEffects)\n        {\n            mAvailableEffectsList->addItem(MWBase::Environment::get().getWorld ()->getStore ().get<ESM::GameSetting>().find(\n                                               ESM::MagicEffect::effectIdToString(effectId))->mValue.getString());\n            mButtonMapping[i] = effectId;\n            ++i;\n        }\n        mAvailableEffectsList->adjustSize ();\n        mAvailableEffectsList->scrollToTop();\n\n        for (const short effectId : knownEffects)\n        {\n            std::string name = MWBase::Environment::get().getWorld ()->getStore ().get<ESM::GameSetting>().find(\n                                               ESM::MagicEffect::effectIdToString(effectId))->mValue.getString();\n            MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name);\n\n            ToolTips::createMagicEffectToolTip (w, effectId);\n        }\n\n        mEffects.clear();\n        updateEffectsView ();\n    }\n\n    void EffectEditorBase::setWidgets (Gui::MWList *availableEffectsList, MyGUI::ScrollView *usedEffectsView)\n    {\n        mAvailableEffectsList = availableEffectsList;\n        mUsedEffectsView = usedEffectsView;\n\n        mAvailableEffectsList->eventWidgetSelected += MyGUI::newDelegate(this, &EffectEditorBase::onAvailableEffectClicked);\n    }\n\n    void EffectEditorBase::onSelectAttribute ()\n    {\n        const ESM::MagicEffect* effect =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);\n\n        mAddEffectDialog.newEffect(effect);\n        mAddEffectDialog.setAttribute (mSelectAttributeDialog->getAttributeId());\n        MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog);\n        mSelectAttributeDialog = nullptr;\n    }\n\n    void EffectEditorBase::onSelectSkill ()\n    {\n        const ESM::MagicEffect* effect =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);\n\n        mAddEffectDialog.newEffect(effect);\n        mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId());\n        MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog);\n        mSelectSkillDialog = nullptr;\n    }\n\n    void EffectEditorBase::onAttributeOrSkillCancel ()\n    {\n        if (mSelectSkillDialog)\n            MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog);\n        if (mSelectAttributeDialog)\n            MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog);\n\n        mSelectSkillDialog = nullptr;\n        mSelectAttributeDialog = nullptr;\n    }\n\n    void EffectEditorBase::onAvailableEffectClicked (MyGUI::Widget* sender)\n    {\n        if (mEffects.size() >= 8)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sNotifyMessage28}\");\n            return;\n        }\n\n        int buttonId = *sender->getUserData<int>();\n        mSelectedKnownEffectId = mButtonMapping[buttonId];\n\n        const ESM::MagicEffect* effect =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);\n\n        if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)\n        {\n            delete mSelectSkillDialog;\n            mSelectSkillDialog = new SelectSkillDialog();\n            mSelectSkillDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel);\n            mSelectSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectSkill);\n            mSelectSkillDialog->setVisible (true);\n        }\n        else if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute)\n        {\n            delete mSelectAttributeDialog;\n            mSelectAttributeDialog = new SelectAttributeDialog();\n            mSelectAttributeDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel);\n            mSelectAttributeDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectAttribute);\n            mSelectAttributeDialog->setVisible (true);\n        }\n        else\n        {\n            for (const ESM::ENAMstruct& effectInfo : mEffects)\n            {\n                if (effectInfo.mEffectID == mSelectedKnownEffectId)\n                {\n                    MWBase::Environment::get().getWindowManager()->messageBox (\"#{sOnetypeEffectMessage}\");\n                    return;\n                }\n            }\n\n            mAddEffectDialog.newEffect(effect);\n        }\n    }\n\n    void EffectEditorBase::onEffectModified (ESM::ENAMstruct effect)\n    {\n        mEffects[mSelectedEffect] = effect;\n\n        updateEffectsView();\n    }\n\n    void EffectEditorBase::onEffectRemoved (ESM::ENAMstruct effect)\n    {\n        mEffects.erase(mEffects.begin() + mSelectedEffect);\n        updateEffectsView();\n    }\n\n    void EffectEditorBase::updateEffectsView ()\n    {\n        MyGUI::EnumeratorWidgetPtr oldWidgets = mUsedEffectsView->getEnumerator ();\n        MyGUI::Gui::getInstance ().destroyWidgets (oldWidgets);\n\n        MyGUI::IntSize size(0,0);\n\n        int i = 0;\n        for (const ESM::ENAMstruct& effectInfo : mEffects)\n        {\n            Widgets::SpellEffectParams params;\n            params.mEffectID = effectInfo.mEffectID;\n            params.mSkill = effectInfo.mSkill;\n            params.mAttribute = effectInfo.mAttribute;\n            params.mDuration = effectInfo.mDuration;\n            params.mMagnMin = effectInfo.mMagnMin;\n            params.mMagnMax = effectInfo.mMagnMax;\n            params.mRange = effectInfo.mRange;\n            params.mArea = effectInfo.mArea;\n            params.mIsConstant = mConstantEffect;\n\n            MyGUI::Button* button = mUsedEffectsView->createWidget<MyGUI::Button>(\"\", MyGUI::IntCoord(0, size.height, 0, 24), MyGUI::Align::Default);\n            button->setUserData(i);\n            button->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onEditEffect);\n            button->setNeedMouseFocus (true);\n\n            Widgets::MWSpellEffectPtr effect = button->createWidget<Widgets::MWSpellEffect>(\"MW_EffectImage\", MyGUI::IntCoord(0,0,0,24), MyGUI::Align::Default);\n\n            effect->setNeedMouseFocus (false);\n            effect->setSpellEffect (params);\n\n            effect->setSize(effect->getRequestedWidth (), 24);\n            button->setSize(effect->getRequestedWidth (), 24);\n\n            size.width = std::max(size.width, effect->getRequestedWidth ());\n            size.height += 24;\n            ++i;\n        }\n\n        // Canvas size must be expressed with HScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mUsedEffectsView->setVisibleHScroll(false);\n        mUsedEffectsView->setCanvasSize(size);\n        mUsedEffectsView->setVisibleHScroll(true);\n\n        notifyEffectsChanged();\n    }\n\n    void EffectEditorBase::onEffectAdded (ESM::ENAMstruct effect)\n    {\n        mEffects.push_back(effect);\n        mSelectedEffect=mEffects.size()-1;\n\n        updateEffectsView();\n    }\n\n    void EffectEditorBase::onEditEffect (MyGUI::Widget *sender)\n    {\n        int id = *sender->getUserData<int>();\n\n        mSelectedEffect = id;\n\n        mAddEffectDialog.editEffect (mEffects[id]);\n        mAddEffectDialog.setVisible (true);\n    }\n\n    void EffectEditorBase::setConstantEffect(bool constant)\n    {\n        mAddEffectDialog.setConstantEffect(constant);\n        mConstantEffect = constant;\n\n        if (!constant)\n            return;\n\n        for (auto it = mEffects.begin(); it != mEffects.end();)\n        {\n            if (it->mRange != ESM::RT_Self)\n            {\n                auto& store = MWBase::Environment::get().getWorld()->getStore();\n                auto magicEffect = store.get<ESM::MagicEffect>().find(it->mEffectID);\n                if ((magicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) == 0)\n                {\n                    it = mEffects.erase(it);\n                    continue;\n                }\n                it->mRange = ESM::RT_Self;\n            }\n            ++it;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/spellcreationdialog.hpp",
    "content": "#ifndef MWGUI_SPELLCREATION_H\n#define MWGUI_SPELLCREATION_H\n\n#include <components/esm/loadmgef.hpp>\n#include <components/esm/loadspel.hpp>\n\n#include \"windowbase.hpp\"\n#include \"referenceinterface.hpp\"\n\nnamespace Gui\n{\n    class MWList;\n}\n\nnamespace MWGui\n{\n\n    class SelectSkillDialog;\n    class SelectAttributeDialog;\n\n    class EditEffectDialog : public WindowModal\n    {\n    public:\n        EditEffectDialog();\n\n        void onOpen() override;\n        bool exit() override;\n\n        void setConstantEffect(bool constant);\n\n        void setSkill(int skill);\n        void setAttribute(int attribute);\n\n        void newEffect (const ESM::MagicEffect* effect);\n        void editEffect (ESM::ENAMstruct effect);\n        typedef MyGUI::delegates::CMultiDelegate1<ESM::ENAMstruct> EventHandle_Effect;\n\n        EventHandle_Effect eventEffectAdded;\n        EventHandle_Effect eventEffectModified;\n        EventHandle_Effect eventEffectRemoved;\n\n    protected:\n        MyGUI::Button* mCancelButton;\n        MyGUI::Button* mOkButton;\n        MyGUI::Button* mDeleteButton;\n\n        MyGUI::Button* mRangeButton;\n\n        MyGUI::Widget* mDurationBox;\n        MyGUI::Widget* mMagnitudeBox;\n        MyGUI::Widget* mAreaBox;\n\n        MyGUI::TextBox* mMagnitudeMinValue;\n        MyGUI::TextBox* mMagnitudeMaxValue;\n        MyGUI::TextBox* mDurationValue;\n        MyGUI::TextBox* mAreaValue;\n\n        MyGUI::ScrollBar* mMagnitudeMinSlider;\n        MyGUI::ScrollBar* mMagnitudeMaxSlider;\n        MyGUI::ScrollBar* mDurationSlider;\n        MyGUI::ScrollBar* mAreaSlider;\n\n        MyGUI::TextBox* mAreaText;\n\n        MyGUI::ImageBox* mEffectImage;\n        MyGUI::TextBox* mEffectName;\n\n        bool mEditing;\n\n    protected:\n        void onRangeButtonClicked (MyGUI::Widget* sender);\n        void onDeleteButtonClicked (MyGUI::Widget* sender);\n        void onOkButtonClicked (MyGUI::Widget* sender);\n        void onCancelButtonClicked (MyGUI::Widget* sender);\n\n        void onMagnitudeMinChanged (MyGUI::ScrollBar* sender, size_t pos);\n        void onMagnitudeMaxChanged (MyGUI::ScrollBar* sender, size_t pos);\n        void onDurationChanged (MyGUI::ScrollBar* sender, size_t pos);\n        void onAreaChanged (MyGUI::ScrollBar* sender, size_t pos);\n        void setMagicEffect(const ESM::MagicEffect* effect);\n\n        void updateBoxes();\n\n    protected:\n        ESM::ENAMstruct mEffect;\n        ESM::ENAMstruct mOldEffect;\n\n        const ESM::MagicEffect* mMagicEffect;\n\n        bool mConstantEffect;\n    };\n\n\n    class EffectEditorBase\n    {\n    public:\n        enum Type\n        {\n            Spellmaking,\n            Enchanting\n        };\n\n        EffectEditorBase(Type type);\n        virtual ~EffectEditorBase();\n\n        void setConstantEffect(bool constant);\n\n    protected:\n        std::map<int, short> mButtonMapping; // maps button ID to effect ID\n\n        Gui::MWList* mAvailableEffectsList;\n        MyGUI::ScrollView* mUsedEffectsView;\n\n        EditEffectDialog mAddEffectDialog;\n        SelectAttributeDialog* mSelectAttributeDialog;\n        SelectSkillDialog* mSelectSkillDialog;\n\n        int mSelectedEffect;\n        short mSelectedKnownEffectId;\n\n        bool mConstantEffect;\n\n        std::vector<ESM::ENAMstruct> mEffects;\n\n        void onEffectAdded(ESM::ENAMstruct effect);\n        void onEffectModified(ESM::ENAMstruct effect);\n        void onEffectRemoved(ESM::ENAMstruct effect);\n\n        void onAvailableEffectClicked (MyGUI::Widget* sender);\n\n        void onAttributeOrSkillCancel();\n        void onSelectAttribute();\n        void onSelectSkill();\n\n        void onEditEffect(MyGUI::Widget* sender);\n\n        void updateEffectsView();\n\n        void startEditing();\n        void setWidgets (Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView);\n\n        virtual void notifyEffectsChanged () {}\n\n    private:\n        Type mType;\n    };\n\n    class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase\n    {\n    public:\n        SpellCreationDialog();\n\n        void onOpen() override;\n        void clear() override { resetReference(); }\n\n        void onFrame(float dt) override { checkReferenceAvailable(); }\n\n        void setPtr(const MWWorld::Ptr& actor) override;\n\n    protected:\n        void onReferenceUnavailable() override;\n\n        void onCancelButtonClicked (MyGUI::Widget* sender);\n        void onBuyButtonClicked (MyGUI::Widget* sender);\n        void onAccept(MyGUI::EditBox* sender);\n\n        void notifyEffectsChanged() override;\n\n        MyGUI::EditBox* mNameEdit;\n        MyGUI::TextBox* mMagickaCost;\n        MyGUI::TextBox* mSuccessChance;\n        MyGUI::Button* mBuyButton;\n        MyGUI::Button* mCancelButton;\n        MyGUI::TextBox* mPriceLabel;\n\n        ESM::Spell mSpell;\n\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/spellicons.cpp",
    "content": "#include \"spellicons.hpp\"\n\n#include <sstream>\n#include <iomanip>\n\n#include <MyGUI_ImageBox.h>\n\n#include <components/esm/loadmgef.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"tooltips.hpp\"\n\n\nnamespace MWGui\n{\n\n    void EffectSourceVisitor::visit (MWMechanics::EffectKey key, int effectIndex,\n                                     const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                                     float magnitude, float remainingTime, float totalTime)\n    {\n        MagicEffectInfo newEffectSource;\n        newEffectSource.mKey = key;\n        newEffectSource.mMagnitude = static_cast<int>(magnitude);\n        newEffectSource.mPermanent = mIsPermanent;\n        newEffectSource.mRemainingTime = remainingTime;\n        newEffectSource.mSource = sourceName;\n        newEffectSource.mTotalTime = totalTime;\n\n        mEffectSources[key.mId].push_back(newEffectSource);\n    }\n\n\n    void SpellIcons::updateWidgets(MyGUI::Widget *parent, bool adjustSize)\n    {\n        // TODO: Tracking add/remove/expire would be better than force updating every frame\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        const MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n\n\n        EffectSourceVisitor visitor;\n\n        // permanent item enchantments & permanent spells\n        visitor.mIsPermanent = true;\n        MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);\n        store.visitEffectSources(visitor);\n        stats.getSpells().visitEffectSources(visitor);\n\n        // now add lasting effects\n        visitor.mIsPermanent = false;\n        stats.getActiveSpells().visitEffectSources(visitor);\n\n        std::map <int, std::vector<MagicEffectInfo> >& effects = visitor.mEffectSources;\n\n        int w=2;\n\n        for (auto& effectInfoPair : effects)\n        {\n            const int effectId = effectInfoPair.first;\n            const ESM::MagicEffect* effect =\n                MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectId);\n\n            float remainingDuration = 0;\n            float totalDuration = 0;\n\n            std::string sourcesDescription;\n\n            static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fMagicStartIconBlink\")->mValue.getFloat();\n\n            std::vector<MagicEffectInfo>& effectInfos = effectInfoPair.second;\n            bool addNewLine = false;\n            for (const MagicEffectInfo& effectInfo : effectInfos)\n            {\n                if (addNewLine)\n                    sourcesDescription += \"\\n\";\n\n                // if at least one of the effect sources is permanent, the effect will never wear off\n                if (effectInfo.mPermanent)\n                {\n                    remainingDuration = fadeTime;\n                    totalDuration = fadeTime;\n                }\n                else\n                {\n                    remainingDuration = std::max(remainingDuration, effectInfo.mRemainingTime);\n                    totalDuration = std::max(totalDuration, effectInfo.mTotalTime);\n                }\n\n                sourcesDescription += effectInfo.mSource;\n\n                if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)\n                    sourcesDescription += \" (\" +\n                            MWBase::Environment::get().getWindowManager()->getGameSettingString(\n                                ESM::Skill::sSkillNameIds[effectInfo.mKey.mArg], \"\") + \")\";\n                if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute)\n                    sourcesDescription += \" (\" +\n                            MWBase::Environment::get().getWindowManager()->getGameSettingString(\n                                ESM::Attribute::sGmstAttributeIds[effectInfo.mKey.mArg], \"\") + \")\";\n\n                ESM::MagicEffect::MagnitudeDisplayType displayType = effect->getMagnitudeDisplayType();\n                if (displayType == ESM::MagicEffect::MDT_TimesInt)\n                {\n                    std::string timesInt =  MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sXTimesINT\", \"\");\n                    std::stringstream formatter;\n                    formatter << std::fixed << std::setprecision(1) << \" \" << (effectInfo.mMagnitude / 10.0f) << timesInt;\n                    sourcesDescription += formatter.str();\n                }\n                else if ( displayType != ESM::MagicEffect::MDT_None )\n                {\n                    sourcesDescription += \": \" + MyGUI::utility::toString(effectInfo.mMagnitude);\n\n                    if ( displayType == ESM::MagicEffect::MDT_Percentage )\n                        sourcesDescription += MWBase::Environment::get().getWindowManager()->getGameSettingString(\"spercent\", \"\");\n                    else if ( displayType == ESM::MagicEffect::MDT_Feet )\n                        sourcesDescription += \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sfeet\", \"\");\n                    else if ( displayType == ESM::MagicEffect::MDT_Level )\n                    {\n                        sourcesDescription += \" \" + ((effectInfo.mMagnitude > 1) ?\n                            MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sLevels\", \"\") :\n                            MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sLevel\", \"\") );\n                    }\n                    else // ESM::MagicEffect::MDT_Points\n                    {\n                        sourcesDescription += \" \" + ((effectInfo.mMagnitude > 1) ?\n                            MWBase::Environment::get().getWindowManager()->getGameSettingString(\"spoints\", \"\") :\n                            MWBase::Environment::get().getWindowManager()->getGameSettingString(\"spoint\", \"\") );\n                    }\n                }\n                if (effectInfo.mRemainingTime > -1 && Settings::Manager::getBool(\"show effect duration\",\"Game\"))\n                    sourcesDescription += MWGui::ToolTips::getDurationString(effectInfo.mRemainingTime, \" #{sDuration}\");\n\n                addNewLine = true;\n            }\n\n            if (remainingDuration > 0.f)\n            {\n                MyGUI::ImageBox* image;\n                if (mWidgetMap.find(effectId) == mWidgetMap.end())\n                {\n                    image = parent->createWidget<MyGUI::ImageBox>\n                        (\"ImageBox\", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default);\n                    mWidgetMap[effectId] = image;\n\n                    image->setImageTexture(MWBase::Environment::get().getWindowManager()->correctIconPath(effect->mIcon));\n\n                    std::string name = ESM::MagicEffect::effectIdToString (effectId);\n\n                    ToolTipInfo tooltipInfo;\n                    tooltipInfo.caption = \"#{\" + name + \"}\";\n                    tooltipInfo.icon = effect->mIcon;\n                    tooltipInfo.imageSize = 16;\n                    tooltipInfo.wordWrap = false;\n\n                    image->setUserData(tooltipInfo);\n                    image->setUserString(\"ToolTipType\", \"ToolTipInfo\");\n                }\n                else\n                    image = mWidgetMap[effectId];\n\n                image->setPosition(w,2);\n                image->setVisible(true);\n                w += 16;\n\n                ToolTipInfo* tooltipInfo = image->getUserData<ToolTipInfo>();\n                tooltipInfo->text = sourcesDescription;\n\n                // Fade out\n                if (totalDuration >= fadeTime && fadeTime > 0.f)\n                    image->setAlpha(std::min(remainingDuration/fadeTime, 1.f));\n            }\n            else if (mWidgetMap.find(effectId) != mWidgetMap.end())\n            {\n                MyGUI::ImageBox* image = mWidgetMap[effectId];\n                image->setVisible(false);\n                image->setAlpha(1.f);\n            }\n        }\n\n        if (adjustSize)\n        {\n            int s = w + 2;\n            if (effects.empty())\n                s = 0;\n            int diff = parent->getWidth() - s;\n            parent->setSize(s, parent->getHeight());\n            parent->setPosition(parent->getLeft()+diff, parent->getTop());\n        }\n\n        // hide inactive effects\n        for (auto& widgetPair : mWidgetMap)\n        {\n            if (effects.find(widgetPair.first) == effects.end())\n                widgetPair.second->setVisible(false);\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/spellicons.hpp",
    "content": "#ifndef MWGUI_SPELLICONS_H\n#define MWGUI_SPELLICONS_H\n\n#include <string>\n#include <vector>\n\n#include \"../mwmechanics/magiceffects.hpp\"\n\nnamespace MyGUI\n{\n    class Widget;\n    class ImageBox;\n}\nnamespace ESM\n{\n    struct ENAMstruct;\n    struct EffectList;\n}\n\nnamespace MWGui\n{\n\n    // information about a single magic effect source as required for display in the tooltip\n    struct MagicEffectInfo\n    {\n        MagicEffectInfo()\n            : mMagnitude(0)\n            , mRemainingTime(0.f)\n            , mTotalTime(0.f)\n            , mPermanent(false)\n        {}\n        std::string mSource; // display name for effect source (e.g. potion name)\n        MWMechanics::EffectKey mKey;\n        int mMagnitude;\n        float mRemainingTime;\n        float mTotalTime;\n        bool mPermanent; // the effect is permanent\n    };\n\n    class EffectSourceVisitor : public MWMechanics::EffectSourceVisitor\n    {\n    public:\n        bool mIsPermanent;\n\n        std::map <int, std::vector<MagicEffectInfo> > mEffectSources;\n\n        virtual ~EffectSourceVisitor() {}\n\n        void visit (MWMechanics::EffectKey key, int effectIndex,\n                            const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                            float magnitude, float remainingTime = -1, float totalTime = -1) override;\n    };\n\n    class SpellIcons\n    {\n    public:\n        void updateWidgets(MyGUI::Widget* parent, bool adjustSize);\n\n    private:\n\n        std::map<int, MyGUI::ImageBox*> mWidgetMap;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/spellmodel.cpp",
    "content": "#include \"spellmodel.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/spellutil.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/class.hpp\"\n\nnamespace\n{\n\n    bool sortSpells(const MWGui::Spell& left, const MWGui::Spell& right)\n    {\n        if (left.mType != right.mType)\n            return left.mType < right.mType;\n\n        std::string leftName = Misc::StringUtils::lowerCase(left.mName);\n        std::string rightName = Misc::StringUtils::lowerCase(right.mName);\n\n        return leftName.compare(rightName) < 0;\n    }\n\n}\n\nnamespace MWGui\n{\n\n    SpellModel::SpellModel(const MWWorld::Ptr &actor, const std::string& filter)\n        : mActor(actor), mFilter(filter)\n    {\n    }\n\n    SpellModel::SpellModel(const MWWorld::Ptr &actor)\n        : mActor(actor)\n    {\n    }\n\n    bool SpellModel::matchingEffectExists(std::string filter, const ESM::EffectList &effects)\n    {\n        auto wm = MWBase::Environment::get().getWindowManager();\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        for (const auto& effect : effects.mList)\n        {\n            short effectId = effect.mEffectID;\n\n            if (effectId != -1)\n            {\n                const ESM::MagicEffect *magicEffect =\n                    store.get<ESM::MagicEffect>().search(effectId);\n                std::string effectIDStr = ESM::MagicEffect::effectIdToString(effectId);\n                std::string fullEffectName = wm->getGameSettingString(effectIDStr, \"\");\n\n                if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill && effect.mSkill != -1)\n                {\n                    fullEffectName += \" \" + wm->getGameSettingString(ESM::Skill::sSkillNameIds[effect.mSkill], \"\");\n                }\n\n                if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute && effect.mAttribute != -1)\n                {\n                    fullEffectName += \" \" + wm->getGameSettingString(ESM::Attribute::sGmstAttributeIds[effect.mAttribute], \"\");\n                }\n\n                std::string convert = Misc::StringUtils::lowerCaseUtf8(fullEffectName);\n                if (convert.find(filter) != std::string::npos)\n                {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    void SpellModel::update()\n    {\n        mSpells.clear();\n\n        MWMechanics::CreatureStats& stats = mActor.getClass().getCreatureStats(mActor);\n        const MWMechanics::Spells& spells = stats.getSpells();\n\n        const MWWorld::ESMStore &esmStore =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        std::string filter = Misc::StringUtils::lowerCaseUtf8(mFilter);\n\n        for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)\n        {\n            const ESM::Spell* spell = it->first;\n            if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell)\n                continue;\n\n            std::string name = Misc::StringUtils::lowerCaseUtf8(spell->mName);\n \n            if (name.find(filter) == std::string::npos \n                && !matchingEffectExists(filter, spell->mEffects))\n                continue;\n\n            Spell newSpell;\n            newSpell.mName = spell->mName;\n            if (spell->mData.mType == ESM::Spell::ST_Spell)\n            {\n                newSpell.mType = Spell::Type_Spell;\n                std::string cost = std::to_string(spell->mData.mCost);\n                std::string chance = std::to_string(int(MWMechanics::getSpellSuccessChance(spell, mActor)));\n                newSpell.mCostColumn = cost + \"/\" + chance;\n            }\n            else\n                newSpell.mType = Spell::Type_Power;\n            newSpell.mId = spell->mId;\n\n            newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == spell->mId);\n            newSpell.mActive = true;\n            newSpell.mCount = 1;\n            mSpells.push_back(newSpell);\n        }\n\n        MWWorld::InventoryStore& invStore = mActor.getClass().getInventoryStore(mActor);\n        for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it)\n        {\n            MWWorld::Ptr item = *it;\n            const std::string enchantId = item.getClass().getEnchantment(item);\n            if (enchantId.empty())\n                continue;\n            const ESM::Enchantment* enchant = esmStore.get<ESM::Enchantment>().search(enchantId);\n            if (!enchant)\n            {\n                Log(Debug::Warning) << \"Warning: Can't find enchantment '\" << enchantId << \"' on item \" << item.getCellRef().getRefId();\n                continue;\n            }\n\n            if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce)\n                continue;\n\n            std::string name = Misc::StringUtils::lowerCaseUtf8(item.getClass().getName(item));\n\n            if (name.find(filter) == std::string::npos\n                && !matchingEffectExists(filter, enchant->mEffects))\n                continue;\n\n            Spell newSpell;\n            newSpell.mItem = item;\n            newSpell.mId = item.getCellRef().getRefId();\n            newSpell.mName = item.getClass().getName(item);\n            newSpell.mCount = item.getRefData().getCount();\n            newSpell.mType = Spell::Type_EnchantedItem;\n            newSpell.mSelected = invStore.getSelectedEnchantItem() == it;\n\n            // FIXME: move to mwmechanics\n            if (enchant->mData.mType == ESM::Enchantment::CastOnce)\n            {\n                newSpell.mCostColumn = \"100/100\";\n                newSpell.mActive = false;\n            }\n            else\n            {\n                if (!item.getClass().getEquipmentSlots(item).first.empty()\n                        && item.getClass().canBeEquipped(item, mActor).first == 0)\n                    continue;\n\n                int castCost = MWMechanics::getEffectiveEnchantmentCastCost(static_cast<float>(enchant->mData.mCost), mActor);\n\n                std::string cost = std::to_string(castCost);\n                int currentCharge = int(item.getCellRef().getEnchantmentCharge());\n                if (currentCharge ==  -1)\n                    currentCharge = enchant->mData.mCharge;\n                std::string charge = std::to_string(currentCharge);\n                newSpell.mCostColumn = cost + \"/\" + charge;\n\n                newSpell.mActive = invStore.isEquipped(item);\n            }\n            mSpells.push_back(newSpell);\n        }\n\n        std::stable_sort(mSpells.begin(), mSpells.end(), sortSpells);\n    }\n\n    size_t SpellModel::getItemCount() const\n    {\n        return mSpells.size();\n    }\n\n    SpellModel::ModelIndex SpellModel::getSelectedIndex() const\n    {\n        ModelIndex selected = -1;\n        for (SpellModel::ModelIndex i = 0; i<int(getItemCount()); ++i)\n        {\n            if (getItem(i).mSelected) {\n                selected = i;\n                break;\n            }\n        }\n        return selected;\n    }\n\n    Spell SpellModel::getItem(ModelIndex index) const\n    {\n        if (index < 0 || index >= int(mSpells.size()))\n            throw std::runtime_error(\"invalid spell index supplied\");\n        return mSpells[index];\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/spellmodel.hpp",
    "content": "#ifndef OPENMW_GUI_SPELLMODEL_H\n#define OPENMW_GUI_SPELLMODEL_H\n\n#include \"../mwworld/ptr.hpp\"\n#include <components/esm/effectlist.hpp>\n\nnamespace MWGui\n{\n\n    struct Spell\n    {\n        enum Type\n        {\n            Type_Power,\n            Type_Spell,\n            Type_EnchantedItem\n        };\n\n        Type mType;\n        std::string mName;\n        std::string mCostColumn; // Cost/chance or Cost/charge\n        std::string mId; // Item ID or spell ID\n        MWWorld::Ptr mItem; // Only for Type_EnchantedItem\n        int mCount; // Only for Type_EnchantedItem\n        bool mSelected; // Is this the currently selected spell/item (only one can be selected at a time)\n        bool mActive; // (Items only) is the item equipped?\n\n        Spell()\n            : mType(Type_Spell)\n            , mCount(0)\n            , mSelected(false)\n            , mActive(false)\n        {\n        }\n    };\n\n    ///@brief Model that lists all usable powers, spells and enchanted items for an actor.\n    class SpellModel\n    {\n    public:\n        SpellModel(const MWWorld::Ptr& actor, const std::string& filter);\n        SpellModel(const MWWorld::Ptr& actor);\n\n        typedef int ModelIndex;\n\n        void update();\n\n        Spell getItem (ModelIndex index) const;\n        ///< throws for invalid index\n\n        size_t getItemCount() const;\n        ModelIndex getSelectedIndex() const;\n        ///< returns -1 if nothing is selected\n\n    private:\n        MWWorld::Ptr mActor;\n\n        std::vector<Spell> mSpells;\n\n        std::string mFilter;\n\n        bool matchingEffectExists(std::string filter, const ESM::EffectList &effects);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/spellview.cpp",
    "content": "#include \"spellview.hpp\"\n\n#include <MyGUI_FactoryManager.h>\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_Gui.h>\n\n#include <components/widgets/sharedstatebutton.hpp>\n#include <components/widgets/box.hpp>\n\n#include \"tooltips.hpp\"\n\nnamespace MWGui\n{\n\n    const char* SpellView::sSpellModelIndex = \"SpellModelIndex\";\n\n    SpellView::LineInfo::LineInfo(MyGUI::Widget* leftWidget, MyGUI::Widget* rightWidget, SpellModel::ModelIndex spellIndex)\n        : mLeftWidget(leftWidget)\n        , mRightWidget(rightWidget)\n        , mSpellIndex(spellIndex)\n    {\n\n    }\n\n    SpellView::SpellView()\n        : mScrollView(nullptr)\n        , mShowCostColumn(true)\n        , mHighlightSelected(true)\n    {\n    }\n\n    void SpellView::initialiseOverride()\n    {\n        Base::initialiseOverride();\n\n        assignWidget(mScrollView, \"ScrollView\");\n        if (mScrollView == nullptr)\n            throw std::runtime_error(\"Item view needs a scroll view\");\n\n        mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top);\n    }\n\n    void SpellView::registerComponents()\n    {\n        MyGUI::FactoryManager::getInstance().registerFactory<SpellView>(\"Widget\");\n    }\n\n    void SpellView::setModel(SpellModel *model)\n    {\n        mModel.reset(model);\n        update();\n    }\n\n    SpellModel* SpellView::getModel()\n    {\n        return mModel.get();\n    }\n\n    void SpellView::setShowCostColumn(bool show)\n    {\n        if (show != mShowCostColumn)\n        {\n            mShowCostColumn = show;\n            update();\n        }\n    }\n\n    void SpellView::setHighlightSelected(bool highlight)\n    {\n        if (highlight != mHighlightSelected)\n        {\n            mHighlightSelected = highlight;\n            update();\n        }\n    }\n\n    void SpellView::update()\n    {\n        if (!mModel.get())\n            return;\n\n        mModel->update();\n\n        int curType = -1;\n\n        const int spellHeight = 18;\n\n        mLines.clear();\n\n        while (mScrollView->getChildCount())\n            MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0));\n\n        for (SpellModel::ModelIndex i = 0; i<int(mModel->getItemCount()); ++i)\n        {\n            const Spell& spell = mModel->getItem(i);\n            if (curType != spell.mType)\n            {\n                if (spell.mType == Spell::Type_Power)\n                    addGroup(\"#{sPowers}\", \"\");\n                else if (spell.mType == Spell::Type_Spell)\n                    addGroup(\"#{sSpells}\", mShowCostColumn ? \"#{sCostChance}\" : \"\");\n                else\n                    addGroup(\"#{sMagicItem}\", mShowCostColumn ? \"#{sCostCharge}\" : \"\");\n                curType = spell.mType;\n            }\n\n            const std::string skin = spell.mActive ? \"SandTextButton\" : \"SpellTextUnequipped\";\n            const std::string captionSuffix = MWGui::ToolTips::getCountString(spell.mCount);\n\n            Gui::SharedStateButton* t = mScrollView->createWidget<Gui::SharedStateButton>(skin,\n                MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);\n            t->setNeedKeyFocus(true);\n            t->setCaption(spell.mName + captionSuffix);\n            t->setTextAlign(MyGUI::Align::Left);\n            adjustSpellWidget(spell, i, t);\n\n            if (!spell.mCostColumn.empty() && mShowCostColumn)\n            {\n                Gui::SharedStateButton* costChance = mScrollView->createWidget<Gui::SharedStateButton>(skin,\n                    MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);\n                costChance->setCaption(spell.mCostColumn);\n                costChance->setTextAlign(MyGUI::Align::Right);\n                adjustSpellWidget(spell, i, costChance);\n\n                Gui::ButtonGroup group;\n                group.push_back(t);\n                group.push_back(costChance);\n                Gui::SharedStateButton::createButtonGroup(group);\n\n                mLines.emplace_back(t, costChance, i);\n            }\n            else\n                mLines.emplace_back(t, (MyGUI::Widget*)nullptr, i);\n\n            t->setStateSelected(spell.mSelected);\n        }\n\n        layoutWidgets();\n    }\n\n    void SpellView::incrementalUpdate()\n    {\n        if (!mModel.get())\n        {\n            return;\n        }\n\n        mModel->update();\n        bool fullUpdateRequired = false;\n        SpellModel::ModelIndex maxSpellIndexFound = -1;\n        for (LineInfo& line : mLines)\n        {\n            // only update the lines that are \"updateable\"\n            SpellModel::ModelIndex spellIndex(line.mSpellIndex);\n            if (spellIndex != NoSpellIndex)\n            {\n                Gui::SharedStateButton* nameButton = reinterpret_cast<Gui::SharedStateButton*>(line.mLeftWidget);\n\n                // match model against line\n                // if don't match, then major change has happened, so do a full update\n                if (mModel->getItemCount() <= static_cast<unsigned>(spellIndex))\n                {\n                    fullUpdateRequired = true;\n                    break;\n                }\n\n                // more checking for major change.\n                const Spell& spell = mModel->getItem(spellIndex);\n                const std::string captionSuffix = MWGui::ToolTips::getCountString(spell.mCount);\n                if (nameButton->getCaption() != (spell.mName + captionSuffix))\n                {\n                    fullUpdateRequired = true;\n                    break;\n                }\n                else\n                {\n                    maxSpellIndexFound = spellIndex;\n                    Gui::SharedStateButton* costButton = reinterpret_cast<Gui::SharedStateButton*>(line.mRightWidget);\n                    if ((costButton != nullptr) && (costButton->getCaption() != spell.mCostColumn))\n                    {\n                        costButton->setCaption(spell.mCostColumn);\n                    }\n                }\n            }\n        }\n\n        // special case, look for spells added to model that are beyond last updatable item\n        SpellModel::ModelIndex topSpellIndex = mModel->getItemCount() - 1;\n        if (fullUpdateRequired ||\n            ((0 <= topSpellIndex) && (maxSpellIndexFound < topSpellIndex)))\n        {\n            update();\n        }\n    }\n\n\n    void SpellView::layoutWidgets()\n    {\n        int height = 0;\n        for (LineInfo& line : mLines)\n        {\n            height += line.mLeftWidget->getHeight();\n        }\n\n        bool scrollVisible = height > mScrollView->getHeight();\n        int width = mScrollView->getWidth() - (scrollVisible ? 18 : 0);\n\n        height = 0;\n        for (LineInfo& line : mLines)\n        {\n            int lineHeight = line.mLeftWidget->getHeight();\n            line.mLeftWidget->setCoord(4, height, width - 8, lineHeight);\n            if (line.mRightWidget)\n            {\n                line.mRightWidget->setCoord(4, height, width - 8, lineHeight);\n                MyGUI::TextBox* second = line.mRightWidget->castType<MyGUI::TextBox>(false);\n                if (second)\n                    line.mLeftWidget->setSize(width - 8 - second->getTextSize().width, lineHeight);\n            }\n\n            height += lineHeight;\n        }\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mScrollView->setVisibleVScroll(false);\n        mScrollView->setCanvasSize(mScrollView->getWidth(), std::max(mScrollView->getHeight(), height));\n        mScrollView->setVisibleVScroll(true);\n    }\n\n    void SpellView::addGroup(const std::string &label, const std::string& label2)\n    {\n        if (mScrollView->getChildCount() > 0)\n        {\n            MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>(\"MW_HLine\",\n                MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18),\n                MyGUI::Align::Left | MyGUI::Align::Top);\n            separator->setNeedMouseFocus(false);\n            mLines.emplace_back(separator, (MyGUI::Widget*)nullptr, NoSpellIndex);\n        }\n\n        MyGUI::TextBox* groupWidget = mScrollView->createWidget<Gui::TextBox>(\"SandBrightText\",\n            MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24),\n            MyGUI::Align::Left | MyGUI::Align::Top);\n        groupWidget->setCaptionWithReplacing(label);\n        groupWidget->setTextAlign(MyGUI::Align::Left);\n        groupWidget->setNeedMouseFocus(false);\n\n        if (label2 != \"\")\n        {\n            MyGUI::TextBox* groupWidget2 = mScrollView->createWidget<Gui::TextBox>(\"SandBrightText\",\n                MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24),\n                MyGUI::Align::Left | MyGUI::Align::Top);\n            groupWidget2->setCaptionWithReplacing(label2);\n            groupWidget2->setTextAlign(MyGUI::Align::Right);\n            groupWidget2->setNeedMouseFocus(false);\n\n            mLines.emplace_back(groupWidget, groupWidget2, NoSpellIndex);\n        }\n        else\n            mLines.emplace_back(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex);\n    }\n\n\n    void SpellView::setSize(const MyGUI::IntSize &_value)\n    {\n        bool changed = (_value.width != getWidth() || _value.height != getHeight());\n        Base::setSize(_value);\n        if (changed)\n            layoutWidgets();\n    }\n\n    void SpellView::setCoord(const MyGUI::IntCoord &_value)\n    {\n        bool changed = (_value.width != getWidth() || _value.height != getHeight());\n        Base::setCoord(_value);\n        if (changed)\n            layoutWidgets();\n    }\n\n    void SpellView::adjustSpellWidget(const Spell &spell, SpellModel::ModelIndex index, MyGUI::Widget *widget)\n    {\n        if (spell.mType == Spell::Type_EnchantedItem)\n        {\n            widget->setUserData(MWWorld::Ptr(spell.mItem));\n            widget->setUserString(\"ToolTipType\", \"ItemPtr\");\n        }\n        else\n        {\n            widget->setUserString(\"ToolTipType\", \"Spell\");\n            widget->setUserString(\"Spell\", spell.mId);\n        }\n\n        widget->setUserString(sSpellModelIndex, MyGUI::utility::toString(index));\n\n        widget->eventMouseWheel += MyGUI::newDelegate(this, &SpellView::onMouseWheelMoved);\n        widget->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellView::onSpellSelected);\n    }\n\n    SpellModel::ModelIndex SpellView::getSpellModelIndex(MyGUI::Widget* widget)\n    {\n        return MyGUI::utility::parseInt(widget->getUserString(sSpellModelIndex));\n    }\n\n    void SpellView::onSpellSelected(MyGUI::Widget* _sender)\n    {\n        eventSpellClicked(getSpellModelIndex(_sender));\n    }\n\n    void SpellView::onMouseWheelMoved(MyGUI::Widget* _sender, int _rel)\n    {\n        if (mScrollView->getViewOffset().top + _rel*0.3f > 0)\n            mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));\n        else\n            mScrollView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mScrollView->getViewOffset().top + _rel*0.3f)));\n    }\n\n    void SpellView::resetScrollbars()\n    {\n        mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/spellview.hpp",
    "content": "#ifndef OPENMW_GUI_SPELLVIEW_H\n#define OPENMW_GUI_SPELLVIEW_H\n\n#include <memory>\n#include <tuple>\n\n#include <MyGUI_Widget.h>\n\n#include \"spellmodel.hpp\"\n\nnamespace MyGUI\n{\n    class ScrollView;\n}\n\nnamespace MWGui\n{\n\n    class SpellModel;\n\n    ///@brief Displays a SpellModel in a list widget\n    class SpellView final : public MyGUI::Widget\n    {\n        MYGUI_RTTI_DERIVED(SpellView)\n    public:\n        SpellView();\n\n        /// Register needed components with MyGUI's factory manager\n        static void registerComponents ();\n\n        /// Should the cost/chance column be shown?\n        void setShowCostColumn(bool show);\n\n        void setHighlightSelected(bool highlight);\n\n        /// Takes ownership of \\a model\n        void setModel (SpellModel* model);\n\n        SpellModel* getModel();\n\n        void update();\n\n        /// simplified update called each frame\n        void incrementalUpdate();\n\n        typedef MyGUI::delegates::CMultiDelegate1<SpellModel::ModelIndex> EventHandle_ModelIndex;\n        /// Fired when a spell was clicked\n        EventHandle_ModelIndex eventSpellClicked;\n\n        void initialiseOverride() override;\n\n        void setSize(const MyGUI::IntSize& _value) override;\n        void setCoord(const MyGUI::IntCoord& _value) override;\n\n        void resetScrollbars();\n\n    private:\n        MyGUI::ScrollView* mScrollView;\n\n        std::unique_ptr<SpellModel> mModel;\n\n        /// tracks a row in the spell view\n        struct LineInfo\n        {\n            /// the widget on the left side of the row\n            MyGUI::Widget* mLeftWidget;\n\n            /// the widget on the left side of the row (if there is one)\n            MyGUI::Widget* mRightWidget;\n\n            /// index to item in mModel that row is showing information for\n            SpellModel::ModelIndex mSpellIndex;\n\n            LineInfo(MyGUI::Widget* leftWidget, MyGUI::Widget* rightWidget, SpellModel::ModelIndex spellIndex);\n        };\n\n        /// magic number indicating LineInfo does not correspond to an item in mModel\n        enum { NoSpellIndex = -1 };\n\n        std::vector< LineInfo > mLines;\n\n        bool mShowCostColumn;\n        bool mHighlightSelected;\n\n        void layoutWidgets();\n        void addGroup(const std::string& label1, const std::string& label2);\n        void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget);\n\n        void onSpellSelected(MyGUI::Widget* _sender);\n        void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel);\n\n        SpellModel::ModelIndex getSpellModelIndex(MyGUI::Widget* _sender);\n\n        static const char* sSpellModelIndex;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/spellwindow.cpp",
    "content": "#include \"spellwindow.hpp\"\n\n#include <MyGUI_EditBox.h>\n#include <MyGUI_InputManager.h>\n\n#include <components/misc/stringops.hpp>\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include\"../mwmp/Main.hpp\"\n#include\"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"../mwmechanics/spellutil.hpp\"\n#include \"../mwmechanics/spells.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"spellicons.hpp\"\n#include \"confirmationdialog.hpp\"\n#include \"spellview.hpp\"\n\nnamespace MWGui\n{\n\n    SpellWindow::SpellWindow(DragAndDrop* drag)\n        : WindowPinnableBase(\"openmw_spell_window.layout\")\n        , NoDrop(drag, mMainWidget)\n        , mSpellView(nullptr)\n        , mUpdateTimer(0.0f)\n    {\n        mSpellIcons = new SpellIcons();\n\n        MyGUI::Widget* deleteButton;\n        getWidget(deleteButton, \"DeleteSpellButton\");\n\n        getWidget(mSpellView, \"SpellView\");\n        getWidget(mEffectBox, \"EffectsBox\");\n        getWidget(mFilterEdit, \"FilterEdit\");\n\n        mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected);\n        mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &SpellWindow::onFilterChanged);\n        deleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onDeleteClicked);\n\n        setCoord(498, 300, 302, 300);\n\n        // Adjust the spell filtering widget size because of MyGUI limitations.\n        int filterWidth = mSpellView->getSize().width - deleteButton->getSize().width - 3;\n        mFilterEdit->setSize(filterWidth, mFilterEdit->getSize().height);\n    }\n\n    SpellWindow::~SpellWindow()\n    {\n        delete mSpellIcons;\n    }\n\n    void SpellWindow::onPinToggled()\n    {\n        Settings::Manager::setBool(\"spells pin\", \"Windows\", mPinned);\n\n        MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned);\n    }\n\n    void SpellWindow::onTitleDoubleClicked()\n    {\n        if (MyGUI::InputManager::getInstance().isShiftPressed())\n            MWBase::Environment::get().getWindowManager()->toggleMaximized(this);\n        else if (!mPinned)\n            MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic);\n    }\n\n    void SpellWindow::onOpen()\n    {\n        // Reset the filter focus when opening the window\n        MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n        if (focus == mFilterEdit)\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr);\n\n        updateSpells();\n    }\n\n    void SpellWindow::onFrame(float dt) \n    {\n        NoDrop::onFrame(dt);\n        mUpdateTimer += dt;\n        if (0.5f < mUpdateTimer)\n        {\n            mUpdateTimer = 0;\n            mSpellView->incrementalUpdate();\n        }\n\n        // Update effects in-game too if the window is pinned\n        if (mPinned && !MWBase::Environment::get().getWindowManager()->isGuiMode())\n            mSpellIcons->updateWidgets(mEffectBox, false);\n    }\n\n    void SpellWindow::updateSpells()\n    {\n        mSpellIcons->updateWidgets(mEffectBox, false);\n\n        mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), mFilterEdit->getCaption()));\n    }\n\n    void SpellWindow::onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);\n\n        // retrieve ContainerStoreIterator to the item\n        MWWorld::ContainerStoreIterator it = store.begin();\n        for (; it != store.end(); ++it)\n        {\n            if (*it == item)\n            {\n                break;\n            }\n        }\n        if (it == store.end())\n            throw std::runtime_error(\"can't find selected item\");\n\n        // equip, if it can be equipped and is not already equipped\n        if (!alreadyEquipped\n            && !item.getClass().getEquipmentSlots(item).first.empty())\n        {\n            MWBase::Environment::get().getWindowManager()->useItem(item);\n            // make sure that item was successfully equipped\n            if (!store.isEquipped(item))\n                return;\n        }\n\n        store.setSelectedEnchantItem(it);\n        // to reset WindowManager::mSelectedSpell immediately\n        MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(*it);\n\n        updateSpells();\n    }\n\n    void SpellWindow::askDeleteSpell(const std::string &spellId)\n    {\n        // delete spell, if allowed\n        const ESM::Spell* spell =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        std::string raceId = player.get<ESM::NPC>()->mBase->mRace;\n        const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceId);\n        // can't delete racial spells, birthsign spells or powers\n        bool isInherent = race->mPowers.exists(spell->mId) || spell->mData.mType == ESM::Spell::ST_Power;\n        const std::string& signId = MWBase::Environment::get().getWorld()->getPlayer().getBirthSign();\n        if (!isInherent && !signId.empty())\n        {\n            const ESM::BirthSign* sign = MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().find(signId);\n            isInherent = sign->mPowers.exists(spell->mId);\n        }\n\n        if (isInherent)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sDeleteSpellError}\");\n        }\n        else\n        {\n            // ask for confirmation\n            mSpellToDelete = spellId;\n            ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();\n            std::string question = MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sQuestionDeleteSpell\", \"Delete %s?\");\n            question = Misc::StringUtils::format(question, spell->mName);\n            dialog->askForConfirmation(question);\n            dialog->eventOkClicked.clear();\n            dialog->eventOkClicked += MyGUI::newDelegate(this, &SpellWindow::onDeleteSpellAccept);\n            dialog->eventCancelClicked.clear();\n        }\n    }\n\n    void SpellWindow::onModelIndexSelected(SpellModel::ModelIndex index)\n    {\n        const Spell& spell = mSpellView->getModel()->getItem(index);\n        if (spell.mType == Spell::Type_EnchantedItem)\n        {\n            onEnchantedItemSelected(spell.mItem, spell.mActive);\n        }\n        else\n        {\n            if (MyGUI::InputManager::getInstance().isShiftPressed())\n                askDeleteSpell(spell.mId);\n            else\n                onSpellSelected(spell.mId);\n        }\n    }\n\n    void SpellWindow::onFilterChanged(MyGUI::EditBox *sender)\n    {\n        mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), sender->getCaption()));\n    }\n\n    void SpellWindow::onDeleteClicked(MyGUI::Widget *widget)\n    {\n        SpellModel::ModelIndex selected = mSpellView->getModel()->getSelectedIndex();\n        if (selected < 0)\n            return;\n\n        const Spell& spell = mSpellView->getModel()->getItem(selected);\n        if (spell.mType != Spell::Type_EnchantedItem)\n            askDeleteSpell(spell.mId);\n    }\n\n    void SpellWindow::onSpellSelected(const std::string& spellId)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);\n        store.setSelectedEnchantItem(store.end());\n        MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));\n\n        updateSpells();\n\n        /*\n            Start of tes3mp addition\n\n            Send a PlayerMiscellaneous packet with the player's new selected spell\n        */\n        mwmp::Main::get().getLocalPlayer()->sendSelectedSpell(spellId);\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void SpellWindow::onDeleteSpellAccept()\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n        MWMechanics::Spells& spells = stats.getSpells();\n\n        if (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == mSpellToDelete)\n            MWBase::Environment::get().getWindowManager()->unsetSelectedSpell();\n\n        spells.remove(mSpellToDelete);\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_PLAYER_SPELLBOOK packet every time a player deletes one of their spells\n        */\n        mwmp::Main::get().getLocalPlayer()->sendSpellChange(mSpellToDelete, mwmp::SpellbookChanges::REMOVE);\n        /*\n            End of tes3mp addition\n        */\n\n        updateSpells();\n    }\n\n    void SpellWindow::cycle(bool next)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n\n        if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))\n            return;\n\n        bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();\n        const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);\n        if ((!godmode && stats.isParalyzed()) || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())\n            return;\n\n        mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), \"\"));\n\n        SpellModel::ModelIndex selected = mSpellView->getModel()->getSelectedIndex();\n        if (selected < 0)\n            selected = 0;\n\n        selected += next ? 1 : -1;\n        int itemcount = mSpellView->getModel()->getItemCount();\n        if (itemcount == 0)\n            return;\n        selected = (selected + itemcount) % itemcount;\n\n        const Spell& spell = mSpellView->getModel()->getItem(selected);\n        if (spell.mType == Spell::Type_EnchantedItem)\n            onEnchantedItemSelected(spell.mItem, spell.mActive);\n        else\n            onSpellSelected(spell.mId);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/spellwindow.hpp",
    "content": "#ifndef MWGUI_SPELLWINDOW_H\n#define MWGUI_SPELLWINDOW_H\n\n#include \"windowpinnablebase.hpp\"\n\n#include \"spellmodel.hpp\"\n\nnamespace MWGui\n{\n    class SpellIcons;\n    class SpellView;\n\n    class SpellWindow : public WindowPinnableBase, public NoDrop\n    {\n    public:\n        SpellWindow(DragAndDrop* drag);\n        virtual ~SpellWindow();\n\n        void updateSpells();\n\n        void onFrame(float dt) override;\n\n        /// Cycle to next/previous spell\n        void cycle(bool next);\n\n    protected:\n        MyGUI::Widget* mEffectBox;\n\n        std::string mSpellToDelete;\n\n        void onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped);\n        void onSpellSelected(const std::string& spellId);\n        void onModelIndexSelected(SpellModel::ModelIndex index);\n        void onFilterChanged(MyGUI::EditBox *sender);\n        void onDeleteClicked(MyGUI::Widget *widget);\n        void onDeleteSpellAccept();\n        void askDeleteSpell(const std::string& spellId);\n\n        void onPinToggled() override;\n        void onTitleDoubleClicked() override;\n        void onOpen() override;\n\n        SpellView* mSpellView;\n        SpellIcons* mSpellIcons;\n        MyGUI::EditBox* mFilterEdit;\n\n    private:\n        float mUpdateTimer;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/statswatcher.cpp",
    "content": "#include \"statswatcher.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/spellutil.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\nnamespace MWGui\n{\n    // mWatchedTimeToStartDrowning = -1 for correct drowning state check,\n    // if stats.getTimeToStartDrowning() == 0 already on game start\n    StatsWatcher::StatsWatcher()\n      : mWatchedLevel(-1), mWatchedTimeToStartDrowning(-1), mWatchedStatsEmpty(true)\n    {\n    }\n\n    void StatsWatcher::watchActor(const MWWorld::Ptr& ptr)\n    {\n        mWatched = ptr;\n    }\n\n    void StatsWatcher::update()\n    {\n        if (mWatched.isEmpty())\n            return;\n\n        MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n        const MWMechanics::NpcStats &stats = mWatched.getClass().getNpcStats(mWatched);\n        for (int i = 0;i < ESM::Attribute::Length;++i)\n        {\n            if (stats.getAttribute(i) != mWatchedAttributes[i] || mWatchedStatsEmpty)\n            {\n                std::stringstream attrname;\n                attrname << \"AttribVal\"<<(i+1);\n\n                mWatchedAttributes[i] = stats.getAttribute(i);\n                setValue(attrname.str(), stats.getAttribute(i));\n            }\n        }\n\n        if (stats.getHealth() != mWatchedHealth || mWatchedStatsEmpty)\n        {\n            static const std::string hbar(\"HBar\");\n            mWatchedHealth = stats.getHealth();\n            setValue(hbar, stats.getHealth());\n        }\n        if (stats.getMagicka() != mWatchedMagicka || mWatchedStatsEmpty)\n        {\n            static const std::string mbar(\"MBar\");\n            mWatchedMagicka = stats.getMagicka();\n            setValue(mbar, stats.getMagicka());\n        }\n        if (stats.getFatigue() != mWatchedFatigue || mWatchedStatsEmpty)\n        {\n            static const std::string fbar(\"FBar\");\n            mWatchedFatigue = stats.getFatigue();\n            setValue(fbar, stats.getFatigue());\n        }\n\n        float timeToDrown = stats.getTimeToStartDrowning();\n\n        if (timeToDrown != mWatchedTimeToStartDrowning)\n        {\n            static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                    .find(\"fHoldBreathTime\")->mValue.getFloat();\n\n            mWatchedTimeToStartDrowning = timeToDrown;\n\n            if(timeToDrown >= fHoldBreathTime || timeToDrown == -1.0) // -1.0 is a special value during initialization\n                winMgr->setDrowningBarVisibility(false);\n            else\n            {\n                winMgr->setDrowningBarVisibility(true);\n                winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning(), fHoldBreathTime);\n            }\n        }\n\n        //Loop over ESM::Skill::SkillEnum\n        for (int i = 0; i < ESM::Skill::Length; ++i)\n        {\n            if(stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty)\n            {\n                mWatchedSkills[i] = stats.getSkill(i);\n                setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i));\n            }\n        }\n\n        if (stats.getLevel() != mWatchedLevel || mWatchedStatsEmpty)\n        {\n            mWatchedLevel = stats.getLevel();\n            setValue(\"level\", mWatchedLevel);\n        }\n\n        if (mWatched.getClass().isNpc())\n        {\n            const ESM::NPC *watchedRecord = mWatched.get<ESM::NPC>()->mBase;\n\n            if (watchedRecord->mName != mWatchedName || mWatchedStatsEmpty)\n            {\n                mWatchedName = watchedRecord->mName;\n                setValue(\"name\", watchedRecord->mName);\n            }\n\n            if (watchedRecord->mRace != mWatchedRace || mWatchedStatsEmpty)\n            {\n                mWatchedRace = watchedRecord->mRace;\n                const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore()\n                    .get<ESM::Race>().find(watchedRecord->mRace);\n                setValue(\"race\", race->mName);\n            }\n\n            if (watchedRecord->mClass != mWatchedClass || mWatchedStatsEmpty)\n            {\n                mWatchedClass = watchedRecord->mClass;\n                const ESM::Class *cls = MWBase::Environment::get().getWorld()->getStore()\n                    .get<ESM::Class>().find(watchedRecord->mClass);\n                setValue(\"class\", cls->mName);\n\n                MWBase::WindowManager::SkillList majorSkills (5);\n                MWBase::WindowManager::SkillList minorSkills (5);\n\n                for (int i=0; i<5; ++i)\n                {\n                    minorSkills[i] = cls->mData.mSkills[i][0];\n                    majorSkills[i] = cls->mData.mSkills[i][1];\n                }\n\n                configureSkills(majorSkills, minorSkills);\n            }\n        }\n\n        mWatchedStatsEmpty = false;\n    }\n\n    void StatsWatcher::addListener(StatsListener* listener)\n    {\n        mListeners.insert(listener);\n    }\n\n    void StatsWatcher::removeListener(StatsListener* listener)\n    {\n        mListeners.erase(listener);\n    }\n\n    void StatsWatcher::setValue(const std::string& id, const MWMechanics::AttributeValue& value)\n    {\n        for (StatsListener* listener : mListeners)\n            listener->setValue(id, value);\n    }\n\n    void StatsWatcher::setValue(ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)\n    {\n        /// \\todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we\n        /// allow custom skills.\n        for (StatsListener* listener : mListeners)\n            listener->setValue(parSkill, value);\n    }\n\n    void StatsWatcher::setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value)\n    {\n        for (StatsListener* listener : mListeners)\n            listener->setValue(id, value);\n    }\n\n    void StatsWatcher::setValue(const std::string& id, const std::string& value)\n    {\n        for (StatsListener* listener : mListeners)\n            listener->setValue(id, value);\n    }\n\n    void StatsWatcher::setValue(const std::string& id, int value)\n    {\n        for (StatsListener* listener : mListeners)\n            listener->setValue(id, value);\n    }\n\n    void StatsWatcher::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)\n    {\n        for (StatsListener* listener : mListeners)\n            listener->configureSkills(major, minor);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/statswatcher.hpp",
    "content": "#ifndef MWGUI_STATSWATCHER_H\n#define MWGUI_STATSWATCHER_H\n\n#include <set>\n\n#include <components/esm/attr.hpp>\n#include <components/esm/loadskil.hpp>\n\n#include \"../mwmechanics/stat.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWGui\n{\n    class StatsListener\n    {\n    public:\n        /// Set value for the given ID.\n        virtual void setValue(const std::string& id, const MWMechanics::AttributeValue& value) {}\n        virtual void setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value) {}\n        virtual void setValue(const std::string& id, const std::string& value) {}\n        virtual void setValue(const std::string& id, int value) {}\n        virtual void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) {}\n        virtual void configureSkills(const std::vector<int>& major, const std::vector<int>& minor) {}\n    };\n\n    class StatsWatcher\n    {\n        MWWorld::Ptr mWatched;\n\n        MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length];\n        MWMechanics::SkillValue mWatchedSkills[ESM::Skill::Length];\n\n        MWMechanics::DynamicStat<float> mWatchedHealth;\n        MWMechanics::DynamicStat<float> mWatchedMagicka;\n        MWMechanics::DynamicStat<float> mWatchedFatigue;\n\n        std::string mWatchedName;\n        std::string mWatchedRace;\n        std::string mWatchedClass;\n\n        int mWatchedLevel;\n\n        float mWatchedTimeToStartDrowning;\n\n        bool mWatchedStatsEmpty;\n\n        std::set<StatsListener*> mListeners;\n\n        void setValue(const std::string& id, const MWMechanics::AttributeValue& value);\n        void setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value);\n        void setValue(const std::string& id, const std::string& value);\n        void setValue(const std::string& id, int value);\n        void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value);\n        void configureSkills(const std::vector<int>& major, const std::vector<int>& minor);\n\n    public:\n        StatsWatcher();\n\n        void update();\n        void addListener(StatsListener* listener);\n        void removeListener(StatsListener* listener);\n\n        void watchActor(const MWWorld::Ptr& ptr);\n        MWWorld::Ptr getWatchedActor() const { return mWatched; }\n\n        void forceUpdate() { mWatchedStatsEmpty = true; }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/statswindow.cpp",
    "content": "#include \"statswindow.hpp\"\n\n#include <MyGUI_Window.h>\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_ProgressBar.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_LanguageManager.h>\n#include <MyGUI_Gui.h>\n\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"tooltips.hpp\"\n\nnamespace MWGui\n{\n    StatsWindow::StatsWindow (DragAndDrop* drag)\n      : WindowPinnableBase(\"openmw_stats_window.layout\")\n      , NoDrop(drag, mMainWidget)\n      , mSkillView(nullptr)\n      , mMajorSkills()\n      , mMinorSkills()\n      , mMiscSkills()\n      , mSkillValues()\n      , mSkillWidgetMap()\n      , mFactionWidgetMap()\n      , mFactions()\n      , mBirthSignId()\n      , mReputation(0)\n      , mBounty(0)\n      , mSkillWidgets()\n      , mChanged(true)\n      , mMinFullWidth(mMainWidget->getSize().width)\n    {\n\n        const char *names[][2] =\n        {\n            { \"Attrib1\", \"sAttributeStrength\" },\n            { \"Attrib2\", \"sAttributeIntelligence\" },\n            { \"Attrib3\", \"sAttributeWillpower\" },\n            { \"Attrib4\", \"sAttributeAgility\" },\n            { \"Attrib5\", \"sAttributeSpeed\" },\n            { \"Attrib6\", \"sAttributeEndurance\" },\n            { \"Attrib7\", \"sAttributePersonality\" },\n            { \"Attrib8\", \"sAttributeLuck\" },\n            { 0, 0 }\n        };\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        for (int i=0; names[i][0]; ++i)\n        {\n            setText (names[i][0], store.get<ESM::GameSetting>().find (names[i][1])->mValue.getString());\n        }\n\n        getWidget(mSkillView, \"SkillView\");\n        getWidget(mLeftPane, \"LeftPane\");\n        getWidget(mRightPane, \"RightPane\");\n\n        for (int i = 0; i < ESM::Skill::Length; ++i)\n        {\n            mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue()));\n            mSkillWidgetMap.insert(std::make_pair(i, std::make_pair((MyGUI::TextBox*)nullptr, (MyGUI::TextBox*)nullptr)));\n        }\n\n        MyGUI::Window* t = mMainWidget->castType<MyGUI::Window>();\n        t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize);\n\n        onWindowResize(t);\n    }\n\n    void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)\n    {\n        if (mSkillView->getViewOffset().top + _rel*0.3 > 0)\n            mSkillView->setViewOffset(MyGUI::IntPoint(0, 0));\n        else\n            mSkillView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mSkillView->getViewOffset().top + _rel*0.3)));\n    }\n\n    void StatsWindow::onWindowResize(MyGUI::Window* window)\n    {\n        int windowWidth = window->getSize().width;\n        int windowHeight = window->getSize().height;\n\n        //initial values defined in openmw_stats_window.layout, if custom options are not present in .layout, a default is loaded\n        float leftPaneRatio = 0.44f;\n        if (mLeftPane->isUserString(\"LeftPaneRatio\"))\n            leftPaneRatio = MyGUI::utility::parseFloat(mLeftPane->getUserString(\"LeftPaneRatio\"));\n\n        int leftOffsetWidth = 24;\n        if (mLeftPane->isUserString(\"LeftOffsetWidth\"))\n            leftOffsetWidth = MyGUI::utility::parseInt(mLeftPane->getUserString(\"LeftOffsetWidth\"));\n\n        float rightPaneRatio = 1.f - leftPaneRatio;\n        int minLeftWidth = static_cast<int>(mMinFullWidth * leftPaneRatio);\n        int minLeftOffsetWidth = minLeftWidth + leftOffsetWidth;\n\n        //if there's no space for right pane\n        mRightPane->setVisible(windowWidth >= minLeftOffsetWidth);\n        if (!mRightPane->getVisible())\n        {\n            mLeftPane->setCoord(MyGUI::IntCoord(0, 0, windowWidth - leftOffsetWidth, windowHeight));\n        }\n        //if there's some space for right pane\n        else if (windowWidth < mMinFullWidth)\n        {\n            mLeftPane->setCoord(MyGUI::IntCoord(0, 0, minLeftWidth, windowHeight));\n            mRightPane->setCoord(MyGUI::IntCoord(minLeftWidth, 0, windowWidth - minLeftWidth, windowHeight));\n        }\n        //if there's enough space for both panes\n        else\n        {\n            mLeftPane->setCoord(MyGUI::IntCoord(0, 0, static_cast<int>(leftPaneRatio*windowWidth), windowHeight));\n            mRightPane->setCoord(MyGUI::IntCoord(static_cast<int>(leftPaneRatio*windowWidth), 0, static_cast<int>(rightPaneRatio*windowWidth), windowHeight));\n        }\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mSkillView->setVisibleVScroll(false);\n        mSkillView->setCanvasSize (mSkillView->getWidth(), mSkillView->getCanvasSize().height);\n        mSkillView->setVisibleVScroll(true);\n    }\n\n    void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max)\n    {\n        MyGUI::ProgressBar* pt;\n        getWidget(pt, name);\n\n        std::stringstream out;\n        out << val << \"/\" << max;\n        setText(tname, out.str());\n\n        pt->setProgressRange(std::max(0, max));\n        pt->setProgressPosition(std::max(0, val));\n    }\n\n    void StatsWindow::setPlayerName(const std::string& playerName)\n    {\n        mMainWidget->castType<MyGUI::Window>()->setCaption(playerName);\n    }\n\n    void StatsWindow::setValue (const std::string& id, const MWMechanics::AttributeValue& value)\n    {\n        static const char *ids[] =\n        {\n            \"AttribVal1\", \"AttribVal2\", \"AttribVal3\", \"AttribVal4\", \"AttribVal5\",\n            \"AttribVal6\", \"AttribVal7\", \"AttribVal8\",\n            0\n        };\n\n        for (int i=0; ids[i]; ++i)\n            if (ids[i]==id)\n            {\n                setText (id, std::to_string(static_cast<int>(value.getModified())));\n\n                MyGUI::TextBox* box;\n                getWidget(box, id);\n\n                if (value.getModified()>value.getBase())\n                    box->_setWidgetState(\"increased\");\n                else if (value.getModified()<value.getBase())\n                    box->_setWidgetState(\"decreased\");\n                else\n                    box->_setWidgetState(\"normal\");\n\n                break;\n            }\n    }\n\n    void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value)\n    {\n        int current = static_cast<int>(value.getCurrent());\n        int modified = static_cast<int>(value.getModified());\n\n        // Fatigue can be negative\n        if (id != \"FBar\")\n            current = std::max(0, current);\n\n        setBar (id, id + \"T\", current, modified);\n\n        // health, magicka, fatigue tooltip\n        MyGUI::Widget* w;\n        std::string valStr =  MyGUI::utility::toString(current) + \" / \" + MyGUI::utility::toString(modified);\n        if (id == \"HBar\")\n        {\n            getWidget(w, \"Health\");\n            w->setUserString(\"Caption_HealthDescription\", \"#{sHealthDesc}\\n\" + valStr);\n        }\n        else if (id == \"MBar\")\n        {\n            getWidget(w, \"Magicka\");\n            w->setUserString(\"Caption_HealthDescription\", \"#{sMagDesc}\\n\" + valStr);\n        }\n        else if (id == \"FBar\")\n        {\n            getWidget(w, \"Fatigue\");\n            w->setUserString(\"Caption_HealthDescription\", \"#{sFatDesc}\\n\" + valStr);\n        }\n    }\n\n    void StatsWindow::setValue (const std::string& id, const std::string& value)\n    {\n        if (id==\"name\")\n            setPlayerName (value);\n        else if (id==\"race\")\n            setText (\"RaceText\", value);\n        else if (id==\"class\")\n            setText (\"ClassText\", value);\n    }\n\n    void StatsWindow::setValue (const std::string& id, int value)\n    {\n        if (id==\"level\")\n        {\n            std::ostringstream text;\n            text << value;\n            setText(\"LevelText\", text.str());\n        }\n    }\n\n    void setSkillProgress(MyGUI::Widget* w, float progress, int skillId)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        const MWWorld::ESMStore &esmStore =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        float progressRequirement = player.getClass().getNpcStats(player).getSkillProgressRequirement(skillId,\n            *esmStore.get<ESM::Class>().find(player.get<ESM::NPC>()->mBase->mClass));\n\n        // This is how vanilla MW displays the progress bar (I think). Note it's slightly inaccurate,\n        // due to the int casting in the skill levelup logic. Also the progress label could in rare cases\n        // reach 100% without the skill levelling up.\n        // Leaving the original display logic for now, for consistency with ess-imported savegames.\n        int progressPercent = int(float(progress) / float(progressRequirement) * 100.f + 0.5f);\n\n        w->setUserString(\"Caption_SkillProgressText\", MyGUI::utility::toString(progressPercent)+\"/100\");\n        w->setUserString(\"RangePosition_SkillProgress\", MyGUI::utility::toString(progressPercent));\n    }\n\n    void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)\n    {\n        mSkillValues[parSkill] = value;\n        std::pair<MyGUI::TextBox*, MyGUI::TextBox*> widgets = mSkillWidgetMap[(int)parSkill];\n        MyGUI::TextBox* valueWidget = widgets.second;\n        MyGUI::TextBox* nameWidget = widgets.first;\n        if (valueWidget && nameWidget)\n        {\n            int modified = value.getModified(), base = value.getBase();\n            std::string text = MyGUI::utility::toString(modified);\n            std::string state = \"normal\";\n            if (modified > base)\n                state = \"increased\";\n            else if (modified < base)\n                state = \"decreased\";\n\n            int widthBefore = valueWidget->getTextSize().width;\n\n            valueWidget->setCaption(text);\n            valueWidget->_setWidgetState(state);\n\n            int widthAfter = valueWidget->getTextSize().width;\n            if (widthBefore != widthAfter)\n            {\n                valueWidget->setCoord(valueWidget->getLeft() - (widthAfter-widthBefore), valueWidget->getTop(), valueWidget->getWidth() + (widthAfter-widthBefore), valueWidget->getHeight());\n                nameWidget->setSize(nameWidget->getWidth() - (widthAfter-widthBefore), nameWidget->getHeight());\n            }\n\n            if (value.getBase() < 100)\n            {\n                nameWidget->setUserString(\"Visible_SkillMaxed\", \"false\");\n                nameWidget->setUserString(\"UserData^Hidden_SkillMaxed\", \"true\");\n                nameWidget->setUserString(\"Visible_SkillProgressVBox\", \"true\");\n                nameWidget->setUserString(\"UserData^Hidden_SkillProgressVBox\", \"false\");\n\n                valueWidget->setUserString(\"Visible_SkillMaxed\", \"false\");\n                valueWidget->setUserString(\"UserData^Hidden_SkillMaxed\", \"true\");\n                valueWidget->setUserString(\"Visible_SkillProgressVBox\", \"true\");\n                valueWidget->setUserString(\"UserData^Hidden_SkillProgressVBox\", \"false\");\n\n                setSkillProgress(nameWidget, value.getProgress(), parSkill);\n                setSkillProgress(valueWidget, value.getProgress(), parSkill);\n            }\n            else\n            {\n                nameWidget->setUserString(\"Visible_SkillMaxed\", \"true\");\n                nameWidget->setUserString(\"UserData^Hidden_SkillMaxed\", \"false\");\n                nameWidget->setUserString(\"Visible_SkillProgressVBox\", \"false\");\n                nameWidget->setUserString(\"UserData^Hidden_SkillProgressVBox\", \"true\");\n\n                valueWidget->setUserString(\"Visible_SkillMaxed\", \"true\");\n                valueWidget->setUserString(\"UserData^Hidden_SkillMaxed\", \"false\");\n                valueWidget->setUserString(\"Visible_SkillProgressVBox\", \"false\");\n                valueWidget->setUserString(\"UserData^Hidden_SkillProgressVBox\", \"true\");\n            }\n        }\n    }\n\n    void StatsWindow::configureSkills (const std::vector<int>& major, const std::vector<int>& minor)\n    {\n        mMajorSkills = major;\n        mMinorSkills = minor;\n\n        // Update misc skills with the remaining skills not in major or minor\n        std::set<int> skillSet;\n        std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin()));\n        std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin()));\n        mMiscSkills.clear();\n        for (const int skill : ESM::Skill::sSkillIds)\n        {\n            if (skillSet.find(skill) == skillSet.end())\n                mMiscSkills.push_back(skill);\n        }\n\n        updateSkillArea();\n    }\n\n    void StatsWindow::onFrame (float dt)\n    {\n        NoDrop::onFrame(dt);\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        const MWMechanics::NpcStats &PCstats = player.getClass().getNpcStats(player);\n\n        // level progress\n        MyGUI::Widget* levelWidget;\n        for (int i=0; i<2; ++i)\n        {\n            int max = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"iLevelUpTotal\")->mValue.getInteger();\n            getWidget(levelWidget, i==0 ? \"Level_str\" : \"LevelText\");\n\n            levelWidget->setUserString(\"RangePosition_LevelProgress\", MyGUI::utility::toString(PCstats.getLevelProgress()));\n            levelWidget->setUserString(\"Range_LevelProgress\", MyGUI::utility::toString(max));\n            levelWidget->setUserString(\"Caption_LevelProgressText\", MyGUI::utility::toString(PCstats.getLevelProgress()) + \"/\"\n                                       + MyGUI::utility::toString(max));\n        }\n        std::stringstream detail;\n        for (int attribute = 0; attribute < ESM::Attribute::Length; ++attribute)\n        {\n            float mult = PCstats.getLevelupAttributeMultiplier(attribute);\n            mult = std::min(mult, 100 - PCstats.getAttribute(attribute).getBase());\n            if (mult > 1)\n                detail << (detail.str().empty() ? \"\" : \"\\n\") << \"#{\"\n                << MyGUI::TextIterator::toTagsString(ESM::Attribute::sGmstAttributeIds[attribute])\n                << \"} x\" << MyGUI::utility::toString(mult);\n        }\n        levelWidget->setUserString(\"Caption_LevelDetailText\", MyGUI::LanguageManager::getInstance().replaceTags(detail.str()));\n\n        setFactions(PCstats.getFactionRanks());\n        setExpelled(PCstats.getExpelled ());\n\n        const std::string &signId =\n            MWBase::Environment::get().getWorld()->getPlayer().getBirthSign();\n\n        setBirthSign(signId);\n        setReputation (PCstats.getReputation ());\n        setBounty (PCstats.getBounty ());\n\n        if (mChanged)\n            updateSkillArea();\n    }\n\n    void StatsWindow::setFactions (const FactionList& factions)\n    {\n        if (mFactions != factions)\n        {\n            mFactions = factions;\n            mChanged = true;\n        }\n    }\n\n    void StatsWindow::setExpelled (const std::set<std::string>& expelled)\n    {\n        if (mExpelled != expelled)\n        {\n            mExpelled = expelled;\n            mChanged = true;\n        }\n    }\n\n    void StatsWindow::setBirthSign (const std::string& signId)\n    {\n        if (signId != mBirthSignId)\n        {\n            mBirthSignId = signId;\n            mChanged = true;\n        }\n    }\n\n    void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        MyGUI::ImageBox* separator = mSkillView->createWidget<MyGUI::ImageBox>(\"MW_HLine\",\n            MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18),\n            MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);\n        separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel);\n        mSkillWidgets.push_back(separator);\n\n        coord1.top += separator->getHeight();\n        coord2.top += separator->getHeight();\n    }\n\n    void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        MyGUI::TextBox* groupWidget = mSkillView->createWidget<MyGUI::TextBox>(\"SandBrightText\",\n            MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height),\n            MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);\n        groupWidget->setCaption(label);\n        groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel);\n        mSkillWidgets.push_back(groupWidget);\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n        coord1.top += lineHeight;\n        coord2.top += lineHeight;\n    }\n\n    std::pair<MyGUI::TextBox*, MyGUI::TextBox*> StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        MyGUI::TextBox *skillNameWidget, *skillValueWidget;\n\n        skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>(\"SandText\", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);\n        skillNameWidget->setCaption(text);\n        skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel);\n\n        skillValueWidget = mSkillView->createWidget<MyGUI::TextBox>(\"SandTextRight\", coord2, MyGUI::Align::Right | MyGUI::Align::Top);\n        skillValueWidget->setCaption(value);\n        skillValueWidget->_setWidgetState(state);\n        skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel);\n\n        // resize dynamically according to text size\n        int textWidthPlusMargin = skillValueWidget->getTextSize().width + 12;\n        skillValueWidget->setCoord(coord2.left + coord2.width - textWidthPlusMargin, coord2.top, textWidthPlusMargin, coord2.height);\n        skillNameWidget->setSize(skillNameWidget->getSize() + MyGUI::IntSize(coord2.width - textWidthPlusMargin, 0));\n\n        mSkillWidgets.push_back(skillNameWidget);\n        mSkillWidgets.push_back(skillValueWidget);\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n        coord1.top += lineHeight;\n        coord2.top += lineHeight;\n\n        return std::make_pair(skillNameWidget, skillValueWidget);\n    }\n\n    MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        MyGUI::TextBox* skillNameWidget;\n\n        skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>(\"SandText\", coord1, MyGUI::Align::Default);\n\n        skillNameWidget->setCaption(text);\n        skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel);\n\n        int textWidth = skillNameWidget->getTextSize().width;\n        skillNameWidget->setSize(textWidth, skillNameWidget->getHeight());\n\n        mSkillWidgets.push_back(skillNameWidget);\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n        coord1.top += lineHeight;\n        coord2.top += lineHeight;\n\n        return skillNameWidget;\n    }\n\n    void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)\n    {\n        // Add a line separator if there are items above\n        if (!mSkillWidgets.empty())\n        {\n            addSeparator(coord1, coord2);\n        }\n\n        addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2);\n\n        for (const int skillId : skills)\n        {\n            if (skillId < 0 || skillId >= ESM::Skill::Length) // Skip unknown skill indexes\n                continue;\n            const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId];\n\n            const MWWorld::ESMStore &esmStore =\n                MWBase::Environment::get().getWorld()->getStore();\n\n            const ESM::Skill* skill = esmStore.get<ESM::Skill>().find(skillId);\n\n            std::string icon = \"icons\\\\k\\\\\" + ESM::Skill::sIconNames[skillId];\n\n            const ESM::Attribute* attr =\n                esmStore.get<ESM::Attribute>().find(skill->mData.mAttribute);\n\n            std::pair<MyGUI::TextBox*, MyGUI::TextBox*> widgets = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId),\n                \"\", \"normal\", coord1, coord2);\n            mSkillWidgetMap[skillId] = widgets;\n\n            for (int i=0; i<2; ++i)\n            {\n                mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"ToolTipType\", \"Layout\");\n                mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"ToolTipLayout\", \"SkillToolTip\");\n                mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"Caption_SkillName\", \"#{\"+skillNameId+\"}\");\n                mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"Caption_SkillDescription\", skill->mDescription);\n                mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"Caption_SkillAttribute\", \"#{sGoverningAttribute}: #{\" + attr->mName + \"}\");\n                mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"ImageTexture_SkillImage\", icon);\n                mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"Range_SkillProgress\", \"100\");\n            }\n\n            setValue(static_cast<ESM::Skill::SkillEnum>(skillId), mSkillValues.find(skillId)->second);\n        }\n    }\n\n    void StatsWindow::updateSkillArea()\n    {\n        mChanged = false;\n\n        for (MyGUI::Widget* widget : mSkillWidgets)\n        {\n            MyGUI::Gui::getInstance().destroyWidget(widget);\n        }\n        mSkillWidgets.clear();\n\n        const int valueSize = 40;\n        MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18);\n        MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height);\n\n        if (!mMajorSkills.empty())\n            addSkills(mMajorSkills, \"sSkillClassMajor\", \"Major Skills\", coord1, coord2);\n\n        if (!mMinorSkills.empty())\n            addSkills(mMinorSkills, \"sSkillClassMinor\", \"Minor Skills\", coord1, coord2);\n\n        if (!mMiscSkills.empty())\n            addSkills(mMiscSkills, \"sSkillClassMisc\", \"Misc Skills\", coord1, coord2);\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        const MWWorld::ESMStore &store = world->getStore();\n        const ESM::NPC *player =\n            world->getPlayerPtr().get<ESM::NPC>()->mBase;\n\n        // race tooltip\n        const ESM::Race* playerRace = store.get<ESM::Race>().find(player->mRace);\n\n        MyGUI::Widget* raceWidget;\n        getWidget(raceWidget, \"RaceText\");\n        ToolTips::createRaceToolTip(raceWidget, playerRace);\n        getWidget(raceWidget, \"Race_str\");\n        ToolTips::createRaceToolTip(raceWidget, playerRace);\n\n        // class tooltip\n        MyGUI::Widget* classWidget;\n\n        const ESM::Class *playerClass =\n            store.get<ESM::Class>().find(player->mClass);\n\n        getWidget(classWidget, \"ClassText\");\n        ToolTips::createClassToolTip(classWidget, *playerClass);\n        getWidget(classWidget, \"Class_str\");\n        ToolTips::createClassToolTip(classWidget, *playerClass);\n\n        if (!mFactions.empty())\n        {\n            MWWorld::Ptr playerPtr = MWMechanics::getPlayer();\n            const MWMechanics::NpcStats &PCstats = playerPtr.getClass().getNpcStats(playerPtr);\n            const std::set<std::string> &expelled = PCstats.getExpelled();\n\n            bool firstFaction=true;\n            for (auto& factionPair : mFactions)\n            {\n                const std::string& factionId = factionPair.first;\n                const ESM::Faction *faction =\n                    store.get<ESM::Faction>().find(factionId);\n                if (faction->mData.mIsHidden == 1)\n                    continue;\n\n                if (firstFaction)\n                {\n                    // Add a line separator if there are items above\n                    if (!mSkillWidgets.empty())\n                        addSeparator(coord1, coord2);\n\n                    addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sFaction\", \"Faction\"), coord1, coord2);\n\n                    firstFaction = false;\n                }\n\n                MyGUI::Widget* w = addItem(faction->mName, coord1, coord2);\n\n                std::string text;\n\n                text += std::string(\"#{fontcolourhtml=header}\") + faction->mName;\n\n                if (expelled.find(factionId) != expelled.end())\n                    text += \"\\n#{fontcolourhtml=normal}#{sExpelled}\";\n                else\n                {\n                    int rank = factionPair.second;\n                    rank = std::max(0, std::min(9, rank));\n                    text += std::string(\"\\n#{fontcolourhtml=normal}\") + faction->mRanks[rank];\n\n                    if (rank < 9)\n                    {\n                        // player doesn't have max rank yet\n                        text += std::string(\"\\n\\n#{fontcolourhtml=header}#{sNextRank} \") + faction->mRanks[rank+1];\n\n                        ESM::RankData rankData = faction->mData.mRankData[rank+1];\n                        const ESM::Attribute* attr1 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[0]);\n                        const ESM::Attribute* attr2 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[1]);\n\n                        text += \"\\n#{fontcolourhtml=normal}#{\" + attr1->mName + \"}: \" + MyGUI::utility::toString(rankData.mAttribute1)\n                                + \", #{\" + attr2->mName + \"}: \" + MyGUI::utility::toString(rankData.mAttribute2);\n\n                        text += \"\\n\\n#{fontcolourhtml=header}#{sFavoriteSkills}\";\n                        text += \"\\n#{fontcolourhtml=normal}\";\n                        bool firstSkill = true;\n                        for (int i=0; i<7; ++i)\n                        {\n                            if (faction->mData.mSkills[i] != -1)\n                            {\n                                if (!firstSkill)\n                                    text += \", \";\n\n                                firstSkill = false;\n\n                                text += \"#{\"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+\"}\";\n                            }\n                        }\n\n                        text += \"\\n\";\n\n                        if (rankData.mPrimarySkill > 0)\n                            text += \"\\n#{sNeedOneSkill} \" + MyGUI::utility::toString(rankData.mPrimarySkill);\n                        if (rankData.mFavouredSkill > 0)\n                            text += \" #{sand} #{sNeedTwoSkills} \" + MyGUI::utility::toString(rankData.mFavouredSkill);\n                    }\n                }\n\n                w->setUserString(\"ToolTipType\", \"Layout\");\n                w->setUserString(\"ToolTipLayout\", \"FactionToolTip\");\n                w->setUserString(\"Caption_FactionText\", text);\n            }\n        }\n\n        if (!mBirthSignId.empty())\n        {\n            // Add a line separator if there are items above\n            if (!mSkillWidgets.empty())\n                addSeparator(coord1, coord2);\n\n            addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sBirthSign\", \"Sign\"), coord1, coord2);\n            const ESM::BirthSign *sign =\n                store.get<ESM::BirthSign>().find(mBirthSignId);\n            MyGUI::Widget* w = addItem(sign->mName, coord1, coord2);\n\n            ToolTips::createBirthsignToolTip(w, mBirthSignId);\n        }\n\n        // Add a line separator if there are items above\n        if (!mSkillWidgets.empty())\n            addSeparator(coord1, coord2);\n\n        addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sReputation\", \"Reputation\"),\n                    MyGUI::utility::toString(static_cast<int>(mReputation)), \"normal\", coord1, coord2);\n\n        for (int i=0; i<2; ++i)\n        {\n            mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"ToolTipType\", \"Layout\");\n            mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"ToolTipLayout\", \"TextToolTip\");\n            mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"Caption_Text\", \"#{sSkillsMenuReputationHelp}\");\n        }\n\n        addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sBounty\", \"Bounty\"),\n                    MyGUI::utility::toString(static_cast<int>(mBounty)), \"normal\", coord1, coord2);\n\n        for (int i=0; i<2; ++i)\n        {\n            mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"ToolTipType\", \"Layout\");\n            mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"ToolTipLayout\", \"TextToolTip\");\n            mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString(\"Caption_Text\", \"#{sCrimeHelp}\");\n        }\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mSkillView->setVisibleVScroll(false);\n        mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top));\n        mSkillView->setVisibleVScroll(true);\n    }\n\n    void StatsWindow::onPinToggled()\n    {\n        Settings::Manager::setBool(\"stats pin\", \"Windows\", mPinned);\n\n        MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned);\n    }\n\n    void StatsWindow::onTitleDoubleClicked()\n    {\n        if (MyGUI::InputManager::getInstance().isShiftPressed())\n        {\n            MWBase::Environment::get().getWindowManager()->toggleMaximized(this);\n            MyGUI::Window* t = mMainWidget->castType<MyGUI::Window>();\n            onWindowResize(t);\n        }\n        else if (!mPinned)\n            MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/statswindow.hpp",
    "content": "#ifndef MWGUI_STATS_WINDOW_H\n#define MWGUI_STATS_WINDOW_H\n\n#include \"statswatcher.hpp\"\n#include \"windowpinnablebase.hpp\"\n\nnamespace MWGui\n{\n    class StatsWindow : public WindowPinnableBase, public NoDrop, public StatsListener\n    {\n        public:\n            typedef std::map<std::string, int> FactionList;\n\n            typedef std::vector<int> SkillList;\n\n            StatsWindow(DragAndDrop* drag);\n\n            /// automatically updates all the data in the stats window, but only if it has changed.\n            void onFrame(float dt) override;\n\n            void setBar(const std::string& name, const std::string& tname, int val, int max);\n            void setPlayerName(const std::string& playerName);\n\n            /// Set value for the given ID.\n            void setValue (const std::string& id, const MWMechanics::AttributeValue& value) override;\n            void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value) override;\n            void setValue (const std::string& id, const std::string& value) override;\n            void setValue (const std::string& id, int value) override;\n            void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override;\n            void configureSkills(const SkillList& major, const SkillList& minor) override;\n\n            void setReputation (int reputation) { if (reputation != mReputation) mChanged = true; this->mReputation = reputation; }\n            void setBounty (int bounty) { if (bounty != mBounty) mChanged = true; this->mBounty = bounty; }\n            void updateSkillArea();\n\n            void onOpen() override { onWindowResize(mMainWidget->castType<MyGUI::Window>()); }\n\n        private:\n            void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n            void addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n            void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n            std::pair<MyGUI::TextBox*, MyGUI::TextBox*> addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n            MyGUI::Widget* addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);\n\n            void setFactions (const FactionList& factions);\n            void setExpelled (const std::set<std::string>& expelled);\n            void setBirthSign (const std::string &signId);\n\n            void onWindowResize(MyGUI::Window* window);\n            void onMouseWheel(MyGUI::Widget* _sender, int _rel);\n\n            MyGUI::Widget* mLeftPane;\n            MyGUI::Widget* mRightPane;\n\n            MyGUI::ScrollView* mSkillView;\n\n            SkillList mMajorSkills, mMinorSkills, mMiscSkills;\n            std::map<int, MWMechanics::SkillValue > mSkillValues;\n            std::map<int, std::pair<MyGUI::TextBox*, MyGUI::TextBox*> > mSkillWidgetMap;\n            std::map<std::string, MyGUI::Widget*> mFactionWidgetMap;\n            FactionList mFactions; ///< Stores a list of factions and the current rank\n            std::string mBirthSignId;\n            int mReputation, mBounty;\n            std::vector<MyGUI::Widget*> mSkillWidgets; //< Skills and other information\n            std::set<std::string> mExpelled;\n\n            bool mChanged;\n            const int mMinFullWidth;\n\n        protected:\n            void onPinToggled() override;\n            void onTitleDoubleClicked() override;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/textcolours.cpp",
    "content": "#include \"textcolours.hpp\"\n\n#include <MyGUI_LanguageManager.h>\n\n#include <string>\n\nnamespace MWGui\n{\n    MyGUI::Colour getTextColour(const std::string& type)\n    {\n        return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags(\"#{fontcolour=\" + type + \"}\"));\n    }\n\n    void TextColours::loadColours()\n    {\n        header = getTextColour(\"header\");\n        normal = getTextColour(\"normal\");\n        notify = getTextColour(\"notify\");\n\n        link = getTextColour(\"link\");\n        linkOver = getTextColour(\"link_over\");\n        linkPressed = getTextColour(\"link_pressed\");\n\n        answer = getTextColour(\"answer\");\n        answerOver = getTextColour(\"answer_over\");\n        answerPressed = getTextColour(\"answer_pressed\");\n\n        journalLink = getTextColour(\"journal_link\");\n        journalLinkOver = getTextColour(\"journal_link_over\");\n        journalLinkPressed = getTextColour(\"journal_link_pressed\");\n\n        journalTopic = getTextColour(\"journal_topic\");\n        journalTopicOver = getTextColour(\"journal_topic_over\");\n        journalTopicPressed = getTextColour(\"journal_topic_pressed\");\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/textcolours.hpp",
    "content": "#ifndef MWGUI_TEXTCOLORS_H\n#define MWGUI_TEXTCOLORS_H\n\n#include <MyGUI_Colour.h>\n\nnamespace MWGui\n{\n    struct TextColours\n    {\n        MyGUI::Colour header;\n        MyGUI::Colour normal;\n        MyGUI::Colour notify;\n\n        MyGUI::Colour link;\n        MyGUI::Colour linkOver;\n        MyGUI::Colour linkPressed;\n\n        MyGUI::Colour answer;\n        MyGUI::Colour answerOver;\n        MyGUI::Colour answerPressed;\n\n        MyGUI::Colour journalLink;\n        MyGUI::Colour journalLinkOver;\n        MyGUI::Colour journalLinkPressed;\n\n        MyGUI::Colour journalTopic;\n        MyGUI::Colour journalTopicOver;\n        MyGUI::Colour journalTopicPressed;\n\n    public:\n        void loadColours();\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/textinput.cpp",
    "content": "#include \"textinput.hpp\"\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include <MyGUI_EditBox.h>\n#include <MyGUI_Button.h>\n\nnamespace MWGui\n{\n\n    TextInputDialog::TextInputDialog()\n      : WindowModal(\"openmw_text_input.layout\")\n    {\n        // Centre dialog\n        center();\n\n        getWidget(mTextEdit, \"TextEdit\");\n        mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted);\n\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked);\n\n        // Make sure the edit box has focus\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);\n    }\n\n    void TextInputDialog::setNextButtonShow(bool shown)\n    {\n        MyGUI::Button* okButton;\n        getWidget(okButton, \"OKButton\");\n\n        if (shown)\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sNext\", \"\"));\n        else\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOK\", \"\"));\n    }\n\n    void TextInputDialog::setTextLabel(const std::string &label)\n    {\n        setText(\"LabelT\", label);\n    }\n\n    void TextInputDialog::onOpen()\n    {\n        WindowModal::onOpen();\n        // Make sure the edit box has focus\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);\n    }\n\n    // widget controls\n\n    void TextInputDialog::onOkClicked(MyGUI::Widget* _sender)\n    {\n        if (mTextEdit->getCaption() == \"\")\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage37}\");\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget (mTextEdit);\n        }\n        else\n            eventDone(this);\n    }\n\n    void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender)\n    {\n        onOkClicked(_sender);\n\n        // To do not spam onTextAccepted() again and again\n        MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);\n    }\n\n    std::string TextInputDialog::getTextInput() const\n    {\n        return mTextEdit->getCaption();\n    }\n\n    void TextInputDialog::setTextInput(const std::string &text)\n    {\n        mTextEdit->setCaption(text);\n    }\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/textinput.hpp",
    "content": "#ifndef MWGUI_TEXT_INPUT_H\n#define MWGUI_TEXT_INPUT_H\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n    class TextInputDialog : public WindowModal\n    {\n    public:\n        TextInputDialog();\n\n        std::string getTextInput() const;\n        void setTextInput(const std::string &text);\n\n        void setNextButtonShow(bool shown);\n        void setTextLabel(const std::string &label);\n        void onOpen() override;\n\n        bool exit() override { return false; }\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n    protected:\n        void onOkClicked(MyGUI::Widget* _sender);\n        void onTextAccepted(MyGUI::Edit* _sender);\n\n    private:\n        MyGUI::EditBox* mTextEdit;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/timeadvancer.cpp",
    "content": "#include \"timeadvancer.hpp\"\n\nnamespace MWGui\n{\n    TimeAdvancer::TimeAdvancer(float delay)\n        : mRunning(false),\n          mCurHour(0),\n          mHours(1),\n          mInterruptAt(-1),\n          mDelay(delay),\n          mRemainingTime(delay)\n    {\n    }\n\n    void TimeAdvancer::run(int hours, int interruptAt)\n    {\n        mHours = hours;\n        mCurHour = 0;\n        mInterruptAt = interruptAt;\n        mRemainingTime = mDelay;\n\n        mRunning = true;\n    }\n\n    void TimeAdvancer::stop()\n    {\n        mRunning = false;\n    }\n\n    void TimeAdvancer::onFrame(float dt)\n    {\n        if (!mRunning)\n            return;\n\n        if (mCurHour == mInterruptAt)\n        {\n            stop();\n            eventInterrupted();\n            return;\n        }\n\n        mRemainingTime -= dt;\n\n        while (mRemainingTime <= 0)\n        {\n            mRemainingTime += mDelay;\n            ++mCurHour;\n\n            if (mCurHour <= mHours)\n                eventProgressChanged(mCurHour, mHours);\n            else\n            {\n                stop();\n                eventFinished();\n                return;\n            }\n\n        }\n    }\n\n    int TimeAdvancer::getHours() const\n    {\n        return mHours;\n    }\n\n    bool TimeAdvancer::isRunning() const\n    {\n        return mRunning;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/timeadvancer.hpp",
    "content": "#ifndef MWGUI_TIMEADVANCER_H\n#define MWGUI_TIMEADVANCER_H\n\n#include <MyGUI_Widget.h>\n\nnamespace MWGui\n{\n    class TimeAdvancer\n    {\n        public:\n            TimeAdvancer(float delay);\n\n            void run(int hours, int interruptAt=-1);\n            void stop();\n            void onFrame(float dt);\n\n            int getHours() const;\n            bool isRunning() const;\n\n            // signals\n            typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n            typedef MyGUI::delegates::CMultiDelegate2<int, int> EventHandle_IntInt;\n\n            EventHandle_IntInt eventProgressChanged;\n            EventHandle_Void eventInterrupted;\n            EventHandle_Void eventFinished;\n\n        private:\n            bool mRunning;\n\n            int mCurHour;\n            int mHours;\n            int mInterruptAt;\n\n            float mDelay;\n            float mRemainingTime;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/tooltips.cpp",
    "content": "#include \"tooltips.hpp\"\n\n#include <iomanip>\n\n#include <MyGUI_Gui.h>\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_ImageBox.h>\n\n#include <components/settings/settings.hpp>\n#include <components/widgets/box.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwmechanics/spellutil.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"mapwindow.hpp\"\n#include \"inventorywindow.hpp\"\n\n#include \"itemmodel.hpp\"\n\nnamespace MWGui\n{\n    std::string ToolTips::sSchoolNames[] = {\"#{sSchoolAlteration}\", \"#{sSchoolConjuration}\", \"#{sSchoolDestruction}\", \"#{sSchoolIllusion}\", \"#{sSchoolMysticism}\", \"#{sSchoolRestoration}\"};\n\n    ToolTips::ToolTips() :\n        Layout(\"openmw_tooltips.layout\")\n        , mFocusToolTipX(0.0)\n        , mFocusToolTipY(0.0)\n        , mHorizontalScrollIndex(0)\n        , mDelay(0.0)\n        , mRemainingDelay(0.0)\n        , mLastMouseX(0)\n        , mLastMouseY(0)\n        , mEnabled(true)\n        , mFullHelp(false)\n        , mShowOwned(0)\n        , mFrameDuration(0.f)\n    {\n        getWidget(mDynamicToolTipBox, \"DynamicToolTipBox\");\n\n        mDynamicToolTipBox->setVisible(false);\n\n        // turn off mouse focus so that getMouseFocusWidget returns the correct widget,\n        // even if the mouse is over the tooltip\n        mDynamicToolTipBox->setNeedMouseFocus(false);\n        mMainWidget->setNeedMouseFocus(false);\n\n        mDelay = Settings::Manager::getFloat(\"tooltip delay\", \"GUI\");\n        mRemainingDelay = mDelay;\n\n        for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i)\n        {\n            mMainWidget->getChildAt(i)->setVisible(false);\n        }\n        \n        mShowOwned = Settings::Manager::getInt(\"show owned\", \"Game\");\n    }\n\n    void ToolTips::setEnabled(bool enabled)\n    {\n        mEnabled = enabled;\n    }\n\n    void ToolTips::onFrame(float frameDuration)\n    {\n        mFrameDuration = frameDuration;\n    }\n\n    void ToolTips::update(float frameDuration)\n    {\n\n        while (mDynamicToolTipBox->getChildCount())\n        {\n            MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0));\n        }\n\n        // start by hiding everything\n        for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i)\n        {\n            mMainWidget->getChildAt(i)->setVisible(false);\n        }\n\n        const MyGUI::IntSize &viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n\n        if (!mEnabled)\n        {\n            return;\n        }\n\n        MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n        bool guiMode = winMgr->isGuiMode();\n\n        if (guiMode)\n        {\n            if (!winMgr->getCursorVisible())\n                return;\n            const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition();\n\n            if (winMgr->getWorldMouseOver() &&\n                (winMgr->isConsoleMode() ||\n                (winMgr->getMode() == GM_Container) ||\n                (winMgr->getMode() == GM_Inventory)))\n            {\n                if (mFocusObject.isEmpty ())\n                    return;\n\n                const MWWorld::Class& objectclass = mFocusObject.getClass();\n\n                MyGUI::IntSize tooltipSize;\n                if (!objectclass.hasToolTip(mFocusObject) && winMgr->isConsoleMode())\n                {\n                    setCoord(0, 0, 300, 300);\n                    mDynamicToolTipBox->setVisible(true);\n                    ToolTipInfo info;\n                    info.caption = mFocusObject.getClass().getName(mFocusObject);\n                    if (info.caption.empty())\n                        info.caption=mFocusObject.getCellRef().getRefId();\n                    info.icon=\"\";\n                    tooltipSize = createToolTip(info, checkOwned());\n                }\n                else\n                    tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true);\n\n                MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition();\n                position(tooltipPosition, tooltipSize, viewSize);\n\n                setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height);\n            }\n\n            else\n            {\n                if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY)\n                {\n                    mRemainingDelay -= frameDuration;\n                }\n                else\n                {\n                    mHorizontalScrollIndex = 0;\n                    mRemainingDelay = mDelay;\n                }\n                mLastMouseX = mousePos.left;\n                mLastMouseY = mousePos.top;\n\n\n                if (mRemainingDelay > 0)\n                    return;\n\n                MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget();\n                if (focus == nullptr)\n                    return;\n\n                MyGUI::IntSize tooltipSize;\n\n                // try to go 1 level up until there is a widget that has tooltip\n                // this is necessary because some skin elements are actually separate widgets\n                int i=0;\n                while (!focus->isUserString(\"ToolTipType\"))\n                {\n                    focus = focus->getParent();\n                    if (!focus)\n                        return;\n                    ++i;\n                }\n\n                std::string type = focus->getUserString(\"ToolTipType\");\n\n                if (type == \"\")\n                {\n                    return;\n                }\n\n\n                // special handling for markers on the local map: the tooltip should only be visible\n                // if the marker is not hidden due to the fog of war.\n                if (type == \"MapMarker\")\n                {\n                    LocalMapBase::MarkerUserData data = *focus->getUserData<LocalMapBase::MarkerUserData>();\n\n                    if (!data.isPositionExplored())\n                        return;\n\n                    ToolTipInfo info;\n                    info.text = data.caption;\n                    info.notes = data.notes;\n                    tooltipSize = createToolTip(info);\n                }\n                else if (type == \"ItemPtr\")\n                {\n                    mFocusObject = *focus->getUserData<MWWorld::Ptr>();\n                    if (!mFocusObject)\n                        return;\n\n                    tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, checkOwned());\n                }\n                else if (type == \"ItemModelIndex\")\n                {\n                    std::pair<ItemModel::ModelIndex, ItemModel*> pair = *focus->getUserData<std::pair<ItemModel::ModelIndex, ItemModel*> >();\n                    mFocusObject = pair.second->getItem(pair.first).mBase;\n                    bool isAllowedToUse = pair.second->allowedToUseItems();\n                    tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false, !isAllowedToUse);\n                }\n                else if (type == \"ToolTipInfo\")\n                {\n                    tooltipSize = createToolTip(*focus->getUserData<MWGui::ToolTipInfo>());\n                }\n                else if (type == \"AvatarItemSelection\")\n                {\n                    MyGUI::IntCoord avatarPos = focus->getAbsoluteCoord();\n                    MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top);\n                    MWWorld::Ptr item = winMgr->getInventoryWindow ()->getAvatarSelectedItem (relMousePos.left, relMousePos.top);\n\n                    mFocusObject = item;\n                    if (!mFocusObject.isEmpty ())\n                        tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false);\n                }\n                else if (type == \"Spell\")\n                {\n                    ToolTipInfo info;\n\n                    const ESM::Spell *spell =\n                        MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(focus->getUserString(\"Spell\"));\n                    info.caption = spell->mName;\n                    Widgets::SpellEffectList effects;\n                    for (const ESM::ENAMstruct& spellEffect : spell->mEffects.mList)\n                    {\n                        Widgets::SpellEffectParams params;\n                        params.mEffectID = spellEffect.mEffectID;\n                        params.mSkill = spellEffect.mSkill;\n                        params.mAttribute = spellEffect.mAttribute;\n                        params.mDuration = spellEffect.mDuration;\n                        params.mMagnMin = spellEffect.mMagnMin;\n                        params.mMagnMax = spellEffect.mMagnMax;\n                        params.mRange = spellEffect.mRange;\n                        params.mArea = spellEffect.mArea;\n                        params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability);\n                        params.mNoTarget = false;\n                        effects.push_back(params);\n                    }\n                    if (MWMechanics::spellIncreasesSkill(spell)) // display school of spells that contribute to skill progress\n                    {\n                        MWWorld::Ptr player = MWMechanics::getPlayer();\n                        int school = MWMechanics::getSpellSchool(spell, player);\n                        info.text = \"#{sSchool}: \" + sSchoolNames[school];\n                    }\n                    std::string cost = focus->getUserString(\"SpellCost\");\n                    if (cost != \"\" && cost != \"0\")\n                        info.text += MWGui::ToolTips::getValueString(spell->mData.mCost, \"#{sCastCost}\");\n                    info.effects = effects;\n                    tooltipSize = createToolTip(info);\n                }\n                else if (type == \"Layout\")\n                {\n                    // tooltip defined in the layout\n                    MyGUI::Widget* tooltip;\n                    getWidget(tooltip, focus->getUserString(\"ToolTipLayout\"));\n\n                    tooltip->setVisible(true);\n\n                    std::map<std::string, std::string> userStrings = focus->getUserStrings();\n                    for (auto& userStringPair : userStrings)\n                    {\n                        size_t underscorePos = userStringPair.first.find('_');\n                        if (underscorePos == std::string::npos)\n                            continue;\n                        std::string key = userStringPair.first.substr(0, underscorePos);\n                        std::string widgetName = userStringPair.first.substr(underscorePos+1, userStringPair.first.size()-(underscorePos+1));\n\n                        type = \"Property\";\n                        size_t caretPos = key.find('^');\n                        if (caretPos != std::string::npos)\n                        {\n                            type = key.substr(0, caretPos);\n                            key.erase(key.begin(), key.begin() + caretPos + 1);\n                        }\n\n                        MyGUI::Widget* w;\n                        getWidget(w, widgetName);\n                        if (type == \"Property\")\n                            w->setProperty(key, userStringPair.second);\n                        else if (type == \"UserData\")\n                            w->setUserString(key, userStringPair.second);\n                    }\n\n                    tooltipSize = tooltip->getSize();\n\n                    tooltip->setCoord(0, 0, tooltipSize.width, tooltipSize.height);\n                }\n                else\n                    throw std::runtime_error (\"unknown tooltip type\");\n\n                MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition();\n\n                position(tooltipPosition, tooltipSize, viewSize);\n\n                setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height);\n            }\n        }\n        else\n        {\n            if (!mFocusObject.isEmpty())\n            {\n                MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true, checkOwned());\n\n                setCoord(viewSize.width/2 - tooltipSize.width/2,\n                        std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)),\n                        tooltipSize.width,\n                        tooltipSize.height);\n\n                mDynamicToolTipBox->setVisible(true);\n            }\n        }\n    }\n\n    void ToolTips::position(MyGUI::IntPoint& position, MyGUI::IntSize size, MyGUI::IntSize viewportSize)\n    {\n        position += MyGUI::IntPoint(0, 32)\n        - MyGUI::IntPoint(static_cast<int>(MyGUI::InputManager::getInstance().getMousePosition().left / float(viewportSize.width) * size.width), 0);\n\n        if ((position.left + size.width) > viewportSize.width)\n        {\n            position.left = viewportSize.width - size.width;\n        }\n        if ((position.top + size.height) > viewportSize.height)\n        {\n            position.top = MyGUI::InputManager::getInstance().getMousePosition().top - size.height - 8;\n        }\n    }\n\n    void ToolTips::clear()\n    {\n        mFocusObject = MWWorld::Ptr();\n\n        while (mDynamicToolTipBox->getChildCount())\n        {\n            MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0));\n        }\n\n        for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i)\n        {\n            mMainWidget->getChildAt(i)->setVisible(false);\n        }\n    }\n\n    void ToolTips::setFocusObject(const MWWorld::Ptr& focus)\n    {\n        mFocusObject = focus;\n\n        update(mFrameDuration);\n    }\n\n    MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image, bool isOwned)\n    {\n        // this the maximum width of the tooltip before it starts word-wrapping\n        setCoord(0, 0, 300, 300);\n\n        MyGUI::IntSize tooltipSize;\n\n        const MWWorld::Class& object = mFocusObject.getClass();\n        if (!object.hasToolTip(mFocusObject))\n        {\n            mDynamicToolTipBox->setVisible(false);\n        }\n        else\n        {\n            mDynamicToolTipBox->setVisible(true);\n\n            ToolTipInfo info = object.getToolTipInfo(mFocusObject, count);\n            if (!image)\n                info.icon = \"\";\n            tooltipSize = createToolTip(info, isOwned);\n        }\n\n        return tooltipSize;\n    }\n    \n    bool ToolTips::checkOwned()\n    {\n        if(mFocusObject.isEmpty())\n            return false;\n\n        MWWorld::Ptr ptr = MWMechanics::getPlayer();\n        MWWorld::Ptr victim;\n\n        MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();\n        return !mm->isAllowedToUse(ptr, mFocusObject, victim);\n    }\n\n    MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isOwned)\n    {\n        mDynamicToolTipBox->setVisible(true);\n        \n        if((mShowOwned == 1 || mShowOwned == 3) && isOwned)\n            mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? \"HUD_Box_NoTransp_Owned\" : \"HUD_Box_Owned\");\n        else\n            mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? \"HUD_Box_NoTransp\" : \"HUD_Box\");\n\n        std::string caption = info.caption;\n        std::string image = info.icon;\n        int imageSize = (image != \"\") ? info.imageSize : 0;\n        std::string text = info.text;\n\n        // remove the first newline (easier this way)\n        if (text.size() > 0 && text[0] == '\\n')\n            text.erase(0, 1);\n\n        const ESM::Enchantment* enchant = nullptr;\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n        if (info.enchant != \"\")\n        {\n            enchant = store.get<ESM::Enchantment>().search(info.enchant);\n            if (enchant)\n            {\n                if (enchant->mData.mType == ESM::Enchantment::CastOnce)\n                    text += \"\\n#{sItemCastOnce}\";\n                else if (enchant->mData.mType == ESM::Enchantment::WhenStrikes)\n                    text += \"\\n#{sItemCastWhenStrikes}\";\n                else if (enchant->mData.mType == ESM::Enchantment::WhenUsed)\n                    text += \"\\n#{sItemCastWhenUsed}\";\n                else if (enchant->mData.mType == ESM::Enchantment::ConstantEffect)\n                    text += \"\\n#{sItemCastConstant}\";\n            }\n        }\n\n        // this the maximum width of the tooltip before it starts word-wrapping\n        setCoord(0, 0, 300, 300);\n\n        const MyGUI::IntPoint padding(8, 8);\n\n        const int imageCaptionHPadding = (caption != \"\" ? 8 : 0);\n        const int imageCaptionVPadding = (caption != \"\" ? 4 : 0);\n\n        const int maximumWidth = MyGUI::RenderManager::getInstance().getViewSize().width - imageCaptionHPadding * 2;\n\n        std::string realImage = MWBase::Environment::get().getWindowManager()->correctIconPath(image);\n\n        Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>(\"NormalText\", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, \"ToolTipCaption\");\n        captionWidget->setEditStatic(true);\n        captionWidget->setNeedKeyFocus(false);\n        captionWidget->setCaptionWithReplacing(caption);\n        MyGUI::IntSize captionSize = captionWidget->getTextSize();\n\n        int captionHeight = std::max(caption != \"\" ? captionSize.height : 0, imageSize);\n\n        Gui::EditBox* textWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>(\"SandText\", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, \"ToolTipText\");\n        textWidget->setEditStatic(true);\n        textWidget->setEditMultiLine(true);\n        textWidget->setEditWordWrap(info.wordWrap);\n        textWidget->setCaptionWithReplacing(text);\n        textWidget->setTextAlign(MyGUI::Align::HCenter | MyGUI::Align::Top);\n        textWidget->setNeedKeyFocus(false);\n        MyGUI::IntSize textSize = textWidget->getTextSize();\n\n        captionSize += MyGUI::IntSize(imageSize, 0); // adjust for image\n        MyGUI::IntSize totalSize = MyGUI::IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != \"\") ? imageCaptionHPadding : 0)),maximumWidth),\n            ((text != \"\") ? textSize.height + imageCaptionVPadding : 0) + captionHeight );\n\n        for (const std::string& note : info.notes)\n        {\n            MyGUI::ImageBox* icon = mDynamicToolTipBox->createWidget<MyGUI::ImageBox>(\"MarkerButton\",\n                MyGUI::IntCoord(padding.left, totalSize.height+padding.top, 8, 8), MyGUI::Align::Default);\n            icon->setColour(MyGUI::Colour(1.0f, 0.3f, 0.3f));\n            Gui::EditBox* edit = mDynamicToolTipBox->createWidget<Gui::EditBox>(\"SandText\",\n                MyGUI::IntCoord(padding.left+8+4, totalSize.height+padding.top, 300-padding.left-8-4, 300-totalSize.height),\n                                                                                    MyGUI::Align::Default);\n            edit->setEditMultiLine(true);\n            edit->setEditWordWrap(true);\n            edit->setCaption(note);\n            edit->setSize(edit->getWidth(), edit->getTextSize().height);\n            icon->setPosition(icon->getLeft(),(edit->getTop()+edit->getBottom())/2-icon->getHeight()/2);\n            totalSize.height += std::max(edit->getHeight(), icon->getHeight());\n            totalSize.width = std::max(totalSize.width, edit->getWidth()+8+4);\n        }\n\n        if (!info.effects.empty())\n        {\n            MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget<MyGUI::Widget>(\"\",\n                MyGUI::IntCoord(padding.left, totalSize.height, 300-padding.left, 300-totalSize.height),\n                MyGUI::Align::Stretch);\n\n            MyGUI::IntCoord coord(0, 6, totalSize.width, 24);\n\n            Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget<Widgets::MWEffectList>\n                (\"MW_StatName\", coord, MyGUI::Align::Default);\n            effectsWidget->setEffectList(info.effects);\n\n            std::vector<MyGUI::Widget*> effectItems;\n            int flag = info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0;\n            flag |= info.isIngredient ? Widgets::MWEffectList::EF_NoMagnitude : 0;\n            effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, flag);\n            totalSize.height += coord.top-6;\n            totalSize.width = std::max(totalSize.width, coord.width);\n        }\n\n        if (enchant)\n        {\n            MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget<MyGUI::Widget>(\"\",\n                MyGUI::IntCoord(padding.left, totalSize.height, 300-padding.left, 300-totalSize.height),\n                MyGUI::Align::Stretch);\n\n            MyGUI::IntCoord coord(0, 6, totalSize.width, 24);\n\n            Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget<Widgets::MWEffectList>\n                (\"MW_StatName\", coord, MyGUI::Align::Default);\n            enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects));\n\n            std::vector<MyGUI::Widget*> enchantEffectItems;\n            int flag = (enchant->mData.mType == ESM::Enchantment::ConstantEffect) ? Widgets::MWEffectList::EF_Constant : 0;\n            enchantWidget->createEffectWidgets(enchantEffectItems, enchantArea, coord, true, flag);\n            totalSize.height += coord.top-6;\n            totalSize.width = std::max(totalSize.width, coord.width);\n\n            if (enchant->mData.mType == ESM::Enchantment::WhenStrikes\n                || enchant->mData.mType == ESM::Enchantment::WhenUsed)\n            {\n                int maxCharge = enchant->mData.mCharge;\n                int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge;\n\n                const int chargeWidth = 204;\n\n                MyGUI::TextBox* chargeText = enchantArea->createWidget<MyGUI::TextBox>(\"SandText\", MyGUI::IntCoord(0, 0, 10, 18), MyGUI::Align::Default, \"ToolTipEnchantChargeText\");\n                chargeText->setCaptionWithReplacing(\"#{sCharges}\");\n\n                const int chargeTextWidth = chargeText->getTextSize().width + 5;\n\n                const int chargeAndTextWidth = chargeWidth + chargeTextWidth;\n\n                totalSize.width = std::max(totalSize.width, chargeAndTextWidth);\n\n                chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18);\n\n                MyGUI::IntCoord chargeCoord;\n                if (totalSize.width < chargeWidth)\n                {\n                    totalSize.width = chargeWidth;\n                    chargeCoord = MyGUI::IntCoord(0, coord.top+6, chargeWidth, 18);\n                }\n                else\n                {\n                    chargeCoord = MyGUI::IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18);\n                }\n                Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget<Widgets::MWDynamicStat>\n                    (\"MW_ChargeBar\", chargeCoord, MyGUI::Align::Default);\n                chargeWidget->setValue(charge, maxCharge);\n                totalSize.height += 24;\n            }\n        }\n\n        captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize,\n            (captionHeight-captionSize.height)/2,\n            captionSize.width-imageSize,\n            captionSize.height);\n\n         //if its too long we do hscroll with the caption\n        if (captionSize.width > maximumWidth)\n        {\n            mHorizontalScrollIndex = mHorizontalScrollIndex + 2;\n            if (mHorizontalScrollIndex > captionSize.width){\n                mHorizontalScrollIndex = -totalSize.width;\n            }\n            int horizontal_scroll = mHorizontalScrollIndex;\n            if (horizontal_scroll < 40){\n                horizontal_scroll = 40;\n            }else{\n                horizontal_scroll = 80 - mHorizontalScrollIndex;\n            }\n            captionWidget->setPosition (MyGUI::IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top));\n        } else {\n            captionWidget->setPosition (captionWidget->getPosition() + padding);\n        }\n\n        textWidget->setPosition (textWidget->getPosition() + MyGUI::IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter\n\n        if (image != \"\")\n        {\n            MyGUI::ImageBox* imageWidget = mDynamicToolTipBox->createWidget<MyGUI::ImageBox>(\"ImageBox\",\n                MyGUI::IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize),\n                MyGUI::Align::Left | MyGUI::Align::Top);\n            imageWidget->setImageTexture(realImage);\n            imageWidget->setPosition (imageWidget->getPosition() + padding);\n        }\n\n        totalSize += MyGUI::IntSize(padding.left*2, padding.top*2);\n\n        return totalSize;\n    }\n\n    std::string ToolTips::toString(const float value)\n    {\n        std::ostringstream stream;\n\n        if (value != int(value))\n            stream << std::setprecision(3);\n\n        stream << value;\n        return stream.str();\n    }\n\n    std::string ToolTips::toString(const int value)\n    {\n        return std::to_string(value);\n    }\n\n    std::string ToolTips::getWeightString(const float weight, const std::string& prefix)\n    {\n        if (weight == 0)\n            return \"\";\n        else\n            return \"\\n\" + prefix + \": \" + toString(weight);\n    }\n\n    std::string ToolTips::getPercentString(const float value, const std::string& prefix)\n    {\n        if (value == 0)\n            return \"\";\n        else\n            return \"\\n\" + prefix + \": \" + toString(value*100) +\"%\";\n    }\n\n    std::string ToolTips::getValueString(const int value, const std::string& prefix)\n    {\n        if (value == 0)\n            return \"\";\n        else\n            return \"\\n\" + prefix + \": \" + toString(value);\n    }\n\n    std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix)\n    {\n        if (text == \"\")\n            return \"\";\n        else\n            return \"\\n\" + prefix + \": \" + text;\n    }\n\n    std::string ToolTips::getCountString(const int value)\n    {\n        if (value == 1)\n            return \"\";\n        else\n            return \" (\" + MyGUI::utility::toString(value) + \")\";\n    }\n\n    std::string ToolTips::getSoulString(const MWWorld::CellRef& cellref)\n    {\n        std::string soul = cellref.getSoul();\n        if (soul.empty())\n            return std::string();\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        const ESM::Creature *creature = store.get<ESM::Creature>().search(soul);\n        if (!creature)\n            return std::string();\n        if (creature->mName.empty())\n            return \" (\" + creature->mId + \")\";\n        return \" (\" + creature->mName + \")\";\n    }\n\n    std::string ToolTips::getCellRefString(const MWWorld::CellRef& cellref)\n    {\n        std::string ret;\n        ret += getMiscString(cellref.getOwner(), \"Owner\");\n        const std::string factionId = cellref.getFaction();\n        if (!factionId.empty())\n        {\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            const ESM::Faction *fact = store.get<ESM::Faction>().search(factionId);\n            if (fact != nullptr)\n            {\n                ret += getMiscString(fact->mName.empty() ? factionId : fact->mName, \"Owner Faction\");\n                if (cellref.getFactionRank() >= 0)\n                {\n                    int rank = cellref.getFactionRank();\n                    const std::string rankName = fact->mRanks[rank];\n                    if (rankName.empty())\n                        ret += getValueString(cellref.getFactionRank(), \"Rank\");\n                    else\n                        ret += getMiscString(rankName, \"Rank\");\n                }\n            }\n        }\n\n        std::vector<std::pair<std::string, int> > itemOwners =\n                MWBase::Environment::get().getMechanicsManager()->getStolenItemOwners(cellref.getRefId());\n\n        for (std::pair<std::string, int>& owner : itemOwners)\n        {\n            if (owner.second == std::numeric_limits<int>::max())\n                ret += std::string(\"\\nStolen from \") + owner.first; // for legacy (ESS) savegames\n            else\n                ret += std::string(\"\\nStolen \") + MyGUI::utility::toString(owner.second) + \" from \" + owner.first;\n        }\n\n        ret += getMiscString(cellref.getGlobalVariable(), \"Global\");\n        return ret;\n    }\n\n    std::string ToolTips::getDurationString(float duration, const std::string& prefix)\n    {\n        std::string ret;\n        ret = prefix + \": \";\n\n        if (duration < 1.f)\n        {\n            ret += \"0 s\";\n            return ret;\n        }\n\n        constexpr int secondsPerMinute = 60; // 60 seconds\n        constexpr int secondsPerHour = secondsPerMinute * 60; // 60 minutes\n        constexpr int secondsPerDay = secondsPerHour * 24; // 24 hours\n        constexpr int secondsPerMonth = secondsPerDay * 30; // 30 days\n        constexpr int secondsPerYear = secondsPerDay * 365;\n        int fullDuration = static_cast<int>(duration);\n        int units = 0;\n        int years = fullDuration / secondsPerYear;\n        int months = fullDuration % secondsPerYear / secondsPerMonth;\n        int days = fullDuration % secondsPerYear % secondsPerMonth / secondsPerDay; // Because a year is not exactly 12 \"months\"\n        int hours = fullDuration % secondsPerDay / secondsPerHour;\n        int minutes = fullDuration % secondsPerHour / secondsPerMinute;\n        int seconds = fullDuration % secondsPerMinute;\n        if (years)\n        {\n            units++;\n            ret += toString(years) + \" y \";\n        }\n        if (months)\n        {\n            units++;\n            ret += toString(months) + \" mo \";\n        }\n        if (units < 2 && days)\n        {\n            units++;\n            ret += toString(days) + \" d \";\n        }\n        if (units < 2 && hours)\n        {\n            units++;\n            ret += toString(hours) + \" h \";\n        }\n        if (units >= 2)\n            return ret;\n        if (minutes)\n            ret += toString(minutes) + \" min \";\n        if (seconds)\n            ret += toString(seconds) + \" s \";\n\n        return ret;\n    }\n\n    bool ToolTips::toggleFullHelp()\n    {\n        mFullHelp = !mFullHelp;\n        return mFullHelp;\n    }\n\n    bool ToolTips::getFullHelp() const\n    {\n        return mFullHelp;\n    }\n\n    void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y)\n    {\n        mFocusToolTipX = (min_x + max_x) / 2;\n        mFocusToolTipY = min_y;\n    }\n\n    void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId)\n    {\n        if (skillId == -1)\n            return;\n\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId];\n        const ESM::Skill* skill = store.get<ESM::Skill>().find(skillId);\n        assert(skill);\n\n        const ESM::Attribute* attr =\n            store.get<ESM::Attribute>().find(skill->mData.mAttribute);\n        assert(attr);\n        std::string icon = \"icons\\\\k\\\\\" + ESM::Skill::sIconNames[skillId];\n\n        widget->setUserString(\"ToolTipType\", \"Layout\");\n        widget->setUserString(\"ToolTipLayout\", \"SkillNoProgressToolTip\");\n        widget->setUserString(\"Caption_SkillNoProgressName\", \"#{\"+skillNameId+\"}\");\n        widget->setUserString(\"Caption_SkillNoProgressDescription\", skill->mDescription);\n        widget->setUserString(\"Caption_SkillNoProgressAttribute\", \"#{sGoverningAttribute}: #{\" + attr->mName + \"}\");\n        widget->setUserString(\"ImageTexture_SkillNoProgressImage\", icon);\n    }\n\n    void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId)\n    {\n        if (attributeId == -1)\n            return;\n\n        std::string icon = ESM::Attribute::sAttributeIcons[attributeId];\n        std::string name = ESM::Attribute::sGmstAttributeIds[attributeId];\n        std::string desc = ESM::Attribute::sGmstAttributeDescIds[attributeId];\n\n        widget->setUserString(\"ToolTipType\", \"Layout\");\n        widget->setUserString(\"ToolTipLayout\", \"AttributeToolTip\");\n        widget->setUserString(\"Caption_AttributeName\", \"#{\"+name+\"}\");\n        widget->setUserString(\"Caption_AttributeDescription\", \"#{\"+desc+\"}\");\n        widget->setUserString(\"ImageTexture_AttributeImage\", icon);\n    }\n\n    void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId)\n    {\n        widget->setUserString(\"Caption_Caption\", name);\n        std::string specText;\n        // get all skills of this specialisation\n        const MWWorld::Store<ESM::Skill> &skills =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>();\n\n        bool isFirst = true;\n        for (auto& skillPair : skills)\n        {\n            if (skillPair.second.mData.mSpecialization == specId)\n            {\n                if (isFirst)\n                    isFirst = false;\n                else\n                    specText += \"\\n\";\n\n                specText += std::string(\"#{\") + ESM::Skill::sSkillNameIds[skillPair.first] + \"}\";\n            }\n        }\n        widget->setUserString(\"Caption_ColumnText\", specText);\n        widget->setUserString(\"ToolTipLayout\", \"SpecializationToolTip\");\n        widget->setUserString(\"ToolTipType\", \"Layout\");\n    }\n\n    void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId)\n    {\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        const ESM::BirthSign *sign = store.get<ESM::BirthSign>().find(birthsignId);\n\n        widget->setUserString(\"ToolTipType\", \"Layout\");\n        widget->setUserString(\"ToolTipLayout\", \"BirthSignToolTip\");\n        widget->setUserString(\"ImageTexture_BirthSignImage\", MWBase::Environment::get().getWindowManager()->correctTexturePath(sign->mTexture));\n        std::string text;\n\n        text += sign->mName;\n        text += \"\\n#{fontcolourhtml=normal}\" + sign->mDescription;\n\n        std::vector<std::string> abilities, powers, spells;\n\n        for (const std::string& spellId : sign->mPowers.mList)\n        {\n            const ESM::Spell *spell = store.get<ESM::Spell>().search(spellId);\n            if (!spell)\n                continue; // Skip spells which cannot be found\n            ESM::Spell::SpellType type = static_cast<ESM::Spell::SpellType>(spell->mData.mType);\n            if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power)\n                continue; // We only want spell, ability and powers.\n\n            if (type == ESM::Spell::ST_Ability)\n                abilities.push_back(spellId);\n            else if (type == ESM::Spell::ST_Power)\n                powers.push_back(spellId);\n            else if (type == ESM::Spell::ST_Spell)\n                spells.push_back(spellId);\n        }\n\n        struct {\n            const std::vector<std::string> &spells;\n            std::string label;\n        }\n        categories[3] = {\n            {abilities, \"sBirthsignmenu1\"},\n            {powers,    \"sPowers\"},\n            {spells,    \"sBirthsignmenu2\"}\n        };\n\n        for (int category = 0; category < 3; ++category)\n        {\n            bool addHeader = true;\n            for (const std::string& spellId : categories[category].spells)\n            {\n                if (addHeader)\n                {\n                    text += std::string(\"\\n\\n#{fontcolourhtml=header}\") + std::string(\"#{\") + categories[category].label + \"}\";\n                    addHeader = false;\n                }\n\n                const ESM::Spell *spell = store.get<ESM::Spell>().find(spellId);\n                text += \"\\n#{fontcolourhtml=normal}\" + spell->mName;\n            }\n        }\n\n        widget->setUserString(\"Caption_BirthSignText\", text);\n    }\n\n    void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace)\n    {\n        widget->setUserString(\"Caption_CenteredCaption\", playerRace->mName);\n        widget->setUserString(\"Caption_CenteredCaptionText\", playerRace->mDescription);\n        widget->setUserString(\"ToolTipType\", \"Layout\");\n        widget->setUserString(\"ToolTipLayout\", \"RaceToolTip\");\n    }\n\n    void ToolTips::createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass)\n    {\n        if (playerClass.mName == \"\")\n            return;\n\n        int spec = playerClass.mData.mSpecialization;\n        std::string specStr;\n        if (spec == 0)\n            specStr = \"#{sSpecializationCombat}\";\n        else if (spec == 1)\n            specStr = \"#{sSpecializationMagic}\";\n        else if (spec == 2)\n            specStr = \"#{sSpecializationStealth}\";\n\n        widget->setUserString(\"Caption_ClassName\", playerClass.mName);\n        widget->setUserString(\"Caption_ClassDescription\", playerClass.mDescription);\n        widget->setUserString(\"Caption_ClassSpecialisation\", \"#{sSpecialization}: \" + specStr);\n        widget->setUserString(\"ToolTipType\", \"Layout\");\n        widget->setUserString(\"ToolTipLayout\", \"ClassToolTip\");\n    }\n\n    void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id)\n    {\n        const ESM::MagicEffect* effect =\n            MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(id);\n        const std::string &name = ESM::MagicEffect::effectIdToString (id);\n\n        std::string icon = effect->mIcon;\n        int slashPos = icon.rfind('\\\\');\n        icon.insert(slashPos+1, \"b_\");\n        icon = MWBase::Environment::get().getWindowManager()->correctIconPath(icon);\n\n        widget->setUserString(\"ToolTipType\", \"Layout\");\n        widget->setUserString(\"ToolTipLayout\", \"MagicEffectToolTip\");\n        widget->setUserString(\"Caption_MagicEffectName\", \"#{\" + name + \"}\");\n        widget->setUserString(\"Caption_MagicEffectDescription\", effect->mDescription);\n        widget->setUserString(\"Caption_MagicEffectSchool\", \"#{sSchool}: \" + sSchoolNames[effect->mData.mSchool]);\n        widget->setUserString(\"ImageTexture_MagicEffectImage\", icon);\n    }\n\n    void ToolTips::setDelay(float delay)\n    {\n        mDelay = delay;\n        mRemainingDelay = mDelay;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/tooltips.hpp",
    "content": "#ifndef MWGUI_TOOLTIPS_H\n#define MWGUI_TOOLTIPS_H\n\n#include \"layout.hpp\"\n#include \"../mwworld/ptr.hpp\"\n\n#include \"widgets.hpp\"\n\nnamespace ESM\n{\n    struct Class;\n    struct Race;\n}\n\nnamespace MWGui\n{\n    // Info about tooltip that is supplied by the MWWorld::Class object\n    struct ToolTipInfo\n    {\n    public:\n        ToolTipInfo()\n            : imageSize(32)\n            , remainingEnchantCharge(-1)\n            , isPotion(false)\n            , isIngredient(false)\n            , wordWrap(true)\n        {}\n\n        std::string caption;\n        std::string text;\n        std::string icon;\n        int imageSize;\n\n        // enchantment (for cloth, armor, weapons)\n        std::string enchant;\n        int remainingEnchantCharge;\n\n        // effects (for potions, ingredients)\n        Widgets::SpellEffectList effects;\n\n        // local map notes\n        std::vector<std::string> notes;\n\n        bool isPotion; // potions do not show target in the tooltip\n        bool isIngredient; // ingredients have no effect magnitude\n        bool wordWrap;\n    };\n\n    class ToolTips : public Layout\n    {\n    public:\n        ToolTips();\n\n        void onFrame(float frameDuration);\n        void update(float frameDuration);\n\n        void setEnabled(bool enabled);\n\n        bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script)\n        bool getFullHelp() const;\n\n        void setDelay(float delay);\n\n        void clear();\n\n        void setFocusObject(const MWWorld::Ptr& focus);\n        void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y);\n        ///< set the screen-space position of the tooltip for focused object\n\n        static std::string getWeightString(const float weight, const std::string& prefix);\n        static std::string getPercentString(const float value, const std::string& prefix);\n        static std::string getValueString(const int value, const std::string& prefix);\n        ///< @return \"prefix: value\" or \"\" if value is 0\n\n        static std::string getMiscString(const std::string& text, const std::string& prefix);\n        ///< @return \"prefix: text\" or \"\" if text is empty\n\n        static std::string toString(const float value);\n        static std::string toString(const int value);\n\n        static std::string getCountString(const int value);\n        ///< @return blank string if count is 1, or else \" (value)\"\n\n        static std::string getSoulString(const MWWorld::CellRef& cellref);\n        ///< Returns a string containing the name of the creature that the ID in the cellref's soul field belongs to.\n\n        static std::string getCellRefString(const MWWorld::CellRef& cellref);\n        ///< Returns a string containing debug tooltip information about the given cellref.\n\n        static std::string getDurationString (float duration, const std::string& prefix);\n        ///< Returns duration as two largest time units, rounded down. Note: not localized; no line break.\n\n        // these do not create an actual tooltip, but they fill in the data that is required so the tooltip\n        // system knows what to show in case this widget is hovered\n        static void createSkillToolTip(MyGUI::Widget* widget, int skillId);\n        static void createAttributeToolTip(MyGUI::Widget* widget, int attributeId);\n        static void createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId);\n        static void createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId);\n        static void createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace);\n        static void createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass);\n        static void createMagicEffectToolTip(MyGUI::Widget* widget, short id);\n        \n        bool checkOwned();\n        /// Returns True if taking mFocusObject would be crime\n \n    private:\n        MyGUI::Widget* mDynamicToolTipBox;\n\n        MWWorld::Ptr mFocusObject;\n\n        MyGUI::IntSize getToolTipViaPtr (int count, bool image = true, bool isOwned = false);\n        ///< @return requested tooltip size\n\n        MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isOwned = false);\n        ///< @return requested tooltip size\n        /// @param isFocusObject Is the object this tooltips originates from mFocusObject?\n\n        float mFocusToolTipX;\n        float mFocusToolTipY;\n\n        /// Adjust position for a tooltip so that it doesn't leave the screen and does not obscure the mouse cursor\n        void position(MyGUI::IntPoint& position, MyGUI::IntSize size, MyGUI::IntSize viewportSize);\n\n        static std::string sSchoolNames[6];\n\n        int mHorizontalScrollIndex;\n\n\n        float mDelay;\n        float mRemainingDelay; // remaining time until tooltip will show\n\n        int mLastMouseX;\n        int mLastMouseY;\n\n        bool mEnabled;\n\n        bool mFullHelp;\n        \n        int mShowOwned;\n\n        float mFrameDuration;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/tradeitemmodel.cpp",
    "content": "#include \"tradeitemmodel.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\nnamespace MWGui\n{\n\n    TradeItemModel::TradeItemModel(ItemModel *sourceModel, const MWWorld::Ptr& merchant)\n        : mMerchant(merchant)\n    {\n        mSourceModel = sourceModel;\n    }\n\n    bool TradeItemModel::allowedToUseItems() const\n    {\n        return true;\n    }\n\n    ItemStack TradeItemModel::getItem (ModelIndex index)\n    {\n        if (index < 0)\n            throw std::runtime_error(\"Invalid index supplied\");\n        if (mItems.size() <= static_cast<size_t>(index))\n            throw std::runtime_error(\"Item index out of range\");\n        return mItems[index];\n    }\n\n    size_t TradeItemModel::getItemCount()\n    {\n        return mItems.size();\n    }\n\n    void TradeItemModel::borrowImpl(const ItemStack &item, std::vector<ItemStack> &out)\n    {\n        bool found = false;\n        for (ItemStack& itemStack : out)\n        {\n            if (itemStack.mBase == item.mBase)\n            {\n                itemStack.mCount += item.mCount;\n                found = true;\n                break;\n            }\n        }\n        if (!found)\n            out.push_back(item);\n    }\n\n    void TradeItemModel::unborrowImpl(const ItemStack &item, size_t count, std::vector<ItemStack> &out)\n    {\n        std::vector<ItemStack>::iterator it = out.begin();\n        bool found = false;\n        for (; it != out.end(); ++it)\n        {\n            if (it->mBase == item.mBase)\n            {\n                if (it->mCount < count)\n                    throw std::runtime_error(\"Not enough borrowed items to return\");\n                it->mCount -= count;\n                if (it->mCount == 0)\n                    out.erase(it);\n                found = true;\n                break;\n            }\n        }\n        if (!found)\n            throw std::runtime_error(\"Can't find borrowed item to return\");\n    }\n\n    void TradeItemModel::borrowItemFromUs (ModelIndex itemIndex, size_t count)\n    {\n        ItemStack item = getItem(itemIndex);\n        item.mCount = count;\n        borrowImpl(item, mBorrowedFromUs);\n    }\n\n    void TradeItemModel::borrowItemToUs (ModelIndex itemIndex, ItemModel* source, size_t count)\n    {\n        ItemStack item = source->getItem(itemIndex);\n        item.mCount = count;\n        borrowImpl(item, mBorrowedToUs);\n    }\n\n    void TradeItemModel::returnItemBorrowedToUs (ModelIndex itemIndex, size_t count)\n    {\n        ItemStack item = getItem(itemIndex);\n        unborrowImpl(item, count, mBorrowedToUs);\n    }\n\n    void TradeItemModel::returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count)\n    {\n        ItemStack item = source->getItem(itemIndex);\n        unborrowImpl(item, count, mBorrowedFromUs);\n    }\n\n    void TradeItemModel::adjustEncumbrance(float &encumbrance)\n    {\n        for (ItemStack& itemStack : mBorrowedToUs)\n        {\n            MWWorld::Ptr& item = itemStack.mBase;\n            encumbrance += item.getClass().getWeight(item) * itemStack.mCount;\n        }\n        for (ItemStack& itemStack : mBorrowedFromUs)\n        {\n            MWWorld::Ptr& item = itemStack.mBase;\n            encumbrance -= item.getClass().getWeight(item) * itemStack.mCount;\n        }\n        encumbrance = std::max(0.f, encumbrance);\n    }\n\n    void TradeItemModel::abort()\n    {\n        mBorrowedFromUs.clear();\n        mBorrowedToUs.clear();\n    }\n\n    const std::vector<ItemStack> TradeItemModel::getItemsBorrowedToUs() const\n    {\n        return mBorrowedToUs;\n    }\n\n    void TradeItemModel::transferItems()\n    {\n        for (ItemStack& itemStack : mBorrowedToUs)\n        {\n            // get index in the source model\n            ItemModel* sourceModel = itemStack.mCreator;\n            size_t i=0;\n            for (; i<sourceModel->getItemCount(); ++i)\n            {\n                if (itemStack.mBase == sourceModel->getItem(i).mBase)\n                    break;\n            }\n            if (i == sourceModel->getItemCount())\n                throw std::runtime_error(\"The borrowed item disappeared\");\n\n            const ItemStack& item = sourceModel->getItem(i);\n            static const bool prevent = Settings::Manager::getBool(\"prevent merchant equipping\", \"Game\");\n            // copy the borrowed items to our model\n            copyItem(item, itemStack.mCount, !prevent);\n            // then remove them from the source model\n            sourceModel->removeItem(item, itemStack.mCount);\n        }\n        mBorrowedToUs.clear();\n        mBorrowedFromUs.clear();\n    }\n\n    void TradeItemModel::update()\n    {\n        mSourceModel->update();\n\n        int services = 0;\n        if (!mMerchant.isEmpty())\n            services = mMerchant.getClass().getServices(mMerchant);\n\n        mItems.clear();\n        // add regular items\n        for (size_t i=0; i<mSourceModel->getItemCount(); ++i)\n        {\n            ItemStack item = mSourceModel->getItem(i);\n            if(!mMerchant.isEmpty())\n            {\n                MWWorld::Ptr base = item.mBase;\n                if(Misc::StringUtils::ciEqual(base.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId))\n                    continue;\n\n                if (!base.getClass().showsInInventory(base))\n                    return;\n\n                if(!base.getClass().canSell(base, services))\n                    continue;\n\n                // Bound items may not be bought\n                if (item.mFlags & ItemStack::Flag_Bound)\n                    continue;\n\n                // don't show equipped items\n                if(mMerchant.getClass().hasInventoryStore(mMerchant))\n                {\n                    MWWorld::InventoryStore& store = mMerchant.getClass().getInventoryStore(mMerchant);\n                    if (store.isEquipped(base))\n                        continue;\n                }\n            }\n\n            // don't show items that we borrowed to someone else\n            for (ItemStack& itemStack : mBorrowedFromUs)\n            {\n                if (itemStack.mBase == item.mBase)\n                {\n                    if (item.mCount < itemStack.mCount)\n                        throw std::runtime_error(\"Lent more items than present\");\n                    item.mCount -= itemStack.mCount;\n                }\n            }\n\n            if (item.mCount > 0)\n                mItems.push_back(item);\n        }\n\n        // add items borrowed to us\n        for (ItemStack& itemStack : mBorrowedToUs)\n        {\n            itemStack.mType = ItemStack::Type_Barter;\n            mItems.push_back(itemStack);\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/tradeitemmodel.hpp",
    "content": "#ifndef MWGUI_TRADE_ITEM_MODEL_H\n#define MWGUI_TRADE_ITEM_MODEL_H\n\n#include \"itemmodel.hpp\"\n\nnamespace MWGui\n{\n\n    class ItemModel;\n\n    /// @brief An item model that allows 'borrowing' items from another item model. Used for previewing barter offers.\n    /// Also filters items that the merchant does not sell.\n    class TradeItemModel : public ProxyItemModel\n    {\n    public:\n        TradeItemModel (ItemModel* sourceModel, const MWWorld::Ptr& merchant);\n\n        bool allowedToUseItems() const override;\n\n        ItemStack getItem (ModelIndex index) override;\n        size_t getItemCount() override;\n\n        void update() override;\n\n        void borrowItemFromUs (ModelIndex itemIndex, size_t count);\n\n        void borrowItemToUs (ModelIndex itemIndex, ItemModel* source, size_t count);\n        ///< @note itemIndex points to an item in \\a source\n\n        void returnItemBorrowedToUs (ModelIndex itemIndex, size_t count);\n\n        void returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count);\n\n        /// Permanently transfers items that were borrowed to us from another model to this model\n        void transferItems ();\n        /// Aborts trade\n        void abort();\n\n        /// Adjusts the given encumbrance by adding weight for items that have been lent to us,\n        /// and removing weight for items we've lent to someone else.\n        void adjustEncumbrance (float& encumbrance);\n\n        const std::vector<ItemStack> getItemsBorrowedToUs() const;\n\n    private:\n        void borrowImpl(const ItemStack& item, std::vector<ItemStack>& out);\n        void unborrowImpl(const ItemStack& item, size_t count, std::vector<ItemStack>& out);\n\n        std::vector<ItemStack> mItems;\n\n        std::vector<ItemStack> mBorrowedToUs;\n        std::vector<ItemStack> mBorrowedFromUs;\n\n        MWWorld::Ptr mMerchant;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/tradewindow.cpp",
    "content": "#include \"tradewindow.hpp\"\n\n#include <MyGUI_Button.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_ControllerManager.h>\n#include <MyGUI_ControllerRepeatClick.h>\n\n#include <components/widgets/numericeditbox.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n\n#include \"inventorywindow.hpp\"\n#include \"itemview.hpp\"\n#include \"sortfilteritemmodel.hpp\"\n#include \"containeritemmodel.hpp\"\n#include \"tradeitemmodel.hpp\"\n#include \"countdialog.hpp\"\n#include \"tooltips.hpp\"\n\nnamespace\n{\n\n    int getEffectiveValue (MWWorld::Ptr item, int count)\n    {\n        float price = static_cast<float>(item.getClass().getValue(item));\n        if (item.getClass().hasItemHealth(item))\n        {\n            price *= item.getClass().getItemNormalizedHealth(item);\n        }\n        return static_cast<int>(price * count);\n    }\n\n}\n\nnamespace MWGui\n{\n    TradeWindow::TradeWindow()\n        : WindowBase(\"openmw_trade_window.layout\")\n        , mSortModel(nullptr)\n        , mTradeModel(nullptr)\n        , mItemToSell(-1)\n        , mCurrentBalance(0)\n        , mCurrentMerchantOffer(0)\n    {\n        getWidget(mFilterAll, \"AllButton\");\n        getWidget(mFilterWeapon, \"WeaponButton\");\n        getWidget(mFilterApparel, \"ApparelButton\");\n        getWidget(mFilterMagic, \"MagicButton\");\n        getWidget(mFilterMisc, \"MiscButton\");\n\n        getWidget(mMaxSaleButton, \"MaxSaleButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mOfferButton, \"OfferButton\");\n        getWidget(mPlayerGold, \"PlayerGold\");\n        getWidget(mMerchantGold, \"MerchantGold\");\n        getWidget(mIncreaseButton, \"IncreaseButton\");\n        getWidget(mDecreaseButton, \"DecreaseButton\");\n        getWidget(mTotalBalance, \"TotalBalance\");\n        getWidget(mTotalBalanceLabel, \"TotalBalanceLabel\");\n        getWidget(mBottomPane, \"BottomPane\");\n        getWidget(mFilterEdit, \"FilterEdit\");\n\n        getWidget(mItemView, \"ItemView\");\n        mItemView->eventItemClicked += MyGUI::newDelegate(this, &TradeWindow::onItemSelected);\n\n        mFilterAll->setStateSelected(true);\n\n        mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged);\n        mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged);\n        mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged);\n        mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged);\n        mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged);\n        mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &TradeWindow::onNameFilterChanged);\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onCancelButtonClicked);\n        mOfferButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onOfferButtonClicked);\n        mMaxSaleButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onMaxSaleButtonClicked);\n        mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &TradeWindow::onIncreaseButtonPressed);\n        mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased);\n        mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &TradeWindow::onDecreaseButtonPressed);\n        mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased);\n\n        mTotalBalance->eventValueChanged += MyGUI::newDelegate(this, &TradeWindow::onBalanceValueChanged);\n        mTotalBalance->eventEditSelectAccept += MyGUI::newDelegate(this, &TradeWindow::onAccept);\n        mTotalBalance->setMinValue(std::numeric_limits<int>::min()+1); // disallow INT_MIN since abs(INT_MIN) is undefined\n\n        setCoord(400, 0, 400, 300);\n    }\n\n    void TradeWindow::setPtr(const MWWorld::Ptr& actor)\n    {\n        mPtr = actor;\n\n        mCurrentBalance = 0;\n        mCurrentMerchantOffer = 0;\n\n        std::vector<MWWorld::Ptr> itemSources;\n        // Important: actor goes first, so purchased items come out of the actor's pocket first\n        itemSources.push_back(actor);\n        MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources);\n\n        std::vector<MWWorld::Ptr> worldItems;\n        MWBase::Environment::get().getWorld()->getItemsOwnedBy(actor, worldItems);\n\n        mTradeModel = new TradeItemModel(new ContainerItemModel(itemSources, worldItems), mPtr);\n        mSortModel = new SortFilterItemModel(mTradeModel);\n        mItemView->setModel (mSortModel);\n        mItemView->resetScrollBars();\n\n        updateLabels();\n\n        setTitle(actor.getClass().getName(actor));\n\n        onFilterChanged(mFilterAll);\n        mFilterEdit->setCaption(\"\");\n    }\n\n    void TradeWindow::onFrame(float dt)\n    {\n        checkReferenceAvailable();\n    }\n\n    void TradeWindow::onNameFilterChanged(MyGUI::EditBox* _sender)\n    {\n        mSortModel->setNameFilter(_sender->getCaption());\n        mItemView->update();\n    }\n\n    void TradeWindow::onFilterChanged(MyGUI::Widget* _sender)\n    {\n        if (_sender == mFilterAll)\n            mSortModel->setCategory(SortFilterItemModel::Category_All);\n        else if (_sender == mFilterWeapon)\n            mSortModel->setCategory(SortFilterItemModel::Category_Weapon);\n        else if (_sender == mFilterApparel)\n            mSortModel->setCategory(SortFilterItemModel::Category_Apparel);\n        else if (_sender == mFilterMagic)\n            mSortModel->setCategory(SortFilterItemModel::Category_Magic);\n        else if (_sender == mFilterMisc)\n            mSortModel->setCategory(SortFilterItemModel::Category_Misc);\n\n        mFilterAll->setStateSelected(false);\n        mFilterWeapon->setStateSelected(false);\n        mFilterApparel->setStateSelected(false);\n        mFilterMagic->setStateSelected(false);\n        mFilterMisc->setStateSelected(false);\n\n        _sender->castType<MyGUI::Button>()->setStateSelected(true);\n\n        mItemView->update();\n    }\n\n    int TradeWindow::getMerchantServices()\n    {\n        return mPtr.getClass().getServices(mPtr);\n    }\n\n    bool TradeWindow::exit()\n    {\n        mTradeModel->abort();\n        MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->abort();\n        return true;\n    }\n\n    void TradeWindow::onItemSelected (int index)\n    {\n        const ItemStack& item = mSortModel->getItem(index);\n\n        MWWorld::Ptr object = item.mBase;\n        int count = item.mCount;\n        bool shift = MyGUI::InputManager::getInstance().isShiftPressed();\n        if (MyGUI::InputManager::getInstance().isControlPressed())\n            count = 1;\n\n        if (count > 1 && !shift)\n        {\n            CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();\n            std::string message = \"#{sQuanityMenuMessage02}\";\n            std::string name = object.getClass().getName(object) + MWGui::ToolTips::getSoulString(object.getCellRef());\n            dialog->openCountDialog(name, message, count);\n            dialog->eventOkClicked.clear();\n            dialog->eventOkClicked += MyGUI::newDelegate(this, &TradeWindow::sellItem);\n            mItemToSell = mSortModel->mapToSource(index);\n        }\n        else\n        {\n            mItemToSell = mSortModel->mapToSource(index);\n            sellItem (nullptr, count);\n        }\n    }\n\n    void TradeWindow::sellItem(MyGUI::Widget* sender, int count)\n    {\n        const ItemStack& item = mTradeModel->getItem(mItemToSell);\n        std::string sound = item.mBase.getClass().getUpSoundId(item.mBase);\n        MWBase::Environment::get().getWindowManager()->playSound(sound);\n\n        TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel();\n\n        if (item.mType == ItemStack::Type_Barter)\n        {\n            // this was an item borrowed to us by the player\n            mTradeModel->returnItemBorrowedToUs(mItemToSell, count);\n            playerTradeModel->returnItemBorrowedFromUs(mItemToSell, mTradeModel, count);\n            buyFromNpc(item.mBase, count, true);\n        }\n        else\n        {\n            // borrow item to player\n            playerTradeModel->borrowItemToUs(mItemToSell, mTradeModel, count);\n            mTradeModel->borrowItemFromUs(mItemToSell, count);\n            buyFromNpc(item.mBase, count, false);\n        }\n\n        MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();\n        mItemView->update();\n    }\n\n    void TradeWindow::borrowItem (int index, size_t count)\n    {\n        TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel();\n        mTradeModel->borrowItemToUs(index, playerTradeModel, count);\n        mItemView->update();\n        sellToNpc(playerTradeModel->getItem(index).mBase, count, false);\n    }\n\n    void TradeWindow::returnItem (int index, size_t count)\n    {\n        TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel();\n        const ItemStack& item = playerTradeModel->getItem(index);\n        mTradeModel->returnItemBorrowedFromUs(index, playerTradeModel, count);\n        mItemView->update();\n        sellToNpc(item.mBase, count, true);\n    }\n\n    void TradeWindow::addOrRemoveGold(int amount, const MWWorld::Ptr& actor)\n    {\n        MWWorld::ContainerStore& store = actor.getClass().getContainerStore(actor);\n\n        if (amount > 0)\n        {\n            store.add(MWWorld::ContainerStore::sGoldId, amount, actor);\n        }\n        else\n        {\n            store.remove(MWWorld::ContainerStore::sGoldId, - amount, actor);\n        }\n    }\n\n    void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender)\n    {\n        TradeItemModel* playerItemModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel();\n\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        // were there any items traded at all?\n        const std::vector<ItemStack>& playerBought = playerItemModel->getItemsBorrowedToUs();\n        const std::vector<ItemStack>& merchantBought = mTradeModel->getItemsBorrowedToUs();\n        if (playerBought.empty() && merchantBought.empty())\n        {\n            // user notification\n            MWBase::Environment::get().getWindowManager()->\n                messageBox(\"#{sBarterDialog11}\");\n            return;\n        }\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        // check if the player can afford this\n        if (mCurrentBalance < 0 && playerGold < std::abs(mCurrentBalance))\n        {\n            // user notification\n            MWBase::Environment::get().getWindowManager()->\n                messageBox(\"#{sBarterDialog1}\");\n            return;\n        }\n\n        // check if the merchant can afford this\n        if (mCurrentBalance > 0 && getMerchantGold() < mCurrentBalance)\n        {\n            // user notification\n            MWBase::Environment::get().getWindowManager()->\n                messageBox(\"#{sBarterDialog2}\");\n            return;\n        }\n\n        // check if the player is attempting to sell back an item stolen from this actor\n        for (const ItemStack& itemStack : merchantBought)\n        {\n            if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(itemStack.mBase.getCellRef().getRefId(), mPtr))\n            {\n                std::string msg = gmst.find(\"sNotifyMessage49\")->mValue.getString();\n                msg = Misc::StringUtils::format(msg, itemStack.mBase.getClass().getName(itemStack.mBase));\n                MWBase::Environment::get().getWindowManager()->messageBox(msg);\n\n                MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, itemStack.mBase, mPtr, itemStack.mCount);\n\n                onCancelButtonClicked(mCancelButton);\n                MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n                return;\n            }\n        }\n\n        bool offerAccepted = mTrading.haggle(player, mPtr, mCurrentBalance, mCurrentMerchantOffer);\n\n        // apply disposition change if merchant is NPC\n        if ( mPtr.getClass().isNpc() ) {\n            int dispositionDelta = offerAccepted\n                ? gmst.find(\"iBarterSuccessDisposition\")->mValue.getInteger()\n                : gmst.find(\"iBarterFailDisposition\")->mValue.getInteger();\n\n            MWBase::Environment::get().getDialogueManager()->applyBarterDispositionChange(dispositionDelta);\n        }\n\n        // display message on haggle failure\n        if ( !offerAccepted ) {\n            MWBase::Environment::get().getWindowManager()->\n                messageBox(\"#{sNotifyMessage9}\");\n            return;\n        }\n\n        // make the item transfer\n        mTradeModel->transferItems();\n        playerItemModel->transferItems();\n\n        // transfer the gold\n        if (mCurrentBalance != 0)\n        {\n            addOrRemoveGold(mCurrentBalance, player);\n\n            /*\n                Start of tes3mp change (major)\n\n                Don't unilaterally change the merchant's gold pool on our client and instead let the server do it\n            */\n            //mPtr.getClass().getCreatureStats(mPtr).setGoldPool(\n            //    mPtr.getClass().getCreatureStats(mPtr).getGoldPool() - mCurrentBalance);\n\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            MWMechanics::CreatureStats& merchantCreatureStats = mPtr.getClass().getCreatureStats(mPtr);\n            objectList->addObjectMiscellaneous(mPtr, merchantCreatureStats.getGoldPool() - mCurrentBalance, merchantCreatureStats.getLastRestockTime().getHour(),\n                merchantCreatureStats.getLastRestockTime().getDay());\n            objectList->sendObjectMiscellaneous();\n            /*\n                End of tes3mp change (major)\n            */\n        }\n\n        eventTradeDone();\n\n        MWBase::Environment::get().getWindowManager()->playSound(\"Item Gold Up\");\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);\n    }\n\n    void TradeWindow::onAccept(MyGUI::EditBox *sender)\n    {\n        onOfferButtonClicked(sender);\n\n        // To do not spam onAccept() again and again\n        MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);\n    }\n\n    void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender)\n    {\n        exit();\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);\n    }\n\n    void TradeWindow::onMaxSaleButtonClicked(MyGUI::Widget* _sender)\n    {\n        mCurrentBalance = getMerchantGold();\n        updateLabels();\n    }\n\n    void TradeWindow::addRepeatController(MyGUI::Widget *widget)\n    {\n        MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(MyGUI::ControllerRepeatClick::getClassTypeName());\n        MyGUI::ControllerRepeatClick* controller = static_cast<MyGUI::ControllerRepeatClick*>(item);\n        controller->eventRepeatClick += newDelegate(this, &TradeWindow::onRepeatClick);\n        MyGUI::ControllerManager::getInstance().addItem(widget, controller);\n    }\n\n    void TradeWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)\n    {\n        addRepeatController(_sender);\n        onIncreaseButtonTriggered();\n    }\n\n    void TradeWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)\n    {\n        addRepeatController(_sender);\n        onDecreaseButtonTriggered();\n    }\n\n    void TradeWindow::onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller)\n    {\n        if (widget == mIncreaseButton)\n            onIncreaseButtonTriggered();\n        else if (widget == mDecreaseButton)\n            onDecreaseButtonTriggered();\n    }\n\n    void TradeWindow::onBalanceButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id)\n    {\n        MyGUI::ControllerManager::getInstance().removeItem(_sender);\n    }\n\n    void TradeWindow::onBalanceValueChanged(int value)\n    {\n        // Entering a \"-\" sign inverts the buying/selling state\n        mCurrentBalance = (mCurrentBalance >= 0 ? 1 : -1) * value;\n        updateLabels();\n\n        if (value != std::abs(value))\n            mTotalBalance->setValue(std::abs(value));\n    }\n\n    void TradeWindow::onIncreaseButtonTriggered()\n    {\n        // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined\n        if (mCurrentBalance == std::numeric_limits<int>::max() || mCurrentBalance == std::numeric_limits<int>::min()+1)\n            return;\n        if (mCurrentBalance < 0) mCurrentBalance -= 1;\n        else mCurrentBalance += 1;\n        updateLabels();\n    }\n\n    void TradeWindow::onDecreaseButtonTriggered()\n    {\n        if (mCurrentBalance < 0) mCurrentBalance += 1;\n        else mCurrentBalance -= 1;\n        updateLabels();\n    }\n\n    void TradeWindow::updateLabels()\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        mPlayerGold->setCaptionWithReplacing(\"#{sYourGold} \" + MyGUI::utility::toString(playerGold));\n\n        if (mCurrentBalance < 0)\n        {\n            mTotalBalanceLabel->setCaptionWithReplacing(\"#{sTotalCost}\");\n        }\n        else\n        {\n            mTotalBalanceLabel->setCaptionWithReplacing(\"#{sTotalSold}\");\n        }\n\n        mTotalBalance->setValue(std::abs(mCurrentBalance));\n\n        mMerchantGold->setCaptionWithReplacing(\"#{sSellerGold} \" + MyGUI::utility::toString(getMerchantGold()));\n    }\n\n    void TradeWindow::updateOffer()\n    {\n        TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel();\n\n        int merchantOffer = 0;\n\n        // The offered price must be capped at 75% of the base price to avoid exploits\n        // connected to buying and selling the same item.\n        // This value has been determined by researching the limitations of the vanilla formula\n        // and may not be sufficient if getBarterOffer behavior has been changed.\n        const std::vector<ItemStack>& playerBorrowed = playerTradeModel->getItemsBorrowedToUs();\n        for (const ItemStack& itemStack : playerBorrowed)\n        {\n            const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);\n            const int cap = static_cast<int>(std::max(1.f, 0.75f * basePrice)); // Minimum buying price -- 75% of the base\n            const int buyingPrice = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, basePrice, true);\n            merchantOffer -= std::max(cap, buyingPrice);\n        }\n\n        const std::vector<ItemStack>& merchantBorrowed = mTradeModel->getItemsBorrowedToUs();\n        for (const ItemStack& itemStack : merchantBorrowed)\n        {\n            const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);\n            const int cap = static_cast<int>(std::max(1.f, 0.75f * basePrice)); // Maximum selling price -- 75% of the base\n            const int sellingPrice = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, basePrice, false);\n            merchantOffer += mPtr.getClass().isNpc() ? std::min(cap, sellingPrice) : sellingPrice;\n        }\n\n        int diff = merchantOffer - mCurrentMerchantOffer;\n        mCurrentMerchantOffer = merchantOffer;\n        mCurrentBalance += diff;\n        updateLabels();\n    }\n\n    void TradeWindow::sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem)\n    {\n        updateOffer();\n    }\n\n    void TradeWindow::buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem)\n    {\n        updateOffer();\n    }\n\n    void TradeWindow::onReferenceUnavailable()\n    {\n        // remove both Trade and Dialogue (since you always trade with the NPC/creature that you have previously talked to)\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);\n        MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n    }\n\n    int TradeWindow::getMerchantGold()\n    {\n        int merchantGold = mPtr.getClass().getCreatureStats(mPtr).getGoldPool();\n        return merchantGold;\n    }\n\n    void TradeWindow::resetReference()\n    {\n        ReferenceInterface::resetReference();\n        mItemView->setModel(nullptr);\n        mTradeModel = nullptr;\n        mSortModel = nullptr;\n    }\n\n    void TradeWindow::onClose()\n    {\n        // Make sure the window was actually closed and not temporarily hidden.\n        if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Barter))\n            return;\n        resetReference();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/tradewindow.hpp",
    "content": "#ifndef MWGUI_TRADEWINDOW_H\n#define MWGUI_TRADEWINDOW_H\n\n#include \"../mwmechanics/trading.hpp\"\n\n#include \"referenceinterface.hpp\"\n#include \"windowbase.hpp\"\n\nnamespace Gui\n{\n    class NumericEditBox;\n}\n\nnamespace MyGUI\n{\n    class ControllerItem;\n}\n\nnamespace MWGui\n{\n    class ItemView;\n    class SortFilterItemModel;\n    class TradeItemModel;\n\n    class TradeWindow : public WindowBase, public ReferenceInterface\n    {\n        public:\n            TradeWindow();\n\n            void setPtr(const MWWorld::Ptr& actor) override;\n\n            void onClose() override;\n            void onFrame(float dt) override;\n            void clear() override { resetReference(); }\n\n            void borrowItem (int index, size_t count);\n            void returnItem (int index, size_t count);\n\n            int getMerchantServices();\n\n            bool exit() override;\n\n            void resetReference() override;\n\n            typedef MyGUI::delegates::CMultiDelegate0 EventHandle_TradeDone;\n            EventHandle_TradeDone eventTradeDone;\n\n        private:\n            ItemView* mItemView;\n            SortFilterItemModel* mSortModel;\n            TradeItemModel* mTradeModel;\n            MWMechanics::Trading mTrading;\n\n            static const float sBalanceChangeInitialPause; // in seconds\n            static const float sBalanceChangeInterval; // in seconds\n\n            MyGUI::Button* mFilterAll;\n            MyGUI::Button* mFilterWeapon;\n            MyGUI::Button* mFilterApparel;\n            MyGUI::Button* mFilterMagic;\n            MyGUI::Button* mFilterMisc;\n\n            MyGUI::EditBox* mFilterEdit;\n\n            MyGUI::Button* mIncreaseButton;\n            MyGUI::Button* mDecreaseButton;\n            MyGUI::TextBox* mTotalBalanceLabel;\n            Gui::NumericEditBox* mTotalBalance;\n\n            MyGUI::Widget* mBottomPane;\n\n            MyGUI::Button* mMaxSaleButton;\n            MyGUI::Button* mCancelButton;\n            MyGUI::Button* mOfferButton;\n            MyGUI::TextBox* mPlayerGold;\n            MyGUI::TextBox* mMerchantGold;\n\n            int mItemToSell;\n\n            int mCurrentBalance;\n            int mCurrentMerchantOffer;\n\n            void sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem); ///< only used for adjusting the gold balance\n            void buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem); ///< only used for adjusting the gold balance\n\n            void updateOffer();\n\n            void onItemSelected (int index);\n            void sellItem (MyGUI::Widget* sender, int count);\n\n            void onFilterChanged(MyGUI::Widget* _sender);\n            void onNameFilterChanged(MyGUI::EditBox* _sender);\n            void onOfferButtonClicked(MyGUI::Widget* _sender);\n            void onAccept(MyGUI::EditBox* sender);\n            void onCancelButtonClicked(MyGUI::Widget* _sender);\n            void onMaxSaleButtonClicked(MyGUI::Widget* _sender);\n            void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);\n            void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);\n            void onBalanceButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);\n            void onBalanceValueChanged(int value);\n            void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller);\n\n            void addRepeatController(MyGUI::Widget* widget);\n\n            void onIncreaseButtonTriggered();\n            void onDecreaseButtonTriggered();\n\n            void addOrRemoveGold(int gold, const MWWorld::Ptr& actor);\n\n            void updateLabels();\n\n            void onReferenceUnavailable() override;\n\n            int getMerchantGold();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/trainingwindow.cpp",
    "content": "#include \"trainingwindow.hpp\"\n\n#include <MyGUI_Gui.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include <components/settings/settings.hpp>\n\n#include \"tooltips.hpp\"\n\nnamespace\n{\n// Sorts a container descending by skill value. If skill value is equal, sorts ascending by skill ID.\n// pair <skill ID, skill value>\nbool sortSkills (const std::pair<int, int>& left, const std::pair<int, int>& right)\n{\n    if (left == right)\n        return false;\n\n    if (left.second > right.second)\n        return true;\n    else if (left.second < right.second)\n        return false;\n\n    return left.first < right.first;\n}\n}\n\nnamespace MWGui\n{\n\n    TrainingWindow::TrainingWindow()\n        : WindowBase(\"openmw_trainingwindow.layout\")\n        , mTimeAdvancer(0.05f)\n        , mTrainingSkillBasedOnBaseSkill(Settings::Manager::getBool(\"trainers training skills based on base skill\", \"Game\"))\n    {\n        getWidget(mTrainingOptions, \"TrainingOptions\");\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mPlayerGold, \"PlayerGold\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onCancelButtonClicked);\n\n        mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &TrainingWindow::onTrainingProgressChanged);\n        mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &TrainingWindow::onTrainingFinished);\n    }\n\n    void TrainingWindow::onOpen()\n    {\n        if (mTimeAdvancer.isRunning())\n        {\n            mProgressBar.setVisible(true);\n            setVisible(false);\n        }\n        else\n            mProgressBar.setVisible(false);\n\n        center();\n    }\n\n    void TrainingWindow::setPtr (const MWWorld::Ptr& actor)\n    {\n        mPtr = actor;\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        mPlayerGold->setCaptionWithReplacing(\"#{sGold}: \" + MyGUI::utility::toString(playerGold));\n\n        // NPC can train you in his best 3 skills\n        std::vector< std::pair<int, float> > skills;\n\n        MWMechanics::NpcStats const& actorStats(actor.getClass().getNpcStats(actor));\n        for (int i=0; i<ESM::Skill::Length; ++i)\n        {\n            float value = getSkillForTraining(actorStats, i);\n\n            skills.emplace_back(i, value);\n        }\n\n        std::sort(skills.begin(), skills.end(), sortSkills);\n\n        MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator ();\n        MyGUI::Gui::getInstance ().destroyWidgets (widgets);\n\n        MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);\n\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        for (int i=0; i<3; ++i)\n        {\n            int price = static_cast<int>(pcStats.getSkill (skills[i].first).getBase() * gmst.find(\"iTrainingMod\")->mValue.getInteger());\n            price = std::max(1, price);\n            price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);\n\n            MyGUI::Button* button = mTrainingOptions->createWidget<MyGUI::Button>(price <= playerGold ? \"SandTextButton\" : \"SandTextButtonDisabled\", // can't use setEnabled since that removes tooltip\n                MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default);\n\n            button->setUserData(skills[i].first);\n            button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected);\n\n            button->setCaptionWithReplacing(\"#{\" + ESM::Skill::sSkillNameIds[skills[i].first] + \"} - \" + MyGUI::utility::toString(price));\n\n            button->setSize(button->getTextSize ().width+12, button->getSize().height);\n\n            ToolTips::createSkillToolTip (button, skills[i].first);\n        }\n\n        center();\n    }\n\n    void TrainingWindow::onReferenceUnavailable ()\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training);\n    }\n\n    void TrainingWindow::onCancelButtonClicked (MyGUI::Widget *sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training);\n    }\n\n    void TrainingWindow::onTrainingSelected (MyGUI::Widget *sender)\n    {\n        int skillId = *sender->getUserData<int>();\n\n        MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);\n\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        int price = pcStats.getSkill (skillId).getBase() * store.get<ESM::GameSetting>().find(\"iTrainingMod\")->mValue.getInteger();\n        price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true);\n\n        if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId))\n            return;\n\n        if (getSkillForTraining(mPtr.getClass().getNpcStats(mPtr), skillId) <= pcStats.getSkill(skillId).getBase())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sServiceTrainingWords}\");\n            return;\n        }\n\n        // You can not train a skill above its governing attribute\n        const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(skillId);\n        if (pcStats.getSkill(skillId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage17}\");\n            return;\n        }\n\n        // increase skill\n        MWWorld::LiveCellRef<ESM::NPC> *playerRef = player.get<ESM::NPC>();\n\n        const ESM::Class *class_ =\n            store.get<ESM::Class>().find(playerRef->mBase->mClass);\n        pcStats.increaseSkill (skillId, *class_, true);\n\n        // remove gold\n        player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);\n\n        // add gold to NPC trading gold pool\n        MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats(mPtr);\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't unilaterally change the merchant's gold pool on our client and instead let the server do it\n        */\n        //npcStats.setGoldPool(npcStats.getGoldPool() + price);\n\n        mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectMiscellaneous(mPtr, npcStats.getGoldPool() + price, npcStats.getLastRestockTime().getHour(),\n            npcStats.getLastRestockTime().getDay());\n        objectList->sendObjectMiscellaneous();\n        /*\n            End of tes3mp change (major)\n        */\n\n        // advance time\n        MWBase::Environment::get().getMechanicsManager()->rest(2, false);\n\n        /*\n            Start of tes3mp change (major)\n\n            Multiplayer requires that time not get advanced here\n        */\n        //MWBase::Environment::get().getWorld ()->advanceTime (2);\n        /*\n            End of tes3mp change (major)\n        */\n\n        setVisible(false);\n        mProgressBar.setVisible(true);\n        mProgressBar.setProgress(0, 2);\n        mTimeAdvancer.run(2);\n\n        MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.25);\n        MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.25, false, 0.25);\n    }\n\n    void TrainingWindow::onTrainingProgressChanged(int cur, int total)\n    {\n        mProgressBar.setProgress(cur, total);\n    }\n\n    void TrainingWindow::onTrainingFinished()\n    {\n        mProgressBar.setVisible(false);\n\n        // go back to game mode\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training);\n        MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n    }\n\n    float TrainingWindow::getSkillForTraining(const MWMechanics::NpcStats& stats, int skillId) const\n    {\n        if (mTrainingSkillBasedOnBaseSkill)\n            return stats.getSkill(skillId).getBase();\n        return stats.getSkill(skillId).getModified();\n    }\n\n    void TrainingWindow::onFrame(float dt)\n    {\n        checkReferenceAvailable();\n        mTimeAdvancer.onFrame(dt);\n    }\n\n    bool TrainingWindow::exit()\n    {\n        return !mTimeAdvancer.isRunning();\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/trainingwindow.hpp",
    "content": "#ifndef MWGUI_TRAININGWINDOW_H\n#define MWGUI_TRAININGWINDOW_H\n\n#include \"windowbase.hpp\"\n#include \"referenceinterface.hpp\"\n#include \"timeadvancer.hpp\"\n#include \"waitdialog.hpp\"\n\nnamespace MWMechanics\n{\n    class NpcStats;\n}\n\nnamespace MWGui\n{\n\n    class TrainingWindow : public WindowBase, public ReferenceInterface\n    {\n    public:\n        TrainingWindow();\n\n        void onOpen() override;\n\n        bool exit() override;\n\n        void setPtr(const MWWorld::Ptr& actor) override;\n\n        void onFrame(float dt) override;\n\n        WindowBase* getProgressBar() { return &mProgressBar; }\n\n        void clear() override { resetReference(); }\n\n    protected:\n        void onReferenceUnavailable() override;\n\n        void onCancelButtonClicked (MyGUI::Widget* sender);\n        void onTrainingSelected(MyGUI::Widget* sender);\n\n        void onTrainingProgressChanged(int cur, int total);\n        void onTrainingFinished();\n\n        // Retrieve the base skill value if the setting 'training skills based on base skill' is set;\n        // otherwise returns the modified skill\n        float getSkillForTraining(const MWMechanics::NpcStats& stats, int skillId) const;\n\n        MyGUI::Widget* mTrainingOptions;\n        MyGUI::Button* mCancelButton;\n        MyGUI::TextBox* mPlayerGold;\n\n        WaitDialogProgressBar mProgressBar;\n        TimeAdvancer mTimeAdvancer;\n        bool mTrainingSkillBasedOnBaseSkill;    //corresponds to the setting 'training skills based on base skill'\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/travelwindow.cpp",
    "content": "#include \"travelwindow.hpp\"\n\n#include <MyGUI_Button.h>\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_Gui.h>\n\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/Worldstate.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/actionteleport.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\nnamespace MWGui\n{\n    TravelWindow::TravelWindow() :\n        WindowBase(\"openmw_travel_window.layout\")\n        , mCurrentY(0)\n    {\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mPlayerGold, \"PlayerGold\");\n        getWidget(mSelect, \"Select\");\n        getWidget(mDestinations, \"Travel\");\n        getWidget(mDestinationsView, \"DestinationsView\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onCancelButtonClicked);\n\n        mDestinations->setCoord(450/2-mDestinations->getTextSize().width/2,\n                          mDestinations->getTop(),\n                          mDestinations->getTextSize().width,\n                          mDestinations->getHeight());\n        mSelect->setCoord(8,\n                          mSelect->getTop(),\n                          mSelect->getTextSize().width,\n                          mSelect->getHeight());\n    }\n\n    void TravelWindow::addDestination(const std::string& name, ESM::Position pos, bool interior)\n    {\n        int price;\n\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        if (!mPtr.getCell()->isExterior())\n        {\n            price = gmst.find(\"fMagesGuildTravel\")->mValue.getInteger();\n        }\n        else\n        {\n            ESM::Position PlayerPos = player.getRefData().getPosition();\n            float d = sqrt(pow(pos.pos[0] - PlayerPos.pos[0], 2) + pow(pos.pos[1] - PlayerPos.pos[1], 2) + pow(pos.pos[2] - PlayerPos.pos[2], 2));\n            float fTravelMult = gmst.find(\"fTravelMult\")->mValue.getFloat();\n            if (fTravelMult != 0)\n                price = static_cast<int>(d / fTravelMult);\n            else\n                price = static_cast<int>(d);\n        }\n\n        price = std::max(1, price);\n        price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);\n\n        // Add price for the travelling followers\n        std::set<MWWorld::Ptr> followers;\n        MWWorld::ActionTeleport::getFollowers(player, followers);\n\n        // Apply followers cost, unlike vanilla the first follower doesn't travel for free\n        price *= 1 + static_cast<int>(followers.size());\n\n        int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;\n\n        MyGUI::Button* toAdd = mDestinationsView->createWidget<MyGUI::Button>(\"SandTextButton\", 0, mCurrentY, 200, lineHeight, MyGUI::Align::Default);\n        toAdd->setEnabled(price <= playerGold);\n        mCurrentY += lineHeight;\n        if(interior)\n            toAdd->setUserString(\"interior\",\"y\");\n        else\n            toAdd->setUserString(\"interior\",\"n\");\n\n        toAdd->setUserString(\"price\", std::to_string(price));\n        toAdd->setCaptionWithReplacing(\"#{sCell=\" + name + \"}   -   \" + MyGUI::utility::toString(price)+\"#{sgp}\");\n        toAdd->setSize(mDestinationsView->getWidth(),lineHeight);\n        toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel);\n        toAdd->setUserString(\"Destination\", name);\n        toAdd->setUserData(pos);\n        toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onTravelButtonClick);\n    }\n\n    void TravelWindow::clearDestinations()\n    {\n        mDestinationsView->setViewOffset(MyGUI::IntPoint(0,0));\n        mCurrentY = 0;\n        while (mDestinationsView->getChildCount())\n            MyGUI::Gui::getInstance().destroyWidget(mDestinationsView->getChildAt(0));\n    }\n\n    void TravelWindow::setPtr(const MWWorld::Ptr& actor)\n    {\n        center();\n        mPtr = actor;\n        clearDestinations();\n\n        std::vector<ESM::Transport::Dest> transport;\n        if (mPtr.getClass().isNpc())\n            transport = mPtr.get<ESM::NPC>()->mBase->getTransport();\n        else if (mPtr.getTypeName() == typeid(ESM::Creature).name())\n            transport = mPtr.get<ESM::Creature>()->mBase->getTransport();\n\n        for(unsigned int i = 0;i<transport.size();i++)\n        {\n            std::string cellname = transport[i].mCellName;\n            bool interior = true;\n            int x,y;\n            MWBase::Environment::get().getWorld()->positionToIndex(transport[i].mPos.pos[0],\n                                                                   transport[i].mPos.pos[1],x,y);\n            if (cellname == \"\")\n            {\n                MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(x,y);\n                cellname = MWBase::Environment::get().getWorld()->getCellName(cell);\n                interior = false;\n            }\n            addDestination(cellname,transport[i].mPos,interior);\n        }\n\n        updateLabels();\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mDestinationsView->setVisibleVScroll(false);\n        mDestinationsView->setCanvasSize (MyGUI::IntSize(mDestinationsView->getWidth(), std::max(mDestinationsView->getHeight(), mCurrentY)));\n        mDestinationsView->setVisibleVScroll(true);\n    }\n\n    void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender)\n    {\n        std::istringstream iss(_sender->getUserString(\"price\"));\n        int price;\n        iss >> price;\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        if (playerGold<price)\n            return;\n\n        // Set \"traveling\" flag, so GetPCTraveling can detect teleportation.\n        // We will reset this flag during next world update.\n        MWBase::Environment::get().getWorld()->setPlayerTraveling(true);\n\n        if (!mPtr.getCell()->isExterior())\n            // Interior cell -> mages guild transport\n            MWBase::Environment::get().getWindowManager()->playSound(\"mysticism cast\");\n\n        player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);\n\n        // add gold to NPC trading gold pool\n        MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't unilaterally change the merchant's gold pool on our client and instead let the server do it\n        */\n        //npcStats.setGoldPool(npcStats.getGoldPool() + price);\n\n        mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectMiscellaneous(mPtr, npcStats.getGoldPool() + price, npcStats.getLastRestockTime().getHour(),\n            npcStats.getLastRestockTime().getDay());\n        objectList->sendObjectMiscellaneous();\n        /*\n            End of tes3mp change (major)\n        */\n\n        MWBase::Environment::get().getWindowManager()->fadeScreenOut(1);\n        ESM::Position pos = *_sender->getUserData<ESM::Position>();\n        std::string cellname = _sender->getUserString(\"Destination\");\n        bool interior = _sender->getUserString(\"interior\") == \"y\";\n        if (mPtr.getCell()->isExterior())\n        {\n            ESM::Position playerPos = player.getRefData().getPosition();\n            float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length();\n            int hours = static_cast<int>(d /MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fTravelTimeMult\")->mValue.getFloat());\n            MWBase::Environment::get().getMechanicsManager()->rest(hours, true);\n\n            /*\n                Start of tes3mp change (major)\n\n                Multiplayer requires that time not get advanced here\n            */\n            //MWBase::Environment::get().getWorld()->advanceTime(hours);\n            /*\n                End of tes3mp change (major)\n            */\n        }\n\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);\n        MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n\n        MWBase::Environment::get().getWindowManager()->fadeScreenOut(1);\n\n        // Teleports any followers, too.\n        MWWorld::ActionTeleport action(interior ? cellname : \"\", pos, true);\n        action.execute(player);\n\n        MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);\n        MWBase::Environment::get().getWindowManager()->fadeScreenIn(1);\n    }\n\n    void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);\n    }\n\n    void TravelWindow::updateLabels()\n    {\n        MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);\n\n        mPlayerGold->setCaptionWithReplacing(\"#{sGold}: \" + MyGUI::utility::toString(playerGold));\n        mPlayerGold->setCoord(8,\n                              mPlayerGold->getTop(),\n                              mPlayerGold->getTextSize().width,\n                              mPlayerGold->getHeight());\n    }\n\n    void TravelWindow::onReferenceUnavailable()\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);\n        MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n    }\n\n    void TravelWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)\n    {\n        if (mDestinationsView->getViewOffset().top + _rel*0.3f > 0)\n            mDestinationsView->setViewOffset(MyGUI::IntPoint(0, 0));\n        else\n            mDestinationsView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mDestinationsView->getViewOffset().top + _rel*0.3f)));\n    }\n}\n\n"
  },
  {
    "path": "apps/openmw/mwgui/travelwindow.hpp",
    "content": "#ifndef MWGUI_TravelWINDOW_H\n#define MWGUI_TravelWINDOW_H\n\n\n#include \"windowbase.hpp\"\n#include \"referenceinterface.hpp\"\n\nnamespace MyGUI\n{\n  class Gui;\n  class Widget;\n}\n\nnamespace MWGui\n{\n    class TravelWindow : public ReferenceInterface, public WindowBase\n    {\n        public:\n            TravelWindow();\n\n            void setPtr (const MWWorld::Ptr& actor) override;\n\n        protected:\n            MyGUI::Button* mCancelButton;\n            MyGUI::TextBox* mPlayerGold;\n            MyGUI::TextBox* mDestinations;\n            MyGUI::TextBox* mSelect;\n\n            MyGUI::ScrollView* mDestinationsView;\n\n            void onCancelButtonClicked(MyGUI::Widget* _sender);\n            void onTravelButtonClick(MyGUI::Widget* _sender);\n            void onMouseWheel(MyGUI::Widget* _sender, int _rel);\n            void addDestination(const std::string& name, ESM::Position pos, bool interior);\n            void clearDestinations();\n            int mCurrentY;\n\n            void updateLabels();\n\n            void onReferenceUnavailable() override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/videowidget.cpp",
    "content": "#include \"videowidget.hpp\"\n\n#include <extern/osg-ffmpeg-videoplayer/videoplayer.hpp>\n\n#include <MyGUI_RenderManager.h>\n\n#include <osg/Texture2D>\n\n#include <components/debug/debuglog.hpp>\n#include <components/vfs/manager.hpp>\n#include <components/myguiplatform/myguitexture.hpp>\n\n#include \"../mwsound/movieaudiofactory.hpp\"\n\nnamespace MWGui\n{\n\nVideoWidget::VideoWidget()\n    : mVFS(nullptr)\n{\n    mPlayer.reset(new Video::VideoPlayer());\n    setNeedKeyFocus(true);\n}\n\nVideoWidget::~VideoWidget() = default;\n\nvoid VideoWidget::setVFS(const VFS::Manager *vfs)\n{\n    mVFS = vfs;\n}\n\nvoid VideoWidget::playVideo(const std::string &video)\n{\n    mPlayer->setAudioFactory(new MWSound::MovieAudioFactory());\n\n    Files::IStreamPtr videoStream;\n    try\n    {\n        videoStream = mVFS->get(video);\n    }\n    catch (std::exception& e)\n    {\n        Log(Debug::Error) << \"Failed to open video: \" << e.what();\n        return;\n    }\n\n    mPlayer->playVideo(videoStream, video);\n\n    osg::ref_ptr<osg::Texture2D> texture = mPlayer->getVideoTexture();\n    if (!texture)\n        return;\n\n    mTexture.reset(new osgMyGUI::OSGTexture(texture));\n\n    setRenderItemTexture(mTexture.get());\n    getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));\n}\n\nint VideoWidget::getVideoWidth()\n{\n    return mPlayer->getVideoWidth();\n}\n\nint VideoWidget::getVideoHeight()\n{\n    return mPlayer->getVideoHeight();\n}\n\nbool VideoWidget::update()\n{\n    return mPlayer->update();\n}\n\nvoid VideoWidget::stop()\n{\n    mPlayer->close();\n}\n\nvoid VideoWidget::pause()\n{\n    mPlayer->pause();\n}\n\nvoid VideoWidget::resume()\n{\n    mPlayer->play();\n}\n\nbool VideoWidget::isPaused() const\n{\n    return mPlayer->isPaused();\n}\n\nbool VideoWidget::hasAudioStream()\n{\n    return mPlayer->hasAudioStream();\n}\n\nvoid VideoWidget::autoResize(bool stretch)\n{\n    MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize();\n    if (getParent())\n        screenSize = getParent()->getSize();\n\n    if (getVideoHeight() > 0 && !stretch)\n    {\n        double imageaspect = static_cast<double>(getVideoWidth())/getVideoHeight();\n\n        int leftPadding = std::max(0, static_cast<int>(screenSize.width - screenSize.height * imageaspect) / 2);\n        int topPadding = std::max(0, static_cast<int>(screenSize.height - screenSize.width / imageaspect) / 2);\n\n        setCoord(leftPadding, topPadding,\n                               screenSize.width - leftPadding*2, screenSize.height - topPadding*2);\n    }\n    else\n        setCoord(0,0,screenSize.width,screenSize.height);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/videowidget.hpp",
    "content": "#ifndef OPENMW_MWGUI_VIDEOWIDGET_H\n#define OPENMW_MWGUI_VIDEOWIDGET_H\n\n#include <memory>\n\n#include <MyGUI_Widget.h>\n\nnamespace Video\n{\n    class VideoPlayer;\n}\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace MWGui\n{\n\n    /**\n     * Widget that plays a video.\n     */\n    class VideoWidget : public MyGUI::Widget\n    {\n    public:\n        MYGUI_RTTI_DERIVED(VideoWidget)\n\n        VideoWidget();\n        \n        ~VideoWidget();\n\n        /// Set the VFS (virtual file system) to find the videos on.\n        void setVFS(const VFS::Manager* vfs);\n\n        void playVideo (const std::string& video);\n\n        int getVideoWidth();\n        int getVideoHeight();\n\n        /// @return Is the video still playing?\n        bool update();\n\n        /// Return true if a video is currently playing and it has an audio stream.\n        bool hasAudioStream();\n\n        /// Stop video and free resources (done automatically on destruction)\n        void stop();\n\n        void pause();\n        void resume();\n        bool isPaused() const;\n\n        /// Adjust the coordinates of this video widget relative to its parent,\n        /// based on the dimensions of the playing video.\n        /// @param stretch Stretch the video to fill the whole screen? If false,\n        ///                black bars may be added to fix the aspect ratio.\n        void autoResize (bool stretch);\n\n    private:\n        const VFS::Manager* mVFS;\n        std::unique_ptr<MyGUI::ITexture> mTexture;\n        std::unique_ptr<Video::VideoPlayer> mPlayer;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/waitdialog.cpp",
    "content": "#include \"waitdialog.hpp\"\n\n#include <MyGUI_ProgressBar.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_ScrollBar.h>\n\n#include <components/misc/rng.hpp>\n\n#include <components/widgets/box.hpp>\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWGui\n{\n\n    WaitDialogProgressBar::WaitDialogProgressBar()\n        : WindowBase(\"openmw_wait_dialog_progressbar.layout\")\n    {\n        getWidget(mProgressBar, \"ProgressBar\");\n        getWidget(mProgressText, \"ProgressText\");\n    }\n\n    void WaitDialogProgressBar::onOpen()\n    {\n        center();\n    }\n\n    void WaitDialogProgressBar::setProgress (int cur, int total)\n    {\n        mProgressBar->setProgressRange (total);\n        mProgressBar->setProgressPosition (cur);\n        mProgressText->setCaption(MyGUI::utility::toString(cur) + \"/\" + MyGUI::utility::toString(total));\n    }\n\n    // ---------------------------------------------------------------------------------------------------------\n\n    WaitDialog::WaitDialog()\n        : WindowBase(\"openmw_wait_dialog.layout\")\n        , mTimeAdvancer(0.05f)\n        , mSleeping(false)\n        , mHours(1)\n        , mManualHours(1)\n        , mFadeTimeRemaining(0)\n        , mInterruptAt(-1)\n        , mProgressBar()\n    {\n        getWidget(mDateTimeText, \"DateTimeText\");\n        getWidget(mRestText, \"RestText\");\n        getWidget(mHourText, \"HourText\");\n        getWidget(mUntilHealedButton, \"UntilHealedButton\");\n        getWidget(mWaitButton, \"WaitButton\");\n        getWidget(mCancelButton, \"CancelButton\");\n        getWidget(mHourSlider, \"HourSlider\");\n\n        mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onCancelButtonClicked);\n        mUntilHealedButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onUntilHealedButtonClicked);\n        mWaitButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onWaitButtonClicked);\n        mHourSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &WaitDialog::onHourSliderChangedPosition);\n\n        mCancelButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &WaitDialog::onKeyButtonPressed);\n        mWaitButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &WaitDialog::onKeyButtonPressed);\n        mUntilHealedButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &WaitDialog::onKeyButtonPressed);\n\n        mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &WaitDialog::onWaitingProgressChanged);\n        mTimeAdvancer.eventInterrupted += MyGUI::newDelegate(this, &WaitDialog::onWaitingInterrupted);\n        mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished);\n    }\n\n    void WaitDialog::setPtr(const MWWorld::Ptr &ptr)\n    {\n        setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == MWBase::World::Rest_Allowed);\n\n        if (ptr.isEmpty() && MWBase::Environment::get().getWorld ()->canRest() == MWBase::World::Rest_PlayerIsInAir)\n        {\n            // Resting in air is not allowed unless you're using a bed\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage1}\");\n            MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Rest);\n        }\n            \n        if (mUntilHealedButton->getVisible())\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton);\n        else\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mWaitButton);\n    }\n\n    bool WaitDialog::exit()\n    {\n        return (!mTimeAdvancer.isRunning()); //Only exit if not currently waiting\n    }\n\n    void WaitDialog::clear()\n    {\n        mSleeping = false;\n        mTimeAdvancer.stop();\n    }\n\n    void WaitDialog::onOpen()\n    {\n        if (mTimeAdvancer.isRunning())\n        {\n            mProgressBar.setVisible(true);\n            setVisible(false);\n            return;\n        }\n        else\n        {\n            mProgressBar.setVisible(false);\n        }\n\n        if (!MWBase::Environment::get().getWindowManager ()->getRestEnabled ())\n        {\n            MWBase::Environment::get().getWindowManager()->popGuiMode ();\n        }\n\n        MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld ()->canRest ();\n\n        if (canRest == MWBase::World::Rest_EnemiesAreNearby)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sNotifyMessage2}\");\n            MWBase::Environment::get().getWindowManager()->popGuiMode ();\n        }\n        else if (canRest == MWBase::World::Rest_PlayerIsUnderwater)\n        {\n            // resting underwater not allowed\n            MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage1}\");\n            MWBase::Environment::get().getWindowManager()->popGuiMode ();\n        }\n        /*\n            Start of tes3mp addition\n\n            Prevent resting and waiting if they have been disabled by the server for the local player\n        */\n        else if (canRest == MWBase::World::Rest_Allowed && !mwmp::Main::get().getLocalPlayer()->wildernessRestAllowed &&\n            !mwmp::Main::get().getLocalPlayer()->isUsingBed)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"You are not allowed to rest without a bed.\");\n            MWBase::Environment::get().getWindowManager()->popGuiMode();\n        }\n        else if (canRest == MWBase::World::Rest_OnlyWaiting && !mwmp::Main::get().getLocalPlayer()->waitAllowed &&\n            !mwmp::Main::get().getLocalPlayer()->isUsingBed)\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"You are not allowed to wait.\");\n            MWBase::Environment::get().getWindowManager()->popGuiMode();\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        onHourSliderChangedPosition(mHourSlider, 0);\n        mHourSlider->setScrollPosition (0);\n\n        std::string month = MWBase::Environment::get().getWorld ()->getMonthName();\n        int hour = static_cast<int>(MWBase::Environment::get().getWorld()->getTimeStamp().getHour());\n        bool pm = hour >= 12;\n        if (hour >= 13) hour -= 12;\n        if (hour == 0) hour = 12;\n\n        ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp();\n        int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay();\n        std::string formattedHour = pm ? \"#{sSaveMenuHelp05}\" : \"#{sSaveMenuHelp04}\";\n        std::string dateTimeText = Misc::StringUtils::format(\"%i %s (#{sDay} %i) %i %s\", currentDate.mDay, month, daysPassed, hour, formattedHour);\n        mDateTimeText->setCaptionWithReplacing (dateTimeText);\n    }\n\n    void WaitDialog::onUntilHealedButtonClicked(MyGUI::Widget* sender)\n    {\n        int autoHours = MWBase::Environment::get().getMechanicsManager()->getHoursToRest();\n\n        startWaiting(autoHours);\n    }\n\n    void WaitDialog::onWaitButtonClicked(MyGUI::Widget* sender)\n    {\n        startWaiting(mManualHours);\n    }\n\n    void WaitDialog::startWaiting(int hoursToWait)\n    {\n        /*\n            Start of tes3mp change (major)\n\n            It should not be possible to autosave the game in multiplayer, so it has been disabled\n        */\n        /*\n        if(Settings::Manager::getBool(\"autosave\",\"Saves\")) //autosaves when enabled\n            MWBase::Environment::get().getStateManager()->quickSave(\"Autosave\");\n        */\n        /*\n            End of tes3mp change (major)\n        */\n\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2f);\n        mFadeTimeRemaining = 0.4f;\n        setVisible(false);\n\n        mHours = hoursToWait;\n\n        // FIXME: move this somewhere else?\n        mInterruptAt = -1;\n        MWWorld::Ptr player = world->getPlayerPtr();\n        if (mSleeping && player.getCell()->isExterior())\n        {\n            std::string regionstr = player.getCell()->getCell()->mRegion;\n            if (!regionstr.empty())\n            {\n                const ESM::Region *region = world->getStore().get<ESM::Region>().find (regionstr);\n                if (!region->mSleepList.empty())\n                {\n                    // figure out if player will be woken while sleeping\n                    int x = Misc::Rng::rollDice(hoursToWait);\n                    float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find(\"fSleepRandMod\")->mValue.getFloat();\n                    if (x < fSleepRandMod * hoursToWait)\n                    {\n                        float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find(\"fSleepRestMod\")->mValue.getFloat();\n                        int interruptAtHoursRemaining = int(fSleepRestMod * hoursToWait);\n                        if (interruptAtHoursRemaining != 0)\n                        {\n                            mInterruptAt = hoursToWait - interruptAtHoursRemaining;\n                            mInterruptCreatureList = region->mSleepList;\n                        }\n                    }\n                }\n            }\n        }\n\n        mProgressBar.setProgress (0, hoursToWait);\n    }\n\n    void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender)\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Rest);\n    }\n\n    void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position)\n    {\n        mHourText->setCaptionWithReplacing (MyGUI::utility::toString(position+1) + \" #{sRestMenu2}\");\n        mManualHours = position+1;\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mWaitButton);\n    }\n\n    void WaitDialog::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character)\n    {\n        if (key == MyGUI::KeyCode::ArrowUp)\n            mHourSlider->setScrollPosition(std::min(mHourSlider->getScrollPosition()+1, mHourSlider->getScrollRange()-1));\n        else if (key == MyGUI::KeyCode::ArrowDown)\n            mHourSlider->setScrollPosition(std::max(static_cast<int>(mHourSlider->getScrollPosition())-1, 0));\n        else\n            return;\n        onHourSliderChangedPosition(mHourSlider, mHourSlider->getScrollPosition());\n    }\n\n    void WaitDialog::onWaitingProgressChanged(int cur, int total)\n    {\n        mProgressBar.setProgress(cur, total);\n        MWBase::Environment::get().getMechanicsManager()->rest(1, mSleeping);\n\n        /*\n            Start of tes3mp change (major)\n\n            Multiplayer requires that time not get advanced here\n        */\n        //MWBase::Environment::get().getWorld()->advanceTime(1);\n        /*\n            End of tes3mp change (major)\n        */\n\n        MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n        if (player.getClass().getCreatureStats(player).isDead())\n            stopWaiting();\n    }\n\n    void WaitDialog::onWaitingInterrupted()\n    {\n        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sSleepInterrupt}\");\n        MWBase::Environment::get().getWorld()->spawnRandomCreature(mInterruptCreatureList);\n        stopWaiting();\n    }\n\n    void WaitDialog::onWaitingFinished()\n    {\n        stopWaiting();\n\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        const MWMechanics::NpcStats &pcstats = player.getClass().getNpcStats(player);\n\n        // trigger levelup if possible\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        if (mSleeping && pcstats.getLevelProgress () >= gmst.find(\"iLevelUpTotal\")->mValue.getInteger())\n        {\n            MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup);\n        }\n    }\n\n    void WaitDialog::setCanRest (bool canRest)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n        bool full = (stats.getHealth().getCurrent() >= stats.getHealth().getModified())\n                && (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified());\n        MWMechanics::NpcStats& npcstats = player.getClass().getNpcStats(player);\n        bool werewolf = npcstats.isWerewolf();\n\n        mUntilHealedButton->setVisible(canRest && !full);\n        mWaitButton->setCaptionWithReplacing (canRest ? \"#{sRest}\" : \"#{sWait}\");\n        mRestText->setCaptionWithReplacing (canRest ? \"#{sRestMenu3}\"\n                                                    : (werewolf ? \"#{sWerewolfRestMessage}\"\n                                                                : \"#{sRestIllegal}\"));\n\n        mSleeping = canRest;\n\n        Gui::Box* box = dynamic_cast<Gui::Box*>(mMainWidget);\n        if (box == nullptr)\n            throw std::runtime_error(\"main widget must be a box\");\n        box->notifyChildrenSizeChanged();\n        center();\n    }\n\n    void WaitDialog::onFrame(float dt)\n    {\n        mTimeAdvancer.onFrame(dt);\n\n        if (mFadeTimeRemaining <= 0)\n            return;\n\n        mFadeTimeRemaining -= dt;\n\n        if (mFadeTimeRemaining <= 0)\n        {\n            mProgressBar.setVisible(true);\n            mTimeAdvancer.run(mHours, mInterruptAt);\n        }\n    }\n\n    void WaitDialog::stopWaiting ()\n    {\n        MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.2f);\n        mProgressBar.setVisible (false);\n        MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest);\n        mTimeAdvancer.stop();\n    }\n\n\n    void WaitDialog::wakeUp ()\n    {\n        mSleeping = false;\n        if (mInterruptAt != -1)\n            onWaitingInterrupted();\n        else\n            stopWaiting();\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/waitdialog.hpp",
    "content": "#ifndef MWGUI_WAIT_DIALOG_H\n#define MWGUI_WAIT_DIALOG_H\n\n#include \"timeadvancer.hpp\"\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n\n    class WaitDialogProgressBar : public WindowBase\n    {\n    public:\n        WaitDialogProgressBar();\n\n        void onOpen() override;\n\n        void setProgress(int cur, int total);\n\n    protected:\n        MyGUI::ProgressBar* mProgressBar;\n        MyGUI::TextBox* mProgressText;\n    };\n\n    class WaitDialog : public WindowBase\n    {\n    public:\n        WaitDialog();\n\n        void setPtr(const MWWorld::Ptr &ptr) override;\n\n        void onOpen() override;\n\n        bool exit() override;\n\n        void clear() override;\n\n        void onFrame(float dt) override;\n\n        bool getSleeping() { return mTimeAdvancer.isRunning() && mSleeping; }\n        void wakeUp();\n        void autosave();\n\n        WindowBase* getProgressBar() { return &mProgressBar; }\n\n    protected:\n        MyGUI::TextBox* mDateTimeText;\n        MyGUI::TextBox* mRestText;\n        MyGUI::TextBox* mHourText;\n        MyGUI::Button* mUntilHealedButton;\n        MyGUI::Button* mWaitButton;\n        MyGUI::Button* mCancelButton;\n        MyGUI::ScrollBar* mHourSlider;\n\n        TimeAdvancer mTimeAdvancer;\n        bool mSleeping;\n        int mHours;\n        int mManualHours; // stores the hours to rest selected via slider\n        float mFadeTimeRemaining;\n\n        int mInterruptAt;\n        std::string mInterruptCreatureList;\n\n        WaitDialogProgressBar mProgressBar;\n\n        void onUntilHealedButtonClicked(MyGUI::Widget* sender);\n        void onWaitButtonClicked(MyGUI::Widget* sender);\n        void onCancelButtonClicked(MyGUI::Widget* sender);\n        void onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position);\n        void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character);\n\n        void onWaitingProgressChanged(int cur, int total);\n        void onWaitingInterrupted();\n        void onWaitingFinished();\n\n        void setCanRest(bool canRest);\n\n        void startWaiting(int hoursToWait);\n        void stopWaiting();\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/widgets.cpp",
    "content": "#include \"widgets.hpp\"\n\n#include <sstream>\n#include <iomanip>\n\n#include <MyGUI_ProgressBar.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_ControllerManager.h>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"controllers.hpp\"\n\nnamespace MWGui\n{\n    namespace Widgets\n    {\n        /* MWSkill */\n\n        MWSkill::MWSkill()\n            : mSkillId(ESM::Skill::Length)\n            , mSkillNameWidget(nullptr)\n            , mSkillValueWidget(nullptr)\n        {\n        }\n\n        void MWSkill::setSkillId(ESM::Skill::SkillEnum skill)\n        {\n            mSkillId = skill;\n            updateWidgets();\n        }\n\n        void MWSkill::setSkillNumber(int skill)\n        {\n            if (skill < 0)\n                setSkillId(ESM::Skill::Length);\n            else if (skill < ESM::Skill::Length)\n                setSkillId(static_cast<ESM::Skill::SkillEnum>(skill));\n            else\n                throw std::runtime_error(\"Skill number out of range\");\n        }\n\n        void MWSkill::setSkillValue(const SkillValue& value)\n        {\n            mValue = value;\n            updateWidgets();\n        }\n\n        void MWSkill::updateWidgets()\n        {\n            if (mSkillNameWidget)\n            {\n                if (mSkillId == ESM::Skill::Length)\n                {\n                    mSkillNameWidget->setCaption(\"\");\n                }\n                else\n                {\n                    const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], \"\");\n                    mSkillNameWidget->setCaption(name);\n                }\n            }\n            if (mSkillValueWidget)\n            {\n                SkillValue::Type modified = mValue.getModified(), base = mValue.getBase();\n                mSkillValueWidget->setCaption(MyGUI::utility::toString(modified));\n                if (modified > base)\n                    mSkillValueWidget->_setWidgetState(\"increased\");\n                else if (modified < base)\n                    mSkillValueWidget->_setWidgetState(\"decreased\");\n                else\n                    mSkillValueWidget->_setWidgetState(\"normal\");\n            }\n        }\n\n        void MWSkill::onClicked(MyGUI::Widget* _sender)\n        {\n            eventClicked(this);\n        }\n\n        MWSkill::~MWSkill()\n        {\n        }\n\n        void MWSkill::initialiseOverride()\n        {\n            Base::initialiseOverride();\n\n            assignWidget(mSkillNameWidget, \"StatName\");\n            assignWidget(mSkillValueWidget, \"StatValue\");\n\n            MyGUI::Button* button;\n            assignWidget(button, \"StatNameButton\");\n            if (button)\n            {\n                mSkillNameWidget = button;\n                button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked);\n            }\n\n            button = nullptr;\n            assignWidget(button, \"StatValueButton\");\n            if (button)\n            {\n                mSkillValueWidget = button;\n                button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked);\n            }\n        }\n\n        /* MWAttribute */\n\n        MWAttribute::MWAttribute()\n            : mId(-1)\n            , mAttributeNameWidget(nullptr)\n            , mAttributeValueWidget(nullptr)\n        {\n        }\n\n        void MWAttribute::setAttributeId(int attributeId)\n        {\n            mId = attributeId;\n            updateWidgets();\n        }\n\n        void MWAttribute::setAttributeValue(const AttributeValue& value)\n        {\n            mValue = value;\n            updateWidgets();\n        }\n\n        void MWAttribute::onClicked(MyGUI::Widget* _sender)\n        {\n            eventClicked(this);\n        }\n\n        void MWAttribute::updateWidgets()\n        {\n            if (mAttributeNameWidget)\n            {\n                if (mId < 0 || mId >= 8)\n                {\n                    mAttributeNameWidget->setCaption(\"\");\n                }\n                else\n                {\n                    static const char *attributes[8] = {\n                        \"sAttributeStrength\",\n                        \"sAttributeIntelligence\",\n                        \"sAttributeWillpower\",\n                        \"sAttributeAgility\",\n                        \"sAttributeSpeed\",\n                        \"sAttributeEndurance\",\n                        \"sAttributePersonality\",\n                        \"sAttributeLuck\"\n                    };\n                    const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(attributes[mId], \"\");\n                    mAttributeNameWidget->setCaption(name);\n                }\n            }\n            if (mAttributeValueWidget)\n            {\n                int modified = mValue.getModified(), base = mValue.getBase();\n                mAttributeValueWidget->setCaption(MyGUI::utility::toString(modified));\n                if (modified > base)\n                    mAttributeValueWidget->_setWidgetState(\"increased\");\n                else if (modified < base)\n                    mAttributeValueWidget->_setWidgetState(\"decreased\");\n                else\n                    mAttributeValueWidget->_setWidgetState(\"normal\");\n            }\n        }\n\n        MWAttribute::~MWAttribute()\n        {\n        }\n\n        void MWAttribute::initialiseOverride()\n        {\n            Base::initialiseOverride();\n\n            assignWidget(mAttributeNameWidget, \"StatName\");\n            assignWidget(mAttributeValueWidget, \"StatValue\");\n\n            MyGUI::Button* button;\n            assignWidget(button, \"StatNameButton\");\n            if (button)\n            {\n                mAttributeNameWidget = button;\n                button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked);\n            }\n\n            button = nullptr;\n            assignWidget(button, \"StatValueButton\");\n            if (button)\n            {\n                mAttributeValueWidget = button;\n                button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked);\n            }\n        }\n\n        /* MWSpell */\n\n        MWSpell::MWSpell()\n            : mSpellNameWidget(nullptr)\n        {\n        }\n\n        void MWSpell::setSpellId(const std::string &spellId)\n        {\n            mId = spellId;\n            updateWidgets();\n        }\n\n        void MWSpell::createEffectWidgets(std::vector<MyGUI::Widget*> &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags)\n        {\n            const MWWorld::ESMStore &store =\n                MWBase::Environment::get().getWorld()->getStore();\n\n            const ESM::Spell *spell = store.get<ESM::Spell>().search(mId);\n            MYGUI_ASSERT(spell, \"spell with id '\" << mId << \"' not found\");\n\n            for (const ESM::ENAMstruct& effectInfo : spell->mEffects.mList)\n            {\n                MWSpellEffectPtr effect = creator->createWidget<MWSpellEffect>(\"MW_EffectImage\", coord, MyGUI::Align::Default);\n                SpellEffectParams params;\n                params.mEffectID = effectInfo.mEffectID;\n                params.mSkill = effectInfo.mSkill;\n                params.mAttribute = effectInfo.mAttribute;\n                params.mDuration = effectInfo.mDuration;\n                params.mMagnMin = effectInfo.mMagnMin;\n                params.mMagnMax = effectInfo.mMagnMax;\n                params.mRange = effectInfo.mRange;\n                params.mIsConstant = (flags & MWEffectList::EF_Constant) != 0;\n                params.mNoTarget = (flags & MWEffectList::EF_NoTarget);\n                params.mNoMagnitude = (flags & MWEffectList::EF_NoMagnitude);\n                effect->setSpellEffect(params);\n                effects.push_back(effect);\n                coord.top += effect->getHeight();\n                coord.width = std::max(coord.width, effect->getRequestedWidth());\n            }\n        }\n\n        void MWSpell::updateWidgets()\n        {\n            if (mSpellNameWidget && MWBase::Environment::get().getWindowManager())\n            {\n                const MWWorld::ESMStore &store =\n                    MWBase::Environment::get().getWorld()->getStore();\n\n                const ESM::Spell *spell = store.get<ESM::Spell>().search(mId);\n                if (spell)\n                    mSpellNameWidget->setCaption(spell->mName);\n                else\n                    mSpellNameWidget->setCaption(\"\");\n            }\n        }\n\n        void MWSpell::initialiseOverride()\n        {\n            Base::initialiseOverride();\n\n            assignWidget(mSpellNameWidget, \"StatName\");\n        }\n\n        MWSpell::~MWSpell()\n        {\n        }\n\n        /* MWEffectList */\n\n        MWEffectList::MWEffectList()\n            : mEffectList(0)\n        {\n        }\n\n        void MWEffectList::setEffectList(const SpellEffectList& list)\n        {\n            mEffectList = list;\n            updateWidgets();\n        }\n\n        void MWEffectList::createEffectWidgets(std::vector<MyGUI::Widget*> &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags)\n        {\n            // We don't know the width of all the elements beforehand, so we do it in\n            // 2 steps: first, create all widgets and check their width....\n            MWSpellEffectPtr effect = nullptr;\n            int maxwidth = coord.width;\n\n            for (auto& effectInfo : mEffectList)\n            {\n                effect = creator->createWidget<MWSpellEffect>(\"MW_EffectImage\", coord, MyGUI::Align::Default);\n                effectInfo.mIsConstant = (flags & EF_Constant) || effectInfo.mIsConstant;\n                effectInfo.mNoTarget = (flags & EF_NoTarget) || effectInfo.mNoTarget;\n                effectInfo.mNoMagnitude = (flags & EF_NoMagnitude) || effectInfo.mNoMagnitude;\n                effect->setSpellEffect(effectInfo);\n                effects.push_back(effect);\n                if (effect->getRequestedWidth() > maxwidth)\n                    maxwidth = effect->getRequestedWidth();\n\n                coord.top += effect->getHeight();\n            }\n\n            // ... then adjust the size for all widgets\n            for (MyGUI::Widget* effectWidget : effects)\n            {\n                effect = effectWidget->castType<MWSpellEffect>();\n                bool needcenter = center && (maxwidth > effect->getRequestedWidth());\n                int diff = maxwidth - effect->getRequestedWidth();\n                if (needcenter)\n                {\n                    effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height);\n                }\n                else\n                {\n                    effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height);\n                }\n            }\n\n            // inform the parent about width\n            coord.width = maxwidth;\n        }\n\n        void MWEffectList::updateWidgets()\n        {\n        }\n\n        void MWEffectList::initialiseOverride()\n        {\n            Base::initialiseOverride();\n        }\n\n        MWEffectList::~MWEffectList()\n        {\n        }\n\n        SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects)\n        {\n            SpellEffectList result;\n            for (const ESM::ENAMstruct& effectInfo : effects->mList)\n            {\n                SpellEffectParams params;\n                params.mEffectID = effectInfo.mEffectID;\n                params.mSkill = effectInfo.mSkill;\n                params.mAttribute = effectInfo.mAttribute;\n                params.mDuration = effectInfo.mDuration;\n                params.mMagnMin = effectInfo.mMagnMin;\n                params.mMagnMax = effectInfo.mMagnMax;\n                params.mRange = effectInfo.mRange;\n                params.mArea = effectInfo.mArea;\n                result.push_back(params);\n            }\n            return result;\n        }\n\n        /* MWSpellEffect */\n\n        MWSpellEffect::MWSpellEffect()\n            : mImageWidget(nullptr)\n            , mTextWidget(nullptr)\n            , mRequestedWidth(0)\n        {\n        }\n\n        void MWSpellEffect::setSpellEffect(const SpellEffectParams& params)\n        {\n            mEffectParams = params;\n            updateWidgets();\n        }\n\n        void MWSpellEffect::updateWidgets()\n        {\n            if (!mEffectParams.mKnown)\n            {\n                mTextWidget->setCaption (\"?\");\n                mTextWidget->setCoord(sIconOffset / 2, mTextWidget->getCoord().top, mTextWidget->getCoord().width, mTextWidget->getCoord().height); // Compensates for the missing image when effect is not known\n                mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset;\n                mImageWidget->setImageTexture (\"\");\n                return;\n            }\n\n            const MWWorld::ESMStore &store =\n                MWBase::Environment::get().getWorld()->getStore();\n\n            const ESM::MagicEffect *magicEffect =\n                store.get<ESM::MagicEffect>().search(mEffectParams.mEffectID);\n\n            assert(magicEffect);\n\n            std::string pt =  MWBase::Environment::get().getWindowManager()->getGameSettingString(\"spoint\", \"\");\n            std::string pts =  MWBase::Environment::get().getWindowManager()->getGameSettingString(\"spoints\", \"\");\n            std::string pct =  MWBase::Environment::get().getWindowManager()->getGameSettingString(\"spercent\", \"\");\n            std::string ft =  MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sfeet\", \"\");\n            std::string lvl =  MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sLevel\", \"\");\n            std::string lvls =  MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sLevels\", \"\");\n            std::string to =  \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sTo\", \"\") + \" \";\n            std::string sec =  \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(\"ssecond\", \"\");\n            std::string secs =  \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sseconds\", \"\");\n\n            std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID);\n            std::string spellLine = MWBase::Environment::get().getWindowManager()->getGameSettingString(effectIDStr, \"\");\n\n            if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill && mEffectParams.mSkill != -1)\n            {\n                spellLine += \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], \"\");\n            }\n            if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute && mEffectParams.mAttribute != -1)\n            {\n                spellLine += \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], \"\");\n            }\n\n            if (mEffectParams.mMagnMin || mEffectParams.mMagnMax) {\n                ESM::MagicEffect::MagnitudeDisplayType displayType = magicEffect->getMagnitudeDisplayType();\n                if ( displayType == ESM::MagicEffect::MDT_TimesInt ) {\n                    std::string timesInt =  MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sXTimesINT\", \"\");\n                    std::stringstream formatter;\n\n                    formatter << std::fixed << std::setprecision(1) << \" \" << (mEffectParams.mMagnMin / 10.0f);\n                    if (mEffectParams.mMagnMin != mEffectParams.mMagnMax)\n                        formatter << to << (mEffectParams.mMagnMax / 10.0f);\n                    formatter << timesInt;\n\n                    spellLine += formatter.str();\n                }\n                else if ( displayType != ESM::MagicEffect::MDT_None  && !mEffectParams.mNoMagnitude) {\n                    spellLine += \" \" + MyGUI::utility::toString(mEffectParams.mMagnMin);\n                    if (mEffectParams.mMagnMin != mEffectParams.mMagnMax)\n                        spellLine += to + MyGUI::utility::toString(mEffectParams.mMagnMax);\n\n                    if ( displayType == ESM::MagicEffect::MDT_Percentage )\n                        spellLine += pct;\n                    else if ( displayType == ESM::MagicEffect::MDT_Feet )\n                        spellLine += \" \" + ft;\n                    else if ( displayType == ESM::MagicEffect::MDT_Level )\n                        spellLine += \" \" + ((mEffectParams.mMagnMin == mEffectParams.mMagnMax && std::abs(mEffectParams.mMagnMin) == 1) ? lvl : lvls );\n                    else  // ESM::MagicEffect::MDT_Points\n                        spellLine += \" \" + ((mEffectParams.mMagnMin == mEffectParams.mMagnMax && std::abs(mEffectParams.mMagnMin) == 1) ? pt : pts );\n                }\n            }\n\n            // constant effects have no duration and no target\n            if (!mEffectParams.mIsConstant)\n            {\n                if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce))\n                    mEffectParams.mDuration = std::max(1, mEffectParams.mDuration);\n\n                if (mEffectParams.mDuration > 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))\n                {\n                    spellLine += \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sfor\", \"\") + \" \" + MyGUI::utility::toString(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs);\n                }\n\n                if (mEffectParams.mArea > 0)\n                {\n                    spellLine += \" #{sin} \" + MyGUI::utility::toString(mEffectParams.mArea) + \" #{sfootarea}\";\n                }\n\n                // potions have no target\n                if (!mEffectParams.mNoTarget)\n                {\n                    std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sonword\", \"\");\n                    if (mEffectParams.mRange == ESM::RT_Self)\n                        spellLine += \" \" + on + \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRangeSelf\", \"\");\n                    else if (mEffectParams.mRange == ESM::RT_Touch)\n                        spellLine += \" \" + on + \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRangeTouch\", \"\");\n                    else if (mEffectParams.mRange == ESM::RT_Target)\n                        spellLine += \" \" + on + \" \" + MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sRangeTarget\", \"\");\n                }\n            }\n\n            mTextWidget->setCaptionWithReplacing(spellLine);\n            mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset;\n\n            mImageWidget->setImageTexture(MWBase::Environment::get().getWindowManager()->correctIconPath(magicEffect->mIcon));\n        }\n\n        MWSpellEffect::~MWSpellEffect()\n        {\n        }\n\n        void MWSpellEffect::initialiseOverride()\n        {\n            Base::initialiseOverride();\n\n            assignWidget(mTextWidget, \"Text\");\n            assignWidget(mImageWidget, \"Image\");\n        }\n\n        /* MWDynamicStat */\n\n        MWDynamicStat::MWDynamicStat()\n        : mValue(0)\n        , mMax(1)\n        , mTextWidget(nullptr)\n        , mBarWidget(nullptr)\n        , mBarTextWidget(nullptr)\n        {\n        }\n\n        void MWDynamicStat::setValue(int cur, int max)\n        {\n            mValue = cur;\n            mMax = max;\n\n            if (mBarWidget)\n            {\n                mBarWidget->setProgressRange(std::max(0, mMax));\n                mBarWidget->setProgressPosition(std::max(0, mValue));\n            }\n\n            if (mBarTextWidget)\n            {\n                std::stringstream out;\n                out << mValue << \"/\" << mMax;\n                mBarTextWidget->setCaption(out.str().c_str());\n            }\n        }\n        void MWDynamicStat::setTitle(const std::string& text)\n        {\n            if (mTextWidget)\n                mTextWidget->setCaption(text);\n        }\n\n        MWDynamicStat::~MWDynamicStat()\n        {\n        }\n\n        void MWDynamicStat::initialiseOverride()\n        {\n            Base::initialiseOverride();\n\n            assignWidget(mTextWidget, \"Text\");\n            assignWidget(mBarWidget, \"Bar\");\n            assignWidget(mBarTextWidget, \"BarText\");\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/widgets.hpp",
    "content": "#ifndef MWGUI_WIDGETS_H\n#define MWGUI_WIDGETS_H\n\n#include \"../mwmechanics/stat.hpp\"\n\n#include <components/esm/effectlist.hpp>\n#include <components/esm/loadskil.hpp>\n\n#include <MyGUI_Button.h>\n#include <MyGUI_EditBox.h>\n#include <MyGUI_ScrollBar.h>\n\nnamespace MyGUI\n{\n    class ImageBox;\n    class ControllerItem;\n}\n\nnamespace MWBase\n{\n    class WindowManager;\n}\n\n/*\n  This file contains various custom widgets used in OpenMW.\n */\n\nnamespace MWGui\n{\n    namespace Widgets\n    {\n        class MWEffectList;\n\n        void fixTexturePath(std::string &path);\n\n        struct SpellEffectParams\n        {\n            SpellEffectParams()\n                : mNoTarget(false)\n                , mIsConstant(false)\n                , mNoMagnitude(false)\n                , mKnown(true)\n                , mEffectID(-1)\n                , mSkill(-1)\n                , mAttribute(-1)\n                , mMagnMin(-1)\n                , mMagnMax(-1)\n                , mRange(-1)\n                , mDuration(-1)\n                , mArea(0)\n            {\n            }\n\n            bool mNoTarget; // potion effects for example have no target (target is always the player)\n            bool mIsConstant; // constant effect means that duration will not be displayed\n            bool mNoMagnitude; // effect magnitude will not be displayed (e.g ingredients)\n\n            bool mKnown; // is this effect known to the player? (If not, will display as a question mark instead)\n\n            // value of -1 here means the effect is unknown to the player\n            short mEffectID;\n\n            // value of -1 here means there is no skill/attribute\n            signed char mSkill, mAttribute;\n\n            // value of -1 here means the value is unavailable\n            int mMagnMin, mMagnMax, mRange, mDuration;\n\n            // value of 0 -> no area effect\n            int mArea;\n\n            bool operator==(const SpellEffectParams& other) const\n            {\n                if (mEffectID !=  other.mEffectID)\n                    return false;\n\n                bool involvesAttribute = (mEffectID == 74 // restore attribute\n                                        || mEffectID == 85 // absorb attribute\n                                        || mEffectID == 17 // drain attribute\n                                        || mEffectID == 79 // fortify attribute\n                                        || mEffectID == 22); // damage attribute\n                bool involvesSkill = (mEffectID == 78 // restore skill\n                                        || mEffectID == 89 // absorb skill\n                                        || mEffectID == 21 // drain skill\n                                        || mEffectID == 83 // fortify skill\n                                        || mEffectID == 26); // damage skill\n                return ((other.mSkill == mSkill) || !involvesSkill) && ((other.mAttribute == mAttribute) && !involvesAttribute) && (other.mArea == mArea);\n            }\n        };\n\n        typedef std::vector<SpellEffectParams> SpellEffectList;\n\n        class MWSkill final : public MyGUI::Widget\n        {\n            MYGUI_RTTI_DERIVED( MWSkill )\n        public:\n            MWSkill();\n\n            typedef MWMechanics::Stat<float> SkillValue;\n\n            void setSkillId(ESM::Skill::SkillEnum skillId);\n            void setSkillNumber(int skillId);\n            void setSkillValue(const SkillValue& value);\n\n            ESM::Skill::SkillEnum getSkillId() const { return mSkillId; }\n            const SkillValue& getSkillValue() const { return mValue; }\n\n            // Events\n            typedef MyGUI::delegates::CMultiDelegate1<MWSkill*> EventHandle_SkillVoid;\n\n            /** Event : Skill clicked.\\n\n                signature : void method(MWSkill* _sender)\\n\n            */\n            EventHandle_SkillVoid eventClicked;\n\n        protected:\n            virtual ~MWSkill();\n\n            void initialiseOverride() override;\n\n            void onClicked(MyGUI::Widget* _sender);\n\n        private:\n\n            void updateWidgets();\n\n            ESM::Skill::SkillEnum mSkillId;\n            SkillValue mValue;\n            MyGUI::TextBox* mSkillNameWidget;\n            MyGUI::TextBox* mSkillValueWidget;\n        };\n        typedef MWSkill* MWSkillPtr;\n\n        class MWAttribute final : public MyGUI::Widget\n        {\n            MYGUI_RTTI_DERIVED( MWAttribute )\n        public:\n            MWAttribute();\n\n            typedef MWMechanics::AttributeValue AttributeValue;\n\n            void setAttributeId(int attributeId);\n            void setAttributeValue(const AttributeValue& value);\n\n            int getAttributeId() const { return mId; }\n            const AttributeValue& getAttributeValue() const { return mValue; }\n\n            // Events\n            typedef MyGUI::delegates::CMultiDelegate1<MWAttribute*> EventHandle_AttributeVoid;\n\n            /** Event : Attribute clicked.\\n\n                signature : void method(MWAttribute* _sender)\\n\n            */\n            EventHandle_AttributeVoid eventClicked;\n\n        protected:\n            virtual ~MWAttribute();\n\n            void initialiseOverride() override;\n\n            void onClicked(MyGUI::Widget* _sender);\n\n        private:\n\n            void updateWidgets();\n\n            int mId;\n            AttributeValue mValue;\n            MyGUI::TextBox* mAttributeNameWidget;\n            MyGUI::TextBox* mAttributeValueWidget;\n        };\n        typedef MWAttribute* MWAttributePtr;\n\n        /**\n         * @todo remove this class and use MWEffectList instead\n         */\n        class MWSpellEffect;\n        class MWSpell final : public MyGUI::Widget\n        {\n            MYGUI_RTTI_DERIVED( MWSpell )\n        public:\n            MWSpell();\n\n            typedef MWMechanics::Stat<int> SpellValue;\n\n            void setSpellId(const std::string &id);\n\n            /**\n             * @param vector to store the created effect widgets\n             * @param parent widget\n             * @param coordinates to use, will be expanded if more space is needed\n             * @param spell category, if this is 0, this means the spell effects are permanent and won't display e.g. duration\n             * @param various flags, see MWEffectList::EffectFlags\n             */\n            void createEffectWidgets(std::vector<MyGUI::Widget*> &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags);\n\n            const std::string &getSpellId() const { return mId; }\n\n        protected:\n            virtual ~MWSpell();\n\n            void initialiseOverride() override;\n\n        private:\n            void updateWidgets();\n\n            std::string mId;\n            MyGUI::TextBox* mSpellNameWidget;\n        };\n        typedef MWSpell* MWSpellPtr;\n\n        class MWEffectList final : public MyGUI::Widget\n        {\n            MYGUI_RTTI_DERIVED( MWEffectList )\n        public:\n            MWEffectList();\n\n            typedef MWMechanics::Stat<int> EnchantmentValue;\n\n            enum EffectFlags\n            {\n                EF_NoTarget = 0x01, // potions have no target (target is always the player)\n                EF_Constant = 0x02, // constant effect means that duration will not be displayed\n                EF_NoMagnitude = 0x04 // ingredients have no magnitude\n\n            };\n\n            void setEffectList(const SpellEffectList& list);\n\n            static SpellEffectList effectListFromESM(const ESM::EffectList* effects);\n\n            /**\n             * @param vector to store the created effect widgets\n             * @param parent widget\n             * @param coordinates to use, will be expanded if more space is needed\n             * @param center the effect widgets horizontally\n             * @param various flags, see MWEffectList::EffectFlags\n             */\n            void createEffectWidgets(std::vector<MyGUI::Widget*> &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags);\n\n        protected:\n            virtual ~MWEffectList();\n\n            void initialiseOverride() override;\n\n        private:\n            void updateWidgets();\n\n            SpellEffectList mEffectList;\n        };\n        typedef MWEffectList* MWEffectListPtr;\n\n        class MWSpellEffect final : public MyGUI::Widget\n        {\n            MYGUI_RTTI_DERIVED( MWSpellEffect )\n        public:\n            MWSpellEffect();\n\n            typedef ESM::ENAMstruct SpellEffectValue;\n\n            void setSpellEffect(const SpellEffectParams& params);\n\n            int getRequestedWidth() const { return mRequestedWidth; }\n\n        protected:\n            virtual ~MWSpellEffect();\n\n            void initialiseOverride() override;\n\n        private:\n            static constexpr int sIconOffset = 24;\n            \n            void updateWidgets();\n\n            SpellEffectParams mEffectParams;\n            MyGUI::ImageBox* mImageWidget;\n            MyGUI::TextBox* mTextWidget;\n            int mRequestedWidth;\n        };\n        typedef MWSpellEffect* MWSpellEffectPtr;\n\n        class MWDynamicStat final : public MyGUI::Widget\n        {\n            MYGUI_RTTI_DERIVED( MWDynamicStat )\n        public:\n            MWDynamicStat();\n\n            void setValue(int value, int max);\n            void setTitle(const std::string& text);\n\n            int getValue() const { return mValue; }\n            int getMax() const { return mMax; }\n\n        protected:\n            virtual ~MWDynamicStat();\n\n            void initialiseOverride() override;\n\n        private:\n\n            int mValue, mMax;\n            MyGUI::TextBox* mTextWidget;\n            MyGUI::ProgressBar* mBarWidget;\n            MyGUI::TextBox* mBarTextWidget;\n        };\n        typedef MWDynamicStat* MWDynamicStatPtr;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/windowbase.cpp",
    "content": "#include \"windowbase.hpp\"\n\n#include <MyGUI_Button.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_RenderManager.h>\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include <components/widgets/imagebutton.hpp>\n\n#include \"draganddrop.hpp\"\n#include \"exposedwindow.hpp\"\n\nusing namespace MWGui;\n\nWindowBase::WindowBase(const std::string& parLayout)\n  : Layout(parLayout)\n{\n    mMainWidget->setVisible(false);\n\n    Window* window = mMainWidget->castType<Window>(false);\n    if (!window)\n        return;\n\n    MyGUI::Button* button = nullptr;\n    MyGUI::VectorWidgetPtr widgets = window->getSkinWidgetsByName(\"Action\");\n    for (MyGUI::Widget* widget : widgets)\n    {\n        if (widget->isUserString(\"SupportDoubleClick\"))\n            button = widget->castType<MyGUI::Button>();\n    }\n\n    if (button)\n        button->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &WindowBase::onDoubleClick);\n}\n\nvoid WindowBase::onTitleDoubleClicked()\n{\n    if (MyGUI::InputManager::getInstance().isShiftPressed())\n        MWBase::Environment::get().getWindowManager()->toggleMaximized(this);\n}\n\nvoid WindowBase::onDoubleClick(MyGUI::Widget *_sender)\n{\n    onTitleDoubleClicked();\n}\n\nvoid WindowBase::setVisible(bool visible)\n{\n    bool wasVisible = mMainWidget->getVisible();\n    mMainWidget->setVisible(visible);\n\n    if (visible)\n        onOpen();\n    else if (wasVisible)\n        onClose();\n}\n\nbool WindowBase::isVisible()\n{\n    return mMainWidget->getVisible();\n}\n\nvoid WindowBase::center()\n{\n    // Centre dialog\n\n    MyGUI::IntSize layerSize = MyGUI::RenderManager::getInstance().getViewSize();\n    if (mMainWidget->getLayer())\n        layerSize = mMainWidget->getLayer()->getSize();\n\n    MyGUI::IntCoord coord = mMainWidget->getCoord();\n    coord.left = (layerSize.width - coord.width)/2;\n    coord.top = (layerSize.height - coord.height)/2;\n    mMainWidget->setCoord(coord);\n}\n\nWindowModal::WindowModal(const std::string& parLayout)\n    : WindowBase(parLayout)\n{\n}\n\nvoid WindowModal::onOpen()\n{\n    MWBase::Environment::get().getWindowManager()->addCurrentModal(this); //Set so we can escape it if needed\n\n    MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n    MyGUI::InputManager::getInstance ().addWidgetModal (mMainWidget);\n    MyGUI::InputManager::getInstance().setKeyFocusWidget(focus);\n}\n\nvoid WindowModal::onClose()\n{\n    MWBase::Environment::get().getWindowManager()->removeCurrentModal(this);\n\n    MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget);\n}\n\nNoDrop::NoDrop(DragAndDrop *drag, MyGUI::Widget *widget)\n    : mWidget(widget), mDrag(drag), mTransparent(false)\n{\n}\n\nvoid NoDrop::onFrame(float dt)\n{\n    if (!mWidget)\n        return;\n\n    MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition();\n\n    if (mDrag->mIsOnDragAndDrop)\n    {\n        MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget();\n        while (focus && focus != mWidget)\n            focus = focus->getParent();\n\n        if (focus == mWidget)\n            mTransparent = true;\n    }\n    if (!mWidget->getAbsoluteCoord().inside(mousePos))\n        mTransparent = false;\n\n    if (mTransparent)\n    {\n        mWidget->setNeedMouseFocus(false); // Allow click-through\n        setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5));\n    }\n    else\n    {\n        mWidget->setNeedMouseFocus(true);\n        setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5));\n    }\n}\n\nvoid NoDrop::setAlpha(float alpha)\n{\n    if (mWidget)\n        mWidget->setAlpha(alpha);\n}\n\nBookWindowBase::BookWindowBase(const std::string& parLayout)\n  : WindowBase(parLayout)\n{\n}\n\nfloat BookWindowBase::adjustButton (char const * name)\n{\n    Gui::ImageButton* button;\n    WindowBase::getWidget (button, name);\n    MyGUI::IntSize requested = button->getRequestedSize();\n    float scale = float(requested.height) / button->getSize().height;\n    MyGUI::IntSize newSize = requested;\n    newSize.width /= scale;\n    newSize.height /= scale;\n    button->setSize(newSize);\n\n    if (button->getAlign().isRight())\n    {\n        MyGUI::IntSize diff = (button->getSize() - requested);\n        diff.width /= scale;\n        diff.height /= scale;\n        button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0));\n    }\n\n    return scale;\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/windowbase.hpp",
    "content": "#ifndef MWGUI_WINDOW_BASE_H\n#define MWGUI_WINDOW_BASE_H\n\n#include \"layout.hpp\"\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWGui\n{\n    class DragAndDrop;\n\n    class WindowBase: public Layout\n    {\n    public:\n        WindowBase(const std::string& parLayout);\n\n        virtual MyGUI::Widget* getDefaultKeyFocus() { return nullptr; }\n\n        // Events\n        typedef MyGUI::delegates::CMultiDelegate1<WindowBase*> EventHandle_WindowBase;\n\n        /// Open this object in the GUI, for windows that support it\n        virtual void setPtr(const MWWorld::Ptr& ptr) {}\n\n        /// Called every frame if the window is in an active GUI mode\n        virtual void onFrame(float duration) {}\n\n        /// Notify that window has been made visible\n        virtual void onOpen() {}\n        /// Notify that window has been hidden\n        virtual void onClose () {}\n        /// Gracefully exits the window\n        virtual bool exit() {return true;}\n        /// Sets the visibility of the window\n        void setVisible(bool visible) override;\n        /// Returns the visibility state of the window\n        bool isVisible();\n\n        void center();\n\n        /// Clear any state specific to the running game\n        virtual void clear() {}\n\n        /// Called when GUI viewport changes size\n        virtual void onResChange(int width, int height) {}\n\n    protected:\n        virtual void onTitleDoubleClicked();\n\n    private:\n        void onDoubleClick(MyGUI::Widget* _sender);\n    };\n\n    /*\n     * \"Modal\" windows cause the rest of the interface to be inaccessible while they are visible\n     */\n    class WindowModal : public WindowBase\n    {\n    public:\n        WindowModal(const std::string& parLayout);\n        void onOpen() override;\n        void onClose() override;\n        bool exit() override {return true;}\n    };\n\n    /// A window that cannot be the target of a drag&drop action.\n    /// When hovered with a drag item, the window will become transparent and allow click-through.\n    class NoDrop\n    {\n    public:\n        NoDrop(DragAndDrop* drag, MyGUI::Widget* widget);\n\n        void onFrame(float dt);\n        virtual void setAlpha(float alpha);\n        virtual ~NoDrop() = default;\n\n    private:\n        MyGUI::Widget* mWidget;\n        DragAndDrop* mDrag;\n        bool mTransparent;\n    };\n\n    class BookWindowBase : public WindowBase\n    {\n    public:\n        BookWindowBase(const std::string& parLayout);\n\n    protected:\n        float adjustButton (char const * name);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/windowmanagerimp.cpp",
    "content": "#include \"windowmanagerimp.hpp\"\n\n#include <algorithm>\n#include <cassert>\n#include <chrono>\n#include <thread>\n\n#include <osgViewer/Viewer>\n\n#include <MyGUI_UString.h>\n#include <MyGUI_IPointer.h>\n#include <MyGUI_FactoryManager.h>\n#include <MyGUI_LanguageManager.h>\n#include <MyGUI_PointerManager.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_ClipboardManager.h>\n#include <MyGUI_WidgetManager.h>\n\n// For BT_NO_PROFILE\n#include <LinearMath/btQuickprof.h>\n\n#include <SDL_keyboard.h>\n#include <SDL_clipboard.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/GUIController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/sdlutil/sdlcursormanager.hpp>\n#include <components/sdlutil/sdlvideowrapper.hpp>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n\n#include <components/fontloader/fontloader.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/imagemanager.hpp>\n\n#include <components/sceneutil/workqueue.hpp>\n\n#include <components/translation/translation.hpp>\n\n#include <components/myguiplatform/myguiplatform.hpp>\n#include <components/myguiplatform/myguirendermanager.hpp>\n#include <components/myguiplatform/additivelayer.hpp>\n#include <components/myguiplatform/scalinglayer.hpp>\n\n#include <components/vfs/manager.hpp>\n\n#include <components/widgets/tags.hpp>\n\n#include <components/misc/resourcehelpers.hpp>\n#include <components/misc/frameratelimiter.hpp>\n\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwrender/vismask.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwrender/localmap.hpp\"\n\n#include \"console.hpp\"\n#include \"journalwindow.hpp\"\n#include \"journalviewmodel.hpp\"\n#include \"charactercreation.hpp\"\n#include \"dialogue.hpp\"\n#include \"statswindow.hpp\"\n#include \"messagebox.hpp\"\n#include \"tooltips.hpp\"\n#include \"scrollwindow.hpp\"\n#include \"bookwindow.hpp\"\n#include \"hud.hpp\"\n#include \"mainmenu.hpp\"\n#include \"countdialog.hpp\"\n#include \"tradewindow.hpp\"\n#include \"spellbuyingwindow.hpp\"\n#include \"travelwindow.hpp\"\n#include \"settingswindow.hpp\"\n#include \"confirmationdialog.hpp\"\n#include \"alchemywindow.hpp\"\n#include \"spellwindow.hpp\"\n#include \"quickkeysmenu.hpp\"\n#include \"loadingscreen.hpp\"\n#include \"levelupdialog.hpp\"\n#include \"waitdialog.hpp\"\n#include \"enchantingdialog.hpp\"\n#include \"trainingwindow.hpp\"\n#include \"recharge.hpp\"\n#include \"exposedwindow.hpp\"\n#include \"cursor.hpp\"\n#include \"merchantrepair.hpp\"\n#include \"repair.hpp\"\n#include \"soulgemdialog.hpp\"\n#include \"companionwindow.hpp\"\n#include \"inventorywindow.hpp\"\n#include \"bookpage.hpp\"\n#include \"itemview.hpp\"\n#include \"videowidget.hpp\"\n#include \"backgroundimage.hpp\"\n#include \"itemwidget.hpp\"\n#include \"screenfader.hpp\"\n#include \"debugwindow.hpp\"\n#include \"spellview.hpp\"\n#include \"draganddrop.hpp\"\n#include \"container.hpp\"\n#include \"controllers.hpp\"\n#include \"jailscreen.hpp\"\n#include \"itemchargeview.hpp\"\n#include \"keyboardnavigation.hpp\"\n#include \"resourceskin.hpp\"\n\nnamespace MWGui\n{\n    WindowManager::WindowManager(\n            SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,\n            const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,\n            ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& userDataPath)\n      : mOldUpdateMask(0)\n      , mOldCullMask(0)\n      , mStore(nullptr)\n      , mResourceSystem(resourceSystem)\n      , mWorkQueue(workQueue)\n      , mViewer(viewer)\n      , mConsoleOnlyScripts(consoleOnlyScripts)\n      , mCurrentModals()\n      , mHud(nullptr)\n      , mMap(nullptr)\n      , mLocalMapRender(nullptr)\n      , mToolTips(nullptr)\n      , mStatsWindow(nullptr)\n      , mMessageBoxManager(nullptr)\n      , mConsole(nullptr)\n      , mDialogueWindow(nullptr)\n      , mDragAndDrop(nullptr)\n      , mInventoryWindow(nullptr)\n      , mScrollWindow(nullptr)\n      , mBookWindow(nullptr)\n      , mCountDialog(nullptr)\n      , mTradeWindow(nullptr)\n      , mSettingsWindow(nullptr)\n      , mConfirmationDialog(nullptr)\n      , mSpellWindow(nullptr)\n      , mQuickKeysMenu(nullptr)\n      , mLoadingScreen(nullptr)\n      , mWaitDialog(nullptr)\n      , mSoulgemDialog(nullptr)\n      , mVideoBackground(nullptr)\n      , mVideoWidget(nullptr)\n      , mWerewolfFader(nullptr)\n      , mBlindnessFader(nullptr)\n      , mHitFader(nullptr)\n      , mScreenFader(nullptr)\n      , mDebugWindow(nullptr)\n      , mJailScreen(nullptr)\n      , mTranslationDataStorage (translationDataStorage)\n      , mCharGen(nullptr)\n      , mInputBlocker(nullptr)\n      , mCrosshairEnabled(Settings::Manager::getBool (\"crosshair\", \"HUD\"))\n      , mSubtitlesEnabled(Settings::Manager::getBool (\"subtitles\", \"GUI\"))\n      , mHitFaderEnabled(Settings::Manager::getBool (\"hit fader\", \"GUI\"))\n      , mWerewolfOverlayEnabled(Settings::Manager::getBool (\"werewolf overlay\", \"GUI\"))\n      , mHudEnabled(true)\n      , mCursorVisible(true)\n      , mCursorActive(true)\n      , mPlayerBounty(-1)\n      , mGui(nullptr)\n      , mGuiModes()\n      , mCursorManager(nullptr)\n      , mGarbageDialogs()\n      , mShown(GW_ALL)\n      , mForceHidden(GW_None)\n      , mAllowed(GW_ALL)\n      , mRestAllowed(true)\n      , mShowOwned(0)\n      , mEncoding(encoding)\n      , mVersionDescription(versionDescription)\n      , mWindowVisible(true)\n    {\n        mScalingFactor = std::clamp(Settings::Manager::getFloat(\"scaling factor\", \"GUI\"), 0.5f, 8.f);\n        mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), mScalingFactor);\n        mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / \"MyGUI.log\").generic_string());\n\n        mGui = new MyGUI::Gui;\n        mGui->initialise(\"\");\n\n        createTextures();\n\n        MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);\n\n        // Load fonts\n        mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath, mScalingFactor));\n        mFontLoader->loadBitmapFonts(exportFonts);\n\n        //Register own widgets with MyGUI\n        MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWAttribute>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSpell>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWEffectList>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSpellEffect>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWDynamicStat>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Window>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<VideoWidget>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<BackgroundImage>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::AdditiveLayer>(\"Layer\");\n        MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::ScalingLayer>(\"Layer\");\n        BookPage::registerMyGUIComponents ();\n        ItemView::registerComponents();\n        ItemChargeView::registerComponents();\n        ItemWidget::registerComponents();\n        SpellView::registerComponents();\n        Gui::registerAllWidgets();\n\n        MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>(\"Controller\");\n\n        MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>(\"Resource\", \"ResourceImageSetPointer\");\n        MyGUI::FactoryManager::getInstance().registerFactory<AutoSizedResourceSkin>(\"Resource\", \"AutoSizedResourceSkin\");\n        MyGUI::ResourceManager::getInstance().load(\"core.xml\");\n        WindowManager::loadUserFonts();\n\n        bool keyboardNav = Settings::Manager::getBool(\"keyboard navigation\", \"GUI\");\n        mKeyboardNavigation.reset(new KeyboardNavigation());\n        mKeyboardNavigation->setEnabled(keyboardNav);\n        Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav);\n\n        mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer);\n        mWindows.push_back(mLoadingScreen);\n\n        //set up the hardware cursor manager\n        mCursorManager = new SDLUtil::SDLCursorManager();\n\n        MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &WindowManager::onCursorChange);\n\n        MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);\n\n        // Create all cursors in advance\n        createCursors();\n        onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());\n        mCursorManager->setEnabled(true);\n\n        // hide mygui's pointer\n        MyGUI::PointerManager::getInstance().setVisible(false);\n\n        mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal<MyGUI::ImageBox>(\"ImageBox\", 0,0,1,1,\n            MyGUI::Align::Default, \"InputBlocker\");\n        mVideoBackground->setImageTexture(\"black\");\n        mVideoBackground->setVisible(false);\n        mVideoBackground->setNeedMouseFocus(true);\n        mVideoBackground->setNeedKeyFocus(true);\n\n        mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>(\"ImageBox\", 0,0,1,1, MyGUI::Align::Default);\n        mVideoWidget->setNeedMouseFocus(true);\n        mVideoWidget->setNeedKeyFocus(true);\n        mVideoWidget->setVFS(resourceSystem->getVFS());\n\n        // Removes default MyGUI system clipboard implementation, which supports windows only\n        MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear();\n        MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear();\n\n        MyGUI::ClipboardManager::getInstance().eventClipboardChanged += MyGUI::newDelegate(this, &WindowManager::onClipboardChanged);\n        MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested);\n\n        mShowOwned = Settings::Manager::getInt(\"show owned\", \"Game\");\n\n        mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer);\n        mVideoWrapper->setGammaContrast(Settings::Manager::getFloat(\"gamma\", \"Video\"),\n                                        Settings::Manager::getFloat(\"contrast\", \"Video\"));\n\n        mStatsWatcher.reset(new StatsWatcher());\n    }\n\n    void WindowManager::loadUserFonts()\n    {\n        mFontLoader->loadTrueTypeFonts();\n    }\n\n    void WindowManager::initUI()\n    {\n        // Get size info from the Gui object\n        int w = MyGUI::RenderManager::getInstance().getViewSize().width;\n        int h = MyGUI::RenderManager::getInstance().getViewSize().height;\n\n        mTextColours.loadColours();\n\n        mDragAndDrop = new DragAndDrop();\n\n        Recharge* recharge = new Recharge();\n        mGuiModeStates[GM_Recharge] = GuiModeState(recharge);\n        mWindows.push_back(recharge);\n\n        MainMenu* menu = new MainMenu(w, h, mResourceSystem->getVFS(), mVersionDescription);\n        mGuiModeStates[GM_MainMenu] = GuiModeState(menu);\n        mWindows.push_back(menu);\n\n        mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup());\n        mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue);\n        mWindows.push_back(mMap);\n        mMap->renderGlobalMap();\n        trackWindow(mMap, \"map\");\n\n        mStatsWindow = new StatsWindow(mDragAndDrop);\n        mWindows.push_back(mStatsWindow);\n        trackWindow(mStatsWindow, \"stats\");\n\n        mInventoryWindow = new InventoryWindow(mDragAndDrop, mViewer->getSceneData()->asGroup(), mResourceSystem);\n        mWindows.push_back(mInventoryWindow);\n\n        mSpellWindow = new SpellWindow(mDragAndDrop);\n        mWindows.push_back(mSpellWindow);\n        trackWindow(mSpellWindow, \"spells\");\n\n        mGuiModeStates[GM_Inventory] = GuiModeState({mMap, mInventoryWindow, mSpellWindow, mStatsWindow});\n        mGuiModeStates[GM_None] = GuiModeState({mMap, mInventoryWindow, mSpellWindow, mStatsWindow});\n\n        mTradeWindow = new TradeWindow();\n        mWindows.push_back(mTradeWindow);\n        trackWindow(mTradeWindow, \"barter\");\n        mGuiModeStates[GM_Barter] = GuiModeState({mInventoryWindow, mTradeWindow});\n\n        mConsole = new Console(w,h, mConsoleOnlyScripts);\n        mWindows.push_back(mConsole);\n        trackWindow(mConsole, \"console\");\n\n        bool questList = mResourceSystem->getVFS()->exists(\"textures/tx_menubook_options_over.dds\");\n        JournalWindow* journal = JournalWindow::create(JournalViewModel::create (), questList, mEncoding);\n        mWindows.push_back(journal);\n        mGuiModeStates[GM_Journal] = GuiModeState(journal);\n        mGuiModeStates[GM_Journal].mCloseSound = \"book close\";\n        mGuiModeStates[GM_Journal].mOpenSound = \"book open\";\n\n        mMessageBoxManager = new MessageBoxManager(mStore->get<ESM::GameSetting>().find(\"fMessageTimePerChar\")->mValue.getFloat());\n\n        SpellBuyingWindow* spellBuyingWindow = new SpellBuyingWindow();\n        mWindows.push_back(spellBuyingWindow);\n        mGuiModeStates[GM_SpellBuying] = GuiModeState(spellBuyingWindow);\n\n        TravelWindow* travelWindow = new TravelWindow();\n        mWindows.push_back(travelWindow);\n        mGuiModeStates[GM_Travel] = GuiModeState(travelWindow);\n\n        mDialogueWindow = new DialogueWindow();\n        mWindows.push_back(mDialogueWindow);\n        trackWindow(mDialogueWindow, \"dialogue\");\n        mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow);\n        mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete);\n\n        /*\n            Start of tes3mp change (major)\n\n            Use a member variable (mContainerWIndow) instead of a local one so\n            we can access it from elsewhere\n        */\n        mContainerWindow = new ContainerWindow(mDragAndDrop);\n        mWindows.push_back(mContainerWindow);\n        trackWindow(mContainerWindow, \"container\");\n        mGuiModeStates[GM_Container] = GuiModeState({mContainerWindow, mInventoryWindow});\n        /*\n            End of tes3mp change (major)\n        */\n\n        mHud = new HUD(mCustomMarkers, mDragAndDrop, mLocalMapRender);\n        mWindows.push_back(mHud);\n\n        mToolTips = new ToolTips();\n\n        mScrollWindow = new ScrollWindow();\n        mWindows.push_back(mScrollWindow);\n        mGuiModeStates[GM_Scroll] = GuiModeState(mScrollWindow);\n        mGuiModeStates[GM_Scroll].mOpenSound = \"scroll\";\n        mGuiModeStates[GM_Scroll].mCloseSound = \"scroll\";\n\n        mBookWindow = new BookWindow();\n        mWindows.push_back(mBookWindow);\n        mGuiModeStates[GM_Book] = GuiModeState(mBookWindow);\n        mGuiModeStates[GM_Book].mOpenSound = \"book open\";\n        mGuiModeStates[GM_Book].mCloseSound = \"book close\";\n\n        mCountDialog = new CountDialog();\n        mWindows.push_back(mCountDialog);\n\n        mSettingsWindow = new SettingsWindow();\n        mWindows.push_back(mSettingsWindow);\n        mGuiModeStates[GM_Settings] = GuiModeState(mSettingsWindow);\n\n        mConfirmationDialog = new ConfirmationDialog();\n        mWindows.push_back(mConfirmationDialog);\n\n        AlchemyWindow* alchemyWindow = new AlchemyWindow();\n        mWindows.push_back(alchemyWindow);\n        trackWindow(alchemyWindow, \"alchemy\");\n        mGuiModeStates[GM_Alchemy] = GuiModeState(alchemyWindow);\n\n        mQuickKeysMenu = new QuickKeysMenu();\n        mWindows.push_back(mQuickKeysMenu);\n        mGuiModeStates[GM_QuickKeysMenu] = GuiModeState(mQuickKeysMenu);\n\n        LevelupDialog* levelupDialog = new LevelupDialog();\n        mWindows.push_back(levelupDialog);\n        mGuiModeStates[GM_Levelup] = GuiModeState(levelupDialog);\n\n        mWaitDialog = new WaitDialog();\n        mWindows.push_back(mWaitDialog);\n        mGuiModeStates[GM_Rest] = GuiModeState({mWaitDialog->getProgressBar(), mWaitDialog});\n\n        SpellCreationDialog* spellCreationDialog = new SpellCreationDialog();\n        mWindows.push_back(spellCreationDialog);\n        mGuiModeStates[GM_SpellCreation] = GuiModeState(spellCreationDialog);\n\n        EnchantingDialog* enchantingDialog = new EnchantingDialog();\n        mWindows.push_back(enchantingDialog);\n        mGuiModeStates[GM_Enchanting] = GuiModeState(enchantingDialog);\n\n        TrainingWindow* trainingWindow = new TrainingWindow();\n        mWindows.push_back(trainingWindow);\n        mGuiModeStates[GM_Training] = GuiModeState({trainingWindow->getProgressBar(), trainingWindow});\n\n        MerchantRepair* merchantRepair = new MerchantRepair();\n        mWindows.push_back(merchantRepair);\n        mGuiModeStates[GM_MerchantRepair] = GuiModeState(merchantRepair);\n\n        Repair* repair = new Repair();\n        mWindows.push_back(repair);\n        mGuiModeStates[GM_Repair] = GuiModeState(repair);\n\n        mSoulgemDialog = new SoulgemDialog(mMessageBoxManager);\n\n        CompanionWindow* companionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager);\n        mWindows.push_back(companionWindow);\n        trackWindow(companionWindow, \"companion\");\n        mGuiModeStates[GM_Companion] = GuiModeState({mInventoryWindow, companionWindow});\n\n        mJailScreen = new JailScreen();\n        mWindows.push_back(mJailScreen);\n        mGuiModeStates[GM_Jail] = GuiModeState(mJailScreen);\n\n        std::string werewolfFaderTex = \"textures\\\\werewolfoverlay.dds\";\n        if (mResourceSystem->getVFS()->exists(werewolfFaderTex))\n        {\n            mWerewolfFader = new ScreenFader(werewolfFaderTex);\n            mWindows.push_back(mWerewolfFader);\n        }\n        mBlindnessFader = new ScreenFader(\"black\");\n        mWindows.push_back(mBlindnessFader);\n\n        // fall back to player_hit_01.dds if bm_player_hit_01.dds is not available\n        std::string hitFaderTexture = \"textures\\\\bm_player_hit_01.dds\";\n        const std::string hitFaderLayout = \"openmw_screen_fader_hit.layout\";\n        MyGUI::FloatCoord hitFaderCoord (0,0,1,1);\n        if(!mResourceSystem->getVFS()->exists(hitFaderTexture))\n        {\n            hitFaderTexture = \"textures\\\\player_hit_01.dds\";\n            hitFaderCoord = MyGUI::FloatCoord(0.2, 0.25, 0.6, 0.5);\n        }\n        mHitFader = new ScreenFader(hitFaderTexture, hitFaderLayout, hitFaderCoord);\n        mWindows.push_back(mHitFader);\n\n        mScreenFader = new ScreenFader(\"black\");\n        mWindows.push_back(mScreenFader);\n\n        mDebugWindow = new DebugWindow();\n        mWindows.push_back(mDebugWindow);\n\n        mInputBlocker = MyGUI::Gui::getInstance().createWidget<MyGUI::Widget>(\"\",0,0,w,h,MyGUI::Align::Stretch,\"InputBlocker\");\n\n        mHud->setVisible(true);\n\n        mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem);\n\n        updatePinnedWindows();\n\n        // Set up visibility\n        updateVisible();\n\n        mStatsWatcher->addListener(mHud);\n        mStatsWatcher->addListener(mStatsWindow);\n        mStatsWatcher->addListener(mCharGen);\n    }\n\n    int WindowManager::getFontHeight() const\n    {\n        return mFontLoader->getFontHeight();\n    }\n\n    void WindowManager::setNewGame(bool newgame)\n    {\n        if (newgame)\n        {\n            disallowAll();\n\n            mStatsWatcher->removeListener(mCharGen);\n            delete mCharGen;\n            mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem);\n            mStatsWatcher->addListener(mCharGen);\n        }\n        else\n            allow(GW_ALL);\n\n        mStatsWatcher->forceUpdate();\n    }\n\n    WindowManager::~WindowManager()\n    {\n        try\n        {\n            mStatsWatcher.reset();\n\n            MyGUI::LanguageManager::getInstance().eventRequestTag.clear();\n            MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear();\n            MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear();\n            MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear();\n            MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear();\n\n            for (WindowBase* window : mWindows)\n                delete window;\n            mWindows.clear();\n\n            delete mMessageBoxManager;\n            delete mLocalMapRender;\n            delete mCharGen;\n            delete mDragAndDrop;\n            delete mSoulgemDialog;\n            delete mCursorManager;\n            delete mToolTips;\n\n            mKeyboardNavigation.reset();\n\n            cleanupGarbage();\n\n            mFontLoader.reset();\n\n            mGui->shutdown();\n            delete mGui;\n\n            mGuiPlatform->shutdown();\n            delete mGuiPlatform;\n            delete mVideoWrapper;\n        }\n        catch(const MyGUI::Exception& e)\n        {\n            Log(Debug::Error) << \"Error in the destructor: \" << e.what();\n        }\n    }\n\n    void WindowManager::setStore(const MWWorld::ESMStore &store)\n    {\n        mStore = &store;\n    }\n\n    void WindowManager::cleanupGarbage()\n    {\n        // Delete any dialogs which are no longer in use\n        if (!mGarbageDialogs.empty())\n        {\n            for (Layout* widget : mGarbageDialogs)\n            {\n                delete widget;\n            }\n            mGarbageDialogs.clear();\n        }\n    }\n\n    void WindowManager::enableScene(bool enable)\n    {\n        unsigned int disablemask = MWRender::Mask_GUI|MWRender::Mask_PreCompile;\n        if (!enable && mViewer->getCamera()->getCullMask() != disablemask)\n        {\n            mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();\n            mOldCullMask = mViewer->getCamera()->getCullMask();\n            mViewer->getUpdateVisitor()->setTraversalMask(disablemask);\n            mViewer->getCamera()->setCullMask(disablemask);\n        }\n        else if (enable && mViewer->getCamera()->getCullMask() == disablemask)\n        {\n            mViewer->getUpdateVisitor()->setTraversalMask(mOldUpdateMask);\n            mViewer->getCamera()->setCullMask(mOldCullMask);\n        }\n    }\n\n    void WindowManager::updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)\n    {\n        mConsole->updateSelectedObjectPtr(currentPtr, newPtr);\n    }\n\n    void WindowManager::updateVisible()\n    {\n        bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper);\n\n        bool mainmenucover = containsMode(GM_MainMenu) && MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame;\n\n        enableScene(!loading && !mainmenucover);\n\n        if (!mMap)\n            return; // UI not created yet\n\n        mHud->setVisible(mHudEnabled && !loading);\n        mToolTips->setVisible(mHudEnabled && !loading);\n\n        bool gameMode = !isGuiMode();\n\n        MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode);\n\n        mInputBlocker->setVisible (gameMode);\n\n        if (loading)\n            setCursorVisible(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox());\n        else\n            setCursorVisible(!gameMode);\n\n        if (gameMode)\n            setKeyFocusWidget (nullptr);\n\n        // Icons of forced hidden windows are displayed\n        setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map)));\n        setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory)));\n        setSpellVisibility((mAllowed & GW_Magic) && (!mSpellWindow->pinned() || (mForceHidden & GW_Magic)));\n        setHMSVisibility((mAllowed & GW_Stats) && (!mStatsWindow->pinned() || (mForceHidden & GW_Stats)));\n\n        mInventoryWindow->setGuiMode(getMode());\n\n        // If in game mode (or interactive messagebox), show the pinned windows\n        if (mGuiModes.empty())\n        {\n            mMap->setVisible(mMap->pinned() && !isConsoleMode() && !(mForceHidden & GW_Map) && (mAllowed & GW_Map));\n            mStatsWindow->setVisible(mStatsWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Stats) && (mAllowed & GW_Stats));\n            mInventoryWindow->setVisible(mInventoryWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Inventory) && (mAllowed & GW_Inventory));\n            mSpellWindow->setVisible(mSpellWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Magic) && (mAllowed & GW_Magic));\n            return;\n        }\n        else if (getMode() != GM_Inventory)\n        {\n            mMap->setVisible(false);\n            mStatsWindow->setVisible(false);\n            mSpellWindow->setVisible(false);\n            mInventoryWindow->setVisible(getMode() == GM_Container || getMode() == GM_Barter || getMode() == GM_Companion);\n        }\n\n        GuiMode mode = mGuiModes.back();\n\n        mInventoryWindow->setTrading(mode == GM_Barter);\n\n        if (getMode() == GM_Inventory)\n        {\n            // For the inventory mode, compute the effective set of windows to show.\n            // This is controlled both by what windows the\n            // user has opened/closed (the 'shown' variable) and by what\n            // windows we are allowed to show (the 'allowed' var.)\n            int eff = mShown & mAllowed & ~mForceHidden;\n            mMap->setVisible(eff & GW_Map);\n            mInventoryWindow->setVisible(eff & GW_Inventory);\n            mSpellWindow->setVisible(eff & GW_Magic);\n            mStatsWindow->setVisible(eff & GW_Stats);\n        }\n\n        switch (mode)\n        {\n        // FIXME: refactor chargen windows to use modes properly (or not use them at all)\n        case GM_Name:\n        case GM_Race:\n        case GM_Class:\n        case GM_ClassPick:\n        case GM_ClassCreate:\n        case GM_Birth:\n        case GM_ClassGenerate:\n        case GM_Review:\n            mCharGen->spawnDialog(mode);\n            break;\n        default:\n            /*\n                Start of tes3mp addition\n\n                Pass the GuiMode further on to the multiplayer-specific GUI controller\n            */\n            mwmp::Main::get().getGUIController()->WM_UpdateVisible(mode);\n            /*\n                End of tes3mp addition\n            */\n            break;\n        }\n    }\n\n    void WindowManager::setDrowningTimeLeft (float time, float maxTime)\n    {\n        mHud->setDrowningTimeLeft(time, maxTime);\n    }\n\n    void WindowManager::removeDialog(Layout*dialog)\n    {\n        if (!dialog)\n            return;\n        dialog->setVisible(false);\n        mGarbageDialogs.push_back(dialog);\n    }\n\n    void WindowManager::exitCurrentGuiMode()\n    {\n        if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop)\n        {\n            mDragAndDrop->finish();\n            return;\n        }\n\n        GuiModeState& state = mGuiModeStates[mGuiModes.back()];\n        for (WindowBase* window : state.mWindows)\n        {\n            if (!window->exit())\n            {\n                // unable to exit window, but give access to main menu\n                if (!MyGUI::InputManager::getInstance().isModalAny() && getMode() != GM_MainMenu)\n                    pushGuiMode (GM_MainMenu);\n                return;\n            }\n        }\n\n        popGuiMode();\n    }\n\n    /*\n        Start of tes3mp change (major)\n\n        Add a hasServerOrigin boolean to the list of arguments so those messageboxes\n        can be differentiated from client-only ones\n\n        Use the hasServerOrigin argument when creating an interactive message box\n    */\n    void WindowManager::interactiveMessageBox(const std::string &message, const std::vector<std::string> &buttons, bool block, bool hasServerOrigin)\n    {\n        mMessageBoxManager->createInteractiveMessageBox(message, buttons, hasServerOrigin);\n    /*\n        End of tes3mp change (major)\n    */\n        updateVisible();\n\n        if (block)\n        {\n            Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());\n            while (mMessageBoxManager->readPressedButton(false) == -1\n                   && !MWBase::Environment::get().getStateManager()->hasQuitRequest())\n            {\n                const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(frameRateLimiter.getLastFrameDuration()).count();\n\n                mKeyboardNavigation->onFrame();\n                mMessageBoxManager->onFrame(dt);\n                MWBase::Environment::get().getInputManager()->update(dt, true, false);\n\n                if (!mWindowVisible)\n                    std::this_thread::sleep_for(std::chrono::milliseconds(5));\n                else\n                {\n                    mViewer->eventTraversal();\n                    mViewer->updateTraversal();\n                    mViewer->renderingTraversals();\n                }\n                // at the time this function is called we are in the middle of a frame,\n                // so out of order calls are necessary to get a correct frameNumber for the next frame.\n                // refer to the advance() and frame() order in Engine::go()\n                mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());\n\n                frameRateLimiter.limit();\n            }\n        }\n    }\n\n    void WindowManager::messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode)\n    {\n        if (getMode() == GM_Dialogue && showInDialogueMode != MWGui::ShowInDialogueMode_Never) {\n            mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message));\n        } else if (showInDialogueMode != MWGui::ShowInDialogueMode_Only) {\n            mMessageBoxManager->createMessageBox(message);\n        }\n    }\n\n    void WindowManager::staticMessageBox(const std::string& message)\n    {\n        mMessageBoxManager->createMessageBox(message, true);\n    }\n\n    void WindowManager::removeStaticMessageBox()\n    {\n        mMessageBoxManager->removeStaticMessageBox();\n    }\n\n    int WindowManager::readPressedButton ()\n    {\n        return mMessageBoxManager->readPressedButton();\n    }\n\n    std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_)\n    {\n        const ESM::GameSetting *setting = mStore->get<ESM::GameSetting>().search(id);\n\n        if (setting && setting->mValue.getType()==ESM::VT_String)\n            return setting->mValue.getString();\n\n        return default_;\n    }\n\n    void WindowManager::updateMap()\n    {\n        if (!mLocalMapRender)\n            return;\n\n        MWWorld::ConstPtr player = MWMechanics::getPlayer();\n\n        osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3();\n        osg::Quat playerOrientation (-player.getRefData().getPosition().rot[2], osg::Vec3(0,0,1));\n\n        osg::Vec3f playerdirection;\n        int x,y;\n        float u,v;\n        mLocalMapRender->updatePlayer(playerPosition, playerOrientation, u, v, x, y, playerdirection);\n\n        if (!player.getCell()->isExterior())\n        {\n            setActiveMap(x, y, true);\n        }\n        // else: need to know the current grid center, call setActiveMap from changeCell\n\n        mMap->setPlayerDir(playerdirection.x(), playerdirection.y());\n        mMap->setPlayerPos(x, y, u, v);\n        mHud->setPlayerDir(playerdirection.x(), playerdirection.y());\n        mHud->setPlayerPos(x, y, u, v);\n    }\n\n    void WindowManager::update (float frameDuration)\n    {\n        bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!=\n            MWBase::StateManager::State_NoGame;\n\n        if (gameRunning)\n            updateMap();\n\n        if (!mGuiModes.empty())\n        {\n            GuiModeState& state = mGuiModeStates[mGuiModes.back()];\n            for (WindowBase* window : state.mWindows)\n                window->onFrame(frameDuration);\n        }\n        else\n        {\n            // update pinned windows if visible\n            for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows)\n                if (window->isVisible())\n                    window->onFrame(frameDuration);\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Fix crashes caused by messageboxes that never have their modals erased elsewhere, working around\n            one of the main GUI-related problems that arise in an unpaused environment\n        */\n        for (auto modalIterator = mCurrentModals.begin(); modalIterator != mCurrentModals.end();) {\n            if ((*modalIterator)->mMainWidget == 0)\n            {\n                mCurrentModals.erase(modalIterator);\n            }\n            else\n            {\n                ++modalIterator;\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        // Make sure message boxes are always in front\n        // This is an awful workaround for a series of awfully interwoven issues that couldn't be worked around\n        // in a better way because of an impressive number of even more awfully interwoven issues.\n        if (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox() && mCurrentModals.back() != mMessageBoxManager->getInteractiveMessageBox())\n        {\n            std::vector<WindowModal*>::iterator found = std::find(mCurrentModals.begin(), mCurrentModals.end(), mMessageBoxManager->getInteractiveMessageBox());\n            if (found != mCurrentModals.end())\n            {\n                WindowModal* msgbox = *found;\n                std::swap(*found, mCurrentModals.back());\n                MyGUI::InputManager::getInstance().addWidgetModal(msgbox->mMainWidget);\n                mKeyboardNavigation->setModalWindow(msgbox->mMainWidget);\n                mKeyboardNavigation->setDefaultFocus(msgbox->mMainWidget, msgbox->getDefaultKeyFocus());\n            }\n        }\n\n        if (!mCurrentModals.empty())\n            mCurrentModals.back()->onFrame(frameDuration);\n\n        mKeyboardNavigation->onFrame();\n\n        if (mMessageBoxManager)\n            mMessageBoxManager->onFrame(frameDuration);\n\n        mToolTips->onFrame(frameDuration);\n\n        if (mLocalMapRender)\n            mLocalMapRender->cleanupCameras();\n\n        if (!gameRunning)\n            return;\n\n        // We should display message about crime only once per frame, even if there are several crimes.\n        // Otherwise we will get message spam when stealing several items via Take All button.\n        const MWWorld::Ptr player = MWMechanics::getPlayer();\n        int currentBounty = player.getClass().getNpcStats(player).getBounty();\n        if (currentBounty != mPlayerBounty)\n        {\n            if (mPlayerBounty >= 0 && currentBounty > mPlayerBounty)\n                messageBox(\"#{sCrimeMessage}\");\n\n            mPlayerBounty = currentBounty;\n        }\n\n        mDragAndDrop->onFrame();\n\n        mHud->onFrame(frameDuration);\n\n        mDebugWindow->onFrame(frameDuration);\n\n        if (mCharGen)\n            mCharGen->onFrame(frameDuration);\n\n        updateActivatedQuickKey();\n\n        mStatsWatcher->update();\n\n        cleanupGarbage();\n    }\n\n    void WindowManager::changeCell(const MWWorld::CellStore* cell)\n    {\n        mMap->requestMapRender(cell);\n\n        std::string name = MWBase::Environment::get().getWorld()->getCellName (cell);\n\n        mMap->setCellName( name );\n        mHud->setCellName( name );\n\n        if (cell->getCell()->isExterior())\n        {\n            if (!cell->getCell()->mName.empty())\n                mMap->addVisitedLocation (name, cell->getCell()->getGridX (), cell->getCell()->getGridY ());\n\n            mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY());\n\n            setActiveMap(cell->getCell()->getGridX(), cell->getCell()->getGridY(), false);\n        }\n        else\n        {\n            mMap->setCellPrefix (cell->getCell()->mName );\n            mHud->setCellPrefix (cell->getCell()->mName );\n\n            osg::Vec3f worldPos;\n            if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos))\n                worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition();\n            else\n                MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos);\n            mMap->setGlobalMapPlayerPosition(worldPos.x(), worldPos.y());\n\n            setActiveMap(0, 0, true);\n        }\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the setting of the image data for a global map tile from elsewhere\n        in the code\n    */\n    void WindowManager::setGlobalMapImage(int cellX, int cellY, const std::vector<char>& imageData)\n    {\n        mMap->setGlobalMapImage(cellX, cellY, imageData);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void WindowManager::setActiveMap(int x, int y, bool interior)\n    {\n        mMap->setActiveCell(x,y, interior);\n        mHud->setActiveCell(x,y, interior);\n    }\n\n    void WindowManager::setDrowningBarVisibility(bool visible)\n    {\n        mHud->setDrowningBarVisible(visible);\n    }\n\n    void WindowManager::setHMSVisibility(bool visible)\n    {\n        mHud->setHmsVisible (visible);\n    }\n\n    void WindowManager::setMinimapVisibility(bool visible)\n    {\n        mHud->setMinimapVisible (visible);\n    }\n\n    bool WindowManager::toggleFogOfWar()\n    {\n        mMap->toggleFogOfWar();\n        return mHud->toggleFogOfWar();\n    }\n\n    void WindowManager::setFocusObject(const MWWorld::Ptr& focus)\n    {\n        mToolTips->setFocusObject(focus);\n\n        if(mHud && (mShowOwned == 2 || mShowOwned == 3))\n        {\n            bool owned = mToolTips->checkOwned();\n            mHud->setCrosshairOwned(owned);\n        }\n    }\n\n    void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y)\n    {\n        mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y);\n    }\n\n    bool WindowManager::toggleFullHelp()\n    {\n        return mToolTips->toggleFullHelp();\n    }\n\n    bool WindowManager::getFullHelp() const\n    {\n        return mToolTips->getFullHelp();\n    }\n\n    void WindowManager::setWeaponVisibility(bool visible)\n    {\n        mHud->setWeapVisible (visible);\n    }\n\n    void WindowManager::setSpellVisibility(bool visible)\n    {\n        mHud->setSpellVisible (visible);\n        mHud->setEffectVisible (visible);\n    }\n\n    void WindowManager::setSneakVisibility(bool visible)\n    {\n        mHud->setSneakVisible(visible);\n    }\n\n    void WindowManager::setDragDrop(bool dragDrop)\n    {\n        mToolTips->setEnabled(!dragDrop);\n        MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the completion of a drag and drop from elsewhere in the code\n    */\n    void WindowManager::finishDragDrop()\n    {\n        if (mDragAndDrop->mIsOnDragAndDrop)\n            mDragAndDrop->finish();\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void WindowManager::setCursorVisible(bool visible)\n    {\n        mCursorVisible = visible;\n    }\n\n    void WindowManager::setCursorActive(bool active)\n    {\n        mCursorActive = active;\n    }\n\n    void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result)\n    {\n        std::string tag(_tag);\n        \n        std::string MyGuiPrefix = \"setting=\";\n        size_t MyGuiPrefixLength = MyGuiPrefix.length();\n\n        std::string tokenToFind = \"sCell=\";\n        size_t tokenLength = tokenToFind.length();\n        \n        if(tag.compare(0, MyGuiPrefixLength, MyGuiPrefix) == 0)\n        {\n            tag = tag.substr(MyGuiPrefixLength, tag.length());\n            size_t comma_pos = tag.find(',');\n            std::string settingSection = tag.substr(0, comma_pos);\n            std::string settingTag = tag.substr(comma_pos+1, tag.length());\n            \n            _result = Settings::Manager::getString(settingTag, settingSection);            \n        }\n        else if (tag.compare(0, tokenLength, tokenToFind) == 0)\n        {\n            _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength));\n            _result = MyGUI::TextIterator::toTagsString(_result);\n        }\n        else if (Gui::replaceTag(tag, _result))\n        {\n            return;\n        }\n        else\n        {\n            if (!mStore)\n            {\n                Log(Debug::Error) << \"Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '\" << tag << \"'\";\n                return;\n            }\n            const ESM::GameSetting *setting = mStore->get<ESM::GameSetting>().find(tag);\n\n            if (setting && setting->mValue.getType()==ESM::VT_String)\n                _result = setting->mValue.getString();\n            else\n                _result = tag;\n        }\n    }\n\n    void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed)\n    {\n        mToolTips->setDelay(Settings::Manager::getFloat(\"tooltip delay\", \"GUI\"));\n\n        bool changeRes = false;\n        for (const auto& setting : changed)\n        {\n            if (setting.first == \"HUD\" && setting.second == \"crosshair\")\n                mCrosshairEnabled = Settings::Manager::getBool (\"crosshair\", \"HUD\");\n            else if (setting.first == \"GUI\" && setting.second == \"subtitles\")\n                mSubtitlesEnabled = Settings::Manager::getBool (\"subtitles\", \"GUI\");\n            else if (setting.first == \"GUI\" && setting.second == \"menu transparency\")\n                setMenuTransparency(Settings::Manager::getFloat(\"menu transparency\", \"GUI\"));\n            else if (setting.first == \"Video\" && (\n                    setting.second == \"resolution x\"\n                    || setting.second == \"resolution y\"\n                    || setting.second == \"fullscreen\"\n                    || setting.second == \"window border\"))\n                changeRes = true;\n\n            else if (setting.first == \"Video\" && setting.second == \"vsync\")\n                mVideoWrapper->setSyncToVBlank(Settings::Manager::getBool(\"vsync\", \"Video\"));\n            else if (setting.first == \"Video\" && (setting.second == \"gamma\" || setting.second == \"contrast\"))\n                mVideoWrapper->setGammaContrast(Settings::Manager::getFloat(\"gamma\", \"Video\"),\n                                                Settings::Manager::getFloat(\"contrast\", \"Video\"));\n        }\n\n        if (changeRes)\n        {\n            mVideoWrapper->setVideoMode(Settings::Manager::getInt(\"resolution x\", \"Video\"),\n                                        Settings::Manager::getInt(\"resolution y\", \"Video\"),\n                                        Settings::Manager::getBool(\"fullscreen\", \"Video\"),\n                                        Settings::Manager::getBool(\"window border\", \"Video\"));\n        }\n    }\n\n    void WindowManager::windowResized(int x, int y)\n    {\n        // Note: this is a side effect of resolution change or window resize.\n        // There is no need to track these changes.\n        Settings::Manager::setInt(\"resolution x\", \"Video\", x);\n        Settings::Manager::setInt(\"resolution y\", \"Video\", y);\n        Settings::Manager::resetPendingChange(\"resolution x\", \"Video\");\n        Settings::Manager::resetPendingChange(\"resolution y\", \"Video\");\n\n        mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y);\n\n        // scaled size\n        const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        x = viewSize.width;\n        y = viewSize.height;\n\n        sizeVideo(x, y);\n\n        if (!mHud)\n            return; // UI not initialized yet\n\n        for (std::map<MyGUI::Window*, std::string>::iterator it = mTrackedWindows.begin(); it != mTrackedWindows.end(); ++it)\n        {\n            std::string settingName = it->second;\n            if (Settings::Manager::getBool(settingName + \" maximized\", \"Windows\"))\n                settingName += \" maximized\";\n\n            MyGUI::IntPoint pos(static_cast<int>(Settings::Manager::getFloat(settingName + \" x\", \"Windows\") * x),\n                                static_cast<int>(Settings::Manager::getFloat(settingName + \" y\", \"Windows\") * y));\n            MyGUI::IntSize size(static_cast<int>(Settings::Manager::getFloat(settingName + \" w\", \"Windows\") * x),\n                                 static_cast<int>(Settings::Manager::getFloat(settingName + \" h\", \"Windows\") * y));\n            it->first->setPosition(pos);\n            it->first->setSize(size);\n        }\n\n        for (WindowBase* window : mWindows)\n            window->onResChange(x, y);\n\n        // We should reload TrueType fonts to fit new resolution\n        loadUserFonts();\n\n        // TODO: check if any windows are now off-screen and move them back if so\n    }\n\n    bool WindowManager::isWindowVisible()\n    {\n        return mWindowVisible;\n    }\n\n    void WindowManager::windowVisibilityChange(bool visible)\n    {\n        mWindowVisible = visible;\n    }\n\n    void WindowManager::windowClosed()\n    {\n        MWBase::Environment::get().getStateManager()->requestQuit();\n    }\n\n    void WindowManager::onCursorChange(const std::string &name)\n    {\n        mCursorManager->cursorChanged(name);\n    }\n\n    void WindowManager::pushGuiMode(GuiMode mode)\n    {\n        pushGuiMode(mode, MWWorld::Ptr());\n    }\n\n    void WindowManager::pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg)\n    {\n        if (mode==GM_Inventory && mAllowed==GW_None)\n            return;\n\n        if (mGuiModes.empty() || mGuiModes.back() != mode)\n        {\n            // If this mode already exists somewhere in the stack, just bring it to the front.\n            if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end())\n            {\n                mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode));\n            }\n\n            if (!mGuiModes.empty())\n            {\n                mKeyboardNavigation->saveFocus(mGuiModes.back());\n                mGuiModeStates[mGuiModes.back()].update(false);\n            }\n            mGuiModes.push_back(mode);\n\n            mGuiModeStates[mode].update(true);\n            playSound(mGuiModeStates[mode].mOpenSound);\n        }\n        for (WindowBase* window : mGuiModeStates[mode].mWindows)\n            window->setPtr(arg);\n\n        mKeyboardNavigation->restoreFocus(mode);\n\n        updateVisible();\n    }\n\n    void WindowManager::popGuiMode(bool noSound)\n    {\n        if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop)\n        {\n            mDragAndDrop->finish();\n        }\n\n        if (!mGuiModes.empty())\n        {\n            const GuiMode mode = mGuiModes.back();\n            mKeyboardNavigation->saveFocus(mode);\n            mGuiModes.pop_back();\n            mGuiModeStates[mode].update(false);\n            if (!noSound)\n                playSound(mGuiModeStates[mode].mCloseSound);\n        }\n\n        if (!mGuiModes.empty())\n        {\n            const GuiMode mode = mGuiModes.back();\n            mGuiModeStates[mode].update(true);\n            mKeyboardNavigation->restoreFocus(mode);\n        }\n\n        updateVisible();\n\n        // To make sure that console window get focus again\n        if (mConsole && mConsole->isVisible())\n            mConsole->onOpen();\n    }\n\n    void WindowManager::removeGuiMode(GuiMode mode, bool noSound)\n    {\n        if (!mGuiModes.empty() && mGuiModes.back() == mode)\n        {\n            popGuiMode(noSound);\n            return;\n        }\n\n        std::vector<GuiMode>::iterator it = mGuiModes.begin();\n        while (it != mGuiModes.end())\n        {\n            if (*it == mode)\n                it = mGuiModes.erase(it);\n            else\n                ++it;\n        }\n\n        updateVisible();\n    }\n\n    void WindowManager::goToJail(int days)\n    {\n        pushGuiMode(MWGui::GM_Jail);\n        mJailScreen->goToJail(days);\n    }\n\n    void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent)\n    {\n        mSelectedSpell = spellId;\n        mSelectedEnchantItem = MWWorld::Ptr();\n        mHud->setSelectedSpell(spellId, successChancePercent);\n\n        const ESM::Spell* spell = mStore->get<ESM::Spell>().find(spellId);\n\n        mSpellWindow->setTitle(spell->mName);\n    }\n\n    void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item)\n    {\n        mSelectedEnchantItem = item;\n        mSelectedSpell = \"\";\n        const ESM::Enchantment* ench = mStore->get<ESM::Enchantment>()\n                .find(item.getClass().getEnchantment(item));\n\n        int chargePercent = static_cast<int>(item.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100);\n        mHud->setSelectedEnchantItem(item, chargePercent);\n        mSpellWindow->setTitle(item.getClass().getName(item));\n    }\n\n    const MWWorld::Ptr &WindowManager::getSelectedEnchantItem() const\n    {\n        return mSelectedEnchantItem;\n    }\n\n    void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item)\n    {\n        mSelectedWeapon = item;\n        int durabilityPercent = 100;\n        if (item.getClass().hasItemHealth(item))\n        {\n            durabilityPercent = static_cast<int>(item.getClass().getItemNormalizedHealth(item) * 100);\n        }\n        mHud->setSelectedWeapon(item, durabilityPercent);\n        mInventoryWindow->setTitle(item.getClass().getName(item));\n    }\n\n    const MWWorld::Ptr &WindowManager::getSelectedWeapon() const\n    {\n        return mSelectedWeapon;\n    }\n\n    void WindowManager::unsetSelectedSpell()\n    {\n        mSelectedSpell = \"\";\n        mSelectedEnchantItem = MWWorld::Ptr();\n        mHud->unsetSelectedSpell();\n\n        MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer();\n        if (player->getDrawState() == MWMechanics::DrawState_Spell)\n            player->setDrawState(MWMechanics::DrawState_Nothing);\n\n        mSpellWindow->setTitle(\"#{sNone}\");\n    }\n\n    void WindowManager::unsetSelectedWeapon()\n    {\n        mSelectedWeapon = MWWorld::Ptr();\n        mHud->unsetSelectedWeapon();\n        mInventoryWindow->setTitle(\"#{sSkillHandtohand}\");\n    }\n\n    void WindowManager::getMousePosition(int &x, int &y)\n    {\n        const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition();\n        x = pos.left;\n        y = pos.top;\n    }\n\n    void WindowManager::getMousePosition(float &x, float &y)\n    {\n        const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition();\n        x = static_cast<float>(pos.left);\n        y = static_cast<float>(pos.top);\n        const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        x /= viewSize.width;\n        y /= viewSize.height;\n    }\n\n    bool WindowManager::getWorldMouseOver()\n    {\n        return mHud->getWorldMouseOver();\n    }\n\n    float WindowManager::getScalingFactor()\n    {\n        return mScalingFactor;\n    }\n\n    void WindowManager::executeInConsole (const std::string& path)\n    {\n        mConsole->executeFile (path);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the execution of console commands from elsewhere in the code\n    */\n    void WindowManager::executeCommandInConsole(const std::string& command)\n    {\n        mConsole->execute(command);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; }\n    MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; }\n    MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }\n    MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the ContainerWindow from elsewhere\n        in the code\n    */\n    MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the DialogueWindow from elsewhere\n    */\n    MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; }\n    /*\n        End of tes3mp addition\n    */\n\n    void WindowManager::useItem(const MWWorld::Ptr &item, bool bypassBeastRestrictions)\n    {\n        if (mInventoryWindow)\n            mInventoryWindow->useItem(item, bypassBeastRestrictions);\n    }\n\n    bool WindowManager::isAllowed (GuiWindow wnd) const\n    {\n        return (mAllowed & wnd) != 0;\n    }\n\n    void WindowManager::allow (GuiWindow wnd)\n    {\n        mAllowed = (GuiWindow)(mAllowed | wnd);\n\n        if (wnd & GW_Inventory)\n        {\n            mBookWindow->setInventoryAllowed (true);\n            mScrollWindow->setInventoryAllowed (true);\n        }\n\n        updateVisible();\n    }\n\n    void WindowManager::disallowAll()\n    {\n        mAllowed = GW_None;\n        mRestAllowed = false;\n\n        mBookWindow->setInventoryAllowed (false);\n        mScrollWindow->setInventoryAllowed (false);\n\n        updateVisible();\n    }\n\n    void WindowManager::toggleVisible (GuiWindow wnd)\n    {\n        if (getMode() != GM_Inventory)\n            return;\n\n        std::string settingName;\n        switch (wnd)\n        {\n            case GW_Inventory:\n                settingName = \"inventory\";\n                break;\n            case GW_Map:\n                settingName = \"map\";\n                break;\n            case GW_Magic:\n                settingName = \"spells\";\n                break;\n            case GW_Stats:\n                settingName = \"stats\";\n                break;\n            default:\n                break;\n        }\n\n        if (!settingName.empty())\n        {\n            settingName += \" hidden\";\n            bool hidden = Settings::Manager::getBool(settingName, \"Windows\");\n            Settings::Manager::setBool(settingName, \"Windows\", !hidden);\n        }\n\n        mShown = (GuiWindow)(mShown ^ wnd);\n        updateVisible();\n    }\n\n    void WindowManager::forceHide(GuiWindow wnd)\n    {\n        mForceHidden = (GuiWindow)(mForceHidden | wnd);\n        updateVisible();\n    }\n\n    void WindowManager::unsetForceHide(GuiWindow wnd)\n    {\n        mForceHidden = (GuiWindow)(mForceHidden & ~wnd);\n        updateVisible();\n    }\n\n    bool WindowManager::isGuiMode() const\n    {\n        return\n            !mGuiModes.empty() ||\n            isConsoleMode() ||\n            (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox());\n    }\n\n    bool WindowManager::isConsoleMode() const\n    {\n        return mConsole && mConsole->isVisible();\n    }\n\n    MWGui::GuiMode WindowManager::getMode() const\n    {\n        if (mGuiModes.empty())\n            return GM_None;\n        return mGuiModes.back();\n    }\n\n    void WindowManager::disallowMouse()\n    {\n        mInputBlocker->setVisible (true);\n    }\n\n    void WindowManager::allowMouse()\n    {\n        mInputBlocker->setVisible (!isGuiMode ());\n    }\n\n    void WindowManager::notifyInputActionBound ()\n    {\n        mSettingsWindow->updateControlsBox ();\n        allowMouse();\n    }\n\n    bool WindowManager::containsMode(GuiMode mode) const\n    {\n        if(mGuiModes.empty())\n            return false;\n\n        return std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end();\n    }\n\n    void WindowManager::showCrosshair (bool show)\n    {\n        if (mHud)\n            mHud->setCrosshairVisible (show && mCrosshairEnabled);\n    }\n\n    void WindowManager::updateActivatedQuickKey ()\n    {\n        mQuickKeysMenu->updateActivatedQuickKey();\n    }\n\n    void WindowManager::activateQuickKey (int index)\n    {\n        mQuickKeysMenu->activateQuickKey(index);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to add quickKeys from elsewhere in the code\n    */\n    void WindowManager::setQuickKey(int slot, int quickKeyType, MWWorld::Ptr item, const std::string& spellId)\n    {\n        if (slot > 0)\n        {\n            // The actual indexes recorded for quick keys are always 1 higher than their\n            // indexes in the mKey vector, so adjust for the latter\n            mQuickKeysMenu->setSelectedIndex(slot - 1);\n\n            switch (quickKeyType)\n            {\n            case QuickKeysMenu::Type_Unassigned:\n                mQuickKeysMenu->unassignIndex(slot - 1);\n                break;\n            case QuickKeysMenu::Type_Item:\n                mQuickKeysMenu->onAssignItem(item);\n                break;\n            case QuickKeysMenu::Type_MagicItem:\n                mQuickKeysMenu->onAssignMagicItem(item);\n                break;\n            case QuickKeysMenu::Type_Magic:\n                mQuickKeysMenu->onAssignMagic(spellId);\n                break;\n            }\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    bool WindowManager::getSubtitlesEnabled ()\n    {\n        return mSubtitlesEnabled;\n    }\n\n    bool WindowManager::toggleHud()\n    {\n        mHudEnabled = !mHudEnabled;\n        updateVisible();\n        return mHudEnabled;\n    }\n\n    bool WindowManager::getRestEnabled()\n    {\n        //Enable rest dialogue if character creation finished\n        if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalFloat (\"chargenstate\")==-1)\n            mRestAllowed=true;\n        return mRestAllowed;\n    }\n\n    bool WindowManager::getPlayerSleeping ()\n    {\n        return mWaitDialog->getSleeping();\n    }\n\n    void WindowManager::wakeUpPlayer()\n    {\n        mWaitDialog->wakeUp();\n    }\n\n    void WindowManager::addVisitedLocation(const std::string& name, int x, int y)\n    {\n        mMap->addVisitedLocation (name, x, y);\n    }\n\n    const Translation::Storage& WindowManager::getTranslationDataStorage() const\n    {\n        return mTranslationDataStorage;\n    }\n\n    void WindowManager::changePointer(const std::string &name)\n    {\n        MyGUI::PointerManager::getInstance().setPointer(name);\n        onCursorChange(name);\n    }\n\n    void WindowManager::showSoulgemDialog(MWWorld::Ptr item)\n    {\n        mSoulgemDialog->show(item);\n        updateVisible();\n    }\n\n    void WindowManager::updatePlayer()\n    {\n        mInventoryWindow->updatePlayer();\n\n        const MWWorld::Ptr player = MWMechanics::getPlayer();\n        if (player.getClass().getNpcStats(player).isWerewolf())\n        {\n            setWerewolfOverlay(true);\n            forceHide((GuiWindow)(MWGui::GW_Inventory | MWGui::GW_Magic));\n        }\n    }\n\n    // Remove this wrapper once onKeyFocusChanged call is rendered unnecessary\n    void WindowManager::setKeyFocusWidget(MyGUI::Widget *widget)\n    {\n        MyGUI::InputManager::getInstance().setKeyFocusWidget(widget);\n        onKeyFocusChanged(widget);\n    }\n\n    void WindowManager::onKeyFocusChanged(MyGUI::Widget *widget)\n    {\n        if (widget && widget->castType<MyGUI::EditBox>(false))\n            SDL_StartTextInput();\n        else\n            SDL_StopTextInput();\n    }\n\n    void WindowManager::setEnemy(const MWWorld::Ptr &enemy)\n    {\n        mHud->setEnemy(enemy);\n    }\n\n    int WindowManager::getMessagesCount() const\n    {\n        int count = 0;\n        if (mMessageBoxManager)\n            count = mMessageBoxManager->getMessagesCount();\n\n        return count;\n    }\n\n    Loading::Listener* WindowManager::getLoadingScreen()\n    {\n        return mLoadingScreen;\n    }\n\n    bool WindowManager::getCursorVisible()\n    {\n        return mCursorVisible && mCursorActive;\n    }\n\n    void WindowManager::trackWindow(Layout *layout, const std::string &name)\n    {\n        std::string settingName = name;\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        bool isMaximized = Settings::Manager::getBool(name + \" maximized\", \"Windows\");\n        if (isMaximized)\n            settingName += \" maximized\";\n\n        MyGUI::IntPoint pos(static_cast<int>(Settings::Manager::getFloat(settingName + \" x\", \"Windows\") * viewSize.width),\n                            static_cast<int>(Settings::Manager::getFloat(settingName + \" y\", \"Windows\") * viewSize.height));\n        MyGUI::IntSize size (static_cast<int>(Settings::Manager::getFloat(settingName + \" w\", \"Windows\") * viewSize.width),\n                             static_cast<int>(Settings::Manager::getFloat(settingName + \" h\", \"Windows\") * viewSize.height));\n        layout->mMainWidget->setPosition(pos);\n        layout->mMainWidget->setSize(size);\n\n        MyGUI::Window* window = layout->mMainWidget->castType<MyGUI::Window>();\n        window->eventWindowChangeCoord += MyGUI::newDelegate(this, &WindowManager::onWindowChangeCoord);\n        mTrackedWindows[window] = name;\n    }\n\n    void WindowManager::toggleMaximized(Layout *layout)\n    {\n        MyGUI::Window* window = layout->mMainWidget->castType<MyGUI::Window>();\n        std::string setting = mTrackedWindows[window];\n        if (setting.empty())\n            return;\n\n        bool maximized = !Settings::Manager::getBool(setting + \" maximized\", \"Windows\");\n        if (maximized)\n            setting += \" maximized\";\n\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        float x = Settings::Manager::getFloat(setting + \" x\", \"Windows\") * float(viewSize.width);\n        float y = Settings::Manager::getFloat(setting + \" y\", \"Windows\") * float(viewSize.height);\n        float w = Settings::Manager::getFloat(setting + \" w\", \"Windows\") * float(viewSize.width);\n        float h = Settings::Manager::getFloat(setting + \" h\", \"Windows\") * float(viewSize.height);\n        window->setCoord(x, y, w, h);\n        Settings::Manager::setBool(mTrackedWindows[window] + \" maximized\", \"Windows\", maximized);\n    }\n\n    void WindowManager::onWindowChangeCoord(MyGUI::Window *_sender)\n    {\n        std::string setting = mTrackedWindows[_sender];\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        float x = _sender->getPosition().left / float(viewSize.width);\n        float y = _sender->getPosition().top / float(viewSize.height);\n        float w = _sender->getSize().width / float(viewSize.width);\n        float h = _sender->getSize().height / float(viewSize.height);\n        Settings::Manager::setFloat(setting + \" x\", \"Windows\", x);\n        Settings::Manager::setFloat(setting + \" y\", \"Windows\", y);\n        Settings::Manager::setFloat(setting + \" w\", \"Windows\", w);\n        Settings::Manager::setFloat(setting + \" h\", \"Windows\", h);\n        bool maximized = Settings::Manager::getBool(setting + \" maximized\", \"Windows\");\n        if (maximized)\n            Settings::Manager::setBool(setting + \" maximized\", \"Windows\", false);\n    }\n\n    void WindowManager::clear()\n    {\n        mPlayerBounty = -1;\n\n        for (WindowBase* window : mWindows)\n            window->clear();\n\n        if (mLocalMapRender)\n            mLocalMapRender->clear();\n\n        mMessageBoxManager->clear();\n\n        mToolTips->clear();\n\n        mSelectedSpell.clear();\n        mCustomMarkers.clear();\n\n        mForceHidden = GW_None;\n        mRestAllowed = true;\n\n        while (!mGuiModes.empty())\n            popGuiMode();\n\n        updateVisible();\n    }\n\n    void WindowManager::write(ESM::ESMWriter &writer, Loading::Listener& progress)\n    {\n        mMap->write(writer, progress);\n\n        mQuickKeysMenu->write(writer);\n\n        if (!mSelectedSpell.empty())\n        {\n            writer.startRecord(ESM::REC_ASPL);\n            writer.writeHNString(\"ID__\", mSelectedSpell);\n            writer.endRecord(ESM::REC_ASPL);\n        }\n\n        for (CustomMarkerCollection::ContainerType::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it)\n        {\n            writer.startRecord(ESM::REC_MARK);\n            it->second.save(writer);\n            writer.endRecord(ESM::REC_MARK);\n        }\n    }\n\n    void WindowManager::readRecord(ESM::ESMReader &reader, uint32_t type)\n    {\n        if (type == ESM::REC_GMAP)\n            mMap->readRecord(reader, type);\n        else if (type == ESM::REC_KEYS)\n            mQuickKeysMenu->readRecord(reader, type);\n        else if (type == ESM::REC_ASPL)\n        {\n            reader.getSubNameIs(\"ID__\");\n            std::string spell = reader.getHString();\n            if (mStore->get<ESM::Spell>().search(spell))\n                mSelectedSpell = spell;\n        }\n        else if (type == ESM::REC_MARK)\n        {\n            ESM::CustomMarker marker;\n            marker.load(reader);\n            mCustomMarkers.addMarker(marker, false);\n        }\n    }\n\n    int WindowManager::countSavedGameRecords() const\n    {\n        return 1 // Global map\n                + 1 // QuickKeysMenu\n                + mCustomMarkers.size()\n                + (!mSelectedSpell.empty() ? 1 : 0);\n    }\n\n    bool WindowManager::isSavingAllowed() const\n    {\n        return !MyGUI::InputManager::getInstance().isModalAny()\n                && !isConsoleMode()\n                // TODO: remove this, once we have properly serialized the state of open windows\n                && (!isGuiMode() || (mGuiModes.size() == 1 && (getMode() == GM_MainMenu || getMode() == GM_Rest)));\n    }\n\n    void WindowManager::playVideo(const std::string &name, bool allowSkipping)\n    {\n        mVideoWidget->playVideo(\"video\\\\\" + name);\n\n        mVideoWidget->eventKeyButtonPressed.clear();\n        mVideoBackground->eventKeyButtonPressed.clear();\n        if (allowSkipping)\n        {\n            mVideoWidget->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed);\n            mVideoBackground->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed);\n        }\n\n        enableScene(false);\n\n        MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize();\n        sizeVideo(screenSize.width, screenSize.height);\n\n        MyGUI::Widget* oldKeyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n        setKeyFocusWidget(mVideoWidget);\n\n        mVideoBackground->setVisible(true);\n\n        bool cursorWasVisible = mCursorVisible;\n        setCursorVisible(false);\n\n        if (mVideoWidget->hasAudioStream())\n            MWBase::Environment::get().getSoundManager()->pauseSounds(MWSound::VideoPlayback,\n                ~MWSound::Type::Movie & MWSound::Type::Mask\n            );\n\n        Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());\n        while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())\n        {\n            const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(frameRateLimiter.getLastFrameDuration()).count();\n\n            MWBase::Environment::get().getInputManager()->update(dt, true, false);\n\n            if (!mWindowVisible)\n            {\n                mVideoWidget->pause();\n                std::this_thread::sleep_for(std::chrono::milliseconds(5));\n            }\n            else\n            {\n                if (mVideoWidget->isPaused())\n                    mVideoWidget->resume();\n\n                mViewer->eventTraversal();\n                mViewer->updateTraversal();\n                mViewer->renderingTraversals();\n            }\n            // at the time this function is called we are in the middle of a frame,\n            // so out of order calls are necessary to get a correct frameNumber for the next frame.\n            // refer to the advance() and frame() order in Engine::go()\n            mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());\n\n            frameRateLimiter.limit();\n        }\n        mVideoWidget->stop();\n\n        MWBase::Environment::get().getSoundManager()->resumeSounds(MWSound::VideoPlayback);\n\n        setKeyFocusWidget(oldKeyFocus);\n\n        setCursorVisible(cursorWasVisible);\n\n        // Restore normal rendering\n        updateVisible();\n\n        mVideoBackground->setVisible(false);\n    }\n\n    void WindowManager::sizeVideo(int screenWidth, int screenHeight)\n    {\n        // Use black bars to correct aspect ratio\n        bool stretch = Settings::Manager::getBool(\"stretch menu background\", \"GUI\");\n        mVideoBackground->setSize(screenWidth, screenHeight);\n        mVideoWidget->autoResize(stretch);\n    }\n\n    void WindowManager::exitCurrentModal()\n    {\n        if (!mCurrentModals.empty())\n        {\n            WindowModal* window = mCurrentModals.back();\n            if (!window->exit())\n                return;\n            window->setVisible(false);\n        }\n    }\n\n    void WindowManager::addCurrentModal(WindowModal *input)\n    {\n        if (mCurrentModals.empty())\n            mKeyboardNavigation->saveFocus(getMode());\n\n        mCurrentModals.push_back(input);\n        mKeyboardNavigation->restoreFocus(-1);\n\n        mKeyboardNavigation->setModalWindow(input->mMainWidget);\n        mKeyboardNavigation->setDefaultFocus(input->mMainWidget, input->getDefaultKeyFocus());\n    }\n\n    void WindowManager::removeCurrentModal(WindowModal* input)\n    {\n        if(!mCurrentModals.empty())\n        {\n            if(input == mCurrentModals.back())\n            {\n                mCurrentModals.pop_back();\n                mKeyboardNavigation->saveFocus(-1);\n            }\n            else\n            {\n                auto found = std::find(mCurrentModals.begin(), mCurrentModals.end(), input);\n                if (found != mCurrentModals.end())\n                    mCurrentModals.erase(found);\n                else\n                    Log(Debug::Warning) << \"Warning: can't find modal window \" << input;\n            }\n        }\n        if (mCurrentModals.empty())\n        {\n            mKeyboardNavigation->setModalWindow(nullptr);\n            mKeyboardNavigation->restoreFocus(getMode());\n        }\n        else\n            mKeyboardNavigation->setModalWindow(mCurrentModals.back()->mMainWidget);\n    }\n\n    void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)\n    {\n        if (_key == MyGUI::KeyCode::Escape)\n            mVideoWidget->stop();\n    }\n\n    void WindowManager::updatePinnedWindows()\n    {\n        mInventoryWindow->setPinned(Settings::Manager::getBool(\"inventory pin\", \"Windows\"));\n        if (Settings::Manager::getBool(\"inventory hidden\", \"Windows\"))\n            mShown = (GuiWindow)(mShown ^ GW_Inventory);\n\n        mMap->setPinned(Settings::Manager::getBool(\"map pin\", \"Windows\"));\n        if (Settings::Manager::getBool(\"map hidden\", \"Windows\"))\n            mShown = (GuiWindow)(mShown ^ GW_Map);\n\n        mSpellWindow->setPinned(Settings::Manager::getBool(\"spells pin\", \"Windows\"));\n        if (Settings::Manager::getBool(\"spells hidden\", \"Windows\"))\n            mShown = (GuiWindow)(mShown ^ GW_Magic);\n\n        mStatsWindow->setPinned(Settings::Manager::getBool(\"stats pin\", \"Windows\"));\n        if (Settings::Manager::getBool(\"stats hidden\", \"Windows\"))\n            mShown = (GuiWindow)(mShown ^ GW_Stats);\n    }\n\n    void WindowManager::pinWindow(GuiWindow window)\n    {\n        switch (window)\n        {\n        case GW_Inventory:\n            mInventoryWindow->setPinned(true);\n            break;\n        case GW_Map:\n            mMap->setPinned(true);\n            break;\n        case GW_Magic:\n            mSpellWindow->setPinned(true);\n            break;\n        case GW_Stats:\n            mStatsWindow->setPinned(true);\n            break;\n        default:\n            break;\n        }\n\n        updateVisible();\n    }\n\n    void WindowManager::fadeScreenIn(const float time, bool clearQueue, float delay)\n    {\n        if (clearQueue)\n            mScreenFader->clearQueue();\n        mScreenFader->fadeOut(time, delay);\n    }\n\n    void WindowManager::fadeScreenOut(const float time, bool clearQueue, float delay)\n    {\n        if (clearQueue)\n            mScreenFader->clearQueue();\n        mScreenFader->fadeIn(time, delay);\n    }\n\n    void WindowManager::fadeScreenTo(const int percent, const float time, bool clearQueue, float delay)\n    {\n        if (clearQueue)\n            mScreenFader->clearQueue();\n        mScreenFader->fadeTo(percent, time, delay);\n    }\n\n    void WindowManager::setBlindness(const int percent)\n    {\n        mBlindnessFader->notifyAlphaChanged(percent / 100.f);\n    }\n\n    void WindowManager::activateHitOverlay(bool interrupt)\n    {\n        if (!mHitFaderEnabled)\n            return;\n\n        if (!interrupt && !mHitFader->isEmpty())\n            return;\n\n        mHitFader->clearQueue();\n        mHitFader->fadeTo(100, 0.0f);\n        mHitFader->fadeTo(0, 0.5f);\n    }\n\n    void WindowManager::setWerewolfOverlay(bool set)\n    {\n        if (!mWerewolfOverlayEnabled)\n            return;\n\n        if (mWerewolfFader)\n            mWerewolfFader->notifyAlphaChanged(set ? 1.0f : 0.0f);\n    }\n\n    void WindowManager::onClipboardChanged(const std::string &_type, const std::string &_data)\n    {\n        if (_type == \"Text\")\n            SDL_SetClipboardText(MyGUI::TextIterator::getOnlyText(MyGUI::UString(_data)).asUTF8().c_str());\n    }\n\n    void WindowManager::onClipboardRequested(const std::string &_type, std::string &_data)\n    {\n        if (_type != \"Text\")\n            return;\n        char* text=nullptr;\n        text = SDL_GetClipboardText();\n        if (text)\n            _data = MyGUI::TextIterator::toTagsString(text);\n\n        SDL_free(text);\n    }\n\n    void WindowManager::toggleConsole()\n    {\n        bool visible = mConsole->isVisible();\n\n        if (!visible && !mGuiModes.empty())\n            mKeyboardNavigation->saveFocus(mGuiModes.back());\n\n        mConsole->setVisible(!visible);\n\n        if (visible && !mGuiModes.empty())\n            mKeyboardNavigation->restoreFocus(mGuiModes.back());\n\n        updateVisible();\n    }\n\n    void WindowManager::toggleDebugWindow()\n    {\n#ifndef BT_NO_PROFILE\n        mDebugWindow->setVisible(!mDebugWindow->isVisible());\n#endif\n    }\n\n    void WindowManager::cycleSpell(bool next)\n    {\n        if (!isGuiMode())\n            mSpellWindow->cycle(next);\n    }\n\n    void WindowManager::cycleWeapon(bool next)\n    {\n        if (!isGuiMode())\n            mInventoryWindow->cycle(next);\n    }\n\n    void WindowManager::playSound(const std::string& soundId, float volume, float pitch)\n    {\n        if (soundId.empty())\n            return;\n\n        MWBase::Environment::get().getSoundManager()->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);\n    }\n\n    void WindowManager::updateSpellWindow()\n    {\n        if (mSpellWindow)\n            mSpellWindow->updateSpells();\n    }\n\n    void WindowManager::setConsoleSelectedObject(const MWWorld::Ptr &object)\n    {\n        mConsole->setSelectedObject(object);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the direct setting of a console's Ptr, without the assumption that an object\n        was clicked and that key focus should be restored to the console window, for console\n        commands executed via server scripts\n    */\n    void WindowManager::setConsolePtr(const MWWorld::Ptr &object)\n    {\n        mConsole->setPtr(object);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Allow the clearing of the console's Ptr from elsewhere in the code, so that\n        Ptrs used in console commands run from server scripts do not stay selected\n    */\n    void WindowManager::clearConsolePtr()\n    {\n        mConsole->resetReference();\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    std::string WindowManager::correctIconPath(const std::string& path)\n    {\n        return Misc::ResourceHelpers::correctIconPath(path, mResourceSystem->getVFS());\n    }\n\n    std::string WindowManager::correctBookartPath(const std::string& path, int width, int height, bool* exists)\n    {\n        std::string corrected = Misc::ResourceHelpers::correctBookartPath(path, width, height, mResourceSystem->getVFS());\n        if (exists)\n            *exists = mResourceSystem->getVFS()->exists(corrected);\n        return corrected;\n    }\n\n    std::string WindowManager::correctTexturePath(const std::string& path)\n    {\n        return Misc::ResourceHelpers::correctTexturePath(path, mResourceSystem->getVFS());\n    }\n\n    bool WindowManager::textureExists(const std::string &path)\n    {\n        std::string corrected = Misc::ResourceHelpers::correctTexturePath(path, mResourceSystem->getVFS());\n        return mResourceSystem->getVFS()->exists(corrected);\n    }\n\n    void WindowManager::createCursors()\n    {\n        // FIXME: currently we do not scale cursor since it is not a MyGUI widget.\n        // In theory, we can do it manually (rescale the cursor image via osg::Imag::scaleImage() and scale the hotspot position).\n        // Unfortunately, this apploach can lead to driver crashes on some setups (e.g. on laptops with nvidia-prime on Linux).\n        MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator();\n        while (enumerator.next())\n        {\n            MyGUI::IResource* resource = enumerator.current().second;\n            ResourceImageSetPointerFix* imgSetPointer = resource->castType<ResourceImageSetPointerFix>(false);\n            if (!imgSetPointer)\n                continue;\n            std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture;\n\n            osg::ref_ptr<osg::Image> image = mResourceSystem->getImageManager()->getImage(tex_name);\n\n            if(image.valid())\n            {\n                //everything looks good, send it to the cursor manager\n                Uint8 hotspot_x = imgSetPointer->getHotSpot().left;\n                Uint8 hotspot_y = imgSetPointer->getHotSpot().top;\n                int rotation = imgSetPointer->getRotation();\n\n                mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, image, hotspot_x, hotspot_y);\n            }\n        }\n    }\n\n    void WindowManager::createTextures()\n    {\n        {\n            MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture(\"white\");\n            tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8);\n            unsigned char* data = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write));\n            for (int x=0; x<8; ++x)\n                for (int y=0; y<8; ++y)\n                {\n                    *(data++) = 255;\n                    *(data++) = 255;\n                    *(data++) = 255;\n                }\n            tex->unlock();\n        }\n\n        {\n            MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture(\"black\");\n            tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8);\n            unsigned char* data = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write));\n            for (int x=0; x<8; ++x)\n                for (int y=0; y<8; ++y)\n                {\n                    *(data++) = 0;\n                    *(data++) = 0;\n                    *(data++) = 0;\n                }\n            tex->unlock();\n        }\n\n        {\n            MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture(\"transparent\");\n            tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8);\n            setMenuTransparency(Settings::Manager::getFloat(\"menu transparency\", \"GUI\"));\n        }\n    }\n\n    void WindowManager::setMenuTransparency(float value)\n    {\n        MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().getTexture(\"transparent\");\n        unsigned char* data = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write));\n        for (int x=0; x<8; ++x)\n            for (int y=0; y<8; ++y)\n            {\n                *(data++) = 255;\n                *(data++) = 255;\n                *(data++) = 255;\n                *(data++) = static_cast<unsigned char>(value*255);\n            }\n        tex->unlock();\n    }\n\n    void WindowManager::addCell(MWWorld::CellStore* cell)\n    {\n        mLocalMapRender->addCell(cell);\n    }\n\n    void WindowManager::removeCell(MWWorld::CellStore *cell)\n    {\n        mLocalMapRender->removeCell(cell);\n    }\n\n    void WindowManager::writeFog(MWWorld::CellStore *cell)\n    {\n        mLocalMapRender->saveFogOfWar(cell);\n    }\n\n    const MWGui::TextColours& WindowManager::getTextColours()\n    {\n        return mTextColours;\n    }\n\n    bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat)\n    {\n        if (!mKeyboardNavigation->injectKeyPress(key, text, repeat))\n        {\n            MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();\n            bool widgetActive = MyGUI::InputManager::getInstance().injectKeyPress(key, text);\n            if (!widgetActive || !focus)\n                return false;\n            // FIXME: MyGUI doesn't allow widgets to state if a given key was actually used, so make a guess\n            if (focus->getTypeName().find(\"Button\") != std::string::npos)\n            {\n                switch (key.getValue())\n                {\n                case MyGUI::KeyCode::ArrowDown:\n                case MyGUI::KeyCode::ArrowUp:\n                case MyGUI::KeyCode::ArrowLeft:\n                case MyGUI::KeyCode::ArrowRight:\n                case MyGUI::KeyCode::Return:\n                case MyGUI::KeyCode::NumpadEnter:\n                case MyGUI::KeyCode::Space:\n                    return true;\n                default:\n                    return false;\n                }\n            }\n            return false;\n        }\n        else\n            return true;\n    }\n\n    bool WindowManager::injectKeyRelease(MyGUI::KeyCode key)\n    {\n        return MyGUI::InputManager::getInstance().injectKeyRelease(key);\n    }\n\n    void WindowManager::GuiModeState::update(bool visible)\n    {\n        for (unsigned int i=0; i<mWindows.size(); ++i)\n            mWindows[i]->setVisible(visible);\n    }\n\n    void WindowManager::watchActor(const MWWorld::Ptr& ptr)\n    {\n        mStatsWatcher->watchActor(ptr);\n    }\n\n    MWWorld::Ptr WindowManager::getWatchedActor() const\n    {\n        return mStatsWatcher->getWatchedActor();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/windowmanagerimp.hpp",
    "content": "#ifndef MWGUI_WINDOWMANAGERIMP_H\n#define MWGUI_WINDOWMANAGERIMP_H\n\n/**\n   This class owns and controls all the MW specific windows in the\n   GUI. It can enable/disable Gui mode, and is responsible for sending\n   and retrieving information from the Gui.\n**/\n\n#include <stack>\n\n#include <osg/ref_ptr>\n\n#include \"../mwbase/windowmanager.hpp\"\n\n#include <components/sdlutil/events.hpp>\n#include <components/settings/settings.hpp>\n#include <components/to_utf8/to_utf8.hpp>\n\n#include \"mapwindow.hpp\"\n#include \"statswatcher.hpp\"\n#include \"textcolours.hpp\"\n\n#include <MyGUI_KeyCode.h>\n#include <MyGUI_Types.h>\n\nnamespace MyGUI\n{\n    class Gui;\n    class Widget;\n    class Window;\n    class UString;\n    class ImageBox;\n}\n\nnamespace MWWorld\n{\n    class ESMStore;\n}\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Translation\n{\n    class Storage;\n}\n\nnamespace osg\n{\n    class Group;\n}\nnamespace osgViewer\n{\n    class Viewer;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace SceneUtil\n{\n    class WorkQueue;\n}\n\nnamespace SDLUtil\n{\n    class SDLCursorManager;\n    class VideoWrapper;\n}\n\nnamespace osgMyGUI\n{\n    class Platform;\n}\n\nnamespace Gui\n{\n    class FontLoader;\n}\n\nnamespace MWRender\n{\n    class LocalMap;\n}\n\nnamespace MWGui\n{\n  class WindowBase;\n  class HUD;\n  class MapWindow;\n  class MainMenu;\n  class StatsWindow;\n  class InventoryWindow;\n  struct JournalWindow;\n  class CharacterCreation;\n  class DragAndDrop;\n  class ToolTips;\n  class TextInputDialog;\n  class InfoBoxDialog;\n  class MessageBoxManager;\n  class SettingsWindow;\n  class AlchemyWindow;\n  class QuickKeysMenu;\n  class LoadingScreen;\n  class LevelupDialog;\n  class WaitDialog;\n  class SpellCreationDialog;\n  class EnchantingDialog;\n  class TrainingWindow;\n  class SpellIcons;\n  class MerchantRepair;\n  class SoulgemDialog;\n  class Recharge;\n  class CompanionWindow;\n  class VideoWidget;\n  class WindowModal;\n  class ScreenFader;\n  class DebugWindow;\n  class JailScreen;\n  class KeyboardNavigation;\n\n  class WindowManager :\n      public MWBase::WindowManager\n  {\n  public:\n    typedef std::pair<std::string, int> Faction;\n    typedef std::vector<Faction> FactionList;\n\n    WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,\n                  const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,\n                  ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& localPath);\n    virtual ~WindowManager();\n\n    /// Set the ESMStore to use for retrieving of GUI-related strings.\n    void setStore (const MWWorld::ESMStore& store);\n\n    void initUI();\n    void loadUserFonts() override;\n\n    Loading::Listener* getLoadingScreen() override;\n\n    /// @note This method will block until the video finishes playing\n    /// (and will continually update the window while doing so)\n    void playVideo(const std::string& name, bool allowSkipping) override;\n\n    /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.\n    void setKeyFocusWidget (MyGUI::Widget* widget) override;\n\n    void setNewGame(bool newgame) override;\n\n    void pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg) override;\n    void pushGuiMode (GuiMode mode) override;\n    void popGuiMode(bool noSound=false) override;\n    void removeGuiMode(GuiMode mode, bool noSound=false) override; ///< can be anywhere in the stack\n\n    void goToJail(int days) override;\n\n    GuiMode getMode() const override;\n    bool containsMode(GuiMode mode) const override;\n\n    bool isGuiMode() const override;\n\n    bool isConsoleMode() const override;\n\n    void toggleVisible(GuiWindow wnd) override;\n\n    void forceHide(MWGui::GuiWindow wnd) override;\n    void unsetForceHide(MWGui::GuiWindow wnd) override;\n\n    /// Disallow all inventory mode windows\n    void disallowAll() override;\n\n    /// Allow one or more windows\n    void allow(GuiWindow wnd) override;\n\n    bool isAllowed(GuiWindow wnd) const override;\n\n    /// \\todo investigate, if we really need to expose every single lousy UI element to the outside world\n    MWGui::InventoryWindow* getInventoryWindow() override;\n    MWGui::CountDialog* getCountDialog() override;\n    MWGui::ConfirmationDialog* getConfirmationDialog() override;\n    MWGui::TradeWindow* getTradeWindow() override;\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the ContainerWindow from elsewhere\n        in the code\n    */\n    virtual MWGui::ContainerWindow* getContainerWindow();\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the DialogueWindow from elsewhere\n    */\n    virtual MWGui::DialogueWindow* getDialogueWindow();\n    /*\n        End of tes3mp addition\n    */\n\n    /// Make the player use an item, while updating GUI state accordingly\n    void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions=false) override;\n\n    void updateSpellWindow() override;\n\n    void setConsoleSelectedObject(const MWWorld::Ptr& object) override;\n\n    /*\n        Start of tes3mp addition\n\n        Allow the direct setting of a console's Ptr, without the assumption that an object\n        was clicked and that key focus should be restored to the console window, for console\n        commands executed via server scripts\n    */\n    virtual void setConsolePtr(const MWWorld::Ptr& object);\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Allow the clearing of the console's Ptr from elsewhere in the code, so that\n        Ptrs used in console commands run from server scripts do not stay selected\n    */\n    virtual void clearConsolePtr();\n    /*\n        End of tes3mp addition\n    */\n\n    /// Set time left for the player to start drowning (update the drowning bar)\n    /// @param time time left to start drowning\n    /// @param maxTime how long we can be underwater (in total) until drowning starts\n    void setDrowningTimeLeft (float time, float maxTime) override;\n\n    void changeCell(const MWWorld::CellStore* cell) override; ///< change the active cell\n\n    /*\n        Start of tes3mp addition\n\n        Allow the setting of the image data for a global map tile from elsewhere\n        in the code\n    */\n    virtual void setGlobalMapImage(int cellX, int cellY, const std::vector<char>& imageData);\n    /*\n        End of tes3mp addition\n    */\n\n    void setFocusObject(const MWWorld::Ptr& focus) override;\n    void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) override;\n\n    void getMousePosition(int& x, int& y) override;\n    void getMousePosition(float& x, float& y) override;\n    void setDragDrop(bool dragDrop) override;\n\n    /*\n        Start of tes3mp addition\n\n        Allow the completion of a drag and drop from elsewhere in the code\n    */\n    virtual void finishDragDrop();\n    /*\n        End of tes3mp addition\n    */\n\n    bool getWorldMouseOver() override;\n\n    float getScalingFactor() override;\n\n    bool toggleFogOfWar() override;\n    bool toggleFullHelp() override; ///< show extra info in item tooltips (owner, script)\n    bool getFullHelp() const override;\n\n    void setActiveMap(int x, int y, bool interior) override;\n    ///< set the indices of the map texture that should be used\n\n    /// sets the visibility of the drowning bar\n    void setDrowningBarVisibility(bool visible) override;\n\n    // sets the visibility of the hud health/magicka/stamina bars\n    void setHMSVisibility(bool visible) override;\n    // sets the visibility of the hud minimap\n    void setMinimapVisibility(bool visible) override;\n    void setWeaponVisibility(bool visible) override;\n    void setSpellVisibility(bool visible) override;\n    void setSneakVisibility(bool visible) override;\n\n    /// activate selected quick key\n    void activateQuickKey (int index) override;\n    /// update activated quick key state (if action executing was delayed for some reason)\n    void updateActivatedQuickKey () override;\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to add quickKeys from elsewhere in the code\n    */\n    virtual void setQuickKey(int slot, int quickKeyType, MWWorld::Ptr item, const std::string& spellId = \"\");\n    /*\n        End of tes3mp addition\n    */\n\n    std::string getSelectedSpell() override { return mSelectedSpell; }\n    void setSelectedSpell(const std::string& spellId, int successChancePercent) override;\n    void setSelectedEnchantItem(const MWWorld::Ptr& item) override;\n    const MWWorld::Ptr& getSelectedEnchantItem() const override;\n    void setSelectedWeapon(const MWWorld::Ptr& item) override;\n    const MWWorld::Ptr& getSelectedWeapon() const override;\n    int getFontHeight() const override;\n    void unsetSelectedSpell() override;\n    void unsetSelectedWeapon() override;\n\n    void updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr) override;\n\n    void showCrosshair(bool show) override;\n    bool getSubtitlesEnabled() override;\n\n    /// Turn visibility of HUD on or off\n    bool toggleHud() override;\n\n    void disallowMouse() override;\n    void allowMouse() override;\n    void notifyInputActionBound() override;\n\n    void addVisitedLocation(const std::string& name, int x, int y) override;\n\n    ///Hides dialog and schedules dialog to be deleted.\n    void removeDialog(Layout* dialog) override;\n\n    ///Gracefully attempts to exit the topmost GUI mode\n    void exitCurrentGuiMode() override;\n\n    void messageBox(const std::string & message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) override;\n    void staticMessageBox(const std::string& message) override;\n    void removeStaticMessageBox() override;\n    /*\n        Start of tes3mp change (major)\n\n        Add a hasServerOrigin boolean to the list of arguments so those messageboxes\n        can be differentiated from client-only ones\n    */\n    void interactiveMessageBox(const std::string& message,\n        const std::vector<std::string>& buttons = std::vector<std::string>(), bool block = false, bool hasServerOrigin = false) override;\n    /*\n        End of tes3mp change (major)\n    */\n\n    int readPressedButton () override; ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)\n\n    void update (float duration) override;\n\n    /**\n     * Fetches a GMST string from the store, if there is no setting with the given\n     * ID or it is not a string the default string is returned.\n     *\n     * @param id Identifier for the GMST setting, e.g. \"aName\"\n     * @param default Default value if the GMST setting cannot be used.\n     */\n    std::string getGameSettingString(const std::string &id, const std::string &default_) override;\n\n    void processChangedSettings(const Settings::CategorySettingVector& changed) override;\n\n    void windowVisibilityChange(bool visible) override;\n    void windowResized(int x, int y) override;\n    void windowClosed() override;\n    bool isWindowVisible() override;\n\n    void watchActor(const MWWorld::Ptr& ptr) override;\n    MWWorld::Ptr getWatchedActor() const override;\n\n    void executeInConsole (const std::string& path) override;\n\n    /*\n        Start of tes3mp addition\n\n        Allow the execution of console commands from elsewhere in the code\n    */\n    virtual void executeCommandInConsole(const std::string& command);\n    /*\n        End of tes3mp addition\n    */\n\n    void enableRest() override { mRestAllowed = true; }\n    bool getRestEnabled() override;\n\n    bool getJournalAllowed() override { return (mAllowed & GW_Magic) != 0; }\n\n    bool getPlayerSleeping() override;\n    void wakeUpPlayer() override;\n\n    void updatePlayer() override;\n\n    void showSoulgemDialog (MWWorld::Ptr item) override;\n\n    void changePointer (const std::string& name) override;\n\n    void setEnemy (const MWWorld::Ptr& enemy) override;\n\n    int getMessagesCount() const override;\n\n    const Translation::Storage& getTranslationDataStorage() const override;\n\n    void onSoulgemDialogButtonPressed (int button);\n\n    bool getCursorVisible() override;\n\n    /// Call when mouse cursor or buttons are used.\n    void setCursorActive(bool active) override;\n\n    /// Clear all savegame-specific data\n    void clear() override;\n\n    void write (ESM::ESMWriter& writer, Loading::Listener& progress) override;\n    void readRecord (ESM::ESMReader& reader, uint32_t type) override;\n    int countSavedGameRecords() const override;\n\n    /// Does the current stack of GUI-windows permit saving?\n    bool isSavingAllowed() const override;\n\n    /// Send exit command to active Modal window **/\n    void exitCurrentModal() override;\n\n    /// Sets the current Modal\n    /** Used to send exit command to active Modal when Esc is pressed **/\n    void addCurrentModal(WindowModal* input) override;\n\n    /// Removes the top Modal\n    /** Used when one Modal adds another Modal\n        \\param input Pointer to the current modal, to ensure proper modal is removed **/\n    void removeCurrentModal(WindowModal* input) override;\n\n    void pinWindow (MWGui::GuiWindow window) override;\n    void toggleMaximized(Layout *layout) override;\n\n    /// Fade the screen in, over \\a time seconds\n    void fadeScreenIn(const float time, bool clearQueue, float delay) override;\n    /// Fade the screen out to black, over \\a time seconds\n    void fadeScreenOut(const float time, bool clearQueue, float delay) override;\n    /// Fade the screen to a specified percentage of black, over \\a time seconds\n    void fadeScreenTo(const int percent, const float time, bool clearQueue, float delay) override;\n    /// Darken the screen to a specified percentage\n    void setBlindness(const int percent) override;\n\n    void activateHitOverlay(bool interrupt) override;\n    void setWerewolfOverlay(bool set) override;\n\n    void toggleConsole() override;\n    void toggleDebugWindow() override;\n\n    /// Cycle to next or previous spell\n    void cycleSpell(bool next) override;\n    /// Cycle to next or previous weapon\n    void cycleWeapon(bool next) override;\n\n    void playSound(const std::string& soundId, float volume = 1.f, float pitch = 1.f) override;\n\n    // In WindowManager for now since there isn't a VFS singleton\n    std::string correctIconPath(const std::string& path) override;\n    std::string correctBookartPath(const std::string& path, int width, int height, bool* exists = nullptr) override;\n    std::string correctTexturePath(const std::string& path) override;\n    bool textureExists(const std::string& path) override;\n\n    void addCell(MWWorld::CellStore* cell) override;\n    void removeCell(MWWorld::CellStore* cell) override;\n    void writeFog(MWWorld::CellStore* cell) override;\n\n    const MWGui::TextColours& getTextColours() override;\n\n    bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat=false) override;\n    bool injectKeyRelease(MyGUI::KeyCode key) override;\n\n  private:\n    unsigned int mOldUpdateMask; unsigned int mOldCullMask;\n\n    const MWWorld::ESMStore* mStore;\n    Resource::ResourceSystem* mResourceSystem;\n    osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;\n\n    osgMyGUI::Platform* mGuiPlatform;\n    osgViewer::Viewer* mViewer;\n\n    std::unique_ptr<Gui::FontLoader> mFontLoader;\n    std::unique_ptr<StatsWatcher> mStatsWatcher;\n\n    bool mConsoleOnlyScripts;\n\n    std::map<MyGUI::Window*, std::string> mTrackedWindows;\n    void trackWindow(Layout* layout, const std::string& name);\n    void onWindowChangeCoord(MyGUI::Window* _sender);\n\n    std::string mSelectedSpell;\n    MWWorld::Ptr mSelectedEnchantItem;\n    MWWorld::Ptr mSelectedWeapon;\n\n    std::vector<WindowModal*> mCurrentModals;\n\n    // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window).\n    CustomMarkerCollection mCustomMarkers;\n\n    HUD *mHud;\n    MapWindow *mMap;\n    MWRender::LocalMap* mLocalMapRender;\n    ToolTips *mToolTips;\n    StatsWindow *mStatsWindow;\n    MessageBoxManager *mMessageBoxManager;\n    Console *mConsole;\n    DialogueWindow *mDialogueWindow;\n    DragAndDrop* mDragAndDrop;\n    InventoryWindow *mInventoryWindow;\n    ScrollWindow* mScrollWindow;\n    BookWindow* mBookWindow;\n    CountDialog* mCountDialog;\n    TradeWindow* mTradeWindow;\n    SettingsWindow* mSettingsWindow;\n    ConfirmationDialog* mConfirmationDialog;\n    SpellWindow* mSpellWindow;\n    QuickKeysMenu* mQuickKeysMenu;\n    LoadingScreen* mLoadingScreen;\n    WaitDialog* mWaitDialog;\n    SoulgemDialog* mSoulgemDialog;\n    MyGUI::ImageBox* mVideoBackground;\n    VideoWidget* mVideoWidget;\n    ScreenFader* mWerewolfFader;\n    ScreenFader* mBlindnessFader;\n    ScreenFader* mHitFader;\n    ScreenFader* mScreenFader;\n    DebugWindow* mDebugWindow;\n    JailScreen* mJailScreen;\n\n    /*\n        Start of tes3mp addition\n\n        Keep a pointer to the container window because of its usefulness\n        in multiplayer for container sync\n    */\n    ContainerWindow* mContainerWindow;\n    /*\n        End of tes3mp addition\n    */\n\n    std::vector<WindowBase*> mWindows;\n\n    Translation::Storage& mTranslationDataStorage;\n\n    CharacterCreation* mCharGen;\n\n    MyGUI::Widget* mInputBlocker;\n\n    bool mCrosshairEnabled;\n    bool mSubtitlesEnabled;\n    bool mHitFaderEnabled;\n    bool mWerewolfOverlayEnabled;\n    bool mHudEnabled;\n    bool mCursorVisible;\n    bool mCursorActive;\n\n    int mPlayerBounty;\n\n    void setCursorVisible(bool visible) override;\n\n    MyGUI::Gui *mGui; // Gui\n\n    struct GuiModeState\n    {\n        GuiModeState(WindowBase* window)\n        {\n            mWindows.push_back(window);\n        }\n        GuiModeState(const std::vector<WindowBase*>& windows)\n            : mWindows(windows) {}\n        GuiModeState() {}\n\n        void update(bool visible);\n\n        std::vector<WindowBase*> mWindows;\n\n        std::string mCloseSound;\n        std::string mOpenSound;\n    };\n    // Defines the windows that should be shown in a particular GUI mode.\n    std::map<GuiMode, GuiModeState> mGuiModeStates;\n    // The currently active stack of GUI modes (top mode is the one we are in).\n    std::vector<GuiMode> mGuiModes;\n\n    SDLUtil::SDLCursorManager* mCursorManager;\n\n    std::vector<Layout*> mGarbageDialogs;\n    void cleanupGarbage();\n\n    GuiWindow mShown; // Currently shown windows in inventory mode\n    GuiWindow mForceHidden; // Hidden windows (overrides mShown)\n\n    /* Currently ALLOWED windows in inventory mode. This is used at\n       the start of the game, when windows are enabled one by one\n       through script commands. You can manipulate this through using\n       allow() and disableAll().\n     */\n    GuiWindow mAllowed;\n    // is the rest window allowed?\n    bool mRestAllowed;\n\n    void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings\n\n    void updateMap();\n\n    int mShowOwned;\n\n    ToUTF8::FromType mEncoding;\n\n    std::string mVersionDescription;\n\n    bool mWindowVisible;\n\n    MWGui::TextColours mTextColours;\n\n    std::unique_ptr<KeyboardNavigation> mKeyboardNavigation;\n\n    SDLUtil::VideoWrapper* mVideoWrapper;\n\n    float mScalingFactor;\n\n    /**\n     * Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property.\n     * Supported syntax:\n     * #{GMSTName}: retrieves String value of the GMST called GMSTName\n     * #{setting=CATEGORY_NAME,SETTING_NAME}: retrieves String value of SETTING_NAME under category CATEGORY_NAME from settings.cfg\n     * #{sCell=CellID}: retrieves translated name of the given CellID (used only by some Morrowind localisations, in others cell ID is == cell name)\n     * #{fontcolour=FontColourName}: retrieves the value of the fallback setting \"FontColor_color_<FontColourName>\" from openmw.cfg,\n     *                              in the format \"r g b a\", float values in range 0-1. Useful for \"Colour\" and \"TextColour\" properties in skins.\n     * #{fontcolourhtml=FontColourName}: retrieves the value of the fallback setting \"FontColor_color_<FontColourName>\" from openmw.cfg,\n     *                              in the format \"#xxxxxx\" where x are hexadecimal numbers. Useful in an EditBox's caption to change the color of following text.\n     */\n    void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result);\n\n    void onCursorChange(const std::string& name);\n    void onKeyFocusChanged(MyGUI::Widget* widget);\n\n    // Key pressed while playing a video\n    void onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char);\n\n    void sizeVideo(int screenWidth, int screenHeight);\n\n    void onClipboardChanged(const std::string& _type, const std::string& _data);\n    void onClipboardRequested(const std::string& _type, std::string& _data);\n\n    void createTextures();\n    void createCursors();\n    void setMenuTransparency(float value);\n\n    void updatePinnedWindows();\n\n    void enableScene(bool enable);\n  };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwgui/windowpinnablebase.cpp",
    "content": "#include \"windowpinnablebase.hpp\"\n\n#include \"exposedwindow.hpp\"\n\nnamespace MWGui\n{\n    WindowPinnableBase::WindowPinnableBase(const std::string& parLayout)\n      : WindowBase(parLayout), mPinned(false)\n    {\n        Window* window = mMainWidget->castType<Window>();\n        mPinButton = window->getSkinWidget (\"Button\");\n\n        mPinButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonPressed);\n    }\n\n    void WindowPinnableBase::onPinButtonPressed(MyGUI::Widget* _sender, int left, int top, MyGUI::MouseButton id)\n    {\n        if (id != MyGUI::MouseButton::Left)\n            return;\n\n        mPinned = !mPinned;\n\n        if (mPinned)\n            mPinButton->changeWidgetSkin (\"PinDown\");\n        else\n            mPinButton->changeWidgetSkin (\"PinUp\");\n\n        onPinToggled();\n    }\n\n    void WindowPinnableBase::setPinned(bool pinned)\n    {\n        if (pinned != mPinned)\n            onPinButtonPressed(mPinButton, 0, 0, MyGUI::MouseButton::Left);\n    }\n\n    void WindowPinnableBase::setPinButtonVisible(bool visible)\n    {\n        mPinButton->setVisible(visible);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwgui/windowpinnablebase.hpp",
    "content": "#ifndef MWGUI_WINDOW_PINNABLE_BASE_H\n#define MWGUI_WINDOW_PINNABLE_BASE_H\n\n#include \"windowbase.hpp\"\n\nnamespace MWGui\n{\n    class WindowPinnableBase: public WindowBase\n    {\n    public:\n        WindowPinnableBase(const std::string& parLayout);\n        bool pinned() { return mPinned; }\n        void setPinned (bool pinned);\n        void setPinButtonVisible(bool visible);\n\n    private:\n        void onPinButtonPressed(MyGUI::Widget* _sender, int left, int top, MyGUI::MouseButton id);\n\n    protected:\n        virtual void onPinToggled() = 0;\n\n        MyGUI::Widget* mPinButton;\n        bool mPinned;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/actionmanager.cpp",
    "content": "#include \"actionmanager.hpp\"\n\n#include <MyGUI_InputManager.h>\n\n#include <SDL_keyboard.h>\n\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/GUIController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"actions.hpp\"\n#include \"bindingsmanager.hpp\"\n\nnamespace MWInput\n{\n    const float ZOOM_SCALE = 10.f; /// Used for scrolling camera in and out\n\n    ActionManager::ActionManager(BindingsManager* bindingsManager,\n            osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,\n            osg::ref_ptr<osgViewer::Viewer> viewer,\n            osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler)\n        : mBindingsManager(bindingsManager)\n        , mViewer(viewer)\n        , mScreenCaptureHandler(screenCaptureHandler)\n        , mScreenCaptureOperation(screenCaptureOperation)\n        , mAlwaysRunActive(Settings::Manager::getBool(\"always run\", \"Input\"))\n        , mSneaking(false)\n        , mAttemptJump(false)\n        , mOverencumberedMessageDelay(0.f)\n        , mPreviewPOVDelay(0.f)\n        , mTimeIdle(0.f)\n    {\n    }\n\n    void ActionManager::update(float dt, bool triedToMove)\n    {\n        // Disable movement in Gui mode\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode()\n            || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)\n        {\n            mAttemptJump = false;\n            return;\n        }\n\n        // Configure player movement according to keyboard input. Actual movement will\n        // be done in the physics system.\n        if (MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n        {\n            bool alwaysRunAllowed = false;\n\n            MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n\n            if (mBindingsManager->actionIsActive(A_MoveLeft) != mBindingsManager->actionIsActive(A_MoveRight))\n            {\n                alwaysRunAllowed = true;\n                triedToMove = true;\n                player.setLeftRight(mBindingsManager->actionIsActive(A_MoveRight) ? 1 : -1);\n            }\n\n            if (mBindingsManager->actionIsActive(A_MoveForward) != mBindingsManager->actionIsActive(A_MoveBackward))\n            {\n                alwaysRunAllowed = true;\n                triedToMove = true;\n                player.setAutoMove (false);\n                player.setForwardBackward(mBindingsManager->actionIsActive(A_MoveForward) ? 1 : -1);\n            }\n\n            if (player.getAutoMove())\n            {\n                alwaysRunAllowed = true;\n                triedToMove = true;\n                player.setForwardBackward (1);\n            }\n\n            if (mAttemptJump && MWBase::Environment::get().getInputManager()->getControlSwitch(\"playerjumping\"))\n            {\n                player.setUpDown(1);\n                triedToMove = true;\n                mOverencumberedMessageDelay = 0.f;\n            }\n\n            // if player tried to start moving, but can't (due to being overencumbered), display a notification.\n            if (triedToMove)\n            {\n                MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n                mOverencumberedMessageDelay -= dt;\n                if (playerPtr.getClass().getEncumbrance(playerPtr) > playerPtr.getClass().getCapacity(playerPtr))\n                {\n                    player.setAutoMove (false);\n                    if (mOverencumberedMessageDelay <= 0)\n                    {\n                        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sNotifyMessage59}\");\n                        mOverencumberedMessageDelay = 1.0;\n                    }\n                }\n            }\n\n            if (MWBase::Environment::get().getInputManager()->getControlSwitch(\"playerviewswitch\"))\n            {\n                const float switchLimit = 0.25;\n                MWBase::World* world = MWBase::Environment::get().getWorld();\n                if (mBindingsManager->actionIsActive(A_TogglePOV))\n                {\n                    if (world->isFirstPerson() ? mPreviewPOVDelay > switchLimit : mPreviewPOVDelay == 0)\n                        world->togglePreviewMode(true);\n                    mPreviewPOVDelay += dt;\n                }\n                else\n                {\n                    //disable preview mode\n                    if (mPreviewPOVDelay > 0)\n                        world->togglePreviewMode(false);\n                    if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= switchLimit)\n                        world->togglePOV();\n                    mPreviewPOVDelay = 0.f;\n                }\n            }\n\n            if (triedToMove)\n                MWBase::Environment::get().getInputManager()->resetIdleTime();\n\n            static const bool isToggleSneak = Settings::Manager::getBool(\"toggle sneak\", \"Input\");\n            if (!isToggleSneak)\n            {\n                if(!MWBase::Environment::get().getInputManager()->joystickLastUsed())\n                    player.setSneak(mBindingsManager->actionIsActive(A_Sneak));\n            }\n\n            float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight);\n            float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward);\n            bool isRunning = osg::Vec2f(xAxis * 2 - 1, yAxis * 2 - 1).length2() > 0.25f;\n            if ((mAlwaysRunActive && alwaysRunAllowed) || isRunning)\n                player.setRunState(!mBindingsManager->actionIsActive(A_Run));\n            else\n                player.setRunState(mBindingsManager->actionIsActive(A_Run));\n        }\n\n        if (mBindingsManager->actionIsActive(A_MoveForward) ||\n            mBindingsManager->actionIsActive(A_MoveBackward) ||\n            mBindingsManager->actionIsActive(A_MoveLeft) ||\n            mBindingsManager->actionIsActive(A_MoveRight) ||\n            mBindingsManager->actionIsActive(A_Jump) ||\n            mBindingsManager->actionIsActive(A_Sneak) ||\n            mBindingsManager->actionIsActive(A_TogglePOV) ||\n            mBindingsManager->actionIsActive(A_ZoomIn) ||\n            mBindingsManager->actionIsActive(A_ZoomOut))\n        {\n            resetIdleTime();\n        }\n        else\n        {\n            updateIdleTime(dt);\n        }\n\n        mAttemptJump = false;\n    }\n    \n    bool ActionManager::isPreviewModeEnabled()\n    {\n        return MWBase::Environment::get().getWorld()->isPreviewModeEnabled();\n    }\n\n    void ActionManager::resetIdleTime()\n    {\n        if (mTimeIdle < 0)\n            MWBase::Environment::get().getWorld()->toggleVanityMode(false);\n        mTimeIdle = 0.f;\n    }\n\n    void ActionManager::updateIdleTime(float dt)\n    {\n        static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"fVanityDelay\")->mValue.getFloat();\n        if (mTimeIdle >= 0.f)\n            mTimeIdle += dt;\n        if (mTimeIdle > vanityDelay)\n        {\n            MWBase::Environment::get().getWorld()->toggleVanityMode(true);\n            mTimeIdle = -1.f;\n        }\n    }\n\n    void ActionManager::executeAction(int action)\n    {\n        auto* inputManager = MWBase::Environment::get().getInputManager();\n        auto* windowManager = MWBase::Environment::get().getWindowManager();\n        // trigger action activated\n        switch (action)\n        {\n        case A_GameMenu:\n            toggleMainMenu ();\n            break;\n        case A_Screenshot:\n            screenshot();\n            break;\n        case A_Inventory:\n            toggleInventory ();\n            break;\n        case A_Console:\n            toggleConsole ();\n            break;\n        case A_Activate:\n            inputManager->resetIdleTime();\n            activate();\n            break;\n        case A_MoveLeft:\n        case A_MoveRight:\n        case A_MoveForward:\n        case A_MoveBackward:\n            handleGuiArrowKey(action);\n            break;\n        case A_Journal:\n            toggleJournal();\n            break;\n        case A_AutoMove:\n            toggleAutoMove();\n            break;\n        case A_AlwaysRun:\n            toggleWalking();\n            break;\n        case A_ToggleWeapon:\n            toggleWeapon();\n            break;\n        case A_Rest:\n            rest();\n            break;\n        case A_ToggleSpell:\n            toggleSpell();\n            break;\n        case A_QuickKey1:\n            quickKey(1);\n            break;\n        case A_QuickKey2:\n            quickKey(2);\n            break;\n        case A_QuickKey3:\n            quickKey(3);\n            break;\n        case A_QuickKey4:\n            quickKey(4);\n            break;\n        case A_QuickKey5:\n            quickKey(5);\n            break;\n        case A_QuickKey6:\n            quickKey(6);\n            break;\n        case A_QuickKey7:\n            quickKey(7);\n            break;\n        case A_QuickKey8:\n            quickKey(8);\n            break;\n        case A_QuickKey9:\n            quickKey(9);\n            break;\n        case A_QuickKey10:\n            quickKey(10);\n            break;\n        case A_QuickKeysMenu:\n            showQuickKeysMenu();\n            break;\n        case A_ToggleHUD:\n            windowManager->toggleHud();\n            break;\n        case A_ToggleDebug:\n            windowManager->toggleDebugWindow();\n            break;\n        case A_ZoomIn:\n            if (inputManager->getControlSwitch(\"playerviewswitch\") && inputManager->getControlSwitch(\"playercontrols\") && !windowManager->isGuiMode())\n                MWBase::Environment::get().getWorld()->adjustCameraDistance(-ZOOM_SCALE);\n            break;\n        case A_ZoomOut:\n            if (inputManager->getControlSwitch(\"playerviewswitch\") && inputManager->getControlSwitch(\"playercontrols\") && !windowManager->isGuiMode())\n                MWBase::Environment::get().getWorld()->adjustCameraDistance(ZOOM_SCALE);\n            break;\n        case A_QuickSave:\n            quickSave();\n            break;\n        case A_QuickLoad:\n            quickLoad();\n            break;\n        case A_CycleSpellLeft:\n            if (checkAllowedToUseItems() && windowManager->isAllowed(MWGui::GW_Magic))\n                MWBase::Environment::get().getWindowManager()->cycleSpell(false);\n            break;\n        case A_CycleSpellRight:\n            if (checkAllowedToUseItems() && windowManager->isAllowed(MWGui::GW_Magic))\n                MWBase::Environment::get().getWindowManager()->cycleSpell(true);\n            break;\n        case A_CycleWeaponLeft:\n            if (checkAllowedToUseItems() && windowManager->isAllowed(MWGui::GW_Inventory))\n                MWBase::Environment::get().getWindowManager()->cycleWeapon(false);\n            break;\n        case A_CycleWeaponRight:\n            if (checkAllowedToUseItems() && windowManager->isAllowed(MWGui::GW_Inventory))\n                MWBase::Environment::get().getWindowManager()->cycleWeapon(true);\n            break;\n        case A_Sneak:\n            static const bool isToggleSneak = Settings::Manager::getBool(\"toggle sneak\", \"Input\");\n            if (isToggleSneak)\n            {\n                toggleSneaking();\n            }\n            break;\n        }\n    }\n\n    bool ActionManager::checkAllowedToUseItems() const\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        if (player.getClass().getNpcStats(player).isWerewolf())\n        {\n            // Cannot use items or spells while in werewolf form\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sWerewolfRefusal}\");\n            return false;\n        }\n        return true;\n    }\n\n    void ActionManager::screenshot()\n    {\n        const std::string& settingStr = Settings::Manager::getString(\"screenshot type\", \"Video\");\n        bool regularScreenshot = settingStr.size() == 0 || settingStr.compare(\"regular\") == 0;\n\n        if (regularScreenshot)\n        {\n            mScreenCaptureHandler->setFramesToCapture(1);\n            mScreenCaptureHandler->captureNextFrame(*mViewer);\n        }\n        else\n        {\n            osg::ref_ptr<osg::Image> screenshot (new osg::Image);\n\n            if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get()))\n            {\n                (*mScreenCaptureOperation) (*(screenshot.get()), 0);\n                // FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason\n            }\n        }\n    }\n\n    void ActionManager::toggleMainMenu()\n    {\n        /*\n            Start  of tes3mp addition\n\n            Don't allow the main menu to be toggled while TES3MP listboxes are open\n        */\n        if (MWBase::Environment::get().getWindowManager()->getMode() == mwmp::GUIController::GM_TES3MP_ListBox)\n        {\n            return;\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        if (MyGUI::InputManager::getInstance().isModalAny())\n        {\n            MWBase::Environment::get().getWindowManager()->exitCurrentModal();\n            return;\n        }\n\n        if (MWBase::Environment::get().getWindowManager()->isConsoleMode())\n        {\n            MWBase::Environment::get().getWindowManager()->toggleConsole();\n            return;\n        }\n\n        if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu\n        {\n            MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);\n        }\n        else //Close current GUI\n        {\n            MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n        }\n    }\n\n    void ActionManager::toggleSpell()\n    {\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return;\n\n        // Not allowed before the magic window is accessible\n        if (!MWBase::Environment::get().getInputManager()->getControlSwitch(\"playermagic\") || !MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n            return;\n\n        if (!checkAllowedToUseItems())\n            return;\n\n        // Not allowed if no spell selected\n        MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n        MWWorld::InventoryStore& inventory = player.getPlayer().getClass().getInventoryStore(player.getPlayer());\n        if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty() &&\n            inventory.getSelectedEnchantItem() == inventory.end())\n            return;\n\n        if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player.getPlayer()))\n            return;\n\n        MWMechanics::DrawState_ state = player.getDrawState();\n        if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing)\n            player.setDrawState(MWMechanics::DrawState_Spell);\n        else\n            player.setDrawState(MWMechanics::DrawState_Nothing);\n    }\n\n    void ActionManager::quickLoad()\n    {\n        if (!MyGUI::InputManager::getInstance().isModalAny())\n            MWBase::Environment::get().getStateManager()->quickLoad();\n    }\n\n    void ActionManager::quickSave()\n    {\n        if (!MyGUI::InputManager::getInstance().isModalAny())\n            MWBase::Environment::get().getStateManager()->quickSave();\n    }\n\n    void ActionManager::toggleWeapon()\n    {\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return;\n\n        // Not allowed before the inventory window is accessible\n        if (!MWBase::Environment::get().getInputManager()->getControlSwitch(\"playerfighting\") || !MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n            return;\n\n        MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n        // We want to interrupt animation only if attack is preparing, but still is not triggered\n        // Otherwise we will get a \"speedshooting\" exploit, when player can skip reload animation by hitting \"Toggle Weapon\" key twice\n        if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(player.getPlayer()))\n            player.setAttackingOrSpell(false);\n        else if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player.getPlayer()))\n            return;\n\n        MWMechanics::DrawState_ state = player.getDrawState();\n        if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)\n            player.setDrawState(MWMechanics::DrawState_Weapon);\n        else\n            player.setDrawState(MWMechanics::DrawState_Nothing);\n    }\n\n    void ActionManager::rest()\n    {\n        if (!MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n            return;\n\n        if (!MWBase::Environment::get().getWindowManager()->getRestEnabled() || MWBase::Environment::get().getWindowManager()->isGuiMode())\n            return;\n\n        /*\n            Start of tes3mp addition\n\n            Ignore attempts to rest if the player has not logged in on the server yet\n\n            Set LocalPlayer's isUsingBed to be able to distinguish bed use from regular rest\n            menu use\n        */\n        if (!mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n            return;\n\n        mwmp::Main::get().getLocalPlayer()->isUsingBed = false;\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Rest); //Open rest GUI\n    }\n\n    void ActionManager::toggleInventory()\n    {\n        if (!MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n            return;\n\n        if (MyGUI::InputManager::getInstance().isModalAny())\n            return;\n\n        if (MWBase::Environment::get().getWindowManager()->isConsoleMode())\n            return;\n\n        /*\n            Start of tes3mp addition\n\n            Ignore attempts to open inventory if the player has not logged in on the server yet\n        */\n        if (!mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n            return;\n        /*\n            End of tes3mp addition\n        */\n\n        // Toggle between game mode and inventory mode\n        if(!MWBase::Environment::get().getWindowManager()->isGuiMode())\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Inventory);\n        else\n        {\n            MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();\n            if(mode == MWGui::GM_Inventory || mode == MWGui::GM_Container)\n                MWBase::Environment::get().getWindowManager()->popGuiMode();\n        }\n\n        // .. but don't touch any other mode, except container.\n    }\n\n    void ActionManager::toggleConsole()\n    {\n        if (MyGUI::InputManager::getInstance().isModalAny())\n            return;\n\n        /*\n            Start of tes3mp addition\n\n            If a player's console is disabled by the server, go no further\n        */\n        if (!mwmp::Main::get().getLocalPlayer()->consoleAllowed)\n            return;\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getWindowManager()->toggleConsole();\n    }\n\n    void ActionManager::toggleJournal()\n    {\n        if (!MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n            return;\n        if (MyGUI::InputManager::getInstance ().isModalAny())\n            return;\n\n        if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal\n                && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu\n                && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings\n                && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())\n        {\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal);\n        }\n        else if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal))\n        {\n            MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal);\n        }\n    }\n\n    void ActionManager::quickKey (int index)\n    {\n        if (!MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\") || !MWBase::Environment::get().getInputManager()->getControlSwitch(\"playerfighting\") || !MWBase::Environment::get().getInputManager()->getControlSwitch(\"playermagic\"))\n            return;\n        if (!checkAllowedToUseItems())\n            return;\n\n        if (MWBase::Environment::get().getWorld()->getGlobalFloat (\"chargenstate\")!=-1)\n            return;\n\n        if (!MWBase::Environment::get().getWindowManager()->isGuiMode())\n            MWBase::Environment::get().getWindowManager()->activateQuickKey (index);\n    }\n\n    void ActionManager::showQuickKeysMenu()\n    {\n        if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()\n                && MWBase::Environment::get().getWorld()->getGlobalFloat (\"chargenstate\")==-1)\n        {\n            if (!checkAllowedToUseItems())\n                return;\n\n            MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu);\n        }\n        else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu)\n        {\n            while (MyGUI::InputManager::getInstance().isModalAny())\n            { //Handle any open Modal windows\n                MWBase::Environment::get().getWindowManager()->exitCurrentModal();\n            }\n            MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); //And handle the actual main window\n        }\n    }\n\n    void ActionManager::activate()\n    {\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n        {\n            bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();\n            if (!SDL_IsTextInputActive() && !mBindingsManager->isLeftOrRightButton(A_Activate, joystickUsed))\n                MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0, false);\n        }\n        else if (MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n        {\n            MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n            player.activate();\n        }\n    }\n\n    void ActionManager::toggleAutoMove()\n    {\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return;\n\n        if (MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n        {\n            MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n            player.setAutoMove (!player.getAutoMove());\n        }\n    }\n\n    void ActionManager::toggleWalking()\n    {\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode() || SDL_IsTextInputActive()) return;\n        mAlwaysRunActive = !mAlwaysRunActive;\n\n        Settings::Manager::setBool(\"always run\", \"Input\", mAlwaysRunActive);\n    }\n\n    void ActionManager::toggleSneaking()\n    {\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return;\n        if (!MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\")) return;\n        mSneaking = !mSneaking;\n        MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n        player.setSneak(mSneaking);\n    }\n\n    void ActionManager::handleGuiArrowKey(int action)\n    {\n        bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();\n        // This is currently keyboard-specific code\n        // TODO: see if GUI controls can be refactored into a single function\n        if (joystickUsed)\n            return;\n\n        if (SDL_IsTextInputActive())\n            return;\n\n        if (mBindingsManager->isLeftOrRightButton(action, joystickUsed))\n            return;\n\n        MyGUI::KeyCode key;\n        switch (action)\n        {\n        case A_MoveLeft:\n            key = MyGUI::KeyCode::ArrowLeft;\n            break;\n        case A_MoveRight:\n            key = MyGUI::KeyCode::ArrowRight;\n            break;\n        case A_MoveForward:\n            key = MyGUI::KeyCode::ArrowUp;\n            break;\n        case A_MoveBackward:\n        default:\n            key = MyGUI::KeyCode::ArrowDown;\n            break;\n        }\n\n        MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/actionmanager.hpp",
    "content": "#ifndef MWINPUT_ACTIONMANAGER_H\n#define MWINPUT_ACTIONMANAGER_H\n\n#include <osg/ref_ptr>\n#include <osgViewer/ViewerEventHandlers>\n\nnamespace osgViewer\n{\n    class Viewer;\n    class ScreenCaptureHandler;\n}\n\nnamespace MWInput\n{\n    class BindingsManager;\n\n    class ActionManager\n    {\n    public:\n\n        ActionManager(BindingsManager* bindingsManager,\n            osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,\n            osg::ref_ptr<osgViewer::Viewer> viewer,\n            osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler);\n\n        void update(float dt, bool triedToMove);\n\n        void executeAction(int action);\n\n        bool checkAllowedToUseItems() const;\n\n        void toggleMainMenu();\n        void toggleSpell();\n        void toggleWeapon();\n        void toggleInventory();\n        void toggleConsole();\n        void screenshot();\n        void toggleJournal();\n        void activate();\n        void toggleWalking();\n        void toggleSneaking();\n        void toggleAutoMove();\n        void rest();\n        void quickLoad();\n        void quickSave();\n\n        void quickKey (int index);\n        void showQuickKeysMenu();\n\n        void resetIdleTime();\n\n        bool isAlwaysRunActive() const { return mAlwaysRunActive; };\n        bool isSneaking() const { return mSneaking; };\n\n        void setAttemptJump(bool enabled) { mAttemptJump = enabled; }\n\n        bool isPreviewModeEnabled();\n\n    private:\n        void handleGuiArrowKey(int action);\n\n        void updateIdleTime(float dt);\n\n        BindingsManager* mBindingsManager;\n        osg::ref_ptr<osgViewer::Viewer> mViewer;\n        osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;\n        osgViewer::ScreenCaptureHandler::CaptureOperation* mScreenCaptureOperation;\n\n        bool mAlwaysRunActive;\n        bool mSneaking;\n        bool mAttemptJump;\n\n        float mOverencumberedMessageDelay;\n        float mPreviewPOVDelay;\n        float mTimeIdle;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/actions.hpp",
    "content": "#ifndef MWINPUT_ACTIONS_H\n#define MWINPUT_ACTIONS_H\n\nnamespace MWInput\n{\n    enum Actions\n    {\n        // please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files\n\n        A_GameMenu,\n\n        A_Unused,\n\n        A_Screenshot,               // Take a screenshot\n\n        A_Inventory,                // Toggle inventory screen\n\n        A_Console,                  // Toggle console screen\n\n        A_MoveLeft,                 // Move player left / right\n        A_MoveRight,\n        A_MoveForward,              // Forward / Backward\n        A_MoveBackward,\n\n        A_Activate,\n\n        A_Use,                      //Use weapon, spell, etc.\n        A_Jump,\n        A_AutoMove,                 //Toggle Auto-move forward\n        A_Rest,                     //Rest\n        A_Journal,                  //Journal\n        A_Weapon,                   //Draw/Sheath weapon\n        A_Spell,                    //Ready/Unready Casting\n        A_Run,                      //Run when held\n        A_CycleSpellLeft,           //cycling through spells\n        A_CycleSpellRight,\n        A_CycleWeaponLeft,          //Cycling through weapons\n        A_CycleWeaponRight,\n        A_ToggleSneak,              //Toggles Sneak\n        A_AlwaysRun,                //Toggle Walking/Running\n        A_Sneak,\n\n        A_QuickSave,\n        A_QuickLoad,\n        A_QuickMenu,\n        A_ToggleWeapon,\n        A_ToggleSpell,\n\n        A_TogglePOV,\n\n        A_QuickKey1,\n        A_QuickKey2,\n        A_QuickKey3,\n        A_QuickKey4,\n        A_QuickKey5,\n        A_QuickKey6,\n        A_QuickKey7,\n        A_QuickKey8,\n        A_QuickKey9,\n        A_QuickKey10,\n\n        A_QuickKeysMenu,\n\n        A_ToggleHUD,\n\n        A_ToggleDebug,\n\n        A_LookUpDown,               //Joystick look\n        A_LookLeftRight,\n        A_MoveForwardBackward,\n        A_MoveLeftRight,\n\n        A_ZoomIn,\n        A_ZoomOut,\n\n        A_Last                      // Marker for the last item\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/bindingsmanager.cpp",
    "content": "#include \"bindingsmanager.hpp\"\n\n#include <MyGUI_EditBox.h>\n\n#include <extern/oics/ICSChannelListener.h>\n#include <extern/oics/ICSInputControlSystem.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/GUIController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/player.hpp\"\n\n#include \"actions.hpp\"\n#include \"sdlmappings.hpp\"\n\nnamespace MWInput\n{\n    static const int sFakeDeviceId = 1; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers\n\n    void clearAllKeyBindings(ICS::InputControlSystem* inputBinder, ICS::Control* control)\n    {\n        // right now we don't really need multiple bindings for the same action, so remove all others first\n        if (inputBinder->getKeyBinding(control, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN)\n            inputBinder->removeKeyBinding(inputBinder->getKeyBinding(control, ICS::Control::INCREASE));\n        if (inputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS)\n            inputBinder->removeMouseButtonBinding(inputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE));\n        if (inputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED)\n            inputBinder->removeMouseWheelBinding(inputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE));\n    }\n\n    void clearAllControllerBindings(ICS::InputControlSystem* inputBinder, ICS::Control* control)\n    {\n        // right now we don't really need multiple bindings for the same action, so remove all others first\n        if (inputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN)\n            inputBinder->removeJoystickAxisBinding(sFakeDeviceId, inputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE));\n        if (inputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS)\n            inputBinder->removeJoystickButtonBinding(sFakeDeviceId, inputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE));\n    }\n\n    class InputControlSystem : public ICS::InputControlSystem\n    {\n    public:\n        InputControlSystem(const std::string& bindingsFile)\n            : ICS::InputControlSystem(bindingsFile, true, nullptr, nullptr, A_Last)\n        {\n        }\n    };\n\n    class BindingsListener :\n            public ICS::ChannelListener,\n            public ICS::DetectingBindingListener\n    {\n    public:\n        BindingsListener(ICS::InputControlSystem* inputBinder, BindingsManager* bindingsManager)\n            : mInputBinder(inputBinder)\n            , mBindingsManager(bindingsManager)\n            , mDetectingKeyboard(false)\n        {\n        }\n\n        virtual ~BindingsListener() = default;\n\n        void channelChanged(ICS::Channel* channel, float currentValue, float previousValue) override\n        {\n            int action = channel->getNumber();\n            mBindingsManager->actionValueChanged(action, currentValue, previousValue);\n        }\n\n        void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control\n            , SDL_Scancode key, ICS::Control::ControlChangingDirection direction) override\n        {\n            //Disallow binding escape key\n            if (key==SDL_SCANCODE_ESCAPE)\n            {\n                //Stop binding if esc pressed\n                mInputBinder->cancelDetectingBindingState();\n                MWBase::Environment::get().getWindowManager()->notifyInputActionBound();\n                return;\n            }\n\n            // Disallow binding reserved keys\n            if (key == SDL_SCANCODE_F3 || key == SDL_SCANCODE_F4 || key == SDL_SCANCODE_F10)\n                return;\n\n            #ifndef __APPLE__\n            // Disallow binding Windows/Meta keys\n            if (key == SDL_SCANCODE_LGUI || key == SDL_SCANCODE_RGUI)\n                return;\n            #endif\n\n            if (!mDetectingKeyboard)\n                return;\n\n            clearAllKeyBindings(mInputBinder, control);\n            control->setInitialValue(0.0f);\n            ICS::DetectingBindingListener::keyBindingDetected(ICS, control, key, direction);\n            MWBase::Environment::get().getWindowManager()->notifyInputActionBound();\n        }\n\n        void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control\n            , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction) override\n        {\n            // we don't want mouse movement bindings\n            return;\n        }\n\n        void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control\n            , unsigned int button, ICS::Control::ControlChangingDirection direction) override\n        {\n            if (!mDetectingKeyboard)\n                return;\n            clearAllKeyBindings(mInputBinder, control);\n            control->setInitialValue(0.0f);\n            ICS::DetectingBindingListener::mouseButtonBindingDetected(ICS, control, button, direction);\n            MWBase::Environment::get().getWindowManager()->notifyInputActionBound();\n        }\n\n        void mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control\n            , ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction) override\n        {\n            if (!mDetectingKeyboard)\n                return;\n            clearAllKeyBindings(mInputBinder, control);\n            control->setInitialValue(0.0f);\n            ICS::DetectingBindingListener::mouseWheelBindingDetected(ICS, control, click, direction);\n            MWBase::Environment::get().getWindowManager()->notifyInputActionBound();\n        }\n\n        void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control\n            , int axis, ICS::Control::ControlChangingDirection direction) override\n        {\n            //only allow binding to the trigers\n            if (axis != SDL_CONTROLLER_AXIS_TRIGGERLEFT && axis != SDL_CONTROLLER_AXIS_TRIGGERRIGHT)\n                return;\n            if (mDetectingKeyboard)\n                return;\n\n            clearAllControllerBindings(mInputBinder, control);\n            control->setValue(0.5f); //axis bindings must start at 0.5\n            control->setInitialValue(0.5f);\n            ICS::DetectingBindingListener::joystickAxisBindingDetected(ICS, deviceID, control, axis, direction);\n            MWBase::Environment::get().getWindowManager()->notifyInputActionBound();\n        }\n\n        void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control\n            , unsigned int button, ICS::Control::ControlChangingDirection direction) override\n        {\n            if (mDetectingKeyboard)\n                return;\n            clearAllControllerBindings(mInputBinder,control);\n            control->setInitialValue(0.0f);\n            ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, deviceID, control, button, direction);\n            MWBase::Environment::get().getWindowManager()->notifyInputActionBound();\n        }\n\n        void setDetectingKeyboard(bool detecting)\n        {\n            mDetectingKeyboard = detecting;\n        }\n\n    private:\n        ICS::InputControlSystem* mInputBinder;\n        BindingsManager* mBindingsManager;\n        bool mDetectingKeyboard;\n    };\n\n    BindingsManager::BindingsManager(const std::string& userFile, bool userFileExists)\n        : mUserFile(userFile)\n        , mDragDrop(false)\n    {\n        std::string file = userFileExists ? userFile : \"\";\n        mInputBinder = std::make_unique<InputControlSystem>(file);\n        mListener = std::make_unique<BindingsListener>(mInputBinder.get(), this);\n        mInputBinder->setDetectingBindingListener(mListener.get());\n\n        loadKeyDefaults();\n        loadControllerDefaults();\n\n        for (int i = 0; i < A_Last; ++i)\n        {\n            mInputBinder->getChannel(i)->addListener(mListener.get());\n        }\n    }\n\n    void BindingsManager::setDragDrop(bool dragDrop)\n    {\n        mDragDrop = dragDrop;\n    }\n\n    BindingsManager::~BindingsManager()\n    {\n        mInputBinder->save(mUserFile);\n    }\n\n    void BindingsManager::update(float dt)\n    {\n        // update values of channels (as a result of pressed keys)\n        mInputBinder->update(dt);\n    }\n\n    bool BindingsManager::isLeftOrRightButton(int action, bool joystick) const\n    {\n        int mouseBinding = mInputBinder->getMouseButtonBinding(mInputBinder->getControl(action), ICS::Control::INCREASE);\n        if (mouseBinding != ICS_MAX_DEVICE_BUTTONS)\n            return true;\n        int buttonBinding = mInputBinder->getJoystickButtonBinding(mInputBinder->getControl(action), sFakeDeviceId, ICS::Control::INCREASE);\n        if (joystick && (buttonBinding == 0 || buttonBinding == 1))\n            return true;\n        return false;\n    }\n\n    void BindingsManager::setPlayerControlsEnabled(bool enabled)\n    {\n        int playerChannels[] = {A_AutoMove, A_AlwaysRun, A_ToggleWeapon,\n                                A_ToggleSpell, A_Rest, A_QuickKey1, A_QuickKey2,\n                                A_QuickKey3, A_QuickKey4, A_QuickKey5, A_QuickKey6,\n                                A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10,\n                                A_Use, A_Journal};\n\n        for(int pc : playerChannels)\n        {\n            mInputBinder->getChannel(pc)->setEnabled(enabled);\n        }\n    }\n\n    void BindingsManager::setJoystickDeadZone(float deadZone)\n    {\n        mInputBinder->setJoystickDeadZone(deadZone);\n    }\n\n    float BindingsManager::getActionValue (int id) const\n    {\n        return mInputBinder->getChannel(id)->getValue();\n    }\n\n    bool BindingsManager::actionIsActive (int id) const\n    {\n        return getActionValue(id) == 1.0;\n    }\n\n    void BindingsManager::loadKeyDefaults (bool force)\n    {\n        // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid\n        // across different versions of OpenMW (in the case where another input action is added)\n        std::map<int, SDL_Scancode> defaultKeyBindings;\n\n        //Gets the Keyvalue from the Scancode; gives the button in the same place reguardless of keyboard format\n        defaultKeyBindings[A_Activate] = SDL_SCANCODE_SPACE;\n        defaultKeyBindings[A_MoveBackward] = SDL_SCANCODE_S;\n        defaultKeyBindings[A_MoveForward] = SDL_SCANCODE_W;\n        defaultKeyBindings[A_MoveLeft] = SDL_SCANCODE_A;\n        defaultKeyBindings[A_MoveRight] = SDL_SCANCODE_D;\n        defaultKeyBindings[A_ToggleWeapon] = SDL_SCANCODE_F;\n        defaultKeyBindings[A_ToggleSpell] = SDL_SCANCODE_R;\n        defaultKeyBindings[A_CycleSpellLeft] = SDL_SCANCODE_MINUS;\n        defaultKeyBindings[A_CycleSpellRight] = SDL_SCANCODE_EQUALS;\n        defaultKeyBindings[A_CycleWeaponLeft] = SDL_SCANCODE_LEFTBRACKET;\n        defaultKeyBindings[A_CycleWeaponRight] = SDL_SCANCODE_RIGHTBRACKET;\n\n        defaultKeyBindings[A_QuickKeysMenu] = SDL_SCANCODE_F1;\n        defaultKeyBindings[A_Console] = SDL_SCANCODE_GRAVE;\n        defaultKeyBindings[A_Run] = SDL_SCANCODE_LSHIFT;\n        defaultKeyBindings[A_Sneak] = SDL_SCANCODE_LCTRL;\n        defaultKeyBindings[A_AutoMove] = SDL_SCANCODE_Q;\n        defaultKeyBindings[A_Jump] = SDL_SCANCODE_E;\n        defaultKeyBindings[A_Journal] = SDL_SCANCODE_J;\n        defaultKeyBindings[A_Rest] = SDL_SCANCODE_T;\n        defaultKeyBindings[A_GameMenu] = SDL_SCANCODE_ESCAPE;\n        defaultKeyBindings[A_TogglePOV] = SDL_SCANCODE_TAB;\n        defaultKeyBindings[A_QuickKey1] = SDL_SCANCODE_1;\n        defaultKeyBindings[A_QuickKey2] = SDL_SCANCODE_2;\n        defaultKeyBindings[A_QuickKey3] = SDL_SCANCODE_3;\n        defaultKeyBindings[A_QuickKey4] = SDL_SCANCODE_4;\n        defaultKeyBindings[A_QuickKey5] = SDL_SCANCODE_5;\n        defaultKeyBindings[A_QuickKey6] = SDL_SCANCODE_6;\n        defaultKeyBindings[A_QuickKey7] = SDL_SCANCODE_7;\n        defaultKeyBindings[A_QuickKey8] = SDL_SCANCODE_8;\n        defaultKeyBindings[A_QuickKey9] = SDL_SCANCODE_9;\n        defaultKeyBindings[A_QuickKey10] = SDL_SCANCODE_0;\n        defaultKeyBindings[A_Screenshot] = SDL_SCANCODE_F12;\n        defaultKeyBindings[A_ToggleHUD] = SDL_SCANCODE_F11;\n        defaultKeyBindings[A_ToggleDebug] = SDL_SCANCODE_F10;\n        defaultKeyBindings[A_AlwaysRun] = SDL_SCANCODE_CAPSLOCK;\n        defaultKeyBindings[A_QuickSave] = SDL_SCANCODE_F5;\n        defaultKeyBindings[A_QuickLoad] = SDL_SCANCODE_F9;\n\n        std::map<int, int> defaultMouseButtonBindings;\n        defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT;\n        defaultMouseButtonBindings[A_Use] = SDL_BUTTON_LEFT;\n\n        std::map<int, ICS::InputControlSystem::MouseWheelClick> defaultMouseWheelBindings;\n        defaultMouseWheelBindings[A_ZoomIn] = ICS::InputControlSystem::MouseWheelClick::UP;\n        defaultMouseWheelBindings[A_ZoomOut] = ICS::InputControlSystem::MouseWheelClick::DOWN;\n\n        for (int i = 0; i < A_Last; ++i)\n        {\n            ICS::Control* control;\n            bool controlExists = mInputBinder->getChannel(i)->getControlsCount() != 0;\n            if (!controlExists)\n            {\n                control = new ICS::Control(std::to_string(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX);\n                mInputBinder->addControl(control);\n                control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT);\n            }\n            else\n            {\n                control = mInputBinder->getChannel(i)->getAttachedControls().front().control;\n            }\n\n            if (!controlExists || force ||\n                    (mInputBinder->getKeyBinding(control, ICS::Control::INCREASE) == SDL_SCANCODE_UNKNOWN\n                      && mInputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS\n                      && mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) == ICS::InputControlSystem::MouseWheelClick::UNASSIGNED))\n            {\n                clearAllKeyBindings(mInputBinder.get(), control);\n\n                if (defaultKeyBindings.find(i) != defaultKeyBindings.end()\n                        && (force || !mInputBinder->isKeyBound(defaultKeyBindings[i])))\n                {\n                    control->setInitialValue(0.0f);\n                    mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE);\n                }\n                else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()\n                         && (force || !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i])))\n                {\n                    control->setInitialValue(0.0f);\n                    mInputBinder->addMouseButtonBinding(control, defaultMouseButtonBindings[i], ICS::Control::INCREASE);\n                }\n                else if (defaultMouseWheelBindings.find(i) != defaultMouseWheelBindings.end()\n                        && (force || !mInputBinder->isMouseWheelBound(defaultMouseWheelBindings[i])))\n                {\n                    control->setInitialValue(0.f);\n                    mInputBinder->addMouseWheelBinding(control, defaultMouseWheelBindings[i], ICS::Control::INCREASE);\n                }\n\n                if (i == A_LookLeftRight && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_4) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_6))\n                {\n                    mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_6, ICS::Control::INCREASE);\n                    mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_4, ICS::Control::DECREASE);\n                }\n                if (i == A_LookUpDown && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_8) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_2))\n                {\n                    mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_2, ICS::Control::INCREASE);\n                    mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_8, ICS::Control::DECREASE);\n                }\n            }\n        }\n    }\n\n    void BindingsManager::loadControllerDefaults(bool force)\n    {\n        // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid\n        // across different versions of OpenMW (in the case where another input action is added)\n        std::map<int, int> defaultButtonBindings;\n\n        defaultButtonBindings[A_Activate] = SDL_CONTROLLER_BUTTON_A;\n        defaultButtonBindings[A_ToggleWeapon] = SDL_CONTROLLER_BUTTON_X;\n        defaultButtonBindings[A_ToggleSpell] = SDL_CONTROLLER_BUTTON_Y;\n        //defaultButtonBindings[A_QuickButtonsMenu] = SDL_GetButtonFromScancode(SDL_SCANCODE_F1); // Need to implement, should be ToggleSpell(5) AND Wait(9)\n        defaultButtonBindings[A_Sneak] = SDL_CONTROLLER_BUTTON_LEFTSTICK;\n        defaultButtonBindings[A_Journal] = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;\n        defaultButtonBindings[A_Rest] = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;\n        defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK;\n        defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B;\n        defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START;\n        defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE;\n        defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP;\n        defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT;\n        defaultButtonBindings[A_MoveBackward] = SDL_CONTROLLER_BUTTON_DPAD_DOWN;\n        defaultButtonBindings[A_MoveRight] = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;\n\n        std::map<int, int> defaultAxisBindings;\n        defaultAxisBindings[A_MoveForwardBackward] = SDL_CONTROLLER_AXIS_LEFTY;\n        defaultAxisBindings[A_MoveLeftRight] = SDL_CONTROLLER_AXIS_LEFTX;\n        defaultAxisBindings[A_LookUpDown] = SDL_CONTROLLER_AXIS_RIGHTY;\n        defaultAxisBindings[A_LookLeftRight] = SDL_CONTROLLER_AXIS_RIGHTX;\n        defaultAxisBindings[A_Use] = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;\n        defaultAxisBindings[A_Jump] = SDL_CONTROLLER_AXIS_TRIGGERLEFT;\n\n        for (int i = 0; i < A_Last; i++)\n        {\n            ICS::Control* control;\n            bool controlExists = mInputBinder->getChannel(i)->getControlsCount() != 0;\n            if (!controlExists)\n            {\n                float initial;\n                if (defaultAxisBindings.find(i) == defaultAxisBindings.end())\n                    initial = 0.0f;\n                else initial = 0.5f;\n                control = new ICS::Control(std::to_string(i), false, true, initial, ICS::ICS_MAX, ICS::ICS_MAX);\n                mInputBinder->addControl(control);\n                control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT);\n            }\n            else\n            {\n                control = mInputBinder->getChannel(i)->getAttachedControls().front().control;\n            }\n\n            if (!controlExists || force || (mInputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE) == ICS::InputControlSystem::UNASSIGNED &&\n                mInputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS))\n            {\n                clearAllControllerBindings(mInputBinder.get(), control);\n\n                if (defaultButtonBindings.find(i) != defaultButtonBindings.end()\n                        && (force || !mInputBinder->isJoystickButtonBound(sFakeDeviceId, defaultButtonBindings[i])))\n                {\n                    control->setInitialValue(0.0f);\n                    mInputBinder->addJoystickButtonBinding(control, sFakeDeviceId, defaultButtonBindings[i], ICS::Control::INCREASE);\n                }\n                else if (defaultAxisBindings.find(i) != defaultAxisBindings.end() && (force || !mInputBinder->isJoystickAxisBound(sFakeDeviceId, defaultAxisBindings[i])))\n                {\n                    control->setValue(0.5f);\n                    control->setInitialValue(0.5f);\n                    mInputBinder->addJoystickAxisBinding(control, sFakeDeviceId, defaultAxisBindings[i], ICS::Control::INCREASE);\n                }\n            }\n        }\n    }\n\n    std::string BindingsManager::getActionDescription(int action)\n    {\n        switch (action)\n        {\n            case A_Screenshot:\n                return \"Screenshot\";\n            case A_ZoomIn:\n                return \"Zoom In\";\n            case A_ZoomOut:\n                return \"Zoom Out\";\n            case A_ToggleHUD:\n                return \"Toggle HUD\";\n            case A_Use:\n                return \"#{sUse}\";\n            case A_Activate:\n                return \"#{sActivate}\";\n            case A_MoveBackward:\n                return \"#{sBack}\";\n            case A_MoveForward:\n                return \"#{sForward}\";\n            case A_MoveLeft:\n                return \"#{sLeft}\";\n            case A_MoveRight:\n                return \"#{sRight}\";\n            case A_ToggleWeapon:\n                return \"#{sReady_Weapon}\";\n            case A_ToggleSpell:\n                return \"#{sReady_Magic}\";\n            case A_CycleSpellLeft:\n                return \"#{sPrevSpell}\";\n            case A_CycleSpellRight:\n                return \"#{sNextSpell}\";\n            case A_CycleWeaponLeft:\n                return \"#{sPrevWeapon}\";\n            case A_CycleWeaponRight:\n                return \"#{sNextWeapon}\";\n            case A_Console:\n                return \"#{sConsoleTitle}\";\n            case A_Run:\n                return \"#{sRun}\";\n            case A_Sneak:\n                return \"#{sCrouch_Sneak}\";\n            case A_AutoMove:\n                return \"#{sAuto_Run}\";\n            case A_Jump:\n                return \"#{sJump}\";\n            case A_Journal:\n                return \"#{sJournal}\";\n            case A_Rest:\n                return \"#{sRestKey}\";\n            case A_Inventory:\n                return \"#{sInventory}\";\n            case A_TogglePOV:\n                return \"#{sTogglePOVCmd}\";\n            case A_QuickKeysMenu:\n                return \"#{sQuickMenu}\";\n            case A_QuickKey1:\n                return \"#{sQuick1Cmd}\";\n            case A_QuickKey2:\n                return \"#{sQuick2Cmd}\";\n            case A_QuickKey3:\n                return \"#{sQuick3Cmd}\";\n            case A_QuickKey4:\n                return \"#{sQuick4Cmd}\";\n            case A_QuickKey5:\n                return \"#{sQuick5Cmd}\";\n            case A_QuickKey6:\n                return \"#{sQuick6Cmd}\";\n            case A_QuickKey7:\n                return \"#{sQuick7Cmd}\";\n            case A_QuickKey8:\n                return \"#{sQuick8Cmd}\";\n            case A_QuickKey9:\n                return \"#{sQuick9Cmd}\";\n            case A_QuickKey10:\n                return \"#{sQuick10Cmd}\";\n            case A_AlwaysRun:\n                return \"#{sAlways_Run}\";\n            case A_QuickSave:\n                return \"#{sQuickSaveCmd}\";\n            case A_QuickLoad:\n                return \"#{sQuickLoadCmd}\";\n            default:\n                return std::string(); // not configurable\n        }\n    }\n\n    std::string BindingsManager::getActionKeyBindingName(int action)\n    {\n        if (mInputBinder->getChannel(action)->getControlsCount() == 0)\n            return \"#{sNone}\";\n\n        ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control;\n\n        SDL_Scancode key = mInputBinder->getKeyBinding(c, ICS::Control::INCREASE);\n        unsigned int mouse = mInputBinder->getMouseButtonBinding(c, ICS::Control::INCREASE);\n        ICS::InputControlSystem::MouseWheelClick wheel = mInputBinder->getMouseWheelBinding(c, ICS::Control::INCREASE);\n        if (key != SDL_SCANCODE_UNKNOWN)\n            return MyGUI::TextIterator::toTagsString(mInputBinder->scancodeToString(key));\n        else if (mouse != ICS_MAX_DEVICE_BUTTONS)\n            return \"#{sMouse} \" + std::to_string(mouse);\n        else if (wheel != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED)\n            switch (wheel)\n            {\n                case ICS::InputControlSystem::MouseWheelClick::UP:\n                    return \"Mouse Wheel Up\";\n                case ICS::InputControlSystem::MouseWheelClick::DOWN:\n                    return \"Mouse Wheel Down\";\n                case ICS::InputControlSystem::MouseWheelClick::RIGHT:\n                    return \"Mouse Wheel Right\";\n                case ICS::InputControlSystem::MouseWheelClick::LEFT:\n                    return \"Mouse Wheel Left\";\n                default:\n                    return \"#{sNone}\";\n            }\n        else\n            return \"#{sNone}\";\n    }\n\n    std::string BindingsManager::getActionControllerBindingName(int action)\n    {\n        if (mInputBinder->getChannel(action)->getControlsCount() == 0)\n            return \"#{sNone}\";\n\n        ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control;\n\n        if (mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE) != ICS::InputControlSystem::UNASSIGNED)\n            return sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE));\n        else if (mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS)\n            return sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE));\n        else\n            return \"#{sNone}\";\n    }\n\n    std::vector<int> BindingsManager::getActionKeySorting()\n    {\n        static const std::vector<int> actions\n        {\n            A_MoveForward, A_MoveBackward, A_MoveLeft, A_MoveRight, A_TogglePOV, A_ZoomIn, A_ZoomOut,\n            A_Run, A_AlwaysRun, A_Sneak, A_Activate, A_Use, A_ToggleWeapon, A_ToggleSpell,\n            A_CycleSpellLeft, A_CycleSpellRight, A_CycleWeaponLeft, A_CycleWeaponRight, A_AutoMove,\n            A_Jump, A_Inventory, A_Journal, A_Rest, A_Console, A_QuickSave, A_QuickLoad,\n            A_ToggleHUD, A_Screenshot, A_QuickKeysMenu, A_QuickKey1, A_QuickKey2, A_QuickKey3,\n            A_QuickKey4, A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10\n        };\n\n        return actions;\n    }\n    std::vector<int> BindingsManager::getActionControllerSorting()\n    {\n        static const std::vector<int> actions\n        {\n            A_TogglePOV, A_ZoomIn, A_ZoomOut, A_Sneak, A_Activate, A_Use, A_ToggleWeapon, A_ToggleSpell,\n            A_AutoMove, A_Jump, A_Inventory, A_Journal, A_Rest, A_QuickSave, A_QuickLoad, A_ToggleHUD,\n            A_Screenshot, A_QuickKeysMenu, A_QuickKey1, A_QuickKey2, A_QuickKey3, A_QuickKey4,\n            A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10,\n            A_CycleSpellLeft, A_CycleSpellRight, A_CycleWeaponLeft, A_CycleWeaponRight\n        };\n\n        return actions;\n    }\n\n    void BindingsManager::enableDetectingBindingMode(int action, bool keyboard)\n    {\n        mListener->setDetectingKeyboard(keyboard);\n        ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control;\n        mInputBinder->enableDetectingBindingState(c, ICS::Control::INCREASE);\n    }\n\n    bool BindingsManager::isDetectingBindingState() const\n    {\n        return mInputBinder->detectingBindingState();\n    }\n\n    void BindingsManager::mousePressed(const SDL_MouseButtonEvent &arg, int deviceID)\n    {\n        mInputBinder->mousePressed(arg, deviceID);\n    }\n\n    void BindingsManager::mouseReleased(const SDL_MouseButtonEvent &arg, int deviceID)\n    {\n        mInputBinder->mouseReleased(arg, deviceID);\n    }\n\n    void BindingsManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg)\n    {\n        mInputBinder->mouseMoved(arg);\n    }\n\n    void BindingsManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg)\n    {\n        mInputBinder->mouseWheelMoved(arg);\n    }\n\n    void BindingsManager::keyPressed(const SDL_KeyboardEvent &arg)\n    {\n        /*\n            Start of tes3mp addition\n\n            Pass the pressed key to the multiplayer-specific GUI controller\n        */\n        mwmp::Main::get().getGUIController()->pressedKey(arg.keysym.scancode);\n        /*\n            End of tes3mp addition\n        */\n\n        mInputBinder->keyPressed(arg);\n    }\n\n    void BindingsManager::keyReleased(const SDL_KeyboardEvent &arg)\n    {\n        mInputBinder->keyReleased(arg);\n    }\n\n    void BindingsManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg)\n    {\n        mInputBinder->controllerAdded(deviceID, arg);\n    }\n\n    void BindingsManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg)\n    {\n        mInputBinder->controllerRemoved(arg);\n    }\n\n    void BindingsManager::controllerButtonPressed(int deviceID, const SDL_ControllerButtonEvent &arg)\n    {\n        mInputBinder->buttonPressed(deviceID, arg);\n    }\n\n    void BindingsManager::controllerButtonReleased(int deviceID, const SDL_ControllerButtonEvent &arg)\n    {\n        mInputBinder->buttonReleased(deviceID, arg);\n    }\n\n    void BindingsManager::controllerAxisMoved(int deviceID, const SDL_ControllerAxisEvent &arg)\n    {\n        mInputBinder->axisMoved(deviceID, arg);\n    }\n\n    SDL_Scancode BindingsManager::getKeyBinding(int actionId)\n    {\n        return mInputBinder->getKeyBinding(mInputBinder->getControl(actionId), ICS::Control::INCREASE);\n    }\n\n    void BindingsManager::actionValueChanged(int action, float currentValue, float previousValue)\n    {\n        MWBase::Environment::get().getInputManager()->resetIdleTime();\n\n        if (mDragDrop && action != A_GameMenu && action != A_Inventory)\n            return;\n\n        if ((previousValue == 1 || previousValue == 0) && (currentValue==1 || currentValue==0))\n        {\n            //Is a normal button press, so don't change it at all\n        }\n        //Otherwise only trigger button presses as they go through specific points\n        else if (previousValue >= 0.8 && currentValue < 0.8)\n        {\n            currentValue = 0.0;\n            previousValue = 1.0;\n        }\n        else if (previousValue <= 0.6 && currentValue > 0.6)\n        {\n            currentValue = 1.0;\n            previousValue = 0.0;\n        }\n        else\n        {\n            //If it's not switching between those values, ignore the channel change.\n            return;\n        }\n\n        if (MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n        {\n            bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();\n            if (action == A_Use)\n            {\n                if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon))\n                    action = A_CycleWeaponRight;\n\n                else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell))\n                    action = A_CycleSpellRight;\n\n                else\n                {\n                    /*\n                        Start of tes3mp addition\n\n                        Prevent players from starting attacks while in the persuasion submenu in dialogue\n                    */\n                    if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue))\n                        return;\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n                    MWMechanics::DrawState_ state = player.getDrawState();\n                    player.setAttackingOrSpell(currentValue != 0 && state != MWMechanics::DrawState_Nothing);\n                }\n            }\n            else if (action == A_Jump)\n            {\n                if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon))\n                    action = A_CycleWeaponLeft;\n\n                else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell))\n                    action = A_CycleSpellLeft;\n\n                else\n                    MWBase::Environment::get().getInputManager()->setAttemptJump(currentValue == 1.0 && previousValue == 0.0);\n            }\n        }\n\n        if (currentValue == 1)\n            MWBase::Environment::get().getInputManager()->executeAction(action);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/bindingsmanager.hpp",
    "content": "#ifndef MWINPUT_MWBINDINGSMANAGER_H\n#define MWINPUT_MWBINDINGSMANAGER_H\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <components/sdlutil/events.hpp>\n\nnamespace MWInput\n{\n    class BindingsListener;\n    class InputControlSystem;\n\n    class BindingsManager\n    {\n    public:\n        BindingsManager(const std::string& userFile, bool userFileExists);\n\n        virtual ~BindingsManager();\n\n        std::string getActionDescription (int action);\n        std::string getActionKeyBindingName (int action);\n        std::string getActionControllerBindingName (int action);\n        std::vector<int> getActionKeySorting();\n        std::vector<int> getActionControllerSorting();\n\n        void enableDetectingBindingMode (int action, bool keyboard);\n        bool isDetectingBindingState() const;\n\n        void loadKeyDefaults(bool force = false);\n        void loadControllerDefaults(bool force = false);\n\n        void setDragDrop(bool dragDrop);\n\n        void update(float dt);\n\n        void setPlayerControlsEnabled(bool enabled);\n\n        void setJoystickDeadZone(float deadZone);\n\n        bool isLeftOrRightButton(int action, bool joystick) const;\n\n        bool actionIsActive(int id) const;\n        float getActionValue(int id) const;\n\n        void mousePressed(const SDL_MouseButtonEvent &evt, int deviceID);\n        void mouseReleased(const SDL_MouseButtonEvent &arg, int deviceID);\n        void mouseMoved(const SDLUtil::MouseMotionEvent &arg);\n        void mouseWheelMoved(const SDL_MouseWheelEvent &arg);\n\n        void keyPressed(const SDL_KeyboardEvent &arg);\n        void keyReleased(const SDL_KeyboardEvent &arg);\n\n        void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg);\n        void controllerRemoved(const SDL_ControllerDeviceEvent &arg);\n        void controllerButtonPressed(int deviceID, const SDL_ControllerButtonEvent &arg);\n        void controllerButtonReleased(int deviceID, const SDL_ControllerButtonEvent &arg);\n        void controllerAxisMoved(int deviceID, const SDL_ControllerAxisEvent &arg);\n\n        SDL_Scancode getKeyBinding(int actionId);\n\n        void actionValueChanged(int action, float currentValue, float previousValue);\n\n    private:\n        void setupSDLKeyMappings();\n\n        std::unique_ptr<InputControlSystem> mInputBinder;\n        std::unique_ptr<BindingsListener> mListener;\n\n        std::string mUserFile;\n\n        bool mDragDrop;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/controllermanager.cpp",
    "content": "#include \"controllermanager.hpp\"\n\n#include <MyGUI_Button.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_Widget.h>\n\n#include <components/debug/debuglog.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/player.hpp\"\n\n#include \"actions.hpp\"\n#include \"actionmanager.hpp\"\n#include \"bindingsmanager.hpp\"\n#include \"mousemanager.hpp\"\n#include \"sdlmappings.hpp\"\n\nnamespace MWInput\n{\n    ControllerManager::ControllerManager(BindingsManager* bindingsManager,\n            ActionManager* actionManager,\n            MouseManager* mouseManager,\n            const std::string& userControllerBindingsFile,\n            const std::string& controllerBindingsFile)\n        : mBindingsManager(bindingsManager)\n        , mActionManager(actionManager)\n        , mMouseManager(mouseManager)\n        , mJoystickEnabled (Settings::Manager::getBool(\"enable controller\", \"Input\"))\n        , mGamepadCursorSpeed(Settings::Manager::getFloat(\"gamepad cursor speed\", \"Input\"))\n        , mSneakToggleShortcutTimer(0.f)\n        , mGamepadZoom(0)\n        , mGamepadGuiCursorEnabled(true)\n        , mGuiCursorEnabled(true)\n        , mJoystickLastUsed(false)\n        , mSneakGamepadShortcut(false)\n        , mGamepadPreviewMode(false)\n    {\n        if (!controllerBindingsFile.empty())\n        {\n            SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str());\n        }\n\n        if (!userControllerBindingsFile.empty())\n        {\n            SDL_GameControllerAddMappingsFromFile(userControllerBindingsFile.c_str());\n        }\n\n        // Open all presently connected sticks\n        int numSticks = SDL_NumJoysticks();\n        for (int i = 0; i < numSticks; i++)\n        {\n            if (SDL_IsGameController(i))\n            {\n                SDL_ControllerDeviceEvent evt;\n                evt.which = i;\n                static const int fakeDeviceID = 1;\n                controllerAdded(fakeDeviceID, evt);\n                Log(Debug::Info) << \"Detected game controller: \" << SDL_GameControllerNameForIndex(i);\n            }\n            else\n            {\n                Log(Debug::Info) << \"Detected unusable controller: \" << SDL_JoystickNameForIndex(i);\n            }\n        }\n\n        float deadZoneRadius = Settings::Manager::getFloat(\"joystick dead zone\", \"Input\");\n        deadZoneRadius = std::min(std::max(deadZoneRadius, 0.0f), 0.5f);\n        mBindingsManager->setJoystickDeadZone(deadZoneRadius);\n    }\n\n    void ControllerManager::processChangedSettings(const Settings::CategorySettingVector& changed)\n    {\n        for (const auto& setting : changed)\n        {\n            if (setting.first == \"Input\" && setting.second == \"enable controller\")\n                mJoystickEnabled = Settings::Manager::getBool(\"enable controller\", \"Input\");\n        }\n    }\n\n    bool ControllerManager::update(float dt)\n    {\n        mGamepadPreviewMode = mActionManager->isPreviewModeEnabled();\n\n        if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled))\n        {\n            float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight) * 2.0f - 1.0f;\n            float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward) * 2.0f - 1.0f;\n            float zAxis = mBindingsManager->getActionValue(A_LookUpDown) * 2.0f - 1.0f;\n\n            xAxis *= (1.5f - mBindingsManager->getActionValue(A_Use));\n            yAxis *= (1.5f - mBindingsManager->getActionValue(A_Use));\n\n            // We keep track of our own mouse position, so that moving the mouse while in\n            // game mode does not move the position of the GUI cursor\n            float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();\n            float xMove = xAxis * dt * 1500.0f / uiScale * mGamepadCursorSpeed;\n            float yMove = yAxis * dt * 1500.0f / uiScale * mGamepadCursorSpeed;\n\n            float mouseWheelMove = -zAxis * dt * 1500.0f;\n            if (xMove != 0 || yMove != 0 || mouseWheelMove != 0)\n            {\n                mMouseManager->injectMouseMove(xMove, yMove, mouseWheelMove);\n                mMouseManager->warpMouse();\n                MWBase::Environment::get().getWindowManager()->setCursorActive(true);\n            }\n        }\n\n        // Disable movement in Gui mode\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode()\n            || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)\n        {\n            mGamepadZoom = 0;\n            return false;\n        }\n\n        MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n        bool triedToMove = false;\n\n        // Configure player movement according to controller input. Actual movement will\n        // be done in the physics system.\n        if (MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\"))\n        {\n            float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight);\n            float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward);\n            if (xAxis != 0.5)\n            {\n                triedToMove = true;\n                player.setLeftRight((xAxis - 0.5f) * 2);\n            }\n\n            if (yAxis != 0.5)\n            {\n                triedToMove = true;\n                player.setAutoMove (false);\n                player.setForwardBackward((0.5f - yAxis) * 2);\n            }\n\n            if (triedToMove)\n            {\n                mJoystickLastUsed = true;\n                MWBase::Environment::get().getInputManager()->resetIdleTime();\n            }\n\n            static const bool isToggleSneak = Settings::Manager::getBool(\"toggle sneak\", \"Input\");\n            if (!isToggleSneak)\n            {\n                if (mJoystickLastUsed)\n                {\n                    if (mBindingsManager->actionIsActive(A_Sneak))\n                    {\n                        if (mSneakToggleShortcutTimer) // New Sneak Button Press\n                        {\n                            if (mSneakToggleShortcutTimer <= 0.3f)\n                            {\n                                mSneakGamepadShortcut = true;\n                                mActionManager->toggleSneaking();\n                            }\n                            else\n                                mSneakGamepadShortcut = false;\n                        }\n\n                        if (!mActionManager->isSneaking())\n                            mActionManager->toggleSneaking();\n                        mSneakToggleShortcutTimer = 0.f;\n                    }\n                    else\n                    {\n                        if (!mSneakGamepadShortcut && mActionManager->isSneaking())\n                            mActionManager->toggleSneaking();\n                        if (mSneakToggleShortcutTimer <= 0.3f)\n                            mSneakToggleShortcutTimer += dt;\n                    }\n                }\n                else\n                    player.setSneak(mBindingsManager->actionIsActive(A_Sneak));\n            }\n        }\n\n        if (MWBase::Environment::get().getInputManager()->getControlSwitch(\"playerviewswitch\"))\n        {\n            if (!mBindingsManager->actionIsActive(A_TogglePOV))\n                mGamepadZoom = 0;\n\n            if (mGamepadZoom)\n                MWBase::Environment::get().getWorld()->adjustCameraDistance(-mGamepadZoom);\n        }\n\n        return triedToMove;\n    }\n\n    void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg)\n    {\n        if (!mJoystickEnabled || mBindingsManager->isDetectingBindingState())\n            return;\n\n        mJoystickLastUsed = true;\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n        {\n            if (gamepadToGuiControl(arg))\n                return;\n\n            if (mGamepadGuiCursorEnabled)\n            {\n                // Temporary mouse binding until keyboard controls are available:\n                if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.\n                {\n                    bool mousePressSuccess = mMouseManager->injectMouseButtonPress(SDL_BUTTON_LEFT);\n                    if (MyGUI::InputManager::getInstance().getMouseFocusWidget())\n                    {\n                        MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType<MyGUI::Button>(false);\n                        if (b && b->getEnabled())\n                            MWBase::Environment::get().getWindowManager()->playSound(\"Menu Click\");\n                    }\n\n                    mBindingsManager->setPlayerControlsEnabled(!mousePressSuccess);\n                }\n            }\n        }\n        else\n            mBindingsManager->setPlayerControlsEnabled(true);\n\n        //esc, to leave initial movie screen\n        auto kc = sdlKeyToMyGUI(SDLK_ESCAPE);\n        mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0));\n\n        if (!MWBase::Environment::get().getInputManager()->controlsDisabled())\n            mBindingsManager->controllerButtonPressed(deviceID, arg);\n    }\n\n    void ControllerManager::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg)\n    {\n        if (mBindingsManager->isDetectingBindingState())\n        {\n            mBindingsManager->controllerButtonReleased(deviceID, arg);\n            return;\n        }\n\n        if (!mJoystickEnabled || MWBase::Environment::get().getInputManager()->controlsDisabled())\n            return;\n\n        mJoystickLastUsed = true;\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n        {\n            if (mGamepadGuiCursorEnabled)\n            {\n                // Temporary mouse binding until keyboard controls are available:\n                if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.\n                {\n                    bool mousePressSuccess = mMouseManager->injectMouseButtonRelease(SDL_BUTTON_LEFT);\n                    if (mBindingsManager->isDetectingBindingState()) // If the player just triggered binding, don't let button release bind.\n                        return;\n\n                    mBindingsManager->setPlayerControlsEnabled(!mousePressSuccess);\n                }\n            }\n        }\n        else\n            mBindingsManager->setPlayerControlsEnabled(true);\n\n        //esc, to leave initial movie screen\n        auto kc = sdlKeyToMyGUI(SDLK_ESCAPE);\n        mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc));\n\n        mBindingsManager->controllerButtonReleased(deviceID, arg);\n    }\n\n    void ControllerManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg)\n    {\n        if (!mJoystickEnabled || MWBase::Environment::get().getInputManager()->controlsDisabled())\n            return;\n\n        mJoystickLastUsed = true;\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n        {\n            gamepadToGuiControl(arg);\n        }\n        else\n        {\n            if (mGamepadPreviewMode) // Preview Mode Gamepad Zooming\n            {\n                if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)\n                {\n                    mGamepadZoom = arg.value * 0.85f / 1000.f / 12.f;\n                    return; // Do not propagate event.\n                }\n                else if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)\n                {\n                    mGamepadZoom = -arg.value * 0.85f / 1000.f / 12.f;\n                    return; // Do not propagate event.\n                }\n            }\n        }\n        mBindingsManager->controllerAxisMoved(deviceID, arg);\n    }\n\n    void ControllerManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg)\n    {\n        mBindingsManager->controllerAdded(deviceID, arg);\n    }\n\n    void ControllerManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg)\n    {\n        mBindingsManager->controllerRemoved(arg);\n    }\n\n    bool ControllerManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg)\n    {\n        // Presumption of GUI mode will be removed in the future.\n        // MyGUI KeyCodes *may* change.\n        MyGUI::KeyCode key = MyGUI::KeyCode::None;\n        switch (arg.button)\n        {\n            case SDL_CONTROLLER_BUTTON_DPAD_UP:\n                key = MyGUI::KeyCode::ArrowUp;\n                break;\n            case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:\n                key = MyGUI::KeyCode::ArrowRight;\n                break;\n            case SDL_CONTROLLER_BUTTON_DPAD_DOWN:\n                key = MyGUI::KeyCode::ArrowDown;\n                break;\n            case SDL_CONTROLLER_BUTTON_DPAD_LEFT:\n                key = MyGUI::KeyCode::ArrowLeft;\n                break;\n            case SDL_CONTROLLER_BUTTON_A:\n                // If we are using the joystick as a GUI mouse, A must be handled via mouse.\n                if (mGamepadGuiCursorEnabled)\n                    return false;\n                key = MyGUI::KeyCode::Space;\n                break;\n            case SDL_CONTROLLER_BUTTON_B:\n                if (MyGUI::InputManager::getInstance().isModalAny())\n                    MWBase::Environment::get().getWindowManager()->exitCurrentModal();\n                else\n                    MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();\n                return true;\n            case SDL_CONTROLLER_BUTTON_X:\n                key = MyGUI::KeyCode::Semicolon;\n                break;\n            case SDL_CONTROLLER_BUTTON_Y:\n                key = MyGUI::KeyCode::Apostrophe;\n                break;\n            case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:\n                key = MyGUI::KeyCode::Period;\n                break;\n            case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:\n                key = MyGUI::KeyCode::Slash;\n                break;\n            case SDL_CONTROLLER_BUTTON_LEFTSTICK:\n                mGamepadGuiCursorEnabled = !mGamepadGuiCursorEnabled;\n                MWBase::Environment::get().getWindowManager()->setCursorActive(mGamepadGuiCursorEnabled);\n                return true;\n            default:\n                return false;\n        }\n\n        // Some keys will work even when Text Input windows/modals are in focus.\n        if (SDL_IsTextInputActive())\n            return false;\n\n        MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false);\n        return true;\n    }\n\n    bool ControllerManager::gamepadToGuiControl(const SDL_ControllerAxisEvent &arg)\n    {\n        switch (arg.axis)\n        {\n            case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:\n                if (arg.value == 32767) // Treat like a button.\n                    MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Minus, 0, false);\n                break;\n            case SDL_CONTROLLER_AXIS_TRIGGERLEFT:\n                if (arg.value == 32767) // Treat like a button.\n                    MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Equals, 0, false);\n                break;\n            case SDL_CONTROLLER_AXIS_LEFTX:\n            case SDL_CONTROLLER_AXIS_LEFTY:\n            case SDL_CONTROLLER_AXIS_RIGHTX:\n            case SDL_CONTROLLER_AXIS_RIGHTY:\n                // If we are using the joystick as a GUI mouse, process mouse movement elsewhere.\n                if (mGamepadGuiCursorEnabled)\n                    return false;\n                break;\n            default:\n                return false;\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/controllermanager.hpp",
    "content": "#ifndef MWINPUT_MWCONTROLLERMANAGER_H\n#define MWINPUT_MWCONTROLLERMANAGER_H\n\n#include <string>\n\n#include <components/settings/settings.hpp>\n#include <components/sdlutil/events.hpp>\n\nnamespace MWInput\n{\n    class ActionManager;\n    class BindingsManager;\n    class MouseManager;\n\n    class ControllerManager : public SDLUtil::ControllerListener\n    {\n    public:\n        ControllerManager(BindingsManager* bindingsManager,\n            ActionManager* actionManager,\n            MouseManager* mouseManager,\n            const std::string& userControllerBindingsFile,\n            const std::string& controllerBindingsFile);\n\n        virtual ~ControllerManager() = default;\n\n        bool update(float dt);\n\n        void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg) override;\n        void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg) override;\n        void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) override;\n        void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg) override;\n        void controllerRemoved(const SDL_ControllerDeviceEvent &arg) override;\n\n        void processChangedSettings(const Settings::CategorySettingVector& changed);\n\n        void setJoystickLastUsed(bool enabled) { mJoystickLastUsed = enabled; }\n        bool joystickLastUsed() { return mJoystickLastUsed; }\n\n        void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }\n\n        void setGamepadGuiCursorEnabled(bool enabled) { mGamepadGuiCursorEnabled = enabled; }\n        bool gamepadGuiCursorEnabled() { return mGamepadGuiCursorEnabled; }\n\n    private:\n        // Return true if GUI consumes input.\n        bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg);\n        bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg);\n\n        BindingsManager* mBindingsManager;\n        ActionManager* mActionManager;\n        MouseManager* mMouseManager;\n\n        bool mJoystickEnabled;\n        float mGamepadCursorSpeed;\n        float mSneakToggleShortcutTimer;\n        float mGamepadZoom;\n        bool mGamepadGuiCursorEnabled;\n        bool mGuiCursorEnabled;\n        bool mJoystickLastUsed;\n        bool mSneakGamepadShortcut;\n        bool mGamepadPreviewMode;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/controlswitch.cpp",
    "content": "#include \"controlswitch.hpp\"\n\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/controlsstate.hpp>\n\n#include <components/loadinglistener/loadinglistener.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/player.hpp\"\n\nnamespace MWInput\n{\n    ControlSwitch::ControlSwitch()\n    {\n        clear();\n    }\n\n    void ControlSwitch::clear()\n    {\n        mSwitches[\"playercontrols\"]      = true;\n        mSwitches[\"playerfighting\"]      = true;\n        mSwitches[\"playerjumping\"]       = true;\n        mSwitches[\"playerlooking\"]       = true;\n        mSwitches[\"playermagic\"]         = true;\n        mSwitches[\"playerviewswitch\"]    = true;\n        mSwitches[\"vanitymode\"]          = true;\n    }\n\n    bool ControlSwitch::get(const std::string& key)\n    {\n        return mSwitches[key];\n    }\n\n    void ControlSwitch::set(const std::string& key, bool value)\n    {\n        MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n\n        /// \\note 7 switches at all, if-else is relevant\n        if (key == \"playercontrols\" && !value)\n        {\n            player.setLeftRight(0);\n            player.setForwardBackward(0);\n            player.setAutoMove(false);\n            player.setUpDown(0);\n        }\n        else if (key == \"playerjumping\" && !value)\n        {\n            /// \\fixme maybe crouching at this time\n            player.setUpDown(0);\n        }\n        else if (key == \"vanitymode\")\n        {\n            MWBase::Environment::get().getWorld()->allowVanityMode(value);\n        }\n        else if (key == \"playerlooking\" && !value)\n        {\n            MWBase::Environment::get().getWorld()->rotateObject(player.getPlayer(), 0.f, 0.f, 0.f);\n        }\n        mSwitches[key] = value;\n    }\n\n    void ControlSwitch::write(ESM::ESMWriter& writer, Loading::Listener& /*progress*/)\n    {\n        ESM::ControlsState controls;\n        controls.mViewSwitchDisabled = !mSwitches[\"playerviewswitch\"];\n        controls.mControlsDisabled = !mSwitches[\"playercontrols\"];\n        controls.mJumpingDisabled = !mSwitches[\"playerjumping\"];\n        controls.mLookingDisabled = !mSwitches[\"playerlooking\"];\n        controls.mVanityModeDisabled = !mSwitches[\"vanitymode\"];\n        controls.mWeaponDrawingDisabled = !mSwitches[\"playerfighting\"];\n        controls.mSpellDrawingDisabled = !mSwitches[\"playermagic\"];\n\n        writer.startRecord (ESM::REC_INPU);\n        controls.save(writer);\n        writer.endRecord (ESM::REC_INPU);\n    }\n\n    void ControlSwitch::readRecord(ESM::ESMReader& reader, uint32_t type)\n    {\n        ESM::ControlsState controls;\n        controls.load(reader);\n\n        set(\"playerviewswitch\", !controls.mViewSwitchDisabled);\n        set(\"playercontrols\", !controls.mControlsDisabled);\n        set(\"playerjumping\", !controls.mJumpingDisabled);\n        set(\"playerlooking\", !controls.mLookingDisabled);\n        set(\"vanitymode\", !controls.mVanityModeDisabled);\n        set(\"playerfighting\", !controls.mWeaponDrawingDisabled);\n        set(\"playermagic\", !controls.mSpellDrawingDisabled);\n    }\n\n    int ControlSwitch::countSavedGameRecords() const\n    {\n        return 1;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/controlswitch.hpp",
    "content": "#ifndef MWINPUT_CONTROLSWITCH_H\n#define MWINPUT_CONTROLSWITCH_H\n\n#include <cstdint>\n#include <map>\n#include <string>\n\nnamespace ESM\n{\n    struct ControlsState;\n    class ESMReader;\n    class ESMWriter;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWInput\n{\n    class ControlSwitch\n    {\n    public:\n        ControlSwitch();\n\n        bool get(const std::string& key);\n        void set(const std::string& key, bool value);\n        void clear();\n\n        void write(ESM::ESMWriter& writer, Loading::Listener& progress);\n        void readRecord(ESM::ESMReader& reader, uint32_t type);\n        int countSavedGameRecords() const;\n\n    private:\n        std::map<std::string, bool> mSwitches;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/inputmanagerimp.cpp",
    "content": "#include \"inputmanagerimp.hpp\"\n\n#include <osgViewer/ViewerEventHandlers>\n\n#include <components/sdlutil/sdlinputwrapper.hpp>\n\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/esmreader.hpp>\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"actionmanager.hpp\"\n#include \"bindingsmanager.hpp\"\n#include \"controllermanager.hpp\"\n#include \"controlswitch.hpp\"\n#include \"keyboardmanager.hpp\"\n#include \"mousemanager.hpp\"\n#include \"sdlmappings.hpp\"\n#include \"sensormanager.hpp\"\n\nnamespace MWInput\n{\n    InputManager::InputManager(\n            SDL_Window* window,\n            osg::ref_ptr<osgViewer::Viewer> viewer,\n            osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,\n            osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,\n            const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile,\n            const std::string& controllerBindingsFile, bool grab)\n        : mControlsDisabled(false)\n    {\n        mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab);\n        mInputWrapper->setWindowEventCallback(MWBase::Environment::get().getWindowManager());\n\n        mBindingsManager = new BindingsManager(userFile, userFileExists);\n\n        mControlSwitch = new ControlSwitch();\n\n        mActionManager = new ActionManager(mBindingsManager, screenCaptureOperation, viewer, screenCaptureHandler);\n\n        mKeyboardManager = new KeyboardManager(mBindingsManager);\n        mInputWrapper->setKeyboardEventCallback(mKeyboardManager);\n\n        mMouseManager = new MouseManager(mBindingsManager, mInputWrapper, window);\n        mInputWrapper->setMouseEventCallback(mMouseManager);\n\n        mControllerManager = new ControllerManager(mBindingsManager, mActionManager, mMouseManager, userControllerBindingsFile, controllerBindingsFile);\n        mInputWrapper->setControllerEventCallback(mControllerManager);\n\n        mSensorManager = new SensorManager();\n        mInputWrapper->setSensorEventCallback(mSensorManager);\n    }\n\n    void InputManager::clear()\n    {\n        // Enable all controls\n        mControlSwitch->clear();\n    }\n\n    InputManager::~InputManager()\n    {\n        delete mActionManager;\n        delete mControllerManager;\n        delete mKeyboardManager;\n        delete mMouseManager;\n        delete mSensorManager;\n\n        delete mControlSwitch;\n\n        delete mBindingsManager;\n\n        delete mInputWrapper;\n    }\n\n    void InputManager::setAttemptJump(bool jumping)\n    {\n        mActionManager->setAttemptJump(jumping);\n    }\n\n    void InputManager::update(float dt, bool disableControls, bool disableEvents)\n    {\n        mControlsDisabled = disableControls;\n\n        mInputWrapper->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible());\n        mInputWrapper->capture(disableEvents);\n\n        if (disableControls)\n        {\n            mMouseManager->updateCursorMode();\n            return;\n        }\n\n        mBindingsManager->update(dt);\n\n        mMouseManager->updateCursorMode();\n\n        bool controllerMove = mControllerManager->update(dt);\n        mMouseManager->update(dt);\n        mSensorManager->update(dt);\n        mActionManager->update(dt, controllerMove);\n\n        MWBase::Environment::get().getWorld()->applyDeferredPreviewRotationToPlayer(dt);\n    }\n\n    void InputManager::setDragDrop(bool dragDrop)\n    {\n        mBindingsManager->setDragDrop(dragDrop);\n    }\n\n    void InputManager::setGamepadGuiCursorEnabled(bool enabled)\n    {\n        mControllerManager->setGamepadGuiCursorEnabled(enabled);\n    }\n\n    void InputManager::changeInputMode(bool guiMode)\n    {\n        mControllerManager->setGuiCursorEnabled(guiMode);\n        mMouseManager->setGuiCursorEnabled(guiMode);\n        mSensorManager->setGuiCursorEnabled(guiMode);\n        mMouseManager->setMouseLookEnabled(!guiMode);\n        if (guiMode)\n            MWBase::Environment::get().getWindowManager()->showCrosshair(false);\n\n        bool isCursorVisible = guiMode && (!mControllerManager->joystickLastUsed() || mControllerManager->gamepadGuiCursorEnabled());\n        MWBase::Environment::get().getWindowManager()->setCursorVisible(isCursorVisible);\n        // if not in gui mode, the camera decides whether to show crosshair or not.\n    }\n\n    void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed)\n    {\n        mMouseManager->processChangedSettings(changed);\n        mSensorManager->processChangedSettings(changed);\n    }\n\n    bool InputManager::getControlSwitch(const std::string& sw)\n    {\n        return mControlSwitch->get(sw);\n    }\n\n    void InputManager::toggleControlSwitch(const std::string& sw, bool value)\n    {\n        mControlSwitch->set(sw, value);\n    }\n\n    void InputManager::resetIdleTime()\n    {\n        mActionManager->resetIdleTime();\n    }\n\n    std::string InputManager::getActionDescription(int action)\n    {\n        return mBindingsManager->getActionDescription(action);\n    }\n\n    std::string InputManager::getActionKeyBindingName(int action)\n    {\n        return mBindingsManager->getActionKeyBindingName(action);\n    }\n\n    std::string InputManager::getActionControllerBindingName(int action)\n    {\n        return mBindingsManager->getActionControllerBindingName(action);\n    }\n\n    std::vector<int> InputManager::getActionKeySorting()\n    {\n        return mBindingsManager->getActionKeySorting();\n    }\n\n    std::vector<int> InputManager::getActionControllerSorting()\n    {\n        return mBindingsManager->getActionControllerSorting();\n    }\n\n    void InputManager::enableDetectingBindingMode(int action, bool keyboard)\n    {\n        mBindingsManager->enableDetectingBindingMode(action, keyboard);\n    }\n\n    int InputManager::countSavedGameRecords() const\n    {\n        return mControlSwitch->countSavedGameRecords();\n    }\n\n    void InputManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)\n    {\n        mControlSwitch->write(writer, progress);\n    }\n\n    void InputManager::readRecord(ESM::ESMReader& reader, uint32_t type)\n    {\n        if (type == ESM::REC_INPU)\n        {\n            mControlSwitch->readRecord(reader, type);\n        }\n    }\n\n    void InputManager::resetToDefaultKeyBindings()\n    {\n        mBindingsManager->loadKeyDefaults(true);\n    }\n\n    void InputManager::resetToDefaultControllerBindings()\n    {\n        mBindingsManager->loadControllerDefaults(true);\n    }\n\n    void InputManager::setJoystickLastUsed(bool enabled)\n    {\n        mControllerManager->setJoystickLastUsed(enabled);\n    }\n\n    bool InputManager::joystickLastUsed()\n    {\n        return mControllerManager->joystickLastUsed();\n    }\n\n    void InputManager::executeAction(int action)\n    {\n        mActionManager->executeAction(action);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/inputmanagerimp.hpp",
    "content": "#ifndef MWINPUT_MWINPUTMANAGERIMP_H\n#define MWINPUT_MWINPUTMANAGERIMP_H\n\n#include <osg/ref_ptr>\n#include <osgViewer/ViewerEventHandlers>\n\n#include <components/settings/settings.hpp>\n#include <components/sdlutil/events.hpp>\n\n#include \"../mwbase/inputmanager.hpp\"\n\n#include \"../mwgui/mode.hpp\"\n\n#include \"actions.hpp\"\n\nnamespace MWWorld\n{\n    class Player;\n}\n\nnamespace MWBase\n{\n    class WindowManager;\n}\n\nnamespace SDLUtil\n{\n    class InputWrapper;\n}\n\nstruct SDL_Window;\n\nnamespace MWInput\n{\n    class ControlSwitch;\n    class ActionManager;\n    class BindingsManager;\n    class ControllerManager;\n    class KeyboardManager;\n    class MouseManager;\n    class SensorManager;\n\n    /**\n    * @brief Class that provides a high-level API for game input\n    */\n    class InputManager : public MWBase::InputManager\n    {\n    public:\n        InputManager(\n            SDL_Window* window,\n            osg::ref_ptr<osgViewer::Viewer> viewer,\n            osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,\n            osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,\n            const std::string& userFile, bool userFileExists,\n            const std::string& userControllerBindingsFile,\n            const std::string& controllerBindingsFile, bool grab);\n\n        virtual ~InputManager();\n\n        /// Clear all savegame-specific data\n        void clear() override;\n\n        void update(float dt, bool disableControls=false, bool disableEvents=false) override;\n\n        void changeInputMode(bool guiMode) override;\n\n        void processChangedSettings(const Settings::CategorySettingVector& changed) override;\n\n        void setDragDrop(bool dragDrop) override;\n        void setGamepadGuiCursorEnabled(bool enabled) override;\n        void setAttemptJump(bool jumping) override;\n\n        void toggleControlSwitch (const std::string& sw, bool value) override;\n        bool getControlSwitch (const std::string& sw) override;\n\n        std::string getActionDescription (int action) override;\n        std::string getActionKeyBindingName (int action) override;\n        std::string getActionControllerBindingName (int action) override;\n        int getNumActions() override { return A_Last; }\n        std::vector<int> getActionKeySorting() override;\n        std::vector<int> getActionControllerSorting() override;\n        void enableDetectingBindingMode (int action, bool keyboard) override;\n        void resetToDefaultKeyBindings() override;\n        void resetToDefaultControllerBindings() override;\n\n        void setJoystickLastUsed(bool enabled) override;\n        bool joystickLastUsed() override;\n\n        int countSavedGameRecords() const override;\n        void write(ESM::ESMWriter& writer, Loading::Listener& progress) override;\n        void readRecord(ESM::ESMReader& reader, uint32_t type) override;\n\n        void resetIdleTime() override;\n\n        void executeAction(int action) override;\n\n        bool controlsDisabled() override { return mControlsDisabled; }\n\n    private:\n        void convertMousePosForMyGUI(int& x, int& y);\n\n        void handleGuiArrowKey(int action);\n\n        void quickKey(int index);\n        void showQuickKeysMenu();\n\n        void loadKeyDefaults(bool force = false);\n        void loadControllerDefaults(bool force = false);\n\n        SDLUtil::InputWrapper* mInputWrapper;\n\n        bool mControlsDisabled;\n\n        ControlSwitch* mControlSwitch;\n\n        ActionManager* mActionManager;\n        BindingsManager* mBindingsManager;\n        ControllerManager* mControllerManager;\n        KeyboardManager* mKeyboardManager;\n        MouseManager* mMouseManager;\n        SensorManager* mSensorManager;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/keyboardmanager.cpp",
    "content": "#include \"keyboardmanager.hpp\"\n\n#include <cctype>\n\n#include <MyGUI_InputManager.h>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/player.hpp\"\n\n#include \"actions.hpp\"\n#include \"bindingsmanager.hpp\"\n#include \"sdlmappings.hpp\"\n\nnamespace MWInput\n{\n    KeyboardManager::KeyboardManager(BindingsManager* bindingsManager)\n        : mBindingsManager(bindingsManager)\n    {\n    }\n\n    void KeyboardManager::textInput(const SDL_TextInputEvent &arg)\n    {\n        MyGUI::UString ustring(&arg.text[0]);\n        MyGUI::UString::utf32string utf32string = ustring.asUTF32();\n        for (MyGUI::UString::utf32string::const_iterator it = utf32string.begin(); it != utf32string.end(); ++it)\n            MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it);\n    }\n\n    void KeyboardManager::keyPressed(const SDL_KeyboardEvent &arg)\n    {\n        // HACK: to make default keybinding for the console work without printing an extra \"^\" upon closing\n        // This assumes that SDL_TextInput events always come *after* the key event\n        // (which is somewhat reasonable, and hopefully true for all SDL platforms)\n        auto kc = sdlKeyToMyGUI(arg.keysym.sym);\n        if (mBindingsManager->getKeyBinding(A_Console) == arg.keysym.scancode\n                && MWBase::Environment::get().getWindowManager()->isConsoleMode())\n            SDL_StopTextInput();\n\n        bool consumed = SDL_IsTextInputActive() &&  // Little trick to check if key is printable\n                        (!(SDLK_SCANCODE_MASK & arg.keysym.sym) &&\n                        (std::isprint(arg.keysym.sym) ||\n                        // Don't trust isprint for symbols outside the extended ASCII range\n                        (kc == MyGUI::KeyCode::None && arg.keysym.sym > 0xff)));\n        if (kc != MyGUI::KeyCode::None && !mBindingsManager->isDetectingBindingState())\n        {\n            if (MWBase::Environment::get().getWindowManager()->injectKeyPress(kc, 0, arg.repeat))\n                consumed = true;\n            mBindingsManager->setPlayerControlsEnabled(!consumed);\n        }\n\n        if (arg.repeat)\n            return;\n\n        MWBase::InputManager* input = MWBase::Environment::get().getInputManager();\n        if (!input->controlsDisabled() && !consumed)\n            mBindingsManager->keyPressed(arg);\n\n        input->setJoystickLastUsed(false);\n    }\n\n    void KeyboardManager::keyReleased(const SDL_KeyboardEvent &arg)\n    {\n        MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false);\n        auto kc = sdlKeyToMyGUI(arg.keysym.sym);\n\n        if (!mBindingsManager->isDetectingBindingState())\n            mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc));\n        mBindingsManager->keyReleased(arg);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/keyboardmanager.hpp",
    "content": "#ifndef MWINPUT_MWKEYBOARDMANAGER_H\n#define MWINPUT_MWKEYBOARDMANAGER_H\n\n#include <components/sdlutil/events.hpp>\n\nnamespace MWInput\n{\n    class BindingsManager;\n\n    class KeyboardManager : public SDLUtil::KeyListener\n    {\n    public:\n        KeyboardManager(BindingsManager* bindingsManager);\n\n        virtual ~KeyboardManager() = default;\n\n        void textInput(const SDL_TextInputEvent &arg) override;\n        void keyPressed(const SDL_KeyboardEvent &arg) override;\n        void keyReleased(const SDL_KeyboardEvent &arg) override;\n\n    private:\n        BindingsManager* mBindingsManager;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/mousemanager.cpp",
    "content": "#include \"mousemanager.hpp\"\n\n#include <MyGUI_Button.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_Widget.h>\n\n#include <components/debug/debuglog.hpp>\n#include <components/sdlutil/sdlinputwrapper.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/player.hpp\"\n\n#include \"actions.hpp\"\n#include \"bindingsmanager.hpp\"\n#include \"sdlmappings.hpp\"\n\nnamespace MWInput\n{\n    MouseManager::MouseManager(BindingsManager* bindingsManager, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window)\n        : mInvertX(Settings::Manager::getBool(\"invert x axis\", \"Input\"))\n        , mInvertY(Settings::Manager::getBool(\"invert y axis\", \"Input\"))\n        , mGrabCursor(Settings::Manager::getBool(\"grab cursor\", \"Input\"))\n        , mCameraSensitivity(Settings::Manager::getFloat(\"camera sensitivity\", \"Input\"))\n        , mCameraYMultiplier(Settings::Manager::getFloat(\"camera y multiplier\", \"Input\"))\n        , mBindingsManager(bindingsManager)\n        , mInputWrapper(inputWrapper)\n        , mGuiCursorX(0)\n        , mGuiCursorY(0)\n        , mMouseWheel(0)\n        , mMouseLookEnabled(false)\n        , mGuiCursorEnabled(true)\n    {\n        int w,h;\n        SDL_GetWindowSize(window, &w, &h);\n\n        float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();\n        mGuiCursorX = w / (2.f * uiScale);\n        mGuiCursorY = h / (2.f * uiScale);\n    }\n\n    void MouseManager::processChangedSettings(const Settings::CategorySettingVector& changed)\n    {\n        for (const auto& setting : changed)\n        {\n            if (setting.first == \"Input\" && setting.second == \"invert x axis\")\n                mInvertX = Settings::Manager::getBool(\"invert x axis\", \"Input\");\n\n            if (setting.first == \"Input\" && setting.second == \"invert y axis\")\n                mInvertY = Settings::Manager::getBool(\"invert y axis\", \"Input\");\n\n            if (setting.first == \"Input\" && setting.second == \"camera sensitivity\")\n                mCameraSensitivity = Settings::Manager::getFloat(\"camera sensitivity\", \"Input\");\n\n            if (setting.first == \"Input\" && setting.second == \"grab cursor\")\n                mGrabCursor = Settings::Manager::getBool(\"grab cursor\", \"Input\");\n        }\n    }\n\n    void MouseManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg)\n    {\n        mBindingsManager->mouseMoved(arg);\n\n        MWBase::InputManager* input = MWBase::Environment::get().getInputManager();\n        input->setJoystickLastUsed(false);\n        input->resetIdleTime();\n\n        if (mGuiCursorEnabled)\n        {\n            input->setGamepadGuiCursorEnabled(true);\n\n            // We keep track of our own mouse position, so that moving the mouse while in\n            // game mode does not move the position of the GUI cursor\n            float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();\n            mGuiCursorX = static_cast<float>(arg.x) / uiScale;\n            mGuiCursorY = static_cast<float>(arg.y) / uiScale;\n\n            mMouseWheel = static_cast<int>(arg.z);\n\n            MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), mMouseWheel);\n            // FIXME: inject twice to force updating focused widget states (tooltips) resulting from changing the viewport by scroll wheel\n            MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), mMouseWheel);\n\n            MWBase::Environment::get().getWindowManager()->setCursorActive(true);\n        }\n\n        if (mMouseLookEnabled && !input->controlsDisabled())\n        {\n            MWBase::World* world = MWBase::Environment::get().getWorld();\n\n            float x = arg.xrel * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f;\n            float y = arg.yrel * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f;\n\n            float rot[3];\n            rot[0] = -y;\n            rot[1] = 0.0f;\n            rot[2] = -x;\n\n            // Only actually turn player when we're not in vanity mode\n            if (!world->vanityRotateCamera(rot) && input->getControlSwitch(\"playerlooking\"))\n            {\n                MWWorld::Player& player = world->getPlayer();\n                player.yaw(x);\n                player.pitch(y);\n            }\n            else if (!input->getControlSwitch(\"playerlooking\"))\n                MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();\n        }\n    }\n\n    void MouseManager::mouseReleased(const SDL_MouseButtonEvent &arg, Uint8 id)\n    {\n        MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false);\n\n        if (mBindingsManager->isDetectingBindingState())\n        {\n            mBindingsManager->mouseReleased(arg, id);\n        }\n        else\n        {\n            bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();\n            guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode;\n\n            if (mBindingsManager->isDetectingBindingState())\n                return; // don't allow same mouseup to bind as initiated bind\n\n            mBindingsManager->setPlayerControlsEnabled(!guiMode);\n            mBindingsManager->mouseReleased(arg, id);\n        }\n    }\n\n    void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg)\n    {\n        MWBase::InputManager* input = MWBase::Environment::get().getInputManager();\n        if (mBindingsManager->isDetectingBindingState() || !input->controlsDisabled())\n            mBindingsManager->mouseWheelMoved(arg);\n\n        input->setJoystickLastUsed(false);\n    }\n\n    void MouseManager::mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id)\n    {\n        MWBase::InputManager* input = MWBase::Environment::get().getInputManager();\n        input->setJoystickLastUsed(false);\n        bool guiMode = false;\n\n        if (id == SDL_BUTTON_LEFT || id == SDL_BUTTON_RIGHT) // MyGUI only uses these mouse events\n        {\n            guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();\n            guiMode = MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode;\n            if (MyGUI::InputManager::getInstance().getMouseFocusWidget () != nullptr)\n            {\n                MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType<MyGUI::Button>(false);\n                if (b && b->getEnabled() && id == SDL_BUTTON_LEFT)\n                {\n                    MWBase::Environment::get().getWindowManager()->playSound(\"Menu Click\");\n                }\n            }\n            MWBase::Environment::get().getWindowManager()->setCursorActive(true);\n        }\n\n        mBindingsManager->setPlayerControlsEnabled(!guiMode);\n\n        // Don't trigger any mouse bindings while in settings menu, otherwise rebinding controls becomes impossible\n        // Also do not trigger bindings when input controls are disabled, e.g. during save loading\n        if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings && !input->controlsDisabled())\n            mBindingsManager->mousePressed(arg, id);\n    }\n\n    void MouseManager::updateCursorMode()\n    {\n        bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu)\n             && !MWBase::Environment::get().getWindowManager()->isConsoleMode();\n\n        bool wasRelative = mInputWrapper->getMouseRelative();\n        bool isRelative = !MWBase::Environment::get().getWindowManager()->isGuiMode();\n\n        // don't keep the pointer away from the window edge in gui mode\n        // stop using raw mouse motions and switch to system cursor movements\n        mInputWrapper->setMouseRelative(isRelative);\n\n        //we let the mouse escape in the main menu\n        mInputWrapper->setGrabPointer(grab && (mGrabCursor || isRelative));\n\n        //we switched to non-relative mode, move our cursor to where the in-game\n        //cursor is\n        if (!isRelative && wasRelative != isRelative)\n        {\n            warpMouse();\n        }\n    }\n\n    void MouseManager::update(float dt)\n    {\n        if (!mMouseLookEnabled)\n            return;\n\n        float xAxis = mBindingsManager->getActionValue(A_LookLeftRight) * 2.0f - 1.0f;\n        float yAxis = mBindingsManager->getActionValue(A_LookUpDown) * 2.0f - 1.0f;\n        if (xAxis == 0 && yAxis == 0)\n            return;\n\n        float rot[3];\n        rot[0] = -yAxis * dt * 1000.0f * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f;\n        rot[1] = 0.0f;\n        rot[2] = -xAxis * dt * 1000.0f * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f;\n\n        // Only actually turn player when we're not in vanity mode\n        bool controls = MWBase::Environment::get().getInputManager()->getControlSwitch(\"playercontrols\");\n        if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && controls)\n        {\n            MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n            player.yaw(-rot[2]);\n            player.pitch(-rot[0]);\n        }\n        else if (!controls)\n            MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();\n\n        MWBase::Environment::get().getInputManager()->resetIdleTime();\n    }\n\n    bool MouseManager::injectMouseButtonPress(Uint8 button)\n    {\n        return MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));\n    }\n\n    bool MouseManager::injectMouseButtonRelease(Uint8 button)\n    {\n        return MyGUI::InputManager::getInstance().injectMouseRelease(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));\n    }\n\n    void MouseManager::injectMouseMove(float xMove, float yMove, float mouseWheelMove)\n    {\n        mGuiCursorX += xMove;\n        mGuiCursorY += yMove;\n        mMouseWheel += mouseWheelMove;\n\n        const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width - 1)));\n        mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height - 1)));\n\n        MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), static_cast<int>(mMouseWheel));\n    }\n\n    void MouseManager::warpMouse()\n    {\n        float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();\n        mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX*uiScale), static_cast<int>(mGuiCursorY*uiScale));\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/mousemanager.hpp",
    "content": "#ifndef MWINPUT_MWMOUSEMANAGER_H\n#define MWINPUT_MWMOUSEMANAGER_H\n\n#include <components/settings/settings.hpp>\n#include <components/sdlutil/events.hpp>\n\nnamespace SDLUtil\n{\n    class InputWrapper;\n}\n\nnamespace MWInput\n{\n    class BindingsManager;\n\n    class MouseManager : public SDLUtil::MouseListener\n    {\n    public:\n        MouseManager(BindingsManager* bindingsManager, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window);\n\n        virtual ~MouseManager() = default;\n\n        void updateCursorMode();\n        void update(float dt);\n\n        void mouseMoved(const SDLUtil::MouseMotionEvent &arg) override;\n        void mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id) override;\n        void mouseReleased(const SDL_MouseButtonEvent &arg, Uint8 id) override;\n        void mouseWheelMoved(const SDL_MouseWheelEvent &arg) override;\n\n        void processChangedSettings(const Settings::CategorySettingVector& changed);\n\n        bool injectMouseButtonPress(Uint8 button);\n        bool injectMouseButtonRelease(Uint8 button);\n        void injectMouseMove(float xMove, float yMove, float mouseWheelMove);\n        void warpMouse();\n\n        void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; }\n        void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }\n\n    private:\n        bool mInvertX;\n        bool mInvertY;\n        bool mGrabCursor;\n        float mCameraSensitivity;\n        float mCameraYMultiplier;\n\n        BindingsManager* mBindingsManager;\n        SDLUtil::InputWrapper* mInputWrapper;\n\n        float mGuiCursorX;\n        float mGuiCursorY;\n        int mMouseWheel;\n        bool mMouseLookEnabled;\n        bool mGuiCursorEnabled;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/sdlmappings.cpp",
    "content": "#include \"sdlmappings.hpp\"\n\n#include <map>\n\n#include <MyGUI_MouseButton.h>\n\n#include <SDL_gamecontroller.h>\n#include <SDL_mouse.h>\n\nnamespace MWInput\n{\n    std::string sdlControllerButtonToString(int button)\n    {\n        switch(button)\n        {\n            case SDL_CONTROLLER_BUTTON_A:\n                return \"A Button\";\n            case SDL_CONTROLLER_BUTTON_B:\n                return \"B Button\";\n            case SDL_CONTROLLER_BUTTON_BACK:\n                return \"Back Button\";\n            case SDL_CONTROLLER_BUTTON_DPAD_DOWN:\n                return \"DPad Down\";\n            case SDL_CONTROLLER_BUTTON_DPAD_LEFT:\n                return \"DPad Left\";\n            case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:\n                return \"DPad Right\";\n            case SDL_CONTROLLER_BUTTON_DPAD_UP:\n                return \"DPad Up\";\n            case SDL_CONTROLLER_BUTTON_GUIDE:\n                return \"Guide Button\";\n            case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:\n                return \"Left Shoulder\";\n            case SDL_CONTROLLER_BUTTON_LEFTSTICK:\n                return \"Left Stick Button\";\n            case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:\n                return \"Right Shoulder\";\n            case SDL_CONTROLLER_BUTTON_RIGHTSTICK:\n                return \"Right Stick Button\";\n            case SDL_CONTROLLER_BUTTON_START:\n                return \"Start Button\";\n            case SDL_CONTROLLER_BUTTON_X:\n                return \"X Button\";\n            case SDL_CONTROLLER_BUTTON_Y:\n                return \"Y Button\";\n            default:\n                return \"Button \" + std::to_string(button);\n        }\n    }\n\n    std::string sdlControllerAxisToString(int axis)\n    {\n        switch(axis)\n        {\n            case SDL_CONTROLLER_AXIS_LEFTX:\n                return \"Left Stick X\";\n            case SDL_CONTROLLER_AXIS_LEFTY:\n                return \"Left Stick Y\";\n            case SDL_CONTROLLER_AXIS_RIGHTX:\n                return \"Right Stick X\";\n            case SDL_CONTROLLER_AXIS_RIGHTY:\n                return \"Right Stick Y\";\n            case SDL_CONTROLLER_AXIS_TRIGGERLEFT:\n                return \"Left Trigger\";\n            case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:\n                return \"Right Trigger\";\n            default:\n                return \"Axis \" + std::to_string(axis);\n        }\n    }\n\n    MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button)\n    {\n        //The right button is the second button, according to MyGUI\n        if(button == SDL_BUTTON_RIGHT)\n            button = SDL_BUTTON_MIDDLE;\n        else if(button == SDL_BUTTON_MIDDLE)\n            button = SDL_BUTTON_RIGHT;\n\n        //MyGUI's buttons are 0 indexed\n        return MyGUI::MouseButton::Enum(button - 1);\n    }\n\n    void initKeyMap(std::map<SDL_Keycode, MyGUI::KeyCode>& keyMap)\n    {\n        keyMap[SDLK_UNKNOWN] = MyGUI::KeyCode::None;\n        keyMap[SDLK_ESCAPE] = MyGUI::KeyCode::Escape;\n        keyMap[SDLK_1] = MyGUI::KeyCode::One;\n        keyMap[SDLK_2] = MyGUI::KeyCode::Two;\n        keyMap[SDLK_3] = MyGUI::KeyCode::Three;\n        keyMap[SDLK_4] = MyGUI::KeyCode::Four;\n        keyMap[SDLK_5] = MyGUI::KeyCode::Five;\n        keyMap[SDLK_6] = MyGUI::KeyCode::Six;\n        keyMap[SDLK_7] = MyGUI::KeyCode::Seven;\n        keyMap[SDLK_8] = MyGUI::KeyCode::Eight;\n        keyMap[SDLK_9] = MyGUI::KeyCode::Nine;\n        keyMap[SDLK_0] = MyGUI::KeyCode::Zero;\n        keyMap[SDLK_MINUS] = MyGUI::KeyCode::Minus;\n        keyMap[SDLK_EQUALS] = MyGUI::KeyCode::Equals;\n        keyMap[SDLK_BACKSPACE] = MyGUI::KeyCode::Backspace;\n        keyMap[SDLK_TAB] = MyGUI::KeyCode::Tab;\n        keyMap[SDLK_q] = MyGUI::KeyCode::Q;\n        keyMap[SDLK_w] = MyGUI::KeyCode::W;\n        keyMap[SDLK_e] = MyGUI::KeyCode::E;\n        keyMap[SDLK_r] = MyGUI::KeyCode::R;\n        keyMap[SDLK_t] = MyGUI::KeyCode::T;\n        keyMap[SDLK_y] = MyGUI::KeyCode::Y;\n        keyMap[SDLK_u] = MyGUI::KeyCode::U;\n        keyMap[SDLK_i] = MyGUI::KeyCode::I;\n        keyMap[SDLK_o] = MyGUI::KeyCode::O;\n        keyMap[SDLK_p] = MyGUI::KeyCode::P;\n        keyMap[SDLK_RETURN] = MyGUI::KeyCode::Return;\n        keyMap[SDLK_a] = MyGUI::KeyCode::A;\n        keyMap[SDLK_s] = MyGUI::KeyCode::S;\n        keyMap[SDLK_d] = MyGUI::KeyCode::D;\n        keyMap[SDLK_f] = MyGUI::KeyCode::F;\n        keyMap[SDLK_g] = MyGUI::KeyCode::G;\n        keyMap[SDLK_h] = MyGUI::KeyCode::H;\n        keyMap[SDLK_j] = MyGUI::KeyCode::J;\n        keyMap[SDLK_k] = MyGUI::KeyCode::K;\n        keyMap[SDLK_l] = MyGUI::KeyCode::L;\n        keyMap[SDLK_SEMICOLON] = MyGUI::KeyCode::Semicolon;\n        keyMap[SDLK_QUOTE] = MyGUI::KeyCode::Apostrophe;\n        keyMap[SDLK_BACKQUOTE] = MyGUI::KeyCode::Grave;\n        keyMap[SDLK_LSHIFT] = MyGUI::KeyCode::LeftShift;\n        keyMap[SDLK_BACKSLASH] = MyGUI::KeyCode::Backslash;\n        keyMap[SDLK_z] = MyGUI::KeyCode::Z;\n        keyMap[SDLK_x] = MyGUI::KeyCode::X;\n        keyMap[SDLK_c] = MyGUI::KeyCode::C;\n        keyMap[SDLK_v] = MyGUI::KeyCode::V;\n        keyMap[SDLK_b] = MyGUI::KeyCode::B;\n        keyMap[SDLK_n] = MyGUI::KeyCode::N;\n        keyMap[SDLK_m] = MyGUI::KeyCode::M;\n        keyMap[SDLK_COMMA] = MyGUI::KeyCode::Comma;\n        keyMap[SDLK_PERIOD] = MyGUI::KeyCode::Period;\n        keyMap[SDLK_SLASH] = MyGUI::KeyCode::Slash;\n        keyMap[SDLK_RSHIFT] = MyGUI::KeyCode::RightShift;\n        keyMap[SDLK_KP_MULTIPLY] = MyGUI::KeyCode::Multiply;\n        keyMap[SDLK_LALT] = MyGUI::KeyCode::LeftAlt;\n        keyMap[SDLK_SPACE] = MyGUI::KeyCode::Space;\n        keyMap[SDLK_CAPSLOCK] = MyGUI::KeyCode::Capital;\n        keyMap[SDLK_F1] = MyGUI::KeyCode::F1;\n        keyMap[SDLK_F2] = MyGUI::KeyCode::F2;\n        keyMap[SDLK_F3] = MyGUI::KeyCode::F3;\n        keyMap[SDLK_F4] = MyGUI::KeyCode::F4;\n        keyMap[SDLK_F5] = MyGUI::KeyCode::F5;\n        keyMap[SDLK_F6] = MyGUI::KeyCode::F6;\n        keyMap[SDLK_F7] = MyGUI::KeyCode::F7;\n        keyMap[SDLK_F8] = MyGUI::KeyCode::F8;\n        keyMap[SDLK_F9] = MyGUI::KeyCode::F9;\n        keyMap[SDLK_F10] = MyGUI::KeyCode::F10;\n        keyMap[SDLK_NUMLOCKCLEAR] = MyGUI::KeyCode::NumLock;\n        keyMap[SDLK_SCROLLLOCK] = MyGUI::KeyCode::ScrollLock;\n        keyMap[SDLK_KP_7] = MyGUI::KeyCode::Numpad7;\n        keyMap[SDLK_KP_8] = MyGUI::KeyCode::Numpad8;\n        keyMap[SDLK_KP_9] = MyGUI::KeyCode::Numpad9;\n        keyMap[SDLK_KP_MINUS] = MyGUI::KeyCode::Subtract;\n        keyMap[SDLK_KP_4] = MyGUI::KeyCode::Numpad4;\n        keyMap[SDLK_KP_5] = MyGUI::KeyCode::Numpad5;\n        keyMap[SDLK_KP_6] = MyGUI::KeyCode::Numpad6;\n        keyMap[SDLK_KP_PLUS] = MyGUI::KeyCode::Add;\n        keyMap[SDLK_KP_1] = MyGUI::KeyCode::Numpad1;\n        keyMap[SDLK_KP_2] = MyGUI::KeyCode::Numpad2;\n        keyMap[SDLK_KP_3] = MyGUI::KeyCode::Numpad3;\n        keyMap[SDLK_KP_0] = MyGUI::KeyCode::Numpad0;\n        keyMap[SDLK_KP_PERIOD] = MyGUI::KeyCode::Decimal;\n        keyMap[SDLK_F11] = MyGUI::KeyCode::F11;\n        keyMap[SDLK_F12] = MyGUI::KeyCode::F12;\n        keyMap[SDLK_F13] = MyGUI::KeyCode::F13;\n        keyMap[SDLK_F14] = MyGUI::KeyCode::F14;\n        keyMap[SDLK_F15] = MyGUI::KeyCode::F15;\n        keyMap[SDLK_KP_EQUALS] = MyGUI::KeyCode::NumpadEquals;\n        keyMap[SDLK_COLON] = MyGUI::KeyCode::Colon;\n        keyMap[SDLK_KP_ENTER] = MyGUI::KeyCode::NumpadEnter;\n        keyMap[SDLK_KP_DIVIDE] = MyGUI::KeyCode::Divide;\n        keyMap[SDLK_SYSREQ] = MyGUI::KeyCode::SysRq;\n        keyMap[SDLK_RALT] = MyGUI::KeyCode::RightAlt;\n        keyMap[SDLK_HOME] = MyGUI::KeyCode::Home;\n        keyMap[SDLK_UP] = MyGUI::KeyCode::ArrowUp;\n        keyMap[SDLK_PAGEUP] = MyGUI::KeyCode::PageUp;\n        keyMap[SDLK_LEFT] = MyGUI::KeyCode::ArrowLeft;\n        keyMap[SDLK_RIGHT] = MyGUI::KeyCode::ArrowRight;\n        keyMap[SDLK_END] = MyGUI::KeyCode::End;\n        keyMap[SDLK_DOWN] = MyGUI::KeyCode::ArrowDown;\n        keyMap[SDLK_PAGEDOWN] = MyGUI::KeyCode::PageDown;\n        keyMap[SDLK_INSERT] = MyGUI::KeyCode::Insert;\n        keyMap[SDLK_DELETE] = MyGUI::KeyCode::Delete;\n        keyMap[SDLK_APPLICATION] = MyGUI::KeyCode::AppMenu;\n\n//The function of the Ctrl and Meta keys are switched on macOS compared to other platforms.\n//For instance] = Cmd+C versus Ctrl+C to copy from the system clipboard\n#if defined(__APPLE__)\n        keyMap[SDLK_LGUI] = MyGUI::KeyCode::LeftControl;\n        keyMap[SDLK_RGUI] = MyGUI::KeyCode::RightControl;\n        keyMap[SDLK_LCTRL] = MyGUI::KeyCode::LeftWindows;\n        keyMap[SDLK_RCTRL] = MyGUI::KeyCode::RightWindows;\n#else\n        keyMap[SDLK_LGUI] = MyGUI::KeyCode::LeftWindows;\n        keyMap[SDLK_RGUI] = MyGUI::KeyCode::RightWindows;\n        keyMap[SDLK_LCTRL] = MyGUI::KeyCode::LeftControl;\n        keyMap[SDLK_RCTRL] = MyGUI::KeyCode::RightControl;\n#endif\n    }\n\n    MyGUI::KeyCode sdlKeyToMyGUI(SDL_Keycode code)\n    {\n        static std::map<SDL_Keycode, MyGUI::KeyCode> keyMap;\n        if (keyMap.empty())\n            initKeyMap(keyMap);\n\n        MyGUI::KeyCode kc = MyGUI::KeyCode::None;\n        auto foundKey = keyMap.find(code);\n        if (foundKey != keyMap.end())\n            kc = foundKey->second;\n\n        return kc;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/sdlmappings.hpp",
    "content": "#ifndef MWINPUT_SDLMAPPINGS_H\n#define MWINPUT_SDLMAPPINGS_H\n\n#include <string>\n\n#include <MyGUI_KeyCode.h>\n\n#include <SDL_keycode.h>\n\nnamespace MyGUI\n{\n    struct MouseButton;\n}\n\nnamespace MWInput\n{\n    std::string sdlControllerButtonToString(int button);\n\n    std::string sdlControllerAxisToString(int axis);\n\n    MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button);\n\n    MyGUI::KeyCode sdlKeyToMyGUI(SDL_Keycode code);\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwinput/sensormanager.cpp",
    "content": "#include \"sensormanager.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/player.hpp\"\n\nnamespace MWInput\n{\n    SensorManager::SensorManager()\n        : mInvertX(Settings::Manager::getBool(\"invert x axis\", \"Input\"))\n        , mInvertY(Settings::Manager::getBool(\"invert y axis\", \"Input\"))\n        , mGyroXSpeed(0.f)\n        , mGyroYSpeed(0.f)\n        , mGyroUpdateTimer(0.f)\n        , mGyroHSensitivity(Settings::Manager::getFloat(\"gyro horizontal sensitivity\", \"Input\"))\n        , mGyroVSensitivity(Settings::Manager::getFloat(\"gyro vertical sensitivity\", \"Input\"))\n        , mGyroHAxis(GyroscopeAxis::Minus_X)\n        , mGyroVAxis(GyroscopeAxis::Y)\n        , mGyroInputThreshold(Settings::Manager::getFloat(\"gyro input threshold\", \"Input\"))\n        , mGyroscope(nullptr)\n        , mGuiCursorEnabled(true)\n    {\n        init();\n    }\n\n    void SensorManager::init()\n    {\n        correctGyroscopeAxes();\n        updateSensors();\n    }\n\n    SensorManager::~SensorManager()\n    {\n        if (mGyroscope != nullptr)\n        {\n            SDL_SensorClose(mGyroscope);\n            mGyroscope = nullptr;\n        }\n    }\n\n    SensorManager::GyroscopeAxis SensorManager::mapGyroscopeAxis(const std::string& axis)\n    {\n        if (axis == \"x\")\n            return GyroscopeAxis::X;\n        else if (axis == \"y\")\n            return GyroscopeAxis::Y;\n        else if (axis == \"z\")\n            return GyroscopeAxis::Z;\n        else if (axis == \"-x\")\n            return GyroscopeAxis::Minus_X;\n        else if (axis == \"-y\")\n            return GyroscopeAxis::Minus_Y;\n        else if (axis == \"-z\")\n            return GyroscopeAxis::Minus_Z;\n\n        return GyroscopeAxis::Unknown;\n    }\n\n    void SensorManager::correctGyroscopeAxes()\n    {\n        if (!Settings::Manager::getBool(\"enable gyroscope\", \"Input\"))\n            return;\n\n        // Treat setting from config as axes for landscape mode.\n        // If the device does not support orientation change, do nothing.\n        // Note: in is unclear how to correct axes for devices with non-standart Z axis direction.\n        mGyroHAxis = mapGyroscopeAxis(Settings::Manager::getString(\"gyro horizontal axis\", \"Input\"));\n        mGyroVAxis = mapGyroscopeAxis(Settings::Manager::getString(\"gyro vertical axis\", \"Input\"));\n\n        SDL_DisplayOrientation currentOrientation = SDL_GetDisplayOrientation(Settings::Manager::getInt(\"screen\", \"Video\"));\n        switch (currentOrientation)\n        {\n            case SDL_ORIENTATION_UNKNOWN:\n                return;\n            case SDL_ORIENTATION_LANDSCAPE:\n                break;\n            case SDL_ORIENTATION_LANDSCAPE_FLIPPED:\n            {\n                mGyroHAxis = GyroscopeAxis(-mGyroHAxis);\n                mGyroVAxis = GyroscopeAxis(-mGyroVAxis);\n\n                break;\n            }\n            case SDL_ORIENTATION_PORTRAIT:\n            {\n                GyroscopeAxis oldVAxis = mGyroVAxis;\n                mGyroVAxis = mGyroHAxis;\n                mGyroHAxis = GyroscopeAxis(-oldVAxis);\n\n                break;\n            }\n            case SDL_ORIENTATION_PORTRAIT_FLIPPED:\n            {\n                GyroscopeAxis oldVAxis = mGyroVAxis;\n                mGyroVAxis = GyroscopeAxis(-mGyroHAxis);\n                mGyroHAxis = oldVAxis;\n\n                break;\n            }\n        }\n    }\n\n    void SensorManager::updateSensors()\n    {\n        if (Settings::Manager::getBool(\"enable gyroscope\", \"Input\"))\n        {\n            int numSensors = SDL_NumSensors();\n            for (int i = 0; i < numSensors; ++i)\n            {\n                if (SDL_SensorGetDeviceType(i) == SDL_SENSOR_GYRO)\n                {\n                    // It is unclear how to handle several enabled gyroscopes, so use the first one.\n                    // Note: Android registers some gyroscope as two separate sensors, for non-wake-up mode and for wake-up mode.\n                    if (mGyroscope != nullptr)\n                    {\n                        SDL_SensorClose(mGyroscope);\n                        mGyroscope = nullptr;\n                        mGyroXSpeed = mGyroYSpeed = 0.f;\n                        mGyroUpdateTimer = 0.f;\n                    }\n\n                    // FIXME: SDL2 does not provide a way to configure a sensor update frequency so far.\n                    SDL_Sensor *sensor = SDL_SensorOpen(i);\n                    if (sensor == nullptr)\n                        Log(Debug::Error) << \"Couldn't open sensor \" << SDL_SensorGetDeviceName(i) << \": \" << SDL_GetError();\n                    else\n                    {\n                        mGyroscope = sensor;\n                        break;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (mGyroscope != nullptr)\n            {\n                SDL_SensorClose(mGyroscope);\n                mGyroscope = nullptr;\n                mGyroXSpeed = mGyroYSpeed = 0.f;\n                mGyroUpdateTimer = 0.f;\n            }\n        }\n    }\n\n    void SensorManager::processChangedSettings(const Settings::CategorySettingVector& changed)\n    {\n        for (const auto& setting : changed)\n        {\n            if (setting.first == \"Input\" && setting.second == \"invert x axis\")\n                mInvertX = Settings::Manager::getBool(\"invert x axis\", \"Input\");\n\n            if (setting.first == \"Input\" && setting.second == \"invert y axis\")\n                mInvertY = Settings::Manager::getBool(\"invert y axis\", \"Input\");\n\n            if (setting.first == \"Input\" && setting.second == \"gyro horizontal sensitivity\")\n                mGyroHSensitivity = Settings::Manager::getFloat(\"gyro horizontal sensitivity\", \"Input\");\n\n            if (setting.first == \"Input\" && setting.second == \"gyro vertical sensitivity\")\n                mGyroVSensitivity = Settings::Manager::getFloat(\"gyro vertical sensitivity\", \"Input\");\n\n            if (setting.first == \"Input\" && setting.second == \"enable gyroscope\")\n                init();\n\n            if (setting.first == \"Input\" && setting.second == \"gyro horizontal axis\")\n                correctGyroscopeAxes();\n\n            if (setting.first == \"Input\" && setting.second == \"gyro vertical axis\")\n                correctGyroscopeAxes();\n\n            if (setting.first == \"Input\" && setting.second == \"gyro input threshold\")\n                mGyroInputThreshold = Settings::Manager::getFloat(\"gyro input threshold\", \"Input\");\n        }\n    }\n\n    float SensorManager::getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const\n    {\n        switch (axis)\n        {\n            case GyroscopeAxis::X:\n            case GyroscopeAxis::Y:\n            case GyroscopeAxis::Z:\n                return std::abs(arg.data[0]) >= mGyroInputThreshold ? arg.data[axis-1] : 0.f;\n            case GyroscopeAxis::Minus_X:\n            case GyroscopeAxis::Minus_Y:\n            case GyroscopeAxis::Minus_Z:\n                return std::abs(arg.data[0]) >= mGyroInputThreshold ? -arg.data[std::abs(axis)-1] : 0.f;\n            default:\n                return 0.f;\n        }\n    }\n\n    void SensorManager::displayOrientationChanged()\n    {\n        correctGyroscopeAxes();\n    }\n\n    void SensorManager::sensorUpdated(const SDL_SensorEvent &arg)\n    {\n        if (!Settings::Manager::getBool(\"enable gyroscope\", \"Input\"))\n            return;\n\n        SDL_Sensor *sensor = SDL_SensorFromInstanceID(arg.which);\n        if (!sensor)\n        {\n            Log(Debug::Info) << \"Couldn't get sensor for sensor event\";\n            return;\n        }\n\n        switch (SDL_SensorGetType(sensor))\n        {\n            case SDL_SENSOR_ACCEL:\n                break;\n            case SDL_SENSOR_GYRO:\n            {\n                mGyroXSpeed = getGyroAxisSpeed(mGyroHAxis, arg);\n                mGyroYSpeed = getGyroAxisSpeed(mGyroVAxis, arg);\n                mGyroUpdateTimer = 0.f;\n\n                break;\n        }\n        default:\n            break;\n        }\n    }\n\n    void SensorManager::update(float dt)\n    {\n        if (mGyroXSpeed == 0.f && mGyroYSpeed == 0.f)\n            return;\n\n        if (mGyroUpdateTimer > 0.5f)\n        {\n            // More than half of second passed since the last gyroscope update.\n            // A device more likely was disconnected or switched to the sleep mode.\n            // Reset current rotation speed and wait for update.\n            mGyroXSpeed = 0.f;\n            mGyroYSpeed = 0.f;\n            mGyroUpdateTimer = 0.f;\n            return;\n        }\n\n        mGyroUpdateTimer += dt;\n\n        if (!mGuiCursorEnabled)\n        {\n            float rot[3];\n            rot[0] = -mGyroYSpeed * dt * mGyroVSensitivity * 4 * (mInvertY ? -1 : 1);\n            rot[1] = 0.0f;\n            rot[2] = -mGyroXSpeed * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1);\n\n            // Only actually turn player when we're not in vanity mode\n            bool playerLooking = MWBase::Environment::get().getInputManager()->getControlSwitch(\"playerlooking\");\n            if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && playerLooking)\n            {\n                MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n                player.yaw(-rot[2]);\n                player.pitch(-rot[0]);\n            }\n            else if (!playerLooking)\n                MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();\n\n            MWBase::Environment::get().getInputManager()->resetIdleTime();\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwinput/sensormanager.hpp",
    "content": "#ifndef MWINPUT_MWSENSORMANAGER_H\n#define MWINPUT_MWSENSORMANAGER_H\n\n#include <SDL_sensor.h>\n\n#include <components/settings/settings.hpp>\n#include <components/sdlutil/events.hpp>\n\nnamespace SDLUtil\n{\n    class InputWrapper;\n}\n\nnamespace MWWorld\n{\n    class Player;\n}\n\nnamespace MWInput\n{\n    class SensorManager : public SDLUtil::SensorListener\n    {\n    public:\n        SensorManager();\n\n        virtual ~SensorManager();\n\n        void init();\n\n        void update(float dt);\n\n        void sensorUpdated(const SDL_SensorEvent &arg) override;\n        void displayOrientationChanged() override;\n        void processChangedSettings(const Settings::CategorySettingVector& changed);\n\n        void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }\n\n    private:\n        enum GyroscopeAxis\n        {\n            Unknown = 0,\n            X = 1,\n            Y = 2,\n            Z = 3,\n            Minus_X = -1,\n            Minus_Y = -2,\n            Minus_Z = -3\n        };\n\n        void updateSensors();\n        void correctGyroscopeAxes();\n        GyroscopeAxis mapGyroscopeAxis(const std::string& axis);\n        float getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const;\n\n        bool mInvertX;\n        bool mInvertY;\n\n        float mGyroXSpeed;\n        float mGyroYSpeed;\n        float mGyroUpdateTimer;\n\n        float mGyroHSensitivity;\n        float mGyroVSensitivity;\n        GyroscopeAxis mGyroHAxis;\n        GyroscopeAxis mGyroVAxis;\n        float mGyroInputThreshold;\n\n        SDL_Sensor* mGyroscope;\n\n        bool mGuiCursorEnabled;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/activespells.cpp",
    "content": "#include \"activespells.hpp\"\n\n#include <components/misc/rng.hpp>\n#include <components/misc/stringops.hpp>\n\n#include <components/esm/loadmgef.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace MWMechanics\n{\n    void ActiveSpells::update(float duration) const\n    {\n        bool rebuild = false;\n\n        // Erase no longer active spells and effects\n        if (duration > 0)\n        {\n            TContainer::iterator iter (mSpells.begin());\n            while (iter!=mSpells.end())\n            {\n                if (!timeToExpire (iter))\n                {\n                    /*\n                        Start of tes3mp addition\n\n                        Whenever the local player loses an active spell, send an ID_PLAYER_SPELLS_ACTIVE packet to the server with it\n\n                        Whenever a local actor loses an active spell, send an ID_ACTOR_SPELLS_ACTIVE packet to the server with it\n                    */\n                    if (this == &MWMechanics::getPlayer().getClass().getCreatureStats(MWMechanics::getPlayer()).getActiveSpells())\n                    {\n                        mwmp::Main::get().getLocalPlayer()->sendSpellsActiveRemoval(iter->first,\n                            MechanicsHelper::isStackingSpell(iter->first), iter->second.mTimeStamp);\n                    }\n                    else\n                    {\n                        MWWorld::Ptr actorPtr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(getActorId());\n\n                        if (mwmp::Main::get().getCellController()->isLocalActor(actorPtr))\n                            mwmp::Main::get().getCellController()->getLocalActor(actorPtr)->sendSpellsActiveRemoval(iter->first,\n                                MechanicsHelper::isStackingSpell(iter->first), iter->second.mTimeStamp);\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    mSpells.erase (iter++);\n                    rebuild = true;\n                }\n                else\n                {\n                    bool interrupt = false;\n                    std::vector<ActiveEffect>& effects = iter->second.mEffects;\n                    for (std::vector<ActiveEffect>::iterator effectIt = effects.begin(); effectIt != effects.end();)\n                    {\n                        if (effectIt->mTimeLeft <= 0)\n                        {\n                            rebuild = true;\n\n                            // Note: it we expire a Corprus effect, we should remove the whole spell.\n                            if (effectIt->mEffectId == ESM::MagicEffect::Corprus)\n                            {\n                                iter = mSpells.erase (iter);\n                                interrupt = true;\n                                break;\n                            }\n\n                            effectIt = effects.erase(effectIt);\n                        }\n                        else\n                        {\n                            effectIt->mTimeLeft -= duration;\n                            ++effectIt;\n                        }\n                    }\n\n                    if (!interrupt)\n                        ++iter;\n                }\n            }\n        }\n\n        if (mSpellsChanged)\n        {\n            mSpellsChanged = false;\n            rebuild = true;\n        }\n\n        if (rebuild)\n            rebuildEffects();\n    }\n\n    void ActiveSpells::rebuildEffects() const\n    {\n        mEffects = MagicEffects();\n\n        for (TIterator iter (begin()); iter!=end(); ++iter)\n        {\n            const std::vector<ActiveEffect>& effects = iter->second.mEffects;\n\n            for (std::vector<ActiveEffect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)\n            {\n                if (effectIt->mTimeLeft > 0)\n                    mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude));\n            }\n        }\n    }\n\n    ActiveSpells::ActiveSpells()\n        : mSpellsChanged (false)\n    {}\n\n    const MagicEffects& ActiveSpells::getMagicEffects() const\n    {\n        update(0.f);\n        return mEffects;\n    }\n\n    ActiveSpells::TIterator ActiveSpells::begin() const\n    {\n        return mSpells.begin();\n    }\n\n    ActiveSpells::TIterator ActiveSpells::end() const\n    {\n        return mSpells.end();\n    }\n\n    double ActiveSpells::timeToExpire (const TIterator& iterator) const\n    {\n        const std::vector<ActiveEffect>& effects = iterator->second.mEffects;\n\n        float duration = 0;\n\n        for (std::vector<ActiveEffect>::const_iterator iter (effects.begin());\n            iter!=effects.end(); ++iter)\n        {\n            if (iter->mTimeLeft > duration)\n                duration = iter->mTimeLeft;\n        }\n\n        if (duration < 0)\n            return 0;\n\n        return duration;\n    }\n\n    bool ActiveSpells::isSpellActive(const std::string& id) const\n    {\n        for (TContainer::iterator iter = mSpells.begin(); iter != mSpells.end(); ++iter)\n        {\n            if (Misc::StringUtils::ciEqual(iter->first, id))\n                return true;\n        }\n        return false;\n    }\n\n    const ActiveSpells::TContainer& ActiveSpells::getActiveSpells() const\n    {\n        return mSpells;\n    }\n\n    /*\n        Start of tes3mp change (major)\n\n        Add a timestamp argument so spells received from other clients can have the same timestamps they had there,\n        as well as a sendPacket argument used to prevent packets from being sent back to the server when we've just\n        received them from it\n    */\n    void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<ActiveEffect> effects,\n                                const std::string &displayName, int casterActorId, MWWorld::TimeStamp timestamp, bool sendPacket)\n    /*\n        End of tes3mp change (major)\n    */\n    {\n        TContainer::iterator it(mSpells.find(id));\n\n        ActiveSpellParams params;\n        params.mEffects = effects;\n        params.mDisplayName = displayName;\n        params.mCasterActorId = casterActorId;\n\n        /*\n            Start of tes3mp addition\n\n            Track the timestamp of this active spell so that, if spells are stacked, the correct one can be removed\n        */\n        params.mTimeStamp = timestamp;\n        /*\n            End of tes3mp addition\n        */\n\n        if (it == end() || stack)\n        {\n            mSpells.insert(std::make_pair(id, params));\n        }\n        else\n        {\n            // addSpell() is called with effects for a range.\n            // but a spell may have effects with different ranges (e.g. Touch & Target)\n            // so, if we see new effects for same spell assume additional \n            // spell effects and add to existing effects of spell\n            mergeEffects(params.mEffects, it->second.mEffects);\n            it->second = params;\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Whenever a player gains an active spell as a result of gameplay, send an ID_PLAYER_SPELLS_ACTIVE packet\n            to the server with it\n        */\n        if (sendPacket)\n        {\n            if (this == &MWMechanics::getPlayer().getClass().getCreatureStats(MWMechanics::getPlayer()).getActiveSpells())\n            {\n                mwmp::Main::get().getLocalPlayer()->sendSpellsActiveAddition(id, stack, params);\n            }\n            else\n            {\n                MWWorld::Ptr actorPtr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(getActorId());\n\n                if (mwmp::Main::get().getCellController()->isLocalActor(actorPtr))\n                    mwmp::Main::get().getCellController()->getLocalActor(actorPtr)->sendSpellsActiveAddition(id, stack, params);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        mSpellsChanged = true;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Declare addSpell() without the timestamp argument and make it call the version with that argument,\n        using the current time for the timestamp\n    */\n    void ActiveSpells::addSpell(const std::string& id, bool stack, std::vector<ActiveEffect> effects,\n                                const std::string& displayName, int casterActorId)\n    {\n        MWWorld::TimeStamp timestamp = MWBase::Environment::get().getWorld()->getTimeStamp();\n\n        addSpell(id, stack, effects, displayName, casterActorId, timestamp);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void ActiveSpells::mergeEffects(std::vector<ActiveEffect>& addTo, const std::vector<ActiveEffect>& from)\n    {\n        for (std::vector<ActiveEffect>::const_iterator effect(from.begin()); effect != from.end(); ++effect)\n        {\n            // if effect is not in addTo, add it\n            bool missing = true;\n            for (std::vector<ActiveEffect>::const_iterator iter(addTo.begin()); iter != addTo.end(); ++iter)\n            {\n                if ((effect->mEffectId == iter->mEffectId) && (effect->mArg == iter->mArg))\n                {\n                    missing = false;\n                    break;\n                }\n            }\n            if (missing)\n            {\n                addTo.push_back(*effect);\n            }\n        }\n    }\n\n    void ActiveSpells::removeEffects(const std::string &id)\n    {\n        for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell)\n        {\n            if (spell->first == id)\n            {\n                spell->second.mEffects.clear();\n                mSpellsChanged = true;\n            }\n        }\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Remove the spell with a certain ID and a certain timestamp, useful\n        when there are stacked spells with the same ID\n\n        Returns a boolean that indicates whether the corresponding spell was found\n    */\n    bool ActiveSpells::removeSpellByTimestamp(const std::string& id, MWWorld::TimeStamp timestamp)\n    {\n        for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell)\n        {\n            if (spell->first == id)\n            {\n                if (spell->second.mTimeStamp == timestamp)\n                {\n                    spell->second.mEffects.clear();\n                    mSpellsChanged = true;\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const\n    {\n        for (TContainer::const_iterator it = begin(); it != end(); ++it)\n        {\n            for (std::vector<ActiveEffect>::const_iterator effectIt = it->second.mEffects.begin();\n                 effectIt != it->second.mEffects.end(); ++effectIt)\n            {\n                std::string name = it->second.mDisplayName;\n\n                float magnitude = effectIt->mMagnitude;\n                if (magnitude)\n                    visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), effectIt->mEffectIndex, name, it->first, it->second.mCasterActorId, magnitude, effectIt->mTimeLeft, effectIt->mDuration);\n            }\n        }\n    }\n\n    void ActiveSpells::purgeAll(float chance, bool spellOnly)\n    {\n        for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); )\n        {\n            const std::string spellId = it->first;\n\n            // if spellOnly is true, dispell only spells. Leave potions, enchanted items etc.\n            if (spellOnly)\n            {\n                const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spellId);\n                if (!spell || spell->mData.mType != ESM::Spell::ST_Spell)\n                {\n                    ++it;\n                    continue;\n                }\n            }\n\n            if (Misc::Rng::roll0to99() < chance)\n                mSpells.erase(it++);\n            else\n                ++it;\n        }\n        mSpellsChanged = true;\n    }\n\n    void ActiveSpells::purgeEffect(short effectId)\n    {\n        for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)\n        {\n            for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();\n                 effectIt != it->second.mEffects.end();)\n            {\n                if (effectIt->mEffectId == effectId)\n                    effectIt = it->second.mEffects.erase(effectIt);\n                else\n                    ++effectIt;\n            }\n        }\n        mSpellsChanged = true;\n    }\n\n    void ActiveSpells::purgeEffect(short effectId, const std::string& sourceId, int effectIndex)\n    {\n        for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)\n        {\n            for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();\n                 effectIt != it->second.mEffects.end();)\n            {\n                if (effectIt->mEffectId == effectId && it->first == sourceId && (effectIndex < 0 || effectIndex == effectIt->mEffectIndex))\n                    effectIt = it->second.mEffects.erase(effectIt);\n                else\n                    ++effectIt;\n            }\n        }\n        mSpellsChanged = true;\n    }\n\n    void ActiveSpells::purge(int casterActorId)\n    {\n        for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)\n        {\n            for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();\n                 effectIt != it->second.mEffects.end();)\n            {\n                if (it->second.mCasterActorId == casterActorId)\n                    effectIt = it->second.mEffects.erase(effectIt);\n                else\n                    ++effectIt;\n            }\n        }\n        mSpellsChanged = true;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the purging of an effect for a specific arg (attribute or skill)\n    */\n    void ActiveSpells::purgeEffectByArg(short effectId, int effectArg)\n    {\n        for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)\n        {\n            for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();\n                effectIt != it->second.mEffects.end();)\n            {\n                if (effectIt->mEffectId == effectId && effectIt->mArg == effectArg)\n                    effectIt = it->second.mEffects.erase(effectIt);\n                else\n                    ++effectIt;\n            }\n        }\n        mSpellsChanged = true;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it easy to get an effect's duration\n    */\n    float ActiveSpells::getEffectDuration(short effectId, std::string sourceId)\n    {\n        for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)\n        {\n            if (sourceId.compare(it->first) == 0)\n            {\n                for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();\n                    effectIt != it->second.mEffects.end(); ++effectIt)\n                {\n                    if (effectIt->mEffectId == effectId)\n                        return effectIt->mDuration;\n                }\n            }\n        }\n        return 0.f;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void ActiveSpells::purgeCorprusDisease()\n    {\n        for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)\n        {\n            bool hasCorprusEffect = false;\n            for (std::vector<ActiveEffect>::iterator effectIt = iter->second.mEffects.begin();\n                 effectIt != iter->second.mEffects.end();++effectIt)\n            {\n                if (effectIt->mEffectId == ESM::MagicEffect::Corprus)\n                {\n                    hasCorprusEffect = true;\n                    break;\n                }\n            }\n\n            if (hasCorprusEffect)\n            {\n                mSpells.erase(iter++);\n                mSpellsChanged = true;\n            }\n            else\n                ++iter;\n        }\n    }\n\n    void ActiveSpells::clear()\n    {\n        mSpells.clear();\n        mSpellsChanged = true;\n    }\n\n    void ActiveSpells::writeState(ESM::ActiveSpells &state) const\n    {\n        for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)\n        {\n            // Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp\n            ESM::ActiveSpells::ActiveSpellParams params;\n            params.mEffects = it->second.mEffects;\n            params.mCasterActorId = it->second.mCasterActorId;\n            params.mDisplayName = it->second.mDisplayName;\n\n            state.mSpells.insert (std::make_pair(it->first, params));\n        }\n    }\n\n    void ActiveSpells::readState(const ESM::ActiveSpells &state)\n    {\n        for (ESM::ActiveSpells::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it)\n        {\n            // Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp\n            ActiveSpellParams params;\n            params.mEffects = it->second.mEffects;\n            params.mCasterActorId = it->second.mCasterActorId;\n            params.mDisplayName = it->second.mDisplayName;\n\n            mSpells.insert (std::make_pair(it->first, params));\n            mSpellsChanged = true;\n        }\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set and get the actorId for these ActiveSpells\n    */\n    int ActiveSpells::getActorId() const\n    {\n        return mActorId;\n    }\n\n    void ActiveSpells::setActorId(int actorId)\n    {\n        mActorId = actorId;\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/activespells.hpp",
    "content": "#ifndef GAME_MWMECHANICS_ACTIVESPELLS_H\n#define GAME_MWMECHANICS_ACTIVESPELLS_H\n\n#include <map>\n#include <vector>\n#include <string>\n\n#include <components/esm/activespells.hpp>\n\n#include \"../mwworld/timestamp.hpp\"\n\n#include \"magiceffects.hpp\"\n\nnamespace MWMechanics\n{\n    /// \\brief Lasting spell effects\n    ///\n    /// \\note The name of this class is slightly misleading, since it also handels lasting potion\n    /// effects.\n    class ActiveSpells\n    {\n        public:\n\n            typedef ESM::ActiveEffect ActiveEffect;\n\n            struct ActiveSpellParams\n            {\n                std::vector<ActiveEffect> mEffects;\n                MWWorld::TimeStamp mTimeStamp;\n                std::string mDisplayName;\n\n                // The caster that inflicted this spell on us\n                int mCasterActorId;\n            };\n\n            typedef std::multimap<std::string, ActiveSpellParams > TContainer;\n            typedef TContainer::const_iterator TIterator;\n\n            void readState (const ESM::ActiveSpells& state);\n            void writeState (ESM::ActiveSpells& state) const;\n\n            TIterator begin() const;\n\n            TIterator end() const;\n\n            void update(float duration) const;\n\n        private:\n\n            mutable TContainer mSpells;\n            mutable MagicEffects mEffects;\n            mutable bool mSpellsChanged;\n\n            /*\n                Start of tes3mp addition\n\n                Track the actorId corresponding to these ActiveSpells\n            */\n            int mActorId;\n            /*\n                End of tes3mp addition\n            */\n\n            void rebuildEffects() const;\n\n            /// Add any effects that are in \"from\" and not in \"addTo\" to \"addTo\"\n            void mergeEffects(std::vector<ActiveEffect>& addTo, const std::vector<ActiveEffect>& from);\n\n            double timeToExpire (const TIterator& iterator) const;\n            ///< Returns time (in in-game hours) until the spell pointed to by \\a iterator\n            /// expires.\n\n            const TContainer& getActiveSpells() const;\n\n        public:\n\n            ActiveSpells();\n\n            /// Add lasting effects\n            ///\n            /// \\brief addSpell\n            /// \\param id ID for stacking purposes.\n            /// \\param stack If false, the spell is not added if one with the same ID exists already.\n            /// \\param effects\n            /// \\param displayName Name for display in magic menu.\n            ///\n            void addSpell (const std::string& id, bool stack, std::vector<ActiveEffect> effects,\n                           const std::string& displayName, int casterActorId);\n\n            /*\n                Start of tes3mp addition\n\n                Add a separate addSpell() with a timestamp argument\n            */\n            void addSpell (const std::string& id, bool stack, std::vector<ActiveEffect> effects,\n                           const std::string& displayName, int casterActorId, MWWorld::TimeStamp timestamp, bool sendPacket = true);\n            /*\n                End of tes3mp addition\n            */\n\n            /// Removes the active effects from this spell/potion/.. with \\a id\n            void removeEffects (const std::string& id);\n\n            /*\n                Start of tes3mp addition\n\n                Remove the spell with a certain ID and a certain timestamp, useful\n                when there are stacked spells with the same ID\n            */\n            bool removeSpellByTimestamp(const std::string& id, MWWorld::TimeStamp timestamp);\n            /*\n                End of tes3mp addition\n            */\n\n            /// Remove all active effects with this effect id\n            void purgeEffect (short effectId);\n\n            /// Remove all active effects with this effect id and source id\n            void purgeEffect (short effectId, const std::string& sourceId, int effectIndex=-1);\n\n            /// Remove all active effects, if roll succeeds (for each effect)\n            void purgeAll(float chance, bool spellOnly = false);\n\n            /// Remove all effects with CASTER_LINKED flag that were cast by \\a casterActorId\n            void purge (int casterActorId);\n\n            /*\n                Start of tes3mp addition\n\n                Allow the purging of an effect for a specific arg (attribute or skill)\n            */\n            void purgeEffectByArg(short effectId, int effectArg);\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it easy to get an effect's duration\n            */\n            float getEffectDuration(short effectId, std::string sourceId);\n            /*\n                End of tes3mp addition\n            */\n\n            /// Remove all spells\n            void clear();\n\n            bool isSpellActive (const std::string& id) const;\n            ///< case insensitive\n\n            void purgeCorprusDisease();\n\n            const MagicEffects& getMagicEffects() const;\n\n            void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set and get the actorId for these ActiveSpells\n            */\n            int getActorId() const;\n            void setActorId(int actorId);\n            /*\n                End of tes3mp addition\n            */\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/actor.cpp",
    "content": "#include \"actor.hpp\"\n\n#include \"character.hpp\"\n\nnamespace MWMechanics\n{\n    Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)\n    {\n        mCharacterController.reset(new CharacterController(ptr, animation));\n    }\n\n    void Actor::updatePtr(const MWWorld::Ptr &newPtr)\n    {\n        mCharacterController->updatePtr(newPtr);\n    }\n\n    CharacterController* Actor::getCharacterController()\n    {\n        return mCharacterController.get();\n    }\n\n    int Actor::getGreetingTimer() const\n    {\n        return mGreetingTimer;\n    }\n\n    void Actor::setGreetingTimer(int timer)\n    {\n        mGreetingTimer = timer;\n    }\n\n    float Actor::getAngleToPlayer() const\n    {\n        return mTargetAngleRadians;\n    }\n\n    void Actor::setAngleToPlayer(float angle)\n    {\n        mTargetAngleRadians = angle;\n    }\n\n    GreetingState Actor::getGreetingState() const\n    {\n        return mGreetingState;\n    }\n\n    void Actor::setGreetingState(GreetingState state)\n    {\n        mGreetingState = state;\n    }\n\n    bool Actor::isTurningToPlayer() const\n    {\n        return mIsTurningToPlayer;\n    }\n\n    void Actor::setTurningToPlayer(bool turning)\n    {\n        mIsTurningToPlayer = turning;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/actor.hpp",
    "content": "#ifndef OPENMW_MECHANICS_ACTOR_H\n#define OPENMW_MECHANICS_ACTOR_H\n\n#include <memory>\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include <components/misc/timer.hpp>\n\nnamespace MWRender\n{\n    class Animation;\n}\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    class CharacterController;\n\n    /// @brief Holds temporary state for an actor that will be discarded when the actor leaves the scene.\n    class Actor\n    {\n    public:\n        Actor(const MWWorld::Ptr& ptr, MWRender::Animation* animation);\n\n        /// Notify this actor of its new base object Ptr, use when the object changed cells\n        void updatePtr(const MWWorld::Ptr& newPtr);\n\n        CharacterController* getCharacterController();\n\n        int getGreetingTimer() const;\n        void setGreetingTimer(int timer);\n\n        float getAngleToPlayer() const;\n        void setAngleToPlayer(float angle);\n\n        GreetingState getGreetingState() const;\n        void setGreetingState(GreetingState state);\n\n        bool isTurningToPlayer() const;\n        void setTurningToPlayer(bool turning);\n\n        Misc::TimerStatus updateEngageCombatTimer(float duration)\n        {\n            return mEngageCombat.update(duration);\n        }\n\n    private:\n        std::unique_ptr<CharacterController> mCharacterController;\n        int mGreetingTimer{0};\n        float mTargetAngleRadians{0.f};\n        GreetingState mGreetingState{Greet_None};\n        bool mIsTurningToPlayer{false};\n        Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f)};\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/actors.cpp",
    "content": "#include \"actors.hpp\"\n\n#include <optional>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/misc/rng.hpp>\n#include <components/misc/mathutil.hpp>\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Utils.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/DedicatedPlayer.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n\n#include \"../mwmechanics/aibreathe.hpp\"\n\n#include \"../mwrender/vismask.hpp\"\n\n#include \"spellcasting.hpp\"\n#include \"steering.hpp\"\n#include \"npcstats.hpp\"\n#include \"creaturestats.hpp\"\n#include \"movement.hpp\"\n#include \"character.hpp\"\n#include \"aicombat.hpp\"\n#include \"aicombataction.hpp\"\n#include \"aifollow.hpp\"\n#include \"aipursue.hpp\"\n#include \"aiwander.hpp\"\n#include \"actor.hpp\"\n#include \"summoning.hpp\"\n#include \"actorutil.hpp\"\n#include \"tickableeffects.hpp\"\n\nnamespace\n{\n\nbool isConscious(const MWWorld::Ptr& ptr)\n{\n    const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n    return !stats.isDead() && !stats.getKnockedDown();\n}\n\nint getBoundItemSlot (const std::string& itemId)\n{\n    static std::map<std::string, int> boundItemsMap;\n    if (boundItemsMap.empty())\n    {\n        std::string boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sMagicBoundBootsID\")->mValue.getString();\n        boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Boots;\n\n        boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sMagicBoundCuirassID\")->mValue.getString();\n        boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Cuirass;\n\n        boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sMagicBoundLeftGauntletID\")->mValue.getString();\n        boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_LeftGauntlet;\n\n        boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sMagicBoundRightGauntletID\")->mValue.getString();\n        boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_RightGauntlet;\n\n        boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sMagicBoundHelmID\")->mValue.getString();\n        boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Helmet;\n\n        boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sMagicBoundShieldID\")->mValue.getString();\n        boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_CarriedLeft;\n    }\n\n    int slot = MWWorld::InventoryStore::Slot_CarriedRight;\n    std::map<std::string, int>::iterator it = boundItemsMap.find(itemId);\n    if (it != boundItemsMap.end())\n        slot = it->second;\n\n    return slot;\n}\n\nclass CheckActorCommanded : public MWMechanics::EffectSourceVisitor\n{\n    MWWorld::Ptr mActor;\npublic:\n    bool mCommanded;\n\n    CheckActorCommanded(const MWWorld::Ptr& actor)\n        : mActor(actor)\n        , mCommanded(false){}\n\n    void visit (MWMechanics::EffectKey key, int effectIndex,\n                        const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                        float magnitude, float remainingTime = -1, float totalTime = -1) override\n    {\n        if (((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc())\n            || (key.mId == ESM::MagicEffect::CommandCreature && mActor.getTypeName() == typeid(ESM::Creature).name()))\n            && magnitude >= mActor.getClass().getCreatureStats(mActor).getLevel())\n                mCommanded = true;\n    }\n};\n\n// Check for command effects having ended and remove package if necessary\nvoid adjustCommandedActor (const MWWorld::Ptr& actor)\n{\n    CheckActorCommanded check(actor);\n    MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n    stats.getActiveSpells().visitEffectSources(check);\n\n    bool hasCommandPackage = false;\n\n    auto it = stats.getAiSequence().begin();\n    for (; it != stats.getAiSequence().end(); ++it)\n    {\n        if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Follow &&\n                static_cast<const MWMechanics::AiFollow*>(it->get())->isCommanded())\n        {\n            hasCommandPackage = true;\n            break;\n        }\n    }\n\n    if (!check.mCommanded && hasCommandPackage)\n        stats.getAiSequence().erase(it);\n}\n\nvoid getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka)\n{\n    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n    const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n    float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();\n    health = 0.1f * endurance;\n\n    float fRestMagicMult = settings.find(\"fRestMagicMult\")->mValue.getFloat ();\n    magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();\n}\n\ntemplate<class T>\nvoid forEachFollowingPackage(MWMechanics::Actors::PtrActorMap& actors, const MWWorld::Ptr& actor, const MWWorld::Ptr& player, T&& func)\n{\n    for(auto& iter : actors)\n    {\n        const MWWorld::Ptr &iteratedActor = iter.first;\n        if (iteratedActor == player || iteratedActor == actor)\n            continue;\n\n        const MWMechanics::CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);\n        if (stats.isDead())\n            continue;\n\n        // An actor counts as following if AiFollow is the current AiPackage,\n        // or there are only Combat and Wander packages before the AiFollow package\n        for (const auto& package : stats.getAiSequence())\n        {\n            if(!func(iter, package))\n                break;\n        }\n    }\n}\n\n}\n\nnamespace MWMechanics\n{\n    static const int GREETING_SHOULD_START = 4; // how many updates should pass before NPC can greet player\n    static const int GREETING_SHOULD_END = 20;  // how many updates should pass before NPC stops turning to player\n    static const int GREETING_COOLDOWN = 40;    // how many updates should pass before NPC can continue movement\n    static const float DECELERATE_DISTANCE = 512.f;\n\n    namespace\n    {\n        float getTimeToDestination(const AiPackage& package, const osg::Vec3f& position, float speed, float duration, const osg::Vec3f& halfExtents)\n        {\n            const auto distanceToNextPathPoint = (package.getNextPathPoint(package.getDestination()) - position).length();\n            return (distanceToNextPathPoint - package.getNextPathPointTolerance(speed, duration, halfExtents)) / speed;\n        }\n    }\n\n    class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor\n    {\n    public:\n        float mRemainingTime;\n\n        GetStuntedMagickaDuration(const MWWorld::Ptr& actor)\n            : mRemainingTime(0.f){}\n\n        void visit (MWMechanics::EffectKey key, int effectIndex,\n                            const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                            float magnitude, float remainingTime = -1, float totalTime = -1) override\n        {\n            if (mRemainingTime == -1) return;\n\n            if (key.mId == ESM::MagicEffect::StuntedMagicka)\n            {\n                if (totalTime == -1)\n                {\n                    mRemainingTime = -1;\n                    return;\n                }\n\n                if (remainingTime > mRemainingTime)\n                    mRemainingTime = remainingTime;\n            }\n        }\n    };\n\n    class GetCurrentMagnitudes : public MWMechanics::EffectSourceVisitor\n    {\n        std::string mSpellId;\n\n    public:\n        GetCurrentMagnitudes(const std::string& spellId)\n            : mSpellId(spellId)\n        {\n        }\n\n        void visit (MWMechanics::EffectKey key, int effectIndex,\n                            const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                            float magnitude, float remainingTime = -1, float totalTime = -1) override\n        {\n            if (magnitude <= 0)\n                return;\n\n            if (sourceId != mSpellId)\n                return;\n\n            mMagnitudes.emplace_back(key, magnitude);\n        }\n\n        std::vector<std::pair<MWMechanics::EffectKey, float>> mMagnitudes;\n    };\n\n    class GetCorprusSpells : public MWMechanics::EffectSourceVisitor\n    {\n\n    public:\n        void visit (MWMechanics::EffectKey key, int effectIndex,\n                            const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                            float magnitude, float remainingTime = -1, float totalTime = -1) override\n        {\n            if (key.mId != ESM::MagicEffect::Corprus)\n                return;\n\n            mSpells.push_back(sourceId);\n        }\n\n        std::vector<std::string> mSpells;\n    };\n\n    class SoulTrap : public MWMechanics::EffectSourceVisitor\n    {\n        MWWorld::Ptr mCreature;\n        MWWorld::Ptr mActor;\n        bool mTrapped;\n    public:\n        SoulTrap(const MWWorld::Ptr& trappedCreature)\n            : mCreature(trappedCreature)\n            , mTrapped(false)\n        {\n        }\n\n        void visit (MWMechanics::EffectKey key, int effectIndex,\n                            const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                            float magnitude, float remainingTime = -1, float totalTime = -1) override\n        {\n            if (mTrapped)\n                return;\n            if (key.mId != ESM::MagicEffect::Soultrap)\n                return;\n            if (magnitude <= 0)\n                return;\n\n            MWBase::World* world = MWBase::Environment::get().getWorld();\n\n            MWWorld::Ptr caster = world->searchPtrViaActorId(casterActorId);\n            if (caster.isEmpty() || !caster.getClass().isActor())\n                return;\n\n            static const float fSoulgemMult = world->getStore().get<ESM::GameSetting>().find(\"fSoulgemMult\")->mValue.getFloat();\n\n            int creatureSoulValue = mCreature.get<ESM::Creature>()->mBase->mData.mSoul;\n            if (creatureSoulValue == 0)\n                return;\n\n            // Use the smallest soulgem that is large enough to hold the soul\n            MWWorld::ContainerStore& container = caster.getClass().getContainerStore(caster);\n            MWWorld::ContainerStoreIterator gem = container.end();\n            float gemCapacity = std::numeric_limits<float>::max();\n            std::string soulgemFilter = \"misc_soulgem\"; // no other way to check for soulgems? :/\n            for (MWWorld::ContainerStoreIterator it = container.begin(MWWorld::ContainerStore::Type_Miscellaneous);\n                 it != container.end(); ++it)\n            {\n                const std::string& id = it->getCellRef().getRefId();\n                if (id.size() >= soulgemFilter.size()\n                        && id.substr(0,soulgemFilter.size()) == soulgemFilter)\n                {\n                    float thisGemCapacity = it->get<ESM::Miscellaneous>()->mBase->mData.mValue * fSoulgemMult;\n                    if (thisGemCapacity >= creatureSoulValue && thisGemCapacity < gemCapacity\n                            && it->getCellRef().getSoul().empty())\n                    {\n                        gem = it;\n                        gemCapacity = thisGemCapacity;\n                    }\n                }\n            }\n\n            if (gem == container.end())\n                return;\n\n            // Set the soul on just one of the gems, not the whole stack\n            gem->getContainerStore()->unstack(*gem, caster);\n\n            /*\n                Start of tes3mp change (minor)\n\n                Send PlayerInventory packets that replace the original gem with the new one\n            */\n            mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n            localPlayer->sendItemChange(*gem, 1, mwmp::InventoryChanges::REMOVE);\n\n            gem->getCellRef().setSoul(mCreature.getCellRef().getRefId());\n\n            localPlayer->sendItemChange(*gem, 1, mwmp::InventoryChanges::ADD);\n            /*\n                End of tes3mp change (minor)\n            */\n\n            // Restack the gem with other gems with the same soul\n            gem->getContainerStore()->restack(*gem);\n\n            mTrapped = true;\n\n            if (caster == getPlayer())\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sSoultrapSuccess}\");\n\n            const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()\n                    .search(\"VFX_Soul_Trap\");\n            if (fx)\n                MWBase::Environment::get().getWorld()->spawnEffect(\"meshes\\\\\" + fx->mModel,\n                    \"\", mCreature.getRefData().getPosition().asVec3());\n\n            MWBase::Environment::get().getSoundManager()->playSound3D(\n                mCreature.getRefData().getPosition().asVec3(), \"conjuration hit\", 1.f, 1.f\n            );\n        }\n    };\n\n    void Actors::addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor)\n    {\n        MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);\n        int slot = getBoundItemSlot(itemId);\n\n        if (actor.getClass().getContainerStore(actor).count(itemId) != 0)\n            return;\n\n        MWWorld::ContainerStoreIterator prevItem = store.getSlot(slot);\n\n        MWWorld::Ptr boundPtr = *store.MWWorld::ContainerStore::add(itemId, 1, actor);\n        MWWorld::ActionEquip action(boundPtr);\n        action.execute(actor);\n\n        if (actor != MWMechanics::getPlayer())\n            return;\n\n        MWWorld::Ptr newItem;\n        auto it = store.getSlot(slot);\n        // Equip can fail because beast races cannot equip boots/helmets\n        if(it != store.end())\n            newItem = *it;\n\n        if (newItem.isEmpty() || boundPtr != newItem)\n            return;\n\n        MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n\n        // change draw state only if the item is in player's right hand\n        if (slot == MWWorld::InventoryStore::Slot_CarriedRight)\n            player.setDrawState(MWMechanics::DrawState_Weapon);\n\n        if (prevItem != store.end())\n            player.setPreviousItem(itemId, prevItem->getCellRef().getRefId());\n    }\n\n    void Actors::removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor)\n    {\n        MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);\n        int slot = getBoundItemSlot(itemId);\n\n        MWWorld::ContainerStoreIterator currentItem = store.getSlot(slot);\n\n        bool wasEquipped = currentItem != store.end() && Misc::StringUtils::ciEqual(currentItem->getCellRef().getRefId(), itemId);\n\n        if (actor != MWMechanics::getPlayer())\n        {\n            store.remove(itemId, 1, actor);\n\n            // Equip a replacement\n            if (!wasEquipped)\n                return;\n\n            std::string type = currentItem->getTypeName();\n            if (type != typeid(ESM::Weapon).name() && type != typeid(ESM::Armor).name() && type != typeid(ESM::Clothing).name())\n                return;\n\n            if (actor.getClass().getCreatureStats(actor).isDead())\n                return;\n\n            if (!actor.getClass().hasInventoryStore(actor))\n                return;\n\n            if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n                return;\n\n            actor.getClass().getInventoryStore(actor).autoEquip(actor);\n\n            return;\n        }\n\n        MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();\n        std::string prevItemId = player.getPreviousItem(itemId);\n        player.erasePreviousItem(itemId);\n\n        if (!prevItemId.empty())\n        {\n            // Find previous item (or its replacement) by id.\n            // we should equip previous item only if expired bound item was equipped.\n            MWWorld::Ptr item = store.findReplacement(prevItemId);\n            if (!item.isEmpty() && wasEquipped)\n            {\n                MWWorld::ActionEquip action(item);\n                action.execute(actor);\n            }\n        }\n\n        store.remove(itemId, 1, actor);\n    }\n\n    void Actors::updateActor (const MWWorld::Ptr& ptr, float duration)\n    {\n        // magic effects\n        adjustMagicEffects (ptr);\n        if (ptr.getClass().getCreatureStats(ptr).needToRecalcDynamicStats())\n            calculateDynamicStats (ptr);\n\n        calculateCreatureStatModifiers (ptr, duration);\n        // fatigue restoration\n        calculateRestoration(ptr, duration);\n    }\n\n    void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,\n                                    MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance,\n                                    bool inCombatOrPursue)\n    {\n        if (!actor.getRefData().getBaseNode())\n            return;\n\n        if (targetActor.getClass().getCreatureStats(targetActor).isDead())\n            return;\n\n        static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"fMaxHeadTrackDistance\")->mValue.getFloat();\n        static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"fInteriorHeadTrackMult\")->mValue.getFloat();\n        float maxDistance = fMaxHeadTrackDistance;\n        const ESM::Cell* currentCell = actor.getCell()->getCell();\n        if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx))\n            maxDistance *= fInteriorHeadTrackMult;\n\n        const osg::Vec3f actor1Pos(actor.getRefData().getPosition().asVec3());\n        const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());\n        float sqrDist = (actor1Pos - actor2Pos).length2();\n\n        if (sqrDist > std::min(maxDistance * maxDistance, sqrHeadTrackDistance) && !inCombatOrPursue)\n            return;\n\n        // stop tracking when target is behind the actor\n        osg::Vec3f actorDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);\n        osg::Vec3f targetDirection(actor2Pos - actor1Pos);\n        actorDirection.z() = 0;\n        targetDirection.z() = 0;\n        if ((actorDirection * targetDirection > 0 || inCombatOrPursue)\n            && MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function\n            && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor))\n        {\n            sqrHeadTrackDistance = sqrDist;\n            headTrackTarget = targetActor;\n        }\n    }\n\n    void Actors::playIdleDialogue(const MWWorld::Ptr& actor)\n    {\n        if (!actor.getClass().isActor() || actor == getPlayer() || MWBase::Environment::get().getSoundManager()->sayActive(actor))\n            return;\n\n        const CreatureStats &stats = actor.getClass().getCreatureStats(actor);\n        if (stats.getAiSetting(CreatureStats::AI_Hello).getModified() == 0)\n            return;\n\n        const MWMechanics::AiSequence& seq = stats.getAiSequence();\n        if (seq.isInCombat() || seq.hasPackage(AiPackageTypeId::Follow) || seq.hasPackage(AiPackageTypeId::Escort))\n            return;\n\n        const osg::Vec3f playerPos(getPlayer().getRefData().getPosition().asVec3());\n        const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        if (world->isSwimming(actor) || (playerPos - actorPos).length2() >= 3000 * 3000)\n            return;\n\n        // Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated.\n        // We chose to use the chance MW would have when run at 60 FPS with the default value of the GMST.\n        const float delta = MWBase::Environment::get().getFrameDuration() * 6.f;\n        static const float fVoiceIdleOdds = world->getStore().get<ESM::GameSetting>().find(\"fVoiceIdleOdds\")->mValue.getFloat();\n        if (Misc::Rng::rollProbability() * 10000.f < fVoiceIdleOdds * delta && world->getLOS(getPlayer(), actor))\n            MWBase::Environment::get().getDialogueManager()->say(actor, \"idle\");\n    }\n\n    void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)\n    {\n        if (mSmoothMovement)\n            return;\n\n        CreatureStats &stats = actor.getClass().getCreatureStats(actor);\n        MWMechanics::AiSequence& seq = stats.getAiSequence();\n\n        if (!seq.isEmpty() && seq.getActivePackage().useVariableSpeed())\n        {\n            osg::Vec3f targetPos = seq.getActivePackage().getDestination();\n            osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();\n            float distance = (targetPos - actorPos).length();\n\n            if (distance < DECELERATE_DISTANCE)\n            {\n                float speedCoef = std::max(0.7f, 0.2f + 0.8f * distance / DECELERATE_DISTANCE);\n                auto& movement = actor.getClass().getMovementSettings(actor);\n                movement.mPosition[0] *= speedCoef;\n                movement.mPosition[1] *= speedCoef;\n            }\n        }\n    }\n\n    void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly)\n    {\n        if (!actor.getClass().isActor() || actor == getPlayer())\n            return;\n\n        CreatureStats &stats = actor.getClass().getCreatureStats(actor);\n        const MWMechanics::AiSequence& seq = stats.getAiSequence();\n        const auto packageId = seq.getTypeId();\n\n        if (seq.isInCombat() ||\n            MWBase::Environment::get().getWorld()->isSwimming(actor) ||\n            (packageId != AiPackageTypeId::Wander && packageId != AiPackageTypeId::Travel && packageId != AiPackageTypeId::None))\n        {\n            actorState.setTurningToPlayer(false);\n            actorState.setGreetingTimer(0);\n            actorState.setGreetingState(Greet_None);\n            return;\n        }\n\n        MWWorld::Ptr player = getPlayer();\n        osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());\n        osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());\n        osg::Vec3f dir = playerPos - actorPos;\n\n        if (actorState.isTurningToPlayer())\n        {\n            // Reduce the turning animation glitch by using a *HUGE* value of\n            // epsilon...  TODO: a proper fix might be in either the physics or the\n            // animation subsystem\n            if (zTurn(actor, actorState.getAngleToPlayer(), osg::DegreesToRadians(5.f)))\n            {\n                actorState.setTurningToPlayer(false);\n                // An original engine launches an endless idle2 when an actor greets player.\n                playAnimationGroup (actor, \"idle2\", 0, std::numeric_limits<int>::max(), false);\n            }\n        }\n\n        if (turnOnly)\n            return;\n\n        // Play a random voice greeting if the player gets too close\n        static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()\n            .get<ESM::GameSetting>().find(\"iGreetDistanceMultiplier\")->mValue.getInteger();\n\n        float helloDistance = static_cast<float>(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier);\n\n        int greetingTimer = actorState.getGreetingTimer();\n        GreetingState greetingState = actorState.getGreetingState();\n        if (greetingState == Greet_None)\n        {\n            if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&\n                !player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()\n                && MWBase::Environment::get().getWorld()->getLOS(player, actor)\n                && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))\n                greetingTimer++;\n\n            if (greetingTimer >= GREETING_SHOULD_START)\n            {\n                greetingState = Greet_InProgress;\n                MWBase::Environment::get().getDialogueManager()->say(actor, \"hello\");\n                greetingTimer = 0;\n            }\n        }\n\n        if (greetingState == Greet_InProgress)\n        {\n            greetingTimer++;\n\n            if (!stats.getMovementFlag(CreatureStats::Flag_ForceJump) && !stats.getMovementFlag(CreatureStats::Flag_ForceSneak)\n                && (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor)))\n                turnActorToFacePlayer(actor, actorState, dir);\n\n            if (greetingTimer >= GREETING_COOLDOWN)\n            {\n                greetingState = Greet_Done;\n                greetingTimer = 0;\n            }\n        }\n\n        if (greetingState == Greet_Done)\n        {\n            float resetDist = 2 * helloDistance;\n            if ((playerPos - actorPos).length2() >= resetDist*resetDist)\n                greetingState = Greet_None;\n        }\n\n        actorState.setGreetingTimer(greetingTimer);\n        actorState.setGreetingState(greetingState);\n    }\n\n    void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir)\n    {\n        actor.getClass().getMovementSettings(actor).mPosition[1] = 0;\n        actor.getClass().getMovementSettings(actor).mPosition[0] = 0;\n\n        if (!actorState.isTurningToPlayer())\n        {\n            float from = dir.x();\n            float to = dir.y();\n            float angle = std::atan2(from, to);\n            actorState.setAngleToPlayer(angle);\n            float deltaAngle = Misc::normalizeAngle(angle - actor.getRefData().getPosition().rot[2]);\n            if (!mSmoothMovement || std::abs(deltaAngle) > osg::DegreesToRadians(60.f))\n                actorState.setTurningToPlayer(true);\n        }\n    }\n\n    void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer)\n    {\n        // No combat for totally static creatures\n        if (!actor1.getClass().isMobile(actor1))\n            return;\n\n        CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1);\n        if (creatureStats1.isDead() || creatureStats1.getAiSequence().isInCombat(actor2))\n            return;\n\n        const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2);\n        if (creatureStats2.isDead())\n            return;\n\n        const osg::Vec3f actor1Pos(actor1.getRefData().getPosition().asVec3());\n        const osg::Vec3f actor2Pos(actor2.getRefData().getPosition().asVec3());\n        float sqrDist = (actor1Pos - actor2Pos).length2();\n\n        if (sqrDist > mActorsProcessingRange*mActorsProcessingRange)\n            return;\n\n        // If this is set to true, actor1 will start combat with actor2 if the awareness check at the end of the method returns true\n        bool aggressive = false;\n\n        // Get actors allied with actor1. Includes those following or escorting actor1, actors following or escorting those actors, (recursive)\n        // and any actor currently being followed or escorted by actor1\n        std::set<MWWorld::Ptr> allies1;\n\n        getActorsSidingWith(actor1, allies1, cachedAllies);\n\n        // If an ally of actor1 has been attacked by actor2 or has attacked actor2, start combat between actor1 and actor2\n        for (const MWWorld::Ptr &ally : allies1)\n        {\n            if (creatureStats1.getAiSequence().isInCombat(ally))\n                continue;\n\n            if (creatureStats2.matchesActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId()))\n            {\n                MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);\n                // Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat\n                // if the player gets out of reach, while the ally would continue combat with the player\n                creatureStats1.setHitAttemptActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId());\n                return;\n            }\n\n            // If there's been no attack attempt yet but an ally of actor1 is in combat with actor2, become aggressive to actor2\n            if (ally.getClass().getCreatureStats(ally).getAiSequence().isInCombat(actor2))\n                aggressive = true;\n        }\n\n        std::set<MWWorld::Ptr> playerAllies;\n        getActorsSidingWith(MWMechanics::getPlayer(), playerAllies, cachedAllies);\n\n        bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end();\n\n        // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them\n        // Doesn't apply for player followers/escorters\n        if (!aggressive && !isPlayerFollowerOrEscorter)\n        {\n            // Check that actor2 is in combat with actor1\n            if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1))\n            {\n                std::set<MWWorld::Ptr> allies2;\n\n                getActorsSidingWith(actor2, allies2, cachedAllies);\n\n                // Check that an ally of actor2 is also in combat with actor1\n                for (const MWWorld::Ptr &ally2 : allies2)\n                {\n                    if (ally2.getClass().getCreatureStats(ally2).getAiSequence().isInCombat(actor1))\n                    {\n                        MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);\n                        // Also have actor1's allies start combat\n                        for (const MWWorld::Ptr& ally1 : allies1)\n                            MWBase::Environment::get().getMechanicsManager()->startCombat(ally1, actor2);\n                        return;\n                    }\n                }\n            }\n        }\n\n        // Stop here if target is unreachable\n        if (!canFight(actor1, actor2))\n            return;\n\n        // If set in the settings file, player followers and escorters will become aggressive toward enemies in combat with them or the player\n        static const bool followersAttackOnSight = Settings::Manager::getBool(\"followers attack on sight\", \"Game\");\n        if (!aggressive && isPlayerFollowerOrEscorter && followersAttackOnSight)\n        {\n            if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1))\n                aggressive = true;\n            else\n            {\n                for (const MWWorld::Ptr &ally : allies1)\n                {\n                    if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(ally))\n                    {\n                        aggressive = true;\n                        break;\n                    }\n                }\n            }\n        }\n\n        // Do aggression check if actor2 is the player or a player follower or escorter\n        if (!aggressive)\n        {\n            if (againstPlayer || playerAllies.find(actor2) != playerAllies.end())\n            {\n                // Player followers and escorters with high fight should not initiate combat with the player or with\n                // other player followers or escorters\n                if (!isPlayerFollowerOrEscorter)\n                    aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);\n            }\n            /*\n                Start of tes3mp addition\n\n                Make aggressive actors initiate combat with DedicatedPlayers\n            */\n            else if (mwmp::PlayerList::isDedicatedPlayer(actor2))\n            {\n                aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);\n            }\n            /*\n                End of tes3mp addition\n            */\n        }\n\n        // Make guards go aggressive with creatures that are in combat, unless the creature is a follower or escorter\n        if (!aggressive && actor1.getClass().isClass(actor1, \"Guard\") && !actor2.getClass().isNpc() && creatureStats2.getAiSequence().isInCombat())\n        {\n            // Check if the creature is too far\n            static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fAlarmRadius\")->mValue.getFloat();\n            if (sqrDist > fAlarmRadius * fAlarmRadius)\n                return;\n\n            bool followerOrEscorter = false;\n            for (const auto& package : creatureStats2.getAiSequence())\n            {\n                // The follow package must be first or have nothing but combat before it\n                if (package->sideWithTarget())\n                {\n                    followerOrEscorter = true;\n                    break;\n                }\n                else if (package->getTypeId() != MWMechanics::AiPackageTypeId::Combat)\n                    break;\n            }\n            if (!followerOrEscorter)\n                aggressive = true;\n        }\n\n        // If any of the above conditions turned actor1 aggressive towards actor2, do an awareness check. If it passes, start combat with actor2.\n        if (aggressive)\n        {\n            bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2)\n                    && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1);\n\n            if (LOS)\n                MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);\n        }\n    }\n\n    void Actors::adjustMagicEffects (const MWWorld::Ptr& creature)\n    {\n        CreatureStats& creatureStats =  creature.getClass().getCreatureStats (creature);\n        if (creatureStats.isDeathAnimationFinished())\n            return;\n\n        MagicEffects now = creatureStats.getSpells().getMagicEffects();\n\n        if (creature.getClass().hasInventoryStore(creature))\n        {\n            MWWorld::InventoryStore& store = creature.getClass().getInventoryStore (creature);\n            now += store.getMagicEffects();\n        }\n\n        now += creatureStats.getActiveSpells().getMagicEffects();\n\n        creatureStats.modifyMagicEffects(now);\n    }\n\n    void Actors::calculateDynamicStats (const MWWorld::Ptr& ptr)\n    {\n        CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);\n\n        float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();\n\n        float base = 1.f;\n        if (ptr == getPlayer())\n            base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fPCbaseMagickaMult\")->mValue.getFloat();\n        else\n            base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fNPCbaseMagickaMult\")->mValue.getFloat();\n\n        double magickaFactor = base +\n            creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1;\n\n        DynamicStat<float> magicka = creatureStats.getMagicka();\n        float diff = (static_cast<int>(magickaFactor*intelligence)) - magicka.getBase();\n        float currentToBaseRatio = (magicka.getCurrent() / magicka.getBase());\n        magicka.setModified(magicka.getModified() + diff, 0);\n        magicka.setCurrent(magicka.getBase() * currentToBaseRatio, false, true);\n        creatureStats.setMagicka(magicka);\n    }\n\n    void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, double hours, bool sleep)\n    {\n        MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n        if (stats.isDead())\n            return;\n\n        const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        if (sleep)\n        {\n            float health, magicka;\n            getRestorationPerHourOfSleep(ptr, health, magicka);\n\n            DynamicStat<float> stat = stats.getHealth();\n            stat.setCurrent(stat.getCurrent() + health * hours);\n            stats.setHealth(stat);\n\n            double restoreHours = hours;\n            bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0;\n            if (stunted)\n            {\n                // Stunted Magicka effect should be taken into account.\n                GetStuntedMagickaDuration visitor(ptr);\n                stats.getActiveSpells().visitEffectSources(visitor);\n                stats.getSpells().visitEffectSources(visitor);\n                if (ptr.getClass().hasInventoryStore(ptr))\n                    ptr.getClass().getInventoryStore(ptr).visitEffectSources(visitor);\n\n                // Take a maximum remaining duration of Stunted Magicka effects (-1 is a constant one) in game hours.\n                if (visitor.mRemainingTime > 0)\n                {\n                    double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();\n                    if(timeScale == 0.0)\n                        timeScale = 1;\n\n                    restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f);\n                }\n                else if (visitor.mRemainingTime == -1)\n                    restoreHours = 0;\n            }\n\n            if (restoreHours > 0)\n            {\n                stat = stats.getMagicka();\n                stat.setCurrent(stat.getCurrent() + magicka * restoreHours);\n                stats.setMagicka(stat);\n            }\n        }\n\n        // Current fatigue can be above base value due to a fortify effect.\n        // In that case stop here and don't try to restore.\n        DynamicStat<float> fatigue = stats.getFatigue();\n        if (fatigue.getCurrent() >= fatigue.getBase())\n            return;\n\n        // Restore fatigue\n        float fFatigueReturnBase = settings.find(\"fFatigueReturnBase\")->mValue.getFloat ();\n        float fFatigueReturnMult = settings.find(\"fFatigueReturnMult\")->mValue.getFloat ();\n        float fEndFatigueMult = settings.find(\"fEndFatigueMult\")->mValue.getFloat ();\n\n        float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();\n\n        float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);\n        if (normalizedEncumbrance > 1)\n            normalizedEncumbrance = 1;\n\n        float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance);\n        x *= fEndFatigueMult * endurance;\n\n        fatigue.setCurrent (fatigue.getCurrent() + 3600 * x * hours);\n        stats.setFatigue (fatigue);\n    }\n\n    void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration)\n    {\n        if (ptr.getClass().getCreatureStats(ptr).isDead())\n            return;\n\n        MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n\n        // Current fatigue can be above base value due to a fortify effect.\n        // In that case stop here and don't try to restore.\n        DynamicStat<float> fatigue = stats.getFatigue();\n        if (fatigue.getCurrent() >= fatigue.getBase())\n            return;\n\n        // Restore fatigue\n        float endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified();\n        const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        static const float fFatigueReturnBase = settings.find(\"fFatigueReturnBase\")->mValue.getFloat ();\n        static const float fFatigueReturnMult = settings.find(\"fFatigueReturnMult\")->mValue.getFloat ();\n\n        float x = fFatigueReturnBase + fFatigueReturnMult * endurance;\n\n        fatigue.setCurrent (fatigue.getCurrent() + duration * x);\n        stats.setFatigue (fatigue);\n    }\n\n    class ExpiryVisitor : public EffectSourceVisitor\n    {\n        private:\n            MWWorld::Ptr mActor;\n            float mDuration;\n\n        public:\n            ExpiryVisitor(const MWWorld::Ptr& actor, float duration)\n                : mActor(actor), mDuration(duration)\n            {\n            }\n\n            void visit (MWMechanics::EffectKey key, int /*effectIndex*/,\n                                const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,\n                                float magnitude, float remainingTime = -1, float /*totalTime*/ = -1) override\n            {\n                if (magnitude > 0 && remainingTime > 0 && remainingTime < mDuration)\n                {\n                    CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor);\n                    if (effectTick(creatureStats, mActor, key, magnitude * remainingTime))\n                        creatureStats.getMagicEffects().add(key, -magnitude);\n                }\n            }\n    };\n\n    void Actors::applyCureEffects(const MWWorld::Ptr& actor)\n    {\n        CreatureStats &creatureStats = actor.getClass().getCreatureStats(actor);\n        const MagicEffects &effects = creatureStats.getMagicEffects();\n\n        if (effects.get(ESM::MagicEffect::CurePoison).getModifier() > 0)\n        {\n            creatureStats.getActiveSpells().purgeEffect(ESM::MagicEffect::Poison);\n            creatureStats.getSpells().purgeEffect(ESM::MagicEffect::Poison);\n            if (actor.getClass().hasInventoryStore(actor))\n                actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Poison);\n        }\n        if (effects.get(ESM::MagicEffect::CureParalyzation).getModifier() > 0)\n        {\n            creatureStats.getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze);\n            creatureStats.getSpells().purgeEffect(ESM::MagicEffect::Paralyze);\n            if (actor.getClass().hasInventoryStore(actor))\n                actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Paralyze);\n        }\n        if (effects.get(ESM::MagicEffect::CureCommonDisease).getModifier() > 0)\n        {\n            creatureStats.getSpells().purgeCommonDisease();\n        }\n        if (effects.get(ESM::MagicEffect::CureBlightDisease).getModifier() > 0)\n        {\n            creatureStats.getSpells().purgeBlightDisease();\n        }\n        if (effects.get(ESM::MagicEffect::CureCorprusDisease).getModifier() > 0)\n        {\n            creatureStats.getActiveSpells().purgeCorprusDisease();\n            creatureStats.getSpells().purgeCorprusDisease();\n            if (actor.getClass().hasInventoryStore(actor))\n                actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Corprus, true);\n        }\n        if (effects.get(ESM::MagicEffect::RemoveCurse).getModifier() > 0)\n        {\n            creatureStats.getSpells().purgeCurses();\n        }\n    }\n\n    void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration)\n    {\n        CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);\n        const MagicEffects &effects = creatureStats.getMagicEffects();\n        bool godmode = ptr == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n\n        applyCureEffects(ptr);\n\n        bool wasDead = creatureStats.isDead();\n\n        if (duration > 0)\n        {\n            // Apply correct magnitude for tickable effects that have just expired,\n            // in case duration > remaining time of effect.\n            // One case where this will happen is when the player uses the rest/wait command\n            // while there is a tickable effect active that should expire before the end of the rest/wait.\n            ExpiryVisitor visitor(ptr, duration);\n            creatureStats.getActiveSpells().visitEffectSources(visitor);\n\n            for (MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it)\n            {\n                // tickable effects (i.e. effects having a lasting impact after expiry)\n                effectTick(creatureStats, ptr, it->first, it->second.getMagnitude() * duration);\n\n                // instant effects are already applied on spell impact in spellcasting.cpp, but may also come from permanent abilities\n                if (it->second.getMagnitude() > 0)\n                {\n                    CastSpell cast(ptr, ptr);\n                    if (cast.applyInstantEffect(ptr, ptr, it->first, it->second.getMagnitude()))\n                    {\n                        creatureStats.getSpells().purgeEffect(it->first.mId);\n                        creatureStats.getActiveSpells().purgeEffect(it->first.mId);\n                        if (ptr.getClass().hasInventoryStore(ptr))\n                            ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first.mId);\n                    }\n                }\n            }\n        }\n\n        // purge levitate effect if levitation is disabled\n        // check only modifier, because base value can be setted from SetFlying console command.\n        if (MWBase::Environment::get().getWorld()->isLevitationEnabled() == false && effects.get(ESM::MagicEffect::Levitate).getModifier() > 0)\n        {\n            creatureStats.getSpells().purgeEffect(ESM::MagicEffect::Levitate);\n            creatureStats.getActiveSpells().purgeEffect(ESM::MagicEffect::Levitate);\n            if (ptr.getClass().hasInventoryStore(ptr))\n                ptr.getClass().getInventoryStore(ptr).purgeEffect(ESM::MagicEffect::Levitate);\n\n            if (ptr == getPlayer())\n            {\n                MWBase::Environment::get().getWindowManager()->messageBox (\"#{sLevitateDisabled}\");\n            }\n        }\n\n        // dynamic stats\n        for (int i = 0; i < 3; ++i)\n        {\n            DynamicStat<float> stat = creatureStats.getDynamic(i);\n            float fortify = effects.get(ESM::MagicEffect::FortifyHealth + i).getMagnitude();\n            float drain = 0.f;\n            if (!godmode)\n                drain = effects.get(ESM::MagicEffect::DrainHealth + i).getMagnitude();\n            stat.setCurrentModifier(fortify - drain,\n                // Magicka can be decreased below zero due to a fortify effect wearing off\n                // Fatigue can be decreased below zero meaning the actor will be knocked out\n                i == 1 || i == 2);\n\n            creatureStats.setDynamic(i, stat);\n        }\n\n        // attributes\n        for(int i = 0;i < ESM::Attribute::Length;++i)\n        {\n            AttributeValue stat = creatureStats.getAttribute(i);\n            float fortify = effects.get(EffectKey(ESM::MagicEffect::FortifyAttribute, i)).getMagnitude();\n            float drain = 0.f, absorb = 0.f;\n            if (!godmode)\n            {\n                drain = effects.get(EffectKey(ESM::MagicEffect::DrainAttribute, i)).getMagnitude();\n                absorb = effects.get(EffectKey(ESM::MagicEffect::AbsorbAttribute, i)).getMagnitude();\n            }\n            stat.setModifier(static_cast<int>(fortify - drain - absorb));\n\n            creatureStats.setAttribute(i, stat);\n        }\n\n        if (creatureStats.needToRecalcDynamicStats())\n            calculateDynamicStats(ptr);\n\n        if (ptr == getPlayer())\n        {\n            GetCorprusSpells getCorprusSpellsVisitor;\n            creatureStats.getSpells().visitEffectSources(getCorprusSpellsVisitor);\n            creatureStats.getActiveSpells().visitEffectSources(getCorprusSpellsVisitor);\n            ptr.getClass().getInventoryStore(ptr).visitEffectSources(getCorprusSpellsVisitor);\n            std::vector<std::string> corprusSpells = getCorprusSpellsVisitor.mSpells;\n            std::vector<std::string> corprusSpellsToRemove;\n\n            for (auto it = creatureStats.getCorprusSpells().begin(); it != creatureStats.getCorprusSpells().end(); ++it)\n            {\n                if(std::find(corprusSpells.begin(), corprusSpells.end(), it->first) == corprusSpells.end())\n                {\n                    // Corprus effect expired, remove entry and restore stats.\n                    MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, it->first);\n                    corprusSpellsToRemove.push_back(it->first);\n                    corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end());\n                    continue;\n                }\n\n                corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end());\n\n                if (MWBase::Environment::get().getWorld()->getTimeStamp() >= it->second.mNextWorsening)\n                {\n                    it->second.mNextWorsening += CorprusStats::sWorseningPeriod;\n                    GetCurrentMagnitudes getMagnitudesVisitor (it->first);\n                    creatureStats.getSpells().visitEffectSources(getMagnitudesVisitor);\n                    creatureStats.getActiveSpells().visitEffectSources(getMagnitudesVisitor);\n                    ptr.getClass().getInventoryStore(ptr).visitEffectSources(getMagnitudesVisitor);\n                    for (auto& effectMagnitude : getMagnitudesVisitor.mMagnitudes)\n                    {\n                        if (effectMagnitude.first.mId == ESM::MagicEffect::FortifyAttribute)\n                        {\n                            AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg);\n                            attr.damage(-effectMagnitude.second);\n                            creatureStats.setAttribute(effectMagnitude.first.mArg, attr);\n                            it->second.mWorsenings[effectMagnitude.first.mArg] = 0;\n                        }\n                        else if (effectMagnitude.first.mId == ESM::MagicEffect::DrainAttribute)\n                        {\n                            AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg);\n                            int currentDamage = attr.getDamage();\n                            if (currentDamage >= 0)\n                                it->second.mWorsenings[effectMagnitude.first.mArg] = std::min(it->second.mWorsenings[effectMagnitude.first.mArg], currentDamage);\n\n                            it->second.mWorsenings[effectMagnitude.first.mArg] += effectMagnitude.second;\n                            attr.damage(effectMagnitude.second);\n                            creatureStats.setAttribute(effectMagnitude.first.mArg, attr);\n                        }\n                    }\n\n                    MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicCorprusWorsens}\");\n                }\n            }\n\n            for (std::string& oldCorprusSpell : corprusSpellsToRemove)\n            {\n                 creatureStats.removeCorprusSpell(oldCorprusSpell);\n            }\n\n            for (std::string& newCorprusSpell : corprusSpells)\n            {\n                CorprusStats corprus;\n                for (int i=0; i<ESM::Attribute::Length; ++i)\n                    corprus.mWorsenings[i] = 0;\n                corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod;\n\n                creatureStats.addCorprusSpell(newCorprusSpell, corprus);\n            }\n        }\n\n        // AI setting modifiers\n        int creature = !ptr.getClass().isNpc();\n        if (creature && ptr.get<ESM::Creature>()->mBase->mData.mType == ESM::Creature::Humanoid)\n            creature = false;\n        // Note: the Creature variants only work on normal creatures, not on daedra or undead creatures.\n        if (!creature || ptr.get<ESM::Creature>()->mBase->mData.mType == ESM::Creature::Creatures)\n        {\n            Stat<int> stat = creatureStats.getAiSetting(CreatureStats::AI_Fight);\n            stat.setModifier(static_cast<int>(effects.get(ESM::MagicEffect::FrenzyHumanoid + creature).getMagnitude()\n                - effects.get(ESM::MagicEffect::CalmHumanoid+creature).getMagnitude()));\n            creatureStats.setAiSetting(CreatureStats::AI_Fight, stat);\n\n            stat = creatureStats.getAiSetting(CreatureStats::AI_Flee);\n            stat.setModifier(static_cast<int>(effects.get(ESM::MagicEffect::DemoralizeHumanoid + creature).getMagnitude()\n                - effects.get(ESM::MagicEffect::RallyHumanoid+creature).getMagnitude()));\n            creatureStats.setAiSetting(CreatureStats::AI_Flee, stat);\n        }\n        if (creature && ptr.get<ESM::Creature>()->mBase->mData.mType == ESM::Creature::Undead)\n        {\n            Stat<int> stat = creatureStats.getAiSetting(CreatureStats::AI_Flee);\n            stat.setModifier(static_cast<int>(effects.get(ESM::MagicEffect::TurnUndead).getMagnitude()));\n            creatureStats.setAiSetting(CreatureStats::AI_Flee, stat);\n        }\n\n        if (!wasDead && creatureStats.isDead())\n        {\n            // The actor was killed by a magic effect. Figure out if the player was responsible for it.\n            const ActiveSpells& spells = creatureStats.getActiveSpells();\n            MWWorld::Ptr player = getPlayer();\n            std::set<MWWorld::Ptr> playerFollowers;\n            getActorsSidingWith(player, playerFollowers);\n\n            for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)\n            {\n                bool actorKilled = false;\n\n                const ActiveSpells::ActiveSpellParams& spell = it->second;\n                MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);\n                for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();\n                     effectIt != spell.mEffects.end(); ++effectIt)\n                {\n                    int effectId = effectIt->mEffectId;\n                    bool isDamageEffect = false;\n\n                    int damageEffects[] = {\n                        ESM::MagicEffect::FireDamage, ESM::MagicEffect::ShockDamage, ESM::MagicEffect::FrostDamage, ESM::MagicEffect::Poison,\n                        ESM::MagicEffect::SunDamage, ESM::MagicEffect::DamageHealth, ESM::MagicEffect::AbsorbHealth\n                    };\n\n                    for (unsigned int i=0; i<sizeof(damageEffects)/sizeof(int); ++i)\n                    {\n                        if (damageEffects[i] == effectId)\n                            isDamageEffect = true;\n                    }\n\n                    if (isDamageEffect)\n                    {\n                        /*\n                            Start of tes3mp addition\n\n                            If the victim was the LocalPlayer or a LocalActor, record the caster as their killer\n                        */\n                        mwmp::Target killer = MechanicsHelper::getTarget(caster);\n\n                        if (ptr == MWMechanics::getPlayer())\n                        {\n                            mwmp::Main::get().getLocalPlayer()->killer = killer;\n                        }\n                        else if (mwmp::Main::get().getCellController()->isLocalActor(ptr))\n                        {\n                            mwmp::Main::get().getCellController()->getLocalActor(ptr)->killer = killer;\n                        }\n                        /*\n                            End of tes3mp addition\n                        */\n\n                        if (caster == player || playerFollowers.find(caster) != playerFollowers.end())\n                        {\n                            if (caster.getClass().isNpc() && caster.getClass().getNpcStats(caster).isWerewolf())\n                                caster.getClass().getNpcStats(caster).addWerewolfKill();\n\n                            MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);\n                            actorKilled = true;\n                            break;\n                        }\n                    }\n                }\n\n                if (actorKilled)\n                    break;\n            }\n        }\n\n        // TODO: dirty flag for magic effects to avoid some unnecessary work below?\n\n        // any value of calm > 0 will stop the actor from fighting\n        if ((effects.get(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0 && ptr.getClass().isNpc())\n            || (effects.get(ESM::MagicEffect::CalmCreature).getMagnitude() > 0 && !ptr.getClass().isNpc()))\n            creatureStats.getAiSequence().stopCombat();\n\n        // Update bound effects\n        // Note: in vanilla MW multiple bound items of the same type can be created by different spells.\n        // As these extra copies are kinda useless this may or may not be important.\n        static std::map<ESM::MagicEffect::Effects, std::string> boundItemsMap;\n        if (boundItemsMap.empty())\n        {\n            boundItemsMap[ESM::MagicEffect::BoundBattleAxe] = \"sMagicBoundBattleAxeID\";\n            boundItemsMap[ESM::MagicEffect::BoundBoots] = \"sMagicBoundBootsID\";\n            boundItemsMap[ESM::MagicEffect::BoundCuirass] = \"sMagicBoundCuirassID\";\n            boundItemsMap[ESM::MagicEffect::BoundDagger] = \"sMagicBoundDaggerID\";\n            boundItemsMap[ESM::MagicEffect::BoundGloves] = \"sMagicBoundLeftGauntletID\"; // Note: needs RightGauntlet variant too (see below)\n            boundItemsMap[ESM::MagicEffect::BoundHelm] = \"sMagicBoundHelmID\";\n            boundItemsMap[ESM::MagicEffect::BoundLongbow] = \"sMagicBoundLongbowID\";\n            boundItemsMap[ESM::MagicEffect::BoundLongsword] = \"sMagicBoundLongswordID\";\n            boundItemsMap[ESM::MagicEffect::BoundMace] = \"sMagicBoundMaceID\";\n            boundItemsMap[ESM::MagicEffect::BoundShield] = \"sMagicBoundShieldID\";\n            boundItemsMap[ESM::MagicEffect::BoundSpear] = \"sMagicBoundSpearID\";\n        }\n\n        if(ptr.getClass().hasInventoryStore(ptr))\n        {\n            for (const auto& [effect, itemGmst] : boundItemsMap)\n            {\n                bool found = creatureStats.mBoundItems.find(effect) != creatureStats.mBoundItems.end();\n                float magnitude = effects.get(effect).getMagnitude();\n                if (found != (magnitude > 0))\n                {\n                    if (magnitude > 0)\n                        creatureStats.mBoundItems.insert(effect);\n                    else\n                        creatureStats.mBoundItems.erase(effect);\n\n                    std::string item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                                itemGmst)->mValue.getString();\n\n                    magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr);\n\n                    if (effect == ESM::MagicEffect::BoundGloves)\n                    {\n                        item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                                    \"sMagicBoundRightGauntletID\")->mValue.getString();\n                        magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr);\n                    }\n                }\n            }\n        }\n\n        // Summoned creature update visitor assumes the actor belongs to a cell.\n        // This assumption isn't always valid for the player character.\n        if (!ptr.isInCell())\n            return;\n\n        bool hasSummonEffect = false;\n        for (MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it)\n        {\n            if (isSummoningEffect(it->first.mId))\n            {\n                hasSummonEffect = true;\n                break;\n            }\n        }\n\n        if (!creatureStats.getSummonedCreatureMap().empty() || !creatureStats.getSummonedCreatureGraveyard().empty() || hasSummonEffect)\n        {\n            UpdateSummonedCreatures updateSummonedCreatures(ptr);\n            creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);\n            creatureStats.getSpells().visitEffectSources(updateSummonedCreatures);\n            if (ptr.getClass().hasInventoryStore(ptr))\n                ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures);\n            updateSummonedCreatures.process(mTimerDisposeSummonsCorpses == 0.f);\n        }\n    }\n\n    void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration)\n    {\n        NpcStats &npcStats = ptr.getClass().getNpcStats(ptr);\n        const MagicEffects &effects = npcStats.getMagicEffects();\n        bool godmode = ptr == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n\n        // skills\n        for(int i = 0;i < ESM::Skill::Length;++i)\n        {\n            SkillValue& skill = npcStats.getSkill(i);\n            float fortify = effects.get(EffectKey(ESM::MagicEffect::FortifySkill, i)).getMagnitude();\n            float drain = 0.f, absorb = 0.f;\n            if (!godmode)\n            {\n                drain = effects.get(EffectKey(ESM::MagicEffect::DrainSkill, i)).getMagnitude();\n                absorb = effects.get(EffectKey(ESM::MagicEffect::AbsorbSkill, i)).getMagnitude();\n            }\n            skill.setModifier(static_cast<int>(fortify - drain - absorb));\n        }\n    }\n\n    bool Actors::isAttackPreparing(const MWWorld::Ptr& ptr)\n    {\n        PtrActorMap::iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return false;\n        CharacterController* ctrl = it->second->getCharacterController();\n\n        return ctrl->isAttackPreparing();\n    }\n\n    bool Actors::isRunning(const MWWorld::Ptr& ptr)\n    {\n        PtrActorMap::iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return false;\n        CharacterController* ctrl = it->second->getCharacterController();\n\n        return ctrl->isRunning();\n    }\n\n    bool Actors::isSneaking(const MWWorld::Ptr& ptr)\n    {\n        PtrActorMap::iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return false;\n        CharacterController* ctrl = it->second->getCharacterController();\n\n        return ctrl->isSneaking();\n    }\n\n    void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer)\n    {\n        NpcStats &stats = ptr.getClass().getNpcStats(ptr);\n\n        // When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST\n        static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fHoldBreathTime\")->mValue.getFloat();\n        if (stats.getTimeToStartDrowning() == -1.f)\n            stats.setTimeToStartDrowning(fHoldBreathTime);\n\n        if (!isPlayer && stats.getTimeToStartDrowning() < fHoldBreathTime / 2)\n        {\n            AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();\n            if (seq.getTypeId() != AiPackageTypeId::Breathe) //Only add it once\n                seq.stack(AiBreathe(), ptr);\n        }\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        bool knockedOutUnderwater = (isKnockedOut && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3())));\n        if((world->isSubmerged(ptr) || knockedOutUnderwater)\n           && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0)\n        {\n            float timeLeft = 0.0f;\n            if(knockedOutUnderwater)\n                stats.setTimeToStartDrowning(0);\n            else\n            {\n                timeLeft = stats.getTimeToStartDrowning() - duration;\n                if(timeLeft < 0.0f)\n                    timeLeft = 0.0f;\n                stats.setTimeToStartDrowning(timeLeft);\n            }\n\n            bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState();\n\n            if(timeLeft == 0.0f && !godmode)\n            {\n                // If drowning, apply 3 points of damage per second\n                static const float fSuffocationDamage = world->getStore().get<ESM::GameSetting>().find(\"fSuffocationDamage\")->mValue.getFloat();\n                DynamicStat<float> health = stats.getHealth();\n                health.setCurrent(health.getCurrent() - fSuffocationDamage*duration);\n                stats.setHealth(health);\n\n                // Play a drowning sound\n                MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager();\n                if(!sndmgr->getSoundPlaying(ptr, \"drown\"))\n                    sndmgr->playSound3D(ptr, \"drown\", 1.0f, 1.0f);\n\n                if(isPlayer)\n                    MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);\n            }\n        }\n        else\n            stats.setTimeToStartDrowning(fHoldBreathTime);\n    }\n\n    void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip)\n    {\n        bool isPlayer = (ptr == getPlayer());\n\n        MWWorld::InventoryStore &inventoryStore = ptr.getClass().getInventoryStore(ptr);\n        MWWorld::ContainerStoreIterator heldIter =\n                inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n        /**\n         * Automatically equip NPCs torches at night and unequip them at day\n         */\n\n        /*\n            Start of tes3mp change (major)\n\n            We need DedicatedPlayers and DedicatedActors to not automatically\n            equip their light-emitting items, so additional conditions have been\n            added for them\n        */\n        if (!isPlayer && !mwmp::PlayerList::isDedicatedPlayer(ptr) && !mwmp::Main::get().getCellController()->isDedicatedActor(ptr))\n        {\n        /*\n            End of tes3mp change (major)    \n        */\n            MWWorld::ContainerStoreIterator torch = inventoryStore.end();\n            for (MWWorld::ContainerStoreIterator it = inventoryStore.begin(); it != inventoryStore.end(); ++it)\n            {\n                if (it->getTypeName() == typeid(ESM::Light).name() &&\n                    it->getClass().canBeEquipped(*it, ptr).first)\n                {\n                    torch = it;\n                    break;\n                }\n            }\n\n            if (mayEquip)\n            {\n                if (torch != inventoryStore.end())\n                {\n                    if (!ptr.getClass().getCreatureStats (ptr).getAiSequence().isInCombat())\n                    {\n                        // For non-hostile NPCs, unequip whatever is in the left slot in favor of a light.\n                        if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name())\n                            inventoryStore.unequipItem(*heldIter, ptr);\n                    }\n                    else if (heldIter == inventoryStore.end() || heldIter->getTypeName() == typeid(ESM::Light).name())\n                    {\n                        // For hostile NPCs, see if they have anything better to equip first\n                        auto shield = inventoryStore.getPreferredShield(ptr);\n                        if(shield != inventoryStore.end())\n                            inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, shield, ptr);\n                    }\n\n                    heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n\n                    // If we have a torch and can equip it, then equip it now.\n                    if (heldIter == inventoryStore.end())\n                    {\n                        inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr);\n                    }\n                }\n            }\n            else\n            {\n                if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name())\n                {\n                    // At day, unequip lights and auto equip shields or other suitable items\n                    // (Note: autoEquip will ignore lights)\n                    inventoryStore.autoEquip(ptr);\n                }\n            }\n        }\n\n        heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n\n        //If holding a light...\n        if(heldIter.getType() == MWWorld::ContainerStore::Type_Light)\n        {\n            // Use time from the player's light\n            if(isPlayer)\n            {\n                // But avoid using it up if the light source is hidden\n                MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);\n                if (anim && anim->getCarriedLeftShown())\n                {\n                    float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter);\n\n                    // -1 is infinite light source. Other negative values are treated as 0.\n                    if (timeRemaining != -1.0f)\n                    {\n                        timeRemaining -= duration;\n                        if (timeRemaining <= 0.f)\n                        {\n                            inventoryStore.remove(*heldIter, 1, ptr); // remove it\n                            return;\n                        }\n\n                        heldIter->getClass().setRemainingUsageTime(*heldIter, timeRemaining);\n                    }\n                }\n            }\n\n            // Both NPC and player lights extinguish in water.\n            if(MWBase::Environment::get().getWorld()->isSwimming(ptr))\n            {\n                inventoryStore.remove(*heldIter, 1, ptr); // remove it\n\n                // ...But, only the player makes a sound.\n                if(isPlayer)\n                    MWBase::Environment::get().getSoundManager()->playSound(\"torch out\",\n                            1.0, 1.0, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);\n            }\n        }\n    }\n\n    void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration)\n    {\n        MWWorld::Ptr player = getPlayer();\n        if (ptr != player && ptr.getClass().isNpc())\n        {\n            // get stats of witness\n            CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);\n            NpcStats& npcStats = ptr.getClass().getNpcStats(ptr);\n\n            if (player.getClass().getNpcStats(player).isWerewolf())\n                return;\n\n            if (ptr.getClass().isClass(ptr, \"Guard\") && creatureStats.getAiSequence().getTypeId() != AiPackageTypeId::Pursue && !creatureStats.getAiSequence().isInCombat()\n                && creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0)\n            {\n                const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();\n                static const int cutoff = esmStore.get<ESM::GameSetting>().find(\"iCrimeThreshold\")->mValue.getInteger();\n                // Force dialogue on sight if bounty is greater than the cutoff\n                // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty)\n                if (   player.getClass().getNpcStats(player).getBounty() >= cutoff\n                       // TODO: do not run these two every frame. keep an Aware state for each actor and update it every 0.2 s or so?\n                    && MWBase::Environment::get().getWorld()->getLOS(ptr, player)\n                    && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))\n                {\n                    static const int iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find(\"iCrimeThresholdMultiplier\")->mValue.getInteger();\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Only attack players based on their high bounty if they haven't died since the last\n                        time an attempt was made to arrest them\n                    */\n                    if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier\n                        && !mwmp::Main::get().getLocalPlayer()->diedSinceArrestAttempt)\n                    /*\n                        End of tes3mp change (major)\n                    */\n                    {\n                        MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);\n                        creatureStats.setHitAttemptActorId(player.getClass().getCreatureStats(player).getActorId()); // Stops the guard from quitting combat if player is unreachable\n                    }\n                    else\n                        creatureStats.getAiSequence().stack(AiPursue(player), ptr);\n                    creatureStats.setAlarmed(true);\n                    npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId());\n                }\n            }\n\n            // if I was a witness to a crime\n            if (npcStats.getCrimeId() != -1)\n            {\n                // if you've paid for your crimes and I havent noticed\n                if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() )\n                {\n                    // Calm witness down\n                    if (ptr.getClass().isClass(ptr, \"Guard\"))\n                        creatureStats.getAiSequence().stopPursuit();\n                    creatureStats.getAiSequence().stopCombat();\n\n                    // Reset factors to attack\n                    creatureStats.setAttacked(false);\n                    creatureStats.setAlarmed(false);\n                    creatureStats.setAiSetting(CreatureStats::AI_Fight, ptr.getClass().getBaseFightRating(ptr));\n\n                    // Update witness crime id\n                    npcStats.setCrimeId(-1);\n                }\n                /*\n                    Start of tes3mp addition\n\n                    If the player has died since their crime was committed, stop combat\n                    with them as though they have paid their bounty\n                */\n                else if (mwmp::Main::get().getLocalPlayer()->diedSinceArrestAttempt && creatureStats.getAiSequence().isInCombat(player))\n                {\n                    if (difftime(mwmp::Main::get().getLocalPlayer()->deathTime, npcStats.getCrimeTime()) > 0)\n                    {\n                        creatureStats.getAiSequence().stopCombat();\n                        creatureStats.setAttacked(false);\n                        creatureStats.setAlarmed(false);\n                        creatureStats.setAiSetting(CreatureStats::AI_Fight, ptr.getClass().getBaseFightRating(ptr));\n\n                        npcStats.setCrimeId(-1);\n                        npcStats.setCrimeTime(time(0));\n                        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"NPC %s %i-%i has forgiven player's crimes after the player's death\",\n                            ptr.getCellRef().getRefId().c_str(), ptr.getCellRef().getRefNum().mIndex, ptr.getCellRef().getMpNum());\n                    }\n                }\n                /*\n                    End of tes3mp addition\n                */\n            }\n        }\n    }\n\n    Actors::Actors() : mSmoothMovement(Settings::Manager::getBool(\"smooth movement\", \"Game\"))\n    {\n        mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning\n\n        updateProcessingRange();\n    }\n\n    Actors::~Actors()\n    {\n        clear();\n    }\n\n    float Actors::getProcessingRange() const\n    {\n        return mActorsProcessingRange;\n    }\n\n    void Actors::updateProcessingRange()\n    {\n        /*\n            Start of tes3mp change (major)\n\n            Multiplayer needs AI processing to not have a distance limit, at least until a better authority system\n            is implemented for LocalActors\n        */\n        // We have to cap it since using high values (larger than 7168) will make some quests harder or impossible to complete (bug #1876)\n        //static const float maxProcessingRange = 7168.f;\n        static const float maxProcessingRange = 8192.f * 50;\n        /*\n            End of tes3mp change (major)\n        */\n\n        static const float minProcessingRange = maxProcessingRange / 2.f;\n\n        float actorsProcessingRange = Settings::Manager::getFloat(\"actors processing range\", \"Game\");\n        actorsProcessingRange = std::min(actorsProcessingRange, maxProcessingRange);\n        actorsProcessingRange = std::max(actorsProcessingRange, minProcessingRange);\n        mActorsProcessingRange = actorsProcessingRange;\n    }\n\n    void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately)\n    {\n        removeActor(ptr);\n\n        MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);\n        if (!anim)\n            return;\n        mActors.insert(std::make_pair(ptr, new Actor(ptr, anim)));\n\n        CharacterController* ctrl = mActors[ptr]->getCharacterController();\n        if (updateImmediately)\n            ctrl->update(0);\n\n        // We should initially hide actors outside of processing range.\n        // Note: since we update player after other actors, distance will be incorrect during teleportation.\n        // Do not update visibility if player was teleported, so actors will be visible during teleportation frame.\n        if (MWBase::Environment::get().getWorld()->getPlayer().wasTeleported())\n            return;\n\n        updateVisibility(ptr, ctrl);\n    }\n\n    void Actors::updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl)\n    {\n        MWWorld::Ptr player = MWMechanics::getPlayer();\n        if (ptr == player)\n            return;\n\n        const float dist = (player.getRefData().getPosition().asVec3() - ptr.getRefData().getPosition().asVec3()).length();\n        if (dist > mActorsProcessingRange)\n        {\n            ptr.getRefData().getBaseNode()->setNodeMask(0);\n            return;\n        }\n        else\n            ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor);\n\n        // Fade away actors on large distance (>90% of actor's processing distance)\n        float visibilityRatio = 1.0;\n        float fadeStartDistance = mActorsProcessingRange*0.9f;\n        float fadeEndDistance = mActorsProcessingRange;\n        float fadeRatio = (dist - fadeStartDistance)/(fadeEndDistance - fadeStartDistance);\n        if (fadeRatio > 0)\n            visibilityRatio -= std::max(0.f, fadeRatio);\n\n        visibilityRatio = std::min(1.f, visibilityRatio);\n\n        ctrl->setVisibility(visibilityRatio);\n    }\n\n    void Actors::removeActor (const MWWorld::Ptr& ptr)\n    {\n        PtrActorMap::iterator iter = mActors.find(ptr);\n        if(iter != mActors.end())\n        {\n            delete iter->second;\n            mActors.erase(iter);\n        }\n    }\n\n    void Actors::castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell)\n    {\n        PtrActorMap::iterator iter = mActors.find(ptr);\n        if(iter != mActors.end())\n            iter->second->getCharacterController()->castSpell(spellId, manualSpell);\n    }\n\n    bool Actors::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)\n    {\n        if (!actor.getClass().isActor())\n            return false;\n\n        // If an observer is NPC, check if he detected an actor\n        if (!observer.isEmpty() && observer.getClass().isNpc())\n        {\n            return\n                MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&\n                MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);\n        }\n\n        // Otherwise check if any actor in AI processing range sees the target actor\n        std::vector<MWWorld::Ptr> neighbors;\n        osg::Vec3f position (actor.getRefData().getPosition().asVec3());\n        getObjectsInRange(position, mActorsProcessingRange, neighbors);\n        for (const MWWorld::Ptr &neighbor : neighbors)\n        {\n            if (neighbor == actor)\n                continue;\n\n            bool result = MWBase::Environment::get().getWorld()->getLOS(neighbor, actor)\n                       && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, neighbor);\n\n            if (result)\n                return true;\n        }\n\n        return false;\n    }\n\n    void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)\n    {\n        PtrActorMap::iterator iter = mActors.find(old);\n        if(iter != mActors.end())\n        {\n            Actor *actor = iter->second;\n            mActors.erase(iter);\n\n            actor->updatePtr(ptr);\n            mActors.insert(std::make_pair(ptr, actor));\n        }\n    }\n\n    void Actors::dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore)\n    {\n        PtrActorMap::iterator iter = mActors.begin();\n        while(iter != mActors.end())\n        {\n            if((iter->first.isInCell() && iter->first.getCell()==cellStore) && iter->first != ignore)\n            {\n                delete iter->second;\n                mActors.erase(iter++);\n            }\n            else\n                ++iter;\n        }\n    }\n\n    void Actors::updateCombatMusic ()\n    {\n        MWWorld::Ptr player = getPlayer();\n        const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();\n        bool hasHostiles = false; // need to know this to play Battle music\n\n        for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)\n        {\n            if (iter->first == player) continue;\n\n            bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= mActorsProcessingRange*mActorsProcessingRange;\n            if (!inProcessingRange) continue;\n\n            MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);\n            if (!stats.isDead() && stats.getAiSequence().isInCombat())\n            {\n                hasHostiles = true;\n                break;\n            }\n        }\n\n        // check if we still have any player enemies to switch music\n        static int currentMusic = 0;\n\n        if (currentMusic != 1 && !hasHostiles && !(player.getClass().getCreatureStats(player).isDead() &&\n        MWBase::Environment::get().getSoundManager()->isMusicPlaying()))\n        {\n            MWBase::Environment::get().getSoundManager()->playPlaylist(std::string(\"Explore\"));\n            currentMusic = 1;\n        }\n        else if (currentMusic != 2 && hasHostiles)\n        {\n            MWBase::Environment::get().getSoundManager()->playPlaylist(std::string(\"Battle\"));\n            currentMusic = 2;\n        }\n\n    }\n\n    void Actors::predictAndAvoidCollisions(float duration)\n    {\n        if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())\n            return;\n\n        const float minGap = 10.f;\n        const float maxDistForPartialAvoiding = 200.f;\n        const float maxDistForStrictAvoiding = 100.f;\n        const float maxTimeToCheck = 2.0f;\n        static const bool giveWayWhenIdle = Settings::Manager::getBool(\"NPCs give way\", \"Game\");\n\n        MWWorld::Ptr player = getPlayer();\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)\n        {\n            const MWWorld::Ptr& ptr = iter->first;\n            if (ptr == player)\n                continue; // Don't interfere with player controls.\n\n            float maxSpeed = ptr.getClass().getMaxSpeed(ptr);\n            if (maxSpeed == 0.0)\n                continue; // Can't move, so there is no sense to predict collisions.\n\n            Movement& movement = ptr.getClass().getMovementSettings(ptr);\n            osg::Vec2f origMovement(movement.mPosition[0], movement.mPosition[1]);\n            bool isMoving = origMovement.length2() > 0.01;\n            if (movement.mPosition[1] < 0)\n                continue; // Actors can not see others when move backward.\n\n            // Moving NPCs always should avoid collisions.\n            // Standing NPCs give way to moving ones if they are not in combat (or pursue) mode and either\n            // follow player or have a AIWander package with non-empty wander area.\n            bool shouldAvoidCollision = isMoving;\n            bool shouldGiveWay = false;\n            bool shouldTurnToApproachingActor = !isMoving;\n            MWWorld::Ptr currentTarget; // Combat or pursue target (NPCs should not avoid collision with their targets).\n            const auto& aiSequence = ptr.getClass().getCreatureStats(ptr).getAiSequence();\n            for (const auto& package : aiSequence)\n            {\n                if (package->getTypeId() == AiPackageTypeId::Follow)\n                    shouldAvoidCollision = true;\n                else if (package->getTypeId() == AiPackageTypeId::Wander && giveWayWhenIdle)\n                {\n                    if (!static_cast<const AiWander*>(package.get())->isStationary())\n                        shouldGiveWay = true;\n                }\n                else if (package->getTypeId() == AiPackageTypeId::Combat || package->getTypeId() == AiPackageTypeId::Pursue)\n                {\n                    currentTarget = package->getTarget();\n                    shouldAvoidCollision = isMoving;\n                    shouldTurnToApproachingActor = false;\n                    break;\n                }\n            }\n            if (!shouldAvoidCollision && !shouldGiveWay)\n                continue;\n\n            osg::Vec2f baseSpeed = origMovement * maxSpeed;\n            osg::Vec3f basePos = ptr.getRefData().getPosition().asVec3();\n            float baseRotZ = ptr.getRefData().getPosition().rot[2];\n            osg::Vec3f halfExtents = world->getHalfExtents(ptr);\n            float maxDistToCheck = isMoving ? maxDistForPartialAvoiding : maxDistForStrictAvoiding;\n\n            float timeToCheck = maxTimeToCheck;\n            if (!shouldGiveWay && !aiSequence.isEmpty())\n                timeToCheck = std::min(timeToCheck, getTimeToDestination(**aiSequence.begin(), basePos, maxSpeed, duration, halfExtents));\n\n            float timeToCollision = timeToCheck;\n            osg::Vec2f movementCorrection(0, 0);\n            float angleToApproachingActor = 0;\n\n            // Iterate through all other actors and predict collisions.\n            for(PtrActorMap::iterator otherIter(mActors.begin()); otherIter != mActors.end(); ++otherIter)\n            {\n                const MWWorld::Ptr& otherPtr = otherIter->first;\n                if (otherPtr == ptr || otherPtr == currentTarget)\n                    continue;\n\n                osg::Vec3f otherHalfExtents = world->getHalfExtents(otherPtr);\n                osg::Vec3f deltaPos = otherPtr.getRefData().getPosition().asVec3() - basePos;\n                osg::Vec2f relPos = Misc::rotateVec2f(osg::Vec2f(deltaPos.x(), deltaPos.y()), baseRotZ);\n                float dist = deltaPos.length();\n\n                // Ignore actors which are not close enough or come from behind.\n                if (dist > maxDistToCheck || relPos.y() < 0)\n                    continue;\n\n                // Don't check for a collision if vertical distance is greater then the actor's height.\n                if (deltaPos.z() > halfExtents.z() * 2 || deltaPos.z() < -otherHalfExtents.z() * 2)\n                    continue;\n\n                osg::Vec3f speed = otherPtr.getClass().getMovementSettings(otherPtr).asVec3() *\n                                   otherPtr.getClass().getMaxSpeed(otherPtr);\n                float rotZ = otherPtr.getRefData().getPosition().rot[2];\n                osg::Vec2f relSpeed = Misc::rotateVec2f(osg::Vec2f(speed.x(), speed.y()), baseRotZ - rotZ) - baseSpeed;\n\n                float collisionDist = minGap + world->getHalfExtents(ptr).x() + world->getHalfExtents(otherPtr).x();\n                collisionDist = std::min(collisionDist, relPos.length());\n\n                // Find the earliest `t` when |relPos + relSpeed * t| == collisionDist.\n                float vr = relPos.x() * relSpeed.x() + relPos.y() * relSpeed.y();\n                float v2 = relSpeed.length2();\n                float Dh = vr * vr - v2 * (relPos.length2() - collisionDist * collisionDist);\n                if (Dh <= 0 || v2 == 0)\n                    continue; // No solution; distance is always >= collisionDist.\n                float t = (-vr - std::sqrt(Dh)) / v2;\n\n                if (t < 0 || t > timeToCollision)\n                    continue;\n\n                // Check visibility and awareness last as it's expensive.\n                if (!MWBase::Environment::get().getWorld()->getLOS(otherPtr, ptr))\n                    continue;\n                if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(otherPtr, ptr))\n                    continue;\n\n                timeToCollision = t;\n                angleToApproachingActor = std::atan2(deltaPos.x(), deltaPos.y());\n                osg::Vec2f posAtT = relPos + relSpeed * t;\n                float coef = (posAtT.x() * relSpeed.x() + posAtT.y() * relSpeed.y()) / (collisionDist * collisionDist * maxSpeed);\n                coef *= osg::clampBetween((maxDistForPartialAvoiding - dist) / (maxDistForPartialAvoiding - maxDistForStrictAvoiding), 0.f, 1.f);\n                movementCorrection = posAtT * coef;\n                if (otherPtr.getClass().getCreatureStats(otherPtr).isDead())\n                    // In case of dead body still try to go around (it looks natural), but reduce the correction twice.\n                    movementCorrection.y() *= 0.5f;\n            }\n\n            if (timeToCollision < timeToCheck)\n            {\n                // Try to evade the nearest collision.\n                osg::Vec2f newMovement = origMovement + movementCorrection;\n                // Step to the side rather than backward. Otherwise player will be able to push the NPC far away from it's original location.\n                newMovement.y() = std::max(newMovement.y(), 0.f);\n                newMovement.normalize();\n                if (isMoving)\n                    newMovement *= origMovement.length(); // Keep the original speed.\n                movement.mPosition[0] = newMovement.x();\n                movement.mPosition[1] = newMovement.y();\n                if (shouldTurnToApproachingActor)\n                    zTurn(ptr, angleToApproachingActor);\n            }\n        }\n    }\n\n    void Actors::update (float duration, bool paused)\n    {\n        if(!paused)\n        {\n            static float timerUpdateHeadTrack = 0;\n            static float timerUpdateEquippedLight = 0;\n            static float timerUpdateHello = 0;\n            const float updateEquippedLightInterval = 1.0f;\n\n            if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;\n            if (timerUpdateHello >= 0.25f) timerUpdateHello = 0;\n            if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;\n            if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;\n\n            // show torches only when there are darkness and no precipitations\n            MWBase::World* world = MWBase::Environment::get().getWorld();\n            bool showTorches = world->useTorches();\n\n            MWWorld::Ptr player = getPlayer();\n            const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();\n\n            /// \\todo move update logic to Actor class where appropriate\n\n            std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> > cachedAllies; // will be filled as engageCombat iterates\n\n            bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive();\n            int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId();\n            if (attackedByPlayerId != -1)\n            {\n                const MWWorld::Ptr playerHitAttemptActor = world->searchPtrViaActorId(attackedByPlayerId);\n\n                if (!playerHitAttemptActor.isInCell())\n                    player.getClass().getCreatureStats(player).setHitAttemptActorId(-1);\n            }\n            bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();\n\n             // AI and magic effects update\n            for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)\n            {\n                bool isPlayer = iter->first == player;\n                CharacterController* ctrl = iter->second->getCharacterController();\n\n                float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2();\n                // AI processing is only done within given distance to the player.\n                bool inProcessingRange = distSqr <= mActorsProcessingRange*mActorsProcessingRange;\n\n                /*\n                    Start of tes3mp change (minor)\n\n                    Instead of merely updating the player character's mAttackingOrSpell here,\n                    prepare an Attack packet for the LocalPlayer\n                */\n                if (isPlayer)\n                {\n                    bool state = MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell();\n                    DrawState_ dstate = player.getClass().getNpcStats(player).getDrawState();\n                    ctrl->setAttackingOrSpell(world->getPlayer().getAttackingOrSpell());\n\n                    if (dstate == DrawState_Weapon)\n                    {\n                        mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(iter->first);\n\n                        if (localAttack->pressed != state)\n                        {\n                            MechanicsHelper::resetAttack(localAttack);\n                            localAttack->type = MechanicsHelper::isUsingRangedWeapon(player) ? mwmp::Attack::RANGED : mwmp::Attack::MELEE;\n                            localAttack->pressed = state;\n\n                            // Prepare this attack for sending as long as it's not a ranged attack that's being released,\n                            // because we need to get the final attackStrength for that from WeaponAnimation to have the\n                            // correct projectile speed\n                            if (localAttack->type == mwmp::Attack::MELEE || state)\n                                localAttack->shouldSend = true;\n                        }\n                    }\n                }\n                /*\n                    End of tes3mp change (minor)\n                */\n\n                /*\n                    Start of tes3mp change (major)\n\n                    Allow this code to use the same logic for DedicatedPlayers as for LocalPlayers\n                */\n\n                // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player.\n                if (iter->first != player && !mwmp::PlayerList::isDedicatedPlayer(iter->first) &&(iter->first.getClass().getCreatureStats(iter->first).isDead()\n                    || !iter->first.getClass().getCreatureStats(iter->first).getAiSequence().isInCombat()\n                    || !inProcessingRange))\n                {\n                    iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActorId(-1);\n                    if (player.getClass().getCreatureStats(player).getHitAttemptActorId() == iter->first.getClass().getCreatureStats(iter->first).getActorId())\n                        player.getClass().getCreatureStats(player).setHitAttemptActorId(-1);\n\n                    mwmp::PlayerList::clearHitAttemptActorId(iter->first.getClass().getCreatureStats(iter->first).getActorId());\n                }\n                /*\n                    End of tes3mp change (major)\n                */\n\n                iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);\n\n                const Misc::TimerStatus engageCombatTimerStatus = iter->second->updateEngageCombatTimer(duration);\n\n                // For dead actors we need to update looping spell particles\n                if (iter->first.getClass().getCreatureStats(iter->first).isDead())\n                {\n                    // They can be added during the death animation\n                    if (!iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished())\n                        adjustMagicEffects(iter->first);\n                    ctrl->updateContinuousVfx();\n                }\n                else\n                {\n                    bool cellChanged = world->hasCellChanged();\n                    MWWorld::Ptr actor = iter->first; // make a copy of the map key to avoid it being invalidated when the player teleports\n                    updateActor(actor, duration);\n\n                    // Looping magic VFX update\n                    // Note: we need to do this before any of the animations are updated.\n                    // Reaching the text keys may trigger Hit / Spellcast (and as such, particles),\n                    // so updating VFX immediately after that would just remove the particle effects instantly.\n                    // There needs to be a magic effect update in between.\n                    ctrl->updateContinuousVfx();\n\n                    if (!cellChanged && world->hasCellChanged())\n                    {\n                        return; // for now abort update of the old cell when cell changes by teleportation magic effect\n                                // a better solution might be to apply cell changes at the end of the frame\n                    }\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Allow AI processing for LocalActors and partially for DedicatedActors\n                    */\n                    bool isLocalActor = mwmp::Main::get().getCellController()->isLocalActor(actor);\n                    bool isDedicatedActor = mwmp::Main::get().getCellController()->isDedicatedActor(actor);\n\n                    if (inProcessingRange && (aiActive || isLocalActor || isDedicatedActor))\n                    {\n                        if (engageCombatTimerStatus == Misc::TimerStatus::Elapsed && (isLocalActor || aiActive))\n                        {\n                            if (!isPlayer)\n                                adjustCommandedActor(iter->first);\n\n                            for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)\n                            {\n                                if (it->first == iter->first || isPlayer) // player is not AI-controlled\n                                    continue;\n                                engageCombat(iter->first, it->first, cachedAllies, it->first == player);\n                            }\n                        }\n                        if (timerUpdateHeadTrack == 0)\n                        {\n                            float sqrHeadTrackDistance = std::numeric_limits<float>::max();\n                            MWWorld::Ptr headTrackTarget;\n\n                            MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);\n                            bool firstPersonPlayer = isPlayer && world->isFirstPerson();\n                            bool inCombatOrPursue = stats.getAiSequence().isInCombat() || stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue);\n                            MWWorld::Ptr activePackageTarget;\n\n                            // 1. Unconsious actor can not track target\n                            // 2. Actors in combat and pursue mode do not bother to headtrack anyone except their target\n                            // 3. Player character does not use headtracking in the 1st-person view\n                            if (!stats.getKnockedDown() && !firstPersonPlayer)\n                            {\n                                if (inCombatOrPursue)\n                                    activePackageTarget = stats.getAiSequence().getActivePackage().getTarget();\n\n                                for (PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)\n                                {\n                                    if (it->first == iter->first)\n                                        continue;\n\n                                    if (inCombatOrPursue && it->first != activePackageTarget)\n                                        continue;\n\n                                    updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance, inCombatOrPursue);\n                                }\n                            }\n\n                            ctrl->setHeadTrackTarget(headTrackTarget);\n                        }\n\n                        if (iter->first.getClass().isNpc() && iter->first != player && (isLocalActor || aiActive))\n                            updateCrimePursuit(iter->first, duration);\n\n                        if (iter->first != player && (isLocalActor || aiActive))\n                        {\n                            CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);\n                            if (isConscious(iter->first))\n                            {\n                                stats.getAiSequence().execute(iter->first, *ctrl, duration);\n                                updateGreetingState(iter->first, *iter->second, timerUpdateHello > 0);\n                                playIdleDialogue(iter->first);\n                                updateMovementSpeed(iter->first);\n                            }\n                        }\n                    }\n                    else if ((isLocalActor || aiActive) && iter->first != player && isConscious(iter->first))\n                    {\n                        CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);\n                        stats.getAiSequence().execute(iter->first, *ctrl, duration, /*outOfRange*/true);\n                    }\n                    /*\n                        End of tes3mp change (major)\n                    */\n\n                    if(iter->first.getClass().isNpc())\n                    {\n                        // We can not update drowning state for actors outside of AI distance - they can not resurface to breathe\n                        if (inProcessingRange)\n                            updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer);\n\n                        calculateNpcStatModifiers(iter->first, duration);\n\n                        if (timerUpdateEquippedLight == 0)\n                            updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches);\n                    }\n                }\n            }\n\n            static const bool avoidCollisions = Settings::Manager::getBool(\"NPCs avoid collisions\", \"Game\");\n            if (avoidCollisions)\n                predictAndAvoidCollisions(duration);\n\n            timerUpdateHeadTrack += duration;\n            timerUpdateEquippedLight += duration;\n            timerUpdateHello += duration;\n            mTimerDisposeSummonsCorpses += duration;\n\n            // Animation/movement update\n            CharacterController* playerCharacter = nullptr;\n            for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)\n            {\n                const float dist = (playerPos - iter->first.getRefData().getPosition().asVec3()).length();\n                bool isPlayer = iter->first == player;\n                CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);\n                // Actors with active AI should be able to move.\n                bool alwaysActive = false;\n                if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed())\n                {\n                    MWMechanics::AiSequence& seq = stats.getAiSequence();\n                    alwaysActive = !seq.isEmpty() && seq.getActivePackage().alwaysActive();\n                }\n                bool inRange = isPlayer || dist <= mActorsProcessingRange || alwaysActive;\n                int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower)\n                if (isPlayer)\n                    activeFlag = 2;\n                int active = inRange ? activeFlag : 0;\n\n                CharacterController* ctrl = iter->second->getCharacterController();\n                ctrl->setActive(active);\n\n                if (!inRange)\n                {\n                    iter->first.getRefData().getBaseNode()->setNodeMask(0);\n                    world->setActorCollisionMode(iter->first, false, false);\n                    continue;\n                }\n                else if (!isPlayer)\n                    iter->first.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor);\n\n                const bool isDead = iter->first.getClass().getCreatureStats(iter->first).isDead();\n                if (!isDead && (!godmode || !isPlayer) && iter->first.getClass().getCreatureStats(iter->first).isParalyzed())\n                    ctrl->skipAnim();\n\n                // Handle player last, in case a cell transition occurs by casting a teleportation spell\n                // (would invalidate the iterator)\n                if (iter->first == getPlayer())\n                {\n                    playerCharacter = ctrl;\n                    continue;\n                }\n\n                world->setActorCollisionMode(iter->first, true, !iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished());\n                ctrl->update(duration);\n\n                updateVisibility(iter->first, ctrl);\n            }\n\n            if (playerCharacter)\n            {\n                playerCharacter->update(duration);\n                playerCharacter->setVisibility(1.f);\n            }\n\n            for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)\n            {\n                const MWWorld::Class &cls = iter->first.getClass();\n                CreatureStats &stats = cls.getCreatureStats(iter->first);\n\n                //KnockedOutOneFrameLogic\n                //Used for \"OnKnockedOut\" command\n                //Put here to ensure that it's run for PRECISELY one frame.\n                if (stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame())\n                { //Start it for one frame if nessesary\n                    stats.setKnockedDownOneFrame(true);\n                }\n                else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame())\n                { //Turn off KnockedOutOneframe\n                    stats.setKnockedDownOneFrame(false);\n                    stats.setKnockedDownOverOneFrame(true);\n                }\n            }\n\n            killDeadActors();\n            updateSneaking(playerCharacter, duration);\n        }\n\n        updateCombatMusic();\n    }\n\n    void Actors::notifyDied(const MWWorld::Ptr &actor)\n    {\n        actor.getClass().getCreatureStats(actor).notifyDied();\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't increment the kill count and expect the server to send a packet to increment\n            it for us instead\n        */\n        //++mDeathCount[Misc::StringUtils::lowerCase(actor.getCellRef().getRefId())];\n        /*\n            End of tes3mp change (major)\n        */\n    }\n\n    void Actors::resurrect(const MWWorld::Ptr &ptr)\n    {\n        PtrActorMap::iterator iter = mActors.find(ptr);\n        if(iter != mActors.end())\n        {\n            if(iter->second->getCharacterController()->isDead())\n            {\n                // Actor has been resurrected. Notify the CharacterController and re-enable collision.\n                MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true);\n                iter->second->getCharacterController()->resurrect();\n            }\n        }\n    }\n\n    void Actors::killDeadActors()\n    {\n        for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)\n        {\n            const MWWorld::Class &cls = iter->first.getClass();\n            CreatureStats &stats = cls.getCreatureStats(iter->first);\n\n            if(!stats.isDead())\n                continue;\n\n            MWBase::Environment::get().getWorld()->removeActorPath(iter->first);\n            CharacterController::KillResult killResult = iter->second->getCharacterController()->kill();\n            if (killResult == CharacterController::Result_DeathAnimStarted)\n            {\n                // Play dying words\n                // Note: It's not known whether the soundgen tags scream, roar, and moan are reliable\n                // for NPCs since some of the npc death animation files are missing them.\n                /*\n                    Start of tes3mp change (major)\n\n                    Don't play dying words for NPCs who have already been marked as having\n                    finished their death animations from elsewhere in the code\n                */\n                if (!stats.isDeathAnimationFinished())\n                    MWBase::Environment::get().getDialogueManager()->say(iter->first, \"hit\");\n                /*\n                    End of tes3mp change (major)\n                */\n\n                // Apply soultrap\n                if (iter->first.getTypeName() == typeid(ESM::Creature).name())\n                {\n                    SoulTrap soulTrap (iter->first);\n                    stats.getActiveSpells().visitEffectSources(soulTrap);\n                }\n\n                // Magic effects will be reset later, and the magic effect that could kill the actor\n                // needs to be determined now\n                calculateCreatureStatModifiers(iter->first, 0);\n\n                if (cls.isEssential(iter->first))\n                    MWBase::Environment::get().getWindowManager()->messageBox(\"#{sKilledEssential}\");\n            }\n            else if (killResult == CharacterController::Result_DeathAnimJustFinished)\n            {\n                bool isPlayer = iter->first == getPlayer();\n                notifyDied(iter->first);\n\n                // Reset magic effects and recalculate derived effects\n                // One case where we need this is to make sure bound items are removed upon death\n                stats.modifyMagicEffects(MWMechanics::MagicEffects());\n                stats.getActiveSpells().clear();\n                // Make sure spell effects are removed\n                purgeSpellEffects(stats.getActorId());\n\n                // Reset dynamic stats, attributes and skills\n                calculateCreatureStatModifiers(iter->first, 0);\n                if (iter->first.getClass().isNpc())\n                    calculateNpcStatModifiers(iter->first, 0);\n\n                if (isPlayer)\n                {\n                    //player's death animation is over\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        The main menu no longer opens when the local player dies,\n                        because of automatic respawning by default\n                    */\n                    //MWBase::Environment::get().getStateManager()->askLoadRecent();\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n                else\n                {\n                    // NPC death animation is over, disable actor collision\n                    MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);\n                }\n\n                // Play Death Music if it was the player dying\n                if(iter->first == getPlayer())\n                    MWBase::Environment::get().getSoundManager()->streamMusic(\"Special/MW_Death.mp3\");\n            }\n        }\n    }\n\n    void Actors::cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId)\n    {\n        MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId);\n\n        /*\n            Start of tes3mp change (major)\n\n            Do a cleanup here and send an ID_OBJECT_DELETE packet every time a summoned creature\n            despawns for the local player or for a local actor\n        */\n        if (!ptr.isEmpty() &&\n            (casterStats.getActorId() == getPlayer().getClass().getCreatureStats(getPlayer()).getActorId() || mwmp::Main::get().getCellController()->hasLocalAuthority(*ptr.getCell()->getCell())))\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectGeneric(ptr);\n            objectList->sendObjectDelete();\n        /*\n            End of tes3mp change (major)\n        */\n\n            const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()\n                    .search(\"VFX_Summon_End\");\n            if (fx)\n                MWBase::Environment::get().getWorld()->spawnEffect(\"meshes\\\\\" + fx->mModel,\n                    \"\", ptr.getRefData().getPosition().asVec3());\n\n            // Remove the summoned creature's summoned creatures as well\n            MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n            std::map<ESM::SummonKey, int>& creatureMap = stats.getSummonedCreatureMap();\n            for (const auto& creature : creatureMap)\n                cleanupSummonedCreature(stats, creature.second);\n            creatureMap.clear();\n        }\n        else if (creatureActorId != -1)\n        {\n            // We didn't find the creature. It's probably in an inactive cell.\n            // Add to graveyard so we can delete it when the cell becomes active.\n            std::vector<int>& graveyard = casterStats.getSummonedCreatureGraveyard();\n            graveyard.push_back(creatureActorId);\n        }\n\n        purgeSpellEffects(creatureActorId);\n    }\n\n    void Actors::purgeSpellEffects(int casterActorId)\n    {\n        for (PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)\n        {\n            MWMechanics::ActiveSpells& spells = iter->first.getClass().getCreatureStats(iter->first).getActiveSpells();\n            spells.purge(casterActorId);\n        }\n    }\n\n    void Actors::rest(double hours, bool sleep)\n    {\n        float duration = hours * 3600.f;\n        float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();\n        if (timeScale != 0.f)\n            duration /= timeScale;\n\n        const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n        const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();\n\n        for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)\n        {\n            if (iter->first.getClass().getCreatureStats(iter->first).isDead())\n            {\n                iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);\n                continue;\n            }\n\n            if (!sleep || iter->first == player)\n                restoreDynamicStats(iter->first, hours, sleep);\n\n            if ((!iter->first.getRefData().getBaseNode()) ||\n                    (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange)\n                continue;\n\n            adjustMagicEffects (iter->first);\n            if (iter->first.getClass().getCreatureStats(iter->first).needToRecalcDynamicStats())\n                calculateDynamicStats (iter->first);\n\n            calculateCreatureStatModifiers (iter->first, duration);\n            if (iter->first.getClass().isNpc())\n                calculateNpcStatModifiers(iter->first, duration);\n\n            iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);\n\n            MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);\n            if (animation)\n            {\n                animation->removeEffects();\n                MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first);\n            }\n        }\n\n        fastForwardAi();\n    }\n\n    void Actors::updateSneaking(CharacterController* ctrl, float duration)\n    {\n        static float sneakTimer = 0.f; // Times update of sneak icon\n\n        if (!ctrl)\n        {\n            MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);\n            return;\n        }\n\n        MWWorld::Ptr player = getPlayer();\n\n        if (!MWBase::Environment::get().getMechanicsManager()->isSneaking(player))\n        {\n            MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);\n            return;\n        }\n\n        static float sneakSkillTimer = 0.f; // Times sneak skill progress from \"avoid notice\"\n\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        const MWWorld::Store<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>();\n        static const float fSneakUseDist = gmst.find(\"fSneakUseDist\")->mValue.getFloat();\n        static const float fSneakUseDelay = gmst.find(\"fSneakUseDelay\")->mValue.getFloat();\n\n        if (sneakTimer >= fSneakUseDelay)\n            sneakTimer = 0.f;\n\n        if (sneakTimer == 0.f)\n        {\n            // Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress.\n            bool avoidedNotice = false;\n            bool detected = false;\n\n            std::vector<MWWorld::Ptr> observers;\n            osg::Vec3f position(player.getRefData().getPosition().asVec3());\n            float radius = std::min(fSneakUseDist, mActorsProcessingRange);\n            getObjectsInRange(position, radius, observers);\n\n            for (const MWWorld::Ptr &observer : observers)\n            {\n                if (observer == player || observer.getClass().getCreatureStats(observer).isDead())\n                    continue;\n\n                /*\n                    Start of tes3mp addition\n\n                    Don't make allied players break each other's sneaking\n                */\n                if (MechanicsHelper::isTeamMember(observer, player))\n                    continue;\n                /*\n                    End of tes3mp addition\n                */\n\n                if (world->getLOS(player, observer))\n                {\n                    if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, observer))\n                    {\n                        detected = true;\n                        avoidedNotice = false;\n                        MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);\n                        break;\n                    }\n                    else\n                    {\n                        avoidedNotice = true;\n                    }\n                }\n            }\n\n            if (sneakSkillTimer >= fSneakUseDelay)\n                sneakSkillTimer = 0.f;\n\n            if (avoidedNotice && sneakSkillTimer == 0.f)\n                player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0);\n\n            if (!detected)\n                MWBase::Environment::get().getWindowManager()->setSneakVisibility(true);\n        }\n\n        sneakTimer += duration;\n        sneakSkillTimer += duration;\n    }\n\n    int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const\n    {\n        float healthPerHour, magickaPerHour;\n        getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour);\n\n        CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n        bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0;\n\n        float healthHours  = healthPerHour > 0\n                             ? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour\n                             : 1.0f;\n        float magickaHours = magickaPerHour > 0 && !stunted\n                              ? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour\n                              : 1.0f;\n\n        int autoHours = static_cast<int>(std::ceil(std::max(1.f, std::max(healthHours, magickaHours))));\n        return autoHours;\n    }\n\n    int Actors::countDeaths (const std::string& id) const\n    {\n        std::map<std::string, int>::const_iterator iter = mDeathCount.find(id);\n        if(iter != mDeathCount.end())\n            return iter->second;\n        return 0;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set the number of deaths for an actor with the given refId\n    */\n    void Actors::setDeaths(const std::string& refId, int number)\n    {\n        mDeathCount[refId] = number;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void Actors::forceStateUpdate(const MWWorld::Ptr & ptr)\n    {\n        PtrActorMap::iterator iter = mActors.find(ptr);\n        if(iter != mActors.end())\n            iter->second->getCharacterController()->forceStateUpdate();\n    }\n\n    bool Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)\n    {\n        PtrActorMap::iterator iter = mActors.find(ptr);\n        if(iter != mActors.end())\n        {\n            return iter->second->getCharacterController()->playGroup(groupName, mode, number, persist);\n        }\n        else\n        {\n            Log(Debug::Warning) << \"Warning: Actors::playAnimationGroup: Unable to find \" << ptr.getCellRef().getRefId();\n            return false;\n        }\n    }\n    void Actors::skipAnimation(const MWWorld::Ptr& ptr)\n    {\n        PtrActorMap::iterator iter = mActors.find(ptr);\n        if(iter != mActors.end())\n            iter->second->getCharacterController()->skipAnim();\n    }\n\n    bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName)\n    {\n        PtrActorMap::iterator iter = mActors.find(ptr);\n        if(iter != mActors.end())\n            return iter->second->getCharacterController()->isAnimPlaying(groupName);\n        return false;\n    }\n\n    void Actors::persistAnimationStates()\n    {\n        for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)\n            iter->second->getCharacterController()->persistAnimationState();\n    }\n\n    void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out)\n    {\n        for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)\n        {\n            if ((iter->first.getRefData().getPosition().asVec3() - position).length2() <= radius*radius)\n                out.push_back(iter->first);\n        }\n    }\n\n    bool Actors::isAnyObjectInRange(const osg::Vec3f& position, float radius)\n    {\n        for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)\n        {\n            if ((iter->first.getRefData().getPosition().asVec3() - position).length2() <= radius*radius)\n                return true;\n        }\n\n        return false;\n    }\n\n    std::list<MWWorld::Ptr> Actors::getActorsSidingWith(const MWWorld::Ptr& actor)\n    {\n        std::list<MWWorld::Ptr> list;\n        for(PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)\n        {\n            const MWWorld::Ptr &iteratedActor = iter->first;\n            if (iteratedActor == getPlayer())\n                continue;\n\n            const bool sameActor = (iteratedActor == actor);\n\n            const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);\n            if (stats.isDead())\n                continue;\n\n            /*\n                Start of tes3mp addition\n\n                If we're checking the LocalPlayer and the iteratedActor is a DedicatedPlayer belonging to this one's alliedPlayers,\n                include the iteratedActor in the actors siding with the player\n\n                Alternatively, if we're checking a DedicatedPlayer and the iteratedActor is a LocalPlayer or DedicatedPlayer\n                belonging to their alliedPlayers, include the iteratedActor in the actors siding with them\n            */\n            if (actor == getPlayer() && mwmp::PlayerList::isDedicatedPlayer(iteratedActor))\n            {\n                if (Utils::vectorContains(mwmp::Main::get().getLocalPlayer()->alliedPlayers, mwmp::PlayerList::getPlayer(iteratedActor)->guid))\n                {\n                    list.push_back(iteratedActor);\n                }\n            }\n            else if (mwmp::PlayerList::isDedicatedPlayer(actor))\n            {\n                if (iteratedActor == getPlayer() &&\n                    Utils::vectorContains(mwmp::PlayerList::getPlayer(actor)->alliedPlayers, mwmp::Main::get().getLocalPlayer()->guid))\n                {\n                    list.push_back(iteratedActor);\n                }\n                else if (mwmp::PlayerList::isDedicatedPlayer(iteratedActor) &&\n                    Utils::vectorContains(mwmp::PlayerList::getPlayer(actor)->alliedPlayers, mwmp::PlayerList::getPlayer(iteratedActor)->guid))\n                {\n                    list.push_back(iteratedActor);\n                }\n            }\n            /*\n                End of tes3mp addition\n            */\n\n            // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package\n            // Actors that are targeted by this actor's Follow or Escort packages also side with them\n            for (const auto& package : stats.getAiSequence())\n            {\n                if (package->sideWithTarget() && !package->getTarget().isEmpty())\n                {\n                    if (sameActor)\n                    {\n                        list.push_back(package->getTarget());\n                    }\n                    else if (package->getTarget() == actor)\n                    {\n                        list.push_back(iteratedActor);\n                    }\n                    break;\n                }\n                else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)\n                    break;\n            }\n        }\n        return list;\n    }\n\n    std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)\n    {\n        std::list<MWWorld::Ptr> list;\n        forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)\n        {\n            if (package->followTargetThroughDoors() && package->getTarget() == actor)\n                list.push_back(iter.first);\n            else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)\n                return false;\n            return true;\n        });\n        return list;\n    }\n\n    void Actors::getActorsFollowing(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out) {\n        std::list<MWWorld::Ptr> followers = getActorsFollowing(actor);\n        for(const MWWorld::Ptr &follower : followers)\n            if (out.insert(follower).second)\n                getActorsFollowing(follower, out);\n    }\n\n    void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out) {\n        std::list<MWWorld::Ptr> followers = getActorsSidingWith(actor);\n        for(const MWWorld::Ptr &follower : followers)\n            if (out.insert(follower).second)\n                getActorsSidingWith(follower, out);\n    }\n\n    void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies) {\n        // If we have already found actor's allies, use the cache\n        std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >::const_iterator search = cachedAllies.find(actor);\n        if (search != cachedAllies.end())\n            out.insert(search->second.begin(), search->second.end());\n        else\n        {\n            std::list<MWWorld::Ptr> followers = getActorsSidingWith(actor);\n            for (const MWWorld::Ptr &follower : followers)\n                if (out.insert(follower).second)\n                    getActorsSidingWith(follower, out, cachedAllies);\n\n            // Cache ptrs and their sets of allies\n            cachedAllies.insert(std::make_pair(actor, out));\n            for (const MWWorld::Ptr &iter : out)\n            {\n                search = cachedAllies.find(iter);\n                if (search == cachedAllies.end())\n                    cachedAllies.insert(std::make_pair(iter, out));\n            }\n        }\n    }\n\n    std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)\n    {\n        std::list<int> list;\n        forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)\n        {\n            if (package->followTargetThroughDoors() && package->getTarget() == actor)\n            {\n                list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());\n                return false;\n            }\n            else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)\n                return false;\n            return true;\n        });\n        return list;\n    }\n\n    std::map<int, MWWorld::Ptr> Actors::getActorsFollowingByIndex(const MWWorld::Ptr &actor)\n    {\n        std::map<int, MWWorld::Ptr> map;\n        forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)\n        {\n            if (package->followTargetThroughDoors() && package->getTarget() == actor)\n            {\n                int index = static_cast<const AiFollow*>(package.get())->getFollowIndex();\n                map[index] = iter.first;\n                return false;\n            }\n            else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)\n                return false;\n            return true;\n        });\n        return map;\n    }\n\n    std::list<MWWorld::Ptr> Actors::getActorsFighting(const MWWorld::Ptr& actor) {\n        std::list<MWWorld::Ptr> list;\n        std::vector<MWWorld::Ptr> neighbors;\n        osg::Vec3f position (actor.getRefData().getPosition().asVec3());\n        getObjectsInRange(position, mActorsProcessingRange, neighbors);\n        for(const MWWorld::Ptr& neighbor : neighbors)\n        {\n            if (neighbor == actor)\n                continue;\n\n            const CreatureStats &stats = neighbor.getClass().getCreatureStats(neighbor);\n            if (stats.isDead())\n                continue;\n\n            if (stats.getAiSequence().isInCombat(actor))\n                list.push_front(neighbor);\n        }\n        return list;\n    }\n\n    std::list<MWWorld::Ptr> Actors::getEnemiesNearby(const MWWorld::Ptr& actor)\n    {\n        std::list<MWWorld::Ptr> list;\n        std::vector<MWWorld::Ptr> neighbors;\n        osg::Vec3f position (actor.getRefData().getPosition().asVec3());\n        getObjectsInRange(position, mActorsProcessingRange, neighbors);\n\n        std::set<MWWorld::Ptr> followers;\n        getActorsFollowing(actor, followers);\n        for (auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor)\n        {\n            const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor);\n            if (stats.isDead() || *neighbor == actor || neighbor->getClass().isPureWaterCreature(*neighbor))\n                continue;\n\n            const bool isFollower = followers.find(*neighbor) != followers.end();\n\n            if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*neighbor, actor) && !isFollower))\n                list.push_back(*neighbor);\n        }\n        return list;\n    }\n\n\n    void Actors::write (ESM::ESMWriter& writer, Loading::Listener& listener) const\n    {\n        writer.startRecord(ESM::REC_DCOU);\n        for (std::map<std::string, int>::const_iterator it = mDeathCount.begin(); it != mDeathCount.end(); ++it)\n        {\n            writer.writeHNString(\"ID__\", it->first);\n            writer.writeHNT (\"COUN\", it->second);\n        }\n        writer.endRecord(ESM::REC_DCOU);\n    }\n\n    void Actors::readRecord (ESM::ESMReader& reader, uint32_t type)\n    {\n        if (type == ESM::REC_DCOU)\n        {\n            while (reader.isNextSub(\"ID__\"))\n            {\n                std::string id = reader.getHString();\n                int count;\n                reader.getHNT(count, \"COUN\");\n                if (MWBase::Environment::get().getWorld()->getStore().find(id))\n                    mDeathCount[id] = count;\n            }\n        }\n    }\n\n    void Actors::clear()\n    {\n        PtrActorMap::iterator it(mActors.begin());\n        for (; it != mActors.end(); ++it)\n        {\n            delete it->second;\n            it->second = nullptr;\n        }\n        mActors.clear();\n        mDeathCount.clear();\n    }\n\n    void Actors::updateMagicEffects(const MWWorld::Ptr &ptr)\n    {\n        adjustMagicEffects(ptr);\n        calculateCreatureStatModifiers(ptr, 0.f);\n        if (ptr.getClass().isNpc())\n            calculateNpcStatModifiers(ptr, 0.f);\n    }\n\n    bool Actors::isReadyToBlock(const MWWorld::Ptr &ptr) const\n    {\n        PtrActorMap::const_iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return false;\n\n        return it->second->getCharacterController()->isReadyToBlock();\n    }\n\n    bool Actors::isCastingSpell(const MWWorld::Ptr &ptr) const\n    {\n        PtrActorMap::const_iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return false;\n\n        return it->second->getCharacterController()->isCastingSpell();\n    }\n\n    bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const\n    {\n        PtrActorMap::const_iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return false;\n        CharacterController* ctrl = it->second->getCharacterController();\n\n        return ctrl->isAttackingOrSpell();\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set the attackingOrSpell state from elsewhere in the code\n    */\n    void Actors::setAttackingOrSpell(const MWWorld::Ptr& ptr, bool state) const\n    {\n        PtrActorMap::const_iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return;\n        CharacterController* ctrl = it->second->getCharacterController();\n\n        ctrl->setAttackingOrSpell(state);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    int Actors::getGreetingTimer(const MWWorld::Ptr& ptr) const\n    {\n        PtrActorMap::const_iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return 0;\n\n        return it->second->getGreetingTimer();\n    }\n\n    float Actors::getAngleToPlayer(const MWWorld::Ptr& ptr) const\n    {\n        PtrActorMap::const_iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return 0.f;\n\n        return it->second->getAngleToPlayer();\n    }\n\n    GreetingState Actors::getGreetingState(const MWWorld::Ptr& ptr) const\n    {\n        PtrActorMap::const_iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return Greet_None;\n\n        return it->second->getGreetingState();\n    }\n\n    bool Actors::isTurningToPlayer(const MWWorld::Ptr& ptr) const\n    {\n        PtrActorMap::const_iterator it = mActors.find(ptr);\n        if (it == mActors.end())\n            return false;\n\n        return it->second->isTurningToPlayer();\n    }\n\n    void Actors::fastForwardAi()\n    {\n        if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())\n            return;\n\n        // making a copy since fast-forward could move actor to a different cell and invalidate the mActors iterator\n        PtrActorMap map = mActors;\n        for (PtrActorMap::iterator it = map.begin(); it != map.end(); ++it)\n        {\n            MWWorld::Ptr ptr = it->first;\n            if (ptr == getPlayer()\n                    || !isConscious(ptr)\n                    || ptr.getClass().getCreatureStats(ptr).isParalyzed())\n                continue;\n            MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();\n            seq.fastForward(ptr);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/actors.hpp",
    "content": "#ifndef GAME_MWMECHANICS_ACTORS_H\n#define GAME_MWMECHANICS_ACTORS_H\n\n#include <set>\n#include <vector>\n#include <string>\n#include <list>\n#include <map>\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n}\n\nnamespace osg\n{\n    class Vec3f;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n    class CellStore;\n}\n\nnamespace MWMechanics\n{\n    class Actor;\n    class CharacterController;\n    class CreatureStats;\n\n    class Actors\n    {\n            std::map<std::string, int> mDeathCount;\n\n            void addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor);\n            void removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor);\n\n            void adjustMagicEffects (const MWWorld::Ptr& creature);\n\n            void calculateDynamicStats (const MWWorld::Ptr& ptr);\n\n            void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration);\n            void calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration);\n\n            void calculateRestoration (const MWWorld::Ptr& ptr, float duration);\n\n            void updateDrowning (const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer);\n\n            void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip);\n\n            void updateCrimePursuit (const MWWorld::Ptr& ptr, float duration);\n\n            void killDeadActors ();\n\n            void purgeSpellEffects (int casterActorId);\n\n            void predictAndAvoidCollisions(float duration);\n\n        public:\n\n            Actors();\n            ~Actors();\n\n            typedef std::map<MWWorld::Ptr,Actor*> PtrActorMap;\n\n            PtrActorMap::const_iterator begin() { return mActors.begin(); }\n            PtrActorMap::const_iterator end() { return mActors.end(); }\n            std::size_t size() const { return mActors.size(); }\n\n            void notifyDied(const MWWorld::Ptr &actor);\n\n            /// Check if the target actor was detected by an observer\n            /// If the observer is a non-NPC, check all actors in AI processing distance as observers\n            bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer);\n\n            /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently\n            /// paused we may want to do it manually (after equipping permanent enchantment)\n            void updateMagicEffects (const MWWorld::Ptr& ptr);\n\n            void updateProcessingRange();\n            float getProcessingRange() const;\n\n            void addActor (const MWWorld::Ptr& ptr, bool updateImmediately=false);\n            ///< Register an actor for stats management\n            ///\n            /// \\note Dead actors are ignored.\n\n            void removeActor (const MWWorld::Ptr& ptr);\n            ///< Deregister an actor for stats management\n            ///\n            /// \\note Ignored, if \\a ptr is not a registered actor.\n\n            void resurrect (const MWWorld::Ptr& ptr);\n\n            void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false);\n\n            void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);\n            ///< Updates an actor with a new Ptr\n\n            void dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore);\n            ///< Deregister all actors (except for \\a ignore) in the given cell.\n\n            void updateCombatMusic();\n            ///< Update combat music state\n\n            void update (float duration, bool paused);\n            ///< Update actor stats and store desired velocity vectors in \\a movement\n\n            void updateActor (const MWWorld::Ptr& ptr, float duration);\n            ///< This function is normally called automatically during the update process, but it can\n            /// also be called explicitly at any time to force an update.\n\n            /** Start combat between two actors\n                @Notes: If againstPlayer = true then actor2 should be the Player.\n                        If one of the combatants is creature it should be actor1.\n            */\n            void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer);\n\n            void playIdleDialogue(const MWWorld::Ptr& actor);\n            void updateMovementSpeed(const MWWorld::Ptr& actor);\n            void updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly);\n            void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir);\n\n            void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,\n                                          MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance,\n                                          bool inCombatOrPursue);\n\n            void rest(double hours, bool sleep);\n            ///< Update actors while the player is waiting or sleeping.\n\n            void updateSneaking(CharacterController* ctrl, float duration);\n            ///< Update the sneaking indicator state according to the given player character controller.\n\n            void restoreDynamicStats(const MWWorld::Ptr& actor, double hours, bool sleep);\n\n            int getHoursToRest(const MWWorld::Ptr& ptr) const;\n            ///< Calculate how many hours the given actor needs to rest in order to be fully healed\n\n            void fastForwardAi();\n            ///< Simulate the passing of time\n\n            int countDeaths (const std::string& id) const;\n            ///< Return the number of deaths for actors with the given ID.\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the number of deaths for an actor with the given refId\n            */\n            void setDeaths(const std::string& refId, int number);\n            /*\n                End of tes3mp addition\n            */\n\n            bool isAttackPreparing(const MWWorld::Ptr& ptr);\n            bool isRunning(const MWWorld::Ptr& ptr);\n            bool isSneaking(const MWWorld::Ptr& ptr);\n\n            void forceStateUpdate(const MWWorld::Ptr &ptr);\n\n            bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false);\n            void skipAnimation(const MWWorld::Ptr& ptr);\n            bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName);\n            void persistAnimationStates();\n\n            void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out);\n\n            bool isAnyObjectInRange(const osg::Vec3f& position, float radius);\n\n            void cleanupSummonedCreature (CreatureStats& casterStats, int creatureActorId);\n\n            ///Returns the list of actors which are siding with the given actor in fights\n            /**ie AiFollow or AiEscort is active and the target is the actor **/\n            std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor);\n            std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor);\n\n            /// Recursive version of getActorsFollowing\n            void getActorsFollowing(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out);\n            /// Recursive version of getActorsSidingWith\n            void getActorsSidingWith(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out);\n            /// Recursive version of getActorsSidingWith that takes, adds to and returns a cache of actors mapped to their allies\n            void getActorsSidingWith(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies);\n\n            /// Get the list of AiFollow::mFollowIndex for all actors following this target\n            std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor);\n            std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor);\n\n            ///Returns the list of actors which are fighting the given actor\n            /**ie AiCombat is active and the target is the actor **/\n            std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor);\n\n            /// Unlike getActorsFighting, also returns actors that *would* fight the given actor if they saw him.\n            std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor);\n\n            void write (ESM::ESMWriter& writer, Loading::Listener& listener) const;\n\n            void readRecord (ESM::ESMReader& reader, uint32_t type);\n\n            void clear(); // Clear death counter\n\n            bool isCastingSpell(const MWWorld::Ptr& ptr) const;\n            bool isReadyToBlock(const MWWorld::Ptr& ptr) const;\n            bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the attackingOrSpell state from elsewhere in the code\n            */\n            void setAttackingOrSpell(const MWWorld::Ptr& ptr, bool state) const;\n            /*\n                End of tes3mp addition\n            */\n\n            int getGreetingTimer(const MWWorld::Ptr& ptr) const;\n            float getAngleToPlayer(const MWWorld::Ptr& ptr) const;\n            GreetingState getGreetingState(const MWWorld::Ptr& ptr) const;\n            bool isTurningToPlayer(const MWWorld::Ptr& ptr) const;\n\n    private:\n        void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl);\n        void applyCureEffects (const MWWorld::Ptr& actor);\n\n        PtrActorMap mActors;\n        float mTimerDisposeSummonsCorpses;\n        float mActorsProcessingRange;\n\n        bool mSmoothMovement;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/actorutil.cpp",
    "content": "#include \"actorutil.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n\nnamespace MWMechanics\n{\n    MWWorld::Ptr getPlayer()\n    {\n        return MWBase::Environment::get().getWorld()->getPlayerPtr();\n    }\n\n    bool isPlayerInCombat()\n    {\n        return MWBase::Environment::get().getWorld()->getPlayer().isInCombat();\n    }\n\n    bool canActorMoveByZAxis(const MWWorld::Ptr& actor)\n    {\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        return (actor.getClass().canSwim(actor) && world->isSwimming(actor)) || world->isFlying(actor);\n    }\n\n    bool hasWaterWalking(const MWWorld::Ptr& actor)\n    {\n        const MWMechanics::MagicEffects& effects = actor.getClass().getCreatureStats(actor).getMagicEffects();\n        return effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() > 0;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/actorutil.hpp",
    "content": "#ifndef OPENMW_MWMECHANICS_ACTORUTIL_H\n#define OPENMW_MWMECHANICS_ACTORUTIL_H\n\n#include <algorithm>\n\n#include <components/esm/loadcont.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/loadnpc.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"./creaturestats.hpp\"\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    enum GreetingState\n    {\n        Greet_None,\n        Greet_InProgress,\n        Greet_Done\n    };\n\n    MWWorld::Ptr getPlayer();\n    bool isPlayerInCombat();\n    bool canActorMoveByZAxis(const MWWorld::Ptr& actor);\n    bool hasWaterWalking(const MWWorld::Ptr& actor);\n\n    template<class T>\n    void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value)\n    {\n        T copy = *MWBase::Environment::get().getWorld()->getStore().get<T>().find(id);\n        switch(setting)\n        {\n            case MWMechanics::CreatureStats::AiSetting::AI_Hello:\n                copy.mAiData.mHello = value;\n                break;\n            case MWMechanics::CreatureStats::AiSetting::AI_Fight:\n                copy.mAiData.mFight = value;\n                break;\n            case MWMechanics::CreatureStats::AiSetting::AI_Flee:\n                copy.mAiData.mFlee = value;\n                break;\n            case MWMechanics::CreatureStats::AiSetting::AI_Alarm:\n                copy.mAiData.mAlarm = value;\n                break;\n            default:\n                assert(0);\n        }\n        MWBase::Environment::get().getWorld()->createOverrideRecord(copy);\n    }\n\n    template<class T>\n    void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount)\n    {\n        T copy = *MWBase::Environment::get().getWorld()->getStore().get<T>().find(actorId);\n        for(auto& it : copy.mInventory.mList)\n        {\n            if(Misc::StringUtils::ciEqual(it.mItem, itemId))\n            {\n                int sign = it.mCount < 1 ? -1 : 1;\n                it.mCount = sign * std::max(it.mCount * sign + amount, 0);\n                MWBase::Environment::get().getWorld()->createOverrideRecord(copy);\n                return;\n            }\n        }\n        if(amount > 0)\n        {\n            ESM::ContItem cont;\n            cont.mItem = itemId;\n            cont.mCount = amount;\n            copy.mInventory.mList.push_back(cont);\n            MWBase::Environment::get().getWorld()->createOverrideRecord(copy);\n        }\n    }\n\n    template void setBaseAISetting<ESM::Creature>(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value);\n    template void setBaseAISetting<ESM::NPC>(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value);\n    template void modifyBaseInventory<ESM::Creature>(const std::string& actorId, const std::string& itemId, int amount);\n    template void modifyBaseInventory<ESM::NPC>(const std::string& actorId, const std::string& itemId, int amount);\n    template void modifyBaseInventory<ESM::Container>(const std::string& containerId, const std::string& itemId, int amount);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiactivate.cpp",
    "content": "#include \"aiactivate.hpp\"\r\n\r\n#include <components/esm/aisequence.hpp>\r\n\r\n/*\r\n    Start of tes3mp addition\r\n\r\n    Include additional headers for multiplayer purposes\r\n*/\r\n#include \"../mwmp/Main.hpp\"\r\n#include \"../mwmp/Networking.hpp\"\r\n#include \"../mwmp/ObjectList.hpp\"\r\n/*\r\n    End of tes3mp addition\r\n*/\r\n\r\n#include \"../mwbase/world.hpp\"\r\n#include \"../mwbase/environment.hpp\"\r\n\r\n#include \"../mwworld/class.hpp\"\r\n\r\n#include \"creaturestats.hpp\"\r\n#include \"movement.hpp\"\r\n#include \"steering.hpp\"\r\n\r\nnamespace MWMechanics\r\n{\r\n    AiActivate::AiActivate(const std::string &objectId)\r\n        : mObjectId(objectId)\r\n    {\r\n    }\r\n\r\n    /*\r\n        Start of tes3mp addition\r\n\r\n        Allow AiActivate to be initialized using a Ptr instead of a refId\r\n    */\r\n    AiActivate::AiActivate(MWWorld::Ptr object)\r\n        : mObjectId(\"\")\r\n    {\r\n        mObjectPtr = object;\r\n    }\r\n    /*\r\n        End of tes3mp addition\r\n    */\r\n\r\n    bool AiActivate::execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)\r\n    {\r\n        /*\r\n            Start of tes3mp change (major)\r\n\r\n            Only search for an object based on its refId if we haven't provided a specific object already\r\n        */\r\n        const MWWorld::Ptr target = mObjectId.empty() ? mObjectPtr : MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false);\r\n        /*\r\n            End of tes3mp change (major)\r\n        */\r\n\r\n        actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);\r\n\r\n        // Stop if the target doesn't exist\r\n        // Really we should be checking whether the target is currently registered with the MechanicsManager\r\n        if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())\r\n            return true;\r\n\r\n        // Turn to target and move to it directly, without pathfinding.\r\n        const osg::Vec3f targetDir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3();\r\n\r\n        zTurn(actor, std::atan2(targetDir.x(), targetDir.y()), 0.f);\r\n        actor.getClass().getMovementSettings(actor).mPosition[1] = 1;\r\n        actor.getClass().getMovementSettings(actor).mPosition[0] = 0;\r\n\r\n        if (MWBase::Environment::get().getWorld()->getMaxActivationDistance() >= targetDir.length())\r\n        {\r\n            /*\r\n                Start of tes3mp addition\r\n\r\n                Send an ID_OBJECT_ACTIVATE packet every time an object is activated here\r\n            */\r\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\r\n            objectList->reset();\r\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\r\n            objectList->addObjectActivate(target, actor);\r\n            objectList->sendObjectActivate();\r\n            /*\r\n                End of tes3mp addition\r\n            */\r\n\r\n            /*\r\n                Start of tes3mp change (major)\r\n\r\n                Disable unilateral activation on this client and expect the server's reply to our\r\n                packet to do it instead\r\n\r\n                Cancel the package to avoid an infinite activation loop, deviating from the behavior\r\n                established in OpenMW in commit 48aba76ce904738d428e79f1ee24ce170f2a8309\r\n            */\r\n            //MWBase::Environment::get().getWorld()->activate(target, actor);\r\n            return true;\r\n            /*\r\n                End of tes3mp change (major)\r\n            */\r\n        }\r\n        return false;\r\n    }\r\n\r\n    void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const\r\n    {\r\n        std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());\r\n        activate->mTargetId = mObjectId;\r\n\r\n        ESM::AiSequence::AiPackageContainer package;\r\n        package.mType = ESM::AiSequence::Ai_Activate;\r\n        package.mPackage = activate.release();\r\n        sequence.mPackages.push_back(package);\r\n    }\r\n\r\n    AiActivate::AiActivate(const ESM::AiSequence::AiActivate *activate)\r\n        : mObjectId(activate->mTargetId)\r\n    {\r\n    }\r\n}\r\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiactivate.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIACTIVATE_H\r\n#define GAME_MWMECHANICS_AIACTIVATE_H\r\n\r\n#include \"typedaipackage.hpp\"\r\n\r\n/*\r\n    Start of tes3mp addition\r\n\r\n    Include additional headers for multiplayer purposes\r\n*/\r\n#include \"../mwworld/ptr.hpp\"\r\n/*\r\n    End of tes3mp addition\r\n*/\r\n\r\n#include <string>\r\n\r\n#include \"pathfinding.hpp\"\r\n\r\nnamespace ESM\r\n{\r\nnamespace AiSequence\r\n{\r\n    struct AiActivate;\r\n}\r\n}\r\n\r\nnamespace MWMechanics\r\n{\r\n    /// \\brief Causes actor to walk to activatable object and activate it\r\n    /** Will activate when close to object **/\r\n    class AiActivate final : public TypedAiPackage<AiActivate>\r\n    {\r\n        public:\r\n            /// Constructor\r\n            /** \\param objectId Reference to object to activate **/\r\n            explicit AiActivate(const std::string &objectId);\r\n\r\n            /*\r\n                Start of tes3mp addition\r\n\r\n                Make it possible to initialize an AiActivate package with a specific Ptr\r\n                as the target, allowing for more fine-tuned activation of objects\r\n            */\r\n            AiActivate(MWWorld::Ptr object);\r\n            /*\r\n                End of tes3mp addition\r\n            */\r\n\r\n            explicit AiActivate(const ESM::AiSequence::AiActivate* activate);\r\n\r\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\r\n\r\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Activate; }\r\n\r\n            void writeState(ESM::AiSequence::AiSequence& sequence) const override;\r\n\r\n        private:\r\n            const std::string mObjectId;\r\n\r\n            /*\r\n                Start of tes3mp addition\r\n\r\n                Track the object associated with this AiActivate package\r\n            */\r\n            MWWorld::Ptr mObjectPtr;\r\n            /*\r\n                End of tes3mp addition\r\n            */\r\n    };\r\n}\r\n#endif // GAME_MWMECHANICS_AIACTIVATE_H\r\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiavoiddoor.cpp",
    "content": "#include \"aiavoiddoor.hpp\"\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"movement.hpp\"\n#include \"actorutil.hpp\"\n#include \"steering.hpp\"\n\nstatic const int MAX_DIRECTIONS = 4;\n\nMWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr)\n: mDuration(1), mDoorPtr(doorPtr), mDirection(0)\n{\n\n}\n\nbool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)\n{\n\n    ESM::Position pos = actor.getRefData().getPosition();\n    if(mDuration == 1) //If it just started, get the actor position as the stuck detection thing\n        mLastPos = pos.asVec3();\n\n    mDuration -= duration; //Update timer\n\n    if (mDuration < 0)\n    {\n        if (isStuck(pos.asVec3()))\n        {\n            adjustDirection();\n            mDuration = 1; //reset timer\n        }\n        else\n            return true; // We have tried backing up for more than one second, we've probably cleared it\n    }\n\n    if (mDoorPtr.getClass().getDoorState(mDoorPtr) == MWWorld::DoorState::Idle)\n        return true; //Door is no longer opening\n\n    ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door\n    float x = pos.pos[1] - tPos.pos[1];\n    float y = pos.pos[0] - tPos.pos[0];\n\n    actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);\n\n    // Turn away from the door and move when turn completed\n    if (zTurn(actor, std::atan2(y,x) + getAdjustedAngle(), osg::DegreesToRadians(5.f)))\n        actor.getClass().getMovementSettings(actor).mPosition[1] = 1;\n    else\n        actor.getClass().getMovementSettings(actor).mPosition[1] = 0;\n    actor.getClass().getMovementSettings(actor).mPosition[0] = 0;\n\n    // Make all nearby actors also avoid the door\n    std::vector<MWWorld::Ptr> actors;\n    MWBase::Environment::get().getMechanicsManager()->getActorsInRange(pos.asVec3(),100,actors);\n    for(auto& neighbor : actors)\n    {\n        if (neighbor == getPlayer())\n            continue;\n\n        MWMechanics::AiSequence& seq = neighbor.getClass().getCreatureStats(neighbor).getAiSequence();\n        if (seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor)\n            seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr), neighbor);\n    }\n\n    return false;\n}\n\nbool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const\n{\n    return (actorPos - mLastPos).length2() < 10 * 10;\n}\n\nvoid MWMechanics::AiAvoidDoor::adjustDirection()\n{\n    mDirection = Misc::Rng::rollDice(MAX_DIRECTIONS);\n}\n\nfloat MWMechanics::AiAvoidDoor::getAdjustedAngle() const\n{\n    return 2 * osg::PI / MAX_DIRECTIONS * mDirection;\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiavoiddoor.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H\n#define GAME_MWMECHANICS_AIAVOIDDOOR_H\n\n#include \"typedaipackage.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"pathfinding.hpp\"\n\nnamespace MWMechanics\n{\n    /// \\brief AiPackage to have an actor avoid an opening door\n    /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it\n    **/\n    class AiAvoidDoor final : public TypedAiPackage<AiAvoidDoor>\n    {\n        public:\n            /// Avoid door until the door is fully open\n            explicit AiAvoidDoor(const MWWorld::ConstPtr& doorPtr);\n\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::AvoidDoor; }\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mPriority = 2;\n                options.mCanCancel = false;\n                options.mShouldCancelPreviousAi = false;\n                return options;\n            }\n\n        private:\n            float mDuration;\n            const MWWorld::ConstPtr mDoorPtr;\n            osg::Vec3f mLastPos;\n            int mDirection;\n\n            bool isStuck(const osg::Vec3f& actorPos) const;\n\n            void adjustDirection();\n\n            float getAdjustedAngle() const;\n    };\n}\n#endif\n\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aibreathe.cpp",
    "content": "#include \"aibreathe.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"npcstats.hpp\"\n\n#include \"movement.hpp\"\n#include \"steering.hpp\"\n\nbool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)\n{\n    static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fHoldBreathTime\")->mValue.getFloat();\n\n    const MWWorld::Class& actorClass = actor.getClass();\n    if (actorClass.isNpc())\n    {\n        if (actorClass.getNpcStats(actor).getTimeToStartDrowning() < fHoldBreathTime / 2)\n        {\n            actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);\n\n            actorClass.getMovementSettings(actor).mPosition[1] = 1;\n            smoothTurn(actor, static_cast<float>(-osg::PI_2), 0);\n\n            return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aibreathe.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIBREATHE_H\n#define GAME_MWMECHANICS_AIBREATHE_H\n\n#include \"typedaipackage.hpp\"\n\nnamespace MWMechanics\n{\n    /// \\brief AiPackage to have an actor resurface to breathe\n    // The AI will go up if lesser than half breath left\n    class AiBreathe final : public TypedAiPackage<AiBreathe>\n    {\n        public:\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Breathe; }\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mPriority = 2;\n                options.mCanCancel = false;\n                options.mShouldCancelPreviousAi = false;\n                return options;\n            }\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aicast.cpp",
    "content": "#include \"aicast.hpp\"\n\n#include <components/misc/constants.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"aicombataction.hpp\"\n#include \"creaturestats.hpp\"\n#include \"steering.hpp\"\n\nnamespace MWMechanics\n{\n    namespace\n    {\n        float getInitialDistance(const std::string& spellId)\n        {\n            ActionSpell action = ActionSpell(spellId);\n            bool isRanged;\n            return action.getCombatRange(isRanged);\n        }\n    }\n}\n\nMWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)\n    : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(getInitialDistance(spellId))\n{\n}\n\nbool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration)\n{\n    MWWorld::Ptr target;\n    if (actor.getCellRef().getRefId() == mTargetId)\n    {\n        // If the target has the same ID as caster, consider that actor casts spell with Self range.\n        target = actor;\n    }\n    else\n    {\n        target = getTarget();\n        if (!target)\n            return true;\n\n        if (!mManual && !pathTo(actor, target.getRefData().getPosition().asVec3(), duration, mDistance))\n        {\n            return false;\n        }\n    }\n\n    osg::Vec3f targetPos = target.getRefData().getPosition().asVec3();\n    // If the target of an on-target spell is an actor that is not the caster\n    // the target position must be adjusted so that it's not casted at the actor's feet.\n    if (target != actor && target.getClass().isActor())\n    {\n        osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(target);\n        targetPos.z() += halfExtents.z() * 2 * Constants::TorsoHeight;\n    }\n\n    osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();\n    osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);\n    actorPos.z() += halfExtents.z() * 2 * Constants::TorsoHeight;\n\n    osg::Vec3f dir = targetPos - actorPos;\n\n    bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f));\n    turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f));\n\n    if (!turned)\n        return false;\n\n    // Check if the actor is already casting another spell\n    bool isCasting = MWBase::Environment::get().getMechanicsManager()->isCastingSpell(actor);\n    if (isCasting && !mCasting)\n        return false;\n\n    if (!mCasting)\n    {\n        MWBase::Environment::get().getMechanicsManager()->castSpell(actor, mSpellId, mManual);\n        mCasting = true;\n        return false;\n    }\n\n    // Finish package, if actor finished spellcasting\n    return !isCasting;\n}\n\nMWWorld::Ptr MWMechanics::AiCast::getTarget() const\n{\n    MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mTargetId, false);\n\n    return target;\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aicast.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AICAST_H\n#define GAME_MWMECHANICS_AICAST_H\n\n#include \"typedaipackage.hpp\"\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    /// AiPackage which makes an actor to cast given spell.\n    class AiCast final : public TypedAiPackage<AiCast> {\n        public:\n            AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false);\n\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Cast; }\n\n            MWWorld::Ptr getTarget() const override;\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mPriority = 3;\n                options.mCanCancel = false;\n                options.mShouldCancelPreviousAi = false;\n                return options;\n            }\n\n        private:\n            const std::string mTargetId;\n            const std::string mSpellId;\n            bool mCasting;\n            const bool mManual;\n            const float mDistance;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aicombat.cpp",
    "content": "#include \"aicombat.hpp\"\n\n#include <components/misc/rng.hpp>\n#include <components/misc/coordinateconverter.hpp>\n\n#include <components/esm/aisequence.hpp>\n\n#include <components/misc/mathutil.hpp>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/detournavigator/navigator.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ActorList.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n#include \"../mwgui/windowmanagerimp.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwphysics/collisiontype.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"pathgrid.hpp\"\n#include \"creaturestats.hpp\"\n#include \"steering.hpp\"\n#include \"movement.hpp\"\n#include \"character.hpp\"\n#include \"aicombataction.hpp\"\n#include \"actorutil.hpp\"\n\nnamespace\n{\n\n    //chooses an attack depending on probability to avoid uniformity\n    std::string chooseBestAttack(const ESM::Weapon* weapon);\n\n    osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const osg::Vec3f& vLastTargetPos,\n        float duration, int weapType, float strength);\n}\n\nnamespace MWMechanics\n{\n    AiCombat::AiCombat(const MWWorld::Ptr& actor)\n    {\n        mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();\n    }\n\n    AiCombat::AiCombat(const ESM::AiSequence::AiCombat *combat)\n    {\n        mTargetActorId = combat->mTargetActorId;\n    }\n\n    void AiCombat::init()\n    {\n\n    }\n\n    /*\n     * Current AiCombat movement states (as of 0.29.0), ignoring the details of the\n     * attack states such as CombatMove, Strike and ReadyToAttack:\n     *\n     *    +----(within strike range)----->attack--(beyond strike range)-->follow\n     *    |                                 | ^                            | |\n     *    |                                 | |                            | |\n     *  pursue<---(beyond follow range)-----+ +----(within strike range)---+ |\n     *    ^                                                                  |\n     *    |                                                                  |\n     *    +-------------------------(beyond follow range)--------------------+\n     *\n     *\n     * Below diagram is high level only, the code detail is a little different\n     * (but including those detail will just complicate the diagram w/o adding much)\n     *\n     *    +----------(same)-------------->attack---------(same)---------->follow\n     *    |                                 |^^                            |||\n     *    |                                 |||                            |||\n     *    |       +--(same)-----------------+|+----------(same)------------+||\n     *    |       |                          |                              ||\n     *    |       |                          | (in range)                   ||\n     *    |   <---+         (too far)        |                              ||\n     *  pursue<-------------------------[door open]<-----+                  ||\n     *    ^^^                                            |                  ||\n     *    |||                                            |                  ||\n     *    ||+----------evade-----+                       |                  ||\n     *    ||                     |    [closed door]      |                  ||\n     *    |+----> maybe stuck, check --------------> back up, check door    ||\n     *    |         ^   |   ^                          |   ^                ||\n     *    |         |   |   |                          |   |                ||\n     *    |         |   +---+                          +---+                ||\n     *    |         +-------------------------------------------------------+|\n     *    |                                                                  |\n     *    +---------------------------(same)---------------------------------+\n     *\n     * FIXME:\n     *\n     * The new scheme is way too complicated, should really be implemented as a\n     * proper state machine.\n     *\n     * TODO:\n     *\n     * Use the observer pattern to coordinate attacks, provide intelligence on\n     * whether the target was hit, etc.\n     */\n\n    bool AiCombat::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)\n    {\n        // get or create temporary storage\n        AiCombatStorage& storage = state.get<AiCombatStorage>();\n        \n        //General description\n        if (actor.getClass().getCreatureStats(actor).isDead())\n            return true;\n\n        MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);\n        if (target.isEmpty())\n            return false;\n\n        if(!target.getRefData().getCount() || !target.getRefData().isEnabled()  // Really we should be checking whether the target is currently registered\n                                                                                // with the MechanicsManager\n                || target.getClass().getCreatureStats(target).isDead())\n            return true;\n\n        if (actor == target) // This should never happen.\n            return true;\n\n        if (!storage.isFleeing())\n        {\n            if (storage.mCurrentAction.get()) // need to wait to init action with its attack range\n            {\n                //Update every frame. UpdateLOS uses a timer, so the LOS check does not happen every frame.\n                updateLOS(actor, target, duration, storage);\n                const float targetReachedTolerance = storage.mLOS && !storage.mUseCustomDestination\n                        ? storage.mAttackRange : 0.0f;\n                const osg::Vec3f destination = storage.mUseCustomDestination\n                        ? storage.mCustomDestination : target.getRefData().getPosition().asVec3();\n                const bool is_target_reached = pathTo(actor, destination, duration, targetReachedTolerance);\n                if (is_target_reached) storage.mReadyToAttack = true;\n            }\n\n            storage.updateCombatMove(duration);\n            if (storage.mReadyToAttack) updateActorsMovement(actor, duration, storage);\n            storage.updateAttack(characterController);\n\n            /*\n                Start of tes3mp addition\n\n                Record that this actor is updating an attack so that a packet will be sent about it\n            */\n            mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);\n\n            if (localAttack && localAttack->pressed != storage.mAttack)\n            {\n                MechanicsHelper::resetAttack(localAttack);\n                localAttack->pressed = storage.mAttack;\n                localAttack->shouldSend = true;\n            }\n            /*\n                End of tes3mp addition\n            */\n        }\n        else\n        {\n            updateFleeing(actor, target, duration, storage);\n        }\n        storage.mActionCooldown -= duration;\n\n        if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting)\n            return false;\n\n        return attack(actor, target, storage, characterController);\n    }\n\n    bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)\n    {\n        const MWWorld::CellStore*& currentCell = storage.mCell;\n        bool cellChange = currentCell && (actor.getCell() != currentCell);\n        if(!currentCell || cellChange)\n        {\n            currentCell = actor.getCell();\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Because multiplayer doesn't pause the world during dialogue, disallow attacks on\n            a player engaged in dialogue\n        */\n        if (target == MWBase::Environment::get().getWorld()->getPlayerPtr())\n        {\n            if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue))\n            {\n                storage.stopAttack();\n                return false;\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        bool forceFlee = false;\n        if (!canFight(actor, target))\n        {\n            storage.stopAttack();\n            characterController.setAttackingOrSpell(false);\n\n            /*\n                Start of tes3mp addition\n\n                Record that this actor is stopping an attack so that a packet will be sent about it\n            */\n            mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);\n\n            if (localAttack && localAttack->pressed != false)\n            {\n                MechanicsHelper::resetAttack(localAttack);\n                localAttack->pressed = false;\n                localAttack->shouldSend = true;\n            }\n            /*\n                End of tes3mp addition\n            */\n\n            storage.mActionCooldown = 0.f;\n            // Continue combat if target is player or player follower/escorter and an attack has been attempted\n            const std::list<MWWorld::Ptr>& playerFollowersAndEscorters = MWBase::Environment::get().getMechanicsManager()->getActorsSidingWith(MWMechanics::getPlayer());\n            bool targetSidesWithPlayer = (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), target) != playerFollowersAndEscorters.end());\n            if ((target == MWMechanics::getPlayer() || targetSidesWithPlayer)\n                && ((actor.getClass().getCreatureStats(actor).getHitAttemptActorId() == target.getClass().getCreatureStats(target).getActorId())\n                || (target.getClass().getCreatureStats(target).getHitAttemptActorId() == actor.getClass().getCreatureStats(actor).getActorId())))\n                forceFlee = true;\n            else // Otherwise end combat\n                return true;\n        }\n\n        const MWWorld::Class& actorClass = actor.getClass();\n        actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);\n\n        float& actionCooldown = storage.mActionCooldown;\n        std::shared_ptr<Action>& currentAction = storage.mCurrentAction;\n\n        if (!forceFlee)\n        {\n            if (actionCooldown > 0)\n                return false;\n\n            if (characterController.readyToPrepareAttack())\n            {\n                currentAction = prepareNextAction(actor, target);\n                actionCooldown = currentAction->getActionCooldown();\n            }\n        }\n        else\n        {\n            currentAction.reset(new ActionFlee());\n            actionCooldown = currentAction->getActionCooldown();\n        }\n\n        if (!currentAction)\n            return false;\n\n        if (storage.isFleeing() != currentAction->isFleeing())\n        {\n            if (currentAction->isFleeing())\n            {\n                storage.startFleeing();\n                MWBase::Environment::get().getDialogueManager()->say(actor, \"flee\");\n                return false;\n            }\n            else\n                storage.stopFleeing();\n        }\n\n        bool isRangedCombat = false;\n        float &rangeAttack = storage.mAttackRange;\n\n        rangeAttack = currentAction->getCombatRange(isRangedCombat);\n\n        // Get weapon characteristics\n        const ESM::Weapon* weapon = currentAction->getWeapon();\n\n        ESM::Position pos = actor.getRefData().getPosition();\n        const osg::Vec3f vActorPos(pos.asVec3());\n        const osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3());\n\n        float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target);\n\n        storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS);\n\n        if (isRangedCombat)\n        {\n            // rotate actor taking into account target movement direction and projectile speed\n            osg::Vec3f vAimDir = AimDirToMovingTarget(actor, target, storage.mLastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength);\n\n            storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);\n            storage.mMovement.mRotation[2] = getZAngleToDir(vAimDir);\n        }\n        else\n        {\n            osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target, false);\n            storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);\n            storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated\n        }\n\n        storage.mLastTargetPos = vTargetPos;\n\n        if (storage.mReadyToAttack)\n        {\n            storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target);\n            // start new attack\n            storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat);\n        }\n\n        // If actor uses custom destination it has to try to rebuild path because environment can change\n        // (door is opened between actor and target) or target position has changed and current custom destination\n        // is not good enough to attack target.\n        if (storage.mCurrentAction->isAttackingOrSpell()\n            && ((!storage.mReadyToAttack && !mPathFinder.isPathConstructed())\n                || (storage.mUseCustomDestination && (storage.mCustomDestination - vTargetPos).length() > rangeAttack)))\n        {\n            const MWBase::World* world = MWBase::Environment::get().getWorld();\n            // Try to build path to the target.\n            const auto halfExtents = world->getPathfindingHalfExtents(actor);\n            const auto navigatorFlags = getNavigatorFlags(actor);\n            const auto areaCosts = getAreaCosts(actor);\n            const auto pathGridGraph = getPathGridGraph(actor.getCell());\n            mPathFinder.buildPath(actor, vActorPos, vTargetPos, actor.getCell(), pathGridGraph, halfExtents, navigatorFlags, areaCosts);\n\n            if (!mPathFinder.isPathConstructed())\n            {\n                // If there is no path, try to find a point on a line from the actor position to target projected\n                // on navmesh to attack the target from there.\n                const auto navigator = world->getNavigator();\n                const auto hit = navigator->raycast(halfExtents, vActorPos, vTargetPos, navigatorFlags);\n\n                if (hit.has_value() && (*hit - vTargetPos).length() <= rangeAttack)\n                {\n                    // If the point is close enough, try to find a path to that point.\n                    mPathFinder.buildPath(actor, vActorPos, *hit, actor.getCell(), pathGridGraph, halfExtents, navigatorFlags, areaCosts);\n                    if (mPathFinder.isPathConstructed())\n                    {\n                        // If path to that point is found use it as custom destination.\n                        storage.mCustomDestination = *hit;\n                        storage.mUseCustomDestination = true;\n                    }\n                }\n\n                if (!mPathFinder.isPathConstructed())\n                {\n                    storage.mUseCustomDestination = false;\n                    storage.stopAttack();\n                    characterController.setAttackingOrSpell(false);\n                    currentAction.reset(new ActionFlee());\n                    actionCooldown = currentAction->getActionCooldown();\n                    storage.startFleeing();\n                    MWBase::Environment::get().getDialogueManager()->say(actor, \"flee\");\n                }\n            }\n            else\n            {\n                storage.mUseCustomDestination = false;\n            }\n        }\n\n        return false;\n    }\n\n    void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)\n    {\n        static const float LOS_UPDATE_DURATION = 0.5f;\n        if (storage.mUpdateLOSTimer <= 0.f)\n        {\n            storage.mLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target);\n            storage.mUpdateLOSTimer = LOS_UPDATE_DURATION;\n        }\n        else\n            storage.mUpdateLOSTimer -= duration;\n    }\n\n    void MWMechanics::AiCombat::updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)\n    {\n        static const float BLIND_RUN_DURATION = 1.0f;\n\n        updateLOS(actor, target, duration, storage);\n\n        AiCombatStorage::FleeState& state = storage.mFleeState;\n        switch (state)\n        {\n            case AiCombatStorage::FleeState_None:\n                return;\n\n            case AiCombatStorage::FleeState_Idle:\n                {\n                    float triggerDist = getMaxAttackDistance(target);\n\n                    if (storage.mLOS &&\n                            (triggerDist >= 1000 || getDistanceMinusHalfExtents(actor, target) <= triggerDist))\n                    {\n                        const ESM::Pathgrid* pathgrid =\n                                MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*storage.mCell->getCell());\n\n                        bool runFallback = true;\n\n                        if (pathgrid != nullptr && !pathgrid->mPoints.empty() && !actor.getClass().isPureWaterCreature(actor))\n                        {\n                            ESM::Pathgrid::PointList points;\n                            Misc::CoordinateConverter coords(storage.mCell->getCell());\n\n                            osg::Vec3f localPos = actor.getRefData().getPosition().asVec3();\n                            coords.toLocal(localPos);\n\n                            int closestPointIndex = PathFinder::getClosestPoint(pathgrid, localPos);\n                            for (int i = 0; i < static_cast<int>(pathgrid->mPoints.size()); i++)\n                            {\n                                if (i != closestPointIndex && getPathGridGraph(storage.mCell).isPointConnected(closestPointIndex, i))\n                                {\n                                    points.push_back(pathgrid->mPoints[static_cast<size_t>(i)]);\n                                }\n                            }\n\n                            if (!points.empty())\n                            {\n                                ESM::Pathgrid::Point dest = points[Misc::Rng::rollDice(points.size())];\n                                coords.toWorld(dest);\n\n                                state = AiCombatStorage::FleeState_RunToDestination;\n                                storage.mFleeDest = ESM::Pathgrid::Point(dest.mX, dest.mY, dest.mZ);\n\n                                runFallback = false;\n                            }\n                        }\n\n                        if (runFallback)\n                        {\n                            state = AiCombatStorage::FleeState_RunBlindly;\n                            storage.mFleeBlindRunTimer = 0.0f;\n                        }\n                    }\n                }\n                break;\n\n            case AiCombatStorage::FleeState_RunBlindly:\n                {\n                    // timer to prevent twitchy movement that can be observed in vanilla MW\n                    if (storage.mFleeBlindRunTimer < BLIND_RUN_DURATION)\n                    {\n                        storage.mFleeBlindRunTimer += duration;\n\n                        storage.mMovement.mRotation[0] = -actor.getRefData().getPosition().rot[0];\n                        storage.mMovement.mRotation[2] = osg::PI + getZAngleToDir(target.getRefData().getPosition().asVec3()-actor.getRefData().getPosition().asVec3());\n                        storage.mMovement.mPosition[1] = 1;\n                        updateActorsMovement(actor, duration, storage);\n                    }\n                    else\n                        state = AiCombatStorage::FleeState_Idle;\n                }\n                break;\n\n            case AiCombatStorage::FleeState_RunToDestination:\n                {\n                    static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fFleeDistance\")->mValue.getFloat();\n\n                    float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length();\n                    if ((dist > fFleeDistance && !storage.mLOS)\n                            || pathTo(actor, PathFinder::makeOsgVec3(storage.mFleeDest), duration))\n                    {\n                        state = AiCombatStorage::FleeState_Idle;\n                    }\n                }\n                break;\n        };\n    }\n\n    void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage)\n    {\n        // apply combat movement\n        float deltaAngle = storage.mMovement.mRotation[2] - actor.getRefData().getPosition().rot[2];\n        osg::Vec2f movement = Misc::rotateVec2f(\n            osg::Vec2f(storage.mMovement.mPosition[0], storage.mMovement.mPosition[1]), -deltaAngle);\n\n        MWMechanics::Movement& actorMovementSettings = actor.getClass().getMovementSettings(actor);\n        actorMovementSettings.mPosition[0] = movement.x();\n        actorMovementSettings.mPosition[1] = movement.y();\n        actorMovementSettings.mPosition[2] = storage.mMovement.mPosition[2];\n\n        rotateActorOnAxis(actor, 2, actorMovementSettings, storage);\n        rotateActorOnAxis(actor, 0, actorMovementSettings, storage);\n    }\n\n    void AiCombat::rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, \n        MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage)\n    {\n        actorMovementSettings.mRotation[axis] = 0;\n        bool isRangedCombat = false;\n        storage.mCurrentAction->getCombatRange(isRangedCombat);\n        float eps = isRangedCombat ? osg::DegreesToRadians(0.5) : osg::DegreesToRadians(3.f);\n        float targetAngleRadians = storage.mMovement.mRotation[axis];\n        smoothTurn(actor, targetAngleRadians, axis, eps);\n    }\n\n    MWWorld::Ptr AiCombat::getTarget() const\n    {\n        return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);\n    }\n\n    void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const\n    {\n        std::unique_ptr<ESM::AiSequence::AiCombat> combat(new ESM::AiSequence::AiCombat());\n        combat->mTargetActorId = mTargetActorId;\n\n        ESM::AiSequence::AiPackageContainer package;\n        package.mType = ESM::AiSequence::Ai_Combat;\n        package.mPackage = combat.release();\n        sequence.mPackages.push_back(package);\n    }\n\n    void AiCombatStorage::startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target)\n    {\n        // get the range of the target's weapon\n        MWWorld::Ptr targetWeapon = MWWorld::Ptr();\n        const MWWorld::Class& targetClass = target.getClass();\n\n        if (targetClass.hasInventoryStore(target))\n        {\n            int weapType = ESM::Weapon::None;\n            MWWorld::ContainerStoreIterator weaponSlot = MWMechanics::getActiveWeapon(target, &weapType);\n            if (weapType > ESM::Weapon::None)\n                targetWeapon = *weaponSlot;\n        }\n\n        bool targetUsesRanged = false;\n        float rangeAttackOfTarget = ActionWeapon(targetWeapon).getCombatRange(targetUsesRanged);\n        \n        if (mMovement.mPosition[0] || mMovement.mPosition[1])\n        {\n            mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();\n            mCombatMove = true;\n        }\n        else if (isDistantCombat)\n        {\n            // Backing up behaviour\n            // Actor backs up slightly further away than opponent's weapon range\n            // (in vanilla - only as far as oponent's weapon range),\n            // or not at all if opponent is using a ranged weapon\n\n            if (targetUsesRanged || distToTarget > rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon\n                return;\n\n            // actor should not back up into water\n            if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f))\n                return;\n\n            int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door;\n\n            // Actor can not back up if there is no free space behind\n            // Currently we take the 35% of actor's height from the ground as vector height.\n            // This approach allows us to detect small obstacles (e.g. crates) and curved walls.\n            osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);\n            osg::Vec3f pos = actor.getRefData().getPosition().asVec3();\n            osg::Vec3f source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z());\n            osg::Vec3f fallbackDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,-1,0);\n            osg::Vec3f destination = source + fallbackDirection * (halfExtents.y() + 16);\n\n            bool isObstacleDetected = MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask);\n            if (isObstacleDetected)\n                return;\n\n            // Check if there is nothing behind - probably actor is near cliff.\n            // A current approach: cast ray 1.5-yard ray down in 1.5 yard behind actor from 35% of actor's height.\n            // If we did not hit anything, there is a cliff behind actor.\n            source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z()) + fallbackDirection * (halfExtents.y() + 96);\n            destination = source - osg::Vec3f(0, 0, 0.75f * halfExtents.z() + 96);\n            bool isCliffDetected = !MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask);\n            if (isCliffDetected)\n                return;\n\n            mMovement.mPosition[1] = -1;\n        }\n        // dodge movements (for NPCs and bipedal creatures)\n        // Note: do not use for ranged combat yet since in couple with back up behaviour can move actor out of cliff\n        else if (actor.getClass().isBipedal(actor))\n        {\n            float moveDuration = 0;\n            float angleToTarget = Misc::normalizeAngle(mMovement.mRotation[2] - actor.getRefData().getPosition().rot[2]);\n            // Apply a big side step if enemy tries to get around and come from behind.\n            // Otherwise apply a random side step (kind of dodging) with some probability\n            // if actor is within range of target's weapon.\n            if (std::abs(angleToTarget) > osg::PI / 4)\n                moveDuration = 0.2f;\n            else if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25)\n                moveDuration = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();\n            if (moveDuration > 0)\n            {\n                mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right\n                mTimerCombatMove = moveDuration;\n                mCombatMove = true;\n            }\n        }\n    }\n\n    void AiCombatStorage::updateCombatMove(float duration)\n    {\n        if (mCombatMove)\n        {\n            mTimerCombatMove -= duration;\n            if (mTimerCombatMove <= 0)\n            {\n                stopCombatMove();\n            }\n        }\n    }\n\n    void AiCombatStorage::stopCombatMove()\n    {\n        mTimerCombatMove = 0;\n        mMovement.mPosition[1] = mMovement.mPosition[0] = 0;\n        mCombatMove = false;\n    }\n\n    void AiCombatStorage::startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, \n        const ESM::Weapon* weapon, bool distantCombat)\n    {\n        if (mReadyToAttack && characterController.readyToStartAttack())\n        {\n            if (mAttackCooldown <= 0)\n            {\n                mAttack = true; // attack starts just now\n                characterController.setAttackingOrSpell(true);\n\n                if (!distantCombat)\n                    characterController.setAIAttackType(chooseBestAttack(weapon));\n\n                /*\n                    Start of tes3mp addition\n\n                    Record that this actor is starting an attack so that a packet will be sent about it\n                */\n                mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);\n\n                if (localAttack && localAttack->pressed != true)\n                {\n                    MechanicsHelper::resetAttack(localAttack);\n                    localAttack->type = distantCombat ? mwmp::Attack::RANGED : mwmp::Attack::MELEE;\n                    localAttack->attackAnimation = characterController.getAttackType();\n                    localAttack->pressed = true;\n\n                    mwmp::ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList();\n                    actorList->reset();\n                    actorList->cell = *actor.getCell()->getCell();\n                    actorList->addAttackActor(actor, *localAttack);\n                    actorList->sendAttackActors();\n                }\n                /*\n                    End of tes3mp addition\n                */\n\n                mStrength = Misc::Rng::rollClosedProbability();\n\n                const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n\n                float baseDelay = store.get<ESM::GameSetting>().find(\"fCombatDelayCreature\")->mValue.getFloat();\n                if (actor.getClass().isNpc())\n                {\n                    baseDelay = store.get<ESM::GameSetting>().find(\"fCombatDelayNPC\")->mValue.getFloat();\n                }\n\n                // Say a provoking combat phrase\n                const int iVoiceAttackOdds = store.get<ESM::GameSetting>().find(\"iVoiceAttackOdds\")->mValue.getInteger();\n                if (Misc::Rng::roll0to99() < iVoiceAttackOdds)\n                {\n                    MWBase::Environment::get().getDialogueManager()->say(actor, \"attack\");\n                }\n                mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(), baseDelay + 0.9);\n            }\n            else\n                mAttackCooldown -= AI_REACTION_TIME;\n        }\n    }\n\n    void AiCombatStorage::updateAttack(CharacterController& characterController)\n    {\n        if (mAttack && (characterController.getAttackStrength() >= mStrength || characterController.readyToPrepareAttack()))\n        {\n            mAttack = false;\n        }\n        characterController.setAttackingOrSpell(mAttack);\n    }\n\n    void AiCombatStorage::stopAttack()\n    {\n        mMovement.mPosition[0] = 0;\n        mMovement.mPosition[1] = 0;\n        mMovement.mPosition[2] = 0;\n        mReadyToAttack = false;\n        mAttack = false;\n    }\n\n    void AiCombatStorage::startFleeing()\n    {\n        stopFleeing();\n        mFleeState = FleeState_Idle;\n    }\n\n    void AiCombatStorage::stopFleeing()\n    {\n        mMovement.mPosition[0] = 0;\n        mMovement.mPosition[1] = 0;\n        mMovement.mPosition[2] = 0;\n        mFleeState = FleeState_None;\n        mFleeDest = ESM::Pathgrid::Point(0, 0, 0);\n    }\n\n    bool AiCombatStorage::isFleeing()\n    {\n        return mFleeState != FleeState_None;\n    }\n}\n\n\nnamespace\n{\n\nstd::string chooseBestAttack(const ESM::Weapon* weapon)\n{\n    std::string attackType;\n\n    if (weapon != nullptr)\n    {\n        //the more damage attackType deals the more probability it has\n        int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2;\n        int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2;\n        int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2;\n\n        float roll = Misc::Rng::rollClosedProbability() * (slash + chop + thrust);\n        if(roll <= slash)\n            attackType = \"slash\";\n        else if(roll <= (slash + thrust))\n            attackType = \"thrust\";\n        else\n            attackType = \"chop\";\n    }\n    else\n        MWMechanics::CharacterController::setAttackTypeRandomly(attackType);\n\n    return attackType;\n}\n\nosg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const osg::Vec3f& vLastTargetPos,\n    float duration, int weapType, float strength)\n{\n    float projSpeed;\n    const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n    // get projectile speed (depending on weapon type)\n    if (MWMechanics::getWeaponType(weapType)->mWeaponClass == ESM::WeaponType::Thrown)\n    {\n        static float fThrownWeaponMinSpeed = gmst.find(\"fThrownWeaponMinSpeed\")->mValue.getFloat();\n        static float fThrownWeaponMaxSpeed = gmst.find(\"fThrownWeaponMaxSpeed\")->mValue.getFloat();\n\n        projSpeed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength;\n    }\n    else if (weapType != 0)\n    {\n        static float fProjectileMinSpeed = gmst.find(\"fProjectileMinSpeed\")->mValue.getFloat();\n        static float fProjectileMaxSpeed = gmst.find(\"fProjectileMaxSpeed\")->mValue.getFloat();\n\n        projSpeed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength;\n    }\n    else // weapType is 0 ==> it's a target spell projectile\n    {\n        projSpeed = gmst.find(\"fTargetSpellMaxSpeed\")->mValue.getFloat();\n    }\n\n    // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same\n\n    osg::Vec3f vTargetPos = target.getRefData().getPosition().asVec3();\n    osg::Vec3f vDirToTarget = MWBase::Environment::get().getWorld()->aimToTarget(actor, target, true);\n    float distToTarget = vDirToTarget.length();\n\n    osg::Vec3f vTargetMoveDir = vTargetPos - vLastTargetPos;\n    vTargetMoveDir /= duration; // |vTargetMoveDir| is target real speed in units/sec now\n\n    osg::Vec3f vPerpToDir = vDirToTarget ^ osg::Vec3f(0,0,1); // cross product\n\n    vPerpToDir.normalize();\n    osg::Vec3f vDirToTargetNormalized = vDirToTarget;\n    vDirToTargetNormalized.normalize();\n\n    // dot product\n    float velPerp = vTargetMoveDir * vPerpToDir;\n    float velDir = vTargetMoveDir * vDirToTargetNormalized;\n\n    // time to collision between target and projectile\n    float t_collision;\n\n    float projVelDirSquared = projSpeed * projSpeed - velPerp * velPerp;\n    if (projVelDirSquared > 0)\n    {\n        osg::Vec3f vTargetMoveDirNormalized = vTargetMoveDir;\n        vTargetMoveDirNormalized.normalize();\n\n        float projDistDiff = vDirToTarget * vTargetMoveDirNormalized; // dot product\n        projDistDiff = std::sqrt(distToTarget * distToTarget - projDistDiff * projDistDiff);\n\n        t_collision = projDistDiff / (std::sqrt(projVelDirSquared) - velDir);\n    }\n    else\n        t_collision = 0; // speed of projectile is not enough to reach moving target\n\n    return vDirToTarget + vTargetMoveDir * t_collision;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aicombat.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AICOMBAT_H\n#define GAME_MWMECHANICS_AICOMBAT_H\n\n#include \"typedaipackage.hpp\"\n\n#include \"../mwworld/cellstore.hpp\" // for Doors\n\n#include \"../mwbase/world.hpp\"\n\n#include \"pathfinding.hpp\"\n#include \"movement.hpp\"\n#include \"aitimer.hpp\"\n\nnamespace ESM\n{\n    namespace AiSequence\n    {\n        struct AiCombat;\n    }\n}\n\nnamespace MWMechanics\n{\n    class Action;\n\n    /// \\brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive.\n    struct AiCombatStorage : AiTemporaryBase\n    {\n        float mAttackCooldown;\n        AiReactionTimer mReaction;\n        float mTimerCombatMove;\n        bool mReadyToAttack;\n        bool mAttack;\n        float mAttackRange;\n        bool mCombatMove;\n        osg::Vec3f mLastTargetPos;\n        const MWWorld::CellStore* mCell;\n        std::shared_ptr<Action> mCurrentAction;\n        float mActionCooldown;\n        float mStrength;\n        bool mForceNoShortcut;\n        ESM::Position mShortcutFailPos;\n        MWMechanics::Movement mMovement;\n\n        enum FleeState\n        {\n            FleeState_None,\n            FleeState_Idle,\n            FleeState_RunBlindly,\n            FleeState_RunToDestination\n        };\n        FleeState mFleeState;\n        bool mLOS;\n        float mUpdateLOSTimer;\n        float mFleeBlindRunTimer;\n        ESM::Pathgrid::Point mFleeDest;\n\n        bool mUseCustomDestination;\n        osg::Vec3f mCustomDestination;\n\n        AiCombatStorage():\n        mAttackCooldown(0.0f),\n        mTimerCombatMove(0.0f),\n        mReadyToAttack(false),\n        mAttack(false),\n        mAttackRange(0.0f),\n        mCombatMove(false),\n        mLastTargetPos(0,0,0),\n        mCell(nullptr),\n        mCurrentAction(),\n        mActionCooldown(0.0f),\n        mStrength(),\n        mForceNoShortcut(false),\n        mShortcutFailPos(),\n        mMovement(),\n        mFleeState(FleeState_None),\n        mLOS(false),\n        mUpdateLOSTimer(0.0f),\n        mFleeBlindRunTimer(0.0f),\n        mUseCustomDestination(false),\n        mCustomDestination()\n        {}\n\n        void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);\n        void updateCombatMove(float duration);\n        void stopCombatMove();\n        void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,\n            const ESM::Weapon* weapon, bool distantCombat);\n        void updateAttack(CharacterController& characterController);\n        void stopAttack();\n\n        void startFleeing();\n        void stopFleeing();\n        bool isFleeing();\n    };\n\n    /// \\brief Causes the actor to fight another actor\n    class AiCombat final : public TypedAiPackage<AiCombat>\n    {\n        public:\n            ///Constructor\n            /** \\param actor Actor to fight **/\n            explicit AiCombat(const MWWorld::Ptr& actor);\n\n            explicit AiCombat (const ESM::AiSequence::AiCombat* combat);\n\n            void init();\n\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Combat; }\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mPriority = 1;\n                options.mCanCancel = false;\n                options.mShouldCancelPreviousAi = false;\n                return options;\n            }\n\n            ///Returns target ID\n            MWWorld::Ptr getTarget() const override;\n\n            void writeState(ESM::AiSequence::AiSequence &sequence) const override;\n\n        private:\n            /// Returns true if combat should end\n            bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);\n\n            void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);\n\n            void updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);\n\n            /// Transfer desired movement (from AiCombatStorage) to Actor\n            void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage);\n            void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, \n                MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage);\n    };\n    \n    \n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aicombataction.cpp",
    "content": "#include \"aicombataction.hpp\"\n\n#include <components/esm/loadench.hpp>\n#include <components/esm/loadmgef.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/actionequip.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"npcstats.hpp\"\n#include \"combat.hpp\"\n#include \"weaponpriority.hpp\"\n#include \"spellpriority.hpp\"\n#include \"weapontype.hpp\"\n\nnamespace MWMechanics\n{\n    float suggestCombatRange(int rangeTypes)\n    {\n        static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fCombatDistance\")->mValue.getFloat();\n        static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fHandToHandReach\")->mValue.getFloat();\n\n        // This distance is a possible distance of melee attack\n        static float distance = fCombatDistance * std::max(2.f, fHandToHandReach);\n\n        if (rangeTypes & RangeTypes::Touch)\n        {\n            return fCombatDistance;\n        }\n\n        return distance * 4;\n    }\n\n    void ActionSpell::prepare(const MWWorld::Ptr &actor)\n    {\n        actor.getClass().getCreatureStats(actor).getSpells().setSelectedSpell(mSpellId);\n        actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Spell);\n        if (actor.getClass().hasInventoryStore(actor))\n        {\n            MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);\n            inv.setSelectedEnchantItem(inv.end());\n        }\n\n        const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(mSpellId);\n        MWBase::Environment::get().getWorld()->preloadEffects(&spell->mEffects);\n    }\n\n    float ActionSpell::getCombatRange (bool& isRanged) const\n    {\n        const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(mSpellId);\n        int types = getRangeTypes(spell->mEffects);\n\n        isRanged = (types & RangeTypes::Target) | (types & RangeTypes::Self);\n        return suggestCombatRange(types);\n    }\n\n    void ActionEnchantedItem::prepare(const MWWorld::Ptr &actor)\n    {\n        actor.getClass().getCreatureStats(actor).getSpells().setSelectedSpell(std::string());\n        actor.getClass().getInventoryStore(actor).setSelectedEnchantItem(mItem);\n        actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Spell);\n    }\n\n    float ActionEnchantedItem::getCombatRange(bool& isRanged) const\n    {\n        const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(mItem->getClass().getEnchantment(*mItem));\n        int types = getRangeTypes(enchantment->mEffects);\n\n        isRanged = (types & RangeTypes::Target) | (types & RangeTypes::Self);\n        return suggestCombatRange(types);\n    }\n\n    float ActionPotion::getCombatRange(bool& isRanged) const\n    {\n        // Distance doesn't matter since this action has no animation\n        // If we want to back away slightly to avoid enemy hits, we should set isRanged to \"true\"\n        return 600.f;\n    }\n\n    void ActionPotion::prepare(const MWWorld::Ptr &actor)\n    {\n        actor.getClass().apply(actor, mPotion.getCellRef().getRefId(), actor);\n        actor.getClass().getContainerStore(actor).remove(mPotion, 1, actor);\n    }\n\n    void ActionWeapon::prepare(const MWWorld::Ptr &actor)\n    {\n        if (actor.getClass().hasInventoryStore(actor))\n        {\n            if (mWeapon.isEmpty())\n                actor.getClass().getInventoryStore(actor).unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor);\n            else\n            {\n                MWWorld::ActionEquip equip(mWeapon);\n                equip.execute(actor);\n            }\n\n            if (!mAmmunition.isEmpty())\n            {\n                MWWorld::ActionEquip equip(mAmmunition);\n                equip.execute(actor);\n            }\n        }\n        actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Weapon);\n    }\n\n    float ActionWeapon::getCombatRange(bool& isRanged) const\n    {\n        isRanged = false;\n\n        static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fCombatDistance\")->mValue.getFloat();\n        static const float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fProjectileMaxSpeed\")->mValue.getFloat();\n\n        if (mWeapon.isEmpty())\n        {\n            static float fHandToHandReach =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fHandToHandReach\")->mValue.getFloat();\n            return fHandToHandReach * fCombatDistance;\n        }\n\n        const ESM::Weapon* weapon = mWeapon.get<ESM::Weapon>()->mBase;\n        if (MWMechanics::getWeaponType(weapon->mData.mType)->mWeaponClass != ESM::WeaponType::Melee)\n        {\n            isRanged = true;\n            return fProjectileMaxSpeed;\n        }\n        else\n            return weapon->mData.mReach * fCombatDistance;\n    }\n\n    const ESM::Weapon* ActionWeapon::getWeapon() const\n    {\n        if (mWeapon.isEmpty())\n            return nullptr;\n        return mWeapon.get<ESM::Weapon>()->mBase;\n    }\n\n    std::shared_ptr<Action> prepareNextAction(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy)\n    {\n        Spells& spells = actor.getClass().getCreatureStats(actor).getSpells();\n\n        float bestActionRating = 0.f;\n        float antiFleeRating = 0.f;\n        // Default to hand-to-hand combat\n        std::shared_ptr<Action> bestAction (new ActionWeapon(MWWorld::Ptr()));\n        if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            bestAction->prepare(actor);\n            return bestAction;\n        }\n\n        if (actor.getClass().hasInventoryStore(actor))\n        {\n            MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);\n\n            for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n            {\n                float rating = ratePotion(*it, actor);\n                if (rating > bestActionRating)\n                {\n                    bestActionRating = rating;\n                    bestAction.reset(new ActionPotion(*it));\n                    antiFleeRating = std::numeric_limits<float>::max();\n                }\n            }\n\n            for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n            {\n                float rating = rateMagicItem(*it, actor, enemy);\n                if (rating > bestActionRating)\n                {\n                    bestActionRating = rating;\n                    bestAction.reset(new ActionEnchantedItem(it));\n                    antiFleeRating = std::numeric_limits<float>::max();\n                }\n            }\n\n            MWWorld::Ptr bestArrow;\n            float bestArrowRating = rateAmmo(actor, enemy, bestArrow, ESM::Weapon::Arrow);\n\n            MWWorld::Ptr bestBolt;\n            float bestBoltRating = rateAmmo(actor, enemy, bestBolt, ESM::Weapon::Bolt);\n\n            for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n            {\n                float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating);\n                if (rating > bestActionRating)\n                {\n                    const ESM::Weapon* weapon = it->get<ESM::Weapon>()->mBase;\n                    int ammotype = getWeaponType(weapon->mData.mType)->mAmmoType;\n\n                    MWWorld::Ptr ammo;\n                    if (ammotype == ESM::Weapon::Arrow)\n                        ammo = bestArrow;\n                    else if (ammotype == ESM::Weapon::Bolt)\n                        ammo = bestBolt;\n\n                    bestActionRating = rating;\n                    bestAction.reset(new ActionWeapon(*it, ammo));\n                    antiFleeRating = vanillaRateWeaponAndAmmo(*it, ammo, actor, enemy);\n                }\n            }\n        }\n\n        for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)\n        {\n            float rating = rateSpell(it->first, actor, enemy);\n            if (rating > bestActionRating)\n            {\n                bestActionRating = rating;\n                bestAction.reset(new ActionSpell(it->first->mId));\n                antiFleeRating = vanillaRateSpell(it->first, actor, enemy);\n            }\n        }\n\n        if (makeFleeDecision(actor, enemy, antiFleeRating))\n            bestAction.reset(new ActionFlee());\n\n        if (bestAction.get())\n            bestAction->prepare(actor);\n\n        return bestAction;\n    }\n\n    float getBestActionRating(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy)\n    {\n        Spells& spells = actor.getClass().getCreatureStats(actor).getSpells();\n\n        float bestActionRating = 0.f;\n        // Default to hand-to-hand combat\n        if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            return bestActionRating;\n        }\n\n        if (actor.getClass().hasInventoryStore(actor))\n        {\n            MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);\n\n            for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n            {\n                float rating = rateMagicItem(*it, actor, enemy);\n                if (rating > bestActionRating)\n                {\n                    bestActionRating = rating;\n                }\n            }\n\n            float bestArrowRating = rateAmmo(actor, enemy, ESM::Weapon::Arrow);\n\n            float bestBoltRating = rateAmmo(actor, enemy, ESM::Weapon::Bolt);\n\n            for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n            {\n                float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating);\n                if (rating > bestActionRating)\n                {\n                    bestActionRating = rating;\n                }\n            }\n        }\n\n        for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)\n        {\n            float rating = rateSpell(it->first, actor, enemy);\n            if (rating > bestActionRating)\n            {\n                bestActionRating = rating;\n            }\n        }\n\n        return bestActionRating;\n    }\n\n\n    float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool minusZDist)\n    {\n        osg::Vec3f actor1Pos = actor1.getRefData().getPosition().asVec3();\n        osg::Vec3f actor2Pos = actor2.getRefData().getPosition().asVec3();\n\n        float dist = (actor1Pos - actor2Pos).length();\n\n        if (minusZDist)\n            dist -= std::abs(actor1Pos.z() - actor2Pos.z());\n\n        return (dist\n                - MWBase::Environment::get().getWorld()->getHalfExtents(actor1).y()\n                - MWBase::Environment::get().getWorld()->getHalfExtents(actor2).y());\n    }\n\n    float getMaxAttackDistance(const MWWorld::Ptr& actor)\n    {\n        const CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        std::string selectedSpellId = stats.getSpells().getSelectedSpell();\n        MWWorld::Ptr selectedEnchItem;\n\n        MWWorld::Ptr activeWeapon, activeAmmo;\n        if (actor.getClass().hasInventoryStore(actor))\n        {\n            MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor);\n\n            MWWorld::ContainerStoreIterator item = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n            if (item != invStore.end() && item.getType() == MWWorld::ContainerStore::Type_Weapon)\n                activeWeapon = *item;\n\n            item = invStore.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n            if (item != invStore.end() && item.getType() == MWWorld::ContainerStore::Type_Weapon)\n                activeAmmo = *item;\n\n            if (invStore.getSelectedEnchantItem() != invStore.end())\n                selectedEnchItem = *invStore.getSelectedEnchantItem();\n        }\n\n        float dist = 1.0f;\n        if (activeWeapon.isEmpty() && !selectedSpellId.empty() && !selectedEnchItem.isEmpty())\n        {\n            static const float fHandToHandReach = gmst.find(\"fHandToHandReach\")->mValue.getFloat();\n            dist = fHandToHandReach;\n        }\n        else if (stats.getDrawState() == MWMechanics::DrawState_Spell)\n        {\n            dist = 1.0f;\n            if (!selectedSpellId.empty())\n            {\n                const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(selectedSpellId);\n                for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =\n                     spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)\n                {\n                    if (effectIt->mRange == ESM::RT_Target)\n                    {\n                        const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);\n                        dist = effect->mData.mSpeed;\n                        break;\n                    }\n                }\n            }\n            else if (!selectedEnchItem.isEmpty())\n            {\n                std::string enchId = selectedEnchItem.getClass().getEnchantment(selectedEnchItem);\n                if (!enchId.empty())\n                {\n                    const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchId);\n                    for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =\n                         ench->mEffects.mList.begin(); effectIt != ench->mEffects.mList.end(); ++effectIt)\n                    {\n                        if (effectIt->mRange == ESM::RT_Target)\n                        {\n                            const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);\n                            dist = effect->mData.mSpeed;\n                            break;\n                        }\n                    }\n                }\n            }\n\n            static const float fTargetSpellMaxSpeed = gmst.find(\"fTargetSpellMaxSpeed\")->mValue.getFloat();\n            dist *= std::max(1000.0f, fTargetSpellMaxSpeed);\n        }\n        else if (!activeWeapon.isEmpty())\n        {\n            const ESM::Weapon* esmWeap = activeWeapon.get<ESM::Weapon>()->mBase;\n            if (MWMechanics::getWeaponType(esmWeap->mData.mType)->mWeaponClass != ESM::WeaponType::Melee)\n            {\n                static const float fTargetSpellMaxSpeed = gmst.find(\"fProjectileMaxSpeed\")->mValue.getFloat();\n                dist = fTargetSpellMaxSpeed;\n                if (!activeAmmo.isEmpty())\n                {\n                    const ESM::Weapon* esmAmmo = activeAmmo.get<ESM::Weapon>()->mBase;\n                    dist *= esmAmmo->mData.mSpeed;\n                }\n            }\n            else if (esmWeap->mData.mReach > 1)\n            {\n                dist = esmWeap->mData.mReach;\n            }\n        }\n\n        dist = (dist > 0.f) ? dist : 1.0f;\n\n        static const float fCombatDistance = gmst.find(\"fCombatDistance\")->mValue.getFloat();\n        static const float fCombatDistanceWerewolfMod = gmst.find(\"fCombatDistanceWerewolfMod\")->mValue.getFloat();\n\n        float combatDistance = fCombatDistance;\n        if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n            combatDistance *= (fCombatDistanceWerewolfMod + 1.0f);\n\n        if (dist < combatDistance)\n            dist *= combatDistance;\n\n        return dist;\n    }\n\n    bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)\n    {\n        ESM::Position actorPos = actor.getRefData().getPosition();\n        ESM::Position enemyPos = enemy.getRefData().getPosition();\n\n        if (isTargetMagicallyHidden(enemy) && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(enemy, actor))\n        {\n            return false;\n        }\n\n        if (actor.getClass().isPureWaterCreature(actor))\n        {\n            if (!MWBase::Environment::get().getWorld()->isWading(enemy))\n                return false;\n        }\n\n        float atDist = getMaxAttackDistance(actor);\n        if (atDist > getDistanceMinusHalfExtents(actor, enemy)\n                && atDist > std::abs(actorPos.pos[2] - enemyPos.pos[2]))\n        {\n            if (MWBase::Environment::get().getWorld()->getLOS(actor, enemy))\n                return true;\n        }\n\n        if (actor.getClass().isPureLandCreature(actor) && MWBase::Environment::get().getWorld()->isWalkingOnWater(enemy))\n        {\n            return false;\n        }\n\n        if (actor.getClass().isPureFlyingCreature(actor) || actor.getClass().isPureLandCreature(actor))\n        {\n            if (MWBase::Environment::get().getWorld()->isSwimming(enemy))\n                return false;\n        }\n\n        if (actor.getClass().isBipedal(actor) || !actor.getClass().canFly(actor))\n        {\n            if (enemy.getClass().getCreatureStats(enemy).getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0)\n            {\n                float attackDistance = getMaxAttackDistance(actor);\n                if ((attackDistance + actorPos.pos[2]) < enemyPos.pos[2])\n                {\n                    if (enemy.getCell()->isExterior())\n                    {\n                        if (attackDistance < (enemyPos.pos[2] - MWBase::Environment::get().getWorld()->getTerrainHeightAt(enemyPos.asVec3())))\n                            return false;\n                    }\n                }\n            }\n        }\n\n        if (!actor.getClass().canWalk(actor) && !actor.getClass().isBipedal(actor))\n            return true;\n\n        if (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0)\n            return true;\n\n        if (MWBase::Environment::get().getWorld()->isSwimming(actor))\n            return true;\n\n        if (getDistanceMinusHalfExtents(actor, enemy, true) <= 0.0f)\n            return false;\n\n        return true;\n    }\n\n    float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)\n    {\n        const CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        int flee = stats.getAiSetting(CreatureStats::AI_Flee).getModified();\n        if (flee >= 100)\n            return flee;\n\n        static const float fAIFleeHealthMult = gmst.find(\"fAIFleeHealthMult\")->mValue.getFloat();\n        static const float fAIFleeFleeMult = gmst.find(\"fAIFleeFleeMult\")->mValue.getFloat();\n\n        float healthPercentage = (stats.getHealth().getModified() == 0.0f)\n                                    ? 1.0f : stats.getHealth().getCurrent() / stats.getHealth().getModified();\n        float rating = (1.0f - healthPercentage) * fAIFleeHealthMult + flee * fAIFleeFleeMult;\n\n        static const int iWereWolfLevelToAttack = gmst.find(\"iWereWolfLevelToAttack\")->mValue.getInteger();\n\n        if (actor.getClass().isNpc() && enemy.getClass().isNpc())\n        {\n            if (enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack)\n            {\n                static const int iWereWolfFleeMod = gmst.find(\"iWereWolfFleeMod\")->mValue.getInteger();\n                rating = iWereWolfFleeMod;\n            }\n        }\n\n        if (rating != 0.0f)\n            rating += getFightDistanceBias(actor, enemy);\n\n        return rating;\n    }\n\n    bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating)\n    {\n        float fleeRating = vanillaRateFlee(actor, enemy);\n        if (fleeRating < 100.0f)\n            fleeRating = 0.0f;\n\n        if (fleeRating > antiFleeRating)\n            return true;\n\n        // Run away after summoning a creature if we have nothing to use but fists.\n        if (antiFleeRating == 0.0f && !actor.getClass().getCreatureStats(actor).getSummonedCreatureMap().empty())\n            return true;\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aicombataction.hpp",
    "content": "#ifndef OPENMW_AICOMBAT_ACTION_H\n#define OPENMW_AICOMBAT_ACTION_H\n\n#include <memory>\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n\nnamespace MWMechanics\n{\n    class Action\n    {\n    public:\n        virtual ~Action() {}\n        virtual void prepare(const MWWorld::Ptr& actor) = 0;\n        virtual float getCombatRange (bool& isRanged) const = 0;\n        virtual float getActionCooldown() { return 0.f; }\n        virtual const ESM::Weapon* getWeapon() const { return nullptr; }\n        virtual bool isAttackingOrSpell() const { return true; }\n        virtual bool isFleeing() const { return false; }\n    };\n\n    class ActionFlee : public Action\n    {\n    public:\n        ActionFlee() {}\n        void prepare(const MWWorld::Ptr& actor) override {}\n        float getCombatRange (bool& isRanged) const override { return 0.0f; }\n        float getActionCooldown() override { return 3.0f; }\n        bool isAttackingOrSpell() const override { return false; }\n        bool isFleeing() const override { return true; }\n    };\n\n    class ActionSpell : public Action\n    {\n    public:\n        ActionSpell(const std::string& spellId) : mSpellId(spellId) {}\n        std::string mSpellId;\n        /// Sets the given spell as selected on the actor's spell list.\n        void prepare(const MWWorld::Ptr& actor) override;\n\n        float getCombatRange (bool& isRanged) const override;\n    };\n\n    class ActionEnchantedItem : public Action\n    {\n    public:\n        ActionEnchantedItem(const MWWorld::ContainerStoreIterator& item) : mItem(item) {}\n        MWWorld::ContainerStoreIterator mItem;\n        /// Sets the given item as selected enchanted item in the actor's InventoryStore.\n        void prepare(const MWWorld::Ptr& actor) override;\n        float getCombatRange (bool& isRanged) const override;\n\n        /// Since this action has no animation, apply a small cool down for using it\n        float getActionCooldown() override { return 0.75f; }\n    };\n\n    class ActionPotion : public Action\n    {\n    public:\n        ActionPotion(const MWWorld::Ptr& potion) : mPotion(potion) {}\n        MWWorld::Ptr mPotion;\n        /// Drinks the given potion.\n        void prepare(const MWWorld::Ptr& actor) override;\n        float getCombatRange (bool& isRanged) const override;\n        bool isAttackingOrSpell() const override { return false; }\n\n        /// Since this action has no animation, apply a small cool down for using it\n        float getActionCooldown() override { return 0.75f; }\n    };\n\n    class ActionWeapon : public Action\n    {\n    private:\n        MWWorld::Ptr mAmmunition;\n        MWWorld::Ptr mWeapon;\n\n    public:\n        /// \\a weapon may be empty for hand-to-hand combat\n        ActionWeapon(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo = MWWorld::Ptr())\n            : mAmmunition(ammo), mWeapon(weapon) {}\n        /// Equips the given weapon.\n        void prepare(const MWWorld::Ptr& actor) override;\n        float getCombatRange (bool& isRanged) const override;\n        const ESM::Weapon* getWeapon() const override;\n    };\n\n    std::shared_ptr<Action> prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n    float getBestActionRating(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy);\n\n    float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool minusZDist=false);\n    float getMaxAttackDistance(const MWWorld::Ptr& actor);\n    bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n\n    float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n    bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiescort.cpp",
    "content": "#include \"aiescort.hpp\"\r\n\r\n#include <components/esm/aisequence.hpp>\r\n#include <components/esm/loadcell.hpp>\r\n\r\n#include \"../mwbase/world.hpp\"\r\n#include \"../mwbase/environment.hpp\"\r\n#include \"../mwbase/mechanicsmanager.hpp\"\r\n\r\n#include \"../mwworld/class.hpp\"\r\n#include \"../mwworld/cellstore.hpp\"\r\n\r\n#include \"creaturestats.hpp\"\r\n#include \"movement.hpp\"\r\n\r\n/*\r\n    TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z.\r\n    TODO: Take account for actors being in different cells.\r\n*/\r\n\r\nnamespace MWMechanics\r\n{\r\n    AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)\r\n    : mX(x), mY(y), mZ(z), mDuration(duration), mRemainingDuration(static_cast<float>(duration))\r\n    , mCellX(std::numeric_limits<int>::max())\r\n    , mCellY(std::numeric_limits<int>::max())\r\n    {\r\n        mTargetActorRefId = actorId;\r\n    }\r\n\r\n    AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z)\r\n    : mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration), mRemainingDuration(static_cast<float>(duration))\r\n    , mCellX(std::numeric_limits<int>::max())\r\n    , mCellY(std::numeric_limits<int>::max())\r\n    {\r\n        mTargetActorRefId = actorId;\r\n    }\r\n\r\n    AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)\r\n        : mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ)\r\n        // mDuration isn't saved in the save file, so just giving it \"1\" for now if the package has a duration.\r\n        // The exact value of mDuration only matters for repeating packages.\r\n        // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.\r\n        , mDuration(escort->mRemainingDuration > 0)\r\n        , mRemainingDuration(escort->mRemainingDuration)\r\n        , mCellX(std::numeric_limits<int>::max())\r\n        , mCellY(std::numeric_limits<int>::max())\r\n    {\r\n        mTargetActorRefId = escort->mTargetId;\r\n        mTargetActorId = escort->mTargetActorId;\r\n    }\r\n\r\n    bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)\r\n    {\r\n        // If AiEscort has ran for as long or longer then the duration specified\r\n        // and the duration is not infinite, the package is complete.\r\n        if (mDuration > 0)\r\n        {\r\n            mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600);\r\n            if (mRemainingDuration <= 0)\r\n            {\r\n                mRemainingDuration = mDuration;\r\n                return true;\r\n            }\r\n        }\r\n\r\n        if (!mCellId.empty() && mCellId != actor.getCell()->getCell()->getCellId().mWorldspace)\r\n            return false; // Not in the correct cell, pause and rely on the player to go back through a teleport door\r\n\r\n        actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);\r\n        actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);\r\n\r\n        const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mTargetActorRefId, false);\r\n        const osg::Vec3f leaderPos = actor.getRefData().getPosition().asVec3();\r\n        const osg::Vec3f followerPos = follower.getRefData().getPosition().asVec3();\r\n        const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);\r\n        const float maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));\r\n\r\n        if ((leaderPos - followerPos).length2() <= mMaxDist * mMaxDist)\r\n        {\r\n            const osg::Vec3f dest(mX, mY, mZ);\r\n            if (pathTo(actor, dest, duration, maxHalfExtent)) //Returns true on path complete\r\n            {\r\n                mRemainingDuration = mDuration;\r\n                return true;\r\n            }\r\n            mMaxDist = maxHalfExtent + 450.0f;\r\n        }\r\n        else\r\n        {\r\n            // Stop moving if the player is too far away\r\n            MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, \"idle3\", 0, 1);\r\n            actor.getClass().getMovementSettings(actor).mPosition[1] = 0;\r\n            mMaxDist = maxHalfExtent + 250.0f;\r\n        }\r\n\r\n        return false;\r\n    }\r\n\r\n    void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const\r\n    {\r\n        std::unique_ptr<ESM::AiSequence::AiEscort> escort(new ESM::AiSequence::AiEscort());\r\n        escort->mData.mX = mX;\r\n        escort->mData.mY = mY;\r\n        escort->mData.mZ = mZ;\r\n        escort->mTargetId = mTargetActorRefId;\r\n        escort->mTargetActorId = mTargetActorId;\r\n        escort->mRemainingDuration = mRemainingDuration;\r\n        escort->mCellId = mCellId;\r\n\r\n        ESM::AiSequence::AiPackageContainer package;\r\n        package.mType = ESM::AiSequence::Ai_Escort;\r\n        package.mPackage = escort.release();\r\n        sequence.mPackages.push_back(package);\r\n    }\r\n\r\n    void AiEscort::fastForward(const MWWorld::Ptr& actor, AiState &state)\r\n    {\r\n        // Update duration counter if this package has a duration\r\n        if (mDuration > 0)\r\n            mRemainingDuration--;\r\n    }\r\n}\r\n\r\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiescort.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIESCORT_H\n#define GAME_MWMECHANICS_AIESCORT_H\n\n#include \"typedaipackage.hpp\"\n\n#include <string>\n\nnamespace ESM\n{\nnamespace AiSequence\n{\n    struct AiEscort;\n}\n}\n\nnamespace MWMechanics\n{\n    /// \\brief AI Package to have an NPC lead the player to a specific point\n    class AiEscort final : public TypedAiPackage<AiEscort>\n    {\n        public:\n            /// Implementation of AiEscort\n            /** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time\n                \\implement AiEscort **/\n            AiEscort(const std::string &actorId, int duration, float x, float y, float z);\n            /// Implementation of AiEscortCell\n            /** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time\n                \\implement AiEscortCell **/\n            AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z);\n\n            AiEscort(const ESM::AiSequence::AiEscort* escort);\n\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Escort; }\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mUseVariableSpeed = true;\n                options.mSideWithTarget = true;\n                return options;\n            }\n\n            void writeState(ESM::AiSequence::AiSequence &sequence) const override;\n\n            void fastForward(const MWWorld::Ptr& actor, AiState& state) override;\n\n            osg::Vec3f getDestination() const override { return osg::Vec3f(mX, mY, mZ); }\n\n        private:\n            const std::string mCellId;\n            const float mX;\n            const float mY;\n            const float mZ;\n            float mMaxDist = 450;\n            const float mDuration; // In hours\n            float mRemainingDuration; // In hours\n\n            const int mCellX;\n            const int mCellY;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiface.cpp",
    "content": "#include \"aiface.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n\n#include \"steering.hpp\"\n\nMWMechanics::AiFace::AiFace(float targetX, float targetY)\n    : mTargetX(targetX), mTargetY(targetY)\n{\n}\n\nbool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& /*characterController*/, MWMechanics::AiState& /*state*/, float /*duration*/)\n{\n    osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3();\n    return zTurn(actor, std::atan2(dir.x(), dir.y()), osg::DegreesToRadians(3.f));\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiface.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIFACE_H\n#define GAME_MWMECHANICS_AIFACE_H\n\n#include \"typedaipackage.hpp\"\n\nnamespace MWMechanics\n{\n    /// AiPackage which makes an actor face a certain direction.\n    class AiFace final : public TypedAiPackage<AiFace> {\n        public:\n            AiFace(float targetX, float targetY);\n\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Face; }\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mPriority = 2;\n                options.mCanCancel = false;\n                options.mShouldCancelPreviousAi = false;\n                return options;\n            }\n\n        private:\n            const float mTargetX;\n            const float mTargetY;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aifollow.cpp",
    "content": "#include \"aifollow.hpp\"\n\n#include <components/esm/aisequence.hpp>\n#include <components/esm/loadcell.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"movement.hpp\"\n#include \"steering.hpp\"\n\nnamespace\n{\nosg::Vec3f::value_type getHalfExtents(const MWWorld::ConstPtr& actor)\n{\n    if(actor.getClass().isNpc())\n        return 64;\n    return MWBase::Environment::get().getWorld()->getHalfExtents(actor).y();\n}\n}\n\nnamespace MWMechanics\n{\nint AiFollow::mFollowIndexCounter = 0;\n\nAiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)\n: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)\n, mCellId(\"\"), mActive(false), mFollowIndex(mFollowIndexCounter++)\n{\n    mTargetActorRefId = actorId;\n}\n\nAiFollow::AiFollow(const std::string &actorId, const std::string &cellId, float duration, float x, float y, float z)\n: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)\n, mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)\n{\n    mTargetActorRefId = actorId;\n}\n\nAiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, float z)\n: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)\n, mCellId(\"\"), mActive(false), mFollowIndex(mFollowIndexCounter++)\n{\n    mTargetActorRefId = actor.getCellRef().getRefId();\n    mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();\n}\n\nAiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float duration, float x, float y, float z)\n: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)\n, mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)\n{\n    mTargetActorRefId = actor.getCellRef().getRefId();\n    mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();\n}\n\nAiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)\n: TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!commanded))\n, mAlwaysFollow(true), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0)\n, mCellId(\"\"), mActive(false), mFollowIndex(mFollowIndexCounter++)\n{\n    mTargetActorRefId = actor.getCellRef().getRefId();\n    mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();\n}\n\nAiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)\n    : TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded))\n    , mAlwaysFollow(follow->mAlwaysFollow)\n    // mDuration isn't saved in the save file, so just giving it \"1\" for now if the package had a duration.\n    // The exact value of mDuration only matters for repeating packages.\n    // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.\n    , mDuration(follow->mRemainingDuration)\n    , mRemainingDuration(follow->mRemainingDuration)\n    , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)\n    , mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++)\n{\n    mTargetActorRefId = follow->mTargetId;\n    mTargetActorId = follow->mTargetActorId;\n}\n\nbool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)\n{\n    const MWWorld::Ptr target = getTarget();\n\n    // Target is not here right now, wait for it to return\n    // Really we should be checking whether the target is currently registered with the MechanicsManager\n    if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())\n        return false;\n\n    actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);\n\n    AiFollowStorage& storage = state.get<AiFollowStorage>();\n\n    bool& rotate = storage.mTurnActorToTarget;\n    if (rotate)\n    {\n        if (zTurn(actor, storage.mTargetAngleRadians))\n            rotate = false;\n\n        return false;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        If this follow package is set to allow for any distance, skip the checks below\n    */\n    if (mIgnoreDistance)\n        mActive = true;\n    /*\n        End of tes3mp addition\n    */\n\n    const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());\n    const osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());\n    const osg::Vec3f targetDir = targetPos - actorPos;\n\n    // AiFollow requires the target to be in range and within sight for the initial activation\n    if (!mActive)\n    {\n        storage.mTimer -= duration;\n\n        if (storage.mTimer < 0)\n        {\n            if (targetDir.length2() < 500*500 && MWBase::Environment::get().getWorld()->getLOS(actor, target))\n                mActive = true;\n            storage.mTimer = 0.5f;\n        }\n    }\n    if (!mActive)\n        return false;\n\n    // In the original engine the first follower stays closer to the player than any subsequent followers.\n    // Followers beyond the first usually attempt to stand inside each other.\n    osg::Vec3f::value_type floatingDistance = 0;\n    auto followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingByIndex(target);\n    if (followers.size() >= 2 && followers.cbegin()->first != mFollowIndex)\n    {\n        for(auto& follower : followers)\n        {\n            auto halfExtent = getHalfExtents(follower.second);\n            if(halfExtent > floatingDistance)\n                floatingDistance = halfExtent;\n        }\n        floatingDistance += 128;\n    }\n    floatingDistance += getHalfExtents(target) + 64;\n    floatingDistance += getHalfExtents(actor) * 2;\n    short followDistance = static_cast<short>(floatingDistance);\n\n    if (!mAlwaysFollow) //Update if you only follow for a bit\n    {\n         //Check if we've run out of time\n        if (mDuration > 0)\n        {\n            mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600);\n            if (mRemainingDuration <= 0)\n            {\n                mRemainingDuration = mDuration;\n                return true;\n            }\n        }\n\n        osg::Vec3f finalPos(mX, mY, mZ);\n        if ((actorPos-finalPos).length2() < followDistance*followDistance) //Close-ish to final position\n        {\n            if (actor.getCell()->isExterior()) //Outside?\n            {\n                if (mCellId == \"\") //No cell to travel to\n                    return true;\n            }\n            else\n            {\n                if (mCellId == actor.getCell()->getCell()->mName) //Cell to travel to\n                    return true;\n            }\n        }\n    }\n\n\n    short baseFollowDistance = followDistance;\n    short threshold = 30; // to avoid constant switching between moving/stopping\n    if (storage.mMoving)\n        followDistance -= threshold;\n    else\n        followDistance += threshold;\n\n    if (targetDir.length2() <= followDistance * followDistance)\n    {\n        float faceAngleRadians = std::atan2(targetDir.x(), targetDir.y());\n\n        if (!zTurn(actor, faceAngleRadians, osg::DegreesToRadians(45.f)))\n        {\n            storage.mTargetAngleRadians = faceAngleRadians;\n            storage.mTurnActorToTarget = true;\n        }\n\n        return false;\n    }\n\n    storage.mMoving = !pathTo(actor, targetPos, duration, baseFollowDistance); // Go to the destination\n\n    if (storage.mMoving)\n    {\n        //Check if you're far away\n        if (targetDir.length2() > 450 * 450)\n            actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run\n        else if (targetDir.length2() < 325 * 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshold\n            actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk\n    }\n\n    return false;\n}\n\nstd::string AiFollow::getFollowedActor()\n{\n    return mTargetActorRefId;\n}\n\nbool AiFollow::isCommanded() const\n{\n    return !mOptions.mShouldCancelPreviousAi;\n}\n\nvoid AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const\n{\n    std::unique_ptr<ESM::AiSequence::AiFollow> follow(new ESM::AiSequence::AiFollow());\n    follow->mData.mX = mX;\n    follow->mData.mY = mY;\n    follow->mData.mZ = mZ;\n    follow->mTargetId = mTargetActorRefId;\n    follow->mTargetActorId = mTargetActorId;\n    follow->mRemainingDuration = mRemainingDuration;\n    follow->mCellId = mCellId;\n    follow->mAlwaysFollow = mAlwaysFollow;\n    follow->mCommanded = isCommanded();\n    follow->mActive = mActive;\n\n    ESM::AiSequence::AiPackageContainer package;\n    package.mType = ESM::AiSequence::Ai_Follow;\n    package.mPackage = follow.release();\n    sequence.mPackages.push_back(package);\n}\n\nint AiFollow::getFollowIndex() const\n{\n    return mFollowIndex;\n}\n\nvoid AiFollow::fastForward(const MWWorld::Ptr& actor, AiState &state)\n{\n    // Update duration counter if this package has a duration\n    if (mDuration > 0)\n        mRemainingDuration--;\n}\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to allow following from any distance\n*/\nvoid AiFollow::allowAnyDistance(bool state)\n{\n    mIgnoreDistance = state;\n}\n/*\n    End of tes3mp addition\n*/\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aifollow.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIFOLLOW_H\n#define GAME_MWMECHANICS_AIFOLLOW_H\n\n#include \"typedaipackage.hpp\"\n\n#include <string>\n\n#include <components/esm/defs.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace ESM\n{\nnamespace AiSequence\n{\n    struct AiFollow;\n}\n}\n\nnamespace MWMechanics\n{\n    struct AiFollowStorage : AiTemporaryBase\n    {\n        float mTimer;\n        bool mMoving;\n        float mTargetAngleRadians;\n        bool mTurnActorToTarget;\n\n        AiFollowStorage() :\n            mTimer(0.f),\n            mMoving(false),\n            mTargetAngleRadians(0.f),\n            mTurnActorToTarget(false)\n        {}\n    };\n\n    /// \\brief AiPackage for an actor to follow another actor/the PC\n    /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely\n    **/\n    class AiFollow final : public TypedAiPackage<AiFollow>\n    {\n        public:\n            AiFollow(const std::string &actorId, float duration, float x, float y, float z);\n            AiFollow(const std::string &actorId, const std::string &CellId, float duration, float x, float y, float z);\n            /// Follow Actor for duration or until you arrive at a world position\n            AiFollow(const MWWorld::Ptr& actor, float duration, float X, float Y, float Z);\n            /// Follow Actor for duration or until you arrive at a position in a cell\n            AiFollow(const MWWorld::Ptr& actor, const std::string &CellId, float duration, float X, float Y, float Z);\n            /// Follow Actor indefinitively\n            AiFollow(const MWWorld::Ptr& actor, bool commanded=false);\n\n            AiFollow(const ESM::AiSequence::AiFollow* follow);\n\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Follow; }\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mUseVariableSpeed = true;\n                options.mSideWithTarget = true;\n                options.mFollowTargetThroughDoors = true;\n                return options;\n            }\n\n            /// Returns the actor being followed\n            std::string getFollowedActor();\n\n            void writeState (ESM::AiSequence::AiSequence& sequence) const override;\n\n            bool isCommanded() const;\n\n            int getFollowIndex() const;\n\n            void fastForward(const MWWorld::Ptr& actor, AiState& state) override;\n\n            osg::Vec3f getDestination() const override\n            {\n                MWWorld::Ptr target = getTarget();\n                if (target.isEmpty())\n                    return osg::Vec3f(0, 0, 0);\n\n                return target.getRefData().getPosition().asVec3();\n            }\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to allow following from any distance\n            */\n            void allowAnyDistance(bool state);\n            /*\n                End of tes3mp addition\n            */\n\n        private:\n            /// This will make the actor always follow.\n            /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/\n            const bool mAlwaysFollow;\n            const float mDuration; // Hours\n            float mRemainingDuration; // Hours\n            const float mX;\n            const float mY;\n            const float mZ;\n            const std::string mCellId;\n            bool mActive; // have we spotted the target?\n            const int mFollowIndex;\n\n            static int mFollowIndexCounter;\n\n            /*\n                Start of tes3mp addition\n\n                Track whether this package allows following to start from any distance\n            */\n            bool mIgnoreDistance = false;\n            /*\n                End of tes3mp addition\n            */\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aipackage.cpp",
    "content": "#include \"aipackage.hpp\"\n\n#include <components/esm/loadcell.hpp>\n#include <components/esm/loadland.hpp>\n#include <components/detournavigator/navigator.hpp>\n#include <components/misc/coordinateconverter.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/action.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"pathgrid.hpp\"\n#include \"creaturestats.hpp\"\n#include \"movement.hpp\"\n#include \"steering.hpp\"\n#include \"actorutil.hpp\"\n\n#include <osg/Quat>\n\nnamespace\n{\n    float divOrMax(float dividend, float divisor)\n    {\n        return divisor == 0 ? std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon() : dividend / divisor;\n    }\n\n    float getPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents)\n    {\n        const float actorTolerance = 2 * speed * duration + 1.2 * std::max(halfExtents.x(), halfExtents.y());\n        return std::max(MWMechanics::MIN_TOLERANCE, actorTolerance);\n    }\n}\n\nMWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :\n    mTypeId(typeId),\n    mOptions(options),\n    mTargetActorRefId(\"\"),\n    mTargetActorId(-1),\n    mRotateOnTheRunChecks(0),\n    mIsShortcutting(false),\n    mShortcutProhibited(false),\n    mShortcutFailPos()\n{\n}\n\nMWWorld::Ptr MWMechanics::AiPackage::getTarget() const\n{\n    if (mTargetActorId == -2)\n        return MWWorld::Ptr();\n\n    if (mTargetActorId == -1)\n    {\n        if (mTargetActorRefId.empty())\n        {\n            mTargetActorId = -2;\n            return MWWorld::Ptr();\n        }\n        MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mTargetActorRefId, false);\n        if (target.isEmpty())\n        {\n            mTargetActorId = -2;\n            return target;\n        }\n        else\n            mTargetActorId = target.getClass().getCreatureStats(target).getActorId();\n    }\n\n    if (mTargetActorId != -1)\n        return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);\n    else\n        return MWWorld::Ptr();\n}\n\nvoid MWMechanics::AiPackage::reset()\n{\n    // reset all members\n    mReaction.reset();\n    mIsShortcutting = false;\n    mShortcutProhibited = false;\n    mShortcutFailPos = osg::Vec3f();\n\n    mPathFinder.clearPath();\n    mObstacleCheck.clear();\n}\n\nbool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, float destTolerance)\n{\n    const Misc::TimerStatus timerStatus = mReaction.update(duration);\n\n    const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n\n    const osg::Vec3f halfExtents = world->getHalfExtents(actor);\n\n    /// Stops the actor when it gets too close to a unloaded cell\n    //... At current time, this test is unnecessary. AI shuts down when actor is more than \"actors processing range\" setting value\n    //... units from player, and exterior cells are 8192 units long and wide.\n    //... But AI processing distance may increase in the future.\n    if (isNearInactiveCell(position))\n    {\n        actor.getClass().getMovementSettings(actor).mPosition[0] = 0;\n        actor.getClass().getMovementSettings(actor).mPosition[1] = 0;\n        world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);\n        return false;\n    }\n\n    mLastDestinationTolerance = destTolerance;\n\n    const float distToTarget = distance(position, dest);\n    const bool isDestReached = (distToTarget <= destTolerance);\n    const bool actorCanMoveByZ = canActorMoveByZAxis(actor);\n\n    if (!isDestReached && timerStatus == Misc::TimerStatus::Elapsed)\n    {\n        if (actor.getClass().isBipedal(actor))\n            openDoors(actor);\n\n        const bool wasShortcutting = mIsShortcutting;\n        bool destInLOS = false;\n\n        // Prohibit shortcuts for AiWander, if the actor can not move in 3 dimensions.\n        mIsShortcutting = actorCanMoveByZ\n            && shortcutPath(position, dest, actor, &destInLOS, actorCanMoveByZ); // try to shortcut first\n\n        if (!mIsShortcutting)\n        {\n            if (wasShortcutting || doesPathNeedRecalc(dest, actor)) // if need to rebuild path\n            {\n                const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor);\n                mPathFinder.buildLimitedPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),\n                    pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor));\n                mRotateOnTheRunChecks = 3;\n\n                // give priority to go directly on target if there is minimal opportunity\n                if (destInLOS && mPathFinder.getPath().size() > 1)\n                {\n                    // get point just before dest\n                    auto pPointBeforeDest = mPathFinder.getPath().rbegin() + 1;\n\n                    // if start point is closer to the target then last point of path (excluding target itself) then go straight on the target\n                    if (distance(position, dest) <= distance(dest, *pPointBeforeDest))\n                    {\n                        mPathFinder.clearPath();\n                        mPathFinder.addPointToPath(dest);\n                    }\n                }\n            }\n\n            if (!mPathFinder.getPath().empty()) //Path has points in it\n            {\n                const osg::Vec3f& lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path\n\n                if(distance(dest, lastPos) > 100) //End of the path is far from the destination\n                    mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go\n            }\n        }\n    }\n\n    const float pointTolerance = getPointTolerance(actor.getClass().getMaxSpeed(actor), duration, halfExtents);\n\n    static const bool smoothMovement = Settings::Manager::getBool(\"smooth movement\", \"Game\");\n    mPathFinder.update(position, pointTolerance, DEFAULT_TOLERANCE,\n                       /*shortenIfAlmostStraight=*/smoothMovement, actorCanMoveByZ,\n                       halfExtents, getNavigatorFlags(actor));\n\n    if (isDestReached || mPathFinder.checkPathCompleted()) // if path is finished\n    {\n        // turn to destination point\n        zTurn(actor, getZAngleToPoint(position, dest));\n        smoothTurn(actor, getXAngleToPoint(position, dest), 0);\n        world->removeActorPath(actor);\n        return true;\n    }\n    else if (mPathFinder.getPath().empty())\n        return false;\n\n    world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);\n\n    if (mRotateOnTheRunChecks == 0\n        || isReachableRotatingOnTheRun(actor, *mPathFinder.getPath().begin())) // to prevent circling around a path point\n    {\n        actor.getClass().getMovementSettings(actor).mPosition[1] = 1; // move to the target\n        if (mRotateOnTheRunChecks > 0) mRotateOnTheRunChecks--;\n    }\n\n    // turn to next path point by X,Z axes\n    float zAngleToNext = mPathFinder.getZAngleToNext(position.x(), position.y());\n    zTurn(actor, zAngleToNext);\n    smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);\n\n    const auto destination = getNextPathPoint(dest);\n    mObstacleCheck.update(actor, destination, duration);\n\n    if (smoothMovement)\n    {\n        const float smoothTurnReservedDist = 150;\n        auto& movement = actor.getClass().getMovementSettings(actor);\n        float distToNextSqr = osg::Vec2f(destination.x() - position.x(), destination.y() - position.y()).length2();\n        float diffAngle = zAngleToNext - actor.getRefData().getPosition().rot[2];\n        if (std::cos(diffAngle) < -0.1)\n            movement.mPosition[0] = movement.mPosition[1] = 0;\n        else if (distToNextSqr > smoothTurnReservedDist * smoothTurnReservedDist)\n        { // Go forward (and slowly turn towards the next path point)\n            movement.mPosition[0] = 0;\n            movement.mPosition[1] = 1;\n        }\n        else\n        { // Next path point is near, so use diagonal movement to follow the path precisely.\n            movement.mPosition[0] = std::sin(diffAngle);\n            movement.mPosition[1] = std::max(std::cos(diffAngle), 0.f);\n        }\n    }\n\n    // handle obstacles on the way\n    evadeObstacles(actor);\n\n    return false;\n}\n\nvoid MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor)\n{\n    // check if stuck due to obstacles\n    if (!mObstacleCheck.isEvading()) return;\n\n    // first check if obstacle is a door\n    static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();\n\n    const MWWorld::Ptr door = getNearbyDoor(actor, distance);\n    if (!door.isEmpty() && actor.getClass().isBipedal(actor))\n    {\n        openDoors(actor);\n    }\n    else\n    {\n        mObstacleCheck.takeEvasiveAction(actor.getClass().getMovementSettings(actor));\n    }\n}\n\nnamespace\n{\n    bool isDoorOnTheWay(const MWWorld::Ptr& actor, const MWWorld::Ptr& door, const osg::Vec3f& nextPathPoint)\n    {\n        const auto world = MWBase::Environment::get().getWorld();\n        const auto halfExtents = world->getHalfExtents(actor);\n        const auto position = actor.getRefData().getPosition().asVec3() + osg::Vec3f(0, 0, halfExtents.z());\n        const auto destination = nextPathPoint + osg::Vec3f(0, 0, halfExtents.z());\n\n        return world->hasCollisionWithDoor(door, position, destination);\n    }\n}\n\nvoid MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor)\n{\n    // note: AiWander currently does not open doors\n    if (getTypeId() == AiPackageTypeId::Wander)\n        return;\n\n    if (mPathFinder.getPathSize() == 0)\n        return;\n\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n    static float distance = world->getMaxActivationDistance();\n\n    const MWWorld::Ptr door = getNearbyDoor(actor, distance);\n    if (door == MWWorld::Ptr())\n        return;\n\n    if (!door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == MWWorld::DoorState::Idle)\n    {\n        if (!isDoorOnTheWay(actor, door, mPathFinder.getPath().front()))\n            return;\n\n        if ((door.getCellRef().getTrap().empty() && door.getCellRef().getLockLevel() <= 0 ))\n        {\n            world->activate(door, actor);\n            return;\n        }\n\n        const std::string keyId = door.getCellRef().getKey();\n        if (keyId.empty())\n            return;\n\n        MWWorld::ContainerStore &invStore = actor.getClass().getContainerStore(actor);\n        MWWorld::Ptr keyPtr = invStore.search(keyId);\n\n        if (!keyPtr.isEmpty())\n            world->activate(door, actor);\n    }\n}\n\nconst MWMechanics::PathgridGraph& MWMechanics::AiPackage::getPathGridGraph(const MWWorld::CellStore *cell)\n{\n    const ESM::CellId& id = cell->getCell()->getCellId();\n    // static cache is OK for now, pathgrids can never change during runtime\n    typedef std::map<ESM::CellId, std::unique_ptr<MWMechanics::PathgridGraph> > CacheMap;\n    static CacheMap cache;\n    CacheMap::iterator found = cache.find(id);\n    if (found == cache.end())\n    {\n        cache.insert(std::make_pair(id, std::make_unique<MWMechanics::PathgridGraph>(MWMechanics::PathgridGraph(cell))));\n    }\n    return *cache[id].get();\n}\n\nbool MWMechanics::AiPackage::shortcutPath(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n        const MWWorld::Ptr& actor, bool *destInLOS, bool isPathClear)\n{\n    if (!mShortcutProhibited || (mShortcutFailPos - startPoint).length() >= PATHFIND_SHORTCUT_RETRY_DIST)\n    {\n        // check if target is clearly visible\n        isPathClear = !MWBase::Environment::get().getWorld()->castRay(\n            startPoint.x(), startPoint.y(), startPoint.z(),\n            endPoint.x(), endPoint.y(), endPoint.z());\n\n        if (destInLOS != nullptr) *destInLOS = isPathClear;\n\n        if (!isPathClear)\n            return false;\n\n        // check if an actor can move along the shortcut path\n        isPathClear = checkWayIsClearForActor(startPoint, endPoint, actor);\n    }\n\n    if (isPathClear) // can shortcut the path\n    {\n        mPathFinder.clearPath();\n        mPathFinder.addPointToPath(endPoint);\n        return true;\n    }\n\n    return false;\n}\n\nbool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor)\n{\n    if (canActorMoveByZAxis(actor))\n        return true;\n\n    const float actorSpeed = actor.getClass().getMaxSpeed(actor);\n    const float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / getAngularVelocity(actorSpeed) * 2; // *2 - for reliability\n    const float distToTarget = osg::Vec2f(endPoint.x(), endPoint.y()).length();\n\n    const float offsetXY = distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2;\n\n    // update shortcut prohibit state\n    if (checkWayIsClear(startPoint, endPoint, offsetXY))\n    {\n        if (mShortcutProhibited)\n        {\n            mShortcutProhibited = false;\n            mShortcutFailPos = osg::Vec3f();\n        }\n        return true;\n    }\n    else\n    {\n        if (mShortcutFailPos == osg::Vec3f())\n        {\n            mShortcutProhibited = true;\n            mShortcutFailPos = startPoint;\n        }\n    }\n\n    return false;\n}\n\nbool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::Ptr& actor) const\n{\n    return mPathFinder.getPath().empty()\n        || getPathDistance(actor, mPathFinder.getPath().back(), newDest) > 10\n        || mPathFinder.getPathCell() != actor.getCell();\n}\n\nbool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)\n{\n    const ESM::Cell* playerCell(getPlayer().getCell()->getCell());\n    if (playerCell->isExterior())\n    {\n        // get actor's distance from origin of center cell\n        Misc::CoordinateConverter(playerCell).toLocal(position);\n\n        // currently assumes 3 x 3 grid for exterior cells, with player at center cell.\n        // AI shuts down actors before they reach edges of 3 x 3 grid.\n        const float distanceFromEdge = 200.0;\n        float minThreshold = (-1.0f * ESM::Land::REAL_SIZE) + distanceFromEdge;\n        float maxThreshold = (2.0f * ESM::Land::REAL_SIZE) - distanceFromEdge;\n        return (position.x() < minThreshold) || (maxThreshold < position.x())\n            || (position.y() < minThreshold) || (maxThreshold < position.y());\n    }\n    else\n    {\n        return false;\n    }\n}\n\nbool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest)\n{\n    // get actor's shortest radius for moving in circle\n    float speed = actor.getClass().getMaxSpeed(actor);\n    speed += speed * 0.1f; // 10% real speed inaccuracy\n    float radius = speed / getAngularVelocity(speed);\n\n    // get radius direction to the center\n    const float* rot = actor.getRefData().getPosition().rot;\n    osg::Quat quatRot(rot[0], -osg::X_AXIS, rot[1], -osg::Y_AXIS, rot[2], -osg::Z_AXIS);\n    osg::Vec3f dir = quatRot * osg::Y_AXIS; // actor's orientation direction is a tangent to circle\n    osg::Vec3f radiusDir = dir ^ osg::Z_AXIS; // radius is perpendicular to a tangent\n    radiusDir.normalize();\n    radiusDir *= radius;\n\n    // pick up the nearest center candidate\n    osg::Vec3f pos = actor.getRefData().getPosition().asVec3();\n    osg::Vec3f center1 = pos - radiusDir;\n    osg::Vec3f center2 = pos + radiusDir;\n    osg::Vec3f center = (center1 - dest).length2() < (center2 - dest).length2() ? center1 : center2;\n\n    float distToDest = (center - dest).length();\n\n    // if pathpoint is reachable for the actor rotating on the run:\n    // no points of actor's circle should be farther from the center than destination point\n    return (radius <= distToDest);\n}\n\nDetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::Ptr& actor) const\n{\n    static const bool allowToFollowOverWaterSurface = Settings::Manager::getBool(\"allow actors to follow over water surface\", \"Game\");\n\n    const MWWorld::Class& actorClass = actor.getClass();\n    DetourNavigator::Flags result = DetourNavigator::Flag_none;\n\n    if ((actorClass.isPureWaterCreature(actor)\n         || (getTypeId() != AiPackageTypeId::Wander\n             && ((allowToFollowOverWaterSurface && getTypeId() == AiPackageTypeId::Follow)\n                 || actorClass.canSwim(actor)\n                 || hasWaterWalking(actor)))\n        ) && actorClass.getSwimSpeed(actor) > 0)\n        result |= DetourNavigator::Flag_swim;\n\n    if (actorClass.canWalk(actor) && actor.getClass().getWalkSpeed(actor) > 0)\n        result |= DetourNavigator::Flag_walk;\n\n    if (actorClass.isBipedal(actor) && getTypeId() != AiPackageTypeId::Wander)\n        result |= DetourNavigator::Flag_openDoor;\n\n    return result;\n}\n\nDetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::Ptr& actor) const\n{\n    DetourNavigator::AreaCosts costs;\n    const DetourNavigator::Flags flags = getNavigatorFlags(actor);\n    const MWWorld::Class& actorClass = actor.getClass();\n\n    if (flags & DetourNavigator::Flag_swim)\n        costs.mWater = divOrMax(costs.mWater, actorClass.getSwimSpeed(actor));\n\n    if (flags & DetourNavigator::Flag_walk)\n    {\n        float walkCost;\n        if (getTypeId() == AiPackageTypeId::Wander)\n            walkCost = divOrMax(1.0, actorClass.getWalkSpeed(actor));\n        else\n            walkCost = divOrMax(1.0, actorClass.getRunSpeed(actor));\n        costs.mDoor = costs.mDoor * walkCost;\n        costs.mPathgrid = costs.mPathgrid * walkCost;\n        costs.mGround = costs.mGround * walkCost;\n    }\n\n    return costs;\n}\n\nosg::Vec3f MWMechanics::AiPackage::getNextPathPoint(const osg::Vec3f& destination) const\n{\n    return mPathFinder.getPath().empty() ? destination : mPathFinder.getPath().front();\n}\n\nfloat MWMechanics::AiPackage::getNextPathPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents) const\n{\n    if (mPathFinder.getPathSize() <= 1)\n        return std::max(DEFAULT_TOLERANCE, mLastDestinationTolerance);\n    return getPointTolerance(speed, duration, halfExtents);\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aipackage.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIPACKAGE_H\n#define GAME_MWMECHANICS_AIPACKAGE_H\n\n#include <memory>\n\n#include <components/detournavigator/areatype.hpp>\n\n#include \"pathfinding.hpp\"\n#include \"obstacle.hpp\"\n#include \"aistate.hpp\"\n#include \"aipackagetypeid.hpp\"\n#include \"aitimer.hpp\"\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace ESM\n{\n    struct Cell;\n    namespace AiSequence\n    {\n        struct AiSequence;\n    }\n}\n\n\nnamespace MWMechanics\n{\n    class CharacterController;\n    class PathgridGraph;\n\n    /// \\brief Base class for AI packages\n    class AiPackage\n    {\n        public:\n            struct Options\n            {\n                unsigned int mPriority = 0;\n                bool mUseVariableSpeed = false;\n                bool mSideWithTarget = false;\n                bool mFollowTargetThroughDoors = false;\n                bool mCanCancel = true;\n                bool mShouldCancelPreviousAi = true;\n                bool mRepeat = false;\n                bool mAlwaysActive = false;\n\n                constexpr Options withRepeat(bool value)\n                {\n                    mRepeat = value;\n                    return *this;\n                }\n\n                constexpr Options withShouldCancelPreviousAi(bool value)\n                {\n                    mShouldCancelPreviousAi = value;\n                    return *this;\n                }\n            };\n\n            AiPackage(AiPackageTypeId typeId, const Options& options);\n\n            virtual ~AiPackage() = default;\n\n            static constexpr Options makeDefaultOptions()\n            {\n                return Options{};\n            }\n\n            ///Clones the package\n            virtual std::unique_ptr<AiPackage> clone() const = 0;\n\n            /// Updates and runs the package (Should run every frame)\n            /// \\return Package completed?\n            virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) = 0;\n\n            /// Returns the TypeID of the AiPackage\n            /// \\see enum TypeId\n            AiPackageTypeId getTypeId() const { return mTypeId; }\n\n            /// Higher number is higher priority (0 being the lowest)\n            unsigned int getPriority() const { return mOptions.mPriority; }\n\n            /// Check if package use movement with variable speed\n            bool useVariableSpeed() const { return mOptions.mUseVariableSpeed; }\n\n            virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {}\n\n            /// Simulates the passing of time\n            virtual void fastForward(const MWWorld::Ptr& actor, AiState& state) {}\n\n            /// Get the target actor the AI is targeted at (not applicable to all AI packages, default return empty Ptr)\n            virtual MWWorld::Ptr getTarget() const;\n\n            /// Get the destination point of the AI package (not applicable to all AI packages, default return (0, 0, 0))\n            virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); };\n\n            /// Return true if having this AiPackage makes the actor side with the target in fights (default false)\n            bool sideWithTarget() const { return mOptions.mSideWithTarget; }\n\n            /// Return true if the actor should follow the target through teleport doors (default false)\n            bool followTargetThroughDoors() const { return mOptions.mFollowTargetThroughDoors; }\n\n            /// Can this Ai package be canceled? (default true)\n            bool canCancel() const { return mOptions.mCanCancel; }\n\n            /// Upon adding this Ai package, should the Ai Sequence attempt to cancel previous Ai packages (default true)?\n            bool shouldCancelPreviousAi() const { return mOptions.mShouldCancelPreviousAi; }\n\n            /// Return true if this package should repeat. Currently only used for Wander packages.\n            bool getRepeat() const { return mOptions.mRepeat; }\n\n            virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); }\n\n            /// Return true if any loaded actor with this AI package must be active.\n            bool alwaysActive() const { return mOptions.mAlwaysActive; }\n\n            /// Reset pathfinding state\n            void reset();\n\n            /// Return if actor's rotation speed is sufficient to rotate to the destination pathpoint on the run. Otherwise actor should rotate while standing.\n            static bool isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest);\n\n            osg::Vec3f getNextPathPoint(const osg::Vec3f& destination) const;\n\n            float getNextPathPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents) const;\n\n        protected:\n            /// Handles path building and shortcutting with obstacles avoiding\n            /** \\return If the actor has arrived at his destination **/\n            bool pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, float destTolerance = 0.0f);\n\n            /// Check if there aren't any obstacles along the path to make shortcut possible\n            /// If a shortcut is possible then path will be cleared and filled with the destination point.\n            /// \\param destInLOS If not nullptr function will return ray cast check result\n            /// \\return If can shortcut the path\n            bool shortcutPath(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor,\n                              bool *destInLOS, bool isPathClear);\n\n            /// Check if the way to the destination is clear, taking into account actor speed\n            bool checkWayIsClearForActor(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor);\n\n            bool doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::Ptr& actor) const;\n\n            void evadeObstacles(const MWWorld::Ptr& actor);\n\n            void openDoors(const MWWorld::Ptr& actor);\n\n            const PathgridGraph& getPathGridGraph(const MWWorld::CellStore* cell);\n\n            DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const;\n\n            DetourNavigator::AreaCosts getAreaCosts(const MWWorld::Ptr& actor) const;\n\n            const AiPackageTypeId mTypeId;\n            const Options mOptions;\n\n            // TODO: all this does not belong here, move into temporary storage\n            PathFinder mPathFinder;\n            ObstacleCheck mObstacleCheck;\n\n            AiReactionTimer mReaction;\n\n            std::string mTargetActorRefId;\n            mutable int mTargetActorId;\n\n            short mRotateOnTheRunChecks; // attempts to check rotation to the pathpoint on the run possibility\n\n            bool mIsShortcutting;   // if shortcutting at the moment\n            bool mShortcutProhibited; // shortcutting may be prohibited after unsuccessful attempt\n            osg::Vec3f mShortcutFailPos; // position of last shortcut fail\n            float mLastDestinationTolerance = 0;\n\n        private:\n            bool isNearInactiveCell(osg::Vec3f position);\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aipackagetypeid.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIPACKAGETYPEID_H\n#define GAME_MWMECHANICS_AIPACKAGETYPEID_H\n\nnamespace MWMechanics\n{\n    ///Enumerates the various AITypes available\n    enum class AiPackageTypeId\n    {\n        None = -1,\n        Wander = 0,\n        Travel = 1,\n        Escort = 2,\n        Follow = 3,\n        Activate = 4,\n\n        // These 5 are not really handled as Ai Packages in the MW engine\n        // For compatibility do *not* return these in the getCurrentAiPackage script function..\n        Combat = 5,\n        Pursue = 6,\n        AvoidDoor = 7,\n        Face = 8,\n        Breathe = 9,\n        InternalTravel = 10,\n        Cast = 11\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aipursue.cpp",
    "content": "#include \"aipursue.hpp\"\n\n#include <components/esm/aisequence.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwgui/windowmanagerimp.hpp\"\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"movement.hpp\"\n#include \"creaturestats.hpp\"\n#include \"combat.hpp\"\n\nnamespace MWMechanics\n{\n\nAiPursue::AiPursue(const MWWorld::Ptr& actor)\n{\n    mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();\n}\n\nAiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue)\n{\n    mTargetActorId = pursue->mTargetActorId;\n}\n\nbool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)\n{\n    if(actor.getClass().getCreatureStats(actor).isDead())\n        return true;\n\n    const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); //The target to follow\n\n    // Stop if the target doesn't exist\n    // Really we should be checking whether the target is currently registered with the MechanicsManager\n    if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())\n        return true;\n\n    if (isTargetMagicallyHidden(target) && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(target, actor))\n        return false;\n\n    if (target.getClass().getCreatureStats(target).isDead())\n        return true;\n\n    /*\n        Start of tes3mp addition\n\n        Because multiplayer does not pause the game, prevent infinite arrest loops by ignoring\n        players already engaged in dialogue while retaining the AiPursue package\n\n        Additionally, do not arrest players who are currently jailed\n    */\n    if (target == MWBase::Environment::get().getWorld()->getPlayerPtr())\n    {\n        if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue) ||\n            MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail))\n        {\n            return false;\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);\n\n    //Set the target destination\n    const osg::Vec3f dest = target.getRefData().getPosition().asVec3();\n    const osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();\n\n    const float pathTolerance = 100.f;\n\n    // check the true distance in case the target is far away in Z-direction\n    bool reached = pathTo(actor, dest, duration, pathTolerance) &&\n                   std::abs(dest.z() - actorPos.z()) < pathTolerance;\n\n    if (reached)\n    {\n        if (!MWBase::Environment::get().getWorld()->getLOS(target, actor))\n            return false;\n\n        /*\n            Start of tes3mp addition\n\n            Record that the player has not died since the last attempt to arrest them\n\n            Close the player's inventory or open container and cancel any drag and drops\n        */\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"After being pursued by %s, diedSinceArrestAttempt is now false\", actor.getCellRef().getRefId().c_str());\n        mwmp::Main::get().getLocalPlayer()->diedSinceArrestAttempt = false;\n        mwmp::Main::get().getLocalPlayer()->closeInventoryWindows();\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, actor); //Arrest player when reached\n        return true;\n    }\n\n    actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run\n\n    return false;\n}\n\nMWWorld::Ptr AiPursue::getTarget() const\n{\n    return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);\n}\n\nvoid AiPursue::writeState(ESM::AiSequence::AiSequence &sequence) const\n{\n    std::unique_ptr<ESM::AiSequence::AiPursue> pursue(new ESM::AiSequence::AiPursue());\n    pursue->mTargetActorId = mTargetActorId;\n\n    ESM::AiSequence::AiPackageContainer package;\n    package.mType = ESM::AiSequence::Ai_Pursue;\n    package.mPackage = pursue.release();\n    sequence.mPackages.push_back(package);\n}\n\n} // namespace MWMechanics\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aipursue.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIPURSUE_H\n#define GAME_MWMECHANICS_AIPURSUE_H\n\n#include \"typedaipackage.hpp\"\n\nnamespace ESM\n{\nnamespace AiSequence\n{\n    struct AiPursue;\n}\n}\n\nnamespace MWMechanics\n{\n    /// \\brief Makes the actor very closely follow the actor\n    /** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them.\n        Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the\n        path is completed). **/\n    class AiPursue final : public TypedAiPackage<AiPursue>\n    {\n        public:\n            ///Constructor\n            /** \\param actor Actor to pursue **/\n            AiPursue(const MWWorld::Ptr& actor);\n\n            AiPursue(const ESM::AiSequence::AiPursue* pursue);\n\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Pursue; }\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mCanCancel = false;\n                options.mShouldCancelPreviousAi = false;\n                return options;\n            }\n\n            MWWorld::Ptr getTarget() const override;\n\n            void writeState (ESM::AiSequence::AiSequence& sequence) const override;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aisequence.cpp",
    "content": "#include \"aisequence.hpp\"\n\n#include <limits>\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/aisequence.hpp>\n\n#include \"aipackage.hpp\"\n#include \"aistate.hpp\"\n#include \"aiwander.hpp\"\n#include \"aiescort.hpp\"\n#include \"aitravel.hpp\"\n#include \"aifollow.hpp\"\n#include \"aiactivate.hpp\"\n#include \"aicombat.hpp\"\n#include \"aicombataction.hpp\"\n#include \"aipursue.hpp\"\n#include \"actorutil.hpp\"\n#include \"../mwworld/class.hpp\"\n\nnamespace MWMechanics\n{\n\nvoid AiSequence::copy (const AiSequence& sequence)\n{\n    for (const auto& package : sequence.mPackages)\n        mPackages.push_back(package->clone());\n\n    // We need to keep an AiWander storage, if present - it has a state machine.\n    // Not sure about another temporary storages\n    sequence.mAiState.copy<AiWanderStorage>(mAiState);\n}\n\nAiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(AiPackageTypeId::None) {}\n\nAiSequence::AiSequence (const AiSequence& sequence)\n{\n    copy (sequence);\n    mDone = sequence.mDone;\n    mLastAiPackage = sequence.mLastAiPackage;\n    mRepeat = sequence.mRepeat;\n}\n\nAiSequence& AiSequence::operator= (const AiSequence& sequence)\n{\n    if (this!=&sequence)\n    {\n        clear();\n        copy (sequence);\n        mDone = sequence.mDone;\n        mLastAiPackage = sequence.mLastAiPackage;\n    }\n\n    return *this;\n}\n\nAiSequence::~AiSequence()\n{\n    clear();\n}\n\nAiPackageTypeId AiSequence::getTypeId() const\n{\n    if (mPackages.empty())\n        return AiPackageTypeId::None;\n\n    return mPackages.front()->getTypeId();\n}\n\nbool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const\n{\n    if (getTypeId() != AiPackageTypeId::Combat)\n        return false;\n\n    targetActor = mPackages.front()->getTarget();\n\n    return !targetActor.isEmpty();\n}\n\nbool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const\n{\n    for (auto it = mPackages.begin(); it != mPackages.end(); ++it)\n    {\n        if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Combat)\n            targetActors.push_back((*it)->getTarget());\n    }\n\n    return !targetActors.empty();\n}\n\nstd::list<std::unique_ptr<AiPackage>>::const_iterator AiSequence::begin() const\n{\n    return mPackages.begin();\n}\n\nstd::list<std::unique_ptr<AiPackage>>::const_iterator AiSequence::end() const\n{\n    return mPackages.end();\n}\n\nvoid AiSequence::erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package)\n{\n    // Not sure if manually terminated packages should trigger mDone, probably not?\n    for(auto it = mPackages.begin(); it != mPackages.end(); ++it)\n    {\n        if (package == it)\n        {\n            mPackages.erase(it);\n            return;\n        }\n    }\n    throw std::runtime_error(\"can't find package to erase\");\n}\n\nbool AiSequence::isInCombat() const\n{\n    for (auto it = mPackages.begin(); it != mPackages.end(); ++it)\n    {\n        if ((*it)->getTypeId() == AiPackageTypeId::Combat)\n            return true;\n    }\n    return false;\n}\n\nbool AiSequence::isEngagedWithActor() const\n{\n    for (auto it = mPackages.begin(); it != mPackages.end(); ++it)\n    {\n        if ((*it)->getTypeId() == AiPackageTypeId::Combat)\n        {\n            MWWorld::Ptr target2 = (*it)->getTarget();\n            if (!target2.isEmpty() && target2.getClass().isNpc())\n                return true;\n        }\n    }\n    return false;\n}\n\nbool AiSequence::hasPackage(AiPackageTypeId typeId) const\n{\n    for (auto it = mPackages.begin(); it != mPackages.end(); ++it)\n    {\n        if ((*it)->getTypeId() == typeId)\n            return true;\n    }\n    return false;\n}\n\nbool AiSequence::isInCombat(const MWWorld::Ptr &actor) const\n{\n    for (auto it = mPackages.begin(); it != mPackages.end(); ++it)\n    {\n        if ((*it)->getTypeId() == AiPackageTypeId::Combat)\n        {\n            if ((*it)->getTarget() == actor)\n                return true;\n        }\n    }\n    return false;\n}\n\nvoid AiSequence::stopCombat()\n{\n    for(auto it = mPackages.begin(); it != mPackages.end(); )\n    {\n        if ((*it)->getTypeId() == AiPackageTypeId::Combat)\n        {\n            it = mPackages.erase(it);\n        }\n        else\n            ++it;\n    }\n}\n\nvoid AiSequence::stopPursuit()\n{\n    for(auto it = mPackages.begin(); it != mPackages.end(); )\n    {\n        if ((*it)->getTypeId() == AiPackageTypeId::Pursue)\n        {\n            it = mPackages.erase(it);\n        }\n        else\n            ++it;\n    }\n}\n\nbool AiSequence::isPackageDone() const\n{\n    return mDone;\n}\n\nnamespace\n{\n    bool isActualAiPackage(AiPackageTypeId packageTypeId)\n    {\n        return (packageTypeId >= AiPackageTypeId::Wander &&\n                packageTypeId <= AiPackageTypeId::Activate);\n    }\n}\n\nvoid AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration, bool outOfRange)\n{\n    if(actor != getPlayer())\n    {\n        if (mPackages.empty())\n        {\n            mLastAiPackage = AiPackageTypeId::None;\n            return;\n        }\n\n        auto packageIt = mPackages.begin();\n        MWMechanics::AiPackage* package = packageIt->get();\n        if (!package->alwaysActive() && outOfRange)\n            return;\n\n        auto packageTypeId = package->getTypeId();\n        // workaround ai packages not being handled as in the vanilla engine\n        if (isActualAiPackage(packageTypeId))\n            mLastAiPackage = packageTypeId;\n        // if active package is combat one, choose nearest target\n        if (packageTypeId == AiPackageTypeId::Combat)\n        {\n            auto itActualCombat = mPackages.end();\n\n            float nearestDist = std::numeric_limits<float>::max();\n            osg::Vec3f vActorPos = actor.getRefData().getPosition().asVec3();\n\n            float bestRating = 0.f;\n\n            for (auto it = mPackages.begin(); it != mPackages.end();)\n            {\n                if ((*it)->getTypeId() != AiPackageTypeId::Combat) break;\n\n                MWWorld::Ptr target = (*it)->getTarget();\n\n                // target disappeared (e.g. summoned creatures)\n                if (target.isEmpty())\n                {\n                    it = mPackages.erase(it);\n                }\n                else\n                {\n                    float rating = MWMechanics::getBestActionRating(actor, target);\n\n                    const ESM::Position &targetPos = target.getRefData().getPosition();\n\n                    float distTo = (targetPos.asVec3() - vActorPos).length2();\n\n                    // Small threshold for changing target\n                    if (it == mPackages.begin())\n                        distTo = std::max(0.f, distTo - 2500.f);\n\n                    // if a target has higher priority than current target or has same priority but closer\n                    if (rating > bestRating || ((distTo < nearestDist) && rating == bestRating))\n                    {\n                        nearestDist = distTo;\n                        itActualCombat = it;\n                        bestRating = rating;\n                    }\n                    ++it;\n                }\n            }\n\n            assert(!mPackages.empty());\n\n            if (nearestDist < std::numeric_limits<float>::max() && mPackages.begin() != itActualCombat)\n            {\n                assert(itActualCombat != mPackages.end());\n                // move combat package with nearest target to the front\n                mPackages.splice(mPackages.begin(), mPackages, itActualCombat);\n            }\n\n            packageIt = mPackages.begin();\n            package = packageIt->get();\n            packageTypeId = package->getTypeId();\n        }\n\n        try\n        {\n            if (package->execute(actor, characterController, mAiState, duration))\n            {\n                // Put repeating noncombat AI packages on the end of the stack so they can be used again\n                if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat()))\n                {\n                    package->reset();\n                    mPackages.push_back(package->clone());\n                }\n                // To account for the rare case where AiPackage::execute() queued another AI package\n                // (e.g. AiPursue executing a dialogue script that uses startCombat)\n                mPackages.erase(packageIt);\n                if (isActualAiPackage(packageTypeId))\n                    mDone = true;\n            }\n            else\n            {\n                mDone = false;\n            }\n        }\n        catch (std::exception& e)\n        {\n            Log(Debug::Error) << \"Error during AiSequence::execute: \" << e.what();\n        }\n    }\n}\n\nvoid AiSequence::clear()\n{\n    mPackages.clear();\n}\n\nvoid AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, bool cancelOther)\n{\n    if (actor == getPlayer())\n        throw std::runtime_error(\"Can't add AI packages to player\");\n\n    // Stop combat when a non-combat AI package is added\n    if (isActualAiPackage(package.getTypeId()))\n        stopCombat();\n\n    // We should return a wandering actor back after combat, casting or pursuit.\n    // The same thing for actors without AI packages.\n    // Also there is no point to stack return packages.\n    const auto currentTypeId = getTypeId();\n    const auto newTypeId = package.getTypeId();\n    if (currentTypeId <= MWMechanics::AiPackageTypeId::Wander\n        && !hasPackage(MWMechanics::AiPackageTypeId::InternalTravel)\n        && (newTypeId <= MWMechanics::AiPackageTypeId::Combat\n        || newTypeId == MWMechanics::AiPackageTypeId::Pursue\n        || newTypeId == MWMechanics::AiPackageTypeId::Cast))\n    {\n        osg::Vec3f dest;\n        if (currentTypeId == MWMechanics::AiPackageTypeId::Wander)\n        {\n            dest = getActivePackage().getDestination(actor);\n        }\n        else\n        {\n            dest = actor.getRefData().getPosition().asVec3();\n        }\n\n        MWMechanics::AiInternalTravel travelPackage(dest.x(), dest.y(), dest.z());\n        stack(travelPackage, actor, false);\n    }\n\n    // remove previous packages if required\n    if (cancelOther && package.shouldCancelPreviousAi())\n    {\n        for (auto it = mPackages.begin(); it != mPackages.end();)\n        {\n            if((*it)->canCancel())\n            {\n                it = mPackages.erase(it);\n            }\n            else\n                ++it;\n        }\n        mRepeat=false;\n    }\n\n    // insert new package in correct place depending on priority\n    for (auto it = mPackages.begin(); it != mPackages.end(); ++it)\n    {\n        // We should keep current AiCast package, if we try to add a new one.\n        if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Cast &&\n            package.getTypeId() == MWMechanics::AiPackageTypeId::Cast)\n        {\n            continue;\n        }\n\n        if((*it)->getPriority() <= package.getPriority())\n        {\n            mPackages.insert(it, package.clone());\n            return;\n        }\n    }\n\n    mPackages.push_back(package.clone());\n\n    // Make sure that temporary storage is empty\n    if (cancelOther)\n    {\n        mAiState.moveIn(new AiCombatStorage());\n        mAiState.moveIn(new AiFollowStorage());\n        mAiState.moveIn(new AiWanderStorage());\n    }\n}\n\nbool MWMechanics::AiSequence::isEmpty() const\n{\n    return mPackages.empty();\n}\n\nconst AiPackage& MWMechanics::AiSequence::getActivePackage()\n{\n    if(mPackages.empty())\n        throw std::runtime_error(std::string(\"No AI Package!\"));\n    return *mPackages.front();\n}\n\nvoid AiSequence::fill(const ESM::AIPackageList &list)\n{\n    // If there is more than one package in the list, enable repeating\n    if (list.mList.size() >= 2)\n        mRepeat = true;\n\n    for (const auto& esmPackage : list.mList)\n    {\n        std::unique_ptr<MWMechanics::AiPackage> package;\n        if (esmPackage.mType == ESM::AI_Wander)\n        {\n            ESM::AIWander data = esmPackage.mWander;\n            std::vector<unsigned char> idles;\n            idles.reserve(8);\n            for (int i=0; i<8; ++i)\n                idles.push_back(data.mIdle[i]);\n            package = std::make_unique<MWMechanics::AiWander>(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0);\n        }\n        else if (esmPackage.mType == ESM::AI_Escort)\n        {\n            ESM::AITarget data = esmPackage.mTarget;\n            package = std::make_unique<MWMechanics::AiEscort>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);\n        }\n        else if (esmPackage.mType == ESM::AI_Travel)\n        {\n            ESM::AITravel data = esmPackage.mTravel;\n            package = std::make_unique<MWMechanics::AiTravel>(data.mX, data.mY, data.mZ);\n        }\n        else if (esmPackage.mType == ESM::AI_Activate)\n        {\n            ESM::AIActivate data = esmPackage.mActivate;\n            package = std::make_unique<MWMechanics::AiActivate>(data.mName.toString());\n        }\n        else //if (esmPackage.mType == ESM::AI_Follow)\n        {\n            ESM::AITarget data = esmPackage.mTarget;\n            package = std::make_unique<MWMechanics::AiFollow>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);\n        }\n        mPackages.push_back(std::move(package));\n    }\n}\n\nvoid AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const\n{\n    for (const auto& package : mPackages)\n        package->writeState(sequence);\n\n    sequence.mLastAiPackage = static_cast<int>(mLastAiPackage);\n}\n\nvoid AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)\n{\n    if (!sequence.mPackages.empty())\n        clear();\n\n    // If there is more than one non-combat, non-pursue package in the list, enable repeating.\n    int count = 0;\n    for (auto& container : sequence.mPackages)\n    {\n        switch (container.mType)\n        {\n            case ESM::AiSequence::Ai_Wander:\n            case ESM::AiSequence::Ai_Travel:\n            case ESM::AiSequence::Ai_Escort:\n            case ESM::AiSequence::Ai_Follow:\n            case ESM::AiSequence::Ai_Activate:\n                ++count;\n        }\n    }\n\n    if (count > 1)\n        mRepeat = true;\n\n    // Load packages\n    for (auto& container : sequence.mPackages)\n    {\n        std::unique_ptr<MWMechanics::AiPackage> package;\n        switch (container.mType)\n        {\n        case ESM::AiSequence::Ai_Wander:\n        {\n            package.reset(new AiWander(static_cast<ESM::AiSequence::AiWander*>(container.mPackage)));\n            break;\n        }\n        case ESM::AiSequence::Ai_Travel:\n        {\n            const auto source = static_cast<const ESM::AiSequence::AiTravel*>(container.mPackage);\n            if (source->mHidden)\n                package.reset(new AiInternalTravel(source));\n            else\n                package.reset(new AiTravel(source));\n            break;\n        }\n        case ESM::AiSequence::Ai_Escort:\n        {\n            package.reset(new AiEscort(static_cast<ESM::AiSequence::AiEscort*>(container.mPackage)));\n            break;\n        }\n        case ESM::AiSequence::Ai_Follow:\n        {\n            package.reset(new AiFollow(static_cast<ESM::AiSequence::AiFollow*>(container.mPackage)));\n            break;\n        }\n        case ESM::AiSequence::Ai_Activate:\n        {\n            package.reset(new AiActivate(static_cast<ESM::AiSequence::AiActivate*>(container.mPackage)));\n            break;\n        }\n        case ESM::AiSequence::Ai_Combat:\n        {\n            package.reset(new AiCombat(static_cast<ESM::AiSequence::AiCombat*>(container.mPackage)));\n            break;\n        }\n        case ESM::AiSequence::Ai_Pursue:\n        {\n            package.reset(new AiPursue(static_cast<ESM::AiSequence::AiPursue*>(container.mPackage)));\n            break;\n        }\n        default:\n            break;\n        }\n\n        if (!package.get())\n            continue;\n\n        mPackages.push_back(std::move(package));\n    }\n\n    mLastAiPackage = static_cast<AiPackageTypeId>(sequence.mLastAiPackage);\n}\n\nvoid AiSequence::fastForward(const MWWorld::Ptr& actor)\n{\n    if (!mPackages.empty())\n    {\n        mPackages.front()->fastForward(actor, mAiState);\n    }\n}\n\n} // namespace MWMechanics\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aisequence.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AISEQUENCE_H\n#define GAME_MWMECHANICS_AISEQUENCE_H\n\n#include <list>\n#include <memory>\n\n#include \"aistate.hpp\"\n#include \"aipackagetypeid.hpp\"\n\n#include <components/esm/loadnpc.hpp>\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace ESM\n{\n    namespace AiSequence\n    {\n        struct AiSequence;\n    }\n}\n\n\n\nnamespace MWMechanics\n{\n    class AiPackage;\n    class CharacterController;\n    \n    template< class Base > class DerivedClassStorage;\n    struct AiTemporaryBase;\n    typedef DerivedClassStorage<AiTemporaryBase> AiState;\n\n    /// \\brief Sequence of AI-packages for a single actor\n    /** The top-most AI package is run each frame. When completed, it is removed from the stack. **/\n    class AiSequence\n    {\n            ///AiPackages to run though\n            std::list<std::unique_ptr<AiPackage>> mPackages;\n\n            ///Finished with top AIPackage, set for one frame\n            bool mDone;\n\n            ///Does this AI sequence repeat (repeating of Wander packages handled separately)\n            bool mRepeat;\n\n            ///Copy AiSequence\n            void copy (const AiSequence& sequence);\n\n            /// The type of AI package that ran last\n            AiPackageTypeId mLastAiPackage;\n            AiState mAiState;\n\n        public:\n            ///Default constructor\n            AiSequence();\n\n            /// Copy Constructor\n            AiSequence (const AiSequence& sequence);\n\n            /// Assignment operator\n            AiSequence& operator= (const AiSequence& sequence);\n\n            virtual ~AiSequence();\n\n            /// Iterator may be invalidated by any function calls other than begin() or end().\n            std::list<std::unique_ptr<AiPackage>>::const_iterator begin() const;\n            std::list<std::unique_ptr<AiPackage>>::const_iterator end() const;\n\n            void erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package);\n\n            /// Returns currently executing AiPackage type\n            /** \\see enum class AiPackageTypeId **/\n            AiPackageTypeId getTypeId() const;\n\n            /// Get the typeid of the Ai package that ran last\n            /** NOT the currently \"active\" Ai package that will be run in the next frame.\n                This difference is important when an Ai package has just finished and been removed.\n                \\see enum class AiPackageTypeId **/\n            AiPackageTypeId getLastRunTypeId() const { return mLastAiPackage; }\n\n            /// Return true and assign target if combat package is currently active, return false otherwise\n            bool getCombatTarget (MWWorld::Ptr &targetActor) const;\n\n            /// Return true and assign targets for all combat packages, or return false if there are no combat packages\n            bool getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const;\n\n            /// Is there any combat package?\n            bool isInCombat () const;\n\n            /// Are we in combat with any other actor, who's also engaging us?\n            bool isEngagedWithActor () const;\n\n            /// Does this AI sequence have the given package type?\n            bool hasPackage(AiPackageTypeId typeId) const;\n\n            /// Are we in combat with this particular actor?\n            bool isInCombat (const MWWorld::Ptr& actor) const;\n\n            bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const;\n            ///< Function assumes that actor can have only 1 target apart player\n\n            /// Removes all combat packages until first non-combat or stack empty.\n            void stopCombat();\n\n            /// Has a package been completed during the last update?\n            bool isPackageDone() const;\n\n            /// Removes all pursue packages until first non-pursue or stack empty.\n            void stopPursuit();\n\n            /// Execute current package, switching if needed.\n            void execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration, bool outOfRange=false);\n\n            /// Simulate the passing of time using the currently active AI package\n            void fastForward(const MWWorld::Ptr &actor);\n\n            /// Remove all packages.\n            void clear();\n\n            ///< Add \\a package to the front of the sequence\n            /** Suspends current package\n                @param actor The actor that owns this AiSequence **/\n            void stack (const AiPackage& package, const MWWorld::Ptr& actor, bool cancelOther=true);\n\n            /// Return the current active package.\n            /** If there is no active package, it will throw an exception **/\n            const AiPackage& getActivePackage();\n\n            /// Fills the AiSequence with packages\n            /** Typically used for loading from the ESM\n                \\see ESM::AIPackageList **/\n            void fill (const ESM::AIPackageList& list);\n\n            bool isEmpty() const;\n\n            void writeState (ESM::AiSequence::AiSequence& sequence) const;\n            void readState (const ESM::AiSequence::AiSequence& sequence);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aistate.hpp",
    "content": "#ifndef AISTATE_H\n#define AISTATE_H\n\n#include <typeinfo>\n\nnamespace MWMechanics\n{\n\n    /** \\brief stores one object of any class derived from Base.\n     *  Requesting a certain derived class via get() either returns\n     * the stored object if it has the correct type or otherwise replaces\n     * it with an object of the requested type.\n     */\n    template< class Base >\n    class DerivedClassStorage\n    {              \n    private:\n        Base* mStorage;\n        \n        //if needed you have to provide a clone member function\n        DerivedClassStorage( const DerivedClassStorage& other );\n        DerivedClassStorage& operator=( const DerivedClassStorage& );\n        \n    public:\n        /// \\brief returns reference to stored object or deletes it and creates a fitting\n        template< class Derived >\n        Derived& get()\n        {\n            Derived* result = dynamic_cast<Derived*>(mStorage);\n            \n            if(!result)\n            {\n                if(mStorage)\n                    delete mStorage;\n                mStorage = result = new Derived();\n            }\n            \n            //return a reference to the (new allocated) object \n            return *result;\n        }\n\n        template< class Derived >\n        void copy(DerivedClassStorage& destination) const\n        {\n            Derived* result = dynamic_cast<Derived*>(mStorage);\n            if (result != nullptr)\n                destination.store<Derived>(*result);\n        }\n        \n        template< class Derived >\n        void store( const Derived& payload )\n        {\n            if(mStorage)\n                delete mStorage;\n            mStorage = new Derived(payload);\n        }\n        \n        /// \\brief takes ownership of the passed object\n        template< class Derived >\n        void moveIn( Derived* p )\n        {\n            if(mStorage)\n                delete mStorage;\n            mStorage = p;\n        }\n        \n        bool empty() const\n        {\n            return mStorage == nullptr;\n        }\n        \n        const std::type_info& getType() const\n        {\n            return typeid(mStorage);\n        }\n        \n        DerivedClassStorage():mStorage(nullptr){}\n        ~DerivedClassStorage()\n        {\n            if(mStorage)\n                delete mStorage;\n        }\n    };\n\n\n    /// \\brief base class for the temporary storage of AiPackages.\n    /**\n     * Each AI package with temporary values needs a AiPackageStorage class\n     * which is derived from AiTemporaryBase. The Actor holds a container\n     * AiState where one of these storages can be stored at a time.\n     * The execute(...) member function takes this container as an argument.\n     * */\n    struct AiTemporaryBase\n    {\n        virtual ~AiTemporaryBase(){}\n    };\n    \n    /// \\brief Container for AI package status.\n    typedef DerivedClassStorage<AiTemporaryBase> AiState;\n}\n\n#endif // AISTATE_H\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aitimer.hpp",
    "content": "#ifndef OPENMW_MECHANICS_AITIMER_H\n#define OPENMW_MECHANICS_AITIMER_H\n\n#include <components/misc/rng.hpp>\n#include <components/misc/timer.hpp>\n\nnamespace MWMechanics\n{\n    constexpr float AI_REACTION_TIME = 0.25f;\n\n    class AiReactionTimer\n    {\n        public:\n            static constexpr float sDeviation = 0.1f;\n\n            Misc::TimerStatus update(float duration) { return mImpl.update(duration); }\n\n            void reset() { mImpl.reset(Misc::Rng::deviate(0, sDeviation)); }\n\n        private:\n            Misc::DeviatingPeriodicTimer mImpl {AI_REACTION_TIME, sDeviation,\n                                                Misc::Rng::deviate(0, sDeviation)};\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aitravel.cpp",
    "content": "#include \"aitravel.hpp\"\n\n#include <components/esm/aisequence.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"movement.hpp\"\n#include \"creaturestats.hpp\"\n\nnamespace\n{\n\nbool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)\n{\n    // Maximum travel distance for vanilla compatibility.\n    // Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well.\n    // We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways.\n    return (pos1 - pos2).length2() <= 7168*7168;\n}\n\n}\n\nnamespace MWMechanics\n{\n    AiTravel::AiTravel(float x, float y, float z, AiTravel*)\n        : mX(x), mY(y), mZ(z), mHidden(false)\n    {\n    }\n\n    AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived)\n        : TypedAiPackage<AiTravel>(derived), mX(x), mY(y), mZ(z), mHidden(true)\n    {\n    }\n\n    AiTravel::AiTravel(float x, float y, float z)\n        : AiTravel(x, y, z, this)\n    {\n    }\n\n    AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)\n        : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false)\n    {\n        // Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type\n        assert(!travel->mHidden);\n    }\n\n    bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)\n    {\n        MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager();\n        auto& stats = actor.getClass().getCreatureStats(actor);\n\n        if (!stats.getMovementFlag(CreatureStats::Flag_ForceJump) && !stats.getMovementFlag(CreatureStats::Flag_ForceSneak)\n                && (mechMgr->isTurningToPlayer(actor) || mechMgr->getGreetingState(actor) == Greet_InProgress))\n            return false;\n\n        const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());\n        const osg::Vec3f targetPos(mX, mY, mZ);\n\n        stats.setMovementFlag(CreatureStats::Flag_Run, false);\n        stats.setDrawState(DrawState_Nothing);\n\n        // Note: we should cancel internal \"return after combat\" package, if original location is too far away\n        if (!isWithinMaxRange(targetPos, actorPos))\n            return mHidden;\n\n        // Unfortunately, with vanilla assets destination is sometimes blocked by other actor.\n        // If we got close to target, check for actors nearby. If they are, finish AI package.\n        int destinationTolerance = 64;\n        if (distance(actorPos, targetPos) <= destinationTolerance)\n        {\n            std::vector<MWWorld::Ptr> targetActors;\n            std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors);\n\n            if (!result.first.isEmpty())\n            {\n                actor.getClass().getMovementSettings(actor).mPosition[1] = 0;\n                return true;\n            }\n        }\n\n        if (pathTo(actor, targetPos, duration))\n        {\n            actor.getClass().getMovementSettings(actor).mPosition[1] = 0;\n            return true;\n        }\n        return false;\n    }\n\n    void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state)\n    {\n        if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3()))\n            return;\n        // does not do any validation on the travel target (whether it's in air, inside collision geometry, etc),\n        // that is the user's responsibility\n        MWBase::Environment::get().getWorld()->moveObject(actor, mX, mY, mZ);\n        actor.getClass().adjustPosition(actor, false);\n        reset();\n    }\n\n    void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const\n    {\n        std::unique_ptr<ESM::AiSequence::AiTravel> travel(new ESM::AiSequence::AiTravel());\n        travel->mData.mX = mX;\n        travel->mData.mY = mY;\n        travel->mData.mZ = mZ;\n        travel->mHidden = mHidden;\n\n        ESM::AiSequence::AiPackageContainer package;\n        package.mType = ESM::AiSequence::Ai_Travel;\n        package.mPackage = travel.release();\n        sequence.mPackages.push_back(package);\n    }\n\n    AiInternalTravel::AiInternalTravel(float x, float y, float z)\n        : AiTravel(x, y, z, this)\n    {\n    }\n\n    AiInternalTravel::AiInternalTravel(const ESM::AiSequence::AiTravel* travel)\n        : AiTravel(travel->mData.mX, travel->mData.mY, travel->mData.mZ, this)\n    {\n    }\n\n    std::unique_ptr<AiPackage> AiInternalTravel::clone() const\n    {\n        return std::make_unique<AiInternalTravel>(*this);\n    }\n}\n\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aitravel.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AITRAVEL_H\r\n#define GAME_MWMECHANICS_AITRAVEL_H\r\n\r\n#include \"typedaipackage.hpp\"\r\n\r\nnamespace ESM\r\n{\r\nnamespace AiSequence\r\n{\r\n    struct AiTravel;\r\n}\r\n}\r\n\r\nnamespace MWMechanics\r\n{\r\n    struct AiInternalTravel;\r\n\r\n    /// \\brief Causes the AI to travel to the specified point\r\n    class AiTravel : public TypedAiPackage<AiTravel>\r\n    {\r\n        public:\r\n            AiTravel(float x, float y, float z, AiTravel* derived);\r\n\r\n            AiTravel(float x, float y, float z, AiInternalTravel* derived);\r\n\r\n            AiTravel(float x, float y, float z);\r\n\r\n            explicit AiTravel(const ESM::AiSequence::AiTravel* travel);\r\n\r\n            /// Simulates the passing of time\r\n            void fastForward(const MWWorld::Ptr& actor, AiState& state) override;\r\n\r\n            void writeState(ESM::AiSequence::AiSequence &sequence) const override;\r\n\r\n            bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\r\n\r\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Travel; }\r\n\r\n            static constexpr Options makeDefaultOptions()\r\n            {\r\n                AiPackage::Options options;\r\n                options.mUseVariableSpeed = true;\r\n                options.mAlwaysActive = true;\r\n                return options;\r\n            }\r\n\r\n            osg::Vec3f getDestination() const override { return osg::Vec3f(mX, mY, mZ); }\r\n\r\n        private:\r\n            const float mX;\r\n            const float mY;\r\n            const float mZ;\r\n\r\n            const bool mHidden;\r\n    };\r\n\r\n    struct AiInternalTravel final : public AiTravel\r\n    {\r\n        AiInternalTravel(float x, float y, float z);\r\n\r\n        explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel);\r\n\r\n        static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::InternalTravel; }\r\n\r\n        std::unique_ptr<AiPackage> clone() const override;\r\n    };\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiwander.cpp",
    "content": "#include \"aiwander.hpp\"\n\n#include <algorithm>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/rng.hpp>\n#include <components/esm/aisequence.hpp>\n#include <components/detournavigator/navigator.hpp>\n#include <components/misc/coordinateconverter.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"../mwphysics/collisiontype.hpp\"\n\n#include \"pathgrid.hpp\"\n#include \"creaturestats.hpp\"\n#include \"movement.hpp\"\n#include \"actorutil.hpp\"\n\nnamespace MWMechanics\n{\n    static const int COUNT_BEFORE_RESET = 10;\n    static const float IDLE_POSITION_CHECK_INTERVAL = 1.5f;\n\n    // to prevent overcrowding\n    static const int DESTINATION_TOLERANCE = 64;\n\n    // distance must be long enough that NPC will need to move to get there.\n    static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2;\n\n    static const std::size_t MAX_IDLE_SIZE = 8;\n\n    const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] =\n    {\n        std::string(\"idle2\"),\n        std::string(\"idle3\"),\n        std::string(\"idle4\"),\n        std::string(\"idle5\"),\n        std::string(\"idle6\"),\n        std::string(\"idle7\"),\n        std::string(\"idle8\"),\n        std::string(\"idle9\"),\n    };\n\n    namespace\n    {\n        inline int getCountBeforeReset(const MWWorld::ConstPtr& actor)\n        {\n            if (actor.getClass().isPureWaterCreature(actor) || actor.getClass().isPureFlyingCreature(actor))\n                return 1;\n            return COUNT_BEFORE_RESET;\n        }\n\n        osg::Vec3f getRandomPointAround(const osg::Vec3f& position, const float distance)\n        {\n            const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI;\n            osg::Matrixf rotation;\n            rotation.makeRotate(randomDirection, osg::Vec3f(0.0, 0.0, 1.0));\n            return position + osg::Vec3f(distance, 0.0, 0.0) * rotation;\n        }\n\n        bool isDestinationHidden(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination)\n        {\n            const auto position = actor.getRefData().getPosition().asVec3();\n            const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);\n            const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);\n            const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);\n            osg::Vec3f direction = destination - position;\n            direction.normalize();\n            const auto visibleDestination = (\n                    isWaterCreature || isFlyingCreature\n                    ? destination\n                    : destination + osg::Vec3f(0, 0, halfExtents.z())\n                ) + direction * std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));\n            const int mask = MWPhysics::CollisionType_World\n                | MWPhysics::CollisionType_HeightMap\n                | MWPhysics::CollisionType_Door\n                | MWPhysics::CollisionType_Actor;\n            return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor);\n        }\n\n        bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination)\n        {\n            const auto world = MWBase::Environment::get().getWorld();\n            const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor);\n            const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));\n            return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor);\n        }\n\n        void stopMovement(const MWWorld::Ptr& actor)\n        {\n            actor.getClass().getMovementSettings(actor).mPosition[0] = 0;\n            actor.getClass().getMovementSettings(actor).mPosition[1] = 0;\n        }\n\n        std::vector<unsigned char> getInitialIdle(const std::vector<unsigned char>& idle)\n        {\n            std::vector<unsigned char> result(MAX_IDLE_SIZE, 0);\n            std::copy_n(idle.begin(), std::min(MAX_IDLE_SIZE, idle.size()), result.begin());\n            return result;\n        }\n\n        std::vector<unsigned char> getInitialIdle(const unsigned char (&idle)[MAX_IDLE_SIZE])\n        {\n            return std::vector<unsigned char>(std::begin(idle), std::end(idle));\n        }\n    }\n\n    AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):\n        TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(repeat)),\n        mDistance(std::max(0, distance)),\n        mDuration(std::max(0, duration)),\n        mRemainingDuration(duration), mTimeOfDay(timeOfDay),\n        mIdle(getInitialIdle(idle)),\n        mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)),\n        mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false)\n    {\n    }\n\n    /*\n     * AiWander high level states (0.29.0). Not entirely accurate in some cases\n     * e.g. non-NPC actors do not greet and some creatures may be moving even in\n     * the IdleNow state.\n     *\n     *                          [select node,\n     *                           build path]\n     *                 +---------->MoveNow----------->Walking\n     *                 |                                 |\n     * [allowed        |                                 |\n     *  nodes]         |        [hello if near]          |\n     *  start--->ChooseAction----->IdleNow               |\n     *                ^ ^           |                    |\n     *                | |           |                    |\n     *                | +-----------+                    |\n     *                |                                  |\n     *                +----------------------------------+\n     *\n     *\n     * New high level states.  Not exactly as per vanilla (e.g. door stuff)\n     * but the differences are required because our physics does not work like\n     * vanilla and therefore have to compensate/work around.\n     *\n     *                         [select node,     [if stuck evade\n     *                          build path]       or remove nodes if near door]\n     *                 +---------->MoveNow<---------->Walking\n     *                 |              ^                | |\n     *                 |              |(near door)     | |\n     * [allowed        |              |                | |\n     *  nodes]         |        [hello if near]        | |\n     *  start--->ChooseAction----->IdleNow             | |\n     *                ^ ^           |  ^               | |\n     *                | |           |  | (stuck near   | |\n     *                | +-----------+  +---------------+ |\n     *                |                    player)       |\n     *                +----------------------------------+\n     *\n     * NOTE: non-time critical operations are run once every 250ms or so.\n     *\n     * TODO: It would be great if door opening/closing can be detected and pathgrid\n     * links dynamically updated.  Currently (0.29.0) AiWander allows choosing a\n     * destination beyond closed doors which sometimes makes the actors stuck at the\n     * door and impossible for the player to open the door.\n     *\n     * For now detect being stuck at the door and simply delete the nodes from the\n     * allowed set.  The issue is when the door opens the allowed set is not\n     * re-calculated.  However this would not be an issue in most cases since hostile\n     * actors will enter combat (i.e. no longer wandering) and different pathfinding\n     * will kick in.\n     */\n    bool AiWander::execute (const MWWorld::Ptr& actor, CharacterController& /*characterController*/, AiState& state, float duration)\n    {\n        MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor);\n        if (cStats.isDead() || cStats.getHealth().getCurrent() <= 0)\n            return true; // Don't bother with dead actors\n\n        // get or create temporary storage\n        AiWanderStorage& storage = state.get<AiWanderStorage>();\n\n        mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600);\n\n        cStats.setDrawState(DrawState_Nothing);\n        cStats.setMovementFlag(CreatureStats::Flag_Run, false);\n\n        ESM::Position pos = actor.getRefData().getPosition();\n\n        // If there is already a destination due to the package having been interrupted by a combat or pursue package,\n        // rebuild a path to it\n        if (!mPathFinder.isPathConstructed() && mHasDestination)\n        {\n            if (mUsePathgrid)\n            {\n                mPathFinder.buildPathByPathgrid(pos.asVec3(), mDestination, actor.getCell(),\n                    getPathGridGraph(actor.getCell()));\n            }\n            else\n            {\n                const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);\n                mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(),\n                    getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor), getAreaCosts(actor));\n            }\n\n            if (mPathFinder.isPathConstructed())\n                storage.setState(AiWanderStorage::Wander_Walking);\n        }\n\n        if(!cStats.getMovementFlag(CreatureStats::Flag_ForceJump) && !cStats.getMovementFlag(CreatureStats::Flag_ForceSneak))\n        {\n            GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor);\n            if (greetingState == Greet_InProgress)\n            {\n                if (storage.mState == AiWanderStorage::Wander_Walking)\n                {\n                    stopMovement(actor);\n                    mObstacleCheck.clear();\n                    storage.setState(AiWanderStorage::Wander_IdleNow);\n                }\n            }\n        }\n\n        doPerFrameActionsForState(actor, duration, storage);\n\n        if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting)\n            return false;\n\n        return reactionTimeActions(actor, storage, pos);\n    }\n\n    bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos)\n    {\n        if (mDistance <= 0)\n            storage.mCanWanderAlongPathGrid = false;\n\n        if (isPackageCompleted())\n        {\n            stopWalking(actor);\n            // Reset package so it can be used again\n            mRemainingDuration=mDuration;\n            return true;\n        }\n\n        if (!mStoredInitialActorPosition)\n        {\n            mInitialActorPosition = actor.getRefData().getPosition().asVec3();\n            mStoredInitialActorPosition = true;\n        }\n\n        // Initialization to discover & store allowed node points for this actor.\n        if (storage.mPopulateAvailableNodes)\n        {\n            getAllowedNodes(actor, actor.getCell()->getCell(), storage);\n        }\n\n        if (canActorMoveByZAxis(actor) && mDistance > 0) {\n            // Typically want to idle for a short time before the next wander\n            if (Misc::Rng::rollDice(100) >= 92 && storage.mState != AiWanderStorage::Wander_Walking) {\n                wanderNearStart(actor, storage, mDistance);\n            }\n\n            storage.mCanWanderAlongPathGrid = false;\n        }\n        // If the package has a wander distance but no pathgrid is available,\n        // randomly idle or wander near spawn point\n        else if(storage.mAllowedNodes.empty() && mDistance > 0 && !storage.mIsWanderingManually) {\n            // Typically want to idle for a short time before the next wander\n            if (Misc::Rng::rollDice(100) >= 96) {\n                wanderNearStart(actor, storage, mDistance);\n            } else {\n                storage.setState(AiWanderStorage::Wander_IdleNow);\n            }\n        } else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) {\n            storage.mCanWanderAlongPathGrid = false;\n        }\n\n        // If Wandering manually and hit an obstacle, stop\n        if (storage.mIsWanderingManually && mObstacleCheck.isEvading()) {\n            completeManualWalking(actor, storage);\n        }\n\n        if (storage.mState == AiWanderStorage::Wander_MoveNow && storage.mCanWanderAlongPathGrid)\n        {\n            // Construct a new path if there isn't one\n            if(!mPathFinder.isPathConstructed())\n            {\n                if (!storage.mAllowedNodes.empty())\n                {\n                    setPathToAnAllowedNode(actor, storage, pos);\n                }\n            }\n        }\n        else if (storage.mIsWanderingManually && mPathFinder.checkPathCompleted())\n        {\n            completeManualWalking(actor, storage);\n        }\n\n        if (storage.mIsWanderingManually\n            && storage.mState == AiWanderStorage::Wander_Walking\n            && (mPathFinder.getPathSize() == 0\n                || isDestinationHidden(actor, mPathFinder.getPath().back())\n                || isAreaOccupiedByOtherActor(actor, mPathFinder.getPath().back())))\n            completeManualWalking(actor, storage);\n\n        return false; // AiWander package not yet completed\n    }\n\n    osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const\n    {\n        if (mHasDestination)\n            return mDestination;\n\n        return actor.getRefData().getPosition().asVec3();\n    }\n\n    bool AiWander::isPackageCompleted() const\n    {\n        // End package if duration is complete\n        return mDuration && mRemainingDuration <= 0;\n    }\n\n    /*\n     * Commands actor to walk to a random location near original spawn location.\n     */\n    void AiWander::wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance) {\n        const auto currentPosition = actor.getRefData().getPosition().asVec3();\n\n        std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here\n        const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);\n        const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);\n        const auto world = MWBase::Environment::get().getWorld();\n        const auto halfExtents = world->getPathfindingHalfExtents(actor);\n        const auto navigator = world->getNavigator();\n        const auto navigatorFlags = getNavigatorFlags(actor);\n        const auto areaCosts = getAreaCosts(actor);\n\n        do {\n            // Determine a random location within radius of original position\n            const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance;\n            if (!isWaterCreature && !isFlyingCreature)\n            {\n                // findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance\n                if (const auto destination = navigator->findRandomPointAroundCircle(halfExtents, mInitialActorPosition, wanderDistance, navigatorFlags))\n                    mDestination = *destination;\n                else\n                    mDestination = getRandomPointAround(mInitialActorPosition, wanderRadius);\n            }\n            else\n                mDestination = getRandomPointAround(mInitialActorPosition, wanderRadius);\n\n            // Check if land creature will walk onto water or if water creature will swim onto land\n            if (!isWaterCreature && destinationIsAtWater(actor, mDestination))\n                continue;\n\n            if (isDestinationHidden(actor, mDestination))\n                continue;\n\n            if (isAreaOccupiedByOtherActor(actor, mDestination))\n                continue;\n\n            if (isWaterCreature || isFlyingCreature)\n                mPathFinder.buildStraightPath(mDestination);\n            else\n                mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags,\n                                               areaCosts);\n\n            if (mPathFinder.isPathConstructed())\n            {\n                storage.setState(AiWanderStorage::Wander_Walking, true);\n                mHasDestination = true;\n                mUsePathgrid = false;\n            }\n\n            break;\n        } while (--attempts);\n    }\n\n    /*\n     * Returns true if the position provided is above water.\n     */\n    bool AiWander::destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination) {\n        float heightToGroundOrWater = MWBase::Environment::get().getWorld()->getDistToNearestRayHit(destination, osg::Vec3f(0,0,-1), 1000.0, true);\n        osg::Vec3f positionBelowSurface = destination;\n        positionBelowSurface[2] = positionBelowSurface[2] - heightToGroundOrWater - 1.0f;\n        return MWBase::Environment::get().getWorld()->isUnderwater(actor.getCell(), positionBelowSurface);\n    }\n\n    void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {\n        stopWalking(actor);\n        mObstacleCheck.clear();\n        storage.setState(AiWanderStorage::Wander_IdleNow);\n    }\n\n    void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)\n    {\n        switch (storage.mState)\n        {\n            case AiWanderStorage::Wander_IdleNow:\n                onIdleStatePerFrameActions(actor, duration, storage);\n                break;\n\n            case AiWanderStorage::Wander_Walking:\n                onWalkingStatePerFrameActions(actor, duration, storage);\n                break;\n\n            case AiWanderStorage::Wander_ChooseAction:\n                onChooseActionStatePerFrameActions(actor, storage);\n                break;\n\n            case AiWanderStorage::Wander_MoveNow:\n                break;  // nothing to do\n\n            default:\n                // should never get here\n                assert(false);\n                break;\n        }\n    }\n\n    void AiWander::onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)\n    {\n        // Check if an idle actor is too far from all allowed nodes or too close to a door - if so start walking.\n        storage.mCheckIdlePositionTimer += duration;\n\n        if (storage.mCheckIdlePositionTimer >= IDLE_POSITION_CHECK_INTERVAL && !isStationary())\n        {\n            storage.mCheckIdlePositionTimer = 0;    // restart timer\n            static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 1.6f;\n            if (proximityToDoor(actor, distance) || !isNearAllowedNode(actor, storage, distance))\n            {\n                storage.setState(AiWanderStorage::Wander_MoveNow);\n                storage.mTrimCurrentNode = false; // just in case\n                return;\n            }\n        }\n\n        // Check if idle animation finished\n        GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor);\n        if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))\n        {\n            if (mPathFinder.isPathConstructed())\n                storage.setState(AiWanderStorage::Wander_Walking);\n            else\n                storage.setState(AiWanderStorage::Wander_ChooseAction);\n        }\n    }\n\n    bool AiWander::isNearAllowedNode(const MWWorld::Ptr& actor, const AiWanderStorage& storage, float distance) const\n    {\n        const osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();\n        auto cell = actor.getCell()->getCell();\n        for (const ESM::Pathgrid::Point& node : storage.mAllowedNodes)\n        {\n            osg::Vec3f point(node.mX, node.mY, node.mZ);\n            Misc::CoordinateConverter(cell).toWorld(point);\n            if ((actorPos - point).length2() < distance * distance)\n                return true;\n        }\n        return false;\n    }\n\n    void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)\n    {\n        // Is there no destination or are we there yet?\n        if ((!mPathFinder.isPathConstructed()) || pathTo(actor, osg::Vec3f(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE))\n        {\n            stopWalking(actor);\n            storage.setState(AiWanderStorage::Wander_ChooseAction);\n        }\n        else\n        {\n            // have not yet reached the destination\n            evadeObstacles(actor, storage);\n        }\n    }\n\n    void AiWander::onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage)\n    {\n        // Wait while fully stop before starting idle animation (important if \"smooth movement\" is enabled).\n        if (actor.getClass().getCurrentSpeed(actor) > 0)\n            return;\n\n        unsigned short idleAnimation = getRandomIdle();\n        storage.mIdleAnimation = idleAnimation;\n\n        if (!idleAnimation && mDistance)\n        {\n            storage.setState(AiWanderStorage::Wander_MoveNow);\n            return;\n        }\n        if(idleAnimation)\n        {\n            if(std::find(storage.mBadIdles.begin(), storage.mBadIdles.end(), idleAnimation)==storage.mBadIdles.end())\n            {\n                if(!playIdle(actor, idleAnimation))\n                {\n                    storage.mBadIdles.push_back(idleAnimation);\n                    storage.setState(AiWanderStorage::Wander_ChooseAction);\n                    return;\n                }\n            }\n        }\n\n        storage.setState(AiWanderStorage::Wander_IdleNow);\n    }\n\n    void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage)\n    {\n        if (mUsePathgrid)\n        {\n            const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);\n            mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor),\n                                                      getAreaCosts(actor));\n        }\n\n        if (mObstacleCheck.isEvading())\n        {\n            // first check if we're walking into a door\n            static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();\n            if (proximityToDoor(actor, distance))\n            {\n                // remove allowed points then select another random destination\n                storage.mTrimCurrentNode = true;\n                trimAllowedNodes(storage.mAllowedNodes, mPathFinder);\n                mObstacleCheck.clear();\n                stopWalking(actor);\n                storage.setState(AiWanderStorage::Wander_MoveNow);\n            }\n\n           storage.mStuckCount++;  // TODO: maybe no longer needed\n        }\n\n        // if stuck for sufficiently long, act like current location was the destination\n        if (storage.mStuckCount >= getCountBeforeReset(actor)) // something has gone wrong, reset\n        {\n            mObstacleCheck.clear();\n            stopWalking(actor);\n            storage.setState(AiWanderStorage::Wander_ChooseAction);\n            storage.mStuckCount = 0;\n        }\n    }\n\n\n\n    void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)\n    {\n        unsigned int randNode = Misc::Rng::rollDice(storage.mAllowedNodes.size());\n        ESM::Pathgrid::Point dest(storage.mAllowedNodes[randNode]);\n\n        ToWorldCoordinates(dest, actor.getCell()->getCell());\n\n        // actor position is already in world coordinates\n        const osg::Vec3f start = actorPos.asVec3();\n\n        // don't take shortcuts for wandering\n        const osg::Vec3f destVec3f = PathFinder::makeOsgVec3(dest);\n        mPathFinder.buildPathByPathgrid(start, destVec3f, actor.getCell(), getPathGridGraph(actor.getCell()));\n\n        if (mPathFinder.isPathConstructed())\n        {\n            mDestination = destVec3f;\n            mHasDestination = true;\n            mUsePathgrid = true;\n            // Remove this node as an option and add back the previously used node (stops NPC from picking the same node):\n            ESM::Pathgrid::Point temp = storage.mAllowedNodes[randNode];\n            storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + randNode);\n            // check if mCurrentNode was taken out of mAllowedNodes\n            if (storage.mTrimCurrentNode && storage.mAllowedNodes.size() > 1)\n                storage.mTrimCurrentNode = false;\n            else\n                storage.mAllowedNodes.push_back(storage.mCurrentNode);\n            storage.mCurrentNode = temp;\n\n            storage.setState(AiWanderStorage::Wander_Walking);\n        }\n        // Choose a different node and delete this one from possible nodes because it is uncreachable:\n        else\n            storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + randNode);\n    }\n\n    void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell)\n    {\n        Misc::CoordinateConverter(cell).toWorld(point);\n    }\n\n    void AiWander::trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes,\n                                    const PathFinder& pathfinder)\n    {\n        // TODO: how to add these back in once the door opens?\n        // Idea: keep a list of detected closed doors (see aicombat.cpp)\n        // Every now and then check whether one of the doors is opened. (maybe\n        // at the end of playing idle?) If the door is opened then re-calculate\n        // allowed nodes starting from the spawn point.\n        auto paths = pathfinder.getPath();\n        while(paths.size() >= 2)\n        {\n            const auto pt = paths.back();\n            for(unsigned int j = 0; j < nodes.size(); j++)\n            {\n                // FIXME: doesn't handle a door with the same X/Y\n                //        coordinates but with a different Z\n                if (std::abs(nodes[j].mX - pt.x()) <= 0.5 && std::abs(nodes[j].mY - pt.y()) <= 0.5)\n                {\n                    nodes.erase(nodes.begin() + j);\n                    break;\n                }\n            }\n            paths.pop_back();\n        }\n    }\n\n    void AiWander::stopWalking(const MWWorld::Ptr& actor)\n    {\n        mPathFinder.clearPath();\n        mHasDestination = false;\n        stopMovement(actor);\n    }\n\n    bool AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)\n    {\n        if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle))\n        {\n            const std::string& groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle];\n            return MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, groupName, 0, 1);\n        }\n        else\n        {\n            Log(Debug::Verbose) << \"Attempted to play out of range idle animation \\\"\" << idleSelect << \"\\\" for \" << actor.getCellRef().getRefId();\n            return false;\n        }\n    }\n\n    bool AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)\n    {\n        if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle))\n        {\n            const std::string& groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle];\n            return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, groupName);\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n    short unsigned AiWander::getRandomIdle()\n    {\n        unsigned short idleRoll = 0;\n        short unsigned selectedAnimation = 0;\n\n        for(unsigned int counter = 0; counter < mIdle.size(); counter++)\n        {\n            static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore()\n                .get<ESM::GameSetting>().find(\"fIdleChanceMultiplier\")->mValue.getFloat();\n\n            unsigned short idleChance = static_cast<unsigned short>(fIdleChanceMultiplier * mIdle[counter]);\n            unsigned short randSelect = (int)(Misc::Rng::rollProbability() * int(100 / fIdleChanceMultiplier));\n            if(randSelect < idleChance && randSelect > idleRoll)\n            {\n                selectedAnimation = counter + GroupIndex_MinIdle;\n                idleRoll = randSelect;\n            }\n        }\n        return selectedAnimation;\n    }\n\n    void AiWander::fastForward(const MWWorld::Ptr& actor, AiState &state)\n    {\n        // Update duration counter\n        mRemainingDuration--;\n        if (mDistance == 0)\n            return;\n\n        AiWanderStorage& storage = state.get<AiWanderStorage>();\n        if (storage.mPopulateAvailableNodes)\n            getAllowedNodes(actor, actor.getCell()->getCell(), storage);\n\n        if (storage.mAllowedNodes.empty())\n            return;\n\n        int index = Misc::Rng::rollDice(storage.mAllowedNodes.size());\n        ESM::Pathgrid::Point dest = storage.mAllowedNodes[index];\n        ESM::Pathgrid::Point worldDest = dest;\n        ToWorldCoordinates(worldDest, actor.getCell()->getCell());\n\n        bool isPathGridOccupied = MWBase::Environment::get().getMechanicsManager()->isAnyActorInRange(PathFinder::makeOsgVec3(worldDest), 60);\n\n        // add offset only if the selected pathgrid is occupied by another actor\n        if (isPathGridOccupied)\n        {\n            ESM::Pathgrid::PointList points;\n            getNeighbouringNodes(dest, actor.getCell(), points);\n\n            // there are no neighbouring nodes, nowhere to move\n            if (points.empty())\n                return;\n\n            int initialSize = points.size();\n            bool isOccupied = false;\n            // AI will try to move the NPC towards every neighboring node until suitable place will be found\n            for (int i = 0; i < initialSize; i++)\n            {\n                int randomIndex = Misc::Rng::rollDice(points.size());\n                ESM::Pathgrid::Point connDest = points[randomIndex];\n\n                // add an offset towards random neighboring node\n                osg::Vec3f dir = PathFinder::makeOsgVec3(connDest) - PathFinder::makeOsgVec3(dest);\n                float length = dir.length();\n                dir.normalize();\n\n                for (int j = 1; j <= 3; j++)\n                {\n                    // move for 5-15% towards random neighboring node\n                    dest = PathFinder::makePathgridPoint(PathFinder::makeOsgVec3(dest) + dir * (j * 5 * length / 100.f));\n                    worldDest = dest;\n                    ToWorldCoordinates(worldDest, actor.getCell()->getCell());\n\n                    isOccupied = MWBase::Environment::get().getMechanicsManager()->isAnyActorInRange(PathFinder::makeOsgVec3(worldDest), 60);\n\n                    if (!isOccupied)\n                        break;\n                }\n\n                if (!isOccupied)\n                    break;\n\n                // Will try an another neighboring node\n                points.erase(points.begin()+randomIndex);\n            }\n\n            // there is no free space, nowhere to move\n            if (isOccupied)\n                return;\n        }\n\n        // place above to prevent moving inside objects, e.g. stairs, because a vector between pathgrids can be underground.\n        // Adding 20 in adjustPosition() is not enough.\n        dest.mZ += 60;\n\n        ToWorldCoordinates(dest, actor.getCell()->getCell());\n\n        state.moveIn(new AiWanderStorage());\n\n        MWBase::Environment::get().getWorld()->moveObject(actor, static_cast<float>(dest.mX),\n            static_cast<float>(dest.mY), static_cast<float>(dest.mZ));\n        actor.getClass().adjustPosition(actor, false);\n    }\n\n    void AiWander::getNeighbouringNodes(ESM::Pathgrid::Point dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points)\n    {\n        const ESM::Pathgrid *pathgrid =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*currentCell->getCell());\n\n        if (pathgrid == nullptr || pathgrid->mPoints.empty())\n            return;\n\n        int index = PathFinder::getClosestPoint(pathgrid, PathFinder::makeOsgVec3(dest));\n\n        getPathGridGraph(currentCell).getNeighbouringPoints(index, points);\n    }\n\n    void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell, AiWanderStorage& storage)\n    {\n        // infrequently used, therefore no benefit in caching it as a member\n        const ESM::Pathgrid *\n            pathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell);\n        const MWWorld::CellStore* cellStore = actor.getCell();\n\n        storage.mAllowedNodes.clear();\n\n        // If there is no path this actor doesn't go anywhere. See:\n        // https://forum.openmw.org/viewtopic.php?t=1556\n        // http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833\n        // Note: In order to wander, need at least two points.\n        if(!pathgrid || (pathgrid->mPoints.size() < 2))\n            storage.mCanWanderAlongPathGrid = false;\n\n        // A distance value passed into the constructor indicates how far the\n        // actor can  wander from the spawn position.  AiWander assumes that\n        // pathgrid points are available, and uses them to randomly select wander\n        // destinations within the allowed set of pathgrid points (nodes).\n        // ... pathgrids don't usually include water, so swimmers ignore them\n        if (mDistance && storage.mCanWanderAlongPathGrid && !actor.getClass().isPureWaterCreature(actor))\n        {\n            // get NPC's position in local (i.e. cell) coordinates\n            osg::Vec3f npcPos(mInitialActorPosition);\n            Misc::CoordinateConverter(cell).toLocal(npcPos);\n\n            // Find closest pathgrid point\n            int closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos);\n\n            // mAllowedNodes for this actor with pathgrid point indexes based on mDistance\n            // and if the point is connected to the closest current point\n            // NOTE: mPoints and mAllowedNodes are in local coordinates\n            int pointIndex = 0;\n            for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)\n            {\n                osg::Vec3f nodePos(PathFinder::makeOsgVec3(pathgrid->mPoints[counter]));\n                if((npcPos - nodePos).length2() <= mDistance * mDistance &&\n                   getPathGridGraph(cellStore).isPointConnected(closestPointIndex, counter))\n                {\n                    storage.mAllowedNodes.push_back(pathgrid->mPoints[counter]);\n                    pointIndex = counter;\n                }\n            }\n            if (storage.mAllowedNodes.size() == 1)\n            {\n                AddNonPathGridAllowedPoints(npcPos, pathgrid, pointIndex, storage);\n            }\n            if(!storage.mAllowedNodes.empty())\n            {\n                SetCurrentNodeToClosestAllowedNode(npcPos, storage);\n            }\n        }\n\n        storage.mPopulateAvailableNodes = false;\n    }\n\n    // When only one path grid point in wander distance,\n    // additional points for NPC to wander to are:\n    // 1. NPC's initial location\n    // 2. Partway along the path between the point and its connected points.\n    void AiWander::AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex, AiWanderStorage& storage)\n    {\n        storage.mAllowedNodes.push_back(PathFinder::makePathgridPoint(npcPos));\n        for (auto& edge : pathGrid->mEdges)\n        {\n            if (edge.mV0 == pointIndex)\n            {\n                AddPointBetweenPathGridPoints(pathGrid->mPoints[edge.mV0], pathGrid->mPoints[edge.mV1], storage);\n            }\n        }\n    }\n\n    void AiWander::AddPointBetweenPathGridPoints(const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage)\n    {\n        osg::Vec3f vectorStart = PathFinder::makeOsgVec3(start);\n        osg::Vec3f delta = PathFinder::makeOsgVec3(end) - vectorStart;\n        float length = delta.length();\n        delta.normalize();\n\n        int distance = std::max(mDistance / 2, MINIMUM_WANDER_DISTANCE);\n\n        // must not travel longer than distance between waypoints or NPC goes past waypoint\n        distance = std::min(distance, static_cast<int>(length));\n        delta *= distance;\n        storage.mAllowedNodes.push_back(PathFinder::makePathgridPoint(vectorStart + delta));\n    }\n\n    void AiWander::SetCurrentNodeToClosestAllowedNode(const osg::Vec3f& npcPos, AiWanderStorage& storage)\n    {\n        float distanceToClosestNode = std::numeric_limits<float>::max();\n        unsigned int index = 0;\n        for (unsigned int counterThree = 0; counterThree < storage.mAllowedNodes.size(); counterThree++)\n        {\n            osg::Vec3f nodePos(PathFinder::makeOsgVec3(storage.mAllowedNodes[counterThree]));\n            float tempDist = (npcPos - nodePos).length2();\n            if (tempDist < distanceToClosestNode)\n            {\n                index = counterThree;\n                distanceToClosestNode = tempDist;\n            }\n        }\n        storage.mCurrentNode = storage.mAllowedNodes[index];\n        storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + index);\n    }\n\n    void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const\n    {\n        float remainingDuration;\n        if (mRemainingDuration > 0 && mRemainingDuration < 24)\n            remainingDuration = mRemainingDuration;\n        else\n            remainingDuration = mDuration;\n\n        std::unique_ptr<ESM::AiSequence::AiWander> wander(new ESM::AiSequence::AiWander());\n        wander->mData.mDistance = mDistance;\n        wander->mData.mDuration = mDuration;\n        wander->mData.mTimeOfDay = mTimeOfDay;\n        wander->mDurationData.mRemainingDuration = remainingDuration;\n        assert (mIdle.size() == 8);\n        for (int i=0; i<8; ++i)\n            wander->mData.mIdle[i] = mIdle[i];\n        wander->mData.mShouldRepeat = mOptions.mRepeat;\n        wander->mStoredInitialActorPosition = mStoredInitialActorPosition;\n        if (mStoredInitialActorPosition)\n            wander->mInitialActorPosition = mInitialActorPosition;\n\n        ESM::AiSequence::AiPackageContainer package;\n        package.mType = ESM::AiSequence::Ai_Wander;\n        package.mPackage = wander.release();\n        sequence.mPackages.push_back(package);\n    }\n\n    AiWander::AiWander (const ESM::AiSequence::AiWander* wander)\n        : TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(wander->mData.mShouldRepeat != 0))\n        , mDistance(std::max(static_cast<short>(0), wander->mData.mDistance))\n        , mDuration(std::max(static_cast<short>(0), wander->mData.mDuration))\n        , mRemainingDuration(wander->mDurationData.mRemainingDuration)\n        , mTimeOfDay(wander->mData.mTimeOfDay)\n        , mIdle(getInitialIdle(wander->mData.mIdle))\n        , mStoredInitialActorPosition(wander->mStoredInitialActorPosition)\n        , mHasDestination(false)\n        , mDestination(osg::Vec3f(0, 0, 0))\n        , mUsePathgrid(false)\n    {\n        if (mStoredInitialActorPosition)\n            mInitialActorPosition = wander->mInitialActorPosition;\n        if (mRemainingDuration <= 0 || mRemainingDuration >= 24)\n            mRemainingDuration = mDuration;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/aiwander.hpp",
    "content": "#ifndef GAME_MWMECHANICS_AIWANDER_H\n#define GAME_MWMECHANICS_AIWANDER_H\n\n#include \"typedaipackage.hpp\"\n\n#include <vector>\n\n#include \"pathfinding.hpp\"\n#include \"obstacle.hpp\"\n#include \"aistate.hpp\"\n#include \"aitimer.hpp\"\n\nnamespace ESM\n{\n    struct Cell;\n    namespace AiSequence\n    {\n        struct AiWander;\n    }\n}\n\nnamespace MWMechanics\n{\n    /// \\brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.\n    struct AiWanderStorage : AiTemporaryBase\n    {\n        AiReactionTimer mReaction;\n\n        // AiWander states\n        enum WanderState\n        {\n            Wander_ChooseAction,\n            Wander_IdleNow,\n            Wander_MoveNow,\n            Wander_Walking\n        };\n        WanderState mState;\n\n        bool mIsWanderingManually;\n        bool mCanWanderAlongPathGrid;\n\n        unsigned short mIdleAnimation;\n        std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors\n\n        // do we need to calculate allowed nodes based on mDistance\n        bool mPopulateAvailableNodes;\n\n        // allowed pathgrid nodes based on mDistance from the spawn point\n        // in local coordinates of mCell\n        std::vector<ESM::Pathgrid::Point> mAllowedNodes;\n\n        ESM::Pathgrid::Point mCurrentNode;\n        bool mTrimCurrentNode;\n\n        float mCheckIdlePositionTimer;\n        int mStuckCount;\n\n        AiWanderStorage():\n            mState(Wander_ChooseAction),\n            mIsWanderingManually(false),\n            mCanWanderAlongPathGrid(true),\n            mIdleAnimation(0),\n            mBadIdles(),\n            mPopulateAvailableNodes(true),\n            mAllowedNodes(),\n            mTrimCurrentNode(false),\n            mCheckIdlePositionTimer(0),\n            mStuckCount(0)\n            {};\n\n        void setState(const WanderState wanderState, const bool isManualWander = false)\n        {\n            mState = wanderState;\n            mIsWanderingManually = isManualWander;\n        }\n    };\n\n    /// \\brief Causes the Actor to wander within a specified range\n    class AiWander final : public TypedAiPackage<AiWander>\n    {\n        public:\n            /// Constructor\n            /** \\param distance Max distance the ACtor will wander\n                \\param duration Time, in hours, that this package will be preformed\n                \\param timeOfDay Currently unimplemented. Not functional in the original engine.\n                \\param idle Chances of each idle to play (9 in total)\n                \\param repeat Repeat wander or not **/\n            AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat);\n\n            explicit AiWander (const ESM::AiSequence::AiWander* wander);\n\n            bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override;\n\n            static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Wander; }\n\n            static constexpr Options makeDefaultOptions()\n            {\n                AiPackage::Options options;\n                options.mUseVariableSpeed = true;\n                return options;\n            }\n\n            void writeState(ESM::AiSequence::AiSequence &sequence) const override;\n\n            void fastForward(const MWWorld::Ptr& actor, AiState& state) override;\n\n            osg::Vec3f getDestination(const MWWorld::Ptr& actor) const override;\n\n            osg::Vec3f getDestination() const override\n            {\n                if (!mHasDestination)\n                    return osg::Vec3f(0, 0, 0);\n\n                return mDestination;\n            }\n\n            bool isStationary() const { return mDistance == 0; }\n\n        private:\n            void stopWalking(const MWWorld::Ptr& actor);\n\n            /// Have the given actor play an idle animation\n            /// @return Success or error\n            bool playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);\n            bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);\n            short unsigned getRandomIdle();\n            void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);\n            void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage);\n            void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);\n            void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);\n            void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);\n            void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage);\n            bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos);\n            inline bool isPackageCompleted() const;\n            void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);\n            bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);\n            void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);\n            bool isNearAllowedNode(const MWWorld::Ptr &actor, const AiWanderStorage& storage, float distance) const;\n\n            const int mDistance; // how far the actor can wander from the spawn point\n            const int mDuration;\n            float mRemainingDuration;\n            const int mTimeOfDay;\n            const std::vector<unsigned char> mIdle;\n\n            bool mStoredInitialActorPosition;\n            osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell\n\n            bool mHasDestination;\n            osg::Vec3f mDestination;\n            bool mUsePathgrid;\n\n            void getNeighbouringNodes(ESM::Pathgrid::Point dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points);\n\n            void getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell, AiWanderStorage& storage);\n\n            void trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes, const PathFinder& pathfinder);\n\n            // constants for converting idleSelect values into groupNames\n            enum GroupIndex\n            {\n                GroupIndex_MinIdle = 2,\n                GroupIndex_MaxIdle = 9\n            };\n\n            /// convert point from local (i.e. cell) to world coordinates\n            void ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell);\n\n            void SetCurrentNodeToClosestAllowedNode(const osg::Vec3f& npcPos, AiWanderStorage& storage);\n\n            void AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex, AiWanderStorage& storage);\n\n            void AddPointBetweenPathGridPoints(const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage);\n\n            /// lookup table for converting idleSelect value to groupName\n            static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];\n\n            static int OffsetToPreventOvercrowding();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/alchemy.cpp",
    "content": "#include \"alchemy.hpp\"\n\n#include <cassert>\n#include <cmath>\n\n#include <algorithm>\n#include <stdexcept>\n#include <map>\n\n#include <components/misc/rng.hpp>\n\n#include <components/esm/loadskil.hpp>\n#include <components/esm/loadappa.hpp>\n#include <components/esm/loadgmst.hpp>\n#include <components/esm/loadmgef.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/Worldstate.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"magiceffects.hpp\"\n#include \"creaturestats.hpp\"\n\nMWMechanics::Alchemy::Alchemy()\n    : mValue(0)\n    , mPotionName(\"\")\n{\n}\n\nstd::set<MWMechanics::EffectKey> MWMechanics::Alchemy::listEffects() const\n{\n    std::map<EffectKey, int> effects;\n\n    for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)\n    {\n        if (!iter->isEmpty())\n        {\n            const MWWorld::LiveCellRef<ESM::Ingredient> *ingredient = iter->get<ESM::Ingredient>();\n\n            std::set<EffectKey> seenEffects;\n\n            for (int i=0; i<4; ++i)\n                if (ingredient->mBase->mData.mEffectID[i]!=-1)\n                {\n                    EffectKey key (\n                        ingredient->mBase->mData.mEffectID[i], ingredient->mBase->mData.mSkills[i]!=-1 ?\n                        ingredient->mBase->mData.mSkills[i] : ingredient->mBase->mData.mAttributes[i]);\n\n                    if (seenEffects.insert(key).second)\n                        ++effects[key];\n                }\n        }\n    }\n\n    std::set<EffectKey> effects2;\n\n    for (std::map<EffectKey, int>::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter)\n        if (iter->second>1)\n            effects2.insert (iter->first);\n\n    return effects2;\n}\n\nvoid MWMechanics::Alchemy::applyTools (int flags, float& value) const\n{\n    bool magnitude = !(flags & ESM::MagicEffect::NoMagnitude);\n    bool duration = !(flags & ESM::MagicEffect::NoDuration);\n    bool negative = (flags & ESM::MagicEffect::Harmful) != 0;\n\n    int tool = negative ? ESM::Apparatus::Alembic : ESM::Apparatus::Retort;\n\n    int setup = 0;\n\n    if (!mTools[tool].isEmpty() && !mTools[ESM::Apparatus::Calcinator].isEmpty())\n        setup = 1;\n    else if (!mTools[tool].isEmpty())\n        setup = 2;\n    else if (!mTools[ESM::Apparatus::Calcinator].isEmpty())\n        setup = 3;\n    else\n        return;\n\n    float toolQuality = setup==1 || setup==2 ? mTools[tool].get<ESM::Apparatus>()->mBase->mData.mQuality : 0;\n    float calcinatorQuality = setup==1 || setup==3 ?\n        mTools[ESM::Apparatus::Calcinator].get<ESM::Apparatus>()->mBase->mData.mQuality : 0;\n\n    float quality = 1;\n\n    switch (setup)\n    {\n        case 1:\n\n            quality = negative ? 2 * toolQuality + 3 * calcinatorQuality :\n                (magnitude && duration ?\n                2 * toolQuality + calcinatorQuality : 2/3.0f * (toolQuality + calcinatorQuality) + 0.5f);\n            break;\n\n        case 2:\n\n            quality = negative ? 1+toolQuality : (magnitude && duration ? toolQuality : toolQuality + 0.5f);\n            break;\n\n        case 3:\n\n            quality = magnitude && duration ? calcinatorQuality : calcinatorQuality + 0.5f;\n            break;\n    }\n\n    if (setup==3 || !negative)\n    {\n        value += quality;\n    }\n    else\n    {\n        if (quality==0)\n            throw std::runtime_error (\"invalid derived alchemy apparatus quality\");\n\n        value /= quality;\n    }\n}\n\nvoid MWMechanics::Alchemy::updateEffects()\n{\n    mEffects.clear();\n    mValue = 0;\n\n    if (countIngredients()<2 || mAlchemist.isEmpty() || mTools[ESM::Apparatus::MortarPestle].isEmpty())\n        return;\n\n    // find effects\n    std::set<EffectKey> effects (listEffects());\n\n    // general alchemy factor\n    float x = getAlchemyFactor();\n\n    x *= mTools[ESM::Apparatus::MortarPestle].get<ESM::Apparatus>()->mBase->mData.mQuality;\n    x *= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find (\"fPotionStrengthMult\")->mValue.getFloat();\n\n    // value\n    mValue = static_cast<int> (\n        x * MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find (\"iAlchemyMod\")->mValue.getFloat());\n\n    // build quantified effect list\n    for (std::set<EffectKey>::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter)\n    {\n        const ESM::MagicEffect *magicEffect =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (iter->mId);\n\n        if (magicEffect->mData.mBaseCost<=0)\n        {\n            const std::string os = \"invalid base cost for magic effect \" + std::to_string(iter->mId);\n            throw std::runtime_error (os);\n        }\n\n        float fPotionT1MagMul =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find (\"fPotionT1MagMult\")->mValue.getFloat();\n\n        if (fPotionT1MagMul<=0)\n            throw std::runtime_error (\"invalid gmst: fPotionT1MagMul\");\n\n        float fPotionT1DurMult =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find (\"fPotionT1DurMult\")->mValue.getFloat();\n\n        if (fPotionT1DurMult<=0)\n            throw std::runtime_error (\"invalid gmst: fPotionT1DurMult\");\n\n        float magnitude = (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) ?\n            1.0f : (x / fPotionT1MagMul) / magicEffect->mData.mBaseCost;\n        float duration = (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) ?\n            1.0f : (x / fPotionT1DurMult) / magicEffect->mData.mBaseCost;\n\n        if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))\n            applyTools (magicEffect->mData.mFlags, magnitude);\n\n        if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))\n            applyTools (magicEffect->mData.mFlags, duration);\n\n        duration = roundf(duration);\n        magnitude = roundf(magnitude);\n\n        if (magnitude>0 && duration>0)\n        {\n            ESM::ENAMstruct effect;\n            effect.mEffectID = iter->mId;\n\n            effect.mAttribute = -1;\n            effect.mSkill = -1;\n\n            if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)\n                effect.mSkill = iter->mArg;\n            else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)\n                effect.mAttribute = iter->mArg;\n\n            effect.mRange = 0;\n            effect.mArea = 0;\n\n            effect.mDuration = static_cast<int>(duration);\n            effect.mMagnMin = effect.mMagnMax = static_cast<int>(magnitude);\n\n            mEffects.push_back (effect);\n        }\n    }\n}\n\nconst ESM::Potion *MWMechanics::Alchemy::getRecord(const ESM::Potion& toFind) const\n{\n    const MWWorld::Store<ESM::Potion> &potions =\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>();\n\n    MWWorld::Store<ESM::Potion>::iterator iter = potions.begin();\n    for (; iter != potions.end(); ++iter)\n    {\n        if (iter->mEffects.mList.size() != mEffects.size())\n            continue;\n\n        if (iter->mName != toFind.mName\n                || iter->mScript != toFind.mScript\n                || iter->mData.mWeight != toFind.mData.mWeight\n                || iter->mData.mValue != toFind.mData.mValue\n                || iter->mData.mAutoCalc != toFind.mData.mAutoCalc)\n            continue;\n\n        // Don't choose an ID that came from the content files, would have unintended side effects\n        // where alchemy can be used to produce quest-relevant items\n        if (!potions.isDynamic(iter->mId))\n            continue;\n\n        bool mismatch = false;\n\n        for (int i=0; i<static_cast<int> (iter->mEffects.mList.size()); ++i)\n        {\n            const ESM::ENAMstruct& first = iter->mEffects.mList[i];\n            const ESM::ENAMstruct& second = mEffects[i];\n\n            if (first.mEffectID!=second.mEffectID ||\n                first.mArea!=second.mArea ||\n                first.mRange!=second.mRange ||\n                first.mSkill!=second.mSkill ||\n                first.mAttribute!=second.mAttribute ||\n                first.mMagnMin!=second.mMagnMin ||\n                first.mMagnMax!=second.mMagnMax ||\n                first.mDuration!=second.mDuration)\n            {\n                mismatch = true;\n                break;\n            }\n        }\n\n        if (!mismatch)\n            return &(*iter);\n    }\n\n    return nullptr;\n}\n\nvoid MWMechanics::Alchemy::removeIngredients()\n{\n    for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)\n        if (!iter->isEmpty())\n        {\n            iter->getContainerStore()->remove(*iter, 1, mAlchemist);\n\n            /*\n                Start of tes3mp addition\n\n                Store this item removal for later sending to avoid packet spam\n            */\n            mwmp::Main::get().getLocalPlayer()->storeItemRemoval(iter->getCellRef().getRefId(), 1);\n            /*\n                End of tes3mp addition\n            */\n\n            if (iter->getRefData().getCount()<1)\n                *iter = MWWorld::Ptr();\n        }\n\n    updateEffects();\n}\n\nvoid MWMechanics::Alchemy::addPotion (const std::string& name)\n{\n    ESM::Potion newRecord;\n\n    newRecord.mData.mWeight = 0;\n\n    for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)\n        if (!iter->isEmpty())\n            newRecord.mData.mWeight += iter->get<ESM::Ingredient>()->mBase->mData.mWeight;\n\n    if (countIngredients() > 0)\n        newRecord.mData.mWeight /= countIngredients();\n\n    newRecord.mData.mValue = mValue;\n    newRecord.mData.mAutoCalc = 0;\n\n    newRecord.mName = name;\n\n    int index = Misc::Rng::rollDice(6);\n    assert (index>=0 && index<6);\n\n    static const char *meshes[] = { \"standard\", \"bargain\", \"cheap\", \"fresh\", \"exclusive\", \"quality\" };\n\n    newRecord.mModel = \"m\\\\misc_potion_\" + std::string (meshes[index]) + \"_01.nif\";\n    newRecord.mIcon = \"m\\\\tx_potion_\" + std::string (meshes[index]) + \"_01.dds\";\n\n    newRecord.mEffects.mList = mEffects;\n\n    /*\n        Start of tes3mp change (major)\n\n        Don't create a record and don't add the potion to the player's inventory;\n        instead just store its record in preparation for sending it to the server\n        and expect the server to add it to the player's inventory\n    */\n    /*\n    const ESM::Potion* record = getRecord(newRecord);\n    if (!record)\n    {\n        record = MWBase::Environment::get().getWorld()->createRecord(newRecord);\n    }\n\n    mAlchemist.getClass().getContainerStore (mAlchemist).add (record->mId, 1, mAlchemist);\n    */\n    mStoredPotion = newRecord;\n    /*\n        End of tes3mp change (major)\n    */\n}\n\nvoid MWMechanics::Alchemy::increaseSkill()\n{\n    mAlchemist.getClass().skillUsageSucceeded (mAlchemist, ESM::Skill::Alchemy, 0);\n}\n\nfloat MWMechanics::Alchemy::getAlchemyFactor() const\n{\n    const CreatureStats& creatureStats = mAlchemist.getClass().getCreatureStats (mAlchemist);\n\n    return\n        (mAlchemist.getClass().getSkill(mAlchemist, ESM::Skill::Alchemy) +\n        0.1f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()\n        + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified());\n}\n\nint MWMechanics::Alchemy::countIngredients() const\n{\n    int ingredients = 0;\n\n    for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)\n        if (!iter->isEmpty())\n            ++ingredients;\n\n    return ingredients;\n}\n\nint MWMechanics::Alchemy::countPotionsToBrew() const\n{\n    Result readyStatus = getReadyStatus();\n    if (readyStatus != Result_Success)\n        return 0;\n\n    int toBrew = -1;\n\n    for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)\n        if (!iter->isEmpty())\n        {\n            int count = iter->getRefData().getCount();\n            if ((count > 0 && count < toBrew) || toBrew < 0)\n                toBrew = count;\n        }\n\n    return toBrew;\n}\n\nvoid MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc)\n{\n    mAlchemist = npc;\n\n    mIngredients.resize (4);\n\n    std::fill (mIngredients.begin(), mIngredients.end(), MWWorld::Ptr());\n\n    mTools.resize (4);\n\n    std::fill (mTools.begin(), mTools.end(), MWWorld::Ptr());\n\n    mEffects.clear();\n\n    MWWorld::ContainerStore& store = npc.getClass().getContainerStore (npc);\n\n    for (MWWorld::ContainerStoreIterator iter (store.begin (MWWorld::ContainerStore::Type_Apparatus));\n        iter!=store.end(); ++iter)\n    {\n        MWWorld::LiveCellRef<ESM::Apparatus>* ref = iter->get<ESM::Apparatus>();\n\n        int type = ref->mBase->mData.mType;\n\n        if (type<0 || type>=static_cast<int> (mTools.size()))\n            throw std::runtime_error (\"invalid apparatus type\");\n\n        if (!mTools[type].isEmpty())\n            if (ref->mBase->mData.mQuality<=mTools[type].get<ESM::Apparatus>()->mBase->mData.mQuality)\n                continue;\n\n        mTools[type] = *iter;\n    }\n}\n\nMWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::beginTools() const\n{\n    return mTools.begin();\n}\n\nMWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::endTools() const\n{\n    return mTools.end();\n}\n\nMWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::beginIngredients() const\n{\n    return mIngredients.begin();\n}\n\nMWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::endIngredients() const\n{\n    return mIngredients.end();\n}\n\nvoid MWMechanics::Alchemy::clear()\n{\n    mAlchemist = MWWorld::Ptr();\n    mTools.clear();\n    mIngredients.clear();\n    mEffects.clear();\n    setPotionName(\"\");\n}\n\nvoid MWMechanics::Alchemy::setPotionName(const std::string& name)\n{\n    mPotionName = name;\n}\n\nint MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient)\n{\n    // find a free slot\n    int slot = -1;\n\n    for (int i=0; i<static_cast<int> (mIngredients.size()); ++i)\n        if (mIngredients[i].isEmpty())\n        {\n            slot = i;\n            break;\n        }\n\n    if (slot==-1)\n        return -1;\n\n    for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)\n        if (!iter->isEmpty() && Misc::StringUtils::ciEqual(ingredient.getCellRef().getRefId(),\n                                                           iter->getCellRef().getRefId()))\n            return -1;\n\n    mIngredients[slot] = ingredient;\n\n    updateEffects();\n\n    return slot;\n}\n\nvoid MWMechanics::Alchemy::removeIngredient (int index)\n{\n    if (index>=0 && index<static_cast<int> (mIngredients.size()))\n    {\n        mIngredients[index] = MWWorld::Ptr();\n        updateEffects();\n    }\n}\n\nMWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::beginEffects() const\n{\n    return mEffects.begin();\n}\n\nMWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const\n{\n    return mEffects.end();\n}\n\nbool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc)\n{\n    float alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy);\n    static const float fWortChanceValue =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fWortChanceValue\")->mValue.getFloat();\n    return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue)\n            || (potionEffectIndex <= 3 && alchemySkill >= fWortChanceValue*2)\n            || (potionEffectIndex <= 5 && alchemySkill >= fWortChanceValue*3)\n            || (potionEffectIndex <= 7 && alchemySkill >= fWortChanceValue*4);\n}\n\nMWMechanics::Alchemy::Result MWMechanics::Alchemy::getReadyStatus() const\n{\n    if (mTools[ESM::Apparatus::MortarPestle].isEmpty())\n        return Result_NoMortarAndPestle;\n\n    if (countIngredients()<2)\n        return Result_LessThanTwoIngredients;\n\n    if (mPotionName.empty())\n        return Result_NoName;\n\n    if (listEffects().empty())\n        return Result_NoEffects;\n\n    return Result_Success;\n}\n\nMWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name, int& count)\n{\n    /*\n        Start of tes3mp addition\n\n        Instead of sending an ID_PLAYER_INVENTORY packet for every ingredient removal in\n        ContainerStore::remove(), as that would get very spammy when many potions are created\n        at the same time, just avoid sending packets here and store the item removals so they\n        can be sent in a single packet when all the potions have been created\n    */\n    mwmp::Main::get().getLocalPlayer()->avoidSendingInventoryPackets = true;\n    /*\n        End of tes3mp addition\n    */\n\n    setPotionName(name);\n    Result readyStatus = getReadyStatus();\n\n    if (readyStatus == Result_NoEffects)\n        removeIngredients();\n\n    /*\n        Start of tes3mp change (minor)\n\n        Set avoidSendingInventoryPackets to false again if this has not been a successful\n        potion creation\n    */\n    if (readyStatus != Result_Success)\n    {\n        mwmp::Main::get().getLocalPlayer()->avoidSendingInventoryPackets = false;\n        return readyStatus;\n    }\n    /*\n        End of tes3mp change (major)\n    */\n\n    Result result = Result_RandomFailure;\n    int brewedCount = 0;\n    for (int i = 0; i < count; ++i)\n    {\n        if (createSingle() == Result_Success)\n        {\n            result = Result_Success;\n            brewedCount++;\n        }\n    }\n\n    count = brewedCount;\n\n    /*\n        Start of tes3mp addition\n\n        Send an ID_RECORD_DYNAMIC packet with the potion we've been creating\n        now that we know its quantity\n\n        Stop avoiding the sending of ID_PLAYER_INVENTORY packets and send all\n        item removals stored so far\n    */\n    mwmp::Main::get().getNetworking()->getWorldstate()->sendPotionRecord(&mStoredPotion, brewedCount);\n    mwmp::Main::get().getLocalPlayer()->avoidSendingInventoryPackets = false;\n    mwmp::Main::get().getLocalPlayer()->sendStoredItemRemovals();\n    /*\n        End of tes3mp addition\n    */\n\n    return result;\n}\n\nMWMechanics::Alchemy::Result MWMechanics::Alchemy::createSingle ()\n{\n    if (beginEffects() == endEffects())\n    {\n        // all effects were nullified due to insufficient skill\n        removeIngredients();\n        return Result_RandomFailure;\n    }\n\n    if (getAlchemyFactor() < Misc::Rng::roll0to99())\n    {\n        removeIngredients();\n        return Result_RandomFailure;\n    }\n\n    addPotion(mPotionName);\n\n    removeIngredients();\n\n    increaseSkill();\n\n    return Result_Success;\n}\n\nstd::string MWMechanics::Alchemy::suggestPotionName()\n{\n    std::set<MWMechanics::EffectKey> effects = listEffects();\n    if (effects.empty())\n        return \"\";\n\n    int effectId = effects.begin()->mId;\n    return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                ESM::MagicEffect::effectIdToString(effectId))->mValue.getString();\n}\n\nstd::vector<std::string> MWMechanics::Alchemy::effectsDescription (const MWWorld::ConstPtr &ptr, const int alchemySkill)\n{\n    std::vector<std::string> effects;\n\n    const auto& item = ptr.get<ESM::Ingredient>()->mBase;\n    const auto& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n    const static auto fWortChanceValue = gmst.find(\"fWortChanceValue\")->mValue.getFloat();\n    const auto& data = item->mData;\n\n    for (auto i = 0; i < 4; ++i)\n    {\n        const auto effectID = data.mEffectID[i];\n        const auto skillID = data.mSkills[i];\n        const auto attributeID = data.mAttributes[i];\n\n        if (alchemySkill < fWortChanceValue * (i + 1))\n            break;\n\n        if (effectID != -1)\n        {\n            std::string effect = gmst.find(ESM::MagicEffect::effectIdToString(effectID))->mValue.getString();\n\n            if (skillID != -1)\n                effect += \" \" + gmst.find(ESM::Skill::sSkillNameIds[skillID])->mValue.getString();\n            else if (attributeID != -1)\n                effect += \" \" + gmst.find(ESM::Attribute::sGmstAttributeIds[attributeID])->mValue.getString();\n\n            effects.push_back(effect);\n\n        }\n    }\n    return effects;\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/alchemy.hpp",
    "content": "#ifndef GAME_MWMECHANICS_ALCHEMY_H\n#define GAME_MWMECHANICS_ALCHEMY_H\n\n#include <vector>\n#include <set>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/esm/loadalch.hpp>\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/effectlist.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace ESM\n{\n    struct Potion;\n}\n\nnamespace MWMechanics\n{\n    struct EffectKey;\n\n    /// \\brief Potion creation via alchemy skill\n    class Alchemy\n    {\n        public:\n\n            Alchemy();\n\n            typedef std::vector<MWWorld::Ptr> TToolsContainer;\n            typedef TToolsContainer::const_iterator TToolsIterator;\n\n            typedef std::vector<MWWorld::Ptr> TIngredientsContainer;\n            typedef TIngredientsContainer::const_iterator TIngredientsIterator;\n\n            typedef std::vector<ESM::ENAMstruct> TEffectsContainer;\n            typedef TEffectsContainer::const_iterator TEffectsIterator;\n\n            enum Result\n            {\n                Result_Success,\n\n                Result_NoMortarAndPestle,\n                Result_LessThanTwoIngredients,\n                Result_NoName,\n                Result_NoEffects,\n                Result_RandomFailure\n            };\n\n        private:\n\n            MWWorld::Ptr mAlchemist;\n            TToolsContainer mTools;\n            TIngredientsContainer mIngredients;\n            TEffectsContainer mEffects;\n            int mValue;\n            std::string mPotionName;\n\n            /*\n                Start of tes3mp addition\n\n                Keep a copy of the last created potion record so it can be sent to the\n                server once we have determined its brewedCount\n            */\n            ESM::Potion mStoredPotion;\n            /*\n                End of tes3mp addition\n            */\n\n            void applyTools (int flags, float& value) const;\n\n            void updateEffects();\n\n            Result getReadyStatus() const;\n\n            const ESM::Potion *getRecord(const ESM::Potion& toFind) const;\n            ///< Try to find a potion record similar to \\a toFind in the record store, or return 0 if not found\n            /// \\note Does not account for record ID, model or icon\n\n            void removeIngredients();\n            ///< Remove selected ingredients from alchemist's inventory, cleanup selected ingredients and\n            /// update effect list accordingly.\n\n            void addPotion (const std::string& name);\n            ///< Add a potion to the alchemist's inventory.\n\n            void increaseSkill();\n            ///< Increase alchemist's skill.\n\n            Result createSingle ();\n            ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and\n            /// adjust the skills of the alchemist accordingly.\n\n            float getAlchemyFactor() const;\n\n            int countIngredients() const;\n\n            TEffectsIterator beginEffects() const;\n\n            TEffectsIterator endEffects() const;\n\n        public:\n            int countPotionsToBrew() const;\n            ///< calculates maximum amount of potions, which you can make from selected ingredients\n\n            static bool knownEffect (unsigned int potionEffectIndex, const MWWorld::Ptr& npc);\n            ///< Does npc have sufficient alchemy skill to know about this potion effect?\n\n            void setAlchemist (const MWWorld::Ptr& npc);\n            ///< Set alchemist and configure alchemy setup accordingly. \\a npc may be empty to indicate that\n            /// there is no alchemist (alchemy session has ended).\n\n            TToolsIterator beginTools() const;\n            ///< \\attention Iterates over tool slots, not over tools. Some of the slots may be empty.\n\n            TToolsIterator endTools() const;\n\n            TIngredientsIterator beginIngredients() const;\n            ///< \\attention Iterates over ingredient slots, not over ingredients. Some of the slots may be empty.\n\n            TIngredientsIterator endIngredients() const;\n\n            void clear();\n            ///< Remove alchemist, tools and ingredients.\n\n            void setPotionName(const std::string& name);\n            ///< Set name of potion to create\n\n            std::set<EffectKey> listEffects() const;\n            ///< List all effects shared by at least two ingredients.\n\n            int addIngredient (const MWWorld::Ptr& ingredient);\n            ///< Add ingredient into the next free slot.\n            ///\n            /// \\return Slot index or -1, if adding failed because of no free slot or the ingredient type being\n            /// listed already.\n\n            void removeIngredient (int index);\n            ///< Remove ingredient from slot (calling this function on an empty slot is a no-op).\n\n            std::string suggestPotionName ();\n            ///< Suggest a name for the potion, based on the current effects\n\n            Result create (const std::string& name, int& count);\n            ///< Try to create potions from the ingredients, place them in the inventory of the alchemist and\n            /// adjust the skills of the alchemist accordingly.\n            /// \\param name must not be an empty string, or Result_NoName is returned\n\n            static std::vector<std::string> effectsDescription (const MWWorld::ConstPtr &ptr, const int alchemySKill);\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "apps/openmw/mwmechanics/autocalcspell.cpp",
    "content": "#include \"autocalcspell.hpp\"\n\n#include <limits>\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"spellutil.hpp\"\n\nnamespace MWMechanics\n{\n\n    struct SchoolCaps\n    {\n        int mCount;\n        int mLimit;\n        bool mReachedLimit;\n        int mMinCost;\n        std::string mWeakestSpell;\n    };\n\n    std::vector<std::string> autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race)\n    {\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        static const float fNPCbaseMagickaMult = gmst.find(\"fNPCbaseMagickaMult\")->mValue.getFloat();\n        float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];\n\n        static const std::string schools[] = {\n            \"alteration\", \"conjuration\", \"destruction\", \"illusion\", \"mysticism\", \"restoration\"\n        };\n        static int iAutoSpellSchoolMax[6];\n        static bool init = false;\n        if (!init)\n        {\n            for (int i=0; i<6; ++i)\n            {\n                const std::string& gmstName = \"iAutoSpell\" + schools[i] + \"Max\";\n                iAutoSpellSchoolMax[i] = gmst.find(gmstName)->mValue.getInteger();\n            }\n            init = true;\n        }\n\n        std::map<int, SchoolCaps> schoolCaps;\n        for (int i=0; i<6; ++i)\n        {\n            SchoolCaps caps;\n            caps.mCount = 0;\n            caps.mLimit = iAutoSpellSchoolMax[i];\n            caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0;\n            caps.mMinCost = std::numeric_limits<int>::max();\n            caps.mWeakestSpell.clear();\n            schoolCaps[i] = caps;\n        }\n\n        std::vector<std::string> selectedSpells;\n\n        const MWWorld::Store<ESM::Spell> &spells =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();\n\n        // Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the\n        // Store must preserve the record ordering as it was in the content files.\n        for (const ESM::Spell& spell : spells)\n        {\n            if (spell.mData.mType != ESM::Spell::ST_Spell)\n                continue;\n            if (!(spell.mData.mFlags & ESM::Spell::F_Autocalc))\n                continue;\n            static const int iAutoSpellTimesCanCast = gmst.find(\"iAutoSpellTimesCanCast\")->mValue.getInteger();\n            if (baseMagicka < iAutoSpellTimesCanCast * spell.mData.mCost)\n                continue;\n\n            if (race && race->mPowers.exists(spell.mId))\n                continue;\n\n            if (!attrSkillCheck(&spell, actorSkills, actorAttributes))\n                continue;\n\n            int school;\n            float skillTerm;\n            calcWeakestSchool(&spell, actorSkills, school, skillTerm);\n            assert(school >= 0 && school < 6);\n            SchoolCaps& cap = schoolCaps[school];\n\n            if (cap.mReachedLimit && spell.mData.mCost <= cap.mMinCost)\n                continue;\n\n            static const float fAutoSpellChance = gmst.find(\"fAutoSpellChance\")->mValue.getFloat();\n            if (calcAutoCastChance(&spell, actorSkills, actorAttributes, school) < fAutoSpellChance)\n                continue;\n\n            selectedSpells.push_back(spell.mId);\n\n            if (cap.mReachedLimit)\n            {\n                std::vector<std::string>::iterator found = std::find(selectedSpells.begin(), selectedSpells.end(), cap.mWeakestSpell);\n                if (found != selectedSpells.end())\n                    selectedSpells.erase(found);\n\n                cap.mMinCost = std::numeric_limits<int>::max();\n                for (const std::string& testSpellName : selectedSpells)\n                {\n                    const ESM::Spell* testSpell = spells.find(testSpellName);\n\n                    //int testSchool;\n                    //float dummySkillTerm;\n                    //calcWeakestSchool(testSpell, actorSkills, testSchool, dummySkillTerm);\n\n                    // Note: if there are multiple spells with the same cost, we pick the first one we found.\n                    // So the algorithm depends on the iteration order of the outer loop.\n                    if (\n                            // There is a huge bug here. It is not checked that weakestSpell is of the correct school.\n                            // As result multiple SchoolCaps could have the same mWeakestSpell. Erasing the weakest spell would then fail if another school\n                            // already erased it, and so the number of spells would often exceed the sum of limits.\n                            // This bug cannot be fixed without significantly changing the results of the spell autocalc, which will not have been playtested.\n                            //testSchool == school &&\n                            testSpell->mData.mCost < cap.mMinCost)\n                    {\n                        cap.mMinCost = testSpell->mData.mCost;\n                        cap.mWeakestSpell = testSpell->mId;\n                    }\n                }\n            }\n            else\n            {\n                cap.mCount += 1;\n                if (cap.mCount == cap.mLimit)\n                    cap.mReachedLimit = true;\n\n                if (spell.mData.mCost < cap.mMinCost)\n                {\n                    cap.mWeakestSpell = spell.mId;\n                    cap.mMinCost = spell.mData.mCost;\n                }\n            }\n        }\n\n        return selectedSpells;\n    }\n\n    std::vector<std::string> autoCalcPlayerSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race)\n    {\n        const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();\n\n        static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find(\"fPCbaseMagickaMult\")->mValue.getFloat();\n\n        float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];\n        bool reachedLimit = false;\n        const ESM::Spell* weakestSpell = nullptr;\n        int minCost = std::numeric_limits<int>::max();\n\n        std::vector<std::string> selectedSpells;\n\n        const MWWorld::Store<ESM::Spell> &spells = esmStore.get<ESM::Spell>();\n        for (const ESM::Spell& spell : spells)\n        {\n            if (spell.mData.mType != ESM::Spell::ST_Spell)\n                continue;\n            if (!(spell.mData.mFlags & ESM::Spell::F_PCStart))\n                continue;\n            if (reachedLimit && spell.mData.mCost <= minCost)\n                continue;\n            if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell.mId) != race->mPowers.mList.end())\n                continue;\n            if (baseMagicka < spell.mData.mCost)\n                continue;\n\n            static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find(\"fAutoPCSpellChance\")->mValue.getFloat();\n            if (calcAutoCastChance(&spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance)\n                continue;\n\n            if (!attrSkillCheck(&spell, actorSkills, actorAttributes))\n                continue;\n\n            selectedSpells.push_back(spell.mId);\n\n            if (reachedLimit)\n            {\n                std::vector<std::string>::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId);\n                if (it != selectedSpells.end())\n                    selectedSpells.erase(it);\n\n                minCost = std::numeric_limits<int>::max();\n                for (const std::string& testSpellName : selectedSpells)\n                {\n                    const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(testSpellName);\n                    if (testSpell->mData.mCost < minCost)\n                    {\n                        minCost = testSpell->mData.mCost;\n                        weakestSpell = testSpell;\n                    }\n                }\n            }\n            else\n            {\n                if (spell.mData.mCost < minCost)\n                {\n                    weakestSpell = &spell;\n                    minCost = weakestSpell->mData.mCost;\n                }\n                static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find(\"iAutoPCSpellMax\")->mValue.getInteger();\n                if (selectedSpells.size() == iAutoPCSpellMax)\n                    reachedLimit = true;\n            }\n        }\n\n        return selectedSpells;\n    }\n\n    bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)\n    {\n        for (const auto& spellEffect : spell->mEffects.mList)\n        {\n            const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(spellEffect.mEffectID);\n            static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"iAutoSpellAttSkillMin\")->mValue.getInteger();\n\n            if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))\n            {\n                assert (spellEffect.mSkill >= 0 && spellEffect.mSkill < ESM::Skill::Length);\n                if (actorSkills[spellEffect.mSkill] < iAutoSpellAttSkillMin)\n                    return false;\n            }\n\n            if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))\n            {\n                assert (spellEffect.mAttribute >= 0 && spellEffect.mAttribute < ESM::Attribute::Length);\n                if (actorAttributes[spellEffect.mAttribute] < iAutoSpellAttSkillMin)\n                    return false;\n            }\n        }\n\n        return true;\n    }\n\n    void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm)\n    {\n        // Morrowind for some reason uses a formula slightly different from magicka cost calculation\n        float minChance = std::numeric_limits<float>::max();\n        for (const ESM::ENAMstruct& effect : spell->mEffects.mList)\n        {\n            const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);\n\n            int minMagn = 1;\n            int maxMagn = 1;\n            if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))\n            {\n                minMagn = effect.mMagnMin;\n                maxMagn = effect.mMagnMax;\n            }\n\n            int duration = 0;\n            if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))\n                duration = effect.mDuration;\n            if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce))\n                duration = std::max(1, duration);\n\n            static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore()\n                .get<ESM::GameSetting>().find(\"fEffectCostMult\")->mValue.getFloat();\n\n            float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));\n            x *= 0.1 * magicEffect->mData.mBaseCost;\n            x *= 1 + duration;\n            x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost;\n            x *= fEffectCostMult;\n\n            if (effect.mRange == ESM::RT_Target)\n                x *= 1.5f;\n\n            float s = 2.f * actorSkills[spellSchoolToSkill(magicEffect->mData.mSchool)];\n            if (s - x < minChance)\n            {\n                minChance = s - x;\n                effectiveSchool = magicEffect->mData.mSchool;\n                skillTerm = s;\n            }\n        }\n    }\n\n    float calcAutoCastChance(const ESM::Spell *spell, const int *actorSkills, const int *actorAttributes, int effectiveSchool)\n    {\n        if (spell->mData.mType != ESM::Spell::ST_Spell)\n            return 100.f;\n\n        if (spell->mData.mFlags & ESM::Spell::F_Always)\n            return 100.f;\n\n        float skillTerm = 0;\n        if (effectiveSchool != -1)\n            skillTerm = 2.f * actorSkills[spellSchoolToSkill(effectiveSchool)];\n        else\n            calcWeakestSchool(spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this\n\n        float castChance = skillTerm - spell->mData.mCost + 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck];\n        return castChance;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/autocalcspell.hpp",
    "content": "#ifndef OPENMW_AUTOCALCSPELL_H\n#define OPENMW_AUTOCALCSPELL_H\n\n#include <string>\n#include <vector>\n\nnamespace ESM\n{\n    struct Spell;\n    struct Race;\n}\n\nnamespace MWMechanics\n{\n\n/// Contains algorithm for calculating an NPC's spells based on stats\n/// @note We might want to move this code to a component later, so the editor can use it for preview purposes\n\nstd::vector<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race);\n\nstd::vector<std::string> autoCalcPlayerSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race);\n\n// Helpers\n\nbool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes);\n\nvoid calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm);\n\nfloat calcAutoCastChance(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/character.cpp",
    "content": "/*\n * OpenMW - The completely unofficial reimplementation of Morrowind\n *\n * This file (character.cpp) is part of the OpenMW package.\n *\n * OpenMW is distributed as free software: you can redistribute it\n * and/or modify it under the terms of the GNU General Public License\n * version 3, as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * version 3 along with this program. If not, see\n * https://www.gnu.org/licenses/ .\n */\n\n#include \"character.hpp\"\n\n#include <iostream>\n\n#include <components/misc/mathutil.hpp>\n#include <components/misc/rng.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/LocalActor.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/DedicatedPlayer.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"aicombataction.hpp\"\n#include \"movement.hpp\"\n#include \"npcstats.hpp\"\n#include \"creaturestats.hpp\"\n#include \"security.hpp\"\n#include \"actorutil.hpp\"\n#include \"spellcasting.hpp\"\n\nnamespace\n{\n\nstd::string getBestAttack (const ESM::Weapon* weapon)\n{\n    int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2;\n    int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2;\n    int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2;\n    if (slash == chop && slash == thrust)\n        return \"slash\";\n    else if (thrust >= chop && thrust >= slash)\n        return \"thrust\";\n    else if (slash >= chop && slash >= thrust)\n        return \"slash\";\n    else\n        return \"chop\";\n}\n\n// Converts a movement Run state to its equivalent Walk state.\nMWMechanics::CharacterState runStateToWalkState (MWMechanics::CharacterState state)\n{\n    using namespace MWMechanics;\n    CharacterState ret = state;\n    switch (state)\n    {\n        case CharState_RunForward:\n            ret = CharState_WalkForward;\n            break;\n        case CharState_RunBack:\n            ret = CharState_WalkBack;\n            break;\n        case CharState_RunLeft:\n            ret = CharState_WalkLeft;\n            break;\n        case CharState_RunRight:\n            ret = CharState_WalkRight;\n            break;\n        case CharState_SwimRunForward:\n            ret = CharState_SwimWalkForward;\n            break;\n        case CharState_SwimRunBack:\n            ret = CharState_SwimWalkBack;\n            break;\n        case CharState_SwimRunLeft:\n            ret = CharState_SwimWalkLeft;\n            break;\n        case CharState_SwimRunRight:\n            ret = CharState_SwimWalkRight;\n            break;\n        default:\n            break;\n    }\n    return ret;\n}\n\nfloat getFallDamage(const MWWorld::Ptr& ptr, float fallHeight)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();\n\n    const float fallDistanceMin = store.find(\"fFallDamageDistanceMin\")->mValue.getFloat();\n\n    if (fallHeight >= fallDistanceMin)\n    {\n        const float acrobaticsSkill = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Acrobatics));\n        const float jumpSpellBonus = ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Jump).getMagnitude();\n        const float fallAcroBase = store.find(\"fFallAcroBase\")->mValue.getFloat();\n        const float fallAcroMult = store.find(\"fFallAcroMult\")->mValue.getFloat();\n        const float fallDistanceBase = store.find(\"fFallDistanceBase\")->mValue.getFloat();\n        const float fallDistanceMult = store.find(\"fFallDistanceMult\")->mValue.getFloat();\n\n        float x = fallHeight - fallDistanceMin;\n        x -= (1.5f * acrobaticsSkill) + jumpSpellBonus;\n        x = std::max(0.0f, x);\n\n        float a = fallAcroBase + fallAcroMult * (100 - acrobaticsSkill);\n        x = fallDistanceBase + fallDistanceMult * x;\n        x *= a;\n\n        return x;\n    }\n    return 0.f;\n}\n\n}\n\nnamespace MWMechanics\n{\n\nstruct StateInfo {\n    CharacterState state;\n    const char groupname[32];\n};\n\nstatic const StateInfo sMovementList[] = {\n    { CharState_WalkForward, \"walkforward\" },\n    { CharState_WalkBack, \"walkback\" },\n    { CharState_WalkLeft, \"walkleft\" },\n    { CharState_WalkRight, \"walkright\" },\n\n    { CharState_SwimWalkForward, \"swimwalkforward\" },\n    { CharState_SwimWalkBack, \"swimwalkback\" },\n    { CharState_SwimWalkLeft, \"swimwalkleft\" },\n    { CharState_SwimWalkRight, \"swimwalkright\" },\n\n    { CharState_RunForward, \"runforward\" },\n    { CharState_RunBack, \"runback\" },\n    { CharState_RunLeft, \"runleft\" },\n    { CharState_RunRight, \"runright\" },\n\n    { CharState_SwimRunForward, \"swimrunforward\" },\n    { CharState_SwimRunBack, \"swimrunback\" },\n    { CharState_SwimRunLeft, \"swimrunleft\" },\n    { CharState_SwimRunRight, \"swimrunright\" },\n\n    { CharState_SneakForward, \"sneakforward\" },\n    { CharState_SneakBack, \"sneakback\" },\n    { CharState_SneakLeft, \"sneakleft\" },\n    { CharState_SneakRight, \"sneakright\" },\n\n    { CharState_Jump, \"jump\" },\n\n    { CharState_TurnLeft, \"turnleft\" },\n    { CharState_TurnRight, \"turnright\" },\n    { CharState_SwimTurnLeft, \"swimturnleft\" },\n    { CharState_SwimTurnRight, \"swimturnright\" },\n};\nstatic const StateInfo *sMovementListEnd = &sMovementList[sizeof(sMovementList)/sizeof(sMovementList[0])];\n\n\nclass FindCharState {\n    CharacterState state;\n\npublic:\n    FindCharState(CharacterState _state) : state(_state) { }\n\n    bool operator()(const StateInfo &info) const\n    { return info.state == state; }\n};\n\n\nstd::string CharacterController::chooseRandomGroup (const std::string& prefix, int* num) const\n{\n    int numAnims=0;\n    while (mAnimation->hasAnimation(prefix + std::to_string(numAnims+1)))\n        ++numAnims;\n\n    int roll = Misc::Rng::rollDice(numAnims) + 1; // [1, numAnims]\n    if (num)\n        *num = roll;\n    return prefix + std::to_string(roll);\n}\n\nvoid CharacterController::refreshHitRecoilAnims(CharacterState& idle)\n{\n    bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery();\n    bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown();\n    bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock();\n    bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);\n    if(mHitState == CharState_None)\n    {\n        if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0\n                || mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0))\n        {\n            mTimeUntilWake = Misc::Rng::rollClosedProbability() * 2 + 1; // Wake up after 1 to 3 seconds\n            if (isSwimming && mAnimation->hasAnimation(\"swimknockout\"))\n            {\n                mHitState = CharState_SwimKnockOut;\n                mCurrentHit = \"swimknockout\";\n                mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, \"start\", \"stop\", 0.0f, ~0ul);\n            }\n            else if (!isSwimming && mAnimation->hasAnimation(\"knockout\"))\n            {\n                mHitState = CharState_KnockOut;\n                mCurrentHit = \"knockout\";\n                mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, \"start\", \"stop\", 0.0f, ~0ul);\n            }\n            else\n            {\n                // Knockout animations are missing. Fall back to idle animation, so target actor still can be killed via HtH.\n                mCurrentHit.erase();\n            }\n\n            mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true);\n        }\n        else if (knockdown)\n        {\n            if (isSwimming && mAnimation->hasAnimation(\"swimknockdown\"))\n            {\n                mHitState = CharState_SwimKnockDown;\n                mCurrentHit = \"swimknockdown\";\n                mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, \"start\", \"stop\", 0.0f, 0);\n            }\n            else if (!isSwimming && mAnimation->hasAnimation(\"knockdown\"))\n            {\n                mHitState = CharState_KnockDown;\n                mCurrentHit = \"knockdown\";\n                mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, \"start\", \"stop\", 0.0f, 0);\n            }\n            else\n            {\n                // Knockdown animation is missing. Cancel knockdown state.\n                mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false);\n            }\n        }\n        else if (recovery)\n        {\n            std::string anim = chooseRandomGroup(\"swimhit\");\n            if (isSwimming && mAnimation->hasAnimation(anim))\n            {\n                mHitState = CharState_SwimHit;\n                mCurrentHit = anim;\n                mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, \"start\", \"stop\", 0.0f, 0);\n            }\n            else\n            {\n                anim = chooseRandomGroup(\"hit\");\n                if (mAnimation->hasAnimation(anim))\n                {\n                    mHitState = CharState_Hit;\n                    mCurrentHit = anim;\n                    mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, \"start\", \"stop\", 0.0f, 0);\n                }\n            }\n        }\n        else if (block && mAnimation->hasAnimation(\"shield\"))\n        {\n            mHitState = CharState_Block;\n            mCurrentHit = \"shield\";\n            MWRender::Animation::AnimPriority priorityBlock (Priority_Hit);\n            priorityBlock[MWRender::Animation::BoneGroup_LeftArm] = Priority_Block;\n            priorityBlock[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;\n            mAnimation->play(mCurrentHit, priorityBlock, MWRender::Animation::BlendMask_All, true, 1, \"block start\", \"block stop\", 0.0f, 0);\n        }\n\n        // Cancel upper body animations\n        if (isKnockedOut() || isKnockedDown())\n        {\n            if (mUpperBodyState > UpperCharState_WeapEquiped)\n            {\n                mAnimation->disable(mCurrentWeapon);\n                mUpperBodyState = UpperCharState_WeapEquiped;\n                if (mWeaponType > ESM::Weapon::None)\n                    mAnimation->showWeapons(true);\n            }\n            else if (mUpperBodyState > UpperCharState_Nothing && mUpperBodyState < UpperCharState_WeapEquiped)\n            {\n                mAnimation->disable(mCurrentWeapon);\n                mUpperBodyState = UpperCharState_Nothing;\n            }\n        }\n        if (mHitState != CharState_None)\n            idle = CharState_None;\n    }\n    else if(!mAnimation->isPlaying(mCurrentHit))\n    {\n        mCurrentHit.erase();\n        if (knockdown)\n            mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false);\n        if (recovery)\n            mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false);\n        if (block)\n            mPtr.getClass().getCreatureStats(mPtr).setBlock(false);\n        mHitState = CharState_None;\n    }\n    else if (isKnockedOut() && mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() > 0 \n            && mTimeUntilWake <= 0)\n    {\n        mHitState = isSwimming ? CharState_SwimKnockDown : CharState_KnockDown;\n        mAnimation->disable(mCurrentHit);\n        mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, \"loop stop\", \"stop\", 0.0f, 0);\n    }\n}\n\nvoid CharacterController::refreshJumpAnims(const std::string& weapShortGroup, JumpingState jump, CharacterState& idle, bool force)\n{\n    if (!force && jump == mJumpState && idle == CharState_None)\n        return;\n\n    std::string jumpAnimName;\n    MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All;\n    if (jump != JumpState_None)\n    {\n        jumpAnimName = \"jump\";\n        if(!weapShortGroup.empty())\n        {\n            jumpAnimName += weapShortGroup;\n            if(!mAnimation->hasAnimation(jumpAnimName))\n            {\n                jumpAnimName = fallbackShortWeaponGroup(\"jump\", &jumpmask);\n\n                // If we apply jump only for lower body, do not reset idle animations.\n                // For upper body there will be idle animation.\n                if (jumpmask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None)\n                    idle = CharState_Idle;\n            }\n        }\n    }\n\n    if (!force && jump == mJumpState)\n        return;\n\n    bool startAtLoop = (jump == mJumpState);\n    mJumpState = jump;\n\n    if (!mCurrentJump.empty())\n    {\n        mAnimation->disable(mCurrentJump);\n        mCurrentJump.clear();\n    }\n\n    if(mJumpState == JumpState_InAir)\n    {\n        if (mAnimation->hasAnimation(jumpAnimName))\n        {\n            mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false,\n                         1.0f, startAtLoop ? \"loop start\" : \"start\", \"stop\", 0.f, ~0ul);\n            mCurrentJump = jumpAnimName;\n        }\n    }\n    else if (mJumpState == JumpState_Landing)\n    {\n        if (mAnimation->hasAnimation(jumpAnimName))\n        {\n            mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true,\n                         1.0f, \"loop stop\", \"stop\", 0.0f, 0);\n            mCurrentJump = jumpAnimName;\n        }\n    }\n}\n\nbool CharacterController::onOpen()\n{\n    if (mPtr.getTypeName() == typeid(ESM::Container).name())\n    {\n        if (!mAnimation->hasAnimation(\"containeropen\"))\n            return true;\n\n        if (mAnimation->isPlaying(\"containeropen\"))\n            return false;\n\n        if (mAnimation->isPlaying(\"containerclose\"))\n            return false;\n\n        mAnimation->play(\"containeropen\", Priority_Persistent, MWRender::Animation::BlendMask_All, false, 1.0f, \"start\", \"stop\", 0.f, 0);\n        if (mAnimation->isPlaying(\"containeropen\"))\n            return false;\n    }\n\n    return true;\n}\n\nvoid CharacterController::onClose()\n{\n    if (mPtr.getTypeName() == typeid(ESM::Container).name())\n    {\n        if (!mAnimation->hasAnimation(\"containerclose\"))\n            return;\n\n        float complete, startPoint = 0.f;\n        bool animPlaying = mAnimation->getInfo(\"containeropen\", &complete);\n        if (animPlaying)\n            startPoint = 1.f - complete;\n\n        mAnimation->play(\"containerclose\", Priority_Persistent, MWRender::Animation::BlendMask_All, false, 1.0f, \"start\", \"stop\", startPoint, 0);\n    }\n}\n\nstd::string CharacterController::getWeaponAnimation(int weaponType) const\n{\n    std::string weaponGroup = getWeaponType(weaponType)->mLongGroup;\n    bool isRealWeapon = weaponType != ESM::Weapon::HandToHand && weaponType != ESM::Weapon::Spell && weaponType != ESM::Weapon::None;\n    if (isRealWeapon && !mAnimation->hasAnimation(weaponGroup))\n    {\n        static const std::string oneHandFallback = getWeaponType(ESM::Weapon::LongBladeOneHand)->mLongGroup;\n        static const std::string twoHandFallback = getWeaponType(ESM::Weapon::LongBladeTwoHand)->mLongGroup;\n\n        const ESM::WeaponType* weapInfo = getWeaponType(weaponType);\n\n        // For real two-handed melee weapons use 2h swords animations as fallback, otherwise use the 1h ones\n        if (weapInfo->mFlags & ESM::WeaponType::TwoHanded && weapInfo->mWeaponClass == ESM::WeaponType::Melee)\n            weaponGroup = twoHandFallback;\n        else if (isRealWeapon)\n            weaponGroup = oneHandFallback;\n    }\n\n    return weaponGroup;\n}\n\nstd::string CharacterController::fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask)\n{\n    bool isRealWeapon = mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None;\n    if (!isRealWeapon)\n    {\n        if (blendMask != nullptr)\n            *blendMask = MWRender::Animation::BlendMask_LowerBody;\n\n        return baseGroupName;\n    }\n\n    static const std::string oneHandFallback = getWeaponType(ESM::Weapon::LongBladeOneHand)->mShortGroup;\n    static const std::string twoHandFallback = getWeaponType(ESM::Weapon::LongBladeTwoHand)->mShortGroup;\n\n    std::string groupName = baseGroupName;\n    const ESM::WeaponType* weapInfo = getWeaponType(mWeaponType);\n\n    // For real two-handed melee weapons use 2h swords animations as fallback, otherwise use the 1h ones\n    if (weapInfo->mFlags & ESM::WeaponType::TwoHanded && weapInfo->mWeaponClass == ESM::WeaponType::Melee)\n        groupName += twoHandFallback;\n    else\n        groupName += oneHandFallback;\n\n    // Special case for crossbows - we shouls apply 1h animations a fallback only for lower body\n    if (mWeaponType == ESM::Weapon::MarksmanCrossbow && blendMask != nullptr)\n        *blendMask = MWRender::Animation::BlendMask_LowerBody;\n\n    if (!mAnimation->hasAnimation(groupName))\n    {\n        groupName = baseGroupName;\n        if (blendMask != nullptr)\n            *blendMask = MWRender::Animation::BlendMask_LowerBody;\n    }\n\n    return groupName;\n}\n\nvoid CharacterController::refreshMovementAnims(const std::string& weapShortGroup, CharacterState movement, CharacterState& idle, bool force)\n{\n    if (movement == mMovementState && idle == mIdleState && !force)\n        return;\n\n    // Reset idle if we actually play movement animations excepts of these cases:\n    // 1. When we play turning animations\n    // 2. When we use a fallback animation for lower body since movement animation for given weapon is missing (e.g. for crossbows and spellcasting)\n    bool resetIdle = (movement != CharState_None && !isTurning());\n\n    std::string movementAnimName;\n    MWRender::Animation::BlendMask movemask;\n    const StateInfo *movestate;\n\n    movemask = MWRender::Animation::BlendMask_All;\n    movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement));\n    if(movestate != sMovementListEnd)\n    {\n        movementAnimName = movestate->groupname;\n        if(!weapShortGroup.empty())\n        {\n            std::string::size_type swimpos = movementAnimName.find(\"swim\");\n            if (swimpos == std::string::npos)\n            {\n                if (mWeaponType == ESM::Weapon::Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case\n                    movementAnimName = weapShortGroup + movementAnimName;\n                else\n                    movementAnimName += weapShortGroup;\n            }\n\n            if(!mAnimation->hasAnimation(movementAnimName))\n            {\n                movementAnimName = movestate->groupname;\n                if (swimpos == std::string::npos)\n                {\n                    movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);\n\n                    // If we apply movement only for lower body, do not reset idle animations.\n                    // For upper body there will be idle animation.\n                    if (movemask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None)\n                        idle = CharState_Idle;\n\n                    if (movemask == MWRender::Animation::BlendMask_LowerBody)\n                        resetIdle = false;\n                }\n            }\n        }\n    }\n\n    if(force || movement != mMovementState)\n    {\n        mMovementState = movement;\n        if(movestate != sMovementListEnd)\n        {\n            if(!mAnimation->hasAnimation(movementAnimName))\n            {\n                std::string::size_type swimpos = movementAnimName.find(\"swim\");\n                if (swimpos != std::string::npos)\n                {\n                    movementAnimName.erase(swimpos, 4);\n                    if (!weapShortGroup.empty())\n                    {\n                        std::string weapMovementAnimName = movementAnimName + weapShortGroup;\n                        if(mAnimation->hasAnimation(weapMovementAnimName))\n                            movementAnimName = weapMovementAnimName;\n                        else\n                        {\n                            movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);\n                            if (movemask == MWRender::Animation::BlendMask_LowerBody)\n                                resetIdle = false;\n                        }\n                    }\n                }\n\n                if (swimpos == std::string::npos || !mAnimation->hasAnimation(movementAnimName))\n                {\n                    std::string::size_type runpos = movementAnimName.find(\"run\");\n                    if (runpos != std::string::npos)\n                    {\n                        movementAnimName.replace(runpos, runpos+3, \"walk\");\n                        if (!mAnimation->hasAnimation(movementAnimName))\n                            movementAnimName.clear();\n                    }\n                    else\n                        movementAnimName.clear();\n                }\n            }\n        }\n\n        // If we're playing the same animation, start it from the point it ended\n        float startpoint = 0.f;\n        if (!mCurrentMovement.empty() && movementAnimName == mCurrentMovement)\n            mAnimation->getInfo(mCurrentMovement, &startpoint);\n\n        mMovementAnimationControlled = true;\n\n        mAnimation->disable(mCurrentMovement);\n\n        if (!mAnimation->hasAnimation(movementAnimName))\n            movementAnimName.clear();\n\n        mCurrentMovement = movementAnimName;\n        if(!mCurrentMovement.empty())\n        {\n            if (resetIdle)\n            {\n                mAnimation->disable(mCurrentIdle);\n                mIdleState = CharState_None;\n                idle = CharState_None;\n            }\n\n            // For non-flying creatures, MW uses the Walk animation to calculate the animation velocity\n            // even if we are running. This must be replicated, otherwise the observed speed would differ drastically.\n            std::string anim = mCurrentMovement;\n            mAdjustMovementAnimSpeed = true;\n            if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name()\n                    && !(mPtr.get<ESM::Creature>()->mBase->mFlags & ESM::Creature::Flies))\n            {\n                CharacterState walkState = runStateToWalkState(mMovementState);\n                const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState));\n                anim = stateinfo->groupname;\n\n                mMovementAnimSpeed = mAnimation->getVelocity(anim);\n                if (mMovementAnimSpeed <= 1.0f)\n                {\n                    // Another bug: when using a fallback animation (e.g. RunForward as fallback to SwimRunForward),\n                    // then the equivalent Walk animation will not use a fallback, and if that animation doesn't exist\n                    // we will play without any scaling.\n                    // Makes the speed attribute of most water creatures totally useless.\n                    // And again, this can not be fixed without patching game data.\n                    mAdjustMovementAnimSpeed = false;\n                    mMovementAnimSpeed = 1.f;\n                }\n            }\n            else\n            {\n                mMovementAnimSpeed = mAnimation->getVelocity(anim);\n\n                if (mMovementAnimSpeed <= 1.0f)\n                {\n                    // The first person anims don't have any velocity to calculate a speed multiplier from.\n                    // We use the third person velocities instead.\n                    // FIXME: should be pulled from the actual animation, but it is not presently loaded.\n                    bool sneaking = mMovementState == CharState_SneakForward || mMovementState == CharState_SneakBack\n                                 || mMovementState == CharState_SneakLeft    || mMovementState == CharState_SneakRight;\n                    mMovementAnimSpeed = (sneaking ? 33.5452f : (isRunning() ? 222.857f : 154.064f));\n                    mMovementAnimationControlled = false;\n                }\n            }\n\n            mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false,\n                             1.f, \"start\", \"stop\", startpoint, ~0ul, true);\n        }\n        else\n            mMovementState = CharState_None;\n    }\n}\n\nvoid CharacterController::refreshIdleAnims(const std::string& weapShortGroup, CharacterState idle, bool force)\n{\n    // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),\n    // the idle animation should be displayed\n    if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped)\n            || (mMovementState != CharState_None && !isTurning())\n            || mHitState != CharState_None)\n            && !mPtr.getClass().isBipedal(mPtr))\n        idle = CharState_None;\n\n    if(force || idle != mIdleState || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty()))\n    {\n        mIdleState = idle;\n        size_t numLoops = ~0ul;\n\n        std::string idleGroup;\n        MWRender::Animation::AnimPriority idlePriority (Priority_Default);\n        // Only play \"idleswim\" or \"idlesneak\" if they exist. Otherwise, fallback to\n        // \"idle\"+weapon or \"idle\".\n        if(mIdleState == CharState_IdleSwim && mAnimation->hasAnimation(\"idleswim\"))\n        {\n            idleGroup = \"idleswim\";\n            idlePriority = Priority_SwimIdle;\n        }\n        else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation(\"idlesneak\"))\n        {\n            idleGroup = \"idlesneak\";\n            idlePriority[MWRender::Animation::BoneGroup_LowerBody] = Priority_SneakIdleLowerBody;\n        }\n        else if(mIdleState != CharState_None)\n        {\n            idleGroup = \"idle\";\n            if(!weapShortGroup.empty())\n            {\n                idleGroup += weapShortGroup;\n                if(!mAnimation->hasAnimation(idleGroup))\n                {\n                    idleGroup = fallbackShortWeaponGroup(\"idle\");\n                }\n\n                // play until the Loop Stop key 2 to 5 times, then play until the Stop key\n                // this replicates original engine behavior for the \"Idle1h\" 1st-person animation\n                numLoops = 1 + Misc::Rng::rollDice(4); \n            }\n        }\n\n        // There is no need to restart anim if the new and old anims are the same.\n        // Just update a number of loops.\n        float startPoint = 0;\n        if (!mCurrentIdle.empty() && mCurrentIdle == idleGroup)\n        {\n            mAnimation->getInfo(mCurrentIdle, &startPoint);\n        }\n\n        if(!mCurrentIdle.empty())\n            mAnimation->disable(mCurrentIdle);\n\n        mCurrentIdle = idleGroup;\n        if(!mCurrentIdle.empty())\n            mAnimation->play(mCurrentIdle, idlePriority, MWRender::Animation::BlendMask_All, false,\n                             1.0f, \"start\", \"stop\", startPoint, numLoops, true);\n    }\n}\n\nvoid CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force)\n{\n    // If the current animation is persistent, do not touch it\n    if (isPersistentAnimPlaying())\n        return;\n\n    if (mPtr.getClass().isActor())\n        refreshHitRecoilAnims(idle);\n\n    std::string weap;\n    if (mPtr.getClass().hasInventoryStore(mPtr))\n        weap = getWeaponType(mWeaponType)->mShortGroup;\n\n    refreshJumpAnims(weap, jump, idle, force);\n    refreshMovementAnims(weap, movement, idle, force);\n\n    // idle handled last as it can depend on the other states\n    refreshIdleAnims(weap, idle, force);\n}\n\nvoid CharacterController::playDeath(float startpoint, CharacterState death)\n{\n    // Make sure the character was swimming upon death for forward-compatibility\n    const bool wasSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);\n\n    switch (death)\n    {\n    case CharState_SwimDeath:\n        mCurrentDeath = \"swimdeath\";\n        break;\n    case CharState_SwimDeathKnockDown:\n        mCurrentDeath = (wasSwimming ? \"swimdeathknockdown\" : \"deathknockdown\");\n        break;\n    case CharState_SwimDeathKnockOut:\n        mCurrentDeath = (wasSwimming ? \"swimdeathknockout\" : \"deathknockout\");\n        break;\n    case CharState_DeathKnockDown:\n        mCurrentDeath = \"deathknockdown\";\n        break;\n    case CharState_DeathKnockOut:\n        mCurrentDeath = \"deathknockout\";\n        break;\n    default:\n        mCurrentDeath = \"death\" + std::to_string(death - CharState_Death1 + 1);\n    }\n    mDeathState = death;\n\n    mPtr.getClass().getCreatureStats(mPtr).setDeathAnimation(mDeathState - CharState_Death1);\n\n    // For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually.\n    // Note that these animations wouldn't actually be visible (due to the Death animation's priority being higher).\n    // However, they could still trigger text keys, such as Hit events, or sounds.\n    mMovementState = CharState_None;\n    mAnimation->disable(mCurrentMovement);\n    mCurrentMovement = \"\";\n    mUpperBodyState = UpperCharState_Nothing;\n    mAnimation->disable(mCurrentWeapon);\n    mCurrentWeapon = \"\";\n    mHitState = CharState_None;\n    mAnimation->disable(mCurrentHit);\n    mCurrentHit = \"\";\n    mIdleState = CharState_None;\n    mAnimation->disable(mCurrentIdle);\n    mCurrentIdle = \"\";\n    mJumpState = JumpState_None;\n    mAnimation->disable(mCurrentJump);\n    mCurrentJump = \"\";\n    mMovementAnimationControlled = true;\n\n    mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::BlendMask_All,\n                    false, 1.0f, \"start\", \"stop\", startpoint, 0);\n}\n\nCharacterState CharacterController::chooseRandomDeathState() const\n{\n    int selected=0;\n    chooseRandomGroup(\"death\", &selected);\n    return static_cast<CharacterState>(CharState_Death1 + (selected-1));\n}\n\nvoid CharacterController::playRandomDeath(float startpoint)\n{\n    /*\n        Start of tes3mp addition\n\n        If this is a LocalActor or DedicatedActor whose death animation is supposed to be finished,\n        set the startpoint to the animation's end\n    */\n    if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() &&\n        (mwmp::Main::get().getCellController()->isLocalActor(mPtr) || mwmp::Main::get().getCellController()->isDedicatedActor(mPtr)))\n    {\n        startpoint = 1.F;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    if (mPtr == getPlayer())\n    {\n        // The first-person animations do not include death, so we need to\n        // force-switch to third person before playing the death animation.\n        MWBase::Environment::get().getWorld()->useDeathCamera();\n    }\n\n    /*\n        Start tes3mp change (major)\n\n        If this is a DedicatedPlayer, use the deathState received from their PlayerDeath packet\n\n        If this is a DedicatedActor, use the deathState from their ActorDeath packet\n    */\n    if (mwmp::PlayerList::isDedicatedPlayer(mPtr))\n    {\n        mDeathState = static_cast<CharacterState>(mwmp::PlayerList::getPlayer(mPtr)->deathState);\n    }\n    else if (mwmp::Main::get().getCellController()->hasQueuedDeathState(mPtr))\n    {\n        mDeathState = static_cast<CharacterState>(mwmp::Main::get().getCellController()->getQueuedDeathState(mPtr));\n        mwmp::Main::get().getCellController()->clearQueuedDeathState(mPtr);\n    }\n    else if(mHitState == CharState_SwimKnockDown && mAnimation->hasAnimation(\"swimdeathknockdown\"))\n    /*\n        End of tes3mp change (major)\n    */\n    {\n        mDeathState = CharState_SwimDeathKnockDown;\n    }\n    else if(mHitState == CharState_SwimKnockOut && mAnimation->hasAnimation(\"swimdeathknockout\"))\n    {\n        mDeathState = CharState_SwimDeathKnockOut;\n    }\n    else if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation(\"swimdeath\"))\n    {\n        mDeathState = CharState_SwimDeath;\n    }\n    else if (mHitState == CharState_KnockDown && mAnimation->hasAnimation(\"deathknockdown\"))\n    {\n        mDeathState = CharState_DeathKnockDown;\n    }\n    else if (mHitState == CharState_KnockOut && mAnimation->hasAnimation(\"deathknockout\"))\n    {\n        mDeathState = CharState_DeathKnockOut;\n    }\n    else\n    {\n        mDeathState = chooseRandomDeathState();\n    }\n\n    /*\n        Start of tes3mp addition\n\n        If this is the local player, send a PlayerDeath packet with the decided-upon\n        death animation\n\n        If this is a local actor, send an ActorDeath packet with the animation\n    */\n    if (mPtr == getPlayer())\n    {\n        mwmp::Main::get().getLocalPlayer()->sendDeath(mDeathState);\n    }\n    else if (!mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() && mwmp::Main::get().getCellController()->isLocalActor(mPtr))\n    {\n        mwmp::Main::get().getCellController()->getLocalActor(mPtr)->sendDeath(mDeathState);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    // Do not interrupt scripted animation by death\n    if (isPersistentAnimPlaying())\n        return;\n\n    playDeath(startpoint, mDeathState);\n}\n\nstd::string CharacterController::chooseRandomAttackAnimation() const\n{\n    std::string result;\n    bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);\n\n    if (isSwimming)\n        result = chooseRandomGroup(\"swimattack\");\n\n    if (!isSwimming || !mAnimation->hasAnimation(result))\n        result = chooseRandomGroup(\"attack\");\n\n    return result;\n}\n\nCharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)\n    : mPtr(ptr)\n    , mWeapon(MWWorld::Ptr())\n    , mAnimation(anim)\n    , mIdleState(CharState_None)\n    , mMovementState(CharState_None)\n    , mMovementAnimSpeed(0.f)\n    , mAdjustMovementAnimSpeed(false)\n    , mHasMovedInXY(false)\n    , mMovementAnimationControlled(true)\n    , mDeathState(CharState_None)\n    , mFloatToSurface(true)\n    , mHitState(CharState_None)\n    , mUpperBodyState(UpperCharState_Nothing)\n    , mJumpState(JumpState_None)\n    , mWeaponType(ESM::Weapon::None)\n    , mAttackStrength(0.f)\n    , mSkipAnim(false)\n    , mSecondsOfSwimming(0)\n    , mSecondsOfRunning(0)\n    , mTurnAnimationThreshold(0)\n    , mAttackingOrSpell(false)\n    , mCastingManualSpell(false)\n    , mTimeUntilWake(0.f)\n    , mIsMovingBackward(false)\n{\n    if(!mAnimation)\n        return;\n\n    mAnimation->setTextKeyListener(this);\n\n    const MWWorld::Class &cls = mPtr.getClass();\n    if(cls.isActor())\n    {\n        /* Accumulate along X/Y only for now, until we can figure out how we should\n         * handle knockout and death which moves the character down. */\n        mAnimation->setAccumulation(osg::Vec3f(1.0f, 1.0f, 0.0f));\n\n        if (cls.hasInventoryStore(mPtr))\n        {\n            getActiveWeapon(mPtr, &mWeaponType);\n            if (mWeaponType != ESM::Weapon::None)\n            {\n                mUpperBodyState = UpperCharState_WeapEquiped;\n                mCurrentWeapon = getWeaponAnimation(mWeaponType);\n            }\n\n            if(mWeaponType != ESM::Weapon::None && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::HandToHand)\n            {\n                mAnimation->showWeapons(true);\n                // Note: controllers for ranged weapon should use time for beginning of animation to play shooting properly,\n                // for other weapons they should use absolute time. Some mods rely on this behaviour (to rotate throwing projectiles, for example)\n                ESM::WeaponType::Class weaponClass = getWeaponType(mWeaponType)->mWeaponClass;\n                bool useRelativeDuration = weaponClass == ESM::WeaponType::Ranged;\n                mAnimation->setWeaponGroup(mCurrentWeapon, useRelativeDuration);\n            }\n\n            mAnimation->showCarriedLeft(updateCarriedLeftVisible(mWeaponType));\n        }\n\n        if(!cls.getCreatureStats(mPtr).isDead())\n        {\n            mIdleState = CharState_Idle;\n            if (cls.getCreatureStats(mPtr).getFallHeight() > 0)\n                mJumpState = JumpState_InAir;\n        }\n        else\n        {\n            const MWMechanics::CreatureStats& cStats = mPtr.getClass().getCreatureStats(mPtr);\n            if (cStats.isDeathAnimationFinished())\n            {\n                // Set the death state, but don't play it yet\n                // We will play it in the first frame, but only if no script set the skipAnim flag\n                signed char deathanim = cStats.getDeathAnimation();\n                if (deathanim == -1)\n                    mDeathState = chooseRandomDeathState();\n                else\n                    mDeathState = static_cast<CharacterState>(CharState_Death1 + deathanim);\n\n                mFloatToSurface = false;\n            }\n            // else: nothing to do, will detect death in the next frame and start playing death animation\n        }\n    }\n    else\n    {\n        /* Don't accumulate with non-actors. */\n        mAnimation->setAccumulation(osg::Vec3f(0.f, 0.f, 0.f));\n\n        mIdleState = CharState_Idle;\n    }\n\n    // Do not update animation status for dead actors\n    if(mDeathState == CharState_None && (!cls.isActor() || !cls.getCreatureStats(mPtr).isDead()))\n        refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);\n\n    mAnimation->runAnimation(0.f);\n\n    unpersistAnimationState();\n}\n\nCharacterController::~CharacterController()\n{\n    if (mAnimation)\n    {\n        persistAnimationState();\n        mAnimation->setTextKeyListener(nullptr);\n    }\n}\n\nvoid split(const std::string &s, char delim, std::vector<std::string> &elems) {\n    std::stringstream ss(s);\n    std::string item;\n    while (std::getline(ss, item, delim)) {\n        elems.push_back(item);\n    }\n}\n\nvoid CharacterController::handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map)\n{\n    const std::string &evt = key->second;\n\n    if(evt.compare(0, 7, \"sound: \") == 0)\n    {\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f);\n        return;\n    }\n    if(evt.compare(0, 10, \"soundgen: \") == 0)\n    {\n        std::string soundgen = evt.substr(10);\n\n        // The event can optionally contain volume and pitch modifiers\n        float volume=1.f, pitch=1.f;\n        if (soundgen.find(' ') != std::string::npos)\n        {\n            std::vector<std::string> tokens;\n            split(soundgen, ' ', tokens);\n            soundgen = tokens[0];\n            if (tokens.size() >= 2)\n            {\n                std::stringstream stream;\n                stream << tokens[1];\n                stream >> volume;\n            }\n            if (tokens.size() >= 3)\n            {\n                std::stringstream stream;\n                stream << tokens[2];\n                stream >> pitch;\n            }\n        }\n\n        std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen);\n        if(!sound.empty())\n        {\n            MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n            // NB: landing sound is not played for NPCs here\n            if(soundgen == \"left\" || soundgen == \"right\" || soundgen == \"land\")\n            {\n                sndMgr->playSound3D(mPtr, sound, volume, pitch, MWSound::Type::Foot,\n                                    MWSound::PlayMode::NoPlayerLocal);\n            }\n            else\n            {\n                sndMgr->playSound3D(mPtr, sound, volume, pitch);\n            }\n        }\n        return;\n    }\n\n    if(evt.compare(0, groupname.size(), groupname) != 0 ||\n       evt.compare(groupname.size(), 2, \": \") != 0)\n    {\n        // Not ours, skip it\n        return;\n    }\n    size_t off = groupname.size()+2;\n    size_t len = evt.size() - off;\n\n    if(groupname == \"shield\" && evt.compare(off, len, \"equip attach\") == 0)\n        mAnimation->showCarriedLeft(true);\n    else if(groupname == \"shield\" && evt.compare(off, len, \"unequip detach\") == 0)\n        mAnimation->showCarriedLeft(false);\n    else if(evt.compare(off, len, \"equip attach\") == 0)\n        mAnimation->showWeapons(true);\n    else if(evt.compare(off, len, \"unequip detach\") == 0)\n        mAnimation->showWeapons(false);\n    else if(evt.compare(off, len, \"chop hit\") == 0)\n        mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop);\n    else if(evt.compare(off, len, \"slash hit\") == 0)\n        mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash);\n    else if(evt.compare(off, len, \"thrust hit\") == 0)\n        mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);\n    else if(evt.compare(off, len, \"hit\") == 0)\n    {\n        if (groupname == \"attack1\" || groupname == \"swimattack1\")\n            mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop);\n        else if (groupname == \"attack2\" || groupname == \"swimattack2\")\n            mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash);\n        else if (groupname == \"attack3\" || groupname == \"swimattack3\")\n            mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);\n        else\n            mPtr.getClass().hit(mPtr, mAttackStrength);\n    }\n    else if (!groupname.empty()\n             && (groupname.compare(0, groupname.size()-1, \"attack\") == 0 || groupname.compare(0, groupname.size()-1, \"swimattack\") == 0)\n             && evt.compare(off, len, \"start\") == 0)\n    {\n        std::multimap<float, std::string>::const_iterator hitKey = key;\n\n        // Not all animations have a hit key defined. If there is none, the hit happens with the start key.\n        bool hasHitKey = false;\n        while (hitKey != map.end())\n        {\n            if (hitKey->second == groupname + \": hit\")\n            {\n                hasHitKey = true;\n                break;\n            }\n            if (hitKey->second == groupname + \": stop\")\n                break;\n            ++hitKey;\n        }\n        if (!hasHitKey)\n        {\n            if (groupname == \"attack1\" || groupname == \"swimattack1\")\n                mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop);\n            else if (groupname == \"attack2\" || groupname == \"swimattack2\")\n                mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash);\n            else if (groupname == \"attack3\" || groupname == \"swimattack3\")\n                mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);\n        }\n    }\n    else if (evt.compare(off, len, \"shoot attach\") == 0)\n        mAnimation->attachArrow();\n    else if (evt.compare(off, len, \"shoot release\") == 0)\n        mAnimation->releaseArrow(mAttackStrength);\n    else if (evt.compare(off, len, \"shoot follow attach\") == 0)\n        mAnimation->attachArrow();\n\n    else if (groupname == \"spellcast\" && evt.substr(evt.size()-7, 7) == \"release\"\n             // Make sure this key is actually for the RangeType we are casting. The flame atronach has\n             // the same animation for all range types, so there are 3 \"release\" keys on the same time, one for each range type.\n             && evt.compare(off, len, mAttackType + \" release\") == 0)\n    {\n        /*\n            Start of tes3mp change (major)\n\n            Make the completion of the spellcast animation actually cast spells only for the\n            local player and local actors, relying on Cast packets to cause spells to be cast\n            for dedicated players and actors\n        */\n        if (mPtr == getPlayer() || mwmp::Main::get().getCellController()->isLocalActor(mPtr))\n        {\n            MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell);\n            mCastingManualSpell = false;\n        }\n        /*\n            End of tes3mp change (major)\n        */\n    }\n\n    else if (groupname == \"shield\" && evt.compare(off, len, \"block hit\") == 0)\n        mPtr.getClass().block(mPtr);\n    else if (groupname == \"containeropen\" && evt.compare(off, len, \"loot\") == 0)\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container, mPtr);\n}\n\nvoid CharacterController::updatePtr(const MWWorld::Ptr &ptr)\n{\n    mPtr = ptr;\n}\n\nvoid CharacterController::updateIdleStormState(bool inwater)\n{\n    if (!mAnimation->hasAnimation(\"idlestorm\") || mUpperBodyState != UpperCharState_Nothing || inwater)\n    {\n        mAnimation->disable(\"idlestorm\");\n        return;\n    }\n\n    if (MWBase::Environment::get().getWorld()->isInStorm())\n    {\n        osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection();\n        osg::Vec3f characterDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);\n        stormDirection.normalize();\n        characterDirection.normalize();\n        if (stormDirection * characterDirection < -0.5f)\n        {\n            if (!mAnimation->isPlaying(\"idlestorm\"))\n            {\n                int mask = MWRender::Animation::BlendMask_Torso | MWRender::Animation::BlendMask_RightArm;\n                mAnimation->play(\"idlestorm\", Priority_Storm, mask, true, 1.0f, \"start\", \"stop\", 0.0f, ~0ul);\n            }\n            else\n            {\n                mAnimation->setLoopingEnabled(\"idlestorm\", true);\n            }\n            return;\n        }\n    }\n\n    if (mAnimation->isPlaying(\"idlestorm\"))\n    {\n        mAnimation->setLoopingEnabled(\"idlestorm\", false);\n    }\n}\n\nbool CharacterController::updateCreatureState()\n{\n    const MWWorld::Class &cls = mPtr.getClass();\n    CreatureStats &stats = cls.getCreatureStats(mPtr);\n\n    int weapType = ESM::Weapon::None;\n    if(stats.getDrawState() == DrawState_Weapon)\n        weapType = ESM::Weapon::HandToHand;\n    else if (stats.getDrawState() == DrawState_Spell)\n        weapType = ESM::Weapon::Spell;\n\n    if (weapType != mWeaponType)\n    {\n        mWeaponType = weapType;\n        if (mAnimation->isPlaying(mCurrentWeapon))\n            mAnimation->disable(mCurrentWeapon);\n    }\n\n    if(mAttackingOrSpell)\n    {\n        if(mUpperBodyState == UpperCharState_Nothing && mHitState == CharState_None)\n        {\n            MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);\n\n            std::string startKey = \"start\";\n            std::string stopKey = \"stop\";\n            if (weapType == ESM::Weapon::Spell)\n            {\n                const std::string spellid = stats.getSpells().getSelectedSpell();\n                bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);\n\n                if (!spellid.empty() && canCast)\n                {\n                    /*\n                        Start of tes3mp addition\n\n                        If this mPtr belongs to a LocalPlayer or LocalActor, get their Attack and prepare\n                        it for sending\n                    */\n                    mwmp::Cast *localCast = MechanicsHelper::getLocalCast(mPtr);\n\n                    if (localCast)\n                    {\n                        MechanicsHelper::resetCast(localCast);\n                        localCast->type = mwmp::Cast::REGULAR;\n                        localCast->spellId = spellid;\n                        localCast->pressed = true;\n                        localCast->shouldSend = true;\n\n                        // Mark the attack as instant if there is no spellcast animation\n                        if (!mAnimation->hasAnimation(\"spellcast\"))\n                            localCast->instant = true;\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell);\n                    cast.playSpellCastingEffects(spellid, false);\n\n                    if (!mAnimation->hasAnimation(\"spellcast\"))\n                    {\n                        MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell); // No \"release\" text key to use, so cast immediately\n                        mCastingManualSpell = false;\n                    }\n                    else\n                    {\n                        const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellid);\n                        const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0);\n\n                        switch(effectentry.mRange)\n                        {\n                            case 0: mAttackType = \"self\"; break;\n                            case 1: mAttackType = \"touch\"; break;\n                            case 2: mAttackType = \"target\"; break;\n                        }\n\n                        startKey = mAttackType + \" \" + startKey;\n                        stopKey = mAttackType + \" \" + stopKey;\n                        mCurrentWeapon = \"spellcast\";\n                    }\n                }\n                else\n                    mCurrentWeapon = \"\";\n            }\n\n            if (weapType != ESM::Weapon::Spell || !mAnimation->hasAnimation(\"spellcast\")) // Not all creatures have a dedicated spellcast animation\n            {\n                mCurrentWeapon = chooseRandomAttackAnimation();\n            }\n\n            if (!mCurrentWeapon.empty())\n            {\n                mAnimation->play(mCurrentWeapon, Priority_Weapon,\n                                 MWRender::Animation::BlendMask_All, true,\n                                 1, startKey, stopKey,\n                                 0.0f, 0);\n                mUpperBodyState = UpperCharState_StartToMinAttack;\n\n                mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability());\n\n                if (weapType == ESM::Weapon::HandToHand)\n                    playSwishSound(0.0f);\n            }\n        }\n\n        mAttackingOrSpell = false;\n    }\n\n    bool animPlaying = mAnimation->getInfo(mCurrentWeapon);\n    if (!animPlaying)\n        mUpperBodyState = UpperCharState_Nothing;\n    return false;\n}\n\nbool CharacterController::updateCarriedLeftVisible(const int weaptype) const\n{\n    // Shields/torches shouldn't be visible during any operation involving two hands\n    // There seems to be no text keys for this purpose, except maybe for \"[un]equip start/stop\",\n    // but they are also present in weapon drawing animation.\n    return mAnimation->updateCarriedLeftVisible(weaptype);\n}\n\nbool CharacterController::updateWeaponState(CharacterState& idle)\n{\n    const MWWorld::Class &cls = mPtr.getClass();\n    CreatureStats &stats = cls.getCreatureStats(mPtr);\n    int weaptype = ESM::Weapon::None;\n    if(stats.getDrawState() == DrawState_Weapon)\n        weaptype = ESM::Weapon::HandToHand;\n    else if (stats.getDrawState() == DrawState_Spell)\n        weaptype = ESM::Weapon::Spell;\n\n    const bool isWerewolf = cls.isNpc() && cls.getNpcStats(mPtr).isWerewolf();\n\n    std::string upSoundId;\n    std::string downSoundId;\n    bool weaponChanged = false;\n    if (mPtr.getClass().hasInventoryStore(mPtr))\n    {\n        MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);\n        MWWorld::ContainerStoreIterator weapon = getActiveWeapon(mPtr, &weaptype);\n        if(stats.getDrawState() == DrawState_Spell)\n            weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n\n        if(weapon != inv.end() && mWeaponType != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::Spell && weaptype != ESM::Weapon::None)\n            upSoundId = weapon->getClass().getUpSoundId(*weapon);\n\n        if(weapon != inv.end() && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None)\n            downSoundId = weapon->getClass().getDownSoundId(*weapon);\n\n        // weapon->HtH switch: weapon is empty already, so we need to take sound from previous weapon\n        if(weapon == inv.end() && !mWeapon.isEmpty() && weaptype == ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell)\n            downSoundId = mWeapon.getClass().getDownSoundId(mWeapon);\n\n        MWWorld::Ptr newWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr();\n\n        if (mWeapon != newWeapon)\n        {\n            mWeapon = newWeapon;\n            weaponChanged = true;\n        }\n    }\n\n    // For biped actors, blend weapon animations with lower body animations with higher priority\n    MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon);\n    if (mPtr.getClass().isBipedal(mPtr))\n        priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;\n\n    bool forcestateupdate = false;\n\n    // We should not play equipping animation and sound during weapon->weapon transition\n    bool isStillWeapon = weaptype != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::Spell && weaptype != ESM::Weapon::None &&\n                            mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None;\n\n    // If the current weapon type was changed in the middle of attack (e.g. by Equip console command or when bound spell expires),\n    // we should force actor to the \"weapon equipped\" state, interrupt attack and update animations.\n    if (isStillWeapon && mWeaponType != weaptype && mUpperBodyState > UpperCharState_WeapEquiped)\n    {\n        forcestateupdate = true;\n        mUpperBodyState = UpperCharState_WeapEquiped;\n        mAttackingOrSpell = false;\n        mAnimation->disable(mCurrentWeapon);\n        mAnimation->showWeapons(true);\n        if (mPtr == getPlayer())\n            MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);\n    }\n\n    if(!isKnockedOut() && !isKnockedDown() && !isRecovery())\n    {\n        std::string weapgroup;\n        if ((!isWerewolf || mWeaponType != ESM::Weapon::Spell)\n            && weaptype != mWeaponType\n            && mUpperBodyState != UpperCharState_UnEquipingWeap\n            && !isStillWeapon)\n        {\n            // We can not play un-equip animation if weapon changed since last update\n            if (!weaponChanged)\n            {\n                // Note: we do not disable unequipping animation automatically to avoid body desync\n                weapgroup = getWeaponAnimation(mWeaponType);\n                int unequipMask = MWRender::Animation::BlendMask_All;\n                bool useShieldAnims = mAnimation->useShieldAnimations();\n                if (useShieldAnims && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && !(mWeaponType == ESM::Weapon::None && weaptype == ESM::Weapon::Spell))\n                {\n                    unequipMask = unequipMask |~MWRender::Animation::BlendMask_LeftArm;\n                    mAnimation->play(\"shield\", Priority_Block,\n                                MWRender::Animation::BlendMask_LeftArm, true,\n                                1.0f, \"unequip start\", \"unequip stop\", 0.0f, 0);\n                }\n                else if (mWeaponType == ESM::Weapon::HandToHand)\n                    mAnimation->showCarriedLeft(false);\n\n                mAnimation->play(weapgroup, priorityWeapon, unequipMask, false,\n                                1.0f, \"unequip start\", \"unequip stop\", 0.0f, 0);\n                mUpperBodyState = UpperCharState_UnEquipingWeap;\n\n                mAnimation->detachArrow();\n\n                // If we do not have the \"unequip detach\" key, hide weapon manually.\n                if (mAnimation->getTextKeyTime(weapgroup+\": unequip detach\") < 0)\n                    mAnimation->showWeapons(false);\n            }\n\n            if(!downSoundId.empty())\n            {\n                MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n                sndMgr->playSound3D(mPtr, downSoundId, 1.0f, 1.0f);\n            }\n        }\n\n        float complete;\n        bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);\n        if (!animPlaying || complete >= 1.0f)\n        {\n            // Weapon is changed, no current animation (e.g. unequipping or attack).\n            // Start equipping animation now.\n            if (weaptype != mWeaponType)\n            {\n                forcestateupdate = true;\n                bool useShieldAnims = mAnimation->useShieldAnimations();\n                if (!useShieldAnims)\n                    mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));\n\n                weapgroup = getWeaponAnimation(weaptype);\n\n                // Note: controllers for ranged weapon should use time for beginning of animation to play shooting properly,\n                // for other weapons they should use absolute time. Some mods rely on this behaviour (to rotate throwing projectiles, for example)\n                ESM::WeaponType::Class weaponClass = getWeaponType(weaptype)->mWeaponClass;\n                bool useRelativeDuration = weaponClass == ESM::WeaponType::Ranged;\n                mAnimation->setWeaponGroup(weapgroup, useRelativeDuration);\n\n                if (!isStillWeapon)\n                {\n                    mAnimation->disable(mCurrentWeapon);\n                    if (weaptype != ESM::Weapon::None)\n                    {\n                        mAnimation->showWeapons(false);\n                        int equipMask = MWRender::Animation::BlendMask_All;\n                        if (useShieldAnims && weaptype != ESM::Weapon::Spell)\n                        {\n                            equipMask = equipMask |~MWRender::Animation::BlendMask_LeftArm;\n                            mAnimation->play(\"shield\", Priority_Block,\n                                        MWRender::Animation::BlendMask_LeftArm, true,\n                                        1.0f, \"equip start\", \"equip stop\", 0.0f, 0);\n                        }\n\n                        mAnimation->play(weapgroup, priorityWeapon, equipMask, true,\n                                        1.0f, \"equip start\", \"equip stop\", 0.0f, 0);\n                        mUpperBodyState = UpperCharState_EquipingWeap;\n\n                        // If we do not have the \"equip attach\" key, show weapon manually.\n                        if (weaptype != ESM::Weapon::Spell)\n                        {\n                            if (mAnimation->getTextKeyTime(weapgroup+\": equip attach\") < 0)\n                                mAnimation->showWeapons(true);\n                        }\n                    }\n                }\n\n                if(isWerewolf)\n                {\n                    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n                    const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfEquip\");\n                    if(sound)\n                    {\n                        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n                        sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);\n                    }\n                }\n\n                mWeaponType = weaptype;\n                mCurrentWeapon = getWeaponAnimation(mWeaponType);\n\n                if(!upSoundId.empty() && !isStillWeapon)\n                {\n                    MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n                    sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);\n                }\n            }\n\n            // Make sure that we disabled unequipping animation\n            if (mUpperBodyState == UpperCharState_UnEquipingWeap)\n            {\n                mUpperBodyState = UpperCharState_Nothing;\n                mAnimation->disable(mCurrentWeapon);\n                mWeaponType = ESM::Weapon::None;\n                mCurrentWeapon = getWeaponAnimation(mWeaponType);\n            }\n        }\n    }\n\n    if(isWerewolf)\n    {\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        if(cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run)\n            && mHasMovedInXY\n            && !MWBase::Environment::get().getWorld()->isSwimming(mPtr)\n            && mWeaponType == ESM::Weapon::None)\n        {\n            if(!sndMgr->getSoundPlaying(mPtr, \"WolfRun\"))\n                sndMgr->playSound3D(mPtr, \"WolfRun\", 1.0f, 1.0f, MWSound::Type::Sfx,\n                                    MWSound::PlayMode::Loop);\n        }\n        else\n            sndMgr->stopSound3D(mPtr, \"WolfRun\");\n    }\n\n    // Cancel attack if we no longer have ammunition\n    bool ammunition = true;\n    bool isWeapon = false;\n    float weapSpeed = 1.f;\n    if (mPtr.getClass().hasInventoryStore(mPtr))\n    {\n        MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);\n        MWWorld::ConstContainerStoreIterator weapon = getActiveWeapon(mPtr, &weaptype);\n        isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name());\n        if (isWeapon)\n        {\n            weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed;\n            MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n            int ammotype = getWeaponType(weapon->get<ESM::Weapon>()->mBase->mData.mType)->mAmmoType;\n            if (ammotype != ESM::Weapon::None && (ammo == inv.end() || ammo->get<ESM::Weapon>()->mBase->mData.mType != ammotype))\n                ammunition = false;\n        }\n\n        if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped)\n        {\n            mAnimation->disable(mCurrentWeapon);\n            mUpperBodyState = UpperCharState_WeapEquiped;\n        }\n    }\n\n    // Combat for actors with persistent animations obviously will be buggy\n    if (isPersistentAnimPlaying())\n        return forcestateupdate;\n\n    float complete;\n    bool animPlaying;\n    ESM::WeaponType::Class weapclass = getWeaponType(mWeaponType)->mWeaponClass;\n    if(mAttackingOrSpell)\n    {\n        MWWorld::Ptr player = getPlayer();\n\n        bool resetIdle = ammunition;\n        if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block))\n        {\n            MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);\n            mAttackStrength = 0;\n\n            // Randomize attacks for non-bipedal creatures with Weapon flag\n            if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() &&\n                !mPtr.getClass().isBipedal(mPtr) &&\n                (!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon)))\n            {\n                mCurrentWeapon = chooseRandomAttackAnimation();\n            }\n\n            if(mWeaponType == ESM::Weapon::Spell)\n            {\n                // Unset casting flag, otherwise pressing the mouse button down would\n                // continue casting every frame if there is no animation\n                mAttackingOrSpell = false;\n                if (mPtr == player)\n                {\n                    MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);\n\n                    // For the player, set the spell we want to cast\n                    // This has to be done at the start of the casting animation,\n                    // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation)\n                    std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell();\n                    stats.getSpells().setSelectedSpell(selectedSpell);\n                }\n                std::string spellid = stats.getSpells().getSelectedSpell();\n                bool isMagicItem = false;\n                bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);\n\n                if (spellid.empty())\n                {\n                    if (mPtr.getClass().hasInventoryStore(mPtr))\n                    {\n                        MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n                        if (inv.getSelectedEnchantItem() != inv.end())\n                        {\n                            const MWWorld::Ptr& enchantItem = *inv.getSelectedEnchantItem();\n                            spellid = enchantItem.getClass().getEnchantment(enchantItem);\n                            isMagicItem = true;\n                        }\n                    }\n                }\n\n                static const bool useCastingAnimations = Settings::Manager::getBool(\"use magic item animations\", \"Game\");\n                if (isMagicItem && !useCastingAnimations)\n                {\n                    // Enchanted items by default do not use casting animations\n                    MWBase::Environment::get().getWorld()->castSpell(mPtr);\n                    resetIdle = false;\n                }\n                else if(!spellid.empty() && canCast)\n                {\n                    /*\n                        Start of tes3mp addition\n\n                        If this mPtr belongs to a LocalPlayer or LocalActor, get their Cast and prepare\n                        it for sending\n                    */\n                    mwmp::Cast *localCast = MechanicsHelper::getLocalCast(mPtr);\n\n                    if (localCast)\n                    {\n                        MechanicsHelper::resetCast(localCast);\n                        localCast->type = mwmp::Cast::REGULAR;\n                        localCast->spellId = spellid;\n                        localCast->pressed = true;\n                        localCast->shouldSend = true;\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell);\n                    cast.playSpellCastingEffects(spellid, isMagicItem);\n\n                    std::vector<ESM::ENAMstruct> effects;\n                    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n                    if (isMagicItem)\n                    {\n                        const ESM::Enchantment *enchantment = store.get<ESM::Enchantment>().find(spellid);\n                        effects = enchantment->mEffects.mList;\n                    }\n                    else\n                    {\n                        const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);\n                        effects = spell->mEffects.mList;\n                    }\n\n                    const ESM::MagicEffect *effect = store.get<ESM::MagicEffect>().find(effects.back().mEffectID); // use last effect of list for color of VFX_Hands\n\n                    const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (\"VFX_Hands\");\n\n                    for (size_t iter = 0; iter < effects.size(); ++iter) // play hands vfx for each effect\n                    {\n                        if (mAnimation->getNode(\"Bip01 L Hand\"))\n                            mAnimation->addEffect(\"meshes\\\\\" + castStatic->mModel, -1, false, \"Bip01 L Hand\", effect->mParticle);\n\n                        if (mAnimation->getNode(\"Bip01 R Hand\"))\n                            mAnimation->addEffect(\"meshes\\\\\" + castStatic->mModel, -1, false, \"Bip01 R Hand\", effect->mParticle);\n                    }\n\n                    const ESM::ENAMstruct &firstEffect = effects.at(0); // first effect used for casting animation\n\n                    std::string startKey;\n                    std::string stopKey;\n                    if (isRandomAttackAnimation(mCurrentWeapon))\n                    {\n                        startKey = \"start\";\n                        stopKey = \"stop\";\n                        MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell); // No \"release\" text key to use, so cast immediately\n                        mCastingManualSpell = false;\n                    }\n                    else\n                    {\n                        switch(firstEffect.mRange)\n                        {\n                            case 0: mAttackType = \"self\"; break;\n                            case 1: mAttackType = \"touch\"; break;\n                            case 2: mAttackType = \"target\"; break;\n                        }\n\n                        startKey = mAttackType+\" start\";\n                        stopKey = mAttackType+\" stop\";\n                    }\n\n                    mAnimation->play(mCurrentWeapon, priorityWeapon,\n                                     MWRender::Animation::BlendMask_All, true,\n                                     1, startKey, stopKey,\n                                     0.0f, 0);\n                    mUpperBodyState = UpperCharState_CastingSpell;\n                }\n                else\n                {\n                    resetIdle = false;\n                }\n            }\n            else if(mWeaponType == ESM::Weapon::PickProbe)\n            {\n                MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n                MWWorld::Ptr item = *weapon;\n                // TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes.\n                MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject();\n                std::string resultMessage, resultSound;\n\n                if(!target.isEmpty())\n                {\n                    if(item.getTypeName() == typeid(ESM::Lockpick).name())\n                        Security(mPtr).pickLock(target, item, resultMessage, resultSound);\n                    else if(item.getTypeName() == typeid(ESM::Probe).name())\n                        Security(mPtr).probeTrap(target, item, resultMessage, resultSound);\n                }\n                mAnimation->play(mCurrentWeapon, priorityWeapon,\n                                 MWRender::Animation::BlendMask_All, true,\n                                 1.0f, \"start\", \"stop\", 0.0, 0);\n                mUpperBodyState = UpperCharState_FollowStartToFollowStop;\n\n                if(!resultMessage.empty())\n                    MWBase::Environment::get().getWindowManager()->messageBox(resultMessage);\n                if(!resultSound.empty())\n                    MWBase::Environment::get().getSoundManager()->playSound3D(target, resultSound,\n                                                                              1.0f, 1.0f);\n            }\n            else if (ammunition)\n            {\n                std::string startKey;\n                std::string stopKey;\n\n                if(weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown)\n                {\n                    mAttackType = \"shoot\";\n                    startKey = mAttackType+\" start\";\n                    stopKey = mAttackType+\" min attack\";\n                }\n                else if (isRandomAttackAnimation(mCurrentWeapon))\n                {\n                    startKey = \"start\";\n                    stopKey = \"stop\";\n                }\n                else\n                {\n                    if(mPtr == getPlayer())\n                    {\n                        if (Settings::Manager::getBool(\"best attack\", \"Game\"))\n                        {\n                            if (isWeapon)\n                            {\n                                MWWorld::ConstContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n                                mAttackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);\n                            }\n                            else\n                            {\n                                // There is no \"best attack\" for Hand-to-Hand\n                                setAttackTypeRandomly(mAttackType);\n                            }\n                        }\n                        else\n                        {\n                            setAttackTypeBasedOnMovement();\n                        }\n\n                        /*\n                            Start of tes3mp addition\n\n                            Record the attack animation chosen so we can send it in the next PlayerAttack packet\n                        */\n                        mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(mPtr);\n\n                        if (localAttack)\n                            localAttack->attackAnimation = mAttackType;\n                        /*\n                            End of tes3mp addition\n                        */\n                    }\n                    /*\n                        Start of tes3mp addition\n\n                        If this is a DedicatedPlayer or DedicatedActor, use the attack animation received\n                        in the latest Attack packet about them\n                    */\n                    else\n                    {\n                        mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(mPtr);\n\n                        if (dedicatedAttack)\n                            mAttackType = dedicatedAttack->attackAnimation;\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    // else if (mPtr != getPlayer()) use mAttackType set by AiCombat\n                    startKey = mAttackType+\" start\";\n                    stopKey = mAttackType+\" min attack\";\n                }\n\n                mAnimation->play(mCurrentWeapon, priorityWeapon,\n                                 MWRender::Animation::BlendMask_All, false,\n                                 weapSpeed, startKey, stopKey,\n                                 0.0f, 0);\n                mUpperBodyState = UpperCharState_StartToMinAttack;\n            }\n        }\n\n        // We should not break swim and sneak animations\n        if (resetIdle &&\n            idle != CharState_IdleSneak && idle != CharState_IdleSwim &&\n            mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)\n        {\n            mAnimation->disable(mCurrentIdle);\n            mIdleState = CharState_None;\n        }\n\n        animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);\n        if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())\n            mAttackStrength = complete;\n    }\n    else\n    {\n        animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);\n        if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())\n        {\n            float attackStrength = complete;\n            float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+\": \"+mAttackType+\" \"+\"min attack\");\n            float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+\": \"+mAttackType+\" \"+\"max attack\");\n            if (minAttackTime == maxAttackTime)\n            {\n                // most creatures don't actually have an attack wind-up animation, so use a uniform random value\n                // (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings)\n                // Note: vanilla MW uses a random value for *all* non-player actors, but we probably don't need to go that far.\n                attackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability());\n            }\n\n            if(weapclass != ESM::WeaponType::Ranged && weapclass != ESM::WeaponType::Thrown)\n            {\n                MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n\n                if(isWerewolf)\n                {\n                    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n                    const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfSwing\");\n                    if(sound)\n                        sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);\n                }\n                else\n                {\n                    playSwishSound(attackStrength);\n                }\n            }\n            mAttackStrength = attackStrength;\n\n            mAnimation->disable(mCurrentWeapon);\n            mAnimation->play(mCurrentWeapon, priorityWeapon,\n                             MWRender::Animation::BlendMask_All, false,\n                             weapSpeed, mAttackType+\" max attack\", mAttackType+\" min hit\",\n                             1.0f-complete, 0);\n\n            complete = 0.f;\n            mUpperBodyState = UpperCharState_MaxAttackToMinHit;\n        }\n        else if (isKnockedDown())\n        {\n            if (mUpperBodyState > UpperCharState_WeapEquiped)\n            {\n                mUpperBodyState = UpperCharState_WeapEquiped;\n                if (mWeaponType > ESM::Weapon::None)\n                    mAnimation->showWeapons(true);\n            }\n            mAnimation->disable(mCurrentWeapon);\n        }\n    }\n\n    mAnimation->setPitchFactor(0.f);\n    if (weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown)\n    {\n        switch (mUpperBodyState)\n        {\n        case UpperCharState_StartToMinAttack:\n            mAnimation->setPitchFactor(complete);\n            break;\n        case UpperCharState_MinAttackToMaxAttack:\n        case UpperCharState_MaxAttackToMinHit:\n        case UpperCharState_MinHitToHit:\n            mAnimation->setPitchFactor(1.f);\n            break;\n        case UpperCharState_FollowStartToFollowStop:\n            if (animPlaying)\n            {\n                // technically we do not need a pitch for crossbow reload animation,\n                // but we should avoid abrupt repositioning\n                if (mWeaponType == ESM::Weapon::MarksmanCrossbow)\n                    mAnimation->setPitchFactor(std::max(0.f, 1.f-complete*10.f));\n                else\n                    mAnimation->setPitchFactor(1.f-complete);\n            }\n            break;\n        default:\n            break;\n        }\n    }\n\n    if(!animPlaying)\n    {\n        if(mUpperBodyState == UpperCharState_EquipingWeap ||\n           mUpperBodyState == UpperCharState_FollowStartToFollowStop ||\n           mUpperBodyState == UpperCharState_CastingSpell)\n        {\n            if (ammunition && mWeaponType == ESM::Weapon::MarksmanCrossbow)\n                mAnimation->attachArrow();\n\n            mUpperBodyState = UpperCharState_WeapEquiped;\n        }\n        else if(mUpperBodyState == UpperCharState_UnEquipingWeap)\n            mUpperBodyState = UpperCharState_Nothing;\n    }\n    else if(complete >= 1.0f && !isRandomAttackAnimation(mCurrentWeapon))\n    {\n        std::string start, stop;\n        switch(mUpperBodyState)\n        {\n            case UpperCharState_MinAttackToMaxAttack:\n                //hack to avoid body pos desync when jumping/sneaking in 'max attack' state\n                if(!mAnimation->isPlaying(mCurrentWeapon))\n                    mAnimation->play(mCurrentWeapon, priorityWeapon,\n                        MWRender::Animation::BlendMask_All, false,\n                        0, mAttackType+\" min attack\", mAttackType+\" max attack\", 0.999f, 0);\n                break;\n            case UpperCharState_StartToMinAttack:\n            case UpperCharState_MaxAttackToMinHit:\n            {\n                if (mUpperBodyState == UpperCharState_StartToMinAttack)\n                {\n                    // If actor is already stopped preparing attack, do not play the \"min attack -> max attack\" part.\n                    // Happens if the player did not hold the attack button.\n                    // Note: if the \"min attack\"->\"max attack\" is a stub, \"play\" it anyway. Attack strength will be random.\n                    float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+\": \"+mAttackType+\" \"+\"min attack\");\n                    float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+\": \"+mAttackType+\" \"+\"max attack\");\n                    if (mAttackingOrSpell || minAttackTime == maxAttackTime)\n                    {\n                        start = mAttackType+\" min attack\";\n                        stop = mAttackType+\" max attack\";\n                        mUpperBodyState = UpperCharState_MinAttackToMaxAttack;\n                        break;\n                    }\n\n                    if(weapclass != ESM::WeaponType::Ranged && weapclass != ESM::WeaponType::Thrown)\n                        playSwishSound(0.0f);\n                }\n\n                if(mAttackType == \"shoot\")\n                {\n                    start = mAttackType+\" min hit\";\n                    stop = mAttackType+\" release\";\n                }\n                else\n                {\n                    start = mAttackType+\" min hit\";\n                    stop = mAttackType+\" hit\";\n                }\n                mUpperBodyState = UpperCharState_MinHitToHit;\n                break;\n            }\n            case UpperCharState_MinHitToHit:\n                if(mAttackType == \"shoot\")\n                {\n                    start = mAttackType+\" follow start\";\n                    stop = mAttackType+\" follow stop\";\n                }\n                else\n                {\n                    float str = mAttackStrength;\n                    start = mAttackType+((str < 0.5f) ? \" small follow start\"\n                                                                  : (str < 1.0f) ? \" medium follow start\"\n                                                                                 : \" large follow start\");\n                    stop = mAttackType+((str < 0.5f) ? \" small follow stop\"\n                                                                 : (str < 1.0f) ? \" medium follow stop\"\n                                                                                : \" large follow stop\");\n                }\n                mUpperBodyState = UpperCharState_FollowStartToFollowStop;\n                break;\n            default:\n                break;\n        }\n\n        // Note: apply crossbow reload animation only for upper body\n        // since blending with movement animations can give weird result.\n        if(!start.empty())\n        {\n            int mask = MWRender::Animation::BlendMask_All;\n            if (mWeaponType == ESM::Weapon::MarksmanCrossbow)\n                mask = MWRender::Animation::BlendMask_UpperBody;\n\n            mAnimation->disable(mCurrentWeapon);\n            if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)\n                mAnimation->play(mCurrentWeapon, priorityWeapon,\n                                 mask, true,\n                                 weapSpeed, start, stop, 0.0f, 0);\n            else\n                mAnimation->play(mCurrentWeapon, priorityWeapon,\n                                 mask, false,\n                                 weapSpeed, start, stop, 0.0f, 0);\n        }\n    }\n    else if(complete >= 1.0f && isRandomAttackAnimation(mCurrentWeapon))\n    {\n        mAnimation->disable(mCurrentWeapon);\n        mUpperBodyState = UpperCharState_WeapEquiped;\n    }\n\n    if (mPtr.getClass().hasInventoryStore(mPtr))\n    {\n        const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n        MWWorld::ConstContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n        if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()\n                && updateCarriedLeftVisible(mWeaponType))\n        {\n            if (mAnimation->isPlaying(\"shield\"))\n                mAnimation->disable(\"shield\");\n\n            mAnimation->play(\"torch\", Priority_Torch, MWRender::Animation::BlendMask_LeftArm,\n                false, 1.0f, \"start\", \"stop\", 0.0f, (~(size_t)0), true);\n        }\n        else if (mAnimation->isPlaying(\"torch\"))\n        {\n            mAnimation->disable(\"torch\");\n        }\n    }\n\n    mAnimation->setAccurateAiming(mUpperBodyState > UpperCharState_WeapEquiped);\n\n    return forcestateupdate;\n}\n\nvoid CharacterController::updateAnimQueue()\n{\n    if(mAnimQueue.size() > 1)\n    {\n        if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false)\n        {\n            mAnimation->disable(mAnimQueue.front().mGroup);\n            mAnimQueue.pop_front();\n\n            bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,\"idle\") == 0);\n            mAnimation->play(mAnimQueue.front().mGroup, Priority_Default,\n                             MWRender::Animation::BlendMask_All, false,\n                             1.0f, \"start\", \"stop\", 0.0f, mAnimQueue.front().mLoopCount, loopfallback);\n        }\n    }\n\n    if(!mAnimQueue.empty())\n        mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, mAnimQueue.size() <= 1);\n}\n\nvoid CharacterController::update(float duration)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    const MWWorld::Class &cls = mPtr.getClass();\n    osg::Vec3f movement(0.f, 0.f, 0.f);\n    float speed = 0.f;\n\n    updateMagicEffects();\n\n    if (isKnockedOut())\n        mTimeUntilWake -= duration;\n\n    bool isPlayer = mPtr == MWMechanics::getPlayer();\n    bool isFirstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson();\n    bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState();\n\n    float scale = mPtr.getCellRef().getScale();\n\n    static const bool normalizeSpeed = Settings::Manager::getBool(\"normalise race speed\", \"Game\");\n    if (!normalizeSpeed && mPtr.getClass().isNpc())\n    {\n        const ESM::NPC* npc = mPtr.get<ESM::NPC>()->mBase;\n        const ESM::Race* race = world->getStore().get<ESM::Race>().find(npc->mRace);\n        float weight = npc->isMale() ? race->mData.mWeight.mMale : race->mData.mWeight.mFemale;\n        scale *= weight;\n    }\n\n    if(!cls.isActor())\n        updateAnimQueue();\n    else if(!cls.getCreatureStats(mPtr).isDead())\n    {\n        bool onground = world->isOnGround(mPtr);\n        bool incapacitated = ((!godmode && cls.getCreatureStats(mPtr).isParalyzed()) || cls.getCreatureStats(mPtr).getKnockedDown());\n        bool inwater = world->isSwimming(mPtr);\n        bool flying = world->isFlying(mPtr);\n        bool solid = world->isActorCollisionEnabled(mPtr);\n        // Can't run and sneak while flying (see speed formula in Npc/Creature::getSpeed)\n        bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !flying;\n        bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !flying;\n        CreatureStats &stats = cls.getCreatureStats(mPtr);\n        Movement& movementSettings = cls.getMovementSettings(mPtr);\n\n        //Force Jump Logic\n\n        bool isMoving = (std::abs(movementSettings.mPosition[0]) > .5 || std::abs(movementSettings.mPosition[1]) > .5);\n        if(!inwater && !flying && solid)\n        {\n            //Force Jump\n            if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceJump))\n                movementSettings.mPosition[2] = onground ? 1 : 0;\n            //Force Move Jump, only jump if they're otherwise moving\n            if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceMoveJump) && isMoving)\n                movementSettings.mPosition[2] = onground ? 1 : 0;\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Character movement setting rotations get reset here, so we have to assign movement\n            settings to the LocalPlayer or a LocalActor now\n        */\n        if (world->getPlayerPtr() == mPtr)\n        {\n            mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n            MWMechanics::Movement &movementSettings = cls.getMovementSettings(mPtr);\n            localPlayer->direction.pos[0] = movementSettings.mPosition[0];\n            localPlayer->direction.pos[1] = movementSettings.mPosition[1];\n            localPlayer->direction.pos[2] = movementSettings.mPosition[2];\n            localPlayer->direction.rot[0] = movementSettings.mRotation[0];\n            localPlayer->direction.rot[1] = movementSettings.mRotation[1];\n            localPlayer->direction.rot[2] = movementSettings.mRotation[2];\n        }\n        else if (mwmp::Main::get().getCellController()->isLocalActor(mPtr))\n        {\n            mwmp::LocalActor *localActor = mwmp::Main::get().getCellController()->getLocalActor(mPtr);\n            MWMechanics::Movement &movementSettings = cls.getMovementSettings(mPtr);\n            localActor->direction.pos[0] = movementSettings.mPosition[0];\n            localActor->direction.pos[1] = movementSettings.mPosition[1];\n            localActor->direction.pos[2] = movementSettings.mPosition[2];\n            localActor->direction.rot[0] = movementSettings.mRotation[0];\n            localActor->direction.rot[1] = movementSettings.mRotation[1];\n            localActor->direction.rot[2] = movementSettings.mRotation[2];\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        osg::Vec3f rot = cls.getRotationVector(mPtr);\n        osg::Vec3f vec(movementSettings.asVec3());\n\n        movementSettings.mSpeedFactor = std::min(vec.length(), 1.f);\n        vec.normalize();\n\n        // TODO: Move this check to mwinput.\n        // Joystick analogue movement.\n        // Due to the half way split between walking/running, we multiply speed by 2 while walking, unless a keyboard was used.\n        if (isPlayer && !isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f)\n            movementSettings.mSpeedFactor *= 2.f;\n\n        static const bool smoothMovement = Settings::Manager::getBool(\"smooth movement\", \"Game\");\n        if (smoothMovement)\n        {\n            static const float playerTurningCoef = 1.0 / std::max(0.01f, Settings::Manager::getFloat(\"smooth movement player turning delay\", \"Game\"));\n            float angle = mPtr.getRefData().getPosition().rot[2];\n            osg::Vec2f targetSpeed = Misc::rotateVec2f(osg::Vec2f(vec.x(), vec.y()), -angle) * movementSettings.mSpeedFactor;\n            osg::Vec2f delta = targetSpeed - mSmoothedSpeed;\n            float speedDelta = movementSettings.mSpeedFactor - mSmoothedSpeed.length();\n            float deltaLen = delta.length();\n\n            float maxDelta;\n            if (isFirstPersonPlayer)\n                maxDelta = 1;\n            else if (std::abs(speedDelta) < deltaLen / 2)\n                // Turning is smooth for player and less smooth for NPCs (otherwise NPC can miss a path point).\n                maxDelta = duration * (isPlayer ? playerTurningCoef : 6.f);\n            else if (isPlayer && speedDelta < -deltaLen / 2)\n                // As soon as controls are released, mwinput switches player from running to walking.\n                // So stopping should be instant for player, otherwise it causes a small twitch.\n                maxDelta = 1;\n            else // In all other cases speeding up and stopping are smooth.\n                maxDelta = duration * 3.f;\n\n            if (deltaLen > maxDelta)\n                delta *= maxDelta / deltaLen;\n            mSmoothedSpeed += delta;\n\n            osg::Vec2f newSpeed = Misc::rotateVec2f(mSmoothedSpeed, angle);\n            movementSettings.mSpeedFactor = newSpeed.normalize();\n            vec.x() = newSpeed.x();\n            vec.y() = newSpeed.y();\n\n            const float eps = 0.001f;\n            if (movementSettings.mSpeedFactor < eps)\n            {\n                movementSettings.mSpeedFactor = 0;\n                vec.x() = 0;\n                vec.y() = 1;\n            }\n            else if ((vec.y() < 0) != mIsMovingBackward)\n            {\n                if (targetSpeed.length() < eps || (movementSettings.mPosition[1] < 0) == mIsMovingBackward)\n                    vec.y() = mIsMovingBackward ? -eps : eps;\n            }\n            vec.normalize();\n        }\n\n        float effectiveRotation = rot.z();\n        bool canMove = cls.getMaxSpeed(mPtr) > 0;\n        static const bool turnToMovementDirection = Settings::Manager::getBool(\"turn to movement direction\", \"Game\");\n        if (!turnToMovementDirection || isFirstPersonPlayer)\n        {\n            movementSettings.mIsStrafing = std::abs(vec.x()) > std::abs(vec.y()) * 2;\n            stats.setSideMovementAngle(0);\n        }\n        else if (canMove)\n        {\n            float targetMovementAngle = vec.y() >= 0 ? std::atan2(-vec.x(), vec.y()) : std::atan2(vec.x(), -vec.y());\n            movementSettings.mIsStrafing = (stats.getDrawState() != MWMechanics::DrawState_Nothing || inwater)\n                                           && std::abs(targetMovementAngle) > osg::DegreesToRadians(60.0f);\n            if (movementSettings.mIsStrafing)\n                targetMovementAngle = 0;\n            float delta = targetMovementAngle - stats.getSideMovementAngle();\n            float cosDelta = cosf(delta);\n\n            if ((vec.y() < 0) == mIsMovingBackward)\n                movementSettings.mSpeedFactor *= std::min(std::max(cosDelta, 0.f) + 0.3f, 1.f); // slow down when turn\n            if (std::abs(delta) < osg::DegreesToRadians(20.0f))\n                mIsMovingBackward = vec.y() < 0;\n\n            float maxDelta = osg::PI * duration * (2.5f - cosDelta);\n            delta = osg::clampBetween(delta, -maxDelta, maxDelta);\n            stats.setSideMovementAngle(stats.getSideMovementAngle() + delta);\n            effectiveRotation += delta;\n        }\n\n        mAnimation->setLegsYawRadians(stats.getSideMovementAngle());\n        if (stats.getDrawState() == MWMechanics::DrawState_Nothing || inwater)\n            mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 2);\n        else\n            mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4);\n        if (smoothMovement && !isPlayer && !inwater)\n            mAnimation->setUpperBodyYawRadians(mAnimation->getUpperBodyYawRadians() + mAnimation->getHeadYaw() / 2);\n\n        speed = cls.getCurrentSpeed(mPtr);\n        vec.x() *= speed;\n        vec.y() *= speed;\n\n        if(mHitState != CharState_None && mJumpState == JumpState_None)\n            vec = osg::Vec3f();\n\n        CharacterState movestate = CharState_None;\n        CharacterState idlestate = CharState_SpecialIdle;\n        JumpingState jumpstate = JumpState_None;\n\n        bool forcestateupdate = false;\n\n        mHasMovedInXY = std::abs(vec.x())+std::abs(vec.y()) > 0.0f;\n        isrunning = isrunning && mHasMovedInXY;\n\n        // advance athletics\n        if(mHasMovedInXY && isPlayer)\n        {\n            if(inwater)\n            {\n                mSecondsOfSwimming += duration;\n                while(mSecondsOfSwimming > 1)\n                {\n                    cls.skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 1);\n                    mSecondsOfSwimming -= 1;\n                }\n            }\n            else if(isrunning && !sneak)\n            {\n                mSecondsOfRunning += duration;\n                while(mSecondsOfRunning > 1)\n                {\n                    cls.skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 0);\n                    mSecondsOfRunning -= 1;\n                }\n            }\n        }\n\n        // reduce fatigue\n        const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();\n        float fatigueLoss = 0;\n        static const float fFatigueRunBase = gmst.find(\"fFatigueRunBase\")->mValue.getFloat();\n        static const float fFatigueRunMult = gmst.find(\"fFatigueRunMult\")->mValue.getFloat();\n        static const float fFatigueSwimWalkBase = gmst.find(\"fFatigueSwimWalkBase\")->mValue.getFloat();\n        static const float fFatigueSwimRunBase = gmst.find(\"fFatigueSwimRunBase\")->mValue.getFloat();\n        static const float fFatigueSwimWalkMult = gmst.find(\"fFatigueSwimWalkMult\")->mValue.getFloat();\n        static const float fFatigueSwimRunMult = gmst.find(\"fFatigueSwimRunMult\")->mValue.getFloat();\n        static const float fFatigueSneakBase = gmst.find(\"fFatigueSneakBase\")->mValue.getFloat();\n        static const float fFatigueSneakMult = gmst.find(\"fFatigueSneakMult\")->mValue.getFloat();\n\n        if (cls.getEncumbrance(mPtr) <= cls.getCapacity(mPtr))\n        {\n            const float encumbrance = cls.getNormalizedEncumbrance(mPtr);\n            if (sneak)\n                fatigueLoss = fFatigueSneakBase + encumbrance * fFatigueSneakMult;\n            else\n            {\n                if (inwater)\n                {\n                    if (!isrunning)\n                        fatigueLoss = fFatigueSwimWalkBase + encumbrance * fFatigueSwimWalkMult;\n                    else\n                        fatigueLoss = fFatigueSwimRunBase + encumbrance * fFatigueSwimRunMult;\n                }\n                else if (isrunning)\n                    fatigueLoss = fFatigueRunBase + encumbrance * fFatigueRunMult;\n            }\n        }\n        fatigueLoss *= duration;\n        fatigueLoss *= movementSettings.mSpeedFactor;\n        DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();\n\n        if (!godmode)\n        {\n            fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss, fatigue.getCurrent() < 0);\n            cls.getCreatureStats(mPtr).setFatigue(fatigue);\n        }\n\n        float z = cls.getJump(mPtr);\n        if(sneak || inwater || flying || incapacitated || !solid || z <= 0)\n            vec.z() = 0.0f;\n\n        bool inJump = true;\n        bool playLandingSound = false;\n        if(!onground && !flying && !inwater && solid)\n        {\n            // In the air (either getting up —ascending part of jump— or falling).\n\n            forcestateupdate = (mJumpState != JumpState_InAir);\n            jumpstate = JumpState_InAir;\n\n            static const float fJumpMoveBase = gmst.find(\"fJumpMoveBase\")->mValue.getFloat();\n            static const float fJumpMoveMult = gmst.find(\"fJumpMoveMult\")->mValue.getFloat();\n            float factor = fJumpMoveBase + fJumpMoveMult * mPtr.getClass().getSkill(mPtr, ESM::Skill::Acrobatics)/100.f;\n            factor = std::min(1.f, factor);\n            vec.x() *= factor;\n            vec.y() *= factor;\n            vec.z()  = 0.0f;\n        }\n        else if(vec.z() > 0.0f && mJumpState != JumpState_InAir)\n        {\n            // Started a jump.\n            if (z > 0)\n            {\n                if(vec.x() == 0 && vec.y() == 0)\n                    vec = osg::Vec3f(0.0f, 0.0f, z);\n                else\n                {\n                    osg::Vec3f lat (vec.x(), vec.y(), 0.0f);\n                    lat.normalize();\n                    vec = osg::Vec3f(lat.x(), lat.y(), 1.0f) * z * 0.707f;\n                }\n            }\n        }\n        else if(mJumpState == JumpState_InAir && !inwater && !flying && solid)\n        {\n            forcestateupdate = true;\n            jumpstate = JumpState_Landing;\n            vec.z() = 0.0f;\n\n            // We should reset idle animation during landing\n            mAnimation->disable(mCurrentIdle);\n\n            float height = cls.getCreatureStats(mPtr).land(isPlayer);\n            float healthLost = getFallDamage(mPtr, height);\n\n            if (healthLost > 0.0f)\n            {\n                const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm();\n\n                // inflict fall damages\n                if (!godmode)\n                {\n                    float realHealthLost = static_cast<float>(healthLost * (1.0f - 0.25f * fatigueTerm));\n                    cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true);\n                }\n\n                const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics);\n                if (healthLost > (acrobaticsSkill * fatigueTerm))\n                {\n                    if (!godmode)\n                        cls.getCreatureStats(mPtr).setKnockedDown(true);\n                }\n                else\n                {\n                    // report acrobatics progression\n                    if (isPlayer)\n                        cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);\n                }\n            }\n\n            if (mPtr.getClass().isNpc())\n                playLandingSound = true;\n        }\n        else\n        {\n            if(mPtr.getClass().isNpc() && mJumpState == JumpState_InAir && !flying && solid)\n                playLandingSound = true;\n\n            jumpstate = mAnimation->isPlaying(mCurrentJump) ? JumpState_Landing : JumpState_None;\n\n            vec.x() *= scale;\n            vec.y() *= scale;\n            vec.z() = 0.0f;\n\n            inJump = false;\n\n            if (movementSettings.mIsStrafing)\n            {\n                if(vec.x() > 0.0f)\n                    movestate = (inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight)\n                                         : (sneak ? CharState_SneakRight\n                                                  : (isrunning ? CharState_RunRight : CharState_WalkRight)));\n                else if(vec.x() < 0.0f)\n                    movestate = (inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft)\n                                         : (sneak ? CharState_SneakLeft\n                                                  : (isrunning ? CharState_RunLeft : CharState_WalkLeft)));\n            }\n            else if (vec.length2() > 0.0f)\n            {\n                if (vec.y() >= 0.0f)\n                    movestate = (inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward)\n                                         : (sneak ? CharState_SneakForward\n                                                  : (isrunning ? CharState_RunForward : CharState_WalkForward)));\n                else\n                    movestate = (inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack)\n                                         : (sneak ? CharState_SneakBack\n                                                  : (isrunning ? CharState_RunBack : CharState_WalkBack)));\n            }\n            else\n            {\n                // Do not play turning animation for player if rotation speed is very slow.\n                // Actual threshold should take framerate in account.\n                float rotationThreshold = (isPlayer ? 0.015f : 0.001f) * 60 * duration;\n\n                // It seems only bipedal actors use turning animations.\n                // Also do not use turning animations in the first-person view and when sneaking.\n                if (!sneak && jumpstate == JumpState_None && !isFirstPersonPlayer && mPtr.getClass().isBipedal(mPtr))\n                {\n                    if(effectiveRotation > rotationThreshold)\n                        movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;\n                    else if(effectiveRotation < -rotationThreshold)\n                        movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;\n                }\n            }\n        }\n\n        if (playLandingSound)\n        {\n            MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n            std::string sound;\n            osg::Vec3f pos(mPtr.getRefData().getPosition().asVec3());\n            if (world->isUnderwater(mPtr.getCell(), pos) || world->isWalkingOnWater(mPtr))\n                sound = \"DefaultLandWater\";\n            else if (onground)\n                sound = \"DefaultLand\";\n\n            if (!sound.empty())\n                sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal);\n        }\n\n        if (turnToMovementDirection && !isFirstPersonPlayer &&\n            (movestate == CharState_SwimRunForward || movestate == CharState_SwimWalkForward ||\n             movestate == CharState_SwimRunBack || movestate == CharState_SwimWalkBack))\n        {\n            float swimmingPitch = mAnimation->getBodyPitchRadians();\n            float targetSwimmingPitch = -mPtr.getRefData().getPosition().rot[0];\n            float maxSwimPitchDelta = 3.0f * duration;\n            swimmingPitch += osg::clampBetween(targetSwimmingPitch - swimmingPitch, -maxSwimPitchDelta, maxSwimPitchDelta);\n            mAnimation->setBodyPitchRadians(swimmingPitch);\n        }\n        else\n            mAnimation->setBodyPitchRadians(0);\n\n        static const bool swimUpwardCorrection = Settings::Manager::getBool(\"swim upward correction\", \"Game\");\n        if (inwater && isPlayer && !isFirstPersonPlayer && swimUpwardCorrection)\n        {\n            static const float swimUpwardCoef = Settings::Manager::getFloat(\"swim upward coef\", \"Game\");\n            static const float swimForwardCoef = sqrtf(1.0f - swimUpwardCoef * swimUpwardCoef);\n            vec.z() = std::abs(vec.y()) * swimUpwardCoef;\n            vec.y() *= swimForwardCoef;\n        }\n\n        // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering\n        if (isPlayer)\n        {\n            float threshold = mCurrentMovement.find(\"swim\") == std::string::npos ? 0.4f : 0.8f;\n            float complete;\n            bool animPlaying = mAnimation->getInfo(mCurrentMovement, &complete);\n            if (movestate == CharState_None && jumpstate == JumpState_None && isTurning())\n            {\n                if (animPlaying && complete < threshold)\n                    movestate = mMovementState;\n            }\n        }\n        else\n        {\n            if (mPtr.getClass().isBipedal(mPtr))\n            {\n                if (mTurnAnimationThreshold > 0)\n                    mTurnAnimationThreshold -= duration;\n\n                if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft ||\n                    movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft)\n                {\n                    mTurnAnimationThreshold = 0.05f;\n                }\n                else if (movestate == CharState_None && isTurning()\n                        && mTurnAnimationThreshold > 0)\n                {\n                    movestate = mMovementState;\n                }\n            }\n        }\n\n        if(movestate != CharState_None && !isTurning())\n            clearAnimQueue();\n\n        if(mAnimQueue.empty() || inwater || (sneak && mIdleState != CharState_SpecialIdle))\n        {\n            if (inwater)\n                idlestate = CharState_IdleSwim;\n            else if (sneak && !inJump)\n                idlestate = CharState_IdleSneak;\n            else\n                idlestate = CharState_Idle;\n        }\n        else\n            updateAnimQueue();\n\n        if (!mSkipAnim)\n        {\n            // bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used.\n            if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr))\n                forcestateupdate = updateWeaponState(idlestate) || forcestateupdate;\n            else\n                forcestateupdate = updateCreatureState() || forcestateupdate;\n\n            refreshCurrentAnims(idlestate, movestate, jumpstate, forcestateupdate);\n\n            updateIdleStormState(inwater);\n        }\n\n        if (inJump)\n            mMovementAnimationControlled = false;\n\n        if (isTurning())\n        {\n            // Adjust animation speed from 1.0 to 1.5 multiplier\n            if (duration > 0)\n            {\n                float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast<float>(osg::PI));\n                mAnimation->adjustSpeedMult(mCurrentMovement, std::max(turnSpeed, 1.0f));\n            }\n        }\n        else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed)\n        {\n            // Vanilla caps the played animation speed.\n            const float maxSpeedMult = 10.f;\n            const float speedMult = speed / mMovementAnimSpeed;\n            mAnimation->adjustSpeedMult(mCurrentMovement, std::min(maxSpeedMult, speedMult));\n            // Make sure the actual speed is the \"expected\" speed even though the animation is slower\n            scale *= std::max(1.f, speedMult / maxSpeedMult);\n        }\n\n        if (!mSkipAnim)\n        {\n            if(!isKnockedDown() && !isKnockedOut())\n            {\n                if (rot != osg::Vec3f())\n                    world->rotateObject(mPtr, rot.x(), rot.y(), rot.z(), true);\n            }\n            else //avoid z-rotating for knockdown\n            {\n                if (rot.x() != 0 && rot.y() != 0)\n                    world->rotateObject(mPtr, rot.x(), rot.y(), 0.0f, true);\n            }\n\n            if (!mMovementAnimationControlled)\n                world->queueMovement(mPtr, vec);\n        }\n\n        movement = vec;\n        movementSettings.mPosition[0] = movementSettings.mPosition[1] = 0;\n        if (movement.z() == 0.f)\n            movementSettings.mPosition[2] = 0;\n        // Can't reset jump state (mPosition[2]) here in full; we don't know for sure whether the PhysicSystem will actually handle it in this frame\n        // due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled.\n\n        if (!mSkipAnim)\n            updateHeadTracking(duration);\n    }\n    else if(cls.getCreatureStats(mPtr).isDead())\n    {\n        // initial start of death animation for actors that started the game as dead\n        // not done in constructor since we need to give scripts a chance to set the mSkipAnim flag\n        if (!mSkipAnim && mDeathState != CharState_None && mCurrentDeath.empty())\n        {\n            // Fast-forward death animation to end for persisting corpses or corpses after end of death animation\n            if (cls.isPersistent(mPtr) || cls.getCreatureStats(mPtr).isDeathAnimationFinished())\n                playDeath(1.f, mDeathState);\n        }\n    }\n\n    bool isPersist = isPersistentAnimPlaying();\n    osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim && !isPersist ? 0.f : duration);\n    if(duration > 0.0f)\n        moved /= duration;\n    else\n        moved = osg::Vec3f(0.f, 0.f, 0.f);\n\n    moved.x() *= scale;\n    moved.y() *= scale;\n\n    // Ensure we're moving in generally the right direction...\n    if (speed > 0.f && moved != osg::Vec3f())\n    {\n        float l = moved.length();\n        if (std::abs(movement.x() - moved.x()) > std::abs(moved.x()) / 2 ||\n            std::abs(movement.y() - moved.y()) > std::abs(moved.y()) / 2 ||\n            std::abs(movement.z() - moved.z()) > std::abs(moved.z()) / 2)\n        {\n            moved = movement;\n            // For some creatures getSpeed doesn't work, so we adjust speed to the animation.\n            // TODO: Fix Creature::getSpeed.\n            float newLength = moved.length();\n            if (newLength > 0 && !cls.isNpc())\n                moved *= (l / newLength);\n        }\n    }\n\n    if (mFloatToSurface && cls.isActor())\n    {\n        if (cls.getCreatureStats(mPtr).isDead()\n            || (!godmode && cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0))\n        {\n            moved.z() = 1.0;\n        }\n    }\n\n    // Update movement\n    if(mMovementAnimationControlled && mPtr.getClass().isActor())\n        world->queueMovement(mPtr, moved);\n\n    mSkipAnim = false;\n\n    mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());\n}\n\nvoid CharacterController::persistAnimationState()\n{\n    ESM::AnimationState& state = mPtr.getRefData().getAnimationState();\n\n    state.mScriptedAnims.clear();\n    for (AnimationQueue::const_iterator iter = mAnimQueue.begin(); iter != mAnimQueue.end(); ++iter)\n    {\n        if (!iter->mPersist)\n            continue;\n\n        ESM::AnimationState::ScriptedAnimation anim;\n        anim.mGroup = iter->mGroup;\n\n        if (iter == mAnimQueue.begin())\n        {\n            anim.mLoopCount = mAnimation->getCurrentLoopCount(anim.mGroup);\n            float complete;\n            mAnimation->getInfo(anim.mGroup, &complete, nullptr);\n            anim.mTime = complete;\n        }\n        else\n        {\n            anim.mLoopCount = iter->mLoopCount;\n            anim.mTime = 0.f;\n        }\n\n        state.mScriptedAnims.push_back(anim);\n    }\n}\n\nvoid CharacterController::unpersistAnimationState()\n{\n    const ESM::AnimationState& state = mPtr.getRefData().getAnimationState();\n\n    if (!state.mScriptedAnims.empty())\n    {\n        clearAnimQueue();\n        for (ESM::AnimationState::ScriptedAnimations::const_iterator iter = state.mScriptedAnims.begin(); iter != state.mScriptedAnims.end(); ++iter)\n        {\n            AnimationQueueEntry entry;\n            entry.mGroup = iter->mGroup;\n            entry.mLoopCount = iter->mLoopCount;\n            entry.mPersist = true;\n\n            mAnimQueue.push_back(entry);\n        }\n\n        const ESM::AnimationState::ScriptedAnimation& anim = state.mScriptedAnims.front();\n        float complete = anim.mTime;\n        if (anim.mAbsolute)\n        {\n            float start = mAnimation->getTextKeyTime(anim.mGroup+\": start\");\n            float stop = mAnimation->getTextKeyTime(anim.mGroup+\": stop\");\n            float time = std::max(start, std::min(stop, anim.mTime));\n            complete = (time - start) / (stop - start);\n        }\n\n        mAnimation->disable(mCurrentIdle);\n        mCurrentIdle.clear();\n        mIdleState = CharState_SpecialIdle;\n\n        bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,\"idle\") == 0);\n        mAnimation->play(anim.mGroup,\n                         Priority_Persistent, MWRender::Animation::BlendMask_All, false, 1.0f,\n                         \"start\", \"stop\", complete, anim.mLoopCount, loopfallback);\n    }\n}\n\nbool CharacterController::playGroup(const std::string &groupname, int mode, int count, bool persist)\n{\n    if(!mAnimation || !mAnimation->hasAnimation(groupname))\n        return false;\n\n    // We should not interrupt persistent animations by non-persistent ones\n    if (isPersistentAnimPlaying() && !persist)\n        return false;\n\n    // If this animation is a looped animation (has a \"loop start\" key) that is already playing\n    // and has not yet reached the end of the loop, allow it to continue animating with its existing loop count\n    // and remove any other animations that were queued.\n    // This emulates observed behavior from the original allows the script \"OutsideBanner\" to animate banners correctly.\n    if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname &&\n        mAnimation->getTextKeyTime(mAnimQueue.front().mGroup + \": loop start\") >= 0 &&\n        mAnimation->isPlaying(groupname))\n    {\n        float endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+\": loop stop\");\n\n        if (endOfLoop < 0) // if no Loop Stop key was found, use the Stop key\n            endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+\": stop\");\n\n        if (endOfLoop > 0 && (mAnimation->getCurrentTime(mAnimQueue.front().mGroup) < endOfLoop))\n        {\n            mAnimQueue.resize(1);\n            return true;\n        }\n    }\n\n    count = std::max(count, 1);\n\n    AnimationQueueEntry entry;\n    entry.mGroup = groupname;\n    entry.mLoopCount = count-1;\n    entry.mPersist = persist;\n\n    if(mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().mGroup))\n    {\n        clearAnimQueue(persist);\n\n        mAnimation->disable(mCurrentIdle);\n        mCurrentIdle.clear();\n\n        mIdleState = CharState_SpecialIdle;\n        bool loopfallback = (entry.mGroup.compare(0,4,\"idle\") == 0);\n        mAnimation->play(groupname, persist && groupname != \"idle\" ? Priority_Persistent : Priority_Default,\n                            MWRender::Animation::BlendMask_All, false, 1.0f,\n                            ((mode==2) ? \"loop start\" : \"start\"), \"stop\", 0.0f, count-1, loopfallback);\n\n        /*\n            Start of tes3mp addition\n\n            If we are the cell authority over this actor, we need to record this new\n            animation for it\n        */\n        if (mwmp::Main::get().getCellController()->isLocalActor(mPtr))\n        {\n            mwmp::LocalActor *actor = mwmp::Main::get().getCellController()->getLocalActor(mPtr);\n            actor->animation.groupname = groupname;\n            actor->animation.mode = mode;\n            actor->animation.count = count;\n            actor->animation.persist = persist;\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n    else\n    {\n        mAnimQueue.resize(1);\n    }\n\n    // \"PlayGroup idle\" is a special case, used to remove to stop scripted animations playing\n    if (groupname == \"idle\")\n        entry.mPersist = false;\n\n    mAnimQueue.push_back(entry);\n\n    return true;\n}\n\nvoid CharacterController::skipAnim()\n{\n    mSkipAnim = true;\n}\n\nbool CharacterController::isPersistentAnimPlaying()\n{\n    if (!mAnimQueue.empty())\n    {\n        AnimationQueueEntry& first = mAnimQueue.front();\n        return first.mPersist && isAnimPlaying(first.mGroup);\n    }\n\n    return false;\n}\n\nbool CharacterController::isAnimPlaying(const std::string &groupName)\n{\n    if(mAnimation == nullptr)\n        return false;\n    return mAnimation->isPlaying(groupName);\n}\n\nvoid CharacterController::clearAnimQueue(bool clearPersistAnims)\n{\n    // Do not interrupt scripted animations, if we want to keep them\n    if ((!isPersistentAnimPlaying() || clearPersistAnims) && !mAnimQueue.empty())\n        mAnimation->disable(mAnimQueue.front().mGroup);\n\n    for (AnimationQueue::iterator it = mAnimQueue.begin(); it != mAnimQueue.end();)\n    {\n        if (clearPersistAnims || !it->mPersist)\n            it = mAnimQueue.erase(it);\n        else\n            ++it;\n    }\n}\n\nvoid CharacterController::forceStateUpdate()\n{\n    if(!mAnimation)\n        return;\n    clearAnimQueue();\n\n    // Make sure we canceled the current attack or spellcasting,\n    // because we disabled attack animations anyway.\n    mCastingManualSpell = false;\n    mAttackingOrSpell = false;\n    if (mUpperBodyState != UpperCharState_Nothing)\n        mUpperBodyState = UpperCharState_WeapEquiped;\n\n    refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);\n\n    if(mDeathState != CharState_None)\n    {\n        playRandomDeath();\n    }\n\n    mAnimation->runAnimation(0.f);\n}\n\nCharacterController::KillResult CharacterController::kill()\n{\n    if (mDeathState == CharState_None)\n    {\n        playRandomDeath();\n\n        mAnimation->disable(mCurrentIdle);\n\n        mIdleState = CharState_None;\n        mCurrentIdle.clear();\n        return Result_DeathAnimStarted;\n    }\n\n    MWMechanics::CreatureStats& cStats = mPtr.getClass().getCreatureStats(mPtr);\n    if (isAnimPlaying(mCurrentDeath))\n        return Result_DeathAnimPlaying;\n    if (!cStats.isDeathAnimationFinished())\n    {\n        /*\n            Start of tes3mp addition\n        */\n        if (mwmp::Main::get().getCellController()->isLocalActor(mPtr))\n        {\n            mwmp::Main::get().getCellController()->getLocalActor(mPtr)->creatureStats.mDeathAnimationFinished = true;\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        cStats.setDeathAnimationFinished(true);\n        return Result_DeathAnimJustFinished;\n    }\n    return Result_DeathAnimFinished;\n}\n\nvoid CharacterController::resurrect()\n{\n    if(mDeathState == CharState_None)\n        return;\n\n    if(mAnimation)\n        mAnimation->disable(mCurrentDeath);\n    mCurrentDeath.clear();\n    mDeathState = CharState_None;\n    mWeaponType = ESM::Weapon::None;\n}\n\nvoid CharacterController::updateContinuousVfx()\n{\n    // Keeping track of when to stop a continuous VFX seems to be very difficult to do inside the spells code,\n    // as it's extremely spread out (ActiveSpells, Spells, InventoryStore effects, etc...) so we do it here.\n\n    // Stop any effects that are no longer active\n    std::vector<int> effects;\n    mAnimation->getLoopingEffects(effects);\n\n    for (int effectId : effects)\n    {\n        if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished()\n            || mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(effectId)).getMagnitude() <= 0)\n            mAnimation->removeEffect(effectId);\n    }\n}\n\nvoid CharacterController::updateMagicEffects()\n{\n    if (!mPtr.getClass().isActor())\n        return;\n\n    float light = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Light).getMagnitude();\n    mAnimation->setLightEffect(light);\n\n    // If you're dead you don't care about whether you've started/stopped being a vampire or not\n    if (mPtr.getClass().getCreatureStats(mPtr).isDead())\n        return;\n\n    bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0.0f;\n    mAnimation->setVampire(vampire);\n}\n\nvoid CharacterController::setVisibility(float visibility)\n{\n    // We should take actor's invisibility in account\n    if (mPtr.getClass().isActor())\n    {\n        float alpha = 1.f;\n        if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Invisibility).getModifier()) // Ignore base magnitude (see bug #3555).\n        {\n            if (mPtr == getPlayer())\n                alpha = 0.25f;\n            else\n                alpha = 0.05f;\n        }\n        float chameleon = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude();\n        if (chameleon)\n        {\n            alpha *= std::min(0.75f, std::max(0.25f, (100.f - chameleon)/100.f));\n        }\n\n        visibility = std::min(visibility, alpha);\n    }\n\n    // TODO: implement a dithering shader rather than just change object transparency.\n    mAnimation->setAlpha(visibility);\n}\n\nvoid CharacterController::setAttackTypeBasedOnMovement()\n{\n    float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition;\n    if (std::abs(move[1]) > std::abs(move[0]) + 0.2f) // forward-backward\n        mAttackType = \"thrust\";\n    else if (std::abs(move[0]) > std::abs(move[1]) + 0.2f) // sideway\n        mAttackType = \"slash\";\n    else\n        mAttackType = \"chop\";\n}\n\nbool CharacterController::isRandomAttackAnimation(const std::string& group) const\n{\n    return (group == \"attack1\" || group == \"swimattack1\" ||\n            group == \"attack2\" || group == \"swimattack2\" ||\n            group == \"attack3\" || group == \"swimattack3\");\n}\n\nbool CharacterController::isAttackPreparing() const\n{\n    return mUpperBodyState == UpperCharState_StartToMinAttack ||\n            mUpperBodyState == UpperCharState_MinAttackToMaxAttack;\n}\n\nbool CharacterController::isCastingSpell() const\n{\n    return mCastingManualSpell || mUpperBodyState == UpperCharState_CastingSpell;\n}\n\nbool CharacterController::isReadyToBlock() const\n{\n    return updateCarriedLeftVisible(mWeaponType);\n}\n\nbool CharacterController::isKnockedDown() const\n{\n    return mHitState == CharState_KnockDown ||\n            mHitState == CharState_SwimKnockDown;\n}\n\nbool CharacterController::isKnockedOut() const\n{\n    return mHitState == CharState_KnockOut ||\n            mHitState == CharState_SwimKnockOut;\n}\n\nbool CharacterController::isTurning() const\n{\n    return mMovementState == CharState_TurnLeft ||\n            mMovementState == CharState_TurnRight ||\n            mMovementState == CharState_SwimTurnLeft ||\n            mMovementState == CharState_SwimTurnRight;\n}\n\nbool CharacterController::isRecovery() const\n{\n    return mHitState == CharState_Hit ||\n            mHitState == CharState_SwimHit;\n}\n\nbool CharacterController::isAttackingOrSpell() const\n{\n    return mUpperBodyState != UpperCharState_Nothing &&\n            mUpperBodyState != UpperCharState_WeapEquiped;\n}\n\nbool CharacterController::isSneaking() const\n{\n    return mIdleState == CharState_IdleSneak ||\n            mMovementState == CharState_SneakForward ||\n            mMovementState == CharState_SneakBack ||\n            mMovementState == CharState_SneakLeft ||\n            mMovementState == CharState_SneakRight;\n}\n\nbool CharacterController::isRunning() const\n{\n    return mMovementState == CharState_RunForward ||\n            mMovementState == CharState_RunBack ||\n            mMovementState == CharState_RunLeft ||\n            mMovementState == CharState_RunRight ||\n            mMovementState == CharState_SwimRunForward ||\n            mMovementState == CharState_SwimRunBack ||\n            mMovementState == CharState_SwimRunLeft ||\n            mMovementState == CharState_SwimRunRight;\n}\n\nvoid CharacterController::setAttackingOrSpell(bool attackingOrSpell)\n{\n    mAttackingOrSpell = attackingOrSpell;\n}\n\nvoid CharacterController::castSpell(const std::string spellId, bool manualSpell)\n{\n    mAttackingOrSpell = true;\n    mCastingManualSpell = manualSpell;\n    ActionSpell action = ActionSpell(spellId);\n    action.prepare(mPtr);\n}\n\nvoid CharacterController::setAIAttackType(const std::string& attackType)\n{\n    mAttackType = attackType;\n}\n\nvoid CharacterController::setAttackTypeRandomly(std::string& attackType)\n{\n    float random = Misc::Rng::rollProbability();\n    if (random >= 2/3.f)\n        attackType = \"thrust\";\n    else if (random >= 1/3.f)\n        attackType = \"slash\";\n    else\n        attackType = \"chop\";\n}\n\nbool CharacterController::readyToPrepareAttack() const\n{\n    return (mHitState == CharState_None || mHitState == CharState_Block)\n            && mUpperBodyState <= UpperCharState_WeapEquiped;\n}\n\nbool CharacterController::readyToStartAttack() const\n{\n    if (mHitState != CharState_None && mHitState != CharState_Block)\n        return false;\n\n    if (mPtr.getClass().hasInventoryStore(mPtr) || mPtr.getClass().isBipedal(mPtr))\n        return mUpperBodyState == UpperCharState_WeapEquiped;\n    else\n        return mUpperBodyState == UpperCharState_Nothing;\n}\n\nfloat CharacterController::getAttackStrength() const\n{\n    return mAttackStrength;\n}\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to get the current attack type from elsewhere in the code\n*/\nstd::string CharacterController::getAttackType() const\n{\n    return mAttackType;\n}\n/*\n    End of tes3mp addition\n*/\n\nvoid CharacterController::setActive(int active)\n{\n    mAnimation->setActive(active);\n}\n\nvoid CharacterController::setHeadTrackTarget(const MWWorld::ConstPtr &target)\n{\n    mHeadTrackTarget = target;\n}\n\nvoid CharacterController::playSwishSound(float attackStrength)\n{\n    MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n\n    std::string sound = \"Weapon Swish\";\n    if(attackStrength < 0.5f)\n        sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack\n    else if(attackStrength < 1.0f)\n        sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack\n    else\n        sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack\n}\n\nvoid CharacterController::updateHeadTracking(float duration)\n{\n    const osg::Node* head = mAnimation->getNode(\"Bip01 Head\");\n    if (!head)\n        return;\n\n    double zAngleRadians = 0.f;\n    double xAngleRadians = 0.f;\n\n    if (!mHeadTrackTarget.isEmpty())\n    {\n        osg::NodePathList nodepaths = head->getParentalNodePaths();\n        if (nodepaths.empty())\n            return;\n        osg::Matrixf mat = osg::computeLocalToWorld(nodepaths[0]);\n        osg::Vec3f headPos = mat.getTrans();\n\n        osg::Vec3f direction;\n        if (const MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget))\n        {\n            const osg::Node* node = anim->getNode(\"Head\");\n            if (node == nullptr)\n                node = anim->getNode(\"Bip01 Head\");\n            if (node != nullptr)\n            {\n                nodepaths = node->getParentalNodePaths();\n                if (!nodepaths.empty())\n                    direction = osg::computeLocalToWorld(nodepaths[0]).getTrans() - headPos;\n            }\n            else\n                // no head node to look at, fall back to look at center of collision box\n                direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget, false);\n        }\n        direction.normalize();\n\n        if (!mPtr.getRefData().getBaseNode())\n            return;\n        const osg::Vec3f actorDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);\n\n        zAngleRadians = std::atan2(actorDirection.x(), actorDirection.y()) - std::atan2(direction.x(), direction.y());\n        zAngleRadians = Misc::normalizeAngle(zAngleRadians - mAnimation->getHeadYaw()) + mAnimation->getHeadYaw();\n        zAngleRadians *= (1 - direction.z() * direction.z());\n        xAngleRadians = std::asin(direction.z());\n    }\n\n    const double xLimit = osg::DegreesToRadians(40.0);\n    const double zLimit = osg::DegreesToRadians(30.0);\n    double zLimitOffset = mAnimation->getUpperBodyYawRadians();\n    xAngleRadians = osg::clampBetween(xAngleRadians, -xLimit, xLimit);\n    zAngleRadians = osg::clampBetween(zAngleRadians, -zLimit + zLimitOffset, zLimit + zLimitOffset);\n\n    float factor = duration*5;\n    factor = std::min(factor, 1.f);\n    xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * xAngleRadians;\n    zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * zAngleRadians;\n\n    mAnimation->setHeadPitch(xAngleRadians);\n    mAnimation->setHeadYaw(zAngleRadians);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/character.hpp",
    "content": "#ifndef GAME_MWMECHANICS_CHARACTER_HPP\n#define GAME_MWMECHANICS_CHARACTER_HPP\n\n#include <deque>\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"weapontype.hpp\"\n\nnamespace MWWorld\n{\n    class InventoryStore;\n}\n\nnamespace MWRender\n{\n    class Animation;\n}\n\nnamespace MWMechanics\n{\n\nstruct Movement;\nclass CreatureStats;\n\nenum Priority {\n    Priority_Default,\n    Priority_WeaponLowerBody,\n    Priority_SneakIdleLowerBody,\n    Priority_SwimIdle,\n    Priority_Jump,\n    Priority_Movement,\n    Priority_Hit,\n    Priority_Weapon,\n    Priority_Block,\n    Priority_Knockdown,\n    Priority_Torch,\n    Priority_Storm,\n    Priority_Death,\n    Priority_Persistent,\n\n    Num_Priorities\n};\n\nenum CharacterState {\n    CharState_None,\n\n    CharState_SpecialIdle,\n    CharState_Idle,\n    CharState_Idle2,\n    CharState_Idle3,\n    CharState_Idle4,\n    CharState_Idle5,\n    CharState_Idle6,\n    CharState_Idle7,\n    CharState_Idle8,\n    CharState_Idle9,\n    CharState_IdleSwim,\n    CharState_IdleSneak,\n\n    CharState_WalkForward,\n    CharState_WalkBack,\n    CharState_WalkLeft,\n    CharState_WalkRight,\n\n    CharState_SwimWalkForward,\n    CharState_SwimWalkBack,\n    CharState_SwimWalkLeft,\n    CharState_SwimWalkRight,\n\n    CharState_RunForward,\n    CharState_RunBack,\n    CharState_RunLeft,\n    CharState_RunRight,\n\n    CharState_SwimRunForward,\n    CharState_SwimRunBack,\n    CharState_SwimRunLeft,\n    CharState_SwimRunRight,\n\n    CharState_SneakForward,\n    CharState_SneakBack,\n    CharState_SneakLeft,\n    CharState_SneakRight,\n\n    CharState_TurnLeft,\n    CharState_TurnRight,\n    CharState_SwimTurnLeft,\n    CharState_SwimTurnRight,\n\n    CharState_Jump,\n\n    CharState_Death1,\n    CharState_Death2,\n    CharState_Death3,\n    CharState_Death4,\n    CharState_Death5,\n    CharState_SwimDeath,\n    CharState_SwimDeathKnockDown,\n    CharState_SwimDeathKnockOut,\n    CharState_DeathKnockDown,\n    CharState_DeathKnockOut,\n\n    CharState_Hit,\n    CharState_SwimHit,\n    CharState_KnockDown,\n    CharState_KnockOut,\n    CharState_SwimKnockDown,\n    CharState_SwimKnockOut,\n    CharState_Block\n};\n\nenum UpperBodyCharacterState {\n    UpperCharState_Nothing,\n    UpperCharState_EquipingWeap,\n    UpperCharState_UnEquipingWeap,\n    UpperCharState_WeapEquiped,\n    UpperCharState_StartToMinAttack,\n    UpperCharState_MinAttackToMaxAttack,\n    UpperCharState_MaxAttackToMinHit,\n    UpperCharState_MinHitToHit,\n    UpperCharState_FollowStartToFollowStop,\n    UpperCharState_CastingSpell\n};\n\nenum JumpingState {\n    JumpState_None,\n    JumpState_InAir,\n    JumpState_Landing\n};\n\nstruct WeaponInfo;\n\nclass CharacterController : public MWRender::Animation::TextKeyListener\n{\n    MWWorld::Ptr mPtr;\n    MWWorld::Ptr mWeapon;\n    MWRender::Animation *mAnimation;\n    \n    struct AnimationQueueEntry\n    {\n        std::string mGroup;\n        size_t mLoopCount;\n        bool mPersist;\n    };\n    typedef std::deque<AnimationQueueEntry> AnimationQueue;\n    AnimationQueue mAnimQueue;\n\n    CharacterState mIdleState;\n    std::string mCurrentIdle;\n\n    CharacterState mMovementState;\n    std::string mCurrentMovement;\n    float mMovementAnimSpeed;\n    bool mAdjustMovementAnimSpeed;\n    bool mHasMovedInXY;\n    bool mMovementAnimationControlled;\n\n    CharacterState mDeathState;\n    std::string mCurrentDeath;\n    bool mFloatToSurface;\n\n    CharacterState mHitState;\n    std::string mCurrentHit;\n\n    UpperBodyCharacterState mUpperBodyState;\n\n    JumpingState mJumpState;\n    std::string mCurrentJump;\n\n    int mWeaponType;\n    std::string mCurrentWeapon;\n\n    float mAttackStrength;\n\n    bool mSkipAnim;\n\n    // counted for skill increase\n    float mSecondsOfSwimming;\n    float mSecondsOfRunning;\n\n    MWWorld::ConstPtr mHeadTrackTarget;\n\n    float mTurnAnimationThreshold; // how long to continue playing turning animation after actor stopped turning\n\n    std::string mAttackType; // slash, chop or thrust\n\n    bool mAttackingOrSpell;\n    bool mCastingManualSpell;\n\n    float mTimeUntilWake;\n\n    bool mIsMovingBackward;\n    osg::Vec2f mSmoothedSpeed;\n\n    void setAttackTypeBasedOnMovement();\n\n    void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false);\n    void refreshHitRecoilAnims(CharacterState& idle);\n    void refreshJumpAnims(const std::string& weapShortGroup, JumpingState jump, CharacterState& idle, bool force=false);\n    void refreshMovementAnims(const std::string& weapShortGroup, CharacterState movement, CharacterState& idle, bool force=false);\n    void refreshIdleAnims(const std::string& weapShortGroup, CharacterState idle, bool force=false);\n\n    void clearAnimQueue(bool clearPersistAnims = false);\n\n    bool updateWeaponState(CharacterState& idle);\n    bool updateCreatureState();\n    void updateIdleStormState(bool inwater);\n\n    std::string chooseRandomAttackAnimation() const;\n    bool isRandomAttackAnimation(const std::string& group) const;\n\n    bool isPersistentAnimPlaying();\n\n    void updateAnimQueue();\n\n    void updateHeadTracking(float duration);\n\n    void updateMagicEffects();\n\n    void playDeath(float startpoint, CharacterState death);\n    CharacterState chooseRandomDeathState() const;\n    void playRandomDeath(float startpoint = 0.0f);\n\n    /// choose a random animation group with \\a prefix and numeric suffix\n    /// @param num if non-nullptr, the chosen animation number will be written here\n    std::string chooseRandomGroup (const std::string& prefix, int* num = nullptr) const;\n\n    bool updateCarriedLeftVisible(int weaptype) const;\n\n    std::string fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask = nullptr);\n\n    std::string getWeaponAnimation(int weaponType) const;\n\npublic:\n    CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);\n    virtual ~CharacterController();\n\n    void handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map) override;\n\n    // Be careful when to call this, see comment in Actors\n    void updateContinuousVfx();\n\n    void updatePtr(const MWWorld::Ptr &ptr);\n\n    void update(float duration);\n\n    bool onOpen();\n    void onClose();\n\n    void persistAnimationState();\n    void unpersistAnimationState();\n\n    bool playGroup(const std::string &groupname, int mode, int count, bool persist=false);\n    void skipAnim();\n    bool isAnimPlaying(const std::string &groupName);\n\n    enum KillResult\n    {\n        Result_DeathAnimStarted,\n        Result_DeathAnimPlaying,\n        Result_DeathAnimJustFinished,\n        Result_DeathAnimFinished\n    };\n    KillResult kill();\n\n    void resurrect();\n    bool isDead() const\n    { return mDeathState != CharState_None; }\n\n    void forceStateUpdate();\n    \n    bool isAttackPreparing() const;\n    bool isCastingSpell() const;\n    bool isReadyToBlock() const;\n    bool isKnockedDown() const;\n    bool isKnockedOut() const;\n    bool isRecovery() const;\n    bool isSneaking() const;\n    bool isRunning() const;\n    bool isTurning() const;\n    bool isAttackingOrSpell() const;\n\n    void setVisibility(float visibility);\n    void setAttackingOrSpell(bool attackingOrSpell);\n    void castSpell(const std::string spellId, bool manualSpell=false);\n    void setAIAttackType(const std::string& attackType);\n    static void setAttackTypeRandomly(std::string& attackType);\n\n    bool readyToPrepareAttack() const;\n    bool readyToStartAttack() const;\n\n    float getAttackStrength() const;\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the current attack type from elsewhere in the code\n    */\n    std::string getAttackType() const;\n    /*\n        End of tes3mp addition\n    */\n\n    /// @see Animation::setActive\n    void setActive(int active);\n\n    /// Make this character turn its head towards \\a target. To turn off head tracking, pass an empty Ptr.\n    void setHeadTrackTarget(const MWWorld::ConstPtr& target);\n\n    void playSwishSound(float attackStrength);\n};\n}\n\n#endif /* GAME_MWMECHANICS_CHARACTER_HPP */\n"
  },
  {
    "path": "apps/openmw/mwmechanics/combat.cpp",
    "content": "#include \"combat.hpp\"\n\n#include <components/misc/rng.hpp>\n#include <components/settings/settings.hpp>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"npcstats.hpp\"\n#include \"movement.hpp\"\n#include \"spellcasting.hpp\"\n#include \"spellresistance.hpp\"\n#include \"difficultyscaling.hpp\"\n#include \"actorutil.hpp\"\n#include \"pathfinding.hpp\"\n\nnamespace\n{\n\nfloat signedAngleRadians (const osg::Vec3f& v1, const osg::Vec3f& v2, const osg::Vec3f& normal)\n{\n    return std::atan2((normal * (v1 ^ v2)), (v1 * v2));\n}\n\n}\n\nnamespace MWMechanics\n{\n\n    bool applyOnStrikeEnchantment(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const osg::Vec3f& hitPosition, const bool fromProjectile)\n    {\n        std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : \"\";\n        if (!enchantmentName.empty())\n        {\n            const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(\n                        enchantmentName);\n            if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)\n            {\n                MWMechanics::CastSpell cast(attacker, victim, fromProjectile);\n                cast.mHitPosition = hitPosition;\n                cast.cast(object, false);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    bool blockMeleeAttack(const MWWorld::Ptr &attacker, const MWWorld::Ptr &blocker, const MWWorld::Ptr &weapon, float damage, float attackStrength)\n    {\n        if (!blocker.getClass().hasInventoryStore(blocker))\n            return false;\n\n        MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker);\n\n        if (blockerStats.getKnockedDown() // Used for both knockout or knockdown\n                || blockerStats.getHitRecovery()\n                || blockerStats.isParalyzed())\n            return false;\n\n        if (!MWBase::Environment::get().getMechanicsManager()->isReadyToBlock(blocker))\n            return false;\n\n        MWWorld::InventoryStore& inv = blocker.getClass().getInventoryStore(blocker);\n        MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n        if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name())\n            return false;\n\n        if (!blocker.getRefData().getBaseNode())\n            return false; // shouldn't happen\n\n        float angleDegrees = osg::RadiansToDegrees(\n                    signedAngleRadians (\n                    (attacker.getRefData().getPosition().asVec3() - blocker.getRefData().getPosition().asVec3()),\n                    blocker.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0),\n                    osg::Vec3f(0,0,1)));\n\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        if (angleDegrees < gmst.find(\"fCombatBlockLeftAngle\")->mValue.getFloat())\n            return false;\n        if (angleDegrees > gmst.find(\"fCombatBlockRightAngle\")->mValue.getFloat())\n            return false;\n\n        MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);\n\n        float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2f * blockerStats.getAttribute(ESM::Attribute::Agility).getModified()\n            + 0.1f * blockerStats.getAttribute(ESM::Attribute::Luck).getModified();\n        float enemySwing = attackStrength;\n        float swingTerm = enemySwing * gmst.find(\"fSwingBlockMult\")->mValue.getFloat() + gmst.find(\"fSwingBlockBase\")->mValue.getFloat();\n\n        float blockerTerm = blockTerm * swingTerm;\n        if (blocker.getClass().getMovementSettings(blocker).mPosition[1] <= 0)\n            blockerTerm *= gmst.find(\"fBlockStillBonus\")->mValue.getFloat();\n        blockerTerm *= blockerStats.getFatigueTerm();\n\n        float attackerSkill = 0;\n        if (weapon.isEmpty())\n            attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand);\n        else\n            attackerSkill = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon));\n        float attackerTerm = attackerSkill + 0.2f * attackerStats.getAttribute(ESM::Attribute::Agility).getModified()\n                + 0.1f * attackerStats.getAttribute(ESM::Attribute::Luck).getModified();\n        attackerTerm *= attackerStats.getFatigueTerm();\n\n        int x = int(blockerTerm - attackerTerm);\n        int iBlockMaxChance = gmst.find(\"iBlockMaxChance\")->mValue.getInteger();\n        int iBlockMinChance = gmst.find(\"iBlockMinChance\")->mValue.getInteger();\n        x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x));\n\n        /*\n            Start of tes3mp change (major)\n\n            Only calculate block chance for LocalPlayers and LocalActors; otherwise,\n            get the block state from the relevant DedicatedPlayer or DedicatedActor\n        */\n        mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);\n\n        if (localAttack)\n        {\n            localAttack->block = false;\n        }\n\n        mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(blocker);\n\n        if ((dedicatedAttack && dedicatedAttack->block == true) ||\n            Misc::Rng::roll0to99() < x)\n        {\n            if (localAttack)\n            {\n                localAttack->block = true;\n            }\n        /*\n            End of tes3mp change (major)\n        */\n\n            // Reduce shield durability by incoming damage\n            int shieldhealth = shield->getClass().getItemHealth(*shield);\n            shieldhealth -= std::min(shieldhealth, int(damage));\n            shield->getCellRef().setCharge(shieldhealth);\n            if (shieldhealth == 0)\n                inv.unequipItem(*shield, blocker);\n            // Reduce blocker fatigue\n            const float fFatigueBlockBase = gmst.find(\"fFatigueBlockBase\")->mValue.getFloat();\n            const float fFatigueBlockMult = gmst.find(\"fFatigueBlockMult\")->mValue.getFloat();\n            const float fWeaponFatigueBlockMult = gmst.find(\"fWeaponFatigueBlockMult\")->mValue.getFloat();\n            MWMechanics::DynamicStat<float> fatigue = blockerStats.getFatigue();\n            float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker);\n            normalizedEncumbrance = std::min(1.f, normalizedEncumbrance);\n            float fatigueLoss = fFatigueBlockBase + normalizedEncumbrance * fFatigueBlockMult;\n            if (!weapon.isEmpty())\n                fatigueLoss += weapon.getClass().getWeight(weapon) * attackStrength * fWeaponFatigueBlockMult;\n            fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);\n            blockerStats.setFatigue(fatigue);\n\n            blockerStats.setBlock(true);\n\n            if (blocker == getPlayer())\n                blocker.getClass().skillUsageSucceeded(blocker, ESM::Skill::Block, 0);\n\n            return true;\n        }\n        return false;\n    }\n\n    bool isNormalWeapon(const MWWorld::Ptr &weapon)\n    {\n        if (weapon.isEmpty())\n            return false;\n\n        const int flags = weapon.get<ESM::Weapon>()->mBase->mData.mFlags;\n        bool isSilver = flags & ESM::Weapon::Silver;\n        bool isMagical = flags & ESM::Weapon::Magical;\n        bool isEnchanted = !weapon.getClass().getEnchantment(weapon).empty();\n\n        return !isSilver && !isMagical && (!isEnchanted || !Settings::Manager::getBool(\"enchanted weapons are magical\", \"Game\"));\n    }\n\n    void resistNormalWeapon(const MWWorld::Ptr &actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr &weapon, float &damage)\n    {\n        if (damage == 0 || weapon.isEmpty() || !isNormalWeapon(weapon))\n            return;\n\n        const MWMechanics::MagicEffects& effects = actor.getClass().getCreatureStats(actor).getMagicEffects();\n        const float resistance = effects.get(ESM::MagicEffect::ResistNormalWeapons).getMagnitude() / 100.f;\n        const float weakness = effects.get(ESM::MagicEffect::WeaknessToNormalWeapons).getMagnitude() / 100.f;\n\n        damage *= 1.f - std::min(1.f, resistance-weakness);\n\n        if (damage == 0 && attacker == getPlayer())\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicTargetResistsWeapons}\");\n    }\n\n    void applyWerewolfDamageMult(const MWWorld::Ptr &actor, const MWWorld::Ptr &weapon, float &damage)\n    {\n        if (damage == 0 || weapon.isEmpty() || !actor.getClass().isNpc())\n            return;\n\n        const int flags = weapon.get<ESM::Weapon>()->mBase->mData.mFlags;\n        bool isSilver = flags & ESM::Weapon::Silver;\n\n        if (isSilver && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n            damage *= store.get<ESM::GameSetting>().find(\"fWereWolfSilverWeaponDamageMult\")->mValue.getFloat();\n        }\n    }\n\n    void projectileHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,\n                       const osg::Vec3f& hitPosition, float attackStrength)\n    {\n        /*\n            Start of tes3mp addition\n\n            Ignore projectiles fired by DedicatedPlayers and DedicatedActors\n\n            If fired by LocalPlayers and LocalActors, get the associated LocalAttack and set its type\n            to RANGED while also marking it as a hit\n        */\n        if (mwmp::PlayerList::isDedicatedPlayer(attacker) || mwmp::Main::get().getCellController()->isDedicatedActor(attacker))\n            return;\n\n        mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);\n\n        if (localAttack)\n        {\n            localAttack->type = mwmp::Attack::RANGED;\n            localAttack->isHit = true;\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();\n\n        bool validVictim = !victim.isEmpty() && victim.getClass().isActor();\n\n        float damage = 0.f;\n        if (validVictim)\n        {\n            if (attacker == getPlayer())\n                MWBase::Environment::get().getWindowManager()->setEnemy(victim);\n\n            int weaponSkill = ESM::Skill::Marksman;\n            if (!weapon.isEmpty())\n                weaponSkill = weapon.getClass().getEquipmentSkill(weapon);\n\n            int skillValue = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon));\n\n            /*\n                Start of tes3mp addition\n\n                Mark this as a successful attack for the associated LocalAttack unless proven otherwise\n            */\n            if (localAttack)\n                localAttack->success = true;\n            /*\n                End of tes3mp addition\n            */\n\n            if (Misc::Rng::roll0to99() >= getHitChance(attacker, victim, skillValue))\n            {\n                /*\n                    Start of tes3mp addition\n\n                    Mark this as a failed LocalAttack now that the hit roll has failed\n                */\n                if (localAttack)\n                    localAttack->success = false;\n                /*\n                    End of tes3mp addition\n                */\n\n                victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false);\n                MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);\n                return;\n            }\n\n            const unsigned char* attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;\n            damage = attack[0] + ((attack[1] - attack[0]) * attackStrength); // Bow/crossbow damage\n\n            // Arrow/bolt damage\n            // NB in case of thrown weapons, we are applying the damage twice since projectile == weapon\n            attack = projectile.get<ESM::Weapon>()->mBase->mData.mChop;\n            damage += attack[0] + ((attack[1] - attack[0]) * attackStrength);\n\n            adjustWeaponDamage(damage, weapon, attacker);\n            if (weapon == projectile || Settings::Manager::getBool(\"only appropriate ammunition bypasses resistance\", \"Game\") || isNormalWeapon(weapon))\n                resistNormalWeapon(victim, attacker, projectile, damage);\n            applyWerewolfDamageMult(victim, projectile, damage);\n\n            if (attacker == getPlayer())\n                attacker.getClass().skillUsageSucceeded(attacker, weaponSkill, 0);\n\n            const MWMechanics::AiSequence& sequence = victim.getClass().getCreatureStats(victim).getAiSequence();\n            bool unaware = attacker == getPlayer() && !sequence.isInCombat()\n                && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim);\n            bool knockedDown = victim.getClass().getCreatureStats(victim).getKnockedDown();\n            if (knockedDown || unaware)\n            {\n                damage *= gmst.find(\"fCombatKODamageMult\")->mValue.getFloat();\n                if (!knockedDown)\n                    MWBase::Environment::get().getSoundManager()->playSound3D(victim, \"critical damage\", 1.0f, 1.0f);\n            }\n        }\n\n        reduceWeaponCondition(damage, validVictim, weapon, attacker);\n\n        // Apply \"On hit\" effect of the projectile\n        bool appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition, true);\n\n        /*\n            Start of tes3mp change (minor)\n\n            Track whether the strike enchantment is successful for attacks by the\n            LocalPlayer or LocalActors for their projectile\n        */\n        if (localAttack)\n            localAttack->applyAmmoEnchantment = appliedEnchantment;\n        /*\n            End of tes3mp change (minor)\n        */\n\n        if (validVictim)\n        {\n            // Non-enchanted arrows shot at enemies have a chance to turn up in their inventory\n            if (victim != getPlayer() && !appliedEnchantment)\n            {\n                float fProjectileThrownStoreChance = gmst.find(\"fProjectileThrownStoreChance\")->mValue.getFloat();\n                if (Misc::Rng::rollProbability() < fProjectileThrownStoreChance / 100.f)\n                    victim.getClass().getContainerStore(victim).add(projectile, 1, victim);\n            }\n\n            victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true);\n        }\n        /*\n            Start of tes3mp addition\n\n            If this is a local attack that had no victim, send a packet for it here\n        */\n        else if (localAttack)\n        {\n            localAttack->hitPosition = MechanicsHelper::getPositionFromVector(hitPosition);\n            localAttack->shouldSend = true;\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    float getHitChance(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, int skillValue)\n    {\n        MWMechanics::CreatureStats &stats = attacker.getClass().getCreatureStats(attacker);\n        const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();\n\n        float defenseTerm = 0;\n        MWMechanics::CreatureStats& victimStats = victim.getClass().getCreatureStats(victim);\n        if (victimStats.getFatigue().getCurrent() >= 0)\n        {\n            // Maybe we should keep an aware state for actors updated every so often instead of testing every time\n            bool unaware = (!victimStats.getAiSequence().isInCombat())\n                    && (attacker == getPlayer())\n                    && (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim));\n            if (!(victimStats.getKnockedDown() ||\n                    victimStats.isParalyzed()\n                    || unaware ))\n            {\n                defenseTerm = victimStats.getEvasion();\n            }\n            defenseTerm += std::min(100.f,\n                                    gmst.find(\"fCombatInvisoMult\")->mValue.getFloat() *\n                                    victimStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude());\n            defenseTerm += std::min(100.f,\n                                    gmst.find(\"fCombatInvisoMult\")->mValue.getFloat() *\n                                    victimStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude());\n        }\n        float attackTerm = skillValue +\n                          (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +\n                          (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);\n        attackTerm *= stats.getFatigueTerm();\n        attackTerm += mageffects.get(ESM::MagicEffect::FortifyAttack).getMagnitude() -\n                     mageffects.get(ESM::MagicEffect::Blind).getMagnitude();\n\n        return round(attackTerm - defenseTerm);\n    }\n\n    void applyElementalShields(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim)\n    {\n        // Don't let elemental shields harm the player in god mode.\n        bool godmode = attacker == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n        if (godmode)\n            return;\n        for (int i=0; i<3; ++i)\n        {\n            float magnitude = victim.getClass().getCreatureStats(victim).getMagicEffects().get(ESM::MagicEffect::FireShield+i).getMagnitude();\n\n            if (!magnitude)\n                continue;\n\n            CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);\n            float saveTerm = attacker.getClass().getSkill(attacker, ESM::Skill::Destruction)\n                    + 0.2f * attackerStats.getAttribute(ESM::Attribute::Willpower).getModified()\n                    + 0.1f * attackerStats.getAttribute(ESM::Attribute::Luck).getModified();\n\n            float fatigueMax = attackerStats.getFatigue().getModified();\n            float fatigueCurrent = attackerStats.getFatigue().getCurrent();\n\n            float normalisedFatigue = floor(fatigueMax)==0 ? 1 : std::max (0.0f, (fatigueCurrent/fatigueMax));\n\n            saveTerm *= 1.25f * normalisedFatigue;\n\n            float x = std::max(0.f, saveTerm - Misc::Rng::roll0to99());\n\n            int element = ESM::MagicEffect::FireDamage;\n            if (i == 1)\n                element = ESM::MagicEffect::ShockDamage;\n            if (i == 2)\n                element = ESM::MagicEffect::FrostDamage;\n\n            float elementResistance = MWMechanics::getEffectResistanceAttribute(element, &attackerStats.getMagicEffects());\n\n            x = std::min(100.f, x + elementResistance);\n\n            static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fElementalShieldMult\")->mValue.getFloat();\n            x = fElementalShieldMult * magnitude * (1.f - 0.01f * x);\n\n            // Note swapped victim and attacker, since the attacker takes the damage here.\n            x = scaleDamage(x, victim, attacker);\n\n            MWMechanics::DynamicStat<float> health = attackerStats.getHealth();\n            health.setCurrent(health.getCurrent() - x);\n            attackerStats.setHealth(health);\n\n            MWBase::Environment::get().getSoundManager()->playSound3D(attacker, \"Health Damage\", 1.0f, 1.0f);\n        }\n    }\n\n    void reduceWeaponCondition(float damage, bool hit, MWWorld::Ptr &weapon, const MWWorld::Ptr &attacker)\n    {\n        if (weapon.isEmpty())\n            return;\n\n        if (!hit)\n            damage = 0.f;\n\n        const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);\n        if(weaphashealth)\n        {\n            int weaphealth = weapon.getClass().getItemHealth(weapon);\n\n            bool godmode = attacker == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n\n            // weapon condition does not degrade when godmode is on\n            if (!godmode)\n            {\n                const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fWeaponDamageMult\")->mValue.getFloat();\n                float x = std::max(1.f, fWeaponDamageMult * damage);\n\n                weaphealth -= std::min(int(x), weaphealth);\n                weapon.getCellRef().setCharge(weaphealth);\n            }\n\n            // Weapon broken? unequip it\n            if (weaphealth == 0)\n                weapon = *attacker.getClass().getInventoryStore(attacker).unequipItem(weapon, attacker);\n        }\n    }\n\n    void adjustWeaponDamage(float &damage, const MWWorld::Ptr &weapon, const MWWorld::Ptr& attacker)\n    {\n        if (weapon.isEmpty())\n            return;\n\n        const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);\n        if (weaphashealth)\n        {\n            damage *= weapon.getClass().getItemNormalizedHealth(weapon);\n        }\n\n        static const float fDamageStrengthBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"fDamageStrengthBase\")->mValue.getFloat();\n        static const float fDamageStrengthMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"fDamageStrengthMult\")->mValue.getFloat();\n        damage *= fDamageStrengthBase +\n                (attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1f);\n    }\n\n    void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg, float attackStrength)\n    {\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n        float minstrike = store.get<ESM::GameSetting>().find(\"fMinHandToHandMult\")->mValue.getFloat();\n        float maxstrike = store.get<ESM::GameSetting>().find(\"fMaxHandToHandMult\")->mValue.getFloat();\n        damage  = static_cast<float>(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand));\n        damage *= minstrike + ((maxstrike-minstrike)*attackStrength);\n\n        MWMechanics::CreatureStats& otherstats = victim.getClass().getCreatureStats(victim);\n        healthdmg = otherstats.isParalyzed()\n                || otherstats.getKnockedDown();\n        bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf());\n\n        // Options in the launcher's combo box: unarmedFactorsStrengthComboBox\n        // 0 = Do not factor strength into hand-to-hand combat.\n        // 1 = Factor into werewolf hand-to-hand combat.\n        // 2 = Ignore werewolves.\n        int factorStrength = Settings::Manager::getInt(\"strength influences hand to hand\", \"Game\");\n        if (factorStrength == 1 || (factorStrength == 2 && !isWerewolf)) {\n            damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f;\n        }\n\n        if(isWerewolf)\n        {\n            healthdmg = true;\n            // GLOB instead of GMST because it gets updated during a quest\n            damage *= MWBase::Environment::get().getWorld()->getGlobalFloat(\"werewolfclawmult\");\n        }\n        if(healthdmg)\n            damage *= store.get<ESM::GameSetting>().find(\"fHandtoHandHealthPer\")->mValue.getFloat();\n\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        if(isWerewolf)\n        {\n            const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfHit\");\n            if(sound)\n                sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f);\n        }\n        else if (!healthdmg)\n            sndMgr->playSound3D(victim, \"Hand To Hand Hit\", 1.0f, 1.0f);\n    }\n\n    void applyFatigueLoss(const MWWorld::Ptr &attacker, const MWWorld::Ptr &weapon, float attackStrength)\n    {\n        // somewhat of a guess, but using the weapon weight makes sense\n        const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        const float fFatigueAttackBase = store.find(\"fFatigueAttackBase\")->mValue.getFloat();\n        const float fFatigueAttackMult = store.find(\"fFatigueAttackMult\")->mValue.getFloat();\n        const float fWeaponFatigueMult = store.find(\"fWeaponFatigueMult\")->mValue.getFloat();\n        CreatureStats& stats = attacker.getClass().getCreatureStats(attacker);\n        MWMechanics::DynamicStat<float> fatigue = stats.getFatigue();\n        const float normalizedEncumbrance = attacker.getClass().getNormalizedEncumbrance(attacker);\n\n        bool godmode = attacker == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n\n        if (!godmode)\n        {\n            float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;\n            if (!weapon.isEmpty())\n                fatigueLoss += weapon.getClass().getWeight(weapon) * attackStrength * fWeaponFatigueMult;\n            fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);\n            stats.setFatigue(fatigue);\n        }\n    }\n\n    float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2)\n    {\n        osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3());\n        osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3());\n\n        float d = getAggroDistance(actor1, pos1, pos2);\n\n        static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                    \"iFightDistanceBase\")->mValue.getInteger();\n        static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                    \"fFightDistanceMultiplier\")->mValue.getFloat();\n\n        return (iFightDistanceBase - fFightDistanceMultiplier * d);\n    }\n\n    bool isTargetMagicallyHidden(const MWWorld::Ptr& target)\n    {\n        const MagicEffects& magicEffects = target.getClass().getCreatureStats(target).getMagicEffects();\n        return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0)\n            || (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75);\n    }\n\n    float getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs)\n    {\n        if (canActorMoveByZAxis(actor))\n            return distanceIgnoreZ(lhs, rhs);\n        return distance(lhs, rhs);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/combat.hpp",
    "content": "#ifndef OPENMW_MECHANICS_COMBAT_H\n#define OPENMW_MECHANICS_COMBAT_H\n\nnamespace osg\n{\n    class Vec3f;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n\nbool applyOnStrikeEnchantment(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const osg::Vec3f& hitPosition,\n                              const bool fromProjectile=false);\n\n/// @return can we block the attack?\nbool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage, float attackStrength);\n\n/// @return does normal weapon resistance and weakness apply to the weapon?\nbool isNormalWeapon (const MWWorld::Ptr& weapon);\n\nvoid resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage);\n\nvoid applyWerewolfDamageMult (const MWWorld::Ptr& actor, const MWWorld::Ptr& weapon, float &damage);\n\n/// @note for a thrown weapon, \\a weapon == \\a projectile, for bows/crossbows, \\a projectile is the arrow/bolt\n/// @note \\a victim may be empty (e.g. for a hit on terrain), a non-actor (environment objects) or an actor\nvoid projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,\n                    const osg::Vec3f& hitPosition, float attackStrength);\n\n/// Get the chance (in percent) for \\a attacker to successfully hit \\a victim with a given weapon skill value\nfloat getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, int skillValue);\n\n/// Applies damage to attacker based on the victim's elemental shields.\nvoid applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);\n\n/// @param damage Unmitigated weapon damage of the attack\n/// @param hit Was the attack successful?\n/// @param weapon The weapon used.\n/// @note if the weapon is unequipped as result of condition damage, a new Ptr will be assigned to \\a weapon.\nvoid reduceWeaponCondition (float damage, bool hit, MWWorld::Ptr& weapon, const MWWorld::Ptr& attacker);\n\n/// Adjust weapon damage based on its condition. A used weapon will be less effective.\nvoid adjustWeaponDamage (float& damage, const MWWorld::Ptr& weapon, const MWWorld::Ptr& attacker);\n\nvoid getHandToHandDamage (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, float& damage, bool& healthdmg, float attackStrength);\n\n/// Apply the fatigue loss incurred by attacking with the given weapon (weapon may be empty = hand-to-hand)\nvoid applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float attackStrength);\n\nfloat getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2);\n\nbool isTargetMagicallyHidden(const MWWorld::Ptr& target);\n\nfloat getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/creaturestats.cpp",
    "content": "#include \"creaturestats.hpp\"\n\n#include <algorithm>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"summoning.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/creaturestats.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\nnamespace MWMechanics\n{\n    int CreatureStats::sActorId = 0;\n\n    CreatureStats::CreatureStats()\n        : mDrawState (DrawState_Nothing), mDead (false), mDeathAnimationFinished(false), mDied (false), mMurdered(false), mFriendlyHits (0),\n          mTalkedTo (false), mAlarmed (false), mAttacked (false),\n          mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),\n          mHitRecovery(false), mBlock(false), mMovementFlags(0),\n          mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),\n          mDeathAnimation(-1), mTimeOfDeath(), mSideMovementAngle(0), mLevel (0)\n    {\n        for (int i=0; i<4; ++i)\n            mAiSettings[i] = 0;\n    }\n\n    const AiSequence& CreatureStats::getAiSequence() const\n    {\n        return mAiSequence;\n    }\n\n    AiSequence& CreatureStats::getAiSequence()\n    {\n        return mAiSequence;\n    }\n\n    float CreatureStats::getFatigueTerm() const\n    {\n        float max = getFatigue().getModified();\n        float current = getFatigue().getCurrent();\n\n        float normalised = floor(max) == 0 ? 1 : std::max (0.0f, current / max);\n\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        static const float fFatigueBase = gmst.find(\"fFatigueBase\")->mValue.getFloat();\n        static const float fFatigueMult = gmst.find(\"fFatigueMult\")->mValue.getFloat();\n\n        return fFatigueBase - fFatigueMult * (1-normalised);\n    }\n\n    const AttributeValue &CreatureStats::getAttribute(int index) const\n    {\n        if (index < 0 || index > 7) {\n            throw std::runtime_error(\"attribute index is out of range\");\n        }\n        return mAttributes[index];\n    }\n\n    const DynamicStat<float> &CreatureStats::getHealth() const\n    {\n        return mDynamic[0];\n    }\n\n    const DynamicStat<float> &CreatureStats::getMagicka() const\n    {\n        return mDynamic[1];\n    }\n\n    const DynamicStat<float> &CreatureStats::getFatigue() const\n    {\n        return mDynamic[2];\n    }\n\n    const Spells &CreatureStats::getSpells() const\n    {\n        return mSpells;\n    }\n\n    const ActiveSpells &CreatureStats::getActiveSpells() const\n    {\n        return mActiveSpells;\n    }\n\n    const MagicEffects &CreatureStats::getMagicEffects() const\n    {\n        return mMagicEffects;\n    }\n\n    int CreatureStats::getLevel() const\n    {\n        return mLevel;\n    }\n\n    Stat<int> CreatureStats::getAiSetting (AiSetting index) const\n    {\n        return mAiSettings[index];\n    }\n\n    const DynamicStat<float> &CreatureStats::getDynamic(int index) const\n    {\n        if (index < 0 || index > 2) {\n            throw std::runtime_error(\"dynamic stat index is out of range\");\n        }\n        return mDynamic[index];\n    }\n\n    Spells &CreatureStats::getSpells()\n    {\n        return mSpells;\n    }\n\n    ActiveSpells &CreatureStats::getActiveSpells()\n    {\n        /*\n            Start of tes3mp addition\n\n            Set the actorId associated with these ActiveSpells so it can be used inside them\n        */\n        mActiveSpells.setActorId(getActorId());\n        /*\n            End of tes3mp addition\n        */\n        return mActiveSpells;\n    }\n\n    MagicEffects &CreatureStats::getMagicEffects()\n    {\n        return mMagicEffects;\n    }\n\n    void CreatureStats::setAttribute(int index, float base)\n    {\n        AttributeValue current = getAttribute(index);\n        current.setBase(base);\n        setAttribute(index, current);\n    }\n\n    void CreatureStats::setAttribute(int index, const AttributeValue &value)\n    {\n        if (index < 0 || index > 7) {\n            throw std::runtime_error(\"attribute index is out of range\");\n        }\n\n        const AttributeValue& currentValue = mAttributes[index];\n\n        if (value != currentValue)\n        {\n            mAttributes[index] = value;\n\n            if (index == ESM::Attribute::Intelligence)\n                mRecalcMagicka = true;\n            else if (index == ESM::Attribute::Strength ||\n                     index == ESM::Attribute::Willpower ||\n                     index == ESM::Attribute::Agility ||\n                     index == ESM::Attribute::Endurance)\n            {\n                float strength     = getAttribute(ESM::Attribute::Strength).getModified();\n                float willpower    = getAttribute(ESM::Attribute::Willpower).getModified();\n                float agility      = getAttribute(ESM::Attribute::Agility).getModified();\n                float endurance    = getAttribute(ESM::Attribute::Endurance).getModified();\n                DynamicStat<float> fatigue = getFatigue();\n                float diff = (strength+willpower+agility+endurance) - fatigue.getBase();\n                float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0;\n                fatigue.setModified(fatigue.getModified() + diff, 0);\n                fatigue.setCurrent(fatigue.getBase() * currentToBaseRatio);\n                setFatigue(fatigue);\n            }\n        }\n    }\n\n    void CreatureStats::setHealth(const DynamicStat<float> &value)\n    {\n        setDynamic (0, value);\n    }\n\n    void CreatureStats::setMagicka(const DynamicStat<float> &value)\n    {\n        setDynamic (1, value);\n    }\n\n    void CreatureStats::setFatigue(const DynamicStat<float> &value)\n    {\n        setDynamic (2, value);\n    }\n\n    void CreatureStats::setDynamic (int index, const DynamicStat<float> &value)\n    {\n        if (index < 0 || index > 2)\n            throw std::runtime_error(\"dynamic stat index is out of range\");\n\n        mDynamic[index] = value;\n\n        if (index==0 && mDynamic[index].getCurrent()<1)\n        {\n            if (!mDead)\n                mTimeOfDeath = MWBase::Environment::get().getWorld()->getTimeStamp();\n\n            mDead = true;\n\n            mDynamic[index].setModifier(0);\n            mDynamic[index].setCurrentModifier(0);\n            mDynamic[index].setCurrent(0);\n        }\n    }\n\n    void CreatureStats::setLevel(int level)\n    {\n        mLevel = level;\n    }\n\n    void CreatureStats::modifyMagicEffects(const MagicEffects &effects)\n    {\n        if (effects.get(ESM::MagicEffect::FortifyMaximumMagicka).getModifier()\n                != mMagicEffects.get(ESM::MagicEffect::FortifyMaximumMagicka).getModifier())\n            mRecalcMagicka = true;\n\n        mMagicEffects.setModifiers(effects);\n    }\n\n    void CreatureStats::setAiSetting (AiSetting index, Stat<int> value)\n    {\n        mAiSettings[index] = value;\n    }\n\n    void CreatureStats::setAiSetting (AiSetting index, int base)\n    {\n        Stat<int> stat = getAiSetting(index);\n        stat.setBase(base);\n        setAiSetting(index, stat);\n    }\n\n    bool CreatureStats::isParalyzed() const\n    {\n        return mMagicEffects.get(ESM::MagicEffect::Paralyze).getMagnitude() > 0;\n    }\n\n    bool CreatureStats::isDead() const\n    {\n        return mDead;\n    }\n\n    bool CreatureStats::isDeathAnimationFinished() const\n    {\n        return mDeathAnimationFinished;\n    }\n\n    void CreatureStats::setDeathAnimationFinished(bool finished)\n    {\n        mDeathAnimationFinished = finished;\n    }\n\n    void CreatureStats::notifyDied()\n    {\n        mDied = true;\n    }\n\n    bool CreatureStats::hasDied() const\n    {\n        return mDied;\n    }\n\n    void CreatureStats::clearHasDied()\n    {\n        mDied = false;\n    }\n\n    bool CreatureStats::hasBeenMurdered() const\n    {\n        return mMurdered;\n    }\n\n    void CreatureStats::notifyMurder()\n    {\n        mMurdered = true;\n    }\n\n    void CreatureStats::clearHasBeenMurdered()\n    {\n        mMurdered = false;\n    }\n\n    void CreatureStats::resurrect()\n    {\n        if (mDead)\n        {\n            if (mDynamic[0].getModified() < 1)\n                mDynamic[0].setModified(1, 0);\n\n            mDynamic[0].setCurrent(mDynamic[0].getModified());\n            mDead = false;\n            mDeathAnimationFinished = false;\n        }\n    }\n\n    bool CreatureStats::hasCommonDisease() const\n    {\n        return mSpells.hasCommonDisease();\n    }\n\n    bool CreatureStats::hasBlightDisease() const\n    {\n        return mSpells.hasBlightDisease();\n    }\n\n    int CreatureStats::getFriendlyHits() const\n    {\n        return mFriendlyHits;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set the number of friendly hits from elsewhere\n    */\n    void CreatureStats::setFriendlyHits(int hits)\n    {\n        mFriendlyHits = hits;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void CreatureStats::friendlyHit()\n    {\n        ++mFriendlyHits;\n    }\n\n    bool CreatureStats::hasTalkedToPlayer() const\n    {\n        return mTalkedTo;\n    }\n\n    void CreatureStats::talkedToPlayer()\n    {\n        mTalkedTo = true;\n    }\n\n    bool CreatureStats::isAlarmed() const\n    {\n        return mAlarmed;\n    }\n\n    void CreatureStats::setAlarmed (bool alarmed)\n    {\n        mAlarmed = alarmed;\n    }\n\n    bool CreatureStats::getAttacked() const\n    {\n        return mAttacked;\n    }\n\n    void CreatureStats::setAttacked (bool attacked)\n    {\n        mAttacked = attacked;\n    }\n\n    float CreatureStats::getEvasion() const\n    {\n        float evasion = (getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +\n                        (getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);\n        evasion *= getFatigueTerm();\n        evasion += std::min(100.f, mMagicEffects.get(ESM::MagicEffect::Sanctuary).getMagnitude());\n\n        return evasion;\n    }\n\n    void CreatureStats::setLastHitObject(const std::string& objectid)\n    {\n        mLastHitObject = objectid;\n    }\n\n    const std::string &CreatureStats::getLastHitObject() const\n    {\n        return mLastHitObject;\n    }\n\n    void CreatureStats::setLastHitAttemptObject(const std::string& objectid)\n    {\n        mLastHitAttemptObject = objectid;\n    }\n\n    const std::string &CreatureStats::getLastHitAttemptObject() const\n    {\n        return mLastHitAttemptObject;\n    }\n\n    void CreatureStats::setHitAttemptActorId(int actorId)\n    {\n        mHitAttemptActorId = actorId;\n    }\n\n    int CreatureStats::getHitAttemptActorId() const\n    {\n        return mHitAttemptActorId;\n    }\n\n    void CreatureStats::addToFallHeight(float height)\n    {\n        mFallHeight += height;\n    }\n\n    float CreatureStats::getFallHeight() const\n    {\n        return mFallHeight;\n    }\n\n    float CreatureStats::land(bool isPlayer)\n    {\n        if (isPlayer)\n            MWBase::Environment::get().getWorld()->getPlayer().setJumping(false);\n\n        float height = mFallHeight;\n        mFallHeight = 0;\n        return height;\n    }\n\n    bool CreatureStats::needToRecalcDynamicStats()\n    {\n         if (mRecalcMagicka)\n         {\n             mRecalcMagicka = false;\n             return true;\n         }\n         return false;\n    }\n\n    void CreatureStats::setNeedRecalcDynamicStats(bool val)\n    {\n        mRecalcMagicka = val;\n    }\n\n    void CreatureStats::setKnockedDown(bool value)\n    {\n        mKnockdown = value;\n        if(!value) //Resets the \"OverOneFrame\" flag\n            setKnockedDownOverOneFrame(false);\n    }\n\n    bool CreatureStats::getKnockedDown() const\n    {\n        return mKnockdown;\n    }\n\n    void CreatureStats::setKnockedDownOneFrame(bool value)\n    {\n        mKnockdownOneFrame = value;\n    }\n\n    bool CreatureStats::getKnockedDownOneFrame() const\n    {\n        return mKnockdownOneFrame;\n    }\n\n    void CreatureStats::setKnockedDownOverOneFrame(bool value) {\n        mKnockdownOverOneFrame = value;\n    }\n    bool CreatureStats::getKnockedDownOverOneFrame() const {\n        return mKnockdownOverOneFrame;\n    }\n\n    void CreatureStats::setHitRecovery(bool value)\n    {\n        mHitRecovery = value;\n    }\n\n    bool CreatureStats::getHitRecovery() const\n    {\n        return mHitRecovery;\n    }\n\n    void CreatureStats::setBlock(bool value)\n    {\n        mBlock = value;\n    }\n\n    bool CreatureStats::getBlock() const\n    {\n        return mBlock;\n    }\n\n    bool CreatureStats::getMovementFlag (Flag flag) const\n    {\n        return (mMovementFlags & flag) != 0;\n    }\n\n    void CreatureStats::setMovementFlag (Flag flag, bool state)\n    {\n        if (state)\n            mMovementFlags |= flag;\n        else\n            mMovementFlags &= ~flag;\n    }\n\n    bool CreatureStats::getStance(Stance flag) const\n    {\n        switch (flag)\n        {\n            case Stance_Run:\n                return getMovementFlag (Flag_Run) || getMovementFlag (Flag_ForceRun);\n            case Stance_Sneak:\n                return getMovementFlag (Flag_Sneak) || getMovementFlag (Flag_ForceSneak);\n            default:\n                return false;\n        }\n    }\n\n    DrawState_ CreatureStats::getDrawState() const\n    {\n        return mDrawState;\n    }\n\n    void CreatureStats::setDrawState(DrawState_ state)\n    {\n        mDrawState = state;\n    }\n\n    void CreatureStats::writeState (ESM::CreatureStats& state) const\n    {\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            mAttributes[i].writeState (state.mAttributes[i]);\n\n        for (int i=0; i<3; ++i)\n            mDynamic[i].writeState (state.mDynamic[i]);\n\n        state.mTradeTime = mLastRestock.toEsm();\n        state.mGoldPool = mGoldPool;\n\n        state.mDead = mDead;\n        state.mDeathAnimationFinished = mDeathAnimationFinished;\n        state.mDied = mDied;\n        state.mMurdered = mMurdered;\n        // The vanilla engine does not store friendly hits in the save file. Since there's no other mechanism\n        // that ever resets the friendly hits (at least not to my knowledge) this should be regarded a feature\n        // rather than a bug.\n        //state.mFriendlyHits = mFriendlyHits;\n        state.mTalkedTo = mTalkedTo;\n        state.mAlarmed = mAlarmed;\n        state.mAttacked = mAttacked;\n        // TODO: rewrite. does this really need 3 separate bools?\n        state.mKnockdown = mKnockdown;\n        state.mKnockdownOneFrame = mKnockdownOneFrame;\n        state.mKnockdownOverOneFrame = mKnockdownOverOneFrame;\n        state.mHitRecovery = mHitRecovery;\n        state.mBlock = mBlock;\n        state.mMovementFlags = mMovementFlags;\n        state.mFallHeight = mFallHeight; // TODO: vertical velocity (move from PhysicActor to CreatureStats?)\n        state.mLastHitObject = mLastHitObject;\n        state.mLastHitAttemptObject = mLastHitAttemptObject;\n        state.mRecalcDynamicStats = mRecalcMagicka;\n        state.mDrawState = mDrawState;\n        state.mLevel = mLevel;\n        state.mActorId = mActorId;\n        state.mDeathAnimation = mDeathAnimation;\n        state.mTimeOfDeath = mTimeOfDeath.toEsm();\n        //state.mHitAttemptActorId = mHitAttemptActorId;\n\n        mSpells.writeState(state.mSpells);\n        mActiveSpells.writeState(state.mActiveSpells);\n        mAiSequence.writeState(state.mAiSequence);\n        mMagicEffects.writeState(state.mMagicEffects);\n\n        state.mSummonedCreatureMap = mSummonedCreatures;\n        state.mSummonGraveyard = mSummonGraveyard;\n\n        state.mHasAiSettings = true;\n        for (int i=0; i<4; ++i)\n            mAiSettings[i].writeState (state.mAiSettings[i]);\n\n        for (auto it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it)\n        {\n            for (int i=0; i<ESM::Attribute::Length; ++i)\n                state.mCorprusSpells[it->first].mWorsenings[i] = mCorprusSpells.at(it->first).mWorsenings[i];\n\n            state.mCorprusSpells[it->first].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm();\n        }\n    }\n\n    void CreatureStats::readState (const ESM::CreatureStats& state)\n    {\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            mAttributes[i].readState (state.mAttributes[i]);\n\n        for (int i=0; i<3; ++i)\n            mDynamic[i].readState (state.mDynamic[i]);\n\n        mLastRestock = MWWorld::TimeStamp(state.mTradeTime);\n        mGoldPool = state.mGoldPool;\n\n        mDead = state.mDead;\n        mDeathAnimationFinished = state.mDeathAnimationFinished;\n        mDied = state.mDied;\n        mMurdered = state.mMurdered;\n        mTalkedTo = state.mTalkedTo;\n        mAlarmed = state.mAlarmed;\n        mAttacked = state.mAttacked;\n        // TODO: rewrite. does this really need 3 separate bools?\n        mKnockdown = state.mKnockdown;\n        mKnockdownOneFrame = state.mKnockdownOneFrame;\n        mKnockdownOverOneFrame = state.mKnockdownOverOneFrame;\n        mHitRecovery = state.mHitRecovery;\n        mBlock = state.mBlock;\n        mMovementFlags = state.mMovementFlags;\n        mFallHeight = state.mFallHeight;\n        mLastHitObject = state.mLastHitObject;\n        mLastHitAttemptObject = state.mLastHitAttemptObject;\n        mRecalcMagicka = state.mRecalcDynamicStats;\n        mDrawState = DrawState_(state.mDrawState);\n        mLevel = state.mLevel;\n        mActorId = state.mActorId;\n        mDeathAnimation = state.mDeathAnimation;\n        mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath);\n        //mHitAttemptActorId = state.mHitAttemptActorId;\n\n        mSpells.readState(state.mSpells, this);\n        mActiveSpells.readState(state.mActiveSpells);\n        mAiSequence.readState(state.mAiSequence);\n        mMagicEffects.readState(state.mMagicEffects);\n\n        // Rebuild the bound item cache\n        for(int effectId = ESM::MagicEffect::BoundDagger; effectId <= ESM::MagicEffect::BoundGloves; effectId++)\n        {\n            if(mMagicEffects.get(effectId).getMagnitude() > 0)\n                mBoundItems.insert(effectId);\n            else\n            {\n                // Check active spell effects\n                // We can't use mActiveSpells::getMagicEffects here because it doesn't include expired effects\n                auto spell = std::find_if(mActiveSpells.begin(), mActiveSpells.end(), [&] (const auto& spell)\n                {\n                    const auto& effects = spell.second.mEffects;\n                    return std::find_if(effects.begin(), effects.end(), [&] (const auto& effect)\n                    {\n                        return effect.mEffectId == effectId;\n                    }) != effects.end();\n                });\n                if(spell != mActiveSpells.end())\n                    mBoundItems.insert(effectId);\n            }\n        }\n\n        mSummonedCreatures = state.mSummonedCreatureMap;\n        mSummonGraveyard = state.mSummonGraveyard;\n\n        if (state.mHasAiSettings)\n            for (int i=0; i<4; ++i)\n                mAiSettings[i].readState(state.mAiSettings[i]);\n\n        mCorprusSpells.clear();\n        for (auto it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it)\n        {\n            for (int i=0; i<ESM::Attribute::Length; ++i)\n                mCorprusSpells[it->first].mWorsenings[i] = state.mCorprusSpells.at(it->first).mWorsenings[i];\n\n            mCorprusSpells[it->first].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening);\n        }\n    }\n\n    void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime)\n    {\n        mLastRestock = tradeTime;\n    }\n\n    MWWorld::TimeStamp CreatureStats::getLastRestockTime() const\n    {\n        return mLastRestock;\n    }\n\n    void CreatureStats::setGoldPool(int pool)\n    {\n        mGoldPool = pool;\n    }\n    int CreatureStats::getGoldPool() const\n    {\n        return mGoldPool;\n    }\n\n    int CreatureStats::getActorId()\n    {\n        if (mActorId==-1)\n            mActorId = sActorId++;\n\n        return mActorId;\n    }\n\n    bool CreatureStats::matchesActorId (int id) const\n    {\n        return mActorId!=-1 && id==mActorId;\n    }\n\n    void CreatureStats::cleanup()\n    {\n        sActorId = 0;\n    }\n\n    void CreatureStats::writeActorIdCounter (ESM::ESMWriter& esm)\n    {\n        esm.startRecord(ESM::REC_ACTC);\n        esm.writeHNT(\"COUN\", sActorId);\n        esm.endRecord(ESM::REC_ACTC);\n    }\n\n    void CreatureStats::readActorIdCounter (ESM::ESMReader& esm)\n    {\n        esm.getHNT(sActorId, \"COUN\");\n    }\n\n    signed char CreatureStats::getDeathAnimation() const\n    {\n        return mDeathAnimation;\n    }\n\n    void CreatureStats::setDeathAnimation(signed char index)\n    {\n        mDeathAnimation = index;\n    }\n\n    MWWorld::TimeStamp CreatureStats::getTimeOfDeath() const\n    {\n        return mTimeOfDeath;\n    }\n\n    std::map<ESM::SummonKey, int>& CreatureStats::getSummonedCreatureMap()\n    {\n        return mSummonedCreatures;\n    }\n\n    std::vector<int>& CreatureStats::getSummonedCreatureGraveyard()\n    {\n        return mSummonGraveyard;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set a new actorId for summoned creatures, necessary for properly\n        initializing them after syncing them across players\n    */\n    void CreatureStats::setSummonedCreatureActorId(std::string refId, int actorId)\n    {\n        for (std::map<ESM::SummonKey, int>::iterator it = mSummonedCreatures.begin(); it != mSummonedCreatures.end(); )\n        {\n            if (Misc::StringUtils::ciEqual(getSummonedCreature(it->first.mEffectId), refId) && it->second == -1)\n            {\n                it->second = actorId;\n                break;\n            }\n            else\n                ++it;\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    std::map<std::string, CorprusStats> &CreatureStats::getCorprusSpells()\n    {\n        return mCorprusSpells;\n    }\n\n    void CreatureStats::addCorprusSpell(const std::string& sourceId, CorprusStats& stats)\n    {\n        mCorprusSpells[sourceId] = stats;\n    }\n\n    void CreatureStats::removeCorprusSpell(const std::string& sourceId)\n    {\n        auto corprusIt = mCorprusSpells.find(sourceId);\n        if (corprusIt != mCorprusSpells.end())\n        {\n            mCorprusSpells.erase(corprusIt);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/creaturestats.hpp",
    "content": "#ifndef GAME_MWMECHANICS_CREATURESTATS_H\n#define GAME_MWMECHANICS_CREATURESTATS_H\n\n#include <set>\n#include <string>\n#include <stdexcept>\n\n#include \"stat.hpp\"\n#include \"magiceffects.hpp\"\n#include \"spells.hpp\"\n#include \"activespells.hpp\"\n#include \"aisequence.hpp\"\n#include \"drawstate.hpp\"\n\n#include <components/esm/attr.hpp>\n#include <components/esm/magiceffects.hpp>\n\nnamespace ESM\n{\n    struct CreatureStats;\n}\n\nnamespace MWMechanics\n{\n    struct CorprusStats\n    {\n        static constexpr int sWorseningPeriod = 24;\n\n        int mWorsenings[ESM::Attribute::Length];\n        MWWorld::TimeStamp mNextWorsening;\n    };\n\n    /// \\brief Common creature stats\n    ///\n    ///\n    class CreatureStats\n    {\n        static int sActorId;\n        DrawState_ mDrawState;\n        AttributeValue mAttributes[ESM::Attribute::Length];\n        DynamicStat<float> mDynamic[3]; // health, magicka, fatigue\n        Spells mSpells;\n        ActiveSpells mActiveSpells;\n        MagicEffects mMagicEffects;\n        Stat<int> mAiSettings[4];\n        AiSequence mAiSequence;\n        bool mDead;\n        bool mDeathAnimationFinished;\n        bool mDied; // flag for OnDeath script function\n        bool mMurdered;\n        int mFriendlyHits;\n        bool mTalkedTo;\n        bool mAlarmed;\n        bool mAttacked;\n        bool mKnockdown;\n        bool mKnockdownOneFrame;\n        bool mKnockdownOverOneFrame;\n        bool mHitRecovery;\n        bool mBlock;\n        unsigned int mMovementFlags;\n\n        float mFallHeight;\n\n        std::string mLastHitObject; // The last object to hit this actor\n        std::string mLastHitAttemptObject; // The last object to attempt to hit this actor\n\n        bool mRecalcMagicka;\n\n        // For merchants: the last time items were restocked and gold pool refilled.\n        MWWorld::TimeStamp mLastRestock;\n\n        // The pool of merchant gold (not in inventory)\n        int mGoldPool;\n\n        int mActorId;\n        int mHitAttemptActorId; // Stores an actor that attacked this actor. Only one is stored at a time,\n                                // and it is not changed if a different actor attacks. It is cleared when combat ends.\n\n        // The index of the death animation that was played, or -1 if none played\n        signed char mDeathAnimation;\n\n        MWWorld::TimeStamp mTimeOfDeath;\n\n        // The difference between view direction and lower body direction.\n        float mSideMovementAngle;\n\n    private:\n        std::map<ESM::SummonKey, int> mSummonedCreatures; // <SummonKey, ActorId>\n\n        // Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet.\n        // This may be necessary when the creature is in an inactive cell.\n        std::vector<int> mSummonGraveyard;\n\n        std::map<std::string, CorprusStats> mCorprusSpells;\n\n    protected:\n        int mLevel;\n\n    public:\n        CreatureStats();\n\n        DrawState_ getDrawState() const;\n        void setDrawState(DrawState_ state);\n\n        bool needToRecalcDynamicStats();\n        void setNeedRecalcDynamicStats(bool val);\n\n        float getFallHeight() const;\n        void addToFallHeight(float height);\n\n        /// Reset the fall height\n        /// @return total fall height\n        float land(bool isPlayer=false);\n\n        const AttributeValue & getAttribute(int index) const;\n\n        const DynamicStat<float> & getHealth() const;\n\n        const DynamicStat<float> & getMagicka() const;\n\n        const DynamicStat<float> & getFatigue() const;\n\n        const DynamicStat<float> & getDynamic (int index) const;\n\n        const Spells & getSpells() const;\n\n        const ActiveSpells & getActiveSpells() const;\n\n        const MagicEffects & getMagicEffects() const;\n\n        bool getAttackingOrSpell() const;\n\n        int getLevel() const;\n\n        Spells & getSpells();\n\n        ActiveSpells & getActiveSpells();\n\n        MagicEffects & getMagicEffects();\n\n        void setAttribute(int index, const AttributeValue &value);\n        // Shortcut to set only the base\n        void setAttribute(int index, float base);\n\n        void setHealth(const DynamicStat<float> &value);\n\n        void setMagicka(const DynamicStat<float> &value);\n\n        void setFatigue(const DynamicStat<float> &value);\n\n        void setDynamic (int index, const DynamicStat<float> &value);\n\n        /// Set Modifier for each magic effect according to \\a effects. Does not touch Base values.\n        void modifyMagicEffects(const MagicEffects &effects);\n\n        void setAttackingOrSpell(bool attackingOrSpell);\n\n        void setLevel(int level);\n\n        enum AiSetting\n        {\n            AI_Hello = 0,\n            AI_Fight = 1,\n            AI_Flee = 2,\n            AI_Alarm = 3\n        };\n        void setAiSetting (AiSetting index, Stat<int> value);\n        void setAiSetting (AiSetting index, int base);\n        Stat<int> getAiSetting (AiSetting index) const;\n\n        const AiSequence& getAiSequence() const;\n\n        AiSequence& getAiSequence();\n\n        float getFatigueTerm() const;\n        ///< Return effective fatigue\n\n        bool isParalyzed() const;\n\n        bool isDead() const;\n\n        bool isDeathAnimationFinished() const;\n        void setDeathAnimationFinished(bool finished);\n\n        void notifyDied();\n\n        bool hasDied() const;\n\n        void clearHasDied();\n\n        bool hasBeenMurdered() const;\n\n        void clearHasBeenMurdered();\n\n        void notifyMurder();\n\n        void resurrect();\n\n        bool hasCommonDisease() const;\n\n        bool hasBlightDisease() const;\n\n        int getFriendlyHits() const;\n        ///< Number of friendly hits received.\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to set the number of friendly hits from elsewhere\n        */\n        void setFriendlyHits(int hits);\n        /*\n            End of tes3mp addition\n        */\n\n        void friendlyHit();\n        ///< Increase number of friendly hits by one.\n\n        bool hasTalkedToPlayer() const;\n        ///< Has this creature talked with the player before?\n\n        void talkedToPlayer();\n\n        bool isAlarmed() const;\n        void setAlarmed (bool alarmed);\n\n        bool getAttacked() const;\n        void setAttacked (bool attacked);\n\n        float getEvasion() const;\n\n        void setKnockedDown(bool value);\n        /// Returns true for the entire duration of the actor being knocked down or knocked out,\n        /// including transition animations (falling down & standing up)\n        bool getKnockedDown() const;\n        void setKnockedDownOneFrame(bool value);\n        ///Returns true only for the first frame of the actor being knocked out; used for \"onKnockedOut\" command\n        bool getKnockedDownOneFrame() const;\n        void setKnockedDownOverOneFrame(bool value);\n        ///Returns true for all but the first frame of being knocked out; used to know to not reset mKnockedDownOneFrame\n        bool getKnockedDownOverOneFrame() const;\n        void setHitRecovery(bool value);\n        bool getHitRecovery() const;\n        void setBlock(bool value);\n        bool getBlock() const;\n\n        std::map<ESM::SummonKey, int>& getSummonedCreatureMap(); // <SummonKey, ActorId of summoned creature>\n        std::vector<int>& getSummonedCreatureGraveyard(); // ActorIds\n\n         /*\n            Start of tes3mp addition\n\n            Make it possible to set a new actorId for summoned creatures, necessary for properly\n            initializing them after syncing them across players\n         */\n        void setSummonedCreatureActorId(std::string refId, int actorId);\n        /*\n            End of tes3mp addition\n        */\n\n        enum Flag\n        {\n            Flag_ForceRun = 1,\n            Flag_ForceSneak = 2,\n            Flag_Run = 4,\n            Flag_Sneak = 8,\n            Flag_ForceJump = 16,\n            Flag_ForceMoveJump = 32\n        };\n        enum Stance\n        {\n            Stance_Run,\n            Stance_Sneak\n        };\n\n        bool getMovementFlag (Flag flag) const;\n        void setMovementFlag (Flag flag, bool state);\n        /// Like getMovementFlag, but also takes into account if the flag is Forced\n        bool getStance (Stance flag) const;\n\n        void setLastHitObject(const std::string &objectid);\n        const std::string &getLastHitObject() const;\n        void setLastHitAttemptObject(const std::string &objectid);\n        const std::string &getLastHitAttemptObject() const;\n        void setHitAttemptActorId(const int actorId);\n        int getHitAttemptActorId() const;\n\n        // Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves.\n        // TODO: Put it somewhere else?\n        std::set<int> mBoundItems;\n\n        void writeState (ESM::CreatureStats& state) const;\n\n        void readState (const ESM::CreatureStats& state);\n\n        static void writeActorIdCounter (ESM::ESMWriter& esm);\n        static void readActorIdCounter (ESM::ESMReader& esm);\n\n        void setLastRestockTime(MWWorld::TimeStamp tradeTime);\n        MWWorld::TimeStamp getLastRestockTime() const;\n\n        void setGoldPool(int pool);\n        int getGoldPool() const;\n\n        signed char getDeathAnimation() const; // -1 means not decided\n        void setDeathAnimation(signed char index);\n\n        MWWorld::TimeStamp getTimeOfDeath() const;\n\n        int getActorId();\n        ///< Will generate an actor ID, if the actor does not have one yet.\n\n        bool matchesActorId (int id) const;\n        ///< Check if \\a id matches the actor ID of *this (if the actor does not have an ID\n        /// assigned this function will return false).\n\n        static void cleanup();\n\n        std::map<std::string, CorprusStats> & getCorprusSpells();\n\n        void addCorprusSpell(const std::string& sourceId, CorprusStats& stats);\n\n        void removeCorprusSpell(const std::string& sourceId);\n\n        float getSideMovementAngle() const { return mSideMovementAngle; }\n        void setSideMovementAngle(float angle) { mSideMovementAngle = angle; }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/difficultyscaling.cpp",
    "content": "#include \"difficultyscaling.hpp\"\n\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"actorutil.hpp\"\n\nfloat scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim)\n{\n    const MWWorld::Ptr& player = MWMechanics::getPlayer();\n\n    // [-500, 500]\n    int difficultySetting = Settings::Manager::getInt(\"difficulty\", \"Game\");\n    difficultySetting = std::min(difficultySetting, 500);\n    difficultySetting = std::max(difficultySetting, -500);\n\n    /*\n        Start of tes3mp change (major)\n\n        Use difficulty setting received from server instead of basing it on client settings\n    */\n    difficultySetting = mwmp::Main::get().getLocalPlayer()->difficulty;\n    /*\n        End of tes3mp change (major)\n    */\n\n    static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fDifficultyMult\")->mValue.getFloat();\n\n    float difficultyTerm = 0.01f * difficultySetting;\n\n    float x = 0;\n    if (victim == player)\n    {\n        if (difficultyTerm > 0)\n            x = fDifficultyMult * difficultyTerm;\n        else\n            x = difficultyTerm / fDifficultyMult;\n    }\n    else if (attacker == player)\n    {\n        if (difficultyTerm > 0)\n            x = -difficultyTerm / fDifficultyMult;\n        else\n            x = fDifficultyMult * (-difficultyTerm);\n    }\n\n    damage *= 1 + x;\n    return damage;\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/difficultyscaling.hpp",
    "content": "#ifndef OPENMW_MWMECHANICS_DIFFICULTYSCALING_H\n#define OPENMW_MWMECHANICS_DIFFICULTYSCALING_H\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\n/// Scales damage dealt to an actor based on difficulty setting\nfloat scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/disease.hpp",
    "content": "#ifndef OPENMW_MECHANICS_DISEASE_H\n#define OPENMW_MECHANICS_DISEASE_H\n\n#include <components/misc/rng.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"spells.hpp\"\n#include \"creaturestats.hpp\"\n#include \"actorutil.hpp\"\n\nnamespace MWMechanics\n{\n\n    /// Call when \\a actor has got in contact with \\a carrier (e.g. hit by him, or loots him)\n    /// @param actor The actor that will potentially catch diseases. Currently only the player can catch diseases.\n    /// @param carrier The disease carrier.\n    inline void diseaseContact (MWWorld::Ptr actor, MWWorld::Ptr carrier)\n    {\n        if (!carrier.getClass().isActor() || actor != getPlayer())\n            return;\n\n        float fDiseaseXferChance =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                    \"fDiseaseXferChance\")->mValue.getFloat();\n\n        MagicEffects& actorEffects = actor.getClass().getCreatureStats(actor).getMagicEffects();\n\n        Spells& spells = carrier.getClass().getCreatureStats(carrier).getSpells();\n        for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)\n        {\n            const ESM::Spell* spell = it->first;\n            if (actor.getClass().getCreatureStats(actor).getSpells().hasSpell(spell->mId))\n                continue;\n\n            float resist = 0.f;\n            if (Spells::hasCorprusEffect(spell))\n                resist = 1.f - 0.01f * (actorEffects.get(ESM::MagicEffect::ResistCorprusDisease).getMagnitude()\n                                        - actorEffects.get(ESM::MagicEffect::WeaknessToCorprusDisease).getMagnitude());\n            else if (spell->mData.mType == ESM::Spell::ST_Disease)\n                resist = 1.f - 0.01f * (actorEffects.get(ESM::MagicEffect::ResistCommonDisease).getMagnitude()\n                                        - actorEffects.get(ESM::MagicEffect::WeaknessToCommonDisease).getMagnitude());\n            else if (spell->mData.mType == ESM::Spell::ST_Blight)\n                resist = 1.f - 0.01f * (actorEffects.get(ESM::MagicEffect::ResistBlightDisease).getMagnitude()\n                                        - actorEffects.get(ESM::MagicEffect::WeaknessToBlightDisease).getMagnitude());\n            else\n                continue;\n\n            int x = static_cast<int>(fDiseaseXferChance * 100 * resist);\n            if (Misc::Rng::rollDice(10000) < x)\n            {\n                // Contracted disease!\n                actor.getClass().getCreatureStats(actor).getSpells().add(it->first);\n                MWBase::Environment::get().getWorld()->applyLoopingParticles(actor);\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_PLAYER_SPELLBOOK packet every time a player gains a disease\n                */\n                mwmp::Main::get().getLocalPlayer()->sendSpellChange(it->first->mId, mwmp::SpellbookChanges::ADD);\n                /*\n                    End of tes3mp addition\n                */\n\n                std::string msg = \"sMagicContractDisease\";\n                msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(msg)->mValue.getString();\n                msg = Misc::StringUtils::format(msg, spell->mName);\n                MWBase::Environment::get().getWindowManager()->messageBox(msg);\n            }\n        }\n    }\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/drawstate.hpp",
    "content": "#ifndef GAME_MWMECHANICS_DRAWSTATE_H\n#define GAME_MWMECHANICS_DRAWSTATE_H\n\nnamespace MWMechanics\n{\n    /// \\note The _ suffix is required to avoid a collision with a Windoze macro. Die, Microsoft! Die!\n    enum DrawState_\n    {\n        DrawState_Nothing = 0,\n        DrawState_Weapon = 1,\n        DrawState_Spell = 2\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/enchanting.cpp",
    "content": "#include \"enchanting.hpp\"\n\n#include <components/misc/rng.hpp>\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/Worldstate.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"spellutil.hpp\"\n#include \"actorutil.hpp\"\n#include \"weapontype.hpp\"\n\nnamespace MWMechanics\n{\n    Enchanting::Enchanting()\n        : mCastStyle(ESM::Enchantment::CastOnce)\n        , mSelfEnchanting(false)\n        , mWeaponType(-1)\n    {}\n\n    void Enchanting::setOldItem(const MWWorld::Ptr& oldItem)\n    {\n        mOldItemPtr=oldItem;\n        mWeaponType = -1;\n        mObjectType.clear();\n        if(!itemEmpty())\n        {\n            mObjectType = mOldItemPtr.getTypeName();\n            if (mObjectType == typeid(ESM::Weapon).name())\n                mWeaponType = mOldItemPtr.get<ESM::Weapon>()->mBase->mData.mType;\n        }\n    }\n\n    void Enchanting::setNewItemName(const std::string& s)\n    {\n        mNewItemName=s;\n    }\n\n    void Enchanting::setEffect(const ESM::EffectList& effectList)\n    {\n        mEffectList=effectList;\n    }\n\n    int Enchanting::getCastStyle() const\n    {\n        return mCastStyle;\n    }\n\n    void Enchanting::setSoulGem(const MWWorld::Ptr& soulGem)\n    {\n        mSoulGemPtr=soulGem;\n    }\n\n    bool Enchanting::create()\n    {\n        const MWWorld::Ptr& player = getPlayer();\n        MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);\n        ESM::Enchantment enchantment;\n        enchantment.mData.mFlags = 0;\n        enchantment.mData.mType = mCastStyle;\n        enchantment.mData.mCost = getBaseCastCost();\n\n        store.remove(mSoulGemPtr, 1, player);\n\n        //Exception for Azura Star, new one will be added after enchanting\n        if(Misc::StringUtils::ciEqual(mSoulGemPtr.get<ESM::Miscellaneous>()->mBase->mId, \"Misc_SoulGem_Azura\"))\n            store.add(\"Misc_SoulGem_Azura\", 1, player);\n\n        if(mSelfEnchanting)\n        {\n            if(getEnchantChance() <= (Misc::Rng::roll0to99()))\n                return false;\n\n            mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);\n        }\n\n        enchantment.mEffects = mEffectList;\n\n        int count = getEnchantItemsCount();\n\n        if(mCastStyle==ESM::Enchantment::ConstantEffect)\n            enchantment.mData.mCharge = 0;\n        else\n            enchantment.mData.mCharge = getGemCharge() / count;\n\n        // Try to find a dynamic enchantment with the same stats, create a new one if not found.\n        const ESM::Enchantment* enchantmentPtr = getRecord(enchantment);\n        if (enchantmentPtr == nullptr)\n            enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);\n\n        // Apply the enchantment\n\n        /*\n            Start of tes3mp change (major)\n\n            Send the enchantment's record to the server\n\n            Don't add the new item to the player's inventory and instead expect the server to\n            add it\n\n            Store the quantity used for the enchantment so it can be retrieved in applyEnchantment()\n            when applicable\n            \n            The applyEnchantment() method is where the record of the newly enchanted item will be sent\n            to the server, causing the server to send back the player's inventory with the new item\n            included\n        */\n        mwmp::Main::get().getNetworking()->getWorldstate()->sendEnchantmentRecord(enchantmentPtr);\n\n        store.remove(mOldItemPtr, count, player);\n\n        if(!mSelfEnchanting)\n            payForEnchantment();\n\n        mwmp::Main::get().getLocalPlayer()->storeLastEnchantmentQuantity(count);\n\n        std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);\n        /*\n            End of tes3mp change (major)\n        */\n\n        return true;\n    }\n    \n    void Enchanting::nextCastStyle()\n    {\n        if (itemEmpty())\n            return;\n\n        const bool powerfulSoul = getGemCharge() >= \\\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find (\"iSoulAmountForConstantEffect\")->mValue.getInteger();\n        if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name()))\n        { // Armor or Clothing\n            switch(mCastStyle)\n            {\n                case ESM::Enchantment::WhenUsed:\n                    if (powerfulSoul)\n                        mCastStyle = ESM::Enchantment::ConstantEffect;\n                    return;\n                default: // takes care of Constant effect too\n                    mCastStyle = ESM::Enchantment::WhenUsed;\n                    return;\n            }\n        }\n        else if (mWeaponType != -1)\n        { // Weapon\n            ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;\n            switch(mCastStyle)\n            {\n                case ESM::Enchantment::WhenStrikes:\n                    if (weapclass == ESM::WeaponType::Melee || weapclass == ESM::WeaponType::Ranged)\n                        mCastStyle = ESM::Enchantment::WhenUsed;\n                    return;\n                case ESM::Enchantment::WhenUsed:\n                    if (powerfulSoul && weapclass != ESM::WeaponType::Ammo && weapclass != ESM::WeaponType::Thrown)\n                        mCastStyle = ESM::Enchantment::ConstantEffect;\n                    else if (weapclass != ESM::WeaponType::Ranged)\n                        mCastStyle = ESM::Enchantment::WhenStrikes;\n                    return;\n                default: // takes care of Constant effect too\n                    mCastStyle = ESM::Enchantment::WhenUsed;\n                    if (weapclass != ESM::WeaponType::Ranged)\n                        mCastStyle = ESM::Enchantment::WhenStrikes;\n                    return;\n            }\n        }\n        else if(mObjectType == typeid(ESM::Book).name())\n        { // Scroll or Book\n            mCastStyle = ESM::Enchantment::CastOnce;\n            return;\n        }\n\n        // Fail case\n        mCastStyle = ESM::Enchantment::CastOnce;\n    }\n\n    /*\n     * Vanilla enchant cost formula:\n     *\n     *  Touch/Self:          (min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025\n     *  Target:       1.5 * ((min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025)\n     *  Constant eff:        (min + max) * baseCost * 2.5              + area * baseCost * 0.025\n     *\n     *  For multiple effects - cost of each effect is multiplied by number of effects that follows +1.\n     *\n     *  Note: Minimal value inside formula for 'min' and 'max' is 1. So in vanilla:\n     *        (0 + 0) == (1 + 0) == (1 + 1) => 2 or (2 + 0) == (1 + 2) => 3\n     *\n     *  Formula on UESPWiki is not entirely correct.\n     */\n    float Enchanting::getEnchantPoints(bool precise) const\n    {\n        if (mEffectList.mList.empty())\n            // No effects added, cost = 0\n            return 0;\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        const float fEffectCostMult = store.get<ESM::GameSetting>().find(\"fEffectCostMult\")->mValue.getFloat();\n        const float fEnchantmentConstantDurationMult = store.get<ESM::GameSetting>().find(\"fEnchantmentConstantDurationMult\")->mValue.getFloat();\n\n        float enchantmentCost = 0.f;\n        float cost = 0.f;\n        for (const ESM::ENAMstruct& effect : mEffectList.mList)\n        {\n            float baseCost = (store.get<ESM::MagicEffect>().find(effect.mEffectID))->mData.mBaseCost;\n            int magMin = std::max(1, effect.mMagnMin);\n            int magMax = std::max(1, effect.mMagnMax);\n            int area = std::max(1, effect.mArea);\n            float duration = static_cast<float>(effect.mDuration);\n            if (mCastStyle == ESM::Enchantment::ConstantEffect)\n                duration = fEnchantmentConstantDurationMult;\n\n            cost += ((magMin + magMax) * duration + area) * baseCost * fEffectCostMult * 0.05f;\n\n            cost = std::max(1.f, cost);\n\n            if (effect.mRange == ESM::RT_Target)\n                cost *= 1.5f;\n\n            enchantmentCost += precise ? cost : std::floor(cost);\n        }\n\n        return enchantmentCost;\n    }\n\n    const ESM::Enchantment* Enchanting::getRecord(const ESM::Enchantment& toFind) const\n    {\n        const MWWorld::Store<ESM::Enchantment>& enchantments = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>();\n        MWWorld::Store<ESM::Enchantment>::iterator iter (enchantments.begin());\n        iter += (enchantments.getSize() - enchantments.getDynamicSize());\n        for (; iter != enchantments.end(); ++iter)\n        {\n            if (iter->mEffects.mList.size() != toFind.mEffects.mList.size())\n                continue;\n\n            if (iter->mData.mFlags != toFind.mData.mFlags\n                    || iter->mData.mType != toFind.mData.mType\n                    || iter->mData.mCost != toFind.mData.mCost\n                    || iter->mData.mCharge != toFind.mData.mCharge)\n                continue;\n\n            // Don't choose an ID that came from the content files, would have unintended side effects\n            if (!enchantments.isDynamic(iter->mId))\n                continue;\n\n            bool mismatch = false;\n\n            for (int i=0; i<static_cast<int> (iter->mEffects.mList.size()); ++i)\n            {\n                const ESM::ENAMstruct& first = iter->mEffects.mList[i];\n                const ESM::ENAMstruct& second = toFind.mEffects.mList[i];\n\n                if (first.mEffectID!=second.mEffectID ||\n                    first.mArea!=second.mArea ||\n                    first.mRange!=second.mRange ||\n                    first.mSkill!=second.mSkill ||\n                    first.mAttribute!=second.mAttribute ||\n                    first.mMagnMin!=second.mMagnMin ||\n                    first.mMagnMax!=second.mMagnMax ||\n                    first.mDuration!=second.mDuration)\n                {\n                    mismatch = true;\n                    break;\n                }\n            }\n\n            if (!mismatch)\n                return &(*iter);\n        }\n\n        return nullptr;\n    }\n\n    int Enchanting::getBaseCastCost() const\n    {\n        if (mCastStyle == ESM::Enchantment::ConstantEffect)\n            return 0;\n\n        return static_cast<int>(getEnchantPoints(false));\n    }\n\n    int Enchanting::getEffectiveCastCost() const\n    {\n        int baseCost = getBaseCastCost();\n        MWWorld::Ptr player = getPlayer();\n        return getEffectiveEnchantmentCastCost(static_cast<float>(baseCost), player);\n    }\n\n\n    int Enchanting::getEnchantPrice() const\n    {\n        if(mEnchanter.isEmpty())\n            return 0;\n\n        float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find (\"fEnchantmentValueMult\")->mValue.getFloat();\n        int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast<int>(getEnchantPoints() * priceMultipler), true);\n        price *= getEnchantItemsCount() * getTypeMultiplier();\n        return std::max(1, price);\n    }\n\n    int Enchanting::getGemCharge() const\n    {\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        if(soulEmpty())\n            return 0;\n        if(mSoulGemPtr.getCellRef().getSoul()==\"\")\n            return 0;\n        const ESM::Creature* soul = store.get<ESM::Creature>().search(mSoulGemPtr.getCellRef().getSoul());\n        if(soul)\n            return soul->mData.mSoul;\n        else\n            return 0;\n    }\n\n    int Enchanting::getMaxEnchantValue() const\n    {\n        if (itemEmpty())\n            return 0;\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n\n        return static_cast<int>(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get<ESM::GameSetting>().find(\"fEnchantmentMult\")->mValue.getFloat());\n    }\n    bool Enchanting::soulEmpty() const\n    {\n        return mSoulGemPtr.isEmpty();\n    }\n\n    bool Enchanting::itemEmpty() const\n    {\n        return mOldItemPtr.isEmpty();\n    }\n\n    void Enchanting::setSelfEnchanting(bool selfEnchanting)\n    {\n        mSelfEnchanting = selfEnchanting;\n    }\n\n    void Enchanting::setEnchanter(const MWWorld::Ptr& enchanter)\n    {\n        mEnchanter = enchanter;\n        // Reset cast style\n        mCastStyle = ESM::Enchantment::CastOnce;\n    }\n\n    int Enchanting::getEnchantChance() const\n    {\n        const CreatureStats& stats = mEnchanter.getClass().getCreatureStats(mEnchanter);\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        const float a = static_cast<float>(mEnchanter.getClass().getSkill(mEnchanter, ESM::Skill::Enchant));\n        const float b = static_cast<float>(stats.getAttribute (ESM::Attribute::Intelligence).getModified());\n        const float c = static_cast<float>(stats.getAttribute (ESM::Attribute::Luck).getModified());\n        const float fEnchantmentChanceMult = gmst.find(\"fEnchantmentChanceMult\")->mValue.getFloat();\n        const float fEnchantmentConstantChanceMult = gmst.find(\"fEnchantmentConstantChanceMult\")->mValue.getFloat();\n\n        float x = (a - getEnchantPoints() * fEnchantmentChanceMult * getTypeMultiplier() * getEnchantItemsCount() + 0.2f * b + 0.1f * c) * stats.getFatigueTerm();\n        if (mCastStyle == ESM::Enchantment::ConstantEffect)\n            x *= fEnchantmentConstantChanceMult;\n\n        return static_cast<int>(x);\n    }\n\n    int Enchanting::getEnchantItemsCount() const\n    {\n        int count = 1;\n        float enchantPoints = getEnchantPoints();\n        if (mWeaponType != -1 && enchantPoints > 0)\n        {\n            ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;\n            if (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo)\n            {\n                static const float multiplier = std::max(0.f, std::min(1.0f, Settings::Manager::getFloat(\"projectiles enchant multiplier\", \"Game\")));\n                MWWorld::Ptr player = getPlayer();\n                int itemsInInventoryCount = player.getClass().getContainerStore(player).count(mOldItemPtr.getCellRef().getRefId());\n                count = std::min(itemsInInventoryCount, std::max(1, int(getGemCharge() * multiplier / enchantPoints)));\n            }\n        }\n\n        return count;\n    }\n\n    float Enchanting::getTypeMultiplier() const\n    {\n        static const bool useMultiplier = Settings::Manager::getFloat(\"projectiles enchant multiplier\", \"Game\") > 0;\n        if (useMultiplier && mWeaponType != -1 && getEnchantPoints() > 0)\n        {\n            ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;\n            if (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo)\n                return 0.125f;\n        }\n\n        return 1.f;\n    }\n\n    void Enchanting::payForEnchantment() const\n    {\n        const MWWorld::Ptr& player = getPlayer();\n        MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);\n\n        store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player);\n\n        // add gold to NPC trading gold pool\n        CreatureStats& enchanterStats = mEnchanter.getClass().getCreatureStats(mEnchanter);\n        enchanterStats.setGoldPool(enchanterStats.getGoldPool() + getEnchantPrice());\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/enchanting.hpp",
    "content": "#ifndef GAME_MWMECHANICS_ENCHANTING_H\n#define GAME_MWMECHANICS_ENCHANTING_H\n\n#include <string>\n\n#include <components/esm/effectlist.hpp>\n#include <components/esm/loadench.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWMechanics\n{\n    class Enchanting\n    {\n            MWWorld::Ptr mOldItemPtr;\n            MWWorld::Ptr mSoulGemPtr;\n            MWWorld::Ptr mEnchanter;\n\n            int mCastStyle;\n\n            bool mSelfEnchanting;\n\n            ESM::EffectList mEffectList;\n\n            std::string mNewItemName;\n            std::string mObjectType;\n            int mWeaponType;\n\n            const ESM::Enchantment* getRecord(const ESM::Enchantment& newEnchantment) const;\n\n        public:\n            Enchanting();\n            void setEnchanter(const MWWorld::Ptr& enchanter);\n            void setSelfEnchanting(bool selfEnchanting);\n            void setOldItem(const MWWorld::Ptr& oldItem);\n            MWWorld::Ptr getOldItem() { return mOldItemPtr; }\n            MWWorld::Ptr getGem() { return mSoulGemPtr; }\n            void setNewItemName(const std::string& s);\n            void setEffect(const ESM::EffectList& effectList);\n            void setSoulGem(const MWWorld::Ptr& soulGem);\n            bool create(); //Return true if created, false if failed.\n            void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object)\n            int getCastStyle() const;\n            float getEnchantPoints(bool precise = true) const;\n            int getBaseCastCost() const; // To be saved in the enchantment's record\n            int getEffectiveCastCost() const; // Effective cost taking player Enchant skill into account, used for preview purposes in the UI\n            int getEnchantPrice() const;\n            int getMaxEnchantValue() const;\n            int getGemCharge() const;\n            int getEnchantChance() const;\n            int getEnchantItemsCount() const;\n            float getTypeMultiplier() const;\n            bool soulEmpty() const; //Return true if empty\n            bool itemEmpty() const; //Return true if empty\n            void payForEnchantment() const;\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/levelledlist.hpp",
    "content": "#ifndef OPENMW_MECHANICS_LEVELLEDLIST_H\n#define OPENMW_MECHANICS_LEVELLEDLIST_H\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/rng.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"actorutil.hpp\"\n\nnamespace MWMechanics\n{\n\n    /// @return ID of resulting item, or empty if none\n    inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Seed& seed = Misc::Rng::getSeed())\n    {\n        const std::vector<ESM::LevelledListBase::LevelItem>& items = levItem->mList;\n\n        const MWWorld::Ptr& player = getPlayer();\n        int playerLevel = player.getClass().getCreatureStats(player).getLevel();\n\n        if (Misc::Rng::roll0to99(seed) < levItem->mChanceNone)\n            return std::string();\n\n        std::vector<std::string> candidates;\n        int highestLevel = 0;\n        for (const auto& levelledItem : items)\n        {\n            if (levelledItem.mLevel > highestLevel && levelledItem.mLevel <= playerLevel)\n                highestLevel = levelledItem.mLevel;\n        }\n\n        // For levelled creatures, the flags are swapped. This file format just makes so much sense.\n        bool allLevels = (levItem->mFlags & ESM::ItemLevList::AllLevels) != 0;\n        if (creature)\n            allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels;\n\n        std::pair<int, std::string> highest = std::make_pair(-1, \"\");\n        for (const auto& levelledItem : items)\n        {\n            if (playerLevel >= levelledItem.mLevel\n                    && (allLevels || levelledItem.mLevel == highestLevel))\n            {\n                candidates.push_back(levelledItem.mId);\n                if (levelledItem.mLevel >= highest.first)\n                    highest = std::make_pair(levelledItem.mLevel, levelledItem.mId);\n            }\n        }\n        if (candidates.empty())\n            return std::string();\n        std::string item = candidates[Misc::Rng::rollDice(candidates.size(), seed)];\n\n        // Vanilla doesn't fail on nonexistent items in levelled lists\n        if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item)))\n        {\n            Log(Debug::Warning) << \"Warning: ignoring nonexistent item '\" << item << \"' in levelled list '\" << levItem->mId << \"'\";\n            return std::string();\n        }\n\n        // Is this another levelled item or a real item?\n        MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item, 1);\n        if (ref.getPtr().getTypeName() != typeid(ESM::ItemLevList).name()\n                && ref.getPtr().getTypeName() != typeid(ESM::CreatureLevList).name())\n        {\n            return item;\n        }\n        else\n        {\n            if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name())\n                return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false, seed);\n            else\n                return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, true, seed);\n        }\n    }\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/linkedeffects.cpp",
    "content": "#include \"linkedeffects.hpp\"\n\n#include <components/misc/rng.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"creaturestats.hpp\"\n\nnamespace MWMechanics\n{\n\n    bool reflectEffect(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect,\n                       const MWWorld::Ptr& caster, const MWWorld::Ptr& target, ESM::EffectList& reflectedEffects)\n    {\n        if (caster.isEmpty() || caster == target || !target.getClass().isActor())\n            return false;\n\n        bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful;\n        bool isUnreflectable = magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable;\n        if (!isHarmful || isUnreflectable)\n            return false;\n\n        float reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude();\n        if (Misc::Rng::roll0to99() >= reflect)\n            return false;\n\n        const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (\"VFX_Reflect\");\n        MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);\n        if (animation && !reflectStatic->mModel.empty())\n            animation->addEffect(\"meshes\\\\\" + reflectStatic->mModel, ESM::MagicEffect::Reflect, false, std::string());\n        reflectedEffects.mList.emplace_back(effect);\n        return true;\n    }\n\n    void absorbStat(const ESM::ENAMstruct& effect, const ESM::ActiveEffect& appliedEffect,\n                    const MWWorld::Ptr& caster, const MWWorld::Ptr& target, bool reflected, const std::string& source)\n    {\n        if (caster.isEmpty() || caster == target)\n            return;\n\n        if (!target.getClass().isActor() || !caster.getClass().isActor())\n            return;\n\n        // Make sure callers don't do something weird\n        if (effect.mEffectID < ESM::MagicEffect::AbsorbAttribute || effect.mEffectID > ESM::MagicEffect::AbsorbSkill)\n            throw std::runtime_error(\"invalid absorb stat effect\");\n\n        if (appliedEffect.mMagnitude == 0)\n            return;\n\n        std::vector<ActiveSpells::ActiveEffect> absorbEffects;\n        ActiveSpells::ActiveEffect absorbEffect = appliedEffect;\n        absorbEffect.mMagnitude *= -1;\n        absorbEffect.mEffectIndex = appliedEffect.mEffectIndex;\n        absorbEffects.emplace_back(absorbEffect);\n\n        // Morrowind negates reflected Absorb spells so the original caster won't be harmed.\n        if (reflected && Settings::Manager::getBool(\"classic reflected absorb spells behavior\", \"Game\"))\n        {\n            target.getClass().getCreatureStats(target).getActiveSpells().addSpell(std::string(), true,\n                            absorbEffects, source, caster.getClass().getCreatureStats(caster).getActorId());\n            return;\n        }\n\n        caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell(std::string(), true,\n                        absorbEffects, source, target.getClass().getCreatureStats(target).getActorId());\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/linkedeffects.hpp",
    "content": "#ifndef MWMECHANICS_LINKEDEFFECTS_H\n#define MWMECHANICS_LINKEDEFFECTS_H\n\n#include <string>\n\nnamespace ESM\n{\n    struct ActiveEffect;\n    struct EffectList;\n    struct ENAMstruct;\n    struct MagicEffect;\n    struct Spell;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n\n    // Try to reflect a spell effect. If it's reflected, it's also put into the passed reflected effects list.\n    bool reflectEffect(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect,\n                       const MWWorld::Ptr& caster, const MWWorld::Ptr& target, ESM::EffectList& reflectedEffects);\n\n    // Try to absorb a stat (skill, attribute, etc.) from the target and transfer it to the caster.\n    void absorbStat(const ESM::ENAMstruct& effect, const ESM::ActiveEffect& appliedEffect,\n                    const MWWorld::Ptr& caster, const MWWorld::Ptr& target, bool reflected, const std::string& source);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/magiceffects.cpp",
    "content": "#include \"magiceffects.hpp\"\n\n#include <stdexcept>\n\n#include <components/esm/effectlist.hpp>\n#include <components/esm/magiceffects.hpp>\n\nnamespace MWMechanics\n{\n    EffectKey::EffectKey() : mId (0), mArg (-1) {}\n\n    EffectKey::EffectKey (const ESM::ENAMstruct& effect)\n    {\n        mId = effect.mEffectID;\n        mArg = -1;\n\n        if (effect.mSkill!=-1)\n            mArg = effect.mSkill;\n\n        if (effect.mAttribute!=-1)\n        {\n            if (mArg!=-1)\n                throw std::runtime_error (\n                    \"magic effect can't have both a skill and an attribute argument\");\n\n            mArg = effect.mAttribute;\n        }\n    }\n\n    bool operator< (const EffectKey& left, const EffectKey& right)\n    {\n        if (left.mId<right.mId)\n            return true;\n\n        if (left.mId>right.mId)\n            return false;\n\n        return left.mArg<right.mArg;\n    }\n\n    float EffectParam::getMagnitude() const\n    {\n        return mBase + mModifier;\n    }\n\n    void EffectParam::modifyBase(int diff)\n    {\n        mBase += diff;\n    }\n\n    int EffectParam::getBase() const\n    {\n        return mBase;\n    }\n\n    void EffectParam::setBase(int base)\n    {\n        mBase = base;\n    }\n\n    void EffectParam::setModifier(float mod)\n    {\n        mModifier = mod;\n    }\n\n    float EffectParam::getModifier() const\n    {\n        return mModifier;\n    }\n\n    EffectParam::EffectParam() : mModifier (0), mBase(0) {}\n\n    EffectParam& EffectParam::operator+= (const EffectParam& param)\n    {\n        mModifier += param.mModifier;\n        mBase += param.mBase;\n        return *this;\n    }\n\n    EffectParam& EffectParam::operator-= (const EffectParam& param)\n    {\n        mModifier -= param.mModifier;\n        mBase -= param.mBase;\n        return *this;\n    }\n\n    void MagicEffects::remove(const EffectKey &key)\n    {\n        mCollection.erase(key);\n    }\n\n    void MagicEffects::add (const EffectKey& key, const EffectParam& param)\n    {\n        Collection::iterator iter = mCollection.find (key);\n\n        if (iter==mCollection.end())\n        {\n            mCollection.insert (std::make_pair (key, param));\n        }\n        else\n        {\n            iter->second += param;\n        }\n    }\n\n    void MagicEffects::modifyBase(const EffectKey &key, int diff)\n    {\n        mCollection[key].modifyBase(diff);\n    }\n\n    void MagicEffects::setModifiers(const MagicEffects &effects)\n    {\n        for (Collection::iterator it = mCollection.begin(); it != mCollection.end(); ++it)\n        {\n            it->second.setModifier(effects.get(it->first).getModifier());\n        }\n\n        for (Collection::const_iterator it = effects.begin(); it != effects.end(); ++it)\n        {\n            mCollection[it->first].setModifier(it->second.getModifier());\n        }\n    }\n\n    MagicEffects& MagicEffects::operator+= (const MagicEffects& effects)\n    {\n        if (this==&effects)\n        {\n            MagicEffects temp (effects);\n            *this += temp;\n            return *this;\n        }\n\n        for (Collection::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter)\n        {\n            Collection::iterator result = mCollection.find (iter->first);\n\n            if (result!=mCollection.end())\n                result->second += iter->second;\n            else\n                mCollection.insert (*iter);\n        }\n\n        return *this;\n    }\n\n    EffectParam MagicEffects::get (const EffectKey& key) const\n    {\n        Collection::const_iterator iter = mCollection.find (key);\n\n        if (iter==mCollection.end())\n        {\n            return EffectParam();\n        }\n        else\n        {\n            return iter->second;\n        }\n    }\n\n    MagicEffects MagicEffects::diff (const MagicEffects& prev, const MagicEffects& now)\n    {\n        MagicEffects result;\n\n        // adding/changing\n        for (Collection::const_iterator iter (now.begin()); iter!=now.end(); ++iter)\n        {\n            Collection::const_iterator other = prev.mCollection.find (iter->first);\n\n            if (other==prev.end())\n            {\n                // adding\n                result.add (iter->first, iter->second);\n            }\n            else\n            {\n                // changing\n                result.add (iter->first, iter->second - other->second);\n            }\n        }\n\n        // removing\n        for (Collection::const_iterator iter (prev.begin()); iter!=prev.end(); ++iter)\n        {\n            Collection::const_iterator other = now.mCollection.find (iter->first);\n            if (other==now.end())\n            {\n                result.add (iter->first, EffectParam() - iter->second);\n            }\n        }\n\n        return result;\n    }\n\n    void MagicEffects::writeState(ESM::MagicEffects &state) const\n    {\n        // Don't need to save Modifiers, they are recalculated every frame anyway.\n        for (Collection::const_iterator iter (begin()); iter!=end(); ++iter)\n        {\n            if (iter->second.getBase() != 0)\n            {\n                // Don't worry about mArg, never used by magic effect script instructions\n                state.mEffects.insert(std::make_pair(iter->first.mId, iter->second.getBase()));\n            }\n        }\n    }\n\n    void MagicEffects::readState(const ESM::MagicEffects &state)\n    {\n        for (std::map<int, int>::const_iterator it = state.mEffects.begin(); it != state.mEffects.end(); ++it)\n        {\n            mCollection[EffectKey(it->first)].setBase(it->second);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/magiceffects.hpp",
    "content": "#ifndef GAME_MWMECHANICS_MAGICEFFECTS_H\n#define GAME_MWMECHANICS_MAGICEFFECTS_H\n\n#include <map>\n#include <string>\n\nnamespace ESM\n{\n    struct ENAMstruct;\n    struct EffectList;\n\n    struct MagicEffects;\n}\n\nnamespace MWMechanics\n{\n    struct EffectKey\n    {\n        int mId;\n        int mArg; // skill or ability\n\n        EffectKey();\n\n        EffectKey (int id, int arg = -1) : mId (id), mArg (arg) {}\n\n        EffectKey (const ESM::ENAMstruct& effect);\n    };\n\n    bool operator< (const EffectKey& left, const EffectKey& right);\n\n    struct EffectParam\n    {\n    private:\n        // Note usually this would be int, but applying partial resistance might introduce a decimal point.\n        float mModifier;\n\n        int mBase;\n\n    public:\n        /// Get the total magnitude including base and modifier.\n        float getMagnitude() const;\n\n        void setModifier(float mod);\n        float getModifier() const;\n\n        /// Change mBase by \\a diff\n        void modifyBase(int diff);\n        void setBase(int base);\n        int getBase() const;\n\n        EffectParam();\n\n        EffectParam(float magnitude) : mModifier(magnitude), mBase(0) {}\n\n        EffectParam& operator+= (const EffectParam& param);\n\n        EffectParam& operator-= (const EffectParam& param);\n    };\n\n    inline EffectParam operator+ (const EffectParam& left, const EffectParam& right)\n    {\n        EffectParam param (left);\n        return param += right;\n    }\n\n    inline EffectParam operator- (const EffectParam& left, const EffectParam& right)\n    {\n        EffectParam param (left);\n        return param -= right;\n    }\n\n    // Used by effect management classes (ActiveSpells, InventoryStore, Spells) to list active effect sources for GUI display\n    struct EffectSourceVisitor\n    {\n        virtual ~EffectSourceVisitor() { }\n\n        virtual void visit (EffectKey key, int effectIndex,\n                            const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                            float magnitude, float remainingTime = -1, float totalTime = -1) = 0;\n    };\n\n    /// \\brief Effects currently affecting a NPC or creature\n    class MagicEffects\n    {\n        public:\n\n            typedef std::map<EffectKey, EffectParam> Collection;\n\n        private:\n\n            Collection mCollection;\n\n        public:\n\n            Collection::const_iterator begin() const { return mCollection.begin(); }\n\n            Collection::const_iterator end() const { return mCollection.end(); }\n\n            void readState (const ESM::MagicEffects& state);\n            void writeState (ESM::MagicEffects& state) const;\n\n            void add (const EffectKey& key, const EffectParam& param);\n            void remove (const EffectKey& key);\n\n            void modifyBase (const EffectKey& key, int diff);\n\n            /// Copy Modifier values from \\a effects, but keep original mBase values.\n            void setModifiers(const MagicEffects& effects);\n\n            MagicEffects& operator+= (const MagicEffects& effects);\n\n            EffectParam get (const EffectKey& key) const;\n            ///< This function can safely be used for keys that are not present.\n\n            static MagicEffects diff (const MagicEffects& prev, const MagicEffects& now);\n            ///< Return changes from \\a prev to \\a now.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/mechanicsmanagerimp.cpp",
    "content": "#include \"mechanicsmanagerimp.hpp\"\n\n#include <osg/Stats>\n\n#include <components/misc/rng.hpp>\n\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/stolenitems.hpp>\n\n#include <components/detournavigator/navigator.hpp>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/ptr.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n\n#include \"aicombat.hpp\"\n#include \"aipursue.hpp\"\n#include \"spellutil.hpp\"\n#include \"autocalcspell.hpp\"\n#include \"npcstats.hpp\"\n#include \"actorutil.hpp\"\n#include \"combat.hpp\"\n\nnamespace\n{\n\n    float getFightDispositionBias(float disposition)\n    {\n        static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                    \"fFightDispMult\")->mValue.getFloat();\n        return ((50.f - disposition)  * fFightDispMult);\n    }\n\n    void getPersuasionRatings(const MWMechanics::NpcStats& stats, float& rating1, float& rating2, float& rating3, bool player)\n    {\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find(\"fPersonalityMod\")->mValue.getFloat();\n        float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find(\"fLuckMod\")->mValue.getFloat();\n        float repTerm = stats.getReputation() * gmst.find(\"fReputationMod\")->mValue.getFloat();\n        float fatigueTerm = stats.getFatigueTerm();\n        float levelTerm = stats.getLevel() * gmst.find(\"fLevelMod\")->mValue.getFloat();\n\n        rating1 = (repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm;\n\n        if (player)\n        {\n            rating2 = rating1 + levelTerm;\n            rating3 = (stats.getSkill(ESM::Skill::Mercantile).getModified() + luckTerm + persTerm) * fatigueTerm;\n        }\n        else\n        {\n            rating2 = (levelTerm + repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm;\n            rating3 = (stats.getSkill(ESM::Skill::Mercantile).getModified() + repTerm + luckTerm + persTerm) * fatigueTerm;\n        }\n    }\n\n}\n\nnamespace MWMechanics\n{\n    void MechanicsManager::buildPlayer()\n    {\n        MWWorld::Ptr ptr = getPlayer();\n\n        MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);\n        MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats (ptr);\n\n        npcStats.setNeedRecalcDynamicStats(true);\n\n        const ESM::NPC *player = ptr.get<ESM::NPC>()->mBase;\n\n        // reset\n        creatureStats.setLevel(player->mNpdt.mLevel);\n        creatureStats.getSpells().clear(true);\n        creatureStats.modifyMagicEffects(MagicEffects());\n\n        for (int i=0; i<27; ++i)\n            npcStats.getSkill (i).setBase (player->mNpdt.mSkills[i]);\n\n        creatureStats.setAttribute(ESM::Attribute::Strength, player->mNpdt.mStrength);\n        creatureStats.setAttribute(ESM::Attribute::Intelligence, player->mNpdt.mIntelligence);\n        creatureStats.setAttribute(ESM::Attribute::Willpower, player->mNpdt.mWillpower);\n        creatureStats.setAttribute(ESM::Attribute::Agility, player->mNpdt.mAgility);\n        creatureStats.setAttribute(ESM::Attribute::Speed, player->mNpdt.mSpeed);\n        creatureStats.setAttribute(ESM::Attribute::Endurance, player->mNpdt.mEndurance);\n        creatureStats.setAttribute(ESM::Attribute::Personality, player->mNpdt.mPersonality);\n        creatureStats.setAttribute(ESM::Attribute::Luck, player->mNpdt.mLuck);\n        const MWWorld::ESMStore &esmStore =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        // race\n        if (mRaceSelected)\n        {\n            const ESM::Race *race =\n                esmStore.get<ESM::Race>().find(player->mRace);\n\n            bool male = (player->mFlags & ESM::NPC::Female) == 0;\n\n            for (int i=0; i<8; ++i)\n            {\n                const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i];\n\n                creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale);\n            }\n\n            for (int i=0; i<27; ++i)\n            {\n                int bonus = 0;\n\n                for (int i2=0; i2<7; ++i2)\n                    if (race->mData.mBonus[i2].mSkill==i)\n                    {\n                        bonus = race->mData.mBonus[i2].mBonus;\n                        break;\n                    }\n\n                npcStats.getSkill (i).setBase (5 + bonus);\n            }\n\n            for (const std::string &power : race->mPowers.mList)\n            {\n                creatureStats.getSpells().add(power);\n            }\n        }\n\n        // birthsign\n        const std::string &signId =\n            MWBase::Environment::get().getWorld()->getPlayer().getBirthSign();\n\n        if (!signId.empty())\n        {\n            const ESM::BirthSign *sign =\n                esmStore.get<ESM::BirthSign>().find(signId);\n\n            for (const std::string &power : sign->mPowers.mList)\n            {\n                creatureStats.getSpells().add(power);\n            }\n        }\n\n        // class\n        if (mClassSelected)\n        {\n            const ESM::Class *class_ =\n                esmStore.get<ESM::Class>().find(player->mClass);\n\n            for (int i=0; i<2; ++i)\n            {\n                int attribute = class_->mData.mAttribute[i];\n                if (attribute>=0 && attribute<8)\n                {\n                    creatureStats.setAttribute(attribute,\n                        creatureStats.getAttribute(attribute).getBase() + 10);\n                }\n            }\n\n            for (int i=0; i<2; ++i)\n            {\n                int bonus = i==0 ? 10 : 25;\n\n                for (int i2=0; i2<5; ++i2)\n                {\n                    int index = class_->mData.mSkills[i2][i];\n\n                    if (index>=0 && index<27)\n                    {\n                        npcStats.getSkill (index).setBase (\n                            npcStats.getSkill (index).getBase() + bonus);\n                    }\n                }\n            }\n\n            const MWWorld::Store<ESM::Skill> &skills =\n                esmStore.get<ESM::Skill>();\n\n            MWWorld::Store<ESM::Skill>::iterator iter = skills.begin();\n            for (; iter != skills.end(); ++iter)\n            {\n                if (iter->second.mData.mSpecialization==class_->mData.mSpecialization)\n                {\n                    int index = iter->first;\n\n                    if (index>=0 && index<27)\n                    {\n                        npcStats.getSkill (index).setBase (\n                            npcStats.getSkill (index).getBase() + 5);\n                    }\n                }\n            }\n        }\n\n        // F_PCStart spells\n        const ESM::Race* race = nullptr;\n        if (mRaceSelected)\n            race = esmStore.get<ESM::Race>().find(player->mRace);\n\n        int skills[ESM::Skill::Length];\n        for (int i=0; i<ESM::Skill::Length; ++i)\n            skills[i] = npcStats.getSkill(i).getBase();\n\n        int attributes[ESM::Attribute::Length];\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            attributes[i] = npcStats.getAttribute(i).getBase();\n\n        std::vector<std::string> selectedSpells = autoCalcPlayerSpells(skills, attributes, race);\n\n        for (const std::string &spell : selectedSpells)\n            creatureStats.getSpells().add(spell);\n\n        // forced update and current value adjustments\n        mActors.updateActor (ptr, 0);\n\n        for (int i=0; i<3; ++i)\n        {\n            DynamicStat<float> stat = creatureStats.getDynamic (i);\n            stat.setCurrent (stat.getModified());\n            creatureStats.setDynamic (i, stat);\n        }\n\n        // auto-equip again. we need this for when the race is changed to a beast race and shoes are no longer equippable\n        MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr);\n        for (int i=0; i<MWWorld::InventoryStore::Slots; ++i)\n            invStore.unequipAll(ptr);\n        invStore.autoEquip(ptr);\n    }\n\n    MechanicsManager::MechanicsManager()\n    : mUpdatePlayer (true), mClassSelected (false),\n      mRaceSelected (false), mAI(true)\n    {\n        //buildPlayer no longer here, needs to be done explicitly after all subsystems are up and running\n    }\n\n    void MechanicsManager::add(const MWWorld::Ptr& ptr)\n    {\n        if(ptr.getClass().isActor())\n            mActors.addActor(ptr);\n        else\n            mObjects.addObject(ptr);\n    }\n\n    void MechanicsManager::castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell)\n    {\n        if(ptr.getClass().isActor())\n            mActors.castSpell(ptr, spellId, manualSpell);\n    }\n\n    void MechanicsManager::remove(const MWWorld::Ptr& ptr)\n    {\n        if(ptr == MWBase::Environment::get().getWindowManager()->getWatchedActor())\n            MWBase::Environment::get().getWindowManager()->watchActor(MWWorld::Ptr());\n        mActors.removeActor(ptr);\n        mObjects.removeObject(ptr);\n    }\n\n    void MechanicsManager::updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)\n    {\n        if(old == MWBase::Environment::get().getWindowManager()->getWatchedActor())\n            MWBase::Environment::get().getWindowManager()->watchActor(ptr);\n\n        if(ptr.getClass().isActor())\n            mActors.updateActor(old, ptr);\n        else\n            mObjects.updateObject(old, ptr);\n    }\n\n    void MechanicsManager::drop(const MWWorld::CellStore *cellStore)\n    {\n        mActors.dropActors(cellStore, getPlayer());\n        mObjects.dropObjects(cellStore);\n    }\n\n    void MechanicsManager::restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId)\n    {\n        auto& stats = actor.getClass().getCreatureStats (actor);\n        auto& corprusSpells = stats.getCorprusSpells();\n\n        auto corprusIt = corprusSpells.find(sourceId);\n\n        if (corprusIt != corprusSpells.end())\n        {\n            for (int i = 0; i < ESM::Attribute::Length; ++i)\n            {\n                MWMechanics::AttributeValue attr = stats.getAttribute(i);\n                attr.restore(corprusIt->second.mWorsenings[i]);\n                actor.getClass().getCreatureStats(actor).setAttribute(i, attr);\n            }\n        }\n    }\n\n    void MechanicsManager::update(float duration, bool paused)\n    {\n        // Note: we should do it here since game mechanics and world updates use these values\n        MWWorld::Ptr ptr = getPlayer();\n        MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n\n        // Update the equipped weapon icon\n        MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);\n        MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n        if (weapon == inv.end())\n            winMgr->unsetSelectedWeapon();\n        else\n            winMgr->setSelectedWeapon(*weapon);\n\n        // Update the selected spell icon\n        MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();\n        if (enchantItem != inv.end())\n            winMgr->setSelectedEnchantItem(*enchantItem);\n        else\n        {\n            const std::string& spell = winMgr->getSelectedSpell();\n            if (!spell.empty())\n                winMgr->setSelectedSpell(spell, int(MWMechanics::getSpellSuccessChance(spell, ptr)));\n            else\n                winMgr->unsetSelectedSpell();\n        }\n\n        if (mUpdatePlayer)\n        {\n            mUpdatePlayer = false;\n\n            // HACK? The player has been changed, so a new Animation object may\n            // have been made for them. Make sure they're properly updated.\n            mActors.removeActor(ptr);\n            mActors.addActor(ptr, true);\n        }\n\n        mActors.update(duration, paused);\n        mObjects.update(duration, paused);\n    }\n\n    void MechanicsManager::processChangedSettings(const Settings::CategorySettingVector &changed)\n    {\n        for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it)\n        {\n            if (it->first == \"Game\" && it->second == \"actors processing range\")\n            {\n                int state = MWBase::Environment::get().getStateManager()->getState();\n                if (state != MWBase::StateManager::State_Running)\n                    continue;\n\n                mActors.updateProcessingRange();\n\n                // Update mechanics for new processing range immediately\n                update(0.f, false);\n            }\n        }\n    }\n\n    void MechanicsManager::notifyDied(const MWWorld::Ptr& actor)\n    {\n        mActors.notifyDied(actor);\n    }\n\n    float MechanicsManager::getActorsProcessingRange() const\n    {\n        return mActors.getProcessingRange();\n    }\n\n    bool MechanicsManager::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)\n    {\n        return mActors.isActorDetected(actor, observer);\n    }\n\n    bool MechanicsManager::isAttackPreparing(const MWWorld::Ptr& ptr)\n    {\n        return mActors.isAttackPreparing(ptr);\n    }\n\n    bool MechanicsManager::isRunning(const MWWorld::Ptr& ptr)\n    {\n        return mActors.isRunning(ptr);\n    }\n\n    bool MechanicsManager::isSneaking(const MWWorld::Ptr& ptr)\n    {\n        CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        bool animActive = mActors.isSneaking(ptr);\n        bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);\n        bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr);\n        return stanceOn && (animActive || inair);\n    }\n\n    void MechanicsManager::rest(double hours, bool sleep)\n    {\n        if (sleep)\n            MWBase::Environment::get().getWorld()->rest(hours);\n\n        mActors.rest(hours, sleep);\n    }\n\n    void MechanicsManager::restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep)\n    {\n        mActors.restoreDynamicStats(actor, hours, sleep);\n    }\n\n    int MechanicsManager::getHoursToRest() const\n    {\n        return mActors.getHoursToRest(getPlayer());\n    }\n\n    void MechanicsManager::setPlayerName (const std::string& name)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n\n        ESM::NPC player =\n            *world->getPlayerPtr().get<ESM::NPC>()->mBase;\n        player.mName = name;\n\n        world->createRecord(player);\n\n        mUpdatePlayer = true;\n    }\n\n    void MechanicsManager::setPlayerRace (const std::string& race, bool male, const std::string &head, const std::string &hair)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n\n        ESM::NPC player =\n            *world->getPlayerPtr().get<ESM::NPC>()->mBase;\n\n        player.mRace = race;\n        player.mHead = head;\n        player.mHair = hair;\n        player.setIsMale(male);\n\n        world->createRecord(player);\n\n        mRaceSelected = true;\n        buildPlayer();\n        mUpdatePlayer = true;\n    }\n\n    void MechanicsManager::setPlayerBirthsign (const std::string& id)\n    {\n        MWBase::Environment::get().getWorld()->getPlayer().setBirthSign(id);\n        buildPlayer();\n        mUpdatePlayer = true;\n    }\n\n    void MechanicsManager::setPlayerClass (const std::string& id)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n\n        ESM::NPC player =\n            *world->getPlayerPtr().get<ESM::NPC>()->mBase;\n        player.mClass = id;\n\n        world->createRecord(player);\n\n        mClassSelected = true;\n        buildPlayer();\n        mUpdatePlayer = true;\n    }\n\n    void MechanicsManager::setPlayerClass (const ESM::Class &cls)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n\n        const ESM::Class *ptr = world->createRecord(cls);\n\n        ESM::NPC player =\n            *world->getPlayerPtr().get<ESM::NPC>()->mBase;\n        player.mClass = ptr->mId;\n\n        world->createRecord(player);\n\n        mClassSelected = true;\n        buildPlayer();\n        mUpdatePlayer = true;\n    }\n\n    int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr, bool addTemporaryDispositionChange)\n    {\n        const MWMechanics::NpcStats& npcSkill = ptr.getClass().getNpcStats(ptr);\n        float x = static_cast<float>(npcSkill.getBaseDisposition());\n\n        MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>();\n        MWWorld::Ptr playerPtr = getPlayer();\n        MWWorld::LiveCellRef<ESM::NPC>* player = playerPtr.get<ESM::NPC>();\n        const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr);\n\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        static const float fDispRaceMod = gmst.find(\"fDispRaceMod\")->mValue.getFloat();\n        if (Misc::StringUtils::ciEqual(npc->mBase->mRace, player->mBase->mRace))\n            x += fDispRaceMod;\n\n        static const float fDispPersonalityMult = gmst.find(\"fDispPersonalityMult\")->mValue.getFloat();\n        static const float fDispPersonalityBase = gmst.find(\"fDispPersonalityBase\")->mValue.getFloat();\n        x += fDispPersonalityMult * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - fDispPersonalityBase);\n\n        float reaction = 0;\n        int rank = 0;\n        std::string npcFaction = ptr.getClass().getPrimaryFaction(ptr);\n\n        Misc::StringUtils::lowerCaseInPlace(npcFaction);\n\n        if (playerStats.getFactionRanks().find(npcFaction) != playerStats.getFactionRanks().end())\n        {\n            if (!playerStats.getExpelled(npcFaction))\n            {\n                // faction reaction towards itself. yes, that exists\n                reaction = static_cast<float>(MWBase::Environment::get().getDialogueManager()->getFactionReaction(npcFaction, npcFaction));\n\n                rank = playerStats.getFactionRanks().find(npcFaction)->second;\n            }\n        }\n        else if (!npcFaction.empty())\n        {\n            std::map<std::string, int>::const_iterator playerFactionIt = playerStats.getFactionRanks().begin();\n            for (; playerFactionIt != playerStats.getFactionRanks().end(); ++playerFactionIt)\n            {\n                std::string itFaction = playerFactionIt->first;\n\n                // Ignore the faction, if a player was expelled from it.\n                if (playerStats.getExpelled(itFaction))\n                    continue;\n\n                int itReaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(npcFaction, itFaction);\n                if (playerFactionIt == playerStats.getFactionRanks().begin() || itReaction < reaction)\n                {\n                    reaction = static_cast<float>(itReaction);\n                    rank = playerFactionIt->second;\n                }\n            }\n        }\n        else\n        {\n            reaction = 0;\n            rank = 0;\n        }\n\n        static const float fDispFactionRankMult = gmst.find(\"fDispFactionRankMult\")->mValue.getFloat();\n        static const float fDispFactionRankBase = gmst.find(\"fDispFactionRankBase\")->mValue.getFloat();\n        static const float fDispFactionMod = gmst.find(\"fDispFactionMod\")->mValue.getFloat();\n        x += (fDispFactionRankMult * rank\n            + fDispFactionRankBase)\n            * fDispFactionMod * reaction;\n\n        static const float fDispCrimeMod = gmst.find(\"fDispCrimeMod\")->mValue.getFloat();\n        static const float fDispDiseaseMod = gmst.find(\"fDispDiseaseMod\")->mValue.getFloat();\n        x -= fDispCrimeMod * playerStats.getBounty();\n        if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease())\n            x += fDispDiseaseMod;\n\n        static const float fDispWeaponDrawn = gmst.find(\"fDispWeaponDrawn\")->mValue.getFloat();\n        if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon)\n            x += fDispWeaponDrawn;\n\n        x += ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Charm).getMagnitude();\n\n        if(addTemporaryDispositionChange)\n          x += MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange();\n\n        int effective_disposition = std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used\n        return effective_disposition;\n    }\n\n    int MechanicsManager::getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying)\n    {\n        // Make sure zero base price items/services can't be bought/sold for 1 gold\n        // and return the intended base price for creature merchants\n        if (basePrice == 0 || ptr.getTypeName() == typeid(ESM::Creature).name())\n            return basePrice;\n\n        const MWMechanics::NpcStats &sellerStats = ptr.getClass().getNpcStats(ptr);\n\n        MWWorld::Ptr playerPtr = getPlayer();\n        const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr);\n\n        // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here,\n        // otherwise one would get different prices when exiting and re-entering the dialogue window...\n        int clampedDisposition = getDerivedDisposition(ptr);\n        float a = std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100.f);\n        float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);\n        float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);\n        float d = std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100.f);\n        float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);\n        float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);\n        float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm();\n        float npcTerm = (d + e + f) * sellerStats.getFatigueTerm();\n        float buyTerm = 0.01f * (100 - 0.5f * (pcTerm - npcTerm));\n        float sellTerm = 0.01f * (50 - 0.5f * (npcTerm - pcTerm));\n        int offerPrice = int(basePrice * (buying ? buyTerm : sellTerm));\n        return std::max(1, offerPrice);\n    }\n\n    int MechanicsManager::countDeaths (const std::string& id) const\n    {\n        return mActors.countDeaths (id);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set the number of deaths for an actor with the given refId\n    */\n    void MechanicsManager::setDeaths(const std::string& refId, int number)\n    {\n        mActors.setDeaths(refId, number);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, bool& success, float& tempChange, float& permChange)\n    {\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        MWMechanics::NpcStats& npcStats = npc.getClass().getNpcStats(npc);\n\n        MWWorld::Ptr playerPtr = getPlayer();\n        const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr);\n\n        float npcRating1, npcRating2, npcRating3;\n        getPersuasionRatings(npcStats, npcRating1, npcRating2, npcRating3, false);\n\n        float playerRating1, playerRating2, playerRating3;\n        getPersuasionRatings(playerStats, playerRating1, playerRating2, playerRating3, true);\n\n        int currentDisposition = getDerivedDisposition(npc);\n\n        float d = 1 - 0.02f * abs(currentDisposition - 50);\n        float target1 = d * (playerRating1 - npcRating1 + 50);\n        float target2 = d * (playerRating2 - npcRating2 + 50);\n\n        float bribeMod;\n        if (type == PT_Bribe10) bribeMod = gmst.find(\"fBribe10Mod\")->mValue.getFloat();\n        else if (type == PT_Bribe100) bribeMod = gmst.find(\"fBribe100Mod\")->mValue.getFloat();\n        else bribeMod = gmst.find(\"fBribe1000Mod\")->mValue.getFloat();\n\n        float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod;\n\n        float iPerMinChance = floor(gmst.find(\"iPerMinChance\")->mValue.getFloat());\n        float iPerMinChange = floor(gmst.find(\"iPerMinChange\")->mValue.getFloat());\n        float fPerDieRollMult = gmst.find(\"fPerDieRollMult\")->mValue.getFloat();\n        float fPerTempMult = gmst.find(\"fPerTempMult\")->mValue.getFloat();\n\n        float x = 0;\n        float y = 0;\n\n        int roll = Misc::Rng::roll0to99();\n\n        if (type == PT_Admire)\n        {\n            target1 = std::max(iPerMinChance, target1);\n            success = (roll <= target1);\n            float c = floor(fPerDieRollMult * (target1 - roll));\n            x = success ? std::max(iPerMinChange, c) : c;\n        }\n        else if (type == PT_Intimidate)\n        {\n            target2 = std::max(iPerMinChance, target2);\n\n            success =  (roll <= target2);\n\n            float r;\n            if (roll != target2)\n                r = floor(target2 - roll);\n            else\n                r = 1;\n\n            if (roll <= target2)\n            {\n                float s = floor(r * fPerDieRollMult * fPerTempMult);\n\n                int flee = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Flee).getBase();\n                int fight = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Fight).getBase();\n                npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee,\n                                       std::max(0, std::min(100, flee + int(std::max(iPerMinChange, s)))));\n                npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight,\n                                       std::max(0, std::min(100, fight + int(std::min(-iPerMinChange, -s)))));\n            }\n\n            float c = -std::abs(floor(r * fPerDieRollMult));\n            if (success)\n            {\n                if (std::abs(c) < iPerMinChange)\n                {\n                    // Deviating from Morrowind here: it doesn't increase disposition on marginal wins,\n                    // which seems to be a bug (MCP fixes it too).\n                    // Original logic: x = 0, y = -iPerMinChange\n                    x = iPerMinChange;\n                    y = x; // This goes unused.\n                }\n                else\n                {\n                    x = -floor(c * fPerTempMult);\n                    y = c;\n                }\n            }\n            else\n            {\n                x = floor(c * fPerTempMult);\n                y = c;\n            }\n        }\n        else if (type == PT_Taunt)\n        {\n            target1 = std::max(iPerMinChance, target1);\n            success = (roll <= target1);\n\n            float c = std::abs(floor(target1 - roll));\n\n            if (success)\n            {\n                float s = c * fPerDieRollMult * fPerTempMult;\n                int flee = npcStats.getAiSetting (CreatureStats::AI_Flee).getBase();\n                int fight = npcStats.getAiSetting (CreatureStats::AI_Fight).getBase();\n                npcStats.setAiSetting (CreatureStats::AI_Flee,\n                                       std::max(0, std::min(100, flee + std::min(-int(iPerMinChange), int(-s)))));\n                npcStats.setAiSetting (CreatureStats::AI_Fight,\n                                       std::max(0, std::min(100, fight + std::max(int(iPerMinChange), int(s)))));\n            }\n            x = floor(-c * fPerDieRollMult);\n\n            if (success && std::abs(x) < iPerMinChange)\n                x = -iPerMinChange;\n        }\n        else // Bribe\n        {\n            target3 = std::max(iPerMinChance, target3);\n            success = (roll <= target3);\n            float c = floor((target3 - roll) * fPerDieRollMult);\n\n            x = success ? std::max(iPerMinChange, c) : c;\n        }\n\n        tempChange = type == PT_Intimidate ? x : int(x * fPerTempMult);\n\n\n        float cappedDispositionChange = tempChange;\n        if (currentDisposition + tempChange > 100.f)\n            cappedDispositionChange = static_cast<float>(100 - currentDisposition);\n        if (currentDisposition + tempChange < 0.f)\n            cappedDispositionChange = static_cast<float>(-currentDisposition);\n\n        permChange = floor(cappedDispositionChange / fPerTempMult);\n        if (type == PT_Intimidate)\n        {\n            permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y;\n        }\n    }\n\n    void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr)\n    {\n        if(ptr.getClass().isActor())\n            mActors.forceStateUpdate(ptr);\n    }\n\n    bool MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)\n    {\n        if(ptr.getClass().isActor())\n            return mActors.playAnimationGroup(ptr, groupName, mode, number, persist);\n        else\n            return mObjects.playAnimationGroup(ptr, groupName, mode, number, persist);\n    }\n    void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)\n    {\n        if(ptr.getClass().isActor())\n            mActors.skipAnimation(ptr);\n        else\n            mObjects.skipAnimation(ptr);\n    }\n    bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName)\n    {\n        if(ptr.getClass().isActor())\n            return mActors.checkAnimationPlaying(ptr, groupName);\n        else\n            return false;\n    }\n\n    bool MechanicsManager::onOpen(const MWWorld::Ptr& ptr)\n    {\n        if(ptr.getClass().isActor())\n            return true;\n        else\n            return mObjects.onOpen(ptr);\n    }\n\n    void MechanicsManager::onClose(const MWWorld::Ptr& ptr)\n    {\n        if(!ptr.getClass().isActor())\n            mObjects.onClose(ptr);\n    }\n\n    void MechanicsManager::persistAnimationStates()\n    {\n        mActors.persistAnimationStates();\n        mObjects.persistAnimationStates();\n    }\n\n    void MechanicsManager::updateMagicEffects(const MWWorld::Ptr &ptr)\n    {\n        mActors.updateMagicEffects(ptr);\n    }\n\n    bool MechanicsManager::toggleAI()\n    {\n        mAI = !mAI;\n\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        world->getNavigator()->setUpdatesEnabled(mAI);\n        if (mAI)\n           world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3());\n\n        return mAI;\n    }\n\n    bool MechanicsManager::isAIActive()\n    {\n        return mAI;\n    }\n\n    void MechanicsManager::playerLoaded()\n    {\n        mUpdatePlayer = true;\n        mClassSelected = true;\n        mRaceSelected = true;\n\n        /*\n            Start of tes3mp change (major)\n\n            Avoid enabling AI in multiplayer\n        */\n        mAI = false;\n        /*\n            End of tes3mp change (major)\n        */\n    }\n\n    /*\n        Start of tes3mp change (major)\n\n        Move boundItemIDCache outside of the original isBoundItem(const MWWorld::Ptr& item)\n        method so it can be reused in the new isBoundItem(std::string itemId) method\n    */\n    std::set<std::string> boundItemIDCache;\n\n    bool MechanicsManager::isBoundItem(const MWWorld::Ptr& item)\n    {\n    /*\n        End of tes3mp change (major)\n    */\n        // If this is empty then we haven't executed the GMST cache logic yet; or there isn't any sMagicBound* GMST's for some reason\n        if (boundItemIDCache.empty())\n        {\n            // Build a list of known bound item ID's\n            const MWWorld::Store<ESM::GameSetting> &gameSettings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n            for (const ESM::GameSetting &currentSetting : gameSettings)\n            {\n                std::string currentGMSTID = currentSetting.mId;\n                Misc::StringUtils::lowerCaseInPlace(currentGMSTID);\n\n                // Don't bother checking this GMST if it's not a sMagicBound* one.\n                const std::string& toFind = \"smagicbound\";\n                if (currentGMSTID.compare(0, toFind.length(), toFind) != 0)\n                    continue;\n\n                // All sMagicBound* GMST's should be of type string\n                std::string currentGMSTValue = currentSetting.mValue.getString();\n                Misc::StringUtils::lowerCaseInPlace(currentGMSTValue);\n\n                boundItemIDCache.insert(currentGMSTValue);\n            }\n        }\n\n        // Perform bound item check and assign the Flag_Bound bit if it passes\n        std::string tempItemID = item.getCellRef().getRefId();\n        Misc::StringUtils::lowerCaseInPlace(tempItemID);\n\n        if (boundItemIDCache.count(tempItemID) != 0)\n            return true;\n\n        return false;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check if an itemId corresponds to a bound item\n    */\n    bool MechanicsManager::isBoundItem(std::string itemId)\n    {\n        Misc::StringUtils::lowerCaseInPlace(itemId);\n\n        if (boundItemIDCache.count(itemId) != 0)\n            return true;\n\n        return false;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim)\n    {\n        if (target.isEmpty())\n            return true;\n\n        const MWWorld::CellRef& cellref = target.getCellRef();\n        // there is no harm to use unlocked doors\n        int lockLevel = cellref.getLockLevel();\n        if (target.getClass().isDoor() &&\n            (lockLevel <= 0 || lockLevel == ESM::UnbreakableLock) &&\n            ptr.getCellRef().getTrap().empty())\n        {\n            return true;\n        }\n\n        if (!target.getClass().hasToolTip(target))\n            return true;\n\n        // TODO: implement a better check to check if target is owned bed\n        if (target.getClass().isActivator() && target.getClass().getScript(target).compare(0, 3, \"Bed\") != 0)\n            return true;\n\n        if (target.getClass().isNpc())\n        {\n            if (target.getClass().getCreatureStats(target).isDead())\n                return true;\n\n            if (target.getClass().getCreatureStats(target).getAiSequence().isInCombat())\n                return true;\n\n            // check if a player tries to pickpocket a target NPC\n            if (target.getClass().getCreatureStats(target).getKnockedDown() || isSneaking(ptr))\n                return false;\n\n            return true;\n        }\n\n        const std::string& owner = cellref.getOwner();\n        bool isOwned = !owner.empty() && owner != \"player\";\n\n        const std::string& faction = cellref.getFaction();\n        bool isFactionOwned = false;\n        if (!faction.empty() && ptr.getClass().isNpc())\n        {\n            const std::map<std::string, int>& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks();\n            std::map<std::string, int>::const_iterator found = factions.find(Misc::StringUtils::lowerCase(faction));\n            if (found == factions.end()\n                    || found->second < cellref.getFactionRank())\n                isFactionOwned = true;\n        }\n\n        const std::string& globalVariable = cellref.getGlobalVariable();\n        if (!globalVariable.empty() && MWBase::Environment::get().getWorld()->getGlobalInt(Misc::StringUtils::lowerCase(globalVariable)) == 1)\n        {\n            isOwned = false;\n            isFactionOwned = false;\n        }\n\n        if (!cellref.getOwner().empty())\n            victim = MWBase::Environment::get().getWorld()->searchPtr(cellref.getOwner(), true, false);\n\n        // A special case for evidence chest - we should not allow to take items even if it is technically permitted\n        if (Misc::StringUtils::ciEqual(cellref.getRefId(), \"stolen_goods\"))\n            return false;\n\n        return (!isOwned && !isFactionOwned);\n    }\n\n    bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed)\n    {\n        if (ptr.getClass().getNpcStats(ptr).isWerewolf())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sWerewolfRefusal}\");\n            return true;\n        }\n\n        if(MWBase::Environment::get().getWorld()->getPlayer().enemiesNearby()) {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sNotifyMessage2}\");\n            return true;\n        }\n\n        MWWorld::Ptr victim;\n        if (isAllowedToUse(ptr, bed, victim))\n            return false;\n\n        if(commitCrime(ptr, victim, OT_SleepingInOwnedBed, bed.getCellRef().getFaction()))\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sNotifyMessage64}\");\n            return true;\n        }\n        else\n            return false;\n    }\n\n    void MechanicsManager::unlockAttempted(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item)\n    {\n        MWWorld::Ptr victim;\n        if (isAllowedToUse(ptr, item, victim))\n            return;\n        commitCrime(ptr, victim, OT_Trespassing, item.getCellRef().getFaction());\n    }\n\n    std::vector<std::pair<std::string, int> > MechanicsManager::getStolenItemOwners(const std::string& itemid)\n    {\n        std::vector<std::pair<std::string, int> > result;\n        StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid));\n        if (it == mStolenItems.end())\n            return result;\n        else\n        {\n            const OwnerMap& owners = it->second;\n            for (OwnerMap::const_iterator ownerIt = owners.begin(); ownerIt != owners.end(); ++ownerIt)\n                result.emplace_back(ownerIt->first.first, ownerIt->second);\n            return result;\n        }\n    }\n\n    bool MechanicsManager::isItemStolenFrom(const std::string &itemid, const MWWorld::Ptr& ptr)\n    {\n        StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid));\n        if (it == mStolenItems.end())\n            return false;\n\n        const OwnerMap& owners = it->second;\n        const std::string ownerid = ptr.getCellRef().getRefId();\n        OwnerMap::const_iterator ownerFound = owners.find(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false));\n        if (ownerFound != owners.end())\n            return true;\n\n        const std::string factionid = ptr.getClass().getPrimaryFaction(ptr);\n        if (!factionid.empty())\n        {\n            OwnerMap::const_iterator factionOwnerFound = owners.find(std::make_pair(Misc::StringUtils::lowerCase(factionid), true));\n            return factionOwnerFound != owners.end();\n        }\n\n        return false;\n    }\n\n    void MechanicsManager::confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count)\n    {\n        if (player != getPlayer())\n            return;\n\n        const std::string itemId = Misc::StringUtils::lowerCase(item.getCellRef().getRefId());\n\n        StolenItemsMap::iterator stolenIt = mStolenItems.find(itemId);\n        if (stolenIt == mStolenItems.end())\n            return;\n\n        Owner owner;\n        owner.first = victim.getCellRef().getRefId();\n        owner.second = false;\n\n        const std::string victimFaction = victim.getClass().getPrimaryFaction(victim);\n        if (!victimFaction.empty() && Misc::StringUtils::ciEqual(item.getCellRef().getFaction(), victimFaction)) // Is the item faction-owned?\n        {\n            owner.first = victimFaction;\n            owner.second = true;\n        }\n\n        Misc::StringUtils::lowerCaseInPlace(owner.first);\n\n        // decrease count of stolen items\n        int toRemove = std::min(count, mStolenItems[itemId][owner]);\n        mStolenItems[itemId][owner] -= toRemove;\n        if (mStolenItems[itemId][owner] == 0)\n        {\n            // erase owner from stolen items owners\n            OwnerMap& owners = stolenIt->second;\n            OwnerMap::iterator ownersIt = owners.find(owner);\n            if (ownersIt != owners.end())\n                owners.erase(ownersIt);\n        }\n\n        MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);\n\n        // move items from player to owner and report about theft\n        victim.getClass().getContainerStore(victim).add(item, toRemove, victim);\n        store.remove(item, toRemove, player);\n        commitCrime(player, victim, OT_Theft, item.getCellRef().getFaction(), item.getClass().getValue(item) * toRemove);\n    }\n\n    void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer)\n    {\n        MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);\n        MWWorld::ContainerStore& containerStore = targetContainer.getClass().getContainerStore(targetContainer);\n        for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n        {\n            StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getCellRef().getRefId()));\n            if (stolenIt == mStolenItems.end())\n                continue;\n            OwnerMap& owners = stolenIt->second;\n            int itemCount = it->getRefData().getCount();\n            for (OwnerMap::iterator ownerIt = owners.begin(); ownerIt != owners.end();)\n            {\n                int toRemove = std::min(itemCount, ownerIt->second);\n                itemCount -= toRemove;\n                ownerIt->second -= toRemove;\n                if (ownerIt->second == 0)\n                    owners.erase(ownerIt++);\n                else\n                    ++ownerIt;\n            }\n\n            int toMove = it->getRefData().getCount() - itemCount;\n\n            containerStore.add(*it, toMove, targetContainer);\n            store.remove(*it, toMove, player);\n        }\n        // TODO: unhardcode the locklevel\n        targetContainer.getCellRef().lock(50);\n    }\n\n    void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container,\n                                     int count, bool alarm)\n    {\n        if (ptr != getPlayer())\n            return;\n\n        MWWorld::Ptr victim;\n\n        bool isAllowed = true;\n        const MWWorld::CellRef* ownerCellRef = &item.getCellRef();\n        if (!container.isEmpty())\n        {\n            // Inherit the owner of the container\n            ownerCellRef = &container.getCellRef();\n            isAllowed = isAllowedToUse(ptr, container, victim);\n        }\n        else\n        {\n            isAllowed = isAllowedToUse(ptr, item, victim);\n            if (!item.getCellRef().hasContentFile())\n            {\n                // this is a manually placed item, which means it was already stolen\n                return;\n            }\n        }\n\n        if (isAllowed)\n            return;\n\n        Owner owner;\n        owner.second = false;\n        if (!container.isEmpty() && container.getClass().isActor())\n        {\n            // \"container\" is an actor inventory, so just take actor's ID\n            owner.first = ownerCellRef->getRefId();\n        }\n        else\n        {\n            owner.first = ownerCellRef->getOwner();\n            if (owner.first.empty())\n            {\n                owner.first = ownerCellRef->getFaction();\n                owner.second = true;\n            }\n        }\n\n        Misc::StringUtils::lowerCaseInPlace(owner.first);\n\n        if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId))\n        {\n            if (victim.isEmpty() || (victim.getClass().isActor() && victim.getRefData().getCount() > 0 && !victim.getClass().getCreatureStats(victim).isDead()))\n                mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count;\n        }\n        if (alarm)\n            commitCrime(ptr, victim, OT_Theft, ownerCellRef->getFaction(), item.getClass().getValue(item) * count);\n    }\n\n\n    bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, const std::string& factionId, int arg, bool victimAware)\n    {\n        // NOTE: victim may be empty\n\n        // Only player can commit crime\n        if (player != getPlayer())\n            return false;\n\n        // Find all the actors within the alarm radius\n        std::vector<MWWorld::Ptr> neighbors;\n\n        osg::Vec3f from (player.getRefData().getPosition().asVec3());\n        const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();\n        float radius = esmStore.get<ESM::GameSetting>().find(\"fAlarmRadius\")->mValue.getFloat();\n\n        mActors.getObjectsInRange(from, radius, neighbors);\n\n        // victim should be considered even beyond alarm radius\n        if (!victim.isEmpty() && (from - victim.getRefData().getPosition().asVec3()).length2() > radius*radius)\n            neighbors.push_back(victim);\n\n        // get the player's followers / allies (works recursively) that will not report crimes\n        std::set<MWWorld::Ptr> playerFollowers;\n        getActorsSidingWith(player, playerFollowers);\n\n        // Did anyone see it?\n        bool crimeSeen = false;\n        for (const MWWorld::Ptr &neighbor : neighbors)\n        {\n            if (!canReportCrime(neighbor, victim, playerFollowers))\n                continue;\n\n            if ((neighbor == victim && victimAware)\n                    // Murder crime can be reported even if no one saw it (hearing is enough, I guess).\n                    // TODO: Add mod support for stealth executions!\n                    || (type == OT_Murder && neighbor != victim)\n                    || (MWBase::Environment::get().getWorld()->getLOS(player, neighbor) && awarenessCheck(player, neighbor)))\n            {\n                /*\n                    Start of tes3mp addition\n                \n                    We need player-controlled NPCs to not report crimes committed by other players\n                */\n                if (mwmp::PlayerList::isDedicatedPlayer(neighbor))\n                    continue;\n                /*\n                    End of tes3mp addition\n                */\n\n                // NPC will complain about theft even if he will do nothing about it\n                if (type == OT_Theft || type == OT_Pickpocket)\n                    MWBase::Environment::get().getDialogueManager()->say(neighbor, \"thief\");\n\n                crimeSeen = true;\n            }\n        }\n\n        if (crimeSeen)\n            reportCrime(player, victim, type, factionId, arg);\n        else if (type == OT_Assault && !victim.isEmpty())\n        {\n            bool reported = false;\n            if (victim.getClass().isClass(victim, \"guard\")\n                && !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue))\n                reported = reportCrime(player, victim, type, std::string(), arg);\n\n            if (!reported)\n                startCombat(victim, player); // TODO: combat should be started with an \"unaware\" flag, which makes the victim flee?\n        }\n        return crimeSeen;\n    }\n\n    bool MechanicsManager::canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set<MWWorld::Ptr> &playerFollowers)\n    {\n        if (actor == getPlayer()\n            || !actor.getClass().isNpc() || actor.getClass().getCreatureStats(actor).isDead())\n            return false;\n\n        if (actor.getClass().getCreatureStats(actor).getAiSequence().isInCombat(victim))\n            return false;\n\n        // Unconsious actor can not report about crime and should not become hostile\n        if (actor.getClass().getCreatureStats(actor).getKnockedDown())\n            return false;\n\n        // Player's followers should not attack player, or try to arrest him\n        if (actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackageTypeId::Follow))\n        {\n            if (playerFollowers.find(actor) != playerFollowers.end())\n                return false;\n        }\n\n        return true;\n    }\n\n    bool MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, const std::string& factionId, int arg)\n    {\n        const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        if (type == OT_Murder && !victim.isEmpty())\n            victim.getClass().getCreatureStats(victim).notifyMurder();\n\n        // Bounty and disposition penalty for each type of crime\n        float disp = 0.f, dispVictim = 0.f;\n        if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)\n        {\n            arg = store.find(\"iCrimeTresspass\")->mValue.getInteger();\n            disp = dispVictim = store.find(\"iDispTresspass\")->mValue.getFloat();\n        }\n        else if (type == OT_Pickpocket)\n        {\n            arg = store.find(\"iCrimePickPocket\")->mValue.getInteger();\n            disp = dispVictim = store.find(\"fDispPickPocketMod\")->mValue.getFloat();\n        }\n        else if (type == OT_Assault)\n        {\n            arg = store.find(\"iCrimeAttack\")->mValue.getInteger();\n            disp = store.find(\"iDispAttackMod\")->mValue.getFloat();\n            dispVictim = store.find(\"fDispAttacking\")->mValue.getFloat();\n        }\n        else if (type == OT_Murder)\n        {\n            arg = store.find(\"iCrimeKilling\")->mValue.getInteger();\n            disp = dispVictim = store.find(\"iDispKilling\")->mValue.getFloat();\n        }\n        else if (type == OT_Theft)\n        {\n            disp = dispVictim = store.find(\"fDispStealing\")->mValue.getFloat() * arg;\n            arg = static_cast<int>(arg * store.find(\"fCrimeStealing\")->mValue.getFloat());\n            arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen\n        }\n\n        // Make surrounding actors within alarm distance respond to the crime\n        std::vector<MWWorld::Ptr> neighbors;\n\n        const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();\n\n        osg::Vec3f from (player.getRefData().getPosition().asVec3());\n        float radius = esmStore.get<ESM::GameSetting>().find(\"fAlarmRadius\")->mValue.getFloat();\n\n        mActors.getObjectsInRange(from, radius, neighbors);\n\n        // victim should be considered even beyond alarm radius\n        if (!victim.isEmpty() && (from - victim.getRefData().getPosition().asVec3()).length2() > radius*radius)\n            neighbors.push_back(victim);\n\n        int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId();\n\n        // What amount of provocation did this crime generate?\n        // Controls whether witnesses will engage combat with the criminal.\n        int fight = 0, fightVictim = 0;\n        if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)\n            fight = fightVictim = esmStore.get<ESM::GameSetting>().find(\"iFightTrespass\")->mValue.getInteger();\n        else if (type == OT_Pickpocket)\n        {\n            fight = esmStore.get<ESM::GameSetting>().find(\"iFightPickpocket\")->mValue.getInteger();\n            fightVictim = esmStore.get<ESM::GameSetting>().find(\"iFightPickpocket\")->mValue.getInteger() * 4; // *4 according to research wiki\n        }\n        else if (type == OT_Assault)\n        {\n            fight = esmStore.get<ESM::GameSetting>().find(\"iFightAttacking\")->mValue.getInteger();\n            fightVictim = esmStore.get<ESM::GameSetting>().find(\"iFightAttack\")->mValue.getInteger();\n        }\n        else if (type == OT_Murder)\n            fight = fightVictim = esmStore.get<ESM::GameSetting>().find(\"iFightKilling\")->mValue.getInteger();\n        else if (type == OT_Theft)\n            fight = fightVictim = esmStore.get<ESM::GameSetting>().find(\"fFightStealing\")->mValue.getInteger();\n\n        bool reported = false;\n\n        std::set<MWWorld::Ptr> playerFollowers;\n        getActorsSidingWith(player, playerFollowers);\n\n        // Tell everyone (including the original reporter) in alarm range\n        for (const MWWorld::Ptr &actor : neighbors)\n        {\n            if (!canReportCrime(actor, victim, playerFollowers))\n                continue;\n\n            // Will the witness report the crime?\n            if (actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100)\n            {\n                reported = true;\n\n                if (type == OT_Trespassing)\n                    MWBase::Environment::get().getDialogueManager()->say(actor, \"intruder\");\n            }\n        }\n\n        for (const MWWorld::Ptr &actor : neighbors)\n        {\n            if (!canReportCrime(actor, victim, playerFollowers))\n                continue;\n\n            if (reported && actor.getClass().isClass(actor, \"guard\"))\n            {\n                // Mark as Alarmed for dialogue\n                actor.getClass().getCreatureStats(actor).setAlarmed(true);\n\n                // Set the crime ID, which we will use to calm down participants\n                // once the bounty has been paid.\n                actor.getClass().getNpcStats(actor).setCrimeId(id);\n\n                if (!actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackageTypeId::Pursue))\n                {\n                    actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiPursue(player), actor);\n                }\n            }\n            else\n            {\n                float dispTerm = (actor == victim) ? dispVictim : disp;\n\n                float alarmTerm = 0.01f * actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Alarm).getBase();\n                if (type == OT_Pickpocket && alarmTerm <= 0)\n                    alarmTerm = 1.0;\n\n                if (actor != victim)\n                    dispTerm *= alarmTerm;\n\n                float fightTerm = static_cast<float>((actor == victim) ? fightVictim : fight);\n                fightTerm += getFightDispositionBias(dispTerm);\n                fightTerm += getFightDistanceBias(actor, player);\n                fightTerm *= alarmTerm;\n\n                int observerFightRating = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Fight).getBase();\n                if (observerFightRating + fightTerm > 100)\n                    fightTerm = static_cast<float>(100 - observerFightRating);\n                fightTerm = std::max(0.f, fightTerm);\n\n                if (observerFightRating + fightTerm >= 100)\n                {\n                    startCombat(actor, player);\n\n                    NpcStats& observerStats = actor.getClass().getNpcStats(actor);\n                    // Apply aggression value to the base Fight rating, so that the actor can continue fighting\n                    // after a Calm spell wears off\n                    observerStats.setAiSetting(CreatureStats::AI_Fight, observerFightRating + static_cast<int>(fightTerm));\n\n                    observerStats.setBaseDisposition(observerStats.getBaseDisposition() + static_cast<int>(dispTerm));\n\n                    // Set the crime ID, which we will use to calm down participants\n                    // once the bounty has been paid.\n                    observerStats.setCrimeId(id);\n\n                    // Mark as Alarmed for dialogue\n                    observerStats.setAlarmed(true);\n                }\n            }\n        }\n\n        if (reported)\n        {\n            player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty()\n                                                      + arg);\n\n            // If committing a crime against a faction member, expell from the faction\n            if (!victim.isEmpty() && victim.getClass().isNpc())\n            {\n                std::string factionID = victim.getClass().getPrimaryFaction(victim);\n\n                const std::map<std::string, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks();\n                if (playerRanks.find(Misc::StringUtils::lowerCase(factionID)) != playerRanks.end())\n                {\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_PLAYER_FACTION packet every time a player is expelled from a faction\n                    */\n                    mwmp::Main::get().getLocalPlayer()->sendFactionExpulsionState(Misc::StringUtils::lowerCase(factionID), true);\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    player.getClass().getNpcStats(player).expell(factionID);\n                }\n            }\n            else if (!factionId.empty())\n            {\n                const std::map<std::string, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks();\n                if (playerRanks.find(Misc::StringUtils::lowerCase(factionId)) != playerRanks.end())\n                {\n                    player.getClass().getNpcStats(player).expell(factionId);\n                }\n            }\n\n            if (type == OT_Assault && !victim.isEmpty()\n                    && !victim.getClass().getCreatureStats(victim).getAiSequence().isInCombat(player)\n                    && victim.getClass().isNpc())\n            {\n                // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.\n                // Note: accidental or collateral damage attacks are ignored.\n                if (!victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue))\n                    startCombat(victim, player);\n\n                // Set the crime ID, which we will use to calm down participants\n                // once the bounty has been paid.\n                victim.getClass().getNpcStats(victim).setCrimeId(id);\n            }\n        }\n\n        return reported;\n    }\n\n    bool MechanicsManager::actorAttacked(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker)\n    {\n        const MWWorld::Ptr& player = getPlayer();\n        if (target == player || !attacker.getClass().isActor())\n            return false;\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't set DedicatedPlayers as being in combat with the attacker, to prevent\n            AI actors from deciding to reciprocate by also starting combat\n        */\n        if (mwmp::PlayerList::isDedicatedPlayer(target))\n            return false;\n        /*\n            End of tes3mp change (major)\n        */\n\n        MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target);\n        /*\n            Start of tes3mp change (major)\n\n            Allow collateral damage from dedicated players as well\n        */\n        if (attacker == player || mwmp::PlayerList::isDedicatedPlayer(attacker))\n        /*\n            End of tes3mp change (major)\n        */\n        {\n            std::set<MWWorld::Ptr> followersAttacker;\n            getActorsSidingWith(attacker, followersAttacker);\n\n            /*\n                Start of tes3mp change (major)\n\n                Check not only whether the target is on the same side as the attacker,\n                but also whether the attacker is on the same side as the target,\n                thus allowing for NPC companions of one player to forgive another player\n                when those players are allied\n            */\n            std::set<MWWorld::Ptr> followersTarget;\n            getActorsSidingWith(target, followersTarget);\n\n            if (followersAttacker.find(target) != followersAttacker.end() || followersTarget.find(attacker) != followersTarget.end())\n            /*\n                End of tes3mp change (major)\n            */\n            {\n                statsTarget.friendlyHit();\n\n                /*\n                    Start of tes3mp change (major)\n\n                    Due to a greater propensity for collateral damage in multiplayer,\n                    allow more friendly hits\n\n                    TODO: Allow the server to change the count of the friendly hits\n                */\n                if (statsTarget.getFriendlyHits() < 8)\n                /*\n                    End of tes3mp change (major)\n                */\n                {\n                    MWBase::Environment::get().getDialogueManager()->say(target, \"hit\");\n                    return false;\n                }\n            }\n        }\n\n        if (canCommitCrimeAgainst(target, attacker))\n            commitCrime(attacker, target, MWBase::MechanicsManager::OT_Assault);\n\n        AiSequence& seq = statsTarget.getAiSequence();\n\n        /*\n            Start of tes3mp change (major)\n\n            Make it possible to start combat with DedicatedPlayers and DedicatedActors by\n            adding additional conditions for them\n        */\n        if (!attacker.isEmpty()\n            && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target) || attacker == player\n                || mwmp::PlayerList::isDedicatedPlayer(attacker) || mwmp::Main::get().getCellController()->isDedicatedActor(attacker))\n            && !seq.isInCombat(attacker))\n        /*\n            End of tes3mp change (major)\n        */\n        {\n            // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.\n            // Note: accidental or collateral damage attacks are ignored.\n            if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackageTypeId::Pursue))\n            {\n                // If an actor has OnPCHitMe declared in his script, his Fight = 0 and the attacker is player,\n                // he will attack the player only if we will force him (e.g. via StartCombat console command)\n                bool peaceful = false;\n                std::string script = target.getClass().getScript(target);\n                if (!script.empty() && target.getRefData().getLocals().hasVar(script, \"onpchitme\") && attacker == player)\n                {\n                    int fight = std::max(0, target.getClass().getCreatureStats(target).getAiSetting(CreatureStats::AI_Fight).getModified());\n                    peaceful = (fight == 0);\n                }\n\n                if (!peaceful)\n                    startCombat(target, attacker);\n            }\n        }\n\n        return true;\n    }\n\n    bool MechanicsManager::canCommitCrimeAgainst(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker)\n    {\n        const MWMechanics::AiSequence& seq = target.getClass().getCreatureStats(target).getAiSequence();\n        return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker)\n                && !isAggressive(target, attacker) && !seq.isEngagedWithActor()\n                && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackageTypeId::Pursue);\n    }\n\n    void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker)\n    {\n        if (attacker.isEmpty() || victim.isEmpty())\n            return;\n\n        if (victim == attacker)\n            return; // known to happen\n\n        if (!victim.getClass().isNpc())\n            return; // TODO: implement animal rights\n\n        const MWMechanics::NpcStats& victimStats = victim.getClass().getNpcStats(victim);\n        const MWWorld::Ptr &player = getPlayer();\n        bool canCommit = attacker == player && canCommitCrimeAgainst(victim, attacker);\n\n        // For now we report only about crimes of player and player's followers\n        if (attacker != player)\n        {\n            std::set<MWWorld::Ptr> playerFollowers;\n            getActorsSidingWith(player, playerFollowers);\n            if (playerFollowers.find(attacker) == playerFollowers.end())\n                return;\n        }\n\n        if (!canCommit && victimStats.getCrimeId() == -1)\n            return;\n\n        // Simple check for who attacked first: if the player attacked first, a crimeId should be set\n        // Doesn't handle possible edge case where no one reported the assault, but in such a case,\n        // for bystanders it is not possible to tell who attacked first, anyway.\n        commitCrime(player, victim, MWBase::MechanicsManager::OT_Murder);\n    }\n\n    bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer)\n    {\n        if (observer.getClass().getCreatureStats(observer).isDead() || !observer.getRefData().isEnabled())\n            return false;\n\n        const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n\n        float invisibility = stats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude();\n        if (invisibility > 0)\n            return false;\n\n        float sneakTerm = 0;\n        if (isSneaking(ptr))\n        {\n            static float fSneakSkillMult = store.find(\"fSneakSkillMult\")->mValue.getFloat();\n            static float fSneakBootMult = store.find(\"fSneakBootMult\")->mValue.getFloat();\n            float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));\n            float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();\n            float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();\n            float bootWeight = 0;\n            if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr))\n            {\n                const MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);\n                MWWorld::ConstContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);\n                if (it != inv.end())\n                    bootWeight = it->getClass().getWeight(*it);\n            }\n            sneakTerm = fSneakSkillMult * sneak + 0.2f * agility + 0.1f * luck + bootWeight * fSneakBootMult;\n        }\n\n        static float fSneakDistBase = store.find(\"fSneakDistanceBase\")->mValue.getFloat();\n        static float fSneakDistMult = store.find(\"fSneakDistanceMultiplier\")->mValue.getFloat();\n\n        osg::Vec3f pos1 (ptr.getRefData().getPosition().asVec3());\n        osg::Vec3f pos2 (observer.getRefData().getPosition().asVec3());\n        float distTerm = fSneakDistBase + fSneakDistMult * (pos1 - pos2).length();\n\n        float chameleon = stats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude();\n        float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility;\n\n        CreatureStats& observerStats = observer.getClass().getCreatureStats(observer);\n        float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();\n        float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();\n        float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude();\n        float obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak);\n\n        float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind;\n\n        // is ptr behind the observer?\n        static float fSneakNoViewMult = store.find(\"fSneakNoViewMult\")->mValue.getFloat();\n        static float fSneakViewMult = store.find(\"fSneakViewMult\")->mValue.getFloat();\n        float y = 0;\n        osg::Vec3f vec = pos1 - pos2;\n        if (observer.getRefData().getBaseNode())\n        {\n            osg::Vec3f observerDir = (observer.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0));\n\n            float angleRadians = std::acos(observerDir * vec / (observerDir.length() * vec.length()));\n            if (angleRadians > osg::DegreesToRadians(90.f))\n                y = obsTerm * observerStats.getFatigueTerm() * fSneakNoViewMult;\n            else\n                y = obsTerm * observerStats.getFatigueTerm() * fSneakViewMult;\n        }\n\n        float target = x - y;\n\n        return (Misc::Rng::roll0to99() >= target);\n    }\n\n    void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)\n    {\n        CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n\n        // Don't add duplicate packages nor add packages to dead actors.\n        if (stats.isDead() || stats.getAiSequence().isInCombat(target))\n            return;\n\n        // The target is somehow the same as the actor. Early-out.\n        if (ptr == target)\n        {\n            // We don't care about dialogue filters since the target is invalid.\n            // We still want to play the combat taunt.\n            MWBase::Environment::get().getDialogueManager()->say(ptr, \"attack\");\n            return;\n        }\n\n        stats.getAiSequence().stack(MWMechanics::AiCombat(target), ptr);\n        if (target == getPlayer())\n        {\n            // if guard starts combat with player, guards pursuing player should do the same\n            if (ptr.getClass().isClass(ptr, \"Guard\"))\n            {\n                stats.setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable\n                for (Actors::PtrActorMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter)\n                {\n                    if (iter->first.getClass().isClass(iter->first, \"Guard\"))\n                    {\n                        MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence();\n                        if (aiSeq.getTypeId() == MWMechanics::AiPackageTypeId::Pursue)\n                        {\n                            aiSeq.stopPursuit();\n                            aiSeq.stack(MWMechanics::AiCombat(target), ptr);\n                            iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable\n                        }\n                    }\n                }\n            }\n        }\n\n        // Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly\n        MWBase::Environment::get().getDialogueManager()->say(ptr, \"attack\");\n    }\n\n    void MechanicsManager::getObjectsInRange(const osg::Vec3f &position, float radius, std::vector<MWWorld::Ptr> &objects)\n    {\n        mActors.getObjectsInRange(position, radius, objects);\n        mObjects.getObjectsInRange(position, radius, objects);\n    }\n\n    void MechanicsManager::getActorsInRange(const osg::Vec3f &position, float radius, std::vector<MWWorld::Ptr> &objects)\n    {\n        mActors.getObjectsInRange(position, radius, objects);\n    }\n\n    bool MechanicsManager::isAnyActorInRange(const osg::Vec3f &position, float radius)\n    {\n        return mActors.isAnyObjectInRange(position, radius);\n    }\n\n    std::list<MWWorld::Ptr> MechanicsManager::getActorsSidingWith(const MWWorld::Ptr& actor)\n    {\n        return mActors.getActorsSidingWith(actor);\n    }\n\n    std::list<MWWorld::Ptr> MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor)\n    {\n        return mActors.getActorsFollowing(actor);\n    }\n\n    std::list<int> MechanicsManager::getActorsFollowingIndices(const MWWorld::Ptr& actor)\n    {\n        return mActors.getActorsFollowingIndices(actor);\n    }\n\n    std::map<int, MWWorld::Ptr> MechanicsManager::getActorsFollowingByIndex(const MWWorld::Ptr& actor)\n    {\n        return mActors.getActorsFollowingByIndex(actor);\n    }\n\n    std::list<MWWorld::Ptr> MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) {\n        return mActors.getActorsFighting(actor);\n    }\n\n    std::list<MWWorld::Ptr> MechanicsManager::getEnemiesNearby(const MWWorld::Ptr& actor) {\n        return mActors.getEnemiesNearby(actor);\n    }\n\n    void MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) {\n        mActors.getActorsFollowing(actor, out);\n    }\n\n    void MechanicsManager::getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) {\n        mActors.getActorsSidingWith(actor, out);\n    }\n\n    int MechanicsManager::countSavedGameRecords() const\n    {\n        return 1 // Death counter\n                +1; // Stolen items\n    }\n\n    void MechanicsManager::write(ESM::ESMWriter &writer, Loading::Listener &listener) const\n    {\n        mActors.write(writer, listener);\n\n        ESM::StolenItems items;\n        items.mStolenItems = mStolenItems;\n        writer.startRecord(ESM::REC_STLN);\n        items.write(writer);\n        writer.endRecord(ESM::REC_STLN);\n    }\n\n    void MechanicsManager::readRecord(ESM::ESMReader &reader, uint32_t type)\n    {\n        if (type == ESM::REC_STLN)\n        {\n            ESM::StolenItems items;\n            items.load(reader);\n            mStolenItems = items.mStolenItems;\n        }\n        else\n            mActors.readRecord(reader, type);\n    }\n\n    void MechanicsManager::clear()\n    {\n        mActors.clear();\n        mStolenItems.clear();\n        mClassSelected = false;\n        mRaceSelected = false;\n    }\n\n    bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)\n    {\n        // Don't become aggressive if a calm effect is active, since it would cause combat to cycle on/off as\n        // combat is activated here and then canceled by the calm effect\n        if ((ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0)\n            || (!ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::CalmCreature).getMagnitude() > 0))\n            return false;\n\n        int disposition = 50;\n        if (ptr.getClass().isNpc())\n            disposition = getDerivedDisposition(ptr, true);\n\n        int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()\n                + static_cast<int>(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast<float>(disposition)));\n\n        if (ptr.getClass().isNpc() && target.getClass().isNpc())\n        {\n            if (target.getClass().getNpcStats(target).isWerewolf() ||\n                    (target == getPlayer() &&\n                     MWBase::Environment::get().getWorld()->getGlobalInt(\"pcknownwerewolf\")))\n            {\n                const ESM::GameSetting * iWerewolfFightMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"iWerewolfFightMod\");\n                fight += iWerewolfFightMod->mValue.getInteger();\n            }\n        }\n\n        return (fight >= 100);\n    }\n\n    void MechanicsManager::resurrect(const MWWorld::Ptr &ptr)\n    {\n        CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n        if (stats.isDead())\n        {\n            stats.resurrect();\n            mActors.resurrect(ptr);\n        }\n    }\n\n    bool MechanicsManager::isCastingSpell(const MWWorld::Ptr &ptr) const\n    {\n        return mActors.isCastingSpell(ptr);\n    }\n\n    bool MechanicsManager::isReadyToBlock(const MWWorld::Ptr &ptr) const\n    {\n        return mActors.isReadyToBlock(ptr);\n    }\n\n    bool MechanicsManager::isAttackingOrSpell(const MWWorld::Ptr &ptr) const\n    {\n        return mActors.isAttackingOrSpell(ptr);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set the attackingOrSpell state from elsewhere in the code\n    */\n    void MechanicsManager::setAttackingOrSpell(const MWWorld::Ptr &ptr, bool state) const\n    {\n        return mActors.setAttackingOrSpell(ptr, state);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void MechanicsManager::setWerewolf(const MWWorld::Ptr& actor, bool werewolf)\n    {\n        MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor);\n\n        // The actor does not have to change state\n        if (npcStats.isWerewolf() == werewolf)\n            return;\n\n        MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer();\n\n        if (actor == player->getPlayer())\n        {\n            if (werewolf)\n            {\n                player->saveStats();\n                player->setWerewolfStats();\n            }\n            else\n                player->restoreStats();\n        }\n\n        // Werewolfs can not cast spells, so we need to unset the prepared spell if there is one.\n        if (npcStats.getDrawState() == MWMechanics::DrawState_Spell)\n            npcStats.setDrawState(MWMechanics::DrawState_Nothing);\n\n        npcStats.setWerewolf(werewolf);\n\n        MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor);\n\n        if(werewolf)\n        {\n            inv.unequipAll(actor);\n            inv.equip(MWWorld::InventoryStore::Slot_Robe, inv.ContainerStore::add(\"werewolfrobe\", 1, actor), actor);\n        }\n        else\n        {\n            inv.unequipSlot(MWWorld::InventoryStore::Slot_Robe, actor);\n            inv.ContainerStore::remove(\"werewolfrobe\", 1, actor);\n        }\n\n        if(actor == player->getPlayer())\n        {\n            MWBase::Environment::get().getWorld()->reattachPlayerCamera();\n\n            // Update the GUI only when called on the player\n            MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();\n\n            if (werewolf)\n            {\n                windowManager->forceHide(MWGui::GW_Inventory);\n                windowManager->forceHide(MWGui::GW_Magic);\n            }\n            else\n            {\n                windowManager->unsetForceHide(MWGui::GW_Inventory);\n                windowManager->unsetForceHide(MWGui::GW_Magic);\n            }\n\n            windowManager->setWerewolfOverlay(werewolf);\n\n            // Witnesses of the player's transformation will make them a globally known werewolf\n            std::vector<MWWorld::Ptr> neighbors;\n            const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n            getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find(\"fAlarmRadius\")->mValue.getFloat(), neighbors);\n\n            bool detected = false, reported = false;\n            for (const MWWorld::Ptr& neighbor : neighbors)\n            {\n                if (neighbor == actor || !neighbor.getClass().isNpc())\n                    continue;\n\n                if (MWBase::Environment::get().getWorld()->getLOS(neighbor, actor) && awarenessCheck(actor, neighbor))\n                {\n                    detected = true;\n                    if (neighbor.getClass().getCreatureStats(neighbor).getAiSetting(MWMechanics::CreatureStats::AI_Alarm).getModified() > 0)\n                    {\n                        reported = true;\n                        break;\n                    }\n                }\n            }\n\n            if (detected)\n            {\n                windowManager->messageBox(\"#{sWerewolfAlarmMessage}\");\n                MWBase::Environment::get().getWorld()->setGlobalInt(\"pcknownwerewolf\", 1);\n\n                if (reported)\n                {\n                    npcStats.setBounty(npcStats.getBounty()+\n                                       gmst.find(\"iWereWolfBounty\")->mValue.getInteger());\n                }\n            }\n        }\n    }\n\n    void MechanicsManager::applyWerewolfAcrobatics(const MWWorld::Ptr &actor)\n    {\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        MWMechanics::NpcStats &stats = actor.getClass().getNpcStats(actor);\n\n        stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find(\"fWerewolfAcrobatics\")->mValue.getInteger());\n    }\n\n    void MechanicsManager::cleanupSummonedCreature(const MWWorld::Ptr &caster, int creatureActorId)\n    {\n        mActors.cleanupSummonedCreature(caster.getClass().getCreatureStats(caster), creatureActorId);\n    }\n\n    void MechanicsManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const\n    {\n        stats.setAttribute(frameNumber, \"Mechanics Actors\", mActors.size());\n        stats.setAttribute(frameNumber, \"Mechanics Objects\", mObjects.size());\n    }\n\n    int MechanicsManager::getGreetingTimer(const MWWorld::Ptr &ptr) const\n    {\n        return mActors.getGreetingTimer(ptr);\n    }\n\n    float MechanicsManager::getAngleToPlayer(const MWWorld::Ptr &ptr) const\n    {\n        return mActors.getAngleToPlayer(ptr);\n    }\n\n    GreetingState MechanicsManager::getGreetingState(const MWWorld::Ptr &ptr) const\n    {\n        return mActors.getGreetingState(ptr);\n    }\n\n    bool MechanicsManager::isTurningToPlayer(const MWWorld::Ptr &ptr) const\n    {\n        return mActors.isTurningToPlayer(ptr);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/mechanicsmanagerimp.hpp",
    "content": "#ifndef GAME_MWMECHANICS_MECHANICSMANAGERIMP_H\n#define GAME_MWMECHANICS_MECHANICSMANAGERIMP_H\n\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"npcstats.hpp\"\n#include \"objects.hpp\"\n#include \"actors.hpp\"\n\nnamespace MWWorld\n{\n    class CellStore;\n}\n\nnamespace MWMechanics\n{\n    class MechanicsManager : public MWBase::MechanicsManager\n    {\n            bool mUpdatePlayer;\n            bool mClassSelected;\n            bool mRaceSelected;\n            bool mAI;///< is AI active?\n\n            Objects mObjects;\n            Actors mActors;\n\n            typedef std::pair<std::string, bool> Owner; // < Owner id, bool isFaction >\n            typedef std::map<Owner, int> OwnerMap; // < Owner, number of stolen items with this id from this owner >\n            typedef std::map<std::string, OwnerMap> StolenItemsMap;\n            StolenItemsMap mStolenItems;\n\n        public:\n\n            void buildPlayer();\n            ///< build player according to stored class/race/birthsign information. Will\n            /// default to the values of the ESM::NPC object, if no explicit information is given.\n\n            MechanicsManager();\n\n            void add (const MWWorld::Ptr& ptr) override;\n            ///< Register an object for management\n\n            void remove (const MWWorld::Ptr& ptr) override;\n            ///< Deregister an object for management\n\n            void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) override;\n            ///< Moves an object to a new cell\n\n            void drop(const MWWorld::CellStore *cellStore) override;\n            ///< Deregister all objects in the given cell.\n\n            void update (float duration, bool paused) override;\n            ///< Update objects\n            ///\n            /// \\param paused In game type does not currently advance (this usually means some GUI\n            /// component is up).\n\n            void setPlayerName (const std::string& name) override;\n            ///< Set player name.\n\n            void setPlayerRace (const std::string& id, bool male, const std::string &head, const std::string &hair) override;\n            ///< Set player race.\n\n            void setPlayerBirthsign (const std::string& id) override;\n            ///< Set player birthsign.\n\n            void setPlayerClass (const std::string& id) override;\n            ///< Set player class to stock class.\n\n            void setPlayerClass (const ESM::Class& class_) override;\n            ///< Set player class to custom class.\n\n            void restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep) override;\n\n            void rest(double hours, bool sleep) override;\n            ///< If the player is sleeping or waiting, this should be called every hour.\n            /// @param sleep is the player sleeping or waiting?\n\n            int getHoursToRest() const override;\n            ///< Calculate how many hours the player needs to rest in order to be fully healed\n\n            int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) override;\n            ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC.\n\n            int getDerivedDisposition(const MWWorld::Ptr& ptr, bool addTemporaryDispositionChange = true) override;\n            ///< Calculate the diposition of an NPC toward the player.\n\n            int countDeaths (const std::string& id) const override;\n            ///< Return the number of deaths for actors with the given ID.\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the number of deaths for an actor with the given refId\n            */\n            virtual void setDeaths(const std::string& refId, int number);\n            /*\n                End of tes3mp addition\n            */\n\n            void getPersuasionDispositionChange(const MWWorld::Ptr& npc, PersuasionType type, bool& success, float& tempChange, float& permChange) override;\n            ///< Perform a persuasion action on NPC\n\n            /// Check if \\a observer is potentially aware of \\a ptr. Does not do a line of sight check!\n            bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) override;\n\n            /// Makes \\a ptr fight \\a target. Also shouts a combat taunt.\n            void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) override;\n\n            /**\n             * @note victim may be empty\n             * @param arg Depends on \\a type, e.g. for Theft, the value of the item that was stolen.\n             * @param victimAware Is the victim already aware of the crime?\n             *                    If this parameter is false, it will be determined by a line-of-sight and awareness check.\n             * @return was the crime seen?\n             */\n            bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,\n                                      OffenseType type, const std::string& factionId=\"\", int arg=0, bool victimAware=false) override;\n            /// @return false if the attack was considered a \"friendly hit\" and forgiven\n            bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) override;\n\n            /// Notify that actor was killed, add a murder bounty if applicable\n            /// @note No-op for non-player attackers\n            void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) override;\n\n            /// Utility to check if taking this item is illegal and calling commitCrime if so\n            /// @param container The container the item is in; may be empty for an item in the world\n            void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container,\n                                    int count, bool alarm = true) override;\n            /// Utility to check if unlocking this object is illegal and calling commitCrime if so\n            void unlockAttempted (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) override;\n            /// Attempt sleeping in a bed. If this is illegal, call commitCrime.\n            /// @return was it illegal, and someone saw you doing it? Also returns fail when enemies are nearby\n            bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) override;\n\n            void forceStateUpdate(const MWWorld::Ptr &ptr) override;\n\n            /// Attempt to play an animation group\n            /// @return Success or error\n            bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false) override;\n            void skipAnimation(const MWWorld::Ptr& ptr) override;\n            bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName) override;\n            void persistAnimationStates() override;\n\n            /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently\n            /// paused we may want to do it manually (after equipping permanent enchantment)\n            void updateMagicEffects (const MWWorld::Ptr& ptr) override;\n\n            void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& objects) override;\n            void getActorsInRange(const osg::Vec3f &position, float radius, std::vector<MWWorld::Ptr> &objects) override;\n\n            /// Check if there are actors in selected range\n            bool isAnyActorInRange(const osg::Vec3f &position, float radius) override;\n\n            std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) override;\n            std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) override;\n            std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor) override;\n            std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor) override;\n\n            std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor) override;\n            std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor) override;\n\n            /// Recursive version of getActorsFollowing\n            void getActorsFollowing(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) override;\n            /// Recursive version of getActorsSidingWith\n            void getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) override;\n\n            bool toggleAI() override;\n            bool isAIActive() override;\n\n            void playerLoaded() override;\n\n            bool onOpen(const MWWorld::Ptr& ptr) override;\n            void onClose(const MWWorld::Ptr& ptr) override;\n\n            int countSavedGameRecords() const override;\n\n            void write (ESM::ESMWriter& writer, Loading::Listener& listener) const override;\n\n            void readRecord (ESM::ESMReader& reader, uint32_t type) override;\n\n            void clear() override;\n\n            bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) override;\n\n            void resurrect(const MWWorld::Ptr& ptr) override;\n\n            bool isCastingSpell (const MWWorld::Ptr& ptr) const override;\n\n            bool isReadyToBlock (const MWWorld::Ptr& ptr) const override;\n            /// Is \\a ptr casting spell or using weapon now?\n            bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the attackingOrSpell state from elsewhere in the code\n            */\n            virtual void setAttackingOrSpell(const MWWorld::Ptr &ptr, bool state) const override;\n            /*\n                End of tes3mp addition\n            */\n\n            void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false) override;\n\n            void processChangedSettings(const Settings::CategorySettingVector& settings) override;\n\n            float getActorsProcessingRange() const override;\n\n            void notifyDied(const MWWorld::Ptr& actor) override;\n\n            /// Check if the target actor was detected by an observer\n            /// If the observer is a non-NPC, check all actors in AI processing distance as observers\n            bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) override;\n\n            void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) override;\n\n            /// List the owners that the player has stolen this item from (the owner can be an NPC or a faction).\n            /// <Owner, item count>\n            std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid) override;\n\n            /// Has the player stolen this item from the given owner?\n            bool isItemStolenFrom(const std::string& itemid, const MWWorld::Ptr& ptr) override;\n\n            bool isBoundItem(const MWWorld::Ptr& item) override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check if an itemId corresponds to a bound item\n            */\n            virtual bool isBoundItem(std::string itemId);\n            /*\n                End of tes3mp addition\n            */\n\n            /// @return is \\a ptr allowed to take/use \\a target or is it a crime?\n            bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) override;\n\n            void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) override;\n            void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) override;\n\n            void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) override;\n\n            void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) override;\n\n            bool isAttackPreparing(const MWWorld::Ptr& ptr) override;\n            bool isRunning(const MWWorld::Ptr& ptr) override;\n            bool isSneaking(const MWWorld::Ptr& ptr) override;\n\n            void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;\n\n            int getGreetingTimer(const MWWorld::Ptr& ptr) const override;\n            float getAngleToPlayer(const MWWorld::Ptr& ptr) const override;\n            GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override;\n            bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override;\n\n            void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) override;\n\n        private:\n            bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);\n            bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set<MWWorld::Ptr> &playerFollowers);\n\n            bool reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,\n                                      OffenseType type, const std::string& factionId, int arg=0);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/movement.hpp",
    "content": "#ifndef GAME_MWMECHANICS_MOVEMENT_H\n#define GAME_MWMECHANICS_MOVEMENT_H\n\n#include <osg/Vec3f>\n\nnamespace MWMechanics\n{\n    /// Desired movement for an actor\n    struct Movement\n    {\n        // Desired movement. Direction is relative to the current orientation.\n        // Length of the vector controls desired speed. 0 - stay, 0.5 - half-speed, 1.0 - max speed.\n        float mPosition[3];\n        // Desired rotation delta (euler angles).\n        float mRotation[3];\n\n        // Controlled by CharacterController, should not be changed from other places.\n        // These fields can not be private fields in CharacterController, because Actor::getCurrentSpeed uses it.\n        float mSpeedFactor;\n        bool mIsStrafing;\n\n        Movement()\n        {\n            mPosition[0] = mPosition[1] = mPosition[2] = 0.0f;\n            mRotation[0] = mRotation[1] = mRotation[2] = 0.0f;\n            mSpeedFactor = 1.f;\n            mIsStrafing = false;\n        }\n\n        osg::Vec3f asVec3()\n        {\n            return osg::Vec3f(mPosition[0], mPosition[1], mPosition[2]);\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/npcstats.cpp",
    "content": "#include \"npcstats.hpp\"\n\n#include <iomanip>\n\n#include <components/esm/loadclas.hpp>\n#include <components/esm/loadgmst.hpp>\n#include <components/esm/loadfact.hpp>\n#include <components/esm/npcstats.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\nMWMechanics::NpcStats::NpcStats()\n    : mDisposition (0)\n, mReputation(0)\n, mCrimeId(-1)\n, mBounty(0)\n, mWerewolfKills (0)\n, mLevelProgress(0)\n, mTimeToStartDrowning(-1.0) // set breath to special value, it will be replaced during actor update\n    , mIsWerewolf(false)\n{\n    mSkillIncreases.resize (ESM::Attribute::Length, 0);\n    mSpecIncreases.resize(3, 0);\n}\n\nint MWMechanics::NpcStats::getBaseDisposition() const\n{\n    return mDisposition;\n}\n\nvoid MWMechanics::NpcStats::setBaseDisposition(int disposition)\n{\n    mDisposition = disposition;\n}\n\nconst MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) const\n{\n    if (index<0 || index>=ESM::Skill::Length)\n        throw std::runtime_error (\"skill index out of range\");\n\n    return mSkill[index];\n}\n\nMWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index)\n{\n    if (index<0 || index>=ESM::Skill::Length)\n        throw std::runtime_error (\"skill index out of range\");\n\n    return mSkill[index];\n}\n\nvoid MWMechanics::NpcStats::setSkill(int index, const MWMechanics::SkillValue &value)\n{\n    if (index<0 || index>=ESM::Skill::Length)\n        throw std::runtime_error (\"skill index out of range\");\n\n    mSkill[index] = value;\n}\n\nconst std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks() const\n{\n    return mFactionRank;\n}\n\nint MWMechanics::NpcStats::getFactionRank(const std::string &faction) const\n{\n    const std::string lower = Misc::StringUtils::lowerCase(faction);\n    std::map<std::string, int>::const_iterator it = mFactionRank.find(lower);\n    if (it != mFactionRank.end())\n        return it->second;\n\n    return -1;\n}\n\nvoid MWMechanics::NpcStats::raiseRank(const std::string &faction)\n{\n    const std::string lower = Misc::StringUtils::lowerCase(faction);\n    std::map<std::string, int>::iterator it = mFactionRank.find(lower);\n    if (it != mFactionRank.end())\n    {\n        // Does the next rank exist?\n        const ESM::Faction* factionPtr = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(lower);\n        if (it->second+1 < 10 && !factionPtr->mRanks[it->second+1].empty())\n            it->second += 1;\n    }\n}\n\nvoid MWMechanics::NpcStats::lowerRank(const std::string &faction)\n{\n    const std::string lower = Misc::StringUtils::lowerCase(faction);\n    std::map<std::string, int>::iterator it = mFactionRank.find(lower);\n    if (it != mFactionRank.end())\n    {\n        it->second = it->second-1;\n        if (it->second < 0)\n        {\n            mFactionRank.erase(it);\n            mExpelled.erase(lower);\n        }\n    }\n}\n\nvoid MWMechanics::NpcStats::joinFaction(const std::string& faction)\n{\n    const std::string lower = Misc::StringUtils::lowerCase(faction);\n    std::map<std::string, int>::iterator it = mFactionRank.find(lower);\n    if (it == mFactionRank.end())\n        mFactionRank[lower] = 0;\n}\n\nbool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const\n{\n    return mExpelled.find(Misc::StringUtils::lowerCase(factionID)) != mExpelled.end();\n}\n\nvoid MWMechanics::NpcStats::expell(const std::string& factionID)\n{\n    std::string lower = Misc::StringUtils::lowerCase(factionID);\n    if (mExpelled.find(lower) == mExpelled.end())\n    {\n        std::string message = \"#{sExpelledMessage}\";\n        message += MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID)->mName;\n        MWBase::Environment::get().getWindowManager()->messageBox(message);\n        mExpelled.insert(lower);\n    }\n}\n\nvoid MWMechanics::NpcStats::clearExpelled(const std::string& factionID)\n{\n    mExpelled.erase(Misc::StringUtils::lowerCase(factionID));\n}\n\nbool MWMechanics::NpcStats::isInFaction (const std::string& faction) const\n{\n    return (mFactionRank.find(Misc::StringUtils::lowerCase(faction)) != mFactionRank.end());\n}\n\nint MWMechanics::NpcStats::getFactionReputation (const std::string& faction) const\n{\n    std::map<std::string, int>::const_iterator iter = mFactionReputation.find (Misc::StringUtils::lowerCase(faction));\n\n    if (iter==mFactionReputation.end())\n        return 0;\n\n    return iter->second;\n}\n\nvoid MWMechanics::NpcStats::setFactionReputation (const std::string& faction, int value)\n{\n    mFactionReputation[Misc::StringUtils::lowerCase(faction)] = value;\n}\n\nfloat MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const ESM::Class& class_) const\n{\n    float progressRequirement = static_cast<float>(1 + getSkill(skillIndex).getBase());\n\n    const MWWorld::Store<ESM::GameSetting> &gmst =\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n    float typeFactor = gmst.find (\"fMiscSkillBonus\")->mValue.getFloat();\n\n    for (int i=0; i<5; ++i)\n    {\n        if (class_.mData.mSkills[i][0]==skillIndex)\n        {\n            typeFactor = gmst.find (\"fMinorSkillBonus\")->mValue.getFloat();\n            break;\n        }\n        else if (class_.mData.mSkills[i][1]==skillIndex)\n        {\n            typeFactor = gmst.find (\"fMajorSkillBonus\")->mValue.getFloat();\n            break;\n        }\n    }\n\n    progressRequirement *= typeFactor;\n\n    if (typeFactor<=0)\n        throw std::runtime_error (\"invalid skill type factor\");\n\n    float specialisationFactor = 1;\n\n    const ESM::Skill *skill =\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find (skillIndex);\n    if (skill->mData.mSpecialization==class_.mData.mSpecialization)\n    {\n        specialisationFactor = gmst.find (\"fSpecialSkillBonus\")->mValue.getFloat();\n\n        if (specialisationFactor<=0)\n            throw std::runtime_error (\"invalid skill specialisation factor\");\n    }\n    progressRequirement *= specialisationFactor;\n\n    return progressRequirement;\n}\n\nvoid MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType, float extraFactor)\n{\n    const ESM::Skill *skill =\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find (skillIndex);\n    float skillGain = 1;\n    if (usageType>=4)\n        throw std::runtime_error (\"skill usage type out of range\");\n    if (usageType>=0)\n    {\n        skillGain = skill->mData.mUseValue[usageType];\n        if (skillGain<0)\n            throw std::runtime_error (\"invalid skill gain factor\");\n    }\n    skillGain *= extraFactor;\n\n    MWMechanics::SkillValue& value = getSkill (skillIndex);\n\n    value.setProgress(value.getProgress() + skillGain);\n\n    if (int(value.getProgress())>=int(getSkillProgressRequirement(skillIndex, class_)))\n    {\n        // skill levelled up\n        increaseSkill(skillIndex, class_, false);\n    }\n}\n\nvoid MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook)\n{\n    float base = getSkill (skillIndex).getBase();\n\n    if (base >= 100.f)\n        return;\n\n    base += 1;\n\n    const MWWorld::Store<ESM::GameSetting> &gmst =\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n    // is this a minor or major skill?\n    int increase = gmst.find(\"iLevelupMiscMultAttriubte\")->mValue.getInteger(); // Note: GMST has a typo\n    for (int k=0; k<5; ++k)\n    {\n        if (class_.mData.mSkills[k][0] == skillIndex)\n        {\n            mLevelProgress += gmst.find(\"iLevelUpMinorMult\")->mValue.getInteger();\n            increase = gmst.find(\"iLevelUpMinorMultAttribute\")->mValue.getInteger();\n            break;\n        }\n        else if (class_.mData.mSkills[k][1] == skillIndex)\n        {\n            mLevelProgress += gmst.find(\"iLevelUpMajorMult\")->mValue.getInteger();\n            increase = gmst.find(\"iLevelUpMajorMultAttribute\")->mValue.getInteger();\n            break;\n        }\n    }\n\n    const ESM::Skill* skill =\n        MWBase::Environment::get().getWorld ()->getStore ().get<ESM::Skill>().find(skillIndex);\n    mSkillIncreases[skill->mData.mAttribute] += increase;\n\n    mSpecIncreases[skill->mData.mSpecialization] += gmst.find(\"iLevelupSpecialization\")->mValue.getInteger();\n\n    // Play sound & skill progress notification\n    /// \\todo check if character is the player, if levelling is ever implemented for NPCs\n    MWBase::Environment::get().getWindowManager()->playSound(\"skillraise\");\n\n    std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString (\"sNotifyMessage39\", \"\");\n    message = Misc::StringUtils::format(message, (\"#{\" + ESM::Skill::sSkillNameIds[skillIndex] + \"}\"), static_cast<int>(base));\n\n    if (readBook)\n        message = \"#{sBookSkillMessage}\\n\" + message;\n    \n    MWBase::Environment::get().getWindowManager ()->messageBox(message, MWGui::ShowInDialogueMode_Never);\n\n    if (mLevelProgress >= gmst.find(\"iLevelUpTotal\")->mValue.getInteger())\n    {\n        // levelup is possible now\n        MWBase::Environment::get().getWindowManager ()->messageBox (\"#{sLevelUpMsg}\", MWGui::ShowInDialogueMode_Never);\n    }\n\n    getSkill(skillIndex).setBase (base);\n    if (!preserveProgress)\n        getSkill(skillIndex).setProgress(0);\n}\n\nint MWMechanics::NpcStats::getLevelProgress () const\n{\n    return mLevelProgress;\n}\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to set a player's level progress directly instead of going\n    through other methods\n*/\nvoid MWMechanics::NpcStats::setLevelProgress(int value)\n{\n    mLevelProgress = value;\n}\n/*\n    End of tes3mp addition\n*/\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to get a player's skill increases for an attribute directly\n    instead of going through other methods\n*/\nint MWMechanics::NpcStats::getSkillIncrease(int attribute) const\n{\n    return mSkillIncreases[attribute];\n}\n/*\n    End of tes3mp addition\n*/\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to set a player's skill increases for an attribute directly\n    instead of going through other methods\n*/\nvoid MWMechanics::NpcStats::setSkillIncrease(int attribute, int value)\n{\n    mSkillIncreases[attribute] = value;\n}\n/*\n    End of tes3mp addition\n*/\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to get and set the time of the last crime witnessed by the NPC,\n    used to stop combat with a player after that player dies and is resurrected\n*/\nstd::time_t MWMechanics::NpcStats::getCrimeTime()\n{\n    return mCrimeTime;\n}\n\nvoid MWMechanics::NpcStats::setCrimeTime(std::time_t crimeTime)\n{\n    mCrimeTime = crimeTime;\n}\n/*\n    End of tes3mp addition\n*/\n\nvoid MWMechanics::NpcStats::levelUp()\n{\n    const MWWorld::Store<ESM::GameSetting> &gmst =\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n    mLevelProgress -= gmst.find(\"iLevelUpTotal\")->mValue.getInteger();\n    mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console\n\n    for (int i=0; i<ESM::Attribute::Length; ++i)\n        mSkillIncreases[i] = 0;\n\n    const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();\n\n    // \"When you gain a level, in addition to increasing three primary attributes, your Health\n    // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,\n    // the Health increase is calculated from the increased Endurance\"\n    // Note: we should add bonus Health points to current level too.\n    float healthGain = endurance * gmst.find(\"fLevelUpHealthEndMult\")->mValue.getFloat();\n    MWMechanics::DynamicStat<float> health(getHealth());\n    health.setBase(getHealth().getBase() + healthGain);\n    health.setCurrent(std::max(1.f, getHealth().getCurrent() + healthGain));\n    setHealth(health);\n\n    setLevel(getLevel()+1);\n}\n\nvoid MWMechanics::NpcStats::updateHealth()\n{\n    const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();\n    const float strength = getAttribute(ESM::Attribute::Strength).getBase();\n\n    setHealth(floor(0.5f * (strength + endurance)));\n}\n\nint MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const\n{\n    int num = mSkillIncreases[attribute];\n\n    if (num == 0)\n        return 1;\n\n    num = std::min(10, num);\n\n    // iLevelUp01Mult - iLevelUp10Mult\n    std::stringstream gmst;\n    gmst << \"iLevelUp\" << std::setfill('0') << std::setw(2) << num << \"Mult\";\n\n    return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(gmst.str())->mValue.getInteger();\n}\n\nint MWMechanics::NpcStats::getSkillIncreasesForSpecialization(int spec) const\n{\n    return mSpecIncreases[spec];\n}\n\nvoid MWMechanics::NpcStats::flagAsUsed (const std::string& id)\n{\n    mUsedIds.insert (id);\n}\n\nbool MWMechanics::NpcStats::hasBeenUsed (const std::string& id) const\n{\n    return mUsedIds.find (id)!=mUsedIds.end();\n}\n\nint MWMechanics::NpcStats::getBounty() const\n{\n    return mBounty;\n}\n\nvoid MWMechanics::NpcStats::setBounty (int bounty)\n{\n    mBounty = bounty;\n}\n\nint MWMechanics::NpcStats::getReputation() const\n{\n    return mReputation;\n}\n\nvoid MWMechanics::NpcStats::setReputation(int reputation)\n{\n    // Reputation is capped in original engine\n    mReputation = std::min(255, std::max(0, reputation));\n}\n\nint MWMechanics::NpcStats::getCrimeId() const\n{\n    return mCrimeId;\n}\n\nvoid MWMechanics::NpcStats::setCrimeId(int id)\n{\n    mCrimeId = id;\n\n    /*\n        Start of tes3mp addition\n\n        Record this as the time of the last crime witnessed by this NPC\n    */\n    if (id != -1)\n        setCrimeTime(time(0));\n    /*\n        End of tes3mp addition\n    */\n}\n\nbool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int rank) const\n{\n    if (rank<0 || rank>=10)\n        throw std::runtime_error (\"rank index out of range\");\n\n    const ESM::Faction& faction =\n        *MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find (factionId);\n\n    std::vector<int> skills;\n\n    for (int i=0; i<7; ++i)\n    {\n        if (faction.mData.mSkills[i] != -1)\n            skills.push_back (static_cast<int> (getSkill (faction.mData.mSkills[i]).getBase()));\n    }\n\n    if (skills.empty())\n        return true;\n\n    std::sort (skills.begin(), skills.end());\n\n    std::vector<int>::const_reverse_iterator iter = skills.rbegin();\n\n    const ESM::RankData& rankData = faction.mData.mRankData[rank];\n\n    if (*iter<rankData.mPrimarySkill)\n        return false;\n\n    if (skills.size() < 2)\n        return true;\n\n    iter++;\n    if (*iter<rankData.mFavouredSkill)\n        return false;\n\n    if (skills.size() < 3)\n        return true;\n\n    iter++;\n    if (*iter<rankData.mFavouredSkill)\n        return false;\n\n    return true;\n}\n\nbool MWMechanics::NpcStats::isWerewolf() const\n{\n    return mIsWerewolf;\n}\n\nvoid MWMechanics::NpcStats::setWerewolf (bool set)\n{\n    if (mIsWerewolf == set)\n        return;\n\n    if(set != false)\n    {\n        mWerewolfKills = 0;\n    }\n    mIsWerewolf = set;\n}\n\nint MWMechanics::NpcStats::getWerewolfKills() const\n{\n    return mWerewolfKills;\n}\n\nvoid MWMechanics::NpcStats::addWerewolfKill()\n{\n    ++mWerewolfKills;\n}\n\nfloat MWMechanics::NpcStats::getTimeToStartDrowning() const\n{\n    return mTimeToStartDrowning;\n}\n\nvoid MWMechanics::NpcStats::setTimeToStartDrowning(float time)\n{\n    mTimeToStartDrowning=time;\n}\n\nvoid MWMechanics::NpcStats::writeState (ESM::CreatureStats& state) const\n{\n    CreatureStats::writeState(state);\n}\n\nvoid MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const\n{\n    for (std::map<std::string, int>::const_iterator iter (mFactionRank.begin());\n        iter!=mFactionRank.end(); ++iter)\n        state.mFactions[iter->first].mRank = iter->second;\n\n    state.mDisposition = mDisposition;\n\n    for (int i=0; i<ESM::Skill::Length; ++i)\n        mSkill[i].writeState (state.mSkills[i]);\n\n    state.mIsWerewolf = mIsWerewolf;\n\n    state.mCrimeId = mCrimeId;\n\n    state.mBounty = mBounty;\n\n    for (std::set<std::string>::const_iterator iter (mExpelled.begin());\n        iter!=mExpelled.end(); ++iter)\n        state.mFactions[*iter].mExpelled = true;\n\n    for (std::map<std::string, int>::const_iterator iter (mFactionReputation.begin());\n        iter!=mFactionReputation.end(); ++iter)\n        state.mFactions[iter->first].mReputation = iter->second;\n\n    state.mReputation = mReputation;\n    state.mWerewolfKills = mWerewolfKills;\n    state.mLevelProgress = mLevelProgress;\n\n    for (int i=0; i<ESM::Attribute::Length; ++i)\n        state.mSkillIncrease[i] = mSkillIncreases[i];\n\n    for (int i=0; i<3; ++i)\n        state.mSpecIncreases[i] = mSpecIncreases[i];\n\n    std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds));\n\n    state.mTimeToStartDrowning = mTimeToStartDrowning;\n}\nvoid MWMechanics::NpcStats::readState (const ESM::CreatureStats& state)\n{\n    CreatureStats::readState(state);\n}\n\nvoid MWMechanics::NpcStats::readState (const ESM::NpcStats& state)\n{\n    const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n\n    for (std::map<std::string, ESM::NpcStats::Faction>::const_iterator iter (state.mFactions.begin());\n        iter!=state.mFactions.end(); ++iter)\n        if (store.get<ESM::Faction>().search (iter->first))\n        {\n            if (iter->second.mExpelled)\n                mExpelled.insert (iter->first);\n\n            if (iter->second.mRank >= 0)\n                mFactionRank[iter->first] = iter->second.mRank;\n\n            if (iter->second.mReputation)\n                mFactionReputation[Misc::StringUtils::lowerCase(iter->first)] = iter->second.mReputation;\n        }\n\n    mDisposition = state.mDisposition;\n\n    for (int i=0; i<ESM::Skill::Length; ++i)\n        mSkill[i].readState (state.mSkills[i]);\n\n    mIsWerewolf = state.mIsWerewolf;\n\n    mCrimeId = state.mCrimeId;\n    mBounty = state.mBounty;\n    mReputation = state.mReputation;\n    mWerewolfKills = state.mWerewolfKills;\n    mLevelProgress = state.mLevelProgress;\n\n    for (int i=0; i<ESM::Attribute::Length; ++i)\n        mSkillIncreases[i] = state.mSkillIncrease[i];\n\n    for (int i=0; i<3; ++i)\n        mSpecIncreases[i] = state.mSpecIncreases[i];\n\n    for (std::vector<std::string>::const_iterator iter (state.mUsedIds.begin());\n        iter!=state.mUsedIds.end(); ++iter)\n        if (store.find (*iter))\n            mUsedIds.insert (*iter);\n\n    mTimeToStartDrowning = state.mTimeToStartDrowning;\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/npcstats.hpp",
    "content": "#ifndef GAME_MWMECHANICS_NPCSTATS_H\n#define GAME_MWMECHANICS_NPCSTATS_H\n\n#include <map>\n#include <set>\n#include <string>\n#include <vector>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <time.h>\n/*\n    End of tes3mp addition\n*/\n\n#include \"creaturestats.hpp\"\n\nnamespace ESM\n{\n    struct Class;\n    struct NpcStats;\n}\n\nnamespace MWMechanics\n{\n    /// \\brief Additional stats for NPCs\n\n    class NpcStats : public CreatureStats\n    {\n            int mDisposition;\n            SkillValue mSkill[ESM::Skill::Length]; // SkillValue.mProgress used by the player only\n\n            int mReputation;\n            int mCrimeId;\n\n            /*\n                Start of tes3mp addition\n\n                Add a variable used to track the time of the most recent crime by a player\n            */\n            time_t mCrimeTime = time(0);\n            /*\n                End of tes3mp addition\n            */\n\n            // ----- used by the player only, maybe should be moved at some point -------\n            int mBounty;\n            int mWerewolfKills;\n            /// Used only for the player and for NPC's with ranks, modified by scripts; other NPCs have maximum one faction defined in their NPC record\n            std::map<std::string, int> mFactionRank;\n            std::set<std::string> mExpelled;\n            std::map<std::string, int> mFactionReputation;\n            int mLevelProgress; // 0-10\n            std::vector<int> mSkillIncreases; // number of skill increases for each attribute (resets after leveling up)\n            std::vector<int> mSpecIncreases; // number of skill increases for each specialization (accumulates throughout the entire game)\n            std::set<std::string> mUsedIds;\n            // ---------------------------------------------------------------------------\n\n            /// Countdown to getting damage while underwater\n            float mTimeToStartDrowning;\n\n            bool mIsWerewolf;\n\n        public:\n\n            NpcStats();\n\n            int getBaseDisposition() const;\n            void setBaseDisposition(int disposition);\n\n            int getReputation() const;\n            void setReputation(int reputation);\n\n            int getCrimeId() const;\n            void setCrimeId(int id);\n\n            const SkillValue& getSkill (int index) const;\n            SkillValue& getSkill (int index);\n            void setSkill(int index, const SkillValue& value);\n\n            int getFactionRank(const std::string &faction) const;\n            const std::map<std::string, int>& getFactionRanks() const;\n\n            /// Increase the rank in this faction by 1, if such a rank exists.\n            void raiseRank(const std::string& faction);\n            /// Lower the rank in this faction by 1, if such a rank exists.\n            void lowerRank(const std::string& faction);\n            /// Join this faction, setting the initial rank to 0.\n            void joinFaction(const std::string& faction);\n\n            const std::set<std::string>& getExpelled() const { return mExpelled; }\n            bool getExpelled(const std::string& factionID) const;\n            void expell(const std::string& factionID);\n            void clearExpelled(const std::string& factionID);\n\n            bool isInFaction (const std::string& faction) const;\n\n            float getSkillProgressRequirement (int skillIndex, const ESM::Class& class_) const;\n\n            void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1, float extraFactor=1.f);\n            ///< Increase skill by usage.\n\n            void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook = false);\n\n            int getLevelProgress() const;\n\n            /*\n                Start of tes3mp addition\n\n                Useful methods for setting player stats\n            */\n            void setLevelProgress(int value);\n            int getSkillIncrease(int attribute) const;\n            void setSkillIncrease(int attribute, int value);\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get and set the time of the last crime witnessed by the NPC,\n                used to stop combat with a player after that player dies and is resurrected\n            */\n            time_t getCrimeTime();\n            void setCrimeTime(time_t crimeTime);\n            /*\n                End of tes3mp addition\n            */\n\n            int getLevelupAttributeMultiplier(int attribute) const;\n\n            int getSkillIncreasesForSpecialization(int spec) const;\n\n            void levelUp();\n\n            void updateHealth();\n            ///< Calculate health based on endurance and strength.\n            ///  Called at character creation.\n\n            void flagAsUsed (const std::string& id);\n            ///< @note Id must be lower-case\n\n            bool hasBeenUsed (const std::string& id) const;\n            ///< @note Id must be lower-case\n\n            int getBounty() const;\n\n            void setBounty (int bounty);\n\n            int getFactionReputation (const std::string& faction) const;\n\n            void setFactionReputation (const std::string& faction, int value);\n\n            bool hasSkillsForRank (const std::string& factionId, int rank) const;\n\n            bool isWerewolf() const;\n\n            void setWerewolf(bool set);\n\n            int getWerewolfKills() const;\n\n            /// Increments mWerewolfKills by 1.\n            void addWerewolfKill();\n\n            float getTimeToStartDrowning() const;\n            /// Sets time left for the creature to drown if it stays underwater.\n            /// @param time value from [0,20]\n            void setTimeToStartDrowning(float time);\n\n            void writeState (ESM::CreatureStats& state) const;\n            void writeState (ESM::NpcStats& state) const;\n\n            void readState (const ESM::CreatureStats& state);\n            void readState (const ESM::NpcStats& state);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/objects.cpp",
    "content": "#include \"objects.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/loadcont.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"character.hpp\"\n#include \"movement.hpp\"\n\nnamespace MWMechanics\n{\n\nObjects::Objects()\n{\n}\n\nObjects::~Objects()\n{\n  for(auto& object : mObjects)\n  {\n    delete object.second;\n    object.second = nullptr;\n  }\n}\n\nvoid Objects::addObject(const MWWorld::Ptr& ptr)\n{\n    removeObject(ptr);\n\n    MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);\n    if(anim) mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim)));\n}\n\nvoid Objects::removeObject(const MWWorld::Ptr& ptr)\n{\n    PtrControllerMap::iterator iter = mObjects.find(ptr);\n    if(iter != mObjects.end())\n    {\n        delete iter->second;\n        mObjects.erase(iter);\n    }\n}\n\nvoid Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)\n{\n    PtrControllerMap::iterator iter = mObjects.find(old);\n    if(iter != mObjects.end())\n    {\n        CharacterController *ctrl = iter->second;\n        mObjects.erase(iter);\n\n        ctrl->updatePtr(ptr);\n        mObjects.insert(std::make_pair(ptr, ctrl));\n    }\n}\n\nvoid Objects::dropObjects (const MWWorld::CellStore *cellStore)\n{\n    PtrControllerMap::iterator iter = mObjects.begin();\n    while(iter != mObjects.end())\n    {\n        if(iter->first.getCell()==cellStore)\n        {\n            delete iter->second;\n            mObjects.erase(iter++);\n        }\n        else\n            ++iter;\n    }\n}\n\nvoid Objects::update(float duration, bool paused)\n{\n    if(!paused)\n    {\n        for(auto& object : mObjects)\n            object.second->update(duration);\n    }\n    else\n    {\n        // We still should play container opening animation in the Container GUI mode.\n        MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();\n        if(mode != MWGui::GM_Container)\n            return;\n\n        for(auto& object : mObjects)\n        {\n            if (object.first.getTypeName() != typeid(ESM::Container).name())\n                continue;\n\n            if (object.second->isAnimPlaying(\"containeropen\"))\n            {\n                object.second->update(duration);\n                MWBase::Environment::get().getWorld()->updateAnimatedCollisionShape(object.first);\n            }\n        }\n    }\n}\n\nbool Objects::onOpen(const MWWorld::Ptr& ptr)\n{\n    PtrControllerMap::iterator iter = mObjects.find(ptr);\n    if(iter != mObjects.end())\n        return iter->second->onOpen();\n    return true;\n}\n\nvoid Objects::onClose(const MWWorld::Ptr& ptr)\n{\n    PtrControllerMap::iterator iter = mObjects.find(ptr);\n    if(iter != mObjects.end())\n        iter->second->onClose();\n}\n\nbool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)\n{\n    PtrControllerMap::iterator iter = mObjects.find(ptr);\n    if(iter != mObjects.end())\n    {\n        return iter->second->playGroup(groupName, mode, number, persist);\n    }\n    else\n    {\n        Log(Debug::Warning) << \"Warning: Objects::playAnimationGroup: Unable to find \" << ptr.getCellRef().getRefId();\n        return false;\n    }\n}\nvoid Objects::skipAnimation(const MWWorld::Ptr& ptr)\n{\n    PtrControllerMap::iterator iter = mObjects.find(ptr);\n    if(iter != mObjects.end())\n        iter->second->skipAnim();\n}\n\nvoid Objects::persistAnimationStates()\n{\n    for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter)\n        iter->second->persistAnimationState();\n}\n\nvoid Objects::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out)\n{\n    for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter)\n    {\n        if ((position - iter->first.getRefData().getPosition().asVec3()).length2() <= radius*radius)\n            out.push_back(iter->first);\n    }\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/objects.hpp",
    "content": "#ifndef GAME_MWMECHANICS_ACTIVATORS_H\n#define GAME_MWMECHANICS_ACTIVATORS_H\n\n#include <string>\n#include <map>\n#include <vector>\n\nnamespace osg\n{\n    class Vec3f;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n    class CellStore;\n}\n\nnamespace MWMechanics\n{\n    class CharacterController;\n\n    class Objects\n    {\n        typedef std::map<MWWorld::Ptr,CharacterController*> PtrControllerMap;\n        PtrControllerMap mObjects;\n\n    public:\n        Objects();\n        ~Objects();\n\n        void addObject (const MWWorld::Ptr& ptr);\n        ///< Register an animated object\n\n        void removeObject (const MWWorld::Ptr& ptr);\n        ///< Deregister an object\n\n        void updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);\n        ///< Updates an object with a new Ptr\n\n        void dropObjects(const MWWorld::CellStore *cellStore);\n        ///< Deregister all objects in the given cell.\n\n        void update(float duration, bool paused);\n        ///< Update object animations\n\n        bool onOpen(const MWWorld::Ptr& ptr);\n        void onClose(const MWWorld::Ptr& ptr);\n\n        bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false);\n        void skipAnimation(const MWWorld::Ptr& ptr);\n        void persistAnimationStates();\n\n        void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out);\n\n        std::size_t size() const\n        {\n            return mObjects.size();\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/obstacle.cpp",
    "content": "#include \"obstacle.hpp\"\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"movement.hpp\"\n\nnamespace MWMechanics\n{\n    // NOTE: determined empirically but probably need further tweaking\n    static const float DIST_SAME_SPOT = 0.5f;\n    static const float DURATION_SAME_SPOT = 1.5f;\n    static const float DURATION_TO_EVADE = 0.4f;\n\n    const float ObstacleCheck::evadeDirections[NUM_EVADE_DIRECTIONS][2] =\n    {\n        { 1.0f, 0.0f },     // move to side\n        { 1.0f, -1.0f },    // move to side and backwards\n        { -1.0f, 0.0f },    // move to other side\n        { -1.0f, -1.0f }    // move to side and backwards\n    };\n\n    bool proximityToDoor(const MWWorld::Ptr& actor, float minDist)\n    {\n        if(getNearbyDoor(actor, minDist).isEmpty())\n            return false;\n        else\n            return true;\n    }\n\n    const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist)\n    {\n        MWWorld::CellStore *cell = actor.getCell();\n\n        // Check all the doors in this cell\n        const MWWorld::CellRefList<ESM::Door>& doors = cell->getReadOnlyDoors();\n        osg::Vec3f pos(actor.getRefData().getPosition().asVec3());\n        pos.z() = 0;\n\n        osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0));\n\n        for (const auto& ref : doors.mList)\n        {\n            osg::Vec3f doorPos(ref.mData.getPosition().asVec3());\n\n            // FIXME: cast\n            const MWWorld::Ptr doorPtr = MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell());\n\n            const auto doorState = doorPtr.getClass().getDoorState(doorPtr);\n            float doorRot = ref.mData.getPosition().rot[2] - doorPtr.getCellRef().getPosition().rot[2];\n\n            if (doorState != MWWorld::DoorState::Idle || doorRot != 0)\n                continue; // the door is already opened/opening\n\n            doorPos.z() = 0;\n\n            float angle = std::acos(actorDir * (doorPos - pos) / (actorDir.length() * (doorPos - pos).length()));\n\n            // Allow 60 degrees angle between actor and door\n            if (angle < -osg::PI / 3 || angle > osg::PI / 3)\n                continue;\n\n            // Door is not close enough\n            if ((pos - doorPos).length2() > minDist*minDist)\n                continue;\n\n            return doorPtr; // found, stop searching\n        }\n\n        return MWWorld::Ptr(); // none found\n    }\n\n    ObstacleCheck::ObstacleCheck()\n      : mWalkState(WalkState::Initial)\n      , mStateDuration(0)\n      , mEvadeDirectionIndex(0)\n    {\n    }\n\n    void ObstacleCheck::clear()\n    {\n        mWalkState = WalkState::Initial;\n    }\n\n    bool ObstacleCheck::isEvading() const\n    {\n        return mWalkState == WalkState::Evade;\n    }\n\n    /*\n     * input   - actor, duration (time since last check)\n     * output  - true if evasive action needs to be taken\n     *\n     * Walking state transitions (player greeting check not shown):\n     *\n     * Initial ----> Norm  <--------> CheckStuck -------> Evade ---+\n     *               ^ ^ | f             ^   |       t    ^   |    |\n     *               | | |               |   |            |   |    |\n     *               | +-+               +---+            +---+    | u\n     *               | any                < t              < u     |\n     *               +---------------------------------------------+\n     *\n     * f = one reaction time\n     * t = how long before considered stuck\n     * u = how long to move sideways\n     *\n     */\n    void ObstacleCheck::update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration)\n    {\n        const auto position = actor.getRefData().getPosition().asVec3();\n\n        if (mWalkState == WalkState::Initial)\n        {\n            mWalkState = WalkState::Norm;\n            mStateDuration = 0;\n            mPrev = position;\n            mInitialDistance = (destination - position).length();\n            return;\n        }\n\n        if (mWalkState != WalkState::Evade)\n        {\n            const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getCurrentSpeed(actor) * duration;\n            const float prevDistance = (destination - mPrev).length();\n            const float currentDistance = (destination - position).length();\n            const float movedDistance = prevDistance - currentDistance;\n            const float movedFromInitialDistance = mInitialDistance - currentDistance;\n\n            mPrev = position;\n\n            if (movedDistance >= distSameSpot && movedFromInitialDistance >= distSameSpot)\n            {\n                mWalkState = WalkState::Norm;\n                mStateDuration = 0;\n                return;\n            }\n\n            if (mWalkState == WalkState::Norm)\n            {\n                mWalkState = WalkState::CheckStuck;\n                mStateDuration = duration;\n                mInitialDistance = (destination - position).length();\n                return;\n            }\n\n            mStateDuration += duration;\n            if (mStateDuration < DURATION_SAME_SPOT)\n            {\n                return;\n            }\n\n            mWalkState = WalkState::Evade;\n            mStateDuration = 0;\n            chooseEvasionDirection();\n            return;\n        }\n\n        mStateDuration += duration;\n        if(mStateDuration >= DURATION_TO_EVADE)\n        {\n            // tried to evade, assume all is ok and start again\n            mWalkState = WalkState::Norm;\n            mStateDuration = 0;\n            mPrev = position;\n        }\n    }\n\n    void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement) const\n    {\n        actorMovement.mPosition[0] = evadeDirections[mEvadeDirectionIndex][0];\n        actorMovement.mPosition[1] = evadeDirections[mEvadeDirectionIndex][1];\n    }\n\n    void ObstacleCheck::chooseEvasionDirection()\n    {\n        // change direction if attempt didn't work\n        ++mEvadeDirectionIndex;\n        if (mEvadeDirectionIndex == NUM_EVADE_DIRECTIONS)\n        {\n            mEvadeDirectionIndex = 0;\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/obstacle.hpp",
    "content": "#ifndef OPENMW_MECHANICS_OBSTACLE_H\n#define OPENMW_MECHANICS_OBSTACLE_H\n\n#include <osg/Vec3f>\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    struct Movement;\n\n    static constexpr int NUM_EVADE_DIRECTIONS = 4;\n\n    /// tests actor's proximity to a closed door by default\n    bool proximityToDoor(const MWWorld::Ptr& actor, float minDist);\n\n    /// Returns door pointer within range. No guarantee is given as to which one\n    /** \\return Pointer to the door, or empty pointer if none exists **/\n    const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist);\n\n    class ObstacleCheck\n    {\n        public:\n            ObstacleCheck();\n\n            // Clear the timers and set the state machine to default\n            void clear();\n\n            bool isEvading() const;\n\n            // Updates internal state, call each frame for moving actor\n            void update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration);\n\n            // change direction to try to fix \"stuck\" actor\n            void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;\n\n        private:\n            osg::Vec3f mPrev;\n\n            // directions to try moving in when get stuck\n            static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];\n\n            enum class WalkState\n            {\n                Initial,\n                Norm,\n                CheckStuck,\n                Evade\n            };\n            WalkState mWalkState;\n\n            float mStateDuration;\n            int mEvadeDirectionIndex;\n            float mInitialDistance = 0;\n\n            void chooseEvasionDirection();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/pathfinding.cpp",
    "content": "#include \"pathfinding.hpp\"\n\n#include <iterator>\n#include <limits>\n\n#include <components/detournavigator/debug.hpp>\n#include <components/detournavigator/navigator.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/misc/coordinateconverter.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwphysics/collisiontype.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"pathgrid.hpp\"\n#include \"actorutil.hpp\"\n\nnamespace\n{\n    // Chooses a reachable end pathgrid point.  start is assumed reachable.\n    std::pair<int, bool> getClosestReachablePoint(const ESM::Pathgrid* grid,\n                                                  const MWMechanics::PathgridGraph *graph,\n                                                  const osg::Vec3f& pos, int start)\n    {\n        assert(grid && !grid->mPoints.empty());\n\n        float closestDistanceBetween = std::numeric_limits<float>::max();\n        float closestDistanceReachable = std::numeric_limits<float>::max();\n        int closestIndex = 0;\n        int closestReachableIndex = 0;\n        // TODO: if this full scan causes performance problems mapping pathgrid\n        //       points to a quadtree may help\n        for(unsigned int counter = 0; counter < grid->mPoints.size(); counter++)\n        {\n            float potentialDistBetween = MWMechanics::PathFinder::distanceSquared(grid->mPoints[counter], pos);\n            if (potentialDistBetween < closestDistanceReachable)\n            {\n                // found a closer one\n                if (graph->isPointConnected(start, counter))\n                {\n                    closestDistanceReachable = potentialDistBetween;\n                    closestReachableIndex = counter;\n                }\n                if (potentialDistBetween < closestDistanceBetween)\n                {\n                    closestDistanceBetween = potentialDistBetween;\n                    closestIndex = counter;\n                }\n            }\n        }\n\n        // post-condition: start and endpoint must be connected\n        assert(graph->isPointConnected(start, closestReachableIndex));\n\n        // AiWander has logic that depends on whether a path was created, deleting\n        // allowed nodes if not.  Hence a path needs to be created even if the start\n        // and the end points are the same.\n\n        return std::pair<int, bool>\n            (closestReachableIndex, closestReachableIndex == closestIndex);\n    }\n\n    float sqrDistance(const osg::Vec2f& lhs, const osg::Vec2f& rhs)\n    {\n        return (lhs - rhs).length2();\n    }\n\n    float sqrDistanceIgnoreZ(const osg::Vec3f& lhs, const osg::Vec3f& rhs)\n    {\n        return sqrDistance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y()));\n    }\n\n    float getPathStepSize(const MWWorld::ConstPtr& actor)\n    {\n        const auto world = MWBase::Environment::get().getWorld();\n        const auto realHalfExtents = world->getHalfExtents(actor);\n        return 2 * std::max(realHalfExtents.x(), realHalfExtents.y());\n    }\n\n    float getHeight(const MWWorld::ConstPtr& actor)\n    {\n        const auto world = MWBase::Environment::get().getWorld();\n        const auto halfExtents = world->getHalfExtents(actor);\n        return 2.0 * halfExtents.z();\n    }\n\n    // Returns true if turn in `p2` is less than 10 degrees and all the 3 points are almost on one line.\n    bool isAlmostStraight(const osg::Vec3f& p1, const osg::Vec3f& p2, const osg::Vec3f& p3, float pointTolerance) {\n        osg::Vec3f v1 = p1 - p2;\n        osg::Vec3f v3 = p3 - p2;\n        v1.z() = v3.z() = 0;\n        float dotProduct = v1.x() * v3.x() + v1.y() * v3.y();\n        float crossProduct = v1.x() * v3.y() - v1.y() * v3.x();\n\n        // Check that the angle between v1 and v3 is less or equal than 5 degrees.\n        static const float cos175 = std::cos(osg::PI * (175.0 / 180));\n        bool checkAngle = dotProduct <= cos175 * v1.length() * v3.length();\n\n        // Check that distance from p2 to the line (p1, p3) is less or equal than `pointTolerance`.\n        bool checkDist = std::abs(crossProduct) <= pointTolerance * (p3 - p1).length();\n\n        return checkAngle && checkDist;\n    }\n\n    struct IsValidShortcut\n    {\n        const DetourNavigator::Navigator* mNavigator;\n        const osg::Vec3f mHalfExtents;\n        const DetourNavigator::Flags mFlags;\n\n        bool operator()(const osg::Vec3f& start, const osg::Vec3f& end) const\n        {\n            const auto position = mNavigator->raycast(mHalfExtents, start, end, mFlags);\n            return position.has_value() && std::abs((position.value() - start).length2() - (end - start).length2()) <= 1;\n        }\n    };\n}\n\nnamespace MWMechanics\n{\n    float getPathDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs)\n    {\n        if (std::abs(lhs.z() - rhs.z()) > getHeight(actor) || canActorMoveByZAxis(actor))\n            return distance(lhs, rhs);\n        return distanceIgnoreZ(lhs, rhs);\n    }\n\n    bool checkWayIsClear(const osg::Vec3f& from, const osg::Vec3f& to, float offsetXY)\n    {\n        osg::Vec3f dir = to - from;\n        dir.z() = 0;\n        dir.normalize();\n        float verticalOffset = 200; // instead of '200' here we want the height of the actor\n        osg::Vec3f _from = from + dir*offsetXY + osg::Z_AXIS * verticalOffset;\n\n        // cast up-down ray and find height of hit in world space\n        float h = _from.z() - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -osg::Z_AXIS, verticalOffset + PATHFIND_Z_REACH + 1);\n\n        return (std::abs(from.z() - h) <= PATHFIND_Z_REACH);\n    }\n\n    /*\n     * NOTE: This method may fail to find a path.  The caller must check the\n     * result before using it.  If there is no path the AI routies need to\n     * implement some other heuristics to reach the target.\n     *\n     * NOTE: It may be desirable to simply go directly to the endPoint if for\n     *       example there are no pathgrids in this cell.\n     *\n     * NOTE: startPoint & endPoint are in world coordinates\n     *\n     * Updates mPath using aStarSearch() or ray test (if shortcut allowed).\n     * mPath consists of pathgrid points, except the last element which is\n     * endPoint.  This may be useful where the endPoint is not on a pathgrid\n     * point (e.g. combat).  However, if the caller has already chosen a\n     * pathgrid point (e.g. wander) then it may be worth while to call\n     * pop_back() to remove the redundant entry.\n     *\n     * NOTE: coordinates must be converted prior to calling getClosestPoint()\n     *\n     *    |\n     *    |       cell\n     *    |     +-----------+\n     *    |     |           |\n     *    |     |           |\n     *    |     |      @    |\n     *    |  i  |   j       |\n     *    |<--->|<---->|    |\n     *    |     +-----------+\n     *    |   k\n     *    |<---------->|         world\n     *    +-----------------------------\n     *\n     *    i = x value of cell itself (multiply by ESM::Land::REAL_SIZE to convert)\n     *    j = @.x in local coordinates (i.e. within the cell)\n     *    k = @.x in world coordinates\n     */\n    void PathFinder::buildPathByPathgridImpl(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n        const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out)\n    {\n        const auto pathgrid = pathgridGraph.getPathgrid();\n\n        // Refer to AiWander reseach topic on openmw forums for some background.\n        // Maybe there is no pathgrid for this cell.  Just go to destination and let\n        // physics take care of any blockages.\n        if(!pathgrid || pathgrid->mPoints.empty())\n            return;\n\n        // NOTE: getClosestPoint expects local coordinates\n        Misc::CoordinateConverter converter(mCell->getCell());\n\n        // NOTE: It is possible that getClosestPoint returns a pathgrind point index\n        //       that is unreachable in some situations. e.g. actor is standing\n        //       outside an area enclosed by walls, but there is a pathgrid\n        //       point right behind the wall that is closer than any pathgrid\n        //       point outside the wall\n        osg::Vec3f startPointInLocalCoords(converter.toLocalVec3(startPoint));\n        int startNode = getClosestPoint(pathgrid, startPointInLocalCoords);\n\n        osg::Vec3f endPointInLocalCoords(converter.toLocalVec3(endPoint));\n        std::pair<int, bool> endNode = getClosestReachablePoint(pathgrid, &pathgridGraph,\n            endPointInLocalCoords,\n                startNode);\n\n        // if it's shorter for actor to travel from start to end, than to travel from either\n        // start or end to nearest pathgrid point, just travel from start to end.\n        float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2();\n        float endTolastNodeLength2 = distanceSquared(pathgrid->mPoints[endNode.first], endPointInLocalCoords);\n        float startTo1stNodeLength2 = distanceSquared(pathgrid->mPoints[startNode], startPointInLocalCoords);\n        if ((startToEndLength2 < startTo1stNodeLength2) || (startToEndLength2 < endTolastNodeLength2))\n        {\n            *out++ = endPoint;\n            return;\n        }\n\n        // AiWander has logic that depends on whether a path was created,\n        // deleting allowed nodes if not.  Hence a path needs to be created\n        // even if the start and the end points are the same.\n        // NOTE: aStarSearch will return an empty path if the start and end\n        //       nodes are the same\n        if(startNode == endNode.first)\n        {\n            ESM::Pathgrid::Point temp(pathgrid->mPoints[startNode]);\n            converter.toWorld(temp);\n            *out++ = makeOsgVec3(temp);\n        }\n        else\n        {\n            auto path = pathgridGraph.aStarSearch(startNode, endNode.first);\n\n            // If nearest path node is in opposite direction from second, remove it from path.\n            // Especially useful for wandering actors, if the nearest node is blocked for some reason.\n            if (path.size() > 1)\n            {\n                ESM::Pathgrid::Point secondNode = *(++path.begin());\n                osg::Vec3f firstNodeVec3f = makeOsgVec3(pathgrid->mPoints[startNode]);\n                osg::Vec3f secondNodeVec3f = makeOsgVec3(secondNode);\n                osg::Vec3f toSecondNodeVec3f = secondNodeVec3f - firstNodeVec3f;\n                osg::Vec3f toStartPointVec3f = startPointInLocalCoords - firstNodeVec3f;\n                if (toSecondNodeVec3f * toStartPointVec3f > 0)\n                {\n                    ESM::Pathgrid::Point temp(secondNode);\n                    converter.toWorld(temp);\n                    // Add Z offset since path node can overlap with other objects.\n                    // Also ignore doors in raytesting.\n                    const int mask = MWPhysics::CollisionType_World;\n                    bool isPathClear = !MWBase::Environment::get().getWorld()->castRay(\n                        startPoint.x(), startPoint.y(), startPoint.z() + 16, temp.mX, temp.mY, temp.mZ + 16, mask);\n                    if (isPathClear)\n                        path.pop_front();\n                }\n            }\n\n            // convert supplied path to world coordinates\n            std::transform(path.begin(), path.end(), out,\n                [&] (ESM::Pathgrid::Point& point)\n                {\n                    converter.toWorld(point);\n                    return makeOsgVec3(point);\n                });\n        }\n\n        // If endNode found is NOT the closest PathGrid point to the endPoint,\n        // assume endPoint is not reachable from endNode. In which case,\n        // path ends at endNode.\n        //\n        // So only add the destination (which may be different to the closest\n        // pathgrid point) when endNode was the closest point to endPoint.\n        //\n        // This logic can fail in the opposite situate, e.g. endPoint may\n        // have been reachable but happened to be very close to an\n        // unreachable pathgrid point.\n        //\n        // The AI routines will have to deal with such situations.\n        if (endNode.second)\n            *out++ = endPoint;\n    }\n\n    float PathFinder::getZAngleToNext(float x, float y) const\n    {\n        // This should never happen (programmers should have an if statement checking\n        // isPathConstructed that prevents this call if otherwise).\n        if(mPath.empty())\n            return 0.;\n\n        const auto& nextPoint = mPath.front();\n        const float directionX = nextPoint.x() - x;\n        const float directionY = nextPoint.y() - y;\n\n        return std::atan2(directionX, directionY);\n    }\n\n    float PathFinder::getXAngleToNext(float x, float y, float z) const\n    {\n        // This should never happen (programmers should have an if statement checking\n        // isPathConstructed that prevents this call if otherwise).\n        if(mPath.empty())\n            return 0.;\n\n        const osg::Vec3f dir = mPath.front() - osg::Vec3f(x, y, z);\n\n        return getXAngleToDir(dir);\n    }\n\n    void PathFinder::update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance,\n                            bool shortenIfAlmostStraight, bool canMoveByZ, const osg::Vec3f& halfExtents,\n                            const DetourNavigator::Flags flags)\n    {\n        if (mPath.empty())\n            return;\n\n        while (mPath.size() > 1 && sqrDistanceIgnoreZ(mPath.front(), position) < pointTolerance * pointTolerance)\n            mPath.pop_front();\n\n        if (shortenIfAlmostStraight)\n        {\n            const IsValidShortcut isValidShortcut {\n                MWBase::Environment::get().getWorld()->getNavigator(),\n                halfExtents, flags\n            };\n            while (mPath.size() > 2 && isAlmostStraight(mPath[0], mPath[1], mPath[2], pointTolerance)\n                   && isValidShortcut(mPath[0], mPath[2]))\n                mPath.erase(mPath.begin() + 1);\n            if (mPath.size() > 1 && isAlmostStraight(position, mPath[0], mPath[1], pointTolerance)\n                    && isValidShortcut(position, mPath[1]))\n                mPath.pop_front();\n        }\n\n        if (mPath.size() == 1)\n        {\n            float distSqr;\n            if (canMoveByZ)\n                distSqr = (mPath.front() - position).length2();\n            else\n                distSqr = sqrDistanceIgnoreZ(mPath.front(), position);\n            if (distSqr < destinationTolerance * destinationTolerance)\n                mPath.pop_front();\n        }\n    }\n\n    void PathFinder::buildStraightPath(const osg::Vec3f& endPoint)\n    {\n        mPath.clear();\n        mPath.push_back(endPoint);\n        mConstructed = true;\n    }\n\n    void PathFinder::buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n        const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph)\n    {\n        mPath.clear();\n        mCell = cell;\n\n        buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath));\n\n        mConstructed = !mPath.empty();\n    }\n\n    void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,\n        const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,\n        const DetourNavigator::AreaCosts& areaCosts)\n    {\n        mPath.clear();\n\n        // If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path\n        DetourNavigator::Status status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags,\n                                                                  areaCosts, std::back_inserter(mPath));\n\n        if (status != DetourNavigator::Status::Success)\n            mPath.clear();\n\n        if (status == DetourNavigator::Status::NavMeshNotFound)\n            mPath.push_back(endPoint);\n\n        mConstructed = !mPath.empty();\n    }\n\n    void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n        const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,\n        const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)\n    {\n        mPath.clear();\n        mCell = cell;\n\n        DetourNavigator::Status status = DetourNavigator::Status::NavMeshNotFound;\n\n        if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor))\n        {\n            status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath));\n            if (status != DetourNavigator::Status::Success)\n                mPath.clear();\n        }\n\n        if (status != DetourNavigator::Status::NavMeshNotFound && mPath.empty())\n        {\n            status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents,\n                                     flags | DetourNavigator::Flag_usePathgrid, areaCosts, std::back_inserter(mPath));\n            if (status != DetourNavigator::Status::Success)\n                mPath.clear();\n        }\n\n        if (mPath.empty())\n            buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath));\n\n        if (status == DetourNavigator::Status::NavMeshNotFound && mPath.empty())\n            mPath.push_back(endPoint);\n\n        mConstructed = !mPath.empty();\n    }\n\n    DetourNavigator::Status PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,\n        const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,\n        const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator<std::deque<osg::Vec3f>> out)\n    {\n        const auto world = MWBase::Environment::get().getWorld();\n        const auto stepSize = getPathStepSize(actor);\n        const auto navigator = world->getNavigator();\n        const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, areaCosts, out);\n\n        if (status != DetourNavigator::Status::Success)\n        {\n            Log(Debug::Debug) << \"Build path by navigator error: \\\"\" << DetourNavigator::getMessage(status)\n                << \"\\\" for \\\"\" << actor.getClass().getName(actor) << \"\\\" (\" << actor.getBase()\n                << \") from \" << startPoint << \" to \" << endPoint << \" with flags (\"\n                << DetourNavigator::WriteFlags {flags} << \")\";\n        }\n\n        return status;\n    }\n\n    void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,\n        const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)\n    {\n        if (mPath.empty())\n            return;\n\n        const auto stepSize = getPathStepSize(actor);\n        const auto startPoint = actor.getRefData().getPosition().asVec3();\n\n        if (sqrDistanceIgnoreZ(mPath.front(), startPoint) <= 4 * stepSize * stepSize)\n            return;\n\n        const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();\n        std::deque<osg::Vec3f> prePath;\n        auto prePathInserter = std::back_inserter(prePath);\n        const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, areaCosts,\n                                                prePathInserter);\n\n        if (status == DetourNavigator::Status::NavMeshNotFound)\n            return;\n\n        if (status != DetourNavigator::Status::Success)\n        {\n            Log(Debug::Debug) << \"Build path by navigator error: \\\"\" << DetourNavigator::getMessage(status)\n                << \"\\\" for \\\"\" << actor.getClass().getName(actor) << \"\\\" (\" << actor.getBase()\n                << \") from \" << startPoint << \" to \" << mPath.front() << \" with flags (\"\n                << DetourNavigator::WriteFlags {flags} << \")\";\n            return;\n        }\n\n        while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.front(), startPoint) < stepSize * stepSize)\n            prePath.pop_front();\n\n        while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.back(), mPath.front()) < stepSize * stepSize)\n            prePath.pop_back();\n\n        std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath));\n    }\n\n    void PathFinder::buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n        const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,\n        const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)\n    {\n        const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();\n        const auto maxDistance = std::min(\n            navigator->getMaxNavmeshAreaRealRadius(),\n            static_cast<float>(Constants::CellSizeInUnits)\n        );\n        const auto startToEnd = endPoint - startPoint;\n        const auto distance = startToEnd.length();\n        if (distance <= maxDistance)\n            return buildPath(actor, startPoint, endPoint, cell, pathgridGraph, halfExtents, flags, areaCosts);\n        const auto end = startPoint + startToEnd * maxDistance / distance;\n        buildPath(actor, startPoint, end, cell, pathgridGraph, halfExtents, flags, areaCosts);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/pathfinding.hpp",
    "content": "#ifndef GAME_MWMECHANICS_PATHFINDING_H\n#define GAME_MWMECHANICS_PATHFINDING_H\n\n#include <deque>\n#include <cassert>\n#include <iterator>\n\n#include <components/detournavigator/flags.hpp>\n#include <components/detournavigator/areatype.hpp>\n#include <components/detournavigator/status.hpp>\n#include <components/esm/defs.hpp>\n#include <components/esm/loadpgrd.hpp>\n\nnamespace MWWorld\n{\n    class CellStore;\n    class ConstPtr;\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    class PathgridGraph;\n\n    template <class T>\n    inline float distance(const T& lhs, const T& rhs)\n    {\n        static_assert(std::is_same<T, osg::Vec2f>::value\n                      || std::is_same<T, osg::Vec3f>::value,\n                      \"T is not a position\");\n        return (lhs - rhs).length();\n    }\n\n    inline float distanceIgnoreZ(const osg::Vec3f& lhs, const osg::Vec3f& rhs)\n    {\n        return distance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y()));\n    }\n\n    float getPathDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs);\n\n    inline float getZAngleToDir(const osg::Vec3f& dir)\n    {\n        return std::atan2(dir.x(), dir.y());\n    }\n\n    inline float getXAngleToDir(const osg::Vec3f& dir)\n    {\n        float dirLen = dir.length();\n        return (dirLen != 0) ? -std::asin(dir.z() / dirLen) : 0;\n    }\n\n    inline float getZAngleToPoint(const osg::Vec3f& origin, const osg::Vec3f& dest)\n    {\n        return getZAngleToDir(dest - origin);\n    }\n\n    inline float getXAngleToPoint(const osg::Vec3f& origin, const osg::Vec3f& dest)\n    {\n        return getXAngleToDir(dest - origin);\n    }\n\n    const float PATHFIND_Z_REACH = 50.0f;\n    // distance after which actor (failed previously to shortcut) will try again\n    const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f;\n\n    const float MIN_TOLERANCE = 1.0f;\n    const float DEFAULT_TOLERANCE = 32.0f;\n\n    // cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;\n    // magnitude of pits/obstacles is defined by PATHFIND_Z_REACH\n    bool checkWayIsClear(const osg::Vec3f& from, const osg::Vec3f& to, float offsetXY);\n\n    class PathFinder\n    {\n        public:\n            PathFinder()\n                : mConstructed(false)\n                , mCell(nullptr)\n            {\n            }\n\n            void clearPath()\n            {\n                mConstructed = false;\n                mPath.clear();\n                mCell = nullptr;\n            }\n\n            void buildStraightPath(const osg::Vec3f& endPoint);\n\n            void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n                const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);\n\n            void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,\n                const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,\n                const DetourNavigator::AreaCosts& areaCosts);\n\n            void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n                const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,\n                const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);\n\n            void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,\n                const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);\n\n            void buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n                const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,\n                const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);\n\n            /// Remove front point if exist and within tolerance\n            void update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance,\n                        bool shortenIfAlmostStraight, bool canMoveByZ, const osg::Vec3f& halfExtents,\n                        const DetourNavigator::Flags flags);\n\n            bool checkPathCompleted() const\n            {\n                return mConstructed && mPath.empty();\n            }\n\n            /// In radians\n            float getZAngleToNext(float x, float y) const;\n\n            float getXAngleToNext(float x, float y, float z) const;\n\n            bool isPathConstructed() const\n            {\n                return mConstructed && !mPath.empty();\n            }\n\n            std::size_t getPathSize() const\n            {\n                return mPath.size();\n            }\n\n            const std::deque<osg::Vec3f>& getPath() const\n            {\n                return mPath;\n            }\n\n            const MWWorld::CellStore* getPathCell() const\n            {\n                return mCell;\n            }\n\n            void addPointToPath(const osg::Vec3f& point)\n            {\n                mConstructed = true;\n                mPath.push_back(point);\n            }\n\n            /// utility function to convert a osg::Vec3f to a Pathgrid::Point\n            static ESM::Pathgrid::Point makePathgridPoint(const osg::Vec3f& v)\n            {\n                return ESM::Pathgrid::Point(static_cast<int>(v[0]), static_cast<int>(v[1]), static_cast<int>(v[2]));\n            }\n\n            /// utility function to convert an ESM::Position to a Pathgrid::Point\n            static ESM::Pathgrid::Point makePathgridPoint(const ESM::Position& p)\n            {\n                return ESM::Pathgrid::Point(static_cast<int>(p.pos[0]), static_cast<int>(p.pos[1]), static_cast<int>(p.pos[2]));\n            }\n\n            static osg::Vec3f makeOsgVec3(const ESM::Pathgrid::Point& p)\n            {\n                return osg::Vec3f(static_cast<float>(p.mX), static_cast<float>(p.mY), static_cast<float>(p.mZ));\n            }\n\n            // Slightly cheaper version for comparisons.\n            // Caller needs to be careful for very short distances (i.e. less than 1)\n            // or when accumuating the results i.e. (a + b)^2 != a^2 + b^2\n            //\n            static float distanceSquared(ESM::Pathgrid::Point point, const osg::Vec3f& pos)\n            {\n                return (MWMechanics::PathFinder::makeOsgVec3(point) - pos).length2();\n            }\n\n            // Return the closest pathgrid point index from the specified position\n            // coordinates.  NOTE: Does not check if there is a sensible way to get there\n            // (e.g. a cliff in front).\n            //\n            // NOTE: pos is expected to be in local coordinates, as is grid->mPoints\n            //\n            static int getClosestPoint(const ESM::Pathgrid* grid, const osg::Vec3f& pos)\n            {\n                assert(grid && !grid->mPoints.empty());\n\n                float distanceBetween = distanceSquared(grid->mPoints[0], pos);\n                int closestIndex = 0;\n\n                // TODO: if this full scan causes performance problems mapping pathgrid\n                //       points to a quadtree may help\n                for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++)\n                {\n                    float potentialDistBetween = distanceSquared(grid->mPoints[counter], pos);\n                    if(potentialDistBetween < distanceBetween)\n                    {\n                        distanceBetween = potentialDistBetween;\n                        closestIndex = counter;\n                    }\n                }\n\n                return closestIndex;\n            }\n\n        private:\n            bool mConstructed;\n            std::deque<osg::Vec3f> mPath;\n\n            const MWWorld::CellStore* mCell;\n\n            void buildPathByPathgridImpl(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,\n                const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out);\n\n            [[nodiscard]] DetourNavigator::Status buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor,\n                const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents,\n                const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts,\n                std::back_insert_iterator<std::deque<osg::Vec3f>> out);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/pathgrid.cpp",
    "content": "#include \"pathgrid.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace\n{\n    // See https://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html\n    //\n    // One of the smallest cost in Seyda Neen is between points 77 & 78:\n    // pt      x     y\n    // 77 = 8026, 4480\n    // 78 = 7986, 4218\n    //\n    // Euclidean distance is about 262 (ignoring z) and Manhattan distance is 300\n    // (again ignoring z).  Using a value of about 300 for D seems like a reasonable\n    // starting point for experiments. If in doubt, just use value 1.\n    //\n    // The distance between 3 & 4 are pretty small, too.\n    // 3 = 5435, 223\n    // 4 = 5948, 193\n    //\n    // Approx. 514 Euclidean distance and 533 Manhattan distance.\n    //\n    float manhattan(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b)\n    {\n        return 300.0f * (abs(a.mX - b.mX) + abs(a.mY - b.mY) + abs(a.mZ - b.mZ));\n    }\n\n    // Choose a heuristics - Note that these may not be the best for directed\n    // graphs with non-uniform edge costs.\n    //\n    //   distance:\n    //   - sqrt((curr.x - goal.x)^2 + (curr.y - goal.y)^2 + (curr.z - goal.z)^2)\n    //   - slower but more accurate\n    //\n    //   Manhattan:\n    //   - |curr.x - goal.x| + |curr.y - goal.y| + |curr.z - goal.z|\n    //   - faster but not the shortest path\n    float costAStar(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b)\n    {\n        //return distance(a, b);\n        return manhattan(a, b);\n    }\n}\n\nnamespace MWMechanics\n{\n    PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell)\n        : mCell(nullptr)\n        , mPathgrid(nullptr)\n        , mGraph(0)\n        , mIsGraphConstructed(false)\n        , mSCCId(0)\n        , mSCCIndex(0)\n    {\n        load(cell);\n    }\n\n    /*\n     * mGraph is populated with the cost of each allowed edge.\n     *\n     * The data structure is based on the code in buildPath2() but modified.\n     * Please check git history if interested.\n     *\n     * mGraph[v].edges[i].index = w\n     *\n     *   v = point index of location \"from\"\n     *   i = index of edges from point v\n     *   w = point index of location \"to\"\n     *\n     *\n     * Example: (notice from p(0) to p(2) is not allowed in this example)\n     *\n     *   mGraph[0].edges[0].index = 1\n     *            .edges[1].index = 3\n     *\n     *   mGraph[1].edges[0].index = 0\n     *            .edges[1].index = 2\n     *            .edges[2].index = 3\n     *\n     *   mGraph[2].edges[0].index = 1\n     *\n     *   (etc, etc)\n     *\n     *\n     *        low\n     *        cost\n     *   p(0) <---> p(1) <------------> p(2)\n     *    ^          ^\n     *    |          |\n     *    |          +-----> p(3)\n     *    +---------------->\n     *      high cost\n     */\n    bool PathgridGraph::load(const MWWorld::CellStore *cell)\n    {\n        if(!cell)\n            return false;\n\n        if(mIsGraphConstructed)\n            return true;\n\n        mCell = cell->getCell();\n        mPathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell->getCell());\n        if(!mPathgrid)\n            return false;\n\n\n        mGraph.resize(mPathgrid->mPoints.size());\n        for(int i = 0; i < static_cast<int> (mPathgrid->mEdges.size()); i++)\n        {\n            ConnectedPoint neighbour;\n            neighbour.cost = costAStar(mPathgrid->mPoints[mPathgrid->mEdges[i].mV0],\n                                       mPathgrid->mPoints[mPathgrid->mEdges[i].mV1]);\n            // forward path of the edge\n            neighbour.index = mPathgrid->mEdges[i].mV1;\n            mGraph[mPathgrid->mEdges[i].mV0].edges.push_back(neighbour);\n            // reverse path of the edge\n            // NOTE: These are redundant, ESM already contains the required reverse paths\n            //neighbour.index = mPathgrid->mEdges[i].mV0;\n            //mGraph[mPathgrid->mEdges[i].mV1].edges.push_back(neighbour);\n        }\n        buildConnectedPoints();\n        mIsGraphConstructed = true;\n        return true;\n    }\n\n    const ESM::Pathgrid *PathgridGraph::getPathgrid() const\n    {\n        return mPathgrid;\n    }\n\n    // v is the pathgrid point index (some call them vertices)\n    void PathgridGraph::recursiveStrongConnect(int v)\n    {\n        mSCCPoint[v].first = mSCCIndex;  // index\n        mSCCPoint[v].second = mSCCIndex; // lowlink\n        mSCCIndex++;\n        mSCCStack.push_back(v);\n        int w;\n\n        for(int i = 0; i < static_cast<int> (mGraph[v].edges.size()); i++)\n        {\n            w = mGraph[v].edges[i].index;\n            if(mSCCPoint[w].first == -1) // not visited\n            {\n                recursiveStrongConnect(w); // recurse\n                mSCCPoint[v].second = std::min(mSCCPoint[v].second,\n                                               mSCCPoint[w].second);\n            }\n            else\n            {\n                if(find(mSCCStack.begin(), mSCCStack.end(), w) != mSCCStack.end())\n                    mSCCPoint[v].second = std::min(mSCCPoint[v].second,\n                                                   mSCCPoint[w].first);\n            }\n        }\n\n        if(mSCCPoint[v].second == mSCCPoint[v].first)\n        {   // new component\n            do\n            {\n                w = mSCCStack.back();\n                mSCCStack.pop_back();\n                mGraph[w].componentId = mSCCId;\n            }\n            while(w != v);\n            mSCCId++;\n        }\n        return;\n    }\n\n    /*\n     * mGraph contains the strongly connected component group id's along\n     * with pre-calculated edge costs.\n     *\n     * A cell can have disjointed pathgrids, e.g. Seyda Neen has 3\n     *\n     * mGraph for Seyda Neen will therefore have 3 different values.  When\n     * selecting a random pathgrid point for AiWander, mGraph can be checked\n     * for quickly finding whether the destination is reachable.\n     *\n     * Otherwise, buildPath can automatically select a closest reachable end\n     * pathgrid point (reachable from the closest start point).\n     *\n     * Using Tarjan's algorithm:\n     *\n     *  mGraph                   | graph G   |\n     *  mSCCPoint                | V         | derived from mPoints\n     *  mGraph[v].edges          | E (for v) |\n     *  mSCCIndex                | index     | tracking smallest unused index\n     *  mSCCStack                | S         |\n     *  mGraph[v].edges[i].index | w         |\n     *\n     */\n    void PathgridGraph::buildConnectedPoints()\n    {\n        // both of these are set to zero in the constructor\n        //mSCCId = 0; // how many strongly connected components in this cell\n        //mSCCIndex = 0;\n        int pointsSize = static_cast<int> (mPathgrid->mPoints.size());\n        mSCCPoint.resize(pointsSize, std::pair<int, int> (-1, -1));\n        mSCCStack.reserve(pointsSize);\n\n        for(int v = 0; v < pointsSize; v++)\n        {\n            if(mSCCPoint[v].first == -1) // undefined (haven't visited)\n                recursiveStrongConnect(v);\n        }\n    }\n\n    bool PathgridGraph::isPointConnected(const int start, const int end) const\n    {\n        return (mGraph[start].componentId == mGraph[end].componentId);\n    }\n\n    void PathgridGraph::getNeighbouringPoints(const int index, ESM::Pathgrid::PointList &nodes) const\n    {\n        for(int i = 0; i < static_cast<int> (mGraph[index].edges.size()); i++)\n        {\n            int neighbourIndex = mGraph[index].edges[i].index;\n            if (neighbourIndex != index)\n                nodes.push_back(mPathgrid->mPoints[neighbourIndex]);\n        }\n    }\n\n    /*\n     * NOTE: Based on buildPath2(), please check git history if interested\n     *       Should consider using a 3rd party library version (e.g. boost)\n     *\n     * Find the shortest path to the target goal using a well known algorithm.\n     * Uses mGraph which has pre-computed costs for allowed edges.  It is assumed\n     * that mGraph is already constructed.\n     *\n     * Should be possible to make this MT safe.\n     *\n     * Returns path which may be empty.  path contains pathgrid points in local\n     * cell coordinates (indoors) or world coordinates (external).\n     *\n     * Input params:\n     *   start, goal - pathgrid point indexes (for this cell)\n     *\n     * Variables:\n     *   openset - point indexes to be traversed, lowest cost at the front\n     *   closedset - point indexes already traversed\n     *   gScore - past accumulated costs vector indexed by point index\n     *   fScore - future estimated costs vector indexed by point index\n     *\n     * TODO: An intersting exercise might be to cache the paths created for a\n     *       start/goal pair.  To cache the results the paths need to be in\n     *       pathgrid points form (currently they are converted to world\n     *       coordinates).  Essentially trading speed w/ memory.\n     */\n    std::deque<ESM::Pathgrid::Point> PathgridGraph::aStarSearch(const int start, const int goal) const\n    {\n        std::deque<ESM::Pathgrid::Point> path;\n        if(!isPointConnected(start, goal))\n        {\n            return path; // there is no path, return an empty path\n        }\n\n        int graphSize = static_cast<int> (mGraph.size());\n        std::vector<float> gScore (graphSize, -1);\n        std::vector<float> fScore (graphSize, -1);\n        std::vector<int> graphParent (graphSize, -1);\n\n        // gScore & fScore keep costs for each pathgrid point in mPoints\n        gScore[start] = 0;\n        fScore[start] = costAStar(mPathgrid->mPoints[start], mPathgrid->mPoints[goal]);\n\n        std::list<int> openset;\n        std::list<int> closedset;\n        openset.push_back(start);\n\n        int current = -1;\n\n        while(!openset.empty())\n        {\n            current = openset.front(); // front has the lowest cost\n            openset.pop_front();\n\n            if(current == goal)\n                break;\n\n            closedset.push_back(current); // remember we've been here\n\n            // check all edges for the current point index\n            for(int j = 0; j < static_cast<int> (mGraph[current].edges.size()); j++)\n            {\n                if(std::find(closedset.begin(), closedset.end(), mGraph[current].edges[j].index) ==\n                   closedset.end())\n                {\n                    // not in closedset - i.e. have not traversed this edge destination\n                    int dest = mGraph[current].edges[j].index;\n                    float tentative_g = gScore[current] + mGraph[current].edges[j].cost;\n                    bool isInOpenSet = std::find(openset.begin(), openset.end(), dest) != openset.end();\n                    if(!isInOpenSet\n                        || tentative_g < gScore[dest])\n                    {\n                        graphParent[dest] = current;\n                        gScore[dest] = tentative_g;\n                        fScore[dest] = tentative_g + costAStar(mPathgrid->mPoints[dest],\n                                                               mPathgrid->mPoints[goal]);\n                        if(!isInOpenSet)\n                        {\n                            // add this edge to openset, lowest cost goes to the front\n                            // TODO: if this causes performance problems a hash table may help\n                            std::list<int>::iterator it = openset.begin();\n                            for(it = openset.begin(); it!= openset.end(); ++it)\n                            {\n                                if(fScore[*it] > fScore[dest])\n                                    break;\n                            }\n                            openset.insert(it, dest);\n                        }\n                    }\n                } // if in closedset, i.e. traversed this edge already, try the next edge\n            }\n        }\n\n        if(current != goal)\n            return path; // for some reason couldn't build a path\n\n        // reconstruct path to return, using local coordinates\n        while(graphParent[current] != -1)\n        {\n            path.push_front(mPathgrid->mPoints[current]);\n            current = graphParent[current];\n        }\n\n        // add first node to path explicitly\n        path.push_front(mPathgrid->mPoints[start]);\n        return path;\n    }\n}\n\n"
  },
  {
    "path": "apps/openmw/mwmechanics/pathgrid.hpp",
    "content": "#ifndef GAME_MWMECHANICS_PATHGRID_H\n#define GAME_MWMECHANICS_PATHGRID_H\n\n#include <deque>\n\n#include <components/esm/loadpgrd.hpp>\n\nnamespace ESM\n{\n    struct Cell;\n}\n\nnamespace MWWorld\n{\n    class CellStore;\n}\n\nnamespace MWMechanics\n{\n    class PathgridGraph\n    {\n        public:\n            PathgridGraph(const MWWorld::CellStore* cell);\n\n            bool load(const MWWorld::CellStore *cell);\n\n            const ESM::Pathgrid* getPathgrid() const;\n\n            // returns true if end point is strongly connected (i.e. reachable\n            // from start point) both start and end are pathgrid point indexes\n            bool isPointConnected(const int start, const int end) const;\n\n            // get neighbouring nodes for index node and put them to \"nodes\" vector\n            void getNeighbouringPoints(const int index, ESM::Pathgrid::PointList &nodes) const;\n\n            // the input parameters are pathgrid point indexes\n            // the output list is in local (internal cells) or world (external\n            // cells) coordinates\n            //\n            // NOTE: if start equals end an empty path is returned\n            std::deque<ESM::Pathgrid::Point> aStarSearch(const int start, const int end) const;\n\n        private:\n\n            const ESM::Cell *mCell;\n            const ESM::Pathgrid *mPathgrid;\n\n            struct ConnectedPoint // edge\n            {\n                int index; // pathgrid point index of neighbour\n                float cost;\n            };\n\n            struct Node // point\n            {\n                int componentId;\n                std::vector<ConnectedPoint> edges; // neighbours\n            };\n\n            // componentId is an integer indicating the groups of connected\n            // pathgrid points (all connected points will have the same value)\n            //\n            // In Seyda Neen there are 3:\n            //\n            //   52, 53 and 54 are one set (enclosed yard)\n            //   48, 49, 50, 51, 84, 85, 86, 87, 88, 89, 90 (ship & office)\n            //   all other pathgrid points are the third set\n            //\n            std::vector<Node> mGraph;\n            bool mIsGraphConstructed;\n\n            // variables used to calculate connected components\n            int mSCCId;\n            int mSCCIndex;\n            std::vector<int> mSCCStack;\n            typedef std::pair<int, int> VPair; // first is index, second is lowlink\n            std::vector<VPair> mSCCPoint;\n            // methods used to calculate connected components\n            void recursiveStrongConnect(int v);\n            void buildConnectedPoints();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/pickpocket.cpp",
    "content": "#include \"pickpocket.hpp\"\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"npcstats.hpp\"\n\nnamespace MWMechanics\n{\n\n    Pickpocket::Pickpocket(const MWWorld::Ptr &thief, const MWWorld::Ptr &victim)\n        : mThief(thief)\n        , mVictim(victim)\n    {\n    }\n\n    float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add)\n    {\n        NpcStats& stats = ptr.getClass().getNpcStats(ptr);\n        float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();\n        float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();\n        float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));\n        return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm();\n    }\n\n    bool Pickpocket::getDetected(float valueTerm)\n    {\n        float x = getChanceModifier(mThief);\n        float y = getChanceModifier(mVictim, valueTerm);\n\n        float t = 2*x - y;\n\n        float pcSneak = static_cast<float>(mThief.getClass().getSkill(mThief, ESM::Skill::Sneak));\n        int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"iPickMinChance\")->mValue.getInteger();\n        int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"iPickMaxChance\")->mValue.getInteger();\n\n        int roll = Misc::Rng::roll0to99();\n        if (t < pcSneak / iPickMinChance)\n        {\n            return (roll > int(pcSneak / iPickMinChance));\n        }\n        else\n        {\n            t = std::min(float(iPickMaxChance), t);\n            return (roll > int(t));\n        }\n    }\n\n    bool Pickpocket::pick(MWWorld::Ptr item, int count)\n    {\n        float stackValue = static_cast<float>(item.getClass().getValue(item) * count);\n        float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"fPickPocketMod\")->mValue.getFloat();\n        float valueTerm = 10 * fPickPocketMod * stackValue;\n\n        return getDetected(valueTerm);\n    }\n\n    bool Pickpocket::finish()\n    {\n        return getDetected(0.f);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/pickpocket.hpp",
    "content": "#ifndef OPENMW_MECHANICS_PICKPOCKET_H\n#define OPENMW_MECHANICS_PICKPOCKET_H\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWMechanics\n{\n\n    class Pickpocket\n    {\n    public:\n        Pickpocket (const MWWorld::Ptr& thief, const MWWorld::Ptr& victim);\n\n        /// Steal some items\n        /// @return Was the thief detected?\n        bool pick (MWWorld::Ptr item, int count);\n        /// End the pickpocketing process\n        /// @return Was the thief detected?\n        bool finish ();\n\n    private:\n        bool getDetected(float valueTerm);\n        float getChanceModifier(const MWWorld::Ptr& ptr, float add=0);\n        MWWorld::Ptr mThief;\n        MWWorld::Ptr mVictim;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/recharge.cpp",
    "content": "#include \"recharge.hpp\"\n\n#include <components/misc/rng.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"actorutil.hpp\"\n\nnamespace MWMechanics\n{\n\nbool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration)\n{\n    float charge = item.getCellRef().getEnchantmentCharge();\n    if (charge == -1 || charge == maxCharge)\n        return false;\n\n    static const float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                \"fMagicItemRechargePerSecond\")->mValue.getFloat();\n\n    item.getCellRef().setEnchantmentCharge(std::min(charge + fMagicItemRechargePerSecond * duration, maxCharge));\n    return true;\n}\n\nbool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem)\n{\n    if (!gem.getRefData().getCount())\n        return false;\n\n    MWWorld::Ptr player = MWMechanics::getPlayer();\n    MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n\n    float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified();\n    if (luckTerm < 1 || luckTerm > 10)\n        luckTerm = 1;\n\n    float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified();\n\n    if (intelligenceTerm > 20)\n        intelligenceTerm = 20;\n    if (intelligenceTerm < 1)\n        intelligenceTerm = 1;\n\n    float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm();\n    int roll = Misc::Rng::roll0to99();\n    if (roll < x)\n    {\n        std::string soul = gem.getCellRef().getSoul();\n        const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul);\n\n        float restored = creature->mData.mSoul * (roll / x);\n\n        const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(\n                    item.getClass().getEnchantment(item));\n        item.getCellRef().setEnchantmentCharge(\n            std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));\n\n        /*\n            Start of tes3mp change (minor)\n\n            Send PlayerInventory packets that replace the original item with the new one\n        */\n        mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n        mwmp::Item removedItem = MechanicsHelper::getItem(item, 1);\n\n        item.getCellRef().setEnchantmentCharge(\n            std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));\n\n        mwmp::Item addedItem = MechanicsHelper::getItem(item, 1);\n\n        localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);\n        localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);\n        /*\n            End of tes3mp change (minor)\n        */\n\n        MWBase::Environment::get().getWindowManager()->playSound(\"Enchant Success\");\n\n        player.getClass().getContainerStore(player).restack(item);\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_OBJECT_SOUND packet every time the player makes a sound here\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectSound(MWMechanics::getPlayer(), \"Enchant Success\", 1.0, 1.0);\n        objectList->sendObjectSound();\n        /*\n            End of tes3mp addition\n        */\n    }\n    else\n    {\n        MWBase::Environment::get().getWindowManager()->playSound(\"Enchant Fail\");\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_OBJECT_SOUND packet every time the player makes a sound here\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectSound(MWMechanics::getPlayer(), \"Enchant Fail\", 1.0, 1.0);\n        objectList->sendObjectSound();\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);\n    gem.getContainerStore()->remove(gem, 1, player);\n\n    if (gem.getRefData().getCount() == 0)\n    {\n        std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"sNotifyMessage51\")->mValue.getString();\n        message = Misc::StringUtils::format(message, gem.getClass().getName(gem));\n\n        MWBase::Environment::get().getWindowManager()->messageBox(message);\n\n        // special case: readd Azura's Star\n        if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, \"Misc_SoulGem_Azura\"))\n            player.getClass().getContainerStore(player).add(\"Misc_SoulGem_Azura\", 1, player);\n    }\n\n    return true;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/recharge.hpp",
    "content": "#ifndef MWMECHANICS_RECHARGE_H\n#define MWMECHANICS_RECHARGE_H\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWMechanics\n{\n\n    bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration);\n\n    bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem);\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/repair.cpp",
    "content": "#include \"repair.hpp\"\n\n#include <components/misc/rng.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"actorutil.hpp\"\n\nnamespace MWMechanics\n{\n\nvoid Repair::repair(const MWWorld::Ptr &itemToRepair)\n{\n    MWWorld::Ptr player = getPlayer();\n    MWWorld::LiveCellRef<ESM::Repair> *ref =\n        mTool.get<ESM::Repair>();\n\n    // unstack tool if required\n    player.getClass().getContainerStore(player).unstack(mTool, player);\n\n    // reduce number of uses left\n    int uses = mTool.getClass().getItemHealth(mTool);\n    uses -= std::min(uses, 1);\n    mTool.getCellRef().setCharge(uses);\n\n    MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);\n\n    float fatigueTerm = stats.getFatigueTerm();\n    float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified();\n    float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();\n    float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer);\n\n    float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n            .find(\"fRepairAmountMult\")->mValue.getFloat();\n\n    float toolQuality = ref->mBase->mData.mQuality;\n\n    float x = (0.1f * pcStrength + 0.1f * pcLuck + armorerSkill) * fatigueTerm;\n\n    int roll = Misc::Rng::roll0to99();\n    if (roll <= x)\n    {\n        int y = static_cast<int>(fRepairAmountMult * toolQuality * roll);\n        y = std::max(1, y);\n\n        // repair by 'y' points\n        int charge = itemToRepair.getClass().getItemHealth(itemToRepair);\n        charge = std::min(charge + y, itemToRepair.getClass().getItemMaxHealth(itemToRepair));\n\n        /*\n            Start of tes3mp change (minor)\n\n            Send PlayerInventory packets that replace the original item with the new one\n        */\n        mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n        mwmp::Item removedItem = MechanicsHelper::getItem(itemToRepair, 1);\n\n        itemToRepair.getCellRef().setCharge(charge);\n\n        mwmp::Item addedItem = MechanicsHelper::getItem(itemToRepair, 1);\n\n        localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);\n        localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);\n        /*\n            End of tes3mp change (minor)\n        */\n\n        // attempt to re-stack item, in case it was fully repaired\n        MWWorld::ContainerStoreIterator stacked = player.getClass().getContainerStore(player).restack(itemToRepair);\n\n        // set the OnPCRepair variable on the item's script\n        std::string script = stacked->getClass().getScript(itemToRepair);\n        if(script != \"\")\n            stacked->getRefData().getLocals().setVarByInt(script, \"onpcrepair\", 1);\n\n        // increase skill\n        player.getClass().skillUsageSucceeded(player, ESM::Skill::Armorer, 0);\n\n        MWBase::Environment::get().getWindowManager()->playSound(\"Repair\");\n        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sRepairSuccess}\");\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_OBJECT_SOUND packet every time the player makes a sound here\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectSound(MWMechanics::getPlayer(), \"Repair\", 1.0, 1.0);\n        objectList->sendObjectSound();\n        /*\n            End of tes3mp addition\n        */\n    }\n    else\n    {\n        MWBase::Environment::get().getWindowManager()->playSound(\"Repair Fail\");\n        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sRepairFailed}\");\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_OBJECT_SOUND packet every time the player makes a sound here\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectSound(MWMechanics::getPlayer(), \"Repair Fail\", 1.0, 1.0);\n        objectList->sendObjectSound();\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    // tool used up?\n    if (mTool.getCellRef().getCharge() == 0)\n    {\n        MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);\n\n        store.remove(mTool, 1, player);\n\n        std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                .find(\"sNotifyMessage51\")->mValue.getString();\n        message = Misc::StringUtils::format(message, mTool.getClass().getName(mTool));\n\n        MWBase::Environment::get().getWindowManager()->messageBox(message);\n\n        // try to find a new tool of the same ID\n        for (MWWorld::ContainerStoreIterator iter (store.begin());\n             iter!=store.end(); ++iter)\n        {\n            if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), mTool.getCellRef().getRefId()))\n            {\n                mTool = *iter;\n\n                MWBase::Environment::get().getWindowManager()->playSound(\"Item Repair Up\");\n\n                break;\n            }\n        }\n    }\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/repair.hpp",
    "content": "#ifndef OPENMW_MWMECHANICS_REPAIR_H\n#define OPENMW_MWMECHANICS_REPAIR_H\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWMechanics\n{\n\n    class Repair\n    {\n    public:\n        void setTool (const MWWorld::Ptr& tool) { mTool = tool; }\n        MWWorld::Ptr getTool() { return mTool; }\n\n        void repair (const MWWorld::Ptr& itemToRepair);\n\n    private:\n        MWWorld::Ptr mTool;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/security.cpp",
    "content": "#include \"security.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwworld/cellstore.hpp\"\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"creaturestats.hpp\"\n\nnamespace MWMechanics\n{\n\n    Security::Security(const MWWorld::Ptr &actor)\n        : mActor(actor)\n    {\n        CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);\n        mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified();\n        mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified();\n        mSecuritySkill = static_cast<float>(actor.getClass().getSkill(actor, ESM::Skill::Security));\n        mFatigueTerm = creatureStats.getFatigueTerm();\n    }\n\n    void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick,\n                            std::string& resultMessage, std::string& resultSound)\n    {\n        if (lock.getCellRef().getLockLevel() <= 0 ||\n            lock.getCellRef().getLockLevel() == ESM::UnbreakableLock ||\n            !lock.getClass().hasToolTip(lock)) //If it's unlocked or can not be unlocked back out immediately\n            return;\n\n        int uses = lockpick.getClass().getItemHealth(lockpick);\n        if (uses == 0)\n            return;\n\n        int lockStrength = lock.getCellRef().getLockLevel();\n\n        float pickQuality = lockpick.get<ESM::Lockpick>()->mBase->mData.mQuality;\n\n        float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fPickLockMult\")->mValue.getFloat();\n\n        float x = 0.2f * mAgility + 0.1f * mLuck + mSecuritySkill;\n        x *= pickQuality * mFatigueTerm;\n        x += fPickLockMult * lockStrength;\n\n        MWBase::Environment::get().getMechanicsManager()->unlockAttempted(mActor, lock);\n\n        resultSound = \"Open Lock Fail\";\n        if (x <= 0)\n            resultMessage = \"#{sLockImpossible}\";\n        else\n        {\n            if (Misc::Rng::roll0to99() <= x)\n            {\n                /*\n                    Start of tes3mp change (major)\n\n                    Disable unilateral locking on this client and expect the server's reply to our\n                    packet to do it instead\n                */\n                //lock.getCellRef().unlock();\n                /*\n                    End of tes3mp change (major)\n                */\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_OBJECT_LOCK packet every time an object is unlocked here\n                */\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectLock(lock, 0);\n                objectList->sendObjectLock();\n                /*\n                    End of tes3mp addition\n                */\n\n                resultMessage = \"#{sLockSuccess}\";\n                resultSound = \"Open Lock\";\n                mActor.getClass().skillUsageSucceeded(mActor, ESM::Skill::Security, 1);\n            }\n            else\n                resultMessage = \"#{sLockFail}\";\n        }\n\n        lockpick.getCellRef().setCharge(--uses);\n        if (!uses)\n            lockpick.getContainerStore()->remove(lockpick, 1, mActor);\n    }\n\n    void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe,\n                             std::string& resultMessage, std::string& resultSound)\n    {\n        if (trap.getCellRef().getTrap().empty())\n            return;\n\n        int uses = probe.getClass().getItemHealth(probe);\n        if (uses == 0)\n            return;\n\n        float probeQuality = probe.get<ESM::Probe>()->mBase->mData.mQuality;\n\n        const ESM::Spell* trapSpell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(trap.getCellRef().getTrap());\n        int trapSpellPoints = trapSpell->mData.mCost;\n\n        float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fTrapCostMult\")->mValue.getFloat();\n\n        float x = 0.2f * mAgility + 0.1f * mLuck + mSecuritySkill;\n        x += fTrapCostMult * trapSpellPoints;\n        x *= probeQuality * mFatigueTerm;\n\n        MWBase::Environment::get().getMechanicsManager()->unlockAttempted(mActor, trap);\n\n        resultSound = \"Disarm Trap Fail\";\n        if (x <= 0)\n            resultMessage = \"#{sTrapImpossible}\";\n        else\n        {\n            if (Misc::Rng::roll0to99() <= x)\n            {\n                /*\n                    Start of tes3mp change (major)\n\n                    Disable unilateral trap disarming on this client and expect the server's reply to our\n                    packet to do it instead\n                */\n                //trap.getCellRef().setTrap(\"\");\n                /*\n                    End of tes3mp change (major)\n                */\n\n                resultSound = \"Disarm Trap\";\n                resultMessage = \"#{sTrapSuccess}\";\n                mActor.getClass().skillUsageSucceeded(mActor, ESM::Skill::Security, 0);\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_OBJECT_TRAP packet every time a trap is disarmed\n                */\n                mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                objectList->reset();\n                objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                objectList->addObjectTrap(trap, trap.getRefData().getPosition(), true);\n                objectList->sendObjectTrap();\n                /*\n                    End of tes3mp addition\n                */\n            }\n            else\n                resultMessage = \"#{sTrapFail}\";\n        }\n\n        probe.getCellRef().setCharge(--uses);\n        if (!uses)\n            probe.getContainerStore()->remove(probe, 1, mActor);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/security.hpp",
    "content": "#ifndef MWMECHANICS_SECURITY_H\n#define MWMECHANICS_SECURITY_H\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWMechanics\n{\n\n    /// @brief implementation of Security skill\n    class Security\n    {\n    public:\n        Security (const MWWorld::Ptr& actor);\n\n        void pickLock (const MWWorld::Ptr& lock, const MWWorld::Ptr& lockpick,\n                              std::string& resultMessage, std::string& resultSound);\n        void probeTrap (const MWWorld::Ptr& trap, const MWWorld::Ptr& probe,\n                               std::string& resultMessage, std::string& resultSound);\n\n    private:\n        float mAgility, mLuck, mSecuritySkill, mFatigueTerm;\n        MWWorld::Ptr mActor;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellabsorption.cpp",
    "content": "#include \"spellabsorption.hpp\"\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"spellutil.hpp\"\n\nnamespace MWMechanics\n{\n\n    class GetAbsorptionProbability : public MWMechanics::EffectSourceVisitor\n    {\n    public:\n        float mProbability{0.f};\n\n        GetAbsorptionProbability() = default;\n\n        void visit (MWMechanics::EffectKey key, int /*effectIndex*/,\n                            const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,\n                            float magnitude, float /*remainingTime*/, float /*totalTime*/) override\n        {\n            if (key.mId == ESM::MagicEffect::SpellAbsorption)\n            {\n                if (mProbability == 0.f)\n                    mProbability = magnitude / 100;\n                else\n                {\n                    // If there are different sources of SpellAbsorption effect, multiply failing probability for all effects.\n                    // Real absorption probability will be the (1 - total fail chance) in this case.\n                    float failProbability = 1.f - mProbability;\n                    failProbability *= 1.f - magnitude / 100;\n                    mProbability = 1.f - failProbability;\n                }\n            }\n        }\n    };\n\n    int getAbsorbChance(const MWWorld::Ptr& caster, const MWWorld::Ptr& target)\n    {\n        if(target.isEmpty() || caster == target || !target.getClass().isActor())\n            return 0;\n\n        CreatureStats& stats = target.getClass().getCreatureStats(target);\n        if (stats.getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude() <= 0.f)\n            return 0;\n\n        GetAbsorptionProbability check;\n        stats.getActiveSpells().visitEffectSources(check);\n        stats.getSpells().visitEffectSources(check);\n        if (target.getClass().hasInventoryStore(target))\n            target.getClass().getInventoryStore(target).visitEffectSources(check);\n\n        return check.mProbability * 100;\n    }\n\n    void absorbSpell (const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target)\n    {\n        CreatureStats& stats = target.getClass().getCreatureStats(target);\n\n        const auto& esmStore = MWBase::Environment::get().getWorld()->getStore();\n        const ESM::Static* absorbStatic = esmStore.get<ESM::Static>().find(\"VFX_Absorb\");\n        MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);\n        if (animation && !absorbStatic->mModel.empty())\n            animation->addEffect( \"meshes\\\\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, std::string());\n        const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(spellId);\n        int spellCost = 0;\n        if (spell)\n        {\n            spellCost = spell->mData.mCost;\n        }\n        else\n        {\n            const ESM::Enchantment* enchantment = esmStore.get<ESM::Enchantment>().search(spellId);\n            if (enchantment)\n                spellCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), caster);\n        }\n\n        // Magicka is increased by the cost of the spell\n        DynamicStat<float> magicka = stats.getMagicka();\n        magicka.setCurrent(magicka.getCurrent() + spellCost);\n        stats.setMagicka(magicka);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellabsorption.hpp",
    "content": "#ifndef MWMECHANICS_SPELLABSORPTION_H\n#define MWMECHANICS_SPELLABSORPTION_H\n\n#include <string>\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    void absorbSpell(const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target);\n    // Calculate the chance to absorb a spell based on the magnitude of every Spell Absorption effect source on the target.\n    int getAbsorbChance(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellcasting.cpp",
    "content": "#include \"spellcasting.hpp\"\n\n#include <components/misc/constants.hpp>\n#include <components/misc/rng.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/actionteleport.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"actorutil.hpp\"\n#include \"aifollow.hpp\"\n#include \"creaturestats.hpp\"\n#include \"linkedeffects.hpp\"\n#include \"spellabsorption.hpp\"\n#include \"spellresistance.hpp\"\n#include \"spellutil.hpp\"\n#include \"summoning.hpp\"\n#include \"tickableeffects.hpp\"\n#include \"weapontype.hpp\"\n\nnamespace MWMechanics\n{\n    CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool manualSpell)\n        : mCaster(caster)\n        , mTarget(target)\n        , mFromProjectile(fromProjectile)\n        , mManualSpell(manualSpell)\n    {\n    }\n\n    void CastSpell::launchMagicBolt ()\n    {\n        osg::Vec3f fallbackDirection(0, 1, 0);\n        osg::Vec3f offset(0, 0, 0);\n        if (!mTarget.isEmpty() && mTarget.getClass().isActor())\n            offset.z() = MWBase::Environment::get().getWorld()->getHalfExtents(mTarget).z();\n\n        // Fall back to a \"caster to target\" direction if we have no other means of determining it\n        // (e.g. when cast by a non-actor)\n        if (!mTarget.isEmpty())\n            fallbackDirection =\n                (mTarget.getRefData().getPosition().asVec3() + offset) -\n                (mCaster.getRefData().getPosition().asVec3());\n\n        MWBase::Environment::get().getWorld()->launchMagicBolt(mId, mCaster, fallbackDirection);\n    }\n\n    void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,\n                            const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)\n    {\n        const bool targetIsActor = !target.isEmpty() && target.getClass().isActor();\n        if (targetIsActor)\n        {\n            // Early-out for characters that have departed.\n            const auto& stats = target.getClass().getCreatureStats(target);\n            if (stats.isDead() && stats.isDeathAnimationFinished())\n                return;\n        }\n\n        // If none of the effects need to apply, we can early-out\n        bool found = false;\n        for (const ESM::ENAMstruct& effect : effects.mList)\n        {\n            if (effect.mRange == range)\n            {\n                found = true;\n                break;\n            }\n        }\n        if (!found)\n            return;\n\n        const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (mId);\n        if (spell && targetIsActor && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight))\n        {\n            int requiredResistance = (spell->mData.mType == ESM::Spell::ST_Disease) ?\n                ESM::MagicEffect::ResistCommonDisease\n                : ESM::MagicEffect::ResistBlightDisease;\n            float x = target.getClass().getCreatureStats(target).getMagicEffects().get(requiredResistance).getMagnitude();\n\n            if (Misc::Rng::roll0to99() <= x)\n            {\n                // Fully resisted, show message\n                if (target == getPlayer())\n                    MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicPCResisted}\");\n                return;\n            }\n        }\n\n        ESM::EffectList reflectedEffects;\n        std::vector<ActiveSpells::ActiveEffect> appliedLastingEffects;\n\n        // HACK: cache target's magic effects here, and add any applied effects to it. Use the cached effects for determining resistance.\n        // This is required for Weakness effects in a spell to apply to any subsequent effects in the spell.\n        // Otherwise, they'd only apply after the whole spell was added.\n        MagicEffects targetEffects;\n        if (targetIsActor)\n            targetEffects += target.getClass().getCreatureStats(target).getMagicEffects();\n\n        bool castByPlayer = (!caster.isEmpty() && caster == getPlayer());\n\n        ActiveSpells targetSpells;\n        if (targetIsActor)\n            targetSpells = target.getClass().getCreatureStats(target).getActiveSpells();\n\n        bool canCastAnEffect = false;    // For bound equipment.If this remains false\n                                         // throughout the iteration of this spell's \n                                         // effects, we display a \"can't re-cast\" message\n\n        int absorbChance = getAbsorbChance(caster, target);\n\n        int currentEffectIndex = 0;\n        for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());\n             !target.isEmpty() && effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex)\n        {\n            if (effectIt->mRange != range)\n                continue;\n\n            const ESM::MagicEffect *magicEffect =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (\n                effectIt->mEffectID);\n\n            // Re-casting a bound equipment effect has no effect if the spell is still active\n            if (magicEffect->mData.mFlags & ESM::MagicEffect::NonRecastable && targetSpells.isSpellActive(mId))\n            {\n                if (effectIt == (effects.mList.end() - 1) && !canCastAnEffect && castByPlayer)\n                    MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicCannotRecast}\");\n                continue;\n            }\n            canCastAnEffect = true;\n\n            // Try absorbing the effect\n            if(absorbChance && Misc::Rng::roll0to99() < absorbChance)\n            {\n                absorbSpell(mId, caster, target);\n                continue;\n            }\n\n            if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer))\n                continue;\n\n            // caster needs to be an actor for linked effects (e.g. Absorb)\n            if (magicEffect->mData.mFlags & ESM::MagicEffect::CasterLinked\n                    && (caster.isEmpty() || !caster.getClass().isActor()))\n                continue;\n\n            // Notify the target actor they've been hit\n            bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful;\n            if (target.getClass().isActor() && target != caster && !caster.isEmpty() && isHarmful)\n                target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true);\n\n            // Reflect harmful effects\n            if (!reflected && reflectEffect(*effectIt, magicEffect, caster, target, reflectedEffects))\n                continue;\n\n            /*\n                Start of tes3mp addition\n\n                Now that reflected effects have been handled, don't unilaterally process effects further for dedicated players\n                and actors on this client and instead expect their effects to be applied correctly through the SpellsActive\n                packets received\n            */\n            if (mwmp::PlayerList::isDedicatedPlayer(target) || mwmp::Main::get().getCellController()->isDedicatedActor(target))\n                continue;\n            /*\n                End of tes3mp addition\n            */\n\n            // Try resisting.\n            float magnitudeMult = getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects);\n            if (magnitudeMult == 0)\n            {\n                // Fully resisted, show message\n                if (target == getPlayer())\n                    MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicPCResisted}\");\n                else if (castByPlayer)\n                    MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicTargetResisted}\");\n            }\n            else\n            {\n                float magnitude = effectIt->mMagnMin + Misc::Rng::rollDice(effectIt->mMagnMax - effectIt->mMagnMin + 1);\n                magnitude *= magnitudeMult;\n\n                if (!target.getClass().isActor())\n                {\n                    // non-actor objects have no list of active magic effects, so have to apply instantly\n                    if (!applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude))\n                        continue;\n                }\n                else // target.getClass().isActor() == true\n                {\n                    ActiveSpells::ActiveEffect effect;\n                    effect.mEffectId = effectIt->mEffectID;\n                    effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;\n                    effect.mMagnitude = magnitude;\n                    effect.mTimeLeft = 0.f;\n                    effect.mEffectIndex = currentEffectIndex;\n\n                    // Avoid applying absorb effects if the caster is the target\n                    // We still need the spell to be added\n                    if (caster == target\n                        && effectIt->mEffectID >= ESM::MagicEffect::AbsorbAttribute\n                        && effectIt->mEffectID <= ESM::MagicEffect::AbsorbSkill)\n                    {\n                        effect.mMagnitude = 0;\n                    }\n\n                    // Avoid applying harmful effects to the player in god mode\n                    if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState() && isHarmful)\n                    {\n                        effect.mMagnitude = 0;\n                    }\n\n                    bool effectAffectsHealth = isHarmful || effectIt->mEffectID == ESM::MagicEffect::RestoreHealth;\n                    if (castByPlayer && target != caster && !target.getClass().getCreatureStats(target).isDead() && effectAffectsHealth)\n                    {\n                        // If player is attempting to cast a harmful spell on or is healing a living target, show the target's HP bar.\n                        MWBase::Environment::get().getWindowManager()->setEnemy(target);\n                    }\n\n                    bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);\n                    effect.mDuration = hasDuration ? static_cast<float>(effectIt->mDuration) : 1.f;\n\n                    bool appliedOnce = magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce;\n                    if (!appliedOnce)\n                        effect.mDuration = std::max(1.f, effect.mDuration);\n\n                    if (effect.mDuration == 0)\n                    {\n                        // We still should add effect to list to allow GetSpellEffects to detect this spell\n                        appliedLastingEffects.push_back(effect);\n\n                        // duration 0 means apply full magnitude instantly\n                        bool wasDead = target.getClass().getCreatureStats(target).isDead();\n                        effectTick(target.getClass().getCreatureStats(target), target, EffectKey(*effectIt), effect.mMagnitude);\n                        bool isDead = target.getClass().getCreatureStats(target).isDead();\n\n                        /*\n                            Start of tes3mp addition\n\n                            If the target was a LocalPlayer or LocalActor who died, record the caster as the killer\n                        */\n                        if (!wasDead && isDead)\n                        {\n                            bool isSuicide = target == caster || caster.isEmpty();\n\n                            if (target == MWMechanics::getPlayer())\n                            {\n                                mwmp::Main::get().getLocalPlayer()->killer = isSuicide ?\n                                    MechanicsHelper::getTarget(target) : MechanicsHelper::getTarget(caster);\n                            }\n                            else if (mwmp::Main::get().getCellController()->isLocalActor(target))\n                            {\n                                mwmp::Main::get().getCellController()->getLocalActor(target)->killer = isSuicide ?\n                                    MechanicsHelper::getTarget(target) : MechanicsHelper::getTarget(caster);\n                            }\n                        }\n                        /*\n                            End of tes3mp addition\n                        */\n\n                        if (!wasDead && isDead)\n                            MWBase::Environment::get().getMechanicsManager()->actorKilled(target, caster);\n                    }\n                    else\n                    {\n                        effect.mTimeLeft = effect.mDuration;\n\n                        targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude));\n\n                        // add to list of active effects, to apply in next frame\n                        appliedLastingEffects.push_back(effect);\n\n                        // Unequip all items, if a spell with the ExtraSpell effect was casted\n                        if (effectIt->mEffectID == ESM::MagicEffect::ExtraSpell && target.getClass().hasInventoryStore(target))\n                        {\n                            MWWorld::InventoryStore& store = target.getClass().getInventoryStore(target);\n                            store.unequipAll(target);\n                        }\n\n                        // Command spells should have their effect, including taking the target out of combat, each time the spell successfully affects the target\n                        if (((effectIt->mEffectID == ESM::MagicEffect::CommandHumanoid && target.getClass().isNpc())\n                        || (effectIt->mEffectID == ESM::MagicEffect::CommandCreature && target.getTypeName() == typeid(ESM::Creature).name()))\n                        && !caster.isEmpty() && caster.getClass().isActor() && target != getPlayer() && effect.mMagnitude >= target.getClass().getCreatureStats(target).getLevel())\n                        {\n                            MWMechanics::AiFollow package(caster, true);\n                            target.getClass().getCreatureStats(target).getAiSequence().stack(package, target);\n                        }\n\n                        // For absorb effects, also apply the effect to the caster - but with a negative\n                        // magnitude, since we're transferring stats from the target to the caster\n                        if (effectIt->mEffectID >= ESM::MagicEffect::AbsorbAttribute && effectIt->mEffectID <= ESM::MagicEffect::AbsorbSkill)\n                            absorbStat(*effectIt, effect, caster, target, reflected, mSourceName);\n                    }\n                }\n\n                // Re-casting a summon effect will remove the creature from previous castings of that effect.\n                if (isSummoningEffect(effectIt->mEffectID) && targetIsActor)\n                {\n                    CreatureStats& targetStats = target.getClass().getCreatureStats(target);\n                    ESM::SummonKey key(effectIt->mEffectID, mId, currentEffectIndex);\n                    auto findCreature = targetStats.getSummonedCreatureMap().find(key);\n                    if (findCreature != targetStats.getSummonedCreatureMap().end())\n                    {\n                        /*\n                            Start of tes3mp change (major)\n\n                            Don't clean up placeholder summoned creatures still awaiting a spawn\n                            packet from the server, because that would make the packet create permanent\n                            spawns instead\n                        */\n                        if (findCreature->second != -1)\n                        {\n                            MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(target, findCreature->second);\n                            targetStats.getSummonedCreatureMap().erase(findCreature);\n                        }\n                        /*\n                            End of tes3mp change (major)\n                        */\n                    }\n                }\n\n                if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)\n                {\n                    static const std::string schools[] = {\n                        \"alteration\", \"conjuration\", \"destruction\", \"illusion\", \"mysticism\", \"restoration\"\n                    };\n\n                    MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n                    if(!magicEffect->mHitSound.empty())\n                        sndMgr->playSound3D(target, magicEffect->mHitSound, 1.0f, 1.0f);\n                    else\n                        sndMgr->playSound3D(target, schools[magicEffect->mData.mSchool]+\" hit\", 1.0f, 1.0f);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_SOUND packet every time a sound is made here\n                    */\n                    mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                    objectList->addObjectSound(target, magicEffect->mHitSound.empty() ? schools[magicEffect->mData.mSchool] + \" hit\" : magicEffect->mHitSound, 1.0f, 1.0f);\n                    objectList->sendObjectSound();\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    // Add VFX\n                    const ESM::Static* castStatic;\n                    if (!magicEffect->mHit.empty())\n                        castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);\n                    else\n                        castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (\"VFX_DefaultHit\");\n\n                    bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;\n                    // Note: in case of non actor, a free effect should be fine as well\n                    MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);\n                    if (anim && !castStatic->mModel.empty())\n                        anim->addEffect(\"meshes\\\\\" + castStatic->mModel, magicEffect->mIndex, loop, \"\", magicEffect->mParticle);\n                }\n            }\n        }\n\n        if (!exploded)\n            MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, target, range, mId, mSourceName, mFromProjectile);\n\n        if (!target.isEmpty())\n        {\n            if (!reflectedEffects.mList.empty())\n                inflict(caster, target, reflectedEffects, range, true, exploded);\n\n            if (!appliedLastingEffects.empty())\n            {\n                int casterActorId = -1;\n                if (!caster.isEmpty() && caster.getClass().isActor())\n                    casterActorId = caster.getClass().getCreatureStats(caster).getActorId();\n                target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,\n                        mSourceName, casterActorId);\n            }\n        }\n    }\n\n    bool CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude)\n    {\n        short effectId = effect.mId;\n        if (target.getClass().canLock(target))\n        {\n            if (effectId == ESM::MagicEffect::Lock)\n            {\n                const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n                const ESM::MagicEffect *magiceffect = store.get<ESM::MagicEffect>().find(effectId);\n                MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);\n                if (animation)\n                    animation->addSpellCastGlow(magiceffect);\n                if (target.getCellRef().getLockLevel() < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude\n                {\n                    if (caster == getPlayer())\n                        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicLockSuccess}\");\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Disable unilateral locking on this client and expect the server's reply to our\n                        packet to do it instead\n                    */\n                    //target.getCellRef().lock(static_cast<int>(magnitude));\n                    /*\n                        End of tes3mp change (major)\n                    */\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_LOCK packet every time an object is locked here\n                    */\n                    mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                    objectList->addObjectLock(target, static_cast<int>(magnitude));\n                    objectList->sendObjectLock();\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n                return true;\n            }\n            else if (effectId == ESM::MagicEffect::Open)\n            {\n                if (!caster.isEmpty())\n                {\n                    MWBase::Environment::get().getMechanicsManager()->unlockAttempted(getPlayer(), target);\n                    // Use the player instead of the caster for vanilla crime compatibility\n                }\n                const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n                const ESM::MagicEffect *magiceffect = store.get<ESM::MagicEffect>().find(effectId);\n                MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);\n                if (animation)\n                    animation->addSpellCastGlow(magiceffect);\n                if (target.getCellRef().getLockLevel() <= magnitude)\n                {\n                    if (target.getCellRef().getLockLevel() > 0)\n                    {\n                        MWBase::Environment::get().getSoundManager()->playSound3D(target, \"Open Lock\", 1.f, 1.f);\n\n                        if (caster == getPlayer())\n                            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicOpenSuccess}\");\n                    }\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Disable unilateral locking on this client and expect the server's reply to our\n                        packet to do it instead\n                    */\n                    //target.getCellRef().unlock();\n                    /*\n                        End of tes3mp change (major)\n                    */\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_LOCK packet every time an object is unlocked here\n                    */\n                    mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                    objectList->addObjectLock(target, 0);\n                    objectList->sendObjectLock();\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n                else\n                {\n                    MWBase::Environment::get().getSoundManager()->playSound3D(target, \"Open Lock Fail\", 1.f, 1.f);\n                }\n\n                return true;\n            }\n        }\n        else if (target.getClass().isActor() && effectId == ESM::MagicEffect::Dispel)\n        {\n            target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude, true);\n            return true;\n        }\n        else if (target.getClass().isActor() && target == getPlayer())\n        {\n            MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mCaster);\n            bool teleportingEnabled = MWBase::Environment::get().getWorld()->isTeleportingEnabled();\n\n            if (effectId == ESM::MagicEffect::DivineIntervention || effectId == ESM::MagicEffect::AlmsiviIntervention)\n            {\n                if (!teleportingEnabled)\n                {\n                    if (caster == getPlayer())\n                        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sTeleportDisabled}\");\n                    return true;\n                }\n                std::string marker = (effectId == ESM::MagicEffect::DivineIntervention) ? \"divinemarker\" : \"templemarker\";\n                MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, marker);\n                anim->removeEffect(effectId);\n                const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()\n                    .search(\"VFX_Summon_end\");\n                if (fx)\n                    anim->addEffect(\"meshes\\\\\" + fx->mModel, -1);\n                return true;\n            }\n            else if (effectId == ESM::MagicEffect::Mark)\n            {\n                if (teleportingEnabled)\n                {\n                    MWBase::Environment::get().getWorld()->getPlayer().markPosition(\n                        target.getCell(), target.getRefData().getPosition());\n                }\n                else if (caster == getPlayer())\n                {\n                    MWBase::Environment::get().getWindowManager()->messageBox(\"#{sTeleportDisabled}\");\n                }\n\n                /*\n                    Start of tes3mp addition\n\n                    Send a PlayerMiscellaneous packet with the player's new mark location\n                */\n                mwmp::Main::get().getLocalPlayer()->sendMarkLocation(*target.getCell()->getCell(), target.getRefData().getPosition());\n                /*\n                    End of tes3mp addition\n                */\n\n                return true;\n            }\n            else if (effectId == ESM::MagicEffect::Recall)\n            {\n                if (!teleportingEnabled)\n                {\n                    if (caster == getPlayer())\n                        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sTeleportDisabled}\");\n                    return true;\n                }\n\n                MWWorld::CellStore* markedCell = nullptr;\n                ESM::Position markedPosition;\n\n                MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition);\n                if (markedCell)\n                {\n                    MWWorld::ActionTeleport action(markedCell->isExterior() ? \"\" : markedCell->getCell()->mName,\n                                            markedPosition, false);\n                    action.execute(target);\n                    anim->removeEffect(effectId);\n                }\n                return true;\n            }\n        }\n        return false;\n    }\n\n\n    bool CastSpell::cast(const std::string &id)\n    {\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n        if (const auto spell = store.get<ESM::Spell>().search(id))\n            return cast(spell);\n\n        if (const auto potion = store.get<ESM::Potion>().search(id))\n            return cast(potion);\n\n        if (const auto ingredient = store.get<ESM::Ingredient>().search(id))\n            return cast(ingredient);\n\n        throw std::runtime_error(\"ID type cannot be casted\");\n    }\n\n    bool CastSpell::cast(const MWWorld::Ptr &item, bool launchProjectile)\n    {\n        std::string enchantmentName = item.getClass().getEnchantment(item);\n        if (enchantmentName.empty())\n            throw std::runtime_error(\"can't cast an item without an enchantment\");\n\n        mSourceName = item.getClass().getName(item);\n        mId = item.getCellRef().getRefId();\n\n        const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchantmentName);\n\n        mStack = false;\n\n        bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n        bool isProjectile = false;\n        if (item.getTypeName() == typeid(ESM::Weapon).name())\n        {\n            int type = item.get<ESM::Weapon>()->mBase->mData.mType;\n            ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass;\n            isProjectile = (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo);\n        }\n        int type = enchantment->mData.mType;\n\n        // Check if there's enough charge left\n        if (!godmode && (type == ESM::Enchantment::WhenUsed || (!isProjectile && type == ESM::Enchantment::WhenStrikes)))\n        {\n            int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), mCaster);\n\n            if (item.getCellRef().getEnchantmentCharge() == -1)\n                item.getCellRef().setEnchantmentCharge(static_cast<float>(enchantment->mData.mCharge));\n\n            if (item.getCellRef().getEnchantmentCharge() < castCost)\n            {\n                if (mCaster == getPlayer())\n                {\n                    MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicInsufficientCharge}\");\n\n                    // Failure sound\n                    int school = 0;\n                    if (!enchantment->mEffects.mList.empty())\n                    {\n                        short effectId = enchantment->mEffects.mList.front().mEffectID;\n                        const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectId);\n                        school = magicEffect->mData.mSchool;\n                    }\n\n                    static const std::string schools[] = {\n                        \"alteration\", \"conjuration\", \"destruction\", \"illusion\", \"mysticism\", \"restoration\"\n                    };\n                    MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n                    sndMgr->playSound3D(mCaster, \"Spell Failure \" + schools[school], 1.0f, 1.0f);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_SOUND packet every time a sound is made here\n                    */\n                    mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                    objectList->addObjectSound(mCaster, \"Spell Failure \" + schools[school], 1.0f, 1.0f);\n                    objectList->sendObjectSound();\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n                return false;\n            }\n            // Reduce charge\n            item.getCellRef().setEnchantmentCharge(item.getCellRef().getEnchantmentCharge() - castCost);\n        }\n\n        if (type == ESM::Enchantment::WhenUsed)\n        {\n            if (mCaster == getPlayer())\n                mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1);\n        }\n        else if (type == ESM::Enchantment::CastOnce)\n        {\n            if (!godmode)\n                item.getContainerStore()->remove(item, 1, mCaster);\n        }\n        else if (type == ESM::Enchantment::WhenStrikes)\n        {\n            if (mCaster == getPlayer())\n                mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3);\n        }\n\n        inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self);\n\n        if (isProjectile || !mTarget.isEmpty())\n            inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);\n\n        if (launchProjectile)\n            launchMagicBolt();\n        else if (isProjectile || !mTarget.isEmpty())\n            inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Target);\n\n        return true;\n    }\n\n    bool CastSpell::cast(const ESM::Potion* potion)\n    {\n        mSourceName = potion->mName;\n        mId = potion->mId;\n        mStack = true;\n\n        inflict(mCaster, mCaster, potion->mEffects, ESM::RT_Self);\n\n        return true;\n    }\n\n    bool CastSpell::cast(const ESM::Spell* spell)\n    {\n        mSourceName = spell->mName;\n        mId = spell->mId;\n        mStack = false;\n\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n\n        int school = 0;\n\n        bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n\n        if (mCaster.getClass().isActor() && !mAlwaysSucceed && !mManualSpell)\n        {\n            school = getSpellSchool(spell, mCaster);\n\n            CreatureStats& stats = mCaster.getClass().getCreatureStats(mCaster);\n\n            if (!godmode)\n            {\n                // Reduce fatigue (note that in the vanilla game, both GMSTs are 0, and there's no fatigue loss)\n                static const float fFatigueSpellBase = store.get<ESM::GameSetting>().find(\"fFatigueSpellBase\")->mValue.getFloat();\n                static const float fFatigueSpellMult = store.get<ESM::GameSetting>().find(\"fFatigueSpellMult\")->mValue.getFloat();\n                DynamicStat<float> fatigue = stats.getFatigue();\n                const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster);\n\n                float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);\n                fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); \n                stats.setFatigue(fatigue);\n\n                bool fail = false;\n\n                /*\n                    Start of tes3mp change (major)\n                \n                    Make spell casting fail based on the casting success rated determined\n                    in MechanicsHelper::getSpellSuccess()\n                */\n                mwmp::Cast *localCast = NULL;\n                mwmp::Cast *dedicatedCast = MechanicsHelper::getDedicatedCast(mCaster);\n\n                if (dedicatedCast)\n                    dedicatedCast->pressed = false;\n                else\n                {\n                    localCast = MechanicsHelper::getLocalCast(mCaster);\n                    localCast->success = MechanicsHelper::getSpellSuccess(mId, mCaster);\n                    localCast->pressed = false;\n                    localCast->shouldSend = true;\n                }\n\n                // Check success\n                if ((localCast && localCast->success == false) ||\n                    (dedicatedCast && dedicatedCast->success == false))\n                {\n                    if (mCaster == getPlayer())\n                        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicSkillFail}\");\n                    fail = true;\n                }\n                /*\n                    End of tes3mp change (major)\n                */\n\n                if (fail)\n                {\n                    // Failure sound\n                    static const std::string schools[] = {\n                        \"alteration\", \"conjuration\", \"destruction\", \"illusion\", \"mysticism\", \"restoration\"\n                    };\n\n                    MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n                    sndMgr->playSound3D(mCaster, \"Spell Failure \" + schools[school], 1.0f, 1.0f);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_SOUND packet every time a sound is made here\n                    */\n                    mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                    objectList->addObjectSound(mCaster, \"Spell Failure \" + schools[school], 1.0f, 1.0f);\n                    objectList->sendObjectSound();\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    return false;\n                }\n            }\n\n            // A power can be used once per 24h\n            if (spell->mData.mType == ESM::Spell::ST_Power)\n                stats.getSpells().usePower(spell);\n        }\n\n        if (!mManualSpell && mCaster == getPlayer() && spellIncreasesSkill(spell))\n            mCaster.getClass().skillUsageSucceeded(mCaster, spellSchoolToSkill(school), 0);\n\n        // A non-actor doesn't play its spell cast effects from a character controller, so play them here\n        if (!mCaster.getClass().isActor())\n            playSpellCastingEffects(spell->mEffects.mList);\n\n        inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self);\n\n        if (!mTarget.isEmpty())\n            inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch);\n\n        launchMagicBolt();\n\n        return true;\n    }\n\n    bool CastSpell::cast (const ESM::Ingredient* ingredient)\n    {\n        mId = ingredient->mId;\n        mStack = true;\n        mSourceName = ingredient->mName;\n\n        ESM::ENAMstruct effect;\n        effect.mEffectID = ingredient->mData.mEffectID[0];\n        effect.mSkill = ingredient->mData.mSkills[0];\n        effect.mAttribute = ingredient->mData.mAttributes[0];\n        effect.mRange = ESM::RT_Self;\n        effect.mArea = 0;\n\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n        const auto magicEffect = store.get<ESM::MagicEffect>().find(effect.mEffectID);\n        const MWMechanics::CreatureStats& creatureStats = mCaster.getClass().getCreatureStats(mCaster);\n\n        float x = (mCaster.getClass().getSkill(mCaster, ESM::Skill::Alchemy) +\n                    0.2f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()\n                    + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())\n                    * creatureStats.getFatigueTerm();\n\n        int roll = Misc::Rng::roll0to99();\n        if (roll > x)\n        {\n            // \"X has no effect on you\"\n            std::string message = store.get<ESM::GameSetting>().find(\"sNotifyMessage50\")->mValue.getString();\n            message = Misc::StringUtils::format(message, ingredient->mName);\n            MWBase::Environment::get().getWindowManager()->messageBox(message);\n            return false;\n        }\n\n        float magnitude = 0;\n        float y = roll / std::min(x, 100.f);\n        y *= 0.25f * x;\n        if (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)\n            effect.mDuration = 1;\n        else\n            effect.mDuration = static_cast<int>(y);\n        if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))\n        {\n            if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))\n                magnitude = floor((0.05f * y) / (0.1f * magicEffect->mData.mBaseCost));\n            else\n                magnitude = floor(y / (0.1f * magicEffect->mData.mBaseCost));\n            magnitude = std::max(1.f, magnitude);\n        }\n        else\n            magnitude = 1;\n\n        effect.mMagnMax = static_cast<int>(magnitude);\n        effect.mMagnMin = static_cast<int>(magnitude);\n\n        ESM::EffectList effects;\n        effects.mList.push_back(effect);\n\n        inflict(mCaster, mCaster, effects, ESM::RT_Self);\n\n        return true;\n    }\n\n    void CastSpell::playSpellCastingEffects(const std::string &spellid, bool enchantment)\n    {\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n        if (enchantment)\n        {\n            if (const auto spell = store.get<ESM::Enchantment>().search(spellid))\n                playSpellCastingEffects(spell->mEffects.mList);\n        }\n        else\n        {\n            if (const auto spell = store.get<ESM::Spell>().search(spellid))\n                playSpellCastingEffects(spell->mEffects.mList);\n        }\n    }\n\n    void CastSpell::playSpellCastingEffects(const std::vector<ESM::ENAMstruct>& effects)\n    {\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n        std::vector<std::string> addedEffects;\n        for (const ESM::ENAMstruct& effectData : effects)\n        {\n            const auto effect = store.get<ESM::MagicEffect>().find(effectData.mEffectID);\n\n            const ESM::Static* castStatic;\n\n            if (!effect->mCasting.empty())\n                castStatic = store.get<ESM::Static>().find (effect->mCasting);\n            else\n                castStatic = store.get<ESM::Static>().find (\"VFX_DefaultCast\");\n\n            // check if the effect was already added\n            if (std::find(addedEffects.begin(), addedEffects.end(), \"meshes\\\\\" + castStatic->mModel) != addedEffects.end())\n                continue;\n\n            MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);\n            if (animation)\n            {\n                animation->addEffect(\"meshes\\\\\" + castStatic->mModel, effect->mIndex, false, \"\", effect->mParticle);\n            }\n            else\n            {\n                // If the caster has no animation, add the effect directly to the effectManager\n                // We should scale it manually\n                osg::Vec3f bounds (MWBase::Environment::get().getWorld()->getHalfExtents(mCaster) * 2.f / Constants::UnitsPerFoot);\n                float scale = std::max({ bounds.x()/3.f, bounds.y()/3.f, bounds.z()/6.f });\n                float meshScale = !mCaster.getClass().isActor() ? mCaster.getCellRef().getScale() : 1.0f;\n                osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3());\n                MWBase::Environment::get().getWorld()->spawnEffect(\"meshes\\\\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale);\n            }\n\n            if (animation && !mCaster.getClass().isActor())\n                animation->addSpellCastGlow(effect);\n\n            static const std::string schools[] = {\n                \"alteration\", \"conjuration\", \"destruction\", \"illusion\", \"mysticism\", \"restoration\"\n            };\n\n            addedEffects.push_back(\"meshes\\\\\" + castStatic->mModel);\n\n            MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n            if(!effect->mCastSound.empty())\n                sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f);\n            else\n                sndMgr->playSound3D(mCaster, schools[effect->mData.mSchool]+\" cast\", 1.0f, 1.0f);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellcasting.hpp",
    "content": "#ifndef MWMECHANICS_SPELLCASTING_H\n#define MWMECHANICS_SPELLCASTING_H\n\n#include <components/esm/effectlist.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace ESM\n{\n    struct Spell;\n    struct Ingredient;\n    struct Potion;\n    struct EffectList;\n}\n\nnamespace MWMechanics\n{\n    struct EffectKey;\n\n    class CastSpell\n    {\n    private:\n        MWWorld::Ptr mCaster; // May be empty\n        MWWorld::Ptr mTarget; // May be empty\n\n        void playSpellCastingEffects(const std::vector<ESM::ENAMstruct>& effects);\n\n    public:\n        bool mStack{false};\n        std::string mId; // ID of spell, potion, item etc\n        std::string mSourceName; // Display name for spell, potion, etc\n        osg::Vec3f mHitPosition{0,0,0}; // Used for spawning area orb\n        bool mAlwaysSucceed{false}; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false)\n        bool mFromProjectile; // True if spell is cast by enchantment of some projectile (arrow, bolt or thrown weapon)\n        bool mManualSpell; // True if spell is casted from script and ignores some checks (mana level, success chance, etc.)\n\n    public:\n        CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const bool fromProjectile=false, const bool manualSpell=false);\n\n        bool cast (const ESM::Spell* spell);\n\n        /// @note mCaster must be an actor\n        /// @param launchProjectile If set to false, \"on target\" effects are directly applied instead of being launched as projectile originating from the caster.\n        bool cast (const MWWorld::Ptr& item, bool launchProjectile=true);\n\n        /// @note mCaster must be an NPC\n        bool cast (const ESM::Ingredient* ingredient);\n\n        bool cast (const ESM::Potion* potion);\n\n        /// @note Auto detects if spell, ingredient or potion\n        bool cast (const std::string& id);\n\n        void playSpellCastingEffects(const std::string &spellid, bool enchantment);\n\n        /// Launch a bolt with the given effects.\n        void launchMagicBolt ();\n\n        /// @note \\a target can be any type of object, not just actors.\n        /// @note \\a caster can be any type of object, or even an empty object.\n        void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster,\n                      const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false);\n\n        /// @note \\a caster can be any type of object, or even an empty object.\n        /// @return was the target suitable for the effect?\n        bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spelllist.cpp",
    "content": "#include \"spelllist.hpp\"\n\n#include <algorithm>\n\n#include <components/esm/loadspel.hpp>\n\n#include \"spells.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace\n{\n    template<class T>\n    const std::vector<std::string> getSpellList(const std::string& id)\n    {\n        return MWBase::Environment::get().getWorld()->getStore().get<T>().find(id)->mSpells.mList;\n    }\n\n    template<class T>\n    bool withBaseRecord(const std::string& id, const std::function<bool(std::vector<std::string>&)>& function)\n    {\n        T copy = *MWBase::Environment::get().getWorld()->getStore().get<T>().find(id);\n        bool changed = function(copy.mSpells.mList);\n        if(changed)\n            MWBase::Environment::get().getWorld()->createOverrideRecord(copy);\n        return changed;\n    }\n}\n\nnamespace MWMechanics\n{\n    SpellList::SpellList(const std::string& id, int type) : mId(id), mType(type) {}\n\n    bool SpellList::withBaseRecord(const std::function<bool(std::vector<std::string>&)>& function)\n    {\n        switch(mType)\n        {\n            case ESM::REC_CREA:\n                return ::withBaseRecord<ESM::Creature>(mId, function);\n            case ESM::REC_NPC_:\n                return ::withBaseRecord<ESM::NPC>(mId, function);\n            default:\n                throw std::logic_error(\"failed to update base record for \" + mId);\n        }\n    }\n\n    const std::vector<std::string> SpellList::getSpells() const\n    {\n        switch(mType)\n        {\n            case ESM::REC_CREA:\n                return getSpellList<ESM::Creature>(mId);\n            case ESM::REC_NPC_:\n                return getSpellList<ESM::NPC>(mId);\n            default:\n                throw std::logic_error(\"failed to get spell list for \" + mId);\n        }\n    }\n\n    const ESM::Spell* SpellList::getSpell(const std::string& id)\n    {\n        return MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(id);\n    }\n\n    void SpellList::add (const ESM::Spell* spell)\n    {\n        auto& id = spell->mId;\n        bool changed = withBaseRecord([&] (auto& spells)\n        {\n            for(const auto& it : spells)\n            {\n                if(Misc::StringUtils::ciEqual(id, it))\n                    return false;\n            }\n            spells.push_back(id);\n            return true;\n        });\n        if(changed)\n        {\n            for(auto listener : mListeners)\n                listener->addSpell(spell);\n        }\n    }\n\n    void SpellList::remove (const ESM::Spell* spell)\n    {\n        auto& id = spell->mId;\n        bool changed = withBaseRecord([&] (auto& spells)\n        {\n            for(auto it = spells.begin(); it != spells.end(); it++)\n            {\n                if(Misc::StringUtils::ciEqual(id, *it))\n                {\n                    spells.erase(it);\n                    return true;\n                }\n            }\n            return false;\n        });\n        if(changed)\n        {\n            for(auto listener : mListeners)\n                listener->removeSpell(spell);\n        }\n    }\n\n    void SpellList::removeAll (const std::vector<std::string>& ids)\n    {\n        bool changed = withBaseRecord([&] (auto& spells)\n        {\n            const auto it = std::remove_if(spells.begin(), spells.end(), [&] (const auto& spell)\n            {\n                const auto isSpell = [&] (const auto& id) { return Misc::StringUtils::ciEqual(spell, id); };\n                return ids.end() != std::find_if(ids.begin(), ids.end(), isSpell);\n            });\n            if (it == spells.end())\n                return false;\n            spells.erase(it, spells.end());\n            return true;\n        });\n        if(changed)\n        {\n            for(auto listener : mListeners)\n            {\n                for(auto& id : ids)\n                {\n                    const auto spell = getSpell(id);\n                    listener->removeSpell(spell);\n                }\n            }\n        }\n    }\n\n    void SpellList::clear()\n    {\n        bool changed = withBaseRecord([] (auto& spells)\n        {\n            if(spells.empty())\n                return false;\n            spells.clear();\n            return true;\n        });\n        if(changed)\n        {\n            for(auto listener : mListeners)\n                listener->removeAllSpells();\n        }\n    }\n\n    void SpellList::addListener(Spells* spells)\n    {\n        if (std::find(mListeners.begin(), mListeners.end(), spells) != mListeners.end())\n            return;\n        mListeners.push_back(spells);\n    }\n\n    void SpellList::removeListener(Spells* spells)\n    {\n        const auto it = std::find(mListeners.begin(), mListeners.end(), spells);\n        if (it != mListeners.end())\n            mListeners.erase(it);\n    }\n\n    void SpellList::updateListener(Spells* before, Spells* after)\n    {\n        const auto it = std::find(mListeners.begin(), mListeners.end(), before);\n        if (it == mListeners.end())\n            return mListeners.push_back(after);\n        *it = after;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spelllist.hpp",
    "content": "#ifndef GAME_MWMECHANICS_SPELLLIST_H\n#define GAME_MWMECHANICS_SPELLLIST_H\n\n#include <functional>\n#include <map>\n#include <string>\n#include <set>\n#include <vector>\n\n#include <components/esm/loadspel.hpp>\n\nnamespace ESM\n{\n    struct SpellState;\n}\n\nnamespace MWMechanics\n{\n    struct SpellParams\n    {\n        std::map<int, float> mEffectRands; // <effect index, normalised random magnitude>\n        std::set<int> mPurgedEffects; // indices of purged effects\n    };\n\n    class Spells;\n\n    /// Multiple instances of the same actor share the same spell list in Morrowind.\n    /// The most obvious result of this is that adding a spell or ability to one instance adds it to all instances.\n    /// @note The original game will only update visual effects associated with any added abilities for the originally targeted actor,\n    ///       changing cells applies the update to all actors.\n    /// Aside from sharing the same active spell list, changes made to this list are also written to the actor's base record.\n    /// Interestingly, it is not just scripted changes that are persisted to the base record. Curing one instance's disease will cure all instances.\n    /// @note The original game is inconsistent in persisting this example;\n    ///       saving and loading the game might reapply the cured disease depending on which instance was cured.\n    class SpellList\n    {\n            const std::string mId;\n            const int mType;\n            std::vector<Spells*> mListeners;\n\n            bool withBaseRecord(const std::function<bool(std::vector<std::string>&)>& function);\n        public:\n            SpellList(const std::string& id, int type);\n\n            /// Get spell from ID, throws exception if not found\n            static const ESM::Spell* getSpell(const std::string& id);\n\n            void add (const ESM::Spell* spell);\n            ///< Adding a spell that is already listed in *this is a no-op.\n\n            void remove (const ESM::Spell* spell);\n\n            void removeAll(const std::vector<std::string>& spells);\n\n            void clear();\n            ///< Remove all spells of all types.\n\n            void addListener(Spells* spells);\n\n            void removeListener(Spells* spells);\n\n            void updateListener(Spells* before, Spells* after);\n\n            const std::vector<std::string> getSpells() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellpriority.cpp",
    "content": "#include \"spellpriority.hpp\"\n#include \"weaponpriority.hpp\"\n\n#include <components/esm/loadench.hpp>\n#include <components/esm/loadmgef.hpp>\n#include <components/esm/loadspel.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"spellresistance.hpp\"\n#include \"weapontype.hpp\"\n#include \"summoning.hpp\"\n#include \"spellutil.hpp\"\n\nnamespace\n{\n    int numEffectsToDispel (const MWWorld::Ptr& actor, int effectFilter=-1, bool negative = true)\n    {\n        int toCure=0;\n        const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells();\n        for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it)\n        {\n            // if the effect filter is not specified, take in account only spells effects. Leave potions, enchanted items etc.\n            if (effectFilter == -1)\n            {\n                const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first);\n                if (!spell || spell->mData.mType != ESM::Spell::ST_Spell)\n                    continue;\n            }\n\n            const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second;\n            for (std::vector<MWMechanics::ActiveSpells::ActiveEffect>::const_iterator effectIt = params.mEffects.begin();\n                effectIt != params.mEffects.end(); ++effectIt)\n            {\n                int effectId = effectIt->mEffectId;\n                if (effectFilter != -1 && effectId != effectFilter)\n                    continue;\n                const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectId);\n\n                if (effectIt->mDuration <= 3) // Don't attempt to dispel if effect runs out shortly anyway\n                    continue;\n\n                if (negative && magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)\n                    ++toCure;\n\n                if (!negative && !(magicEffect->mData.mFlags & ESM::MagicEffect::Harmful))\n                    ++toCure;\n            }\n        }\n        return toCure;\n    }\n\n    float getSpellDuration (const MWWorld::Ptr& actor, const std::string& spellId)\n    {\n        float duration = 0;\n        const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells();\n        for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it)\n        {\n            if (it->first != spellId)\n                continue;\n\n            const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second;\n            for (std::vector<MWMechanics::ActiveSpells::ActiveEffect>::const_iterator effectIt = params.mEffects.begin();\n                effectIt != params.mEffects.end(); ++effectIt)\n            {\n                if (effectIt->mDuration > duration)\n                    duration = effectIt->mDuration;\n            }\n        }\n        return duration;\n    }\n}\n\nnamespace MWMechanics\n{\n    int getRangeTypes (const ESM::EffectList& effects)\n    {\n        int types = 0;\n        for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)\n        {\n            if (it->mRange == ESM::RT_Self)\n                types |= RangeTypes::Self;\n            else if (it->mRange == ESM::RT_Touch)\n                types |= RangeTypes::Touch;\n            else if (it->mRange == ESM::RT_Target)\n                types |= RangeTypes::Target;\n        }\n        return types;\n    }\n\n    float ratePotion (const MWWorld::Ptr &item, const MWWorld::Ptr& actor)\n    {\n        if (item.getTypeName() != typeid(ESM::Potion).name())\n            return 0.f;\n\n        const ESM::Potion* potion = item.get<ESM::Potion>()->mBase;\n        return rateEffects(potion->mEffects, actor, MWWorld::Ptr());\n    }\n\n    float rateSpell(const ESM::Spell *spell, const MWWorld::Ptr &actor, const MWWorld::Ptr& enemy)\n    {\n        const CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n\n        float successChance = MWMechanics::getSpellSuccessChance(spell, actor);\n        if (successChance == 0.f)\n            return 0.f;\n\n        if (spell->mData.mType != ESM::Spell::ST_Spell)\n            return 0.f;\n\n        // Don't make use of racial bonus spells, like MW. Can be made optional later\n        if (actor.getClass().isNpc())\n        {\n            std::string raceid = actor.get<ESM::NPC>()->mBase->mRace;\n            const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceid);\n            if (race->mPowers.exists(spell->mId))\n                return 0.f;\n        }\n\n        // Spells don't stack, so early out if the spell is still active on the target\n        int types = getRangeTypes(spell->mEffects);\n        if ((types & Self) && stats.getActiveSpells().isSpellActive(spell->mId))\n            return 0.f;\n        if ( ((types & Touch) || (types & Target)) && enemy.getClass().getCreatureStats(enemy).getActiveSpells().isSpellActive(spell->mId))\n            return 0.f;\n\n        return rateEffects(spell->mEffects, actor, enemy) * (successChance / 100.f);\n    }\n\n    float rateMagicItem(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor, const MWWorld::Ptr& enemy)\n    {\n        if (ptr.getClass().getEnchantment(ptr).empty())\n            return 0.f;\n\n        const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr));\n\n        // Spells don't stack, so early out if the spell is still active on the target\n        int types = getRangeTypes(enchantment->mEffects);\n        if ((types & Self) && actor.getClass().getCreatureStats(actor).getActiveSpells().isSpellActive(ptr.getCellRef().getRefId()))\n            return 0.f;\n\n        if (types & (Touch|Target) && getSpellDuration(enemy, ptr.getCellRef().getRefId()) > 3)\n            return 0.f;\n\n        if (enchantment->mData.mType == ESM::Enchantment::CastOnce)\n        {\n            return rateEffects(enchantment->mEffects, actor, enemy);\n        }\n        else if (enchantment->mData.mType == ESM::Enchantment::WhenUsed)\n        {\n            MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);\n\n            // Creatures can not wear armor/clothing, so allow creatures to use non-equipped items, \n            if (actor.getClass().isNpc() && !store.isEquipped(ptr))\n                return 0.f;\n\n            int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);\n\n            if (ptr.getCellRef().getEnchantmentCharge() != -1\n               && ptr.getCellRef().getEnchantmentCharge() < castCost)\n                return 0.f;\n\n            float rating = rateEffects(enchantment->mEffects, actor, enemy);\n\n            rating *= 1.25f; // prefer rechargable magic items over spells\n            return rating;\n        }\n\n        return 0.f;\n    }\n\n    float rateEffect(const ESM::ENAMstruct &effect, const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy)\n    {\n        // NOTE: enemy may be empty\n\n        float rating = 1;\n        switch (effect.mEffectID)\n        {\n        case ESM::MagicEffect::Soultrap:\n        case ESM::MagicEffect::AlmsiviIntervention:\n        case ESM::MagicEffect::DivineIntervention:\n        case ESM::MagicEffect::CalmHumanoid:\n        case ESM::MagicEffect::CalmCreature:\n        case ESM::MagicEffect::FrenzyHumanoid:\n        case ESM::MagicEffect::FrenzyCreature:\n        case ESM::MagicEffect::DemoralizeHumanoid:\n        case ESM::MagicEffect::DemoralizeCreature:\n        case ESM::MagicEffect::RallyHumanoid:\n        case ESM::MagicEffect::RallyCreature:\n        case ESM::MagicEffect::Charm:\n        case ESM::MagicEffect::DetectAnimal:\n        case ESM::MagicEffect::DetectEnchantment:\n        case ESM::MagicEffect::DetectKey:\n        case ESM::MagicEffect::Telekinesis:\n        case ESM::MagicEffect::Mark:\n        case ESM::MagicEffect::Recall:\n        case ESM::MagicEffect::Jump:\n        case ESM::MagicEffect::WaterBreathing:\n        case ESM::MagicEffect::SwiftSwim:\n        case ESM::MagicEffect::WaterWalking:\n        case ESM::MagicEffect::SlowFall:\n        case ESM::MagicEffect::Light:\n        case ESM::MagicEffect::Lock:\n        case ESM::MagicEffect::Open:\n        case ESM::MagicEffect::TurnUndead:\n        case ESM::MagicEffect::WeaknessToCommonDisease:\n        case ESM::MagicEffect::WeaknessToBlightDisease:\n        case ESM::MagicEffect::WeaknessToCorprusDisease:\n        case ESM::MagicEffect::CureCommonDisease:\n        case ESM::MagicEffect::CureBlightDisease:\n        case ESM::MagicEffect::CureCorprusDisease:\n        case ESM::MagicEffect::ResistBlightDisease:\n        case ESM::MagicEffect::ResistCommonDisease:\n        case ESM::MagicEffect::ResistCorprusDisease:\n        case ESM::MagicEffect::Invisibility:\n        case ESM::MagicEffect::Chameleon:\n        case ESM::MagicEffect::NightEye:\n        case ESM::MagicEffect::Vampirism:\n        case ESM::MagicEffect::StuntedMagicka:\n        case ESM::MagicEffect::ExtraSpell:\n        case ESM::MagicEffect::RemoveCurse:\n        case ESM::MagicEffect::CommandCreature:\n        case ESM::MagicEffect::CommandHumanoid:\n            return 0.f;\n\n        case ESM::MagicEffect::Blind:\n            {\n                if (enemy.isEmpty())\n                    return 0.f;\n\n                const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);\n\n                // Enemy can't attack\n                if (stats.isParalyzed() || stats.getKnockedDown())\n                    return 0.f;\n\n                // Enemy doesn't attack\n                if (stats.getDrawState() != MWMechanics::DrawState_Weapon)\n                    return 0.f;\n\n                break;\n            }\n\n        case ESM::MagicEffect::Sound:\n            {\n                if (enemy.isEmpty())\n                    return 0.f;\n\n                const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);\n\n                // Enemy can't cast spells\n                if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0)\n                    return 0.f;\n\n                if (stats.isParalyzed() || stats.getKnockedDown())\n                    return 0.f;\n\n                // Enemy doesn't cast spells\n                if (stats.getDrawState() != MWMechanics::DrawState_Spell)\n                    return 0.f;\n\n                break;\n            }\n\n        case ESM::MagicEffect::Silence:\n            {\n                if (enemy.isEmpty())\n                    return 0.f;\n\n                const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);\n\n                // Enemy can't cast spells\n                if (stats.isParalyzed() || stats.getKnockedDown())\n                    return 0.f;\n\n                // Enemy doesn't cast spells\n                if (stats.getDrawState() != MWMechanics::DrawState_Spell)\n                    return 0.f;\n                break;\n            }\n\n        case ESM::MagicEffect::RestoreAttribute:\n            return 0.f; // TODO: implement based on attribute damage\n        case ESM::MagicEffect::RestoreSkill:\n            return 0.f; // TODO: implement based on skill damage\n\n        case ESM::MagicEffect::ResistFire:\n        case ESM::MagicEffect::ResistFrost:\n        case ESM::MagicEffect::ResistMagicka:\n        case ESM::MagicEffect::ResistNormalWeapons:\n        case ESM::MagicEffect::ResistParalysis:\n        case ESM::MagicEffect::ResistPoison:\n        case ESM::MagicEffect::ResistShock:\n        case ESM::MagicEffect::SpellAbsorption:\n        case ESM::MagicEffect::Reflect:\n            return 0.f; // probably useless since we don't know in advance what the enemy will cast\n\n        // don't cast these for now as they would make the NPC cast the same effect over and over again, especially when they have potions\n        case ESM::MagicEffect::FortifyAttribute:\n        case ESM::MagicEffect::FortifyHealth:\n        case ESM::MagicEffect::FortifyMagicka:\n        case ESM::MagicEffect::FortifyFatigue:\n        case ESM::MagicEffect::FortifySkill:\n        case ESM::MagicEffect::FortifyMaximumMagicka:\n        case ESM::MagicEffect::FortifyAttack:\n            return 0.f;\n\n        case ESM::MagicEffect::Burden:\n            {\n                if (enemy.isEmpty())\n                    return 0.f;\n\n                // Ignore enemy without inventory\n                if (!enemy.getClass().hasInventoryStore(enemy))\n                    return 0.f;\n\n                // burden makes sense only to overburden an enemy\n                float burden = enemy.getClass().getEncumbrance(enemy) - enemy.getClass().getCapacity(enemy);\n                if (burden > 0)\n                    return 0.f;\n\n                if ((effect.mMagnMin + effect.mMagnMax)/2.f > -burden)\n                    rating *= 3;\n                else\n                    return 0.f;\n\n                break;\n            }\n\n        case ESM::MagicEffect::Feather:\n            {\n                // Ignore actors without inventory\n                if (!actor.getClass().hasInventoryStore(actor))\n                    return 0.f;\n\n                // feather makes sense only for overburden actors\n                float burden = actor.getClass().getEncumbrance(actor) - actor.getClass().getCapacity(actor);\n                if (burden <= 0)\n                    return 0.f;\n\n                if ((effect.mMagnMin + effect.mMagnMax)/2.f >= burden)\n                    rating *= 3;\n                else\n                    return 0.f;\n\n                break;\n            }\n\n        case ESM::MagicEffect::Levitate:\n            return 0.f; // AI isn't designed to take advantage of this, and could be perceived as unfair anyway\n        case ESM::MagicEffect::BoundBoots:\n        case ESM::MagicEffect::BoundHelm:\n            if (actor.getClass().isNpc())\n            {\n                // Beast races can't wear helmets or boots\n                std::string raceid = actor.get<ESM::NPC>()->mBase->mRace;\n                const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceid);\n                if (race->mData.mFlags & ESM::Race::Beast)\n                    return 0.f;\n            }\n            else\n                return 0.f;\n\n            break;\n        // Creatures can not wear armor\n        case ESM::MagicEffect::BoundCuirass:\n        case ESM::MagicEffect::BoundGloves:\n            if (!actor.getClass().isNpc())\n                return 0.f;\n            break;\n\n        case ESM::MagicEffect::BoundLongbow:\n            // AI should not summon the bow if there is no suitable ammo.\n            if (rateAmmo(actor, enemy, getWeaponType(ESM::Weapon::MarksmanBow)->mAmmoType) <= 0.f)\n                return 0.f;\n            break;\n\n        case ESM::MagicEffect::RestoreHealth:\n        case ESM::MagicEffect::RestoreMagicka:\n        case ESM::MagicEffect::RestoreFatigue:\n            if (effect.mRange == ESM::RT_Self)\n            {\n                int priority = 1;\n                if (effect.mEffectID == ESM::MagicEffect::RestoreHealth)\n                    priority = 10;\n                const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n                const DynamicStat<float>& current = stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth);\n                // NB: this currently assumes the hardcoded magic effect flags are used\n                const float magnitude = (effect.mMagnMin + effect.mMagnMax)/2.f;\n                const float toHeal = magnitude * std::max(1, effect.mDuration);\n                // Effect doesn't heal more than we need, *or* we are below 1/2 health\n                if (current.getModified() - current.getCurrent() > toHeal\n                        || current.getCurrent() < current.getModified()*0.5)\n                {\n                    return 10000.f * priority\n                            - (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion\n                }\n                else\n                    return -10000.f * priority; // Save for later\n            }\n            break;\n\n        case ESM::MagicEffect::Dispel:\n        {\n            int numPositive = 0;\n            int numNegative = 0;\n            int diff = 0;\n\n            if (effect.mRange == ESM::RT_Self)\n            {\n                numPositive = numEffectsToDispel(actor, -1, false);\n                numNegative = numEffectsToDispel(actor);\n\n                diff = numNegative - numPositive;\n            }\n            else\n            {\n                if (enemy.isEmpty())\n                    return 0.f;\n\n                numPositive = numEffectsToDispel(enemy, -1, false);\n                numNegative = numEffectsToDispel(enemy);\n\n                diff = numPositive - numNegative;\n\n                // if rating < 0 here, the spell will be considered as negative later\n                rating *= -1;\n            }\n\n            if (diff <= 0)\n                return 0.f;\n\n            rating *= (diff) / 5.f;\n\n            break;\n        }\n\n        // Prefer Cure effects over Dispel, because Dispel also removes positive effects\n        case ESM::MagicEffect::CureParalyzation:\n            return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Paralyze);\n\n        case ESM::MagicEffect::CurePoison:\n            return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Poison);\n        case ESM::MagicEffect::DisintegrateArmor:\n            {\n                if (enemy.isEmpty())\n                    return 0.f;\n\n                // Ignore enemy without inventory\n                if (!enemy.getClass().hasInventoryStore(enemy))\n                    return 0.f;\n\n                MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy);\n\n                // According to UESP\n                static const int armorSlots[] = {\n                    MWWorld::InventoryStore::Slot_CarriedLeft,\n                    MWWorld::InventoryStore::Slot_Cuirass,\n                    MWWorld::InventoryStore::Slot_LeftPauldron,\n                    MWWorld::InventoryStore::Slot_RightPauldron,\n                    MWWorld::InventoryStore::Slot_LeftGauntlet,\n                    MWWorld::InventoryStore::Slot_RightGauntlet,\n                    MWWorld::InventoryStore::Slot_Helmet,\n                    MWWorld::InventoryStore::Slot_Greaves,\n                    MWWorld::InventoryStore::Slot_Boots\n                };\n\n                bool enemyHasArmor = false;\n\n                // Ignore enemy without armor\n                for (unsigned int i=0; i<sizeof(armorSlots)/sizeof(int); ++i)\n                {\n                    MWWorld::ContainerStoreIterator item = inv.getSlot(armorSlots[i]);\n\n                    if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor))\n                    {\n                        enemyHasArmor = true;\n                        break;\n                    }\n                }\n\n                if (!enemyHasArmor)\n                    return 0.f;\n\n                break;\n            }\n\n        case ESM::MagicEffect::DisintegrateWeapon:\n            {\n                if (enemy.isEmpty())\n                    return 0.f;\n\n                // Ignore enemy without inventory\n                if (!enemy.getClass().hasInventoryStore(enemy))\n                    return 0.f;\n\n                MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy);\n                MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n\n                // Ignore enemy without weapons\n                if (item == inv.end() || (item.getType() != MWWorld::ContainerStore::Type_Weapon))\n                    return 0.f;\n\n                break;\n            }\n\n        case ESM::MagicEffect::DamageAttribute:\n        case ESM::MagicEffect::DrainAttribute:\n            if (!enemy.isEmpty() && enemy.getClass().getCreatureStats(enemy).getAttribute(effect.mAttribute).getModified() <= 0)\n                return 0.f;\n            {\n                if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length)\n                {\n                    const float attributePriorities[ESM::Attribute::Length] = {\n                        1.0f, // Strength\n                        0.5f, // Intelligence\n                        0.6f, // Willpower\n                        0.7f, // Agility\n                        0.5f, // Speed\n                        0.8f, // Endurance\n                        0.7f, // Personality\n                        0.3f // Luck\n                    };\n                    rating *= attributePriorities[effect.mAttribute];\n                }\n            }\n            break;\n\n        case ESM::MagicEffect::DamageSkill:\n        case ESM::MagicEffect::DrainSkill:\n            if (enemy.isEmpty() || !enemy.getClass().isNpc())\n                return 0.f;\n            if (enemy.getClass().getSkill(enemy, effect.mSkill) <= 0)\n                return 0.f;\n            break;\n\n        default:\n            break;\n        }\n\n        // Allow only one summoned creature at time\n        if (isSummoningEffect(effect.mEffectID))\n        {\n            MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);\n\n            if (!creatureStats.getSummonedCreatureMap().empty())\n                return 0.f;\n        }\n\n        // Underwater casting not possible\n        if (effect.mRange == ESM::RT_Target)\n        {\n            if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f))\n                return 0.f;\n\n            if (enemy.isEmpty())\n                return 0.f;\n\n            if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))\n                return 0.f;\n        }\n\n        const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);\n        if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)\n        {\n            rating *= -1.f;\n\n            if (enemy.isEmpty())\n                return 0.f;\n\n            // Check resistance for harmful effects\n            CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);\n\n            float resistance = MWMechanics::getEffectResistanceAttribute(effect.mEffectID, &stats.getMagicEffects());\n\n            rating *= (1.f - std::min(resistance, 100.f) / 100.f);\n        }\n\n        // for harmful no-magnitude effects (e.g. silence) check if enemy is already has them\n        // for non-harmful no-magnitude effects (e.g. bound items) check if actor is already has them\n        if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)\n        {\n            if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)\n            {\n                CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);\n\n                if (stats.getMagicEffects().get(effect.mEffectID).getMagnitude() > 0)\n                    return 0.f;\n            }\n            else\n            {\n                CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n\n                if (stats.getMagicEffects().get(effect.mEffectID).getMagnitude() > 0)\n                    return 0.f;\n            }\n        }\n\n        rating *= calcEffectCost(effect, magicEffect);\n\n        // Currently treating all \"on target\" or \"on touch\" effects to target the enemy actor.\n        // Combat AI is egoistic, so doesn't consider applying positive effects to friendly actors.\n        if (effect.mRange != ESM::RT_Self)\n            rating *= -1.f;\n\n        return rating;\n    }\n\n    float rateEffects(const ESM::EffectList &list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)\n    {\n        // NOTE: enemy may be empty\n\n        float rating = 0.f;\n        float ratingMult = 1.f; // NB: this multiplier is applied to the effect rating, not the final rating\n\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        static const float fAIMagicSpellMult = gmst.find(\"fAIMagicSpellMult\")->mValue.getFloat();\n        static const float fAIRangeMagicSpellMult = gmst.find(\"fAIRangeMagicSpellMult\")->mValue.getFloat();\n\n        for (std::vector<ESM::ENAMstruct>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)\n        {\n            ratingMult = (it->mRange == ESM::RT_Target) ? fAIRangeMagicSpellMult : fAIMagicSpellMult;\n\n            rating += rateEffect(*it, actor, enemy) * ratingMult;\n        }\n        return rating;\n    }\n\n    float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)\n    {\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        static const float fAIMagicSpellMult = gmst.find(\"fAIMagicSpellMult\")->mValue.getFloat();\n        static const float fAIRangeMagicSpellMult = gmst.find(\"fAIRangeMagicSpellMult\")->mValue.getFloat();\n\n        float mult = fAIMagicSpellMult;\n\n        for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =\n             spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)\n        {\n            if (effectIt->mRange == ESM::RT_Target)\n            {\n                if (!MWBase::Environment::get().getWorld()->isSwimming(enemy))\n                    mult = fAIRangeMagicSpellMult;\n                else\n                    mult = 0.0f;\n                break;\n            }\n        }\n\n        return MWMechanics::getSpellSuccessChance(spell, actor) * mult;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellpriority.hpp",
    "content": "#ifndef OPENMW_SPELL_PRIORITY_H\n#define OPENMW_SPELL_PRIORITY_H\n\nnamespace ESM\n{\n    struct Spell;\n    struct EffectList;\n    struct ENAMstruct;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    // RangeTypes using bitflags to allow multiple range types, as can be the case with spells having multiple effects.\n    enum RangeTypes\n    {\n        Self = 0x1,\n        Touch = 0x10,\n        Target = 0x100\n    };\n\n    int getRangeTypes (const ESM::EffectList& effects);\n\n    float rateSpell (const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n    float rateMagicItem (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n    float ratePotion (const MWWorld::Ptr& item, const MWWorld::Ptr &actor);\n\n    /// @note target may be empty\n    float rateEffect (const ESM::ENAMstruct& effect, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n    /// @note target may be empty\n    float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n\n    float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellresistance.cpp",
    "content": "#include \"spellresistance.hpp\"\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"spellutil.hpp\"\n\nnamespace MWMechanics\n{\n\n    float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,\n                              const ESM::Spell* spell, const MagicEffects* effects)\n    {\n        if (!actor.getClass().isActor())\n            return 1;\n\n        float resistance = getEffectResistance(effectId, actor, caster, spell, effects);\n        return 1 - resistance / 100.f;\n    }\n\n    float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,\n                               const ESM::Spell* spell, const MagicEffects* effects)\n    {\n        // Effects with no resistance attribute belonging to them can not be resisted\n        if (ESM::MagicEffect::getResistanceEffect(effectId) == -1)\n            return 0.f;\n\n        const auto magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectId);\n\n        const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n        const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects();\n        if (effects)\n            magicEffects = effects;\n\n        float resistance = getEffectResistanceAttribute(effectId, magicEffects);\n\n        float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();\n        float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();\n        float x = (willpower + 0.1f * luck) * stats.getFatigueTerm();\n\n        // This makes spells that are easy to cast harder to resist and vice versa\n        float castChance = 100.f;\n        if (spell != nullptr && !caster.isEmpty() && caster.getClass().isActor())\n            castChance = getSpellSuccessChance(spell, caster, nullptr, false, false); // Uncapped casting chance\n        if (castChance > 0)\n            x *= 50 / castChance;\n\n        float roll = Misc::Rng::rollClosedProbability() * 100;\n        if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)\n            roll -= resistance;\n\n        if (x <= roll)\n            x = 0;\n        else\n        {\n            if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)\n                x = 100;\n            else\n                x = roll / std::min(x, 100.f);\n        }\n\n        x = std::min(x + resistance, 100.f);\n        return x;\n    }\n\n    float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects)\n    {\n        short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId);\n        short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId);\n\n        float resistance = 0;\n        if (resistanceEffect != -1)\n            resistance += actorEffects->get(resistanceEffect).getMagnitude();\n        if (weaknessEffect != -1)\n            resistance -= actorEffects->get(weaknessEffect).getMagnitude();\n\n        if (effectId == ESM::MagicEffect::FireDamage)\n            resistance += actorEffects->get(ESM::MagicEffect::FireShield).getMagnitude();\n        if (effectId == ESM::MagicEffect::ShockDamage)\n            resistance += actorEffects->get(ESM::MagicEffect::LightningShield).getMagnitude();\n        if (effectId == ESM::MagicEffect::FrostDamage)\n            resistance += actorEffects->get(ESM::MagicEffect::FrostShield).getMagnitude();\n\n        return resistance;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellresistance.hpp",
    "content": "#ifndef MWMECHANICS_SPELLRESISTANCE_H\n#define MWMECHANICS_SPELLRESISTANCE_H\n\nnamespace ESM\n{\n    struct Spell;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    class MagicEffects;\n\n    /// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional).\n    /// @return effect multiplier from 0 to 2.  (100% net resistance to 100% net weakness)\n    /// @param effects Override the actor's current magicEffects. Useful if there are effects currently\n    ///                being applied (but not applied yet) that should also be considered.\n    float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,\n                              const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr);\n\n    /// Get the effective resistance against an effect casted by the given actor in the given spell (optional).\n    /// @return >=100 for fully resisted. can also return negative value for damage amplification.\n    /// @param effects Override the actor's current magicEffects. Useful if there are effects currently\n    ///                being applied (but not applied yet) that should also be considered.\n    float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,\n                               const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr);\n\n    /// Get the resistance attribute against an effect for a given actor. This will add together\n    /// ResistX and Weakness to X effects relevant against the given effect.\n    float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spells.cpp",
    "content": "#include \"spells.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/loadspel.hpp>\n#include <components/esm/spellstate.hpp>\n#include <components/misc/rng.hpp>\n#include <components/misc/stringops.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"actorutil.hpp\"\n#include \"creaturestats.hpp\"\n#include \"magiceffects.hpp\"\n#include \"stat.hpp\"\n\nnamespace MWMechanics\n{\n    Spells::Spells()\n        : mSpellsChanged(false)\n    {\n    }\n\n    Spells::Spells(const Spells& spells) : mSpellList(spells.mSpellList), mSpells(spells.mSpells),\n        mSelectedSpell(spells.mSelectedSpell), mUsedPowers(spells.mUsedPowers),\n        mSpellsChanged(spells.mSpellsChanged), mEffects(spells.mEffects), mSourcedEffects(spells.mSourcedEffects)\n    {\n        if(mSpellList)\n            mSpellList->addListener(this);\n    }\n\n    Spells::Spells(Spells&& spells) : mSpellList(std::move(spells.mSpellList)), mSpells(std::move(spells.mSpells)),\n        mSelectedSpell(std::move(spells.mSelectedSpell)), mUsedPowers(std::move(spells.mUsedPowers)),\n        mSpellsChanged(std::move(spells.mSpellsChanged)), mEffects(std::move(spells.mEffects)),\n        mSourcedEffects(std::move(spells.mSourcedEffects))\n    {\n        if (mSpellList)\n            mSpellList->updateListener(&spells, this);\n    }\n\n    std::map<const ESM::Spell*, SpellParams>::const_iterator Spells::begin() const\n    {\n        return mSpells.begin();\n    }\n\n    std::map<const ESM::Spell*, SpellParams>::const_iterator Spells::end() const\n    {\n        return mSpells.end();\n    }\n\n    void Spells::rebuildEffects() const\n    {\n        mEffects = MagicEffects();\n        mSourcedEffects.clear();\n\n        for (const auto& iter : mSpells)\n        {\n            const ESM::Spell *spell = iter.first;\n\n            if (spell->mData.mType==ESM::Spell::ST_Ability || spell->mData.mType==ESM::Spell::ST_Blight ||\n                spell->mData.mType==ESM::Spell::ST_Disease || spell->mData.mType==ESM::Spell::ST_Curse)\n            {\n                int i=0;\n                for (const auto& effect : spell->mEffects.mList)\n                {\n                    if (iter.second.mPurgedEffects.find(i) != iter.second.mPurgedEffects.end())\n                    {\n                        ++i;\n                        continue; // effect was purged\n                    }\n\n                    float random = 1.f;\n                    if (iter.second.mEffectRands.find(i) != iter.second.mEffectRands.end())\n                        random = iter.second.mEffectRands.at(i);\n\n                    float magnitude = effect.mMagnMin + (effect.mMagnMax - effect.mMagnMin) * random;\n                    mEffects.add (effect, magnitude);\n                    mSourcedEffects[spell].add(MWMechanics::EffectKey(effect), magnitude);\n\n                    ++i;\n                }\n            }\n        }\n    }\n\n    bool Spells::hasSpell(const std::string &spell) const\n    {\n        return hasSpell(SpellList::getSpell(spell));\n    }\n\n    bool Spells::hasSpell(const ESM::Spell *spell) const\n    {\n        return mSpells.find(spell) != mSpells.end();\n    }\n\n    void Spells::add (const ESM::Spell* spell)\n    {\n        mSpellList->add(spell);\n    }\n\n    void Spells::add (const std::string& spellId)\n    {\n        add(SpellList::getSpell(spellId));\n    }\n\n    void Spells::addSpell(const ESM::Spell* spell)\n    {\n        if (mSpells.find (spell)==mSpells.end())\n        {\n            std::map<int, float> random;\n\n            // Determine the random magnitudes (unless this is a castable spell, in which case\n            // they will be determined when the spell is cast)\n            if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell)\n            {\n                for (unsigned int i=0; i<spell->mEffects.mList.size();++i)\n                {\n                    if (spell->mEffects.mList[i].mMagnMin != spell->mEffects.mList[i].mMagnMax)\n                    {\n                        int delta = spell->mEffects.mList[i].mMagnMax - spell->mEffects.mList[i].mMagnMin;\n                        random[i] = Misc::Rng::rollDice(delta + 1) / static_cast<float>(delta);\n                    }\n                }\n            }\n\n            SpellParams params;\n            params.mEffectRands = random;\n            mSpells.emplace(spell, params);\n            mSpellsChanged = true;\n        }\n    }\n\n    void Spells::remove (const std::string& spellId)\n    {\n        const auto spell = SpellList::getSpell(spellId);\n        removeSpell(spell);\n        mSpellList->remove(spell);\n\n        if (spellId==mSelectedSpell)\n            mSelectedSpell.clear();\n    }\n\n    void Spells::removeSpell(const ESM::Spell* spell)\n    {\n        const auto it = mSpells.find(spell);\n        if(it != mSpells.end())\n        {\n            mSpells.erase(it);\n            mSpellsChanged = true;\n        }\n    }\n\n    MagicEffects Spells::getMagicEffects() const\n    {\n        if (mSpellsChanged) {\n            rebuildEffects();\n            mSpellsChanged = false;\n        }\n        return mEffects;\n    }\n\n    void Spells::removeAllSpells()\n    {\n        mSpells.clear();\n        mSpellsChanged = true;\n    }\n\n    void Spells::clear(bool modifyBase)\n    {\n        removeAllSpells();\n        if(modifyBase)\n            mSpellList->clear();\n    }\n\n    void Spells::setSelectedSpell (const std::string& spellId)\n    {\n        mSelectedSpell = spellId;\n    }\n\n    const std::string Spells::getSelectedSpell() const\n    {\n        return mSelectedSpell;\n    }\n\n    bool Spells::isSpellActive(const std::string &id) const\n    {\n        if (id.empty())\n            return false;\n\n        const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id);\n        if (spell && hasSpell(spell))\n        {\n            auto type = spell->mData.mType;\n            return (type==ESM::Spell::ST_Ability || type==ESM::Spell::ST_Blight || type==ESM::Spell::ST_Disease || type==ESM::Spell::ST_Curse);\n        }\n\n        return false;\n    }\n\n    bool Spells::hasDisease(const ESM::Spell::SpellType type) const\n    {\n        for (const auto& iter : mSpells)\n        {\n            const ESM::Spell *spell = iter.first;\n            if (spell->mData.mType == type)\n                return true;\n        }\n\n        return false;\n    }\n\n    bool Spells::hasCommonDisease() const\n    {\n        return hasDisease(ESM::Spell::ST_Disease);\n    }\n\n    bool Spells::hasBlightDisease() const\n    {\n        return hasDisease(ESM::Spell::ST_Blight);\n    }\n\n    void Spells::purge(const SpellFilter& filter)\n    {\n        std::vector<std::string> purged;\n        for (auto iter = mSpells.begin(); iter!=mSpells.end();)\n        {\n            const ESM::Spell *spell = iter->first;\n            if (filter(spell))\n            {\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_PLAYER_SPELLBOOK packet every time a spell is purged here\n                */\n                mwmp::Main::get().getLocalPlayer()->sendSpellChange(spell->mId, mwmp::SpellbookChanges::REMOVE);\n                /*\n                    End of tes3mp addition\n                */\n\n                mSpells.erase(iter++);\n                purged.push_back(spell->mId);\n                mSpellsChanged = true;\n            }\n            else\n                ++iter;\n        }\n        if(!purged.empty())\n            mSpellList->removeAll(purged);\n    }\n\n    void Spells::purgeCommonDisease()\n    {\n        purge([](auto spell) { return spell->mData.mType == ESM::Spell::ST_Disease; });\n    }\n\n    void Spells::purgeBlightDisease()\n    {\n        purge([](auto spell) { return spell->mData.mType == ESM::Spell::ST_Blight && !hasCorprusEffect(spell); });\n    }\n\n    void Spells::purgeCorprusDisease()\n    {\n        purge(&hasCorprusEffect);\n    }\n\n    void Spells::purgeCurses()\n    {\n        purge([](auto spell) { return spell->mData.mType == ESM::Spell::ST_Curse; });\n    }\n\n    void Spells::removeEffects(const std::string &id)\n    {\n        if (isSpellActive(id))\n        {\n            for (auto& spell : mSpells)\n            {\n                if (spell.first == SpellList::getSpell(id))\n                {\n                    for (long unsigned int i = 0; i != spell.first->mEffects.mList.size(); i++)\n                    {\n                        spell.second.mPurgedEffects.insert(i);\n                    }\n                }\n            }\n\n            mSpellsChanged = true;\n        }\n    }\n\n    void Spells::visitEffectSources(EffectSourceVisitor &visitor) const\n    {\n        if (mSpellsChanged) {\n            rebuildEffects();\n            mSpellsChanged = false;\n        }\n\n        for (const auto& it : mSourcedEffects)\n        {\n            const ESM::Spell * spell = it.first;\n            for (const auto& effectIt : it.second)\n            {\n                // FIXME: since Spells merges effects with the same ID, there is no sense to use multiple effects with same ID here\n                visitor.visit(effectIt.first, -1, spell->mName, spell->mId, -1, effectIt.second.getMagnitude());\n            }\n        }\n    }\n\n    bool Spells::hasCorprusEffect(const ESM::Spell *spell)\n    {\n        for (const auto& effectIt : spell->mEffects.mList)\n        {\n            if (effectIt.mEffectID == ESM::MagicEffect::Corprus)\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    void Spells::purgeEffect(int effectId)\n    {\n        for (auto& spellIt : mSpells)\n        {\n            int i = 0;\n            for (auto& effectIt : spellIt.first->mEffects.mList)\n            {\n                if (effectIt.mEffectID == effectId)\n                {\n                    spellIt.second.mPurgedEffects.insert(i);\n                    mSpellsChanged = true;\n                }\n                ++i;\n            }\n        }\n    }\n\n    void Spells::purgeEffect(int effectId, const std::string & sourceId)\n    {\n        // Effect source may be not a spell\n        const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(sourceId);\n        if (spell == nullptr)\n            return;\n\n        auto spellIt = mSpells.find(spell);\n        if (spellIt == mSpells.end())\n            return;\n\n        int index = 0;\n        for (auto& effectIt : spellIt->first->mEffects.mList)\n        {\n            if (effectIt.mEffectID == effectId)\n            {\n                spellIt->second.mPurgedEffects.insert(index);\n                mSpellsChanged = true;\n            }\n            ++index;\n        }\n    }\n\n    bool Spells::canUsePower(const ESM::Spell* spell) const\n    {\n        const auto it = mUsedPowers.find(spell);\n        return it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp();\n    }\n\n    void Spells::usePower(const ESM::Spell* spell)\n    {\n        mUsedPowers[spell] = MWBase::Environment::get().getWorld()->getTimeStamp();\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_PLAYER_COOLDOWN packet every time a cooldown is recorded here\n        */\n        mwmp::Main::get().getLocalPlayer()->sendCooldownChange(spell->mId, MWBase::Environment::get().getWorld()->getTimeStamp().getDay(),\n            MWBase::Environment::get().getWorld()->getTimeStamp().getHour());\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set timestamps for power cooldowns, necessary for ID_PLAYER_COOLDOWNS packets\n    */\n    void Spells::setPowerUseTimestamp(const ESM::Spell* spell, int startDay, float startHour)\n    {\n        ESM::TimeStamp timestamp;\n        timestamp.mDay = startDay;\n        timestamp.mHour = startHour;\n\n        mUsedPowers[spell] = MWWorld::TimeStamp(timestamp);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void Spells::readState(const ESM::SpellState &state, CreatureStats* creatureStats)\n    {\n        const auto& baseSpells = mSpellList->getSpells();\n\n        for (ESM::SpellState::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it)\n        {\n            // Discard spells that are no longer available due to changed content files\n            const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first);\n            if (spell)\n            {\n                mSpells[spell].mEffectRands = it->second.mEffectRands;\n                mSpells[spell].mPurgedEffects = it->second.mPurgedEffects;\n\n                if (it->first == state.mSelectedSpell)\n                    mSelectedSpell = it->first;\n            }\n        }\n        // Add spells from the base record\n        for(const std::string& id : baseSpells)\n        {\n            const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id);\n            if(spell)\n                addSpell(spell);\n        }\n\n        for (std::map<std::string, ESM::TimeStamp>::const_iterator it = state.mUsedPowers.begin(); it != state.mUsedPowers.end(); ++it)\n        {\n            const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first);\n            if (!spell)\n                continue;\n            mUsedPowers[spell] = MWWorld::TimeStamp(it->second);\n        }\n\n        for (std::map<std::string, ESM::SpellState::CorprusStats>::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it)\n        {\n            const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first);\n            if (!spell)\n                continue;\n\n            CorprusStats stats;\n\n            int worsening = state.mCorprusSpells.at(it->first).mWorsenings;\n\n            for (int i=0; i<ESM::Attribute::Length; ++i)\n                stats.mWorsenings[i] = 0;\n\n            for (auto& effect : spell->mEffects.mList)\n            {\n                if (effect.mEffectID == ESM::MagicEffect::DrainAttribute)\n                    stats.mWorsenings[effect.mAttribute] = worsening;\n            }\n            stats.mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening);\n\n            creatureStats->addCorprusSpell(it->first, stats);\n        }\n\n        mSpellsChanged = true;\n\n        // Permanent effects are used only to keep the custom magnitude of corprus spells effects (after cure too), and only in old saves. Convert data to the new approach.\n        for (std::map<std::string, std::vector<ESM::SpellState::PermanentSpellEffectInfo> >::const_iterator it =\n            state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it)\n        {\n            const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first);\n            if (!spell)\n                continue;\n\n            // Import data only for player, other actors should not suffer from corprus worsening.\n            MWWorld::Ptr player = getPlayer();\n            if (creatureStats->getActorId() != player.getClass().getCreatureStats(player).getActorId())\n                return;\n\n            // Note: if target actor has the Restore attirbute effects, stats will be restored.\n            for (std::vector<ESM::SpellState::PermanentSpellEffectInfo>::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt)\n            {\n                // Applied corprus effects are already in loaded stats modifiers\n                if (effectIt->mId == ESM::MagicEffect::FortifyAttribute)\n                {\n                    AttributeValue attr = creatureStats->getAttribute(effectIt->mArg);\n                    attr.setModifier(attr.getModifier() - effectIt->mMagnitude);\n                    attr.damage(-effectIt->mMagnitude);\n                    creatureStats->setAttribute(effectIt->mArg, attr);\n                }\n                else if (effectIt->mId == ESM::MagicEffect::DrainAttribute)\n                {\n                    AttributeValue attr = creatureStats->getAttribute(effectIt->mArg);\n                    attr.setModifier(attr.getModifier() + effectIt->mMagnitude);\n                    attr.damage(effectIt->mMagnitude);\n                    creatureStats->setAttribute(effectIt->mArg, attr);\n                }\n            }\n        }\n    }\n\n    void Spells::writeState(ESM::SpellState &state) const\n    {\n        const auto& baseSpells = mSpellList->getSpells();\n        for (const auto& it : mSpells)\n        {\n            // Don't save spells and powers stored in the base record\n            if((it.first->mData.mType != ESM::Spell::ST_Spell && it.first->mData.mType != ESM::Spell::ST_Power) ||\n                std::find(baseSpells.begin(), baseSpells.end(), it.first->mId) == baseSpells.end())\n            {\n                ESM::SpellState::SpellParams params;\n                params.mEffectRands = it.second.mEffectRands;\n                params.mPurgedEffects = it.second.mPurgedEffects;\n                state.mSpells.emplace(it.first->mId, params);\n            }\n        }\n\n        state.mSelectedSpell = mSelectedSpell;\n\n        for (const auto& it : mUsedPowers)\n            state.mUsedPowers[it.first->mId] = it.second.toEsm();\n    }\n\n    bool Spells::setSpells(const std::string& actorId)\n    {\n        bool result;\n        std::tie(mSpellList, result) = MWBase::Environment::get().getWorld()->getStore().getSpellList(actorId);\n        mSpellList->addListener(this);\n        addAllToInstance(mSpellList->getSpells());\n        return result;\n    }\n\n    void Spells::addAllToInstance(const std::vector<std::string>& spells)\n    {\n        for(const std::string& id : spells)\n        {\n            const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id);\n            if(spell)\n                addSpell(spell);\n            else\n                Log(Debug::Warning) << \"Warning: ignoring nonexistent spell '\" << id << \"'\";\n        }\n    }\n\n    Spells::~Spells()\n    {\n        if(mSpellList)\n            mSpellList->removeListener(this);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spells.hpp",
    "content": "#ifndef GAME_MWMECHANICS_SPELLS_H\n#define GAME_MWMECHANICS_SPELLS_H\n\n#include <memory>\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"../mwworld/timestamp.hpp\"\n\n#include \"magiceffects.hpp\"\n#include \"spelllist.hpp\"\n\nnamespace ESM\n{\n    struct SpellState;\n}\n\nnamespace MWMechanics\n{\n    class CreatureStats;\n\n    class MagicEffects;\n\n    /// \\brief Spell list\n    ///\n    /// This class manages known spells as well as abilities, powers and permanent negative effects like\n    /// diseases. It also keeps track of used powers (which can only be used every 24h).\n    class Spells\n    {\n            std::shared_ptr<SpellList> mSpellList;\n            std::map<const ESM::Spell*, SpellParams> mSpells;\n\n            // Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different)\n            std::string mSelectedSpell;\n\n            std::map<const ESM::Spell*, MWWorld::TimeStamp> mUsedPowers;\n\n            mutable bool mSpellsChanged;\n            mutable MagicEffects mEffects;\n            mutable std::map<const ESM::Spell*, MagicEffects> mSourcedEffects;\n            void rebuildEffects() const;\n\n            bool hasDisease(const ESM::Spell::SpellType type) const;\n\n            using SpellFilter = bool (*)(const ESM::Spell*);\n            void purge(const SpellFilter& filter);\n\n            void addSpell(const ESM::Spell* spell);\n            void removeSpell(const ESM::Spell* spell);\n            void removeAllSpells();\n\n            friend class SpellList;\n        public:\n            using TIterator = std::map<const ESM::Spell*, SpellParams>::const_iterator;\n\n            Spells();\n\n            Spells(const Spells&);\n\n            Spells(Spells&& spells);\n\n            ~Spells();\n\n            static bool hasCorprusEffect(const ESM::Spell *spell);\n\n            void purgeEffect(int effectId);\n            void purgeEffect(int effectId, const std::string & sourceId);\n\n            bool canUsePower (const ESM::Spell* spell) const;\n            void usePower (const ESM::Spell* spell);\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set timestamps for power cooldowns, necessary for ID_PLAYER_COOLDOWNS packets\n            */\n            void setPowerUseTimestamp(const ESM::Spell* spell, int startDay, float startHour);\n            /*\n                End of tes3mp addition\n            */\n\n            void purgeCommonDisease();\n            void purgeBlightDisease();\n            void purgeCorprusDisease();\n            void purgeCurses();\n\n            TIterator begin() const;\n\n            TIterator end() const;\n\n            bool hasSpell(const std::string& spell) const;\n            bool hasSpell(const ESM::Spell* spell) const;\n\n            void add (const std::string& spell);\n            ///< Adding a spell that is already listed in *this is a no-op.\n\n            void add (const ESM::Spell* spell);\n            ///< Adding a spell that is already listed in *this is a no-op.\n\n            void remove (const std::string& spell);\n            ///< If the spell to be removed is the selected spell, the selected spell will be changed to\n            /// no spell (empty string).\n\n            MagicEffects getMagicEffects() const;\n            ///< Return sum of magic effects resulting from abilities, blights, deseases and curses.\n\n            void clear(bool modifyBase = false);\n            ///< Remove all spells of al types.\n\n            void setSelectedSpell (const std::string& spellId);\n            ///< This function does not verify, if the spell is available.\n\n            const std::string getSelectedSpell() const;\n            ///< May return an empty string.\n\n            bool isSpellActive(const std::string& id) const;\n            ///< Are we under the effects of the given spell ID?\n\n            bool hasCommonDisease() const;\n\n            bool hasBlightDisease() const;\n\n            void removeEffects(const std::string& id);\n\n            void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;\n\n            void readState (const ESM::SpellState& state, CreatureStats* creatureStats);\n            void writeState (ESM::SpellState& state) const;\n\n            bool setSpells(const std::string& id);\n\n            void addAllToInstance(const std::vector<std::string>& spells);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellutil.cpp",
    "content": "#include \"spellutil.hpp\"\n\n#include <limits>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"actorutil.hpp\"\n#include \"creaturestats.hpp\"\n\nnamespace MWMechanics\n{\n    ESM::Skill::SkillEnum spellSchoolToSkill(int school)\n    {\n        static const std::array<ESM::Skill::SkillEnum, 6> schoolSkillArray\n        {\n            ESM::Skill::Alteration, ESM::Skill::Conjuration, ESM::Skill::Destruction,\n            ESM::Skill::Illusion, ESM::Skill::Mysticism, ESM::Skill::Restoration\n        };\n        return schoolSkillArray.at(school);\n    }\n\n    float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect)\n    {\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n        if (!magicEffect)\n            magicEffect = store.get<ESM::MagicEffect>().find(effect.mEffectID);\n        bool hasMagnitude = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude);\n        bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);\n        bool appliedOnce = magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce;\n        int minMagn = hasMagnitude ? effect.mMagnMin : 1;\n        int maxMagn = hasMagnitude ? effect.mMagnMax : 1;\n        int duration = hasDuration ? effect.mDuration : 1;\n        if (!appliedOnce)\n            duration = std::max(1, duration);\n        static const float fEffectCostMult = store.get<ESM::GameSetting>().find(\"fEffectCostMult\")->mValue.getFloat();\n\n        float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));\n        x *= 0.1 * magicEffect->mData.mBaseCost;\n        x *= 1 + duration;\n        x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost;\n\n        return x * fEffectCostMult;\n    }\n\n    int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor)\n    {\n        /*\n         * Each point of enchant skill above/under 10 subtracts/adds\n         * one percent of enchantment cost while minimum is 1.\n         */\n        int eSkill = actor.getClass().getSkill(actor, ESM::Skill::Enchant);\n        const float result = castCost - (castCost / 100) * (eSkill - 10);\n\n        return static_cast<int>((result < 1) ? 1 : result);\n    }\n\n    float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool)\n    {\n        // Morrowind for some reason uses a formula slightly different from magicka cost calculation\n        float y = std::numeric_limits<float>::max();\n        float lowestSkill = 0;\n\n        for (const ESM::ENAMstruct& effect : spell->mEffects.mList)\n        {\n            float x = static_cast<float>(effect.mDuration);\n            const auto magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);\n\n            if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce))\n                x = std::max(1.f, x);\n\n            x *= 0.1f * magicEffect->mData.mBaseCost;\n            x *= 0.5f * (effect.mMagnMin + effect.mMagnMax);\n            x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost;\n            if (effect.mRange == ESM::RT_Target)\n                x *= 1.5f;\n            static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                        \"fEffectCostMult\")->mValue.getFloat();\n            x *= fEffectCostMult;\n\n            float s = 2.0f * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool));\n            if (s - x < y)\n            {\n                y = s - x;\n                if (effectiveSchool)\n                    *effectiveSchool = magicEffect->mData.mSchool;\n                lowestSkill = s;\n            }\n        }\n\n        CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n\n        float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();\n        float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();\n\n        float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck);\n\n        return castChance;\n    }\n\n    float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka)\n    {\n        // NB: Base chance is calculated here because the effective school pointer must be filled\n        float baseChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool);\n\n        bool godmode = actor == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n\n        CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n\n        if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() && !godmode)\n            return 0;\n\n        if (spell->mData.mType == ESM::Spell::ST_Power)\n            return stats.getSpells().canUsePower(spell) ? 100 : 0;\n\n        if (godmode)\n            return 100;\n\n        if (spell->mData.mType != ESM::Spell::ST_Spell)\n            return 100;\n\n        if (checkMagicka && spell->mData.mCost > 0 && stats.getMagicka().getCurrent() < spell->mData.mCost)\n            return 0;\n\n        if (spell->mData.mFlags & ESM::Spell::F_Always)\n            return 100;\n\n        float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();\n        float castChance = baseChance + castBonus;\n        castChance *= stats.getFatigueTerm();\n\n        return std::max(0.f, cap ? std::min(100.f, castChance) : castChance);\n    }\n\n    float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka)\n    {\n        if (const auto spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spellId))\n            return getSpellSuccessChance(spell, actor, effectiveSchool, cap, checkMagicka);\n        return 0.f;\n    }\n\n    int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor)\n    {\n        int school = 0;\n        getSpellSuccessChance(spellId, actor, &school);\n        return school;\n    }\n\n    int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor)\n    {\n        int school = 0;\n        getSpellSuccessChance(spell, actor, &school);\n        return school;\n    }\n\n    bool spellIncreasesSkill(const ESM::Spell *spell)\n    {\n        return spell->mData.mType == ESM::Spell::ST_Spell && !(spell->mData.mFlags & ESM::Spell::F_Always);\n    }\n\n    bool spellIncreasesSkill(const std::string &spellId)\n    {\n        const auto spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spellId);\n        return spell && spellIncreasesSkill(spell);\n    }\n\n    bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer)\n    {\n        switch (effectId)\n        {\n            case ESM::MagicEffect::Levitate:\n            {\n                if (!MWBase::Environment::get().getWorld()->isLevitationEnabled())\n                {\n                    if (castByPlayer)\n                        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sLevitateDisabled}\");\n                    return false;\n                }\n                break;\n            }\n            case ESM::MagicEffect::Soultrap:\n            {\n                if (!target.getClass().isNpc() // no messagebox for NPCs\n                     && (target.getTypeName() == typeid(ESM::Creature).name() && target.get<ESM::Creature>()->mBase->mData.mSoul == 0))\n                {\n                    if (castByPlayer)\n                        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sMagicInvalidTarget}\");\n                    return true; // must still apply to get visual effect and have target regard it as attack\n                }\n                break;\n            }\n            case ESM::MagicEffect::WaterWalking:\n            {\n                if (target.getClass().isPureWaterCreature(target) && MWBase::Environment::get().getWorld()->isSwimming(target))\n                    return false;\n\n                MWBase::World *world = MWBase::Environment::get().getWorld();\n\n                if (!world->isWaterWalkingCastableOnTarget(target))\n                {\n                    if (castByPlayer && caster == target)\n                        MWBase::Environment::get().getWindowManager()->messageBox (\"#{sMagicInvalidEffect}\");\n                    return false;\n                }\n                break;\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/spellutil.hpp",
    "content": "#ifndef MWMECHANICS_SPELLUTIL_H\n#define MWMECHANICS_SPELLUTIL_H\n\n#include <components/esm/loadskil.hpp>\n\nnamespace ESM\n{\n    struct ENAMstruct;\n    struct MagicEffect;\n    struct Spell;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    ESM::Skill::SkillEnum spellSchoolToSkill(int school);\n\n    float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect = nullptr);\n\n    int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor);\n\n    /**\n     * @param spell spell to cast\n     * @param actor calculate spell success chance for this actor (depends on actor's skills)\n     * @param effectiveSchool the spell's effective school (relevant for skill progress) will be written here\n     * @param cap cap the result to 100%?\n     * @param checkMagicka check magicka?\n     * @note actor can be an NPC or a creature\n     * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned.\n     */\n    float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool);\n    float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=true);\n    float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=true);\n\n    int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor);\n    int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);\n\n    /// Get whether or not the given spell contributes to skill progress.\n    bool spellIncreasesSkill(const ESM::Spell* spell);\n    bool spellIncreasesSkill(const std::string& spellId);\n\n    /// Check if the given effect can be applied to the target. If \\a castByPlayer, emits a message box on failure.\n    bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/stat.cpp",
    "content": "#include \"stat.hpp\"\n\n#include <components/esm/statstate.hpp>\n\nnamespace MWMechanics\n{\n    template<typename T>\n    Stat<T>::Stat() : mBase (0), mModified (0), mCurrentModified (0) {}\n    template<typename T>\n    Stat<T>::Stat(T base) : mBase (base), mModified (base), mCurrentModified (base) {}\n    template<typename T>\n    Stat<T>::Stat(T base, T modified) : mBase (base), mModified (modified), mCurrentModified (modified) {}\n\n    template<typename T>\n    const T& Stat<T>::getBase() const\n    {\n        return mBase;\n    }\n\n    template<typename T>\n    T Stat<T>::getModified(bool capped) const\n    {\n        if(!capped)\n            return mModified;\n        return std::max(static_cast<T>(0), mModified);\n    }\n\n    template<typename T>\n    T Stat<T>::getCurrentModified() const\n    {\n        return mCurrentModified;\n    }\n\n    template<typename T>\n    T Stat<T>::getModifier() const\n    {\n        return mModified-mBase;\n    }\n\n    template<typename T>\n    T Stat<T>::getCurrentModifier() const\n    {\n        return mCurrentModified - mModified;\n    }\n\n    template<typename T>\n    void Stat<T>::set (const T& value)\n    {\n        T diff = value - mBase;\n        mBase = mModified = value;\n        mCurrentModified += diff;\n    }\n\n    template<typename T>\n    void Stat<T>::setBase (const T& value)\n    {\n        T diff = value - mBase;\n        mBase = value;\n        mModified += diff;\n        mCurrentModified += diff;\n    }\n\n    template<typename T>\n    void Stat<T>::setModified (T value, const T& min, const T& max)\n    {\n        T diff = value - mModified;\n\n        if (mBase+diff<min)\n        {\n            value = min + (mModified - mBase);\n            diff = value - mModified;\n        }\n        else if (mBase+diff>max)\n        {\n            value = max + (mModified - mBase);\n            diff = value - mModified;\n        }\n\n        mModified = value;\n        mBase += diff;\n        mCurrentModified += diff;\n    }\n\n    template<typename T>\n    void Stat<T>::setCurrentModified(T value)\n    {\n        mCurrentModified = value;\n    }\n\n    template<typename T>\n    void Stat<T>::setModifier (const T& modifier)\n    {\n        mModified = mBase + modifier;\n    }\n\n    template<typename T>\n    void Stat<T>::setCurrentModifier(const T& modifier)\n    {\n        mCurrentModified = mModified + modifier;\n    }\n\n    template<typename T>\n    void Stat<T>::writeState (ESM::StatState<T>& state) const\n    {\n        state.mBase = mBase;\n        state.mMod = mCurrentModified;\n    }\n    template<typename T>\n    void Stat<T>::readState (const ESM::StatState<T>& state)\n    {\n        mBase = state.mBase;\n        mModified = state.mBase;\n        mCurrentModified = state.mMod;\n    }\n\n\n    template<typename T>\n    DynamicStat<T>::DynamicStat() : mStatic (0), mCurrent (0) {}\n    template<typename T>\n    DynamicStat<T>::DynamicStat(T base) : mStatic (base), mCurrent (base) {}\n    template<typename T>\n    DynamicStat<T>::DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {}\n    template<typename T>\n    DynamicStat<T>::DynamicStat(const Stat<T> &stat, T current) : mStatic(stat), mCurrent (current) {}\n\n\n    template<typename T>\n    const T& DynamicStat<T>::getBase() const\n    {\n        return mStatic.getBase();\n    }\n    template<typename T>\n    T DynamicStat<T>::getModified() const\n    {\n        return mStatic.getModified();\n    }\n    template<typename T>\n    T DynamicStat<T>::getCurrentModified() const\n    {\n        return mStatic.getCurrentModified();\n    }\n\n    template<typename T>\n    const T& DynamicStat<T>::getCurrent() const\n    {\n        return mCurrent;\n    }\n\n    template<typename T>\n    void DynamicStat<T>::set (const T& value)\n    {\n        mStatic.set (value);\n        mCurrent = value;\n    }\n    template<typename T>\n    void DynamicStat<T>::setBase (const T& value)\n    {\n        mStatic.setBase (value);\n\n        if (mCurrent>getModified())\n            mCurrent = getModified();\n    }\n    template<typename T>\n    void DynamicStat<T>::setModified (T value, const T& min, const T& max)\n    {\n        mStatic.setModified (value, min, max);\n\n        if (mCurrent>getModified())\n            mCurrent = getModified();\n    }\n    template<typename T>\n    void DynamicStat<T>::setCurrentModified(T value)\n    {\n        mStatic.setCurrentModified(value);\n    }\n    template<typename T>\n    void DynamicStat<T>::setCurrent (const T& value, bool allowDecreaseBelowZero, bool allowIncreaseAboveModified)\n    {\n        if (value > mCurrent)\n        {\n            // increase\n            if (value <= getModified() || allowIncreaseAboveModified)\n                mCurrent = value;\n            else if (mCurrent > getModified())\n                return;\n            else\n                mCurrent = getModified();\n        }\n        else if (value > 0 || allowDecreaseBelowZero)\n        {\n            // allowed decrease\n            mCurrent = value;\n        }\n        else if (mCurrent > 0)\n        {\n            // capped decrease\n            mCurrent = 0;\n        }\n    }\n    template<typename T>\n    void DynamicStat<T>::setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero)\n    {\n        T diff =  modifier - mStatic.getModifier();\n        mStatic.setModifier (modifier);\n        setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero);\n    }\n\n    template<typename T>\n    void DynamicStat<T>::setCurrentModifier(const T& modifier, bool allowCurrentDecreaseBelowZero)\n    {\n        T diff = modifier - mStatic.getCurrentModifier();\n        mStatic.setCurrentModifier(modifier);\n\n        // The (modifier > 0) check here allows increase over modified only if the modifier is positive (a fortify effect is active).\n        setCurrent (getCurrent() + diff, allowCurrentDecreaseBelowZero, (modifier > 0));\n    }\n\n    template<typename T>\n    void DynamicStat<T>::writeState (ESM::StatState<T>& state) const\n    {\n        mStatic.writeState (state);\n        state.mCurrent = mCurrent;\n    }\n    template<typename T>\n    void DynamicStat<T>::readState (const ESM::StatState<T>& state)\n    {\n        mStatic.readState (state);\n        mCurrent = state.mCurrent;\n    }\n\n    AttributeValue::AttributeValue() :\n        mBase(0.f), mModifier(0.f), mDamage(0.f)\n    {\n    }\n\n    float AttributeValue::getModified() const\n    {\n        return std::max(0.f, mBase - mDamage + mModifier);\n    }\n    float AttributeValue::getBase() const\n    {\n        return mBase;\n    }\n    float AttributeValue::getModifier() const\n    {\n        return mModifier;\n    }\n\n    void AttributeValue::setBase(float base)\n    {\n        mBase = base;\n    }\n\n    void AttributeValue::setModifier(float mod)\n    {\n        mModifier = mod;\n    }\n\n    void AttributeValue::damage(float damage)\n    {\n        float threshold = mBase + mModifier;\n\n        if (mDamage + damage > threshold)\n            mDamage = threshold;\n        else\n            mDamage += damage;\n    }\n    void AttributeValue::restore(float amount)\n    {\n        if (mDamage <= 0) return;\n\n        mDamage -= std::min(mDamage, amount);\n    }\n\n    float AttributeValue::getDamage() const\n    {\n        return mDamage;\n    }\n\n    void AttributeValue::writeState (ESM::StatState<float>& state) const\n    {\n        state.mBase = mBase;\n        state.mMod = mModifier;\n        state.mDamage = mDamage;\n    }\n\n    void AttributeValue::readState (const ESM::StatState<float>& state)\n    {\n        mBase = state.mBase;\n        mModifier = state.mMod;\n        mDamage = state.mDamage;\n    }\n\n    SkillValue::SkillValue() :\n        mProgress(0)\n    {\n    }\n\n    float SkillValue::getProgress() const\n    {\n        return mProgress;\n    }\n    void SkillValue::setProgress(float progress)\n    {\n        mProgress = progress;\n    }\n\n    void SkillValue::writeState (ESM::StatState<float>& state) const\n    {\n        AttributeValue::writeState (state);\n        state.mProgress = mProgress;\n    }\n\n    void SkillValue::readState (const ESM::StatState<float>& state)\n    {\n        AttributeValue::readState (state);\n        mProgress = state.mProgress;\n    }\n}\n\ntemplate class MWMechanics::Stat<int>;\ntemplate class MWMechanics::Stat<float>;\ntemplate class MWMechanics::DynamicStat<int>;\ntemplate class MWMechanics::DynamicStat<float>;\n"
  },
  {
    "path": "apps/openmw/mwmechanics/stat.hpp",
    "content": "#ifndef GAME_MWMECHANICS_STAT_H\n#define GAME_MWMECHANICS_STAT_H\n\n#include <algorithm>\n#include <limits>\n\nnamespace ESM\n{\n    template<typename T>\n    struct StatState;\n}\n\nnamespace MWMechanics\n{\n    template<typename T>\n    class Stat\n    {\n            T mBase;\n            T mModified;\n            T mCurrentModified;\n\n        public:\n            typedef T Type;\n\n            Stat();\n            Stat(T base);\n            Stat(T base, T modified);\n\n            const T& getBase() const;\n\n            T getModified(bool capped = true) const;\n            T getCurrentModified() const;\n            T getModifier() const;\n            T getCurrentModifier() const;\n\n            /// Set base and modified to \\a value.\n            void set (const T& value);\n\n            /// Set base and adjust modified accordingly.\n            void setBase (const T& value);\n\n            /// Set modified value and adjust base accordingly.\n            void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max());\n\n            /// Set \"current modified,\" used for drain and fortify. Unlike the regular modifier\n            /// this just adds and subtracts from the current value without changing the maximum.\n            void setCurrentModified(T value);\n\n            void setModifier (const T& modifier);\n            void setCurrentModifier (const T& modifier);\n\n            void writeState (ESM::StatState<T>& state) const;\n            void readState (const ESM::StatState<T>& state);\n    };\n\n    template<typename T>\n    inline bool operator== (const Stat<T>& left, const Stat<T>& right)\n    {\n        return left.getBase()==right.getBase() &&\n            left.getModified()==right.getModified();\n    }\n\n    template<typename T>\n    inline bool operator!= (const Stat<T>& left, const Stat<T>& right)\n    {\n        return !(left==right);\n    }\n\n    template<typename T>\n    class DynamicStat\n    {\n            Stat<T> mStatic;\n            T mCurrent;\n\n        public:\n            typedef T Type;\n\n            DynamicStat();\n            DynamicStat(T base);\n            DynamicStat(T base, T modified, T current);\n            DynamicStat(const Stat<T> &stat, T current);\n\n            const T& getBase() const;\n            T getModified() const;\n            T getCurrentModified() const;\n            const T& getCurrent() const;\n\n            /// Set base, modified and current to \\a value.\n            void set (const T& value);\n\n            /// Set base and adjust modified accordingly.\n            void setBase (const T& value);\n\n            /// Set modified value and adjust base accordingly.\n            void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max());\n\n            /// Set \"current modified,\" used for drain and fortify. Unlike the regular modifier\n            /// this just adds and subtracts from the current value without changing the maximum.\n            void setCurrentModified(T value);\n\n            void setCurrent (const T& value, bool allowDecreaseBelowZero = false, bool allowIncreaseAboveModified = false);\n            void setModifier (const T& modifier, bool allowCurrentToDecreaseBelowZero=false);\n            void setCurrentModifier (const T& modifier, bool allowCurrentToDecreaseBelowZero = false);\n\n            void writeState (ESM::StatState<T>& state) const;\n            void readState (const ESM::StatState<T>& state);\n    };\n\n    template<typename T>\n    inline bool operator== (const DynamicStat<T>& left, const DynamicStat<T>& right)\n    {\n        return left.getBase()==right.getBase() &&\n            left.getModified()==right.getModified() &&\n            left.getCurrent()==right.getCurrent();\n    }\n\n    template<typename T>\n    inline bool operator!= (const DynamicStat<T>& left, const DynamicStat<T>& right)\n    {\n        return !(left==right);\n    }\n\n    class AttributeValue\n    {\n        float mBase;\n        float mModifier;\n        float mDamage; // needs to be float to allow continuous damage\n\n    public:\n        AttributeValue();\n\n        float getModified() const;\n        float getBase() const;\n        float getModifier() const;\n\n        void setBase(float base);\n\n        void setModifier(float mod);\n\n        // Maximum attribute damage is limited to the modified value.\n        // Note: I think MW applies damage directly to mModified, since you can also\n        // \"restore\" drained attributes. We need to rewrite the magic effect system to support this.\n        void damage(float damage);\n        void restore(float amount);\n\n        float getDamage() const;\n\n        void writeState (ESM::StatState<float>& state) const;\n        void readState (const ESM::StatState<float>& state);\n    };\n\n    class SkillValue : public AttributeValue\n    {\n        float mProgress;\n    public:\n        SkillValue();\n        float getProgress() const;\n        void setProgress(float progress);\n\n        void writeState (ESM::StatState<float>& state) const;\n        void readState (const ESM::StatState<float>& state);\n    };\n\n    inline bool operator== (const AttributeValue& left, const AttributeValue& right)\n    {\n        return left.getBase() == right.getBase()\n                && left.getModifier() == right.getModifier()\n                && left.getDamage() == right.getDamage();\n    }\n    inline bool operator!= (const AttributeValue& left, const AttributeValue& right)\n    {\n        return !(left == right);\n    }\n\n    inline bool operator== (const SkillValue& left, const SkillValue& right)\n    {\n        return left.getBase() == right.getBase()\n                && left.getModifier() == right.getModifier()\n                && left.getDamage() == right.getDamage()\n                && left.getProgress() == right.getProgress();\n    }\n    inline bool operator!= (const SkillValue& left, const SkillValue& right)\n    {\n        return !(left == right);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/steering.cpp",
    "content": "#include \"steering.hpp\"\n\n#include <components/misc/mathutil.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/ptr.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n\n#include \"movement.hpp\"\n\nnamespace MWMechanics\n{\n\nbool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians)\n{\n    MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor);\n    float diff = Misc::normalizeAngle(targetAngleRadians - actor.getRefData().getPosition().rot[axis]);\n    float absDiff = std::abs(diff);\n\n    // The turning animation actually moves you slightly, so the angle will be wrong again.\n    // Use epsilon to prevent jerkiness.\n    if (absDiff < epsilonRadians)\n        return true;\n\n    float limit = getAngularVelocity(actor.getClass().getMaxSpeed(actor)) * MWBase::Environment::get().getFrameDuration();\n    static const bool smoothMovement = Settings::Manager::getBool(\"smooth movement\", \"Game\");\n    if (smoothMovement)\n        limit *= std::min(absDiff / osg::PI + 0.1, 0.5);\n\n    if (absDiff > limit)\n        diff = osg::sign(diff) * limit;\n\n    movement.mRotation[axis] = diff;\n    return false;\n}\n\nbool zTurn(const MWWorld::Ptr& actor, float targetAngleRadians, float epsilonRadians)\n{\n    return smoothTurn(actor, targetAngleRadians, 2, epsilonRadians);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/steering.hpp",
    "content": "#ifndef OPENMW_MECHANICS_STEERING_H\n#define OPENMW_MECHANICS_STEERING_H\n\n#include <osg/Math>\n\n#include <algorithm>\n\nnamespace MWWorld\n{\nclass Ptr;\n}\n\nnamespace MWMechanics\n{\n\n// Max rotating speed, radian/sec\ninline float getAngularVelocity(const float actorSpeed)\n{\n    const float baseAngluarVelocity = 10;\n    const float baseSpeed = 200;\n    return baseAngluarVelocity * std::max(actorSpeed / baseSpeed, 1.0f);\n}\n\n/// configure rotation settings for an actor to reach this target angle (eventually)\n/// @return have we reached the target angle?\nbool zTurn(const MWWorld::Ptr& actor, float targetAngleRadians,\n                                      float epsilonRadians = osg::DegreesToRadians(0.5));\n\nbool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis,\n                                      float epsilonRadians = osg::DegreesToRadians(0.5));\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/summoning.cpp",
    "content": "#include \"summoning.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"creaturestats.hpp\"\n#include \"aifollow.hpp\"\n\nnamespace MWMechanics\n{\n\n    bool isSummoningEffect(int effectId)\n    {\n        return ((effectId >= ESM::MagicEffect::SummonScamp && effectId <= ESM::MagicEffect::SummonStormAtronach)\n             || (effectId == ESM::MagicEffect::SummonCenturionSphere)\n             || (effectId >= ESM::MagicEffect::SummonFabricant && effectId <= ESM::MagicEffect::SummonCreature05));\n    }\n\n    std::string getSummonedCreature(int effectId)\n    {\n        static const std::map<int, std::string> summonMap\n        {\n            {ESM::MagicEffect::SummonAncestralGhost, \"sMagicAncestralGhostID\"},\n            {ESM::MagicEffect::SummonBonelord, \"sMagicBonelordID\"},\n            {ESM::MagicEffect::SummonBonewalker, \"sMagicLeastBonewalkerID\"},\n            {ESM::MagicEffect::SummonCenturionSphere, \"sMagicCenturionSphereID\"},\n            {ESM::MagicEffect::SummonClannfear, \"sMagicClannfearID\"},\n            {ESM::MagicEffect::SummonDaedroth, \"sMagicDaedrothID\"},\n            {ESM::MagicEffect::SummonDremora, \"sMagicDremoraID\"},\n            {ESM::MagicEffect::SummonFabricant, \"sMagicFabricantID\"},\n            {ESM::MagicEffect::SummonFlameAtronach, \"sMagicFlameAtronachID\"},\n            {ESM::MagicEffect::SummonFrostAtronach, \"sMagicFrostAtronachID\"},\n            {ESM::MagicEffect::SummonGoldenSaint, \"sMagicGoldenSaintID\"},\n            {ESM::MagicEffect::SummonGreaterBonewalker, \"sMagicGreaterBonewalkerID\"},\n            {ESM::MagicEffect::SummonHunger, \"sMagicHungerID\"},\n            {ESM::MagicEffect::SummonScamp, \"sMagicScampID\"},\n            {ESM::MagicEffect::SummonSkeletalMinion, \"sMagicSkeletalMinionID\"},\n            {ESM::MagicEffect::SummonStormAtronach, \"sMagicStormAtronachID\"},\n            {ESM::MagicEffect::SummonWingedTwilight, \"sMagicWingedTwilightID\"},\n            {ESM::MagicEffect::SummonWolf, \"sMagicCreature01ID\"},\n            {ESM::MagicEffect::SummonBear, \"sMagicCreature02ID\"},\n            {ESM::MagicEffect::SummonBonewolf, \"sMagicCreature03ID\"},\n            {ESM::MagicEffect::SummonCreature04, \"sMagicCreature04ID\"},\n            {ESM::MagicEffect::SummonCreature05, \"sMagicCreature05ID\"}\n        };\n\n        auto it = summonMap.find(effectId);\n        if (it != summonMap.end())\n            return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(it->second)->mValue.getString();\n        return std::string();\n    }\n\n    UpdateSummonedCreatures::UpdateSummonedCreatures(const MWWorld::Ptr &actor)\n        : mActor(actor)\n    {\n    }\n\n    void UpdateSummonedCreatures::visit(EffectKey key, int effectIndex, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime)\n    {\n        if (isSummoningEffect(key.mId) && magnitude > 0)\n        {\n            mActiveEffects.insert(ESM::SummonKey(key.mId, sourceId, effectIndex));\n        }\n    }\n\n    void UpdateSummonedCreatures::process(bool cleanup)\n    {\n        MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor);\n        std::map<ESM::SummonKey, int>& creatureMap = creatureStats.getSummonedCreatureMap();\n\n        for (std::set<ESM::SummonKey>::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it)\n        {\n            bool found = creatureMap.find(*it) != creatureMap.end();\n            if (!found)\n            {\n                std::string creatureID = getSummonedCreature(it->mEffectId);\n                if (!creatureID.empty())\n                {\n                    int creatureActorId = -1;\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Send an ID_OBJECT_SPAWN packet every time a creature is summoned in a cell that we hold\n                        authority over, then delete the creature and wait for the server to send it back with a\n                        unique mpNum of its own\n\n                        Comment out most of the code here except for the actual placement of the Ptr and the\n                        creatureActorId insertion into the creatureMap\n                    */\n                    try\n                    {\n                        MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);\n\n                        /*\n                        MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());\n\n                        // Make the summoned creature follow its master and help in fights\n                        AiFollow package(mActor);\n                        summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());\n                        creatureActorId = summonedCreatureStats.getActorId();\n                        */\n\n                        MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), mActor, mActor.getCell(), 0, 120.f);\n\n                        /*\n                        MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed);\n                        if (anim)\n                        {\n                            const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()\n                                    .search(\"VFX_Summon_Start\");\n                            if (fx)\n                                anim->addEffect(\"meshes\\\\\" + fx->mModel, -1, false);\n                        }\n                        */\n\n                        if (mwmp::Main::get().getCellController()->hasLocalAuthority(*placed.getCell()->getCell()))\n                        {\n                            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                            objectList->reset();\n                            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n\n                            MWMechanics::CreatureStats *actorCreatureStats = &mActor.getClass().getCreatureStats(mActor);\n                            int effectId = it->mEffectId;\n                            std::string sourceId = it->mSourceId;\n                            float duration = actorCreatureStats->getActiveSpells().getEffectDuration(effectId, sourceId);\n                            objectList->addObjectSpawn(placed, mActor, sourceId, effectId, duration);\n                            objectList->sendObjectSpawn();\n                        }\n\n                        MWBase::Environment::get().getWorld()->deleteObject(placed);\n                    }\n                    catch (std::exception& e)\n                    {\n                        Log(Debug::Error) << \"Failed to spawn summoned creature: \" << e.what();\n                        // still insert into creatureMap so we don't try to spawn again every frame, that would spam the warning log\n                    }\n\n                    creatureMap.emplace(*it, creatureActorId);\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n            }\n        }\n\n        // Update summon effects\n        for (std::map<ESM::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )\n        {\n            bool found = mActiveEffects.find(it->first) != mActiveEffects.end();\n            if (!found)\n            {\n                // Effect has ended\n                MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second);\n                creatureMap.erase(it++);\n                continue;\n            }\n            ++it;\n        }\n\n        std::vector<int> graveyard = creatureStats.getSummonedCreatureGraveyard();\n        creatureStats.getSummonedCreatureGraveyard().clear();\n\n        for (const int creature : graveyard)\n            MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, creature);\n\n        if (!cleanup)\n            return;\n\n        for (std::map<ESM::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )\n        {\n            if(it->second == -1)\n            {\n                // Keep the spell effect active if we failed to spawn anything\n                it++;\n                continue;\n            }\n            MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);\n            if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished())\n            {\n                // Purge the magic effect so a new creature can be summoned if desired\n                purgeSummonEffect(mActor, *it);\n                creatureMap.erase(it++);\n            }\n            else\n                ++it;\n        }\n    }\n\n    void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair<const ESM::SummonKey, int>& summon)\n    {\n        auto& creatureStats = summoner.getClass().getCreatureStats(summoner);\n        creatureStats.getActiveSpells().purgeEffect(summon.first.mEffectId, summon.first.mSourceId, summon.first.mEffectIndex);\n        creatureStats.getSpells().purgeEffect(summon.first.mEffectId, summon.first.mSourceId);\n        if (summoner.getClass().hasInventoryStore(summoner))\n            summoner.getClass().getInventoryStore(summoner).purgeEffect(summon.first.mEffectId, summon.first.mSourceId, false, summon.first.mEffectIndex);\n\n        MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(summoner, summon.second);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/summoning.hpp",
    "content": "#ifndef OPENMW_MECHANICS_SUMMONING_H\n#define OPENMW_MECHANICS_SUMMONING_H\n\n#include <set>\n\n#include \"../mwworld/ptr.hpp\"\n\n#include <components/esm/magiceffects.hpp>\n\n#include \"magiceffects.hpp\"\n\nnamespace MWMechanics\n{\n    class CreatureStats;\n\n    bool isSummoningEffect(int effectId);\n\n    std::string getSummonedCreature(int effectId);\n\n    void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair<const ESM::SummonKey, int>& summon);\n\n    struct UpdateSummonedCreatures : public EffectSourceVisitor\n    {\n        UpdateSummonedCreatures(const MWWorld::Ptr& actor);\n        virtual ~UpdateSummonedCreatures() = default;\n\n        void visit (MWMechanics::EffectKey key, int effectIndex,\n                            const std::string& sourceName, const std::string& sourceId, int casterActorId,\n                            float magnitude, float remainingTime = -1, float totalTime = -1) override;\n\n        /// To call after all effect sources have been visited\n        void process(bool cleanup);\n\n    private:\n        MWWorld::Ptr mActor;\n\n        std::set<ESM::SummonKey> mActiveEffects;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/tickableeffects.cpp",
    "content": "#include \"tickableeffects.hpp\"\n\n#include <components/settings/settings.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"actorutil.hpp\"\n#include \"npcstats.hpp\"\n\nnamespace MWMechanics\n{\n    void adjustDynamicStat(CreatureStats& creatureStats, int index, float magnitude, bool allowDecreaseBelowZero = false)\n    {\n        DynamicStat<float> stat = creatureStats.getDynamic(index);\n        stat.setCurrent(stat.getCurrent() + magnitude, allowDecreaseBelowZero);\n        creatureStats.setDynamic(index, stat);\n    }\n\n    bool disintegrateSlot (const MWWorld::Ptr& ptr, int slot, float disintegrate)\n    {\n        if (!ptr.getClass().hasInventoryStore(ptr))\n            return false;\n\n        MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);\n        MWWorld::ContainerStoreIterator item = inv.getSlot(slot);\n\n        if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor || item.getType() == MWWorld::ContainerStore::Type_Weapon))\n        {\n            if (!item->getClass().hasItemHealth(*item))\n                return false;\n            int charge = item->getClass().getItemHealth(*item);\n            if (charge == 0)\n                return false;\n\n            // Store remainder of disintegrate amount (automatically subtracted if > 1)\n            item->getCellRef().applyChargeRemainderToBeSubtracted(disintegrate - std::floor(disintegrate));\n\n            charge = item->getClass().getItemHealth(*item);\n            charge -= std::min(static_cast<int>(disintegrate), charge);\n            item->getCellRef().setCharge(charge);\n\n            if (charge == 0)\n            {\n                // Will unequip the broken item and try to find a replacement\n                if (ptr != getPlayer())\n                    inv.autoEquip(ptr);\n                else\n                    inv.unequipItem(*item, ptr);\n            }\n\n            return true;\n        }\n\n        return false;\n    }\n\n    bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey &effectKey, float magnitude)\n    {\n        if (magnitude == 0.f)\n            return false;\n\n        bool receivedMagicDamage = false;\n        bool godmode = actor == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();\n\n        switch (effectKey.mId)\n        {\n        case ESM::MagicEffect::DamageAttribute:\n        {\n            if (godmode)\n                break;\n            AttributeValue attr = creatureStats.getAttribute(effectKey.mArg);\n            attr.damage(magnitude);\n            creatureStats.setAttribute(effectKey.mArg, attr);\n            break;\n        }\n        case ESM::MagicEffect::RestoreAttribute:\n        {\n            AttributeValue attr = creatureStats.getAttribute(effectKey.mArg);\n            attr.restore(magnitude);\n            creatureStats.setAttribute(effectKey.mArg, attr);\n            break;\n        }\n        case ESM::MagicEffect::RestoreHealth:\n        case ESM::MagicEffect::RestoreMagicka:\n        case ESM::MagicEffect::RestoreFatigue:\n            adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude);\n            break;\n        case ESM::MagicEffect::DamageHealth:\n            if (godmode)\n                break;\n            receivedMagicDamage = true;\n            adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);\n            break;\n\n        case ESM::MagicEffect::DamageMagicka:\n        case ESM::MagicEffect::DamageFatigue:\n        {\n            if (godmode)\n                break;\n            int index = effectKey.mId-ESM::MagicEffect::DamageHealth;\n            static const bool uncappedDamageFatigue = Settings::Manager::getBool(\"uncapped damage fatigue\", \"Game\");\n            adjustDynamicStat(creatureStats, index, -magnitude, index == 2 && uncappedDamageFatigue);\n            break;\n        }\n        case ESM::MagicEffect::AbsorbHealth:\n            if (!godmode || magnitude <= 0)\n            {\n                if (magnitude > 0.f)\n                    receivedMagicDamage = true;\n                adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);\n            }\n            break;\n\n        case ESM::MagicEffect::AbsorbMagicka:\n        case ESM::MagicEffect::AbsorbFatigue:\n            if (!godmode || magnitude <= 0)\n                adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);\n            break;\n\n        case ESM::MagicEffect::DisintegrateArmor:\n        {\n            if (godmode)\n                break;\n            static const std::array<int, 9>  priorities\n            {\n                MWWorld::InventoryStore::Slot_CarriedLeft,\n                MWWorld::InventoryStore::Slot_Cuirass,\n                MWWorld::InventoryStore::Slot_LeftPauldron,\n                MWWorld::InventoryStore::Slot_RightPauldron,\n                MWWorld::InventoryStore::Slot_LeftGauntlet,\n                MWWorld::InventoryStore::Slot_RightGauntlet,\n                MWWorld::InventoryStore::Slot_Helmet,\n                MWWorld::InventoryStore::Slot_Greaves,\n                MWWorld::InventoryStore::Slot_Boots\n            };\n            for (const int priority : priorities)\n            {\n                if (disintegrateSlot(actor, priority, magnitude))\n                    break;\n            }\n\n            break;\n        }\n        case ESM::MagicEffect::DisintegrateWeapon:\n            if (!godmode)\n                disintegrateSlot(actor, MWWorld::InventoryStore::Slot_CarriedRight, magnitude);\n            break;\n\n        case ESM::MagicEffect::SunDamage:\n        {\n            // isInCell shouldn't be needed, but updateActor called during game start\n            if (!actor.isInCell() || !actor.getCell()->isExterior() || godmode)\n                break;\n            float time = MWBase::Environment::get().getWorld()->getTimeStamp().getHour();\n            float timeDiff = std::min(7.f, std::max(0.f, std::abs(time - 13)));\n            float damageScale = 1.f - timeDiff / 7.f;\n            // When cloudy, the sun damage effect is halved\n            static float fMagicSunBlockedMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\n                        \"fMagicSunBlockedMult\")->mValue.getFloat();\n\n            int weather = MWBase::Environment::get().getWorld()->getCurrentWeather();\n            if (weather > 1)\n                damageScale *= fMagicSunBlockedMult;\n\n            adjustDynamicStat(creatureStats, 0, -magnitude * damageScale);\n            if (magnitude * damageScale > 0.f)\n                receivedMagicDamage = true;\n\n            break;\n        }\n\n        case ESM::MagicEffect::FireDamage:\n        case ESM::MagicEffect::ShockDamage:\n        case ESM::MagicEffect::FrostDamage:\n        case ESM::MagicEffect::Poison:\n        {\n            if (godmode)\n                break;\n            adjustDynamicStat(creatureStats, 0, -magnitude);\n            receivedMagicDamage = true;\n            break;\n        }\n\n        case ESM::MagicEffect::DamageSkill:\n        case ESM::MagicEffect::RestoreSkill:\n        {\n            if (!actor.getClass().isNpc())\n                break;\n            if (godmode && effectKey.mId == ESM::MagicEffect::DamageSkill)\n                break;\n            NpcStats &npcStats = actor.getClass().getNpcStats(actor);\n            SkillValue& skill = npcStats.getSkill(effectKey.mArg);\n            if (effectKey.mId == ESM::MagicEffect::RestoreSkill)\n                skill.restore(magnitude);\n            else\n                skill.damage(magnitude);\n            break;\n        }\n\n        default:\n            return false;\n        }\n\n        if (receivedMagicDamage && actor == getPlayer())\n            MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);\n        return true;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/tickableeffects.hpp",
    "content": "#ifndef MWMECHANICS_TICKABLEEFFECTS_H\n#define MWMECHANICS_TICKABLEEFFECTS_H\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    class CreatureStats;\n    struct EffectKey;\n\n    /// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed\n    /// Note: this function works in loop, so magic effects should not be removed here to avoid iterator invalidation.\n    /// @return Was the effect a tickable effect with a magnitude?\n    bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey& effectKey, float magnitude);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/trading.cpp",
    "content": "#include \"trading.hpp\"\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"creaturestats.hpp\"\n\nnamespace MWMechanics\n{\n    Trading::Trading() {}\n\n    bool Trading::haggle(const MWWorld::Ptr& player, const MWWorld::Ptr& merchant, int playerOffer, int merchantOffer)\n    {\n        // accept if merchant offer is better than player offer\n        if ( playerOffer <= merchantOffer ) {\n            return true;\n        }\n\n        // reject if npc is a creature\n        if ( merchant.getTypeName() != typeid(ESM::NPC).name() ) {\n            return false;\n        }\n\n        const MWWorld::Store<ESM::GameSetting> &gmst =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        // Is the player buying?\n        bool buying = (merchantOffer < 0);\n        int a = std::abs(merchantOffer);\n        int b = std::abs(playerOffer);\n        int d = (buying)\n            ? int(100 * (a - b) / a)\n            : int(100 * (b - a) / b);\n\n        int clampedDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(merchant);\n\n        const MWMechanics::CreatureStats &merchantStats = merchant.getClass().getCreatureStats(merchant);\n        const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player);\n\n        float a1 = static_cast<float>(player.getClass().getSkill(player, ESM::Skill::Mercantile));\n        float b1 = 0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified();\n        float c1 = 0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified();\n        float d1 = static_cast<float>(merchant.getClass().getSkill(merchant, ESM::Skill::Mercantile));\n        float e1 = 0.1f * merchantStats.getAttribute(ESM::Attribute::Luck).getModified();\n        float f1 = 0.2f * merchantStats.getAttribute(ESM::Attribute::Personality).getModified();\n\n        float dispositionTerm = gmst.find(\"fDispositionMod\")->mValue.getFloat() * (clampedDisposition - 50);\n        float pcTerm = (dispositionTerm + a1 + b1 + c1) * playerStats.getFatigueTerm();\n        float npcTerm = (d1 + e1 + f1) * merchantStats.getFatigueTerm();\n        float x = gmst.find(\"fBargainOfferMulti\")->mValue.getFloat() * d\n            + gmst.find(\"fBargainOfferBase\")->mValue.getFloat()\n            + int(pcTerm - npcTerm);\n\n        int roll = Misc::Rng::rollDice(100) + 1;\n\n        // reject if roll fails\n        // (or if player tries to buy things and get money)\n        if ( roll > x || (merchantOffer < 0 && 0 < playerOffer) ) {\n            return false;\n        }\n\n        // apply skill gain on successful barter\n        float skillGain = 0.f;\n        int finalPrice = std::abs(playerOffer);\n        int initialMerchantOffer = std::abs(merchantOffer);\n\n        if ( !buying && (finalPrice > initialMerchantOffer) ) {\n            skillGain = floor(100.f * (finalPrice - initialMerchantOffer) / finalPrice);\n        }\n        else if ( buying && (finalPrice < initialMerchantOffer) ) {\n            skillGain = floor(100.f * (initialMerchantOffer - finalPrice) / initialMerchantOffer);\n        }\n        player.getClass().skillUsageSucceeded(player, ESM::Skill::Mercantile, 0, skillGain);\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/trading.hpp",
    "content": "#ifndef OPENMW_MECHANICS_TRADING_H\n#define OPENMW_MECHANICS_TRADING_H\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWMechanics\n{\n    class Trading\n    {\n    public:\n        Trading();\n\n        bool haggle(const MWWorld::Ptr& player, const MWWorld::Ptr& merchant, int playerOffer, int merchantOffer);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/typedaipackage.hpp",
    "content": "#ifndef GAME_MWMECHANICS_TYPEDAIPACKAGE_H\n#define GAME_MWMECHANICS_TYPEDAIPACKAGE_H\n\n#include \"aipackage.hpp\"\n\nnamespace MWMechanics\n{\n    template <class T>\n    struct TypedAiPackage : public AiPackage\n    {\n        TypedAiPackage() :\n            AiPackage(T::getTypeId(), T::makeDefaultOptions()) {}\n\n        TypedAiPackage(const Options& options) :\n            AiPackage(T::getTypeId(), options) {}\n\n        template <class Derived>\n        TypedAiPackage(Derived*) :\n            AiPackage(Derived::getTypeId(), Derived::makeDefaultOptions()) {}\n\n        std::unique_ptr<AiPackage> clone() const override\n        {\n            return std::make_unique<T>(*static_cast<const T*>(this));\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/weaponpriority.cpp",
    "content": "#include \"weaponpriority.hpp\"\n\n#include <components/esm/loadench.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"combat.hpp\"\n#include \"aicombataction.hpp\"\n#include \"spellpriority.hpp\"\n#include \"spellutil.hpp\"\n#include \"weapontype.hpp\"\n\nnamespace MWMechanics\n{\n    float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type,\n                      float arrowRating, float boltRating)\n    {\n        if (enemy.isEmpty() || item.getTypeName() != typeid(ESM::Weapon).name())\n            return 0.f;\n\n        if (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) == 0)\n            return 0.f;\n\n        const ESM::Weapon* weapon = item.get<ESM::Weapon>()->mBase;\n\n        if (type != -1 && weapon->mData.mType != type)\n            return 0.f;\n\n        const MWBase::World* world = MWBase::Environment::get().getWorld();\n        const MWWorld::Store<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>();\n\n        ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(weapon->mData.mType)->mWeaponClass;\n        if (type == -1 && weapclass == ESM::WeaponType::Ammo)\n            return 0.f;\n\n        float rating=0.f;\n        static const float fAIMeleeWeaponMult = gmst.find(\"fAIMeleeWeaponMult\")->mValue.getFloat();\n        float ratingMult = fAIMeleeWeaponMult;\n\n        if (weapclass != ESM::WeaponType::Melee)\n        {\n            // Underwater ranged combat is impossible\n            if (world->isUnderwater(MWWorld::ConstPtr(actor), 0.75f)\n             || world->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))\n                return 0.f;\n\n            // Use a higher rating multiplier if the actor is out of enemy's reach, use the normal mult otherwise\n            if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy))\n            {\n                static const float fAIRangeMeleeWeaponMult = gmst.find(\"fAIRangeMeleeWeaponMult\")->mValue.getFloat();\n                ratingMult = fAIRangeMeleeWeaponMult;\n            }\n        }\n\n        const float chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f;\n        // We need to account for the fact that thrown weapons have 2x real damage applied to the target\n        // as they're both the weapon and the ammo of the hit\n        if (weapclass == ESM::WeaponType::Thrown)\n        {\n            rating = chop * 2;\n        }\n        else if (weapclass != ESM::WeaponType::Melee)\n        {\n            rating = chop;\n        }\n        else\n        {\n            const float slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1]) / 2.f;\n            const float thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1]) / 2.f;\n            rating = (slash * slash + thrust * thrust + chop * chop) / (slash + thrust + chop);\n        }\n\n        adjustWeaponDamage(rating, item, actor);\n\n        if (weapclass != ESM::WeaponType::Ranged)\n        {\n            resistNormalWeapon(enemy, actor, item, rating);\n            applyWerewolfDamageMult(enemy, item, rating);\n        }\n        else\n        {\n            int ammotype = MWMechanics::getWeaponType(weapon->mData.mType)->mAmmoType;\n            if (ammotype == ESM::Weapon::Arrow)\n            {\n                if (arrowRating <= 0.f)\n                    rating = 0.f;\n                else\n                    rating += arrowRating;\n            }\n            else if (ammotype == ESM::Weapon::Bolt)\n            {\n                if (boltRating <= 0.f)\n                    rating = 0.f;\n                else\n                    rating += boltRating;\n            }\n        }\n\n        if (!weapon->mEnchant.empty())\n        {\n            const ESM::Enchantment* enchantment = world->getStore().get<ESM::Enchantment>().find(weapon->mEnchant);\n            if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)\n            {\n                int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);\n                float charge = item.getCellRef().getEnchantmentCharge();\n\n                if (charge == -1 || charge >= castCost || weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo)\n                    rating += rateEffects(enchantment->mEffects, actor, enemy);\n            }\n        }\n\n        int value = 50.f;\n        if (actor.getClass().isNpc())\n        {\n            int skill = item.getClass().getEquipmentSkill(item);\n            if (skill != -1)\n               value = actor.getClass().getSkill(actor, skill);\n        }\n        else\n        {\n            MWWorld::LiveCellRef<ESM::Creature> *ref = actor.get<ESM::Creature>();\n            value = ref->mBase->mData.mCombat;\n        }\n\n        // Take hit chance in account, but do not allow rating become negative.\n        float chance = getHitChance(actor, enemy, value) / 100.f;\n        rating *= std::min(1.f, std::max(0.01f, chance));\n\n        if (weapclass != ESM::WeaponType::Ammo)\n            rating *= weapon->mData.mSpeed;\n\n        return rating * ratingMult;\n    }\n\n    float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, int ammoType)\n    {\n        float bestAmmoRating = 0.f;\n        if (!actor.getClass().hasInventoryStore(actor))\n            return bestAmmoRating;\n\n        MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);\n\n        for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n        {\n            float rating = rateWeapon(*it, actor, enemy, ammoType);\n            if (rating > bestAmmoRating)\n            {\n                bestAmmoRating = rating;\n                bestAmmo = *it;\n            }\n        }\n\n        return bestAmmoRating;\n    }\n\n    float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, int ammoType)\n    {\n        MWWorld::Ptr emptyPtr;\n        return rateAmmo(actor, enemy, emptyPtr, ammoType);\n    }\n\n    float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)\n    {\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        static const float fAIMeleeWeaponMult = gmst.find(\"fAIMeleeWeaponMult\")->mValue.getFloat();\n        static const float fAIMeleeArmorMult = gmst.find(\"fAIMeleeArmorMult\")->mValue.getFloat();\n        static const float fAIRangeMeleeWeaponMult = gmst.find(\"fAIRangeMeleeWeaponMult\")->mValue.getFloat();\n\n        if (weapon.isEmpty())\n            return 0.f;\n\n        float skillMult = actor.getClass().getSkill(actor, weapon.getClass().getEquipmentSkill(weapon)) * 0.01f;\n        float chopMult = fAIMeleeWeaponMult;\n        float bonusDamage = 0.f;\n\n        const ESM::Weapon* esmWeap = weapon.get<ESM::Weapon>()->mBase;\n        int type = esmWeap->mData.mType;\n        if (getWeaponType(type)->mWeaponClass != ESM::WeaponType::Melee)\n        {\n            if (!ammo.isEmpty() && !MWBase::Environment::get().getWorld()->isSwimming(enemy))\n            {\n                bonusDamage = ammo.get<ESM::Weapon>()->mBase->mData.mChop[1];\n                chopMult = fAIRangeMeleeWeaponMult;\n            }\n            else\n                chopMult = 0.f;\n        }\n\n        float chopRating = (esmWeap->mData.mChop[1] + bonusDamage) * skillMult * chopMult;\n        float slashRating = esmWeap->mData.mSlash[1] * skillMult * fAIMeleeWeaponMult;\n        float thrustRating = esmWeap->mData.mThrust[1] * skillMult * fAIMeleeWeaponMult;\n\n        return actor.getClass().getArmorRating(actor) * fAIMeleeArmorMult\n                    + std::max(std::max(chopRating, slashRating), thrustRating);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/weaponpriority.hpp",
    "content": "#ifndef OPENMW_WEAPON_PRIORITY_H\n#define OPENMW_WEAPON_PRIORITY_H\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWMechanics\n{\n    float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy,\n                      int type=-1, float arrowRating=0.f, float boltRating=0.f);\n\n    float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, int ammoType);\n    float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, int ammoType);\n\n    float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmechanics/weapontype.cpp",
    "content": "#include \"weapontype.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\nnamespace MWMechanics\n{\n    MWWorld::ContainerStoreIterator getActiveWeapon(MWWorld::Ptr actor, int *weaptype)\n    {\n        MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor);\n        CreatureStats &stats = actor.getClass().getCreatureStats(actor);\n        if(stats.getDrawState() == MWMechanics::DrawState_Spell)\n        {\n            *weaptype = ESM::Weapon::Spell;\n            return inv.end();\n        }\n\n        if(stats.getDrawState() == MWMechanics::DrawState_Weapon)\n        {\n            MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n            if(weapon == inv.end())\n                *weaptype = ESM::Weapon::HandToHand;\n            else\n            {\n                const std::string &type = weapon->getTypeName();\n                if(type == typeid(ESM::Weapon).name())\n                {\n                    const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();\n                    *weaptype = ref->mBase->mData.mType;\n                }\n                else if (type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())\n                    *weaptype = ESM::Weapon::PickProbe;\n            }\n\n            return weapon;\n        }\n\n        return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    }\n\n    const ESM::WeaponType* getWeaponType(const int weaponType)\n    {\n        std::map<int, ESM::WeaponType>::const_iterator found = sWeaponTypeList.find(weaponType);\n        if (found == sWeaponTypeList.end())\n        {\n            // Use one-handed short blades as fallback\n            return &sWeaponTypeList[0];\n        }\n\n         return &found->second;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmechanics/weapontype.hpp",
    "content": "#ifndef GAME_MWMECHANICS_WEAPONTYPE_H\n#define GAME_MWMECHANICS_WEAPONTYPE_H\n\n#include \"../mwworld/inventorystore.hpp\"\n\nnamespace MWMechanics\n{\n    static std::map<int, ESM::WeaponType> sWeaponTypeList =\n    {\n        {\n            ESM::Weapon::None,\n            {\n                /* short group */ \"\",\n                /* long group  */ \"\",\n                /*  sound ID   */ \"\",\n                /* attach bone */ \"\",\n                /* sheath bone */ \"\",\n                /* usage skill */ ESM::Skill::HandToHand,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ 0\n            }\n        },\n        {\n            ESM::Weapon::PickProbe,\n            {\n                /* short group */ \"1h\",\n                /* long group  */ \"pickprobe\",\n                /*  sound ID   */ \"\",\n                /* attach bone */ \"\",\n                /* sheath bone */ \"\",\n                /* usage skill */ ESM::Skill::Security,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ 0\n            }\n        },\n        {\n            ESM::Weapon::Spell,\n            {\n                /* short group */ \"spell\",\n                /* long group  */ \"spellcast\",\n                /*  sound ID   */ \"\",\n                /* attach bone */ \"\",\n                /* sheath bone */ \"\",\n                /* usage skill */ ESM::Skill::HandToHand,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::HandToHand,\n            {\n                /* short group */ \"hh\",\n                /* long group  */ \"handtohand\",\n                /*  sound ID   */ \"\",\n                /* attach bone */ \"\",\n                /* sheath bone */ \"\",\n                /* usage skill */ ESM::Skill::HandToHand,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::ShortBladeOneHand,\n            {\n                /* short group */ \"1s\",\n                /* long group  */ \"shortbladeonehand\",\n                /*  sound ID   */ \"Item Weapon Shortblade\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 ShortBladeOneHand\",\n                /* usage skill */ ESM::Skill::ShortBlade,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth\n            }\n        },\n        {\n            ESM::Weapon::LongBladeOneHand,\n            {\n                /* short group */ \"1h\",\n                /* long group  */ \"weapononehand\",\n                /*  sound ID   */ \"Item Weapon Longblade\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 LongBladeOneHand\",\n                /* usage skill */ ESM::Skill::LongBlade,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth\n            }\n        },\n        {\n            ESM::Weapon::BluntOneHand,\n            {\n                /* short group */ \"1b\",\n                /* long group  */ \"bluntonehand\",\n                /*  sound ID   */ \"Item Weapon Blunt\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 BluntOneHand\",\n                /* usage skill */ ESM::Skill::BluntWeapon,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth\n            }\n        },\n        {\n            ESM::Weapon::AxeOneHand,\n            {\n                /* short group */ \"1b\",\n                /* long group  */ \"bluntonehand\",\n                /*  sound ID   */ \"Item Weapon Blunt\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 LongBladeOneHand\",\n                /* usage skill */ ESM::Skill::Axe,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth\n            }\n        },\n        {\n            ESM::Weapon::LongBladeTwoHand,\n            {\n                /* short group */ \"2c\",\n                /* long group  */ \"weapontwohand\",\n                /*  sound ID   */ \"Item Weapon Longblade\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 LongBladeTwoClose\",\n                /* usage skill */ ESM::Skill::LongBlade,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::AxeTwoHand,\n            {\n                /* short group */ \"2b\",\n                /* long group  */ \"blunttwohand\",\n                /*  sound ID   */ \"Item Weapon Blunt\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 AxeTwoClose\",\n                /* usage skill */ ESM::Skill::Axe,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::BluntTwoClose,\n            {\n                /* short group */ \"2b\",\n                /* long group  */ \"blunttwohand\",\n                /*  sound ID   */ \"Item Weapon Blunt\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 BluntTwoClose\",\n                /* usage skill */ ESM::Skill::BluntWeapon,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::BluntTwoWide,\n            {\n                /* short group */ \"2w\",\n                /* long group  */ \"weapontwowide\",\n                /*  sound ID   */ \"Item Weapon Blunt\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 BluntTwoWide\",\n                /* usage skill */ ESM::Skill::BluntWeapon,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::SpearTwoWide,\n            {\n                /* short group */ \"2w\",\n                /* long group  */ \"weapontwowide\",\n                /*  sound ID   */ \"Item Weapon Spear\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 SpearTwoWide\",\n                /* usage skill */ ESM::Skill::Spear,\n                /* weapon class*/ ESM::WeaponType::Melee,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::MarksmanBow,\n            {\n                /* short group */ \"bow\",\n                /* long group  */ \"bowandarrow\",\n                /*  sound ID   */ \"Item Weapon Bow\",\n                /* attach bone */ \"Weapon Bone Left\",\n                /* sheath bone */ \"Bip01 MarksmanBow\",\n                /* usage skill */ ESM::Skill::Marksman,\n                /* weapon class*/ ESM::WeaponType::Ranged,\n                /*  ammo type  */ ESM::Weapon::Arrow,\n                /*    flags    */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::MarksmanCrossbow,\n            {\n                /* short group */ \"crossbow\",\n                /* long group  */ \"crossbow\",\n                /*  sound ID   */ \"Item Weapon Crossbow\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 MarksmanCrossbow\",\n                /* usage skill */ ESM::Skill::Marksman,\n                /* weapon class*/ ESM::WeaponType::Ranged,\n                /*  ammo type  */ ESM::Weapon::Bolt,\n                /*    flags    */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded\n            }\n        },\n        {\n            ESM::Weapon::MarksmanThrown,\n            {\n                /* short group */ \"1t\",\n                /* long group  */ \"throwweapon\",\n                /*  sound ID   */ \"Item Weapon Blunt\",\n                /* attach bone */ \"Weapon Bone\",\n                /* sheath bone */ \"Bip01 MarksmanThrown\",\n                /* usage skill */ ESM::Skill::Marksman,\n                /* weapon class*/ ESM::WeaponType::Thrown,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ 0\n            }\n        },\n        {\n            ESM::Weapon::Arrow,\n            {\n                /* short group */ \"\",\n                /* long group  */ \"\",\n                /*  sound ID   */ \"Item Ammo\",\n                /* attach bone */ \"Bip01 Arrow\",\n                /* sheath bone */ \"\",\n                /* usage skill */ ESM::Skill::Marksman,\n                /* weapon class*/ ESM::WeaponType::Ammo,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ 0\n            }\n        },\n        {\n            ESM::Weapon::Bolt,\n            {\n                /* short group */ \"\",\n                /* long group  */ \"\",\n                /*  sound ID   */ \"Item Ammo\",\n                /* attach bone */ \"ArrowBone\",\n                /* sheath bone */ \"\",\n                /* usage skill */ ESM::Skill::Marksman,\n                /* weapon class*/ ESM::WeaponType::Ammo,\n                /*  ammo type  */ ESM::Weapon::None,\n                /*    flags    */ 0\n            }\n        }\n    };\n\n    MWWorld::ContainerStoreIterator getActiveWeapon(MWWorld::Ptr actor, int *weaptype);\n\n    const ESM::WeaponType* getWeaponType(const int weaponType);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwmp/ActorList.cpp",
    "content": "#include \"ActorList.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"LocalPlayer.hpp\"\n#include \"MechanicsHelper.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include <components/openmw-mp/TimedLog.hpp>\n\nusing namespace mwmp;\n\nActorList::ActorList()\n{\n\n}\n\nActorList::~ActorList()\n{\n\n}\n\nNetworking *ActorList::getNetworking()\n{\n    return mwmp::Main::get().getNetworking();\n}\n\nvoid ActorList::reset()\n{\n    cell.blank();\n    baseActors.clear();\n    positionActors.clear();\n    animFlagsActors.clear();\n    animPlayActors.clear();\n    speechActors.clear();\n    statsDynamicActors.clear();\n    deathActors.clear();\n    equipmentActors.clear();\n    aiActors.clear();\n    attackActors.clear();\n    castActors.clear();\n    cellChangeActors.clear();\n    guid = mwmp::Main::get().getNetworking()->getLocalPlayer()->guid;\n}\n\nvoid ActorList::addActor(BaseActor baseActor)\n{\n    baseActors.push_back(baseActor);\n}\n\nvoid ActorList::addPositionActor(BaseActor baseActor)\n{\n    positionActors.push_back(baseActor);\n}\n\nvoid ActorList::addAnimFlagsActor(BaseActor baseActor)\n{\n    animFlagsActors.push_back(baseActor);\n}\n\nvoid ActorList::addAnimPlayActor(BaseActor baseActor)\n{\n    animPlayActors.push_back(baseActor);\n}\n\nvoid ActorList::addSpeechActor(BaseActor baseActor)\n{\n    speechActors.push_back(baseActor);\n}\n\nvoid ActorList::addStatsDynamicActor(BaseActor baseActor)\n{\n    statsDynamicActors.push_back(baseActor);\n}\n\nvoid ActorList::addDeathActor(BaseActor baseActor)\n{\n    deathActors.push_back(baseActor);\n}\n\nvoid ActorList::addEquipmentActor(BaseActor baseActor)\n{\n    equipmentActors.push_back(baseActor);\n}\n\nvoid ActorList::addAiActor(BaseActor baseActor)\n{\n    aiActors.push_back(baseActor);\n}\n\nvoid ActorList::addAiActor(const MWWorld::Ptr& actorPtr, const MWWorld::Ptr& targetPtr, unsigned int aiAction)\n{\n    mwmp::BaseActor baseActor;\n    baseActor.refNum = actorPtr.getCellRef().getRefNum().mIndex;\n    baseActor.mpNum = actorPtr.getCellRef().getMpNum();\n    baseActor.aiAction = aiAction;\n    baseActor.aiTarget = MechanicsHelper::getTarget(targetPtr);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Preparing to send ID_ACTOR_AI about %s %i-%i\\n- action: %i\",\n        actorPtr.getCellRef().getRefId().c_str(), baseActor.refNum, baseActor.mpNum, aiAction);\n\n    if (baseActor.aiTarget.isPlayer)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"- Has player target %s\",\n            targetPtr.getClass().getName(targetPtr).c_str());\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"- Has actor target %s %i-%i\",\n            targetPtr.getCellRef().getRefId().c_str(), baseActor.aiTarget.refNum, baseActor.aiTarget.mpNum);\n    }\n\n    addAiActor(baseActor);\n}\n\nvoid ActorList::addAttackActor(BaseActor baseActor)\n{\n    attackActors.push_back(baseActor);\n}\n\nvoid ActorList::addAttackActor(const MWWorld::Ptr& actorPtr, const mwmp::Attack &attack)\n{\n    mwmp::BaseActor baseActor;\n    baseActor.refNum = actorPtr.getCellRef().getRefNum().mIndex;\n    baseActor.mpNum = actorPtr.getCellRef().getMpNum();\n    baseActor.attack = attack;\n    attackActors.push_back(baseActor);\n}\n\nvoid ActorList::addCastActor(BaseActor baseActor)\n{\n    castActors.push_back(baseActor);\n}\n\nvoid ActorList::addCellChangeActor(BaseActor baseActor)\n{\n    cellChangeActors.push_back(baseActor);\n}\n\nvoid ActorList::sendPositionActors()\n{\n    if (positionActors.size() > 0)\n    {\n        baseActors = positionActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_POSITION)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_POSITION)->Send();\n    }\n}\n\nvoid ActorList::sendAnimFlagsActors()\n{\n    if (animFlagsActors.size() > 0)\n    {\n        baseActors = animFlagsActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_ANIM_FLAGS)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_ANIM_FLAGS)->Send();\n    }\n}\n\nvoid ActorList::sendAnimPlayActors()\n{\n    if (animPlayActors.size() > 0)\n    {\n        baseActors = animPlayActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_ANIM_PLAY)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_ANIM_PLAY)->Send();\n    }\n}\n\nvoid ActorList::sendSpeechActors()\n{\n    if (speechActors.size() > 0)\n    {\n        baseActors = speechActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_SPEECH)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_SPEECH)->Send();\n    }\n}\n\nvoid ActorList::sendStatsDynamicActors()\n{\n    if (statsDynamicActors.size() > 0)\n    {\n        baseActors = statsDynamicActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_STATS_DYNAMIC)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_STATS_DYNAMIC)->Send();\n    }\n}\n\nvoid ActorList::sendDeathActors()\n{\n    if (deathActors.size() > 0)\n    {\n        baseActors = deathActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_DEATH)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_DEATH)->Send();\n    }\n}\n\nvoid ActorList::sendEquipmentActors()\n{\n    if (equipmentActors.size() > 0)\n    {\n        baseActors = equipmentActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_EQUIPMENT)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_EQUIPMENT)->Send();\n    }\n}\n\nvoid ActorList::sendAiActors()\n{\n    if (aiActors.size() > 0)\n    {\n        baseActors = aiActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_AI)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_AI)->Send();\n    }\n}\n\nvoid ActorList::sendAttackActors()\n{\n    if (attackActors.size() > 0)\n    {\n        baseActors = attackActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_ATTACK)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_ATTACK)->Send();\n    }\n}\n\nvoid ActorList::sendCastActors()\n{\n    if (castActors.size() > 0)\n    {\n        baseActors = castActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_CAST)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_CAST)->Send();\n    }\n}\n\nvoid ActorList::sendCellChangeActors()\n{\n    if (cellChangeActors.size() > 0)\n    {\n        baseActors = cellChangeActors;\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_CELL_CHANGE)->setActorList(this);\n        Main::get().getNetworking()->getActorPacket(ID_ACTOR_CELL_CHANGE)->Send();\n    }\n}\n\nvoid ActorList::sendActorsInCell(MWWorld::CellStore* cellStore)\n{\n    reset();\n    cell = *cellStore->getCell();\n    action = BaseActorList::SET;\n\n    for (auto &ref : cellStore->getNpcs()->mList)\n    {\n        MWWorld::Ptr ptr(&ref, 0);\n\n        // If this Ptr is lacking a unique index, ignore it\n        if (ptr.getCellRef().getRefNum().mIndex == 0 && ptr.getCellRef().getMpNum() == 0) continue;\n\n        BaseActor actor;\n        actor.refId = ptr.getCellRef().getRefId();\n        actor.refNum = ptr.getCellRef().getRefNum().mIndex;\n        actor.mpNum = ptr.getCellRef().getMpNum();\n\n        addActor(actor);\n    }\n\n    for (auto &ref : cellStore->getCreatures()->mList)\n    {\n        MWWorld::Ptr ptr(&ref, 0);\n\n        // If this Ptr is lacking a unique index, ignore it\n        if (ptr.getCellRef().getRefNum().mIndex == 0 && ptr.getCellRef().getMpNum() == 0) continue;\n\n        BaseActor actor;\n        actor.refId = ptr.getCellRef().getRefId();\n        actor.refNum = ptr.getCellRef().getRefNum().mIndex;\n        actor.mpNum = ptr.getCellRef().getMpNum();\n\n        addActor(actor);\n    }\n\n    mwmp::Main::get().getNetworking()->getActorPacket(ID_ACTOR_LIST)->setActorList(this);\n    mwmp::Main::get().getNetworking()->getActorPacket(ID_ACTOR_LIST)->Send();\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/ActorList.hpp",
    "content": "#ifndef OPENMW_ACTORLIST_HPP\n#define OPENMW_ACTORLIST_HPP\n\n#include <components/openmw-mp/Base/BaseActor.hpp>\n#include \"../mwworld/cellstore.hpp\"\n#include <RakNetTypes.h>\n\n#include \"LocalActor.hpp\"\n\nnamespace mwmp\n{\n    class Networking;\n    class ActorList : public BaseActorList\n    {\n    public:\n\n        ActorList();\n        virtual ~ActorList();\n\n        void reset();\n        void addActor(BaseActor baseActor);\n\n        void addPositionActor(BaseActor baseActor);\n        void addAnimFlagsActor(BaseActor baseActor);\n        void addAnimPlayActor(BaseActor baseActor);\n        void addSpeechActor(BaseActor baseActor);\n        void addStatsDynamicActor(BaseActor baseActor);\n        void addDeathActor(BaseActor baseActor);\n        void addEquipmentActor(BaseActor baseActor);\n        void addAiActor(BaseActor baseActor);\n        void addAiActor(const MWWorld::Ptr& actorPtr, const MWWorld::Ptr& targetPtr, unsigned int aiAction);\n        void addAttackActor(BaseActor baseActor);\n        void addAttackActor(const MWWorld::Ptr& actorPtr, const mwmp::Attack &attack);\n        void addCastActor(BaseActor baseActor);\n        void addCellChangeActor(BaseActor baseActor);\n\n        void sendPositionActors();\n        void sendAnimFlagsActors();\n        void sendAnimPlayActors();\n        void sendSpeechActors();\n        void sendStatsDynamicActors();\n        void sendDeathActors();\n        void sendEquipmentActors();\n        void sendAiActors();\n        void sendAttackActors();\n        void sendCastActors();\n        void sendCellChangeActors();\n\n        void sendActorsInCell(MWWorld::CellStore* cellStore);\n\n    private:\n        Networking *getNetworking();\n\n        std::vector<BaseActor> positionActors;\n        std::vector<BaseActor> animFlagsActors;\n        std::vector<BaseActor> animPlayActors;\n        std::vector<BaseActor> speechActors;\n        std::vector<BaseActor> statsDynamicActors;\n        std::vector<BaseActor> deathActors;\n        std::vector<BaseActor> equipmentActors;\n        std::vector<BaseActor> aiActors;\n        std::vector<BaseActor> attackActors;\n        std::vector<BaseActor> castActors;\n        std::vector<BaseActor> cellChangeActors;\n    };\n}\n\n#endif //OPENMW_ACTORLIST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/Cell.cpp",
    "content": "#include <components/esm/cellid.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/livecellref.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"Cell.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"LocalPlayer.hpp\"\n#include \"CellController.hpp\"\n#include \"MechanicsHelper.hpp\"\n\nusing namespace mwmp;\n\nmwmp::Cell::Cell(MWWorld::CellStore* cellStore)\n{\n    store = cellStore;\n    shouldInitializeActors = false;\n\n    updateTimer = 0;\n}\n\nCell::~Cell()\n{\n\n}\n\nvoid Cell::updateLocal(bool forceUpdate)\n{\n    if (localActors.empty())\n        return;\n\n    const float timeoutSec = 0.025;\n\n    if (!forceUpdate && (updateTimer += MWBase::Environment::get().getFrameDuration()) < timeoutSec)\n        return;\n    else\n        updateTimer = 0;\n\n    CellController *cellController = Main::get().getCellController();\n    ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList();\n    actorList->reset();\n\n    actorList->cell = *store->getCell();\n\n    for (auto it = localActors.begin(); it != localActors.end();)\n    {\n        LocalActor *actor = it->second;\n\n        MWWorld::CellStore *newStore = actor->getPtr().getCell();\n\n        if (newStore != store)\n        {\n            actor->updateCell();\n            std::string mapIndex = it->first;\n\n            // If the cell this actor has moved to is under our authority, move them to it\n            if (cellController->hasLocalAuthority(actor->cell))\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Moving LocalActor %s to our authority in %s\",\n                    mapIndex.c_str(), actor->cell.getShortDescription().c_str());\n                Cell *newCell = cellController->getCell(actor->cell);\n                newCell->localActors[mapIndex] = actor;\n                cellController->setLocalActorRecord(mapIndex, newCell->getShortDescription());\n            }\n            else\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Deleting LocalActor %s which is no longer under our authority\",\n                    mapIndex.c_str(), getShortDescription().c_str());\n                cellController->removeLocalActorRecord(mapIndex);\n                delete actor;\n            }\n\n            localActors.erase(it++);\n        }\n        else\n        {\n            if (actor->getPtr().getRefData().isEnabled())\n            {\n                if (actor->getPtr().getRefData().isDeleted())\n                {\n                    std::string mapIndex = it->first;\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Deleting LocalActor %s whose reference has been deleted\",\n                        mapIndex.c_str(), getShortDescription().c_str());\n                    cellController->removeLocalActorRecord(mapIndex);\n                    delete actor;\n                    localActors.erase(it++);\n                }\n                else\n                {\n                    // Forcibly update this local actor if its data has never been sent before;\n                    // otherwise, use the current forceUpdate value\n                    actor->update(actor->hasSentData ? forceUpdate : true);\n                }\n            }\n\n            ++it;\n        }\n    }\n\n    actorList->sendPositionActors();\n    actorList->sendAnimFlagsActors();\n    actorList->sendAnimPlayActors();\n    actorList->sendSpeechActors();\n    actorList->sendDeathActors();\n    actorList->sendStatsDynamicActors();\n    actorList->sendEquipmentActors();\n    actorList->sendAttackActors();\n    actorList->sendCastActors();\n    actorList->sendCellChangeActors();\n}\n\nvoid Cell::updateDedicated(float dt)\n{\n    if (dedicatedActors.empty()) return;\n    \n    for (auto &actor : dedicatedActors)\n        actor.second->update(dt);\n\n    // Are we the authority over this cell? If so, uninitialize DedicatedActors\n    // after the above update\n    if (hasLocalAuthority())\n        uninitializeDedicatedActors();\n}\n\nvoid Cell::readPositions(ActorList& actorList)\n{\n    initializeDedicatedActors(actorList);\n\n    if (dedicatedActors.empty()) return;\n    \n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->position = baseActor.position;\n            actor->direction = baseActor.direction;\n\n            if (!actor->hasPositionData)\n            {\n                actor->hasPositionData = true;\n\n                // If this is our first packet about this actor's position, force an update\n                // now instead of waiting for its frame\n                //\n                // That way, if this actor is about to become a LocalActor, initial data about it\n                // received from the server still gets set\n                actor->setPosition();\n            }\n        }\n    }\n}\n\nvoid Cell::readAnimFlags(ActorList& actorList)\n{\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->movementFlags = baseActor.movementFlags;\n            actor->drawState = baseActor.drawState;\n            actor->isFlying = baseActor.isFlying;\n        }\n    }\n}\n\nvoid Cell::readAnimPlay(ActorList& actorList)\n{\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->animation.groupname = baseActor.animation.groupname;\n            actor->animation.mode = baseActor.animation.mode;\n            actor->animation.count = baseActor.animation.count;\n            actor->animation.persist = baseActor.animation.persist;\n            actor->playAnimation();\n        }\n    }\n}\n\nvoid Cell::readStatsDynamic(ActorList& actorList)\n{\n    initializeDedicatedActors(actorList);\n\n    if (dedicatedActors.empty()) return;\n\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->creatureStats = baseActor.creatureStats;\n\n            if (!actor->hasStatsDynamicData)\n            {\n                actor->hasStatsDynamicData = true;\n\n                // If this is our first packet about this actor's dynamic stats, force an update\n                // now instead of waiting for its frame\n                //\n                // That way, if this actor is about to become a LocalActor, initial data about it\n                // received from the server still gets set\n                actor->setStatsDynamic();\n            }\n        }\n    }\n}\n\nvoid Cell::readDeath(ActorList& actorList)\n{\n    initializeDedicatedActors(actorList);\n\n    if (dedicatedActors.empty()) return;\n\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->creatureStats.mDead = true;\n            actor->creatureStats.mDynamic[0].mCurrent = 0;\n\n            Main::get().getCellController()->setQueuedDeathState(actor->getPtr(), baseActor.deathState);\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_ACTOR_DEATH about %s %i-%i in cell %s\\n- deathState: %d\\n-isInstantDeath: %s\",\n                actor->refId.c_str(), actor->refNum, actor->mpNum, getShortDescription().c_str(),\n                baseActor.deathState, baseActor.isInstantDeath ? \"true\" : \"false\");\n\n            if (baseActor.isInstantDeath)\n            {\n                actor->getPtr().getClass().getCreatureStats(actor->getPtr()).setDeathAnimationFinished(true);\n                MWBase::Environment::get().getWorld()->enableActorCollision(actor->getPtr(), false);\n            }\n        }\n    }\n}\n\nvoid Cell::readEquipment(ActorList& actorList)\n{\n    initializeDedicatedActors(actorList);\n\n    if (dedicatedActors.empty()) return;\n\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n\n            for (int slot = 0; slot < 19; ++slot)\n                actor->equipmentItems[slot] = baseActor.equipmentItems[slot];\n\n            actor->setEquipment();\n        }\n    }\n\n    if (hasLocalAuthority())\n        uninitializeDedicatedActors(actorList);\n}\n\nvoid Cell::readSpeech(ActorList& actorList)\n{\n    initializeDedicatedActors(actorList);\n\n    if (dedicatedActors.empty()) return;\n\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->sound = baseActor.sound;\n            actor->playSound();\n        }\n    }\n\n    if (hasLocalAuthority())\n        uninitializeDedicatedActors(actorList);\n}\n\nvoid Cell::readSpellsActive(ActorList& actorList)\n{\n    initializeDedicatedActors(actorList);\n\n    if (dedicatedActors.empty()) return;\n\n    for (const auto& baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor* actor = dedicatedActors[mapIndex];\n            actor->spellsActiveChanges = baseActor.spellsActiveChanges;\n\n            int spellsActiveAction = baseActor.spellsActiveChanges.action;\n\n            if (spellsActiveAction == SpellsActiveChanges::ADD)\n                actor->addSpellsActive();\n            else if (spellsActiveAction == SpellsActiveChanges::REMOVE)\n                actor->removeSpellsActive();\n            else\n                actor->setSpellsActive();\n        }\n    }\n\n    if (hasLocalAuthority())\n        uninitializeDedicatedActors(actorList);\n}\n\nvoid Cell::readAi(ActorList& actorList)\n{\n    initializeDedicatedActors(actorList);\n\n    if (dedicatedActors.empty()) return;\n\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->aiAction = baseActor.aiAction;\n            actor->aiDistance = baseActor.aiDistance;\n            actor->aiDuration = baseActor.aiDuration;\n            actor->aiShouldRepeat = baseActor.aiShouldRepeat;\n            actor->aiCoordinates = baseActor.aiCoordinates;\n            actor->hasAiTarget = baseActor.hasAiTarget;\n            actor->aiTarget = baseActor.aiTarget;\n            actor->setAi();\n        }\n    }\n\n    if (hasLocalAuthority())\n        uninitializeDedicatedActors(actorList);\n}\n\nvoid Cell::readAttack(ActorList& actorList)\n{\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Reading ActorAttack about %s\", mapIndex.c_str());\n\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->attack = baseActor.attack;\n\n            MechanicsHelper::processAttack(actor->attack, actor->getPtr());\n        }\n    }\n}\n\nvoid Cell::readCast(ActorList& actorList)\n{\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Reading ActorCast about %s\", mapIndex.c_str());\n\n            DedicatedActor *actor = dedicatedActors[mapIndex];\n            actor->cast = baseActor.cast;\n\n            // Set the correct drawState here if we've somehow we've missed a previous\n            // AnimFlags packet\n            if (actor->drawState != MWMechanics::DrawState_::DrawState_Spell)\n            {\n                actor->drawState = MWMechanics::DrawState_::DrawState_Spell;\n                actor->setAnimFlags();\n            }\n\n            MechanicsHelper::processCast(actor->cast, actor->getPtr());\n        }\n    }\n}\n\nvoid Cell::readCellChange(ActorList& actorList)\n{\n    initializeDedicatedActors(actorList);\n\n    if (dedicatedActors.empty()) return;\n\n    CellController *cellController = Main::get().getCellController();\n\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        // Is a packet mistakenly moving the actor to the cell it's already in? If so, ignore it\n        if (Misc::StringUtils::ciEqual(getShortDescription(), baseActor.cell.getShortDescription()))\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Server says DedicatedActor %s moved to %s, but it was already there\",\n                mapIndex.c_str(), getShortDescription().c_str());\n            continue;\n        }\n\n        if (dedicatedActors.count(mapIndex) > 0)\n        {\n            DedicatedActor *dedicatedActor = dedicatedActors[mapIndex];\n            dedicatedActor->cell = baseActor.cell;\n            dedicatedActor->position = baseActor.position;\n            dedicatedActor->direction = baseActor.direction;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Server says DedicatedActor %s moved to %s\",\n                mapIndex.c_str(), dedicatedActor->cell.getShortDescription().c_str());\n\n            MWWorld::CellStore *newStore = cellController->getCellStore(dedicatedActor->cell);\n            dedicatedActor->setCell(newStore);\n\n            // If the cell this actor has moved to is active and not under our authority, move them to it\n            if (cellController->isActiveWorldCell(dedicatedActor->cell) && !cellController->hasLocalAuthority(dedicatedActor->cell))\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Moving DedicatedActor %s to our active cell %s\",\n                    mapIndex.c_str(), dedicatedActor->cell.getShortDescription().c_str());\n                cellController->initializeCell(dedicatedActor->cell);\n                Cell *newCell = cellController->getCell(dedicatedActor->cell);\n                newCell->dedicatedActors[mapIndex] = dedicatedActor;\n                cellController->setDedicatedActorRecord(mapIndex, newCell->getShortDescription());\n            }\n            else\n            {\n                if (cellController->hasLocalAuthority(dedicatedActor->cell))\n                {\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Creating new LocalActor based on %s in %s\",\n                        mapIndex.c_str(), dedicatedActor->cell.getShortDescription().c_str());\n                    Cell *newCell = cellController->getCell(dedicatedActor->cell);\n                    LocalActor *localActor = new LocalActor();\n                    localActor->cell = dedicatedActor->cell;\n                    localActor->setPtr(dedicatedActor->getPtr());\n                    localActor->position = dedicatedActor->position;\n                    localActor->direction = dedicatedActor->direction;\n                    localActor->movementFlags = dedicatedActor->movementFlags;\n                    localActor->drawState = dedicatedActor->drawState;\n                    localActor->isFlying = dedicatedActor->isFlying;\n                    localActor->creatureStats = dedicatedActor->creatureStats;\n\n                    newCell->localActors[mapIndex] = localActor;\n                    cellController->setLocalActorRecord(mapIndex, newCell->getShortDescription());\n                }\n\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Deleting DedicatedActor %s which is no longer needed\",\n                    mapIndex.c_str(), getShortDescription().c_str());\n                cellController->removeDedicatedActorRecord(mapIndex);\n                delete dedicatedActor;\n            }\n\n            dedicatedActors.erase(mapIndex);\n        }\n    }\n}\n\nvoid Cell::initializeLocalActor(const MWWorld::Ptr& ptr)\n{\n    std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Initializing LocalActor %s in %s\", mapIndex.c_str(), getShortDescription().c_str());\n\n    LocalActor *actor = new LocalActor();\n    actor->cell = *store->getCell();\n    actor->setPtr(ptr);\n\n    localActors[mapIndex] = actor;\n\n    Main::get().getCellController()->setLocalActorRecord(mapIndex, getShortDescription());\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Successfully initialized LocalActor %s in %s\", mapIndex.c_str(), getShortDescription().c_str());\n}\n\nvoid Cell::initializeLocalActors()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Initializing LocalActors in %s\", getShortDescription().c_str());\n\n    for (const auto &mergedRef : store->getMergedRefs())\n    {\n        if (mergedRef->mClass->isActor())\n        {\n            MWWorld::Ptr ptr(mergedRef, store);\n\n            // If this Ptr is lacking a unique index, ignore it\n            if (ptr.getCellRef().getRefNum().mIndex == 0 && ptr.getCellRef().getMpNum() == 0) continue;\n\n            // If this Ptr is disabled or deleted, ignore it\n            if (!ptr.getRefData().isEnabled() || ptr.getRefData().isDeleted()) continue;\n\n            std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);\n\n            // Only initialize this actor if it isn't already initialized\n            if (localActors.count(mapIndex) == 0)\n                initializeLocalActor(ptr);\n        }\n    }\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Successfully initialized LocalActors in %s\", getShortDescription().c_str());\n}\n\nvoid Cell::initializeDedicatedActor(const MWWorld::Ptr& ptr)\n{\n    std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Initializing DedicatedActor %s in %s\", mapIndex.c_str(), getShortDescription().c_str());\n\n    DedicatedActor *actor = new DedicatedActor();\n    actor->cell = *store->getCell();\n    actor->setPtr(ptr);\n\n    dedicatedActors[mapIndex] = actor;\n\n    Main::get().getCellController()->setDedicatedActorRecord(mapIndex, getShortDescription());\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Successfully initialized DedicatedActor %s in %s\", mapIndex.c_str(), getShortDescription().c_str());\n}\n\nvoid Cell::initializeDedicatedActors(ActorList& actorList)\n{\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n\n        // If this key doesn't exist, create it\n        if (dedicatedActors.count(mapIndex) == 0)\n        {\n            MWWorld::Ptr ptrFound = store->searchExact(baseActor.refNum, baseActor.mpNum, baseActor.refId, true);\n\n            if (!ptrFound) continue;\n\n            initializeDedicatedActor(ptrFound);\n        }\n    }\n}\n\nvoid Cell::uninitializeLocalActors()\n{\n    for (const auto &actor : localActors)\n    {\n        Main::get().getCellController()->removeLocalActorRecord(actor.first);\n        delete actor.second;\n    }\n\n    localActors.clear();\n}\n\nvoid Cell::uninitializeDedicatedActors(ActorList& actorList)\n{\n    for (const auto &baseActor : actorList.baseActors)\n    {\n        std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor);\n        Main::get().getCellController()->removeDedicatedActorRecord(mapIndex);\n        delete dedicatedActors.at(mapIndex);\n        dedicatedActors.erase(mapIndex);\n    }\n}\n\nvoid Cell::uninitializeDedicatedActors()\n{\n    for (const auto &actor : dedicatedActors)\n    {\n        Main::get().getCellController()->removeDedicatedActorRecord(actor.first);\n        delete actor.second;\n    }\n\n    dedicatedActors.clear();\n}\n\nLocalActor *Cell::getLocalActor(std::string actorIndex)\n{\n    return localActors.at(actorIndex);\n}\n\nDedicatedActor *Cell::getDedicatedActor(std::string actorIndex)\n{\n    return dedicatedActors.at(actorIndex);\n}\n\nbool Cell::hasLocalAuthority()\n{\n    return authorityGuid == Main::get().getLocalPlayer()->guid;\n}\n\nvoid Cell::setAuthority(const RakNet::RakNetGUID& guid)\n{\n    authorityGuid = guid;\n}\n\nMWWorld::CellStore *Cell::getCellStore()\n{\n    return store;\n}\n\nstd::string Cell::getShortDescription()\n{\n    return store->getCell()->getShortDescription();\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/Cell.hpp",
    "content": "#ifndef OPENMW_MPCELL_HPP\n#define OPENMW_MPCELL_HPP\n\n#include \"ActorList.hpp\"\n#include \"LocalActor.hpp\"\n#include \"DedicatedActor.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\nnamespace mwmp\n{\n    class Cell\n    {\n    public:\n\n        Cell(MWWorld::CellStore* cellStore);\n        virtual ~Cell();\n\n        void updateLocal(bool forceUpdate);\n        void updateDedicated(float dt);\n\n        void readPositions(ActorList& actorList);\n        void readAnimFlags(ActorList& actorList);\n        void readAnimPlay(ActorList& actorList);\n        void readStatsDynamic(ActorList& actorList);\n        void readDeath(ActorList& actorList);\n        void readEquipment(ActorList& actorList);\n        void readSpeech(ActorList& actorList);\n        void readSpellsActive(ActorList& actorList);\n        void readAi(ActorList& actorList);\n        void readAttack(ActorList& actorList);\n        void readCast(ActorList& actorList);\n        void readCellChange(ActorList& actorList);\n\n        void initializeLocalActor(const MWWorld::Ptr& ptr);\n        void initializeLocalActors();\n\n        void initializeDedicatedActor(const MWWorld::Ptr& ptr);\n        void initializeDedicatedActors(ActorList& actorList);\n\n        void uninitializeLocalActors();\n        void uninitializeDedicatedActors(ActorList& actorList);\n        void uninitializeDedicatedActors();\n\n        virtual LocalActor *getLocalActor(std::string actorIndex);\n        virtual DedicatedActor *getDedicatedActor(std::string actorIndex);\n\n        bool hasLocalAuthority();\n        void setAuthority(const RakNet::RakNetGUID& guid);\n\n        MWWorld::CellStore* getCellStore();\n        std::string getShortDescription();\n\n        bool shouldInitializeActors;\n\n    private:\n        MWWorld::CellStore* store;\n        RakNet::RakNetGUID authorityGuid;\n\n        std::map<std::string, LocalActor *> localActors;\n        std::map<std::string, DedicatedActor *> dedicatedActors;\n\n        float updateTimer;\n    };\n}\n\n#endif //OPENMW_MPCELL_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/CellController.cpp",
    "content": "#include <components/detournavigator/navigator.hpp>\n#include <components/esm/cellid.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Utils.hpp>\n\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"CellController.hpp\"\n#include \"Main.hpp\"\n#include \"LocalActor.hpp\"\n#include \"LocalPlayer.hpp\"\nusing namespace mwmp;\n\nstd::map<std::string, mwmp::Cell *> CellController::cellsInitialized;\nstd::map<std::string, std::string> CellController::localActorsToCells;\nstd::map<std::string, std::string> CellController::dedicatedActorsToCells;\nstd::map<std::string, unsigned int> CellController::queuedDeathStates;\n\nmwmp::CellController::CellController()\n{\n\n}\n\nCellController::~CellController()\n{\n\n}\n\nvoid CellController::updateLocal(bool forceUpdate)\n{\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n\n    // Loop through Cells, deleting inactive ones and updating LocalActors in active ones\n    for (auto it = cellsInitialized.begin(); it != cellsInitialized.end();)\n    {\n        mwmp::Cell *mpCell = it->second;\n\n        if (mpCell->getCellStore() == nullptr || mpCell->getCellStore()->getCell() == nullptr || !world->isCellActive(*mpCell->getCellStore()->getCell()))\n        {\n            mpCell->uninitializeLocalActors();\n            mpCell->uninitializeDedicatedActors();\n            delete it->second;\n            cellsInitialized.erase(it++);\n        }\n        else\n        {\n            mpCell->updateLocal(forceUpdate);\n            ++it;\n        }\n    }\n\n    // If there are cellsInitialized remaining, loop through them and initialize new LocalActors for eligible ones\n    // \n    //\n    // Note: This cannot be combined with the above loop because initializing LocalActors in a Cell before they are\n    //       deleted from their previous one can make their records stay deleted\n    if (cellsInitialized.size() > 0)\n    {\n        for (auto& cell : cellsInitialized)\n        {\n            mwmp::Cell* mpCell = cell.second;\n            if (mpCell->shouldInitializeActors == true)\n            {\n                mpCell->shouldInitializeActors = false;\n                mpCell->initializeLocalActors();\n            }\n        }\n    }\n    // Otherwise, disable the DetourNavigator for advanced pathfinding for the time being\n    else\n    {\n        world->getNavigator()->setUpdatesEnabled(false);\n    }\n}\n\nvoid CellController::updateDedicated(float dt)\n{\n    for (const auto &cell : cellsInitialized)\n        cell.second->updateDedicated(dt);\n}\n\nvoid CellController::initializeCell(const ESM::Cell& cell)\n{\n    std::string mapIndex = cell.getShortDescription();\n\n    // If this key doesn't exist, create it\n    if (cellsInitialized.count(mapIndex) == 0)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Initializing mwmp::Cell %s\", cell.getShortDescription().c_str());\n\n        MWWorld::CellStore *cellStore = getCellStore(cell);\n\n        if (!cellStore) return;\n\n        mwmp::Cell *mpCell = new mwmp::Cell(cellStore);\n        cellsInitialized[mapIndex] = mpCell;\n\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Successfully initialized mwmp::Cell %s\", cell.getShortDescription().c_str());\n    }\n}\n\nvoid CellController::uninitializeCell(const ESM::Cell& cell)\n{\n    std::string mapIndex = cell.getShortDescription();\n\n    // If this key exists, erase the key-value pair from the map\n    if (cellsInitialized.count(mapIndex) > 0)\n    {\n        mwmp::Cell* mpCell = cellsInitialized.at(mapIndex);\n        mpCell->uninitializeLocalActors();\n        mpCell->uninitializeDedicatedActors();\n        delete cellsInitialized.at(mapIndex);\n        cellsInitialized.erase(mapIndex);\n    }\n}\n\nvoid CellController::uninitializeCells()\n{\n    if (cellsInitialized.size() > 0)\n    {\n        for (auto it = cellsInitialized.cbegin(); it != cellsInitialized.cend(); it++)\n        {\n            mwmp::Cell* mpCell = it->second;\n            mpCell->uninitializeLocalActors();\n            mpCell->uninitializeDedicatedActors();\n            delete it->second;\n        }\n\n        cellsInitialized.clear();\n    }\n}\n\nvoid CellController::readPositions(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readPositions(actorList);\n}\n\nvoid CellController::readAnimFlags(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readAnimFlags(actorList);\n}\n\nvoid CellController::readAnimPlay(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readAnimPlay(actorList);\n}\n\nvoid CellController::readStatsDynamic(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readStatsDynamic(actorList);\n}\n\nvoid CellController::readDeath(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readDeath(actorList);\n}\n\nvoid CellController::readEquipment(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readEquipment(actorList);\n}\n\nvoid CellController::readSpeech(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readSpeech(actorList);\n}\n\nvoid CellController::readSpellsActive(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readSpellsActive(actorList);\n}\n\nvoid CellController::readAi(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readAi(actorList);\n}\n\nvoid CellController::readAttack(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readAttack(actorList);\n}\n\nvoid CellController::readCast(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readCast(actorList);\n}\n\nvoid CellController::readCellChange(ActorList& actorList)\n{\n    std::string mapIndex = actorList.cell.getShortDescription();\n\n    initializeCell(actorList.cell);\n\n    // If this now exists, send it the data\n    if (cellsInitialized.count(mapIndex) > 0)\n        cellsInitialized[mapIndex]->readCellChange(actorList);\n}\n\nbool CellController::hasQueuedDeathState(MWWorld::Ptr ptr)\n{\n    std::string actorIndex = generateMapIndex(ptr);\n\n    return queuedDeathStates.count(actorIndex) > 0;\n}\n\nunsigned int CellController::getQueuedDeathState(MWWorld::Ptr ptr)\n{\n    std::string actorIndex = generateMapIndex(ptr);\n\n    return queuedDeathStates[actorIndex];\n}\n\nvoid CellController::clearQueuedDeathState(MWWorld::Ptr ptr)\n{\n    std::string actorIndex = generateMapIndex(ptr);\n\n    queuedDeathStates.erase(actorIndex);\n}\n\nvoid CellController::setQueuedDeathState(MWWorld::Ptr ptr, unsigned int deathState)\n{\n    std::string actorIndex = generateMapIndex(ptr);\n\n    queuedDeathStates[actorIndex] = deathState;\n}\n\nvoid CellController::setLocalActorRecord(std::string actorIndex, std::string cellIndex)\n{\n    localActorsToCells[actorIndex] = cellIndex;\n}\n\nvoid CellController::removeLocalActorRecord(std::string actorIndex)\n{\n    localActorsToCells.erase(actorIndex);\n}\n\nbool CellController::isLocalActor(MWWorld::Ptr ptr)\n{\n    if (ptr.mRef == nullptr)\n        return false;\n\n    std::string actorIndex = generateMapIndex(ptr);\n\n    return localActorsToCells.count(actorIndex) > 0;\n}\n\nbool CellController::isLocalActor(int refNum, int mpNum)\n{\n    std::string actorIndex = generateMapIndex(refNum, mpNum);\n\n    return localActorsToCells.count(actorIndex) > 0;\n}\n\nLocalActor *CellController::getLocalActor(MWWorld::Ptr ptr)\n{\n    std::string actorIndex = generateMapIndex(ptr);\n    std::string cellIndex = localActorsToCells.at(actorIndex);\n\n    return cellsInitialized.at(cellIndex)->getLocalActor(actorIndex);\n}\n\nLocalActor *CellController::getLocalActor(int refNum, int mpNum)\n{\n    std::string actorIndex = generateMapIndex(refNum, mpNum);\n    std::string cellIndex = localActorsToCells.at(actorIndex);\n\n    return cellsInitialized.at(cellIndex)->getLocalActor(actorIndex);\n}\n\nvoid CellController::setDedicatedActorRecord(std::string actorIndex, std::string cellIndex)\n{\n    dedicatedActorsToCells[actorIndex] = cellIndex;\n}\n\nvoid CellController::removeDedicatedActorRecord(std::string actorIndex)\n{\n    dedicatedActorsToCells.erase(actorIndex);\n}\n\nbool CellController::isDedicatedActor(MWWorld::Ptr ptr)\n{\n    if (ptr.mRef == nullptr)\n        return false;\n\n    std::string actorIndex = generateMapIndex(ptr);\n\n    return dedicatedActorsToCells.count(actorIndex) > 0;\n}\n\nbool CellController::isDedicatedActor(int refNum, int mpNum)\n{\n    std::string actorIndex = generateMapIndex(refNum, mpNum);\n\n    return dedicatedActorsToCells.count(actorIndex) > 0;\n}\n\nDedicatedActor *CellController::getDedicatedActor(MWWorld::Ptr ptr)\n{\n    std::string actorIndex = generateMapIndex(ptr);\n    std::string cellIndex = dedicatedActorsToCells.at(actorIndex);\n\n    return cellsInitialized.at(cellIndex)->getDedicatedActor(actorIndex);\n}\n\nDedicatedActor *CellController::getDedicatedActor(int refNum, int mpNum)\n{\n    std::string actorIndex = generateMapIndex(refNum, mpNum);\n    std::string cellIndex = dedicatedActorsToCells.at(actorIndex);\n\n    return cellsInitialized.at(cellIndex)->getDedicatedActor(actorIndex);\n}\n\nstd::string CellController::generateMapIndex(int refNum, int mpNum)\n{\n    std::string mapIndex = \"\";\n    mapIndex = Utils::toString(refNum) + \"-\" + Utils::toString(mpNum);\n    return mapIndex;\n}\n\nstd::string CellController::generateMapIndex(MWWorld::Ptr ptr)\n{\n    return generateMapIndex(ptr.getCellRef().getRefNum().mIndex, ptr.getCellRef().getMpNum());\n}\n\nstd::string CellController::generateMapIndex(BaseActor baseActor)\n{\n    return generateMapIndex(baseActor.refNum, baseActor.mpNum);\n}\n\nbool CellController::hasLocalAuthority(const ESM::Cell& cell)\n{\n    if (isInitializedCell(cell) && isActiveWorldCell(cell))\n        return getCell(cell)->hasLocalAuthority();\n\n    return false;\n}\n\nbool CellController::isInitializedCell(const std::string& cellDescription)\n{\n    return (cellsInitialized.count(cellDescription) > 0);\n}\n\nbool CellController::isInitializedCell(const ESM::Cell& cell)\n{\n    return isInitializedCell(cell.getShortDescription());\n}\n\nbool CellController::isActiveWorldCell(const ESM::Cell& cell)\n{\n    return MWBase::Environment::get().getWorld()->isCellActive(cell);\n}\n\nCell *CellController::getCell(const ESM::Cell& cell)\n{\n    return cellsInitialized.at(cell.getShortDescription());\n}\n\nMWWorld::CellStore *CellController::getCellStore(const ESM::Cell& cell)\n{\n    MWWorld::CellStore *cellStore;\n\n    if (cell.isExterior())\n        cellStore = MWBase::Environment::get().getWorld()->getExterior(cell.mData.mX, cell.mData.mY);\n    else\n    {\n        try\n        {\n            cellStore = MWBase::Environment::get().getWorld()->getInterior(cell.mName);\n        }\n        catch (std::exception&)\n        {\n            cellStore = nullptr;\n        }\n    }\n\n    return cellStore;\n}\n\nbool CellController::isSameCell(const ESM::Cell& cell, const ESM::Cell& otherCell)\n{\n    if (&cell == nullptr || &otherCell == nullptr) return false;\n\n    bool isCellExterior = false;\n    bool isOtherCellExterior = false;\n\n    try\n    {\n        isCellExterior = cell.isExterior();\n        isOtherCellExterior = otherCell.isExterior();\n    }\n    catch (std::exception& e)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Failed cell comparison\");\n        return false;\n    }\n\n    if (isCellExterior && isOtherCellExterior)\n    {\n        if (cell.mData.mX == otherCell.mData.mX && cell.mData.mY == otherCell.mData.mY)\n            return true;\n    }\n    else if (Misc::StringUtils::ciEqual(cell.mName, otherCell.mName))\n        return true;\n\n    return false;\n}\n\nint CellController::getCellSize() const\n{\n    return 8192;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/CellController.hpp",
    "content": "#ifndef OPENMW_CELLCONTROLLER_HPP\n#define OPENMW_CELLCONTROLLER_HPP\n\n#include \"Cell.hpp\"\n#include \"ActorList.hpp\"\n#include \"LocalActor.hpp\"\n#include \"DedicatedActor.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\nnamespace mwmp\n{\n    class CellController\n    {\n    public:\n\n        CellController();\n        virtual ~CellController();\n\n        void updateLocal(bool forceUpdate);\n        void updateDedicated(float dt);\n\n        void initializeCell(const ESM::Cell& cell);\n        void uninitializeCell(const ESM::Cell& cell);\n        void uninitializeCells();\n\n        void readPositions(mwmp::ActorList& actorList);\n        void readAnimFlags(mwmp::ActorList& actorList);\n        void readAnimPlay(mwmp::ActorList& actorList);\n        void readStatsDynamic(mwmp::ActorList& actorList);\n        void readDeath(mwmp::ActorList& actorList);\n        void readEquipment(mwmp::ActorList& actorList);\n        void readSpeech(mwmp::ActorList& actorList);\n        void readSpellsActive(mwmp::ActorList& actorList);\n        void readAi(mwmp::ActorList& actorList);\n        void readAttack(mwmp::ActorList& actorList);\n        void readCast(mwmp::ActorList& actorList);\n        void readCellChange(mwmp::ActorList& actorList);\n\n        bool hasQueuedDeathState(MWWorld::Ptr ptr);\n        unsigned int getQueuedDeathState(MWWorld::Ptr ptr);\n        void clearQueuedDeathState(MWWorld::Ptr ptr);\n        void setQueuedDeathState(MWWorld::Ptr ptr, unsigned int deathState);\n\n        void setLocalActorRecord(std::string actorIndex, std::string cellIndex);\n        void removeLocalActorRecord(std::string actorIndex);\n        \n        bool isLocalActor(MWWorld::Ptr ptr);\n        bool isLocalActor(int refNum, int mpNum);\n        virtual LocalActor *getLocalActor(MWWorld::Ptr ptr);\n        virtual LocalActor *getLocalActor(int refNum, int mpNum);\n\n        void setDedicatedActorRecord(std::string actorIndex, std::string cellIndex);\n        void removeDedicatedActorRecord(std::string actorIndex);\n        \n        bool isDedicatedActor(MWWorld::Ptr ptr);\n        bool isDedicatedActor(int refNum, int mpNum);\n        virtual DedicatedActor *getDedicatedActor(MWWorld::Ptr ptr);\n        virtual DedicatedActor *getDedicatedActor(int refNum, int mpNum);\n\n        std::string generateMapIndex(int refNumindex, int mpNum);\n        std::string generateMapIndex(MWWorld::Ptr ptr);\n        std::string generateMapIndex(mwmp::BaseActor baseActor);\n\n        bool hasLocalAuthority(const ESM::Cell& cell);\n        bool isInitializedCell(const std::string& cellDescription);\n        bool isInitializedCell(const ESM::Cell& cell);\n        bool isActiveWorldCell(const ESM::Cell& cell);\n        virtual Cell *getCell(const ESM::Cell& cell);\n\n        virtual MWWorld::CellStore *getCellStore(const ESM::Cell& cell);\n\n        bool isSameCell(const ESM::Cell& cell, const ESM::Cell& otherCell);\n\n        int getCellSize() const;\n\n    private:\n        static std::map<std::string, mwmp::Cell *> cellsInitialized;\n        static std::map<std::string, std::string> localActorsToCells;\n        static std::map<std::string, std::string> dedicatedActorsToCells;\n        static std::map<std::string, unsigned int> queuedDeathStates;\n    };\n}\n\n#endif //OPENMW_CELLCONTROLLER_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/DedicatedActor.cpp",
    "content": "#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwdialogue/dialoguemanagerimp.hpp\"\n\n#include \"../mwmechanics/aiactivate.hpp\"\n#include \"../mwmechanics/aicombat.hpp\"\n#include \"../mwmechanics/aiescort.hpp\"\n#include \"../mwmechanics/aifollow.hpp\"\n#include \"../mwmechanics/aitravel.hpp\"\n#include \"../mwmechanics/aiwander.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/mechanicsmanagerimp.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"../mwworld/action.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"DedicatedActor.hpp\"\n#include \"Main.hpp\"\n#include \"CellController.hpp\"\n#include \"MechanicsHelper.hpp\"\n\nusing namespace mwmp;\n\nDedicatedActor::DedicatedActor()\n{\n    drawState = MWMechanics::DrawState_::DrawState_Nothing;\n    movementFlags = 0;\n    animation.groupname = \"\";\n    sound = \"\";\n\n    hasPositionData = false;\n    hasStatsDynamicData = false;\n    hasReceivedInitialEquipment = false;\n    hasChangedCell = true;\n\n    attack.pressed = false;\n    cast.pressed = false;\n}\n\nDedicatedActor::~DedicatedActor()\n{\n\n}\n\nvoid DedicatedActor::update(float dt)\n{\n    // Only move and set anim flags if the framerate isn't too low\n    if (dt < 0.1)\n    {\n        move(dt);\n        setAnimFlags();\n    }\n\n    setStatsDynamic();\n}\n\nvoid DedicatedActor::setCell(MWWorld::CellStore *cellStore)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    ptr = world->moveObject(ptr, cellStore, position.pos[0], position.pos[1], position.pos[2]);\n    setMovementSettings();\n\n    hasChangedCell = true;\n}\n\nvoid DedicatedActor::move(float dt)\n{\n    ESM::Position refPos = ptr.getRefData().getPosition();\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    const int maxInterpolationDistance = 40;\n\n    // Apply interpolation only if the position hasn't changed too much from last time\n    bool shouldInterpolate = abs(position.pos[0] - refPos.pos[0]) < maxInterpolationDistance && abs(position.pos[1] - refPos.pos[1]) < maxInterpolationDistance && abs(position.pos[2] - refPos.pos[2]) < maxInterpolationDistance;\n\n    // Don't apply linear interpolation if the DedicatedActor has just gone through a cell change, because\n    // the interpolated position will be invalid, causing a slight hopping glitch\n    if (shouldInterpolate && !hasChangedCell)\n    {\n        static const int timeMultiplier = 15;\n        osg::Vec3f lerp = MechanicsHelper::getLinearInterpolation(refPos.asVec3(), position.asVec3(), dt * timeMultiplier);\n        refPos.pos[0] = lerp.x();\n        refPos.pos[1] = lerp.y();\n        refPos.pos[2] = lerp.z();\n\n        world->moveObject(ptr, refPos.pos[0], refPos.pos[1], refPos.pos[2]);\n    }\n    else\n    {\n        setPosition();\n        hasChangedCell = false;\n    }\n\n    setMovementSettings();\n    world->rotateObject(ptr, position.rot[0], position.rot[1], position.rot[2]);\n}\n\nvoid DedicatedActor::setMovementSettings()\n{\n    MWMechanics::Movement *move = &ptr.getClass().getMovementSettings(ptr);\n    move->mPosition[0] = direction.pos[0];\n    move->mPosition[1] = direction.pos[1];\n    move->mPosition[2] = direction.pos[2];\n\n    // Make sure the values are valid, or we'll get an infinite error loop\n    if (!isnan(direction.rot[0]) && !isnan(direction.rot[1]) && !isnan(direction.rot[2]))\n    {\n        move->mRotation[0] = direction.rot[0];\n        move->mRotation[1] = direction.rot[1];\n        move->mRotation[2] = direction.rot[2];\n    }\n}\n\nvoid DedicatedActor::setPosition()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    world->moveObject(ptr, position.pos[0], position.pos[1], position.pos[2]);\n}\n\nvoid DedicatedActor::setAnimFlags()\n{\n    using namespace MWMechanics;\n\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);\n\n    ptrCreatureStats->setDrawState(static_cast<MWMechanics::DrawState_>(drawState));\n\n    ptrCreatureStats->setMovementFlag(CreatureStats::Flag_Run, (movementFlags & CreatureStats::Flag_Run) != 0);\n    ptrCreatureStats->setMovementFlag(CreatureStats::Flag_Sneak, (movementFlags & CreatureStats::Flag_Sneak) != 0);\n    ptrCreatureStats->setMovementFlag(CreatureStats::Flag_ForceJump, (movementFlags & CreatureStats::Flag_ForceJump) != 0);\n    ptrCreatureStats->setMovementFlag(CreatureStats::Flag_ForceMoveJump, (movementFlags & CreatureStats::Flag_ForceMoveJump) != 0);\n}\n\nvoid DedicatedActor::setStatsDynamic()\n{\n    // Only set dynamic stats if we have received at least one packet about them\n    if (!hasStatsDynamicData) return;\n\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);\n    MWMechanics::DynamicStat<float> value;\n\n    // Resurrect this Actor if it's not supposed to be dead according to its authority\n    if (creatureStats.mDynamic[0].mCurrent > 0)\n        MWBase::Environment::get().getMechanicsManager()->resurrect(ptr);\n\n    for (int i = 0; i < 3; ++i)\n    {\n        value.readState(creatureStats.mDynamic[i]);\n        ptrCreatureStats->setDynamic(i, value);\n    }\n}\n\nvoid DedicatedActor::setEquipment()\n{\n    if (!ptr.getClass().hasInventoryStore(ptr))\n        return;\n\n    MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr);\n\n    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)\n    {\n        int count = equipmentItems[slot].count;\n\n        // If we've somehow received a corrupted item with a count lower than 0, ignore it\n        if (count < 0) continue;\n\n        MWWorld::ContainerStoreIterator it = invStore.getSlot(slot);\n\n        const std::string &packetRefId = equipmentItems[slot].refId;\n        int packetCharge = equipmentItems[slot].charge;\n        std::string storeRefId = \"\";\n        bool equal = false;\n\n        if (it != invStore.end())\n        {\n            storeRefId = it->getCellRef().getRefId();\n\n            if (!Misc::StringUtils::ciEqual(storeRefId, packetRefId)) // if other item equiped\n                invStore.unequipSlot(slot, ptr);\n            else\n                equal = true;\n        }\n\n        if (packetRefId.empty() || equal)\n            continue;\n\n        if (!hasItem(packetRefId, packetCharge))\n        {\n            ptr.getClass().getContainerStore(ptr).add(packetRefId, count, ptr);\n        }\n\n        // Equip items silently if this is the first time equipment is being set for this character\n        equipItem(packetRefId, packetCharge, !hasReceivedInitialEquipment);\n    }\n\n    hasReceivedInitialEquipment = true;\n}\n\nvoid DedicatedActor::setAi()\n{\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);\n    ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Fight, 0);\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- actor cellRef: %s %i-%i\",\n        ptr.getCellRef().getRefId().c_str(), ptr.getCellRef().getRefNum().mIndex, ptr.getCellRef().getMpNum());\n\n    if (aiAction == mwmp::BaseActorList::CANCEL)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Cancelling AI sequence\");\n\n        ptrCreatureStats->getAiSequence().clear();\n    }\n    else if (aiAction == mwmp::BaseActorList::TRAVEL)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Travelling to %f, %f, %f\",\n            aiCoordinates.pos[0], aiCoordinates.pos[1], aiCoordinates.pos[2]);\n\n        MWMechanics::AiTravel package(aiCoordinates.pos[0], aiCoordinates.pos[1], aiCoordinates.pos[2]);\n        ptrCreatureStats->getAiSequence().stack(package, ptr, true);\n    }\n    else if (aiAction == mwmp::BaseActorList::WANDER)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Wandering for distance %i and duration %i, repetition is %s\",\n            aiDistance, aiDuration, aiShouldRepeat ? \"true\" : \"false\");\n\n        std::vector<unsigned char> idleList;\n\n        MWMechanics::AiWander package(aiDistance, aiDuration, -1, idleList, aiShouldRepeat);\n        ptrCreatureStats->getAiSequence().stack(package, ptr, true);\n    }\n    else if (hasAiTarget)\n    {\n        MWWorld::Ptr targetPtr;\n\n        if (aiTarget.isPlayer)\n        {\n            targetPtr = MechanicsHelper::getPlayerPtr(aiTarget);\n\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Has player target %s\",\n                targetPtr.getClass().getName(targetPtr).c_str());\n        }\n        else\n        {\n            if (mwmp::Main::get().getCellController()->isLocalActor(aiTarget.refNum, aiTarget.mpNum))\n                targetPtr = mwmp::Main::get().getCellController()->getLocalActor(aiTarget.refNum, aiTarget.mpNum)->getPtr();\n            else if (mwmp::Main::get().getCellController()->isDedicatedActor(aiTarget.refNum, aiTarget.mpNum))\n                targetPtr = mwmp::Main::get().getCellController()->getDedicatedActor(aiTarget.refNum, aiTarget.mpNum)->getPtr();\n            else if (aiAction == mwmp::BaseActorList::ACTIVATE)\n                targetPtr = MWBase::Environment::get().getWorld()->searchPtrViaUniqueIndex(aiTarget.refNum, aiTarget.mpNum);\n\n            if (targetPtr)\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Has actor target %s %i-%i\",\n                    targetPtr.getCellRef().getRefId().c_str(), aiTarget.refNum, aiTarget.mpNum);\n            }\n            else\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Has invalid actor target %i-%i\",\n                    aiTarget.refNum, aiTarget.mpNum);\n            }\n\n        }\n\n        if (targetPtr)\n        {\n            if (aiAction == mwmp::BaseActorList::ACTIVATE)\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Activating target\");\n\n                MWMechanics::AiActivate package(targetPtr);\n                ptrCreatureStats->getAiSequence().stack(package, ptr, true);\n            }\n\n            if (aiAction == mwmp::BaseActorList::COMBAT)\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Starting combat with target\");\n\n                MWMechanics::AiCombat package(targetPtr);\n                ptrCreatureStats->getAiSequence().stack(package, ptr, true);\n            }\n            else if (aiAction == mwmp::BaseActorList::ESCORT)\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Being escorted by target, for duration %i, to coordinates %f, %f, %f\",\n                    aiDuration, aiCoordinates.pos[0], aiCoordinates.pos[1], aiCoordinates.pos[2]);\n\n                MWMechanics::AiEscort package(targetPtr.getCellRef().getRefId(), aiDuration,\n                    aiCoordinates.pos[0], aiCoordinates.pos[1], aiCoordinates.pos[2]);\n                ptrCreatureStats->getAiSequence().stack(package, ptr, true);\n            }\n            else if (aiAction == mwmp::BaseActorList::FOLLOW)\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Following target\");\n\n                MWMechanics::AiFollow package(targetPtr);\n                package.allowAnyDistance(true);\n                ptrCreatureStats->getAiSequence().stack(package, ptr, true);\n            }\n        }\n    }\n}\n\nvoid DedicatedActor::playAnimation()\n{\n    if (!animation.groupname.empty())\n    {\n        MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(ptr,\n            animation.groupname, animation.mode, animation.count, animation.persist);\n\n        animation.groupname.clear();\n    }\n}\n\nvoid DedicatedActor::playSound()\n{\n    if (!sound.empty())\n    {\n        MWBase::Environment::get().getSoundManager()->say(ptr, sound);\n\n        MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n        if (winMgr->getSubtitlesEnabled())\n            winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never);\n\n        sound.clear();\n    }\n}\n\nbool DedicatedActor::hasItem(std::string itemId, int charge)\n{\n    for (const auto &itemPtr : ptr.getClass().getInventoryStore(ptr))\n    {\n        if (::Misc::StringUtils::ciEqual(itemPtr.getCellRef().getRefId(), itemId) && itemPtr.getCellRef().getCharge() == charge)\n            return true;\n    }\n\n    return false;\n}\n\nvoid DedicatedActor::equipItem(std::string itemId, int charge, bool noSound)\n{\n    for (const auto &itemPtr : ptr.getClass().getInventoryStore(ptr))\n    {\n        if (::Misc::StringUtils::ciEqual(itemPtr.getCellRef().getRefId(), itemId) && itemPtr.getCellRef().getCharge() == charge)\n        {\n            std::shared_ptr<MWWorld::Action> action = itemPtr.getClass().use(itemPtr);\n            action->execute(ptr, noSound);\n            break;\n        }\n    }\n}\n\nvoid DedicatedActor::addSpellsActive()\n{\n    MWMechanics::ActiveSpells& activeSpells = getPtr().getClass().getCreatureStats(getPtr()).getActiveSpells();\n\n    for (const auto& activeSpell : spellsActiveChanges.activeSpells)\n    {\n        MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay);\n        int casterActorId = MechanicsHelper::getActorId(activeSpell.caster);\n\n        MechanicsHelper::createSpellGfx(getPtr(), activeSpell.params.mEffects);\n\n        // Don't do a check for a spell's existence, because active effects from potions need to be applied here too\n        activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, casterActorId, timestamp, false);\n    }\n}\n\nvoid DedicatedActor::removeSpellsActive()\n{\n    MWMechanics::ActiveSpells& activeSpells = getPtr().getClass().getCreatureStats(getPtr()).getActiveSpells();\n\n    for (const auto& activeSpell : spellsActiveChanges.activeSpells)\n    {\n        // Remove stacking spells based on their timestamps\n        if (activeSpell.isStackingSpell)\n        {\n            MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay);\n            activeSpells.removeSpellByTimestamp(activeSpell.id, timestamp);\n        }\n        else\n        {\n            activeSpells.removeEffects(activeSpell.id);\n        }\n    }\n}\n\nvoid DedicatedActor::setSpellsActive()\n{\n    MWMechanics::ActiveSpells& activeSpells = getPtr().getClass().getCreatureStats(getPtr()).getActiveSpells();\n    activeSpells.clear();\n\n    // Proceed by adding spells active\n    addSpellsActive();\n}\n\nMWWorld::Ptr DedicatedActor::getPtr()\n{\n    return ptr;\n}\n\nvoid DedicatedActor::setPtr(const MWWorld::Ptr& newPtr)\n{\n    ptr = newPtr;\n\n    refId = ptr.getCellRef().getRefId();\n    refNum = ptr.getCellRef().getRefNum().mIndex;\n    mpNum = ptr.getCellRef().getMpNum();\n\n    position = ptr.getRefData().getPosition();\n    drawState = ptr.getClass().getCreatureStats(ptr).getDrawState();\n}\n\nvoid DedicatedActor::reloadPtr()\n{\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n    world->disable(ptr);\n    world->enable(ptr);\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/DedicatedActor.hpp",
    "content": "#ifndef OPENMW_DEDICATEDACTOR_HPP\n#define OPENMW_DEDICATEDACTOR_HPP\n\n#include <components/openmw-mp/Base/BaseActor.hpp>\n#include \"../mwmechanics/aisequence.hpp\"\n#include \"../mwworld/manualref.hpp\"\n\nnamespace mwmp\n{\n    class DedicatedActor : public BaseActor\n    {\n    public:\n\n        DedicatedActor();\n        virtual ~DedicatedActor();\n\n        void update(float dt);\n        void move(float dt);\n        void setCell(MWWorld::CellStore *cellStore);\n        void setMovementSettings();\n        void setPosition();\n        void setAnimFlags();\n        void setStatsDynamic();\n        void setEquipment();\n        void setAi();\n        void playAnimation();\n        void playSound();\n\n        bool hasItem(std::string itemId, int charge);\n        void equipItem(std::string itemId, int charge, bool noSound = false);\n\n        void addSpellsActive();\n        void removeSpellsActive();\n        void setSpellsActive();\n\n        MWWorld::Ptr getPtr();\n        void setPtr(const MWWorld::Ptr& newPtr);\n        void reloadPtr();\n\n    private:\n        MWWorld::Ptr ptr;\n\n        bool hasReceivedInitialEquipment;\n        bool hasChangedCell;\n    };\n}\n\n#endif //OPENMW_DEDICATEDACTOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/DedicatedPlayer.cpp",
    "content": "#include <boost/algorithm/clamp.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <apps/openmw/mwmechanics/steering.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwclass/npc.hpp\"\n\n#include \"../mwdialogue/dialoguemanagerimp.hpp\"\n\n#include \"../mwgui/windowmanagerimp.hpp\"\n\n#include \"../mwinput/inputmanagerimp.hpp\"\n\n#include \"../mwmechanics/actor.hpp\"\n#include \"../mwmechanics/aitravel.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/mechanicsmanagerimp.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n\n#include \"../mwstate/statemanagerimp.hpp\"\n\n#include \"../mwworld/action.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/customdata.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"DedicatedPlayer.hpp\"\n#include \"Main.hpp\"\n#include \"GUIController.hpp\"\n#include \"CellController.hpp\"\n#include \"MechanicsHelper.hpp\"\n#include \"RecordHelper.hpp\"\n\n\nusing namespace mwmp;\n\nDedicatedPlayer::DedicatedPlayer(RakNet::RakNetGUID guid) : BasePlayer(guid)\n{\n    reference = 0;\n    attack.pressed = false;\n    cast.pressed = false;\n\n    creatureStats.mDead = false;\n    // Give this new character a temporary high fatigue so it doesn't spawn on\n    // the ground\n    creatureStats.mDynamic[2].mBase = 1000;\n\n    attack.instant = false;\n\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n    \n    cell = *world->getInterior(RecordHelper::getPlaceholderInteriorCellName())->getCell();\n    position.pos[0] = position.pos[1] = position.pos[2] = 0;\n\n    npc = *world->getPlayerPtr().get<ESM::NPC>()->mBase;\n    npc.mId = \"\";\n    previousRace = npc.mRace;\n\n    hasReceivedInitialEquipment = false;\n    hasFinishedInitialTeleportation = false;\n\n    isJumping = false;\n    wasJumping = false;\n}\n\nDedicatedPlayer::~DedicatedPlayer()\n{\n\n}\n\nvoid DedicatedPlayer::update(float dt)\n{\n    // Only move and set anim flags if the framerate isn't too low\n    if (dt < 0.1)\n    {\n        move(dt);\n        setAnimFlags();\n    }\n\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);\n\n    MWMechanics::DynamicStat<float> value;\n\n    if (creatureStats.mDead)\n    {\n        value.readState(creatureStats.mDynamic[0]);\n        ptrCreatureStats->setHealth(value);\n        return;\n    }\n\n    for (int i = 0; i < 3; ++i)\n    {\n        value.readState(creatureStats.mDynamic[i]);\n        ptrCreatureStats->setDynamic(i, value);\n    }\n\n    if (ptrCreatureStats->isDead())\n        MWBase::Environment::get().getMechanicsManager()->resurrect(ptr);\n\n    ptrCreatureStats->setAttacked(false);\n\n    ptrCreatureStats->getAiSequence().stopCombat();\n\n    ptrCreatureStats->setAlarmed(false);\n    ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Alarm, 0);\n    ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Fight, 0);\n    ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Flee, 0);\n    ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Hello, 0);\n}\n\nvoid DedicatedPlayer::move(float dt)\n{\n    if (!reference) return;\n\n    ESM::Position refPos = ptr.getRefData().getPosition();\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    const int maxInterpolationDistance = 80;\n\n    // Apply interpolation only if the position hasn't changed too much from last time\n    bool shouldInterpolate =\n            abs(position.pos[0] - refPos.pos[0]) < maxInterpolationDistance &&\n            abs(position.pos[1] - refPos.pos[1]) < maxInterpolationDistance &&\n            abs(position.pos[2] - refPos.pos[2]) < maxInterpolationDistance;\n\n    if (shouldInterpolate)\n    {\n        static const int timeMultiplier = 15;\n        osg::Vec3f lerp = MechanicsHelper::getLinearInterpolation(refPos.asVec3(), position.asVec3(), dt * timeMultiplier);\n\n        world->moveObject(ptr, lerp.x(), lerp.y(), lerp.z());\n    }\n    else\n        world->moveObject(ptr, position.pos[0], position.pos[1], position.pos[2]);\n\n    world->rotateObject(ptr, position.rot[0], 0, position.rot[2]);\n\n    MWMechanics::Movement *move = &ptr.getClass().getMovementSettings(ptr);\n    move->mPosition[0] = direction.pos[0];\n    move->mPosition[1] = direction.pos[1];\n    move->mPosition[2] = direction.pos[2];\n\n    // Make sure the values are valid, or we'll get an infinite error loop\n    if (!isnan(direction.rot[0]) && !isnan(direction.rot[1]) && !isnan(direction.rot[2]))\n    {\n        move->mRotation[0] = direction.rot[0];\n        move->mRotation[1] = direction.rot[1];\n        move->mRotation[2] = direction.rot[2];\n    }\n}\n\nvoid DedicatedPlayer::setBaseInfo()\n{\n    // Use the previous race if the new one doesn't exist\n    if (!RecordHelper::doesRecordIdExist<ESM::Race>(npc.mRace))\n        npc.mRace = previousRace;\n\n    if (!reference)\n    {\n        npc.mId = RecordHelper::createRecord(npc)->mId;\n        createReference(npc.mId);\n    }\n    else\n    {\n        RecordHelper::overrideRecord(npc);\n        reloadPtr();\n    }\n\n    // Only set equipment if the player isn't disguised as a creature\n    if (ptr.getTypeName() == typeid(ESM::NPC).name())\n        setEquipment();\n\n    previousRace = npc.mRace;\n}\n\nvoid DedicatedPlayer::setStatsDynamic()\n{\n    MWMechanics::CreatureStats* ptrCreatureStats = &getPtr().getClass().getCreatureStats(getPtr());\n    MWMechanics::DynamicStat<float> value;\n\n    for (int i = 0; i < 3; ++i)\n    {\n        value.readState(creatureStats.mDynamic[i]);\n        ptrCreatureStats->setDynamic(i, value);\n    }\n}\n\nvoid DedicatedPlayer::setAnimFlags()\n{\n    using namespace MWMechanics;\n\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    // Until we figure out a better workaround for disabling player gravity,\n    // simply cast Levitate over and over on a player that's supposed to be flying\n    if (!isFlying && !hasTcl && !isLevitationPurged)\n    {\n        ptr.getClass().getCreatureStats(ptr).getActiveSpells().purgeEffect(ESM::MagicEffect::Levitate);\n        isLevitationPurged = true;\n    }\n    else if ((isFlying || hasTcl) && !world->isFlying(ptr))\n    {\n        MWMechanics::CastSpell levitationCast(ptr, ptr);\n        levitationCast.mHitPosition = ptr.getRefData().getPosition().asVec3();\n        levitationCast.mAlwaysSucceed = true;\n        levitationCast.cast(\"Levitate\");\n        isLevitationPurged = false;\n    }\n\n    if (isJumping && !wasJumping)\n    {\n        world->setOnGround(ptr, false);\n        wasJumping = true;\n    }\n    else if (wasJumping && !isJumping)\n    {\n        world->setOnGround(ptr, true);\n        wasJumping = false;\n    }\n\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);\n\n    ptrCreatureStats->setDrawState(static_cast<MWMechanics::DrawState_>(drawState));\n\n    ptrCreatureStats->setMovementFlag(CreatureStats::Flag_Run, (movementFlags & CreatureStats::Flag_Run) != 0);\n    ptrCreatureStats->setMovementFlag(CreatureStats::Flag_Sneak, (movementFlags & CreatureStats::Flag_Sneak) != 0);\n    ptrCreatureStats->setMovementFlag(CreatureStats::Flag_ForceJump, (movementFlags & CreatureStats::Flag_ForceJump) != 0);\n    ptrCreatureStats->setMovementFlag(CreatureStats::Flag_ForceMoveJump, (movementFlags & CreatureStats::Flag_ForceMoveJump) != 0);\n}\n\nvoid DedicatedPlayer::setAttributes()\n{\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);\n    MWMechanics::AttributeValue attributeValue;\n\n    for (int i = 0; i < 8; ++i)\n    {\n        attributeValue.readState(creatureStats.mAttributes[i]);\n        ptrCreatureStats->setAttribute(i, attributeValue);\n    }\n}\n\nvoid DedicatedPlayer::setSkills()\n{\n    // Go no further if the player is disguised as a creature\n    if (ptr.getTypeName() != typeid(ESM::NPC).name()) return;\n\n    MWMechanics::NpcStats *ptrNpcStats = &ptr.getClass().getNpcStats(ptr);\n    MWMechanics::SkillValue skillValue;\n\n    for (int i = 0; i < 27; ++i)\n    {\n        skillValue.readState(npcStats.mSkills[i]);\n        ptrNpcStats->setSkill(i, skillValue);\n    }\n}\n\nvoid DedicatedPlayer::setEquipment()\n{\n    // Go no further if the player is disguised as a creature\n    if (!ptr.getClass().hasInventoryStore(ptr)) return;\n\n    bool equippedSomething = false;\n\n    MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr);\n    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)\n    {\n        MWWorld::ContainerStoreIterator it = invStore.getSlot(slot);\n\n        const std::string &packetRefId = equipmentItems[slot].refId;\n        std::string ptrItemId = \"\";\n        bool equal = false;\n\n        if (it != invStore.end())\n        {\n            ptrItemId = it->getCellRef().getRefId();\n\n            if (!Misc::StringUtils::ciEqual(ptrItemId, packetRefId)) // if other item is now equipped\n            {\n                MWWorld::ContainerStore &store = ptr.getClass().getContainerStore(ptr);\n\n                // Remove the items that are no longer equipped, except for throwing weapons and ranged weapon ammo that\n                // have just run out but still need to be kept briefly so they can be used in attacks about to be released\n                bool shouldRemove = true;\n\n                if (attack.type == mwmp::Attack::RANGED && packetRefId.empty() && !attack.pressed)\n                {\n                    if (slot == MWWorld::InventoryStore::Slot_CarriedRight && Misc::StringUtils::ciEqual(ptrItemId, attack.rangedWeaponId))\n                        shouldRemove = false;\n                    else if (slot == MWWorld::InventoryStore::Slot_Ammunition && Misc::StringUtils::ciEqual(ptrItemId, attack.rangedAmmoId))\n                        shouldRemove = false;\n                }\n                \n                if (shouldRemove)\n                {\n                    store.remove(ptrItemId, store.count(ptrItemId), ptr);\n                }\n            }\n            else\n                equal = true;\n        }\n\n        if (packetRefId.empty() || equal)\n            continue;\n\n        const int count = equipmentItems[slot].count;\n        ptr.getClass().getContainerStore(ptr).add(packetRefId, count, ptr);\n        // Equip items silently if this is the first time equipment is being set for this character\n        equipItem(packetRefId, !hasReceivedInitialEquipment);\n        equippedSomething = true;\n    }\n\n    // Only track the initial equipment as received if at least one item has been equipped\n    if (equippedSomething)\n        hasReceivedInitialEquipment = true;\n}\n\nvoid DedicatedPlayer::setShapeshift()\n{\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n\n    bool isNpc = false;\n\n    if (reference)\n        isNpc = ptr.getTypeName() == typeid(ESM::NPC).name();\n\n    if (creatureRefId != previousCreatureRefId || displayCreatureName != previousDisplayCreatureName)\n    {\n        if (!creatureRefId.empty() && RecordHelper::doesRecordIdExist<ESM::Creature>(creatureRefId))\n        {\n            deleteReference();\n\n            const ESM::Creature* tmpCreature = world->getStore().get<ESM::Creature>().search(creatureRefId);\n            creature = *tmpCreature;\n            creature.mScript = \"\";\n            if (!displayCreatureName)\n                creature.mName = npc.mName;\n            LOG_APPEND(TimedLog::LOG_INFO, \"- %s is disguised as %s\", npc.mName.c_str(), creatureRefId.c_str());\n\n            // Is this our first time creating a creature record id for this player? If so, keep it around\n            // and reuse it\n            if (creatureRecordId.empty())\n            {\n                creature.mId = creatureRecordId = RecordHelper::createRecord(creature)->mId;\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Creating new creature record %s\", creatureRecordId.c_str());\n            }\n            else\n            {\n                creature.mId = creatureRecordId;\n                RecordHelper::overrideRecord(creature);\n            }\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Creating reference for %s\", creature.mId.c_str());\n            createReference(creature.mId);\n        }\n        // This player was already a creature, but the new creature refId was empty or\n        // invalid, so we'll turn this player into their NPC self again as a result\n        else if (!isNpc)\n        {\n            if (reference)\n            {\n                deleteReference();\n            }\n\n            RecordHelper::overrideRecord(npc);\n            createReference(npc.mId);\n            reloadPtr();\n        }\n\n        previousCreatureRefId = creatureRefId;\n        previousDisplayCreatureName = displayCreatureName;\n    }\n\n    if (ptr.getTypeName() == typeid(ESM::NPC).name())\n    {\n        MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, isWerewolf);\n\n        if (!isWerewolf)\n            setEquipment();\n    }\n\n    MWBase::Environment::get().getWorld()->scaleObject(ptr, scale);\n}\n\nvoid DedicatedPlayer::setCell()\n{\n    // Prevent cell update when reference doesn't exist\n    if (!reference) return;\n\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Server says DedicatedPlayer %s moved to %s\",\n        npc.mName.c_str(), cell.getShortDescription().c_str());\n\n    MWWorld::CellStore *cellStore = Main::get().getCellController()->getCellStore(cell);\n\n    if (!cellStore)\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"%s\", \"- Cell doesn't exist on this client\");\n        world->disable(getPtr());\n        return;\n    }\n    else\n        world->enable(getPtr());\n\n    // Make sure the Ptr's dynamic stats and anim flags are up-to-date, so it doesn't show up\n    // knocked down or in a jump loop when it shouldn't\n    setStatsDynamic();\n    setAnimFlags();\n\n    // Allow this player's reference to move across a cell now that a manual cell\n    // update has been called\n    setPtr(world->moveObject(ptr, cellStore, position.pos[0], position.pos[1], position.pos[2]));\n\n    // Remove the marker entirely if this player has moved to an interior that is inactive for us\n    if (!cell.isExterior() && !Main::get().getCellController()->isActiveWorldCell(cell))\n        removeMarker();\n    // Otherwise, update their marker so the player shows up in the right cell on the world map\n    else\n    {\n        enableMarker();\n    }\n\n    // If this player is now in a cell that we are the local authority over, we should send them all\n    // NPC data in that cell\n    if (Main::get().getCellController()->hasLocalAuthority(cell))\n        Main::get().getCellController()->getCell(cell)->updateLocal(true);\n\n    // If this player is a new player or is now in a region that we are the weather authority over,\n    // or is a new player, we should send our latest weather data to the server\n    if (world->getWeatherCreationState())\n    {\n        if (!hasFinishedInitialTeleportation || Misc::StringUtils::ciEqual(getPtr().getCell()->getCell()->mRegion,\n            world->getPlayerPtr().getCell()->getCell()->mRegion))\n        {\n            world->sendWeather();\n        }\n    }\n\n    hasFinishedInitialTeleportation = true;\n}\n\nvoid DedicatedPlayer::playAnimation()\n{\n    MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(getPtr(),\n        animation.groupname, animation.mode, animation.count, animation.persist);\n}\n\nvoid DedicatedPlayer::playSpeech()\n{\n    MWBase::Environment::get().getSoundManager()->say(getPtr(), sound);\n\n    MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n    if (winMgr->getSubtitlesEnabled())\n        winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never);\n}\n\nvoid DedicatedPlayer::equipItem(std::string itemId, bool noSound)\n{\n    for (const auto& itemPtr : ptr.getClass().getInventoryStore(ptr))\n    {\n        if (::Misc::StringUtils::ciEqual(itemPtr.getCellRef().getRefId(), itemId))\n        {\n            std::shared_ptr<MWWorld::Action> action = itemPtr.getClass().use(itemPtr);\n            action->execute(ptr, noSound);\n            break;\n        }\n    }\n}\n\nvoid DedicatedPlayer::die()\n{\n    MWMechanics::DynamicStat<float> health;\n    creatureStats.mDead = true;\n    health.readState(creatureStats.mDynamic[0]);\n    health.setCurrent(0);\n    health.writeState(creatureStats.mDynamic[0]);\n\n    ptr.getClass().getCreatureStats(ptr).setHealth(health);\n}\n\nvoid DedicatedPlayer::resurrect()\n{\n    creatureStats.mDead = false;\n    if (creatureStats.mDynamic[0].mMod < 1)\n        creatureStats.mDynamic[0].mMod = 1;\n    creatureStats.mDynamic[0].mCurrent = creatureStats.mDynamic[0].mMod;\n\n    MWBase::Environment::get().getMechanicsManager()->resurrect(getPtr());\n\n    MWMechanics::DynamicStat<float> health;\n    health.readState(creatureStats.mDynamic[0]);\n    getPtr().getClass().getCreatureStats(getPtr()).setHealth(health);\n}\n\nvoid DedicatedPlayer::addSpellsActive()\n{\n    MWMechanics::ActiveSpells& activeSpells = getPtr().getClass().getCreatureStats(getPtr()).getActiveSpells();\n\n    for (const auto& activeSpell : spellsActiveChanges.activeSpells)\n    {\n        MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay);\n        int casterActorId = MechanicsHelper::getActorId(activeSpell.caster);\n\n        MechanicsHelper::createSpellGfx(getPtr(), activeSpell.params.mEffects);\n\n        // Don't do a check for a spell's existence, because active effects from potions need to be applied here too\n        activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, casterActorId, timestamp, false);\n    }\n}\n\nvoid DedicatedPlayer::removeSpellsActive()\n{\n    MWMechanics::ActiveSpells& activeSpells = getPtr().getClass().getCreatureStats(getPtr()).getActiveSpells();\n\n    for (const auto& activeSpell : spellsActiveChanges.activeSpells)\n    {\n        // Remove stacking spells based on their timestamps\n        if (activeSpell.isStackingSpell)\n        {\n            MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay);\n            activeSpells.removeSpellByTimestamp(activeSpell.id, timestamp);\n        }\n        else\n        {\n            activeSpells.removeEffects(activeSpell.id);\n        }\n    }\n}\n\nvoid DedicatedPlayer::setSpellsActive()\n{\n    MWMechanics::ActiveSpells& activeSpells = getPtr().getClass().getCreatureStats(getPtr()).getActiveSpells();\n    activeSpells.clear();\n\n    // Proceed by adding spells active\n    addSpellsActive();\n}\n\nvoid DedicatedPlayer::updateMarker()\n{\n    if (!markerEnabled)\n    {\n        return;\n    }\n\n    GUIController* gui = Main::get().getGUIController();\n\n    if (gui->mPlayerMarkers.contains(marker))\n    {\n        gui->mPlayerMarkers.deleteMarker(marker);\n        marker = gui->createMarker(guid);\n        gui->mPlayerMarkers.addMarker(marker);\n    }\n    else\n    {\n        gui->mPlayerMarkers.addMarker(marker, true);\n    }\n}\n\nvoid DedicatedPlayer::enableMarker()\n{\n    markerEnabled = true;\n    updateMarker();\n}\n\nvoid DedicatedPlayer::removeMarker()\n{\n    if (!markerEnabled)\n        return;\n\n    markerEnabled = false;\n    GUIController* gui = Main::get().getGUIController();\n\n    if (gui->mPlayerMarkers.contains(marker))\n    {\n        Main::get().getGUIController()->mPlayerMarkers.deleteMarker(marker);\n    }\n}\n\nvoid DedicatedPlayer::createReference(const std::string& recId)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    reference = new MWWorld::ManualRef(world->getStore(), recId, 1);\n\n    LOG_APPEND(TimedLog::LOG_INFO, \"- Creating new reference pointer for %s\", npc.mName.c_str());\n\n    ptr = world->placeObject(reference->getPtr(), Main::get().getCellController()->getCellStore(cell), position);\n\n    ESM::CustomMarker mEditingMarker = Main::get().getGUIController()->createMarker(guid);\n    marker = mEditingMarker;\n    enableMarker();\n}\n\nvoid DedicatedPlayer::deleteReference()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    LOG_APPEND(TimedLog::LOG_INFO, \"- Deleting reference\");\n    world->deleteObject(ptr);\n    delete reference;\n    reference = nullptr;\n}\n\nMWWorld::Ptr DedicatedPlayer::getPtr()\n{\n    return ptr;\n}\n\nMWWorld::ManualRef *DedicatedPlayer::getRef()\n{\n    return reference;\n}\n\nvoid DedicatedPlayer::setPtr(const MWWorld::Ptr& newPtr)\n{\n    ptr = newPtr;\n}\n\nvoid DedicatedPlayer::reloadPtr()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    world->disable(ptr);\n    world->enable(ptr);\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/DedicatedPlayer.hpp",
    "content": "#ifndef OPENMW_DEDICATEDPLAYER_HPP\n#define OPENMW_DEDICATEDPLAYER_HPP\n\n#include <components/esm/custommarkerstate.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/openmw-mp/Base/BasePlayer.hpp>\n\n#include \"../mwclass/npc.hpp\"\n\n#include \"../mwmechanics/aisequence.hpp\"\n\n#include \"../mwworld/manualref.hpp\"\n\n#include <map>\n#include <RakNetTypes.h>\n\nnamespace MWMechanics\n{\n    class Actor;\n}\n\nnamespace mwmp\n{\n    class DedicatedPlayer : public BasePlayer\n    {\n        friend class PlayerList;\n\n    public:\n\n        void update(float dt);\n\n        void move(float dt);\n        void setBaseInfo();\n        void setStatsDynamic();\n        void setAnimFlags();\n        void setAttributes();\n        void setSkills();\n        void setEquipment();\n        void setShapeshift();\n        void setCell();\n\n        void playAnimation();\n        void playSpeech();\n\n        void equipItem(std::string itemId, bool noSound = false);\n        void die();\n        void resurrect();\n\n        void addSpellsActive();\n        void removeSpellsActive();\n        void setSpellsActive();\n\n        void updateMarker();\n        void removeMarker();\n        void enableMarker();\n\n        void createReference(const std::string& recId);\n        void deleteReference();\n\n        MWWorld::Ptr getPtr();\n        MWWorld::ManualRef* getRef();\n\n        void setPtr(const MWWorld::Ptr& newPtr);\n        void reloadPtr();\n\n    private:\n\n        DedicatedPlayer(RakNet::RakNetGUID guid);\n        virtual ~DedicatedPlayer();\n\n        MWWorld::ManualRef* reference;\n\n        MWWorld::Ptr ptr;\n\n        ESM::CustomMarker marker;\n        bool markerEnabled;\n\n        std::string previousRace;\n        std::string previousCreatureRefId;\n        bool previousDisplayCreatureName;\n\n        std::string creatureRecordId;\n\n        bool hasReceivedInitialEquipment;\n        bool hasFinishedInitialTeleportation;\n        bool isLevitationPurged;\n\n        bool wasJumping;\n    };\n}\n#endif //OPENMW_DEDICATEDPLAYER_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/GUIChat.cpp",
    "content": "#include \"GUIChat.hpp\"\n\n#include <MyGUI_EditBox.h>\n#include \"apps/openmw/mwbase/environment.hpp\"\n#include \"apps/openmw/mwgui/windowmanagerimp.hpp\"\n#include \"apps/openmw/mwinput/inputmanagerimp.hpp\"\n#include <MyGUI_InputManager.h>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"../Networking.hpp\"\n#include \"../Main.hpp\"\n#include \"../LocalPlayer.hpp\"\n\n#include \"../GUIController.hpp\"\n\n\nnamespace mwmp\n{\n    GUIChat::GUIChat(int x, int y, int w, int h)\n            : WindowBase(\"tes3mp_chat.layout\")\n    {\n        setCoord(x, y, w, h);\n\n        getWidget(mCommandLine, \"edit_Command\");\n        getWidget(mHistory, \"list_History\");\n\n        // Set up the command line box\n        mCommandLine->eventEditSelectAccept +=\n                newDelegate(this, &GUIChat::acceptCommand);\n        mCommandLine->eventKeyButtonPressed +=\n                newDelegate(this, &GUIChat::keyPress);\n\n        setTitle(\"Chat\");\n\n        mHistory->setOverflowToTheLeft(true);\n        mHistory->setEditWordWrap(true);\n        mHistory->setTextShadow(true);\n        mHistory->setTextShadowColour(MyGUI::Colour::Black);\n\n        mHistory->setNeedKeyFocus(false);\n\n        windowState = CHAT_DISABLED;\n        mCommandLine->setVisible(false);\n        delay = 3; // 3 sec.\n    }\n\n    void GUIChat::onOpen()\n    {\n        // Give keyboard focus to the combo box whenever the console is\n        // turned on\n        setEditState(false);\n\n        if (windowState == CHAT_DISABLED)\n            windowState = CHAT_ENABLED;\n    }\n\n    void GUIChat::onClose()\n    {\n        setEditState(false);\n    }\n\n    bool GUIChat::exit()\n    {\n        //WindowBase::exit();\n        return true;\n    }\n\n    bool GUIChat::getEditState()\n    {\n        return editState;\n    }\n\n    void GUIChat::acceptCommand(MyGUI::EditBox *_sender)\n    {\n        const std::string &cm = mCommandLine->getOnlyText();\n\n        // If they enter nothing, then it should be canceled.\n        // Otherwise, there's no way of closing without having text.\n        if (cm.empty())\n        {\n            mCommandLine->setCaption(\"\");\n            setEditState(false);\n            return;\n        }\n\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Player: %s\", cm.c_str());\n\n        // Add the command to the history, and set the current pointer to\n        // the end of the list\n        if (mCommandHistory.empty() || mCommandHistory.back() != cm)\n            mCommandHistory.push_back(cm);\n        mCurrent = mCommandHistory.end();\n        mEditString.clear();\n\n        // Reset the command line before the command execution.\n        // It prevents the re-triggering of the acceptCommand() event for the same command\n        // during the actual command execution\n        mCommandLine->setCaption(\"\");\n        setEditState(false);\n        send(cm);\n    }\n\n    void GUIChat::onResChange(int width, int height)\n    {\n        setCoord(10,10, width-10, height/2);\n    }\n\n    void GUIChat::setFont(const std::string &fntName)\n    {\n        mHistory->setFontName(fntName);\n        mCommandLine->setFontName(fntName);\n    }\n\n    void GUIChat::print(const std::string &msg, const std::string &color)\n    {\n        if (windowState == CHAT_HIDDENMODE && !isVisible())\n        {\n            setVisible(true);\n        }\n\n        if(msg.size() == 0)\n        {\n            clean();\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Chat cleaned\");\n        }\n        else\n        {\n            mHistory->addText(color + msg);\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"%s\", msg.c_str());\n        }\n    }\n\n    void GUIChat::printOK(const std::string &msg)\n    {\n        print(msg + \"\\n\", \"#FF00FF\");\n    }\n\n    void GUIChat::printError(const std::string &msg)\n    {\n        print(msg + \"\\n\", \"#FF2222\");\n    }\n\n    void GUIChat::send(const std::string &str)\n    {\n        LocalPlayer *localPlayer = Main::get().getLocalPlayer();\n\n        Networking *networking = Main::get().getNetworking();\n\n        localPlayer->chatMessage = str;\n\n        networking->getPlayerPacket(ID_CHAT_MESSAGE)->setPlayer(localPlayer);\n        networking->getPlayerPacket(ID_CHAT_MESSAGE)->Send();\n    }\n\n    void GUIChat::clean()\n    {\n        mHistory->setCaption(\"\");\n    }\n\n    void GUIChat::pressedChatMode()\n    {\n        windowState++;\n        if (windowState == 3) windowState = 0;\n\n        std::string chatMode = windowState == CHAT_DISABLED ? \"Chat hidden\" :\n                               windowState == CHAT_ENABLED ? \"Chat visible\" :\n                               \"Chat appearing when needed\";\n\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Switch chat mode to %s\", chatMode.c_str());\n        MWBase::Environment::get().getWindowManager()->messageBox(chatMode);\n\n        switch (windowState)\n        {\n            case CHAT_DISABLED:\n                setVisible(false);\n                setEditState(false);\n                break;\n            case CHAT_ENABLED:\n                setVisible(true);\n                break;\n            default: //CHAT_HIDDENMODE\n                setVisible(true);\n                curTime = 0;\n        }\n    }\n\n    void GUIChat::setEditState(bool state)\n    {\n        editState = state;\n        mCommandLine->setVisible(editState);\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(editState ? mCommandLine : nullptr);\n    }\n\n    void GUIChat::pressedSay()\n    {\n        if (windowState == CHAT_DISABLED)\n            return;\n\n        if (!mCommandLine->getVisible())\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Opening chat.\");\n\n        if (windowState == CHAT_HIDDENMODE)\n        {\n            setVisible(true);\n            curTime = 0;\n        }\n\n        setEditState(true);\n    }\n\n    void GUIChat::keyPress(MyGUI::Widget *_sender, MyGUI::KeyCode key, MyGUI::Char _char)\n    {\n        if (mCommandHistory.empty()) return;\n\n        // Traverse history with up and down arrows\n        if (key == MyGUI::KeyCode::ArrowUp)\n        {\n            // If the user was editing a string, store it for later\n            if (mCurrent == mCommandHistory.end())\n                mEditString = mCommandLine->getOnlyText();\n\n            if (mCurrent != mCommandHistory.begin())\n            {\n                --mCurrent;\n                mCommandLine->setCaption(*mCurrent);\n            }\n        }\n        else if (key == MyGUI::KeyCode::ArrowDown)\n        {\n            if (mCurrent != mCommandHistory.end())\n            {\n                ++mCurrent;\n\n                if (mCurrent != mCommandHistory.end())\n                    mCommandLine->setCaption(*mCurrent);\n                else\n                    // Restore the edit string\n                    mCommandLine->setCaption(mEditString);\n            }\n        }\n\n    }\n\n    void GUIChat::update(float dt)\n    {\n        if (windowState == CHAT_HIDDENMODE && !editState && isVisible())\n        {\n            curTime += dt;\n            if (curTime >= delay)\n            {\n                setEditState(false);\n                setVisible(false);\n            }\n        }\n    }\n\n    void GUIChat::setDelay(float newDelay)\n    {\n        this->delay = newDelay;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/GUIChat.hpp",
    "content": "#ifndef OPENMW_GUICHAT_HPP\n#define OPENMW_GUICHAT_HPP\n\n#include <list>\n#include <string>\n#include <vector>\n\n#include \"apps/openmw/mwgui/windowbase.hpp\"\n\nnamespace mwmp\n{\n    class GUIController;\n    class GUIChat : public MWGui::WindowBase\n    {\n        friend class GUIController;\n    public:\n        enum\n        {\n            CHAT_DISABLED = 0,\n            CHAT_ENABLED,\n            CHAT_HIDDENMODE\n        } CHAT_WIN_STATE;\n\n        MyGUI::EditBox* mCommandLine;\n        MyGUI::EditBox* mHistory;\n\n        typedef std::list<std::string> StringList;\n\n        // History of previous entered commands\n        StringList mCommandHistory;\n        StringList::iterator mCurrent;\n        std::string mEditString;\n\n        GUIChat(int x, int y, int w, int h);\n\n        void pressedChatMode(); //switch chat mode\n        void pressedSay(); // switch chat focus (if chat mode != CHAT_DISABLED)\n        void setDelay(float newDelay);\n\n        void update(float dt);\n\n        virtual void onOpen();\n        virtual void onClose();\n\n        virtual bool exit();\n\n        bool getEditState();\n\n        void setFont(const std::string &fntName);\n\n        void onResChange(int width, int height);\n\n        // Print a message to the console, in specified color.\n        void print(const std::string &msg, const std::string& color = \"#FFFFFF\");\n\n        // Clean chat\n        void clean();\n\n        // These are pre-colored versions that you should use.\n\n        /// Output from successful console command\n        void printOK(const std::string &msg);\n\n        /// Error message\n        void printError(const std::string &msg);\n\n        void send(const std::string &str);\n\n    protected:\n\n    private:\n\n        void keyPress(MyGUI::Widget* _sender,\n                      MyGUI::KeyCode key,\n                      MyGUI::Char _char);\n\n        void acceptCommand(MyGUI::EditBox* _sender);\n\n        void setEditState(bool state);\n\n        int windowState;\n        bool editState;\n        float delay;\n        float curTime;\n    };\n}\n#endif //OPENMW_GUICHAT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/GUIDialogList.cpp",
    "content": "#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"apps/openmw/mwbase/environment.hpp\"\n#include \"apps/openmw/mwgui/windowmanagerimp.hpp\"\n\n#include <MyGUI_EditBox.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_ListBox.h>\n\n#include \"GUIDialogList.hpp\"\n#include \"../Main.hpp\"\n#include \"../Networking.hpp\"\n#include \"../LocalPlayer.hpp\"\n\nusing namespace mwmp;\n\nGUIDialogList::GUIDialogList(const std::string &message, const std::vector<std::string> &list) : WindowModal(\"tes3mp_dialog_list.layout\")\n{\n    center(); // center window\n\n    getWidget(mListBox, \"ListBox\");\n    getWidget(mMessage, \"Message\");\n    getWidget(mButton, \"OkButton\");\n\n    mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GUIDialogList::mousePressed);\n\n    mMessage->setCaptionWithReplacing(message);\n    for (size_t i = 0; i < list.size(); i++)\n        mListBox->addItem(list[i]);\n\n}\n\nvoid GUIDialogList::mousePressed(MyGUI::Widget * /*widget*/)\n{\n    setVisible(false);\n    MWBase::Environment::get().getWindowManager()->popGuiMode();\n\n    size_t id = mListBox->getIndexSelected();\n\n    Main::get().getLocalPlayer()->guiMessageBox.data = MyGUI::utility::toString(id);\n    Main::get().getNetworking()->getPlayerPacket(ID_GUI_MESSAGEBOX)->setPlayer(Main::get().getLocalPlayer());\n    Main::get().getNetworking()->getPlayerPacket(ID_GUI_MESSAGEBOX)->Send();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Selected id: %d\", id);\n    if (id == MyGUI::ITEM_NONE)\n        return;\n\n    std::string itemName = mListBox->getItemNameAt(mListBox->getIndexSelected()).asUTF8();\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"name of item: '%s'\", itemName.c_str());\n}\n\nvoid GUIDialogList::onFrame(float frameDuration)\n{\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/GUIDialogList.hpp",
    "content": "#ifndef OPENMW_GUIDIALOGLIST_HPP\n#define OPENMW_GUIDIALOGLIST_HPP\n\n\n#include \"apps/openmw/mwgui/windowbase.hpp\"\n\nnamespace mwmp\n{\n    class GUIDialogList : public MWGui::WindowModal\n    {\n    public:\n        GUIDialogList(const std::string &message, const std::vector<std::string> &list);\n        void mousePressed(MyGUI::Widget *_widget);\n        void onFrame(float frameDuration);\n    private:\n        bool mMarkedToDelete;\n        MyGUI::EditBox *mMessage;\n        MyGUI::ListBox *mListBox;\n        MyGUI::Button *mButton;\n    };\n}\n\n#endif //OPENMW_GUIDIALOGLIST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/GUILogin.cpp",
    "content": "#include \"GUILogin.hpp\"\n#include <MyGUI_EditBox.h>\n#include <MyGUI_Button.h>\n\n\nGUILogin::GUILogin() : WindowModal(\"tes3mp_login.layout\")\n{\n    center(); // center window\n\n    setVisible(false);\n\n\n    getWidget(mLogin, \"EditLogin\");\n    getWidget(mServer, \"EditServer\");\n    getWidget(mPort, \"EditPort\");\n    getWidget(mConnect, \"ButtonConnect\");\n\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/GUILogin.hpp",
    "content": "#ifndef OPENMW_GUILOGIN_HPP\n#define OPENMW_GUILOGIN_HPP\n\n#include \"apps/openmw/mwgui/windowbase.hpp\"\n\nclass GUILogin : public MWGui::WindowModal\n{\npublic:\n    GUILogin();\n\n    MyGUI::EditBox* mLogin;\n    MyGUI::EditBox* mServer;\n    MyGUI::EditBox* mPort;\nprotected:\n    MyGUI::Button* mConnect;\n};\n\n\n#endif //OPENMW_GUILOGIN_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/PlayerMarkerCollection.cpp",
    "content": "#include <stdexcept>\n#include \"PlayerMarkerCollection.hpp\"\n\n\nusing namespace mwmp;\n\nvoid PlayerMarkerCollection::addMarker(const ESM::CustomMarker &marker, bool triggerEvent)\n{\n    mMarkers.insert(std::make_pair(marker.mCell, marker));\n    if (triggerEvent)\n        eventMarkersChanged();\n}\n\nvoid PlayerMarkerCollection::deleteMarker(const ESM::CustomMarker &marker)\n{\n    std::pair<ContainerType::iterator, ContainerType::iterator> range = mMarkers.equal_range(marker.mCell);\n\n    for (ContainerType::iterator it = range.first; it != range.second; ++it)\n    {\n        if (it->second == marker)\n        {\n            mMarkers.erase(it);\n            eventMarkersChanged();\n            return;\n        }\n    }\n    throw std::runtime_error(\"can't find marker to delete\");\n}\n\nvoid PlayerMarkerCollection::updateMarker(const ESM::CustomMarker &marker, const std::string &newNote)\n{\n    std::pair<ContainerType::iterator, ContainerType::iterator> range = mMarkers.equal_range(marker.mCell);\n\n    for (ContainerType::iterator it = range.first; it != range.second; ++it)\n    {\n        if (it->second == marker)\n        {\n            it->second.mNote = newNote;\n            eventMarkersChanged();\n            return;\n        }\n    }\n    throw std::runtime_error(\"can't find marker to update\");\n}\n\nvoid PlayerMarkerCollection::clear()\n{\n    mMarkers.clear();\n    eventMarkersChanged();\n}\n\nPlayerMarkerCollection::ContainerType::const_iterator PlayerMarkerCollection::begin() const\n{\n    return mMarkers.begin();\n}\n\nPlayerMarkerCollection::ContainerType::const_iterator PlayerMarkerCollection::end() const\n{\n    return mMarkers.end();\n}\n\nPlayerMarkerCollection::RangeType PlayerMarkerCollection::getMarkers(const ESM::CellId &cellId) const\n{\n    return mMarkers.equal_range(cellId);\n}\n\nsize_t PlayerMarkerCollection::size() const\n{\n    return mMarkers.size();\n}\n\nbool PlayerMarkerCollection::contains(const ESM::CustomMarker &marker)\n{\n    std::pair<ContainerType::iterator, ContainerType::iterator> range = mMarkers.equal_range(marker.mCell);\n\n    for (ContainerType::iterator it = range.first; it != range.second; ++it)\n    {\n        if (it->second == marker)\n            return true;\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/PlayerMarkerCollection.hpp",
    "content": "// Copied from MWGui::CustomMarkerCollection\n\n#ifndef OPENMW_PLAYERMARKERCOLLECTION_HPP\n#define OPENMW_PLAYERMARKERCOLLECTION_HPP\n\n#include <components/esm/cellid.hpp>\n#include <components/esm/custommarkerstate.hpp>\n#include <map>\n#include <MyGUI_Common.h>\n#include <MyGUI_Colour.h>\n\nnamespace mwmp\n{\n    class PlayerMarkerCollection\n    {\n    public:\n\n        void addMarker(const ESM::CustomMarker &marker, bool triggerEvent = true);\n        void deleteMarker(const ESM::CustomMarker &marker);\n        void updateMarker(const ESM::CustomMarker &marker, const std::string &newNote);\n\n        void clear();\n\n        size_t size() const;\n\n        typedef std::multimap <ESM::CellId, ESM::CustomMarker> ContainerType;\n\n        typedef std::pair <ContainerType::const_iterator, ContainerType::const_iterator> RangeType;\n\n        ContainerType::const_iterator begin() const;\n        ContainerType::const_iterator end() const;\n\n        RangeType getMarkers(const ESM::CellId &cellId) const;\n\n        typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;\n        EventHandle_Void eventMarkersChanged;\n\n        bool contains(const ESM::CustomMarker &marker);\n    private:\n        ContainerType mMarkers;\n    };\n}\n\n#endif //OPENMW_PLAYERMARKERCOLLECTION_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/TextInputDialog.cpp",
    "content": "#include \"TextInputDialog.hpp\"\n\n#include \"apps/openmw/mwbase/windowmanager.hpp\"\n#include \"apps/openmw/mwbase/environment.hpp\"\n\n#include <MyGUI_EditBox.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_InputManager.h>\n\nnamespace mwmp\n{\n    TextInputDialog::TextInputDialog()\n            : MWGui::WindowModal(\"tes3mp_text_input.layout\")\n    {\n        // Centre dialog\n        center();\n\n        getWidget(mTextEdit, \"TextEdit\");\n        mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted);\n\n        MyGUI::Button *okButton;\n        getWidget(okButton, \"OKButton\");\n        okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked);\n\n        // Make sure the edit box has focus\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);\n    }\n\n    void TextInputDialog::setNextButtonShow(bool shown)\n    {\n        MyGUI::Button *okButton;\n        getWidget(okButton, \"OKButton\");\n\n        if (shown)\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sNext\", \"\"));\n        else\n            okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sOK\", \"\"));\n    }\n\n    void TextInputDialog::setEditPassword(bool value)\n    {\n        mTextEdit->setEditPassword(value);\n    }\n\n    void TextInputDialog::setTextLabel(const std::string &label)\n    {\n        setText(\"LabelT\", label);\n    }\n\n    void TextInputDialog::setTextNote(const std::string &note)\n    {\n        setText(\"TextNote\", note);\n    }\n\n    void TextInputDialog::onOpen()\n    {\n        WindowModal::onOpen();\n        // Make sure the edit box has focus\n        MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);\n    }\n\n    bool TextInputDialog::exit()\n    {\n        return false;\n    }\n\n// widget controls\n\n    void TextInputDialog::onOkClicked(MyGUI::Widget *_sender)\n    {\n        if (mTextEdit->getCaption() == \"\")\n        {\n            //MWBase::Environment::get().getWindowManager()->messageBox (\"#{sNotifyMessage37}\");\n            MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);\n        }\n        else\n            eventDone(this);\n    }\n\n    void TextInputDialog::onTextAccepted(MyGUI::Edit *_sender)\n    {\n        onOkClicked(_sender);\n    }\n\n    std::string TextInputDialog::getTextInput() const\n    {\n        return mTextEdit->getCaption();\n    }\n\n    void TextInputDialog::setTextInput(const std::string &text)\n    {\n        mTextEdit->setCaption(text);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/GUI/TextInputDialog.hpp",
    "content": "#ifndef OPENMW_TEXTINPUTDIALOG_HPP\n#define OPENMW_TEXTINPUTDIALOG_HPP\n\n#include \"apps/openmw/mwgui/windowbase.hpp\"\n\nnamespace MWGui\n{\n    class WindowManager;\n}\n\nnamespace mwmp\n{\n    class TextInputDialog : public MWGui::WindowModal\n    {\n    public:\n        TextInputDialog();\n\n        std::string getTextInput() const;\n        void setTextInput(const std::string &text);\n\n        void setNextButtonShow(bool shown);\n        void setTextLabel(const std::string &label);\n        void setTextNote(const std::string &note);\n\n        void setEditPassword(bool value);\n\n        virtual void onOpen();\n        virtual bool exit();\n\n        /** Event : Dialog finished, OK button clicked.\\n\n            signature : void method()\\n\n        */\n        EventHandle_WindowBase eventDone;\n\n    protected:\n        void onOkClicked(MyGUI::Widget *_sender);\n        void onTextAccepted(MyGUI::Edit *_sender);\n\n    private:\n        MyGUI::EditBox *mTextEdit;\n    };\n}\n\n\n#endif //OPENMW_TEXTINPUTDIALOG_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/GUIController.cpp",
    "content": "#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Base/BasePlayer.hpp>\n\n#include <SDL_system.h>\n\n#include <MyGUI_FactoryManager.h>\n#include <MyGUI_Gui.h>\n#include <MyGUI_ImageBox.h>\n#include <MyGUI_InputManager.h>\n#include <MyGUI_LanguageManager.h>\n#include <MyGUI_RenderManager.h>\n#include <MyGUI_RotatingSkin.h>\n#include <MyGUI_ScrollView.h>\n#include <MyGUI_TextIterator.h>\n\n#include <extern/PicoSHA2/picosha2.h>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n\n#include \"../mwgui/mapwindow.hpp\"\n\n#include \"../mwworld/worldimp.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"GUIController.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"GUI/PlayerMarkerCollection.hpp\"\n#include \"GUI/GUIDialogList.hpp\"\n#include \"GUI/GUIChat.hpp\"\n#include \"LocalPlayer.hpp\"\n#include \"DedicatedPlayer.hpp\"\n#include \"PlayerList.hpp\"\n\n\nmwmp::GUIController::GUIController(): mInputBox(0), mListBox(0)\n{\n    mChat = nullptr;\n    keySay = SDL_SCANCODE_Y;\n    keyChatMode = SDL_SCANCODE_F2;\n}\n\nmwmp::GUIController::~GUIController()\n{\n\n}\n\nvoid mwmp::GUIController::cleanUp()\n{\n    mPlayerMarkers.clear();\n    if (mChat != nullptr)\n        delete mChat;\n    mChat = nullptr;\n}\n\nvoid mwmp::GUIController::refreshGuiMode(MWGui::GuiMode guiMode)\n{\n    if (MWBase::Environment::get().getWindowManager()->containsMode(guiMode))\n    {\n        MWBase::Environment::get().getWindowManager()->removeGuiMode(guiMode);\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(guiMode);\n    }\n}\n\nvoid mwmp::GUIController::setupChat()\n{\n    assert(mChat == nullptr);\n\n    float chatDelay = Settings::Manager::getFloat(\"delay\", \"Chat\");\n    int chatY = Settings::Manager::getInt(\"y\", \"Chat\");\n    int chatX = Settings::Manager::getInt(\"x\", \"Chat\");\n    int chatW = Settings::Manager::getInt(\"w\", \"Chat\");\n    int chatH = Settings::Manager::getInt(\"h\", \"Chat\");\n\n    keySay = SDL_GetScancodeFromName(Settings::Manager::getString(\"keySay\", \"Chat\").c_str());\n    keyChatMode = SDL_GetScancodeFromName(Settings::Manager::getString(\"keyChatMode\", \"Chat\").c_str());\n\n    mChat = new GUIChat(chatX, chatY, chatW, chatH);\n    mChat->setDelay(chatDelay);\n}\n\nvoid mwmp::GUIController::printChatMessage(std::string &msg)\n{\n    if (mChat != nullptr)\n        mChat->print(msg);\n}\n\n\nvoid mwmp::GUIController::setChatVisible(bool chatVisible)\n{\n    mChat->setVisible(chatVisible);\n}\n\nvoid mwmp::GUIController::showDialogList(const mwmp::BasePlayer::GUIMessageBox &guiMessageBox)\n{\n    MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();\n    \n    if (mListBox != NULL)\n    {\n        windowManager->removeDialog(mListBox);\n        windowManager->removeCurrentModal(mListBox);\n        mListBox = NULL;\n    }\n\n    std::vector<std::string> list;\n\n    std::string buf;\n\n    for (const auto &data : guiMessageBox.data)\n    {\n        if (data == '\\n')\n        {\n            list.push_back(buf);\n            buf.erase();\n            continue;\n        }\n        buf += data;\n    }\n\n    list.push_back(buf);\n\n    mListBox = new GUIDialogList(guiMessageBox.label, list);\n    windowManager->pushGuiMode((MWGui::GuiMode)GM_TES3MP_ListBox);\n}\n\nvoid mwmp::GUIController::showMessageBox(const BasePlayer::GUIMessageBox &guiMessageBox)\n{\n    MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();\n    windowManager->messageBox(guiMessageBox.label);\n}\n\nstd::vector<std::string> splitString(const std::string &str, char delim = ';')\n{\n    std::istringstream ss(str);\n    std::vector<std::string> result;\n    std::string token;\n    while (std::getline(ss, token, delim))\n        result.push_back(token);\n    return result;\n}\n\nvoid mwmp::GUIController::showCustomMessageBox(const BasePlayer::GUIMessageBox &guiMessageBox)\n{\n    MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();\n    std::vector<std::string> buttons = splitString(guiMessageBox.buttons);\n    windowManager->interactiveMessageBox(guiMessageBox.label, buttons, false, true);\n}\n\nvoid mwmp::GUIController::showInputBox(const BasePlayer::GUIMessageBox &guiMessageBox)\n{\n    MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();\n\n    windowManager->removeDialog(mInputBox);\n    windowManager->pushGuiMode((MWGui::GuiMode)GM_TES3MP_InputBox);\n    mInputBox = 0;\n    mInputBox = new TextInputDialog();\n\n    mInputBox->setEditPassword(guiMessageBox.type == BasePlayer::GUIMessageBox::PasswordDialog);\n\n    mInputBox->setTextLabel(guiMessageBox.label);\n    mInputBox->setTextNote(guiMessageBox.note);\n\n    mInputBox->eventDone += MyGUI::newDelegate(this, &GUIController::onInputBoxDone);\n\n    mInputBox->setVisible(true);\n}\n\nvoid mwmp::GUIController::onInputBoxDone(MWGui::WindowBase *parWindow)\n{\n    LocalPlayer *localPlayer = Main::get().getLocalPlayer();\n    std::string textInput = mInputBox->getTextInput();\n\n    // Send input for password dialogs after it's been hashed and rehashed, for some slight\n    // extra security that doesn't require the client to keep storing a salt\n    if (localPlayer->guiMessageBox.type == BasePlayer::GUIMessageBox::PasswordDialog)\n    {\n        textInput = picosha2::hash256_hex_string(textInput);\n        textInput = picosha2::hash256_hex_string(textInput + picosha2::hash256_hex_string(picosha2::hash256_hex_string((textInput))));\n    }\n\n    localPlayer->guiMessageBox.data = textInput;\n\n    PlayerPacket *playerPacket = Main::get().getNetworking()->getPlayerPacket(ID_GUI_MESSAGEBOX);\n    playerPacket->setPlayer(Main::get().getLocalPlayer());\n    playerPacket->Send();\n\n    MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();\n    windowManager->removeDialog(mInputBox);\n    mInputBox = 0;\n    windowManager->popGuiMode();\n}\n\nbool mwmp::GUIController::pressedKey(int key)\n{\n    MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();\n    if (mChat == nullptr || windowManager->isConsoleMode() || windowManager->getMode() != MWGui::GM_None)\n        return false;\n    if (key == keyChatMode)\n    {\n        mChat->pressedChatMode();\n        return true;\n    }\n    else if (key == keySay)\n    {\n        mChat->pressedSay();\n        return true;\n    }\n    return false;\n}\n\nvoid mwmp::GUIController::changeChatMode()\n{\n    mChat->pressedChatMode();\n}\n\nbool mwmp::GUIController::getChatEditState()\n{\n    return mChat->editState;\n}\n\nvoid mwmp::GUIController::update(float dt)\n{\n    if (mChat != nullptr)\n        mChat->update(dt);\n}\n\nvoid mwmp::GUIController::processCustomMessageBoxInput(int pressedButton)\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Pressed: %d\", pressedButton);\n\n    LocalPlayer* localPlayer = Main::get().getLocalPlayer();\n    localPlayer->guiMessageBox.data = MyGUI::utility::toString(pressedButton);\n\n    PlayerPacket* playerPacket = Main::get().getNetworking()->getPlayerPacket(ID_GUI_MESSAGEBOX);\n    playerPacket->setPlayer(Main::get().getLocalPlayer());\n    playerPacket->Send();\n}\n\nvoid mwmp::GUIController::WM_UpdateVisible(MWGui::GuiMode mode)\n{\n    switch((int)mode)\n    {\n        case GM_TES3MP_InputBox:\n        {\n            if (mInputBox != 0)\n                mInputBox->setVisible(true);\n            break;\n        }\n        case GM_TES3MP_ListBox:\n        {\n            if (mListBox != 0)\n                mListBox->setVisible(true);\n            break;\n        }\n        default:\n            break;\n    }\n}\n\nclass MarkerWidget: public MyGUI::Widget\n{\nMYGUI_RTTI_DERIVED(MarkerWidget)\n\npublic:\n    void setNormalColour(const MyGUI::Colour& colour)\n    {\n        mNormalColour = colour;\n        setColour(colour);\n    }\n\n    void setHoverColour(const MyGUI::Colour& colour)\n    {\n        mHoverColour = colour;\n    }\n\nprivate:\n    MyGUI::Colour mNormalColour;\n    MyGUI::Colour mHoverColour;\n\n    void onMouseLostFocus(MyGUI::Widget* _new)\n    {\n        setColour(mNormalColour);\n    }\n\n    void onMouseSetFocus(MyGUI::Widget* _old)\n    {\n        setColour(mHoverColour);\n    }\n};\n\nESM::CustomMarker mwmp::GUIController::createMarker(const RakNet::RakNetGUID &guid)\n{\n    DedicatedPlayer *player = PlayerList::getPlayer(guid);\n    ESM::CustomMarker mEditingMarker;\n    if (!player)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Unknown player guid: %s\", guid.ToString());\n        return mEditingMarker;\n    }\n\n    mEditingMarker.mNote = player->npc.mName;\n\n    const ESM::Cell *playerCell = &player->cell;\n\n    mEditingMarker.mCell = player->cell.mCellId;\n\n    mEditingMarker.mWorldX = player->position.pos[0];\n    mEditingMarker.mWorldY = player->position.pos[1];\n\n    mEditingMarker.mCell.mPaged = playerCell->isExterior();\n    if (!playerCell->isExterior())\n        mEditingMarker.mCell.mWorldspace = playerCell->mName;\n    else\n    {\n        mEditingMarker.mCell.mWorldspace = ESM::CellId::sDefaultWorldspace;\n\n        // Don't remove these, or the markers will stop showing up in exteriors\n        mEditingMarker.mCell.mIndex.mX = playerCell->getGridX();\n        mEditingMarker.mCell.mIndex.mY = playerCell->getGridY();\n    }\n    return mEditingMarker;\n}\n\n\nvoid mwmp::GUIController::updatePlayersMarkers(MWGui::LocalMapBase *localMapBase)\n{\n    std::vector<MyGUI::Widget*>::iterator markerWidgetIterator = localMapBase->mPlayerMarkerWidgets.begin();\n    for (; markerWidgetIterator != localMapBase->mPlayerMarkerWidgets.end(); ++markerWidgetIterator)\n        MyGUI::Gui::getInstance().destroyWidget(*markerWidgetIterator);\n    localMapBase->mPlayerMarkerWidgets.clear();\n\n    for (int dX = -localMapBase->mCellDistance; dX <= localMapBase->mCellDistance; ++dX)\n    {\n        for (int dY =-localMapBase->mCellDistance; dY <= localMapBase->mCellDistance; ++dY)\n        {\n            ESM::CellId cellId;\n            cellId.mPaged = !localMapBase->mInterior;\n            cellId.mWorldspace = (localMapBase->mInterior ? localMapBase->mPrefix : ESM::CellId::sDefaultWorldspace);\n            cellId.mIndex.mX = localMapBase->mCurX+dX;\n            cellId.mIndex.mY = localMapBase->mCurY+dY;\n\n            PlayerMarkerCollection::RangeType markers = mPlayerMarkers.getMarkers(cellId);\n            for (PlayerMarkerCollection::ContainerType::const_iterator markerIterator = markers.first;\n                markerIterator != markers.second; ++markerIterator)\n            {\n                const ESM::CustomMarker &marker = markerIterator->second;\n\n                MWGui::LocalMapBase::MarkerUserData markerPos (localMapBase->mLocalMapRender);\n                MyGUI::IntPoint widgetPos = localMapBase->getMarkerPosition(marker.mWorldX, marker.mWorldY, markerPos);\n\n                MyGUI::IntCoord widgetCoord(widgetPos.left - 8, widgetPos.top - 8, 16, 16);\n                MarkerWidget* markerWidget = localMapBase->mLocalMap->createWidget<MarkerWidget>(\"CustomMarkerButton\",\n                                                                                   widgetCoord, MyGUI::Align::Default);\n\n                markerWidget->setDepth(0); // Local_MarkerAboveFogLayer\n                markerWidget->setUserString(\"ToolTipType\", \"Layout\");\n                markerWidget->setUserString(\"ToolTipLayout\", \"TextToolTipOneLine\");\n                markerWidget->setUserString(\"Caption_TextOneLine\", MyGUI::TextIterator::toTagsString(marker.mNote));\n                markerWidget->setNormalColour(MyGUI::Colour(0.6f, 0.6f, 0.6f));\n                markerWidget->setHoverColour(MyGUI::Colour(1.0f, 1.0f, 1.0f));\n                markerWidget->setUserData(marker);\n                markerWidget->setNeedMouseFocus(true);\n                //localMapBase->customMarkerCreated(markerWidget);\n                localMapBase->mPlayerMarkerWidgets.push_back(markerWidget);\n            }\n        }\n    }\n    localMapBase->redraw();\n}\n\nvoid mwmp::GUIController::setGlobalMapMarkerTooltip(MWGui::MapWindow *mapWindow, MyGUI::Widget *markerWidget, int x, int y)\n{\n    ESM::CellId cellId;\n    cellId.mIndex.mX = x;\n    cellId.mIndex.mY = y;\n    cellId.mWorldspace = ESM::CellId::sDefaultWorldspace;\n    cellId.mPaged = true;\n    PlayerMarkerCollection::RangeType markers = mPlayerMarkers.getMarkers(cellId);\n    std::vector<std::string> destNotes;\n    for (PlayerMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; ++it)\n        destNotes.push_back(it->second.mNote);\n\n    if (!destNotes.empty())\n    {\n        MWGui::LocalMapBase::MarkerUserData data (nullptr);\n        data.notes = destNotes;\n        data.caption = markerWidget->getUserString(\"Caption_TextOneLine\");\n\n        markerWidget->setUserData(data);\n        markerWidget->setUserString(\"ToolTipType\", \"MapMarker\");\n    }\n    else\n        markerWidget->setUserString(\"ToolTipType\", \"Layout\");\n}\n\nvoid mwmp::GUIController::updateGlobalMapMarkerTooltips(MWGui::MapWindow *mapWindow)\n{\n    for (const auto &widget : mapWindow->mGlobalMapMarkers)\n    {\n        const int x = widget.first.first;\n        const int y = widget.first.second;\n        setGlobalMapMarkerTooltip(mapWindow, widget.second, x, y);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/GUIController.hpp",
    "content": "#ifndef OPENMW_GUICONTROLLER_HPP\n#define OPENMW_GUICONTROLLER_HPP\n\n#include <components/settings/settings.hpp>\n\n#include \"apps/openmw/mwgui/mode.hpp\"\n\n#include <components/openmw-mp/Base/BasePlayer.hpp>\n#include \"GUI/PlayerMarkerCollection.hpp\"\n#include \"GUI/TextInputDialog.hpp\"\n\nnamespace MWGui\n{\n    class LocalMapBase;\n    class MapWindow;\n}\n\nnamespace mwmp\n{\n    class GUIDialogList;\n    class GUIChat;\n    class GUIController\n    {\n    public:\n        enum GM\n        {\n            GM_VR_MetaMenu = MWGui::GM_QuickKeysMenu + 1, // Put this dummy GuiMode here because it's used in VR\n            GM_TES3MP_InputBox,\n            GM_TES3MP_ListBox\n\n        };\n        GUIController();\n        ~GUIController();\n        void cleanUp();\n\n        void refreshGuiMode(MWGui::GuiMode guiMode);\n\n        void setupChat();\n\n        void printChatMessage(std::string &msg);\n        void setChatVisible(bool chatVisible);\n\n        void showMessageBox(const BasePlayer::GUIMessageBox &guiMessageBox);\n        void showCustomMessageBox(const BasePlayer::GUIMessageBox &guiMessageBox);\n        void showInputBox(const BasePlayer::GUIMessageBox &guiMessageBox);\n\n        void showDialogList(const BasePlayer::GUIMessageBox &guiMessageBox);\n\n        /// Returns 0 if there was no events\n        bool pressedKey(int key);\n\n        void changeChatMode();\n\n        bool getChatEditState();\n\n        void update(float dt);\n\n        void processCustomMessageBoxInput(int pressedButton);\n\n        void WM_UpdateVisible(MWGui::GuiMode mode);\n\n        void updatePlayersMarkers(MWGui::LocalMapBase *localMapBase);\n        void updateGlobalMapMarkerTooltips(MWGui::MapWindow *pWindow);\n\n        ESM::CustomMarker createMarker(const RakNet::RakNetGUID &guid);\n        PlayerMarkerCollection mPlayerMarkers;\n    private:\n        void setGlobalMapMarkerTooltip(MWGui::MapWindow *mapWindow ,MyGUI::Widget* markerWidget, int x, int y);\n\n    private:\n        GUIChat *mChat;\n        int keySay;\n        int keyChatMode;\n\n        long id;\n        TextInputDialog *mInputBox;\n        GUIDialogList *mListBox;\n        void onInputBoxDone(MWGui::WindowBase* parWindow);\n        //MyGUI::Widget *oldFocusWidget, *currentFocusWidget;\n    };\n}\n\n#endif //OPENMW_GUICONTROLLER_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/LocalActor.cpp",
    "content": "#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwmechanics/mechanicsmanagerimp.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"LocalActor.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"ActorList.hpp\"\n#include \"MechanicsHelper.hpp\"\n\nusing namespace mwmp;\n\nLocalActor::LocalActor()\n{\n    hasSentData = false;\n    posWasChanged = false;\n    equipmentChanged = false;\n\n    wasRunning = false;\n    wasSneaking = false;\n    wasForceJumping = false;\n    wasForceMoveJumping = false;\n    wasFlying = false;\n\n    attack.type = Attack::MELEE;\n    attack.shouldSend = false;\n    attack.instant = false;\n    attack.pressed = false;\n\n    cast.type = Cast::REGULAR;\n    cast.shouldSend = false;\n    cast.instant = false;\n    cast.pressed = false;\n\n    killer.isPlayer = false;\n    killer.refId = \"\";\n    killer.name = \"\";\n\n    creatureStats.mDead = false;\n    creatureStats.mDeathAnimationFinished = false;\n}\n\nLocalActor::~LocalActor()\n{\n\n}\n\nvoid LocalActor::update(bool forceUpdate)\n{\n    updateStatsDynamic(forceUpdate);\n    updateEquipment(forceUpdate, false);\n\n    if (forceUpdate || !creatureStats.mDeathAnimationFinished)\n    {\n        updatePosition(forceUpdate);\n        updateAnimFlags(forceUpdate);\n        updateAnimPlay();\n        updateSpeech();\n        updateAttackOrCast();\n    }\n\n    hasSentData = true;\n}\n\nvoid LocalActor::updateCell()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Sending ID_ACTOR_CELL_CHANGE about %s %i-%i in cell %s to server\",\n                       refId.c_str(), refNum, mpNum, cell.getShortDescription().c_str());\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Moved to cell %s\", ptr.getCell()->getCell()->getShortDescription().c_str());\n\n    cell = *ptr.getCell()->getCell();\n    position = ptr.getRefData().getPosition();\n    isFollowerCellChange = false;\n\n    mwmp::Main::get().getNetworking()->getActorList()->addCellChangeActor(*this);\n}\n\nvoid LocalActor::updatePosition(bool forceUpdate)\n{\n    bool posIsChanging = false;\n\n    if (creatureStats.mDead)\n    {\n        ESM::Position ptrPosition = ptr.getRefData().getPosition();\n        posIsChanging = position.pos[0] != ptrPosition.pos[0] || position.pos[1] != ptrPosition.pos[1] ||\n            position.pos[2] != ptrPosition.pos[2];\n    }\n    else\n    {\n        posIsChanging = direction.pos[0] != 0 || direction.pos[1] != 0 || direction.pos[2] != 0 ||\n            direction.rot[0] != 0 || direction.rot[1] != 0 || direction.rot[2] != 0 ||\n            !MWBase::Environment::get().getWorld()->isOnGround(ptr);\n    }\n\n    if (forceUpdate || posIsChanging || posWasChanged)\n    {\n        posWasChanged = posIsChanging;\n        position = ptr.getRefData().getPosition();\n        mwmp::Main::get().getNetworking()->getActorList()->addPositionActor(*this);\n    }\n}\n\nvoid LocalActor::updateAnimFlags(bool forceUpdate)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWMechanics::CreatureStats ptrCreatureStats = ptr.getClass().getCreatureStats(ptr);\n\n    using namespace MWMechanics;\n\n    bool isRunning = ptrCreatureStats.getMovementFlag(CreatureStats::Flag_Run);\n    bool isSneaking = ptrCreatureStats.getMovementFlag(CreatureStats::Flag_Sneak);\n    bool isForceJumping = ptrCreatureStats.getMovementFlag(CreatureStats::Flag_ForceJump);\n    bool isForceMoveJumping = ptrCreatureStats.getMovementFlag(CreatureStats::Flag_ForceMoveJump);\n\n    isFlying = world->isFlying(ptr);\n\n    MWMechanics::DrawState_ currentDrawState = ptr.getClass().getCreatureStats(ptr).getDrawState();\n\n    if (wasRunning != isRunning || wasSneaking != isSneaking ||\n        wasForceJumping != isForceJumping || wasForceMoveJumping != isForceMoveJumping ||\n        lastDrawState != currentDrawState || wasFlying != isFlying ||\n        forceUpdate)\n    {\n\n        wasRunning = isRunning;\n        wasSneaking = isSneaking;\n        wasForceJumping = isForceJumping;\n        wasForceMoveJumping = isForceMoveJumping;\n        lastDrawState = currentDrawState;\n\n        wasFlying = isFlying;\n\n        movementFlags = 0;\n\n#define __SETFLAG(flag, value) (value) ? (movementFlags | flag) : (movementFlags & ~flag)\n\n        movementFlags = __SETFLAG(CreatureStats::Flag_Sneak, isSneaking);\n        movementFlags = __SETFLAG(CreatureStats::Flag_Run, isRunning);\n        movementFlags = __SETFLAG(CreatureStats::Flag_ForceJump, isForceJumping);\n        movementFlags = __SETFLAG(CreatureStats::Flag_ForceMoveJump, isForceMoveJumping);\n\n#undef __SETFLAG\n\n        drawState = currentDrawState;\n\n        mwmp::Main::get().getNetworking()->getActorList()->addAnimFlagsActor(*this);\n    }\n}\n\nvoid LocalActor::updateAnimPlay()\n{\n    if (!animation.groupname.empty())\n    {\n        mwmp::Main::get().getNetworking()->getActorList()->addAnimPlayActor(*this);\n        animation.groupname.clear();\n    }\n}\n\nvoid LocalActor::updateSpeech()\n{\n    if (!sound.empty())\n    {\n        mwmp::Main::get().getNetworking()->getActorList()->addSpeechActor(*this);\n        sound.clear();\n    }\n}\n\nvoid LocalActor::updateStatsDynamic(bool forceUpdate)\n{\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);\n    MWMechanics::DynamicStat<float> health(ptrCreatureStats->getHealth());\n    MWMechanics::DynamicStat<float> magicka(ptrCreatureStats->getMagicka());\n    MWMechanics::DynamicStat<float> fatigue(ptrCreatureStats->getFatigue());\n\n    // Update stats when they become 0 or they have changed enough\n    //\n    // Also check for an oldHealth of 0 changing to something else for resurrected NPCs\n\n    auto needUpdate = [](MWMechanics::DynamicStat<float> &oldVal, MWMechanics::DynamicStat<float> &newVal, int limit) {\n        return oldVal != newVal && (newVal.getCurrent() == 0 || oldVal.getCurrent() == 0\n                                    || abs(oldVal.getCurrent() - newVal.getCurrent()) >= limit);\n    };\n\n    if (forceUpdate || needUpdate(oldHealth, health, 3) || needUpdate(oldMagicka, magicka, 7) ||\n        needUpdate(oldFatigue, fatigue, 7))\n    {\n        oldHealth = health;\n        oldMagicka = magicka;\n        oldFatigue = fatigue;\n\n        health.writeState(creatureStats.mDynamic[0]);\n        magicka.writeState(creatureStats.mDynamic[1]);\n        fatigue.writeState(creatureStats.mDynamic[2]);\n\n        creatureStats.mDead = ptrCreatureStats->isDead();\n        creatureStats.mDeathAnimationFinished = ptrCreatureStats->isDeathAnimationFinished();\n\n        mwmp::Main::get().getNetworking()->getActorList()->addStatsDynamicActor(*this);\n    }\n}\n\nvoid LocalActor::updateEquipment(bool forceUpdate, bool sendImmediately)\n{\n    if (!ptr.getClass().hasInventoryStore(ptr))\n        return;\n\n    MWWorld::InventoryStore &invStore = ptr.getClass().getInventoryStore(ptr);\n    \n    // If we've never sent any data, autoEquip the actor just in case its inventory\n    // slots have been cleared by a previous Container packet\n    if (!hasSentData)\n        invStore.autoEquip(ptr);\n\n    if (forceUpdate)\n        equipmentChanged = true;\n\n    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)\n    {\n        auto &item = equipmentItems[slot];\n        MWWorld::ContainerStoreIterator it = invStore.getSlot(slot);\n\n        if (it != invStore.end())\n        {\n            auto &cellRef = it->getCellRef();\n            if (!::Misc::StringUtils::ciEqual(cellRef.getRefId(), item.refId))\n            {\n                equipmentChanged = true;\n\n                item.refId = cellRef.getRefId();\n                item.charge = cellRef.getCharge();\n                item.enchantmentCharge = it->getCellRef().getEnchantmentCharge();\n                item.count = it->getRefData().getCount();\n            }\n        }\n        else if (!item.refId.empty())\n        {\n            equipmentChanged = true;\n            item.refId = \"\";\n            item.count = 0;\n            item.charge = -1;\n            item.enchantmentCharge = -1;\n        }\n    }\n\n    if (equipmentChanged)\n    {\n        if (sendImmediately)\n            sendEquipment();\n        else\n            mwmp::Main::get().getNetworking()->getActorList()->addEquipmentActor(*this);\n\n        equipmentChanged = false;\n    }\n}\n\nvoid LocalActor::updateAttackOrCast()\n{\n    if (attack.shouldSend)\n    {\n        mwmp::Main::get().getNetworking()->getActorList()->addAttackActor(*this);\n        attack.shouldSend = false;\n    }\n    else if (cast.shouldSend)\n    {\n        mwmp::Main::get().getNetworking()->getActorList()->addCastActor(*this);\n        cast.shouldSend = false;\n        cast.hasProjectile = false;\n    }\n}\n\nvoid LocalActor::sendEquipment()\n{\n    ActorList actorList;\n    actorList.cell = cell;\n    actorList.addActor(*this);\n    Main::get().getNetworking()->getActorPacket(ID_ACTOR_EQUIPMENT)->setActorList(&actorList);\n    Main::get().getNetworking()->getActorPacket(ID_ACTOR_EQUIPMENT)->Send();\n}\n\nvoid LocalActor::sendSpellsActiveAddition(const std::string id, bool isStackingSpell, const MWMechanics::ActiveSpells::ActiveSpellParams& params)\n{\n    // Skip any bugged spells that somehow have clientside-only dynamic IDs\n    if (id.find(\"$dynamic\") != std::string::npos)\n        return;\n\n    spellsActiveChanges.activeSpells.clear();\n\n    const MWWorld::Ptr& caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(params.mCasterActorId);\n\n    mwmp::ActiveSpell spell;\n    spell.id = id;\n    spell.isStackingSpell = isStackingSpell;\n    spell.caster = MechanicsHelper::getTarget(caster);\n    spell.timestampDay = params.mTimeStamp.getDay();\n    spell.timestampHour = params.mTimeStamp.getHour();\n    spell.params.mEffects = params.mEffects;\n    spell.params.mDisplayName = params.mDisplayName;\n    spellsActiveChanges.activeSpells.push_back(spell);\n\n    spellsActiveChanges.action = mwmp::SpellsActiveChanges::ADD;\n\n    ActorList actorList;\n    actorList.cell = cell;\n    actorList.addActor(*this);\n    Main::get().getNetworking()->getActorPacket(ID_ACTOR_SPELLS_ACTIVE)->setActorList(&actorList);\n    Main::get().getNetworking()->getActorPacket(ID_ACTOR_SPELLS_ACTIVE)->Send();\n}\n\nvoid LocalActor::sendSpellsActiveRemoval(const std::string id, bool isStackingSpell, MWWorld::TimeStamp timestamp)\n{\n    // Skip any bugged spells that somehow have clientside-only dynamic IDs\n    if (id.find(\"$dynamic\") != std::string::npos)\n        return;\n\n    spellsActiveChanges.activeSpells.clear();\n\n    mwmp::ActiveSpell spell;\n    spell.id = id;\n    spell.isStackingSpell = isStackingSpell;\n    spell.timestampDay = timestamp.getDay();\n    spell.timestampHour = timestamp.getHour();\n    spellsActiveChanges.activeSpells.push_back(spell);\n\n    spellsActiveChanges.action = mwmp::SpellsActiveChanges::REMOVE;\n\n    ActorList actorList;\n    actorList.cell = cell;\n    actorList.addActor(*this);\n    Main::get().getNetworking()->getActorPacket(ID_ACTOR_SPELLS_ACTIVE)->setActorList(&actorList);\n    Main::get().getNetworking()->getActorPacket(ID_ACTOR_SPELLS_ACTIVE)->Send();\n}\n\nvoid LocalActor::sendDeath(char newDeathState)\n{\n    deathState = newDeathState;\n\n    if (MechanicsHelper::isEmptyTarget(killer))\n        killer = MechanicsHelper::getTarget(ptr);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_ACTOR_DEATH about %s %i-%i in cell %s to server\\n- deathState: %d\",\n        refId.c_str(), refNum, mpNum, cell.getShortDescription().c_str(), deathState);\n\n    ActorList actorList;\n    actorList.cell = cell;\n    actorList.addActor(*this);\n    Main::get().getNetworking()->getActorPacket(ID_ACTOR_DEATH)->setActorList(&actorList);\n    Main::get().getNetworking()->getActorPacket(ID_ACTOR_DEATH)->Send();\n\n    MechanicsHelper::clearTarget(killer);\n}\n\nMWWorld::Ptr LocalActor::getPtr()\n{\n    return ptr;\n}\n\nvoid LocalActor::setPtr(const MWWorld::Ptr& newPtr)\n{\n    ptr = newPtr;\n\n    refId = ptr.getCellRef().getRefId();\n    refNum = ptr.getCellRef().getRefNum().mIndex;\n    mpNum = ptr.getCellRef().getMpNum();\n\n    lastDrawState = ptr.getClass().getCreatureStats(ptr).getDrawState();\n    oldHealth = ptr.getClass().getCreatureStats(ptr).getHealth();\n    oldMagicka = ptr.getClass().getCreatureStats(ptr).getMagicka();\n    oldFatigue = ptr.getClass().getCreatureStats(ptr).getFatigue();\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/LocalActor.hpp",
    "content": "#ifndef OPENMW_LOCALACTOR_HPP\n#define OPENMW_LOCALACTOR_HPP\n\n#include <components/openmw-mp/Base/BaseActor.hpp>\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/activespells.hpp\"\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/timestamp.hpp\"\n\nnamespace mwmp\n{\n    class LocalActor : public BaseActor\n    {\n    public:\n\n        LocalActor();\n        virtual ~LocalActor();\n\n        void update(bool forceUpdate);\n\n        void updateCell();\n        void updatePosition(bool forceUpdate);\n        void updateAnimFlags(bool forceUpdate);\n        void updateAnimPlay();\n        void updateSpeech();\n        void updateStatsDynamic(bool forceUpdate);\n        void updateEquipment(bool forceUpdate, bool sendImmediately = false);\n        void updateAttackOrCast();\n\n        void sendEquipment();\n        void sendSpellsActiveAddition(const std::string id, bool isStackingSpell, const MWMechanics::ActiveSpells::ActiveSpellParams& params);\n        void sendSpellsActiveRemoval(const std::string id, bool isStackingSpell, MWWorld::TimeStamp timestamp);\n        void sendDeath(char newDeathState);\n\n        MWWorld::Ptr getPtr();\n        void setPtr(const MWWorld::Ptr& newPtr);\n\n        bool hasSentData;\n\n    private:\n        MWWorld::Ptr ptr;\n\n        bool posWasChanged;\n        bool equipmentChanged;\n\n        bool wasRunning;\n        bool wasSneaking;\n        bool wasForceJumping;\n        bool wasForceMoveJumping;\n\n        bool wasJumping;\n        bool wasFlying;\n\n        MWMechanics::DrawState_ lastDrawState;\n\n        MWMechanics::DynamicStat<float> oldHealth;\n        MWMechanics::DynamicStat<float> oldMagicka;\n        MWMechanics::DynamicStat<float> oldFatigue;\n    };\n}\n\n#endif //OPENMW_LOCALACTOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/LocalPlayer.cpp",
    "content": "#include <components/esm/esmwriter.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Utils.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/journal.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwclass/creature.hpp\"\n#include \"../mwclass/npc.hpp\"\n\n#include \"../mwdialogue/dialoguemanagerimp.hpp\"\n\n#include \"../mwgui/inventorywindow.hpp\"\n#include \"../mwgui/windowmanagerimp.hpp\"\n\n#include \"../mwinput/inputmanagerimp.hpp\"\n\n#include \"../mwmechanics/activespells.hpp\"\n#include \"../mwmechanics/aitravel.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/mechanicsmanagerimp.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n#include \"../mwmechanics/spellutil.hpp\"\n\n#include \"../mwscript/scriptmanagerimp.hpp\"\n\n#include \"../mwstate/statemanagerimp.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/customdata.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"LocalPlayer.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"PlayerList.hpp\"\n#include \"CellController.hpp\"\n#include \"GUIController.hpp\"\n#include \"MechanicsHelper.hpp\"\n\nusing namespace mwmp;\n\nstd::map<std::string, int> storedItemRemovals;\n\nLocalPlayer::LocalPlayer()\n{\n    deathTime = time(0);\n    receivedCharacter = false;\n\n    charGenState.currentStage = 0;\n    charGenState.endStage = 1;\n    charGenState.isFinished = false;\n\n    ignorePosPacket = false;\n    ignoreJailTeleportation = false;\n    ignoreJailSkillIncreases = false;\n    \n    attack.shouldSend = false;\n    attack.instant = false;\n    attack.pressed = false;\n\n    cast.shouldSend = false;\n    cast.instant = false;\n    cast.pressed = false;\n\n    killer.isPlayer = false;\n    killer.refId = \"\";\n    killer.name = \"\";\n\n    isChangingRegion = false;\n\n    jailProgressText = \"\";\n    jailEndText = \"\";\n\n    isUsingBed = false;\n    avoidSendingInventoryPackets = false;\n    isReceivingQuickKeys = false;\n    isPlayingAnimation = false;\n    diedSinceArrestAttempt = false;\n}\n\nLocalPlayer::~LocalPlayer()\n{\n\n}\n\nNetworking *LocalPlayer::getNetworking()\n{\n    return mwmp::Main::get().getNetworking();\n}\n\nMWWorld::Ptr LocalPlayer::getPlayerPtr()\n{\n    return MWBase::Environment::get().getWorld()->getPlayerPtr();\n}\n\nvoid LocalPlayer::update()\n{\n    static float updateTimer = 0;\n    const float timeoutSec = 0.015;\n\n    if ((updateTimer += MWBase::Environment::get().getFrameDuration()) >= timeoutSec)\n    {\n        updateTimer = 0;\n        updateCell();\n        updatePosition();\n        updateAnimFlags();\n        updateAttackOrCast();\n        updateEquipment();\n        updateStatsDynamic();\n        updateAttributes();\n        updateSkills();\n        updateLevel();\n        updateBounty();\n        updateReputation();\n    }\n}\n\nbool LocalPlayer::processCharGen()\n{\n    MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();\n\n    // If we haven't finished CharGen and we're in a menu, it must be\n    // one of the CharGen menus, so go no further until it's closed\n    if (windowManager->isGuiMode() && !charGenState.isFinished)\n    {\n        return false;\n    }\n\n    // If the current stage of CharGen is not the last one,\n    // move to the next one\n    else if (charGenState.currentStage < charGenState.endStage)\n    {\n        switch (charGenState.currentStage)\n        {\n        case 0:\n            windowManager->pushGuiMode(MWGui::GM_Name);\n            break;\n        case 1:\n            windowManager->pushGuiMode(MWGui::GM_Race);\n            break;\n        case 2:\n            windowManager->pushGuiMode(MWGui::GM_Class);\n            break;\n        case 3:\n            windowManager->pushGuiMode(MWGui::GM_Birth);\n            break;\n        default:\n            windowManager->pushGuiMode(MWGui::GM_Review);\n            break;\n        }\n        getNetworking()->getPlayerPacket(ID_PLAYER_CHARGEN)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_CHARGEN)->Send();\n\n        return false;\n    }\n\n    // If we've reached the last stage of CharGen, send the\n    // corresponding packets and mark CharGen as finished\n    else if (!charGenState.isFinished)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n        npc = *ptrPlayer.get<ESM::NPC>()->mBase;\n        birthsign = world->getPlayer().getBirthSign();\n\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_BASEINFO to server with my CharGen info\");\n        getNetworking()->getPlayerPacket(ID_PLAYER_BASEINFO)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_BASEINFO)->Send();\n\n        // Send stats packets if this is the 2nd round of CharGen that\n        // only happens for new characters\n        if (charGenState.endStage != 1)\n        {\n            updateStatsDynamic(true);\n            updateAttributes(true);\n            updateSkills(true);\n            updateLevel(true);\n            sendClass();\n            sendSpellbook();\n            getNetworking()->getPlayerPacket(ID_PLAYER_CHARGEN)->setPlayer(this);\n            getNetworking()->getPlayerPacket(ID_PLAYER_CHARGEN)->Send();\n        }\n\n        // Mark character generation as finished until overridden by a new ID_PLAYER_CHARGEN packet\n        charGenState.isFinished = true;\n    }\n\n    return true;\n}\n\nbool LocalPlayer::isLoggedIn()\n{\n    if (charGenState.isFinished && (charGenState.endStage > 1 || receivedCharacter))\n        return true;\n\n    return false;\n}\n\nvoid LocalPlayer::updateStatsDynamic(bool forceUpdate)\n{\n    if (statsDynamicIndexChanges.size() > 0)\n        statsDynamicIndexChanges.clear();\n\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer);\n    MWMechanics::DynamicStat<float> health(ptrCreatureStats->getHealth());\n    MWMechanics::DynamicStat<float> magicka(ptrCreatureStats->getMagicka());\n    MWMechanics::DynamicStat<float> fatigue(ptrCreatureStats->getFatigue());\n\n    static MWMechanics::DynamicStat<float> oldHealth(ptrCreatureStats->getHealth());\n    static MWMechanics::DynamicStat<float> oldMagicka(ptrCreatureStats->getMagicka());\n    static MWMechanics::DynamicStat<float> oldFatigue(ptrCreatureStats->getFatigue());\n\n\n    // Update stats when they become 0 or they have changed enough\n    auto needUpdate = [](MWMechanics::DynamicStat<float> &oldVal, MWMechanics::DynamicStat<float> &newVal, int limit) {\n        return oldVal != newVal && (newVal.getCurrent() == 0 || oldVal.getCurrent() == 0\n                                    || abs(oldVal.getCurrent() - newVal.getCurrent()) >= limit);\n    };\n\n    if (forceUpdate || needUpdate(oldHealth, health, 2))\n        statsDynamicIndexChanges.push_back(0);\n\n    if (forceUpdate || needUpdate(oldMagicka, magicka, 4))\n        statsDynamicIndexChanges.push_back(1);\n\n    if (forceUpdate || needUpdate(oldFatigue, fatigue, 4))\n        statsDynamicIndexChanges.push_back(2);\n\n    if (forceUpdate || statsDynamicIndexChanges.size() > 0)\n    {\n        oldHealth = health;\n        oldMagicka = magicka;\n        oldFatigue = fatigue;\n\n        health.writeState(creatureStats.mDynamic[0]);\n        magicka.writeState(creatureStats.mDynamic[1]);\n        fatigue.writeState(creatureStats.mDynamic[2]);\n\n        creatureStats.mDead = ptrCreatureStats->isDead();\n\n        exchangeFullInfo = false;\n        getNetworking()->getPlayerPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_STATS_DYNAMIC)->Send();\n    }\n}\n\nvoid LocalPlayer::updateAttributes(bool forceUpdate)\n{\n    // Only send attributes if we are not a werewolf, or they will be\n    // overwritten by the werewolf ones\n    if (isWerewolf) return;\n\n    if (attributeIndexChanges.size() > 0)\n        attributeIndexChanges.clear();\n\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    const MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);\n\n    for (int i = 0; i < 8; ++i)\n    {\n        if (ptrNpcStats.getAttribute(i).getBase() != creatureStats.mAttributes[i].mBase ||\n            ptrNpcStats.getAttribute(i).getModifier() != creatureStats.mAttributes[i].mMod ||\n            ptrNpcStats.getAttribute(i).getDamage() != creatureStats.mAttributes[i].mDamage ||\n            ptrNpcStats.getSkillIncrease(i) != npcStats.mSkillIncrease[i] ||\n            forceUpdate)\n        {\n            attributeIndexChanges.push_back(i);\n            ptrNpcStats.getAttribute(i).writeState(creatureStats.mAttributes[i]);\n            npcStats.mSkillIncrease[i] = ptrNpcStats.getSkillIncrease(i);\n        }\n    }\n\n    if (attributeIndexChanges.size() > 0)\n    {\n        exchangeFullInfo = false;\n        getNetworking()->getPlayerPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_ATTRIBUTE)->Send();\n    }\n}\n\nvoid LocalPlayer::updateSkills(bool forceUpdate)\n{\n    // Only send skills if we are not a werewolf, or they will be\n    // overwritten by the werewolf ones\n    if (isWerewolf) return;\n\n    if (skillIndexChanges.size() > 0)\n        skillIndexChanges.clear();\n\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    const MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);\n\n    for (int i = 0; i < 27; ++i)\n    {\n        // Update a skill if its base value has changed at all or its progress has changed enough\n        if (ptrNpcStats.getSkill(i).getBase() != npcStats.mSkills[i].mBase ||\n            ptrNpcStats.getSkill(i).getModifier() != npcStats.mSkills[i].mMod ||\n            ptrNpcStats.getSkill(i).getDamage() != npcStats.mSkills[i].mDamage ||\n            abs(ptrNpcStats.getSkill(i).getProgress() - npcStats.mSkills[i].mProgress) > 0.75 ||\n            forceUpdate)\n        {\n            skillIndexChanges.push_back(i);\n            ptrNpcStats.getSkill(i).writeState(npcStats.mSkills[i]);\n        }\n    }\n\n    if (skillIndexChanges.size() > 0)\n    {\n        exchangeFullInfo = false;\n        getNetworking()->getPlayerPacket(ID_PLAYER_SKILL)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_SKILL)->Send();\n    }\n}\n\nvoid LocalPlayer::updateLevel(bool forceUpdate)\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    const MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);\n\n    if (ptrNpcStats.getLevel() != creatureStats.mLevel ||\n        ptrNpcStats.getLevelProgress() != npcStats.mLevelProgress ||\n        forceUpdate)\n    {\n        creatureStats.mLevel = ptrNpcStats.getLevel();\n        npcStats.mLevelProgress = ptrNpcStats.getLevelProgress();\n        getNetworking()->getPlayerPacket(ID_PLAYER_LEVEL)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_LEVEL)->Send();\n    }\n}\n\nvoid LocalPlayer::updateBounty(bool forceUpdate)\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    const MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);\n\n    if (ptrNpcStats.getBounty() != npcStats.mBounty || forceUpdate)\n    {\n        npcStats.mBounty = ptrNpcStats.getBounty();\n        getNetworking()->getPlayerPacket(ID_PLAYER_BOUNTY)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_BOUNTY)->Send();\n    }\n}\n\nvoid LocalPlayer::updateReputation(bool forceUpdate)\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    const MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);\n\n    if (ptrNpcStats.getReputation() != npcStats.mReputation || forceUpdate)\n    {\n        npcStats.mReputation = ptrNpcStats.getReputation();\n        getNetworking()->getPlayerPacket(ID_PLAYER_REPUTATION)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_REPUTATION)->Send();\n    }\n}\n\nvoid LocalPlayer::updatePosition(bool forceUpdate)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n\n    static bool posWasChanged = false;\n    static bool isJumping = false;\n    static bool sentJumpEnd = true;\n    static float oldRot[2] = {0};\n\n    position = ptrPlayer.getRefData().getPosition();\n\n    bool posIsChanging = (direction.pos[0] != 0 || direction.pos[1] != 0 ||\n        direction.rot[0] != 0 || direction.rot[1] != 0 || direction.rot[2] != 0);\n\n    // Animations can change a player's position without actually creating directional movement,\n    // so update positions accordingly\n    if (!posIsChanging && isPlayingAnimation)\n    {\n        if (MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(ptrPlayer, animation.groupname))\n            posIsChanging = true;\n        else\n            isPlayingAnimation = false;\n    }\n\n    if (forceUpdate || posIsChanging || posWasChanged)\n    {\n        oldRot[0] = position.rot[0];\n        oldRot[1] = position.rot[2];\n\n        posWasChanged = posIsChanging;\n\n        if (!isJumping && !world->isOnGround(ptrPlayer) && !world->isFlying(ptrPlayer))\n            isJumping = true;\n\n        getNetworking()->getPlayerPacket(ID_PLAYER_POSITION)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_POSITION)->Send();\n    }\n    else if (isJumping && world->isOnGround(ptrPlayer))\n    {\n        isJumping = false;\n        sentJumpEnd = false;\n    }\n    // Packet with jump end position has to be sent one tick after above check\n    else if (!sentJumpEnd)\n    {\n        sentJumpEnd = true;\n        position = ptrPlayer.getRefData().getPosition();\n        getNetworking()->getPlayerPacket(ID_PLAYER_POSITION)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_POSITION)->Send();\n    }\n}\n\nvoid LocalPlayer::updateCell(bool forceUpdate)\n{\n    const ESM::Cell *ptrCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->getCell();\n\n    // If the LocalPlayer's Ptr cell is different from the LocalPlayer's packet cell, proceed\n    if (forceUpdate || !Main::get().getCellController()->isSameCell(*ptrCell, cell))\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_CELL_CHANGE about LocalPlayer to server\");\n\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Moved from %s to %s\", cell.getShortDescription().c_str(),\n                   ptrCell->getShortDescription().c_str());\n\n        if (!Misc::StringUtils::ciEqual(cell.mRegion, ptrCell->mRegion))\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Changed region from %s to %s\",\n                cell.mRegion.empty() ? \"none\" : cell.mRegion.c_str(),\n                ptrCell->mRegion.empty() ? \"none\" : ptrCell->mRegion.c_str());\n\n            isChangingRegion = true;\n        }\n\n        cell = *ptrCell;\n        previousCellPosition = position;\n\n        // Make sure the position is updated before a cell packet is sent, or else\n        // cell change events in server scripts will have the wrong player position\n        updatePosition(true);\n\n        getNetworking()->getPlayerPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_CELL_CHANGE)->Send();\n\n        isChangingRegion = false;\n\n        // If this is an interior cell, are there any other players in it? If so,\n        // enable their markers\n        if (!ptrCell->isExterior())\n        {\n            mwmp::PlayerList::enableMarkers(*ptrCell);\n        }\n    }\n}\n\nvoid LocalPlayer::updateEquipment(bool forceUpdate)\n{\n    if (equipmentIndexChanges.size() > 0)\n        equipmentIndexChanges.clear();\n\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    MWWorld::InventoryStore &invStore = ptrPlayer.getClass().getInventoryStore(ptrPlayer);\n    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)\n    {\n        auto &item = equipmentItems[slot];\n        MWWorld::ContainerStoreIterator it = invStore.getSlot(slot);\n\n        if (it != invStore.end())\n        {\n            MWWorld::CellRef &cellRef = it->getCellRef();\n\n            if (Misc::StringUtils::ciEqual(cellRef.getRefId(), item.refId) == false ||\n                cellRef.getCharge() != item.charge ||\n                Utils::compareFloats(cellRef.getEnchantmentCharge(), item.enchantmentCharge, 1.0f) == false ||\n                it->getRefData().getCount() != item.count ||\n                forceUpdate)\n            {\n                equipmentIndexChanges.push_back(slot);\n\n                item.refId = it->getCellRef().getRefId();\n                item.count = it->getRefData().getCount();\n                item.charge = it->getCellRef().getCharge();\n                item.enchantmentCharge = it->getCellRef().getEnchantmentCharge();\n            }\n        }\n        else if (!item.refId.empty())\n        {\n            equipmentIndexChanges.push_back(slot);\n            item.refId = \"\";\n            item.count = 0;\n            item.charge = -1;\n            item.enchantmentCharge = -1;\n        }\n    }\n\n    if (equipmentIndexChanges.size() > 0)\n    {\n        exchangeFullInfo = false;\n        getNetworking()->getPlayerPacket(ID_PLAYER_EQUIPMENT)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_EQUIPMENT)->Send();\n    }\n}\n\nvoid LocalPlayer::updateInventory(bool forceUpdate)\n{\n    static bool invChanged = false;\n\n    if (forceUpdate)\n        invChanged = true;\n\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWWorld::InventoryStore &ptrInventory = ptrPlayer.getClass().getInventoryStore(ptrPlayer);\n    mwmp::Item item;\n\n    auto setItem = [](Item &item, const MWWorld::Ptr &iter) {\n        item.refId = iter.getCellRef().getRefId();\n        if (item.refId.find(\"$dynamic\") != std::string::npos)\n            return true;\n        item.count = iter.getRefData().getCount();\n        item.charge = iter.getCellRef().getCharge();\n        item.enchantmentCharge = iter.getCellRef().getEnchantmentCharge();\n        item.soul = iter.getCellRef().getSoul();\n\n        return false;\n    };\n\n    if (!invChanged)\n    {\n        for (const auto &itemOld : inventoryChanges.items)\n        {\n            auto result = ptrInventory.begin();\n            for (; result != ptrInventory.end(); ++result)\n            {\n                if(setItem(item, *result))\n                    continue;\n\n                if (item == itemOld)\n                    break;\n            }\n            if (result == ptrInventory.end())\n            {\n                invChanged = true;\n                break;\n            }\n        }\n    }\n\n    if (!invChanged)\n    {\n        for (const auto &iter : ptrInventory)\n        {\n            if(setItem(item, iter))\n                continue;\n\n            auto items = inventoryChanges.items;\n\n            if (find(items.begin(), items.end(), item) == items.end())\n            {\n                invChanged = true;\n                break;\n            }\n        }\n    }\n\n    if (!invChanged)\n        return;\n\n    invChanged = false;\n\n    sendInventory();\n}\n\nvoid LocalPlayer::updateAttackOrCast()\n{\n    if (attack.shouldSend)\n    {\n        getNetworking()->getPlayerPacket(ID_PLAYER_ATTACK)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_ATTACK)->Send();\n\n        attack.shouldSend = false;\n    }\n    else if (cast.shouldSend)\n    {\n        getNetworking()->getPlayerPacket(ID_PLAYER_CAST)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_CAST)->Send();\n\n        cast.shouldSend = false;\n        cast.hasProjectile = false;\n    }\n}\n\nvoid LocalPlayer::updateAnimFlags(bool forceUpdate)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n\n    MWMechanics::NpcStats ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);\n    using namespace MWMechanics;\n\n    static bool wasRunning = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Run);\n    static bool wasSneaking = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Sneak);\n    static bool wasForceJumping = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceJump);\n    static bool wasForceMoveJumping = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceMoveJump);\n\n    bool isRunning = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Run);\n    bool isSneaking = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Sneak);\n    bool isForceJumping = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceJump);\n    bool isForceMoveJumping = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceMoveJump);\n    \n    isFlying = world->isFlying(ptrPlayer);\n    isJumping = !world->isOnGround(ptrPlayer) && !isFlying;\n\n    // We need to send a new packet at the end of jumping, flying and TCL-ing too,\n    // so keep track of what we were doing last frame\n    static bool wasJumping = false;\n    static bool wasFlying = false;\n    static bool hadTcl = false;\n\n    drawState = ptrPlayer.getClass().getNpcStats(ptrPlayer).getDrawState();\n    static char lastDrawState = ptrPlayer.getClass().getNpcStats(ptrPlayer).getDrawState();\n\n    if (wasRunning != isRunning ||\n        wasSneaking != isSneaking || wasForceJumping != isForceJumping ||\n        wasForceMoveJumping != isForceMoveJumping || lastDrawState != drawState ||\n        wasJumping || isJumping || wasFlying != isFlying || hadTcl != hasTcl ||\n        forceUpdate)\n    {\n        wasSneaking = isSneaking;\n        wasRunning = isRunning;\n        wasForceJumping = isForceJumping;\n        wasForceMoveJumping = isForceMoveJumping;\n        lastDrawState = drawState;\n        \n        wasJumping = isJumping;\n        wasFlying = isFlying;\n        hadTcl = hasTcl;\n\n        movementFlags = 0;\n\n#define __SETFLAG(flag, value) (value) ? (movementFlags | flag) : (movementFlags & ~flag)\n\n        movementFlags = __SETFLAG(CreatureStats::Flag_Sneak, isSneaking);\n        movementFlags = __SETFLAG(CreatureStats::Flag_Run, isRunning);\n        movementFlags = __SETFLAG(CreatureStats::Flag_ForceJump, isForceJumping);\n        movementFlags = __SETFLAG(CreatureStats::Flag_ForceJump, isJumping);\n        movementFlags = __SETFLAG(CreatureStats::Flag_ForceMoveJump, isForceMoveJumping);\n\n#undef __SETFLAG\n\n        if (isJumping)\n            updatePosition(true); // fix position after jump;\n\n        getNetworking()->getPlayerPacket(ID_PLAYER_ANIM_FLAGS)->setPlayer(this);\n        getNetworking()->getPlayerPacket(ID_PLAYER_ANIM_FLAGS)->Send();\n    }\n}\n\nvoid LocalPlayer::addItems()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore();\n    MWWorld::ContainerStore &ptrStore = ptrPlayer.getClass().getContainerStore(ptrPlayer);\n\n    for (const auto &item : inventoryChanges.items)\n    {\n        // Skip bound items\n        if (MWBase::Environment::get().getMechanicsManager()->isBoundItem(item.refId))\n            continue;\n\n        try\n        {\n            MWWorld::ManualRef itemRef(esmStore, item.refId, item.count);\n            MWWorld::Ptr itemPtr = itemRef.getPtr();\n\n            if (item.charge != -1)\n                itemPtr.getCellRef().setCharge(item.charge);\n\n            if (item.enchantmentCharge != -1)\n                itemPtr.getCellRef().setEnchantmentCharge(item.enchantmentCharge);\n\n            if (!item.soul.empty())\n                itemPtr.getCellRef().setSoul(item.soul);\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Adding inventory item %s with count %i\", item.refId.c_str(), item.count);\n\n            ptrStore.add(itemPtr, item.count, ptrPlayer);\n        }\n        catch (std::exception&)\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Ignored addition of invalid inventory item %s\", item.refId.c_str());\n        }\n    }\n\n    updateInventoryWindow();\n}\n\nvoid LocalPlayer::addSpells()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::Spells &ptrSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getSpells();\n\n    for (const auto &spell : spellbookChanges.spells)\n        // Only add spells that are ensured to exist\n        if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spell.mId))\n            ptrSpells.add(spell.mId);\n        else\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Ignored addition of invalid spell %s\", spell.mId.c_str());\n}\n\nvoid LocalPlayer::addSpellsActive()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::ActiveSpells& activeSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getActiveSpells();\n\n    for (const auto& activeSpell : spellsActiveChanges.activeSpells)\n    {\n        MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay);\n        int casterActorId = MechanicsHelper::getActorId(activeSpell.caster);\n\n        // Don't do a check for a spell's existence, because active effects from potions need to be applied here too\n        activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, casterActorId, timestamp, false);\n    }\n}\n\nvoid LocalPlayer::addJournalItems()\n{\n    for (const auto &journalItem : journalChanges)\n    {\n        MWWorld::Ptr ptrFound;\n\n        if (journalItem.type == JournalItem::ENTRY)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"- type: ENTRY, quest: %s, index: %i, actorRefId: %s\",\n                journalItem.quest.c_str(), journalItem.index, journalItem.actorRefId.c_str());\n\n            ptrFound = MWBase::Environment::get().getWorld()->searchPtr(journalItem.actorRefId, false);\n\n            if (!ptrFound)\n                ptrFound = getPlayerPtr();\n        }\n        else\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"- type: INDEX, quest: %s, index: %i\",\n                journalItem.quest.c_str(), journalItem.index);\n        }\n\n        try\n        {\n            if (journalItem.type == JournalItem::ENTRY)\n            {\n                if (journalItem.hasTimestamp)\n                {\n                    MWBase::Environment::get().getJournal()->addEntry(journalItem.quest, journalItem.index, ptrFound,\n                        journalItem.timestamp.daysPassed, journalItem.timestamp.month, journalItem.timestamp.day);\n                }\n                else\n                {\n                    MWBase::Environment::get().getJournal()->addEntry(journalItem.quest, journalItem.index, ptrFound);\n                }\n            }\n            else\n                MWBase::Environment::get().getJournal()->setJournalIndex(journalItem.quest, journalItem.index);\n        }\n        catch (std::exception&)\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Ignored addition of invalid journal quest %s\", journalItem.quest.c_str());\n        }\n    }\n}\n\nvoid LocalPlayer::addTopics()\n{\n    auto &env = MWBase::Environment::get();\n    for (const auto &topic : topicChanges)\n    {\n        std::string topicId = topic.topicId;\n\n        // If we're using a translated version of Morrowind, translate this topic from English into our language\n        if (env.getWindowManager()->getTranslationDataStorage().hasTranslation())\n            topicId = env.getWindowManager()->getTranslationDataStorage().getLocalizedTopicId(topicId);\n\n        env.getDialogueManager()->addTopic(topicId);\n\n        if (env.getWindowManager()->containsMode(MWGui::GM_Dialogue))\n            env.getDialogueManager()->updateActorKnownTopics();\n    }\n}\n\nvoid LocalPlayer::removeItems()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWWorld::ContainerStore &ptrStore = ptrPlayer.getClass().getContainerStore(ptrPlayer);\n\n    for (const auto &item : inventoryChanges.items)\n    {\n        ptrStore.remove(item.refId, item.count, ptrPlayer);\n\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Removing inventory item %s with count %i\", item.refId.c_str(), item.count);\n    }\n}\n\nvoid LocalPlayer::removeSpells()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::Spells &ptrSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getSpells();\n\n    MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();\n    for (const auto &spell : spellbookChanges.spells)\n    {\n        ptrSpells.remove(spell.mId);\n        if (spell.mId == wm->getSelectedSpell())\n            wm->unsetSelectedSpell();\n    }\n}\n\nvoid LocalPlayer::removeSpellsActive()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::ActiveSpells& activeSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getActiveSpells();\n \n    for (const auto& activeSpell : spellsActiveChanges.activeSpells)\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- removing %sstacking active spell %s\", activeSpell.isStackingSpell ? \"\" : \"non-\", activeSpell.id.c_str());\n\n        // Remove stacking spells based on their timestamps\n        if (activeSpell.isStackingSpell)\n        {\n            MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay);\n            bool foundSpell = activeSpells.removeSpellByTimestamp(activeSpell.id, timestamp);\n\n            if (!foundSpell)\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"-- spell with this ID and timestamp could not be found!\");\n            }\n        }\n        else\n        {\n            activeSpells.removeEffects(activeSpell.id);\n        }\n    }\n}\n\nvoid LocalPlayer::die()\n{\n    creatureStats.mDead = true;\n\n    MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr();\n    MWMechanics::DynamicStat<float> health = playerPtr.getClass().getCreatureStats(playerPtr).getHealth();\n    health.setCurrent(0);\n    playerPtr.getClass().getCreatureStats(playerPtr).setHealth(health);\n\n    Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_DEATH)->setPlayer(this);\n    Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_DEATH)->Send();\n}\n\nvoid LocalPlayer::resurrect()\n{\n    creatureStats.mDead = false;\n\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    if (resurrectType == mwmp::RESURRECT_TYPE::IMPERIAL_SHRINE)\n        MWBase::Environment::get().getWorld()->teleportToClosestMarker(ptrPlayer, \"divinemarker\");\n    else if (resurrectType == mwmp::RESURRECT_TYPE::TRIBUNAL_TEMPLE)\n        MWBase::Environment::get().getWorld()->teleportToClosestMarker(ptrPlayer, \"templemarker\");\n\n    MWBase::Environment::get().getMechanicsManager()->resurrect(ptrPlayer);\n\n    // The player could have died from a hand-to-hand attack, so reset their fatigue\n    // as well\n    if (creatureStats.mDynamic[2].mMod < 1)\n        creatureStats.mDynamic[2].mMod = 1;\n\n    creatureStats.mDynamic[2].mCurrent = creatureStats.mDynamic[2].mMod;\n    MWMechanics::DynamicStat<float> fatigue;\n    fatigue.readState(creatureStats.mDynamic[2]);\n    ptrPlayer.getClass().getCreatureStats(ptrPlayer).setFatigue(fatigue);\n\n    // If this player had a weapon or spell readied when dying, they will still have it\n    // readied but be unable to use it unless we clear it here\n    ptrPlayer.getClass().getNpcStats(ptrPlayer).setDrawState(MWMechanics::DrawState_Nothing);\n\n    // Record that the player has died since the last attempt was made to arrest them,\n    // used to make guards lenient enough to attempt an arrest again\n    diedSinceArrestAttempt = true;\n\n    deathTime = time(0);\n\n    LOG_APPEND(TimedLog::LOG_INFO, \"- diedSinceArrestAttempt is now true\");\n\n    // Record that we are no longer a known werewolf, to avoid being attacked infinitely\n    MWBase::Environment::get().getWorld()->setGlobalInt(\"pcknownwerewolf\", 0);\n\n    // Ensure we unequip any items with constant effects that can put us into an infinite\n    // death loop\n    static const int damageEffects[5] = { ESM::MagicEffect::DrainHealth, ESM::MagicEffect::FireDamage,\n        ESM::MagicEffect::FrostDamage, ESM::MagicEffect::ShockDamage, ESM::MagicEffect::SunDamage };\n\n    for (const auto &damageEffect : damageEffects)\n        MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, damageEffect);\n\n    Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_RESURRECT)->setPlayer(this);\n    Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_RESURRECT)->Send();\n\n    updateStatsDynamic(true);\n}\n\nvoid LocalPlayer::closeInventoryWindows()\n{\n    if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container) ||\n        MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Inventory))\n        MWBase::Environment::get().getWindowManager()->popGuiMode();\n\n    MWBase::Environment::get().getWindowManager()->finishDragDrop();\n}\n\nvoid LocalPlayer::updateInventoryWindow()\n{\n    MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();\n}\n\nvoid LocalPlayer::setCharacter()\n{\n    receivedCharacter = true;\n\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    // Ignore invalid races\n    if (world->getStore().get<ESM::Race>().search(npc.mRace) != 0)\n    {\n        MWBase::Environment::get().getWorld()->getPlayer().setBirthSign(birthsign);\n\n        if (resetStats)\n        {\n            MWBase::Environment::get().getMechanicsManager()->setPlayerRace(npc.mRace, npc.isMale(), npc.mHead, npc.mHair);\n            setEquipment();\n        }\n        else\n        {\n            ESM::NPC player = *world->getPlayerPtr().get<ESM::NPC>()->mBase;\n\n            player.mRace = npc.mRace;\n            player.mHead = npc.mHead;\n            player.mHair = npc.mHair;\n            player.mModel = npc.mModel;\n            player.setIsMale(npc.isMale());\n            world->createRecord(player);\n\n            MWBase::Environment::get().getMechanicsManager()->playerLoaded();\n\n            // This is needed to update the player's model instantly if they're in 3rd person\n            world->reattachPlayerCamera();\n        }\n\n        MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar();\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Character update was ignored due to invalid race %s\", npc.mRace.c_str());\n    }\n}\n\nvoid LocalPlayer::setDynamicStats()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n\n    MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer);\n    MWMechanics::DynamicStat<float> dynamicStat;\n\n    for (int i = 0; i < 3; ++i)\n    {\n        dynamicStat = ptrCreatureStats->getDynamic(i);\n        dynamicStat.setBase(creatureStats.mDynamic[i].mBase);\n        dynamicStat.setCurrent(creatureStats.mDynamic[i].mCurrent);\n        ptrCreatureStats->setDynamic(i, dynamicStat);\n    }\n}\n\nvoid LocalPlayer::setAttributes()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);\n    MWMechanics::AttributeValue attributeValue;\n\n    for (int attributeIndex = 0; attributeIndex < 8; ++attributeIndex)\n    {\n        // If the server wants to clear our attribute's non-zero modifier, we need to remove\n        // the spell effect causing it, to avoid an infinite loop where the effect keeps resetting\n        // the modifier\n        if (creatureStats.mAttributes[attributeIndex].mMod == 0 && ptrNpcStats->getAttribute(attributeIndex).getModifier() > 0)\n        {\n            ptrNpcStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifyAttribute, attributeIndex);\n            MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(ptrPlayer);\n\n            // Is the modifier for this attribute still higher than 0? If so, unequip items that\n            // fortify the attribute\n            if (ptrNpcStats->getAttribute(attributeIndex).getModifier() > 0)\n            {\n                MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FortifyAttribute, attributeIndex, -1);\n                mwmp::Main::get().getGUIController()->refreshGuiMode(MWGui::GM_Inventory);\n            }\n        }\n\n        attributeValue.readState(creatureStats.mAttributes[attributeIndex]);\n        ptrNpcStats->setAttribute(attributeIndex, attributeValue);\n\n        ptrNpcStats->setSkillIncrease(attributeIndex, npcStats.mSkillIncrease[attributeIndex]);\n    }\n}\n\nvoid LocalPlayer::setSkills()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);\n    MWMechanics::SkillValue skillValue;\n\n    for (int skillIndex = 0; skillIndex < 27; ++skillIndex)\n    {\n        // If the server wants to clear our skill's non-zero modifier, we need to remove\n        // the spell effect causing it, to avoid an infinite loop where the effect keeps resetting\n        // the modifier\n        if (npcStats.mSkills[skillIndex].mMod == 0 && ptrNpcStats->getSkill(skillIndex).getModifier() > 0)\n        {\n            ptrNpcStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifySkill, skillIndex);\n            MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(ptrPlayer);\n\n            // Is the modifier for this skill still higher than 0? If so, unequip items that\n            // fortify the skill\n            if (ptrNpcStats->getSkill(skillIndex).getModifier() > 0)\n            {\n                MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FortifySkill, -1, skillIndex);\n                mwmp::Main::get().getGUIController()->refreshGuiMode(MWGui::GM_Inventory);\n            }\n        }\n\n        skillValue.readState(npcStats.mSkills[skillIndex]);\n        ptrNpcStats->setSkill(skillIndex, skillValue);\n    }\n}\n\nvoid LocalPlayer::setLevel()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n\n    MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);\n    ptrNpcStats->setLevel(creatureStats.mLevel);\n    ptrNpcStats->setLevelProgress(npcStats.mLevelProgress);\n}\n\nvoid LocalPlayer::setBounty()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n\n    MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);\n    ptrNpcStats->setBounty(npcStats.mBounty);\n}\n\nvoid LocalPlayer::setReputation()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n\n    MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);\n    ptrNpcStats->setReputation(npcStats.mReputation);\n}\n\nvoid LocalPlayer::setPosition()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n\n    // If we're ignoring this position packet because of an invalid cell change,\n    // don't make the next one get ignored as well\n    if (ignorePosPacket)\n        ignorePosPacket = false;\n    else\n    {\n        world->getPlayer().setTeleported(true);\n\n        world->moveObject(ptrPlayer, position.pos[0], position.pos[1], position.pos[2]);\n        world->rotateObject(ptrPlayer, position.rot[0], position.rot[1], position.rot[2]);\n        world->setInertialForce(ptrPlayer, osg::Vec3f(0.f, 0.f, 0.f));\n    }\n\n    updatePosition(true);\n\n    // Make sure we update our draw state, or we'll end up with the wrong one\n    updateAnimFlags(true);\n}\n\nvoid LocalPlayer::setMomentum()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n    world->setInertialForce(ptrPlayer, momentum.asVec3());\n}\n\nvoid LocalPlayer::setCell()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = world->getPlayerPtr();\n    ESM::Position pos;\n\n    // To avoid crashes, close container windows this player may be in\n    closeInventoryWindows();\n\n    world->getPlayer().setTeleported(true);\n\n    int x = cell.mData.mX;\n    int y = cell.mData.mY;\n\n    if (cell.isExterior())\n    {\n        world->indexToPosition(x, y, pos.pos[0], pos.pos[1], true);\n        pos.pos[2] = 0;\n\n        pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;\n\n        world->changeToExteriorCell(pos, true);\n        world->fixPosition();\n    }\n    else if (world->findExteriorPosition(cell.mName, pos))\n    {\n        world->changeToExteriorCell(pos, true);\n        world->fixPosition();\n    }\n    else\n    {\n        try\n        {\n            world->findInteriorPosition(cell.mName, pos);\n            world->changeToInteriorCell(cell.mName, pos, true);\n        }\n        // If we've been sent to an invalid interior, ignore the incoming\n        // packet about our position in that cell\n        catch (std::exception&)\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"%s\", \"- Cell doesn't exist on this client\");\n            ignorePosPacket = true;\n        }\n    }\n\n    updateCell(true);\n}\n\nvoid LocalPlayer::setClass()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_CLASS from server\");\n\n    if (charClass.mId.empty()) // custom class\n    {\n        charClass.mData.mIsPlayable = 0x1;\n        MWBase::Environment::get().getMechanicsManager()->setPlayerClass(charClass);\n    }\n    else\n    {\n        const ESM::Class *existingCharClass = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().search(charClass.mId);\n\n        if (existingCharClass)\n        {\n            MWBase::Environment::get().getMechanicsManager()->setPlayerClass(charClass.mId);\n        }\n        else\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Ignored invalid default class %s\", charClass.mId.c_str());\n    }\n}\n\nvoid LocalPlayer::setEquipment()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    MWWorld::InventoryStore &ptrInventory = ptrPlayer.getClass().getInventoryStore(ptrPlayer);\n\n    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)\n    {\n        mwmp::Item &currentItem = equipmentItems[slot];\n\n        if (!currentItem.refId.empty())\n        {\n            auto it = find_if(ptrInventory.begin(), ptrInventory.end(), [&currentItem](const MWWorld::Ptr &itemPtr) {\n                return Misc::StringUtils::ciEqual(itemPtr.getCellRef().getRefId(), currentItem.refId);\n            });\n\n            // If the item is not in our inventory, add it as long as it's not a bound item\n            if (it == ptrInventory.end())\n            {\n                if (!MWBase::Environment::get().getMechanicsManager()->isBoundItem(currentItem.refId))\n                {\n                    try\n                    {\n                        auto addIter = ptrInventory.ContainerStore::add(currentItem.refId.c_str(), currentItem.count, ptrPlayer);\n\n                        ptrInventory.equip(slot, addIter, ptrPlayer);\n                    }\n                    catch (std::exception&)\n                    {\n                        LOG_APPEND(TimedLog::LOG_INFO, \"- Ignored addition of invalid equipment item %s\", currentItem.refId.c_str());\n                    }\n                }\n            }\n            else\n            {\n                // Don't try to equip an item that is already equipped\n                if (ptrInventory.getSlot(slot) != it)\n                    ptrInventory.equip(slot, it, ptrPlayer);\n            }\n        }\n        else\n            ptrInventory.unequipSlot(slot, ptrPlayer);\n    }\n\n    MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updatePlayer();\n}\n\nvoid LocalPlayer::setInventory()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWWorld::ContainerStore &ptrStore = ptrPlayer.getClass().getContainerStore(ptrPlayer);\n\n    // Ensure no item is being drag and dropped\n    MWBase::Environment::get().getWindowManager()->finishDragDrop();\n\n    // Clear items in inventory\n    ptrStore.clear();\n\n    // Proceed by adding items\n    addItems();\n\n    // Don't automatically setEquipment() here, or the player could end\n    // up getting a new set of their starting clothes, or other items\n    // supposed to no longer exist\n    //\n    // Instead, expect server scripts to do that manually\n}\n\nvoid LocalPlayer::setSpellbook()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::Spells &ptrSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getSpells();\n\n    // Clear spells in spellbook, while ignoring abilities, powers, etc.\n    while (true)\n    {\n        MWMechanics::Spells::TIterator iter = ptrSpells.begin();\n        for (; iter != ptrSpells.end(); iter++)\n        {\n            const ESM::Spell *spell = iter->first;\n            if (spell->mData.mType == ESM::Spell::ST_Spell)\n            {\n                ptrSpells.remove(spell->mId);\n                break;\n            }\n        }\n        if (iter == ptrSpells.end())\n            break;\n    }\n\n    // Proceed by adding spells\n    addSpells();\n}\n\nvoid LocalPlayer::setSpellsActive()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::ActiveSpells& activeSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getActiveSpells();\n    activeSpells.clear();\n\n    // Proceed by adding spells active\n    addSpellsActive();\n}\n\nvoid LocalPlayer::setCooldowns()\n{\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::Spells& ptrSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getSpells();\n\n    for (const auto& cooldown : cooldownChanges)\n    {\n        if (world->getStore().get<ESM::Spell>().search(cooldown.id))\n        {\n            const ESM::Spell* spell = world->getStore().get<ESM::Spell>().search(cooldown.id);\n\n            ptrSpells.setPowerUseTimestamp(spell, cooldown.startTimestampDay, cooldown.startTimestampHour);\n        }\n    }\n}\n\nvoid LocalPlayer::setQuickKeys()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_QUICKKEYS from server\");\n\n    for (const auto &quickKey : quickKeyChanges)\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- slot: %i, type: %i, itemId: %s\", quickKey.slot, quickKey.type, quickKey.itemId.c_str());\n\n        if (quickKey.type == QuickKey::ITEM || quickKey.type == QuickKey::ITEM_MAGIC)\n        {\n            MWWorld::InventoryStore &ptrInventory = ptrPlayer.getClass().getInventoryStore(ptrPlayer);\n\n            auto it = find_if(ptrInventory.begin(), ptrInventory.end(), [&quickKey](const MWWorld::Ptr &inventoryItem) {\n                return Misc::StringUtils::ciEqual(inventoryItem.getCellRef().getRefId(), quickKey.itemId);\n            });\n\n            if (it != ptrInventory.end())\n                MWBase::Environment::get().getWindowManager()->setQuickKey(quickKey.slot, quickKey.type, (*it));\n        }\n        else if (quickKey.type == QuickKey::MAGIC)\n        {\n            MWMechanics::Spells &ptrSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getSpells();\n            bool hasSpell = false;\n\n            MWMechanics::Spells::TIterator iter = ptrSpells.begin();\n            for (; iter != ptrSpells.end(); iter++)\n            {\n                const ESM::Spell *spell = iter->first;\n                if (Misc::StringUtils::ciEqual(spell->mId, quickKey.itemId))\n                {\n                    hasSpell = true;\n                    break;\n                }\n            }\n\n            if (hasSpell)\n                MWBase::Environment::get().getWindowManager()->setQuickKey(quickKey.slot, quickKey.type, 0, quickKey.itemId);\n        }\n        else\n            MWBase::Environment::get().getWindowManager()->setQuickKey(quickKey.slot, quickKey.type, 0);\n    }\n}\n\nvoid LocalPlayer::setFactions()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_FACTION from server - action: %i\", factionChanges.action);\n\n    for (const auto &faction : factionChanges.factions)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \" - processing faction: %s\", faction.factionId.c_str());\n        const ESM::Faction *esmFaction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().search(faction.factionId);\n\n        if (!esmFaction)\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Ignored invalid faction %s\", faction.factionId.c_str());\n            continue;\n        }\n\n        if (factionChanges.action == mwmp::FactionChanges::RANK)\n        {\n\n            if (!ptrNpcStats.isInFaction(faction.factionId))\n            {\n                // If the player isn't in this faction, make them join it\n                ptrNpcStats.joinFaction(faction.factionId);\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"\\t>JOINED FACTION: %s on rank change to: %d.\",\n                    faction.factionId.c_str(), faction.rank);\n            }\n\n            // While the faction rank is different in the packet than in the NpcStats,\n            // adjust the NpcStats accordingly\n            while (faction.rank != ptrNpcStats.getFactionRanks().at(faction.factionId))\n            {\n                if (faction.rank > ptrNpcStats.getFactionRanks().at(faction.factionId))\n                    ptrNpcStats.raiseRank(faction.factionId);\n                else\n                    ptrNpcStats.lowerRank(faction.factionId);\n            }\n        }\n        else if (factionChanges.action == mwmp::FactionChanges::EXPULSION)\n        {\n            // If the expelled state is different in the packet than in the NpcStats,\n            // adjust the NpcStats accordingly\n            if (faction.isExpelled != ptrNpcStats.getExpelled(faction.factionId))\n            {\n                if (faction.isExpelled)\n                    ptrNpcStats.expell(faction.factionId);\n                else\n                    ptrNpcStats.clearExpelled(faction.factionId);\n            }\n        }\n\n        else if (factionChanges.action == mwmp::FactionChanges::REPUTATION)\n            ptrNpcStats.setFactionReputation(faction.factionId, faction.reputation);\n    }\n}\n\nvoid LocalPlayer::setBooks()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);\n\n    for (const auto &book : bookChanges)\n        ptrNpcStats.flagAsUsed(book.bookId);\n}\n\nvoid LocalPlayer::setShapeshift()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    MWBase::Environment::get().getWorld()->scaleObject(ptrPlayer, scale);\n    MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptrPlayer, isWerewolf);\n}\n\nvoid LocalPlayer::setMarkLocation()\n{\n    MWWorld::CellStore *ptrCellStore = Main::get().getCellController()->getCellStore(markCell);\n\n    if (ptrCellStore)\n        MWBase::Environment::get().getWorld()->getPlayer().markPosition(ptrCellStore, markPosition);\n}\n\nvoid LocalPlayer::setSelectedSpell()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n\n    MWMechanics::CreatureStats& stats = ptrPlayer.getClass().getCreatureStats(ptrPlayer);\n    MWMechanics::Spells& spells = stats.getSpells();\n\n    if (!spells.hasSpell(selectedSpellId))\n        return;\n \n    MWBase::Environment::get().getWindowManager()->setSelectedSpell(selectedSpellId,\n        int(MWMechanics::getSpellSuccessChance(selectedSpellId, ptrPlayer)));\n}\n\nvoid LocalPlayer::sendDeath(char newDeathState)\n{\n    if (MechanicsHelper::isEmptyTarget(killer))\n        killer = MechanicsHelper::getTarget(getPlayerPtr());\n\n    deathState = newDeathState;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_DEATH about myself to server\\n- deathState: %d\", deathState);\n    getNetworking()->getPlayerPacket(ID_PLAYER_DEATH)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_DEATH)->Send();\n\n    MechanicsHelper::clearTarget(killer);\n}\n\nvoid LocalPlayer::sendClass()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    const ESM::NPC *npcBase = world->getPlayerPtr().get<ESM::NPC>()->mBase;\n    const ESM::Class *esmClass = world->getStore().get<ESM::Class>().find(npcBase->mClass);\n\n    if (npcBase->mClass.find(\"$dynamic\") != std::string::npos) // custom class\n    {\n        charClass.mId = \"\";\n        charClass.mName = esmClass->mName;\n        charClass.mDescription = esmClass->mDescription;\n        charClass.mData = esmClass->mData;\n    }\n    else\n        charClass.mId = esmClass->mId;\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_CHARCLASS)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_CHARCLASS)->Send();\n}\n\nvoid LocalPlayer::sendInventory()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending entire inventory to server\");\n\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWWorld::InventoryStore &ptrInventory = ptrPlayer.getClass().getInventoryStore(ptrPlayer);\n    mwmp::Item item;\n\n    inventoryChanges.items.clear();\n\n    for (const auto &iter : ptrInventory)\n    {\n        item.refId = iter.getCellRef().getRefId();\n\n        // Skip any items that somehow have clientside-only dynamic IDs\n        if (item.refId.find(\"$dynamic\") != std::string::npos)\n            continue;\n\n        // Skip bound items\n        if (MWBase::Environment::get().getMechanicsManager()->isBoundItem(item.refId))\n            continue;\n\n        item.count = iter.getRefData().getCount();\n        item.charge = iter.getCellRef().getCharge();\n        item.enchantmentCharge = iter.getCellRef().getEnchantmentCharge();\n        item.soul = iter.getCellRef().getSoul();\n\n        inventoryChanges.items.push_back(item);\n    }\n\n    inventoryChanges.action = InventoryChanges::SET;\n    getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->Send();\n}\n\nvoid LocalPlayer::sendItemChange(const mwmp::Item& item, unsigned int action)\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending item change for %s with action %i, count %i\",\n        item.refId.c_str(), action, item.count);\n\n    inventoryChanges.items.clear();\n    inventoryChanges.items.push_back(item);\n    inventoryChanges.action = action;\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->Send();\n}\n\nvoid LocalPlayer::sendItemChange(const MWWorld::Ptr& itemPtr, int count, unsigned int action)\n{\n    mwmp::Item item = MechanicsHelper::getItem(itemPtr, count);\n    sendItemChange(item, action);\n}\n\nvoid LocalPlayer::sendItemChange(const std::string& refId, int count, unsigned int action)\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending item change for %s with action %i, count %i\",\n        refId.c_str(), action, count);\n\n    inventoryChanges.items.clear();\n    \n    mwmp::Item item;\n    item.refId = refId;\n    item.count = count;\n    item.charge = -1;\n    item.enchantmentCharge = -1;\n    item.soul = \"\";\n\n    inventoryChanges.items.push_back(item);\n\n    inventoryChanges.action = action;\n    getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->Send();\n}\n\nvoid LocalPlayer::sendStoredItemRemovals()\n{\n    inventoryChanges.items.clear();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending stored item removals for LocalPlayer:\");\n\n    for (auto storedItemRemoval : storedItemRemovals)\n    {\n        mwmp::Item item;\n        item.refId = storedItemRemoval.first;\n        item.count = storedItemRemoval.second;\n        item.charge = -1;\n        item.enchantmentCharge = -1;\n        item.soul = \"\";\n        inventoryChanges.items.push_back(item);\n\n        LOG_APPEND(TimedLog::LOG_INFO, \"- %s with count %i\", item.refId.c_str(), item.count);\n    }\n\n    inventoryChanges.action = mwmp::InventoryChanges::ACTION_TYPE::REMOVE;\n    getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->Send();\n\n    storedItemRemovals.clear();\n}\n\nvoid LocalPlayer::sendSpellbook()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::Spells &ptrSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getSpells();\n\n    spellbookChanges.spells.clear();\n\n    // Send spells in spellbook, while ignoring abilities, powers, etc.\n    for (const auto &spell : ptrSpells)\n    {\n        if (spell.first->mData.mType == ESM::Spell::ST_Spell)\n            spellbookChanges.spells.push_back(*spell.first);\n    }\n\n    spellbookChanges.action = SpellbookChanges::SET;\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLBOOK)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLBOOK)->Send();\n}\n\nvoid LocalPlayer::sendSpellChange(std::string id, unsigned int action)\n{\n    // Skip any bugged spells that somehow have clientside-only dynamic IDs\n    if (id.find(\"$dynamic\") != std::string::npos)\n        return;\n\n    spellbookChanges.spells.clear();\n\n    ESM::Spell spell;\n    spell.mId = id;\n    spellbookChanges.spells.push_back(spell);\n\n    spellbookChanges.action = action;\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLBOOK)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLBOOK)->Send();\n}\n\nvoid LocalPlayer::sendSpellsActive()\n{\n    MWWorld::Ptr ptrPlayer = getPlayerPtr();\n    MWMechanics::ActiveSpells& activeSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getActiveSpells();\n\n    spellsActiveChanges.activeSpells.clear();\n\n    // Send spells in spellbook, while ignoring abilities, powers, etc.\n    for (const auto& ptrSpell : activeSpells)\n    {\n        mwmp::ActiveSpell packetSpell;\n        packetSpell.id = ptrSpell.first;\n        packetSpell.params.mDisplayName = ptrSpell.second.mDisplayName;\n        packetSpell.params.mEffects = ptrSpell.second.mEffects;\n        spellsActiveChanges.activeSpells.push_back(packetSpell);\n    }\n\n    spellsActiveChanges.action = mwmp::SpellsActiveChanges::SET;\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->Send();\n}\n\nvoid LocalPlayer::sendSpellsActiveAddition(const std::string id, bool isStackingSpell, const MWMechanics::ActiveSpells::ActiveSpellParams& params)\n{\n    // Skip any bugged spells that somehow have clientside-only dynamic IDs\n    if (id.find(\"$dynamic\") != std::string::npos)\n        return;\n\n    spellsActiveChanges.activeSpells.clear();\n\n\n    MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(params.mCasterActorId);\n\n    mwmp::ActiveSpell spell;\n    spell.id = id;\n    spell.isStackingSpell = isStackingSpell;\n    spell.caster = MechanicsHelper::getTarget(caster);\n    spell.timestampDay = params.mTimeStamp.getDay();\n    spell.timestampHour = params.mTimeStamp.getHour();\n    spell.params.mEffects = params.mEffects;\n    spell.params.mDisplayName = params.mDisplayName;\n    spellsActiveChanges.activeSpells.push_back(spell);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending active spell addition with stacking %s, timestamp %i %f\",\n        spell.isStackingSpell ? \"true\" : \"false\", spell.timestampDay, spell.timestampHour);\n\n    spellsActiveChanges.action = mwmp::SpellsActiveChanges::ADD;\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->Send();\n}\n\nvoid LocalPlayer::sendSpellsActiveRemoval(const std::string id, bool isStackingSpell, MWWorld::TimeStamp timestamp)\n{\n    // Skip any bugged spells that somehow have clientside-only dynamic IDs\n    if (id.find(\"$dynamic\") != std::string::npos)\n        return;\n\n    spellsActiveChanges.activeSpells.clear();\n\n    mwmp::ActiveSpell spell;\n    spell.id = id;\n    spell.isStackingSpell = isStackingSpell;\n    spell.timestampDay = timestamp.getDay();\n    spell.timestampHour = timestamp.getHour();\n    spellsActiveChanges.activeSpells.push_back(spell);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending active spell removal with stacking %s, timestamp %i %f\",\n        spell.isStackingSpell ? \"true\" : \"false\", spell.timestampDay, spell.timestampHour);\n\n    spellsActiveChanges.action = mwmp::SpellsActiveChanges::REMOVE;\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->Send();\n}\n\nvoid LocalPlayer::sendCooldownChange(std::string id, int startTimestampDay, float startTimestampHour)\n{\n    // Skip any bugged spells that somehow have clientside-only dynamic IDs\n    if (id.find(\"$dynamic\") != std::string::npos)\n        return;\n\n    cooldownChanges.clear();\n\n    SpellCooldown spellCooldown;\n    spellCooldown.id = id;\n    spellCooldown.startTimestampDay = startTimestampDay;\n    spellCooldown.startTimestampHour = startTimestampHour;\n\n    cooldownChanges.push_back(spellCooldown);\n;\n    getNetworking()->getPlayerPacket(ID_PLAYER_COOLDOWNS)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_COOLDOWNS)->Send();\n}\n\nvoid LocalPlayer::sendQuickKey(unsigned short slot, int type, const std::string& itemId)\n{\n    quickKeyChanges.clear();\n\n    mwmp::QuickKey quickKey;\n    quickKey.slot = slot;\n    quickKey.type = type;\n    quickKey.itemId = itemId;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_QUICKKEYS\", itemId.c_str());\n    LOG_APPEND(TimedLog::LOG_INFO, \"- slot: %i, type: %i, itemId: %s\", quickKey.slot, quickKey.type, quickKey.itemId.c_str());\n\n    quickKeyChanges.push_back(quickKey);\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_QUICKKEYS)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_QUICKKEYS)->Send();\n}\n\nvoid LocalPlayer::sendJournalEntry(const std::string& quest, int index, const MWWorld::Ptr& actor)\n{\n    journalChanges.clear();\n\n    mwmp::JournalItem journalItem;\n    journalItem.type = JournalItem::ENTRY;\n    journalItem.quest = quest;\n    journalItem.index = index;\n    journalItem.actorRefId = actor.getCellRef().getRefId();\n    journalItem.hasTimestamp = false;\n\n    journalChanges.push_back(journalItem);\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_JOURNAL)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_JOURNAL)->Send();\n}\n\nvoid LocalPlayer::sendJournalIndex(const std::string& quest, int index)\n{\n    journalChanges.clear();\n\n    mwmp::JournalItem journalItem;\n    journalItem.type = JournalItem::INDEX;\n    journalItem.quest = quest;\n    journalItem.index = index;\n\n    journalChanges.push_back(journalItem);\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_JOURNAL)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_JOURNAL)->Send();\n}\n\nvoid LocalPlayer::sendFactionRank(const std::string& factionId, int rank)\n{\n    factionChanges.factions.clear();\n    factionChanges.action = FactionChanges::RANK;\n\n    mwmp::Faction faction;\n    faction.factionId = factionId;\n    faction.rank = rank;\n\n    factionChanges.factions.push_back(faction);\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_FACTION)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_FACTION)->Send();\n}\n\nvoid LocalPlayer::sendFactionExpulsionState(const std::string& factionId, bool isExpelled)\n{\n    factionChanges.factions.clear();\n    factionChanges.action = FactionChanges::EXPULSION;\n\n    mwmp::Faction faction;\n    faction.factionId = factionId;\n    faction.isExpelled = isExpelled;\n\n    factionChanges.factions.push_back(faction);\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_FACTION)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_FACTION)->Send();\n}\n\nvoid LocalPlayer::sendFactionReputation(const std::string& factionId, int reputation)\n{\n    factionChanges.factions.clear();\n    factionChanges.action = FactionChanges::REPUTATION;\n\n    mwmp::Faction faction;\n    faction.factionId = factionId;\n    faction.reputation = reputation;\n\n    factionChanges.factions.push_back(faction);\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_FACTION)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_FACTION)->Send();\n}\n\nvoid LocalPlayer::sendTopic(const std::string& topicId)\n{\n    topicChanges.clear();\n\n    mwmp::Topic topic;\n\n    // For translated versions of the game, make sure we translate the topic back into English first\n    if (MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())\n        topic.topicId = MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().topicID(topicId);\n    else\n        topic.topicId = topicId;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_TOPIC with topic %s\", topic.topicId.c_str());\n\n    topicChanges.push_back(topic);\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_TOPIC)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_TOPIC)->Send();\n}\n\nvoid LocalPlayer::sendBook(const std::string& bookId)\n{\n    bookChanges.clear();\n\n    mwmp::Book book;\n    book.bookId = bookId;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_BOOK with book %s\", book.bookId.c_str());\n\n    bookChanges.push_back(book);\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_BOOK)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_BOOK)->Send();\n}\n\nvoid LocalPlayer::sendWerewolfState(bool werewolfState)\n{\n    isWerewolf = werewolfState;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_SHAPESHIFT with isWerewolf of %s\", isWerewolf ? \"true\" : \"false\");\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_SHAPESHIFT)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_SHAPESHIFT)->Send();\n}\n\nvoid LocalPlayer::sendMarkLocation(const ESM::Cell& newMarkCell, const ESM::Position& newMarkPosition)\n{\n    miscellaneousChangeType = mwmp::MISCELLANEOUS_CHANGE_TYPE::MARK_LOCATION;\n    markCell = newMarkCell;\n    markPosition = newMarkPosition;\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_MISCELLANEOUS)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_MISCELLANEOUS)->Send();\n}\n\nvoid LocalPlayer::sendSelectedSpell(const std::string& newSelectedSpellId)\n{\n    miscellaneousChangeType = mwmp::MISCELLANEOUS_CHANGE_TYPE::SELECTED_SPELL;\n    selectedSpellId = newSelectedSpellId;\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_MISCELLANEOUS)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_MISCELLANEOUS)->Send();\n}\n\nvoid LocalPlayer::sendItemUse(const MWWorld::Ptr& itemPtr, bool itemMagicState, char currentDrawState)\n{\n    usedItem.refId = itemPtr.getCellRef().getRefId();\n    usedItem.count = itemPtr.getRefData().getCount();\n    usedItem.charge = itemPtr.getCellRef().getCharge();\n    usedItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();\n    usedItem.soul = itemPtr.getCellRef().getSoul();\n\n    usingItemMagic = itemMagicState;\n    itemUseDrawState = currentDrawState;\n\n    getNetworking()->getPlayerPacket(ID_PLAYER_ITEM_USE)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_ITEM_USE)->Send();\n}\n\nvoid LocalPlayer::sendCellStates()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_CELL_STATE to server\");\n    getNetworking()->getPlayerPacket(ID_PLAYER_CELL_STATE)->setPlayer(this);\n    getNetworking()->getPlayerPacket(ID_PLAYER_CELL_STATE)->Send();\n}\n\nvoid LocalPlayer::clearCellStates()\n{\n    cellStateChanges.clear();\n}\n\nvoid LocalPlayer::clearCurrentContainer()\n{\n    currentContainer.refId = \"\";\n    currentContainer.refNum = 0;\n    currentContainer.mpNum = 0;\n}\n\nvoid LocalPlayer::storeCellState(const ESM::Cell& storedCell, int stateType)\n{\n    std::vector<CellState>::iterator iter;\n\n    for (iter = cellStateChanges.begin(); iter != cellStateChanges.end(); )\n    {\n        // If there's already a cell state recorded for this particular cell,\n        // remove it\n        if (storedCell.getShortDescription() == (*iter).cell.getShortDescription())\n            iter = cellStateChanges.erase(iter);\n        else\n            ++iter;\n    }\n\n    CellState cellState;\n    cellState.cell = storedCell;\n    cellState.type = stateType;\n\n    cellStateChanges.push_back(cellState);\n}\n\nvoid LocalPlayer::storeCurrentContainer(const MWWorld::Ptr &container)\n{\n    currentContainer.refId = container.getCellRef().getRefId();\n    currentContainer.refNum = container.getCellRef().getRefNum().mIndex;\n    currentContainer.mpNum = container.getCellRef().getMpNum();\n}\n\nvoid LocalPlayer::storeItemRemoval(const std::string& refId, int count)\n{\n    storedItemRemovals[refId] = storedItemRemovals[refId] + count;\n}\n\nvoid LocalPlayer::storeLastEnchantmentQuantity(unsigned int quantity)\n{\n    lastEnchantmentQuantity = quantity;\n}\n\nvoid LocalPlayer::playAnimation()\n{\n    MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(getPlayerPtr(),\n        animation.groupname, animation.mode, animation.count, animation.persist);\n\n    isPlayingAnimation = true;\n}\n\nvoid LocalPlayer::playSpeech()\n{\n    MWBase::Environment::get().getSoundManager()->say(getPlayerPtr(), sound);\n\n    MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();\n    if (winMgr->getSubtitlesEnabled())\n        winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never);\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/LocalPlayer.hpp",
    "content": "#ifndef OPENMW_LOCALPLAYER_HPP\n#define OPENMW_LOCALPLAYER_HPP\n\n#include <components/openmw-mp/Base/BasePlayer.hpp>\n#include \"../mwmechanics/activespells.hpp\"\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/timestamp.hpp\"\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    class Networking;\n    class LocalPlayer : public BasePlayer\n    {\n    public:\n\n        LocalPlayer();\n        virtual ~LocalPlayer();\n\n        time_t deathTime;\n        bool receivedCharacter;\n\n        bool isUsingBed;\n        bool avoidSendingInventoryPackets;\n        bool isReceivingQuickKeys;\n        bool isPlayingAnimation;\n        bool diedSinceArrestAttempt;\n        unsigned int lastEnchantmentQuantity;\n\n        void update();\n\n        bool processCharGen();\n        bool isLoggedIn();\n\n        void updateStatsDynamic(bool forceUpdate = false);\n        void updateAttributes(bool forceUpdate = false);\n        void updateSkills(bool forceUpdate = false);\n        void updateLevel(bool forceUpdate = false);\n        void updateBounty(bool forceUpdate = false);\n        void updateReputation(bool forceUpdate = false);\n        void updatePosition(bool forceUpdate = false);\n        void updateCell(bool forceUpdate = false);\n        void updateEquipment(bool forceUpdate = false);\n        void updateInventory(bool forceUpdate = false);\n        void updateAttackOrCast();\n        void updateAnimFlags(bool forceUpdate = false);\n\n        void addItems();\n        void addSpells();\n        void addSpellsActive();\n        void addJournalItems();\n        void addTopics();\n\n        void removeItems();\n        void removeSpells();\n        void removeSpellsActive();\n\n        void die();\n        void resurrect();\n\n        void closeInventoryWindows();\n        void updateInventoryWindow();\n\n        void setCharacter();\n        void setDynamicStats();\n        void setAttributes();\n        void setSkills();\n        void setLevel();\n        void setBounty();\n        void setReputation();\n        void setPosition();\n        void setMomentum();\n        void setCell();\n        void setClass();\n        void setEquipment();\n        void setInventory();\n        void setSpellbook();\n        void setSpellsActive();\n        void setCooldowns();\n        void setQuickKeys();\n        void setFactions();\n        void setBooks();\n        void setShapeshift();\n        void setMarkLocation();\n        void setSelectedSpell();\n\n        void sendDeath(char newDeathState);\n        void sendClass();\n        void sendInventory();\n        void sendItemChange(const mwmp::Item& item, unsigned int action);\n        void sendItemChange(const MWWorld::Ptr& itemPtr, int count, unsigned int action);\n        void sendItemChange(const std::string& refId, int count, unsigned int action);\n        void sendStoredItemRemovals();\n        void sendSpellbook();\n        void sendSpellChange(std::string id, unsigned int action);\n        void sendSpellsActive();\n        void sendSpellsActiveAddition(const std::string id, bool isStackingSpell, const MWMechanics::ActiveSpells::ActiveSpellParams& params);\n        void sendSpellsActiveRemoval(const std::string id, bool isStackingSpell, MWWorld::TimeStamp timestamp);\n        void sendCooldownChange(std::string id, int startTimestampDay, float startTimestampHour);\n        void sendQuickKey(unsigned short slot, int type, const std::string& itemId = \"\");\n        void sendJournalEntry(const std::string& quest, int index, const MWWorld::Ptr& actor);\n        void sendJournalIndex(const std::string& quest, int index);\n        void sendFactionRank(const std::string& factionId, int rank);\n        void sendFactionExpulsionState(const std::string& factionId, bool isExpelled);\n        void sendFactionReputation(const std::string& factionId, int reputation);\n        void sendTopic(const std::string& topic);\n        void sendBook(const std::string& bookId);\n        void sendWerewolfState(bool isWerewolf);\n        void sendMarkLocation(const ESM::Cell& newMarkCell, const ESM::Position& newMarkPosition);\n        void sendSelectedSpell(const std::string& newSelectedSpellId);\n        void sendItemUse(const MWWorld::Ptr& itemPtr, bool usingItemMagic = false, char currentDrawState = 0);\n        void sendCellStates();\n\n        void clearCellStates();\n        void clearCurrentContainer();\n\n        void storeCellState(const ESM::Cell& cell, int stateType);\n        void storeCurrentContainer(const MWWorld::Ptr& container);\n        void storeItemRemoval(const std::string& refId, int count);\n        void storeLastEnchantmentQuantity(unsigned int quantity);\n\n        void playAnimation();\n        void playSpeech();\n\n        MWWorld::Ptr getPlayerPtr();\n\n    private:\n        Networking *getNetworking();\n\n    };\n}\n\n#endif //OPENMW_LOCALPLAYER_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/LocalSystem.cpp",
    "content": "#include \"LocalSystem.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n\nusing namespace mwmp;\n\nLocalSystem::LocalSystem()\n{\n\n}\n\nLocalSystem::~LocalSystem()\n{\n\n}\n\nNetworking *LocalSystem::getNetworking()\n{\n    return mwmp::Main::get().getNetworking();\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/LocalSystem.hpp",
    "content": "#ifndef OPENMW_LOCALSYSTEM_HPP\n#define OPENMW_LOCALSYSTEM_HPP\n\n#include <components/openmw-mp/Base/BaseSystem.hpp>\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    class Networking;\n    class LocalSystem : public BaseSystem\n    {\n    public:\n\n        LocalSystem();\n        virtual ~LocalSystem();\n\n    private:\n        Networking *getNetworking();\n\n    };\n}\n\n#endif //OPENMW_LOCALSYSTEM_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/Main.cpp",
    "content": "#include <cstdlib>\n\n#include <components/openmw-mp/Utils.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Version.hpp>\n\n#include <components/esm/esmwriter.hpp>\n#include <components/files/configurationmanager.hpp>\n#include <components/files/escape.hpp>\n\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwclass/creature.hpp\"\n#include \"../mwclass/npc.hpp\"\n\n#include \"../mwdialogue/dialoguemanagerimp.hpp\"\n\n#include \"../mwgui/windowmanagerimp.hpp\"\n\n#include \"../mwinput/inputmanagerimp.hpp\"\n\n#include \"../mwmechanics/aitravel.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/mechanicsmanagerimp.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n\n#include \"../mwscript/scriptmanagerimp.hpp\"\n\n#include \"../mwstate/statemanagerimp.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/customdata.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"LocalSystem.hpp\"\n#include \"LocalPlayer.hpp\"\n#include \"DedicatedPlayer.hpp\"\n#include \"PlayerList.hpp\"\n#include \"GUIController.hpp\"\n#include \"CellController.hpp\"\n#include \"MechanicsHelper.hpp\"\n#include \"RecordHelper.hpp\"\n\nusing namespace mwmp;\n\nMain *Main::pMain = 0;\nstd::string Main::address = \"\";\nstd::string Main::serverPassword = TES3MP_DEFAULT_PASSW;\nstd::string Main::resourceDir = \"\";\n\nstd::string Main::getResDir()\n{\n    return resourceDir;\n}\n\nstd::string loadSettings(Settings::Manager& settings)\n{\n    Files::ConfigurationManager mCfgMgr;\n    // Create the settings manager and load default settings file\n    const std::string localdefault = (mCfgMgr.getLocalPath() / \"tes3mp-client-default.cfg\").string();\n    const std::string globaldefault = (mCfgMgr.getGlobalPath() / \"tes3mp-client-default.cfg\").string();\n\n    // prefer local\n    if (boost::filesystem::exists(localdefault))\n        settings.loadDefault(localdefault, false);\n    else if (boost::filesystem::exists(globaldefault))\n        settings.loadDefault(globaldefault, false);\n    else\n        throw std::runtime_error (\"No default settings file found! Make sure the file \\\"tes3mp-client-default.cfg\\\" was properly installed.\");\n\n    // load user settings if they exist\n    const std::string settingspath = (mCfgMgr.getUserConfigPath() / \"tes3mp-client.cfg\").string();\n    if (boost::filesystem::exists(settingspath))\n        settings.loadUser(settingspath);\n\n    return settingspath;\n}\n\nMain::Main()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"tes3mp started\");\n    mNetworking = new Networking();\n    mLocalSystem = new LocalSystem();\n    mLocalPlayer = new LocalPlayer();\n    mGUIController = new GUIController();\n    mCellController = new CellController();\n\n    server = \"mp.tes3mp.com\";\n    port = 25565;\n}\n\nMain::~Main()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"tes3mp stopped\");\n    delete mNetworking;\n    delete mLocalSystem;\n    delete mLocalPlayer;\n    delete mCellController;\n    delete mGUIController;\n    PlayerList::cleanUp();\n}\n\nvoid Main::optionsDesc(boost::program_options::options_description *desc)\n{\n    namespace bpo = boost::program_options;\n    desc->add_options()\n            (\"connect\", bpo::value<std::string>()->default_value(\"\"),\n                        \"connect to server (e.g. --connect=127.0.0.1:25565)\")\n            (\"password\", bpo::value<std::string>()->default_value(TES3MP_DEFAULT_PASSW),\n                        \"сonnect to a secured server. (e.g. --password=AnyPassword\");\n}\n\nvoid Main::configure(const boost::program_options::variables_map &variables)\n{\n    Main::address = variables[\"connect\"].as<std::string>();\n    Main::serverPassword = variables[\"password\"].as<std::string>();\n    resourceDir = variables[\"resources\"].as<Files::EscapePath>().mPath.string();\n}\n\nbool Main::init(std::vector<std::string> &content, Files::Collections &collections)\n{\n    assert(!pMain);\n    pMain = new Main();\n\n    Settings::Manager manager;\n    loadSettings(manager);\n\n    int logLevel = manager.getInt(\"logLevel\", \"General\");\n    TimedLog::SetLevel(logLevel);\n    if (address.empty())\n    {\n        pMain->server = manager.getString(\"destinationAddress\", \"General\");\n        pMain->port = (unsigned short) manager.getInt(\"port\", \"General\");\n\n        serverPassword = manager.getString(\"password\", \"General\");\n        if (serverPassword.empty())\n            serverPassword = TES3MP_DEFAULT_PASSW;\n    }\n    else\n    {\n        size_t delimPos = address.find(':');\n        pMain->server = address.substr(0, delimPos);\n        pMain->port = atoi(address.substr(delimPos + 1).c_str());\n    }\n    get().mLocalSystem->serverPassword = serverPassword;\n\n    pMain->mNetworking->connect(pMain->server, pMain->port, content, collections);\n\n    return pMain->mNetworking->isConnected();\n}\n\nvoid Main::postInit()\n{\n    pMain->mGUIController->setupChat();\n\n    const MWBase::Environment &environment = MWBase::Environment::get();\n    environment.getStateManager()->newGame(true);\n    MWBase::Environment::get().getMechanicsManager()->toggleAI();\n    RecordHelper::createPlaceholderInteriorCell();\n}\n\nbool Main::isInitialized()\n{\n    return pMain != nullptr;\n}\n\nvoid Main::destroy()\n{\n    assert(pMain);\n\n    delete pMain;\n    pMain = 0;\n}\n\nvoid Main::frame(float dt)\n{\n    get().getNetworking()->update();\n\n    PlayerList::update(dt);\n    get().getCellController()->updateDedicated(dt);\n    get().updateWorld(dt);\n\n    get().getGUIController()->update(dt);\n}\n\nvoid Main::updateWorld(float dt) const\n{\n\n    if (!mLocalPlayer->processCharGen())\n        return;\n\n    static bool init = true;\n    if (init)\n    {\n        init = false;\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_BASEINFO to server\");\n\n        mNetworking->getPlayerPacket(ID_PLAYER_BASEINFO)->setPlayer(getLocalPlayer());\n        mNetworking->getPlayerPacket(ID_LOADED)->setPlayer(getLocalPlayer());\n        mNetworking->getPlayerPacket(ID_PLAYER_BASEINFO)->Send();\n        mNetworking->getPlayerPacket(ID_LOADED)->Send();\n        mLocalPlayer->updateStatsDynamic(true);\n        get().getGUIController()->setChatVisible(true);\n    }\n    else\n    {\n        mLocalPlayer->update();\n        mCellController->updateLocal(false);\n    }\n}\n\nconst Main &Main::get()\n{\n    return *pMain;\n}\n\nNetworking *Main::getNetworking() const\n{\n    return mNetworking;\n}\n\nLocalSystem *Main::getLocalSystem() const\n{\n    return mLocalSystem;\n}\n\nLocalPlayer *Main::getLocalPlayer() const\n{\n    return mLocalPlayer;\n}\n\nGUIController *Main::getGUIController() const\n{\n    return mGUIController;\n}\n\nCellController *Main::getCellController() const\n{\n    return mCellController;\n}\n\nbool Main::isValidPacketScript(std::string scriptId)\n{\n    mwmp::BaseWorldstate *worldstate = get().getNetworking()->getWorldstate();\n\n    if (Utils::vectorContains(worldstate->synchronizedClientScriptIds, scriptId))\n        return true;\n\n    return false;\n}\n\nbool Main::isValidPacketGlobal(std::string globalId)\n{\n    mwmp::BaseWorldstate *worldstate = get().getNetworking()->getWorldstate();\n\n    if (Utils::vectorContains(worldstate->synchronizedClientGlobalIds, globalId))\n        return true;\n\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/Main.hpp",
    "content": "#ifndef OPENMW_MWMP_MAIN\n#define OPENMW_MWMP_MAIN\n\n#include \"../mwworld/ptr.hpp\"\n#include <boost/program_options.hpp>\n#include <components/files/collections.hpp>\n\nnamespace mwmp\n{\n    class GUIController;\n    class CellController;\n    class LocalSystem;\n    class LocalPlayer;\n    class Networking;\n\n    class Main\n    {\n    public:\n        Main();\n        ~Main();\n\n        static void optionsDesc(boost::program_options::options_description *desc);\n        static void configure(const boost::program_options::variables_map &variables);\n        static bool init(std::vector<std::string> &content, Files::Collections &collections);\n        static void postInit();\n        static bool isInitialized();\n        static void destroy();\n        static const Main &get();\n        static void frame(float dt);\n\n        static bool isValidPacketScript(std::string scriptId);\n        static bool isValidPacketGlobal(std::string globalId);\n\n        static std::string getResDir();\n\n        Networking *getNetworking() const;\n        LocalSystem *getLocalSystem() const;\n        LocalPlayer *getLocalPlayer() const;\n        GUIController *getGUIController() const;\n        CellController *getCellController() const;\n\n        void updateWorld(float dt) const;\n\n    private:\n        static std::string resourceDir;\n        static std::string address;\n        static std::string serverPassword;\n        Main (const Main&);\n        ///< not implemented\n        Main& operator= (const Main&);\n        ///< not implemented\n        static Main *pMain;\n        Networking *mNetworking;\n        LocalSystem *mLocalSystem;\n        LocalPlayer *mLocalPlayer;\n\n        GUIController *mGUIController;\n        CellController *mCellController;\n\n        std::string server;\n        unsigned short port;\n    };\n}\n\n#endif //OPENMW_MWMP_MAIN\n"
  },
  {
    "path": "apps/openmw/mwmp/MechanicsHelper.cpp",
    "content": "#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Utils.hpp>\n\n#include <components/misc/rng.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/combat.hpp\"\n#include \"../mwmechanics/levelledlist.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n#include \"../mwmechanics/spellutil.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"MechanicsHelper.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"LocalPlayer.hpp\"\n#include \"DedicatedPlayer.hpp\"\n#include \"PlayerList.hpp\"\n#include \"ObjectList.hpp\"\n#include \"CellController.hpp\"\n\nusing namespace mwmp;\n\nosg::Vec3f MechanicsHelper::getLinearInterpolation(osg::Vec3f start, osg::Vec3f end, float percent)\n{\n    osg::Vec3f position(percent, percent, percent);\n\n    return (start + osg::componentMultiply(position, (end - start)));\n}\n\nESM::Position MechanicsHelper::getPositionFromVector(osg::Vec3f vector)\n{\n    ESM::Position position;\n    position.pos[0] = vector.x();\n    position.pos[1] = vector.y();\n    position.pos[2] = vector.z();\n\n    return position;\n}\n\n// Inspired by similar code in mwclass\\creaturelevlist.cpp\n//\n// TODO: Add handling of scaling based on leveled list's assigned scale\nvoid MechanicsHelper::spawnLeveledCreatures(MWWorld::CellStore* cellStore)\n{\n    MWWorld::CellRefList<ESM::CreatureLevList> *creatureLevList = cellStore->getCreatureLists();\n    mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n    objectList->reset();\n    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n\n    int spawnCount = 0;\n\n    for (auto &lref : creatureLevList->mList)\n    {\n        MWWorld::Ptr ptr(&lref, cellStore);\n\n        std::string id = MWMechanics::getLevelledItem(ptr.get<ESM::CreatureLevList>()->mBase, true);\n\n        if (!id.empty())\n        {\n            const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n            MWWorld::ManualRef manualRef(store, id);\n            manualRef.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition());\n            MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(manualRef.getPtr(), ptr.getCell(),\n                                                                                     ptr.getCellRef().getPosition());\n            objectList->addObjectSpawn(placed);\n            MWBase::Environment::get().getWorld()->deleteObject(placed);\n\n            spawnCount++;\n        }\n    }\n\n    if (spawnCount > 0)\n        objectList->sendObjectSpawn();\n}\n\nbool MechanicsHelper::isUsingRangedWeapon(const MWWorld::Ptr& ptr)\n{\n    if (ptr.getClass().hasInventoryStore(ptr))\n    {\n        MWWorld::InventoryStore &inventoryStore = ptr.getClass().getInventoryStore(ptr);\n        MWWorld::ContainerStoreIterator weaponSlot = inventoryStore.getSlot(\n            MWWorld::InventoryStore::Slot_CarriedRight);\n\n        if (weaponSlot != inventoryStore.end() && weaponSlot->getTypeName() == typeid(ESM::Weapon).name())\n        {\n            const ESM::Weapon* weaponRecord = weaponSlot->get<ESM::Weapon>()->mBase;\n\n            if (weaponRecord->mData.mType >= ESM::Weapon::MarksmanBow)\n                return true;\n        }\n    }\n\n    return false;\n}\n\nAttack *MechanicsHelper::getLocalAttack(const MWWorld::Ptr& ptr)\n{\n    if (ptr == MWMechanics::getPlayer())\n        return &mwmp::Main::get().getLocalPlayer()->attack;\n    else if (mwmp::Main::get().getCellController()->isLocalActor(ptr))\n        return &mwmp::Main::get().getCellController()->getLocalActor(ptr)->attack;\n\n    return nullptr;\n}\n\nAttack *MechanicsHelper::getDedicatedAttack(const MWWorld::Ptr& ptr)\n{\n    if (mwmp::PlayerList::isDedicatedPlayer(ptr))\n        return &mwmp::PlayerList::getPlayer(ptr)->attack;\n    else if (mwmp::Main::get().getCellController()->isDedicatedActor(ptr))\n        return &mwmp::Main::get().getCellController()->getDedicatedActor(ptr)->attack;\n\n    return nullptr;\n}\n\nCast *MechanicsHelper::getLocalCast(const MWWorld::Ptr& ptr)\n{\n    if (ptr == MWMechanics::getPlayer())\n        return &mwmp::Main::get().getLocalPlayer()->cast;\n    else if (mwmp::Main::get().getCellController()->isLocalActor(ptr))\n        return &mwmp::Main::get().getCellController()->getLocalActor(ptr)->cast;\n\n    return nullptr;\n}\n\nCast *MechanicsHelper::getDedicatedCast(const MWWorld::Ptr& ptr)\n{\n    if (mwmp::PlayerList::isDedicatedPlayer(ptr))\n        return &mwmp::PlayerList::getPlayer(ptr)->cast;\n    else if (mwmp::Main::get().getCellController()->isDedicatedActor(ptr))\n        return &mwmp::Main::get().getCellController()->getDedicatedActor(ptr)->cast;\n\n    return nullptr;\n}\n\nMWWorld::Ptr MechanicsHelper::getPlayerPtr(const Target& target)\n{\n    if (target.guid == mwmp::Main::get().getLocalPlayer()->guid)\n    {\n        return MWMechanics::getPlayer();\n    }\n    else\n    {\n        mwmp::DedicatedPlayer* dedicatedPlayer = mwmp::PlayerList::getPlayer(target.guid);\n\n        if (dedicatedPlayer != nullptr)\n        {\n            return dedicatedPlayer->getPtr();\n        }\n    }\n\n    return nullptr;\n}\n\nunsigned int MechanicsHelper::getActorId(const mwmp::Target& target)\n{\n    int actorId = -1;\n    MWWorld::Ptr targetPtr;\n\n    if (target.isPlayer)\n    {\n        targetPtr = getPlayerPtr(target);\n    }\n    else\n    {\n        auto controller = mwmp::Main::get().getCellController();\n        if (controller->isLocalActor(target.refNum, target.mpNum))\n        {\n            targetPtr = controller->getLocalActor(target.refNum, target.mpNum)->getPtr();\n        }\n        else if (controller->isDedicatedActor(target.refNum, target.mpNum))\n        {\n            targetPtr = controller->getDedicatedActor(target.refNum, target.mpNum)->getPtr();\n        }\n    }\n\n    if (targetPtr)\n    {\n        actorId = targetPtr.getClass().getCreatureStats(targetPtr).getActorId();\n    }\n\n    return actorId;\n}\n\nmwmp::Item MechanicsHelper::getItem(const MWWorld::Ptr& itemPtr, int count)\n{\n    mwmp::Item item;\n\n    if (itemPtr.getClass().isGold(itemPtr))\n        item.refId = MWWorld::ContainerStore::sGoldId;\n    else\n        item.refId = itemPtr.getCellRef().getRefId();\n\n    item.count = count;\n    item.charge = itemPtr.getCellRef().getCharge();\n    item.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();\n    item.soul = itemPtr.getCellRef().getSoul();\n\n    return item;\n}\n\nmwmp::Target MechanicsHelper::getTarget(const MWWorld::Ptr& ptr)\n{\n    mwmp::Target target;\n    clearTarget(target);\n\n    if (ptr != nullptr)\n    {\n        if (ptr == MWMechanics::getPlayer())\n        {\n            target.isPlayer = true;\n            target.guid = mwmp::Main::get().getLocalPlayer()->guid;\n        }\n        else if (mwmp::PlayerList::isDedicatedPlayer(ptr))\n        {\n            target.isPlayer = true;\n            target.guid = mwmp::PlayerList::getPlayer(ptr)->guid;\n        }\n        else\n        {\n            MWWorld::CellRef *ptrRef = &ptr.getCellRef();\n\n            if (ptrRef)\n            {\n                target.isPlayer = false;\n                target.refId = ptrRef->getRefId();\n                target.refNum = ptrRef->getRefNum().mIndex;\n                target.mpNum = ptrRef->getMpNum();\n                target.name = ptr.getClass().getName(ptr);\n            }\n        }\n    }\n\n    return target;\n}\n\nvoid MechanicsHelper::clearTarget(mwmp::Target& target)\n{\n    target.isPlayer = false;\n    target.refId.clear();\n    target.refNum = -1;\n    target.mpNum = -1;\n\n    target.name.clear();\n}\n\nbool MechanicsHelper::isEmptyTarget(const mwmp::Target& target)\n{\n    if (target.isPlayer == false && target.refId.empty())\n        return true;\n\n    return false;\n}\n\nvoid MechanicsHelper::assignAttackTarget(Attack* attack, const MWWorld::Ptr& target)\n{\n    if (target == MWMechanics::getPlayer())\n    {\n        attack->target.isPlayer = true;\n        attack->target.guid = mwmp::Main::get().getLocalPlayer()->guid;\n    }\n    else if (mwmp::PlayerList::isDedicatedPlayer(target))\n    {\n        attack->target.isPlayer = true;\n        attack->target.guid = mwmp::PlayerList::getPlayer(target)->guid;\n    }\n    else\n    {\n        MWWorld::CellRef *targetRef = &target.getCellRef();\n\n        attack->target.isPlayer = false;\n        attack->target.refId = targetRef->getRefId();\n        attack->target.refNum = targetRef->getRefNum().mIndex;\n        attack->target.mpNum = targetRef->getMpNum();\n    }\n}\n\nvoid MechanicsHelper::resetAttack(Attack* attack)\n{\n    attack->isHit = false;\n    attack->success = false;\n    attack->knockdown = false;\n    attack->block = false;\n    attack->applyWeaponEnchantment = false;\n    attack->applyAmmoEnchantment = false;\n    attack->hitPosition.pos[0] = attack->hitPosition.pos[1] = attack->hitPosition.pos[2] = 0;\n    attack->target.guid = RakNet::RakNetGUID();\n    attack->target.refId.clear();\n    attack->target.refNum = 0;\n    attack->target.mpNum = 0;\n}\n\nvoid MechanicsHelper::resetCast(Cast* cast)\n{\n    cast->isHit = false;\n    cast->success = false;\n    cast->target.guid = RakNet::RakNetGUID();\n    cast->target.refId.clear();\n    cast->target.refNum = 0;\n    cast->target.mpNum = 0;\n}\n\nbool MechanicsHelper::getSpellSuccess(std::string spellId, const MWWorld::Ptr& caster)\n{\n    return Misc::Rng::roll0to99() < MWMechanics::getSpellSuccessChance(spellId, caster, nullptr, true, false);\n}\n\nbool MechanicsHelper::isTeamMember(const MWWorld::Ptr& playerChecked, const MWWorld::Ptr& playerWithTeam)\n{\n    bool isTeamMember = false;\n    bool playerCheckedIsLocal = playerChecked == MWMechanics::getPlayer();\n    bool playerCheckedIsDedicated = !playerCheckedIsLocal ? mwmp::PlayerList::isDedicatedPlayer(playerChecked) : false;\n    bool playerWithTeamIsLocal = !playerCheckedIsLocal ? playerWithTeam == MWMechanics::getPlayer() : false;\n    bool playerWithTeamIsDedicated = !playerWithTeamIsLocal ? mwmp::PlayerList::isDedicatedPlayer(playerWithTeam) : false;\n\n    if (playerCheckedIsLocal || playerCheckedIsDedicated)\n    {\n        if (playerWithTeamIsLocal || playerWithTeamIsDedicated)\n        {\n            RakNet::RakNetGUID playerCheckedGuid;\n\n            if (playerCheckedIsLocal)\n                playerCheckedGuid = mwmp::Main::get().getLocalPlayer()->guid;\n            else\n                playerCheckedGuid = PlayerList::getPlayer(playerChecked)->guid;\n\n            if (playerWithTeamIsLocal)\n                isTeamMember = Utils::vectorContains(mwmp::Main::get().getLocalPlayer()->alliedPlayers, playerCheckedGuid);\n            else\n                isTeamMember = Utils::vectorContains(PlayerList::getPlayer(playerWithTeam)->alliedPlayers, playerCheckedGuid);\n        }\n    }\n\n    return isTeamMember;\n}\n\nvoid MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Processing attack from %s of type %i\",\n        attacker.getClass().getName(attacker).c_str(), attack.type);\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- pressed: %s\", attack.pressed ? \"true\" : \"false\");\n\n    if (!attack.pressed)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- success: %s\", attack.success ? \"true\" : \"false\");\n\n        if (attack.success)\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"- damage: %f\", attack.damage);\n    }\n    else\n    {\n        if (attack.type == attack.MELEE)\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"- animation: %s\", attack.attackAnimation.c_str());\n    }\n\n    MWBase::Environment::get().getMechanicsManager()->setAttackingOrSpell(attacker, attack.pressed);\n\n    MWWorld::Ptr victim;\n\n    if (attack.target.isPlayer)\n    {\n        if (attack.target.guid == mwmp::Main::get().getLocalPlayer()->guid)\n            victim = MWMechanics::getPlayer();\n        else if (PlayerList::getPlayer(attack.target.guid) != nullptr)\n            victim = PlayerList::getPlayer(attack.target.guid)->getPtr();\n    }\n    else\n    {\n        auto controller = mwmp::Main::get().getCellController();\n        if (controller->isLocalActor(attack.target.refNum, attack.target.mpNum))\n            victim = controller->getLocalActor(attack.target.refNum, attack.target.mpNum)->getPtr();\n        else if (controller->isDedicatedActor(attack.target.refNum, attack.target.mpNum))\n            victim = controller->getDedicatedActor(attack.target.refNum, attack.target.mpNum)->getPtr();\n    }\n\n    if (attack.isHit)\n    {\n        bool isRanged = attack.type == attack.RANGED;\n\n        MWWorld::Ptr weaponPtr;\n        MWWorld::Ptr ammoPtr;\n\n        bool usedTempRangedWeapon = false;\n        bool usedTempRangedAmmo = false;\n\n        // Get the attacker's current weapon\n        //\n        // Note: if using hand-to-hand, the weapon is equal to inv.end()\n        if (attacker.getClass().hasInventoryStore(attacker))\n        {\n            MWWorld::InventoryStore &inventoryStore = attacker.getClass().getInventoryStore(attacker);\n            MWWorld::ContainerStoreIterator weaponSlot = inventoryStore.getSlot(\n                MWWorld::InventoryStore::Slot_CarriedRight);\n\n            weaponPtr = weaponSlot != inventoryStore.end() ? *weaponSlot : MWWorld::Ptr();\n\n            // Is the currently selected weapon different from the one recorded for this ranged attack?\n            // If so, try to find the correct one in the attacker's inventory and use it here. If it\n            // no longer exists, add it back temporarily.\n            if (isRanged)\n            {\n                if (!weaponPtr || !Misc::StringUtils::ciEqual(weaponPtr.getCellRef().getRefId(), attack.rangedWeaponId))\n                {\n                    weaponPtr = inventoryStore.search(attack.rangedWeaponId);\n\n                    if (!weaponPtr)\n                    {\n                        weaponPtr = *attacker.getClass().getContainerStore(attacker).add(attack.rangedWeaponId, 1, attacker);\n                        usedTempRangedWeapon = true;\n                    }\n                }\n\n                if (!attack.rangedAmmoId.empty())\n                {\n                    MWWorld::ContainerStoreIterator ammoSlot = inventoryStore.getSlot(\n                        MWWorld::InventoryStore::Slot_Ammunition);\n                    ammoPtr = ammoSlot != inventoryStore.end() ? *ammoSlot : MWWorld::Ptr();\n\n                    if (!ammoPtr || !Misc::StringUtils::ciEqual(ammoPtr.getCellRef().getRefId(), attack.rangedAmmoId))\n                    {\n                        ammoPtr = inventoryStore.search(attack.rangedAmmoId);\n\n                        if (!ammoPtr)\n                        {\n                            ammoPtr = *attacker.getClass().getContainerStore(attacker).add(attack.rangedAmmoId, 1, attacker);\n                            usedTempRangedAmmo = true;\n                        }\n                    }\n                }\n            }\n\n            if (!weaponPtr.isEmpty() && weaponPtr.getTypeName() != typeid(ESM::Weapon).name())\n                weaponPtr = MWWorld::Ptr();\n        }\n\n        if (!weaponPtr.isEmpty())\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"- weapon: %s\\n- isRanged: %s\\n- applyWeaponEnchantment: %s\\n- applyAmmoEnchantment: %s\",\n                weaponPtr.getCellRef().getRefId().c_str(), isRanged ? \"true\" : \"false\", attack.applyWeaponEnchantment ? \"true\" : \"false\",\n                attack.applyAmmoEnchantment ? \"true\" : \"false\");\n\n            if (attack.applyWeaponEnchantment)\n            {\n                MWMechanics::CastSpell cast(attacker, victim, isRanged);\n                cast.mHitPosition = attack.hitPosition.asVec3();\n                cast.cast(weaponPtr, false);\n            }\n\n            if (isRanged && !ammoPtr.isEmpty() && attack.applyAmmoEnchantment)\n            {\n                MWMechanics::CastSpell cast(attacker, victim, isRanged);\n                cast.mHitPosition = attack.hitPosition.asVec3();\n                cast.cast(ammoPtr, false);\n            }\n        }\n\n        if (victim.mRef != nullptr)\n        {\n            bool isHealthDamage = true;\n\n            if (weaponPtr.isEmpty())\n            {\n                if (attacker.getClass().isBipedal(attacker))\n                {\n                    MWMechanics::CreatureStats &victimStats = victim.getClass().getCreatureStats(victim);\n                    isHealthDamage = victimStats.isParalyzed() || victimStats.getKnockedDown();\n                }\n            }\n\n            if (!isRanged)\n                MWMechanics::blockMeleeAttack(attacker, victim, weaponPtr, attack.damage, 1);\n\n            victim.getClass().onHit(victim, attack.damage, isHealthDamage, weaponPtr, attacker, attack.hitPosition.asVec3(),\n                attack.success);\n        }\n\n        // Remove temporary items that may have been added above for ranged attacks\n        if (isRanged && attacker.getClass().hasInventoryStore(attacker))\n        {\n            MWWorld::InventoryStore &inventoryStore = attacker.getClass().getInventoryStore(attacker);\n\n            if (usedTempRangedWeapon)\n                inventoryStore.remove(weaponPtr, 1, attacker);\n            \n            if (usedTempRangedAmmo)\n                inventoryStore.remove(ammoPtr, 1, attacker);\n        }\n    }\n}\n\nvoid MechanicsHelper::processCast(Cast cast, const MWWorld::Ptr& caster)\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Processing cast from %s of type %i\",\n        caster.getClass().getName(caster).c_str(), cast.type);\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- pressed: %s\", cast.pressed ? \"true\" : \"false\");\n\n    if (!cast.pressed)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- success: %s\", cast.success ? \"true\" : \"false\");\n    }\n\n    MWBase::Environment::get().getMechanicsManager()->setAttackingOrSpell(caster, cast.pressed);\n    MWMechanics::CreatureStats &casterStats = caster.getClass().getCreatureStats(caster);\n\n    MWWorld::Ptr victim;\n\n    if (cast.target.isPlayer)\n    {\n        if (cast.target.guid == mwmp::Main::get().getLocalPlayer()->guid)\n            victim = MWMechanics::getPlayer();\n        else if (PlayerList::getPlayer(cast.target.guid) != nullptr)\n            victim = PlayerList::getPlayer(cast.target.guid)->getPtr();\n    }\n    else\n    {\n        auto controller = mwmp::Main::get().getCellController();\n        if (controller->isLocalActor(cast.target.refNum, cast.target.mpNum))\n            victim = controller->getLocalActor(cast.target.refNum, cast.target.mpNum)->getPtr();\n        else if (controller->isDedicatedActor(cast.target.refNum, cast.target.mpNum))\n            victim = controller->getDedicatedActor(cast.target.refNum, cast.target.mpNum)->getPtr();\n    }\n\n    if (cast.type == cast.REGULAR)\n    {\n        casterStats.getSpells().setSelectedSpell(cast.spellId);\n\n        if (cast.success)\n            MWBase::Environment::get().getWorld()->castSpell(caster);\n\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- spellId: %s\", cast.spellId.c_str());\n    }\n    else if (cast.type == cast.ITEM)\n    {\n        casterStats.getSpells().setSelectedSpell(\"\");\n\n        MWWorld::InventoryStore& inventoryStore = caster.getClass().getInventoryStore(caster);\n\n        MWWorld::ContainerStoreIterator it = inventoryStore.begin();\n        for (; it != inventoryStore.end(); ++it)\n        {\n            if (Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), cast.itemId))\n                break;\n        }\n\n        // Add the item if it's missing\n        if (it == inventoryStore.end())\n            it = caster.getClass().getContainerStore(caster).add(cast.itemId, 1, caster);\n\n        inventoryStore.setSelectedEnchantItem(it);\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- itemId: %s\", cast.itemId.c_str());\n        MWBase::Environment::get().getWorld()->castSpell(caster);\n        inventoryStore.setSelectedEnchantItem(inventoryStore.end());\n    }\n}\n\nvoid MechanicsHelper::createSpellGfx(const MWWorld::Ptr& targetPtr, const std::vector<ESM::ActiveEffect>& mEffects)\n{\n    for (auto&& effect : mEffects)\n    {\n        const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectId);\n\n        const ESM::Static* castStatic;\n        if (!magicEffect->mHit.empty())\n            castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find(magicEffect->mHit);\n        else\n            castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find(\"VFX_DefaultHit\");\n\n        bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;\n        // Note: in case of non actor, a free effect should be fine as well\n        MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(targetPtr);\n        if (anim && !castStatic->mModel.empty())\n        {\n            anim->addEffect(\"meshes\\\\\" + castStatic->mModel, magicEffect->mIndex, loop, \"\", magicEffect->mParticle);\n        }\n    }\n}\n\nbool MechanicsHelper::isStackingSpell(const std::string& id)\n{\n    return !MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id);\n}\n\nbool MechanicsHelper::doesEffectListContainEffect(const ESM::EffectList& effectList, short effectId, short attributeId, short skillId)\n{\n    for (const auto &effect : effectList.mList)\n    {\n        if (effect.mEffectID == effectId)\n        {\n            if (attributeId == -1 || effect.mAttribute == attributeId)\n            {\n                if (skillId == -1 || effect.mSkill == skillId)\n                {\n                    return true;\n                }\n            }\n        }\n    }\n\n    return false;\n}\n\nvoid MechanicsHelper::unequipItemsByEffect(const MWWorld::Ptr& ptr, short enchantmentType, short effectId, short attributeId, short skillId)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n    MWWorld::InventoryStore &ptrInventory = ptr.getClass().getInventoryStore(ptr);\n\n    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)\n    {\n        if (ptrInventory.getSlot(slot) != ptrInventory.end())\n        {\n            MWWorld::ConstContainerStoreIterator itemIterator = ptrInventory.getSlot(slot);\n            std::string enchantmentName = itemIterator->getClass().getEnchantment(*itemIterator);\n\n            if (!enchantmentName.empty())\n            {\n                const ESM::Enchantment* enchantment = world->getStore().get<ESM::Enchantment>().find(enchantmentName);\n\n                if (enchantment->mData.mType == enchantmentType && doesEffectListContainEffect(enchantment->mEffects, effectId, attributeId, skillId))\n                    ptrInventory.unequipSlot(slot, ptr);\n            }\n        }\n    }\n}\n\nMWWorld::Ptr MechanicsHelper::getItemPtrFromStore(const mwmp::Item& item, MWWorld::ContainerStore& store)\n{\n    MWWorld::Ptr closestPtr;\n\n    for (MWWorld::ContainerStoreIterator storeIterator = store.begin(); storeIterator != store.end(); ++storeIterator)\n    {\n        // Enchantment charges are often in the process of refilling themselves, so don't check for them here\n        if (Misc::StringUtils::ciEqual(item.refId, storeIterator->getCellRef().getRefId()) &&\n            item.count == storeIterator->getRefData().getCount() &&\n            item.charge == storeIterator->getCellRef().getCharge() &&\n            Misc::StringUtils::ciEqual(item.soul, storeIterator->getCellRef().getSoul()))\n        {\n            // If we have no closestPtr, set it to the Ptr corresponding to this storeIterator; otherwise, make\n            // sure the storeIterator's enchantmentCharge is closer to our goal than that of the previous closestPtr\n            if (!closestPtr || abs(storeIterator->getCellRef().getEnchantmentCharge() - item.enchantmentCharge) <\n                abs(closestPtr.getCellRef().getEnchantmentCharge() - item.enchantmentCharge))\n            {\n                closestPtr = *storeIterator;\n            }\n        }\n    }\n\n    return closestPtr;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/MechanicsHelper.hpp",
    "content": "#ifndef OPENMW_MECHANICSHELPER_HPP\n#define OPENMW_MECHANICSHELPER_HPP\n\n#include <components/openmw-mp/Base/BaseStructs.hpp>\n\n#include \"../mwworld/containerstore.hpp\"\n\n#include <osg/Vec3f>\n\n\nnamespace MechanicsHelper\n{\n    osg::Vec3f getLinearInterpolation(osg::Vec3f start, osg::Vec3f end, float percent);\n    ESM::Position getPositionFromVector(osg::Vec3f vector);\n\n    void spawnLeveledCreatures(MWWorld::CellStore* cellStore);\n\n    bool isUsingRangedWeapon(const MWWorld::Ptr& ptr);\n\n    mwmp::Attack *getLocalAttack(const MWWorld::Ptr& ptr);\n    mwmp::Attack *getDedicatedAttack(const MWWorld::Ptr& ptr);\n\n    mwmp::Cast *getLocalCast(const MWWorld::Ptr& ptr);\n    mwmp::Cast *getDedicatedCast(const MWWorld::Ptr& ptr);\n\n    MWWorld::Ptr getPlayerPtr(const mwmp::Target& target);\n    unsigned int getActorId(const mwmp::Target& target);\n\n    mwmp::Item getItem(const MWWorld::Ptr& itemPtr, int count);\n    mwmp::Target getTarget(const MWWorld::Ptr& ptr);\n    void clearTarget(mwmp::Target& target);\n    bool isEmptyTarget(const mwmp::Target& target);\n\n    void assignAttackTarget(mwmp::Attack* attack, const MWWorld::Ptr& target);\n    void resetAttack(mwmp::Attack* attack);\n    void resetCast(mwmp::Cast* cast);\n\n    // See whether playerChecked belongs to playerWithTeam's team\n    // Note: This is not supposed to also check if playerWithTeam is on playerChecked's\n    //       team, because it should technically be possible to be allied to someone\n    //       who isn't mutually allied to you\n    bool isTeamMember(const MWWorld::Ptr& playerChecked, const MWWorld::Ptr& playerWithTeam);\n\n    bool getSpellSuccess(std::string spellId, const MWWorld::Ptr& caster);\n\n    void processAttack(mwmp::Attack attack, const MWWorld::Ptr& attacker);\n    void processCast(mwmp::Cast cast, const MWWorld::Ptr& caster);\n\n    void createSpellGfx(const MWWorld::Ptr& targetPtr, const std::vector<ESM::ActiveEffect>& mEffects);\n\n    bool isStackingSpell(const std::string& id);\n    bool doesEffectListContainEffect(const ESM::EffectList& effectList, short effectId, short attributeId = -1, short skillId = -1);\n    void unequipItemsByEffect(const MWWorld::Ptr& ptr, short enchantmentType, short effectId, short attributeId = -1, short skillId = -1);\n\n    MWWorld::Ptr getItemPtrFromStore(const mwmp::Item& item, MWWorld::ContainerStore& store);\n}\n\n\n#endif //OPENMW_MECHANICSHELPER_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/Networking.cpp",
    "content": "#include <stdexcept>\n#include <iostream>\n#include <string>\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Utils.hpp>\n#include <components/openmw-mp/Version.hpp>\n#include <components/openmw-mp/Packets/PacketPreInit.hpp>\n\n#include <components/esm/cellid.hpp>\n#include <components/files/configurationmanager.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwclass/npc.hpp\"\n\n#include \"../mwmechanics/combat.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n\n#include \"../mwstate/statemanagerimp.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include <SDL_messagebox.h>\n#include <RakSleep.h>\n#include <iomanip>\n#include <components/version/version.hpp>\n\n#include \"Networking.hpp\"\n#include \"Main.hpp\"\n#include \"processors/ProcessorInitializer.hpp\"\n#include \"processors/SystemProcessor.hpp\"\n#include \"processors/PlayerProcessor.hpp\"\n#include \"processors/ObjectProcessor.hpp\"\n#include \"processors/ActorProcessor.hpp\"\n#include \"processors/WorldstateProcessor.hpp\"\n#include \"GUIController.hpp\"\n#include \"CellController.hpp\"\n\nusing namespace mwmp;\n\nstd::string listDiscrepancies(PacketPreInit::PluginContainer checksums, PacketPreInit::PluginContainer checksumsResponse)\n{\n    std::ostringstream sstr;\n    sstr << \"Your plugins or their load order don't match the server's. A full comparison is included in your debug window and latest log file. In short, the following discrepancies have been found:\\n\\n\";\n\n    int discrepancyCount = 0;\n\n    for (size_t fileIndex = 0; fileIndex < checksums.size() || fileIndex < checksumsResponse.size(); fileIndex++)\n    {\n        if (fileIndex >= checksumsResponse.size())\n        {\n            discrepancyCount++;\n\n            if (discrepancyCount > 1)\n                sstr << \"\\n\";\n\n            std::string clientFilename = checksums.at(fileIndex).first;\n\n            sstr << fileIndex << \": \";\n            sstr << clientFilename << \" is past the number of plugins used by the server\";\n        }\n        else if (fileIndex >= checksums.size())\n        {\n            discrepancyCount++;\n\n            if (discrepancyCount > 1)\n                sstr << \"\\n\";\n\n            std::string serverFilename = checksumsResponse.at(fileIndex).first;\n\n            sstr << fileIndex << \": \";\n            sstr << serverFilename << \" is completely missing from the client but required by the server\";\n        }\n        else\n        {\n            std::string clientFilename = checksums.at(fileIndex).first;\n            std::string serverFilename = checksumsResponse.at(fileIndex).first;\n\n            std::string clientChecksum = Utils::intToHexStr(checksums.at(fileIndex).second.at(0));\n\n            bool filenameMatches = false;\n            bool checksumMatches = false;\n            std::string eligibleChecksums = \"\";\n\n            if (Misc::StringUtils::ciEqual(clientFilename, serverFilename))\n                filenameMatches = true;\n\n            if (checksumsResponse.at(fileIndex).second.size() > 0)\n            {\n                for (size_t checksumIndex = 0; checksumIndex < checksumsResponse.at(fileIndex).second.size(); checksumIndex++)\n                {\n                    std::string serverChecksum = Utils::intToHexStr(checksumsResponse.at(fileIndex).second.at(checksumIndex));\n\n                    if (checksumIndex != 0)\n                        eligibleChecksums = eligibleChecksums + \" or \";\n\n                    eligibleChecksums = eligibleChecksums + serverChecksum;\n\n                    if (Misc::StringUtils::ciEqual(clientChecksum, serverChecksum))\n                    {\n                        checksumMatches = true;\n                        break;\n                    }\n                }\n            }\n            else\n                checksumMatches = true;\n\n            if (!filenameMatches || !checksumMatches)\n            {\n                discrepancyCount++;\n\n                if (discrepancyCount > 1)\n                    sstr << \"\\n\";\n\n                sstr << fileIndex << \": \";\n\n                if (!filenameMatches)\n                    sstr << clientFilename << \" doesn't match \" << serverFilename;\n\n                if (!filenameMatches && !checksumMatches)\n                    sstr << \", \";\n\n                if (!checksumMatches)\n                    sstr << \"checksum \" << clientChecksum << \" doesn't match \" << eligibleChecksums;\n            }\n        }\n    }\n\n    return sstr.str();\n}\n\nstd::string listComparison(PacketPreInit::PluginContainer checksums, PacketPreInit::PluginContainer checksumsResponse,\n                      bool full = false)\n{\n    std::ostringstream sstr;\n    size_t pluginNameLen1 = 0;\n    size_t pluginNameLen2 = 0;\n    for (const auto &checksum : checksums)\n        if (pluginNameLen1 < checksum.first.size())\n            pluginNameLen1 = checksum.first.size();\n\n    for (const auto &checksum : checksums)\n        if (pluginNameLen2 < checksum.first.size())\n            pluginNameLen2 = checksum.first.size();\n\n    Utils::printWithWidth(sstr, \"Your current plugins are:\", pluginNameLen1 + 16);\n    sstr << \"To join this server, use:\\n\";\n\n    Utils::printWithWidth(sstr, \"name\", pluginNameLen1 + 2);\n    Utils::printWithWidth(sstr, \"hash\", 14);\n    Utils::printWithWidth(sstr, \"name\", pluginNameLen2 + 2);\n    sstr << \"hash\\n\";\n\n    for (size_t i = 0; i < checksums.size() || i < checksumsResponse.size(); i++)\n    {\n        std::string plugin;\n        unsigned val;\n\n        if (i < checksums.size())\n        {\n            plugin = checksums.at(i).first;\n            val = checksums.at(i).second[0];\n\n            Utils::printWithWidth(sstr, plugin, pluginNameLen1 + 2);\n            Utils::printWithWidth(sstr, Utils::intToHexStr(val), 14);\n        }\n        else\n            Utils::printWithWidth(sstr, \"\", pluginNameLen1 + 16);\n\n        if (i < checksumsResponse.size())\n        {\n            Utils::printWithWidth(sstr, checksumsResponse[i].first, pluginNameLen2 + 2);\n            if (checksumsResponse[i].second.size() > 0)\n            {\n                if (full)\n                    for (size_t j = 0; j < checksumsResponse[i].second.size(); j++)\n                        Utils::printWithWidth(sstr, Utils::intToHexStr(checksumsResponse[i].second[j]), 14);\n                else\n                    sstr << Utils::intToHexStr(checksumsResponse[i].second[0]);\n            }\n            else\n                sstr << \"any\";\n        }\n\n        sstr << \"\\n\";\n    }\n\n    return sstr.str();\n}\n\nNetworking::Networking(): peer(RakNet::RakPeerInterface::GetInstance()), systemPacketController(peer),\n    playerPacketController(peer), actorPacketController(peer), objectPacketController(peer),\n    worldstatePacketController(peer)\n{\n\n    RakNet::SocketDescriptor sd;\n    sd.port=0;\n    auto b = peer->Startup(1, &sd, 1);\n    RakAssert(b==RakNet::CRABNET_STARTED);\n\n    systemPacketController.SetStream(0, &bsOut);\n    playerPacketController.SetStream(0, &bsOut);\n    actorPacketController.SetStream(0, &bsOut);\n    objectPacketController.SetStream(0, &bsOut);\n    worldstatePacketController.SetStream(0, &bsOut);\n\n    connected = 0;\n    ProcessorInitializer();\n}\n\nNetworking::~Networking()\n{\n    peer->Shutdown(100);\n    peer->CloseConnection(peer->GetSystemAddressFromIndex(0), true, 0);\n    RakNet::RakPeerInterface::DestroyInstance(peer);\n}\n\nvoid Networking::update()\n{\n    RakNet::Packet *packet;\n    std::string errmsg = \"\";\n\n    for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())\n    {\n        switch (packet->data[0])\n        {\n            case ID_REMOTE_DISCONNECTION_NOTIFICATION:\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Another client has disconnected.\");\n                break;\n            case ID_REMOTE_CONNECTION_LOST:\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Another client has lost connection.\");\n                break;\n            case ID_REMOTE_NEW_INCOMING_CONNECTION:\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Another client has connected.\");\n                break;\n            case ID_CONNECTION_REQUEST_ACCEPTED:\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Our connection request has been accepted.\");\n                break;\n            case ID_NEW_INCOMING_CONNECTION:\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"A connection is incoming.\");\n                break;\n            case ID_NO_FREE_INCOMING_CONNECTIONS:\n                errmsg = \"The server is full.\";\n                break;\n            case ID_DISCONNECTION_NOTIFICATION:\n                errmsg = \"We have been disconnected.\";\n                break;\n            case ID_CONNECTION_LOST:\n                errmsg = \"Connection lost.\";\n                break;\n            default:\n                receiveMessage(packet);\n                //LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Message with identifier %i has arrived.\", packet->data[0]);\n                break;\n        }\n    }\n\n    if (!errmsg.empty())\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, errmsg.c_str());\n        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, \"tes3mp\", errmsg.c_str(), 0);\n        MWBase::Environment::get().getStateManager()->requestQuit();\n    }\n}\n\nvoid Networking::connect(const std::string &ip, unsigned short port, std::vector<std::string> &content, Files::Collections &collections)\n{\n    RakNet::SystemAddress master;\n    master.SetBinaryAddress(ip.c_str());\n    master.SetPortHostOrder(port);\n    std::string errmsg = \"\";\n\n    std::stringstream sstr;\n    sstr << TES3MP_VERSION;\n    sstr << TES3MP_PROTO_VERSION;\n    std::string commitHashString = Version::getOpenmwVersion(Main::getResDir()).mCommitHash;\n    // Remove carriage returns added to version file on Windows\n    commitHashString.erase(std::remove(commitHashString.begin(), commitHashString.end(), '\\r'), commitHashString.end());\n    sstr << commitHashString;\n\n    if (peer->Connect(master.ToString(false), master.GetPort(), sstr.str().c_str(), (int) sstr.str().size(), 0, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)\n        errmsg = \"Connection attempt failed.\\n\";\n\n    bool queue = true;\n    while (queue)\n    {\n        for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())\n        {\n            switch (packet->data[0])\n            {\n                case ID_CONNECTION_ATTEMPT_FAILED:\n                {\n                    errmsg = \"Connection failed.\\n\"\n                            \"Either the IP address is wrong or a firewall on either system is blocking\\n\"\n                            \"UDP packets on the port you have chosen.\";\n                    queue = false;\n                    break;\n                }\n                case ID_INVALID_PASSWORD:\n                {\n                    errmsg = \"Version mismatch!\\nYour client is on version \" TES3MP_VERSION \"\\n\"\n                        \"Please make sure the server is on the same version.\";\n                    queue = false;\n                    break;\n                }\n                case ID_INCOMPATIBLE_PROTOCOL_VERSION:\n                {\n                    errmsg = \"Network protocol mismatch!\\nMake sure your client is really on the same version\\n\"\n                        \"as the server you are trying to connect to.\";\n                    queue = false;\n                    break;\n                }\n                case ID_CONNECTION_REQUEST_ACCEPTED:\n                {\n                    serverAddr = packet->systemAddress;\n                    BaseClientPacketProcessor::SetServerAddr(packet->systemAddress);\n\n                    connected = true;\n                    queue = false;\n\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Received ID_CONNECTION_REQUESTED_ACCEPTED from %s\",\n                                       serverAddr.ToString());\n\n                    break;\n                }\n                case ID_DISCONNECTION_NOTIFICATION:\n                    throw std::runtime_error(\"ID_DISCONNECTION_NOTIFICATION.\\n\");\n                case ID_CONNECTION_BANNED:\n                    throw std::runtime_error(\"You have been banned from this server.\\n\");\n                case ID_CONNECTION_LOST:\n                    throw std::runtime_error(\"ID_CONNECTION_LOST.\\n\");\n                default:\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Connection message with identifier %i has arrived in initialization.\",\n                                       packet->data[0]);\n            }\n        }\n    }\n\n    if (!errmsg.empty())\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, errmsg.c_str());\n        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, \"tes3mp\", errmsg.c_str(), 0);\n    }\n    else\n        preInit(content, collections);\n\n    getLocalPlayer()->guid = getLocalSystem()->guid = peer->GetMyGUID();\n}\n\nvoid Networking::preInit(std::vector<std::string> &content, Files::Collections &collections)\n{\n    PacketPreInit::PluginContainer checksums;\n    std::vector<std::string>::const_iterator it(content.begin());\n    for (int idx = 0; it != content.end(); ++it, ++idx)\n    {\n        boost::filesystem::path filename(*it);\n        const Files::MultiDirCollection& col = collections.getCollection(filename.extension().string());\n        if (col.doesExist(*it))\n        {\n            PacketPreInit::HashList hashList;\n            unsigned crc32 = Utils::crc32Checksum(col.getPath(*it).string());\n            hashList.push_back(crc32);\n            checksums.push_back(make_pair(*it, hashList));\n\n            LOG_APPEND(TimedLog::LOG_WARN, \"idx: %d\\tchecksum: %X\\tfile: %s\\n\", idx, crc32, col.getPath(*it).string().c_str());\n        }\n        else\n            throw std::runtime_error(\"Plugin doesn't exist.\");\n    }\n\n    PacketPreInit packetPreInit(peer);\n    RakNet::BitStream bs;\n    RakNet::RakNetGUID guid;\n    packetPreInit.setChecksums(&checksums);\n    packetPreInit.setGUID(guid);\n    packetPreInit.SetSendStream(&bs);\n    packetPreInit.Send(serverAddr);\n\n    PacketPreInit::PluginContainer checksumsResponse;\n    bool done = false;\n    while (!done)\n    {\n        RakNet::Packet *packet = peer->Receive();\n        if (!packet)\n        {\n            RakSleep(500);\n            continue;\n        }\n\n        RakNet::BitStream bsIn(&packet->data[0], packet->length, false);\n        unsigned char packetId;\n        bsIn.Read(packetId);\n        switch(packetId)\n        {\n            case ID_DISCONNECTION_NOTIFICATION:\n            case ID_CONNECTION_LOST:\n                done = true;\n                break;\n            case ID_GAME_PREINIT:\n                bsIn.IgnoreBytes((unsigned) RakNet::RakNetGUID::size());\n                packetPreInit.setChecksums(&checksumsResponse);\n                packetPreInit.Packet(&bsIn, false);\n                done = true;\n                break;\n        }\n\n        peer->DeallocatePacket(packet);\n    }\n\n    if (!checksumsResponse.empty()) // something wrong\n    {\n        std::string errmsg = listDiscrepancies(checksums, checksumsResponse);\n\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, listDiscrepancies(checksums, checksumsResponse).c_str());\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, listComparison(checksums, checksumsResponse, true).c_str());\n        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, \"tes3mp\", errmsg.c_str(), 0);\n        connected = false;\n    }\n}\n\nvoid Networking::receiveMessage(RakNet::Packet *packet)\n{\n    if (packet->length < 2)\n        return;\n\n    if (systemPacketController.ContainsPacket(packet->data[0]))\n    {\n        if (!SystemProcessor::Process(*packet))\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled SystemPacket with identifier %i has arrived\", packet->data[0]);\n    }\n    else if (playerPacketController.ContainsPacket(packet->data[0]))\n    {\n        if (!PlayerProcessor::Process(*packet))\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled PlayerPacket with identifier %i has arrived\", packet->data[0]);\n    }\n    else if (actorPacketController.ContainsPacket(packet->data[0]))\n    {\n        if (!ActorProcessor::Process(*packet, actorList))\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled ActorPacket with identifier %i has arrived\", packet->data[0]);\n    }\n    else if (objectPacketController.ContainsPacket(packet->data[0]))\n    {\n        if (!ObjectProcessor::Process(*packet, objectList))\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled ObjectPacket with identifier %i has arrived\", packet->data[0]);\n    }\n    else if (worldstatePacketController.ContainsPacket(packet->data[0]))\n    {\n        if (!WorldstateProcessor::Process(*packet, worldstate))\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled WorldstatePacket with identifier %i has arrived\", packet->data[0]);\n    }\n}\n\nSystemPacket *Networking::getSystemPacket(RakNet::MessageID id)\n{\n    return systemPacketController.GetPacket(id);\n}\n\nPlayerPacket *Networking::getPlayerPacket(RakNet::MessageID id)\n{\n    return playerPacketController.GetPacket(id);\n}\n\nActorPacket *Networking::getActorPacket(RakNet::MessageID id)\n{\n    return actorPacketController.GetPacket(id);\n}\n\nObjectPacket *Networking::getObjectPacket(RakNet::MessageID id)\n{\n    return objectPacketController.GetPacket(id);\n}\n\nWorldstatePacket *Networking::getWorldstatePacket(RakNet::MessageID id)\n{\n    return worldstatePacketController.GetPacket(id);\n}\n\nLocalSystem *Networking::getLocalSystem()\n{\n    return mwmp::Main::get().getLocalSystem();\n}\n\nLocalPlayer *Networking::getLocalPlayer()\n{\n    return mwmp::Main::get().getLocalPlayer();\n}\n\nActorList *Networking::getActorList()\n{\n    return &actorList;\n}\n\nObjectList *Networking::getObjectList()\n{\n    return &objectList;\n}\n\nWorldstate *Networking::getWorldstate()\n{\n    return &worldstate;\n}\n\nbool Networking::isConnected()\n{\n    return connected;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/Networking.hpp",
    "content": "#ifndef OPENMW_NETWORKING_HPP\n#define OPENMW_NETWORKING_HPP\n\n#include <RakPeerInterface.h>\n#include <BitStream.h>\n#include <string>\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <components/openmw-mp/Controllers/SystemPacketController.hpp>\n#include <components/openmw-mp/Controllers/PlayerPacketController.hpp>\n#include <components/openmw-mp/Controllers/ActorPacketController.hpp>\n#include <components/openmw-mp/Controllers/ObjectPacketController.hpp>\n#include <components/openmw-mp/Controllers/WorldstatePacketController.hpp>\n\n#include <components/files/collections.hpp>\n\n#include \"LocalSystem.hpp\"\n#include \"ActorList.hpp\"\n#include \"ObjectList.hpp\"\n#include \"Worldstate.hpp\"\n\nnamespace mwmp\n{\n    class LocalPlayer;\n\n    class Networking\n    {\n    public:\n        Networking();\n        ~Networking();\n        void connect(const std::string& ip, unsigned short port, std::vector<std::string> &content, Files::Collections &collections);\n        void update();\n\n        SystemPacket *getSystemPacket(RakNet::MessageID id);\n        PlayerPacket *getPlayerPacket(RakNet::MessageID id);\n        ActorPacket *getActorPacket(RakNet::MessageID id);\n        ObjectPacket *getObjectPacket(RakNet::MessageID id);\n        WorldstatePacket *getWorldstatePacket(RakNet::MessageID id);\n\n        RakNet::SystemAddress serverAddress()\n        {\n            return serverAddr;\n        }\n\n        bool isConnected();\n\n        LocalSystem *getLocalSystem();\n        LocalPlayer *getLocalPlayer();\n        ActorList *getActorList();\n        ObjectList *getObjectList();\n        Worldstate *getWorldstate();\n\n    private:\n        bool connected;\n        RakNet::RakPeerInterface *peer;\n        RakNet::SystemAddress serverAddr;\n        RakNet::BitStream bsOut;\n\n        SystemPacketController systemPacketController;\n        PlayerPacketController playerPacketController;\n        ActorPacketController actorPacketController;\n        ObjectPacketController objectPacketController;\n        WorldstatePacketController worldstatePacketController;\n\n        ActorList actorList;\n        ObjectList objectList;\n        Worldstate worldstate;\n\n        void receiveMessage(RakNet::Packet *packet);\n\n        void preInit(std::vector<std::string> &content, Files::Collections &collections);\n    };\n}\n\n\n#endif //OPENMW_NETWORKING_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/ObjectList.cpp",
    "content": "#include \"ObjectList.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"MechanicsHelper.hpp\"\n#include \"LocalPlayer.hpp\"\n#include \"DedicatedPlayer.hpp\"\n#include \"PlayerList.hpp\"\n#include \"CellController.hpp\"\n#include \"RecordHelper.hpp\"\n\n#include <components/translation/translation.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwgui/container.hpp\"\n#include \"../mwgui/dialogue.hpp\"\n#include \"../mwgui/inventorywindow.hpp\"\n#include \"../mwgui/windowmanagerimp.hpp\"\n\n#include \"../mwmechanics/aifollow.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n#include \"../mwmechanics/summoning.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n\n#include \"../mwscript/interpretercontext.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/timestamp.hpp\"\n\nusing namespace mwmp;\n\nObjectList::ObjectList()\n{\n\n}\n\nObjectList::~ObjectList()\n{\n\n}\n\nNetworking *ObjectList::getNetworking()\n{\n    return mwmp::Main::get().getNetworking();\n}\n\nvoid ObjectList::reset()\n{\n    cell.blank();\n    baseObjects.clear();\n    guid = mwmp::Main::get().getNetworking()->getLocalPlayer()->guid;\n\n    action = -1;\n    containerSubAction = 0;\n}\n\nvoid ObjectList::addBaseObject(BaseObject baseObject)\n{\n    baseObjects.push_back(baseObject);\n}\n\nmwmp::BaseObject ObjectList::getBaseObjectFromPtr(const MWWorld::Ptr& ptr)\n{\n    mwmp::BaseObject baseObject;\n\n    if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())\n    {\n        baseObject.isPlayer = true;\n        baseObject.guid = mwmp::Main::get().getLocalPlayer()->guid;\n    }\n    else if (mwmp::PlayerList::isDedicatedPlayer(ptr))\n    {\n        baseObject.isPlayer = true;\n        baseObject.guid = mwmp::PlayerList::getPlayer(ptr)->guid;\n    }\n    else\n    {\n        baseObject.isPlayer = false;\n        baseObject.refId = ptr.getCellRef().getRefId();\n        baseObject.refNum = ptr.getCellRef().getRefNum().mIndex;\n        baseObject.mpNum = ptr.getCellRef().getMpNum();\n    }\n\n    return baseObject;\n}\n\nvoid ObjectList::addContainerItem(mwmp::BaseObject& baseObject, const MWWorld::Ptr& itemPtr, int itemCount, int actionCount)\n{\n    mwmp::ContainerItem containerItem;\n    containerItem.refId = itemPtr.getCellRef().getRefId();\n    containerItem.count = itemCount;\n    containerItem.charge = itemPtr.getCellRef().getCharge();\n    containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();\n    containerItem.soul = itemPtr.getCellRef().getSoul();\n    containerItem.actionCount = actionCount;\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"--- Adding container item %s to packet with count %i and actionCount %i\",\n        containerItem.refId.c_str(), itemCount, actionCount);\n\n    baseObject.containerItems.push_back(containerItem);\n}\n\nvoid ObjectList::addContainerItem(mwmp::BaseObject& baseObject, const MWGui::ItemStack& itemStack, int itemCount, int actionCount)\n{\n    mwmp::ContainerItem containerItem;\n    containerItem.refId = itemStack.mBase.getCellRef().getRefId();\n    containerItem.count = itemCount;\n    containerItem.charge = itemStack.mBase.getCellRef().getCharge();\n    containerItem.enchantmentCharge = itemStack.mBase.getCellRef().getEnchantmentCharge();\n    containerItem.soul = itemStack.mBase.getCellRef().getSoul();\n    containerItem.actionCount = actionCount;\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"--- Adding container item %s to packet with count %i and actionCount %i\",\n        containerItem.refId.c_str(), itemCount, actionCount);\n\n    baseObject.containerItems.push_back(containerItem);\n}\n\nvoid ObjectList::addContainerItem(mwmp::BaseObject& baseObject, const std::string itemId, int itemCount, int actionCount)\n{\n    mwmp::ContainerItem containerItem;\n    containerItem.refId = itemId;\n    containerItem.count = itemCount;\n    containerItem.charge = -1;\n    containerItem.enchantmentCharge = -1;\n    containerItem.soul = \"\";\n    containerItem.actionCount = actionCount;\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"--- Adding container item %s to packet with count %i and actionCount %i\",\n        containerItem.refId.c_str(), itemCount, actionCount);\n\n    baseObject.containerItems.push_back(containerItem);\n}\n\nvoid ObjectList::addEntireContainer(const MWWorld::Ptr& ptr)\n{\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Adding entire container %s %i-%i\", ptr.getCellRef().getRefId().c_str(),\n        ptr.getCellRef().getRefNum().mIndex, ptr.getCellRef().getMpNum());\n\n    MWWorld::ContainerStore& containerStore = ptr.getClass().getContainerStore(ptr);\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n\n    // If the container store has not been populated with items yet, handle that now\n    if (!containerStore.isResolved())\n        containerStore.resolve();\n\n    for (const auto itemPtr : containerStore)\n    {\n        addContainerItem(baseObject, itemPtr, itemPtr.getRefData().getCount(), itemPtr.getRefData().getCount());\n    }\n\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::editContainers(MWWorld::CellStore* cellStore)\n{\n    bool isLocalEvent = guid == Main::get().getLocalPlayer()->guid;\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- isLocalEvent? %s\", isLocalEvent ? \"true\" : \"false\");\n\n    BaseObject baseObject;\n\n    for (unsigned int i = 0; i < baseObjectCount; i++)\n    {\n        baseObject = baseObjects.at(i);\n\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- container %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            bool isCurrentContainer = false;\n            bool hasActorEquipment = ptrFound.getClass().isActor() && ptrFound.getClass().hasInventoryStore(ptrFound);\n\n            // If we are in a container, and it happens to be this container, keep track of that\n            if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container))\n            {\n                CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer;\n\n                if (currentContainer->refNum == ptrFound.getCellRef().getRefNum().mIndex &&\n                    currentContainer->mpNum == ptrFound.getCellRef().getMpNum())\n                {\n                    isCurrentContainer = true;\n                }\n            }\n\n            MWWorld::ContainerStore& containerStore = ptrFound.getClass().getContainerStore(ptrFound);\n\n            // If we are setting the entire contents, clear the current ones\n            if (action == BaseObjectList::SET)\n            {\n                containerStore.setResolved(true);\n                containerStore.clear();\n            }\n\n            bool isLocalDrag = isLocalEvent && containerSubAction == BaseObjectList::DRAG;\n            bool isLocalTakeAll = isLocalEvent && containerSubAction == BaseObjectList::TAKE_ALL;\n            std::string takeAllSound = \"\";\n\n            MWWorld::Ptr ownerPtr = ptrFound.getClass().isActor() ? ptrFound : MWBase::Environment::get().getWorld()->getPlayerPtr();\n\n            for (const auto &containerItem : baseObject.containerItems)\n            {\n                //LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- containerItem %s, count: %i, actionCount: %i\",\n                //    containerItem.refId.c_str(), containerItem.count, containerItem.actionCount);\n\n                if (containerItem.refId.find(\"$dynamic\") != std::string::npos)\n                    continue;\n\n                if (action == BaseObjectList::SET || action == BaseObjectList::ADD)\n                {\n                    // Create a ManualRef to be able to set item charge\n                    MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), containerItem.refId, 1);\n                    MWWorld::Ptr newPtr = ref.getPtr();\n\n                    if (containerItem.count > 1)\n                        newPtr.getRefData().setCount(containerItem.count);\n\n                    if (containerItem.charge > -1)\n                        newPtr.getCellRef().setCharge(containerItem.charge);\n\n                    if (containerItem.enchantmentCharge > -1)\n                        newPtr.getCellRef().setEnchantmentCharge(containerItem.enchantmentCharge);\n\n                    if (!containerItem.soul.empty())\n                        newPtr.getCellRef().setSoul(containerItem.soul);\n\n                    containerStore.add(newPtr, containerItem.count, ownerPtr);\n                }\n\n                else if (action == BaseObjectList::REMOVE && containerItem.actionCount > 0)\n                {\n                    // We have to find the right item ourselves because ContainerStore has no method\n                    // accounting for charge\n                    for (const auto itemPtr : containerStore)\n                    {\n                        if (Misc::StringUtils::ciEqual(itemPtr.getCellRef().getRefId(), containerItem.refId))\n                        {\n                            if (itemPtr.getCellRef().getCharge() == containerItem.charge &&\n                                itemPtr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge &&\n                                Misc::StringUtils::ciEqual(itemPtr.getCellRef().getSoul(), containerItem.soul))\n                            {\n                                // Store the sound of the first item in a TAKE_ALL\n                                if (isLocalTakeAll && takeAllSound.empty())\n                                    takeAllSound = itemPtr.getClass().getUpSoundId(itemPtr);\n\n                                // Is this an actor's container? If so, unequip this item if it was equipped\n                                if (hasActorEquipment)\n                                {\n                                    MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound);\n\n                                    if (invStore.isEquipped(itemPtr))\n                                        invStore.unequipItemQuantity(itemPtr, ptrFound, containerItem.count);\n                                }\n\n                                bool isDragResolved = false;\n\n                                if (isLocalDrag && isCurrentContainer)\n                                {\n                                    MWGui::ContainerWindow* containerWindow = MWBase::Environment::get().getWindowManager()->getContainerWindow();\n\n                                    if (!containerWindow->isOnDragAndDrop())\n                                    {\n                                        isDragResolved = containerWindow->dragItemByPtr(itemPtr, containerItem.actionCount);\n                                    }\n                                }\n\n                                if (!isLocalDrag || !isDragResolved)\n                                {\n                                    containerStore.remove(itemPtr, containerItem.actionCount, ownerPtr);\n\n                                    if (isLocalDrag || isLocalTakeAll)\n                                    {\n                                        MWWorld::Ptr ptrPlayer = MWBase::Environment::get().getWorld()->getPlayerPtr();\n                                        MWWorld::ContainerStore &playerStore = ptrPlayer.getClass().getContainerStore(ptrPlayer);\n                                        *playerStore.add(itemPtr, containerItem.actionCount, ownerPtr, false);\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            // Was this a SET or ADD action on an actor's container, and are we the authority\n            // over the actor? If so, autoequip the actor\n            if ((action == BaseObjectList::ADD || action == BaseObjectList::SET) && hasActorEquipment &&\n                mwmp::Main::get().getCellController()->isLocalActor(ptrFound))\n            {\n                MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound);\n                invStore.autoEquip(ptrFound);\n                mwmp::Main::get().getCellController()->getLocalActor(ptrFound)->updateEquipment(true, true);\n            }\n\n            // If this container can be harvested, disable and then enable it again to refresh its animation\n            if (ptrFound.getClass().canBeHarvested(ptrFound))\n            {\n                MWBase::Environment::get().getWorld()->disable(ptrFound);\n                MWBase::Environment::get().getWorld()->enable(ptrFound);\n            }\n\n            // If this container was open for us, update its view\n            if (isCurrentContainer)\n            {\n                if (isLocalTakeAll)\n                {\n                    MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);\n                    MWBase::Environment::get().getWindowManager()->playSound(takeAllSound);\n                }\n                else\n                {\n                    MWGui::ContainerWindow* containerWindow = MWBase::Environment::get().getWindowManager()->getContainerWindow();\n                    containerWindow->setPtr(ptrFound);\n                }\n            }\n        }\n    }\n}\n\nvoid ObjectList::activateObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        MWWorld::Ptr ptrFound;\n\n        if (baseObject.isPlayer)\n        {\n            if (baseObject.guid == Main::get().getLocalPlayer()->guid)\n            {\n                ptrFound = Main::get().getLocalPlayer()->getPlayerPtr();\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Activated object is local player\");\n            }\n            else\n            {\n                DedicatedPlayer *player = PlayerList::getPlayer(baseObject.guid);\n\n                if (player != 0)\n                {\n                    ptrFound = player->getPtr();\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Activated object is player %s\", player->npc.mName.c_str());\n                }\n                else\n                {\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Could not find player to activate!\");\n                }\n            }\n        }\n        else\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Activated object is %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n            ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n        }\n\n        if (ptrFound)\n        {\n            MWWorld::Ptr activatingActorPtr;\n\n            if (baseObject.activatingActor.isPlayer)\n            {\n                activatingActorPtr = MechanicsHelper::getPlayerPtr(baseObject.activatingActor);\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Object has been activated by player %s\",\n                    activatingActorPtr.getClass().getName(activatingActorPtr).c_str());\n            }\n            else\n            {\n                activatingActorPtr = cellStore->searchExact(baseObject.activatingActor.refNum, baseObject.activatingActor.mpNum, baseObject.activatingActor.refId);\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Object has been activated by actor %s %i-%i\", activatingActorPtr.getCellRef().getRefId().c_str(),\n                    activatingActorPtr.getCellRef().getRefNum().mIndex, activatingActorPtr.getCellRef().getMpNum());\n            }\n\n            if (activatingActorPtr)\n            {\n                // Is an item that can be picked up being activated by the local player with their inventory open?\n                if (activatingActorPtr == MWBase::Environment::get().getWorld()->getPlayerPtr() &&\n                    (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container ||\n                    MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory))\n                {\n                    MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(ptrFound);\n                }\n                else\n                {\n                    MWBase::Environment::get().getWorld()->activate(ptrFound, activatingActorPtr);\n                }\n            }\n        }\n    }\n}\n\nvoid ObjectList::placeObjects(MWWorld::CellStore* cellStore)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i, count: %i, charge: %i, enchantmentCharge: %.2f, soul: %s\",\n            baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum, baseObject.count, baseObject.charge,\n            baseObject.enchantmentCharge, baseObject.soul.c_str());\n\n        // Ignore generic dynamic refIds because they could be anything on other clients\n        if (baseObject.refId.find(\"$dynamic\") != std::string::npos)\n            continue;\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(0, baseObject.mpNum);\n\n        // Only create this object if it doesn't already exist\n        if (!ptrFound)\n        {\n            try\n            {\n                MWWorld::ManualRef ref(world->getStore(), baseObject.refId, 1);\n\n                MWWorld::Ptr newPtr = ref.getPtr();\n\n                if (baseObject.count > 1)\n                    newPtr.getRefData().setCount(baseObject.count);\n\n                if (baseObject.charge > -1)\n                    newPtr.getCellRef().setCharge(baseObject.charge);\n\n                if (baseObject.enchantmentCharge > -1)\n                    newPtr.getCellRef().setEnchantmentCharge(baseObject.enchantmentCharge);\n\n                if (!baseObject.soul.empty())\n                    newPtr.getCellRef().setSoul(baseObject.soul);\n\n                newPtr.getCellRef().setGoldValue(baseObject.goldValue);\n                newPtr = world->placeObject(newPtr, cellStore, baseObject.position);\n\n                // Because gold automatically gets replaced with a new object, make sure we set the mpNum at the end\n                newPtr.getCellRef().setMpNum(baseObject.mpNum);\n\n                if (baseObject.droppedByPlayer)\n                {\n                    MWBase::Environment::get().getSoundManager()->playSound3D(newPtr, newPtr.getClass().getDownSoundId(newPtr), 1.f, 1.f);\n\n                    if (guid == Main::get().getLocalPlayer()->guid)\n                        world->PCDropped(newPtr);\n                }\n            }\n            catch (std::exception&)\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Ignored placement of invalid object %s\", baseObject.refId.c_str());\n            }\n        }\n        else\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Object already existed!\");\n    }\n}\n\nvoid ObjectList::spawnObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(),\n            baseObject.refNum, baseObject.mpNum);\n\n        // Ignore generic dynamic refIds because they could be anything on other clients\n        if (baseObject.refId.find(\"$dynamic\") != std::string::npos)\n            continue;\n        else if (!RecordHelper::doesRecordIdExist<ESM::Creature>(baseObject.refId) && !RecordHelper::doesRecordIdExist<ESM::NPC>(baseObject.refId))\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Ignored spawning of invalid object %s\", baseObject.refId.c_str());\n            continue;\n        }\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(0, baseObject.mpNum);\n\n        // Only create this object if it doesn't already exist\n        if (!ptrFound)\n        {\n            MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), baseObject.refId, 1);\n            MWWorld::Ptr newPtr = ref.getPtr();\n\n            newPtr.getCellRef().setMpNum(baseObject.mpNum);\n\n            newPtr = MWBase::Environment::get().getWorld()->placeObject(newPtr, cellStore, baseObject.position);\n            MWMechanics::CreatureStats& creatureStats = newPtr.getClass().getCreatureStats(newPtr);\n\n            if (baseObject.isSummon)\n            {\n                MWWorld::Ptr masterPtr;\n\n                if (baseObject.master.isPlayer)\n                    masterPtr = MechanicsHelper::getPlayerPtr(baseObject.master);\n                else\n                    masterPtr = cellStore->searchExact(baseObject.master.refNum, baseObject.master.mpNum, baseObject.master.refId);\n\n                if (masterPtr)\n                {\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Actor has master: %s\", masterPtr.getCellRef().getRefId().c_str());\n\n                    MWMechanics::AiFollow package(masterPtr);\n                    creatureStats.getAiSequence().stack(package, newPtr);\n\n                    MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(newPtr);\n                    if (anim)\n                    {\n                        const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()\n                            .search(\"VFX_Summon_Start\");\n                        if (fx)\n                            anim->addEffect(\"meshes\\\\\" + fx->mModel, -1, false);\n                    }\n\n                    int creatureActorId = newPtr.getClass().getCreatureStats(newPtr).getActorId();\n                    MWMechanics::CreatureStats& masterCreatureStats = masterPtr.getClass().getCreatureStats(masterPtr);\n\n                    std::vector<ESM::ActiveEffect> activeEffects;\n                    ESM::ActiveEffect activeEffect;\n                    activeEffect.mEffectId = baseObject.summonEffectId;\n                    activeEffect.mDuration = baseObject.summonDuration;\n                    activeEffect.mMagnitude = 1;\n                    activeEffects.push_back(activeEffect);\n\n                    LOG_APPEND(TimedLog::LOG_INFO, \"-- adding active spell to master with id %s, effect %i, duration %f\",\n                        baseObject.summonSpellId.c_str(), baseObject.summonEffectId, baseObject.summonDuration);\n\n                    auto activeSpells = masterCreatureStats.getActiveSpells();\n                    if (!activeSpells.isSpellActive(baseObject.summonSpellId))\n                        activeSpells.addSpell(baseObject.summonSpellId, false, activeEffects, \"\", masterCreatureStats.getActorId());\n\n                    LOG_APPEND(TimedLog::LOG_INFO, \"-- setting summoned creatureActorId for %i-%i to %i\",\n                        newPtr.getCellRef().getRefNum(), newPtr.getCellRef().getMpNum(), creatureActorId);\n\n                    // Check if this creature is present in the summoner's summoned creature map\n                    std::map<ESM::SummonKey, int>& creatureMap = masterCreatureStats.getSummonedCreatureMap();\n\n                    bool foundSummonedCreature = false;\n\n                    for (std::map<ESM::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )\n                    {\n                        if (it->first.mEffectId == baseObject.summonEffectId && it->first.mSourceId == baseObject.summonSpellId)\n                        {\n                            foundSummonedCreature = true;\n                            break;\n                        }\n                        ++it;\n                    }\n\n                    // If it is, update its creatureActorId\n                    if (foundSummonedCreature)\n                    {\n                        masterCreatureStats.setSummonedCreatureActorId(baseObject.refId, creatureActorId);\n                    }\n                    // If not, add it to the summoned creature map\n                    else\n                    {\n                        ESM::SummonKey summonKey(baseObject.summonEffectId, baseObject.summonSpellId, -1);\n                        creatureMap.emplace(summonKey, creatureActorId);\n                    }\n\n                    creatureStats.setFriendlyHits(0);\n                }\n            }\n        }\n        else\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Actor already existed!\");\n    }\n}\n\nvoid ObjectList::deleteObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            // If we are in a container, and it happens to be this object, exit it\n            if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container))\n            {\n                CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer;\n\n                if (currentContainer->refNum == ptrFound.getCellRef().getRefNum().mIndex &&\n                    currentContainer->mpNum == ptrFound.getCellRef().getMpNum())\n                {\n                    MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);\n                    MWBase::Environment::get().getWindowManager()->setDragDrop(false);\n                }\n            }\n\n            // Is this a dying actor being deleted before its death animation has finished? If so,\n            // increase the death count for the actor if applicable and run the actor's script,\n            // which is the same as what happens in OpenMW's ContainerWindow::onDisposeCorpseButtonClicked()\n            // if an actor's corpse is disposed of before its death animation is finished\n            if (ptrFound.getClass().isActor())\n            {\n                MWMechanics::CreatureStats& creatureStats = ptrFound.getClass().getCreatureStats(ptrFound);\n\n                if (creatureStats.isDead() && !creatureStats.isDeathAnimationFinished())\n                {\n                    creatureStats.setDeathAnimationFinished(true);\n                    MWBase::Environment::get().getMechanicsManager()->notifyDied(ptrFound);\n\n                    const std::string script = ptrFound.getClass().getScript(ptrFound);\n                    if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled())\n                    {\n                        MWScript::InterpreterContext interpreterContext(&ptrFound.getRefData().getLocals(), ptrFound);\n                        MWBase::Environment::get().getScriptManager()->run(script, interpreterContext);\n                    }\n                }\n            }\n\n            MWBase::Environment::get().getWorld()->deleteObject(ptrFound);\n        }\n    }\n}\n\nvoid ObjectList::lockObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            if (baseObject.lockLevel > 0)\n                ptrFound.getCellRef().lock(baseObject.lockLevel);\n            else\n                ptrFound.getCellRef().unlock();\n        }\n    }\n}\n\nvoid ObjectList::triggerTrapObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            if (!baseObject.isDisarmed)\n            {\n                MWMechanics::CastSpell cast(ptrFound, ptrFound);\n                cast.mHitPosition = baseObject.position.asVec3();\n                cast.cast(ptrFound.getCellRef().getTrap());\n            }\n\n            ptrFound.getCellRef().setTrap(\"\");\n            MWBase::Environment::get().getSoundManager()->playSound3D(ptrFound, \"Disarm Trap\", 1.0f, 1.0f);\n        }\n    }\n}\n\nvoid ObjectList::scaleObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i, scale: %f\", baseObject.refId.c_str(), baseObject.refNum,\n            baseObject.mpNum, baseObject.scale);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            MWBase::Environment::get().getWorld()->scaleObject(ptrFound, baseObject.scale);\n        }\n    }\n}\n\nvoid ObjectList::setObjectStates(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i, state: %s\", baseObject.refId.c_str(), baseObject.refNum,\n            baseObject.mpNum, baseObject.objectState ? \"true\" : \"false\");\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            if (baseObject.objectState)\n            {\n                MWBase::Environment::get().getWorld()->enable(ptrFound);\n\n                // Is this an actor in a cell where we're the authority? If so, initialize it as\n                // a LocalActor\n                if (ptrFound.getClass().isActor() && mwmp::Main::get().getCellController()->hasLocalAuthority(*cellStore->getCell()))\n                {\n                    mwmp::Main::get().getCellController()->getCell(*cellStore->getCell())->initializeLocalActor(ptrFound);\n                }\n            }\n            else\n                MWBase::Environment::get().getWorld()->disable(ptrFound);\n        }\n    }\n}\n\nvoid ObjectList::moveObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            MWBase::Environment::get().getWorld()->moveObject(ptrFound, baseObject.position.pos[0], baseObject.position.pos[1],\n                                                              baseObject.position.pos[2]);\n        }\n    }\n}\n\nvoid ObjectList::restockObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            //ptrFound.getClass().restock(ptrFound);\n\n            reset();\n            packetOrigin = mwmp::PACKET_ORIGIN::CLIENT_GAMEPLAY;\n            cell = *ptrFound.getCell()->getCell();\n            action = mwmp::BaseObjectList::SET;\n            containerSubAction = mwmp::BaseObjectList::RESTOCK_RESULT;\n            addEntireContainer(ptrFound);\n            sendContainer();\n        }\n    }\n}\n\nvoid ObjectList::rotateObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            MWBase::Environment::get().getWorld()->rotateObject(ptrFound,\n                                                                baseObject.position.rot[0], baseObject.position.rot[1], baseObject.position.rot[2], MWBase::RotationFlag_none);\n        }\n    }\n}\n\nvoid ObjectList::animateObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            MWBase::MechanicsManager * mechanicsManager = MWBase::Environment::get().getMechanicsManager();\n            mechanicsManager->playAnimationGroup(ptrFound, baseObject.animGroup, baseObject.animMode,\n                                                 std::numeric_limits<int>::max(), true);\n        }\n    }\n}\n\nvoid ObjectList::playObjectSounds(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        MWWorld::Ptr ptrFound;\n        std::string objectDescription;\n\n        if (baseObject.isPlayer)\n        {\n            if (baseObject.guid == Main::get().getLocalPlayer()->guid)\n            {\n                objectDescription = \"LocalPlayer \" + Main::get().getLocalPlayer()->npc.mName;\n                ptrFound = Main::get().getLocalPlayer()->getPlayerPtr();\n            }\n            else\n            {\n                DedicatedPlayer *player = PlayerList::getPlayer(baseObject.guid);\n\n                if (player != 0)\n                {\n                    objectDescription = \"DedicatedPlayer \" + player->npc.mName;\n                    ptrFound = player->getPtr();\n                }\n            }\n        }\n        else\n        {\n            objectDescription = baseObject.refId + \" \" + std::to_string(baseObject.refNum) + \"-\" + std::to_string(baseObject.mpNum);\n            ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n        }\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Playing sound %s on %s\", baseObject.soundId.c_str(), objectDescription.c_str());\n            bool playAtPosition = false;\n            if (ptrFound.isInCell()) {\n                ESM::CellId localCell = Main::get().getLocalPlayer()->cell.getCellId();\n                ESM::CellId soundCell = ptrFound.getCell()->getCell()->getCellId();\n                playAtPosition = localCell == soundCell;\n            }\n\n            if (playAtPosition) {\n                MWBase::Environment::get().getSoundManager()->playSound3D(ptrFound.getRefData().getPosition().asVec3(),\n                    baseObject.soundId, baseObject.volume, baseObject.pitch, MWSound::Type::Sfx, MWSound::PlayMode::Normal, 0);\n            }\n            else {\n                MWBase::Environment::get().getSoundManager()->playSound3D(ptrFound,\n                    baseObject.soundId, baseObject.volume, baseObject.pitch, MWSound::Type::Sfx, MWSound::PlayMode::Normal, 0);\n            }\n        }\n    }\n}\n\nvoid ObjectList::setGoldPoolsForObjects(MWWorld::CellStore* cellStore)\n{\n    for (const auto& baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            if (ptrFound.getClass().isActor())\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Setting gold pool to %u\", baseObject.goldPool);\n                ptrFound.getClass().getCreatureStats(ptrFound).setGoldPool(baseObject.goldPool);\n\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Setting last gold restock time to %f hours and %i days passed\",\n                    baseObject.lastGoldRestockHour, baseObject.lastGoldRestockDay);\n                ptrFound.getClass().getCreatureStats(ptrFound).setLastRestockTime(MWWorld::TimeStamp(baseObject.lastGoldRestockHour,\n                    baseObject.lastGoldRestockDay));\n            }\n            else\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Failed to set gold pool on %s %i-%i because it is not an actor!\",\n                    ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n            }\n        }\n    }\n}\n\nvoid ObjectList::activateDoors(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            MWWorld::DoorState doorState = static_cast<MWWorld::DoorState>(baseObject.doorState);\n\n            ptrFound.getClass().setDoorState(ptrFound, doorState);\n            MWBase::Environment::get().getWorld()->saveDoorState(ptrFound, doorState);\n        }\n    }\n}\n\nvoid ObjectList::setDoorDestinations(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            ptrFound.getCellRef().setTeleport(baseObject.teleportState);\n\n            if (baseObject.teleportState)\n            {\n                ptrFound.getCellRef().setDoorDest(baseObject.destinationPosition);\n\n                if (baseObject.destinationCell.isExterior())\n                    ptrFound.getCellRef().setDestCell(\"\");\n                else\n                    ptrFound.getCellRef().setDestCell(baseObject.destinationCell.getShortDescription());\n            }\n        }\n    }\n}\n\nvoid ObjectList::runConsoleCommands(MWWorld::CellStore* cellStore)\n{\n    MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();\n\n    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Console command: %s\", consoleCommand.c_str());\n\n    if (baseObjects.empty())\n    {\n        windowManager->clearConsolePtr();\n\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Running with no object reference\");\n        windowManager->executeCommandInConsole(consoleCommand);\n    }\n    else\n    {\n        for (const auto &baseObject : baseObjects)\n        {\n            windowManager->clearConsolePtr();\n\n            if (baseObject.isPlayer)\n            {\n                if (baseObject.guid == Main::get().getLocalPlayer()->guid)\n                {\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Running on local player\");\n                    windowManager->setConsolePtr(Main::get().getLocalPlayer()->getPlayerPtr());\n                    windowManager->executeCommandInConsole(consoleCommand);\n                }\n                else\n                {\n                    DedicatedPlayer *player = PlayerList::getPlayer(baseObject.guid);\n\n                    if (player != 0)\n                    {\n                        LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Running on player %s\", player->npc.mName.c_str());\n                        windowManager->setConsolePtr(player->getPtr());\n                        windowManager->executeCommandInConsole(consoleCommand);\n                    }\n                }\n            }\n            // Only require a valid cellStore if running on cell objects\n            else if (cellStore)\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Running on object %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n                MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n                if (ptrFound)\n                {\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                        ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n                    windowManager->setConsolePtr(ptrFound);\n                    windowManager->executeCommandInConsole(consoleCommand);\n                }\n            }\n        }\n\n        windowManager->clearConsolePtr();\n    }\n}\n\nvoid ObjectList::makeDialogueChoices(MWWorld::CellStore* cellStore)\n{\n    for (const auto& baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n        \n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            if (ptrFound.getClass().isActor())\n            {\n                // Ensure the dialogue window has the correct Ptr set for it\n                if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue))\n                {\n                    if (MWBase::Environment::get().getWindowManager()->getDialogueWindow()->getPtr() != ptrFound)\n                    {\n                        MWBase::Environment::get().getWindowManager()->getDialogueWindow()->setPtr(ptrFound);\n                    }\n                }\n                else\n                {\n                    MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, ptrFound);\n                }\n                \n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Making dialogue choice of type %i\", baseObject.dialogueChoiceType);\n\n                if (baseObject.dialogueChoiceType == DialogueChoiceType::TOPIC)\n                {\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- topic was %s\", baseObject.topicId.c_str());\n                }\n\n                std::string topic = baseObject.topicId;\n\n                if (MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())\n                {\n                    char delimiter = '|';\n\n                    // If we're using a translated version of Morrowind, we may have received a string that had the original\n                    // topic delimited from its possible English translation by a | character, in which case we need to use\n                    // the original topic here\n                    if (topic.find(delimiter) != std::string::npos)\n                    {\n                        topic = topic.substr(0, topic.find(delimiter));\n                    }\n                    // Alternatively, we may have received a topic that needs to be translated into the current language's\n                    // version of it\n                    else\n                    {\n                        std::string translatedTopic = MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().getLocalizedTopicId(topic);\n\n                        if (!translatedTopic.empty())\n                        {\n                            topic = translatedTopic;\n                        }\n                    }\n                }\n\n                MWBase::Environment::get().getWindowManager()->getDialogueWindow()->activateDialogueChoice(baseObject.dialogueChoiceType, topic);\n            }\n            else\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Failed to make dialogue choice for %s %i-%i because it is not an actor!\",\n                    ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n            }\n        }\n    }\n}\n\nvoid ObjectList::setClientLocals(MWWorld::CellStore* cellStore)\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            for (const auto& clientLocal : baseObject.clientLocals)\n            {\n                std::string valueAsString;\n                std::string variableTypeAsString;\n\n                if (clientLocal.variableType == mwmp::VARIABLE_TYPE::SHORT || clientLocal.variableType == mwmp::VARIABLE_TYPE::LONG)\n                {\n                    variableTypeAsString = clientLocal.variableType == mwmp::VARIABLE_TYPE::SHORT ? \"short\" : \"long\";\n                    valueAsString = std::to_string(clientLocal.intValue);\n                }\n                else if (clientLocal.variableType == mwmp::VARIABLE_TYPE::FLOAT)\n                {\n                    variableTypeAsString = \"float\";\n                    valueAsString = std::to_string(clientLocal.floatValue);\n                }\n\n                if (clientLocal.variableType == mwmp::VARIABLE_TYPE::SHORT)\n                    ptrFound.getRefData().getLocals().mShorts.at(clientLocal.internalIndex) = clientLocal.intValue;\n                else if (clientLocal.variableType == mwmp::VARIABLE_TYPE::LONG)\n                    ptrFound.getRefData().getLocals().mLongs.at(clientLocal.internalIndex) = clientLocal.intValue;\n                else if (clientLocal.variableType == mwmp::VARIABLE_TYPE::FLOAT)\n                    ptrFound.getRefData().getLocals().mFloats.at(clientLocal.internalIndex) = clientLocal.floatValue;\n            }\n        }\n    }\n}\n\nvoid ObjectList::setMemberShorts()\n{\n    /*\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s, index: %i, shortVal: %i\", baseObject.refId.c_str(),\n                   baseObject.index, baseObject.shortVal);\n\n        // Mimic the way a Ptr is fetched in InterpreterContext for similar situations\n        MWWorld::Ptr ptrFound = MWBase::Environment::get().getWorld()->searchPtr(baseObject.refId, false);\n\n        if (ptrFound)\n        {\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Found %s %i-%i\", ptrFound.getCellRef().getRefId().c_str(),\n                               ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());\n\n            std::string scriptId = ptrFound.getClass().getScript(ptrFound);\n\n            ptrFound.getRefData().setLocals(\n                *MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find(scriptId));\n\n            ptrFound.getRefData().getLocals().mShorts.at(baseObject.index) = baseObject.shortVal;;\n        }\n    }\n    */\n}\n\nvoid ObjectList::playMusic()\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- filename: %s\", baseObject.musicFilename.c_str());\n\n        MWBase::Environment::get().getSoundManager()->streamMusic(baseObject.musicFilename);\n    }\n}\n\nvoid ObjectList::playVideo()\n{\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- filename: %s, allowSkipping: %s\", baseObject.videoFilename.c_str(),\n            baseObject.allowSkipping ? \"true\" : \"false\");\n\n        MWBase::Environment::get().getWindowManager()->playVideo(baseObject.videoFilename, baseObject.allowSkipping);\n    }\n}\n\nvoid ObjectList::addAllContainers(MWWorld::CellStore* cellStore)\n{\n    for (auto &ref : cellStore->getContainers()->mList)\n    {\n        MWWorld::Ptr ptr(&ref, 0);\n        addEntireContainer(ptr);\n    }\n\n    for (auto &ref : cellStore->getNpcs()->mList)\n    {\n        MWWorld::Ptr ptr(&ref, 0);\n        addEntireContainer(ptr);\n    }\n\n    for (auto &ref : cellStore->getCreatures()->mList)\n    {\n        MWWorld::Ptr ptr(&ref, 0);\n        addEntireContainer(ptr);\n    }\n}\n\nvoid ObjectList::addRequestedContainers(MWWorld::CellStore* cellStore, const std::vector<BaseObject>& requestObjects)\n{\n    for (const auto &baseObject : requestObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(),\n            baseObject.refNum, baseObject.mpNum);\n\n        MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum, baseObject.refId);\n\n        if (ptrFound)\n        {\n            if (ptrFound.getClass().hasContainerStore(ptrFound))\n                addEntireContainer(ptrFound);\n            else\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"-- Object lacks container store\", ptrFound.getCellRef().getRefId().c_str());\n        }\n    }\n}\n\nvoid ObjectList::addObjectGeneric(const MWWorld::Ptr& ptr)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectActivate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& activatingActor)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.activatingActor = MechanicsHelper::getTarget(activatingActor);\n\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectHit(const MWWorld::Ptr& ptr, const MWWorld::Ptr& hittingActor)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.hittingActor = MechanicsHelper::getTarget(hittingActor);\n    baseObject.hitAttack.success = false;\n\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectHit(const MWWorld::Ptr& ptr, const MWWorld::Ptr& hittingActor, const Attack hitAttack)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.hittingActor = MechanicsHelper::getTarget(hittingActor);\n    baseObject.hitAttack = hitAttack;\n\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer)\n{\n    if (ptr.getCellRef().getRefId().find(\"$dynamic\") != std::string::npos)\n    {\n        MWBase::Environment::get().getWindowManager()->messageBox(\"You cannot place unsynchronized custom items in multiplayer.\");\n        return;\n    }\n\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.charge = ptr.getCellRef().getCharge();\n    baseObject.enchantmentCharge = ptr.getCellRef().getEnchantmentCharge();\n    baseObject.soul = ptr.getCellRef().getSoul();\n    baseObject.droppedByPlayer = droppedByPlayer;\n    baseObject.hasContainer = ptr.getClass().hasContainerStore(ptr);\n\n    // Make sure we send the RefData position instead of the CellRef one, because that's what\n    // we actually see on this client\n    baseObject.position = ptr.getRefData().getPosition();\n\n    // We have to get the count from the dropped object because it gets changed\n    // automatically for stacks of gold\n    baseObject.count = ptr.getRefData().getCount();\n\n    // Get the real count of gold in a stack\n    baseObject.goldValue = ptr.getCellRef().getGoldValue();\n\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectSpawn(const MWWorld::Ptr& ptr)\n{\n    if (ptr.getCellRef().getRefId().find(\"$dynamic\") != std::string::npos)\n    {\n        MWBase::Environment::get().getWindowManager()->messageBox(\"You're trying to spawn a custom object lacking a server-given refId, \"\n            \"and those cannot be synchronized in multiplayer.\");\n        return;\n    }\n\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.isSummon = false;\n    baseObject.summonDuration = -1;\n\n    // Make sure we send the RefData position instead of the CellRef one, because that's what\n    // we actually see on this client\n    baseObject.position = ptr.getRefData().getPosition();\n\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master, std::string spellId, int effectId, float duration)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.isSummon = true;\n    baseObject.summonSpellId = spellId;\n    baseObject.summonEffectId = effectId;\n    baseObject.summonDuration = duration;\n    baseObject.master = MechanicsHelper::getTarget(master);\n\n    // Make sure we send the RefData position instead of the CellRef one, because that's what\n    // we actually see on this client\n    baseObject.position = ptr.getRefData().getPosition();\n\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectLock(const MWWorld::Ptr& ptr, int lockLevel)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.lockLevel = lockLevel;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectDialogueChoice(const MWWorld::Ptr& ptr, std::string dialogueChoice)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n\n    const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n    // Because the actual text for any of the special dialogue choices can vary according to the game language used,\n    // set the type of dialogue choice by doing a lot of checks\n    if (dialogueChoice == gmst.find(\"sPersuasion\")->mValue.getString())\n        baseObject.dialogueChoiceType = static_cast<int>(DialogueChoiceType::PERSUASION);\n    else if (dialogueChoice == gmst.find(\"sCompanionShare\")->mValue.getString())\n        baseObject.dialogueChoiceType = DialogueChoiceType::COMPANION_SHARE;\n    else if (dialogueChoice == gmst.find(\"sBarter\")->mValue.getString())\n        baseObject.dialogueChoiceType = DialogueChoiceType::BARTER;\n    else if (dialogueChoice == gmst.find(\"sSpells\")->mValue.getString())\n        baseObject.dialogueChoiceType = DialogueChoiceType::SPELLS;\n    else if (dialogueChoice == gmst.find(\"sTravel\")->mValue.getString())\n        baseObject.dialogueChoiceType = DialogueChoiceType::TRAVEL;\n    else if (dialogueChoice == gmst.find(\"sSpellMakingMenuTitle\")->mValue.getString())\n        baseObject.dialogueChoiceType = DialogueChoiceType::SPELLMAKING;\n    else if (dialogueChoice == gmst.find(\"sEnchanting\")->mValue.getString())\n        baseObject.dialogueChoiceType = DialogueChoiceType::ENCHANTING;\n    else if (dialogueChoice == gmst.find(\"sServiceTrainingTitle\")->mValue.getString())\n        baseObject.dialogueChoiceType = DialogueChoiceType::TRAINING;\n    else if (dialogueChoice == gmst.find(\"sRepair\")->mValue.getString())\n        baseObject.dialogueChoiceType = DialogueChoiceType::REPAIR;\n    else\n    {\n        baseObject.dialogueChoiceType = DialogueChoiceType::TOPIC;\n\n        // For translated versions of the game, make sure we translate the topic back into English first\n        if (MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())\n            baseObject.topicId = dialogueChoice + \"|\" + MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().topicID(dialogueChoice);\n        else\n            baseObject.topicId = dialogueChoice;\n    }\n\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectMiscellaneous(const MWWorld::Ptr& ptr, unsigned int goldPool, float lastGoldRestockHour, int lastGoldRestockDay)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.goldPool = goldPool;\n    baseObject.lastGoldRestockHour = lastGoldRestockHour;\n    baseObject.lastGoldRestockDay = lastGoldRestockDay;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectTrap(const MWWorld::Ptr& ptr, const ESM::Position& pos, bool isDisarmed)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.isDisarmed = isDisarmed;\n    baseObject.position = pos;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectScale(const MWWorld::Ptr& ptr, float scale)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.scale = scale;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectSound(const MWWorld::Ptr& ptr, std::string soundId, float volume, float pitch)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.soundId = soundId;\n    baseObject.volume = volume;\n    baseObject.pitch = pitch;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectState(const MWWorld::Ptr& ptr, bool objectState)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.objectState = objectState;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.animGroup = group;\n    baseObject.animMode = mode;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addDoorState(const MWWorld::Ptr& ptr, MWWorld::DoorState state)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    baseObject.doorState = static_cast<int>(state);\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addMusicPlay(std::string filename)\n{\n    mwmp::BaseObject baseObject;\n    baseObject.musicFilename = filename;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addVideoPlay(std::string filename, bool allowSkipping)\n{\n    mwmp::BaseObject baseObject;\n    baseObject.videoFilename = filename;\n    baseObject.allowSkipping = allowSkipping;\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addClientScriptLocal(const MWWorld::Ptr& ptr, int internalIndex, int value, mwmp::VARIABLE_TYPE variableType)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    ClientVariable clientLocal;\n    clientLocal.internalIndex = internalIndex;\n    clientLocal.variableType = variableType;\n    clientLocal.intValue = value;\n    baseObject.clientLocals.push_back(clientLocal);\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addClientScriptLocal(const MWWorld::Ptr& ptr, int internalIndex, float value)\n{\n    cell = *ptr.getCell()->getCell();\n\n    mwmp::BaseObject baseObject = getBaseObjectFromPtr(ptr);\n    ClientVariable clientLocal;\n    clientLocal.internalIndex = internalIndex;\n    clientLocal.variableType = mwmp::VARIABLE_TYPE::FLOAT;\n    clientLocal.floatValue = value;\n    baseObject.clientLocals.push_back(clientLocal);\n    addBaseObject(baseObject);\n}\n\nvoid ObjectList::addScriptMemberShort(std::string refId, int index, int shortVal)\n{\n    /*\n    mwmp::BaseObject baseObject;\n    baseObject.refId = refId;\n    baseObject.index = index;\n    baseObject.shortVal = shortVal;\n    addBaseObject(baseObject);\n    */\n}\n\nvoid ObjectList::sendObjectActivate()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_ACTIVATE)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_ACTIVATE)->Send();\n}\n\nvoid ObjectList::sendObjectHit()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_HIT)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_HIT)->Send();\n}\n\nvoid ObjectList::sendObjectPlace()\n{\n    if (baseObjects.size() == 0)\n        return;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Sending ID_OBJECT_PLACE about %s\", cell.getShortDescription().c_str());\n\n    for (const auto &baseObject : baseObjects)\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s, count: %i\", baseObject.refId.c_str(), baseObject.count);\n\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_PLACE)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_PLACE)->Send();\n}\n\nvoid ObjectList::sendObjectSpawn()\n{\n    if (baseObjects.size() == 0)\n        return;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Sending ID_OBJECT_SPAWN about %s\", cell.getShortDescription().c_str());\n\n    for (const auto &baseObject : baseObjects)\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s-%i\", baseObject.refId.c_str(), baseObject.refNum);\n\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_SPAWN)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_SPAWN)->Send();\n}\n\nvoid ObjectList::sendObjectDelete()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_DELETE)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_DELETE)->Send();\n}\n\nvoid ObjectList::sendObjectLock()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_LOCK)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_LOCK)->Send();\n}\n\nvoid ObjectList::sendObjectDialogueChoice()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_DIALOGUE_CHOICE)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_DIALOGUE_CHOICE)->Send();\n}\n\nvoid ObjectList::sendObjectMiscellaneous()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_MISCELLANEOUS)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_MISCELLANEOUS)->Send();\n}\n\nvoid ObjectList::sendObjectRestock()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_RESTOCK)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_RESTOCK)->Send();\n}\n\nvoid ObjectList::sendObjectSound()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_SOUND)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_SOUND)->Send();\n}\n\nvoid ObjectList::sendObjectTrap()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_TRAP)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_TRAP)->Send();\n}\n\nvoid ObjectList::sendObjectScale()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_SCALE)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_SCALE)->Send();\n}\n\nvoid ObjectList::sendObjectState()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_STATE)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_STATE)->Send();\n}\n\nvoid ObjectList::sendObjectAnimPlay()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_ANIM_PLAY)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_ANIM_PLAY)->Send();\n}\n\nvoid ObjectList::sendDoorState()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Sending ID_DOOR_STATE about %s\", cell.getShortDescription().c_str());\n\n    for (const auto &baseObject : baseObjects)\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s-%i, state: %s\", baseObject.refId.c_str(), baseObject.refNum,\n                   baseObject.doorState ? \"true\" : \"false\");\n\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_DOOR_STATE)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_DOOR_STATE)->Send();\n}\n\nvoid ObjectList::sendMusicPlay()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_MUSIC_PLAY)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_MUSIC_PLAY)->Send();\n}\n\nvoid ObjectList::sendVideoPlay()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_VIDEO_PLAY)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_VIDEO_PLAY)->Send();\n}\n\nvoid ObjectList::sendClientScriptLocal()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Sending ID_CLIENT_SCRIPT_LOCAL about %s\", cell.getShortDescription().c_str());\n\n    for (const auto &baseObject : baseObjects)\n    {\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s %i-%i\", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum);\n\n        for (const auto& clientLocal : baseObject.clientLocals)\n        {\n            std::string valueAsString;\n            std::string variableTypeAsString;\n\n            if (clientLocal.variableType == mwmp::VARIABLE_TYPE::SHORT || clientLocal.variableType == mwmp::VARIABLE_TYPE::LONG)\n            {\n                variableTypeAsString = clientLocal.variableType == mwmp::VARIABLE_TYPE::SHORT ? \"short\" : \"long\";\n                valueAsString = std::to_string(clientLocal.intValue);\n            }\n            else if (clientLocal.variableType == mwmp::VARIABLE_TYPE::FLOAT)\n            {\n                variableTypeAsString = \"float\";\n                valueAsString = std::to_string(clientLocal.floatValue);\n            }\n\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"- type %s, value: %s\", variableTypeAsString.c_str(), valueAsString.c_str());\n        }\n    }\n\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_CLIENT_SCRIPT_LOCAL)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_CLIENT_SCRIPT_LOCAL)->Send();\n}\n\nvoid ObjectList::sendScriptMemberShort()\n{\n    /*\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Sending ID_SCRIPT_MEMBER_SHORT\");\n\n    for (const auto &baseObject : baseObjects)\n        LOG_APPEND(TimedLog::LOG_VERBOSE, \"- cellRef: %s, index: %i, shortVal: %i\", baseObject.refId.c_str(),\n                   baseObject.index, baseObject.shortVal);\n\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_SCRIPT_MEMBER_SHORT)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_SCRIPT_MEMBER_SHORT)->Send();\n    */\n}\n\nvoid ObjectList::sendContainer()\n{\n    std::string debugMessage = \"Sending ID_CONTAINER with action \";\n    \n    if (action == mwmp::BaseObjectList::SET)\n        debugMessage += \"SET\";\n    else if (action == mwmp::BaseObjectList::ADD)\n        debugMessage += \"ADD\";\n    else if (action == mwmp::BaseObjectList::REMOVE)\n        debugMessage += \"REMOVE\";\n\n    debugMessage += \" and subaction \";\n\n    if (containerSubAction == mwmp::BaseObjectList::NONE)\n        debugMessage += \"NONE\";\n    else if (containerSubAction == mwmp::BaseObjectList::DRAG)\n        debugMessage += \"DRAG\";\n    else if (containerSubAction == mwmp::BaseObjectList::DROP)\n        debugMessage += \"DROP\";\n    else if (containerSubAction == mwmp::BaseObjectList::TAKE_ALL)\n        debugMessage += \"TAKE_ALL\";\n    else if (containerSubAction == mwmp::BaseObjectList::REPLY_TO_REQUEST)\n        debugMessage += \"REPLY_TO_REQUEST\";\n\n    debugMessage += \"\\n- cell \" + cell.getShortDescription();\n\n    for (const auto &baseObject : baseObjects)\n    {\n        debugMessage += \"\\n- container \" + baseObject.refId + \" \" + std::to_string(baseObject.refNum) + \"-\" + std::to_string(baseObject.mpNum);\n\n        for (const auto &containerItem : baseObject.containerItems)\n        {\n            debugMessage += \"\\n-- item \" + containerItem.refId + \", count \" + std::to_string(containerItem.count) +\n                \", actionCount \" + std::to_string(containerItem.actionCount);\n        }\n    }\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"%s\", debugMessage.c_str());\n\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_CONTAINER)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_CONTAINER)->Send();\n}\n\nvoid ObjectList::sendConsoleCommand()\n{\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_CONSOLE_COMMAND)->setObjectList(this);\n    mwmp::Main::get().getNetworking()->getObjectPacket(ID_CONSOLE_COMMAND)->Send();\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/ObjectList.hpp",
    "content": "#ifndef OPENMW_OBJECTLIST_HPP\n#define OPENMW_OBJECTLIST_HPP\n\n#include <components/openmw-mp/Base/BaseObject.hpp>\n#include \"../mwgui/itemmodel.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    class Networking;\n    class ObjectList : public BaseObjectList\n    {\n    public:\n\n        ObjectList();\n        virtual ~ObjectList();\n\n        void reset();\n\n        void addBaseObject(BaseObject baseObject);\n        mwmp::BaseObject getBaseObjectFromPtr(const MWWorld::Ptr& ptr);\n        void addContainerItem(mwmp::BaseObject& baseObject, const MWWorld::Ptr& itemPtr, int itemCount, int actionCount);\n        void addContainerItem(mwmp::BaseObject& baseObject, const MWGui::ItemStack& itemStack, int itemCount, int actionCount);\n        void addContainerItem(mwmp::BaseObject& baseObject, const std::string itemId, int itemCount, int actionCount);\n        void addEntireContainer(const MWWorld::Ptr& ptr);\n\n        void editContainers(MWWorld::CellStore* cellStore);\n\n        void activateObjects(MWWorld::CellStore* cellStore);\n        void placeObjects(MWWorld::CellStore* cellStore);\n        void spawnObjects(MWWorld::CellStore* cellStore);\n        void deleteObjects(MWWorld::CellStore* cellStore);\n        void lockObjects(MWWorld::CellStore* cellStore);\n        void triggerTrapObjects(MWWorld::CellStore* cellStore);\n        void scaleObjects(MWWorld::CellStore* cellStore);\n        void setObjectStates(MWWorld::CellStore* cellStore);\n        void moveObjects(MWWorld::CellStore* cellStore);\n        void restockObjects(MWWorld::CellStore* cellStore);\n        void rotateObjects(MWWorld::CellStore* cellStore);\n        void animateObjects(MWWorld::CellStore* cellStore);\n        void playObjectSounds(MWWorld::CellStore* cellStore);\n        void setGoldPoolsForObjects(MWWorld::CellStore* cellStore);\n        void activateDoors(MWWorld::CellStore* cellStore);\n        void setDoorDestinations(MWWorld::CellStore* cellStore);\n        void runConsoleCommands(MWWorld::CellStore* cellStore);\n        void makeDialogueChoices(MWWorld::CellStore* cellStore);\n\n        void setClientLocals(MWWorld::CellStore* cellStore);\n        void setMemberShorts();\n\n        void playMusic();\n        void playVideo();\n\n        void addAllContainers(MWWorld::CellStore* cellStore);\n        void addRequestedContainers(MWWorld::CellStore* cellStore, const std::vector<BaseObject>& requestObjects);\n\n        void addObjectGeneric(const MWWorld::Ptr& ptr);\n        void addObjectActivate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& activatingActor);\n        void addObjectHit(const MWWorld::Ptr& ptr, const MWWorld::Ptr& hittingActor);\n        void addObjectHit(const MWWorld::Ptr& ptr, const MWWorld::Ptr& hittingActor, const Attack hitAttack);\n        void addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer = false);\n        void addObjectSpawn(const MWWorld::Ptr& ptr);\n        void addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master, std::string spellId, int effectId, float duration);\n        void addObjectLock(const MWWorld::Ptr& ptr, int lockLevel);\n        void addObjectDialogueChoice(const MWWorld::Ptr& ptr, std::string dialogueChoice);\n        void addObjectMiscellaneous(const MWWorld::Ptr& ptr, unsigned int goldPool, float lastGoldRestockHour, int lastGoldRestockDay);\n        void addObjectTrap(const MWWorld::Ptr& ptr, const ESM::Position& pos, bool isDisarmed);\n        void addObjectScale(const MWWorld::Ptr& ptr, float scale);\n        void addObjectSound(const MWWorld::Ptr& ptr, std::string soundId, float volume, float pitch);\n        void addObjectState(const MWWorld::Ptr& ptr, bool objectState);\n        void addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode);\n\n        void addDoorState(const MWWorld::Ptr& ptr, MWWorld::DoorState state);\n        void addMusicPlay(std::string filename);\n        void addVideoPlay(std::string filename, bool allowSkipping);\n        void addClientScriptLocal(const MWWorld::Ptr& ptr, int internalIndex, int value, mwmp::VARIABLE_TYPE variableType);\n        void addClientScriptLocal(const MWWorld::Ptr& ptr, int internalIndex, float value);\n        void addScriptMemberShort(std::string refId, int index, int shortVal);\n\n        void sendObjectActivate();\n        void sendObjectHit();\n        void sendObjectPlace();\n        void sendObjectSpawn();\n        void sendObjectDelete();\n        void sendObjectLock();\n        void sendObjectDialogueChoice();\n        void sendObjectMiscellaneous();\n        void sendObjectRestock();\n        void sendObjectTrap();\n        void sendObjectScale();\n        void sendObjectSound();\n        void sendObjectState();\n        void sendObjectAnimPlay();\n        void sendDoorState();\n        void sendMusicPlay();\n        void sendVideoPlay();\n        void sendClientScriptLocal();\n        void sendScriptMemberShort();\n        void sendContainer();\n        void sendConsoleCommand();\n\n    private:\n        Networking *getNetworking();\n\n    };\n}\n\n#endif //OPENMW_OBJECTLIST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/PlayerList.cpp",
    "content": "#include <components/openmw-mp/TimedLog.hpp>\n#include <apps/openmw/mwclass/creature.hpp>\n\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwclass/npc.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"PlayerList.hpp\"\n#include \"Main.hpp\"\n#include \"DedicatedPlayer.hpp\"\n#include \"CellController.hpp\"\n#include \"GUIController.hpp\"\n\n\nusing namespace mwmp;\n\nstd::map <RakNet::RakNetGUID, DedicatedPlayer *> PlayerList::playerList;\n\nvoid PlayerList::update(float dt)\n{\n    for (auto &playerEntry : playerList)\n    {\n        DedicatedPlayer *player = playerEntry.second;\n        if (player == nullptr) continue;\n\n        player->update(dt);\n    }\n}\n\nDedicatedPlayer *PlayerList::newPlayer(RakNet::RakNetGUID guid)\n{\n    LOG_APPEND(TimedLog::LOG_INFO, \"- Creating new DedicatedPlayer with guid %s\", guid.ToString());\n\n    playerList[guid] = new DedicatedPlayer(guid);\n\n    LOG_APPEND(TimedLog::LOG_INFO, \"- There are now %i DedicatedPlayers\", playerList.size());\n\n    return playerList[guid];\n}\n\nvoid PlayerList::deletePlayer(RakNet::RakNetGUID guid)\n{\n    if (playerList[guid]->reference)\n        playerList[guid]->deleteReference();\n\n    delete playerList[guid];\n    playerList.erase(guid);\n}\n\nvoid PlayerList::cleanUp()\n{\n    for (auto &playerEntry : playerList)\n        delete playerEntry.second;\n}\n\nDedicatedPlayer *PlayerList::getPlayer(RakNet::RakNetGUID guid)\n{\n    return playerList[guid];\n}\n\nDedicatedPlayer *PlayerList::getPlayer(const MWWorld::Ptr &ptr)\n{\n    for (auto &playerEntry : playerList)\n    {\n        if (playerEntry.second == nullptr || playerEntry.second->getPtr().mRef == nullptr)\n            continue;\n        \n        std::string refId = ptr.getCellRef().getRefId();\n        \n        if (playerEntry.second->getPtr().getCellRef().getRefId() == refId)\n            return playerEntry.second;\n    }\n\n    return nullptr;\n}\n\nDedicatedPlayer* PlayerList::getPlayer(int actorId)\n{\n    for (auto& playerEntry : playerList)\n    {\n        if (playerEntry.second == nullptr || playerEntry.second->getPtr().mRef == nullptr)\n            continue;\n\n        MWWorld::Ptr playerPtr = playerEntry.second->getPtr();\n        int playerActorId = playerPtr.getClass().getCreatureStats(playerPtr).getActorId();\n\n        if (actorId == playerActorId)\n            return playerEntry.second;\n    }\n\n    return nullptr;\n}\n\nstd::vector<RakNet::RakNetGUID> PlayerList::getPlayersInCell(const ESM::Cell& cell)\n{\n    std::vector<RakNet::RakNetGUID> playersInCell;\n\n    for (auto& playerEntry : playerList)\n    {\n        if (playerEntry.first != RakNet::UNASSIGNED_CRABNET_GUID)\n        {\n            if (Main::get().getCellController()->isSameCell(cell, playerEntry.second->cell))\n            {\n                playersInCell.push_back(playerEntry.first);\n            }\n        }\n    }\n\n    return playersInCell;\n}\n\nbool PlayerList::isDedicatedPlayer(const MWWorld::Ptr &ptr)\n{\n    if (ptr.mRef == nullptr)\n        return false;\n\n    // Players always have 0 as their refNum and mpNum\n    if (ptr.getCellRef().getRefNum().mIndex != 0 || ptr.getCellRef().getMpNum() != 0)\n        return false;\n\n    return (getPlayer(ptr) != nullptr);\n}\n\nvoid PlayerList::enableMarkers(const ESM::Cell& cell)\n{\n    for (auto &playerEntry : playerList)\n    {\n        if (playerEntry.second == nullptr || playerEntry.second->getPtr().mRef == nullptr)\n            continue;\n\n        if (Main::get().getCellController()->isSameCell(cell, playerEntry.second->cell))\n        {\n            playerEntry.second->enableMarker();\n        }\n    }\n}\n\n/*\n    Go through all DedicatedPlayers checking if their mHitAttemptActorId matches this one\n    and set it to -1 if it does\n\n    This resets the combat target for a DedicatedPlayer's followers in Actors::update()\n*/\nvoid PlayerList::clearHitAttemptActorId(int actorId)\n{\n    for (auto &playerEntry : playerList)\n    {\n        if (playerEntry.second == nullptr || playerEntry.second->getPtr().mRef == nullptr)\n            continue;\n\n        MWMechanics::CreatureStats &playerCreatureStats = playerEntry.second->getPtr().getClass().getCreatureStats(playerEntry.second->getPtr());\n\n        if (playerCreatureStats.getHitAttemptActorId() == actorId)\n            playerCreatureStats.setHitAttemptActorId(-1);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/PlayerList.hpp",
    "content": "#ifndef OPENMW_PLAYERLIST_HPP\n#define OPENMW_PLAYERLIST_HPP\n\n#include <components/esm/custommarkerstate.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/openmw-mp/Base/BasePlayer.hpp>\n\n#include \"../mwmechanics/aisequence.hpp\"\n\n#include \"../mwworld/manualref.hpp\"\n\n#include \"DedicatedPlayer.hpp\"\n\n#include <map>\n#include <RakNetTypes.h>\n\nnamespace MWMechanics\n{\n    class Actor;\n}\n\nnamespace mwmp\n{\n    class PlayerList\n    {\n    public:\n\n        static void update(float dt);\n\n        static DedicatedPlayer *newPlayer(RakNet::RakNetGUID guid);\n\n        static void deletePlayer(RakNet::RakNetGUID guid);\n        static void cleanUp();\n\n        static DedicatedPlayer *getPlayer(RakNet::RakNetGUID guid);\n        static DedicatedPlayer *getPlayer(const MWWorld::Ptr &ptr);\n        static DedicatedPlayer* getPlayer(int actorId);\n        static std::vector<RakNet::RakNetGUID> getPlayersInCell(const ESM::Cell& cell);\n\n        static bool isDedicatedPlayer(const MWWorld::Ptr &ptr);\n\n        static void enableMarkers(const ESM::Cell& cell);\n\n        static void clearHitAttemptActorId(int actorId);\n\n    private:\n\n        static std::map<RakNet::RakNetGUID, DedicatedPlayer *> playerList;\n    };\n}\n\n#endif //OPENMW_PLAYERLIST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/RecordHelper.cpp",
    "content": "#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"RecordHelper.hpp\"\n#include \"Main.hpp\"\n#include \"CellController.hpp\"\n#include \"Cell.hpp\"\n\nvoid RecordHelper::overrideRecord(const mwmp::ActivatorRecord& record)\n{\n    const ESM::Activator &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Activator>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Activator>(record.baseId))\n    {\n        const ESM::Activator *baseData = world->getStore().get<ESM::Activator>().search(record.baseId);\n        ESM::Activator finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::ApparatusRecord& record)\n{\n    const ESM::Apparatus &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Apparatus>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Apparatus>(record.baseId))\n    {\n        const ESM::Apparatus *baseData = world->getStore().get<ESM::Apparatus>().search(record.baseId);\n        ESM::Apparatus finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasSubtype)\n            finalData.mData.mType = recordData.mData.mType;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasQuality)\n            finalData.mData.mQuality = recordData.mData.mQuality;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::ArmorRecord& record)\n{\n    const ESM::Armor &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Armor>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        if (!recordData.mEnchant.empty() && !doesRecordIdExist<ESM::Enchantment>(recordData.mEnchant))\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring new armor record with invalid enchantmentId %s\", recordData.mEnchant.c_str());\n            return;\n        }\n        else\n            world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Armor>(record.baseId))\n    {\n        const ESM::Armor *baseData = world->getStore().get<ESM::Armor>().search(record.baseId);\n        ESM::Armor finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        finalData.mParts.mParts.at(0);\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasSubtype)\n            finalData.mData.mType = recordData.mData.mType;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasHealth)\n            finalData.mData.mHealth = recordData.mData.mHealth;\n\n        if (record.baseOverrides.hasArmorRating)\n            finalData.mData.mArmor = recordData.mData.mArmor;\n\n        if (record.baseOverrides.hasEnchantmentId)\n        {\n            if (recordData.mEnchant.empty() || doesRecordIdExist<ESM::Enchantment>(recordData.mEnchant))\n                finalData.mEnchant = recordData.mEnchant;\n            else\n                LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring invalid enchantmentId %s\", recordData.mEnchant.c_str());\n        }\n\n        if (record.baseOverrides.hasEnchantmentCharge)\n            finalData.mData.mEnchant = recordData.mData.mEnchant;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        if (record.baseOverrides.hasBodyParts)\n            finalData.mParts.mParts = recordData.mParts.mParts;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::BodyPartRecord& record)\n{\n    const ESM::BodyPart &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::BodyPart>(record.baseId))\n    {\n        const ESM::BodyPart *baseData = world->getStore().get<ESM::BodyPart>().search(record.baseId);\n        ESM::BodyPart finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasRace)\n            finalData.mRace = recordData.mRace;\n\n        if (record.baseOverrides.hasSubtype)\n            finalData.mData.mType = recordData.mData.mType;\n\n        if (record.baseOverrides.hasBodyPartType)\n            finalData.mData.mPart = recordData.mData.mPart;\n\n        if (record.baseOverrides.hasVampireState)\n            finalData.mData.mVampire = recordData.mData.mVampire;\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mData.mFlags = recordData.mData.mFlags;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::BookRecord& record)\n{\n    const ESM::Book &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Book>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        if (!recordData.mEnchant.empty() && !doesRecordIdExist<ESM::Enchantment>(recordData.mEnchant))\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring new book record with invalid enchantmentId %s\", recordData.mEnchant.c_str());\n            return;\n        }\n        else\n            world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Book>(record.baseId))\n    {\n        const ESM::Book *baseData = world->getStore().get<ESM::Book>().search(record.baseId);\n        ESM::Book finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasText)\n            finalData.mText = recordData.mText;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasScrollState)\n            finalData.mData.mIsScroll = recordData.mData.mIsScroll;\n\n        if (record.baseOverrides.hasSkillId)\n            finalData.mData.mSkillId = recordData.mData.mSkillId;\n\n        if (record.baseOverrides.hasEnchantmentId)\n        {\n            if (recordData.mEnchant.empty() || doesRecordIdExist<ESM::Enchantment>(recordData.mEnchant))\n                finalData.mEnchant = recordData.mEnchant;\n            else\n                LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring invalid enchantmentId %s\", recordData.mEnchant.c_str());\n        }\n\n        if (record.baseOverrides.hasEnchantmentCharge)\n            finalData.mData.mEnchant = recordData.mData.mEnchant;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::CellRecord& record)\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    ESM::Cell recordData = record.data;\n\n    if (recordData.mName.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    MWWorld::Ptr playerPtr = world->getPlayerPtr();\n    ESM::Cell playerCell = *playerPtr.getCell()->getCell();\n    ESM::Position playerPos = playerPtr.getRefData().getPosition();\n\n    bool isActiveCell = world->isCellActive(recordData);\n\n    if (isActiveCell)\n    {\n        mwmp::Main::get().getCellController()->uninitializeCell(recordData);\n\n        // Change to temporary holding interior cell\n        world->changeToInteriorCell(RecordHelper::getPlaceholderInteriorCellName(), playerPos, true, true);\n    }\n\n    if (record.baseId.empty())\n    {\n        recordData.mData.mFlags |= ESM::Cell::Flags::Interior;\n        recordData.mCellId.mWorldspace = Misc::StringUtils::lowerCase(recordData.mName);\n\n        world->unloadCell(recordData);\n        world->clearCellStore(recordData);\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Cell>(record.baseId))\n    {\n        const ESM::Cell *baseData = world->getStore().get<ESM::Cell>().search(record.baseId);\n        ESM::Cell finalData = *baseData;\n        finalData.mName = recordData.mName;\n        finalData.mCellId.mWorldspace = Misc::StringUtils::lowerCase(recordData.mName);\n\n        world->unloadCell(finalData);\n        world->clearCellStore(finalData);\n        world->getModifiableStore().overrideRecord(finalData);\n\n        // Create a Pathgrid record for this new Cell based on the base Cell's Pathgrid\n        // Note: This has to be done after the new Cell has been created so the Pathgrid override\n        //       can correctly determine whether the Cell is an interior or an exterior\n        const ESM::Pathgrid* basePathgrid = world->getStore().get<ESM::Pathgrid>().search(record.baseId);\n\n        if (basePathgrid)\n        {\n            ESM::Pathgrid finalPathgrid = *basePathgrid;\n            finalPathgrid.mCell = recordData.mName;\n            world->getModifiableStore().overrideRecord(finalPathgrid);\n        }\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    // Move the player back to the cell they were in\n    if (isActiveCell)\n    {\n        if (playerCell.isExterior())\n            world->changeToExteriorCell(playerPos, true, true);\n        else\n            world->changeToInteriorCell(playerCell.mName, playerPos, true, true);\n    }\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::ClothingRecord& record)\n{\n    const ESM::Clothing &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Clothing>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        if (!recordData.mEnchant.empty() && !doesRecordIdExist<ESM::Enchantment>(recordData.mEnchant))\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring new clothing record with invalid enchantmentId %s\", recordData.mEnchant.c_str());\n            return;\n        }\n        else\n            world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Clothing>(record.baseId))\n    {\n        const ESM::Clothing *baseData = world->getStore().get<ESM::Clothing>().search(record.baseId);\n        ESM::Clothing finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasSubtype)\n            finalData.mData.mType = recordData.mData.mType;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasEnchantmentId)\n        {\n            if (recordData.mEnchant.empty() || doesRecordIdExist<ESM::Enchantment>(recordData.mEnchant))\n                finalData.mEnchant = recordData.mEnchant;\n            else\n                LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring invalid enchantmentId %s\", recordData.mEnchant.c_str());\n        }\n\n        if (record.baseOverrides.hasEnchantmentCharge)\n            finalData.mData.mEnchant = recordData.mData.mEnchant;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        if (record.baseOverrides.hasBodyParts)\n            finalData.mParts.mParts = recordData.mParts.mParts;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::ContainerRecord& record)\n{\n    const ESM::Container &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Container>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Container>(record.baseId))\n    {\n        const ESM::Container *baseData = world->getStore().get<ESM::Container>().search(record.baseId);\n        ESM::Container finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mWeight = recordData.mWeight;\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mFlags = recordData.mFlags;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        if (record.baseOverrides.hasInventory)\n            finalData.mInventory.mList = recordData.mInventory.mList;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::CreatureRecord& record)\n{\n    const ESM::Creature &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Creature>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Creature>(record.baseId))\n    {\n        const ESM::Creature *baseData = world->getStore().get<ESM::Creature>().search(record.baseId);\n        ESM::Creature finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasScale)\n            finalData.mScale = recordData.mScale;\n\n        if (record.baseOverrides.hasBloodType)\n            finalData.mBloodType = recordData.mBloodType;\n\n        if (record.baseOverrides.hasSubtype)\n            finalData.mData.mType = recordData.mData.mType;\n\n        if (record.baseOverrides.hasLevel)\n            finalData.mData.mLevel = recordData.mData.mLevel;\n\n        if (record.baseOverrides.hasHealth)\n            finalData.mData.mHealth = recordData.mData.mHealth;\n\n        if (record.baseOverrides.hasMagicka)\n            finalData.mData.mMana = recordData.mData.mMana;\n\n        if (record.baseOverrides.hasFatigue)\n            finalData.mData.mFatigue = recordData.mData.mFatigue;\n\n        if (record.baseOverrides.hasSoulValue)\n            finalData.mData.mSoul = recordData.mData.mSoul;\n\n        if (record.baseOverrides.hasDamageChop)\n        {\n            finalData.mData.mAttack[0] = recordData.mData.mAttack[0];\n            finalData.mData.mAttack[1] = recordData.mData.mAttack[1];\n        }\n\n        if (record.baseOverrides.hasDamageSlash)\n        {\n            finalData.mData.mAttack[2] = recordData.mData.mAttack[2];\n            finalData.mData.mAttack[3] = recordData.mData.mAttack[3];\n        }\n\n        if (record.baseOverrides.hasDamageThrust)\n        {\n            finalData.mData.mAttack[4] = recordData.mData.mAttack[4];\n            finalData.mData.mAttack[5] = recordData.mData.mAttack[5];\n        }\n\n        if (record.baseOverrides.hasAiFight)\n            finalData.mAiData.mFight = recordData.mAiData.mFight;\n\n        if (record.baseOverrides.hasAiFlee)\n            finalData.mAiData.mFlee = recordData.mAiData.mFlee;\n\n        if (record.baseOverrides.hasAiAlarm)\n            finalData.mAiData.mAlarm = recordData.mAiData.mAlarm;\n\n        if (record.baseOverrides.hasAiServices)\n            finalData.mAiData.mServices = recordData.mAiData.mServices;\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mFlags = recordData.mFlags;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        if (!record.inventoryBaseId.empty() && doesRecordIdExist<ESM::Creature>(record.inventoryBaseId))\n            finalData.mInventory.mList = world->getStore().get<ESM::Creature>().search(record.inventoryBaseId)->mInventory.mList;\n        else if (record.baseOverrides.hasInventory)\n            finalData.mInventory.mList = recordData.mInventory.mList;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::DoorRecord& record)\n{\n    const ESM::Door &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Door>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Door>(record.baseId))\n    {\n        const ESM::Door *baseData = world->getStore().get<ESM::Door>().search(record.baseId);\n        ESM::Door finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasOpenSound)\n            finalData.mOpenSound = recordData.mOpenSound;\n\n        if (record.baseOverrides.hasCloseSound)\n            finalData.mCloseSound = recordData.mCloseSound;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::EnchantmentRecord& record)\n{\n    const ESM::Enchantment &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        if (recordData.mEffects.mList.empty())\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring new enchantment record with no effects\");\n            return;\n        }\n        else\n            world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Enchantment>(record.baseId))\n    {\n        const ESM::Enchantment *baseData = world->getStore().get<ESM::Enchantment>().search(record.baseId);\n        ESM::Enchantment finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasSubtype)\n            finalData.mData.mType = recordData.mData.mType;\n\n        if (record.baseOverrides.hasCost)\n            finalData.mData.mCost = recordData.mData.mCost;\n\n        if (record.baseOverrides.hasCharge)\n            finalData.mData.mCharge = recordData.mData.mCharge;\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mData.mFlags = recordData.mData.mFlags;\n\n        if (record.baseOverrides.hasEffects)\n            finalData.mEffects.mList = recordData.mEffects.mList;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::GameSettingRecord& record)\n{\n    const ESM::GameSetting& recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::GameSetting>(record.baseId))\n    {\n        const ESM::GameSetting* baseData = world->getStore().get<ESM::GameSetting>().search(record.baseId);\n        ESM::GameSetting finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::IngredientRecord& record)\n{\n    const ESM::Ingredient &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Ingredient>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Ingredient>(record.baseId))\n    {\n        const ESM::Ingredient *baseData = world->getStore().get<ESM::Ingredient>().search(record.baseId);\n        ESM::Ingredient finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        if (record.baseOverrides.hasEffects)\n        {\n            const static unsigned int effectCap = sizeof(recordData.mData.mEffectID) / sizeof(recordData.mData.mEffectID[0]);\n\n            for (int effectIndex = 0; effectIndex < effectCap; effectIndex++)\n            {\n                finalData.mData.mEffectID[effectIndex] = recordData.mData.mEffectID[effectIndex];\n                finalData.mData.mAttributes[effectIndex] = recordData.mData.mAttributes[effectIndex];\n                finalData.mData.mSkills[effectIndex] = recordData.mData.mSkills[effectIndex];\n            }\n        }\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::LightRecord& record)\n{\n    const ESM::Light &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Light>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Light>(record.baseId))\n    {\n        const ESM::Light *baseData = world->getStore().get<ESM::Light>().search(record.baseId);\n        ESM::Light finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasSound)\n            finalData.mSound = recordData.mSound;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasTime)\n            finalData.mData.mTime = recordData.mData.mTime;\n\n        if (record.baseOverrides.hasRadius)\n            finalData.mData.mRadius = recordData.mData.mRadius;\n\n        if (record.baseOverrides.hasColor)\n            finalData.mData.mColor = recordData.mData.mColor;\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mData.mFlags = recordData.mData.mFlags;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::LockpickRecord& record)\n{\n    const ESM::Lockpick &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Lockpick>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Lockpick>(record.baseId))\n    {\n        const ESM::Lockpick *baseData = world->getStore().get<ESM::Lockpick>().search(record.baseId);\n        ESM::Lockpick finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasQuality)\n            finalData.mData.mQuality = recordData.mData.mQuality;\n\n        if (record.baseOverrides.hasUses)\n            finalData.mData.mUses = recordData.mData.mUses;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::MiscellaneousRecord& record)\n{\n    const ESM::Miscellaneous &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Miscellaneous>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Miscellaneous>(record.baseId))\n    {\n        const ESM::Miscellaneous *baseData = world->getStore().get<ESM::Miscellaneous>().search(record.baseId);\n        ESM::Miscellaneous finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasKeyState)\n            finalData.mData.mIsKey = recordData.mData.mIsKey;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::NpcRecord& record)\n{\n    const ESM::NPC &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::NPC>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        if (!doesRecordIdExist<ESM::Race>(recordData.mRace))\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring new NPC record with invalid race provided\");\n            return;\n        }\n        else if (!doesRecordIdExist<ESM::Class>(recordData.mClass))\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring new NPC record with invalid class provided\");\n            return;\n        }\n        else\n            world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::NPC>(record.baseId))\n    {\n        const ESM::NPC *baseData = world->getStore().get<ESM::NPC>().search(record.baseId);\n        ESM::NPC finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mFlags = recordData.mFlags;\n\n        // Because the gender is part of mFlags and can easily be set incorrectly there,\n        // we handle it separately here\n        if (record.baseOverrides.hasGender)\n            finalData.setIsMale(recordData.isMale());\n        else\n            finalData.setIsMale(baseData->isMale());\n\n        if (record.baseOverrides.hasRace)\n            finalData.mRace = recordData.mRace;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasHair)\n            finalData.mHair = recordData.mHair;\n\n        if (record.baseOverrides.hasHead)\n            finalData.mHead = recordData.mHead;\n\n        if (!recordData.mClass.empty())\n            finalData.mClass = recordData.mClass;\n\n        if (record.baseOverrides.hasFaction)\n            finalData.mFaction = recordData.mFaction;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        if (record.baseOverrides.hasLevel)\n            finalData.mNpdt.mLevel = recordData.mNpdt.mLevel;\n\n        if (record.baseOverrides.hasHealth)\n            finalData.mNpdt.mHealth = recordData.mNpdt.mHealth;\n\n        if (record.baseOverrides.hasMagicka)\n            finalData.mNpdt.mMana = recordData.mNpdt.mMana;\n\n        if (record.baseOverrides.hasFatigue)\n            finalData.mNpdt.mFatigue = recordData.mNpdt.mFatigue;\n\n        if (record.baseOverrides.hasAiFight)\n            finalData.mAiData.mFight = recordData.mAiData.mFight;\n\n        if (record.baseOverrides.hasAiFlee)\n            finalData.mAiData.mFlee = recordData.mAiData.mFlee;\n\n        if (record.baseOverrides.hasAiAlarm)\n            finalData.mAiData.mAlarm = recordData.mAiData.mAlarm;\n\n        if (record.baseOverrides.hasAiServices)\n            finalData.mAiData.mServices = recordData.mAiData.mServices;\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mFlags = recordData.mFlags;\n\n        if (record.baseOverrides.hasAutoCalc)\n        {\n            finalData.mNpdtType = recordData.mNpdtType;\n\n            if ((recordData.mFlags & ESM::NPC::Autocalc) != 0)\n                finalData.mFlags |= ESM::NPC::Autocalc;\n            else\n                finalData.mFlags &= ~ESM::NPC::Autocalc;\n        }\n\n        if (!record.inventoryBaseId.empty() && doesRecordIdExist<ESM::NPC>(record.inventoryBaseId))\n            finalData.mInventory.mList = world->getStore().get<ESM::NPC>().search(record.inventoryBaseId)->mInventory.mList;\n        else if (record.baseOverrides.hasInventory)\n            finalData.mInventory.mList = recordData.mInventory.mList;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::PotionRecord& record)\n{\n    const ESM::Potion &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Potion>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Potion>(record.baseId))\n    {\n        const ESM::Potion *baseData = world->getStore().get<ESM::Potion>().search(record.baseId);\n        ESM::Potion finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasAutoCalc)\n            finalData.mData.mAutoCalc = recordData.mData.mAutoCalc;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        if (record.baseOverrides.hasEffects)\n            finalData.mEffects.mList = recordData.mEffects.mList;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::ProbeRecord& record)\n{\n    const ESM::Probe &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Probe>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Probe>(record.baseId))\n    {\n        const ESM::Probe *baseData = world->getStore().get<ESM::Probe>().search(record.baseId);\n        ESM::Probe finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasQuality)\n            finalData.mData.mQuality = recordData.mData.mQuality;\n\n        if (record.baseOverrides.hasUses)\n            finalData.mData.mUses = recordData.mData.mUses;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::RepairRecord& record)\n{\n    const ESM::Repair &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Repair>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Repair>(record.baseId))\n    {\n        const ESM::Repair *baseData = world->getStore().get<ESM::Repair>().search(record.baseId);\n        ESM::Repair finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasQuality)\n            finalData.mData.mQuality = recordData.mData.mQuality;\n\n        if (record.baseOverrides.hasUses)\n            finalData.mData.mUses = recordData.mData.mUses;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::ScriptRecord& record)\n{\n    const ESM::Script &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Script>(record.baseId))\n    {\n        const ESM::Script *baseData = world->getStore().get<ESM::Script>().search(record.baseId);\n        ESM::Script finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasScriptText)\n            finalData.mScriptText = recordData.mScriptText;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::SoundRecord& record)\n{\n    const ESM::Sound& recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Sound>(recordData.mId);\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Sound>(record.baseId))\n    {\n        const ESM::Sound* baseData = world->getStore().get<ESM::Sound>().search(record.baseId);\n        ESM::Sound finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasSound)\n            finalData.mSound = recordData.mSound;\n\n        if (record.baseOverrides.hasVolume)\n            finalData.mData.mVolume = recordData.mData.mVolume;\n\n        if (record.baseOverrides.hasMinRange)\n            finalData.mData.mMinRange = recordData.mData.mMinRange;\n\n        if (record.baseOverrides.hasMaxRange)\n            finalData.mData.mMaxRange = recordData.mData.mMaxRange;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::SpellRecord& record)\n{\n    const ESM::Spell &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Spell>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Spell>(record.baseId))\n    {\n        const ESM::Spell *baseData = world->getStore().get<ESM::Spell>().search(record.baseId);\n        ESM::Spell finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasSubtype)\n            finalData.mData.mType = recordData.mData.mType;\n\n        if (record.baseOverrides.hasCost)\n            finalData.mData.mCost = recordData.mData.mCost;\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mData.mFlags = recordData.mData.mFlags;\n\n        if (record.baseOverrides.hasEffects)\n            finalData.mEffects.mList = recordData.mEffects.mList;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::StaticRecord& record)\n{\n    const ESM::Static &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Static>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Static>(record.baseId))\n    {\n        const ESM::Static *baseData = world->getStore().get<ESM::Static>().search(record.baseId);\n        ESM::Static finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::overrideRecord(const mwmp::WeaponRecord& record)\n{\n    const ESM::Weapon &recordData = record.data;\n\n    if (recordData.mId.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with no id provided\");\n        return;\n    }\n\n    bool isExistingId = doesRecordIdExist<ESM::Weapon>(recordData.mId);\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    if (record.baseId.empty())\n    {\n        if (!recordData.mEnchant.empty() && !doesRecordIdExist<ESM::Enchantment>(recordData.mEnchant))\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring new weapon record with invalid enchantmentId %s\", recordData.mEnchant.c_str());\n            return;\n        }\n        else\n            world->getModifiableStore().overrideRecord(recordData);\n    }\n    else if (doesRecordIdExist<ESM::Weapon>(record.baseId))\n    {\n        const ESM::Weapon *baseData = world->getStore().get<ESM::Weapon>().search(record.baseId);\n        ESM::Weapon finalData = *baseData;\n        finalData.mId = recordData.mId;\n\n        if (record.baseOverrides.hasName)\n            finalData.mName = recordData.mName;\n\n        if (record.baseOverrides.hasModel)\n            finalData.mModel = recordData.mModel;\n\n        if (record.baseOverrides.hasIcon)\n            finalData.mIcon = recordData.mIcon;\n\n        if (record.baseOverrides.hasSubtype)\n            finalData.mData.mType = recordData.mData.mType;\n\n        if (record.baseOverrides.hasWeight)\n            finalData.mData.mWeight = recordData.mData.mWeight;\n\n        if (record.baseOverrides.hasValue)\n            finalData.mData.mValue = recordData.mData.mValue;\n\n        if (record.baseOverrides.hasHealth)\n            finalData.mData.mHealth = recordData.mData.mHealth;\n\n        if (record.baseOverrides.hasSpeed)\n            finalData.mData.mSpeed = recordData.mData.mSpeed;\n\n        if (record.baseOverrides.hasReach)\n            finalData.mData.mReach = recordData.mData.mReach;\n\n        if (record.baseOverrides.hasDamageChop)\n        {\n            finalData.mData.mChop[0] = recordData.mData.mChop[0];\n            finalData.mData.mChop[1] = recordData.mData.mChop[1];\n        }\n\n        if (record.baseOverrides.hasDamageSlash)\n        {\n            finalData.mData.mSlash[0] = recordData.mData.mSlash[0];\n            finalData.mData.mSlash[1] = recordData.mData.mSlash[1];\n        }\n\n        if (record.baseOverrides.hasDamageThrust)\n        {\n            finalData.mData.mThrust[0] = recordData.mData.mThrust[0];\n            finalData.mData.mThrust[1] = recordData.mData.mThrust[1];\n        }\n\n        if (record.baseOverrides.hasFlags)\n            finalData.mData.mFlags = recordData.mData.mFlags;\n\n        if (record.baseOverrides.hasEnchantmentId)\n        {\n            if (recordData.mEnchant.empty() || doesRecordIdExist<ESM::Enchantment>(recordData.mEnchant))\n                finalData.mEnchant = recordData.mEnchant;\n            else\n                LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring invalid enchantmentId %s\", recordData.mEnchant.c_str());\n        }\n\n        if (record.baseOverrides.hasEnchantmentCharge)\n            finalData.mData.mEnchant = recordData.mData.mEnchant;\n\n        if (record.baseOverrides.hasScript)\n            finalData.mScript = recordData.mScript;\n\n        world->getModifiableStore().overrideRecord(finalData);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"-- Ignoring record override with invalid baseId %s\", record.baseId.c_str());\n        return;\n    }\n\n    if (isExistingId)\n        world->updatePtrsWithRefId(recordData.mId);\n}\n\nvoid RecordHelper::createPlaceholderInteriorCell()\n{\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n\n    ESM::Cell placeholderInterior;\n    placeholderInterior.mData.mFlags |= ESM::Cell::Flags::Interior;\n    placeholderInterior.mName = placeholderInteriorCellName;\n\n    world->getModifiableStore().insert(placeholderInterior);\n}\n\nconst std::string RecordHelper::getPlaceholderInteriorCellName()\n{\n    return placeholderInteriorCellName;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/RecordHelper.hpp",
    "content": "#ifndef OPENMW_RECORDHELPER_HPP\n#define OPENMW_RECORDHELPER_HPP\n\n#include <components/openmw-mp/Base/BaseWorldstate.hpp>\n\n#include \"../mwbase/environment.hpp\"\n\nnamespace RecordHelper\n{\n    void overrideRecord(const mwmp::ActivatorRecord& record);\n    void overrideRecord(const mwmp::ApparatusRecord& record);\n    void overrideRecord(const mwmp::ArmorRecord& record);\n    void overrideRecord(const mwmp::BodyPartRecord& record);\n    void overrideRecord(const mwmp::BookRecord& record);\n    void overrideRecord(const mwmp::CellRecord& record);\n    void overrideRecord(const mwmp::ClothingRecord& record);\n    void overrideRecord(const mwmp::ContainerRecord& record);\n    void overrideRecord(const mwmp::CreatureRecord& record);\n    void overrideRecord(const mwmp::DoorRecord& record);\n    void overrideRecord(const mwmp::EnchantmentRecord& record);\n    void overrideRecord(const mwmp::GameSettingRecord& record);\n    void overrideRecord(const mwmp::IngredientRecord& record);\n    void overrideRecord(const mwmp::LightRecord& record);\n    void overrideRecord(const mwmp::LockpickRecord& record);\n    void overrideRecord(const mwmp::MiscellaneousRecord& record);\n    void overrideRecord(const mwmp::NpcRecord& record);\n    void overrideRecord(const mwmp::PotionRecord& record);\n    void overrideRecord(const mwmp::ProbeRecord& record);\n    void overrideRecord(const mwmp::RepairRecord& record);\n    void overrideRecord(const mwmp::ScriptRecord& record);\n    void overrideRecord(const mwmp::SoundRecord& record);\n    void overrideRecord(const mwmp::SpellRecord& record);\n    void overrideRecord(const mwmp::StaticRecord& record);\n    void overrideRecord(const mwmp::WeaponRecord& record);\n\n    template<class RecordType>\n    void overrideRecord(const RecordType &record)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n\n        world->getModifiableStore().overrideRecord(record);\n    }\n\n    template<class RecordType>\n    const RecordType *createRecord(const RecordType &record)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n\n        return world->createRecord(record);\n    }\n\n    template<class RecordType>\n    bool doesRecordIdExist(const std::string& id)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n\n        return world->getStore().get<RecordType>().search(id);\n    }\n\n    void createPlaceholderInteriorCell();\n    const std::string getPlaceholderInteriorCellName();\n\n    const std::string placeholderInteriorCellName = \"$Transitional Void\";\n}\n\n\n#endif //OPENMW_RECORDHELPER_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/ScriptController.cpp",
    "content": "#include <components/openmw-mp/Base/BaseStructs.hpp>\n\n#include \"../mwscript/interpretercontext.hpp\"\n\n#include \"ScriptController.hpp\"\n\nunsigned short ScriptController::getPacketOriginFromContextType(unsigned short contextType)\n{\n    if (contextType == Interpreter::Context::CONSOLE)\n        return mwmp::CLIENT_CONSOLE;\n    else if (contextType == Interpreter::Context::DIALOGUE)\n        return mwmp::CLIENT_DIALOGUE;\n    else if (contextType == Interpreter::Context::SCRIPT_LOCAL)\n        return mwmp::CLIENT_SCRIPT_LOCAL;\n    else if (contextType == Interpreter::Context::SCRIPT_GLOBAL)\n        return mwmp::CLIENT_SCRIPT_GLOBAL;\n\n    return mwmp::CLIENT_GAMEPLAY;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/ScriptController.hpp",
    "content": "#ifndef OPENMW_SCRIPTCONTROLLER_HPP\n#define OPENMW_SCRIPTCONTROLLER_HPP\n\nnamespace ScriptController\n{\n    unsigned short getPacketOriginFromContextType(unsigned short contextType);\n}\n\n\n#endif //OPENMW_SCRIPTCONTROLLER_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/Worldstate.cpp",
    "content": "#include <components/openmw-mp/TimedLog.hpp>\n\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwgui/windowmanagerimp.hpp\"\n\n#include \"../mwmechanics/mechanicsmanagerimp.hpp\"\n\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/worldimp.hpp\"\n\n#include \"Worldstate.hpp\"\n#include \"Main.hpp\"\n#include \"Networking.hpp\"\n#include \"PlayerList.hpp\"\n#include \"DedicatedPlayer.hpp\"\n#include \"RecordHelper.hpp\"\n#include \"CellController.hpp\"\n\nusing namespace mwmp;\n\nWorldstate::Worldstate()\n{\n    hasPlayerCollision = true;\n    hasActorCollision = true;\n    hasPlacedObjectCollision = false;\n    useActorCollisionForPlacedObjects = false;\n}\n\nWorldstate::~Worldstate()\n{\n\n}\n\nNetworking *Worldstate::getNetworking()\n{\n    return mwmp::Main::get().getNetworking();\n}\n\nvoid Worldstate::addRecords()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_RECORD_DYNAMIC with %i records of type %i\",\n        recordsCount, recordsType);\n\n    if (recordsType == mwmp::RECORD_TYPE::SPELL)\n    {\n        for (auto &&record : spellRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- spell record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::POTION)\n    {\n        for (auto &&record : potionRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- potion record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n    {\n        for (auto &&record : enchantmentRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- enchantment record %s, %i\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mData.mType,\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::CREATURE)\n    {\n        for (auto &&record : creatureRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- creature record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::NPC)\n    {\n        for (auto &&record : npcRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- NPC record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::ARMOR)\n    {\n        for (auto &&record : armorRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- armor record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::BOOK)\n    {\n        for (auto &&record : bookRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- book record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::CLOTHING)\n    {\n        for (auto &&record : clothingRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- clothing record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n    {\n        for (auto &&record : miscellaneousRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- miscellaneous record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::WEAPON)\n    {\n        for (auto &&record : weaponRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- weapon record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::CONTAINER)\n    {\n        for (auto &&record : containerRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- container record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::DOOR)\n    {\n        for (auto &&record : doorRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- door record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n    {\n        for (auto &&record : activatorRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- activator record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::STATIC)\n    {\n        for (auto &&record : staticRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- static record %s\\n-- baseId is %s\", record.data.mId.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::INGREDIENT)\n    {\n        for (auto &&record : ingredientRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- ingredient record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::APPARATUS)\n    {\n        for (auto &&record : apparatusRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- apparatus record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::LOCKPICK)\n    {\n        for (auto &&record : lockpickRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- lockpick record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::PROBE)\n    {\n        for (auto &&record : probeRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- probe record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::REPAIR)\n    {\n        for (auto &&record : repairRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- repair record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::LIGHT)\n    {\n        for (auto &&record : lightRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- light record %s, %s\\n-- baseId is %s\", record.data.mId.c_str(), record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::CELL)\n    {\n        for (auto &&record : cellRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- cell record %s\\n-- baseId is %s\", record.data.mName.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::SCRIPT)\n    {\n        for (auto &&record : scriptRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- script record %s\\n-- baseId is %s\", record.data.mId.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::BODYPART)\n    {\n        for (auto &&record : bodyPartRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- bodypart record %s\\n-- baseId is %s\", record.data.mId.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::SOUND)\n    {\n        for (auto&& record : soundRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- sound record %s\\n-- baseId is %s\", record.data.mId.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n    else if (recordsType == mwmp::RECORD_TYPE::GAMESETTING)\n    {\n        for (auto&& record : gameSettingRecords)\n        {\n            bool hasBaseId = !record.baseId.empty();\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- gameSetting record %s\\n-- baseId is %s\", record.data.mId.c_str(),\n                hasBaseId ? record.baseId.c_str() : \"empty\");\n\n            RecordHelper::overrideRecord(record);\n        }\n    }\n}\n\nbool Worldstate::containsExploredMapTile(int cellX, int cellY)\n{\n    for (const auto &mapTile : exploredMapTiles)\n    {\n        if (mapTile.x == cellX && mapTile.y == cellY)\n            return true;\n    }\n\n    return false;\n}\n\nvoid Worldstate::markExploredMapTile(int cellX, int cellY)\n{\n    mwmp::MapTile exploredTile;\n    exploredTile.x = cellX;\n    exploredTile.y = cellY;\n    exploredMapTiles.push_back(exploredTile);\n}\n\nvoid Worldstate::setClientGlobals()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_CLIENT_SCRIPT_GLOBAL with the following global values:\");\n    std::string debugMessage = \"\";\n\n    for (const auto &clientGlobal : clientGlobals)\n    {\n        if (TimedLog::GetLevel() <= TimedLog::LOG_INFO)\n        {\n            if (!debugMessage.empty())\n                debugMessage += \", \";\n\n            std::string variableTypeAsString;\n            std::string valueAsString;\n\n            if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::SHORT || clientGlobal.variableType == mwmp::VARIABLE_TYPE::LONG)\n            {\n                variableTypeAsString = clientGlobal.variableType == mwmp::VARIABLE_TYPE::SHORT ? \"short\" : \"long\";\n                valueAsString = std::to_string(clientGlobal.intValue);\n            }\n            else if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::FLOAT)\n            {\n                variableTypeAsString = \"float\";\n                valueAsString = std::to_string(clientGlobal.floatValue);\n            }\n\n            debugMessage += clientGlobal.id + \": \" + variableTypeAsString + \" \" + valueAsString;\n        }\n\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n\n        // If this global doesn't exist, create it\n        if (!world->hasGlobal(clientGlobal.id))\n        {\n            ESM::VarType varType;\n\n            if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::SHORT)\n                varType = ESM::VarType::VT_Short;\n            else if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::LONG)\n                varType = ESM::VarType::VT_Long;\n            if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::FLOAT)\n                varType = ESM::VarType::VT_Float;\n\n            world->createGlobal(clientGlobal.id, varType);\n        }\n\n        if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::SHORT || clientGlobal.variableType == mwmp::VARIABLE_TYPE::LONG)\n            world->setGlobalInt(clientGlobal.id, clientGlobal.intValue);\n        else if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::FLOAT)\n            world->setGlobalFloat(clientGlobal.id, clientGlobal.floatValue);\n    }\n\n    LOG_APPEND(TimedLog::LOG_INFO, \"- %s\", debugMessage.c_str());\n}\n\nvoid Worldstate::setKills()\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_WORLD_KILL_COUNT with the following kill counts:\");\n    std::string debugMessage = \"\";\n\n    for (const auto &killChange : killChanges)\n    {\n        if (TimedLog::GetLevel() <= TimedLog::LOG_INFO)\n        {\n            if (!debugMessage.empty())\n                debugMessage += \", \";\n\n            debugMessage += killChange.refId + \": \" + std::to_string(killChange.number);\n        }\n\n        MWBase::Environment::get().getMechanicsManager()->setDeaths(killChange.refId, killChange.number);\n    }\n\n    LOG_APPEND(TimedLog::LOG_INFO, \"- %s\", debugMessage.c_str());\n}\n\nvoid Worldstate::setMapExplored()\n{\n    for (const auto &mapTile : mapTiles)\n    {\n        const MWWorld::CellStore *cellStore = MWBase::Environment::get().getWorld()->getExterior(mapTile.x, mapTile.y);\n\n        if (!cellStore->getCell()->mName.empty())\n            MWBase::Environment::get().getWindowManager()->addVisitedLocation(cellStore->getCell()->mName, mapTile.x, mapTile.y);\n\n        MWBase::Environment::get().getWindowManager()->setGlobalMapImage(mapTile.x, mapTile.y, mapTile.imageData);\n\n        // Keep this tile marked as explored so we don't send any more packets for it\n        markExploredMapTile(mapTile.x, mapTile.y);\n    }\n}\n\nvoid Worldstate::setWeather()\n{\n    MWBase::World *world = MWBase::Environment::get().getWorld();\n\n    // There's a chance we've been sent the weather for a region right after a teleportation\n    // that hasn't been registered in the WeatherManager yet, meaning the WeatherManager\n    // doesn't have the correct new region set for us, so make sure we update it\n    world->updateWeather(0);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Setting weather for region: %s, currentWeather: %i, \"\n        \"nextWeather: %i, queuedWeather: %i, transitionFactor: %f, forceWeather is %s\",\n        weather.region.c_str(), weather.currentWeather, weather.nextWeather,\n        weather.queuedWeather, weather.transitionFactor, forceWeather ? \"true\" : \"false\");\n\n    world->setRegionWeather(weather.region.c_str(), weather.currentWeather, weather.nextWeather,\n        weather.queuedWeather, weather.transitionFactor, forceWeather);\n}\n\nvoid Worldstate::resetCells(std::vector<ESM::Cell>* cells)\n{\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n\n    bool haveUnloadedActiveCells = false;\n    ESM::Cell playerCell = *world->getPlayerPtr().getCell()->getCell();\n    ESM::Position playerPos = world->getPlayerPtr().getRefData().getPosition();\n    std::vector<RakNet::RakNetGUID> playersInCell;\n\n    for (auto cell : *cells)\n    {\n        if (!haveUnloadedActiveCells)\n        {\n            if (world->isCellActive(cell))\n            {\n                playersInCell = mwmp::PlayerList::getPlayersInCell(cell);\n\n                // If there are any DedicatedPlayers in this cell, also move them to the temporary holding interior cell\n                if (!playersInCell.empty())\n                {\n                    for (RakNet::RakNetGUID otherGuid : playersInCell)\n                    {\n                        DedicatedPlayer* dedicatedPlayer = mwmp::PlayerList::getPlayer(otherGuid);\n                        dedicatedPlayer->cell = *world->getInterior(RecordHelper::getPlaceholderInteriorCellName())->getCell();\n                        dedicatedPlayer->setCell();\n                    }\n                }\n\n                // Change to temporary holding interior cell\n                world->changeToInteriorCell(RecordHelper::getPlaceholderInteriorCellName(), playerPos, true, true);\n\n                mwmp::Main::get().getCellController()->uninitializeCells();\n                world->unloadActiveCells();\n\n                haveUnloadedActiveCells = true;\n            }\n        }\n\n        world->clearCellStore(cell);\n\n        for (RakNet::RakNetGUID otherGuid : playersInCell)\n        {\n            DedicatedPlayer* dedicatedPlayer = mwmp::PlayerList::getPlayer(otherGuid);\n            dedicatedPlayer->cell = cell;\n            dedicatedPlayer->setCell();\n        }\n    }\n\n    // Move the player from their temporary holding cell to their previous cell\n    if (haveUnloadedActiveCells)\n    {\n        if (playerCell.isExterior())\n            world->changeToExteriorCell(playerPos, true, true);\n        else\n            world->changeToInteriorCell(playerCell.mName, playerPos, true, true);\n    }\n}\n\nvoid Worldstate::sendClientGlobal(std::string varName, int value, mwmp::VARIABLE_TYPE variableType)\n{\n    clientGlobals.clear();\n\n    mwmp::ClientVariable clientVariable;\n    clientVariable.id = varName;\n    clientVariable.variableType = variableType;\n    clientVariable.intValue = value;\n\n    std::string variableTypeAsString;\n\n    if (variableType == mwmp::VARIABLE_TYPE::SHORT)\n        variableTypeAsString = \"short\";\n    else if (variableType == mwmp::VARIABLE_TYPE::LONG)\n        variableTypeAsString = \"long\";\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_CLIENT_SCRIPT_GLOBAL with name %s, type %s, value %i\",\n        varName.c_str(), variableTypeAsString.c_str(), value);\n\n    clientGlobals.push_back(clientVariable);\n\n    getNetworking()->getWorldstatePacket(ID_CLIENT_SCRIPT_GLOBAL)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_CLIENT_SCRIPT_GLOBAL)->Send();\n}\n\nvoid Worldstate::sendClientGlobal(std::string varName, float value)\n{\n    clientGlobals.clear();\n\n    mwmp::ClientVariable clientVariable;\n    clientVariable.id = varName;\n    clientVariable.variableType = mwmp::VARIABLE_TYPE::FLOAT;\n    clientVariable.floatValue = value;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_CLIENT_SCRIPT_GLOBAL with name %s, type float, value %f\", varName.c_str(), value);\n\n    clientGlobals.push_back(clientVariable);\n\n    getNetworking()->getWorldstatePacket(ID_CLIENT_SCRIPT_GLOBAL)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_CLIENT_SCRIPT_GLOBAL)->Send();\n}\n\nvoid Worldstate::sendMapExplored(int cellX, int cellY, const std::vector<char>& imageData)\n{\n    mapTiles.clear();\n\n    mwmp::MapTile mapTile;\n    mapTile.x = cellX;\n    mapTile.y = cellY;\n    mapTile.imageData = imageData;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_MAP with x: %i, y: %i\", cellX, cellY);\n\n    mapTiles.push_back(mapTile);\n\n    getNetworking()->getWorldstatePacket(ID_WORLD_MAP)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_WORLD_MAP)->Send();\n}\n\nvoid Worldstate::sendWeather(std::string region, int currentWeather, int nextWeather, int queuedWeather, float transitionFactor)\n{\n    forceWeather = false;\n    weather.region = region;\n    weather.currentWeather = currentWeather;\n    weather.nextWeather = nextWeather;\n    weather.queuedWeather = queuedWeather;\n    weather.transitionFactor = transitionFactor;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_PLAYER_WEATHER with region: %s, currentWeather: %i, \"\n        \"nextWeather: %i, queuedWeather, %i, transitionFactor: %f\",\n        region.c_str(), currentWeather, nextWeather, queuedWeather, transitionFactor);\n\n    getNetworking()->getWorldstatePacket(ID_WORLD_WEATHER)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_WORLD_WEATHER)->Send();\n}\n\nvoid Worldstate::sendEnchantmentRecord(const ESM::Enchantment* enchantment)\n{\n    enchantmentRecords.clear();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_RECORD_DYNAMIC with enchantment\");\n\n    recordsType = mwmp::RECORD_TYPE::ENCHANTMENT;\n\n    mwmp::EnchantmentRecord record;\n    record.data = *enchantment;\n    enchantmentRecords.push_back(record);\n\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();\n}\n\nvoid Worldstate::sendPotionRecord(const ESM::Potion* potion, unsigned int quantity)\n{\n    potionRecords.clear();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_RECORD_DYNAMIC with potion %s\", potion->mName.c_str());\n\n    recordsType = mwmp::RECORD_TYPE::POTION;\n\n    mwmp::PotionRecord record;\n    record.data = *potion;\n    record.quantity = quantity;\n    potionRecords.push_back(record);\n\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();\n}\n\nvoid Worldstate::sendSpellRecord(const ESM::Spell* spell)\n{\n    spellRecords.clear();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_RECORD_DYNAMIC with spell %s\", spell->mName.c_str());\n\n    recordsType = mwmp::RECORD_TYPE::SPELL;\n\n    mwmp::SpellRecord record;\n    record.data = *spell;\n    spellRecords.push_back(record);\n\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();\n}\n\nvoid Worldstate::sendArmorRecord(const ESM::Armor* armor, std::string baseId)\n{\n    armorRecords.clear();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_RECORD_DYNAMIC with armor %s\", armor->mName.c_str());\n\n    recordsType = mwmp::RECORD_TYPE::ARMOR;\n\n    mwmp::ArmorRecord record;\n    record.data = *armor;\n    record.baseId = baseId;\n    record.baseOverrides.hasName = true;\n    record.baseOverrides.hasEnchantmentId = true;\n    record.baseOverrides.hasEnchantmentCharge = true;\n    armorRecords.push_back(record);\n\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();\n}\n\nvoid Worldstate::sendBookRecord(const ESM::Book* book, std::string baseId)\n{\n    bookRecords.clear();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_RECORD_DYNAMIC with book %s\", book->mName.c_str());\n\n    recordsType = mwmp::RECORD_TYPE::BOOK;\n\n    mwmp::BookRecord record;\n    record.data = *book;\n    record.baseId = baseId;\n    record.baseOverrides.hasName = true;\n    record.baseOverrides.hasEnchantmentId = true;\n    record.baseOverrides.hasEnchantmentCharge = true;\n    bookRecords.push_back(record);\n\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();\n}\n\nvoid Worldstate::sendClothingRecord(const ESM::Clothing* clothing, std::string baseId)\n{\n    clothingRecords.clear();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_RECORD_DYNAMIC with clothing %s\", clothing->mName.c_str());\n\n    recordsType = mwmp::RECORD_TYPE::CLOTHING;\n\n    mwmp::ClothingRecord record;\n    record.data = *clothing;\n    record.baseId = baseId;\n    record.baseOverrides.hasName = true;\n    record.baseOverrides.hasEnchantmentId = true;\n    record.baseOverrides.hasEnchantmentCharge = true;\n    clothingRecords.push_back(record);\n\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();\n}\n\nvoid Worldstate::sendWeaponRecord(const ESM::Weapon* weapon, std::string baseId, unsigned int quantity)\n{\n    weaponRecords.clear();\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_RECORD_DYNAMIC with weapon %s\", weapon->mName.c_str());\n\n    recordsType = mwmp::RECORD_TYPE::WEAPON;\n\n    mwmp::WeaponRecord record;\n    record.data = *weapon;\n    record.quantity = quantity;\n    record.baseId = baseId;\n    record.baseOverrides.hasName = true;\n    record.baseOverrides.hasEnchantmentId = true;\n    record.baseOverrides.hasEnchantmentCharge = true;\n    weaponRecords.push_back(record);\n\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);\n    getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/Worldstate.hpp",
    "content": "#ifndef OPENMW_WORLDSTATE_HPP\n#define OPENMW_WORLDSTATE_HPP\n\n#include <components/openmw-mp/Base/BaseWorldstate.hpp>\n\nnamespace mwmp\n{\n    class Networking;\n    class Worldstate : public BaseWorldstate\n    {\n    public:\n\n        Worldstate();\n        virtual ~Worldstate();\n\n        void addRecords();\n\n        bool containsExploredMapTile(int cellX, int cellY);\n        void markExploredMapTile(int cellX, int cellY);\n\n        void setClientGlobals();\n        void setKills();\n        void setMapExplored();\n        void setWeather();\n\n        void resetCells(std::vector<ESM::Cell>* cells);\n\n        void sendClientGlobal(std::string varName, int value, mwmp::VARIABLE_TYPE variableType);\n        void sendClientGlobal(std::string varName, float value);\n        void sendMapExplored(int cellX, int cellY, const std::vector<char>& imageData);\n        void sendWeather(std::string region, int currentWeather, int nextWeather, int queuedWeather, float transitionFactor);\n\n        void sendEnchantmentRecord(const ESM::Enchantment* enchantment);\n        void sendPotionRecord(const ESM::Potion* potion, unsigned int quantity);\n        void sendSpellRecord(const ESM::Spell* spell);\n\n        void sendArmorRecord(const ESM::Armor* armor, std::string baseRefId = \"\");\n        void sendBookRecord(const ESM::Book* book, std::string baseRefId = \"\");\n        void sendClothingRecord(const ESM::Clothing* clothing, std::string baseRefId = \"\");\n        void sendWeaponRecord(const ESM::Weapon* weapon, std::string baseRefId = \"\", unsigned int quantity = 1);\n\n    private:\n\n        std::vector<MapTile> exploredMapTiles;\n\n        Networking *getNetworking();\n\n    };\n}\n\n#endif //OPENMW_WORLDSTATE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/ActorProcessor.cpp",
    "content": "#include \"ActorProcessor.hpp\"\n#include \"../Networking.hpp\"\n#include \"../Main.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nActorProcessor::~ActorProcessor()\n{\n\n}\n\nbool ActorProcessor::Process(RakNet::Packet &packet, ActorList &actorList)\n{\n    RakNet::BitStream bsIn(&packet.data[1], packet.length, false);\n    bsIn.Read(guid);\n    actorList.guid = guid;\n\n    ActorPacket *myPacket = Main::get().getNetworking()->getActorPacket(packet.data[0]);\n\n    myPacket->setActorList(&actorList);\n    myPacket->SetReadStream(&bsIn);\n\n    for (auto &processor : processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            myGuid = Main::get().getLocalPlayer()->guid;\n            request = packet.length == myPacket->headerSize();\n\n            actorList.isValid = true;\n\n            if (!request && !processor.second->avoidReading)\n            {\n                myPacket->Read();\n            }\n\n            if (actorList.isValid)\n                processor.second->Do(*myPacket, actorList);\n            else\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Received %s that failed integrity check and was ignored!\", processor.second->strPacketID.c_str());\n\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/ActorProcessor.hpp",
    "content": "#ifndef OPENMW_ACTORPROCESSOR_HPP\n#define OPENMW_ACTORPROCESSOR_HPP\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n#include \"../ObjectList.hpp\"\n#include \"../ActorList.hpp\"\n#include \"BaseClientPacketProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ActorProcessor : public BasePacketProcessor<ActorProcessor>, public BaseClientPacketProcessor\n    {\n    public:\n        virtual void Do(ActorPacket &packet, ActorList &actorList) = 0;\n\n        static bool Process(RakNet::Packet &packet, ActorList &actorList);\n\n        virtual ~ActorProcessor();\n    };\n}\n\n\n#endif //OPENMW_ACTORPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/BaseClientPacketProcessor.cpp",
    "content": "#include \"BaseClientPacketProcessor.hpp\"\n#include \"../Main.hpp\"\n\nusing namespace mwmp;\n\nRakNet::RakNetGUID BaseClientPacketProcessor::guid;\nRakNet::RakNetGUID BaseClientPacketProcessor::myGuid;\nRakNet::SystemAddress BaseClientPacketProcessor::serverAddr;\nbool BaseClientPacketProcessor::request;\n\nLocalPlayer *BaseClientPacketProcessor::getLocalPlayer()\n{\n    return Main::get().getLocalPlayer();\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/BaseClientPacketProcessor.hpp",
    "content": "#ifndef OPENMW_BASECLIENTPACKETPROCESSOR_HPP\n#define OPENMW_BASECLIENTPACKETPROCESSOR_HPP\n\n#include <components/openmw-mp/Base/BasePacketProcessor.hpp>\n#include \"../LocalPlayer.hpp\"\n#include \"../DedicatedPlayer.hpp\"\n\nnamespace mwmp\n{\n    class BaseClientPacketProcessor\n    {\n    public:\n        static void SetServerAddr(RakNet::SystemAddress addr)\n        {\n            serverAddr = addr;\n        }\n\n    protected:\n        inline bool isRequest()\n        {\n            return request;\n        }\n\n        inline bool isLocal()\n        {\n            return guid == myGuid;\n        }\n\n        LocalPlayer *getLocalPlayer();\n\n    protected:\n        static RakNet::RakNetGUID guid, myGuid;\n        static RakNet::SystemAddress serverAddr;\n\n        static bool request;\n    };\n}\n\n#endif //OPENMW_BASECLIENTPACKETPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/ObjectProcessor.cpp",
    "content": "#include \"../Main.hpp\"\n#include \"../Networking.hpp\"\n\n#include \"ObjectProcessor.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nObjectProcessor::~ObjectProcessor()\n{\n\n}\n\nbool ObjectProcessor::Process(RakNet::Packet &packet, ObjectList &objectList)\n{\n    RakNet::BitStream bsIn(&packet.data[1], packet.length, false);\n    bsIn.Read(guid);\n    objectList.guid = guid;\n\n    ObjectPacket *myPacket = Main::get().getNetworking()->getObjectPacket(packet.data[0]);\n\n    myPacket->setObjectList(&objectList);\n    myPacket->SetReadStream(&bsIn);\n\n    for (auto &processor: processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            myGuid = Main::get().getLocalPlayer()->guid;\n            request = packet.length == myPacket->headerSize();\n\n            objectList.isValid = true;\n\n            if (!request && !processor.second->avoidReading)\n                myPacket->Read();\n\n            if (objectList.isValid)\n                processor.second->Do(*myPacket, objectList);\n            else\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Received %s that failed integrity check and was ignored!\", processor.second->strPacketID.c_str());\n\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/ObjectProcessor.hpp",
    "content": "#ifndef OPENMW_OBJECTPROCESSSOR_HPP\n#define OPENMW_OBJECTPROCESSSOR_HPP\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n#include \"../ObjectList.hpp\"\n#include \"../LocalPlayer.hpp\"\n#include \"../DedicatedPlayer.hpp\"\n#include \"BaseClientPacketProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ObjectProcessor : public BasePacketProcessor<ObjectProcessor>, public BaseClientPacketProcessor\n    {\n    public:\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList) = 0;\n\n        static bool Process(RakNet::Packet &packet, ObjectList &objectList);\n\n        virtual ~ObjectProcessor();\n    };\n}\n\n\n#endif //OPENMW_OBJECTPROCESSSOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/PlayerProcessor.cpp",
    "content": "#include \"../Networking.hpp\"\n#include \"PlayerProcessor.hpp\"\n#include \"../Main.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nPlayerProcessor::~PlayerProcessor()\n{\n\n}\n\nbool PlayerProcessor::Process(RakNet::Packet &packet)\n{\n    RakNet::BitStream bsIn(&packet.data[1], packet.length, false);\n    bsIn.Read(guid);\n\n    PlayerPacket *myPacket = Main::get().getNetworking()->getPlayerPacket(packet.data[0]);\n    myPacket->SetReadStream(&bsIn);\n\n    /*if (myPacket == 0)\n    {\n        // error: packet not found\n    }*/\n\n    for (auto &processor : processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            myGuid = Main::get().getLocalPlayer()->guid;\n            request = packet.length == myPacket->headerSize();\n\n            BasePlayer *player = 0;\n            if (guid != myGuid)\n                player = PlayerList::getPlayer(guid);\n            else\n                player = Main::get().getLocalPlayer();\n\n            if (!request && !processor.second->avoidReading && player != 0)\n            {\n                myPacket->setPlayer(player);\n                myPacket->Read();\n            }\n\n            processor.second->Do(*myPacket, player);\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/PlayerProcessor.hpp",
    "content": "#ifndef OPENMW_PLAYERPROCESSOR_HPP\n#define OPENMW_PLAYERPROCESSOR_HPP\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n#include \"../LocalPlayer.hpp\"\n#include \"../DedicatedPlayer.hpp\"\n#include \"../PlayerList.hpp\"\n#include \"BaseClientPacketProcessor.hpp\"\n\nnamespace mwmp\n{\n    class PlayerProcessor : public BasePacketProcessor<PlayerProcessor>, public BaseClientPacketProcessor\n    {\n    public:\n        virtual void Do(PlayerPacket &packet, BasePlayer *player) = 0;\n\n        static bool Process(RakNet::Packet &packet);\n\n        virtual ~PlayerProcessor();\n    };\n}\n\n\n\n#endif //OPENMW_PLAYERPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/ProcessorInitializer.cpp",
    "content": "#include \"ProcessorInitializer.hpp\"\n\n#include \"SystemProcessor.hpp\"\n#include \"system/ProcessorSystemHandshake.hpp\"\n\n#include \"PlayerProcessor.hpp\"\n#include \"player/ProcessorChatMessage.hpp\"\n#include \"player/ProcessorGUIMessageBox.hpp\"\n#include \"player/ProcessorUserDisconnected.hpp\"\n#include \"player/ProcessorGameSettings.hpp\"\n#include \"player/ProcessorPlayerAlly.hpp\"\n#include \"player/ProcessorPlayerAnimFlags.hpp\"\n#include \"player/ProcessorPlayerAnimPlay.hpp\"\n#include \"player/ProcessorPlayerAttack.hpp\"\n#include \"player/ProcessorPlayerAttribute.hpp\"\n#include \"player/ProcessorPlayerBaseInfo.hpp\"\n#include \"player/ProcessorPlayerBehavior.hpp\"\n#include \"player/ProcessorPlayerBook.hpp\"\n#include \"player/ProcessorPlayerBounty.hpp\"\n#include \"player/ProcessorPlayerCast.hpp\"\n#include \"player/ProcessorPlayerCellChange.hpp\"\n#include \"player/ProcessorPlayerCellState.hpp\"\n#include \"player/ProcessorPlayerCharClass.hpp\"\n#include \"player/ProcessorPlayerCharGen.hpp\"\n#include \"player/ProcessorPlayerCooldowns.hpp\"\n#include \"player/ProcessorPlayerDeath.hpp\"\n#include \"player/ProcessorPlayerDisposition.hpp\"\n#include \"player/ProcessorPlayerEquipment.hpp\"\n#include \"player/ProcessorPlayerFaction.hpp\"\n#include \"player/ProcessorPlayerInput.hpp\"\n#include \"player/ProcessorPlayerInventory.hpp\"\n#include \"player/ProcessorPlayerItemUse.hpp\"\n#include \"player/ProcessorPlayerJail.hpp\"\n#include \"player/ProcessorPlayerJournal.hpp\"\n#include \"player/ProcessorPlayerLevel.hpp\"\n#include \"player/ProcessorPlayerMiscellaneous.hpp\"\n#include \"player/ProcessorPlayerMomentum.hpp\"\n#include \"player/ProcessorPlayerPosition.hpp\"\n#include \"player/ProcessorPlayerQuickKeys.hpp\"\n#include \"player/ProcessorPlayerReputation.hpp\"\n#include \"player/ProcessorPlayerRest.hpp\"\n#include \"player/ProcessorPlayerResurrect.hpp\"\n#include \"player/ProcessorPlayerShapeshift.hpp\"\n#include \"player/ProcessorPlayerSkill.hpp\"\n#include \"player/ProcessorPlayerSpeech.hpp\"\n#include \"player/ProcessorPlayerSpellbook.hpp\"\n#include \"player/ProcessorPlayerSpellsActive.hpp\"\n#include \"player/ProcessorPlayerStatsDynamic.hpp\"\n#include \"player/ProcessorPlayerTopic.hpp\"\n\n#include \"ObjectProcessor.hpp\"\n#include \"object/ProcessorConsoleCommand.hpp\"\n#include \"object/ProcessorContainer.hpp\"\n#include \"object/ProcessorDoorDestination.hpp\"\n#include \"object/ProcessorDoorState.hpp\"\n#include \"object/ProcessorMusicPlay.hpp\"\n#include \"object/ProcessorObjectActivate.hpp\"\n#include \"object/ProcessorObjectAnimPlay.hpp\"\n#include \"object/ProcessorObjectAttach.hpp\"\n#include \"object/ProcessorObjectDelete.hpp\"\n#include \"object/ProcessorObjectDialogueChoice.hpp\"\n#include \"object/ProcessorObjectHit.hpp\"\n#include \"object/ProcessorObjectLock.hpp\"\n#include \"object/ProcessorObjectMiscellaneous.hpp\"\n#include \"object/ProcessorObjectMove.hpp\"\n#include \"object/ProcessorObjectPlace.hpp\"\n#include \"object/ProcessorObjectRestock.hpp\"\n#include \"object/ProcessorObjectRotate.hpp\"\n#include \"object/ProcessorObjectScale.hpp\"\n#include \"object/ProcessorObjectSound.hpp\"\n#include \"object/ProcessorObjectSpawn.hpp\"\n#include \"object/ProcessorObjectState.hpp\"\n#include \"object/ProcessorObjectTrap.hpp\"\n#include \"object/ProcessorClientScriptLocal.hpp\"\n#include \"object/ProcessorScriptMemberShort.hpp\"\n#include \"object/ProcessorVideoPlay.hpp\"\n\n#include \"ActorProcessor.hpp\"\n#include \"actor/ProcessorActorAI.hpp\"\n#include \"actor/ProcessorActorAnimFlags.hpp\"\n#include \"actor/ProcessorActorAnimPlay.hpp\"\n#include \"actor/ProcessorActorAttack.hpp\"\n#include \"actor/ProcessorActorAuthority.hpp\"\n#include \"actor/ProcessorActorCast.hpp\"\n#include \"actor/ProcessorActorCellChange.hpp\"\n#include \"actor/ProcessorActorDeath.hpp\"\n#include \"actor/ProcessorActorEquipment.hpp\"\n#include \"actor/ProcessorActorList.hpp\"\n#include \"actor/ProcessorActorPosition.hpp\"\n#include \"actor/ProcessorActorSpeech.hpp\"\n#include \"actor/ProcessorActorSpellsActive.hpp\"\n#include \"actor/ProcessorActorStatsDynamic.hpp\"\n#include \"actor/ProcessorActorTest.hpp\"\n\n#include \"WorldstateProcessor.hpp\"\n#include \"worldstate/ProcessorCellReset.hpp\"\n#include \"worldstate/ProcessorClientScriptGlobal.hpp\"\n#include \"worldstate/ProcessorClientScriptSettings.hpp\"\n#include \"worldstate/ProcessorRecordDynamic.hpp\"\n#include \"worldstate/ProcessorWorldCollisionOverride.hpp\"\n#include \"worldstate/ProcessorWorldDestinationOverride.hpp\"\n#include \"worldstate/ProcessorWorldKillCount.hpp\"\n#include \"worldstate/ProcessorWorldMap.hpp\"\n#include \"worldstate/ProcessorWorldRegionAuthority.hpp\"\n#include \"worldstate/ProcessorWorldTime.hpp\"\n#include \"worldstate/ProcessorWorldWeather.hpp\"\n\nusing namespace mwmp;\n\nvoid ProcessorInitializer()\n{\n    SystemProcessor::AddProcessor(new ProcessorSystemHandshake());\n\n    PlayerProcessor::AddProcessor(new ProcessorChatMessage());\n    PlayerProcessor::AddProcessor(new ProcessorGUIMessageBox());\n    PlayerProcessor::AddProcessor(new ProcessorUserDisconnected());\n    PlayerProcessor::AddProcessor(new ProcessorGameSettings());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAlly());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAnimFlags());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAnimPlay());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAttack());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAttribute());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerBaseInfo());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerBehavior());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerBook());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerBounty());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCast());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCellChange());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCellState());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCharClass());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCharGen());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCooldowns());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerDeath());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerDisposition());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerEquipment());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerFaction());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerInput());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerInventory());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerItemUse());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerJail());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerJournal());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerLevel());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerMiscellaneous());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerMomentum());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerPosition());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerQuickKeys());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerReputation());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerRest());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerResurrect());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerShapeshift());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerSkill());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerSpeech());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerSpellbook());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerSpellsActive());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerStatsDynamic());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerTopic());\n\n    ObjectProcessor::AddProcessor(new ProcessorConsoleCommand());\n    ObjectProcessor::AddProcessor(new ProcessorContainer());\n    ObjectProcessor::AddProcessor(new ProcessorDoorDestination());\n    ObjectProcessor::AddProcessor(new ProcessorDoorState());\n    ObjectProcessor::AddProcessor(new ProcessorMusicPlay());\n    ObjectProcessor::AddProcessor(new ProcessorObjectActivate());\n    ObjectProcessor::AddProcessor(new ProcessorObjectAnimPlay());\n    ObjectProcessor::AddProcessor(new ProcessorObjectAttach());\n    ObjectProcessor::AddProcessor(new ProcessorObjectDelete());\n    ObjectProcessor::AddProcessor(new ProcessorObjectDialogueChoice());\n    ObjectProcessor::AddProcessor(new ProcessorObjectHit());\n    ObjectProcessor::AddProcessor(new ProcessorObjectLock());\n    ObjectProcessor::AddProcessor(new ProcessorObjectMiscellaneous());\n    ObjectProcessor::AddProcessor(new ProcessorObjectMove());\n    ObjectProcessor::AddProcessor(new ProcessorObjectPlace());\n    ObjectProcessor::AddProcessor(new ProcessorObjectRestock());\n    ObjectProcessor::AddProcessor(new ProcessorObjectRotate());\n    ObjectProcessor::AddProcessor(new ProcessorObjectScale());\n    ObjectProcessor::AddProcessor(new ProcessorObjectSound());\n    ObjectProcessor::AddProcessor(new ProcessorObjectSpawn());\n    ObjectProcessor::AddProcessor(new ProcessorObjectState());\n    ObjectProcessor::AddProcessor(new ProcessorObjectTrap());\n    ObjectProcessor::AddProcessor(new ProcessorClientScriptLocal());\n    ObjectProcessor::AddProcessor(new ProcessorScriptMemberShort());\n    ObjectProcessor::AddProcessor(new ProcessorVideoPlay());\n\n    ActorProcessor::AddProcessor(new ProcessorActorAI());\n    ActorProcessor::AddProcessor(new ProcessorActorAnimFlags());\n    ActorProcessor::AddProcessor(new ProcessorActorAnimPlay());\n    ActorProcessor::AddProcessor(new ProcessorActorAttack());\n    ActorProcessor::AddProcessor(new ProcessorActorAuthority());\n    ActorProcessor::AddProcessor(new ProcessorActorCast());\n    ActorProcessor::AddProcessor(new ProcessorActorCellChange());\n    ActorProcessor::AddProcessor(new ProcessorActorDeath());\n    ActorProcessor::AddProcessor(new ProcessorActorEquipment());\n    ActorProcessor::AddProcessor(new ProcessorActorList());\n    ActorProcessor::AddProcessor(new ProcessorActorPosition());\n    ActorProcessor::AddProcessor(new ProcessorActorSpeech());\n    ActorProcessor::AddProcessor(new ProcessorActorSpellsActive());\n    ActorProcessor::AddProcessor(new ProcessorActorStatsDynamic());\n    ActorProcessor::AddProcessor(new ProcessorActorTest());\n\n    WorldstateProcessor::AddProcessor(new ProcessorCellReset());\n    WorldstateProcessor::AddProcessor(new ProcessorClientScriptGlobal());\n    WorldstateProcessor::AddProcessor(new ProcessorClientScriptSettings());\n    WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldCollisionOverride());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldDestinationOverride());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldKillCount());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldMap());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldRegionAuthority());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldTime());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldWeather());\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/ProcessorInitializer.hpp",
    "content": "#ifndef OPENMW_INIT_PROCESSORS_HPP\n#define OPENMW_INIT_PROCESSORS_HPP\n\nvoid ProcessorInitializer();\n\n\n#endif //OPENMW_INIT_PROCESSORS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/SystemProcessor.cpp",
    "content": "#include \"../Networking.hpp\"\n#include \"SystemProcessor.hpp\"\n#include \"../Main.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nSystemProcessor::~SystemProcessor()\n{\n\n}\n\nbool SystemProcessor::Process(RakNet::Packet &packet)\n{\n    RakNet::BitStream bsIn(&packet.data[1], packet.length, false);\n    bsIn.Read(guid);\n\n    SystemPacket *myPacket = Main::get().getNetworking()->getSystemPacket(packet.data[0]);\n    myPacket->SetReadStream(&bsIn);\n\n    /*if (myPacket == 0)\n    {\n        // error: packet not found\n    }*/\n\n    for (auto &processor : processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            myGuid = Main::get().getLocalSystem()->guid;\n            request = packet.length == myPacket->headerSize();\n\n            BaseSystem *system = 0;\n            system = Main::get().getLocalSystem();\n\n            if (!request && !processor.second->avoidReading && system != 0)\n            {\n                myPacket->setSystem(system);\n                myPacket->Read();\n            }\n\n            processor.second->Do(*myPacket, system);\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/SystemProcessor.hpp",
    "content": "#ifndef OPENMW_SYSTEMPROCESSOR_HPP\n#define OPENMW_SYSTEMPROCESSOR_HPP\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Packets/System/SystemPacket.hpp>\n#include \"../LocalSystem.hpp\"\n#include \"BaseClientPacketProcessor.hpp\"\n\nnamespace mwmp\n{\n    class SystemProcessor : public BasePacketProcessor<SystemProcessor>, public BaseClientPacketProcessor\n    {\n    public:\n        virtual void Do(SystemPacket &packet, BaseSystem *system) = 0;\n\n        static bool Process(RakNet::Packet &packet);\n\n        virtual ~SystemProcessor();\n    };\n}\n\n\n\n#endif //OPENMW_SYSTEMPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/WorldstateProcessor.cpp",
    "content": "#include \"../Main.hpp\"\n#include \"../Networking.hpp\"\n\n#include \"WorldstateProcessor.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nWorldstateProcessor::~WorldstateProcessor()\n{\n\n}\n\nbool WorldstateProcessor::Process(RakNet::Packet &packet, Worldstate &worldstate)\n{\n    RakNet::BitStream bsIn(&packet.data[1], packet.length, false);\n    bsIn.Read(guid);\n    worldstate.guid = guid;\n\n    WorldstatePacket *myPacket = Main::get().getNetworking()->getWorldstatePacket(packet.data[0]);\n\n    myPacket->setWorldstate(&worldstate);\n    myPacket->SetReadStream(&bsIn);\n\n    for (auto &processor : processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            myGuid = Main::get().getLocalPlayer()->guid;\n            request = packet.length == myPacket->headerSize();\n\n            worldstate.isValid = true;\n\n            if (!request && !processor.second->avoidReading)\n                myPacket->Read();\n\n            if (worldstate.isValid)\n                processor.second->Do(*myPacket, worldstate);\n            else\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Received %s that failed integrity check and was ignored!\", processor.second->strPacketID.c_str());\n\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/WorldstateProcessor.hpp",
    "content": "#ifndef OPENMW_WORLDSTATEPROCESSOR_HPP\n#define OPENMW_WORLDSTATEPROCESSOR_HPP\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n#include \"BaseClientPacketProcessor.hpp\"\n\nnamespace mwmp\n{\n    class WorldstateProcessor : public BasePacketProcessor<WorldstateProcessor>, public BaseClientPacketProcessor\n    {\n    public:\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate) = 0;\n\n        static bool Process(RakNet::Packet &packet, Worldstate &worldstate);\n\n        virtual ~WorldstateProcessor();\n    };\n}\n\n\n#endif //OPENMW_WORLDSTATEPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorAI.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORAI_HPP\n#define OPENMW_PROCESSORACTORAI_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAI final: public ActorProcessor\n    {\n    public:\n        ProcessorActorAI()\n        {\n            BPP_INIT(ID_ACTOR_AI);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s about %s\", strPacketID.c_str(), actorList.cell.getShortDescription().c_str());\n\n            Main::get().getCellController()->readAi(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORAI_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorAnimFlags.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORANIMFLAGS_HPP\n#define OPENMW_PROCESSORACTORANIMFLAGS_HPP\n\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAnimFlags final: public ActorProcessor\n    {\n    public:\n        ProcessorActorAnimFlags()\n        {\n            BPP_INIT(ID_ACTOR_ANIM_FLAGS);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readAnimFlags(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORANIMFLAGS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorAnimPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORANIMPLAY_HPP\n#define OPENMW_PROCESSORACTORANIMPLAY_HPP\n\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAnimPlay final: public ActorProcessor\n    {\n    public:\n        ProcessorActorAnimPlay()\n        {\n            BPP_INIT(ID_ACTOR_ANIM_PLAY);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readAnimPlay(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORANIMPLAY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorAttack.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORATTACK_HPP\n#define OPENMW_PROCESSORACTORATTACK_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAttack final: public ActorProcessor\n    {\n    public:\n        ProcessorActorAttack()\n        {\n            BPP_INIT(ID_ACTOR_ATTACK);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readAttack(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORATTACK_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorAuthority.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORAUTHORITY_HPP\n#define OPENMW_PROCESSORACTORAUTHORITY_HPP\n\n\n#include \"../ActorProcessor.hpp\"\n#include <components/detournavigator/navigator.hpp>\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAuthority final: public ActorProcessor\n    {\n    public:\n        ProcessorActorAuthority()\n        {\n            BPP_INIT(ID_ACTOR_AUTHORITY)\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s about %s\", strPacketID.c_str(), actorList.cell.getShortDescription().c_str());\n            mwmp::CellController *cellController = Main::get().getCellController();\n\n            // Never initialize LocalActors in a cell that is no longer loaded, if the server's packet arrived too late\n            if (cellController->isActiveWorldCell(actorList.cell))\n            {\n                cellController->initializeCell(actorList.cell);\n                mwmp::Cell *cell = cellController->getCell(actorList.cell);\n                cell->setAuthority(guid);\n\n                if (isLocal())\n                {\n                    LOG_APPEND(TimedLog::LOG_INFO, \"- The new authority is me\");\n                    cell->uninitializeDedicatedActors();\n                    cell->initializeLocalActors();\n                    cell->updateLocal(true);\n\n                    // Enable updates for DetourNavigator for advanced pathfinding\n                    MWBase::World* world = MWBase::Environment::get().getWorld();\n                    world->getNavigator()->setUpdatesEnabled(true);\n                    world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3());\n                }\n                else\n                {\n                    BasePlayer *player = PlayerList::getPlayer(guid);\n\n                    if (player != 0)\n                        LOG_APPEND(TimedLog::LOG_INFO, \"- The new authority is %s\", player->npc.mName.c_str());\n\n                    cell->uninitializeLocalActors();\n                }\n            }\n            else\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Ignoring it because that cell isn't loaded\");\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORAUTHORITY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorCast.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORCAST_HPP\n#define OPENMW_PROCESSORACTORCAST_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorCast final: public ActorProcessor\n    {\n    public:\n        ProcessorActorCast()\n        {\n            BPP_INIT(ID_ACTOR_CAST);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readCast(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORCAST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorCellChange.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORCELLCHANGE_HPP\n#define OPENMW_PROCESSORACTORCELLCHANGE_HPP\n\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorCellChange final: public ActorProcessor\n    {\n    public:\n        ProcessorActorCellChange()\n        {\n            BPP_INIT(ID_ACTOR_CELL_CHANGE);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readCellChange(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORCELLCHANGE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorDeath.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORDEATH_HPP\n#define OPENMW_PROCESSORACTORDEATH_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorDeath final: public ActorProcessor\n    {\n    public:\n        ProcessorActorDeath()\n        {\n            BPP_INIT(ID_ACTOR_DEATH);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readDeath(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORDEATH_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorEquipment.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTOREQUIPMENT_HPP\n#define OPENMW_PROCESSORACTOREQUIPMENT_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorEquipment final: public ActorProcessor\n    {\n    public:\n        ProcessorActorEquipment()\n        {\n            BPP_INIT(ID_ACTOR_EQUIPMENT);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readEquipment(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTOREQUIPMENT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorList.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORLIST_HPP\n#define OPENMW_PROCESSORACTORLIST_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n#include \"apps/openmw/mwmp/MechanicsHelper.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorList final: public ActorProcessor\n    {\n    public:\n        ProcessorActorList()\n        {\n            BPP_INIT(ID_ACTOR_LIST)\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            MWWorld::CellStore *ptrCellStore = Main::get().getCellController()->getCellStore(actorList.cell);\n\n            if (!ptrCellStore) return;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Received %s about %s\", strPacketID.c_str(), actorList.cell.getShortDescription().c_str());\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"- action: %i\", actorList.action);\n\n            // If we've received a request for information, comply with it\n            if (actorList.action == mwmp::BaseActorList::REQUEST)\n            {\n                MechanicsHelper::spawnLeveledCreatures(ptrCellStore);\n                actorList.sendActorsInCell(ptrCellStore);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORLIST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorPosition.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORPOSITION_HPP\n#define OPENMW_PROCESSORACTORPOSITION_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorPosition final: public ActorProcessor\n    {\n    public:\n        ProcessorActorPosition()\n        {\n            BPP_INIT(ID_ACTOR_POSITION);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readPositions(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORPOSITION_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorSpeech.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORSPEECH_HPP\n#define OPENMW_PROCESSORACTORSPEECH_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorSpeech final: public ActorProcessor\n    {\n    public:\n        ProcessorActorSpeech()\n        {\n            BPP_INIT(ID_ACTOR_SPEECH);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readSpeech(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORSPEECH_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorSpellsActive.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORSPELLSACTIVE_HPP\n#define OPENMW_PROCESSORACTORSPELLSACTIVE_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorSpellsActive final: public ActorProcessor\n    {\n    public:\n        ProcessorActorSpellsActive()\n        {\n            BPP_INIT(ID_ACTOR_SPELLS_ACTIVE);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readSpellsActive(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORSPELLSACTIVE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorStatsDynamic.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORSTATSDYNAMIC_HPP\n#define OPENMW_PROCESSORACTORSTATSDYNAMIC_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorStatsDynamic final: public ActorProcessor\n    {\n    public:\n        ProcessorActorStatsDynamic()\n        {\n            BPP_INIT(ID_ACTOR_STATS_DYNAMIC);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n            Main::get().getCellController()->readStatsDynamic(actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORSTATSDYNAMIC_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/actor/ProcessorActorTest.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORTEST_HPP\n#define OPENMW_PROCESSORACTORTEST_HPP\n\n#include \"../ActorProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorTest final: public ActorProcessor\n    {\n    public:\n        ProcessorActorTest()\n        {\n            BPP_INIT(ID_ACTOR_TEST);\n        }\n\n        virtual void Do(ActorPacket &packet, ActorList &actorList)\n        {\n\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORTEST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/BaseObjectProcessor.hpp",
    "content": "#ifndef OPENMW_BASEOBJECTPROCESSOR_HPP\n#define OPENMW_BASEOBJECTPROCESSOR_HPP\n\n#include \"../ObjectProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/CellController.hpp\"\n#include \"apps/openmw/mwworld/cellstore.hpp\"\n\nnamespace mwmp\n{\n    class BaseObjectProcessor : public ObjectProcessor\n    {\n    public:\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Received %s about %s\", strPacketID.c_str(), objectList.cell.getShortDescription().c_str());\n        }\n    protected:\n        MWWorld::CellStore *ptrCellStore;\n\n    };\n}\n\n#endif //OPENMW_BASEOBJECTPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorClientScriptLocal.hpp",
    "content": "#ifndef OPENMW_PROCESSORCLIENTSCRIPTLOCAL_HPP\n#define OPENMW_PROCESSORCLIENTSCRIPTLOCAL_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorClientScriptLocal final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorClientScriptLocal()\n        {\n            BPP_INIT(ID_CLIENT_SCRIPT_LOCAL)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.setClientLocals(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORCLIENTSCRIPTLOCAL_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorConsoleCommand.hpp",
    "content": "#ifndef OPENMW_PROCESSORCONSOLECOMMAND_HPP\n#define OPENMW_PROCESSORCONSOLECOMMAND_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorConsoleCommand final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorConsoleCommand()\n        {\n            BPP_INIT(ID_CONSOLE_COMMAND)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            objectList.runConsoleCommands(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORCONSOLECOMMAND_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorContainer.hpp",
    "content": "#ifndef OPENMW_PROCESSORCONTAINER_HPP\n#define OPENMW_PROCESSORCONTAINER_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorContainer final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorContainer()\n        {\n            BPP_INIT(ID_CONTAINER)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            std::string debugMessage = \"- action \";\n            unsigned char action = objectList.action;\n            unsigned char containerSubAction = objectList.containerSubAction;\n\n            if (action == mwmp::BaseObjectList::SET)\n                debugMessage += \"SET\";\n            else if (action == mwmp::BaseObjectList::ADD)\n                debugMessage += \"ADD\";\n            else if (action == mwmp::BaseObjectList::REMOVE)\n                debugMessage += \"REMOVE\";\n            else if (action == mwmp::BaseObjectList::REQUEST)\n                debugMessage += \"REQUEST\";\n\n            debugMessage += \" and subaction \";\n\n            if (containerSubAction == mwmp::BaseObjectList::NONE)\n                debugMessage += \"NONE\";\n            else if (containerSubAction == mwmp::BaseObjectList::DRAG)\n                debugMessage += \"DRAG\";\n            else if (containerSubAction == mwmp::BaseObjectList::DROP)\n                debugMessage += \"DROP\";\n            else if (containerSubAction == mwmp::BaseObjectList::TAKE_ALL)\n                debugMessage += \"TAKE_ALL\";\n            else if (containerSubAction == mwmp::BaseObjectList::REPLY_TO_REQUEST)\n                debugMessage += \"REPLY_TO_REQUEST\";\n\n            LOG_APPEND(TimedLog::LOG_VERBOSE, \"%s\", debugMessage.c_str());\n\n            // If we've received a request for information, comply with it\n            if (objectList.action == mwmp::BaseObjectList::REQUEST)\n            {\n                if (objectList.baseObjectCount == 0)\n                {\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Request had no objects attached, so we are sending all containers in the cell %s\",\n                        objectList.cell.getShortDescription().c_str());\n                    objectList.reset();\n                    objectList.cell = *ptrCellStore->getCell();\n                    objectList.action = mwmp::BaseObjectList::SET;\n                    objectList.containerSubAction = mwmp::BaseObjectList::REPLY_TO_REQUEST;\n                    objectList.addAllContainers(ptrCellStore);\n                    objectList.sendContainer();\n                }\n                else\n                {\n                    LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Request was for %i %s\", objectList.baseObjectCount, objectList.baseObjectCount == 1 ? \"object\" : \"objects\");\n                    std::vector<BaseObject> requestObjects = objectList.baseObjects;\n                    objectList.reset();\n                    objectList.cell = *ptrCellStore->getCell();\n                    objectList.action = mwmp::BaseObjectList::SET;\n                    objectList.containerSubAction = mwmp::BaseObjectList::REPLY_TO_REQUEST;\n                    objectList.addRequestedContainers(ptrCellStore, requestObjects);\n\n                    if (objectList.baseObjects.size() > 0)\n                        objectList.sendContainer();\n                }\n            }\n            // Otherwise, edit containers based on the information received\n            else\n            {\n                LOG_APPEND(TimedLog::LOG_VERBOSE, \"- Editing container contents to match those of packet\", objectList.baseObjectCount);\n                objectList.editContainers(ptrCellStore);\n            }\n        }\n\n    };\n}\n\n#endif //OPENMW_PROCESSORCONTAINER_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorDoorDestination.hpp",
    "content": "#ifndef OPENMW_PROCESSDOORDESTINATION_HPP\n#define OPENMW_PROCESSDOORDESTINATION_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorDoorDestination final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorDoorDestination()\n        {\n            BPP_INIT(ID_DOOR_DESTINATION)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.setDoorDestinations(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSDOORDESTINATION_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorDoorState.hpp",
    "content": "#ifndef OPENMW_PROCESSDOORSTATE_HPP\n#define OPENMW_PROCESSDOORSTATE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorDoorState final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorDoorState()\n        {\n            BPP_INIT(ID_DOOR_STATE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.activateDoors(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSDOORSTATE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorMusicPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSORMUSICPLAY_HPP\n#define OPENMW_PROCESSORMUSICPLAY_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorMusicPlay final: public ObjectProcessor\n    {\n    public:\n        ProcessorMusicPlay()\n        {\n            BPP_INIT(ID_MUSIC_PLAY)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Received %s\", strPacketID.c_str());\n            objectList.playMusic();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORSCRIPTGLOBALSHORT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectActivate.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTACTIVATE_HPP\n#define OPENMW_PROCESSOROBJECTACTIVATE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectActivate final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectActivate()\n        {\n            BPP_INIT(ID_OBJECT_ACTIVATE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.activateObjects(ptrCellStore);\n        }\n\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTACTIVATE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectAnimPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTANIMPLAY_HPP\n#define OPENMW_PROCESSOROBJECTANIMPLAY_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectAnimPlay final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectAnimPlay()\n        {\n            BPP_INIT(ID_OBJECT_ANIM_PLAY)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.animateObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTANIMPLAY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectAttach.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTATTACH_HPP\n#define OPENMW_PROCESSOROBJECTATTACH_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectAttach final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectAttach()\n        {\n            BPP_INIT(ID_OBJECT_ATTACH)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            //objectList.attachObjects(ptrCellStore);\n        }\n\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTATTACH_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectDelete.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTDELETE_HPP\n#define OPENMW_PROCESSOROBJECTDELETE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectDelete final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectDelete()\n        {\n            BPP_INIT(ID_OBJECT_DELETE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.deleteObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTDELETE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectDialogueChoice.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTDIALOGUECHOICE_HPP\n#define OPENMW_PROCESSOROBJECTDIALOGUECHOICE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectDialogueChoice final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectDialogueChoice()\n        {\n            BPP_INIT(ID_OBJECT_DIALOGUE_CHOICE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.makeDialogueChoices(ptrCellStore);\n        }\n\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTDIALOGUECHOICE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectHit.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTHIT_HPP\n#define OPENMW_PROCESSOROBJECTHIT_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectHit final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectHit()\n        {\n            BPP_INIT(ID_OBJECT_HIT)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            //objectList.hitObjects(ptrCellStore);\n        }\n\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTHIT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectLock.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTLOCK_HPP\n#define OPENMW_PROCESSOROBJECTLOCK_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectLock final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectLock()\n        {\n            BPP_INIT(ID_OBJECT_LOCK)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.lockObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTLOCK_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectMiscellaneous.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTMISCELLANEOUS_HPP\n#define OPENMW_PROCESSOROBJECTMISCELLANEOUS_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectMiscellaneous final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectMiscellaneous()\n        {\n            BPP_INIT(ID_OBJECT_MISCELLANEOUS)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.setGoldPoolsForObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTMISCELLANEOUS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectMove.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTMOVE_HPP\n#define OPENMW_PROCESSOROBJECTMOVE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectMove final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectMove()\n        {\n            BPP_INIT(ID_OBJECT_MOVE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.moveObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTMOVE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectPlace.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTPLACE_HPP\n#define OPENMW_PROCESSOROBJECTPLACE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectPlace final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectPlace()\n        {\n            BPP_INIT(ID_OBJECT_PLACE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.placeObjects(ptrCellStore);\n        }\n\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTPLACE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectRestock.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTRESTOCK_HPP\n#define OPENMW_PROCESSOROBJECTRESTOCK_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectRestock final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectRestock()\n        {\n            BPP_INIT(ID_OBJECT_RESTOCK)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.restockObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTRESTOCK_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectRotate.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTROTATE_HPP\n#define OPENMW_PROCESSOROBJECTROTATE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectRotate final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectRotate()\n        {\n            BPP_INIT(ID_OBJECT_ROTATE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.rotateObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTROTATE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectScale.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTSCALE_HPP\n#define OPENMW_PROCESSOROBJECTSCALE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectScale final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectScale()\n        {\n            BPP_INIT(ID_OBJECT_SCALE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.scaleObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTSCALE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectSound.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTSOUND_HPP\n#define OPENMW_PROCESSOROBJECTSOUND_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectSound final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectSound()\n        {\n            BPP_INIT(ID_OBJECT_SOUND)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            MWBase::World* world = MWBase::Environment::get().getWorld();\n\n            // Only play sounds in active cells\n            if (world->isCellActive(*ptrCellStore->getCell()))\n            {\n                objectList.playObjectSounds(ptrCellStore);\n            }\n        }\n\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTSOUND_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectSpawn.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTSPAWN_HPP\n#define OPENMW_PROCESSOROBJECTSPAWN_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectSpawn final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectSpawn()\n        {\n            BPP_INIT(ID_OBJECT_SPAWN)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.spawnObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTSPAWN_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectState.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTSTATE_HPP\n#define OPENMW_PROCESSOROBJECTSTATE_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectState final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectState()\n        {\n            BPP_INIT(ID_OBJECT_STATE)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.setObjectStates(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTSTATE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorObjectTrap.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTTRAP_HPP\n#define OPENMW_PROCESSOROBJECTTRAP_HPP\n\n#include \"BaseObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectTrap final: public BaseObjectProcessor\n    {\n    public:\n        ProcessorObjectTrap()\n        {\n            BPP_INIT(ID_OBJECT_TRAP)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            BaseObjectProcessor::Do(packet, objectList);\n\n            ptrCellStore = Main::get().getCellController()->getCellStore(objectList.cell);\n\n            if (!ptrCellStore) return;\n\n            objectList.triggerTrapObjects(ptrCellStore);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTTRAP_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorScriptMemberShort.hpp",
    "content": "#ifndef OPENMW_PROCESSORSCRIPTMEMBERSHORT_HPP\n#define OPENMW_PROCESSORSCRIPTMEMBERSHORT_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorScriptMemberShort final: public ObjectProcessor\n    {\n    public:\n        ProcessorScriptMemberShort()\n        {\n            BPP_INIT(ID_SCRIPT_MEMBER_SHORT)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Received %s\", strPacketID.c_str());\n            objectList.setMemberShorts();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORSCRIPTMEMBERSHORT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/object/ProcessorVideoPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSORVIDEOPLAY_HPP\n#define OPENMW_PROCESSORVIDEOPLAY_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorVideoPlay final: public ObjectProcessor\n    {\n    public:\n        ProcessorVideoPlay()\n        {\n            BPP_INIT(ID_VIDEO_PLAY)\n        }\n\n        virtual void Do(ObjectPacket &packet, ObjectList &objectList)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Received %s\", strPacketID.c_str());\n            objectList.playVideo();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORVIDEOPLAY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorChatMessage.hpp",
    "content": "#ifndef OPENMW_PROCESSORCHATMESSAGE_HPP\n#define OPENMW_PROCESSORCHATMESSAGE_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/GUIController.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorChatMessage final: public PlayerProcessor\n    {\n    public:\n        ProcessorChatMessage()\n        {\n            BPP_INIT(ID_CHAT_MESSAGE)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (player != 0)\n                Main::get().getGUIController()->printChatMessage(player->chatMessage);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORCHATMESSAGE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorGUIMessageBox.hpp",
    "content": "#ifndef OPENMW_PROCESSORGUIMESSAGEBOX_HPP\n#define OPENMW_PROCESSORGUIMESSAGEBOX_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorGUIMessageBox final: public PlayerProcessor\n    {\n    public:\n        ProcessorGUIMessageBox()\n        {\n            BPP_INIT(ID_GUI_MESSAGEBOX)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"ID_GUI_MESSAGEBOX, Type %d, MSG %s\", player->guiMessageBox.type,\n                                   player->guiMessageBox.label.c_str());\n\n                switch(player->guiMessageBox.type)\n                {\n                    case BasePlayer::GUIMessageBox::MessageBox:\n                        Main::get().getGUIController()->showMessageBox(player->guiMessageBox);\n                        break;\n                    case BasePlayer::GUIMessageBox::CustomMessageBox:\n                        Main::get().getGUIController()->showCustomMessageBox(player->guiMessageBox);\n                        break;\n                    case BasePlayer::GUIMessageBox::InputDialog:\n                    case BasePlayer::GUIMessageBox::PasswordDialog:\n                        Main::get().getGUIController()->showInputBox(player->guiMessageBox);\n                        break;\n                    case BasePlayer::GUIMessageBox::ListBox:\n                        Main::get().getGUIController()->showDialogList(player->guiMessageBox);\n                        break;\n                }\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORGUIMESSAGEBOX_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp",
    "content": "#ifndef OPENMW_PROCESSORGAMESETTINGS_HPP\n#define OPENMW_PROCESSORGAMESETTINGS_HPP\n\n#include \"apps/openmw/mwbase/environment.hpp\"\n#include \"apps/openmw/mwworld/worldimp.hpp\"\n#include \"apps/openmw/mwgui/windowmanagerimp.hpp\"\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorGameSettings final: public PlayerProcessor\n    {\n        const std::string GAME_SETTING_CATEGORY = \"Game\";\n        const std::string VR_SETTING_CATEGORY = \"VR\";\n    public:\n        ProcessorGameSettings()\n        {\n            BPP_INIT(ID_GAME_SETTINGS)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            static const int initialLogLevel = TimedLog::GetLevel();\n\n            if (isLocal())\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_GAME_SETTINGS\");\n                LOG_APPEND(TimedLog::LOG_INFO, \"- player %s rest in beds, %s rest in the wilderness, %s wait\",\n                    player->bedRestAllowed ? \"can\" : \"cannot\", player->wildernessRestAllowed ? \"can\" : \"cannot\",\n                    player->waitAllowed ? \"can\" : \"cannot\");\n\n                if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n                {\n                    if (MWBase::Environment::get().getWindowManager()->isConsoleMode() && !player->consoleAllowed)\n                        MWBase::Environment::get().getWindowManager()->popGuiMode();\n                    else if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Rest &&\n                        (!player->bedRestAllowed || !player->wildernessRestAllowed || !player->waitAllowed))\n                        MWBase::Environment::get().getWindowManager()->popGuiMode();\n                }\n\n                if (player->enforcedLogLevel > -1)\n                {\n                    LOG_APPEND(TimedLog::LOG_INFO, \"- server is enforcing log level %i\", player->enforcedLogLevel);\n                    TimedLog::SetLevel(player->enforcedLogLevel);\n                }\n                else if (initialLogLevel != TimedLog::GetLevel())\n                {\n                    LOG_APPEND(TimedLog::LOG_INFO, \"- log level has been reset to initial value %i\", initialLogLevel);\n                    TimedLog::SetLevel(initialLogLevel);\n                }\n\n                MWBase::Environment::get().getWorld()->setPhysicsFramerate(player->physicsFramerate);\n\n                for (auto setting : player->gameSettings)\n                {\n                    Settings::Manager::setString(setting.first, GAME_SETTING_CATEGORY, setting.second);\n                }\n\n                // Only read VR settings for players using a VR build\n#ifdef USE_OPENXR\n                for (auto setting : player->vrSettings)\n                {\n                    Settings::Manager::setString(setting.first, VR_SETTING_CATEGORY, setting.second);\n                }\n#endif\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORGAMESETTINGS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerAlly.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERALLY_HPP\n#define OPENMW_PROCESSORPLAYERALLY_HPP\n\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/LocalPlayer.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAlly final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerAlly()\n        {\n            BPP_INIT(ID_PLAYER_ALLY)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n\n            if (isLocal())\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_ALLY about LocalPlayer %s from server\", localPlayer->npc.mName.c_str());\n\n                for (std::vector<RakNet::RakNetGUID>::iterator iter = localPlayer->alliedPlayers.begin(); iter != localPlayer->alliedPlayers.end(); )\n                {\n                    DedicatedPlayer *dedicatedPlayer = PlayerList::getPlayer(*iter);\n\n                    if (dedicatedPlayer)\n                    {\n                        LOG_APPEND(TimedLog::LOG_INFO, \"- Adding DedicatedPlayer %s to our allied players\", dedicatedPlayer->npc.mName.c_str());\n                    }\n\n                    ++iter;\n                }\n            }\n            else if (player != 0)\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_ALLY about DedicatedPlayer %s from server\", player->npc.mName.c_str());\n\n                for (std::vector<RakNet::RakNetGUID>::iterator iter = player->alliedPlayers.begin(); iter != player->alliedPlayers.end(); )\n                {\n                    if (*iter == localPlayer->guid)\n                    {\n                        LOG_APPEND(TimedLog::LOG_INFO, \"- Adding LocalPlayer %s to their allied players\", localPlayer->npc.mName.c_str());\n                    }\n                    else\n                    {\n                        DedicatedPlayer *otherDedicatedPlayer = PlayerList::getPlayer(*iter);\n\n                        if (otherDedicatedPlayer)\n                        {\n                            LOG_APPEND(TimedLog::LOG_INFO, \"- Adding DedicatedPlayer %s to their allied players\", otherDedicatedPlayer->npc.mName.c_str());\n                        }\n                    }\n\n                    ++iter;\n                }\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERALLY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerAnimFlags.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERANIMFLAGS_HPP\n#define OPENMW_PROCESSORPLAYERANIMFLAGS_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAnimFlags final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerAnimFlags()\n        {\n            BPP_INIT(ID_PLAYER_ANIM_FLAGS)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                if (isRequest())\n                    static_cast<LocalPlayer *>(player)->updateAnimFlags(true);\n            }\n            else if (player != 0)\n                static_cast<DedicatedPlayer *>(player)->setAnimFlags();\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERANIMFLAGS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerAnimPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERANIMPLAY_HPP\n#define OPENMW_PROCESSORPLAYERANIMPLAY_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAnimPlay final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerAnimPlay()\n        {\n            BPP_INIT(ID_PLAYER_ANIM_PLAY)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_ANIM_PLAY about LocalPlayer from server\");\n                static_cast<LocalPlayer*>(player)->playAnimation();\n            }\n            else if (player != 0)\n                static_cast<DedicatedPlayer*>(player)->playAnimation();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERANIMPLAY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerAttack.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERATTACK_HPP\n#define OPENMW_PROCESSORPLAYERATTACK_HPP\n\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw/mwmp/MechanicsHelper.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAttack final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerAttack()\n        {\n            BPP_INIT(ID_PLAYER_ATTACK)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal() && player != 0)\n            {\n                DedicatedPlayer& dedicatedPlayer = static_cast<DedicatedPlayer&>(*player);\n                MWWorld::Ptr playerPtr = dedicatedPlayer.getPtr();\n                MWBase::World* world = MWBase::Environment::get().getWorld();\n                world->moveObject(playerPtr, dedicatedPlayer.position.pos[0], dedicatedPlayer.position.pos[1], dedicatedPlayer.position.pos[2]);\n                world->rotateObject(playerPtr, dedicatedPlayer.position.rot[0], 0, dedicatedPlayer.position.rot[2]);\n                MechanicsHelper::processAttack(player->attack, playerPtr);\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERATTACK_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerAttribute.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERATTRIBUTE_HPP\n#define OPENMW_PROCESSORPLAYERATTRIBUTE_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw/mwmechanics/npcstats.hpp\"\n#include \"apps/openmw/mwclass/npc.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAttribute final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerAttribute()\n        {\n            BPP_INIT(ID_PLAYER_ATTRIBUTE)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                if (isRequest())\n                    static_cast<LocalPlayer *>(player)->updateAttributes(true);\n                else\n                    static_cast<LocalPlayer *>(player)->setAttributes();\n            }\n            else if (player != 0)\n            {\n                static_cast<DedicatedPlayer *>(player)->setAttributes();\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERATTRIBUTE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERBASEINFO_HPP\n#define OPENMW_PROCESSORPLAYERBASEINFO_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerBaseInfo final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerBaseInfo()\n        {\n            BPP_INIT(ID_PLAYER_BASEINFO)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_BASEINFO from server\");\n\n            if (isLocal())\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about LocalPlayer\");\n\n                if (isRequest())\n                {\n                    LOG_APPEND(TimedLog::LOG_INFO, \"- Requesting info\");\n                    packet.Send(serverAddr);\n                }\n                else\n                {\n                    LOG_APPEND(TimedLog::LOG_INFO, \"- Setting character for LocalPlayer\");\n                    static_cast<LocalPlayer*>(player)->setCharacter();\n                }\n            }\n            else\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about %s\", player == 0 ? \"new player\" : player->npc.mName.c_str());\n\n                if (player == 0)\n                {\n                    LOG_APPEND(TimedLog::LOG_INFO, \"- Exchanging data with new player\");\n                    player = PlayerList::newPlayer(guid);\n\n                    packet.setPlayer(player);\n                    packet.Read();\n                }\n\n                static_cast<DedicatedPlayer*>(player)->setBaseInfo();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERBASEINFO_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerBehavior.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERBEHAVIOR_HPP\n#define OPENMW_PROCESSORPLAYERBEHAVIOR_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerBehavior final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerBehavior()\n        {\n            BPP_INIT(ID_PLAYER_BEHAVIOR)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                //static_cast<LocalPlayer *>(player)->setBehavior();\n            }\n            else if (player != 0)\n            {\n                //static_cast<DedicatedPlayer *>(player)->setBehavior();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERBEHAVIOR_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerBook.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERBOOK_HPP\n#define OPENMW_PROCESSORPLAYERBOOK_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerBook final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerBook()\n        {\n            BPP_INIT(ID_PLAYER_BOOK)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                static_cast<LocalPlayer*>(player)->setBooks();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERBOOK_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerBounty.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERBOUNTY_HPP\n#define OPENMW_PROCESSORPLAYERBOUNTY_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerBounty final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerBounty()\n        {\n            BPP_INIT(ID_PLAYER_BOUNTY)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                if (isRequest())\n                    static_cast<LocalPlayer *>(player)->updateBounty(true);\n                else\n                    static_cast<LocalPlayer *>(player)->setBounty();\n            }\n            else if (player != 0)\n            {\n                MWWorld::Ptr ptrPlayer =  static_cast<DedicatedPlayer *>(player)->getPtr();\n                MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);\n\n                ptrNpcStats->setBounty(player->npcStats.mBounty);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERBOUNTY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerCast.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCAST_HPP\n#define OPENMW_PROCESSORPLAYERCAST_HPP\n\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw/mwmp/MechanicsHelper.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCast final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCast()\n        {\n            BPP_INIT(ID_PLAYER_CAST)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal() && player != 0)\n            {\n                DedicatedPlayer& dedicatedPlayer = static_cast<DedicatedPlayer&>(*player);\n                MWWorld::Ptr playerPtr = dedicatedPlayer.getPtr();\n                MWBase::World* world = MWBase::Environment::get().getWorld();\n                world->moveObject(playerPtr, dedicatedPlayer.position.pos[0], dedicatedPlayer.position.pos[1], dedicatedPlayer.position.pos[2]);\n                world->rotateObject(playerPtr, dedicatedPlayer.position.rot[0], 0, dedicatedPlayer.position.rot[2]);\n                MechanicsHelper::processCast(player->cast, playerPtr);\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERCAST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerCellChange.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCELLCHANGE_HPP\n#define OPENMW_PROCESSORPLAYERCELLCHANGE_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCellChange final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCellChange()\n        {\n            BPP_INIT(ID_PLAYER_CELL_CHANGE)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_CELL_CHANGE from server about me\");\n\n                if (isRequest())\n                    static_cast<LocalPlayer *>(player)->updateCell(true);\n                else\n                    static_cast<LocalPlayer *>(player)->setCell();\n            }\n            else if (player != 0)\n                static_cast<DedicatedPlayer*>(player)->setCell();\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERCELLCHANGE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerCellState.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCELLSTATE_HPP\n#define OPENMW_PROCESSORPLAYERCELLSTATE_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCellState final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCellState()\n        {\n            BPP_INIT(ID_PLAYER_CELL_STATE)\n            avoidReading = true;\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal() && isRequest())\n                static_cast<LocalPlayer *>(player)->sendCellStates();\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERCELLSTATE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerCharClass.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCHARCLASS_HPP\n#define OPENMW_PROCESSORPLAYERCHARCLASS_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCharClass final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCharClass()\n        {\n            BPP_INIT(ID_PLAYER_CHARCLASS)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                if (isRequest())\n                    static_cast<LocalPlayer *>(player)->sendClass();\n                else\n                    static_cast<LocalPlayer *>(player)->setClass();\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERCHARCLASS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerCharGen.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCHARGEN_HPP\n#define OPENMW_PROCESSORPLAYERCHARGEN_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCharGen final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCharGen()\n        {\n            BPP_INIT(ID_PLAYER_CHARGEN)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERCHARGEN_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerCooldowns.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCOOLDOWNS_HPP\n#define OPENMW_PROCESSORPLAYERCOOLDOWNS_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCooldowns final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCooldowns()\n        {\n            BPP_INIT(ID_PLAYER_COOLDOWNS)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal()) return;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_COOLDOWNS about LocalPlayer from server\");\n\n            LocalPlayer &localPlayer = static_cast<LocalPlayer&>(*player);\n            localPlayer.setCooldowns();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERCOOLDOWNS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerDeath.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERDEATH_HPP\n#define OPENMW_PROCESSORPLAYERDEATH_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerDeath final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerDeath()\n        {\n            BPP_INIT(ID_PLAYER_DEATH)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_DEATH from server\");\n\n            if (isLocal())\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about me\");\n\n                static_cast<LocalPlayer*>(player)->die();\n            }\n            else if (player != 0)\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about %s\", player->npc.mName.c_str());\n\n                static_cast<DedicatedPlayer*>(player)->die();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERDEATH_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerDisposition.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERDISPOSITION_HPP\n#define OPENMW_PROCESSORPLAYERDISPOSITION_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerDisposition final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerDisposition()\n        {\n            BPP_INIT(ID_PLAYER_DISPOSITION)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            // Placeholder to be filled in later\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERDISPOSITION_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerEquipment.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYEREQUIPMENT_HPP\n#define OPENMW_PROCESSORPLAYEREQUIPMENT_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerEquipment final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerEquipment()\n        {\n            BPP_INIT(ID_PLAYER_EQUIPMENT)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_EQUIPMENT about LocalPlayer from server\");\n\n                if (isRequest())\n                    static_cast<LocalPlayer*>(player)->updateEquipment(true);\n                else\n                    static_cast<LocalPlayer*>(player)->setEquipment();\n            }\n            else if (player != 0)\n                static_cast<DedicatedPlayer*>(player)->setEquipment();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYEREQUIPMENT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerFaction.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERFACTION_HPP\n#define OPENMW_PROCESSORPLAYERFACTION_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerFaction final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerFaction()\n        {\n            BPP_INIT(ID_PLAYER_FACTION)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isRequest())\n            {\n                // Entire faction membership cannot currently be requested from players\n            }\n            else if (player != 0)\n            {\n                static_cast<LocalPlayer*>(player)->setFactions();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERFACTION_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerInput.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERINPUT_HPP\n#define OPENMW_PROCESSORPLAYERINPUT_HPP\n\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw/mwmp/MechanicsHelper.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerInput final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerInput()\n        {\n            BPP_INIT(ID_PLAYER_INPUT)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            //if (player != 0)\n            //    MechanicsHelper::processInteraction(player->interaction, static_cast<DedicatedPlayer*>(player)->getPtr());\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERINPUT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerInventory.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERUPDATEINVENTORY_HPP\n#define OPENMW_PROCESSORPLAYERUPDATEINVENTORY_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerInventory final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerInventory()\n        {\n            BPP_INIT(ID_PLAYER_INVENTORY)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal()) return;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_INVENTORY about LocalPlayer from server\");\n\n            if (isRequest())\n                static_cast<LocalPlayer*>(player)->updateInventory(true);\n            else\n            {\n                LocalPlayer &localPlayer = static_cast<LocalPlayer&>(*player);\n                int inventoryAction = localPlayer.inventoryChanges.action;\n\n                // Because we send PlayerInventory packets from the same OpenMW methods that we use to set the\n                // items received, we need to set a boolean to prevent resending the items set here\n                localPlayer.avoidSendingInventoryPackets = true;\n\n                if (inventoryAction == InventoryChanges::ADD)\n                    localPlayer.addItems();\n                else if (inventoryAction == InventoryChanges::REMOVE)\n                    localPlayer.removeItems();\n                else // InventoryChanges::SET\n                    localPlayer.setInventory();\n\n                localPlayer.avoidSendingInventoryPackets = false;\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERUPDATEINVENTORY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerItemUse.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERITEMUSE_HPP\n#define OPENMW_PROCESSORPLAYERITEMUSE_HPP\n\n#include \"apps/openmw/mwbase/environment.hpp\"\n#include \"apps/openmw/mwgui/inventorywindow.hpp\"\n#include \"apps/openmw/mwgui/windowmanagerimp.hpp\"\n#include \"apps/openmw/mwworld/inventorystore.hpp\"\n\n#include \"apps/openmw/mwmp/MechanicsHelper.hpp\"\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerItemUse final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerItemUse()\n        {\n            BPP_INIT(ID_PLAYER_ITEM_USE)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal()) return;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_ITEM_USE about LocalPlayer from server\");\n\n            if (!isRequest())\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- refId: %s, count: %i, charge: %i, enchantmentCharge: %f, soul: %s\",\n                    player->usedItem.refId.c_str(), player->usedItem.count, player->usedItem.charge,\n                    player->usedItem.enchantmentCharge, player->usedItem.soul.c_str());\n\n                MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr();\n                MWWorld::InventoryStore &inventoryStore = playerPtr.getClass().getInventoryStore(playerPtr);\n\n                MWWorld::Ptr itemPtr = MechanicsHelper::getItemPtrFromStore(player->usedItem, inventoryStore);\n\n                if (itemPtr)\n                {\n                    MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(itemPtr);\n\n                    if (player->usingItemMagic)\n                    {\n                        MWWorld::ContainerStoreIterator storeIterator = inventoryStore.begin();\n                        for (; storeIterator != inventoryStore.end(); ++storeIterator)\n                        {\n                            if (*storeIterator == itemPtr)\n                                break;\n                        }\n\n                        inventoryStore.setSelectedEnchantItem(storeIterator);\n                    }\n\n                    if (player->itemUseDrawState != MWMechanics::DrawState_Nothing)\n                        playerPtr.getClass().getNpcStats(playerPtr).setDrawState(static_cast<MWMechanics::DrawState_>(player->itemUseDrawState));\n                }\n                else\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Cannot use non-existent item %s\", player->usedItem.refId.c_str());\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERITEMUSE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerJail.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERJAIL_HPP\n#define OPENMW_PROCESSORPLAYERJAIL_HPP\n\n#include \"apps/openmw/mwbase/environment.hpp\"\n#include \"apps/openmw/mwgui/windowmanagerimp.hpp\"\n\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/Networking.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerJail final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerJail()\n        {\n            BPP_INIT(ID_PLAYER_JAIL)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_JAIL from server\");\n            \n            if (isLocal())\n            {\n                // Apply death penalties\n                if (player->jailDays > 0)\n                {\n                    MWBase::Environment::get().getWindowManager()->goToJail(player->jailDays);\n                }\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERJAIL_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerJournal.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERJOURNAL_HPP\n#define OPENMW_PROCESSORPLAYERJOURNAL_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerJournal final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerJournal()\n        {\n            BPP_INIT(ID_PLAYER_JOURNAL)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_JOURNAL from server\");\n\n            if (isRequest())\n            {\n                // Entire journal cannot currently be requested from players\n            }\n            else if (player != 0)\n            {\n                static_cast<LocalPlayer*>(player)->addJournalItems();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERJOURNAL_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerLevel.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERLEVEL_HPP\n#define OPENMW_PROCESSORPLAYERLEVEL_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerLevel final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerLevel()\n        {\n            BPP_INIT(ID_PLAYER_LEVEL)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                if (isRequest())\n                    static_cast<LocalPlayer *>(player)->updateLevel(true);\n                else\n                    static_cast<LocalPlayer *>(player)->setLevel();\n            }\n            else if (player != 0)\n            {\n                MWWorld::Ptr ptrPlayer =  static_cast<DedicatedPlayer *>(player)->getPtr();\n                MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer);\n\n                ptrCreatureStats->setLevel(player->creatureStats.mLevel);\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERLEVEL_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerMiscellaneous.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERMISCELLANEOUS_HPP\n#define OPENMW_PROCESSORPLAYERMISCELLANEOUS_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerMiscellaneous final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerMiscellaneous()\n        {\n            BPP_INIT(ID_PLAYER_MISCELLANEOUS)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal()) return;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_MISCELLANEOUS about LocalPlayer from server\");\n\n            if (!isRequest())\n            {\n                LocalPlayer &localPlayer = static_cast<LocalPlayer&>(*player);\n\n                if (player->miscellaneousChangeType == mwmp::MISCELLANEOUS_CHANGE_TYPE::MARK_LOCATION)\n                    localPlayer.setMarkLocation();\n                else if (player->miscellaneousChangeType == mwmp::MISCELLANEOUS_CHANGE_TYPE::SELECTED_SPELL)\n                    localPlayer.setSelectedSpell();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERMISCELLANEOUS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerMomentum.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERMOMENTUM_HPP\n#define OPENMW_PROCESSORPLAYERMOMENTUM_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerMomentum final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerMomentum()\n        {\n            BPP_INIT(ID_PLAYER_MOMENTUM)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal()) return;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_MOMENTUM about LocalPlayer from server\");\n\n            if (!isRequest())\n            {\n                LocalPlayer &localPlayer = static_cast<LocalPlayer&>(*player);\n                localPlayer.setMomentum();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERMOMENTUM_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerPosition.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERPOSITION_HPP\n#define OPENMW_PROCESSORPLAYERPOSITION_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerPosition final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerPosition()\n        {\n            BPP_INIT(ID_PLAYER_POSITION)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                if (!isRequest())\n                {\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"ID_PLAYER_POSITION changed by server\");\n\n                    static_cast<LocalPlayer*>(player)->setPosition();\n                }\n                else\n                    static_cast<LocalPlayer*>(player)->updatePosition(true);\n            }\n            else if (player != 0) // dedicated player\n                static_cast<DedicatedPlayer*>(player)->updateMarker();\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERPOSITION_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerQuickKeys.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERQUICKKEYS_HPP\n#define OPENMW_PROCESSORPLAYERQUICKKEYS_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerQuickKeys final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerQuickKeys()\n        {\n            BPP_INIT(ID_PLAYER_QUICKKEYS)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal()) return;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_QUICKKEYS about LocalPlayer from server\");\n\n            if (!isRequest())\n            {\n                LocalPlayer &localPlayer = static_cast<LocalPlayer&>(*player);\n\n                // Because we send PlayerQuickKeys packets from the same OpenMW methods that we use to set the\n                // quick keys received, we need to set a boolean to prevent resending the keys set here\n                localPlayer.isReceivingQuickKeys = true;\n\n                localPlayer.setQuickKeys();\n\n                localPlayer.isReceivingQuickKeys = false;\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERQUICKKEYS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerReputation.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERREPUTATION_HPP\n#define OPENMW_PROCESSORPLAYERREPUTATION_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerReputation final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerReputation()\n        {\n            BPP_INIT(ID_PLAYER_REPUTATION)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isRequest())\n            {\n                static_cast<LocalPlayer *>(player)->updateReputation(true);\n            }\n            else if (player != 0)\n            {\n                static_cast<LocalPlayer *>(player)->setReputation();\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERREPUTATION_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerRest.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERREST_HPP\n#define OPENMW_PROCESSORPLAYERREST_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerRest final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerRest()\n        {\n            BPP_INIT(ID_PLAYER_REST)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            // Placeholder to be filled in later\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERREST_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerResurrect.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERRESURRECT_HPP\n#define OPENMW_PROCESSORPLAYERRESURRECT_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\n#include \"apps/openmw/mwmechanics/mechanicsmanagerimp.hpp\"\n\n#include \"apps/openmw/mwmp/Main.hpp\"\n#include \"apps/openmw/mwmp/Networking.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerResurrect final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerResurrect()\n        {\n            BPP_INIT(ID_PLAYER_RESURRECT)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_RESURRECT from server\");\n            \n            if (isLocal())\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about me with resurrectType of %i\", player->resurrectType);\n\n                static_cast<LocalPlayer*>(player)->resurrect();\n            }\n            else if (player != 0)\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about %s\", player->npc.mName.c_str());\n\n                static_cast<DedicatedPlayer*>(player)->resurrect();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERRESURRECT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerShapeshift.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSHAPESHIFT_HPP\n#define OPENMW_PROCESSORPLAYERSHAPESHIFT_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerShapeshift final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerShapeshift()\n        {\n            BPP_INIT(ID_PLAYER_SHAPESHIFT)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_SHAPESHIFT from server\");\n\n            if (isLocal())\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about LocalPlayer\");\n\n                static_cast<LocalPlayer *>(player)->setShapeshift();\n            }\n            else if (player != 0)\n            {\n                static_cast<DedicatedPlayer *>(player)->setShapeshift();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERSHAPESHIFT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerSkill.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSKILL_HPP\n#define OPENMW_PROCESSORPLAYERSKILL_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerSkill final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerSkill()\n        {\n            BPP_INIT(ID_PLAYER_SKILL)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                if (isRequest())\n                    static_cast<LocalPlayer *>(player)->updateSkills(true);\n                else\n                    static_cast<LocalPlayer *>(player)->setSkills();\n            }\n            else if (player != 0)\n            {\n                static_cast<DedicatedPlayer *>(player)->setSkills();\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERSKILL_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerSpeech.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSPEECH_HPP\n#define OPENMW_PROCESSORPLAYERSPEECH_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerSpeech final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerSpeech()\n        {\n            BPP_INIT(ID_PLAYER_SPEECH)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_SPEECH about LocalPlayer from server\");\n                static_cast<LocalPlayer*>(player)->playSpeech();\n            }\n            else if (player != 0)\n                static_cast<DedicatedPlayer*>(player)->playSpeech();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERSPEECH_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerSpellbook.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSPELLBOOK_HPP\n#define OPENMW_PROCESSORPLAYERSPELLBOOK_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerSpellbook final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerSpellbook()\n        {\n            BPP_INIT(ID_PLAYER_SPELLBOOK)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (!isLocal()) return;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_SPELLBOOK about LocalPlayer from server\");\n\n            if (isRequest())\n                static_cast<LocalPlayer*>(player)->sendSpellbook();\n            else\n            {\n                LocalPlayer &localPlayer = static_cast<LocalPlayer&>(*player);\n                \n                int spellbookAction = localPlayer.spellbookChanges.action;\n\n                if (spellbookAction == SpellbookChanges::ADD)\n                    localPlayer.addSpells();\n                else if (spellbookAction == SpellbookChanges::REMOVE)\n                    localPlayer.removeSpells();\n                else // SpellbookChanges::SET\n                    localPlayer.setSpellbook();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERSPELLBOOK_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerSpellsActive.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSPELLSACTIVE_HPP\n#define OPENMW_PROCESSORPLAYERSPELLSACTIVE_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerSpellsActive final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerSpellsActive()\n        {\n            BPP_INIT(ID_PLAYER_SPELLS_ACTIVE)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_SPELLS_ACTIVE from server\");\n\n            if (isLocal())\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about me\");\n\n                if (isRequest())\n                    static_cast<LocalPlayer*>(player)->sendSpellsActive();\n                else\n                {\n                    LocalPlayer& localPlayer = static_cast<LocalPlayer&>(*player);\n\n                    int spellsActiveAction = localPlayer.spellsActiveChanges.action;\n\n                    if (spellsActiveAction == SpellsActiveChanges::ADD)\n                        localPlayer.addSpellsActive();\n                    else if (spellsActiveAction == SpellsActiveChanges::REMOVE)\n                        localPlayer.removeSpellsActive();\n                    else // SpellsActiveChanges::SET\n                        localPlayer.setSpellsActive();\n                }\n            }\n            else if (player != 0)\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Packet was about %s\", player->npc.mName.c_str());\n\n                DedicatedPlayer& dedicatedPlayer = static_cast<DedicatedPlayer&>(*player);\n\n                int spellsActiveAction = dedicatedPlayer.spellsActiveChanges.action;\n\n                if (spellsActiveAction == SpellsActiveChanges::ADD)\n                    dedicatedPlayer.addSpellsActive();\n                else if (spellsActiveAction == SpellsActiveChanges::REMOVE)\n                    dedicatedPlayer.removeSpellsActive();\n                else // SpellsActiveChanges::SET\n                    dedicatedPlayer.setSpellsActive();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERSPELLSACTIVE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerStatsDynamic.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSTATSDYNAMIC_HPP\n#define OPENMW_PROCESSORPLAYERSTATSDYNAMIC_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerStatsDynamic final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerStatsDynamic()\n        {\n            BPP_INIT(ID_PLAYER_STATS_DYNAMIC)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n            {\n                if (isRequest())\n                    static_cast<LocalPlayer *>(player)->updateStatsDynamic(true);\n                else\n                    static_cast<LocalPlayer *>(player)->setDynamicStats();\n            }\n            else if (player != 0)\n            {\n                static_cast<DedicatedPlayer*>(player)->setStatsDynamic();\n            }\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERSTATSDYNAMIC_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorPlayerTopic.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERTOPIC_HPP\n#define OPENMW_PROCESSORPLAYERTOPIC_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerTopic final: public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerTopic()\n        {\n            BPP_INIT(ID_PLAYER_TOPIC)\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isRequest())\n            {\n                // Entire list of topics cannot currently be requested from players\n            }\n            else if (player != 0)\n            {\n                static_cast<LocalPlayer*>(player)->addTopics();\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERTOPIC_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp",
    "content": "#ifndef OPENMW_PROCESSORUSERDISCONNECTED_HPP\n#define OPENMW_PROCESSORUSERDISCONNECTED_HPP\n\n\n#include \"../PlayerProcessor.hpp\"\n#include <components/openmw-mp/Utils.hpp>\n#include <apps/openmw/mwbase/environment.hpp>\n#include \"apps/openmw/mwstate/statemanagerimp.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorUserDisconnected final: public PlayerProcessor\n    {\n    public:\n        ProcessorUserDisconnected()\n        {\n            BPP_INIT(ID_USER_DISCONNECTED)\n            avoidReading = true;\n        }\n\n        virtual void Do(PlayerPacket &packet, BasePlayer *player)\n        {\n            if (isLocal())\n                MWBase::Environment::get().getStateManager()->requestQuit();\n            else if (player != 0)\n            {\n                mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n\n                for (std::vector<RakNet::RakNetGUID>::iterator iter = localPlayer->alliedPlayers.begin(); iter != localPlayer->alliedPlayers.end(); )\n                {\n                    if (*iter == guid)\n                    {\n                        DedicatedPlayer *dedicatedPlayer = PlayerList::getPlayer(guid);\n                        LOG_APPEND(TimedLog::LOG_INFO, \"- Deleting %s from our allied players\", dedicatedPlayer->npc.mName.c_str());\n                        iter = localPlayer->alliedPlayers.erase(iter);\n                    }\n                    else\n                        ++iter;\n                }\n\n                PlayerList::deletePlayer(guid);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORUSERDISCONNECTED_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/system/ProcessorSystemHandshake.hpp",
    "content": "#ifndef OPENMW_PROCESSORSYSTEMHANDSHAKE_HPP\n#define OPENMW_PROCESSORSYSTEMHANDSHAKE_HPP\n\n#include <components/openmw-mp/Base/BaseSystem.hpp>\n\n#include \"apps/openmw/mwmp/Main.hpp\"\n\n#include \"../SystemProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorSystemHandshake final: public SystemProcessor\n    {\n    public:\n        ProcessorSystemHandshake()\n        {\n            BPP_INIT(ID_SYSTEM_HANDSHAKE)\n        }\n\n        virtual void Do(SystemPacket &packet, BaseSystem *system)\n        {\n            packet.setSystem(Main::get().getLocalSystem());\n            packet.Send(serverAddr);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORSYSTEMHANDSHAKE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp",
    "content": "#ifndef OPENMW_PROCESSORCELLRESET_HPP\n#define OPENMW_PROCESSORCELLRESET_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n#include <apps/openmw/mwworld/player.hpp>\n\nnamespace mwmp\n{\n    class ProcessorCellReset final: public WorldstateProcessor\n    {\n    public:\n        ProcessorCellReset()\n        {\n            BPP_INIT(ID_CELL_RESET)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_CELL_RESET\");\n\n            CellController* cellController = Main::get().getCellController();\n\n            mwmp::Main::get().getNetworking()->getWorldstate()->resetCells(&worldstate.cellsToReset);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORCELLRESET_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorClientScriptGlobal.hpp",
    "content": "#ifndef OPENMW_PROCESSORCLIENTSCRIPTGLOBAL_HPP\n#define OPENMW_PROCESSORCLIENTSCRIPTGLOBAL_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorClientScriptGlobal final: public WorldstateProcessor\n    {\n    public:\n        ProcessorClientScriptGlobal()\n        {\n            BPP_INIT(ID_CLIENT_SCRIPT_GLOBAL)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            mwmp::Main::get().getNetworking()->getWorldstate()->setClientGlobals();\n        }\n    };\n}\n\n\n\n#endif //OPENMW_PROCESSORCLIENTSCRIPTGLOBAL_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorClientScriptSettings.hpp",
    "content": "#ifndef OPENMW_PROCESSORCLIENTSCRIPTSETTINGS_HPP\n#define OPENMW_PROCESSORCLIENTSCRIPTSETTINGS_HPP\n\n\n#include <apps/openmw/mwbase/world.hpp>\n#include <apps/openmw/mwbase/environment.hpp>\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorClientScriptSettings final: public WorldstateProcessor\n    {\n    public:\n        ProcessorClientScriptSettings()\n        {\n            BPP_INIT(ID_CLIENT_SCRIPT_SETTINGS)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_CLIENT_SCRIPT_SETTINGS making us send packets for the following globals:\");\n            std::string debugMessage = \"\";\n\n            for (const auto &globalId : worldstate.synchronizedClientGlobalIds)\n            {\n                if (TimedLog::GetLevel() <= TimedLog::LOG_INFO)\n                {\n                    if (!debugMessage.empty())\n                        debugMessage += \", \";\n\n                    debugMessage += globalId;\n                }\n            }\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- %s\", debugMessage.c_str());\n        }\n    };\n}\n\n\n\n#endif //OPENMW_PROCESSORCLIENTSCRIPTSETTINGS_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorRecordDynamic.hpp",
    "content": "#ifndef OPENMW_PROCESSORRECORDDYNAMIC_HPP\n#define OPENMW_PROCESSORRECORDDYNAMIC_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorRecordDynamic final: public WorldstateProcessor\n    {\n    public:\n        ProcessorRecordDynamic()\n        {\n            BPP_INIT(ID_RECORD_DYNAMIC)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            worldstate.addRecords();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORRECORDDYNAMIC_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorWorldCollisionOverride.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDCOLLISIONOVERRIDE_HPP\n#define OPENMW_PROCESSORWORLDCOLLISIONOVERRIDE_HPP\n\n\n#include <apps/openmw/mwbase/world.hpp>\n#include <apps/openmw/mwbase/environment.hpp>\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldCollisionOverride final: public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldCollisionOverride()\n        {\n            BPP_INIT(ID_WORLD_COLLISION_OVERRIDE)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            // Placeholder\n        }\n    };\n}\n\n\n\n#endif //OPENMW_PROCESSORWORLDCOLLISIONOVERRIDE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorWorldDestinationOverride.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDDESTINATIONOVERRIDE_HPP\n#define OPENMW_PROCESSORWORLDDESTINATIONOVERRIDE_HPP\n\n\n#include <apps/openmw/mwbase/world.hpp>\n#include <apps/openmw/mwbase/environment.hpp>\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldDestinationOverride final: public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldDestinationOverride()\n        {\n            BPP_INIT(ID_WORLD_DESTINATION_OVERRIDE)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_WORLD_DESTINATION_OVERRIDE with the following overrides:\");\n\n            for (auto iterator : worldstate.destinationOverrides)\n            {\n                LOG_APPEND(TimedLog::LOG_INFO, \"- %s now leads to %s\", iterator.first.c_str(), iterator.second.c_str());\n            }\n        }\n    };\n}\n\n\n\n#endif //OPENMW_PROCESSORWORLDDESTINATIONOVERRIDE_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorWorldKillCount.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDKILLCOUNT_HPP\n#define OPENMW_PROCESSORWORLDKILLCOUNT_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldKillCount final: public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldKillCount()\n        {\n            BPP_INIT(ID_WORLD_KILL_COUNT)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            mwmp::Main::get().getNetworking()->getWorldstate()->setKills();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORWORLDKILLCOUNT_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorWorldMap.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDMAP_HPP\n#define OPENMW_PROCESSORWORLDMAP_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldMap final: public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldMap()\n        {\n            BPP_INIT(ID_WORLD_MAP)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            worldstate.setMapExplored();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORWORLDMAP_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorWorldRegionAuthority.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDREGIONAUTHORITY_HPP\n#define OPENMW_PROCESSORWORLDREGIONAUTHORITY_HPP\n\n#include <apps/openmw/mwbase/world.hpp>\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldRegionAuthority final: public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldRegionAuthority()\n        {\n            BPP_INIT(ID_WORLD_REGION_AUTHORITY)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            MWBase::World *world = MWBase::Environment::get().getWorld();\n\n            if (!worldstate.authorityRegion.empty() && Misc::StringUtils::ciEqual(worldstate.authorityRegion,\n                world->getPlayerPtr().getCell()->getCell()->mRegion))\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s about %s\", strPacketID.c_str(), worldstate.authorityRegion.c_str());\n\n                if (isLocal())\n                {\n                    LOG_APPEND(TimedLog::LOG_INFO, \"- The new region authority is me\");\n                    // There's a chance we've been made the region authority right after a teleportation that hasn't\n                    // been registered in the WeatherManager yet, so make sure we update it\n                    world->updateWeather(0);\n\n                    world->setWeatherCreationState(true);\n                    world->sendWeather();\n                }\n                else\n                {\n                    BasePlayer *player = PlayerList::getPlayer(guid);\n\n                    if (player != 0)\n                        LOG_APPEND(TimedLog::LOG_INFO, \"- The new region authority is %s\", player->npc.mName.c_str());\n\n                    world->setWeatherCreationState(false);\n                }\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORWORLDREGIONAUTHORITY_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorWorldTime.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDTIME_HPP\n#define OPENMW_PROCESSORWORLDTIME_HPP\n\n#include <apps/openmw/mwbase/world.hpp>\n#include <apps/openmw/mwbase/environment.hpp>\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldTime final: public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldTime()\n        {\n            BPP_INIT(ID_WORLD_TIME)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            MWBase::World *world = MWBase::Environment::get().getWorld();\n\n            if (worldstate.time.hour != -1)\n                world->setGlobalFloat(\"gamehour\", worldstate.time.hour);\n\n            if (worldstate.time.day != -1)\n                world->setGlobalInt(\"day\", worldstate.time.day);\n\n            if (worldstate.time.month != -1)\n                world->setGlobalInt(\"month\", worldstate.time.month);\n\n            if (worldstate.time.year != -1)\n                world->setGlobalInt(\"year\", worldstate.time.year);\n\n            if (worldstate.time.timeScale != -1)\n                world->setGlobalFloat(\"timescale\", worldstate.time.timeScale);\n\n            if (worldstate.time.daysPassed != -1)\n                world->setGlobalInt(\"dayspassed\", worldstate.time.daysPassed);\n        }\n    };\n}\n\n\n\n#endif //OPENMW_PROCESSORWORLDTIME_HPP\n"
  },
  {
    "path": "apps/openmw/mwmp/processors/worldstate/ProcessorWorldWeather.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDWEATHER_HPP\n#define OPENMW_PROCESSORWORLDWEATHER_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldWeather final: public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldWeather()\n        {\n            BPP_INIT(ID_WORLD_WEATHER)\n        }\n\n        virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)\n        {\n            worldstate.setWeather();\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORWORLDWEATHER_HPP\n"
  },
  {
    "path": "apps/openmw/mwphysics/actor.cpp",
    "content": "#include \"actor.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/resource/bulletshape.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/misc/convert.hpp>\n\n#include \"../mwworld/class.hpp\"\n\n#include \"collisiontype.hpp\"\n#include \"mtphysics.hpp\"\n\n#include <cmath>\n\nnamespace MWPhysics\n{\n\n\nActor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler, bool canWaterWalk)\n  : mStandingOnPtr(nullptr), mCanWaterWalk(canWaterWalk), mWalkingOnWater(false)\n  , mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBox.center), mHalfExtents(shape->mCollisionBox.extents)\n  , mVelocity(0,0,0), mStuckFrames(0), mLastStuckPosition{0, 0, 0}\n  , mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)\n  , mInternalCollisionMode(true)\n  , mExternalCollisionMode(true)\n  , mTaskScheduler(scheduler)\n{\n    mPtr = ptr;\n\n    // We can not create actor without collisions - he will fall through the ground.\n    // In this case we should autogenerate collision box based on mesh shape\n    // (NPCs have bodyparts and use a different approach)\n    if (!ptr.getClass().isNpc() && mHalfExtents.length2() == 0.f)\n    {\n        if (shape->mCollisionShape)\n        {\n            btTransform transform;\n            transform.setIdentity();\n            btVector3 min;\n            btVector3 max;\n\n            shape->mCollisionShape->getAabb(transform, min, max);\n            mHalfExtents.x() = (max[0] - min[0])/2.f;\n            mHalfExtents.y() = (max[1] - min[1])/2.f;\n            mHalfExtents.z() = (max[2] - min[2])/2.f;\n\n            mMeshTranslation = osg::Vec3f(0.f, 0.f, mHalfExtents.z());\n        }\n\n        if (mHalfExtents.length2() == 0.f)\n            Log(Debug::Error) << \"Error: Failed to calculate bounding box for actor \\\"\" << ptr.getCellRef().getRefId() << \"\\\".\";\n    }\n\n    mShape.reset(new btBoxShape(Misc::Convert::toBullet(mHalfExtents)));\n    mRotationallyInvariant = (mMeshTranslation.x() == 0.0 && mMeshTranslation.y() == 0.0) && std::fabs(mHalfExtents.x() - mHalfExtents.y()) < 2.2;\n\n    mConvexShape = static_cast<btConvexShape*>(mShape.get());\n\n    mCollisionObject = std::make_unique<btCollisionObject>();\n    mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);\n    mCollisionObject->setActivationState(DISABLE_DEACTIVATION);\n    mCollisionObject->setCollisionShape(mShape.get());\n    mCollisionObject->setUserPointer(this);\n\n    updateScale();\n\n    if(!mRotationallyInvariant)\n        updateRotation();\n\n    updatePosition();\n    addCollisionMask(getCollisionMask());\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to disable collision for players or regular actors from a packet\n    */\n    mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n    if (mwmp::PlayerList::isDedicatedPlayer(ptr))\n    {\n        if (!worldstate->hasPlayerCollision)\n            enableCollisionBody(false);\n    }\n    else\n    {\n        if (!worldstate->hasActorCollision)\n            enableCollisionBody(false);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    updateCollisionObjectPosition();\n}\n\nActor::~Actor()\n{\n    mTaskScheduler->removeCollisionObject(mCollisionObject.get());\n}\n\nvoid Actor::enableCollisionMode(bool collision)\n{\n    mInternalCollisionMode.store(collision, std::memory_order_release);\n}\n\nvoid Actor::enableCollisionBody(bool collision)\n{\n    if (mExternalCollisionMode != collision)\n    {\n        mExternalCollisionMode = collision;\n        updateCollisionMask();\n    }\n}\n\nvoid Actor::addCollisionMask(int collisionMask)\n{\n    mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Actor, collisionMask);\n}\n\nvoid Actor::updateCollisionMask()\n{\n    mTaskScheduler->setCollisionFilterMask(mCollisionObject.get(), getCollisionMask());\n}\n\nint Actor::getCollisionMask() const\n{\n    int collisionMask = CollisionType_World | CollisionType_HeightMap;\n    if (mExternalCollisionMode)\n        collisionMask |= CollisionType_Actor | CollisionType_Projectile | CollisionType_Door;\n    if (mCanWaterWalk)\n        collisionMask |= CollisionType_Water;\n    return collisionMask;\n}\n\nvoid Actor::updatePosition()\n{\n    std::scoped_lock lock(mPositionMutex);\n    const auto worldPosition = mPtr.getRefData().getPosition().asVec3();\n    mPreviousPosition = worldPosition;\n    mPosition = worldPosition;\n    mSimulationPosition = worldPosition;\n    mPositionOffset = osg::Vec3f();\n    mStandingOnPtr = nullptr;\n    mSkipCollisions = true;\n    mSkipSimulation = true;\n}\n\nvoid Actor::setSimulationPosition(const osg::Vec3f& position)\n{\n    if (!std::exchange(mSkipSimulation, false))\n        mSimulationPosition = position;\n}\n\nosg::Vec3f Actor::getSimulationPosition() const\n{\n    return mSimulationPosition;\n}\n\nosg::Vec3f Actor::getScaledMeshTranslation() const\n{\n    return mRotation * osg::componentMultiply(mMeshTranslation, mScale);\n}\n\nvoid Actor::updateCollisionObjectPosition()\n{\n    std::scoped_lock lock(mPositionMutex);\n    mShape->setLocalScaling(Misc::Convert::toBullet(mScale));\n    osg::Vec3f newPosition = getScaledMeshTranslation() + mPosition;\n\n    auto& trans = mCollisionObject->getWorldTransform();\n    trans.setOrigin(Misc::Convert::toBullet(newPosition));\n    trans.setRotation(Misc::Convert::toBullet(mRotation));\n    mCollisionObject->setWorldTransform(trans);\n\n    mWorldPositionChanged = false;\n}\n\nosg::Vec3f Actor::getCollisionObjectPosition() const\n{\n    std::scoped_lock lock(mPositionMutex);\n    return getScaledMeshTranslation() + mPosition;\n}\n\nbool Actor::setPosition(const osg::Vec3f& position)\n{\n    std::scoped_lock lock(mPositionMutex);\n    applyOffsetChange();\n    bool hasChanged = mPosition != position || mWorldPositionChanged;\n    mPreviousPosition = mPosition;\n    mPosition = position;\n    return hasChanged;\n}\n\nvoid Actor::adjustPosition(const osg::Vec3f& offset, bool ignoreCollisions)\n{\n    std::scoped_lock lock(mPositionMutex);\n    mPositionOffset += offset;\n    mSkipCollisions = mSkipCollisions || ignoreCollisions;\n}\n\nvoid Actor::applyOffsetChange()\n{\n    if (mPositionOffset.length() == 0)\n        return;\n    mPosition += mPositionOffset;\n    mPreviousPosition += mPositionOffset;\n    mSimulationPosition += mPositionOffset;\n    mPositionOffset = osg::Vec3f();\n    mWorldPositionChanged = true;\n}\n\nosg::Vec3f Actor::getPosition() const\n{\n    return mPosition;\n}\n\nosg::Vec3f Actor::getPreviousPosition() const\n{\n    return mPreviousPosition;\n}\n\nvoid Actor::updateRotation ()\n{\n    std::scoped_lock lock(mPositionMutex);\n    mRotation = mPtr.getRefData().getBaseNode()->getAttitude();\n}\n\nbool Actor::isRotationallyInvariant() const\n{\n    return mRotationallyInvariant;\n}\n\nvoid Actor::updateScale()\n{\n    std::scoped_lock lock(mPositionMutex);\n    float scale = mPtr.getCellRef().getScale();\n    osg::Vec3f scaleVec(scale,scale,scale);\n\n    mPtr.getClass().adjustScale(mPtr, scaleVec, false);\n    mScale = scaleVec;\n\n    scaleVec = osg::Vec3f(scale,scale,scale);\n    mPtr.getClass().adjustScale(mPtr, scaleVec, true);\n    mRenderingScale = scaleVec;\n}\n\nosg::Vec3f Actor::getHalfExtents() const\n{\n    std::scoped_lock lock(mPositionMutex);\n    return osg::componentMultiply(mHalfExtents, mScale);\n}\n\nosg::Vec3f Actor::getOriginalHalfExtents() const\n{\n    return mHalfExtents;\n}\n\nosg::Vec3f Actor::getRenderingHalfExtents() const\n{\n    std::scoped_lock lock(mPositionMutex);\n    return osg::componentMultiply(mHalfExtents, mRenderingScale);\n}\n\nvoid Actor::setInertialForce(const osg::Vec3f &force)\n{\n    mForce = force;\n}\n\nvoid Actor::setOnGround(bool grounded)\n{\n    mOnGround.store(grounded, std::memory_order_release);\n}\n\nvoid Actor::setOnSlope(bool slope)\n{\n    mOnSlope.store(slope, std::memory_order_release);\n}\n\nbool Actor::isWalkingOnWater() const\n{\n    return mWalkingOnWater.load(std::memory_order_acquire);\n}\n\nvoid Actor::setWalkingOnWater(bool walkingOnWater)\n{\n    mWalkingOnWater.store(walkingOnWater, std::memory_order_release);\n}\n\nvoid Actor::setCanWaterWalk(bool waterWalk)\n{\n    if (waterWalk != mCanWaterWalk)\n    {\n        mCanWaterWalk = waterWalk;\n        updateCollisionMask();\n    }\n}\n\nMWWorld::Ptr Actor::getStandingOnPtr() const\n{\n    std::scoped_lock lock(mPositionMutex);\n    return mStandingOnPtr;\n}\n\nvoid Actor::setStandingOnPtr(const MWWorld::Ptr& ptr)\n{\n    std::scoped_lock lock(mPositionMutex);\n    mStandingOnPtr = ptr;\n}\n\nbool Actor::skipCollisions()\n{\n    return std::exchange(mSkipCollisions, false);\n}\n\nvoid Actor::setVelocity(osg::Vec3f velocity)\n{\n    mVelocity = velocity;\n}\n\nosg::Vec3f Actor::velocity()\n{\n    return std::exchange(mVelocity, osg::Vec3f());\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/actor.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_ACTOR_H\n#define OPENMW_MWPHYSICS_ACTOR_H\n\n#include <atomic>\n#include <memory>\n#include <mutex>\n\n#include \"ptrholder.hpp\"\n\n#include <LinearMath/btTransform.h>\n#include <osg/Vec3f>\n#include <osg/Quat>\n\nclass btCollisionShape;\nclass btCollisionObject;\nclass btConvexShape;\n\nnamespace Resource\n{\n    class BulletShape;\n}\n\nnamespace MWPhysics\n{\n    class PhysicsTaskScheduler;\n\n    class Actor final : public PtrHolder\n    {\n    public:\n        Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler, bool canWaterWalk);\n        ~Actor() override;\n\n        /**\n         * Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry.\n         */\n        void enableCollisionMode(bool collision);\n\n        bool getCollisionMode() const\n        {\n            return mInternalCollisionMode.load(std::memory_order_acquire);\n        }\n\n        btConvexShape* getConvexShape() const { return mConvexShape; }\n\n        /**\n         * Enables or disables the *external* collision body. If disabled, other actors will not collide with this actor.\n         */\n        void enableCollisionBody(bool collision);\n\n        void updateScale();\n        void updateRotation();\n\n        /**\n         * Return true if the collision shape looks the same no matter how its Z rotated.\n         */\n        bool isRotationallyInvariant() const;\n\n        /**\n        * Used by the physics simulation to store the simulation result. Used in conjunction with mWorldPosition\n        * to account for e.g. scripted movements\n        */\n        void setSimulationPosition(const osg::Vec3f& position);\n        osg::Vec3f getSimulationPosition() const;\n\n        void updateCollisionObjectPosition();\n\n        /**\n         * Returns the half extents of the collision body (scaled according to collision scale)\n         */\n        osg::Vec3f getHalfExtents() const;\n\n        /**\n         * Returns the half extents of the collision body (not scaled)\n         */\n        osg::Vec3f getOriginalHalfExtents() const;\n\n        /**\n         * Returns the position of the collision body\n         * @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space.\n         */\n        osg::Vec3f getCollisionObjectPosition() const;\n\n        /**\n          * Store the current position into mPreviousPosition, then move to this position.\n          * Returns true if the new position is different.\n          */\n        bool setPosition(const osg::Vec3f& position);\n\n        // force set actor position to be as in Ptr::RefData\n        void updatePosition();\n\n        // register a position offset that will be applied during simulation.\n        void adjustPosition(const osg::Vec3f& offset, bool ignoreCollisions);\n\n        // apply position offset. Can't be called during simulation\n        void applyOffsetChange();\n\n        osg::Vec3f getPosition() const;\n\n        osg::Vec3f getPreviousPosition() const;\n\n        /**\n         * Returns the half extents of the collision body (scaled according to rendering scale)\n         * @note The reason we need this extra method is because of an inconsistency in MW - NPC race scales aren't applied to the collision shape,\n         * most likely to make environment collision testing easier. However in some cases (swimming level) we want the actual scale.\n         */\n        osg::Vec3f getRenderingHalfExtents() const;\n\n        /**\n         * Sets the current amount of inertial force (incl. gravity) affecting this physic actor\n         */\n        void setInertialForce(const osg::Vec3f &force);\n\n        /**\n         * Gets the current amount of inertial force (incl. gravity) affecting this physic actor\n         */\n        const osg::Vec3f &getInertialForce() const\n        {\n            return mForce;\n        }\n\n        void setOnGround(bool grounded);\n\n        bool getOnGround() const\n        {\n            return mInternalCollisionMode.load(std::memory_order_acquire) && mOnGround.load(std::memory_order_acquire);\n        }\n\n        void setOnSlope(bool slope);\n\n        bool getOnSlope() const\n        {\n            return mInternalCollisionMode.load(std::memory_order_acquire) && mOnSlope.load(std::memory_order_acquire);\n        }\n\n        btCollisionObject* getCollisionObject() const\n        {\n            return mCollisionObject.get();\n        }\n\n        /// Sets whether this actor should be able to collide with the water surface\n        void setCanWaterWalk(bool waterWalk);\n\n        /// Sets whether this actor has been walking on the water surface in the last frame\n        void setWalkingOnWater(bool walkingOnWater);\n        bool isWalkingOnWater() const;\n\n        MWWorld::Ptr getStandingOnPtr() const;\n        void setStandingOnPtr(const MWWorld::Ptr& ptr);\n\n        unsigned int getStuckFrames() const\n        {\n            return mStuckFrames;\n        }\n        void setStuckFrames(unsigned int frames)\n        {\n            mStuckFrames = frames;\n        }\n\n        const osg::Vec3f &getLastStuckPosition() const\n        {\n            return mLastStuckPosition;\n        }\n        void setLastStuckPosition(osg::Vec3f position)\n        {\n            mLastStuckPosition = position;\n        }\n\n        bool skipCollisions();\n\n        void setVelocity(osg::Vec3f velocity);\n        osg::Vec3f velocity();\n\n    private:\n        MWWorld::Ptr mStandingOnPtr;\n        /// Removes then re-adds the collision object to the dynamics world\n        void updateCollisionMask();\n        void addCollisionMask(int collisionMask);\n        int getCollisionMask() const;\n\n        /// Returns the mesh translation, scaled and rotated as necessary\n        osg::Vec3f getScaledMeshTranslation() const;\n\n        bool mCanWaterWalk;\n        std::atomic<bool> mWalkingOnWater;\n\n        bool mRotationallyInvariant;\n\n        std::unique_ptr<btCollisionShape> mShape;\n        btConvexShape* mConvexShape;\n\n        std::unique_ptr<btCollisionObject> mCollisionObject;\n\n        osg::Vec3f mMeshTranslation;\n        osg::Vec3f mHalfExtents;\n        osg::Quat mRotation;\n\n        osg::Vec3f mScale;\n        osg::Vec3f mRenderingScale;\n        osg::Vec3f mSimulationPosition;\n        osg::Vec3f mPosition;\n        osg::Vec3f mPreviousPosition;\n        osg::Vec3f mPositionOffset;\n        osg::Vec3f mVelocity;\n        bool mWorldPositionChanged;\n        bool mSkipCollisions;\n        bool mSkipSimulation;\n        mutable std::mutex mPositionMutex;\n\n        unsigned int mStuckFrames;\n        osg::Vec3f mLastStuckPosition;\n\n        osg::Vec3f mForce;\n        std::atomic<bool> mOnGround;\n        std::atomic<bool> mOnSlope;\n        std::atomic<bool> mInternalCollisionMode;\n        bool mExternalCollisionMode;\n\n        PhysicsTaskScheduler* mTaskScheduler;\n\n        Actor(const Actor&);\n        Actor& operator=(const Actor&);\n    };\n\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/actorconvexcallback.cpp",
    "content": "#include \"actorconvexcallback.hpp\"\n#include \"collisiontype.hpp\"\n#include \"contacttestwrapper.h\"\n\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n#include <components/misc/convert.hpp>\n\n#include \"projectile.hpp\"\n\nnamespace MWPhysics\n{\n    class ActorOverlapTester : public btCollisionWorld::ContactResultCallback\n    {\n    public:\n        bool overlapping = false;\n\n        btScalar addSingleResult(btManifoldPoint& cp,\n            const btCollisionObjectWrapper* colObj0Wrap,\n            int partId0,\n            int index0,\n            const btCollisionObjectWrapper* colObj1Wrap,\n            int partId1,\n            int index1) override\n        {\n            if(cp.getDistance() <= 0.0f)\n                overlapping = true;\n            return btScalar(1);\n        }\n    };\n\n    ActorConvexCallback::ActorConvexCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world)\n    : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)),\n      mMe(me), mMotion(motion), mMinCollisionDot(minCollisionDot), mWorld(world)\n    {\n    }\n\n    btScalar ActorConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)\n    {\n        if (convexResult.m_hitCollisionObject == mMe)\n            return btScalar(1);\n\n        // override data for actor-actor collisions\n        // vanilla Morrowind seems to make overlapping actors collide as though they are both cylinders with a diameter of the distance between them\n        // For some reason this doesn't work as well as it should when using capsules, but it still helps a lot.\n        if(convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor)\n        {\n            ActorOverlapTester isOverlapping;\n            // FIXME: This is absolutely terrible and bullet should feel terrible for not making contactPairTest const-correct.\n            ContactTestWrapper::contactPairTest(const_cast<btCollisionWorld*>(mWorld), const_cast<btCollisionObject*>(mMe), const_cast<btCollisionObject*>(convexResult.m_hitCollisionObject), isOverlapping);\n\n            if(isOverlapping.overlapping)\n            {\n                auto originA = Misc::Convert::toOsg(mMe->getWorldTransform().getOrigin());\n                auto originB = Misc::Convert::toOsg(convexResult.m_hitCollisionObject->getWorldTransform().getOrigin());\n                osg::Vec3f motion = Misc::Convert::toOsg(mMotion);\n                osg::Vec3f normal = (originA-originB);\n                normal.z() = 0;\n                normal.normalize();\n                // only collide if horizontally moving towards the hit actor (note: the motion vector appears to be inverted)\n                // FIXME: This kinda screws with standing on actors that walk up slopes for some reason. Makes you fall through them.\n                // It happens in vanilla Morrowind too, but much less often.\n                // I tried hunting down why but couldn't figure it out. Possibly a stair stepping or ground ejection bug.\n                if(normal * motion > 0.0f)\n                {\n                    convexResult.m_hitFraction = 0.0f;\n                    convexResult.m_hitNormalLocal = Misc::Convert::toBullet(normal);\n                    return ClosestConvexResultCallback::addSingleResult(convexResult, true);\n                }\n                else\n                {\n                    return btScalar(1);\n                }\n            }\n        }\n        if (convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Projectile)\n        {\n            auto* projectileHolder = static_cast<Projectile*>(convexResult.m_hitCollisionObject->getUserPointer());\n            if (!projectileHolder->isActive())\n                return btScalar(1);\n            auto* targetHolder = static_cast<PtrHolder*>(mMe->getUserPointer());\n            const MWWorld::Ptr target = targetHolder->getPtr();\n            if (projectileHolder->isValidTarget(target))\n                projectileHolder->hit(target, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal);\n            return btScalar(1);\n        }\n\n        btVector3 hitNormalWorld;\n        if (normalInWorldSpace)\n            hitNormalWorld = convexResult.m_hitNormalLocal;\n        else\n        {\n            ///need to transform normal into worldspace\n            hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;\n        }\n\n        // dot product of the motion vector against the collision contact normal\n        btScalar dotCollision = mMotion.dot(hitNormalWorld);\n        if (dotCollision <= mMinCollisionDot)\n            return btScalar(1);\n\n        return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/actorconvexcallback.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_ACTORCONVEXCALLBACK_H\n#define OPENMW_MWPHYSICS_ACTORCONVEXCALLBACK_H\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\nclass btCollisionObject;\n\nnamespace MWPhysics\n{\n    class ActorConvexCallback : public btCollisionWorld::ClosestConvexResultCallback\n    {\n    public:\n        ActorConvexCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world);\n\n        btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) override;\n\n    protected:\n        const btCollisionObject *mMe;\n        const btVector3 mMotion;\n        const btScalar mMinCollisionDot;\n        const btCollisionWorld * mWorld;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/closestnotmerayresultcallback.cpp",
    "content": "#include \"closestnotmerayresultcallback.hpp\"\n\n#include <algorithm>\n#include <utility>\n\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n\n#include \"../mwworld/class.hpp\"\n\n#include \"ptrholder.hpp\"\n\nnamespace MWPhysics\n{\n    ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*>  targets, const btVector3& from, const btVector3& to)\n    : btCollisionWorld::ClosestRayResultCallback(from, to)\n    , mMe(me), mTargets(std::move(targets))\n    {\n    }\n\n    btScalar ClosestNotMeRayResultCallback::addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)\n    {\n        if (rayResult.m_collisionObject == mMe)\n            return 1.f;\n\n        if (!mTargets.empty())\n        {\n            if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end()))\n            {\n                auto* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());\n                if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor())\n                    return 1.f;\n            }\n        }\n\n        return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/closestnotmerayresultcallback.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_CLOSESTNOTMERAYRESULTCALLBACK_H\n#define OPENMW_MWPHYSICS_CLOSESTNOTMERAYRESULTCALLBACK_H\n\n#include <vector>\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\nclass btCollisionObject;\n\nnamespace MWPhysics\n{\n    class Projectile;\n\n    class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback\n    {\n    public:\n        ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to);\n\n        btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;\n\n    private:\n        const btCollisionObject* mMe;\n        const std::vector<const btCollisionObject*> mTargets;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/collisiontype.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_COLLISIONTYPE_H\n#define OPENMW_MWPHYSICS_COLLISIONTYPE_H\n\nnamespace MWPhysics\n{\n\nenum CollisionType {\n    CollisionType_World = 1<<0,\n    CollisionType_Door = 1<<1,\n    CollisionType_Actor = 1<<2,\n    CollisionType_HeightMap = 1<<3,\n    CollisionType_Projectile = 1<<4,\n    CollisionType_Water = 1<<5\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/constants.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_CONSTANTS_H\n#define OPENMW_MWPHYSICS_CONSTANTS_H\n\nnamespace MWPhysics\n{\n    static constexpr float sStepSizeUp = 34.0f;\n    static constexpr float sStepSizeDown = 62.0f;\n\n    static constexpr float sMinStep = 10.0f; // hack to skip over tiny unwalkable slopes\n    static constexpr float sMinStep2 = 20.0f; // hack to skip over shorter but longer/wider/further unwalkable slopes\n    // whether to do the above stairstepping logic hacks to work around bad morrowind assets - disabling causes problems but improves performance\n    static constexpr bool sDoExtraStairHacks = true;\n\n    static constexpr float sGroundOffset = 1.0f;\n    static constexpr float sMaxSlope = 49.0f;\n\n    // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.\n    static constexpr int sMaxIterations = 8;\n    // Allows for more precise movement solving without getting stuck or snagging too easily.\n    static constexpr float sCollisionMargin = 0.1f;\n    // Allow for a small amount of penetration to prevent numerical precision issues from causing the \"unstuck\"ing code to run unnecessarily\n    // Currently set to 0 because having the \"unstuck\"ing code run whenever possible prevents some glitchy snagging issues\n    static constexpr float sAllowedPenetration = 0.0f;\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/contacttestresultcallback.cpp",
    "content": "#include \"contacttestresultcallback.hpp\"\n\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n\n#include \"components/misc/convert.hpp\"\n\n#include \"ptrholder.hpp\"\n\nnamespace MWPhysics\n{\n    ContactTestResultCallback::ContactTestResultCallback(const btCollisionObject* testedAgainst)\n        : mTestedAgainst(testedAgainst)\n    {\n    }\n\n    btScalar ContactTestResultCallback::addSingleResult(btManifoldPoint& cp,\n                                                        const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,\n                                                        const btCollisionObjectWrapper* col1Wrap,int partId1,int index1)\n    {\n        const btCollisionObject* collisionObject = col0Wrap->m_collisionObject;\n        if (collisionObject == mTestedAgainst)\n            collisionObject = col1Wrap->m_collisionObject;\n        PtrHolder* holder = static_cast<PtrHolder*>(collisionObject->getUserPointer());\n        if (holder)\n            mResult.emplace_back(ContactPoint{holder->getPtr(), Misc::Convert::toOsg(cp.m_positionWorldOnB), Misc::Convert::toOsg(cp.m_normalWorldOnB)});\n        return 0.f;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/contacttestresultcallback.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_CONTACTTESTRESULTCALLBACK_H\n#define OPENMW_MWPHYSICS_CONTACTTESTRESULTCALLBACK_H\n\n#include <vector>\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\n#include \"physicssystem.hpp\"\n\nclass btCollisionObject;\nstruct btCollisionObjectWrapper;\n\nnamespace MWPhysics\n{\n    class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback\n    {\n        const btCollisionObject* mTestedAgainst;\n\n    public:\n        ContactTestResultCallback(const btCollisionObject* testedAgainst);\n\n        btScalar addSingleResult(btManifoldPoint& cp,\n                                         const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,\n                                         const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) override;\n\n        std::vector<ContactPoint> mResult;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/contacttestwrapper.cpp",
    "content": "#include <mutex>\n\n#include \"contacttestwrapper.h\"\n\nnamespace MWPhysics\n{\n    // Concurrent calls to contactPairTest (and by extension contactTest) are forbidden.\n    static std::mutex contactMutex;\n    void ContactTestWrapper::contactTest(btCollisionWorld* collisionWorld, btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback)\n    {\n        std::unique_lock lock(contactMutex);\n        collisionWorld->contactTest(colObj, resultCallback);\n    }\n\n    void ContactTestWrapper::contactPairTest(btCollisionWorld* collisionWorld, btCollisionObject* colObjA, btCollisionObject* colObjB, btCollisionWorld::ContactResultCallback& resultCallback)\n    {\n        std::unique_lock lock(contactMutex);\n        collisionWorld->contactPairTest(colObjA, colObjB, resultCallback);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/contacttestwrapper.h",
    "content": "#ifndef OPENMW_MWPHYSICS_CONTACTTESTWRAPPER_H\n#define OPENMW_MWPHYSICS_CONTACTTESTWRAPPER_H\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\nnamespace MWPhysics\n{\n    struct ContactTestWrapper\n    {\n        static void contactTest(btCollisionWorld* collisionWorld, btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback);\n        static void contactPairTest(btCollisionWorld* collisionWorld, btCollisionObject* colObjA, btCollisionObject* colObjB, btCollisionWorld::ContactResultCallback& resultCallback);\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp",
    "content": "#include \"deepestnotmecontacttestresultcallback.hpp\"\n\n#include <algorithm>\n\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n\n#include \"../mwworld/class.hpp\"\n\n#include \"ptrholder.hpp\"\n\nnamespace MWPhysics\n{\n\n    DeepestNotMeContactTestResultCallback::DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*>& targets, const btVector3 &origin)\n    : mMe(me), mTargets(targets), mOrigin(origin), mLeastDistSqr(std::numeric_limits<float>::max())\n    {\n    }\n\n    btScalar DeepestNotMeContactTestResultCallback::addSingleResult(btManifoldPoint& cp,\n                                                                    const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,\n                                                                    const btCollisionObjectWrapper* col1Wrap,int partId1,int index1)\n    {\n        const btCollisionObject* collisionObject = col1Wrap->m_collisionObject;\n        if (collisionObject != mMe)\n        {\n            if (!mTargets.empty())\n            {\n                if ((std::find(mTargets.begin(), mTargets.end(), collisionObject) == mTargets.end()))\n                {\n                    PtrHolder* holder = static_cast<PtrHolder*>(collisionObject->getUserPointer());\n                    if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor())\n                        return 0.f;\n                }\n            }\n\n            btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());\n            if(!mObject || distsqr < mLeastDistSqr)\n            {\n                mObject = collisionObject;\n                mLeastDistSqr = distsqr;\n                mContactPoint = cp.getPositionWorldOnA();\n                mContactNormal = cp.m_normalWorldOnB;\n            }\n        }\n\n        return 0.f;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_DEEPESTNOTMECONTACTTESTRESULTCALLBACK_H\n#define OPENMW_MWPHYSICS_DEEPESTNOTMECONTACTTESTRESULTCALLBACK_H\n\n#include <vector>\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\nclass btCollisionObject;\n\nnamespace MWPhysics\n{\n    class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback\n    {\n        const btCollisionObject* mMe;\n        const std::vector<const btCollisionObject*> mTargets;\n\n        // Store the real origin, since the shape's origin is its center\n        btVector3 mOrigin;\n\n    public:\n        const btCollisionObject *mObject{nullptr};\n        btVector3 mContactPoint{0,0,0};\n        btVector3 mContactNormal{0,0,0};\n        btScalar mLeastDistSqr;\n\n        DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*>& targets, const btVector3 &origin);\n\n        btScalar addSingleResult(btManifoldPoint& cp,\n                                         const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,\n                                         const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/hasspherecollisioncallback.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_HASSPHERECOLLISIONCALLBACK_H\n#define OPENMW_MWPHYSICS_HASSPHERECOLLISIONCALLBACK_H\n\n#include <LinearMath/btVector3.h>\n#include <BulletCollision/BroadphaseCollision/btBroadphaseInterface.h>\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\n#include <algorithm>\n\nnamespace MWPhysics\n{\n    // https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_collision_detection\n    bool testAabbAgainstSphere(const btVector3& aabbMin, const btVector3& aabbMax,\n        const btVector3& position, const btScalar radius)\n    {\n        const btVector3 nearest(\n            std::max(aabbMin.x(), std::min(aabbMax.x(), position.x())),\n            std::max(aabbMin.y(), std::min(aabbMax.y(), position.y())),\n            std::max(aabbMin.z(), std::min(aabbMax.z(), position.z()))\n        );\n        return nearest.distance(position) < radius;\n    }\n\n    class HasSphereCollisionCallback final : public btBroadphaseAabbCallback\n    {\n    public:\n        HasSphereCollisionCallback(const btVector3& position, const btScalar radius, btCollisionObject* object,\n                const int mask, const int group)\n            : mPosition(position),\n              mRadius(radius),\n              mCollisionObject(object),\n              mCollisionFilterMask(mask),\n              mCollisionFilterGroup(group)\n        {\n        }\n\n        bool process(const btBroadphaseProxy* proxy) override\n        {\n            if (mResult)\n                return false;\n            const auto collisionObject = static_cast<btCollisionObject*>(proxy->m_clientObject);\n            if (collisionObject == mCollisionObject)\n                return true;\n            if (needsCollision(*proxy))\n                mResult = testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius);\n            return !mResult;\n        }\n\n        bool getResult() const\n        {\n            return mResult;\n        }\n\n    private:\n        btVector3 mPosition;\n        btScalar mRadius;\n        btCollisionObject* mCollisionObject;\n        int mCollisionFilterMask;\n        int mCollisionFilterGroup;\n        bool mResult = false;\n\n        bool needsCollision(const btBroadphaseProxy& proxy) const\n        {\n            bool collides = (proxy.m_collisionFilterGroup & mCollisionFilterMask) != 0;\n            collides = collides && (mCollisionFilterGroup & proxy.m_collisionFilterMask);\n            return collides;\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/heightfield.cpp",
    "content": "#include \"heightfield.hpp\"\n#include \"mtphysics.hpp\"\n\n#include <osg/Object>\n\n#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n\n#include <LinearMath/btTransform.h>\n\n#include <type_traits>\n\n#if BT_BULLET_VERSION < 310\n// Older Bullet versions only support `btScalar` heightfields.\n// Our heightfield data is `float`.\n//\n// These functions handle conversion from `float` to `double` when\n// `btScalar` is `double` (`BT_USE_DOUBLE_PRECISION`).\nnamespace\n{\n    template <class T>\n    auto makeHeights(const T* heights, float sqrtVerts)\n        -> std::enable_if_t<std::is_same<btScalar, T>::value, std::vector<btScalar>>\n    {\n        return {};\n    }\n\n    template <class T>\n    auto makeHeights(const T* heights, float sqrtVerts)\n        -> std::enable_if_t<!std::is_same<btScalar, T>::value, std::vector<btScalar>>\n    {\n        return std::vector<btScalar>(heights, heights + static_cast<std::ptrdiff_t>(sqrtVerts * sqrtVerts));\n    }\n\n    template <class T>\n    auto getHeights(const T* floatHeights, const std::vector<btScalar>&)\n        -> std::enable_if_t<std::is_same<btScalar, T>::value, const btScalar*>\n    {\n        return floatHeights;\n    }\n\n    template <class T>\n    auto getHeights(const T*, const std::vector<btScalar>& btScalarHeights)\n        -> std::enable_if_t<!std::is_same<btScalar, T>::value, const btScalar*>\n    {\n        return btScalarHeights.data();\n    }\n}\n#endif\n\nnamespace MWPhysics\n{\n    HeightField::HeightField() {}\n\n    HeightField::HeightField(const HeightField&, const osg::CopyOp&) {}\n\n    HeightField::HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler)\n        : mHoldObject(holdObject)\n#if BT_BULLET_VERSION < 310\n        , mHeights(makeHeights(heights, sqrtVerts))\n#endif\n        , mTaskScheduler(scheduler)\n    {\n#if BT_BULLET_VERSION < 310\n        mShape = std::make_unique<btHeightfieldTerrainShape>(\n            sqrtVerts, sqrtVerts,\n            getHeights(heights, mHeights),\n            1,\n            minH, maxH, 2,\n            PHY_FLOAT, false\n        );\n#else\n        mShape = std::make_unique<btHeightfieldTerrainShape>(\n            sqrtVerts, sqrtVerts, heights, minH, maxH, 2, false);\n#endif\n        mShape->setUseDiamondSubdivision(true);\n        mShape->setLocalScaling(btVector3(triSize, triSize, 1));\n\n#if BT_BULLET_VERSION >= 289\n        // Accelerates some collision tests.\n        //\n        // Note: The accelerator data structure in Bullet is only used\n        // in some operations. This could be improved, see:\n        // https://github.com/bulletphysics/bullet3/issues/3276\n        mShape->buildAccelerator();\n#endif\n\n        btTransform transform(btQuaternion::getIdentity(),\n                                btVector3((x+0.5f) * triSize * (sqrtVerts-1),\n                                          (y+0.5f) * triSize * (sqrtVerts-1),\n                                          (maxH+minH)*0.5f));\n\n        mCollisionObject = std::make_unique<btCollisionObject>();\n        mCollisionObject->setCollisionShape(mShape.get());\n        mCollisionObject->setWorldTransform(transform);\n        mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_HeightMap, CollisionType_Actor|CollisionType_Projectile);\n    }\n\n    HeightField::~HeightField()\n    {\n        mTaskScheduler->removeCollisionObject(mCollisionObject.get());\n    }\n\n    btCollisionObject* HeightField::getCollisionObject()\n    {\n        return mCollisionObject.get();\n    }\n\n    const btCollisionObject* HeightField::getCollisionObject() const\n    {\n        return mCollisionObject.get();\n    }\n\n    const btHeightfieldTerrainShape* HeightField::getShape() const\n    {\n        return mShape.get();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/heightfield.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_HEIGHTFIELD_H\n#define OPENMW_MWPHYSICS_HEIGHTFIELD_H\n\n#include <osg/ref_ptr>\n#include <osg/Object>\n\n#include <LinearMath/btScalar.h>\n\n#include <memory>\n#include <vector>\n\nclass btCollisionObject;\nclass btHeightfieldTerrainShape;\n\nnamespace osg\n{\n    class Object;\n}\n\nnamespace MWPhysics\n{\n    class PhysicsTaskScheduler;\n\n    class HeightField : public osg::Object\n    {\n    public:\n        HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler);\n        ~HeightField();\n\n        META_Object(MWPhysics, HeightField)\n\n        btCollisionObject* getCollisionObject();\n        const btCollisionObject* getCollisionObject() const;\n        const btHeightfieldTerrainShape* getShape() const;\n\n    private:\n        std::unique_ptr<btHeightfieldTerrainShape> mShape;\n        std::unique_ptr<btCollisionObject> mCollisionObject;\n        osg::ref_ptr<const osg::Object> mHoldObject;\n#if BT_BULLET_VERSION < 310\n        std::vector<btScalar> mHeights;\n#endif\n\n        PhysicsTaskScheduler* mTaskScheduler;\n\n        HeightField();\n        HeightField(const HeightField&, const osg::CopyOp&);\n        void operator=(const HeightField&);\n        HeightField(const HeightField&);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/movementsolver.cpp",
    "content": "#include \"movementsolver.hpp\"\n\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n#include <BulletCollision/CollisionShapes/btCollisionShape.h>\n\n#include <components/esm/loadgmst.hpp>\n#include <components/misc/convert.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/refdata.hpp\"\n\n#include \"actor.hpp\"\n#include \"collisiontype.hpp\"\n#include \"constants.hpp\"\n#include \"contacttestwrapper.h\"\n#include \"physicssystem.hpp\"\n#include \"stepper.hpp\"\n#include \"trace.h\"\n\n#include <cmath>\n\nnamespace MWPhysics\n{\n    static bool isActor(const btCollisionObject *obj)\n    {\n        assert(obj);\n        return obj->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor;\n    }\n\n    class ContactCollectionCallback : public btCollisionWorld::ContactResultCallback\n    {\n    public:\n        ContactCollectionCallback(const btCollisionObject * me, osg::Vec3f velocity) : mMe(me)\n        {\n            m_collisionFilterGroup = me->getBroadphaseHandle()->m_collisionFilterGroup;\n            m_collisionFilterMask = me->getBroadphaseHandle()->m_collisionFilterMask & ~CollisionType_Projectile;\n            mVelocity = Misc::Convert::toBullet(velocity);\n        }\n        btScalar addSingleResult(btManifoldPoint & contact, const btCollisionObjectWrapper * colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper * colObj1Wrap, int partId1, int index1) override\n        {\n            if (isActor(colObj0Wrap->getCollisionObject()) && isActor(colObj1Wrap->getCollisionObject()))\n                return 0.0;\n            // ignore overlap if we're moving in the same direction as it would push us out (don't change this to >=, that would break detection when not moving)\n            if (contact.m_normalWorldOnB.dot(mVelocity) > 0.0)\n                return 0.0;\n            auto delta = contact.m_normalWorldOnB * -contact.m_distance1;\n            mContactSum += delta;\n            mMaxX = std::max(std::abs(delta.x()), mMaxX);\n            mMaxY = std::max(std::abs(delta.y()), mMaxY);\n            mMaxZ = std::max(std::abs(delta.z()), mMaxZ);\n            if (contact.m_distance1 < mDistance)\n            {\n                mDistance = contact.m_distance1;\n                mNormal = contact.m_normalWorldOnB;\n                mDelta = delta;\n                return mDistance;\n            }\n            else\n            {\n                return 0.0;\n            }\n        }\n        btScalar mMaxX = 0.0;\n        btScalar mMaxY = 0.0;\n        btScalar mMaxZ = 0.0;\n        btVector3 mContactSum{0.0, 0.0, 0.0};\n        btVector3 mNormal{0.0, 0.0, 0.0}; // points towards \"me\"\n        btVector3 mDelta{0.0, 0.0, 0.0}; // points towards \"me\"\n        btScalar mDistance = 0.0; // negative or zero\n    protected:\n        btVector3 mVelocity;\n        const btCollisionObject * mMe;\n    };\n\n    osg::Vec3f MovementSolver::traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight)\n    {\n        osg::Vec3f offset = actor->getCollisionObjectPosition() - ptr.getRefData().getPosition().asVec3();\n\n        ActorTracer tracer;\n        tracer.findGround(actor, position + offset, position + offset - osg::Vec3f(0,0,maxHeight), collisionWorld);\n        if (tracer.mFraction >= 1.0f)\n        {\n            actor->setOnGround(false);\n            return position;\n        }\n\n        actor->setOnGround(true);\n\n        // Check if we actually found a valid spawn point (use an infinitely thin ray this time).\n        // Required for some broken door destinations in Morrowind.esm, where the spawn point\n        // intersects with other geometry if the actor's base is taken into account\n        btVector3 from = Misc::Convert::toBullet(position);\n        btVector3 to = from - btVector3(0,0,maxHeight);\n\n        btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);\n        resultCallback1.m_collisionFilterGroup = 0xff;\n        resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap;\n\n        collisionWorld->rayTest(from, to, resultCallback1);\n\n        if (resultCallback1.hasHit() && ((Misc::Convert::toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos + offset).length2() > 35*35\n            || !isWalkableSlope(tracer.mPlaneNormal)))\n        {\n            actor->setOnSlope(!isWalkableSlope(resultCallback1.m_hitNormalWorld));\n            return Misc::Convert::toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, sGroundOffset);\n        }\n\n        actor->setOnSlope(!isWalkableSlope(tracer.mPlaneNormal));\n\n        return tracer.mEndPos-offset + osg::Vec3f(0.f, 0.f, sGroundOffset);\n    }\n\n    void MovementSolver::move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld,\n                                           WorldFrameData& worldData)\n    {\n        auto* physicActor = actor.mActorRaw;\n        const ESM::Position& refpos = actor.mRefpos;\n        // Early-out for totally static creatures\n        // (Not sure if gravity should still apply?)\n        {\n            const auto ptr = physicActor->getPtr();\n            if (!ptr.getClass().isMobile(ptr))\n                return;\n        }\n\n        // Reset per-frame data\n        physicActor->setWalkingOnWater(false);\n        // Anything to collide with?\n        if(!physicActor->getCollisionMode() || actor.mSkipCollisionDetection)\n        {\n            actor.mPosition += (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) *\n                                osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))\n                                ) * actor.mMovement * time;\n            return;\n        }\n\n        const btCollisionObject *colobj = physicActor->getCollisionObject();\n\n        // Adjust for collision mesh offset relative to actor's \"location\"\n        // (doTrace doesn't take local/interior collision shape translation into account, so we have to do it on our own)\n        // for compatibility with vanilla assets, we have to derive this from the vertical half extent instead of from internal hull translation\n        // if not for this hack, the \"correct\" collision hull position would be physicActor->getScaledMeshTranslation()\n        osg::Vec3f halfExtents = physicActor->getHalfExtents();\n        actor.mPosition.z() += halfExtents.z(); // vanilla-accurate\n\n        static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fSwimHeightScale\")->mValue.getFloat();\n        float swimlevel = actor.mWaterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale);\n\n        ActorTracer tracer;\n\n        osg::Vec3f inertia = physicActor->getInertialForce();\n        osg::Vec3f velocity;\n\n        if (actor.mPosition.z() < swimlevel || actor.mFlying)\n        {\n            velocity = (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement;\n        }\n        else\n        {\n            velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement;\n\n            if ((velocity.z() > 0.f && physicActor->getOnGround() && !physicActor->getOnSlope())\n            || (velocity.z() > 0.f && velocity.z() + inertia.z() <= -velocity.z() && physicActor->getOnSlope()))\n                inertia = velocity;\n            else if (!physicActor->getOnGround() || physicActor->getOnSlope())\n                velocity = velocity + inertia;\n        }\n\n        // Dead and paralyzed actors underwater will float to the surface,\n        // if the CharacterController tells us to do so\n        if (actor.mMovement.z() > 0 && actor.mFloatToSurface && actor.mPosition.z() < swimlevel)\n            velocity = osg::Vec3f(0,0,1) * 25;\n\n        if (actor.mWantJump)\n            actor.mDidJump = true;\n\n        // Now that we have the effective movement vector, apply wind forces to it\n        if (worldData.mIsInStorm)\n        {\n            osg::Vec3f stormDirection = worldData.mStormDirection;\n            float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length())));\n            static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fStromWalkMult\")->mValue.getFloat();\n            velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));\n        }\n\n        Stepper stepper(collisionWorld, colobj);\n        osg::Vec3f origVelocity = velocity;\n        osg::Vec3f newPosition = actor.mPosition;\n        /*\n         * A loop to find newPosition using tracer, if successful different from the starting position.\n         * nextpos is the local variable used to find potential newPosition, using velocity and remainingTime\n         * The initial velocity was set earlier (see above).\n        */\n        float remainingTime = time;\n        bool seenGround = physicActor->getOnGround() && !physicActor->getOnSlope() && !actor.mFlying;\n\n        int numTimesSlid = 0;\n        osg::Vec3f lastSlideNormal(0,0,1);\n        osg::Vec3f lastSlideNormalFallback(0,0,1);\n        bool forceGroundTest = false;\n\n        for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.0001f; ++iterations)\n        {\n            osg::Vec3f nextpos = newPosition + velocity * remainingTime;\n\n            // If not able to fly, don't allow to swim up into the air\n            if(!actor.mFlying && nextpos.z() > swimlevel && newPosition.z() < swimlevel)\n            {\n                const osg::Vec3f down(0,0,-1);\n                velocity = reject(velocity, down);\n                // NOTE: remainingTime is unchanged before the loop continues\n                continue; // velocity updated, calculate nextpos again\n            }\n\n            if((newPosition - nextpos).length2() > 0.0001)\n            {\n                // trace to where character would go if there were no obstructions\n                tracer.doTrace(colobj, newPosition, nextpos, collisionWorld);\n\n                // check for obstructions\n                if(tracer.mFraction >= 1.0f)\n                {\n                    newPosition = tracer.mEndPos; // ok to move, so set newPosition\n                    break;\n                }\n            }\n            else\n            {\n                // The current position and next position are nearly the same, so just exit.\n                // Note: Bullet can trigger an assert in debug modes if the positions\n                // are the same, since that causes it to attempt to normalize a zero\n                // length vector (which can also happen with nearly identical vectors, since\n                // precision can be lost due to any math Bullet does internally). Since we\n                // aren't performing any collision detection, we want to reject the next\n                // position, so that we don't slowly move inside another object.\n                break;\n            }\n\n            if (isWalkableSlope(tracer.mPlaneNormal) && !actor.mFlying && newPosition.z() >= swimlevel)\n                seenGround = true;\n\n            // We hit something. Check if we can step up.\n            float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z();\n            osg::Vec3f oldPosition = newPosition;\n            bool usedStepLogic = false;\n            if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject))\n            {\n                // Try to step up onto it.\n                // NOTE: this modifies newPosition and velocity on its own if successful\n                usedStepLogic = stepper.step(newPosition, velocity, remainingTime, seenGround, iterations == 0);\n            }\n            if (usedStepLogic)\n            {\n                // don't let pure water creatures move out of water after stepMove\n                const auto ptr = physicActor->getPtr();\n                if (ptr.getClass().isPureWaterCreature(ptr) && newPosition.z() + halfExtents.z() > actor.mWaterlevel)\n                    newPosition = oldPosition;\n                else if(!actor.mFlying && actor.mPosition.z() >= swimlevel)\n                    forceGroundTest = true;\n            }\n            else\n            {\n                // Can't step up, so slide against what we ran into\n                remainingTime *= (1.0f-tracer.mFraction);\n\n                auto planeNormal = tracer.mPlaneNormal;\n\n                // If we touched the ground this frame, and whatever we ran into is a wall of some sort,\n                // pretend that its collision normal is pointing horizontally\n                // (fixes snagging on slightly downward-facing walls, and crawling up the bases of very steep walls because of the collision margin)\n                if (seenGround && !isWalkableSlope(planeNormal) && planeNormal.z() != 0)\n                {\n                    planeNormal.z() = 0;\n                    planeNormal.normalize();\n                }\n\n                // Move up to what we ran into (with a bit of a collision margin)\n                if ((newPosition-tracer.mEndPos).length2() > sCollisionMargin*sCollisionMargin)\n                {\n                    auto direction = velocity;\n                    direction.normalize();\n                    newPosition = tracer.mEndPos;\n                    newPosition -= direction*sCollisionMargin;\n                }\n\n                osg::Vec3f newVelocity = (velocity * planeNormal <= 0.0) ? reject(velocity, planeNormal) : velocity;\n                bool usedSeamLogic = false;\n\n                // check for the current and previous collision planes forming an acute angle; slide along the seam if they do\n                if(numTimesSlid > 0)\n                {\n                    auto dotA = lastSlideNormal * planeNormal;\n                    auto dotB = lastSlideNormalFallback * planeNormal;\n                    if(numTimesSlid <= 1) // ignore fallback normal if this is only the first or second slide\n                        dotB = 1.0;\n                    if(dotA <= 0.0 || dotB <= 0.0)\n                    {\n                        osg::Vec3f bestNormal = lastSlideNormal;\n                        // use previous-to-previous collision plane if it's acute with current plane but actual previous plane isn't\n                        if(dotB < dotA)\n                        {\n                            bestNormal = lastSlideNormalFallback;\n                            lastSlideNormal = lastSlideNormalFallback;\n                        }\n\n                        auto constraintVector = bestNormal ^ planeNormal; // cross product\n                        if(constraintVector.length2() > 0) // only if it's not zero length\n                        {\n                            constraintVector.normalize();\n                            newVelocity = project(velocity, constraintVector);\n\n                            // version of surface rejection for acute crevices/seams\n                            auto averageNormal = bestNormal + planeNormal;\n                            averageNormal.normalize();\n                            tracer.doTrace(colobj, newPosition, newPosition + averageNormal*(sCollisionMargin*2.0), collisionWorld);\n                            newPosition = (newPosition + tracer.mEndPos)/2.0;\n\n                            usedSeamLogic = true;\n                        }\n                    }\n                }\n                // otherwise just keep the normal vector rejection\n\n                // if this isn't the first iteration, or if the first iteration is also the last iteration,\n                // move away from the collision plane slightly, if possible\n                // this reduces getting stuck in some concave geometry, like the gaps above the railings in some ald'ruhn buildings\n                // this is different from the normal collision margin, because the normal collision margin is along the movement path,\n                // but this is along the collision normal\n                if(!usedSeamLogic && (iterations > 0 || remainingTime < 0.01f))\n                {\n                    tracer.doTrace(colobj, newPosition, newPosition + planeNormal*(sCollisionMargin*2.0), collisionWorld);\n                    newPosition = (newPosition + tracer.mEndPos)/2.0;\n                }\n\n                // Do not allow sliding up steep slopes if there is gravity.\n                if (newPosition.z() >= swimlevel && !actor.mFlying && !isWalkableSlope(planeNormal))\n                    newVelocity.z() = std::min(newVelocity.z(), velocity.z());\n\n                if (newVelocity * origVelocity <= 0.0f)\n                    break;\n\n                numTimesSlid += 1;\n                lastSlideNormalFallback = lastSlideNormal;\n                lastSlideNormal = planeNormal;\n                velocity = newVelocity;\n            }\n        }\n\n        bool isOnGround = false;\n        bool isOnSlope = false;\n        if (forceGroundTest || (inertia.z() <= 0.f && newPosition.z() >= swimlevel))\n        {\n            osg::Vec3f from = newPosition;\n            auto dropDistance = 2*sGroundOffset + (physicActor->getOnGround() ? sStepSizeDown : 0);\n            osg::Vec3f to = newPosition - osg::Vec3f(0,0,dropDistance);\n            tracer.doTrace(colobj, from, to, collisionWorld);\n            if(tracer.mFraction < 1.0f)\n            {\n                if (!isActor(tracer.mHitObject))\n                {\n                    isOnGround = true;\n                    isOnSlope = !isWalkableSlope(tracer.mPlaneNormal);\n\n                    const btCollisionObject* standingOn = tracer.mHitObject;\n                    PtrHolder* ptrHolder = static_cast<PtrHolder*>(standingOn->getUserPointer());\n                    if (ptrHolder)\n                        actor.mStandingOn = ptrHolder->getPtr();\n\n                    if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water)\n                        physicActor->setWalkingOnWater(true);\n                    if (!actor.mFlying && !isOnSlope)\n                    {\n                        if (tracer.mFraction*dropDistance > sGroundOffset)\n                            newPosition.z() = tracer.mEndPos.z() + sGroundOffset;\n                        else\n                        {\n                            newPosition.z() = tracer.mEndPos.z();\n                            tracer.doTrace(colobj, newPosition, newPosition + osg::Vec3f(0, 0, 2*sGroundOffset), collisionWorld);\n                            newPosition = (newPosition+tracer.mEndPos)/2.0;\n                        }\n                    }\n                }\n                else\n                {\n                    // Vanilla allows actors to float on top of other actors. Do not push them off.\n                    if (!actor.mFlying && isWalkableSlope(tracer.mPlaneNormal) && tracer.mEndPos.z()+sGroundOffset <= newPosition.z())\n                        newPosition.z() = tracer.mEndPos.z() + sGroundOffset;\n\n                    isOnGround = false;\n                }\n            }\n            // forcibly treat stuck actors as if they're on flat ground because buggy collisions when inside of things can/will break ground detection\n            if(physicActor->getStuckFrames() > 0)\n            {\n                isOnGround = true;\n                isOnSlope = false;\n            }\n        }\n\n        if((isOnGround && !isOnSlope) || newPosition.z() < swimlevel || actor.mFlying)\n            physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f));\n        else\n        {\n            inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter;\n            if (inertia.z() < 0)\n                inertia.z() *= actor.mSlowFall;\n            if (actor.mSlowFall < 1.f) {\n                inertia.x() *= actor.mSlowFall;\n                inertia.y() *= actor.mSlowFall;\n            }\n            physicActor->setInertialForce(inertia);\n        }\n        physicActor->setOnGround(isOnGround);\n        physicActor->setOnSlope(isOnSlope);\n\n        actor.mPosition = newPosition;\n        // remove what was added earlier in compensating for doTrace not taking interior transformation into account\n        actor.mPosition.z() -= halfExtents.z(); // vanilla-accurate\n    }\n\n    btVector3 addMarginToDelta(btVector3 delta)\n    {\n        if(delta.length2() == 0.0)\n            return delta;\n        return delta + delta.normalized() * sCollisionMargin;\n    }\n\n    void MovementSolver::unstuck(ActorFrameData& actor, const btCollisionWorld* collisionWorld)\n    {\n        const auto& ptr = actor.mActorRaw->getPtr();\n        if (!ptr.getClass().isMobile(ptr))\n            return;\n\n        auto* physicActor = actor.mActorRaw;\n        if(!physicActor->getCollisionMode() || actor.mSkipCollisionDetection) // noclipping/tcl\n            return;\n\n        auto* collisionObject = physicActor->getCollisionObject();\n        auto tempPosition = actor.mPosition;\n\n        if(physicActor->getStuckFrames() >= 10)\n        {\n            if((physicActor->getLastStuckPosition() - actor.mPosition).length2() < 100)\n                return;\n            else\n            {\n                physicActor->setStuckFrames(0);\n                physicActor->setLastStuckPosition({0, 0, 0});\n            }\n        }\n\n        // use vanilla-accurate collision hull position hack (do same hitbox offset hack as movement solver)\n        // if vanilla compatibility didn't matter, the \"correct\" collision hull position would be physicActor->getScaledMeshTranslation()\n        const auto verticalHalfExtent = osg::Vec3f(0.0, 0.0, physicActor->getHalfExtents().z());\n\n        // use a 3d approximation of the movement vector to better judge player intent\n        auto velocity = (osg::Quat(actor.mRefpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(actor.mRefpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement;\n        // try to pop outside of the world before doing anything else if we're inside of it\n        if (!physicActor->getOnGround() || physicActor->getOnSlope())\n                velocity += physicActor->getInertialForce();\n\n        // because of the internal collision box offset hack, and the fact that we're moving the collision box manually,\n        // we need to replicate part of the collision box's transform process from scratch\n        osg::Vec3f refPosition = tempPosition + verticalHalfExtent;\n        osg::Vec3f goodPosition = refPosition;\n        const btTransform oldTransform = collisionObject->getWorldTransform();\n        btTransform newTransform = oldTransform;\n\n        auto gatherContacts = [&](btVector3 newOffset) -> ContactCollectionCallback\n        {\n            goodPosition = refPosition + Misc::Convert::toOsg(addMarginToDelta(newOffset));\n            newTransform.setOrigin(Misc::Convert::toBullet(goodPosition));\n            collisionObject->setWorldTransform(newTransform);\n\n            ContactCollectionCallback callback{collisionObject, velocity};\n            ContactTestWrapper::contactTest(const_cast<btCollisionWorld*>(collisionWorld), collisionObject, callback);\n            return callback;\n        };\n\n        // check whether we're inside the world with our collision box with manually-derived offset\n        auto contactCallback = gatherContacts({0.0, 0.0, 0.0});\n        if(contactCallback.mDistance < -sAllowedPenetration)\n        {\n            physicActor->setStuckFrames(physicActor->getStuckFrames() + 1);\n            physicActor->setLastStuckPosition(actor.mPosition);\n            // we are; try moving it out of the world\n            auto positionDelta = contactCallback.mContactSum;\n            // limit rejection delta to the largest known individual rejections\n            if(std::abs(positionDelta.x()) > contactCallback.mMaxX)\n                positionDelta *= contactCallback.mMaxX / std::abs(positionDelta.x());\n            if(std::abs(positionDelta.y()) > contactCallback.mMaxY)\n                positionDelta *= contactCallback.mMaxY / std::abs(positionDelta.y());\n            if(std::abs(positionDelta.z()) > contactCallback.mMaxZ)\n                positionDelta *= contactCallback.mMaxZ / std::abs(positionDelta.z());\n\n            auto contactCallback2 = gatherContacts(positionDelta);\n            // successfully moved further out from contact (does not have to be in open space, just less inside of things)\n            if(contactCallback2.mDistance > contactCallback.mDistance)\n                tempPosition = goodPosition - verticalHalfExtent;\n            // try again but only upwards (fixes some bad coc floors)\n            else\n            {\n                // upwards-only offset\n                auto contactCallback3 = gatherContacts({0.0, 0.0, std::abs(positionDelta.z())});\n                // success\n                if(contactCallback3.mDistance > contactCallback.mDistance)\n                    tempPosition = goodPosition - verticalHalfExtent;\n                else\n                // try again but fixed distance up\n                {\n                    auto contactCallback4 = gatherContacts({0.0, 0.0, 10.0});\n                    // success\n                    if(contactCallback4.mDistance > contactCallback.mDistance)\n                        tempPosition = goodPosition - verticalHalfExtent;\n                }\n            }\n        }\n        else\n        {\n            physicActor->setStuckFrames(0);\n            physicActor->setLastStuckPosition({0, 0, 0});\n        }\n\n        collisionObject->setWorldTransform(oldTransform);\n        actor.mPosition = tempPosition;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/movementsolver.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_MOVEMENTSOLVER_H\n#define OPENMW_MWPHYSICS_MOVEMENTSOLVER_H\n\n#include <osg/Vec3f>\n\n#include \"constants.hpp\"\n#include \"../mwworld/ptr.hpp\"\n\nclass btCollisionWorld;\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWPhysics\n{\n    /// Vector projection\n    static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v)\n    {\n        return v * (u * v);\n    }\n\n    /// Vector rejection\n    static inline osg::Vec3f reject(const osg::Vec3f& direction, const osg::Vec3f &planeNormal)\n    {\n        return direction - project(direction, planeNormal);\n    }\n\n    template <class Vec3>\n    static bool isWalkableSlope(const Vec3 &normal)\n    {\n        static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope));\n        return (normal.z() > sMaxSlopeCos);\n    }\n\n    class Actor;\n    struct ActorFrameData;\n    struct WorldFrameData;\n\n    class MovementSolver\n    {\n    public:\n        static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight);\n        static void move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld, WorldFrameData& worldData);\n        static void unstuck(ActorFrameData& actor, const btCollisionWorld* collisionWorld);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/mtphysics.cpp",
    "content": "#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>\n#include <BulletCollision/CollisionShapes/btCollisionShape.h>\n\n#include <osg/Stats>\n\n#include \"components/debug/debuglog.hpp\"\n#include <components/misc/barrier.hpp>\n#include \"components/misc/convert.hpp\"\n#include \"components/settings/settings.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n#include \"../mwrender/bulletdebugdraw.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"actor.hpp\"\n#include \"contacttestwrapper.h\"\n#include \"movementsolver.hpp\"\n#include \"mtphysics.hpp\"\n#include \"object.hpp\"\n#include \"physicssystem.hpp\"\n#include \"projectile.hpp\"\n\nnamespace\n{\n    /// @brief A scoped lock that is either shared or exclusive depending on configuration\n    template<class Mutex>\n    class MaybeSharedLock\n    {\n        public:\n            /// @param mutex a shared mutex\n            /// @param canBeSharedLock decide wether the lock will be shared or exclusive\n            MaybeSharedLock(Mutex& mutex, bool canBeSharedLock) : mMutex(mutex), mCanBeSharedLock(canBeSharedLock)\n            {\n                if (mCanBeSharedLock)\n                    mMutex.lock_shared();\n                else\n                    mMutex.lock();\n            }\n\n            ~MaybeSharedLock()\n            {\n                if (mCanBeSharedLock)\n                    mMutex.unlock_shared();\n                else\n                    mMutex.unlock();\n            }\n        private:\n            Mutex& mMutex;\n            bool mCanBeSharedLock;\n    };\n\n    void handleFall(MWPhysics::ActorFrameData& actorData, bool simulationPerformed)\n    {\n        const float heightDiff = actorData.mPosition.z() - actorData.mOldHeight;\n\n        const bool isStillOnGround = (simulationPerformed && actorData.mWasOnGround && actorData.mActorRaw->getOnGround());\n\n        if (isStillOnGround || actorData.mFlying || actorData.mSwimming || actorData.mSlowFall < 1)\n            actorData.mNeedLand = true;\n        else if (heightDiff < 0)\n            actorData.mFallHeight += heightDiff;\n    }\n\n    void handleJump(const MWWorld::Ptr &ptr)\n    {\n        const bool isPlayer = (ptr == MWMechanics::getPlayer());\n        // Advance acrobatics and set flag for GetPCJumping\n        if (isPlayer)\n        {\n            ptr.getClass().skillUsageSucceeded(ptr, ESM::Skill::Acrobatics, 0);\n            MWBase::Environment::get().getWorld()->getPlayer().setJumping(true);\n        }\n\n        // Decrease fatigue\n        if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState())\n        {\n            const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n            const float fFatigueJumpBase = gmst.find(\"fFatigueJumpBase\")->mValue.getFloat();\n            const float fFatigueJumpMult = gmst.find(\"fFatigueJumpMult\")->mValue.getFloat();\n            const float normalizedEncumbrance = std::min(1.f, ptr.getClass().getNormalizedEncumbrance(ptr));\n            const float fatigueDecrease = fFatigueJumpBase + normalizedEncumbrance * fFatigueJumpMult;\n            MWMechanics::DynamicStat<float> fatigue = ptr.getClass().getCreatureStats(ptr).getFatigue();\n            fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);\n            ptr.getClass().getCreatureStats(ptr).setFatigue(fatigue);\n        }\n        ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0;\n    }\n\n    void updateMechanics(MWPhysics::ActorFrameData& actorData)\n    {\n        auto ptr = actorData.mActorRaw->getPtr();\n        if (actorData.mDidJump)\n            handleJump(ptr);\n\n        MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n        if (actorData.mNeedLand)\n            stats.land(ptr == MWMechanics::getPlayer() && (actorData.mFlying || actorData.mSwimming));\n        else if (actorData.mFallHeight < 0)\n            stats.addToFallHeight(-actorData.mFallHeight);\n    }\n\n    osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt)\n    {\n        const float interpolationFactor = std::clamp(timeAccum / physicsDt, 0.0f, 1.0f);\n        return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor);\n    }\n\n    namespace Config\n    {\n        /// @return either the number of thread as configured by the user, or 1 if Bullet doesn't support multithreading\n        int computeNumThreads(bool& threadSafeBullet)\n        {\n            int wantedThread = Settings::Manager::getInt(\"async num threads\", \"Physics\");\n\n            auto broad = std::make_unique<btDbvtBroadphase>();\n            auto maxSupportedThreads = broad->m_rayTestStacks.size();\n            threadSafeBullet = (maxSupportedThreads > 1);\n            if (!threadSafeBullet && wantedThread > 1)\n            {\n                Log(Debug::Warning) << \"Bullet was not compiled with multithreading support, 1 async thread will be used\";\n                return 1;\n            }\n            return std::max(0, wantedThread);\n        }\n    }\n}\n\nnamespace MWPhysics\n{\n    PhysicsTaskScheduler::PhysicsTaskScheduler(float physicsDt, btCollisionWorld *collisionWorld, MWRender::DebugDrawer* debugDrawer)\n          : mDefaultPhysicsDt(physicsDt)\n          , mPhysicsDt(physicsDt)\n          , mTimeAccum(0.f)\n          , mCollisionWorld(collisionWorld)\n          , mDebugDrawer(debugDrawer)\n          , mNumJobs(0)\n          , mRemainingSteps(0)\n          , mLOSCacheExpiry(Settings::Manager::getInt(\"lineofsight keep inactive cache\", \"Physics\"))\n          , mDeferAabbUpdate(Settings::Manager::getBool(\"defer aabb update\", \"Physics\"))\n          , mFrameCounter(0)\n          , mAdvanceSimulation(false)\n          , mQuit(false)\n          , mNextJob(0)\n          , mNextLOS(0)\n          , mFrameNumber(0)\n          , mTimer(osg::Timer::instance())\n          , mPrevStepCount(1)\n          , mBudget(physicsDt)\n          , mAsyncBudget(0.0f)\n          , mBudgetCursor(0)\n          , mAsyncStartTime(0)\n          , mTimeBegin(0)\n          , mTimeEnd(0)\n          , mFrameStart(0)\n    {\n        mNumThreads = Config::computeNumThreads(mThreadSafeBullet);\n\n        if (mNumThreads >= 1)\n        {\n            for (int i = 0; i < mNumThreads; ++i)\n                mThreads.emplace_back([&] { worker(); } );\n        }\n        else\n        {\n            mLOSCacheExpiry = -1;\n            mDeferAabbUpdate = false;\n        }\n\n        mPreStepBarrier = std::make_unique<Misc::Barrier>(mNumThreads);\n\n        mPostStepBarrier = std::make_unique<Misc::Barrier>(mNumThreads);\n\n        mPostSimBarrier = std::make_unique<Misc::Barrier>(mNumThreads);\n    }\n\n    PhysicsTaskScheduler::~PhysicsTaskScheduler()\n    {\n        waitForWorkers();\n        std::unique_lock lock(mSimulationMutex);\n        mQuit = true;\n        mNumJobs = 0;\n        mRemainingSteps = 0;\n        mHasJob.notify_all();\n        lock.unlock();\n        for (auto& thread : mThreads)\n            thread.join();\n    }\n\n    std::tuple<int, float> PhysicsTaskScheduler::calculateStepConfig(float timeAccum) const\n    {\n        int maxAllowedSteps = 2;\n        int numSteps = timeAccum / mDefaultPhysicsDt;\n\n        // adjust maximum step count based on whether we're likely physics bottlenecked or not\n        // if maxAllowedSteps ends up higher than numSteps, we will not invoke delta time\n        // if it ends up lower than numSteps, but greater than 1, we will run a number of true delta time physics steps that we expect to be within budget\n        // if it ends up lower than numSteps and also 1, we will run a single delta time physics step\n        // if we did not do this, and had a fixed step count limit,\n        // we would have an unnecessarily low render framerate if we were only physics bottlenecked,\n        // and we would be unnecessarily invoking true delta time if we were only render bottlenecked\n\n        // get physics timing stats\n        float budgetMeasurement = std::max(mBudget.get(), mAsyncBudget.get());\n        // time spent per step in terms of the intended physics framerate\n        budgetMeasurement /= mDefaultPhysicsDt;\n        // ensure sane minimum value\n        budgetMeasurement = std::max(0.00001f, budgetMeasurement);\n        // we're spending almost or more than realtime per physics frame; limit to a single step\n        if (budgetMeasurement > 0.95)\n            maxAllowedSteps = 1;\n        // physics is fairly cheap; limit based on expense\n        if (budgetMeasurement < 0.5)\n            maxAllowedSteps = std::ceil(1.0/budgetMeasurement);\n        // limit to a reasonable amount\n        maxAllowedSteps = std::min(10, maxAllowedSteps);\n\n        // fall back to delta time for this frame if fixed timestep physics would fall behind\n        float actualDelta = mDefaultPhysicsDt;\n        if (numSteps > maxAllowedSteps)\n        {\n            numSteps = maxAllowedSteps;\n            // ensure that we do not simulate a frame ahead when doing delta time; this reduces stutter and latency\n            // this causes interpolation to 100% use the most recent physics result when true delta time is happening\n            // and we deliberately simulate up to exactly the timestamp that we want to render\n            actualDelta = timeAccum/float(numSteps+1);\n            // actually: if this results in a per-step delta less than the target physics steptime, clamp it\n            // this might reintroduce some stutter, but only comes into play in obscure cases\n            // (because numSteps is originally based on mDefaultPhysicsDt, this won't cause us to overrun)\n            actualDelta = std::max(actualDelta, mDefaultPhysicsDt);\n        }\n\n        return std::make_tuple(numSteps, actualDelta);\n    }\n\n    const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::moveActors(float & timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)\n    {\n        waitForWorkers();\n\n        // This function run in the main thread.\n        // While the mSimulationMutex is held, background physics threads can't run.\n        std::unique_lock lock(mSimulationMutex);\n\n        double timeStart = mTimer->tick();\n\n        mMovedActors.clear();\n\n        // start by finishing previous background computation\n        if (mNumThreads != 0)\n        {\n            for (auto& data : mActorsFrameData)\n            {\n                const auto actorActive = [&data](const auto& newFrameData) -> bool\n                {\n                    const auto actor = data.mActor.lock();\n                    return actor && actor->getPtr() == newFrameData.mActorRaw->getPtr();\n                };\n                // Only return actors that are still part of the scene\n                if (std::any_of(actorsData.begin(), actorsData.end(), actorActive))\n                {\n                    updateMechanics(data);\n\n                    // these variables are accessed directly from the main thread, update them here to prevent accessing \"too new\" values\n                    if (mAdvanceSimulation)\n                        data.mActorRaw->setStandingOnPtr(data.mStandingOn);\n                    data.mActorRaw->setSimulationPosition(interpolateMovements(data, mTimeAccum, mPhysicsDt));\n                    mMovedActors.emplace_back(data.mActorRaw->getPtr());\n                }\n            }\n            if(mAdvanceSimulation)\n                mAsyncBudget.update(mTimer->delta_s(mAsyncStartTime, mTimeEnd), mPrevStepCount, mBudgetCursor);\n            updateStats(frameStart, frameNumber, stats);\n        }\n\n        auto [numSteps, newDelta] = calculateStepConfig(timeAccum);\n        timeAccum -= numSteps*newDelta;\n\n        // init\n        for (auto& data : actorsData)\n            data.updatePosition(mCollisionWorld);\n        mPrevStepCount = numSteps;\n        mRemainingSteps = numSteps;\n        mTimeAccum = timeAccum;\n        mPhysicsDt = newDelta;\n        mActorsFrameData = std::move(actorsData);\n        mAdvanceSimulation = (mRemainingSteps != 0);\n        ++mFrameCounter;\n        mNumJobs = mActorsFrameData.size();\n        mNextLOS.store(0, std::memory_order_relaxed);\n        mNextJob.store(0, std::memory_order_release);\n\n        if (mAdvanceSimulation)\n            mWorldFrameData = std::make_unique<WorldFrameData>();\n\n        if (mAdvanceSimulation)\n            mBudgetCursor += 1;\n\n        if (mNumThreads == 0)\n        {\n            syncComputation();\n            if(mAdvanceSimulation)\n                mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), numSteps, mBudgetCursor);\n            return mMovedActors;\n        }\n\n        mAsyncStartTime = mTimer->tick();\n        mHasJob.notify_all();\n        lock.unlock();\n        if (mAdvanceSimulation)\n            mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), 1, mBudgetCursor);\n        return mMovedActors;\n    }\n\n    const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)\n    {\n        waitForWorkers();\n        std::unique_lock lock(mSimulationMutex);\n        mBudget.reset(mDefaultPhysicsDt);\n        mAsyncBudget.reset(0.0f);\n        mMovedActors.clear();\n        mActorsFrameData.clear();\n        for (const auto& [_, actor] : actors)\n        {\n            actor->updatePosition();\n            actor->updateCollisionObjectPosition();\n            mMovedActors.emplace_back(actor->getPtr());\n        }\n        return mMovedActors;\n    }\n\n    void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const\n    {\n        MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet);\n        mCollisionWorld->rayTest(rayFromWorld, rayToWorld, resultCallback);\n    }\n\n    void PhysicsTaskScheduler::convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const\n    {\n        MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet);\n        mCollisionWorld->convexSweepTest(castShape, from, to, resultCallback);\n    }\n\n    void PhysicsTaskScheduler::contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback)\n    {\n        std::shared_lock lock(mCollisionWorldMutex);\n        ContactTestWrapper::contactTest(mCollisionWorld, colObj, resultCallback);\n    }\n\n    std::optional<btVector3> PhysicsTaskScheduler::getHitPoint(const btTransform& from, btCollisionObject* target)\n    {\n        MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet);\n        // target the collision object's world origin, this should be the center of the collision object\n        btTransform rayTo;\n        rayTo.setIdentity();\n        rayTo.setOrigin(target->getWorldTransform().getOrigin());\n\n        btCollisionWorld::ClosestRayResultCallback cb(from.getOrigin(), rayTo.getOrigin());\n\n        mCollisionWorld->rayTestSingle(from, rayTo, target, target->getCollisionShape(), target->getWorldTransform(), cb);\n        if (!cb.hasHit())\n            // didn't hit the target. this could happen if point is already inside the collision box\n            return std::nullopt;\n        return {cb.m_hitPointWorld};\n    }\n\n    void PhysicsTaskScheduler::aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback)\n    {\n        std::shared_lock lock(mCollisionWorldMutex);\n        mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback);\n    }\n\n    void PhysicsTaskScheduler::getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max)\n    {\n        std::shared_lock lock(mCollisionWorldMutex);\n        obj->getCollisionShape()->getAabb(obj->getWorldTransform(), min, max);\n    }\n\n    void PhysicsTaskScheduler::setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask)\n    {\n        std::unique_lock lock(mCollisionWorldMutex);\n        collisionObject->getBroadphaseHandle()->m_collisionFilterMask = collisionFilterMask;\n    }\n\n    void PhysicsTaskScheduler::addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask)\n    {\n        std::unique_lock lock(mCollisionWorldMutex);\n        mCollisionWorld->addCollisionObject(collisionObject, collisionFilterGroup, collisionFilterMask);\n    }\n\n    void PhysicsTaskScheduler::removeCollisionObject(btCollisionObject* collisionObject)\n    {\n        std::unique_lock lock(mCollisionWorldMutex);\n        mCollisionWorld->removeCollisionObject(collisionObject);\n    }\n\n    void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate)\n    {\n        if (!mDeferAabbUpdate || immediate)\n        {\n            updatePtrAabb(ptr);\n        }\n        else\n        {\n            std::unique_lock lock(mUpdateAabbMutex);\n            mUpdateAabb.insert(std::move(ptr));\n        }\n    }\n\n    bool PhysicsTaskScheduler::getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2)\n    {\n        std::unique_lock lock(mLOSCacheMutex);\n\n        auto actorPtr1 = actor1.lock();\n        auto actorPtr2 = actor2.lock();\n        if (!actorPtr1 || !actorPtr2)\n            return false;\n\n        auto req = LOSRequest(actor1, actor2);\n        auto result = std::find(mLOSCache.begin(), mLOSCache.end(), req);\n        if (result == mLOSCache.end())\n        {\n            req.mResult = hasLineOfSight(actorPtr1.get(), actorPtr2.get());\n            if (mLOSCacheExpiry >= 0)\n                mLOSCache.push_back(req);\n            return req.mResult;\n        }\n        result->mAge = 0;\n        return result->mResult;\n    }\n\n    void PhysicsTaskScheduler::refreshLOSCache()\n    {\n        std::shared_lock lock(mLOSCacheMutex);\n        int job = 0;\n        int numLOS = mLOSCache.size();\n        while ((job = mNextLOS.fetch_add(1, std::memory_order_relaxed)) < numLOS)\n        {\n            auto& req = mLOSCache[job];\n            auto actorPtr1 = req.mActors[0].lock();\n            auto actorPtr2 = req.mActors[1].lock();\n\n            if (req.mAge++ > mLOSCacheExpiry || !actorPtr1 || !actorPtr2)\n                req.mStale = true;\n            else\n                req.mResult = hasLineOfSight(actorPtr1.get(), actorPtr2.get());\n        }\n\n    }\n\n    void PhysicsTaskScheduler::updateAabbs()\n    {\n        std::scoped_lock lock(mUpdateAabbMutex);\n        std::for_each(mUpdateAabb.begin(), mUpdateAabb.end(),\n            [this](const std::weak_ptr<PtrHolder>& ptr) { updatePtrAabb(ptr); });\n        mUpdateAabb.clear();\n    }\n\n    void PhysicsTaskScheduler::updatePtrAabb(const std::weak_ptr<PtrHolder>& ptr)\n    {\n        if (const auto p = ptr.lock())\n        {\n            std::scoped_lock lock(mCollisionWorldMutex);\n            if (const auto actor = std::dynamic_pointer_cast<Actor>(p))\n            {\n                actor->updateCollisionObjectPosition();\n                mCollisionWorld->updateSingleAabb(actor->getCollisionObject());\n            }\n            else if (const auto object = std::dynamic_pointer_cast<Object>(p))\n            {\n                object->commitPositionChange();\n                mCollisionWorld->updateSingleAabb(object->getCollisionObject());\n            }\n            else if (const auto projectile = std::dynamic_pointer_cast<Projectile>(p))\n            {\n                projectile->commitPositionChange();\n                mCollisionWorld->updateSingleAabb(projectile->getCollisionObject());\n            }\n        };\n    }\n\n    void PhysicsTaskScheduler::worker()\n    {\n        std::size_t lastFrame = 0;\n        std::shared_lock lock(mSimulationMutex);\n        while (!mQuit)\n        {\n            if (mRemainingSteps == 0 && lastFrame == mFrameCounter)\n                mHasJob.wait(lock, [&] { return mQuit || lastFrame != mFrameCounter; });\n            lastFrame = mFrameCounter;\n\n            mPreStepBarrier->wait([this] { afterPreStep(); });\n\n            int job = 0;\n            while (mRemainingSteps && (job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)\n            {\n                if(const auto actor = mActorsFrameData[job].mActor.lock())\n                {\n                    MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet);\n                    MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld, *mWorldFrameData);\n                }\n            }\n\n            mPostStepBarrier->wait([this] { afterPostStep(); });\n\n            if (!mRemainingSteps)\n            {\n                while ((job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)\n                {\n                    if(const auto actor = mActorsFrameData[job].mActor.lock())\n                    {\n                        auto& actorData = mActorsFrameData[job];\n                        handleFall(actorData, mAdvanceSimulation);\n                    }\n                }\n\n                if (mLOSCacheExpiry >= 0)\n                    refreshLOSCache();\n                mPostSimBarrier->wait([this] { afterPostSim(); });\n            }\n        }\n    }\n\n    void PhysicsTaskScheduler::updateActorsPositions()\n    {\n        for (auto& actorData : mActorsFrameData)\n        {\n            if(const auto actor = actorData.mActor.lock())\n            {\n                if (actor->setPosition(actorData.mPosition))\n                {\n                    std::scoped_lock lock(mCollisionWorldMutex);\n                    actorData.mPosition = actor->getPosition(); // account for potential position change made by script\n                    actor->updateCollisionObjectPosition();\n                    mCollisionWorld->updateSingleAabb(actor->getCollisionObject());\n                }\n            }\n        }\n    }\n\n    bool PhysicsTaskScheduler::hasLineOfSight(const Actor* actor1, const Actor* actor2)\n    {\n        btVector3 pos1  = Misc::Convert::toBullet(actor1->getCollisionObjectPosition() + osg::Vec3f(0,0,actor1->getHalfExtents().z() * 0.9)); // eye level\n        btVector3 pos2  = Misc::Convert::toBullet(actor2->getCollisionObjectPosition() + osg::Vec3f(0,0,actor2->getHalfExtents().z() * 0.9));\n\n        btCollisionWorld::ClosestRayResultCallback resultCallback(pos1, pos2);\n        resultCallback.m_collisionFilterGroup = 0xFF;\n        resultCallback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door;\n\n        MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet);\n        mCollisionWorld->rayTest(pos1, pos2, resultCallback);\n\n        return !resultCallback.hasHit();\n    }\n\n    void PhysicsTaskScheduler::syncComputation()\n    {\n        while (mRemainingSteps--)\n        {\n            for (auto& actorData : mActorsFrameData)\n            {\n                MovementSolver::unstuck(actorData, mCollisionWorld);\n                MovementSolver::move(actorData, mPhysicsDt, mCollisionWorld, *mWorldFrameData);\n            }\n\n            updateActorsPositions();\n        }\n\n        for (auto& actorData : mActorsFrameData)\n        {\n            handleFall(actorData, mAdvanceSimulation);\n            actorData.mActorRaw->setSimulationPosition(interpolateMovements(actorData, mTimeAccum, mPhysicsDt));\n            updateMechanics(actorData);\n            mMovedActors.emplace_back(actorData.mActorRaw->getPtr());\n            if (mAdvanceSimulation)\n                actorData.mActorRaw->setStandingOnPtr(actorData.mStandingOn);\n        }\n    }\n\n    void PhysicsTaskScheduler::updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)\n    {\n        if (!stats.collectStats(\"engine\"))\n            return;\n        if (mFrameNumber == frameNumber - 1)\n        {\n            stats.setAttribute(mFrameNumber, \"physicsworker_time_begin\", mTimer->delta_s(mFrameStart, mTimeBegin));\n            stats.setAttribute(mFrameNumber, \"physicsworker_time_taken\", mTimer->delta_s(mTimeBegin, mTimeEnd));\n            stats.setAttribute(mFrameNumber, \"physicsworker_time_end\", mTimer->delta_s(mFrameStart, mTimeEnd));\n        }\n        mFrameStart = frameStart;\n        mTimeBegin = mTimer->tick();\n        mFrameNumber = frameNumber;\n    }\n\n    void PhysicsTaskScheduler::debugDraw()\n    {\n        std::shared_lock lock(mCollisionWorldMutex);\n        mDebugDrawer->step();\n    }\n\n    void PhysicsTaskScheduler::afterPreStep()\n    {\n        if (mDeferAabbUpdate)\n            updateAabbs();\n        if (!mRemainingSteps)\n            return;\n        for (auto& data : mActorsFrameData)\n            if (const auto actor = data.mActor.lock())\n            {\n                std::unique_lock lock(mCollisionWorldMutex);\n                MovementSolver::unstuck(data, mCollisionWorld);\n            }\n    }\n\n    void PhysicsTaskScheduler::afterPostStep()\n    {\n        if (mRemainingSteps)\n        {\n            --mRemainingSteps;\n            updateActorsPositions();\n        }\n        mNextJob.store(0, std::memory_order_release);\n    }\n\n    void PhysicsTaskScheduler::afterPostSim()\n    {\n        if (mLOSCacheExpiry >= 0)\n        {\n            std::unique_lock lock(mLOSCacheMutex);\n            mLOSCache.erase(\n                    std::remove_if(mLOSCache.begin(), mLOSCache.end(),\n                        [](const LOSRequest& req) { return req.mStale; }),\n                    mLOSCache.end());\n        }\n        mTimeEnd = mTimer->tick();\n        std::unique_lock lock(mWorkersDoneMutex);\n        ++mWorkersFrameCounter;\n        mWorkersDone.notify_all();\n    }\n\n    // Attempt to acquire unique lock on mSimulationMutex while not all worker\n    // threads are holding shared lock but will have to may lead to a deadlock because\n    // C++ standard does not guarantee priority for exclusive and shared locks\n    // for std::shared_mutex. For example microsoft STL implementation points out\n    // for the absence of such priority:\n    // https://docs.microsoft.com/en-us/windows/win32/sync/slim-reader-writer--srw--locks\n    void PhysicsTaskScheduler::waitForWorkers()\n    {\n        if (mNumThreads == 0)\n            return;\n        std::unique_lock lock(mWorkersDoneMutex);\n        if (mFrameCounter != mWorkersFrameCounter)\n            mWorkersDone.wait(lock);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/mtphysics.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_MTPHYSICS_H\n#define OPENMW_MWPHYSICS_MTPHYSICS_H\n\n#include <atomic>\n#include <condition_variable>\n#include <optional>\n#include <shared_mutex>\n#include <thread>\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\n#include <osg/Timer>\n\n#include \"physicssystem.hpp\"\n#include \"ptrholder.hpp\"\n#include \"components/misc/budgetmeasurement.hpp\"\n\nnamespace Misc\n{\n    class Barrier;\n}\n\nnamespace MWRender\n{\n    class DebugDrawer;\n}\n\nnamespace MWPhysics\n{\n    class PhysicsTaskScheduler\n    {\n        public:\n            PhysicsTaskScheduler(float physicsDt, btCollisionWorld* collisionWorld, MWRender::DebugDrawer* debugDrawer);\n            ~PhysicsTaskScheduler();\n\n            /// @brief move actors taking into account desired movements and collisions\n            /// @param numSteps how much simulation step to run\n            /// @param timeAccum accumulated time from previous run to interpolate movements\n            /// @param actorsData per actor data needed to compute new positions\n            /// @return new position of each actor\n            const std::vector<MWWorld::Ptr>& moveActors(float & timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);\n\n            const std::vector<MWWorld::Ptr>& resetSimulation(const ActorMap& actors);\n\n            // Thread safe wrappers\n            void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const;\n            void convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const;\n            void contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback);\n            std::optional<btVector3> getHitPoint(const btTransform& from, btCollisionObject* target);\n            void aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback);\n            void getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max);\n            void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask);\n            void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask);\n            void removeCollisionObject(btCollisionObject* collisionObject);\n            void updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate=false);\n            bool getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2);\n            void debugDraw();\n\n        private:\n            void syncComputation();\n            void worker();\n            void updateActorsPositions();\n            bool hasLineOfSight(const Actor* actor1, const Actor* actor2);\n            void refreshLOSCache();\n            void updateAabbs();\n            void updatePtrAabb(const std::weak_ptr<PtrHolder>& ptr);\n            void updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);\n            std::tuple<int, float> calculateStepConfig(float timeAccum) const;\n            void afterPreStep();\n            void afterPostStep();\n            void afterPostSim();\n            void waitForWorkers();\n\n            std::unique_ptr<WorldFrameData> mWorldFrameData;\n            std::vector<ActorFrameData> mActorsFrameData;\n            std::vector<MWWorld::Ptr> mMovedActors;\n            float mDefaultPhysicsDt;\n        /*\n            Start of tes3mp change (major)\n\n            Turn mPhysicsDt into a public variable so it can be set from elsewhere\n        */\n        public:\n            float mPhysicsDt;\n        private:\n        /*\n            End of tes3mp change (major)\n        */\n            float mTimeAccum;\n            btCollisionWorld* mCollisionWorld;\n            MWRender::DebugDrawer* mDebugDrawer;\n            std::vector<LOSRequest> mLOSCache;\n            std::set<std::weak_ptr<PtrHolder>, std::owner_less<std::weak_ptr<PtrHolder>>> mUpdateAabb;\n\n            // TODO: use std::experimental::flex_barrier or std::barrier once it becomes a thing\n            std::unique_ptr<Misc::Barrier> mPreStepBarrier;\n            std::unique_ptr<Misc::Barrier> mPostStepBarrier;\n            std::unique_ptr<Misc::Barrier> mPostSimBarrier;\n\n            int mNumThreads;\n            int mNumJobs;\n            int mRemainingSteps;\n            int mLOSCacheExpiry;\n            bool mDeferAabbUpdate;\n            std::size_t mFrameCounter;\n            bool mAdvanceSimulation;\n            bool mThreadSafeBullet;\n            bool mQuit;\n            std::atomic<int> mNextJob;\n            std::atomic<int> mNextLOS;\n            std::vector<std::thread> mThreads;\n\n            std::size_t mWorkersFrameCounter = 0;\n            std::condition_variable mWorkersDone;\n            std::mutex mWorkersDoneMutex;\n\n            mutable std::shared_mutex mSimulationMutex;\n            mutable std::shared_mutex mCollisionWorldMutex;\n            mutable std::shared_mutex mLOSCacheMutex;\n            mutable std::mutex mUpdateAabbMutex;\n            std::condition_variable_any mHasJob;\n\n            unsigned int mFrameNumber;\n            const osg::Timer* mTimer;\n\n            int mPrevStepCount;\n            Misc::BudgetMeasurement mBudget;\n            Misc::BudgetMeasurement mAsyncBudget;\n            unsigned int mBudgetCursor;\n            osg::Timer_t mAsyncStartTime;\n            osg::Timer_t mTimeBegin;\n            osg::Timer_t mTimeEnd;\n            osg::Timer_t mFrameStart;\n    };\n\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/object.cpp",
    "content": "#include \"object.hpp\"\n#include \"mtphysics.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/nifosg/particle.hpp>\n#include <components/resource/bulletshape.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/misc/convert.hpp>\n\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n\n#include <LinearMath/btTransform.h>\n\nnamespace MWPhysics\n{\n    Object::Object(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance, int collisionType, PhysicsTaskScheduler* scheduler)\n        : mShapeInstance(shapeInstance)\n        , mSolid(true)\n        , mTaskScheduler(scheduler)\n    {\n        mPtr = ptr;\n\n        mCollisionObject = std::make_unique<btCollisionObject>();\n        mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape());\n\n        mCollisionObject->setUserPointer(this);\n\n        setScale(ptr.getCellRef().getScale());\n        setRotation(ptr.getRefData().getBaseNode()->getAttitude());\n        updatePosition();\n        commitPositionChange();\n\n        mTaskScheduler->addCollisionObject(mCollisionObject.get(), collisionType, CollisionType_Actor|CollisionType_HeightMap|CollisionType_Projectile);\n    }\n\n    Object::~Object()\n    {\n        mTaskScheduler->removeCollisionObject(mCollisionObject.get());\n    }\n\n    const Resource::BulletShapeInstance* Object::getShapeInstance() const\n    {\n        return mShapeInstance.get();\n    }\n\n    void Object::setScale(float scale)\n    {\n        std::unique_lock<std::mutex> lock(mPositionMutex);\n        mScale = { scale,scale,scale };\n        mScaleUpdatePending = true;\n    }\n\n    void Object::setRotation(const osg::Quat& quat)\n    {\n        std::unique_lock<std::mutex> lock(mPositionMutex);\n        mRotation = quat;\n        mTransformUpdatePending = true;\n    }\n\n    void Object::updatePosition()\n    {\n        std::unique_lock<std::mutex> lock(mPositionMutex);\n        mPosition = mPtr.getRefData().getPosition().asVec3();\n        mTransformUpdatePending = true;\n    }\n\n    void Object::commitPositionChange()\n    {\n        std::unique_lock<std::mutex> lock(mPositionMutex);\n        if (mScaleUpdatePending)\n        {\n            mShapeInstance->setLocalScaling(mScale);\n            mScaleUpdatePending = false;\n        }\n        if (mTransformUpdatePending)\n        {\n            btTransform trans;\n            trans.setOrigin(Misc::Convert::toBullet(mPosition));\n            trans.setRotation(Misc::Convert::toBullet(mRotation));\n            mCollisionObject->setWorldTransform(trans);\n            mTransformUpdatePending = false;\n        }\n    }\n\n    btCollisionObject* Object::getCollisionObject()\n    {\n        return mCollisionObject.get();\n    }\n\n    const btCollisionObject* Object::getCollisionObject() const\n    {\n        return mCollisionObject.get();\n    }\n\n    btTransform Object::getTransform() const\n    {\n        std::unique_lock<std::mutex> lock(mPositionMutex);\n        btTransform trans;\n        trans.setOrigin(Misc::Convert::toBullet(mPosition));\n        trans.setRotation(Misc::Convert::toBullet(mRotation));\n        return trans;\n    }\n\n    bool Object::isSolid() const\n    {\n        return mSolid;\n    }\n\n    void Object::setSolid(bool solid)\n    {\n        mSolid = solid;\n    }\n\n    bool Object::isAnimated() const\n    {\n        return !mShapeInstance->mAnimatedShapes.empty();\n    }\n\n    bool Object::animateCollisionShapes()\n    {\n        if (mShapeInstance->mAnimatedShapes.empty())\n            return false;\n\n        assert (mShapeInstance->getCollisionShape()->isCompound());\n\n        btCompoundShape* compound = static_cast<btCompoundShape*>(mShapeInstance->getCollisionShape());\n        for (const auto& [recIndex, shapeIndex] : mShapeInstance->mAnimatedShapes)\n        {\n            auto nodePathFound = mRecIndexToNodePath.find(recIndex);\n            if (nodePathFound == mRecIndexToNodePath.end())\n            {\n                NifOsg::FindGroupByRecIndex visitor(recIndex);\n                mPtr.getRefData().getBaseNode()->accept(visitor);\n                if (!visitor.mFound)\n                {\n                    Log(Debug::Warning) << \"Warning: animateCollisionShapes can't find node \" << recIndex << \" for \" << mPtr.getCellRef().getRefId();\n\n                    // Remove nonexistent nodes from animated shapes map and early out\n                    mShapeInstance->mAnimatedShapes.erase(recIndex);\n                    return false;\n                }\n                osg::NodePath nodePath = visitor.mFoundPath;\n                nodePath.erase(nodePath.begin());\n                nodePathFound = mRecIndexToNodePath.emplace(recIndex, nodePath).first;\n            }\n\n            osg::NodePath& nodePath = nodePathFound->second;\n            osg::Matrixf matrix = osg::computeLocalToWorld(nodePath);\n            matrix.orthoNormalize(matrix);\n\n            btTransform transform;\n            transform.setOrigin(Misc::Convert::toBullet(matrix.getTrans()) * compound->getLocalScaling());\n            for (int i=0; i<3; ++i)\n                for (int j=0; j<3; ++j)\n                    transform.getBasis()[i][j] = matrix(j,i); // NB column/row major difference\n\n            // Note: we can not apply scaling here for now since we treat scaled shapes\n            // as new shapes (btScaledBvhTriangleMeshShape) with 1.0 scale for now\n            if (!(transform == compound->getChildTransform(shapeIndex)))\n                compound->updateChildTransform(shapeIndex, transform);\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/object.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_OBJECT_H\n#define OPENMW_MWPHYSICS_OBJECT_H\n\n#include \"ptrholder.hpp\"\n\n#include <LinearMath/btTransform.h>\n#include <osg/Node>\n\n#include <map>\n#include <memory>\n#include <mutex>\n\nnamespace Resource\n{\n    class BulletShapeInstance;\n}\n\nclass btCollisionObject;\nclass btVector3;\n\nnamespace MWPhysics\n{\n    class PhysicsTaskScheduler;\n\n    class Object final : public PtrHolder\n    {\n    public:\n        Object(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance, int collisionType, PhysicsTaskScheduler* scheduler);\n        ~Object() override;\n\n        const Resource::BulletShapeInstance* getShapeInstance() const;\n        void setScale(float scale);\n        void setRotation(const osg::Quat& quat);\n        void updatePosition();\n        void commitPositionChange();\n        btCollisionObject* getCollisionObject();\n        const btCollisionObject* getCollisionObject() const;\n        btTransform getTransform() const;\n        /// Return solid flag. Not used by the object itself, true by default.\n        bool isSolid() const;\n        void setSolid(bool solid);\n        bool isAnimated() const;\n        /// @brief update object shape\n        /// @return true if shape changed\n        bool animateCollisionShapes();\n\n    private:\n        std::unique_ptr<btCollisionObject> mCollisionObject;\n        osg::ref_ptr<Resource::BulletShapeInstance> mShapeInstance;\n        std::map<int, osg::NodePath> mRecIndexToNodePath;\n        bool mSolid;\n        btVector3 mScale;\n        osg::Vec3f mPosition;\n        osg::Quat mRotation;\n        bool mScaleUpdatePending;\n        bool mTransformUpdatePending;\n        mutable std::mutex mPositionMutex;\n        PhysicsTaskScheduler* mTaskScheduler;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/physicssystem.cpp",
    "content": "#include \"physicssystem.hpp\"\n\n#include <LinearMath/btIDebugDraw.h>\n#include <LinearMath/btVector3.h>\n#include <memory>\n#include <osg/Group>\n#include <osg/Stats>\n#include <osg/Timer>\n\n#include <BulletCollision/CollisionShapes/btConeShape.h>\n#include <BulletCollision/CollisionShapes/btSphereShape.h>\n#include <BulletCollision/CollisionShapes/btStaticPlaneShape.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>\n#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>\n\n#include <LinearMath/btQuickprof.h>\n\n#include <components/nifbullet/bulletnifloader.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/bulletshapemanager.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/esm/loadgmst.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/sceneutil/unrefqueue.hpp>\n#include <components/misc/convert.hpp>\n\n#include <components/nifosg/particle.hpp> // FindRecIndexVisitor\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"../mwrender/bulletdebugdraw.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"collisiontype.hpp\"\n#include \"actor.hpp\"\n\n#include \"projectile.hpp\"\n#include \"trace.h\"\n#include \"object.hpp\"\n#include \"heightfield.hpp\"\n#include \"hasspherecollisioncallback.hpp\"\n#include \"deepestnotmecontacttestresultcallback.hpp\"\n#include \"closestnotmerayresultcallback.hpp\"\n#include \"contacttestresultcallback.hpp\"\n#include \"projectileconvexcallback.hpp\"\n#include \"movementsolver.hpp\"\n#include \"mtphysics.hpp\"\n\nnamespace\n{\n    bool canMoveToWaterSurface(const MWPhysics::Actor* physicActor, const float waterlevel, btCollisionWorld* world)\n    {\n        if (!physicActor)\n            return false;\n        const float halfZ = physicActor->getHalfExtents().z();\n        const osg::Vec3f actorPosition = physicActor->getPosition();\n        const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);\n        const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);\n        MWPhysics::ActorTracer tracer;\n        tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, world);\n        return (tracer.mFraction >= 1.0f);\n    }\n}\n\nnamespace MWPhysics\n{\n    PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)\n        : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager()))\n        , mResourceSystem(resourceSystem)\n        , mDebugDrawEnabled(false)\n        , mTimeAccum(0.0f)\n        , mProjectileId(0)\n        , mWaterHeight(0)\n        , mWaterEnabled(false)\n        , mParentNode(parentNode)\n        , mPhysicsDt(1.f / 60.f)\n    {\n        mResourceSystem->addResourceManager(mShapeManager.get());\n\n        mCollisionConfiguration = std::make_unique<btDefaultCollisionConfiguration>();\n        mDispatcher = std::make_unique<btCollisionDispatcher>(mCollisionConfiguration.get());\n        mBroadphase = std::make_unique<btDbvtBroadphase>();\n\n        mCollisionWorld = std::make_unique<btCollisionWorld>(mDispatcher.get(), mBroadphase.get(), mCollisionConfiguration.get());\n\n        // Don't update AABBs of all objects every frame. Most objects in MW are static, so we don't need this.\n        // Should a \"static\" object ever be moved, we have to update its AABB manually using DynamicsWorld::updateSingleAabb.\n        mCollisionWorld->setForceUpdateAllAabbs(false);\n\n        // Check if a user decided to override a physics system FPS\n        const char* env = getenv(\"OPENMW_PHYSICS_FPS\");\n        if (env)\n        {\n            float physFramerate = std::atof(env);\n            if (physFramerate > 0)\n            {\n                mPhysicsDt = 1.f / physFramerate;\n                Log(Debug::Warning) << \"Warning: using custom physics framerate (\" << physFramerate << \" FPS).\";\n            }\n        }\n\n        mDebugDrawer = std::make_unique<MWRender::DebugDrawer>(mParentNode, mCollisionWorld.get(), mDebugDrawEnabled);\n        mTaskScheduler = std::make_unique<PhysicsTaskScheduler>(mPhysicsDt, mCollisionWorld.get(), mDebugDrawer.get());\n    }\n\n    PhysicsSystem::~PhysicsSystem()\n    {\n        mResourceSystem->removeResourceManager(mShapeManager.get());\n\n        if (mWaterCollisionObject)\n            mTaskScheduler->removeCollisionObject(mWaterCollisionObject.get());\n\n        mHeightFields.clear();\n        mObjects.clear();\n        mActors.clear();\n        mProjectiles.clear();\n    }\n\n    void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)\n    {\n        mUnrefQueue = unrefQueue;\n    }\n\n    Resource::BulletShapeManager *PhysicsSystem::getShapeManager()\n    {\n        return mShapeManager.get();\n    }\n\n    bool PhysicsSystem::toggleDebugRendering()\n    {\n        mDebugDrawEnabled = !mDebugDrawEnabled;\n\n        mCollisionWorld->setDebugDrawer(mDebugDrawEnabled ? mDebugDrawer.get() : nullptr);\n        mDebugDrawer->setDebugMode(mDebugDrawEnabled);\n        return mDebugDrawEnabled;\n    }\n\n    void PhysicsSystem::markAsNonSolid(const MWWorld::ConstPtr &ptr)\n    {\n        ObjectMap::iterator found = mObjects.find(ptr);\n        if (found == mObjects.end())\n            return;\n\n        found->second->setSolid(false);\n    }\n\n    bool PhysicsSystem::isOnSolidGround (const MWWorld::Ptr& actor) const\n    {\n        const Actor* physactor = getActor(actor);\n        if (!physactor || !physactor->getOnGround())\n            return false;\n\n        const auto obj = physactor->getStandingOnPtr();\n        if (obj.isEmpty())\n            return true; // assume standing on terrain (which is a non-object, so not collision tracked)\n\n        ObjectMap::const_iterator foundObj = mObjects.find(obj);\n        if (foundObj == mObjects.end())\n            return false;\n\n        if (!foundObj->second->isSolid())\n            return false;\n\n        return true;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set the physics framerate from elsewhere\n    */\n    void PhysicsSystem::setPhysicsFramerate(float physFramerate)\n    {\n        if (physFramerate > 0 && physFramerate < 100)\n        {\n            mPhysicsDt = 1.f / physFramerate;\n            mTaskScheduler->mPhysicsDt = mPhysicsDt;\n\n            std::cerr << \"Warning: physics framerate was overridden (a new value is \" << physFramerate << \").\" << std::endl;\n        }\n        else\n        {\n            std::cerr << \"Warning: attempted to override physics framerate with new value of \" << physFramerate <<\n                \", but it was outside accepted values.\" << std::endl;\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    std::pair<MWWorld::Ptr, osg::Vec3f> PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor,\n                                                                     const osg::Vec3f &origin,\n                                                                     const osg::Quat &orient,\n                                                                     float queryDistance, std::vector<MWWorld::Ptr>& targets)\n    {\n        // First of all, try to hit where you aim to\n        int hitmask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor;\n        RayCastingResult result = castRay(origin, origin + (orient * osg::Vec3f(0.0f, queryDistance, 0.0f)), actor, targets, hitmask, CollisionType_Actor);\n\n        if (result.mHit)\n        {\n            reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));\n            return std::make_pair(result.mHitObject, result.mHitPos);\n        }\n\n        // Use cone shape as fallback\n        const MWWorld::Store<ESM::GameSetting> &store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n        btConeShape shape (osg::DegreesToRadians(store.find(\"fCombatAngleXY\")->mValue.getFloat()/2.0f), queryDistance);\n        shape.setLocalScaling(btVector3(1, 1, osg::DegreesToRadians(store.find(\"fCombatAngleZ\")->mValue.getFloat()/2.0f) /\n                                              shape.getRadius()));\n\n        // The shape origin is its center, so we have to move it forward by half the length. The\n        // real origin will be provided to getFilteredContact to find the closest.\n        osg::Vec3f center = origin + (orient * osg::Vec3f(0.0f, queryDistance*0.5f, 0.0f));\n\n        btCollisionObject object;\n        object.setCollisionShape(&shape);\n        object.setWorldTransform(btTransform(Misc::Convert::toBullet(orient), Misc::Convert::toBullet(center)));\n\n        const btCollisionObject* me = nullptr;\n        std::vector<const btCollisionObject*> targetCollisionObjects;\n\n        const Actor* physactor = getActor(actor);\n        if (physactor)\n            me = physactor->getCollisionObject();\n\n        if (!targets.empty())\n        {\n            for (MWWorld::Ptr& target : targets)\n            {\n                const Actor* targetActor = getActor(target);\n                if (targetActor)\n                    targetCollisionObjects.push_back(targetActor->getCollisionObject());\n            }\n        }\n\n        DeepestNotMeContactTestResultCallback resultCallback(me, targetCollisionObjects, Misc::Convert::toBullet(origin));\n        resultCallback.m_collisionFilterGroup = CollisionType_Actor;\n        resultCallback.m_collisionFilterMask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor;\n        mTaskScheduler->contactTest(&object, resultCallback);\n\n        if (resultCallback.mObject)\n        {\n            PtrHolder* holder = static_cast<PtrHolder*>(resultCallback.mObject->getUserPointer());\n            if (holder)\n            {\n                reportCollision(resultCallback.mContactPoint, resultCallback.mContactNormal);\n                return std::make_pair(holder->getPtr(), Misc::Convert::toOsg(resultCallback.mContactPoint));\n            }\n        }\n        return std::make_pair(MWWorld::Ptr(), osg::Vec3f());\n    }\n\n    float PhysicsSystem::getHitDistance(const osg::Vec3f &point, const MWWorld::ConstPtr &target) const\n    {\n        btCollisionObject* targetCollisionObj = nullptr;\n        const Actor* actor = getActor(target);\n        if (actor)\n            targetCollisionObj = actor->getCollisionObject();\n        if (!targetCollisionObj)\n            return 0.f;\n\n        btTransform rayFrom;\n        rayFrom.setIdentity();\n        rayFrom.setOrigin(Misc::Convert::toBullet(point));\n\n        auto hitpoint = mTaskScheduler->getHitPoint(rayFrom, targetCollisionObj);\n        if (hitpoint)\n            return (point - Misc::Convert::toOsg(*hitpoint)).length();\n\n        // didn't hit the target. this could happen if point is already inside the collision box\n        return 0.f;\n    }\n\n    RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr> targets, int mask, int group) const\n    {\n        if (from == to)\n        {\n            RayCastingResult result;\n            result.mHit = false;\n            return result;\n        }\n        btVector3 btFrom = Misc::Convert::toBullet(from);\n        btVector3 btTo = Misc::Convert::toBullet(to);\n\n        const btCollisionObject* me = nullptr;\n        std::vector<const btCollisionObject*> targetCollisionObjects;\n\n        if (!ignore.isEmpty())\n        {\n            const Actor* actor = getActor(ignore);\n            if (actor)\n                me = actor->getCollisionObject();\n            else\n            {\n                const Object* object = getObject(ignore);\n                if (object)\n                    me = object->getCollisionObject();\n            }\n        }\n\n        if (!targets.empty())\n        {\n            for (MWWorld::Ptr& target : targets)\n            {\n                const Actor* actor = getActor(target);\n                if (actor)\n                    targetCollisionObjects.push_back(actor->getCollisionObject());\n            }\n        }\n\n        ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo);\n        resultCallback.m_collisionFilterGroup = group;\n        resultCallback.m_collisionFilterMask = mask;\n\n        mTaskScheduler->rayTest(btFrom, btTo, resultCallback);\n\n        RayCastingResult result;\n        result.mHit = resultCallback.hasHit();\n        if (resultCallback.hasHit())\n        {\n            result.mHitPos = Misc::Convert::toOsg(resultCallback.m_hitPointWorld);\n            result.mHitNormal = Misc::Convert::toOsg(resultCallback.m_hitNormalWorld);\n            if (PtrHolder* ptrHolder = static_cast<PtrHolder*>(resultCallback.m_collisionObject->getUserPointer()))\n                result.mHitObject = ptrHolder->getPtr();\n        }\n        return result;\n    }\n\n    RayCastingResult PhysicsSystem::castSphere(const osg::Vec3f &from, const osg::Vec3f &to, float radius) const\n    {\n        btCollisionWorld::ClosestConvexResultCallback callback(Misc::Convert::toBullet(from), Misc::Convert::toBullet(to));\n        callback.m_collisionFilterGroup = 0xff;\n        callback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door;\n\n        btSphereShape shape(radius);\n        const btQuaternion btrot = btQuaternion::getIdentity();\n\n        btTransform from_ (btrot, Misc::Convert::toBullet(from));\n        btTransform to_ (btrot, Misc::Convert::toBullet(to));\n\n        mTaskScheduler->convexSweepTest(&shape, from_, to_, callback);\n\n        RayCastingResult result;\n        result.mHit = callback.hasHit();\n        if (result.mHit)\n        {\n            result.mHitPos = Misc::Convert::toOsg(callback.m_hitPointWorld);\n            result.mHitNormal = Misc::Convert::toOsg(callback.m_hitNormalWorld);\n        }\n        return result;\n    }\n\n    bool PhysicsSystem::getLineOfSight(const MWWorld::ConstPtr &actor1, const MWWorld::ConstPtr &actor2) const\n    {\n        const auto getWeakPtr = [&](const MWWorld::ConstPtr &ptr) -> std::weak_ptr<Actor>\n        {\n            const auto found = mActors.find(ptr);\n            if (found != mActors.end())\n                return { found->second };\n            return {};\n        };\n\n        return mTaskScheduler->getLineOfSight(getWeakPtr(actor1), getWeakPtr(actor2));\n    }\n\n    bool PhysicsSystem::isOnGround(const MWWorld::Ptr &actor)\n    {\n        Actor* physactor = getActor(actor);\n        return physactor && physactor->getOnGround();\n    }\n\n    bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)\n    {\n        return ::canMoveToWaterSurface(getActor(actor), waterlevel, mCollisionWorld.get());\n    }\n\n    osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const\n    {\n        const Actor* physactor = getActor(actor);\n        if (physactor)\n            return physactor->getHalfExtents();\n        else\n            return osg::Vec3f();\n    }\n\n    osg::Vec3f PhysicsSystem::getOriginalHalfExtents(const MWWorld::ConstPtr &actor) const\n    {\n        if (const Actor* physactor = getActor(actor))\n            return physactor->getOriginalHalfExtents();\n        else\n            return osg::Vec3f();\n    }\n\n    osg::Vec3f PhysicsSystem::getRenderingHalfExtents(const MWWorld::ConstPtr &actor) const\n    {\n        const Actor* physactor = getActor(actor);\n        if (physactor)\n            return physactor->getRenderingHalfExtents();\n        else\n            return osg::Vec3f();\n    }\n\n    osg::BoundingBox PhysicsSystem::getBoundingBox(const MWWorld::ConstPtr &object) const\n    {\n        const Object * physobject = getObject(object);\n        if (!physobject) return osg::BoundingBox();\n        btVector3 min, max;\n        mTaskScheduler->getAabb(physobject->getCollisionObject(), min, max);\n        return osg::BoundingBox(Misc::Convert::toOsg(min), Misc::Convert::toOsg(max));\n    }\n\n    osg::Vec3f PhysicsSystem::getCollisionObjectPosition(const MWWorld::ConstPtr &actor) const\n    {\n        const Actor* physactor = getActor(actor);\n        if (physactor)\n            return physactor->getCollisionObjectPosition();\n        else\n            return osg::Vec3f();\n    }\n\n    std::vector<ContactPoint> PhysicsSystem::getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const\n    {\n        btCollisionObject* me = nullptr;\n\n        auto found = mObjects.find(ptr);\n        if (found != mObjects.end())\n            me = found->second->getCollisionObject();\n        else\n            return {};\n\n        ContactTestResultCallback resultCallback (me);\n        resultCallback.m_collisionFilterGroup = collisionGroup;\n        resultCallback.m_collisionFilterMask = collisionMask;\n        mTaskScheduler->contactTest(me, resultCallback);\n        return resultCallback.mResult;\n    }\n\n    std::vector<MWWorld::Ptr> PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const\n    {\n        std::vector<MWWorld::Ptr> actors;\n        for (auto& [actor, point, normal] : getCollisionsPoints(ptr, collisionGroup, collisionMask))\n            actors.emplace_back(actor);\n        return actors;\n    }\n\n    osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight)\n    {\n        ActorMap::iterator found = mActors.find(ptr);\n        if (found ==  mActors.end())\n            return ptr.getRefData().getPosition().asVec3();\n        return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight);\n    }\n\n    void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject)\n    {\n        mHeightFields[std::make_pair(x,y)] = osg::ref_ptr<HeightField>(new HeightField(heights, x, y, triSize, sqrtVerts, minH, maxH, holdObject, mTaskScheduler.get()));\n    }\n\n    void PhysicsSystem::removeHeightField (int x, int y)\n    {\n        HeightFieldMap::iterator heightfield = mHeightFields.find(std::make_pair(x,y));\n        if(heightfield != mHeightFields.end())\n            mHeightFields.erase(heightfield);\n    }\n\n    const HeightField* PhysicsSystem::getHeightField(int x, int y) const\n    {\n        const auto heightField = mHeightFields.find(std::make_pair(x, y));\n        if (heightField == mHeightFields.end())\n            return nullptr;\n        return heightField->second.get();\n    }\n\n    void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType)\n    {\n        osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);\n        if (!shapeInstance || !shapeInstance->getCollisionShape())\n            return;\n\n        auto obj = std::make_shared<Object>(ptr, shapeInstance, collisionType, mTaskScheduler.get());\n        mObjects.emplace(ptr, obj);\n\n        if (obj->isAnimated())\n            mAnimatedObjects.insert(obj.get());\n    }\n\n    void PhysicsSystem::remove(const MWWorld::Ptr &ptr)\n    {\n        ObjectMap::iterator found = mObjects.find(ptr);\n        if (found != mObjects.end())\n        {\n            if (mUnrefQueue.get())\n                mUnrefQueue->push(found->second->getShapeInstance());\n\n            mAnimatedObjects.erase(found->second.get());\n\n            mObjects.erase(found);\n        }\n\n        ActorMap::iterator foundActor = mActors.find(ptr);\n        if (foundActor != mActors.end())\n        {\n            mActors.erase(foundActor);\n        }\n    }\n\n    void PhysicsSystem::removeProjectile(const int projectileId)\n    {\n        ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId);\n        if (foundProjectile != mProjectiles.end())\n            mProjectiles.erase(foundProjectile);\n    }\n\n    void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)\n    {\n        ObjectMap::iterator found = mObjects.find(old);\n        if (found != mObjects.end())\n        {\n            auto obj = found->second;\n            obj->updatePtr(updated);\n            mObjects.erase(found);\n            mObjects.emplace(updated, std::move(obj));\n        }\n\n        ActorMap::iterator foundActor = mActors.find(old);\n        if (foundActor != mActors.end())\n        {\n            auto actor = foundActor->second;\n            actor->updatePtr(updated);\n            mActors.erase(foundActor);\n            mActors.emplace(updated, std::move(actor));\n        }\n\n        for (auto& [_, actor] : mActors)\n        {\n            if (actor->getStandingOnPtr() == old)\n                actor->setStandingOnPtr(updated);\n        }\n\n        for (auto& [_, projectile] : mProjectiles)\n        {\n            if (projectile->getCaster() == old)\n                projectile->setCaster(updated);\n        }\n\n    }\n\n    Actor *PhysicsSystem::getActor(const MWWorld::Ptr &ptr)\n    {\n        ActorMap::iterator found = mActors.find(ptr);\n        if (found != mActors.end())\n            return found->second.get();\n        return nullptr;\n    }\n\n    const Actor *PhysicsSystem::getActor(const MWWorld::ConstPtr &ptr) const\n    {\n        ActorMap::const_iterator found = mActors.find(ptr);\n        if (found != mActors.end())\n            return found->second.get();\n        return nullptr;\n    }\n\n    const Object* PhysicsSystem::getObject(const MWWorld::ConstPtr &ptr) const\n    {\n        ObjectMap::const_iterator found = mObjects.find(ptr);\n        if (found != mObjects.end())\n            return found->second.get();\n        return nullptr;\n    }\n\n    Projectile* PhysicsSystem::getProjectile(int projectileId) const\n    {\n        ProjectileMap::const_iterator found = mProjectiles.find(projectileId);\n        if (found != mProjectiles.end())\n            return found->second.get();\n        return nullptr;\n    }\n\n    void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr)\n    {\n        ObjectMap::iterator found = mObjects.find(ptr);\n        if (found != mObjects.end())\n        {\n            float scale = ptr.getCellRef().getScale();\n            found->second->setScale(scale);\n            mTaskScheduler->updateSingleAabb(found->second);\n            return;\n        }\n        ActorMap::iterator foundActor = mActors.find(ptr);\n        if (foundActor != mActors.end())\n        {\n            foundActor->second->updateScale();\n            mTaskScheduler->updateSingleAabb(foundActor->second);\n            return;\n        }\n    }\n\n    void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position) const\n    {\n        const auto foundProjectile = mProjectiles.find(projectileId);\n        assert(foundProjectile != mProjectiles.end());\n        auto* projectile = foundProjectile->second.get();\n\n        btVector3 btFrom = Misc::Convert::toBullet(projectile->getPosition());\n        btVector3 btTo = Misc::Convert::toBullet(position);\n\n        if (btFrom == btTo)\n            return;\n\n        const auto casterPtr = projectile->getCaster();\n        const auto* caster = [this,&casterPtr]() -> const btCollisionObject*\n        {\n            const Actor* actor = getActor(casterPtr);\n            if (actor)\n                return actor->getCollisionObject();\n            const Object* object = getObject(casterPtr);\n            if (object)\n                return object->getCollisionObject();\n            return nullptr;\n        }();\n\n        ProjectileConvexCallback resultCallback(caster, btFrom, btTo, projectile);\n        resultCallback.m_collisionFilterMask = 0xff;\n        resultCallback.m_collisionFilterGroup = CollisionType_Projectile;\n\n        const btQuaternion btrot = btQuaternion::getIdentity();\n        btTransform from_ (btrot, btFrom);\n        btTransform to_ (btrot, btTo);\n\n        mTaskScheduler->convexSweepTest(projectile->getConvexShape(), from_, to_, resultCallback);\n\n        const auto newpos = projectile->isActive() ? position : Misc::Convert::toOsg(projectile->getHitPosition());\n        projectile->setPosition(newpos);\n        mTaskScheduler->updateSingleAabb(foundProjectile->second);\n    }\n\n    void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)\n    {\n        ObjectMap::iterator found = mObjects.find(ptr);\n        if (found != mObjects.end())\n        {\n            found->second->setRotation(ptr.getRefData().getBaseNode()->getAttitude());\n            mTaskScheduler->updateSingleAabb(found->second);\n            return;\n        }\n        ActorMap::iterator foundActor = mActors.find(ptr);\n        if (foundActor != mActors.end())\n        {\n            if (!foundActor->second->isRotationallyInvariant())\n            {\n                foundActor->second->updateRotation();\n                mTaskScheduler->updateSingleAabb(foundActor->second);\n            }\n            return;\n        }\n    }\n\n    void PhysicsSystem::updatePosition(const MWWorld::Ptr &ptr)\n    {\n        ObjectMap::iterator found = mObjects.find(ptr);\n        if (found != mObjects.end())\n        {\n            found->second->updatePosition();\n            mTaskScheduler->updateSingleAabb(found->second);\n            return;\n        }\n        ActorMap::iterator foundActor = mActors.find(ptr);\n        if (foundActor != mActors.end())\n        {\n            foundActor->second->updatePosition();\n            mTaskScheduler->updateSingleAabb(foundActor->second, true);\n            return;\n        }\n    }\n\n    void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh)\n    {\n        osg::ref_ptr<const Resource::BulletShape> shape = mShapeManager->getShape(mesh);\n\n        // Try to get shape from basic model as fallback for creatures\n        if (!ptr.getClass().isNpc() && shape && shape->mCollisionBox.extents.length2() == 0)\n        {\n            const std::string fallbackModel = ptr.getClass().getModel(ptr);\n            if (fallbackModel != mesh)\n            {\n                shape = mShapeManager->getShape(fallbackModel);\n            }\n        }\n\n        if (!shape)\n            return;\n\n        // check if Actor should spawn above water\n        const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects();\n        const bool canWaterWalk = effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() > 0;\n\n        auto actor = std::make_shared<Actor>(ptr, shape, mTaskScheduler.get(), canWaterWalk);\n        \n        // check if Actor is on the ground or in the air\n        traceDown(ptr, ptr.getRefData().getPosition().asVec3(), 10.f);\n\n        mActors.emplace(ptr, std::move(actor));\n    }\n\n    int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius)\n    {\n        osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);\n        assert(shapeInstance);\n        float radius = computeRadius ? shapeInstance->mCollisionBox.extents.length() / 2.f : 1.f;\n\n        mProjectileId++;\n\n        auto projectile = std::make_shared<Projectile>(caster, position, radius, mTaskScheduler.get(), this);\n        mProjectiles.emplace(mProjectileId, std::move(projectile));\n\n        return mProjectileId;\n    }\n\n    void PhysicsSystem::setCaster(int projectileId, const MWWorld::Ptr& caster)\n    {\n        const auto foundProjectile = mProjectiles.find(projectileId);\n        assert(foundProjectile != mProjectiles.end());\n        auto* projectile = foundProjectile->second.get();\n\n        projectile->setCaster(caster);\n    }\n\n    bool PhysicsSystem::toggleCollisionMode()\n    {\n        ActorMap::iterator found = mActors.find(MWMechanics::getPlayer());\n        if (found != mActors.end())\n        {\n            bool cmode = found->second->getCollisionMode();\n            cmode = !cmode;\n            found->second->enableCollisionMode(cmode);\n            // NB: Collision body isn't disabled for vanilla TCL compatibility\n            return cmode;\n        }\n\n        return false;\n    }\n\n    void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity)\n    {\n        ActorMap::iterator found = mActors.find(ptr);\n        if (found != mActors.end())\n            found->second->setVelocity(velocity);\n    }\n\n    void PhysicsSystem::clearQueuedMovement()\n    {\n        for (const auto& [_, actor] : mActors)\n            actor->setVelocity(osg::Vec3f());\n    }\n\n    const std::vector<MWWorld::Ptr>& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)\n    {\n        mTimeAccum += dt;\n\n        if (skipSimulation)\n            return mTaskScheduler->resetSimulation(mActors);\n\n        // modifies mTimeAccum\n        return mTaskScheduler->moveActors(mTimeAccum, prepareFrameData(mTimeAccum >= mPhysicsDt), frameStart, frameNumber, stats);\n    }\n\n    std::vector<ActorFrameData> PhysicsSystem::prepareFrameData(bool willSimulate)\n    {\n        std::vector<ActorFrameData> actorsFrameData;\n        actorsFrameData.reserve(mActors.size());\n        const MWBase::World *world = MWBase::Environment::get().getWorld();\n        for (const auto& [ptr, physicActor] : mActors)\n        {\n            float waterlevel = -std::numeric_limits<float>::max();\n            const MWWorld::CellStore *cell = ptr.getCell();\n            if(cell->getCell()->hasWater())\n                waterlevel = cell->getWaterLevel();\n\n            const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(physicActor->getPtr()).getMagicEffects();\n\n            bool waterCollision = false;\n            if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())\n            {\n                if (physicActor->getCollisionMode() || !world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3())))\n                    waterCollision = true;\n            }\n\n            physicActor->setCanWaterWalk(waterCollision);\n\n            // Slow fall reduces fall speed by a factor of (effect magnitude / 200)\n            const float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f));\n\n            // Ue current value only if we don't advance the simulation. Otherwise we might get a stale value.\n            MWWorld::Ptr standingOn;\n            if (!willSimulate)\n                standingOn = physicActor->getStandingOnPtr();\n\n            actorsFrameData.emplace_back(physicActor, standingOn, waterCollision, slowFall, waterlevel);\n        }\n        return actorsFrameData;\n    }\n\n    void PhysicsSystem::stepSimulation()\n    {\n        for (Object* animatedObject : mAnimatedObjects)\n            if (animatedObject->animateCollisionShapes())\n            {\n                auto obj = mObjects.find(animatedObject->getPtr());\n                assert(obj != mObjects.end());\n                mTaskScheduler->updateSingleAabb(obj->second);\n            }\n\n#ifndef BT_NO_PROFILE\n        CProfileManager::Reset();\n        CProfileManager::Increment_Frame_Counter();\n#endif\n    }\n\n    void PhysicsSystem::updateAnimatedCollisionShape(const MWWorld::Ptr& object)\n    {\n        ObjectMap::iterator found = mObjects.find(object);\n        if (found != mObjects.end())\n            if (found->second->animateCollisionShapes())\n                mTaskScheduler->updateSingleAabb(found->second);\n    }\n\n    void PhysicsSystem::debugDraw()\n    {\n        if (mDebugDrawEnabled)\n            mTaskScheduler->debugDraw();\n    }\n\n    bool PhysicsSystem::isActorStandingOn(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const\n    {\n        const auto physActor = mActors.find(actor);\n        if (physActor != mActors.end())\n            return physActor->second->getStandingOnPtr() == object;\n        return false;\n    }\n\n    void PhysicsSystem::getActorsStandingOn(const MWWorld::ConstPtr &object, std::vector<MWWorld::Ptr> &out) const\n    {\n        for (const auto& [_, actor] : mActors)\n        {\n            if (actor->getStandingOnPtr() == object)\n                out.emplace_back(actor->getPtr());\n        }\n    }\n\n    bool PhysicsSystem::isActorCollidingWith(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const\n    {\n        std::vector<MWWorld::Ptr> collisions = getCollisions(object, CollisionType_World, CollisionType_Actor);\n        return (std::find(collisions.begin(), collisions.end(), actor) != collisions.end());\n    }\n\n    void PhysicsSystem::getActorsCollidingWith(const MWWorld::ConstPtr &object, std::vector<MWWorld::Ptr> &out) const\n    {\n        std::vector<MWWorld::Ptr> collisions = getCollisions(object, CollisionType_World, CollisionType_Actor);\n        out.insert(out.end(), collisions.begin(), collisions.end());\n    }\n\n    void PhysicsSystem::disableWater()\n    {\n        if (mWaterEnabled)\n        {\n            mWaterEnabled = false;\n            updateWater();\n        }\n    }\n\n    void PhysicsSystem::enableWater(float height)\n    {\n        if (!mWaterEnabled || mWaterHeight != height)\n        {\n            mWaterEnabled = true;\n            mWaterHeight = height;\n            updateWater();\n        }\n    }\n\n    void PhysicsSystem::setWaterHeight(float height)\n    {\n        if (mWaterHeight != height)\n        {\n            mWaterHeight = height;\n            updateWater();\n        }\n    }\n\n    void PhysicsSystem::updateWater()\n    {\n        if (mWaterCollisionObject)\n        {\n            mTaskScheduler->removeCollisionObject(mWaterCollisionObject.get());\n        }\n\n        if (!mWaterEnabled)\n        {\n            mWaterCollisionObject.reset();\n            return;\n        }\n\n        mWaterCollisionObject.reset(new btCollisionObject());\n        mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight));\n        mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get());\n        mTaskScheduler->addCollisionObject(mWaterCollisionObject.get(), CollisionType_Water,\n                                                    CollisionType_Actor|CollisionType_Projectile);\n    }\n\n    bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const\n    {\n        btCollisionObject* object = nullptr;\n        const auto it = mActors.find(ignore);\n        if (it != mActors.end())\n            object = it->second->getCollisionObject();\n        const auto bulletPosition = Misc::Convert::toBullet(position);\n        const auto aabbMin = bulletPosition - btVector3(radius, radius, radius);\n        const auto aabbMax = bulletPosition + btVector3(radius, radius, radius);\n        const int mask = MWPhysics::CollisionType_Actor;\n        const int group = 0xff;\n        HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group);\n        mTaskScheduler->aabbTest(aabbMin, aabbMax, callback);\n        return callback.getResult();\n    }\n\n    void PhysicsSystem::reportStats(unsigned int frameNumber, osg::Stats& stats) const\n    {\n        stats.setAttribute(frameNumber, \"Physics Actors\", mActors.size());\n        stats.setAttribute(frameNumber, \"Physics Objects\", mObjects.size());\n        stats.setAttribute(frameNumber, \"Physics HeightFields\", mHeightFields.size());\n    }\n\n    void PhysicsSystem::reportCollision(const btVector3& position, const btVector3& normal)\n    {\n        if (mDebugDrawEnabled)\n            mDebugDrawer->addCollision(position, normal);\n    }\n\n    ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn,\n            bool waterCollision, float slowFall, float waterlevel)\n        : mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),\n        mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision), mSkipCollisionDetection(actor->skipCollisions()),\n        mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(actor->velocity()), mPosition(), mRefpos()\n    {\n        const MWBase::World *world = MWBase::Environment::get().getWorld();\n        const auto ptr = actor->getPtr();\n        mFlying = world->isFlying(ptr);\n        mSwimming = world->isSwimming(ptr);\n        mWantJump = ptr.getClass().getMovementSettings(ptr).mPosition[2] != 0;\n        auto& stats = ptr.getClass().getCreatureStats(ptr);\n        const bool godmode = ptr == world->getPlayerConstPtr() && world->getGodModeState();\n        mFloatToSurface = stats.isDead() || (!godmode && stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0);\n        mWasOnGround = actor->getOnGround();\n    }\n\n    void ActorFrameData::updatePosition(btCollisionWorld* world)\n    {\n        mActorRaw->applyOffsetChange();\n        mPosition = mActorRaw->getPosition();\n        if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(mActorRaw, mWaterlevel, world))\n        {\n            mPosition.z() = mWaterlevel;\n            MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z(), false);\n        }\n        mOldHeight = mPosition.z();\n        mRefpos = mActorRaw->getPtr().getRefData().getPosition();\n    }\n\n    WorldFrameData::WorldFrameData()\n        : mIsInStorm(MWBase::Environment::get().getWorld()->isInStorm())\n        , mStormDirection(MWBase::Environment::get().getWorld()->getStormDirection())\n    {}\n\n    LOSRequest::LOSRequest(const std::weak_ptr<Actor>& a1, const std::weak_ptr<Actor>& a2)\n        : mResult(false), mStale(false), mAge(0)\n    {\n        // we use raw actor pointer pair to uniquely identify request\n        // sort the pointer value in ascending order to not duplicate equivalent requests, eg. getLOS(A, B) and getLOS(B, A)\n        auto* raw1 = a1.lock().get();\n        auto* raw2 = a2.lock().get();\n        assert(raw1 != raw2);\n        if (raw1 < raw2)\n        {\n            mActors = {a1, a2};\n            mRawActors = {raw1, raw2};\n        }\n        else\n        {\n            mActors = {a2, a1};\n            mRawActors = {raw2, raw1};\n        }\n    }\n\n    bool operator==(const LOSRequest& lhs, const LOSRequest& rhs) noexcept\n    {\n        return lhs.mRawActors == rhs.mRawActors;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/physicssystem.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_PHYSICSSYSTEM_H\n#define OPENMW_MWPHYSICS_PHYSICSSYSTEM_H\n\n#include <array>\n#include <memory>\n#include <map>\n#include <set>\n#include <algorithm>\n\n#include <osg/Quat>\n#include <osg/BoundingBox>\n#include <osg/ref_ptr>\n#include <osg/Timer>\n\n#include \"../mwworld/ptr.hpp\"\n\n#include \"collisiontype.hpp\"\n#include \"raycasting.hpp\"\n\nnamespace osg\n{\n    class Group;\n    class Object;\n    class Stats;\n}\n\nnamespace MWRender\n{\n    class DebugDrawer;\n}\n\nnamespace Resource\n{\n    class BulletShapeManager;\n    class ResourceSystem;\n}\n\nnamespace SceneUtil\n{\n    class UnrefQueue;\n}\n\nclass btCollisionWorld;\nclass btBroadphaseInterface;\nclass btDefaultCollisionConfiguration;\nclass btCollisionDispatcher;\nclass btCollisionObject;\nclass btCollisionShape;\nclass btVector3;\n\nnamespace MWPhysics\n{\n    class HeightField;\n    class Object;\n    class Actor;\n    class PhysicsTaskScheduler;\n    class Projectile;\n\n    using ActorMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Actor>>;\n\n    struct ContactPoint\n    {\n        MWWorld::Ptr mObject;\n        osg::Vec3f mPoint;\n        osg::Vec3f mNormal;\n    };\n\n    struct LOSRequest\n    {\n        LOSRequest(const std::weak_ptr<Actor>& a1, const std::weak_ptr<Actor>& a2);\n        std::array<std::weak_ptr<Actor>, 2> mActors;\n        std::array<const Actor*, 2> mRawActors;\n        bool mResult;\n        bool mStale;\n        int mAge;\n    };\n    bool operator==(const LOSRequest& lhs, const LOSRequest& rhs) noexcept;\n\n    struct ActorFrameData\n    {\n        ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, float slowFall, float waterlevel);\n        void  updatePosition(btCollisionWorld* world);\n        std::weak_ptr<Actor> mActor;\n        Actor* mActorRaw;\n        MWWorld::Ptr mStandingOn;\n        bool mFlying;\n        bool mSwimming;\n        bool mWasOnGround;\n        bool mWantJump;\n        bool mDidJump;\n        bool mFloatToSurface;\n        bool mNeedLand;\n        bool mWaterCollision;\n        bool mSkipCollisionDetection;\n        float mWaterlevel;\n        float mSlowFall;\n        float mOldHeight;\n        float mFallHeight;\n        osg::Vec3f mMovement;\n        osg::Vec3f mPosition;\n        ESM::Position mRefpos;\n    };\n\n    struct WorldFrameData\n    {\n        WorldFrameData();\n        bool mIsInStorm;\n        osg::Vec3f mStormDirection;\n    };\n\n    class PhysicsSystem : public RayCastingInterface\n    {\n        public:\n            PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode);\n            virtual ~PhysicsSystem ();\n\n            void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue);\n\n            Resource::BulletShapeManager* getShapeManager();\n\n            void enableWater(float height);\n            void setWaterHeight(float height);\n            void disableWater();\n\n            void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);\n            void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);\n\n            int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius);\n            void setCaster(int projectileId, const MWWorld::Ptr& caster);\n            void updateProjectile(const int projectileId, const osg::Vec3f &position) const;\n            void removeProjectile(const int projectileId);\n\n            void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);\n\n            Actor* getActor(const MWWorld::Ptr& ptr);\n            const Actor* getActor(const MWWorld::ConstPtr& ptr) const;\n\n            const Object* getObject(const MWWorld::ConstPtr& ptr) const;\n\n            Projectile* getProjectile(int projectileId) const;\n\n            // Object or Actor\n            void remove (const MWWorld::Ptr& ptr);\n\n            void updateScale (const MWWorld::Ptr& ptr);\n            void updateRotation (const MWWorld::Ptr& ptr);\n            void updatePosition (const MWWorld::Ptr& ptr);\n\n            void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject);\n\n            void removeHeightField (int x, int y);\n\n            const HeightField* getHeightField(int x, int y) const;\n\n            bool toggleCollisionMode();\n\n            void stepSimulation();\n            void debugDraw();\n\n            std::vector<MWWorld::Ptr> getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with\n            std::vector<ContactPoint> getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const;\n            osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight);\n\n            std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::ConstPtr& actor,\n                                                               const osg::Vec3f &origin,\n                                                               const osg::Quat &orientation,\n                                                               float queryDistance, std::vector<MWWorld::Ptr>& targets);\n\n\n            /// Get distance from \\a point to the collision shape of \\a target. Uses a raycast to find where the\n            /// target vector hits the collision shape and then calculates distance from the intersection point.\n            /// This can be used to find out how much nearer we need to move to the target for a \"getHitContact\" to be successful.\n            /// \\note Only Actor targets are supported at the moment.\n            float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const override;\n\n            /// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.\n            RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(),\n                    std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),\n                    int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const override;\n\n            RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override;\n\n            /// Return true if actor1 can see actor2.\n            bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const override;\n\n            bool isOnGround (const MWWorld::Ptr& actor);\n\n            bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel);\n\n            /// Get physical half extents (scaled) of the given actor.\n            osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const;\n\n            /// Get physical half extents (not scaled) of the given actor.\n            osg::Vec3f getOriginalHalfExtents(const MWWorld::ConstPtr& actor) const;\n\n            /// @see MWPhysics::Actor::getRenderingHalfExtents\n            osg::Vec3f getRenderingHalfExtents(const MWWorld::ConstPtr& actor) const;\n\n            /// Get the position of the collision shape for the actor. Use together with getHalfExtents() to get the collision bounds in world space.\n            /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space.\n            osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const;\n\n            /// Get bounding box in world space of the given object.\n            osg::BoundingBox getBoundingBox(const MWWorld::ConstPtr &object) const;\n\n            /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will\n            /// be overwritten. Valid until the next call to applyQueuedMovement.\n            void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity);\n\n            /// Apply all queued movements, then clear the list.\n            const std::vector<MWWorld::Ptr>& applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);\n\n            /// Clear the queued movements list without applying.\n            void clearQueuedMovement();\n\n            /// Return true if \\a actor has been standing on \\a object in this frame\n            /// This will trigger whenever the object is directly below the actor.\n            /// It doesn't matter if the actor is stationary or moving.\n            bool isActorStandingOn(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const;\n\n            /// Get the handle of all actors standing on \\a object in this frame.\n            void getActorsStandingOn(const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr>& out) const;\n\n            /// Return true if \\a actor has collided with \\a object in this frame.\n            /// This will detect running into objects, but will not detect climbing stairs, stepping up a small object, etc.\n            bool isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const;\n\n            /// Get the handle of all actors colliding with \\a object in this frame.\n            void getActorsCollidingWith(const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr>& out) const;\n\n            bool toggleDebugRendering();\n\n            /// Mark the given object as a 'non-solid' object. A non-solid object means that\n            /// \\a isOnSolidGround will return false for actors standing on that object.\n            void markAsNonSolid (const MWWorld::ConstPtr& ptr);\n\n            bool isOnSolidGround (const MWWorld::Ptr& actor) const;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the physics framerate from elsewhere\n            */\n            void setPhysicsFramerate(float physFramerate);\n            /*\n                End of tes3mp addition\n            */\n\n            void updateAnimatedCollisionShape(const MWWorld::Ptr& object);\n\n            template <class Function>\n            void forEachAnimatedObject(Function&& function) const\n            {\n                std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function);\n            }\n\n            bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const;\n\n            void reportStats(unsigned int frameNumber, osg::Stats& stats) const;\n            void reportCollision(const btVector3& position, const btVector3& normal);\n\n        private:\n\n            void updateWater();\n\n            std::vector<ActorFrameData> prepareFrameData(bool willSimulate);\n\n            osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;\n\n            std::unique_ptr<btBroadphaseInterface> mBroadphase;\n            std::unique_ptr<btDefaultCollisionConfiguration> mCollisionConfiguration;\n            std::unique_ptr<btCollisionDispatcher> mDispatcher;\n            std::unique_ptr<btCollisionWorld> mCollisionWorld;\n            std::unique_ptr<PhysicsTaskScheduler> mTaskScheduler;\n\n            std::unique_ptr<Resource::BulletShapeManager> mShapeManager;\n            Resource::ResourceSystem* mResourceSystem;\n\n            using ObjectMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Object>>;\n            ObjectMap mObjects;\n\n            std::set<Object*> mAnimatedObjects; // stores pointers to elements in mObjects\n\n            ActorMap mActors;\n\n            using ProjectileMap = std::map<int, std::shared_ptr<Projectile>>;\n            ProjectileMap mProjectiles;\n\n            using HeightFieldMap = std::map<std::pair<int, int>, osg::ref_ptr<HeightField>>;\n            HeightFieldMap mHeightFields;\n\n            bool mDebugDrawEnabled;\n\n            float mTimeAccum;\n\n            unsigned int mProjectileId;\n\n            float mWaterHeight;\n            bool mWaterEnabled;\n\n            std::unique_ptr<btCollisionObject> mWaterCollisionObject;\n            std::unique_ptr<btCollisionShape> mWaterCollisionShape;\n\n            std::unique_ptr<MWRender::DebugDrawer> mDebugDrawer;\n\n            osg::ref_ptr<osg::Group> mParentNode;\n\n            float mPhysicsDt;\n\n            PhysicsSystem (const PhysicsSystem&);\n            PhysicsSystem& operator= (const PhysicsSystem&);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/projectile.cpp",
    "content": "#include <memory>\n\n#include <BulletCollision/CollisionShapes/btSphereShape.h>\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\n#include <components/misc/convert.hpp>\n\n#include \"../mwworld/class.hpp\"\n\n#include \"collisiontype.hpp\"\n#include \"mtphysics.hpp\"\n#include \"projectile.hpp\"\n\nnamespace MWPhysics\n{\nProjectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)\n    : mHitWater(false)\n    , mActive(true)\n    , mCaster(caster)\n    , mPhysics(physicssystem)\n    , mTaskScheduler(scheduler)\n{\n    mShape = std::make_unique<btSphereShape>(radius);\n    mConvexShape = static_cast<btConvexShape*>(mShape.get());\n\n    mCollisionObject = std::make_unique<btCollisionObject>();\n    mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);\n    mCollisionObject->setActivationState(DISABLE_DEACTIVATION);\n    mCollisionObject->setCollisionShape(mShape.get());\n    mCollisionObject->setUserPointer(this);\n\n    setPosition(position);\n\n    const int collisionMask = CollisionType_World | CollisionType_HeightMap |\n        CollisionType_Actor | CollisionType_Door | CollisionType_Water | CollisionType_Projectile;\n    mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask);\n\n    commitPositionChange();\n}\n\nProjectile::~Projectile()\n{\n    if (!mActive)\n        mPhysics->reportCollision(mHitPosition, mHitNormal);\n    mTaskScheduler->removeCollisionObject(mCollisionObject.get());\n}\n\nvoid Projectile::commitPositionChange()\n{\n    std::scoped_lock lock(mMutex);\n    if (mTransformUpdatePending)\n    {\n        auto& trans = mCollisionObject->getWorldTransform();\n        trans.setOrigin(Misc::Convert::toBullet(mPosition));\n        mCollisionObject->setWorldTransform(trans);\n        mTransformUpdatePending = false;\n    }\n}\n\nvoid Projectile::setPosition(const osg::Vec3f &position)\n{\n    std::scoped_lock lock(mMutex);\n    mPosition = position;\n    mTransformUpdatePending = true;\n}\n\nosg::Vec3f Projectile::getPosition() const\n{\n    std::scoped_lock lock(mMutex);\n    return mPosition;\n}\n\nvoid Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal)\n{\n    if (!mActive.load(std::memory_order_acquire))\n        return;\n    std::scoped_lock lock(mMutex);\n    mHitTarget = target;\n    mHitPosition = pos;\n    mHitNormal = normal;\n    mActive.store(false, std::memory_order_release);\n}\n\nMWWorld::Ptr Projectile::getCaster() const\n{\n    std::scoped_lock lock(mMutex);\n    return mCaster;\n}\n\nvoid Projectile::setCaster(MWWorld::Ptr caster)\n{\n    std::scoped_lock lock(mMutex);\n    mCaster = caster;\n}\n\nvoid Projectile::setValidTargets(const std::vector<MWWorld::Ptr>& targets)\n{\n    std::scoped_lock lock(mMutex);\n    mValidTargets = targets;\n}\n\nbool Projectile::isValidTarget(const MWWorld::Ptr& target) const\n{\n    std::scoped_lock lock(mMutex);\n    if (mCaster == target)\n        return false;\n\n    if (target.isEmpty() || mValidTargets.empty())\n        return true;\n\n    bool validTarget = false;\n    for (const auto& targetActor : mValidTargets)\n    {\n        if (targetActor == target)\n        {\n            validTarget = true;\n            break;\n        }\n    }\n    return validTarget;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/projectile.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_PROJECTILE_H\n#define OPENMW_MWPHYSICS_PROJECTILE_H\n\n#include <atomic>\n#include <memory>\n#include <mutex>\n\n#include <LinearMath/btVector3.h>\n\n#include \"ptrholder.hpp\"\n\nclass btCollisionObject;\nclass btCollisionShape;\nclass btConvexShape;\n\nnamespace osg\n{\n    class Vec3f;\n}\n\nnamespace Resource\n{\n    class BulletShape;\n}\n\nnamespace MWPhysics\n{\n    class PhysicsTaskScheduler;\n    class PhysicsSystem;\n\n    class Projectile final : public PtrHolder\n    {\n    public:\n        Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);\n        ~Projectile() override;\n\n        btConvexShape* getConvexShape() const { return mConvexShape; }\n\n        void commitPositionChange();\n\n        void setPosition(const osg::Vec3f& position);\n        osg::Vec3f getPosition() const;\n\n        btCollisionObject* getCollisionObject() const\n        {\n            return mCollisionObject.get();\n        }\n\n        bool isActive() const\n        {\n            return mActive.load(std::memory_order_acquire);\n        }\n\n        MWWorld::Ptr getTarget() const\n        {\n            assert(!mActive);\n            return mHitTarget;\n        }\n\n        MWWorld::Ptr getCaster() const;\n        void setCaster(MWWorld::Ptr caster);\n\n        void setHitWater()\n        {\n            mHitWater = true;\n        }\n\n        bool getHitWater() const\n        {\n            return mHitWater;\n        }\n\n        void hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal);\n\n        void setValidTargets(const std::vector<MWWorld::Ptr>& targets);\n        bool isValidTarget(const MWWorld::Ptr& target) const;\n\n        btVector3 getHitPosition() const\n        {\n            return mHitPosition;\n        }\n\n    private:\n\n        std::unique_ptr<btCollisionShape> mShape;\n        btConvexShape* mConvexShape;\n\n        std::unique_ptr<btCollisionObject> mCollisionObject;\n        bool mTransformUpdatePending;\n        bool mHitWater;\n        std::atomic<bool> mActive;\n        MWWorld::Ptr mCaster;\n        MWWorld::Ptr mHitTarget;\n        osg::Vec3f mPosition;\n        btVector3 mHitPosition;\n        btVector3 mHitNormal;\n\n        std::vector<MWWorld::Ptr> mValidTargets;\n\n        mutable std::mutex mMutex;\n\n        PhysicsSystem *mPhysics;\n        PhysicsTaskScheduler *mTaskScheduler;\n\n        Projectile(const Projectile&);\n        Projectile& operator=(const Projectile&);\n    };\n\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/projectileconvexcallback.cpp",
    "content": "#include \"../mwworld/class.hpp\"\n\n#include \"actor.hpp\"\n#include \"collisiontype.hpp\"\n#include \"projectile.hpp\"\n#include \"projectileconvexcallback.hpp\"\n#include \"ptrholder.hpp\"\n\nnamespace MWPhysics\n{\n    ProjectileConvexCallback::ProjectileConvexCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj)\n        : btCollisionWorld::ClosestConvexResultCallback(from, to)\n        , mMe(me), mProjectile(proj)\n    {\n        assert(mProjectile);\n    }\n\n    btScalar ProjectileConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace)\n    {\n        // don't hit the caster\n        if (result.m_hitCollisionObject == mMe)\n            return 1.f;\n\n        // don't hit the projectile\n        if (result.m_hitCollisionObject == mProjectile->getCollisionObject())\n            return 1.f;\n\n        btCollisionWorld::ClosestConvexResultCallback::addSingleResult(result, normalInWorldSpace);\n        switch (result.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup)\n        {\n            case CollisionType_Actor:\n                {\n                    auto* target = static_cast<Actor*>(result.m_hitCollisionObject->getUserPointer());\n                    if (!mProjectile->isValidTarget(target->getPtr()))\n                        return 1.f;\n                    mProjectile->hit(target->getPtr(), result.m_hitPointLocal, result.m_hitNormalLocal);\n                    break;\n                }\n            case CollisionType_Projectile:\n                {\n                    auto* target = static_cast<Projectile*>(result.m_hitCollisionObject->getUserPointer());\n                    if (!mProjectile->isValidTarget(target->getCaster()))\n                        return 1.f;\n                    target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);\n                    mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);\n                    break;\n                }\n            case CollisionType_Water:\n                {\n                    mProjectile->setHitWater();\n                    mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld);\n                    break;\n                }\n            default:\n                {\n                    auto* target = static_cast<PtrHolder*>(result.m_hitCollisionObject->getUserPointer());\n                    auto ptr = target ? target->getPtr() : MWWorld::Ptr();\n                    mProjectile->hit(ptr, m_hitPointWorld, m_hitNormalWorld);\n                    break;\n                }\n        }\n\n        return result.m_hitFraction;\n    }\n\n}\n\n"
  },
  {
    "path": "apps/openmw/mwphysics/projectileconvexcallback.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_PROJECTILECONVEXCALLBACK_H\n#define OPENMW_MWPHYSICS_PROJECTILECONVEXCALLBACK_H\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\nclass btCollisionObject;\n\nnamespace MWPhysics\n{\n    class Projectile;\n\n    class ProjectileConvexCallback : public btCollisionWorld::ClosestConvexResultCallback\n    {\n    public:\n        ProjectileConvexCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj);\n\n        btScalar addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace) override;\n\n    private:\n        const btCollisionObject* mMe;\n        Projectile* mProjectile;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/ptrholder.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_PTRHOLDER_H\n#define OPENMW_MWPHYSICS_PTRHOLDER_H\n\n#include <mutex>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWPhysics\n{\n    class PtrHolder\n    {\n    public:\n        virtual ~PtrHolder() {}\n\n        void updatePtr(const MWWorld::Ptr& updated)\n        {\n            std::scoped_lock lock(mMutex);\n            mPtr = updated;\n        }\n\n        MWWorld::Ptr getPtr()\n        {\n            std::scoped_lock lock(mMutex);\n            return mPtr;\n        }\n\n        MWWorld::ConstPtr getPtr() const\n        {\n            std::scoped_lock lock(mMutex);\n            return mPtr;\n        }\n\n    protected:\n        MWWorld::Ptr mPtr;\n\n    private:\n        mutable std::mutex mMutex;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/raycasting.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_RAYCASTING_H\n#define OPENMW_MWPHYSICS_RAYCASTING_H\n\n#include <osg/Vec3f>\n\n#include \"../mwworld/ptr.hpp\"\n\n#include \"collisiontype.hpp\"\n\nnamespace MWPhysics\n{\n    struct RayCastingResult\n    {\n        bool mHit;\n        osg::Vec3f mHitPos;\n        osg::Vec3f mHitNormal;\n        MWWorld::Ptr mHitObject;\n    };\n\n    class RayCastingInterface\n    {\n        public:\n            /// Get distance from \\a point to the collision shape of \\a target. Uses a raycast to find where the\n            /// target vector hits the collision shape and then calculates distance from the intersection point.\n            /// This can be used to find out how much nearer we need to move to the target for a \"getHitContact\" to be successful.\n            /// \\note Only Actor targets are supported at the moment.\n            virtual float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const = 0;\n\n            /// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.\n            virtual RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(),\n                    std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),\n                    int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const = 0;\n\n            virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const = 0;\n\n            /// Return true if actor1 can see actor2.\n            virtual bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/stepper.cpp",
    "content": "#include \"stepper.hpp\"\n\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\n#include \"collisiontype.hpp\"\n#include \"constants.hpp\"\n#include \"movementsolver.hpp\"\n\nnamespace MWPhysics\n{\n    static bool canStepDown(const ActorTracer &stepper)\n    {\n        if (!stepper.mHitObject)\n            return false;\n        static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope));\n        if (stepper.mPlaneNormal.z() <= sMaxSlopeCos)\n            return false;\n\n        return stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor;\n    }\n\n    Stepper::Stepper(const btCollisionWorld *colWorld, const btCollisionObject *colObj)\n        : mColWorld(colWorld)\n        , mColObj(colObj)\n    {\n    }\n\n    bool Stepper::step(osg::Vec3f &position, osg::Vec3f &velocity, float &remainingTime, const bool & onGround, bool firstIteration)\n    {\n        if(velocity.x() == 0.0 && velocity.y() == 0.0)\n            return false;\n\n        // Stairstepping algorithms work by moving up to avoid the step, moving forwards, then moving back down onto the ground.\n        // This algorithm has a couple of minor problems, but they don't cause problems for sane geometry, and just prevent stepping on insane geometry.\n\n        mUpStepper.doTrace(mColObj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), mColWorld);\n\n        float upDistance = 0;\n        if(!mUpStepper.mHitObject)\n            upDistance = sStepSizeUp;\n        else if(mUpStepper.mFraction*sStepSizeUp > sCollisionMargin)\n            upDistance = mUpStepper.mFraction*sStepSizeUp - sCollisionMargin;\n        else\n        {\n            return false;\n        }\n\n        auto toMove = velocity * remainingTime;\n\n        osg::Vec3f tracerPos = position + osg::Vec3f(0.0f, 0.0f, upDistance);\n\n        osg::Vec3f tracerDest;\n        auto normalMove = toMove;\n        auto moveDistance = normalMove.normalize();\n        // attempt 1: normal movement\n        // attempt 2: fixed distance movement, only happens on the first movement solver iteration/bounce each frame to avoid a glitch\n        // attempt 3: further, less tall fixed distance movement, same as above\n        // If you're making a full conversion you should purge the logic for attempts 2 and 3. Attempts 2 and 3 just try to work around problems with vanilla Morrowind assets.\n        int attempt = 0;\n        float downStepSize = 0;\n        while(attempt < 3)\n        {\n            attempt++;\n\n            if(attempt == 1)\n                tracerDest = tracerPos + toMove;\n            else if (!sDoExtraStairHacks) // early out if we have extra hacks disabled\n            {\n                return false;\n            }\n            else if(attempt == 2)\n            {\n                moveDistance = sMinStep;\n                tracerDest = tracerPos + normalMove*sMinStep;\n            }\n            else if(attempt == 3)\n            {\n                if(upDistance > sStepSizeUp)\n                {\n                    upDistance = sStepSizeUp;\n                    tracerPos = position + osg::Vec3f(0.0f, 0.0f, upDistance);\n                }\n                moveDistance = sMinStep2;\n                tracerDest = tracerPos + normalMove*sMinStep2;\n            }\n\n            mTracer.doTrace(mColObj, tracerPos, tracerDest, mColWorld);\n            if(mTracer.mHitObject)\n            {\n                // map against what we hit, minus the safety margin\n                moveDistance *= mTracer.mFraction;\n                if(moveDistance <= sCollisionMargin) // didn't move enough to accomplish anything\n                {\n                    return false;\n                }\n\n                moveDistance -= sCollisionMargin;\n                tracerDest = tracerPos + normalMove*moveDistance;\n\n                // safely eject from what we hit by the safety margin\n                auto tempDest = tracerDest + mTracer.mPlaneNormal*sCollisionMargin*2;\n\n                ActorTracer tempTracer;\n                tempTracer.doTrace(mColObj, tracerDest, tempDest, mColWorld);\n\n                if(tempTracer.mFraction > 0.5f) // distance to any object is greater than sCollisionMargin (we checked sCollisionMargin*2 distance)\n                {\n                    auto effectiveFraction = tempTracer.mFraction*2.0f - 1.0f;\n                    tracerDest += mTracer.mPlaneNormal*sCollisionMargin*effectiveFraction;\n                }\n            }\n\n            if(attempt > 2) // do not allow stepping down below original height for attempt 3\n                downStepSize = upDistance;\n            else\n                downStepSize = moveDistance + upDistance + sStepSizeDown;\n            mDownStepper.doTrace(mColObj, tracerDest, tracerDest + osg::Vec3f(0.0f, 0.0f, -downStepSize), mColWorld);\n\n            // can't step down onto air, non-walkable-slopes, or actors\n            // NOTE: using a capsule causes isWalkableSlope (used in canStepDown) to fail on certain geometry that were intended to be valid at the bottoms of stairs\n            // (like the bottoms of the staircases in aldruhn's guild of mages)\n            // The old code worked around this by trying to do mTracer again with a fixed distance of sMinStep (10.0) but it caused all sorts of other problems.\n            // Switched back to cylinders to avoid that and similer problems.\n            if(canStepDown(mDownStepper))\n            {\n                break;\n            }\n            else\n            {\n                // do not try attempt 3 if we just tried attempt 2 and the horizontal distance was rather large\n                // (forces actor to get snug against the defective ledge for attempt 3 to be tried)\n                if(attempt == 2 && moveDistance > upDistance-(mDownStepper.mFraction*downStepSize))\n                {\n                    return false;\n                }\n                // do next attempt if first iteration of movement solver and not out of attempts\n                if(firstIteration && attempt < 3)\n                {\n                    continue;\n                }\n\n                return false;\n            }\n        }\n\n        // note: can't downstep onto actors so no need to pick safety margin\n        float downDistance = 0;\n        if(mDownStepper.mFraction*downStepSize > sCollisionMargin)\n            downDistance = mDownStepper.mFraction*downStepSize - sCollisionMargin;\n\n        if(downDistance-sCollisionMargin-sGroundOffset > upDistance && !onGround)\n            return false;\n\n        auto newpos = tracerDest + osg::Vec3f(0.0f, 0.0f, -downDistance);\n\n        if((position-newpos).length2() < sCollisionMargin*sCollisionMargin)\n            return false;\n\n        if(mTracer.mHitObject)\n        {\n            auto planeNormal = mTracer.mPlaneNormal;\n            if (onGround && !isWalkableSlope(planeNormal) && planeNormal.z() != 0)\n            {\n                planeNormal.z() = 0;\n                planeNormal.normalize();\n            }\n            velocity = reject(velocity, planeNormal);\n        }\n        velocity = reject(velocity, mDownStepper.mPlaneNormal);\n\n        position = newpos;\n\n        remainingTime *= (1.0f-mTracer.mFraction); // remaining time is proportional to remaining distance\n        return true;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/stepper.hpp",
    "content": "#ifndef OPENMW_MWPHYSICS_STEPPER_H\n#define OPENMW_MWPHYSICS_STEPPER_H\n\n#include \"trace.h\"\n\nclass btCollisionObject;\nclass btCollisionWorld;\n\nnamespace osg\n{\n    class Vec3f;\n}\n\nnamespace MWPhysics\n{\n    class Stepper\n    {\n    private:\n        const btCollisionWorld *mColWorld;\n        const btCollisionObject *mColObj;\n\n        ActorTracer mTracer, mUpStepper, mDownStepper;\n\n    public:\n        Stepper(const btCollisionWorld *colWorld, const btCollisionObject *colObj);\n\n        bool step(osg::Vec3f &position, osg::Vec3f &velocity, float &remainingTime, const bool & onGround, bool firstIteration);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwphysics/trace.cpp",
    "content": "#include \"trace.h\"\n\n#include <components/misc/convert.hpp>\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n#include <BulletCollision/CollisionShapes/btConvexShape.h>\n\n#include \"collisiontype.hpp\"\n#include \"actor.hpp\"\n#include \"actorconvexcallback.hpp\"\n\nnamespace MWPhysics\n{\n\nvoid ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& start, const osg::Vec3f& end, const btCollisionWorld* world)\n{\n    const btVector3 btstart = Misc::Convert::toBullet(start);\n    const btVector3 btend = Misc::Convert::toBullet(end);\n\n    const btTransform &trans = actor->getWorldTransform();\n    btTransform from(trans);\n    btTransform to(trans);\n    from.setOrigin(btstart);\n    to.setOrigin(btend);\n\n    const btVector3 motion = btstart-btend;\n    ActorConvexCallback newTraceCallback(actor, motion, btScalar(0.0), world);\n    // Inherit the actor's collision group and mask\n    newTraceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup;\n    newTraceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask;\n\n    const btCollisionShape *shape = actor->getCollisionShape();\n    assert(shape->isConvex());\n    world->convexSweepTest(static_cast<const btConvexShape*>(shape), from, to, newTraceCallback);\n\n    // Copy the hit data over to our trace results struct:\n    if(newTraceCallback.hasHit())\n    {\n        mFraction = newTraceCallback.m_closestHitFraction;\n        mPlaneNormal = Misc::Convert::toOsg(newTraceCallback.m_hitNormalWorld);\n        mEndPos = (end-start)*mFraction + start;\n        mHitPoint = Misc::Convert::toOsg(newTraceCallback.m_hitPointWorld);\n        mHitObject = newTraceCallback.m_hitCollisionObject;\n    }\n    else\n    {\n        mEndPos = end;\n        mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f);\n        mFraction = 1.0f;\n        mHitPoint = end;\n        mHitObject = nullptr;\n    }\n}\n\nvoid ActorTracer::findGround(const Actor* actor, const osg::Vec3f& start, const osg::Vec3f& end, const btCollisionWorld* world)\n{\n    const btVector3 btstart = Misc::Convert::toBullet(start);\n    const btVector3 btend = Misc::Convert::toBullet(end);\n\n    const btTransform &trans = actor->getCollisionObject()->getWorldTransform();\n    btTransform from(trans.getBasis(), btstart);\n    btTransform to(trans.getBasis(), btend);\n\n    const btVector3 motion = btstart-btend;\n    ActorConvexCallback newTraceCallback(actor->getCollisionObject(), motion, btScalar(0.0), world);\n    // Inherit the actor's collision group and mask\n    newTraceCallback.m_collisionFilterGroup = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterGroup;\n    newTraceCallback.m_collisionFilterMask = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterMask;\n    newTraceCallback.m_collisionFilterMask &= ~CollisionType_Actor;\n\n    world->convexSweepTest(actor->getConvexShape(), from, to, newTraceCallback);\n    if(newTraceCallback.hasHit())\n    {\n        mFraction = newTraceCallback.m_closestHitFraction;\n        mPlaneNormal = Misc::Convert::toOsg(newTraceCallback.m_hitNormalWorld);\n        mEndPos = (end-start)*mFraction + start;\n    }\n    else\n    {\n        mEndPos = end;\n        mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f);\n        mFraction = 1.0f;\n    }\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwphysics/trace.h",
    "content": "#ifndef OENGINE_BULLET_TRACE_H\n#define OENGINE_BULLET_TRACE_H\n\n#include <osg/Vec3f>\n\nclass btCollisionObject;\nclass btCollisionWorld;\n\n\nnamespace MWPhysics\n{\n    class Actor;\n\n    struct ActorTracer\n    {\n        osg::Vec3f mEndPos;\n        osg::Vec3f mPlaneNormal;\n        osg::Vec3f mHitPoint;\n        const btCollisionObject* mHitObject;\n\n        float mFraction;\n\n        void doTrace(const btCollisionObject *actor, const osg::Vec3f& start, const osg::Vec3f& end, const btCollisionWorld* world);\n        void findGround(const Actor* actor, const osg::Vec3f& start, const osg::Vec3f& end, const btCollisionWorld* world);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/.gitignore",
    "content": "old\n"
  },
  {
    "path": "apps/openmw/mwrender/actoranimation.cpp",
    "content": "#include \"actoranimation.hpp\"\n#include <utility>\n\n#include <osg/Node>\n#include <osg/Group>\n#include <osg/Vec4f>\n\n#include <components/esm/loadligh.hpp>\n#include <components/esm/loadcell.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n\n#include <components/sceneutil/lightmanager.hpp>\n#include <components/sceneutil/lightutil.hpp>\n#include <components/sceneutil/visitor.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include <components/vfs/manager.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"vismask.hpp\"\n\nnamespace MWRender\n{\n\nActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem)\n    : Animation(ptr, parentNode, resourceSystem)\n{\n    MWWorld::ContainerStore& store = mPtr.getClass().getContainerStore(mPtr);\n\n    for (MWWorld::ConstContainerStoreIterator iter = store.cbegin(MWWorld::ContainerStore::Type_Light);\n         iter != store.cend(); ++iter)\n    {\n        const ESM::Light* light = iter->get<ESM::Light>()->mBase;\n        if (!(light->mData.mFlags & ESM::Light::Carry))\n        {\n            addHiddenItemLight(*iter, light);\n        }\n    }\n\n    // Make sure we cleaned object from effects, just in cast if we re-use node\n    removeEffects();\n}\n\nActorAnimation::~ActorAnimation()\n{\n    for (ItemLightMap::iterator iter = mItemLights.begin(); iter != mItemLights.end(); ++iter)\n    {\n        mInsert->removeChild(iter->second);\n    }\n\n    mScabbard.reset();\n    mHolsteredShield.reset();\n}\n\nPartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor)\n{\n    osg::Group* parent = getBoneByName(bonename);\n    if (!parent)\n        return nullptr;\n\n    osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->getInstance(model, parent);\n\n    const NodeMap& nodeMap = getNodeMap();\n    NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));\n    if (found == nodeMap.end())\n        return PartHolderPtr();\n\n    if (enchantedGlow)\n        mGlowUpdater = SceneUtil::addEnchantedGlow(instance, mResourceSystem, *glowColor);\n\n    return PartHolderPtr(new PartHolder(instance));\n}\n\nstd::string ActorAnimation::getShieldMesh(MWWorld::ConstPtr shield) const\n{\n    std::string mesh = shield.getClass().getModel(shield);\n    const ESM::Armor *armor = shield.get<ESM::Armor>()->mBase;\n    const std::vector<ESM::PartReference>& bodyparts = armor->mParts.mParts;\n    if (!bodyparts.empty())\n    {\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();\n\n        // Try to get shield model from bodyparts first, with ground model as fallback\n        for (const auto& part : bodyparts)\n        {\n            // Assume all creatures use the male mesh.\n            if (part.mPart != ESM::PRT_Shield || part.mMale.empty())\n                continue;\n            const ESM::BodyPart *bodypart = partStore.search(part.mMale);\n            if (bodypart && bodypart->mData.mType == ESM::BodyPart::MT_Armor && !bodypart->mModel.empty())\n            {\n                mesh = \"meshes\\\\\" + bodypart->mModel;\n                break;\n            }\n        }\n    }\n\n    if (mesh.empty())\n        return mesh;\n\n    std::string holsteredName = mesh;\n    holsteredName = holsteredName.replace(holsteredName.size()-4, 4, \"_sh.nif\");\n    if(mResourceSystem->getVFS()->exists(holsteredName))\n    {\n        osg::ref_ptr<osg::Node> shieldTemplate = mResourceSystem->getSceneManager()->getInstance(holsteredName);\n        SceneUtil::FindByNameVisitor findVisitor (\"Bip01 Sheath\");\n        shieldTemplate->accept(findVisitor);\n        osg::ref_ptr<osg::Node> sheathNode = findVisitor.mFoundNode;\n        if(!sheathNode)\n            return std::string();\n    }\n\n    return mesh;\n}\n\nbool ActorAnimation::updateCarriedLeftVisible(const int weaptype) const\n{\n    static const bool shieldSheathing = Settings::Manager::getBool(\"shield sheathing\", \"Game\");\n    if (shieldSheathing)\n    {\n        const MWWorld::Class &cls = mPtr.getClass();\n        MWMechanics::CreatureStats &stats = cls.getCreatureStats(mPtr);\n        if (cls.hasInventoryStore(mPtr) && weaptype != ESM::Weapon::Spell)\n        {\n            SceneUtil::FindByNameVisitor findVisitor (\"Bip01 AttachShield\");\n            mObjectRoot->accept(findVisitor);\n            if (findVisitor.mFoundNode || (mPtr == MWMechanics::getPlayer() && mPtr.isInCell() && MWBase::Environment::get().getWorld()->isFirstPerson()))\n            {\n                const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);\n                const MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n                const MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n                if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Armor).name() && !getShieldMesh(*shield).empty())\n                {\n                    if(stats.getDrawState() != MWMechanics::DrawState_Weapon)\n                        return false;\n\n                    if (weapon != inv.end())\n                    {\n                        const std::string &type = weapon->getTypeName();\n                        if(type == typeid(ESM::Weapon).name())\n                        {\n                            const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();\n                            ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;\n                            return !(MWMechanics::getWeaponType(weaponType)->mFlags & ESM::WeaponType::TwoHanded);\n                        }\n                        else if (type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())\n                            return true;\n                    }\n                }\n            }\n        }\n    }\n\n    return !(MWMechanics::getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded);\n}\n\nvoid ActorAnimation::updateHolsteredShield(bool showCarriedLeft)\n{\n    static const bool shieldSheathing = Settings::Manager::getBool(\"shield sheathing\", \"Game\");\n    if (!shieldSheathing)\n        return;\n\n    if (!mPtr.getClass().hasInventoryStore(mPtr))\n        return;\n\n    mHolsteredShield.reset();\n\n    if (showCarriedLeft)\n        return;\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n    if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name())\n        return;\n\n    // Can not show holdstered shields with two-handed weapons at all\n    const MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if(weapon == inv.end())\n        return;\n\n    const std::string &type = weapon->getTypeName();\n    if(type == typeid(ESM::Weapon).name())\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();\n        ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;\n        if (MWMechanics::getWeaponType(weaponType)->mFlags & ESM::WeaponType::TwoHanded)\n            return;\n    }\n\n    std::string mesh = getShieldMesh(*shield);\n    if (mesh.empty())\n        return;\n\n    std::string boneName = \"Bip01 AttachShield\";\n    osg::Vec4f glowColor = shield->getClass().getEnchantmentColor(*shield);\n    std::string holsteredName = mesh;\n    holsteredName = holsteredName.replace(holsteredName.size()-4, 4, \"_sh.nif\");\n    bool isEnchanted = !shield->getClass().getEnchantment(*shield).empty();\n\n    // If we have no dedicated sheath model, use basic shield model as fallback.\n    if (!mResourceSystem->getVFS()->exists(holsteredName))\n        mHolsteredShield = attachMesh(mesh, boneName, isEnchanted, &glowColor);\n    else\n        mHolsteredShield = attachMesh(holsteredName, boneName, isEnchanted, &glowColor);\n\n    if (!mHolsteredShield)\n        return;\n\n    SceneUtil::FindByNameVisitor findVisitor (\"Bip01 Sheath\");\n    mHolsteredShield->getNode()->accept(findVisitor);\n    osg::Group* shieldNode = findVisitor.mFoundNode;\n\n    // If mesh author declared an empty sheath node, use transformation from this node, but use the common shield mesh.\n    // This approach allows to tweak shield position without need to store the whole shield mesh in the _sh file.\n    if (shieldNode && !shieldNode->getNumChildren())\n    {\n        osg::ref_ptr<osg::Node> fallbackNode = mResourceSystem->getSceneManager()->getInstance(mesh, shieldNode);\n        if (isEnchanted)\n            SceneUtil::addEnchantedGlow(shieldNode, mResourceSystem, glowColor);\n    }\n\n    if (mAlpha != 1.f)\n        mResourceSystem->getSceneManager()->recreateShaders(mHolsteredShield->getNode());\n}\n\nbool ActorAnimation::useShieldAnimations() const\n{\n    static const bool shieldSheathing = Settings::Manager::getBool(\"shield sheathing\", \"Game\");\n    if (!shieldSheathing)\n        return false;\n\n    const MWWorld::Class &cls = mPtr.getClass();\n    if (!cls.hasInventoryStore(mPtr))\n        return false;\n\n    if (getTextKeyTime(\"shield: equip attach\") < 0 || getTextKeyTime(\"shield: unequip detach\") < 0)\n        return false;\n\n    const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);\n    const MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    const MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n    if (weapon != inv.end() && shield != inv.end() &&\n        shield->getTypeName() == typeid(ESM::Armor).name() &&\n        !getShieldMesh(*shield).empty())\n    {\n        const std::string &type = weapon->getTypeName();\n        if(type == typeid(ESM::Weapon).name())\n        {\n            const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();\n            ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;\n            return !(MWMechanics::getWeaponType(weaponType)->mFlags & ESM::WeaponType::TwoHanded);\n        }\n        else if (type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())\n            return true;\n    }\n\n    return false;\n}\n\nosg::Group* ActorAnimation::getBoneByName(const std::string& boneName)\n{\n    if (!mObjectRoot)\n        return nullptr;\n\n    SceneUtil::FindByNameVisitor findVisitor (boneName);\n    mObjectRoot->accept(findVisitor);\n\n    return findVisitor.mFoundNode;\n}\n\nstd::string ActorAnimation::getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon)\n{\n    std::string boneName;\n    if(weapon.isEmpty())\n        return boneName;\n\n    const std::string &type = weapon.getClass().getTypeName();\n    if(type == typeid(ESM::Weapon).name())\n    {\n        const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon.get<ESM::Weapon>();\n        int weaponType = ref->mBase->mData.mType;\n        return MWMechanics::getWeaponType(weaponType)->mSheathingBone;\n    }\n\n    return boneName;\n}\n\nvoid ActorAnimation::resetControllers(osg::Node* node)\n{\n    if (node == nullptr)\n        return;\n\n    std::shared_ptr<SceneUtil::ControllerSource> src;\n    src.reset(new NullAnimationTime);\n    SceneUtil::AssignControllerSourcesVisitor removeVisitor(src);\n    node->accept(removeVisitor);\n}\n\nvoid ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)\n{\n    static const bool weaponSheathing = Settings::Manager::getBool(\"weapon sheathing\", \"Game\");\n    if (!weaponSheathing)\n        return;\n\n    if (!mPtr.getClass().hasInventoryStore(mPtr))\n        return;\n\n    mScabbard.reset();\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if (weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())\n        return;\n\n    // Since throwing weapons stack themselves, do not show such weapon itself\n    int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;\n    if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)\n        showHolsteredWeapons = false;\n\n    std::string mesh = weapon->getClass().getModel(*weapon);\n    std::string scabbardName = mesh;\n\n    std::string boneName = getHolsteredWeaponBoneName(*weapon);\n    if (mesh.empty() || boneName.empty())\n        return;\n\n    // If the scabbard is not found, use a weapon mesh as fallback.\n    // Note: it is unclear how to handle time for controllers attached to bodyparts, so disable them for now.\n    // We use the similar approach for other bodyparts.\n    scabbardName = scabbardName.replace(scabbardName.size()-4, 4, \"_sh.nif\");\n    bool isEnchanted = !weapon->getClass().getEnchantment(*weapon).empty();\n    if(!mResourceSystem->getVFS()->exists(scabbardName))\n    {\n        if (showHolsteredWeapons)\n        {\n            osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);\n            mScabbard = attachMesh(mesh, boneName, isEnchanted, &glowColor);\n            if (mScabbard)\n                resetControllers(mScabbard->getNode());\n        }\n\n        return;\n    }\n\n    mScabbard = attachMesh(scabbardName, boneName);\n    if (mScabbard)\n        resetControllers(mScabbard->getNode());\n\n    osg::Group* weaponNode = getBoneByName(\"Bip01 Weapon\");\n    if (!weaponNode)\n        return;\n\n    // When we draw weapon, hide the Weapon node from sheath model.\n    // Otherwise add the enchanted glow to it.\n    if (!showHolsteredWeapons)\n    {\n        weaponNode->setNodeMask(0);\n    }\n    else\n    {\n        // If mesh author declared empty weapon node, use transformation from this node, but use the common weapon mesh.\n        // This approach allows to tweak weapon position without need to store the whole weapon mesh in the _sh file.\n        if (!weaponNode->getNumChildren())\n        {\n            osg::ref_ptr<osg::Node> fallbackNode = mResourceSystem->getSceneManager()->getInstance(mesh, weaponNode);\n            resetControllers(fallbackNode);\n        }\n\n        if (isEnchanted)\n        {\n            osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);\n            mGlowUpdater = SceneUtil::addEnchantedGlow(weaponNode, mResourceSystem, glowColor);\n        }\n    }\n}\n\nvoid ActorAnimation::updateQuiver()\n{\n    static const bool weaponSheathing = Settings::Manager::getBool(\"weapon sheathing\", \"Game\");\n    if (!weaponSheathing)\n        return;\n\n    if (!mPtr.getClass().hasInventoryStore(mPtr))\n        return;\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())\n        return;\n\n    std::string mesh = weapon->getClass().getModel(*weapon);\n    std::string boneName = getHolsteredWeaponBoneName(*weapon);\n    if (mesh.empty() || boneName.empty())\n        return;\n\n    osg::Group* ammoNode = getBoneByName(\"Bip01 Ammo\");\n    if (!ammoNode)\n        return;\n\n    // Special case for throwing weapons - they do not use ammo, but they stack themselves\n    bool suitableAmmo = false;\n    MWWorld::ConstContainerStoreIterator ammo = weapon;\n    unsigned int ammoCount = 0;\n    int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;\n    const auto& weaponType = MWMechanics::getWeaponType(type);\n    if (weaponType->mWeaponClass == ESM::WeaponType::Thrown)\n    {\n        ammoCount = ammo->getRefData().getCount();\n        osg::Group* throwingWeaponNode = getBoneByName(weaponType->mAttachBone);\n        if (throwingWeaponNode && throwingWeaponNode->getNumChildren())\n            ammoCount--;\n\n        suitableAmmo = true;\n    }\n    else\n    {\n        ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n        if (ammo == inv.end())\n            return;\n\n        ammoCount = ammo->getRefData().getCount();\n        bool arrowAttached = isArrowAttached();\n        if (arrowAttached)\n            ammoCount--;\n\n        suitableAmmo = ammo->get<ESM::Weapon>()->mBase->mData.mType == weaponType->mAmmoType;\n    }\n\n    if (!suitableAmmo)\n        return;\n\n    // We should not show more ammo than equipped and more than quiver mesh has\n    ammoCount = std::min(ammoCount, ammoNode->getNumChildren());\n\n    // Remove existing ammo nodes\n    for (unsigned int i=0; i<ammoNode->getNumChildren(); ++i)\n    {\n        osg::ref_ptr<osg::Group> arrowNode = ammoNode->getChild(i)->asGroup();\n        if (!arrowNode->getNumChildren())\n            continue;\n\n        osg::ref_ptr<osg::Node> arrowChildNode = arrowNode->getChild(0);\n        arrowNode->removeChild(arrowChildNode);\n    }\n\n    // Add new ones\n    osg::Vec4f glowColor = ammo->getClass().getEnchantmentColor(*ammo);\n    std::string model = ammo->getClass().getModel(*ammo);\n    for (unsigned int i=0; i<ammoCount; ++i)\n    {\n        osg::ref_ptr<osg::Group> arrowNode = ammoNode->getChild(i)->asGroup();\n        osg::ref_ptr<osg::Node> arrow = mResourceSystem->getSceneManager()->getInstance(model, arrowNode);\n        if (!ammo->getClass().getEnchantment(*ammo).empty())\n            mGlowUpdater = SceneUtil::addEnchantedGlow(arrow, mResourceSystem, glowColor);\n    }\n}\n\nvoid ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/)\n{\n    if (item.getTypeName() == typeid(ESM::Light).name())\n    {\n        const ESM::Light* light = item.get<ESM::Light>()->mBase;\n        if (!(light->mData.mFlags & ESM::Light::Carry))\n        {\n            addHiddenItemLight(item, light);\n        }\n    }\n\n    if (!mPtr.getClass().hasInventoryStore(mPtr))\n        return;\n\n    // If the count of equipped ammo or throwing weapon was changed, we should update quiver\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())\n        return;\n\n    MWWorld::ConstContainerStoreIterator ammo = inv.end();\n    int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;\n    if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)\n        ammo = weapon;\n    else\n        ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n\n    if(ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId())\n        updateQuiver();\n}\n\nvoid ActorAnimation::itemRemoved(const MWWorld::ConstPtr& item, int /*count*/)\n{\n    if (item.getTypeName() == typeid(ESM::Light).name())\n    {\n        ItemLightMap::iterator iter = mItemLights.find(item);\n        if (iter != mItemLights.end())\n        {\n            if (!item.getRefData().getCount())\n            {\n                removeHiddenItemLight(item);\n            }\n        }\n    }\n\n    if (!mPtr.getClass().hasInventoryStore(mPtr))\n        return;\n\n    // If the count of equipped ammo or throwing weapon was changed, we should update quiver\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())\n        return;\n\n    MWWorld::ConstContainerStoreIterator ammo = inv.end();\n    int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;\n    if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)\n        ammo = weapon;\n    else\n        ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n\n    if(ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId())\n        updateQuiver();\n}\n\nvoid ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight)\n{\n    if (mItemLights.find(item) != mItemLights.end())\n        return;\n\n    bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();\n\n    osg::Vec4f ambient(1,1,1,1);\n    osg::ref_ptr<SceneUtil::LightSource> lightSource = SceneUtil::createLightSource(esmLight, Mask_Lighting, exterior, ambient);\n\n    mInsert->addChild(lightSource);\n\n    if (mLightListCallback && mPtr == MWMechanics::getPlayer())\n        mLightListCallback->getIgnoredLightSources().insert(lightSource.get());\n\n    mItemLights.insert(std::make_pair(item, lightSource));\n}\n\nvoid ActorAnimation::removeHiddenItemLight(const MWWorld::ConstPtr& item)\n{\n    ItemLightMap::iterator iter = mItemLights.find(item);\n    if (iter == mItemLights.end())\n        return;\n\n    if (mLightListCallback && mPtr == MWMechanics::getPlayer())\n    {\n        std::set<SceneUtil::LightSource*>::iterator ignoredIter = mLightListCallback->getIgnoredLightSources().find(iter->second.get());\n        if (ignoredIter != mLightListCallback->getIgnoredLightSources().end())\n            mLightListCallback->getIgnoredLightSources().erase(ignoredIter);\n    }\n\n    mInsert->removeChild(iter->second);\n    mItemLights.erase(iter);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/actoranimation.hpp",
    "content": "#ifndef GAME_RENDER_ACTORANIMATION_H\n#define GAME_RENDER_ACTORANIMATION_H\n\n#include <map>\n\n#include <osg/ref_ptr>\n\n#include \"../mwworld/containerstore.hpp\"\n\n#include \"animation.hpp\"\n\nnamespace osg\n{\n    class Node;\n}\n\nnamespace MWWorld\n{\n    class ConstPtr;\n}\n\nnamespace SceneUtil\n{\n    class LightSource;\n    class LightListCallback;\n}\n\nnamespace MWRender\n{\n\nclass ActorAnimation : public Animation, public MWWorld::ContainerStoreListener\n{\n    public:\n        ActorAnimation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem);\n        virtual ~ActorAnimation();\n\n        void itemAdded(const MWWorld::ConstPtr& item, int count) override;\n        void itemRemoved(const MWWorld::ConstPtr& item, int count) override;\n        virtual bool isArrowAttached() const { return false; }\n        bool useShieldAnimations() const override;\n        bool updateCarriedLeftVisible(const int weaptype) const override;\n\n    protected:\n        osg::Group* getBoneByName(const std::string& boneName);\n        virtual void updateHolsteredWeapon(bool showHolsteredWeapons);\n        virtual void updateHolsteredShield(bool showCarriedLeft);\n        virtual void updateQuiver();\n        virtual std::string getShieldMesh(MWWorld::ConstPtr shield) const;\n        virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);\n        virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);\n        virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename)\n        {\n            osg::Vec4f stubColor = osg::Vec4f(0,0,0,0);\n            return attachMesh(model, bonename, false, &stubColor);\n        };\n\n        PartHolderPtr mScabbard;\n        PartHolderPtr mHolsteredShield;\n\n    private:\n        void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight);\n        void removeHiddenItemLight(const MWWorld::ConstPtr& item);\n        void resetControllers(osg::Node* node);\n\n        typedef std::map<MWWorld::ConstPtr, osg::ref_ptr<SceneUtil::LightSource> > ItemLightMap;\n        ItemLightMap mItemLights;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/actorspaths.cpp",
    "content": "#include \"actorspaths.hpp\"\n#include \"vismask.hpp\"\n\n#include <components/sceneutil/agentpath.hpp>\n\n#include <osg/PositionAttitudeTransform>\n\nnamespace MWRender\n{\n    ActorsPaths::ActorsPaths(const osg::ref_ptr<osg::Group>& root, bool enabled)\n        : mRootNode(root)\n        , mEnabled(enabled)\n    {\n    }\n\n    ActorsPaths::~ActorsPaths()\n    {\n        if (mEnabled)\n            disable();\n    }\n\n    bool ActorsPaths::toggle()\n    {\n        if (mEnabled)\n            disable();\n        else\n            enable();\n\n        return mEnabled;\n    }\n\n    void ActorsPaths::update(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,\n            const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end,\n            const DetourNavigator::Settings& settings)\n    {\n        if (!mEnabled)\n            return;\n\n        const auto group = mGroups.find(actor);\n        if (group != mGroups.end())\n            mRootNode->removeChild(group->second);\n\n        const auto newGroup = SceneUtil::createAgentPathGroup(path, halfExtents, start, end, settings);\n        if (newGroup)\n        {\n            newGroup->setNodeMask(Mask_Debug);\n            mRootNode->addChild(newGroup);\n            mGroups[actor] = newGroup;\n        }\n    }\n\n    void ActorsPaths::remove(const MWWorld::ConstPtr& actor)\n    {\n        const auto group = mGroups.find(actor);\n        if (group != mGroups.end())\n        {\n            mRootNode->removeChild(group->second);\n            mGroups.erase(group);\n        }\n    }\n\n    void ActorsPaths::removeCell(const MWWorld::CellStore* const store)\n    {\n        for (auto it = mGroups.begin(); it != mGroups.end(); )\n        {\n            if (it->first.getCell() == store)\n            {\n                mRootNode->removeChild(it->second);\n                it = mGroups.erase(it);\n            }\n            else\n                ++it;\n        }\n    }\n\n    void ActorsPaths::updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated)\n    {\n        const auto it = mGroups.find(old);\n        if (it == mGroups.end())\n            return;\n        auto group = std::move(it->second);\n        mGroups.erase(it);\n        mGroups.insert(std::make_pair(updated, std::move(group)));\n    }\n\n    void ActorsPaths::enable()\n    {\n        std::for_each(mGroups.begin(), mGroups.end(),\n            [&] (const Groups::value_type& v) { mRootNode->addChild(v.second); });\n        mEnabled = true;\n    }\n\n    void ActorsPaths::disable()\n    {\n        std::for_each(mGroups.begin(), mGroups.end(),\n            [&] (const Groups::value_type& v) { mRootNode->removeChild(v.second); });\n        mEnabled = false;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/actorspaths.hpp",
    "content": "#ifndef OPENMW_MWRENDER_AGENTSPATHS_H\n#define OPENMW_MWRENDER_AGENTSPATHS_H\n\n#include <apps/openmw/mwworld/ptr.hpp>\n\n#include <components/detournavigator/navigator.hpp>\n\n#include <osg/ref_ptr>\n\n#include <unordered_map>\n#include <deque>\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace MWRender\n{\n    class ActorsPaths\n    {\n    public:\n        ActorsPaths(const osg::ref_ptr<osg::Group>& root, bool enabled);\n        ~ActorsPaths();\n\n        bool toggle();\n\n        void update(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,\n                const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end,\n                const DetourNavigator::Settings& settings);\n\n        void remove(const MWWorld::ConstPtr& actor);\n\n        void removeCell(const MWWorld::CellStore* const store);\n\n        void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated);\n\n        void enable();\n\n        void disable();\n\n    private:\n        using Groups = std::map<MWWorld::ConstPtr, osg::ref_ptr<osg::Group>>;\n\n        osg::ref_ptr<osg::Group> mRootNode;\n        Groups mGroups;\n        bool mEnabled;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/animation.cpp",
    "content": "#include \"animation.hpp\"\n\n#include <iomanip>\n#include <limits>\n\n#include <osg/MatrixTransform>\n#include <osg/BlendFunc>\n#include <osg/Material>\n#include <osg/PositionAttitudeTransform>\n#include <osg/Switch>\n\n#include <osgParticle/ParticleSystem>\n#include <osgParticle/ParticleProcessor>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/keyframemanager.hpp>\n\n#include <components/misc/constants.hpp>\n#include <components/misc/resourcehelpers.hpp>\n\n#include <components/sceneutil/keyframe.hpp>\n\n#include <components/vfs/manager.hpp>\n\n#include <components/sceneutil/actorutil.hpp>\n#include <components/sceneutil/statesetupdater.hpp>\n#include <components/sceneutil/visitor.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n#include <components/sceneutil/lightutil.hpp>\n#include <components/sceneutil/skeleton.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/sceneutil/util.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"../mwmechanics/character.hpp\" // FIXME: for MWMechanics::Priority\n\n#include \"vismask.hpp\"\n#include \"util.hpp\"\n#include \"rotatecontroller.hpp\"\n\nnamespace\n{\n\n    /// Removes all particle systems and related nodes in a subgraph.\n    class RemoveParticlesVisitor : public osg::NodeVisitor\n    {\n    public:\n        RemoveParticlesVisitor()\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        { }\n\n        void apply(osg::Node &node) override\n        {\n            if (dynamic_cast<osgParticle::ParticleProcessor*>(&node))\n                mToRemove.emplace_back(&node);\n\n            traverse(node);\n        }\n\n        void apply(osg::Drawable& drw) override\n        {\n            if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(&drw))\n                mToRemove.emplace_back(partsys);\n        }\n\n        void remove()\n        {\n            for (osg::Node* node : mToRemove)\n            {\n                // FIXME: a Drawable might have more than one parent\n                if (node->getNumParents())\n                    node->getParent(0)->removeChild(node);\n            }\n            mToRemove.clear();\n        }\n\n    private:\n        std::vector<osg::ref_ptr<osg::Node> > mToRemove;\n    };\n\n    class DayNightCallback : public osg::NodeCallback\n    {\n    public:\n        DayNightCallback() : mCurrentState(0)\n        {\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            unsigned int state = MWBase::Environment::get().getWorld()->getNightDayMode();\n            const unsigned int newState = node->asGroup()->getNumChildren() > state ? state : 0;\n\n            if (newState != mCurrentState)\n            {\n                mCurrentState = newState;\n                node->asSwitch()->setSingleChildOn(mCurrentState);\n            }\n\n            traverse(node, nv);\n        }\n\n    private:\n        unsigned int mCurrentState;\n    };\n\n    class AddSwitchCallbacksVisitor : public osg::NodeVisitor\n    {\n    public:\n        AddSwitchCallbacksVisitor()\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        { }\n\n        void apply(osg::Switch &switchNode) override\n        {\n            if (switchNode.getName() == Constants::NightDayLabel)\n                switchNode.addUpdateCallback(new DayNightCallback());\n\n            traverse(switchNode);\n        }\n    };\n\n    class HarvestVisitor : public osg::NodeVisitor\n    {\n    public:\n        HarvestVisitor()\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        {\n        }\n\n        void apply(osg::Switch& node) override\n        {\n            if (node.getName() == Constants::HerbalismLabel)\n            {\n                node.setSingleChildOn(1);\n            }\n\n            traverse(node);\n        }\n    };\n\n    float calcAnimVelocity(const SceneUtil::TextKeyMap& keys, SceneUtil::KeyframeController *nonaccumctrl,\n                           const osg::Vec3f& accum, const std::string &groupname)\n    {\n        const std::string start = groupname+\": start\";\n        const std::string loopstart = groupname+\": loop start\";\n        const std::string loopstop = groupname+\": loop stop\";\n        const std::string stop = groupname+\": stop\";\n        float starttime = std::numeric_limits<float>::max();\n        float stoptime = 0.0f;\n\n        // Pick the last Loop Stop key and the last Loop Start key.\n        // This is required because of broken text keys in AshVampire.nif.\n        // It has *two* WalkForward: Loop Stop keys at different times, the first one is used for stopping playback\n        // but the animation velocity calculation uses the second one.\n        // As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated,\n        // because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough.\n        auto keyiter = keys.rbegin();\n        while(keyiter != keys.rend())\n        {\n            if(keyiter->second == start || keyiter->second == loopstart)\n            {\n                starttime = keyiter->first;\n                break;\n            }\n            ++keyiter;\n        }\n        keyiter = keys.rbegin();\n        while(keyiter != keys.rend())\n        {\n            if (keyiter->second == stop)\n                stoptime = keyiter->first;\n            else if (keyiter->second == loopstop)\n            {\n                stoptime = keyiter->first;\n                break;\n            }\n            ++keyiter;\n        }\n\n        if(stoptime > starttime)\n        {\n            osg::Vec3f startpos = osg::componentMultiply(nonaccumctrl->getTranslation(starttime), accum);\n            osg::Vec3f endpos = osg::componentMultiply(nonaccumctrl->getTranslation(stoptime), accum);\n\n            return (startpos-endpos).length() / (stoptime - starttime);\n        }\n\n        return 0.0f;\n    }\n\n    /// @brief Base class for visitors that remove nodes from a scene graph.\n    /// Subclasses need to fill the mToRemove vector.\n    /// To use, node->accept(removeVisitor); removeVisitor.remove();\n    class RemoveVisitor : public osg::NodeVisitor\n    {\n    public:\n        RemoveVisitor()\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        {\n        }\n\n        void remove()\n        {\n            for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)\n            {\n                if (!it->second->removeChild(it->first))\n                    Log(Debug::Error) << \"Error removing \" << it->first->getName();\n            }\n        }\n\n    protected:\n        // <node to remove, parent node to remove it from>\n        typedef std::vector<std::pair<osg::Node*, osg::Group*> > RemoveVec;\n        std::vector<std::pair<osg::Node*, osg::Group*> > mToRemove;\n    };\n\n    class GetExtendedBonesVisitor : public osg::NodeVisitor\n    {\n    public:\n        GetExtendedBonesVisitor()\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        {\n        }\n\n        void apply(osg::Node& node) override\n        {\n            if (SceneUtil::hasUserDescription(&node, \"CustomBone\"))\n            {\n                mFoundBones.emplace_back(&node, node.getParent(0));\n                return;\n            }\n\n            traverse(node);\n        }\n\n        std::vector<std::pair<osg::Node*, osg::Group*> > mFoundBones;\n    };\n\n    class RemoveFinishedCallbackVisitor : public RemoveVisitor\n    {\n    public:\n        bool mHasMagicEffects;\n\n        RemoveFinishedCallbackVisitor()\n            : RemoveVisitor()\n            , mHasMagicEffects(false)\n        {\n        }\n\n        void apply(osg::Node &node) override\n        {\n            traverse(node);\n        }\n\n        void apply(osg::Group &group) override\n        {\n            traverse(group);\n\n            osg::Callback* callback = group.getUpdateCallback();\n            if (callback)\n            {\n                // We should remove empty transformation nodes and finished callbacks here\n                MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);\n                if (vfxCallback)\n                {\n                    if (vfxCallback->mFinished)\n                        mToRemove.emplace_back(group.asNode(), group.getParent(0));\n                    else\n                        mHasMagicEffects = true;\n                }\n            }\n        }\n\n        void apply(osg::MatrixTransform &node) override\n        {\n            traverse(node);\n        }\n\n        void apply(osg::Geometry&) override\n        {\n        }\n    };\n\n    class RemoveCallbackVisitor : public RemoveVisitor\n    {\n    public:\n        bool mHasMagicEffects;\n\n        RemoveCallbackVisitor()\n            : RemoveVisitor()\n            , mHasMagicEffects(false)\n            , mEffectId(-1)\n        {\n        }\n\n        RemoveCallbackVisitor(int effectId)\n            : RemoveVisitor()\n            , mHasMagicEffects(false)\n            , mEffectId(effectId)\n        {\n        }\n\n        void apply(osg::Node &node) override\n        {\n            traverse(node);\n        }\n\n        void apply(osg::Group &group) override\n        {\n            traverse(group);\n\n            osg::Callback* callback = group.getUpdateCallback();\n            if (callback)\n            {\n                MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);\n                if (vfxCallback)\n                {\n                    bool toRemove = mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId;\n                    if (toRemove)\n                        mToRemove.emplace_back(group.asNode(), group.getParent(0));\n                    else\n                        mHasMagicEffects = true;\n                }\n            }\n        }\n\n        void apply(osg::MatrixTransform &node) override\n        {\n            traverse(node);\n        }\n\n        void apply(osg::Geometry&) override\n        {\n        }\n\n    private:\n        int mEffectId;\n    };\n\n    class FindVfxCallbacksVisitor : public osg::NodeVisitor\n    {\n    public:\n\n        std::vector<MWRender::UpdateVfxCallback*> mCallbacks;\n\n        FindVfxCallbacksVisitor()\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mEffectId(-1)\n        {\n        }\n\n        FindVfxCallbacksVisitor(int effectId)\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mEffectId(effectId)\n        {\n        }\n\n        void apply(osg::Node &node) override\n        {\n            traverse(node);\n        }\n\n        void apply(osg::Group &group) override\n        {\n            osg::Callback* callback = group.getUpdateCallback();\n            if (callback)\n            {\n                MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);\n                if (vfxCallback)\n                {\n                    if (mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId)\n                    {\n                        mCallbacks.push_back(vfxCallback);\n                    }\n                }\n            }\n            traverse(group);\n        }\n\n        void apply(osg::MatrixTransform &node) override\n        {\n            traverse(node);\n        }\n\n        void apply(osg::Geometry&) override\n        {\n        }\n\n    private:\n        int mEffectId;\n    };\n\n    // Removes all drawables from a graph.\n    class CleanObjectRootVisitor : public RemoveVisitor\n    {\n    public:\n        void apply(osg::Drawable& drw) override\n        {\n            applyDrawable(drw);\n        }\n\n        void apply(osg::Group& node) override\n        {\n            applyNode(node);\n        }\n        void apply(osg::MatrixTransform& node) override\n        {\n            applyNode(node);\n        }\n        void apply(osg::Node& node) override\n        {\n            applyNode(node);\n        }\n\n        void applyNode(osg::Node& node)\n        {\n            if (node.getStateSet())\n                node.setStateSet(nullptr);\n\n            if (node.getNodeMask() == 0x1 && node.getNumParents() == 1)\n                mToRemove.emplace_back(&node, node.getParent(0));\n            else\n                traverse(node);\n        }\n        void applyDrawable(osg::Node& node)\n        {\n            osg::NodePath::iterator parent = getNodePath().end()-2;\n            // We know that the parent is a Group because only Groups can have children.\n            osg::Group* parentGroup = static_cast<osg::Group*>(*parent);\n\n            // Try to prune nodes that would be empty after the removal\n            if (parent != getNodePath().begin())\n            {\n                // This could be extended to remove the parent's parent, and so on if they are empty as well.\n                // But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance.\n                osg::Group* parentParent = static_cast<osg::Group*>(*(parent - 1));\n                if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC)\n                {\n                    mToRemove.emplace_back(parentGroup, parentParent);\n                    return;\n                }\n            }\n\n            mToRemove.emplace_back(&node, parentGroup);\n        }\n    };\n\n    class RemoveTriBipVisitor : public RemoveVisitor\n    {\n    public:\n        void apply(osg::Drawable& drw) override\n        {\n            applyImpl(drw);\n        }\n\n        void apply(osg::Group& node) override\n        {\n            traverse(node);\n        }\n        void apply(osg::MatrixTransform& node) override\n        {\n            traverse(node);\n        }\n\n        void applyImpl(osg::Node& node)\n        {\n            const std::string toFind = \"tri bip\";\n            if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0)\n            {\n                osg::Group* parent = static_cast<osg::Group*>(*(getNodePath().end()-2));\n                // Not safe to remove in apply(), since the visitor is still iterating the child list\n                mToRemove.emplace_back(&node, parent);\n            }\n        }\n    };\n}\n\nnamespace MWRender\n{\n    class TransparencyUpdater : public SceneUtil::StateSetUpdater\n    {\n    public:\n        TransparencyUpdater(const float alpha)\n            : mAlpha(alpha)\n        {\n        }\n\n        void setAlpha(const float alpha)\n        {\n            mAlpha = alpha;\n        }\n\n        void setLightSource(const osg::ref_ptr<SceneUtil::LightSource>& lightSource)\n        {\n            mLightSource = lightSource;\n        }\n\n    protected:\n        void setDefaults(osg::StateSet* stateset) override\n        {\n            osg::BlendFunc* blendfunc (new osg::BlendFunc);\n            stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n\n            stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n            stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);\n\n            // FIXME: overriding diffuse/ambient/emissive colors\n            osg::Material* material = new osg::Material;\n            material->setColorMode(osg::Material::OFF);\n            material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,mAlpha));\n            material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n            stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n            stateset->addUniform(new osg::Uniform(\"colorMode\", 0), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n        }\n\n        void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override\n        {\n            osg::Material* material = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));\n            material->setAlpha(osg::Material::FRONT_AND_BACK, mAlpha);\n            if (mLightSource)\n                mLightSource->setActorFade(mAlpha);\n        }\n\n    private:\n        float mAlpha;\n        osg::ref_ptr<SceneUtil::LightSource> mLightSource;\n    };\n\n    struct Animation::AnimSource\n    {\n        osg::ref_ptr<const SceneUtil::KeyframeHolder> mKeyframes;\n\n        typedef std::map<std::string, osg::ref_ptr<SceneUtil::KeyframeController> > ControllerMap;\n\n        ControllerMap mControllerMap[Animation::sNumBlendMasks];\n\n        const SceneUtil::TextKeyMap& getTextKeys() const;\n    };\n\n    void UpdateVfxCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)\n    {\n        traverse(node, nv);\n\n        if (mFinished)\n            return;\n\n        double newTime = nv->getFrameStamp()->getSimulationTime();\n        if (mStartingTime == 0)\n        {\n            mStartingTime = newTime;\n            return;\n        }\n\n        double duration = newTime - mStartingTime;\n        mStartingTime = newTime;\n\n        mParams.mAnimTime->addTime(duration);\n        if (mParams.mAnimTime->getTime() >= mParams.mMaxControllerLength)\n        {\n            if (mParams.mLoop)\n            {\n                // Start from the beginning again; carry over the remainder\n                // Not sure if this is actually needed, the controller function might already handle loops\n                float remainder = mParams.mAnimTime->getTime() - mParams.mMaxControllerLength;\n                mParams.mAnimTime->resetTime(remainder);\n            }\n            else\n            {\n                // Hide effect immediately\n                node->setNodeMask(0);\n                mFinished = true;\n            }\n        }\n    }\n\n    class ResetAccumRootCallback : public osg::NodeCallback\n    {\n    public:\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);\n\n            osg::Matrix mat = transform->getMatrix();\n            osg::Vec3f position = mat.getTrans();\n            position = osg::componentMultiply(mResetAxes, position);\n            mat.setTrans(position);\n            transform->setMatrix(mat);\n\n            traverse(node, nv);\n        }\n\n        void setAccumulate(const osg::Vec3f& accumulate)\n        {\n            // anything that accumulates (1.f) should be reset in the callback to (0.f)\n            mResetAxes.x() = accumulate.x() != 0.f ? 0.f : 1.f;\n            mResetAxes.y() = accumulate.y() != 0.f ? 0.f : 1.f;\n            mResetAxes.z() = accumulate.z() != 0.f ? 0.f : 1.f;\n        }\n\n    private:\n        osg::Vec3f mResetAxes;\n    };\n\n    Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem)\n        : mInsert(parentNode)\n        , mSkeleton(nullptr)\n        , mNodeMapCreated(false)\n        , mPtr(ptr)\n        , mResourceSystem(resourceSystem)\n        , mAccumulate(1.f, 1.f, 0.f)\n        , mTextKeyListener(nullptr)\n        , mHeadYawRadians(0.f)\n        , mHeadPitchRadians(0.f)\n        , mUpperBodyYawRadians(0.f)\n        , mLegsYawRadians(0.f)\n        , mBodyPitchRadians(0.f)\n        , mHasMagicEffects(false)\n        , mAlpha(1.f)\n    {\n        for(size_t i = 0;i < sNumBlendMasks;i++)\n            mAnimationTimePtr[i].reset(new AnimationTime);\n\n        mLightListCallback = new SceneUtil::LightListCallback;\n    }\n\n    Animation::~Animation()\n    {\n        Animation::setLightEffect(0.f);\n\n        if (mObjectRoot)\n            mInsert->removeChild(mObjectRoot);\n    }\n\n    MWWorld::ConstPtr Animation::getPtr() const\n    {\n        return mPtr;\n    }\n\n    MWWorld::Ptr Animation::getPtr()\n    {\n        return mPtr;\n    }\n\n    void Animation::setActive(int active)\n    {\n        if (mSkeleton)\n            mSkeleton->setActive(static_cast<SceneUtil::Skeleton::ActiveType>(active));\n    }\n\n    void Animation::updatePtr(const MWWorld::Ptr &ptr)\n    {\n        mPtr = ptr;\n    }\n\n    void Animation::setAccumulation(const osg::Vec3f& accum)\n    {\n        mAccumulate = accum;\n\n        if (mResetAccumRootCallback)\n            mResetAccumRootCallback->setAccumulate(mAccumulate);\n    }\n\n    size_t Animation::detectBlendMask(const osg::Node* node) const\n    {\n        static const char sBlendMaskRoots[sNumBlendMasks][32] = {\n            \"\", /* Lower body / character root */\n            \"Bip01 Spine1\", /* Torso */\n            \"Bip01 L Clavicle\", /* Left arm */\n            \"Bip01 R Clavicle\", /* Right arm */\n        };\n\n        while(node != mObjectRoot)\n        {\n            const std::string &name = node->getName();\n            for(size_t i = 1;i < sNumBlendMasks;i++)\n            {\n                if(name == sBlendMaskRoots[i])\n                    return i;\n            }\n\n            assert(node->getNumParents() > 0);\n\n            node = node->getParent(0);\n        }\n\n        return 0;\n    }\n\n    const SceneUtil::TextKeyMap &Animation::AnimSource::getTextKeys() const\n    {\n        return mKeyframes->mTextKeys;\n    }\n\n    void Animation::loadAllAnimationsInFolder(const std::string &model, const std::string &baseModel)\n    {\n        const std::map<std::string, VFS::File*>& index = mResourceSystem->getVFS()->getIndex();\n\n        std::string animationPath = model;\n        if (animationPath.find(\"meshes\") == 0)\n        {\n            animationPath.replace(0, 6, \"animations\");\n        }\n        animationPath.replace(animationPath.size()-3, 3, \"/\");\n\n        mResourceSystem->getVFS()->normalizeFilename(animationPath);\n\n        std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(animationPath);\n        while (found != index.end())\n        {\n            const std::string& name = found->first;\n            if (name.size() >= animationPath.size() && name.substr(0, animationPath.size()) == animationPath)\n            {\n                size_t pos = name.find_last_of('.');\n                if (pos != std::string::npos && name.compare(pos, name.size()-pos, \".kf\") == 0)\n                    addSingleAnimSource(name, baseModel);\n            }\n            else\n                break;\n            ++found;\n        }\n    }\n\n    void Animation::addAnimSource(const std::string &model, const std::string& baseModel)\n    {\n        std::string kfname = model;\n        Misc::StringUtils::lowerCaseInPlace(kfname);\n\n        if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, \".nif\") == 0)\n            kfname.replace(kfname.size()-4, 4, \".kf\");\n\n        addSingleAnimSource(kfname, baseModel);\n\n        static const bool useAdditionalSources = Settings::Manager::getBool (\"use additional anim sources\", \"Game\");\n        if (useAdditionalSources)\n            loadAllAnimationsInFolder(kfname, baseModel);\n    }\n\n    void Animation::addSingleAnimSource(const std::string &kfname, const std::string& baseModel)\n    {\n        if(!mResourceSystem->getVFS()->exists(kfname))\n            return;\n\n        std::shared_ptr<AnimSource> animsrc;\n        animsrc.reset(new AnimSource);\n        animsrc->mKeyframes = mResourceSystem->getKeyframeManager()->get(kfname);\n\n        if (!animsrc->mKeyframes || animsrc->mKeyframes->mTextKeys.empty() || animsrc->mKeyframes->mKeyframeControllers.empty())\n            return;\n\n        const NodeMap& nodeMap = getNodeMap();\n\n        for (SceneUtil::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin();\n             it != animsrc->mKeyframes->mKeyframeControllers.end(); ++it)\n        {\n            std::string bonename = Misc::StringUtils::lowerCase(it->first);\n            NodeMap::const_iterator found = nodeMap.find(bonename);\n            if (found == nodeMap.end())\n            {\n                Log(Debug::Warning) << \"Warning: addAnimSource: can't find bone '\" + bonename << \"' in \" << baseModel << \" (referenced by \" << kfname << \")\";\n                continue;\n            }\n\n            osg::Node* node = found->second;\n\n            size_t blendMask = detectBlendMask(node);\n\n            // clone the controller, because each Animation needs its own ControllerSource\n            osg::ref_ptr<SceneUtil::KeyframeController> cloned = osg::clone(it->second.get(), osg::CopyOp::SHALLOW_COPY);\n            cloned->setSource(mAnimationTimePtr[blendMask]);\n\n            animsrc->mControllerMap[blendMask].insert(std::make_pair(bonename, cloned));\n        }\n\n        mAnimSources.push_back(animsrc);\n\n        SceneUtil::AssignControllerSourcesVisitor assignVisitor(mAnimationTimePtr[0]);\n        mObjectRoot->accept(assignVisitor);\n\n        if (!mAccumRoot)\n        {\n            NodeMap::const_iterator found = nodeMap.find(\"bip01\");\n            if (found == nodeMap.end())\n                found = nodeMap.find(\"root bone\");\n\n            if (found != nodeMap.end())\n                mAccumRoot = found->second;\n        }\n    }\n\n    void Animation::clearAnimSources()\n    {\n        mStates.clear();\n\n        for(size_t i = 0;i < sNumBlendMasks;i++)\n            mAnimationTimePtr[i]->setTimePtr(std::shared_ptr<float>());\n\n        mAccumCtrl = nullptr;\n\n        mAnimSources.clear();\n\n        mAnimVelocities.clear();\n    }\n\n    bool Animation::hasAnimation(const std::string &anim) const\n    {\n        AnimSourceList::const_iterator iter(mAnimSources.begin());\n        for(;iter != mAnimSources.end();++iter)\n        {\n            const SceneUtil::TextKeyMap &keys = (*iter)->getTextKeys();\n            if (keys.hasGroupStart(anim))\n                return true;\n        }\n\n        return false;\n    }\n\n    float Animation::getStartTime(const std::string &groupname) const\n    {\n        for(AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)\n        {\n            const SceneUtil::TextKeyMap &keys = (*iter)->getTextKeys();\n\n            const auto found = keys.findGroupStart(groupname);\n            if(found != keys.end())\n                return found->first;\n        }\n        return -1.f;\n    }\n\n    float Animation::getTextKeyTime(const std::string &textKey) const\n    {\n        for(AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)\n        {\n            const SceneUtil::TextKeyMap &keys = (*iter)->getTextKeys();\n\n            for(auto iterKey = keys.begin(); iterKey != keys.end(); ++iterKey)\n            {\n                if(iterKey->second.compare(0, textKey.size(), textKey) == 0)\n                    return iterKey->first;\n            }\n        }\n\n        return -1.f;\n    }\n\n    void Animation::handleTextKey(AnimState &state, const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key,\n                       const SceneUtil::TextKeyMap& map)\n    {\n        const std::string &evt = key->second;\n\n        size_t off = groupname.size()+2;\n        size_t len = evt.size() - off;\n\n        if(evt.compare(0, groupname.size(), groupname) == 0 &&\n           evt.compare(groupname.size(), 2, \": \") == 0)\n        {\n            if(evt.compare(off, len, \"loop start\") == 0)\n                state.mLoopStartTime = key->first;\n            else if(evt.compare(off, len, \"loop stop\") == 0)\n                state.mLoopStopTime = key->first;\n        }\n\n        if (mTextKeyListener)\n        {\n            try\n            {\n                mTextKeyListener->handleTextKey(groupname, key, map);\n            }\n            catch (std::exception& e)\n            {\n                Log(Debug::Error) << \"Error handling text key \" << evt << \": \" << e.what();\n            }\n        }\n    }\n\n    void Animation::play(const std::string &groupname, const AnimPriority& priority, int blendMask, bool autodisable, float speedmult,\n                         const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback)\n    {\n        if(!mObjectRoot || mAnimSources.empty())\n            return;\n\n        if(groupname.empty())\n        {\n            resetActiveGroups();\n            return;\n        }\n\n        AnimStateMap::iterator stateiter = mStates.begin();\n        while(stateiter != mStates.end())\n        {\n            if(stateiter->second.mPriority == priority)\n                mStates.erase(stateiter++);\n            else\n                ++stateiter;\n        }\n\n        stateiter = mStates.find(groupname);\n        if(stateiter != mStates.end())\n        {\n            stateiter->second.mPriority = priority;\n            resetActiveGroups();\n            return;\n        }\n\n        /* Look in reverse; last-inserted source has priority. */\n        AnimState state;\n        AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());\n        for(;iter != mAnimSources.rend();++iter)\n        {\n            const SceneUtil::TextKeyMap &textkeys = (*iter)->getTextKeys();\n            if(reset(state, textkeys, groupname, start, stop, startpoint, loopfallback))\n            {\n                state.mSource = *iter;\n                state.mSpeedMult = speedmult;\n                state.mLoopCount = loops;\n                state.mPlaying = (state.getTime() < state.mStopTime);\n                state.mPriority = priority;\n                state.mBlendMask = blendMask;\n                state.mAutoDisable = autodisable;\n                mStates[groupname] = state;\n\n                if (state.mPlaying)\n                {\n                    auto textkey = textkeys.lowerBound(state.getTime());\n                    while(textkey != textkeys.end() && textkey->first <= state.getTime())\n                    {\n                        handleTextKey(state, groupname, textkey, textkeys);\n                        ++textkey;\n                    }\n                }\n\n                if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0)\n                {\n                    state.mLoopCount--;\n                    state.setTime(state.mLoopStartTime);\n                    state.mPlaying = true;\n                    if(state.getTime() >= state.mLoopStopTime)\n                        break;\n\n                    auto textkey = textkeys.lowerBound(state.getTime());\n                    while(textkey != textkeys.end() && textkey->first <= state.getTime())\n                    {\n                        handleTextKey(state, groupname, textkey, textkeys);\n                        ++textkey;\n                    }\n                }\n\n                break;\n            }\n        }\n\n        resetActiveGroups();\n    }\n\n    bool Animation::reset(AnimState &state, const SceneUtil::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback)\n    {\n        // Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two\n        // separate walkforward keys, and the last one is supposed to be used.\n        auto groupend = keys.rbegin();\n        for(;groupend != keys.rend();++groupend)\n        {\n            if(groupend->second.compare(0, groupname.size(), groupname) == 0 &&\n               groupend->second.compare(groupname.size(), 2, \": \") == 0)\n                break;\n        }\n\n        std::string starttag = groupname+\": \"+start;\n        auto startkey = groupend;\n        while(startkey != keys.rend() && startkey->second != starttag)\n            ++startkey;\n        if(startkey == keys.rend() && start == \"loop start\")\n        {\n            starttag = groupname+\": start\";\n            startkey = groupend;\n            while(startkey != keys.rend() && startkey->second != starttag)\n                ++startkey;\n        }\n        if(startkey == keys.rend())\n            return false;\n\n        const std::string stoptag = groupname+\": \"+stop;\n        auto stopkey = groupend;\n        while(stopkey != keys.rend()\n              // We have to ignore extra garbage at the end.\n              // The Scrib's idle3 animation has \"Idle3: Stop.\" instead of \"Idle3: Stop\".\n              // Why, just why? :(\n              && (stopkey->second.size() < stoptag.size() || stopkey->second.compare(0,stoptag.size(), stoptag) != 0))\n            ++stopkey;\n        if(stopkey == keys.rend())\n            return false;\n\n        if(startkey->first > stopkey->first)\n            return false;\n\n        state.mStartTime = startkey->first;\n        if (loopfallback)\n        {\n            state.mLoopStartTime = startkey->first;\n            state.mLoopStopTime = stopkey->first;\n        }\n        else\n        {\n            state.mLoopStartTime = startkey->first;\n            state.mLoopStopTime = std::numeric_limits<float>::max();\n        }\n        state.mStopTime = stopkey->first;\n\n        state.setTime(state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint));\n\n        // mLoopStartTime and mLoopStopTime normally get assigned when encountering these keys while playing the animation\n        // (see handleTextKey). But if startpoint is already past these keys, or start time is == stop time, we need to assign them now.\n        const std::string loopstarttag = groupname+\": loop start\";\n        const std::string loopstoptag = groupname+\": loop stop\";\n\n        auto key = groupend;\n        for (; key != startkey && key != keys.rend(); ++key)\n        {\n            if (key->first > state.getTime())\n                continue;\n\n            if (key->second == loopstarttag)\n                state.mLoopStartTime = key->first;\n            else if (key->second == loopstoptag)\n                state.mLoopStopTime = key->first;\n        }\n\n        return true;\n    }\n\n    void Animation::setTextKeyListener(Animation::TextKeyListener *listener)\n    {\n        mTextKeyListener = listener;\n    }\n\n    const Animation::NodeMap &Animation::getNodeMap() const\n    {\n        if (!mNodeMapCreated && mObjectRoot)\n        {\n            SceneUtil::NodeMapVisitor visitor(mNodeMap);\n            mObjectRoot->accept(visitor);\n            mNodeMapCreated = true;\n        }\n        return mNodeMap;\n    }\n\n    void Animation::resetActiveGroups()\n    {\n        // remove all previous external controllers from the scene graph\n        for (auto it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it)\n        {\n            osg::Node* node = it->first;\n            node->removeUpdateCallback(it->second);\n\n            // Should be no longer needed with OSG 3.4\n            it->second->setNestedCallback(nullptr);\n        }\n\n        mActiveControllers.clear();\n\n        mAccumCtrl = nullptr;\n\n        for(size_t blendMask = 0;blendMask < sNumBlendMasks;blendMask++)\n        {\n            AnimStateMap::const_iterator active = mStates.end();\n\n            AnimStateMap::const_iterator state = mStates.begin();\n            for(;state != mStates.end();++state)\n            {\n                if(!(state->second.mBlendMask&(1<<blendMask)))\n                    continue;\n\n                if(active == mStates.end() || active->second.mPriority[(BoneGroup)blendMask] < state->second.mPriority[(BoneGroup)blendMask])\n                    active = state;\n            }\n\n            mAnimationTimePtr[blendMask]->setTimePtr(active == mStates.end() ? std::shared_ptr<float>() : active->second.mTime);\n\n            // add external controllers for the AnimSource active in this blend mask\n            if (active != mStates.end())\n            {\n                std::shared_ptr<AnimSource> animsrc = active->second.mSource;\n\n                for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[blendMask].begin(); it != animsrc->mControllerMap[blendMask].end(); ++it)\n                {\n                    osg::ref_ptr<osg::Node> node = getNodeMap().at(it->first); // this should not throw, we already checked for the node existing in addAnimSource\n\n                    node->addUpdateCallback(it->second);\n                    mActiveControllers.emplace_back(node, it->second);\n\n                    if (blendMask == 0 && node == mAccumRoot)\n                    {\n                        mAccumCtrl = it->second;\n\n                        // make sure reset is last in the chain of callbacks\n                        if (!mResetAccumRootCallback)\n                        {\n                            mResetAccumRootCallback = new ResetAccumRootCallback;\n                            mResetAccumRootCallback->setAccumulate(mAccumulate);\n                        }\n                        mAccumRoot->addUpdateCallback(mResetAccumRootCallback);\n                        mActiveControllers.emplace_back(mAccumRoot, mResetAccumRootCallback);\n                    }\n                }\n            }\n        }\n        addControllers();\n    }\n\n    void Animation::adjustSpeedMult(const std::string &groupname, float speedmult)\n    {\n        AnimStateMap::iterator state(mStates.find(groupname));\n        if(state != mStates.end())\n            state->second.mSpeedMult = speedmult;\n    }\n\n    bool Animation::isPlaying(const std::string &groupname) const\n    {\n        AnimStateMap::const_iterator state(mStates.find(groupname));\n        if(state != mStates.end())\n            return state->second.mPlaying;\n        return false;\n    }\n\n    bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult) const\n    {\n        AnimStateMap::const_iterator iter = mStates.find(groupname);\n        if(iter == mStates.end())\n        {\n            if(complete) *complete = 0.0f;\n            if(speedmult) *speedmult = 0.0f;\n            return false;\n        }\n\n        if(complete)\n        {\n            if(iter->second.mStopTime > iter->second.mStartTime)\n                *complete = (iter->second.getTime() - iter->second.mStartTime) /\n                            (iter->second.mStopTime - iter->second.mStartTime);\n            else\n                *complete = (iter->second.mPlaying ? 0.0f : 1.0f);\n        }\n        if(speedmult) *speedmult = iter->second.mSpeedMult;\n        return true;\n    }\n\n    float Animation::getCurrentTime(const std::string &groupname) const\n    {\n        AnimStateMap::const_iterator iter = mStates.find(groupname);\n        if(iter == mStates.end())\n            return -1.f;\n\n        return iter->second.getTime();\n    }\n\n    size_t Animation::getCurrentLoopCount(const std::string& groupname) const\n    {\n        AnimStateMap::const_iterator iter = mStates.find(groupname);\n        if(iter == mStates.end())\n            return 0;\n\n        return iter->second.mLoopCount;\n    }\n\n    void Animation::disable(const std::string &groupname)\n    {\n        AnimStateMap::iterator iter = mStates.find(groupname);\n        if(iter != mStates.end())\n            mStates.erase(iter);\n        resetActiveGroups();\n    }\n\n    float Animation::getVelocity(const std::string &groupname) const\n    {\n        if (!mAccumRoot)\n            return 0.0f;\n\n        std::map<std::string, float>::const_iterator found = mAnimVelocities.find(groupname);\n        if (found != mAnimVelocities.end())\n            return found->second;\n\n        // Look in reverse; last-inserted source has priority.\n        AnimSourceList::const_reverse_iterator animsrc(mAnimSources.rbegin());\n        for(;animsrc != mAnimSources.rend();++animsrc)\n        {\n            const SceneUtil::TextKeyMap &keys = (*animsrc)->getTextKeys();\n            if (keys.hasGroupStart(groupname))\n                break;\n        }\n        if(animsrc == mAnimSources.rend())\n            return 0.0f;\n\n        float velocity = 0.0f;\n        const SceneUtil::TextKeyMap &keys = (*animsrc)->getTextKeys();\n\n        const AnimSource::ControllerMap& ctrls = (*animsrc)->mControllerMap[0];\n        for (AnimSource::ControllerMap::const_iterator it = ctrls.begin(); it != ctrls.end(); ++it)\n        {\n            if (Misc::StringUtils::ciEqual(it->first, mAccumRoot->getName()))\n            {\n                velocity = calcAnimVelocity(keys, it->second, mAccumulate, groupname);\n                break;\n            }\n        }\n\n        // If there's no velocity, keep looking\n        if(!(velocity > 1.0f))\n        {\n            AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin();\n            while(*animiter != *animsrc)\n                ++animiter;\n\n            while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend())\n            {\n                const SceneUtil::TextKeyMap &keys2 = (*animiter)->getTextKeys();\n\n                const AnimSource::ControllerMap& ctrls2 = (*animiter)->mControllerMap[0];\n                for (AnimSource::ControllerMap::const_iterator it = ctrls2.begin(); it != ctrls2.end(); ++it)\n                {\n                    if (Misc::StringUtils::ciEqual(it->first, mAccumRoot->getName()))\n                    {\n                        velocity = calcAnimVelocity(keys2, it->second, mAccumulate, groupname);\n                        break;\n                    }\n                }\n            }\n        }\n\n        mAnimVelocities.insert(std::make_pair(groupname, velocity));\n\n        return velocity;\n    }\n\n    void Animation::updatePosition(float oldtime, float newtime, osg::Vec3f& position)\n    {\n        // Get the difference from the last update, and move the position\n        osg::Vec3f off = osg::componentMultiply(mAccumCtrl->getTranslation(newtime), mAccumulate);\n        position += off - osg::componentMultiply(mAccumCtrl->getTranslation(oldtime), mAccumulate);\n    }\n\n    osg::Vec3f Animation::runAnimation(float duration)\n    {\n        // If we have scripted animations, play only them\n        bool hasScriptedAnims = false;\n        for (AnimStateMap::iterator stateiter = mStates.begin(); stateiter != mStates.end(); stateiter++)\n        {\n            if (stateiter->second.mPriority.contains(int(MWMechanics::Priority_Persistent)) && stateiter->second.mPlaying)\n            {\n                hasScriptedAnims = true;\n                break;\n            }\n        }\n\n        osg::Vec3f movement(0.f, 0.f, 0.f);\n        AnimStateMap::iterator stateiter = mStates.begin();\n        while(stateiter != mStates.end())\n        {\n            AnimState &state = stateiter->second;\n            if (hasScriptedAnims && !state.mPriority.contains(int(MWMechanics::Priority_Persistent)))\n            {\n                ++stateiter;\n                continue;\n            }\n\n            const SceneUtil::TextKeyMap &textkeys = state.mSource->getTextKeys();\n            auto textkey = textkeys.upperBound(state.getTime());\n\n            float timepassed = duration * state.mSpeedMult;\n            while(state.mPlaying)\n            {\n                if (!state.shouldLoop())\n                {\n                    float targetTime = state.getTime() + timepassed;\n                    if(textkey == textkeys.end() || textkey->first > targetTime)\n                    {\n                        if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr())\n                            updatePosition(state.getTime(), targetTime, movement);\n                        state.setTime(std::min(targetTime, state.mStopTime));\n                    }\n                    else\n                    {\n                        if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr())\n                            updatePosition(state.getTime(), textkey->first, movement);\n                        state.setTime(textkey->first);\n                    }\n\n                    state.mPlaying = (state.getTime() < state.mStopTime);\n                    timepassed = targetTime - state.getTime();\n\n                    while(textkey != textkeys.end() && textkey->first <= state.getTime())\n                    {\n                        handleTextKey(state, stateiter->first, textkey, textkeys);\n                        ++textkey;\n                    }\n                }\n                if(state.shouldLoop())\n                {\n                    state.mLoopCount--;\n                    state.setTime(state.mLoopStartTime);\n                    state.mPlaying = true;\n\n                    textkey = textkeys.lowerBound(state.getTime());\n                    while(textkey != textkeys.end() && textkey->first <= state.getTime())\n                    {\n                        handleTextKey(state, stateiter->first, textkey, textkeys);\n                        ++textkey;\n                    }\n\n                    if(state.getTime() >= state.mLoopStopTime)\n                        break;\n                }\n\n                if(timepassed <= 0.0f)\n                    break;\n            }\n\n            if(!state.mPlaying && state.mAutoDisable)\n            {\n                mStates.erase(stateiter++);\n\n                resetActiveGroups();\n            }\n            else\n                ++stateiter;\n        }\n\n        updateEffects();\n\n        const float epsilon = 0.001f;\n        float yawOffset = 0;\n        if (mRootController)\n        {\n            bool enable = std::abs(mLegsYawRadians) > epsilon || std::abs(mBodyPitchRadians) > epsilon;\n            mRootController->setEnabled(enable);\n            if (enable)\n            {\n                mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0,0,1)) * osg::Quat(mBodyPitchRadians, osg::Vec3f(1,0,0)));\n                yawOffset = mLegsYawRadians;\n            }\n        }\n        if (mSpineController)\n        {\n            float yaw = mUpperBodyYawRadians - yawOffset;\n            bool enable = std::abs(yaw) > epsilon;\n            mSpineController->setEnabled(enable);\n            if (enable)\n            {\n                mSpineController->setRotate(osg::Quat(yaw, osg::Vec3f(0,0,1)));\n                yawOffset = mUpperBodyYawRadians;\n            }\n        }\n        if (mHeadController)\n        {\n            float yaw = mHeadYawRadians - yawOffset;\n            bool enable = (std::abs(mHeadPitchRadians) > epsilon || std::abs(yaw) > epsilon);\n            mHeadController->setEnabled(enable);\n            if (enable)\n                mHeadController->setRotate(osg::Quat(mHeadPitchRadians, osg::Vec3f(1,0,0)) * osg::Quat(yaw, osg::Vec3f(0,0,1)));\n        }\n\n        // Scripted animations should not cause movement\n        if (hasScriptedAnims)\n            return osg::Vec3f(0, 0, 0);\n\n        return movement;\n    }\n\n    void Animation::setLoopingEnabled(const std::string &groupname, bool enabled)\n    {\n        AnimStateMap::iterator state(mStates.find(groupname));\n        if(state != mStates.end())\n            state->second.mLoopingEnabled = enabled;\n    }\n\n    void loadBonesFromFile(osg::ref_ptr<osg::Node>& baseNode, const std::string &model, Resource::ResourceSystem* resourceSystem)\n    {\n        const osg::Node* node = resourceSystem->getSceneManager()->getTemplate(model).get();\n        osg::ref_ptr<osg::Node> sheathSkeleton (const_cast<osg::Node*>(node)); // const-trickery required because there is no const version of NodeVisitor\n\n        GetExtendedBonesVisitor getBonesVisitor;\n        sheathSkeleton->accept(getBonesVisitor);\n        for (auto& nodePair : getBonesVisitor.mFoundBones)\n        {\n            SceneUtil::FindByNameVisitor findVisitor (nodePair.second->getName());\n            baseNode->accept(findVisitor);\n\n            osg::Group* sheathParent = findVisitor.mFoundNode;\n            if (sheathParent)\n            {\n                osg::Node* copy = static_cast<osg::Node*>(nodePair.first->clone(osg::CopyOp::DEEP_COPY_NODES));\n                sheathParent->addChild(copy);\n            }\n        }\n    }\n\n    void injectCustomBones(osg::ref_ptr<osg::Node>& node, const std::string& model, Resource::ResourceSystem* resourceSystem)\n    {\n        if (model.empty())\n            return;\n\n        const std::map<std::string, VFS::File*>& index = resourceSystem->getVFS()->getIndex();\n\n        std::string animationPath = model;\n        if (animationPath.find(\"meshes\") == 0)\n        {\n            animationPath.replace(0, 6, \"animations\");\n        }\n        animationPath.replace(animationPath.size()-4, 4, \"/\");\n\n        resourceSystem->getVFS()->normalizeFilename(animationPath);\n\n        std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(animationPath);\n        while (found != index.end())\n        {\n            const std::string& name = found->first;\n            if (name.size() >= animationPath.size() && name.substr(0, animationPath.size()) == animationPath)\n            {\n                size_t pos = name.find_last_of('.');\n                if (pos != std::string::npos && name.compare(pos, name.size()-pos, \".nif\") == 0)\n                    loadBonesFromFile(node, name, resourceSystem);\n            }\n            else\n                break;\n            ++found;\n        }\n    }\n\n    osg::ref_ptr<osg::Node> getModelInstance(Resource::ResourceSystem* resourceSystem, const std::string& model, bool baseonly, bool inject, const std::string& defaultSkeleton)\n    {\n        Resource::SceneManager* sceneMgr = resourceSystem->getSceneManager();\n        if (baseonly)\n        {\n            typedef std::map<std::string, osg::ref_ptr<osg::Node> > Cache;\n            static Cache cache;\n            Cache::iterator found = cache.find(model);\n            if (found == cache.end())\n            {\n                osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);\n\n                if (inject)\n                {\n                    injectCustomBones(created, defaultSkeleton, resourceSystem);\n                    injectCustomBones(created, model, resourceSystem);\n                }\n\n                SceneUtil::CleanObjectRootVisitor removeDrawableVisitor;\n                created->accept(removeDrawableVisitor);\n                removeDrawableVisitor.remove();\n\n                cache.insert(std::make_pair(model, created));\n\n                return sceneMgr->createInstance(created);\n            }\n            else\n                return sceneMgr->createInstance(found->second);\n        }\n        else\n        {\n            osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);\n\n            if (inject)\n            {\n                injectCustomBones(created, defaultSkeleton, resourceSystem);\n                injectCustomBones(created, model, resourceSystem);\n            }\n\n            return created;\n        }\n    }\n\n    void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature)\n    {\n        osg::ref_ptr<osg::StateSet> previousStateset;\n        if (mObjectRoot)\n        {\n            if (mLightListCallback)\n                mObjectRoot->removeCullCallback(mLightListCallback);\n            if (mTransparencyUpdater)\n                mObjectRoot->removeCullCallback(mTransparencyUpdater);\n            previousStateset = mObjectRoot->getStateSet();\n            mObjectRoot->getParent(0)->removeChild(mObjectRoot);\n        }\n        mObjectRoot = nullptr;\n        mSkeleton = nullptr;\n\n        mNodeMap.clear();\n        mNodeMapCreated = false;\n        mActiveControllers.clear();\n        mAccumRoot = nullptr;\n        mAccumCtrl = nullptr;\n\n        static const bool useAdditionalSources = Settings::Manager::getBool (\"use additional anim sources\", \"Game\");\n        std::string defaultSkeleton;\n        bool inject = false;\n\n        if (useAdditionalSources && mPtr.getClass().isActor())\n        {\n            if (isCreature)\n            {\n                MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();\n                if(ref->mBase->mFlags & ESM::Creature::Bipedal)\n                {\n                    defaultSkeleton = Settings::Manager::getString(\"xbaseanim\", \"Models\");\n                    inject = true;\n                }\n            }\n            else\n            {\n                inject = true;\n                MWWorld::LiveCellRef<ESM::NPC> *ref = mPtr.get<ESM::NPC>();\n                if (!ref->mBase->mModel.empty())\n                {\n                    // If NPC has a custom animation model attached, we should inject bones from default skeleton for given race and gender as well\n                    // Since it is a quite rare case, there should not be a noticable performance loss\n                    // Note: consider that player and werewolves have no custom animation files attached for now\n                    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n                    const ESM::Race *race = store.get<ESM::Race>().find(ref->mBase->mRace);\n\n                    bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;\n                    bool isFemale = !ref->mBase->isMale();\n\n                    defaultSkeleton = SceneUtil::getActorSkeleton(false, isFemale, isBeast, false);\n                    defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS());\n                }\n            }\n        }\n\n        if (!forceskeleton)\n        {\n            osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject, defaultSkeleton);\n            mInsert->addChild(created);\n            mObjectRoot = created->asGroup();\n            if (!mObjectRoot)\n            {\n                mInsert->removeChild(created);\n                mObjectRoot = new osg::Group;\n                mObjectRoot->addChild(created);\n                mInsert->addChild(mObjectRoot);\n            }\n            osg::ref_ptr<SceneUtil::Skeleton> skel = dynamic_cast<SceneUtil::Skeleton*>(mObjectRoot.get());\n            if (skel)\n                mSkeleton = skel.get();\n        }\n        else\n        {\n            osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject, defaultSkeleton);\n            osg::ref_ptr<SceneUtil::Skeleton> skel = dynamic_cast<SceneUtil::Skeleton*>(created.get());\n            if (!skel)\n            {\n                skel = new SceneUtil::Skeleton;\n                skel->addChild(created);\n            }\n            mSkeleton = skel.get();\n            mObjectRoot = skel;\n            mInsert->addChild(mObjectRoot);\n        }\n\n        if (previousStateset)\n            mObjectRoot->setStateSet(previousStateset);\n\n        if (isCreature)\n        {\n            SceneUtil::RemoveTriBipVisitor removeTriBipVisitor;\n            mObjectRoot->accept(removeTriBipVisitor);\n            removeTriBipVisitor.remove();\n        }\n\n        if (!mLightListCallback)\n            mLightListCallback = new SceneUtil::LightListCallback;\n        mObjectRoot->addCullCallback(mLightListCallback);\n        if (mTransparencyUpdater)\n            mObjectRoot->addCullCallback(mTransparencyUpdater);\n    }\n\n    osg::Group* Animation::getObjectRoot()\n    {\n        return mObjectRoot.get();\n    }\n\n    osg::Group* Animation::getOrCreateObjectRoot()\n    {\n        if (mObjectRoot)\n            return mObjectRoot.get();\n\n        mObjectRoot = new osg::Group;\n        mInsert->addChild(mObjectRoot);\n        return mObjectRoot.get();\n    }\n\n    void Animation::addSpellCastGlow(const ESM::MagicEffect *effect, float glowDuration)\n    {\n        osg::Vec4f glowColor(1,1,1,1);\n        glowColor.x() = effect->mData.mRed / 255.f;\n        glowColor.y() = effect->mData.mGreen / 255.f;\n        glowColor.z() = effect->mData.mBlue / 255.f;\n\n        if (!mGlowUpdater || (mGlowUpdater->isDone() || (mGlowUpdater->isPermanentGlowUpdater() == true)))\n        {\n            if (mGlowUpdater && mGlowUpdater->isDone())\n                mObjectRoot->removeUpdateCallback(mGlowUpdater);\n\n            if (mGlowUpdater && mGlowUpdater->isPermanentGlowUpdater())\n            {\n                mGlowUpdater->setColor(glowColor);\n                mGlowUpdater->setDuration(glowDuration);\n            }\n            else\n                mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, glowColor, glowDuration);\n        }\n    }\n\n    void Animation::addExtraLight(osg::ref_ptr<osg::Group> parent, const ESM::Light *esmLight)\n    {\n        bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();\n\n        mExtraLightSource = SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);\n    }\n\n    void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)\n    {\n        if (!mObjectRoot.get())\n            return;\n\n        // Early out if we already have this effect\n        FindVfxCallbacksVisitor visitor(effectId);\n        mInsert->accept(visitor);\n\n        for (std::vector<UpdateVfxCallback*>::iterator it = visitor.mCallbacks.begin(); it != visitor.mCallbacks.end(); ++it)\n        {\n            UpdateVfxCallback* callback = *it;\n\n            if (loop && !callback->mFinished && callback->mParams.mLoop && callback->mParams.mBoneName == bonename)\n                return;\n        }\n\n        EffectParams params;\n        params.mModelName = model;\n        osg::ref_ptr<osg::Group> parentNode;\n        if (bonename.empty())\n            parentNode = mInsert;\n        else\n        {\n            NodeMap::const_iterator found = getNodeMap().find(Misc::StringUtils::lowerCase(bonename));\n            if (found == getNodeMap().end())\n                throw std::runtime_error(\"Can't find bone \" + bonename);\n\n            parentNode = found->second;\n        }\n\n        osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;\n        if (!mPtr.getClass().isNpc())\n        {\n            osg::Vec3f bounds (MWBase::Environment::get().getWorld()->getHalfExtents(mPtr) * 2.f / Constants::UnitsPerFoot);\n            float scale = std::max({ bounds.x()/3.f, bounds.y()/3.f, bounds.z()/6.f });\n            trans->setScale(osg::Vec3f(scale, scale, scale));\n        }\n        parentNode->addChild(trans);\n\n        osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model, trans);\n        node->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n\n        SceneUtil::FindMaxControllerLengthVisitor findMaxLengthVisitor;\n        node->accept(findMaxLengthVisitor);\n\n        // FreezeOnCull doesn't work so well with effect particles, that tend to have moving emitters\n        SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor;\n        node->accept(disableFreezeOnCullVisitor);\n        node->setNodeMask(Mask_Effect);\n\n        params.mMaxControllerLength = findMaxLengthVisitor.getMaxLength();\n        params.mLoop = loop;\n        params.mEffectId = effectId;\n        params.mBoneName = bonename;\n        params.mAnimTime = std::shared_ptr<EffectAnimationTime>(new EffectAnimationTime);\n        trans->addUpdateCallback(new UpdateVfxCallback(params));\n\n        SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::shared_ptr<SceneUtil::ControllerSource>(params.mAnimTime));\n        node->accept(assignVisitor);\n\n        // Notify that this animation has attached magic effects\n        mHasMagicEffects = true;\n\n        overrideFirstRootTexture(texture, mResourceSystem, node);\n    }\n\n    void Animation::removeEffect(int effectId)\n    {\n        RemoveCallbackVisitor visitor(effectId);\n        mInsert->accept(visitor);\n        visitor.remove();\n        mHasMagicEffects = visitor.mHasMagicEffects;\n    }\n\n    void Animation::removeEffects()\n    {\n        removeEffect(-1);\n    }\n\n    void Animation::getLoopingEffects(std::vector<int> &out) const\n    {\n        if (!mHasMagicEffects)\n            return;\n\n        FindVfxCallbacksVisitor visitor;\n        mInsert->accept(visitor);\n\n        for (std::vector<UpdateVfxCallback*>::iterator it = visitor.mCallbacks.begin(); it != visitor.mCallbacks.end(); ++it)\n        {\n            UpdateVfxCallback* callback = *it;\n\n            if (callback->mParams.mLoop && !callback->mFinished)\n                out.push_back(callback->mParams.mEffectId);\n        }\n    }\n\n    void Animation::updateEffects()\n    {\n        // We do not need to visit scene every frame.\n        // We can use a bool flag to check in spellcasting effect found.\n        if (!mHasMagicEffects)\n            return;\n\n        // TODO: objects without animation still will have\n        // transformation nodes with finished callbacks\n        RemoveFinishedCallbackVisitor visitor;\n        mInsert->accept(visitor);\n        visitor.remove();\n        mHasMagicEffects = visitor.mHasMagicEffects;\n    }\n\n    bool Animation::upperBodyReady() const\n    {\n        for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter)\n        {\n            if (stateiter->second.mPriority.contains(int(MWMechanics::Priority_Hit))\n                    || stateiter->second.mPriority.contains(int(MWMechanics::Priority_Weapon))\n                    || stateiter->second.mPriority.contains(int(MWMechanics::Priority_Knockdown))\n                    || stateiter->second.mPriority.contains(int(MWMechanics::Priority_Death)))\n                return false;\n        }\n        return true;\n    }\n\n    const osg::Node* Animation::getNode(const std::string &name) const\n    {\n        std::string lowerName = Misc::StringUtils::lowerCase(name);\n        NodeMap::const_iterator found = getNodeMap().find(lowerName);\n        if (found == getNodeMap().end())\n            return nullptr;\n        else\n            return found->second;\n    }\n\n    void Animation::setAlpha(float alpha)\n    {\n        if (alpha == mAlpha)\n            return;\n        mAlpha = alpha;\n\n        // TODO: we use it to fade actors away too, but it would be nice to have a dithering shader instead.\n        if (alpha != 1.f)\n        {\n            if (mTransparencyUpdater == nullptr)\n            {\n                mTransparencyUpdater = new TransparencyUpdater(alpha);\n                mTransparencyUpdater->setLightSource(mExtraLightSource);\n                mObjectRoot->addCullCallback(mTransparencyUpdater);\n            }\n            else\n                mTransparencyUpdater->setAlpha(alpha);\n        }\n        else\n        {\n            mObjectRoot->removeCullCallback(mTransparencyUpdater);\n            mTransparencyUpdater = nullptr;\n        }\n    }\n\n    void Animation::setLightEffect(float effect)\n    {\n        if (effect == 0)\n        {\n            if (mGlowLight)\n            {\n                mInsert->removeChild(mGlowLight);\n                mGlowLight = nullptr;\n            }\n        }\n        else\n        {\n            // 1 pt of Light magnitude corresponds to 1 foot of radius\n            float radius = effect * std::ceil(Constants::UnitsPerFoot);\n            // Arbitrary multiplier used to make the obvious cut-off less obvious\n            float cutoffMult = 3;\n\n            if (!mGlowLight || (radius * cutoffMult) != mGlowLight->getRadius())\n            {\n                if (mGlowLight)\n                {\n                    mInsert->removeChild(mGlowLight);\n                    mGlowLight = nullptr;\n                }\n\n                osg::ref_ptr<osg::Light> light (new osg::Light);\n                light->setDiffuse(osg::Vec4f(0,0,0,0));\n                light->setSpecular(osg::Vec4f(0,0,0,0));\n                light->setAmbient(osg::Vec4f(1.5f,1.5f,1.5f,1.f));\n\n                bool isExterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();\n                SceneUtil::configureLight(light, radius, isExterior);\n\n                mGlowLight = new SceneUtil::LightSource;\n                mGlowLight->setNodeMask(Mask_Lighting);\n                mInsert->addChild(mGlowLight);\n                mGlowLight->setLight(light);\n            }\n\n            mGlowLight->setRadius(radius * cutoffMult);\n        }\n    }\n\n    void Animation::addControllers()\n    {\n        mHeadController = addRotateController(\"bip01 head\");\n        mSpineController = addRotateController(\"bip01 spine1\");\n        mRootController = addRotateController(\"bip01\");\n    }\n\n    RotateController* Animation::addRotateController(std::string bone)\n    {\n        auto iter = getNodeMap().find(bone);\n        if (iter == getNodeMap().end())\n            return nullptr;\n        osg::MatrixTransform* node = iter->second;\n\n        bool foundKeyframeCtrl = false;\n        osg::Callback* cb = node->getUpdateCallback();\n        while (cb)\n        {\n            if (dynamic_cast<SceneUtil::KeyframeController*>(cb))\n            {\n                foundKeyframeCtrl = true;\n                break;\n            }\n            cb = cb->getNestedCallback();\n        }\n        // Without KeyframeController the orientation will not be reseted each frame, so\n        // RotateController shouldn't be used for such nodes.\n        if (!foundKeyframeCtrl)\n            return nullptr;\n\n        RotateController* controller = new RotateController(mObjectRoot.get());\n        node->addUpdateCallback(controller);\n        mActiveControllers.emplace_back(node, controller);\n        return controller;\n    }\n\n    void Animation::setHeadPitch(float pitchRadians)\n    {\n        mHeadPitchRadians = pitchRadians;\n    }\n\n    void Animation::setHeadYaw(float yawRadians)\n    {\n        mHeadYawRadians = yawRadians;\n    }\n\n    float Animation::getHeadPitch() const\n    {\n        return mHeadPitchRadians;\n    }\n\n    float Animation::getHeadYaw() const\n    {\n        return mHeadYawRadians;\n    }\n\n    // ------------------------------------------------------\n\n    float Animation::AnimationTime::getValue(osg::NodeVisitor*)\n    {\n        if (mTimePtr)\n            return *mTimePtr;\n        return 0.f;\n    }\n\n    float EffectAnimationTime::getValue(osg::NodeVisitor*)\n    {\n        return mTime;\n    }\n\n    void EffectAnimationTime::addTime(float duration)\n    {\n        mTime += duration;\n    }\n\n    void EffectAnimationTime::resetTime(float time)\n    {\n        mTime = time;\n    }\n\n    float EffectAnimationTime::getTime() const\n    {\n        return mTime;\n    }\n\n    // --------------------------------------------------------------------------------\n\n    ObjectAnimation::ObjectAnimation(const MWWorld::Ptr &ptr, const std::string &model, Resource::ResourceSystem* resourceSystem, bool animated, bool allowLight)\n        : Animation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), resourceSystem)\n    {\n        if (!model.empty())\n        {\n            setObjectRoot(model, false, false, false);\n            if (animated)\n                addAnimSource(model, model);\n\n            if (!ptr.getClass().getEnchantment(ptr).empty())\n                mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));\n        }\n        if (ptr.getTypeName() == typeid(ESM::Light).name() && allowLight)\n            addExtraLight(getOrCreateObjectRoot(), ptr.get<ESM::Light>()->mBase);\n\n        if (!allowLight && mObjectRoot)\n        {\n            RemoveParticlesVisitor visitor;\n            mObjectRoot->accept(visitor);\n            visitor.remove();\n        }\n\n        if (SceneUtil::hasUserDescription(mObjectRoot, Constants::NightDayLabel))\n        {\n            AddSwitchCallbacksVisitor visitor;\n            mObjectRoot->accept(visitor);\n        }\n\n        if (ptr.getRefData().getCustomData() != nullptr && canBeHarvested())\n        {\n            const MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);\n            if (!store.hasVisibleItems())\n            {\n                HarvestVisitor visitor;\n                mObjectRoot->accept(visitor);\n            }\n        }\n    }\n\n    bool ObjectAnimation::canBeHarvested() const\n    {\n        if (mPtr.getTypeName() != typeid(ESM::Container).name())\n            return false;\n\n        const MWWorld::LiveCellRef<ESM::Container>* ref = mPtr.get<ESM::Container>();\n        if (!(ref->mBase->mFlags & ESM::Container::Organic))\n            return false;\n\n        return SceneUtil::hasUserDescription(mObjectRoot, Constants::HerbalismLabel);\n    }\n\n    Animation::AnimState::~AnimState()\n    {\n\n    }\n\n    // ------------------------------\n\n    PartHolder::PartHolder(osg::ref_ptr<osg::Node> node)\n        : mNode(node)\n    {\n    }\n\n    PartHolder::~PartHolder()\n    {\n        if (mNode.get() && !mNode->getNumParents())\n            Log(Debug::Verbose) << \"Part \\\"\" << mNode->getName() << \"\\\" has no parents\" ;\n\n        if (mNode.get() && mNode->getNumParents())\n        {\n            if (mNode->getNumParents() > 1)\n                Log(Debug::Verbose) << \"Part \\\"\" << mNode->getName() << \"\\\" has multiple (\" << mNode->getNumParents() << \") parents\";\n            mNode->getParent(0)->removeChild(mNode);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/animation.hpp",
    "content": "#ifndef GAME_RENDER_ANIMATION_H\n#define GAME_RENDER_ANIMATION_H\n\n#include \"../mwworld/ptr.hpp\"\n\n#include <components/sceneutil/controller.hpp>\n#include <components/sceneutil/textkeymap.hpp>\n#include <components/sceneutil/util.hpp>\n\n#include <vector>\n\nnamespace ESM\n{\n    struct Light;\n    struct MagicEffect;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace SceneUtil\n{\n    class KeyframeHolder;\n    class KeyframeController;\n    class LightSource;\n    class LightListCallback;\n    class Skeleton;\n}\n\nnamespace MWRender\n{\n\nclass ResetAccumRootCallback;\nclass RotateController;\nclass TransparencyUpdater;\n\nclass EffectAnimationTime : public SceneUtil::ControllerSource\n{\nprivate:\n    float mTime;\npublic:\n    float getValue(osg::NodeVisitor* nv) override;\n\n    void addTime(float duration);\n    void resetTime(float time);\n    float getTime() const;\n\n    EffectAnimationTime() : mTime(0) {  }\n};\n\n/// @brief Detaches the node from its parent when the object goes out of scope.\nclass PartHolder\n{\npublic:\n    PartHolder(osg::ref_ptr<osg::Node> node);\n\n    ~PartHolder();\n\n    osg::ref_ptr<osg::Node> getNode()\n    {\n        return mNode;\n    }\n\nprivate:\n    osg::ref_ptr<osg::Node> mNode;\n\n    void operator= (const PartHolder&);\n    PartHolder(const PartHolder&);\n};\ntypedef std::shared_ptr<PartHolder> PartHolderPtr;\n\nstruct EffectParams\n{\n    std::string mModelName; // Just here so we don't add the same effect twice\n    std::shared_ptr<EffectAnimationTime> mAnimTime;\n    float mMaxControllerLength;\n    int mEffectId;\n    bool mLoop;\n    std::string mBoneName;\n};\n\nclass Animation : public osg::Referenced\n{\npublic:\n    enum BoneGroup {\n        BoneGroup_LowerBody = 0,\n        BoneGroup_Torso,\n        BoneGroup_LeftArm,\n        BoneGroup_RightArm\n    };\n\n    enum BlendMask {\n        BlendMask_LowerBody = 1<<0,\n        BlendMask_Torso = 1<<1,\n        BlendMask_LeftArm = 1<<2,\n        BlendMask_RightArm = 1<<3,\n\n        BlendMask_UpperBody = BlendMask_Torso | BlendMask_LeftArm | BlendMask_RightArm,\n\n        BlendMask_All = BlendMask_LowerBody | BlendMask_UpperBody\n    };\n    /* This is the number of *discrete* blend masks. */\n    static constexpr size_t sNumBlendMasks = 4;\n\n    /// Holds an animation priority value for each BoneGroup.\n    struct AnimPriority\n    {\n        /// Convenience constructor, initialises all priorities to the same value.\n        AnimPriority(int priority)\n        {\n            for (unsigned int i=0; i<sNumBlendMasks; ++i)\n                mPriority[i] = priority;\n        }\n\n        bool operator == (const AnimPriority& other) const\n        {\n            for (unsigned int i=0; i<sNumBlendMasks; ++i)\n                if (other.mPriority[i] != mPriority[i])\n                    return false;\n            return true;\n        }\n\n        int& operator[] (BoneGroup n)\n        {\n            return mPriority[n];\n        }\n\n        const int& operator[] (BoneGroup n) const\n        {\n            return mPriority[n];\n        }\n\n        bool contains(int priority) const\n        {\n            for (unsigned int i=0; i<sNumBlendMasks; ++i)\n                if (priority == mPriority[i])\n                    return true;\n            return false;\n        }\n\n        int mPriority[sNumBlendMasks];\n    };\n\n    class TextKeyListener\n    {\n    public:\n        virtual void handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key,\n                                   const SceneUtil::TextKeyMap& map) = 0;\n\n        virtual ~TextKeyListener() = default;\n    };\n\n    void setTextKeyListener(TextKeyListener* listener);\n\n    virtual bool updateCarriedLeftVisible(const int weaptype) const { return false; };\n\nprotected:\n    class AnimationTime : public SceneUtil::ControllerSource\n    {\n    private:\n        std::shared_ptr<float> mTimePtr;\n\n    public:\n\n        void setTimePtr(std::shared_ptr<float> time)\n        { mTimePtr = time; }\n        std::shared_ptr<float> getTimePtr() const\n        { return mTimePtr; }\n\n        float getValue(osg::NodeVisitor* nv) override;\n    };\n\n    class NullAnimationTime : public SceneUtil::ControllerSource\n    {\n    public:\n        float getValue(osg::NodeVisitor *nv) override\n        {\n            return 0.f;\n        }\n    };\n\n    struct AnimSource;\n\n    struct AnimState {\n        std::shared_ptr<AnimSource> mSource;\n        float mStartTime;\n        float mLoopStartTime;\n        float mLoopStopTime;\n        float mStopTime;\n\n        typedef std::shared_ptr<float> TimePtr;\n        TimePtr mTime;\n        float mSpeedMult;\n\n        bool mPlaying;\n        bool mLoopingEnabled;\n        size_t mLoopCount;\n\n        AnimPriority mPriority;\n        int mBlendMask;\n        bool mAutoDisable;\n\n        AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f),\n                      mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopingEnabled(true),\n                      mLoopCount(0), mPriority(0), mBlendMask(0), mAutoDisable(true)\n        {\n        }\n        ~AnimState();\n\n        float getTime() const\n        {\n            return *mTime;\n        }\n        void setTime(float time)\n        {\n            *mTime = time;\n        }\n\n        bool shouldLoop() const\n        {\n            return getTime() >= mLoopStopTime && mLoopingEnabled && mLoopCount > 0;\n        }\n    };\n    typedef std::map<std::string,AnimState> AnimStateMap;\n    AnimStateMap mStates;\n\n    typedef std::vector<std::shared_ptr<AnimSource> > AnimSourceList;\n    AnimSourceList mAnimSources;\n\n    osg::ref_ptr<osg::Group> mInsert;\n\n    osg::ref_ptr<osg::Group> mObjectRoot;\n    SceneUtil::Skeleton* mSkeleton;\n\n    // The node expected to accumulate movement during movement animations.\n    osg::ref_ptr<osg::Node> mAccumRoot;\n\n    // The controller animating that node.\n    osg::ref_ptr<SceneUtil::KeyframeController> mAccumCtrl;\n\n    // Used to reset the position of the accumulation root every frame - the movement should be applied to the physics system\n    osg::ref_ptr<ResetAccumRootCallback> mResetAccumRootCallback;\n\n    // Keep track of controllers that we added to our scene graph.\n    // We may need to rebuild these controllers when the active animation groups / sources change.\n    std::vector<std::pair<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback>>> mActiveControllers;\n\n    std::shared_ptr<AnimationTime> mAnimationTimePtr[sNumBlendMasks];\n\n    // Stored in all lowercase for a case-insensitive lookup\n    typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;\n    mutable NodeMap mNodeMap;\n    mutable bool mNodeMapCreated;\n\n    MWWorld::Ptr mPtr;\n\n    Resource::ResourceSystem* mResourceSystem;\n\n    osg::Vec3f mAccumulate;\n\n    TextKeyListener* mTextKeyListener;\n\n    osg::ref_ptr<RotateController> mHeadController;\n    osg::ref_ptr<RotateController> mSpineController;\n    osg::ref_ptr<RotateController> mRootController;\n    float mHeadYawRadians;\n    float mHeadPitchRadians;\n    float mUpperBodyYawRadians;\n    float mLegsYawRadians;\n    float mBodyPitchRadians;\n\n    RotateController* addRotateController(std::string bone);\n\n    bool mHasMagicEffects;\n\n    osg::ref_ptr<SceneUtil::LightSource> mGlowLight;\n    osg::ref_ptr<SceneUtil::GlowUpdater> mGlowUpdater;\n    osg::ref_ptr<TransparencyUpdater> mTransparencyUpdater;\n    osg::ref_ptr<SceneUtil::LightSource> mExtraLightSource;\n\n    float mAlpha;\n\n    mutable std::map<std::string, float> mAnimVelocities;\n\n    osg::ref_ptr<SceneUtil::LightListCallback> mLightListCallback;\n\n    const NodeMap& getNodeMap() const;\n\n    /* Sets the appropriate animations on the bone groups based on priority.\n     */\n    void resetActiveGroups();\n\n    size_t detectBlendMask(const osg::Node* node) const;\n\n    /* Updates the position of the accum root node for the given time, and\n     * returns the wanted movement vector from the previous time. */\n    void updatePosition(float oldtime, float newtime, osg::Vec3f& position);\n\n    /* Resets the animation to the time of the specified start marker, without\n     * moving anything, and set the end time to the specified stop marker. If\n     * the marker is not found, or if the markers are the same, it returns\n     * false.\n     */\n    bool reset(AnimState &state, const SceneUtil::TextKeyMap &keys,\n               const std::string &groupname, const std::string &start, const std::string &stop,\n               float startpoint, bool loopfallback);\n\n    void handleTextKey(AnimState &state, const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key,\n                       const SceneUtil::TextKeyMap& map);\n\n    /** Sets the root model of the object.\n     *\n     * Note that you must make sure all animation sources are cleared before resetting the object\n     * root. All nodes previously retrieved with getNode will also become invalidated.\n     * @param forceskeleton Wrap the object root in a Skeleton, even if it contains no skinned parts. Use this if you intend to add skinned parts manually.\n     * @param baseonly If true, then any meshes or particle systems in the model are ignored\n     *      (useful for NPCs, where only the skeleton is needed for the root, and the actual NPC parts are then assembled from separate files).\n     */\n    void setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature);\n\n    void loadAllAnimationsInFolder(const std::string &model, const std::string &baseModel);\n\n    /** Adds the keyframe controllers in the specified model as a new animation source.\n     * @note Later added animation sources have the highest priority when it comes to finding a particular animation.\n     * @param model The file to add the keyframes for. Note that the .nif file extension will be replaced with .kf.\n     * @param baseModel The filename of the mObjectRoot, only used for error messages.\n    */\n    void addAnimSource(const std::string &model, const std::string& baseModel);\n    void addSingleAnimSource(const std::string &model, const std::string& baseModel);\n\n    /** Adds an additional light to the given node using the specified ESM record. */\n    void addExtraLight(osg::ref_ptr<osg::Group> parent, const ESM::Light *light);\n\n    void clearAnimSources();\n\n    /**\n     * Provided to allow derived classes adding their own controllers. Note, the controllers must be added to mActiveControllers\n     * so they get cleaned up properly on the next controller rebuild. A controller rebuild may be necessary to ensure correct ordering.\n     */\n    virtual void addControllers();\n\npublic:\n\n    Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem);\n\n    /// Must be thread safe\n    virtual ~Animation();\n\n    MWWorld::ConstPtr getPtr() const;\n\n    MWWorld::Ptr getPtr();\n\n    /// Set active flag on the object skeleton, if one exists.\n    /// @see SceneUtil::Skeleton::setActive\n    /// 0 = Inactive, 1 = Active in place, 2 = Active\n    void setActive(int active);\n\n    osg::Group* getOrCreateObjectRoot();\n\n    osg::Group* getObjectRoot();\n\n    /**\n     * @brief Add an effect mesh attached to a bone or the insert scene node\n     * @param model\n     * @param effectId An ID for this effect by which you can identify it later. If this is not wanted, set to -1.\n     * @param loop Loop the effect. If false, it is removed automatically after it finishes playing. If true,\n     *              you need to remove it manually using removeEffect when the effect should end.\n     * @param bonename Bone to attach to, or empty string to use the scene node instead\n     * @param texture override the texture specified in the model's materials - if empty, do not override\n     * @note Will not add an effect twice.\n     */\n    void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = \"\", const std::string& texture = \"\");\n    void removeEffect (int effectId);\n    void removeEffects ();\n    void getLoopingEffects (std::vector<int>& out) const;\n\n    // Add a spell casting glow to an object. From measuring video taken from the original engine,\n    // the glow seems to be about 1.5 seconds except for telekinesis, which is 1 second.\n    void addSpellCastGlow(const ESM::MagicEffect *effect, float glowDuration = 1.5);\n\n    virtual void updatePtr(const MWWorld::Ptr &ptr);\n\n    bool hasAnimation(const std::string &anim) const;\n\n    // Specifies the axis' to accumulate on. Non-accumulated axis will just\n    // move visually, but not affect the actual movement. Each x/y/z value\n    // should be on the scale of 0 to 1.\n    void setAccumulation(const osg::Vec3f& accum);\n\n    /** Plays an animation.\n     * \\param groupname Name of the animation group to play.\n     * \\param priority Priority of the animation. The animation will play on\n     *                 bone groups that don't have another animation set of a\n     *                 higher priority.\n     * \\param blendMask Bone groups to play the animation on.\n     * \\param autodisable Automatically disable the animation when it stops\n     *                    playing.\n     * \\param speedmult Speed multiplier for the animation.\n     * \\param start Key marker from which to start.\n     * \\param stop Key marker to stop at.\n     * \\param startpoint How far in between the two markers to start. 0 starts\n     *                   at the start marker, 1 starts at the stop marker.\n     * \\param loops How many times to loop the animation. This will use the\n     *              \"loop start\" and \"loop stop\" markers if they exist,\n     *              otherwise it may fall back to \"start\" and \"stop\", but only if\n     *              the \\a loopFallback parameter is true.\n     * \\param loopFallback Allow looping an animation that has no loop keys, i.e. fall back to use\n     *                     the \"start\" and \"stop\" keys for looping?\n     */\n    void play(const std::string &groupname, const AnimPriority& priority, int blendMask, bool autodisable,\n              float speedmult, const std::string &start, const std::string &stop,\n              float startpoint, size_t loops, bool loopfallback=false);\n\n    /** Adjust the speed multiplier of an already playing animation.\n     */\n    void adjustSpeedMult (const std::string& groupname, float speedmult);\n\n    /** Returns true if the named animation group is playing. */\n    bool isPlaying(const std::string &groupname) const;\n\n    /// Returns true if no important animations are currently playing on the upper body.\n    bool upperBodyReady() const;\n\n    /** Gets info about the given animation group.\n     * \\param groupname Animation group to check.\n     * \\param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.\n     * \\param speedmult Stores the animation speed multiplier\n     * \\return True if the animation is active, false otherwise.\n     */\n    bool getInfo(const std::string &groupname, float *complete=nullptr, float *speedmult=nullptr) const;\n\n    /// Get the absolute position in the animation track of the first text key with the given group.\n    float getStartTime(const std::string &groupname) const;\n\n    /// Get the absolute position in the animation track of the text key\n    float getTextKeyTime(const std::string &textKey) const;\n\n    /// Get the current absolute position in the animation track for the animation that is currently playing from the given group.\n    float getCurrentTime(const std::string& groupname) const;\n\n    size_t getCurrentLoopCount(const std::string& groupname) const;\n\n    /** Disables the specified animation group;\n     * \\param groupname Animation group to disable.\n     */\n    void disable(const std::string &groupname);\n\n    /** Retrieves the velocity (in units per second) that the animation will move. */\n    float getVelocity(const std::string &groupname) const;\n\n    virtual osg::Vec3f runAnimation(float duration);\n\n    void setLoopingEnabled(const std::string &groupname, bool enabled);\n\n    /// This is typically called as part of runAnimation, but may be called manually if needed.\n    void updateEffects();\n\n    /// Return a node with the specified name, or nullptr if not existing.\n    /// @note The matching is case-insensitive.\n    const osg::Node* getNode(const std::string& name) const;\n\n    virtual bool useShieldAnimations() const { return false; }\n    virtual void showWeapons(bool showWeapon) {}\n    virtual bool getCarriedLeftShown() const { return false; }\n    virtual void showCarriedLeft(bool show) {}\n    virtual void setWeaponGroup(const std::string& group, bool relativeDuration) {}\n    virtual void setVampire(bool vampire) {}\n    /// A value < 1 makes the animation translucent, 1.f = fully opaque\n    void setAlpha(float alpha);\n    virtual void setPitchFactor(float factor) {}\n    virtual void attachArrow() {}\n    virtual void detachArrow() {}\n    virtual void releaseArrow(float attackStrength) {}\n    virtual void enableHeadAnimation(bool enable) {}\n    // TODO: move outside of this class\n    /// Makes this object glow, by placing a Light in its center.\n    /// @param effect Controls the radius and intensity of the light.\n    virtual void setLightEffect(float effect);\n\n    virtual void setHeadPitch(float pitchRadians);\n    virtual void setHeadYaw(float yawRadians);\n    virtual float getHeadPitch() const;\n    virtual float getHeadYaw() const;\n\n    virtual void setUpperBodyYawRadians(float v) { mUpperBodyYawRadians = v; }\n    virtual void setLegsYawRadians(float v) { mLegsYawRadians = v; }\n    virtual float getUpperBodyYawRadians() const { return mUpperBodyYawRadians; }\n    virtual float getLegsYawRadians() const { return mLegsYawRadians; }\n    virtual void setBodyPitchRadians(float v) { mBodyPitchRadians = v; }\n    virtual float getBodyPitchRadians() const { return mBodyPitchRadians; }\n\n    virtual void setAccurateAiming(bool enabled) {}\n    virtual bool canBeHarvested() const { return false; }\n\nprivate:\n    Animation(const Animation&);\n    void operator=(Animation&);\n};\n\nclass ObjectAnimation : public Animation {\npublic:\n    ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, Resource::ResourceSystem* resourceSystem, bool animated, bool allowLight);\n\n    bool canBeHarvested() const override;\n};\n\nclass UpdateVfxCallback : public osg::NodeCallback\n{\npublic:\n    UpdateVfxCallback(EffectParams& params)\n        : mFinished(false)\n        , mParams(params)\n        , mStartingTime(0)\n    {\n    }\n\n    bool mFinished;\n    EffectParams mParams;\n\n    void operator()(osg::Node* node, osg::NodeVisitor* nv) override;\n\nprivate:\n    double mStartingTime;\n};\n\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/bulletdebugdraw.cpp",
    "content": "#include <algorithm>\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n\n#include <osg/Geometry>\n#include <osg/Group>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/convert.hpp>\n#include <osg/PolygonMode>\n#include <osg/ShapeDrawable>\n#include <osg/StateSet>\n\n#include \"bulletdebugdraw.hpp\"\n#include \"vismask.hpp\"\n\nnamespace MWRender\n{\n\nDebugDrawer::DebugDrawer(osg::ref_ptr<osg::Group> parentNode, btCollisionWorld *world, int debugMode)\n    : mParentNode(parentNode),\n      mWorld(world)\n{\n    setDebugMode(debugMode);\n}\n\nvoid DebugDrawer::createGeometry()\n{\n    if (!mGeometry)\n    {\n        mGeometry = new osg::Geometry;\n        mGeometry->setNodeMask(Mask_Debug);\n\n        mVertices = new osg::Vec3Array;\n        mColors = new osg::Vec4Array;\n\n        mDrawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES);\n\n        mGeometry->setUseDisplayList(false);\n        mGeometry->setVertexArray(mVertices);\n        mGeometry->setColorArray(mColors);\n        mGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);\n        mGeometry->setDataVariance(osg::Object::DYNAMIC);\n        mGeometry->addPrimitiveSet(mDrawArrays);\n\n        mParentNode->addChild(mGeometry);\n\n        auto* stateSet = new osg::StateSet;\n        stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE), osg::StateAttribute::ON);\n        mShapesRoot = new osg::Group;\n        mShapesRoot->setStateSet(stateSet);\n        mShapesRoot->setDataVariance(osg::Object::DYNAMIC);\n        mShapesRoot->setNodeMask(Mask_Debug);\n        mParentNode->addChild(mShapesRoot);\n    }\n}\n\nvoid DebugDrawer::destroyGeometry()\n{\n    if (mGeometry)\n    {\n        mParentNode->removeChild(mGeometry);\n        mParentNode->removeChild(mShapesRoot);\n        mGeometry = nullptr;\n        mVertices = nullptr;\n        mDrawArrays = nullptr;\n    }\n}\n\nDebugDrawer::~DebugDrawer()\n{\n    destroyGeometry();\n}\n\nvoid DebugDrawer::step()\n{\n    if (mDebugOn)\n    {\n        mVertices->clear();\n        mColors->clear();\n        mShapesRoot->removeChildren(0, mShapesRoot->getNumChildren());\n        mWorld->debugDrawWorld();\n        showCollisions();\n        mDrawArrays->setCount(mVertices->size());\n        mVertices->dirty();\n        mColors->dirty();\n        mGeometry->dirtyBound();\n    }\n}\n\nvoid DebugDrawer::drawLine(const btVector3 &from, const btVector3 &to, const btVector3 &color)\n{\n    mVertices->push_back(Misc::Convert::toOsg(from));\n    mVertices->push_back(Misc::Convert::toOsg(to));\n    mColors->push_back({1,1,1,1});\n    mColors->push_back({1,1,1,1});\n}\n\nvoid DebugDrawer::addCollision(const btVector3& orig, const btVector3& normal)\n{\n    mCollisionViews.emplace_back(orig, normal);\n}\n\nvoid DebugDrawer::showCollisions()\n{\n    const auto now = std::chrono::steady_clock::now();\n    for (auto& [from, to , created] : mCollisionViews)\n    {\n        if (now - created < std::chrono::seconds(2))\n        {\n            mVertices->push_back(Misc::Convert::toOsg(from));\n            mVertices->push_back(Misc::Convert::toOsg(to));\n            mColors->push_back({1,0,0,1});\n            mColors->push_back({1,0,0,1});\n        }\n    }\n    mCollisionViews.erase(std::remove_if(mCollisionViews.begin(), mCollisionViews.end(),\n                [&now](const CollisionView& view) { return now - view.mCreated >= std::chrono::seconds(2); }),\n            mCollisionViews.end());\n}\n\nvoid DebugDrawer::drawSphere(btScalar radius, const btTransform& transform, const btVector3& color)\n{\n    auto* geom = new osg::ShapeDrawable(new osg::Sphere(Misc::Convert::toOsg(transform.getOrigin()), radius));\n    geom->setColor(osg::Vec4(1, 1, 1, 1));\n    mShapesRoot->addChild(geom);\n}\n\nvoid DebugDrawer::reportErrorWarning(const char *warningString)\n{\n    Log(Debug::Warning) << warningString;\n}\n\nvoid DebugDrawer::setDebugMode(int isOn)\n{\n    mDebugOn = (isOn != 0);\n\n    if (!mDebugOn)\n        destroyGeometry();\n    else\n        createGeometry();\n}\n\nint DebugDrawer::getDebugMode() const\n{\n    return mDebugOn;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/bulletdebugdraw.hpp",
    "content": "#ifndef OPENMW_MWRENDER_BULLETDEBUGDRAW_H\n#define OPENMW_MWRENDER_BULLETDEBUGDRAW_H\n\n#include <chrono>\n#include <vector>\n\n#include <osg/ref_ptr>\n#include <osg/Array>\n#include <osg/PrimitiveSet>\n\n#include <LinearMath/btIDebugDraw.h>\n\nclass btCollisionWorld;\n\nnamespace osg\n{\n    class Group;\n    class Geometry;\n}\n\nnamespace MWRender\n{\n\nclass DebugDrawer : public btIDebugDraw\n{\nprivate:\n    struct CollisionView\n    {\n        btVector3 mOrig;\n        btVector3 mEnd;\n        std::chrono::time_point<std::chrono::steady_clock> mCreated;\n        CollisionView(btVector3 orig, btVector3 normal) : mOrig(orig), mEnd(orig + normal * 20), mCreated(std::chrono::steady_clock::now()) {};\n    };\n    std::vector<CollisionView> mCollisionViews;\n    osg::ref_ptr<osg::Group> mShapesRoot;\n\nprotected:\n    osg::ref_ptr<osg::Group> mParentNode;\n    btCollisionWorld *mWorld;\n    osg::ref_ptr<osg::Geometry> mGeometry;\n    osg::ref_ptr<osg::Vec3Array> mVertices;\n    osg::ref_ptr<osg::Vec4Array> mColors;\n    osg::ref_ptr<osg::DrawArrays> mDrawArrays;\n\n    bool mDebugOn;\n\n    void createGeometry();\n    void destroyGeometry();\n\npublic:\n\n    DebugDrawer(osg::ref_ptr<osg::Group> parentNode, btCollisionWorld *world, int debugMode = 1);\n    ~DebugDrawer();\n\n    void step();\n\n    void drawLine(const btVector3& from,const btVector3& to,const btVector3& color) override;\n\n    void addCollision(const btVector3& orig, const btVector3& normal);\n\n    void showCollisions();\n\n    void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color) override {};\n    void drawSphere(btScalar radius, const btTransform& transform, const btVector3& color) override;\n\n    void reportErrorWarning(const char* warningString) override;\n\n    void draw3dText(const btVector3& location,const char* textString) override {}\n\n    //0 for off, anything else for on.\n    void setDebugMode(int isOn) override;\n\n    //0 for off, anything else for on.\n    int getDebugMode() const override;\n\n};\n\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/camera.cpp",
    "content": "#include \"camera.hpp\"\n\n#include <osg/Camera>\n\n#include <components/misc/mathutil.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/refdata.hpp\"\n\n#include \"../mwmechanics/drawstate.hpp\"\n#include \"../mwmechanics/movement.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n\n#include \"../mwphysics/raycasting.hpp\"\n\n#include \"npcanimation.hpp\"\n\nnamespace\n{\n\nclass UpdateRenderCameraCallback : public osg::NodeCallback\n{\npublic:\n    UpdateRenderCameraCallback(MWRender::Camera* cam)\n        : mCamera(cam)\n    {\n    }\n\n    void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n    {\n        osg::Camera* cam = static_cast<osg::Camera*>(node);\n\n        // traverse first to update animations, in case the camera is attached to an animated node\n        traverse(node, nv);\n\n        mCamera->updateCamera(cam);\n    }\n\nprivate:\n    MWRender::Camera* mCamera;\n};\n\n}\n\nnamespace MWRender\n{\n\n    Camera::Camera (osg::Camera* camera)\n    : mHeightScale(1.f),\n      mCamera(camera),\n      mAnimation(nullptr),\n      mFirstPersonView(true),\n      mMode(Mode::Normal),\n      mVanityAllowed(true),\n      mStandingPreviewAllowed(Settings::Manager::getBool(\"preview if stand still\", \"Camera\")),\n      mDeferredRotationAllowed(Settings::Manager::getBool(\"deferred preview rotation\", \"Camera\")),\n      mNearest(30.f),\n      mFurthest(800.f),\n      mIsNearest(false),\n      mHeight(124.f),\n      mBaseCameraDistance(Settings::Manager::getFloat(\"third person camera distance\", \"Camera\")),\n      mPitch(0.f),\n      mYaw(0.f),\n      mRoll(0.f),\n      mVanityToggleQueued(false),\n      mVanityToggleQueuedValue(false),\n      mViewModeToggleQueued(false),\n      mCameraDistance(0.f),\n      mMaxNextCameraDistance(800.f),\n      mFocalPointCurrentOffset(osg::Vec2d()),\n      mFocalPointTargetOffset(osg::Vec2d()),\n      mFocalPointTransitionSpeedCoef(1.f),\n      mSkipFocalPointTransition(true),\n      mPreviousTransitionInfluence(0.f),\n      mSmoothedSpeed(0.f),\n      mZoomOutWhenMoveCoef(Settings::Manager::getFloat(\"zoom out when move coef\", \"Camera\")),\n      mDynamicCameraDistanceEnabled(false),\n      mShowCrosshairInThirdPersonMode(false),\n      mHeadBobbingEnabled(Settings::Manager::getBool(\"head bobbing\", \"Camera\")),\n      mHeadBobbingOffset(0.f),\n      mHeadBobbingWeight(0.f),\n      mTotalMovement(0.f),\n      mDeferredRotation(osg::Vec3f()),\n      mDeferredRotationDisabled(false)\n    {\n        mCameraDistance = mBaseCameraDistance;\n\n        mUpdateCallback = new UpdateRenderCameraCallback(this);\n        mCamera->addUpdateCallback(mUpdateCallback);\n    }\n\n    Camera::~Camera()\n    {\n        mCamera->removeUpdateCallback(mUpdateCallback);\n    }\n\n    osg::Vec3d Camera::getFocalPoint() const\n    {\n        if (!mTrackingNode)\n            return osg::Vec3d();\n        osg::NodePathList nodepaths = mTrackingNode->getParentalNodePaths();\n        if (nodepaths.empty())\n            return osg::Vec3d();\n        osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);\n\n        osg::Vec3d position = worldMat.getTrans();\n        if (isFirstPerson())\n            position.z() += mHeadBobbingOffset;\n        else\n        {\n            position.z() += mHeight * mHeightScale;\n\n            // We subtract 10.f here and add it within focalPointOffset in order to avoid camera clipping through ceiling.\n            // Needed because character's head can be a bit higher than collision area.\n            position.z() -= 10.f;\n\n            position += getFocalPointOffset() + mFocalPointAdjustment;\n        }\n        return position;\n    }\n\n    osg::Vec3d Camera::getFocalPointOffset() const\n    {\n        osg::Vec3d offset(0, 0, 10.f);\n        offset.x() += mFocalPointCurrentOffset.x() * cos(getYaw());\n        offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw());\n        offset.z() += mFocalPointCurrentOffset.y();\n        return offset;\n    }\n\n    void Camera::getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const\n    {\n        focal = getFocalPoint();\n        osg::Vec3d offset(0,0,0);\n        if (!isFirstPerson())\n        {\n            osg::Quat orient =  osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1));\n            offset = orient * osg::Vec3d(0.f, -mCameraDistance, 0.f);\n        }\n        camera = focal + offset;\n    }\n\n    void Camera::updateCamera(osg::Camera *cam)\n    {\n        osg::Vec3d focal, position;\n        getPosition(focal, position);\n\n        osg::Quat orient = osg::Quat(mRoll, osg::Vec3d(0, 1, 0)) * osg::Quat(mPitch, osg::Vec3d(1, 0, 0)) * osg::Quat(mYaw, osg::Vec3d(0, 0, 1));\n        osg::Vec3d forward = orient * osg::Vec3d(0,1,0);\n        osg::Vec3d up = orient * osg::Vec3d(0,0,1);\n\n        cam->setViewMatrixAsLookAt(position, position + forward, up);\n    }\n\n    void Camera::updateHeadBobbing(float duration) {\n        static const float doubleStepLength = Settings::Manager::getFloat(\"head bobbing step\", \"Camera\") * 2;\n        static const float stepHeight = Settings::Manager::getFloat(\"head bobbing height\", \"Camera\");\n        static const float maxRoll = osg::DegreesToRadians(Settings::Manager::getFloat(\"head bobbing roll\", \"Camera\"));\n\n        if (MWBase::Environment::get().getWorld()->isOnGround(mTrackingPtr))\n            mHeadBobbingWeight = std::min(mHeadBobbingWeight + duration * 5, 1.f);\n        else\n            mHeadBobbingWeight = std::max(mHeadBobbingWeight - duration * 5, 0.f);\n\n        float doubleStepState = mTotalMovement / doubleStepLength - std::floor(mTotalMovement / doubleStepLength); // from 0 to 1 during 2 steps\n        float stepState = std::abs(doubleStepState * 4 - 2) - 1; // from -1 to 1 on even steps and from 1 to -1 on odd steps\n        float effect = (1 - std::cos(stepState * osg::DegreesToRadians(30.f))) * 7.5f; // range from 0 to 1\n        float coef = std::min(mSmoothedSpeed / 300.f, 1.f) * mHeadBobbingWeight;\n        mHeadBobbingOffset = (0.5f - effect) * coef * stepHeight; // range from -stepHeight/2 to stepHeight/2\n        mRoll = osg::sign(stepState) * effect * coef * maxRoll; // range from -maxRoll to maxRoll\n    }\n\n    void Camera::reset()\n    {\n        togglePreviewMode(false);\n        toggleVanityMode(false);\n        if (!mFirstPersonView)\n            toggleViewMode();\n    }\n\n    void Camera::rotateCamera(float pitch, float yaw, bool adjust)\n    {\n        if (adjust)\n        {\n            pitch += getPitch();\n            yaw += getYaw();\n        }\n        setYaw(yaw);\n        setPitch(pitch);\n    }\n\n    void Camera::update(float duration, bool paused)\n    {\n        if (mAnimation->upperBodyReady())\n        {\n            // Now process the view changes we queued earlier\n            if (mVanityToggleQueued)\n            {\n                toggleVanityMode(mVanityToggleQueuedValue);\n                mVanityToggleQueued = false;\n            }\n            if (mViewModeToggleQueued)\n            {\n                togglePreviewMode(false);\n                toggleViewMode();\n                mViewModeToggleQueued = false;\n            }\n        }\n\n        if (paused)\n            return;\n\n        // only show the crosshair in game mode\n        MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();\n        wm->showCrosshair(!wm->isGuiMode() && mMode != Mode::Preview && mMode != Mode::Vanity\n                          && (mFirstPersonView || mShowCrosshairInThirdPersonMode));\n\n        if(mMode == Mode::Vanity)\n            rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true);\n\n        if (isFirstPerson() && mHeadBobbingEnabled)\n            updateHeadBobbing(duration);\n        else\n            mRoll = mHeadBobbingOffset = 0;\n\n        updateFocalPointOffset(duration);\n        updatePosition();\n\n        float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr);\n        mTotalMovement += speed * duration;\n        speed /= (1.f + speed / 500.f);\n        float maxDelta = 300.f * duration;\n        mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta);\n\n        mMaxNextCameraDistance = mCameraDistance + duration * (100.f + mBaseCameraDistance);\n        updateStandingPreviewMode();\n    }\n\n    void Camera::updatePosition()\n    {\n        mFocalPointAdjustment = osg::Vec3d();\n        if (isFirstPerson())\n            return;\n\n        const float cameraObstacleLimit = 5.0f;\n        const float focalObstacleLimit = 10.f;\n\n        const auto* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting();\n\n        // Adjust focal point to prevent clipping.\n        osg::Vec3d focal = getFocalPoint();\n        osg::Vec3d focalOffset = getFocalPointOffset();\n        float offsetLen = focalOffset.length();\n        if (offsetLen > 0)\n        {\n            MWPhysics::RayCastingResult result = rayCasting->castSphere(focal - focalOffset, focal, focalObstacleLimit);\n            if (result.mHit)\n            {\n                double adjustmentCoef = -(result.mHitPos + result.mHitNormal * focalObstacleLimit - focal).length() / offsetLen;\n                mFocalPointAdjustment = focalOffset * std::max(-1.0, adjustmentCoef);\n            }\n        }\n\n        // Calculate camera distance.\n        mCameraDistance = mBaseCameraDistance + getCameraDistanceCorrection();\n        if (mDynamicCameraDistanceEnabled)\n            mCameraDistance = std::min(mCameraDistance, mMaxNextCameraDistance);\n        osg::Vec3d cameraPos;\n        getPosition(focal, cameraPos);\n        MWPhysics::RayCastingResult result = rayCasting->castSphere(focal, cameraPos, cameraObstacleLimit);\n        if (result.mHit)\n            mCameraDistance = (result.mHitPos + result.mHitNormal * cameraObstacleLimit - focal).length();\n    }\n\n    void Camera::updateStandingPreviewMode()\n    {\n        if (!mStandingPreviewAllowed)\n            return;\n        float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr);\n        bool combat = mTrackingPtr.getClass().isActor() &&\n                      mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing;\n        bool standingStill = speed == 0 && !combat && !mFirstPersonView;\n        if (!standingStill && mMode == Mode::StandingPreview)\n        {\n            mMode = Mode::Normal;\n            calculateDeferredRotation();\n        }\n        else if (standingStill && mMode == Mode::Normal)\n            mMode = Mode::StandingPreview;\n    }\n\n    void Camera::setFocalPointTargetOffset(osg::Vec2d v)\n    {\n        mFocalPointTargetOffset = v;\n        mPreviousTransitionSpeed = mFocalPointTransitionSpeed;\n        mPreviousTransitionInfluence = 1.0f;\n    }\n\n    void Camera::updateFocalPointOffset(float duration)\n    {\n        if (duration <= 0)\n            return;\n\n        if (mSkipFocalPointTransition)\n        {\n            mSkipFocalPointTransition = false;\n            mPreviousExtraOffset = osg::Vec2d();\n            mPreviousTransitionInfluence = 0.f;\n            mFocalPointCurrentOffset = mFocalPointTargetOffset;\n        }\n\n        osg::Vec2d oldOffset = mFocalPointCurrentOffset;\n\n        if (mPreviousTransitionInfluence > 0)\n        {\n            mFocalPointCurrentOffset -= mPreviousExtraOffset;\n            mPreviousExtraOffset = mPreviousExtraOffset / mPreviousTransitionInfluence + mPreviousTransitionSpeed * duration;\n            mPreviousTransitionInfluence =\n                std::max(0.f, mPreviousTransitionInfluence - duration * mFocalPointTransitionSpeedCoef);\n            mPreviousExtraOffset *= mPreviousTransitionInfluence;\n            mFocalPointCurrentOffset += mPreviousExtraOffset;\n        }\n\n        osg::Vec2d delta = mFocalPointTargetOffset - mFocalPointCurrentOffset;\n        if (delta.length2() > 0)\n        {\n            float coef = duration * (1.0 + 5.0 / delta.length()) *\n                         mFocalPointTransitionSpeedCoef * (1.0f - mPreviousTransitionInfluence);\n            mFocalPointCurrentOffset += delta * std::min(coef, 1.0f);\n        }\n        else\n        {\n            mPreviousExtraOffset = osg::Vec2d();\n            mPreviousTransitionInfluence = 0.f;\n        }\n\n        mFocalPointTransitionSpeed = (mFocalPointCurrentOffset - oldOffset) / duration;\n    }\n\n    void Camera::toggleViewMode(bool force)\n    {\n        // Changing the view will stop all playing animations, so if we are playing\n        // anything important, queue the view change for later\n        if (!mAnimation->upperBodyReady() && !force)\n        {\n            mViewModeToggleQueued = true;\n            return;\n        }\n        else\n            mViewModeToggleQueued = false;\n\n        mFirstPersonView = !mFirstPersonView;\n        updateStandingPreviewMode();\n        instantTransition();\n        processViewChange();\n    }\n\n    void Camera::allowVanityMode(bool allow)\n    {\n        if (!allow && mMode == Mode::Vanity)\n        {\n            disableDeferredPreviewRotation();\n            toggleVanityMode(false);\n        }\n        mVanityAllowed = allow;\n    }\n\n    bool Camera::toggleVanityMode(bool enable)\n    {\n        // Changing the view will stop all playing animations, so if we are playing\n        // anything important, queue the view change for later\n        if (mFirstPersonView && !mAnimation->upperBodyReady())\n        {\n            mVanityToggleQueued = true;\n            mVanityToggleQueuedValue = enable;\n            return false;\n        }\n\n        if (!mVanityAllowed && enable)\n            return false;\n\n        if ((mMode == Mode::Vanity) == enable)\n            return true;\n        mMode = enable ? Mode::Vanity : Mode::Normal;\n        if (!mDeferredRotationAllowed)\n            disableDeferredPreviewRotation();\n        if (!enable)\n            calculateDeferredRotation();\n\n        processViewChange();\n        return true;\n    }\n\n    void Camera::togglePreviewMode(bool enable)\n    {\n        if (mFirstPersonView && !mAnimation->upperBodyReady())\n            return;\n\n        if((mMode == Mode::Preview) == enable)\n            return;\n\n        mMode = enable ? Mode::Preview : Mode::Normal;\n        if (mMode == Mode::Normal)\n            updateStandingPreviewMode();\n        else if (mFirstPersonView)\n            instantTransition();\n        if (mMode == Mode::Normal)\n        {\n            if (!mDeferredRotationAllowed)\n                disableDeferredPreviewRotation();\n            calculateDeferredRotation();\n        }\n        processViewChange();\n    }\n\n    void Camera::setSneakOffset(float offset)\n    {\n        mAnimation->setFirstPersonOffset(osg::Vec3f(0,0,-offset));\n    }\n\n    void Camera::setYaw(float angle)\n    {\n        mYaw = Misc::normalizeAngle(angle);\n    }\n\n    void Camera::setPitch(float angle)\n    {\n        const float epsilon = 0.000001f;\n        float limit = static_cast<float>(osg::PI_2) - epsilon;\n        mPitch = osg::clampBetween(angle, -limit, limit);\n    }\n\n    float Camera::getCameraDistance() const\n    {\n        if (isFirstPerson())\n            return 0.f;\n        return mCameraDistance;\n    }\n\n    void Camera::adjustCameraDistance(float delta)\n    {\n        if (!isFirstPerson())\n        {\n            if(isNearest() && delta < 0.f && getMode() != Mode::Preview && getMode() != Mode::Vanity)\n                toggleViewMode();\n            else\n                mBaseCameraDistance = std::min(mCameraDistance - getCameraDistanceCorrection(), mBaseCameraDistance) + delta;\n        }\n        else if (delta > 0.f)\n        {\n            toggleViewMode();\n            mBaseCameraDistance = 0;\n        }\n\n        mIsNearest = mBaseCameraDistance <= mNearest;\n        mBaseCameraDistance = osg::clampBetween(mBaseCameraDistance, mNearest, mFurthest);\n        Settings::Manager::setFloat(\"third person camera distance\", \"Camera\", mBaseCameraDistance);\n    }\n\n    float Camera::getCameraDistanceCorrection() const\n    {\n        if (!mDynamicCameraDistanceEnabled)\n            return 0;\n\n        float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f;\n\n        float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed;\n        float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * mZoomOutWhenMoveCoef;\n\n        return pitchCorrection + speedCorrection;\n    }\n\n    void Camera::setAnimation(NpcAnimation *anim)\n    {\n        mAnimation = anim;\n        processViewChange();\n    }\n\n    void Camera::processViewChange()\n    {\n        if(isFirstPerson())\n        {\n            mAnimation->setViewMode(NpcAnimation::VM_FirstPerson);\n            mTrackingNode = mAnimation->getNode(\"Camera\");\n            if (!mTrackingNode)\n                mTrackingNode = mAnimation->getNode(\"Head\");\n            mHeightScale = 1.f;\n        }\n        else\n        {\n            mAnimation->setViewMode(NpcAnimation::VM_Normal);\n            SceneUtil::PositionAttitudeTransform* transform = mTrackingPtr.getRefData().getBaseNode();\n            mTrackingNode = transform;\n            if (transform)\n                mHeightScale = transform->getScale().z();\n            else\n                mHeightScale = 1.f;\n        }\n        rotateCamera(getPitch(), getYaw(), false);\n    }\n\n    void Camera::applyDeferredPreviewRotationToPlayer(float dt)\n    {\n        if (isVanityOrPreviewModeEnabled() || mTrackingPtr.isEmpty())\n            return;\n\n        osg::Vec3f rot = mDeferredRotation;\n        float delta = rot.normalize();\n        delta = std::min(delta, (delta + 1.f) * 3 * dt);\n        rot *= delta;\n        mDeferredRotation -= rot;\n\n        if (mDeferredRotationDisabled)\n        {\n            mDeferredRotationDisabled = delta > 0.0001;\n            rotateCameraToTrackingPtr();\n            return;\n        }\n\n        auto& movement = mTrackingPtr.getClass().getMovementSettings(mTrackingPtr);\n        movement.mRotation[0] += rot.x();\n        movement.mRotation[1] += rot.y();\n        movement.mRotation[2] += rot.z();\n        if (std::abs(mDeferredRotation.z()) > 0.0001)\n        {\n            float s = std::sin(mDeferredRotation.z());\n            float c = std::cos(mDeferredRotation.z());\n            float x = movement.mPosition[0];\n            float y = movement.mPosition[1];\n            movement.mPosition[0] = x *  c + y * s;\n            movement.mPosition[1] = x * -s + y * c;\n        }\n    }\n\n    void Camera::rotateCameraToTrackingPtr()\n    {\n        setPitch(-mTrackingPtr.getRefData().getPosition().rot[0] - mDeferredRotation.x());\n        setYaw(-mTrackingPtr.getRefData().getPosition().rot[2] - mDeferredRotation.z());\n    }\n\n    void Camera::instantTransition()\n    {\n        mSkipFocalPointTransition = true;\n        mDeferredRotationDisabled = false;\n        mDeferredRotation = osg::Vec3f();\n        rotateCameraToTrackingPtr();\n    }\n\n    void Camera::calculateDeferredRotation()\n    {\n        MWWorld::Ptr ptr = mTrackingPtr;\n        if (isVanityOrPreviewModeEnabled() || ptr.isEmpty())\n            return;\n        if (mFirstPersonView)\n        {\n            instantTransition();\n            return;\n        }\n\n        mDeferredRotation.x() = Misc::normalizeAngle(-ptr.getRefData().getPosition().rot[0] - mPitch);\n        mDeferredRotation.z() = Misc::normalizeAngle(-ptr.getRefData().getPosition().rot[2] - mYaw);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/camera.hpp",
    "content": "#ifndef GAME_MWRENDER_CAMERA_H\n#define GAME_MWRENDER_CAMERA_H\n\n#include <string>\n\n#include <osg/ref_ptr>\n#include <osg/Vec3>\n#include <osg/Vec3d>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace osg\n{\n    class Camera;\n    class NodeCallback;\n    class Node;\n}\n\nnamespace MWRender\n{\n    class NpcAnimation;\n\n    /// \\brief Camera control\n    class Camera\n    {\n    public:\n        enum class Mode { Normal, Vanity, Preview, StandingPreview };\n\n    private:\n        MWWorld::Ptr mTrackingPtr;\n        osg::ref_ptr<const osg::Node> mTrackingNode;\n        float mHeightScale;\n\n        osg::ref_ptr<osg::Camera> mCamera;\n\n        NpcAnimation *mAnimation;\n\n        bool mFirstPersonView;\n        Mode mMode;\n        bool mVanityAllowed;\n        bool mStandingPreviewAllowed;\n        bool mDeferredRotationAllowed;\n\n        float mNearest;\n        float mFurthest;\n        bool mIsNearest;\n\n        float mHeight, mBaseCameraDistance;\n        float mPitch, mYaw, mRoll;\n\n        bool mVanityToggleQueued;\n        bool mVanityToggleQueuedValue;\n        bool mViewModeToggleQueued;\n\n        float mCameraDistance;\n        float mMaxNextCameraDistance;\n\n        osg::Vec3d mFocalPointAdjustment;\n        osg::Vec2d mFocalPointCurrentOffset;\n        osg::Vec2d mFocalPointTargetOffset;\n        float mFocalPointTransitionSpeedCoef;\n        bool mSkipFocalPointTransition;\n\n        // This fields are used to make focal point transition smooth if previous transition was not finished.\n        float mPreviousTransitionInfluence;\n        osg::Vec2d mFocalPointTransitionSpeed;\n        osg::Vec2d mPreviousTransitionSpeed;\n        osg::Vec2d mPreviousExtraOffset;\n\n        float mSmoothedSpeed;\n        float mZoomOutWhenMoveCoef;\n        bool mDynamicCameraDistanceEnabled;\n        bool mShowCrosshairInThirdPersonMode;\n\n        bool mHeadBobbingEnabled;\n        float mHeadBobbingOffset;\n        float mHeadBobbingWeight; // Value from 0 to 1 for smooth enabling/disabling.\n        float mTotalMovement; // Needed for head bobbing.\n        void updateHeadBobbing(float duration);\n\n        void updateFocalPointOffset(float duration);\n        void updatePosition();\n        float getCameraDistanceCorrection() const;\n\n        osg::ref_ptr<osg::NodeCallback> mUpdateCallback;\n\n        // Used to rotate player to the direction of view after exiting preview or vanity mode.\n        osg::Vec3f mDeferredRotation;\n        bool mDeferredRotationDisabled;\n        void calculateDeferredRotation();\n        void updateStandingPreviewMode();\n\n    public:\n        Camera(osg::Camera* camera);\n        ~Camera();\n\n        /// Attach camera to object\n        void attachTo(const MWWorld::Ptr &ptr) { mTrackingPtr = ptr; }\n        MWWorld::Ptr getTrackingPtr() const { return mTrackingPtr; }\n\n        void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; }\n        void setFocalPointTargetOffset(osg::Vec2d v);\n        void instantTransition();\n        void enableDynamicCameraDistance(bool v) { mDynamicCameraDistanceEnabled = v; }\n        void enableCrosshairInThirdPersonMode(bool v) { mShowCrosshairInThirdPersonMode = v; }\n\n        /// Update the view matrix of \\a cam\n        void updateCamera(osg::Camera* cam);\n\n        /// Reset to defaults\n        void reset();\n\n        /// Set where the camera is looking at. Uses Morrowind (euler) angles\n        /// \\param rot Rotation angles in radians\n        void rotateCamera(float pitch, float yaw, bool adjust);\n        void rotateCameraToTrackingPtr();\n\n        float getYaw() const { return mYaw; }\n        void setYaw(float angle);\n\n        float getPitch() const { return mPitch; }\n        void setPitch(float angle);\n\n        /// @param Force view mode switch, even if currently not allowed by the animation.\n        void toggleViewMode(bool force=false);\n\n        bool toggleVanityMode(bool enable);\n        void allowVanityMode(bool allow);\n\n        /// @note this may be ignored if an important animation is currently playing\n        void togglePreviewMode(bool enable);\n\n        void applyDeferredPreviewRotationToPlayer(float dt);\n        void disableDeferredPreviewRotation() { mDeferredRotationDisabled = true; }\n\n        /// \\brief Lowers the camera for sneak.\n        void setSneakOffset(float offset);\n\n        bool isFirstPerson() const { return mFirstPersonView && mMode == Mode::Normal; }\n\n        void processViewChange();\n\n        void update(float duration, bool paused=false);\n\n        /// Adds distDelta to the camera distance. Switches 3rd/1st person view if distance is less than limit.\n        void adjustCameraDistance(float distDelta);\n\n        float getCameraDistance() const;\n\n        void setAnimation(NpcAnimation *anim);\n\n        osg::Vec3d getFocalPoint() const;\n        osg::Vec3d getFocalPointOffset() const;\n\n        /// Stores focal and camera world positions in passed arguments\n        void getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const;\n\n        bool isVanityOrPreviewModeEnabled() const { return mMode != Mode::Normal; }\n        Mode getMode() const { return mMode; }\n\n        bool isNearest() const { return mIsNearest; }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/cell.hpp",
    "content": "#ifndef GAME_RENDER_CELL_H\n#define GAME_RENDER_CELL_H\n\n#include <string>\n\nnamespace MWRender\n{\n    class CellRender\n    {\n        public:\n\n            virtual ~CellRender() {};\n\n            /// Make the cell visible. Load the cell if necessary.\n            virtual void show() = 0;\n\n            /// Remove the cell from rendering, but don't remove it from\n            /// memory.\n            virtual void hide() = 0;\n\n            /// Destroy all rendering objects connected with this cell.\n            virtual void destroy() = 0;\n\n            /// Make the reference with the given handle visible.\n            virtual void enable (const std::string& handle) = 0;\n\n            /// Make the reference with the given handle invisible.\n            virtual void disable (const std::string& handle) = 0;\n\n            /// Remove the reference with the given handle permanently from the scene.\n            virtual void deleteObject (const std::string& handle) = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/characterpreview.cpp",
    "content": "#include \"characterpreview.hpp\"\n\n#include <cmath>\n\n#include <osg/Material>\n#include <osg/Fog>\n#include <osg/BlendFunc>\n#include <osg/TexEnvCombine>\n#include <osg/Texture2D>\n#include <osg/Camera>\n#include <osg/PositionAttitudeTransform>\n#include <osg/LightModel>\n#include <osg/LightSource>\n#include <osg/ValueObject>\n#include <osgUtil/IntersectionVisitor>\n#include <osgUtil/LineSegmentIntersector>\n\n#include <components/debug/debuglog.hpp>\n#include <components/fallback/fallback.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n#include <components/sceneutil/shadow.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"npcanimation.hpp\"\n#include \"vismask.hpp\"\n\nnamespace MWRender\n{\n\n    class DrawOnceCallback : public osg::NodeCallback\n    {\n    public:\n        DrawOnceCallback ()\n            : mRendered(false)\n            , mLastRenderedFrame(0)\n        {\n        }\n\n        void operator () (osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            if (!mRendered)\n            {\n                mRendered = true;\n\n                mLastRenderedFrame = nv->getTraversalNumber();\n\n                osg::ref_ptr<osg::FrameStamp> previousFramestamp = const_cast<osg::FrameStamp*>(nv->getFrameStamp());\n                osg::FrameStamp* fs = new osg::FrameStamp(*previousFramestamp);\n                fs->setSimulationTime(0.0);\n\n                nv->setFrameStamp(fs);\n\n                traverse(node, nv);\n\n                nv->setFrameStamp(previousFramestamp);\n            }\n            else\n            {\n                node->setNodeMask(0);\n            }\n        }\n\n        void redrawNextFrame()\n        {\n            mRendered = false;\n        }\n\n        unsigned int getLastRenderedFrame() const\n        {\n            return mLastRenderedFrame;\n        }\n\n    private:\n        bool mRendered;\n        unsigned int mLastRenderedFrame;\n    };\n\n\n    // Set up alpha blending mode to avoid issues caused by transparent objects writing onto the alpha value of the FBO\n    // This makes the RTT have premultiplied alpha, though, so the source blend factor must be GL_ONE when it's applied\n    class SetUpBlendVisitor : public osg::NodeVisitor\n    {\n    public:\n        SetUpBlendVisitor(): osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mNoAlphaUniform(new osg::Uniform(\"noAlpha\", false))\n        {\n        }\n\n        void apply(osg::Node& node) override\n        {\n            if (osg::ref_ptr<osg::StateSet> stateset = node.getStateSet())\n            {\n                osg::ref_ptr<osg::StateSet> newStateSet;\n                if (stateset->getAttribute(osg::StateAttribute::BLENDFUNC) || stateset->getBinNumber() == osg::StateSet::TRANSPARENT_BIN)\n                {\n                    osg::BlendFunc* blendFunc = static_cast<osg::BlendFunc*>(stateset->getAttribute(osg::StateAttribute::BLENDFUNC));\n\n                    if (blendFunc)\n                    {\n                        newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY);\n                        node.setStateSet(newStateSet);\n                        osg::ref_ptr<osg::BlendFunc> newBlendFunc = new osg::BlendFunc(*blendFunc);\n                        newStateSet->setAttribute(newBlendFunc, osg::StateAttribute::ON);\n                        // I *think* (based on some by-hand maths) that the RGB and dest alpha factors are unchanged, and only dest determines source alpha factor\n                        // This has the benefit of being idempotent if we assume nothing used glBlendFuncSeparate before we touched it\n                        if (blendFunc->getDestination() == osg::BlendFunc::ONE_MINUS_SRC_ALPHA)\n                            newBlendFunc->setSourceAlpha(osg::BlendFunc::ONE);\n                        else if (blendFunc->getDestination() == osg::BlendFunc::ONE)\n                            newBlendFunc->setSourceAlpha(osg::BlendFunc::ZERO);\n                        // Other setups barely exist in the wild and aren't worth supporting as they're not equippable gear\n                        else\n                            Log(Debug::Info) << \"Unable to adjust blend mode for character preview. Source factor 0x\" << std::hex << blendFunc->getSource() << \", destination factor 0x\" << blendFunc->getDestination() << std::dec;\n                    }\n                }\n                if (stateset->getMode(GL_BLEND) & osg::StateAttribute::ON)\n                {\n                    if (!newStateSet)\n                    {\n                        newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY);\n                        node.setStateSet(newStateSet);\n                    }\n                    // Disable noBlendAlphaEnv\n                    newStateSet->setTextureMode(7, GL_TEXTURE_2D, osg::StateAttribute::OFF);\n                    newStateSet->addUniform(mNoAlphaUniform);\n                }\n            }\n            traverse(node);\n        }\n    private:\n        osg::ref_ptr<osg::Uniform> mNoAlphaUniform;\n    };\n\n    CharacterPreview::CharacterPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem,\n                                       const MWWorld::Ptr& character, int sizeX, int sizeY, const osg::Vec3f& position, const osg::Vec3f& lookAt)\n        : mParent(parent)\n        , mResourceSystem(resourceSystem)\n        , mPosition(position)\n        , mLookAt(lookAt)\n        , mCharacter(character)\n        , mAnimation(nullptr)\n        , mSizeX(sizeX)\n        , mSizeY(sizeY)\n    {\n        mTexture = new osg::Texture2D;\n        mTexture->setTextureSize(sizeX, sizeY);\n        mTexture->setInternalFormat(GL_RGBA);\n        mTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        mTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n        mTexture->setUserValue(\"premultiplied alpha\", true);\n\n        mCamera = new osg::Camera;\n        // hints that the camera is not relative to the master camera\n        mCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);\n        mCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);\n        mCamera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 0.f));\n        mCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        const float fovYDegrees = 12.3f;\n        mCamera->setProjectionMatrixAsPerspective(fovYDegrees, sizeX/static_cast<float>(sizeY), 0.1f, 10000.f); // zNear and zFar are autocomputed\n        mCamera->setViewport(0, 0, sizeX, sizeY);\n        mCamera->setRenderOrder(osg::Camera::PRE_RENDER);\n        mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture, 0, 0, false, Settings::Manager::getInt(\"antialiasing\", \"Video\"));\n        mCamera->setName(\"CharacterPreview\");\n        mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);\n        mCamera->setCullMask(~(Mask_UpdateVisitor));\n\n        mCamera->setNodeMask(Mask_RenderToTexture);\n\n        bool ffp = mResourceSystem->getSceneManager()->getLightingMethod() == SceneUtil::LightingMethod::FFP;\n\n        osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager(ffp);\n        lightManager->setStartLight(1);\n        osg::ref_ptr<osg::StateSet> stateset = lightManager->getOrCreateStateSet();\n        stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);\n        stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON);\n        stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON);\n        osg::ref_ptr<osg::Material> defaultMat (new osg::Material);\n        defaultMat->setColorMode(osg::Material::OFF);\n        defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n        defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n        defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n        stateset->setAttribute(defaultMat);\n\n        SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);\n\n        // assign large value to effectively turn off fog\n        // shaders don't respect glDisable(GL_FOG)\n        osg::ref_ptr<osg::Fog> fog (new osg::Fog);\n        fog->setStart(10000000);\n        fog->setEnd(10000000);\n        stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);\n\n        // Opaque stuff must have 1 as its fragment alpha as the FBO is translucent, so having blending off isn't enough\n        osg::ref_ptr<osg::TexEnvCombine> noBlendAlphaEnv = new osg::TexEnvCombine();\n        noBlendAlphaEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE);\n        noBlendAlphaEnv->setSource0_Alpha(osg::TexEnvCombine::CONSTANT);\n        noBlendAlphaEnv->setConstantColor(osg::Vec4(0.0, 0.0, 0.0, 1.0));\n        noBlendAlphaEnv->setCombine_RGB(osg::TexEnvCombine::REPLACE);\n        noBlendAlphaEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);\n        osg::ref_ptr<osg::Texture2D> dummyTexture = new osg::Texture2D();\n        dummyTexture->setInternalFormat(GL_DEPTH_COMPONENT);\n        dummyTexture->setTextureSize(1, 1);\n        // This might clash with a shadow map, so make sure it doesn't cast shadows\n        dummyTexture->setShadowComparison(true);\n        dummyTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS);\n        stateset->setTextureAttributeAndModes(7, dummyTexture, osg::StateAttribute::ON);\n        stateset->setTextureAttribute(7, noBlendAlphaEnv, osg::StateAttribute::ON);\n        stateset->addUniform(new osg::Uniform(\"noAlpha\", true));\n\n        osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;\n        lightmodel->setAmbientIntensity(osg::Vec4(0.0, 0.0, 0.0, 1.0));\n        stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON);\n\n        osg::ref_ptr<osg::Light> light = new osg::Light;\n        float diffuseR = Fallback::Map::getFloat(\"Inventory_DirectionalDiffuseR\");\n        float diffuseG = Fallback::Map::getFloat(\"Inventory_DirectionalDiffuseG\");\n        float diffuseB = Fallback::Map::getFloat(\"Inventory_DirectionalDiffuseB\");\n        float ambientR = Fallback::Map::getFloat(\"Inventory_DirectionalAmbientR\");\n        float ambientG = Fallback::Map::getFloat(\"Inventory_DirectionalAmbientG\");\n        float ambientB = Fallback::Map::getFloat(\"Inventory_DirectionalAmbientB\");\n        float azimuth = osg::DegreesToRadians(Fallback::Map::getFloat(\"Inventory_DirectionalRotationX\"));\n        float altitude = osg::DegreesToRadians(Fallback::Map::getFloat(\"Inventory_DirectionalRotationY\"));\n        float positionX = -std::cos(azimuth) * std::sin(altitude);\n        float positionY = std::sin(azimuth) * std::sin(altitude);\n        float positionZ = std::cos(altitude);\n        light->setPosition(osg::Vec4(positionX,positionY,positionZ, 0.0));\n        light->setDiffuse(osg::Vec4(diffuseR,diffuseG,diffuseB,1));\n        osg::Vec4 ambientRGBA = osg::Vec4(ambientR,ambientG,ambientB,1);\n        if (mResourceSystem->getSceneManager()->getForceShaders())\n        {\n            // When using shaders, we now skip the ambient sun calculation as this is the only place it's used.\n            // Using the scene ambient will give identical results.\n            lightmodel->setAmbientIntensity(ambientRGBA);\n            light->setAmbient(osg::Vec4(0,0,0,1));\n        }\n        else\n            light->setAmbient(ambientRGBA);\n        light->setSpecular(osg::Vec4(0,0,0,0));\n        light->setLightNum(0);\n        light->setConstantAttenuation(1.f);\n        light->setLinearAttenuation(0.f);\n        light->setQuadraticAttenuation(0.f);\n        lightManager->setSunlight(light);\n\n        osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource;\n        lightSource->setLight(light);\n\n        lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON);\n\n        lightManager->addChild(lightSource);\n\n        mCamera->addChild(lightManager);\n\n        mNode = new osg::PositionAttitudeTransform;\n        lightManager->addChild(mNode);\n\n        mDrawOnceCallback = new DrawOnceCallback;\n        mCamera->addUpdateCallback(mDrawOnceCallback);\n\n        mParent->addChild(mCamera);\n\n        mCharacter.mCell = nullptr;\n    }\n\n    CharacterPreview::~CharacterPreview ()\n    {\n        mCamera->removeChildren(0, mCamera->getNumChildren());\n        mParent->removeChild(mCamera);\n    }\n\n    int CharacterPreview::getTextureWidth() const\n    {\n        return mSizeX;\n    }\n\n    int CharacterPreview::getTextureHeight() const\n    {\n        return mSizeY;\n    }\n\n    void CharacterPreview::setBlendMode()\n    {\n        mResourceSystem->getSceneManager()->recreateShaders(mNode, \"objects\", true);\n        SetUpBlendVisitor visitor;\n        mNode->accept(visitor);\n    }\n\n    void CharacterPreview::onSetup()\n    {\n        setBlendMode();\n    }\n\n    osg::ref_ptr<osg::Texture2D> CharacterPreview::getTexture()\n    {\n        return mTexture;\n    }\n\n    void CharacterPreview::rebuild()\n    {\n        mAnimation = nullptr;\n\n        mAnimation = new NpcAnimation(mCharacter, mNode, mResourceSystem, true,\n                                      (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));\n\n        onSetup();\n\n        redraw();\n    }\n\n    void CharacterPreview::redraw()\n    {\n        mCamera->setNodeMask(Mask_RenderToTexture);\n        mDrawOnceCallback->redrawNextFrame();\n    }\n\n    // --------------------------------------------------------------------------------------------------\n\n\n    InventoryPreview::InventoryPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem, const MWWorld::Ptr& character)\n        : CharacterPreview(parent, resourceSystem, character, 512, 1024, osg::Vec3f(0, 700, 71), osg::Vec3f(0,0,71))\n    {\n    }\n\n    void InventoryPreview::setViewport(int sizeX, int sizeY)\n    {\n        sizeX = std::max(sizeX, 0);\n        sizeY = std::max(sizeY, 0);\n\n        // NB Camera::setViewport has threading issues\n        osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;\n        mViewport = new osg::Viewport(0, mSizeY-sizeY, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY));\n        stateset->setAttributeAndModes(mViewport);\n        mCamera->setStateSet(stateset);\n\n        redraw();\n    }\n\n    void InventoryPreview::update()\n    {\n        if (!mAnimation.get())\n            return;\n\n        mAnimation->showWeapons(true);\n        mAnimation->updateParts();\n\n        MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter);\n        MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n        std::string groupname = \"inventoryhandtohand\";\n        bool showCarriedLeft = true;\n        if(iter != inv.end())\n        {\n            groupname = \"inventoryweapononehand\";\n            if(iter->getTypeName() == typeid(ESM::Weapon).name())\n            {\n                MWWorld::LiveCellRef<ESM::Weapon> *ref = iter->get<ESM::Weapon>();\n                int type = ref->mBase->mData.mType;\n                const ESM::WeaponType* weaponInfo = MWMechanics::getWeaponType(type);\n                showCarriedLeft = !(weaponInfo->mFlags & ESM::WeaponType::TwoHanded);\n\n                std::string inventoryGroup = weaponInfo->mLongGroup;\n                inventoryGroup = \"inventory\" + inventoryGroup;\n\n                // We still should use one-handed animation as fallback\n                if (mAnimation->hasAnimation(inventoryGroup))\n                    groupname = inventoryGroup;\n                else\n                {\n                    static const std::string oneHandFallback = \"inventory\" + MWMechanics::getWeaponType(ESM::Weapon::LongBladeOneHand)->mLongGroup;\n                    static const std::string twoHandFallback = \"inventory\" + MWMechanics::getWeaponType(ESM::Weapon::LongBladeTwoHand)->mLongGroup;\n\n                    // For real two-handed melee weapons use 2h swords animations as fallback, otherwise use the 1h ones\n                    if (weaponInfo->mFlags & ESM::WeaponType::TwoHanded && weaponInfo->mWeaponClass == ESM::WeaponType::Melee)\n                        groupname = twoHandFallback;\n                    else\n                        groupname = oneHandFallback;\n                }\n           }\n        }\n\n        mAnimation->showCarriedLeft(showCarriedLeft);\n\n        mCurrentAnimGroup = groupname;\n        mAnimation->play(mCurrentAnimGroup, 1, Animation::BlendMask_All, false, 1.0f, \"start\", \"stop\", 0.0f, 0);\n\n        MWWorld::ConstContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n        if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && showCarriedLeft)\n        {\n            if(!mAnimation->getInfo(\"torch\"))\n                mAnimation->play(\"torch\", 2, Animation::BlendMask_LeftArm, false,\n                                 1.0f, \"start\", \"stop\", 0.0f, ~0ul, true);\n        }\n        else if(mAnimation->getInfo(\"torch\"))\n            mAnimation->disable(\"torch\");\n\n        mAnimation->runAnimation(0.0f);\n\n        setBlendMode();\n\n        redraw();\n    }\n\n    int InventoryPreview::getSlotSelected (int posX, int posY)\n    {\n        if (!mViewport)\n            return -1;\n        float projX = (posX / mViewport->width()) * 2 - 1.f;\n        float projY = (posY / mViewport->height()) * 2 - 1.f;\n        // With Intersector::WINDOW, the intersection ratios are slightly inaccurate. Seems to be a\n        // precision issue - compiling with OSG_USE_FLOAT_MATRIX=0, Intersector::WINDOW works ok.\n        // Using Intersector::PROJECTION results in better precision because the start/end points and the model matrices\n        // don't go through as many transformations.\n        osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::PROJECTION, projX, projY));\n\n        intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);\n        osgUtil::IntersectionVisitor visitor(intersector);\n        visitor.setTraversalMode(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);\n        // Set the traversal number from the last draw, so that the frame switch used for RigGeometry double buffering works correctly\n        visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame());\n\n        osg::Node::NodeMask nodeMask = mCamera->getNodeMask();\n        mCamera->setNodeMask(~0u);\n        mCamera->accept(visitor);\n        mCamera->setNodeMask(nodeMask);\n\n        if (intersector->containsIntersections())\n        {\n            osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection();\n            return mAnimation->getSlot(intersection.nodePath);\n        }\n        return -1;\n    }\n\n    void InventoryPreview::updatePtr(const MWWorld::Ptr &ptr)\n    {\n        mCharacter = MWWorld::Ptr(ptr.getBase(), nullptr);\n    }\n\n    void InventoryPreview::onSetup()\n    {\n        CharacterPreview::onSetup();\n        osg::Vec3f scale (1.f, 1.f, 1.f);\n        mCharacter.getClass().adjustScale(mCharacter, scale, true);\n\n        mNode->setScale(scale);\n\n        mCamera->setViewMatrixAsLookAt(mPosition * scale.z(), mLookAt * scale.z(), osg::Vec3f(0,0,1));\n    }\n\n    // --------------------------------------------------------------------------------------------------\n\n    RaceSelectionPreview::RaceSelectionPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem)\n        : CharacterPreview(parent, resourceSystem, MWMechanics::getPlayer(),\n            512, 512, osg::Vec3f(0, 125, 8), osg::Vec3f(0,0,8))\n        , mBase (*mCharacter.get<ESM::NPC>()->mBase)\n        , mRef(&mBase)\n        , mPitchRadians(osg::DegreesToRadians(6.f))\n    {\n        mCharacter = MWWorld::Ptr(&mRef, nullptr);\n    }\n\n    RaceSelectionPreview::~RaceSelectionPreview()\n    {\n    }\n\n    void RaceSelectionPreview::setAngle(float angleRadians)\n    {\n        mNode->setAttitude(osg::Quat(mPitchRadians, osg::Vec3(1,0,0))\n                * osg::Quat(angleRadians, osg::Vec3(0,0,1)));\n        redraw();\n    }\n\n    void RaceSelectionPreview::setPrototype(const ESM::NPC &proto)\n    {\n        mBase = proto;\n        mBase.mId = \"player\";\n        rebuild();\n    }\n\n    class UpdateCameraCallback : public osg::NodeCallback\n    {\n    public:\n        UpdateCameraCallback(osg::ref_ptr<const osg::Node> nodeToFollow, const osg::Vec3& posOffset, const osg::Vec3& lookAtOffset)\n            : mNodeToFollow(nodeToFollow)\n            , mPosOffset(posOffset)\n            , mLookAtOffset(lookAtOffset)\n        {\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osg::Camera* cam = static_cast<osg::Camera*>(node);\n\n            // Update keyframe controllers in the scene graph first...\n            traverse(node, nv);\n\n            // Now update camera utilizing the updated head position\n            osg::NodePathList nodepaths = mNodeToFollow->getParentalNodePaths();\n            if (nodepaths.empty())\n                return;\n            osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);\n            osg::Vec3 headOffset = worldMat.getTrans();\n\n            cam->setViewMatrixAsLookAt(headOffset + mPosOffset, headOffset + mLookAtOffset, osg::Vec3(0,0,1));\n        }\n\n    private:\n        osg::ref_ptr<const osg::Node> mNodeToFollow;\n        osg::Vec3 mPosOffset;\n        osg::Vec3 mLookAtOffset;\n    };\n\n    void RaceSelectionPreview::onSetup ()\n    {\n        CharacterPreview::onSetup();\n        mAnimation->play(\"idle\", 1, Animation::BlendMask_All, false, 1.0f, \"start\", \"stop\", 0.0f, 0);\n        mAnimation->runAnimation(0.f);\n\n        // attach camera to follow the head node\n        if (mUpdateCameraCallback)\n            mCamera->removeUpdateCallback(mUpdateCameraCallback);\n\n        const osg::Node* head = mAnimation->getNode(\"Bip01 Head\");\n        if (head)\n        {\n            mUpdateCameraCallback = new UpdateCameraCallback(head, mPosition, mLookAt);\n            mCamera->addUpdateCallback(mUpdateCameraCallback);\n        }\n        else\n            Log(Debug::Error) << \"Error: Bip01 Head node not found\";\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/characterpreview.hpp",
    "content": "#ifndef MWRENDER_CHARACTERPREVIEW_H\n#define MWRENDER_CHARACTERPREVIEW_H\n\n#include <osg/ref_ptr>\n#include <memory>\n\n#include <osg/PositionAttitudeTransform>\n\n#include <components/esm/loadnpc.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace osg\n{\n    class Texture2D;\n    class Camera;\n    class Group;\n    class Viewport;\n}\n\nnamespace MWRender\n{\n\n    class NpcAnimation;\n    class DrawOnceCallback;\n\n    class CharacterPreview\n    {\n    public:\n        CharacterPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem, const MWWorld::Ptr& character, int sizeX, int sizeY,\n                         const osg::Vec3f& position, const osg::Vec3f& lookAt);\n        virtual ~CharacterPreview();\n\n        int getTextureWidth() const;\n        int getTextureHeight() const;\n\n        void redraw();\n\n        void rebuild();\n\n        osg::ref_ptr<osg::Texture2D> getTexture();\n\n    private:\n        CharacterPreview(const CharacterPreview&);\n        CharacterPreview& operator=(const CharacterPreview&);\n\n    protected:\n        virtual bool renderHeadOnly() { return false; }\n        void setBlendMode();\n        virtual void onSetup();\n\n        osg::ref_ptr<osg::Group> mParent;\n        Resource::ResourceSystem* mResourceSystem;\n        osg::ref_ptr<osg::Texture2D> mTexture;\n        osg::ref_ptr<osg::Camera> mCamera;\n        osg::ref_ptr<DrawOnceCallback> mDrawOnceCallback;\n\n        osg::Vec3f mPosition;\n        osg::Vec3f mLookAt;\n\n        MWWorld::Ptr mCharacter;\n\n        osg::ref_ptr<MWRender::NpcAnimation> mAnimation;\n        osg::ref_ptr<osg::PositionAttitudeTransform> mNode;\n        std::string mCurrentAnimGroup;\n\n        int mSizeX;\n        int mSizeY;\n    };\n\n    class InventoryPreview : public CharacterPreview\n    {\n    public:\n\n        InventoryPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem, const MWWorld::Ptr& character);\n\n        void updatePtr(const MWWorld::Ptr& ptr);\n\n        void update(); // Render preview again, e.g. after changed equipment\n        void setViewport(int sizeX, int sizeY);\n\n        int getSlotSelected(int posX, int posY);\n\n    protected:\n        osg::ref_ptr<osg::Viewport> mViewport;\n\n        void onSetup() override;\n    };\n\n    class UpdateCameraCallback;\n\n    class RaceSelectionPreview : public CharacterPreview\n    {\n        ESM::NPC                        mBase;\n        MWWorld::LiveCellRef<ESM::NPC>  mRef;\n\n    protected:\n\n        bool renderHeadOnly() override { return true; }\n        void onSetup() override;\n\n    public:\n        RaceSelectionPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem);\n        virtual ~RaceSelectionPreview();\n\n        void setAngle(float angleRadians);\n\n        const ESM::NPC &getPrototype() const {\n            return mBase;\n        }\n\n        void setPrototype(const ESM::NPC &proto);\n\n    private:\n\n        osg::ref_ptr<UpdateCameraCallback> mUpdateCameraCallback;\n\n        float mPitchRadians;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/creatureanimation.cpp",
    "content": "#include \"creatureanimation.hpp\"\n\n#include <osg/MatrixTransform>\n\n#include <components/esm/loadcrea.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/sceneutil/attach.hpp>\n#include <components/sceneutil/visitor.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/sceneutil/skeleton.hpp>\n#include <components/settings/settings.hpp>\n#include <components/misc/stringops.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace MWRender\n{\n\nCreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr,\n                                     const std::string& model, Resource::ResourceSystem* resourceSystem)\n  : ActorAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), resourceSystem)\n{\n    MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();\n\n    if(!model.empty())\n    {\n        setObjectRoot(model, false, false, true);\n\n        if((ref->mBase->mFlags&ESM::Creature::Bipedal))\n            addAnimSource(Settings::Manager::getString(\"xbaseanim\", \"Models\"), model);\n        addAnimSource(model, model);\n    }\n}\n\n\nCreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem)\n    : ActorAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), resourceSystem)\n    , mShowWeapons(false)\n    , mShowCarriedLeft(false)\n{\n    MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();\n\n    if(!model.empty())\n    {\n        setObjectRoot(model, true, false, true);\n\n        if((ref->mBase->mFlags&ESM::Creature::Bipedal))\n        {\n            addAnimSource(Settings::Manager::getString(\"xbaseanim\", \"Models\"), model);\n        }\n        addAnimSource(model, model);\n\n        mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr);\n\n        updateParts();\n    }\n\n    mWeaponAnimationTime = std::shared_ptr<WeaponAnimationTime>(new WeaponAnimationTime(this));\n}\n\nvoid CreatureWeaponAnimation::showWeapons(bool showWeapon)\n{\n    if (showWeapon != mShowWeapons)\n    {\n        mShowWeapons = showWeapon;\n        updateParts();\n    }\n}\n\nvoid CreatureWeaponAnimation::showCarriedLeft(bool show)\n{\n    if (show != mShowCarriedLeft)\n    {\n        mShowCarriedLeft = show;\n        updateParts();\n    }\n}\n\nvoid CreatureWeaponAnimation::updateParts()\n{\n    mAmmunition.reset();\n    mWeapon.reset();\n    mShield.reset();\n\n    updateHolsteredWeapon(!mShowWeapons);\n    updateQuiver();\n    updateHolsteredShield(mShowCarriedLeft);\n\n    if (mShowWeapons)\n        updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);\n    if (mShowCarriedLeft)\n        updatePart(mShield, MWWorld::InventoryStore::Slot_CarriedLeft);\n}\n\nvoid CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)\n{\n    if (!mObjectRoot)\n        return;\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator it = inv.getSlot(slot);\n\n    if (it == inv.end())\n    {\n        scene.reset();\n        return;\n    }\n    MWWorld::ConstPtr item = *it;\n\n    std::string bonename;\n    std::string itemModel = item.getClass().getModel(item);\n    if (slot == MWWorld::InventoryStore::Slot_CarriedRight)\n    {\n        if(item.getTypeName() == typeid(ESM::Weapon).name())\n        {\n            int type = item.get<ESM::Weapon>()->mBase->mData.mType;\n            bonename = MWMechanics::getWeaponType(type)->mAttachBone;\n            if (bonename != \"Weapon Bone\")\n            {\n                const NodeMap& nodeMap = getNodeMap();\n                NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));\n                if (found == nodeMap.end())\n                    bonename = \"Weapon Bone\";\n            }\n        }\n        else\n            bonename = \"Weapon Bone\";\n    }\n    else\n    {\n        bonename = \"Shield Bone\";\n        if (item.getTypeName() == typeid(ESM::Armor).name())\n        {\n            // Shield body part model should be used if possible.\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            for (const auto& part : item.get<ESM::Armor>()->mBase->mParts.mParts)\n            {\n                // Assume all creatures use the male mesh.\n                if (part.mPart != ESM::PRT_Shield || part.mMale.empty())\n                    continue;\n                const ESM::BodyPart *bodypart = store.get<ESM::BodyPart>().search(part.mMale);\n                if (bodypart && bodypart->mData.mType == ESM::BodyPart::MT_Armor && !bodypart->mModel.empty())\n                {\n                    itemModel = \"meshes\\\\\" + bodypart->mModel;\n                    break;\n                }\n            }\n        }\n    }\n\n    try\n    {\n        osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(itemModel);\n\n        const NodeMap& nodeMap = getNodeMap();\n        NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));\n        if (found == nodeMap.end())\n            throw std::runtime_error(\"Can't find attachment node \" + bonename);\n        osg::ref_ptr<osg::Node> attached = SceneUtil::attach(node, mObjectRoot, bonename, found->second.get());\n\n        scene.reset(new PartHolder(attached));\n\n        if (!item.getClass().getEnchantment(item).empty())\n            mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, item.getClass().getEnchantmentColor(item));\n\n        // Crossbows start out with a bolt attached\n        // FIXME: code duplicated from NpcAnimation\n        if (slot == MWWorld::InventoryStore::Slot_CarriedRight &&\n                item.getTypeName() == typeid(ESM::Weapon).name() &&\n                item.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)\n        {\n            const ESM::WeaponType* weaponInfo = MWMechanics::getWeaponType(ESM::Weapon::MarksmanCrossbow);\n            MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n            if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == weaponInfo->mAmmoType)\n                attachArrow();\n            else\n                mAmmunition.reset();\n        }\n        else\n            mAmmunition.reset();\n\n        std::shared_ptr<SceneUtil::ControllerSource> source;\n\n        if (slot == MWWorld::InventoryStore::Slot_CarriedRight)\n            source = mWeaponAnimationTime;\n        else\n            source.reset(new NullAnimationTime);\n\n        SceneUtil::AssignControllerSourcesVisitor assignVisitor(source);\n        attached->accept(assignVisitor);\n    }\n    catch (std::exception& e)\n    {\n        Log(Debug::Error) << \"Can not add creature part: \" << e.what();\n    }\n}\n\nbool CreatureWeaponAnimation::isArrowAttached() const\n{\n    return mAmmunition != nullptr;\n}\n\nvoid CreatureWeaponAnimation::detachArrow()\n{\n    WeaponAnimation::detachArrow(mPtr);\n    updateQuiver();\n}\n\nvoid CreatureWeaponAnimation::attachArrow()\n{\n    WeaponAnimation::attachArrow(mPtr);\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n    if (ammo != inv.end() && !ammo->getClass().getEnchantment(*ammo).empty())\n    {\n        osg::Group* bone = getArrowBone();\n        if (bone != nullptr && bone->getNumChildren())\n            SceneUtil::addEnchantedGlow(bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo));\n    }\n\n    updateQuiver();\n}\n\nvoid CreatureWeaponAnimation::releaseArrow(float attackStrength)\n{\n    WeaponAnimation::releaseArrow(mPtr, attackStrength);\n    updateQuiver();\n}\n\nosg::Group *CreatureWeaponAnimation::getArrowBone()\n{\n    if (!mWeapon)\n        return nullptr;\n\n    if (!mPtr.getClass().hasInventoryStore(mPtr))\n        return nullptr;\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())\n        return nullptr;\n\n    int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;\n    int ammoType = MWMechanics::getWeaponType(type)->mAmmoType;\n\n    // Try to find and attachment bone in actor's skeleton, otherwise fall back to the ArrowBone in weapon's mesh\n    osg::Group* bone = getBoneByName(MWMechanics::getWeaponType(ammoType)->mAttachBone);\n    if (bone == nullptr)\n    {\n        SceneUtil::FindByNameVisitor findVisitor (\"ArrowBone\");\n        mWeapon->getNode()->accept(findVisitor);\n        bone = findVisitor.mFoundNode;\n    }\n    return bone;\n}\n\nosg::Node *CreatureWeaponAnimation::getWeaponNode()\n{\n    return mWeapon ? mWeapon->getNode().get() : nullptr;\n}\n\nResource::ResourceSystem *CreatureWeaponAnimation::getResourceSystem()\n{\n    return mResourceSystem;\n}\n\nvoid CreatureWeaponAnimation::addControllers()\n{\n    Animation::addControllers();\n    WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());\n}\n\nosg::Vec3f CreatureWeaponAnimation::runAnimation(float duration)\n{\n    osg::Vec3f ret = Animation::runAnimation(duration);\n\n    WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians());\n\n    return ret;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/creatureanimation.hpp",
    "content": "#ifndef GAME_RENDER_CREATUREANIMATION_H\n#define GAME_RENDER_CREATUREANIMATION_H\n\n#include \"actoranimation.hpp\"\n#include \"weaponanimation.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\nnamespace MWWorld\n{\n    class Ptr;\n}\n\nnamespace MWRender\n{\n    class CreatureAnimation : public ActorAnimation\n    {\n    public:\n        CreatureAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem);\n        virtual ~CreatureAnimation() {}\n    };\n\n    // For creatures with weapons and shields\n    // Animation is already virtual anyway, so might as well make a separate class.\n    // Most creatures don't need weapons/shields, so this will save some memory.\n    class CreatureWeaponAnimation : public ActorAnimation, public WeaponAnimation, public MWWorld::InventoryStoreListener\n    {\n    public:\n        CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem);\n        virtual ~CreatureWeaponAnimation() {}\n\n        void equipmentChanged() override { updateParts(); }\n\n        void showWeapons(bool showWeapon) override;\n\n        bool getCarriedLeftShown() const override { return mShowCarriedLeft; }\n        void showCarriedLeft(bool show) override;\n\n        void updateParts();\n\n        void updatePart(PartHolderPtr& scene, int slot);\n\n        void attachArrow() override;\n        void detachArrow() override;\n        void releaseArrow(float attackStrength) override;\n        // WeaponAnimation\n        osg::Group* getArrowBone() override;\n        osg::Node* getWeaponNode() override;\n        Resource::ResourceSystem* getResourceSystem() override;\n        void showWeapon(bool show) override { showWeapons(show); }\n        void setWeaponGroup(const std::string& group, bool relativeDuration) override { mWeaponAnimationTime->setGroup(group, relativeDuration); }\n\n        void addControllers() override;\n\n        osg::Vec3f runAnimation(float duration) override;\n\n        /// A relative factor (0-1) that decides if and how much the skeleton should be pitched\n        /// to indicate the facing orientation of the character.\n        void setPitchFactor(float factor) override { mPitchFactor = factor; }\n\n    protected:\n        bool isArrowAttached() const override;\n\n    private:\n        PartHolderPtr mWeapon;\n        PartHolderPtr mShield;\n        bool mShowWeapons;\n        bool mShowCarriedLeft;\n\n        std::shared_ptr<WeaponAnimationTime> mWeaponAnimationTime;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/effectmanager.cpp",
    "content": "#include \"effectmanager.hpp\"\n\n#include <osg/PositionAttitudeTransform>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n\n#include <components/sceneutil/controller.hpp>\n\n#include \"animation.hpp\"\n#include \"vismask.hpp\"\n#include \"util.hpp\"\n\nnamespace MWRender\n{\n\nEffectManager::EffectManager(osg::ref_ptr<osg::Group> parent, Resource::ResourceSystem* resourceSystem)\n    : mParentNode(parent)\n    , mResourceSystem(resourceSystem)\n{\n}\n\nEffectManager::~EffectManager()\n{\n    clear();\n}\n\nvoid EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)\n{\n    osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model);\n\n    node->setNodeMask(Mask_Effect);\n\n    Effect effect;\n    effect.mAnimTime.reset(new EffectAnimationTime);\n\n    SceneUtil::FindMaxControllerLengthVisitor findMaxLengthVisitor;\n    node->accept(findMaxLengthVisitor);\n    effect.mMaxControllerLength = findMaxLengthVisitor.getMaxLength();\n\n    osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;\n    trans->setPosition(worldPosition);\n    trans->setScale(osg::Vec3f(scale, scale, scale));\n    trans->addChild(node);\n\n    SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime);\n    node->accept(assignVisitor);\n\n    if (isMagicVFX)\n        overrideFirstRootTexture(textureOverride, mResourceSystem, node);\n    else\n        overrideTexture(textureOverride, mResourceSystem, node);\n\n    mParentNode->addChild(trans);\n\n    mEffects[trans] = effect;\n}\n\nvoid EffectManager::update(float dt)\n{\n    for (EffectMap::iterator it = mEffects.begin(); it != mEffects.end(); )\n    {\n        it->second.mAnimTime->addTime(dt);\n\n        if (it->second.mAnimTime->getTime() >= it->second.mMaxControllerLength)\n        {\n            mParentNode->removeChild(it->first);\n            mEffects.erase(it++);\n        }\n        else\n            ++it;\n    }\n}\n\nvoid EffectManager::clear()\n{\n    for (EffectMap::iterator it = mEffects.begin(); it != mEffects.end(); ++it)\n    {\n        mParentNode->removeChild(it->first);\n    }\n    mEffects.clear();\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/effectmanager.hpp",
    "content": "#ifndef OPENMW_MWRENDER_EFFECTMANAGER_H\n#define OPENMW_MWRENDER_EFFECTMANAGER_H\n\n#include <map>\n#include <memory>\n#include <string>\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class Group;\n    class Vec3f;\n    class PositionAttitudeTransform;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWRender\n{\n    class EffectAnimationTime;\n\n    // Note: effects attached to another object should be managed by MWRender::Animation::addEffect.\n    // This class manages \"free\" effects, i.e. attached to a dedicated scene node in the world.\n    class EffectManager\n    {\n    public:\n        EffectManager(osg::ref_ptr<osg::Group> parent, Resource::ResourceSystem* resourceSystem);\n        ~EffectManager();\n\n        /// Add an effect. When it's finished playing, it will be removed automatically.\n        void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true);\n\n        void update(float dt);\n\n        /// Remove all effects\n        void clear();\n\n    private:\n        struct Effect\n        {\n            float mMaxControllerLength;\n            std::shared_ptr<EffectAnimationTime> mAnimTime;\n        };\n\n        typedef std::map<osg::ref_ptr<osg::PositionAttitudeTransform>, Effect> EffectMap;\n        EffectMap mEffects;\n\n        osg::ref_ptr<osg::Group> mParentNode;\n        Resource::ResourceSystem* mResourceSystem;\n\n        EffectManager(const EffectManager&);\n        void operator=(const EffectManager&);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/fogmanager.cpp",
    "content": "#include \"fogmanager.hpp\"\n\n#include <algorithm>\n\n#include <components/esm/loadcell.hpp>\n#include <components/fallback/fallback.hpp>\n#include <components/sceneutil/util.hpp>\n#include <components/settings/settings.hpp>\n\nnamespace\n{\n    float DLLandFogStart;\n    float DLLandFogEnd;\n    float DLUnderwaterFogStart;\n    float DLUnderwaterFogEnd;\n    float DLInteriorFogStart;\n    float DLInteriorFogEnd;\n}\n\nnamespace MWRender\n{\n    FogManager::FogManager()\n        : mLandFogStart(0.f)\n        , mLandFogEnd(std::numeric_limits<float>::max())\n        , mUnderwaterFogStart(0.f)\n        , mUnderwaterFogEnd(std::numeric_limits<float>::max())\n        , mFogColor(osg::Vec4f())\n        , mDistantFog(Settings::Manager::getBool(\"use distant fog\", \"Fog\"))\n        , mUnderwaterColor(Fallback::Map::getColour(\"Water_UnderwaterColor\"))\n        , mUnderwaterWeight(Fallback::Map::getFloat(\"Water_UnderwaterColorWeight\"))\n        , mUnderwaterIndoorFog(Fallback::Map::getFloat(\"Water_UnderwaterIndoorFog\"))\n    {\n        DLLandFogStart = Settings::Manager::getFloat(\"distant land fog start\", \"Fog\");\n        DLLandFogEnd = Settings::Manager::getFloat(\"distant land fog end\", \"Fog\");\n        DLUnderwaterFogStart = Settings::Manager::getFloat(\"distant underwater fog start\", \"Fog\");\n        DLUnderwaterFogEnd = Settings::Manager::getFloat(\"distant underwater fog end\", \"Fog\");\n        DLInteriorFogStart = Settings::Manager::getFloat(\"distant interior fog start\", \"Fog\");\n        DLInteriorFogEnd = Settings::Manager::getFloat(\"distant interior fog end\", \"Fog\");\n    }\n\n    void FogManager::configure(float viewDistance, const ESM::Cell *cell)\n    {\n        osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog);\n\n        if (mDistantFog)\n        {\n            float density = std::max(0.2f, cell->mAmbi.mFogDensity);\n            mLandFogStart = DLInteriorFogEnd * (1.0f - density) + DLInteriorFogStart*density;\n            mLandFogEnd = DLInteriorFogEnd;\n            mUnderwaterFogStart = DLUnderwaterFogStart;\n            mUnderwaterFogEnd = DLUnderwaterFogEnd;\n            mFogColor = color;\n        }\n        else\n            configure(viewDistance, cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color);\n    }\n\n    void FogManager::configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)\n    {\n        if (mDistantFog)\n        {\n            mLandFogStart = dlFactor * (DLLandFogStart - dlOffset * DLLandFogEnd);\n            mLandFogEnd = dlFactor * (1.0f - dlOffset) * DLLandFogEnd;\n            mUnderwaterFogStart = DLUnderwaterFogStart;\n            mUnderwaterFogEnd = DLUnderwaterFogEnd;\n        }\n        else\n        {\n            if (fogDepth == 0.0)\n            {\n                mLandFogStart = 0.0f;\n                mLandFogEnd = std::numeric_limits<float>::max();\n            }\n            else\n            {\n                mLandFogStart = viewDistance * (1 - fogDepth);\n                mLandFogEnd = viewDistance;\n            }\n            mUnderwaterFogStart = std::min(viewDistance, 7168.f) * (1 - underwaterFog);\n            mUnderwaterFogEnd = std::min(viewDistance, 7168.f);\n        }\n        mFogColor = color;\n    }\n\n    float FogManager::getFogStart(bool isUnderwater) const\n    {\n        return isUnderwater ? mUnderwaterFogStart : mLandFogStart;\n    }\n\n    float FogManager::getFogEnd(bool isUnderwater) const\n    {\n        return isUnderwater ? mUnderwaterFogEnd : mLandFogEnd;\n    }\n\n    osg::Vec4f FogManager::getFogColor(bool isUnderwater) const\n    {\n        if (isUnderwater)\n        {\n            return mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight);\n        }\n\n        return mFogColor;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/fogmanager.hpp",
    "content": "#ifndef OPENMW_MWRENDER_FOGMANAGER_H\n#define OPENMW_MWRENDER_FOGMANAGER_H\n\n#include <osg/Vec4f>\n\nnamespace ESM\n{\n    struct Cell;\n}\n\nnamespace MWRender\n{\n    class FogManager\n    {\n    public:\n        FogManager();\n\n        void configure(float viewDistance, const ESM::Cell *cell);\n        void configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color);\n\n        osg::Vec4f getFogColor(bool isUnderwater) const;\n        float getFogStart(bool isUnderwater) const;\n        float getFogEnd(bool isUnderwater) const;\n\n    private:\n        float mLandFogStart;\n        float mLandFogEnd;\n        float mUnderwaterFogStart;\n        float mUnderwaterFogEnd;\n        osg::Vec4f mFogColor;\n        bool mDistantFog;\n\n        osg::Vec4f mUnderwaterColor;\n        float mUnderwaterWeight;\n        float mUnderwaterIndoorFog;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/globalmap.cpp",
    "content": "#include \"globalmap.hpp\"\n\n#include <osg/Image>\n#include <osg/Texture2D>\n#include <osg/Group>\n#include <osg/Geometry>\n#include <osg/Depth>\n#include <osg/TexEnvCombine>\n\n#include <osgDB/WriteFile>\n\n#include <components/settings/settings.hpp>\n#include <components/files/memorystream.hpp>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/sceneutil/workqueue.hpp>\n\n#include <components/esm/globalmap.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/Worldstate.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"vismask.hpp\"\n\nnamespace\n{\n\n    // Create a screen-aligned quad with given texture coordinates.\n    // Assumes a top-left origin of the sampled image.\n    osg::ref_ptr<osg::Geometry> createTexturedQuad(float leftTexCoord, float topTexCoord, float rightTexCoord, float bottomTexCoord)\n    {\n        osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;\n\n        osg::ref_ptr<osg::Vec3Array> verts = new osg::Vec3Array;\n        verts->push_back(osg::Vec3f(-1, -1, 0));\n        verts->push_back(osg::Vec3f(-1, 1, 0));\n        verts->push_back(osg::Vec3f(1, 1, 0));\n        verts->push_back(osg::Vec3f(1, -1, 0));\n\n        geom->setVertexArray(verts);\n\n        osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array;\n        texcoords->push_back(osg::Vec2f(leftTexCoord, 1.f-bottomTexCoord));\n        texcoords->push_back(osg::Vec2f(leftTexCoord, 1.f-topTexCoord));\n        texcoords->push_back(osg::Vec2f(rightTexCoord, 1.f-topTexCoord));\n        texcoords->push_back(osg::Vec2f(rightTexCoord, 1.f-bottomTexCoord));\n\n        osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;\n        colors->push_back(osg::Vec4(1.f, 1.f, 1.f, 1.f));\n        geom->setColorArray(colors, osg::Array::BIND_OVERALL);\n\n        geom->setTexCoordArray(0, texcoords, osg::Array::BIND_PER_VERTEX);\n\n        geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));\n\n        return geom;\n    }\n\n\n    class CameraUpdateGlobalCallback : public osg::NodeCallback\n    {\n    public:\n        CameraUpdateGlobalCallback(MWRender::GlobalMap* parent)\n            : mRendered(false)\n            , mParent(parent)\n        {\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            if (mRendered)\n            {\n                if (mParent->copyResult(static_cast<osg::Camera*>(node), nv->getTraversalNumber()))\n                {\n                    node->setNodeMask(0);\n                    mParent->markForRemoval(static_cast<osg::Camera*>(node));\n                }\n                return;\n            }\n\n            traverse(node, nv);\n\n            mRendered = true;\n        }\n\n    private:\n        bool mRendered;\n        MWRender::GlobalMap* mParent;\n    };\n\n}\n\nnamespace MWRender\n{\n    /*\n        Start of tes3mp addition\n\n        Use maps to track which global map coordinates belong to which cell coordinates\n        without having to significantly change other methods\n    */\n    std::map<int, int> originToCellX;\n    std::map<int, int> originToCellY;\n    /*\n        End of tes3mp addition\n    */\n\n    class CreateMapWorkItem : public SceneUtil::WorkItem\n    {\n    public:\n        CreateMapWorkItem(int width, int height, int minX, int minY, int maxX, int maxY, int cellSize, const MWWorld::Store<ESM::Land>& landStore)\n            : mWidth(width), mHeight(height), mMinX(minX), mMinY(minY), mMaxX(maxX), mMaxY(maxY), mCellSize(cellSize), mLandStore(landStore)\n        {\n        }\n\n        void doWork() override\n        {\n            osg::ref_ptr<osg::Image> image = new osg::Image;\n            image->allocateImage(mWidth, mHeight, 1, GL_RGB, GL_UNSIGNED_BYTE);\n            unsigned char* data = image->data();\n\n            osg::ref_ptr<osg::Image> alphaImage = new osg::Image;\n            alphaImage->allocateImage(mWidth, mHeight, 1, GL_ALPHA, GL_UNSIGNED_BYTE);\n            unsigned char* alphaData = alphaImage->data();\n\n            for (int x = mMinX; x <= mMaxX; ++x)\n            {\n                for (int y = mMinY; y <= mMaxY; ++y)\n                {\n                    const ESM::Land* land = mLandStore.search (x,y);\n\n                    for (int cellY=0; cellY<mCellSize; ++cellY)\n                    {\n                        for (int cellX=0; cellX<mCellSize; ++cellX)\n                        {\n                            int vertexX = static_cast<int>(float(cellX) / float(mCellSize) * 9);\n                            int vertexY = static_cast<int>(float(cellY) / float(mCellSize) * 9);\n\n                            int texelX = (x-mMinX) * mCellSize + cellX;\n                            int texelY = (y-mMinY) * mCellSize + cellY;\n\n                            unsigned char r,g,b;\n\n                            float y2 = 0;\n                            if (land && (land->mDataTypes & ESM::Land::DATA_WNAM))\n                                y2 = land->mWnam[vertexY * 9 + vertexX] / 128.f;\n                            else\n                                y2 = SCHAR_MIN / 128.f;\n                            if (y2 < 0)\n                            {\n                                r = static_cast<unsigned char>(14 * y2 + 38);\n                                g = static_cast<unsigned char>(20 * y2 + 56);\n                                b = static_cast<unsigned char>(18 * y2 + 51);\n                            }\n                            else if (y2 < 0.3f)\n                            {\n                                if (y2 < 0.1f)\n                                    y2 *= 8.f;\n                                else\n                                {\n                                    y2 -= 0.1f;\n                                    y2 += 0.8f;\n                                }\n                                r = static_cast<unsigned char>(66 - 32 * y2);\n                                g = static_cast<unsigned char>(48 - 23 * y2);\n                                b = static_cast<unsigned char>(33 - 16 * y2);\n                            }\n                            else\n                            {\n                                y2 -= 0.3f;\n                                y2 *= 1.428f;\n                                r = static_cast<unsigned char>(34 - 29 * y2);\n                                g = static_cast<unsigned char>(25 - 20 * y2);\n                                b = static_cast<unsigned char>(17 - 12 * y2);\n                            }\n\n                            data[texelY * mWidth * 3 + texelX * 3] = r;\n                            data[texelY * mWidth * 3 + texelX * 3+1] = g;\n                            data[texelY * mWidth * 3 + texelX * 3+2] = b;\n\n                            alphaData[texelY * mWidth+ texelX] = (y2 < 0) ? static_cast<unsigned char>(0) : static_cast<unsigned char>(255);\n                        }\n                    }\n                }\n            }\n\n            mBaseTexture = new osg::Texture2D;\n            mBaseTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n            mBaseTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n            mBaseTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n            mBaseTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n            mBaseTexture->setImage(image);\n            mBaseTexture->setResizeNonPowerOfTwoHint(false);\n\n            mAlphaTexture = new osg::Texture2D;\n            mAlphaTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n            mAlphaTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n            mAlphaTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n            mAlphaTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n            mAlphaTexture->setImage(alphaImage);\n            mAlphaTexture->setResizeNonPowerOfTwoHint(false);\n\n            mOverlayImage = new osg::Image;\n            mOverlayImage->allocateImage(mWidth, mHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE);\n            assert(mOverlayImage->isDataContiguous());\n\n            memset(mOverlayImage->data(), 0, mOverlayImage->getTotalSizeInBytes());\n\n            mOverlayTexture = new osg::Texture2D;\n            mOverlayTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n            mOverlayTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n            mOverlayTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n            mOverlayTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n            mOverlayTexture->setResizeNonPowerOfTwoHint(false);\n            mOverlayTexture->setInternalFormat(GL_RGBA);\n            mOverlayTexture->setTextureSize(mWidth, mHeight);\n        }\n\n        int mWidth, mHeight;\n        int mMinX, mMinY, mMaxX, mMaxY;\n        int mCellSize;\n        const MWWorld::Store<ESM::Land>& mLandStore;\n\n        osg::ref_ptr<osg::Texture2D> mBaseTexture;\n        osg::ref_ptr<osg::Texture2D> mAlphaTexture;\n\n        osg::ref_ptr<osg::Image> mOverlayImage;\n        osg::ref_ptr<osg::Texture2D> mOverlayTexture;\n    };\n\n    GlobalMap::GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue)\n        : mRoot(root)\n        , mWorkQueue(workQueue)\n        , mWidth(0)\n        , mHeight(0)\n        , mMinX(0), mMaxX(0)\n        , mMinY(0), mMaxY(0)\n\n    {\n        /*\n            Start of tes3mp change (major)\n\n            We need map tiles to have consistent sizes, because the server's map\n            is gradually filled in through tiles sent by players via WorldMap packets\n\n            As a result, the default value is enforced for the time being\n        */\n        //mCellSize = Settings::Manager::getInt(\"global map cell size\", \"Map\");\n        mCellSize = 18;\n        /*\n            End of tes3mp change (major)\n        */\n    }\n\n    GlobalMap::~GlobalMap()\n    {\n        for (auto& camera : mCamerasPendingRemoval)\n            removeCamera(camera);\n        for (auto& camera : mActiveCameras)\n            removeCamera(camera);\n\n        if (mWorkItem)\n            mWorkItem->waitTillDone();\n    }\n\n    void GlobalMap::render ()\n    {\n        const MWWorld::ESMStore &esmStore =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        // get the size of the world\n        MWWorld::Store<ESM::Cell>::iterator it = esmStore.get<ESM::Cell>().extBegin();\n        for (; it != esmStore.get<ESM::Cell>().extEnd(); ++it)\n        {\n            if (it->getGridX() < mMinX)\n                mMinX = it->getGridX();\n            if (it->getGridX() > mMaxX)\n                mMaxX = it->getGridX();\n            if (it->getGridY() < mMinY)\n                mMinY = it->getGridY();\n            if (it->getGridY() > mMaxY)\n                mMaxY = it->getGridY();\n        }\n\n        mWidth = mCellSize*(mMaxX-mMinX+1);\n        mHeight = mCellSize*(mMaxY-mMinY+1);\n\n        mWorkItem = new CreateMapWorkItem(mWidth, mHeight, mMinX, mMinY, mMaxX, mMaxY, mCellSize, esmStore.get<ESM::Land>());\n        mWorkQueue->addWorkItem(mWorkItem);\n    }\n\n    void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY)\n    {\n        imageX = float(x / float(Constants::CellSizeInUnits) - mMinX) / (mMaxX - mMinX + 1);\n\n        imageY = 1.f-float(z / float(Constants::CellSizeInUnits) - mMinY) / (mMaxY - mMinY + 1);\n    }\n\n    void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY)\n    {\n        imageX = float(x - mMinX) / (mMaxX - mMinX + 1);\n\n        // NB y + 1, because we want the top left corner, not bottom left where the origin of the cell is\n        imageY = 1.f-float(y - mMinY + 1) / (mMaxY - mMinY + 1);\n    }\n\n    void GlobalMap::requestOverlayTextureUpdate(int x, int y, int width, int height, osg::ref_ptr<osg::Texture2D> texture, bool clear, bool cpuCopy,\n                                                float srcLeft, float srcTop, float srcRight, float srcBottom)\n    {\n        osg::ref_ptr<osg::Camera> camera (new osg::Camera);\n        camera->setNodeMask(Mask_RenderToTexture);\n        camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);\n        camera->setViewMatrix(osg::Matrix::identity());\n        camera->setProjectionMatrix(osg::Matrix::identity());\n        camera->setProjectionResizePolicy(osg::Camera::FIXED);\n        camera->setRenderOrder(osg::Camera::PRE_RENDER, 1); // Make sure the global map is rendered after the local map\n        y = mHeight - y - height; // convert top-left origin to bottom-left\n        camera->setViewport(x, y, width, height);\n\n        if (clear)\n        {\n            camera->setClearMask(GL_COLOR_BUFFER_BIT);\n            camera->setClearColor(osg::Vec4(0,0,0,0));\n        }\n        else\n            camera->setClearMask(GL_NONE);\n\n        camera->setUpdateCallback(new CameraUpdateGlobalCallback(this));\n\n        camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);\n        camera->attach(osg::Camera::COLOR_BUFFER, mOverlayTexture);\n\n        // no need for a depth buffer\n        camera->setImplicitBufferAttachmentMask(osg::DisplaySettings::IMPLICIT_COLOR_BUFFER_ATTACHMENT);\n\n        if (cpuCopy)\n        {\n            // Attach an image to copy the render back to the CPU when finished\n            osg::ref_ptr<osg::Image> image (new osg::Image);\n            image->setPixelFormat(mOverlayImage->getPixelFormat());\n            image->setDataType(mOverlayImage->getDataType());\n            camera->attach(osg::Camera::COLOR_BUFFER, image);\n\n            ImageDest imageDest;\n            imageDest.mImage = image;\n            imageDest.mX = x;\n            imageDest.mY = y;\n            mPendingImageDest[camera] = imageDest;\n        }\n\n        // Create a quad rendering the updated texture\n        if (texture)\n        {\n            osg::ref_ptr<osg::Geometry> geom = createTexturedQuad(srcLeft, srcTop, srcRight, srcBottom);\n            osg::ref_ptr<osg::Depth> depth = new osg::Depth;\n            depth->setWriteMask(0);\n            osg::StateSet* stateset = geom->getOrCreateStateSet();\n            stateset->setAttribute(depth);\n            stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);\n            stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n            stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n\n            if (mAlphaTexture)\n            {\n                osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array;\n\n                float x1 = x / static_cast<float>(mWidth);\n                float x2 = (x + width) / static_cast<float>(mWidth);\n                float y1 = y / static_cast<float>(mHeight);\n                float y2 = (y + height) / static_cast<float>(mHeight);\n                texcoords->push_back(osg::Vec2f(x1, y1));\n                texcoords->push_back(osg::Vec2f(x1, y2));\n                texcoords->push_back(osg::Vec2f(x2, y2));\n                texcoords->push_back(osg::Vec2f(x2, y1));\n                geom->setTexCoordArray(1, texcoords, osg::Array::BIND_PER_VERTEX);\n\n                stateset->setTextureAttributeAndModes(1, mAlphaTexture, osg::StateAttribute::ON);\n                osg::ref_ptr<osg::TexEnvCombine> texEnvCombine = new osg::TexEnvCombine;\n                texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE);\n                texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);\n                stateset->setTextureAttributeAndModes(1, texEnvCombine);\n            }\n\n            camera->addChild(geom);\n        }\n\n        mRoot->addChild(camera);\n\n        mActiveCameras.push_back(camera);\n    }\n\n    void GlobalMap::exploreCell(int cellX, int cellY, osg::ref_ptr<osg::Texture2D> localMapTexture)\n    {\n        ensureLoaded();\n\n        if (!localMapTexture)\n            return;\n\n        int originX = (cellX - mMinX) * mCellSize;\n        int originY = (cellY - mMinY + 1) * mCellSize; // +1 because we want the top left corner of the cell, not the bottom left\n\n        if (cellX > mMaxX || cellX < mMinX || cellY > mMaxY || cellY < mMinY)\n            return;\n\n        /*\n            Start of tes3mp addition\n\n            Track the cell coordinates corresponding to these map image coordinates\n        */\n        originToCellX[originX] = cellX;\n        originToCellY[originY - mCellSize] = cellY;\n        /*\n            End of tes3mp addition\n        */\n\n        requestOverlayTextureUpdate(originX, mHeight - originY, mCellSize, mCellSize, localMapTexture, false, true);\n    }\n\n    void GlobalMap::clear()\n    {\n        ensureLoaded();\n\n        memset(mOverlayImage->data(), 0, mOverlayImage->getTotalSizeInBytes());\n\n        mPendingImageDest.clear();\n\n        // just push a Camera to clear the FBO, instead of setImage()/dirty()\n        // easier, since we don't need to worry about synchronizing access :)\n        requestOverlayTextureUpdate(0, 0, mWidth, mHeight, osg::ref_ptr<osg::Texture2D>(), true, false);\n    }\n\n    void GlobalMap::write(ESM::GlobalMap& map)\n    {\n        ensureLoaded();\n\n        map.mBounds.mMinX = mMinX;\n        map.mBounds.mMaxX = mMaxX;\n        map.mBounds.mMinY = mMinY;\n        map.mBounds.mMaxY = mMaxY;\n\n        std::ostringstream ostream;\n        osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n        if (!readerwriter)\n        {\n            Log(Debug::Error) << \"Error: Can't write map overlay: no png readerwriter found\";\n            return;\n        }\n\n        osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mOverlayImage, ostream);\n        if (!result.success())\n        {\n            Log(Debug::Warning) << \"Error: Can't write map overlay: \" << result.message() << \" code \" << result.status();\n            return;\n        }\n\n        std::string data = ostream.str();\n        map.mImageData = std::vector<char>(data.begin(), data.end());\n    }\n\n    struct Box\n    {\n        int mLeft, mTop, mRight, mBottom;\n\n        Box(int left, int top, int right, int bottom)\n            : mLeft(left), mTop(top), mRight(right), mBottom(bottom)\n        {\n        }\n        bool operator == (const Box& other)\n        {\n            return mLeft == other.mLeft && mTop == other.mTop && mRight == other.mRight && mBottom == other.mBottom;\n        }\n    };\n\n    void GlobalMap::read(ESM::GlobalMap& map)\n    {\n        ensureLoaded();\n\n        const ESM::GlobalMap::Bounds& bounds = map.mBounds;\n\n        if (bounds.mMaxX-bounds.mMinX < 0)\n            return;\n        if (bounds.mMaxY-bounds.mMinY < 0)\n            return;\n\n        if (bounds.mMinX > bounds.mMaxX\n                || bounds.mMinY > bounds.mMaxY)\n            throw std::runtime_error(\"invalid map bounds\");\n\n        if (map.mImageData.empty())\n            return;\n\n        Files::IMemStream istream(map.mImageData.data(), map.mImageData.size());\n\n        osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n        if (!readerwriter)\n        {\n            Log(Debug::Error) << \"Error: Can't read map overlay: no png readerwriter found\";\n            return;\n        }\n\n        osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(istream);\n        if (!result.success())\n        {\n            Log(Debug::Error) << \"Error: Can't read map overlay: \" << result.message() << \" code \" << result.status();\n            return;\n        }\n\n        osg::ref_ptr<osg::Image> image = result.getImage();\n        int imageWidth = image->s();\n        int imageHeight = image->t();\n\n        int xLength = (bounds.mMaxX-bounds.mMinX+1);\n        int yLength = (bounds.mMaxY-bounds.mMinY+1);\n\n        // Size of one cell in image space\n        int cellImageSizeSrc = imageWidth / xLength;\n        if (int(imageHeight / yLength) != cellImageSizeSrc)\n            throw std::runtime_error(\"cell size must be quadratic\");\n\n        // If cell bounds of the currently loaded content and the loaded savegame do not match,\n        // we need to resize source/dest boxes to accommodate\n        // This means nonexisting cells will be dropped silently\n        int cellImageSizeDst = mCellSize;\n\n        // Completely off-screen? -> no need to blit anything\n        if (bounds.mMaxX < mMinX\n                || bounds.mMaxY < mMinY\n                || bounds.mMinX > mMaxX\n                || bounds.mMinY > mMaxY)\n            return;\n\n        int leftDiff = (mMinX - bounds.mMinX);\n        int topDiff = (bounds.mMaxY - mMaxY);\n        int rightDiff = (bounds.mMaxX - mMaxX);\n        int bottomDiff =  (mMinY - bounds.mMinY);\n\n        Box srcBox ( std::max(0, leftDiff * cellImageSizeSrc),\n                                  std::max(0, topDiff * cellImageSizeSrc),\n                                  std::min(imageWidth, imageWidth - rightDiff * cellImageSizeSrc),\n                                  std::min(imageHeight, imageHeight - bottomDiff * cellImageSizeSrc));\n\n        Box destBox ( std::max(0, -leftDiff * cellImageSizeDst),\n                                   std::max(0, -topDiff * cellImageSizeDst),\n                                   std::min(mWidth, mWidth + rightDiff * cellImageSizeDst),\n                                   std::min(mHeight, mHeight + bottomDiff * cellImageSizeDst));\n\n        osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);\n        texture->setImage(image);\n        texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n        texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n        texture->setResizeNonPowerOfTwoHint(false);\n\n        if (srcBox == destBox && imageWidth == mWidth && imageHeight == mHeight)\n        {\n            mOverlayImage = image;\n\n            requestOverlayTextureUpdate(0, 0, mWidth, mHeight, texture, true, false);\n        }\n        else\n        {\n            // Dimensions don't match. This could mean a changed map region, or a changed map resolution.\n            // In the latter case, we'll want filtering.\n            // Create a RTT Camera and draw the image onto mOverlayImage in the next frame.\n            requestOverlayTextureUpdate(destBox.mLeft, destBox.mTop, destBox.mRight-destBox.mLeft, destBox.mBottom-destBox.mTop, texture, true, true,\n                                        srcBox.mLeft/float(imageWidth), srcBox.mTop/float(imageHeight),\n                                        srcBox.mRight/float(imageWidth), srcBox.mBottom/float(imageHeight));\n        }\n    }\n\n    osg::ref_ptr<osg::Texture2D> GlobalMap::getBaseTexture()\n    {\n        ensureLoaded();\n        return mBaseTexture;\n    }\n\n    osg::ref_ptr<osg::Texture2D> GlobalMap::getOverlayTexture()\n    {\n        ensureLoaded();\n        return mOverlayTexture;\n    }\n\n    void GlobalMap::ensureLoaded()\n    {\n        if (mWorkItem)\n        {\n            mWorkItem->waitTillDone();\n\n            mOverlayImage = mWorkItem->mOverlayImage;\n            mBaseTexture = mWorkItem->mBaseTexture;\n            mAlphaTexture = mWorkItem->mAlphaTexture;\n            mOverlayTexture = mWorkItem->mOverlayTexture;\n\n            requestOverlayTextureUpdate(0, 0, mWidth, mHeight, osg::ref_ptr<osg::Texture2D>(), true, false);\n\n            mWorkItem = nullptr;\n        }\n    }\n\n    bool GlobalMap::copyResult(osg::Camera *camera, unsigned int frame)\n    {\n        ImageDestMap::iterator it = mPendingImageDest.find(camera);\n        if (it == mPendingImageDest.end())\n            return true;\n        else\n        {\n            ImageDest& imageDest = it->second;\n            if (imageDest.mFrameDone == 0) imageDest.mFrameDone = frame+2; // wait an extra frame to ensure the draw thread has completed its frame.\n            if (imageDest.mFrameDone > frame)\n            {\n                ++it;\n                return false;\n            }\n\n            mOverlayImage->copySubImage(imageDest.mX, imageDest.mY, 0, imageDest.mImage);\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_PLAYER_MAP packet with this map tile to the server, but only if:\n                1) We have recorded the exterior cell corresponding to this tile's coordinates\n                2) The tile has not previously been marked as explored in this client's mwmp::Worldstate\n                3) The tile does not belong to a Wilderness cell\n            */\n            if (originToCellX.count(imageDest.mX) > 0 && originToCellY.count(imageDest.mY) > 0)\n            {\n                int cellX = originToCellX.at(imageDest.mX);\n                int cellY = originToCellY.at(imageDest.mY);\n\n                mwmp::Worldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n\n                if (!worldstate->containsExploredMapTile(cellX, cellY))\n                {\n                    // Keep this tile marked as explored so we don't send any more packets for it\n                    worldstate->markExploredMapTile(cellX, cellY);\n\n                    if (MWBase::Environment::get().getWorld()->getExterior(cellX, cellY)->getCell()->mContextList.empty() == false)\n                    {\n                        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"New global map tile corresponds to cell %i, %i\", originToCellX.at(imageDest.mX), originToCellY.at(imageDest.mY));\n\n                        osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n                        if (!readerwriter)\n                        {\n                            std::cerr << \"Error: Can't write temporary map image, no '\" << \"png\" << \"' readerwriter found\" << std::endl;\n                            return false;\n                        }\n\n                        std::ostringstream ostream;\n\n                        osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*imageDest.mImage, ostream);\n\n                        if (!result.success())\n                        {\n                            std::cerr << \"Error: Can't write temporary map image: \" << result.message() << \" code \" << result.status() << std::endl;\n                        }\n\n                        std::string stringData = ostream.str();\n                        std::vector<char> vectorData = std::vector<char>(stringData.begin(), stringData.end());\n\n                        worldstate->sendMapExplored(cellX, cellY, vectorData);\n                    }\n                }\n            }\n            /*\n                End of tes3mp addition\n            */\n\n            mPendingImageDest.erase(it);\n            return true;\n        }\n    }\n\n    void GlobalMap::markForRemoval(osg::Camera *camera)\n    {\n        CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), camera);\n        if (found == mActiveCameras.end())\n        {\n            Log(Debug::Error) << \"Error: GlobalMap trying to remove an inactive camera\";\n            return;\n        }\n        mActiveCameras.erase(found);\n        mCamerasPendingRemoval.push_back(camera);\n    }\n\n    void GlobalMap::cleanupCameras()\n    {\n        for (auto& camera : mCamerasPendingRemoval)\n            removeCamera(camera);\n\n        mCamerasPendingRemoval.clear();\n    }\n\n    void GlobalMap::removeCamera(osg::Camera *cam)\n    {\n        cam->removeChildren(0, cam->getNumChildren());\n        mRoot->removeChild(cam);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the setting of the image data for a global map tile from elsewhere\n        in the code\n    */\n    void GlobalMap::setImage(int cellX, int cellY, const std::vector<char>& imageData)\n    {\n        Files::IMemStream istream(&imageData[0], imageData.size());\n\n        osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n        if (!reader)\n        {\n            std::cerr << \"Error: Failed to read map tile image data, no png readerwriter found\" << std::endl;\n            return;\n        }\n        osgDB::ReaderWriter::ReadResult result = reader->readImage(istream);\n\n        if (!result.success())\n        {\n            std::cerr << \"Error: Can't read map tile image: \" << result.message() << \" code \" << result.status() << std::endl;\n            return;\n        }\n        \n        osg::ref_ptr<osg::Image> image = result.getImage();\n\n        int posX = (cellX - mMinX) * mCellSize;\n        int posY = (cellY - mMinY + 1) * mCellSize;\n\n        if (cellX > mMaxX || cellX < mMinX || cellY > mMaxY || cellY < mMinY)\n            return;\n        \n        osg::ref_ptr<osg::Texture2D> texture(new osg::Texture2D);\n        texture->setImage(image);\n        texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n        texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n        texture->setResizeNonPowerOfTwoHint(false);\n\n        requestOverlayTextureUpdate(posX, mHeight - posY, mCellSize, mCellSize, texture, true, false);\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/globalmap.hpp",
    "content": "#ifndef GAME_RENDER_GLOBALMAP_H\n#define GAME_RENDER_GLOBALMAP_H\n\n#include <string>\n#include <vector>\n#include <map>\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class Texture2D;\n    class Image;\n    class Group;\n    class Camera;\n}\n\nnamespace ESM\n{\n    struct GlobalMap;\n}\n\nnamespace SceneUtil\n{\n    class WorkQueue;\n}\n\nnamespace MWRender\n{\n\n    class CreateMapWorkItem;\n\n    class GlobalMap\n    {\n    public:\n        GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue);\n        ~GlobalMap();\n\n        void render();\n\n        int getWidth() const { return mWidth; }\n        int getHeight() const { return mHeight; }\n\n        int getCellSize() const { return mCellSize; }\n\n        void worldPosToImageSpace(float x, float z, float& imageX, float& imageY);\n\n        void cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY);\n\n        void exploreCell (int cellX, int cellY, osg::ref_ptr<osg::Texture2D> localMapTexture);\n\n        /// Clears the overlay\n        void clear();\n\n        /**\n         * Removes cameras that have already been rendered. Should be called every frame to ensure that\n         * we do not render the same map more than once. Note, this cleanup is difficult to implement in an\n         * automated fashion, since we can't alter the scene graph structure from within an update callback.\n         */\n        void cleanupCameras();\n\n        void removeCamera(osg::Camera* cam);\n\n        bool copyResult(osg::Camera* cam, unsigned int frame);\n\n        /*\n            Start of tes3mp addition\n\n            Allow the setting of the image data for a global map tile from elsewhere\n            in the code\n        */\n        void setImage(int cellX, int cellY, const std::vector<char>& imageData);\n        /*\n            End of tes3mp addition\n        */\n\n        /**\n         * Mark a camera for cleanup in the next update. For internal use only.\n         */\n        void markForRemoval(osg::Camera* camera);\n\n        void write (ESM::GlobalMap& map);\n        void read (ESM::GlobalMap& map);\n\n        osg::ref_ptr<osg::Texture2D> getBaseTexture();\n        osg::ref_ptr<osg::Texture2D> getOverlayTexture();\n\n        void ensureLoaded();\n\n    private:\n        /**\n         * Request rendering a 2d quad onto mOverlayTexture.\n         * x, y, width and height are the destination coordinates (top-left coordinate origin)\n         * @param cpuCopy copy the resulting render onto mOverlayImage as well?\n         */\n        void requestOverlayTextureUpdate(int x, int y, int width, int height, osg::ref_ptr<osg::Texture2D> texture, bool clear, bool cpuCopy,\n                                         float srcLeft = 0.f, float srcTop = 0.f, float srcRight = 1.f, float srcBottom = 1.f);\n\n        int mCellSize;\n\n        osg::ref_ptr<osg::Group> mRoot;\n\n        typedef std::vector<osg::ref_ptr<osg::Camera> > CameraVector;\n        CameraVector mActiveCameras;\n\n        CameraVector mCamerasPendingRemoval;\n\n        struct ImageDest\n        {\n            ImageDest()\n                : mX(0), mY(0)\n                , mFrameDone(0)\n            {\n            }\n\n            osg::ref_ptr<osg::Image> mImage;\n            int mX, mY;\n            unsigned int mFrameDone;\n        };\n\n        typedef std::map<osg::ref_ptr<osg::Camera>, ImageDest> ImageDestMap;\n\n        ImageDestMap mPendingImageDest;\n\n        std::vector< std::pair<int,int> > mExploredCells;\n\n        osg::ref_ptr<osg::Texture2D> mBaseTexture;\n        osg::ref_ptr<osg::Texture2D> mAlphaTexture;\n\n        // GPU copy of overlay\n        // Note, uploads are pushed through a Camera, instead of through mOverlayImage\n        osg::ref_ptr<osg::Texture2D> mOverlayTexture;\n\n        // CPU copy of overlay\n        osg::ref_ptr<osg::Image> mOverlayImage;\n\n        osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;\n        osg::ref_ptr<CreateMapWorkItem> mWorkItem;\n\n        int mWidth;\n        int mHeight;\n\n        int mMinX, mMaxX, mMinY, mMaxY;\n    };\n\n}\n\n#endif\n\n"
  },
  {
    "path": "apps/openmw/mwrender/groundcover.cpp",
    "content": "#include \"groundcover.hpp\"\n\n#include <osg/AlphaFunc>\n#include <osg/Geometry>\n#include <osg/VertexAttribDivisor>\n\n#include <components/esm/esmreader.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n\n#include \"apps/openmw/mwworld/esmstore.hpp\"\n#include \"apps/openmw/mwbase/environment.hpp\"\n#include \"apps/openmw/mwbase/world.hpp\"\n\n#include \"vismask.hpp\"\n\nnamespace MWRender\n{\n    std::string getGroundcoverModel(int type, const std::string& id, const MWWorld::ESMStore& store)\n    {\n        switch (type)\n        {\n          case ESM::REC_STAT:\n            return store.get<ESM::Static>().searchStatic(id)->mModel;\n          default:\n            return std::string();\n        }\n    }\n\n    void GroundcoverUpdater::setWindSpeed(float windSpeed)\n    {\n        mWindSpeed = windSpeed;\n    }\n\n    void GroundcoverUpdater::setPlayerPos(osg::Vec3f playerPos)\n    {\n        mPlayerPos = playerPos;\n    }\n\n    void GroundcoverUpdater::setDefaults(osg::StateSet *stateset)\n    {\n        osg::ref_ptr<osg::Uniform> windUniform = new osg::Uniform(\"windSpeed\", 0.0f);\n        stateset->addUniform(windUniform.get());\n\n        osg::ref_ptr<osg::Uniform> playerPosUniform = new osg::Uniform(\"playerPos\", osg::Vec3f(0.f, 0.f, 0.f));\n        stateset->addUniform(playerPosUniform.get());\n    }\n\n    void GroundcoverUpdater::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)\n    {\n        osg::ref_ptr<osg::Uniform> windUniform = stateset->getUniform(\"windSpeed\");\n        if (windUniform != nullptr)\n            windUniform->set(mWindSpeed);\n\n        osg::ref_ptr<osg::Uniform> playerPosUniform = stateset->getUniform(\"playerPos\");\n        if (playerPosUniform != nullptr)\n            playerPosUniform->set(mPlayerPos);\n    }\n\n    class InstancingVisitor : public osg::NodeVisitor\n    {\n    public:\n        InstancingVisitor(std::vector<Groundcover::GroundcoverEntry>& instances, osg::Vec3f& chunkPosition)\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        , mInstances(instances)\n        , mChunkPosition(chunkPosition)\n        {\n        }\n\n        void apply(osg::Node& node) override\n        {\n            osg::ref_ptr<osg::StateSet> ss = node.getStateSet();\n            if (ss != nullptr)\n            {\n                ss->removeAttribute(osg::StateAttribute::MATERIAL);\n                removeAlpha(ss);\n            }\n\n            traverse(node);\n        }\n\n        void apply(osg::Geometry& geom) override\n        {\n            for (unsigned int i = 0; i < geom.getNumPrimitiveSets(); ++i)\n            {\n                geom.getPrimitiveSet(i)->setNumInstances(mInstances.size());\n            }\n\n            osg::ref_ptr<osg::Vec4Array> transforms = new osg::Vec4Array(mInstances.size());\n            osg::BoundingBox box;\n            float radius = geom.getBoundingBox().radius();\n            for (unsigned int i = 0; i < transforms->getNumElements(); i++)\n            {\n                osg::Vec3f pos(mInstances[i].mPos.asVec3());\n                osg::Vec3f relativePos = pos - mChunkPosition;\n                (*transforms)[i] = osg::Vec4f(relativePos, mInstances[i].mScale);\n\n                // Use an additional margin due to groundcover animation\n                float instanceRadius = radius * mInstances[i].mScale * 1.1f;\n                osg::BoundingSphere instanceBounds(relativePos, instanceRadius);\n                box.expandBy(instanceBounds);\n            }\n\n            geom.setInitialBound(box);\n\n            osg::ref_ptr<osg::Vec3Array> rotations = new osg::Vec3Array(mInstances.size());\n            for (unsigned int i = 0; i < rotations->getNumElements(); i++)\n            {\n                (*rotations)[i] = mInstances[i].mPos.asRotationVec3();\n            }\n\n            // Display lists do not support instancing in OSG 3.4\n            geom.setUseDisplayList(false);\n\n            geom.setVertexAttribArray(6, transforms.get(), osg::Array::BIND_PER_VERTEX);\n            geom.setVertexAttribArray(7, rotations.get(), osg::Array::BIND_PER_VERTEX);\n\n            osg::ref_ptr<osg::StateSet> ss = geom.getOrCreateStateSet();\n            ss->setAttribute(new osg::VertexAttribDivisor(6, 1));\n            ss->setAttribute(new osg::VertexAttribDivisor(7, 1));\n\n            ss->removeAttribute(osg::StateAttribute::MATERIAL);\n            removeAlpha(ss);\n\n            traverse(geom);\n        }\n    private:\n        std::vector<Groundcover::GroundcoverEntry> mInstances;\n        osg::Vec3f mChunkPosition;\n\n        void removeAlpha(osg::StateSet* stateset)\n        {\n            // MGE uses default alpha settings for groundcover, so we can not rely on alpha properties\n            stateset->removeAttribute(osg::StateAttribute::ALPHAFUNC);\n            stateset->removeMode(GL_ALPHA_TEST);\n            stateset->removeAttribute(osg::StateAttribute::BLENDFUNC);\n            stateset->removeMode(GL_BLEND);\n            stateset->setRenderBinToInherit();\n        }\n    };\n\n    class DensityCalculator\n    {\n    public:\n        DensityCalculator(float density)\n            : mDensity(density)\n        {\n        }\n\n        bool isInstanceEnabled()\n        {\n            if (mDensity >= 1.f) return true;\n\n            mCurrentGroundcover += mDensity;\n            if (mCurrentGroundcover < 1.f) return false;\n\n            mCurrentGroundcover -= 1.f;\n\n            return true;\n        }\n        void reset() { mCurrentGroundcover = 0.f; }\n\n    private:\n        float mCurrentGroundcover = 0.f;\n        float mDensity = 0.f;\n    };\n\n    inline bool isInChunkBorders(ESM::CellRef& ref, osg::Vec2f& minBound, osg::Vec2f& maxBound)\n    {\n        osg::Vec2f size = maxBound - minBound;\n        if (size.x() >=1 && size.y() >=1) return true;\n\n        osg::Vec3f pos = ref.mPos.asVec3();\n        osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE;\n        if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())\n            || (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y()))\n            return false;\n\n        return true;\n    }\n\n    osg::ref_ptr<osg::Node> Groundcover::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)\n    {\n        ChunkId id = std::make_tuple(center, size, activeGrid);\n\n        osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);\n        if (obj)\n            return obj->asNode();\n        else\n        {\n            InstanceMap instances;\n            collectInstances(instances, size, center);\n            osg::ref_ptr<osg::Node> node = createChunk(instances, center);\n            mCache->addEntryToObjectCache(id, node.get());\n            return node;\n        }\n    }\n\n    Groundcover::Groundcover(Resource::SceneManager* sceneManager, float density)\n         : GenericResourceManager<ChunkId>(nullptr)\n         , mSceneManager(sceneManager)\n         , mDensity(density)\n    {\n    }\n\n    void Groundcover::collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center)\n    {\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n        osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f));\n        osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f));\n        DensityCalculator calculator(mDensity);\n        std::vector<ESM::ESMReader> esm;\n        osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f));\n        for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX)\n        {\n            for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY)\n            {\n                const ESM::Cell* cell = store.get<ESM::Cell>().searchStatic(cellX, cellY);\n                if (!cell) continue;\n\n                calculator.reset();\n                for (size_t i=0; i<cell->mContextList.size(); ++i)\n                {\n                    unsigned int index = cell->mContextList.at(i).index;\n                    if (esm.size() <= index)\n                        esm.resize(index+1);\n                    cell->restore(esm[index], i);\n                    ESM::CellRef ref;\n                    ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile;\n                    bool deleted = false;\n                    while(cell->getNextRef(esm[index], ref, deleted))\n                    {\n                        if (deleted) continue;\n                        if (!ref.mRefNum.fromGroundcoverFile()) continue;\n\n                        if (!calculator.isInstanceEnabled()) continue;\n                        if (!isInChunkBorders(ref, minBound, maxBound)) continue;\n\n                        Misc::StringUtils::lowerCaseInPlace(ref.mRefID);\n                        int type = store.findStatic(ref.mRefID);\n                        std::string model = getGroundcoverModel(type, ref.mRefID, store);\n                        if (model.empty()) continue;\n                        model = \"meshes/\" + model;\n\n                        instances[model].emplace_back(std::move(ref), std::move(model));\n                    }\n                }\n            }\n        }\n    }\n\n    osg::ref_ptr<osg::Node> Groundcover::createChunk(InstanceMap& instances, const osg::Vec2f& center)\n    {\n        osg::ref_ptr<osg::Group> group = new osg::Group;\n        osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE;\n        for (auto& pair : instances)\n        {\n            const osg::Node* temp = mSceneManager->getTemplate(pair.first);\n            osg::ref_ptr<osg::Node> node = static_cast<osg::Node*>(temp->clone(osg::CopyOp::DEEP_COPY_ALL&(~osg::CopyOp::DEEP_COPY_TEXTURES)));\n\n            // Keep link to original mesh to keep it in cache\n            group->getOrCreateUserDataContainer()->addUserObject(new Resource::TemplateRef(temp));\n\n            mSceneManager->reinstateRemovedState(node);\n\n            InstancingVisitor visitor(pair.second, worldCenter);\n            node->accept(visitor);\n            group->addChild(node);\n        }\n\n        // Force a unified alpha handling instead of data from meshes\n        osg::ref_ptr<osg::AlphaFunc> alpha = new osg::AlphaFunc(osg::AlphaFunc::GEQUAL, 128.f / 255.f);\n        group->getOrCreateStateSet()->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON);\n        group->getBound();\n        group->setNodeMask(Mask_Groundcover);\n        if (mSceneManager->getLightingMethod() != SceneUtil::LightingMethod::FFP)\n            group->setCullCallback(new SceneUtil::LightListCallback);\n        mSceneManager->recreateShaders(group, \"groundcover\", false, true);\n\n        return group;\n    }\n\n    unsigned int Groundcover::getNodeMask()\n    {\n        return Mask_Groundcover;\n    }\n\n    void Groundcover::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n    {\n        stats->setAttribute(frameNumber, \"Groundcover Chunk\", mCache->getCacheSize());\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/groundcover.hpp",
    "content": "#ifndef OPENMW_MWRENDER_GROUNDCOVER_H\n#define OPENMW_MWRENDER_GROUNDCOVER_H\n\n#include <components/terrain/quadtreeworld.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/sceneutil/statesetupdater.hpp>\n#include <components/esm/loadcell.hpp>\n\nnamespace MWRender\n{\n    class GroundcoverUpdater : public SceneUtil::StateSetUpdater\n    {\n    public:\n        GroundcoverUpdater()\n            : mWindSpeed(0.f)\n            , mPlayerPos(osg::Vec3f())\n        {\n        }\n\n        void setWindSpeed(float windSpeed);\n        void setPlayerPos(osg::Vec3f playerPos);\n\n    protected:\n        void setDefaults(osg::StateSet *stateset) override;\n        void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) override;\n\n    private:\n        float mWindSpeed;\n        osg::Vec3f mPlayerPos;\n    };\n\n    typedef std::tuple<osg::Vec2f, float, bool> ChunkId; // Center, Size, ActiveGrid\n    class Groundcover : public Resource::GenericResourceManager<ChunkId>, public Terrain::QuadTreeWorld::ChunkManager\n    {\n    public:\n        Groundcover(Resource::SceneManager* sceneManager, float density);\n        ~Groundcover() = default;\n\n        osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;\n\n        unsigned int getNodeMask() override;\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;\n\n        struct GroundcoverEntry\n        {\n            ESM::Position mPos;\n            float mScale;\n            std::string mModel;\n\n            GroundcoverEntry(const ESM::CellRef& ref, const std::string& model)\n            {\n                mPos = ref.mPos;\n                mScale = ref.mScale;\n                mModel = model;\n            }\n        };\n\n    private:\n        Resource::SceneManager* mSceneManager;\n        float mDensity;\n\n        typedef std::map<std::string, std::vector<GroundcoverEntry>> InstanceMap;\n        osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center);\n        void collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/landmanager.cpp",
    "content": "#include \"landmanager.hpp\"\n\n#include <osg/Stats>\n\n#include <components/resource/objectcache.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace MWRender\n{\n\nLandManager::LandManager(int loadFlags)\n    : GenericResourceManager<std::pair<int, int> >(nullptr)\n    , mLoadFlags(loadFlags)\n{\n    mCache = new CacheType;\n}\n\nosg::ref_ptr<ESMTerrain::LandObject> LandManager::getLand(int x, int y)\n{\n    osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(std::make_pair(x,y));\n    if (obj)\n        return static_cast<ESMTerrain::LandObject*>(obj.get());\n    else\n    {\n        const ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get<ESM::Land>().search(x,y);\n        if (!land)\n            return nullptr;\n        osg::ref_ptr<ESMTerrain::LandObject> landObj (new ESMTerrain::LandObject(land, mLoadFlags));\n        mCache->addEntryToObjectCache(std::make_pair(x,y), landObj.get());\n        return landObj;\n    }\n}\n\nvoid LandManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n{\n    stats->setAttribute(frameNumber, \"Land\", mCache->getCacheSize());\n}\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/landmanager.hpp",
    "content": "#ifndef OPENMW_MWRENDER_LANDMANAGER_H\n#define OPENMW_MWRENDER_LANDMANAGER_H\n\n#include <osg/Object>\n\n#include <components/resource/resourcemanager.hpp>\n#include <components/esmterrain/storage.hpp>\n\nnamespace ESM\n{\n    struct Land;\n}\n\nnamespace MWRender\n{\n\n    class LandManager : public Resource::GenericResourceManager<std::pair<int, int> >\n    {\n    public:\n        LandManager(int loadFlags);\n\n        /// @note Will return nullptr if not found.\n        osg::ref_ptr<ESMTerrain::LandObject> getLand(int x, int y);\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;\n\n    private:\n        int mLoadFlags;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/localmap.cpp",
    "content": "#include \"localmap.hpp\"\n\n#include <stdint.h>\n\n#include <osg/Fog>\n#include <osg/LightModel>\n#include <osg/Texture2D>\n#include <osg/ComputeBoundsVisitor>\n#include <osg/LightSource>\n#include <osg/PolygonMode>\n\n#include <osgDB/ReadFile>\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/fogstate.hpp>\n#include <components/esm/loadcell.hpp>\n#include <components/misc/constants.hpp>\n#include <components/settings/settings.hpp>\n#include <components/sceneutil/visitor.hpp>\n#include <components/sceneutil/shadow.hpp>\n#include <components/sceneutil/util.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n#include <components/files/memorystream.hpp>\n#include <components/resource/scenemanager.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"vismask.hpp\"\n\nnamespace\n{\n\n    class CameraLocalUpdateCallback : public osg::NodeCallback\n    {\n    public:\n        CameraLocalUpdateCallback(MWRender::LocalMap* parent)\n            : mRendered(false)\n            , mParent(parent)\n        {\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor*) override\n        {\n            if (mRendered)\n                node->setNodeMask(0);\n\n            if (!mRendered)\n            {\n                mRendered = true;\n                mParent->markForRemoval(static_cast<osg::Camera*>(node));\n            }\n\n            // Note, we intentionally do not traverse children here. The map camera's scene data is the same as the master camera's,\n            // so it has been updated already.\n            //traverse(node, nv);\n        }\n\n    private:\n        bool mRendered;\n        MWRender::LocalMap* mParent;\n    };\n\n    float square(float val)\n    {\n        return val*val;\n    }\n\n    std::pair<int, int> divideIntoSegments(const osg::BoundingBox& bounds, float mapSize)\n    {\n        osg::Vec2f min(bounds.xMin(), bounds.yMin());\n        osg::Vec2f max(bounds.xMax(), bounds.yMax());\n        osg::Vec2f length = max - min;\n        const int segsX = static_cast<int>(std::ceil(length.x() / mapSize));\n        const int segsY = static_cast<int>(std::ceil(length.y() / mapSize));\n        return {segsX, segsY};\n    }\n}\n\nnamespace MWRender\n{\n\nLocalMap::LocalMap(osg::Group* root)\n    : mRoot(root)\n    , mMapResolution(Settings::Manager::getInt(\"local map resolution\", \"Map\"))\n    , mMapWorldSize(Constants::CellSizeInUnits)\n    , mCellDistance(Constants::CellGridRadius)\n    , mAngle(0.f)\n    , mInterior(false)\n{\n    // Increase map resolution, if use UI scaling\n    float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();\n    mMapResolution *= uiScale;\n\n    SceneUtil::FindByNameVisitor find(\"Scene Root\");\n    mRoot->accept(find);\n    mSceneRoot = find.mFoundNode;\n    if (!mSceneRoot)\n        throw std::runtime_error(\"no scene root found\");\n}\n\nLocalMap::~LocalMap()\n{\n    for (auto& camera : mActiveCameras)\n        removeCamera(camera);\n    for (auto& camera : mCamerasPendingRemoval)\n        removeCamera(camera);\n}\n\nconst osg::Vec2f LocalMap::rotatePoint(const osg::Vec2f& point, const osg::Vec2f& center, const float angle)\n{\n    return osg::Vec2f( std::cos(angle) * (point.x() - center.x()) - std::sin(angle) * (point.y() - center.y()) + center.x(),\n                    std::sin(angle) * (point.x() - center.x()) + std::cos(angle) * (point.y() - center.y()) + center.y());\n}\n\nvoid LocalMap::clear()\n{\n    mSegments.clear();\n}\n\nvoid LocalMap::saveFogOfWar(MWWorld::CellStore* cell)\n{\n    if (!mInterior)\n    {\n        const MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];\n\n        if (segment.mFogOfWarImage && segment.mHasFogState)\n        {\n            std::unique_ptr<ESM::FogState> fog (new ESM::FogState());\n            fog->mFogTextures.emplace_back();\n\n            segment.saveFogOfWar(fog->mFogTextures.back());\n\n            cell->setFog(fog.release());\n        }\n    }\n    else\n    {\n        auto segments = divideIntoSegments(mBounds, mMapWorldSize);\n\n        std::unique_ptr<ESM::FogState> fog (new ESM::FogState());\n\n        fog->mBounds.mMinX = mBounds.xMin();\n        fog->mBounds.mMaxX = mBounds.xMax();\n        fog->mBounds.mMinY = mBounds.yMin();\n        fog->mBounds.mMaxY = mBounds.yMax();\n        fog->mNorthMarkerAngle = mAngle;\n\n        fog->mFogTextures.reserve(segments.first * segments.second);\n\n        for (int x = 0; x < segments.first; ++x)\n        {\n            for (int y = 0; y < segments.second; ++y)\n            {\n                const MapSegment& segment = mSegments[std::make_pair(x,y)];\n\n                fog->mFogTextures.emplace_back();\n\n                // saving even if !segment.mHasFogState so we don't mess up the segmenting\n                // plus, older openmw versions can't deal with empty images\n                segment.saveFogOfWar(fog->mFogTextures.back());\n\n                fog->mFogTextures.back().mX = x;\n                fog->mFogTextures.back().mY = y;\n            }\n        }\n\n        cell->setFog(fog.release());\n    }\n}\n\nosg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax)\n{\n    osg::ref_ptr<osg::Camera> camera (new osg::Camera);\n    camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10);\n    camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);\n    camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector);\n    camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);\n    camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);\n    camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f));\n    camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    camera->setRenderOrder(osg::Camera::PRE_RENDER);\n\n    camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static);\n    camera->setNodeMask(Mask_RenderToTexture);\n\n    // Disable small feature culling, it's not going to be reliable for this camera\n    osg::Camera::CullingMode cullingMode = (osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING) & ~(osg::CullStack::SMALL_FEATURE_CULLING);\n    camera->setCullingMode(cullingMode);\n\n    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;\n    stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE);\n\n    // assign large value to effectively turn off fog\n    // shaders don't respect glDisable(GL_FOG)\n    osg::ref_ptr<osg::Fog> fog (new osg::Fog);\n    fog->setStart(10000000);\n    fog->setEnd(10000000);\n    stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);\n\n    osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;\n    lightmodel->setAmbientIntensity(osg::Vec4(0.3f, 0.3f, 0.3f, 1.f));\n    stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n\n    osg::ref_ptr<osg::Light> light = new osg::Light;\n    light->setPosition(osg::Vec4(-0.3f, -0.3f, 0.7f, 0.f));\n    light->setDiffuse(osg::Vec4(0.7f, 0.7f, 0.7f, 1.f));\n    light->setAmbient(osg::Vec4(0,0,0,1));\n    light->setSpecular(osg::Vec4(0,0,0,0));\n    light->setLightNum(0);\n    light->setConstantAttenuation(1.f);\n    light->setLinearAttenuation(0.f);\n    light->setQuadraticAttenuation(0.f);\n\n    osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource;\n    lightSource->setLight(light);\n\n    lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n\n    SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);\n\n    // override sun for local map \n    SceneUtil::configureStateSetSunOverride(static_cast<SceneUtil::LightManager*>(mSceneRoot.get()), light, stateset);\n\n    camera->addChild(lightSource);\n    camera->setStateSet(stateset);\n    camera->setViewport(0, 0, mMapResolution, mMapResolution);\n    camera->setUpdateCallback(new CameraLocalUpdateCallback(this));\n\n    return camera;\n}\n\nvoid LocalMap::setupRenderToTexture(osg::ref_ptr<osg::Camera> camera, int x, int y)\n{\n    osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);\n    texture->setTextureSize(mMapResolution, mMapResolution);\n    texture->setInternalFormat(GL_RGB);\n    texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n    texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n    texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n    texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n    SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, texture);\n\n    camera->addChild(mSceneRoot);\n    mRoot->addChild(camera);\n    mActiveCameras.push_back(camera);\n\n    MapSegment& segment = mSegments[std::make_pair(x, y)];\n    segment.mMapTexture = texture;\n}\n\nbool needUpdate(std::set<std::pair<int, int> >& renderedGrid, std::set<std::pair<int, int> >& currentGrid, int cellX, int cellY)\n{\n    // if all the cells of the current grid are contained in the rendered grid then we can keep the old render\n    for (int dx=-1;dx<2;dx+=1)\n    {\n        for (int dy=-1;dy<2;dy+=1)\n        {\n            bool haveInRenderedGrid = renderedGrid.find(std::make_pair(cellX+dx,cellY+dy)) != renderedGrid.end();\n            bool haveInCurrentGrid = currentGrid.find(std::make_pair(cellX+dx,cellY+dy)) != currentGrid.end();\n            if (haveInCurrentGrid && !haveInRenderedGrid)\n                return true;\n        }\n    }\n    return false;\n}\n\nvoid LocalMap::requestMap(const MWWorld::CellStore* cell)\n{\n    if (cell->isExterior())\n    {\n        int cellX = cell->getCell()->getGridX();\n        int cellY = cell->getCell()->getGridY();\n\n        MapSegment& segment = mSegments[std::make_pair(cellX, cellY)];\n        if (!needUpdate(segment.mGrid, mCurrentGrid, cellX, cellY))\n            return;\n        else\n        {\n            segment.mGrid = mCurrentGrid;\n            requestExteriorMap(cell);\n        }\n    }\n    else\n        requestInteriorMap(cell);\n}\n\nvoid LocalMap::addCell(MWWorld::CellStore *cell)\n{\n    if (cell->isExterior())\n        mCurrentGrid.emplace(cell->getCell()->getGridX(), cell->getCell()->getGridY());\n}\n\nvoid LocalMap::removeCell(MWWorld::CellStore *cell)\n{\n    saveFogOfWar(cell);\n\n    if (cell->isExterior())\n    {\n        std::pair<int, int> coords = std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY());\n        mSegments.erase(coords);\n        mCurrentGrid.erase(coords);\n    }\n    else\n        mSegments.clear();\n}\n\nosg::ref_ptr<osg::Texture2D> LocalMap::getMapTexture(int x, int y)\n{\n    SegmentMap::iterator found = mSegments.find(std::make_pair(x, y));\n    if (found == mSegments.end())\n        return osg::ref_ptr<osg::Texture2D>();\n    else\n        return found->second.mMapTexture;\n}\n\nosg::ref_ptr<osg::Texture2D> LocalMap::getFogOfWarTexture(int x, int y)\n{\n    SegmentMap::iterator found = mSegments.find(std::make_pair(x, y));\n    if (found == mSegments.end())\n        return osg::ref_ptr<osg::Texture2D>();\n    else\n        return found->second.mFogOfWarTexture;\n}\n\nvoid LocalMap::removeCamera(osg::Camera *cam)\n{\n    cam->removeChildren(0, cam->getNumChildren());\n    mRoot->removeChild(cam);\n}\n\nvoid LocalMap::markForRemoval(osg::Camera *cam)\n{\n    CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), cam);\n    if (found == mActiveCameras.end())\n    {\n        Log(Debug::Error) << \"Error: trying to remove an inactive camera\";\n        return;\n    }\n    mActiveCameras.erase(found);\n    mCamerasPendingRemoval.push_back(cam);\n}\n\nvoid LocalMap::cleanupCameras()\n{\n    if (mCamerasPendingRemoval.empty())\n        return;\n\n    for (auto& camera : mCamerasPendingRemoval)\n        removeCamera(camera);\n\n    mCamerasPendingRemoval.clear();\n}\n\nvoid LocalMap::requestExteriorMap(const MWWorld::CellStore* cell)\n{\n    mInterior = false;\n\n    int x = cell->getCell()->getGridX();\n    int y = cell->getCell()->getGridY();\n\n    osg::BoundingSphere bound = mSceneRoot->getBound();\n    float zmin = bound.center().z() - bound.radius();\n    float zmax = bound.center().z() + bound.radius();\n\n    osg::ref_ptr<osg::Camera> camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize,\n                                                                osg::Vec3d(0,1,0), zmin, zmax);\n    setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY());\n\n    MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];\n    if (!segment.mFogOfWarImage)\n    {\n        if (cell->getFog())\n            segment.loadFogOfWar(cell->getFog()->mFogTextures.back());\n        else\n            segment.initFogOfWar();\n    }\n}\n\nvoid LocalMap::requestInteriorMap(const MWWorld::CellStore* cell)\n{\n    osg::ComputeBoundsVisitor computeBoundsVisitor;\n    computeBoundsVisitor.setTraversalMask(Mask_Scene | Mask_Terrain | Mask_Object | Mask_Static);\n    mSceneRoot->accept(computeBoundsVisitor);\n\n    osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox();\n\n    // If we're in an empty cell, bail out\n    // The operations in this function are only valid for finite bounds\n    if (!bounds.valid() || bounds.radius2() == 0.0)\n        return;\n\n    mInterior = true;\n\n    mBounds = bounds;\n\n    // Get the cell's NorthMarker rotation. This is used to rotate the entire map.\n    osg::Vec2f north = MWBase::Environment::get().getWorld()->getNorthVector(cell);\n\n    mAngle = std::atan2(north.x(), north.y());\n\n    // Rotate the cell and merge the rotated corners to the bounding box\n    osg::Vec2f origCenter(bounds.center().x(), bounds.center().y());\n    osg::Vec3f origCorners[8];\n    for (int i=0; i<8; ++i)\n        origCorners[i] = mBounds.corner(i);\n\n    for (int i=0; i<8; ++i)\n    {\n        osg::Vec3f corner = origCorners[i];\n        osg::Vec2f corner2d (corner.x(), corner.y());\n        corner2d = rotatePoint(corner2d, origCenter, mAngle);\n        mBounds.expandBy(osg::Vec3f(corner2d.x(), corner2d.y(), 0));\n    }\n\n    // Do NOT change padding! This will break older savegames.\n    // If the padding really needs to be changed, then it must be saved in the ESM::FogState and\n    // assume the old (500) value as default for older savegames.\n    const float padding = 500.0f;\n\n    // Apply a little padding\n    mBounds.set(mBounds._min - osg::Vec3f(padding,padding,0.f),\n                mBounds._max + osg::Vec3f(padding,padding,0.f));\n\n    float zMin = mBounds.zMin();\n    float zMax = mBounds.zMax();\n\n    // If there is fog state in the CellStore (e.g. when it came from a savegame) we need to do some checks\n    // to see if this state is still valid.\n    // Both the cell bounds and the NorthMarker rotation could be changed by the content files or exchanged models.\n    // If they changed by too much then parts of the interior might not be covered by the map anymore.\n    // The following code detects this, and discards the CellStore's fog state if it needs to.\n    std::vector<std::pair<int, int>> segmentMappings;\n    if (cell->getFog())\n    {\n        ESM::FogState* fog = cell->getFog();\n\n        if (std::abs(mAngle - fog->mNorthMarkerAngle) < osg::DegreesToRadians(5.f))\n        {\n            // Expand mBounds so the saved textures fit the same grid\n            int xOffset = 0;\n            int yOffset = 0;\n            if(fog->mBounds.mMinX < mBounds.xMin())\n            {\n                mBounds.xMin() = fog->mBounds.mMinX;\n            }\n            else if(fog->mBounds.mMinX > mBounds.xMin())\n            {\n                float diff = fog->mBounds.mMinX - mBounds.xMin();\n                xOffset += diff / mMapWorldSize;\n                xOffset++;\n                mBounds.xMin() = fog->mBounds.mMinX - xOffset * mMapWorldSize;\n            }\n            if(fog->mBounds.mMinY < mBounds.yMin())\n            {\n                mBounds.yMin() = fog->mBounds.mMinY;\n            }\n            else if(fog->mBounds.mMinY > mBounds.yMin())\n            {\n                float diff = fog->mBounds.mMinY - mBounds.yMin();\n                yOffset += diff / mMapWorldSize;\n                yOffset++;\n                mBounds.yMin() = fog->mBounds.mMinY - yOffset * mMapWorldSize;\n            }\n            mBounds.xMax() = std::max(mBounds.xMax(), fog->mBounds.mMaxX);\n            mBounds.yMax() = std::max(mBounds.yMax(), fog->mBounds.mMaxY);\n\n            if(xOffset != 0 || yOffset != 0)\n                Log(Debug::Warning) << \"Warning: expanding fog by \" << xOffset << \", \" << yOffset;\n\n            const auto& textures = fog->mFogTextures;\n            segmentMappings.reserve(textures.size());\n            osg::BoundingBox savedBounds{\n                fog->mBounds.mMinX, fog->mBounds.mMinY, 0,\n                fog->mBounds.mMaxX, fog->mBounds.mMaxY, 0\n            };\n            auto segments = divideIntoSegments(savedBounds, mMapWorldSize);\n            for (int x = 0; x < segments.first; ++x)\n                for (int y = 0; y < segments.second; ++y)\n                    segmentMappings.emplace_back(std::make_pair(x + xOffset, y + yOffset));\n\n            mAngle = fog->mNorthMarkerAngle;\n        }\n    }\n\n    osg::Vec2f min(mBounds.xMin(), mBounds.yMin());\n\n    osg::Vec2f center(mBounds.center().x(), mBounds.center().y());\n    osg::Quat cameraOrient (mAngle, osg::Vec3d(0,0,-1));\n\n    auto segments = divideIntoSegments(mBounds, mMapWorldSize);\n    for (int x = 0; x < segments.first; ++x)\n    {\n        for (int y = 0; y < segments.second; ++y)\n        {\n            osg::Vec2f start = min + osg::Vec2f(mMapWorldSize*x, mMapWorldSize*y);\n            osg::Vec2f newcenter = start + osg::Vec2f(mMapWorldSize/2.f, mMapWorldSize/2.f);\n\n            osg::Vec2f a = newcenter - center;\n            osg::Vec3f rotatedCenter = cameraOrient * (osg::Vec3f(a.x(), a.y(), 0));\n\n            osg::Vec2f pos = osg::Vec2f(rotatedCenter.x(), rotatedCenter.y()) + center;\n\n            osg::ref_ptr<osg::Camera> camera = createOrthographicCamera(pos.x(), pos.y(),\n                                                                        mMapWorldSize, mMapWorldSize,\n                                                                        osg::Vec3f(north.x(), north.y(), 0.f), zMin, zMax);\n\n            setupRenderToTexture(camera, x, y);\n\n            auto coords = std::make_pair(x,y);\n            MapSegment& segment = mSegments[coords];\n            if (!segment.mFogOfWarImage)\n            {\n                bool loaded = false;\n                for(size_t index{}; index < segmentMappings.size(); index++)\n                {\n                    if(segmentMappings[index] == coords)\n                    {\n                        ESM::FogState* fog = cell->getFog();\n                        segment.loadFogOfWar(fog->mFogTextures[index]);\n                        loaded = true;\n                        break;\n                    }\n                }\n                if(!loaded)\n                    segment.initFogOfWar();\n            }\n        }\n    }\n}\n\nvoid LocalMap::worldToInteriorMapPosition (osg::Vec2f pos, float& nX, float& nY, int& x, int& y)\n{\n    pos = rotatePoint(pos, osg::Vec2f(mBounds.center().x(), mBounds.center().y()), mAngle);\n\n    osg::Vec2f min(mBounds.xMin(), mBounds.yMin());\n\n    x = static_cast<int>(std::ceil((pos.x() - min.x()) / mMapWorldSize) - 1);\n    y = static_cast<int>(std::ceil((pos.y() - min.y()) / mMapWorldSize) - 1);\n\n    nX = (pos.x() - min.x() - mMapWorldSize*x)/mMapWorldSize;\n    nY = 1.0f-(pos.y() - min.y() - mMapWorldSize*y)/mMapWorldSize;\n}\n\nosg::Vec2f LocalMap::interiorMapToWorldPosition (float nX, float nY, int x, int y)\n{\n    osg::Vec2f min(mBounds.xMin(), mBounds.yMin());\n    osg::Vec2f pos (mMapWorldSize * (nX + x) + min.x(),\n                    mMapWorldSize * (1.0f-nY + y) + min.y());\n\n    pos = rotatePoint(pos, osg::Vec2f(mBounds.center().x(), mBounds.center().y()), -mAngle);\n    return pos;\n}\n\nbool LocalMap::isPositionExplored (float nX, float nY, int x, int y)\n{\n    const MapSegment& segment = mSegments[std::make_pair(x, y)];\n    if (!segment.mFogOfWarImage)\n        return false;\n\n    nX = std::max(0.f, std::min(1.f, nX));\n    nY = std::max(0.f, std::min(1.f, nY));\n\n    int texU = static_cast<int>((sFogOfWarResolution - 1) * nX);\n    int texV = static_cast<int>((sFogOfWarResolution - 1) * nY);\n\n    uint32_t clr = ((const uint32_t*)segment.mFogOfWarImage->data())[texV * sFogOfWarResolution + texU];\n    uint8_t alpha = (clr >> 24);\n    return alpha < 200;\n}\n\nosg::Group* LocalMap::getRoot()\n{\n    return mRoot;\n}\n\nvoid LocalMap::updatePlayer (const osg::Vec3f& position, const osg::Quat& orientation,\n                             float& u, float& v, int& x, int& y, osg::Vec3f& direction)\n{\n    // retrieve the x,y grid coordinates the player is in\n    osg::Vec2f pos(position.x(), position.y());\n\n    if (mInterior)\n    {\n        worldToInteriorMapPosition(pos, u,v, x,y);\n\n        osg::Quat cameraOrient (mAngle, osg::Vec3(0,0,-1));\n        direction = orientation * cameraOrient.inverse() * osg::Vec3f(0,1,0);\n    }\n    else\n    {\n        direction = orientation * osg::Vec3f(0,1,0);\n\n        x = static_cast<int>(std::ceil(pos.x() / mMapWorldSize) - 1);\n        y = static_cast<int>(std::ceil(pos.y() / mMapWorldSize) - 1);\n\n        // convert from world coordinates to texture UV coordinates\n        u = std::abs((pos.x() - (mMapWorldSize*x))/mMapWorldSize);\n        v = 1.0f-std::abs((pos.y() - (mMapWorldSize*y))/mMapWorldSize);\n    }\n\n    // explore radius (squared)\n    const float exploreRadius = 0.17f * (sFogOfWarResolution-1); // explore radius from 0 to sFogOfWarResolution-1\n    const float sqrExploreRadius = square(exploreRadius);\n    const float exploreRadiusUV = exploreRadius / sFogOfWarResolution; // explore radius from 0 to 1 (UV space)\n\n    // change the affected fog of war textures (in a 3x3 grid around the player)\n    for (int mx = -mCellDistance; mx<=mCellDistance; ++mx)\n    {\n        for (int my = -mCellDistance; my<=mCellDistance; ++my)\n        {\n            // is this texture affected at all?\n            bool affected = false;\n            if (mx == 0 && my == 0) // the player is always in the center of the 3x3 grid\n                affected = true;\n            else\n            {\n                bool affectsX = (mx > 0)? (u + exploreRadiusUV > 1) : (u - exploreRadiusUV < 0);\n                bool affectsY = (my > 0)? (v + exploreRadiusUV > 1) : (v - exploreRadiusUV < 0);\n                affected = (affectsX && (my == 0)) || (affectsY && mx == 0) || (affectsX && affectsY);\n            }\n\n            if (!affected)\n                continue;\n\n            int texX = x + mx;\n            int texY = y + my*-1;\n\n            MapSegment& segment = mSegments[std::make_pair(texX, texY)];\n\n            if (!segment.mFogOfWarImage || !segment.mMapTexture)\n                continue;\n\n            uint32_t* data = (uint32_t*)segment.mFogOfWarImage->data();\n            bool changed = false;\n            for (int texV = 0; texV<sFogOfWarResolution; ++texV)\n            {\n                for (int texU = 0; texU<sFogOfWarResolution; ++texU)\n                {\n                    float sqrDist = square((texU + mx*(sFogOfWarResolution-1)) - u*(sFogOfWarResolution-1))\n                            + square((texV + my*(sFogOfWarResolution-1)) - v*(sFogOfWarResolution-1));\n\n                    uint32_t clr = *(uint32_t*)data;\n                    uint8_t alpha = (clr >> 24);\n\n                    alpha = std::min( alpha, (uint8_t) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) );\n                    uint32_t val = (uint32_t) (alpha << 24);\n                    if ( *data != val)\n                    {\n                        *data = val;\n                        changed = true;\n                    }\n\n                    ++data;\n                }\n            }\n\n            if (changed)\n            {\n                segment.mHasFogState = true;\n                segment.mFogOfWarImage->dirty();\n            }\n        }\n    }\n}\n\nLocalMap::MapSegment::MapSegment()\n    : mHasFogState(false)\n{\n}\n\nLocalMap::MapSegment::~MapSegment()\n{\n\n}\n\nvoid LocalMap::MapSegment::createFogOfWarTexture()\n{\n    if (mFogOfWarTexture)\n        return;\n    mFogOfWarTexture = new osg::Texture2D;\n    // TODO: synchronize access? for now, the worst that could happen is the draw thread jumping a frame ahead.\n    //mFogOfWarTexture->setDataVariance(osg::Object::DYNAMIC);\n    mFogOfWarTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n    mFogOfWarTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n    mFogOfWarTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n    mFogOfWarTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n    mFogOfWarTexture->setUnRefImageDataAfterApply(false);\n}\n\nvoid LocalMap::MapSegment::initFogOfWar()\n{\n    mFogOfWarImage = new osg::Image;\n    // Assign a PixelBufferObject for asynchronous transfer of data to the GPU\n    mFogOfWarImage->setPixelBufferObject(new osg::PixelBufferObject);\n    mFogOfWarImage->allocateImage(sFogOfWarResolution, sFogOfWarResolution, 1, GL_RGBA, GL_UNSIGNED_BYTE);\n    assert(mFogOfWarImage->isDataContiguous());\n    std::vector<uint32_t> data;\n    data.resize(sFogOfWarResolution*sFogOfWarResolution, 0xff000000);\n\n    memcpy(mFogOfWarImage->data(), &data[0], data.size()*4);\n\n    createFogOfWarTexture();\n    mFogOfWarTexture->setImage(mFogOfWarImage);\n}\n\nvoid LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)\n{\n    const std::vector<char>& data = esm.mImageData;\n    if (data.empty())\n    {\n        initFogOfWar();\n        return;\n    }\n\n    osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n    if (!readerwriter)\n    {\n        Log(Debug::Error) << \"Error: Unable to load fog, can't find a png ReaderWriter\" ;\n        return;\n    }\n\n    Files::IMemStream in(&data[0], data.size());\n\n    osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(in);\n    if (!result.success())\n    {\n        Log(Debug::Error) << \"Error: Failed to read fog: \" << result.message() << \" code \" << result.status();\n        return;\n    }\n\n    mFogOfWarImage = result.getImage();\n    mFogOfWarImage->flipVertical();\n    mFogOfWarImage->dirty();\n\n    createFogOfWarTexture();\n    mFogOfWarTexture->setImage(mFogOfWarImage);\n    mHasFogState = true;\n}\n\nvoid LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const\n{\n    if (!mFogOfWarImage)\n        return;\n\n    std::ostringstream ostream;\n\n    osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n    if (!readerwriter)\n    {\n        Log(Debug::Error) << \"Error: Unable to write fog, can't find a png ReaderWriter\";\n        return;\n    }\n\n    // extra flips are unfortunate, but required for compatibility with older versions\n    mFogOfWarImage->flipVertical();\n    osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mFogOfWarImage, ostream);\n    if (!result.success())\n    {\n        Log(Debug::Error) << \"Error: Unable to write fog: \" << result.message() << \" code \" << result.status();\n        return;\n    }\n    mFogOfWarImage->flipVertical();\n\n    std::string data = ostream.str();\n    fog.mImageData = std::vector<char>(data.begin(), data.end());\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/localmap.hpp",
    "content": "#ifndef GAME_RENDER_LOCALMAP_H\n#define GAME_RENDER_LOCALMAP_H\n\n#include <set>\n#include <vector>\n#include <map>\n\n#include <osg/BoundingBox>\n#include <osg/Quat>\n#include <osg/ref_ptr>\n\nnamespace MWWorld\n{\n    class CellStore;\n}\n\nnamespace ESM\n{\n    struct FogTexture;\n}\n\nnamespace osg\n{\n    class Texture2D;\n    class Image;\n    class Camera;\n    class Group;\n    class Node;\n}\n\nnamespace MWRender\n{\n    ///\n    /// \\brief Local map rendering\n    ///\n    class LocalMap\n    {\n    public:\n        LocalMap(osg::Group* root);\n        ~LocalMap();\n\n        /**\n         * Clear all savegame-specific data (i.e. fog of war textures)\n         */\n        void clear();\n\n        /**\n         * Request a map render for the given cell. Render textures will be immediately created and can be retrieved with the getMapTexture function.\n         */\n        void requestMap (const MWWorld::CellStore* cell);\n\n        void addCell(MWWorld::CellStore* cell);\n\n        void removeCell (MWWorld::CellStore* cell);\n\n        osg::ref_ptr<osg::Texture2D> getMapTexture (int x, int y);\n\n        osg::ref_ptr<osg::Texture2D> getFogOfWarTexture (int x, int y);\n\n        void removeCamera(osg::Camera* cam);\n\n        /**\n         * Indicates a camera has been queued for rendering and can be cleaned up in the next frame. For internal use only.\n         */\n        void markForRemoval(osg::Camera* cam);\n\n        /**\n         * Removes cameras that have already been rendered. Should be called every frame to ensure that\n         * we do not render the same map more than once. Note, this cleanup is difficult to implement in an\n         * automated fashion, since we can't alter the scene graph structure from within an update callback.\n         */\n        void cleanupCameras();\n\n        /**\n         * Set the position & direction of the player, and returns the position in map space through the reference parameters.\n         * @remarks This is used to draw a \"fog of war\" effect\n         * to hide areas on the map the player has not discovered yet.\n         */\n        void updatePlayer (const osg::Vec3f& position, const osg::Quat& orientation,\n                           float& u, float& v, int& x, int& y, osg::Vec3f& direction);\n\n        /**\n         * Save the fog of war for this cell to its CellStore.\n         * @remarks This should be called when unloading a cell, and for all active cells prior to saving the game.\n         */\n        void saveFogOfWar(MWWorld::CellStore* cell);\n\n        /**\n         * Get the interior map texture index and normalized position on this texture, given a world position\n         */\n        void worldToInteriorMapPosition (osg::Vec2f pos, float& nX, float& nY, int& x, int& y);\n\n        osg::Vec2f interiorMapToWorldPosition (float nX, float nY, int x, int y);\n\n        /**\n         * Check if a given position is explored by the player (i.e. not obscured by fog of war)\n         */\n        bool isPositionExplored (float nX, float nY, int x, int y);\n\n        osg::Group* getRoot();\n\n    private:\n        osg::ref_ptr<osg::Group> mRoot;\n        osg::ref_ptr<osg::Node> mSceneRoot;\n\n        typedef std::vector< osg::ref_ptr<osg::Camera> > CameraVector;\n\n        CameraVector mActiveCameras;\n\n        CameraVector mCamerasPendingRemoval;\n\n        typedef std::set<std::pair<int, int> > Grid;\n        Grid mCurrentGrid;\n\n        struct MapSegment\n        {\n            MapSegment();\n            ~MapSegment();\n\n            void initFogOfWar();\n            void loadFogOfWar(const ESM::FogTexture& fog);\n            void saveFogOfWar(ESM::FogTexture& fog) const;\n            void createFogOfWarTexture();\n\n            osg::ref_ptr<osg::Texture2D> mMapTexture;\n            osg::ref_ptr<osg::Texture2D> mFogOfWarTexture;\n            osg::ref_ptr<osg::Image> mFogOfWarImage;\n\n            Grid mGrid; // the grid that was active at the time of rendering this segment\n\n            bool mHasFogState;\n        };\n\n        typedef std::map<std::pair<int, int>, MapSegment> SegmentMap;\n        SegmentMap mSegments;\n\n        int mMapResolution;\n\n        // the dynamic texture is a bottleneck, so don't set this too high\n        static const int sFogOfWarResolution = 32;\n\n        // size of a map segment (for exteriors, 1 cell)\n        float mMapWorldSize;\n\n        int mCellDistance;\n\n        float mAngle;\n        const osg::Vec2f rotatePoint(const osg::Vec2f& point, const osg::Vec2f& center, const float angle);\n\n        void requestExteriorMap(const MWWorld::CellStore* cell);\n        void requestInteriorMap(const MWWorld::CellStore* cell);\n\n        osg::ref_ptr<osg::Camera> createOrthographicCamera(float left, float top, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax);\n        void setupRenderToTexture(osg::ref_ptr<osg::Camera> camera, int x, int y);\n\n        bool mInterior;\n        osg::BoundingBox mBounds;\n    };\n\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/navmesh.cpp",
    "content": "#include \"navmesh.hpp\"\n#include \"vismask.hpp\"\n\n#include <components/sceneutil/navmesh.hpp>\n\n#include <osg/PositionAttitudeTransform>\n\nnamespace MWRender\n{\n    NavMesh::NavMesh(const osg::ref_ptr<osg::Group>& root, bool enabled)\n        : mRootNode(root)\n        , mEnabled(enabled)\n        , mGeneration(0)\n        , mRevision(0)\n    {\n    }\n\n    NavMesh::~NavMesh()\n    {\n        if (mEnabled)\n            disable();\n    }\n\n    bool NavMesh::toggle()\n    {\n        if (mEnabled)\n            disable();\n        else\n            enable();\n\n        return mEnabled;\n    }\n\n    void NavMesh::update(const dtNavMesh& navMesh, const std::size_t id,\n        const std::size_t generation, const std::size_t revision, const DetourNavigator::Settings& settings)\n    {\n        if (!mEnabled || (mGroup && mId == id && mGeneration == generation && mRevision == revision))\n            return;\n\n        mId = id;\n        mGeneration = generation;\n        mRevision = revision;\n        if (mGroup)\n            mRootNode->removeChild(mGroup);\n        mGroup = SceneUtil::createNavMeshGroup(navMesh, settings);\n        if (mGroup)\n        {\n            mGroup->setNodeMask(Mask_Debug);\n            mRootNode->addChild(mGroup);\n        }\n    }\n\n    void NavMesh::reset()\n    {\n        if (mGroup)\n        {\n            mRootNode->removeChild(mGroup);\n            mGroup = nullptr;\n        }\n    }\n\n    void NavMesh::enable()\n    {\n        if (mGroup)\n            mRootNode->addChild(mGroup);\n        mEnabled = true;\n    }\n\n    void NavMesh::disable()\n    {\n        if (mGroup)\n            mRootNode->removeChild(mGroup);\n        mEnabled = false;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/navmesh.hpp",
    "content": "#ifndef OPENMW_MWRENDER_NAVMESH_H\n#define OPENMW_MWRENDER_NAVMESH_H\n\n#include <components/detournavigator/navigator.hpp>\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class Group;\n    class Geometry;\n}\n\nnamespace MWRender\n{\n    class NavMesh\n    {\n    public:\n        NavMesh(const osg::ref_ptr<osg::Group>& root, bool enabled);\n        ~NavMesh();\n\n        bool toggle();\n\n        void update(const dtNavMesh& navMesh, const std::size_t number, const std::size_t generation,\n                    const std::size_t revision, const DetourNavigator::Settings& settings);\n\n        void reset();\n\n        void enable();\n\n        void disable();\n\n        bool isEnabled() const\n        {\n            return mEnabled;\n        }\n\n    private:\n        osg::ref_ptr<osg::Group> mRootNode;\n        bool mEnabled;\n        std::size_t mId = std::numeric_limits<std::size_t>::max();\n        std::size_t mGeneration;\n        std::size_t mRevision;\n        osg::ref_ptr<osg::Group> mGroup;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/npcanimation.cpp",
    "content": "#include \"npcanimation.hpp\"\n\n#include <osg/UserDataContainer>\n#include <osg/MatrixTransform>\n#include <osg/Depth>\n\n#include <osgUtil/RenderBin>\n#include <osgUtil/CullVisitor>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/misc/rng.hpp>\n\n#include <components/misc/resourcehelpers.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/sceneutil/actorutil.hpp>\n#include <components/sceneutil/attach.hpp>\n#include <components/sceneutil/visitor.hpp>\n#include <components/sceneutil/skeleton.hpp>\n#include <components/sceneutil/keyframe.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include <components/vfs/manager.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"camera.hpp\"\n#include \"rotatecontroller.hpp\"\n#include \"renderbin.hpp\"\n#include \"vismask.hpp\"\n\nnamespace\n{\n\nstd::string getVampireHead(const std::string& race, bool female)\n{\n    static std::map <std::pair<std::string,int>, const ESM::BodyPart* > sVampireMapping;\n\n    std::pair<std::string, int> thisCombination = std::make_pair(race, int(female));\n\n    if (sVampireMapping.find(thisCombination) == sVampireMapping.end())\n    {\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n        for (const ESM::BodyPart& bodypart : store.get<ESM::BodyPart>())\n        {\n            if (!bodypart.mData.mVampire)\n                continue;\n            if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)\n                continue;\n            if (bodypart.mData.mPart != ESM::BodyPart::MP_Head)\n                continue;\n            if (female != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))\n                continue;\n            if (!Misc::StringUtils::ciEqual(bodypart.mRace, race))\n                continue;\n            sVampireMapping[thisCombination] = &bodypart;\n        }\n    }\n\n    sVampireMapping.emplace(thisCombination, nullptr);\n\n    const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination];\n    if (!bodyPart)\n        return std::string();\n    return \"meshes\\\\\" + bodyPart->mModel;\n}\n\nstd::string getShieldBodypartMesh(const std::vector<ESM::PartReference>& bodyparts, bool female)\n{\n    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n    const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();\n    for (const auto& part : bodyparts)\n    {\n        if (part.mPart != ESM::PRT_Shield)\n            continue;\n\n        std::string bodypartName;\n        if (female && !part.mFemale.empty())\n            bodypartName = part.mFemale;\n        else if (!part.mMale.empty())\n            bodypartName = part.mMale;\n\n        if (!bodypartName.empty())\n        {\n            const ESM::BodyPart *bodypart = partStore.search(bodypartName);\n            if (bodypart == nullptr || bodypart->mData.mType != ESM::BodyPart::MT_Armor)\n                return std::string();\n            if (!bodypart->mModel.empty())\n                return \"meshes\\\\\" + bodypart->mModel;\n        }\n    }\n\n    return std::string();\n}\n\n}\n\n\nnamespace MWRender\n{\n\nclass HeadAnimationTime : public SceneUtil::ControllerSource\n{\nprivate:\n    MWWorld::Ptr mReference;\n    float mTalkStart;\n    float mTalkStop;\n    float mBlinkStart;\n    float mBlinkStop;\n\n    float mBlinkTimer;\n\n    bool mEnabled;\n\n    float mValue;\nprivate:\n    void resetBlinkTimer();\npublic:\n    HeadAnimationTime(const MWWorld::Ptr& reference);\n\n    void updatePtr(const MWWorld::Ptr& updated);\n\n    void update(float dt);\n\n    void setEnabled(bool enabled);\n\n    void setTalkStart(float value);\n    void setTalkStop(float value);\n    void setBlinkStart(float value);\n    void setBlinkStop(float value);\n\n    float getValue(osg::NodeVisitor* nv) override;\n};\n\n// --------------------------------------------------------------------------------\n\n/// Subclass RotateController to add a Z-offset for sneaking in first person mode.\n/// @note We use inheritance instead of adding another controller, so that we do not have to compute the worldOrient twice.\n/// @note Must be set on a MatrixTransform.\nclass NeckController : public RotateController\n{\npublic:\n    NeckController(osg::Node* relativeTo)\n        : RotateController(relativeTo)\n    {\n    }\n\n    void setOffset(const osg::Vec3f& offset)\n    {\n        mOffset = offset;\n    }\n\n    void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n    {\n        osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);\n        osg::Matrix matrix = transform->getMatrix();\n\n        osg::Quat worldOrient = getWorldOrientation(node);\n        osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate();\n\n        matrix.setRotate(orient);\n        matrix.setTrans(matrix.getTrans() + worldOrient.inverse() * mOffset);\n\n        transform->setMatrix(matrix);\n\n        traverse(node,nv);\n    }\n\nprivate:\n    osg::Vec3f mOffset;\n};\n\n// --------------------------------------------------------------------------------------------------------------\n\nHeadAnimationTime::HeadAnimationTime(const MWWorld::Ptr& reference)\n    : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mEnabled(true), mValue(0)\n{\n    resetBlinkTimer();\n}\n\nvoid HeadAnimationTime::updatePtr(const MWWorld::Ptr &updated)\n{\n    mReference = updated;\n}\n\nvoid HeadAnimationTime::setEnabled(bool enabled)\n{\n    mEnabled = enabled;\n}\n\nvoid HeadAnimationTime::resetBlinkTimer()\n{\n    mBlinkTimer = -(2.0f + Misc::Rng::rollDice(6));\n}\n\nvoid HeadAnimationTime::update(float dt)\n{\n    if (!mEnabled)\n        return;\n\n    if (!MWBase::Environment::get().getSoundManager()->sayActive(mReference))\n    {\n        mBlinkTimer += dt;\n\n        float duration = mBlinkStop - mBlinkStart;\n\n        if (mBlinkTimer >= 0 && mBlinkTimer <= duration)\n        {\n            mValue = mBlinkStart + mBlinkTimer;\n        }\n        else\n            mValue = mBlinkStop;\n\n        if (mBlinkTimer > duration)\n            resetBlinkTimer();\n    }\n    else\n    {\n        // FIXME: would be nice to hold on to the SoundPtr so we don't have to retrieve it every frame\n        mValue = mTalkStart +\n            (mTalkStop - mTalkStart) *\n            std::min(1.f, MWBase::Environment::get().getSoundManager()->getSaySoundLoudness(mReference)*2); // Rescale a bit (most voices are not very loud)\n    }\n}\n\nfloat HeadAnimationTime::getValue(osg::NodeVisitor*)\n{\n    return mValue;\n}\n\nvoid HeadAnimationTime::setTalkStart(float value)\n{\n    mTalkStart = value;\n}\n\nvoid HeadAnimationTime::setTalkStop(float value)\n{\n    mTalkStop = value;\n}\n\nvoid HeadAnimationTime::setBlinkStart(float value)\n{\n    mBlinkStart = value;\n}\n\nvoid HeadAnimationTime::setBlinkStop(float value)\n{\n    mBlinkStop = value;\n}\n\n// ----------------------------------------------------\n\nNpcAnimation::NpcType NpcAnimation::getNpcType() const\n{\n    const MWWorld::Class &cls = mPtr.getClass();\n    // Dead vampires should typically stay vampires.\n    if (mNpcType == Type_Vampire && cls.getNpcStats(mPtr).isDead() && !cls.getNpcStats(mPtr).isWerewolf())\n        return mNpcType;\n    return getNpcType(mPtr);\n}\n\nNpcAnimation::NpcType NpcAnimation::getNpcType(const MWWorld::Ptr& ptr)\n{\n    const MWWorld::Class &cls = ptr.getClass();\n    NpcAnimation::NpcType curType = Type_Normal;\n    if (cls.getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0)\n        curType = Type_Vampire;\n    if (cls.getNpcStats(ptr).isWerewolf())\n        curType = Type_Werewolf;\n\n    return curType;\n}\n\nstatic NpcAnimation::PartBoneMap createPartListMap()\n{\n    NpcAnimation::PartBoneMap result;\n    result.insert(std::make_pair(ESM::PRT_Head, \"Head\"));\n    result.insert(std::make_pair(ESM::PRT_Hair, \"Head\")); // note it uses \"Head\" as attach bone, but \"Hair\" as filter\n    result.insert(std::make_pair(ESM::PRT_Neck, \"Neck\"));\n    result.insert(std::make_pair(ESM::PRT_Cuirass, \"Chest\"));\n    result.insert(std::make_pair(ESM::PRT_Groin, \"Groin\"));\n    result.insert(std::make_pair(ESM::PRT_Skirt, \"Groin\"));\n    result.insert(std::make_pair(ESM::PRT_RHand, \"Right Hand\"));\n    result.insert(std::make_pair(ESM::PRT_LHand, \"Left Hand\"));\n    result.insert(std::make_pair(ESM::PRT_RWrist, \"Right Wrist\"));\n    result.insert(std::make_pair(ESM::PRT_LWrist, \"Left Wrist\"));\n    result.insert(std::make_pair(ESM::PRT_Shield, \"Shield Bone\"));\n    result.insert(std::make_pair(ESM::PRT_RForearm, \"Right Forearm\"));\n    result.insert(std::make_pair(ESM::PRT_LForearm, \"Left Forearm\"));\n    result.insert(std::make_pair(ESM::PRT_RUpperarm, \"Right Upper Arm\"));\n    result.insert(std::make_pair(ESM::PRT_LUpperarm, \"Left Upper Arm\"));\n    result.insert(std::make_pair(ESM::PRT_RFoot, \"Right Foot\"));\n    result.insert(std::make_pair(ESM::PRT_LFoot, \"Left Foot\"));\n    result.insert(std::make_pair(ESM::PRT_RAnkle, \"Right Ankle\"));\n    result.insert(std::make_pair(ESM::PRT_LAnkle, \"Left Ankle\"));\n    result.insert(std::make_pair(ESM::PRT_RKnee, \"Right Knee\"));\n    result.insert(std::make_pair(ESM::PRT_LKnee, \"Left Knee\"));\n    result.insert(std::make_pair(ESM::PRT_RLeg, \"Right Upper Leg\"));\n    result.insert(std::make_pair(ESM::PRT_LLeg, \"Left Upper Leg\"));\n    result.insert(std::make_pair(ESM::PRT_RPauldron, \"Right Clavicle\"));\n    result.insert(std::make_pair(ESM::PRT_LPauldron, \"Left Clavicle\"));\n    result.insert(std::make_pair(ESM::PRT_Weapon, \"Weapon Bone\")); // Fallback. The real node name depends on the current weapon type.\n    result.insert(std::make_pair(ESM::PRT_Tail, \"Tail\"));\n    return result;\n}\nconst NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap();\n\nNpcAnimation::~NpcAnimation()\n{\n    mAmmunition.reset();\n}\n\nNpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,\n                           bool disableSounds, ViewMode viewMode, float firstPersonFieldOfView)\n  : ActorAnimation(ptr, parentNode, resourceSystem),\n    mViewMode(viewMode),\n    mShowWeapons(false),\n    mShowCarriedLeft(true),\n    mNpcType(getNpcType(ptr)),\n    mFirstPersonFieldOfView(firstPersonFieldOfView),\n    mSoundsDisabled(disableSounds),\n    mAccurateAiming(false),\n    mAimingFactor(0.f)\n{\n    mNpc = mPtr.get<ESM::NPC>()->mBase;\n\n    mHeadAnimationTime = std::shared_ptr<HeadAnimationTime>(new HeadAnimationTime(mPtr));\n    mWeaponAnimationTime = std::shared_ptr<WeaponAnimationTime>(new WeaponAnimationTime(this));\n\n    for(size_t i = 0;i < ESM::PRT_Count;i++)\n    {\n        mPartslots[i] = -1;  //each slot is empty\n        mPartPriorities[i] = 0;\n    }\n\n    std::fill(mSounds.begin(), mSounds.end(), nullptr);\n\n    updateNpcBase();\n}\n\nvoid NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)\n{\n    assert(viewMode != VM_HeadOnly);\n    if(mViewMode == viewMode)\n        return;\n\n    mViewMode = viewMode;\n    MWBase::Environment::get().getWorld()->scaleObject(mPtr, mPtr.getCellRef().getScale()); // apply race height after view change\n\n    mAmmunition.reset();\n    rebuild();\n    setRenderBin();\n}\n\n/// @brief A RenderBin callback to clear the depth buffer before rendering.\nclass DepthClearCallback : public osgUtil::RenderBin::DrawCallback\n{\npublic:\n    DepthClearCallback()\n    {\n        mDepth = new osg::Depth;\n        mDepth->setWriteMask(true);\n    }\n\n    void drawImplementation(osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous) override\n    {\n        renderInfo.getState()->applyAttribute(mDepth);\n\n        glClear(GL_DEPTH_BUFFER_BIT);\n\n        bin->drawImplementation(renderInfo, previous);\n    }\n\n    osg::ref_ptr<osg::Depth> mDepth;\n};\n\n/// Overrides Field of View to given value for rendering the subgraph.\n/// Must be added as cull callback.\nclass OverrideFieldOfViewCallback : public osg::NodeCallback\n{\npublic:\n    OverrideFieldOfViewCallback(float fov)\n        : mFov(fov)\n    {\n    }\n\n    void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n    {\n        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n        float fov, aspect, zNear, zFar;\n        if (cv->getProjectionMatrix()->getPerspective(fov, aspect, zNear, zFar))\n        {\n            fov = mFov;\n            osg::ref_ptr<osg::RefMatrix> newProjectionMatrix = new osg::RefMatrix();\n            newProjectionMatrix->makePerspective(fov, aspect, zNear, zFar);\n            osg::ref_ptr<osg::RefMatrix> invertedOldMatrix = cv->getProjectionMatrix();\n            invertedOldMatrix = new osg::RefMatrix(osg::RefMatrix::inverse(*invertedOldMatrix));\n            osg::ref_ptr<osg::RefMatrix> viewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix());\n            viewMatrix->postMult(*newProjectionMatrix);\n            viewMatrix->postMult(*invertedOldMatrix);\n            cv->pushModelViewMatrix(viewMatrix, osg::Transform::ReferenceFrame::ABSOLUTE_RF);\n            traverse(node, nv);\n            cv->popModelViewMatrix();\n        }\n        else\n            traverse(node, nv);\n    }\n\nprivate:\n    float mFov;\n};\n\nvoid NpcAnimation::setRenderBin()\n{\n    if (mViewMode == VM_FirstPerson)\n    {\n        static bool prototypeAdded = false;\n        if (!prototypeAdded)\n        {\n            osg::ref_ptr<osgUtil::RenderBin> depthClearBin (new osgUtil::RenderBin);\n            depthClearBin->setDrawCallback(new DepthClearCallback);\n            osgUtil::RenderBin::addRenderBinPrototype(\"DepthClear\", depthClearBin);\n            prototypeAdded = true;\n        }\n        mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_FirstPerson, \"DepthClear\", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);\n    }\n    else if (osg::StateSet* stateset = mObjectRoot->getStateSet())\n        stateset->setRenderBinToInherit();\n}\n\nvoid NpcAnimation::rebuild()\n{\n    mScabbard.reset();\n    mHolsteredShield.reset();\n    updateNpcBase();\n\n    MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);\n}\n\nint NpcAnimation::getSlot(const osg::NodePath &path) const\n{\n    for (int i=0; i<ESM::PRT_Count; ++i)\n    {\n        PartHolderPtr part = mObjectParts[i];\n        if (!part.get())\n            continue;\n        if (std::find(path.begin(), path.end(), part->getNode().get()) != path.end())\n        {\n            return mPartslots[i];\n        }\n    }\n    return -1;\n}\n\nvoid NpcAnimation::updateNpcBase()\n{\n    clearAnimSources();\n    for(size_t i = 0;i < ESM::PRT_Count;i++)\n        removeIndividualPart((ESM::PartReferenceType)i);\n\n    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n    const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);\n    NpcType curType = getNpcType();\n    bool isWerewolf = (curType == Type_Werewolf);\n    bool isVampire = (curType == Type_Vampire);\n    bool isFemale = !mNpc->isMale();\n\n    mHeadModel.clear();\n    mHairModel.clear();\n\n    std::string headName = isWerewolf ? \"WerewolfHead\" : mNpc->mHead;\n    std::string hairName = isWerewolf ? \"WerewolfHair\" : mNpc->mHair;\n\n    if (!headName.empty())\n    {\n        const ESM::BodyPart* bp = store.get<ESM::BodyPart>().search(headName);\n        if (bp)\n            mHeadModel = \"meshes\\\\\" + bp->mModel;\n        else\n            Log(Debug::Warning) << \"Warning: Failed to load body part '\" << headName << \"'\";\n    }\n\n    if (!hairName.empty())\n    {\n        const ESM::BodyPart* bp = store.get<ESM::BodyPart>().search(hairName);\n        if (bp)\n            mHairModel = \"meshes\\\\\" + bp->mModel;\n        else\n            Log(Debug::Warning) << \"Warning: Failed to load body part '\" << hairName << \"'\";\n    }\n\n    const std::string& vampireHead = getVampireHead(mNpc->mRace, isFemale);\n    if (!isWerewolf && isVampire && !vampireHead.empty())\n        mHeadModel = vampireHead;\n\n    bool is1stPerson = mViewMode == VM_FirstPerson;\n    bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;\n\n    std::string defaultSkeleton = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf);\n    defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS());\n\n    std::string smodel = defaultSkeleton;\n    if (!is1stPerson && !isWerewolf && !mNpc->mModel.empty())\n        smodel = Misc::ResourceHelpers::correctActorModelPath(\"meshes\\\\\" + mNpc->mModel, mResourceSystem->getVFS());\n\n    setObjectRoot(smodel, true, true, false);\n\n    updateParts();\n\n    if(!is1stPerson)\n    {\n        const std::string base = Settings::Manager::getString(\"xbaseanim\", \"Models\");\n        if (smodel != base && !isWerewolf)\n            addAnimSource(base, smodel);\n\n        if (smodel != defaultSkeleton && base != defaultSkeleton)\n            addAnimSource(defaultSkeleton, smodel);\n\n        addAnimSource(smodel, smodel);\n\n        if(!isWerewolf && Misc::StringUtils::lowerCase(mNpc->mRace).find(\"argonian\") != std::string::npos)\n            addAnimSource(\"meshes\\\\xargonian_swimkna.nif\", smodel);\n    }\n    else\n    {\n        const std::string base = Settings::Manager::getString(\"xbaseanim1st\", \"Models\");\n        if (smodel != base && !isWerewolf)\n            addAnimSource(base, smodel);\n\n        addAnimSource(smodel, smodel);\n\n        mObjectRoot->setNodeMask(Mask_FirstPerson);\n        mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView));\n    }\n\n    mWeaponAnimationTime->updateStartTime();\n}\n\nstd::string NpcAnimation::getShieldMesh(MWWorld::ConstPtr shield) const\n{\n    std::string mesh = shield.getClass().getModel(shield);\n    const ESM::Armor *armor = shield.get<ESM::Armor>()->mBase;\n    const std::vector<ESM::PartReference>& bodyparts = armor->mParts.mParts;\n    // Try to recover the body part model, use ground model as a fallback otherwise.\n    if (!bodyparts.empty())\n        mesh = getShieldBodypartMesh(bodyparts, !mNpc->isMale());\n\n    if (mesh.empty())\n        return std::string();\n\n    std::string holsteredName = mesh;\n    holsteredName = holsteredName.replace(holsteredName.size()-4, 4, \"_sh.nif\");\n    if(mResourceSystem->getVFS()->exists(holsteredName))\n    {\n        osg::ref_ptr<osg::Node> shieldTemplate = mResourceSystem->getSceneManager()->getInstance(holsteredName);\n        SceneUtil::FindByNameVisitor findVisitor (\"Bip01 Sheath\");\n        shieldTemplate->accept(findVisitor);\n        osg::ref_ptr<osg::Node> sheathNode = findVisitor.mFoundNode;\n        if(!sheathNode)\n            return std::string();\n    }\n\n    return mesh;\n}\n\nvoid NpcAnimation::updateParts()\n{\n    if (!mObjectRoot.get())\n        return;\n\n    NpcType curType = getNpcType();\n    if (curType != mNpcType)\n    {\n        mNpcType = curType;\n        rebuild();\n        return;\n    }\n\n    static const struct {\n        int mSlot;\n        int mBasePriority;\n    } slotlist[] = {\n        // FIXME: Priority is based on the number of reserved slots. There should be a better way.\n        { MWWorld::InventoryStore::Slot_Robe,         11 },\n        { MWWorld::InventoryStore::Slot_Skirt,         3 },\n        { MWWorld::InventoryStore::Slot_Helmet,        0 },\n        { MWWorld::InventoryStore::Slot_Cuirass,       0 },\n        { MWWorld::InventoryStore::Slot_Greaves,       0 },\n        { MWWorld::InventoryStore::Slot_LeftPauldron,  0 },\n        { MWWorld::InventoryStore::Slot_RightPauldron, 0 },\n        { MWWorld::InventoryStore::Slot_Boots,         0 },\n        { MWWorld::InventoryStore::Slot_LeftGauntlet,  0 },\n        { MWWorld::InventoryStore::Slot_RightGauntlet, 0 },\n        { MWWorld::InventoryStore::Slot_Shirt,         0 },\n        { MWWorld::InventoryStore::Slot_Pants,         0 },\n        { MWWorld::InventoryStore::Slot_CarriedLeft,   0 },\n        { MWWorld::InventoryStore::Slot_CarriedRight,  0 }\n    };\n    static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]);\n\n    bool wasArrowAttached = isArrowAttached();\n    mAmmunition.reset();\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++)\n    {\n        MWWorld::ConstContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot);\n\n        removePartGroup(slotlist[i].mSlot);\n\n        if(store == inv.end())\n            continue;\n\n        if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet)\n            removeIndividualPart(ESM::PRT_Hair);\n\n        int prio = 1;\n        bool enchantedGlow = !store->getClass().getEnchantment(*store).empty();\n        osg::Vec4f glowColor = store->getClass().getEnchantmentColor(*store);\n        if(store->getTypeName() == typeid(ESM::Clothing).name())\n        {\n            prio = ((slotlist[i].mBasePriority+1)<<1) + 0;\n            const ESM::Clothing *clothes = store->get<ESM::Clothing>()->mBase;\n            addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts, enchantedGlow, &glowColor);\n        }\n        else if(store->getTypeName() == typeid(ESM::Armor).name())\n        {\n            prio = ((slotlist[i].mBasePriority+1)<<1) + 1;\n            const ESM::Armor *armor = store->get<ESM::Armor>()->mBase;\n            addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts, enchantedGlow, &glowColor);\n        }\n\n        if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe)\n        {\n            ESM::PartReferenceType parts[] = {\n                ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,\n                ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee,\n                ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_Cuirass\n            };\n            size_t parts_size = sizeof(parts)/sizeof(parts[0]);\n            for(size_t p = 0;p < parts_size;++p)\n                reserveIndividualPart(parts[p], slotlist[i].mSlot, prio);\n        }\n        else if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Skirt)\n        {\n            reserveIndividualPart(ESM::PRT_Groin, slotlist[i].mSlot, prio);\n            reserveIndividualPart(ESM::PRT_RLeg, slotlist[i].mSlot, prio);\n            reserveIndividualPart(ESM::PRT_LLeg, slotlist[i].mSlot, prio);\n        }\n    }\n\n    if(mViewMode != VM_FirstPerson)\n    {\n        if(mPartPriorities[ESM::PRT_Head] < 1 && !mHeadModel.empty())\n            addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel);\n        if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1 && !mHairModel.empty())\n            addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel);\n    }\n    if(mViewMode == VM_HeadOnly)\n        return;\n\n    if(mPartPriorities[ESM::PRT_Shield] < 1)\n    {\n        MWWorld::ConstContainerStoreIterator store = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n        MWWorld::ConstPtr part;\n        if(store != inv.end() && (part=*store).getTypeName() == typeid(ESM::Light).name())\n        {\n            const ESM::Light *light = part.get<ESM::Light>()->mBase;\n            addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft,\n                                       1, \"meshes\\\\\"+light->mModel);\n            if (mObjectParts[ESM::PRT_Shield])\n                addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), light);\n        }\n    }\n\n    showWeapons(mShowWeapons);\n    showCarriedLeft(mShowCarriedLeft);\n\n    bool isWerewolf = (getNpcType() == Type_Werewolf);\n    std::string race = (isWerewolf ? \"werewolf\" : Misc::StringUtils::lowerCase(mNpc->mRace));\n\n    const std::vector<const ESM::BodyPart*> &parts = getBodyParts(race, !mNpc->isMale(), mViewMode == VM_FirstPerson, isWerewolf);\n    for(int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part)\n    {\n        if(mPartPriorities[part] < 1)\n        {\n            const ESM::BodyPart* bodypart = parts[part];\n            if(bodypart)\n                addOrReplaceIndividualPart((ESM::PartReferenceType)part, -1, 1,\n                                           \"meshes\\\\\"+bodypart->mModel);\n        }\n    }\n\n    if (wasArrowAttached)\n        attachArrow();\n}\n\n\n\nPartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const std::string& bonename, const std::string& bonefilter, bool enchantedGlow, osg::Vec4f* glowColor)\n{\n    osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->getInstance(model);\n\n    const NodeMap& nodeMap = getNodeMap();\n    NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));\n    if (found == nodeMap.end())\n        throw std::runtime_error(\"Can't find attachment node \" + bonename);\n\n    osg::ref_ptr<osg::Node> attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, found->second);\n    if (enchantedGlow)\n        mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, *glowColor);\n\n    return PartHolderPtr(new PartHolder(attached));\n}\n\nosg::Vec3f NpcAnimation::runAnimation(float timepassed)\n{\n    osg::Vec3f ret = Animation::runAnimation(timepassed);\n\n    mHeadAnimationTime->update(timepassed);\n\n    if (mFirstPersonNeckController)\n    {\n        if (mAccurateAiming)\n            mAimingFactor = 1.f;\n        else\n            mAimingFactor = std::max(0.f, mAimingFactor - timepassed * 0.5f);\n\n        float rotateFactor = 0.75f + 0.25f * mAimingFactor;\n\n        mFirstPersonNeckController->setRotate(osg::Quat(mPtr.getRefData().getPosition().rot[0] * rotateFactor, osg::Vec3f(-1,0,0)));\n        mFirstPersonNeckController->setOffset(mFirstPersonOffset);\n    }\n\n    WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians());\n\n    return ret;\n}\n\nvoid NpcAnimation::removeIndividualPart(ESM::PartReferenceType type)\n{\n    mPartPriorities[type] = 0;\n    mPartslots[type] = -1;\n\n    mObjectParts[type].reset();\n    if (mSounds[type] != nullptr && !mSoundsDisabled)\n    {\n        MWBase::Environment::get().getSoundManager()->stopSound(mSounds[type]);\n        mSounds[type] = nullptr;\n    }\n}\n\nvoid NpcAnimation::reserveIndividualPart(ESM::PartReferenceType type, int group, int priority)\n{\n    if(priority > mPartPriorities[type])\n    {\n        removeIndividualPart(type);\n        mPartPriorities[type] = priority;\n        mPartslots[type] = group;\n    }\n}\n\nvoid NpcAnimation::removePartGroup(int group)\n{\n    for(int i = 0; i < ESM::PRT_Count; i++)\n    {\n        if(mPartslots[i] == group)\n            removeIndividualPart((ESM::PartReferenceType)i);\n    }\n}\n\nbool NpcAnimation::isFirstPersonPart(const ESM::BodyPart* bodypart)\n{\n    return bodypart->mId.size() >= 3 && bodypart->mId.substr(bodypart->mId.size()-3, 3) == \"1st\";\n}\n\nbool NpcAnimation::isFemalePart(const ESM::BodyPart* bodypart)\n{\n    return bodypart->mData.mFlags & ESM::BodyPart::BPF_Female;\n}\n\nbool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh, bool enchantedGlow, osg::Vec4f* glowColor)\n{\n    if(priority <= mPartPriorities[type])\n        return false;\n\n    removeIndividualPart(type);\n    mPartslots[type] = group;\n    mPartPriorities[type] = priority;\n    try\n    {\n        std::string bonename = sPartList.at(type);\n        if (type == ESM::PRT_Weapon)\n        {\n            const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n            MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n            if(weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name())\n            {\n                int weaponType = weapon->get<ESM::Weapon>()->mBase->mData.mType;\n                const std::string weaponBonename = MWMechanics::getWeaponType(weaponType)->mAttachBone;\n\n                if (weaponBonename != bonename)\n                {\n                    const NodeMap& nodeMap = getNodeMap();\n                    NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(weaponBonename));\n                    if (found != nodeMap.end())\n                        bonename = weaponBonename;\n                }\n            }\n        }\n\n        // PRT_Hair seems to be the only type that breaks consistency and uses a filter that's different from the attachment bone\n        const std::string bonefilter = (type == ESM::PRT_Hair) ? \"hair\" : bonename;\n        mObjectParts[type] = insertBoundedPart(mesh, bonename, bonefilter, enchantedGlow, glowColor);\n    }\n    catch (std::exception& e)\n    {\n        Log(Debug::Error) << \"Error adding NPC part: \" << e.what();\n        return false;\n    }\n\n    if (!mSoundsDisabled)\n    {\n        const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n        MWWorld::ConstContainerStoreIterator csi = inv.getSlot(group < 0 ? MWWorld::InventoryStore::Slot_Helmet : group);\n        if (csi != inv.end())\n        {\n            const auto soundId = csi->getClass().getSound(*csi);\n            if (!soundId.empty())\n            {\n                mSounds[type] = MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, soundId,\n                    1.0f, 1.0f, MWSound::Type::Sfx, MWSound::PlayMode::Loop\n                );\n            }\n        }\n    }\n\n    osg::Node* node = mObjectParts[type]->getNode();\n    if (node->getNumChildrenRequiringUpdateTraversal() > 0)\n    {\n        std::shared_ptr<SceneUtil::ControllerSource> src;\n        if (type == ESM::PRT_Head)\n        {\n            src = mHeadAnimationTime;\n\n            if (node->getUserDataContainer())\n            {\n                for (unsigned int i=0; i<node->getUserDataContainer()->getNumUserObjects(); ++i)\n                {\n                    osg::Object* obj = node->getUserDataContainer()->getUserObject(i);\n                    if (SceneUtil::TextKeyMapHolder* keys = dynamic_cast<SceneUtil::TextKeyMapHolder*>(obj))\n                    {\n                        for (const auto &key : keys->mTextKeys)\n                        {\n                            if (Misc::StringUtils::ciEqual(key.second, \"talk: start\"))\n                                mHeadAnimationTime->setTalkStart(key.first);\n                            if (Misc::StringUtils::ciEqual(key.second, \"talk: stop\"))\n                                mHeadAnimationTime->setTalkStop(key.first);\n                            if (Misc::StringUtils::ciEqual(key.second, \"blink: start\"))\n                                mHeadAnimationTime->setBlinkStart(key.first);\n                            if (Misc::StringUtils::ciEqual(key.second, \"blink: stop\"))\n                                mHeadAnimationTime->setBlinkStop(key.first);\n                        }\n\n                        break;\n                    }\n                }\n            }\n        }\n        else if (type == ESM::PRT_Weapon)\n            src = mWeaponAnimationTime;\n        else\n            src.reset(new NullAnimationTime);\n\n        SceneUtil::AssignControllerSourcesVisitor assignVisitor(src);\n        node->accept(assignVisitor);\n    }\n\n    return true;\n}\n\nvoid NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts, bool enchantedGlow, osg::Vec4f* glowColor)\n{\n    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n    const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();\n\n    const char *ext = (mViewMode == VM_FirstPerson) ? \".1st\" : \"\";\n    for(const ESM::PartReference& part : parts)\n    {\n        const ESM::BodyPart *bodypart = nullptr;\n        if(!mNpc->isMale() && !part.mFemale.empty())\n        {\n            bodypart = partStore.search(part.mFemale+ext);\n            if(!bodypart && mViewMode == VM_FirstPerson)\n            {\n                bodypart = partStore.search(part.mFemale);\n                if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand ||\n                                 bodypart->mData.mPart == ESM::BodyPart::MP_Wrist ||\n                                 bodypart->mData.mPart == ESM::BodyPart::MP_Forearm ||\n                                 bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm))\n                    bodypart = nullptr;\n            }\n            else if (!bodypart)\n                Log(Debug::Warning) << \"Warning: Failed to find body part '\" << part.mFemale << \"'\";\n        }\n        if(!bodypart && !part.mMale.empty())\n        {\n            bodypart = partStore.search(part.mMale+ext);\n            if(!bodypart && mViewMode == VM_FirstPerson)\n            {\n                bodypart = partStore.search(part.mMale);\n                if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand ||\n                                 bodypart->mData.mPart == ESM::BodyPart::MP_Wrist ||\n                                 bodypart->mData.mPart == ESM::BodyPart::MP_Forearm ||\n                                 bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm))\n                    bodypart = nullptr;\n            }\n            else if (!bodypart)\n                Log(Debug::Warning) << \"Warning: Failed to find body part '\" << part.mMale << \"'\";\n        }\n\n        if(bodypart)\n            addOrReplaceIndividualPart((ESM::PartReferenceType)part.mPart, group, priority, \"meshes\\\\\"+bodypart->mModel, enchantedGlow, glowColor);\n        else\n            reserveIndividualPart((ESM::PartReferenceType)part.mPart, group, priority);\n    }\n}\n\nvoid NpcAnimation::addControllers()\n{\n    Animation::addControllers();\n\n    mFirstPersonNeckController = nullptr;\n    WeaponAnimation::deleteControllers();\n\n    if (mViewMode == VM_FirstPerson)\n    {\n        NodeMap::iterator found = mNodeMap.find(\"bip01 neck\");\n        if (found != mNodeMap.end())\n        {\n            osg::MatrixTransform* node = found->second.get();\n            mFirstPersonNeckController = new NeckController(mObjectRoot.get());\n            node->addUpdateCallback(mFirstPersonNeckController);\n            mActiveControllers.emplace_back(node, mFirstPersonNeckController);\n        }\n    }\n    else if (mViewMode == VM_Normal)\n    {\n        WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());\n    }\n}\n\nvoid NpcAnimation::showWeapons(bool showWeapon)\n{\n    mShowWeapons = showWeapon;\n    mAmmunition.reset();\n    if(showWeapon)\n    {\n        const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n        MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n        if(weapon != inv.end())\n        {\n            osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);\n            std::string mesh = weapon->getClass().getModel(*weapon);\n            addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1,\n                                       mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);\n\n            // Crossbows start out with a bolt attached\n            if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&\n                    weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)\n            {\n                int ammotype = MWMechanics::getWeaponType(ESM::Weapon::MarksmanCrossbow)->mAmmoType;\n                MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n                if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ammotype)\n                    attachArrow();\n            }\n        }\n    }\n    else\n    {\n        removeIndividualPart(ESM::PRT_Weapon);\n        // If we remove/hide weapon from player, we should reset attack animation as well\n        if (mPtr == MWMechanics::getPlayer())\n            MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);\n    }\n\n    updateHolsteredWeapon(!mShowWeapons);\n    updateQuiver();\n}\n\nvoid NpcAnimation::showCarriedLeft(bool show)\n{\n    mShowCarriedLeft = show;\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);\n    if(show && iter != inv.end())\n    {\n        osg::Vec4f glowColor = iter->getClass().getEnchantmentColor(*iter);\n        std::string mesh = iter->getClass().getModel(*iter);\n        // For shields we must try to use the body part model\n        if (iter->getTypeName() == typeid(ESM::Armor).name())\n        {\n            const ESM::Armor *armor = iter->get<ESM::Armor>()->mBase;\n            const std::vector<ESM::PartReference>& bodyparts = armor->mParts.mParts;\n            if (!bodyparts.empty())\n                mesh = getShieldBodypartMesh(bodyparts, !mNpc->isMale());\n        }\n        if (mesh.empty() || addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1,\n                                        mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor))\n        {\n            if (mesh.empty())\n                reserveIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1);\n            if (iter->getTypeName() == typeid(ESM::Light).name() && mObjectParts[ESM::PRT_Shield])\n                addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), iter->get<ESM::Light>()->mBase);\n        }\n    }\n    else\n        removeIndividualPart(ESM::PRT_Shield);\n\n    updateHolsteredShield(mShowCarriedLeft);\n}\n\nvoid NpcAnimation::attachArrow()\n{\n    WeaponAnimation::attachArrow(mPtr);\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n    if (ammo != inv.end() && !ammo->getClass().getEnchantment(*ammo).empty())\n    {\n        osg::Group* bone = getArrowBone();\n        if (bone != nullptr && bone->getNumChildren())\n            SceneUtil::addEnchantedGlow(bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo));\n    }\n\n    updateQuiver();\n}\n\nvoid NpcAnimation::detachArrow()\n{\n    WeaponAnimation::detachArrow(mPtr);\n    updateQuiver();\n}\n\nvoid NpcAnimation::releaseArrow(float attackStrength)\n{\n    WeaponAnimation::releaseArrow(mPtr, attackStrength);\n    updateQuiver();\n}\n\nosg::Group* NpcAnimation::getArrowBone()\n{\n    PartHolderPtr part = mObjectParts[ESM::PRT_Weapon];\n    if (!part)\n        return nullptr;\n\n    const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);\n    MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())\n        return nullptr;\n\n    int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;\n    int ammoType = MWMechanics::getWeaponType(type)->mAmmoType;\n\n    // Try to find and attachment bone in actor's skeleton, otherwise fall back to the ArrowBone in weapon's mesh\n    osg::Group* bone = getBoneByName(MWMechanics::getWeaponType(ammoType)->mAttachBone);\n    if (bone == nullptr)\n    {\n        SceneUtil::FindByNameVisitor findVisitor (\"ArrowBone\");\n        part->getNode()->accept(findVisitor);\n        bone = findVisitor.mFoundNode;\n    }\n    return bone;\n}\n\nosg::Node* NpcAnimation::getWeaponNode()\n{\n    PartHolderPtr part = mObjectParts[ESM::PRT_Weapon];\n    if (!part)\n        return nullptr;\n    return part->getNode();\n}\n\nResource::ResourceSystem* NpcAnimation::getResourceSystem()\n{\n    return mResourceSystem;\n}\n\nvoid NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew)\n{\n    // During first auto equip, we don't play any sounds.\n    // Basically we don't want sounds when the actor is first loaded,\n    // the items should appear as if they'd always been equipped.\n    if (isNew)\n    {\n        static const std::string schools[] = {\n            \"alteration\", \"conjuration\", \"destruction\", \"illusion\", \"mysticism\", \"restoration\"\n        };\n\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        if(!magicEffect->mHitSound.empty())\n            sndMgr->playSound3D(mPtr, magicEffect->mHitSound, 1.0f, 1.0f);\n        else\n            sndMgr->playSound3D(mPtr, schools[magicEffect->mData.mSchool]+\" hit\", 1.0f, 1.0f);\n    }\n\n    if (!magicEffect->mHit.empty())\n    {\n        const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);\n        bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;\n        // Don't play particle VFX unless the effect is new or it should be looping.\n        if (isNew || loop)\n            addEffect(\"meshes\\\\\" + castStatic->mModel, magicEffect->mIndex, loop, \"\", magicEffect->mParticle);\n    }\n}\n\nvoid NpcAnimation::enableHeadAnimation(bool enable)\n{\n    mHeadAnimationTime->setEnabled(enable);\n}\n\nvoid NpcAnimation::setWeaponGroup(const std::string &group, bool relativeDuration)\n{\n    mWeaponAnimationTime->setGroup(group, relativeDuration);\n}\n\nvoid NpcAnimation::equipmentChanged()\n{\n    static const bool shieldSheathing = Settings::Manager::getBool(\"shield sheathing\", \"Game\");\n    if (shieldSheathing)\n    {\n        int weaptype = ESM::Weapon::None;\n        MWMechanics::getActiveWeapon(mPtr, &weaptype);\n        showCarriedLeft(updateCarriedLeftVisible(weaptype));\n    }\n\n    updateParts();\n}\n\nvoid NpcAnimation::setVampire(bool vampire)\n{\n    if (mNpcType == Type_Werewolf) // we can't have werewolf vampires, can we\n        return;\n    if ((mNpcType == Type_Vampire) != vampire)\n    {\n        if (mPtr == MWMechanics::getPlayer())\n            MWBase::Environment::get().getWorld()->reattachPlayerCamera();\n        else\n            rebuild();\n    }\n}\n\nvoid NpcAnimation::setFirstPersonOffset(const osg::Vec3f &offset)\n{\n    mFirstPersonOffset = offset;\n}\n\nvoid NpcAnimation::updatePtr(const MWWorld::Ptr &updated)\n{\n    Animation::updatePtr(updated);\n    mHeadAnimationTime->updatePtr(updated);\n}\n\n// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination\ntypedef std::map< std::pair<std::string,int>,std::vector<const ESM::BodyPart*> > RaceMapping;\nstatic RaceMapping sRaceMapping;\n\nconst std::vector<const ESM::BodyPart *>& NpcAnimation::getBodyParts(const std::string &race, bool female, bool firstPerson, bool werewolf)\n{\n    static const int Flag_FirstPerson = 1<<1;\n    static const int Flag_Female      = 1<<0;\n\n    int flags = (werewolf ? -1 : 0);\n    if(female)\n        flags |= Flag_Female;\n    if(firstPerson)\n        flags |= Flag_FirstPerson;\n\n    RaceMapping::iterator found = sRaceMapping.find(std::make_pair(race, flags));\n    if (found != sRaceMapping.end())\n        return found->second;\n    else\n    {\n        std::vector<const ESM::BodyPart*>& parts = sRaceMapping[std::make_pair(race, flags)];\n\n        typedef std::multimap<ESM::BodyPart::MeshPart,ESM::PartReferenceType> BodyPartMapType;\n        static const BodyPartMapType sBodyPartMap =\n        {\n            {ESM::BodyPart::MP_Neck, ESM::PRT_Neck},\n            {ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass},\n            {ESM::BodyPart::MP_Groin, ESM::PRT_Groin},\n            {ESM::BodyPart::MP_Hand, ESM::PRT_RHand},\n            {ESM::BodyPart::MP_Hand, ESM::PRT_LHand},\n            {ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist},\n            {ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist},\n            {ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm},\n            {ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm},\n            {ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm},\n            {ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm},\n            {ESM::BodyPart::MP_Foot, ESM::PRT_RFoot},\n            {ESM::BodyPart::MP_Foot, ESM::PRT_LFoot},\n            {ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle},\n            {ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle},\n            {ESM::BodyPart::MP_Knee, ESM::PRT_RKnee},\n            {ESM::BodyPart::MP_Knee, ESM::PRT_LKnee},\n            {ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg},\n            {ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg},\n            {ESM::BodyPart::MP_Tail, ESM::PRT_Tail}\n        };\n\n        parts.resize(ESM::PRT_Count, nullptr);\n\n        if (werewolf)\n            return parts;\n\n        const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n\n        for(const ESM::BodyPart& bodypart : store.get<ESM::BodyPart>())\n        {\n            if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)\n                continue;\n            if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)\n                continue;\n\n            if (!Misc::StringUtils::ciEqual(bodypart.mRace, race))\n                continue;\n\n            bool partFirstPerson = isFirstPersonPart(&bodypart);\n\n            bool isHand = bodypart.mData.mPart == ESM::BodyPart::MP_Hand ||\n                                    bodypart.mData.mPart == ESM::BodyPart::MP_Wrist ||\n                                    bodypart.mData.mPart == ESM::BodyPart::MP_Forearm ||\n                                    bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm;\n\n            bool isSameGender = isFemalePart(&bodypart) == female;\n\n            /* A fallback for the arms if 1st person is missing:\n             1. Try to use 3d person skin for same gender\n             2. Try to use 1st person skin for male, if female == true\n             3. Try to use 3d person skin for male, if female == true\n\n             A fallback in another cases: allow to use male bodyparts, if female == true\n            */\n            if (firstPerson && isHand && !partFirstPerson)\n            {\n                // Allow 3rd person skins as a fallback for the arms if 1st person is missing\n                BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));\n                while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)\n                {\n                    // If we have no fallback bodypart now and bodypart is for same gender (1)\n                    if(!parts[bIt->second] && isSameGender)\n                       parts[bIt->second] = &bodypart;\n\n                    // If we have fallback bodypart for other gender and found fallback for current gender (1)\n                    else if(isSameGender && isFemalePart(parts[bIt->second]) != female)\n                       parts[bIt->second] = &bodypart;\n\n                    // If we have no fallback bodypart and searching for female bodyparts (3)\n                    else if(!parts[bIt->second] && female)\n                       parts[bIt->second] = &bodypart;\n\n                    ++bIt;\n                }\n\n                continue;\n            }\n\n            // Don't allow to use podyparts for a different view\n            if (partFirstPerson != firstPerson)\n                continue;\n\n            if (female && !isFemalePart(&bodypart))\n            {\n                // Allow male parts as fallback for females if female parts are missing\n                BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));\n                while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)\n                {\n                    // If we have no fallback bodypart now\n                    if(!parts[bIt->second])\n                        parts[bIt->second] = &bodypart;\n\n                    // If we have 3d person fallback bodypart for hand and 1st person fallback found (2)\n                    else if(isHand && !isFirstPersonPart(parts[bIt->second]) && partFirstPerson)\n                        parts[bIt->second] = &bodypart;\n\n                    ++bIt;\n                }\n\n                continue;\n            }\n\n            // Don't allow to use podyparts for another gender\n            if (female != isFemalePart(&bodypart))\n                continue;\n\n            // Use properly found bodypart, replacing fallbacks\n            BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));\n            while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)\n            {\n                parts[bIt->second] = &bodypart;\n                ++bIt;\n            }\n        }\n        return parts;\n    }\n}\n\nvoid NpcAnimation::setAccurateAiming(bool enabled)\n{\n    mAccurateAiming = enabled;\n}\n\nbool NpcAnimation::isArrowAttached() const\n{\n    return mAmmunition != nullptr;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/npcanimation.hpp",
    "content": "#ifndef GAME_RENDER_NPCANIMATION_H\n#define GAME_RENDER_NPCANIMATION_H\n\n#include \"animation.hpp\"\n\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"actoranimation.hpp\"\n#include \"weaponanimation.hpp\"\n\n#include <array>\n\nnamespace ESM\n{\n    struct NPC;\n    struct BodyPart;\n}\n\nnamespace MWSound\n{\n    class Sound;\n}\n\nnamespace MWRender\n{\n\nclass NeckController;\nclass HeadAnimationTime;\n\nclass NpcAnimation : public ActorAnimation, public WeaponAnimation, public MWWorld::InventoryStoreListener\n{\npublic:\n    void equipmentChanged() override;\n    void permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew) override;\n\npublic:\n    typedef std::map<ESM::PartReferenceType,std::string> PartBoneMap;\n\n    enum ViewMode {\n        VM_Normal,\n        VM_FirstPerson,\n        VM_HeadOnly\n    };\n\nprivate:\n    static const PartBoneMap sPartList;\n\n    // Bounded Parts\n    PartHolderPtr mObjectParts[ESM::PRT_Count];\n    std::array<MWSound::Sound*, ESM::PRT_Count> mSounds;\n\n    const ESM::NPC *mNpc;\n    std::string    mHeadModel;\n    std::string    mHairModel;\n    ViewMode       mViewMode;\n    bool mShowWeapons;\n    bool mShowCarriedLeft;\n\n    enum NpcType\n    {\n        Type_Normal,\n        Type_Werewolf,\n        Type_Vampire\n    };\n    NpcType mNpcType;\n\n    int mPartslots[ESM::PRT_Count];  //Each part slot is taken by clothing, armor, or is empty\n    int mPartPriorities[ESM::PRT_Count];\n\n    osg::Vec3f mFirstPersonOffset;\n    // Field of view to use when rendering first person meshes\n    float mFirstPersonFieldOfView;\n\n    std::shared_ptr<HeadAnimationTime> mHeadAnimationTime;\n    std::shared_ptr<WeaponAnimationTime> mWeaponAnimationTime;\n\n    bool mSoundsDisabled;\n\n    bool mAccurateAiming;\n    float mAimingFactor;\n\n    void updateNpcBase();\n\n    NpcType getNpcType() const;\n\n    PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename,\n                                        const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=nullptr);\n\n    void removeIndividualPart(ESM::PartReferenceType type);\n    void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority);\n\n    bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh,\n                                    bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr);\n    void removePartGroup(int group);\n    void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts,\n                                    bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr);\n\n    void setRenderBin();\n\n    osg::ref_ptr<NeckController> mFirstPersonNeckController;\n\n    static bool isFirstPersonPart(const ESM::BodyPart* bodypart);\n    static bool isFemalePart(const ESM::BodyPart* bodypart);\n    static NpcType getNpcType(const MWWorld::Ptr& ptr);\n\nprotected:\n    void addControllers() override;\n    bool isArrowAttached() const override;\n    std::string getShieldMesh(MWWorld::ConstPtr shield) const override;\n\npublic:\n    /**\n     * @param ptr\n     * @param disableListener  Don't listen for equipment changes and magic effects. InventoryStore only supports\n     *                         one listener at a time, so you shouldn't do this if creating several NpcAnimations\n     *                         for the same Ptr, eg preview dolls for the player.\n     *                         Those need to be manually rendered anyway.\n     * @param disableSounds    Same as \\a disableListener but for playing items sounds\n     * @param viewMode\n     */\n    NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,\n                 bool disableSounds = false, ViewMode viewMode=VM_Normal, float firstPersonFieldOfView=55.f);\n    virtual ~NpcAnimation();\n\n    void enableHeadAnimation(bool enable) override;\n\n    /// 1: the first person meshes follow the camera's rotation completely\n    /// 0: the first person meshes follow the camera with a reduced factor, so you can look down at your own hands\n    void setAccurateAiming(bool enabled) override;\n\n    void setWeaponGroup(const std::string& group, bool relativeDuration) override;\n\n    osg::Vec3f runAnimation(float timepassed) override;\n\n    /// A relative factor (0-1) that decides if and how much the skeleton should be pitched\n    /// to indicate the facing orientation of the character.\n    void setPitchFactor(float factor) override { mPitchFactor = factor; }\n\n    void showWeapons(bool showWeapon) override;\n\n    bool getCarriedLeftShown() const override { return mShowCarriedLeft; }\n    void showCarriedLeft(bool show) override;\n\n    void attachArrow() override;\n    void detachArrow() override;\n    void releaseArrow(float attackStrength) override;\n\n    osg::Group* getArrowBone() override;\n    osg::Node* getWeaponNode() override;\n    Resource::ResourceSystem* getResourceSystem() override;\n\n    // WeaponAnimation\n    void showWeapon(bool show) override { showWeapons(show); }\n\n    void setViewMode(ViewMode viewMode);\n\n    void updateParts();\n\n    /// Rebuilds the NPC, updating their root model, animation sources, and equipment.\n    void rebuild();\n\n    /// Get the inventory slot that the given node path leads into, or -1 if not found.\n    int getSlot(const osg::NodePath& path) const;\n\n    void setVampire(bool vampire) override;\n\n    /// Set a translation offset (in object root space) to apply to meshes when in first person mode.\n    void setFirstPersonOffset(const osg::Vec3f& offset);\n\n    void updatePtr(const MWWorld::Ptr& updated) override;\n\n    /// Get a list of body parts that may be used by an NPC of given race and gender.\n    /// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain nullptr body parts.\n    static const std::vector<const ESM::BodyPart*>& getBodyParts(const std::string& raceId, bool female, bool firstperson, bool werewolf);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/objectpaging.cpp",
    "content": "#include \"objectpaging.hpp\"\n\n#include <unordered_map>\n\n#include <osg/Version>\n#include <osg/LOD>\n#include <osg/Switch>\n#include <osg/MatrixTransform>\n#include <osg/Material>\n#include <osgUtil/IncrementalCompileOperation>\n\n#include <components/esm/esmreader.hpp>\n#include <components/misc/resourcehelpers.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/sceneutil/optimizer.hpp>\n#include <components/sceneutil/clone.hpp>\n#include <components/sceneutil/util.hpp>\n#include <components/vfs/manager.hpp>\n\n#include <osgParticle/ParticleProcessor>\n#include <osgParticle/ParticleSystemUpdater>\n\n#include <components/sceneutil/lightmanager.hpp>\n#include <components/sceneutil/morphgeometry.hpp>\n#include <components/sceneutil/riggeometry.hpp>\n#include <components/settings/settings.hpp>\n#include <components/misc/rng.hpp>\n\n#include \"apps/openmw/mwworld/esmstore.hpp\"\n#include \"apps/openmw/mwbase/environment.hpp\"\n#include \"apps/openmw/mwbase/world.hpp\"\n\n#include \"vismask.hpp\"\n\nnamespace MWRender\n{\n\n    bool typeFilter(int type, bool far)\n    {\n        switch (type)\n        {\n          case ESM::REC_STAT:\n          case ESM::REC_ACTI:\n          case ESM::REC_DOOR:\n            return true;\n          case ESM::REC_CONT:\n            return !far;\n\n        default:\n            return false;\n        }\n    }\n\n    std::string getModel(int type, const std::string& id, const MWWorld::ESMStore& store)\n    {\n        switch (type)\n        {\n          case ESM::REC_STAT:\n            return store.get<ESM::Static>().searchStatic(id)->mModel;\n          case ESM::REC_ACTI:\n            return store.get<ESM::Activator>().searchStatic(id)->mModel;\n          case ESM::REC_DOOR:\n            return store.get<ESM::Door>().searchStatic(id)->mModel;\n          case ESM::REC_CONT:\n            return store.get<ESM::Container>().searchStatic(id)->mModel;\n          default:\n            return std::string();\n        }\n    }\n\n    osg::ref_ptr<osg::Node> ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)\n    {\n        if (activeGrid && !mActiveGrid)\n            return nullptr;\n\n        ChunkId id = std::make_tuple(center, size, activeGrid);\n\n        osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);\n        if (obj)\n            return obj->asNode();\n        else\n        {\n            osg::ref_ptr<osg::Node> node = createChunk(size, center, activeGrid, viewPoint, compile);\n            mCache->addEntryToObjectCache(id, node.get());\n            return node;\n        }\n    }\n\n    class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback\n    {\n    public:\n        bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const override\n        {\n            return true;\n        }\n        bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const override\n        {\n            return (node->getDataVariance() != osg::Object::DYNAMIC);\n        }\n    };\n\n    class CopyOp : public osg::CopyOp\n    {\n    public:\n        bool mOptimizeBillboards = true;\n        float mSqrDistance = 0.f;\n        osg::Vec3f mViewVector;\n        osg::Node::NodeMask mCopyMask = ~0u;\n        mutable std::vector<const osg::Node*> mNodePath;\n\n        void copy(const osg::Node* toCopy, osg::Group* attachTo)\n        {\n            const osg::Group* groupToCopy = toCopy->asGroup();\n            if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy)\n                attachTo->addChild(operator()(toCopy));\n            else\n            {\n                for (unsigned int i=0; i<groupToCopy->getNumChildren(); ++i)\n                    attachTo->addChild(operator()(groupToCopy->getChild(i)));\n            }\n        }\n\n        osg::Node* operator() (const osg::Node* node) const override\n        {\n            if (!(node->getNodeMask() & mCopyMask))\n                return nullptr;\n\n            if (const osg::Drawable* d = node->asDrawable())\n                return operator()(d);\n\n            if (dynamic_cast<const osgParticle::ParticleProcessor*>(node))\n                return nullptr;\n            if (dynamic_cast<const osgParticle::ParticleSystemUpdater*>(node))\n                return nullptr;\n\n            if (const osg::Switch* sw = node->asSwitch())\n            {\n                osg::Group* n = new osg::Group;\n                for (unsigned int i=0; i<sw->getNumChildren(); ++i)\n                    if (sw->getValue(i))\n                        n->addChild(operator()(sw->getChild(i)));\n                n->setDataVariance(osg::Object::STATIC);\n                return n;\n            }\n            if (const osg::LOD* lod = dynamic_cast<const osg::LOD*>(node))\n            {\n                osg::Group* n = new osg::Group;\n                for (unsigned int i=0; i<lod->getNumChildren(); ++i)\n                    if (lod->getMinRange(i) * lod->getMinRange(i) <= mSqrDistance && mSqrDistance < lod->getMaxRange(i) * lod->getMaxRange(i))\n                        n->addChild(operator()(lod->getChild(i)));\n                n->setDataVariance(osg::Object::STATIC);\n                return n;\n            }\n\n            mNodePath.push_back(node);\n\n            osg::Node* cloned = static_cast<osg::Node*>(node->clone(*this));\n            cloned->setDataVariance(osg::Object::STATIC);\n            cloned->setUserDataContainer(nullptr);\n            cloned->setName(\"\");\n\n            mNodePath.pop_back();\n\n            handleCallbacks(node, cloned);\n\n            return cloned;\n        }\n        void handleCallbacks(const osg::Node* node, osg::Node *cloned) const\n        {\n            for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr; callback = callback->getNestedCallback())\n            {\n                if (callback->className() == std::string(\"BillboardCallback\"))\n                {\n                    if (mOptimizeBillboards)\n                    {\n                        handleBillboard(cloned);\n                        continue;\n                    }\n                    else\n                        cloned->setDataVariance(osg::Object::DYNAMIC);\n                }\n\n                if (node->getCullCallback()->getNestedCallback())\n                {\n                    osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY);\n                    clonedCallback->setNestedCallback(nullptr);\n                    cloned->addCullCallback(clonedCallback);\n                }\n                else\n                    cloned->addCullCallback(const_cast<osg::Callback*>(callback));\n            }\n        }\n        void handleBillboard(osg::Node* node) const\n        {\n            osg::Transform* transform = node->asTransform();\n            if (!transform) return;\n            osg::MatrixTransform* matrixTransform = transform->asMatrixTransform();\n            if (!matrixTransform) return;\n\n            osg::Matrix worldToLocal = osg::Matrix::identity();\n            for (auto pathNode : mNodePath)\n                if (const osg::Transform* t = pathNode->asTransform())\n                    t->computeWorldToLocalMatrix(worldToLocal, nullptr);\n            worldToLocal = osg::Matrix::orthoNormal(worldToLocal);\n\n            osg::Matrix billboardMatrix;\n            osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans());\n            viewVector.normalize();\n            osg::Vec3f right = viewVector ^ osg::Vec3f(0,0,1);\n            right.normalize();\n            osg::Vec3f up = right ^ viewVector;\n            up.normalize();\n            billboardMatrix.makeLookAt(osg::Vec3f(0,0,0), viewVector, up);\n            billboardMatrix.invert(billboardMatrix);\n\n            const osg::Matrix& oldMatrix = matrixTransform->getMatrix();\n            float mag[3]; // attempt to preserve scale\n            for (int i=0;i<3;++i)\n                mag[i] = std::sqrt(oldMatrix(0,i) * oldMatrix(0,i) + oldMatrix(1,i) * oldMatrix(1,i) + oldMatrix(2,i) * oldMatrix(2,i));\n            osg::Matrix newMatrix;\n            worldToLocal.setTrans(0,0,0);\n            newMatrix *= worldToLocal;\n            newMatrix.preMult(billboardMatrix);\n            newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2]));\n            newMatrix.setTrans(oldMatrix.getTrans());\n\n            matrixTransform->setMatrix(newMatrix);\n        }\n        osg::Drawable* operator() (const osg::Drawable* drawable) const override\n        {\n            if (!(drawable->getNodeMask() & mCopyMask))\n                return nullptr;\n\n            if (dynamic_cast<const osgParticle::ParticleSystem*>(drawable))\n                return nullptr;\n\n            if (const SceneUtil::RigGeometry* rig = dynamic_cast<const SceneUtil::RigGeometry*>(drawable))\n                return operator()(rig->getSourceGeometry());\n            if (const SceneUtil::MorphGeometry* morph = dynamic_cast<const SceneUtil::MorphGeometry*>(drawable))\n                return operator()(morph->getSourceGeometry());\n\n            if (getCopyFlags() & DEEP_COPY_DRAWABLES)\n            {\n                osg::Drawable* d = static_cast<osg::Drawable*>(drawable->clone(*this));\n                d->setDataVariance(osg::Object::STATIC);\n                d->setUserDataContainer(nullptr);\n                d->setName(\"\");\n                return d;\n            }\n            else\n                return const_cast<osg::Drawable*>(drawable);\n        }\n        osg::Callback* operator() (const osg::Callback* callback) const override\n        {\n            return nullptr;\n        }\n    };\n\n    class RefnumSet : public osg::Object\n    {\n    public:\n        RefnumSet(){}\n        RefnumSet(const RefnumSet& copy, const osg::CopyOp&) : mRefnums(copy.mRefnums) {}\n        META_Object(MWRender, RefnumSet)\n        std::set<ESM::RefNum> mRefnums;\n    };\n\n    class AnalyzeVisitor : public osg::NodeVisitor\n    {\n    public:\n        AnalyzeVisitor(osg::Node::NodeMask analyzeMask)\n         : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n         , mCurrentStateSet(nullptr)\n         , mCurrentDistance(0.f)\n         , mAnalyzeMask(analyzeMask) {}\n\n        typedef std::unordered_map<osg::StateSet*, unsigned int> StateSetCounter;\n        struct Result\n        {\n            StateSetCounter mStateSetCounter;\n            unsigned int mNumVerts = 0;\n        };\n\n        void apply(osg::Node& node) override\n        {\n            if (!(node.getNodeMask() & mAnalyzeMask))\n                return;\n\n            if (node.getStateSet())\n                mCurrentStateSet = node.getStateSet();\n\n            if (osg::Switch* sw = node.asSwitch())\n            {\n                for (unsigned int i=0; i<sw->getNumChildren(); ++i)\n                    if (sw->getValue(i))\n                        traverse(*sw->getChild(i));\n                return;\n            }\n            if (osg::LOD* lod = dynamic_cast<osg::LOD*>(&node))\n            {\n                for (unsigned int i=0; i<lod->getNumChildren(); ++i)\n                    if (lod->getMinRange(i) * lod->getMinRange(i) <= mCurrentDistance && mCurrentDistance < lod->getMaxRange(i) * lod->getMaxRange(i))\n                        traverse(*lod->getChild(i));\n                return;\n            }\n\n            traverse(node);\n        }\n        void apply(osg::Geometry& geom) override\n        {\n            if (!(geom.getNodeMask() & mAnalyzeMask))\n                return;\n\n            if (osg::Array* array = geom.getVertexArray())\n                mResult.mNumVerts += array->getNumElements();\n\n            ++mResult.mStateSetCounter[mCurrentStateSet];\n            ++mGlobalStateSetCounter[mCurrentStateSet];\n        }\n        Result retrieveResult()\n        {\n            Result result = mResult;\n            mResult = Result();\n            mCurrentStateSet = nullptr;\n            return result;\n        }\n        void addInstance(const Result& result)\n        {\n            for (auto pair : result.mStateSetCounter)\n                mGlobalStateSetCounter[pair.first] += pair.second;\n        }\n        float getMergeBenefit(const Result& result)\n        {\n            if (result.mStateSetCounter.empty()) return 1;\n            float mergeBenefit = 0;\n            for (auto pair : result.mStateSetCounter)\n            {\n                mergeBenefit += mGlobalStateSetCounter[pair.first];\n            }\n            mergeBenefit /= result.mStateSetCounter.size();\n            return mergeBenefit;\n        }\n\n        Result mResult;\n        osg::StateSet* mCurrentStateSet;\n        StateSetCounter mGlobalStateSetCounter;\n        float mCurrentDistance;\n        osg::Node::NodeMask mAnalyzeMask;\n    };\n\n    class DebugVisitor : public osg::NodeVisitor\n    {\n    public:\n        DebugVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}\n        void apply(osg::Drawable& node) override\n        {\n            osg::ref_ptr<osg::Material> m (new osg::Material);\n            osg::Vec4f color(Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f);\n            color.normalize();\n            m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f));\n            m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f));\n            m->setColorMode(osg::Material::OFF);\n            m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color));\n            osg::ref_ptr<osg::StateSet> stateset = node.getStateSet() ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) : new osg::StateSet;\n            stateset->setAttribute(m);\n            stateset->addUniform(new osg::Uniform(\"colorMode\", 0));\n            stateset->addUniform(new osg::Uniform(\"emissiveMult\", 1.f));\n            node.setStateSet(stateset);\n        }\n    };\n\n    class AddRefnumMarkerVisitor : public osg::NodeVisitor\n    {\n    public:\n        AddRefnumMarkerVisitor(const ESM::RefNum &refnum) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mRefnum(refnum) {}\n        ESM::RefNum mRefnum;\n        void apply(osg::Geometry &node) override\n        {\n            osg::ref_ptr<RefnumMarker> marker (new RefnumMarker);\n            marker->mRefnum = mRefnum;\n            if (osg::Array* array = node.getVertexArray())\n                marker->mNumVertices = array->getNumElements();\n            node.getOrCreateUserDataContainer()->addUserObject(marker);\n        }\n    };\n\n    ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager)\n            : GenericResourceManager<ChunkId>(nullptr)\n         , mSceneManager(sceneManager)\n         , mRefTrackerLocked(false)\n    {\n        mActiveGrid = Settings::Manager::getBool(\"object paging active grid\", \"Terrain\");\n        mDebugBatches = Settings::Manager::getBool(\"object paging debug batches\", \"Terrain\");\n        mMergeFactor = Settings::Manager::getFloat(\"object paging merge factor\", \"Terrain\");\n        mMinSize = Settings::Manager::getFloat(\"object paging min size\", \"Terrain\");\n        mMinSizeMergeFactor = Settings::Manager::getFloat(\"object paging min size merge factor\", \"Terrain\");\n        mMinSizeCostMultiplier = Settings::Manager::getFloat(\"object paging min size cost multiplier\", \"Terrain\");\n    }\n\n    osg::ref_ptr<osg::Node> ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)\n    {\n        osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f));\n\n        osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE;\n        osg::Vec3f relativeViewPoint = viewPoint - worldCenter;\n\n        std::map<ESM::RefNum, ESM::CellRef> refs;\n        std::vector<ESM::ESMReader> esm;\n        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n\n        for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX)\n        {\n            for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY)\n            {\n                const ESM::Cell* cell = store.get<ESM::Cell>().searchStatic(cellX, cellY);\n                if (!cell) continue;\n                for (size_t i=0; i<cell->mContextList.size(); ++i)\n                {\n                    try\n                    {\n                        unsigned int index = cell->mContextList[i].index;\n                        if (esm.size()<=index)\n                            esm.resize(index+1);\n                        cell->restore(esm[index], i);\n                        ESM::CellRef ref;\n                        ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile;\n                        bool deleted = false;\n                        while(cell->getNextRef(esm[index], ref, deleted))\n                        {\n                            if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue;\n                            Misc::StringUtils::lowerCaseInPlace(ref.mRefID);\n                            int type = store.findStatic(ref.mRefID);\n                            if (!typeFilter(type,size>=2)) continue;\n                            if (deleted) { refs.erase(ref.mRefNum); continue; }\n                            if (ref.mRefNum.fromGroundcoverFile()) continue;\n                            refs[ref.mRefNum] = std::move(ref);\n                        }\n                    }\n                    catch (std::exception&)\n                    {\n                        continue;\n                    }\n                }\n                for (auto [ref, deleted] : cell->mLeasedRefs)\n                {\n                    if (deleted) { refs.erase(ref.mRefNum); continue; }\n                    Misc::StringUtils::lowerCaseInPlace(ref.mRefID);\n                    int type = store.findStatic(ref.mRefID);\n                    if (!typeFilter(type,size>=2)) continue;\n                    refs[ref.mRefNum] = std::move(ref);\n                }\n            }\n        }\n\n        if (activeGrid)\n        {\n            std::lock_guard<std::mutex> lock(mRefTrackerMutex);\n            for (auto ref : getRefTracker().mBlacklist)\n                refs.erase(ref);\n        }\n\n        osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f));\n        osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f));\n        struct InstanceList\n        {\n            std::vector<const ESM::CellRef*> mInstances;\n            AnalyzeVisitor::Result mAnalyzeResult;\n            bool mNeedCompile = false;\n        };\n        typedef std::map<osg::ref_ptr<const osg::Node>, InstanceList> NodeMap;\n        NodeMap nodes;\n        osg::ref_ptr<RefnumSet> refnumSet = activeGrid ? new RefnumSet : nullptr;\n\n        // Mask_UpdateVisitor is used in such cases in NIF loader:\n        // 1. For collision nodes, which is not supposed to be rendered.\n        // 2. For nodes masked via Flag_Hidden (VisController can change this flag value at runtime).\n        // Since ObjectPaging does not handle VisController, we can just ignore both types of nodes.\n        constexpr auto copyMask = ~Mask_UpdateVisitor;\n\n        AnalyzeVisitor analyzeVisitor(copyMask);\n        analyzeVisitor.mCurrentDistance = (viewPoint - worldCenter).length2();\n        float minSize = mMinSize;\n        if (mMinSizeMergeFactor)\n            minSize *= mMinSizeMergeFactor;\n        for (const auto& pair : refs)\n        {\n            const ESM::CellRef& ref = pair.second;\n\n            osg::Vec3f pos = ref.mPos.asVec3();\n            if (size < 1.f)\n            {\n                osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE;\n                if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())\n                 || (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y()))\n                    continue;\n            }\n\n            float dSqr = (viewPoint - pos).length2();\n            if (!activeGrid)\n            {\n                std::lock_guard<std::mutex> lock(mSizeCacheMutex);\n                SizeCache::iterator found = mSizeCache.find(pair.first);\n                if (found != mSizeCache.end() && found->second < dSqr*minSize*minSize)\n                    continue;\n            }\n\n            if (ref.mRefID == \"prisonmarker\" || ref.mRefID == \"divinemarker\" || ref.mRefID == \"templemarker\" || ref.mRefID == \"northmarker\")\n                continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player\n\n            int type = store.findStatic(ref.mRefID);\n            std::string model = getModel(type, ref.mRefID, store);\n            if (model.empty()) continue;\n            model = \"meshes/\" + model;\n\n            if (activeGrid && type != ESM::REC_STAT)\n            {\n                model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS());\n                std::string kfname = Misc::StringUtils::lowerCase(model);\n                if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, \".nif\") == 0)\n                {\n                    kfname.replace(kfname.size()-4, 4, \".kf\");\n                    if (mSceneManager->getVFS()->exists(kfname))\n                        continue;\n                }\n            }\n\n            osg::ref_ptr<const osg::Node> cnode = mSceneManager->getTemplate(model, false);\n\n            if (activeGrid)\n            {\n                if (cnode->getNumChildrenRequiringUpdateTraversal() > 0 || SceneUtil::hasUserDescription(cnode, Constants::NightDayLabel) || SceneUtil::hasUserDescription(cnode, Constants::HerbalismLabel))\n                    continue;\n                else\n                    refnumSet->mRefnums.insert(pair.first);\n            }\n\n            {\n                std::lock_guard<std::mutex> lock(mRefTrackerMutex);\n                if (getRefTracker().mDisabled.count(pair.first))\n                    continue;\n            }\n\n            float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale;\n            if (radius2 < dSqr*minSize*minSize && !activeGrid)\n            {\n                std::lock_guard<std::mutex> lock(mSizeCacheMutex);\n                mSizeCache[pair.first] = radius2;\n                continue;\n            }\n\n            auto emplaced = nodes.emplace(cnode, InstanceList());\n            if (emplaced.second)\n            {\n                const_cast<osg::Node*>(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor\n                emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult();\n                emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3;\n            }\n            else\n                analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult);\n            emplaced.first->second.mInstances.push_back(&ref);\n        }\n\n        osg::ref_ptr<osg::Group> group = new osg::Group;\n        osg::ref_ptr<osg::Group> mergeGroup = new osg::Group;\n        osg::ref_ptr<Resource::TemplateMultiRef> templateRefs = new Resource::TemplateMultiRef;\n        osgUtil::StateToCompile stateToCompile(0, nullptr);\n        CopyOp copyop;\n        copyop.mCopyMask = copyMask;\n        for (const auto& pair : nodes)\n        {\n            const osg::Node* cnode = pair.first;\n\n            const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult;\n\n            float mergeCost = analyzeResult.mNumVerts * size;\n            float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor;\n            bool merge = mergeBenefit > mergeCost;\n\n            float minSizeMerged = mMinSize;\n            float factor2 = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1;\n            float minSizeMergeFactor2 = (1-factor2) * mMinSizeMergeFactor + factor2;\n            if (minSizeMergeFactor2 > 0)\n                minSizeMerged *= minSizeMergeFactor2;\n\n            unsigned int numinstances = 0;\n            for (auto cref : pair.second.mInstances)\n            {\n                const ESM::CellRef& ref = *cref;\n                osg::Vec3f pos = ref.mPos.asVec3();\n\n                if (!activeGrid && minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged)\n                    continue;\n\n                osg::Matrixf matrix;\n                matrix.preMultTranslate(pos - worldCenter);\n                matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) *\n                                        osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) *\n                                        osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) );\n                matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale));\n                osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform(matrix);\n                trans->setDataVariance(osg::Object::STATIC);\n\n                copyop.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES);\n                copyop.mOptimizeBillboards = (size > 1/4.f);\n                copyop.mNodePath.push_back(trans);\n                copyop.mSqrDistance = (viewPoint - pos).length2();\n                copyop.mViewVector = (viewPoint - worldCenter);\n                copyop.copy(cnode, trans);\n                copyop.mNodePath.pop_back();\n\n                if (activeGrid)\n                {\n                    if (merge)\n                    {\n                        AddRefnumMarkerVisitor visitor(ref.mRefNum);\n                        trans->accept(visitor);\n                    }\n                    else\n                    {\n                        osg::ref_ptr<RefnumMarker> marker = new RefnumMarker; marker->mRefnum = ref.mRefNum;\n                        trans->getOrCreateUserDataContainer()->addUserObject(marker);\n                    }\n                }\n\n                osg::Group* attachTo = merge ? mergeGroup : group;\n                attachTo->addChild(trans);\n                ++numinstances;\n            }\n            if (numinstances > 0)\n            {\n                // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache\n                templateRefs->addRef(cnode);\n\n                if (pair.second.mNeedCompile)\n                {\n                    int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES;\n                    if (!merge)\n                        mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS;\n                    stateToCompile._mode = mode;\n                    const_cast<osg::Node*>(cnode)->accept(stateToCompile);\n                }\n            }\n        }\n\n        if (mergeGroup->getNumChildren())\n        {\n            SceneUtil::Optimizer optimizer;\n            if (size > 1/8.f)\n            {\n                optimizer.setViewPoint(relativeViewPoint);\n                optimizer.setMergeAlphaBlending(true);\n            }\n            optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);\n            unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY;\n            mSceneManager->shareState(mergeGroup);\n            optimizer.optimize(mergeGroup, options);\n\n            group->addChild(mergeGroup);\n\n            if (mDebugBatches)\n            {\n                DebugVisitor dv;\n                mergeGroup->accept(dv);\n            }\n            if (compile)\n            {\n                stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS;\n                mergeGroup->accept(stateToCompile);\n            }\n        }\n\n        auto ico = mSceneManager->getIncrementalCompileOperation();\n        if (!stateToCompile.empty() && ico)\n        {\n            auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(group);\n            compileSet->buildCompileMap(ico->getContextSet(), stateToCompile);\n            ico->add(compileSet, false);\n        }\n\n        group->getBound();\n        group->setNodeMask(Mask_Static);\n        osg::UserDataContainer* udc = group->getOrCreateUserDataContainer();\n        if (activeGrid)\n        {\n            udc->addUserObject(refnumSet);\n            group->addCullCallback(new SceneUtil::LightListCallback);\n        }\n        udc->addUserObject(templateRefs);\n\n        return group;\n    }\n\n    unsigned int ObjectPaging::getNodeMask()\n    {\n        return Mask_Static;\n    }\n\n    struct ClearCacheFunctor\n    {\n        void operator()(MWRender::ChunkId id, osg::Object* obj)\n        {\n            if (intersects(id, mPosition))\n                mToClear.insert(id);\n        }\n        bool intersects(ChunkId id, osg::Vec3f pos)\n        {\n            if (mActiveGridOnly && !std::get<2>(id)) return false;\n            pos /= ESM::Land::REAL_SIZE;\n            clampToCell(pos);\n            osg::Vec2f center = std::get<0>(id);\n            float halfSize = std::get<1>(id)/2;\n            return pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize;\n        }\n        void clampToCell(osg::Vec3f& cellPos)\n        {\n            osg::Vec2i min (mCell.x(), mCell.y());\n            osg::Vec2i max (mCell.x()+1, mCell.y()+1);\n            if (cellPos.x() < min.x()) cellPos.x() = min.x();\n            if (cellPos.x() > max.x()) cellPos.x() = max.x();\n            if (cellPos.y() < min.y()) cellPos.y() = min.y();\n            if (cellPos.y() > max.y()) cellPos.y() = max.y();\n        }\n        osg::Vec3f mPosition;\n        osg::Vec2i mCell;\n        std::set<MWRender::ChunkId> mToClear;\n        bool mActiveGridOnly = false;\n    };\n\n    bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, const osg::Vec2i& cell, bool enabled)\n    {\n        if (!typeFilter(type, false))\n            return false;\n\n        {\n            std::lock_guard<std::mutex> lock(mRefTrackerMutex);\n            if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false;\n            if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false;\n            if (mRefTrackerLocked) return false;\n        }\n\n        ClearCacheFunctor ccf;\n        ccf.mPosition = pos;\n        ccf.mCell = cell;\n        mCache->call(ccf);\n        if (ccf.mToClear.empty()) return false;\n        for (const auto& chunk : ccf.mToClear)\n            mCache->removeFromObjectCache(chunk);\n        return true;\n    }\n\n    bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, const osg::Vec2i& cell)\n    {\n        if (!typeFilter(type, false))\n            return false;\n\n        {\n            std::lock_guard<std::mutex> lock(mRefTrackerMutex);\n            if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false;\n            if (mRefTrackerLocked) return false;\n        }\n\n        ClearCacheFunctor ccf;\n        ccf.mPosition = pos;\n        ccf.mCell = cell;\n        ccf.mActiveGridOnly = true;\n        mCache->call(ccf);\n        if (ccf.mToClear.empty()) return false;\n        for (const auto& chunk : ccf.mToClear)\n            mCache->removeFromObjectCache(chunk);\n        return true;\n    }\n\n\n    void ObjectPaging::clear()\n    {\n        std::lock_guard<std::mutex> lock(mRefTrackerMutex);\n        mRefTrackerNew.mDisabled.clear();\n        mRefTrackerNew.mBlacklist.clear();\n        mRefTrackerLocked = true;\n    }\n\n    bool ObjectPaging::unlockCache()\n    {\n        if (!mRefTrackerLocked) return false;\n        {\n            std::lock_guard<std::mutex> lock(mRefTrackerMutex);\n            mRefTrackerLocked = false;\n            if (mRefTracker == mRefTrackerNew)\n                return false;\n            else\n                mRefTracker = mRefTrackerNew;\n        }\n        mCache->clear();\n        return true;\n    }\n\n    struct GetRefnumsFunctor\n    {\n        GetRefnumsFunctor(std::set<ESM::RefNum>& output) : mOutput(output) {}\n        void operator()(MWRender::ChunkId chunkId, osg::Object* obj)\n        {\n            if (!std::get<2>(chunkId)) return;\n            const osg::Vec2f& center = std::get<0>(chunkId);\n            bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y() || center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w());\n            if (!activeGrid) return;\n\n            osg::UserDataContainer* udc = obj->getUserDataContainer();\n            if (udc && udc->getNumUserObjects())\n            {\n                RefnumSet* refnums = dynamic_cast<RefnumSet*>(udc->getUserObject(0));\n                if (!refnums) return;\n                mOutput.insert(refnums->mRefnums.begin(), refnums->mRefnums.end());\n            }\n        }\n        osg::Vec4i mActiveGrid;\n        std::set<ESM::RefNum>& mOutput;\n    };\n\n    void ObjectPaging::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out)\n    {\n        GetRefnumsFunctor grf(out);\n        grf.mActiveGrid = activeGrid;\n        mCache->call(grf);\n    }\n\n    void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n    {\n        stats->setAttribute(frameNumber, \"Object Chunk\", mCache->getCacheSize());\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/objectpaging.hpp",
    "content": "#ifndef OPENMW_MWRENDER_OBJECTPAGING_H\n#define OPENMW_MWRENDER_OBJECTPAGING_H\n\n#include <components/terrain/quadtreeworld.hpp>\n#include <components/resource/resourcemanager.hpp>\n#include <components/esm/loadcell.hpp>\n\n#include <mutex>\n\nnamespace Resource\n{\n    class SceneManager;\n}\nnamespace MWWorld\n{\n    class ESMStore;\n}\n\nnamespace MWRender\n{\n\n    typedef std::tuple<osg::Vec2f, float, bool> ChunkId; // Center, Size, ActiveGrid\n\n    class ObjectPaging : public Resource::GenericResourceManager<ChunkId>, public Terrain::QuadTreeWorld::ChunkManager\n    {\n    public:\n        ObjectPaging(Resource::SceneManager* sceneManager);\n        ~ObjectPaging() = default;\n\n        osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;\n\n        osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile);\n\n        unsigned int getNodeMask() override;\n\n        /// @return true if view needs rebuild\n        bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, const osg::Vec2i& cell, bool enabled);\n\n        /// @return true if view needs rebuild\n        bool blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, const osg::Vec2i& cell);\n\n        void clear();\n\n        /// Must be called after clear() before rendering starts.\n        /// @return true if view needs rebuild\n        bool unlockCache();\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;\n\n        void getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out);\n\n    private:\n        Resource::SceneManager* mSceneManager;\n        bool mActiveGrid;\n        bool mDebugBatches;\n        float mMergeFactor;\n        float mMinSize;\n        float mMinSizeMergeFactor;\n        float mMinSizeCostMultiplier;\n\n        std::mutex mRefTrackerMutex;\n        struct RefTracker\n        {\n            std::set<ESM::RefNum> mDisabled;\n            std::set<ESM::RefNum> mBlacklist;\n            bool operator==(const RefTracker&other) const { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; }\n        };\n        RefTracker mRefTracker;\n        RefTracker mRefTrackerNew;\n        bool mRefTrackerLocked;\n\n        const RefTracker& getRefTracker() const { return mRefTracker; }\n        RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; }\n\n        std::mutex mSizeCacheMutex;\n        typedef std::map<ESM::RefNum, float> SizeCache;\n        SizeCache mSizeCache;\n    };\n\n    class RefnumMarker : public osg::Object\n    {\n    public:\n        RefnumMarker() : mNumVertices(0) { mRefnum.unset(); }\n        RefnumMarker(const RefnumMarker &copy, osg::CopyOp co) : mRefnum(copy.mRefnum), mNumVertices(copy.mNumVertices) {}\n        META_Object(MWRender, RefnumMarker)\n\n        ESM::RefNum mRefnum;\n        unsigned int mNumVertices;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/objects.cpp",
    "content": "#include \"objects.hpp\"\n\n#include <osg/Group>\n#include <osg/UserDataContainer>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/sceneutil/unrefqueue.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"animation.hpp\"\n#include \"npcanimation.hpp\"\n#include \"creatureanimation.hpp\"\n#include \"vismask.hpp\"\n\n\nnamespace MWRender\n{\n\nObjects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> rootNode, SceneUtil::UnrefQueue* unrefQueue)\n    : mRootNode(rootNode)\n    , mResourceSystem(resourceSystem)\n    , mUnrefQueue(unrefQueue)\n{\n}\n\nObjects::~Objects()\n{\n    mObjects.clear();\n\n    for (CellMap::iterator iter = mCellSceneNodes.begin(); iter != mCellSceneNodes.end(); ++iter)\n        iter->second->getParent(0)->removeChild(iter->second);\n    mCellSceneNodes.clear();\n}\n\nvoid Objects::insertBegin(const MWWorld::Ptr& ptr)\n{\n    assert(mObjects.find(ptr) == mObjects.end());\n\n    osg::ref_ptr<osg::Group> cellnode;\n\n    CellMap::iterator found = mCellSceneNodes.find(ptr.getCell());\n    if (found == mCellSceneNodes.end())\n    {\n        cellnode = new osg::Group;\n        cellnode->setName(\"Cell Root\");\n        mRootNode->addChild(cellnode);\n        mCellSceneNodes[ptr.getCell()] = cellnode;\n    }\n    else\n        cellnode = found->second;\n\n    osg::ref_ptr<SceneUtil::PositionAttitudeTransform> insert (new SceneUtil::PositionAttitudeTransform);\n    cellnode->addChild(insert);\n\n    insert->getOrCreateUserDataContainer()->addUserObject(new PtrHolder(ptr));\n\n    const float *f = ptr.getRefData().getPosition().pos;\n\n    insert->setPosition(osg::Vec3(f[0], f[1], f[2]));\n\n    const float scale = ptr.getCellRef().getScale();\n    osg::Vec3f scaleVec(scale, scale, scale);\n    ptr.getClass().adjustScale(ptr, scaleVec, true);\n    insert->setScale(scaleVec);\n\n    ptr.getRefData().setBaseNode(insert);\n}\n\nvoid Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool animated, bool allowLight)\n{\n    insertBegin(ptr);\n    ptr.getRefData().getBaseNode()->setNodeMask(Mask_Object);\n\n    osg::ref_ptr<ObjectAnimation> anim (new ObjectAnimation(ptr, mesh, mResourceSystem, animated, allowLight));\n\n    mObjects.insert(std::make_pair(ptr, anim));\n}\n\nvoid Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, bool weaponsShields)\n{\n    insertBegin(ptr);\n    ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor);\n\n    // CreatureAnimation\n    osg::ref_ptr<Animation> anim;\n\n    if (weaponsShields)\n        anim = new CreatureWeaponAnimation(ptr, mesh, mResourceSystem);\n    else\n        anim = new CreatureAnimation(ptr, mesh, mResourceSystem);\n\n    if (mObjects.insert(std::make_pair(ptr, anim)).second)\n        ptr.getClass().getContainerStore(ptr).setContListener(static_cast<ActorAnimation*>(anim.get()));\n}\n\nvoid Objects::insertNPC(const MWWorld::Ptr &ptr)\n{\n    insertBegin(ptr);\n    ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor);\n\n    osg::ref_ptr<NpcAnimation> anim (new NpcAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), mResourceSystem));\n\n    if (mObjects.insert(std::make_pair(ptr, anim)).second)\n    {\n        ptr.getClass().getInventoryStore(ptr).setInvListener(anim.get(), ptr);\n        ptr.getClass().getInventoryStore(ptr).setContListener(anim.get());\n    }\n}\n\nbool Objects::removeObject (const MWWorld::Ptr& ptr)\n{\n    if(!ptr.getRefData().getBaseNode())\n        return true;\n\n    PtrAnimationMap::iterator iter = mObjects.find(ptr);\n    if(iter != mObjects.end())\n    {\n        if (mUnrefQueue.get())\n            mUnrefQueue->push(iter->second);\n\n        mObjects.erase(iter);\n\n        if (ptr.getClass().isActor())\n        {\n            if (ptr.getClass().hasInventoryStore(ptr))\n                ptr.getClass().getInventoryStore(ptr).setInvListener(nullptr, ptr);\n\n            ptr.getClass().getContainerStore(ptr).setContListener(nullptr);\n        }\n\n        ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode());\n\n        ptr.getRefData().setBaseNode(nullptr);\n        return true;\n    }\n    return false;\n}\n\n\nvoid Objects::removeCell(const MWWorld::CellStore* store)\n{\n    for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();)\n    {\n        MWWorld::Ptr ptr = iter->second->getPtr();\n        if(ptr.getCell() == store)\n        {\n            if (mUnrefQueue.get())\n                mUnrefQueue->push(iter->second);\n\n            if (ptr.getClass().isNpc() && ptr.getRefData().getCustomData())\n            {\n                MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr);\n                invStore.setInvListener(nullptr, ptr);\n                invStore.setContListener(nullptr);\n            }\n\n            mObjects.erase(iter++);\n        }\n        else\n            ++iter;\n    }\n\n    CellMap::iterator cell = mCellSceneNodes.find(store);\n    if(cell != mCellSceneNodes.end())\n    {\n        cell->second->getParent(0)->removeChild(cell->second);\n        if (mUnrefQueue.get())\n            mUnrefQueue->push(cell->second);\n        mCellSceneNodes.erase(cell);\n    }\n}\n\nvoid Objects::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)\n{\n    osg::Node* objectNode = cur.getRefData().getBaseNode();\n    if (!objectNode)\n        return;\n\n    MWWorld::CellStore *newCell = cur.getCell();\n\n    osg::Group* cellnode;\n    if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) {\n        cellnode = new osg::Group;\n        mRootNode->addChild(cellnode);\n        mCellSceneNodes[newCell] = cellnode;\n    } else {\n        cellnode = mCellSceneNodes[newCell];\n    }\n\n    osg::UserDataContainer* userDataContainer = objectNode->getUserDataContainer();\n    if (userDataContainer)\n        for (unsigned int i=0; i<userDataContainer->getNumUserObjects(); ++i)\n        {\n            if (dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i)))\n                userDataContainer->setUserObject(i, new PtrHolder(cur));\n        }\n\n    if (objectNode->getNumParents())\n        objectNode->getParent(0)->removeChild(objectNode);\n    cellnode->addChild(objectNode);\n\n    PtrAnimationMap::iterator iter = mObjects.find(old);\n    if(iter != mObjects.end())\n    {\n        osg::ref_ptr<Animation> anim = iter->second;\n        mObjects.erase(iter);\n        anim->updatePtr(cur);\n        mObjects[cur] = anim;\n    }\n}\n\nAnimation* Objects::getAnimation(const MWWorld::Ptr &ptr)\n{\n    PtrAnimationMap::const_iterator iter = mObjects.find(ptr);\n    if(iter != mObjects.end())\n        return iter->second;\n\n    return nullptr;\n}\n\nconst Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const\n{\n    PtrAnimationMap::const_iterator iter = mObjects.find(ptr);\n    if(iter != mObjects.end())\n        return iter->second;\n\n    return nullptr;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/objects.hpp",
    "content": "#ifndef GAME_RENDER_OBJECTS_H\n#define GAME_RENDER_OBJECTS_H\n\n#include <map>\n#include <string>\n\n#include <osg/ref_ptr>\n#include <osg/Object>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWWorld\n{\n    class CellStore;\n}\n\nnamespace SceneUtil\n{\n    class UnrefQueue;\n}\n\nnamespace MWRender{\n\nclass Animation;\n\nclass PtrHolder : public osg::Object\n{\npublic:\n    PtrHolder(const MWWorld::Ptr& ptr)\n        : mPtr(ptr)\n    {\n    }\n\n    PtrHolder()\n    {\n    }\n\n    PtrHolder(const PtrHolder& copy, const osg::CopyOp& copyop)\n        : mPtr(copy.mPtr)\n    {\n    }\n\n    META_Object(MWRender, PtrHolder)\n\n    MWWorld::Ptr mPtr;\n};\n\nclass Objects{\n    typedef std::map<MWWorld::ConstPtr,osg::ref_ptr<Animation> > PtrAnimationMap;\n\n    typedef std::map<const MWWorld::CellStore*, osg::ref_ptr<osg::Group> > CellMap;\n    CellMap mCellSceneNodes;\n    PtrAnimationMap mObjects;\n\n    osg::ref_ptr<osg::Group> mRootNode;\n\n    Resource::ResourceSystem* mResourceSystem;\n\n    osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;\n\n    void insertBegin(const MWWorld::Ptr& ptr);\n\npublic:\n    Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> rootNode, SceneUtil::UnrefQueue* unrefQueue);\n    ~Objects();\n\n    /// @param animated Attempt to load separate keyframes from a .kf file matching the model file?\n    /// @param allowLight If false, no lights will be created, and particles systems will be removed.\n    void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool animated=false, bool allowLight=true);\n\n    void insertNPC(const MWWorld::Ptr& ptr);\n    void insertCreature (const MWWorld::Ptr& ptr, const std::string& model, bool weaponsShields);\n\n    Animation* getAnimation(const MWWorld::Ptr &ptr);\n    const Animation* getAnimation(const MWWorld::ConstPtr &ptr) const;\n\n    bool removeObject (const MWWorld::Ptr& ptr);\n    ///< \\return found?\n\n    void removeCell(const MWWorld::CellStore* store);\n\n    /// Updates containing cell for object rendering data\n    void updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);\n\nprivate:\n    void operator = (const Objects&);\n    Objects(const Objects&);\n};\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/pathgrid.cpp",
    "content": "#include \"pathgrid.hpp\"\n\n#include <cassert>\n\n#include <osg/Geometry>\n#include <osg/PositionAttitudeTransform>\n#include <osg/Group>\n\n#include <components/esm/loadpgrd.hpp>\n#include <components/sceneutil/pathgridutil.hpp>\n#include <components/misc/coordinateconverter.hpp>\n\n#include \"../mwbase/world.hpp\" // these includes can be removed once the static-hack is gone\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwmechanics/pathfinding.hpp\"\n\n#include \"vismask.hpp\"\n\nnamespace MWRender\n{\n\nPathgrid::Pathgrid(osg::ref_ptr<osg::Group> root)\n    : mPathgridEnabled(false)\n    , mRootNode(root)\n    , mPathGridRoot(nullptr)\n    , mInteriorPathgridNode(nullptr)\n{\n}\n\nPathgrid::~Pathgrid()\n{\n    if (mPathgridEnabled)\n    {\n        togglePathgrid();\n    }\n}\n\n\nbool Pathgrid::toggleRenderMode (int mode){\n    switch (mode)\n    {\n        case Render_Pathgrid:\n            togglePathgrid();\n            return mPathgridEnabled;\n        default:\n            return false;\n    }\n\n    return false;\n}\n\nvoid Pathgrid::addCell(const MWWorld::CellStore *store)\n{\n    mActiveCells.push_back(store);\n    if (mPathgridEnabled)\n        enableCellPathgrid(store);\n}\n\nvoid Pathgrid::removeCell(const MWWorld::CellStore *store)\n{\n    mActiveCells.erase(std::remove(mActiveCells.begin(), mActiveCells.end(), store), mActiveCells.end());\n    if (mPathgridEnabled)\n        disableCellPathgrid(store);\n}\n\nvoid Pathgrid::togglePathgrid()\n{\n    mPathgridEnabled = !mPathgridEnabled;\n    if (mPathgridEnabled)\n    {\n        // add path grid meshes to already loaded cells\n        mPathGridRoot = new osg::Group;\n        mPathGridRoot->setNodeMask(Mask_Debug);\n        mRootNode->addChild(mPathGridRoot);\n\n        for(const MWWorld::CellStore* cell : mActiveCells)\n        {\n            enableCellPathgrid(cell);\n        }\n    }\n    else\n    {\n        // remove path grid meshes from already loaded cells\n        for(const MWWorld::CellStore* cell : mActiveCells)\n        {\n            disableCellPathgrid(cell);\n        }\n\n        if (mPathGridRoot)\n        {\n            mRootNode->removeChild(mPathGridRoot);\n            mPathGridRoot = nullptr;\n        }\n    }\n}\n\nvoid Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store)\n{\n    MWBase::World* world = MWBase::Environment::get().getWorld();\n    const ESM::Pathgrid *pathgrid =\n        world->getStore().get<ESM::Pathgrid>().search(*store->getCell());\n    if (!pathgrid) return;\n\n    osg::Vec3f cellPathGridPos(0, 0, 0);\n    Misc::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos);\n\n    osg::ref_ptr<osg::PositionAttitudeTransform> cellPathGrid = new osg::PositionAttitudeTransform;\n    cellPathGrid->setPosition(cellPathGridPos);\n\n    osg::ref_ptr<osg::Geometry> geometry = SceneUtil::createPathgridGeometry(*pathgrid);\n\n    cellPathGrid->addChild(geometry);\n\n    mPathGridRoot->addChild(cellPathGrid);\n\n    if (store->getCell()->isExterior())\n    {\n        mExteriorPathgridNodes[std::make_pair(store->getCell()->getGridX(), store->getCell()->getGridY())] = cellPathGrid;\n    }\n    else\n    {\n        assert(mInteriorPathgridNode == nullptr);\n        mInteriorPathgridNode = cellPathGrid;\n    }\n}\n\nvoid Pathgrid::disableCellPathgrid(const MWWorld::CellStore *store)\n{\n    if (store->getCell()->isExterior())\n    {\n        ExteriorPathgridNodes::iterator it =\n                mExteriorPathgridNodes.find(std::make_pair(store->getCell()->getGridX(), store->getCell()->getGridY()));\n        if (it != mExteriorPathgridNodes.end())\n        {\n            mPathGridRoot->removeChild(it->second);\n            mExteriorPathgridNodes.erase(it);\n        }\n    }\n    else\n    {\n        if (mInteriorPathgridNode)\n        {\n            mPathGridRoot->removeChild(mInteriorPathgridNode);\n            mInteriorPathgridNode = nullptr;\n        }\n    }\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/pathgrid.hpp",
    "content": "#ifndef GAME_RENDER_MWSCENE_H\n#define GAME_RENDER_MWSCENE_H\n\n#include <utility>\n\n#include <vector>\n#include <map>\n\n#include <osg/ref_ptr>\n\nnamespace ESM\n{\n    struct Pathgrid;\n}\n\nnamespace osg\n{\n    class Group;\n    class Geometry;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n    class CellStore;\n}\n\nnamespace MWRender\n{\n    class Pathgrid\n    {\n        bool mPathgridEnabled;\n\n        void togglePathgrid();\n\n        typedef std::vector<const MWWorld::CellStore *> CellList;\n        CellList mActiveCells;\n\n        osg::ref_ptr<osg::Group> mRootNode;\n\n        osg::ref_ptr<osg::Group> mPathGridRoot;\n\n        typedef std::map<std::pair<int,int>, osg::ref_ptr<osg::Group> > ExteriorPathgridNodes;\n        ExteriorPathgridNodes mExteriorPathgridNodes;\n        osg::ref_ptr<osg::Group> mInteriorPathgridNode;\n\n        void enableCellPathgrid(const MWWorld::CellStore *store);\n        void disableCellPathgrid(const MWWorld::CellStore *store);\n\n    public:\n        Pathgrid(osg::ref_ptr<osg::Group> root);\n        ~Pathgrid();\n        bool toggleRenderMode (int mode);\n\n        void addCell(const MWWorld::CellStore* store);\n        void removeCell(const MWWorld::CellStore* store);\n    };\n\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/recastmesh.cpp",
    "content": "#include \"recastmesh.hpp\"\n\n#include <components/sceneutil/recastmesh.hpp>\n\n#include <osg/PositionAttitudeTransform>\n\n#include \"vismask.hpp\"\n\nnamespace MWRender\n{\n    RecastMesh::RecastMesh(const osg::ref_ptr<osg::Group>& root, bool enabled)\n        : mRootNode(root)\n        , mEnabled(enabled)\n    {\n    }\n\n    RecastMesh::~RecastMesh()\n    {\n        if (mEnabled)\n            disable();\n    }\n\n    bool RecastMesh::toggle()\n    {\n        if (mEnabled)\n            disable();\n        else\n            enable();\n\n        return mEnabled;\n    }\n\n    void RecastMesh::update(const DetourNavigator::RecastMeshTiles& tiles, const DetourNavigator::Settings& settings)\n    {\n        if (!mEnabled)\n            return;\n\n        for (auto it = mGroups.begin(); it != mGroups.end();)\n        {\n            const auto tile = tiles.find(it->first);\n            if (tile == tiles.end())\n            {\n                mRootNode->removeChild(it->second.mValue);\n                it = mGroups.erase(it);\n                continue;\n            }\n\n            if (it->second.mGeneration != tile->second->getGeneration()\n                || it->second.mRevision != tile->second->getRevision())\n            {\n                const auto group = SceneUtil::createRecastMeshGroup(*tile->second, settings);\n                group->setNodeMask(Mask_Debug);\n                mRootNode->removeChild(it->second.mValue);\n                mRootNode->addChild(group);\n                it->second.mValue = group;\n                it->second.mGeneration = tile->second->getGeneration();\n                it->second.mRevision = tile->second->getRevision();\n                continue;\n            }\n\n            ++it;\n        }\n\n        for (const auto& tile : tiles)\n        {\n            if (mGroups.count(tile.first))\n                continue;\n            const auto group = SceneUtil::createRecastMeshGroup(*tile.second, settings);\n            group->setNodeMask(Mask_Debug);\n            mGroups.emplace(tile.first, Group {tile.second->getGeneration(), tile.second->getRevision(), group});\n            mRootNode->addChild(group);\n        }\n    }\n\n    void RecastMesh::reset()\n    {\n        std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->removeChild(v.second.mValue); });\n        mGroups.clear();\n    }\n\n    void RecastMesh::enable()\n    {\n        std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->addChild(v.second.mValue); });\n        mEnabled = true;\n    }\n\n    void RecastMesh::disable()\n    {\n        std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->removeChild(v.second.mValue); });\n        mEnabled = false;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/recastmesh.hpp",
    "content": "#ifndef OPENMW_MWRENDER_RECASTMESH_H\n#define OPENMW_MWRENDER_RECASTMESH_H\n\n#include <components/detournavigator/navigator.hpp>\n\n#include <osg/ref_ptr>\n\n#include <vector>\n\nnamespace osg\n{\n    class Group;\n    class Geometry;\n}\n\nnamespace MWRender\n{\n    class RecastMesh\n    {\n    public:\n        RecastMesh(const osg::ref_ptr<osg::Group>& root, bool enabled);\n        ~RecastMesh();\n\n        bool toggle();\n\n        void update(const DetourNavigator::RecastMeshTiles& recastMeshTiles, const DetourNavigator::Settings& settings);\n\n        void reset();\n\n        void enable();\n\n        void disable();\n\n        bool isEnabled() const\n        {\n            return mEnabled;\n        }\n\n    private:\n        struct Group\n        {\n            std::size_t mGeneration;\n            std::size_t mRevision;\n            osg::ref_ptr<osg::Group> mValue;\n        };\n\n        osg::ref_ptr<osg::Group> mRootNode;\n        bool mEnabled;\n        std::map<DetourNavigator::TilePosition, Group> mGroups;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/renderbin.hpp",
    "content": "#ifndef OPENMW_MWRENDER_RENDERBIN_H\n#define OPENMW_MWRENDER_RENDERBIN_H\n\nnamespace MWRender\n{\n\n    /// Defines the render bin numbers used in the OpenMW scene graph. The bin with the lowest number is rendered first.\n    enum RenderBins\n    {\n        RenderBin_Sky = -1,\n        RenderBin_Default = 0, // osg::StateSet::OPAQUE_BIN\n        RenderBin_Water = 9,\n        RenderBin_DepthSorted = 10, // osg::StateSet::TRANSPARENT_BIN\n        RenderBin_OcclusionQuery = 11,\n        RenderBin_FirstPerson = 12,\n        RenderBin_SunGlare = 13\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/renderinginterface.hpp",
    "content": "#ifndef GAME_RENDERING_INTERFACE_H\n#define GAME_RENDERING_INTERFACE_H\n\nnamespace MWRender\n{\n    class Objects;\n    class Actors;\n      \n    class RenderingInterface\n    {\n    public:\n        virtual MWRender::Objects& getObjects() = 0;\n        virtual ~RenderingInterface(){}\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/renderingmanager.cpp",
    "content": "#include \"renderingmanager.hpp\"\n\n#include <limits>\n#include <cstdlib>\n\n#include <osg/Light>\n#include <osg/LightModel>\n#include <osg/Fog>\n#include <osg/Material>\n#include <osg/PolygonMode>\n#include <osg/Group>\n#include <osg/UserDataContainer>\n#include <osg/ComputeBoundsVisitor>\n\n#include <osgUtil/LineSegmentIntersector>\n\n#include <osgViewer/Viewer>\n\n#include <components/nifosg/nifloader.hpp>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/imagemanager.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/keyframemanager.hpp>\n\n#include <components/shader/removedalphafunc.hpp>\n#include <components/shader/shadermanager.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include <components/sceneutil/util.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n#include <components/sceneutil/statesetupdater.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/sceneutil/workqueue.hpp>\n#include <components/sceneutil/unrefqueue.hpp>\n#include <components/sceneutil/writescene.hpp>\n#include <components/sceneutil/shadow.hpp>\n\n#include <components/terrain/terraingrid.hpp>\n#include <components/terrain/quadtreeworld.hpp>\n\n#include <components/esm/loadcell.hpp>\n\n#include <components/detournavigator/navigator.hpp>\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwgui/loadingscreen.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"sky.hpp\"\n#include \"effectmanager.hpp\"\n#include \"npcanimation.hpp\"\n#include \"vismask.hpp\"\n#include \"pathgrid.hpp\"\n#include \"camera.hpp\"\n#include \"viewovershoulder.hpp\"\n#include \"water.hpp\"\n#include \"terrainstorage.hpp\"\n#include \"navmesh.hpp\"\n#include \"actorspaths.hpp\"\n#include \"recastmesh.hpp\"\n#include \"fogmanager.hpp\"\n#include \"objectpaging.hpp\"\n#include \"screenshotmanager.hpp\"\n#include \"groundcover.hpp\"\n\nnamespace MWRender\n{\n\n    class StateUpdater : public SceneUtil::StateSetUpdater\n    {\n    public:\n        StateUpdater()\n            : mFogStart(0.f)\n            , mFogEnd(0.f)\n            , mWireframe(false)\n        {\n        }\n\n        void setDefaults(osg::StateSet *stateset) override\n        {\n            osg::LightModel* lightModel = new osg::LightModel;\n            stateset->setAttribute(lightModel, osg::StateAttribute::ON);\n            osg::Fog* fog = new osg::Fog;\n            fog->setMode(osg::Fog::LINEAR);\n            stateset->setAttributeAndModes(fog, osg::StateAttribute::ON);\n            if (mWireframe)\n            {\n                osg::PolygonMode* polygonmode = new osg::PolygonMode;\n                polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);\n                stateset->setAttributeAndModes(polygonmode, osg::StateAttribute::ON);\n            }\n            else\n                stateset->removeAttribute(osg::StateAttribute::POLYGONMODE);\n        }\n\n        void apply(osg::StateSet* stateset, osg::NodeVisitor*) override\n        {\n            osg::LightModel* lightModel = static_cast<osg::LightModel*>(stateset->getAttribute(osg::StateAttribute::LIGHTMODEL));\n            lightModel->setAmbientIntensity(mAmbientColor);\n            osg::Fog* fog = static_cast<osg::Fog*>(stateset->getAttribute(osg::StateAttribute::FOG));\n            fog->setColor(mFogColor);\n            fog->setStart(mFogStart);\n            fog->setEnd(mFogEnd);\n        }\n\n        void setAmbientColor(const osg::Vec4f& col)\n        {\n            mAmbientColor = col;\n        }\n\n        void setFogColor(const osg::Vec4f& col)\n        {\n            mFogColor = col;\n        }\n\n        void setFogStart(float start)\n        {\n            mFogStart = start;\n        }\n\n        void setFogEnd(float end)\n        {\n            mFogEnd = end;\n        }\n\n        void setWireframe(bool wireframe)\n        {\n            if (mWireframe != wireframe)\n            {\n                mWireframe = wireframe;\n                reset();\n            }\n        }\n\n        bool getWireframe() const\n        {\n            return mWireframe;\n        }\n\n    private:\n        osg::Vec4f mAmbientColor;\n        osg::Vec4f mFogColor;\n        float mFogStart;\n        float mFogEnd;\n        bool mWireframe;\n    };\n\n    class PreloadCommonAssetsWorkItem : public SceneUtil::WorkItem\n    {\n    public:\n        PreloadCommonAssetsWorkItem(Resource::ResourceSystem* resourceSystem)\n            : mResourceSystem(resourceSystem)\n        {\n        }\n\n        void doWork() override\n        {\n            try\n            {\n                for (std::vector<std::string>::const_iterator it = mModels.begin(); it != mModels.end(); ++it)\n                    mResourceSystem->getSceneManager()->cacheInstance(*it);\n                for (std::vector<std::string>::const_iterator it = mTextures.begin(); it != mTextures.end(); ++it)\n                    mResourceSystem->getImageManager()->getImage(*it);\n                for (std::vector<std::string>::const_iterator it = mKeyframes.begin(); it != mKeyframes.end(); ++it)\n                    mResourceSystem->getKeyframeManager()->get(*it);\n            }\n            catch (std::exception&)\n            {\n                // ignore error (will be shown when these are needed proper)\n            }\n        }\n\n        std::vector<std::string> mModels;\n        std::vector<std::string> mTextures;\n        std::vector<std::string> mKeyframes;\n\n    private:\n        Resource::ResourceSystem* mResourceSystem;\n    };\n\n    RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,\n                                       Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,\n                                       const std::string& resourcePath, DetourNavigator::Navigator& navigator)\n        : mViewer(viewer)\n        , mRootNode(rootNode)\n        , mResourceSystem(resourceSystem)\n        , mWorkQueue(workQueue)\n        , mUnrefQueue(new SceneUtil::UnrefQueue)\n        , mNavigator(navigator)\n        , mMinimumAmbientLuminance(0.f)\n        , mNightEyeFactor(0.f)\n        , mFieldOfViewOverridden(false)\n        , mFieldOfViewOverride(0.f)\n    {\n        auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString(\"lighting method\", \"Shaders\"));\n\n        resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);\n        resourceSystem->getSceneManager()->setShaderPath(resourcePath + \"/shaders\");\n        // Shadows and radial fog have problems with fixed-function mode\n        bool forceShaders = Settings::Manager::getBool(\"radial fog\", \"Shaders\")\n                            || Settings::Manager::getBool(\"force shaders\", \"Shaders\")\n                            || Settings::Manager::getBool(\"enable shadows\", \"Shadows\")\n                            || lightingMethod != SceneUtil::LightingMethod::FFP;\n        resourceSystem->getSceneManager()->setForceShaders(forceShaders);\n        // FIXME: calling dummy method because terrain needs to know whether lighting is clamped\n        resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool(\"clamp lighting\", \"Shaders\"));\n        resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool(\"auto use object normal maps\", \"Shaders\"));\n        resourceSystem->getSceneManager()->setNormalMapPattern(Settings::Manager::getString(\"normal map pattern\", \"Shaders\"));\n        resourceSystem->getSceneManager()->setNormalHeightMapPattern(Settings::Manager::getString(\"normal height map pattern\", \"Shaders\"));\n        resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool(\"auto use object specular maps\", \"Shaders\"));\n        resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString(\"specular map pattern\", \"Shaders\"));\n        resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool(\"apply lighting to environment maps\", \"Shaders\"));\n        resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool(\"antialias alpha test\", \"Shaders\") && Settings::Manager::getInt(\"antialiasing\", \"Video\") > 1);\n\n        // Let LightManager choose which backend to use based on our hint. For methods besides legacy lighting, this depends on support for various OpenGL extensions.\n        osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(lightingMethod == SceneUtil::LightingMethod::FFP);\n        resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());\n        resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());\n        resourceSystem->getSceneManager()->setSupportedLightingMethods(sceneRoot->getSupportedLightingMethods());\n        mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat(\"minimum interior brightness\", \"Shaders\"), 0.f, 1.f);\n\n        sceneRoot->setLightingMask(Mask_Lighting);\n        mSceneRoot = sceneRoot;\n        sceneRoot->setStartLight(1);\n        sceneRoot->setNodeMask(Mask_Scene);\n        sceneRoot->setName(\"Scene Root\");\n\n        int shadowCastingTraversalMask = Mask_Scene;\n        if (Settings::Manager::getBool(\"actor shadows\", \"Shadows\"))\n            shadowCastingTraversalMask |= Mask_Actor;\n        if (Settings::Manager::getBool(\"player shadows\", \"Shadows\"))\n            shadowCastingTraversalMask |= Mask_Player;\n        if (Settings::Manager::getBool(\"terrain shadows\", \"Shadows\"))\n            shadowCastingTraversalMask |= Mask_Terrain;\n\n        int indoorShadowCastingTraversalMask = shadowCastingTraversalMask;\n        if (Settings::Manager::getBool(\"object shadows\", \"Shadows\"))\n            shadowCastingTraversalMask |= (Mask_Object|Mask_Static);\n\n        mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager()));\n\n        Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines();\n        Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines();\n        Shader::ShaderManager::DefineMap globalDefines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();\n\n        for (auto itr = shadowDefines.begin(); itr != shadowDefines.end(); itr++)\n            globalDefines[itr->first] = itr->second;\n\n        globalDefines[\"forcePPL\"] = Settings::Manager::getBool(\"force per pixel lighting\", \"Shaders\") ? \"1\" : \"0\";\n        globalDefines[\"clamp\"] = Settings::Manager::getBool(\"clamp lighting\", \"Shaders\") ? \"1\" : \"0\";\n        globalDefines[\"preLightEnv\"] = Settings::Manager::getBool(\"apply lighting to environment maps\", \"Shaders\") ? \"1\" : \"0\";\n        globalDefines[\"radialFog\"] = Settings::Manager::getBool(\"radial fog\", \"Shaders\") ? \"1\" : \"0\";\n        globalDefines[\"useGPUShader4\"] = \"0\";\n\n        for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)\n            globalDefines[itr->first] = itr->second;\n\n        // Refactor this at some point - most shaders don't care about these defines\n        float groundcoverDistance = std::max(0.f, Settings::Manager::getFloat(\"rendering distance\", \"Groundcover\"));\n        globalDefines[\"groundcoverFadeStart\"] = std::to_string(groundcoverDistance * 0.9f);\n        globalDefines[\"groundcoverFadeEnd\"] = std::to_string(groundcoverDistance);\n        globalDefines[\"groundcoverStompMode\"] = std::to_string(std::clamp(Settings::Manager::getInt(\"stomp mode\", \"Groundcover\"), 0, 2));\n        globalDefines[\"groundcoverStompIntensity\"] = std::to_string(std::clamp(Settings::Manager::getInt(\"stomp intensity\", \"Groundcover\"), 0, 2));\n\n        // It is unnecessary to stop/start the viewer as no frames are being rendered yet.\n        mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);\n\n        mNavMesh.reset(new NavMesh(mRootNode, Settings::Manager::getBool(\"enable nav mesh render\", \"Navigator\")));\n        mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool(\"enable agents paths render\", \"Navigator\")));\n        mRecastMesh.reset(new RecastMesh(mRootNode, Settings::Manager::getBool(\"enable recast mesh render\", \"Navigator\")));\n        mPathgrid.reset(new Pathgrid(mRootNode));\n\n        mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get()));\n\n        if (getenv(\"OPENMW_DONT_PRECOMPILE\") == nullptr)\n        {\n            mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation);\n            mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat(\"target framerate\", \"Cells\"));\n        }\n\n        mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation());\n\n        mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem));\n\n        const std::string normalMapPattern = Settings::Manager::getString(\"normal map pattern\", \"Shaders\");\n        const std::string heightMapPattern = Settings::Manager::getString(\"normal height map pattern\", \"Shaders\");\n        const std::string specularMapPattern = Settings::Manager::getString(\"terrain specular map pattern\", \"Shaders\");\n        const bool useTerrainNormalMaps = Settings::Manager::getBool(\"auto use terrain normal maps\", \"Shaders\");\n        const bool useTerrainSpecularMaps = Settings::Manager::getBool(\"auto use terrain specular maps\", \"Shaders\");\n\n        mTerrainStorage.reset(new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps));\n        const float lodFactor = Settings::Manager::getFloat(\"lod factor\", \"Terrain\");\n\n        if (Settings::Manager::getBool(\"distant terrain\", \"Terrain\"))\n        {\n            const int compMapResolution = Settings::Manager::getInt(\"composite map resolution\", \"Terrain\");\n            int compMapPower = Settings::Manager::getInt(\"composite map level\", \"Terrain\");\n            compMapPower = std::max(-3, compMapPower);\n            float compMapLevel = pow(2, compMapPower);\n            const int vertexLodMod = Settings::Manager::getInt(\"vertex lod mod\", \"Terrain\");\n            float maxCompGeometrySize = Settings::Manager::getFloat(\"max composite geometry size\", \"Terrain\");\n            maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f);\n            mTerrain.reset(new Terrain::QuadTreeWorld(\n                sceneRoot, mRootNode, mResourceSystem, mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug,\n                compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize));\n            if (Settings::Manager::getBool(\"object paging\", \"Terrain\"))\n            {\n                mObjectPaging.reset(new ObjectPaging(mResourceSystem->getSceneManager()));\n                static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mObjectPaging.get());\n                mResourceSystem->addResourceManager(mObjectPaging.get());\n            }\n        }\n        else\n            mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug));\n\n        mTerrain->setTargetFrameRate(Settings::Manager::getFloat(\"target framerate\", \"Cells\"));\n        mTerrain->setWorkQueue(mWorkQueue.get());\n\n        if (Settings::Manager::getBool(\"enabled\", \"Groundcover\"))\n        {\n            osg::ref_ptr<osg::Group> groundcoverRoot = new osg::Group;\n            groundcoverRoot->setNodeMask(Mask_Groundcover);\n            groundcoverRoot->setName(\"Groundcover Root\");\n            sceneRoot->addChild(groundcoverRoot);\n\n            mGroundcoverUpdater = new GroundcoverUpdater;\n            groundcoverRoot->addUpdateCallback(mGroundcoverUpdater);\n\n            float chunkSize = Settings::Manager::getFloat(\"min chunk size\", \"Groundcover\");\n            if (chunkSize >= 1.0f)\n                chunkSize = 1.0f;\n            else if (chunkSize >= 0.5f)\n                chunkSize = 0.5f;\n            else if (chunkSize >= 0.25f)\n                chunkSize = 0.25f;\n            else if (chunkSize != 0.125f)\n                chunkSize = 0.125f;\n\n            float density = Settings::Manager::getFloat(\"density\", \"Groundcover\");\n            density = std::clamp(density, 0.f, 1.f);\n\n            mGroundcoverWorld.reset(new Terrain::QuadTreeWorld(groundcoverRoot, mTerrainStorage.get(), Mask_Groundcover, lodFactor, chunkSize));\n            mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density));\n            static_cast<Terrain::QuadTreeWorld*>(mGroundcoverWorld.get())->addChunkManager(mGroundcover.get());\n            mResourceSystem->addResourceManager(mGroundcover.get());\n\n            // Groundcover it is handled in the same way indifferently from if it is from active grid or from distant cell.\n            // Use a stub grid to avoid splitting between chunks for active grid and chunks for distant cells.\n            mGroundcoverWorld->setActiveGrid(osg::Vec4i(0, 0, 0, 0));\n        }\n        // water goes after terrain for correct waterculling order\n        mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));\n\n        mCamera.reset(new Camera(mViewer->getCamera()));\n        if (Settings::Manager::getBool(\"view over shoulder\", \"Camera\"))\n            mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));\n\n        mScreenshotManager.reset(new ScreenshotManager(viewer, mRootNode, sceneRoot, mResourceSystem, mWater.get()));\n\n        mViewer->setLightingMode(osgViewer::View::NO_LIGHT);\n\n        osg::ref_ptr<osg::LightSource> source = new osg::LightSource;\n        source->setNodeMask(Mask_Lighting);\n        mSunLight = new osg::Light;\n        source->setLight(mSunLight);\n        mSunLight->setDiffuse(osg::Vec4f(0,0,0,1));\n        mSunLight->setAmbient(osg::Vec4f(0,0,0,1));\n        mSunLight->setSpecular(osg::Vec4f(0,0,0,0));\n        mSunLight->setConstantAttenuation(1.f);\n        sceneRoot->setSunlight(mSunLight);\n        sceneRoot->addChild(source);\n\n        sceneRoot->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);\n        sceneRoot->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);\n        sceneRoot->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);\n        osg::ref_ptr<osg::Material> defaultMat (new osg::Material);\n        defaultMat->setColorMode(osg::Material::OFF);\n        defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n        defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n        defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n        sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat);\n\n        mFog.reset(new FogManager());\n\n        mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager()));\n        mSky->setCamera(mViewer->getCamera());\n\n        source->setStateSetModes(*mRootNode->getOrCreateStateSet(), osg::StateAttribute::ON);\n\n        mStateUpdater = new StateUpdater;\n        sceneRoot->addUpdateCallback(mStateUpdater);\n\n        osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING;\n\n        if (!Settings::Manager::getBool(\"small feature culling\", \"Camera\"))\n            cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING);\n        else\n        {\n            mViewer->getCamera()->setSmallFeatureCullingPixelSize(Settings::Manager::getFloat(\"small feature culling pixel size\", \"Camera\"));\n            cullingMode |= osg::CullStack::SMALL_FEATURE_CULLING;\n        }\n\n        mViewer->getCamera()->setCullingMode( cullingMode );\n\n        mViewer->getCamera()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);\n        mViewer->getCamera()->setCullingMode(cullingMode);\n\n        mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater));\n        NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor);\n        NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);\n        Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool(\"load unsupported nif files\", \"Models\"));\n\n        mNearClip = Settings::Manager::getFloat(\"near clip\", \"Camera\");\n        mViewDistance = Settings::Manager::getFloat(\"viewing distance\", \"Camera\");\n        float fov = Settings::Manager::getFloat(\"field of view\", \"Camera\");\n        mFieldOfView = std::min(std::max(1.f, fov), 179.f);\n        float firstPersonFov = Settings::Manager::getFloat(\"first person field of view\", \"Camera\");\n        mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);\n        mStateUpdater->setFogEnd(mViewDistance);\n\n        mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform(\"near\", mNearClip));\n        mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform(\"far\", mViewDistance));\n        mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform(\"simpleWater\", false));\n\n        // Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it\n        mRootNode->getOrCreateStateSet()->setAttribute(Shader::RemovedAlphaFunc::getInstance(GL_ALWAYS));\n        // The transparent renderbin sets alpha testing on because that was faster on old GPUs. It's now slower and breaks things.\n        mRootNode->getOrCreateStateSet()->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);\n\n        mUniformNear = mRootNode->getOrCreateStateSet()->getUniform(\"near\");\n        mUniformFar = mRootNode->getOrCreateStateSet()->getUniform(\"far\");\n        updateProjectionMatrix();\n    }\n\n    RenderingManager::~RenderingManager()\n    {\n        // let background loading thread finish before we delete anything else\n        mWorkQueue = nullptr;\n    }\n\n    osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()\n    {\n        return mViewer->getIncrementalCompileOperation();\n    }\n\n    MWRender::Objects& RenderingManager::getObjects()\n    {\n        return *mObjects.get();\n    }\n\n    Resource::ResourceSystem* RenderingManager::getResourceSystem()\n    {\n        return mResourceSystem;\n    }\n\n    SceneUtil::WorkQueue* RenderingManager::getWorkQueue()\n    {\n        return mWorkQueue.get();\n    }\n\n    SceneUtil::UnrefQueue* RenderingManager::getUnrefQueue()\n    {\n        return mUnrefQueue.get();\n    }\n\n    Terrain::World* RenderingManager::getTerrain()\n    {\n        return mTerrain.get();\n    }\n\n    void RenderingManager::preloadCommonAssets()\n    {\n        osg::ref_ptr<PreloadCommonAssetsWorkItem> workItem (new PreloadCommonAssetsWorkItem(mResourceSystem));\n        mSky->listAssetsToPreload(workItem->mModels, workItem->mTextures);\n        mWater->listAssetsToPreload(workItem->mTextures);\n\n        workItem->mModels.push_back(Settings::Manager::getString(\"xbaseanim\", \"Models\"));\n        workItem->mModels.push_back(Settings::Manager::getString(\"xbaseanim1st\", \"Models\"));\n        workItem->mModels.push_back(Settings::Manager::getString(\"xbaseanimfemale\", \"Models\"));\n        workItem->mModels.push_back(Settings::Manager::getString(\"xargonianswimkna\", \"Models\"));\n\n        workItem->mKeyframes.push_back(Settings::Manager::getString(\"xbaseanimkf\", \"Models\"));\n        workItem->mKeyframes.push_back(Settings::Manager::getString(\"xbaseanim1stkf\", \"Models\"));\n        workItem->mKeyframes.push_back(Settings::Manager::getString(\"xbaseanimfemalekf\", \"Models\"));\n        workItem->mKeyframes.push_back(Settings::Manager::getString(\"xargonianswimknakf\", \"Models\"));\n\n        workItem->mTextures.emplace_back(\"textures/_land_default.dds\");\n\n        mWorkQueue->addWorkItem(workItem);\n    }\n\n    double RenderingManager::getReferenceTime() const\n    {\n        return mViewer->getFrameStamp()->getReferenceTime();\n    }\n\n    osg::Group* RenderingManager::getLightRoot()\n    {\n        return mSceneRoot.get();\n    }\n\n    void RenderingManager::setNightEyeFactor(float factor)\n    {\n        if (factor != mNightEyeFactor)\n        {\n            mNightEyeFactor = factor;\n            updateAmbient();\n        }\n    }\n\n    void RenderingManager::setAmbientColour(const osg::Vec4f &colour)\n    {\n        mAmbientColor = colour;\n        updateAmbient();\n    }\n\n    void RenderingManager::skySetDate(int day, int month)\n    {\n        mSky->setDate(day, month);\n    }\n\n    int RenderingManager::skyGetMasserPhase() const\n    {\n        return mSky->getMasserPhase();\n    }\n\n    int RenderingManager::skyGetSecundaPhase() const\n    {\n        return mSky->getSecundaPhase();\n    }\n\n    void RenderingManager::skySetMoonColour(bool red)\n    {\n        mSky->setMoonColour(red);\n    }\n\n    void RenderingManager::configureAmbient(const ESM::Cell *cell)\n    {\n        bool needsAdjusting = false;\n        if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP)\n            needsAdjusting = !cell->isExterior() && !(cell->mData.mFlags & ESM::Cell::QuasiEx);\n\n        auto ambient = SceneUtil::colourFromRGB(cell->mAmbi.mAmbient);\n\n        if (needsAdjusting)\n        {\n            constexpr float pR = 0.2126;\n            constexpr float pG = 0.7152;\n            constexpr float pB = 0.0722;\n\n            // we already work in linear RGB so no conversions are needed for the luminosity function\n            float relativeLuminance = pR*ambient.r() + pG*ambient.g() + pB*ambient.b();\n            if (relativeLuminance < mMinimumAmbientLuminance)\n            {\n                // brighten ambient so it reaches the minimum threshold but no more, we want to mess with content data as least we can\n                float targetBrightnessIncreaseFactor = mMinimumAmbientLuminance / relativeLuminance;\n                if (ambient.r() == 0.f && ambient.g() == 0.f && ambient.b() == 0.f)\n                    ambient = osg::Vec4(mMinimumAmbientLuminance, mMinimumAmbientLuminance, mMinimumAmbientLuminance, ambient.a());\n                else\n                    ambient *= targetBrightnessIncreaseFactor;\n            }\n        }\n\n        setAmbientColour(ambient);\n\n        osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight);\n        mSunLight->setDiffuse(diffuse);\n        mSunLight->setSpecular(diffuse);\n        mSunLight->setPosition(osg::Vec4f(-0.15f, 0.15f, 1.f, 0.f));\n    }\n\n    void RenderingManager::setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular)\n    {\n        // need to wrap this in a StateUpdater?\n        mSunLight->setDiffuse(diffuse);\n        mSunLight->setSpecular(specular);\n    }\n\n    void RenderingManager::setSunDirection(const osg::Vec3f &direction)\n    {\n        osg::Vec3 position = direction * -1;\n        // need to wrap this in a StateUpdater?\n        mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0));\n\n        mSky->setSunDirection(position);\n    }\n\n    void RenderingManager::addCell(const MWWorld::CellStore *store)\n    {\n        mPathgrid->addCell(store);\n\n        mWater->changeCell(store);\n\n        if (store->getCell()->isExterior())\n        {\n            mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());\n            if (mGroundcoverWorld)\n                mGroundcoverWorld->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());\n        }\n    }\n    void RenderingManager::removeCell(const MWWorld::CellStore *store)\n    {\n        mPathgrid->removeCell(store);\n        mActorsPaths->removeCell(store);\n        mObjects->removeCell(store);\n\n        if (store->getCell()->isExterior())\n        {\n            mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());\n            if (mGroundcoverWorld)\n                mGroundcoverWorld->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());\n        }\n\n        mWater->removeCell(store);\n    }\n\n    void RenderingManager::enableTerrain(bool enable)\n    {\n        if (!enable)\n            mWater->setCullCallback(nullptr);\n        mTerrain->enable(enable);\n        if (mGroundcoverWorld)\n            mGroundcoverWorld->enable(enable);\n    }\n\n    void RenderingManager::setSkyEnabled(bool enabled)\n    {\n        mSky->setEnabled(enabled);\n        if (enabled)\n            mShadowManager->enableOutdoorMode();\n        else\n            mShadowManager->enableIndoorMode();\n    }\n\n    bool RenderingManager::toggleBorders()\n    {\n        bool borders = !mTerrain->getBordersVisible();\n        mTerrain->setBordersVisible(borders);\n        return borders;\n    }\n\n    bool RenderingManager::toggleRenderMode(RenderMode mode)\n    {\n        if (mode == Render_CollisionDebug || mode == Render_Pathgrid)\n            return mPathgrid->toggleRenderMode(mode);\n        else if (mode == Render_Wireframe)\n        {\n            bool wireframe = !mStateUpdater->getWireframe();\n            mStateUpdater->setWireframe(wireframe);\n            return wireframe;\n        }\n        else if (mode == Render_Water)\n        {\n            return mWater->toggle();\n        }\n        else if (mode == Render_Scene)\n        {\n            unsigned int mask = mViewer->getCamera()->getCullMask();\n            bool enabled = mask&Mask_Scene;\n            enabled = !enabled;\n            if (enabled)\n                mask |= Mask_Scene;\n            else\n                mask &= ~Mask_Scene;\n            mViewer->getCamera()->setCullMask(mask);\n            return enabled;\n        }\n        else if (mode == Render_NavMesh)\n        {\n            return mNavMesh->toggle();\n        }\n        else if (mode == Render_ActorsPaths)\n        {\n            return mActorsPaths->toggle();\n        }\n        else if (mode == Render_RecastMesh)\n        {\n            return mRecastMesh->toggle();\n        }\n        return false;\n    }\n\n    void RenderingManager::configureFog(const ESM::Cell *cell)\n    {\n        mFog->configure(mViewDistance, cell);\n    }\n\n    void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)\n    {\n        mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color);\n    }\n\n    SkyManager* RenderingManager::getSkyManager()\n    {\n        return mSky.get();\n    }\n\n    void RenderingManager::update(float dt, bool paused)\n    {\n        reportStats();\n\n        mUnrefQueue->flush(mWorkQueue.get());\n\n        float rainIntensity = mSky->getPrecipitationAlpha();\n        mWater->setRainIntensity(rainIntensity);\n\n        if (!paused)\n        {\n            mEffectManager->update(dt);\n            mSky->update(dt);\n            mWater->update(dt);\n\n            if (mGroundcoverUpdater)\n            {\n                const MWWorld::Ptr& player = mPlayerAnimation->getPtr();\n                osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());\n\n                float windSpeed = mSky->getBaseWindSpeed();\n                mGroundcoverUpdater->setWindSpeed(windSpeed);\n                mGroundcoverUpdater->setPlayerPos(playerPos);\n            }\n        }\n\n        updateNavMesh();\n        updateRecastMesh();\n\n        if (mViewOverShoulderController)\n            mViewOverShoulderController->update();\n        mCamera->update(dt, paused);\n\n        osg::Vec3d focal, cameraPos;\n        mCamera->getPosition(focal, cameraPos);\n        mCurrentCameraPos = cameraPos;\n\n        bool isUnderwater = mWater->isUnderwater(cameraPos);\n        mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater));\n        mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater));\n        setFogColor(mFog->getFogColor(isUnderwater));\n    }\n\n    void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)\n    {\n        if(mPlayerAnimation.get())\n        {\n            setupPlayer(ptr);\n            mPlayerAnimation->updatePtr(ptr);\n        }\n        mCamera->attachTo(ptr);\n    }\n\n    void RenderingManager::removePlayer(const MWWorld::Ptr &player)\n    {\n        mWater->removeEmitter(player);\n    }\n\n    void RenderingManager::rotateObject(const MWWorld::Ptr &ptr, const osg::Quat& rot)\n    {\n        if(ptr == mCamera->getTrackingPtr() &&\n           !mCamera->isVanityOrPreviewModeEnabled())\n        {\n            mCamera->rotateCameraToTrackingPtr();\n        }\n\n        ptr.getRefData().getBaseNode()->setAttitude(rot);\n    }\n\n    void RenderingManager::moveObject(const MWWorld::Ptr &ptr, const osg::Vec3f &pos)\n    {\n        ptr.getRefData().getBaseNode()->setPosition(pos);\n    }\n\n    void RenderingManager::scaleObject(const MWWorld::Ptr &ptr, const osg::Vec3f &scale)\n    {\n        ptr.getRefData().getBaseNode()->setScale(scale);\n\n        if (ptr == mCamera->getTrackingPtr()) // update height of camera\n            mCamera->processViewChange();\n    }\n\n    void RenderingManager::removeObject(const MWWorld::Ptr &ptr)\n    {\n        mActorsPaths->remove(ptr);\n        mObjects->removeObject(ptr);\n        mWater->removeEmitter(ptr);\n    }\n\n    void RenderingManager::setWaterEnabled(bool enabled)\n    {\n        mWater->setEnabled(enabled);\n        mSky->setWaterEnabled(enabled);\n    }\n\n    void RenderingManager::setWaterHeight(float height)\n    {\n        mWater->setCullCallback(mTerrain->getHeightCullCallback(height, Mask_Water));\n        mWater->setHeight(height);\n        mSky->setWaterHeight(height);\n    }\n\n    void RenderingManager::screenshot(osg::Image* image, int w, int h)\n    {\n        mScreenshotManager->screenshot(image, w, h);\n    }\n\n    bool RenderingManager::screenshot360(osg::Image* image)\n    {\n        if (mCamera->isVanityOrPreviewModeEnabled())\n        {\n            Log(Debug::Warning) << \"Spherical screenshots are not allowed in preview mode.\";\n            return false;\n        }\n\n        unsigned int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask();\n\n        if (mCamera->isFirstPerson())\n            mPlayerAnimation->getObjectRoot()->setNodeMask(0);\n\n        mScreenshotManager->screenshot360(image);\n\n        mPlayerAnimation->getObjectRoot()->setNodeMask(maskBackup);\n\n        return true;\n    }\n\n    osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb)\n    {\n        if (!worldbb.valid()) return osg::Vec4f();\n        osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix();\n        float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f;\n        for (int i=0; i<8; ++i)\n        {\n            osg::Vec3f corner = worldbb.corner(i);\n            corner = corner * viewProj;\n\n            float x = (corner.x() + 1.f) * 0.5f;\n            float y = (corner.y() - 1.f) * (-0.5f);\n\n            if (x < min_x)\n            min_x = x;\n\n            if (x > max_x)\n            max_x = x;\n\n            if (y < min_y)\n            min_y = y;\n\n            if (y > max_y)\n            max_y = y;\n        }\n\n        return osg::Vec4f(min_x, min_y, max_x, max_y);\n    }\n\n    RenderingManager::RayResult getIntersectionResult (osgUtil::LineSegmentIntersector* intersector)\n    {\n        RenderingManager::RayResult result;\n        result.mHit = false;\n        result.mHitRefnum.unset();\n        result.mRatio = 0;\n        if (intersector->containsIntersections())\n        {\n            result.mHit = true;\n            osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection();\n\n            result.mHitPointWorld = intersection.getWorldIntersectPoint();\n            result.mHitNormalWorld = intersection.getWorldIntersectNormal();\n            result.mRatio = intersection.ratio;\n\n            PtrHolder* ptrHolder = nullptr;\n            std::vector<RefnumMarker*> refnumMarkers;\n            for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)\n            {\n                osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer();\n                if (!userDataContainer)\n                    continue;\n                for (unsigned int i=0; i<userDataContainer->getNumUserObjects(); ++i)\n                {\n                    if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i)))\n                        ptrHolder = p;\n                    if (RefnumMarker* r = dynamic_cast<RefnumMarker*>(userDataContainer->getUserObject(i)))\n                        refnumMarkers.push_back(r);\n                }\n            }\n\n            if (ptrHolder)\n                result.mHitObject = ptrHolder->mPtr;\n\n            unsigned int vertexCounter = 0;\n            for (unsigned int i=0; i<refnumMarkers.size(); ++i)\n            {\n                unsigned int intersectionIndex = intersection.indexList.empty() ? 0 : intersection.indexList[0];\n                if (!refnumMarkers[i]->mNumVertices || (intersectionIndex >= vertexCounter && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices))\n                {\n                    result.mHitRefnum = refnumMarkers[i]->mRefnum;\n                    break;\n                }\n                vertexCounter += refnumMarkers[i]->mNumVertices;\n            }\n        }\n\n        return result;\n\n    }\n\n    osg::ref_ptr<osgUtil::IntersectionVisitor> RenderingManager::getIntersectionVisitor(osgUtil::Intersector *intersector, bool ignorePlayer, bool ignoreActors)\n    {\n        if (!mIntersectionVisitor)\n            mIntersectionVisitor = new osgUtil::IntersectionVisitor;\n\n        mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber());\n        mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp());\n        mIntersectionVisitor->setIntersector(intersector);\n\n        unsigned int mask = ~0u;\n        mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater|Mask_Groundcover);\n        if (ignorePlayer)\n            mask &= ~(Mask_Player);\n        if (ignoreActors)\n            mask &= ~(Mask_Actor|Mask_Player);\n\n        mIntersectionVisitor->setTraversalMask(mask);\n        return mIntersectionVisitor;\n    }\n\n    RenderingManager::RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors)\n    {\n        osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL,\n            origin, dest));\n        intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);\n\n        mRootNode->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors));\n\n        return getIntersectionResult(intersector);\n    }\n\n    RenderingManager::RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors)\n    {\n        osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::PROJECTION,\n                                                                                                       nX * 2.f - 1.f, nY * (-2.f) + 1.f));\n\n        osg::Vec3d dist (0.f, 0.f, -maxDistance);\n\n        dist = dist * mViewer->getCamera()->getProjectionMatrix();\n\n        osg::Vec3d end = intersector->getEnd();\n        end.z() = dist.z();\n        intersector->setEnd(end);\n        intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);\n\n        mViewer->getCamera()->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors));\n\n        return getIntersectionResult(intersector);\n    }\n\n    void RenderingManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)\n    {\n        mObjects->updatePtr(old, updated);\n        mActorsPaths->updatePtr(old, updated);\n    }\n\n    void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)\n    {\n        mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX);\n    }\n\n    void RenderingManager::notifyWorldSpaceChanged()\n    {\n        mEffectManager->clear();\n        mWater->clearRipples();\n    }\n\n    void RenderingManager::clear()\n    {\n        mSky->setMoonColour(false);\n\n        notifyWorldSpaceChanged();\n        if (mObjectPaging)\n            mObjectPaging->clear();\n    }\n\n    MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)\n    {\n        if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr())\n            return mPlayerAnimation.get();\n\n        return mObjects->getAnimation(ptr);\n    }\n\n    const MWRender::Animation* RenderingManager::getAnimation(const MWWorld::ConstPtr &ptr) const\n    {\n        if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr())\n            return mPlayerAnimation.get();\n\n        return mObjects->getAnimation(ptr);\n    }\n\n    void RenderingManager::setupPlayer(const MWWorld::Ptr &player)\n    {\n        if (!mPlayerNode)\n        {\n            mPlayerNode = new SceneUtil::PositionAttitudeTransform;\n            mPlayerNode->setNodeMask(Mask_Player);\n            mPlayerNode->setName(\"Player Root\");\n            mSceneRoot->addChild(mPlayerNode);\n        }\n\n        mPlayerNode->setUserDataContainer(new osg::DefaultUserDataContainer);\n        mPlayerNode->getUserDataContainer()->addUserObject(new PtrHolder(player));\n\n        player.getRefData().setBaseNode(mPlayerNode);\n\n        mWater->removeEmitter(player);\n        mWater->addEmitter(player);\n    }\n\n    void RenderingManager::renderPlayer(const MWWorld::Ptr &player)\n    {\n        mPlayerAnimation = new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, NpcAnimation::VM_Normal,\n                                                mFirstPersonFieldOfView);\n\n        mCamera->setAnimation(mPlayerAnimation.get());\n        mCamera->attachTo(player);\n    }\n\n    void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr)\n    {\n        NpcAnimation *anim = nullptr;\n        if(ptr == mPlayerAnimation->getPtr())\n            anim = mPlayerAnimation.get();\n        else\n            anim = dynamic_cast<NpcAnimation*>(mObjects->getAnimation(ptr));\n        if(anim)\n        {\n            anim->rebuild();\n            if(mCamera->getTrackingPtr() == ptr)\n            {\n                mCamera->attachTo(ptr);\n                mCamera->setAnimation(anim);\n            }\n        }\n    }\n\n    void RenderingManager::addWaterRippleEmitter(const MWWorld::Ptr &ptr)\n    {\n        mWater->addEmitter(ptr);\n    }\n\n    void RenderingManager::removeWaterRippleEmitter(const MWWorld::Ptr &ptr)\n    {\n        mWater->removeEmitter(ptr);\n    }\n\n    void RenderingManager::emitWaterRipple(const osg::Vec3f &pos)\n    {\n        mWater->emitRipple(pos);\n    }\n\n    void RenderingManager::updateProjectionMatrix()\n    {\n        double aspect = mViewer->getCamera()->getViewport()->aspectRatio();\n        float fov = mFieldOfView;\n        if (mFieldOfViewOverridden)\n            fov = mFieldOfViewOverride;\n        mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance);\n\n        mUniformNear->set(mNearClip);\n        mUniformFar->set(mViewDistance);\n\n        // Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear.\n        // Limit FOV here just for sure, otherwise viewing distance can be too high.\n        fov = std::min(mFieldOfView, 140.f);\n        float distanceMult = std::cos(osg::DegreesToRadians(fov)/2.f);\n        mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f));\n\n        if (mGroundcoverWorld)\n        {\n            float groundcoverDistance = std::max(0.f, Settings::Manager::getFloat(\"rendering distance\", \"Groundcover\"));\n            mGroundcoverWorld->setViewDistance(groundcoverDistance * (distanceMult ? 1.f/distanceMult : 1.f));\n        }\n    }\n\n    void RenderingManager::updateTextureFiltering()\n    {\n        mViewer->stopThreading();\n\n        mResourceSystem->getSceneManager()->setFilterSettings(\n            Settings::Manager::getString(\"texture mag filter\", \"General\"),\n            Settings::Manager::getString(\"texture min filter\", \"General\"),\n            Settings::Manager::getString(\"texture mipmap\", \"General\"),\n            Settings::Manager::getInt(\"anisotropy\", \"General\")\n        );\n\n        mTerrain->updateTextureFiltering();\n\n        mViewer->startThreading();\n    }\n\n    void RenderingManager::updateAmbient()\n    {\n        osg::Vec4f color = mAmbientColor;\n\n        if (mNightEyeFactor > 0.f)\n            color += osg::Vec4f(0.7, 0.7, 0.7, 0.0) * mNightEyeFactor;\n\n        mStateUpdater->setAmbientColor(color);\n    }\n\n    void RenderingManager::setFogColor(const osg::Vec4f &color)\n    {\n        mViewer->getCamera()->setClearColor(color);\n\n        mStateUpdater->setFogColor(color);\n    }\n\n    void RenderingManager::reportStats() const\n    {\n        osg::Stats* stats = mViewer->getViewerStats();\n        unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();\n        if (stats->collectStats(\"resource\"))\n        {\n            stats->setAttribute(frameNumber, \"UnrefQueue\", mUnrefQueue->getNumItems());\n\n            mTerrain->reportStats(frameNumber, stats);\n        }\n    }\n\n    void RenderingManager::processChangedSettings(const Settings::CategorySettingVector &changed)\n    {\n        for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it)\n        {\n            if (it->first == \"Camera\" && it->second == \"field of view\")\n            {\n                mFieldOfView = Settings::Manager::getFloat(\"field of view\", \"Camera\");\n                updateProjectionMatrix();\n            }\n            else if (it->first == \"Camera\" && it->second == \"viewing distance\")\n            {\n                mViewDistance = Settings::Manager::getFloat(\"viewing distance\", \"Camera\");\n                if(!Settings::Manager::getBool(\"use distant fog\", \"Fog\"))\n                    mStateUpdater->setFogEnd(mViewDistance);\n                updateProjectionMatrix();\n            }\n            else if (it->first == \"General\" && (it->second == \"texture filter\" ||\n                                                it->second == \"texture mipmap\" ||\n                                                it->second == \"anisotropy\"))\n            {\n                updateTextureFiltering();\n            }\n            else if (it->first == \"Water\")\n            {\n                mWater->processChangedSettings(changed);\n            }\n            else if (it->first == \"Shaders\" && it->second == \"minimum interior brightness\")\n            {\n                mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat(\"minimum interior brightness\", \"Shaders\"), 0.f, 1.f);\n                if (MWMechanics::getPlayer().isInCell())\n                    configureAmbient(MWMechanics::getPlayer().getCell()->getCell());\n            }\n            else if (it->first == \"Shaders\" && (it->second == \"light bounds multiplier\" ||\n                                                it->second == \"maximum light distance\" ||\n                                                it->second == \"light fade start\" ||\n                                                it->second == \"max lights\"))\n            {\n                auto* lightManager = static_cast<SceneUtil::LightManager*>(getLightRoot());\n                lightManager->processChangedSettings(changed);\n\n                if (it->second == \"max lights\" && !lightManager->usingFFP())\n                {\n                    mViewer->stopThreading();\n\n                    lightManager->updateMaxLights();\n\n                    auto defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();\n                    for (const auto& [name, key] : lightManager->getLightDefines())\n                        defines[name] = key;\n                    mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);\n\n                    mSceneRoot->removeUpdateCallback(mStateUpdater);\n                    mStateUpdater = new StateUpdater;\n                    mSceneRoot->addUpdateCallback(mStateUpdater);\n                    mStateUpdater->setFogEnd(mViewDistance);\n                    updateAmbient();\n\n                    mViewer->startThreading();\n                }\n            }\n        }\n    }\n\n    float RenderingManager::getNearClipDistance() const\n    {\n        return mNearClip;\n    }\n\n    float RenderingManager::getTerrainHeightAt(const osg::Vec3f &pos)\n    {\n        return mTerrain->getHeightAt(pos);\n    }\n\n    void RenderingManager::overrideFieldOfView(float val)\n    {\n        if (mFieldOfViewOverridden != true || mFieldOfViewOverride != val)\n        {\n            mFieldOfViewOverridden = true;\n            mFieldOfViewOverride = val;\n            updateProjectionMatrix();\n        }\n    }\n\n    osg::Vec3f RenderingManager::getHalfExtents(const MWWorld::ConstPtr& object) const\n    {\n        osg::Vec3f halfExtents(0, 0, 0);\n        std::string modelName = object.getClass().getModel(object);\n        if (modelName.empty())\n            return halfExtents;\n\n        osg::ref_ptr<const osg::Node> node = mResourceSystem->getSceneManager()->getTemplate(modelName);\n        osg::ComputeBoundsVisitor computeBoundsVisitor;\n        computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect));\n        const_cast<osg::Node*>(node.get())->accept(computeBoundsVisitor);\n        osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox();\n\n        if (bounds.valid())\n        {\n            halfExtents[0] = std::abs(bounds.xMax() - bounds.xMin()) / 2.f;\n            halfExtents[1] = std::abs(bounds.yMax() - bounds.yMin()) / 2.f;\n            halfExtents[2] = std::abs(bounds.zMax() - bounds.zMin()) / 2.f;\n        }\n\n        return halfExtents;\n    }\n\n    void RenderingManager::resetFieldOfView()\n    {\n        if (mFieldOfViewOverridden == true)\n        {\n            mFieldOfViewOverridden = false;\n\n            updateProjectionMatrix();\n        }\n    }\n    void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format)\n    {\n        osg::Node* node = mViewer->getSceneData();\n        if (!ptr.isEmpty())\n            node = ptr.getRefData().getBaseNode();\n\n        SceneUtil::writeScene(node, filename, format);\n    }\n\n    LandManager *RenderingManager::getLandManager() const\n    {\n        return mTerrainStorage->getLandManager();\n    }\n\n    void RenderingManager::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,\n            const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const\n    {\n        mActorsPaths->update(actor, path, halfExtents, start, end, mNavigator.getSettings());\n    }\n\n    void RenderingManager::removeActorPath(const MWWorld::ConstPtr& actor) const\n    {\n        mActorsPaths->remove(actor);\n    }\n\n    void RenderingManager::setNavMeshNumber(const std::size_t value)\n    {\n        mNavMeshNumber = value;\n    }\n\n    void RenderingManager::updateNavMesh()\n    {\n        if (!mNavMesh->isEnabled())\n            return;\n\n        const auto navMeshes = mNavigator.getNavMeshes();\n\n        auto it = navMeshes.begin();\n        for (std::size_t i = 0; it != navMeshes.end() && i < mNavMeshNumber; ++i)\n            ++it;\n        if (it == navMeshes.end())\n        {\n            mNavMesh->reset();\n        }\n        else\n        {\n            try\n            {\n                const auto locked = it->second->lockConst();\n                mNavMesh->update(locked->getImpl(), mNavMeshNumber, locked->getGeneration(),\n                                 locked->getNavMeshRevision(), mNavigator.getSettings());\n            }\n            catch (const std::exception& e)\n            {\n                Log(Debug::Error) << \"NavMesh render update exception: \" << e.what();\n            }\n        }\n    }\n\n    void RenderingManager::updateRecastMesh()\n    {\n        if (!mRecastMesh->isEnabled())\n            return;\n\n        mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings());\n    }\n\n    void RenderingManager::setActiveGrid(const osg::Vec4i &grid)\n    {\n        mTerrain->setActiveGrid(grid);\n    }\n    bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled)\n    {\n        if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)\n            return false;\n        if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), osg::Vec2i(ptr.getCell()->getCell()->getGridX(), ptr.getCell()->getCell()->getGridY()), enabled))\n        {\n            mTerrain->rebuildViews();\n            return true;\n        }\n        return false;\n    }\n    void RenderingManager::pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr)\n    {\n        if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)\n            return;\n        const ESM::RefNum & refnum = ptr.getCellRef().getRefNum();\n        if (!refnum.hasContentFile()) return;\n        if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3(), osg::Vec2i(ptr.getCell()->getCell()->getGridX(), ptr.getCell()->getCell()->getGridY())))\n            mTerrain->rebuildViews();\n    }\n    bool RenderingManager::pagingUnlockCache()\n    {\n        if (mObjectPaging && mObjectPaging->unlockCache())\n        {\n            mTerrain->rebuildViews();\n            return true;\n        }\n        return false;\n    }\n    void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out)\n    {\n        if (mObjectPaging)\n            mObjectPaging->getPagedRefnums(activeGrid, out);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/renderingmanager.hpp",
    "content": "#ifndef OPENMW_MWRENDER_RENDERINGMANAGER_H\n#define OPENMW_MWRENDER_RENDERINGMANAGER_H\n\n#include <osg/ref_ptr>\n#include <osg/Light>\n#include <osg/Camera>\n\n#include <components/settings/settings.hpp>\n\n#include <osgUtil/IncrementalCompileOperation>\n\n#include \"objects.hpp\"\n\n#include \"renderinginterface.hpp\"\n#include \"rendermode.hpp\"\n\n#include <deque>\n#include <memory>\n\nnamespace osg\n{\n    class Group;\n    class PositionAttitudeTransform;\n}\n\nnamespace osgUtil\n{\n    class IntersectionVisitor;\n    class Intersector;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace osgViewer\n{\n    class Viewer;\n}\n\nnamespace ESM\n{\n    struct Cell;\n    struct RefNum;\n}\n\nnamespace Terrain\n{\n    class World;\n}\n\nnamespace Fallback\n{\n    class Map;\n}\n\nnamespace SceneUtil\n{\n    class ShadowManager;\n    class WorkQueue;\n    class UnrefQueue;\n}\n\nnamespace DetourNavigator\n{\n    struct Navigator;\n    struct Settings;\n}\n\nnamespace MWRender\n{\n    class GroundcoverUpdater;\n    class StateUpdater;\n\n    class EffectManager;\n    class ScreenshotManager;\n    class FogManager;\n    class SkyManager;\n    class NpcAnimation;\n    class Pathgrid;\n    class Camera;\n    class ViewOverShoulderController;\n    class Water;\n    class TerrainStorage;\n    class LandManager;\n    class NavMesh;\n    class ActorsPaths;\n    class RecastMesh;\n    class ObjectPaging;\n    class Groundcover;\n\n    class RenderingManager : public MWRender::RenderingInterface\n    {\n    public:\n        RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,\n                         Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,\n                         const std::string& resourcePath, DetourNavigator::Navigator& navigator);\n        ~RenderingManager();\n\n        osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation();\n\n        MWRender::Objects& getObjects() override;\n\n        Resource::ResourceSystem* getResourceSystem();\n\n        SceneUtil::WorkQueue* getWorkQueue();\n        SceneUtil::UnrefQueue* getUnrefQueue();\n        Terrain::World* getTerrain();\n\n        osg::Uniform* mUniformNear;\n        osg::Uniform* mUniformFar;\n\n        void preloadCommonAssets();\n\n        double getReferenceTime() const;\n\n        osg::Group* getLightRoot();\n\n        void setNightEyeFactor(float factor);\n\n        void setAmbientColour(const osg::Vec4f& colour);\n\n        void skySetDate(int day, int month);\n        int skyGetMasserPhase() const;\n        int skyGetSecundaPhase() const;\n        void skySetMoonColour(bool red);\n\n        void setSunDirection(const osg::Vec3f& direction);\n        void setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular);\n\n        void configureAmbient(const ESM::Cell* cell);\n        void configureFog(const ESM::Cell* cell);\n        void configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f& colour);\n\n        void addCell(const MWWorld::CellStore* store);\n        void removeCell(const MWWorld::CellStore* store);\n\n        void enableTerrain(bool enable);\n\n        void updatePtr(const MWWorld::Ptr& old, const MWWorld::Ptr& updated);\n\n        void rotateObject(const MWWorld::Ptr& ptr, const osg::Quat& rot);\n        void moveObject(const MWWorld::Ptr& ptr, const osg::Vec3f& pos);\n        void scaleObject(const MWWorld::Ptr& ptr, const osg::Vec3f& scale);\n\n        void removeObject(const MWWorld::Ptr& ptr);\n\n        void setWaterEnabled(bool enabled);\n        void setWaterHeight(float level);\n\n        /// Take a screenshot of w*h onto the given image, not including the GUI.\n        void screenshot(osg::Image* image, int w, int h);\n        bool screenshot360(osg::Image* image);\n\n        struct RayResult\n        {\n            bool mHit;\n            osg::Vec3f mHitNormalWorld;\n            osg::Vec3f mHitPointWorld;\n            MWWorld::Ptr mHitObject;\n            ESM::RefNum mHitRefnum;\n            float mRatio;\n        };\n\n        RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false);\n\n        /// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates,\n        /// where (0,0) is the top left corner.\n        RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false);\n\n        /// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner.\n        osg::Vec4f getScreenBounds(const osg::BoundingBox &worldbb);\n\n        void setSkyEnabled(bool enabled);\n\n        bool toggleRenderMode(RenderMode mode);\n\n        SkyManager* getSkyManager();\n\n        void spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale = 1.f, bool isMagicVFX = true);\n\n        /// Clear all savegame-specific data\n        void clear();\n\n        /// Clear all worldspace-specific data\n        void notifyWorldSpaceChanged();\n\n        void update(float dt, bool paused);\n\n        Animation* getAnimation(const MWWorld::Ptr& ptr);\n        const Animation* getAnimation(const MWWorld::ConstPtr& ptr) const;\n\n        void addWaterRippleEmitter(const MWWorld::Ptr& ptr);\n        void removeWaterRippleEmitter(const MWWorld::Ptr& ptr);\n        void emitWaterRipple(const osg::Vec3f& pos);\n\n        void updatePlayerPtr(const MWWorld::Ptr &ptr);\n\n        void removePlayer(const MWWorld::Ptr& player);\n        void setupPlayer(const MWWorld::Ptr& player);\n        void renderPlayer(const MWWorld::Ptr& player);\n\n        void rebuildPtr(const MWWorld::Ptr& ptr);\n\n        void processChangedSettings(const Settings::CategorySettingVector& settings);\n\n        float getNearClipDistance() const;\n\n        float getTerrainHeightAt(const osg::Vec3f& pos);\n\n        // camera stuff\n        Camera* getCamera() { return mCamera.get(); }\n        const osg::Vec3f& getCameraPosition() const { return mCurrentCameraPos; }\n\n        /// temporarily override the field of view with given value.\n        void overrideFieldOfView(float val);\n        /// reset a previous overrideFieldOfView() call, i.e. revert to field of view specified in the settings file.\n        void resetFieldOfView();\n\n        osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& object) const;\n\n        void exportSceneGraph(const MWWorld::Ptr& ptr, const std::string& filename, const std::string& format);\n\n        LandManager* getLandManager() const;\n\n        bool toggleBorders();\n\n        void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,\n                const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const;\n\n        void removeActorPath(const MWWorld::ConstPtr& actor) const;\n\n        void setNavMeshNumber(const std::size_t value);\n\n        void setActiveGrid(const osg::Vec4i &grid);\n\n        bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled);\n        void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr);\n        bool pagingUnlockCache();\n        void getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out);\n\n    private:\n        void updateProjectionMatrix();\n        void updateTextureFiltering();\n        void updateAmbient();\n        void setFogColor(const osg::Vec4f& color);\n        void updateThirdPersonViewMode();\n\n        void reportStats() const;\n\n        void updateNavMesh();\n\n        void updateRecastMesh();\n\n        osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors);\n\n        osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor;\n\n        osg::ref_ptr<osgViewer::Viewer> mViewer;\n        osg::ref_ptr<osg::Group> mRootNode;\n        osg::ref_ptr<osg::Group> mSceneRoot;\n        Resource::ResourceSystem* mResourceSystem;\n\n        osg::ref_ptr<GroundcoverUpdater> mGroundcoverUpdater;\n\n        osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;\n        osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;\n\n        osg::ref_ptr<osg::Light> mSunLight;\n\n        DetourNavigator::Navigator& mNavigator;\n        std::unique_ptr<NavMesh> mNavMesh;\n        std::size_t mNavMeshNumber = 0;\n        std::unique_ptr<ActorsPaths> mActorsPaths;\n        std::unique_ptr<RecastMesh> mRecastMesh;\n        std::unique_ptr<Pathgrid> mPathgrid;\n        std::unique_ptr<Objects> mObjects;\n        std::unique_ptr<Water> mWater;\n        std::unique_ptr<Terrain::World> mTerrain;\n        std::unique_ptr<Terrain::World> mGroundcoverWorld;\n        std::unique_ptr<TerrainStorage> mTerrainStorage;\n        std::unique_ptr<ObjectPaging> mObjectPaging;\n        std::unique_ptr<Groundcover> mGroundcover;\n        std::unique_ptr<SkyManager> mSky;\n        std::unique_ptr<FogManager> mFog;\n        std::unique_ptr<ScreenshotManager> mScreenshotManager;\n        std::unique_ptr<EffectManager> mEffectManager;\n        std::unique_ptr<SceneUtil::ShadowManager> mShadowManager;\n        osg::ref_ptr<NpcAnimation> mPlayerAnimation;\n        osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;\n        std::unique_ptr<Camera> mCamera;\n        std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;\n        osg::Vec3f mCurrentCameraPos;\n\n        osg::ref_ptr<StateUpdater> mStateUpdater;\n\n        osg::Vec4f mAmbientColor;\n        float mMinimumAmbientLuminance;\n        float mNightEyeFactor;\n\n        float mNearClip;\n        float mViewDistance;\n        bool mFieldOfViewOverridden;\n        float mFieldOfViewOverride;\n        float mFieldOfView;\n        float mFirstPersonFieldOfView;\n\n        void operator = (const RenderingManager&);\n        RenderingManager(const RenderingManager&);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/rendermode.hpp",
    "content": "#ifndef OPENMW_MWRENDER_RENDERMODE_H\n#define OPENMW_MWRENDER_RENDERMODE_H\n\nnamespace MWRender\n{\n\n    enum RenderMode\n    {\n        Render_CollisionDebug,\n        Render_Wireframe,\n        Render_Pathgrid,\n        Render_Water,\n        Render_Scene,\n        Render_NavMesh,\n        Render_ActorsPaths,\n        Render_RecastMesh,\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/ripplesimulation.cpp",
    "content": "#include \"ripplesimulation.hpp\"\n\n#include <iomanip>\n\n#include <osg/PolygonOffset>\n#include <osg/Texture2D>\n#include <osg/Material>\n#include <osg/Depth>\n#include <osg/PositionAttitudeTransform>\n#include <osgParticle/ParticleSystem>\n#include <osgParticle/ParticleSystemUpdater>\n\n#include <components/misc/rng.hpp>\n#include <components/nifosg/controller.hpp>\n#include <components/resource/imagemanager.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/fallback/fallback.hpp>\n\n#include \"vismask.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace\n{\n    void createWaterRippleStateSet(Resource::ResourceSystem* resourceSystem,osg::Node* node)\n    {\n        int rippleFrameCount = Fallback::Map::getInt(\"Water_RippleFrameCount\");\n        if (rippleFrameCount <= 0)\n            return;\n\n        const std::string& tex = Fallback::Map::getString(\"Water_RippleTexture\");\n\n        std::vector<osg::ref_ptr<osg::Texture2D> > textures;\n        for (int i=0; i<rippleFrameCount; ++i)\n        {\n            std::ostringstream texname;\n            texname << \"textures/water/\" << tex << std::setw(2) << std::setfill('0') << i << \".dds\";\n            osg::ref_ptr<osg::Texture2D> tex2 (new osg::Texture2D(resourceSystem->getImageManager()->getImage(texname.str())));\n            tex2->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n            tex2->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n            resourceSystem->getSceneManager()->applyFilterSettings(tex2);\n            textures.push_back(tex2);\n        }\n\n        osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures));\n        controller->setSource(std::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));\n        node->addUpdateCallback(controller);\n\n        osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);\n        stateset->setMode(GL_BLEND, osg::StateAttribute::ON);\n        stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n        stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON);\n\n        osg::ref_ptr<osg::Depth> depth (new osg::Depth);\n        depth->setWriteMask(false);\n        stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);\n\n        osg::ref_ptr<osg::PolygonOffset> polygonOffset (new osg::PolygonOffset);\n        polygonOffset->setUnits(-1);\n        polygonOffset->setFactor(-1);\n        stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON);\n\n        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n\n        osg::ref_ptr<osg::Material> mat (new osg::Material);\n        mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n        mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f));\n        mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n        mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n        mat->setColorMode(osg::Material::DIFFUSE);\n        stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);\n\n        node->setStateSet(stateset);\n    }\n}\n\nnamespace MWRender\n{\n\nRippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* resourceSystem)\n    : mParent(parent)\n{\n    mParticleSystem = new osgParticle::ParticleSystem;\n\n    mParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED);\n    mParticleSystem->setAlignVectorX(osg::Vec3f(1,0,0));\n    mParticleSystem->setAlignVectorY(osg::Vec3f(0,1,0));\n\n    osgParticle::Particle& particleTemplate = mParticleSystem->getDefaultParticleTemplate();\n    particleTemplate.setSizeRange(osgParticle::rangef(15, 180));\n    particleTemplate.setColorRange(osgParticle::rangev4(osg::Vec4f(1,1,1,0.7), osg::Vec4f(1,1,1,0.7)));\n    particleTemplate.setAlphaRange(osgParticle::rangef(1.f, 0.f));\n    particleTemplate.setAngularVelocity(osg::Vec3f(0,0,Fallback::Map::getFloat(\"Water_RippleRotSpeed\")));\n    particleTemplate.setLifeTime(Fallback::Map::getFloat(\"Water_RippleLifetime\"));\n\n    osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater (new osgParticle::ParticleSystemUpdater);\n    updater->addParticleSystem(mParticleSystem);\n\n    mParticleNode = new osg::PositionAttitudeTransform;\n    mParticleNode->setName(\"Ripple Root\");\n    mParticleNode->addChild(updater);\n    mParticleNode->addChild(mParticleSystem);\n    mParticleNode->setNodeMask(Mask_Water);\n\n    createWaterRippleStateSet(resourceSystem, mParticleNode);\n\n    resourceSystem->getSceneManager()->recreateShaders(mParticleNode);\n\n    mParent->addChild(mParticleNode);\n}\n\nRippleSimulation::~RippleSimulation()\n{\n    mParent->removeChild(mParticleNode);\n}\n\nvoid RippleSimulation::update(float dt)\n{\n    const MWBase::World* world = MWBase::Environment::get().getWorld();\n    for (Emitter& emitter : mEmitters)\n    {\n        MWWorld::ConstPtr& ptr = emitter.mPtr;\n        if (ptr == MWBase::Environment::get().getWorld ()->getPlayerPtr())\n        {\n            // fetch a new ptr (to handle cell change etc)\n            // for non-player actors this is done in updateObjectCell\n            ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n        }\n\n        osg::Vec3f currentPos (ptr.getRefData().getPosition().asVec3());\n\n        bool shouldEmit = (world->isUnderwater(ptr.getCell(), currentPos) && !world->isSubmerged(ptr)) || world->isWalkingOnWater(ptr);\n        if (shouldEmit && (currentPos - emitter.mLastEmitPosition).length() > 10)\n        {\n            emitter.mLastEmitPosition = currentPos;\n\n            currentPos.z() = mParticleNode->getPosition().z();\n\n            if (mParticleSystem->numParticles()-mParticleSystem->numDeadParticles() > 500)\n                continue; // TODO: remove the oldest particle to make room?\n\n            emitRipple(currentPos);\n        }\n    }\n}\n\n\nvoid RippleSimulation::addEmitter(const MWWorld::ConstPtr& ptr, float scale, float force)\n{\n    Emitter newEmitter;\n    newEmitter.mPtr = ptr;\n    newEmitter.mScale = scale;\n    newEmitter.mForce = force;\n    newEmitter.mLastEmitPosition = osg::Vec3f(0,0,0);\n    mEmitters.push_back (newEmitter);\n}\n\nvoid RippleSimulation::removeEmitter (const MWWorld::ConstPtr& ptr)\n{\n    for (std::vector<Emitter>::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it)\n    {\n        if (it->mPtr == ptr)\n        {\n            mEmitters.erase(it);\n            return;\n        }\n    }\n}\n\nvoid RippleSimulation::updateEmitterPtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& ptr)\n{\n    for (std::vector<Emitter>::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it)\n    {\n        if (it->mPtr == old)\n        {\n            it->mPtr = ptr;\n            return;\n        }\n    }\n}\n\nvoid RippleSimulation::removeCell(const MWWorld::CellStore *store)\n{\n    for (std::vector<Emitter>::iterator it = mEmitters.begin(); it != mEmitters.end();)\n    {\n        if ((it->mPtr.isInCell() && it->mPtr.getCell() == store) && it->mPtr != MWMechanics::getPlayer())\n        {\n            it = mEmitters.erase(it);\n        }\n        else\n            ++it;\n    }\n}\n\nvoid RippleSimulation::emitRipple(const osg::Vec3f &pos)\n{\n    if (std::abs(pos.z() - mParticleNode->getPosition().z()) < 20)\n    {\n        osgParticle::ParticleSystem::ScopedWriteLock lock(*mParticleSystem->getReadWriteMutex());\n        osgParticle::Particle* p = mParticleSystem->createParticle(nullptr);\n        p->setPosition(osg::Vec3f(pos.x(), pos.y(), 0.f));\n        p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI));\n    }\n}\n\nvoid RippleSimulation::setWaterHeight(float height)\n{\n    mParticleNode->setPosition(osg::Vec3f(0,0,height));\n}\n\nvoid RippleSimulation::clear()\n{\n    for (int i=0; i<mParticleSystem->numParticles(); ++i)\n        mParticleSystem->destroyParticle(i);\n}\n\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/ripplesimulation.hpp",
    "content": "#ifndef OPENMW_MWRENDER_RIPPLESIMULATION_H\n#define OPENMW_MWRENDER_RIPPLESIMULATION_H\n\n#include <osg/ref_ptr>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace osg\n{\n    class Group;\n    class PositionAttitudeTransform;\n}\n\nnamespace osgParticle\n{\n    class ParticleSystem;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace Fallback\n{\n    class Map;\n}\n\nnamespace MWRender\n{\n\n    struct Emitter\n    {\n        MWWorld::ConstPtr mPtr;\n        osg::Vec3f mLastEmitPosition;\n        float mScale;\n        float mForce;\n    };\n\n    class RippleSimulation\n    {\n    public:\n        RippleSimulation(osg::Group* parent, Resource::ResourceSystem* resourceSystem);\n        ~RippleSimulation();\n\n        /// @param dt Time since the last frame\n        void update(float dt);\n\n        /// adds an emitter, position will be tracked automatically\n        void addEmitter (const MWWorld::ConstPtr& ptr, float scale = 1.f, float force = 1.f);\n        void removeEmitter (const MWWorld::ConstPtr& ptr);\n        void updateEmitterPtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& ptr);\n        void removeCell(const MWWorld::CellStore* store);\n\n        void emitRipple(const osg::Vec3f& pos);\n\n        /// Change the height of the water surface, thus moving all ripples with it\n        void setWaterHeight(float height);\n\n        /// Remove all active ripples\n        void clear();\n\n    private:\n        osg::ref_ptr<osg::Group> mParent;\n\n        osg::ref_ptr<osgParticle::ParticleSystem> mParticleSystem;\n        osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;\n\n        std::vector<Emitter> mEmitters;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/rotatecontroller.cpp",
    "content": "#include \"rotatecontroller.hpp\"\n\n#include <osg/MatrixTransform>\n\nnamespace MWRender\n{\n\nRotateController::RotateController(osg::Node *relativeTo)\n    : mEnabled(true)\n    , mRelativeTo(relativeTo)\n{\n\n}\n\nvoid RotateController::setEnabled(bool enabled)\n{\n    mEnabled = enabled;\n}\n\nvoid RotateController::setRotate(const osg::Quat &rotate)\n{\n    mRotate = rotate;\n}\n\nvoid RotateController::operator()(osg::Node *node, osg::NodeVisitor *nv)\n{\n    if (!mEnabled)\n    {\n        traverse(node, nv);\n        return;\n    }\n    osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);\n    osg::Matrix matrix = transform->getMatrix();\n    osg::Quat worldOrient = getWorldOrientation(node);\n\n    osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate();\n    matrix.setRotate(orient);\n\n    transform->setMatrix(matrix);\n\n    traverse(node,nv);\n}\n\nosg::Quat RotateController::getWorldOrientation(osg::Node *node)\n{\n    // this could be optimized later, we just need the world orientation, not the full matrix\n    osg::NodePathList nodepaths = node->getParentalNodePaths(mRelativeTo);\n    osg::Quat worldOrient;\n    if (!nodepaths.empty())\n    {\n        osg::Matrixf worldMat = osg::computeLocalToWorld(nodepaths[0]);\n        worldOrient = worldMat.getRotate();\n    }\n    return worldOrient;\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/rotatecontroller.hpp",
    "content": "#ifndef OPENMW_MWRENDER_ROTATECONTROLLER_H\n#define OPENMW_MWRENDER_ROTATECONTROLLER_H\n\n#include <osg/NodeCallback>\n#include <osg/Quat>\n\nnamespace MWRender\n{\n\n/// Applies a rotation in \\a relativeTo's space.\n/// @note Assumes that the node being rotated has its \"original\" orientation set every frame by a different controller.\n/// The rotation is then applied on top of that orientation.\n/// @note Must be set on a MatrixTransform.\nclass RotateController : public osg::NodeCallback\n{\npublic:\n    RotateController(osg::Node* relativeTo);\n\n    void setEnabled(bool enabled);\n\n    void setRotate(const osg::Quat& rotate);\n\n    void operator()(osg::Node* node, osg::NodeVisitor* nv) override;\n\nprotected:\n    osg::Quat getWorldOrientation(osg::Node* node);\n\n    bool mEnabled;\n    osg::Quat mRotate;\n    osg::Node* mRelativeTo;\n};\n\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/screenshotmanager.cpp",
    "content": "#include \"screenshotmanager.hpp\"\n\n#include <condition_variable>\n#include <mutex>\n\n#include <osg/ImageUtils>\n#include <osg/ShapeDrawable>\n#include <osg/Texture2D>\n#include <osg/TextureCubeMap>\n\n#include <components/misc/stringops.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/shader/shadermanager.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include \"../mwgui/loadingscreen.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"util.hpp\"\n#include \"vismask.hpp\"\n#include \"water.hpp\"\n\nnamespace MWRender\n{\n    enum Screenshot360Type\n    {\n        Spherical,\n        Cylindrical,\n        Planet,\n        RawCubemap\n    };\n\n    class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback\n    {\n    public:\n        NotifyDrawCompletedCallback()\n            : mDone(false), mFrame(0)\n        {\n        }\n\n        void operator () (osg::RenderInfo& renderInfo) const override\n        {\n            std::lock_guard<std::mutex> lock(mMutex);\n            if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame && !mDone)\n            {\n                mDone = true;\n                mCondition.notify_one();\n            }\n        }\n\n        void waitTillDone()\n        {\n            std::unique_lock<std::mutex> lock(mMutex);\n            if (mDone)\n                return;\n            mCondition.wait(lock);\n        }\n\n        void reset(unsigned int frame)\n        {\n            std::lock_guard<std::mutex> lock(mMutex);\n            mDone = false;\n            mFrame = frame;\n        }\n\n        mutable std::condition_variable mCondition;\n        mutable std::mutex mMutex;\n        mutable bool mDone;\n        unsigned int mFrame;\n    };\n\n    class ReadImageFromFramebufferCallback : public osg::Drawable::DrawCallback\n    {\n    public:\n        ReadImageFromFramebufferCallback(osg::Image* image, int width, int height)\n            : mWidth(width), mHeight(height), mImage(image)\n        {\n        }\n        void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* /*drawable*/) const override\n        {\n            int screenW = renderInfo.getCurrentCamera()->getViewport()->width();\n            int screenH = renderInfo.getCurrentCamera()->getViewport()->height();\n            double imageaspect = (double)mWidth/(double)mHeight;\n            int leftPadding = std::max(0, static_cast<int>(screenW - screenH * imageaspect) / 2);\n            int topPadding = std::max(0, static_cast<int>(screenH - screenW / imageaspect) / 2);\n            int width = screenW - leftPadding*2;\n            int height = screenH - topPadding*2;\n            mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE);\n            mImage->scaleImage(mWidth, mHeight, 1);\n        }\n    private:\n        int mWidth;\n        int mHeight;\n        osg::ref_ptr<osg::Image> mImage;\n    };\n\n    ScreenshotManager::ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, osg::ref_ptr<osg::Group> sceneRoot, Resource::ResourceSystem* resourceSystem, Water* water)\n        : mViewer(viewer)\n        , mRootNode(rootNode)\n        , mSceneRoot(sceneRoot)\n        , mDrawCompleteCallback(new NotifyDrawCompletedCallback)\n        , mResourceSystem(resourceSystem)\n        , mWater(water)\n    {\n    }\n\n    ScreenshotManager::~ScreenshotManager()\n    {\n    }\n\n    void ScreenshotManager::screenshot(osg::Image* image, int w, int h)\n    {\n        osg::Camera* camera = mViewer->getCamera();\n        osg::ref_ptr<osg::Drawable> tempDrw = new osg::Drawable;\n        tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h));\n        tempDrw->setCullingActive(false);\n        tempDrw->getOrCreateStateSet()->setRenderBinDetails(100, \"RenderBin\", osg::StateSet::USE_RENDERBIN_DETAILS); // so its after all scene bins but before POST_RENDER gui camera\n        camera->addChild(tempDrw);\n        traversalsAndWait(mViewer->getFrameStamp()->getFrameNumber());\n        // now that we've \"used up\" the current frame, get a fresh frame number for the next frame() following after the screenshot is completed\n        mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());\n        camera->removeChild(tempDrw);\n    }\n\n    bool ScreenshotManager::screenshot360(osg::Image* image)\n    {\n        int screenshotW = mViewer->getCamera()->getViewport()->width();\n        int screenshotH = mViewer->getCamera()->getViewport()->height();\n        Screenshot360Type screenshotMapping = Spherical;\n\n        const std::string& settingStr = Settings::Manager::getString(\"screenshot type\", \"Video\");\n        std::vector<std::string> settingArgs;\n        Misc::StringUtils::split(settingStr, settingArgs);\n\n        if (settingArgs.size() > 0)\n        {\n            std::string typeStrings[4] = {\"spherical\", \"cylindrical\", \"planet\", \"cubemap\"};\n            bool found = false;\n\n            for (int i = 0; i < 4; ++i)\n            {\n                if (settingArgs[0].compare(typeStrings[i]) == 0)\n                {\n                    screenshotMapping = static_cast<Screenshot360Type>(i);\n                    found = true;\n                    break;\n                }\n            }\n\n            if (!found)\n            {\n                Log(Debug::Warning) << \"Wrong screenshot type: \" << settingArgs[0] << \".\";\n                return false;\n            }\n        }\n\n        // planet mapping needs higher resolution\n        int cubeSize = screenshotMapping == Planet ? screenshotW : screenshotW / 2;\n\n        if (settingArgs.size() > 1)\n            screenshotW = std::min(10000, std::atoi(settingArgs[1].c_str()));\n\n        if (settingArgs.size() > 2)\n            screenshotH = std::min(10000, std::atoi(settingArgs[2].c_str()));\n\n        if (settingArgs.size() > 3)\n            cubeSize = std::min(5000, std::atoi(settingArgs[3].c_str()));\n\n        bool rawCubemap = screenshotMapping == RawCubemap;\n\n        if (rawCubemap)\n            screenshotW = cubeSize * 6;  // the image will consist of 6 cube sides in a row\n        else if (screenshotMapping == Planet)\n            screenshotH = screenshotW;   // use square resolution for planet mapping\n\n        std::vector<osg::ref_ptr<osg::Image>> images;\n\n        for (int i = 0; i < 6; ++i)\n            images.push_back(new osg::Image);\n\n        osg::Vec3 directions[6] = {\n            rawCubemap ? osg::Vec3(1,0,0) : osg::Vec3(0,0,1),\n            osg::Vec3(0,0,-1),\n            osg::Vec3(-1,0,0),\n            rawCubemap ? osg::Vec3(0,0,1) : osg::Vec3(1,0,0),\n            osg::Vec3(0,1,0),\n            osg::Vec3(0,-1,0)};\n\n        double rotations[] = {\n            -osg::PI / 2.0,\n            osg::PI / 2.0,\n            osg::PI,\n            0,\n            osg::PI / 2.0,\n            osg::PI / 2.0 };\n\n        for (int i = 0; i < 6; ++i) // for each cubemap side\n        {\n            osg::Matrixd transform = osg::Matrixd::rotate(osg::Vec3(0,0,-1), directions[i]);\n\n            if (!rawCubemap)\n                transform *= osg::Matrixd::rotate(rotations[i],osg::Vec3(0,0,-1));\n\n            osg::Image *sideImage = images[i].get();\n            makeCubemapScreenshot(sideImage, cubeSize, cubeSize, transform);\n\n            if (!rawCubemap)\n                sideImage->flipHorizontal();\n        }\n\n        if (rawCubemap) // for raw cubemap don't run on GPU, just merge the images\n        {\n            image->allocateImage(cubeSize * 6,cubeSize,images[0]->r(),images[0]->getPixelFormat(),images[0]->getDataType());\n\n            for (int i = 0; i < 6; ++i)\n                osg::copyImage(images[i].get(),0,0,0,images[i]->s(),images[i]->t(),images[i]->r(),image,i * cubeSize,0,0);\n\n            return true;\n        }\n\n        // run on GPU now:\n        osg::ref_ptr<osg::TextureCubeMap> cubeTexture (new osg::TextureCubeMap);\n        cubeTexture->setResizeNonPowerOfTwoHint(false);\n\n        cubeTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::NEAREST);\n        cubeTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST);\n\n        cubeTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        cubeTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n        for (int i = 0; i < 6; ++i)\n            cubeTexture->setImage(i, images[i].get());\n\n        osg::ref_ptr<osg::Camera> screenshotCamera(new osg::Camera);\n        osg::ref_ptr<osg::ShapeDrawable> quad(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0,0,0), 2.0)));\n\n        std::map<std::string, std::string> defineMap;\n\n        Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();\n        osg::ref_ptr<osg::Shader> fragmentShader(shaderMgr.getShader(\"s360_fragment.glsl\", defineMap,osg::Shader::FRAGMENT));\n        osg::ref_ptr<osg::Shader> vertexShader(shaderMgr.getShader(\"s360_vertex.glsl\", defineMap, osg::Shader::VERTEX));\n        osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;\n\n        osg::ref_ptr<osg::Program> program(new osg::Program);\n        program->addShader(fragmentShader);\n        program->addShader(vertexShader);\n        stateset->setAttributeAndModes(program, osg::StateAttribute::ON);\n\n        stateset->addUniform(new osg::Uniform(\"cubeMap\", 0));\n        stateset->addUniform(new osg::Uniform(\"mapping\", screenshotMapping));\n        stateset->setTextureAttributeAndModes(0, cubeTexture, osg::StateAttribute::ON);\n\n        quad->setStateSet(stateset);\n        quad->setUpdateCallback(nullptr);\n\n        screenshotCamera->addChild(quad);\n\n        renderCameraToImage(screenshotCamera, image, screenshotW, screenshotH);\n\n        return true;\n    }\n\n    void ScreenshotManager::traversalsAndWait(unsigned int frame)\n    {\n        // Ref https://gitlab.com/OpenMW/openmw/-/issues/6013\n        mDrawCompleteCallback->reset(frame);\n        mViewer->getCamera()->setFinalDrawCallback(mDrawCompleteCallback);\n\n        mViewer->eventTraversal();\n        mViewer->updateTraversal();\n        mViewer->renderingTraversals();\n        mDrawCompleteCallback->waitTillDone();\n    }\n\n    void ScreenshotManager::renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h)\n    {\n        camera->setNodeMask(Mask_RenderToTexture);\n        camera->attach(osg::Camera::COLOR_BUFFER, image);\n        camera->setRenderOrder(osg::Camera::PRE_RENDER);\n        camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);\n        camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT,osg::Camera::PIXEL_BUFFER_RTT);\n\n        camera->setViewport(0, 0, w, h);\n\n        osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);\n        texture->setInternalFormat(GL_RGB);\n        texture->setTextureSize(w,h);\n        texture->setResizeNonPowerOfTwoHint(false);\n        texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n        camera->attach(osg::Camera::COLOR_BUFFER,texture);\n\n        image->setDataType(GL_UNSIGNED_BYTE);\n        image->setPixelFormat(texture->getInternalFormat());\n\n        mRootNode->addChild(camera);\n\n        MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false);\n\n        // The draw needs to complete before we can copy back our image.\n        traversalsAndWait(0);\n\n        MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOff();\n\n        // now that we've \"used up\" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed\n        mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());\n\n        camera->removeChildren(0, camera->getNumChildren());\n        mRootNode->removeChild(camera);\n    }\n\n    void ScreenshotManager::makeCubemapScreenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform)\n    {\n        osg::ref_ptr<osg::Camera> rttCamera (new osg::Camera);\n        float nearClip = Settings::Manager::getFloat(\"near clip\", \"Camera\");\n        float viewDistance = Settings::Manager::getFloat(\"viewing distance\", \"Camera\");\n        // each cubemap side sees 90 degrees\n        rttCamera->setProjectionMatrixAsPerspective(90.0, w/float(h), nearClip, viewDistance);\n        rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform);\n\n        rttCamera->setUpdateCallback(new NoTraverseCallback);\n        rttCamera->addChild(mSceneRoot);\n\n        rttCamera->addChild(mWater->getReflectionCamera());\n        rttCamera->addChild(mWater->getRefractionCamera());\n\n        rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));\n\n        rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\n        renderCameraToImage(rttCamera.get(),image,w,h);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/screenshotmanager.hpp",
    "content": "#ifndef MWRENDER_SCREENSHOTMANAGER_H\n#define MWRENDER_SCREENSHOTMANAGER_H\n\n#include <memory>\n\n#include <osg/Group>\n#include <osg/ref_ptr>\n\n#include <osgViewer/Viewer>\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWRender\n{\n    class Water;\n    class NotifyDrawCompletedCallback;\n\n    class ScreenshotManager\n    {\n    public:\n        ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, osg::ref_ptr<osg::Group> sceneRoot, Resource::ResourceSystem* resourceSystem, Water* water);\n        ~ScreenshotManager();\n\n        void screenshot(osg::Image* image, int w, int h);\n        bool screenshot360(osg::Image* image);\n\n    private:\n        osg::ref_ptr<osgViewer::Viewer> mViewer;\n        osg::ref_ptr<osg::Group> mRootNode;\n        osg::ref_ptr<osg::Group> mSceneRoot;\n        osg::ref_ptr<NotifyDrawCompletedCallback> mDrawCompleteCallback;\n        Resource::ResourceSystem* mResourceSystem;\n        Water* mWater;\n\n        void traversalsAndWait(unsigned int frame);\n        void renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h);\n        void makeCubemapScreenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd());\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/sky.cpp",
    "content": "#include \"sky.hpp\"\n\n#include <cmath>\n\n#include <osg/ClipPlane>\n#include <osg/Fog>\n#include <osg/Transform>\n#include <osg/Depth>\n#include <osg/Geometry>\n#include <osg/Material>\n#include <osg/TexEnvCombine>\n#include <osg/TexMat>\n#include <osg/OcclusionQueryNode>\n#include <osg/ColorMask>\n#include <osg/PositionAttitudeTransform>\n#include <osg/BlendFunc>\n#include <osg/AlphaFunc>\n#include <osg/PolygonOffset>\n#include <osg/Version>\n#include <osg/observer_ptr>\n\n#include <osgParticle/BoxPlacer>\n#include <osgParticle/ModularEmitter>\n#include <osgParticle/ParticleSystem>\n#include <osgParticle/ParticleSystemUpdater>\n#include <osgParticle/ConstantRateCounter>\n#include <osgParticle/RadialShooter>\n\n#include <osgParticle/Operator>\n#include <osgParticle/ModularProgram>\n\n#include <components/misc/rng.hpp>\n\n#include <components/misc/resourcehelpers.hpp>\n\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/imagemanager.hpp>\n\n#include <components/vfs/manager.hpp>\n#include <components/fallback/fallback.hpp>\n\n#include <components/sceneutil/util.hpp>\n#include <components/sceneutil/statesetupdater.hpp>\n#include <components/sceneutil/controller.hpp>\n#include <components/sceneutil/visitor.hpp>\n#include <components/sceneutil/shadow.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include <components/nifosg/particle.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"vismask.hpp\"\n#include \"renderbin.hpp\"\n\nnamespace\n{\n    osg::ref_ptr<osg::Material> createAlphaTrackingUnlitMaterial()\n    {\n        osg::ref_ptr<osg::Material> mat = new osg::Material;\n        mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n        mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n        mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f));\n        mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n        mat->setColorMode(osg::Material::DIFFUSE);\n        return mat;\n    }\n\n    osg::ref_ptr<osg::Material> createUnlitMaterial()\n    {\n        osg::ref_ptr<osg::Material> mat = new osg::Material;\n        mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n        mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n        mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f));\n        mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n        mat->setColorMode(osg::Material::OFF);\n        return mat;\n    }\n\n    osg::ref_ptr<osg::Geometry> createTexturedQuad(int numUvSets=1)\n    {\n        osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;\n\n        osg::ref_ptr<osg::Vec3Array> verts = new osg::Vec3Array;\n        verts->push_back(osg::Vec3f(-0.5, -0.5, 0));\n        verts->push_back(osg::Vec3f(-0.5, 0.5, 0));\n        verts->push_back(osg::Vec3f(0.5, 0.5, 0));\n        verts->push_back(osg::Vec3f(0.5, -0.5, 0));\n\n        geom->setVertexArray(verts);\n\n        osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array;\n        texcoords->push_back(osg::Vec2f(0, 0));\n        texcoords->push_back(osg::Vec2f(0, 1));\n        texcoords->push_back(osg::Vec2f(1, 1));\n        texcoords->push_back(osg::Vec2f(1, 0));\n\n        osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;\n        colors->push_back(osg::Vec4(1.f, 1.f, 1.f, 1.f));\n        geom->setColorArray(colors, osg::Array::BIND_OVERALL);\n\n        for (int i=0; i<numUvSets; ++i)\n            geom->setTexCoordArray(i, texcoords, osg::Array::BIND_PER_VERTEX);\n\n        geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));\n\n        return geom;\n    }\n\n}\n\nnamespace MWRender\n{\n\nclass AtmosphereUpdater : public SceneUtil::StateSetUpdater\n{\npublic:\n    void setEmissionColor(const osg::Vec4f& emissionColor)\n    {\n        mEmissionColor = emissionColor;\n    }\n\nprotected:\n    void setDefaults(osg::StateSet* stateset) override\n    {\n        stateset->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n    }\n\n    void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override\n    {\n        osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));\n        mat->setEmission(osg::Material::FRONT_AND_BACK, mEmissionColor);\n    }\n\nprivate:\n    osg::Vec4f mEmissionColor;\n};\n\nclass AtmosphereNightUpdater : public SceneUtil::StateSetUpdater\n{\npublic:\n    AtmosphereNightUpdater(Resource::ImageManager* imageManager)\n    {\n        // we just need a texture, its contents don't really matter\n        mTexture = new osg::Texture2D(imageManager->getWarningImage());\n    }\n\n    void setFade(const float fade)\n    {\n        mColor.a() = fade;\n    }\n\nprotected:\n    void setDefaults(osg::StateSet* stateset) override\n    {\n        osg::ref_ptr<osg::TexEnvCombine> texEnv (new osg::TexEnvCombine);\n        texEnv->setCombine_Alpha(osg::TexEnvCombine::MODULATE);\n        texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);\n        texEnv->setSource1_Alpha(osg::TexEnvCombine::CONSTANT);\n        texEnv->setCombine_RGB(osg::TexEnvCombine::REPLACE);\n        texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);\n\n        stateset->setTextureAttributeAndModes(1, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n        stateset->setTextureAttributeAndModes(1, texEnv, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n    }\n\n    void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override\n    {\n        osg::TexEnvCombine* texEnv = static_cast<osg::TexEnvCombine*>(stateset->getTextureAttribute(1, osg::StateAttribute::TEXENV));\n        texEnv->setConstantColor(mColor);\n    }\n\n    osg::ref_ptr<osg::Texture2D> mTexture;\n\n    osg::Vec4f mColor;\n};\n\nclass CloudUpdater : public SceneUtil::StateSetUpdater\n{\npublic:\n    CloudUpdater()\n        : mAnimationTimer(0.f)\n        , mOpacity(0.f)\n    {\n    }\n\n    void setAnimationTimer(float timer)\n    {\n        mAnimationTimer = timer;\n    }\n\n    void setTexture(osg::ref_ptr<osg::Texture2D> texture)\n    {\n        mTexture = texture;\n    }\n    void setEmissionColor(const osg::Vec4f& emissionColor)\n    {\n        mEmissionColor = emissionColor;\n    }\n    void setOpacity(float opacity)\n    {\n        mOpacity = opacity;\n    }\n\nprotected:\n    void setDefaults(osg::StateSet *stateset) override\n    {\n        osg::ref_ptr<osg::TexMat> texmat (new osg::TexMat);\n        stateset->setTextureAttributeAndModes(0, texmat, osg::StateAttribute::ON);\n        stateset->setTextureAttributeAndModes(1, texmat, osg::StateAttribute::ON);\n        stateset->setAttribute(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n\n        // need to set opacity on a separate texture unit, diffuse alpha is used by the vertex colors already\n        osg::ref_ptr<osg::TexEnvCombine> texEnvCombine (new osg::TexEnvCombine);\n        texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);\n        texEnvCombine->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);\n        texEnvCombine->setSource1_Alpha(osg::TexEnvCombine::CONSTANT);\n        texEnvCombine->setConstantColor(osg::Vec4f(1,1,1,1));\n        texEnvCombine->setCombine_Alpha(osg::TexEnvCombine::MODULATE);\n        texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE);\n\n        stateset->setTextureAttributeAndModes(1, texEnvCombine, osg::StateAttribute::ON);\n\n        stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n        stateset->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n    }\n\n    void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) override\n    {\n        osg::TexMat* texMat = static_cast<osg::TexMat*>(stateset->getTextureAttribute(0, osg::StateAttribute::TEXMAT));\n        texMat->setMatrix(osg::Matrix::translate(osg::Vec3f(0, -mAnimationTimer, 0.f)));\n\n        stateset->setTextureAttribute(0, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n        stateset->setTextureAttribute(1, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n\n        osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));\n        mat->setEmission(osg::Material::FRONT_AND_BACK, mEmissionColor);\n\n        osg::TexEnvCombine* texEnvCombine = static_cast<osg::TexEnvCombine*>(stateset->getTextureAttribute(1, osg::StateAttribute::TEXENV));\n        texEnvCombine->setConstantColor(osg::Vec4f(1,1,1,mOpacity));\n    }\n\nprivate:\n    float mAnimationTimer;\n    osg::ref_ptr<osg::Texture2D> mTexture;\n    osg::Vec4f mEmissionColor;\n    float mOpacity;\n};\n\n/// Transform that removes the eyepoint of the modelview matrix,\n/// i.e. its children are positioned relative to the camera.\nclass CameraRelativeTransform : public osg::Transform\n{\npublic:\n    CameraRelativeTransform()\n    {\n        // Culling works in node-local space, not in camera space, so we can't cull this node correctly\n        // That's not a problem though, children of this node can be culled just fine\n        // Just make sure you do not place a CameraRelativeTransform deep in the scene graph\n        setCullingActive(false);\n\n        addCullCallback(new CullCallback);\n    }\n\n    CameraRelativeTransform(const CameraRelativeTransform& copy, const osg::CopyOp& copyop)\n        : osg::Transform(copy, copyop)\n    {\n    }\n\n    META_Node(MWRender, CameraRelativeTransform)\n\n    const osg::Vec3f& getLastViewPoint() const\n    {\n        return mViewPoint;\n    }\n\n    bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const override\n    {\n        if (nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR)\n        {\n            mViewPoint = static_cast<osgUtil::CullVisitor*>(nv)->getViewPoint();\n        }\n\n        if (_referenceFrame==RELATIVE_RF)\n        {\n            matrix.setTrans(osg::Vec3f(0.f,0.f,0.f));\n            return false;\n        }\n        else // absolute\n        {\n            matrix.makeIdentity();\n            return true;\n        }\n    }\n\n    osg::BoundingSphere computeBound() const override\n    {\n        return osg::BoundingSphere(osg::Vec3f(0,0,0), 0);\n    }\n\n    class CullCallback : public osg::NodeCallback\n    {\n    public:\n        void operator() (osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n\n            // XXX have to remove unwanted culling plane of the water reflection camera\n\n            // Remove all planes that aren't from the standard frustum\n            unsigned int numPlanes = 4;\n            if (cv->getCullingMode() & osg::CullSettings::NEAR_PLANE_CULLING)\n                ++numPlanes;\n            if (cv->getCullingMode() & osg::CullSettings::FAR_PLANE_CULLING)\n                ++numPlanes;\n\n            unsigned int mask = 0x1;\n            unsigned int resultMask = cv->getProjectionCullingStack().back().getFrustum().getResultMask();\n            for (unsigned int i=0; i<cv->getProjectionCullingStack().back().getFrustum().getPlaneList().size(); ++i)\n            {\n                if (i >= numPlanes)\n                {\n                    // turn off this culling plane\n                    resultMask &= (~mask);\n                }\n\n                mask <<= 1;\n            }\n\n            cv->getProjectionCullingStack().back().getFrustum().setResultMask(resultMask);\n            cv->getCurrentCullingSet().getFrustum().setResultMask(resultMask);\n\n            cv->getProjectionCullingStack().back().pushCurrentMask();\n            cv->getCurrentCullingSet().pushCurrentMask();\n\n            traverse(node, nv);\n\n            cv->getProjectionCullingStack().back().popCurrentMask();\n            cv->getCurrentCullingSet().popCurrentMask();\n        }\n    };\nprivate:\n    // viewPoint for the current frame\n    mutable osg::Vec3f mViewPoint;\n};\n\nclass ModVertexAlphaVisitor : public osg::NodeVisitor\n{\npublic:\n    ModVertexAlphaVisitor(int meshType)\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        , mMeshType(meshType)\n    {\n    }\n\n    void apply(osg::Drawable& drw) override\n    {\n        osg::Geometry* geom = drw.asGeometry();\n        if (!geom)\n            return;\n\n        osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(geom->getVertexArray()->getNumElements());\n        for (unsigned int i=0; i<colors->size(); ++i)\n        {\n            float alpha = 1.f;\n            if (mMeshType == 0) alpha = (i%2) ? 0.f : 1.f; // this is a cylinder, so every second vertex belongs to the bottom-most row\n            else if (mMeshType == 1)\n            {\n                if (i>= 49 && i <= 64) alpha = 0.f; // bottom-most row\n                else if (i>= 33 && i <= 48) alpha = 0.25098; // second row\n                else alpha = 1.f;\n            }\n            else if (mMeshType == 2)\n            {\n                if (geom->getColorArray())\n                {\n                    osg::Vec4Array* origColors = static_cast<osg::Vec4Array*>(geom->getColorArray());\n                    alpha = ((*origColors)[i].x() == 1.f) ? 1.f : 0.f;\n                }\n                else\n                    alpha = 1.f;\n            }\n\n            (*colors)[i] = osg::Vec4f(0.f, 0.f, 0.f, alpha);\n        }\n\n        geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);\n    }\n\nprivate:\n    int mMeshType;\n};\n\n/// @brief Hides the node subgraph if the eye point is below water.\n/// @note Must be added as cull callback.\n/// @note Meant to be used on a node that is child of a CameraRelativeTransform.\n/// The current view point must be retrieved by the CameraRelativeTransform since we can't get it anymore once we are in camera-relative space.\nclass UnderwaterSwitchCallback : public osg::NodeCallback\n{\npublic:\n    UnderwaterSwitchCallback(CameraRelativeTransform* cameraRelativeTransform)\n        : mCameraRelativeTransform(cameraRelativeTransform)\n        , mEnabled(true)\n        , mWaterLevel(0.f)\n    {\n    }\n\n    bool isUnderwater()\n    {\n        osg::Vec3f viewPoint = mCameraRelativeTransform->getLastViewPoint();\n        return mEnabled && viewPoint.z() < mWaterLevel;\n    }\n\n    void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n    {\n        if (isUnderwater())\n            return;\n\n        traverse(node, nv);\n    }\n\n    void setEnabled(bool enabled)\n    {\n        mEnabled = enabled;\n    }\n    void setWaterLevel(float waterLevel)\n    {\n        mWaterLevel = waterLevel;\n    }\n\nprivate:\n    osg::ref_ptr<CameraRelativeTransform> mCameraRelativeTransform;\n    bool mEnabled;\n    float mWaterLevel;\n};\n\n/// A base class for the sun and moons.\nclass CelestialBody\n{\npublic:\n    CelestialBody(osg::Group* parentNode, float scaleFactor, int numUvSets, unsigned int visibleMask=~0u)\n        : mVisibleMask(visibleMask)\n    {\n        mGeom = createTexturedQuad(numUvSets);\n        mTransform = new osg::PositionAttitudeTransform;\n        mTransform->setNodeMask(mVisibleMask);\n        mTransform->setScale(osg::Vec3f(450,450,450) * scaleFactor);\n        mTransform->addChild(mGeom);\n\n        parentNode->addChild(mTransform);\n    }\n\n    virtual ~CelestialBody() {}\n\n    virtual void adjustTransparency(const float ratio) = 0;\n\n    void setVisible(bool visible)\n    {\n        mTransform->setNodeMask(visible ? mVisibleMask : 0);\n    }\n\nprotected:\n    unsigned int mVisibleMask;\n    static const float mDistance;\n    osg::ref_ptr<osg::PositionAttitudeTransform> mTransform;\n    osg::ref_ptr<osg::Geometry> mGeom;\n};\n\nconst float CelestialBody::mDistance = 1000.0f;\n\nclass Sun : public CelestialBody\n{\npublic:\n    Sun(osg::Group* parentNode, Resource::ImageManager& imageManager)\n        : CelestialBody(parentNode, 1.0f, 1, Mask_Sun)\n        , mUpdater(new Updater)\n    {\n        mTransform->addUpdateCallback(mUpdater);\n\n        osg::ref_ptr<osg::Texture2D> sunTex (new osg::Texture2D(imageManager.getImage(\"textures/tx_sun_05.dds\")));\n        sunTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        sunTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n        mGeom->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON);\n\n        osg::ref_ptr<osg::Group> queryNode (new osg::Group);\n        // Need to render after the world geometry so we can correctly test for occlusions\n        osg::StateSet* stateset = queryNode->getOrCreateStateSet();\n        stateset->setRenderBinDetails(RenderBin_OcclusionQuery, \"RenderBin\");\n        stateset->setNestRenderBins(false);\n        // Set up alpha testing on the occlusion testing subgraph, that way we can get the occlusion tested fragments to match the circular shape of the sun\n        osg::ref_ptr<osg::AlphaFunc> alphaFunc (new osg::AlphaFunc);\n        alphaFunc->setFunction(osg::AlphaFunc::GREATER, 0.8);\n        stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON);\n        stateset->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON);\n        stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON);\n        // Disable writing to the color buffer. We are using this geometry for visibility tests only.\n        osg::ref_ptr<osg::ColorMask> colormask (new osg::ColorMask(0, 0, 0, 0));\n        stateset->setAttributeAndModes(colormask, osg::StateAttribute::ON);\n\n        mTransform->addChild(queryNode);\n\n        mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true);\n        mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false);\n\n        createSunFlash(imageManager);\n        createSunGlare();\n    }\n\n    ~Sun()\n    {\n        mTransform->removeUpdateCallback(mUpdater);\n        destroySunFlash();\n        destroySunGlare();\n    }\n\n    void setColor(const osg::Vec4f& color)\n    {\n        mUpdater->mColor.r() = color.r();\n        mUpdater->mColor.g() = color.g();\n        mUpdater->mColor.b() = color.b();\n    }\n\n    void adjustTransparency(const float ratio) override\n    {\n        mUpdater->mColor.a() = ratio;\n        if (mSunGlareCallback)\n            mSunGlareCallback->setGlareView(ratio);\n        if (mSunFlashCallback)\n            mSunFlashCallback->setGlareView(ratio);\n    }\n\n    void setDirection(const osg::Vec3f& direction)\n    {\n        osg::Vec3f normalizedDirection = direction / direction.length();\n        mTransform->setPosition(normalizedDirection * mDistance);\n\n        osg::Quat quat;\n        quat.makeRotate(osg::Vec3f(0.0f, 0.0f, 1.0f), normalizedDirection);\n        mTransform->setAttitude(quat);\n    }\n\n    void setGlareTimeOfDayFade(float val)\n    {\n        if (mSunGlareCallback)\n            mSunGlareCallback->setTimeOfDayFade(val);\n    }\n\nprivate:\n    class DummyComputeBoundCallback : public osg::Node::ComputeBoundingSphereCallback\n    {\n    public:\n        osg::BoundingSphere computeBound(const osg::Node& node) const override { return osg::BoundingSphere(); }\n    };\n\n    /// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels.\n    osg::ref_ptr<osg::OcclusionQueryNode> createOcclusionQueryNode(osg::Group* parent, bool queryVisible)\n    {\n        osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode;\n        oqn->setQueriesEnabled(true);\n\n#if OSG_VERSION_GREATER_OR_EQUAL(3, 6, 5)\n        // With OSG 3.6.5, the method of providing user defined query geometry has been completely replaced\n        osg::ref_ptr<osg::QueryGeometry> queryGeom = new osg::QueryGeometry(oqn->getName());\n#else\n        osg::ref_ptr<osg::QueryGeometry> queryGeom = oqn->getQueryGeometry();\n#endif\n\n        // Make it fast! A DYNAMIC query geometry means we can't break frame until the flare is rendered (which is rendered after all the other geometry,\n        // so that would be pretty bad). STATIC should be safe, since our node's local bounds are static, thus computeBounds() which modifies the queryGeometry\n        // is only called once.\n        // Note the debug geometry setDebugDisplay(true) is always DYNAMIC and that can't be changed, not a big deal.\n        queryGeom->setDataVariance(osg::Object::STATIC);\n\n        // Set up the query geometry to match the actual sun's rendering shape. osg::OcclusionQueryNode wasn't originally intended to allow this,\n        // normally it would automatically adjust the query geometry to match the sub graph's bounding box. The below hack is needed to\n        // circumvent this.\n        queryGeom->setVertexArray(mGeom->getVertexArray());\n        queryGeom->setTexCoordArray(0, mGeom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);\n        queryGeom->removePrimitiveSet(0, queryGeom->getNumPrimitiveSets());\n        queryGeom->addPrimitiveSet(mGeom->getPrimitiveSet(0));\n\n        // Hack to disable unwanted awful code inside OcclusionQueryNode::computeBound.\n        oqn->setComputeBoundingSphereCallback(new DummyComputeBoundCallback);\n        // Still need a proper bounding sphere.\n        oqn->setInitialBound(queryGeom->getBound());\n\n#if OSG_VERSION_GREATER_OR_EQUAL(3, 6, 5)\n        oqn->setQueryGeometry(queryGeom.release());\n#endif\n\n        osg::StateSet* queryStateSet = new osg::StateSet;\n        if (queryVisible)\n        {\n            osg::ref_ptr<osg::Depth> depth (new osg::Depth);\n            depth->setFunction(osg::Depth::LEQUAL);\n            // This is a trick to make fragments written by the query always use the maximum depth value,\n            // without having to retrieve the current far clipping distance.\n            // We want the sun glare to be \"infinitely\" far away.\n            depth->setZNear(1.0);\n            depth->setZFar(1.0);\n            depth->setWriteMask(false);\n            queryStateSet->setAttributeAndModes(depth, osg::StateAttribute::ON);\n        }\n        else\n        {\n            queryStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n        }\n        oqn->setQueryStateSet(queryStateSet);\n\n        parent->addChild(oqn);\n\n        return oqn;\n    }\n\n    void createSunFlash(Resource::ImageManager& imageManager)\n    {\n        osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(imageManager.getImage(\"textures/tx_sun_flash_grey_05.dds\")));\n        tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n        osg::ref_ptr<osg::PositionAttitudeTransform> transform (new osg::PositionAttitudeTransform);\n        const float scale = 2.6f;\n        transform->setScale(osg::Vec3f(scale,scale,scale));\n\n        mTransform->addChild(transform);\n\n        osg::ref_ptr<osg::Geometry> geom = createTexturedQuad();\n        transform->addChild(geom);\n\n        osg::StateSet* stateset = geom->getOrCreateStateSet();\n\n        stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON);\n        stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n        stateset->setRenderBinDetails(RenderBin_SunGlare, \"RenderBin\");\n        stateset->setNestRenderBins(false);\n\n        mSunFlashNode = transform;\n\n        mSunFlashCallback = new SunFlashCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels);\n        mSunFlashNode->addCullCallback(mSunFlashCallback);\n    }\n    void destroySunFlash()\n    {\n        if (mSunFlashNode)\n        {\n            mSunFlashNode->removeCullCallback(mSunFlashCallback);\n            mSunFlashCallback = nullptr;\n        }\n    }\n\n    void createSunGlare()\n    {\n        osg::ref_ptr<osg::Camera> camera (new osg::Camera);\n        camera->setProjectionMatrix(osg::Matrix::identity());\n        camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // add to skyRoot instead?\n        camera->setViewMatrix(osg::Matrix::identity());\n        camera->setClearMask(0);\n        camera->setRenderOrder(osg::Camera::NESTED_RENDER);\n        camera->setAllowEventFocus(false);\n\n        osg::ref_ptr<osg::Geometry> geom = osg::createTexturedQuadGeometry(osg::Vec3f(-1,-1,0), osg::Vec3f(2,0,0), osg::Vec3f(0,2,0));\n\n        camera->addChild(geom);\n\n        osg::StateSet* stateset = geom->getOrCreateStateSet();\n\n        stateset->setRenderBinDetails(RenderBin_SunGlare, \"RenderBin\");\n        stateset->setNestRenderBins(false);\n        stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n\n        // set up additive blending\n        osg::ref_ptr<osg::BlendFunc> blendFunc (new osg::BlendFunc);\n        blendFunc->setSource(osg::BlendFunc::SRC_ALPHA);\n        blendFunc->setDestination(osg::BlendFunc::ONE);\n        stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);\n\n        mSunGlareCallback = new SunGlareCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels, mTransform);\n        mSunGlareNode = camera;\n\n        mSunGlareNode->addCullCallback(mSunGlareCallback);\n\n        mTransform->addChild(camera);\n    }\n    void destroySunGlare()\n    {\n        if (mSunGlareNode)\n        {\n            mSunGlareNode->removeCullCallback(mSunGlareCallback);\n            mSunGlareCallback = nullptr;\n        }\n    }\n\n    class Updater : public SceneUtil::StateSetUpdater\n    {\n    public:\n        osg::Vec4f mColor;\n\n        Updater()\n            : mColor(1.f, 1.f, 1.f, 1.f)\n        {\n        }\n\n        void setDefaults(osg::StateSet* stateset) override\n        {\n            stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON);\n        }\n\n        void apply(osg::StateSet* stateset, osg::NodeVisitor*) override\n        {\n            osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));\n            mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,mColor.a()));\n            mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(mColor.r(), mColor.g(), mColor.b(), 1));\n        }\n    };\n\n    class OcclusionCallback : public osg::NodeCallback\n    {\n    public:\n        OcclusionCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal)\n            : mOcclusionQueryVisiblePixels(oqnVisible)\n            , mOcclusionQueryTotalPixels(oqnTotal)\n        {\n        }\n\n    protected:\n        float getVisibleRatio (osg::Camera* camera)\n        {\n            int visible = mOcclusionQueryVisiblePixels->getQueryGeometry()->getNumPixels(camera);\n            int total = mOcclusionQueryTotalPixels->getQueryGeometry()->getNumPixels(camera);\n\n            float visibleRatio = 0.f;\n            if (total > 0)\n                visibleRatio = static_cast<float>(visible) / static_cast<float>(total);\n\n            float dt = MWBase::Environment::get().getFrameDuration();\n\n            float lastRatio = mLastRatio[osg::observer_ptr<osg::Camera>(camera)];\n\n            float change = dt*10;\n\n            if (visibleRatio > lastRatio)\n                visibleRatio = std::min(visibleRatio, lastRatio + change);\n            else\n                visibleRatio = std::max(visibleRatio, lastRatio - change);\n\n            mLastRatio[osg::observer_ptr<osg::Camera>(camera)] = visibleRatio;\n\n            return visibleRatio;\n        }\n\n    private:\n        osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;\n        osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;\n\n        std::map<osg::observer_ptr<osg::Camera>, float> mLastRatio;\n    };\n\n    /// SunFlashCallback handles fading/scaling of a node depending on occlusion query result. Must be attached as a cull callback.\n    class SunFlashCallback : public OcclusionCallback\n    {\n    public:\n        SunFlashCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal)\n            : OcclusionCallback(oqnVisible, oqnTotal)\n            , mGlareView(1.f)\n        {\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n\n            float visibleRatio = getVisibleRatio(cv->getCurrentCamera());\n\n            osg::ref_ptr<osg::StateSet> stateset;\n\n            if (visibleRatio > 0.f)\n            {\n                const float fadeThreshold = 0.1;\n                if (visibleRatio < fadeThreshold)\n                {\n                    float fade = 1.f - (fadeThreshold - visibleRatio) / fadeThreshold;\n                    osg::ref_ptr<osg::Material> mat (createUnlitMaterial());\n                    mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade*mGlareView));\n                    stateset = new osg::StateSet;\n                    stateset->setAttributeAndModes(mat, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n                }\n\n                const float threshold = 0.6;\n                visibleRatio = visibleRatio * (1.f - threshold) + threshold;\n            }\n\n            float scale = visibleRatio;\n\n            if (scale == 0.f)\n            {\n                // no traverse\n                return;\n            }\n            else\n            {\n                osg::Matrix modelView = *cv->getModelViewMatrix();\n\n                modelView.preMultScale(osg::Vec3f(visibleRatio, visibleRatio, visibleRatio));\n\n                if (stateset)\n                    cv->pushStateSet(stateset);\n\n                cv->pushModelViewMatrix(new osg::RefMatrix(modelView), osg::Transform::RELATIVE_RF);\n\n                traverse(node, nv);\n\n                cv->popModelViewMatrix();\n\n                if (stateset)\n                    cv->popStateSet();\n            }\n        }\n\n        void setGlareView(float value)\n        {\n            mGlareView = value;\n        }\n\n    private:\n        float mGlareView;\n    };\n\n\n    /// SunGlareCallback controls a full-screen glare effect depending on occlusion query result and the angle between sun and camera.\n    /// Must be attached as a cull callback to the node above the glare node.\n    class SunGlareCallback : public OcclusionCallback\n    {\n    public:\n        SunGlareCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal,\n                         osg::ref_ptr<osg::PositionAttitudeTransform> sunTransform)\n            : OcclusionCallback(oqnVisible, oqnTotal)\n            , mSunTransform(sunTransform)\n            , mTimeOfDayFade(1.f)\n            , mGlareView(1.f)\n        {\n            mColor = Fallback::Map::getColour(\"Weather_Sun_Glare_Fader_Color\");\n            mSunGlareFaderMax = Fallback::Map::getFloat(\"Weather_Sun_Glare_Fader_Max\");\n            mSunGlareFaderAngleMax = Fallback::Map::getFloat(\"Weather_Sun_Glare_Fader_Angle_Max\");\n\n            // Replicating a design flaw in MW. The color was being set on both ambient and emissive properties, which multiplies the result by two,\n            // then finally gets clamped by the fixed function pipeline. With the default INI settings, only the red component gets clamped,\n            // so the resulting color looks more orange than red.\n            mColor *= 2;\n            for (int i=0; i<3; ++i)\n                mColor[i] = std::min(1.f, mColor[i]);\n        }\n\n        void operator ()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n\n            float angleRadians = getAngleToSunInRadians(*cv->getCurrentRenderStage()->getInitialViewMatrix());\n            float visibleRatio = getVisibleRatio(cv->getCurrentCamera());\n\n            const float angleMaxRadians = osg::DegreesToRadians(mSunGlareFaderAngleMax);\n\n            float value = 1.f - std::min(1.f, angleRadians / angleMaxRadians);\n            float fade = value * mSunGlareFaderMax;\n\n            fade *= mTimeOfDayFade * mGlareView * visibleRatio;\n\n            if (fade == 0.f)\n            {\n                // no traverse\n                return;\n            }\n            else\n            {\n                osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);\n\n                osg::ref_ptr<osg::Material> mat (createUnlitMaterial());\n\n                mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade));\n                mat->setEmission(osg::Material::FRONT_AND_BACK, mColor);\n\n                stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);\n\n                cv->pushStateSet(stateset);\n                traverse(node, nv);\n                cv->popStateSet();\n            }\n        }\n\n        void setTimeOfDayFade(float val)\n        {\n            mTimeOfDayFade = val;\n        }\n\n        void setGlareView(float glareView)\n        {\n            mGlareView = glareView;\n        }\n\n    private:\n        float getAngleToSunInRadians(const osg::Matrix& viewMatrix) const\n        {\n            osg::Vec3d eye, center, up;\n            viewMatrix.getLookAt(eye, center, up);\n\n            osg::Vec3d forward = center - eye;\n            osg::Vec3d sun = mSunTransform->getPosition();\n\n            forward.normalize();\n            sun.normalize();\n            float angleRadians = std::acos(forward * sun);\n            return angleRadians;\n        }\n\n        osg::ref_ptr<osg::PositionAttitudeTransform> mSunTransform;\n        float mTimeOfDayFade;\n        float mGlareView;\n        osg::Vec4f mColor;\n        float mSunGlareFaderMax;\n        float mSunGlareFaderAngleMax;\n    };\n\n    osg::ref_ptr<Updater> mUpdater;\n    osg::ref_ptr<SunFlashCallback> mSunFlashCallback;\n    osg::ref_ptr<osg::Node> mSunFlashNode;\n    osg::ref_ptr<SunGlareCallback> mSunGlareCallback;\n    osg::ref_ptr<osg::Node> mSunGlareNode;\n    osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;\n    osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;\n};\n\nclass Moon : public CelestialBody\n{\npublic:\n    enum Type\n    {\n        Type_Masser = 0,\n        Type_Secunda\n    };\n\n    Moon(osg::Group* parentNode, Resource::ImageManager& imageManager, float scaleFactor, Type type)\n        : CelestialBody(parentNode, scaleFactor, 2)\n        , mType(type)\n        , mPhase(MoonState::Phase::Unspecified)\n        , mUpdater(new Updater(imageManager))\n    {\n        setPhase(MoonState::Phase::Full);\n        setVisible(true);\n\n        mGeom->addUpdateCallback(mUpdater);\n    }\n\n    ~Moon()\n    {\n        mGeom->removeUpdateCallback(mUpdater);\n    }\n\n    void adjustTransparency(const float ratio) override\n    {\n        mUpdater->mTransparency *= ratio;\n    }\n\n    void setState(const MoonState& state)\n    {\n        float radsX = ((state.mRotationFromHorizon) * static_cast<float>(osg::PI)) / 180.0f;\n        float radsZ = ((state.mRotationFromNorth) * static_cast<float>(osg::PI)) / 180.0f;\n\n        osg::Quat rotX(radsX, osg::Vec3f(1.0f, 0.0f, 0.0f));\n        osg::Quat rotZ(radsZ, osg::Vec3f(0.0f, 0.0f, 1.0f));\n\n        osg::Vec3f direction = rotX * rotZ * osg::Vec3f(0.0f, 1.0f, 0.0f);\n        mTransform->setPosition(direction * mDistance);\n\n        // The moon quad is initially oriented facing down, so we need to offset its X-axis\n        // rotation to rotate it to face the camera when sitting at the horizon.\n        osg::Quat attX((-static_cast<float>(osg::PI) / 2.0f) + radsX, osg::Vec3f(1.0f, 0.0f, 0.0f));\n        mTransform->setAttitude(attX * rotZ);\n\n        setPhase(state.mPhase);\n        mUpdater->mTransparency = state.mMoonAlpha;\n        mUpdater->mShadowBlend = state.mShadowBlend;\n    }\n\n    void setAtmosphereColor(const osg::Vec4f& color)\n    {\n        mUpdater->mAtmosphereColor = color;\n    }\n\n    void setColor(const osg::Vec4f& color)\n    {\n        mUpdater->mMoonColor = color;\n    }\n\n    unsigned int getPhaseInt() const\n    {\n        if      (mPhase == MoonState::Phase::New)              return 0;\n        else if (mPhase == MoonState::Phase::WaxingCrescent)   return 1;\n        else if (mPhase == MoonState::Phase::WaningCrescent)   return 1;\n        else if (mPhase == MoonState::Phase::FirstQuarter)     return 2;\n        else if (mPhase == MoonState::Phase::ThirdQuarter)     return 2;\n        else if (mPhase == MoonState::Phase::WaxingGibbous)    return 3;\n        else if (mPhase == MoonState::Phase::WaningGibbous)    return 3;\n        else if (mPhase == MoonState::Phase::Full)             return 4;\n        return 0;\n    }\n\nprivate:\n    struct Updater : public SceneUtil::StateSetUpdater\n    {\n        Resource::ImageManager& mImageManager;\n        osg::ref_ptr<osg::Texture2D> mPhaseTex;\n        osg::ref_ptr<osg::Texture2D> mCircleTex;\n        float mTransparency;\n        float mShadowBlend;\n        osg::Vec4f mAtmosphereColor;\n        osg::Vec4f mMoonColor;\n\n        Updater(Resource::ImageManager& imageManager)\n            : mImageManager(imageManager)\n            , mPhaseTex()\n            , mCircleTex()\n            , mTransparency(1.0f)\n            , mShadowBlend(1.0f)\n            , mAtmosphereColor(1.0f, 1.0f, 1.0f, 1.0f)\n            , mMoonColor(1.0f, 1.0f, 1.0f, 1.0f)\n        {\n        }\n\n        void setDefaults(osg::StateSet* stateset) override\n        {\n            stateset->setTextureAttributeAndModes(0, mPhaseTex, osg::StateAttribute::ON);\n            osg::ref_ptr<osg::TexEnvCombine> texEnv = new osg::TexEnvCombine;\n            texEnv->setCombine_RGB(osg::TexEnvCombine::MODULATE);\n            texEnv->setSource0_RGB(osg::TexEnvCombine::CONSTANT);\n            texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE);\n            texEnv->setConstantColor(osg::Vec4f(1.f, 0.f, 0.f, 1.f)); // mShadowBlend * mMoonColor\n            stateset->setTextureAttributeAndModes(0, texEnv, osg::StateAttribute::ON);\n\n            stateset->setTextureAttributeAndModes(1, mCircleTex, osg::StateAttribute::ON);\n            osg::ref_ptr<osg::TexEnvCombine> texEnv2 = new osg::TexEnvCombine;\n            texEnv2->setCombine_RGB(osg::TexEnvCombine::ADD);\n            texEnv2->setCombine_Alpha(osg::TexEnvCombine::MODULATE);\n            texEnv2->setSource0_Alpha(osg::TexEnvCombine::TEXTURE);\n            texEnv2->setSource1_Alpha(osg::TexEnvCombine::CONSTANT);\n            texEnv2->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);\n            texEnv2->setSource1_RGB(osg::TexEnvCombine::CONSTANT);\n            texEnv2->setConstantColor(osg::Vec4f(0.f, 0.f, 0.f, 1.f)); // mAtmosphereColor.rgb, mTransparency\n            stateset->setTextureAttributeAndModes(1, texEnv2, osg::StateAttribute::ON);\n\n            stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n        }\n\n        void apply(osg::StateSet* stateset, osg::NodeVisitor*) override\n        {\n            osg::TexEnvCombine* texEnv = static_cast<osg::TexEnvCombine*>(stateset->getTextureAttribute(0, osg::StateAttribute::TEXENV));\n            texEnv->setConstantColor(mMoonColor * mShadowBlend);\n\n            osg::TexEnvCombine* texEnv2 = static_cast<osg::TexEnvCombine*>(stateset->getTextureAttribute(1, osg::StateAttribute::TEXENV));\n            texEnv2->setConstantColor(osg::Vec4f(mAtmosphereColor.x(), mAtmosphereColor.y(), mAtmosphereColor.z(), mTransparency));\n        }\n\n        void setTextures(const std::string& phaseTex, const std::string& circleTex)\n        {\n            mPhaseTex = new osg::Texture2D(mImageManager.getImage(phaseTex));\n            mPhaseTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n            mPhaseTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n            mCircleTex = new osg::Texture2D(mImageManager.getImage(circleTex));\n            mCircleTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n            mCircleTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n            reset();\n        }\n    };\n\n    Type mType;\n    MoonState::Phase mPhase;\n    osg::ref_ptr<Updater> mUpdater;\n\n    void setPhase(const MoonState::Phase& phase)\n    {\n        if(mPhase == phase)\n            return;\n\n        mPhase = phase;\n\n        std::string textureName = \"textures/tx_\";\n\n        if (mType == Moon::Type_Secunda)\n            textureName += \"secunda_\";\n        else\n            textureName += \"masser_\";\n\n        if     (phase == MoonState::Phase::New)            textureName += \"new\";\n        else if(phase == MoonState::Phase::WaxingCrescent) textureName += \"one_wax\";\n        else if(phase == MoonState::Phase::FirstQuarter)   textureName += \"half_wax\";\n        else if(phase == MoonState::Phase::WaxingGibbous)  textureName += \"three_wax\";\n        else if(phase == MoonState::Phase::WaningCrescent) textureName += \"one_wan\";\n        else if(phase == MoonState::Phase::ThirdQuarter)   textureName += \"half_wan\";\n        else if(phase == MoonState::Phase::WaningGibbous)  textureName += \"three_wan\";\n        else if(phase == MoonState::Phase::Full)           textureName += \"full\";\n\n        textureName += \".dds\";\n\n        if (mType == Moon::Type_Secunda)\n            mUpdater->setTextures(textureName, \"textures/tx_mooncircle_full_s.dds\");\n        else\n            mUpdater->setTextures(textureName, \"textures/tx_mooncircle_full_m.dds\");\n    }\n};\n\nSkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager)\n    : mSceneManager(sceneManager)\n    , mCamera(nullptr)\n    , mAtmosphereNightRoll(0.f)\n    , mCreated(false)\n    , mIsStorm(false)\n    , mDay(0)\n    , mMonth(0)\n    , mCloudAnimationTimer(0.f)\n    , mRainTimer(0.f)\n    , mStormDirection(0,1,0)\n    , mClouds()\n    , mNextClouds()\n    , mCloudBlendFactor(0.0f)\n    , mCloudSpeed(0.0f)\n    , mStarsOpacity(0.0f)\n    , mRemainingTransitionTime(0.0f)\n    , mRainEnabled(false)\n    , mRainSpeed(0)\n    , mRainDiameter(0)\n    , mRainMinHeight(0)\n    , mRainMaxHeight(0)\n    , mRainEntranceSpeed(1)\n    , mRainMaxRaindrops(0)\n    , mWindSpeed(0.f)\n    , mBaseWindSpeed(0.f)\n    , mEnabled(true)\n    , mSunEnabled(true)\n    , mPrecipitationAlpha(0.f)\n{\n    osg::ref_ptr<CameraRelativeTransform> skyroot (new CameraRelativeTransform);\n    skyroot->setName(\"Sky Root\");\n    // Assign empty program to specify we don't want shaders\n    // The shaders generated by the SceneManager can't handle everything we need\n    skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::PROTECTED|osg::StateAttribute::ON);\n    SceneUtil::ShadowManager::disableShadowsForStateSet(skyroot->getOrCreateStateSet());\n\n    skyroot->setNodeMask(Mask_Sky);\n    parentNode->addChild(skyroot);\n\n    mRootNode = skyroot;\n\n    mEarlyRenderBinRoot = new osg::Group;\n    // render before the world is rendered\n    mEarlyRenderBinRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Sky, \"RenderBin\");\n    // Prevent unwanted clipping by water reflection camera's clipping plane\n    mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_CLIP_PLANE0, osg::StateAttribute::OFF);\n    mRootNode->addChild(mEarlyRenderBinRoot);\n\n    mUnderwaterSwitch = new UnderwaterSwitchCallback(skyroot);\n}\n\nvoid SkyManager::create()\n{\n    assert(!mCreated);\n\n    mAtmosphereDay = mSceneManager->getInstance(Settings::Manager::getString(\"skyatmosphere\", \"Models\"), mEarlyRenderBinRoot);\n    ModVertexAlphaVisitor modAtmosphere(0);\n    mAtmosphereDay->accept(modAtmosphere);\n\n    mAtmosphereUpdater = new AtmosphereUpdater;\n    mAtmosphereDay->addUpdateCallback(mAtmosphereUpdater);\n\n    mAtmosphereNightNode = new osg::PositionAttitudeTransform;\n    mAtmosphereNightNode->setNodeMask(0);\n    mEarlyRenderBinRoot->addChild(mAtmosphereNightNode);\n\n    osg::ref_ptr<osg::Node> atmosphereNight;\n    if (mSceneManager->getVFS()->exists(Settings::Manager::getString(\"skynight02\", \"Models\")))\n        atmosphereNight = mSceneManager->getInstance(Settings::Manager::getString(\"skynight02\", \"Models\"), mAtmosphereNightNode);\n    else\n        atmosphereNight = mSceneManager->getInstance(Settings::Manager::getString(\"skynight01\", \"Models\"), mAtmosphereNightNode);\n    atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n    ModVertexAlphaVisitor modStars(2);\n    atmosphereNight->accept(modStars);\n    mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getImageManager());\n    atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater);\n\n    mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager()));\n\n    mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat(\"Moons_Masser_Size\")/125, Moon::Type_Masser));\n    mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat(\"Moons_Secunda_Size\")/125, Moon::Type_Secunda));\n\n    mCloudNode = new osg::PositionAttitudeTransform;\n    mEarlyRenderBinRoot->addChild(mCloudNode);\n    mCloudMesh = mSceneManager->getInstance(Settings::Manager::getString(\"skyclouds\", \"Models\"), mCloudNode);\n    ModVertexAlphaVisitor modClouds(1);\n    mCloudMesh->accept(modClouds);\n    mCloudUpdater = new CloudUpdater;\n    mCloudUpdater->setOpacity(1.f);\n    mCloudMesh->addUpdateCallback(mCloudUpdater);\n\n    mCloudMesh2 = mSceneManager->getInstance(Settings::Manager::getString(\"skyclouds\", \"Models\"), mCloudNode);\n    mCloudMesh2->accept(modClouds);\n    mCloudUpdater2 = new CloudUpdater;\n    mCloudUpdater2->setOpacity(0.f);\n    mCloudMesh2->addUpdateCallback(mCloudUpdater2);\n    mCloudMesh2->setNodeMask(0);\n\n    osg::ref_ptr<osg::Depth> depth = new osg::Depth;\n    depth->setWriteMask(false);\n    mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON);\n    mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);\n    mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF);\n\n    mMoonScriptColor = Fallback::Map::getColour(\"Moons_Script_Color\");\n\n    mCreated = true;\n}\n\nclass RainCounter : public osgParticle::ConstantRateCounter\n{\npublic:\n    int numParticlesToCreate(double dt) const override\n    {\n        // limit dt to avoid large particle emissions if there are jumps in the simulation time\n        // 0.2 seconds is the same cap as used in Engine's frame loop\n        dt = std::min(dt, 0.2);\n        return ConstantRateCounter::numParticlesToCreate(dt);\n    }\n};\n\nclass RainShooter : public osgParticle::Shooter\n{\npublic:\n    RainShooter()\n        : mAngle(0.f)\n    {\n    }\n\n    void shoot(osgParticle::Particle* particle) const override\n    {\n        particle->setVelocity(mVelocity);\n        particle->setAngle(osg::Vec3f(-mAngle, 0, (Misc::Rng::rollProbability() * 2 - 1) * osg::PI));\n    }\n\n    void setVelocity(const osg::Vec3f& velocity)\n    {\n        mVelocity = velocity;\n    }\n\n    void setAngle(float angle)\n    {\n        mAngle = angle;\n    }\n\n    osg::Object* cloneType() const override\n    {\n        return new RainShooter;\n    }\n    osg::Object* clone(const osg::CopyOp &) const override\n    {\n        return new RainShooter(*this);\n    }\n\nprivate:\n    osg::Vec3f mVelocity;\n    float mAngle;\n};\n\n// Updater for alpha value on a node's StateSet. Assumes the node has an existing Material StateAttribute.\nclass AlphaFader : public SceneUtil::StateSetUpdater\n{\npublic:\n    /// @param alpha the variable alpha value is recovered from\n    AlphaFader(float& alpha)\n        : mAlpha(alpha)\n    {\n    }\n\n    void setDefaults(osg::StateSet* stateset) override\n    {\n        // need to create a deep copy of StateAttributes we will modify\n        osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));\n        stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON);\n    }\n\n    void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override\n    {\n        osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));\n        mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,mAlpha));\n    }\n\n    // Helper for adding AlphaFaders to a subgraph\n    class SetupVisitor : public osg::NodeVisitor\n    {\n    public:\n        SetupVisitor(float &alpha)\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mAlpha(alpha)\n        {\n        }\n\n        void apply(osg::Node &node) override\n        {\n            if (osg::StateSet* stateset = node.getStateSet())\n            {\n                if (stateset->getAttribute(osg::StateAttribute::MATERIAL))\n                {\n                    SceneUtil::CompositeStateSetUpdater* composite = nullptr;\n                    osg::Callback* callback = node.getUpdateCallback();\n\n                    while (callback)\n                    {\n                        composite = dynamic_cast<SceneUtil::CompositeStateSetUpdater*>(callback);\n                        if (composite)\n                            break;\n\n                        callback = callback->getNestedCallback();\n                    }\n\n                    osg::ref_ptr<AlphaFader> alphaFader (new AlphaFader(mAlpha));\n\n                    if (composite)\n                        composite->addController(alphaFader);\n                    else\n                        node.addUpdateCallback(alphaFader);\n                }\n            }\n\n            traverse(node);\n        }\n\n    private:\n        float &mAlpha;\n    };\n\nprotected:\n    float &mAlpha;\n};\n\nvoid SkyManager::setCamera(osg::Camera *camera)\n{\n    mCamera = camera;\n}\n\nclass WrapAroundOperator : public osgParticle::Operator\n{\npublic:\n    WrapAroundOperator(osg::Camera *camera, const osg::Vec3 &wrapRange): osgParticle::Operator()\n    {\n        mCamera = camera;\n        mWrapRange = wrapRange;\n        mHalfWrapRange = mWrapRange / 2.0;\n        mPreviousCameraPosition = getCameraPosition();\n    }\n\n    osg::Object *cloneType() const override\n    {\n        return nullptr;\n    }\n\n    osg::Object *clone(const osg::CopyOp &op) const override\n    {\n        return nullptr;\n    }\n\n    void operate(osgParticle::Particle *P, double dt) override\n    {\n    }\n\n    void operateParticles(osgParticle::ParticleSystem *ps, double dt) override\n    {\n        osg::Vec3 position = getCameraPosition();\n        osg::Vec3 positionDifference = position - mPreviousCameraPosition;\n\n        osg::Matrix toWorld, toLocal;\n\n        std::vector<osg::Matrix> worldMatrices = ps->getWorldMatrices();\n \n        if (!worldMatrices.empty())\n        {\n            toWorld = worldMatrices[0];\n            toLocal.invert(toWorld);\n        }\n\n        for (int i = 0; i < ps->numParticles(); ++i)\n        {\n            osgParticle::Particle *p = ps->getParticle(i);\n            p->setPosition(toWorld.preMult(p->getPosition()));\n            p->setPosition(p->getPosition() - positionDifference);\n\n            for (int j = 0; j < 3; ++j)  // wrap-around in all 3 dimensions\n            {\n                osg::Vec3 pos = p->getPosition();\n\n                if (pos[j] < -mHalfWrapRange[j])\n                    pos[j] = mHalfWrapRange[j] + fmod(pos[j] - mHalfWrapRange[j],mWrapRange[j]);\n                else if (pos[j] > mHalfWrapRange[j])\n                    pos[j] = fmod(pos[j] + mHalfWrapRange[j],mWrapRange[j]) - mHalfWrapRange[j];\n\n                p->setPosition(pos);\n            }\n\n            p->setPosition(toLocal.preMult(p->getPosition()));\n        }\n\n        mPreviousCameraPosition = position;\n    }\n\nprotected:\n    osg::Camera *mCamera;\n    osg::Vec3 mPreviousCameraPosition;\n    osg::Vec3 mWrapRange;\n    osg::Vec3 mHalfWrapRange;\n\n    osg::Vec3 getCameraPosition()\n    {\n        return mCamera->getInverseViewMatrix().getTrans();\n    }\n};\n\nclass WeatherAlphaOperator : public osgParticle::Operator\n{\npublic:\n    WeatherAlphaOperator(float& alpha, bool rain)\n        : mAlpha(alpha)\n        , mIsRain(rain)\n    {\n    }\n\n    osg::Object *cloneType() const override\n    {\n        return nullptr;\n    }\n\n    osg::Object *clone(const osg::CopyOp &op) const override\n    {\n        return nullptr;\n    }\n\n    void operate(osgParticle::Particle *particle, double dt) override\n    {\n        constexpr float rainThreshold = 0.6f; // Rain_Threshold?\n        const float alpha = mIsRain ? mAlpha * rainThreshold : mAlpha;\n        particle->setAlphaRange(osgParticle::rangef(alpha, alpha));\n    }\n\nprivate:\n    float &mAlpha;\n    bool mIsRain;\n};\n\nvoid SkyManager::createRain()\n{\n    if (mRainNode)\n        return;\n\n    mRainNode = new osg::Group;\n\n    mRainParticleSystem = new NifOsg::ParticleSystem;\n    osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f);\n\n    mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED);\n    mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0));\n    mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,1));\n\n    osg::ref_ptr<osg::StateSet> stateset (mRainParticleSystem->getOrCreateStateSet());\n\n    osg::ref_ptr<osg::Texture2D> raindropTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(\"textures/tx_raindrop_01.dds\")));\n    raindropTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n    raindropTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n    stateset->setTextureAttributeAndModes(0, raindropTex, osg::StateAttribute::ON);\n    stateset->setNestRenderBins(false);\n    stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n    stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n    stateset->setMode(GL_BLEND, osg::StateAttribute::ON);\n\n    osg::ref_ptr<osg::Material> mat (new osg::Material);\n    mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n    mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n    mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);\n    stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);\n\n    osgParticle::Particle& particleTemplate = mRainParticleSystem->getDefaultParticleTemplate();\n    particleTemplate.setSizeRange(osgParticle::rangef(5.f, 15.f));\n    particleTemplate.setAlphaRange(osgParticle::rangef(1.f, 1.f));\n    particleTemplate.setLifeTime(1);\n\n    osg::ref_ptr<osgParticle::ModularEmitter> emitter (new osgParticle::ModularEmitter);\n    emitter->setParticleSystem(mRainParticleSystem);\n\n    osg::ref_ptr<osgParticle::BoxPlacer> placer (new osgParticle::BoxPlacer);\n    placer->setXRange(-rainRange.x() / 2, rainRange.x() / 2);\n    placer->setYRange(-rainRange.y() / 2, rainRange.y() / 2);\n    placer->setZRange(-rainRange.z() / 2, rainRange.z() / 2);\n    emitter->setPlacer(placer);\n    mPlacer = placer;\n\n    // FIXME: vanilla engine does not use a particle system to handle rain, it uses a NIF-file with 20 raindrops in it.\n    // It spawns the (maxRaindrops-getParticleSystem()->numParticles())*dt/rainEntranceSpeed batches every frame (near 1-2).\n    // Since the rain is a regular geometry, it produces water ripples, also in theory it can be removed if collides with something.\n    osg::ref_ptr<RainCounter> counter (new RainCounter);\n    counter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20);\n    emitter->setCounter(counter);\n    mCounter = counter;\n\n    osg::ref_ptr<RainShooter> shooter (new RainShooter);\n    mRainShooter = shooter;\n    emitter->setShooter(shooter);\n\n    osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater (new osgParticle::ParticleSystemUpdater);\n    updater->addParticleSystem(mRainParticleSystem);\n\n    osg::ref_ptr<osgParticle::ModularProgram> program (new osgParticle::ModularProgram);\n    program->addOperator(new WrapAroundOperator(mCamera,rainRange));\n    program->addOperator(new WeatherAlphaOperator(mPrecipitationAlpha, true));\n    program->setParticleSystem(mRainParticleSystem);\n    mRainNode->addChild(program);\n\n    mRainNode->addChild(emitter);\n    mRainNode->addChild(mRainParticleSystem);\n    mRainNode->addChild(updater);\n\n    // Note: if we ever switch to regular geometry rain, it'll need to use an AlphaFader.\n    mRainNode->addCullCallback(mUnderwaterSwitch);\n    mRainNode->setNodeMask(Mask_WeatherParticles);\n\n    mRootNode->addChild(mRainNode);\n}\n\nvoid SkyManager::destroyRain()\n{\n    if (!mRainNode)\n        return;\n\n    mRootNode->removeChild(mRainNode);\n    mRainNode = nullptr;\n    mPlacer = nullptr;\n    mCounter = nullptr;\n    mRainParticleSystem = nullptr;\n    mRainShooter = nullptr;\n}\n\nSkyManager::~SkyManager()\n{\n    if (mRootNode)\n    {\n        mRootNode->getParent(0)->removeChild(mRootNode);\n        mRootNode = nullptr;\n    }\n}\n\nint SkyManager::getMasserPhase() const\n{\n    if (!mCreated) return 0;\n    return mMasser->getPhaseInt();\n}\n\nint SkyManager::getSecundaPhase() const\n{\n    if (!mCreated) return 0;\n    return mSecunda->getPhaseInt();\n}\n\nbool SkyManager::isEnabled()\n{\n    return mEnabled;\n}\n\nbool SkyManager::hasRain() const\n{\n    return mRainNode != nullptr;\n}\n\nfloat SkyManager::getPrecipitationAlpha() const\n{\n    if (mEnabled && !mIsStorm && (hasRain() || mParticleNode))\n        return mPrecipitationAlpha;\n\n    return 0.f;\n}\n\nvoid SkyManager::update(float duration)\n{\n    if (!mEnabled)\n        return;\n\n    switchUnderwaterRain();\n\n    if (mIsStorm)\n    {\n        osg::Quat quat;\n        quat.makeRotate(osg::Vec3f(0,1,0), mStormDirection);\n\n        mCloudNode->setAttitude(quat);\n        if (mParticleNode)\n        {\n            // Morrowind deliberately rotates the blizzard mesh, so so should we.\n            if (mCurrentParticleEffect == Settings::Manager::getString(\"weatherblizzard\", \"Models\"))\n                quat.makeRotate(osg::Vec3f(-1,0,0), mStormDirection);\n            mParticleNode->setAttitude(quat);\n        }\n    }\n    else\n        mCloudNode->setAttitude(osg::Quat());\n\n    // UV Scroll the clouds\n    mCloudAnimationTimer += duration * mCloudSpeed * 0.003;\n    mCloudUpdater->setAnimationTimer(mCloudAnimationTimer);\n    mCloudUpdater2->setAnimationTimer(mCloudAnimationTimer);\n\n    // rotate the stars by 360 degrees every 4 days\n    mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeScaleFactor()*duration*osg::DegreesToRadians(360.f) / (3600*96.f);\n    if (mAtmosphereNightNode->getNodeMask() != 0)\n        mAtmosphereNightNode->setAttitude(osg::Quat(mAtmosphereNightRoll, osg::Vec3f(0,0,1)));\n}\n\nvoid SkyManager::setEnabled(bool enabled)\n{\n    if (enabled && !mCreated)\n        create();\n\n    mRootNode->setNodeMask(enabled ? Mask_Sky : 0u);\n\n    mEnabled = enabled;\n}\n\nvoid SkyManager::setMoonColour (bool red)\n{\n    if (!mCreated) return;\n    mSecunda->setColor(red ? mMoonScriptColor : osg::Vec4f(1,1,1,1));\n}\n\nvoid SkyManager::updateRainParameters()\n{\n    if (mRainShooter)\n    {\n        float angle = -std::atan(mWindSpeed/50.f);\n        mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed*std::sin(angle), -mRainSpeed/std::cos(angle)));\n        mRainShooter->setAngle(angle);\n\n        osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f);\n\n        mPlacer->setXRange(-rainRange.x() / 2, rainRange.x() / 2);\n        mPlacer->setYRange(-rainRange.y() / 2, rainRange.y() / 2);\n        mPlacer->setZRange(-rainRange.z() / 2, rainRange.z() / 2);\n\n        mCounter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20);\n    }\n}\n\nvoid SkyManager::switchUnderwaterRain()\n{\n    if (!mRainParticleSystem)\n        return;\n\n    bool freeze = mUnderwaterSwitch->isUnderwater();\n    mRainParticleSystem->setFrozen(freeze);\n}\n\nvoid SkyManager::setWeather(const WeatherResult& weather)\n{\n    if (!mCreated) return;\n\n    mRainEntranceSpeed = weather.mRainEntranceSpeed;\n    mRainMaxRaindrops = weather.mRainMaxRaindrops;\n    mRainDiameter = weather.mRainDiameter;\n    mRainMinHeight = weather.mRainMinHeight;\n    mRainMaxHeight = weather.mRainMaxHeight;\n    mRainSpeed = weather.mRainSpeed;\n    mWindSpeed = weather.mWindSpeed;\n    mBaseWindSpeed = weather.mBaseWindSpeed;\n\n    if (mRainEffect != weather.mRainEffect)\n    {\n        mRainEffect = weather.mRainEffect;\n        if (!mRainEffect.empty())\n        {\n            createRain();\n        }\n        else\n        {\n            destroyRain();\n        }\n    }\n\n    updateRainParameters();\n\n    mIsStorm = weather.mIsStorm;\n\n    if (mCurrentParticleEffect != weather.mParticleEffect)\n    {\n        mCurrentParticleEffect = weather.mParticleEffect;\n\n        // cleanup old particles\n        if (mParticleEffect)\n        {\n            mParticleNode->removeChild(mParticleEffect);\n            mParticleEffect = nullptr;\n        }\n\n        if (mCurrentParticleEffect.empty())\n        {\n            if (mParticleNode)\n            {\n                mRootNode->removeChild(mParticleNode);\n                mParticleNode = nullptr;\n            }\n        }\n        else\n        {\n            if (!mParticleNode)\n            {\n                mParticleNode = new osg::PositionAttitudeTransform;\n                mParticleNode->addCullCallback(mUnderwaterSwitch);\n                mParticleNode->setNodeMask(Mask_WeatherParticles);\n                mRootNode->addChild(mParticleNode);\n            }\n\n            mParticleEffect = mSceneManager->getInstance(mCurrentParticleEffect, mParticleNode);\n\n            SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));\n            mParticleEffect->accept(assignVisitor);\n\n            AlphaFader::SetupVisitor alphaFaderSetupVisitor(mPrecipitationAlpha);\n\n            mParticleEffect->accept(alphaFaderSetupVisitor);\n\n            SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor;\n            mParticleEffect->accept(disableFreezeOnCullVisitor);\n\n            SceneUtil::FindByClassVisitor findPSVisitor(std::string(\"ParticleSystem\"));\n            mParticleEffect->accept(findPSVisitor);\n\n            for (unsigned int i = 0; i < findPSVisitor.mFoundNodes.size(); ++i)\n            {\n                osgParticle::ParticleSystem *ps = static_cast<osgParticle::ParticleSystem *>(findPSVisitor.mFoundNodes[i]);\n                    \n                osg::ref_ptr<osgParticle::ModularProgram> program (new osgParticle::ModularProgram);\n                if (!mIsStorm)\n                    program->addOperator(new WrapAroundOperator(mCamera,osg::Vec3(1024,1024,800)));\n                program->addOperator(new WeatherAlphaOperator(mPrecipitationAlpha, false));\n                program->setParticleSystem(ps);\n                mParticleNode->addChild(program);\n            }\n        }\n    }\n\n    if (mClouds != weather.mCloudTexture)\n    {\n        mClouds = weather.mCloudTexture;\n\n        std::string texture = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS());\n\n        osg::ref_ptr<osg::Texture2D> cloudTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)));\n        cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n        cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n\n        mCloudUpdater->setTexture(cloudTex);\n    }\n\n    if (mNextClouds != weather.mNextCloudTexture)\n    {\n        mNextClouds = weather.mNextCloudTexture;\n\n        if (!mNextClouds.empty())\n        {\n            std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS());\n\n            osg::ref_ptr<osg::Texture2D> cloudTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)));\n            cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n            cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n\n            mCloudUpdater2->setTexture(cloudTex);\n        }\n    }\n\n    if (mCloudBlendFactor != weather.mCloudBlendFactor)\n    {\n        mCloudBlendFactor = weather.mCloudBlendFactor;\n\n        mCloudUpdater->setOpacity((1.f-mCloudBlendFactor));\n        mCloudUpdater2->setOpacity(mCloudBlendFactor);\n        mCloudMesh2->setNodeMask(mCloudBlendFactor > 0.f ? ~0u : 0);\n    }\n\n    if (mCloudColour != weather.mFogColor)\n    {\n        osg::Vec4f clr (weather.mFogColor);\n        clr += osg::Vec4f(0.13f, 0.13f, 0.13f, 0.f);\n\n        mCloudUpdater->setEmissionColor(clr);\n        mCloudUpdater2->setEmissionColor(clr);\n\n        mCloudColour = weather.mFogColor;\n    }\n\n    if (mSkyColour != weather.mSkyColor)\n    {\n        mSkyColour = weather.mSkyColor;\n\n        mAtmosphereUpdater->setEmissionColor(mSkyColour);\n        mMasser->setAtmosphereColor(mSkyColour);\n        mSecunda->setAtmosphereColor(mSkyColour);\n    }\n\n    if (mFogColour != weather.mFogColor)\n    {\n        mFogColour = weather.mFogColor;\n    }\n\n    mCloudSpeed = weather.mCloudSpeed;\n\n    mMasser->adjustTransparency(weather.mGlareView);\n    mSecunda->adjustTransparency(weather.mGlareView);\n\n    mSun->setColor(weather.mSunDiscColor);\n    mSun->adjustTransparency(weather.mGlareView * weather.mSunDiscColor.a());\n\n    float nextStarsOpacity = weather.mNightFade * weather.mGlareView;\n\n    if (weather.mNight && mStarsOpacity != nextStarsOpacity)\n    {\n        mStarsOpacity = nextStarsOpacity;\n\n        mAtmosphereNightUpdater->setFade(mStarsOpacity);\n    }\n\n    mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0u : 0);\n\n    mPrecipitationAlpha = weather.mPrecipitationAlpha;\n}\n\nfloat SkyManager::getBaseWindSpeed() const\n{\n    if (!mCreated) return 0.f;\n\n    return mBaseWindSpeed;\n}\n\nvoid SkyManager::sunEnable()\n{\n    if (!mCreated) return;\n\n    mSun->setVisible(true);\n}\n\nvoid SkyManager::sunDisable()\n{\n    if (!mCreated) return;\n\n    mSun->setVisible(false);\n}\n\nvoid SkyManager::setStormDirection(const osg::Vec3f &direction)\n{\n    mStormDirection = direction;\n}\n\nvoid SkyManager::setSunDirection(const osg::Vec3f& direction)\n{\n    if (!mCreated) return;\n\n    mSun->setDirection(direction);\n}\n\nvoid SkyManager::setMasserState(const MoonState& state)\n{\n    if(!mCreated) return;\n\n    mMasser->setState(state);\n}\n\nvoid SkyManager::setSecundaState(const MoonState& state)\n{\n    if(!mCreated) return;\n\n    mSecunda->setState(state);\n}\n\nvoid SkyManager::setDate(int day, int month)\n{\n    mDay = day;\n    mMonth = month;\n}\n\nvoid SkyManager::setGlareTimeOfDayFade(float val)\n{\n    mSun->setGlareTimeOfDayFade(val);\n}\n\nvoid SkyManager::setWaterHeight(float height)\n{\n    mUnderwaterSwitch->setWaterLevel(height);\n}\n\nvoid SkyManager::listAssetsToPreload(std::vector<std::string>& models, std::vector<std::string>& textures)\n{\n    models.emplace_back(Settings::Manager::getString(\"skyatmosphere\", \"Models\"));\n    if (mSceneManager->getVFS()->exists(Settings::Manager::getString(\"skynight02\", \"Models\")))\n        models.emplace_back(Settings::Manager::getString(\"skynight02\", \"Models\"));\n    models.emplace_back(Settings::Manager::getString(\"skynight01\", \"Models\"));\n    models.emplace_back(Settings::Manager::getString(\"skyclouds\", \"Models\"));\n\n    models.emplace_back(Settings::Manager::getString(\"weatherashcloud\", \"Models\"));\n    models.emplace_back(Settings::Manager::getString(\"weatherblightcloud\", \"Models\"));\n    models.emplace_back(Settings::Manager::getString(\"weathersnow\", \"Models\"));\n    models.emplace_back(Settings::Manager::getString(\"weatherblizzard\", \"Models\"));\n\n    textures.emplace_back(\"textures/tx_mooncircle_full_s.dds\");\n    textures.emplace_back(\"textures/tx_mooncircle_full_m.dds\");\n\n    textures.emplace_back(\"textures/tx_masser_new.dds\");\n    textures.emplace_back(\"textures/tx_masser_one_wax.dds\");\n    textures.emplace_back(\"textures/tx_masser_half_wax.dds\");\n    textures.emplace_back(\"textures/tx_masser_three_wax.dds\");\n    textures.emplace_back(\"textures/tx_masser_one_wan.dds\");\n    textures.emplace_back(\"textures/tx_masser_half_wan.dds\");\n    textures.emplace_back(\"textures/tx_masser_three_wan.dds\");\n    textures.emplace_back(\"textures/tx_masser_full.dds\");\n\n    textures.emplace_back(\"textures/tx_secunda_new.dds\");\n    textures.emplace_back(\"textures/tx_secunda_one_wax.dds\");\n    textures.emplace_back(\"textures/tx_secunda_half_wax.dds\");\n    textures.emplace_back(\"textures/tx_secunda_three_wax.dds\");\n    textures.emplace_back(\"textures/tx_secunda_one_wan.dds\");\n    textures.emplace_back(\"textures/tx_secunda_half_wan.dds\");\n    textures.emplace_back(\"textures/tx_secunda_three_wan.dds\");\n    textures.emplace_back(\"textures/tx_secunda_full.dds\");\n\n    textures.emplace_back(\"textures/tx_sun_05.dds\");\n    textures.emplace_back(\"textures/tx_sun_flash_grey_05.dds\");\n\n    textures.emplace_back(\"textures/tx_raindrop_01.dds\");\n}\n\nvoid SkyManager::setWaterEnabled(bool enabled)\n{\n    mUnderwaterSwitch->setEnabled(enabled);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/sky.hpp",
    "content": "#ifndef OPENMW_MWRENDER_SKY_H\n#define OPENMW_MWRENDER_SKY_H\n\n#include <string>\n#include <memory>\n#include <vector>\n\n#include <osg/ref_ptr>\n#include <osg/Vec4f>\n\nnamespace osg\n{\n    class Camera;\n}\n\nnamespace osg\n{\n    class Group;\n    class Node;\n    class Material;\n    class PositionAttitudeTransform;\n}\n\nnamespace osgParticle\n{\n    class ParticleSystem;\n    class BoxPlacer;\n}\n\nnamespace Resource\n{\n    class SceneManager;\n}\n\nnamespace MWRender\n{\n    class AtmosphereUpdater;\n    class AtmosphereNightUpdater;\n    class CloudUpdater;\n    class Sun;\n    class Moon;\n    class RainCounter;\n    class RainShooter;\n    class RainFader;\n    class AlphaFader;\n    class UnderwaterSwitchCallback;\n\n    struct WeatherResult\n    {\n        std::string mCloudTexture;\n        std::string mNextCloudTexture;\n        float mCloudBlendFactor;\n\n        osg::Vec4f mFogColor;\n\n        osg::Vec4f mAmbientColor;\n\n        osg::Vec4f mSkyColor;\n\n        // sun light color\n        osg::Vec4f mSunColor;\n\n        // alpha is the sun transparency\n        osg::Vec4f mSunDiscColor;\n\n        float mFogDepth;\n\n        float mDLFogFactor;\n        float mDLFogOffset;\n\n        float mWindSpeed;\n        float mBaseWindSpeed;\n        float mCurrentWindSpeed;\n        float mNextWindSpeed;\n\n        float mCloudSpeed;\n\n        float mGlareView;\n\n        bool mNight; // use night skybox\n        float mNightFade; // fading factor for night skybox\n\n        bool mIsStorm;\n\n        std::string mAmbientLoopSoundID;\n        float mAmbientSoundVolume;\n\n        std::string mParticleEffect;\n        std::string mRainEffect;\n        float mPrecipitationAlpha;\n\n        float mRainDiameter;\n        float mRainMinHeight;\n        float mRainMaxHeight;\n        float mRainSpeed;\n        float mRainEntranceSpeed;\n        int mRainMaxRaindrops;\n    };\n\n    struct MoonState\n    {\n        enum class Phase\n        {\n            Full = 0,\n            WaningGibbous,\n            ThirdQuarter,\n            WaningCrescent,\n            New,\n            WaxingCrescent,\n            FirstQuarter,\n            WaxingGibbous,\n            Unspecified\n        };\n\n        float mRotationFromHorizon;\n        float mRotationFromNorth;\n        Phase mPhase;\n        float mShadowBlend;\n        float mMoonAlpha;\n    };\n\n    ///@brief The SkyManager handles rendering of the sky domes, celestial bodies as well as other objects that need to be rendered\n    /// relative to the camera (e.g. weather particle effects)\n    class SkyManager\n    {\n    public:\n        SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager);\n        ~SkyManager();\n\n        void update(float duration);\n\n        void setEnabled(bool enabled);\n\n        void setHour (double hour);\n        ///< will be called even when sky is disabled.\n\n        void setDate (int day, int month);\n        ///< will be called even when sky is disabled.\n\n        int getMasserPhase() const;\n        ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,\n        /// 3 waxing or waning gibbous, 4 full moon\n\n        int getSecundaPhase() const;\n        ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,\n        /// 3 waxing or waning gibbous, 4 full moon\n\n        void setMoonColour (bool red);\n        ///< change Secunda colour to red\n\n        void setWeather(const WeatherResult& weather);\n\n        void sunEnable();\n\n        void sunDisable();\n\n        bool isEnabled();\n\n        bool hasRain() const;\n\n        float getPrecipitationAlpha() const;\n\n        void setRainSpeed(float speed);\n\n        void setStormDirection(const osg::Vec3f& direction);\n\n        void setSunDirection(const osg::Vec3f& direction);\n\n        void setMasserState(const MoonState& state);\n        void setSecundaState(const MoonState& state);\n\n        void setGlareTimeOfDayFade(float val);\n\n        /// Enable or disable the water plane (used to remove underwater weather particles)\n        void setWaterEnabled(bool enabled);\n\n        /// Set height of water plane (used to remove underwater weather particles)\n        void setWaterHeight(float height);\n\n        void listAssetsToPreload(std::vector<std::string>& models, std::vector<std::string>& textures);\n\n        void setCamera(osg::Camera *camera);\n\n        float getBaseWindSpeed() const;\n\n    private:\n        void create();\n        ///< no need to call this, automatically done on first enable()\n\n        void createRain();\n        void destroyRain();\n        void switchUnderwaterRain();\n        void updateRainParameters();\n\n        Resource::SceneManager* mSceneManager;\n\n        osg::Camera *mCamera;\n\n        osg::ref_ptr<osg::Group> mRootNode;\n        osg::ref_ptr<osg::Group> mEarlyRenderBinRoot;\n\n        osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;\n        osg::ref_ptr<osg::Node> mParticleEffect;\n        osg::ref_ptr<UnderwaterSwitchCallback> mUnderwaterSwitch;\n\n        osg::ref_ptr<osg::PositionAttitudeTransform> mCloudNode;\n\n        osg::ref_ptr<CloudUpdater> mCloudUpdater;\n        osg::ref_ptr<CloudUpdater> mCloudUpdater2;\n        osg::ref_ptr<osg::Node> mCloudMesh;\n        osg::ref_ptr<osg::Node> mCloudMesh2;\n\n        osg::ref_ptr<osg::Node> mAtmosphereDay;\n\n        osg::ref_ptr<osg::PositionAttitudeTransform> mAtmosphereNightNode;\n        float mAtmosphereNightRoll;\n        osg::ref_ptr<AtmosphereNightUpdater> mAtmosphereNightUpdater;\n\n        osg::ref_ptr<AtmosphereUpdater> mAtmosphereUpdater;\n\n        std::unique_ptr<Sun> mSun;\n        std::unique_ptr<Moon> mMasser;\n        std::unique_ptr<Moon> mSecunda;\n\n        osg::ref_ptr<osg::Group> mRainNode;\n        osg::ref_ptr<osgParticle::ParticleSystem> mRainParticleSystem;\n        osg::ref_ptr<osgParticle::BoxPlacer> mPlacer;\n        osg::ref_ptr<RainCounter> mCounter;\n        osg::ref_ptr<RainShooter> mRainShooter;\n\n        bool mCreated;\n\n        bool mIsStorm;\n\n        int mDay;\n        int mMonth;\n\n        float mCloudAnimationTimer;\n\n        float mRainTimer;\n\n        osg::Vec3f mStormDirection;\n\n        // remember some settings so we don't have to apply them again if they didn't change\n        std::string mClouds;\n        std::string mNextClouds;\n        float mCloudBlendFactor;\n        float mCloudSpeed;\n        float mStarsOpacity;\n        osg::Vec4f mCloudColour;\n        osg::Vec4f mSkyColour;\n        osg::Vec4f mFogColour;\n\n        std::string mCurrentParticleEffect;\n\n        float mRemainingTransitionTime;\n\n        bool mRainEnabled;\n        std::string mRainEffect;\n        float mRainSpeed;\n        float mRainDiameter;\n        float mRainMinHeight;\n        float mRainMaxHeight;\n        float mRainEntranceSpeed;\n        int mRainMaxRaindrops;\n        float mWindSpeed;\n        float mBaseWindSpeed;\n\n        bool mEnabled;\n        bool mSunEnabled;\n\n        float mPrecipitationAlpha;\n\n        osg::Vec4f mMoonScriptColor;\n    };\n}\n\n#endif // GAME_RENDER_SKY_H\n"
  },
  {
    "path": "apps/openmw/mwrender/terrainstorage.cpp",
    "content": "#include \"terrainstorage.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"landmanager.hpp\"\n\nnamespace MWRender\n{\n\n    TerrainStorage::TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps)\n        : ESMTerrain::Storage(resourceSystem->getVFS(), normalMapPattern, normalHeightMapPattern, autoUseNormalMaps, specularMapPattern, autoUseSpecularMaps)\n        , mLandManager(new LandManager(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX))\n        , mResourceSystem(resourceSystem)\n    {\n        mResourceSystem->addResourceManager(mLandManager.get());\n    }\n\n    TerrainStorage::~TerrainStorage()\n    {\n        mResourceSystem->removeResourceManager(mLandManager.get());\n    }\n\n    bool TerrainStorage::hasData(int cellX, int cellY)\n    {\n        const MWWorld::ESMStore &esmStore =\n             MWBase::Environment::get().getWorld()->getStore();\n\n        const ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY);\n        return land != nullptr;\n    }\n\n    void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)\n    {\n        minX = 0, minY = 0, maxX = 0, maxY = 0;\n\n        const MWWorld::ESMStore &esmStore =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        MWWorld::Store<ESM::Land>::iterator it = esmStore.get<ESM::Land>().begin();\n        for (; it != esmStore.get<ESM::Land>().end(); ++it)\n        {\n            if (it->mX < minX)\n                minX = static_cast<float>(it->mX);\n            if (it->mX > maxX)\n                maxX = static_cast<float>(it->mX);\n            if (it->mY < minY)\n                minY = static_cast<float>(it->mY);\n            if (it->mY > maxY)\n                maxY = static_cast<float>(it->mY);\n        }\n\n        // since grid coords are at cell origin, we need to add 1 cell\n        maxX += 1;\n        maxY += 1;\n    }\n\n    LandManager *TerrainStorage::getLandManager() const\n    {\n        return mLandManager.get();\n    }\n\n    osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)\n    {\n        return mLandManager->getLand(cellX, cellY);\n    }\n\n    const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)\n    {\n        const MWWorld::ESMStore &esmStore =\n            MWBase::Environment::get().getWorld()->getStore();\n        return esmStore.get<ESM::LandTexture>().search(index, plugin);\n    }\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/terrainstorage.hpp",
    "content": "#ifndef MWRENDER_TERRAINSTORAGE_H\n#define MWRENDER_TERRAINSTORAGE_H\n\n#include <memory>\n\n#include <components/esmterrain/storage.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n\nnamespace MWRender\n{\n\n    class LandManager;\n\n    /// @brief Connects the ESM Store used in OpenMW with the ESMTerrain storage.\n    class TerrainStorage : public ESMTerrain::Storage\n    {\n    public:\n\n        TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern = \"\", const std::string& normalHeightMapPattern = \"\", bool autoUseNormalMaps = false, const std::string& specularMapPattern = \"\", bool autoUseSpecularMaps = false);\n        ~TerrainStorage();\n\n        osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) override;\n        const ESM::LandTexture* getLandTexture(int index, short plugin) override;\n\n        bool hasData(int cellX, int cellY) override;\n\n        /// Get bounds of the whole terrain in cell units\n        void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;\n\n        LandManager* getLandManager() const;\n\n    private:\n       std::unique_ptr<LandManager> mLandManager;\n\n       Resource::ResourceSystem* mResourceSystem;\n    };\n\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/util.cpp",
    "content": "#include \"util.hpp\"\n\n#include <osg/Node>\n#include <osg/ValueObject>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/imagemanager.hpp>\n#include <components/misc/resourcehelpers.hpp>\n#include <components/sceneutil/visitor.hpp>\n\nnamespace MWRender\n{\n\nclass TextureOverrideVisitor : public osg::NodeVisitor\n    {\n    public:\n        TextureOverrideVisitor(const std::string& texture, Resource::ResourceSystem* resourcesystem)\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mTexture(texture)\n            , mResourcesystem(resourcesystem)\n        {\n        }\n\n        void apply(osg::Node& node) override\n        {\n            int index = 0;\n            osg::ref_ptr<osg::Node> nodePtr(&node);\n            if (node.getUserValue(\"overrideFx\", index))\n            {\n                if (index == 1) \n                    overrideTexture(mTexture, mResourcesystem, nodePtr);\n            }\n            traverse(node);\n        }\n        std::string mTexture;\n        Resource::ResourceSystem* mResourcesystem;\n};\n\nvoid overrideFirstRootTexture(const std::string &texture, Resource::ResourceSystem *resourceSystem, osg::ref_ptr<osg::Node> node)\n{\n    TextureOverrideVisitor overrideVisitor(texture, resourceSystem);\n    node->accept(overrideVisitor);\n}\n\nvoid overrideTexture(const std::string &texture, Resource::ResourceSystem *resourceSystem, osg::ref_ptr<osg::Node> node)\n{\n    if (texture.empty())\n        return;\n    std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture, resourceSystem->getVFS());\n    // Not sure if wrap settings should be pulled from the overridden texture?\n    osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture));\n    tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n    tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n    tex->setName(\"diffuseMap\");\n\n    osg::ref_ptr<osg::StateSet> stateset;\n    if (node->getStateSet())\n        stateset = new osg::StateSet(*node->getStateSet(), osg::CopyOp::SHALLOW_COPY);\n    else\n        stateset = new osg::StateSet;\n\n    stateset->setTextureAttribute(0, tex, osg::StateAttribute::OVERRIDE);\n\n    node->setStateSet(stateset);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/util.hpp",
    "content": "#ifndef OPENMW_MWRENDER_UTIL_H\n#define OPENMW_MWRENDER_UTIL_H\n\n#include <osg/NodeCallback>\n#include <osg/ref_ptr>\n#include <string>\n\nnamespace osg\n{\n    class Node;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWRender\n{\n    // Overrides the texture of nodes in the mesh that had the same NiTexturingProperty as the first NiTexturingProperty of the .NIF file's root node,\n    // if it had a NiTexturingProperty. Used for applying \"particle textures\" to magic effects.\n    void overrideFirstRootTexture(const std::string &texture, Resource::ResourceSystem *resourceSystem, osg::ref_ptr<osg::Node> node);\n\n    void overrideTexture(const std::string& texture, Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Node> node);\n\n    // Node callback to entirely skip the traversal.\n    class NoTraverseCallback : public osg::NodeCallback\n    {\n    public:\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            // no traverse()\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/viewovershoulder.cpp",
    "content": "#include \"viewovershoulder.hpp\"\n\n#include <osg/Quat>\n\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/refdata.hpp\"\n\n#include \"../mwmechanics/drawstate.hpp\"\n\nnamespace MWRender\n{\n\n    ViewOverShoulderController::ViewOverShoulderController(Camera* camera) :\n        mCamera(camera), mMode(Mode::RightShoulder),\n        mAutoSwitchShoulder(Settings::Manager::getBool(\"auto switch shoulder\", \"Camera\")),\n        mOverShoulderHorizontalOffset(30.f), mOverShoulderVerticalOffset(-10.f)\n    {\n        osg::Vec2f offset = Settings::Manager::getVector2(\"view over shoulder offset\", \"Camera\");\n        mOverShoulderHorizontalOffset = std::abs(offset.x());\n        mOverShoulderVerticalOffset = offset.y();\n        mDefaultShoulderIsRight = offset.x() >= 0;\n\n        mCamera->enableDynamicCameraDistance(true);\n        mCamera->enableCrosshairInThirdPersonMode(true);\n        mCamera->setFocalPointTargetOffset(offset);\n    }\n\n    void ViewOverShoulderController::update()\n    {\n        if (mCamera->isFirstPerson())\n            return;\n\n        Mode oldMode = mMode;\n        auto ptr = mCamera->getTrackingPtr();\n        bool combat = ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Nothing;\n        if (combat && !mCamera->isVanityOrPreviewModeEnabled())\n            mMode = Mode::Combat;\n        else if (MWBase::Environment::get().getWorld()->isSwimming(ptr))\n            mMode = Mode::Swimming;\n        else if (oldMode == Mode::Combat || oldMode == Mode::Swimming)\n            mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;\n        if (mAutoSwitchShoulder && (mMode == Mode::LeftShoulder || mMode == Mode::RightShoulder))\n            trySwitchShoulder();\n\n        if (oldMode == mMode)\n            return;\n\n        if (mCamera->getMode() == Camera::Mode::Vanity)\n            // Player doesn't touch controls for a long time. Transition should be very slow.\n            mCamera->setFocalPointTransitionSpeed(0.2f);\n        else if ((oldMode == Mode::Combat || mMode == Mode::Combat) && mCamera->getMode() == Camera::Mode::Normal)\n            // Transition to/from combat mode and we are not it preview mode. Should be fast.\n            mCamera->setFocalPointTransitionSpeed(5.f);\n        else\n            mCamera->setFocalPointTransitionSpeed(1.f);  // Default transition speed.\n\n        switch (mMode)\n        {\n        case Mode::RightShoulder:\n            mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});\n            break;\n        case Mode::LeftShoulder:\n            mCamera->setFocalPointTargetOffset({-mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});\n            break;\n        case Mode::Combat:\n        case Mode::Swimming:\n        default:\n            mCamera->setFocalPointTargetOffset({0, 15});\n        }\n    }\n\n    void ViewOverShoulderController::trySwitchShoulder()\n    {\n        if (mCamera->getMode() != Camera::Mode::Normal)\n            return;\n\n        const float limitToSwitch = 120; // switch to other shoulder if wall is closer than this limit\n        const float limitToSwitchBack = 300; // switch back to default shoulder if there is no walls at this distance\n\n        auto orient = osg::Quat(mCamera->getYaw(), osg::Vec3d(0,0,1));\n        osg::Vec3d playerPos = mCamera->getFocalPoint() - mCamera->getFocalPointOffset();\n\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        osg::Vec3d sideOffset = orient * osg::Vec3d(world->getHalfExtents(mCamera->getTrackingPtr()).x() - 1, 0, 0);\n        float rayRight = world->getDistToNearestRayHit(\n            playerPos + sideOffset, orient * osg::Vec3d(1, 0, 0), limitToSwitchBack + 1);\n        float rayLeft = world->getDistToNearestRayHit(\n            playerPos - sideOffset, orient * osg::Vec3d(-1, 0, 0), limitToSwitchBack + 1);\n        float rayRightForward = world->getDistToNearestRayHit(\n            playerPos + sideOffset, orient * osg::Vec3d(1, 3, 0), limitToSwitchBack + 1);\n        float rayLeftForward = world->getDistToNearestRayHit(\n            playerPos - sideOffset, orient * osg::Vec3d(-1, 3, 0), limitToSwitchBack + 1);\n        float distRight = std::min(rayRight, rayRightForward);\n        float distLeft = std::min(rayLeft, rayLeftForward);\n\n        if (distLeft < limitToSwitch && distRight > limitToSwitchBack)\n            mMode = Mode::RightShoulder;\n        else if (distRight < limitToSwitch && distLeft > limitToSwitchBack)\n            mMode = Mode::LeftShoulder;\n        else if (distRight > limitToSwitchBack && distLeft > limitToSwitchBack)\n            mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/viewovershoulder.hpp",
    "content": "#ifndef VIEWOVERSHOULDER_H\n#define VIEWOVERSHOULDER_H\n\n#include \"camera.hpp\"\n\nnamespace MWRender\n{\n\n    class ViewOverShoulderController\n    {\n        public:\n            ViewOverShoulderController(Camera* camera);\n\n            void update();\n\n        private:\n            void trySwitchShoulder();\n            enum class Mode { RightShoulder, LeftShoulder, Combat, Swimming };\n\n            Camera* mCamera;\n            Mode mMode;\n            bool mAutoSwitchShoulder;\n            float mOverShoulderHorizontalOffset;\n            float mOverShoulderVerticalOffset;\n            bool mDefaultShoulderIsRight;\n    };\n\n}\n\n#endif // VIEWOVERSHOULDER_H\n"
  },
  {
    "path": "apps/openmw/mwrender/vismask.hpp",
    "content": "#ifndef OPENMW_MWRENDER_VISMASK_H\n#define OPENMW_MWRENDER_VISMASK_H\n\nnamespace MWRender\n{\n\n    /// Node masks used for controlling visibility of game objects.\n    /// @par Any node in the OSG scene graph can have a node mask. When traversing the scene graph,\n    /// the node visitor's traversal mask is bitwise AND'ed with the node mask. If the result of this test is\n    /// 0, then the node <i>and all its child nodes</i> are not processed.\n    /// @par Important traversal masks are the camera's cull mask (determines what is visible),\n    /// the update visitor mask (what is updated) and the intersection visitor mask (what is\n    /// selectable through mouse clicks or other intersection tests).\n    /// @par In practice, it can be useful to make a \"hierarchy\" out of the node masks - e.g. in OpenMW,\n    /// all 3D rendering nodes are child of a Scene Root node with Mask_Scene. When we do not want 3D rendering,\n    /// we can just omit Mask_Scene from the traversal mask, and do not need to omit all the individual\n    /// element masks (water, sky, terrain, etc.) since the traversal will already have stopped at the Scene root node.\n    /// @par The comments within the VisMask enum should give some hints as to what masks are commonly \"child\" of\n    /// another mask, or what type of node this mask is usually set on.\n    /// @note The mask values are not serialized within models, nor used in any other way that would break backwards\n    /// compatibility if the enumeration values were to be changed. Feel free to change them when it makes sense.\n    enum VisMask : unsigned int\n    {\n        Mask_UpdateVisitor = 0x1, // reserved for separating UpdateVisitors from CullVisitors\n\n        // child of Scene\n        Mask_Effect = (1<<1),\n        Mask_Debug = (1<<2),\n        Mask_Actor = (1<<3),\n        Mask_Player = (1<<4),\n        Mask_Sky = (1<<5),\n        Mask_Water = (1<<6), // choose Water or SimpleWater depending on detail required\n        Mask_SimpleWater = (1<<7),\n        Mask_Terrain = (1<<8),\n        Mask_FirstPerson = (1<<9),\n        Mask_Object = (1<<10),\n        Mask_Static = (1<<11),\n\n        // child of Sky\n        Mask_Sun = (1<<12),\n        Mask_WeatherParticles = (1<<13),\n\n        // top level masks\n        Mask_Scene = (1<<14),\n        Mask_GUI = (1<<15),\n\n        // Set on a ParticleSystem Drawable\n        Mask_ParticleSystem = (1<<16),\n\n        // Set on cameras within the main scene graph\n        Mask_RenderToTexture = (1<<17),\n\n        Mask_PreCompile = (1<<18),\n\n        // Set on a camera's cull mask to enable the LightManager\n        Mask_Lighting = (1<<19),\n\n        Mask_Groundcover = (1<<20),\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/water.cpp",
    "content": "#include \"water.hpp\"\n\n#include <iomanip>\n\n#include <osg/Fog>\n#include <osg/Depth>\n#include <osg/Group>\n#include <osg/Geometry>\n#include <osg/Material>\n#include <osg/PositionAttitudeTransform>\n#include <osg/ClipNode>\n#include <osg/FrontFace>\n\n#include <osgDB/ReadFile>\n\n#include <boost/filesystem/path.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <osgUtil/IncrementalCompileOperation>\n#include <osgUtil/CullVisitor>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/imagemanager.hpp>\n#include <components/resource/scenemanager.hpp>\n\n#include <components/sceneutil/shadow.hpp>\n#include <components/sceneutil/util.hpp>\n#include <components/sceneutil/waterutil.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n\n#include <components/misc/constants.hpp>\n\n#include <components/nifosg/controller.hpp>\n\n#include <components/shader/shadermanager.hpp>\n\n#include <components/esm/loadcell.hpp>\n\n#include <components/fallback/fallback.hpp>\n\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"vismask.hpp\"\n#include \"ripplesimulation.hpp\"\n#include \"renderbin.hpp\"\n#include \"util.hpp\"\n\nnamespace MWRender\n{\n\n// --------------------------------------------------------------------------------------------------------------------------------\n\n/// @brief Allows to cull and clip meshes that are below a plane. Useful for reflection & refraction camera effects.\n/// Also handles flipping of the plane when the eye point goes below it.\n/// To use, simply create the scene as subgraph of this node, then do setPlane(const osg::Plane& plane);\nclass ClipCullNode : public osg::Group\n{\n    class PlaneCullCallback : public osg::NodeCallback\n    {\n    public:\n        /// @param cullPlane The culling plane (in world space).\n        PlaneCullCallback(const osg::Plane* cullPlane)\n            : osg::NodeCallback()\n            , mCullPlane(cullPlane)\n        {\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n\n            osg::Polytope::PlaneList origPlaneList = cv->getProjectionCullingStack().back().getFrustum().getPlaneList();\n\n            osg::Plane plane = *mCullPlane;\n            plane.transform(*cv->getCurrentRenderStage()->getInitialViewMatrix());\n\n            osg::Vec3d eyePoint = cv->getEyePoint();\n            if (mCullPlane->intersect(osg::BoundingSphere(osg::Vec3d(0,0,eyePoint.z()), 0)) > 0)\n                plane.flip();\n\n            cv->getProjectionCullingStack().back().getFrustum().add(plane);\n\n            traverse(node, nv);\n\n            // undo\n            cv->getProjectionCullingStack().back().getFrustum().set(origPlaneList);\n        }\n\n    private:\n        const osg::Plane* mCullPlane;\n    };\n\n    class FlipCallback : public osg::NodeCallback\n    {\n    public:\n        FlipCallback(const osg::Plane* cullPlane)\n            : mCullPlane(cullPlane)\n        {\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n            osg::Vec3d eyePoint = cv->getEyePoint();\n\n            osg::RefMatrix* modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix());\n\n            // apply the height of the plane\n            // we can't apply this height in the addClipPlane() since the \"flip the below graph\" function would otherwise flip the height as well\n            modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * ((*mCullPlane)[3] * -1));\n\n            // flip the below graph if the eye point is above the plane\n            if (mCullPlane->intersect(osg::BoundingSphere(osg::Vec3d(0,0,eyePoint.z()), 0)) > 0)\n            {\n                modelViewMatrix->preMultScale(osg::Vec3(1,1,-1));\n            }\n\n            // move the plane back along its normal a little bit to prevent bleeding at the water shore\n            const float clipFudge = -5;\n            modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * clipFudge);\n\n            cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::RELATIVE_RF);\n            traverse(node, nv);\n            cv->popModelViewMatrix();\n        }\n\n    private:\n        const osg::Plane* mCullPlane;\n    };\n\npublic:\n    ClipCullNode()\n    {\n        addCullCallback (new PlaneCullCallback(&mPlane));\n\n        mClipNodeTransform = new osg::Group;\n        mClipNodeTransform->addCullCallback(new FlipCallback(&mPlane));\n        osg::Group::addChild(mClipNodeTransform);\n\n        mClipNode = new osg::ClipNode;\n\n        mClipNodeTransform->addChild(mClipNode);\n    }\n\n    void setPlane (const osg::Plane& plane)\n    {\n        if (plane == mPlane)\n            return;\n        mPlane = plane;\n\n        mClipNode->getClipPlaneList().clear();\n        mClipNode->addClipPlane(new osg::ClipPlane(0, osg::Plane(mPlane.getNormal(), 0))); // mPlane.d() applied in FlipCallback\n        mClipNode->setStateSetModes(*getOrCreateStateSet(), osg::StateAttribute::ON);\n        mClipNode->setCullingActive(false);\n    }\n\nprivate:\n    osg::ref_ptr<osg::Group> mClipNodeTransform;\n    osg::ref_ptr<osg::ClipNode> mClipNode;\n\n    osg::Plane mPlane;\n};\n\n/// This callback on the Camera has the effect of a RELATIVE_RF_INHERIT_VIEWPOINT transform mode (which does not exist in OSG).\n/// We want to keep the View Point of the parent camera so we will not have to recreate LODs.\nclass InheritViewPointCallback : public osg::NodeCallback\n{\npublic:\n        InheritViewPointCallback() {}\n\n    void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n    {\n        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n        osg::ref_ptr<osg::RefMatrix> modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix());\n        cv->popModelViewMatrix();\n        cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::ABSOLUTE_RF_INHERIT_VIEWPOINT);\n        traverse(node, nv);\n    }\n};\n\n/// Moves water mesh away from the camera slightly if the camera gets too close on the Z axis.\n/// The offset works around graphics artifacts that occurred with the GL_DEPTH_CLAMP when the camera gets extremely close to the mesh (seen on NVIDIA at least).\n/// Must be added as a Cull callback.\nclass FudgeCallback : public osg::NodeCallback\n{\npublic:\n    void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n    {\n        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n\n        const float fudge = 0.2;\n        if (std::abs(cv->getEyeLocal().z()) < fudge)\n        {\n            float diff = fudge - cv->getEyeLocal().z();\n            osg::RefMatrix* modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix());\n\n            if (cv->getEyeLocal().z() > 0)\n                modelViewMatrix->preMultTranslate(osg::Vec3f(0,0,-diff));\n            else\n                modelViewMatrix->preMultTranslate(osg::Vec3f(0,0,diff));\n\n            cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::RELATIVE_RF);\n            traverse(node, nv);\n            cv->popModelViewMatrix();\n        }\n        else\n            traverse(node, nv);\n    }\n};\n\nclass RainIntensityUpdater : public SceneUtil::StateSetUpdater\n{\npublic:\n    RainIntensityUpdater()\n        : mRainIntensity(0.f)\n    {\n    }\n\n    void setRainIntensity(float rainIntensity)\n    {\n        mRainIntensity = rainIntensity;\n    }\n\nprotected:\n    void setDefaults(osg::StateSet* stateset) override\n    {\n        osg::ref_ptr<osg::Uniform> rainIntensityUniform = new osg::Uniform(\"rainIntensity\", 0.0f);\n        stateset->addUniform(rainIntensityUniform.get());\n    }\n\n    void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override\n    {\n        osg::ref_ptr<osg::Uniform> rainIntensityUniform = stateset->getUniform(\"rainIntensity\");\n        if (rainIntensityUniform != nullptr)\n            rainIntensityUniform->set(mRainIntensity);\n    }\n\nprivate:\n    float mRainIntensity;\n};\n\nosg::ref_ptr<osg::Image> readPngImage (const std::string& file)\n{\n    // use boost in favor of osgDB::readImage, to handle utf-8 path issues on Windows\n    boost::filesystem::ifstream inStream;\n    inStream.open(file, std::ios_base::in | std::ios_base::binary);\n    if (inStream.fail())\n        Log(Debug::Error) << \"Error: Failed to open \" << file;\n    osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n    if (!reader)\n    {\n        Log(Debug::Error) << \"Error: Failed to read \" << file << \", no png readerwriter found\";\n        return osg::ref_ptr<osg::Image>();\n    }\n    osgDB::ReaderWriter::ReadResult result = reader->readImage(inStream);\n    if (!result.success())\n        Log(Debug::Error) << \"Error: Failed to read \" << file << \": \" << result.message() << \" code \" << result.status();\n\n    return result.getImage();\n}\n\n\nclass Refraction : public osg::Camera\n{\npublic:\n    Refraction()\n    {\n        unsigned int rttSize = Settings::Manager::getInt(\"rtt size\", \"Water\");\n        setRenderOrder(osg::Camera::PRE_RENDER, 1);\n        setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);\n        setReferenceFrame(osg::Camera::RELATIVE_RF);\n        setSmallFeatureCullingPixelSize(Settings::Manager::getInt(\"small feature culling pixel size\", \"Water\"));\n        osg::Camera::setName(\"RefractionCamera\");\n        setCullCallback(new InheritViewPointCallback);\n        setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);\n\n        setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting|Mask_Groundcover);\n        setNodeMask(Mask_RenderToTexture);\n        setViewport(0, 0, rttSize, rttSize);\n\n        // No need for Update traversal since the scene is already updated as part of the main scene graph\n        // A double update would mess with the light collection (in addition to being plain redundant)\n        setUpdateCallback(new NoTraverseCallback);\n\n        // No need for fog here, we are already applying fog on the water surface itself as well as underwater fog\n        // assign large value to effectively turn off fog\n        // shaders don't respect glDisable(GL_FOG)\n        osg::ref_ptr<osg::Fog> fog (new osg::Fog);\n        fog->setStart(10000000);\n        fog->setEnd(10000000);\n        getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);\n\n        mClipCullNode = new ClipCullNode;\n        osg::Camera::addChild(mClipCullNode);\n\n        mRefractionTexture = new osg::Texture2D;\n        mRefractionTexture->setTextureSize(rttSize, rttSize);\n        mRefractionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        mRefractionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n        mRefractionTexture->setInternalFormat(GL_RGB);\n        mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n\n        SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(this, osg::Camera::COLOR_BUFFER, mRefractionTexture);\n\n        mRefractionDepthTexture = new osg::Texture2D;\n        mRefractionDepthTexture->setTextureSize(rttSize, rttSize);\n        mRefractionDepthTexture->setSourceFormat(GL_DEPTH_COMPONENT);\n        mRefractionDepthTexture->setInternalFormat(GL_DEPTH_COMPONENT24);\n        mRefractionDepthTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        mRefractionDepthTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n        mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT);\n        mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n\n        attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture);\n\n        if (Settings::Manager::getFloat(\"refraction scale\", \"Water\") != 1) // TODO: to be removed with issue #5709\n            SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet());\n    }\n\n    void setScene(osg::Node* scene)\n    {\n        if (mScene)\n            mClipCullNode->removeChild(mScene);\n        mScene = scene;\n        mClipCullNode->addChild(scene);\n    }\n\n    void setWaterLevel(float waterLevel)\n    {\n        const float refractionScale = std::min(1.0f,std::max(0.0f,\n            Settings::Manager::getFloat(\"refraction scale\", \"Water\")));\n\n        setViewMatrix(osg::Matrix::scale(1,1,refractionScale) *\n            osg::Matrix::translate(0,0,(1.0 - refractionScale) * waterLevel));\n\n        mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,-1), osg::Vec3d(0,0, waterLevel)));\n    }\n\n    osg::Texture2D* getRefractionTexture() const\n    {\n        return mRefractionTexture.get();\n    }\n\n    osg::Texture2D* getRefractionDepthTexture() const\n    {\n        return mRefractionDepthTexture.get();\n    }\n\nprivate:\n    osg::ref_ptr<ClipCullNode> mClipCullNode;\n    osg::ref_ptr<osg::Texture2D> mRefractionTexture;\n    osg::ref_ptr<osg::Texture2D> mRefractionDepthTexture;\n    osg::ref_ptr<osg::Node> mScene;\n};\n\nclass Reflection : public osg::Camera\n{\npublic:\n    Reflection(bool isInterior)\n    {\n        setRenderOrder(osg::Camera::PRE_RENDER);\n        setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);\n        setReferenceFrame(osg::Camera::RELATIVE_RF);\n        setSmallFeatureCullingPixelSize(Settings::Manager::getInt(\"small feature culling pixel size\", \"Water\"));\n        osg::Camera::setName(\"ReflectionCamera\");\n        setCullCallback(new InheritViewPointCallback);\n\n        setInterior(isInterior);\n        setNodeMask(Mask_RenderToTexture);\n\n        unsigned int rttSize = Settings::Manager::getInt(\"rtt size\", \"Water\");\n        setViewport(0, 0, rttSize, rttSize);\n\n        // No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph\n        // A double update would mess with the light collection (in addition to being plain redundant)\n        setUpdateCallback(new NoTraverseCallback);\n\n        mReflectionTexture = new osg::Texture2D;\n        mReflectionTexture->setTextureSize(rttSize, rttSize);\n        mReflectionTexture->setInternalFormat(GL_RGB);\n        mReflectionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n        mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n        SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(this, osg::Camera::COLOR_BUFFER, mReflectionTexture);\n\n        // XXX: should really flip the FrontFace on each renderable instead of forcing clockwise.\n        osg::ref_ptr<osg::FrontFace> frontFace (new osg::FrontFace);\n        frontFace->setMode(osg::FrontFace::CLOCKWISE);\n        getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON);\n\n        mClipCullNode = new ClipCullNode;\n        osg::Camera::addChild(mClipCullNode);\n\n        SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet());\n    }\n\n    void setInterior(bool isInterior)\n    {\n        int reflectionDetail = Settings::Manager::getInt(\"reflection detail\", \"Water\");\n        reflectionDetail = std::min(5, std::max(isInterior ? 2 : 0, reflectionDetail));\n        unsigned int extraMask = 0;\n        if(reflectionDetail >= 1) extraMask |= Mask_Terrain;\n        if(reflectionDetail >= 2) extraMask |= Mask_Static;\n        if(reflectionDetail >= 3) extraMask |= Mask_Effect|Mask_ParticleSystem|Mask_Object;\n        if(reflectionDetail >= 4) extraMask |= Mask_Player|Mask_Actor;\n        if(reflectionDetail >= 5) extraMask |= Mask_Groundcover;\n        setCullMask(Mask_Scene|Mask_Sky|Mask_Lighting|extraMask);\n    }\n\n    void setWaterLevel(float waterLevel)\n    {\n        setViewMatrix(osg::Matrix::scale(1,1,-1) * osg::Matrix::translate(0,0,2 * waterLevel));\n        mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,1), osg::Vec3d(0,0,waterLevel)));\n    }\n\n    void setScene(osg::Node* scene)\n    {\n        if (mScene)\n            mClipCullNode->removeChild(mScene);\n        mScene = scene;\n        mClipCullNode->addChild(scene);\n    }\n\n    osg::Texture2D* getReflectionTexture() const\n    {\n        return mReflectionTexture.get();\n    }\n\nprivate:\n    osg::ref_ptr<osg::Texture2D> mReflectionTexture;\n    osg::ref_ptr<ClipCullNode> mClipCullNode;\n    osg::ref_ptr<osg::Node> mScene;\n};\n\n/// DepthClampCallback enables GL_DEPTH_CLAMP for the current draw, if supported.\nclass DepthClampCallback : public osg::Drawable::DrawCallback\n{\npublic:\n    void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const override\n    {\n        static bool supported = osg::isGLExtensionOrVersionSupported(renderInfo.getState()->getContextID(), \"GL_ARB_depth_clamp\", 3.3);\n        if (!supported)\n        {\n            drawable->drawImplementation(renderInfo);\n            return;\n        }\n\n        glEnable(GL_DEPTH_CLAMP);\n\n        drawable->drawImplementation(renderInfo);\n\n        // restore default\n        glDisable(GL_DEPTH_CLAMP);\n    }\n};\n\nWater::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem *resourceSystem,\n             osgUtil::IncrementalCompileOperation *ico, const std::string& resourcePath)\n    : mRainIntensityUpdater(nullptr)\n    , mParent(parent)\n    , mSceneRoot(sceneRoot)\n    , mResourceSystem(resourceSystem)\n    , mResourcePath(resourcePath)\n    , mEnabled(true)\n    , mToggled(true)\n    , mTop(0)\n    , mInterior(false)\n    , mCullCallback(nullptr)\n{\n    mSimulation.reset(new RippleSimulation(mSceneRoot, resourceSystem));\n\n    mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900);\n    mWaterGeom->setDrawCallback(new DepthClampCallback);\n    mWaterGeom->setNodeMask(Mask_Water);\n    mWaterGeom->setDataVariance(osg::Object::STATIC);\n\n    mWaterNode = new osg::PositionAttitudeTransform;\n    mWaterNode->setName(\"Water Root\");\n    mWaterNode->addChild(mWaterGeom);\n    mWaterNode->addCullCallback(new FudgeCallback);\n\n    // simple water fallback for the local map\n    osg::ref_ptr<osg::Geometry> geom2 (osg::clone(mWaterGeom.get(), osg::CopyOp::DEEP_COPY_NODES));\n    createSimpleWaterStateSet(geom2, Fallback::Map::getFloat(\"Water_Map_Alpha\"));\n    geom2->setNodeMask(Mask_SimpleWater);\n    mWaterNode->addChild(geom2);\n \n    mSceneRoot->addChild(mWaterNode);\n\n    setHeight(mTop);\n\n    updateWaterMaterial();\n\n    if (ico)\n        ico->add(mWaterNode);\n}\n\nvoid Water::setCullCallback(osg::Callback* callback)\n{\n    if (mCullCallback)\n    {\n        mWaterNode->removeCullCallback(mCullCallback);\n        if (mReflection)\n            mReflection->removeCullCallback(mCullCallback);\n        if (mRefraction)\n            mRefraction->removeCullCallback(mCullCallback);\n    }\n\n    mCullCallback = callback;\n\n    if (callback)\n    {\n        mWaterNode->addCullCallback(callback);\n        if (mReflection)\n            mReflection->addCullCallback(callback);\n        if (mRefraction)\n            mRefraction->addCullCallback(callback);\n    }\n}\n\nvoid Water::updateWaterMaterial()\n{\n    if (mReflection)\n    {\n        mReflection->removeChildren(0, mReflection->getNumChildren());\n        mParent->removeChild(mReflection);\n        mReflection = nullptr;\n    }\n    if (mRefraction)\n    {\n        mRefraction->removeChildren(0, mRefraction->getNumChildren());\n        mParent->removeChild(mRefraction);\n        mRefraction = nullptr;\n    }\n\n    if (Settings::Manager::getBool(\"shader\", \"Water\"))\n    {\n        mReflection = new Reflection(mInterior);\n        mReflection->setWaterLevel(mTop);\n        mReflection->setScene(mSceneRoot);\n        if (mCullCallback)\n            mReflection->addCullCallback(mCullCallback);\n        mParent->addChild(mReflection);\n\n        if (Settings::Manager::getBool(\"refraction\", \"Water\"))\n        {\n            mRefraction = new Refraction;\n            mRefraction->setWaterLevel(mTop);\n            mRefraction->setScene(mSceneRoot);\n            if (mCullCallback)\n                mRefraction->addCullCallback(mCullCallback);\n            mParent->addChild(mRefraction);\n        }\n\n        createShaderWaterStateSet(mWaterGeom, mReflection, mRefraction);\n    }\n    else\n        createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat(\"Water_World_Alpha\"));\n\n    updateVisible();\n}\n\nosg::Camera *Water::getReflectionCamera()\n{\n    return mReflection;\n}\n\nosg::Camera *Water::getRefractionCamera()\n{\n    return mRefraction;\n}\n\nvoid Water::createSimpleWaterStateSet(osg::Node* node, float alpha)\n{\n    osg::ref_ptr<osg::StateSet> stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water);\n\n    node->setStateSet(stateset);\n    node->setUpdateCallback(nullptr);\n    mRainIntensityUpdater = nullptr;\n\n    // Add animated textures\n    std::vector<osg::ref_ptr<osg::Texture2D> > textures;\n    int frameCount = std::max(0, std::min(Fallback::Map::getInt(\"Water_SurfaceFrameCount\"), 320));\n    const std::string& texture = Fallback::Map::getString(\"Water_SurfaceTexture\");\n    for (int i=0; i<frameCount; ++i)\n    {\n        std::ostringstream texname;\n        texname << \"textures/water/\" << texture << std::setw(2) << std::setfill('0') << i << \".dds\";\n        osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(mResourceSystem->getImageManager()->getImage(texname.str())));\n        tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n        tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n        textures.push_back(tex);\n    }\n\n    if (textures.empty())\n        return;\n\n    float fps = Fallback::Map::getFloat(\"Water_SurfaceFPS\");\n\n    osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 1.f/fps, textures));\n    controller->setSource(std::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));\n    node->setUpdateCallback(controller);\n\n    stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON);\n\n    // use a shader to render the simple water, ensuring that fog is applied per pixel as required.\n    // this could be removed if a more detailed water mesh, using some sort of paging solution, is implemented.\n    Resource::SceneManager* sceneManager = mResourceSystem->getSceneManager();\n    bool oldValue = sceneManager->getForceShaders();\n    sceneManager->setForceShaders(true);\n    sceneManager->recreateShaders(node);\n    sceneManager->setForceShaders(oldValue);\n}\n\nvoid Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction)\n{\n    // use a define map to conditionally compile the shader\n    std::map<std::string, std::string> defineMap;\n    defineMap.insert(std::make_pair(std::string(\"refraction_enabled\"), std::string(refraction ? \"1\" : \"0\")));\n\n    Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();\n    osg::ref_ptr<osg::Shader> vertexShader (shaderMgr.getShader(\"water_vertex.glsl\", defineMap, osg::Shader::VERTEX));\n    osg::ref_ptr<osg::Shader> fragmentShader (shaderMgr.getShader(\"water_fragment.glsl\", defineMap, osg::Shader::FRAGMENT));\n\n    osg::ref_ptr<osg::Texture2D> normalMap (new osg::Texture2D(readPngImage(mResourcePath + \"/shaders/water_nm.png\")));\n\n    if (normalMap->getImage())\n        normalMap->getImage()->flipVertical();\n    normalMap->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n    normalMap->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n    normalMap->setMaxAnisotropy(16);\n    normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);\n    normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n\n    osg::ref_ptr<osg::StateSet> shaderStateset = new osg::StateSet;\n    shaderStateset->addUniform(new osg::Uniform(\"normalMap\", 0));\n    shaderStateset->addUniform(new osg::Uniform(\"reflectionMap\", 1));\n\n    shaderStateset->setTextureAttributeAndModes(0, normalMap, osg::StateAttribute::ON);\n    shaderStateset->setTextureAttributeAndModes(1, reflection->getReflectionTexture(), osg::StateAttribute::ON);\n\n    if (refraction)\n    {\n        shaderStateset->setTextureAttributeAndModes(2, refraction->getRefractionTexture(), osg::StateAttribute::ON);\n        shaderStateset->setTextureAttributeAndModes(3, refraction->getRefractionDepthTexture(), osg::StateAttribute::ON);\n        shaderStateset->addUniform(new osg::Uniform(\"refractionMap\", 2));\n        shaderStateset->addUniform(new osg::Uniform(\"refractionDepthMap\", 3));\n        shaderStateset->setRenderBinDetails(MWRender::RenderBin_Default, \"RenderBin\");\n    }\n    else\n    {\n        shaderStateset->setMode(GL_BLEND, osg::StateAttribute::ON);\n\n        shaderStateset->setRenderBinDetails(MWRender::RenderBin_Water, \"RenderBin\");\n\n        osg::ref_ptr<osg::Depth> depth (new osg::Depth);\n        depth->setWriteMask(false);\n        shaderStateset->setAttributeAndModes(depth, osg::StateAttribute::ON);\n    }\n\n    shaderStateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n\n    osg::ref_ptr<osg::Program> program (new osg::Program);\n    program->addShader(vertexShader);\n    program->addShader(fragmentShader);\n    auto method = mResourceSystem->getSceneManager()->getLightingMethod();\n    if (method == SceneUtil::LightingMethod::SingleUBO)\n        program->addBindUniformBlock(\"LightBufferBinding\", static_cast<int>(Shader::UBOBinding::LightBuffer));\n    shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);\n\n    node->setStateSet(shaderStateset);\n\n    mRainIntensityUpdater = new RainIntensityUpdater();\n    node->setUpdateCallback(mRainIntensityUpdater);\n}\n\nvoid Water::processChangedSettings(const Settings::CategorySettingVector& settings)\n{\n    updateWaterMaterial();\n}\n\nWater::~Water()\n{\n    mParent->removeChild(mWaterNode);\n\n    if (mReflection)\n    {\n        mReflection->removeChildren(0, mReflection->getNumChildren());\n        mParent->removeChild(mReflection);\n        mReflection = nullptr;\n    }\n    if (mRefraction)\n    {\n        mRefraction->removeChildren(0, mRefraction->getNumChildren());\n        mParent->removeChild(mRefraction);\n        mRefraction = nullptr;\n    }\n}\n\nvoid Water::listAssetsToPreload(std::vector<std::string> &textures)\n{\n    int frameCount = std::max(0, std::min(Fallback::Map::getInt(\"Water_SurfaceFrameCount\"), 320));\n    const std::string& texture = Fallback::Map::getString(\"Water_SurfaceTexture\");\n    for (int i=0; i<frameCount; ++i)\n    {\n        std::ostringstream texname;\n        texname << \"textures/water/\" << texture << std::setw(2) << std::setfill('0') << i << \".dds\";\n        textures.push_back(texname.str());\n    }\n}\n\nvoid Water::setEnabled(bool enabled)\n{\n    mEnabled = enabled;\n    updateVisible();\n}\n\nvoid Water::changeCell(const MWWorld::CellStore* store)\n{\n    bool isInterior = !store->getCell()->isExterior();\n    bool wasInterior = mInterior;\n    if (!isInterior)\n    {\n        mWaterNode->setPosition(getSceneNodeCoordinates(store->getCell()->mData.mX, store->getCell()->mData.mY));\n        mInterior = false;\n    }\n    else\n    {\n        mWaterNode->setPosition(osg::Vec3f(0,0,mTop));\n        mInterior = true;\n    }\n    if(mInterior != wasInterior && mReflection)\n        mReflection->setInterior(mInterior);\n\n    // create a new StateSet to prevent threading issues\n    osg::ref_ptr<osg::StateSet> nodeStateSet (new osg::StateSet);\n    nodeStateSet->addUniform(new osg::Uniform(\"nodePosition\", osg::Vec3f(mWaterNode->getPosition())));\n    mWaterNode->setStateSet(nodeStateSet);\n}\n\nvoid Water::setHeight(const float height)\n{\n    mTop = height;\n\n    mSimulation->setWaterHeight(height);\n\n    osg::Vec3f pos = mWaterNode->getPosition();\n    pos.z() = height;\n    mWaterNode->setPosition(pos);\n\n    if (mReflection)\n        mReflection->setWaterLevel(mTop);\n    if (mRefraction)\n        mRefraction->setWaterLevel(mTop);\n}\n\nvoid Water::setRainIntensity(float rainIntensity)\n{\n    if (mRainIntensityUpdater)\n        mRainIntensityUpdater->setRainIntensity(rainIntensity);\n}\n\nvoid Water::update(float dt)\n{\n    mSimulation->update(dt);\n}\n\nvoid Water::updateVisible()\n{\n    bool visible = mEnabled && mToggled;\n    mWaterNode->setNodeMask(visible ? ~0u : 0u);\n    if (mRefraction)\n        mRefraction->setNodeMask(visible ? Mask_RenderToTexture : 0u);\n    if (mReflection)\n        mReflection->setNodeMask(visible ? Mask_RenderToTexture : 0u);\n}\n\nbool Water::toggle()\n{\n    mToggled = !mToggled;\n    updateVisible();\n    return mToggled;\n}\n\nbool Water::isUnderwater(const osg::Vec3f &pos) const\n{\n    return pos.z() < mTop && mToggled && mEnabled;\n}\n\nosg::Vec3f Water::getSceneNodeCoordinates(int gridX, int gridY)\n{\n    return osg::Vec3f(static_cast<float>(gridX * Constants::CellSizeInUnits + (Constants::CellSizeInUnits / 2)),\n                      static_cast<float>(gridY * Constants::CellSizeInUnits + (Constants::CellSizeInUnits / 2)), mTop);\n}\n\nvoid Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force)\n{\n    mSimulation->addEmitter (ptr, scale, force);\n}\n\nvoid Water::removeEmitter (const MWWorld::Ptr& ptr)\n{\n    mSimulation->removeEmitter (ptr);\n}\n\nvoid Water::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr)\n{\n    mSimulation->updateEmitterPtr(old, ptr);\n}\n\nvoid Water::emitRipple(const osg::Vec3f &pos)\n{\n    mSimulation->emitRipple(pos);\n}\n\nvoid Water::removeCell(const MWWorld::CellStore *store)\n{\n    mSimulation->removeCell(store);\n}\n\nvoid Water::clearRipples()\n{\n    mSimulation->clear();\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/water.hpp",
    "content": "#ifndef OPENMW_MWRENDER_WATER_H\n#define OPENMW_MWRENDER_WATER_H\n\n#include <memory>\n#include <vector>\n\n#include <osg/ref_ptr>\n#include <osg/Vec3f>\n#include <osg/Camera>\n\n#include <components/settings/settings.hpp>\n\nnamespace osg\n{\n    class Group;\n    class PositionAttitudeTransform;\n    class Geometry;\n    class Node;\n}\n\nnamespace osgUtil\n{\n    class IncrementalCompileOperation;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWWorld\n{\n    class CellStore;\n    class Ptr;\n}\n\nnamespace Fallback\n{\n    class Map;\n}\n\nnamespace MWRender\n{\n\n    class Refraction;\n    class Reflection;\n    class RippleSimulation;\n    class RainIntensityUpdater;\n\n    /// Water rendering\n    class Water\n    {\n        osg::ref_ptr<RainIntensityUpdater> mRainIntensityUpdater;\n\n        osg::ref_ptr<osg::Group> mParent;\n        osg::ref_ptr<osg::Group> mSceneRoot;\n        osg::ref_ptr<osg::PositionAttitudeTransform> mWaterNode;\n        osg::ref_ptr<osg::Geometry> mWaterGeom;\n        Resource::ResourceSystem* mResourceSystem;\n        osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;\n\n        std::unique_ptr<RippleSimulation> mSimulation;\n\n        osg::ref_ptr<Refraction> mRefraction;\n        osg::ref_ptr<Reflection> mReflection;\n\n        const std::string mResourcePath;\n\n        bool mEnabled;\n        bool mToggled;\n        float mTop;\n        bool mInterior;\n\n        osg::Callback* mCullCallback;\n\n        osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY);\n        void updateVisible();\n\n        void createSimpleWaterStateSet(osg::Node* node, float alpha);\n\n        /// @param reflection the reflection camera (required)\n        /// @param refraction the refraction camera (optional)\n        void createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction);\n\n        void updateWaterMaterial();\n\n    public:\n        Water(osg::Group* parent, osg::Group* sceneRoot,\n              Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico,\n              const std::string& resourcePath);\n        ~Water();\n\n        void setCullCallback(osg::Callback* callback);\n\n        void listAssetsToPreload(std::vector<std::string>& textures);\n\n        void setEnabled(bool enabled);\n\n        bool toggle();\n\n        bool isUnderwater(const osg::Vec3f& pos) const;\n\n        /// adds an emitter, position will be tracked automatically using its scene node\n        void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f);\n        void removeEmitter (const MWWorld::Ptr& ptr);\n        void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);\n        void emitRipple(const osg::Vec3f& pos);\n\n        void removeCell(const MWWorld::CellStore* store); ///< remove all emitters in this cell\n\n        void clearRipples();\n\n        void changeCell(const MWWorld::CellStore* store);\n        void setHeight(const float height);\n        void setRainIntensity(const float rainIntensity);\n\n        void update(float dt);\n\n        osg::Camera *getReflectionCamera();\n        osg::Camera *getRefractionCamera();\n\n        void processChangedSettings(const Settings::CategorySettingVector& settings);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwrender/weaponanimation.cpp",
    "content": "#include \"weaponanimation.hpp\"\n\n#include <osg/MatrixTransform>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/combat.hpp\"\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"animation.hpp\"\n#include \"rotatecontroller.hpp\"\n\nnamespace MWRender\n{\n\nfloat WeaponAnimationTime::getValue(osg::NodeVisitor*)\n{\n    if (mWeaponGroup.empty())\n        return 0;\n\n    float current = mAnimation->getCurrentTime(mWeaponGroup);\n    if (current == -1)\n        return 0;\n    return current - mStartTime;\n}\n\nvoid WeaponAnimationTime::setGroup(const std::string &group, bool relativeTime)\n{\n    mWeaponGroup = group;\n    mRelativeTime = relativeTime;\n\n    if (mRelativeTime)\n        mStartTime = mAnimation->getStartTime(mWeaponGroup);\n    else\n        mStartTime = 0;\n}\n\nvoid WeaponAnimationTime::updateStartTime()\n{\n    setGroup(mWeaponGroup, mRelativeTime);\n}\n\nWeaponAnimation::WeaponAnimation()\n    : mPitchFactor(0)\n{\n}\n\nWeaponAnimation::~WeaponAnimation()\n{\n\n}\n\nvoid WeaponAnimation::attachArrow(MWWorld::Ptr actor)\n{\n    const MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);\n    MWWorld::ConstContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if (weaponSlot == inv.end())\n        return;\n    if (weaponSlot->getTypeName() != typeid(ESM::Weapon).name())\n        return;\n\n    int type = weaponSlot->get<ESM::Weapon>()->mBase->mData.mType;\n    ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass;\n    if (weapclass == ESM::WeaponType::Thrown)\n    {\n        std::string soundid = weaponSlot->getClass().getUpSoundId(*weaponSlot);\n        if(!soundid.empty())\n        {\n            MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n            sndMgr->playSound3D(actor, soundid, 1.0f, 1.0f);\n        }\n        showWeapon(true);\n    }\n    else if (weapclass == ESM::WeaponType::Ranged)\n    {\n        osg::Group* parent = getArrowBone();\n        if (!parent)\n            return;\n\n        MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n        if (ammo == inv.end())\n            return;\n        std::string model = ammo->getClass().getModel(*ammo);\n\n        osg::ref_ptr<osg::Node> arrow = getResourceSystem()->getSceneManager()->getInstance(model, parent);\n\n        mAmmunition = PartHolderPtr(new PartHolder(arrow));\n    }\n}\n\nvoid WeaponAnimation::detachArrow(MWWorld::Ptr actor)\n{\n    mAmmunition.reset();\n}\n\nvoid WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)\n{\n    MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);\n    MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n    if (weapon == inv.end())\n        return;\n    if (weapon->getTypeName() != typeid(ESM::Weapon).name())\n        return;\n\n    /*\n        Start of tes3mp addition\n\n        If this is an attack by a LocalPlayer or LocalActor, record its attackStrength and\n        rangedWeaponId and prepare an attack packet for sending.\n\n        Unlike melee attacks, ranged attacks require the weapon and ammo IDs to be recorded\n        because players and actors can have multiple projectiles in the air at the same time.\n\n        If it's an attack by a DedicatedPlayer or DedicatedActor, apply the attackStrength\n        from their latest attack packet.\n    */\n    mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);\n\n    if (localAttack)\n    {\n        localAttack->attackStrength = attackStrength;\n        localAttack->rangedWeaponId = weapon->getCellRef().getRefId();\n        localAttack->shouldSend = true;\n    }\n    else\n    {\n        mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(actor);\n\n        if (dedicatedAttack)\n            attackStrength = dedicatedAttack->attackStrength;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.\n    osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))\n            * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));\n\n    const MWWorld::Store<ESM::GameSetting> &gmst =\n        MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n\n    MWMechanics::applyFatigueLoss(actor, *weapon, attackStrength);\n\n    if (MWMechanics::getWeaponType(weapon->get<ESM::Weapon>()->mBase->mData.mType)->mWeaponClass == ESM::WeaponType::Thrown)\n    {\n        /*\n            Start of tes3mp addition\n\n            If this is a local attack, clear the rangedAmmoId used for it\n        */\n        if (localAttack)\n            localAttack->rangedAmmoId = \"\";\n        /*\n            End of tes3mp addition\n        */\n\n        // Thrown weapons get detached now\n        osg::Node* weaponNode = getWeaponNode();\n        if (!weaponNode)\n            return;\n        osg::NodePathList nodepaths = weaponNode->getParentalNodePaths();\n        if (nodepaths.empty())\n            return;\n        osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();\n\n        /*\n            Start of tes3mp addition\n\n            If the actor shooting this is a LocalPlayer or LocalActor, track their projectile origin so it can be sent\n            in the next PlayerAttack or ActorAttack packet\n\n            Otherwise, set the projectileOrigin for a DedicatedPlayer or DedicatedActor\n        */\n        if (localAttack)\n        {\n            localAttack->projectileOrigin.origin[0] = launchPos.x();\n            localAttack->projectileOrigin.origin[1] = launchPos.y();\n            localAttack->projectileOrigin.origin[2] = launchPos.z();\n            localAttack->projectileOrigin.orientation[0] = orient.x();\n            localAttack->projectileOrigin.orientation[1] = orient.y();\n            localAttack->projectileOrigin.orientation[2] = orient.z();\n            localAttack->projectileOrigin.orientation[3] = orient.w();\n        }\n        else\n        {\n            mwmp::Attack* dedicatedAttack = MechanicsHelper::getDedicatedAttack(actor);\n\n            if (dedicatedAttack)\n            {\n                launchPos = osg::Vec3f(dedicatedAttack->projectileOrigin.origin[0], dedicatedAttack->projectileOrigin.origin[1], dedicatedAttack->projectileOrigin.origin[2]);\n                orient = osg::Quat(dedicatedAttack->projectileOrigin.orientation[0], dedicatedAttack->projectileOrigin.orientation[1], dedicatedAttack->projectileOrigin.orientation[2],\n                    dedicatedAttack->projectileOrigin.orientation[3]);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        float fThrownWeaponMinSpeed = gmst.find(\"fThrownWeaponMinSpeed\")->mValue.getFloat();\n        float fThrownWeaponMaxSpeed = gmst.find(\"fThrownWeaponMaxSpeed\")->mValue.getFloat();\n        float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength;\n\n        MWWorld::Ptr weaponPtr = *weapon;\n        MWBase::Environment::get().getWorld()->launchProjectile(actor, weaponPtr, launchPos, orient, weaponPtr, speed, attackStrength);\n\n        showWeapon(false);\n\n        inv.remove(*weapon, 1, actor);\n    }\n    else\n    {\n        // With bows and crossbows only the used arrow/bolt gets detached\n        MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);\n        if (ammo == inv.end())\n            return;\n\n        if (!mAmmunition)\n            return;\n\n        /*\n            Start of tes3mp addition\n\n            If this is a local attack, record the rangedAmmoId used for it\n        */\n        if (localAttack)\n            localAttack->rangedAmmoId = ammo->getCellRef().getRefId();\n        /*\n            End of tes3mp addition\n        */\n\n        osg::ref_ptr<osg::Node> ammoNode = mAmmunition->getNode();\n        osg::NodePathList nodepaths = ammoNode->getParentalNodePaths();\n        if (nodepaths.empty())\n            return;\n        osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();\n\n        /*\n            Start of tes3mp addition\n\n            If the actor shooting this is a LocalPlayer or LocalActor, track their projectile origin so it can be sent\n            in the next PlayerAttack or ActorAttack packet\n\n            Otherwise, set the projectileOrigin for a DedicatedPlayer or DedicatedActor\n        */\n        if (localAttack)\n        {\n            localAttack->projectileOrigin.origin[0] = launchPos.x();\n            localAttack->projectileOrigin.origin[1] = launchPos.y();\n            localAttack->projectileOrigin.origin[2] = launchPos.z();\n            localAttack->projectileOrigin.orientation[0] = orient.x();\n            localAttack->projectileOrigin.orientation[1] = orient.y();\n            localAttack->projectileOrigin.orientation[2] = orient.z();\n            localAttack->projectileOrigin.orientation[3] = orient.w();\n        }\n        else\n        {\n            mwmp::Attack* dedicatedAttack = MechanicsHelper::getDedicatedAttack(actor);\n\n            if (dedicatedAttack)\n            {\n                launchPos = osg::Vec3f(dedicatedAttack->projectileOrigin.origin[0], dedicatedAttack->projectileOrigin.origin[1], dedicatedAttack->projectileOrigin.origin[2]);\n                orient = osg::Quat(dedicatedAttack->projectileOrigin.orientation[0], dedicatedAttack->projectileOrigin.orientation[1], dedicatedAttack->projectileOrigin.orientation[2],\n                    dedicatedAttack->projectileOrigin.orientation[3]);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        float fProjectileMinSpeed = gmst.find(\"fProjectileMinSpeed\")->mValue.getFloat();\n        float fProjectileMaxSpeed = gmst.find(\"fProjectileMaxSpeed\")->mValue.getFloat();\n        float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength;\n\n        MWWorld::Ptr weaponPtr = *weapon;\n        MWWorld::Ptr ammoPtr = *ammo;\n        MWBase::Environment::get().getWorld()->launchProjectile(actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength);\n\n        inv.remove(ammoPtr, 1, actor);\n        mAmmunition.reset();\n    }\n}\n\nvoid WeaponAnimation::addControllers(const std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >& nodes,\n    std::vector<std::pair<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback>>> &map, osg::Node* objectRoot)\n{\n    for (int i=0; i<2; ++i)\n    {\n        mSpineControllers[i] = nullptr;\n\n        std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >::const_iterator found = nodes.find(i == 0 ? \"bip01 spine1\" : \"bip01 spine2\");\n        if (found != nodes.end())\n        {\n            osg::Node* node = found->second;\n            mSpineControllers[i] = new RotateController(objectRoot);\n            node->addUpdateCallback(mSpineControllers[i]);\n            map.emplace_back(node, mSpineControllers[i]);\n        }\n    }\n}\n\nvoid WeaponAnimation::deleteControllers()\n{\n    for (int i=0; i<2; ++i)\n        mSpineControllers[i] = nullptr;\n}\n\nvoid WeaponAnimation::configureControllers(float characterPitchRadians)\n{\n    if (mPitchFactor == 0.f || characterPitchRadians == 0.f)\n    {\n        setControllerEnabled(false);\n        return;\n    }\n\n    float pitch = characterPitchRadians * mPitchFactor;\n    osg::Quat rotate (pitch/2, osg::Vec3f(-1,0,0));\n    setControllerRotate(rotate);\n    setControllerEnabled(true);\n}\n\nvoid WeaponAnimation::setControllerRotate(const osg::Quat& rotate)\n{\n    for (int i=0; i<2; ++i)\n        if (mSpineControllers[i])\n            mSpineControllers[i]->setRotate(rotate);\n}\n\nvoid WeaponAnimation::setControllerEnabled(bool enabled)\n{\n    for (int i=0; i<2; ++i)\n        if (mSpineControllers[i])\n            mSpineControllers[i]->setEnabled(enabled);\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwrender/weaponanimation.hpp",
    "content": "#ifndef OPENMW_MWRENDER_WEAPONANIMATION_H\n#define OPENMW_MWRENDER_WEAPONANIMATION_H\n\n#include <components/sceneutil/controller.hpp>\n\n#include \"../mwworld/ptr.hpp\"\n#include \"animation.hpp\"\n\nnamespace MWRender\n{\n\n    class RotateController;\n\n    class WeaponAnimationTime : public SceneUtil::ControllerSource\n    {\n    private:\n        Animation* mAnimation;\n        std::string mWeaponGroup;\n        float mStartTime;\n        bool mRelativeTime;\n    public:\n        WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0), mRelativeTime(false) {}\n        void setGroup(const std::string& group, bool relativeTime);\n        void updateStartTime();\n\n        float getValue(osg::NodeVisitor* nv) override;\n    };\n\n    /// Handles attach & release of projectiles for ranged weapons\n    class WeaponAnimation\n    {\n    public:\n        WeaponAnimation();\n        virtual ~WeaponAnimation();\n\n        /// @note If no weapon (or an invalid weapon) is equipped, this function is a no-op.\n        void attachArrow(MWWorld::Ptr actor);\n\n        void detachArrow(MWWorld::Ptr actor);\n\n        /// @note If no weapon (or an invalid weapon) is equipped, this function is a no-op.\n        void releaseArrow(MWWorld::Ptr actor, float attackStrength);\n\n        /// Add WeaponAnimation-related controllers to \\a nodes and store the added controllers in \\a map.\n        void addControllers(const std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >& nodes,\n                std::vector<std::pair<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback>>>& map, osg::Node* objectRoot);\n\n        void deleteControllers();\n\n        /// Configure controllers, should be called every animation frame.\n        void configureControllers(float characterPitchRadians);\n\n    protected:\n        PartHolderPtr mAmmunition;\n\n        osg::ref_ptr<RotateController> mSpineControllers[2];\n\n        void setControllerRotate(const osg::Quat& rotate);\n        void setControllerEnabled(bool enabled);\n\n        virtual osg::Group* getArrowBone() = 0;\n        virtual osg::Node* getWeaponNode() = 0;\n        virtual Resource::ResourceSystem* getResourceSystem() = 0;\n\n        virtual void showWeapon(bool show) = 0;\n\n        /// A relative factor (0-1) that decides if and how much the skeleton should be pitched\n        /// to indicate the facing orientation of the character, for ranged weapon aiming.\n        float mPitchFactor;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/aiextensions.cpp",
    "content": "#include \"aiextensions.hpp\"\n\n#include <stdexcept>\n\n#include <components/debug/debuglog.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ActorList.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/compiler/extensions.hpp>\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/aiactivate.hpp\"\n#include \"../mwmechanics/aiescort.hpp\"\n#include \"../mwmechanics/aifollow.hpp\"\n#include \"../mwmechanics/aitravel.hpp\"\n#include \"../mwmechanics/aiwander.hpp\"\n#include \"../mwmechanics/aiface.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"interpretercontext.hpp\"\n#include \"ref.hpp\"\n\n\nnamespace MWScript\n{\n    namespace Ai\n    {\n        template<class R>\n        class OpAiActivate : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string objectID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    // discard additional arguments (reset), because we have no idea what they mean.\n                    for (unsigned int i=0; i<arg0; ++i) runtime.pop();\n\n                    MWMechanics::AiActivate activatePackage(objectID);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(activatePackage, ptr);\n                    Log(Debug::Info) << \"AiActivate\";\n                }\n        };\n\n        template<class R>\n        class OpAiTravel : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n\n                    // discard additional arguments (reset), because we have no idea what they mean.\n                    for (unsigned int i=0; i<arg0; ++i) runtime.pop();\n\n                    MWMechanics::AiTravel travelPackage(x, y, z);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(travelPackage, ptr);\n\n                    Log(Debug::Info) << \"AiTravel: \" << x << \", \" << y << \", \" << z;\n                }\n        };\n\n        template<class R>\n        class OpAiEscort : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Float duration = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n\n                    // discard additional arguments (reset), because we have no idea what they mean.\n                    for (unsigned int i=0; i<arg0; ++i) runtime.pop();\n\n                    MWMechanics::AiEscort escortPackage(actorID, static_cast<int>(duration), x, y, z);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);\n\n                    Log(Debug::Info) << \"AiEscort: \" << x << \", \" << y << \", \" << z << \", \" << duration;\n                }\n        };\n\n        template<class R>\n        class OpAiEscortCell : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    std::string cellID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Float duration = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n\n                    // discard additional arguments (reset), because we have no idea what they mean.\n                    for (unsigned int i=0; i<arg0; ++i) runtime.pop();\n\n                    if (cellID.empty())\n                        throw std::runtime_error(\"AiEscortCell: no cell ID given\");\n\n                    MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().find(cellID);\n\n                    MWMechanics::AiEscort escortPackage(actorID, cellID, static_cast<int>(duration), x, y, z);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);\n\n                    Log(Debug::Info) << \"AiEscort: \" << x << \", \" << y << \", \" << z << \", \" << duration;\n                }\n        };\n\n        template<class R>\n        class OpGetAiPackageDone : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).getAiSequence().isPackageDone();\n\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpAiWander : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer range = static_cast<Interpreter::Type_Integer>(runtime[0].mFloat);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer duration = static_cast<Interpreter::Type_Integer>(runtime[0].mFloat);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer time = static_cast<Interpreter::Type_Integer>(runtime[0].mFloat);\n                    runtime.pop();\n\n                    // Chance for Idle is unused\n                    if (arg0)\n                    {\n                        --arg0;\n                        runtime.pop();\n                    }\n\n                    std::vector<unsigned char> idleList;\n                    bool repeat = false;\n\n                    // Chances for Idle2-Idle9\n                    for(int i=2; i<=9 && arg0; ++i)\n                    {\n                        if(!repeat)\n                            repeat = true;\n                        Interpreter::Type_Integer idleValue = runtime[0].mInteger;\n                        idleValue = std::min(255, std::max(0, idleValue));\n                        idleList.push_back(idleValue);\n                        runtime.pop();\n                        --arg0;\n                    }\n\n                    if(arg0)\n                    {\n                        repeat = runtime[0].mInteger != 0;\n                        runtime.pop();\n                        --arg0;\n                    }\n\n                    // discard additional arguments (reset), because we have no idea what they mean.\n                    for (unsigned int i=0; i<arg0; ++i) runtime.pop();\n\n                    MWMechanics::AiWander wanderPackage(range, duration, time, idleList, repeat);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(wanderPackage, ptr);\n                }\n        };\n\n        template<class R>\n        class OpGetAiSetting : public Interpreter::Opcode0\n        {\n            MWMechanics::CreatureStats::AiSetting mIndex;\n            public:\n                OpGetAiSetting(MWMechanics::CreatureStats::AiSetting index) : mIndex(index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push(ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getModified(false));\n                }\n        };\n        template<class R>\n        class OpModAiSetting : public Interpreter::Opcode0\n        {\n            MWMechanics::CreatureStats::AiSetting mIndex;\n            public:\n                OpModAiSetting(MWMechanics::CreatureStats::AiSetting index) : mIndex(index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    int modified = ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value;\n\n                    ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, modified);\n                    ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, modified);\n                }\n        };\n        template<class R>\n        class OpSetAiSetting : public Interpreter::Opcode0\n        {\n            MWMechanics::CreatureStats::AiSetting mIndex;\n            public:\n                OpSetAiSetting(MWMechanics::CreatureStats::AiSetting index) : mIndex(index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    /*\n                        Start of tes3mp addition\n\n                        Track the original stat value, to ensure we don't send repetitive packets to the server\n                        about its changes\n                    */\n                    MWMechanics::Stat<int> stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex);\n\n                    int initialValue = stat.getBase();\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    ptr.getClass().getCreatureStats(ptr).setAiSetting(mIndex, value);\n                    ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, value);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Setting an actor's AI_Fight to 100 is equivalent to starting combat with the local player,\n                        so send a combat packet regardless of whether we're the cell authority or not; the server\n                        can decide if it wants to comply with them by forwarding them to the cell authority\n                    */\n                    if (stat.getBase() != initialValue && mIndex == MWMechanics::CreatureStats::AI_Fight && value == 100)\n                    {\n                        mwmp::ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList();\n                        actorList->reset();\n                        actorList->cell = *ptr.getCell()->getCell();\n                        actorList->addAiActor(ptr, MWBase::Environment::get().getWorld()->getPlayerPtr(), mwmp::BaseActorList::COMBAT);\n                        actorList->sendAiActors();\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template<class R>\n        class OpAiFollow : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Float duration = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n\n                    // discard additional arguments (reset), because we have no idea what they mean.\n                    for (unsigned int i=0; i<arg0; ++i) runtime.pop();\n\n                    MWMechanics::AiFollow followPackage(actorID, duration, x, y ,z);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);\n\n                    Log(Debug::Info) << \"AiFollow: \" << actorID << \", \" << x << \", \" << y << \", \" << z << \", \" << duration;\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send ActorAI packets when an actor becomes a follower, regardless of whether we're\n                        the cell authority or not; the server can decide if it wants to comply with them by\n                        forwarding them to the cell authority\n                    */\n                    MWWorld::Ptr targetPtr = MWBase::Environment::get().getWorld()->searchPtr(actorID, true);\n\n                    if (targetPtr)\n                    {\n                        mwmp::ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList();\n                        actorList->reset();\n                        actorList->cell = *ptr.getCell()->getCell();\n                        actorList->addAiActor(ptr, targetPtr, mwmp::BaseActorList::FOLLOW);\n                        actorList->sendAiActors();\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template<class R>\n        class OpAiFollowCell : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    std::string cellID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Float duration = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n\n                    // discard additional arguments (reset), because we have no idea what they mean.\n                    for (unsigned int i=0; i<arg0; ++i) runtime.pop();\n\n                    MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y ,z);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);\n                    Log(Debug::Info) << \"AiFollow: \" << actorID << \", \" << x << \", \" << y << \", \" << z << \", \" << duration;\n                }\n        };\n\n        template<class R>\n        class OpGetCurrentAIPackage : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    const auto value = static_cast<Interpreter::Type_Integer>(ptr.getClass().getCreatureStats (ptr).getAiSequence().getLastRunTypeId());\n\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpGetDetected : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr observer = R()(runtime, false); // required=false\n\n                    std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->searchPtr(actorID, true, false);\n\n                    Interpreter::Type_Integer value = 0;\n                    if (!actor.isEmpty())\n                        value = MWBase::Environment::get().getMechanicsManager()->isActorDetected(actor, observer);\n\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpGetLineOfSight : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n\n                    MWWorld::Ptr source = R()(runtime);\n\n                    std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n\n                    MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->searchPtr(actorID, true, false);\n                    bool value = false;\n                    if (!dest.isEmpty() && source.getClass().isActor() && dest.getClass().isActor())\n                    {\n                        value = MWBase::Environment::get().getWorld()->getLOS(source,dest);\n                    }\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpGetTarget : public Interpreter::Opcode0\n        {\n            public:\n                void execute (Interpreter::Runtime &runtime) override\n                {\n                    MWWorld::Ptr actor = R()(runtime);\n                    std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);\n\n                    bool targetsAreEqual = false;\n                    MWWorld::Ptr targetPtr;\n                    if (creatureStats.getAiSequence().getCombatTarget (targetPtr))\n                    {\n                        if (!targetPtr.isEmpty() && targetPtr.getCellRef().getRefId() == testedTargetId)\n                            targetsAreEqual = true;\n                    }\n                    else if (testedTargetId == \"player\") // Currently the player ID is hardcoded\n                    {\n                        MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager();\n                        bool greeting = mechMgr->getGreetingState(actor) == MWMechanics::Greet_InProgress;\n                        bool sayActive = MWBase::Environment::get().getSoundManager()->sayActive(actor);\n                        targetsAreEqual = (greeting && sayActive) || mechMgr->isTurningToPlayer(actor);\n                    }\n                    runtime.push(int(targetsAreEqual));\n                }\n        };\n\n        template<class R>\n        class OpStartCombat : public Interpreter::Opcode0\n        {\n            public:\n                void execute (Interpreter::Runtime &runtime) override\n                {\n                    MWWorld::Ptr actor = R()(runtime);\n                    std::string targetID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(targetID, true, false);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Track whether this actor is already in combat with its target, to ensure we don't\n                        send repetitive packets to the server\n                    */\n                    bool alreadyInCombatWithTarget = !target.isEmpty() ? actor.getClass().getCreatureStats(actor).getAiSequence().isInCombat(target) : false;\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    if (!target.isEmpty())\n                        MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send ActorAI packets when an actor starts combat, regardless of whether we're the\n                        cell authority or not; the server can decide if it wants to comply with them by\n                        forwarding them to the cell authority\n                    */\n                    if (!target.isEmpty() && !alreadyInCombatWithTarget)\n                    {\n                        mwmp::ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList();\n                        actorList->reset();\n                        actorList->cell = *actor.getCell()->getCell();\n                        actorList->addAiActor(actor, target, mwmp::BaseActorList::COMBAT);\n                        actorList->sendAiActors();\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template<class R>\n        class OpStopCombat : public Interpreter::Opcode0\n        {\n            public:\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr actor = R()(runtime);\n                    MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);\n                    creatureStats.getAiSequence().stopCombat();\n                }\n        };\n\n        class OpToggleAI : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled = MWBase::Environment::get().getMechanicsManager()->toggleAI();\n\n                    runtime.getContext().report (enabled ? \"AI -> On\" : \"AI -> Off\");\n                }\n        };\n\n        template <class R>\n        class OpFace : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime& runtime) override\n            {\n                MWWorld::Ptr actor = R()(runtime);\n\n                Interpreter::Type_Float x = runtime[0].mFloat;\n                runtime.pop();\n\n                Interpreter::Type_Float y = runtime[0].mFloat;\n                runtime.pop();\n\n                MWMechanics::AiFace facePackage(x, y);\n                actor.getClass().getCreatureStats(actor).getAiSequence().stack(facePackage, actor);\n            }\n        };\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment3 (Compiler::Ai::opcodeAIActivate, new OpAiActivate<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAIActivateExplicit, new OpAiActivate<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiTravel, new OpAiTravel<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiTravelExplicit, new OpAiTravel<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiEscort, new OpAiEscort<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortExplicit, new OpAiEscort<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortCell, new OpAiEscortCell<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortCellExplicit, new OpAiEscortCell<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiWander, new OpAiWander<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiWanderExplicit, new OpAiWander<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiFollow, new OpAiFollow<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowExplicit, new OpAiFollow<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowCell, new OpAiFollowCell<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowCellExplicit, new OpAiFollowCell<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetAiPackageDone, new OpGetAiPackageDone<ImplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetAiPackageDoneExplicit,\n                new OpGetAiPackageDone<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetDetected, new OpGetDetected<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetTarget, new OpGetTarget<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetTargetExplicit, new OpGetTarget<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeStartCombat, new OpStartCombat<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeStartCombatExplicit, new OpStartCombat<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeStopCombat, new OpStopCombat<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeStopCombatExplicit, new OpStopCombat<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI);\n\n            interpreter.installSegment5 (Compiler::Ai::opcodeSetHello, new OpSetAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Hello));\n            interpreter.installSegment5 (Compiler::Ai::opcodeSetHelloExplicit, new OpSetAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Hello));\n            interpreter.installSegment5 (Compiler::Ai::opcodeSetFight, new OpSetAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Fight));\n            interpreter.installSegment5 (Compiler::Ai::opcodeSetFightExplicit, new OpSetAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Fight));\n            interpreter.installSegment5 (Compiler::Ai::opcodeSetFlee, new OpSetAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Flee));\n            interpreter.installSegment5 (Compiler::Ai::opcodeSetFleeExplicit, new OpSetAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Flee));\n            interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarm, new OpSetAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Alarm));\n            interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarmExplicit, new OpSetAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Alarm));\n\n            interpreter.installSegment5 (Compiler::Ai::opcodeModHello, new OpModAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Hello));\n            interpreter.installSegment5 (Compiler::Ai::opcodeModHelloExplicit, new OpModAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Hello));\n            interpreter.installSegment5 (Compiler::Ai::opcodeModFight, new OpModAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Fight));\n            interpreter.installSegment5 (Compiler::Ai::opcodeModFightExplicit, new OpModAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Fight));\n            interpreter.installSegment5 (Compiler::Ai::opcodeModFlee, new OpModAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Flee));\n            interpreter.installSegment5 (Compiler::Ai::opcodeModFleeExplicit, new OpModAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Flee));\n            interpreter.installSegment5 (Compiler::Ai::opcodeModAlarm, new OpModAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Alarm));\n            interpreter.installSegment5 (Compiler::Ai::opcodeModAlarmExplicit, new OpModAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Alarm));\n\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetHello, new OpGetAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Hello));\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetHelloExplicit, new OpGetAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Hello));\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetFight, new OpGetAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Fight));\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetFightExplicit, new OpGetAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Fight));\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetFlee, new OpGetAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Flee));\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Flee));\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting<ImplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Alarm));\n            interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting<ExplicitRef>(MWMechanics::CreatureStats::AiSetting::AI_Alarm));\n\n            interpreter.installSegment5 (Compiler::Ai::opcodeFace, new OpFace<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Ai::opcodeFaceExplicit, new OpFace<ExplicitRef>);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/aiextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_AIEXTENSIONS_H\n#define GAME_SCRIPT_AIEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief AI-related script functionality\n    namespace Ai\n    {\n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/animationextensions.cpp",
    "content": "#include \"animationextensions.hpp\"\n\n#include <stdexcept>\n#include <limits>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/ScriptController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"interpretercontext.hpp\"\n#include \"ref.hpp\"\n\nnamespace MWScript\n{\n    namespace Animation\n    {\n        template<class R>\n        class OpSkipAnim : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    MWBase::Environment::get().getMechanicsManager()->skipAnimation (ptr);\n               }\n        };\n\n        template<class R>\n        class OpPlayAnim : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (!ptr.getRefData().isEnabled())\n                        return;\n\n                    std::string group = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer mode = 0;\n\n                    if (arg0==1)\n                    {\n                        mode = runtime[0].mInteger;\n                        runtime.pop();\n\n                        if (mode<0 || mode>2)\n                            throw std::runtime_error (\"animation mode out of range\");\n                    }\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_ANIM_PLAY every time an animation is played for an object\n                        through an approved script\n                    */\n                    if (mwmp::Main::isValidPacketScript(ptr.getClass().getScript(ptr)))\n                    {\n                        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                        objectList->reset();\n\n                        objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                        objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                        objectList->addObjectAnimPlay(ptr, group, mode);\n                        objectList->sendObjectAnimPlay();\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, std::numeric_limits<int>::max(), true);\n               }\n        };\n\n        template<class R>\n        class OpLoopAnim : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (!ptr.getRefData().isEnabled())\n                        return;\n\n                    std::string group = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer loops = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (loops<0)\n                        throw std::runtime_error (\"number of animation loops must be non-negative\");\n\n                    Interpreter::Type_Integer mode = 0;\n\n                    if (arg0==1)\n                    {\n                        mode = runtime[0].mInteger;\n                        runtime.pop();\n\n                        if (mode<0 || mode>2)\n                            throw std::runtime_error (\"animation mode out of range\");\n                    }\n\n                    MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops + 1, true);\n               }\n        };\n        \n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5 (Compiler::Animation::opcodeSkipAnim, new OpSkipAnim<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Animation::opcodeSkipAnimExplicit, new OpSkipAnim<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Animation::opcodePlayAnim, new OpPlayAnim<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Animation::opcodePlayAnimExplicit, new OpPlayAnim<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Animation::opcodeLoopAnim, new OpLoopAnim<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Animation::opcodeLoopAnimExplicit, new OpLoopAnim<ExplicitRef>);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/animationextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_ANIMATIONEXTENSIONS_H\n#define GAME_SCRIPT_ANIMATIONEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    namespace Animation\n    {\n        void registerExtensions (Compiler::Extensions& extensions);\n\n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/cellextensions.cpp",
    "content": "#include \"cellextensions.hpp\"\n\n#include <limits>\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwworld/actionteleport.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"interpretercontext.hpp\"\n\nnamespace MWScript\n{\n    namespace Cell\n    {\n        class OpCellChanged : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push (MWBase::Environment::get().getWorld()->hasCellChanged() ? 1 : 0);\n                }\n        };\n\n        class OpTestCells : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame)\n                    {\n                        runtime.getContext().report(\"Use TestCells from the main menu, when there is no active game session.\");\n                        return;\n                    }\n\n                    bool wasConsole = MWBase::Environment::get().getWindowManager()->isConsoleMode();\n                    if (wasConsole)\n                        MWBase::Environment::get().getWindowManager()->toggleConsole();\n\n                    MWBase::Environment::get().getWorld()->testExteriorCells();\n\n                    if (wasConsole)\n                        MWBase::Environment::get().getWindowManager()->toggleConsole();\n                }\n        };\n\n        class OpTestInteriorCells : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame)\n                    {\n                        runtime.getContext().report(\"Use TestInteriorCells from the main menu, when there is no active game session.\");\n                        return;\n                    }\n\n                    bool wasConsole = MWBase::Environment::get().getWindowManager()->isConsoleMode();\n                    if (wasConsole)\n                        MWBase::Environment::get().getWindowManager()->toggleConsole();\n\n                    MWBase::Environment::get().getWorld()->testInteriorCells();\n\n                    if (wasConsole)\n                        MWBase::Environment::get().getWindowManager()->toggleConsole();\n                }\n        };\n\n        class OpCOC : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string cell = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    ESM::Position pos;\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n                    const MWWorld::Ptr playerPtr = world->getPlayerPtr();\n\n                    if (world->findExteriorPosition(cell, pos))\n                    {\n                        MWWorld::ActionTeleport(\"\", pos, false).execute(playerPtr);\n                        world->adjustPosition(playerPtr, false);\n                    }\n                    else\n                    {\n                        // Change to interior even if findInteriorPosition()\n                        // yields false. In this case position will be zero-point.\n                        world->findInteriorPosition(cell, pos);\n                        MWWorld::ActionTeleport(cell, pos, false).execute(playerPtr);\n                    }\n                }\n        };\n\n        class OpCOE : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    Interpreter::Type_Integer x = runtime[0].mInteger;\n                    runtime.pop();\n\n                    Interpreter::Type_Integer y = runtime[0].mInteger;\n                    runtime.pop();\n\n                    ESM::Position pos;\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n                    const MWWorld::Ptr playerPtr = world->getPlayerPtr();\n\n                    world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true);\n                    pos.pos[2] = 0;\n\n                    pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;\n\n                    MWWorld::ActionTeleport(\"\", pos, false).execute(playerPtr);\n                    world->adjustPosition(playerPtr, false);\n                }\n        };\n\n        class OpGetInterior : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    if (!MWMechanics::getPlayer().isInCell())\n                    {\n                        runtime.push (0);\n                        return;\n                    }\n\n                    bool interior =\n                        !MWMechanics::getPlayer().getCell()->getCell()->isExterior();\n\n                    runtime.push (interior ? 1 : 0);\n                }\n        };\n\n        class OpGetPCCell : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string name = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if (!MWMechanics::getPlayer().isInCell())\n                    {\n                        runtime.push(0);\n                        return;\n                    }\n                    const MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell();\n\n                    std::string current = MWBase::Environment::get().getWorld()->getCellName(cell);\n                    Misc::StringUtils::lowerCaseInPlace(current);\n\n                    bool match = current.length()>=name.length() &&\n                        current.substr (0, name.length())==name;\n\n                    runtime.push (match ? 1 : 0);\n                }\n        };\n\n        class OpGetWaterLevel : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    if (!MWMechanics::getPlayer().isInCell())\n                    {\n                        runtime.push(0.f);\n                        return;\n                    }\n                    MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell();\n                    if (cell->isExterior())\n                        runtime.push(0.f); // vanilla oddity, return 0 even though water is actually at -1\n                    else if (cell->getCell()->hasWater())\n                        runtime.push (cell->getWaterLevel());\n                    else\n                        runtime.push (-std::numeric_limits<float>::max());\n                }\n        };\n\n        class OpSetWaterLevel : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    Interpreter::Type_Float level = runtime[0].mFloat;\n\n                    if (!MWMechanics::getPlayer().isInCell())\n                    {\n                        return;\n                    }\n\n                    MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell();\n\n                    if (cell->getCell()->isExterior())\n                        throw std::runtime_error(\"Can't set water level in exterior cell\");\n\n                    cell->setWaterLevel (level);\n                    MWBase::Environment::get().getWorld()->setWaterHeight (cell->getWaterLevel());\n                }\n        };\n\n        class OpModWaterLevel : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    Interpreter::Type_Float level = runtime[0].mFloat;\n\n                    if (!MWMechanics::getPlayer().isInCell())\n                    {\n                        return;\n                    }\n\n                    MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell();\n\n                    if (cell->getCell()->isExterior())\n                        throw std::runtime_error(\"Can't set water level in exterior cell\");\n\n                    cell->setWaterLevel (cell->getWaterLevel()+level);\n                    MWBase::Environment::get().getWorld()->setWaterHeight(cell->getWaterLevel());\n                }\n        };\n\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5 (Compiler::Cell::opcodeCellChanged, new OpCellChanged);\n            interpreter.installSegment5 (Compiler::Cell::opcodeTestCells, new OpTestCells);\n            interpreter.installSegment5 (Compiler::Cell::opcodeTestInteriorCells, new OpTestInteriorCells);\n            interpreter.installSegment5 (Compiler::Cell::opcodeCOC, new OpCOC);\n            interpreter.installSegment5 (Compiler::Cell::opcodeCOE, new OpCOE);\n            interpreter.installSegment5 (Compiler::Cell::opcodeGetInterior, new OpGetInterior);\n            interpreter.installSegment5 (Compiler::Cell::opcodeGetPCCell, new OpGetPCCell);\n            interpreter.installSegment5 (Compiler::Cell::opcodeGetWaterLevel, new OpGetWaterLevel);\n            interpreter.installSegment5 (Compiler::Cell::opcodeSetWaterLevel, new OpSetWaterLevel);\n            interpreter.installSegment5 (Compiler::Cell::opcodeModWaterLevel, new OpModWaterLevel);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/cellextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_CELLEXTENSIONS_H\n#define GAME_SCRIPT_CELLEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief cell-related script functionality\n    namespace Cell\n    {        \n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n\n\n"
  },
  {
    "path": "apps/openmw/mwscript/compilercontext.cpp",
    "content": "#include \"compilercontext.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include <components/esm/loaddial.hpp>\n\n#include <components/compiler/locals.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/manualref.hpp\"\n\nnamespace MWScript\n{\n    CompilerContext::CompilerContext (Type type)\n    : mType (type)\n    {}\n\n    bool CompilerContext::canDeclareLocals() const\n    {\n        return mType==Type_Full;\n    }\n\n    char CompilerContext::getGlobalType (const std::string& name) const\n    {\n        return MWBase::Environment::get().getWorld()->getGlobalVariableType (name);\n    }\n\n    std::pair<char, bool> CompilerContext::getMemberType (const std::string& name,\n        const std::string& id) const\n    {\n        std::string script;\n        bool reference = false;\n\n        if (const ESM::Script *scriptRecord =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().search (id))\n        {\n            script = scriptRecord->mId;\n        }\n        else\n        {\n            MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id);\n\n            script = ref.getPtr().getClass().getScript (ref.getPtr());\n            reference = true;\n        }\n\n        char type = ' ';\n\n        if (!script.empty())\n            type = MWBase::Environment::get().getScriptManager()->getLocals (script).getType (\n                Misc::StringUtils::lowerCase (name));\n\n        return std::make_pair (type, reference);\n    }\n\n    bool CompilerContext::isId (const std::string& name) const\n    {\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        return\n            store.get<ESM::Activator>().search (name) ||\n            store.get<ESM::Potion>().search (name) ||\n            store.get<ESM::Apparatus>().search (name) ||\n            store.get<ESM::Armor>().search (name) ||\n            store.get<ESM::Book>().search (name) ||\n            store.get<ESM::Clothing>().search (name) ||\n            store.get<ESM::Container>().search (name) ||\n            store.get<ESM::Creature>().search (name) ||\n            store.get<ESM::Door>().search (name) ||\n            store.get<ESM::Ingredient>().search (name) ||\n            store.get<ESM::CreatureLevList>().search (name) ||\n            store.get<ESM::ItemLevList>().search (name) ||\n            store.get<ESM::Light>().search (name) ||\n            store.get<ESM::Lockpick>().search (name) ||\n            store.get<ESM::Miscellaneous>().search (name) ||\n            store.get<ESM::NPC>().search (name) ||\n            store.get<ESM::Probe>().search (name) ||\n            store.get<ESM::Repair>().search (name) ||\n            store.get<ESM::Static>().search (name) ||\n            store.get<ESM::Weapon>().search (name) ||\n            store.get<ESM::Script>().search (name);\n    }\n\n    bool CompilerContext::isJournalId (const std::string& name) const\n    {\n        const MWWorld::ESMStore &store =\n            MWBase::Environment::get().getWorld()->getStore();\n\n        const ESM::Dialogue *topic = store.get<ESM::Dialogue>().search (name);\n\n        return topic && topic->mType==ESM::Dialogue::Journal;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/compilercontext.hpp",
    "content": "#ifndef GAME_SCRIPT_COMPILERCONTEXT_H\n#define GAME_SCRIPT_COMPILERCONTEXT_H\n\n#include <components/compiler/context.hpp>\n\nnamespace MWScript\n{\n    class CompilerContext : public Compiler::Context\n    {\n        public:\n\n            enum Type\n            {\n                Type_Full, // global, local, targeted\n                Type_Dialogue,\n                Type_Console\n            };\n\n        private:\n\n            Type mType;\n\n        public:\n\n            CompilerContext (Type type);\n\n            /// Is the compiler allowed to declare local variables?\n            bool canDeclareLocals() const override;\n\n            /// 'l: long, 's': short, 'f': float, ' ': does not exist.\n            char getGlobalType (const std::string& name) const override;\n\n            std::pair<char, bool> getMemberType (const std::string& name,\n                const std::string& id) const override;\n            ///< Return type of member variable \\a name in script \\a id or in script of reference of\n            /// \\a id\n            /// \\return first: 'l: long, 's': short, 'f': float, ' ': does not exist.\n            /// second: true: script of reference\n\n            bool isId (const std::string& name) const override;\n            ///< Does \\a name match an ID, that can be referenced?\n\n            bool isJournalId (const std::string& name) const override;\n            ///< Does \\a name match a journal ID?\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/consoleextensions.cpp",
    "content": "#include \"consoleextensions.hpp\"\n\n#include <components/interpreter/interpreter.hpp>\n\nnamespace MWScript\n{\n    namespace Console\n    {\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/consoleextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_CONSOLEEXTENSIONS_H\n#define GAME_SCRIPT_CONSOLEEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief Script functionality limited to the console\n    namespace Console\n    {\n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/containerextensions.cpp",
    "content": "#include \"containerextensions.hpp\"\n\n#include <stdexcept>\n\n#include <MyGUI_LanguageManager.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/ScriptController.hpp\"\n#include <components/interpreter/context.hpp>\n/*\n    End of tes3mp addition\n*/\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/esm/loadskil.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwclass/container.hpp\"\n\n#include \"../mwworld/action.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/manualref.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/levelledlist.hpp\"\n\n#include \"ref.hpp\"\n\nnamespace\n{\n    void addToStore(const MWWorld::Ptr& itemPtr, int count, MWWorld::Ptr& ptr, MWWorld::ContainerStore& store, bool resolve = true)\n    {\n        if (itemPtr.getClass().getScript(itemPtr).empty())\n        {\n            store.add (itemPtr, count, ptr, true, resolve);\n        }\n        else\n        {\n            // Adding just one item per time to make sure there isn't a stack of scripted items\n            for (int i = 0; i < count; i++)\n                store.add (itemPtr, 1, ptr, true, resolve);\n        }\n    }\n\n    void addRandomToStore(const MWWorld::Ptr& itemPtr, int count, MWWorld::Ptr& owner, MWWorld::ContainerStore& store, bool topLevel = true)\n    {\n        if(itemPtr.getTypeName() == typeid(ESM::ItemLevList).name())\n        {\n            const ESM::ItemLevList* levItemList = itemPtr.get<ESM::ItemLevList>()->mBase;\n\n            if(topLevel && count > 1 && levItemList->mFlags & ESM::ItemLevList::Each)\n            {\n                for(int i = 0; i < count; i++)\n                    addRandomToStore(itemPtr, 1, owner, store, true);\n            }\n            else\n            {\n                std::string itemId = MWMechanics::getLevelledItem(itemPtr.get<ESM::ItemLevList>()->mBase, false);\n                if (itemId.empty())\n                    return;\n                MWWorld::ManualRef manualRef(MWBase::Environment::get().getWorld()->getStore(), itemId, 1);\n                addRandomToStore(manualRef.getPtr(), count, owner, store, false);\n            }\n        }\n        else\n            addToStore(itemPtr, count, owner, store);\n    }\n}\n\nnamespace MWScript\n{\n    namespace Container\n    {\n        template<class R>\n        class OpAddItem : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string item = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer count = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (count<0)\n                        count = static_cast<uint16_t>(count);\n\n                    // no-op\n                    if (count == 0)\n                        return;\n\n                    if(::Misc::StringUtils::ciEqual(item, \"gold_005\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_010\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_025\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_100\"))\n                        item = \"gold_001\";\n\n                    // Check if \"item\" can be placed in a container\n                    MWWorld::ManualRef manualRef(MWBase::Environment::get().getWorld()->getStore(), item, 1);\n                    MWWorld::Ptr itemPtr = manualRef.getPtr();\n                    bool isLevelledList = itemPtr.getClass().getTypeName() == typeid(ESM::ItemLevList).name();\n                    if(!isLevelledList)\n                        MWWorld::ContainerStore::getType(itemPtr);\n\n                    // Explicit calls to non-unique actors affect the base record\n                    if(!R::implicit && ptr.getClass().isActor() && MWBase::Environment::get().getWorld()->getStore().getRefCount(ptr.getCellRef().getRefId()) > 1)\n                    {\n                        ptr.getClass().modifyBaseInventory(ptr.getCellRef().getRefId(), item, count);\n                        return;\n                    }\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Allow unilateral item removal on this client from client scripts and dialogue (but not console commands)\n                        to prevent infinite loops in certain mods. Otherwise, expect the server's reply to our packet to do the\n                        removal instead, except for changes to player inventories which still require the PlayerInventory to be\n                        reworked.\n                    */\n                    unsigned char packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n\n                    if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr() || packetOrigin != mwmp::CLIENT_CONSOLE)\n                    {\n                        // Calls to unresolved containers affect the base record\n                        if (ptr.getClass().getTypeName() == typeid(ESM::Container).name() && (!ptr.getRefData().getCustomData() ||\n                            !ptr.getClass().getContainerStore(ptr).isResolved()))\n                        {\n                            ptr.getClass().modifyBaseInventory(ptr.getCellRef().getRefId(), item, count);\n                            const ESM::Container* baseRecord = MWBase::Environment::get().getWorld()->getStore().get<ESM::Container>().find(ptr.getCellRef().getRefId());\n                            const auto& ptrs = MWBase::Environment::get().getWorld()->getAll(ptr.getCellRef().getRefId());\n                            for (const auto& container : ptrs)\n                            {\n                                // use the new base record\n                                container.get<ESM::Container>()->mBase = baseRecord;\n                                if (container.getRefData().getCustomData())\n                                {\n                                    auto& store = container.getClass().getContainerStore(container);\n                                    if (isLevelledList)\n                                    {\n                                        if (store.isResolved())\n                                        {\n                                            addRandomToStore(itemPtr, count, ptr, store);\n                                        }\n                                    }\n                                    else\n                                        addToStore(itemPtr, count, ptr, store, store.isResolved());\n                                }\n                            }\n                            return;\n                        }\n                        MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);\n                        if (isLevelledList)\n                            addRandomToStore(itemPtr, count, ptr, store);\n                        else\n                            addToStore(itemPtr, count, ptr, store);\n                    }\n                    /*\n                        End of tes3mp change (major)\n                    */\n\n                    // Spawn a messagebox (only for items added to player's inventory and if player is talking to someone)\n                    if (ptr == MWBase::Environment::get().getWorld ()->getPlayerPtr() )\n                    {\n                        // The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory\n                        std::string msgBox;\n                        std::string itemName = itemPtr.getClass().getName(itemPtr);\n                        if (count == 1)\n                        {\n                            msgBox = MyGUI::LanguageManager::getInstance().replaceTags(\"#{sNotifyMessage60}\");\n                            msgBox = ::Misc::StringUtils::format(msgBox, itemName);\n                        }\n                        else\n                        {\n                            msgBox = MyGUI::LanguageManager::getInstance().replaceTags(\"#{sNotifyMessage61}\");\n                            msgBox = ::Misc::StringUtils::format(msgBox, count, itemName);\n                        }\n                        MWBase::Environment::get().getWindowManager()->messageBox(msgBox, MWGui::ShowInDialogueMode_Only);\n                    }\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_CONTAINER packet every time an item is added to a Ptr\n                        that doesn't belong to a DedicatedPlayer\n                    */\n                    else if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() &&\n                        (!ptr.getClass().isActor() || !mwmp::PlayerList::isDedicatedPlayer(ptr)))\n                    {\n                        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                        objectList->reset();\n                        objectList->packetOrigin = packetOrigin;\n                        objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                        objectList->cell = *ptr.getCell()->getCell();\n                        objectList->action = mwmp::BaseObjectList::ADD;\n                        objectList->containerSubAction = mwmp::BaseObjectList::NONE;\n                        mwmp::BaseObject baseObject = objectList->getBaseObjectFromPtr(ptr);\n                        objectList->addContainerItem(baseObject, item, count, 0);\n                        objectList->addBaseObject(baseObject);\n                        objectList->sendContainer();\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template<class R>\n        class OpGetItemCount : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string item = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if(::Misc::StringUtils::ciEqual(item, \"gold_005\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_010\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_025\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_100\"))\n                        item = \"gold_001\";\n\n                    MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr);\n\n                    runtime.push (store.count(item));\n                }\n        };\n\n        template<class R>\n        class OpRemoveItem : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string item = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer count = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (count<0)\n                        throw std::runtime_error (\"second argument for RemoveItem must be non-negative\");\n\n                    // no-op\n                    if (count == 0)\n                        return;\n\n                    if(::Misc::StringUtils::ciEqual(item, \"gold_005\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_010\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_025\")\n                            || ::Misc::StringUtils::ciEqual(item, \"gold_100\"))\n                        item = \"gold_001\";\n\n                    // Explicit calls to non-unique actors affect the base record\n                    if(!R::implicit && ptr.getClass().isActor() && MWBase::Environment::get().getWorld()->getStore().getRefCount(ptr.getCellRef().getRefId()) > 1)\n                    {\n                        ptr.getClass().modifyBaseInventory(ptr.getCellRef().getRefId(), item, -count);\n                        return;\n                    }\n                    // Calls to unresolved containers affect the base record instead\n                    else if(ptr.getClass().getTypeName() == typeid(ESM::Container).name() &&\n                        (!ptr.getRefData().getCustomData() || !ptr.getClass().getContainerStore(ptr).isResolved()))\n                    {\n                        ptr.getClass().modifyBaseInventory(ptr.getCellRef().getRefId(), item, -count);\n                        const ESM::Container* baseRecord = MWBase::Environment::get().getWorld()->getStore().get<ESM::Container>().find(ptr.getCellRef().getRefId());\n                        const auto& ptrs = MWBase::Environment::get().getWorld()->getAll(ptr.getCellRef().getRefId());\n                        for(const auto& container : ptrs)\n                        {\n                            container.get<ESM::Container>()->mBase = baseRecord;\n                            if(container.getRefData().getCustomData())\n                            {\n                                auto& store = container.getClass().getContainerStore(container);\n                                // Note that unlike AddItem, RemoveItem only removes from unresolved containers\n                                if(!store.isResolved())\n                                    store.remove(item, count, ptr, false, false);\n                            }\n                        }\n                        return;\n                    }\n                    MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr);\n\n                    std::string itemName;\n                    for (MWWorld::ConstContainerStoreIterator iter(store.cbegin()); iter != store.cend(); ++iter)\n                    {\n                        if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item))\n                        {\n                            itemName = iter->getClass().getName(*iter);\n                            break;\n                        }\n                    }\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Allow unilateral item removal on this client from client scripts and dialogue (but not console commands)\n                        to prevent infinite loops in certain mods. Otherwise, expect the server's reply to our packet to do the\n                        removal instead, except for changes to player inventories which still require the PlayerInventory to be\n                        reworked.\n                    */\n                    unsigned char packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                    int numRemoved = 0;\n                    \n                    if (ptr == MWMechanics::getPlayer() || packetOrigin != mwmp::CLIENT_CONSOLE)\n                        numRemoved = store.remove(item, count, ptr);\n\n                    // Spawn a messagebox (only for items removed from player's inventory)\n                    if ((numRemoved > 0)\n                        && (ptr == MWMechanics::getPlayer()))\n                    {\n                    /*\n                        End of tes3mp change (major)\n                    */\n\n                        // The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory\n                        std::string msgBox;\n\n                        if (numRemoved > 1)\n                        {\n                            msgBox = MyGUI::LanguageManager::getInstance().replaceTags(\"#{sNotifyMessage63}\");\n                            msgBox = ::Misc::StringUtils::format(msgBox, numRemoved, itemName);\n                        }\n                        else\n                        {\n                            msgBox = MyGUI::LanguageManager::getInstance().replaceTags(\"#{sNotifyMessage62}\");\n                            msgBox = ::Misc::StringUtils::format(msgBox, itemName);\n                        }\n                        MWBase::Environment::get().getWindowManager()->messageBox(msgBox, MWGui::ShowInDialogueMode_Only);\n                    }\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_CONTAINER packet every time an item is removed from a Ptr\n                        that doesn't belong to a DedicatedPlayer\n                    */\n                    else if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() &&\n                        (!ptr.getClass().isActor() || !mwmp::PlayerList::isDedicatedPlayer(ptr)))\n                    {\n                        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                        objectList->reset();\n                        objectList->packetOrigin = packetOrigin;\n                        objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                        objectList->cell = *ptr.getCell()->getCell();\n                        objectList->action = mwmp::BaseObjectList::REMOVE;\n                        objectList->containerSubAction = mwmp::BaseObjectList::NONE;\n\n                        mwmp::BaseObject baseObject = objectList->getBaseObjectFromPtr(ptr);\n                        objectList->addContainerItem(baseObject, item, 0, count);\n                        objectList->addBaseObject(baseObject);\n                        objectList->sendContainer();\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template <class R>\n        class OpEquip : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute(Interpreter::Runtime &runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string item = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr);\n                    MWWorld::ContainerStoreIterator it = invStore.begin();\n                    for (; it != invStore.end(); ++it)\n                    {\n                        if (::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item))\n                            break;\n                    }\n                    if (it == invStore.end())\n                    {\n                        it = ptr.getClass().getContainerStore (ptr).add (item, 1, ptr);\n                        Log(Debug::Warning) << \"Implicitly adding one \" << item << \n                            \" to the inventory store of \" << ptr.getCellRef().getRefId() <<\n                            \" to fulfill the requirements of Equip instruction\";\n                    }\n\n                    if (ptr == MWMechanics::getPlayer())\n                        MWBase::Environment::get().getWindowManager()->useItem(*it, true);\n                    else\n                    {\n                        std::shared_ptr<MWWorld::Action> action = it->getClass().use(*it, true);\n                        action->execute(ptr, true);\n                    }\n                }\n        };\n\n        template <class R>\n        class OpGetArmorType : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute(Interpreter::Runtime &runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer location = runtime[0].mInteger;\n                    runtime.pop();\n\n                    int slot;\n                    switch (location)\n                    {\n                        case 0:\n                            slot = MWWorld::InventoryStore::Slot_Helmet;\n                            break;\n                        case 1:\n                            slot = MWWorld::InventoryStore::Slot_Cuirass;\n                            break;\n                        case 2:\n                            slot = MWWorld::InventoryStore::Slot_LeftPauldron;\n                            break;\n                        case 3:\n                            slot = MWWorld::InventoryStore::Slot_RightPauldron;\n                            break;\n                        case 4:\n                            slot = MWWorld::InventoryStore::Slot_Greaves;\n                            break;\n                        case 5:\n                            slot = MWWorld::InventoryStore::Slot_Boots;\n                            break;\n                        case 6:\n                            slot = MWWorld::InventoryStore::Slot_LeftGauntlet;\n                            break;\n                        case 7:\n                            slot = MWWorld::InventoryStore::Slot_RightGauntlet;\n                            break;\n                        case 8:\n                            slot = MWWorld::InventoryStore::Slot_CarriedLeft; // shield\n                            break;\n                        case 9:\n                            slot = MWWorld::InventoryStore::Slot_LeftGauntlet;\n                            break;\n                        case 10:\n                            slot = MWWorld::InventoryStore::Slot_RightGauntlet;\n                            break;\n                        default:\n                            throw std::runtime_error (\"armor index out of range\");\n                    }\n\n                    const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr);\n                    MWWorld::ConstContainerStoreIterator it = invStore.getSlot (slot);\n                    \n                    if (it == invStore.end() || it->getTypeName () != typeid(ESM::Armor).name())\n                    {\n                        runtime.push(-1);\n                        return;\n                    }\n\n                    int skill = it->getClass().getEquipmentSkill (*it) ;\n                    if (skill == ESM::Skill::HeavyArmor)\n                        runtime.push(2);\n                    else if (skill == ESM::Skill::MediumArmor)\n                        runtime.push(1);\n                    else if (skill == ESM::Skill::LightArmor)\n                        runtime.push(0);\n                    else\n                        runtime.push(-1);\n            }\n        };\n\n        template <class R>\n        class OpHasItemEquipped : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute(Interpreter::Runtime &runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string item = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr);\n                    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)\n                    {\n                        MWWorld::ConstContainerStoreIterator it = invStore.getSlot (slot);\n                        if (it != invStore.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item))\n                        {\n                            runtime.push(1);\n                            return;\n                        }\n                    }\n                    runtime.push(0);\n                }\n        };\n\n        template <class R>\n        class OpHasSoulGem : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute(Interpreter::Runtime &runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    const std::string &name = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    int count = 0;\n                    const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr);\n                    for (MWWorld::ConstContainerStoreIterator it = invStore.cbegin(MWWorld::ContainerStore::Type_Miscellaneous);\n                         it != invStore.cend(); ++it)\n                    {\n                        if (::Misc::StringUtils::ciEqual(it->getCellRef().getSoul(), name))\n                            count += it->getRefData().getCount();\n                    }\n                    runtime.push(count);\n                }\n        };\n\n        template <class R>\n        class OpGetWeaponType : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute(Interpreter::Runtime &runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr);\n                    MWWorld::ConstContainerStoreIterator it = invStore.getSlot (MWWorld::InventoryStore::Slot_CarriedRight);\n                    if (it == invStore.end())\n                    {\n                        runtime.push(-1);\n                        return;\n                    }\n                    else if (it->getTypeName() != typeid(ESM::Weapon).name())\n                    {\n                        if (it->getTypeName() == typeid(ESM::Lockpick).name())\n                        {\n                            runtime.push(-2);\n                        }\n                        else if (it->getTypeName() == typeid(ESM::Probe).name())\n                        {\n                            runtime.push(-3);\n                        }\n                        else\n                        {\n                            runtime.push(-1);\n                        }\n                        return;\n                    }\n\n                    runtime.push(it->get<ESM::Weapon>()->mBase->mData.mType);\n                }\n        };\n\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n             interpreter.installSegment5 (Compiler::Container::opcodeAddItem, new OpAddItem<ImplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeAddItemExplicit, new OpAddItem<ExplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeGetItemCount, new OpGetItemCount<ImplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeGetItemCountExplicit, new OpGetItemCount<ExplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeRemoveItem, new OpRemoveItem<ImplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeRemoveItemExplicit, new OpRemoveItem<ExplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeEquip, new OpEquip<ImplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeEquipExplicit, new OpEquip<ExplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeGetArmorType, new OpGetArmorType<ImplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeGetArmorTypeExplicit, new OpGetArmorType<ExplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeHasItemEquipped, new OpHasItemEquipped<ImplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeHasItemEquippedExplicit, new OpHasItemEquipped<ExplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeHasSoulGem, new OpHasSoulGem<ImplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeHasSoulGemExplicit, new OpHasSoulGem<ExplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeGetWeaponType, new OpGetWeaponType<ImplicitRef>);\n             interpreter.installSegment5 (Compiler::Container::opcodeGetWeaponTypeExplicit, new OpGetWeaponType<ExplicitRef>);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/containerextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_CONTAINEREXTENSIONS_H\n#define GAME_SCRIPT_CONTAINEREXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief Container-related script functionality (chests, NPCs, creatures)\n    namespace Container\n    {\n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/controlextensions.cpp",
    "content": "#include \"controlextensions.hpp\"\n\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/ptr.hpp\"\n\n#include \"interpretercontext.hpp\"\n#include \"ref.hpp\"\n\nnamespace MWScript\n{\n    namespace Control\n    {\n        class OpSetControl : public Interpreter::Opcode0\n        {\n                std::string mControl;\n                bool mEnable;\n\n            public:\n\n                OpSetControl (const std::string& control, bool enable)\n                : mControl (control), mEnable (enable)\n                {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::Environment::get()\n                        .getInputManager()\n                        ->toggleControlSwitch(mControl, mEnable);\n                }\n        };\n\n        class OpGetDisabled : public Interpreter::Opcode0\n        {\n                std::string mControl;\n\n            public:\n\n                OpGetDisabled (const std::string& control)\n                : mControl (control)\n                {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push(!MWBase::Environment::get().getInputManager()->getControlSwitch (mControl));\n                }\n        };\n\n        class OpToggleCollision : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled = MWBase::Environment::get().getWorld()->toggleCollisionMode();\n\n                    /*\n                        Start of tes3mp addition\n\n                        Update the LocalPlayer's tclState so it gets sent to the server\n                    */\n                    mwmp::Main::get().getLocalPlayer()->hasTcl = !enabled;\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    runtime.getContext().report (enabled ? \"Collision -> On\" : \"Collision -> Off\");\n                }\n        };\n\n        template<class R>\n        class OpClearMovementFlag : public Interpreter::Opcode0\n        {\n                MWMechanics::CreatureStats::Flag mFlag;\n\n            public:\n\n                OpClearMovementFlag (MWMechanics::CreatureStats::Flag flag) : mFlag (flag) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    ptr.getClass().getCreatureStats(ptr).setMovementFlag (mFlag, false);\n                }\n        };\n\n        template<class R>\n        class OpSetMovementFlag : public Interpreter::Opcode0\n        {\n                MWMechanics::CreatureStats::Flag mFlag;\n\n            public:\n\n                OpSetMovementFlag (MWMechanics::CreatureStats::Flag flag) : mFlag (flag) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    ptr.getClass().getCreatureStats(ptr).setMovementFlag (mFlag, true);\n                }\n        };\n\n        template <class R>\n        class OpGetForceRun : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n                    runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun));\n                }\n        };\n\n        template <class R>\n        class OpGetForceJump : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n                    runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump));\n                }\n        };\n\n        template <class R>\n        class OpGetForceMoveJump : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n                    runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump));\n                }\n        };\n\n        template <class R>\n        class OpGetForceSneak : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n                    runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak));\n                }\n        };\n\n        class OpGetPcRunning : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();\n                    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n                    MWBase::World* world = MWBase::Environment::get().getWorld();\n\n                    bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Run);\n                    bool running = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr);\n                    bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr);\n\n                    runtime.push(stanceOn && (running || inair));\n                }\n        };\n\n        class OpGetPcSneaking : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();\n                    runtime.push(MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr));\n                }\n        };\n\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            for (int i=0; i<Compiler::Control::numberOfControls; ++i)\n            {\n                interpreter.installSegment5 (Compiler::Control::opcodeEnable+i, new OpSetControl (Compiler::Control::controls[i], true));\n                interpreter.installSegment5 (Compiler::Control::opcodeDisable+i, new OpSetControl (Compiler::Control::controls[i], false));\n                interpreter.installSegment5 (Compiler::Control::opcodeGetDisabled+i, new OpGetDisabled (Compiler::Control::controls[i]));\n            }\n\n            interpreter.installSegment5 (Compiler::Control::opcodeToggleCollision, new OpToggleCollision);\n\n            //Force Run\n            interpreter.installSegment5 (Compiler::Control::opcodeClearForceRun,\n                new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));\n            interpreter.installSegment5 (Compiler::Control::opcodeClearForceRunExplicit,\n                new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));\n            interpreter.installSegment5 (Compiler::Control::opcodeForceRun,\n                new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));\n            interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit,\n                new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));\n\n            //Force Jump\n            interpreter.installSegment5 (Compiler::Control::opcodeClearForceJump,\n                new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceJump));\n            interpreter.installSegment5 (Compiler::Control::opcodeClearForceJumpExplicit,\n                new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceJump));\n            interpreter.installSegment5 (Compiler::Control::opcodeForceJump,\n                new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceJump));\n            interpreter.installSegment5 (Compiler::Control::opcodeForceJumpExplicit,\n                new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceJump));\n\n            //Force MoveJump\n            interpreter.installSegment5 (Compiler::Control::opcodeClearForceMoveJump,\n                new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceMoveJump));\n            interpreter.installSegment5 (Compiler::Control::opcodeClearForceMoveJumpExplicit,\n                new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceMoveJump));\n            interpreter.installSegment5 (Compiler::Control::opcodeForceMoveJump,\n                new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceMoveJump));\n            interpreter.installSegment5 (Compiler::Control::opcodeForceMoveJumpExplicit,\n                new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceMoveJump));\n\n            //Force Sneak\n            interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak,\n                new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));\n            interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneakExplicit,\n                new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));\n            interpreter.installSegment5 (Compiler::Control::opcodeForceSneak,\n                new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));\n            interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit,\n                new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));\n\n            interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetForceRunExplicit, new OpGetForceRun<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetForceJump, new OpGetForceJump<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetForceJumpExplicit, new OpGetForceJump<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetForceMoveJump, new OpGetForceMoveJump<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetForceMoveJumpExplicit, new OpGetForceMoveJump<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneak, new OpGetForceSneak<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneakExplicit, new OpGetForceSneak<ExplicitRef>);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/controlextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_CONTROLEXTENSIONS_H\n#define GAME_SCRIPT_CONTROLEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief player controls-related script functionality\n    namespace Control\n    {\n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/dialogueextensions.cpp",
    "content": "#include \"dialogueextensions.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/compiler/extensions.hpp>\n#include <components/compiler/opcodes.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n#include \"../mwbase/journal.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n\n#include \"interpretercontext.hpp\"\n#include \"ref.hpp\"\n\nnamespace MWScript\n{\n    namespace Dialogue\n    {\n        template <class R>\n        class OpJournal : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime, false); // required=false\n                    if (ptr.isEmpty())\n                        ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();\n\n                    std::string quest = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer index = runtime[0].mInteger;\n                    runtime.pop();\n\n                    // Invoking Journal with a non-existing index is allowed, and triggers no errors. Seriously? :(\n                    try\n                    {\n                        /*\n                            Start of tes3mp addition\n\n                            Send an ID_PLAYER_JOURNAL packet every time a new journal entry is added\n                            through a script\n                        */\n                        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && !MWBase::Environment::get().getJournal()->hasEntry(quest, index))\n                            mwmp::Main::get().getLocalPlayer()->sendJournalEntry(quest, index, ptr);\n                        /*\n                            End of tes3mp addition\n                        */\n\n                        MWBase::Environment::get().getJournal()->addEntry (quest, index, ptr);\n                    }\n                    catch (...)\n                    {\n                        if (MWBase::Environment::get().getJournal()->getJournalIndex(quest) < index)\n                            MWBase::Environment::get().getJournal()->setJournalIndex(quest, index);\n                    }\n                }\n        };\n\n        class OpSetJournalIndex : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string quest = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer index = runtime[0].mInteger;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getJournal()->setJournalIndex (quest, index);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_PLAYER_JOURNAL packet every time a journal index is set\n                        through a script\n                    */\n                    if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n                        mwmp::Main::get().getLocalPlayer()->sendJournalIndex(quest, index);\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        class OpGetJournalIndex : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string quest = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    int index = MWBase::Environment::get().getJournal()->getJournalIndex (quest);\n\n                    runtime.push (index);\n\n                }\n        };\n\n        class OpAddTopic : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string topic = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_PLAYER_TOPIC packet every time a new topic is added\n                        through a script\n                    */\n                    if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() &&\n                        MWBase::Environment::get().getDialogueManager()->isNewTopic(Misc::StringUtils::lowerCase(topic)))\n                        mwmp::Main::get().getLocalPlayer()->sendTopic(Misc::StringUtils::lowerCase(topic));\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    MWBase::Environment::get().getDialogueManager()->addTopic(topic);\n                }\n        };\n\n        class OpChoice : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWBase::DialogueManager* dialogue = MWBase::Environment::get().getDialogueManager();\n                    while(arg0>0)\n                    {\n                        std::string question = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                        arg0 = arg0 -1;\n                        Interpreter::Type_Integer choice = 1;\n                        if(arg0>0)\n                        {\n                            choice = runtime[0].mInteger;\n                            runtime.pop();\n                            arg0 = arg0 -1;\n                        }\n                        dialogue->addChoice(question,choice);\n                    }\n                }\n        };\n\n        template<class R>\n        class OpForceGreeting : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (!ptr.getRefData().isEnabled())\n                        return;\n\n                    if (!ptr.getClass().isActor())\n                    {\n                        const std::string error = \"Warning: \\\"forcegreeting\\\" command works only for actors.\";\n                        runtime.getContext().report(error);\n                        Log(Debug::Warning) << error;\n                        return;\n                    }\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Don't start a dialogue if the target is already engaged in one, thus\n                        preventing infinite greeting loops\n                    */\n                    if (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue))\n                        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, ptr);\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n        };\n\n        class OpGoodbye : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute(Interpreter::Runtime& runtime) override\n                {\n                    MWBase::Environment::get().getDialogueManager()->goodbye();\n                }\n        };\n\n        template<class R>\n        class OpModReputation : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    ptr.getClass().getNpcStats (ptr).setReputation (ptr.getClass().getNpcStats (ptr).getReputation () + value);\n                }\n        };\n\n        template<class R>\n        class OpSetReputation : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    ptr.getClass().getNpcStats (ptr).setReputation (value);\n                }\n        };\n\n        template<class R>\n        class OpGetReputation : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push (ptr.getClass().getNpcStats (ptr).getReputation ());\n                }\n        };\n\n        template<class R>\n        class OpSameFaction : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n\n                    runtime.push(player.getClass().getNpcStats (player).isInFaction(ptr.getClass().getPrimaryFaction(ptr)));\n                }\n        };\n\n        class OpModFactionReaction : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                int modReaction = runtime[0].mInteger;\n                runtime.pop();\n\n                MWBase::Environment::get().getDialogueManager()->modFactionReaction(faction1, faction2, modReaction);\n            }\n        };\n\n        class OpGetFactionReaction : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                runtime.push(MWBase::Environment::get().getDialogueManager()\n                             ->getFactionReaction(faction1, faction2));\n            }\n        };\n\n        class OpSetFactionReaction : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                int newValue = runtime[0].mInteger;\n                runtime.pop();\n\n                MWBase::Environment::get().getDialogueManager()->setFactionReaction(faction1, faction2, newValue);\n            }\n        };\n\n        template <class R>\n        class OpClearInfoActor : public Interpreter::Opcode0\n        {\n        public:\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWWorld::Ptr ptr = R()(runtime);\n\n                MWBase::Environment::get().getDialogueManager()->clearInfoActor(ptr);\n            }\n        };\n\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeJournal, new OpJournal<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeJournalExplicit, new OpJournal<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeSetJournalIndex, new OpSetJournalIndex);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeGetJournalIndex, new OpGetJournalIndex);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeAddTopic, new OpAddTopic);\n            interpreter.installSegment3 (Compiler::Dialogue::opcodeChoice,new OpChoice);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeForceGreeting, new OpForceGreeting<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeForceGreetingExplicit, new OpForceGreeting<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeGoodbye, new OpGoodbye);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeGetReputation, new OpGetReputation<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeSetReputation, new OpSetReputation<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeModReputation, new OpModReputation<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeSetReputationExplicit, new OpSetReputation<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeModReputationExplicit, new OpModReputation<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeGetReputationExplicit, new OpGetReputation<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeSetFactionReaction, new OpSetFactionReaction);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor<ExplicitRef>);\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/dialogueextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_DIALOGUEEXTENSIONS_H\n#define GAME_SCRIPT_DIALOGUEEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief Dialogue/Journal-related script functionality\n    namespace Dialogue\n    {\n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/docs/vmformat.txt",
    "content": "OpenMW Extensions:\n\nSegment 0:\n(not implemented yet)\nopcodes 0x20-0x3f unused\n\nSegment 1:\n(not implemented yet)\nopcodes 0x20-0x3f unused\n\nSegment 2:\n(not implemented yet)\nopcodes 0x200-0x3ff unused\n\nSegment 3:\nop 0x20000: AiTravel\nop 0x20001: AiTravel, explicit reference\nop 0x20002: AiEscort\nop 0x20003: AiEscort, explicit reference\nop 0x20004: Lock\nop 0x20005: Lock, explicit reference\nop 0x20006: PlayAnim\nop 0x20007: PlayAnim, explicit reference\nop 0x20008: LoopAnim\nop 0x20009: LoopAnim, explicit reference\nop 0x2000a: Choice\nop 0x2000b: PCRaiseRank\nop 0x2000c: PCLowerRank\nop 0x2000d: PCJoinFaction\nop 0x2000e: PCGetRank implicit\nop 0x2000f: PCGetRank explicit\nop 0x20010: AiWander\nop 0x20011: AiWander, explicit reference\nop 0x20012: GetPCFacRep\nop 0x20013: GetPCFacRep, explicit reference\nop 0x20014: SetPCFacRep\nop 0x20015: SetPCFacRep, explicit reference\nop 0x20016: ModPCFacRep\nop 0x20017: ModPCFacRep, explicit reference\nop 0x20018: PcExpelled\nop 0x20019: PcExpelled, explicit\nop 0x2001a: PcExpell\nop 0x2001b: PcExpell, explicit\nop 0x2001c: PcClearExpelled\nop 0x2001d: PcClearExpelled, explicit\nop 0x2001e: AIActivate\nop 0x2001f: AIActivate, explicit reference\nop 0x20020: AiEscortCell\nop 0x20021: AiEscortCell, explicit reference\nop 0x20022: AiFollow\nop 0x20023: AiFollow, explicit reference\nop 0x20024: AiFollowCell\nop 0x20025: AiFollowCell, explicit reference\nop 0x20026: ModRegion\nop 0x20027: RemoveSoulGem\nop 0x20028: RemoveSoulGem, explicit reference\nop 0x20029: PCRaiseRank, explicit reference\nop 0x2002a: PCLowerRank, explicit reference\nop 0x2002b: PCJoinFaction, explicit reference\nop 0x2002c: MenuTest\nop 0x2002d: BetaComment\nop 0x2002e: BetaComment, explicit reference\nop 0x2002f: ShowSceneGraph\nop 0x20030: ShowSceneGraph, explicit\nopcodes 0x20031-0x3ffff unused\n\nSegment 4:\n(not implemented yet)\nopcodes 0x200-0x3ff unused\n\nSegment 5:\nop 0x2000000: CellChanged\nop 0x2000001: Say\nop 0x2000002: SayDone\nop 0x2000003: StreamMusic\nop 0x2000004: PlaySound\nop 0x2000005: PlaySoundVP\nop 0x2000006: PlaySound3D\nop 0x2000007: PlaySound3DVP\nop 0x2000008: PlayLoopSound3D\nop 0x2000009: PlayLoopSound3DVP\nop 0x200000a: StopSound\nop 0x200000b: GetSoundPlaying\nop 0x200000c: XBox (always 0)\nop 0x200000d: OnActivate\nop 0x200000e: EnableBirthMenu\nop 0x200000f: EnableClassMenu\nop 0x2000010: EnableNameMenu\nop 0x2000011: EnableRaceMenu\nop 0x2000012: EnableStatsReviewMenu\nop 0x2000013: EnableInventoryMenu\nop 0x2000014: EnableMagicMenu\nop 0x2000015: EnableMapMenu\nop 0x2000016: EnableStatsMenu\nop 0x2000017: EnableRest\nop 0x2000018: ShowRestMenu\nop 0x2000019: Say, explicit reference\nop 0x200001a: SayDone, explicit reference\nop 0x200001b: PlaySound3D, explicit reference\nop 0x200001c: PlaySound3DVP, explicit reference\nop 0x200001d: PlayLoopSound3D, explicit reference\nop 0x200001e: PlayLoopSound3DVP, explicit reference\nop 0x200001f: StopSound, explicit reference\nop 0x2000020: GetSoundPlaying, explicit reference\nop 0x2000021: ToggleSky\nop 0x2000022: TurnMoonWhite\nop 0x2000023: TurnMoonRed\nop 0x2000024: GetMasserPhase\nop 0x2000025: GetSecundaPhase\nop 0x2000026: COC\nop 0x2000027-0x200002e: GetAttribute\nop 0x200002f-0x2000036: GetAttribute, explicit reference\nop 0x2000037-0x200003e: SetAttribute\nop 0x200003f-0x2000046: SetAttribute, explicit reference\nop 0x2000047-0x200004e: ModAttribute\nop 0x200004f-0x2000056: ModAttribute, explicit reference\nop 0x2000057-0x2000059: GetDynamic (health, magicka, fatigue)\nop 0x200005a-0x200005c: GetDynamic (health, magicka, fatigue), explicit reference\nop 0x200005d-0x200005f: SetDynamic (health, magicka, fatigue)\nop 0x2000060-0x2000062: SetDynamic (health, magicka, fatigue), explicit reference\nop 0x2000063-0x2000065: ModDynamic (health, magicka, fatigue)\nop 0x2000066-0x2000068: ModDynamic (health, magicka, fatigue), explicit reference\nop 0x2000069-0x200006b: ModDynamic (health, magicka, fatigue)\nop 0x200006c-0x200006e: ModDynamic (health, magicka, fatigue), explicit reference\nop 0x200006f-0x2000071: GetDynamic (health, magicka, fatigue)\nop 0x2000072-0x2000074: GetDynamic (health, magicka, fatigue), explicit reference\nop 0x2000075: Activate\nop 0x2000076: AddItem\nop 0x2000077: AddItem, explicit reference\nop 0x2000078: GetItemCount\nop 0x2000079: GetItemCount, explicit reference\nop 0x200007a: RemoveItem\nop 0x200007b: RemoveItem, explicit reference\nop 0x200007c: GetAiPackageDone\nop 0x200007d: GetAiPackageDone, explicit reference\nop 0x200007e-0x2000084: Enable Controls\nop 0x2000085-0x200008b: Disable Controls\nop 0x200008c: Unlock\nop 0x200008d: Unlock, explicit reference\nop 0x200008e-0x20000a8: GetSkill\nop 0x20000a9-0x20000c3: GetSkill, explicit reference\nop 0x20000c4-0x20000de: SetSkill\nop 0x20000df-0x20000f9: SetSkill, explicit reference\nop 0x20000fa-0x2000114: ModSkill\nop 0x2000115-0x200012f: ModSKill, explicit reference\nop 0x2000130: ToggleCollision\nop 0x2000131: GetInterior\nop 0x2000132: ToggleCollsionDebug\nop 0x2000133: Journal\nop 0x2000134: SetJournalIndex\nop 0x2000135: GetJournalIndex\nop 0x2000136: GetPCCell\nop 0x2000137: GetButtonPressed\nop 0x2000138: SkipAnim\nop 0x2000139: SkipAnim, expplicit reference\nop 0x200013a: AddTopic\nop 0x200013b: twf\nop 0x200013c: FadeIn\nop 0x200013d: FadeOut\nop 0x200013e: FadeTo\nop 0x200013f: GetCurrentWeather\nop 0x2000140: ChangeWeather\nop 0x2000141: GetWaterLevel\nop 0x2000142: SetWaterLevel\nop 0x2000143: ModWaterLevel\nop 0x2000144: ToggleWater, twa\nop 0x2000145: ToggleFogOfWar (tfow)\nop 0x2000146: TogglePathgrid\nop 0x2000147: AddSpell\nop 0x2000148: AddSpell, explicit reference\nop 0x2000149: RemoveSpell\nop 0x200014a: RemoveSpell, explicit reference\nop 0x200014b: GetSpell\nop 0x200014c: GetSpell, explicit reference\nop 0x200014d: ModDisposition\nop 0x200014e: ModDisposition, explicit reference\nop 0x200014f: ForceGreeting\nop 0x2000150: ForceGreeting, explicit reference\nop 0x2000151: ToggleFullHelp\nop 0x2000152: Goodbye\nop 0x2000153: DontSaveObject (left unimplemented)\nop 0x2000154: ClearForceRun\nop 0x2000155: ClearForceRun, explicit reference\nop 0x2000156: ForceRun\nop 0x2000157: ForceRun, explicit reference\nop 0x2000158: ClearForceSneak\nop 0x2000159: ClearForceSneak, explicit reference\nop 0x200015a: ForceSneak\nop 0x200015b: ForceSneak, explicit reference\nop 0x200015c: SetHello\nop 0x200015d: SetHello, explicit reference\nop 0x200015e: SetFight\nop 0x200015f: SetFight, explicit reference\nop 0x2000160: SetFlee\nop 0x2000161: SetFlee, explicit reference\nop 0x2000162: SetAlarm\nop 0x2000163: SetAlarm, explicit reference\nop 0x2000164: SetScale\nop 0x2000165: SetScale, explicit reference\nop 0x2000166: SetAngle\nop 0x2000167: SetAngle, explicit reference\nop 0x2000168: GetScale\nop 0x2000169: GetScale, explicit reference\nop 0x200016a: GetAngle\nop 0x200016b: GetAngle, explicit reference\nop 0x200016c: user1 (console only, requires --script-console switch)\nop 0x200016d: user2 (console only, requires --script-console switch)\nop 0x200016e: user3, explicit reference (console only, requires --script-console switch)\nop 0x200016f: user3 (implicit reference, console only, requires --script-console switch)\nop 0x2000170: user4, explicit reference (console only, requires --script-console switch)\nop 0x2000171: user4 (implicit reference, console only, requires --script-console switch)\nop 0x2000172: GetStartingAngle\nop 0x2000173: GetStartingAngle, explicit reference\nop 0x2000174: ToggleVanityMode\nop 0x2000175-0x200018B: Get controls disabled\nop 0x200018C: GetLevel\nop 0x200018D: GetLevel, explicit reference\nop 0x200018E: SetLevel\nop 0x200018F: SetLevel, explicit reference\nop 0x2000190: GetPos\nop 0x2000191: GetPosExplicit\nop 0x2000192: SetPos\nop 0x2000193: SetPosExplicit\nop 0x2000194: GetStartingPos\nop 0x2000195: GetStartingPosExplicit\nop 0x2000196: Position\nop 0x2000197: Position Explicit\nop 0x2000198: PositionCell\nop 0x2000199: PositionCell Explicit\nop 0x200019a: PlaceItemCell\nop 0x200019b: PlaceItem\nop 0x200019c: PlaceAtPc\nop 0x200019d: PlaceAtMe\nop 0x200019e: PlaceAtMe Explicit\nop 0x200019f: GetPcSleep\nop 0x20001a0: ShowMap\nop 0x20001a1: FillMap\nop 0x20001a2: WakeUpPc\nop 0x20001a3: GetDeadCount\nop 0x20001a4: SetDisposition\nop 0x20001a5: SetDisposition, Explicit\nop 0x20001a6: GetDisposition\nop 0x20001a7: GetDisposition, Explicit\nop 0x20001a8: CommonDisease\nop 0x20001a9: CommonDisease, explicit reference\nop 0x20001aa: BlightDisease\nop 0x20001ab: BlightDisease, explicit reference\nop 0x20001ac: ToggleCollisionBoxes\nop 0x20001ad: SetReputation\nop 0x20001ae: ModReputation\nop 0x20001af: SetReputation, explicit\nop 0x20001b0: ModReputation, explicit\nop 0x20001b1: GetReputation\nop 0x20001b2: GetReputation, explicit\nop 0x20001b3: Equip\nop 0x20001b4: Equip, explicit\nop 0x20001b5: SameFaction\nop 0x20001b6: SameFaction, explicit\nop 0x20001b7: ModHello\nop 0x20001b8: ModHello, explicit reference\nop 0x20001b9: ModFight\nop 0x20001ba: ModFight, explicit reference\nop 0x20001bb: ModFlee\nop 0x20001bc: ModFlee, explicit reference\nop 0x20001bd: ModAlarm\nop 0x20001be: ModAlarm, explicit reference\nop 0x20001bf: GetHello\nop 0x20001c0: GetHello, explicit reference\nop 0x20001c1: GetFight\nop 0x20001c2: GetFight, explicit reference\nop 0x20001c3: GetFlee\nop 0x20001c4: GetFlee, explicit reference\nop 0x20001c5: GetAlarm\nop 0x20001c6: GetAlarm, explicit reference\nop 0x20001c7: GetLocked\nop 0x20001c8: GetLocked, explicit reference\nop 0x20001c9: GetPcRunning\nop 0x20001ca: GetPcSneaking\nop 0x20001cb: GetForceRun\nop 0x20001cc: GetForceSneak\nop 0x20001cd: GetForceRun, explicit\nop 0x20001ce: GetForceSneak, explicit\nop 0x20001cf: GetEffect\nop 0x20001d0: GetEffect, explicit\nop 0x20001d1: GetArmorType\nop 0x20001d2: GetArmorType, explicit\nop 0x20001d3: GetAttacked\nop 0x20001d4: GetAttacked, explicit\nop 0x20001d5: HasItemEquipped\nop 0x20001d6: HasItemEquipped, explicit\nop 0x20001d7: GetWeaponDrawn\nop 0x20001d8: GetWeaponDrawn, explicit\nop 0x20001d9: GetRace\nop 0x20001da: GetRace, explicit\nop 0x20001db: GetSpellEffects\nop 0x20001dc: GetSpellEffects, explicit\nop 0x20001dd: GetCurrentTime\nop 0x20001de: HasSoulGem\nop 0x20001df: HasSoulGem, explicit\nop 0x20001e0: GetWeaponType\nop 0x20001e1: GetWeaponType, explicit\nop 0x20001e2: GetWerewolfKills\nop 0x20001e3: ModScale\nop 0x20001e4: ModScale, explicit\nop 0x20001e5: SetDelete\nop 0x20001e6: SetDelete, explicit\nop 0x20001e7: GetSquareRoot\nop 0x20001e8: RaiseRank\nop 0x20001e9: RaiseRank, explicit\nop 0x20001ea: LowerRank\nop 0x20001eb: LowerRank, explicit\nop 0x20001ec: GetPCCrimeLevel\nop 0x20001ed: SetPCCrimeLevel\nop 0x20001ee: ModPCCrimeLevel\nop 0x20001ef: GetCurrentAIPackage\nop 0x20001f0: GetCurrentAIPackage, explicit reference\nop 0x20001f1: GetDetected\nop 0x20001f2: GetDetected, explicit reference\nop 0x20001f3: AddSoulGem\nop 0x20001f4: AddSoulGem, explicit reference\nop 0x20001f5: unused\nop 0x20001f6: unused\nop 0x20001f7: PlayBink\nop 0x20001f8: Drop\nop 0x20001f9: Drop, explicit reference\nop 0x20001fa: DropSoulGem\nop 0x20001fb: DropSoulGem, explicit reference\nop 0x20001fc: OnDeath\nop 0x20001fd: IsWerewolf\nop 0x20001fe: IsWerewolf, explicit reference\nop 0x20001ff: Rotate\nop 0x2000200: Rotate, explicit reference\nop 0x2000201: RotateWorld\nop 0x2000202: RotateWorld, explicit reference\nop 0x2000203: SetAtStart\nop 0x2000204: SetAtStart, explicit\nop 0x2000205: OnDeath, explicit\nop 0x2000206: Move\nop 0x2000207: Move, explicit\nop 0x2000208: MoveWorld\nop 0x2000209: MoveWorld, explicit\nop 0x200020a: Fall\nop 0x200020b: Fall, explicit\nop 0x200020c: GetStandingPC\nop 0x200020d: GetStandingPC, explicit\nop 0x200020e: GetStandingActor\nop 0x200020f: GetStandingActor, explicit\nop 0x2000210: GetStartingAngle\nop 0x2000211: GetStartingAngle, explicit\nop 0x2000212: GetWindSpeed\nop 0x2000213: HitOnMe\nop 0x2000214: HitOnMe, explicit\nop 0x2000215: DisableTeleporting\nop 0x2000216: EnableTeleporting\nop 0x2000217: BecomeWerewolf\nop 0x2000218: BecomeWerewolfExplicit\nop 0x2000219: UndoWerewolf\nop 0x200021a: UndoWerewolfExplicit\nop 0x200021b: SetWerewolfAcrobatics\nop 0x200021c: SetWerewolfAcrobaticsExplicit\nop 0x200021d: ShowVars\nop 0x200021e: ShowVarsExplicit\nop 0x200021f: ToggleGodMode\nop 0x2000220: DisableLevitation\nop 0x2000221: EnableLevitation\nop 0x2000222: GetLineOfSight\nop 0x2000223: GetLineOfSightExplicit\nop 0x2000224: ToggleAI\nop 0x2000225: unused\nop 0x2000226: COE\nop 0x2000227: Cast\nop 0x2000228: Cast, explicit\nop 0x2000229: ExplodeSpell\nop 0x200022a: ExplodeSpell, explicit\nop 0x200022b: RemoveSpellEffects\nop 0x200022c: RemoveSpellEffects, explicit\nop 0x200022d: RemoveEffects\nop 0x200022e: RemoveEffects, explicit\nop 0x200022f: Resurrect\nop 0x2000230: Resurrect, explicit\nop 0x2000231: GetSpellReadied\nop 0x2000232: GetSpellReadied, explicit\nop 0x2000233: GetPcJumping\nop 0x2000234: ShowRestMenu, explicit\nop 0x2000235: GoToJail\nop 0x2000236: PayFine\nop 0x2000237: PayFineThief\nop 0x2000238: GetTarget\nop 0x2000239: GetTargetExplicit\nop 0x200023a: StartCombat\nop 0x200023b: StartCombatExplicit\nop 0x200023c: StopCombat\nop 0x200023d: StopCombatExplicit\nop 0x200023e: GetPcInJail\nop 0x200023f: GetPcTraveling\nop 0x2000240: onKnockout\nop 0x2000241: onKnockoutExplicit\nop 0x2000242: ModFactionReaction\nop 0x2000243: GetFactionReaction\nop 0x2000244: Activate, explicit\nop 0x2000245: ClearInfoActor\nop 0x2000246: ClearInfoActor, explicit\nop 0x2000247: (unused)\nop 0x2000248: (unused)\nop 0x2000249: OnMurder\nop 0x200024a: OnMurder, explicit\nop 0x200024b: ToggleMenus\nop 0x200024c: Face\nop 0x200024d: Face, explicit\nop 0x200024e: GetStat (dummy function)\nop 0x200024f: GetStat (dummy function), explicit\nop 0x2000250: GetCollidingPC\nop 0x2000251: GetCollidingPC, explicit\nop 0x2000252: GetCollidingActor\nop 0x2000253: GetCollidingActor, explicit\nop 0x2000254: HurtStandingActor\nop 0x2000255: HurtStandingActor, explicit\nop 0x2000256: HurtCollidingActor\nop 0x2000257: HurtCollidingActor, explicit\nop 0x2000258: ClearForceJump\nop 0x2000259: ClearForceJump, explicit reference\nop 0x200025a: ForceJump\nop 0x200025b: ForceJump, explicit reference\nop 0x200025c: ClearForceMoveJump\nop 0x200025d: ClearForceMoveJump, explicit reference\nop 0x200025e: ForceMoveJump\nop 0x200025f: ForceMoveJump, explicit reference\nop 0x2000260: GetForceJump\nop 0x2000261: GetForceJump, explicit reference\nop 0x2000262: GetForceMoveJump\nop 0x2000263: GetForceMoveJump, explicit reference\nop 0x2000264-0x200027b: GetMagicEffect\nop 0x200027c-0x2000293: GetMagicEffect, explicit\nop 0x2000294-0x20002ab: SetMagicEffect\nop 0x20002ac-0x20002c3: SetMagicEffect, explicit\nop 0x20002c4-0x20002db: ModMagicEffect\nop 0x20002dc-0x20002f3: ModMagicEffect, explicit\nop 0x20002f4: ResetActors\nop 0x20002f5: ToggleWorld\nop 0x20002f6: PCForce1stPerson\nop 0x20002f7: PCForce3rdPerson\nop 0x20002f8: PCGet3rdPerson\nop 0x20002f9: HitAttemptOnMe\nop 0x20002fa: HitAttemptOnMe, explicit\nop 0x20002fb: AddToLevCreature\nop 0x20002fc: RemoveFromLevCreature\nop 0x20002fd: AddToLevItem\nop 0x20002fe: RemoveFromLevItem\nop 0x20002ff: SetFactionReaction\nop 0x2000300: EnableLevelupMenu\nop 0x2000301: ToggleScripts\nop 0x2000302: Fixme\nop 0x2000303: Fixme, explicit\nop 0x2000304: Show\nop 0x2000305: Show, explicit\nop 0x2000306: OnActivate, explicit\nop 0x2000307: ToggleBorders, tb\nop 0x2000308: ToggleNavMesh\nop 0x2000309: ToggleActorsPaths\nop 0x200030a: SetNavMeshNumber\nop 0x200030b: Journal, explicit\nop 0x200030c: RepairedOnMe\nop 0x200030d: RepairedOnMe, explicit\nop 0x200030e: TestCells\nop 0x200030f: TestInteriorCells\nop 0x2000310: ToggleRecastMesh\nop 0x2000311: MenuMode\nop 0x2000312: Random\nop 0x2000313: ScriptRunning\nop 0x2000314: StartScript\nop 0x2000315: StopScript\nop 0x2000316: GetSecondsPassed\nop 0x2000317: Enable\nop 0x2000318: Disable\nop 0x2000319: GetDisabled\nop 0x200031a: Enable, explicit\nop 0x200031b: Disable, explicit\nop 0x200031c: GetDisabled, explicit\nop 0x200031d: StartScript, explicit\nop 0x200031e: GetDistance\nop 0x200031f: GetDistance, explicit\n\nopcodes 0x2000320-0x3ffffff unused\n"
  },
  {
    "path": "apps/openmw/mwscript/extensions.cpp",
    "content": "#include \"extensions.hpp\"\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/installopcodes.hpp>\n\n#include \"soundextensions.hpp\"\n#include \"cellextensions.hpp\"\n#include \"miscextensions.hpp\"\n#include \"guiextensions.hpp\"\n#include \"skyextensions.hpp\"\n#include \"statsextensions.hpp\"\n#include \"containerextensions.hpp\"\n#include \"aiextensions.hpp\"\n#include \"controlextensions.hpp\"\n#include \"dialogueextensions.hpp\"\n#include \"animationextensions.hpp\"\n#include \"transformationextensions.hpp\"\n#include \"consoleextensions.hpp\"\n#include \"userextensions.hpp\"\n\nnamespace MWScript\n{\n    void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly)\n    {\n        Interpreter::installOpcodes (interpreter);\n        Cell::installOpcodes (interpreter);\n        Misc::installOpcodes (interpreter);\n        Gui::installOpcodes (interpreter);\n        Sound::installOpcodes (interpreter);\n        Sky::installOpcodes (interpreter);\n        Stats::installOpcodes (interpreter);\n        Container::installOpcodes (interpreter);\n        Ai::installOpcodes (interpreter);\n        Control::installOpcodes (interpreter);\n        Dialogue::installOpcodes (interpreter);\n        Animation::installOpcodes (interpreter);\n        Transformation::installOpcodes (interpreter);\n\n        if (consoleOnly)\n        {\n            Console::installOpcodes (interpreter);\n            User::installOpcodes (interpreter);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/extensions.hpp",
    "content": "#ifndef GAME_SCRIPT_EXTENSIONS_H\n#define GAME_SCRIPT_EXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly = false);\n    ///< \\param consoleOnly include console only opcodes\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/globalscripts.cpp",
    "content": "#include \"globalscripts.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/globalscript.hpp>\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n\n#include \"interpretercontext.hpp\"\n\nnamespace\n{\n    struct ScriptCreatingVisitor : public boost::static_visitor<ESM::GlobalScript>\n    {\n        ESM::GlobalScript operator()(const MWWorld::Ptr &ptr) const\n        {\n            ESM::GlobalScript script;\n            script.mTargetRef.unset();\n            script.mRunning = false;\n            if (!ptr.isEmpty())\n            {\n                if (ptr.getCellRef().hasContentFile())\n                {\n                    script.mTargetId = ptr.getCellRef().getRefId();\n                    script.mTargetRef = ptr.getCellRef().getRefNum();\n                }\n                else if (MWBase::Environment::get().getWorld()->getPlayerPtr() == ptr)\n                    script.mTargetId = ptr.getCellRef().getRefId();\n            }\n            return script;\n        }\n\n        ESM::GlobalScript operator()(const std::pair<ESM::RefNum, std::string> &pair) const\n        {\n            ESM::GlobalScript script;\n            script.mTargetId = pair.second;\n            script.mTargetRef = pair.first;\n            script.mRunning = false;\n            return script;\n        }\n    };\n\n    struct PtrGettingVisitor : public boost::static_visitor<const MWWorld::Ptr*>\n    {\n        const MWWorld::Ptr* operator()(const MWWorld::Ptr &ptr) const\n        {\n            return &ptr;\n        }\n\n        const MWWorld::Ptr* operator()(const std::pair<ESM::RefNum, std::string> &pair) const\n        {\n            return nullptr;\n        }\n    };\n\n    struct PtrResolvingVisitor : public boost::static_visitor<MWWorld::Ptr>\n    {\n        MWWorld::Ptr operator()(const MWWorld::Ptr &ptr) const\n        {\n            return ptr;\n        }\n\n        MWWorld::Ptr operator()(const std::pair<ESM::RefNum, std::string> &pair) const\n        {\n            if (pair.second.empty())\n                return MWWorld::Ptr();\n            else if(pair.first.hasContentFile())\n                return MWBase::Environment::get().getWorld()->searchPtrViaRefNum(pair.second, pair.first);\n            return MWBase::Environment::get().getWorld()->searchPtr(pair.second, false);\n        }\n    };\n\n    class MatchPtrVisitor : public boost::static_visitor<bool>\n    {\n        const MWWorld::Ptr& mPtr;\n    public:\n        MatchPtrVisitor(const MWWorld::Ptr& ptr) : mPtr(ptr) {}\n\n        bool operator()(const MWWorld::Ptr &ptr) const\n        {\n            return ptr == mPtr;\n        }\n\n        bool operator()(const std::pair<ESM::RefNum, std::string> &pair) const\n        {\n            return false;\n        }\n    };\n}\n\nnamespace MWScript\n{\n    GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {}\n\n    const MWWorld::Ptr* GlobalScriptDesc::getPtrIfPresent() const\n    {\n        return boost::apply_visitor(PtrGettingVisitor(), mTarget);\n    }\n\n    MWWorld::Ptr GlobalScriptDesc::getPtr()\n    {\n        MWWorld::Ptr ptr = boost::apply_visitor(PtrResolvingVisitor(), mTarget);\n        mTarget = ptr;\n        return ptr;\n    }\n\n\n    GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store)\n    : mStore (store)\n    {}\n\n    void GlobalScripts::addScript (const std::string& name, const MWWorld::Ptr& target)\n    {\n        const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));\n\n        if (iter==mScripts.end())\n        {\n            if (const ESM::Script *script = mStore.get<ESM::Script>().search(name))\n            {\n                auto desc = std::make_shared<GlobalScriptDesc>();\n                MWWorld::Ptr ptr = target;\n                desc->mTarget = ptr;\n                desc->mRunning = true;\n                desc->mLocals.configure (*script);\n                mScripts.insert (std::make_pair(name, desc));\n            }\n            else\n            {\n                Log(Debug::Error) << \"Failed to add global script \" << name << \": script record not found\";\n            }\n        }\n        else if (!iter->second->mRunning)\n        {\n            iter->second->mRunning = true;\n            MWWorld::Ptr ptr = target;\n            iter->second->mTarget = ptr;\n        }\n    }\n\n    void GlobalScripts::removeScript (const std::string& name)\n    {\n        const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));\n\n        if (iter!=mScripts.end())\n            iter->second->mRunning = false;\n    }\n\n    bool GlobalScripts::isRunning (const std::string& name) const\n    {\n        const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));\n\n        if (iter==mScripts.end())\n            return false;\n\n        return iter->second->mRunning;\n    }\n\n    void GlobalScripts::run()\n    {\n        for (const auto& script : mScripts)\n        {\n            if (script.second->mRunning)\n            {\n                MWScript::InterpreterContext context(script.second);\n\n                /*\n                    Start of tes3mp addition\n\n                    Mark this InterpreterContext as having a SCRIPT_GLOBAL context\n                    and as currently running the script with this name, so that\n                    packets sent by the Interpreter can have their\n                    origin determined by serverside scripts\n                */\n                context.trackContextType(Interpreter::Context::SCRIPT_GLOBAL);\n                context.trackCurrentScriptName(script.first);\n                /*\n                    End of tes3mp addition\n                */\n\n                if (!MWBase::Environment::get().getScriptManager()->run(script.first, context))\n                    script.second->mRunning = false;\n            }\n        }\n    }\n\n    void GlobalScripts::clear()\n    {\n        mScripts.clear();\n    }\n\n    void GlobalScripts::addStartup()\n    {\n        // make list of global scripts to be added\n        std::vector<std::string> scripts;\n\n        scripts.emplace_back(\"main\");\n\n        for (MWWorld::Store<ESM::StartScript>::iterator iter =\n            mStore.get<ESM::StartScript>().begin();\n            iter != mStore.get<ESM::StartScript>().end(); ++iter)\n        {\n            scripts.push_back (iter->mId);\n        }\n\n        // add scripts\n        for (std::vector<std::string>::const_iterator iter (scripts.begin());\n            iter!=scripts.end(); ++iter)\n        {\n            try\n            {\n                addScript (*iter);\n            }\n            catch (const std::exception& exception)\n            {\n                Log(Debug::Error)\n                    << \"Failed to add start script \" << *iter << \" because an exception has \"\n                    << \"been thrown: \" << exception.what();\n            }\n        }\n    }\n\n    int GlobalScripts::countSavedGameRecords() const\n    {\n        return mScripts.size();\n    }\n\n    void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n    {\n        for (const auto& iter : mScripts)\n        {\n            ESM::GlobalScript script = boost::apply_visitor (ScriptCreatingVisitor(), iter.second->mTarget);\n\n            script.mId = iter.first;\n\n            iter.second->mLocals.write (script.mLocals, iter.first);\n\n            script.mRunning = iter.second->mRunning ? 1 : 0;\n\n            writer.startRecord (ESM::REC_GSCR);\n            script.save (writer);\n            writer.endRecord (ESM::REC_GSCR);\n        }\n    }\n\n    bool GlobalScripts::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)\n    {\n        if (type==ESM::REC_GSCR)\n        {\n            ESM::GlobalScript script;\n            script.load (reader);\n\n            if (script.mTargetRef.hasContentFile())\n            {\n                auto iter = contentFileMap.find(script.mTargetRef.mContentFile);\n                if (iter != contentFileMap.end())\n                    script.mTargetRef.mContentFile = iter->second;\n            }\n\n            auto iter = mScripts.find (script.mId);\n\n            if (iter==mScripts.end())\n            {\n                if (const ESM::Script *scriptRecord = mStore.get<ESM::Script>().search (script.mId))\n                {\n                    try\n                    {\n                        auto desc = std::make_shared<GlobalScriptDesc>();\n                        if (!script.mTargetId.empty())\n                        {\n                            desc->mTarget = std::make_pair(script.mTargetRef, script.mTargetId);\n                        }\n                        desc->mLocals.configure (*scriptRecord);\n\n                        iter = mScripts.insert (std::make_pair (script.mId, desc)).first;\n                    }\n                    catch (const std::exception& exception)\n                    {\n                        Log(Debug::Error)\n                            << \"Failed to add start script \" << script.mId\n                            << \" because an exception has been thrown: \" << exception.what();\n\n                        return true;\n                    }\n                }\n                else // script does not exist anymore\n                    return true;\n            }\n\n            iter->second->mRunning = script.mRunning!=0;\n            iter->second->mLocals.read (script.mLocals, script.mId);\n\n            return true;\n        }\n\n        return false;\n    }\n\n    Locals& GlobalScripts::getLocals (const std::string& name)\n    {\n        std::string name2 = ::Misc::StringUtils::lowerCase (name);\n        auto iter = mScripts.find (name2);\n\n        if (iter==mScripts.end())\n        {\n            const ESM::Script *script = mStore.get<ESM::Script>().find (name);\n\n            auto desc = std::make_shared<GlobalScriptDesc>();\n            desc->mLocals.configure (*script);\n\n            iter = mScripts.insert (std::make_pair (name2, desc)).first;\n        }\n\n        return iter->second->mLocals;\n    }\n\n    const Locals* GlobalScripts::getLocalsIfPresent (const std::string& name) const\n    {\n        std::string name2 = ::Misc::StringUtils::lowerCase (name);\n        auto iter = mScripts.find (name2);\n        if (iter==mScripts.end())\n            return nullptr;\n        return &iter->second->mLocals;\n    }\n\n    void GlobalScripts::updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)\n    {\n        MatchPtrVisitor visitor(base);\n        for (const auto& script : mScripts)\n        {\n            if (boost::apply_visitor (visitor, script.second->mTarget))\n                script.second->mTarget = updated;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/globalscripts.hpp",
    "content": "#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H\n#define GAME_SCRIPT_GLOBALSCRIPTS_H\n\n#include <boost/variant/variant.hpp>\n\n#include <string>\n#include <map>\n#include <memory>\n#include <utility>\n\n#include <stdint.h>\n\n#include \"locals.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace ESM\n{\n    class ESMWriter;\n    class ESMReader;\n    struct RefNum;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWWorld\n{\n    class ESMStore;\n}\n\nnamespace MWScript\n{\n    struct GlobalScriptDesc\n    {\n        bool mRunning;\n        Locals mLocals;\n        boost::variant<MWWorld::Ptr, std::pair<ESM::RefNum, std::string> > mTarget; // Used to start targeted script\n\n        GlobalScriptDesc();\n\n        const MWWorld::Ptr* getPtrIfPresent() const; // Returns a Ptr if one has been resolved\n\n        MWWorld::Ptr getPtr(); // Resolves mTarget to a Ptr and caches the (potentially empty) result\n    };\n\n    class GlobalScripts\n    {\n            const MWWorld::ESMStore& mStore;\n            std::map<std::string, std::shared_ptr<GlobalScriptDesc> > mScripts;\n\n        public:\n\n            GlobalScripts (const MWWorld::ESMStore& store);\n\n            void addScript (const std::string& name, const MWWorld::Ptr& target = MWWorld::Ptr());\n\n            void removeScript (const std::string& name);\n\n            bool isRunning (const std::string& name) const;\n\n            void run();\n            ///< run all active global scripts\n\n            void clear();\n\n            void addStartup();\n            ///< Add startup script\n\n            int countSavedGameRecords() const;\n\n            void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;\n\n            bool readRecord (ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap);\n            ///< Records for variables that do not exist are dropped silently.\n            ///\n            /// \\return Known type?\n\n            Locals& getLocals (const std::string& name);\n            ///< If the script \\a name has not been added as a global script yet, it is added\n            /// automatically, but is not set to running state.\n\n            const Locals* getLocalsIfPresent (const std::string& name) const;\n\n            void updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated);\n            ///< Update the Ptrs stored in mTarget. Should be called after the reference has been moved to a new cell.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/guiextensions.cpp",
    "content": "#include \"guiextensions.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"interpretercontext.hpp\"\n#include \"ref.hpp\"\n\nnamespace MWScript\n{\n    namespace Gui\n    {\n        class OpEnableWindow : public Interpreter::Opcode0\n        {\n                MWGui::GuiWindow mWindow;\n\n            public:\n\n                OpEnableWindow (MWGui::GuiWindow window) : mWindow (window) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::Environment::get().getWindowManager()->allow (mWindow);\n                }\n        };\n\n        class OpEnableRest : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::Environment::get().getWindowManager()->enableRest();\n                }\n        };\n\n        template <class R>\n        class OpShowRestMenu : public Interpreter::Opcode0\n        {\n        public:\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWWorld::Ptr bed = R()(runtime, false);\n\n                if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWMechanics::getPlayer(),\n                                                                             bed))\n                /*\n                    Start of tes3mp change (minor)\n\n                    Prevent resting if it has been disabled by the server for the local player\n                */\n                {\n                    if (!mwmp::Main::get().getLocalPlayer()->bedRestAllowed)\n                        MWBase::Environment::get().getWindowManager()->messageBox(\"You are not allowed to rest in beds.\");\n                    else\n                    {\n                        mwmp::Main::get().getLocalPlayer()->isUsingBed = true;\n                        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Rest, bed);\n                    }\n                }\n                /*\n                    End of tes3mp change (minor)\n                */\n            }\n        };\n\n        class OpShowDialogue : public Interpreter::Opcode0\n        {\n                MWGui::GuiMode mDialogue;\n\n            public:\n\n                OpShowDialogue (MWGui::GuiMode dialogue)\n                : mDialogue (dialogue)\n                {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::Environment::get().getWindowManager()->pushGuiMode(mDialogue);\n                }\n        };\n\n        class OpGetButtonPressed : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push (MWBase::Environment::get().getWindowManager()->readPressedButton());\n                }\n        };\n\n        class OpToggleFogOfWar : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFogOfWar() ? \"Fog of war -> On\"\n                                                                                                                : \"Fog of war -> Off\");\n                }\n        };\n\n        class OpToggleFullHelp : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFullHelp() ? \"Full help -> On\"\n                                                                                                                : \"Full help -> Off\");\n                }\n        };\n\n        class OpShowMap : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                std::string cell = (runtime.getStringLiteral (runtime[0].mInteger));\n                ::Misc::StringUtils::lowerCaseInPlace(cell);\n                runtime.pop();\n\n                // \"Will match complete or partial cells, so ShowMap, \"Vivec\" will show cells Vivec and Vivec, Fred's House as well.\"\n                // http://www.uesp.net/wiki/Tes3Mod:ShowMap\n\n                const MWWorld::Store<ESM::Cell> &cells =\n                    MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>();\n\n                MWWorld::Store<ESM::Cell>::iterator it = cells.extBegin();\n                for (; it != cells.extEnd(); ++it)\n                {\n                    std::string name = it->mName;\n                    ::Misc::StringUtils::lowerCaseInPlace(name);\n                    if (name.find(cell) != std::string::npos)\n                        MWBase::Environment::get().getWindowManager()->addVisitedLocation (\n                            it->mName,\n                            it->getGridX(),\n                            it->getGridY()\n                        );\n                }\n            }\n        };\n\n        class OpFillMap : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                const MWWorld::Store<ESM::Cell> &cells =\n                    MWBase::Environment::get().getWorld ()->getStore().get<ESM::Cell>();\n\n                MWWorld::Store<ESM::Cell>::iterator it = cells.extBegin();\n                for (; it != cells.extEnd(); ++it)\n                {\n                    std::string name = it->mName;\n                    if (name != \"\")\n                        MWBase::Environment::get().getWindowManager()->addVisitedLocation (\n                            name,\n                            it->getGridX(),\n                            it->getGridY()\n                        );\n                }\n            }\n        };\n\n        class OpMenuTest : public Interpreter::Opcode1\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n            {\n                int arg=0;\n                if(arg0>0)\n                {\n                    arg = runtime[0].mInteger;\n                    runtime.pop();\n                }\n\n\n                if (arg == 0)\n                {\n                    MWGui::GuiMode modes[] = { MWGui::GM_Inventory, MWGui::GM_Container };\n\n                    for (int i=0; i<2; ++i)\n                    {\n                        if (MWBase::Environment::get().getWindowManager()->containsMode(modes[i]))\n                            MWBase::Environment::get().getWindowManager()->removeGuiMode(modes[i]);\n                    }\n                }\n                else\n                {\n                    MWGui::GuiWindow gw = MWGui::GW_None;\n                    if (arg == 3)\n                        gw = MWGui::GW_Stats;\n                    if (arg == 4)\n                        gw = MWGui::GW_Inventory;\n                    if (arg == 5)\n                        gw = MWGui::GW_Magic;\n                    if (arg == 6)\n                        gw = MWGui::GW_Map;\n\n                    MWBase::Environment::get().getWindowManager()->pinWindow(gw);\n                }\n            }\n        };\n\n        class OpToggleMenus : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                bool state = MWBase::Environment::get().getWindowManager()->toggleHud();\n                runtime.getContext().report(state ? \"GUI -> On\" : \"GUI -> Off\");\n\n                if (!state)\n                {\n                    while (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_None) // don't use isGuiMode, or we get an infinite loop for modal message boxes!\n                        MWBase::Environment::get().getWindowManager()->popGuiMode();\n                }\n            }\n        };\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableBirthMenu,\n                new OpShowDialogue (MWGui::GM_Birth));\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableClassMenu,\n                new OpShowDialogue (MWGui::GM_Class));\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableNameMenu,\n                new OpShowDialogue (MWGui::GM_Name));\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableRaceMenu,\n                new OpShowDialogue (MWGui::GM_Race));\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableStatsReviewMenu,\n                new OpShowDialogue (MWGui::GM_Review));\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableLevelupMenu,\n                new OpShowDialogue (MWGui::GM_Levelup));\n\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableInventoryMenu,\n                new OpEnableWindow (MWGui::GW_Inventory));\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableMagicMenu,\n                new OpEnableWindow (MWGui::GW_Magic));\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableMapMenu,\n                new OpEnableWindow (MWGui::GW_Map));\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableStatsMenu,\n                new OpEnableWindow (MWGui::GW_Stats));\n\n            interpreter.installSegment5 (Compiler::Gui::opcodeEnableRest,\n                new OpEnableRest ());\n\n            interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenu,\n                new OpShowRestMenu<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenuExplicit, new OpShowRestMenu<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Gui::opcodeGetButtonPressed, new OpGetButtonPressed);\n\n            interpreter.installSegment5 (Compiler::Gui::opcodeToggleFogOfWar, new OpToggleFogOfWar);\n\n            interpreter.installSegment5 (Compiler::Gui::opcodeToggleFullHelp, new OpToggleFullHelp);\n\n            interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap);\n            interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap);\n            interpreter.installSegment3 (Compiler::Gui::opcodeMenuTest, new OpMenuTest);\n            interpreter.installSegment5 (Compiler::Gui::opcodeToggleMenus, new OpToggleMenus);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/guiextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_GUIEXTENSIONS_H\n#define GAME_SCRIPT_GUIEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief GUI-related script functionality\n    namespace Gui\n    {        \n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n\n"
  },
  {
    "path": "apps/openmw/mwscript/interpretercontext.cpp",
    "content": "#include \"interpretercontext.hpp\"\n\n#include <cmath>\n#include <sstream>\n\n#include <components/compiler/locals.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/ScriptController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n\n#include \"../mwworld/action.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n\n#include \"locals.hpp\"\n#include \"globalscripts.hpp\"\n\nnamespace MWScript\n{\n    /*\n        Start of tes3mp addition\n\n        Used for tracking and checking the type of this InterpreterContext, as well as\n        its current script\n    */\n    unsigned short InterpreterContext::getContextType() const\n    {\n        return mContextType;\n    }\n\n    std::string InterpreterContext::getCurrentScriptName() const\n    {\n        return mCurrentScriptName;\n    }\n\n    void InterpreterContext::trackContextType(unsigned short contextType)\n    {\n        mContextType = contextType;\n    }\n\n    void InterpreterContext::trackCurrentScriptName(const std::string& name)\n    {\n        mCurrentScriptName = name;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    const MWWorld::Ptr InterpreterContext::getReferenceImp (\n        const std::string& id, bool activeOnly, bool doThrow) const\n    {\n        if (!id.empty())\n        {\n            return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly);\n        }\n        else\n        {\n            if (mReference.isEmpty() && mGlobalScriptDesc)\n                mReference = mGlobalScriptDesc->getPtr();\n\n            if (mReference.isEmpty() && doThrow)\n                throw MissingImplicitRefError();\n\n            return mReference;\n        }\n    }\n\n    const Locals& InterpreterContext::getMemberLocals (std::string& id, bool global)\n        const\n    {\n        if (global)\n        {\n            return MWBase::Environment::get().getScriptManager()->getGlobalScripts().\n                getLocals (id);\n        }\n        else\n        {\n            const MWWorld::Ptr ptr = getReferenceImp (id, false);\n\n            id = ptr.getClass().getScript (ptr);\n\n            ptr.getRefData().setLocals (\n                *MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id));\n\n            return ptr.getRefData().getLocals();\n        }\n    }\n\n    Locals& InterpreterContext::getMemberLocals (std::string& id, bool global)\n    {\n        if (global)\n        {\n            return MWBase::Environment::get().getScriptManager()->getGlobalScripts().\n                getLocals (id);\n        }\n        else\n        {\n            const MWWorld::Ptr ptr = getReferenceImp (id, false);\n\n            id = ptr.getClass().getScript (ptr);\n\n            ptr.getRefData().setLocals (\n                *MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id));\n\n            return ptr.getRefData().getLocals();\n        }\n    }\n\n    MissingImplicitRefError::MissingImplicitRefError() : std::runtime_error(\"no implicit reference\") {}\n\n    int InterpreterContext::findLocalVariableIndex (const std::string& scriptId,\n        const std::string& name, char type) const\n    {\n        int index = MWBase::Environment::get().getScriptManager()->getLocals (scriptId).\n            searchIndex (type, name);\n\n        if (index!=-1)\n            return index;\n\n        std::ostringstream stream;\n\n        stream << \"Failed to access \";\n\n        switch (type)\n        {\n            case 's': stream << \"short\"; break;\n            case 'l': stream << \"long\"; break;\n            case 'f': stream << \"float\"; break;\n        }\n\n        stream << \" member variable \" << name << \" in script \" << scriptId;\n\n        throw std::runtime_error (stream.str().c_str());\n    }\n\n    InterpreterContext::InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference)\n    : mLocals (locals), mReference (reference)\n    {}\n\n    InterpreterContext::InterpreterContext (std::shared_ptr<GlobalScriptDesc> globalScriptDesc)\n    : mLocals (&(globalScriptDesc->mLocals))\n    {\n        const MWWorld::Ptr* ptr = globalScriptDesc->getPtrIfPresent();\n        // A nullptr here signifies that the script's target has not yet been resolved after loading the game.\n        // Script targets are lazily resolved to MWWorld::Ptrs (which can, upon resolution, be empty)\n        // because scripts started through dialogue often don't use their implicit target.\n        if (ptr)\n            mReference = *ptr;\n        else\n            mGlobalScriptDesc = globalScriptDesc;\n    }\n\n    int InterpreterContext::getLocalShort (int index) const\n    {\n        if (!mLocals)\n            throw std::runtime_error (\"local variables not available in this context\");\n\n        return mLocals->mShorts.at (index);\n    }\n\n    int InterpreterContext::getLocalLong (int index) const\n    {\n        if (!mLocals)\n            throw std::runtime_error (\"local variables not available in this context\");\n\n        return mLocals->mLongs.at (index);\n    }\n\n    float InterpreterContext::getLocalFloat (int index) const\n    {\n        if (!mLocals)\n            throw std::runtime_error (\"local variables not available in this context\");\n\n        return mLocals->mFloats.at (index);\n    }\n\n    void InterpreterContext::setLocalShort (int index, int value)\n    {\n        if (!mLocals)\n            throw std::runtime_error (\"local variables not available in this context\");\n\n        /*\n            Start of tes3mp addition\n\n            Avoid setting a local to a value it already is, preventing packet spam\n        */\n        if (mLocals->mShorts.at(index) == value) return;\n        /*\n            End of tes3mp addition\n        */\n\n        mLocals->mShorts.at (index) = value;\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CLIENT_SCRIPT_LOCAL packet when a local short changes its value if\n            it is being set in a script that has been approved for packet sending\n        */\n        if (sendPackets)\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType());\n            objectList->originClientScript = getCurrentScriptName();\n            objectList->addClientScriptLocal(mReference, index, value, mwmp::VARIABLE_TYPE::SHORT);\n            objectList->sendClientScriptLocal();\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void InterpreterContext::setLocalLong (int index, int value)\n    {\n        if (!mLocals)\n            throw std::runtime_error (\"local variables not available in this context\");\n\n        /*\n            Start of tes3mp addition\n\n            Avoid setting a local to a value it already is, preventing packet spam\n        */\n        if (mLocals->mLongs.at(index) == value) return;\n        /*\n            End of tes3mp addition\n        */\n\n        mLocals->mLongs.at (index) = value;\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CLIENT_SCRIPT_LOCAL packet when a local long changes its value if\n            it is being set in a script that has been approved for packet sending\n        */\n        if (sendPackets)\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType());\n            objectList->originClientScript = getCurrentScriptName();\n            objectList->addClientScriptLocal(mReference, index, value, mwmp::VARIABLE_TYPE::LONG);\n            objectList->sendClientScriptLocal();\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void InterpreterContext::setLocalFloat (int index, float value)\n    {\n        if (!mLocals)\n            throw std::runtime_error (\"local variables not available in this context\");\n\n        /*\n            Start of tes3mp addition\n\n            Avoid setting a local to a value it already is, preventing packet spam\n\n            Additionally, record the old value to check it below when determining if\n            it has changed enough to warrant sending a packet about it\n        */\n        float oldValue = mLocals->mFloats.at(index);\n\n        if (oldValue == value) return;\n        /*\n            End of tes3mp addition\n        */\n\n        mLocals->mFloats.at (index) = value;\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CLIENT_SCRIPT_LOCAL packet when a local float changes its value if\n            its value has changed enough and it is being set in a script that has been approved\n            for packet sending\n        */\n        if (floor(oldValue) != floor(value) && sendPackets)\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType());\n            objectList->originClientScript = getCurrentScriptName();\n            objectList->addClientScriptLocal(mReference, index, value);\n            objectList->sendClientScriptLocal();\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void InterpreterContext::messageBox (const std::string& message,\n        const std::vector<std::string>& buttons)\n    {\n        if (buttons.empty())\n            MWBase::Environment::get().getWindowManager()->messageBox (message);\n        else\n            MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons);\n    }\n\n    void InterpreterContext::report (const std::string& message)\n    {\n    }\n\n    int InterpreterContext::getGlobalShort (const std::string& name) const\n    {\n        return MWBase::Environment::get().getWorld()->getGlobalInt (name);\n    }\n\n    int InterpreterContext::getGlobalLong (const std::string& name) const\n    {\n        // a global long is internally a float.\n        return MWBase::Environment::get().getWorld()->getGlobalInt (name);\n    }\n\n    float InterpreterContext::getGlobalFloat (const std::string& name) const\n    {\n        return MWBase::Environment::get().getWorld()->getGlobalFloat (name);\n    }\n\n    void InterpreterContext::setGlobalShort (const std::string& name, int value)\n    {\n        /*\n            Start of tes3mp addition\n\n            Avoid setting a global to a value it already is, preventing packet spam\n        */\n        if (getGlobalShort(name) == value) return;\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CLIENT_SCRIPT_GLOBAL packet when a global short changes its value if\n            it is being set in a script that has been approved for packet sending or the global\n            itself has been set to always be synchronized\n        */\n        if (sendPackets || mwmp::Main::isValidPacketGlobal(name))\n        {\n            mwmp::Main::get().getNetworking()->getWorldstate()->sendClientGlobal(name, value, mwmp::VARIABLE_TYPE::SHORT);\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getWorld()->setGlobalInt (name, value);\n    }\n\n    void InterpreterContext::setGlobalLong (const std::string& name, int value)\n    {\n        /*\n            Start of tes3mp addition\n\n            Avoid setting a global to a value it already is, preventing packet spam\n        */\n        if (getGlobalLong(name) == value) return;\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CLIENT_SCRIPT_GLOBAL packet when a global long changes its value if\n            it is being set in a script that has been approved for packet sending or the global\n            itself has been set to always be synchronized\n        */\n        if (sendPackets || mwmp::Main::isValidPacketGlobal(name))\n        {\n            mwmp::Main::get().getNetworking()->getWorldstate()->sendClientGlobal(name, value, mwmp::VARIABLE_TYPE::LONG);\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getWorld()->setGlobalInt (name, value);\n    }\n\n    void InterpreterContext::setGlobalFloat (const std::string& name, float value)\n    {\n        /*\n            Start of tes3mp addition\n\n            Avoid setting a global to a value it already is, preventing packet spam\n\n            Additionally, record the old value to check it below when determining if\n            it has changed enough to warrant sending a packet about it\n        */\n        float oldValue = getGlobalFloat(name);\n\n        if (oldValue == value) return;\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CLIENT_SCRIPT_GLOBAL packet when a global float changes its value if\n            its value has changed enough and it is being set in a script that has been approved\n            for packet sending or the global itself has been set to always be synchronized\n        */\n        if (floor(oldValue) != floor(value) && (sendPackets || mwmp::Main::isValidPacketGlobal(name)))\n        {\n            mwmp::Main::get().getNetworking()->getWorldstate()->sendClientGlobal(name, value);\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getWorld()->setGlobalFloat (name, value);\n    }\n\n    std::vector<std::string> InterpreterContext::getGlobals() const\n    {\n        const MWWorld::Store<ESM::Global>& globals =\n            MWBase::Environment::get().getWorld()->getStore().get<ESM::Global>();\n\n        std::vector<std::string> ids;\n        for (auto& globalVariable : globals)\n        {\n            ids.emplace_back(globalVariable.mId);\n        }\n\n        return ids;\n    }\n\n    char InterpreterContext::getGlobalType (const std::string& name) const\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        return world->getGlobalVariableType(name);\n    }\n\n    std::string InterpreterContext::getActionBinding(const std::string& targetAction) const\n    {\n        MWBase::InputManager* input = MWBase::Environment::get().getInputManager();\n        std::vector<int> actions = input->getActionKeySorting ();\n        for (const int action : actions)\n        {\n            std::string desc = input->getActionDescription (action);\n            if(desc == \"\")\n                continue;\n\n            if(desc == targetAction)\n            {\n                if(input->joystickLastUsed())\n                    return input->getActionControllerBindingName(action);\n                else\n                    return input->getActionKeyBindingName(action);\n            }\n        }\n\n        return \"None\";\n    }\n\n    std::string InterpreterContext::getActorName() const\n    {\n        const MWWorld::Ptr& ptr = getReferenceImp();\n        if (ptr.getClass().isNpc())\n        {\n            const ESM::NPC* npc = ptr.get<ESM::NPC>()->mBase;\n            return npc->mName;\n        }\n\n        const ESM::Creature* creature = ptr.get<ESM::Creature>()->mBase;\n        return creature->mName;\n    }\n\n    std::string InterpreterContext::getNPCRace() const\n    {\n        ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;\n        const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc.mRace);\n        return race->mName;\n    }\n\n    std::string InterpreterContext::getNPCClass() const\n    {\n        ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;\n        const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc.mClass);\n        return class_->mName;\n    }\n\n    std::string InterpreterContext::getNPCFaction() const\n    {\n        ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;\n        const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npc.mFaction);\n        return faction->mName;\n    }\n\n    std::string InterpreterContext::getNPCRank() const\n    {\n        const MWWorld::Ptr& ptr = getReferenceImp();\n        std::string faction = ptr.getClass().getPrimaryFaction(ptr);\n        if (faction.empty())\n            throw std::runtime_error(\"getNPCRank(): NPC is not in a faction\");\n\n        int rank = ptr.getClass().getPrimaryFactionRank(ptr);\n        if (rank < 0 || rank > 9)\n            throw std::runtime_error(\"getNPCRank(): invalid rank\");\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        const MWWorld::ESMStore &store = world->getStore();\n        const ESM::Faction *fact = store.get<ESM::Faction>().find(faction);\n        return fact->mRanks[rank];\n    }\n\n    std::string InterpreterContext::getPCName() const\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        ESM::NPC player = *world->getPlayerPtr().get<ESM::NPC>()->mBase;\n        return player.mName;\n    }\n\n    std::string InterpreterContext::getPCRace() const\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        std::string race = world->getPlayerPtr().get<ESM::NPC>()->mBase->mRace;\n        return world->getStore().get<ESM::Race>().find(race)->mName;\n    }\n\n    std::string InterpreterContext::getPCClass() const\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        std::string class_ = world->getPlayerPtr().get<ESM::NPC>()->mBase->mClass;\n        return world->getStore().get<ESM::Class>().find(class_)->mName;\n    }\n\n    std::string InterpreterContext::getPCRank() const\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        MWWorld::Ptr player = world->getPlayerPtr();\n\n        std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());\n        if (factionId.empty())\n            throw std::runtime_error(\"getPCRank(): NPC is not in a faction\");\n\n        const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();\n        std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));\n        int rank = -1;\n        if (it != ranks.end())\n            rank = it->second;\n\n        // If you are not in the faction, PcRank returns the first rank, for whatever reason.\n        // This is used by the dialogue when joining the Thieves Guild in Balmora.\n        if (rank == -1)\n            rank = 0;\n\n        const MWWorld::ESMStore &store = world->getStore();\n        const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);\n\n        if(rank < 0 || rank > 9) // there are only 10 ranks\n            return \"\";\n\n        return faction->mRanks[rank];\n    }\n\n    std::string InterpreterContext::getPCNextRank() const\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        MWWorld::Ptr player = world->getPlayerPtr();\n\n        std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());\n        if (factionId.empty())\n            throw std::runtime_error(\"getPCNextRank(): NPC is not in a faction\");\n\n        const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();\n        std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));\n        int rank = -1;\n        if (it != ranks.end())\n            rank = it->second;\n\n        ++rank; // Next rank\n\n        // if we are already at max rank, there is no next rank\n        if (rank > 9)\n            rank = 9;\n\n        const MWWorld::ESMStore &store = world->getStore();\n        const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);\n\n        if(rank < 0)\n            return \"\";\n\n        return faction->mRanks[rank];\n    }\n\n    int InterpreterContext::getPCBounty() const\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        MWWorld::Ptr player = world->getPlayerPtr();\n        return player.getClass().getNpcStats (player).getBounty();\n    }\n\n    std::string InterpreterContext::getCurrentCellName() const\n    {\n        return  MWBase::Environment::get().getWorld()->getCellName();\n    }\n\n    void InterpreterContext::executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor)\n    {\n        std::shared_ptr<MWWorld::Action> action = (ptr.getClass().activate(ptr, actor));\n        action->execute (actor);\n        if (action->getTarget() != MWWorld::Ptr() && action->getTarget() != ptr)\n        {\n            updatePtr(ptr, action->getTarget());\n        }\n    }\n\n    int InterpreterContext::getMemberShort (const std::string& id, const std::string& name,\n        bool global) const\n    {\n        std::string scriptId (id);\n\n        const Locals& locals = getMemberLocals (scriptId, global);\n\n        return locals.mShorts[findLocalVariableIndex (scriptId, name, 's')];\n    }\n\n    int InterpreterContext::getMemberLong (const std::string& id, const std::string& name,\n        bool global) const\n    {\n        std::string scriptId (id);\n\n        const Locals& locals = getMemberLocals (scriptId, global);\n\n        return locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')];\n    }\n\n    float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name,\n        bool global) const\n    {\n        std::string scriptId (id);\n\n        const Locals& locals = getMemberLocals (scriptId, global);\n\n        return locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')];\n    }\n\n    void InterpreterContext::setMemberShort (const std::string& id, const std::string& name,\n        int value, bool global)\n    {\n        std::string scriptId (id);\n\n        Locals& locals = getMemberLocals (scriptId, global);\n\n        /*\n            Start of tes3mp change (minor)\n\n            Declare an integer so it can be reused below for multiplayer script sync purposes\n        */\n        int index = findLocalVariableIndex(scriptId, name, 's');\n\n        locals.mShorts[index] = value;\n        /*\n            End of tes3mp change (minor)\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_SCRIPT_MEMBER_SHORT packet every time a member short changes its value\n            in a script approved for packet sending\n        */\n        if (sendPackets && !global)\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType());\n            objectList->originClientScript = getCurrentScriptName();\n            objectList->addScriptMemberShort(id, index, value);\n            objectList->sendScriptMemberShort();\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global)\n    {\n        std::string scriptId (id);\n\n        Locals& locals = getMemberLocals (scriptId, global);\n\n        locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')] = value;\n    }\n\n    void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global)\n    {\n        std::string scriptId (id);\n\n        Locals& locals = getMemberLocals (scriptId, global);\n\n        locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')] = value;\n    }\n\n    MWWorld::Ptr InterpreterContext::getReference(bool required)\n    {\n        return getReferenceImp (\"\", true, required);\n    }\n\n    void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)\n    {\n        if (!mReference.isEmpty() && base == mReference)\n        {\n            mReference = updated;\n            if (mLocals == &base.getRefData().getLocals())\n                mLocals = &mReference.getRefData().getLocals();\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/interpretercontext.hpp",
    "content": "#ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H\n#define GAME_SCRIPT_INTERPRETERCONTEXT_H\n\n#include <memory>\n#include <stdexcept>\n\n#include <components/interpreter/context.hpp>\n\n#include \"globalscripts.hpp\"\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace MWScript\n{\n    class Locals;\n\n    class MissingImplicitRefError : public std::runtime_error\n    {\n        public:\n            MissingImplicitRefError();\n    };\n\n    class InterpreterContext : public Interpreter::Context\n    {\n            Locals *mLocals;\n            mutable MWWorld::Ptr mReference;\n            std::shared_ptr<GlobalScriptDesc> mGlobalScriptDesc;\n\n            /// If \\a id is empty, a reference the script is run from is returned or in case\n            /// of a non-local script the reference derived from the target ID.\n            const MWWorld::Ptr getReferenceImp (const std::string& id = \"\",\n                bool activeOnly = false, bool doThrow=true) const;\n\n            const Locals& getMemberLocals (std::string& id, bool global) const;\n            ///< \\a id is changed to the respective script ID, if \\a id wasn't a script ID before\n\n            Locals& getMemberLocals (std::string& id, bool global);\n            ///< \\a id is changed to the respective script ID, if \\a id wasn't a script ID before\n\n            /// Throws an exception if local variable can't be found.\n            int findLocalVariableIndex (const std::string& scriptId, const std::string& name,\n                char type) const;\n\n        public:\n            InterpreterContext (std::shared_ptr<GlobalScriptDesc> globalScriptDesc);\n\n            InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference);\n            ///< The ownership of \\a locals is not transferred. 0-pointer allowed.\n\n            /*\n                Start of tes3mp addition\n\n                Useful boolean for setting whether scripts send packets, set to false by default\n                to avoid massive packet spam\n            */\n            bool sendPackets = false;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Used for tracking and checking the type of this InterpreterContext, as well as\n                its current script\n            */\n            unsigned short mContextType;\n            std::string mCurrentScriptName = \"\";\n\n            virtual unsigned short getContextType() const;\n\n            virtual std::string getCurrentScriptName() const;\n\n            virtual void trackContextType(unsigned short contextType);\n\n            virtual void trackCurrentScriptName(const std::string& name);\n            /*\n                End of tes3mp addition\n            */\n\n            int getLocalShort (int index) const override;\n\n            int getLocalLong (int index) const override;\n\n            float getLocalFloat (int index) const override;\n\n            void setLocalShort (int index, int value) override;\n\n            void setLocalLong (int index, int value) override;\n\n            void setLocalFloat (int index, float value) override;\n\n            using Interpreter::Context::messageBox;\n\n            void messageBox (const std::string& message,\n                const std::vector<std::string>& buttons) override;\n\n            void report (const std::string& message) override;\n            ///< By default, do nothing.\n\n            int getGlobalShort (const std::string& name) const override;\n\n            int getGlobalLong (const std::string& name) const override;\n\n            float getGlobalFloat (const std::string& name) const override;\n\n            void setGlobalShort (const std::string& name, int value) override;\n\n            void setGlobalLong (const std::string& name, int value) override;\n\n            void setGlobalFloat (const std::string& name, float value) override;\n\n            std::vector<std::string> getGlobals () const override;\n\n            char getGlobalType (const std::string& name) const override;\n\n            std::string getActionBinding(const std::string& action) const override;\n\n            std::string getActorName() const override;\n\n            std::string getNPCRace() const override;\n\n            std::string getNPCClass() const override;\n\n            std::string getNPCFaction() const override;\n\n            std::string getNPCRank() const override;\n\n            std::string getPCName() const override;\n\n            std::string getPCRace() const override;\n\n            std::string getPCClass() const override;\n\n            std::string getPCRank() const override;\n\n            std::string getPCNextRank() const override;\n\n            int getPCBounty() const override;\n\n            std::string getCurrentCellName() const override;\n\n            void executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor);\n            ///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled.\n\n            int getMemberShort (const std::string& id, const std::string& name, bool global) const override;\n\n            int getMemberLong (const std::string& id, const std::string& name, bool global) const override;\n\n            float getMemberFloat (const std::string& id, const std::string& name, bool global) const override;\n\n            void setMemberShort (const std::string& id, const std::string& name, int value, bool global) override;\n\n            void setMemberLong (const std::string& id, const std::string& name, int value, bool global) override;\n\n            void setMemberFloat (const std::string& id, const std::string& name, float value, bool global) override;\n\n            MWWorld::Ptr getReference(bool required=true);\n            ///< Reference, that the script is running from (can be empty)\n\n            void updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated);\n            ///< Update the Ptr stored in mReference, if there is one stored there. Should be called after the reference has been moved to a new cell.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/locals.cpp",
    "content": "#include \"locals.hpp\"\n#include \"globalscripts.hpp\"\n\n#include <components/esm/loadscpt.hpp>\n#include <components/esm/variant.hpp>\n#include <components/esm/locals.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/compiler/locals.hpp>\n#include <components/compiler/exception.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace MWScript\n{\n    void Locals::ensure (const std::string& scriptName)\n    {\n        if (!mInitialised)\n        {\n            const ESM::Script *script = MWBase::Environment::get().getWorld()->getStore().\n                get<ESM::Script>().find (scriptName);\n\n            configure (*script);\n        }\n    }\n\n    Locals::Locals() : mInitialised (false) {}\n\n    bool Locals::configure (const ESM::Script& script)\n    {\n        if (mInitialised)\n            return false;\n\n        const Locals* global = MWBase::Environment::get().getScriptManager()->getGlobalScripts().getLocalsIfPresent(script.mId);\n        if(global)\n        {\n            mShorts = global->mShorts;\n            mLongs = global->mLongs;\n            mFloats = global->mFloats;\n        }\n        else\n        {\n            const Compiler::Locals& locals =\n                MWBase::Environment::get().getScriptManager()->getLocals (script.mId);\n\n            mShorts.clear();\n            mShorts.resize (locals.get ('s').size(), 0);\n            mLongs.clear();\n            mLongs.resize (locals.get ('l').size(), 0);\n            mFloats.clear();\n            mFloats.resize (locals.get ('f').size(), 0);\n        }\n\n        mInitialised = true;\n        return true;\n    }\n\n    bool Locals::isEmpty() const\n    {\n        return (mShorts.empty() && mLongs.empty() && mFloats.empty());\n    }\n\n    bool Locals::hasVar(const std::string &script, const std::string &var)\n    {\n        try\n        {\n            ensure (script);\n\n            const Compiler::Locals& locals =\n                MWBase::Environment::get().getScriptManager()->getLocals(script);\n            int index = locals.getIndex(var);\n            return (index != -1);\n        }\n        catch (const Compiler::SourceException&)\n        {\n            return false;\n        }\n    }\n\n    int Locals::getIntVar(const std::string &script, const std::string &var)\n    {\n        ensure (script);\n\n        const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script);\n        int index = locals.getIndex(var);\n        char type = locals.getType(var);\n        if(index != -1)\n        {\n            switch(type)\n            {\n                case 's':\n                    return mShorts.at (index);\n\n                case 'l':\n                    return mLongs.at (index);\n\n                case 'f':\n                    return static_cast<int>(mFloats.at(index));\n                default:\n                    return 0;\n            }\n        }\n        return 0;\n    }\n\n    float Locals::getFloatVar(const std::string &script, const std::string &var)\n    {\n        ensure (script);\n\n        const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script);\n        int index = locals.getIndex(var);\n        char type = locals.getType(var);\n        if(index != -1)\n        {\n            switch(type)\n            {\n                case 's':\n                    return mShorts.at (index);\n\n                case 'l':\n                    return mLongs.at (index);\n\n                case 'f':\n                    return mFloats.at(index);\n                default:\n                    return 0;\n            }\n        }\n        return 0;\n    }\n\n    bool Locals::setVarByInt(const std::string& script, const std::string& var, int val)\n    {\n        ensure (script);\n\n        const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script);\n        int index = locals.getIndex(var);\n        char type = locals.getType(var);\n        if(index != -1)\n        {\n            switch(type)\n            {\n                case 's':\n                    mShorts.at (index) = val; break;\n\n                case 'l':\n                    mLongs.at (index) = val; break;\n\n                case 'f':\n                    mFloats.at(index) = static_cast<float>(val); break;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    bool Locals::write (ESM::Locals& locals, const std::string& script) const\n    {\n        if (!mInitialised)\n            return false;\n\n        try\n        {\n            const Compiler::Locals& declarations =\n                MWBase::Environment::get().getScriptManager()->getLocals(script);\n\n            for (int i=0; i<3; ++i)\n            {\n                char type = 0;\n\n                switch (i)\n                {\n                    case 0: type = 's'; break;\n                    case 1: type = 'l'; break;\n                    case 2: type = 'f'; break;\n                }\n\n                const std::vector<std::string>& names = declarations.get (type);\n\n                for (int i2=0; i2<static_cast<int> (names.size()); ++i2)\n                {\n                    ESM::Variant value;\n\n                    switch (i)\n                    {\n                        case 0: value.setType (ESM::VT_Int); value.setInteger (mShorts.at (i2)); break;\n                        case 1: value.setType (ESM::VT_Int); value.setInteger (mLongs.at (i2)); break;\n                        case 2: value.setType (ESM::VT_Float); value.setFloat (mFloats.at (i2)); break;\n                    }\n\n                    locals.mVariables.emplace_back (names[i2], value);\n                }\n            }\n        }\n        catch (const Compiler::SourceException&)\n        {\n        }\n\n        return true;\n    }\n\n    void Locals::read (const ESM::Locals& locals, const std::string& script)\n    {\n        ensure (script);\n\n        try\n        {\n            const Compiler::Locals& declarations =\n                MWBase::Environment::get().getScriptManager()->getLocals(script);\n\n            int index = 0, numshorts = 0, numlongs = 0;\n            for (unsigned int v=0; v<locals.mVariables.size();++v)\n            {\n                ESM::VarType type = locals.mVariables[v].second.getType();\n                if (type == ESM::VT_Short)\n                    ++numshorts;\n                else if (type == ESM::VT_Int)\n                    ++numlongs;\n            }\n\n            for (std::vector<std::pair<std::string, ESM::Variant> >::const_iterator iter\n                = locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter,++index)\n            {\n                if (iter->first.empty())\n                {\n                    // no variable names available (this will happen for legacy, i.e. ESS-imported savegames only)\n                    try\n                    {\n                        if (index >= numshorts+numlongs)\n                            mFloats.at(index - (numshorts+numlongs)) = iter->second.getFloat();\n                        else if (index >= numshorts)\n                            mLongs.at(index - numshorts) = iter->second.getInteger();\n                        else\n                            mShorts.at(index) = iter->second.getInteger();\n                    }\n                    catch (std::exception& e)\n                    {\n                        Log(Debug::Error) << \"Failed to read local variable state for script '\"\n                                          << script << \"' (legacy format): \" << e.what()\n                                          << \"\\nNum shorts: \" << numshorts << \" / \" << mShorts.size()\n                                          << \" Num longs: \" << numlongs << \" / \" << mLongs.size();\n                    }\n                }\n                else\n                {\n                    char type =  declarations.getType (iter->first);\n                    int index2 = declarations.getIndex (iter->first);\n\n                    // silently ignore locals that don't exist anymore\n                    if (type == ' ' || index2 == -1)\n                        continue;\n\n                    try\n                    {\n                        switch (type)\n                        {\n                            case 's': mShorts.at (index2) = iter->second.getInteger(); break;\n                            case 'l': mLongs.at (index2) = iter->second.getInteger(); break;\n                            case 'f': mFloats.at (index2) = iter->second.getFloat(); break;\n                        }\n                    }\n                    catch (...)\n                    {\n                        // ignore type changes\n                        /// \\todo write to log\n                    }\n                }\n            }\n        }\n        catch (const Compiler::SourceException&)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/locals.hpp",
    "content": "#ifndef GAME_SCRIPT_LOCALS_H\n#define GAME_SCRIPT_LOCALS_H\n\n#include <vector>\n\n#include <components/interpreter/types.hpp>\n\nnamespace ESM\n{\n    class Script;\n    struct Locals;\n}\n\nnamespace MWScript\n{\n    class Locals\n    {\n            bool mInitialised;\n\n            void ensure (const std::string& scriptName);\n\n        public:\n            std::vector<Interpreter::Type_Short> mShorts;\n            std::vector<Interpreter::Type_Integer> mLongs;\n            std::vector<Interpreter::Type_Float> mFloats;\n\n            Locals();\n\n            /// Are there any locals?\n            ///\n            /// \\note Will return false, if locals have not been configured yet.\n            bool isEmpty() const;\n\n            /// \\return Did the state of *this change from uninitialised to initialised?\n            bool configure (const ESM::Script& script);\n\n            /// @note var needs to be in lowercase\n            ///\n            /// \\note Locals will be automatically configured first, if necessary\n            bool setVarByInt(const std::string& script, const std::string& var, int val);\n\n            /// \\note Locals will be automatically configured first, if necessary\n            //\n            // \\note If it can not be determined if the variable exists, the error will be\n            // ignored and false will be returned.\n            bool hasVar(const std::string& script, const std::string& var);\n\n            /// if var does not exist, returns 0\n            /// @note var needs to be in lowercase\n            ///\n            /// \\note Locals will be automatically configured first, if necessary\n            int getIntVar (const std::string& script, const std::string& var);\n\n            /// if var does not exist, returns 0\n            /// @note var needs to be in lowercase\n            ///\n            /// \\note Locals will be automatically configured first, if necessary\n            float getFloatVar (const std::string& script, const std::string& var);\n\n            /// \\note If locals have not been configured yet, no data is written.\n            ///\n            /// \\return Locals written?\n            bool write (ESM::Locals& locals, const std::string& script) const;\n\n            /// \\note Locals will be automatically configured first, if necessary\n            void read (const ESM::Locals& locals, const std::string& script);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/miscextensions.cpp",
    "content": "#include \"miscextensions.hpp\"\n\n#include <cstdlib>\n#include <iomanip>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/ScriptController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/compiler/opcodes.hpp>\n#include <components/compiler/locals.hpp>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include <components/misc/rng.hpp>\n#include <components/misc/resourcehelpers.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n\n#include <components/esm/loadmgef.hpp>\n#include <components/esm/loadcrea.hpp>\n\n#include <components/vfs/manager.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/containerstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/manualref.hpp\"\n\n#include \"../mwmechanics/aicast.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"interpretercontext.hpp\"\n#include \"ref.hpp\"\n\nnamespace\n{\n\n    void addToLevList(ESM::LevelledListBase* list, const std::string& itemId, int level)\n    {\n        for (auto& levelItem : list->mList)\n        {\n            if (levelItem.mLevel == level && itemId == levelItem.mId)\n                return;\n        }\n\n        ESM::LevelledListBase::LevelItem item;\n        item.mId = itemId;\n        item.mLevel = level;\n        list->mList.push_back(item);\n    }\n\n    void removeFromLevList(ESM::LevelledListBase* list, const std::string& itemId, int level)\n    {\n        // level of -1 removes all items with that itemId\n        for (std::vector<ESM::LevelledListBase::LevelItem>::iterator it = list->mList.begin(); it != list->mList.end();)\n        {\n            if (level != -1 && it->mLevel != level)\n            {\n                ++it;\n                continue;\n            }\n            if (Misc::StringUtils::ciEqual(itemId, it->mId))\n                it = list->mList.erase(it);\n            else\n                ++it;\n        }\n    }\n\n}\n\nnamespace MWScript\n{\n    namespace Misc\n    {\n        class OpMenuMode : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    /*\n                        Start of tes3mp change (major)\n\n                        Being in a menu should not pause scripts in multiplayer, so always return false\n                    */\n                    //runtime.push (MWBase::Environment::get().getWindowManager()->isGuiMode());\n                    runtime.push(false);\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n        };\n\n        class OpRandom : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    Interpreter::Type_Integer limit = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (limit<0)\n                        throw std::runtime_error (\n                            \"random: argument out of range (Don't be so negative!)\");\n\n                    runtime.push (static_cast<Interpreter::Type_Float>(::Misc::Rng::rollDice(limit))); // [o, limit)\n                }\n        };\n\n        template<class R>\n        class OpStartScript : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr target = R()(runtime, false);\n                    std::string name = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target);\n                }\n        };\n\n        class OpScriptRunning : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string name = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    runtime.push(MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name));\n                }\n        };\n\n        class OpStopScript : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string name = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    MWBase::Environment::get().getScriptManager()->getGlobalScripts().removeScript (name);\n                }\n        };\n\n        class OpGetSecondsPassed : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push (MWBase::Environment::get().getFrameDuration());\n                }\n        };\n\n        template<class R>\n        class OpEnable : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_STATE packet whenever an object should be enabled, as long as the\n                        player is logged in on the server and  if triggered from a clientside script  our\n                        last packet regarding its state did not already attempt to enable it (to prevent\n                        packet spam)\n                    */\n                    if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && ptr.isInCell())\n                    {\n                        unsigned char packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n\n                        if (packetOrigin == mwmp::CLIENT_CONSOLE || packetOrigin == mwmp::CLIENT_DIALOGUE ||\n                            ptr.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Enabled)\n                        {\n                            ptr.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Enabled);\n\n                            mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                            objectList->reset();\n                            objectList->packetOrigin = packetOrigin;\n                            objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                            objectList->addObjectState(ptr, true);\n                            objectList->sendObjectState();\n                        }\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Disable unilateral state enabling on this client and expect the server's reply to our\n                        packet to do it instead\n                    */\n                    //MWBase::Environment::get().getWorld()->enable (ptr);\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n        };\n\n        template<class R>\n        class OpDisable : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_STATE packet whenever an object should be disabled, as long as the\n                        player is logged in on the server and  if triggered from a clientside script  our\n                        last packet regarding its state did not already attempt to disable it (to prevent\n                        packet spam)\n                    */\n                    if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && ptr.isInCell())\n                    {\n                        unsigned char packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n\n                        if (packetOrigin == mwmp::CLIENT_CONSOLE || packetOrigin == mwmp::CLIENT_DIALOGUE ||\n                            ptr.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Disabled)\n                        {\n                            ptr.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Disabled);\n\n                            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                            objectList->reset();\n                            objectList->packetOrigin = packetOrigin;\n                            objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                            objectList->addObjectState(ptr, false);\n                            objectList->sendObjectState();\n                        }\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Disable unilateral state disabling on this client and expect the server's reply to our\n                        packet to do it instead\n                    */\n                    //MWBase::Environment::get().getWorld()->disable (ptr);\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n        };\n\n        template<class R>\n        class OpGetDisabled : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    runtime.push (!ptr.getRefData().isEnabled());\n                }\n        };\n\n        class OpPlayBink : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                std::string name = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                bool allowSkipping = runtime[0].mInteger != 0;\n                runtime.pop();\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_VIDEO_PLAY packet every time a video is played\n                    through a script\n                */\n                if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n                {\n                    mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                    objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                    objectList->addVideoPlay(name, allowSkipping);\n                    objectList->sendVideoPlay();\n                }\n                /*\n                    End of tes3mp addition\n                */\n\n                MWBase::Environment::get().getWindowManager()->playVideo (name, allowSkipping);\n            }\n        };\n\n        class OpGetPcSleep : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                runtime.push (MWBase::Environment::get().getWindowManager ()->getPlayerSleeping());\n            }\n        };\n\n        class OpGetPcJumping : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWBase::World* world = MWBase::Environment::get().getWorld();\n                runtime.push (world->getPlayer().getJumping());\n            }\n        };\n\n        class OpWakeUpPc : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWBase::Environment::get().getWindowManager ()->wakeUpPlayer();\n            }\n        };\n\n        class OpXBox : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push (0);\n                }\n        };\n\n        template <class R>\n        class OpOnActivate : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push (ptr.getRefData().onActivate());\n                }\n        };\n\n        template <class R>\n        class OpActivate : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    InterpreterContext& context =\n                        static_cast<InterpreterContext&> (runtime.getContext());\n\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (ptr.getRefData().activateByScript() || ptr.getContainerStore())\n                        context.executeActivation(ptr, MWMechanics::getPlayer());\n                }\n        };\n\n        template<class R>\n        class OpLock : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer lockLevel = ptr.getCellRef().getLockLevel();\n                    if(lockLevel==0) { //no lock level was ever set, set to 100 as default\n                        lockLevel = 100;\n                    }\n\n                    if (arg0==1)\n                    {\n                        lockLevel = runtime[0].mInteger;\n                        runtime.pop();\n                    }\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_LOCK packet every time an object is locked\n                        through a script, as long as the lock level being set is not\n                        the one it already has\n                    */\n                    if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && ptr.getCellRef().getLockLevel() != lockLevel)\n                    {\n                        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                        objectList->reset();\n                        objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                        objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                        objectList->addObjectLock(ptr, lockLevel);\n                        objectList->sendObjectLock();\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Disable unilateral locking on this client and expect the server's reply to our\n                        packet to do it instead\n                    */\n                    //ptr.getCellRef().lock (lockLevel);\n                    /*\n                        End of tes3mp change (major)\n                    */\n\n                    // Instantly reset door to closed state\n                    // This is done when using Lock in scripts, but not when using Lock spells.\n                    if (ptr.getTypeName() == typeid(ESM::Door).name() && !ptr.getCellRef().getTeleport())\n                    {\n                        MWBase::Environment::get().getWorld()->activateDoor(ptr, MWWorld::DoorState::Idle);\n                    }\n                }\n        };\n\n        template<class R>\n        class OpUnlock : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_LOCK packet every time an object is unlocked\n                        through a script, as long as it's not already unlocked\n                    */\n                    if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && ptr.getCellRef().getLockLevel() > 0)\n                    {\n                        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                        objectList->reset();\n                        objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                        objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                        objectList->addObjectLock(ptr, 0);\n                        objectList->sendObjectLock();\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Disable unilateral unlocking on this client and expect the server's reply to our\n                        packet to do it instead\n                    */\n                    //ptr.getCellRef().unlock ();\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n        };\n\n        class OpToggleCollisionDebug : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled =\n                        MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_CollisionDebug);\n\n                    runtime.getContext().report (enabled ?\n                        \"Collision Mesh Rendering -> On\" : \"Collision Mesh Rendering -> Off\");\n                }\n        };\n\n\n        class OpToggleCollisionBoxes : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled =\n                        MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_CollisionDebug);\n\n                    runtime.getContext().report (enabled ?\n                        \"Collision Mesh Rendering -> On\" : \"Collision Mesh Rendering -> Off\");\n                }\n        };\n\n        class OpToggleWireframe : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled =\n                        MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_Wireframe);\n\n                    runtime.getContext().report (enabled ?\n                        \"Wireframe Rendering -> On\" : \"Wireframe Rendering -> Off\");\n                }\n        };\n\n        class OpToggleBorders : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled =\n                        MWBase::Environment::get().getWorld()->toggleBorders();\n\n                    runtime.getContext().report (enabled ?\n                        \"Border Rendering -> On\" : \"Border Rendering -> Off\");\n                }\n        };\n\n        class OpTogglePathgrid : public Interpreter::Opcode0\n        {\n        public:\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                bool enabled =\n                    MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_Pathgrid);\n\n                runtime.getContext().report (enabled ?\n                    \"Path Grid rendering -> On\" : \"Path Grid Rendering -> Off\");\n            }\n        };\n\n        class OpFadeIn : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    Interpreter::Type_Float time = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getWindowManager()->fadeScreenIn(time, false);\n                }\n        };\n\n        class OpFadeOut : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    Interpreter::Type_Float time = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getWindowManager()->fadeScreenOut(time, false);\n                }\n        };\n\n        class OpFadeTo : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    Interpreter::Type_Float alpha = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float time = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getWindowManager()->fadeScreenTo(static_cast<int>(alpha), time, false);\n                }\n        };\n\n        class OpToggleWater : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWater() ? \"Water -> On\"\n                                                                                                     : \"Water -> Off\");\n                }\n        };\n\n        class OpToggleWorld : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWorld() ? \"World -> On\"\n                                                                                                     : \"World -> Off\");\n                }\n        };\n\n        class OpDontSaveObject : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    // We are ignoring the DontSaveObject statement for now. Probably not worth\n                    // bothering with. The incompatibility we are creating should be marginal at most.\n                }\n        };\n\n        class OpPcForce1stPerson : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                if (!MWBase::Environment::get().getWorld()->isFirstPerson())\n                    MWBase::Environment::get().getWorld()->togglePOV(true);\n            }\n        };\n\n        class OpPcForce3rdPerson : public Interpreter::Opcode0\n        {\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                if (MWBase::Environment::get().getWorld()->isFirstPerson())\n                    MWBase::Environment::get().getWorld()->togglePOV(true);\n            }\n        };\n\n        class OpPcGet3rdPerson : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime& runtime) override\n            {\n                runtime.push(!MWBase::Environment::get().getWorld()->isFirstPerson());\n            }\n        };\n\n        class OpToggleVanityMode : public Interpreter::Opcode0\n        {\n            static bool sActivate;\n\n        public:\n\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                MWBase::World *world =\n                    MWBase::Environment::get().getWorld();\n\n                if (world->toggleVanityMode(sActivate)) {\n                    runtime.getContext().report(sActivate ? \"Vanity Mode -> On\" : \"Vanity Mode -> Off\");\n                    sActivate = !sActivate;\n                } else {\n                    runtime.getContext().report(\"Vanity Mode -> No\");\n                }\n            }\n        };\n        bool OpToggleVanityMode::sActivate = true;\n\n        template <class R>\n        class OpGetLocked : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push (ptr.getCellRef().getLockLevel() > 0);\n                }\n        };\n\n        template <class R>\n        class OpGetEffect : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string effect = runtime.getStringLiteral(runtime[0].mInteger);\n                    runtime.pop();\n\n                    if (!ptr.getClass().isActor())\n                    {\n                        runtime.push(0);\n                        return;\n                    }\n\n                    char *end;\n                    long key = strtol(effect.c_str(), &end, 10);\n                    if(key < 0 || key > 32767 || *end != '\\0')\n                        key = ESM::MagicEffect::effectStringToId(effect);\n\n                    const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n\n                    MWMechanics::MagicEffects effects = stats.getSpells().getMagicEffects();\n                    effects += stats.getActiveSpells().getMagicEffects();\n                    if (ptr.getClass().hasInventoryStore(ptr) && !stats.isDeathAnimationFinished())\n                    {\n                        MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);\n                        effects += store.getMagicEffects();\n                    }\n\n                    for (const auto& activeEffect : effects)\n                    {\n                        if (activeEffect.first.mId == key && activeEffect.second.getModifier() > 0)\n                        {\n                            runtime.push(1);\n                            return;\n                        }\n                    }\n                    runtime.push(0);\n               }\n        };\n\n        template<class R>\n        class OpAddSoulGem : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string creature = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    std::string gem = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if (!ptr.getClass().hasInventoryStore(ptr))\n                        return;\n\n                    const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n                    store.get<ESM::Creature>().find(creature); // This line throws an exception if it can't find the creature\n\n                    MWWorld::Ptr item = *ptr.getClass().getContainerStore(ptr).add(gem, 1, ptr);\n\n                    // Set the soul on just one of the gems, not the whole stack\n                    item.getContainerStore()->unstack(item, ptr);\n                    item.getCellRef().setSoul(creature);\n\n                    // Restack the gem with other gems with the same soul\n                    item.getContainerStore()->restack(item);\n                }\n        };\n\n        template<class R>\n        class OpRemoveSoulGem : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string soul = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    // throw away additional arguments\n                    for (unsigned int i=0; i<arg0; ++i)\n                        runtime.pop();\n\n                    if (!ptr.getClass().hasInventoryStore(ptr))\n                        return;\n\n                    MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);\n                    for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n                    {\n                        if (::Misc::StringUtils::ciEqual(it->getCellRef().getSoul(), soul))\n                        {\n                            store.remove(*it, 1, ptr);\n                            return;\n                        }\n                    }\n                }\n        };\n\n        template<class R>\n        class OpDrop : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string item = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer amount = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (amount<0)\n                        throw std::runtime_error (\"amount must be non-negative\");\n\n                    // no-op\n                    if (amount == 0)\n                        return;\n\n                    if (!ptr.getClass().isActor())\n                        return;\n\n                    if (ptr.getClass().hasInventoryStore(ptr))\n                    {\n                        // Prefer dropping unequipped items first; re-stack if possible by unequipping items before dropping them.\n                        MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);\n                        int numNotEquipped = store.count(item);\n                        for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)\n                        {\n                            MWWorld::ConstContainerStoreIterator it = store.getSlot (slot);\n                            if (it != store.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item))\n                            {\n                                numNotEquipped -= it->getRefData().getCount();\n                            }\n                        }\n\n                        for (int slot = 0; slot < MWWorld::InventoryStore::Slots && amount > numNotEquipped; ++slot)\n                        {\n                            MWWorld::ContainerStoreIterator it = store.getSlot (slot);\n                            if (it != store.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item))\n                            {\n                                int numToRemove = std::min(amount - numNotEquipped, it->getRefData().getCount());\n                                store.unequipItemQuantity(*it, ptr, numToRemove);\n                                numNotEquipped += numToRemove;\n                            }\n                        }\n\n                        for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter)\n                        {\n                            if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item) && !store.isEquipped(*iter))\n                            {\n                                int removed = store.remove(*iter, amount, ptr);\n                                MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed);\n                                dropped.getCellRef().setOwner(\"\");\n\n                                amount -= removed;\n\n                                if (amount <= 0)\n                                    break;\n                            }\n                        }\n                    }\n\n                    MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), item, 1);\n                    MWWorld::Ptr itemPtr(ref.getPtr());\n                    if (amount > 0)\n                    {\n                        if (itemPtr.getClass().getScript(itemPtr).empty())\n                        {\n                            MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, itemPtr, amount);\n                        }\n                        else\n                        {\n                            // Dropping one item per time to prevent making stacks of scripted items\n                            for (int i = 0; i < amount; i++)\n                                MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, itemPtr, 1);\n                        }\n                    }\n\n                    MWBase::Environment::get().getSoundManager()->playSound3D(ptr, itemPtr.getClass().getDownSoundId(itemPtr), 1.f, 1.f);\n                }\n        };\n\n        template<class R>\n        class OpDropSoulGem : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string soul = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if (!ptr.getClass().hasInventoryStore(ptr))\n                        return;\n\n                    MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);\n\n                    for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter)\n                    {\n                        if (::Misc::StringUtils::ciEqual(iter->getCellRef().getSoul(), soul))\n                        {\n                            MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, 1);\n                            store.remove(*iter, 1, ptr);\n                            break;\n                        }\n                    }\n                }\n        };\n\n        template <class R>\n        class OpGetAttacked : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push(ptr.getClass().getCreatureStats (ptr).getAttacked ());\n                }\n        };\n\n        template <class R>\n        class OpGetWeaponDrawn : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push((ptr.getClass().hasInventoryStore(ptr) || ptr.getClass().isBipedal(ptr)) &&\n                                ptr.getClass().getCreatureStats (ptr).getDrawState () == MWMechanics::DrawState_Weapon);\n                }\n        };\n\n        template <class R>\n        class OpGetSpellReadied : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push(ptr.getClass().getCreatureStats (ptr).getDrawState () == MWMechanics::DrawState_Spell);\n                }\n        };\n\n        template <class R>\n        class OpGetSpellEffects : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    std::string id = runtime.getStringLiteral(runtime[0].mInteger);\n                    runtime.pop();\n\n                    if (!ptr.getClass().isActor())\n                    {\n                        runtime.push(0);\n                        return;\n                    }\n\n                    const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n                    runtime.push(stats.getActiveSpells().isSpellActive(id) || stats.getSpells().isSpellActive(id));\n                }\n        };\n\n        class OpGetCurrentTime : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                runtime.push(MWBase::Environment::get().getWorld()->getTimeStamp().getHour());\n            }\n        };\n\n        template <class R>\n        class OpSetDelete : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    int parameter = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (parameter == 1)\n                    {\n                        /*\n                            Start of tes3mp addition\n\n                            Send an ID_OBJECT_DELETE packet every time an object is deleted\n                            through a script, as long as we haven't already communicated\n                            a deletion for it\n                        */\n                        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() &&\n                            ptr.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Deleted)\n                        {\n                            ptr.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Deleted);\n\n                            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                            objectList->reset();\n                            objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                            objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                            objectList->addObjectGeneric(ptr);\n                            objectList->sendObjectDelete();\n                        }\n                        /*\n                            End of tes3mp addition\n                        */\n\n                        /*\n                            Start of tes3mp change (major)\n\n                            Disable unilateral deletion on this client and expect the server's reply to our\n                            packet to do it instead\n                        */\n                        //MWBase::Environment::get().getWorld()->deleteObject(ptr);\n                        /*\n                            End of tes3mp change (major)\n                        */\n                    }\n                    else if (parameter == 0)\n                        MWBase::Environment::get().getWorld()->undeleteObject(ptr);\n                    else\n                        throw std::runtime_error(\"SetDelete: unexpected parameter\");\n                }\n        };\n\n        class OpGetSquareRoot : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    float param = runtime[0].mFloat;\n                    runtime.pop();\n\n                    runtime.push(std::sqrt (param));\n                }\n        };\n\n        template <class R>\n        class OpFall : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                }\n        };\n\n        template <class R>\n        class OpGetStandingPc : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    runtime.push (MWBase::Environment::get().getWorld()->getPlayerStandingOn(ptr));\n                }\n        };\n\n        template <class R>\n        class OpGetStandingActor : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    runtime.push (MWBase::Environment::get().getWorld()->getActorStandingOn(ptr));\n                }\n        };\n\n        template <class R>\n        class OpGetCollidingPc : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    runtime.push (MWBase::Environment::get().getWorld()->getPlayerCollidingWith(ptr));\n                }\n        };\n\n        template <class R>\n        class OpGetCollidingActor : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    runtime.push (MWBase::Environment::get().getWorld()->getActorCollidingWith(ptr));\n                }\n        };\n\n        template <class R>\n        class OpHurtStandingActor : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    float healthDiffPerSecond = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getWorld()->hurtStandingActors(ptr, healthDiffPerSecond);\n                }\n        };\n\n        template <class R>\n        class OpHurtCollidingActor : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    float healthDiffPerSecond = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getWorld()->hurtCollidingActors(ptr, healthDiffPerSecond);\n                }\n        };\n\n        class OpGetWindSpeed : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push(MWBase::Environment::get().getWorld()->getWindSpeed());\n                }\n        };\n\n        template <class R>\n        class OpHitOnMe : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string objectID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);\n                    runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitObject()));\n\n                    stats.setLastHitObject(std::string());\n                }\n        };\n\n        template <class R>\n        class OpHitAttemptOnMe : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string objectID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);\n                    runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitAttemptObject()));\n\n                    stats.setLastHitAttemptObject(std::string());\n                }\n        };\n\n        template <bool Enable>\n        class OpEnableTeleporting : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n                    world->enableTeleporting(Enable);\n                }\n        };\n\n        template <bool Enable>\n        class OpEnableLevitation : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n                    world->enableLevitation(Enable);\n                }\n        };\n\n        template <class R>\n        class OpShow : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWWorld::Ptr ptr = R()(runtime, false);\n                std::string var = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n\n                std::stringstream output;\n\n                if (!ptr.isEmpty())\n                {\n                    const std::string& script = ptr.getClass().getScript(ptr);\n                    if (!script.empty())\n                    {\n                        const Compiler::Locals& locals =\n                            MWBase::Environment::get().getScriptManager()->getLocals(script);\n                        char type = locals.getType(var);\n                        std::string refId = ptr.getCellRef().getRefId();\n                        if (refId.find(' ') != std::string::npos)\n                            refId = '\"' + refId + '\"';\n                        switch (type)\n                        {\n                        case 'l':\n                        case 's':\n                            output << refId << \".\" << var << \" = \" << ptr.getRefData().getLocals().getIntVar(script, var);\n                            break;\n                        case 'f':\n                            output << refId << \".\" << var << \" = \" << ptr.getRefData().getLocals().getFloatVar(script, var);\n                            break;\n                        }\n                    }\n                }\n                if (output.rdbuf()->in_avail() == 0)\n                {\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n                    char type = world->getGlobalVariableType (var);\n\n                    switch (type)\n                    {\n                    case 's':\n                        output << var << \" = \" << runtime.getContext().getGlobalShort (var);\n                        break;\n                    case 'l':\n                        output << var << \" = \" << runtime.getContext().getGlobalLong (var);\n                        break;\n                    case 'f':\n                        output << var << \" = \" << runtime.getContext().getGlobalFloat (var);\n                        break;\n                    default:\n                        output << \"unknown variable\";\n                    }\n                }\n                runtime.getContext().report(output.str());\n            }\n        };\n\n        template <class R>\n        class OpShowVars : public Interpreter::Opcode0\n        {\n            void printLocalVars(Interpreter::Runtime &runtime, const MWWorld::Ptr &ptr)\n            {\n                std::stringstream str;\n\n                const std::string script = ptr.getClass().getScript(ptr);\n                if(script.empty())\n                    str<< ptr.getCellRef().getRefId()<<\" does not have a script.\";\n                else\n                {\n                    str<< \"Local variables for \"<<ptr.getCellRef().getRefId();\n\n                    const Locals &locals = ptr.getRefData().getLocals();\n                    const Compiler::Locals &complocals = MWBase::Environment::get().getScriptManager()->getLocals(script);\n\n                    const std::vector<std::string> *names = &complocals.get('s');\n                    for(size_t i = 0;i < names->size();++i)\n                    {\n                        if(i >= locals.mShorts.size())\n                            break;\n                        str<<std::endl<< \"  \"<<(*names)[i]<<\" = \"<<locals.mShorts[i]<<\" (short)\";\n                    }\n                    names = &complocals.get('l');\n                    for(size_t i = 0;i < names->size();++i)\n                    {\n                        if(i >= locals.mLongs.size())\n                            break;\n                        str<<std::endl<< \"  \"<<(*names)[i]<<\" = \"<<locals.mLongs[i]<<\" (long)\";\n                    }\n                    names = &complocals.get('f');\n                    for(size_t i = 0;i < names->size();++i)\n                    {\n                        if(i >= locals.mFloats.size())\n                            break;\n                        str<<std::endl<< \"  \"<<(*names)[i]<<\" = \"<<locals.mFloats[i]<<\" (float)\";\n                    }\n                }\n\n                runtime.getContext().report(str.str());\n            }\n\n            void printGlobalVars(Interpreter::Runtime &runtime)\n            {\n                std::stringstream str;\n                str<< \"Global variables:\";\n\n                MWBase::World *world = MWBase::Environment::get().getWorld();\n                std::vector<std::string> names = runtime.getContext().getGlobals();\n                for(size_t i = 0;i < names.size();++i)\n                {\n                    char type = world->getGlobalVariableType (names[i]);\n                    str << std::endl << \" \" << names[i] << \" = \";\n\n                    switch (type)\n                    {\n                        case 's':\n\n                            str << runtime.getContext().getGlobalShort (names[i]) << \" (short)\";\n                            break;\n\n                        case 'l':\n\n                            str << runtime.getContext().getGlobalLong (names[i]) << \" (long)\";\n                            break;\n\n                        case 'f':\n\n                            str << runtime.getContext().getGlobalFloat (names[i]) << \" (float)\";\n                            break;\n\n                        default:\n\n                            str << \"<unknown type>\";\n                    }\n                }\n\n                runtime.getContext().report (str.str());\n            }\n\n        public:\n            void execute(Interpreter::Runtime& runtime) override\n            {\n                MWWorld::Ptr ptr = R()(runtime, false);\n                if (!ptr.isEmpty())\n                    printLocalVars(runtime, ptr);\n                else\n                {\n                    // No reference, no problem.\n                    printGlobalVars(runtime);\n                }\n            }\n        };\n\n        class OpToggleScripts : public Interpreter::Opcode0\n        {\n        public:\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                bool enabled = MWBase::Environment::get().getWorld()->toggleScripts();\n\n                runtime.getContext().report(enabled ? \"Scripts -> On\" : \"Scripts -> Off\");\n            }\n        };\n\n        class OpToggleGodMode : public Interpreter::Opcode0\n        {\n            public:\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled = MWBase::Environment::get().getWorld()->toggleGodMode();\n\n                    runtime.getContext().report (enabled ? \"God Mode -> On\" : \"God Mode -> Off\");\n                }\n        };\n\n        template <class R>\n        class OpCast : public Interpreter::Opcode0\n        {\n        public:\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWWorld::Ptr ptr = R()(runtime);\n\n                std::string spellId = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral (runtime[0].mInteger));\n                runtime.pop();\n\n                const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spellId);\n                if (!spell)\n                {\n                    runtime.getContext().report(\"spellcasting failed: cannot find spell \\\"\"+spellId+\"\\\"\");\n                    return;\n                }\n\n                if (ptr == MWMechanics::getPlayer())\n                {\n                    MWBase::Environment::get().getWorld()->getPlayer().setSelectedSpell(spellId);\n                    return;\n                }\n\n                if (ptr.getClass().isActor())\n                {\n                    MWMechanics::AiCast castPackage(targetId, spellId, true);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(castPackage, ptr);\n                    return;\n                }\n\n                MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(targetId, false, false);\n                if (target.isEmpty())\n                    return;\n\n                MWMechanics::CastSpell cast(ptr, target, false, true);\n                cast.playSpellCastingEffects(spell->mId, false);\n                cast.mHitPosition = target.getRefData().getPosition().asVec3();\n                cast.mAlwaysSucceed = true;\n                cast.cast(spell);\n            }\n        };\n\n        template <class R>\n        class OpExplodeSpell : public Interpreter::Opcode0\n        {\n        public:\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWWorld::Ptr ptr = R()(runtime);\n\n                std::string spellId = runtime.getStringLiteral (runtime[0].mInteger);\n                runtime.pop();\n\n                const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spellId);\n                if (!spell)\n                {\n                    runtime.getContext().report(\"spellcasting failed: cannot find spell \\\"\"+spellId+\"\\\"\");\n                    return;\n                }\n\n                if (ptr == MWMechanics::getPlayer())\n                {\n                    MWBase::Environment::get().getWorld()->getPlayer().setSelectedSpell(spellId);\n                    return;\n                }\n\n                if (ptr.getClass().isActor())\n                {\n                    MWMechanics::AiCast castPackage(ptr.getCellRef().getRefId(), spellId, true);\n                    ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(castPackage, ptr);\n                    return;\n                }\n\n                MWMechanics::CastSpell cast(ptr, ptr, false, true);\n                cast.mHitPosition = ptr.getRefData().getPosition().asVec3();\n                cast.mAlwaysSucceed = true;\n                cast.cast(spell);\n            }\n        };\n\n        class OpGoToJail : public Interpreter::Opcode0\n        {\n        public:\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWBase::World* world = MWBase::Environment::get().getWorld();\n                world->goToJail();\n            }\n        };\n\n        class OpPayFine : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                MWWorld::Ptr player = MWMechanics::getPlayer();\n                player.getClass().getNpcStats(player).setBounty(0);\n                MWBase::Environment::get().getWorld()->confiscateStolenItems(player);\n                MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId();\n            }\n        };\n\n        class OpPayFineThief : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                MWWorld::Ptr player = MWMechanics::getPlayer();\n                player.getClass().getNpcStats(player).setBounty(0);\n                MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId();\n            }\n        };\n\n        class OpGetPcInJail : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime &runtime) override\n                {\n                    runtime.push (MWBase::Environment::get().getWorld()->isPlayerInJail());\n                }\n        };\n\n        class OpGetPcTraveling : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime &runtime) override\n                {\n                    runtime.push (MWBase::Environment::get().getWorld()->isPlayerTraveling());\n                }\n        };\n\n        template <class R>\n        class OpBetaComment : public Interpreter::Opcode1\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime, unsigned int arg0) override\n            {\n                MWWorld::Ptr ptr = R()(runtime);\n\n                std::stringstream msg;\n\n                msg << \"Report time: \";\n\n                std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());\n                msg << std::put_time(std::gmtime(&currentTime), \"%Y.%m.%d %T UTC\") << std::endl;\n\n                msg << \"Content file: \";\n\n                if (!ptr.getCellRef().hasContentFile())\n                    msg << \"[None]\" << std::endl;\n                else\n                {\n                    std::vector<std::string> contentFiles = MWBase::Environment::get().getWorld()->getContentFiles();\n\n                    msg << contentFiles.at (ptr.getCellRef().getRefNum().mContentFile) << std::endl;\n                    msg << \"RefNum: \" << ptr.getCellRef().getRefNum().mIndex << std::endl;\n                }\n\n                if (ptr.getRefData().isDeletedByContentFile())\n                    msg << \"[Deleted by content file]\" << std::endl;\n                if (!ptr.getRefData().getCount())\n                    msg << \"[Deleted]\" << std::endl;\n\n                msg << \"RefID: \" << ptr.getCellRef().getRefId() << std::endl;\n                msg << \"Memory address: \" << ptr.getBase() << std::endl;\n\n                if (ptr.isInCell())\n                {\n                    MWWorld::CellStore* cell = ptr.getCell();\n                    msg << \"Cell: \" << MWBase::Environment::get().getWorld()->getCellName(cell) << std::endl;\n                    if (cell->getCell()->isExterior())\n                        msg << \"Grid: \" << cell->getCell()->getGridX() << \" \" << cell->getCell()->getGridY() << std::endl;\n                    osg::Vec3f pos (ptr.getRefData().getPosition().asVec3());\n                    msg << \"Coordinates: \" << pos.x() << \" \" << pos.y() << \" \" << pos.z() << std::endl;\n                    auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();\n                    std::string model = ::Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr), vfs);\n                    msg << \"Model: \" << model << std::endl;\n                    if(!model.empty())\n                    {\n                        const std::string archive = vfs->getArchive(model);\n                        if(!archive.empty())\n                            msg << \"(\" << archive << \")\" << std::endl;\n                    }\n                    if (!ptr.getClass().getScript(ptr).empty())\n                        msg << \"Script: \" << ptr.getClass().getScript(ptr) << std::endl;\n                }\n\n                while (arg0 > 0)\n                {\n                    std::string notes = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    if (!notes.empty())\n                        msg << \"Notes: \" << notes << std::endl;\n                    --arg0;\n                }\n\n                Log(Debug::Warning) << \"\\n\" << msg.str();\n\n                runtime.getContext().report(msg.str());\n            }\n        };\n\n        class OpAddToLevCreature : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n                const std::string& creatureId = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n                int level = runtime[0].mInteger;\n                runtime.pop();\n\n                ESM::CreatureLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get<ESM::CreatureLevList>().find(levId);\n                addToLevList(&listCopy, creatureId, level);\n                MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy);\n            }\n        };\n\n        class OpRemoveFromLevCreature : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n                const std::string& creatureId = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n                int level = runtime[0].mInteger;\n                runtime.pop();\n\n                ESM::CreatureLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get<ESM::CreatureLevList>().find(levId);\n                removeFromLevList(&listCopy, creatureId, level);\n                MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy);\n            }\n        };\n\n        class OpAddToLevItem : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n                const std::string& itemId = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n                int level = runtime[0].mInteger;\n                runtime.pop();\n\n                ESM::ItemLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get<ESM::ItemLevList>().find(levId);\n                addToLevList(&listCopy, itemId, level);\n                MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy);\n            }\n        };\n\n        class OpRemoveFromLevItem : public Interpreter::Opcode0\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n                const std::string& itemId = runtime.getStringLiteral(runtime[0].mInteger);\n                runtime.pop();\n                int level = runtime[0].mInteger;\n                runtime.pop();\n\n                ESM::ItemLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get<ESM::ItemLevList>().find(levId);\n                removeFromLevList(&listCopy, itemId, level);\n                MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy);\n            }\n        };\n\n        template <class R>\n        class OpShowSceneGraph : public Interpreter::Opcode1\n        {\n        public:\n            void execute(Interpreter::Runtime &runtime, unsigned int arg0) override\n            {\n                MWWorld::Ptr ptr = R()(runtime, false);\n\n                int confirmed = 0;\n                if (arg0==1)\n                {\n                    confirmed = runtime[0].mInteger;\n                    runtime.pop();\n                }\n\n                if (ptr.isEmpty() && !confirmed)\n                    runtime.getContext().report(\"Exporting the entire scene graph will result in a large file. Confirm this action using 'showscenegraph 1' or select an object instead.\");\n                else\n                {\n                    const std::string& filename = MWBase::Environment::get().getWorld()->exportSceneGraph(ptr);\n                    runtime.getContext().report(\"Wrote '\" + filename + \"'\");\n                }\n            }\n        };\n\n        class OpToggleNavMesh : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled =\n                        MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_NavMesh);\n\n                    runtime.getContext().report (enabled ?\n                        \"Navigation Mesh Rendering -> On\" : \"Navigation Mesh Rendering -> Off\");\n                }\n        };\n\n        class OpToggleActorsPaths : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled =\n                        MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_ActorsPaths);\n\n                    runtime.getContext().report (enabled ?\n                        \"Agents Paths Rendering -> On\" : \"Agents Paths Rendering -> Off\");\n                }\n        };\n\n        class OpSetNavMeshNumberToRender : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    const auto navMeshNumber = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (navMeshNumber < 0)\n                    {\n                        runtime.getContext().report(\"Invalid navmesh number: use not less than zero values\");\n                        return;\n                    }\n\n                    MWBase::Environment::get().getWorld()->setNavMeshNumberToRender(static_cast<std::size_t>(navMeshNumber));\n                }\n        };\n\n        template <class R>\n        class OpRepairedOnMe : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    // Broken in vanilla and deliberately no-op.\n                    runtime.push(0);\n                }\n        };\n\n        class OpToggleRecastMesh : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled =\n                        MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_RecastMesh);\n\n                    runtime.getContext().report (enabled ?\n                        \"Recast Mesh Rendering -> On\" : \"Recast Mesh Rendering -> Off\");\n                }\n        };\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5 (Compiler::Misc::opcodeMenuMode, new OpMenuMode);\n            interpreter.installSegment5 (Compiler::Misc::opcodeRandom, new OpRandom);\n            interpreter.installSegment5 (Compiler::Misc::opcodeScriptRunning, new OpScriptRunning);\n            interpreter.installSegment5 (Compiler::Misc::opcodeStartScript, new OpStartScript<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeStartScriptExplicit, new OpStartScript<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeStopScript, new OpStopScript);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetSecondsPassed, new OpGetSecondsPassed);\n            interpreter.installSegment5 (Compiler::Misc::opcodeEnable, new OpEnable<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeEnableExplicit, new OpEnable<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDisable, new OpDisable<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDisableExplicit, new OpDisable<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetDisabled, new OpGetDisabled<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetDisabledExplicit, new OpGetDisabled<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox);\n            interpreter.installSegment5 (Compiler::Misc::opcodeOnActivate, new OpOnActivate<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeOnActivateExplicit, new OpOnActivate<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeActivate, new OpActivate<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeActivateExplicit, new OpActivate<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Misc::opcodeLock, new OpLock<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Misc::opcodeLockExplicit, new OpLock<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeUnlock, new OpUnlock<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeUnlockExplicit, new OpUnlock<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleCollisionDebug, new OpToggleCollisionDebug);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleCollisionBoxes, new OpToggleCollisionBoxes);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleWireframe, new OpToggleWireframe);\n            interpreter.installSegment5 (Compiler::Misc::opcodeFadeIn, new OpFadeIn);\n            interpreter.installSegment5 (Compiler::Misc::opcodeFadeOut, new OpFadeOut);\n            interpreter.installSegment5 (Compiler::Misc::opcodeFadeTo, new OpFadeTo);\n            interpreter.installSegment5 (Compiler::Misc::opcodeTogglePathgrid, new OpTogglePathgrid);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleWater, new OpToggleWater);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleWorld, new OpToggleWorld);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDontSaveObject, new OpDontSaveObject);\n            interpreter.installSegment5 (Compiler::Misc::opcodePcForce1stPerson, new OpPcForce1stPerson);\n            interpreter.installSegment5 (Compiler::Misc::opcodePcForce3rdPerson, new OpPcForce3rdPerson);\n            interpreter.installSegment5 (Compiler::Misc::opcodePcGet3rdPerson, new OpPcGet3rdPerson);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleVanityMode, new OpToggleVanityMode);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetPcSleep, new OpGetPcSleep);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetPcJumping, new OpGetPcJumping);\n            interpreter.installSegment5 (Compiler::Misc::opcodeWakeUpPc, new OpWakeUpPc);\n            interpreter.installSegment5 (Compiler::Misc::opcodePlayBink, new OpPlayBink);\n            interpreter.installSegment5 (Compiler::Misc::opcodePayFine, new OpPayFine);\n            interpreter.installSegment5 (Compiler::Misc::opcodePayFineThief, new OpPayFineThief);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGoToJail, new OpGoToJail);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetLocked, new OpGetLocked<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetLockedExplicit, new OpGetLocked<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetEffect, new OpGetEffect<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetEffectExplicit, new OpGetEffect<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGem, new OpAddSoulGem<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGemExplicit, new OpAddSoulGem<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDrop, new OpDrop<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDropExplicit, new OpDrop<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGem, new OpDropSoulGem<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGemExplicit, new OpDropSoulGem<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetAttacked, new OpGetAttacked<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetAttackedExplicit, new OpGetAttacked<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawn, new OpGetWeaponDrawn<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellReadied, new OpGetSpellReadied<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellReadiedExplicit, new OpGetSpellReadied<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffects, new OpGetSpellEffects<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffectsExplicit, new OpGetSpellEffects<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetCurrentTime, new OpGetCurrentTime);\n            interpreter.installSegment5 (Compiler::Misc::opcodeSetDelete, new OpSetDelete<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeSetDeleteExplicit, new OpSetDelete<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetSquareRoot, new OpGetSquareRoot);\n            interpreter.installSegment5 (Compiler::Misc::opcodeFall, new OpFall<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeFallExplicit, new OpFall<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPc, new OpGetStandingPc<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPcExplicit, new OpGetStandingPc<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActor, new OpGetStandingActor<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActorExplicit, new OpGetStandingActor<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingPc, new OpGetCollidingPc<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingPcExplicit, new OpGetCollidingPc<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingActor, new OpGetCollidingActor<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingActorExplicit, new OpGetCollidingActor<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeHurtStandingActor, new OpHurtStandingActor<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeHurtStandingActorExplicit, new OpHurtStandingActor<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeHurtCollidingActor, new OpHurtCollidingActor<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeHurtCollidingActorExplicit, new OpHurtCollidingActor<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetWindSpeed, new OpGetWindSpeed);\n            interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMe, new OpHitOnMe<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMeExplicit, new OpHitOnMe<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeHitAttemptOnMe, new OpHitAttemptOnMe<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeHitAttemptOnMeExplicit, new OpHitAttemptOnMe<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDisableTeleporting, new OpEnableTeleporting<false>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeEnableTeleporting, new OpEnableTeleporting<true>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeShowVars, new OpShowVars<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeShowVarsExplicit, new OpShowVars<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeShow, new OpShow<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeShowExplicit, new OpShow<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleGodMode, new OpToggleGodMode);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleScripts, new OpToggleScripts);\n            interpreter.installSegment5 (Compiler::Misc::opcodeDisableLevitation, new OpEnableLevitation<false>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeEnableLevitation, new OpEnableLevitation<true>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeCast, new OpCast<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetPcInJail, new OpGetPcInJail);\n            interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling);\n            interpreter.installSegment3 (Compiler::Misc::opcodeBetaComment, new OpBetaComment<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Misc::opcodeBetaCommentExplicit, new OpBetaComment<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevCreature, new OpAddToLevCreature);\n            interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevCreature, new OpRemoveFromLevCreature);\n            interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevItem, new OpAddToLevItem);\n            interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevItem, new OpRemoveFromLevItem);\n            interpreter.installSegment3 (Compiler::Misc::opcodeShowSceneGraph, new OpShowSceneGraph<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Misc::opcodeShowSceneGraphExplicit, new OpShowSceneGraph<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleBorders, new OpToggleBorders);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleNavMesh, new OpToggleNavMesh);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleActorsPaths, new OpToggleActorsPaths);\n            interpreter.installSegment5 (Compiler::Misc::opcodeSetNavMeshNumberToRender, new OpSetNavMeshNumberToRender);\n            interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMe, new OpRepairedOnMe<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMeExplicit, new OpRepairedOnMe<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Misc::opcodeToggleRecastMesh, new OpToggleRecastMesh);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/miscextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_MISCEXTENSIONS_H\n#define GAME_SCRIPT_MISCEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    namespace Misc\n    {        \n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n\n\n"
  },
  {
    "path": "apps/openmw/mwscript/ref.cpp",
    "content": "#include \"ref.hpp\"\n\n#include <components/interpreter/runtime.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"interpretercontext.hpp\"\n\nMWWorld::Ptr MWScript::ExplicitRef::operator() (Interpreter::Runtime& runtime, bool required,\n    bool activeOnly) const\n{\n    std::string id = runtime.getStringLiteral(runtime[0].mInteger);\n    runtime.pop();\n\n    if (required)\n        return MWBase::Environment::get().getWorld()->getPtr(id, activeOnly);\n    else\n        return MWBase::Environment::get().getWorld()->searchPtr(id, activeOnly);\n}\n\nMWWorld::Ptr MWScript::ImplicitRef::operator() (Interpreter::Runtime& runtime, bool required,\n    bool activeOnly) const\n{\n    MWScript::InterpreterContext& context\n    = static_cast<MWScript::InterpreterContext&> (runtime.getContext());\n\n    return context.getReference(required);\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/ref.hpp",
    "content": "#ifndef GAME_MWSCRIPT_REF_H\n#define GAME_MWSCRIPT_REF_H\n\n#include <string>\n\n#include \"../mwworld/ptr.hpp\"\n\nnamespace Interpreter\n{\n    class Runtime;\n}\n\nnamespace MWScript\n{\n    struct ExplicitRef\n    {\n        static constexpr bool implicit = false;\n\n        MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required = true,\n            bool activeOnly = false) const;\n    };\n\n    struct ImplicitRef\n    {\n        static constexpr bool implicit = true;\n\n        MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required = true,\n            bool activeOnly = false) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/scriptmanagerimp.cpp",
    "content": "#include \"scriptmanagerimp.hpp\"\n\n#include <cassert>\n#include <sstream>\n#include <exception>\n#include <algorithm>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/esm/loadscpt.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/compiler/scanner.hpp>\n#include <components/compiler/context.hpp>\n#include <components/compiler/exception.hpp>\n#include <components/compiler/quickfileparser.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"extensions.hpp\"\n#include \"interpretercontext.hpp\"\n\nnamespace MWScript\n{\n    ScriptManager::ScriptManager (const MWWorld::ESMStore& store,\n        Compiler::Context& compilerContext, int warningsMode,\n        const std::vector<std::string>& scriptBlacklist)\n    : mErrorHandler(), mStore (store),\n      mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext),\n      mOpcodesInstalled (false), mGlobalScripts (store)\n    {\n        mErrorHandler.setWarningsMode (warningsMode);\n\n        mScriptBlacklist.resize (scriptBlacklist.size());\n\n        std::transform (scriptBlacklist.begin(), scriptBlacklist.end(),\n            mScriptBlacklist.begin(), Misc::StringUtils::lowerCase);\n        std::sort (mScriptBlacklist.begin(), mScriptBlacklist.end());\n    }\n\n    bool ScriptManager::compile (const std::string& name)\n    {\n        mParser.reset();\n        mErrorHandler.reset();\n\n        if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))\n        {\n            mErrorHandler.setContext(name);\n\n            bool Success = true;\n            try\n            {\n                std::istringstream input (script->mScriptText);\n\n                Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions());\n\n                scanner.scan (mParser);\n\n                if (!mErrorHandler.isGood())\n                    Success = false;\n            }\n            catch (const Compiler::SourceException&)\n            {\n                // error has already been reported via error handler\n                Success = false;\n            }\n            catch (const std::exception& error)\n            {\n                Log(Debug::Error) << \"Error: An exception has been thrown: \" << error.what();\n                Success = false;\n            }\n\n            if (!Success)\n            {\n                Log(Debug::Error) << \"Error: script compiling failed: \" << name;\n            }\n\n            if (Success)\n            {\n                std::vector<Interpreter::Type_Code> code;\n                mParser.getCode(code);\n                mScripts.emplace(name, CompiledScript(code, mParser.getLocals()));\n\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    bool ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext)\n    {\n        // compile script\n        ScriptCollection::iterator iter = mScripts.find (name);\n\n        if (iter==mScripts.end())\n        {\n            if (!compile (name))\n            {\n                // failed -> ignore script from now on.\n                std::vector<Interpreter::Type_Code> empty;\n                mScripts.emplace(name, CompiledScript(empty, Compiler::Locals()));\n                return false;\n            }\n\n            iter = mScripts.find (name);\n            assert (iter!=mScripts.end());\n        }\n\n        // execute script\n        if (!iter->second.mByteCode.empty() && iter->second.mActive)\n            try\n            {\n                if (!mOpcodesInstalled)\n                {\n                    installOpcodes (mInterpreter);\n                    mOpcodesInstalled = true;\n                }\n\n                mInterpreter.run (&iter->second.mByteCode[0], iter->second.mByteCode.size(), interpreterContext);\n                return true;\n            }\n            catch (const MissingImplicitRefError& e)\n            {\n                Log(Debug::Error) << \"Execution of script \" << name << \" failed: \"  << e.what();\n            }\n            catch (const std::exception& e)\n            {\n                Log(Debug::Error) << \"Execution of script \" << name << \" failed: \"  << e.what();\n\n                iter->second.mActive = false; // don't execute again.\n            }\n        return false;\n    }\n\n    void ScriptManager::clear()\n    {\n        for (auto& script : mScripts)\n        {\n            script.second.mActive = true;\n        }\n\n        mGlobalScripts.clear();\n    }\n\n    std::pair<int, int> ScriptManager::compileAll()\n    {\n        int count = 0;\n        int success = 0;\n\n        for (auto& script : mStore.get<ESM::Script>())\n        {\n            if (!std::binary_search (mScriptBlacklist.begin(), mScriptBlacklist.end(),\n                Misc::StringUtils::lowerCase(script.mId)))\n            {\n                ++count;\n\n                if (compile(script.mId))\n                    ++success;\n            }\n        }\n\n        return std::make_pair (count, success);\n    }\n\n    const Compiler::Locals& ScriptManager::getLocals (const std::string& name)\n    {\n        std::string name2 = Misc::StringUtils::lowerCase (name);\n\n        {\n            ScriptCollection::iterator iter = mScripts.find (name2);\n\n            if (iter!=mScripts.end())\n                return iter->second.mLocals;\n        }\n\n        {\n            std::map<std::string, Compiler::Locals>::iterator iter = mOtherLocals.find (name2);\n\n            if (iter!=mOtherLocals.end())\n                return iter->second;\n        }\n\n        if (const ESM::Script *script = mStore.get<ESM::Script>().search (name2))\n        {\n            Compiler::Locals locals;\n\n            const Compiler::ContextOverride override(mErrorHandler, name2 + \"[local variables]\");\n\n            std::istringstream stream (script->mScriptText);\n            Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals);\n            Compiler::Scanner scanner (mErrorHandler, stream, mCompilerContext.getExtensions());\n            scanner.scan (parser);\n\n            std::map<std::string, Compiler::Locals>::iterator iter =\n                mOtherLocals.emplace(name2, locals).first;\n\n            return iter->second;\n        }\n\n        throw std::logic_error (\"script \" + name + \" does not exist\");\n    }\n\n    GlobalScripts& ScriptManager::getGlobalScripts()\n    {\n        return mGlobalScripts;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/scriptmanagerimp.hpp",
    "content": "#ifndef GAME_SCRIPT_SCRIPTMANAGER_H\n#define GAME_SCRIPT_SCRIPTMANAGER_H\n\n#include <map>\n#include <string>\n\n#include <components/compiler/streamerrorhandler.hpp>\n#include <components/compiler/fileparser.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/types.hpp>\n\n#include \"../mwbase/scriptmanager.hpp\"\n\n#include \"globalscripts.hpp\"\n\nnamespace MWWorld\n{\n    class ESMStore;\n}\n\nnamespace Compiler\n{\n    class Context;\n}\n\nnamespace Interpreter\n{\n    class Context;\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    class ScriptManager : public MWBase::ScriptManager\n    {\n            Compiler::StreamErrorHandler mErrorHandler;\n            const MWWorld::ESMStore& mStore;\n            Compiler::Context& mCompilerContext;\n            Compiler::FileParser mParser;\n            Interpreter::Interpreter mInterpreter;\n            bool mOpcodesInstalled;\n\n            struct CompiledScript\n            {\n                std::vector<Interpreter::Type_Code> mByteCode;\n                Compiler::Locals mLocals;\n                bool mActive;\n\n                CompiledScript(const std::vector<Interpreter::Type_Code>& code, const Compiler::Locals& locals)\n                {\n                    mByteCode = code;\n                    mLocals = locals;\n                    mActive = true;\n                }\n            };\n\n            typedef std::map<std::string, CompiledScript> ScriptCollection;\n\n            ScriptCollection mScripts;\n            GlobalScripts mGlobalScripts;\n            std::map<std::string, Compiler::Locals> mOtherLocals;\n            std::vector<std::string> mScriptBlacklist;\n\n        public:\n\n            ScriptManager (const MWWorld::ESMStore& store,\n                Compiler::Context& compilerContext, int warningsMode,\n                const std::vector<std::string>& scriptBlacklist);\n\n            void clear() override;\n\n            bool run (const std::string& name, Interpreter::Context& interpreterContext) override;\n            ///< Run the script with the given name (compile first, if not compiled yet)\n\n            bool compile (const std::string& name) override;\n            ///< Compile script with the given namen\n            /// \\return Success?\n\n            std::pair<int, int> compileAll() override;\n            ///< Compile all scripts\n            /// \\return count, success\n\n            const Compiler::Locals& getLocals (const std::string& name) override;\n            ///< Return locals for script \\a name.\n\n            GlobalScripts& getGlobalScripts() override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/skyextensions.cpp",
    "content": "#include \"skyextensions.hpp\"\n\n#include <algorithm>\n\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"interpretercontext.hpp\"\n\nnamespace MWScript\n{\n    namespace Sky\n    {\n        class OpToggleSky : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    bool enabled = MWBase::Environment::get().getWorld()->toggleSky();\n\n                    runtime.getContext().report (enabled ? \"Sky -> On\" : \"Sky -> Off\");\n                }\n        };\n\n        class OpTurnMoonWhite : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::Environment::get().getWorld()->setMoonColour (false);\n                }\n        };\n\n        class OpTurnMoonRed : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::Environment::get().getWorld()->setMoonColour (true);\n                }\n        };\n\n        class OpGetMasserPhase : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push (MWBase::Environment::get().getWorld()->getMasserPhase());\n                }\n        };\n\n        class OpGetSecundaPhase : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push (MWBase::Environment::get().getWorld()->getSecundaPhase());\n                }\n        };\n\n        class OpGetCurrentWeather : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.push (MWBase::Environment::get().getWorld()->getCurrentWeather());\n                }\n        };\n\n        class OpChangeWeather : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string region = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer id = runtime[0].mInteger;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getWorld()->changeWeather(region, id);\n                }\n        };\n\n        class OpModRegion : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    std::string region = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    std::vector<char> chances;\n                    chances.reserve(10);\n                    while(arg0 > 0)\n                    {\n                        chances.push_back(std::max(0, std::min(127, runtime[0].mInteger)));\n                        runtime.pop();\n                        arg0--;\n                    }\n\n                    MWBase::Environment::get().getWorld()->modRegion(region, chances);\n                }\n        };\n\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5 (Compiler::Sky::opcodeToggleSky, new OpToggleSky);\n            interpreter.installSegment5 (Compiler::Sky::opcodeTurnMoonWhite, new OpTurnMoonWhite);\n            interpreter.installSegment5 (Compiler::Sky::opcodeTurnMoonRed, new OpTurnMoonRed);\n            interpreter.installSegment5 (Compiler::Sky::opcodeGetMasserPhase, new OpGetMasserPhase);\n            interpreter.installSegment5 (Compiler::Sky::opcodeGetSecundaPhase, new OpGetSecundaPhase);\n            interpreter.installSegment5 (Compiler::Sky::opcodeGetCurrentWeather, new OpGetCurrentWeather);\n            interpreter.installSegment5 (Compiler::Sky::opcodeChangeWeather, new OpChangeWeather);\n            interpreter.installSegment3 (Compiler::Sky::opcodeModRegion, new OpModRegion);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/skyextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_SKYEXTENSIONS_H\n#define GAME_SCRIPT_SKYEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief sky-related script functionality\n    namespace Sky\n    {        \n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/soundextensions.cpp",
    "content": "#include \"soundextensions.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/ScriptController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwworld/inventorystore.hpp\"\n#include \"../mwworld/class.hpp\"\n\n#include \"interpretercontext.hpp\"\n#include \"ref.hpp\"\n\nnamespace MWScript\n{\n    namespace Sound\n    {\n        template<class R>\n        class OpSay : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    MWScript::InterpreterContext& context\n                        = static_cast<MWScript::InterpreterContext&> (runtime.getContext());\n\n                    std::string file = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    std::string text = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWBase::Environment::get().getSoundManager()->say (ptr, file);\n\n                    if (MWBase::Environment::get().getWindowManager ()->getSubtitlesEnabled())\n                        context.messageBox (text);\n                }\n        };\n\n        template<class R>\n        class OpSayDone : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push (MWBase::Environment::get().getSoundManager()->sayDone (ptr));\n                }\n        };\n\n        class OpStreamMusic : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string sound = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_MUSIC_PLAY packet every time new music is streamed through\n                        a script\n                    */\n                    mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                    objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                    objectList->addMusicPlay(sound);\n                    objectList->sendMusicPlay();\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    MWBase::Environment::get().getSoundManager()->streamMusic (sound);\n                }\n        };\n\n        class OpPlaySound : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string sound = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWBase::Environment::get().getSoundManager()->playSound(sound, 1.0, 1.0, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);\n                }\n        };\n\n        class OpPlaySoundVP : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string sound = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Float volume = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float pitch = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getSoundManager()->playSound(sound, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);\n                }\n        };\n\n        template<class R>\n        class OpPlaySound3D : public Interpreter::Opcode0\n        {\n                bool mLoop;\n\n            public:\n\n                OpPlaySound3D (bool loop) : mLoop (loop) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string sound = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWBase::Environment::get().getSoundManager()->playSound3D(ptr, sound, 1.0, 1.0,\n                                                                              MWSound::Type::Sfx,\n                                                                              mLoop ? MWSound::PlayMode::LoopRemoveAtDistance\n                                                                                    : MWSound::PlayMode::Normal);\n                }\n        };\n\n        template<class R>\n        class OpPlaySoundVP3D : public Interpreter::Opcode0\n        {\n                bool mLoop;\n\n            public:\n\n                OpPlaySoundVP3D (bool loop) : mLoop (loop) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string sound = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Float volume = runtime[0].mFloat;\n                    runtime.pop();\n\n                    Interpreter::Type_Float pitch = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWBase::Environment::get().getSoundManager()->playSound3D(ptr, sound, volume, pitch,\n                                                                              MWSound::Type::Sfx,\n                                                                              mLoop ? MWSound::PlayMode::LoopRemoveAtDistance\n                                                                                    : MWSound::PlayMode::Normal);\n\n                }\n        };\n\n        template<class R>\n        class OpStopSound : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string sound = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWBase::Environment::get().getSoundManager()->stopSound3D (ptr, sound);\n                }\n        };\n\n        template<class R>\n        class OpGetSoundPlaying : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    int index = runtime[0].mInteger;\n                    runtime.pop();\n\n                    bool ret = MWBase::Environment::get().getSoundManager()->getSoundPlaying (\n                                    ptr, runtime.getStringLiteral (index));\n\n                    // GetSoundPlaying called on an equipped item should also look for sounds played by the equipping actor.\n                    if (!ret && ptr.getContainerStore())\n                    {\n                        MWWorld::Ptr cont = MWBase::Environment::get().getWorld()->findContainer(ptr);\n\n                        if (!cont.isEmpty() && cont.getClass().hasInventoryStore(cont) && cont.getClass().getInventoryStore(cont).isEquipped(ptr))\n                        {\n                            ret = MWBase::Environment::get().getSoundManager()->getSoundPlaying (\n                                        cont, runtime.getStringLiteral (index));\n                        }\n                    }\n\n                    runtime.push(ret);\n                }\n        };\n\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5 (Compiler::Sound::opcodeSay, new OpSay<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Sound::opcodeSayDone, new OpSayDone<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Sound::opcodeStreamMusic, new OpStreamMusic);\n            interpreter.installSegment5 (Compiler::Sound::opcodePlaySound, new OpPlaySound);\n            interpreter.installSegment5 (Compiler::Sound::opcodePlaySoundVP, new OpPlaySoundVP);\n            interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3D, new OpPlaySound3D<ImplicitRef> (false));\n            interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DVP, new OpPlaySoundVP3D<ImplicitRef> (false));\n            interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3D, new OpPlaySound3D<ImplicitRef> (true));\n            interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DVP,\n                new OpPlaySoundVP3D<ImplicitRef> (true));\n            interpreter.installSegment5 (Compiler::Sound::opcodeStopSound, new OpStopSound<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Sound::opcodeGetSoundPlaying, new OpGetSoundPlaying<ImplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Sound::opcodeSayExplicit, new OpSay<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Sound::opcodeSayDoneExplicit, new OpSayDone<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DExplicit,\n                new OpPlaySound3D<ExplicitRef> (false));\n            interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DVPExplicit,\n                new OpPlaySoundVP3D<ExplicitRef> (false));\n            interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DExplicit,\n                new OpPlaySound3D<ExplicitRef> (true));\n            interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DVPExplicit,\n                new OpPlaySoundVP3D<ExplicitRef> (true));\n            interpreter.installSegment5 (Compiler::Sound::opcodeStopSoundExplicit, new OpStopSound<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Sound::opcodeGetSoundPlayingExplicit,\n                new OpGetSoundPlaying<ExplicitRef>);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/soundextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_SOUNDEXTENSIONS_H\n#define GAME_SCRIPT_SOUNDEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    namespace Sound\n    {\n        // Script-extensions related to sound        \n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n\n"
  },
  {
    "path": "apps/openmw/mwscript/statsextensions.cpp",
    "content": "#include \"statsextensions.hpp\"\n\n#include <cmath>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/loadnpc.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n\n#include <components/compiler/extensions.hpp>\n#include <components/compiler/opcodes.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n\n#include \"ref.hpp\"\n\nnamespace\n{\n    std::string getDialogueActorFaction(MWWorld::ConstPtr actor)\n    {\n        std::string factionId = actor.getClass().getPrimaryFaction(actor);\n        if (factionId.empty())\n            throw std::runtime_error (\n                \"failed to determine dialogue actors faction (because actor is factionless)\");\n\n        return factionId;\n    }\n}\n\nnamespace MWScript\n{\n    namespace Stats\n    {\n        template<class R>\n        class OpGetLevel : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer value =\n                        ptr.getClass()\n                            .getCreatureStats (ptr)\n                            .getLevel();\n\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpSetLevel : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    ptr.getClass()\n                        .getCreatureStats (ptr)\n                        .setLevel(value);\n                }\n        };\n\n        template<class R>\n        class OpGetAttribute : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpGetAttribute (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float value =\n                        ptr.getClass()\n                            .getCreatureStats (ptr)\n                            .getAttribute(mIndex)\n                            .getModified();\n\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpSetAttribute : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpSetAttribute (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float value = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex);\n                    attribute.setBase (value);\n                    ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute);\n                }\n        };\n\n        template<class R>\n        class OpModAttribute : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpModAttribute (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float value = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWMechanics::AttributeValue attribute = ptr.getClass()\n                        .getCreatureStats(ptr)\n                        .getAttribute(mIndex);\n\n                    if (value == 0)\n                        return;\n\n                    if (((attribute.getBase() <= 0) && (value < 0))\n                        || ((attribute.getBase() >= 100) && (value > 0)))\n                        return;\n\n                    if (value < 0)\n                        attribute.setBase(std::max(0.f, attribute.getBase() + value));\n                    else\n                        attribute.setBase(std::min(100.f, attribute.getBase() + value));\n\n                    ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute);\n                }\n        };\n\n        template<class R>\n        class OpGetDynamic : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpGetDynamic (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    Interpreter::Type_Float value;\n\n                    if (mIndex==0 && ptr.getClass().hasItemHealth (ptr))\n                    {\n                        // health is a special case\n                        value = static_cast<Interpreter::Type_Float>(ptr.getClass().getItemMaxHealth(ptr));\n                    } else {\n                        value =\n                            ptr.getClass()\n                                .getCreatureStats(ptr)\n                                .getDynamic(mIndex)\n                                .getCurrent();\n                        // GetMagicka shouldn't return negative values\n                        if(mIndex == 1 && value < 0)\n                            value = 0;\n                    }\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpSetDynamic : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpSetDynamic (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float value = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWMechanics::DynamicStat<float> stat (ptr.getClass().getCreatureStats (ptr)\n                        .getDynamic (mIndex));\n\n                    stat.setModified (value, 0);\n                    stat.setCurrent(value);\n\n                    ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat);\n                }\n        };\n\n        template<class R>\n        class OpModDynamic : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpModDynamic (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    int peek = R::implicit ? 0 : runtime[0].mInteger;\n\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float diff = runtime[0].mFloat;\n                    runtime.pop();\n\n                    // workaround broken endgame scripts that kill dagoth ur\n                    if (!R::implicit &&\n                        ::Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), \"dagoth_ur_1\"))\n                    {\n                        runtime.push (peek);\n\n                        if (R()(runtime, false, true).isEmpty())\n                        {\n                            Log(Debug::Warning)\n                                << \"Warning: Compensating for broken script in Morrowind.esm by \"\n                                << \"ignoring remote access to dagoth_ur_1\";\n\n                            return;\n                        }\n                    }\n\n                    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n\n                    Interpreter::Type_Float current = stats.getDynamic(mIndex).getCurrent();\n\n                    MWMechanics::DynamicStat<float> stat (ptr.getClass().getCreatureStats (ptr)\n                        .getDynamic (mIndex));\n\n                    stat.setModified (diff + stat.getModified(), 0);\n                    stat.setCurrentModified (diff + stat.getCurrentModified());\n\n                    stat.setCurrent (diff + current);\n\n                    ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat);\n                }\n        };\n\n        template<class R>\n        class OpModCurrentDynamic : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpModCurrentDynamic (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float diff = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n\n                    Interpreter::Type_Float current = stats.getDynamic(mIndex).getCurrent();\n\n                    MWMechanics::DynamicStat<float> stat (ptr.getClass().getCreatureStats (ptr)\n                        .getDynamic (mIndex));\n\n                    bool allowDecreaseBelowZero = false;\n                    if (mIndex == 2) // Fatigue-specific logic\n                    {\n                        // For fatigue, a negative current value is allowed and means the actor will be knocked down\n                        allowDecreaseBelowZero = true;\n                        // Knock down the actor immediately if a non-positive new value is the case\n                        if (diff + current <= 0.f)\n                            ptr.getClass().getCreatureStats(ptr).setKnockedDown(true);\n                    }\n                    stat.setCurrent (diff + current, allowDecreaseBelowZero);\n\n                    ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat);\n                }\n        };\n\n        template<class R>\n        class OpGetDynamicGetRatio : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpGetDynamicGetRatio (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);\n\n                    Interpreter::Type_Float value = 0;\n\n                    Interpreter::Type_Float max = stats.getDynamic(mIndex).getModified();\n\n                    if (max>0)\n                        value = stats.getDynamic(mIndex).getCurrent() / max;\n\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpGetSkill : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpGetSkill (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex);\n\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpSetSkill : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpSetSkill (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float value = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr);\n\n                    stats.getSkill (mIndex).setBase (value);\n                }\n        };\n\n        template<class R>\n        class OpModSkill : public Interpreter::Opcode0\n        {\n                int mIndex;\n\n            public:\n\n                OpModSkill (int index) : mIndex (index) {}\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float value = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWMechanics::SkillValue &skill = ptr.getClass()\n                        .getNpcStats(ptr)\n                        .getSkill(mIndex);\n\n                    if (value == 0)\n                        return;\n\n                    if (((skill.getBase() <= 0.f) && (value < 0.f))\n                        || ((skill.getBase() >= 100.f) && (value > 0.f)))\n                        return;\n\n                    if (value < 0)\n                        skill.setBase(std::max(0.f, skill.getBase() + value));\n                    else\n                        skill.setBase(std::min(100.f, skill.getBase() + value));\n                }\n        };\n\n        class OpGetPCCrimeLevel : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n                    MWWorld::Ptr player = world->getPlayerPtr();\n                    runtime.push (static_cast <Interpreter::Type_Float> (player.getClass().getNpcStats (player).getBounty()));\n                }\n        };\n\n        class OpSetPCCrimeLevel : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n                    MWWorld::Ptr player = world->getPlayerPtr();\n\n                    int bounty = static_cast<int>(runtime[0].mFloat);\n                    runtime.pop();\n                    player.getClass().getNpcStats (player).setBounty(bounty);\n\n                    if (bounty == 0)\n                        MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId();\n                }\n        };\n\n        class OpModPCCrimeLevel : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWBase::World *world = MWBase::Environment::get().getWorld();\n                    MWWorld::Ptr player = world->getPlayerPtr();\n\n                    player.getClass().getNpcStats(player).setBounty(static_cast<int>(runtime[0].mFloat) + player.getClass().getNpcStats(player).getBounty());\n                    runtime.pop();\n                }\n        };\n\n        template<class R>\n        class OpAddSpell : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string id = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (id);\n\n                    MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Only add the spell if the target doesn't already have it\n\n                        Send an ID_PLAYER_SPELLBOOK packet every time a player gains a spell here\n                    */\n                    MWMechanics::Spells &spells = creatureStats.getSpells();\n\n                    if (!spells.hasSpell(id))\n                    {\n                        spells.add(id);\n\n                        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && ptr == MWMechanics::getPlayer())\n                            mwmp::Main::get().getLocalPlayer()->sendSpellChange(id, mwmp::SpellbookChanges::ADD);\n                    }\n                    /*\n                        End of tes3mp change (major)\n                    */\n\n                    ESM::Spell::SpellType type = static_cast<ESM::Spell::SpellType>(spell->mData.mType);\n                    if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Power)\n                    {\n                        // Apply looping particles immediately for constant effects\n                        MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr);\n                    }\n                }\n        };\n\n        template<class R>\n        class OpRemoveSpell : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string id = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Only remove the spell if the target has it\n                    */\n                    MWMechanics::Spells& spells = creatureStats.getSpells();\n\n                    if (!spells.hasSpell(id)) return;\n                    /*\n                        End of tes3mp change (major)\n                    */\n                                        \n                    // The spell may have an instant effect which must be handled before the spell's removal.\n                    for (const auto& effect : creatureStats.getSpells().getMagicEffects())\n                    {\n                        if (effect.second.getMagnitude() <= 0)\n                            continue;\n                        MWMechanics::CastSpell cast(ptr, ptr);\n                        if (cast.applyInstantEffect(ptr, ptr, effect.first, effect.second.getMagnitude()))\n                            creatureStats.getSpells().purgeEffect(effect.first.mId);\n                    }\n\n                    MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, id);\n                    creatureStats.getSpells().remove (id);\n\n                    MWBase::WindowManager* wm = MWBase::Environment::get().getWindowManager();\n\n                    if (ptr == MWMechanics::getPlayer() &&\n                        id == wm->getSelectedSpell())\n                    {\n                        wm->unsetSelectedSpell();\n                    }\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Send an ID_PLAYER_SPELLBOOK packet every time a player loses a spell here\n                    */\n                    if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n                        mwmp::Main::get().getLocalPlayer()->sendSpellChange(id, mwmp::SpellbookChanges::REMOVE);\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n        };\n\n        template<class R>\n        class OpRemoveSpellEffects : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string spellid = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    ptr.getClass().getCreatureStats (ptr).getActiveSpells().removeEffects(spellid);\n                    ptr.getClass().getCreatureStats (ptr).getSpells().removeEffects(spellid);\n                }\n        };\n\n        template<class R>\n        class OpRemoveEffects : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer effectId = runtime[0].mInteger;\n                    runtime.pop();\n\n                    ptr.getClass().getCreatureStats (ptr).getActiveSpells().purgeEffect(effectId);\n                }\n        };\n\n        template<class R>\n        class OpGetSpell : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string id = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer value = 0;\n\n                    if (ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getSpells().hasSpell(id))\n                        value = 1;\n\n                    runtime.push (value);\n                }\n        };\n\n        template<class R>\n        class OpPCJoinFaction : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr actor = R()(runtime, false);\n\n                    std::string factionID = \"\";\n\n                    if(arg0==0)\n                    {\n                        factionID = getDialogueActorFaction(actor);\n                    }\n                    else\n                    {\n                        factionID = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    ::Misc::StringUtils::lowerCaseInPlace(factionID);\n                    // Make sure this faction exists\n                    MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID);\n\n                    if(factionID != \"\")\n                    {\n                        MWWorld::Ptr player = MWMechanics::getPlayer();\n                        player.getClass().getNpcStats(player).joinFaction(factionID);\n\n                        /*\n                            Start of tes3mp addition\n\n                            Send an ID_PLAYER_FACTION packet every time a player joins a faction\n                        */\n                        int newRank = player.getClass().getNpcStats(player).getFactionRanks().at(factionID);\n                        mwmp::Main::get().getLocalPlayer()->sendFactionRank(factionID, newRank);\n                        /*\n                            End of tes3mp addition\n                        */\n                    }\n                }\n        };\n\n        template<class R>\n        class OpPCRaiseRank : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr actor = R()(runtime, false);\n\n                    std::string factionID = \"\";\n\n                    if(arg0==0)\n                    {\n                        factionID = getDialogueActorFaction(actor);\n                    }\n                    else\n                    {\n                        factionID = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    ::Misc::StringUtils::lowerCaseInPlace(factionID);\n                    // Make sure this faction exists\n                    MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID);\n\n                    if(factionID != \"\")\n                    {\n                        MWWorld::Ptr player = MWMechanics::getPlayer();\n                        if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) == player.getClass().getNpcStats(player).getFactionRanks().end())\n                        {\n                            player.getClass().getNpcStats(player).joinFaction(factionID);\n                        }\n                        else\n                        {\n                            player.getClass().getNpcStats(player).raiseRank(factionID);\n                        }\n\n                        /*\n                            Start of tes3mp addition\n\n                            Send an ID_PLAYER_FACTION packet every time a player rises in a faction\n                        */\n                        int newRank = player.getClass().getNpcStats(player).getFactionRanks().at(factionID);\n                        mwmp::Main::get().getLocalPlayer()->sendFactionRank(factionID, newRank);\n                        /*\n                            End of tes3mp addition\n                        */\n                    }\n                }\n        };\n\n        template<class R>\n        class OpPCLowerRank : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr actor = R()(runtime, false);\n\n                    std::string factionID = \"\";\n\n                    if(arg0==0)\n                    {\n                        factionID = getDialogueActorFaction(actor);\n                    }\n                    else\n                    {\n                        factionID = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    ::Misc::StringUtils::lowerCaseInPlace(factionID);\n                    // Make sure this faction exists\n                    MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID);\n\n                    if(factionID != \"\")\n                    {\n                        MWWorld::Ptr player = MWMechanics::getPlayer();\n                        player.getClass().getNpcStats(player).lowerRank(factionID);\n\n                        /*\n                            Start of tes3mp addition\n\n                            Send an ID_PLAYER_FACTION packet every time a player falls in a faction\n                        */\n                        int newRank = player.getClass().getNpcStats(player).getFactionRanks().at(factionID);\n                        mwmp::Main::get().getLocalPlayer()->sendFactionRank(factionID, newRank);\n                        /*\n                            End of tes3mp addition\n                        */\n                    }\n                }\n        };\n\n        template<class R>\n        class OpGetPCRank : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr ptr = R()(runtime, false);\n\n                    std::string factionID = \"\";\n                    if(arg0 >0)\n                    {\n                        factionID = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    else\n                    {\n                        factionID = ptr.getClass().getPrimaryFaction(ptr);\n                    }\n                    ::Misc::StringUtils::lowerCaseInPlace(factionID);\n                    // Make sure this faction exists\n                    MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID);\n\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n                    if(factionID!=\"\")\n                    {\n                        if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end())\n                        {\n                            runtime.push(player.getClass().getNpcStats(player).getFactionRanks().at(factionID));\n                        }\n                        else\n                        {\n                            runtime.push(-1);\n                        }\n                    }\n                    else\n                    {\n                        runtime.push(-1);\n                    }\n                }\n        };\n\n        template<class R>\n        class OpModDisposition : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (ptr.getClass().isNpc())\n                        ptr.getClass().getNpcStats (ptr).setBaseDisposition\n                            (ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value);\n\n                    // else: must not throw exception (used by an Almalexia dialogue script)\n                }\n        };\n\n        template<class R>\n        class OpSetDisposition : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (ptr.getClass().isNpc())\n                        ptr.getClass().getNpcStats (ptr).setBaseDisposition (value);\n                }\n        };\n\n        template<class R>\n        class OpGetDisposition : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (!ptr.getClass().isNpc())\n                        runtime.push(0);\n                    else\n                        runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr));\n                }\n        };\n\n        class OpGetDeadCount : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string id = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime[0].mInteger = MWBase::Environment::get().getMechanicsManager()->countDeaths (id);\n                }\n        };\n\n        template<class R>\n        class OpGetPCFacRep : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr ptr = R()(runtime, false);\n\n                    std::string factionId;\n\n                    if (arg0==1)\n                    {\n                        factionId = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    else\n                    {\n                        factionId = getDialogueActorFaction(ptr);\n                    }\n\n                    if (factionId.empty())\n                        throw std::runtime_error (\"failed to determine faction\");\n\n                    ::Misc::StringUtils::lowerCaseInPlace (factionId);\n\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n                    runtime.push (\n                        player.getClass().getNpcStats (player).getFactionReputation (factionId));\n                }\n        };\n\n        template<class R>\n        class OpSetPCFacRep : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr ptr = R()(runtime, false);\n\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    std::string factionId;\n\n                    if (arg0==1)\n                    {\n                        factionId = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    else\n                    {\n                        factionId = getDialogueActorFaction(ptr);\n                    }\n\n                    if (factionId.empty())\n                        throw std::runtime_error (\"failed to determine faction\");\n\n                    ::Misc::StringUtils::lowerCaseInPlace (factionId);\n\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n                    player.getClass().getNpcStats (player).setFactionReputation (factionId, value);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_PLAYER_FACTION packet every time a player's faction reputation changes\n                    */\n                    mwmp::Main::get().getLocalPlayer()->sendFactionReputation(Misc::StringUtils::lowerCase(factionId), value);\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template<class R>\n        class OpModPCFacRep : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr ptr = R()(runtime, false);\n\n                    Interpreter::Type_Integer value = runtime[0].mInteger;\n                    runtime.pop();\n\n                    std::string factionId;\n\n                    if (arg0==1)\n                    {\n                        factionId = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    else\n                    {\n                        factionId = getDialogueActorFaction(ptr);\n                    }\n\n                    if (factionId.empty())\n                        throw std::runtime_error (\"failed to determine faction\");\n\n                    ::Misc::StringUtils::lowerCaseInPlace (factionId);\n\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n                    player.getClass().getNpcStats (player).setFactionReputation (factionId,\n                        player.getClass().getNpcStats (player).getFactionReputation (factionId)+\n                        value);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_PLAYER_FACTION packet every time a player's faction reputation changes\n                    */\n                    int newReputation = player.getClass().getNpcStats(player).getFactionReputation(factionId);\n                    mwmp::Main::get().getLocalPlayer()->sendFactionReputation(Misc::StringUtils::lowerCase(factionId), newReputation);\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template<class R>\n        class OpGetCommonDisease : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push (ptr.getClass().getCreatureStats (ptr).hasCommonDisease());\n                }\n        };\n\n        template<class R>\n        class OpGetBlightDisease : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.push (ptr.getClass().getCreatureStats (ptr).hasBlightDisease());\n                }\n        };\n\n        template<class R>\n        class OpGetRace : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::ConstPtr ptr = R()(runtime);\n\n                    std::string race = runtime.getStringLiteral(runtime[0].mInteger);\n                    ::Misc::StringUtils::lowerCaseInPlace(race);\n                    runtime.pop();\n\n                    std::string npcRace = ptr.get<ESM::NPC>()->mBase->mRace;\n                    ::Misc::StringUtils::lowerCaseInPlace(npcRace);\n\n                    runtime.push (npcRace == race);\n            }\n        };\n\n        class OpGetWerewolfKills : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n\n                    runtime.push (ptr.getClass().getNpcStats (ptr).getWerewolfKills ());\n                }\n        };\n\n        template <class R>\n        class OpPcExpelled : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr ptr = R()(runtime, false);\n\n                    std::string factionID = \"\";\n                    if(arg0 >0 )\n                    {\n                        factionID = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    else\n                    {\n                        factionID = ptr.getClass().getPrimaryFaction(ptr);\n                    }\n                    ::Misc::StringUtils::lowerCaseInPlace(factionID);\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n                    if(factionID!=\"\")\n                    {\n                        runtime.push(player.getClass().getNpcStats(player).getExpelled(factionID));\n                    }\n                    else\n                    {\n                        runtime.push(0);\n                    }\n                }\n        };\n\n        template <class R>\n        class OpPcExpell : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr ptr = R()(runtime, false);\n\n                    std::string factionID = \"\";\n                    if(arg0 >0 )\n                    {\n                        factionID = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    else\n                    {\n                        factionID = ptr.getClass().getPrimaryFaction(ptr);\n                    }\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n                    if(factionID!=\"\")\n                    {\n                        player.getClass().getNpcStats(player).expell(factionID);\n\n                        /*\n                            Start of tes3mp addition\n\n                            Send an ID_PLAYER_FACTION packet every time a player is expelled from a faction\n                        */\n                        mwmp::Main::get().getLocalPlayer()->sendFactionExpulsionState(Misc::StringUtils::lowerCase(factionID), true);\n                        /*\n                            End of tes3mp addition\n                        */\n                    }\n                }\n        };\n\n        template <class R>\n        class OpPcClearExpelled : public Interpreter::Opcode1\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime, unsigned int arg0) override\n                {\n                    MWWorld::ConstPtr ptr = R()(runtime, false);\n\n                    std::string factionID = \"\";\n                    if(arg0 >0 )\n                    {\n                        factionID = runtime.getStringLiteral (runtime[0].mInteger);\n                        runtime.pop();\n                    }\n                    else\n                    {\n                        factionID = ptr.getClass().getPrimaryFaction(ptr);\n                    }\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n                    if(factionID!=\"\")\n                        player.getClass().getNpcStats(player).clearExpelled(factionID);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_PLAYER_FACTION packet every time a player is no longer expelled from a faction\n                    */\n                    if (factionID != \"\")\n                        mwmp::Main::get().getLocalPlayer()->sendFactionExpulsionState(Misc::StringUtils::lowerCase(factionID), false);\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template <class R>\n        class OpRaiseRank : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string factionID = ptr.getClass().getPrimaryFaction(ptr);\n                    if(factionID.empty())\n                        return;\n\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n\n                    // no-op when executed on the player\n                    if (ptr == player)\n                        return;\n\n                    // If we already changed rank for this NPC, modify current rank in the NPC stats.\n                    // Otherwise take rank from base NPC record, increase it and put it to NPC data.\n                    int currentRank = ptr.getClass().getNpcStats(ptr).getFactionRank(factionID);\n                    if (currentRank >= 0)\n                        ptr.getClass().getNpcStats(ptr).raiseRank(factionID);\n                    else\n                    {\n                        int rank = ptr.getClass().getPrimaryFactionRank(ptr);\n                        rank++;\n                        ptr.getClass().getNpcStats(ptr).joinFaction(factionID);\n                        for (int i=0; i<rank; i++)\n                            ptr.getClass().getNpcStats(ptr).raiseRank(factionID);\n                    }\n                }\n        };\n\n        template <class R>\n        class OpLowerRank : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string factionID = ptr.getClass().getPrimaryFaction(ptr);\n                    if(factionID.empty())\n                        return;\n\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n\n                    // no-op when executed on the player\n                    if (ptr == player)\n                        return;\n\n                    // If we already changed rank for this NPC, modify current rank in the NPC stats.\n                    // Otherwise take rank from base NPC record, decrease it and put it to NPC data.\n                    int currentRank = ptr.getClass().getNpcStats(ptr).getFactionRank(factionID);\n                    if (currentRank == 0)\n                        return;\n                    else if (currentRank > 0)\n                        ptr.getClass().getNpcStats(ptr).lowerRank(factionID);\n                    else\n                    {\n                        int rank = ptr.getClass().getPrimaryFactionRank(ptr);\n                        rank--;\n                        ptr.getClass().getNpcStats(ptr).joinFaction(factionID);\n                        for (int i=0; i<rank; i++)\n                            ptr.getClass().getNpcStats(ptr).raiseRank(factionID);\n                    }\n                }\n        };\n\n        template <class R>\n        class OpOnDeath : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer value =\n                        ptr.getClass().getCreatureStats (ptr).hasDied();\n\n                    if (value)\n                        ptr.getClass().getCreatureStats (ptr).clearHasDied();\n\n                    runtime.push (value);\n                }\n        };\n\n        template <class R>\n        class OpOnMurder : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer value =\n                        ptr.getClass().getCreatureStats (ptr).hasBeenMurdered();\n\n                    if (value)\n                        ptr.getClass().getCreatureStats (ptr).clearHasBeenMurdered();\n\n                    runtime.push (value);\n                }\n        };\n\n        template <class R>\n        class OpOnKnockout : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Integer value =\n                        ptr.getClass().getCreatureStats (ptr).getKnockedDownOneFrame();\n\n                    runtime.push (value);\n                }\n        };\n\n        template <class R>\n        class OpIsWerewolf : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    runtime.push(ptr.getClass().getNpcStats(ptr).isWerewolf());\n                }\n        };\n\n        template <class R, bool set>\n        class OpSetWerewolf : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, set);\n\n                    /*\n                        Start of tes3mp addition\n\n                        When the player's werewolf state changes, send an ID_PLAYER_SHAPESHIFT packet\n                    */\n                    if (ptr == MWMechanics::getPlayer())\n                        mwmp::Main::get().getLocalPlayer()->sendWerewolfState(set);\n                    /*\n                        End of tes3mp addition\n                    */\n                }\n        };\n\n        template <class R>\n        class OpSetWerewolfAcrobatics : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    MWBase::Environment::get().getMechanicsManager()->applyWerewolfAcrobatics(ptr);\n                }\n        };\n\n        template <class R>\n        class OpResurrect : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (ptr == MWMechanics::getPlayer())\n                    {\n                        MWBase::Environment::get().getMechanicsManager()->resurrect(ptr);\n                        if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Ended)\n                            MWBase::Environment::get().getStateManager()->resumeGame();\n                    }\n                    else if (ptr.getClass().getCreatureStats(ptr).isDead())\n                    {\n                        bool wasEnabled = ptr.getRefData().isEnabled();\n                        MWBase::Environment::get().getWorld()->undeleteObject(ptr);\n                        MWBase::Environment::get().getWorld()->removeContainerScripts(ptr);\n\n                        // HACK: disable/enable object to re-add it to the scene properly (need a new Animation).\n                        MWBase::Environment::get().getWorld()->disable(ptr);\n                        // resets runtime state such as inventory, stats and AI. does not reset position in the world\n                        ptr.getRefData().setCustomData(nullptr);\n                        if (wasEnabled)\n                            MWBase::Environment::get().getWorld()->enable(ptr);\n                    }\n                }\n        };\n\n        template <class R>\n        class OpGetStat : public Interpreter::Opcode0\n        {\n        public:\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                // dummy\n                runtime.push(0);\n            }\n        };\n\n        template <class R>\n        class OpGetMagicEffect : public Interpreter::Opcode0\n        {\n            int mPositiveEffect;\n            int mNegativeEffect;\n\n        public:\n            OpGetMagicEffect (int positiveEffect, int negativeEffect)\n                : mPositiveEffect(positiveEffect)\n                , mNegativeEffect(negativeEffect)\n            {\n            }\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWWorld::Ptr ptr = R()(runtime);\n\n                const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects();\n                float currentValue = effects.get(mPositiveEffect).getMagnitude();\n                if (mNegativeEffect != -1)\n                    currentValue -= effects.get(mNegativeEffect).getMagnitude();\n\n                // GetResist* should take in account elemental shields\n                if (mPositiveEffect == ESM::MagicEffect::ResistFire)\n                    currentValue += effects.get(ESM::MagicEffect::FireShield).getMagnitude();\n                if (mPositiveEffect == ESM::MagicEffect::ResistShock)\n                    currentValue += effects.get(ESM::MagicEffect::LightningShield).getMagnitude();\n                if (mPositiveEffect == ESM::MagicEffect::ResistFrost)\n                    currentValue += effects.get(ESM::MagicEffect::FrostShield).getMagnitude();\n\n                int ret = static_cast<int>(currentValue);\n                runtime.push(ret);\n            }\n        };\n\n        template <class R>\n        class OpSetMagicEffect : public Interpreter::Opcode0\n        {\n            int mPositiveEffect;\n            int mNegativeEffect;\n\n        public:\n            OpSetMagicEffect (int positiveEffect, int negativeEffect)\n                : mPositiveEffect(positiveEffect)\n                , mNegativeEffect(negativeEffect)\n            {\n            }\n\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                MWWorld::Ptr ptr = R()(runtime);\n                MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects();\n                float currentValue = effects.get(mPositiveEffect).getMagnitude();\n                if (mNegativeEffect != -1)\n                    currentValue -= effects.get(mNegativeEffect).getMagnitude();\n\n                // SetResist* should take in account elemental shields\n                if (mPositiveEffect == ESM::MagicEffect::ResistFire)\n                    currentValue += effects.get(ESM::MagicEffect::FireShield).getMagnitude();\n                if (mPositiveEffect == ESM::MagicEffect::ResistShock)\n                    currentValue += effects.get(ESM::MagicEffect::LightningShield).getMagnitude();\n                if (mPositiveEffect == ESM::MagicEffect::ResistFrost)\n                    currentValue += effects.get(ESM::MagicEffect::FrostShield).getMagnitude();\n\n                int arg = runtime[0].mInteger;\n                runtime.pop();\n                effects.modifyBase(mPositiveEffect, (arg - static_cast<int>(currentValue)));\n            }\n        };\n\n        template <class R>\n        class OpModMagicEffect : public Interpreter::Opcode0\n        {\n            int mPositiveEffect;\n            int mNegativeEffect;\n\n        public:\n            OpModMagicEffect (int positiveEffect, int negativeEffect)\n                : mPositiveEffect(positiveEffect)\n                , mNegativeEffect(negativeEffect)\n            {\n            }\n\n            void execute(Interpreter::Runtime &runtime) override\n            {\n                MWWorld::Ptr ptr = R()(runtime);\n                MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);\n\n                int arg = runtime[0].mInteger;\n                runtime.pop();\n                stats.getMagicEffects().modifyBase(mPositiveEffect, arg);\n            }\n        };\n\n        struct MagicEffect\n        {\n            int mPositiveEffect;\n            int mNegativeEffect;\n        };\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            for (int i=0; i<Compiler::Stats::numberOfAttributes; ++i)\n            {\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetAttribute+i, new OpGetAttribute<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetAttributeExplicit+i,\n                    new OpGetAttribute<ExplicitRef> (i));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeSetAttribute+i, new OpSetAttribute<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeSetAttributeExplicit+i,\n                    new OpSetAttribute<ExplicitRef> (i));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeModAttribute+i, new OpModAttribute<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeModAttributeExplicit+i,\n                    new OpModAttribute<ExplicitRef> (i));\n            }\n\n            for (int i=0; i<Compiler::Stats::numberOfDynamics; ++i)\n            {\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamic+i, new OpGetDynamic<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicExplicit+i,\n                    new OpGetDynamic<ExplicitRef> (i));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeSetDynamic+i, new OpSetDynamic<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeSetDynamicExplicit+i,\n                    new OpSetDynamic<ExplicitRef> (i));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeModDynamic+i, new OpModDynamic<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeModDynamicExplicit+i,\n                    new OpModDynamic<ExplicitRef> (i));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeModCurrentDynamic+i,\n                    new OpModCurrentDynamic<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeModCurrentDynamicExplicit+i,\n                    new OpModCurrentDynamic<ExplicitRef> (i));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicGetRatio+i,\n                    new OpGetDynamicGetRatio<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicGetRatioExplicit+i,\n                    new OpGetDynamicGetRatio<ExplicitRef> (i));\n            }\n\n            for (int i=0; i<Compiler::Stats::numberOfSkills; ++i)\n            {\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetSkill+i, new OpGetSkill<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetSkillExplicit+i, new OpGetSkill<ExplicitRef> (i));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeSetSkill+i, new OpSetSkill<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeSetSkillExplicit+i, new OpSetSkill<ExplicitRef> (i));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeModSkill+i, new OpModSkill<ImplicitRef> (i));\n                interpreter.installSegment5 (Compiler::Stats::opcodeModSkillExplicit+i, new OpModSkill<ExplicitRef> (i));\n            }\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetPCCrimeLevel, new OpGetPCCrimeLevel);\n            interpreter.installSegment5 (Compiler::Stats::opcodeSetPCCrimeLevel, new OpSetPCCrimeLevel);\n            interpreter.installSegment5 (Compiler::Stats::opcodeModPCCrimeLevel, new OpModPCCrimeLevel);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeAddSpell, new OpAddSpell<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeAddSpellExplicit, new OpAddSpell<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpell, new OpRemoveSpell<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellExplicit,\n                new OpRemoveSpell<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffects, new OpRemoveSpellEffects<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffectsExplicit,\n                new OpRemoveSpellEffects<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeResurrect, new OpResurrect<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeResurrectExplicit,\n                new OpResurrect<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffects, new OpRemoveEffects<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffectsExplicit,\n                new OpRemoveEffects<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetSpell, new OpGetSpell<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetSpellExplicit, new OpGetSpell<ExplicitRef>);\n\n            interpreter.installSegment3(Compiler::Stats::opcodePCRaiseRank,new OpPCRaiseRank<ImplicitRef>);\n            interpreter.installSegment3(Compiler::Stats::opcodePCLowerRank,new OpPCLowerRank<ImplicitRef>);\n            interpreter.installSegment3(Compiler::Stats::opcodePCJoinFaction,new OpPCJoinFaction<ImplicitRef>);\n            interpreter.installSegment3(Compiler::Stats::opcodePCRaiseRankExplicit,new OpPCRaiseRank<ExplicitRef>);\n            interpreter.installSegment3(Compiler::Stats::opcodePCLowerRankExplicit,new OpPCLowerRank<ExplicitRef>);\n            interpreter.installSegment3(Compiler::Stats::opcodePCJoinFactionExplicit,new OpPCJoinFaction<ExplicitRef>);\n            interpreter.installSegment3(Compiler::Stats::opcodeGetPCRank,new OpGetPCRank<ImplicitRef>);\n            interpreter.installSegment3(Compiler::Stats::opcodeGetPCRankExplicit,new OpGetPCRank<ExplicitRef>);\n\n            interpreter.installSegment5(Compiler::Stats::opcodeModDisposition,new OpModDisposition<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Stats::opcodeModDispositionExplicit,new OpModDisposition<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Stats::opcodeSetDisposition,new OpSetDisposition<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Stats::opcodeSetDispositionExplicit,new OpSetDisposition<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Stats::opcodeGetDisposition,new OpGetDisposition<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Stats::opcodeGetDispositionExplicit,new OpGetDisposition<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetLevel, new OpGetLevel<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetLevelExplicit, new OpGetLevel<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeSetLevel, new OpSetLevel<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeSetLevelExplicit, new OpSetLevel<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetDeadCount, new OpGetDeadCount);\n\n            interpreter.installSegment3 (Compiler::Stats::opcodeGetPCFacRep, new OpGetPCFacRep<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodeGetPCFacRepExplicit, new OpGetPCFacRep<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodeSetPCFacRep, new OpSetPCFacRep<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodeSetPCFacRepExplicit, new OpSetPCFacRep<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodeModPCFacRep, new OpModPCFacRep<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodeModPCFacRepExplicit, new OpModPCFacRep<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetCommonDisease, new OpGetCommonDisease<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetCommonDiseaseExplicit, new OpGetCommonDisease<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetBlightDisease, new OpGetBlightDisease<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetBlightDiseaseExplicit, new OpGetBlightDisease<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetRace, new OpGetRace<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetRaceExplicit, new OpGetRace<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetWerewolfKills, new OpGetWerewolfKills);\n\n            interpreter.installSegment3 (Compiler::Stats::opcodePcExpelled, new OpPcExpelled<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodePcExpelledExplicit, new OpPcExpelled<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodePcExpell, new OpPcExpell<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodePcExpellExplicit, new OpPcExpell<ExplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodePcClearExpelled, new OpPcClearExpelled<ImplicitRef>);\n            interpreter.installSegment3 (Compiler::Stats::opcodePcClearExpelledExplicit, new OpPcClearExpelled<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeRaiseRank, new OpRaiseRank<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeRaiseRankExplicit, new OpRaiseRank<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeLowerRank, new OpLowerRank<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeLowerRankExplicit, new OpLowerRank<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeOnMurder, new OpOnMurder<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeOnMurderExplicit, new OpOnMurder<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockout, new OpOnKnockout<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeIsWerewolf, new OpIsWerewolf<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeIsWerewolfExplicit, new OpIsWerewolf<ExplicitRef>);\n\n            interpreter.installSegment5 (Compiler::Stats::opcodeBecomeWerewolf, new OpSetWerewolf<ImplicitRef, true>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeBecomeWerewolfExplicit, new OpSetWerewolf<ExplicitRef, true>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolf, new OpSetWerewolf<ImplicitRef, false>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolfExplicit, new OpSetWerewolf<ExplicitRef, false>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobatics, new OpSetWerewolfAcrobatics<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobaticsExplicit, new OpSetWerewolfAcrobatics<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetStat, new OpGetStat<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::Stats::opcodeGetStatExplicit, new OpGetStat<ExplicitRef>);\n\n            static const MagicEffect sMagicEffects[] = {\n                { ESM::MagicEffect::ResistMagicka, ESM::MagicEffect::WeaknessToMagicka },\n                { ESM::MagicEffect::ResistFire, ESM::MagicEffect::WeaknessToFire },\n                { ESM::MagicEffect::ResistFrost, ESM::MagicEffect::WeaknessToFrost },\n                { ESM::MagicEffect::ResistShock, ESM::MagicEffect::WeaknessToShock },\n                { ESM::MagicEffect::ResistCommonDisease, ESM::MagicEffect::WeaknessToCommonDisease },\n                { ESM::MagicEffect::ResistBlightDisease, ESM::MagicEffect::WeaknessToBlightDisease },\n                { ESM::MagicEffect::ResistCorprusDisease, ESM::MagicEffect::WeaknessToCorprusDisease },\n                { ESM::MagicEffect::ResistPoison, ESM::MagicEffect::WeaknessToPoison },\n                { ESM::MagicEffect::ResistParalysis, -1 },\n                { ESM::MagicEffect::ResistNormalWeapons, ESM::MagicEffect::WeaknessToNormalWeapons },\n                { ESM::MagicEffect::WaterBreathing, -1 },\n                { ESM::MagicEffect::Chameleon, -1 },\n                { ESM::MagicEffect::WaterWalking, -1 },\n                { ESM::MagicEffect::SwiftSwim, -1 },\n                { ESM::MagicEffect::Jump, -1 },\n                { ESM::MagicEffect::Levitate, -1 },\n                { ESM::MagicEffect::Shield, -1 },\n                { ESM::MagicEffect::Sound, -1 },\n                { ESM::MagicEffect::Silence, -1 },\n                { ESM::MagicEffect::Blind, -1 },\n                { ESM::MagicEffect::Paralyze, -1 },\n                { ESM::MagicEffect::Invisibility, -1 },\n                { ESM::MagicEffect::FortifyAttack, -1 },\n                { ESM::MagicEffect::Sanctuary, -1 },\n            };\n\n            for (int i=0; i<24; ++i)\n            {\n                int positive = sMagicEffects[i].mPositiveEffect;\n                int negative = sMagicEffects[i].mNegativeEffect;\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetMagicEffect+i, new OpGetMagicEffect<ImplicitRef> (positive, negative));\n                interpreter.installSegment5 (Compiler::Stats::opcodeGetMagicEffectExplicit+i, new OpGetMagicEffect<ExplicitRef> (positive, negative));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeSetMagicEffect+i, new OpSetMagicEffect<ImplicitRef> (positive, negative));\n                interpreter.installSegment5 (Compiler::Stats::opcodeSetMagicEffectExplicit+i, new OpSetMagicEffect<ExplicitRef> (positive, negative));\n\n                interpreter.installSegment5 (Compiler::Stats::opcodeModMagicEffect+i, new OpModMagicEffect<ImplicitRef> (positive, negative));\n                interpreter.installSegment5 (Compiler::Stats::opcodeModMagicEffectExplicit+i, new OpModMagicEffect<ExplicitRef> (positive, negative));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/statsextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_STATSEXTENSIONS_H\n#define GAME_SCRIPT_STATSEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief stats-related script functionality (creatures and NPCs)\n    namespace Stats\n    {        \n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/transformationextensions.cpp",
    "content": "#include <components/debug/debuglog.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/ScriptController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n#include <components/esm/loadcell.hpp>\n\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/player.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"interpretercontext.hpp\"\n#include \"ref.hpp\"\n\nnamespace MWScript\n{\n    namespace Transformation\n    {\n        void moveStandingActors(const MWWorld::Ptr &ptr, const osg::Vec3f& diff)\n        {\n            std::vector<MWWorld::Ptr> actors;\n            MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors);\n            for (auto& actor : actors)\n                MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff, false, false);\n        }\n\n        template<class R>\n        class OpGetDistance : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr from = R()(runtime);\n                    std::string name = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if (from.getContainerStore()) // is the object contained?\n                    {\n                        MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(from);\n\n                        if (!container.isEmpty())\n                            from = container;\n                        else\n                        {\n                            std::string error = \"Failed to find the container of object '\" + from.getCellRef().getRefId() + \"'\";\n                            runtime.getContext().report(error);\n                            Log(Debug::Error) << error;\n                            runtime.push(0.f);\n                            return;\n                        }\n                    }\n\n                    const MWWorld::Ptr to = MWBase::Environment::get().getWorld()->searchPtr(name, false);\n                    if (to.isEmpty())\n                    {\n                        std::string error = \"Failed to find an instance of object '\" + name + \"'\";\n                        runtime.getContext().report(error);\n                        Log(Debug::Error) << error;\n                        runtime.push(0.f);\n                        return;\n                    }\n\n                    float distance;\n                    // If the objects are in different worldspaces, return a large value (just like vanilla)\n                    if (!to.isInCell() || !from.isInCell() || to.getCell()->getCell()->getCellId().mWorldspace != from.getCell()->getCell()->getCellId().mWorldspace)\n                        distance = std::numeric_limits<float>::max();\n                    else\n                    {\n                        double diff[3];\n\n                        const float* const pos1 = to.getRefData().getPosition().pos;\n                        const float* const pos2 = from.getRefData().getPosition().pos;\n                        for (int i=0; i<3; ++i)\n                            diff[i] = pos1[i] - pos2[i];\n\n                        distance = static_cast<float>(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]));\n                    }\n\n                    runtime.push(distance);\n                }\n        };\n\n        template<class R>\n        class OpSetScale : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float scale = runtime[0].mFloat;\n                    runtime.pop();\n\n                    /*\n                        Start of tes3mp addition\n\n                        Prevent players from changing their own scale\n\n                        Send an ID_OBJECT_SCALE every time an object's scale is changed through a script\n                    */\n                    if (ptr == MWMechanics::getPlayer())\n                    {\n                        MWBase::Environment::get().getWindowManager()->\n                            messageBox(\"You can't change your own scale in multiplayer. Only the server can.\");\n                    }\n                    else if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && ptr.isInCell() && ptr.getCellRef().getScale() != scale)\n                    {\n                        // Ignore attempts to change another player's scale\n                        if (mwmp::PlayerList::isDedicatedPlayer(ptr))\n                        {\n                            MWBase::Environment::get().getWindowManager()->\n                                messageBox(\"You can't change the scales of other players. Only the server can.\");\n                        }\n                        else\n                        {\n                            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                            objectList->reset();\n                            objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                            objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n                            objectList->addObjectScale(ptr, scale);\n                            objectList->sendObjectScale();\n                        }\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Disable unilateral scaling on this client and expect the server's reply to our\n                        packet to do it instead\n                    */\n                    //MWBase::Environment::get().getWorld()->scaleObject(ptr,scale);\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n        };\n\n        template<class R>\n        class OpGetScale : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n                    runtime.push(ptr.getCellRef().getScale());\n                }\n        };\n\n        template<class R>\n        class OpModScale : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    Interpreter::Type_Float scale = runtime[0].mFloat;\n                    runtime.pop();\n\n                    // add the parameter to the object's scale.\n                    MWBase::Environment::get().getWorld()->scaleObject(ptr,ptr.getCellRef().getScale() + scale);\n                }\n        };\n\n        template<class R>\n        class OpSetAngle : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    Interpreter::Type_Float angle = osg::DegreesToRadians(runtime[0].mFloat);\n                    runtime.pop();\n\n                    float ax = ptr.getRefData().getPosition().rot[0];\n                    float ay = ptr.getRefData().getPosition().rot[1];\n                    float az = ptr.getRefData().getPosition().rot[2];\n\n                    // XYZ axis use the inverse (XYZ) rotation order like vanilla SetAngle.\n                    // UWV axis use the standard (ZYX) rotation order like TESCS/OpenMW-CS and the rest of the game.\n                    if (axis == \"x\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az,MWBase::RotationFlag_inverseOrder);\n                    else if (axis == \"y\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az,MWBase::RotationFlag_inverseOrder);\n                    else if (axis == \"z\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle,MWBase::RotationFlag_inverseOrder);\n                    else if (axis == \"u\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az,MWBase::RotationFlag_none);\n                    else if (axis == \"w\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az,MWBase::RotationFlag_none);\n                    else if (axis == \"v\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle,MWBase::RotationFlag_none);\n                }\n        };\n\n        template<class R>\n        class OpGetStartingAngle : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if (axis == \"x\")\n                    {\n                        runtime.push(osg::RadiansToDegrees(ptr.getCellRef().getPosition().rot[0]));\n                    }\n                    else if (axis == \"y\")\n                    {\n                        runtime.push(osg::RadiansToDegrees(ptr.getCellRef().getPosition().rot[1]));\n                    }\n                    else if (axis == \"z\")\n                    {\n                        runtime.push(osg::RadiansToDegrees(ptr.getCellRef().getPosition().rot[2]));\n                    }\n                }\n        };\n\n        template<class R>\n        class OpGetAngle : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if (axis==\"x\")\n                    {\n                        runtime.push(osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]));\n                    }\n                    else if (axis==\"y\")\n                    {\n                        runtime.push(osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]));\n                    }\n                    else if (axis==\"z\")\n                    {\n                        runtime.push(osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[2]));\n                    }\n                }\n        };\n\n        template<class R>\n        class OpGetPos : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if(axis == \"x\")\n                    {\n                        runtime.push(ptr.getRefData().getPosition().pos[0]);\n                    }\n                    else if(axis == \"y\")\n                    {\n                        runtime.push(ptr.getRefData().getPosition().pos[1]);\n                    }\n                    else if(axis == \"z\")\n                    {\n                        runtime.push(ptr.getRefData().getPosition().pos[2]);\n                    }\n                }\n        };\n\n        template<class R>\n        class OpSetPos : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (!ptr.isInCell())\n                        return;\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    Interpreter::Type_Float pos = runtime[0].mFloat;\n                    runtime.pop();\n\n                    // Note: SetPos does not skip weather transitions in vanilla engine, so we do not call setTeleported(true) here.\n\n                    const auto curPos = ptr.getRefData().getPosition().asVec3();\n                    auto newPos = curPos;\n                    if(axis == \"x\")\n                    {\n                        newPos[0] = pos;\n                    }\n                    else if(axis == \"y\")\n                    {\n                        newPos[1] = pos;\n                    }\n                    else if(axis == \"z\")\n                    {\n                        // We should not place actors under ground\n                        if (ptr.getClass().isActor())\n                        {\n                            float terrainHeight = -std::numeric_limits<float>::max();\n                            if (ptr.getCell()->isExterior())\n                                terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos);\n \n                            if (pos < terrainHeight)\n                                pos = terrainHeight;\n                        }\n \n                        newPos[2] = pos;\n                    }\n                    else\n                    {\n                        return;\n                    }\n\n                    dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,\n                        MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos, true, true));\n                }\n        };\n\n        template<class R>\n        class OpGetStartingPos : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    if(axis == \"x\")\n                    {\n                        runtime.push(ptr.getCellRef().getPosition().pos[0]);\n                    }\n                    else if(axis == \"y\")\n                    {\n                        runtime.push(ptr.getCellRef().getPosition().pos[1]);\n                    }\n                    else if(axis == \"z\")\n                    {\n                        runtime.push(ptr.getCellRef().getPosition().pos[2]);\n                    }\n                }\n        };\n\n        template<class R>\n        class OpPositionCell : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (ptr.getContainerStore())\n                        return;\n\n                    if (ptr == MWMechanics::getPlayer())\n                    {\n                        MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true);\n                    }\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float zRot = runtime[0].mFloat;\n                    runtime.pop();\n                    std::string cellID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    MWWorld::CellStore* store = nullptr;\n                    try\n                    {\n                        store = MWBase::Environment::get().getWorld()->getInterior(cellID);\n                    }\n                    catch(std::exception&)\n                    {\n                        // cell not found, move to exterior instead (vanilla PositionCell compatibility)\n                        const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID);\n                        int cx,cy;\n                        MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy);\n                        store = MWBase::Environment::get().getWorld()->getExterior(cx,cy);\n                        if(!cell)\n                        {\n                            std::string error = \"Warning: PositionCell: unknown interior cell (\" + cellID + \"), moving to exterior instead\";\n                            runtime.getContext().report (error);\n                            Log(Debug::Warning) << error;\n                        }\n                    }\n                    if(store)\n                    {\n                        /*\n                            Start of tes3mp addition\n\n                            Track the original cell of this object in case we need to use it when sending a packet\n                        */\n                        ESM::Cell originalCell = *ptr.getCell()->getCell();\n                        /*\n                            End of tes3mp addition\n                        */\n\n                        MWWorld::Ptr base = ptr;\n                        ptr = MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z);\n                        dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);\n\n                        /*\n                            Start of tes3mp addition\n\n                            Send ActorCellChange packets when actors are moved here, regardless of whether we're\n                            the cell authority or not; the server can decide if it wants to comply with them\n                        */\n                        if (ptr.getClass().isActor() && !mwmp::Main::get().getCellController()->isSameCell(originalCell, *store->getCell()))\n                        {\n                            mwmp::BaseActor baseActor;\n                            baseActor.refNum = ptr.getCellRef().getRefNum().mIndex;\n                            baseActor.mpNum = ptr.getCellRef().getMpNum();\n                            baseActor.cell = *store->getCell();\n                            baseActor.position = ptr.getRefData().getPosition();\n                            baseActor.isFollowerCellChange = true;\n\n                            mwmp::ActorList* actorList = mwmp::Main::get().getNetworking()->getActorList();\n                            actorList->reset();\n                            actorList->cell = originalCell;\n\n                            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_ACTOR_CELL_CHANGE about %s %i-%i to server\",\n                                ptr.getCellRef().getRefId().c_str(), baseActor.refNum, baseActor.mpNum);\n\n                            LOG_APPEND(TimedLog::LOG_INFO, \"- Moved from %s to %s\", actorList->cell.getDescription().c_str(),\n                                baseActor.cell.getDescription().c_str());\n\n                            actorList->addCellChangeActor(baseActor);\n                            actorList->sendCellChangeActors();\n                        }\n                        /*\n                            End of tes3mp addition\n                        */\n\n                        float ax = ptr.getRefData().getPosition().rot[0];\n                        float ay = ptr.getRefData().getPosition().rot[1];\n                        // Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)\n                        // except for when you position the player, then degrees must be used.\n                        // See \"Morrowind Scripting for Dummies (9th Edition)\" pages 50 and 54 for reference.\n                        if(ptr != MWMechanics::getPlayer())\n                            zRot = zRot/60.0f;\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,osg::DegreesToRadians(zRot));\n\n                        ptr.getClass().adjustPosition(ptr, false);\n                    }\n                }\n        };\n\n        template<class R>\n        class OpPosition : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (!ptr.isInCell())\n                        return;\n\n                    if (ptr == MWMechanics::getPlayer())\n                    {\n                        MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true);\n                    }\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float zRot = runtime[0].mFloat;\n                    runtime.pop();\n                    int cx,cy;\n                    MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy);\n\n                    // another morrowind oddity: player will be moved to the exterior cell at this location,\n                    // non-player actors will move within the cell they are in.\n                    MWWorld::Ptr base = ptr;\n                    if (ptr == MWMechanics::getPlayer())\n                    {\n                        MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(cx,cy);\n                        ptr = MWBase::Environment::get().getWorld()->moveObject(ptr,cell,x,y,z);\n                    }\n                    else\n                    {\n                        ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true, true);\n                    }\n                    dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);\n\n                    float ax = ptr.getRefData().getPosition().rot[0];\n                    float ay = ptr.getRefData().getPosition().rot[1];\n                    // Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)\n                    // except for when you position the player, then degrees must be used.\n                    // See \"Morrowind Scripting for Dummies (9th Edition)\" pages 50 and 54 for reference.\n                    if(ptr != MWMechanics::getPlayer())\n                        zRot = zRot/60.0f;\n                    MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,osg::DegreesToRadians(zRot));\n                    ptr.getClass().adjustPosition(ptr, false);\n                }\n        };\n\n        class OpPlaceItemCell : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string itemID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    std::string cellID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float zRotDegrees = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWWorld::CellStore* store = nullptr;\n                    try\n                    {\n                        store = MWBase::Environment::get().getWorld()->getInterior(cellID);\n                    }\n                    catch(std::exception&)\n                    {\n                        const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID);\n                        int cx,cy;\n                        MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy);\n                        store = MWBase::Environment::get().getWorld()->getExterior(cx,cy);\n                        if(!cell)\n                        {\n                            runtime.getContext().report (\"unknown cell (\" + cellID + \")\");\n                            Log(Debug::Error) << \"Error: unknown cell (\" << cellID << \")\";\n                        }\n                    }\n                    if(store)\n                    {\n                        ESM::Position pos;\n                        pos.pos[0] = x;\n                        pos.pos[1] = y;\n                        pos.pos[2] = z;\n                        pos.rot[0] = pos.rot[1] = 0;\n                        pos.rot[2] = osg::DegreesToRadians(zRotDegrees);\n                        MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);\n                        ref.getPtr().getCellRef().setPosition(pos);\n                        MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(),store,pos);\n                        placed.getClass().adjustPosition(placed, true);\n\n                        /*\n                            Start of tes3mp addition\n\n                            Send an ID_OBJECT_PLACE or ID_OBJECT_SPAWN packet every time an object is placed\n                            in the world through a script\n                        */\n                        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n                        {\n                            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                            objectList->reset();\n                            objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                            objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n\n                            if (placed.getClass().isActor())\n                            {\n                                objectList->addObjectSpawn(placed);\n                                objectList->sendObjectSpawn();\n                            }\n                            else\n                            {\n                                objectList->addObjectPlace(placed);\n                                objectList->sendObjectPlace();\n                            }\n                        }\n                        /*\n                            End of tes3mp addition\n                        */\n\n                        /*\n                            Start of tes3mp change (major)\n\n                            Instead of actually keeping this object as is, delete it after sending the packet\n                            and wait for the server to send it back with a unique mpNum of its own\n                        */\n                        MWBase::Environment::get().getWorld()->deleteObject(placed);\n                        /*\n                            End of tes3mp change (major)\n                        */\n                    }\n                }\n        };\n\n        class OpPlaceItem : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    std::string itemID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Float x = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float y = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float z = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Float zRotDegrees = runtime[0].mFloat;\n                    runtime.pop();\n\n                    MWWorld::Ptr player = MWMechanics::getPlayer();\n\n                    if (!player.isInCell())\n                        throw std::runtime_error(\"player not in a cell\");\n\n                    MWWorld::CellStore* store = nullptr;\n                    if (player.getCell()->isExterior())\n                    {\n                        int cx,cy;\n                        MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy);\n                        store = MWBase::Environment::get().getWorld()->getExterior(cx,cy);\n                    }\n                    else\n                        store = player.getCell();\n\n                    ESM::Position pos;\n                    pos.pos[0] = x;\n                    pos.pos[1] = y;\n                    pos.pos[2] = z;\n                    pos.rot[0] = pos.rot[1] = 0;\n                    pos.rot[2] = osg::DegreesToRadians(zRotDegrees);\n                    MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);\n                    ref.getPtr().getCellRef().setPosition(pos);\n                    MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(),store,pos);\n                    placed.getClass().adjustPosition(placed, true);\n\n                    /*\n                        Start of tes3mp addition\n\n                        Send an ID_OBJECT_PLACE or ID_OBJECT_SPAWN packet every time an object is placed\n                        in the world through a script\n                    */\n                    if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n                    {\n                        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                        objectList->reset();\n                        objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                        objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n\n                        if (placed.getClass().isActor())\n                        {\n                            objectList->addObjectSpawn(placed);\n                            objectList->sendObjectSpawn();\n                        }\n                        else\n                        {\n                            objectList->addObjectPlace(placed);\n                            objectList->sendObjectPlace();\n                        }\n                    }\n                    /*\n                        End of tes3mp addition\n                    */\n\n                    /*\n                        Start of tes3mp change (major)\n\n                        Instead of actually keeping this object as is, delete it after sending the packet\n                        and wait for the server to send it back with a unique mpNum of its own\n                    */\n                    MWBase::Environment::get().getWorld()->deleteObject(placed);\n                    /*\n                        End of tes3mp change (major)\n                    */\n                }\n        };\n\n        template<class R, bool pc>\n        class OpPlaceAt : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr actor = pc\n                        ? MWMechanics::getPlayer()\n                        : R()(runtime);\n\n                    std::string itemID = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n\n                    Interpreter::Type_Integer count = runtime[0].mInteger;\n                    runtime.pop();\n                    Interpreter::Type_Float distance = runtime[0].mFloat;\n                    runtime.pop();\n                    Interpreter::Type_Integer direction = runtime[0].mInteger;\n                    runtime.pop();\n\n                    if (direction < 0 || direction > 3)\n                        throw std::runtime_error (\"invalid direction\");\n\n                    if (count<0)\n                        throw std::runtime_error (\"count must be non-negative\");\n\n                    if (!actor.isInCell())\n                        throw std::runtime_error (\"actor is not in a cell\");\n\n                    for (int i=0; i<count; ++i)\n                    {\n                        // create item\n                        MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, 1);\n\n                        MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), actor, actor.getCell(), direction, distance);\n                        MWBase::Environment::get().getWorld()->scaleObject(ptr, actor.getCellRef().getScale());\n\n                        /*\n                            Start of tes3mp addition\n\n                            Send an ID_OBJECT_PLACE or ID_OBJECT_SPAWN packet every time an object is placed\n                            in the world through a script\n                        */\n                        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n                        {\n                            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                            objectList->reset();\n                            objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());\n                            objectList->originClientScript = runtime.getContext().getCurrentScriptName();\n\n                            if (ptr.getClass().isActor())\n                            {\n                                objectList->addObjectSpawn(ptr);\n                                objectList->sendObjectSpawn();\n                            }\n                            else\n                            {\n                                objectList->addObjectPlace(ptr);\n                                objectList->sendObjectPlace();\n                            }\n                        }\n                        /*\n                            End of tes3mp addition\n                        */\n\n                        /*\n                            Start of tes3mp change (major)\n\n                            Instead of actually keeping this object as is, delete it after sending the packet\n                            and wait for the server to send it back with a unique mpNum of its own\n                        */\n                        MWBase::Environment::get().getWorld()->deleteObject(ptr);\n                        /*\n                            End of tes3mp change (major)\n                        */\n                    }\n                }\n        };\n\n        template<class R>\n        class OpRotate : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    const MWWorld::Ptr& ptr = R()(runtime);\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    Interpreter::Type_Float rotation = osg::DegreesToRadians(runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());\n                    runtime.pop();\n\n                    float ax = ptr.getRefData().getPosition().rot[0];\n                    float ay = ptr.getRefData().getPosition().rot[1];\n                    float az = ptr.getRefData().getPosition().rot[2];\n\n                    if (axis == \"x\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,ax+rotation,ay,az);\n                    else if (axis == \"y\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay+rotation,az);\n                    else if (axis == \"z\")\n                        MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,az+rotation);\n                }\n        };\n\n        template<class R>\n        class OpRotateWorld : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    Interpreter::Type_Float rotation = osg::DegreesToRadians(runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());\n                    runtime.pop();\n\n                    if (!ptr.getRefData().getBaseNode())\n                        return;\n\n                    // We can rotate actors only around Z axis\n                    if (ptr.getClass().isActor() && (axis == \"x\" || axis == \"y\"))\n                        return;\n\n                    osg::Quat rot;\n                    if (axis == \"x\")\n                        rot = osg::Quat(rotation, -osg::X_AXIS);\n                    else if (axis == \"y\")\n                        rot = osg::Quat(rotation, -osg::Y_AXIS);\n                    else if (axis == \"z\")\n                        rot = osg::Quat(rotation, -osg::Z_AXIS);\n                    else\n                        return;\n\n                    osg::Quat attitude = ptr.getRefData().getBaseNode()->getAttitude();\n                    MWBase::Environment::get().getWorld()->rotateWorldObject(ptr, attitude * rot);\n                }\n        };\n\n        template<class R>\n        class OpSetAtStart : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (!ptr.isInCell())\n                        return;\n\n                    float xr = ptr.getCellRef().getPosition().rot[0];\n                    float yr = ptr.getCellRef().getPosition().rot[1];\n                    float zr = ptr.getCellRef().getPosition().rot[2];\n\n                    MWBase::Environment::get().getWorld()->rotateObject(ptr, xr, yr, zr);\n\n                    dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,\n                        MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0],\n                            ptr.getCellRef().getPosition().pos[1], ptr.getCellRef().getPosition().pos[2]));\n                }\n        };\n\n        template<class R>\n        class OpMove : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    const MWWorld::Ptr& ptr = R()(runtime);\n\n                    if (!ptr.isInCell())\n                        return;\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());\n                    runtime.pop();\n\n                    osg::Vec3f posChange;\n                    if (axis == \"x\")\n                    {\n                        posChange=osg::Vec3f(movement, 0, 0);\n                    }\n                    else if (axis == \"y\")\n                    {\n                        posChange=osg::Vec3f(0, movement, 0);\n                    }\n                    else if (axis == \"z\")\n                    {\n                        posChange=osg::Vec3f(0, 0, movement);\n                    }\n                    else\n                        return;\n\n                    // is it correct that disabled objects can't be Move-d?\n                    if (!ptr.getRefData().getBaseNode())\n                        return;\n\n                    osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange;\n\n                    // We should move actors, standing on moving object, too.\n                    // This approach can be used to create elevators.\n                    moveStandingActors(ptr, diff);\n                    dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,\n                        MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false, true));\n                }\n        };\n\n        template<class R>\n        class OpMoveWorld : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    MWWorld::Ptr ptr = R()(runtime);\n\n                    if (!ptr.isInCell())\n                        return;\n\n                    std::string axis = runtime.getStringLiteral (runtime[0].mInteger);\n                    runtime.pop();\n                    Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());\n                    runtime.pop();\n\n                    osg::Vec3f diff;\n\n                    if (axis == \"x\")\n                        diff.x() = movement;\n                    else if (axis == \"y\")\n                        diff.y() = movement;\n                    else if (axis == \"z\")\n                        diff.z() = movement;\n                    else\n                        return;\n\n                    // We should move actors, standing on moving object, too.\n                    // This approach can be used to create elevators.\n                    moveStandingActors(ptr, diff);\n                    dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,\n                        MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false, true));\n                }\n        };\n\n        class OpResetActors : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWBase::Environment::get().getWorld()->resetActors();\n            }\n        };\n\n        class OpFixme : public Interpreter::Opcode0\n        {\n        public:\n\n            void execute (Interpreter::Runtime& runtime) override\n            {\n                MWBase::Environment::get().getWorld()->fixPosition();\n            }\n        };\n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetDistance, new OpGetDistance<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetDistanceExplicit, new OpGetDistance<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeSetScale,new OpSetScale<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeSetScaleExplicit,new OpSetScale<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeSetAngle,new OpSetAngle<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeSetAngleExplicit,new OpSetAngle<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetScale,new OpGetScale<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetScaleExplicit,new OpGetScale<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetAngle,new OpGetAngle<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetAngleExplicit,new OpGetAngle<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetPos,new OpGetPos<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetPosExplicit,new OpGetPos<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeSetPos,new OpSetPos<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeSetPosExplicit,new OpSetPos<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingPos,new OpGetStartingPos<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingPosExplicit,new OpGetStartingPos<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodePosition,new OpPosition<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodePositionExplicit,new OpPosition<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodePositionCell,new OpPositionCell<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodePositionCellExplicit,new OpPositionCell<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell);\n            interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem);\n            interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAt<ImplicitRef, true>);\n            interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAt<ImplicitRef, false>);\n            interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAt<ExplicitRef, false>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeModScale,new OpModScale<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeModScaleExplicit,new OpModScale<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeRotate,new OpRotate<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeRotateExplicit,new OpRotate<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeRotateWorld,new OpRotateWorld<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeRotateWorldExplicit,new OpRotateWorld<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeSetAtStart,new OpSetAtStart<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeSetAtStartExplicit,new OpSetAtStart<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeMove,new OpMove<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeMoveExplicit,new OpMove<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeMoveWorld,new OpMoveWorld<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeMoveWorldExplicit,new OpMoveWorld<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngle, new OpGetStartingAngle<ImplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngleExplicit, new OpGetStartingAngle<ExplicitRef>);\n            interpreter.installSegment5(Compiler::Transformation::opcodeResetActors, new OpResetActors);\n            interpreter.installSegment5(Compiler::Transformation::opcodeFixme, new OpFixme);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/transformationextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_TRANSFORMATIONEXTENSIONS_H\n#define GAME_SCRIPT_TRANSFORMATIONEXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief stats-related script functionality (creatures and NPCs)\n    namespace Transformation\n    {        \n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwscript/userextensions.cpp",
    "content": "#include \"userextensions.hpp\"\n\n#include <components/compiler/extensions.hpp>\n#include <components/compiler/opcodes.hpp>\n\n#include <components/interpreter/interpreter.hpp>\n#include <components/interpreter/runtime.hpp>\n#include <components/interpreter/opcodes.hpp>\n#include <components/interpreter/context.hpp>\n\n#include \"ref.hpp\"\n\nnamespace MWScript\n{\n    /// Temporary script extensions.\n    ///\n    /// \\attention Do not commit changes to this file to a git repository!\n    namespace User\n    {\n        class OpUser1 : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.getContext().report (\"user1: not in use\");\n                }\n        };\n\n        class OpUser2 : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n                    runtime.getContext().report (\"user2: not in use\");\n                }\n        };\n\n        template<class R>\n        class OpUser3 : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n//                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.getContext().report (\"user3: not in use\");\n                }\n        };\n\n        template<class R>\n        class OpUser4 : public Interpreter::Opcode0\n        {\n            public:\n\n                void execute (Interpreter::Runtime& runtime) override\n                {\n//                    MWWorld::Ptr ptr = R()(runtime);\n\n                    runtime.getContext().report (\"user4: not in use\");\n                }\n        };\n        \n\n        void installOpcodes (Interpreter::Interpreter& interpreter)\n        {\n            interpreter.installSegment5 (Compiler::User::opcodeUser1, new OpUser1);\n            interpreter.installSegment5 (Compiler::User::opcodeUser2, new OpUser2);\n            interpreter.installSegment5 (Compiler::User::opcodeUser3, new OpUser3<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::User::opcodeUser3Explicit, new OpUser3<ExplicitRef>);\n            interpreter.installSegment5 (Compiler::User::opcodeUser4, new OpUser4<ImplicitRef>);\n            interpreter.installSegment5 (Compiler::User::opcodeUser4Explicit, new OpUser4<ExplicitRef>);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwscript/userextensions.hpp",
    "content": "#ifndef GAME_SCRIPT_USEREXTENSIONS_H\n#define GAME_SCRIPT_USEREXTENSIONS_H\n\nnamespace Compiler\n{\n    class Extensions;\n}\n\nnamespace Interpreter\n{\n    class Interpreter;\n}\n\nnamespace MWScript\n{\n    /// \\brief Temporary script functionality limited to the console\n    namespace User\n    {\n        void installOpcodes (Interpreter::Interpreter& interpreter);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/alext.h",
    "content": "/**\n * OpenAL cross platform audio library\n * Copyright (C) 2008 by authors.\n * This library is free software; you can redistribute it and/or\n *  modify it under the terms of the GNU Library General Public\n *  License as published by the Free Software Foundation; either\n *  version 2 of the License, or (at your option) any later version.\n *\n * This library is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *  Library General Public License for more details.\n *\n * You should have received a copy of the GNU Library General Public\n *  License along with this library; if not, write to the\n *  Free Software Foundation, Inc.,\n *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n * Or go to https://www.gnu.org/copyleft/lgpl.html\n */\n\n#ifndef AL_ALEXT_H\n#define AL_ALEXT_H\n\n#include <stddef.h>\n/* Define int64_t and uint64_t types */\n#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L\n#include <inttypes.h>\n#elif defined(_WIN32) && defined(__GNUC__)\n#include <stdint.h>\n#elif defined(_WIN32)\ntypedef __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n#else\n/* Fallback if nothing above works */\n#include <inttypes.h>\n#endif\n\n#include \"alc.h\"\n#include \"al.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef AL_LOKI_IMA_ADPCM_format\n#define AL_LOKI_IMA_ADPCM_format 1\n#define AL_FORMAT_IMA_ADPCM_MONO16_EXT           0x10000\n#define AL_FORMAT_IMA_ADPCM_STEREO16_EXT         0x10001\n#endif\n\n#ifndef AL_LOKI_WAVE_format\n#define AL_LOKI_WAVE_format 1\n#define AL_FORMAT_WAVE_EXT                       0x10002\n#endif\n\n#ifndef AL_EXT_vorbis\n#define AL_EXT_vorbis 1\n#define AL_FORMAT_VORBIS_EXT                     0x10003\n#endif\n\n#ifndef AL_LOKI_quadriphonic\n#define AL_LOKI_quadriphonic 1\n#define AL_FORMAT_QUAD8_LOKI                     0x10004\n#define AL_FORMAT_QUAD16_LOKI                    0x10005\n#endif\n\n#ifndef AL_EXT_float32\n#define AL_EXT_float32 1\n#define AL_FORMAT_MONO_FLOAT32                   0x10010\n#define AL_FORMAT_STEREO_FLOAT32                 0x10011\n#endif\n\n#ifndef AL_EXT_double\n#define AL_EXT_double 1\n#define AL_FORMAT_MONO_DOUBLE_EXT                0x10012\n#define AL_FORMAT_STEREO_DOUBLE_EXT              0x10013\n#endif\n\n#ifndef AL_EXT_MULAW\n#define AL_EXT_MULAW 1\n#define AL_FORMAT_MONO_MULAW_EXT                 0x10014\n#define AL_FORMAT_STEREO_MULAW_EXT               0x10015\n#endif\n\n#ifndef AL_EXT_ALAW\n#define AL_EXT_ALAW 1\n#define AL_FORMAT_MONO_ALAW_EXT                  0x10016\n#define AL_FORMAT_STEREO_ALAW_EXT                0x10017\n#endif\n\n#ifndef ALC_LOKI_audio_channel\n#define ALC_LOKI_audio_channel 1\n#define ALC_CHAN_MAIN_LOKI                       0x500001\n#define ALC_CHAN_PCM_LOKI                        0x500002\n#define ALC_CHAN_CD_LOKI                         0x500003\n#endif\n\n#ifndef AL_EXT_MCFORMATS\n#define AL_EXT_MCFORMATS 1\n#define AL_FORMAT_QUAD8                          0x1204\n#define AL_FORMAT_QUAD16                         0x1205\n#define AL_FORMAT_QUAD32                         0x1206\n#define AL_FORMAT_REAR8                          0x1207\n#define AL_FORMAT_REAR16                         0x1208\n#define AL_FORMAT_REAR32                         0x1209\n#define AL_FORMAT_51CHN8                         0x120A\n#define AL_FORMAT_51CHN16                        0x120B\n#define AL_FORMAT_51CHN32                        0x120C\n#define AL_FORMAT_61CHN8                         0x120D\n#define AL_FORMAT_61CHN16                        0x120E\n#define AL_FORMAT_61CHN32                        0x120F\n#define AL_FORMAT_71CHN8                         0x1210\n#define AL_FORMAT_71CHN16                        0x1211\n#define AL_FORMAT_71CHN32                        0x1212\n#endif\n\n#ifndef AL_EXT_MULAW_MCFORMATS\n#define AL_EXT_MULAW_MCFORMATS 1\n#define AL_FORMAT_MONO_MULAW                     0x10014\n#define AL_FORMAT_STEREO_MULAW                   0x10015\n#define AL_FORMAT_QUAD_MULAW                     0x10021\n#define AL_FORMAT_REAR_MULAW                     0x10022\n#define AL_FORMAT_51CHN_MULAW                    0x10023\n#define AL_FORMAT_61CHN_MULAW                    0x10024\n#define AL_FORMAT_71CHN_MULAW                    0x10025\n#endif\n\n#ifndef AL_EXT_IMA4\n#define AL_EXT_IMA4 1\n#define AL_FORMAT_MONO_IMA4                      0x1300\n#define AL_FORMAT_STEREO_IMA4                    0x1301\n#endif\n\n#ifndef AL_EXT_STATIC_BUFFER\n#define AL_EXT_STATIC_BUFFER 1\ntypedef ALvoid (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei);\n#ifdef AL_ALEXT_PROTOTYPES\nAL_API ALvoid AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq);\n#endif\n#endif\n\n#ifndef ALC_EXT_EFX\n#define ALC_EXT_EFX 1\n#include \"efx.h\"\n#endif\n\n#ifndef ALC_EXT_disconnect\n#define ALC_EXT_disconnect 1\n#define ALC_CONNECTED                            0x313\n#endif\n\n#ifndef ALC_EXT_thread_local_context\n#define ALC_EXT_thread_local_context 1\ntypedef ALCboolean  (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context);\ntypedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void);\n#ifdef AL_ALEXT_PROTOTYPES\nALC_API ALCboolean  ALC_APIENTRY alcSetThreadContext(ALCcontext *context);\nALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void);\n#endif\n#endif\n\n#ifndef AL_EXT_source_distance_model\n#define AL_EXT_source_distance_model 1\n#define AL_SOURCE_DISTANCE_MODEL                 0x200\n#endif\n\n#ifndef AL_SOFT_buffer_sub_data\n#define AL_SOFT_buffer_sub_data 1\n#define AL_BYTE_RW_OFFSETS_SOFT                  0x1031\n#define AL_SAMPLE_RW_OFFSETS_SOFT                0x1032\ntypedef ALvoid (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei);\n#ifdef AL_ALEXT_PROTOTYPES\nAL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length);\n#endif\n#endif\n\n#ifndef AL_SOFT_loop_points\n#define AL_SOFT_loop_points 1\n#define AL_LOOP_POINTS_SOFT                      0x2015\n#endif\n\n#ifndef AL_EXT_FOLDBACK\n#define AL_EXT_FOLDBACK 1\n#define AL_EXT_FOLDBACK_NAME                     \"AL_EXT_FOLDBACK\"\n#define AL_FOLDBACK_EVENT_BLOCK                  0x4112\n#define AL_FOLDBACK_EVENT_START                  0x4111\n#define AL_FOLDBACK_EVENT_STOP                   0x4113\n#define AL_FOLDBACK_MODE_MONO                    0x4101\n#define AL_FOLDBACK_MODE_STEREO                  0x4102\ntypedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei);\ntypedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK);\ntypedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void);\n#ifdef AL_ALEXT_PROTOTYPES\nAL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback);\nAL_API void AL_APIENTRY alRequestFoldbackStop(void);\n#endif\n#endif\n\n#ifndef ALC_EXT_DEDICATED\n#define ALC_EXT_DEDICATED 1\n#define AL_DEDICATED_GAIN                        0x0001\n#define AL_EFFECT_DEDICATED_DIALOGUE             0x9001\n#define AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT 0x9000\n#endif\n\n#ifndef AL_SOFT_buffer_samples\n#define AL_SOFT_buffer_samples 1\n/* Channel configurations */\n#define AL_MONO_SOFT                             0x1500\n#define AL_STEREO_SOFT                           0x1501\n#define AL_REAR_SOFT                             0x1502\n#define AL_QUAD_SOFT                             0x1503\n#define AL_5POINT1_SOFT                          0x1504\n#define AL_6POINT1_SOFT                          0x1505\n#define AL_7POINT1_SOFT                          0x1506\n\n/* Sample types */\n#define AL_BYTE_SOFT                             0x1400\n#define AL_UNSIGNED_BYTE_SOFT                    0x1401\n#define AL_SHORT_SOFT                            0x1402\n#define AL_UNSIGNED_SHORT_SOFT                   0x1403\n#define AL_INT_SOFT                              0x1404\n#define AL_UNSIGNED_INT_SOFT                     0x1405\n#define AL_FLOAT_SOFT                            0x1406\n#define AL_DOUBLE_SOFT                           0x1407\n#define AL_BYTE3_SOFT                            0x1408\n#define AL_UNSIGNED_BYTE3_SOFT                   0x1409\n\n/* Storage formats */\n#define AL_MONO8_SOFT                            0x1100\n#define AL_MONO16_SOFT                           0x1101\n#define AL_MONO32F_SOFT                          0x10010\n#define AL_STEREO8_SOFT                          0x1102\n#define AL_STEREO16_SOFT                         0x1103\n#define AL_STEREO32F_SOFT                        0x10011\n#define AL_QUAD8_SOFT                            0x1204\n#define AL_QUAD16_SOFT                           0x1205\n#define AL_QUAD32F_SOFT                          0x1206\n#define AL_REAR8_SOFT                            0x1207\n#define AL_REAR16_SOFT                           0x1208\n#define AL_REAR32F_SOFT                          0x1209\n#define AL_5POINT1_8_SOFT                        0x120A\n#define AL_5POINT1_16_SOFT                       0x120B\n#define AL_5POINT1_32F_SOFT                      0x120C\n#define AL_6POINT1_8_SOFT                        0x120D\n#define AL_6POINT1_16_SOFT                       0x120E\n#define AL_6POINT1_32F_SOFT                      0x120F\n#define AL_7POINT1_8_SOFT                        0x1210\n#define AL_7POINT1_16_SOFT                       0x1211\n#define AL_7POINT1_32F_SOFT                      0x1212\n\n/* Buffer attributes */\n#define AL_INTERNAL_FORMAT_SOFT                  0x2008\n#define AL_BYTE_LENGTH_SOFT                      0x2009\n#define AL_SAMPLE_LENGTH_SOFT                    0x200A\n#define AL_SEC_LENGTH_SOFT                       0x200B\n\ntypedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*);\ntypedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*);\ntypedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*);\ntypedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum);\n#ifdef AL_ALEXT_PROTOTYPES\nAL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);\nAL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);\nAL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data);\nAL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format);\n#endif\n#endif\n\n#ifndef AL_SOFT_direct_channels\n#define AL_SOFT_direct_channels 1\n#define AL_DIRECT_CHANNELS_SOFT                  0x1033\n#endif\n\n#ifndef ALC_SOFT_loopback\n#define ALC_SOFT_loopback 1\n#define ALC_FORMAT_CHANNELS_SOFT                 0x1990\n#define ALC_FORMAT_TYPE_SOFT                     0x1991\n\n/* Sample types */\n#define ALC_BYTE_SOFT                            0x1400\n#define ALC_UNSIGNED_BYTE_SOFT                   0x1401\n#define ALC_SHORT_SOFT                           0x1402\n#define ALC_UNSIGNED_SHORT_SOFT                  0x1403\n#define ALC_INT_SOFT                             0x1404\n#define ALC_UNSIGNED_INT_SOFT                    0x1405\n#define ALC_FLOAT_SOFT                           0x1406\n\n/* Channel configurations */\n#define ALC_MONO_SOFT                            0x1500\n#define ALC_STEREO_SOFT                          0x1501\n#define ALC_QUAD_SOFT                            0x1503\n#define ALC_5POINT1_SOFT                         0x1504\n#define ALC_6POINT1_SOFT                         0x1505\n#define ALC_7POINT1_SOFT                         0x1506\n\ntypedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*);\ntypedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum);\ntypedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei);\n#ifdef AL_ALEXT_PROTOTYPES\nALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName);\nALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type);\nALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);\n#endif\n#endif\n\n#ifndef AL_EXT_STEREO_ANGLES\n#define AL_EXT_STEREO_ANGLES 1\n#define AL_STEREO_ANGLES                         0x1030\n#endif\n\n#ifndef AL_EXT_SOURCE_RADIUS\n#define AL_EXT_SOURCE_RADIUS 1\n#define AL_SOURCE_RADIUS                         0x1031\n#endif\n\n#ifndef AL_SOFT_source_latency\n#define AL_SOFT_source_latency 1\n#define AL_SAMPLE_OFFSET_LATENCY_SOFT            0x1200\n#define AL_SEC_OFFSET_LATENCY_SOFT               0x1201\ntypedef int64_t ALint64SOFT;\ntypedef uint64_t ALuint64SOFT;\ntypedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble);\ntypedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble);\ntypedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*);\ntypedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*);\ntypedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*);\ntypedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*);\ntypedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT);\ntypedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT);\ntypedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*);\ntypedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*);\ntypedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*);\ntypedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*);\n#ifdef AL_ALEXT_PROTOTYPES\nAL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value);\nAL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3);\nAL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values);\nAL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value);\nAL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3);\nAL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values);\nAL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value);\nAL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3);\nAL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values);\nAL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value);\nAL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3);\nAL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values);\n#endif\n#endif\n\n#ifndef ALC_EXT_DEFAULT_FILTER_ORDER\n#define ALC_EXT_DEFAULT_FILTER_ORDER 1\n#define ALC_DEFAULT_FILTER_ORDER                 0x1100\n#endif\n\n#ifndef AL_SOFT_deferred_updates\n#define AL_SOFT_deferred_updates 1\n#define AL_DEFERRED_UPDATES_SOFT                 0xC002\ntypedef ALvoid (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void);\ntypedef ALvoid (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void);\n#ifdef AL_ALEXT_PROTOTYPES\nAL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void);\nAL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void);\n#endif\n#endif\n\n#ifndef AL_SOFT_block_alignment\n#define AL_SOFT_block_alignment 1\n#define AL_UNPACK_BLOCK_ALIGNMENT_SOFT           0x200C\n#define AL_PACK_BLOCK_ALIGNMENT_SOFT             0x200D\n#endif\n\n#ifndef AL_SOFT_MSADPCM\n#define AL_SOFT_MSADPCM 1\n#define AL_FORMAT_MONO_MSADPCM_SOFT              0x1302\n#define AL_FORMAT_STEREO_MSADPCM_SOFT            0x1303\n#endif\n\n#ifndef AL_SOFT_source_length\n#define AL_SOFT_source_length 1\n/*#define AL_BYTE_LENGTH_SOFT                      0x2009*/\n/*#define AL_SAMPLE_LENGTH_SOFT                    0x200A*/\n/*#define AL_SEC_LENGTH_SOFT                       0x200B*/\n#endif\n\n#ifndef ALC_SOFT_pause_device\n#define ALC_SOFT_pause_device 1\ntypedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device);\ntypedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device);\n#ifdef AL_ALEXT_PROTOTYPES\nALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device);\nALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);\n#endif\n#endif\n\n#ifndef AL_EXT_BFORMAT\n#define AL_EXT_BFORMAT 1\n#define AL_FORMAT_BFORMAT2D_8                    0x20021\n#define AL_FORMAT_BFORMAT2D_16                   0x20022\n#define AL_FORMAT_BFORMAT2D_FLOAT32              0x20023\n#define AL_FORMAT_BFORMAT3D_8                    0x20031\n#define AL_FORMAT_BFORMAT3D_16                   0x20032\n#define AL_FORMAT_BFORMAT3D_FLOAT32              0x20033\n#endif\n\n#ifndef AL_EXT_MULAW_BFORMAT\n#define AL_EXT_MULAW_BFORMAT 1\n#define AL_FORMAT_BFORMAT2D_MULAW                0x10031\n#define AL_FORMAT_BFORMAT3D_MULAW                0x10032\n#endif\n\n#ifndef ALC_SOFT_HRTF\n#define ALC_SOFT_HRTF 1\n#define ALC_HRTF_SOFT                            0x1992\n#define ALC_DONT_CARE_SOFT                       0x0002\n#define ALC_HRTF_STATUS_SOFT                     0x1993\n#define ALC_HRTF_DISABLED_SOFT                   0x0000\n#define ALC_HRTF_ENABLED_SOFT                    0x0001\n#define ALC_HRTF_DENIED_SOFT                     0x0002\n#define ALC_HRTF_REQUIRED_SOFT                   0x0003\n#define ALC_HRTF_HEADPHONES_DETECTED_SOFT        0x0004\n#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT         0x0005\n#define ALC_NUM_HRTF_SPECIFIERS_SOFT             0x1994\n#define ALC_HRTF_SPECIFIER_SOFT                  0x1995\n#define ALC_HRTF_ID_SOFT                         0x1996\ntypedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);\ntypedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);\n#ifdef AL_ALEXT_PROTOTYPES\nALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);\nALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);\n#endif\n#endif\n\n#ifndef AL_SOFT_gain_clamp_ex\n#define AL_SOFT_gain_clamp_ex 1\n#define AL_GAIN_LIMIT_SOFT                       0x200E\n#endif\n\n#ifndef AL_SOFT_source_resampler\n#define AL_SOFT_source_resampler\n#define AL_NUM_RESAMPLERS_SOFT                   0x1210\n#define AL_DEFAULT_RESAMPLER_SOFT                0x1211\n#define AL_SOURCE_RESAMPLER_SOFT                 0x1212\n#define AL_RESAMPLER_NAME_SOFT                   0x1213\ntypedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index);\n#ifdef AL_ALEXT_PROTOTYPES\nAL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index);\n#endif\n#endif\n\n#ifndef AL_SOFT_source_spatialize\n#define AL_SOFT_source_spatialize\n#define AL_SOURCE_SPATIALIZE_SOFT                0x1214\n#define AL_AUTO_SOFT                             0x0002\n#endif\n\n#ifndef ALC_SOFT_output_limiter\n#define ALC_SOFT_output_limiter\n#define ALC_OUTPUT_LIMITER_SOFT                  0x199A\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/efx-presets.h",
    "content": "/* Reverb presets for EFX */\n\n#ifndef EFX_PRESETS_H\n#define EFX_PRESETS_H\n\n#ifndef EFXEAXREVERBPROPERTIES_DEFINED\n#define EFXEAXREVERBPROPERTIES_DEFINED\ntypedef struct {\n    float flDensity;\n    float flDiffusion;\n    float flGain;\n    float flGainHF;\n    float flGainLF;\n    float flDecayTime;\n    float flDecayHFRatio;\n    float flDecayLFRatio;\n    float flReflectionsGain;\n    float flReflectionsDelay;\n    float flReflectionsPan[3];\n    float flLateReverbGain;\n    float flLateReverbDelay;\n    float flLateReverbPan[3];\n    float flEchoTime;\n    float flEchoDepth;\n    float flModulationTime;\n    float flModulationDepth;\n    float flAirAbsorptionGainHF;\n    float flHFReference;\n    float flLFReference;\n    float flRoomRolloffFactor;\n    int   iDecayHFLimit;\n} EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES;\n#endif\n\n/* Default Presets */\n\n#define EFX_REVERB_PRESET_GENERIC \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.8913f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_PADDEDCELL \\\n    { 0.1715f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.1700f, 0.1000f, 1.0000f, 0.2500f, 0.0010f, { 0.0000f, 0.0000f, 0.0000f }, 1.2691f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ROOM \\\n    { 0.4287f, 1.0000f, 0.3162f, 0.5929f, 1.0000f, 0.4000f, 0.8300f, 1.0000f, 0.1503f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.0629f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_BATHROOM \\\n    { 0.1715f, 1.0000f, 0.3162f, 0.2512f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.6531f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 3.2734f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_LIVINGROOM \\\n    { 0.9766f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.5000f, 0.1000f, 1.0000f, 0.2051f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2805f, 0.0040f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_STONEROOM \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 2.3100f, 0.6400f, 1.0000f, 0.4411f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1003f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_AUDITORIUM \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.5781f, 1.0000f, 4.3200f, 0.5900f, 1.0000f, 0.4032f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7170f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CONCERTHALL \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.5623f, 1.0000f, 3.9200f, 0.7000f, 1.0000f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.9977f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CAVE \\\n    { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 2.9100f, 1.3000f, 1.0000f, 0.5000f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.7063f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_ARENA \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.4477f, 1.0000f, 7.2400f, 0.3300f, 1.0000f, 0.2612f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.0186f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_HANGAR \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 10.0500f, 0.2300f, 1.0000f, 0.5000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2560f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CARPETEDHALLWAY \\\n    { 0.4287f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 0.3000f, 0.1000f, 1.0000f, 0.1215f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.1531f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_HALLWAY \\\n    { 0.3645f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 1.4900f, 0.5900f, 1.0000f, 0.2458f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.6615f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_STONECORRIDOR \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.7612f, 1.0000f, 2.7000f, 0.7900f, 1.0000f, 0.2472f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 1.5758f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ALLEY \\\n    { 1.0000f, 0.3000f, 0.3162f, 0.7328f, 1.0000f, 1.4900f, 0.8600f, 1.0000f, 0.2500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.9954f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.9500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FOREST \\\n    { 1.0000f, 0.3000f, 0.3162f, 0.0224f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.0525f, 0.1620f, { 0.0000f, 0.0000f, 0.0000f }, 0.7682f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CITY \\\n    { 1.0000f, 0.5000f, 0.3162f, 0.3981f, 1.0000f, 1.4900f, 0.6700f, 1.0000f, 0.0730f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1427f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_MOUNTAINS \\\n    { 1.0000f, 0.2700f, 0.3162f, 0.0562f, 1.0000f, 1.4900f, 0.2100f, 1.0000f, 0.0407f, 0.3000f, { 0.0000f, 0.0000f, 0.0000f }, 0.1919f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_QUARRY \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0000f, 0.0610f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.7000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_PLAIN \\\n    { 1.0000f, 0.2100f, 0.3162f, 0.1000f, 1.0000f, 1.4900f, 0.5000f, 1.0000f, 0.0585f, 0.1790f, { 0.0000f, 0.0000f, 0.0000f }, 0.1089f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_PARKINGLOT \\\n    { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 1.6500f, 1.5000f, 1.0000f, 0.2082f, 0.0080f, { 0.0000f, 0.0000f, 0.0000f }, 0.2652f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_SEWERPIPE \\\n    { 0.3071f, 0.8000f, 0.3162f, 0.3162f, 1.0000f, 2.8100f, 0.1400f, 1.0000f, 1.6387f, 0.0140f, { 0.0000f, 0.0000f, 0.0000f }, 3.2471f, 0.0210f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_UNDERWATER \\\n    { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_DRUGGED \\\n    { 0.4287f, 0.5000f, 0.3162f, 1.0000f, 1.0000f, 8.3900f, 1.3900f, 1.0000f, 0.8760f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 3.1081f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_DIZZY \\\n    { 0.3645f, 0.6000f, 0.3162f, 0.6310f, 1.0000f, 17.2300f, 0.5600f, 1.0000f, 0.1392f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4937f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.8100f, 0.3100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_PSYCHOTIC \\\n    { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n/* Castle Presets */\n\n#define EFX_REVERB_PRESET_CASTLE_SMALLROOM \\\n    { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 1.2200f, 0.8300f, 0.3100f, 0.8913f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE \\\n    { 1.0000f, 0.8900f, 0.3162f, 0.3162f, 0.1000f, 2.3200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CASTLE_MEDIUMROOM \\\n    { 1.0000f, 0.9300f, 0.3162f, 0.2818f, 0.1000f, 2.0400f, 0.8300f, 0.4600f, 0.6310f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1550f, 0.0300f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CASTLE_LARGEROOM \\\n    { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.1259f, 2.5300f, 0.8300f, 0.5000f, 0.4467f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1850f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CASTLE_LONGPASSAGE \\\n    { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 3.4200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CASTLE_HALL \\\n    { 1.0000f, 0.8100f, 0.3162f, 0.2818f, 0.1778f, 3.1400f, 0.7900f, 0.6200f, 0.1778f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CASTLE_CUPBOARD \\\n    { 1.0000f, 0.8900f, 0.3162f, 0.2818f, 0.1000f, 0.6700f, 0.8700f, 0.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 3.5481f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CASTLE_COURTYARD \\\n    { 1.0000f, 0.4200f, 0.3162f, 0.4467f, 0.1995f, 2.1300f, 0.6100f, 0.2300f, 0.2239f, 0.1600f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3700f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_CASTLE_ALCOVE \\\n    { 1.0000f, 0.8900f, 0.3162f, 0.5012f, 0.1000f, 1.6400f, 0.8700f, 0.3100f, 1.0000f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }\n\n/* Factory Presets */\n\n#define EFX_REVERB_PRESET_FACTORY_SMALLROOM \\\n    { 0.3645f, 0.8200f, 0.3162f, 0.7943f, 0.5012f, 1.7200f, 0.6500f, 1.3100f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.1190f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE \\\n    { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 2.5300f, 0.6500f, 1.3100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FACTORY_MEDIUMROOM \\\n    { 0.4287f, 0.8200f, 0.2512f, 0.7943f, 0.5012f, 2.7600f, 0.6500f, 1.3100f, 0.2818f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1740f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FACTORY_LARGEROOM \\\n    { 0.4287f, 0.7500f, 0.2512f, 0.7079f, 0.6310f, 4.2400f, 0.5100f, 1.3100f, 0.1778f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2310f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FACTORY_LONGPASSAGE \\\n    { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 4.0600f, 0.6500f, 1.3100f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FACTORY_HALL \\\n    { 0.4287f, 0.7500f, 0.3162f, 0.7079f, 0.6310f, 7.4300f, 0.5100f, 1.3100f, 0.0631f, 0.0730f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FACTORY_CUPBOARD \\\n    { 0.3071f, 0.6300f, 0.2512f, 0.7943f, 0.5012f, 0.4900f, 0.6500f, 1.3100f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.1070f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FACTORY_COURTYARD \\\n    { 0.3071f, 0.5700f, 0.3162f, 0.3162f, 0.6310f, 2.3200f, 0.2900f, 0.5600f, 0.2239f, 0.1400f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2900f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_FACTORY_ALCOVE \\\n    { 0.3645f, 0.5900f, 0.2512f, 0.7943f, 0.5012f, 3.1400f, 0.6500f, 1.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1140f, 0.1000f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }\n\n/* Ice Palace Presets */\n\n#define EFX_REVERB_PRESET_ICEPALACE_SMALLROOM \\\n    { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 1.5100f, 1.5300f, 0.2700f, 0.8913f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1640f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ICEPALACE_SHORTPASSAGE \\\n    { 1.0000f, 0.7500f, 0.3162f, 0.5623f, 0.2818f, 1.7900f, 1.4600f, 0.2800f, 0.5012f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ICEPALACE_MEDIUMROOM \\\n    { 1.0000f, 0.8700f, 0.3162f, 0.5623f, 0.4467f, 2.2200f, 1.5300f, 0.3200f, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ICEPALACE_LARGEROOM \\\n    { 1.0000f, 0.8100f, 0.3162f, 0.5623f, 0.4467f, 3.1400f, 1.5300f, 0.3200f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ICEPALACE_LONGPASSAGE \\\n    { 1.0000f, 0.7700f, 0.3162f, 0.5623f, 0.3981f, 3.0100f, 1.4600f, 0.2800f, 0.7943f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.0400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ICEPALACE_HALL \\\n    { 1.0000f, 0.7600f, 0.3162f, 0.4467f, 0.5623f, 5.4900f, 1.5300f, 0.3800f, 0.1122f, 0.0540f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0520f, { 0.0000f, 0.0000f, 0.0000f }, 0.2260f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ICEPALACE_CUPBOARD \\\n    { 1.0000f, 0.8300f, 0.3162f, 0.5012f, 0.2239f, 0.7600f, 1.5300f, 0.2600f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1430f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ICEPALACE_COURTYARD \\\n    { 1.0000f, 0.5900f, 0.3162f, 0.2818f, 0.3162f, 2.0400f, 1.2000f, 0.3800f, 0.3162f, 0.1730f, { 0.0000f, 0.0000f, 0.0000f }, 0.3162f, 0.0430f, { 0.0000f, 0.0000f, 0.0000f }, 0.2350f, 0.4800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_ICEPALACE_ALCOVE \\\n    { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 2.7600f, 1.4600f, 0.2800f, 1.1220f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1610f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }\n\n/* Space Station Presets */\n\n#define EFX_REVERB_PRESET_SPACESTATION_SMALLROOM \\\n    { 0.2109f, 0.7000f, 0.3162f, 0.7079f, 0.8913f, 1.7200f, 0.8200f, 0.5500f, 0.7943f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 0.1880f, 0.2600f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE \\\n    { 0.2109f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 3.5700f, 0.5000f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1720f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM \\\n    { 0.2109f, 0.7500f, 0.3162f, 0.6310f, 0.8913f, 3.0100f, 0.5000f, 0.5500f, 0.3981f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2090f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPACESTATION_LARGEROOM \\\n    { 0.3645f, 0.8100f, 0.3162f, 0.6310f, 0.8913f, 3.8900f, 0.3800f, 0.6100f, 0.3162f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2330f, 0.2800f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE \\\n    { 0.4287f, 0.8200f, 0.3162f, 0.6310f, 0.8913f, 4.6200f, 0.6200f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPACESTATION_HALL \\\n    { 0.4287f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 7.1100f, 0.3800f, 0.6100f, 0.1778f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2500f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPACESTATION_CUPBOARD \\\n    { 0.1715f, 0.5600f, 0.3162f, 0.7079f, 0.8913f, 0.7900f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1810f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPACESTATION_ALCOVE \\\n    { 0.2109f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.1600f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1920f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }\n\n/* Wooden Galleon Presets */\n\n#define EFX_REVERB_PRESET_WOODEN_SMALLROOM \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.1122f, 0.3162f, 0.7900f, 0.3200f, 0.8700f, 1.0000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.7500f, 0.5000f, 0.8700f, 0.8913f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_WOODEN_MEDIUMROOM \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.2818f, 1.4700f, 0.4200f, 0.8200f, 0.8913f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_WOODEN_LARGEROOM \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.2818f, 2.6500f, 0.3300f, 0.8200f, 0.8913f, 0.0660f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_WOODEN_LONGPASSAGE \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.3162f, 1.9900f, 0.4000f, 0.7900f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4467f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_WOODEN_HALL \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.0794f, 0.2818f, 3.4500f, 0.3000f, 0.8200f, 0.8913f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_WOODEN_CUPBOARD \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.1413f, 0.3162f, 0.5600f, 0.4600f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_WOODEN_COURTYARD \\\n    { 1.0000f, 0.6500f, 0.3162f, 0.0794f, 0.3162f, 1.7900f, 0.3500f, 0.7900f, 0.5623f, 0.1230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_WOODEN_ALCOVE \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.2200f, 0.6200f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }\n\n/* Sports Presets */\n\n#define EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.4467f, 0.7943f, 6.2600f, 0.5100f, 1.1000f, 0.0631f, 0.1830f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPORT_SQUASHCOURT \\\n    { 1.0000f, 0.7500f, 0.3162f, 0.3162f, 0.7943f, 2.2200f, 0.9100f, 1.1600f, 0.4467f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1260f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL \\\n    { 1.0000f, 0.7000f, 0.3162f, 0.7943f, 0.8913f, 2.7600f, 1.2500f, 1.1400f, 0.6310f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_SPORT_LARGESWIMMINGPOOL \\\n    { 1.0000f, 0.8200f, 0.3162f, 0.7943f, 1.0000f, 5.4900f, 1.3100f, 1.1400f, 0.4467f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2220f, 0.5500f, 1.1590f, 0.2100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_SPORT_GYMNASIUM \\\n    { 1.0000f, 0.8100f, 0.3162f, 0.4467f, 0.8913f, 3.1400f, 1.0600f, 1.3500f, 0.3981f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0450f, { 0.0000f, 0.0000f, 0.0000f }, 0.1460f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPORT_FULLSTADIUM \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.0708f, 0.7943f, 5.2500f, 0.1700f, 0.8000f, 0.1000f, 0.1880f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SPORT_STADIUMTANNOY \\\n    { 1.0000f, 0.7800f, 0.3162f, 0.5623f, 0.5012f, 2.5300f, 0.8800f, 0.6800f, 0.2818f, 0.2300f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n/* Prefab Presets */\n\n#define EFX_REVERB_PRESET_PREFAB_WORKSHOP \\\n    { 0.4287f, 1.0000f, 0.3162f, 0.1413f, 0.3981f, 0.7600f, 1.0000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_PREFAB_SCHOOLROOM \\\n    { 0.4022f, 0.6900f, 0.3162f, 0.6310f, 0.5012f, 0.9800f, 0.4500f, 0.1800f, 1.4125f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_PREFAB_PRACTISEROOM \\\n    { 0.4022f, 0.8700f, 0.3162f, 0.3981f, 0.5012f, 1.1200f, 0.5600f, 0.1800f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_PREFAB_OUTHOUSE \\\n    { 1.0000f, 0.8200f, 0.3162f, 0.1122f, 0.1585f, 1.3800f, 0.3800f, 0.3500f, 0.8913f, 0.0240f, { 0.0000f, 0.0000f, -0.0000f }, 0.6310f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.1210f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_PREFAB_CARAVAN \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.1259f, 0.4300f, 1.5000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n/* Dome and Pipe Presets */\n\n#define EFX_REVERB_PRESET_DOME_TOMB \\\n    { 1.0000f, 0.7900f, 0.3162f, 0.3548f, 0.2239f, 4.1800f, 0.2100f, 0.1000f, 0.3868f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 1.6788f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_PIPE_SMALL \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 5.0400f, 0.1000f, 0.1000f, 0.5012f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 2.5119f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_DOME_SAINTPAULS \\\n    { 1.0000f, 0.8700f, 0.3162f, 0.3548f, 0.2239f, 10.4800f, 0.1900f, 0.1000f, 0.1778f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0420f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_PIPE_LONGTHIN \\\n    { 0.2560f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 9.2100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_PIPE_LARGE \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 8.4500f, 0.1000f, 0.1000f, 0.3981f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_PIPE_RESONANT \\\n    { 0.1373f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 6.8100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }\n\n/* Outdoors Presets */\n\n#define EFX_REVERB_PRESET_OUTDOORS_BACKYARD \\\n    { 1.0000f, 0.4500f, 0.3162f, 0.2512f, 0.5012f, 1.1200f, 0.3400f, 0.4600f, 0.4467f, 0.0690f, { 0.0000f, 0.0000f, -0.0000f }, 0.7079f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS \\\n    { 1.0000f, 0.0000f, 0.3162f, 0.0112f, 0.6310f, 2.1300f, 0.2100f, 0.4600f, 0.1778f, 0.3000f, { 0.0000f, 0.0000f, -0.0000f }, 0.4467f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON \\\n    { 1.0000f, 0.7400f, 0.3162f, 0.1778f, 0.6310f, 3.8900f, 0.2100f, 0.4600f, 0.3162f, 0.2230f, { 0.0000f, 0.0000f, -0.0000f }, 0.3548f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_OUTDOORS_CREEK \\\n    { 1.0000f, 0.3500f, 0.3162f, 0.1778f, 0.5012f, 2.1300f, 0.2100f, 0.4600f, 0.3981f, 0.1150f, { 0.0000f, 0.0000f, -0.0000f }, 0.1995f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_OUTDOORS_VALLEY \\\n    { 1.0000f, 0.2800f, 0.3162f, 0.0282f, 0.1585f, 2.8800f, 0.2600f, 0.3500f, 0.1413f, 0.2630f, { 0.0000f, 0.0000f, -0.0000f }, 0.3981f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }\n\n/* Mood Presets */\n\n#define EFX_REVERB_PRESET_MOOD_HEAVEN \\\n    { 1.0000f, 0.9400f, 0.3162f, 0.7943f, 0.4467f, 5.0400f, 1.1200f, 0.5600f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0800f, 2.7420f, 0.0500f, 0.9977f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_MOOD_HELL \\\n    { 1.0000f, 0.5700f, 0.3162f, 0.3548f, 0.4467f, 3.5700f, 0.4900f, 2.0000f, 0.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1100f, 0.0400f, 2.1090f, 0.5200f, 0.9943f, 5000.0000f, 139.5000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_MOOD_MEMORY \\\n    { 1.0000f, 0.8500f, 0.3162f, 0.6310f, 0.3548f, 4.0600f, 0.8200f, 0.5600f, 0.0398f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.4740f, 0.4500f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n/* Driving Presets */\n\n#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \\\n    { 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \\\n    { 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_DRIVING_INCAR_RACER \\\n    { 0.0832f, 0.8000f, 0.3162f, 1.0000f, 0.7943f, 0.1700f, 2.0000f, 0.4100f, 1.7783f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS \\\n    { 0.0832f, 0.8000f, 0.3162f, 0.6310f, 1.0000f, 0.1700f, 0.7500f, 0.4100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY \\\n    { 0.2560f, 1.0000f, 0.3162f, 0.1000f, 0.5012f, 0.1300f, 0.4100f, 0.4600f, 0.7943f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND \\\n    { 1.0000f, 1.0000f, 0.3162f, 0.2818f, 0.6310f, 3.0100f, 1.3700f, 1.2800f, 0.3548f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.1778f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND \\\n    { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 0.7943f, 4.6200f, 1.7500f, 1.4000f, 0.2082f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_DRIVING_TUNNEL \\\n    { 1.0000f, 0.8100f, 0.3162f, 0.3981f, 0.8913f, 3.4200f, 0.9400f, 1.3100f, 0.7079f, 0.0510f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.0500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 155.3000f, 0.0000f, 0x1 }\n\n/* City Presets */\n\n#define EFX_REVERB_PRESET_CITY_STREETS \\\n    { 1.0000f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.7900f, 1.1200f, 0.9100f, 0.2818f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 0.1995f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CITY_SUBWAY \\\n    { 1.0000f, 0.7400f, 0.3162f, 0.7079f, 0.8913f, 3.0100f, 1.2300f, 0.9100f, 0.7079f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CITY_MUSEUM \\\n    { 1.0000f, 0.8200f, 0.3162f, 0.1778f, 0.1778f, 3.2800f, 1.4000f, 0.5700f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_CITY_LIBRARY \\\n    { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.0891f, 2.7600f, 0.8900f, 0.4100f, 0.3548f, 0.0290f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }\n\n#define EFX_REVERB_PRESET_CITY_UNDERPASS \\\n    { 1.0000f, 0.8200f, 0.3162f, 0.4467f, 0.8913f, 3.5700f, 1.1200f, 0.9100f, 0.3981f, 0.0590f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1400f, 0.2500f, 0.0000f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CITY_ABANDONED \\\n    { 1.0000f, 0.6900f, 0.3162f, 0.7943f, 0.8913f, 3.2800f, 1.1700f, 0.9100f, 0.4467f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9966f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n/* Misc. Presets */\n\n#define EFX_REVERB_PRESET_DUSTYROOM \\\n    { 0.3645f, 0.5600f, 0.3162f, 0.7943f, 0.7079f, 1.7900f, 0.3800f, 0.2100f, 0.5012f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0060f, { 0.0000f, 0.0000f, 0.0000f }, 0.2020f, 0.0500f, 0.2500f, 0.0000f, 0.9886f, 13046.0000f, 163.3000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_CHAPEL \\\n    { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 1.0000f, 4.6200f, 0.6400f, 1.2300f, 0.4467f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.1100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\n#define EFX_REVERB_PRESET_SMALLWATERROOM \\\n    { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n\n#endif /* EFX_PRESETS_H */\n"
  },
  {
    "path": "apps/openmw/mwsound/efx.h",
    "content": "#ifndef AL_EFX_H\n#define AL_EFX_H\n\n\n#include \"alc.h\"\n#include \"al.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ALC_EXT_EFX_NAME                         \"ALC_EXT_EFX\"\n\n#define ALC_EFX_MAJOR_VERSION                    0x20001\n#define ALC_EFX_MINOR_VERSION                    0x20002\n#define ALC_MAX_AUXILIARY_SENDS                  0x20003\n\n\n/* Listener properties. */\n#define AL_METERS_PER_UNIT                       0x20004\n\n/* Source properties. */\n#define AL_DIRECT_FILTER                         0x20005\n#define AL_AUXILIARY_SEND_FILTER                 0x20006\n#define AL_AIR_ABSORPTION_FACTOR                 0x20007\n#define AL_ROOM_ROLLOFF_FACTOR                   0x20008\n#define AL_CONE_OUTER_GAINHF                     0x20009\n#define AL_DIRECT_FILTER_GAINHF_AUTO             0x2000A\n#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO       0x2000B\n#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO     0x2000C\n\n\n/* Effect properties. */\n\n/* Reverb effect parameters */\n#define AL_REVERB_DENSITY                        0x0001\n#define AL_REVERB_DIFFUSION                      0x0002\n#define AL_REVERB_GAIN                           0x0003\n#define AL_REVERB_GAINHF                         0x0004\n#define AL_REVERB_DECAY_TIME                     0x0005\n#define AL_REVERB_DECAY_HFRATIO                  0x0006\n#define AL_REVERB_REFLECTIONS_GAIN               0x0007\n#define AL_REVERB_REFLECTIONS_DELAY              0x0008\n#define AL_REVERB_LATE_REVERB_GAIN               0x0009\n#define AL_REVERB_LATE_REVERB_DELAY              0x000A\n#define AL_REVERB_AIR_ABSORPTION_GAINHF          0x000B\n#define AL_REVERB_ROOM_ROLLOFF_FACTOR            0x000C\n#define AL_REVERB_DECAY_HFLIMIT                  0x000D\n\n/* EAX Reverb effect parameters */\n#define AL_EAXREVERB_DENSITY                     0x0001\n#define AL_EAXREVERB_DIFFUSION                   0x0002\n#define AL_EAXREVERB_GAIN                        0x0003\n#define AL_EAXREVERB_GAINHF                      0x0004\n#define AL_EAXREVERB_GAINLF                      0x0005\n#define AL_EAXREVERB_DECAY_TIME                  0x0006\n#define AL_EAXREVERB_DECAY_HFRATIO               0x0007\n#define AL_EAXREVERB_DECAY_LFRATIO               0x0008\n#define AL_EAXREVERB_REFLECTIONS_GAIN            0x0009\n#define AL_EAXREVERB_REFLECTIONS_DELAY           0x000A\n#define AL_EAXREVERB_REFLECTIONS_PAN             0x000B\n#define AL_EAXREVERB_LATE_REVERB_GAIN            0x000C\n#define AL_EAXREVERB_LATE_REVERB_DELAY           0x000D\n#define AL_EAXREVERB_LATE_REVERB_PAN             0x000E\n#define AL_EAXREVERB_ECHO_TIME                   0x000F\n#define AL_EAXREVERB_ECHO_DEPTH                  0x0010\n#define AL_EAXREVERB_MODULATION_TIME             0x0011\n#define AL_EAXREVERB_MODULATION_DEPTH            0x0012\n#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF       0x0013\n#define AL_EAXREVERB_HFREFERENCE                 0x0014\n#define AL_EAXREVERB_LFREFERENCE                 0x0015\n#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR         0x0016\n#define AL_EAXREVERB_DECAY_HFLIMIT               0x0017\n\n/* Chorus effect parameters */\n#define AL_CHORUS_WAVEFORM                       0x0001\n#define AL_CHORUS_PHASE                          0x0002\n#define AL_CHORUS_RATE                           0x0003\n#define AL_CHORUS_DEPTH                          0x0004\n#define AL_CHORUS_FEEDBACK                       0x0005\n#define AL_CHORUS_DELAY                          0x0006\n\n/* Distortion effect parameters */\n#define AL_DISTORTION_EDGE                       0x0001\n#define AL_DISTORTION_GAIN                       0x0002\n#define AL_DISTORTION_LOWPASS_CUTOFF             0x0003\n#define AL_DISTORTION_EQCENTER                   0x0004\n#define AL_DISTORTION_EQBANDWIDTH                0x0005\n\n/* Echo effect parameters */\n#define AL_ECHO_DELAY                            0x0001\n#define AL_ECHO_LRDELAY                          0x0002\n#define AL_ECHO_DAMPING                          0x0003\n#define AL_ECHO_FEEDBACK                         0x0004\n#define AL_ECHO_SPREAD                           0x0005\n\n/* Flanger effect parameters */\n#define AL_FLANGER_WAVEFORM                      0x0001\n#define AL_FLANGER_PHASE                         0x0002\n#define AL_FLANGER_RATE                          0x0003\n#define AL_FLANGER_DEPTH                         0x0004\n#define AL_FLANGER_FEEDBACK                      0x0005\n#define AL_FLANGER_DELAY                         0x0006\n\n/* Frequency shifter effect parameters */\n#define AL_FREQUENCY_SHIFTER_FREQUENCY           0x0001\n#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION      0x0002\n#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION     0x0003\n\n/* Vocal morpher effect parameters */\n#define AL_VOCAL_MORPHER_PHONEMEA                0x0001\n#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING  0x0002\n#define AL_VOCAL_MORPHER_PHONEMEB                0x0003\n#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING  0x0004\n#define AL_VOCAL_MORPHER_WAVEFORM                0x0005\n#define AL_VOCAL_MORPHER_RATE                    0x0006\n\n/* Pitchshifter effect parameters */\n#define AL_PITCH_SHIFTER_COARSE_TUNE             0x0001\n#define AL_PITCH_SHIFTER_FINE_TUNE               0x0002\n\n/* Ringmodulator effect parameters */\n#define AL_RING_MODULATOR_FREQUENCY              0x0001\n#define AL_RING_MODULATOR_HIGHPASS_CUTOFF        0x0002\n#define AL_RING_MODULATOR_WAVEFORM               0x0003\n\n/* Autowah effect parameters */\n#define AL_AUTOWAH_ATTACK_TIME                   0x0001\n#define AL_AUTOWAH_RELEASE_TIME                  0x0002\n#define AL_AUTOWAH_RESONANCE                     0x0003\n#define AL_AUTOWAH_PEAK_GAIN                     0x0004\n\n/* Compressor effect parameters */\n#define AL_COMPRESSOR_ONOFF                      0x0001\n\n/* Equalizer effect parameters */\n#define AL_EQUALIZER_LOW_GAIN                    0x0001\n#define AL_EQUALIZER_LOW_CUTOFF                  0x0002\n#define AL_EQUALIZER_MID1_GAIN                   0x0003\n#define AL_EQUALIZER_MID1_CENTER                 0x0004\n#define AL_EQUALIZER_MID1_WIDTH                  0x0005\n#define AL_EQUALIZER_MID2_GAIN                   0x0006\n#define AL_EQUALIZER_MID2_CENTER                 0x0007\n#define AL_EQUALIZER_MID2_WIDTH                  0x0008\n#define AL_EQUALIZER_HIGH_GAIN                   0x0009\n#define AL_EQUALIZER_HIGH_CUTOFF                 0x000A\n\n/* Effect type */\n#define AL_EFFECT_FIRST_PARAMETER                0x0000\n#define AL_EFFECT_LAST_PARAMETER                 0x8000\n#define AL_EFFECT_TYPE                           0x8001\n\n/* Effect types, used with the AL_EFFECT_TYPE property */\n#define AL_EFFECT_NULL                           0x0000\n#define AL_EFFECT_REVERB                         0x0001\n#define AL_EFFECT_CHORUS                         0x0002\n#define AL_EFFECT_DISTORTION                     0x0003\n#define AL_EFFECT_ECHO                           0x0004\n#define AL_EFFECT_FLANGER                        0x0005\n#define AL_EFFECT_FREQUENCY_SHIFTER              0x0006\n#define AL_EFFECT_VOCAL_MORPHER                  0x0007\n#define AL_EFFECT_PITCH_SHIFTER                  0x0008\n#define AL_EFFECT_RING_MODULATOR                 0x0009\n#define AL_EFFECT_AUTOWAH                        0x000A\n#define AL_EFFECT_COMPRESSOR                     0x000B\n#define AL_EFFECT_EQUALIZER                      0x000C\n#define AL_EFFECT_EAXREVERB                      0x8000\n\n/* Auxiliary Effect Slot properties. */\n#define AL_EFFECTSLOT_EFFECT                     0x0001\n#define AL_EFFECTSLOT_GAIN                       0x0002\n#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO        0x0003\n\n/* NULL Auxiliary Slot ID to disable a source send. */\n#define AL_EFFECTSLOT_NULL                       0x0000\n\n\n/* Filter properties. */\n\n/* Lowpass filter parameters */\n#define AL_LOWPASS_GAIN                          0x0001\n#define AL_LOWPASS_GAINHF                        0x0002\n\n/* Highpass filter parameters */\n#define AL_HIGHPASS_GAIN                         0x0001\n#define AL_HIGHPASS_GAINLF                       0x0002\n\n/* Bandpass filter parameters */\n#define AL_BANDPASS_GAIN                         0x0001\n#define AL_BANDPASS_GAINLF                       0x0002\n#define AL_BANDPASS_GAINHF                       0x0003\n\n/* Filter type */\n#define AL_FILTER_FIRST_PARAMETER                0x0000\n#define AL_FILTER_LAST_PARAMETER                 0x8000\n#define AL_FILTER_TYPE                           0x8001\n\n/* Filter types, used with the AL_FILTER_TYPE property */\n#define AL_FILTER_NULL                           0x0000\n#define AL_FILTER_LOWPASS                        0x0001\n#define AL_FILTER_HIGHPASS                       0x0002\n#define AL_FILTER_BANDPASS                       0x0003\n\n\n/* Effect object function types. */\ntypedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*);\ntypedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*);\ntypedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint);\ntypedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint);\ntypedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*);\ntypedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat);\ntypedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*);\ntypedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*);\ntypedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*);\ntypedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*);\ntypedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*);\n\n/* Filter object function types. */\ntypedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*);\ntypedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*);\ntypedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint);\ntypedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint);\ntypedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*);\ntypedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat);\ntypedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*);\ntypedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*);\ntypedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*);\ntypedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*);\ntypedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*);\n\n/* Auxiliary Effect Slot object function types. */\ntypedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*);\ntypedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*);\ntypedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint);\ntypedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint);\ntypedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*);\ntypedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat);\ntypedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*);\ntypedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*);\ntypedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*);\ntypedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*);\ntypedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*);\n\n#ifdef AL_ALEXT_PROTOTYPES\nAL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects);\nAL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects);\nAL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect);\nAL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue);\nAL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues);\nAL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue);\nAL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues);\nAL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue);\nAL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues);\nAL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue);\nAL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);\n\nAL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters);\nAL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters);\nAL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter);\nAL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue);\nAL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues);\nAL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue);\nAL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues);\nAL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue);\nAL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues);\nAL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue);\nAL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);\n\nAL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);\nAL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots);\nAL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot);\nAL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue);\nAL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues);\nAL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue);\nAL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues);\nAL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue);\nAL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);\nAL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue);\nAL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);\n#endif\n\n/* Filter ranges and defaults. */\n\n/* Lowpass filter */\n#define AL_LOWPASS_MIN_GAIN                      (0.0f)\n#define AL_LOWPASS_MAX_GAIN                      (1.0f)\n#define AL_LOWPASS_DEFAULT_GAIN                  (1.0f)\n\n#define AL_LOWPASS_MIN_GAINHF                    (0.0f)\n#define AL_LOWPASS_MAX_GAINHF                    (1.0f)\n#define AL_LOWPASS_DEFAULT_GAINHF                (1.0f)\n\n/* Highpass filter */\n#define AL_HIGHPASS_MIN_GAIN                     (0.0f)\n#define AL_HIGHPASS_MAX_GAIN                     (1.0f)\n#define AL_HIGHPASS_DEFAULT_GAIN                 (1.0f)\n\n#define AL_HIGHPASS_MIN_GAINLF                   (0.0f)\n#define AL_HIGHPASS_MAX_GAINLF                   (1.0f)\n#define AL_HIGHPASS_DEFAULT_GAINLF               (1.0f)\n\n/* Bandpass filter */\n#define AL_BANDPASS_MIN_GAIN                     (0.0f)\n#define AL_BANDPASS_MAX_GAIN                     (1.0f)\n#define AL_BANDPASS_DEFAULT_GAIN                 (1.0f)\n\n#define AL_BANDPASS_MIN_GAINHF                   (0.0f)\n#define AL_BANDPASS_MAX_GAINHF                   (1.0f)\n#define AL_BANDPASS_DEFAULT_GAINHF               (1.0f)\n\n#define AL_BANDPASS_MIN_GAINLF                   (0.0f)\n#define AL_BANDPASS_MAX_GAINLF                   (1.0f)\n#define AL_BANDPASS_DEFAULT_GAINLF               (1.0f)\n\n\n/* Effect parameter ranges and defaults. */\n\n/* Standard reverb effect */\n#define AL_REVERB_MIN_DENSITY                    (0.0f)\n#define AL_REVERB_MAX_DENSITY                    (1.0f)\n#define AL_REVERB_DEFAULT_DENSITY                (1.0f)\n\n#define AL_REVERB_MIN_DIFFUSION                  (0.0f)\n#define AL_REVERB_MAX_DIFFUSION                  (1.0f)\n#define AL_REVERB_DEFAULT_DIFFUSION              (1.0f)\n\n#define AL_REVERB_MIN_GAIN                       (0.0f)\n#define AL_REVERB_MAX_GAIN                       (1.0f)\n#define AL_REVERB_DEFAULT_GAIN                   (0.32f)\n\n#define AL_REVERB_MIN_GAINHF                     (0.0f)\n#define AL_REVERB_MAX_GAINHF                     (1.0f)\n#define AL_REVERB_DEFAULT_GAINHF                 (0.89f)\n\n#define AL_REVERB_MIN_DECAY_TIME                 (0.1f)\n#define AL_REVERB_MAX_DECAY_TIME                 (20.0f)\n#define AL_REVERB_DEFAULT_DECAY_TIME             (1.49f)\n\n#define AL_REVERB_MIN_DECAY_HFRATIO              (0.1f)\n#define AL_REVERB_MAX_DECAY_HFRATIO              (2.0f)\n#define AL_REVERB_DEFAULT_DECAY_HFRATIO          (0.83f)\n\n#define AL_REVERB_MIN_REFLECTIONS_GAIN           (0.0f)\n#define AL_REVERB_MAX_REFLECTIONS_GAIN           (3.16f)\n#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN       (0.05f)\n\n#define AL_REVERB_MIN_REFLECTIONS_DELAY          (0.0f)\n#define AL_REVERB_MAX_REFLECTIONS_DELAY          (0.3f)\n#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY      (0.007f)\n\n#define AL_REVERB_MIN_LATE_REVERB_GAIN           (0.0f)\n#define AL_REVERB_MAX_LATE_REVERB_GAIN           (10.0f)\n#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN       (1.26f)\n\n#define AL_REVERB_MIN_LATE_REVERB_DELAY          (0.0f)\n#define AL_REVERB_MAX_LATE_REVERB_DELAY          (0.1f)\n#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY      (0.011f)\n\n#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF      (0.892f)\n#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF      (1.0f)\n#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF  (0.994f)\n\n#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR        (0.0f)\n#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR        (10.0f)\n#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR    (0.0f)\n\n#define AL_REVERB_MIN_DECAY_HFLIMIT              AL_FALSE\n#define AL_REVERB_MAX_DECAY_HFLIMIT              AL_TRUE\n#define AL_REVERB_DEFAULT_DECAY_HFLIMIT          AL_TRUE\n\n/* EAX reverb effect */\n#define AL_EAXREVERB_MIN_DENSITY                 (0.0f)\n#define AL_EAXREVERB_MAX_DENSITY                 (1.0f)\n#define AL_EAXREVERB_DEFAULT_DENSITY             (1.0f)\n\n#define AL_EAXREVERB_MIN_DIFFUSION               (0.0f)\n#define AL_EAXREVERB_MAX_DIFFUSION               (1.0f)\n#define AL_EAXREVERB_DEFAULT_DIFFUSION           (1.0f)\n\n#define AL_EAXREVERB_MIN_GAIN                    (0.0f)\n#define AL_EAXREVERB_MAX_GAIN                    (1.0f)\n#define AL_EAXREVERB_DEFAULT_GAIN                (0.32f)\n\n#define AL_EAXREVERB_MIN_GAINHF                  (0.0f)\n#define AL_EAXREVERB_MAX_GAINHF                  (1.0f)\n#define AL_EAXREVERB_DEFAULT_GAINHF              (0.89f)\n\n#define AL_EAXREVERB_MIN_GAINLF                  (0.0f)\n#define AL_EAXREVERB_MAX_GAINLF                  (1.0f)\n#define AL_EAXREVERB_DEFAULT_GAINLF              (1.0f)\n\n#define AL_EAXREVERB_MIN_DECAY_TIME              (0.1f)\n#define AL_EAXREVERB_MAX_DECAY_TIME              (20.0f)\n#define AL_EAXREVERB_DEFAULT_DECAY_TIME          (1.49f)\n\n#define AL_EAXREVERB_MIN_DECAY_HFRATIO           (0.1f)\n#define AL_EAXREVERB_MAX_DECAY_HFRATIO           (2.0f)\n#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO       (0.83f)\n\n#define AL_EAXREVERB_MIN_DECAY_LFRATIO           (0.1f)\n#define AL_EAXREVERB_MAX_DECAY_LFRATIO           (2.0f)\n#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO       (1.0f)\n\n#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN        (0.0f)\n#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN        (3.16f)\n#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN    (0.05f)\n\n#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY       (0.0f)\n#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY       (0.3f)\n#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY   (0.007f)\n\n#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)\n\n#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN        (0.0f)\n#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN        (10.0f)\n#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN    (1.26f)\n\n#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY       (0.0f)\n#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY       (0.1f)\n#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY   (0.011f)\n\n#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)\n\n#define AL_EAXREVERB_MIN_ECHO_TIME               (0.075f)\n#define AL_EAXREVERB_MAX_ECHO_TIME               (0.25f)\n#define AL_EAXREVERB_DEFAULT_ECHO_TIME           (0.25f)\n\n#define AL_EAXREVERB_MIN_ECHO_DEPTH              (0.0f)\n#define AL_EAXREVERB_MAX_ECHO_DEPTH              (1.0f)\n#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH          (0.0f)\n\n#define AL_EAXREVERB_MIN_MODULATION_TIME         (0.04f)\n#define AL_EAXREVERB_MAX_MODULATION_TIME         (4.0f)\n#define AL_EAXREVERB_DEFAULT_MODULATION_TIME     (0.25f)\n\n#define AL_EAXREVERB_MIN_MODULATION_DEPTH        (0.0f)\n#define AL_EAXREVERB_MAX_MODULATION_DEPTH        (1.0f)\n#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH    (0.0f)\n\n#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF   (0.892f)\n#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF   (1.0f)\n#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)\n\n#define AL_EAXREVERB_MIN_HFREFERENCE             (1000.0f)\n#define AL_EAXREVERB_MAX_HFREFERENCE             (20000.0f)\n#define AL_EAXREVERB_DEFAULT_HFREFERENCE         (5000.0f)\n\n#define AL_EAXREVERB_MIN_LFREFERENCE             (20.0f)\n#define AL_EAXREVERB_MAX_LFREFERENCE             (1000.0f)\n#define AL_EAXREVERB_DEFAULT_LFREFERENCE         (250.0f)\n\n#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR     (0.0f)\n#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR     (10.0f)\n#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)\n\n#define AL_EAXREVERB_MIN_DECAY_HFLIMIT           AL_FALSE\n#define AL_EAXREVERB_MAX_DECAY_HFLIMIT           AL_TRUE\n#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT       AL_TRUE\n\n/* Chorus effect */\n#define AL_CHORUS_WAVEFORM_SINUSOID              (0)\n#define AL_CHORUS_WAVEFORM_TRIANGLE              (1)\n\n#define AL_CHORUS_MIN_WAVEFORM                   (0)\n#define AL_CHORUS_MAX_WAVEFORM                   (1)\n#define AL_CHORUS_DEFAULT_WAVEFORM               (1)\n\n#define AL_CHORUS_MIN_PHASE                      (-180)\n#define AL_CHORUS_MAX_PHASE                      (180)\n#define AL_CHORUS_DEFAULT_PHASE                  (90)\n\n#define AL_CHORUS_MIN_RATE                       (0.0f)\n#define AL_CHORUS_MAX_RATE                       (10.0f)\n#define AL_CHORUS_DEFAULT_RATE                   (1.1f)\n\n#define AL_CHORUS_MIN_DEPTH                      (0.0f)\n#define AL_CHORUS_MAX_DEPTH                      (1.0f)\n#define AL_CHORUS_DEFAULT_DEPTH                  (0.1f)\n\n#define AL_CHORUS_MIN_FEEDBACK                   (-1.0f)\n#define AL_CHORUS_MAX_FEEDBACK                   (1.0f)\n#define AL_CHORUS_DEFAULT_FEEDBACK               (0.25f)\n\n#define AL_CHORUS_MIN_DELAY                      (0.0f)\n#define AL_CHORUS_MAX_DELAY                      (0.016f)\n#define AL_CHORUS_DEFAULT_DELAY                  (0.016f)\n\n/* Distortion effect */\n#define AL_DISTORTION_MIN_EDGE                   (0.0f)\n#define AL_DISTORTION_MAX_EDGE                   (1.0f)\n#define AL_DISTORTION_DEFAULT_EDGE               (0.2f)\n\n#define AL_DISTORTION_MIN_GAIN                   (0.01f)\n#define AL_DISTORTION_MAX_GAIN                   (1.0f)\n#define AL_DISTORTION_DEFAULT_GAIN               (0.05f)\n\n#define AL_DISTORTION_MIN_LOWPASS_CUTOFF         (80.0f)\n#define AL_DISTORTION_MAX_LOWPASS_CUTOFF         (24000.0f)\n#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF     (8000.0f)\n\n#define AL_DISTORTION_MIN_EQCENTER               (80.0f)\n#define AL_DISTORTION_MAX_EQCENTER               (24000.0f)\n#define AL_DISTORTION_DEFAULT_EQCENTER           (3600.0f)\n\n#define AL_DISTORTION_MIN_EQBANDWIDTH            (80.0f)\n#define AL_DISTORTION_MAX_EQBANDWIDTH            (24000.0f)\n#define AL_DISTORTION_DEFAULT_EQBANDWIDTH        (3600.0f)\n\n/* Echo effect */\n#define AL_ECHO_MIN_DELAY                        (0.0f)\n#define AL_ECHO_MAX_DELAY                        (0.207f)\n#define AL_ECHO_DEFAULT_DELAY                    (0.1f)\n\n#define AL_ECHO_MIN_LRDELAY                      (0.0f)\n#define AL_ECHO_MAX_LRDELAY                      (0.404f)\n#define AL_ECHO_DEFAULT_LRDELAY                  (0.1f)\n\n#define AL_ECHO_MIN_DAMPING                      (0.0f)\n#define AL_ECHO_MAX_DAMPING                      (0.99f)\n#define AL_ECHO_DEFAULT_DAMPING                  (0.5f)\n\n#define AL_ECHO_MIN_FEEDBACK                     (0.0f)\n#define AL_ECHO_MAX_FEEDBACK                     (1.0f)\n#define AL_ECHO_DEFAULT_FEEDBACK                 (0.5f)\n\n#define AL_ECHO_MIN_SPREAD                       (-1.0f)\n#define AL_ECHO_MAX_SPREAD                       (1.0f)\n#define AL_ECHO_DEFAULT_SPREAD                   (-1.0f)\n\n/* Flanger effect */\n#define AL_FLANGER_WAVEFORM_SINUSOID             (0)\n#define AL_FLANGER_WAVEFORM_TRIANGLE             (1)\n\n#define AL_FLANGER_MIN_WAVEFORM                  (0)\n#define AL_FLANGER_MAX_WAVEFORM                  (1)\n#define AL_FLANGER_DEFAULT_WAVEFORM              (1)\n\n#define AL_FLANGER_MIN_PHASE                     (-180)\n#define AL_FLANGER_MAX_PHASE                     (180)\n#define AL_FLANGER_DEFAULT_PHASE                 (0)\n\n#define AL_FLANGER_MIN_RATE                      (0.0f)\n#define AL_FLANGER_MAX_RATE                      (10.0f)\n#define AL_FLANGER_DEFAULT_RATE                  (0.27f)\n\n#define AL_FLANGER_MIN_DEPTH                     (0.0f)\n#define AL_FLANGER_MAX_DEPTH                     (1.0f)\n#define AL_FLANGER_DEFAULT_DEPTH                 (1.0f)\n\n#define AL_FLANGER_MIN_FEEDBACK                  (-1.0f)\n#define AL_FLANGER_MAX_FEEDBACK                  (1.0f)\n#define AL_FLANGER_DEFAULT_FEEDBACK              (-0.5f)\n\n#define AL_FLANGER_MIN_DELAY                     (0.0f)\n#define AL_FLANGER_MAX_DELAY                     (0.004f)\n#define AL_FLANGER_DEFAULT_DELAY                 (0.002f)\n\n/* Frequency shifter effect */\n#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY       (0.0f)\n#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY       (24000.0f)\n#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY   (0.0f)\n\n#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION  (0)\n#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION  (2)\n#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0)\n\n#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN      (0)\n#define AL_FREQUENCY_SHIFTER_DIRECTION_UP        (1)\n#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF       (2)\n\n#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0)\n#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2)\n#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0)\n\n/* Vocal morpher effect */\n#define AL_VOCAL_MORPHER_MIN_PHONEMEA            (0)\n#define AL_VOCAL_MORPHER_MAX_PHONEMEA            (29)\n#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA        (0)\n\n#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24)\n#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24)\n#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0)\n\n#define AL_VOCAL_MORPHER_MIN_PHONEMEB            (0)\n#define AL_VOCAL_MORPHER_MAX_PHONEMEB            (29)\n#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB        (10)\n\n#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24)\n#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24)\n#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0)\n\n#define AL_VOCAL_MORPHER_PHONEME_A               (0)\n#define AL_VOCAL_MORPHER_PHONEME_E               (1)\n#define AL_VOCAL_MORPHER_PHONEME_I               (2)\n#define AL_VOCAL_MORPHER_PHONEME_O               (3)\n#define AL_VOCAL_MORPHER_PHONEME_U               (4)\n#define AL_VOCAL_MORPHER_PHONEME_AA              (5)\n#define AL_VOCAL_MORPHER_PHONEME_AE              (6)\n#define AL_VOCAL_MORPHER_PHONEME_AH              (7)\n#define AL_VOCAL_MORPHER_PHONEME_AO              (8)\n#define AL_VOCAL_MORPHER_PHONEME_EH              (9)\n#define AL_VOCAL_MORPHER_PHONEME_ER              (10)\n#define AL_VOCAL_MORPHER_PHONEME_IH              (11)\n#define AL_VOCAL_MORPHER_PHONEME_IY              (12)\n#define AL_VOCAL_MORPHER_PHONEME_UH              (13)\n#define AL_VOCAL_MORPHER_PHONEME_UW              (14)\n#define AL_VOCAL_MORPHER_PHONEME_B               (15)\n#define AL_VOCAL_MORPHER_PHONEME_D               (16)\n#define AL_VOCAL_MORPHER_PHONEME_F               (17)\n#define AL_VOCAL_MORPHER_PHONEME_G               (18)\n#define AL_VOCAL_MORPHER_PHONEME_J               (19)\n#define AL_VOCAL_MORPHER_PHONEME_K               (20)\n#define AL_VOCAL_MORPHER_PHONEME_L               (21)\n#define AL_VOCAL_MORPHER_PHONEME_M               (22)\n#define AL_VOCAL_MORPHER_PHONEME_N               (23)\n#define AL_VOCAL_MORPHER_PHONEME_P               (24)\n#define AL_VOCAL_MORPHER_PHONEME_R               (25)\n#define AL_VOCAL_MORPHER_PHONEME_S               (26)\n#define AL_VOCAL_MORPHER_PHONEME_T               (27)\n#define AL_VOCAL_MORPHER_PHONEME_V               (28)\n#define AL_VOCAL_MORPHER_PHONEME_Z               (29)\n\n#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID       (0)\n#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE       (1)\n#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH       (2)\n\n#define AL_VOCAL_MORPHER_MIN_WAVEFORM            (0)\n#define AL_VOCAL_MORPHER_MAX_WAVEFORM            (2)\n#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM        (0)\n\n#define AL_VOCAL_MORPHER_MIN_RATE                (0.0f)\n#define AL_VOCAL_MORPHER_MAX_RATE                (10.0f)\n#define AL_VOCAL_MORPHER_DEFAULT_RATE            (1.41f)\n\n/* Pitch shifter effect */\n#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE         (-12)\n#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE         (12)\n#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE     (12)\n\n#define AL_PITCH_SHIFTER_MIN_FINE_TUNE           (-50)\n#define AL_PITCH_SHIFTER_MAX_FINE_TUNE           (50)\n#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE       (0)\n\n/* Ring modulator effect */\n#define AL_RING_MODULATOR_MIN_FREQUENCY          (0.0f)\n#define AL_RING_MODULATOR_MAX_FREQUENCY          (8000.0f)\n#define AL_RING_MODULATOR_DEFAULT_FREQUENCY      (440.0f)\n\n#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF    (0.0f)\n#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF    (24000.0f)\n#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f)\n\n#define AL_RING_MODULATOR_SINUSOID               (0)\n#define AL_RING_MODULATOR_SAWTOOTH               (1)\n#define AL_RING_MODULATOR_SQUARE                 (2)\n\n#define AL_RING_MODULATOR_MIN_WAVEFORM           (0)\n#define AL_RING_MODULATOR_MAX_WAVEFORM           (2)\n#define AL_RING_MODULATOR_DEFAULT_WAVEFORM       (0)\n\n/* Autowah effect */\n#define AL_AUTOWAH_MIN_ATTACK_TIME               (0.0001f)\n#define AL_AUTOWAH_MAX_ATTACK_TIME               (1.0f)\n#define AL_AUTOWAH_DEFAULT_ATTACK_TIME           (0.06f)\n\n#define AL_AUTOWAH_MIN_RELEASE_TIME              (0.0001f)\n#define AL_AUTOWAH_MAX_RELEASE_TIME              (1.0f)\n#define AL_AUTOWAH_DEFAULT_RELEASE_TIME          (0.06f)\n\n#define AL_AUTOWAH_MIN_RESONANCE                 (2.0f)\n#define AL_AUTOWAH_MAX_RESONANCE                 (1000.0f)\n#define AL_AUTOWAH_DEFAULT_RESONANCE             (1000.0f)\n\n#define AL_AUTOWAH_MIN_PEAK_GAIN                 (0.00003f)\n#define AL_AUTOWAH_MAX_PEAK_GAIN                 (31621.0f)\n#define AL_AUTOWAH_DEFAULT_PEAK_GAIN             (11.22f)\n\n/* Compressor effect */\n#define AL_COMPRESSOR_MIN_ONOFF                  (0)\n#define AL_COMPRESSOR_MAX_ONOFF                  (1)\n#define AL_COMPRESSOR_DEFAULT_ONOFF              (1)\n\n/* Equalizer effect */\n#define AL_EQUALIZER_MIN_LOW_GAIN                (0.126f)\n#define AL_EQUALIZER_MAX_LOW_GAIN                (7.943f)\n#define AL_EQUALIZER_DEFAULT_LOW_GAIN            (1.0f)\n\n#define AL_EQUALIZER_MIN_LOW_CUTOFF              (50.0f)\n#define AL_EQUALIZER_MAX_LOW_CUTOFF              (800.0f)\n#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF          (200.0f)\n\n#define AL_EQUALIZER_MIN_MID1_GAIN               (0.126f)\n#define AL_EQUALIZER_MAX_MID1_GAIN               (7.943f)\n#define AL_EQUALIZER_DEFAULT_MID1_GAIN           (1.0f)\n\n#define AL_EQUALIZER_MIN_MID1_CENTER             (200.0f)\n#define AL_EQUALIZER_MAX_MID1_CENTER             (3000.0f)\n#define AL_EQUALIZER_DEFAULT_MID1_CENTER         (500.0f)\n\n#define AL_EQUALIZER_MIN_MID1_WIDTH              (0.01f)\n#define AL_EQUALIZER_MAX_MID1_WIDTH              (1.0f)\n#define AL_EQUALIZER_DEFAULT_MID1_WIDTH          (1.0f)\n\n#define AL_EQUALIZER_MIN_MID2_GAIN               (0.126f)\n#define AL_EQUALIZER_MAX_MID2_GAIN               (7.943f)\n#define AL_EQUALIZER_DEFAULT_MID2_GAIN           (1.0f)\n\n#define AL_EQUALIZER_MIN_MID2_CENTER             (1000.0f)\n#define AL_EQUALIZER_MAX_MID2_CENTER             (8000.0f)\n#define AL_EQUALIZER_DEFAULT_MID2_CENTER         (3000.0f)\n\n#define AL_EQUALIZER_MIN_MID2_WIDTH              (0.01f)\n#define AL_EQUALIZER_MAX_MID2_WIDTH              (1.0f)\n#define AL_EQUALIZER_DEFAULT_MID2_WIDTH          (1.0f)\n\n#define AL_EQUALIZER_MIN_HIGH_GAIN               (0.126f)\n#define AL_EQUALIZER_MAX_HIGH_GAIN               (7.943f)\n#define AL_EQUALIZER_DEFAULT_HIGH_GAIN           (1.0f)\n\n#define AL_EQUALIZER_MIN_HIGH_CUTOFF             (4000.0f)\n#define AL_EQUALIZER_MAX_HIGH_CUTOFF             (16000.0f)\n#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF         (6000.0f)\n\n\n/* Source parameter value ranges and defaults. */\n#define AL_MIN_AIR_ABSORPTION_FACTOR             (0.0f)\n#define AL_MAX_AIR_ABSORPTION_FACTOR             (10.0f)\n#define AL_DEFAULT_AIR_ABSORPTION_FACTOR         (0.0f)\n\n#define AL_MIN_ROOM_ROLLOFF_FACTOR               (0.0f)\n#define AL_MAX_ROOM_ROLLOFF_FACTOR               (10.0f)\n#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR           (0.0f)\n\n#define AL_MIN_CONE_OUTER_GAINHF                 (0.0f)\n#define AL_MAX_CONE_OUTER_GAINHF                 (1.0f)\n#define AL_DEFAULT_CONE_OUTER_GAINHF             (1.0f)\n\n#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO         AL_FALSE\n#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO         AL_TRUE\n#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO     AL_TRUE\n\n#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO   AL_FALSE\n#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO   AL_TRUE\n#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE\n\n#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE\n#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE\n#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE\n\n\n/* Listener parameter value ranges and defaults. */\n#define AL_MIN_METERS_PER_UNIT                   FLT_MIN\n#define AL_MAX_METERS_PER_UNIT                   FLT_MAX\n#define AL_DEFAULT_METERS_PER_UNIT               (1.0f)\n\n\n#ifdef __cplusplus\n}  /* extern \"C\" */\n#endif\n\n#endif /* AL_EFX_H */\n"
  },
  {
    "path": "apps/openmw/mwsound/ffmpeg_decoder.cpp",
    "content": "#include \"ffmpeg_decoder.hpp\"\n\n#include <memory>\n\n#include <stdexcept>\n#include <algorithm>\n\n#include <components/debug/debuglog.hpp>\n#include <components/vfs/manager.hpp>\n\nnamespace MWSound\n{\n\nint FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)\n{\n    try\n    {\n        std::istream& stream = *static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;\n        stream.clear();\n        stream.read((char*)buf, buf_size);\n        return stream.gcount();\n    }\n    catch (std::exception& )\n    {\n        return 0;\n    }\n}\n\nint FFmpeg_Decoder::writePacket(void *, uint8_t *, int)\n{\n    Log(Debug::Error) << \"can't write to read-only stream\";\n    return -1;\n}\n\nint64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence)\n{\n    std::istream& stream = *static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;\n\n    whence &= ~AVSEEK_FORCE;\n\n    stream.clear();\n\n    if(whence == AVSEEK_SIZE)\n    {\n        size_t prev = stream.tellg();\n        stream.seekg(0, std::ios_base::end);\n        size_t size = stream.tellg();\n        stream.seekg(prev, std::ios_base::beg);\n        return size;\n    }\n\n    if(whence == SEEK_SET)\n        stream.seekg(offset, std::ios_base::beg);\n    else if(whence == SEEK_CUR)\n        stream.seekg(offset, std::ios_base::cur);\n    else if(whence == SEEK_END)\n        stream.seekg(offset, std::ios_base::end);\n    else\n        return -1;\n\n    return stream.tellg();\n}\n\n\n/* Used by getAV*Data to search for more compressed data, and buffer it in the\n * correct stream. It won't buffer data for streams that the app doesn't have a\n * handle for. */\nbool FFmpeg_Decoder::getNextPacket()\n{\n    if(!mStream)\n        return false;\n\n    int stream_idx = mStream - mFormatCtx->streams;\n    while(av_read_frame(mFormatCtx, &mPacket) >= 0)\n    {\n        /* Check if the packet belongs to this stream */\n        if(stream_idx == mPacket.stream_index)\n        {\n            if(mPacket.pts != (int64_t)AV_NOPTS_VALUE)\n                mNextPts = av_q2d((*mStream)->time_base)*mPacket.pts;\n            return true;\n        }\n\n        /* Free the packet and look for another */\n        av_packet_unref(&mPacket);\n    }\n\n    return false;\n}\n\nbool FFmpeg_Decoder::getAVAudioData()\n{\n    bool got_frame = false;\n\n    if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)\n        return false;\n\n    do {\n        /* Decode some data, and check for errors */\n        int ret = avcodec_receive_frame(mCodecCtx, mFrame);\n        if (ret == AVERROR(EAGAIN))\n        {\n            if (mPacket.size == 0 && !getNextPacket())\n                return false;\n            ret = avcodec_send_packet(mCodecCtx, &mPacket);\n            av_packet_unref(&mPacket);\n            if (ret == 0)\n                continue;\n        }\n        if (ret != 0)\n            return false;\n\n        av_packet_unref(&mPacket);\n\n        if (mFrame->nb_samples == 0)\n            continue;\n        got_frame = true;\n\n        if(mSwr)\n        {\n            if(!mDataBuf || mDataBufLen < mFrame->nb_samples)\n            {\n                av_freep(&mDataBuf);\n                if(av_samples_alloc(&mDataBuf, nullptr, av_get_channel_layout_nb_channels(mOutputChannelLayout),\n                                    mFrame->nb_samples, mOutputSampleFormat, 0) < 0)\n                    return false;\n                else\n                    mDataBufLen = mFrame->nb_samples;\n            }\n\n            if(swr_convert(mSwr, (uint8_t**)&mDataBuf, mFrame->nb_samples,\n                (const uint8_t**)mFrame->extended_data, mFrame->nb_samples) < 0)\n            {\n                return false;\n            }\n            mFrameData = &mDataBuf;\n        }\n        else\n            mFrameData = &mFrame->data[0];\n\n    } while(!got_frame);\n    mNextPts += (double)mFrame->nb_samples / mCodecCtx->sample_rate;\n\n    return true;\n}\n\nsize_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length)\n{\n    size_t dec = 0;\n\n    while(dec < length)\n    {\n        /* If there's no decoded data, find some */\n        if(mFramePos >= mFrameSize)\n        {\n            if(!getAVAudioData())\n                break;\n            mFramePos = 0;\n            mFrameSize = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) *\n                         av_get_bytes_per_sample(mOutputSampleFormat);\n        }\n\n        /* Get the amount of bytes remaining to be written, and clamp to\n         * the amount of decoded data we have */\n        size_t rem = std::min<size_t>(length-dec, mFrameSize-mFramePos);\n\n        /* Copy the data to the app's buffer and increment */\n        memcpy(data, mFrameData[0]+mFramePos, rem);\n        data = (char*)data + rem;\n        dec += rem;\n        mFramePos += rem;\n    }\n\n    /* Return the number of bytes we were able to get */\n    return dec;\n}\n\nvoid FFmpeg_Decoder::open(const std::string &fname)\n{\n    close();\n    mDataStream = mResourceMgr->get(fname);\n\n    if((mFormatCtx=avformat_alloc_context()) == nullptr)\n        throw std::runtime_error(\"Failed to allocate context\");\n\n    try\n    {\n        mFormatCtx->pb = avio_alloc_context(nullptr, 0, 0, this, readPacket, writePacket, seek);\n        if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), nullptr, nullptr) != 0)\n        {\n            // \"Note that a user-supplied AVFormatContext will be freed on failure\".\n            if (mFormatCtx)\n            {\n                if (mFormatCtx->pb != nullptr)\n                {\n                    if (mFormatCtx->pb->buffer != nullptr)\n                    {\n                        av_free(mFormatCtx->pb->buffer);\n                        mFormatCtx->pb->buffer = nullptr;\n                    }\n                    av_free(mFormatCtx->pb);\n                    mFormatCtx->pb = nullptr;\n                }\n                avformat_free_context(mFormatCtx);\n            }\n            mFormatCtx = nullptr;\n            throw std::runtime_error(\"Failed to allocate input stream\");\n        }\n\n        if(avformat_find_stream_info(mFormatCtx, nullptr) < 0)\n            throw std::runtime_error(\"Failed to find stream info in \"+fname);\n\n        for(size_t j = 0;j < mFormatCtx->nb_streams;j++)\n        {\n            if(mFormatCtx->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)\n            {\n                mStream = &mFormatCtx->streams[j];\n                break;\n            }\n        }\n        if(!mStream)\n            throw std::runtime_error(\"No audio streams in \"+fname);\n\n        const AVCodec *codec = avcodec_find_decoder((*mStream)->codecpar->codec_id);\n        if(!codec)\n        {\n            std::string ss = \"No codec found for id \" +\n                                std::to_string((*mStream)->codecpar->codec_id);\n            throw std::runtime_error(ss);\n        }\n\n        AVCodecContext *avctx = avcodec_alloc_context3(codec);\n        avcodec_parameters_to_context(avctx, (*mStream)->codecpar);\n\n// This is not needed anymore above FFMpeg version 4.0\n#if LIBAVCODEC_VERSION_INT < 3805796\n        av_codec_set_pkt_timebase(avctx, (*mStream)->time_base);\n#endif\n\n        mCodecCtx = avctx;\n\n        if(avcodec_open2(mCodecCtx, codec, nullptr) < 0)\n            throw std::runtime_error(std::string(\"Failed to open audio codec \") + codec->long_name);\n\n        mFrame = av_frame_alloc();\n\n        if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP)\n            mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support\n        else if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8P)\n            mOutputSampleFormat = AV_SAMPLE_FMT_U8;\n        else if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16P)\n            mOutputSampleFormat = AV_SAMPLE_FMT_S16;\n        else\n            mOutputSampleFormat = AV_SAMPLE_FMT_S16;\n\n        mOutputChannelLayout = (*mStream)->codecpar->channel_layout;\n        if(mOutputChannelLayout == 0)\n            mOutputChannelLayout = av_get_default_channel_layout(mCodecCtx->channels);\n\n        mCodecCtx->channel_layout = mOutputChannelLayout;\n    }\n    catch(...)\n    {\n        if(mStream)\n            avcodec_free_context(&mCodecCtx);\n        mStream = nullptr;\n\n        if (mFormatCtx != nullptr)\n        {\n            if (mFormatCtx->pb->buffer != nullptr)\n            {\n                av_free(mFormatCtx->pb->buffer);\n                mFormatCtx->pb->buffer = nullptr;\n            }\n            av_free(mFormatCtx->pb);\n            mFormatCtx->pb = nullptr;\n\n            avformat_close_input(&mFormatCtx);\n        }\n    }\n}\n\nvoid FFmpeg_Decoder::close()\n{\n    if(mStream)\n        avcodec_free_context(&mCodecCtx);\n    mStream = nullptr;\n\n    av_packet_unref(&mPacket);\n    av_freep(&mDataBuf);\n    av_frame_free(&mFrame);\n    swr_free(&mSwr);\n\n    if(mFormatCtx)\n    {\n        if (mFormatCtx->pb != nullptr)\n        {\n            // mFormatCtx->pb->buffer must be freed by hand,\n            // if not, valgrind will show memleak, see:\n            //\n            // https://trac.ffmpeg.org/ticket/1357\n            //\n            if (mFormatCtx->pb->buffer != nullptr)\n            {\n                av_freep(&mFormatCtx->pb->buffer);\n            }\n#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100)\n            avio_context_free(&mFormatCtx->pb);\n#else\n            av_freep(&mFormatCtx->pb);\n#endif\n        }\n        avformat_close_input(&mFormatCtx);\n    }\n\n    mDataStream.reset();\n}\n\nstd::string FFmpeg_Decoder::getName()\n{\n// In the FFMpeg 4.0 a \"filename\" field was replaced by \"url\"\n#if LIBAVCODEC_VERSION_INT < 3805796\n    return mFormatCtx->filename;\n#else\n    return mFormatCtx->url;\n#endif\n}\n\nvoid FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)\n{\n    if(!mStream)\n        throw std::runtime_error(\"No audio stream info\");\n\n    if(mOutputSampleFormat == AV_SAMPLE_FMT_U8)\n        *type = SampleType_UInt8;\n    else if(mOutputSampleFormat == AV_SAMPLE_FMT_S16)\n        *type = SampleType_Int16;\n    else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT)\n        *type = SampleType_Float32;\n    else\n    {\n        mOutputSampleFormat = AV_SAMPLE_FMT_S16;\n        *type = SampleType_Int16;\n    }\n\n    if(mOutputChannelLayout == AV_CH_LAYOUT_MONO)\n        *chans = ChannelConfig_Mono;\n    else if(mOutputChannelLayout == AV_CH_LAYOUT_STEREO)\n        *chans = ChannelConfig_Stereo;\n    else if(mOutputChannelLayout == AV_CH_LAYOUT_QUAD)\n        *chans = ChannelConfig_Quad;\n    else if(mOutputChannelLayout == AV_CH_LAYOUT_5POINT1)\n        *chans = ChannelConfig_5point1;\n    else if(mOutputChannelLayout == AV_CH_LAYOUT_7POINT1)\n        *chans = ChannelConfig_7point1;\n    else\n    {\n        char str[1024];\n        av_get_channel_layout_string(str, sizeof(str), mCodecCtx->channels, mCodecCtx->channel_layout);\n        Log(Debug::Error) << \"Unsupported channel layout: \"<< str;\n\n        if(mCodecCtx->channels == 1)\n        {\n            mOutputChannelLayout = AV_CH_LAYOUT_MONO;\n            *chans = ChannelConfig_Mono;\n        }\n        else\n        {\n            mOutputChannelLayout = AV_CH_LAYOUT_STEREO;\n            *chans = ChannelConfig_Stereo;\n        }\n    }\n\n    *samplerate = mCodecCtx->sample_rate;\n    int64_t ch_layout = mCodecCtx->channel_layout;\n    if(ch_layout == 0)\n        ch_layout = av_get_default_channel_layout(mCodecCtx->channels);\n\n    if(mOutputSampleFormat != mCodecCtx->sample_fmt ||\n       mOutputChannelLayout != ch_layout)\n    {\n        mSwr = swr_alloc_set_opts(mSwr,                   // SwrContext\n                          mOutputChannelLayout,           // output ch layout\n                          mOutputSampleFormat,            // output sample format\n                          mCodecCtx->sample_rate, // output sample rate\n                          ch_layout,                      // input ch layout\n                          mCodecCtx->sample_fmt,         // input sample format\n                          mCodecCtx->sample_rate, // input sample rate\n                          0,                              // logging level offset\n                          nullptr);                          // log context\n        if(!mSwr)\n            throw std::runtime_error(\"Couldn't allocate SwrContext\");\n        int init=swr_init(mSwr);\n        if(init < 0)\n            throw std::runtime_error(\"Couldn't initialize SwrContext: \"+std::to_string(init));\n    }\n}\n\nsize_t FFmpeg_Decoder::read(char *buffer, size_t bytes)\n{\n    if(!mStream)\n    {\n        Log(Debug::Error) << \"No audio stream\";\n        return 0;\n    }\n    return readAVAudioData(buffer, bytes);\n}\n\nvoid FFmpeg_Decoder::readAll(std::vector<char> &output)\n{\n    if(!mStream)\n    {\n        Log(Debug::Error) << \"No audio stream\";\n        return;\n    }\n\n    while(getAVAudioData())\n    {\n        size_t got = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) *\n                     av_get_bytes_per_sample(mOutputSampleFormat);\n        const char *inbuf = reinterpret_cast<char*>(mFrameData[0]);\n        output.insert(output.end(), inbuf, inbuf+got);\n    }\n}\n\nsize_t FFmpeg_Decoder::getSampleOffset()\n{\n    int delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) /\n                av_get_bytes_per_sample(mOutputSampleFormat);\n    return (int)(mNextPts*mCodecCtx->sample_rate) - delay;\n}\n\nFFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs)\n  : Sound_Decoder(vfs)\n  , mFormatCtx(nullptr)\n  , mCodecCtx(nullptr)\n  , mStream(nullptr)\n  , mFrame(nullptr)\n  , mFrameSize(0)\n  , mFramePos(0)\n  , mNextPts(0.0)\n  , mSwr(nullptr)\n  , mOutputSampleFormat(AV_SAMPLE_FMT_NONE)\n  , mOutputChannelLayout(0)\n  , mDataBuf(nullptr)\n  , mFrameData(nullptr)\n  , mDataBufLen(0)\n{\n    memset(&mPacket, 0, sizeof(mPacket));\n\n    /* We need to make sure ffmpeg is initialized. Optionally silence warning\n     * output from the lib */\n    static bool done_init = false;\n    if(!done_init)\n    {\n// This is not needed anymore above FFMpeg version 4.0\n#if LIBAVCODEC_VERSION_INT < 3805796\n        av_register_all();\n#endif\n        av_log_set_level(AV_LOG_ERROR);\n        done_init = true;\n    }\n}\n\nFFmpeg_Decoder::~FFmpeg_Decoder()\n{\n    close();\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/ffmpeg_decoder.hpp",
    "content": "#ifndef GAME_SOUND_FFMPEG_DECODER_H\n#define GAME_SOUND_FFMPEG_DECODER_H\n\n#include <stdint.h>\n\n#if defined(_MSC_VER)\n    #pragma warning (push)\n    #pragma warning (disable : 4244)\n#endif\n\nextern \"C\"\n{\n#include <libavcodec/avcodec.h>\n#include <libavformat/avformat.h>\n#include <libavutil/channel_layout.h>\n\n// From version 54.56 binkaudio encoding format changed from S16 to FLTP. See:\n// https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d\n// https://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872\n#include <libswresample/swresample.h>\n}\n\n#if defined(_MSC_VER)\n    #pragma warning (pop)\n#endif\n\n#include <components/files/constrainedfilestream.hpp>\n\n#include <string>\n#include <istream>\n\n#include \"sound_decoder.hpp\"\n\n\nnamespace MWSound\n{\n    class FFmpeg_Decoder final : public Sound_Decoder\n    {\n        AVFormatContext *mFormatCtx;\n        AVCodecContext *mCodecCtx;\n        AVStream **mStream;\n\n        AVPacket mPacket;\n        AVFrame *mFrame;\n\n        int mFrameSize;\n        int mFramePos;\n\n        double mNextPts;\n\n        SwrContext *mSwr;\n        enum AVSampleFormat mOutputSampleFormat;\n        int64_t mOutputChannelLayout;\n        uint8_t *mDataBuf;\n        uint8_t **mFrameData;\n        int mDataBufLen;\n\n        bool getNextPacket();\n\n        Files::IStreamPtr mDataStream;\n\n        static int readPacket(void *user_data, uint8_t *buf, int buf_size);\n        static int writePacket(void *user_data, uint8_t *buf, int buf_size);\n        static int64_t seek(void *user_data, int64_t offset, int whence);\n\n        bool getAVAudioData();\n        size_t readAVAudioData(void *data, size_t length);\n\n        void open(const std::string &fname) override;\n        void close() override;\n\n        std::string getName() override;\n        void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;\n\n        size_t read(char *buffer, size_t bytes) override;\n        void readAll(std::vector<char> &output) override;\n        size_t getSampleOffset() override;\n\n        FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);\n        FFmpeg_Decoder(const FFmpeg_Decoder &rhs);\n\n    public:\n        explicit FFmpeg_Decoder(const VFS::Manager* vfs);\n\n        virtual ~FFmpeg_Decoder();\n\n        friend class SoundManager;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/loudness.cpp",
    "content": "#include \"loudness.hpp\"\n\n#include <stdint.h>\n#include <limits>\n#include <algorithm>\n\n#include \"soundmanagerimp.hpp\"\n\nnamespace MWSound\n{\n\nvoid Sound_Loudness::analyzeLoudness(const std::vector< char >& data)\n{\n    mQueue.insert( mQueue.end(), data.begin(), data.end() );\n    if (!mQueue.size())\n        return;\n\n    int samplesPerSegment = static_cast<int>(mSampleRate / mSamplesPerSec);\n    int numSamples = bytesToFrames(mQueue.size(), mChannelConfig, mSampleType);\n    int advance = framesToBytes(1, mChannelConfig, mSampleType);\n\n\n    int segment=0;\n    int sample=0;\n    while (segment < numSamples/samplesPerSegment)\n    {\n        float sum=0;\n        int samplesAdded = 0;\n        while (sample < numSamples && sample < (segment+1)*samplesPerSegment)\n        {\n            // get sample on a scale from -1 to 1\n            float value = 0;\n            if (mSampleType == SampleType_UInt8)\n                value = ((char)(mQueue[sample*advance]^0x80))/128.f;\n            else if (mSampleType == SampleType_Int16)\n            {\n                value = *reinterpret_cast<const int16_t*>(&mQueue[sample*advance]);\n                value /= float(std::numeric_limits<int16_t>::max());\n            }\n            else if (mSampleType == SampleType_Float32)\n            {\n                value = *reinterpret_cast<const float*>(&mQueue[sample*advance]);\n                value = std::max(-1.f, std::min(1.f, value)); // Float samples *should* be scaled to [-1,1] already.\n            }\n\n            sum += value*value;\n            ++samplesAdded;\n            ++sample;\n        }\n\n        float rms = 0; // root mean square\n        if (samplesAdded > 0)\n            rms = std::sqrt(sum / samplesAdded);\n        mSamples.push_back(rms);\n        ++segment;\n    }\n\n    mQueue.erase(mQueue.begin(), mQueue.begin() + sample*advance);\n}\n\n\nfloat Sound_Loudness::getLoudnessAtTime(float sec) const\n{\n    if(mSamplesPerSec <= 0.0f || mSamples.empty() || sec < 0.0f)\n        return 0.0f;\n\n    size_t index = static_cast<size_t>(sec * mSamplesPerSec);\n    index = std::max<size_t>(0, std::min(index, mSamples.size()-1));\n    return mSamples[index];\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/loudness.hpp",
    "content": "#ifndef GAME_SOUND_LOUDNESS_H\n#define GAME_SOUND_LOUDNESS_H\n\n#include <vector>\n#include <deque>\n\n#include \"sound_decoder.hpp\"\n\nnamespace MWSound\n{\n\nclass Sound_Loudness {\n    float mSamplesPerSec;\n    int mSampleRate;\n    ChannelConfig mChannelConfig;\n    SampleType mSampleType;\n\n    // Loudness sample info\n    std::vector<float> mSamples;\n\n    std::deque<char> mQueue;\n\npublic:\n    /**\n     * @param samplesPerSecond How many loudness values per second of audio to compute.\n     * @param sampleRate the sample rate of the sound buffer\n     * @param chans channel layout of the buffer\n     * @param type sample type of the buffer\n    */\n    Sound_Loudness(float samplesPerSecond, int sampleRate, ChannelConfig chans, SampleType type)\n        : mSamplesPerSec(samplesPerSecond)\n        , mSampleRate(sampleRate)\n        , mChannelConfig(chans)\n        , mSampleType(type)\n    { }\n\n    /**\n     * Analyzes the energy (closely related to loudness) of a sound buffer.\n     * The buffer will be divided into segments according to \\a valuesPerSecond,\n     * and for each segment a loudness value in the range of [0,1] will be computed.\n     * The computed values are then added to the mSamples vector. This method should be called continuously\n     * with chunks of audio until the whole audio file is processed.\n     * If the size of \\a data does not exactly fit a number of loudness samples, the remainder\n     * will be kept in the mQueue and used in the next call to analyzeLoudness.\n     * @param data the sound buffer to analyze, containing raw samples\n     */\n    void analyzeLoudness(const std::vector<char>& data);\n\n    /**\n     * Get loudness at a particular time. Before calling this, the stream has to be analyzed up to that point in time (see analyzeLoudness()).\n     */\n    float getLoudnessAtTime(float sec) const;\n};\n\n}\n\n#endif /* GAME_SOUND_LOUDNESS_H */\n"
  },
  {
    "path": "apps/openmw/mwsound/movieaudiofactory.cpp",
    "content": "#include \"movieaudiofactory.hpp\"\n\n#include <extern/osg-ffmpeg-videoplayer/audiodecoder.hpp>\n#include <extern/osg-ffmpeg-videoplayer/videostate.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"sound_decoder.hpp\"\n#include \"sound.hpp\"\n\nnamespace MWSound\n{\n\n    class MovieAudioDecoder;\n    class MWSoundDecoderBridge final : public Sound_Decoder\n    {\n    public:\n        MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder)\n            : Sound_Decoder(nullptr)\n            , mDecoder(decoder)\n        {\n        }\n\n    private:\n        MWSound::MovieAudioDecoder* mDecoder;\n\n        void open(const std::string &fname) override;\n        void close() override;\n        std::string getName() override;\n        void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;\n        size_t read(char *buffer, size_t bytes) override;\n        size_t getSampleOffset() override;\n    };\n\n    class MovieAudioDecoder : public Video::MovieAudioDecoder\n    {\n    public:\n        MovieAudioDecoder(Video::VideoState *videoState)\n            : Video::MovieAudioDecoder(videoState), mAudioTrack(nullptr)\n        {\n            mDecoderBridge.reset(new MWSoundDecoderBridge(this));\n        }\n\n        size_t getSampleOffset()\n        {\n            ssize_t clock_delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) /\n                                  av_get_bytes_per_sample(mOutputSampleFormat);\n            return (size_t)(mAudioClock*mAudioContext->sample_rate) - clock_delay;\n        }\n\n        std::string getStreamName()\n        {\n            return std::string();\n        }\n\n    private:\n        // MovieAudioDecoder overrides\n\n        double getAudioClock() override\n        {\n            return (double)getSampleOffset()/(double)mAudioContext->sample_rate -\n                   MWBase::Environment::get().getSoundManager()->getTrackTimeDelay(mAudioTrack);\n        }\n\n        void adjustAudioSettings(AVSampleFormat& sampleFormat, uint64_t& channelLayout, int& sampleRate) override\n        {\n            if (sampleFormat == AV_SAMPLE_FMT_U8P || sampleFormat == AV_SAMPLE_FMT_U8)\n                sampleFormat = AV_SAMPLE_FMT_U8;\n            else if (sampleFormat == AV_SAMPLE_FMT_S16P || sampleFormat == AV_SAMPLE_FMT_S16)\n                sampleFormat = AV_SAMPLE_FMT_S16;\n            else if (sampleFormat == AV_SAMPLE_FMT_FLTP || sampleFormat == AV_SAMPLE_FMT_FLT)\n                sampleFormat = AV_SAMPLE_FMT_S16; // FIXME: check for AL_EXT_FLOAT32 support\n            else\n                sampleFormat = AV_SAMPLE_FMT_S16;\n\n            if (channelLayout == AV_CH_LAYOUT_5POINT1 || channelLayout == AV_CH_LAYOUT_7POINT1\n                    || channelLayout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support\n                channelLayout = AV_CH_LAYOUT_STEREO;\n            else if (channelLayout != AV_CH_LAYOUT_MONO\n                     && channelLayout != AV_CH_LAYOUT_STEREO)\n                channelLayout = AV_CH_LAYOUT_STEREO;\n        }\n\n    public:\n        ~MovieAudioDecoder()\n        {\n            if(mAudioTrack)\n                MWBase::Environment::get().getSoundManager()->stopTrack(mAudioTrack);\n            mAudioTrack = nullptr;\n            mDecoderBridge.reset();\n        }\n\n        MWBase::SoundStream *mAudioTrack;\n        std::shared_ptr<MWSoundDecoderBridge> mDecoderBridge;\n    };\n\n\n    void MWSoundDecoderBridge::open(const std::string &fname)\n    {\n        throw std::runtime_error(\"Method not implemented\");\n    }\n    void MWSoundDecoderBridge::close() {}\n\n    std::string MWSoundDecoderBridge::getName()\n    {\n        return mDecoder->getStreamName();\n    }\n\n    void MWSoundDecoderBridge::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)\n    {\n        *samplerate = mDecoder->getOutputSampleRate();\n\n        uint64_t outputChannelLayout = mDecoder->getOutputChannelLayout();\n        if (outputChannelLayout == AV_CH_LAYOUT_MONO)\n            *chans = ChannelConfig_Mono;\n        else if (outputChannelLayout == AV_CH_LAYOUT_5POINT1)\n            *chans = ChannelConfig_5point1;\n        else if (outputChannelLayout == AV_CH_LAYOUT_7POINT1)\n            *chans = ChannelConfig_7point1;\n        else if (outputChannelLayout == AV_CH_LAYOUT_STEREO)\n            *chans = ChannelConfig_Stereo;\n        else if (outputChannelLayout == AV_CH_LAYOUT_QUAD)\n            *chans = ChannelConfig_Quad;\n        else\n            throw std::runtime_error(\"Unsupported channel layout: \"+\n                                     std::to_string(outputChannelLayout));\n\n        AVSampleFormat outputSampleFormat = mDecoder->getOutputSampleFormat();\n        if (outputSampleFormat == AV_SAMPLE_FMT_U8)\n            *type = SampleType_UInt8;\n        else if (outputSampleFormat == AV_SAMPLE_FMT_FLT)\n            *type = SampleType_Float32;\n        else if (outputSampleFormat == AV_SAMPLE_FMT_S16)\n            *type = SampleType_Int16;\n        else\n        {\n            char str[1024];\n            av_get_sample_fmt_string(str, sizeof(str), outputSampleFormat);\n            throw std::runtime_error(std::string(\"Unsupported sample format: \")+str);\n        }\n    }\n\n    size_t MWSoundDecoderBridge::read(char *buffer, size_t bytes)\n    {\n        return mDecoder->read(buffer, bytes);\n    }\n\n    size_t MWSoundDecoderBridge::getSampleOffset()\n    {\n        return mDecoder->getSampleOffset();\n    }\n\n\n\n    std::shared_ptr<Video::MovieAudioDecoder> MovieAudioFactory::createDecoder(Video::VideoState* videoState)\n    {\n        std::shared_ptr<MWSound::MovieAudioDecoder> decoder(new MWSound::MovieAudioDecoder(videoState));\n        decoder->setupFormat();\n\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        MWBase::SoundStream *sound = sndMgr->playTrack(decoder->mDecoderBridge, MWSound::Type::Movie);\n        if (!sound)\n        {\n            decoder.reset();\n            return decoder;\n        }\n\n        decoder->mAudioTrack = sound;\n        return decoder;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/movieaudiofactory.hpp",
    "content": "#ifndef OPENMW_MWSOUND_MOVIEAUDIOFACTORY_H\n#define OPENMW_MWSOUND_MOVIEAUDIOFACTORY_H\n\n#include <extern/osg-ffmpeg-videoplayer/audiofactory.hpp>\n\nnamespace MWSound\n{\n\n    class MovieAudioFactory : public Video::MovieAudioFactory\n    {\n        std::shared_ptr<Video::MovieAudioDecoder> createDecoder(Video::VideoState* videoState) override;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/openal_output.cpp",
    "content": "#include <algorithm>\n#include <cstring>\n#include <vector>\n#include <memory>\n#include <array>\n#include <atomic>\n#include <condition_variable>\n#include <thread>\n#include <mutex>\n#include <chrono>\n\n#include <stdint.h>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/constants.hpp>\n#include <components/vfs/manager.hpp>\n\n#include \"openal_output.hpp\"\n#include \"sound_decoder.hpp\"\n#include \"sound.hpp\"\n#include \"soundmanagerimp.hpp\"\n#include \"loudness.hpp\"\n\n#include \"efx-presets.h\"\n\n#ifndef ALC_ALL_DEVICES_SPECIFIER\n#define ALC_ALL_DEVICES_SPECIFIER 0x1013\n#endif\n\n\n#define MAKE_PTRID(id) ((void*)(uintptr_t)id)\n#define GET_PTRID(ptr) ((ALuint)(uintptr_t)ptr)\n\nnamespace\n{\n\nconst int sLoudnessFPS = 20; // loudness values per second of audio\n\nALCenum checkALCError(ALCdevice *device, const char *func, int line)\n{\n    ALCenum err = alcGetError(device);\n    if(err != ALC_NO_ERROR)\n        Log(Debug::Error) << \"ALC error \"<< alcGetString(device, err) << \" (\" << err << \") @ \" << func << \":\" << line;\n    return err;\n}\n#define getALCError(d) checkALCError((d), __FUNCTION__, __LINE__)\n\nALenum checkALError(const char *func, int line)\n{\n    ALenum err = alGetError();\n    if(err != AL_NO_ERROR)\n        Log(Debug::Error) << \"AL error \" << alGetString(err) << \" (\" << err << \") @ \" << func << \":\" << line;\n    return err;\n}\n#define getALError() checkALError(__FUNCTION__, __LINE__)\n\n// Helper to get an OpenAL extension function\ntemplate<typename T, typename R>\nvoid convertPointer(T& dest, R src)\n{\n    memcpy(&dest, &src, sizeof(src));\n}\n\ntemplate<typename T>\nvoid getALCFunc(T& func, ALCdevice *device, const char *name)\n{\n    void* funcPtr = alcGetProcAddress(device, name);\n    convertPointer(func, funcPtr);\n}\n\ntemplate<typename T>\nvoid getALFunc(T& func, const char *name)\n{\n    void* funcPtr = alGetProcAddress(name);\n    convertPointer(func, funcPtr);\n}\n\n// Effect objects\nLPALGENEFFECTS alGenEffects;\nLPALDELETEEFFECTS alDeleteEffects;\nLPALISEFFECT alIsEffect;\nLPALEFFECTI alEffecti;\nLPALEFFECTIV alEffectiv;\nLPALEFFECTF alEffectf;\nLPALEFFECTFV alEffectfv;\nLPALGETEFFECTI alGetEffecti;\nLPALGETEFFECTIV alGetEffectiv;\nLPALGETEFFECTF alGetEffectf;\nLPALGETEFFECTFV alGetEffectfv;\n// Filter objects\nLPALGENFILTERS alGenFilters;\nLPALDELETEFILTERS alDeleteFilters;\nLPALISFILTER alIsFilter;\nLPALFILTERI alFilteri;\nLPALFILTERIV alFilteriv;\nLPALFILTERF alFilterf;\nLPALFILTERFV alFilterfv;\nLPALGETFILTERI alGetFilteri;\nLPALGETFILTERIV alGetFilteriv;\nLPALGETFILTERF alGetFilterf;\nLPALGETFILTERFV alGetFilterfv;\n// Auxiliary slot objects\nLPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;\nLPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;\nLPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;\nLPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;\nLPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;\nLPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;\nLPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;\nLPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;\nLPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;\nLPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;\nLPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;\n\n\nvoid LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES &props)\n{\n    ALint type = AL_NONE;\n    alGetEffecti(effect, AL_EFFECT_TYPE, &type);\n    if(type == AL_EFFECT_EAXREVERB)\n    {\n        alEffectf(effect, AL_EAXREVERB_DIFFUSION, props.flDiffusion);\n        alEffectf(effect, AL_EAXREVERB_DENSITY, props.flDensity);\n        alEffectf(effect, AL_EAXREVERB_GAIN, props.flGain);\n        alEffectf(effect, AL_EAXREVERB_GAINHF, props.flGainHF);\n        alEffectf(effect, AL_EAXREVERB_GAINLF, props.flGainLF);\n        alEffectf(effect, AL_EAXREVERB_DECAY_TIME, props.flDecayTime);\n        alEffectf(effect, AL_EAXREVERB_DECAY_HFRATIO, props.flDecayHFRatio);\n        alEffectf(effect, AL_EAXREVERB_DECAY_LFRATIO, props.flDecayLFRatio);\n        alEffectf(effect, AL_EAXREVERB_REFLECTIONS_GAIN, props.flReflectionsGain);\n        alEffectf(effect, AL_EAXREVERB_REFLECTIONS_DELAY, props.flReflectionsDelay);\n        alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, props.flReflectionsPan);\n        alEffectf(effect, AL_EAXREVERB_LATE_REVERB_GAIN, props.flLateReverbGain);\n        alEffectf(effect, AL_EAXREVERB_LATE_REVERB_DELAY, props.flLateReverbDelay);\n        alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, props.flLateReverbPan);\n        alEffectf(effect, AL_EAXREVERB_ECHO_TIME, props.flEchoTime);\n        alEffectf(effect, AL_EAXREVERB_ECHO_DEPTH, props.flEchoDepth);\n        alEffectf(effect, AL_EAXREVERB_MODULATION_TIME, props.flModulationTime);\n        alEffectf(effect, AL_EAXREVERB_MODULATION_DEPTH, props.flModulationDepth);\n        alEffectf(effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, props.flAirAbsorptionGainHF);\n        alEffectf(effect, AL_EAXREVERB_HFREFERENCE, props.flHFReference);\n        alEffectf(effect, AL_EAXREVERB_LFREFERENCE, props.flLFReference);\n        alEffectf(effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, props.flRoomRolloffFactor);\n        alEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, props.iDecayHFLimit ? AL_TRUE : AL_FALSE);\n    }\n    else if(type == AL_EFFECT_REVERB)\n    {\n        alEffectf(effect, AL_REVERB_DIFFUSION, props.flDiffusion);\n        alEffectf(effect, AL_REVERB_DENSITY, props.flDensity);\n        alEffectf(effect, AL_REVERB_GAIN, props.flGain);\n        alEffectf(effect, AL_REVERB_GAINHF, props.flGainHF);\n        alEffectf(effect, AL_REVERB_DECAY_TIME, props.flDecayTime);\n        alEffectf(effect, AL_REVERB_DECAY_HFRATIO, props.flDecayHFRatio);\n        alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, props.flReflectionsGain);\n        alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, props.flReflectionsDelay);\n        alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, props.flLateReverbGain);\n        alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, props.flLateReverbDelay);\n        alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, props.flAirAbsorptionGainHF);\n        alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, props.flRoomRolloffFactor);\n        alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, props.iDecayHFLimit ? AL_TRUE : AL_FALSE);\n    }\n    getALError();\n}\n\n}\n\nnamespace MWSound\n{\n\nstatic ALenum getALFormat(ChannelConfig chans, SampleType type)\n{\n    struct FormatEntry {\n        ALenum format;\n        ChannelConfig chans;\n        SampleType type;\n    };\n    struct FormatEntryExt {\n        const char name[32];\n        ChannelConfig chans;\n        SampleType type;\n    };\n    static const std::array<FormatEntry,4> fmtlist{{\n        { AL_FORMAT_MONO16,   ChannelConfig_Mono,   SampleType_Int16 },\n        { AL_FORMAT_MONO8,    ChannelConfig_Mono,   SampleType_UInt8 },\n        { AL_FORMAT_STEREO16, ChannelConfig_Stereo, SampleType_Int16 },\n        { AL_FORMAT_STEREO8,  ChannelConfig_Stereo, SampleType_UInt8 },\n    }};\n\n    for(auto &fmt : fmtlist)\n    {\n        if(fmt.chans == chans && fmt.type == type)\n            return fmt.format;\n    }\n\n    if(alIsExtensionPresent(\"AL_EXT_MCFORMATS\"))\n    {\n        static const std::array<FormatEntryExt,6> mcfmtlist{{\n            { \"AL_FORMAT_QUAD16\",   ChannelConfig_Quad,    SampleType_Int16 },\n            { \"AL_FORMAT_QUAD8\",    ChannelConfig_Quad,    SampleType_UInt8 },\n            { \"AL_FORMAT_51CHN16\",  ChannelConfig_5point1, SampleType_Int16 },\n            { \"AL_FORMAT_51CHN8\",   ChannelConfig_5point1, SampleType_UInt8 },\n            { \"AL_FORMAT_71CHN16\",  ChannelConfig_7point1, SampleType_Int16 },\n            { \"AL_FORMAT_71CHN8\",   ChannelConfig_7point1, SampleType_UInt8 },\n        }};\n\n        for(auto &fmt : mcfmtlist)\n        {\n            if(fmt.chans == chans && fmt.type == type)\n            {\n                ALenum format = alGetEnumValue(fmt.name);\n                if(format != 0 && format != -1)\n                    return format;\n            }\n        }\n    }\n    if(alIsExtensionPresent(\"AL_EXT_FLOAT32\"))\n    {\n        static const std::array<FormatEntryExt,2> fltfmtlist{{\n            { \"AL_FORMAT_MONO_FLOAT32\",   ChannelConfig_Mono,   SampleType_Float32 },\n            { \"AL_FORMAT_STEREO_FLOAT32\", ChannelConfig_Stereo, SampleType_Float32 },\n        }};\n\n        for(auto &fmt : fltfmtlist)\n        {\n            if(fmt.chans == chans && fmt.type == type)\n            {\n                ALenum format = alGetEnumValue(fmt.name);\n                if(format != 0 && format != -1)\n                    return format;\n            }\n        }\n\n        if(alIsExtensionPresent(\"AL_EXT_MCFORMATS\"))\n        {\n            static const std::array<FormatEntryExt,3> fltmcfmtlist{{\n                { \"AL_FORMAT_QUAD32\",  ChannelConfig_Quad,    SampleType_Float32 },\n                { \"AL_FORMAT_51CHN32\", ChannelConfig_5point1, SampleType_Float32 },\n                { \"AL_FORMAT_71CHN32\", ChannelConfig_7point1, SampleType_Float32 },\n            }};\n\n            for(auto &fmt : fltmcfmtlist)\n            {\n                if(fmt.chans == chans && fmt.type == type)\n                {\n                    ALenum format = alGetEnumValue(fmt.name);\n                    if(format != 0 && format != -1)\n                        return format;\n                }\n            }\n        }\n    }\n\n    Log(Debug::Warning) << \"Unsupported sound format (\" << getChannelConfigName(chans) << \", \" << getSampleTypeName(type) << \")\";\n    return AL_NONE;\n}\n\n\n//\n// A streaming OpenAL sound.\n//\nclass OpenAL_SoundStream\n{\n    static const ALfloat sBufferLength;\n\nprivate:\n    ALuint mSource;\n\n    std::array<ALuint,6> mBuffers;\n    ALint mCurrentBufIdx;\n\n    ALenum mFormat;\n    ALsizei mSampleRate;\n    ALuint mBufferSize;\n    ALuint mFrameSize;\n    ALint mSilence;\n\n    DecoderPtr mDecoder;\n\n    std::unique_ptr<Sound_Loudness> mLoudnessAnalyzer;\n\n    std::atomic<bool> mIsFinished;\n\n    void updateAll(bool local);\n\n    OpenAL_SoundStream(const OpenAL_SoundStream &rhs);\n    OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs);\n\n    friend class OpenAL_Output;\n\npublic:\n    OpenAL_SoundStream(ALuint src, DecoderPtr decoder);\n    ~OpenAL_SoundStream();\n\n    bool init(bool getLoudnessData=false);\n\n    bool isPlaying();\n    double getStreamDelay() const;\n    double getStreamOffset() const;\n\n    float getCurrentLoudness() const;\n\n    bool process();\n    ALint refillQueue();\n};\nconst ALfloat OpenAL_SoundStream::sBufferLength = 0.125f;\n\n\n//\n// A background streaming thread (keeps active streams processed)\n//\nstruct OpenAL_Output::StreamThread\n{\n    typedef std::vector<OpenAL_SoundStream*> StreamVec;\n    StreamVec mStreams;\n\n    std::atomic<bool> mQuitNow;\n    std::mutex mMutex;\n    std::condition_variable mCondVar;\n    std::thread mThread;\n\n    StreamThread()\n      : mQuitNow(false)\n      , mThread([this] { run(); })\n    {\n    }\n    ~StreamThread()\n    {\n        mQuitNow = true;\n        mMutex.lock(); mMutex.unlock();\n        mCondVar.notify_all();\n        mThread.join();\n    }\n\n    // thread entry point\n    void run()\n    {\n        std::unique_lock<std::mutex> lock(mMutex);\n        while(!mQuitNow)\n        {\n            StreamVec::iterator iter = mStreams.begin();\n            while(iter != mStreams.end())\n            {\n                if((*iter)->process() == false)\n                    iter = mStreams.erase(iter);\n                else\n                    ++iter;\n            }\n\n            mCondVar.wait_for(lock, std::chrono::milliseconds(50));\n        }\n    }\n\n    void add(OpenAL_SoundStream *stream)\n    {\n        std::lock_guard<std::mutex> lock(mMutex);\n        if(std::find(mStreams.begin(), mStreams.end(), stream) == mStreams.end())\n        {\n            mStreams.push_back(stream);\n            mCondVar.notify_all();\n        }\n    }\n\n    void remove(OpenAL_SoundStream *stream)\n    {\n        std::lock_guard<std::mutex> lock(mMutex);\n        StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream);\n        if(iter != mStreams.end()) mStreams.erase(iter);\n    }\n\n    void removeAll()\n    {\n        std::lock_guard<std::mutex> lock(mMutex);\n        mStreams.clear();\n    }\n\nprivate:\n    StreamThread(const StreamThread &rhs);\n    StreamThread& operator=(const StreamThread &rhs);\n};\n\n\nOpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder)\n  : mSource(src), mCurrentBufIdx(0), mFormat(AL_NONE), mSampleRate(0)\n  , mBufferSize(0), mFrameSize(0), mSilence(0), mDecoder(std::move(decoder))\n  , mLoudnessAnalyzer(nullptr), mIsFinished(true)\n{\n    mBuffers.fill(0);\n}\n\nOpenAL_SoundStream::~OpenAL_SoundStream()\n{\n    if(mBuffers[0] && alIsBuffer(mBuffers[0]))\n        alDeleteBuffers(mBuffers.size(), mBuffers.data());\n    alGetError();\n\n    mDecoder->close();\n}\n\nbool OpenAL_SoundStream::init(bool getLoudnessData)\n{\n    alGenBuffers(mBuffers.size(), mBuffers.data());\n    ALenum err = getALError();\n    if(err != AL_NO_ERROR)\n        return false;\n\n    ChannelConfig chans;\n    SampleType type;\n\n    try {\n        mDecoder->getInfo(&mSampleRate, &chans, &type);\n        mFormat = getALFormat(chans, type);\n    }\n    catch(std::exception &e)\n    {\n        Log(Debug::Error) << \"Failed to get stream info: \" << e.what();\n        return false;\n    }\n\n    switch(type)\n    {\n        case SampleType_UInt8: mSilence = 0x80; break;\n        case SampleType_Int16: mSilence = 0x00; break;\n        case SampleType_Float32: mSilence = 0x00; break;\n    }\n\n    mFrameSize = framesToBytes(1, chans, type);\n    mBufferSize = static_cast<ALuint>(sBufferLength*mSampleRate);\n    mBufferSize *= mFrameSize;\n\n    if (getLoudnessData)\n        mLoudnessAnalyzer.reset(new Sound_Loudness(sLoudnessFPS, mSampleRate, chans, type));\n\n    mIsFinished = false;\n    return true;\n}\n\nbool OpenAL_SoundStream::isPlaying()\n{\n    ALint state;\n\n    alGetSourcei(mSource, AL_SOURCE_STATE, &state);\n    getALError();\n\n    if(state == AL_PLAYING || state == AL_PAUSED)\n        return true;\n    return !mIsFinished;\n}\n\ndouble OpenAL_SoundStream::getStreamDelay() const\n{\n    ALint state = AL_STOPPED;\n    double d = 0.0;\n    ALint offset;\n\n    alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset);\n    alGetSourcei(mSource, AL_SOURCE_STATE, &state);\n    if(state == AL_PLAYING || state == AL_PAUSED)\n    {\n        ALint queued;\n        alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);\n        ALint inqueue = mBufferSize/mFrameSize*queued - offset;\n        d = (double)inqueue / (double)mSampleRate;\n    }\n\n    getALError();\n    return d;\n}\n\ndouble OpenAL_SoundStream::getStreamOffset() const\n{\n    ALint state = AL_STOPPED;\n    ALint offset;\n    double t;\n\n    alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset);\n    alGetSourcei(mSource, AL_SOURCE_STATE, &state);\n    if(state == AL_PLAYING || state == AL_PAUSED)\n    {\n        ALint queued;\n        alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);\n        ALint inqueue = mBufferSize/mFrameSize*queued - offset;\n        t = (double)(mDecoder->getSampleOffset() - inqueue) / (double)mSampleRate;\n    }\n    else\n    {\n        /* Underrun, or not started yet. The decoder offset is where we'll play\n         * next. */\n        t = (double)mDecoder->getSampleOffset() / (double)mSampleRate;\n    }\n\n    getALError();\n    return t;\n}\n\nfloat OpenAL_SoundStream::getCurrentLoudness() const\n{\n    if (!mLoudnessAnalyzer.get())\n        return 0.f;\n\n    float time = getStreamOffset();\n    return mLoudnessAnalyzer->getLoudnessAtTime(time);\n}\n\nbool OpenAL_SoundStream::process()\n{\n    try {\n        if(refillQueue() > 0)\n        {\n            ALint state;\n            alGetSourcei(mSource, AL_SOURCE_STATE, &state);\n            if(state != AL_PLAYING && state != AL_PAUSED)\n            {\n                // Ensure all processed buffers are removed so we don't replay them.\n                refillQueue();\n\n                alSourcePlay(mSource);\n            }\n        }\n    }\n    catch(std::exception&) {\n        Log(Debug::Error) << \"Error updating stream \\\"\" << mDecoder->getName() << \"\\\"\";\n        mIsFinished = true;\n    }\n    return !mIsFinished;\n}\n\nALint OpenAL_SoundStream::refillQueue()\n{\n    ALint processed;\n    alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);\n    while(processed > 0)\n    {\n        ALuint buf;\n        alSourceUnqueueBuffers(mSource, 1, &buf);\n        --processed;\n    }\n\n    ALint queued;\n    alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);\n    if(!mIsFinished && (ALuint)queued < mBuffers.size())\n    {\n        std::vector<char> data(mBufferSize);\n        for(;!mIsFinished && (ALuint)queued < mBuffers.size();++queued)\n        {\n            size_t got = mDecoder->read(data.data(), data.size());\n            if(got < data.size())\n            {\n                mIsFinished = true;\n                std::fill(data.begin()+got, data.end(), mSilence);\n            }\n            if(got > 0)\n            {\n                if (mLoudnessAnalyzer.get())\n                    mLoudnessAnalyzer->analyzeLoudness(data);\n\n                ALuint bufid = mBuffers[mCurrentBufIdx];\n                alBufferData(bufid, mFormat, data.data(), data.size(), mSampleRate);\n                alSourceQueueBuffers(mSource, 1, &bufid);\n                mCurrentBufIdx = (mCurrentBufIdx+1) % mBuffers.size();\n            }\n        }\n    }\n\n    return queued;\n}\n\n\n//\n// An OpenAL output device\n//\nstd::vector<std::string> OpenAL_Output::enumerate()\n{\n    std::vector<std::string> devlist;\n    const ALCchar *devnames;\n\n    if(alcIsExtensionPresent(nullptr, \"ALC_ENUMERATE_ALL_EXT\"))\n        devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);\n    else\n        devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);\n    while(devnames && *devnames)\n    {\n        devlist.emplace_back(devnames);\n        devnames += strlen(devnames)+1;\n    }\n    return devlist;\n}\n\nbool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname, HrtfMode hrtfmode)\n{\n    deinit();\n\n    Log(Debug::Info) << \"Initializing OpenAL...\";\n\n    mDevice = alcOpenDevice(devname.c_str());\n    if(!mDevice && !devname.empty())\n    {\n        Log(Debug::Warning) << \"Failed to open \\\"\" << devname << \"\\\", trying default\";\n        mDevice = alcOpenDevice(nullptr);\n    }\n\n    if(!mDevice)\n    {\n        Log(Debug::Error) << \"Failed to open default audio device\";\n        return false;\n    }\n\n    const ALCchar *name = nullptr;\n    if(alcIsExtensionPresent(mDevice, \"ALC_ENUMERATE_ALL_EXT\"))\n        name = alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER);\n    if(alcGetError(mDevice) != AL_NO_ERROR || !name)\n        name = alcGetString(mDevice, ALC_DEVICE_SPECIFIER);\n    Log(Debug::Info) << \"Opened \\\"\" << name << \"\\\"\";\n\n    ALCint major=0, minor=0;\n    alcGetIntegerv(mDevice, ALC_MAJOR_VERSION, 1, &major);\n    alcGetIntegerv(mDevice, ALC_MINOR_VERSION, 1, &minor);\n    Log(Debug::Info) << \"  ALC Version: \" << major << \".\" << minor <<\"\\n\" <<\n                        \"  ALC Extensions: \" << alcGetString(mDevice, ALC_EXTENSIONS);\n\n    ALC.EXT_EFX = alcIsExtensionPresent(mDevice, \"ALC_EXT_EFX\");\n    ALC.SOFT_HRTF = alcIsExtensionPresent(mDevice, \"ALC_SOFT_HRTF\");\n\n    std::vector<ALCint> attrs;\n    attrs.reserve(15);\n    if(ALC.SOFT_HRTF)\n    {\n        LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;\n        getALCFunc(alcGetStringiSOFT, mDevice, \"alcGetStringiSOFT\");\n\n        attrs.push_back(ALC_HRTF_SOFT);\n        attrs.push_back(hrtfmode == HrtfMode::Disable ? ALC_FALSE :\n                        hrtfmode == HrtfMode::Enable ? ALC_TRUE :\n                        /*hrtfmode == HrtfMode::Auto ?*/ ALC_DONT_CARE_SOFT);\n        if(!hrtfname.empty())\n        {\n            ALCint index = -1;\n            ALCint num_hrtf;\n            alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);\n            for(ALCint i = 0;i < num_hrtf;++i)\n            {\n                const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i);\n                if(hrtfname == entry)\n                {\n                    index = i;\n                    break;\n                }\n            }\n\n            if(index < 0)\n                Log(Debug::Warning) << \"Failed to find HRTF \\\"\" << hrtfname << \"\\\", using default\";\n            else\n            {\n                attrs.push_back(ALC_HRTF_ID_SOFT);\n                attrs.push_back(index);\n            }\n        }\n    }\n    attrs.push_back(0);\n\n    mContext = alcCreateContext(mDevice, attrs.data());\n    if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE)\n    {\n        Log(Debug::Error) << \"Failed to setup audio context: \"<<alcGetString(mDevice, alcGetError(mDevice));\n        if(mContext)\n            alcDestroyContext(mContext);\n        mContext = nullptr;\n        alcCloseDevice(mDevice);\n        mDevice = nullptr;\n        return false;\n    }\n\n    Log(Debug::Info) << \"  Vendor: \"<<alGetString(AL_VENDOR)<<\"\\n\"<<\n                        \"  Renderer: \"<<alGetString(AL_RENDERER)<<\"\\n\"<<\n                        \"  Version: \"<<alGetString(AL_VERSION)<<\"\\n\"<<\n                        \"  Extensions: \"<<alGetString(AL_EXTENSIONS);\n\n    if(!ALC.SOFT_HRTF)\n        Log(Debug::Warning) << \"HRTF status unavailable\";\n    else\n    {\n        ALCint hrtf_state;\n        alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state);\n        if(!hrtf_state)\n            Log(Debug::Info) << \"HRTF disabled\";\n        else\n        {\n            const ALCchar *hrtf = alcGetString(mDevice, ALC_HRTF_SPECIFIER_SOFT);\n            Log(Debug::Info) << \"Enabled HRTF \" << hrtf;\n        }\n    }\n\n    AL.SOFT_source_spatialize = alIsExtensionPresent(\"AL_SOFT_source_spatialize\");\n\n    ALCuint maxtotal;\n    ALCint maxmono = 0, maxstereo = 0;\n    alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono);\n    alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo);\n    if(getALCError(mDevice) != ALC_NO_ERROR)\n        maxtotal = 256;\n    else\n    {\n        maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256);\n        if (maxtotal == 0) // workaround for broken implementations\n            maxtotal = 256;\n    }\n    for(size_t i = 0;i < maxtotal;i++)\n    {\n        ALuint src = 0;\n        alGenSources(1, &src);\n        if(alGetError() != AL_NO_ERROR)\n            break;\n        mFreeSources.push_back(src);\n    }\n    if(mFreeSources.empty())\n    {\n        Log(Debug::Warning) << \"Could not allocate any sound sourcess\";\n        alcMakeContextCurrent(nullptr);\n        alcDestroyContext(mContext);\n        mContext = nullptr;\n        alcCloseDevice(mDevice);\n        mDevice = nullptr;\n        return false;\n    }\n    Log(Debug::Info) << \"Allocated \" << mFreeSources.size() << \" sound sources\";\n\n    if(ALC.EXT_EFX)\n    {\n#define LOAD_FUNC(x) getALFunc(x, #x)\n        LOAD_FUNC(alGenEffects);\n        LOAD_FUNC(alDeleteEffects);\n        LOAD_FUNC(alIsEffect);\n        LOAD_FUNC(alEffecti);\n        LOAD_FUNC(alEffectiv);\n        LOAD_FUNC(alEffectf);\n        LOAD_FUNC(alEffectfv);\n        LOAD_FUNC(alGetEffecti);\n        LOAD_FUNC(alGetEffectiv);\n        LOAD_FUNC(alGetEffectf);\n        LOAD_FUNC(alGetEffectfv);\n        LOAD_FUNC(alGenFilters);\n        LOAD_FUNC(alDeleteFilters);\n        LOAD_FUNC(alIsFilter);\n        LOAD_FUNC(alFilteri);\n        LOAD_FUNC(alFilteriv);\n        LOAD_FUNC(alFilterf);\n        LOAD_FUNC(alFilterfv);\n        LOAD_FUNC(alGetFilteri);\n        LOAD_FUNC(alGetFilteriv);\n        LOAD_FUNC(alGetFilterf);\n        LOAD_FUNC(alGetFilterfv);\n        LOAD_FUNC(alGenAuxiliaryEffectSlots);\n        LOAD_FUNC(alDeleteAuxiliaryEffectSlots);\n        LOAD_FUNC(alIsAuxiliaryEffectSlot);\n        LOAD_FUNC(alAuxiliaryEffectSloti);\n        LOAD_FUNC(alAuxiliaryEffectSlotiv);\n        LOAD_FUNC(alAuxiliaryEffectSlotf);\n        LOAD_FUNC(alAuxiliaryEffectSlotfv);\n        LOAD_FUNC(alGetAuxiliaryEffectSloti);\n        LOAD_FUNC(alGetAuxiliaryEffectSlotiv);\n        LOAD_FUNC(alGetAuxiliaryEffectSlotf);\n        LOAD_FUNC(alGetAuxiliaryEffectSlotfv);\n#undef LOAD_FUNC\n        if(getALError() != AL_NO_ERROR)\n        {\n            ALC.EXT_EFX = false;\n            goto skip_efx;\n        }\n\n        alGenFilters(1, &mWaterFilter);\n        if(alGetError() == AL_NO_ERROR)\n        {\n            alFilteri(mWaterFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);\n            if(alGetError() == AL_NO_ERROR)\n            {\n                Log(Debug::Info) << \"Low-pass filter supported\";\n                alFilterf(mWaterFilter, AL_LOWPASS_GAIN, 0.9f);\n                alFilterf(mWaterFilter, AL_LOWPASS_GAINHF, 0.125f);\n            }\n            else\n            {\n                alDeleteFilters(1, &mWaterFilter);\n                mWaterFilter = 0;\n            }\n            alGetError();\n        }\n\n        alGenAuxiliaryEffectSlots(1, &mEffectSlot);\n        alGetError();\n\n        alGenEffects(1, &mDefaultEffect);\n        if(alGetError() == AL_NO_ERROR)\n        {\n            alEffecti(mDefaultEffect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);\n            if(alGetError() == AL_NO_ERROR)\n                Log(Debug::Info) << \"EAX Reverb supported\";\n            else\n            {\n                alEffecti(mDefaultEffect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);\n                if(alGetError() == AL_NO_ERROR)\n                    Log(Debug::Info) << \"Standard Reverb supported\";\n            }\n            EFXEAXREVERBPROPERTIES props = EFX_REVERB_PRESET_LIVINGROOM;\n            props.flGain = 0.0f;\n            LoadEffect(mDefaultEffect, props);\n        }\n\n        alGenEffects(1, &mWaterEffect);\n        if(alGetError() == AL_NO_ERROR)\n        {\n            alEffecti(mWaterEffect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);\n            if(alGetError() != AL_NO_ERROR)\n            {\n                alEffecti(mWaterEffect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);\n                alGetError();\n            }\n            LoadEffect(mWaterEffect, EFX_REVERB_PRESET_UNDERWATER);\n        }\n\n        alListenerf(AL_METERS_PER_UNIT, 1.0f / Constants::UnitsPerMeter);\n    }\nskip_efx:\n    alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);\n    // Speed of sound is in units per second. Take the sound speed in air (assumed\n    // meters per second), multiply by the units per meter to get the speed in u/s.\n    alSpeedOfSound(Constants::SoundSpeedInAir * Constants::UnitsPerMeter);\n    alGetError();\n\n    mInitialized = true;\n    return true;\n}\n\nvoid OpenAL_Output::deinit()\n{\n    mStreamThread->removeAll();\n\n    for(ALuint source : mFreeSources)\n        alDeleteSources(1, &source);\n    mFreeSources.clear();\n\n    if(mEffectSlot)\n        alDeleteAuxiliaryEffectSlots(1, &mEffectSlot);\n    mEffectSlot = 0;\n    if(mDefaultEffect)\n        alDeleteEffects(1, &mDefaultEffect);\n    mDefaultEffect = 0;\n    if(mWaterEffect)\n        alDeleteEffects(1, &mWaterEffect);\n    mWaterEffect = 0;\n    if(mWaterFilter)\n        alDeleteFilters(1, &mWaterFilter);\n    mWaterFilter = 0;\n\n    alcMakeContextCurrent(nullptr);\n    if(mContext)\n        alcDestroyContext(mContext);\n    mContext = nullptr;\n    if(mDevice)\n        alcCloseDevice(mDevice);\n    mDevice = nullptr;\n\n    mInitialized = false;\n}\n\n\nstd::vector<std::string> OpenAL_Output::enumerateHrtf()\n{\n    std::vector<std::string> ret;\n\n    if(!mDevice || !ALC.SOFT_HRTF)\n        return ret;\n\n    LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;\n    getALCFunc(alcGetStringiSOFT, mDevice, \"alcGetStringiSOFT\");\n\n    ALCint num_hrtf;\n    alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);\n    ret.reserve(num_hrtf);\n    for(ALCint i = 0;i < num_hrtf;++i)\n    {\n        const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i);\n        ret.emplace_back(entry);\n    }\n\n    return ret;\n}\n\nvoid OpenAL_Output::setHrtf(const std::string &hrtfname, HrtfMode hrtfmode)\n{\n    if(!mDevice || !ALC.SOFT_HRTF)\n    {\n        Log(Debug::Info) << \"HRTF extension not present\";\n        return;\n    }\n\n    LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;\n    getALCFunc(alcGetStringiSOFT, mDevice, \"alcGetStringiSOFT\");\n\n    LPALCRESETDEVICESOFT alcResetDeviceSOFT = nullptr;\n    getALCFunc(alcResetDeviceSOFT, mDevice, \"alcResetDeviceSOFT\");\n\n    std::vector<ALCint> attrs;\n    attrs.reserve(15);\n\n    attrs.push_back(ALC_HRTF_SOFT);\n    attrs.push_back(hrtfmode == HrtfMode::Disable ? ALC_FALSE :\n                    hrtfmode == HrtfMode::Enable ? ALC_TRUE :\n                    /*hrtfmode == HrtfMode::Auto ?*/ ALC_DONT_CARE_SOFT);\n    if(!hrtfname.empty())\n    {\n        ALCint index = -1;\n        ALCint num_hrtf;\n        alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);\n        for(ALCint i = 0;i < num_hrtf;++i)\n        {\n            const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i);\n            if(hrtfname == entry)\n            {\n                index = i;\n                break;\n            }\n        }\n\n        if(index < 0)\n            Log(Debug::Warning) << \"Failed to find HRTF name \\\"\" << hrtfname << \"\\\", using default\";\n        else\n        {\n            attrs.push_back(ALC_HRTF_ID_SOFT);\n            attrs.push_back(index);\n        }\n    }\n    attrs.push_back(0);\n    alcResetDeviceSOFT(mDevice, attrs.data());\n\n    ALCint hrtf_state;\n    alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state);\n    if(!hrtf_state)\n        Log(Debug::Info) << \"HRTF disabled\";\n    else\n    {\n        const ALCchar *hrtf = alcGetString(mDevice, ALC_HRTF_SPECIFIER_SOFT);\n        Log(Debug::Info) << \"Enabled HRTF \" << hrtf;\n    }\n}\n\n\nstd::pair<Sound_Handle,size_t> OpenAL_Output::loadSound(const std::string &fname)\n{\n    getALError();\n\n    std::vector<char> data;\n    ALenum format = AL_NONE;\n    int srate = 0;\n\n    try\n    {\n        DecoderPtr decoder = mManager.getDecoder();\n        // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.\n        if(decoder->mResourceMgr->exists(fname))\n            decoder->open(fname);\n        else\n        {\n            std::string file = fname;\n            std::string::size_type pos = file.rfind('.');\n            if(pos != std::string::npos)\n                file = file.substr(0, pos)+\".mp3\";\n            decoder->open(file);\n        }\n\n        ChannelConfig chans;\n        SampleType type;\n        decoder->getInfo(&srate, &chans, &type);\n        format = getALFormat(chans, type);\n        if(format) decoder->readAll(data);\n    }\n    catch(std::exception &e)\n    {\n        Log(Debug::Error) << \"Failed to load audio from \" << fname << \": \" << e.what();\n    }\n\n    if(data.empty())\n    {\n        // If we failed to get any usable audio, substitute with silence.\n        format = AL_FORMAT_MONO8;\n        srate = 8000;\n        data.assign(8000, -128);\n    }\n\n    ALint size;\n    ALuint buf = 0;\n    alGenBuffers(1, &buf);\n    alBufferData(buf, format, data.data(), data.size(), srate);\n    alGetBufferi(buf, AL_SIZE, &size);\n    if(getALError() != AL_NO_ERROR)\n    {\n        if(buf && alIsBuffer(buf))\n            alDeleteBuffers(1, &buf);\n        getALError();\n        return std::make_pair(nullptr, 0);\n    }\n    return std::make_pair(MAKE_PTRID(buf), size);\n}\n\nsize_t OpenAL_Output::unloadSound(Sound_Handle data)\n{\n    ALuint buffer = GET_PTRID(data);\n    if(!buffer) return 0;\n\n    // Make sure no sources are playing this buffer before unloading it.\n    SoundVec::const_iterator iter = mActiveSounds.begin();\n    for(;iter != mActiveSounds.end();++iter)\n    {\n        if(!(*iter)->mHandle)\n            continue;\n\n        ALuint source = GET_PTRID((*iter)->mHandle);\n        ALint srcbuf;\n        alGetSourcei(source, AL_BUFFER, &srcbuf);\n        if((ALuint)srcbuf == buffer)\n        {\n            alSourceStop(source);\n            alSourcei(source, AL_BUFFER, 0);\n        }\n    }\n    ALint size = 0;\n    alGetBufferi(buffer, AL_SIZE, &size);\n    alDeleteBuffers(1, &buffer);\n    getALError();\n    return size;\n}\n\n\nvoid OpenAL_Output::initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv)\n{\n    alSourcef(source, AL_REFERENCE_DISTANCE, 1.0f);\n    alSourcef(source, AL_MAX_DISTANCE, 1000.0f);\n    alSourcef(source, AL_ROLLOFF_FACTOR, 0.0f);\n    alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);\n    alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);\n    if(AL.SOFT_source_spatialize)\n        alSourcei(source, AL_SOURCE_SPATIALIZE_SOFT, AL_FALSE);\n\n    if(useenv)\n    {\n        if(mWaterFilter)\n            alSourcei(source, AL_DIRECT_FILTER,\n                (mListenerEnv == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL\n            );\n        else if(mListenerEnv == Env_Underwater)\n        {\n            gain *= 0.9f;\n            pitch *= 0.7f;\n        }\n        if(mEffectSlot)\n            alSource3i(source, AL_AUXILIARY_SEND_FILTER, mEffectSlot, 0, AL_FILTER_NULL);\n    }\n    else\n    {\n        if(mWaterFilter)\n            alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);\n        if(mEffectSlot)\n            alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);\n    }\n\n    alSourcef(source, AL_GAIN, gain);\n    alSourcef(source, AL_PITCH, pitch);\n    alSourcefv(source, AL_POSITION, pos.ptr());\n    alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);\n    alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);\n}\n\nvoid OpenAL_Output::initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv)\n{\n    alSourcef(source, AL_REFERENCE_DISTANCE, mindist);\n    alSourcef(source, AL_MAX_DISTANCE, maxdist);\n    alSourcef(source, AL_ROLLOFF_FACTOR, 1.0f);\n    alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);\n    alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);\n    if(AL.SOFT_source_spatialize)\n        alSourcei(source, AL_SOURCE_SPATIALIZE_SOFT, AL_TRUE);\n\n    if((pos - mListenerPos).length2() > maxdist*maxdist)\n        gain = 0.0f;\n    if(useenv)\n    {\n        if(mWaterFilter)\n            alSourcei(source, AL_DIRECT_FILTER,\n                (mListenerEnv == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL\n            );\n        else if(mListenerEnv == Env_Underwater)\n        {\n            gain *= 0.9f;\n            pitch *= 0.7f;\n        }\n        if(mEffectSlot)\n            alSource3i(source, AL_AUXILIARY_SEND_FILTER, mEffectSlot, 0, AL_FILTER_NULL);\n    }\n    else\n    {\n        if(mWaterFilter)\n            alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);\n        if(mEffectSlot)\n            alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);\n    }\n\n    alSourcef(source, AL_GAIN, gain);\n    alSourcef(source, AL_PITCH, pitch);\n    alSourcefv(source, AL_POSITION, pos.ptr());\n    alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);\n    alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);\n}\n\nvoid OpenAL_Output::updateCommon(ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d)\n{\n    if(is3d)\n    {\n        if((pos - mListenerPos).length2() > maxdist*maxdist)\n            gain = 0.0f;\n    }\n    if(useenv && mListenerEnv == Env_Underwater && !mWaterFilter)\n    {\n        gain *= 0.9f;\n        pitch *= 0.7f;\n    }\n\n    alSourcef(source, AL_GAIN, gain);\n    alSourcef(source, AL_PITCH, pitch);\n    alSourcefv(source, AL_POSITION, pos.ptr());\n    alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);\n    alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);\n}\n\n\nbool OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset)\n{\n    ALuint source;\n\n    if(mFreeSources.empty())\n    {\n        Log(Debug::Warning) << \"No free sources!\";\n        return false;\n    }\n    source = mFreeSources.front();\n\n    initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(),\n                 sound->getIsLooping(), sound->getUseEnv());\n    alSourcei(source, AL_BUFFER, GET_PTRID(data));\n    alSourcef(source, AL_SEC_OFFSET, offset);\n    if(getALError() != AL_NO_ERROR)\n    {\n        alSourceRewind(source);\n        alSourcei(source, AL_BUFFER, 0);\n        alGetError();\n        return false;\n    }\n\n    alSourcePlay(source);\n    if(getALError() != AL_NO_ERROR)\n    {\n        alSourceRewind(source);\n        alSourcei(source, AL_BUFFER, 0);\n        alGetError();\n        return false;\n    }\n\n    mFreeSources.pop_front();\n    sound->mHandle = MAKE_PTRID(source);\n    mActiveSounds.push_back(sound);\n\n    return true;\n}\n\nbool OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset)\n{\n    ALuint source;\n\n    if(mFreeSources.empty())\n    {\n        Log(Debug::Warning) << \"No free sources!\";\n        return false;\n    }\n    source = mFreeSources.front();\n\n    initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),\n                 sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(),\n                 sound->getUseEnv());\n    alSourcei(source, AL_BUFFER, GET_PTRID(data));\n    alSourcef(source, AL_SEC_OFFSET, offset);\n    if(getALError() != AL_NO_ERROR)\n    {\n        alSourceRewind(source);\n        alSourcei(source, AL_BUFFER, 0);\n        alGetError();\n        return false;\n    }\n\n    alSourcePlay(source);\n    if(getALError() != AL_NO_ERROR)\n    {\n        alSourceRewind(source);\n        alSourcei(source, AL_BUFFER, 0);\n        alGetError();\n        return false;\n    }\n\n    mFreeSources.pop_front();\n    sound->mHandle = MAKE_PTRID(source);\n    mActiveSounds.push_back(sound);\n\n    return true;\n}\n\nvoid OpenAL_Output::finishSound(Sound *sound)\n{\n    if(!sound->mHandle) return;\n    ALuint source = GET_PTRID(sound->mHandle);\n    sound->mHandle = nullptr;\n\n    // Rewind the stream to put the source back into an AL_INITIAL state, for\n    // the next time it's used.\n    alSourceRewind(source);\n    alSourcei(source, AL_BUFFER, 0);\n    getALError();\n\n    mFreeSources.push_back(source);\n    mActiveSounds.erase(std::find(mActiveSounds.begin(), mActiveSounds.end(), sound));\n}\n\nbool OpenAL_Output::isSoundPlaying(Sound *sound)\n{\n    if(!sound->mHandle) return false;\n    ALuint source = GET_PTRID(sound->mHandle);\n    ALint state = AL_STOPPED;\n\n    alGetSourcei(source, AL_SOURCE_STATE, &state);\n    getALError();\n\n    return state == AL_PLAYING || state == AL_PAUSED;\n}\n\nvoid OpenAL_Output::updateSound(Sound *sound)\n{\n    if(!sound->mHandle) return;\n    ALuint source = GET_PTRID(sound->mHandle);\n\n    updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(),\n                 sound->getPitch(), sound->getUseEnv(), sound->getIs3D());\n    getALError();\n}\n\n\nbool OpenAL_Output::streamSound(DecoderPtr decoder, Stream *sound, bool getLoudnessData)\n{\n    if(mFreeSources.empty())\n    {\n        Log(Debug::Warning) << \"No free sources!\";\n        return false;\n    }\n    ALuint source = mFreeSources.front();\n\n    if(sound->getIsLooping())\n        Log(Debug::Warning) << \"Warning: cannot loop stream \\\"\" << decoder->getName() << \"\\\"\";\n\n    initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(),\n                 false, sound->getUseEnv());\n    if(getALError() != AL_NO_ERROR)\n        return false;\n\n    OpenAL_SoundStream *stream = new OpenAL_SoundStream(source, std::move(decoder));\n    if(!stream->init(getLoudnessData))\n    {\n        delete stream;\n        return false;\n    }\n    mStreamThread->add(stream);\n\n    mFreeSources.pop_front();\n    sound->mHandle = stream;\n    mActiveStreams.push_back(sound);\n    return true;\n}\n\nbool OpenAL_Output::streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData)\n{\n    if(mFreeSources.empty())\n    {\n        Log(Debug::Warning) << \"No free sources!\";\n        return false;\n    }\n    ALuint source = mFreeSources.front();\n\n    if(sound->getIsLooping())\n        Log(Debug::Warning) << \"Warning: cannot loop stream \\\"\" << decoder->getName() << \"\\\"\";\n\n    initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),\n                 sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv());\n    if(getALError() != AL_NO_ERROR)\n        return false;\n\n    OpenAL_SoundStream *stream = new OpenAL_SoundStream(source, std::move(decoder));\n    if(!stream->init(getLoudnessData))\n    {\n        delete stream;\n        return false;\n    }\n    mStreamThread->add(stream);\n\n    mFreeSources.pop_front();\n    sound->mHandle = stream;\n    mActiveStreams.push_back(sound);\n    return true;\n}\n\nvoid OpenAL_Output::finishStream(Stream *sound)\n{\n    if(!sound->mHandle) return;\n    OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);\n    ALuint source = stream->mSource;\n\n    sound->mHandle = nullptr;\n    mStreamThread->remove(stream);\n\n    // Rewind the stream to put the source back into an AL_INITIAL state, for\n    // the next time it's used.\n    alSourceRewind(source);\n    alSourcei(source, AL_BUFFER, 0);\n    getALError();\n\n    mFreeSources.push_back(source);\n    mActiveStreams.erase(std::find(mActiveStreams.begin(), mActiveStreams.end(), sound));\n\n    delete stream;\n}\n\ndouble OpenAL_Output::getStreamDelay(Stream *sound)\n{\n    if(!sound->mHandle) return 0.0;\n    OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);\n    return stream->getStreamDelay();\n}\n\ndouble OpenAL_Output::getStreamOffset(Stream *sound)\n{\n    if(!sound->mHandle) return 0.0;\n    OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);\n    std::lock_guard<std::mutex> lock(mStreamThread->mMutex);\n    return stream->getStreamOffset();\n}\n\nfloat OpenAL_Output::getStreamLoudness(Stream *sound)\n{\n    if(!sound->mHandle) return 0.0;\n    OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);\n    std::lock_guard<std::mutex> lock(mStreamThread->mMutex);\n    return stream->getCurrentLoudness();\n}\n\nbool OpenAL_Output::isStreamPlaying(Stream *sound)\n{\n    if(!sound->mHandle) return false;\n    OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);\n    std::lock_guard<std::mutex> lock(mStreamThread->mMutex);\n    return stream->isPlaying();\n}\n\nvoid OpenAL_Output::updateStream(Stream *sound)\n{\n    if(!sound->mHandle) return;\n    OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);\n    ALuint source = stream->mSource;\n\n    updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(),\n                 sound->getPitch(), sound->getUseEnv(), sound->getIs3D());\n    getALError();\n}\n\n\nvoid OpenAL_Output::startUpdate()\n{\n    alcSuspendContext(alcGetCurrentContext());\n}\n\nvoid OpenAL_Output::finishUpdate()\n{\n    alcProcessContext(alcGetCurrentContext());\n}\n\n\nvoid OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env)\n{\n    if(mContext)\n    {\n        ALfloat orient[6] = {\n            atdir.x(), atdir.y(), atdir.z(),\n            updir.x(), updir.y(), updir.z()\n        };\n        alListenerfv(AL_POSITION, pos.ptr());\n        alListenerfv(AL_ORIENTATION, orient);\n\n        if(env != mListenerEnv)\n        {\n            alSpeedOfSound(((env == Env_Underwater) ? Constants::SoundSpeedUnderwater : Constants::SoundSpeedInAir) * Constants::UnitsPerMeter);\n\n            // Update active sources with the environment's direct filter\n            if(mWaterFilter)\n            {\n                ALuint filter = (env == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL;\n                for(Sound *sound : mActiveSounds)\n                {\n                    if(sound->getUseEnv())\n                        alSourcei(GET_PTRID(sound->mHandle), AL_DIRECT_FILTER, filter);\n                }\n                for(Stream *sound : mActiveStreams)\n                {\n                    if(sound->getUseEnv())\n                        alSourcei(\n                            reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle)->mSource,\n                            AL_DIRECT_FILTER, filter\n                        );\n                }\n            }\n            // Update the environment effect\n            if(mEffectSlot)\n                alAuxiliaryEffectSloti(mEffectSlot, AL_EFFECTSLOT_EFFECT,\n                    (env == Env_Underwater) ? mWaterEffect : mDefaultEffect\n                );\n        }\n        getALError();\n    }\n\n    mListenerPos = pos;\n    mListenerEnv = env;\n}\n\n\nvoid OpenAL_Output::pauseSounds(int types)\n{\n    std::vector<ALuint> sources;\n    for(Sound *sound : mActiveSounds)\n    {\n        if((types&sound->getPlayType()))\n            sources.push_back(GET_PTRID(sound->mHandle));\n    }\n    for(Stream *sound : mActiveStreams)\n    {\n        if((types&sound->getPlayType()))\n        {\n            OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);\n            sources.push_back(stream->mSource);\n        }\n    }\n    if(!sources.empty())\n    {\n        alSourcePausev(sources.size(), sources.data());\n        getALError();\n    }\n}\n\nvoid OpenAL_Output::pauseActiveDevice()\n{\n    if (mDevice == nullptr)\n        return;\n\n    if(alcIsExtensionPresent(mDevice, \"ALC_SOFT_PAUSE_DEVICE\"))\n    {\n        LPALCDEVICEPAUSESOFT alcDevicePauseSOFT = nullptr;\n        getALCFunc(alcDevicePauseSOFT, mDevice, \"alcDevicePauseSOFT\");\n        alcDevicePauseSOFT(mDevice);\n        getALCError(mDevice);\n    }\n\n    alListenerf(AL_GAIN, 0.0f);\n}\n\nvoid OpenAL_Output::resumeActiveDevice()\n{\n    if (mDevice == nullptr)\n        return;\n\n    if(alcIsExtensionPresent(mDevice, \"ALC_SOFT_PAUSE_DEVICE\"))\n    {\n        LPALCDEVICERESUMESOFT alcDeviceResumeSOFT = nullptr;\n        getALCFunc(alcDeviceResumeSOFT, mDevice, \"alcDeviceResumeSOFT\");\n        alcDeviceResumeSOFT(mDevice);\n        getALCError(mDevice);\n    }\n\n    alListenerf(AL_GAIN, 1.0f);\n}\n\nvoid OpenAL_Output::resumeSounds(int types)\n{\n    std::vector<ALuint> sources;\n    for(Sound *sound : mActiveSounds)\n    {\n        if((types&sound->getPlayType()))\n            sources.push_back(GET_PTRID(sound->mHandle));\n    }\n    for(Stream *sound : mActiveStreams)\n    {\n        if((types&sound->getPlayType()))\n        {\n            OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);\n            sources.push_back(stream->mSource);\n        }\n    }\n    if(!sources.empty())\n    {\n        alSourcePlayv(sources.size(), sources.data());\n        getALError();\n    }\n}\n\n\nOpenAL_Output::OpenAL_Output(SoundManager &mgr)\n  : Sound_Output(mgr)\n  , mDevice(nullptr), mContext(nullptr)\n  , mListenerPos(0.0f, 0.0f, 0.0f), mListenerEnv(Env_Normal)\n  , mWaterFilter(0), mWaterEffect(0), mDefaultEffect(0), mEffectSlot(0)\n  , mStreamThread(new StreamThread)\n{\n}\n\nOpenAL_Output::~OpenAL_Output()\n{\n    OpenAL_Output::deinit();\n}\n\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/openal_output.hpp",
    "content": "#ifndef GAME_SOUND_OPENAL_OUTPUT_H\n#define GAME_SOUND_OPENAL_OUTPUT_H\n\n#include <string>\n#include <vector>\n#include <map>\n#include <deque>\n\n#include \"alc.h\"\n#include \"al.h\"\n#include \"alext.h\"\n\n#include \"sound_output.hpp\"\n\nnamespace MWSound\n{\n    class SoundManager;\n    class Sound;\n    class Stream;\n\n    class OpenAL_Output : public Sound_Output\n    {\n        ALCdevice *mDevice;\n        ALCcontext *mContext;\n\n        struct {\n            bool EXT_EFX : 1;\n            bool SOFT_HRTF : 1;\n        } ALC = {false, false};\n        struct {\n            bool SOFT_source_spatialize : 1;\n        } AL = {false};\n\n        typedef std::deque<ALuint> IDDq;\n        IDDq mFreeSources;\n\n        typedef std::vector<Sound*> SoundVec;\n        SoundVec mActiveSounds;\n        typedef std::vector<Stream*> StreamVec;\n        StreamVec mActiveStreams;\n\n        osg::Vec3f mListenerPos;\n        Environment mListenerEnv;\n\n        ALuint mWaterFilter;\n        ALuint mWaterEffect;\n        ALuint mDefaultEffect;\n        ALuint mEffectSlot;\n\n        struct StreamThread;\n        std::unique_ptr<StreamThread> mStreamThread;\n\n        void initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv);\n        void initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv);\n\n        void updateCommon(ALuint source, const osg::Vec3f &pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d);\n\n        OpenAL_Output& operator=(const OpenAL_Output &rhs);\n        OpenAL_Output(const OpenAL_Output &rhs);\n\n    public:\n        std::vector<std::string> enumerate() override;\n        bool init(const std::string &devname, const std::string &hrtfname, HrtfMode hrtfmode) override;\n        void deinit() override;\n\n        std::vector<std::string> enumerateHrtf() override;\n        void setHrtf(const std::string &hrtfname, HrtfMode hrtfmode) override;\n\n        std::pair<Sound_Handle,size_t> loadSound(const std::string &fname) override;\n        size_t unloadSound(Sound_Handle data) override;\n\n        bool playSound(Sound *sound, Sound_Handle data, float offset) override;\n        bool playSound3D(Sound *sound, Sound_Handle data, float offset) override;\n        void finishSound(Sound *sound) override;\n        bool isSoundPlaying(Sound *sound) override;\n        void updateSound(Sound *sound) override;\n\n        bool streamSound(DecoderPtr decoder, Stream *sound, bool getLoudnessData=false) override;\n        bool streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData) override;\n        void finishStream(Stream *sound) override;\n        double getStreamDelay(Stream *sound) override;\n        double getStreamOffset(Stream *sound) override;\n        float getStreamLoudness(Stream *sound) override;\n        bool isStreamPlaying(Stream *sound) override;\n        void updateStream(Stream *sound) override;\n\n        void startUpdate() override;\n        void finishUpdate() override;\n\n        void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) override;\n\n        void pauseSounds(int types) override;\n        void resumeSounds(int types) override;\n\n        void pauseActiveDevice() override;\n        void resumeActiveDevice() override;\n\n        OpenAL_Output(SoundManager &mgr);\n        virtual ~OpenAL_Output();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/regionsoundselector.cpp",
    "content": "#include \"regionsoundselector.hpp\"\n\n#include <components/fallback/fallback.hpp>\n#include <components/misc/rng.hpp>\n\n#include <algorithm>\n#include <numeric>\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\nnamespace MWSound\n{\n    namespace\n    {\n        int addChance(int result, const ESM::Region::SoundRef &v)\n        {\n            return result + v.mChance;\n        }\n    }\n\n    RegionSoundSelector::RegionSoundSelector()\n    : mMinTimeBetweenSounds(Fallback::Map::getFloat(\"Weather_Minimum_Time_Between_Environmental_Sounds\"))\n    , mMaxTimeBetweenSounds(Fallback::Map::getFloat(\"Weather_Maximum_Time_Between_Environmental_Sounds\"))\n    {}\n\n    std::optional<std::string> RegionSoundSelector::getNextRandom(float duration, const std::string& regionName,\n                                                                    const MWBase::World& world)\n    {\n        mTimePassed += duration;\n\n        if (mTimePassed < mTimeToNextEnvSound)\n            return {};\n\n        const float a = Misc::Rng::rollClosedProbability();\n        mTimeToNextEnvSound = mMinTimeBetweenSounds + (mMaxTimeBetweenSounds - mMinTimeBetweenSounds) * a;\n        mTimePassed = 0;\n\n        if (mLastRegionName != regionName)\n        {\n            mLastRegionName = regionName;\n            mSumChance = 0;\n        }\n\n        const ESM::Region* const region = world.getStore().get<ESM::Region>().search(mLastRegionName);\n\n        if (region == nullptr)\n            return {};\n\n        if (mSumChance == 0)\n        {\n            mSumChance = std::accumulate(region->mSoundList.begin(), region->mSoundList.end(), 0, addChance);\n            if (mSumChance == 0)\n                return {};\n        }\n\n        const int r = Misc::Rng::rollDice(std::max(mSumChance, 100));\n        int pos = 0;\n\n        const auto isSelected = [&] (const ESM::Region::SoundRef& sound)\n        {\n            if (r - pos < sound.mChance)\n                return true;\n            pos += sound.mChance;\n            return false;\n        };\n\n        const auto it = std::find_if(region->mSoundList.begin(), region->mSoundList.end(), isSelected);\n\n        if (it == region->mSoundList.end())\n            return {};\n\n        return it->mSound;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/regionsoundselector.hpp",
    "content": "#ifndef GAME_SOUND_REGIONSOUNDSELECTOR_H\n#define GAME_SOUND_REGIONSOUNDSELECTOR_H\n\n#include <optional>\n#include <string>\n\nnamespace MWBase\n{\n    class World;\n}\n\nnamespace MWSound\n{\n    class RegionSoundSelector\n    {\n        public:\n            std::optional<std::string> getNextRandom(float duration, const std::string& regionName,\n                                                       const MWBase::World& world);\n\n            RegionSoundSelector();\n\n        private:\n            float mTimeToNextEnvSound = 0.0f;\n            int mSumChance = 0;\n            std::string mLastRegionName;\n            float mTimePassed = 0.0;\n            float mMinTimeBetweenSounds;\n            float mMaxTimeBetweenSounds;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/sound.hpp",
    "content": "#ifndef GAME_SOUND_SOUND_H\n#define GAME_SOUND_SOUND_H\n\n#include <algorithm>\n\n#include \"sound_output.hpp\"\n\nnamespace MWSound\n{\n    // Extra play flags, not intended for caller use\n    enum PlayModeEx\n    {\n        Play_2D = 0,\n        Play_3D = 1 << 31,\n    };\n\n    // For testing individual PlayMode flags\n    inline int operator&(int a, PlayMode b) { return a & static_cast<int>(b); }\n    inline int operator&(PlayMode a, PlayMode b) { return static_cast<int>(a) & static_cast<int>(b); }\n\n    struct SoundParams\n    {\n        osg::Vec3f mPos;\n        float mVolume = 1;\n        float mBaseVolume = 1;\n        float mPitch = 1;\n        float mMinDistance = 1;\n        float mMaxDistance = 1000;\n        int mFlags = 0;\n        float mFadeOutTime = 0;\n    };\n\n    class SoundBase {\n        SoundBase& operator=(const SoundBase&) = delete;\n        SoundBase(const SoundBase&) = delete;\n        SoundBase(SoundBase&&) = delete;\n\n        SoundParams mParams;\n\n    protected:\n        Sound_Instance mHandle = nullptr;\n\n        friend class OpenAL_Output;\n\n    public:\n        void setPosition(const osg::Vec3f &pos) { mParams.mPos = pos; }\n        void setVolume(float volume) { mParams.mVolume = volume; }\n        void setBaseVolume(float volume) { mParams.mBaseVolume = volume; }\n        void setFadeout(float duration) { mParams.mFadeOutTime = duration; }\n        void updateFade(float duration)\n        {\n            if (mParams.mFadeOutTime > 0.0f)\n            {\n                float soundDuration = std::min(duration, mParams.mFadeOutTime);\n                mParams.mVolume *= (mParams.mFadeOutTime - soundDuration) / mParams.mFadeOutTime;\n                mParams.mFadeOutTime -= soundDuration;\n            }\n        }\n\n        const osg::Vec3f &getPosition() const { return mParams.mPos; }\n        float getRealVolume() const { return mParams.mVolume * mParams.mBaseVolume; }\n        float getPitch() const { return mParams.mPitch; }\n        float getMinDistance() const { return mParams.mMinDistance; }\n        float getMaxDistance() const { return mParams.mMaxDistance; }\n\n        MWSound::Type getPlayType() const\n        { return static_cast<MWSound::Type>(mParams.mFlags & MWSound::Type::Mask); }\n        bool getUseEnv() const { return !(mParams.mFlags & MWSound::PlayMode::NoEnv); }\n        bool getIsLooping() const { return mParams.mFlags & MWSound::PlayMode::Loop; }\n        bool getDistanceCull() const { return mParams.mFlags & MWSound::PlayMode::RemoveAtDistance; }\n        bool getIs3D() const { return mParams.mFlags & Play_3D; }\n\n        void init(const SoundParams& params)\n        {\n            mParams = params;\n            mHandle = nullptr;\n        }\n\n        SoundBase() = default;\n    };\n\n    class Sound : public SoundBase {\n        Sound& operator=(const Sound&) = delete;\n        Sound(const Sound&) = delete;\n        Sound(Sound&&) = delete;\n\n    public:\n        Sound() { }\n    };\n\n    class Stream : public SoundBase {\n        Stream& operator=(const Stream&) = delete;\n        Stream(const Stream&) = delete;\n        Stream(Stream&&) = delete;\n\n    public:\n        Stream() { }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/sound_buffer.cpp",
    "content": "#include \"sound_buffer.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/settings/settings.hpp>\n#include <components/vfs/manager.hpp>\n\n#include <algorithm>\n#include <cmath>\n\nnamespace MWSound\n{\n    namespace\n    {\n        struct AudioParams\n        {\n            float mAudioDefaultMinDistance;\n            float mAudioDefaultMaxDistance;\n            float mAudioMinDistanceMult;\n            float mAudioMaxDistanceMult;\n        };\n\n        AudioParams makeAudioParams(const MWBase::World& world)\n        {\n            const auto& settings = world.getStore().get<ESM::GameSetting>();\n            AudioParams params;\n            params.mAudioDefaultMinDistance = settings.find(\"fAudioDefaultMinDistance\")->mValue.getFloat();\n            params.mAudioDefaultMaxDistance = settings.find(\"fAudioDefaultMaxDistance\")->mValue.getFloat();\n            params.mAudioMinDistanceMult = settings.find(\"fAudioMinDistanceMult\")->mValue.getFloat();\n            params.mAudioMaxDistanceMult = settings.find(\"fAudioMaxDistanceMult\")->mValue.getFloat();\n            return params;\n        }\n    }\n\n    SoundBufferPool::SoundBufferPool(const VFS::Manager& vfs, Sound_Output& output) :\n        mVfs(&vfs),\n        mOutput(&output),\n        mBufferCacheMax(std::max(Settings::Manager::getInt(\"buffer cache max\", \"Sound\"), 1) * 1024 * 1024),\n        mBufferCacheMin(std::min(static_cast<std::size_t>(std::max(Settings::Manager::getInt(\"buffer cache min\", \"Sound\"), 1)) * 1024 * 1024, mBufferCacheMax))\n    {\n    }\n\n    SoundBufferPool::~SoundBufferPool()\n    {\n        clear();\n    }\n\n    Sound_Buffer* SoundBufferPool::lookup(const std::string& soundId) const\n    {\n        const auto it = mBufferNameMap.find(soundId);\n        if (it != mBufferNameMap.end())\n        {\n            Sound_Buffer* sfx = it->second;\n            if (sfx->getHandle() != nullptr)\n                return sfx;\n        }\n        return nullptr;\n    }\n\n    Sound_Buffer* SoundBufferPool::load(const std::string& soundId)\n    {\n        if (mBufferNameMap.empty())\n        {\n            for (const ESM::Sound& sound : MWBase::Environment::get().getWorld()->getStore().get<ESM::Sound>())\n                insertSound(Misc::StringUtils::lowerCase(sound.mId), sound);\n        }\n\n        Sound_Buffer* sfx;\n        const auto it = mBufferNameMap.find(soundId);\n        if (it != mBufferNameMap.end())\n            sfx = it->second;\n        else\n        {\n            const ESM::Sound *sound = MWBase::Environment::get().getWorld()->getStore().get<ESM::Sound>().search(soundId);\n            if (sound == nullptr)\n                return {};\n            sfx = insertSound(soundId, *sound);\n        }\n\n        if (sfx->getHandle() == nullptr)\n        {\n            auto [handle, size] = mOutput->loadSound(sfx->getResourceName());\n            if (handle == nullptr)\n                return {};\n\n            sfx->mHandle = handle;\n\n            mBufferCacheSize += size;\n            if (mBufferCacheSize > mBufferCacheMax)\n            {\n                unloadUnused();\n                if (!mUnusedBuffers.empty() && mBufferCacheSize > mBufferCacheMax)\n                    Log(Debug::Warning) << \"No unused sound buffers to free, using \" << mBufferCacheSize << \" bytes!\";\n            }\n            mUnusedBuffers.push_front(sfx);\n        }\n\n        return sfx;\n    }\n\n    void SoundBufferPool::clear()\n    {\n        for (auto &sfx : mSoundBuffers)\n        {\n            if(sfx.mHandle)\n                mOutput->unloadSound(sfx.mHandle);\n            sfx.mHandle = nullptr;\n        }\n        mUnusedBuffers.clear();\n    }\n\n    Sound_Buffer* SoundBufferPool::insertSound(const std::string& soundId, const ESM::Sound& sound)\n    {\n        static const AudioParams audioParams = makeAudioParams(*MWBase::Environment::get().getWorld());\n\n        float volume = static_cast<float>(std::pow(10.0, (sound.mData.mVolume / 255.0 * 3348.0 - 3348.0) / 2000.0));\n        float min = sound.mData.mMinRange;\n        float max = sound.mData.mMaxRange;\n        if (min == 0 && max == 0)\n        {\n            min = audioParams.mAudioDefaultMinDistance;\n            max = audioParams.mAudioDefaultMaxDistance;\n        }\n\n        min *= audioParams.mAudioMinDistanceMult;\n        max *= audioParams.mAudioMaxDistanceMult;\n        min = std::max(min, 1.0f);\n        max = std::max(min, max);\n\n        Sound_Buffer& sfx = mSoundBuffers.emplace_back(\"Sound/\" + sound.mSound, volume, min, max);\n        mVfs->normalizeFilename(sfx.mResourceName);\n\n        mBufferNameMap.emplace(soundId, &sfx);\n        return &sfx;\n    }\n\n    void SoundBufferPool::unloadUnused()\n    {\n        while (!mUnusedBuffers.empty() && mBufferCacheSize > mBufferCacheMin)\n        {\n            Sound_Buffer* const unused = mUnusedBuffers.back();\n\n            mBufferCacheSize -= mOutput->unloadSound(unused->getHandle());\n            unused->mHandle = nullptr;\n\n            mUnusedBuffers.pop_back();\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/sound_buffer.hpp",
    "content": "#ifndef GAME_SOUND_SOUND_BUFFER_H\n#define GAME_SOUND_SOUND_BUFFER_H\n\n#include <algorithm>\n#include <string>\n#include <deque>\n#include <unordered_map>\n\n#include \"sound_output.hpp\"\n\nnamespace ESM\n{\n    struct Sound;\n}\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace MWSound\n{\n    class SoundBufferPool;\n\n    class Sound_Buffer\n    {\n        public:\n            template <class T>\n            Sound_Buffer(T&& resname, float volume, float mindist, float maxdist)\n                : mResourceName(std::forward<T>(resname)), mVolume(volume), mMinDist(mindist), mMaxDist(maxdist)\n            {}\n\n            const std::string& getResourceName() const noexcept { return mResourceName; }\n\n            Sound_Handle getHandle() const noexcept { return mHandle; }\n\n            float getVolume() const noexcept { return mVolume; }\n\n            float getMinDist() const noexcept { return mMinDist; }\n\n            float getMaxDist() const noexcept { return mMaxDist; }\n\n        private:\n            std::string mResourceName;\n            float mVolume;\n            float mMinDist;\n            float mMaxDist;\n            Sound_Handle mHandle = nullptr;\n            std::size_t mUses = 0;\n\n            friend class SoundBufferPool;\n    };\n\n    class SoundBufferPool\n    {\n        public:\n            SoundBufferPool(const VFS::Manager& vfs, Sound_Output& output);\n\n            SoundBufferPool(const SoundBufferPool&) = delete;\n\n            ~SoundBufferPool();\n\n            /// Lookup a soundId for its sound data (resource name, local volume,\n            /// minRange, and maxRange)\n            Sound_Buffer* lookup(const std::string& soundId) const;\n\n            /// Lookup a soundId for its sound data (resource name, local volume,\n            /// minRange, and maxRange), and ensure it's ready for use.\n            Sound_Buffer* load(const std::string& soundId);\n\n            void use(Sound_Buffer& sfx)\n            {\n                if (sfx.mUses++ == 0)\n                {\n                    const auto it = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), &sfx);\n                    if (it != mUnusedBuffers.end())\n                        mUnusedBuffers.erase(it);\n                }\n            }\n\n            void release(Sound_Buffer& sfx)\n            {\n                if (--sfx.mUses == 0)\n                    mUnusedBuffers.push_front(&sfx);\n            }\n\n            void clear();\n\n        private:\n            const VFS::Manager* const mVfs;\n            Sound_Output* mOutput;\n            std::deque<Sound_Buffer> mSoundBuffers;\n            std::unordered_map<std::string, Sound_Buffer*> mBufferNameMap;\n            std::size_t mBufferCacheMax;\n            std::size_t mBufferCacheMin;\n            std::size_t mBufferCacheSize = 0;\n            // NOTE: unused buffers are stored in front-newest order.\n            std::deque<Sound_Buffer*> mUnusedBuffers;\n\n            inline Sound_Buffer* insertSound(const std::string& soundId, const ESM::Sound& sound);\n\n            inline void unloadUnused();\n    };\n}\n\n#endif /* GAME_SOUND_SOUND_BUFFER_H */\n"
  },
  {
    "path": "apps/openmw/mwsound/sound_decoder.hpp",
    "content": "#ifndef GAME_SOUND_SOUND_DECODER_H\n#define GAME_SOUND_SOUND_DECODER_H\n\n#include <string>\n#include <vector>\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace MWSound\n{\n    enum SampleType {\n        SampleType_UInt8,\n        SampleType_Int16,\n        SampleType_Float32\n    };\n    const char *getSampleTypeName(SampleType type);\n\n    enum ChannelConfig {\n        ChannelConfig_Mono,\n        ChannelConfig_Stereo,\n        ChannelConfig_Quad,\n        ChannelConfig_5point1,\n        ChannelConfig_7point1\n    };\n    const char *getChannelConfigName(ChannelConfig config);\n\n    size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type);\n    size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type);\n\n    struct Sound_Decoder\n    {\n        const VFS::Manager* mResourceMgr;\n\n        virtual void open(const std::string &fname) = 0;\n        virtual void close() = 0;\n\n        virtual std::string getName() = 0;\n        virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0;\n\n        virtual size_t read(char *buffer, size_t bytes) = 0;\n        virtual void readAll(std::vector<char> &output);\n        virtual size_t getSampleOffset() = 0;\n\n        Sound_Decoder(const VFS::Manager* resourceMgr) : mResourceMgr(resourceMgr)\n        { }\n        virtual ~Sound_Decoder() { }\n\n    private:\n        Sound_Decoder(const Sound_Decoder &rhs);\n        Sound_Decoder& operator=(const Sound_Decoder &rhs);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/sound_output.hpp",
    "content": "#ifndef GAME_SOUND_SOUND_OUTPUT_H\n#define GAME_SOUND_SOUND_OUTPUT_H\n\n#include <string>\n#include <memory>\n#include <vector>\n\n#include \"../mwbase/soundmanager.hpp\"\n\nnamespace MWSound\n{\n    class SoundManager;\n    struct Sound_Decoder;\n    class Sound;\n    class Stream;\n\n    // An opaque handle for the implementation's sound buffers.\n    typedef void *Sound_Handle;\n    // An opaque handle for the implementation's sound instances.\n    typedef void *Sound_Instance;\n\n    enum class HrtfMode {\n        Disable,\n        Enable,\n        Auto\n    };\n\n    enum Environment\n    {\n        Env_Normal,\n        Env_Underwater\n    };\n\n    class Sound_Output\n    {\n        SoundManager &mManager;\n\n        virtual std::vector<std::string> enumerate() = 0;\n        virtual bool init(const std::string &devname, const std::string &hrtfname, HrtfMode hrtfmode) = 0;\n        virtual void deinit() = 0;\n\n        virtual std::vector<std::string> enumerateHrtf() = 0;\n        virtual void setHrtf(const std::string &hrtfname, HrtfMode hrtfmode) = 0;\n\n        virtual std::pair<Sound_Handle,size_t> loadSound(const std::string &fname) = 0;\n        virtual size_t unloadSound(Sound_Handle data) = 0;\n\n        virtual bool playSound(Sound *sound, Sound_Handle data, float offset) = 0;\n        virtual bool playSound3D(Sound *sound, Sound_Handle data, float offset) = 0;\n        virtual void finishSound(Sound *sound) = 0;\n        virtual bool isSoundPlaying(Sound *sound) = 0;\n        virtual void updateSound(Sound *sound) = 0;\n\n        virtual bool streamSound(DecoderPtr decoder, Stream *sound, bool getLoudnessData=false) = 0;\n        virtual bool streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData) = 0;\n        virtual void finishStream(Stream *sound) = 0;\n        virtual double getStreamDelay(Stream *sound) = 0;\n        virtual double getStreamOffset(Stream *sound) = 0;\n        virtual float getStreamLoudness(Stream *sound) = 0;\n        virtual bool isStreamPlaying(Stream *sound) = 0;\n        virtual void updateStream(Stream *sound) = 0;\n\n        virtual void startUpdate() = 0;\n        virtual void finishUpdate() = 0;\n\n        virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) = 0;\n\n        virtual void pauseSounds(int types) = 0;\n        virtual void resumeSounds(int types) = 0;\n\n        virtual void pauseActiveDevice() = 0;\n        virtual void resumeActiveDevice() = 0;\n\n        Sound_Output& operator=(const Sound_Output &rhs);\n        Sound_Output(const Sound_Output &rhs);\n\n    protected:\n        bool mInitialized;\n\n        Sound_Output(SoundManager &mgr)\n          : mManager(mgr), mInitialized(false)\n        { }\n    public:\n        virtual ~Sound_Output() { }\n\n        bool isInitialized() const { return mInitialized; }\n\n        friend class OpenAL_Output;\n        friend class SoundManager;\n        friend class SoundBufferPool;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/soundmanagerimp.cpp",
    "content": "#include \"soundmanagerimp.hpp\"\n\n#include <algorithm>\n#include <map>\n#include <numeric>\n\n#include <osg/Matrixf>\n\n#include <components/misc/rng.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/vfs/manager.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/statemanager.hpp\"\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"sound_buffer.hpp\"\n#include \"sound_decoder.hpp\"\n#include \"sound_output.hpp\"\n#include \"sound.hpp\"\n\n#include \"openal_output.hpp\"\n#include \"ffmpeg_decoder.hpp\"\n\n\nnamespace MWSound\n{\n    namespace\n    {\n        constexpr float sMinUpdateInterval = 1.0f / 30.0f;\n\n        WaterSoundUpdaterSettings makeWaterSoundUpdaterSettings()\n        {\n            WaterSoundUpdaterSettings settings;\n\n            settings.mNearWaterRadius = Fallback::Map::getInt(\"Water_NearWaterRadius\");\n            settings.mNearWaterPoints = Fallback::Map::getInt(\"Water_NearWaterPoints\");\n            settings.mNearWaterIndoorTolerance = Fallback::Map::getFloat(\"Water_NearWaterIndoorTolerance\");\n            settings.mNearWaterOutdoorTolerance = Fallback::Map::getFloat(\"Water_NearWaterOutdoorTolerance\");\n            settings.mNearWaterIndoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString(\"Water_NearWaterIndoorID\"));\n            settings.mNearWaterOutdoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString(\"Water_NearWaterOutdoorID\"));\n\n            return settings;\n        }\n    }\n\n    // For combining PlayMode and Type flags\n    inline int operator|(PlayMode a, Type b) { return static_cast<int>(a) | static_cast<int>(b); }\n\n    SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound)\n        : mVFS(vfs)\n        , mOutput(new OpenAL_Output(*this))\n        , mWaterSoundUpdater(makeWaterSoundUpdaterSettings())\n        , mSoundBuffers(*vfs, *mOutput)\n        , mListenerUnderwater(false)\n        , mListenerPos(0,0,0)\n        , mListenerDir(1,0,0)\n        , mListenerUp(0,0,1)\n        , mUnderwaterSound(nullptr)\n        , mNearWaterSound(nullptr)\n        , mPlaybackPaused(false)\n        , mTimePassed(0.f)\n        , mLastCell(nullptr)\n        , mCurrentRegionSound(nullptr)\n    {\n        if(!useSound)\n        {\n            Log(Debug::Info) << \"Sound disabled.\";\n            return;\n        }\n\n        std::string hrtfname = Settings::Manager::getString(\"hrtf\", \"Sound\");\n        int hrtfstate = Settings::Manager::getInt(\"hrtf enable\", \"Sound\");\n        HrtfMode hrtfmode = hrtfstate < 0 ? HrtfMode::Auto :\n                            hrtfstate > 0 ? HrtfMode::Enable : HrtfMode::Disable;\n\n        std::string devname = Settings::Manager::getString(\"device\", \"Sound\");\n        if(!mOutput->init(devname, hrtfname, hrtfmode))\n        {\n            Log(Debug::Error) << \"Failed to initialize audio output, sound disabled\";\n            return;\n        }\n\n        std::vector<std::string> names = mOutput->enumerate();\n        std::stringstream stream;\n\n        stream << \"Enumerated output devices:\\n\";\n        for(const std::string &name : names)\n            stream << \"  \" << name;\n\n        Log(Debug::Info) << stream.str();\n        stream.str(\"\");\n\n        names = mOutput->enumerateHrtf();\n        if(!names.empty())\n        {\n            stream << \"Enumerated HRTF names:\\n\";\n            for(const std::string &name : names)\n                stream << \"  \" << name;\n\n            Log(Debug::Info) << stream.str();\n        }\n    }\n\n    SoundManager::~SoundManager()\n    {\n        SoundManager::clear();\n        mSoundBuffers.clear();\n        mOutput.reset();\n    }\n\n    // Return a new decoder instance, used as needed by the output implementations\n    DecoderPtr SoundManager::getDecoder()\n    {\n        return std::make_shared<FFmpeg_Decoder>(mVFS);\n    }\n\n    DecoderPtr SoundManager::loadVoice(const std::string &voicefile)\n    {\n        try\n        {\n            DecoderPtr decoder = getDecoder();\n\n            // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.\n            if(mVFS->exists(voicefile))\n                decoder->open(voicefile);\n            else\n            {\n                std::string file = voicefile;\n                std::string::size_type pos = file.rfind('.');\n                if(pos != std::string::npos)\n                    file = file.substr(0, pos)+\".mp3\";\n                decoder->open(file);\n            }\n\n            return decoder;\n        }\n        catch(std::exception &e)\n        {\n            Log(Debug::Error) << \"Failed to load audio from \" << voicefile << \": \" << e.what();\n        }\n\n        return nullptr;\n    }\n\n    SoundPtr SoundManager::getSoundRef()\n    {\n        return mSounds.get();\n    }\n\n    StreamPtr SoundManager::getStreamRef()\n    {\n        return mStreams.get();\n    }\n\n    StreamPtr SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal)\n    {\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        static const float fAudioMinDistanceMult = world->getStore().get<ESM::GameSetting>().find(\"fAudioMinDistanceMult\")->mValue.getFloat();\n        static const float fAudioMaxDistanceMult = world->getStore().get<ESM::GameSetting>().find(\"fAudioMaxDistanceMult\")->mValue.getFloat();\n        static const float fAudioVoiceDefaultMinDistance = world->getStore().get<ESM::GameSetting>().find(\"fAudioVoiceDefaultMinDistance\")->mValue.getFloat();\n        static const float fAudioVoiceDefaultMaxDistance = world->getStore().get<ESM::GameSetting>().find(\"fAudioVoiceDefaultMaxDistance\")->mValue.getFloat();\n        static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f);\n        static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance);\n\n        bool played;\n        float basevol = volumeFromType(Type::Voice);\n        StreamPtr sound = getStreamRef();\n        if(playlocal)\n        {\n            sound->init([&] {\n                SoundParams params;\n                params.mBaseVolume = basevol;\n                params.mFlags = PlayMode::NoEnv | Type::Voice | Play_2D;\n                return params;\n            } ());\n            played = mOutput->streamSound(decoder, sound.get(), true);\n        }\n        else\n        {\n            sound->init([&] {\n                SoundParams params;\n                params.mPos = pos;\n                params.mBaseVolume = basevol;\n                params.mMinDistance = minDistance;\n                params.mMaxDistance = maxDistance;\n                params.mFlags = PlayMode::Normal | Type::Voice | Play_3D;\n                return params;\n            } ());\n            played = mOutput->streamSound3D(decoder, sound.get(), true);\n        }\n        if(!played)\n            return nullptr;\n        return sound;\n    }\n\n    // Gets the combined volume settings for the given sound type\n    float SoundManager::volumeFromType(Type type) const\n    {\n        return mVolumeSettings.getVolumeFromType(type);\n    }\n\n    void SoundManager::stopMusic()\n    {\n        if(mMusic)\n        {\n            mOutput->finishStream(mMusic.get());\n            mMusic = nullptr;\n        }\n    }\n\n    void SoundManager::streamMusicFull(const std::string& filename)\n    {\n        if(!mOutput->isInitialized())\n            return;\n        Log(Debug::Info) << \"Playing \" << filename;\n        mLastPlayedMusic = filename;\n\n        stopMusic();\n\n        DecoderPtr decoder = getDecoder();\n        decoder->open(filename);\n\n        mMusic = getStreamRef();\n        mMusic->init([&] {\n            SoundParams params;\n            params.mBaseVolume = volumeFromType(Type::Music);\n            params.mFlags = PlayMode::NoEnv | Type::Music | Play_2D;\n            return params;\n        } ());\n        mOutput->streamSound(decoder, mMusic.get());\n    }\n\n    void SoundManager::advanceMusic(const std::string& filename)\n    {\n        if (!isMusicPlaying())\n        {\n            streamMusicFull(filename);\n            return;\n        }\n\n        mNextMusic = filename;\n\n        mMusic->setFadeout(1.f);\n    }\n\n    void SoundManager::startRandomTitle()\n    {\n        const std::vector<std::string> &filelist = mMusicFiles[mCurrentPlaylist];\n        auto &tracklist = mMusicToPlay[mCurrentPlaylist];\n\n        // Do a Fisher-Yates shuffle\n\n        // Repopulate if playlist is empty\n        if(tracklist.empty())\n        {\n            tracklist.resize(filelist.size());\n            std::iota(tracklist.begin(), tracklist.end(), 0);\n        }\n\n        int i = Misc::Rng::rollDice(tracklist.size());\n\n        // Reshuffle if last played music is the same after a repopulation\n        if(filelist[tracklist[i]] == mLastPlayedMusic)\n            i = (i+1) % tracklist.size();\n\n        // Remove music from list after advancing music\n        advanceMusic(filelist[tracklist[i]]);\n        tracklist[i] = tracklist.back();\n        tracklist.pop_back();\n    }\n\n\n    void SoundManager::streamMusic(const std::string& filename)\n    {\n        advanceMusic(\"Music/\"+filename);\n    }\n\n    bool SoundManager::isMusicPlaying()\n    {\n        return mMusic && mOutput->isStreamPlaying(mMusic.get());\n    }\n\n    void SoundManager::playPlaylist(const std::string &playlist)\n    {\n        if (mCurrentPlaylist == playlist)\n            return;\n\n        if (mMusicFiles.find(playlist) == mMusicFiles.end())\n        {\n            std::vector<std::string> filelist;\n            const std::map<std::string, VFS::File*>& index = mVFS->getIndex();\n\n            std::string pattern = \"Music/\" + playlist;\n            mVFS->normalizeFilename(pattern);\n\n            std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(pattern);\n            while (found != index.end())\n            {\n                if (found->first.size() >= pattern.size() && found->first.substr(0, pattern.size()) == pattern)\n                    filelist.push_back(found->first);\n                else\n                    break;\n                ++found;\n            }\n\n            mMusicFiles[playlist] = filelist;\n        }\n\n        if (mMusicFiles[playlist].empty())\n            return;\n\n        mCurrentPlaylist = playlist;\n        startRandomTitle();\n    }\n\n    void SoundManager::playTitleMusic()\n    {\n        if (mCurrentPlaylist == \"Title\")\n            return;\n\n        if (mMusicFiles.find(\"Title\") == mMusicFiles.end())\n        {\n            std::vector<std::string> filelist;\n            const std::map<std::string, VFS::File*>& index = mVFS->getIndex();\n            // Is there an ini setting for this filename or something?\n            std::string filename = \"music/special/morrowind title.mp3\";\n            auto found = index.find(filename);\n            if (found != index.end())\n            {\n                filelist.emplace_back(found->first);\n                mMusicFiles[\"Title\"] = filelist;\n            }\n            else\n            {\n                Log(Debug::Warning) << \"Title music not found\";\n                return;\n            }\n        }\n\n        if (mMusicFiles[\"Title\"].empty())\n            return;\n\n        mCurrentPlaylist = \"Title\";\n        startRandomTitle();\n    }\n\n    void SoundManager::say(const MWWorld::ConstPtr &ptr, const std::string &filename)\n    {\n        if(!mOutput->isInitialized())\n            return;\n\n        std::string voicefile = \"Sound/\"+filename;\n\n        mVFS->normalizeFilename(voicefile);\n        DecoderPtr decoder = loadVoice(voicefile);\n        if (!decoder)\n            return;\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans();\n\n        stopSay(ptr);\n        StreamPtr sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer()));\n        if(!sound) return;\n\n        mSaySoundsQueue.emplace(ptr, std::move(sound));\n    }\n\n    float SoundManager::getSaySoundLoudness(const MWWorld::ConstPtr &ptr) const\n    {\n        SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr);\n        if(snditer != mActiveSaySounds.end())\n        {\n            Stream *sound = snditer->second.get();\n            return mOutput->getStreamLoudness(sound);\n        }\n\n        return 0.0f;\n    }\n\n    void SoundManager::say(const std::string& filename)\n    {\n        if(!mOutput->isInitialized())\n            return;\n\n        std::string voicefile = \"Sound/\"+filename;\n\n        mVFS->normalizeFilename(voicefile);\n        DecoderPtr decoder = loadVoice(voicefile);\n        if (!decoder)\n            return;\n\n        stopSay(MWWorld::ConstPtr());\n        StreamPtr sound = playVoice(decoder, osg::Vec3f(), true);\n        if(!sound) return;\n\n        mActiveSaySounds.emplace(MWWorld::ConstPtr(), std::move(sound));\n    }\n\n    bool SoundManager::sayDone(const MWWorld::ConstPtr &ptr) const\n    {\n        SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr);\n        if(snditer != mActiveSaySounds.end())\n        {\n            if(mOutput->isStreamPlaying(snditer->second.get()))\n                return false;\n            return true;\n        }\n        return true;\n    }\n\n    bool SoundManager::sayActive(const MWWorld::ConstPtr &ptr) const\n    {\n        SaySoundMap::const_iterator snditer = mSaySoundsQueue.find(ptr);\n        if(snditer != mSaySoundsQueue.end())\n        {\n            if(mOutput->isStreamPlaying(snditer->second.get()))\n                return true;\n            return false;\n        }\n\n        snditer = mActiveSaySounds.find(ptr);\n        if(snditer != mActiveSaySounds.end())\n        {\n            if(mOutput->isStreamPlaying(snditer->second.get()))\n                return true;\n            return false;\n        }\n\n        return false;\n    }\n\n    void SoundManager::stopSay(const MWWorld::ConstPtr &ptr)\n    {\n        SaySoundMap::iterator snditer = mSaySoundsQueue.find(ptr);\n        if(snditer != mSaySoundsQueue.end())\n        {\n            mOutput->finishStream(snditer->second.get());\n            mSaySoundsQueue.erase(snditer);\n        }\n\n        snditer = mActiveSaySounds.find(ptr);\n        if(snditer != mActiveSaySounds.end())\n        {\n            mOutput->finishStream(snditer->second.get());\n            mActiveSaySounds.erase(snditer);\n        }\n    }\n\n\n    Stream *SoundManager::playTrack(const DecoderPtr& decoder, Type type)\n    {\n        if(!mOutput->isInitialized())\n            return nullptr;\n\n        StreamPtr track = getStreamRef();\n        track->init([&] {\n            SoundParams params;\n            params.mBaseVolume = volumeFromType(type);\n            params.mFlags = PlayMode::NoEnv | type | Play_2D;\n            return params;\n        } ());\n        if(!mOutput->streamSound(decoder, track.get()))\n            return nullptr;\n\n        Stream* result = track.get();\n        const auto it = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), track);\n        mActiveTracks.insert(it, std::move(track));\n        return result;\n    }\n\n    void SoundManager::stopTrack(Stream *stream)\n    {\n        mOutput->finishStream(stream);\n        TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), stream,\n                                                    [] (const StreamPtr& lhs, Stream* rhs) { return lhs.get() < rhs; });\n        if(iter != mActiveTracks.end() && iter->get() == stream)\n            mActiveTracks.erase(iter);\n    }\n\n    double SoundManager::getTrackTimeDelay(Stream *stream)\n    {\n        return mOutput->getStreamDelay(stream);\n    }\n\n\n    Sound* SoundManager::playSound(const std::string& soundId, float volume, float pitch, Type type, PlayMode mode, float offset)\n    {\n        if(!mOutput->isInitialized())\n            return nullptr;\n\n        Sound_Buffer *sfx = mSoundBuffers.load(Misc::StringUtils::lowerCase(soundId));\n        if(!sfx) return nullptr;\n\n        // Only one copy of given sound can be played at time, so stop previous copy\n        stopSound(sfx, MWWorld::ConstPtr());\n\n        SoundPtr sound = getSoundRef();\n        sound->init([&] {\n            SoundParams params;\n            params.mVolume = volume * sfx->getVolume();\n            params.mBaseVolume = volumeFromType(type);\n            params.mPitch = pitch;\n            params.mFlags = mode | type | Play_2D;\n            return params;\n        } ());\n        if(!mOutput->playSound(sound.get(), sfx->getHandle(), offset))\n            return nullptr;\n\n        Sound* result = sound.get();\n        mActiveSounds[MWWorld::ConstPtr()].emplace_back(std::move(sound), sfx);\n        mSoundBuffers.use(*sfx);\n        return result;\n    }\n\n    Sound *SoundManager::playSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId,\n                                     float volume, float pitch, Type type, PlayMode mode,\n                                     float offset)\n    {\n        if(!mOutput->isInitialized())\n            return nullptr;\n\n        const osg::Vec3f objpos(ptr.getRefData().getPosition().asVec3());\n        if ((mode & PlayMode::RemoveAtDistance) && (mListenerPos - objpos).length2() > 2000 * 2000)\n            return nullptr;\n\n        // Look up the sound in the ESM data\n        Sound_Buffer *sfx = mSoundBuffers.load(Misc::StringUtils::lowerCase(soundId));\n        if(!sfx) return nullptr;\n\n        // Only one copy of given sound can be played at time on ptr, so stop previous copy\n        stopSound(sfx, ptr);\n\n        bool played;\n        SoundPtr sound = getSoundRef();\n        if(!(mode&PlayMode::NoPlayerLocal) && ptr == MWMechanics::getPlayer())\n        {\n            sound->init([&] {\n                SoundParams params;\n                params.mVolume = volume * sfx->getVolume();\n                params.mBaseVolume = volumeFromType(type);\n                params.mPitch = pitch;\n                params.mFlags = mode | type | Play_2D;\n                return params;\n            } ());\n            played = mOutput->playSound(sound.get(), sfx->getHandle(), offset);\n        }\n        else\n        {\n            sound->init([&] {\n                SoundParams params;\n                params.mPos = objpos;\n                params.mVolume = volume * sfx->getVolume();\n                params.mBaseVolume = volumeFromType(type);\n                params.mPitch = pitch;\n                params.mMinDistance = sfx->getMinDist();\n                params.mMaxDistance = sfx->getMaxDist();\n                params.mFlags = mode | type | Play_3D;\n                return params;\n            } ());\n            played = mOutput->playSound3D(sound.get(), sfx->getHandle(), offset);\n        }\n        if(!played)\n            return nullptr;\n\n        Sound* result = sound.get();\n        mActiveSounds[ptr].emplace_back(std::move(sound), sfx);\n        mSoundBuffers.use(*sfx);\n        return result;\n    }\n\n    Sound *SoundManager::playSound3D(const osg::Vec3f& initialPos, const std::string& soundId,\n                                     float volume, float pitch, Type type, PlayMode mode,\n                                     float offset)\n    {\n        if(!mOutput->isInitialized())\n            return nullptr;\n\n        // Look up the sound in the ESM data\n        Sound_Buffer *sfx = mSoundBuffers.load(Misc::StringUtils::lowerCase(soundId));\n        if(!sfx) return nullptr;\n\n        SoundPtr sound = getSoundRef();\n        sound->init([&] {\n            SoundParams params;\n            params.mPos = initialPos;\n            params.mVolume = volume * sfx->getVolume();\n            params.mBaseVolume = volumeFromType(type);\n            params.mPitch = pitch;\n            params.mMinDistance = sfx->getMinDist();\n            params.mMaxDistance = sfx->getMaxDist();\n            params.mFlags = mode | type | Play_3D;\n            return params;\n        } ());\n        if(!mOutput->playSound3D(sound.get(), sfx->getHandle(), offset))\n            return nullptr;\n\n        Sound* result = sound.get();\n        mActiveSounds[MWWorld::ConstPtr()].emplace_back(std::move(sound), sfx);\n        mSoundBuffers.use(*sfx);\n        return result;\n    }\n\n    void SoundManager::stopSound(Sound *sound)\n    {\n        if(sound)\n            mOutput->finishSound(sound);\n    }\n\n    void SoundManager::stopSound(Sound_Buffer *sfx, const MWWorld::ConstPtr &ptr)\n    {\n        SoundMap::iterator snditer = mActiveSounds.find(ptr);\n        if(snditer != mActiveSounds.end())\n        {\n            for(SoundBufferRefPair &snd : snditer->second)\n            {\n                if(snd.second == sfx)\n                    mOutput->finishSound(snd.first.get());\n            }\n        }\n    }\n\n    void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId)\n    {\n        if(!mOutput->isInitialized())\n            return;\n\n        Sound_Buffer *sfx = mSoundBuffers.lookup(Misc::StringUtils::lowerCase(soundId));\n        if (!sfx) return;\n\n        stopSound(sfx, ptr);\n    }\n\n    void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr)\n    {\n        SoundMap::iterator snditer = mActiveSounds.find(ptr);\n        if(snditer != mActiveSounds.end())\n        {\n            for(SoundBufferRefPair &snd : snditer->second)\n                mOutput->finishSound(snd.first.get());\n        }\n        SaySoundMap::iterator sayiter = mSaySoundsQueue.find(ptr);\n        if(sayiter != mSaySoundsQueue.end())\n            mOutput->finishStream(sayiter->second.get());\n        sayiter = mActiveSaySounds.find(ptr);\n        if(sayiter != mActiveSaySounds.end())\n            mOutput->finishStream(sayiter->second.get());\n    }\n\n    void SoundManager::stopSound(const MWWorld::CellStore *cell)\n    {\n        for(SoundMap::value_type &snd : mActiveSounds)\n        {\n            if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell)\n            {\n                for(SoundBufferRefPair &sndbuf : snd.second)\n                    mOutput->finishSound(sndbuf.first.get());\n            }\n        }\n\n        for(SaySoundMap::value_type &snd : mSaySoundsQueue)\n        {\n            if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell)\n                mOutput->finishStream(snd.second.get());\n        }\n\n        for(SaySoundMap::value_type &snd : mActiveSaySounds)\n        {\n            if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell)\n                mOutput->finishStream(snd.second.get());\n        }\n    }\n\n    void SoundManager::fadeOutSound3D(const MWWorld::ConstPtr &ptr,\n            const std::string& soundId, float duration)\n    {\n        SoundMap::iterator snditer = mActiveSounds.find(ptr);\n        if(snditer != mActiveSounds.end())\n        {\n            Sound_Buffer *sfx = mSoundBuffers.lookup(Misc::StringUtils::lowerCase(soundId));\n            if (sfx == nullptr)\n                return;\n            for(SoundBufferRefPair &sndbuf : snditer->second)\n            {\n                if(sndbuf.second == sfx)\n                    sndbuf.first->setFadeout(duration);\n            }\n        }\n    }\n\n    bool SoundManager::getSoundPlaying(const MWWorld::ConstPtr &ptr, const std::string& soundId) const\n    {\n        SoundMap::const_iterator snditer = mActiveSounds.find(ptr);\n        if(snditer != mActiveSounds.end())\n        {\n            Sound_Buffer *sfx = mSoundBuffers.lookup(Misc::StringUtils::lowerCase(soundId));\n            return std::find_if(snditer->second.cbegin(), snditer->second.cend(),\n                [this,sfx](const SoundBufferRefPair &snd) -> bool\n                { return snd.second == sfx && mOutput->isSoundPlaying(snd.first.get()); }\n            ) != snditer->second.cend();\n        }\n        return false;\n    }\n\n    void SoundManager::pauseSounds(BlockerType blocker, int types)\n    {\n        if(mOutput->isInitialized())\n        {\n            if (mPausedSoundTypes[blocker] != 0)\n                resumeSounds(blocker);\n\n            types = types & Type::Mask;\n            mOutput->pauseSounds(types);\n            mPausedSoundTypes[blocker] = types;\n        }\n    }\n\n    void SoundManager::resumeSounds(BlockerType blocker)\n    {\n        if(mOutput->isInitialized())\n        {\n            mPausedSoundTypes[blocker] = 0;\n            int types = int(Type::Mask);\n            for (int currentBlocker = 0; currentBlocker < BlockerType::MaxCount; currentBlocker++)\n            {\n                if (currentBlocker != blocker)\n                    types &= ~mPausedSoundTypes[currentBlocker];\n            }\n\n            mOutput->resumeSounds(types);\n        }\n    }\n\n    void SoundManager::pausePlayback()\n    {\n        if (mPlaybackPaused)\n            return;\n\n        mPlaybackPaused = true;\n        mOutput->pauseActiveDevice();\n    }\n\n    void SoundManager::resumePlayback()\n    {\n        if (!mPlaybackPaused)\n            return;\n\n        mPlaybackPaused = false;\n        mOutput->resumeActiveDevice();\n    }\n\n    void SoundManager::updateRegionSound(float duration)\n    {\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        const MWWorld::ConstPtr player = world->getPlayerPtr();\n        const ESM::Cell *cell = player.getCell()->getCell();\n\n        if (!cell->isExterior())\n            return;\n        if (mCurrentRegionSound && mOutput->isSoundPlaying(mCurrentRegionSound))\n            return;\n\n        if (const auto next = mRegionSoundSelector.getNextRandom(duration, cell->mRegion, *world))\n            mCurrentRegionSound = playSound(*next, 1.0f, 1.0f);\n    }\n\n    void SoundManager::updateWaterSound()\n    {\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        const MWWorld::ConstPtr player = world->getPlayerPtr();\n        const ESM::Cell *curcell = player.getCell()->getCell();\n        const auto update = mWaterSoundUpdater.update(player, *world);\n\n        WaterSoundAction action;\n        Sound_Buffer* sfx;\n        std::tie(action, sfx) = getWaterSoundAction(update, curcell);\n\n        switch (action)\n        {\n            case WaterSoundAction::DoNothing:\n                break;\n            case WaterSoundAction::SetVolume:\n                mNearWaterSound->setVolume(update.mVolume * sfx->getVolume());\n                break;\n            case WaterSoundAction::FinishSound:\n                mOutput->finishSound(mNearWaterSound);\n                mNearWaterSound = nullptr;\n                break;\n            case WaterSoundAction::PlaySound:\n                if (mNearWaterSound)\n                    mOutput->finishSound(mNearWaterSound);\n                mNearWaterSound = playSound(update.mId, update.mVolume, 1.0f, Type::Sfx, PlayMode::Loop);\n                break;\n        }\n\n        mLastCell = curcell;\n    }\n\n    std::pair<SoundManager::WaterSoundAction, Sound_Buffer*> SoundManager::getWaterSoundAction(\n            const WaterSoundUpdate& update, const ESM::Cell* cell) const\n    {\n        if (mNearWaterSound)\n        {\n            if (update.mVolume == 0.0f)\n                return {WaterSoundAction::FinishSound, nullptr};\n\n            bool soundIdChanged = false;\n\n            Sound_Buffer* sfx = mSoundBuffers.lookup(update.mId);\n            if (mLastCell != cell)\n            {\n                const auto snditer = mActiveSounds.find(MWWorld::ConstPtr());\n                if (snditer != mActiveSounds.end())\n                {\n                    const auto pairiter = std::find_if(\n                        snditer->second.begin(), snditer->second.end(),\n                        [this](const SoundBufferRefPairList::value_type &item) -> bool\n                        { return mNearWaterSound == item.first.get(); }\n                    );\n                    if (pairiter != snditer->second.end() && pairiter->second != sfx)\n                        soundIdChanged = true;\n                }\n            }\n\n            if (soundIdChanged)\n                return {WaterSoundAction::PlaySound, nullptr};\n\n            if (sfx)\n                return {WaterSoundAction::SetVolume, sfx};\n        }\n        else if (update.mVolume > 0.0f)\n            return {WaterSoundAction::PlaySound, nullptr};\n\n        return {WaterSoundAction::DoNothing, nullptr};\n    }\n\n    void SoundManager::updateSounds(float duration)\n    {\n        // We update active say sounds map for specific actors here\n        // because for vanilla compatibility we can't do it immediately.\n        SaySoundMap::iterator queuesayiter = mSaySoundsQueue.begin();\n        while (queuesayiter != mSaySoundsQueue.end())\n        {\n            const auto dst = mActiveSaySounds.find(queuesayiter->first);\n            if (dst == mActiveSaySounds.end())\n                mActiveSaySounds.emplace(queuesayiter->first, std::move(queuesayiter->second));\n            else\n                dst->second = std::move(queuesayiter->second);\n            mSaySoundsQueue.erase(queuesayiter++);\n        }\n\n        mTimePassed += duration;\n        if (mTimePassed < sMinUpdateInterval)\n            return;\n        duration = mTimePassed;\n        mTimePassed = 0.0f;\n\n        // Make sure music is still playing\n        if(!isMusicPlaying() && !mCurrentPlaylist.empty())\n            startRandomTitle();\n\n        Environment env = Env_Normal;\n        if (mListenerUnderwater)\n            env = Env_Underwater;\n        else if(mUnderwaterSound)\n        {\n            mOutput->finishSound(mUnderwaterSound);\n            mUnderwaterSound = nullptr;\n        }\n\n        mOutput->startUpdate();\n        mOutput->updateListener(\n            mListenerPos,\n            mListenerDir,\n            mListenerUp,\n            env\n        );\n\n        updateMusic(duration);\n\n        // Check if any sounds are finished playing, and trash them\n        SoundMap::iterator snditer = mActiveSounds.begin();\n        while(snditer != mActiveSounds.end())\n        {\n            MWWorld::ConstPtr ptr = snditer->first;\n            SoundBufferRefPairList::iterator sndidx = snditer->second.begin();\n            while(sndidx != snditer->second.end())\n            {\n                Sound *sound = sndidx->first.get();\n\n                if(!ptr.isEmpty() && sound->getIs3D())\n                {\n                    const ESM::Position &pos = ptr.getRefData().getPosition();\n                    const osg::Vec3f objpos(pos.asVec3());\n                    sound->setPosition(objpos);\n\n                    if(sound->getDistanceCull())\n                    {\n                        if((mListenerPos - objpos).length2() > 2000*2000)\n                            mOutput->finishSound(sound);\n                    }\n                }\n\n                if(!mOutput->isSoundPlaying(sound))\n                {\n                    mOutput->finishSound(sound);\n                    if (sound == mUnderwaterSound)\n                        mUnderwaterSound = nullptr;\n                    if (sound == mNearWaterSound)\n                        mNearWaterSound = nullptr;\n                    mSoundBuffers.release(*sndidx->second);\n                    sndidx = snditer->second.erase(sndidx);\n                }\n                else\n                {\n                    sound->updateFade(duration);\n\n                    mOutput->updateSound(sound);\n                    ++sndidx;\n                }\n            }\n            if(snditer->second.empty())\n                snditer = mActiveSounds.erase(snditer);\n            else\n                ++snditer;\n        }\n\n        SaySoundMap::iterator sayiter = mActiveSaySounds.begin();\n        while(sayiter != mActiveSaySounds.end())\n        {\n            MWWorld::ConstPtr ptr = sayiter->first;\n            Stream *sound = sayiter->second.get();\n            if(!ptr.isEmpty() && sound->getIs3D())\n            {\n                MWBase::World *world = MWBase::Environment::get().getWorld();\n                const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans();\n                sound->setPosition(pos);\n\n                if(sound->getDistanceCull())\n                {\n                    if((mListenerPos - pos).length2() > 2000*2000)\n                        mOutput->finishStream(sound);\n                }\n            }\n\n            if(!mOutput->isStreamPlaying(sound))\n            {\n                mOutput->finishStream(sound);\n                mActiveSaySounds.erase(sayiter++);\n            }\n            else\n            {\n                sound->updateFade(duration);\n\n                mOutput->updateStream(sound);\n                ++sayiter;\n            }\n        }\n\n        TrackList::iterator trkiter = mActiveTracks.begin();\n        for(;trkiter != mActiveTracks.end();++trkiter)\n        {\n            Stream *sound = trkiter->get();\n            if(!mOutput->isStreamPlaying(sound))\n            {\n                mOutput->finishStream(sound);\n                trkiter = mActiveTracks.erase(trkiter);\n            }\n            else\n            {\n                sound->updateFade(duration);\n\n                mOutput->updateStream(sound);\n                ++trkiter;\n            }\n        }\n\n        if(mListenerUnderwater)\n        {\n            // Play underwater sound (after updating sounds)\n            if(!mUnderwaterSound)\n                mUnderwaterSound = playSound(\"Underwater\", 1.0f, 1.0f, Type::Sfx, PlayMode::LoopNoEnv);\n        }\n        mOutput->finishUpdate();\n    }\n\n\n    void SoundManager::updateMusic(float duration)\n    {\n        if (!mNextMusic.empty())\n        {\n            mMusic->updateFade(duration);\n\n            mOutput->updateStream(mMusic.get());\n\n            if (mMusic->getRealVolume() <= 0.f)\n            {\n                streamMusicFull(mNextMusic);\n                mNextMusic.clear();\n            }\n        }\n    }\n\n\n    void SoundManager::update(float duration)\n    {\n        if(!mOutput->isInitialized() || mPlaybackPaused)\n            return;\n\n        updateSounds(duration);\n        if (MWBase::Environment::get().getStateManager()->getState()!=\n            MWBase::StateManager::State_NoGame)\n        {\n            updateRegionSound(duration);\n            updateWaterSound();\n        }\n    }\n\n\n    void SoundManager::processChangedSettings(const Settings::CategorySettingVector& settings)\n    {\n        mVolumeSettings.update();\n\n        if(!mOutput->isInitialized())\n            return;\n        mOutput->startUpdate();\n        for(SoundMap::value_type &snd : mActiveSounds)\n        {\n            for(SoundBufferRefPair &sndbuf : snd.second)\n            {\n                Sound *sound = sndbuf.first.get();\n                sound->setBaseVolume(volumeFromType(sound->getPlayType()));\n                mOutput->updateSound(sound);\n            }\n        }\n        for(SaySoundMap::value_type &snd : mActiveSaySounds)\n        {\n            Stream *sound = snd.second.get();\n            sound->setBaseVolume(volumeFromType(sound->getPlayType()));\n            mOutput->updateStream(sound);\n        }\n        for(SaySoundMap::value_type &snd : mSaySoundsQueue)\n        {\n            Stream *sound = snd.second.get();\n            sound->setBaseVolume(volumeFromType(sound->getPlayType()));\n            mOutput->updateStream(sound);\n        }\n        for (const StreamPtr& sound : mActiveTracks)\n        {\n            sound->setBaseVolume(volumeFromType(sound->getPlayType()));\n            mOutput->updateStream(sound.get());\n        }\n        if(mMusic)\n        {\n            mMusic->setBaseVolume(volumeFromType(mMusic->getPlayType()));\n            mOutput->updateStream(mMusic.get());\n        }\n        mOutput->finishUpdate();\n    }\n\n    void SoundManager::setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater)\n    {\n        mListenerPos = pos;\n        mListenerDir = dir;\n        mListenerUp  = up;\n\n        mListenerUnderwater = underwater;\n\n        mWaterSoundUpdater.setUnderwater(underwater);\n    }\n\n    void SoundManager::updatePtr(const MWWorld::ConstPtr &old, const MWWorld::ConstPtr &updated)\n    {\n        SoundMap::iterator snditer = mActiveSounds.find(old);\n        if(snditer != mActiveSounds.end())\n        {\n            SoundBufferRefPairList sndlist = std::move(snditer->second);\n            mActiveSounds.erase(snditer);\n            mActiveSounds.emplace(updated, std::move(sndlist));\n        }\n\n        SaySoundMap::iterator sayiter = mSaySoundsQueue.find(old);\n        if(sayiter != mSaySoundsQueue.end())\n        {\n            StreamPtr stream = std::move(sayiter->second);\n            mSaySoundsQueue.erase(sayiter);\n            mSaySoundsQueue.emplace(updated, std::move(stream));\n        }\n\n        sayiter = mActiveSaySounds.find(old);\n        if(sayiter != mActiveSaySounds.end())\n        {\n            StreamPtr stream = std::move(sayiter->second);\n            mActiveSaySounds.erase(sayiter);\n            mActiveSaySounds.emplace(updated, std::move(stream));\n        }\n    }\n\n    // Default readAll implementation, for decoders that can't do anything\n    // better\n    void Sound_Decoder::readAll(std::vector<char> &output)\n    {\n        size_t total = output.size();\n        size_t got;\n\n        output.resize(total+32768);\n        while((got=read(&output[total], output.size()-total)) > 0)\n        {\n            total += got;\n            output.resize(total*2);\n        }\n        output.resize(total);\n    }\n\n\n    const char *getSampleTypeName(SampleType type)\n    {\n        switch(type)\n        {\n            case SampleType_UInt8: return \"U8\";\n            case SampleType_Int16: return \"S16\";\n            case SampleType_Float32: return \"Float32\";\n        }\n        return \"(unknown sample type)\";\n    }\n\n    const char *getChannelConfigName(ChannelConfig config)\n    {\n        switch(config)\n        {\n            case ChannelConfig_Mono:    return \"Mono\";\n            case ChannelConfig_Stereo:  return \"Stereo\";\n            case ChannelConfig_Quad:    return \"Quad\";\n            case ChannelConfig_5point1: return \"5.1 Surround\";\n            case ChannelConfig_7point1: return \"7.1 Surround\";\n        }\n        return \"(unknown channel config)\";\n    }\n\n    size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type)\n    {\n        switch(config)\n        {\n            case ChannelConfig_Mono:    frames *= 1; break;\n            case ChannelConfig_Stereo:  frames *= 2; break;\n            case ChannelConfig_Quad:    frames *= 4; break;\n            case ChannelConfig_5point1: frames *= 6; break;\n            case ChannelConfig_7point1: frames *= 8; break;\n        }\n        switch(type)\n        {\n            case SampleType_UInt8: frames *= 1; break;\n            case SampleType_Int16: frames *= 2; break;\n            case SampleType_Float32: frames *= 4; break;\n        }\n        return frames;\n    }\n\n    size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type)\n    {\n        return bytes / framesToBytes(1, config, type);\n    }\n\n    void SoundManager::clear()\n    {\n        SoundManager::stopMusic();\n\n        for(SoundMap::value_type &snd : mActiveSounds)\n        {\n            for(SoundBufferRefPair &sndbuf : snd.second)\n            {\n                mOutput->finishSound(sndbuf.first.get());\n                mSoundBuffers.release(*sndbuf.second);\n            }\n        }\n        mActiveSounds.clear();\n        mUnderwaterSound = nullptr;\n        mNearWaterSound = nullptr;\n\n        for(SaySoundMap::value_type &snd : mSaySoundsQueue)\n            mOutput->finishStream(snd.second.get());\n        mSaySoundsQueue.clear();\n\n        for(SaySoundMap::value_type &snd : mActiveSaySounds)\n            mOutput->finishStream(snd.second.get());\n        mActiveSaySounds.clear();\n\n        for(StreamPtr& sound : mActiveTracks)\n            mOutput->finishStream(sound.get());\n        mActiveTracks.clear();\n        mPlaybackPaused = false;\n        std::fill(std::begin(mPausedSoundTypes), std::end(mPausedSoundTypes), 0);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/soundmanagerimp.hpp",
    "content": "#ifndef GAME_SOUND_SOUNDMANAGER_H\n#define GAME_SOUND_SOUNDMANAGER_H\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <map>\n#include <unordered_map>\n\n#include <components/settings/settings.hpp>\n#include <components/misc/objectpool.hpp>\n#include <components/fallback/fallback.hpp>\n\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"regionsoundselector.hpp\"\n#include \"watersoundupdater.hpp\"\n#include \"type.hpp\"\n#include \"volumesettings.hpp\"\n#include \"sound_buffer.hpp\"\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace ESM\n{\n    struct Sound;\n    struct Cell;\n}\n\nnamespace MWSound\n{\n    class Sound_Output;\n    struct Sound_Decoder;\n    class Sound;\n    class Stream;\n\n    using SoundPtr = Misc::ObjectPtr<Sound>;\n    using StreamPtr = Misc::ObjectPtr<Stream>;\n\n    class SoundManager : public MWBase::SoundManager\n    {\n        const VFS::Manager* mVFS;\n\n        std::unique_ptr<Sound_Output> mOutput;\n\n        // Caches available music tracks by <playlist name, (sound files) >\n        std::unordered_map<std::string, std::vector<std::string>> mMusicFiles;\n        std::unordered_map<std::string, std::vector<int>> mMusicToPlay; // A list with music files not yet played\n        std::string mLastPlayedMusic; // The music file that was last played\n\n        VolumeSettings mVolumeSettings;\n\n        WaterSoundUpdater mWaterSoundUpdater;\n\n        SoundBufferPool mSoundBuffers;\n\n        Misc::ObjectPool<Sound> mSounds;\n\n        Misc::ObjectPool<Stream> mStreams;\n\n        typedef std::pair<SoundPtr, Sound_Buffer*> SoundBufferRefPair;\n        typedef std::vector<SoundBufferRefPair> SoundBufferRefPairList;\n        typedef std::map<MWWorld::ConstPtr,SoundBufferRefPairList> SoundMap;\n        SoundMap mActiveSounds;\n\n        typedef std::map<MWWorld::ConstPtr, StreamPtr> SaySoundMap;\n        SaySoundMap mSaySoundsQueue;\n        SaySoundMap mActiveSaySounds;\n\n        typedef std::vector<StreamPtr> TrackList;\n        TrackList mActiveTracks;\n\n        StreamPtr mMusic;\n        std::string mCurrentPlaylist;\n\n        bool mListenerUnderwater;\n        osg::Vec3f mListenerPos;\n        osg::Vec3f mListenerDir;\n        osg::Vec3f mListenerUp;\n\n        int mPausedSoundTypes[BlockerType::MaxCount] = {};\n\n        Sound *mUnderwaterSound;\n        Sound *mNearWaterSound;\n\n        std::string mNextMusic;\n        bool mPlaybackPaused;\n\n        RegionSoundSelector mRegionSoundSelector;\n\n        float mTimePassed;\n\n        const ESM::Cell *mLastCell;\n\n        Sound* mCurrentRegionSound;\n\n        Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound);\n\n        // returns a decoder to start streaming, or nullptr if the sound was not found\n        DecoderPtr loadVoice(const std::string &voicefile);\n\n        SoundPtr getSoundRef();\n        StreamPtr getStreamRef();\n\n        StreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal);\n\n        void streamMusicFull(const std::string& filename);\n        void advanceMusic(const std::string& filename);\n        void startRandomTitle();\n\n        void updateSounds(float duration);\n        void updateRegionSound(float duration);\n        void updateWaterSound();\n        void updateMusic(float duration);\n\n        float volumeFromType(Type type) const;\n\n        enum class WaterSoundAction\n        {\n            DoNothing,\n            SetVolume,\n            FinishSound,\n            PlaySound,\n        };\n\n        std::pair<WaterSoundAction, Sound_Buffer*> getWaterSoundAction(const WaterSoundUpdate& update,\n                                                                       const ESM::Cell* cell) const;\n\n        SoundManager(const SoundManager &rhs);\n        SoundManager& operator=(const SoundManager &rhs);\n\n    protected:\n        DecoderPtr getDecoder();\n        friend class OpenAL_Output;\n\n        void stopSound(Sound_Buffer *sfx, const MWWorld::ConstPtr &ptr);\n        ///< Stop the given object from playing given sound buffer.\n\n    public:\n        SoundManager(const VFS::Manager* vfs, bool useSound);\n        ~SoundManager() override;\n\n        void processChangedSettings(const Settings::CategorySettingVector& settings) override;\n\n        void stopMusic() override;\n        ///< Stops music if it's playing\n\n        void streamMusic(const std::string& filename) override;\n        ///< Play a soundifle\n        /// \\param filename name of a sound file in \"Music/\" in the data directory.\n\n        bool isMusicPlaying() override;\n        ///< Returns true if music is playing\n\n        void playPlaylist(const std::string &playlist) override;\n        ///< Start playing music from the selected folder\n        /// \\param name of the folder that contains the playlist\n\n        void playTitleMusic() override;\n        ///< Start playing title music\n\n        void say(const MWWorld::ConstPtr &reference, const std::string& filename) override;\n        ///< Make an actor say some text.\n        /// \\param filename name of a sound file in \"Sound/\" in the data directory.\n\n        void say(const std::string& filename) override;\n        ///< Say some text, without an actor ref\n        /// \\param filename name of a sound file in \"Sound/\" in the data directory.\n\n        bool sayActive(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const override;\n        ///< Is actor not speaking?\n\n        bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const override;\n        ///< For scripting backward compatibility\n\n        void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) override;\n        ///< Stop an actor speaking\n\n        float getSaySoundLoudness(const MWWorld::ConstPtr& reference) const override;\n        ///< Check the currently playing say sound for this actor\n        /// and get an average loudness value (scale [0,1]) at the current time position.\n        /// If the actor is not saying anything, returns 0.\n\n        Stream *playTrack(const DecoderPtr& decoder, Type type) override;\n        ///< Play a 2D audio track, using a custom decoder\n\n        void stopTrack(Stream *stream) override;\n        ///< Stop the given audio track from playing\n\n        double getTrackTimeDelay(Stream *stream) override;\n        ///< Retives the time delay, in seconds, of the audio track (must be a sound\n        /// returned by \\ref playTrack). Only intended to be called by the track\n        /// decoder's read method.\n\n        Sound *playSound(const std::string& soundId, float volume, float pitch, Type type=Type::Sfx, PlayMode mode=PlayMode::Normal, float offset=0) override;\n        ///< Play a sound, independently of 3D-position\n        ///< @param offset Number of seconds into the sound to start playback.\n\n        Sound *playSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId,\n                                   float volume, float pitch, Type type=Type::Sfx,\n                                   PlayMode mode=PlayMode::Normal, float offset=0) override;\n        ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified.\n        ///< @param offset Number of seconds into the sound to start playback.\n\n        Sound *playSound3D(const osg::Vec3f& initialPos, const std::string& soundId,\n                                   float volume, float pitch, Type type, PlayMode mode, float offset=0) override;\n        ///< Play a 3D sound at \\a initialPos. If the sound should be moving, it must be updated using Sound::setPosition.\n        ///< @param offset Number of seconds into the sound to start playback.\n\n        void stopSound(Sound *sound) override;\n        ///< Stop the given sound from playing\n        /// @note no-op if \\a sound is null\n\n        void stopSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId) override;\n        ///< Stop the given object from playing the given sound,\n\n        void stopSound3D(const MWWorld::ConstPtr &reference) override;\n        ///< Stop the given object from playing all sounds.\n\n        void stopSound(const MWWorld::CellStore *cell) override;\n        ///< Stop all sounds for the given cell.\n\n        void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration) override;\n        ///< Fade out given sound (that is already playing) of given object\n        ///< @param reference Reference to object, whose sound is faded out\n        ///< @param soundId ID of the sound to fade out.\n        ///< @param duration Time until volume reaches 0.\n\n        bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const override;\n        ///< Is the given sound currently playing on the given object?\n\n        void pauseSounds(MWSound::BlockerType blocker, int types=int(Type::Mask)) override;\n        ///< Pauses all currently playing sounds, including music.\n\n        void resumeSounds(MWSound::BlockerType blocker) override;\n        ///< Resumes all previously paused sounds.\n\n        void pausePlayback() override;\n        void resumePlayback() override;\n\n        void update(float duration) override;\n\n        void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater) override;\n\n        void updatePtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) override;\n\n        void clear() override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/type.hpp",
    "content": "#ifndef GAME_SOUND_TYPE_H\n#define GAME_SOUND_TYPE_H\n\nnamespace MWSound\n{\n    enum class Type\n    {\n        Sfx   = 1 << 4, /* Normal SFX sound */\n        Voice = 1 << 5, /* Voice sound */\n        Foot  = 1 << 6, /* Footstep sound */\n        Music = 1 << 7, /* Music track */\n        Movie = 1 << 8, /* Movie audio track */\n        Mask  = Sfx | Voice | Foot | Music | Movie\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/volumesettings.cpp",
    "content": "#include \"volumesettings.hpp\"\n\n#include <components/settings/settings.hpp>\n\n#include <algorithm>\n\nnamespace MWSound\n{\n    namespace\n    {\n        float clamp(float value)\n        {\n            return std::max(0.0f, std::min(1.0f, value));\n        }\n    }\n\n    VolumeSettings::VolumeSettings()\n        : mMasterVolume(clamp(Settings::Manager::getFloat(\"master volume\", \"Sound\"))),\n          mSFXVolume(clamp(Settings::Manager::getFloat(\"sfx volume\", \"Sound\"))),\n          mMusicVolume(clamp(Settings::Manager::getFloat(\"music volume\", \"Sound\"))),\n          mVoiceVolume(clamp(Settings::Manager::getFloat(\"voice volume\", \"Sound\"))),\n          mFootstepsVolume(clamp(Settings::Manager::getFloat(\"footsteps volume\", \"Sound\")))\n    {\n    }\n\n    float VolumeSettings::getVolumeFromType(Type type) const\n    {\n        float volume = mMasterVolume;\n\n        switch(type)\n        {\n            case Type::Sfx:\n                volume *= mSFXVolume;\n                break;\n            case Type::Voice:\n                volume *= mVoiceVolume;\n                break;\n            case Type::Foot:\n                volume *= mFootstepsVolume;\n                break;\n            case Type::Music:\n                volume *= mMusicVolume;\n                break;\n            case Type::Movie:\n            case Type::Mask:\n                break;\n        }\n\n        return volume;\n    }\n\n    void VolumeSettings::update()\n    {\n        *this = VolumeSettings();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/volumesettings.hpp",
    "content": "#ifndef GAME_SOUND_VOLUMESETTINGS_H\n#define GAME_SOUND_VOLUMESETTINGS_H\n\n#include \"type.hpp\"\n\nnamespace MWSound\n{\n    class VolumeSettings\n    {\n        public:\n            VolumeSettings();\n\n            float getVolumeFromType(Type type) const;\n\n            void update();\n\n        private:\n            float mMasterVolume;\n            float mSFXVolume;\n            float mMusicVolume;\n            float mVoiceVolume;\n            float mFootstepsVolume;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwsound/watersoundupdater.cpp",
    "content": "#include \"watersoundupdater.hpp\"\n\n#include \"../mwbase/world.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/ptr.hpp\"\n\n#include <components/esm/loadcell.hpp>\n\n#include <osg/Vec3f>\n\nnamespace MWSound\n{\n    WaterSoundUpdater::WaterSoundUpdater(const WaterSoundUpdaterSettings& settings)\n        : mSettings(settings)\n    {\n    }\n\n    WaterSoundUpdate WaterSoundUpdater::update(const MWWorld::ConstPtr& player, const MWBase::World& world) const\n    {\n        WaterSoundUpdate result;\n\n        result.mId = player.getCell()->isExterior() ? mSettings.mNearWaterOutdoorID : mSettings.mNearWaterIndoorID;\n        result.mVolume = std::min(1.0f, getVolume(player, world));\n\n        return result;\n    }\n\n    float WaterSoundUpdater::getVolume(const MWWorld::ConstPtr& player, const MWBase::World& world) const\n    {\n        if (mListenerUnderwater)\n            return 1.0f;\n\n        const MWWorld::CellStore& cell = *player.getCell();\n\n        if (!cell.getCell()->hasWater())\n            return 0.0f;\n\n        const osg::Vec3f pos = player.getRefData().getPosition().asVec3();\n        const float dist = std::abs(cell.getWaterLevel() - pos.z());\n\n        if (cell.isExterior() && dist < mSettings.mNearWaterOutdoorTolerance)\n        {\n            if (mSettings.mNearWaterPoints <= 1)\n                return (mSettings.mNearWaterOutdoorTolerance - dist) / mSettings.mNearWaterOutdoorTolerance;\n\n            const float step = mSettings.mNearWaterRadius * 2.0f / (mSettings.mNearWaterPoints - 1);\n\n            int underwaterPoints = 0;\n\n            for (int x = 0; x < mSettings.mNearWaterPoints; x++)\n            {\n                for (int y = 0; y < mSettings.mNearWaterPoints; y++)\n                {\n                    const float terrainX = pos.x() - mSettings.mNearWaterRadius + x * step;\n                    const float terrainY = pos.y() - mSettings.mNearWaterRadius + y * step;\n                    const float height = world.getTerrainHeightAt(osg::Vec3f(terrainX, terrainY, 0.0f));\n\n                    if (height < 0)\n                        underwaterPoints++;\n                }\n            }\n\n            return underwaterPoints * 2.0f / (mSettings.mNearWaterPoints * mSettings.mNearWaterPoints);\n        }\n\n        if (!cell.isExterior() && dist < mSettings.mNearWaterIndoorTolerance)\n            return (mSettings.mNearWaterIndoorTolerance - dist) / mSettings.mNearWaterIndoorTolerance;\n\n        return 0.0f;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwsound/watersoundupdater.hpp",
    "content": "#ifndef GAME_SOUND_WATERSOUNDUPDATER_H\n#define GAME_SOUND_WATERSOUNDUPDATER_H\n\n#include <string>\n\nnamespace MWBase\n{\n    class World;\n}\n\nnamespace MWWorld\n{\n    class ConstPtr;\n}\n\nnamespace MWSound\n{\n    struct WaterSoundUpdaterSettings\n    {\n        int mNearWaterRadius;\n        int mNearWaterPoints;\n        float mNearWaterIndoorTolerance;\n        float mNearWaterOutdoorTolerance;\n        std::string mNearWaterIndoorID;\n        std::string mNearWaterOutdoorID;\n    };\n\n    struct WaterSoundUpdate\n    {\n        std::string mId;\n        float mVolume;\n    };\n\n    class WaterSoundUpdater\n    {\n        public:\n            explicit WaterSoundUpdater(const WaterSoundUpdaterSettings& settings);\n\n            WaterSoundUpdate update(const MWWorld::ConstPtr& player, const MWBase::World& world) const;\n\n            void setUnderwater(bool value)\n            {\n                mListenerUnderwater = value;\n            }\n\n        private:\n            const WaterSoundUpdaterSettings mSettings;\n            bool mListenerUnderwater = false;\n\n            float getVolume(const MWWorld::ConstPtr& player, const MWBase::World& world) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwstate/character.cpp",
    "content": "#include \"character.hpp\"\n\n#include <cctype>\n#include <sstream>\n\n#include <boost/filesystem.hpp>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/defs.hpp>\n\nbool MWState::operator< (const Slot& left, const Slot& right)\n{\n    return left.mTimeStamp<right.mTimeStamp;\n}\n\n\nvoid MWState::Character::addSlot (const boost::filesystem::path& path, const std::string& game)\n{\n    Slot slot;\n    slot.mPath = path;\n    slot.mTimeStamp = boost::filesystem::last_write_time (path);\n\n    ESM::ESMReader reader;\n    reader.open (slot.mPath.string());\n\n    if (reader.getRecName()!=ESM::REC_SAVE)\n        return; // invalid save file -> ignore\n\n    reader.getRecHeader();\n\n    slot.mProfile.load (reader);\n\n    if (Misc::StringUtils::lowerCase (slot.mProfile.mContentFiles.at (0))!=\n        Misc::StringUtils::lowerCase (game))\n        return; // this file is for a different game -> ignore\n\n    mSlots.push_back (slot);\n}\n\nvoid MWState::Character::addSlot (const ESM::SavedGame& profile)\n{\n    Slot slot;\n\n    std::ostringstream stream;\n\n    // The profile description is user-supplied, so we need to escape the path\n    for (std::string::const_iterator it = profile.mDescription.begin(); it != profile.mDescription.end(); ++it)\n    {\n        if (std::isalnum(*it))  // Ignores multibyte characters and non alphanumeric characters\n            stream << *it;\n        else\n            stream << \"_\";\n    }\n\n    const std::string ext = \".omwsave\";\n    slot.mPath = mPath / (stream.str() + ext);\n\n    // Append an index if necessary to ensure a unique file\n    int i=0;\n    while (boost::filesystem::exists(slot.mPath))\n    {\n        const std::string test = stream.str() + \" - \" + std::to_string(++i);\n        slot.mPath = mPath / (test + ext);\n    }\n\n    slot.mProfile = profile;\n    slot.mTimeStamp = std::time (nullptr);\n\n    mSlots.push_back (slot);\n}\n\nMWState::Character::Character (const boost::filesystem::path& saves, const std::string& game)\n: mPath (saves)\n{\n    if (!boost::filesystem::is_directory (mPath))\n    {\n        boost::filesystem::create_directories (mPath);\n    }\n    else\n    {\n        for (boost::filesystem::directory_iterator iter (mPath);\n            iter!=boost::filesystem::directory_iterator(); ++iter)\n        {\n            boost::filesystem::path slotPath = *iter;\n\n            try\n            {\n                addSlot (slotPath, game);\n            }\n            catch (...) {} // ignoring bad saved game files for now\n        }\n\n        std::sort (mSlots.begin(), mSlots.end());\n    }\n}\n\nvoid MWState::Character::cleanup()\n{\n    if (mSlots.size() == 0)\n    {\n        // All slots are gone, no need to keep the empty directory\n        if (boost::filesystem::is_directory (mPath))\n        {\n            // Extra safety check to make sure the directory is empty (e.g. slots failed to parse header)\n            boost::filesystem::directory_iterator it(mPath);\n            if (it == boost::filesystem::directory_iterator())\n                boost::filesystem::remove_all(mPath);\n        }\n    }\n}\n\nconst MWState::Slot *MWState::Character::createSlot (const ESM::SavedGame& profile)\n{\n    addSlot (profile);\n\n    return &mSlots.back();\n}\n\nvoid MWState::Character::deleteSlot (const Slot *slot)\n{\n    int index = slot - &mSlots[0];\n\n    if (index<0 || index>=static_cast<int> (mSlots.size()))\n    {\n        // sanity check; not entirely reliable\n        throw std::logic_error (\"slot not found\");\n    }\n\n    boost::filesystem::remove(slot->mPath);\n\n    mSlots.erase (mSlots.begin()+index);\n}\n\nconst MWState::Slot *MWState::Character::updateSlot (const Slot *slot, const ESM::SavedGame& profile)\n{\n    int index = slot - &mSlots[0];\n\n    if (index<0 || index>=static_cast<int> (mSlots.size()))\n    {\n        // sanity check; not entirely reliable\n        throw std::logic_error (\"slot not found\");\n    }\n\n    Slot newSlot = *slot;\n    newSlot.mProfile = profile;\n    newSlot.mTimeStamp = std::time (nullptr);\n\n    mSlots.erase (mSlots.begin()+index);\n\n    mSlots.push_back (newSlot);\n\n    return &mSlots.back();\n}\n\nMWState::Character::SlotIterator MWState::Character::begin() const\n{\n    return mSlots.rbegin();\n}\n\nMWState::Character::SlotIterator MWState::Character::end() const\n{\n    return mSlots.rend();\n}\n\nESM::SavedGame MWState::Character::getSignature() const\n{\n    if (mSlots.empty())\n        throw std::logic_error (\"character signature not available\");\n\n    std::vector<Slot>::const_iterator iter (mSlots.begin());\n\n    Slot slot = *iter;\n\n    for (++iter; iter!=mSlots.end(); ++iter)\n        if (iter->mProfile.mPlayerLevel>slot.mProfile.mPlayerLevel)\n            slot = *iter;\n        else if (iter->mProfile.mPlayerLevel==slot.mProfile.mPlayerLevel &&\n            iter->mTimeStamp>slot.mTimeStamp)\n            slot = *iter;\n\n    return slot.mProfile;\n}\n\nconst boost::filesystem::path& MWState::Character::getPath() const\n{\n    return mPath;\n}\n"
  },
  {
    "path": "apps/openmw/mwstate/character.hpp",
    "content": "#ifndef GAME_STATE_CHARACTER_H\n#define GAME_STATE_CHARACTER_H\n\n#include <boost/filesystem/path.hpp>\n\n#include <components/esm/savedgame.hpp>\n\nnamespace MWState\n{\n    struct Slot\n    {\n        boost::filesystem::path mPath;\n        ESM::SavedGame mProfile;\n        std::time_t mTimeStamp;\n    };\n\n    bool operator< (const Slot& left, const Slot& right);\n\n    class Character\n    {\n        public:\n\n            typedef std::vector<Slot>::const_reverse_iterator SlotIterator;\n\n        private:\n\n            boost::filesystem::path mPath;\n            std::vector<Slot> mSlots;\n\n            void addSlot (const boost::filesystem::path& path, const std::string& game);\n\n            void addSlot (const ESM::SavedGame& profile);\n\n        public:\n\n            Character (const boost::filesystem::path& saves, const std::string& game);\n\n            void cleanup();\n            ///< Delete the directory we used, if it is empty\n\n            const Slot *createSlot (const ESM::SavedGame& profile);\n            ///< Create new slot.\n            ///\n            /// \\attention The ownership of the slot is not transferred.\n\n            /// \\note Slot must belong to this character.\n            ///\n            /// \\attention The \\a slot pointer will be invalidated by this call.\n            void deleteSlot (const Slot *slot);\n\n            const Slot *updateSlot (const Slot *slot, const ESM::SavedGame& profile);\n            /// \\note Slot must belong to this character.\n            ///\n            /// \\attention The \\a slot pointer will be invalidated by this call.\n\n            SlotIterator begin() const;\n            ///<  Any call to createSlot and updateSlot can invalidate the returned iterator.\n\n            SlotIterator end() const;\n\n            const boost::filesystem::path& getPath() const;\n\n            ESM::SavedGame getSignature() const;\n            ///< Return signature information for this character.\n            ///\n            /// \\attention This function must not be called if there are no slots.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwstate/charactermanager.cpp",
    "content": "#include \"charactermanager.hpp\"\n\n#include <cctype>\n#include <sstream>\n\n#include <boost/filesystem.hpp>\n\nMWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves,\n    const std::string& game)\n: mPath (saves), mCurrent (nullptr), mGame (game)\n{\n    if (!boost::filesystem::is_directory (mPath))\n    {\n        boost::filesystem::create_directories (mPath);\n    }\n    else\n    {\n        for (boost::filesystem::directory_iterator iter (mPath);\n            iter!=boost::filesystem::directory_iterator(); ++iter)\n        {\n            boost::filesystem::path characterDir = *iter;\n\n            if (boost::filesystem::is_directory (characterDir))\n            {\n                Character character (characterDir, mGame);\n\n                if (character.begin()!=character.end())\n                    mCharacters.push_back (character);\n            }\n        }\n    }\n}\n\nMWState::Character *MWState::CharacterManager::getCurrentCharacter ()\n{\n    return mCurrent;\n}\n\nvoid MWState::CharacterManager::deleteSlot(const MWState::Character *character, const MWState::Slot *slot)\n{\n    std::list<Character>::iterator it = findCharacter(character);\n\n    it->deleteSlot(slot);\n\n    if (character->begin() == character->end())\n    {\n        // All slots deleted, cleanup and remove this character\n        it->cleanup();\n        if (character == mCurrent)\n            mCurrent = nullptr;\n        mCharacters.erase(it);\n    }\n}\n\nMWState::Character* MWState::CharacterManager::createCharacter(const std::string& name)\n{\n    std::ostringstream stream;\n\n    // The character name is user-supplied, so we need to escape the path\n    for (std::string::const_iterator it = name.begin(); it != name.end(); ++it)\n    {\n        if (std::isalnum(*it)) // Ignores multibyte characters and non alphanumeric characters\n            stream << *it;\n        else\n            stream << \"_\";\n    }\n\n    boost::filesystem::path path = mPath / stream.str();\n\n    // Append an index if necessary to ensure a unique directory\n    int i=0;\n    while (boost::filesystem::exists(path))\n    {\n           std::ostringstream test;\n           test << stream.str();\n           test << \" - \" << ++i;\n           path = mPath / test.str();\n    }\n\n    mCharacters.emplace_back(path, mGame);\n    return &mCharacters.back();\n}\n\nstd::list<MWState::Character>::iterator MWState::CharacterManager::findCharacter(const MWState::Character* character)\n{\n    std::list<Character>::iterator it = mCharacters.begin();\n    for (; it != mCharacters.end(); ++it)\n    {\n        if (&*it == character)\n            break;\n    }\n    if (it == mCharacters.end())\n        throw std::logic_error (\"invalid character\");\n    return it;\n}\n\nvoid MWState::CharacterManager::setCurrentCharacter (const Character *character)\n{\n    if (!character)\n        mCurrent = nullptr;\n    else\n    {\n        std::list<Character>::iterator it = findCharacter(character);\n\n        mCurrent = &*it;\n    }\n}\n\n\nstd::list<MWState::Character>::const_iterator MWState::CharacterManager::begin() const\n{\n    return mCharacters.begin();\n}\n\nstd::list<MWState::Character>::const_iterator MWState::CharacterManager::end() const\n{\n    return mCharacters.end();\n}\n"
  },
  {
    "path": "apps/openmw/mwstate/charactermanager.hpp",
    "content": "#ifndef GAME_STATE_CHARACTERMANAGER_H\n#define GAME_STATE_CHARACTERMANAGER_H\n\n#include <boost/filesystem/path.hpp>\n\n#include \"character.hpp\"\n\nnamespace MWState\n{\n    class CharacterManager\n    {\n            boost::filesystem::path mPath;\n\n            // Uses std::list, so that mCurrent stays valid when characters are deleted\n            std::list<Character> mCharacters;\n\n            Character *mCurrent;\n            std::string mGame;\n\n        private:\n\n            CharacterManager (const CharacterManager&);\n            ///< Not implemented\n\n            CharacterManager& operator= (const CharacterManager&);\n            ///< Not implemented\n\n            std::list<Character>::iterator findCharacter(const MWState::Character* character);\n\n        public:\n\n            CharacterManager (const boost::filesystem::path& saves, const std::string& game);\n\n            Character *getCurrentCharacter ();\n            ///< @note May return null\n\n            void deleteSlot(const MWState::Character *character, const MWState::Slot *slot);\n\n            Character* createCharacter(const std::string& name);\n            ///< Create new character within saved game management\n            /// \\param name Name for the character (does not need to be unique)\n\n            void setCurrentCharacter (const Character *character);\n\n            std::list<Character>::const_iterator begin() const;\n\n            std::list<Character>::const_iterator end() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwstate/quicksavemanager.cpp",
    "content": "#include \"quicksavemanager.hpp\"\n\nMWState::QuickSaveManager::QuickSaveManager(std::string &saveName, unsigned int maxSaves)\n  : mSaveName(saveName)\n  , mMaxSaves(maxSaves)\n  , mSlotsVisited(0)\n  , mOldestSlotVisited(nullptr)\n{\n}\n\nvoid MWState::QuickSaveManager::visitSave(const Slot *saveSlot)\n{\n    if(mSaveName == saveSlot->mProfile.mDescription)\n    {\n        ++mSlotsVisited;\n        if(isOldestSave(saveSlot))\n            mOldestSlotVisited = saveSlot;\n    }\n}\n\nbool MWState::QuickSaveManager::isOldestSave(const Slot *compare) const\n{\n    if(mOldestSlotVisited == nullptr)\n        return true;\n    return (compare->mTimeStamp <= mOldestSlotVisited->mTimeStamp);\n}\n\nbool MWState::QuickSaveManager::shouldCreateNewSlot() const\n{\n    return (mSlotsVisited < mMaxSaves);\n}\n\nconst MWState::Slot *MWState::QuickSaveManager::getNextQuickSaveSlot()\n{\n    if(shouldCreateNewSlot())\n        return nullptr;\n    return mOldestSlotVisited;\n}\n"
  },
  {
    "path": "apps/openmw/mwstate/quicksavemanager.hpp",
    "content": "#ifndef GAME_STATE_QUICKSAVEMANAGER_H\n#define GAME_STATE_QUICKSAVEMANAGER_H\n\n#include <string>\n\n#include \"character.hpp\"\n\nnamespace MWState{\n    class QuickSaveManager{\n        std::string mSaveName;\n        unsigned int mMaxSaves;\n        unsigned int mSlotsVisited;\n        const Slot *mOldestSlotVisited;\n    private:\n        bool shouldCreateNewSlot() const;\n        bool isOldestSave(const Slot *compare) const;\n    public:\n        QuickSaveManager(std::string &saveName, unsigned int maxSaves);\n        ///< A utility class to manage multiple quicksave slots\n        ///\n        /// \\param saveName The name of the save (\"QuickSave\", \"AutoSave\", etc)\n        /// \\param maxSaves The maximum number of save slots to create before recycling old ones\n\n        void visitSave(const Slot *saveSlot);\n        ///< Visits the given \\a slot \\a\n\n        const Slot *getNextQuickSaveSlot();\n        ///< Get the slot that the next quicksave should use.\n        ///\n        ///\\return Either the oldest quicksave slot visited, or nullptr if a new slot can be made\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwstate/statemanagerimp.cpp",
    "content": "#include \"statemanagerimp.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/cellid.hpp>\n#include <components/esm/loadcell.hpp>\n\n#include <components/loadinglistener/loadinglistener.hpp>\n\n#include <components/settings/settings.hpp>\n\n#include <osg/Image>\n\n#include <osgDB/Registry>\n\n#include <boost/filesystem/fstream.hpp>\n#include <boost/filesystem/operations.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/journal.hpp\"\n#include \"../mwbase/dialoguemanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/inputmanager.hpp\"\n\n#include \"../mwworld/player.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/cellstore.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwscript/globalscripts.hpp\"\n\n#include \"quicksavemanager.hpp\"\n\nvoid MWState::StateManager::cleanup (bool force)\n{\n    if (mState!=State_NoGame || force)\n    {\n        MWBase::Environment::get().getSoundManager()->clear();\n        MWBase::Environment::get().getDialogueManager()->clear();\n        MWBase::Environment::get().getJournal()->clear();\n        MWBase::Environment::get().getScriptManager()->clear();\n        MWBase::Environment::get().getWindowManager()->clear();\n        MWBase::Environment::get().getWorld()->clear();\n        MWBase::Environment::get().getInputManager()->clear();\n        MWBase::Environment::get().getMechanicsManager()->clear();\n\n        mState = State_NoGame;\n        mCharacterManager.setCurrentCharacter(nullptr);\n        mTimePlayed = 0;\n\n        MWMechanics::CreatureStats::cleanup();\n    }\n}\n\nstd::map<int, int> MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader)\n    const\n{\n    const std::vector<std::string>& current =\n        MWBase::Environment::get().getWorld()->getContentFiles();\n\n    const std::vector<ESM::Header::MasterData>& prev = reader.getGameFiles();\n\n    std::map<int, int> map;\n\n    for (int iPrev = 0; iPrev<static_cast<int> (prev.size()); ++iPrev)\n    {\n        std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name);\n\n        for (int iCurrent = 0; iCurrent<static_cast<int> (current.size()); ++iCurrent)\n            if (id==Misc::StringUtils::lowerCase (current[iCurrent]))\n            {\n                map.insert (std::make_pair (iPrev, iCurrent));\n                break;\n            }\n    }\n\n    return map;\n}\n\nMWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game)\n: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0)\n{\n\n}\n\nvoid MWState::StateManager::requestQuit()\n{\n    mQuitRequest = true;\n}\n\nbool MWState::StateManager::hasQuitRequest() const\n{\n    return mQuitRequest;\n}\n\nvoid MWState::StateManager::askLoadRecent()\n{\n    if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu)\n        return;\n\n    if( !mAskLoadRecent )\n    {\n        const MWState::Character* character = getCurrentCharacter();\n        if(!character || character->begin() == character->end())//no saves\n        {\n            MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);\n        }\n        else\n        {\n            MWState::Slot lastSave = *character->begin();\n            std::vector<std::string> buttons;\n            buttons.emplace_back(\"#{sYes}\");\n            buttons.emplace_back(\"#{sNo}\");\n            std::string tag(\"%s\");\n            std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString(\"sLoadLastSaveMsg\", tag);\n            size_t pos = message.find(tag);\n            message.replace(pos, tag.length(), lastSave.mProfile.mDescription);\n            MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons);\n            mAskLoadRecent = true;\n        }\n    }\n}\n\nMWState::StateManager::State MWState::StateManager::getState() const\n{\n    return mState;\n}\n\nvoid MWState::StateManager::newGame (bool bypass)\n{\n    cleanup();\n\n    if (!bypass)\n        MWBase::Environment::get().getWindowManager()->setNewGame (true);\n\n    try\n    {\n        Log(Debug::Info) << \"Starting a new game\";\n        MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();\n\n        MWBase::Environment::get().getWorld()->startNewGame (bypass);\n\n        mState = State_Running;\n\n        MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);\n        MWBase::Environment::get().getWindowManager()->fadeScreenIn(1);\n    }\n    catch (std::exception& e)\n    {\n        std::stringstream error;\n        error << \"Failed to start new game: \" << e.what();\n\n        Log(Debug::Error) << error.str();\n        cleanup (true);\n\n        MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);\n\n        std::vector<std::string> buttons;\n        buttons.emplace_back(\"#{sOk}\");\n        MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);\n    }\n}\n\nvoid MWState::StateManager::endGame()\n{\n    mState = State_Ended;\n}\n\nvoid MWState::StateManager::resumeGame()\n{\n    mState = State_Running;\n}\n\nvoid MWState::StateManager::saveGame (const std::string& description, const Slot *slot)\n{\n    MWState::Character* character = getCurrentCharacter();\n\n    try\n    {\n        if (!character)\n        {\n            MWWorld::ConstPtr player = MWMechanics::getPlayer();\n            std::string name = player.get<ESM::NPC>()->mBase->mName;\n\n            character = mCharacterManager.createCharacter(name);\n            mCharacterManager.setCurrentCharacter(character);\n        }\n\n        ESM::SavedGame profile;\n\n        MWBase::World& world = *MWBase::Environment::get().getWorld();\n\n        MWWorld::Ptr player = world.getPlayerPtr();\n\n        profile.mContentFiles = world.getContentFiles();\n\n        profile.mPlayerName = player.get<ESM::NPC>()->mBase->mName;\n        profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel();\n\n        std::string classId = player.get<ESM::NPC>()->mBase->mClass;\n        if (world.getStore().get<ESM::Class>().isDynamic(classId))\n            profile.mPlayerClassName = world.getStore().get<ESM::Class>().find(classId)->mName;\n        else\n            profile.mPlayerClassId = classId;\n\n        profile.mPlayerCell = world.getCellName();\n        profile.mInGameTime = world.getEpochTimeStamp();\n        profile.mTimePlayed = mTimePlayed;\n        profile.mDescription = description;\n\n        Log(Debug::Info) << \"Making a screenshot for saved game '\" << description << \"'\";;\n        writeScreenshot(profile.mScreenshot);\n\n        if (!slot)\n            slot = character->createSlot (profile);\n        else\n            slot = character->updateSlot (slot, profile);\n\n        // Make sure the animation state held by references is up to date before saving the game.\n        MWBase::Environment::get().getMechanicsManager()->persistAnimationStates();\n\n        Log(Debug::Info) << \"Writing saved game '\" << description << \"' for character '\" << profile.mPlayerName << \"'\";\n\n        // Write to a memory stream first. If there is an exception during the save process, we don't want to trash the\n        // existing save file we are overwriting.\n        std::stringstream stream;\n\n        ESM::ESMWriter writer;\n\n        for (const std::string& contentFile : MWBase::Environment::get().getWorld()->getContentFiles())\n            writer.addMaster(contentFile, 0); // not using the size information anyway -> use value of 0\n\n        writer.setFormat (ESM::SavedGame::sCurrentFormat);\n\n        // all unused\n        writer.setVersion(0);\n        writer.setType(0);\n        writer.setAuthor(\"\");\n        writer.setDescription(\"\");\n\n        int recordCount =         1 // saved game header\n                +MWBase::Environment::get().getJournal()->countSavedGameRecords()\n                +MWBase::Environment::get().getWorld()->countSavedGameRecords()\n                +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()\n                +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords()\n                +MWBase::Environment::get().getWindowManager()->countSavedGameRecords()\n                +MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords()\n                +MWBase::Environment::get().getInputManager()->countSavedGameRecords();\n        writer.setRecordCount (recordCount);\n\n        writer.save (stream);\n\n        Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();\n        // Using only Cells for progress information, since they typically have the largest records by far\n        listener.setProgressRange(MWBase::Environment::get().getWorld()->countSavedGameCells());\n        listener.setLabel(\"#{sNotifyMessage4}\", true);\n\n        Loading::ScopedLoad load(&listener);\n\n        writer.startRecord (ESM::REC_SAVE);\n        slot->mProfile.save (writer);\n        writer.endRecord (ESM::REC_SAVE);\n\n        MWBase::Environment::get().getJournal()->write (writer, listener);\n        MWBase::Environment::get().getDialogueManager()->write (writer, listener);\n        MWBase::Environment::get().getWorld()->write (writer, listener);\n        MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener);\n        MWBase::Environment::get().getWindowManager()->write(writer, listener);\n        MWBase::Environment::get().getMechanicsManager()->write(writer, listener);\n        MWBase::Environment::get().getInputManager()->write(writer, listener);\n\n        // Ensure we have written the number of records that was estimated\n        if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record\n            Log(Debug::Warning) << \"Warning: number of written savegame records does not match. Estimated: \" << recordCount+1 << \", written: \" << writer.getRecordCount();\n\n        writer.close();\n\n        if (stream.fail())\n            throw std::runtime_error(\"Write operation failed (memory stream)\");\n\n        // All good, write to file\n        boost::filesystem::ofstream filestream (slot->mPath, std::ios::binary);\n        filestream << stream.rdbuf();\n\n        if (filestream.fail())\n            throw std::runtime_error(\"Write operation failed (file stream)\");\n\n        Settings::Manager::setString (\"character\", \"Saves\",\n            slot->mPath.parent_path().filename().string());\n    }\n    catch (const std::exception& e)\n    {\n        std::stringstream error;\n        error << \"Failed to save game: \" << e.what();\n\n        Log(Debug::Error) << error.str();\n\n        std::vector<std::string> buttons;\n        buttons.emplace_back(\"#{sOk}\");\n        MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);\n\n        // If no file was written, clean up the slot\n        if (character && slot && !boost::filesystem::exists(slot->mPath))\n        {\n            character->deleteSlot(slot);\n            character->cleanup();\n        }\n    }\n}\n\nvoid MWState::StateManager::quickSave (std::string name)\n{\n    /*\n        Start of tes3mp change (major)\n\n        It should not be possible to quicksave the game in multiplayer, so it has been disabled\n    */\n    return;\n    /*\n        End of tes3mp change (major)\n    */\n\n    if (!(mState==State_Running &&\n        MWBase::Environment::get().getWorld()->getGlobalInt (\"chargenstate\")==-1 // char gen\n            && MWBase::Environment::get().getWindowManager()->isSavingAllowed()))\n    {\n        //You can not save your game right now\n        MWBase::Environment::get().getWindowManager()->messageBox(\"#{sSaveGameDenied}\");\n        return;\n    }\n\n    int maxSaves = Settings::Manager::getInt(\"max quicksaves\", \"Saves\");\n    if(maxSaves < 1)\n        maxSaves = 1;\n\n    Character* currentCharacter = getCurrentCharacter(); //Get current character\n    QuickSaveManager saveFinder = QuickSaveManager(name, maxSaves);\n\n    if (currentCharacter)\n    {\n        for (auto& save : *currentCharacter)\n        {\n            //Visiting slots allows the quicksave finder to find the oldest quicksave\n            saveFinder.visitSave(&save);\n        }\n    }\n\n    //Once all the saves have been visited, the save finder can tell us which\n    //one to replace (or create)\n    saveGame(name, saveFinder.getNextQuickSaveSlot());\n}\n\nvoid MWState::StateManager::loadGame(const std::string& filepath)\n{\n    for (const auto& character : mCharacterManager)\n    {\n        for (const auto& slot : character)\n        {\n            if (slot.mPath == boost::filesystem::path(filepath))\n            {\n                loadGame(&character, slot.mPath.string());\n                return;\n            }\n        }\n    }\n\n    MWState::Character* character = getCurrentCharacter();\n    loadGame(character, filepath);\n}\n\nvoid MWState::StateManager::loadGame (const Character *character, const std::string& filepath)\n{\n    try\n    {\n        cleanup();\n\n        Log(Debug::Info) << \"Reading save file \" << boost::filesystem::path(filepath).filename().string();\n\n        ESM::ESMReader reader;\n        reader.open (filepath);\n\n        if (reader.getFormat() > ESM::SavedGame::sCurrentFormat)\n            throw std::runtime_error(\"This save file was created using a newer version of OpenMW and is thus not supported. Please upgrade to the newest OpenMW version to load this file.\");\n\n        std::map<int, int> contentFileMap = buildContentFileIndexMap (reader);\n\n        Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();\n\n        listener.setProgressRange(100);\n        listener.setLabel(\"#{sLoadingMessage14}\");\n\n        Loading::ScopedLoad load(&listener);\n\n        bool firstPersonCam = false;\n\n        size_t total = reader.getFileSize();\n        int currentPercent = 0;\n        while (reader.hasMoreRecs())\n        {\n            ESM::NAME n = reader.getRecName();\n            reader.getRecHeader();\n\n            switch (n.intval)\n            {\n                case ESM::REC_SAVE:\n                    {\n                        ESM::SavedGame profile;\n                        profile.load(reader);\n                        if (!verifyProfile(profile))\n                        {\n                            cleanup (true);\n                            MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);\n                            return;\n                        }\n                        mTimePlayed = profile.mTimePlayed;\n                        Log(Debug::Info) << \"Loading saved game '\" << profile.mDescription << \"' for character '\" << profile.mPlayerName << \"'\";\n                    }\n                    break;\n\n                case ESM::REC_JOUR:\n                case ESM::REC_JOUR_LEGACY:\n                case ESM::REC_QUES:\n\n                    MWBase::Environment::get().getJournal()->readRecord (reader, n.intval);\n                    break;\n\n                case ESM::REC_DIAS:\n\n                    MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.intval);\n                    break;\n\n                case ESM::REC_ALCH:\n                case ESM::REC_ARMO:\n                case ESM::REC_BOOK:\n                case ESM::REC_CLAS:\n                case ESM::REC_CLOT:\n                case ESM::REC_ENCH:\n                case ESM::REC_NPC_:\n                case ESM::REC_SPEL:\n                case ESM::REC_WEAP:\n                case ESM::REC_GLOB:\n                case ESM::REC_PLAY:\n                case ESM::REC_CSTA:\n                case ESM::REC_WTHR:\n                case ESM::REC_DYNA:\n                case ESM::REC_ACTC:\n                case ESM::REC_PROJ:\n                case ESM::REC_MPRJ:\n                case ESM::REC_ENAB:\n                case ESM::REC_LEVC:\n                case ESM::REC_LEVI:\n                case ESM::REC_CREA:\n                case ESM::REC_CONT:\n                    MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap);\n                    break;\n\n                case ESM::REC_CAM_:\n                    reader.getHNT(firstPersonCam, \"FIRS\");\n                    break;\n\n                case ESM::REC_GSCR:\n\n                    MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.intval, contentFileMap);\n                    break;\n\n                case ESM::REC_GMAP:\n                case ESM::REC_KEYS:\n                case ESM::REC_ASPL:\n                case ESM::REC_MARK:\n\n                    MWBase::Environment::get().getWindowManager()->readRecord(reader, n.intval);\n                    break;\n\n                case ESM::REC_DCOU:\n                case ESM::REC_STLN:\n\n                    MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.intval);\n                    break;\n\n                case ESM::REC_INPU:\n                    MWBase::Environment::get().getInputManager()->readRecord(reader, n.intval);\n                    break;\n\n                default:\n\n                    // ignore invalid records\n                    Log(Debug::Warning) << \"Warning: Ignoring unknown record: \" << n.toString();\n                    reader.skipRecord();\n            }\n            int progressPercent = static_cast<int>(float(reader.getFileOffset())/total*100);\n            if (progressPercent > currentPercent)\n            {\n                listener.increaseProgress(progressPercent-currentPercent);\n                currentPercent = progressPercent;\n            }\n        }\n\n        mCharacterManager.setCurrentCharacter(character);\n\n        mState = State_Running;\n\n        if (character)\n            Settings::Manager::setString (\"character\", \"Saves\",\n                                      character->getPath().filename().string());\n\n        MWBase::Environment::get().getWindowManager()->setNewGame(false);\n        MWBase::Environment::get().getWorld()->saveLoaded();\n        MWBase::Environment::get().getWorld()->setupPlayer();\n        MWBase::Environment::get().getWorld()->renderPlayer();\n        MWBase::Environment::get().getWindowManager()->updatePlayer();\n        MWBase::Environment::get().getMechanicsManager()->playerLoaded();\n\n        if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson())\n            MWBase::Environment::get().getWorld()->togglePOV();\n\n        MWWorld::ConstPtr ptr = MWMechanics::getPlayer();\n\n        if (ptr.isInCell())\n        {\n            const ESM::CellId& cellId = ptr.getCell()->getCell()->getCellId();\n\n            // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again\n            MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false, false);\n        }\n        else\n        {\n            // Cell no longer exists (i.e. changed game files), choose a default cell\n            Log(Debug::Warning) << \"Warning: Player character's cell no longer exists, changing to the default cell\";\n            MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(0,0);\n            float x,y;\n            MWBase::Environment::get().getWorld()->indexToPosition(0,0,x,y,false);\n            ESM::Position pos;\n            pos.pos[0] = x;\n            pos.pos[1] = y;\n            pos.pos[2] = 0; // should be adjusted automatically (adjustPlayerPos=true)\n            pos.rot[0] = 0;\n            pos.rot[1] = 0;\n            pos.rot[2] = 0;\n            MWBase::Environment::get().getWorld()->changeToCell(cell->getCell()->getCellId(), pos, true, false);\n        }\n\n        MWBase::Environment::get().getWorld()->updateProjectilesCasters();\n\n        // Vanilla MW will restart startup scripts when a save game is loaded. This is unintuitive,\n        // but some mods may be using it as a reload detector.\n        MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();\n\n        // Since we passed \"changeEvent=false\" to changeCell, we shouldn't have triggered the cell change flag.\n        // But make sure the flag is cleared anyway in case it was set from an earlier game.\n        MWBase::Environment::get().getWorld()->markCellAsUnchanged();\n    }\n    catch (const std::exception& e)\n    {\n        std::stringstream error;\n        error << \"Failed to load saved game: \" << e.what();\n\n        Log(Debug::Error) << error.str();\n        cleanup (true);\n\n        MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);\n\n        std::vector<std::string> buttons;\n        buttons.emplace_back(\"#{sOk}\");\n        MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);\n    }\n}\n\nvoid MWState::StateManager::quickLoad()\n{\n    /*\n        Start of tes3mp change (major)\n\n        It should not be possible to quickload the game in multiplayer, so it has been disabled\n    */\n    return;\n    /*\n        End of tes3mp change (major)\n    */\n\n    if (Character* currentCharacter = getCurrentCharacter ())\n    {\n        if (currentCharacter->begin() == currentCharacter->end())\n            return;\n        loadGame (currentCharacter, currentCharacter->begin()->mPath.string()); //Get newest save\n    }\n}\n\nvoid MWState::StateManager::deleteGame(const MWState::Character *character, const MWState::Slot *slot)\n{\n    mCharacterManager.deleteSlot(character, slot);\n}\n\nMWState::Character *MWState::StateManager::getCurrentCharacter ()\n{\n    return mCharacterManager.getCurrentCharacter();\n}\n\nMWState::StateManager::CharacterIterator MWState::StateManager::characterBegin()\n{\n    return mCharacterManager.begin();\n}\n\nMWState::StateManager::CharacterIterator MWState::StateManager::characterEnd()\n{\n    return mCharacterManager.end();\n}\n\nvoid MWState::StateManager::update (float duration)\n{\n    mTimePlayed += duration;\n\n    // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update.\n    if (mAskLoadRecent)\n    {\n        int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton();\n        MWState::Character *curCharacter = getCurrentCharacter();\n        if(iButton==0 && curCharacter)\n        {\n            mAskLoadRecent = false;\n            //Load last saved game for current character\n\n            MWState::Slot lastSave = *curCharacter->begin();\n            loadGame(curCharacter, lastSave.mPath.string());\n        }\n        else if(iButton==1)\n        {\n            mAskLoadRecent = false;\n            MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);\n        }\n    }\n}\n\nbool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const\n{\n    const std::vector<std::string>& selectedContentFiles = MWBase::Environment::get().getWorld()->getContentFiles();\n    bool notFound = false;\n    for (const std::string& contentFile : profile.mContentFiles)\n    {\n        if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), contentFile)\n                == selectedContentFiles.end())\n        {\n            Log(Debug::Warning) << \"Warning: Saved game dependency \" << contentFile << \" is missing.\";\n            notFound = true;\n        }\n    }\n    if (notFound)\n    {\n        std::vector<std::string> buttons;\n        buttons.emplace_back(\"#{sYes}\");\n        buttons.emplace_back(\"#{sNo}\");\n        MWBase::Environment::get().getWindowManager()->interactiveMessageBox(\"#{sMissingMastersMsg}\", buttons, true);\n        int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton();\n        if (selectedButton == 1 || selectedButton == -1)\n            return false;\n    }\n    return true;\n}\n\nvoid MWState::StateManager::writeScreenshot(std::vector<char> &imageData) const\n{\n    int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing\n\n    osg::ref_ptr<osg::Image> screenshot (new osg::Image);\n\n    MWBase::Environment::get().getWorld()->screenshot(screenshot.get(), screenshotW, screenshotH);\n\n    osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"jpg\");\n    if (!readerwriter)\n    {\n        Log(Debug::Error) << \"Error: Unable to write screenshot, can't find a jpg ReaderWriter\";\n        return;\n    }\n\n    std::ostringstream ostream;\n    osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*screenshot, ostream);\n    if (!result.success())\n    {\n        Log(Debug::Error) << \"Error: Unable to write screenshot: \" << result.message() << \" code \" << result.status();\n        return;\n    }\n\n    std::string data = ostream.str();\n    imageData = std::vector<char>(data.begin(), data.end());\n\n}\n"
  },
  {
    "path": "apps/openmw/mwstate/statemanagerimp.hpp",
    "content": "#ifndef GAME_STATE_STATEMANAGER_H\n#define GAME_STATE_STATEMANAGER_H\n\n#include <map>\n\n#include \"../mwbase/statemanager.hpp\"\n\n#include <boost/filesystem/path.hpp>\n\n#include \"charactermanager.hpp\"\n\nnamespace MWState\n{\n    class StateManager : public MWBase::StateManager\n    {\n            bool mQuitRequest;\n            bool mAskLoadRecent;\n            State mState;\n            CharacterManager mCharacterManager;\n            double mTimePlayed;\n\n        private:\n\n            void cleanup (bool force = false);\n\n            bool verifyProfile (const ESM::SavedGame& profile) const;\n\n            void writeScreenshot (std::vector<char>& imageData) const;\n\n            std::map<int, int> buildContentFileIndexMap (const ESM::ESMReader& reader) const;\n\n        public:\n\n            StateManager (const boost::filesystem::path& saves, const std::string& game);\n\n            void requestQuit() override;\n\n            bool hasQuitRequest() const override;\n\n            void askLoadRecent() override;\n\n            State getState() const override;\n\n            void newGame (bool bypass = false) override;\n            ///< Start a new game.\n            ///\n            /// \\param bypass Skip new game mechanics.\n\n            void endGame() override;\n\n            void resumeGame() override;\n\n            void deleteGame (const MWState::Character *character, const MWState::Slot *slot) override;\n            ///< Delete a saved game slot from this character. If all save slots are deleted, the character will be deleted too.\n\n            void saveGame (const std::string& description, const Slot *slot = nullptr) override;\n            ///< Write a saved game to \\a slot or create a new slot if \\a slot == 0.\n            ///\n            /// \\note Slot must belong to the current character.\n\n            ///Saves a file, using supplied filename, overwritting if needed\n            /** This is mostly used for quicksaving and autosaving, for they use the same name over and over again\n                \\param name Name of save, defaults to \"Quicksave\"**/\n            void quickSave(std::string name = \"Quicksave\") override;\n\n            ///Loads the last saved file\n            /** Used for quickload **/\n            void quickLoad() override;\n\n            void loadGame (const std::string& filepath) override;\n            ///< Load a saved game directly from the given file path. This will search the CharacterManager\n            /// for a Character containing this save file, and set this Character current if one was found.\n            /// Otherwise, a new Character will be created.\n\n            void loadGame (const Character *character, const std::string &filepath) override;\n            ///< Load a saved game file belonging to the given character.\n\n            Character *getCurrentCharacter () override;\n            ///< @note May return null.\n\n            CharacterIterator characterBegin() override;\n            ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned\n            /// iterator.\n\n            CharacterIterator characterEnd() override;\n\n            void update (float duration) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/action.cpp",
    "content": "#include \"action.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nconst MWWorld::Ptr& MWWorld::Action::getTarget() const\n{\n    return mTarget;\n}\n\nvoid MWWorld::Action::setTarget(const MWWorld::Ptr& target)\n{\n    mTarget = target;\n}\n\nMWWorld::Action::Action (bool keepSound, const Ptr& target) : mKeepSound (keepSound), mSoundOffset(0), mTarget (target)\n{}\n\nMWWorld::Action::~Action() {}\n\nvoid MWWorld::Action::execute (const Ptr& actor, bool noSound)\n{\n    if(!mSoundId.empty() && !noSound)\n    {\n        MWSound::PlayMode envType = MWSound::PlayMode::Normal;\n\n        // Action sounds should not have a distortion in GUI mode\n        // example: take an item or drink a potion underwater\n        if (actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWindowManager()->isGuiMode())\n        {\n            envType = MWSound::PlayMode::NoEnv;\n        }\n\n        if(mKeepSound && actor == MWMechanics::getPlayer())\n        {\n            MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0,\n                MWSound::Type::Sfx, envType, mSoundOffset\n            );\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_OBJECT_SOUND packet every time the local player makes a sound here\n            */\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectSound(actor, mSoundId, 1.0, 1.0);\n            objectList->sendObjectSound();\n            /*\n                End of tes3mp addition\n            */\n        }\n        else\n        {\n            bool local = mTarget.isEmpty() || !mTarget.isInCell(); // no usable target\n            if(mKeepSound)\n            {\n                MWBase::Environment::get().getSoundManager()->playSound3D(\n                    (local ? actor : mTarget).getRefData().getPosition().asVec3(),\n                    mSoundId, 1.0, 1.0, MWSound::Type::Sfx, envType, mSoundOffset\n                );\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_OBJECT_SOUND packet every time a local actor makes a sound here\n                */\n                if (mwmp::Main::get().getCellController()->isLocalActor(actor))\n                {\n                    mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                    objectList->addObjectSound(local ? actor : mTarget, mSoundId, 1.0, 1.0);\n                    objectList->sendObjectSound();\n                }\n                /*\n                    End of tes3mp addition\n                */\n            }\n            else\n            {\n                MWBase::Environment::get().getSoundManager()->playSound3D(local ? actor : mTarget,\n                    mSoundId, 1.0, 1.0, MWSound::Type::Sfx, envType, mSoundOffset\n                );\n\n                /*\n                    Start of tes3mp addition\n\n                    Send an ID_OBJECT_SOUND packet every time a local actor makes a sound here\n                */\n                if (mwmp::Main::get().getCellController()->isLocalActor(actor))\n                {\n                    mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n                    objectList->reset();\n                    objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n                    objectList->addObjectSound(local ? actor : mTarget, mSoundId, 1.0, 1.0);\n                    objectList->sendObjectSound();\n                }\n                /*\n                    End of tes3mp addition\n                */\n            }\n        }\n    }\n\n    executeImp (actor);\n}\n\nvoid MWWorld::Action::setSound (const std::string& id)\n{\n    mSoundId = id;\n}\n\nvoid MWWorld::Action::setSoundOffset(float offset)\n{\n    mSoundOffset=offset;\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/action.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTION_H\n#define GAME_MWWORLD_ACTION_H\n\n#include <string>\n\n#include \"ptr.hpp\"\n\nnamespace MWWorld\n{\n    /// \\brief Abstract base for actions\n    class Action\n    {\n            std::string mSoundId;\n            bool mKeepSound;\n            float mSoundOffset;\n            Ptr mTarget;\n\n            // not implemented\n            Action (const Action& action);\n            Action& operator= (const Action& action);\n\n            virtual void executeImp (const Ptr& actor) = 0;\n\n        protected:\n\n            void setTarget(const Ptr&);\n\n        public:\n\n            const Ptr& getTarget() const;\n\n            Action (bool keepSound = false, const Ptr& target = Ptr());\n            ///< \\param keepSound Keep playing the sound even if the object the sound is played on is removed.\n\n            virtual ~Action();\n\n            virtual bool isNullAction() { return false; }\n            ///< Is running this action a no-op? (default false)\n\n            void execute (const Ptr& actor, bool noSound = false);\n\n            void setSound (const std::string& id);\n            void setSoundOffset(float offset);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actionalchemy.cpp",
    "content": "#include \"actionalchemy.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWWorld\n{\n    ActionAlchemy::ActionAlchemy(bool force)\n    : Action (false)\n    , mForce(force)\n    {\n    }\n\n    void ActionAlchemy::executeImp (const Ptr& actor)\n    {\n        if (actor != MWMechanics::getPlayer())\n            return;\n\n        if(!mForce && MWMechanics::isPlayerInCombat())\n        { //Ensure we're not in combat\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sInventoryMessage3}\");\n            return;\n        }\n\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Alchemy);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionalchemy.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONALCHEMY_H\n#define GAME_MWWORLD_ACTIONALCHEMY_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionAlchemy : public Action\n    {\n        bool mForce;\n        void executeImp (const Ptr& actor) override;\n\n    public:\n        ActionAlchemy(bool force=false);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actionapply.cpp",
    "content": "#include \"actionapply.hpp\"\n\n#include \"class.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwworld/containerstore.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWWorld\n{\n    ActionApply::ActionApply (const Ptr& object, const std::string& id)\n    : Action (false, object), mId (id)\n    {}\n\n    void ActionApply::executeImp (const Ptr& actor)\n    {\n        MWBase::Environment::get().getWorld()->breakInvisibility(actor);\n\n        actor.getClass().apply (actor, mId, actor);\n\n        actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor);\n    }\n\n\n    ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& object, const std::string& id,\n        int skillIndex, int usageType)\n    : Action (false, object), mId (id), mSkillIndex (skillIndex), mUsageType (usageType)\n    {}\n\n    void ActionApplyWithSkill::executeImp (const Ptr& actor)\n    {\n        MWBase::Environment::get().getWorld()->breakInvisibility(actor);\n\n        if (actor.getClass().apply (actor, mId, actor) && mUsageType!=-1 && actor == MWMechanics::getPlayer())\n            actor.getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType);\n\n        actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionapply.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONAPPLY_H\n#define GAME_MWWORLD_ACTIONAPPLY_H\n\n#include <string>\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionApply : public Action\n    {\n            std::string mId;\n\n            void executeImp (const Ptr& actor) override;\n\n        public:\n\n            ActionApply (const Ptr& object, const std::string& id);\n    };\n\n    class ActionApplyWithSkill : public Action\n    {\n            std::string mId;\n            int mSkillIndex;\n            int mUsageType;\n\n            void executeImp (const Ptr& actor) override;\n\n        public:\n\n            ActionApplyWithSkill (const Ptr& object, const std::string& id,\n                int skillIndex, int usageType);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actiondoor.cpp",
    "content": "#include \"actiondoor.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\nnamespace MWWorld\n{\n    ActionDoor::ActionDoor (const MWWorld::Ptr& object) : Action (false, object)\n    {\n    }\n\n    void ActionDoor::executeImp (const MWWorld::Ptr& actor)\n    {\n        MWBase::Environment::get().getWorld()->activateDoor(getTarget());\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actiondoor.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONDOOR_H\n#define GAME_MWWORLD_ACTIONDOOR_H\n\n#include \"action.hpp\"\n#include \"ptr.hpp\"\n\nnamespace MWWorld\n{\n    class ActionDoor : public Action\n    {\n            void executeImp (const MWWorld::Ptr& actor) override;\n\n        public:\n            ActionDoor (const Ptr& object);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actioneat.cpp",
    "content": "#include \"actioneat.hpp\"\n\n#include <components/esm/loadskil.hpp>\n\n#include \"../mwworld/containerstore.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"class.hpp\"\n\nnamespace MWWorld\n{\n    void ActionEat::executeImp (const Ptr& actor)\n    {\n        // remove used item (assume the item is present in inventory)\n        getTarget().getContainerStore()->remove(getTarget(), 1, actor);\n\n        // apply to actor\n        std::string id = getTarget().getCellRef().getRefId();\n\n        if (actor.getClass().apply (actor, id, actor) && actor == MWMechanics::getPlayer())\n            actor.getClass().skillUsageSucceeded (actor, ESM::Skill::Alchemy, 1);\n    }\n\n    ActionEat::ActionEat (const MWWorld::Ptr& object) : Action (false, object) {}\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actioneat.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONEAT_H\n#define GAME_MWWORLD_ACTIONEAT_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionEat : public Action\n    {\n            void executeImp (const Ptr& actor) override;\n\n        public:\n\n            ActionEat (const MWWorld::Ptr& object);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actionequip.cpp",
    "content": "#include \"actionequip.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include <components/compiler/locals.hpp>\n\n#include \"inventorystore.hpp\"\n#include \"player.hpp\"\n#include \"class.hpp\"\n\nnamespace MWWorld\n{\n    ActionEquip::ActionEquip (const MWWorld::Ptr& object, bool force)\n    : Action (false, object)\n    , mForce(force)\n    {\n    }\n\n    void ActionEquip::executeImp (const Ptr& actor)\n    {\n        MWWorld::Ptr object = getTarget();\n        MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor);\n\n        if (object.getClass().hasItemHealth(object) && object.getCellRef().getCharge() == 0)\n        {\n            if (actor == MWMechanics::getPlayer())\n                MWBase::Environment::get().getWindowManager()->messageBox(\"#{sInventoryMessage1}\");\n\n            return;\n        }\n\n        if (!mForce)\n        {\n            std::pair <int, std::string> result = object.getClass().canBeEquipped (object, actor);\n\n            // display error message if the player tried to equip something\n            if (!result.second.empty() && actor == MWMechanics::getPlayer())\n                MWBase::Environment::get().getWindowManager()->messageBox(result.second);\n\n            switch(result.first)\n            {\n                case 0:\n                    return;\n                default:\n                    break;\n            }\n        }\n\n        // slots that this item can be equipped in\n        std::pair<std::vector<int>, bool> slots_ = getTarget().getClass().getEquipmentSlots(getTarget());\n        if (slots_.first.empty())\n            return;\n\n        // retrieve ContainerStoreIterator to the item\n        MWWorld::ContainerStoreIterator it = invStore.begin();\n        for (; it != invStore.end(); ++it)\n        {\n            if (*it == object)\n            {\n                break;\n            }\n        }\n\n        if (it == invStore.end())\n        {\n            std::stringstream error;\n            error << \"ActionEquip can't find item \" << object.getCellRef().getRefId();\n            throw std::runtime_error(error.str());\n        }\n\n        // equip the item in the first free slot\n        std::vector<int>::const_iterator slot=slots_.first.begin();\n        for (;slot!=slots_.first.end(); ++slot)\n        {\n            // if the item is equipped already, nothing to do\n            if (invStore.getSlot(*slot) == it)\n                return;\n\n            if (invStore.getSlot(*slot) == invStore.end())\n            {\n                // slot is not occupied\n                invStore.equip(*slot, it, actor);\n                break;\n            }\n        }\n\n        // all slots are occupied -> cycle\n        // move all slots one towards begin(), then equip the item in the slot that is now free\n        if (slot == slots_.first.end())\n        {\n            ContainerStoreIterator enchItem = invStore.getSelectedEnchantItem();\n            bool reEquip = false;\n            for (slot = slots_.first.begin(); slot != slots_.first.end(); ++slot)\n            {\n                invStore.unequipSlot(*slot, actor, false);\n                if (slot + 1 != slots_.first.end())\n                {\n                    invStore.equip(*slot, invStore.getSlot(*(slot + 1)), actor);\n                }\n                else\n                {\n                    invStore.equip(*slot, it, actor);\n                }\n\n                //Fix for issue of selected enchated item getting remmoved on cycle\n                if (invStore.getSlot(*slot) == enchItem)\n                {\n                    reEquip = true;\n                }\n            }\n            if (reEquip)\n            {\n                invStore.setSelectedEnchantItem(enchItem);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionequip.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONEQUIP_H\n#define GAME_MWWORLD_ACTIONEQUIP_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionEquip : public Action\n    {\n        bool mForce;\n\n        void executeImp (const Ptr& actor) override;\n\n    public:\n        /// @param item to equip\n        ActionEquip (const Ptr& object, bool force=false);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actionharvest.cpp",
    "content": "#include \"actionharvest.hpp\"\n\n#include <sstream>\n\n#include <MyGUI_LanguageManager.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/misc/stringops.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"class.hpp\"\n#include \"containerstore.hpp\"\n\nnamespace MWWorld\n{\n    ActionHarvest::ActionHarvest (const MWWorld::Ptr& container)\n        : Action (true, container)\n    {\n        setSound(\"Item Ingredient Up\");\n    }\n\n    void ActionHarvest::executeImp (const MWWorld::Ptr& actor)\n    {\n        if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))\n            return;\n\n        MWWorld::Ptr target = getTarget();\n\n        /*\n            Start of tes3mp addition\n\n            Prepare an ID_CONTAINER packet that will let the server know about the\n            items removed from the harvested objects\n        */\n        mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->cell = *target.getCell()->getCell();\n        objectList->action = mwmp::BaseObjectList::REMOVE;\n        objectList->containerSubAction = mwmp::BaseObjectList::NONE;\n\n        mwmp::BaseObject baseObject = objectList->getBaseObjectFromPtr(target);\n        /*\n            End of tes3mp addition\n        */\n\n        MWWorld::ContainerStore& store = target.getClass().getContainerStore (target);\n        store.resolve();\n        MWWorld::ContainerStore& actorStore = actor.getClass().getContainerStore(actor);\n        std::map<std::string, int> takenMap;\n        for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n        {\n            if (!it->getClass().showsInInventory(*it))\n                continue;\n\n            int itemCount = it->getRefData().getCount();\n            // Note: it is important to check for crime before move an item from container. Otherwise owner check will not work\n            // for a last item in the container - empty harvested containers are considered as \"allowed to use\".\n            MWBase::Environment::get().getMechanicsManager()->itemTaken(actor, *it, target, itemCount);\n            actorStore.add(*it, itemCount, actor);\n\n            /*\n                Start of tes3mp addition\n\n                Track this item removal in the ID_CONTAINER packet being prepared\n            */\n            objectList->addContainerItem(baseObject, *it, 0, itemCount);\n            /*\n                End of tes3mp addition\n            */\n\n            store.remove(*it, itemCount, getTarget());\n            takenMap[it->getClass().getName(*it)]+=itemCount;\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_CONTAINER packet if the local player is logged in\n        */\n        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n        {\n            objectList->addBaseObject(baseObject);\n            objectList->sendContainer();\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        // Spawn a messagebox (only for items added to player's inventory)\n        if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr())\n        {\n            std::ostringstream stream;\n            int lineCount = 0;\n            const static int maxLines = 10;\n            for (auto & pair : takenMap)\n            {\n                std::string itemName = pair.first;\n                int itemCount = pair.second;\n                lineCount++;\n                if (lineCount == maxLines)\n                    stream << \"\\n...\";\n                else if (lineCount > maxLines)\n                    break;\n\n                // The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory\n                std::string msgBox;\n                if (itemCount == 1)\n                {\n                    msgBox = MyGUI::LanguageManager::getInstance().replaceTags(\"\\n#{sNotifyMessage60}\");\n                    msgBox = Misc::StringUtils::format(msgBox, itemName);\n                }\n                else\n                {\n                    msgBox = MyGUI::LanguageManager::getInstance().replaceTags(\"\\n#{sNotifyMessage61}\");\n                    msgBox = Misc::StringUtils::format(msgBox, itemCount, itemName);\n                }\n\n                stream << msgBox;\n            }\n            std::string tooltip = stream.str();\n            // remove the first newline (easier this way)\n            if (tooltip.size() > 0 && tooltip[0] == '\\n')\n                tooltip.erase(0, 1);\n\n            if (tooltip.size() > 0)\n                MWBase::Environment::get().getWindowManager()->messageBox(tooltip);\n        }\n\n        // Update animation object\n        MWBase::Environment::get().getWorld()->disable(target);\n        MWBase::Environment::get().getWorld()->enable(target);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionharvest.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONHARVEST_H\n#define GAME_MWWORLD_ACTIONHARVEST_H\n\n#include \"action.hpp\"\n#include \"ptr.hpp\"\n\nnamespace MWWorld\n{\n    class ActionHarvest : public Action\n    {\n            void executeImp (const MWWorld::Ptr& actor) override;\n\n        public:\n            ActionHarvest (const Ptr& container);\n            ///< \\param container The Container the Player has activated.\n    };\n}\n\n#endif // ACTIONOPEN_H\n"
  },
  {
    "path": "apps/openmw/mwworld/actionopen.cpp",
    "content": "#include \"actionopen.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/disease.hpp\"\n\nnamespace MWWorld\n{\n    ActionOpen::ActionOpen (const MWWorld::Ptr& container)\n        : Action (false, container)\n    {\n    }\n\n    void ActionOpen::executeImp (const MWWorld::Ptr& actor)\n    {\n        if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))\n            return;\n\n        if (actor != MWMechanics::getPlayer())\n            return;\n\n        if (!MWBase::Environment::get().getMechanicsManager()->onOpen(getTarget()))\n            return;\n\n        MWMechanics::diseaseContact(actor, getTarget());\n\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container, getTarget());\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionopen.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONOPEN_H\n#define GAME_MWWORLD_ACTIONOPEN_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionOpen : public Action\n    {\n            void executeImp (const MWWorld::Ptr& actor) override;\n\n        public:\n            ActionOpen (const Ptr& container);\n            ///< \\param container The Container the Player has activated.\n\n    };\n}\n\n#endif // ACTIONOPEN_H\n"
  },
  {
    "path": "apps/openmw/mwworld/actionread.cpp",
    "content": "#include \"actionread.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"player.hpp\"\n#include \"class.hpp\"\n#include \"esmstore.hpp\"\n\nnamespace MWWorld\n{\n    ActionRead::ActionRead (const MWWorld::Ptr& object) : Action (false, object)\n    {\n    }\n\n    void ActionRead::executeImp (const MWWorld::Ptr& actor) {\n\n        if (actor != MWMechanics::getPlayer())\n            return;\n\n        //Ensure we're not in combat\n        if(MWMechanics::isPlayerInCombat()\n                // Reading in combat is still allowed if the scroll/book is not in the player inventory yet\n                // (since otherwise, there would be no way to pick it up)\n                && getTarget().getContainerStore() == &actor.getClass().getContainerStore(actor)\n                ) {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sInventoryMessage4}\");\n            return;\n        }\n\n        LiveCellRef<ESM::Book> *ref = getTarget().get<ESM::Book>();\n\n        if (ref->mBase->mData.mIsScroll)\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Scroll, getTarget());\n        else\n            MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Book, getTarget());\n\n        MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor);\n\n        // Skill gain from books\n        if (ref->mBase->mData.mSkillId >= 0 && ref->mBase->mData.mSkillId < ESM::Skill::Length\n                && !npcStats.hasBeenUsed (ref->mBase->mId))\n        {\n            MWWorld::LiveCellRef<ESM::NPC> *playerRef = actor.get<ESM::NPC>();\n\n            const ESM::Class *class_ =\n                MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find (\n                    playerRef->mBase->mClass\n                );\n\n            npcStats.increaseSkill (ref->mBase->mData.mSkillId, *class_, true, true);\n\n            npcStats.flagAsUsed (ref->mBase->mId);\n\n            /*\n                Start of tes3mp addition\n\n                Send an ID_PLAYER_BOOK packet every time a player reads a skill book\n            */\n            mwmp::Main::get().getLocalPlayer()->sendBook(ref->mBase->mId);\n            /*\n                End of tes3mp addition\n            */\n        }\n\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionread.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONREAD_H\n#define GAME_MWWORLD_ACTIONREAD_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionRead : public Action\n    {\n            void executeImp (const MWWorld::Ptr& actor) override;\n\n        public:\n            /// @param book or scroll to read\n            ActionRead (const Ptr& object);\n    };\n}\n\n#endif // ACTIONREAD_H\n"
  },
  {
    "path": "apps/openmw/mwworld/actionrepair.cpp",
    "content": "#include \"actionrepair.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWWorld\n{\n    ActionRepair::ActionRepair(const Ptr& item, bool force)\n        : Action (false, item)\n        , mForce(force)\n    {\n    }\n\n    void ActionRepair::executeImp (const Ptr& actor)\n    {\n        if (actor != MWMechanics::getPlayer())\n            return;\n\n        if(!mForce && MWMechanics::isPlayerInCombat())\n        {\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sInventoryMessage2}\");\n            return;\n        }\n\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Repair, getTarget());\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionrepair.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONREPAIR_H\n#define GAME_MWWORLD_ACTIONREPAIR_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionRepair : public Action\n    {\n        bool mForce;\n\n        void executeImp (const Ptr& actor) override;\n\n    public:\n        /// @param item repair hammer\n        ActionRepair(const Ptr& item, bool force=false);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actionsoulgem.cpp",
    "content": "#include \"actionsoulgem.hpp\"\n\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWWorld\n{\n\n    ActionSoulgem::ActionSoulgem(const Ptr &object)\n        : Action(false, object)\n    {\n\n    }\n\n    void ActionSoulgem::executeImp(const Ptr &actor)\n    {\n        if (actor != MWMechanics::getPlayer())\n            return;\n\n        if(MWMechanics::isPlayerInCombat()) { //Ensure we're not in combat\n            MWBase::Environment::get().getWindowManager()->messageBox(\"#{sInventoryMessage5}\");\n            return;\n        }\n        MWBase::Environment::get().getWindowManager()->showSoulgemDialog(getTarget());\n    }\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionsoulgem.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONSOULGEM_H\n#define GAME_MWWORLD_ACTIONSOULGEM_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionSoulgem : public Action\n    {\n            void executeImp (const MWWorld::Ptr& actor) override;\n\n        public:\n            /// @param soulgem to use\n            ActionSoulgem (const Ptr& object);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actiontake.cpp",
    "content": "#include \"actiontake.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwgui/inventorywindow.hpp\"\n\n#include \"class.hpp\"\n#include \"containerstore.hpp\"\n\nnamespace MWWorld\n{\n    ActionTake::ActionTake (const MWWorld::Ptr& object) : Action (true, object) {}\n\n    void ActionTake::executeImp (const Ptr& actor)\n    {\n        // When in GUI mode, we should use drag and drop\n        if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr())\n        {\n            MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();\n            if (mode == MWGui::GM_Inventory || mode == MWGui::GM_Container)\n            {\n                MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(getTarget());\n                return;\n            }\n        }\n\n        MWBase::Environment::get().getMechanicsManager()->itemTaken(\n                    actor, getTarget(), MWWorld::Ptr(), getTarget().getRefData().getCount());\n        MWWorld::Ptr newitem = *actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor);\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_OBJECT_DELETE packet every time an item is taken from the world\n            by the player outside of the inventory screen\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectGeneric(getTarget());\n        objectList->sendObjectDelete();\n        /*\n            End of tes3mp addition\n        */\n\n        MWBase::Environment::get().getWorld()->deleteObject (getTarget());\n        setTarget(newitem);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actiontake.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONTAKE_H\n#define GAME_MWWORLD_ACTIONTAKE_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionTake : public Action\n    {\n            void executeImp (const Ptr& actor) override;\n\n        public:\n\n            ActionTake (const MWWorld::Ptr& object);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actiontalk.cpp",
    "content": "#include \"actiontalk.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWWorld\n{\n    ActionTalk::ActionTalk (const Ptr& actor) : Action (false, actor) {}\n\n    void ActionTalk::executeImp (const Ptr& actor)\n    {\n        /*\n            Start of tes3mp change (major)\n\n            We need to be able to make actors start conversations with players, so reverse the check added\n            by 4118b20608b630b8d166d060a34c1234b80e378d here\n        */\n        MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, getTarget());\n        /*\n            End of tes3mp change (major)    \n        */\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actiontalk.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONTALK_H\n#define GAME_MWWORLD_ACTIONTALK_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionTalk : public Action\n    {\n            void executeImp (const Ptr& actor) override;\n\n        public:\n\n            ActionTalk (const Ptr& actor);\n            ///< \\param actor The actor the player is talking to\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actionteleport.cpp",
    "content": "#include \"actionteleport.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ActorList.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n\n#include \"../mwworld/class.hpp\"\n\n#include \"player.hpp\"\n\nnamespace MWWorld\n{\n    ActionTeleport::ActionTeleport (const std::string& cellName,\n        const ESM::Position& position, bool teleportFollowers)\n    : Action (true), mCellName (cellName), mPosition (position), mTeleportFollowers(teleportFollowers)\n    {\n    }\n\n    void ActionTeleport::executeImp (const Ptr& actor)\n    {\n        if (mTeleportFollowers)\n        {\n            // Find any NPCs that are following the actor and teleport them with him\n            std::set<MWWorld::Ptr> followers;\n            getFollowers(actor, followers, true);\n\n            for (std::set<MWWorld::Ptr>::iterator it = followers.begin(); it != followers.end(); ++it)\n                teleport(*it);\n        }\n\n        teleport(actor);\n    }\n\n    void ActionTeleport::teleport(const Ptr &actor)\n    {\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n        actor.getClass().getCreatureStats(actor).land(actor == world->getPlayerPtr());\n        if(actor == world->getPlayerPtr())\n        {\n            world->getPlayer().setTeleported(true);\n            if (mCellName.empty())\n                world->changeToExteriorCell (mPosition, true);\n            else\n                world->changeToInteriorCell (mCellName, mPosition, true);\n        }\n        else\n        {\n            /*\n                Start of tes3mp addition\n\n                Track the original cell of this actor so we can use it when sending a packet\n            */\n            ESM::Cell originalCell = *actor.getCell()->getCell();\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp change (minor)\n\n                If this is a DedicatedActor, get their new cell and override their stored cell with it\n                so their cell change is approved in World::moveObject()\n            */\n            MWWorld::CellStore *newCellStore;\n            mwmp::CellController *cellController = mwmp::Main::get().getCellController();\n\n            if (actor.getClass().getCreatureStats(actor).getAiSequence().isInCombat(world->getPlayerPtr()))\n                actor.getClass().getCreatureStats(actor).getAiSequence().stopCombat();\n            else if (mCellName.empty())\n            {\n                int cellX;\n                int cellY;\n                world->positionToIndex(mPosition.pos[0],mPosition.pos[1],cellX,cellY);\n\n                newCellStore = world->getExterior(cellX, cellY);\n                if (cellController->isDedicatedActor(actor))\n                    cellController->getDedicatedActor(actor)->cell = *newCellStore->getCell();\n\n                world->moveObject(actor,world->getExterior(cellX,cellY),\n                    mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);\n            }\n            else\n            {\n                newCellStore = world->getInterior(mCellName);\n                if (cellController->isDedicatedActor(actor))\n                    cellController->getDedicatedActor(actor)->cell = *newCellStore->getCell();\n\n                world->moveObject(actor,world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);\n            }\n            /*\n                Start of tes3mp change (minor)\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Send ActorCellChange packets when an actor follows us across cells, regardless of\n                whether we're the cell authority or not; the server can decide if it wants to comply\n                with them\n\n                Afterwards, send an ActorAI packet about this actor being our follower, to ensure\n                they remain our follower even if the destination cell has another player as its\n                cell authority\n            */\n            mwmp::BaseActor baseActor;\n            baseActor.refNum = actor.getCellRef().getRefNum().mIndex;\n            baseActor.mpNum = actor.getCellRef().getMpNum();\n            baseActor.cell = *newCellStore->getCell();\n            baseActor.position = actor.getRefData().getPosition();\n            baseActor.isFollowerCellChange = true;\n\n            mwmp::ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList();\n            actorList->reset();\n            actorList->cell = originalCell;\n\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sending ID_ACTOR_CELL_CHANGE about %s %i-%i to server\",\n                actor.getCellRef().getRefId().c_str(), baseActor.refNum, baseActor.mpNum);\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Moved from %s to %s\", actorList->cell.getDescription().c_str(),\n                baseActor.cell.getDescription().c_str());\n\n            actorList->addCellChangeActor(baseActor);\n            actorList->sendCellChangeActors();\n\n            // Send ActorAI to bring all players in the new cell up to speed with this follower\n            actorList->cell = baseActor.cell;\n            baseActor.aiAction = mwmp::BaseActorList::FOLLOW;\n            baseActor.aiTarget = MechanicsHelper::getTarget(world->getPlayerPtr());\n            actorList->addAiActor(baseActor);\n            actorList->sendAiActors();\n            /*\n                End of tes3mp addition\n            */\n        }\n    }\n\n    void ActionTeleport::getFollowers(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out, bool includeHostiles) {\n        std::set<MWWorld::Ptr> followers;\n        MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor, followers);\n\n        for(std::set<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)\n        {\n            MWWorld::Ptr follower = *it;\n\n            std::string script = follower.getClass().getScript(follower);\n\n            if (!includeHostiles && follower.getClass().getCreatureStats(follower).getAiSequence().isInCombat(actor))\n                continue;\n\n            if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, \"stayoutside\") == 1)\n                continue;\n\n            if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() > 800 * 800)\n                continue;\n\n            out.emplace(follower);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actionteleport.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONTELEPORT_H\n#define GAME_MWWORLD_ACTIONTELEPORT_H\n\n#include <set>\n#include <string>\n\n#include <components/esm/defs.hpp>\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionTeleport : public Action\n    {\n            std::string mCellName;\n            ESM::Position mPosition;\n            bool mTeleportFollowers;\n\n            /// Teleports this actor and also teleports anyone following that actor.\n            void executeImp (const Ptr& actor) override;\n\n            /// Teleports only the given actor (internal use).\n            void teleport(const Ptr &actor);\n\n        public:\n\n            /// If cellName is empty, an exterior cell is assumed.\n            /// @param teleportFollowers Whether to teleport any following actors of the target actor as well.\n            ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers);\n\n            /// @param includeHostiles If true, include hostile followers (which won't actually be teleported) in the output,\n            ///                        e.g. so that the teleport action can calm them.\n            static void getFollowers(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out, bool includeHostiles = false);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/actiontrap.cpp",
    "content": "#include \"actiontrap.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwmechanics/spellcasting.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\nnamespace MWWorld\n{\n    void ActionTrap::executeImp(const Ptr &actor)\n    {\n        osg::Vec3f actorPosition(actor.getRefData().getPosition().asVec3());\n        osg::Vec3f trapPosition(mTrapSource.getRefData().getPosition().asVec3());\n        float trapRange = MWBase::Environment::get().getWorld()->getMaxActivationDistance();\n\n        // Note: can't just detonate the trap at the trapped object's location and use the blast\n        // radius, because for most trap spells this is 1 foot, much less than the activation distance.\n        // Using activation distance as the trap range.\n\n        if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && MWBase::Environment::get().getWorld()->getDistanceToFacedObject() > trapRange) // player activated object outside range of trap\n        {\n            MWMechanics::CastSpell cast(mTrapSource, mTrapSource);\n            cast.mHitPosition = trapPosition;\n            cast.cast(mSpellId);\n        }\n        else // player activated object within range of trap, or NPC activated trap\n        {\n            MWMechanics::CastSpell cast(mTrapSource, actor);\n            cast.mHitPosition = actorPosition;\n            cast.cast(mSpellId);\n        }\n\n        /*\n            Start of tes3mp change (major)\n\n            Disable unilateral trap disarming on this client and expect the server's reply to our\n            packet to do it instead\n        */\n        //mTrapSource.getCellRef().setTrap(\"\");\n        /*\n            End of tes3mp change (major)\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_OBJECT_TRAP packet every time a trap is triggered\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        \n        ESM::Position pos;\n\n        if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && MWBase::Environment::get().getWorld()->getDistanceToFacedObject() > trapRange)\n            pos = mTrapSource.getRefData().getPosition();\n        else\n            pos = actor.getRefData().getPosition();\n\n        objectList->addObjectTrap(mTrapSource, pos, false);\n        objectList->sendObjectTrap();\n        /*\n            End of tes3mp addition\n        */\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/actiontrap.hpp",
    "content": "#ifndef GAME_MWWORLD_ACTIONTRAP_H\n#define GAME_MWWORLD_ACTIONTRAP_H\n\n#include <string>\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    class ActionTrap : public Action\n    {\n            std::string mSpellId;\n            MWWorld::Ptr mTrapSource;\n\n            void executeImp (const Ptr& actor) override;\n\n        public:\n\n            /// @param spellId\n            /// @param trapSource\n            ActionTrap (const std::string& spellId, const Ptr& trapSource)\n                : Action(false, trapSource), mSpellId(spellId), mTrapSource(trapSource) {}\n    };\n}\n\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/cellpreloader.cpp",
    "content": "#include \"cellpreloader.hpp\"\n\n#include <atomic>\n#include <limits>\n\n#include <components/debug/debuglog.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/bulletshapemanager.hpp>\n#include <components/resource/keyframemanager.hpp>\n#include <components/vfs/manager.hpp>\n#include <components/misc/resourcehelpers.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/terrain/world.hpp>\n#include <components/sceneutil/unrefqueue.hpp>\n#include <components/esm/loadcell.hpp>\n\n#include \"../mwrender/landmanager.hpp\"\n\n#include \"cellstore.hpp\"\n#include \"class.hpp\"\n\nnamespace MWWorld\n{\n\n    struct ListModelsVisitor\n    {\n        ListModelsVisitor(std::vector<std::string>& out)\n            : mOut(out)\n        {\n        }\n\n        virtual bool operator()(const MWWorld::Ptr& ptr)\n        {\n            ptr.getClass().getModelsToPreload(ptr, mOut);\n\n            return true;\n        }\n\n        virtual ~ListModelsVisitor() = default;\n\n        std::vector<std::string>& mOut;\n    };\n\n    /// Worker thread item: preload models in a cell.\n    class PreloadItem : public SceneUtil::WorkItem\n    {\n    public:\n        /// Constructor to be called from the main thread.\n        PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager, Terrain::World* terrain, MWRender::LandManager* landManager, bool preloadInstances)\n            : mIsExterior(cell->getCell()->isExterior())\n            , mX(cell->getCell()->getGridX())\n            , mY(cell->getCell()->getGridY())\n            , mSceneManager(sceneManager)\n            , mBulletShapeManager(bulletShapeManager)\n            , mKeyframeManager(keyframeManager)\n            , mTerrain(terrain)\n            , mLandManager(landManager)\n            , mPreloadInstances(preloadInstances)\n            , mAbort(false)\n        {\n            mTerrainView = mTerrain->createView();\n\n            ListModelsVisitor visitor (mMeshes);\n            cell->forEach(visitor);\n        }\n\n        void abort() override\n        {\n            mAbort = true;\n        }\n\n        /// Preload work to be called from the worker thread.\n        void doWork() override\n        {\n            if (mIsExterior)\n            {\n                try\n                {\n                    mTerrain->cacheCell(mTerrainView.get(), mX, mY);\n                    mPreloadedObjects.insert(mLandManager->getLand(mX, mY));\n                }\n                catch(std::exception&)\n                {\n                }\n            }\n\n            for (std::string& mesh: mMeshes)\n            {\n                if (mAbort)\n                    break;\n\n                try\n                {\n                    mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS());\n\n                    bool animated = false;\n                    size_t slashpos = mesh.find_last_of(\"/\\\\\");\n                    if (slashpos != std::string::npos && slashpos != mesh.size()-1)\n                    {\n                        Misc::StringUtils::lowerCaseInPlace(mesh);\n                        if (mesh[slashpos+1] == 'x')\n                        {\n                            std::string kfname = mesh;\n                            if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, \".nif\") == 0)\n                            {\n                                kfname.replace(kfname.size()-4, 4, \".kf\");\n                                if (mSceneManager->getVFS()->exists(kfname))\n                                {\n                                    mPreloadedObjects.insert(mKeyframeManager->get(kfname));\n                                    animated = true;\n                                }\n                            }\n                        }\n                    }\n                    if (mPreloadInstances && animated)\n                        mPreloadedObjects.insert(mSceneManager->cacheInstance(mesh));\n                    else\n                        mPreloadedObjects.insert(mSceneManager->getTemplate(mesh));\n                    if (mPreloadInstances)\n                        mPreloadedObjects.insert(mBulletShapeManager->cacheInstance(mesh));\n                    else\n                        mPreloadedObjects.insert(mBulletShapeManager->getShape(mesh));\n\n                }\n                catch (std::exception&)\n                {\n                    // ignore error for now, would spam the log too much\n                    // error will be shown when visiting the cell\n                }\n            }\n        }\n\n    private:\n        typedef std::vector<std::string> MeshList;\n        bool mIsExterior;\n        int mX;\n        int mY;\n        MeshList mMeshes;\n        Resource::SceneManager* mSceneManager;\n        Resource::BulletShapeManager* mBulletShapeManager;\n        Resource::KeyframeManager* mKeyframeManager;\n        Terrain::World* mTerrain;\n        MWRender::LandManager* mLandManager;\n        bool mPreloadInstances;\n\n        std::atomic<bool> mAbort;\n\n        osg::ref_ptr<Terrain::View> mTerrainView;\n\n        // keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state\n        std::set<osg::ref_ptr<const osg::Object> > mPreloadedObjects;\n    };\n\n    class TerrainPreloadItem : public SceneUtil::WorkItem\n    {\n    public:\n        TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<CellPreloader::PositionCellGrid>& preloadPositions)\n            : mAbort(false)\n            , mProgress(views.size())\n            , mProgressRange(0)\n            , mTerrainViews(views)\n            , mWorld(world)\n            , mPreloadPositions(preloadPositions)\n        {\n        }\n\n        bool storeViews(double referenceTime)\n        {\n            for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size(); ++i)\n                if (!mWorld->storeView(mTerrainViews[i], referenceTime))\n                    return false;\n            return true;\n        }\n\n        void doWork() override\n        {\n            for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)\n            {\n                mTerrainViews[i]->reset();\n                mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mProgress[i], mProgressRange);\n            }\n        }\n\n        void abort() override\n        {\n            mAbort = true;\n        }\n\n        int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; }\n        int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; }\n\n    private:\n        std::atomic<bool> mAbort;\n        std::vector<std::atomic<int>> mProgress;\n        int mProgressRange;\n        std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;\n        Terrain::World* mWorld;\n        std::vector<CellPreloader::PositionCellGrid> mPreloadPositions;\n    };\n\n    /// Worker thread item: update the resource system's cache, effectively deleting unused entries.\n    class UpdateCacheItem : public SceneUtil::WorkItem\n    {\n    public:\n        UpdateCacheItem(Resource::ResourceSystem* resourceSystem, double referenceTime)\n            : mReferenceTime(referenceTime)\n            , mResourceSystem(resourceSystem)\n        {\n        }\n\n        void doWork() override\n        {\n            mResourceSystem->updateCache(mReferenceTime);\n        }\n\n    private:\n        double mReferenceTime;\n        Resource::ResourceSystem* mResourceSystem;\n    };\n\n    CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain, MWRender::LandManager* landManager)\n        : mResourceSystem(resourceSystem)\n        , mBulletShapeManager(bulletShapeManager)\n        , mTerrain(terrain)\n        , mLandManager(landManager)\n        , mExpiryDelay(0.0)\n        , mMinCacheSize(0)\n        , mMaxCacheSize(0)\n        , mPreloadInstances(true)\n        , mLastResourceCacheUpdate(0.0)\n        , mStoreViewsFailCount(0)\n    {\n    }\n\n    CellPreloader::~CellPreloader()\n    {\n        if (mTerrainPreloadItem)\n        {\n            mTerrainPreloadItem->abort();\n            mTerrainPreloadItem->waitTillDone();\n            mTerrainPreloadItem = nullptr;\n        }\n\n        if (mUpdateCacheItem)\n        {\n            mUpdateCacheItem->waitTillDone();\n            mUpdateCacheItem = nullptr;\n        }\n\n        for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it)\n            it->second.mWorkItem->abort();\n\n        for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it)\n            it->second.mWorkItem->waitTillDone();\n\n        mPreloadCells.clear();\n    }\n\n    void CellPreloader::preload(CellStore *cell, double timestamp)\n    {\n        if (!mWorkQueue)\n        {\n            Log(Debug::Error) << \"Error: can't preload, no work queue set\";\n            return;\n        }\n        if (cell->getState() == CellStore::State_Unloaded)\n        {\n            Log(Debug::Error) << \"Error: can't preload objects for unloaded cell\";\n            return;\n        }\n\n        PreloadMap::iterator found = mPreloadCells.find(cell);\n        if (found != mPreloadCells.end())\n        {\n            // already preloaded, nothing to do other than updating the timestamp\n            found->second.mTimeStamp = timestamp;\n            return;\n        }\n\n        while (mPreloadCells.size() >= mMaxCacheSize)\n        {\n            // throw out oldest cell to make room\n            PreloadMap::iterator oldestCell = mPreloadCells.begin();\n            double oldestTimestamp = std::numeric_limits<double>::max();\n            double threshold = 1.0; // seconds\n            for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end(); ++it)\n            {\n                if (it->second.mTimeStamp < oldestTimestamp)\n                {\n                    oldestTimestamp = it->second.mTimeStamp;\n                    oldestCell = it;\n                }\n            }\n\n            if (oldestTimestamp + threshold < timestamp)\n            {\n                oldestCell->second.mWorkItem->abort();\n                mPreloadCells.erase(oldestCell);\n            }\n            else\n                return;\n        }\n\n        osg::ref_ptr<PreloadItem> item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain, mLandManager, mPreloadInstances));\n        mWorkQueue->addWorkItem(item);\n\n        mPreloadCells[cell] = PreloadEntry(timestamp, item);\n    }\n\n    void CellPreloader::notifyLoaded(CellStore *cell)\n    {\n        PreloadMap::iterator found = mPreloadCells.find(cell);\n        if (found != mPreloadCells.end())\n        {\n            // do the deletion in the background thread\n            if (found->second.mWorkItem)\n            {\n                found->second.mWorkItem->abort();\n                mUnrefQueue->push(mPreloadCells[cell].mWorkItem);\n            }\n\n            mPreloadCells.erase(found);\n        }\n    }\n\n    void CellPreloader::clear()\n    {\n        for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();)\n        {\n            if (it->second.mWorkItem)\n            {\n                it->second.mWorkItem->abort();\n                mUnrefQueue->push(it->second.mWorkItem);\n            }\n\n            mPreloadCells.erase(it++);\n        }\n    }\n\n    void CellPreloader::updateCache(double timestamp)\n    {\n        for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();)\n        {\n            if (mPreloadCells.size() >= mMinCacheSize && it->second.mTimeStamp < timestamp - mExpiryDelay)\n            {\n                if (it->second.mWorkItem)\n                {\n                    it->second.mWorkItem->abort();\n                    mUnrefQueue->push(it->second.mWorkItem);\n                }\n                mPreloadCells.erase(it++);\n            }\n            else\n                ++it;\n        }\n\n        if (timestamp - mLastResourceCacheUpdate > 1.0 && (!mUpdateCacheItem || mUpdateCacheItem->isDone()))\n        {\n            // the resource cache is cleared from the worker thread so that we're not holding up the main thread with delete operations\n            mUpdateCacheItem = new UpdateCacheItem(mResourceSystem, timestamp);\n            mWorkQueue->addWorkItem(mUpdateCacheItem, true);\n            mLastResourceCacheUpdate = timestamp;\n        }\n\n        if (mTerrainPreloadItem && mTerrainPreloadItem->isDone())\n        {\n            if (!mTerrainPreloadItem->storeViews(timestamp))\n            {\n                if (++mStoreViewsFailCount > 100)\n                {\n                    OSG_ALWAYS << \"paging views are rebuilt every frame, please check for faulty enable/disable scripts.\" << std::endl;\n                    mStoreViewsFailCount = 0;\n                }\n                setTerrainPreloadPositions(std::vector<PositionCellGrid>());\n            }\n            else\n                mStoreViewsFailCount = 0;\n            mTerrainPreloadItem = nullptr;\n        }\n    }\n\n    void CellPreloader::setExpiryDelay(double expiryDelay)\n    {\n        mExpiryDelay = expiryDelay;\n    }\n\n    void CellPreloader::setMinCacheSize(unsigned int num)\n    {\n        mMinCacheSize = num;\n    }\n\n    void CellPreloader::setMaxCacheSize(unsigned int num)\n    {\n        mMaxCacheSize = num;\n    }\n\n    void CellPreloader::setPreloadInstances(bool preload)\n    {\n        mPreloadInstances = preload;\n    }\n\n    unsigned int CellPreloader::getMaxCacheSize() const\n    {\n        return mMaxCacheSize;\n    }\n\n    void CellPreloader::setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue)\n    {\n        mWorkQueue = workQueue;\n    }\n\n    void CellPreloader::setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue)\n    {\n        mUnrefQueue = unrefQueue;\n    }\n\n    bool CellPreloader::syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, int& progress, int& progressRange, double timestamp)\n    {\n        if (!mTerrainPreloadItem)\n            return true;\n        else if (mTerrainPreloadItem->isDone())\n        {\n            if (mTerrainPreloadItem->storeViews(timestamp))\n            {\n                mTerrainPreloadItem = nullptr;\n                return true;\n            }\n            else\n            {\n                setTerrainPreloadPositions(std::vector<CellPreloader::PositionCellGrid>());\n                setTerrainPreloadPositions(positions);\n                return false;\n            }\n        }\n        else\n        {\n            progress = mTerrainPreloadItem->getProgress();\n            progressRange = mTerrainPreloadItem->getProgressRange();\n            return false;\n        }\n    }\n\n    void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid *exceptPos)\n    {\n        const float resetThreshold = ESM::Land::REAL_SIZE;\n        for (const auto& pos : mTerrainPreloadPositions)\n            if (exceptPos && (pos.first-exceptPos->first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos->second)\n                return;\n        if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())\n        {\n            mTerrainPreloadItem->abort();\n            mTerrainPreloadItem->waitTillDone();\n        }\n        setTerrainPreloadPositions(std::vector<CellPreloader::PositionCellGrid>());\n    }\n\n    bool contains(const std::vector<CellPreloader::PositionCellGrid>& container, const std::vector<CellPreloader::PositionCellGrid>& contained)\n    {\n        for (const auto& pos : contained)\n        {\n            bool found = false;\n            for (const auto& pos2 : container)\n            {\n                if ((pos.first-pos2.first).length2() < 1 && pos.second == pos2.second)\n                {\n                    found = true;\n                    break;\n                }\n            }\n            if (!found) return false;\n        }\n        return true;\n    }\n\n    void CellPreloader::setTerrainPreloadPositions(const std::vector<CellPreloader::PositionCellGrid> &positions)\n    {\n        if (positions.empty())\n            mTerrainPreloadPositions.clear();\n        else if (contains(mTerrainPreloadPositions, positions))\n            return;\n        if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())\n            return;\n        else\n        {\n            if (mTerrainViews.size() > positions.size())\n            {\n                for (unsigned int i=positions.size(); i<mTerrainViews.size(); ++i)\n                    mUnrefQueue->push(mTerrainViews[i]);\n                mTerrainViews.resize(positions.size());\n            }\n            else if (mTerrainViews.size() < positions.size())\n            {\n                for (unsigned int i=mTerrainViews.size(); i<positions.size(); ++i)\n                    mTerrainViews.emplace_back(mTerrain->createView());\n            }\n\n            mTerrainPreloadPositions = positions;\n            if (!positions.empty())\n            {\n                mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);\n                mWorkQueue->addWorkItem(mTerrainPreloadItem);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/cellpreloader.hpp",
    "content": "#ifndef OPENMW_MWWORLD_CELLPRELOADER_H\n#define OPENMW_MWWORLD_CELLPRELOADER_H\n\n#include <map>\n#include <osg/ref_ptr>\n#include <osg/Vec3f>\n#include <osg/Vec4i>\n#include <components/sceneutil/workqueue.hpp>\n\nnamespace Resource\n{\n    class ResourceSystem;\n    class BulletShapeManager;\n}\n\nnamespace Terrain\n{\n    class World;\n    class View;\n}\n\nnamespace SceneUtil\n{\n    class UnrefQueue;\n}\n\nnamespace MWRender\n{\n    class LandManager;\n}\n\nnamespace MWWorld\n{\n    class CellStore;\n    class TerrainPreloadItem;\n\n    class CellPreloader\n    {\n    public:\n        CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain, MWRender::LandManager* landManager);\n        ~CellPreloader();\n\n        /// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell.\n        /// @note The cell itself must be in State_Loaded or State_Preloaded.\n        void preload(MWWorld::CellStore* cell, double timestamp);\n\n        void notifyLoaded(MWWorld::CellStore* cell);\n\n        void clear();\n\n        /// Removes preloaded cells that have not had a preload request for a while.\n        void updateCache(double timestamp);\n\n        /// How long to keep a preloaded cell in cache after it's no longer requested.\n        void setExpiryDelay(double expiryDelay);\n\n        /// The minimum number of preloaded cells before unused cells get thrown out.\n        void setMinCacheSize(unsigned int num);\n\n        /// The maximum number of preloaded cells.\n        void setMaxCacheSize(unsigned int num);\n\n        /// Enables the creation of instances in the preloading thread.\n        void setPreloadInstances(bool preload);\n\n        unsigned int getMaxCacheSize() const;\n\n        void setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue);\n\n        void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue);\n\n        typedef std::pair<osg::Vec3f, osg::Vec4i> PositionCellGrid;\n        void setTerrainPreloadPositions(const std::vector<PositionCellGrid>& positions);\n\n        bool syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, int& progress, int& progressRange, double timestamp);\n        void abortTerrainPreloadExcept(const PositionCellGrid *exceptPos);\n\n    private:\n        Resource::ResourceSystem* mResourceSystem;\n        Resource::BulletShapeManager* mBulletShapeManager;\n        Terrain::World* mTerrain;\n        MWRender::LandManager* mLandManager;\n        osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;\n        osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;\n        double mExpiryDelay;\n        unsigned int mMinCacheSize;\n        unsigned int mMaxCacheSize;\n        bool mPreloadInstances;\n\n        double mLastResourceCacheUpdate;\n        int mStoreViewsFailCount;\n\n        struct PreloadEntry\n        {\n            PreloadEntry(double timestamp, osg::ref_ptr<SceneUtil::WorkItem> workItem)\n                : mTimeStamp(timestamp)\n                , mWorkItem(workItem)\n            {\n            }\n            PreloadEntry()\n                : mTimeStamp(0.0)\n            {\n            }\n\n            double mTimeStamp;\n            osg::ref_ptr<SceneUtil::WorkItem> mWorkItem;\n        };\n        typedef std::map<const MWWorld::CellStore*, PreloadEntry> PreloadMap;\n\n        // Cells that are currently being preloaded, or have already finished preloading\n        PreloadMap mPreloadCells;\n\n        std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;\n        std::vector<PositionCellGrid> mTerrainPreloadPositions;\n        osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem;\n        osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/cellref.cpp",
    "content": "#include \"cellref.hpp\"\n\n#include <components/esm/objectstate.hpp>\n\nnamespace MWWorld\n{\n\n    const ESM::RefNum& CellRef::getRefNum() const\n    {\n        return mCellRef.mRefNum;\n    }\n\n    bool CellRef::hasContentFile() const\n    {\n        return mCellRef.mRefNum.hasContentFile();\n    }\n\n    void CellRef::unsetRefNum()\n    {\n        mCellRef.mRefNum.unset();\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Set the unique reference number index of a CellRef, needed to\n        make objects retain their uniqueIndex when they are updated\n        after their records are modified on the fly by the server\n    */\n    void CellRef::setRefNum(unsigned int index)\n    {\n        mCellRef.mRefNum.mIndex = index;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Get the mMpNum (unique multiplayer number) of a CellRef\n    */\n    unsigned int CellRef::getMpNum() const\n    {\n        return mCellRef.mMpNum;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Set the mMpNum (unique multiplayer reference number) of a CellRef\n    */\n    void CellRef::setMpNum(unsigned int index)\n    {\n        mCellRef.mMpNum = index;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    std::string CellRef::getRefId() const\n    {\n        return mCellRef.mRefID;\n    }\n\n    const std::string* CellRef::getRefIdPtr() const\n    {\n        return &mCellRef.mRefID;\n    }\n\n    bool CellRef::getTeleport() const\n    {\n        return mCellRef.mTeleport;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to change the teleport state from elsewhere\n    */\n    void CellRef::setTeleport(bool teleportState)\n    {\n        mCellRef.mTeleport = teleportState;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    ESM::Position CellRef::getDoorDest() const\n    {\n        return mCellRef.mDoorDest;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to change the destination position from elsewhere\n    */\n    void CellRef::setDoorDest(const ESM::Position& position)\n    {\n        mCellRef.mDoorDest = position;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    std::string CellRef::getDestCell() const\n    {\n        return mCellRef.mDestCell;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to change the destination cell from elsewhere\n    */\n    void CellRef::setDestCell(const std::string& cellDescription)\n    {\n        mCellRef.mDestCell = cellDescription;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    float CellRef::getScale() const\n    {\n        return mCellRef.mScale;\n    }\n\n    void CellRef::setScale(float scale)\n    {\n        if (scale != mCellRef.mScale)\n        {\n            mChanged = true;\n            mCellRef.mScale = scale;\n        }\n    }\n\n    ESM::Position CellRef::getPosition() const\n    {\n        return mCellRef.mPos;\n    }\n\n    void CellRef::setPosition(const ESM::Position &position)\n    {\n        mChanged = true;\n        mCellRef.mPos = position;\n    }\n\n    float CellRef::getEnchantmentCharge() const\n    {\n        return mCellRef.mEnchantmentCharge;\n    }\n\n    float CellRef::getNormalizedEnchantmentCharge(int maxCharge) const\n    {\n        if (maxCharge == 0)\n        {\n            return 0;\n        }\n        else if (mCellRef.mEnchantmentCharge == -1)\n        {\n            return 1;\n        }\n        else\n        {\n            return mCellRef.mEnchantmentCharge / static_cast<float>(maxCharge);\n        }\n    }\n\n    void CellRef::setEnchantmentCharge(float charge)\n    {\n        if (charge != mCellRef.mEnchantmentCharge)\n        {\n            mChanged = true;\n            mCellRef.mEnchantmentCharge = charge;\n        }\n    }\n\n    int CellRef::getCharge() const\n    {\n        return mCellRef.mChargeInt;\n    }\n\n    void CellRef::setCharge(int charge)\n    {\n        if (charge != mCellRef.mChargeInt)\n        {\n            mChanged = true;\n            mCellRef.mChargeInt = charge;\n        }\n    }\n\n    void CellRef::applyChargeRemainderToBeSubtracted(float chargeRemainder)\n    {\n        mCellRef.mChargeIntRemainder += std::abs(chargeRemainder);\n        if (mCellRef.mChargeIntRemainder > 1.0f)\n        {\n            float newChargeRemainder = (mCellRef.mChargeIntRemainder - std::floor(mCellRef.mChargeIntRemainder));\n            if (mCellRef.mChargeInt <= static_cast<int>(mCellRef.mChargeIntRemainder))\n            {\n                mCellRef.mChargeInt = 0;\n            }\n            else\n            {\n                mCellRef.mChargeInt -= static_cast<int>(mCellRef.mChargeIntRemainder);\n            }\n            mCellRef.mChargeIntRemainder = newChargeRemainder;\n        }\n    }\n\n    float CellRef::getChargeFloat() const\n    {\n        return mCellRef.mChargeFloat;\n    }\n\n    void CellRef::setChargeFloat(float charge)\n    {\n        if (charge != mCellRef.mChargeFloat)\n        {\n            mChanged = true;\n            mCellRef.mChargeFloat = charge;\n        }\n    }\n\n    std::string CellRef::getOwner() const\n    {\n        return mCellRef.mOwner;\n    }\n\n    std::string CellRef::getGlobalVariable() const\n    {\n        return mCellRef.mGlobalVariable;\n    }\n\n    void CellRef::resetGlobalVariable()\n    {\n        if (!mCellRef.mGlobalVariable.empty())\n        {\n            mChanged = true;\n            mCellRef.mGlobalVariable.erase();\n        }\n    }\n\n    void CellRef::setFactionRank(int factionRank)\n    {\n        if (factionRank != mCellRef.mFactionRank)\n        {\n            mChanged = true;\n            mCellRef.mFactionRank = factionRank;\n        }\n    }\n\n    int CellRef::getFactionRank() const\n    {\n        return mCellRef.mFactionRank;\n    }\n\n    void CellRef::setOwner(const std::string &owner)\n    {\n        if (owner != mCellRef.mOwner)\n        {\n            mChanged = true;\n            mCellRef.mOwner = owner;\n        }\n    }\n\n    std::string CellRef::getSoul() const\n    {\n        return mCellRef.mSoul;\n    }\n\n    void CellRef::setSoul(const std::string &soul)\n    {\n        if (soul != mCellRef.mSoul)\n        {\n            mChanged = true;\n            mCellRef.mSoul = soul;\n        }\n    }\n\n    std::string CellRef::getFaction() const\n    {\n        return mCellRef.mFaction;\n    }\n\n    void CellRef::setFaction(const std::string &faction)\n    {\n        if (faction != mCellRef.mFaction)\n        {\n            mChanged = true;\n            mCellRef.mFaction = faction;\n        }\n    }\n\n    int CellRef::getLockLevel() const\n    {\n        return mCellRef.mLockLevel;\n    }\n\n    void CellRef::setLockLevel(int lockLevel)\n    {\n        if (lockLevel != mCellRef.mLockLevel)\n        {\n            mChanged = true;\n            mCellRef.mLockLevel = lockLevel;\n        }\n    }\n\n    void CellRef::lock(int lockLevel)\n    {\n        if(lockLevel != 0)\n            setLockLevel(abs(lockLevel)); //Changes lock to locklevel, if positive\n        else\n            setLockLevel(ESM::UnbreakableLock); // If zero, set to max lock level\n    }\n\n    void CellRef::unlock()\n    {\n        setLockLevel(-abs(mCellRef.mLockLevel)); //Makes lockLevel negative\n    }\n\n    std::string CellRef::getKey() const\n    {\n        return mCellRef.mKey;\n    }\n\n    std::string CellRef::getTrap() const\n    {\n        return mCellRef.mTrap;\n    }\n\n    void CellRef::setTrap(const std::string& trap)\n    {\n        if (trap != mCellRef.mTrap)\n        {\n            mChanged = true;\n            mCellRef.mTrap = trap;\n        }\n    }\n\n    int CellRef::getGoldValue() const\n    {\n        return mCellRef.mGoldValue;\n    }\n\n    void CellRef::setGoldValue(int value)\n    {\n        if (value != mCellRef.mGoldValue)\n        {\n            mChanged = true;\n            mCellRef.mGoldValue = value;\n        }\n    }\n\n    void CellRef::writeState(ESM::ObjectState &state) const\n    {\n        state.mRef = mCellRef;\n    }\n\n    bool CellRef::hasChanged() const\n    {\n        return mChanged;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/cellref.hpp",
    "content": "#ifndef OPENMW_MWWORLD_CELLREF_H\n#define OPENMW_MWWORLD_CELLREF_H\n\n#include <components/esm/cellref.hpp>\n\nnamespace ESM\n{\n    struct ObjectState;\n}\n\nnamespace MWWorld\n{\n\n    /// \\brief Encapsulated variant of ESM::CellRef with change tracking\n    class CellRef\n    {\n    public:\n\n        CellRef (const ESM::CellRef& ref)\n            : mCellRef(ref)\n        {\n            mChanged = false;\n        }\n\n        // Note: Currently unused for items in containers\n        const ESM::RefNum& getRefNum() const;\n\n        // Set RefNum to its default state.\n        void unsetRefNum();\n\n        /*\n            Start of tes3mp addition\n\n            Set the unique reference number index of a CellRef, needed to\n            make objects retain their uniqueIndex when they are updated\n            after their records are modified on the fly by the server\n        */\n        void setRefNum(unsigned int index);\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Get the mMpNum (unique multiplayer reference number) of a CellRef\n        */\n        unsigned int getMpNum() const;\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Set the mMpNum (unique multiplayer reference number) of a CellRef\n        */\n        void setMpNum(unsigned int index);\n        /*\n            End of tes3mp addition\n        */\n\n        /// Does the RefNum have a content file?\n        bool hasContentFile() const;\n\n        // Id of object being referenced\n        std::string getRefId() const;\n\n        // Pointer to ID of the object being referenced\n        const std::string* getRefIdPtr() const;\n\n        // For doors - true if this door teleports to somewhere else, false\n        // if it should open through animation.\n        bool getTeleport() const;\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to change the teleport state from elsewhere\n        */\n        void setTeleport(bool teleportState);\n        /*\n            End of tes3mp addition\n        */\n\n        // Teleport location for the door, if this is a teleporting door.\n        ESM::Position getDoorDest() const;\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to change the destination position from elsewhere\n        */\n        void setDoorDest(const ESM::Position& position);\n        /*\n            End of tes3mp addition\n        */\n\n        // Destination cell for doors (optional)\n        std::string getDestCell() const;\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to change the destination cell from elsewhere\n        */\n        void setDestCell(const std::string& cellDescription);\n        /*\n            End of tes3mp addition\n        */\n\n        // Scale applied to mesh\n        float getScale() const;\n        void setScale(float scale);\n\n        // The *original* position and rotation as it was given in the Construction Set.\n        // Current position and rotation of the object is stored in RefData.\n        ESM::Position getPosition() const;\n        void setPosition (const ESM::Position& position);\n\n        // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full).\n        float getEnchantmentCharge() const;\n\n        // Remaining enchantment charge rescaled to the supplied maximum charge (such as one of the enchantment).\n        float getNormalizedEnchantmentCharge(int maxCharge) const;\n\n        void setEnchantmentCharge(float charge);\n\n        // For weapon or armor, this is the remaining item health.\n        // For tools (lockpicks, probes, repair hammer) it is the remaining uses.\n        // If this returns int(-1) it means full health.\n        int getCharge() const;\n        float getChargeFloat() const; // Implemented as union with int charge\n        void setCharge(int charge);\n        void setChargeFloat(float charge);\n        void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if > 1\n\n        // The NPC that owns this object (and will get angry if you steal it)\n        std::string getOwner() const;\n        void setOwner(const std::string& owner);\n\n        // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed\n        // even if it has an Owner field.\n        // Used by bed rent scripts to allow the player to use the bed for the duration of the rent.\n        std::string getGlobalVariable() const;\n\n        void resetGlobalVariable();\n\n        // ID of creature trapped in this soul gem\n        std::string getSoul() const;\n        void setSoul(const std::string& soul);\n\n        // The faction that owns this object (and will get angry if\n        // you take it and are not a faction member)\n        std::string getFaction() const;\n        void setFaction (const std::string& faction);\n\n        // PC faction rank required to use the item. Sometimes is -1, which means \"any rank\".\n        void setFactionRank(int factionRank);\n        int getFactionRank() const;\n\n        // Lock level for doors and containers\n        // Positive for a locked door. 0 for a door that was never locked.\n        // For an unlocked door, it is set to -(previous locklevel)\n        int getLockLevel() const;\n        void setLockLevel(int lockLevel);\n        void lock(int lockLevel);\n        void unlock();\n         // Key and trap ID names, if any\n        std::string getKey() const;\n        std::string getTrap() const;\n        void setTrap(const std::string& trap);\n\n        // This is 5 for Gold_005 references, 100 for Gold_100 and so on.\n        int getGoldValue() const;\n        void setGoldValue(int value);\n\n        // Write the content of this CellRef into the given ObjectState\n        void writeState (ESM::ObjectState& state) const;\n\n        // Has this CellRef changed since it was originally loaded?\n        bool hasChanged() const;\n\n    private:\n        bool mChanged;\n        ESM::CellRef mCellRef;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/cellreflist.hpp",
    "content": "#ifndef GAME_MWWORLD_CELLREFLIST_H\n#define GAME_MWWORLD_CELLREFLIST_H\n\n#include <list>\n\n#include \"livecellref.hpp\"\n\nnamespace MWWorld\n{\n    /// \\brief Collection of references of one type\n    template <typename X>\n    struct CellRefList\n    {\n        typedef LiveCellRef<X> LiveRef;\n        typedef std::list<LiveRef> List;\n        List mList;\n\n        /// Search for the given reference in the given reclist from\n        /// ESMStore. Insert the reference into the list if a match is\n        /// found. If not, throw an exception.\n        /// Moved to cpp file, as we require a custom compare operator for it,\n        /// and the build will fail with an ugly three-way cyclic header dependence\n        /// so we need to pass the instantiation of the method to the linker, when\n        /// all methods are known.\n        void load (ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore);\n\n        LiveRef &insert (const LiveRef &item)\n        {\n            mList.push_back(item);\n            return mList.back();\n        }\n\n        /// Remove all references with the given refNum from this list.\n        void remove (const ESM::RefNum &refNum)\n        {\n            for (typename List::iterator it = mList.begin(); it != mList.end();)\n            {\n                if (*it == refNum)\n                    mList.erase(it++);\n                else\n                    ++it;\n            }\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/cells.cpp",
    "content": "#include \"cells.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/defs.hpp>\n#include <components/esm/cellstate.hpp>\n#include <components/esm/cellref.hpp>\n#include <components/loadinglistener/loadinglistener.hpp>\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"esmstore.hpp\"\n#include \"containerstore.hpp\"\n#include \"cellstore.hpp\"\n\nnamespace\n{\n    template<class Visitor, class Key>\n    bool forEachInStore(const std::string& id, Visitor&& visitor, std::map<Key, MWWorld::CellStore>& cellStore)\n    {\n        for(auto& cell : cellStore)\n        {\n            if(cell.second.getState() == MWWorld::CellStore::State_Unloaded)\n                cell.second.preload();\n            if(cell.second.getState() == MWWorld::CellStore::State_Preloaded)\n            {\n                if(cell.second.hasId(id))\n                {\n                    cell.second.load();\n                }\n                else\n                    continue;\n            }\n            bool cont = cell.second.forEach([&] (MWWorld::Ptr ptr)\n            {\n                if(*ptr.getCellRef().getRefIdPtr() == id)\n                {\n                    return visitor(ptr);\n                }\n                return true;\n            });\n            if(!cont)\n                return false;\n        }\n        return true;\n    }\n\n    struct PtrCollector\n    {\n        std::vector<MWWorld::Ptr> mPtrs;\n\n        bool operator()(MWWorld::Ptr ptr)\n        {\n            mPtrs.emplace_back(ptr);\n            return true;\n        }\n    };\n}\n\nMWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell)\n{\n    if (cell->mData.mFlags & ESM::Cell::Interior)\n    {\n        std::string lowerName(Misc::StringUtils::lowerCase(cell->mName));\n        std::map<std::string, CellStore>::iterator result = mInteriors.find (lowerName);\n\n        if (result==mInteriors.end())\n        {\n            result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first;\n        }\n\n        return &result->second;\n    }\n    else\n    {\n        std::map<std::pair<int, int>, CellStore>::iterator result =\n            mExteriors.find (std::make_pair (cell->getGridX(), cell->getGridY()));\n\n        if (result==mExteriors.end())\n        {\n            result = mExteriors.insert (std::make_pair (\n                std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell, mStore, mReader))).first;\n\n        }\n\n        return &result->second;\n    }\n}\n\nvoid MWWorld::Cells::clear()\n{\n    mInteriors.clear();\n    mExteriors.clear();\n    std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair(\"\", (MWWorld::CellStore*)nullptr));\n    mIdCacheIndex = 0;\n}\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to clear the CellStore for a specific Cell,\n    allowing cells to be replaced from elsewhere in the code\n*/\nvoid MWWorld::Cells::clear(const ESM::Cell& cell)\n{\n    if (cell.isExterior())\n    {\n        std::pair <int, int> cellCoordinates;\n        cellCoordinates = std::make_pair(cell.getGridX(), cell.getGridY());\n        mExteriors.erase(cellCoordinates);\n    }\n    else if (mInteriors.count(Misc::StringUtils::lowerCase(cell.mName)) > 0)\n    {\n        mInteriors.erase(Misc::StringUtils::lowerCase(cell.mName));\n    }\n}\n/*\n    End of tes3mp addition\n*/\n\nMWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore& cellStore)\n{\n    Ptr ptr = getPtr (name, cellStore);\n\n    if (!ptr.isEmpty() && ptr.isInCell())\n    {\n        mIdCache[mIdCacheIndex].first = name;\n        mIdCache[mIdCacheIndex].second = &cellStore;\n        if (++mIdCacheIndex>=mIdCache.size())\n            mIdCacheIndex = 0;\n    }\n\n    return ptr;\n}\n\nvoid MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const\n{\n    if (cell.getState()!=CellStore::State_Loaded)\n        cell.load ();\n\n    ESM::CellState cellState;\n\n    cell.saveState (cellState);\n\n    writer.startRecord (ESM::REC_CSTA);\n    cellState.mId.save (writer);\n    cellState.save (writer);\n    cell.writeFog(writer);\n    cell.writeReferences (writer);\n    writer.endRecord (ESM::REC_CSTA);\n}\n\nMWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)\n: mStore (store), mReader (reader),\n  mIdCacheIndex (0)\n{\n    int cacheSize = std::clamp(Settings::Manager::getInt(\"pointers cache size\", \"Cells\"), 40, 1000);\n    mIdCache = IdCache(cacheSize, std::pair<std::string, CellStore *> (\"\", (CellStore*)nullptr));\n}\n\nMWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y)\n{\n    std::map<std::pair<int, int>, CellStore>::iterator result =\n        mExteriors.find (std::make_pair (x, y));\n\n    if (result==mExteriors.end())\n    {\n        const ESM::Cell *cell = mStore.get<ESM::Cell>().search(x, y);\n\n        if (!cell)\n        {\n            // Cell isn't predefined. Make one on the fly.\n            ESM::Cell record;\n            record.mCellId.mWorldspace = ESM::CellId::sDefaultWorldspace;\n            record.mCellId.mPaged = true;\n            record.mCellId.mIndex.mX = x;\n            record.mCellId.mIndex.mY = y;\n\n            record.mData.mFlags = ESM::Cell::HasWater;\n            record.mData.mX = x;\n            record.mData.mY = y;\n            record.mWater = 0;\n            record.mMapColor = 0;\n\n            cell = MWBase::Environment::get().getWorld()->createRecord (record);\n        }\n\n        result = mExteriors.insert (std::make_pair (\n            std::make_pair (x, y), CellStore (cell, mStore, mReader))).first;\n    }\n\n    if (result->second.getState()!=CellStore::State_Loaded)\n    {\n        result->second.load ();\n    }\n\n    return &result->second;\n}\n\nMWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name)\n{\n    std::string lowerName = Misc::StringUtils::lowerCase(name);\n    std::map<std::string, CellStore>::iterator result = mInteriors.find (lowerName);\n\n    if (result==mInteriors.end())\n    {\n        const ESM::Cell *cell = mStore.get<ESM::Cell>().find(lowerName);\n\n        result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first;\n    }\n\n    if (result->second.getState()!=CellStore::State_Loaded)\n    {\n        result->second.load ();\n    }\n\n    return &result->second;\n}\n\nvoid MWWorld::Cells::rest (double hours)\n{\n    for (auto &interior : mInteriors)\n    {\n        interior.second.rest(hours);\n    }\n\n    for (auto &exterior : mExteriors)\n    {\n        exterior.second.rest(hours);\n    }\n}\n\nvoid MWWorld::Cells::recharge (float duration)\n{\n    for (auto &interior : mInteriors)\n    {\n        interior.second.recharge(duration);\n    }\n\n    for (auto &exterior : mExteriors)\n    {\n        exterior.second.recharge(duration);\n    }\n}\n\nMWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id)\n{\n    if (id.mPaged)\n        return getExterior (id.mIndex.mX, id.mIndex.mY);\n\n    return getInterior (id.mWorldspace);\n}\n\nMWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell,\n    bool searchInContainers)\n{\n    if (cell.getState()==CellStore::State_Unloaded)\n        cell.preload ();\n\n    if (cell.getState()==CellStore::State_Preloaded)\n    {\n        if (cell.hasId (name))\n        {\n            cell.load ();\n        }\n        else\n            return Ptr();\n    }\n\n    Ptr ptr = cell.search (name);\n\n    if (!ptr.isEmpty() && MWWorld::CellStore::isAccessible(ptr.getRefData(), ptr.getCellRef()))\n        return ptr;\n\n    if (searchInContainers)\n        return cell.searchInContainer (name);\n\n    return Ptr();\n}\n\nMWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)\n{\n    // First check the cache\n    for (IdCache::iterator iter (mIdCache.begin()); iter!=mIdCache.end(); ++iter)\n        if (iter->first==name && iter->second)\n        {\n            Ptr ptr = getPtr (name, *iter->second);\n            if (!ptr.isEmpty())\n                return ptr;\n        }\n\n    // Then check cells that are already listed\n    // Search in reverse, this is a workaround for an ambiguous chargen_plank reference in the vanilla game.\n    // there is one at -22,16 and one at -2,-9, the latter should be used.\n    for (std::map<std::pair<int, int>, CellStore>::reverse_iterator iter = mExteriors.rbegin();\n        iter!=mExteriors.rend(); ++iter)\n    {\n        Ptr ptr = getPtrAndCache (name, iter->second);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    for (std::map<std::string, CellStore>::iterator iter = mInteriors.begin();\n        iter!=mInteriors.end(); ++iter)\n    {\n        Ptr ptr = getPtrAndCache (name, iter->second);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    // Now try the other cells\n    const MWWorld::Store<ESM::Cell> &cells = mStore.get<ESM::Cell>();\n    MWWorld::Store<ESM::Cell>::iterator iter;\n\n    for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter)\n    {\n        CellStore *cellStore = getCellStore (&(*iter));\n\n        Ptr ptr = getPtrAndCache (name, *cellStore);\n\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter)\n    {\n        CellStore *cellStore = getCellStore (&(*iter));\n\n        Ptr ptr = getPtrAndCache (name, *cellStore);\n\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    // giving up\n    return Ptr();\n}\n\nMWWorld::Ptr MWWorld::Cells::getPtr (const std::string& id, const ESM::RefNum& refNum)\n{\n    for (auto& pair : mInteriors)\n    {\n        Ptr ptr = getPtr(pair.second, id, refNum);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n    for (auto& pair : mExteriors)\n    {\n        Ptr ptr = getPtr(pair.second, id, refNum);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n    return Ptr();\n}\n\nMWWorld::Ptr MWWorld::Cells::getPtr(CellStore& cellStore, const std::string& id, const ESM::RefNum& refNum)\n{\n    if (cellStore.getState() == CellStore::State_Unloaded)\n        cellStore.preload();\n    if (cellStore.getState() == CellStore::State_Preloaded)\n    {\n        if (cellStore.hasId(id))\n            cellStore.load();\n        else\n            return Ptr();\n    }\n    return cellStore.searchViaRefNum(refNum);\n}\n\nvoid MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector<MWWorld::Ptr> &out)\n{\n    const MWWorld::Store<ESM::Cell> &cells = mStore.get<ESM::Cell>();\n    for (MWWorld::Store<ESM::Cell>::iterator iter = cells.extBegin(); iter != cells.extEnd(); ++iter)\n    {\n        CellStore *cellStore = getCellStore (&(*iter));\n\n        Ptr ptr = getPtrAndCache (name, *cellStore);\n\n        if (!ptr.isEmpty())\n            out.push_back(ptr);\n    }\n}\n\nvoid MWWorld::Cells::getInteriorPtrs(const std::string &name, std::vector<MWWorld::Ptr> &out)\n{\n    const MWWorld::Store<ESM::Cell> &cells = mStore.get<ESM::Cell>();\n    for (MWWorld::Store<ESM::Cell>::iterator iter = cells.intBegin(); iter != cells.intEnd(); ++iter)\n    {\n        CellStore *cellStore = getCellStore (&(*iter));\n\n        Ptr ptr = getPtrAndCache (name, *cellStore);\n\n        if (!ptr.isEmpty())\n            out.push_back(ptr);\n    }\n}\n\nstd::vector<MWWorld::Ptr> MWWorld::Cells::getAll(const std::string& id)\n{\n    PtrCollector visitor;\n    if(forEachInStore(id, visitor, mInteriors))\n        forEachInStore(id, visitor, mExteriors);\n    return visitor.mPtrs;\n}\n\nint MWWorld::Cells::countSavedGameRecords() const\n{\n    int count = 0;\n\n    for (std::map<std::string, CellStore>::const_iterator iter (mInteriors.begin());\n        iter!=mInteriors.end(); ++iter)\n        if (iter->second.hasState())\n            ++count;\n\n    for (std::map<std::pair<int, int>, CellStore>::const_iterator iter (mExteriors.begin());\n        iter!=mExteriors.end(); ++iter)\n        if (iter->second.hasState())\n            ++count;\n\n    return count;\n}\n\nvoid MWWorld::Cells::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n{\n    for (std::map<std::pair<int, int>, CellStore>::iterator iter (mExteriors.begin());\n        iter!=mExteriors.end(); ++iter)\n        if (iter->second.hasState())\n        {\n            writeCell (writer, iter->second);\n            progress.increaseProgress();\n        }\n\n    for (std::map<std::string, CellStore>::iterator iter (mInteriors.begin());\n        iter!=mInteriors.end(); ++iter)\n        if (iter->second.hasState())\n        {\n            writeCell (writer, iter->second);\n            progress.increaseProgress();\n        }\n}\n\nstruct GetCellStoreCallback : public MWWorld::CellStore::GetCellStoreCallback\n{\npublic:\n    GetCellStoreCallback(MWWorld::Cells& cells)\n        : mCells(cells)\n    {\n    }\n\n    MWWorld::Cells& mCells;\n\n    MWWorld::CellStore* getCellStore(const ESM::CellId& cellId) override\n    {\n        try\n        {\n            return mCells.getCell(cellId);\n        }\n        catch (...)\n        {\n            return nullptr;\n        }\n    }\n};\n\nbool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type,\n    const std::map<int, int>& contentFileMap)\n{\n    if (type==ESM::REC_CSTA)\n    {\n        ESM::CellState state;\n        state.mId.load (reader);\n\n        CellStore *cellStore = nullptr;\n\n        try\n        {\n            cellStore = getCell (state.mId);\n        }\n        catch (...)\n        {\n            // silently drop cells that don't exist anymore\n            Log(Debug::Warning) << \"Warning: Dropping state for cell \" << state.mId.mWorldspace << \" (cell no longer exists)\";\n            reader.skipRecord();\n            return true;\n        }\n\n        state.load (reader);\n        cellStore->loadState (state);\n\n        if (state.mHasFogOfWar)\n            cellStore->readFog(reader);\n\n        if (cellStore->getState()!=CellStore::State_Loaded)\n            cellStore->load ();\n\n        GetCellStoreCallback callback(*this);\n\n        cellStore->readReferences (reader, contentFileMap, &callback);\n\n        return true;\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/cells.hpp",
    "content": "#ifndef GAME_MWWORLD_CELLS_H\n#define GAME_MWWORLD_CELLS_H\n\n#include <map>\n#include <list>\n#include <string>\n\n#include \"ptr.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n    struct CellId;\n    struct Cell;\n    struct RefNum;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWWorld\n{\n    class ESMStore;\n\n    /// \\brief Cell container\n    class Cells\n    {\n            typedef std::vector<std::pair<std::string, CellStore *> > IdCache;\n            const MWWorld::ESMStore& mStore;\n            std::vector<ESM::ESMReader>& mReader;\n            mutable std::map<std::string, CellStore> mInteriors;\n            mutable std::map<std::pair<int, int>, CellStore> mExteriors;\n            IdCache mIdCache;\n            std::size_t mIdCacheIndex;\n\n            Cells (const Cells&);\n            Cells& operator= (const Cells&);\n\n            CellStore *getCellStore (const ESM::Cell *cell);\n\n            Ptr getPtrAndCache (const std::string& name, CellStore& cellStore);\n\n            Ptr getPtr(CellStore& cellStore, const std::string& id, const ESM::RefNum& refNum);\n\n            void writeCell (ESM::ESMWriter& writer, CellStore& cell) const;\n\n        public:\n\n            void clear();\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to clear the CellStore for a specific Cell,\n                allowing cells to be replaced from elsewhere in the code\n            */\n            void clear(const ESM::Cell& cell);\n            /*\n                End of tes3mp addition\n            */\n\n            Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader);\n\n            CellStore *getExterior (int x, int y);\n\n            CellStore *getInterior (const std::string& name);\n\n            CellStore *getCell (const ESM::CellId& id);\n\n            Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false);\n            ///< \\param searchInContainers Only affect loaded cells.\n            /// @note name must be lower case\n\n            /// @note name must be lower case\n            Ptr getPtr (const std::string& name);\n\n            Ptr getPtr(const std::string& id, const ESM::RefNum& refNum);\n\n            void rest (double hours);\n            void recharge (float duration);\n\n            /// Get all Ptrs referencing \\a name in exterior cells\n            /// @note Due to the current implementation of getPtr this only supports one Ptr per cell.\n            /// @note name must be lower case\n            void getExteriorPtrs (const std::string& name, std::vector<MWWorld::Ptr>& out);\n\n            /// Get all Ptrs referencing \\a name in interior cells\n            /// @note Due to the current implementation of getPtr this only supports one Ptr per cell.\n            /// @note name must be lower case\n            void getInteriorPtrs (const std::string& name, std::vector<MWWorld::Ptr>& out);\n\n            std::vector<MWWorld::Ptr> getAll(const std::string& id);\n\n            int countSavedGameRecords() const;\n\n            void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;\n\n            bool readRecord (ESM::ESMReader& reader, uint32_t type,\n                const std::map<int, int>& contentFileMap);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/cellstore.cpp",
    "content": "#include \"cellstore.hpp\"\n\n#include <algorithm>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/CellController.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/esm/cellstate.hpp>\n#include <components/esm/cellid.hpp>\n#include <components/esm/cellref.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/objectstate.hpp>\n#include <components/esm/containerstate.hpp>\n#include <components/esm/npcstate.hpp>\n#include <components/esm/creaturestate.hpp>\n#include <components/esm/fogstate.hpp>\n#include <components/esm/creaturelevliststate.hpp>\n#include <components/esm/doorstate.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/recharge.hpp\"\n\n#include \"ptr.hpp\"\n#include \"esmstore.hpp\"\n#include \"class.hpp\"\n#include \"containerstore.hpp\"\n\nnamespace\n{\n    template<typename T>\n    MWWorld::Ptr searchInContainerList (MWWorld::CellRefList<T>& containerList, const std::string& id)\n    {\n        for (typename MWWorld::CellRefList<T>::List::iterator iter (containerList.mList.begin());\n             iter!=containerList.mList.end(); ++iter)\n        {\n            MWWorld::Ptr container (&*iter, nullptr);\n\n            if (container.getRefData().getCustomData() == nullptr)\n                continue;\n\n            MWWorld::Ptr ptr =\n                container.getClass().getContainerStore (container).search (id);\n\n            if (!ptr.isEmpty())\n                return ptr;\n        }\n\n        return MWWorld::Ptr();\n    }\n\n    template<typename T>\n    MWWorld::Ptr searchViaActorId (MWWorld::CellRefList<T>& actorList, int actorId,\n        MWWorld::CellStore *cell, const std::map<MWWorld::LiveCellRefBase*, MWWorld::CellStore*>& toIgnore)\n    {\n        for (typename MWWorld::CellRefList<T>::List::iterator iter (actorList.mList.begin());\n             iter!=actorList.mList.end(); ++iter)\n        {\n            MWWorld::Ptr actor (&*iter, cell);\n\n            if (toIgnore.find(&*iter) != toIgnore.end())\n                continue;\n\n            if (actor.getClass().getCreatureStats (actor).matchesActorId (actorId) && actor.getRefData().getCount() > 0)\n                return actor;\n        }\n\n        return MWWorld::Ptr();\n    }\n\n    template<typename RecordType, typename T>\n    void writeReferenceCollection (ESM::ESMWriter& writer,\n        const MWWorld::CellRefList<T>& collection)\n    {\n        if (!collection.mList.empty())\n        {\n            // references\n            for (typename MWWorld::CellRefList<T>::List::const_iterator\n                iter (collection.mList.begin());\n                iter!=collection.mList.end(); ++iter)\n            {\n                if (!iter->mData.hasChanged() && !iter->mRef.hasChanged() && iter->mRef.hasContentFile())\n                {\n                    // Reference that came from a content file and has not been changed -> ignore\n                    continue;\n                }\n                if (iter->mData.getCount()==0 && !iter->mRef.hasContentFile())\n                {\n                    // Deleted reference that did not come from a content file -> ignore\n                    continue;\n                }\n\n                RecordType state;\n                iter->save (state);\n\n                // recordId currently unused\n                writer.writeHNT (\"OBJE\", collection.mList.front().mBase->sRecordId);\n\n                state.save (writer);\n            }\n        }\n    }\n\n    template<class RecordType, class T>\n    void fixRestockingImpl(const T* base, RecordType& state)\n    {\n        // Workaround for old saves not containing negative quantities\n        for(const auto& baseItem : base->mInventory.mList)\n        {\n            if(baseItem.mCount < 0)\n            {\n                for(auto& item : state.mInventory.mItems)\n                {\n                    if(item.mCount > 0 && Misc::StringUtils::ciEqual(baseItem.mItem, item.mRef.mRefID))\n                        item.mCount = -item.mCount;\n                }\n            }\n        }\n    }\n\n    template<class RecordType, class T>\n    void fixRestocking(const T* base, RecordType& state)\n    {}\n\n    template<>\n    void fixRestocking<>(const ESM::Creature* base, ESM::CreatureState& state)\n    {\n        fixRestockingImpl(base, state);\n    }\n\n    template<>\n    void fixRestocking<>(const ESM::NPC* base, ESM::NpcState& state)\n    {\n        fixRestockingImpl(base, state);\n    }\n\n    template<>\n    void fixRestocking<>(const ESM::Container* base, ESM::ContainerState& state)\n    {\n        fixRestockingImpl(base, state);\n    }\n\n    template<typename RecordType, typename T>\n    void readReferenceCollection (ESM::ESMReader& reader,\n        MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, const std::map<int, int>& contentFileMap, MWWorld::CellStore* cellstore)\n    {\n        const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();\n\n        RecordType state;\n        state.mRef = cref;\n        state.load(reader);\n\n        // If the reference came from a content file, make sure this content file is loaded\n        if (state.mRef.mRefNum.hasContentFile())\n        {\n            std::map<int, int>::const_iterator iter =\n                contentFileMap.find (state.mRef.mRefNum.mContentFile);\n\n            if (iter==contentFileMap.end())\n                return; // content file has been removed -> skip\n\n            state.mRef.mRefNum.mContentFile = iter->second;\n        }\n\n        if (!MWWorld::LiveCellRef<T>::checkState (state))\n            return; // not valid anymore with current content files -> skip\n\n        const T *record = esmStore.get<T>().search (state.mRef.mRefID);\n\n        if (!record)\n            return;\n\n        if (state.mVersion < 15)\n            fixRestocking(record, state);\n\n        if (state.mRef.mRefNum.hasContentFile())\n        {\n            for (typename MWWorld::CellRefList<T>::List::iterator iter (collection.mList.begin());\n                iter!=collection.mList.end(); ++iter)\n                if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID)\n                {\n                    // overwrite existing reference\n                    float oldscale = iter->mRef.getScale();\n                    iter->load (state);\n                    const ESM::Position & oldpos = iter->mRef.getPosition();\n                    const ESM::Position & newpos = iter->mData.getPosition();\n                    const MWWorld::Ptr ptr(&*iter, cellstore);\n                    if ((oldscale != iter->mRef.getScale() || oldpos.asVec3() != newpos.asVec3() || oldpos.rot[0] != newpos.rot[0] || oldpos.rot[1] != newpos.rot[1] || oldpos.rot[2] != newpos.rot[2]) && !ptr.getClass().isActor())\n                        MWBase::Environment::get().getWorld()->moveObject(ptr, newpos.pos[0], newpos.pos[1], newpos.pos[2]);\n                    if (!iter->mData.isEnabled())\n                    {\n                        iter->mData.enable();\n                        MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore));\n                    }\n                    return;\n                }\n\n            Log(Debug::Warning) << \"Warning: Dropping reference to \" << state.mRef.mRefID << \" (invalid content file link)\";\n            return;\n        }\n\n        // new reference\n        MWWorld::LiveCellRef<T> ref (record);\n        ref.load (state);\n        collection.mList.push_back (ref);\n    }\n}\n\nnamespace MWWorld\n{\n\n    template <typename X>\n    void CellRefList<X>::load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore)\n    {\n        const MWWorld::Store<X> &store = esmStore.get<X>();\n\n        if (const X *ptr = store.search (ref.mRefID))\n        {\n            typename std::list<LiveRef>::iterator iter =\n                std::find(mList.begin(), mList.end(), ref.mRefNum);\n\n            LiveRef liveCellRef (ref, ptr);\n\n            if (deleted)\n                liveCellRef.mData.setDeletedByContentFile(true);\n\n            if (iter != mList.end())\n                *iter = liveCellRef;\n            else\n                mList.push_back (liveCellRef);\n        }\n        else\n        {\n            Log(Debug::Warning)\n                << \"Warning: could not resolve cell reference '\" << ref.mRefID << \"'\"\n                << \" (dropping reference)\";\n        }\n    }\n\n    template<typename X> bool operator==(const LiveCellRef<X>& ref, int pRefnum)\n    {\n        return (ref.mRef.mRefnum == pRefnum);\n    }\n\n    Ptr CellStore::getCurrentPtr(LiveCellRefBase *ref)\n    {\n        MovedRefTracker::iterator found = mMovedToAnotherCell.find(ref);\n        if (found != mMovedToAnotherCell.end())\n            return Ptr(ref, found->second);\n        return Ptr(ref, this);\n    }\n\n    void CellStore::moveFrom(const Ptr &object, CellStore *from)\n    {\n        if (mState != State_Loaded)\n            load();\n\n        mHasState = true;\n        MovedRefTracker::iterator found = mMovedToAnotherCell.find(object.getBase());\n        if (found != mMovedToAnotherCell.end())\n        {\n            // A cell we had previously moved an object to is returning it to us.\n            \n            /*\n                Start of tes3mp addition\n\n                Add extra debug for multiplayer purposes\n            */\n            if (found->second != from)\n            {\n                \n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Storage: %s owned %s which it gave to %s which isn't %s, which should result in a crash\\n\",\n                    this->getCell()->getDescription().c_str(),\n                    object.getBase()->mRef.getRefId().c_str(),\n                    found->second->getCell()->getDescription().c_str(),\n                    from->getCell()->getDescription().c_str());\n            }\n            /*\n                End of tes3mp addition\n            */\n            \n            assert (found->second == from);\n            mMovedToAnotherCell.erase(found);\n        }\n        else\n        {\n            mMovedHere.insert(std::make_pair(object.getBase(), from));\n        }\n        updateMergedRefs();\n    }\n\n    MWWorld::Ptr CellStore::moveTo(const Ptr &object, CellStore *cellToMoveTo)\n    {\n        if (cellToMoveTo == this)\n            throw std::runtime_error(\"moveTo: object is already in this cell\");\n\n        // We assume that *this is in State_Loaded since we could hardly have reference to a live object otherwise.\n        if (mState != State_Loaded)\n            throw std::runtime_error(\"moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)\");\n\n        // Ensure that the object actually exists in the cell\n        if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty())\n            throw std::runtime_error(\"moveTo: object is not in this cell\");\n\n\n        // Objects with no refnum can't be handled correctly in the merging process that happens\n        // on a save/load, so do a simple copy & delete for these objects.\n\n        /*\n            Start of tes3mp change (major)\n\n            Disable the following code because it breaks DedicatedPlayers\n        */\n        /*\n        if (!object.getCellRef().getRefNum().hasContentFile())\n        {\n            MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo, object.getRefData().getCount());\n            object.getRefData().setCount(0);\n            object.getRefData().setBaseNode(nullptr);\n            return copied;\n        }\n        */\n        /*\n            End of tes3mp change (major)\n        */\n\n        MovedRefTracker::iterator found = mMovedHere.find(object.getBase());\n        if (found != mMovedHere.end())\n        {\n            // Special case - object didn't originate in this cell\n            // Move it back to its original cell first\n            CellStore* originalCell = found->second;\n            assert (originalCell != this);\n            originalCell->moveFrom(object, this);\n\n            mMovedHere.erase(found);\n\n            // Now that object is back to its rightful owner, we can move it\n            if (cellToMoveTo != originalCell)\n            {\n                /*\n                    Start of tes3mp addition\n\n                    Add extra debug for multiplayer purposes\n                */\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Storage: %s's original cell %s gives it from %s to %s\\n\",\n                    object.getBase()->mRef.getRefId().c_str(),\n                    originalCell->getCell()->getDescription().c_str(),\n                    this->getCell()->getDescription().c_str(),\n                    cellToMoveTo->getCell()->getDescription().c_str());\n                /*\n                    End of tes3mp addition\n                */\n                \n                originalCell->moveTo(object, cellToMoveTo);\n            }\n\n            updateMergedRefs();\n            return MWWorld::Ptr(object.getBase(), cellToMoveTo);\n        }\n\n        cellToMoveTo->moveFrom(object, this);\n        mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo));\n\n        updateMergedRefs();\n        return MWWorld::Ptr(object.getBase(), cellToMoveTo);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to clear the moves to other cells tracked for objects, allowing for\n        on-the-fly cell resets that don't cause crashes\n    */\n    void CellStore::clearMovesToCells()\n    {\n        MWBase::World* world = MWBase::Environment::get().getWorld();\n\n        for (auto &reference : mMovedHere)\n        {\n            MWWorld::CellStore *otherCell = reference.second;\n\n            otherCell->mMovedToAnotherCell.erase(reference.first);\n        }\n\n        for (auto &reference : mMovedToAnotherCell)\n        {\n            MWWorld::CellStore *otherCell = reference.second;\n            \n            otherCell->mMovedHere.erase(reference.first);\n        }\n\n        mMovedHere.empty();\n        mMovedToAnotherCell.empty();\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    struct MergeVisitor\n    {\n        MergeVisitor(std::vector<LiveCellRefBase*>& mergeTo, const std::map<LiveCellRefBase*, MWWorld::CellStore*>& movedHere,\n                     const std::map<LiveCellRefBase*, MWWorld::CellStore*>& movedToAnotherCell)\n            : mMergeTo(mergeTo)\n            , mMovedHere(movedHere)\n            , mMovedToAnotherCell(movedToAnotherCell)\n        {\n        }\n\n        bool operator() (const MWWorld::Ptr& ptr)\n        {\n            if (mMovedToAnotherCell.find(ptr.getBase()) != mMovedToAnotherCell.end())\n                return true;\n            mMergeTo.push_back(ptr.getBase());\n            return true;\n        }\n\n        void merge()\n        {\n            for (const auto & [base, _] : mMovedHere)\n                mMergeTo.push_back(base);\n        }\n\n    private:\n        std::vector<LiveCellRefBase*>& mMergeTo;\n\n        const std::map<LiveCellRefBase*, MWWorld::CellStore*>& mMovedHere;\n        const std::map<LiveCellRefBase*, MWWorld::CellStore*>& mMovedToAnotherCell;\n    };\n\n    void CellStore::updateMergedRefs()\n    {\n        mMergedRefs.clear();\n        mRechargingItemsUpToDate = false;\n        MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);\n        forEachInternal(visitor);\n        visitor.merge();\n\n        /*\n            Start of tes3mp addition\n\n            If the mwmp::Cell corresponding to this CellStore is under the authority of the LocalPlayer,\n            prepare a new initialization of LocalActors in it\n\n            Warning: Don't directly use initializeLocalActors() from here because that will break any current\n            cell transition that started in World::moveObject()\n        */\n        if (mwmp::Main::get().getCellController()->hasLocalAuthority(*getCell()))\n        {\n            mwmp::Main::get().getCellController()->getCell(*getCell())->shouldInitializeActors = true;\n        }\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    bool CellStore::movedHere(const MWWorld::Ptr& ptr) const\n    {\n        if (ptr.isEmpty())\n            return false;\n\n        if (mMovedHere.find(ptr.getBase()) != mMovedHere.end())\n            return true;\n\n        return false;\n    }\n\n    CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector<ESM::ESMReader>& readerList)\n        : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0), mRechargingItemsUpToDate(false)\n    {\n        mWaterLevel = cell->mWater;\n    }\n\n    const ESM::Cell *CellStore::getCell() const\n    {\n        return mCell;\n    }\n\n    CellStore::State CellStore::getState() const\n    {\n        return mState;\n    }\n\n    const std::vector<std::string> &CellStore::getPreloadedIds() const\n    {\n        return mIds;\n    }\n\n    bool CellStore::hasState() const\n    {\n        return mHasState;\n    }\n\n    bool CellStore::hasId (const std::string& id) const\n    {\n        if (mState==State_Unloaded)\n            return false;\n\n        if (mState==State_Preloaded)\n            return std::binary_search (mIds.begin(), mIds.end(), id);\n\n        return searchConst (id).isEmpty();\n    }\n\n    template <typename PtrType>\n    struct SearchVisitor\n    {\n        PtrType mFound;\n        const std::string *mIdToFind;\n        bool operator()(const PtrType& ptr)\n        {\n            if (*ptr.getCellRef().getRefIdPtr() == *mIdToFind)\n            {\n                mFound = ptr;\n                return false;\n            }\n            return true;\n        }\n    };\n\n    Ptr CellStore::search (const std::string& id)\n    {\n        SearchVisitor<MWWorld::Ptr> searchVisitor;\n        searchVisitor.mIdToFind = &id;\n        forEach(searchVisitor);\n        return searchVisitor.mFound;\n    }\n\n    ConstPtr CellStore::searchConst (const std::string& id) const\n    {\n        SearchVisitor<MWWorld::ConstPtr> searchVisitor;\n        searchVisitor.mIdToFind = &id;\n        forEachConst(searchVisitor);\n        return searchVisitor.mFound;\n    }\n\n    Ptr CellStore::searchViaActorId (int id)\n    {\n        if (Ptr ptr = ::searchViaActorId (mNpcs, id, this, mMovedToAnotherCell))\n            return ptr;\n\n        if (Ptr ptr = ::searchViaActorId (mCreatures, id, this, mMovedToAnotherCell))\n            return ptr;\n\n        for (const auto& [base, _] : mMovedHere)\n        {\n            MWWorld::Ptr actor (base, this);\n            if (!actor.getClass().isActor())\n                continue;\n            if (actor.getClass().getCreatureStats (actor).matchesActorId (id) && actor.getRefData().getCount() > 0)\n                return actor;\n        }\n\n        return Ptr();\n    }\n\n    class RefNumSearchVisitor\n    {\n        const ESM::RefNum& mRefNum;\n    public:\n        RefNumSearchVisitor(const ESM::RefNum& refNum) : mRefNum(refNum) {}\n\n        Ptr mFound;\n\n        bool operator()(const Ptr& ptr)\n        {\n            if (ptr.getCellRef().getRefNum() == mRefNum)\n            {\n                mFound = ptr;\n                return false;\n            }\n            return true;\n        }\n    };\n\n    Ptr CellStore::searchViaRefNum(const ESM::RefNum& refNum)\n    {\n        RefNumSearchVisitor searchVisitor(refNum);\n        forEach(searchVisitor);\n        return searchVisitor.mFound;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        A custom type of search visitor used to find objects by their reference numbers\n    */\n    class SearchExactVisitor\n    {\n        const unsigned int mRefNumToFind;\n        const unsigned int mMpNumToFind;\n        const std::string mRefIdToFind;\n        const bool mActorsOnly;\n    public:\n        SearchExactVisitor(const unsigned int refNum, const unsigned int mpNum, const std::string refId, const bool actorsOnly) :\n            mRefNumToFind(refNum), mMpNumToFind(mpNum), mRefIdToFind(refId), mActorsOnly(actorsOnly) {}\n\n        Ptr mFound;\n\n        bool operator()(const Ptr& ptr)\n        {\n            if (ptr.getCellRef().getRefNum().mIndex == mRefNumToFind && ptr.getCellRef().getMpNum() == mMpNumToFind)\n            {\n                if (!mActorsOnly || ptr.getClass().isActor())\n                {\n                    if (mRefIdToFind.empty() || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), mRefIdToFind))\n                    {\n                        mFound = ptr;\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n    };\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Allow the searching of objects by their reference numbers\n    */\n    Ptr CellStore::searchExact (const unsigned int refNum, const unsigned int mpNum, const std::string refId, bool actorsOnly)\n    {\n        // Ensure that all objects searched for have a valid reference number\n        if (refNum == 0 && mpNum == 0)\n            return 0;\n\n        SearchExactVisitor searchVisitor(refNum, mpNum, refId, actorsOnly);\n        forEach(searchVisitor);\n        return searchVisitor.mFound;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the mMergedRefs in the CellStore from elsewhere in the code\n    */\n    std::vector<LiveCellRefBase*> &CellStore::getMergedRefs()\n    {\n        return mMergedRefs;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the mNPCs in the CellStore from elsewhere in the code\n    */\n    CellRefList<ESM::NPC> *CellStore::getNpcs()\n    {\n        return &mNpcs;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the mCreatures in the CellStore from elsewhere in the code\n    */\n    CellRefList<ESM::Creature> *CellStore::getCreatures()\n    {\n        return &mCreatures;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the mCreatureLists in the CellStore from elsewhere in the code\n    */\n    CellRefList<ESM::CreatureLevList> *CellStore::getCreatureLists()\n    {\n        return &mCreatureLists;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the mContainers in the CellStore from elsewhere in the code\n    */\n    CellRefList<ESM::Container> *CellStore::getContainers()\n    {\n        return &mContainers;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    float CellStore::getWaterLevel() const\n    {\n        if (isExterior())\n            return -1;\n        return mWaterLevel;\n    }\n\n    void CellStore::setWaterLevel (float level)\n    {\n        mWaterLevel = level;\n        mHasState = true;\n    }\n\n    std::size_t CellStore::count() const\n    {\n        return mMergedRefs.size();\n    }\n\n    void CellStore::load ()\n    {\n        if (mState!=State_Loaded)\n        {\n            if (mState==State_Preloaded)\n                mIds.clear();\n\n            loadRefs ();\n\n            mState = State_Loaded;\n        }\n    }\n\n    void CellStore::preload ()\n    {\n        if (mState==State_Unloaded)\n        {\n            listRefs ();\n\n            mState = State_Preloaded;\n        }\n    }\n\n    void CellStore::listRefs()\n    {\n        std::vector<ESM::ESMReader>& esm = mReader;\n\n        assert (mCell);\n\n        if (mCell->mContextList.empty())\n            return; // this is a dynamically generated cell -> skipping.\n\n        // Load references from all plugins that do something with this cell.\n        for (size_t i = 0; i < mCell->mContextList.size(); i++)\n        {\n            try\n            {\n                // Reopen the ESM reader and seek to the right position.\n                int index = mCell->mContextList[i].index;\n                mCell->restore (esm[index], i);\n\n                ESM::CellRef ref;\n\n                // Get each reference in turn\n                bool deleted = false;\n                while (mCell->getNextRef (esm[index], ref, deleted))\n                {\n                    if (deleted)\n                        continue;\n\n                    // Don't list reference if it was moved to a different cell.\n                    ESM::MovedCellRefTracker::const_iterator iter =\n                        std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum);\n                    if (iter != mCell->mMovedRefs.end()) {\n                        continue;\n                    }\n\n                    Misc::StringUtils::lowerCaseInPlace(ref.mRefID);\n                    mIds.push_back(std::move(ref.mRefID));\n                }\n            }\n            catch (std::exception& e)\n            {\n                Log(Debug::Error) << \"An error occurred listing references for cell \" << getCell()->getDescription() << \": \" << e.what();\n            }\n        }\n\n        // List moved references, from separately tracked list.\n        for (const auto& [ref, deleted]: mCell->mLeasedRefs)\n        {\n            if (!deleted)\n                mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID));\n        }\n\n        std::sort (mIds.begin(), mIds.end());\n    }\n\n    void CellStore::loadRefs()\n    {\n        std::vector<ESM::ESMReader>& esm = mReader;\n\n        assert (mCell);\n\n        if (mCell->mContextList.empty())\n            return; // this is a dynamically generated cell -> skipping.\n\n        std::map<ESM::RefNum, std::string> refNumToID; // used to detect refID modifications\n\n        // Load references from all plugins that do something with this cell.\n        for (size_t i = 0; i < mCell->mContextList.size(); i++)\n        {\n            try\n            {\n                // Reopen the ESM reader and seek to the right position.\n                int index = mCell->mContextList[i].index;\n                mCell->restore (esm[index], i);\n\n                ESM::CellRef ref;\n                ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile;\n\n                // Get each reference in turn\n                bool deleted = false;\n                while(mCell->getNextRef(esm[index], ref, deleted))\n                {\n                    // Don't load reference if it was moved to a different cell.\n                    ESM::MovedCellRefTracker::const_iterator iter =\n                        std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum);\n                    if (iter != mCell->mMovedRefs.end()) {\n                        continue;\n                    }\n\n                    loadRef (ref, deleted, refNumToID);\n                }\n            }\n            catch (std::exception& e)\n            {\n                Log(Debug::Error) << \"An error occurred loading references for cell \" << getCell()->getDescription() << \": \" << e.what();\n            }\n        }\n\n        // Load moved references, from separately tracked list.\n        for (const auto& leasedRef : mCell->mLeasedRefs)\n        {\n            ESM::CellRef &ref = const_cast<ESM::CellRef&>(leasedRef.first);\n            bool deleted = leasedRef.second;\n\n            loadRef (ref, deleted, refNumToID);\n        }\n\n        updateMergedRefs();\n    }\n\n    bool CellStore::isExterior() const\n    {\n        return mCell->isExterior();\n    }\n\n    Ptr CellStore::searchInContainer (const std::string& id)\n    {\n        bool oldState = mHasState;\n\n        mHasState = true;\n\n        if (Ptr ptr = searchInContainerList (mContainers, id))\n            return ptr;\n\n        if (Ptr ptr = searchInContainerList (mCreatures, id))\n            return ptr;\n\n        if (Ptr ptr = searchInContainerList (mNpcs, id))\n            return ptr;\n\n        mHasState = oldState;\n\n        return Ptr();\n    }\n\n    void CellStore::loadRef (ESM::CellRef& ref, bool deleted, std::map<ESM::RefNum, std::string>& refNumToID)\n    {\n        Misc::StringUtils::lowerCaseInPlace (ref.mRefID);\n\n        const MWWorld::ESMStore& store = mStore;\n\n        std::map<ESM::RefNum, std::string>::iterator it = refNumToID.find(ref.mRefNum);\n        if (it != refNumToID.end())\n        {\n            if (it->second != ref.mRefID)\n            {\n                // refID was modified, make sure we don't end up with duplicated refs\n                switch (store.find(it->second))\n                {\n                    case ESM::REC_ACTI: mActivators.remove(ref.mRefNum); break;\n                    case ESM::REC_ALCH: mPotions.remove(ref.mRefNum); break;\n                    case ESM::REC_APPA: mAppas.remove(ref.mRefNum); break;\n                    case ESM::REC_ARMO: mArmors.remove(ref.mRefNum); break;\n                    case ESM::REC_BOOK: mBooks.remove(ref.mRefNum); break;\n                    case ESM::REC_CLOT: mClothes.remove(ref.mRefNum); break;\n                    case ESM::REC_CONT: mContainers.remove(ref.mRefNum); break;\n                    case ESM::REC_CREA: mCreatures.remove(ref.mRefNum); break;\n                    case ESM::REC_DOOR: mDoors.remove(ref.mRefNum); break;\n                    case ESM::REC_INGR: mIngreds.remove(ref.mRefNum); break;\n                    case ESM::REC_LEVC: mCreatureLists.remove(ref.mRefNum); break;\n                    case ESM::REC_LEVI: mItemLists.remove(ref.mRefNum); break;\n                    case ESM::REC_LIGH: mLights.remove(ref.mRefNum); break;\n                    case ESM::REC_LOCK: mLockpicks.remove(ref.mRefNum); break;\n                    case ESM::REC_MISC: mMiscItems.remove(ref.mRefNum); break;\n                    case ESM::REC_NPC_: mNpcs.remove(ref.mRefNum); break;\n                    case ESM::REC_PROB: mProbes.remove(ref.mRefNum); break;\n                    case ESM::REC_REPA: mRepairs.remove(ref.mRefNum); break;\n                    case ESM::REC_STAT: mStatics.remove(ref.mRefNum); break;\n                    case ESM::REC_WEAP: mWeapons.remove(ref.mRefNum); break;\n                    case ESM::REC_BODY: mBodyParts.remove(ref.mRefNum); break;\n                    default:\n                        break;\n                }\n            }\n        }\n\n        switch (store.find (ref.mRefID))\n        {\n            case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break;\n            case ESM::REC_ALCH: mPotions.load(ref, deleted,store); break;\n            case ESM::REC_APPA: mAppas.load(ref, deleted, store); break;\n            case ESM::REC_ARMO: mArmors.load(ref, deleted, store); break;\n            case ESM::REC_BOOK: mBooks.load(ref, deleted, store); break;\n            case ESM::REC_CLOT: mClothes.load(ref, deleted, store); break;\n            case ESM::REC_CONT: mContainers.load(ref, deleted, store); break;\n            case ESM::REC_CREA: mCreatures.load(ref, deleted, store); break;\n            case ESM::REC_DOOR: mDoors.load(ref, deleted, store); break;\n            case ESM::REC_INGR: mIngreds.load(ref, deleted, store); break;\n            case ESM::REC_LEVC: mCreatureLists.load(ref, deleted, store); break;\n            case ESM::REC_LEVI: mItemLists.load(ref, deleted, store); break;\n            case ESM::REC_LIGH: mLights.load(ref, deleted, store); break;\n            case ESM::REC_LOCK: mLockpicks.load(ref, deleted, store); break;\n            case ESM::REC_MISC: mMiscItems.load(ref, deleted, store); break;\n            case ESM::REC_NPC_: mNpcs.load(ref, deleted, store); break;\n            case ESM::REC_PROB: mProbes.load(ref, deleted, store); break;\n            case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break;\n            case ESM::REC_STAT:\n            {\n                if (ref.mRefNum.fromGroundcoverFile()) return;\n                mStatics.load(ref, deleted, store); break;\n            }\n            case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break;\n            case ESM::REC_BODY: mBodyParts.load(ref, deleted, store); break;\n\n            case 0: Log(Debug::Error) << \"Cell reference '\" + ref.mRefID + \"' not found!\"; return;\n\n            default:\n                Log(Debug::Error) << \"Error: Ignoring reference '\" << ref.mRefID << \"' of unhandled type\";\n                return;\n        }\n\n        refNumToID[ref.mRefNum] = ref.mRefID;\n    }\n\n    void CellStore::loadState (const ESM::CellState& state)\n    {\n        mHasState = true;\n\n        if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater)\n            mWaterLevel = state.mWaterLevel;\n\n        mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn);\n    }\n\n    void CellStore::saveState (ESM::CellState& state) const\n    {\n        state.mId = mCell->getCellId();\n\n        if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater)\n            state.mWaterLevel = mWaterLevel;\n\n        state.mHasFogOfWar = (mFogState.get() ? 1 : 0);\n        state.mLastRespawn = mLastRespawn.toEsm();\n    }\n\n    void CellStore::writeFog(ESM::ESMWriter &writer) const\n    {\n        if (mFogState.get())\n        {\n            mFogState->save(writer, mCell->mData.mFlags & ESM::Cell::Interior);\n        }\n    }\n\n    void CellStore::readFog(ESM::ESMReader &reader)\n    {\n        mFogState.reset(new ESM::FogState());\n        mFogState->load(reader);\n    }\n\n    void CellStore::writeReferences (ESM::ESMWriter& writer) const\n    {\n        writeReferenceCollection<ESM::ObjectState> (writer, mActivators);\n        writeReferenceCollection<ESM::ObjectState> (writer, mPotions);\n        writeReferenceCollection<ESM::ObjectState> (writer, mAppas);\n        writeReferenceCollection<ESM::ObjectState> (writer, mArmors);\n        writeReferenceCollection<ESM::ObjectState> (writer, mBooks);\n        writeReferenceCollection<ESM::ObjectState> (writer, mClothes);\n        writeReferenceCollection<ESM::ContainerState> (writer, mContainers);\n        writeReferenceCollection<ESM::CreatureState> (writer, mCreatures);\n        writeReferenceCollection<ESM::DoorState> (writer, mDoors);\n        writeReferenceCollection<ESM::ObjectState> (writer, mIngreds);\n        writeReferenceCollection<ESM::CreatureLevListState> (writer, mCreatureLists);\n        writeReferenceCollection<ESM::ObjectState> (writer, mItemLists);\n        writeReferenceCollection<ESM::ObjectState> (writer, mLights);\n        writeReferenceCollection<ESM::ObjectState> (writer, mLockpicks);\n        writeReferenceCollection<ESM::ObjectState> (writer, mMiscItems);\n        writeReferenceCollection<ESM::NpcState> (writer, mNpcs);\n        writeReferenceCollection<ESM::ObjectState> (writer, mProbes);\n        writeReferenceCollection<ESM::ObjectState> (writer, mRepairs);\n        writeReferenceCollection<ESM::ObjectState> (writer, mStatics);\n        writeReferenceCollection<ESM::ObjectState> (writer, mWeapons);\n        writeReferenceCollection<ESM::ObjectState> (writer, mBodyParts);\n\n        for (const auto& [base, store] : mMovedToAnotherCell)\n        {\n            ESM::RefNum refNum = base->mRef.getRefNum();\n            ESM::CellId movedTo = store->getCell()->getCellId();\n\n            refNum.save(writer, true, \"MVRF\");\n            movedTo.save(writer);\n        }\n    }\n\n    void CellStore::readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback)\n    {\n        mHasState = true;\n\n        while (reader.isNextSub (\"OBJE\"))\n        {\n            unsigned int unused;\n            reader.getHT (unused);\n\n            // load the RefID first so we know what type of object it is\n            ESM::CellRef cref;\n            cref.loadId(reader, true);\n\n            int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID);\n            if (type == 0)\n            {\n                Log(Debug::Warning) << \"Dropping reference to '\" << cref.mRefID << \"' (object no longer exists)\";\n                reader.skipHSubUntil(\"OBJE\");\n                continue;\n            }\n\n            switch (type)\n            {\n                case ESM::REC_ACTI:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_ALCH:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_APPA:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_ARMO:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_BOOK:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_CLOT:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_CONT:\n\n                    readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_CREA:\n\n                    readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_DOOR:\n\n                    readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_INGR:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_LEVC:\n\n                    readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_LEVI:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_LIGH:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_LOCK:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_MISC:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_NPC_:\n\n                    readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_PROB:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_REPA:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_STAT:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_WEAP:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap, this);\n                    break;\n\n                case ESM::REC_BODY:\n\n                    readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap, this);\n                    break;\n\n                default:\n\n                    throw std::runtime_error (\"unknown type in cell reference section\");\n            }\n        }\n\n        // Do another update here to make sure objects referred to by MVRF tags can be found\n        // This update is only needed for old saves that used the old copy&delete way of moving objects\n        updateMergedRefs();\n\n        while (reader.isNextSub(\"MVRF\"))\n        {\n            reader.cacheSubName();\n            ESM::RefNum refnum;\n            ESM::CellId movedTo;\n            refnum.load(reader, true, \"MVRF\");\n            movedTo.load(reader);\n\n            if (refnum.hasContentFile())\n            {\n                auto iter = contentFileMap.find(refnum.mContentFile);\n                if (iter != contentFileMap.end())\n                    refnum.mContentFile = iter->second;\n            }\n\n            // Search for the reference. It might no longer exist if its content file was removed.\n            Ptr movedRef = searchViaRefNum(refnum);\n            if (movedRef.isEmpty())\n            {\n                Log(Debug::Warning) << \"Warning: Dropping moved ref tag for \" << refnum.mIndex << \" (moved object no longer exists)\";\n                continue;\n            }\n\n            CellStore* otherCell = callback->getCellStore(movedTo);\n\n            if (otherCell == nullptr)\n            {\n                Log(Debug::Warning) << \"Warning: Dropping moved ref tag for \" << movedRef.getCellRef().getRefId()\n                                    << \" (target cell \" << movedTo.mWorldspace << \" no longer exists). Reference moved back to its original location.\";\n                // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates.\n                // Restore original coordinates:\n                movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition());\n                continue;\n            }\n\n            if (otherCell == this)\n            {\n                // Should never happen unless someone's tampering with files.\n                Log(Debug::Warning) << \"Found invalid moved ref, ignoring\";\n                continue;\n            }\n\n            moveTo(movedRef, otherCell);\n        }\n    }\n\n    bool operator== (const CellStore& left, const CellStore& right)\n    {\n        return left.getCell()->getCellId()==right.getCell()->getCellId();\n    }\n\n    bool operator!= (const CellStore& left, const CellStore& right)\n    {\n        return !(left==right);\n    }\n\n    void CellStore::setFog(ESM::FogState *fog)\n    {\n        mFogState.reset(fog);\n    }\n\n    ESM::FogState* CellStore::getFog() const\n    {\n        return mFogState.get();\n    }\n\n    void clearCorpse(const MWWorld::Ptr& ptr)\n    {\n        const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);\n        static const float fCorpseClearDelay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"fCorpseClearDelay\")->mValue.getFloat();\n        if (creatureStats.isDead() &&\n            creatureStats.isDeathAnimationFinished() &&\n            !ptr.getClass().isPersistent(ptr) &&\n            creatureStats.getTimeOfDeath() + fCorpseClearDelay <= MWBase::Environment::get().getWorld()->getTimeStamp())\n        {\n            MWBase::Environment::get().getWorld()->deleteObject(ptr);\n        }\n    }\n\n    void CellStore::rest(double hours)\n    {\n        if (mState == State_Loaded)\n        {\n            for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)\n            {\n                Ptr ptr = getCurrentPtr(&*it);\n                if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)\n                {\n                    MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true);\n                }\n            }\n            for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)\n            {\n                Ptr ptr = getCurrentPtr(&*it);\n                if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)\n                {\n                    MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true);\n                }\n            }\n        }\n    }\n\n    void CellStore::recharge(float duration)\n    {\n        if (duration <= 0)\n            return;\n\n        if (mState == State_Loaded)\n        {\n            for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)\n            {\n                Ptr ptr = getCurrentPtr(&*it);\n                if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)\n                {\n                    ptr.getClass().getContainerStore(ptr).rechargeItems(duration);\n                }\n            }\n            for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)\n            {\n                Ptr ptr = getCurrentPtr(&*it);\n                if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)\n                {\n                    ptr.getClass().getContainerStore(ptr).rechargeItems(duration);\n                }\n            }\n            for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)\n            {\n                Ptr ptr = getCurrentPtr(&*it);\n                if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0\n                && ptr.getClass().getContainerStore(ptr).isResolved())\n                {\n                    ptr.getClass().getContainerStore(ptr).rechargeItems(duration);\n                }\n            }\n\n            rechargeItems(duration);\n        }\n    }\n\n    void CellStore::respawn()\n    {\n        if (mState == State_Loaded)\n        {\n            static const int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(\"iMonthsToRespawn\")->mValue.getInteger();\n            if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn)\n            {\n                mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp();\n                for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)\n                {\n                    Ptr ptr = getCurrentPtr(&*it);\n                    ptr.getClass().respawn(ptr);\n                }\n            }\n\n            for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)\n            {\n                Ptr ptr = getCurrentPtr(&*it);\n                clearCorpse(ptr);\n                ptr.getClass().respawn(ptr);\n            }\n            for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)\n            {\n                Ptr ptr = getCurrentPtr(&*it);\n                clearCorpse(ptr);\n                ptr.getClass().respawn(ptr);\n            }\n            for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it)\n            {\n                Ptr ptr = getCurrentPtr(&*it);\n                // no need to clearCorpse, handled as part of mCreatures\n                ptr.getClass().respawn(ptr);\n            }\n        }\n    }\n\n    void MWWorld::CellStore::rechargeItems(float duration)\n    {\n        if (!mRechargingItemsUpToDate)\n        {\n            updateRechargingItems();\n            mRechargingItemsUpToDate = true;\n        }\n        for (const auto& [item, charge] : mRechargingItems)\n        {\n            MWMechanics::rechargeItem(item, charge, duration);\n        }\n    }\n\n    void MWWorld::CellStore::updateRechargingItems()\n    {\n        mRechargingItems.clear();\n\n        const auto update = [this](auto& list)\n        {\n            for (auto & item : list)\n            {\n                Ptr ptr = getCurrentPtr(&item);\n                if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)\n                {\n                    checkItem(ptr);\n                }\n            }\n        };\n\n        update(mWeapons.mList);\n        update(mArmors.mList);\n        update(mClothes.mList);\n        update(mBooks.mList);\n    }\n\n    void MWWorld::CellStore::checkItem(Ptr ptr)\n    {\n        if (ptr.getClass().getEnchantment(ptr).empty())\n            return;\n\n        std::string enchantmentId = ptr.getClass().getEnchantment(ptr);\n        const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId);\n        if (!enchantment)\n        {\n            Log(Debug::Warning) << \"Warning: Can't find enchantment '\" << enchantmentId << \"' on item \" << ptr.getCellRef().getRefId();\n            return;\n        }\n\n        if (enchantment->mData.mType == ESM::Enchantment::WhenUsed\n                || enchantment->mData.mType == ESM::Enchantment::WhenStrikes)\n            mRechargingItems.emplace_back(ptr.getBase(), static_cast<float>(enchantment->mData.mCharge));\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/cellstore.hpp",
    "content": "#ifndef GAME_MWWORLD_CELLSTORE_H\n#define GAME_MWWORLD_CELLSTORE_H\n\n#include <algorithm>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <map>\n#include <memory>\n\n#include \"livecellref.hpp\"\n#include \"cellreflist.hpp\"\n\n#include <components/esm/loadacti.hpp>\n#include <components/esm/loadalch.hpp>\n#include <components/esm/loadappa.hpp>\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadbook.hpp>\n#include <components/esm/loadclot.hpp>\n#include <components/esm/loadcont.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/loaddoor.hpp>\n#include <components/esm/loadingr.hpp>\n#include <components/esm/loadlevlist.hpp>\n#include <components/esm/loadligh.hpp>\n#include <components/esm/loadlock.hpp>\n#include <components/esm/loadprob.hpp>\n#include <components/esm/loadrepa.hpp>\n#include <components/esm/loadstat.hpp>\n#include <components/esm/loadweap.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/loadmisc.hpp>\n#include <components/esm/loadbody.hpp>\n\n#include \"timestamp.hpp\"\n#include \"ptr.hpp\"\n\nnamespace ESM\n{\n    struct Cell;\n    struct CellState;\n    struct FogState;\n    struct CellId;\n    struct RefNum;\n}\n\nnamespace MWWorld\n{\n    class ESMStore;\n\n    /// \\brief Mutable state of a cell\n    class CellStore\n    {\n        public:\n\n            enum State\n            {\n                State_Unloaded, State_Preloaded, State_Loaded\n            };\n\n        private:\n\n            const MWWorld::ESMStore& mStore;\n            std::vector<ESM::ESMReader>& mReader;\n\n            // Even though fog actually belongs to the player and not cells,\n            // it makes sense to store it here since we need it once for each cell.\n            // Note this is nullptr until the cell is explored to save some memory\n            std::shared_ptr<ESM::FogState> mFogState;\n\n            const ESM::Cell *mCell;\n            State mState;\n            bool mHasState;\n            std::vector<std::string> mIds;\n            float mWaterLevel;\n\n            MWWorld::TimeStamp mLastRespawn;\n\n            // List of refs owned by this cell\n            CellRefList<ESM::Activator>         mActivators;\n            CellRefList<ESM::Potion>            mPotions;\n            CellRefList<ESM::Apparatus>         mAppas;\n            CellRefList<ESM::Armor>             mArmors;\n            CellRefList<ESM::Book>              mBooks;\n            CellRefList<ESM::Clothing>          mClothes;\n            CellRefList<ESM::Container>         mContainers;\n            CellRefList<ESM::Creature>          mCreatures;\n            CellRefList<ESM::Door>              mDoors;\n            CellRefList<ESM::Ingredient>        mIngreds;\n            CellRefList<ESM::CreatureLevList>   mCreatureLists;\n            CellRefList<ESM::ItemLevList>       mItemLists;\n            CellRefList<ESM::Light>             mLights;\n            CellRefList<ESM::Lockpick>          mLockpicks;\n            CellRefList<ESM::Miscellaneous>     mMiscItems;\n            CellRefList<ESM::NPC>               mNpcs;\n            CellRefList<ESM::Probe>             mProbes;\n            CellRefList<ESM::Repair>            mRepairs;\n            CellRefList<ESM::Static>            mStatics;\n            CellRefList<ESM::Weapon>            mWeapons;\n            CellRefList<ESM::BodyPart>          mBodyParts;\n\n            typedef std::map<LiveCellRefBase*, MWWorld::CellStore*> MovedRefTracker;\n            // References owned by a different cell that have been moved here.\n            // <reference, cell the reference originally came from>\n            MovedRefTracker mMovedHere;\n            // References owned by this cell that have been moved to another cell.\n            // <reference, cell the reference was moved to>\n            MovedRefTracker mMovedToAnotherCell;\n\n            // Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell\n            std::vector<LiveCellRefBase*> mMergedRefs;\n\n            // Get the Ptr for the given ref which originated from this cell (possibly moved to another cell at this point).\n            Ptr getCurrentPtr(MWWorld::LiveCellRefBase* ref);\n\n            /// Moves object from the given cell to this cell.\n            void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from);\n\n            /// Repopulate mMergedRefs.\n            void updateMergedRefs();\n\n            // (item, max charge)\n            typedef std::vector<std::pair<LiveCellRefBase*, float> > TRechargingItems;\n            TRechargingItems mRechargingItems;\n\n            bool mRechargingItemsUpToDate;\n\n            void updateRechargingItems();\n            void rechargeItems(float duration);\n            void checkItem(Ptr ptr);\n\n            // helper function for forEachInternal\n            template<class Visitor, class List>\n            bool forEachImp (Visitor& visitor, List& list)\n            {\n                for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end();\n                    ++iter)\n                {\n                    if (!isAccessible(iter->mData, iter->mRef))\n                        continue;\n                    if (!visitor (MWWorld::Ptr(&*iter, this)))\n                        return false;\n                }\n                return true;\n            }\n\n            // listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved objects are accounted for.\n            template<class Visitor>\n            bool forEachInternal (Visitor& visitor)\n            {\n                return\n                    forEachImp (visitor, mActivators) &&\n                    forEachImp (visitor, mPotions) &&\n                    forEachImp (visitor, mAppas) &&\n                    forEachImp (visitor, mArmors) &&\n                    forEachImp (visitor, mBooks) &&\n                    forEachImp (visitor, mClothes) &&\n                    forEachImp (visitor, mContainers) &&\n                    forEachImp (visitor, mDoors) &&\n                    forEachImp (visitor, mIngreds) &&\n                    forEachImp (visitor, mItemLists) &&\n                    forEachImp (visitor, mLights) &&\n                    forEachImp (visitor, mLockpicks) &&\n                    forEachImp (visitor, mMiscItems) &&\n                    forEachImp (visitor, mProbes) &&\n                    forEachImp (visitor, mRepairs) &&\n                    forEachImp (visitor, mStatics) &&\n                    forEachImp (visitor, mWeapons) &&\n                    forEachImp (visitor, mBodyParts) &&\n                    forEachImp (visitor, mCreatures) &&\n                    forEachImp (visitor, mNpcs) &&\n                    forEachImp (visitor, mCreatureLists);\n            }\n\n            /// @note If you get a linker error here, this means the given type can not be stored in a cell. The supported types are\n            /// defined at the bottom of this file.\n            template <class T>\n            CellRefList<T>& get();\n\n        public:\n\n            /// Should this reference be accessible to the outside world (i.e. to scripts / game logic)?\n            /// Determined based on the deletion flags. By default, objects deleted by content files are never accessible;\n            /// objects deleted by setCount(0) are still accessible *if* they came from a content file (needed for vanilla\n            /// scripting compatibility, and the fact that objects may be \"un-deleted\" in the original game).\n            static bool isAccessible(const MWWorld::RefData& refdata, const MWWorld::CellRef& cref)\n            {\n                return !refdata.isDeletedByContentFile() && (cref.hasContentFile() || refdata.getCount() > 0);\n            }\n\n            /// Moves object from this cell to the given cell.\n            /// @note automatically updates given cell by calling cellToMoveTo->moveFrom(...)\n            /// @note throws exception if cellToMoveTo == this\n            /// @return updated MWWorld::Ptr with the new CellStore pointer set.\n            MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo);\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to clear the moves to other cells tracked for objects, allowing for\n                on-the-fly cell resets that don't cause crashes\n            */\n            void clearMovesToCells();\n            /*\n                End of tes3mp addition\n            */\n\n            void rest(double hours);\n            void recharge(float duration);\n\n            /// Make a copy of the given object and insert it into this cell.\n            /// @note If you get a linker error here, this means the given type can not be inserted into a cell.\n            /// The supported types are defined at the bottom of this file.\n            template <typename T>\n            LiveCellRefBase* insert(const LiveCellRef<T>* ref)\n            {\n                mHasState = true;\n                CellRefList<T>& list = get<T>();\n                LiveCellRefBase* ret = &list.insert(*ref);\n                updateMergedRefs();\n                return ret;\n            }\n\n            /// @param readerList The readers to use for loading of the cell on-demand.\n            CellStore (const ESM::Cell *cell_,\n                       const MWWorld::ESMStore& store,\n                       std::vector<ESM::ESMReader>& readerList);\n\n            const ESM::Cell *getCell() const;\n\n            State getState() const;\n\n            const std::vector<std::string>& getPreloadedIds() const;\n            ///< Get Ids of objects in this cell, only valid in State_Preloaded\n\n            bool hasState() const;\n            ///< Does this cell have state that needs to be stored in a saved game file?\n\n            bool hasId (const std::string& id) const;\n            ///< May return true for deleted IDs when in preload state. Will return false, if cell is\n            /// unloaded.\n            /// @note Will not account for moved references which may exist in Loaded state. Use search() instead if the cell is loaded.\n\n            Ptr search (const std::string& id);\n            ///< Will return an empty Ptr if cell is not loaded. Does not check references in\n            /// containers.\n            /// @note Triggers CellStore hasState flag.\n\n            ConstPtr searchConst (const std::string& id) const;\n            ///< Will return an empty Ptr if cell is not loaded. Does not check references in\n            /// containers.\n            /// @note Does not trigger CellStore hasState flag.\n\n            Ptr searchViaActorId (int id);\n            ///< Will return an empty Ptr if cell is not loaded.\n\n            Ptr searchViaRefNum (const ESM::RefNum& refNum);\n            ///< Will return an empty Ptr if cell is not loaded. Does not check references in\n            /// containers.\n            /// @note Triggers CellStore hasState flag.\n\n            /*\n                Start of tes3mp addition\n\n                Allow the searching of objects by their reference numbers and, optionally,\n                their refIds\n            */\n            Ptr searchExact (unsigned int refNum, unsigned int mpNum, std::string refId = \"\", bool actorsOnly = false);\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the mMergedRefs in the CellStore from elsewhere in the code\n            */\n            std::vector<LiveCellRefBase*> &getMergedRefs();\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the mNPCs in the CellStore from elsewhere in the code\n            */\n            CellRefList<ESM::NPC> *getNpcs();\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the mCreatures in the CellStore from elsewhere in the code\n            */\n            CellRefList<ESM::Creature> *getCreatures();\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the mCreatureLists in the CellStore from elsewhere in the code\n            */\n            CellRefList<ESM::CreatureLevList> *getCreatureLists();\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the mContainers in the CellStore from elsewhere in the code\n            */\n            CellRefList<ESM::Container> *getContainers();\n            /*\n                End of tes3mp addition\n            */\n\n            float getWaterLevel() const;\n\n            bool movedHere(const MWWorld::Ptr& ptr) const;\n\n            void setWaterLevel (float level);\n\n            void setFog (ESM::FogState* fog);\n            ///< \\note Takes ownership of the pointer\n\n            ESM::FogState* getFog () const;\n\n            std::size_t count() const;\n            ///< Return total number of references, including deleted ones.\n\n            void load ();\n            ///< Load references from content file.\n\n            void preload ();\n            ///< Build ID list from content file.\n\n            /// Call visitor (MWWorld::Ptr) for each reference. visitor must return a bool. Returning\n            /// false will abort the iteration.\n            /// \\note Prefer using forEachConst when possible.\n            /// \\note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in unintended behaviour.\n            /// \\attention This function also lists deleted (count 0) objects!\n            /// \\return Iteration completed?\n            template<class Visitor>\n            bool forEach (Visitor&& visitor)\n            {\n                if (mState != State_Loaded)\n                    return false;\n\n                if (mMergedRefs.empty())\n                    return true;\n\n                mHasState = true;\n\n                for (unsigned int i=0; i<mMergedRefs.size(); ++i)\n                {\n                    if (!isAccessible(mMergedRefs[i]->mData, mMergedRefs[i]->mRef))\n                        continue;\n\n                    if (!visitor(MWWorld::Ptr(mMergedRefs[i], this)))\n                        return false;\n                }\n                return true;\n            }\n\n            /// Call visitor (MWWorld::ConstPtr) for each reference. visitor must return a bool. Returning\n            /// false will abort the iteration.\n            /// \\note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in unintended behaviour.\n            /// \\attention This function also lists deleted (count 0) objects!\n            /// \\return Iteration completed?\n            template<class Visitor>\n            bool forEachConst (Visitor&& visitor) const\n            {\n                if (mState != State_Loaded)\n                    return false;\n\n                for (unsigned int i=0; i<mMergedRefs.size(); ++i)\n                {\n                    if (!isAccessible(mMergedRefs[i]->mData, mMergedRefs[i]->mRef))\n                        continue;\n\n                    if (!visitor(MWWorld::ConstPtr(mMergedRefs[i], this)))\n                        return false;\n                }\n                return true;\n            }\n\n\n            /// Call visitor (ref) for each reference of given type. visitor must return a bool. Returning\n            /// false will abort the iteration.\n            /// \\note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in unintended behaviour.\n            /// \\attention This function also lists deleted (count 0) objects!\n            /// \\return Iteration completed?\n            template <class T, class Visitor>\n            bool forEachType(Visitor& visitor)\n            {\n                if (mState != State_Loaded)\n                    return false;\n\n                if (mMergedRefs.empty())\n                    return true;\n\n                mHasState = true;\n\n                CellRefList<T>& list = get<T>();\n\n                for (typename CellRefList<T>::List::iterator it (list.mList.begin()); it!=list.mList.end(); ++it)\n                {\n                    LiveCellRefBase* base = &*it;\n                    if (mMovedToAnotherCell.find(base) != mMovedToAnotherCell.end())\n                        continue;\n                    if (!isAccessible(base->mData, base->mRef))\n                        continue;\n                    if (!visitor(MWWorld::Ptr(base, this)))\n                        return false;\n                }\n\n                for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it)\n                {\n                    LiveCellRefBase* base = it->first;\n                    if (dynamic_cast<LiveCellRef<T>*>(base))\n                        if (!visitor(MWWorld::Ptr(base, this)))\n                            return false;\n                }\n                return true;\n            }\n\n            // NOTE: does not account for moved references\n            // Should be phased out when we have const version of forEach\n            inline const CellRefList<ESM::Door>& getReadOnlyDoors() const\n            {\n                return mDoors;\n            }\n            inline const CellRefList<ESM::Static>& getReadOnlyStatics() const\n            {\n                return mStatics;\n            }\n\n            bool isExterior() const;\n\n            Ptr searchInContainer (const std::string& id);\n\n            void loadState (const ESM::CellState& state);\n\n            void saveState (ESM::CellState& state) const;\n\n            void writeFog (ESM::ESMWriter& writer) const;\n\n            void readFog (ESM::ESMReader& reader);\n\n            void writeReferences (ESM::ESMWriter& writer) const;\n\n            struct GetCellStoreCallback\n            {\n            public:\n                ///@note must return nullptr if the cell is not found\n                virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0;\n                virtual ~GetCellStoreCallback() = default;\n            };\n\n            /// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved references)\n            void readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback);\n\n            void respawn ();\n            ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded.\n\n        private:\n\n            /// Run through references and store IDs\n            void listRefs();\n\n            void loadRefs();\n\n            void loadRef (ESM::CellRef& ref, bool deleted, std::map<ESM::RefNum, std::string>& refNumToID);\n            ///< Make case-adjustments to \\a ref and insert it into the respective container.\n            ///\n            /// Invalid \\a ref objects are silently dropped.\n    };\n\n    template<>\n    inline CellRefList<ESM::Activator>& CellStore::get<ESM::Activator>()\n    {\n        mHasState = true;\n        return mActivators;\n    }\n\n    template<>\n    inline CellRefList<ESM::Potion>& CellStore::get<ESM::Potion>()\n    {\n        mHasState = true;\n        return mPotions;\n    }\n\n    template<>\n    inline CellRefList<ESM::Apparatus>& CellStore::get<ESM::Apparatus>()\n    {\n        mHasState = true;\n        return mAppas;\n    }\n\n    template<>\n    inline CellRefList<ESM::Armor>& CellStore::get<ESM::Armor>()\n    {\n        mHasState = true;\n        return mArmors;\n    }\n\n    template<>\n    inline CellRefList<ESM::Book>& CellStore::get<ESM::Book>()\n    {\n        mHasState = true;\n        return mBooks;\n    }\n\n    template<>\n    inline CellRefList<ESM::Clothing>& CellStore::get<ESM::Clothing>()\n    {\n        mHasState = true;\n        return mClothes;\n    }\n\n    template<>\n    inline CellRefList<ESM::Container>& CellStore::get<ESM::Container>()\n    {\n        mHasState = true;\n        return mContainers;\n    }\n\n    template<>\n    inline CellRefList<ESM::Creature>& CellStore::get<ESM::Creature>()\n    {\n        mHasState = true;\n        return mCreatures;\n    }\n\n    template<>\n    inline CellRefList<ESM::Door>& CellStore::get<ESM::Door>()\n    {\n        mHasState = true;\n        return mDoors;\n    }\n\n    template<>\n    inline CellRefList<ESM::Ingredient>& CellStore::get<ESM::Ingredient>()\n    {\n        mHasState = true;\n        return mIngreds;\n    }\n\n    template<>\n    inline CellRefList<ESM::CreatureLevList>& CellStore::get<ESM::CreatureLevList>()\n    {\n        mHasState = true;\n        return mCreatureLists;\n    }\n\n    template<>\n    inline CellRefList<ESM::ItemLevList>& CellStore::get<ESM::ItemLevList>()\n    {\n        mHasState = true;\n        return mItemLists;\n    }\n\n    template<>\n    inline CellRefList<ESM::Light>& CellStore::get<ESM::Light>()\n    {\n        mHasState = true;\n        return mLights;\n    }\n\n    template<>\n    inline CellRefList<ESM::Lockpick>& CellStore::get<ESM::Lockpick>()\n    {\n        mHasState = true;\n        return mLockpicks;\n    }\n\n    template<>\n    inline CellRefList<ESM::Miscellaneous>& CellStore::get<ESM::Miscellaneous>()\n    {\n        mHasState = true;\n        return mMiscItems;\n    }\n\n    template<>\n    inline CellRefList<ESM::NPC>& CellStore::get<ESM::NPC>()\n    {\n        mHasState = true;\n        return mNpcs;\n    }\n\n    template<>\n    inline CellRefList<ESM::Probe>& CellStore::get<ESM::Probe>()\n    {\n        mHasState = true;\n        return mProbes;\n    }\n\n    template<>\n    inline CellRefList<ESM::Repair>& CellStore::get<ESM::Repair>()\n    {\n        mHasState = true;\n        return mRepairs;\n    }\n\n    template<>\n    inline CellRefList<ESM::Static>& CellStore::get<ESM::Static>()\n    {\n        mHasState = true;\n        return mStatics;\n    }\n\n    template<>\n    inline CellRefList<ESM::Weapon>& CellStore::get<ESM::Weapon>()\n    {\n        mHasState = true;\n        return mWeapons;\n    }\n\n    template<>\n    inline CellRefList<ESM::BodyPart>& CellStore::get<ESM::BodyPart>()\n    {\n        mHasState = true;\n        return mBodyParts;\n    }\n\n    bool operator== (const CellStore& left, const CellStore& right);\n    bool operator!= (const CellStore& left, const CellStore& right);\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/cellvisitors.hpp",
    "content": "#ifndef GAME_MWWORLD_CELLVISITORS_H\n#define GAME_MWWORLD_CELLVISITORS_H\n\n#include <vector>\n#include <string>\n\n#include \"ptr.hpp\"\n\n\nnamespace MWWorld\n{\n    struct ListAndResetObjectsVisitor\n    {\n        std::vector<MWWorld::Ptr> mObjects;\n\n        bool operator() (MWWorld::Ptr ptr)\n        {\n            if (ptr.getRefData().getBaseNode())\n            {\n                ptr.getRefData().setBaseNode(nullptr);\n                mObjects.push_back (ptr);\n            }\n\n            return true;\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/class.cpp",
    "content": "#include \"class.hpp\"\n\n#include <stdexcept>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/defs.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n\n#include \"ptr.hpp\"\n#include \"refdata.hpp\"\n#include \"nullaction.hpp\"\n#include \"failedaction.hpp\"\n#include \"actiontake.hpp\"\n#include \"containerstore.hpp\"\n\n#include \"../mwgui/tooltips.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n\nnamespace MWWorld\n{\n    std::map<std::string, std::shared_ptr<Class> > Class::sClasses;\n\n    Class::Class() {}\n\n    Class::~Class() {}\n\n    void Class::insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const\n    {\n\n    }\n\n    void Class::insertObject(const Ptr& ptr, const std::string& mesh, MWPhysics::PhysicsSystem& physics) const\n    {\n\n    }\n\n    bool Class::apply (const MWWorld::Ptr& ptr, const std::string& id,  const MWWorld::Ptr& actor) const\n    {\n        return false;\n    }\n\n    void Class::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const\n    {\n        throw std::runtime_error (\"class does not represent an actor\");\n    }\n\n    bool Class::canSell (const MWWorld::ConstPtr& item, int npcServices) const\n    {\n        return false;\n    }\n\n    int Class::getServices(const ConstPtr &actor) const\n    {\n        throw std::runtime_error (\"class does not have services\");\n    }\n\n    MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const\n    {\n        /*\n            Start of tes3mp addition\n\n            This is a common error in multiplayer, so additional logging has been added for it\n        */\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Attempt at getting creatureStats for %s %i-%i which is a %s!\",\n            ptr.getCellRef().getRefId().c_str(), ptr.getCellRef().getRefNum().mIndex, ptr.getCellRef().getMpNum(),\n            ptr.getClass().getTypeName().c_str());\n        /*\n            End of tes3mp addition\n        */\n\n        throw std::runtime_error (\"class does not have creature stats\");\n    }\n\n    MWMechanics::NpcStats& Class::getNpcStats (const Ptr& ptr) const\n    {\n        throw std::runtime_error (\"class does not have NPC stats\");\n    }\n\n    bool Class::hasItemHealth (const ConstPtr& ptr) const\n    {\n        return false;\n    }\n\n    int Class::getItemHealth(const ConstPtr &ptr) const\n    {\n        if (ptr.getCellRef().getCharge() == -1)\n            return getItemMaxHealth(ptr);\n        else\n            return ptr.getCellRef().getCharge();\n    }\n\n    float Class::getItemNormalizedHealth (const ConstPtr& ptr) const\n    {\n        if (getItemMaxHealth(ptr) == 0)\n        {\n            return 0.f;\n        }\n        else\n        {\n            return getItemHealth(ptr) / static_cast<float>(getItemMaxHealth(ptr));\n        }\n    }\n\n    int Class::getItemMaxHealth (const ConstPtr& ptr) const\n    {\n        throw std::runtime_error (\"class does not have item health\");\n    }\n\n    void Class::hit(const Ptr& ptr, float attackStrength, int type) const\n    {\n        throw std::runtime_error(\"class cannot hit\");\n    }\n\n    void Class::block(const Ptr &ptr) const\n    {\n        throw std::runtime_error(\"class cannot block\");\n    }\n\n    void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const\n    {\n        throw std::runtime_error(\"class cannot be hit\");\n    }\n\n    std::shared_ptr<Action> Class::activate (const Ptr& ptr, const Ptr& actor) const\n    {\n        return std::shared_ptr<Action> (new NullAction);\n    }\n\n    std::shared_ptr<Action> Class::use (const Ptr& ptr, bool force) const\n    {\n        return std::shared_ptr<Action> (new NullAction);\n    }\n\n    ContainerStore& Class::getContainerStore (const Ptr& ptr) const\n    {\n        throw std::runtime_error (\"class does not have a container store\");\n    }\n\n    InventoryStore& Class::getInventoryStore (const Ptr& ptr) const\n    {\n        throw std::runtime_error (\"class does not have an inventory store\");\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check whether a class has a container store\n    */\n    bool Class::hasContainerStore(const Ptr &ptr) const\n    {\n        return false;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    bool Class::hasInventoryStore(const Ptr &ptr) const\n    {\n        return false;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check whether a class can be harvested\n    */\n    bool Class::canBeHarvested(const ConstPtr& ptr) const\n    {\n        return false;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    bool Class::canLock(const ConstPtr &ptr) const\n    {\n        return false;\n    }\n\n    void Class::setRemainingUsageTime (const Ptr& ptr, float duration) const\n    {\n        throw std::runtime_error (\"class does not support time-based uses\");\n    }\n\n    float Class::getRemainingUsageTime (const ConstPtr& ptr) const\n    {\n        return -1;\n    }\n\n    std::string Class::getScript (const ConstPtr& ptr) const\n    {\n        return \"\";\n    }\n\n    float Class::getMaxSpeed (const Ptr& ptr) const\n    {\n        return 0;\n    }\n    \n    float Class::getCurrentSpeed (const Ptr& ptr) const\n    {\n        return 0;\n    }\n\n    float Class::getJump (const Ptr& ptr) const\n    {\n        return 0;\n    }\n\n    int Class::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const\n    {\n        throw std::runtime_error (\"class does not support enchanting\");\n    }\n\n    MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const\n    {\n        throw std::runtime_error (\"movement settings not supported by class\");\n    }\n\n    osg::Vec3f Class::getRotationVector (const Ptr& ptr) const\n    {\n        return osg::Vec3f (0, 0, 0);\n    }\n\n    std::pair<std::vector<int>, bool> Class::getEquipmentSlots (const ConstPtr& ptr) const\n    {\n        return std::make_pair (std::vector<int>(), false);\n    }\n\n    int Class::getEquipmentSkill (const ConstPtr& ptr) const\n    {\n        return -1;\n    }\n\n    int Class::getValue (const ConstPtr& ptr) const\n    {\n        throw std::logic_error (\"value not supported by this class\");\n    }\n\n    float Class::getCapacity (const MWWorld::Ptr& ptr) const\n    {\n        throw std::runtime_error (\"capacity not supported by this class\");\n    }\n\n    float Class::getWeight(const ConstPtr &ptr) const\n    {\n        throw std::runtime_error (\"weight not supported by this class\");\n    }\n\n    float Class::getEncumbrance (const MWWorld::Ptr& ptr) const\n    {\n        throw std::runtime_error (\"encumbrance not supported by class\");\n    }\n\n    bool Class::isEssential (const MWWorld::ConstPtr& ptr) const\n    {\n        return false;\n    }\n\n    float Class::getArmorRating (const MWWorld::Ptr& ptr) const\n    {\n        throw std::runtime_error(\"Class does not support armor rating\");\n    }\n\n    const Class& Class::get (const std::string& key)\n    {\n        if (key.empty())\n            throw std::logic_error (\"Class::get(): attempting to get an empty key\");\n\n        std::map<std::string, std::shared_ptr<Class> >::const_iterator iter = sClasses.find (key);\n\n        if (iter==sClasses.end())\n            throw std::logic_error (\"Class::get(): unknown class key: \" + key);\n\n        return *iter->second;\n    }\n\n    bool Class::isPersistent(const ConstPtr &ptr) const\n    {\n        throw std::runtime_error (\"class does not support persistence\");\n    }\n\n    void Class::registerClass(const std::string& key,  std::shared_ptr<Class> instance)\n    {\n        instance->mTypeName = key;\n        sClasses.insert(std::make_pair(key, instance));\n    }\n\n    std::string Class::getUpSoundId (const ConstPtr& ptr) const\n    {\n        throw std::runtime_error (\"class does not have an up sound\");\n    }\n\n    std::string Class::getDownSoundId (const ConstPtr& ptr) const\n    {\n        throw std::runtime_error (\"class does not have an down sound\");\n    }\n\n    std::string Class::getSoundIdFromSndGen(const Ptr &ptr, const std::string &type) const\n    {\n        throw std::runtime_error(\"class does not support soundgen look up\");\n    }\n\n    std::string Class::getInventoryIcon (const MWWorld::ConstPtr& ptr) const\n    {\n        throw std::runtime_error (\"class does not have any inventory icon\");\n    }\n\n    MWGui::ToolTipInfo Class::getToolTipInfo (const ConstPtr& ptr, int count) const\n    {\n        throw std::runtime_error (\"class does not have a tool tip\");\n    }\n\n    bool Class::showsInInventory (const ConstPtr& ptr) const\n    {\n        // NOTE: Don't show WerewolfRobe objects in the inventory, or allow them to be taken.\n        // Vanilla likely uses a hack like this since there's no other way to prevent it from\n        // being shown or taken.\n        return (ptr.getCellRef().getRefId() != \"werewolfrobe\");\n    }\n\n    bool Class::hasToolTip (const ConstPtr& ptr) const\n    {\n        return true;\n    }\n\n    std::string Class::getEnchantment (const ConstPtr& ptr) const\n    {\n        return \"\";\n    }\n\n    void Class::adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const\n    {\n    }\n\n    std::string Class::getModel(const MWWorld::ConstPtr &ptr) const\n    {\n        return \"\";\n    }\n\n    bool Class::useAnim() const\n    {\n        return false;\n    }\n\n    void Class::getModelsToPreload(const Ptr &ptr, std::vector<std::string> &models) const\n    {\n        std::string model = getModel(ptr);\n        if (!model.empty())\n            models.push_back(model);\n    }\n\n    std::string Class::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const\n    {\n        throw std::runtime_error (\"class can't be enchanted\");\n    }\n\n    std::pair<int, std::string> Class::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const\n    {\n        return std::make_pair (1, \"\");\n    }\n\n    void Class::adjustPosition(const MWWorld::Ptr& ptr, bool force) const\n    {\n    }\n\n    std::shared_ptr<Action> Class::defaultItemActivate(const Ptr &ptr, const Ptr &actor) const\n    {\n        if(!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))\n            return std::shared_ptr<Action>(new NullAction());\n\n        if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())\n        {\n            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();\n            const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom(\"WolfItem\");\n\n            std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(\"#{sWerewolfRefusal}\"));\n            if(sound) action->setSound(sound->mId);\n\n            return action;\n        }\n\n        std::shared_ptr<MWWorld::Action> action(new ActionTake(ptr));\n        action->setSound(getUpSoundId(ptr));\n\n        return action;\n    }\n\n    MWWorld::Ptr\n    Class::copyToCellImpl(const ConstPtr &ptr, CellStore &cell) const\n    {\n        throw std::runtime_error(\"unable to copy class to cell\");\n    }\n\n    MWWorld::Ptr\n    Class::copyToCell(const ConstPtr &ptr, CellStore &cell, int count) const\n    {\n        Ptr newPtr = copyToCellImpl(ptr, cell);\n        newPtr.getCellRef().unsetRefNum(); // This RefNum is only valid within the original cell of the reference\n        newPtr.getRefData().setCount(count);\n        return newPtr;\n    }\n\n    MWWorld::Ptr\n    Class::copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const\n    {\n        Ptr newPtr = copyToCell(ptr, cell, count);\n        newPtr.getRefData().setPosition(pos);\n\n        return newPtr;\n    }\n\n    bool Class::isBipedal(const ConstPtr &ptr) const\n    {\n        return false;\n    }\n\n    bool Class::canFly(const ConstPtr &ptr) const\n    {\n        return false;\n    }\n\n    bool Class::canSwim(const ConstPtr &ptr) const\n    {\n        return false;\n    }\n\n    bool Class::canWalk(const ConstPtr &ptr) const\n    {\n        return false;\n    }\n\n    bool Class::isPureWaterCreature(const ConstPtr& ptr) const\n    {\n        return canSwim(ptr)\n                && !isBipedal(ptr)\n                && !canFly(ptr)\n                && !canWalk(ptr);\n    }\n\n    bool Class::isPureFlyingCreature(const ConstPtr& ptr) const\n    {\n        return canFly(ptr)\n                && !isBipedal(ptr)\n                && !canSwim(ptr)\n                && !canWalk(ptr);\n    }\n\n    bool Class::isPureLandCreature(const Ptr& ptr) const\n    {\n        return canWalk(ptr)\n                && !isBipedal(ptr)\n                && !canFly(ptr)\n                && !canSwim(ptr);\n    }\n\n    bool Class::isMobile(const MWWorld::Ptr& ptr) const\n    {\n        return canSwim(ptr) || canWalk(ptr) || canFly(ptr);\n    }\n\n    float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const\n    {\n        throw std::runtime_error(\"class does not support skills\");\n    }\n\n    int Class::getBloodTexture (const MWWorld::ConstPtr& ptr) const\n    {\n        throw std::runtime_error(\"class does not support gore\");\n    }\n\n    void Class::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const {}\n\n    void Class::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const {}\n\n    int Class::getBaseGold(const MWWorld::ConstPtr& ptr) const\n    {\n        throw std::runtime_error(\"class does not support base gold\");\n    }\n\n    bool Class::isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const\n    {\n        return false;\n    }\n\n    MWWorld::DoorState Class::getDoorState (const MWWorld::ConstPtr &ptr) const\n    {\n        throw std::runtime_error(\"this is not a door\");\n    }\n\n    void Class::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const\n    {\n        throw std::runtime_error(\"this is not a door\");\n    }\n\n    float Class::getNormalizedEncumbrance(const Ptr &ptr) const\n    {\n        float capacity = getCapacity(ptr);\n        float encumbrance = getEncumbrance(ptr);\n\n        if (encumbrance == 0)\n            return 0.f;\n\n        if (capacity == 0)\n            return 1.f;\n\n        return encumbrance / capacity;\n    }\n\n    std::string Class::getSound(const MWWorld::ConstPtr&) const\n    {\n      return std::string();\n    }\n\n    int Class::getBaseFightRating(const ConstPtr &ptr) const\n    {\n        throw std::runtime_error(\"class does not support fight rating\");\n    }\n\n    std::string Class::getPrimaryFaction (const MWWorld::ConstPtr& ptr) const\n    {\n        return std::string();\n    }\n    int Class::getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const\n    {\n        return -1;\n    }\n\n    float Class::getEffectiveArmorRating(const ConstPtr &armor, const Ptr &actor) const\n    {\n        throw std::runtime_error(\"class does not support armor ratings\");\n    }\n\n    osg::Vec4f Class::getEnchantmentColor(const MWWorld::ConstPtr& item) const\n    {\n        osg::Vec4f result(1,1,1,1);\n        std::string enchantmentName = item.getClass().getEnchantment(item);\n        if (enchantmentName.empty())\n            return result;\n\n        const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentName);\n        if (!enchantment)\n            return result;\n\n        assert (enchantment->mEffects.mList.size());\n\n        const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().search(\n                enchantment->mEffects.mList.front().mEffectID);\n        if (!magicEffect)\n            return result;\n\n        result.x() = magicEffect->mData.mRed / 255.f;\n        result.y() = magicEffect->mData.mGreen / 255.f;\n        result.z() = magicEffect->mData.mBlue / 255.f;\n        return result;\n    }\n\n    void Class::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const\n    {\n        throw std::runtime_error (\"class does not have creature stats\");\n    }\n\n    void Class::modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const\n    {\n        throw std::runtime_error (\"class does not have an inventory store\");\n    }\n\n    float Class::getWalkSpeed(const Ptr& /*ptr*/) const\n    {\n        return 0;\n    }\n\n    float Class::getRunSpeed(const Ptr& /*ptr*/) const\n    {\n        return 0;\n    }\n\n    float Class::getSwimSpeed(const Ptr& /*ptr*/) const\n    {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/class.hpp",
    "content": "#ifndef GAME_MWWORLD_CLASS_H\n#define GAME_MWWORLD_CLASS_H\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <osg/Vec4f>\n\n#include \"ptr.hpp\"\n#include \"doorstate.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n\nnamespace ESM\n{\n    struct ObjectState;\n}\n\nnamespace MWRender\n{\n    class RenderingInterface;\n}\n\nnamespace MWPhysics\n{\n    class PhysicsSystem;\n}\n\nnamespace MWMechanics\n{\n    class NpcStats;\n    struct Movement;\n}\n\nnamespace MWGui\n{\n    struct ToolTipInfo;\n}\n\nnamespace ESM\n{\n    struct Position;\n}\n\nnamespace MWWorld\n{\n    class ContainerStore;\n    class InventoryStore;\n    class CellStore;\n    class Action;\n\n    /// \\brief Base class for referenceable esm records\n    class Class\n    {\n            static std::map<std::string, std::shared_ptr<Class> > sClasses;\n\n            std::string mTypeName;\n\n            // not implemented\n            Class (const Class&);\n            Class& operator= (const Class&);\n\n        protected:\n\n            Class();\n\n            std::shared_ptr<Action> defaultItemActivate(const Ptr &ptr, const Ptr &actor) const;\n            ///< Generate default action for activating inventory items\n\n            virtual Ptr copyToCellImpl(const ConstPtr &ptr, CellStore &cell) const;\n\n        public:\n\n            virtual ~Class();\n\n            const std::string& getTypeName() const {\n                return mTypeName;\n            }\n\n            virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const;\n            virtual void insertObject(const Ptr& ptr, const std::string& mesh, MWPhysics::PhysicsSystem& physics) const;\n            ///< Add reference into a cell for rendering (default implementation: don't render anything).\n\n            virtual std::string getName (const ConstPtr& ptr) const = 0;\n            ///< \\return name or ID; can return an empty string.\n\n            virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const;\n            ///< Adjust position to stand on ground. Must be called post model load\n            /// @param force do this even if the ptr is flying\n\n            virtual MWMechanics::CreatureStats& getCreatureStats (const Ptr& ptr) const;\n            ///< Return creature stats or throw an exception, if class does not have creature stats\n            /// (default implementation: throw an exception)\n\n            virtual bool hasToolTip (const ConstPtr& ptr) const;\n            ///< @return true if this object has a tooltip when focused (default implementation: true)\n\n            virtual MWGui::ToolTipInfo getToolTipInfo (const ConstPtr& ptr, int count) const;\n            ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.\n\n            virtual bool showsInInventory (const ConstPtr& ptr) const;\n            ///< Return whether ptr shows in inventory views.\n            /// Hidden items are not displayed and cannot be (re)moved by the user.\n            /// \\return True if shown, false if hidden.\n\n            virtual MWMechanics::NpcStats& getNpcStats (const Ptr& ptr) const;\n            ///< Return NPC stats or throw an exception, if class does not have NPC stats\n            /// (default implementation: throw an exception)\n\n            virtual bool hasItemHealth (const ConstPtr& ptr) const;\n            ///< \\return Item health data available? (default implementation: false)\n\n            virtual int getItemHealth (const ConstPtr& ptr) const;\n            ///< Return current item health or throw an exception if class does not have item health\n\n            virtual float getItemNormalizedHealth (const ConstPtr& ptr) const;\n            ///< Return current item health re-scaled to maximum health\n\n            virtual int getItemMaxHealth (const ConstPtr& ptr) const;\n            ///< Return item max health or throw an exception, if class does not have item health\n            /// (default implementation: throw an exception)\n\n            virtual void hit(const Ptr& ptr, float attackStrength, int type=-1) const;\n            ///< Execute a melee hit, using the current weapon. This will check the relevant skills\n            /// of the given attacker, and whoever is hit.\n            /// \\param attackStrength how long the attack was charged for, a value in 0-1 range.\n            /// \\param type - type of attack, one of the MWMechanics::CreatureStats::AttackType\n            ///               enums. ignored for creature attacks.\n            /// (default implementation: throw an exception)\n\n            virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;\n            ///< Alerts \\a ptr that it's being hit for \\a damage points to health if \\a ishealth is\n            /// true (else fatigue) by \\a object (sword, arrow, etc). \\a attacker specifies the\n            /// actor responsible for the attack, and \\a successful specifies if the hit is\n            /// successful or not.\n\n            virtual void block (const Ptr& ptr) const;\n            ///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield\n            /// (default implementation: throw an exception)\n\n            virtual std::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const;\n            ///< Generate action for activation (default implementation: return a null action).\n\n            virtual std::shared_ptr<Action> use (const Ptr& ptr, bool force=false)\n                const;\n            ///< Generate action for using via inventory menu (default implementation: return a\n            /// null action).\n\n            virtual ContainerStore& getContainerStore (const Ptr& ptr) const;\n            ///< Return container store or throw an exception, if class does not have a\n            /// container store (default implementation: throw an exception)\n\n            virtual InventoryStore& getInventoryStore (const Ptr& ptr) const;\n            ///< Return inventory store or throw an exception, if class does not have a\n            /// inventory store (default implementation: throw an exception)\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a class has a container store\n            */\n            virtual bool hasContainerStore(const Ptr& ptr) const;\n            ///< Does this object have a container store? (default implementation: false)\n            /*\n                End of tes3mp addition\n            */\n\n            virtual bool hasInventoryStore (const Ptr& ptr) const;\n            ///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false)\n\n            virtual bool canLock (const ConstPtr& ptr) const;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a class can be harvested\n            */\n            virtual bool canBeHarvested(const ConstPtr& ptr) const;\n            ///< Can this object be harvested? (default implementation: false)\n            /*\n                End of tes3mp addition\n            */\n\n            virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const;\n            ///< Sets the remaining duration of the object, such as an equippable light\n            /// source. (default implementation: throw an exception)\n\n            virtual float getRemainingUsageTime (const ConstPtr& ptr) const;\n            ///< Returns the remaining duration of the object, such as an equippable light\n            /// source. (default implementation: -1, i.e. infinite)\n\n            virtual std::string getScript (const ConstPtr& ptr) const;\n            ///< Return name of the script attached to ptr (default implementation: return an empty\n            /// string).\n\n            virtual float getWalkSpeed(const Ptr& ptr) const;\n            virtual float getRunSpeed(const Ptr& ptr) const;\n            virtual float getSwimSpeed(const Ptr& ptr) const;\n\n            /// Return maximal movement speed for the current state.\n            virtual float getMaxSpeed(const Ptr& ptr) const;\n\n            /// Return current movement speed.\n            virtual float getCurrentSpeed(const Ptr& ptr) const;\n\n            virtual float getJump(const MWWorld::Ptr &ptr) const;\n            ///< Return jump velocity (not accounting for movement)\n\n            virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const;\n            ///< Return desired movement.\n\n            virtual osg::Vec3f getRotationVector (const Ptr& ptr) const;\n            ///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.\n\n            virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const ConstPtr& ptr) const;\n            ///< \\return first: Return IDs of the slot this object can be equipped in; second: can object\n            /// stay stacked when equipped?\n            ///\n            /// Default implementation: return (empty vector, false).\n\n            virtual int getEquipmentSkill (const ConstPtr& ptr)\n                const;\n            /// Return the index of the skill this item corresponds to when equipped or -1, if there is\n            /// no such skill.\n            /// (default implementation: return -1)\n\n            virtual int getValue (const ConstPtr& ptr) const;\n            ///< Return trade value of the object. Throws an exception, if the object can't be traded.\n            /// (default implementation: throws an exception)\n\n            virtual float getCapacity (const MWWorld::Ptr& ptr) const;\n            ///< Return total weight that fits into the object. Throws an exception, if the object can't\n            /// hold other objects.\n            /// (default implementation: throws an exception)\n\n            virtual float getEncumbrance (const MWWorld::Ptr& ptr) const;\n            ///< Returns total weight of objects inside this object (including modifications from magic\n            /// effects). Throws an exception, if the object can't hold other objects.\n            /// (default implementation: throws an exception)\n\n            virtual float getNormalizedEncumbrance (const MWWorld::Ptr& ptr) const;\n            ///< Returns encumbrance re-scaled to capacity\n\n            virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id,\n                const MWWorld::Ptr& actor) const;\n            ///< Apply \\a id on \\a ptr.\n            /// \\param actor Actor that is resposible for the ID being applied to \\a ptr.\n            /// \\return Any effect?\n            ///\n            /// (default implementation: ignore and return false)\n\n            virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor=1.f) const;\n            ///< Inform actor \\a ptr that a skill use has succeeded.\n            ///\n            /// (default implementations: throws an exception)\n\n            virtual bool isEssential (const MWWorld::ConstPtr& ptr) const;\n            ///< Is \\a ptr essential? (i.e. may losing \\a ptr make the game unwinnable)\n            ///\n            /// (default implementation: return false)\n\n            virtual std::string getUpSoundId (const ConstPtr& ptr) const;\n            ///< Return the up sound ID of \\a ptr or throw an exception, if class does not support ID retrieval\n            /// (default implementation: throw an exception)\n\n            virtual std::string getDownSoundId (const ConstPtr& ptr) const;\n            ///< Return the down sound ID of \\a ptr or throw an exception, if class does not support ID retrieval\n            /// (default implementation: throw an exception)\n\n            virtual std::string getSoundIdFromSndGen(const Ptr &ptr, const std::string &type) const;\n            ///< Returns the sound ID for \\a ptr of the given soundgen \\a type.\n\n            virtual float getArmorRating (const MWWorld::Ptr& ptr) const;\n            ///< @return combined armor rating of this actor\n\n            virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;\n            ///< Return name of inventory icon.\n\n            virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const;\n            ///< @return the enchantment ID if the object is enchanted, otherwise an empty string\n            /// (default implementation: return empty string)\n\n            virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const;\n            ///< @return the number of enchantment points available for possible enchanting\n\n            virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const;\n            /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh\n\n            virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const;\n            ///< Determine whether or not \\a item can be sold to an npc with the given \\a npcServices\n\n            virtual int getServices (const MWWorld::ConstPtr& actor) const;\n\n            virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;\n\n            virtual bool useAnim() const;\n            ///< Whether or not to use animated variant of model (default false)\n\n            virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;\n            ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().\n\n            virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const;\n            ///< Creates a new record using \\a ptr as template, with the given name and the given enchantment applied to it.\n\n            virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;\n            ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.\n            ///  Second item in the pair specifies the error message\n\n            virtual float getWeight (const MWWorld::ConstPtr& ptr) const;\n\n            virtual bool isPersistent (const MWWorld::ConstPtr& ptr) const;\n\n            virtual bool isKey (const MWWorld::ConstPtr& ptr) const { return false; }\n\n            virtual bool isGold(const MWWorld::ConstPtr& ptr) const { return false; }\n\n            virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const { return true; }\n            ///< Return whether this class of object can be activated with telekinesis\n\n            /// Get a blood texture suitable for \\a ptr (see Blood Texture 0-2 in Morrowind.ini)\n            virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;\n\n            virtual Ptr copyToCell(const ConstPtr &ptr, CellStore &cell, int count) const;\n\n            virtual Ptr copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const;\n\n            virtual bool isActivator() const {\n                return false;\n            }\n\n            virtual bool isActor() const {\n                return false;\n            }\n\n            virtual bool isNpc() const {\n                return false;\n            }\n\n            virtual bool isDoor() const {\n                return false;\n            }\n\n            virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const;\n            virtual bool canFly(const MWWorld::ConstPtr& ptr) const;\n            virtual bool canSwim(const MWWorld::ConstPtr& ptr) const;\n            virtual bool canWalk(const MWWorld::ConstPtr& ptr) const;\n            bool isPureWaterCreature(const MWWorld::ConstPtr& ptr) const;\n            bool isPureFlyingCreature(const MWWorld::ConstPtr& ptr) const;\n            bool isPureLandCreature(const MWWorld::Ptr& ptr) const;\n            bool isMobile(const MWWorld::Ptr& ptr) const;\n\n            virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const;\n\n            virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)\n                const;\n            ///< Read additional state from \\a state into \\a ptr.\n\n            virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state)\n                const;\n            ///< Write additional state from \\a ptr into \\a state.\n\n            static const Class& get (const std::string& key);\n            ///< If there is no class for this \\a key, an exception is thrown.\n\n            static void registerClass (const std::string& key,  std::shared_ptr<Class> instance);\n\n            virtual int getBaseGold(const MWWorld::ConstPtr& ptr) const;\n\n            virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const;\n\n            virtual DoorState getDoorState (const MWWorld::ConstPtr &ptr) const;\n            /// This does not actually cause the door to move. Use World::activateDoor instead.\n            virtual void setDoorState (const MWWorld::Ptr &ptr, DoorState state) const;\n\n            virtual void respawn (const MWWorld::Ptr& ptr) const {}\n\n            /// Returns sound id\n            virtual std::string getSound(const MWWorld::ConstPtr& ptr) const;\n\n            virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const;\n\n            virtual std::string getPrimaryFaction (const MWWorld::ConstPtr& ptr) const;\n            virtual int getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const;\n\n            /// Get the effective armor rating, factoring in the actor's skills, for the given armor.\n            virtual float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const;\n\n            virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const;\n\n            virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;\n\n            virtual void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/containerstore.cpp",
    "content": "#include \"containerstore.hpp\"\n\n#include <cassert>\n#include <typeinfo>\n#include <stdexcept>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include <components/openmw-mp/TimedLog.hpp>\n/*\n    End of tes3mp addition\n*/\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/inventorystate.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/levelledlist.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/recharge.hpp\"\n\n#include \"manualref.hpp\"\n#include \"refdata.hpp\"\n#include \"class.hpp\"\n#include \"localscripts.hpp\"\n#include \"player.hpp\"\n\nnamespace\n{\n    void addScripts(MWWorld::ContainerStore& store, MWWorld::CellStore* cell)\n    {\n        auto& scripts = MWBase::Environment::get().getWorld()->getLocalScripts();\n        for(const auto&& ptr : store)\n        {\n            const std::string& script = ptr.getClass().getScript(ptr);\n            if(!script.empty())\n            {\n                MWWorld::Ptr item = ptr;\n                item.mCell = cell;\n                scripts.add(script, item);\n            }\n        }\n    }\n\n    template<typename T>\n    float getTotalWeight (const MWWorld::CellRefList<T>& cellRefList)\n    {\n        float sum = 0;\n\n        for (const auto& iter : cellRefList.mList)\n        {\n            if (iter.mData.getCount()>0)\n                sum += iter.mData.getCount()*iter.mBase->mData.mWeight;\n        }\n\n        return sum;\n    }\n\n    template<typename T>\n    MWWorld::Ptr searchId (MWWorld::CellRefList<T>& list, const std::string& id,\n        MWWorld::ContainerStore *store)\n    {\n        store->resolve();\n        std::string id2 = Misc::StringUtils::lowerCase (id);\n\n        for (auto& iter : list.mList)\n        {\n            if (Misc::StringUtils::ciEqual(iter.mBase->mId, id2) && iter.mData.getCount())\n            {\n                MWWorld::Ptr ptr (&iter, nullptr);\n                ptr.setContainerStore (store);\n                return ptr;\n            }\n        }\n\n        return MWWorld::Ptr();\n    }\n}\n\nMWWorld::ResolutionListener::~ResolutionListener()\n{\n    try\n    {\n        mStore.unresolve();\n    }\n    catch(const std::exception& e)\n    {\n        Log(Debug::Error) << \"Failed to clear temporary container contents: \" << e.what();\n    }\n}\n\ntemplate<typename T>\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList<T>& collection,\n    const ESM::ObjectState& state)\n{\n    if (!LiveCellRef<T>::checkState (state))\n        return ContainerStoreIterator (this); // not valid anymore with current content files -> skip\n\n    const T *record = MWBase::Environment::get().getWorld()->getStore().\n        get<T>().search (state.mRef.mRefID);\n\n    if (!record)\n        return ContainerStoreIterator (this);\n\n    LiveCellRef<T> ref (record);\n    ref.load (state);\n    collection.mList.push_back (ref);\n\n    return ContainerStoreIterator (this, --collection.mList.end());\n}\n\nvoid MWWorld::ContainerStore::storeEquipmentState(const MWWorld::LiveCellRefBase &ref, int index, ESM::InventoryState &inventory) const\n{\n}\n\nvoid MWWorld::ContainerStore::readEquipmentState(const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState &inventory)\n{\n}\n\ntemplate<typename T>\nvoid MWWorld::ContainerStore::storeState (const LiveCellRef<T>& ref, ESM::ObjectState& state) const\n{\n    ref.save (state);\n}\n\ntemplate<typename T>\nvoid MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,\n    ESM::InventoryState& inventory, int& index, bool equipable) const\n{\n    for (const auto& iter : collection.mList)\n    {\n        if (iter.mData.getCount() == 0)\n            continue;\n        ESM::ObjectState state;\n        storeState (iter, state);\n        if (equipable)\n            storeEquipmentState(iter, index, inventory);\n        inventory.mItems.push_back (state);\n        ++index;\n    }\n}\n\nconst std::string MWWorld::ContainerStore::sGoldId = \"gold_001\";\n\nMWWorld::ContainerStore::ContainerStore()\n    : mListener(nullptr)\n    , mRechargingItemsUpToDate(false)\n    , mCachedWeight (0)\n    , mWeightUpToDate (false)\n    , mModified(false)\n    , mResolved(false)\n    , mSeed()\n    , mPtr() {}\n\nMWWorld::ContainerStore::~ContainerStore() {}\n\nMWWorld::ConstContainerStoreIterator MWWorld::ContainerStore::cbegin (int mask) const\n{\n    return ConstContainerStoreIterator (mask, this);\n}\n\nMWWorld::ConstContainerStoreIterator MWWorld::ContainerStore::cend() const\n{\n    return ConstContainerStoreIterator (this);\n}\n\nMWWorld::ConstContainerStoreIterator MWWorld::ContainerStore::begin (int mask) const\n{\n    return cbegin(mask);\n}\n\nMWWorld::ConstContainerStoreIterator MWWorld::ContainerStore::end() const\n{\n    return cend();\n}\n\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::begin (int mask)\n{\n    return ContainerStoreIterator (mask, this);\n}\n\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::end()\n{\n    return ContainerStoreIterator (this);\n}\n\nint MWWorld::ContainerStore::count(const std::string &id) const\n{\n    int total=0;\n    for (const auto&& iter : *this)\n        if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))\n            total += iter.getRefData().getCount();\n    return total;\n}\n\nMWWorld::ContainerStoreListener* MWWorld::ContainerStore::getContListener() const\n{\n    return mListener;\n}\n\n\nvoid MWWorld::ContainerStore::setContListener(MWWorld::ContainerStoreListener* listener)\n{\n    mListener = listener;\n}\n\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container, int count)\n{\n    resolve();\n    if (ptr.getRefData().getCount() <= count)\n        return end();\n    MWWorld::ContainerStoreIterator it = addNewStack(ptr, subtractItems(ptr.getRefData().getCount(false), count));\n\n    /*\n        Start of tes3mp addition\n\n        Send an ID_PLAYER_INVENTORY packet every time an item stack gets added for a player here\n    */\n    Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n\n    if (container == player && this == &player.getClass().getContainerStore(player))\n    {\n        mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n\n        if (!localPlayer->avoidSendingInventoryPackets)\n            localPlayer->sendItemChange(ptr, ptr.getRefData().getCount() - count, mwmp::InventoryChanges::ADD);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    const std::string script = it->getClass().getScript(*it);\n    if (!script.empty())\n        MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it);\n\n    remove(ptr, ptr.getRefData().getCount()-count, container);\n\n    return it;\n}\n\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item)\n{\n    resolve();\n    MWWorld::ContainerStoreIterator retval = end();\n    for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)\n    {\n        if (item == *iter)\n        {\n            retval = iter;\n            break;\n        }\n    }\n\n    if (retval == end())\n        throw std::runtime_error(\"item is not from this container\");\n\n    for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)\n    {\n        if (stacks(*iter, item))\n        {\n            iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), item.getRefData().getCount(false)));\n            item.getRefData().setCount(0);\n            retval = iter;\n            break;\n        }\n    }\n    return retval;\n}\n\nbool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const\n{\n    const MWWorld::Class& cls1 = ptr1.getClass();\n    const MWWorld::Class& cls2 = ptr2.getClass();\n\n    if (!Misc::StringUtils::ciEqual(ptr1.getCellRef().getRefId(), ptr2.getCellRef().getRefId()))\n        return false;\n\n    // If it has an enchantment, don't stack when some of the charge is already used\n    if (!ptr1.getClass().getEnchantment(ptr1).empty())\n    {\n        const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(\n                    ptr1.getClass().getEnchantment(ptr1));\n        float maxCharge = static_cast<float>(enchantment->mData.mCharge);\n        float enchantCharge1 = ptr1.getCellRef().getEnchantmentCharge() == -1 ? maxCharge : ptr1.getCellRef().getEnchantmentCharge();\n        float enchantCharge2 = ptr2.getCellRef().getEnchantmentCharge() == -1 ? maxCharge : ptr2.getCellRef().getEnchantmentCharge();\n        if (enchantCharge1 != maxCharge || enchantCharge2 != maxCharge)\n            return false;\n    }\n\n    return ptr1 != ptr2 // an item never stacks onto itself\n        && ptr1.getCellRef().getSoul() == ptr2.getCellRef().getSoul()\n\n        && ptr1.getClass().getRemainingUsageTime(ptr1) == ptr2.getClass().getRemainingUsageTime(ptr2)\n\n        // Items with scripts never stack\n        && cls1.getScript(ptr1).empty()\n        && cls2.getScript(ptr2).empty()\n\n        // item that is already partly used up never stacks\n        && (!cls1.hasItemHealth(ptr1) || (\n                cls1.getItemHealth(ptr1) == cls1.getItemMaxHealth(ptr1)\n            && cls2.getItemHealth(ptr2) == cls2.getItemMaxHealth(ptr2)));\n}\n\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr)\n{\n    MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count);\n    return add(ref.getPtr(), count, actorPtr);\n}\n\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool /*allowAutoEquip*/, bool resolve)\n{\n    Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();\n\n    MWWorld::ContainerStoreIterator it = addImp(itemPtr, count, resolve);\n\n    // The copy of the original item we just made\n    MWWorld::Ptr item = *it;\n\n    /*\n        Start of tes3mp addition\n\n        Send an ID_PLAYER_INVENTORY packet every time an item gets added for a player here\n    */\n    if (this == &player.getClass().getContainerStore(player))\n    {\n        mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n\n        if (!localPlayer->avoidSendingInventoryPackets)\n        {\n            int realCount = count;\n\n            if (itemPtr.getClass().isGold(itemPtr))\n            {\n                realCount = realCount * itemPtr.getClass().getValue(itemPtr);\n            }\n\n            localPlayer->sendItemChange(item, realCount, mwmp::InventoryChanges::ADD);\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    // we may have copied an item from the world, so reset a few things first\n    item.getRefData().setBaseNode(nullptr); // Especially important, otherwise scripts on the item could think that it's actually in a cell\n    ESM::Position pos;\n    pos.rot[0] = 0;\n    pos.rot[1] = 0;\n    pos.rot[2] = 0;\n    pos.pos[0] = 0;\n    pos.pos[1] = 0;\n    pos.pos[2] = 0;\n    item.getCellRef().setPosition(pos);\n\n    // We do not need to store owners for items in container stores - we do not use it anyway.\n    item.getCellRef().setOwner(\"\");\n    item.getCellRef().resetGlobalVariable();\n    item.getCellRef().setFaction(\"\");\n    item.getCellRef().setFactionRank(-2);\n\n    // must reset the RefNum on the copied item, so that the RefNum on the original item stays unique\n    // maybe we should do this in the copy constructor instead?\n    item.getCellRef().unsetRefNum(); // destroy link to content file\n\n    std::string script = item.getClass().getScript(item);\n    if (!script.empty())\n    {\n        if (actorPtr == player)\n        {\n            // Items in player's inventory have cell set to 0, so their scripts will never be removed\n            item.mCell = nullptr;\n        }\n        else\n        {\n            // Set mCell to the cell of the container/actor, so that the scripts are removed properly when\n            // the cell of the container/actor goes inactive\n            item.mCell = actorPtr.getCell();\n        }\n\n        item.mContainerStore = this;\n\n        MWBase::Environment::get().getWorld()->getLocalScripts().add(script, item);\n\n        // Set OnPCAdd special variable, if it is declared\n        // Make sure to do this *after* we have added the script to LocalScripts\n        if (actorPtr == player)\n            item.getRefData().getLocals().setVarByInt(script, \"onpcadd\", 1);\n    }\n\n    /*\n        Start of tes3mp change (major)\n\n        Only fire inventory events for actors in loaded cells to avoid crashes\n    */\n    if (mListener && !actorPtr.getClass().hasInventoryStore(actorPtr) && MWBase::Environment::get().getWorld()->isCellActive(*actorPtr.getCell()->getCell()))\n        mListener->itemAdded(item, count);\n    /*\n        End of tes3mp change (major)\n    */\n\n    return it;\n}\n\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, int count, bool markModified)\n{\n    if(markModified)\n        resolve();\n    int type = getType(ptr);\n\n    const MWWorld::ESMStore &esmStore =\n        MWBase::Environment::get().getWorld()->getStore();\n\n    // gold needs special handling: when it is inserted into a container, the base object automatically becomes Gold_001\n    // this ensures that gold piles of different sizes stack with each other (also, several scripts rely on Gold_001 for detecting player gold)\n    if(ptr.getClass().isGold(ptr))\n    {\n        int realCount = count * ptr.getClass().getValue(ptr);\n\n        for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter)\n        {\n            if (Misc::StringUtils::ciEqual((*iter).getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId))\n            {\n                iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), realCount));\n                flagAsModified();\n                return iter;\n            }\n        }\n\n        MWWorld::ManualRef ref(esmStore, MWWorld::ContainerStore::sGoldId, realCount);\n        return addNewStack(ref.getPtr(), realCount);\n    }\n\n    // determine whether to stack or not\n    for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter)\n    {\n        if (stacks(*iter, ptr))\n        {\n            // stack\n            iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), count));\n\n            flagAsModified();\n            return iter;\n        }\n    }\n    // if we got here, this means no stacking\n    return addNewStack(ptr, count);\n}\n\nMWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const ConstPtr& ptr, int count)\n{\n    ContainerStoreIterator it = begin();\n\n    switch (getType(ptr))\n    {\n        case Type_Potion: potions.mList.push_back (*ptr.get<ESM::Potion>()); it = ContainerStoreIterator(this, --potions.mList.end()); break;\n        case Type_Apparatus: appas.mList.push_back (*ptr.get<ESM::Apparatus>()); it = ContainerStoreIterator(this, --appas.mList.end()); break;\n        case Type_Armor: armors.mList.push_back (*ptr.get<ESM::Armor>()); it = ContainerStoreIterator(this, --armors.mList.end()); break;\n        case Type_Book: books.mList.push_back (*ptr.get<ESM::Book>()); it = ContainerStoreIterator(this, --books.mList.end()); break;\n        case Type_Clothing: clothes.mList.push_back (*ptr.get<ESM::Clothing>()); it = ContainerStoreIterator(this, --clothes.mList.end()); break;\n        case Type_Ingredient: ingreds.mList.push_back (*ptr.get<ESM::Ingredient>()); it = ContainerStoreIterator(this, --ingreds.mList.end()); break;\n        case Type_Light: lights.mList.push_back (*ptr.get<ESM::Light>()); it = ContainerStoreIterator(this, --lights.mList.end()); break;\n        case Type_Lockpick: lockpicks.mList.push_back (*ptr.get<ESM::Lockpick>()); it = ContainerStoreIterator(this, --lockpicks.mList.end()); break;\n        case Type_Miscellaneous: miscItems.mList.push_back (*ptr.get<ESM::Miscellaneous>()); it = ContainerStoreIterator(this, --miscItems.mList.end()); break;\n        case Type_Probe: probes.mList.push_back (*ptr.get<ESM::Probe>()); it = ContainerStoreIterator(this, --probes.mList.end()); break;\n        case Type_Repair: repairs.mList.push_back (*ptr.get<ESM::Repair>()); it = ContainerStoreIterator(this, --repairs.mList.end()); break;\n        case Type_Weapon: weapons.mList.push_back (*ptr.get<ESM::Weapon>()); it = ContainerStoreIterator(this, --weapons.mList.end()); break;\n    }\n\n    it->getRefData().setCount(count);\n\n    flagAsModified();\n    return it;\n}\n\nvoid MWWorld::ContainerStore::rechargeItems(float duration)\n{\n    if (!mRechargingItemsUpToDate)\n    {\n        updateRechargingItems();\n        mRechargingItemsUpToDate = true;\n    }\n    for (auto& it : mRechargingItems)\n    {\n        if (!MWMechanics::rechargeItem(*it.first, it.second, duration))\n            continue;\n\n        // attempt to restack when fully recharged\n        if (it.first->getCellRef().getEnchantmentCharge() == it.second)\n            it.first = restack(*it.first);\n    }\n}\n\nvoid MWWorld::ContainerStore::updateRechargingItems()\n{\n    mRechargingItems.clear();\n    for (ContainerStoreIterator it = begin(); it != end(); ++it)\n    {\n        const std::string& enchantmentId = it->getClass().getEnchantment(*it);\n        if (!enchantmentId.empty())\n        {\n            const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId);\n            if (!enchantment)\n            {\n                Log(Debug::Warning) << \"Warning: Can't find enchantment '\" << enchantmentId << \"' on item \" << it->getCellRef().getRefId();\n                continue;\n            }\n\n            if (enchantment->mData.mType == ESM::Enchantment::WhenUsed\n                    || enchantment->mData.mType == ESM::Enchantment::WhenStrikes)\n                mRechargingItems.emplace_back(it, static_cast<float>(enchantment->mData.mCharge));\n        }\n    }\n}\n\nint MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement, bool resolveFirst)\n{\n    if(resolveFirst)\n        resolve();\n    int toRemove = count;\n\n    for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter)\n        if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), itemId))\n            toRemove -= remove(*iter, toRemove, actor, equipReplacement, resolveFirst);\n\n    flagAsModified();\n\n    // number of removed items\n    return count - toRemove;\n}\n\nbool MWWorld::ContainerStore::hasVisibleItems() const\n{\n    for (const auto&& iter : *this)\n    {\n        if (iter.getClass().showsInInventory(iter))\n            return true;\n    }\n\n    return false;\n}\n\nint MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement, bool resolveFirst)\n{\n    assert(this == item.getContainerStore());\n    if(resolveFirst)\n        resolve();\n\n    /*\n        Start of tes3mp addition\n\n        Send an ID_PLAYER_INVENTORY packet every time an item gets removed for a player here\n    */\n    Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n\n    if (this == &player.getClass().getContainerStore(player))\n    {\n        mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();\n\n        if (!localPlayer->avoidSendingInventoryPackets)\n            localPlayer->sendItemChange(item, count, mwmp::InventoryChanges::REMOVE);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    int toRemove = count;\n    RefData& itemRef = item.getRefData();\n\n    if (itemRef.getCount() <= toRemove)\n    {\n        toRemove -= itemRef.getCount();\n        itemRef.setCount(0);\n    }\n    else\n    {\n        itemRef.setCount(subtractItems(itemRef.getCount(false), toRemove));\n        toRemove = 0;\n    }\n\n    flagAsModified();\n\n    // we should not fire event for InventoryStore yet - it has some custom logic\n\n    /*\n        Start of tes3mp change (major)\n\n        Only fire inventory events for actors in loaded cells to avoid crashes\n    */\n    if (mListener && !actor.getClass().hasInventoryStore(actor) && MWBase::Environment::get().getWorld()->isCellActive(*actor.getCell()->getCell()))\n        mListener->itemRemoved(item, count - toRemove);\n    /*\n        End of tes3mp change (major)\n    */\n\n    // number of removed items\n    return count - toRemove;\n}\n\nvoid MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, Misc::Rng::Seed& seed)\n{\n    for (const ESM::ContItem& iter : items.mList)\n    {\n        std::string id = Misc::StringUtils::lowerCase(iter.mItem);\n        addInitialItem(id, owner, iter.mCount, &seed);\n    }\n\n    flagAsModified();\n    mResolved = true;\n}\n\nvoid MWWorld::ContainerStore::fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed)\n{\n    mSeed = seed;\n    for (const ESM::ContItem& iter : items.mList)\n    {\n        std::string id = Misc::StringUtils::lowerCase(iter.mItem);\n        addInitialItem(id, owner, iter.mCount, nullptr);\n    }\n\n    flagAsModified();\n    mResolved = false;\n}\n\nvoid MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count,\n                                            Misc::Rng::Seed* seed, bool topLevel)\n{\n    if (count == 0) return; //Don't restock with nothing.\n    try\n    {\n        ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);\n        if (ref.getPtr().getClass().getScript(ref.getPtr()).empty())\n        {\n            addInitialItemImp(ref.getPtr(), owner, count, seed, topLevel);\n        }\n        else\n        {\n            // Adding just one item per time to make sure there isn't a stack of scripted items\n            for (int i = 0; i < std::abs(count); i++)\n                addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, seed, topLevel);\n        }\n    }\n    catch (const std::exception& e)\n    {\n        Log(Debug::Warning) << \"Warning: MWWorld::ContainerStore::addInitialItem: \" << e.what();\n    }\n}\n\nvoid MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner, int count,\n                                               Misc::Rng::Seed* seed, bool topLevel)\n{\n    if (ptr.getTypeName()==typeid (ESM::ItemLevList).name())\n    {\n        if(!seed)\n            return;\n        const ESM::ItemLevList* levItemList = ptr.get<ESM::ItemLevList>()->mBase;\n\n        if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)\n        {\n            for (int i=0; i<std::abs(count); ++i)\n                addInitialItem(ptr.getCellRef().getRefId(), owner, count > 0 ? 1 : -1, seed, true);\n            return;\n        }\n        else\n        {\n            std::string itemId = MWMechanics::getLevelledItem(ptr.get<ESM::ItemLevList>()->mBase, false, *seed);\n            if (itemId.empty())\n                return;\n            addInitialItem(itemId, owner, count, seed, false);\n        }\n    }\n    else\n    {\n        ptr.getCellRef().setOwner(owner);\n        addImp (ptr, count, false);\n    }\n}\n\nvoid MWWorld::ContainerStore::clear()\n{\n    for (auto&& iter : *this)\n        iter.getRefData().setCount (0);\n\n    flagAsModified();\n    mModified = true;\n}\n\nvoid MWWorld::ContainerStore::flagAsModified()\n{\n    mWeightUpToDate = false;\n    mRechargingItemsUpToDate = false;\n}\n\nbool MWWorld::ContainerStore::isResolved() const\n{\n    return mResolved;\n}\n\n/*\n    Start of tes3mp addiition\n\n    Make it possible to set the container's resolved state from elsewhere, to avoid unnecessary\n    refills before overriding its contents\n*/\nvoid MWWorld::ContainerStore::setResolved(bool state)\n{\n    mResolved = state;\n}\n/*\n    End of tes3mp addition\n*/\n\nvoid MWWorld::ContainerStore::resolve()\n{\n    if(!mResolved && !mPtr.isEmpty())\n    {\n        for(const auto&& ptr : *this)\n            ptr.getRefData().setCount(0);\n        Misc::Rng::Seed seed{mSeed};\n        fill(mPtr.get<ESM::Container>()->mBase->mInventory, \"\", seed);\n        addScripts(*this, mPtr.mCell);\n    }\n    mModified = true;\n}\n\nMWWorld::ResolutionHandle MWWorld::ContainerStore::resolveTemporarily()\n{\n    if(mModified)\n        return {};\n    std::shared_ptr<ResolutionListener> listener = mResolutionListener.lock();\n    if(!listener)\n    {\n        listener = std::make_shared<ResolutionListener>(*this);\n        mResolutionListener = listener;\n    }\n    if(!mResolved && !mPtr.isEmpty())\n    {\n        for(const auto&& ptr : *this)\n            ptr.getRefData().setCount(0);\n        Misc::Rng::Seed seed{mSeed};\n        fill(mPtr.get<ESM::Container>()->mBase->mInventory, \"\", seed);\n        addScripts(*this, mPtr.mCell);\n    }\n    return {listener};\n}\n\nvoid MWWorld::ContainerStore::unresolve()\n{\n    if (mModified)\n        return;\n\n    if (mResolved && !mPtr.isEmpty())\n    {\n        for(const auto&& ptr : *this)\n            ptr.getRefData().setCount(0);\n        fillNonRandom(mPtr.get<ESM::Container>()->mBase->mInventory, \"\", mSeed);\n        addScripts(*this, mPtr.mCell);\n        mResolved = false;\n    }\n}\n\nfloat MWWorld::ContainerStore::getWeight() const\n{\n    if (!mWeightUpToDate)\n    {\n        mCachedWeight = 0;\n\n        mCachedWeight += getTotalWeight (potions);\n        mCachedWeight += getTotalWeight (appas);\n        mCachedWeight += getTotalWeight (armors);\n        mCachedWeight += getTotalWeight (books);\n        mCachedWeight += getTotalWeight (clothes);\n        mCachedWeight += getTotalWeight (ingreds);\n        mCachedWeight += getTotalWeight (lights);\n        mCachedWeight += getTotalWeight (lockpicks);\n        mCachedWeight += getTotalWeight (miscItems);\n        mCachedWeight += getTotalWeight (probes);\n        mCachedWeight += getTotalWeight (repairs);\n        mCachedWeight += getTotalWeight (weapons);\n\n        mWeightUpToDate = true;\n    }\n\n    return mCachedWeight;\n}\n\nint MWWorld::ContainerStore::getType (const ConstPtr& ptr)\n{\n    if (ptr.isEmpty())\n        throw std::runtime_error (\"can't put a non-existent object into a container\");\n\n    if (ptr.getTypeName()==typeid (ESM::Potion).name())\n        return Type_Potion;\n\n    if (ptr.getTypeName()==typeid (ESM::Apparatus).name())\n        return Type_Apparatus;\n\n    if (ptr.getTypeName()==typeid (ESM::Armor).name())\n        return Type_Armor;\n\n    if (ptr.getTypeName()==typeid (ESM::Book).name())\n        return Type_Book;\n\n    if (ptr.getTypeName()==typeid (ESM::Clothing).name())\n        return Type_Clothing;\n\n    if (ptr.getTypeName()==typeid (ESM::Ingredient).name())\n        return Type_Ingredient;\n\n    if (ptr.getTypeName()==typeid (ESM::Light).name())\n        return Type_Light;\n\n    if (ptr.getTypeName()==typeid (ESM::Lockpick).name())\n        return Type_Lockpick;\n\n    if (ptr.getTypeName()==typeid (ESM::Miscellaneous).name())\n        return Type_Miscellaneous;\n\n    if (ptr.getTypeName()==typeid (ESM::Probe).name())\n        return Type_Probe;\n\n    if (ptr.getTypeName()==typeid (ESM::Repair).name())\n        return Type_Repair;\n\n    if (ptr.getTypeName()==typeid (ESM::Weapon).name())\n        return Type_Weapon;\n\n    throw std::runtime_error (\n        \"Object '\" + ptr.getCellRef().getRefId() + \"' of type \" + ptr.getTypeName() + \" can not be placed into a container\");\n}\n\nMWWorld::Ptr MWWorld::ContainerStore::findReplacement(const std::string& id)\n{\n    MWWorld::Ptr item;\n    int itemHealth = 1;\n    for (auto&& iter : *this)\n    {\n        int iterHealth = iter.getClass().hasItemHealth(iter) ? iter.getClass().getItemHealth(iter) : 1;\n        if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))\n        {\n            // Prefer the stack with the lowest remaining uses\n            // Try to get item with zero durability only if there are no other items found\n            if (item.isEmpty() ||\n                (iterHealth > 0 && iterHealth < itemHealth) ||\n                (itemHealth <= 0 && iterHealth > 0))\n            {\n                item = iter;\n                itemHealth = iterHealth;\n            }\n        }\n    }\n\n    return item;\n}\n\nMWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)\n{\n    resolve();\n    {\n        Ptr ptr = searchId (potions, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (appas, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (armors, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (books, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (clothes, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (ingreds, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (lights, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (lockpicks, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (miscItems, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (probes, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (repairs, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    {\n        Ptr ptr = searchId (weapons, id, this);\n        if (!ptr.isEmpty())\n            return ptr;\n    }\n\n    return Ptr();\n}\n\nint MWWorld::ContainerStore::addItems(int count1, int count2)\n{\n    int sum = std::abs(count1) + std::abs(count2);\n    if(count1 < 0 || count2 < 0)\n        return -sum;\n    return sum;\n}\n\nint MWWorld::ContainerStore::subtractItems(int count1, int count2)\n{\n    int sum = std::abs(count1) - std::abs(count2);\n    if(count1 < 0 || count2 < 0)\n        return -sum;\n    return sum;\n}\n\nvoid MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const\n{\n    state.mItems.clear();\n\n    int index = 0;\n    storeStates (potions, state, index);\n    storeStates (appas, state, index);\n    storeStates (armors, state, index, true);\n    storeStates (books, state, index, true); // not equipable as such, but for selectedEnchantItem\n    storeStates (clothes, state, index, true);\n    storeStates (ingreds, state, index);\n    storeStates (lockpicks, state, index, true);\n    storeStates (miscItems, state, index);\n    storeStates (probes, state, index, true);\n    storeStates (repairs, state, index);\n    storeStates (weapons, state, index, true);\n    storeStates (lights, state, index, true);\n}\n\nvoid MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)\n{\n    clear();\n    mModified = true;\n    mResolved = true;\n\n    int index = 0;\n    for (const ESM::ObjectState& state : inventory.mItems)\n    {\n        int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID);\n\n        int thisIndex = index++;\n\n        switch (type)\n        {\n            case ESM::REC_ALCH: getState (potions, state); break;\n            case ESM::REC_APPA: getState (appas, state); break;\n            case ESM::REC_ARMO: readEquipmentState (getState (armors, state), thisIndex, inventory); break;\n            case ESM::REC_BOOK: readEquipmentState (getState (books, state), thisIndex, inventory); break; // not equipable as such, but for selectedEnchantItem\n            case ESM::REC_CLOT: readEquipmentState (getState (clothes, state), thisIndex, inventory); break;\n            case ESM::REC_INGR: getState (ingreds, state); break;\n            case ESM::REC_LOCK: readEquipmentState (getState (lockpicks, state), thisIndex, inventory); break;\n            case ESM::REC_MISC: getState (miscItems, state); break;\n            case ESM::REC_PROB: readEquipmentState (getState (probes, state), thisIndex, inventory); break;\n            case ESM::REC_REPA: getState (repairs, state); break;\n            case ESM::REC_WEAP: readEquipmentState (getState (weapons, state), thisIndex, inventory); break;\n            case ESM::REC_LIGH: readEquipmentState (getState (lights, state), thisIndex, inventory); break;\n            case 0:\n                Log(Debug::Warning) << \"Dropping inventory reference to '\" << state.mRef.mRefID << \"' (object no longer exists)\";\n                break;\n            default:\n                Log(Debug::Warning) << \"Warning: Invalid item type in inventory state, refid \" << state.mRef.mRefID;\n                break;\n        }\n    }\n}\n\ntemplate<class PtrType>\ntemplate<class T>\nvoid MWWorld::ContainerStoreIteratorBase<PtrType>::copy (const ContainerStoreIteratorBase<T>& src)\n{\n    mType = src.mType;\n    mMask = src.mMask;\n    mContainer = src.mContainer;\n    mPtr = src.mPtr;\n\n    switch (src.mType)\n    {\n        case MWWorld::ContainerStore::Type_Potion: mPotion = src.mPotion; break;\n        case MWWorld::ContainerStore::Type_Apparatus: mApparatus = src.mApparatus; break;\n        case MWWorld::ContainerStore::Type_Armor: mArmor = src.mArmor; break;\n        case MWWorld::ContainerStore::Type_Book: mBook = src.mBook; break;\n        case MWWorld::ContainerStore::Type_Clothing: mClothing = src.mClothing; break;\n        case MWWorld::ContainerStore::Type_Ingredient: mIngredient = src.mIngredient; break;\n        case MWWorld::ContainerStore::Type_Light: mLight = src.mLight; break;\n        case MWWorld::ContainerStore::Type_Lockpick: mLockpick = src.mLockpick; break;\n        case MWWorld::ContainerStore::Type_Miscellaneous: mMiscellaneous = src.mMiscellaneous; break;\n        case MWWorld::ContainerStore::Type_Probe: mProbe = src.mProbe; break;\n        case MWWorld::ContainerStore::Type_Repair: mRepair = src.mRepair; break;\n        case MWWorld::ContainerStore::Type_Weapon: mWeapon = src.mWeapon; break;\n        case -1: break;\n        default: assert(0);\n    }\n}\n\ntemplate<class PtrType>\nvoid MWWorld::ContainerStoreIteratorBase<PtrType>::incType()\n{\n    if (mType==0)\n        mType = 1;\n    else if (mType!=-1)\n    {\n        mType <<= 1;\n\n        if (mType>ContainerStore::Type_Last)\n            mType = -1;\n    }\n}\n\ntemplate<class PtrType>\nvoid MWWorld::ContainerStoreIteratorBase<PtrType>::nextType()\n{\n    while (mType!=-1)\n    {\n        incType();\n\n        if ((mType & mMask) && mType>0)\n            if (resetIterator())\n                break;\n    }\n}\n\ntemplate<class PtrType>\nbool MWWorld::ContainerStoreIteratorBase<PtrType>::resetIterator()\n{\n    switch (mType)\n    {\n        case ContainerStore::Type_Potion:\n\n            mPotion = mContainer->potions.mList.begin();\n            return mPotion!=mContainer->potions.mList.end();\n\n        case ContainerStore::Type_Apparatus:\n\n            mApparatus = mContainer->appas.mList.begin();\n            return mApparatus!=mContainer->appas.mList.end();\n\n        case ContainerStore::Type_Armor:\n\n            mArmor = mContainer->armors.mList.begin();\n            return mArmor!=mContainer->armors.mList.end();\n\n        case ContainerStore::Type_Book:\n\n            mBook = mContainer->books.mList.begin();\n            return mBook!=mContainer->books.mList.end();\n\n        case ContainerStore::Type_Clothing:\n\n            mClothing = mContainer->clothes.mList.begin();\n            return mClothing!=mContainer->clothes.mList.end();\n\n        case ContainerStore::Type_Ingredient:\n\n            mIngredient = mContainer->ingreds.mList.begin();\n            return mIngredient!=mContainer->ingreds.mList.end();\n\n        case ContainerStore::Type_Light:\n\n            mLight = mContainer->lights.mList.begin();\n            return mLight!=mContainer->lights.mList.end();\n\n        case ContainerStore::Type_Lockpick:\n\n            mLockpick = mContainer->lockpicks.mList.begin();\n            return mLockpick!=mContainer->lockpicks.mList.end();\n\n        case ContainerStore::Type_Miscellaneous:\n\n            mMiscellaneous = mContainer->miscItems.mList.begin();\n            return mMiscellaneous!=mContainer->miscItems.mList.end();\n\n        case ContainerStore::Type_Probe:\n\n            mProbe = mContainer->probes.mList.begin();\n            return mProbe!=mContainer->probes.mList.end();\n\n        case ContainerStore::Type_Repair:\n\n            mRepair = mContainer->repairs.mList.begin();\n            return mRepair!=mContainer->repairs.mList.end();\n\n        case ContainerStore::Type_Weapon:\n\n            mWeapon = mContainer->weapons.mList.begin();\n            return mWeapon!=mContainer->weapons.mList.end();\n    }\n\n    return false;\n}\n\ntemplate<class PtrType>\nbool MWWorld::ContainerStoreIteratorBase<PtrType>::incIterator()\n{\n    switch (mType)\n    {\n        case ContainerStore::Type_Potion:\n\n            ++mPotion;\n            return mPotion==mContainer->potions.mList.end();\n\n        case ContainerStore::Type_Apparatus:\n\n            ++mApparatus;\n            return mApparatus==mContainer->appas.mList.end();\n\n        case ContainerStore::Type_Armor:\n\n            ++mArmor;\n            return mArmor==mContainer->armors.mList.end();\n\n        case ContainerStore::Type_Book:\n\n            ++mBook;\n            return mBook==mContainer->books.mList.end();\n\n        case ContainerStore::Type_Clothing:\n\n            ++mClothing;\n            return mClothing==mContainer->clothes.mList.end();\n\n        case ContainerStore::Type_Ingredient:\n\n            ++mIngredient;\n            return mIngredient==mContainer->ingreds.mList.end();\n\n        case ContainerStore::Type_Light:\n\n            ++mLight;\n            return mLight==mContainer->lights.mList.end();\n\n        case ContainerStore::Type_Lockpick:\n\n            ++mLockpick;\n            return mLockpick==mContainer->lockpicks.mList.end();\n\n        case ContainerStore::Type_Miscellaneous:\n\n            ++mMiscellaneous;\n            return mMiscellaneous==mContainer->miscItems.mList.end();\n\n        case ContainerStore::Type_Probe:\n\n            ++mProbe;\n            return mProbe==mContainer->probes.mList.end();\n\n        case ContainerStore::Type_Repair:\n\n            ++mRepair;\n            return mRepair==mContainer->repairs.mList.end();\n\n        case ContainerStore::Type_Weapon:\n\n            ++mWeapon;\n            return mWeapon==mContainer->weapons.mList.end();\n    }\n\n    return true;\n}\n\n\ntemplate<class PtrType>\ntemplate<class T>\nbool MWWorld::ContainerStoreIteratorBase<PtrType>::isEqual (const ContainerStoreIteratorBase<T>& other) const\n{\n    if (mContainer!=other.mContainer)\n        return false;\n\n    if (mType!=other.mType)\n        return false;\n\n    switch (mType)\n    {\n        case ContainerStore::Type_Potion: return mPotion==other.mPotion;\n        case ContainerStore::Type_Apparatus: return mApparatus==other.mApparatus;\n        case ContainerStore::Type_Armor: return mArmor==other.mArmor;\n        case ContainerStore::Type_Book: return mBook==other.mBook;\n        case ContainerStore::Type_Clothing: return mClothing==other.mClothing;\n        case ContainerStore::Type_Ingredient: return mIngredient==other.mIngredient;\n        case ContainerStore::Type_Light: return mLight==other.mLight;\n        case ContainerStore::Type_Lockpick: return mLockpick==other.mLockpick;\n        case ContainerStore::Type_Miscellaneous: return mMiscellaneous==other.mMiscellaneous;\n        case ContainerStore::Type_Probe: return mProbe==other.mProbe;\n        case ContainerStore::Type_Repair: return mRepair==other.mRepair;\n        case ContainerStore::Type_Weapon: return mWeapon==other.mWeapon;\n        case -1: return true;\n    }\n\n    return false;  \n}\n\ntemplate<class PtrType>\nPtrType *MWWorld::ContainerStoreIteratorBase<PtrType>::operator->() const\n{\n    mPtr = **this;\n    return &mPtr;\n}\n\ntemplate<class PtrType>\nPtrType MWWorld::ContainerStoreIteratorBase<PtrType>::operator*() const\n{\n    PtrType ptr;\n\n    switch (mType)\n    {\n        case ContainerStore::Type_Potion: ptr = PtrType (&*mPotion, nullptr); break;\n        case ContainerStore::Type_Apparatus: ptr = PtrType (&*mApparatus, nullptr); break;\n        case ContainerStore::Type_Armor: ptr = PtrType (&*mArmor, nullptr); break;\n        case ContainerStore::Type_Book: ptr = PtrType (&*mBook, nullptr); break;\n        case ContainerStore::Type_Clothing: ptr = PtrType (&*mClothing, nullptr); break;\n        case ContainerStore::Type_Ingredient: ptr = PtrType (&*mIngredient, nullptr); break;\n        case ContainerStore::Type_Light: ptr = PtrType (&*mLight, nullptr); break;\n        case ContainerStore::Type_Lockpick: ptr = PtrType (&*mLockpick, nullptr); break;\n        case ContainerStore::Type_Miscellaneous: ptr = PtrType (&*mMiscellaneous, nullptr); break;\n        case ContainerStore::Type_Probe: ptr = PtrType (&*mProbe, nullptr); break;\n        case ContainerStore::Type_Repair: ptr = PtrType (&*mRepair, nullptr); break;\n        case ContainerStore::Type_Weapon: ptr = PtrType (&*mWeapon, nullptr); break;\n    }\n\n    if (ptr.isEmpty())\n        throw std::runtime_error (\"invalid iterator\");\n\n    ptr.setContainerStore (mContainer);\n\n    return ptr;\n}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>& MWWorld::ContainerStoreIteratorBase<PtrType>::operator++()\n{\n    do\n    {\n        if (incIterator())\n            nextType();\n    }\n    while (mType!=-1 && !(**this).getRefData().getCount());\n\n    return *this;\n}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType> MWWorld::ContainerStoreIteratorBase<PtrType>::operator++ (int)\n{\n    ContainerStoreIteratorBase<PtrType> iter (*this);\n    ++*this;\n    return iter;\n}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>& MWWorld::ContainerStoreIteratorBase<PtrType>::operator= (const ContainerStoreIteratorBase<PtrType>& rhs)\n{\n    if (this!=&rhs)\n    {\n        copy(rhs);\n    }\n    return *this;\n}\n\ntemplate<class PtrType>\nint MWWorld::ContainerStoreIteratorBase<PtrType>::getType() const\n{\n    return mType;\n}\n\ntemplate<class PtrType>\nconst MWWorld::ContainerStore *MWWorld::ContainerStoreIteratorBase<PtrType>::getContainerStore() const\n{\n    return mContainer;\n}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container)\n: mType (-1), mMask (0), mContainer (container)\n{}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (int mask, ContainerStoreType container)\n: mType (0), mMask (mask), mContainer (container)\n{\n    nextType();\n\n    if (mType==-1 || (**this).getRefData().getCount())\n        return;\n\n    ++*this;\n}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Potion>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Potion), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mPotion(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Apparatus>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Apparatus), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mApparatus(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Armor>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Armor), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mArmor(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Book>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Book), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mBook(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Clothing>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Clothing), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mClothing(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Ingredient>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Ingredient), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mIngredient(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Light>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Light), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLight(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Lockpick>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Lockpick), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLockpick(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Miscellaneous>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Miscellaneous), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mMiscellaneous(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Probe>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Probe), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mProbe(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Repair>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Repair), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mRepair(iterator){}\n\ntemplate<class PtrType>\nMWWorld::ContainerStoreIteratorBase<PtrType>::ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Weapon>::type iterator)\n    : mType(MWWorld::ContainerStore::Type_Weapon), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mWeapon(iterator){}\n\n\ntemplate<class T, class U>\nbool MWWorld::operator== (const ContainerStoreIteratorBase<T>& left, const ContainerStoreIteratorBase<U>& right)\n{\n    return left.isEqual (right);\n}\n\ntemplate<class T, class U>\nbool MWWorld::operator!= (const ContainerStoreIteratorBase<T>& left, const ContainerStoreIteratorBase<U>& right)\n{\n    return !(left==right);\n}\n\ntemplate class MWWorld::ContainerStoreIteratorBase<MWWorld::Ptr>;\ntemplate class MWWorld::ContainerStoreIteratorBase<MWWorld::ConstPtr>;\n\ntemplate bool MWWorld::operator== (const ContainerStoreIteratorBase<Ptr>& left, const ContainerStoreIteratorBase<Ptr>& right);\ntemplate bool MWWorld::operator!= (const ContainerStoreIteratorBase<Ptr>& left, const ContainerStoreIteratorBase<Ptr>& right);\ntemplate bool MWWorld::operator== (const ContainerStoreIteratorBase<ConstPtr>& left, const ContainerStoreIteratorBase<ConstPtr>& right);\ntemplate bool MWWorld::operator!= (const ContainerStoreIteratorBase<ConstPtr>& left, const ContainerStoreIteratorBase<ConstPtr>& right);\ntemplate bool MWWorld::operator== (const ContainerStoreIteratorBase<ConstPtr>& left, const ContainerStoreIteratorBase<Ptr>& right);\ntemplate bool MWWorld::operator!= (const ContainerStoreIteratorBase<ConstPtr>& left, const ContainerStoreIteratorBase<Ptr>& right);\ntemplate bool MWWorld::operator== (const ContainerStoreIteratorBase<Ptr>& left, const ContainerStoreIteratorBase<ConstPtr>& right);\ntemplate bool MWWorld::operator!= (const ContainerStoreIteratorBase<Ptr>& left, const ContainerStoreIteratorBase<ConstPtr>& right);\n\ntemplate void MWWorld::ContainerStoreIteratorBase<MWWorld::Ptr>::copy(const ContainerStoreIteratorBase<Ptr>& src);\ntemplate void MWWorld::ContainerStoreIteratorBase<MWWorld::ConstPtr>::copy(const ContainerStoreIteratorBase<Ptr>& src);\ntemplate void MWWorld::ContainerStoreIteratorBase<MWWorld::ConstPtr>::copy(const ContainerStoreIteratorBase<ConstPtr>& src);\n"
  },
  {
    "path": "apps/openmw/mwworld/containerstore.hpp",
    "content": "#ifndef GAME_MWWORLD_CONTAINERSTORE_H\n#define GAME_MWWORLD_CONTAINERSTORE_H\n\n#include <iterator>\n#include <map>\n#include <memory>\n#include <utility>\n\n#include <components/esm/loadalch.hpp>\n#include <components/esm/loadappa.hpp>\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadbook.hpp>\n#include <components/esm/loadclot.hpp>\n#include <components/esm/loadingr.hpp>\n#include <components/esm/loadlock.hpp>\n#include <components/esm/loadligh.hpp>\n#include <components/esm/loadmisc.hpp>\n#include <components/esm/loadprob.hpp>\n#include <components/esm/loadrepa.hpp>\n#include <components/esm/loadweap.hpp>\n\n#include <components/misc/rng.hpp>\n\n#include \"ptr.hpp\"\n#include \"cellreflist.hpp\"\n\nnamespace ESM\n{\n    struct InventoryList;\n    struct InventoryState;\n}\n\nnamespace MWClass\n{\n    class Container;\n}\n\nnamespace MWWorld\n{\n    class ContainerStore;\n\n    template<class PtrType>\n    class ContainerStoreIteratorBase;\n\n    typedef ContainerStoreIteratorBase<Ptr> ContainerStoreIterator;\n    typedef ContainerStoreIteratorBase<ConstPtr> ConstContainerStoreIterator;\n\n    class ResolutionListener\n    {\n            ContainerStore& mStore;\n        public:\n            ResolutionListener(ContainerStore& store) : mStore(store) {}\n            ~ResolutionListener();\n    };\n\n    class ResolutionHandle\n    {\n            std::shared_ptr<ResolutionListener> mListener;\n        public:\n            ResolutionHandle(std::shared_ptr<ResolutionListener> listener) : mListener(listener) {}\n            ResolutionHandle() {}\n    };\n    \n    class ContainerStoreListener\n    {\n        public:\n            virtual void itemAdded(const ConstPtr& item, int count) {}\n            virtual void itemRemoved(const ConstPtr& item, int count) {}\n            virtual ~ContainerStoreListener() = default;\n    };\n\n    class ContainerStore\n    {\n        public:\n\n            static constexpr int Type_Potion = 0x0001;\n            static constexpr int Type_Apparatus = 0x0002;\n            static constexpr int Type_Armor = 0x0004;\n            static constexpr int Type_Book = 0x0008;\n            static constexpr int Type_Clothing = 0x0010;\n            static constexpr int Type_Ingredient = 0x0020;\n            static constexpr int Type_Light = 0x0040;\n            static constexpr int Type_Lockpick = 0x0080;\n            static constexpr int Type_Miscellaneous = 0x0100;\n            static constexpr int Type_Probe = 0x0200;\n            static constexpr int Type_Repair = 0x0400;\n            static constexpr int Type_Weapon = 0x0800;\n\n            static constexpr int Type_Last = Type_Weapon;\n\n            static constexpr int Type_All = 0xffff;\n\n            static const std::string sGoldId;\n\n        protected:\n            ContainerStoreListener* mListener;\n\n            // (item, max charge)\n            typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;\n            TRechargingItems mRechargingItems;\n\n            bool mRechargingItemsUpToDate;\n\n        private:\n\n            MWWorld::CellRefList<ESM::Potion>            potions;\n            MWWorld::CellRefList<ESM::Apparatus>         appas;\n            MWWorld::CellRefList<ESM::Armor>             armors;\n            MWWorld::CellRefList<ESM::Book>              books;\n            MWWorld::CellRefList<ESM::Clothing>          clothes;\n            MWWorld::CellRefList<ESM::Ingredient>        ingreds;\n            MWWorld::CellRefList<ESM::Light>             lights;\n            MWWorld::CellRefList<ESM::Lockpick>          lockpicks;\n            MWWorld::CellRefList<ESM::Miscellaneous>     miscItems;\n            MWWorld::CellRefList<ESM::Probe>             probes;\n            MWWorld::CellRefList<ESM::Repair>            repairs;\n            MWWorld::CellRefList<ESM::Weapon>            weapons;\n\n            mutable float mCachedWeight;\n            mutable bool mWeightUpToDate;\n\n            bool mModified;\n            bool mResolved;\n            unsigned int mSeed;\n            MWWorld::Ptr mPtr;\n            std::weak_ptr<ResolutionListener> mResolutionListener;\n\n            ContainerStoreIterator addImp (const Ptr& ptr, int count, bool markModified = true);\n            void addInitialItem (const std::string& id, const std::string& owner, int count, Misc::Rng::Seed* seed, bool topLevel=true);\n            void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, Misc::Rng::Seed* seed, bool topLevel=true);\n\n            template<typename T>\n            ContainerStoreIterator getState (CellRefList<T>& collection,\n                const ESM::ObjectState& state);\n\n            template<typename T>\n            void storeState (const LiveCellRef<T>& ref, ESM::ObjectState& state) const;\n\n            template<typename T>\n            void storeStates (const CellRefList<T>& collection,\n                ESM::InventoryState& inventory, int& index,\n                bool equipable = false) const;\n\n            void updateRechargingItems();\n\n            virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;\n\n            virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory);\n\n        public:\n\n            ContainerStore();\n\n            virtual ~ContainerStore();\n\n            virtual std::unique_ptr<ContainerStore> clone() { return std::make_unique<ContainerStore>(*this); }\n\n            ConstContainerStoreIterator cbegin (int mask = Type_All) const;\n            ConstContainerStoreIterator cend() const;\n            ConstContainerStoreIterator begin (int mask = Type_All) const;\n            ConstContainerStoreIterator end() const;\n            \n            ContainerStoreIterator begin (int mask = Type_All);\n            ContainerStoreIterator end();\n\n            bool hasVisibleItems() const;\n\n            virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true);\n            ///< Add the item pointed to by \\a ptr to this container. (Stacks automatically if needed)\n            ///\n            /// \\note The item pointed to is not required to exist beyond this function call.\n            ///\n            /// \\attention Do not add items to an existing stack by increasing the count instead of\n            /// calling this function!\n            ///\n            /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item.\n\n            ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr);\n            ///< Utility to construct a ManualRef and call add(ptr, count, actorPtr, true)\n\n            int remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true);\n            ///< Remove \\a count item(s) designated by \\a itemId from this container.\n            ///\n            /// @return the number of items actually removed\n\n            virtual int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true);\n            ///< Remove \\a count item(s) designated by \\a item from this inventory.\n            ///\n            /// @return the number of items actually removed\n\n            void rechargeItems (float duration);\n            ///< Restore charge on enchanted items. Note this should only be done for the player.\n\n            ContainerStoreIterator unstack (const Ptr& ptr, const Ptr& container, int count = 1);\n            ///< Unstack an item in this container. The item's count will be set to count, then a new stack will be added with (origCount-count).\n            ///\n            /// @return an iterator to the new stack, or end() if no new stack was created.\n\n            MWWorld::ContainerStoreIterator restack (const MWWorld::Ptr& item);\n            ///< Attempt to re-stack an item in this container.\n            /// If a compatible stack is found, the item's count is added to that stack, then the original is deleted.\n            /// @return If the item was stacked, return the stack, otherwise return the old (untouched) item.\n\n            int count (const std::string& id) const;\n            ///< @return How many items with refID \\a id are in this container?\n\n            ContainerStoreListener* getContListener() const;\n            void setContListener(ContainerStoreListener* listener);\n        protected:\n            ContainerStoreIterator addNewStack (const ConstPtr& ptr, int count);\n            ///< Add the item to this container (do not try to stack it onto existing items)\n\n            virtual void flagAsModified();\n\n            /// + and - operations that can deal with negative stacks\n            /// Note that negativity is infectious\n            static int addItems(int count1, int count2);\n            static int subtractItems(int count1, int count2);\n        public:\n\n            virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;\n            ///< @return true if the two specified objects can stack with each other\n\n            void fill (const ESM::InventoryList& items, const std::string& owner, Misc::Rng::Seed& seed = Misc::Rng::getSeed());\n            ///< Insert items into *this.\n\n            void fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed);\n            ///< Insert items into *this, excluding leveled items\n\n            virtual void clear();\n            ///< Empty container.\n\n            float getWeight() const;\n            ///< Return total weight of the items contained in *this.\n\n            static int getType (const ConstPtr& ptr);\n            ///< This function throws an exception, if ptr does not point to an object, that can be\n            /// put into a container.\n\n            Ptr findReplacement(const std::string& id);\n            ///< Returns replacement for object with given id. Prefer used items (with low durability left).\n\n            Ptr search (const std::string& id);\n\n            virtual void writeState (ESM::InventoryState& state) const;\n\n            virtual void readState (const ESM::InventoryState& state);\n\n            bool isResolved() const;\n\n            /*\n                Start of tes3mp addiition\n\n                Make it possible to set the container's resolved state from elsewhere, to avoid unnecessary\n                refills before overriding its contents\n            */\n            void setResolved(bool state);\n            /*\n                End of tes3mp addition\n            */\n\n            void resolve();\n            ResolutionHandle resolveTemporarily();\n            void unresolve();\n\n            friend class ContainerStoreIteratorBase<Ptr>;\n            friend class ContainerStoreIteratorBase<ConstPtr>;\n            friend class ResolutionListener;\n            friend class MWClass::Container;\n    };\n\n    \n    template<class PtrType>\n    class ContainerStoreIteratorBase\n        : public std::iterator<std::forward_iterator_tag, PtrType, std::ptrdiff_t, PtrType *, PtrType&>\n    {\n        template<class From, class To, class Dummy>\n        struct IsConvertible\n        {\n            static constexpr bool value = true;\n        };\n\n        template<class Dummy>\n        struct IsConvertible<ConstPtr, Ptr, Dummy>\n        {\n            static constexpr bool value = false;\n        };\n\n        template<class T, class U>\n        struct IteratorTrait\n        {\n            typedef typename MWWorld::CellRefList<T>::List::iterator type;\n        };\n\n        template<class T>\n        struct IteratorTrait<T, ConstPtr>\n        {\n            typedef typename MWWorld::CellRefList<T>::List::const_iterator type;\n        };\n\n        template<class T>\n        struct Iterator : IteratorTrait<T, PtrType>\n        {\n        };\n\n        template<class T, class Dummy>\n        struct ContainerStoreTrait\n        {\n            typedef ContainerStore* type;\n        };\n        \n        template<class Dummy>\n        struct ContainerStoreTrait<ConstPtr, Dummy>\n        {\n            typedef const ContainerStore* type;\n        };\n\n        typedef typename ContainerStoreTrait<PtrType, void>::type ContainerStoreType;\n\n        int mType;\n        int mMask;\n        ContainerStoreType mContainer;\n        mutable PtrType mPtr;\n\n        typename Iterator<ESM::Potion>::type mPotion;\n        typename Iterator<ESM::Apparatus>::type mApparatus;\n        typename Iterator<ESM::Armor>::type mArmor;\n        typename Iterator<ESM::Book>::type mBook;\n        typename Iterator<ESM::Clothing>::type mClothing;\n        typename Iterator<ESM::Ingredient>::type mIngredient;\n        typename Iterator<ESM::Light>::type mLight;\n        typename Iterator<ESM::Lockpick>::type mLockpick;\n        typename Iterator<ESM::Miscellaneous>::type mMiscellaneous;\n        typename Iterator<ESM::Probe>::type mProbe;\n        typename Iterator<ESM::Repair>::type mRepair;\n        typename Iterator<ESM::Weapon>::type mWeapon;\n\n        ContainerStoreIteratorBase (ContainerStoreType container);\n        ///< End-iterator\n\n        ContainerStoreIteratorBase (int mask, ContainerStoreType container);\n        ///< Begin-iterator\n\n        // construct iterator using a CellRefList iterator\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Potion>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Apparatus>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Armor>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Book>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Clothing>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Ingredient>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Light>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Lockpick>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Miscellaneous>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Probe>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Repair>::type);\n        ContainerStoreIteratorBase (ContainerStoreType container, typename Iterator<ESM::Weapon>::type);\n\n        template<class T>\n        void copy (const ContainerStoreIteratorBase<T>& src);\n        \n        void incType ();\n        \n        void nextType ();\n\n        bool resetIterator ();\n        ///< Reset iterator for selected type.\n        ///\n        /// \\return Type not empty?\n\n        bool incIterator ();\n        ///< Increment iterator for selected type.\n        ///\n        /// \\return reached the end?\n\n        public:\n            template<class T>\n            ContainerStoreIteratorBase (const ContainerStoreIteratorBase<T>& other)\n            {\n                char CANNOT_CONVERT_CONST_ITERATOR_TO_ITERATOR[IsConvertible<T, PtrType, void>::value ? 1 : -1];\n                ((void)CANNOT_CONVERT_CONST_ITERATOR_TO_ITERATOR);\n                copy (other);\n            }\n\n            template<class T>\n            bool isEqual(const ContainerStoreIteratorBase<T>& other) const;\n\n            PtrType *operator->() const;\n            PtrType operator*() const;\n\n            ContainerStoreIteratorBase& operator++ ();\n            ContainerStoreIteratorBase operator++ (int);\n            ContainerStoreIteratorBase& operator= (const ContainerStoreIteratorBase& rhs);\n            ContainerStoreIteratorBase (const ContainerStoreIteratorBase& rhs) = default;\n\n            int getType() const;\n            const ContainerStore *getContainerStore() const;\n\n            friend class ContainerStore;\n            friend class ContainerStoreIteratorBase<Ptr>;\n            friend class ContainerStoreIteratorBase<ConstPtr>;\n    };\n\n    template<class T, class U>\n    bool operator== (const ContainerStoreIteratorBase<T>& left, const ContainerStoreIteratorBase<U>& right);\n    template<class T, class U>\n    bool operator!= (const ContainerStoreIteratorBase<T>& left, const ContainerStoreIteratorBase<U>& right);\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/contentloader.hpp",
    "content": "#ifndef CONTENTLOADER_HPP\n#define CONTENTLOADER_HPP\n\n#include <boost/filesystem/path.hpp>\n#include <MyGUI_TextIterator.h>\n\n#include <components/debug/debuglog.hpp>\n#include \"components/loadinglistener/loadinglistener.hpp\"\n\nnamespace MWWorld\n{\n\nstruct ContentLoader\n{\n    ContentLoader(Loading::Listener& listener)\n      : mListener(listener)\n    {\n    }\n\n    virtual ~ContentLoader()\n    {\n    }\n\n    virtual void load(const boost::filesystem::path& filepath, int& index)\n    {\n        Log(Debug::Info) << \"Loading content file \" << filepath.string();\n        mListener.setLabel(MyGUI::TextIterator::toTagsString(filepath.string()));\n    }\n\n    protected:\n        Loading::Listener& mListener;\n};\n\n} /* namespace MWWorld */\n\n#endif /* CONTENTLOADER_HPP */\n"
  },
  {
    "path": "apps/openmw/mwworld/customdata.cpp",
    "content": "#include \"customdata.hpp\"\n\n#include <stdexcept>\n#include <sstream>\n#include <typeinfo>\n\nnamespace MWWorld\n{\n\nMWClass::CreatureCustomData &CustomData::asCreatureCustomData()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to CreatureCustomData\";\n    throw std::logic_error(error.str());\n}\n\nconst MWClass::CreatureCustomData &CustomData::asCreatureCustomData() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to CreatureCustomData\";\n    throw std::logic_error(error.str());\n}\n\nMWClass::NpcCustomData &CustomData::asNpcCustomData()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to NpcCustomData\";\n    throw std::logic_error(error.str());\n}\n\nconst MWClass::NpcCustomData &CustomData::asNpcCustomData() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to NpcCustomData\";\n    throw std::logic_error(error.str());\n}\n\nMWClass::ContainerCustomData &CustomData::asContainerCustomData()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to ContainerCustomData\";\n    throw std::logic_error(error.str());\n}\n\nconst MWClass::ContainerCustomData &CustomData::asContainerCustomData() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to ContainerCustomData\";\n    throw std::logic_error(error.str());\n}\n\nMWClass::DoorCustomData &CustomData::asDoorCustomData()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to DoorCustomData\";\n    throw std::logic_error(error.str());\n}\n\nconst MWClass::DoorCustomData &CustomData::asDoorCustomData() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to DoorCustomData\";\n    throw std::logic_error(error.str());\n}\n\nMWClass::CreatureLevListCustomData &CustomData::asCreatureLevListCustomData()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to CreatureLevListCustomData\";\n    throw std::logic_error(error.str());\n}\n\nconst MWClass::CreatureLevListCustomData &CustomData::asCreatureLevListCustomData() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to CreatureLevListCustomData\";\n    throw std::logic_error(error.str());\n}\n\n\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/customdata.hpp",
    "content": "#ifndef GAME_MWWORLD_CUSTOMDATA_H\n#define GAME_MWWORLD_CUSTOMDATA_H\n\n#include <memory>\n\nnamespace MWClass\n{\n    class CreatureCustomData;\n    class NpcCustomData;\n    class ContainerCustomData;\n    class DoorCustomData;\n    class CreatureLevListCustomData;\n}\n\nnamespace MWWorld\n{\n    /// \\brief Base class for the MW-class-specific part of RefData\n    class CustomData\n    {\n        public:\n\n            virtual ~CustomData() {}\n\n            virtual std::unique_ptr<CustomData> clone() const = 0;\n\n            // Fast version of dynamic_cast<X&>. Needs to be overridden in the respective class.\n\n            virtual MWClass::CreatureCustomData& asCreatureCustomData();\n            virtual const MWClass::CreatureCustomData& asCreatureCustomData() const;\n\n            virtual MWClass::NpcCustomData& asNpcCustomData();\n            virtual const MWClass::NpcCustomData& asNpcCustomData() const;\n\n            virtual MWClass::ContainerCustomData& asContainerCustomData();\n            virtual const MWClass::ContainerCustomData& asContainerCustomData() const;\n\n            virtual MWClass::DoorCustomData& asDoorCustomData();\n            virtual const MWClass::DoorCustomData& asDoorCustomData() const;\n\n            virtual MWClass::CreatureLevListCustomData& asCreatureLevListCustomData();\n            virtual const MWClass::CreatureLevListCustomData& asCreatureLevListCustomData() const;\n    };\n\n    template <class T>\n    struct TypedCustomData : CustomData\n    {\n        std::unique_ptr<CustomData> clone() const final\n        {\n            return std::make_unique<T>(*static_cast<const T*>(this));\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/datetimemanager.cpp",
    "content": "#include \"datetimemanager.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"esmstore.hpp\"\n#include \"globals.hpp\"\n#include \"timestamp.hpp\"\n\nnamespace\n{\n    static int getDaysPerMonth(int month)\n    {\n        switch (month)\n        {\n            case 0: return 31;\n            case 1: return 28;\n            case 2: return 31;\n            case 3: return 30;\n            case 4: return 31;\n            case 5: return 30;\n            case 6: return 31;\n            case 7: return 31;\n            case 8: return 30;\n            case 9: return 31;\n            case 10: return 30;\n            case 11: return 31;\n        }\n\n        throw std::runtime_error (\"month out of range\");\n    }\n}\n\nnamespace MWWorld\n{\n    void DateTimeManager::setup(Globals& globalVariables)\n    {\n        mGameHour = globalVariables[\"gamehour\"].getFloat();\n        mDaysPassed = globalVariables[\"dayspassed\"].getInteger();\n        mDay = globalVariables[\"day\"].getInteger();\n        mMonth = globalVariables[\"month\"].getInteger();\n        mYear = globalVariables[\"year\"].getInteger();\n        mTimeScale = globalVariables[\"timescale\"].getFloat();\n    }\n\n    void DateTimeManager::setHour(double hour)\n    {\n        if (hour < 0)\n            hour = 0;\n\n        int days = static_cast<int>(hour / 24);\n        hour = std::fmod(hour, 24);\n        mGameHour = static_cast<float>(hour);\n\n        if (days > 0)\n            setDay(days + mDay);\n    }\n\n    void DateTimeManager::setDay(int day)\n    {\n        if (day < 1)\n            day = 1;\n\n        int month = mMonth;\n        while (true)\n        {\n            int days = getDaysPerMonth(month);\n            if (day <= days)\n                break;\n\n            if (month < 11)\n            {\n                ++month;\n            }\n            else\n            {\n                month = 0;\n                mYear++;\n            }\n\n            day -= days;\n        }\n\n        mDay = day;\n        mMonth = month;\n    }\n\n    TimeStamp DateTimeManager::getTimeStamp() const\n    {\n        return TimeStamp(mGameHour, mDaysPassed);\n    }\n\n    float DateTimeManager::getTimeScaleFactor() const\n    {\n        return mTimeScale;\n    }\n\n    ESM::EpochTimeStamp DateTimeManager::getEpochTimeStamp() const\n    {\n        ESM::EpochTimeStamp timeStamp;\n        timeStamp.mGameHour = mGameHour;\n        timeStamp.mDay = mDay;\n        timeStamp.mMonth = mMonth;\n        timeStamp.mYear = mYear;\n        return timeStamp;\n    }\n\n    void DateTimeManager::setMonth(int month)\n    {\n        if (month < 0)\n            month = 0;\n\n        int years = month / 12;\n        month = month % 12;\n\n        int days = getDaysPerMonth(month);\n        if (mDay > days)\n            mDay = days;\n\n        mMonth = month;\n\n        if (years > 0)\n            mYear += years;\n    }\n\n    void DateTimeManager::advanceTime(double hours, Globals& globalVariables)\n    {\n        hours += mGameHour;\n        setHour(hours);\n\n        int days = static_cast<int>(hours / 24);\n        if (days > 0)\n            mDaysPassed += days;\n\n        globalVariables[\"gamehour\"].setFloat(mGameHour);\n        globalVariables[\"dayspassed\"].setInteger(mDaysPassed);\n        globalVariables[\"day\"].setInteger(mDay);\n        globalVariables[\"month\"].setInteger(mMonth);\n        globalVariables[\"year\"].setInteger(mYear);\n    }\n\n    std::string DateTimeManager::getMonthName(int month) const\n    {\n        if (month == -1)\n            month = mMonth;\n\n        const int months = 12;\n        if (month < 0 || month >= months)\n            return std::string();\n\n        static const char *monthNames[months] =\n        {\n            \"sMonthMorningstar\", \"sMonthSunsdawn\", \"sMonthFirstseed\", \"sMonthRainshand\",\n            \"sMonthSecondseed\", \"sMonthMidyear\", \"sMonthSunsheight\", \"sMonthLastseed\",\n            \"sMonthHeartfire\", \"sMonthFrostfall\", \"sMonthSunsdusk\", \"sMonthEveningstar\"\n        };\n\n        const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(monthNames[month]);\n        return setting->mValue.getString();\n    }\n\n    bool DateTimeManager::updateGlobalFloat(const std::string& name, float value)\n    {\n        if (name==\"gamehour\")\n        {\n            setHour(value);\n            return true;\n        }\n        else if (name==\"day\")\n        {\n            setDay(static_cast<int>(value));\n            return true;\n        }\n        else if (name==\"month\")\n        {\n            setMonth(static_cast<int>(value));\n            return true;\n        }\n        else if (name==\"year\")\n        {\n            mYear = static_cast<int>(value);\n        }\n        else if (name==\"timescale\")\n        {\n            mTimeScale = value;\n        }\n        else if (name==\"dayspassed\")\n        {\n            mDaysPassed = static_cast<int>(value);\n        }\n\n        return false;\n    }\n\n    bool DateTimeManager::updateGlobalInt(const std::string& name, int value)\n    {\n        if (name==\"gamehour\")\n        {\n            setHour(static_cast<float>(value));\n            return true;\n        }\n        else if (name==\"day\")\n        {\n            setDay(value);\n            return true;\n        }\n        else if (name==\"month\")\n        {\n            setMonth(value);\n            return true;\n        }\n        else if (name==\"year\")\n        {\n            mYear = value;\n        }\n        else if (name==\"timescale\")\n        {\n            mTimeScale = static_cast<float>(value);\n        }\n        else if (name==\"dayspassed\")\n        {\n            mDaysPassed = value;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/datetimemanager.hpp",
    "content": "#ifndef GAME_MWWORLD_DATETIMEMANAGER_H\n#define GAME_MWWORLD_DATETIMEMANAGER_H\n\n#include <string>\n\nnamespace ESM\n{\n    struct EpochTimeStamp;\n}\n\nnamespace MWWorld\n{\n    class Globals;\n    class TimeStamp;\n\n    class DateTimeManager\n    {\n        int mDaysPassed = 0;\n        int mDay = 0;\n        int mMonth = 0;\n        int mYear = 0;\n        float mGameHour = 0.f;\n        float mTimeScale = 0.f;\n\n        void setHour(double hour);\n        void setDay(int day);\n        void setMonth(int month);\n\n    public:\n        std::string getMonthName(int month) const;\n        TimeStamp getTimeStamp() const;\n        ESM::EpochTimeStamp getEpochTimeStamp() const;\n        float getTimeScaleFactor() const;\n\n        void advanceTime(double hours, Globals& globalVariables);\n\n        void setup(Globals& globalVariables);\n        bool updateGlobalInt(const std::string& name, int value);\n        bool updateGlobalFloat(const std::string& name, float value);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/doorstate.hpp",
    "content": "#ifndef GAME_MWWORLD_DOORSTATE_H\n#define GAME_MWWORLD_DOORSTATE_H\n\nnamespace MWWorld\n{\n    enum class DoorState\n    {\n        Idle = 0,\n        Opening = 1,\n        Closing = 2,\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/esmloader.cpp",
    "content": "#include \"esmloader.hpp\"\n#include \"esmstore.hpp\"\n\n#include <components/esm/esmreader.hpp>\n\nnamespace MWWorld\n{\n\nEsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,\n  ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener)\n  : ContentLoader(listener)\n  , mEsm(readers)\n  , mStore(store)\n  , mEncoder(encoder)\n{\n}\n\nvoid EsmLoader::load(const boost::filesystem::path& filepath, int& index)\n{\n  ContentLoader::load(filepath.filename(), index);\n\n  ESM::ESMReader lEsm;\n  lEsm.setEncoder(mEncoder);\n  lEsm.setIndex(index);\n  lEsm.setGlobalReaderList(&mEsm);\n  lEsm.open(filepath.string());\n  mEsm[index] = lEsm;\n  mStore.load(mEsm[index], &mListener);\n}\n\n} /* namespace MWWorld */\n"
  },
  {
    "path": "apps/openmw/mwworld/esmloader.hpp",
    "content": "#ifndef ESMLOADER_HPP\n#define ESMLOADER_HPP\n\n#include <vector>\n\n#include \"contentloader.hpp\"\n\nnamespace ToUTF8\n{\n  class Utf8Encoder;\n}\n\nnamespace ESM\n{\n    class ESMReader;\n}\n\nnamespace MWWorld\n{\n\nclass ESMStore;\n\nstruct EsmLoader : public ContentLoader\n{\n    EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,\n      ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener);\n\n    void load(const boost::filesystem::path& filepath, int& index) override;\n\n    private:\n      std::vector<ESM::ESMReader>& mEsm;\n      MWWorld::ESMStore& mStore;\n      ToUTF8::Utf8Encoder* mEncoder;\n};\n\n} /* namespace MWWorld */\n\n#endif // ESMLOADER_HPP\n"
  },
  {
    "path": "apps/openmw/mwworld/esmstore.cpp",
    "content": "#include \"esmstore.hpp\"\n\n#include <algorithm>\n#include <set>\n\n#include <boost/filesystem/operations.hpp>\n\n#include <components/debug/debuglog.hpp>\n#include <components/loadinglistener/loadinglistener.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/misc/algorithm.hpp>\n\n#include \"../mwmechanics/spelllist.hpp\"\n\nnamespace\n{\n    struct Ref\n    {\n        ESM::RefNum mRefNum;\n        std::size_t mRefID;\n\n        Ref(ESM::RefNum refNum, std::size_t refID) : mRefNum(refNum), mRefID(refID) {}\n    };\n\n    constexpr std::size_t deletedRefID = std::numeric_limits<std::size_t>::max();\n\n    void readRefs(const ESM::Cell& cell, std::vector<Ref>& refs, std::vector<std::string>& refIDs, std::vector<ESM::ESMReader>& readers)\n    {\n        for (size_t i = 0; i < cell.mContextList.size(); i++)\n        {\n            size_t index = cell.mContextList[i].index;\n            if (readers.size() <= index)\n                readers.resize(index + 1);\n            cell.restore(readers[index], i);\n            ESM::CellRef ref;\n            ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile;\n            bool deleted = false;\n            while(cell.getNextRef(readers[index], ref, deleted))\n            {\n                if(deleted)\n                    refs.emplace_back(ref.mRefNum, deletedRefID);\n                else if (std::find(cell.mMovedRefs.begin(), cell.mMovedRefs.end(), ref.mRefNum) == cell.mMovedRefs.end())\n                {\n                    refs.emplace_back(ref.mRefNum, refIDs.size());\n                    refIDs.push_back(std::move(ref.mRefID));\n                }\n            }\n        }\n        for(const auto& [value, deleted] : cell.mLeasedRefs)\n        {\n            if(deleted)\n                refs.emplace_back(value.mRefNum, deletedRefID);\n            else\n            {\n                refs.emplace_back(value.mRefNum, refIDs.size());\n                refIDs.push_back(value.mRefID);\n            }\n        }\n    }\n\n    std::vector<ESM::NPC> getNPCsToReplace(const MWWorld::Store<ESM::Faction>& factions, const MWWorld::Store<ESM::Class>& classes, const std::map<std::string, ESM::NPC>& npcs)\n    {\n        // Cache first class from store - we will use it if current class is not found\n        std::string defaultCls;\n        auto it = classes.begin();\n        if (it != classes.end())\n            defaultCls = it->mId;\n        else\n            throw std::runtime_error(\"List of NPC classes is empty!\");\n\n        // Validate NPCs for non-existing class and faction.\n        // We will replace invalid entries by fixed ones\n        std::vector<ESM::NPC> npcsToReplace;\n\n        for (const auto& npcIter : npcs)\n        {\n            ESM::NPC npc = npcIter.second;\n            bool changed = false;\n\n            const std::string npcFaction = npc.mFaction;\n            if (!npcFaction.empty())\n            {\n                const ESM::Faction *fact = factions.search(npcFaction);\n                if (!fact)\n                {\n                    Log(Debug::Verbose) << \"NPC '\" << npc.mId << \"' (\" << npc.mName << \") has nonexistent faction '\" << npc.mFaction << \"', ignoring it.\";\n                    npc.mFaction.clear();\n                    npc.mNpdt.mRank = 0;\n                    changed = true;\n                }\n            }\n\n            std::string npcClass = npc.mClass;\n            if (!npcClass.empty())\n            {\n                const ESM::Class *cls = classes.search(npcClass);\n                if (!cls)\n                {\n                    Log(Debug::Verbose) << \"NPC '\" << npc.mId << \"' (\" << npc.mName << \") has nonexistent class '\" << npc.mClass << \"', using '\" << defaultCls << \"' class as replacement.\";\n                    npc.mClass = defaultCls;\n                    changed = true;\n                }\n            }\n\n            if (changed)\n                npcsToReplace.push_back(npc);\n        }\n\n        return npcsToReplace;\n    }\n}\n\nnamespace MWWorld\n{\n\nstatic bool isCacheableRecord(int id)\n{\n    if (id == ESM::REC_ACTI || id == ESM::REC_ALCH || id == ESM::REC_APPA || id == ESM::REC_ARMO ||\n        id == ESM::REC_BOOK || id == ESM::REC_CLOT || id == ESM::REC_CONT || id == ESM::REC_CREA ||\n        id == ESM::REC_DOOR || id == ESM::REC_INGR || id == ESM::REC_LEVC || id == ESM::REC_LEVI ||\n        id == ESM::REC_LIGH || id == ESM::REC_LOCK || id == ESM::REC_MISC || id == ESM::REC_NPC_ ||\n        id == ESM::REC_PROB || id == ESM::REC_REPA || id == ESM::REC_STAT || id == ESM::REC_WEAP ||\n        id == ESM::REC_BODY)\n    {\n        return true;\n    }\n    return false;\n}\n\nvoid ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)\n{\n    listener->setProgressRange(1000);\n\n    ESM::Dialogue *dialogue = nullptr;\n\n    // Land texture loading needs to use a separate internal store for each plugin.\n    // We set the number of plugins here to avoid continual resizes during loading,\n    // and so we can properly verify if valid plugin indices are being passed to the\n    // LandTexture Store retrieval methods.\n    mLandTextures.resize(esm.getGlobalReaderList()->size());\n\n    /// \\todo Move this to somewhere else. ESMReader?\n    // Cache parent esX files by tracking their indices in the global list of\n    //  all files/readers used by the engine. This will greaty accelerate\n    //  refnumber mangling, as required for handling moved references.\n    const std::vector<ESM::Header::MasterData> &masters = esm.getGameFiles();\n    std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList();\n    for (size_t j = 0; j < masters.size(); j++) {\n        const ESM::Header::MasterData &mast = masters[j];\n        std::string fname = mast.name;\n        int index = ~0;\n        for (int i = 0; i < esm.getIndex(); i++) {\n            const std::string candidate = allPlugins->at(i).getContext().filename;\n            std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();\n            if (Misc::StringUtils::ciEqual(fname, fnamecandidate)) {\n                index = i;\n                break;\n            }\n        }\n        if (index == (int)~0) {\n            // Tried to load a parent file that has not been loaded yet. This is bad,\n            //  the launcher should have taken care of this.\n            std::string fstring = \"File \" + esm.getName() + \" asks for parent file \" + masters[j].name\n                + \", but it has not been loaded yet. Please check your load order.\";\n            esm.fail(fstring);\n        }\n        esm.addParentFileIndex(index);\n    }\n\n    // Loop through all records\n    while(esm.hasMoreRecs())\n    {\n        ESM::NAME n = esm.getRecName();\n        esm.getRecHeader();\n\n        // Look up the record type.\n        std::map<int, StoreBase *>::iterator it = mStores.find(n.intval);\n\n        if (it == mStores.end()) {\n            if (n.intval == ESM::REC_INFO) {\n                if (dialogue)\n                {\n                    dialogue->readInfo(esm, esm.getIndex() != 0);\n                }\n                else\n                {\n                    Log(Debug::Error) << \"Error: info record without dialog\";\n                    esm.skipRecord();\n                }\n            } else if (n.intval == ESM::REC_MGEF) {\n                mMagicEffects.load (esm);\n            } else if (n.intval == ESM::REC_SKIL) {\n                mSkills.load (esm);\n            }\n            else if (n.intval==ESM::REC_FILT || n.intval == ESM::REC_DBGP)\n            {\n                // ignore project file only records\n                esm.skipRecord();\n            }\n            else {\n                std::stringstream error;\n                error << \"Unknown record: \" << n.toString();\n                throw std::runtime_error(error.str());\n            }\n        } else {\n            RecordId id = it->second->load(esm);\n            if (id.mIsDeleted)\n            {\n                it->second->eraseStatic(id.mId);\n                continue;\n            }\n\n            if (n.intval==ESM::REC_DIAL) {\n                dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id.mId));\n            } else {\n                dialogue = nullptr;\n            }\n        }\n        listener->setProgress(static_cast<size_t>(esm.getFileOffset() / (float)esm.getFileSize() * 1000));\n    }\n}\n\nvoid ESMStore::setUp(bool validateRecords)\n{\n    mIds.clear();\n\n    std::map<int, StoreBase *>::iterator storeIt = mStores.begin();\n    for (; storeIt != mStores.end(); ++storeIt) {\n        storeIt->second->setUp();\n\n        if (isCacheableRecord(storeIt->first))\n        {\n            std::vector<std::string> identifiers;\n            storeIt->second->listIdentifier(identifiers);\n\n            for (std::vector<std::string>::const_iterator record = identifiers.begin(); record != identifiers.end(); ++record)\n                mIds[*record] = storeIt->first;\n        }\n    }\n\n    if (mStaticIds.empty())\n        mStaticIds = mIds;\n\n    mSkills.setUp();\n    mMagicEffects.setUp();\n    mAttributes.setUp();\n    mDialogs.setUp();\n\n    if (validateRecords)\n    {\n        validate();\n        countRecords();\n    }\n}\n\nvoid ESMStore::countRecords()\n{\n    if(!mRefCount.empty())\n        return;\n    std::vector<Ref> refs;\n    std::vector<std::string> refIDs;\n    std::vector<ESM::ESMReader> readers;\n    for(auto it = mCells.intBegin(); it != mCells.intEnd(); it++)\n        readRefs(*it, refs, refIDs, readers);\n    for(auto it = mCells.extBegin(); it != mCells.extEnd(); it++)\n        readRefs(*it, refs, refIDs, readers);\n    const auto lessByRefNum = [] (const Ref& l, const Ref& r) { return l.mRefNum < r.mRefNum; };\n    std::stable_sort(refs.begin(), refs.end(), lessByRefNum);\n    const auto equalByRefNum = [] (const Ref& l, const Ref& r) { return l.mRefNum == r.mRefNum; };\n    const auto incrementRefCount = [&] (const Ref& value)\n    {\n        if (value.mRefID != deletedRefID)\n        {\n            std::string& refId = refIDs[value.mRefID];\n            Misc::StringUtils::lowerCaseInPlace(refId);\n            ++mRefCount[std::move(refId)];\n        }\n    };\n    Misc::forEachUnique(refs.rbegin(), refs.rend(), equalByRefNum, incrementRefCount);\n}\n\nint ESMStore::getRefCount(const std::string& id) const\n{\n    const std::string lowerId = Misc::StringUtils::lowerCase(id);\n    auto it = mRefCount.find(lowerId);\n    if(it == mRefCount.end())\n        return 0;\n    return it->second;\n}\n\nvoid ESMStore::validate()\n{\n    std::vector<ESM::NPC> npcsToReplace = getNPCsToReplace(mFactions, mClasses, mNpcs.mStatic);\n\n    for (const ESM::NPC &npc : npcsToReplace)\n    {\n        mNpcs.eraseStatic(npc.mId);\n        mNpcs.insertStatic(npc);\n    }\n\n    // Validate spell effects for invalid arguments\n    std::vector<ESM::Spell> spellsToReplace;\n    for (ESM::Spell spell : mSpells)\n    {\n        if (spell.mEffects.mList.empty())\n            continue;\n\n        bool changed = false;\n        auto iter = spell.mEffects.mList.begin();\n        while (iter != spell.mEffects.mList.end())\n        {\n            const ESM::MagicEffect* mgef = mMagicEffects.search(iter->mEffectID);\n            if (!mgef)\n            {\n                Log(Debug::Verbose) << \"Spell '\" << spell.mId << \"' has an invalid effect (index \" << iter->mEffectID << \") present. Dropping the effect.\";\n                iter = spell.mEffects.mList.erase(iter);\n                changed = true;\n                continue;\n            }\n\n            if (mgef->mData.mFlags & ESM::MagicEffect::TargetSkill)\n            {\n                if (iter->mAttribute != -1)\n                {\n                    iter->mAttribute = -1;\n                    Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<\n                        \" effect of spell '\" << spell.mId << \"' has an attribute argument present. Dropping the argument.\";\n                    changed = true;\n                }\n            }\n            else if (mgef->mData.mFlags & ESM::MagicEffect::TargetAttribute)\n            {\n                if (iter->mSkill != -1)\n                {\n                    iter->mSkill = -1;\n                    Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<\n                        \" effect of spell '\" << spell.mId << \"' has a skill argument present. Dropping the argument.\";\n                    changed = true;\n                }\n            }\n            else if (iter->mSkill != -1 || iter->mAttribute != -1)\n            {\n                iter->mSkill = -1;\n                iter->mAttribute = -1;\n                Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<\n                    \" effect of spell '\" << spell.mId << \"' has argument(s) present. Dropping the argument(s).\";\n                changed = true;\n            }\n\n            ++iter;\n        }\n\n        if (changed)\n            spellsToReplace.emplace_back(spell);\n    }\n\n    for (const ESM::Spell &spell : spellsToReplace)\n    {\n        mSpells.eraseStatic(spell.mId);\n        mSpells.insertStatic(spell);\n    }\n}\n\nvoid ESMStore::validateDynamic()\n{\n    std::vector<ESM::NPC> npcsToReplace = getNPCsToReplace(mFactions, mClasses, mNpcs.mDynamic);\n\n    for (const ESM::NPC &npc : npcsToReplace)\n        mNpcs.insert(npc);\n}\n\n    int ESMStore::countSavedGameRecords() const\n    {\n        return 1 // DYNA (dynamic name counter)\n            +mPotions.getDynamicSize()\n            +mArmors.getDynamicSize()\n            +mBooks.getDynamicSize()\n            +mClasses.getDynamicSize()\n            +mClothes.getDynamicSize()\n            +mEnchants.getDynamicSize()\n            +mNpcs.getDynamicSize()\n            +mSpells.getDynamicSize()\n            +mWeapons.getDynamicSize()\n            +mCreatureLists.getDynamicSize()\n            +mItemLists.getDynamicSize()\n            +mCreatures.getDynamicSize()\n            +mContainers.getDynamicSize();\n    }\n\n    void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n    {\n        writer.startRecord(ESM::REC_DYNA);\n        writer.startSubRecord(\"COUN\");\n        writer.writeT(mDynamicCount);\n        writer.endRecord(\"COUN\");\n        writer.endRecord(ESM::REC_DYNA);\n\n        mPotions.write (writer, progress);\n        mArmors.write (writer, progress);\n        mBooks.write (writer, progress);\n        mClasses.write (writer, progress);\n        mClothes.write (writer, progress);\n        mEnchants.write (writer, progress);\n        mSpells.write (writer, progress);\n        mWeapons.write (writer, progress);\n        mNpcs.write (writer, progress);\n        mItemLists.write (writer, progress);\n        mCreatureLists.write (writer, progress);\n        mCreatures.write (writer, progress);\n        mContainers.write (writer, progress);\n    }\n\n    bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type)\n    {\n        switch (type)\n        {\n            case ESM::REC_ALCH:\n            case ESM::REC_ARMO:\n            case ESM::REC_BOOK:\n            case ESM::REC_CLAS:\n            case ESM::REC_CLOT:\n            case ESM::REC_ENCH:\n            case ESM::REC_SPEL:\n            case ESM::REC_WEAP:\n            case ESM::REC_LEVI:\n            case ESM::REC_LEVC:\n                mStores[type]->read (reader);\n                return true;\n            case ESM::REC_NPC_:\n            case ESM::REC_CREA:\n            case ESM::REC_CONT:\n                mStores[type]->read (reader, true);\n                return true;\n\n            case ESM::REC_DYNA:\n                reader.getSubNameIs(\"COUN\");\n                reader.getHT(mDynamicCount);\n                return true;\n\n            default:\n\n                return false;\n        }\n    }\n\n    void ESMStore::checkPlayer()\n    {\n        setUp();\n\n        const ESM::NPC *player = mNpcs.find (\"player\");\n\n        if (!mRaces.find (player->mRace) ||\n            !mClasses.find (player->mClass))\n            throw std::runtime_error (\"Invalid player record (race or class unavailable\");\n    }\n\n    std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> ESMStore::getSpellList(const std::string& originalId) const\n    {\n        const std::string id = Misc::StringUtils::lowerCase(originalId);\n        auto result = mSpellListCache.find(id);\n        std::shared_ptr<MWMechanics::SpellList> ptr;\n        if (result != mSpellListCache.end())\n            ptr = result->second.lock();\n        if (!ptr)\n        {\n            int type = find(id);\n            ptr = std::make_shared<MWMechanics::SpellList>(id, type);\n            if (result != mSpellListCache.end())\n                result->second = ptr;\n            else\n                mSpellListCache.insert({id, ptr});\n            return {ptr, false};\n        }\n        return {ptr, true};\n    }\n} // end namespace\n"
  },
  {
    "path": "apps/openmw/mwworld/esmstore.hpp",
    "content": "#ifndef OPENMW_MWWORLD_ESMSTORE_H\n#define OPENMW_MWWORLD_ESMSTORE_H\n\n#include <memory>\n#include <sstream>\n#include <stdexcept>\n#include <unordered_map>\n\n#include <components/esm/records.hpp>\n#include \"store.hpp\"\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWMechanics\n{\n    class SpellList;\n}\n\nnamespace MWWorld\n{\n    class ESMStore\n    {\n        Store<ESM::Activator>       mActivators;\n        Store<ESM::Potion>          mPotions;\n        Store<ESM::Apparatus>       mAppas;\n        Store<ESM::Armor>           mArmors;\n        Store<ESM::BodyPart>        mBodyParts;\n        Store<ESM::Book>            mBooks;\n        Store<ESM::BirthSign>       mBirthSigns;\n        Store<ESM::Class>           mClasses;\n        Store<ESM::Clothing>        mClothes;\n        Store<ESM::Container>       mContainers;\n        Store<ESM::Creature>        mCreatures;\n        Store<ESM::Dialogue>        mDialogs;\n        Store<ESM::Door>            mDoors;\n        Store<ESM::Enchantment>     mEnchants;\n        Store<ESM::Faction>         mFactions;\n        Store<ESM::Global>          mGlobals;\n        Store<ESM::Ingredient>      mIngreds;\n        Store<ESM::CreatureLevList> mCreatureLists;\n        Store<ESM::ItemLevList>     mItemLists;\n        Store<ESM::Light>           mLights;\n        Store<ESM::Lockpick>        mLockpicks;\n        Store<ESM::Miscellaneous>   mMiscItems;\n        Store<ESM::NPC>             mNpcs;\n        Store<ESM::Probe>           mProbes;\n        Store<ESM::Race>            mRaces;\n        Store<ESM::Region>          mRegions;\n        Store<ESM::Repair>          mRepairs;\n        Store<ESM::SoundGenerator>  mSoundGens;\n        Store<ESM::Sound>           mSounds;\n        Store<ESM::Spell>           mSpells;\n        Store<ESM::StartScript>     mStartScripts;\n        Store<ESM::Static>          mStatics;\n        Store<ESM::Weapon>          mWeapons;\n\n        Store<ESM::GameSetting>     mGameSettings;\n        Store<ESM::Script>          mScripts;\n\n        // Lists that need special rules\n        Store<ESM::Cell>        mCells;\n        Store<ESM::Land>        mLands;\n        Store<ESM::LandTexture> mLandTextures;\n        Store<ESM::Pathgrid>    mPathgrids;\n\n        Store<ESM::MagicEffect> mMagicEffects;\n        Store<ESM::Skill>       mSkills;\n\n        // Special entry which is hardcoded and not loaded from an ESM\n        Store<ESM::Attribute>   mAttributes;\n\n        // Lookup of all IDs. Makes looking up references faster. Just\n        // maps the id name to the record type.\n        std::map<std::string, int> mIds;\n        std::map<std::string, int> mStaticIds;\n\n        std::unordered_map<std::string, int> mRefCount;\n\n        std::map<int, StoreBase *> mStores;\n\n        unsigned int mDynamicCount;\n\n        mutable std::map<std::string, std::weak_ptr<MWMechanics::SpellList> > mSpellListCache;\n\n        /// Validate entries in store after setup\n        void validate();\n\n        void countRecords();\n    public:\n        /// \\todo replace with SharedIterator<StoreBase>\n        typedef std::map<int, StoreBase *>::const_iterator iterator;\n\n        iterator begin() const {\n            return mStores.begin();\n        }\n\n        iterator end() const {\n            return mStores.end();\n        }\n\n        /// Look up the given ID in 'all'. Returns 0 if not found.\n        /// \\note id must be in lower case.\n        int find(const std::string &id) const\n        {\n            std::map<std::string, int>::const_iterator it = mIds.find(id);\n            if (it == mIds.end()) {\n                return 0;\n            }\n            return it->second;\n        }\n        int findStatic(const std::string &id) const\n        {\n            std::map<std::string, int>::const_iterator it = mStaticIds.find(id);\n            if (it == mStaticIds.end()) {\n                return 0;\n            }\n            return it->second;\n        }\n\n        ESMStore()\n          : mDynamicCount(0)\n        {\n            mStores[ESM::REC_ACTI] = &mActivators;\n            mStores[ESM::REC_ALCH] = &mPotions;\n            mStores[ESM::REC_APPA] = &mAppas;\n            mStores[ESM::REC_ARMO] = &mArmors;\n            mStores[ESM::REC_BODY] = &mBodyParts;\n            mStores[ESM::REC_BOOK] = &mBooks;\n            mStores[ESM::REC_BSGN] = &mBirthSigns;\n            mStores[ESM::REC_CELL] = &mCells;\n            mStores[ESM::REC_CLAS] = &mClasses;\n            mStores[ESM::REC_CLOT] = &mClothes;\n            mStores[ESM::REC_CONT] = &mContainers;\n            mStores[ESM::REC_CREA] = &mCreatures;\n            mStores[ESM::REC_DIAL] = &mDialogs;\n            mStores[ESM::REC_DOOR] = &mDoors;\n            mStores[ESM::REC_ENCH] = &mEnchants;\n            mStores[ESM::REC_FACT] = &mFactions;\n            mStores[ESM::REC_GLOB] = &mGlobals;\n            mStores[ESM::REC_GMST] = &mGameSettings;\n            mStores[ESM::REC_INGR] = &mIngreds;\n            mStores[ESM::REC_LAND] = &mLands;\n            mStores[ESM::REC_LEVC] = &mCreatureLists;\n            mStores[ESM::REC_LEVI] = &mItemLists;\n            mStores[ESM::REC_LIGH] = &mLights;\n            mStores[ESM::REC_LOCK] = &mLockpicks;\n            mStores[ESM::REC_LTEX] = &mLandTextures;\n            mStores[ESM::REC_MISC] = &mMiscItems;\n            mStores[ESM::REC_NPC_] = &mNpcs;\n            mStores[ESM::REC_PGRD] = &mPathgrids;\n            mStores[ESM::REC_PROB] = &mProbes;\n            mStores[ESM::REC_RACE] = &mRaces;\n            mStores[ESM::REC_REGN] = &mRegions;\n            mStores[ESM::REC_REPA] = &mRepairs;\n            mStores[ESM::REC_SCPT] = &mScripts;\n            mStores[ESM::REC_SNDG] = &mSoundGens;\n            mStores[ESM::REC_SOUN] = &mSounds;\n            mStores[ESM::REC_SPEL] = &mSpells;\n            mStores[ESM::REC_SSCR] = &mStartScripts;\n            mStores[ESM::REC_STAT] = &mStatics;\n            mStores[ESM::REC_WEAP] = &mWeapons;\n\n            mPathgrids.setCells(mCells);\n        }\n\n        void clearDynamic ()\n        {\n            for (std::map<int, StoreBase *>::iterator it = mStores.begin(); it != mStores.end(); ++it)\n                it->second->clearDynamic();\n\n            movePlayerRecord();\n        }\n\n        void movePlayerRecord ()\n        {\n            auto player = mNpcs.find(\"player\");\n            mNpcs.insert(*player);\n        }\n\n        /// Validate entries in store after loading a save\n        void validateDynamic();\n\n        void load(ESM::ESMReader &esm, Loading::Listener* listener);\n\n        template <class T>\n        const Store<T> &get() const {\n            throw std::runtime_error(\"Storage for this type not exist\");\n        }\n\n        /// Insert a custom record (i.e. with a generated ID that will not clash will pre-existing records)\n        template <class T>\n        const T *insert(const T &x)\n        {\n            const std::string id = \"$dynamic\" + std::to_string(mDynamicCount++);\n\n            Store<T> &store = const_cast<Store<T> &>(get<T>());\n            if (store.search(id) != nullptr)\n            {\n                const std::string msg = \"Try to override existing record '\" + id + \"'\";\n                throw std::runtime_error(msg);\n            }\n            T record = x;\n\n            record.mId = id;\n\n            T *ptr = store.insert(record);\n            for (iterator it = mStores.begin(); it != mStores.end(); ++it) {\n                if (it->second == &store) {\n                    mIds[ptr->mId] = it->first;\n                }\n            }\n            return ptr;\n        }\n\n        /// Insert a record with set ID, and allow it to override a pre-existing static record.\n        template <class T>\n        const T *overrideRecord(const T &x) {\n            Store<T> &store = const_cast<Store<T> &>(get<T>());\n\n            T *ptr = store.insert(x);\n            for (iterator it = mStores.begin(); it != mStores.end(); ++it) {\n                if (it->second == &store) {\n                    mIds[ptr->mId] = it->first;\n                }\n            }\n            return ptr;\n        }\n\n        template <class T>\n        const T *insertStatic(const T &x)\n        {\n            const std::string id = \"$dynamic\" + std::to_string(mDynamicCount++);\n\n            Store<T> &store = const_cast<Store<T> &>(get<T>());\n            if (store.search(id) != nullptr)\n            {\n                const std::string msg = \"Try to override existing record '\" + id + \"'\";\n                throw std::runtime_error(msg);\n            }\n            T record = x;\n\n            T *ptr = store.insertStatic(record);\n            for (iterator it = mStores.begin(); it != mStores.end(); ++it) {\n                if (it->second == &store) {\n                    mIds[ptr->mId] = it->first;\n                }\n            }\n            return ptr;\n        }\n\n        // This method must be called once, after loading all master/plugin files. This can only be done\n        //  from the outside, so it must be public.\n        void setUp(bool validateRecords = false);\n\n        int countSavedGameRecords() const;\n\n        void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;\n\n        bool readRecord (ESM::ESMReader& reader, uint32_t type);\n        ///< \\return Known type?\n\n        // To be called when we are done with dynamic record loading\n        void checkPlayer();\n\n        /// @return The number of instances defined in the base files. Excludes changes from the save file.\n        int getRefCount(const std::string& id) const;\n\n        /// Actors with the same ID share spells, abilities, etc.\n        /// @return The shared spell list to use for this actor and whether or not it has already been initialized.\n        std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> getSpellList(const std::string& id) const;\n    };\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to override a Cell record similarly to how\n        other types of records can be overridden\n    */\n    template <>\n    inline const ESM::Cell *ESMStore::overrideRecord<ESM::Cell>(const ESM::Cell &cell) {\n        return mCells.override(cell);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to override a Pathgrid record similarly to how\n        other types of records can be overridden\n    */\n    template <>\n    inline const ESM::Pathgrid* ESMStore::overrideRecord<ESM::Pathgrid>(const ESM::Pathgrid& pathgrid) {\n        return mPathgrids.override(pathgrid);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    template <>\n    inline const ESM::Cell *ESMStore::insert<ESM::Cell>(const ESM::Cell &cell) {\n        return mCells.insert(cell);\n    }\n\n    template <>\n    inline const ESM::NPC *ESMStore::insert<ESM::NPC>(const ESM::NPC &npc)\n    {\n        const std::string id = \"$dynamic\" + std::to_string(mDynamicCount++);\n\n        if (Misc::StringUtils::ciEqual(npc.mId, \"player\"))\n        {\n            return mNpcs.insert(npc);\n        }\n        else if (mNpcs.search(id) != nullptr)\n        {\n            const std::string msg = \"Try to override existing record '\" + id + \"'\";\n            throw std::runtime_error(msg);\n        }\n        ESM::NPC record = npc;\n\n        record.mId = id;\n\n        ESM::NPC *ptr = mNpcs.insert(record);\n        mIds[ptr->mId] = ESM::REC_NPC_;\n        return ptr;\n    }\n\n    template <>\n    inline const Store<ESM::Activator> &ESMStore::get<ESM::Activator>() const {\n        return mActivators;\n    }\n\n    template <>\n    inline const Store<ESM::Potion> &ESMStore::get<ESM::Potion>() const {\n        return mPotions;\n    }\n\n    template <>\n    inline const Store<ESM::Apparatus> &ESMStore::get<ESM::Apparatus>() const {\n        return mAppas;\n    }\n\n    template <>\n    inline const Store<ESM::Armor> &ESMStore::get<ESM::Armor>() const {\n        return mArmors;\n    }\n\n    template <>\n    inline const Store<ESM::BodyPart> &ESMStore::get<ESM::BodyPart>() const {\n        return mBodyParts;\n    }\n\n    template <>\n    inline const Store<ESM::Book> &ESMStore::get<ESM::Book>() const {\n        return mBooks;\n    }\n\n    template <>\n    inline const Store<ESM::BirthSign> &ESMStore::get<ESM::BirthSign>() const {\n        return mBirthSigns;\n    }\n\n    template <>\n    inline const Store<ESM::Class> &ESMStore::get<ESM::Class>() const {\n        return mClasses;\n    }\n\n    template <>\n    inline const Store<ESM::Clothing> &ESMStore::get<ESM::Clothing>() const {\n        return mClothes;\n    }\n\n    template <>\n    inline const Store<ESM::Container> &ESMStore::get<ESM::Container>() const {\n        return mContainers;\n    }\n\n    template <>\n    inline const Store<ESM::Creature> &ESMStore::get<ESM::Creature>() const {\n        return mCreatures;\n    }\n\n    template <>\n    inline const Store<ESM::Dialogue> &ESMStore::get<ESM::Dialogue>() const {\n        return mDialogs;\n    }\n\n    template <>\n    inline const Store<ESM::Door> &ESMStore::get<ESM::Door>() const {\n        return mDoors;\n    }\n\n    template <>\n    inline const Store<ESM::Enchantment> &ESMStore::get<ESM::Enchantment>() const {\n        return mEnchants;\n    }\n\n    template <>\n    inline const Store<ESM::Faction> &ESMStore::get<ESM::Faction>() const {\n        return mFactions;\n    }\n\n    template <>\n    inline const Store<ESM::Global> &ESMStore::get<ESM::Global>() const {\n        return mGlobals;\n    }\n\n    template <>\n    inline const Store<ESM::Ingredient> &ESMStore::get<ESM::Ingredient>() const {\n        return mIngreds;\n    }\n\n    template <>\n    inline const Store<ESM::CreatureLevList> &ESMStore::get<ESM::CreatureLevList>() const {\n        return mCreatureLists;\n    }\n\n    template <>\n    inline const Store<ESM::ItemLevList> &ESMStore::get<ESM::ItemLevList>() const {\n        return mItemLists;\n    }\n\n    template <>\n    inline const Store<ESM::Light> &ESMStore::get<ESM::Light>() const {\n        return mLights;\n    }\n\n    template <>\n    inline const Store<ESM::Lockpick> &ESMStore::get<ESM::Lockpick>() const {\n        return mLockpicks;\n    }\n\n    template <>\n    inline const Store<ESM::Miscellaneous> &ESMStore::get<ESM::Miscellaneous>() const {\n        return mMiscItems;\n    }\n\n    template <>\n    inline const Store<ESM::NPC> &ESMStore::get<ESM::NPC>() const {\n        return mNpcs;\n    }\n\n    template <>\n    inline const Store<ESM::Probe> &ESMStore::get<ESM::Probe>() const {\n        return mProbes;\n    }\n\n    template <>\n    inline const Store<ESM::Race> &ESMStore::get<ESM::Race>() const {\n        return mRaces;\n    }\n\n    template <>\n    inline const Store<ESM::Region> &ESMStore::get<ESM::Region>() const {\n        return mRegions;\n    }\n\n    template <>\n    inline const Store<ESM::Repair> &ESMStore::get<ESM::Repair>() const {\n        return mRepairs;\n    }\n\n    template <>\n    inline const Store<ESM::SoundGenerator> &ESMStore::get<ESM::SoundGenerator>() const {\n        return mSoundGens;\n    }\n\n    template <>\n    inline const Store<ESM::Sound> &ESMStore::get<ESM::Sound>() const {\n        return mSounds;\n    }\n\n    template <>\n    inline const Store<ESM::Spell> &ESMStore::get<ESM::Spell>() const {\n        return mSpells;\n    }\n\n    template <>\n    inline const Store<ESM::StartScript> &ESMStore::get<ESM::StartScript>() const {\n        return mStartScripts;\n    }\n\n    template <>\n    inline const Store<ESM::Static> &ESMStore::get<ESM::Static>() const {\n        return mStatics;\n    }\n\n    template <>\n    inline const Store<ESM::Weapon> &ESMStore::get<ESM::Weapon>() const {\n        return mWeapons;\n    }\n\n    template <>\n    inline const Store<ESM::GameSetting> &ESMStore::get<ESM::GameSetting>() const {\n        return mGameSettings;\n    }\n\n    template <>\n    inline const Store<ESM::Script> &ESMStore::get<ESM::Script>() const {\n        return mScripts;\n    }\n\n    template <>\n    inline const Store<ESM::Cell> &ESMStore::get<ESM::Cell>() const {\n        return mCells;\n    }\n\n    template <>\n    inline const Store<ESM::Land> &ESMStore::get<ESM::Land>() const {\n        return mLands;\n    }\n\n    template <>\n    inline const Store<ESM::LandTexture> &ESMStore::get<ESM::LandTexture>() const {\n        return mLandTextures;\n    }\n\n    template <>\n    inline const Store<ESM::Pathgrid> &ESMStore::get<ESM::Pathgrid>() const {\n        return mPathgrids;\n    }\n\n    template <>\n    inline const Store<ESM::MagicEffect> &ESMStore::get<ESM::MagicEffect>() const {\n        return mMagicEffects;\n    }\n\n    template <>\n    inline const Store<ESM::Skill> &ESMStore::get<ESM::Skill>() const {\n        return mSkills;\n    }\n\n    template <>\n    inline const Store<ESM::Attribute> &ESMStore::get<ESM::Attribute>() const {\n        return mAttributes;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/failedaction.cpp",
    "content": "#include \"failedaction.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\nnamespace MWWorld\n{\n    FailedAction::FailedAction(const std::string &msg, const Ptr& target)\n      : Action(false, target), mMessage(msg)\n    {   }\n\n    void FailedAction::executeImp(const Ptr &actor)\n    {\n        if(actor == MWMechanics::getPlayer() && !mMessage.empty())\n            MWBase::Environment::get().getWindowManager()->messageBox(mMessage);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/failedaction.hpp",
    "content": "#ifndef GAME_MWWORLD_FAILEDACTION_H\n#define GAME_MWWORLD_FAILEDACTION_H\n\n#include \"action.hpp\"\n#include \"ptr.hpp\"\n\nnamespace MWWorld\n{\n    class FailedAction : public Action\n    {\n        std::string mMessage;\n\n        void executeImp(const Ptr &actor) override;\n\n    public:\n        FailedAction(const std::string &message = std::string(), const Ptr& target = Ptr());\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/globals.cpp",
    "content": "#include \"globals.hpp\"\n\n#include <stdexcept>\n\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/misc/stringops.hpp>\n\n#include \"esmstore.hpp\"\n\nnamespace MWWorld\n{\n    Globals::Collection::const_iterator Globals::find (const std::string& name) const\n    {\n        Collection::const_iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name));\n\n        if (iter==mVariables.end())\n            throw std::runtime_error (\"unknown global variable: \" + name);\n\n        return iter;\n    }\n\n    Globals::Collection::iterator Globals::find (const std::string& name)\n    {\n        Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name));\n\n        if (iter==mVariables.end())\n            throw std::runtime_error (\"unknown global variable: \" + name);\n\n        return iter;\n    }\n\n    void Globals::fill (const MWWorld::ESMStore& store)\n    {\n        mVariables.clear();\n\n        const MWWorld::Store<ESM::Global>& globals = store.get<ESM::Global>();\n\n        for (const ESM::Global& esmGlobal : globals)\n        {\n            mVariables.insert (std::make_pair (Misc::StringUtils::lowerCase (esmGlobal.mId), esmGlobal));\n        }\n    }\n\n    const ESM::Variant& Globals::operator[] (const std::string& name) const\n    {\n        return find (Misc::StringUtils::lowerCase (name))->second.mValue;\n    }\n\n    ESM::Variant& Globals::operator[] (const std::string& name)\n    {\n        return find (Misc::StringUtils::lowerCase (name))->second.mValue;\n    }\n\n    char Globals::getType (const std::string& name) const\n    {\n        Collection::const_iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name));\n\n        if (iter==mVariables.end())\n            return ' ';\n\n        switch (iter->second.mValue.getType())\n        {\n            case ESM::VT_Short: return 's';\n            case ESM::VT_Long: return 'l';\n            case ESM::VT_Float: return 'f';\n\n            default: return ' ';\n        }\n    }\n\n    int Globals::countSavedGameRecords() const\n    {\n        return mVariables.size();\n    }\n\n    void Globals::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n    {\n        for (Collection::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter)\n        {\n            writer.startRecord (ESM::REC_GLOB);\n            iter->second.save (writer);\n            writer.endRecord (ESM::REC_GLOB);\n        }\n    }\n\n    bool Globals::readRecord (ESM::ESMReader& reader,  uint32_t type)\n    {\n        if (type==ESM::REC_GLOB)\n        {\n            ESM::Global global;\n            bool isDeleted = false;\n\n            // This readRecord() method is used when reading a saved game.\n            // Deleted globals can't appear there, so isDeleted will be ignored here.\n            global.load(reader, isDeleted);\n            Misc::StringUtils::lowerCaseInPlace(global.mId);\n\n            Collection::iterator iter = mVariables.find (global.mId);\n            if (iter!=mVariables.end())\n                iter->second = global;\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to add a global record from elsewhere\n    */\n    void Globals::addRecord(const ESM::Global global)\n    {\n        mVariables.insert(std::make_pair(Misc::StringUtils::lowerCase(global.mId), global));\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check whether a global exists\n    */\n    bool Globals::hasRecord(const std::string& name)\n    {\n        return (mVariables.find(name) != mVariables.end());\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/globals.hpp",
    "content": "#ifndef GAME_MWWORLD_GLOBALS_H\n#define GAME_MWWORLD_GLOBALS_H\n\n#include <vector>\n#include <string>\n#include <map>\n\n#include <stdint.h>\n\n#include <components/esm/loadglob.hpp>\n\nnamespace ESM\n{\n    class ESMWriter;\n    class ESMReader;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWWorld\n{\n    class ESMStore;\n\n    class Globals\n    {\n        private:\n\n            typedef std::map<std::string, ESM::Global> Collection;\n\n            Collection mVariables; // type, value\n\n            Collection::const_iterator find (const std::string& name) const;\n\n            Collection::iterator find (const std::string& name);\n\n        public:\n\n            const ESM::Variant& operator[] (const std::string& name) const;\n\n            ESM::Variant& operator[] (const std::string& name);\n\n            char getType (const std::string& name) const;\n            ///< If there is no global variable with this name, ' ' is returned.\n\n            void fill (const MWWorld::ESMStore& store);\n            ///< Replace variables with variables from \\a store with default values.\n\n            int countSavedGameRecords() const;\n\n            void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;\n\n            bool readRecord (ESM::ESMReader& reader, uint32_t type);\n            ///< Records for variables that do not exist are dropped silently.\n            ///\n            /// \\return Known type?\n        \n            /*\n                Start of tes3mp addition\n\n                Make it possible to add a global record from elsewhere\n            */\n            void addRecord(const ESM::Global global);\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a global exists\n            */\n            bool hasRecord(const std::string& name);\n            /*\n                End of tes3mp addition\n            */\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/inventorystore.cpp",
    "content": "#include \"inventorystore.hpp\"\n\n#include <iterator>\n#include <algorithm>\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/loadench.hpp>\n#include <components/esm/inventorystate.hpp>\n#include <components/misc/rng.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/spellresistance.hpp\"\n#include \"../mwmechanics/spellutil.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"esmstore.hpp\"\n#include \"class.hpp\"\n\nvoid MWWorld::InventoryStore::copySlots (const InventoryStore& store)\n{\n    // some const-trickery, required because of a flaw in the handling of MW-references and the\n    // resulting workarounds\n    for (std::vector<ContainerStoreIterator>::const_iterator iter (\n        const_cast<InventoryStore&> (store).mSlots.begin());\n        iter!=const_cast<InventoryStore&> (store).mSlots.end(); ++iter)\n    {\n        std::size_t distance = std::distance (const_cast<InventoryStore&> (store).begin(), *iter);\n\n        ContainerStoreIterator slot = begin();\n\n        std::advance (slot, distance);\n\n        mSlots.push_back (slot);\n    }\n\n    // some const-trickery, required because of a flaw in the handling of MW-references and the\n    // resulting workarounds\n    std::size_t distance = std::distance (const_cast<InventoryStore&> (store).begin(), const_cast<InventoryStore&> (store).mSelectedEnchantItem);\n    ContainerStoreIterator slot = begin();\n    std::advance (slot, distance);\n    mSelectedEnchantItem = slot;\n}\n\nvoid MWWorld::InventoryStore::initSlots (TSlots& slots_)\n{\n    for (int i=0; i<Slots; ++i)\n        slots_.push_back (end());\n}\n\nvoid MWWorld::InventoryStore::storeEquipmentState(const MWWorld::LiveCellRefBase &ref, int index, ESM::InventoryState &inventory) const\n{\n    for (int i = 0; i<static_cast<int> (mSlots.size()); ++i)\n        if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref)\n        {\n            inventory.mEquipmentSlots[index] = i;\n        }\n\n    if (mSelectedEnchantItem.getType()!=-1 && mSelectedEnchantItem->getBase() == &ref)\n        inventory.mSelectedEnchantItem = index;\n}\n\nvoid MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIterator &iter, int index, const ESM::InventoryState &inventory)\n{\n    if (index == inventory.mSelectedEnchantItem)\n        mSelectedEnchantItem = iter;\n\n    std::map<int, int>::const_iterator found = inventory.mEquipmentSlots.find(index);\n    if (found != inventory.mEquipmentSlots.end())\n    {\n        if (found->second < 0 || found->second >= MWWorld::InventoryStore::Slots)\n            throw std::runtime_error(\"Invalid slot index in inventory state\");\n\n        // make sure the item can actually be equipped in this slot\n        int slot = found->second;\n        std::pair<std::vector<int>, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter);\n        if (!allowedSlots.first.size())\n            return;\n        if (std::find(allowedSlots.first.begin(), allowedSlots.first.end(), slot) == allowedSlots.first.end())\n            slot = allowedSlots.first.front();\n\n        // unstack if required\n        if (!allowedSlots.second && iter->getRefData().getCount() > 1)\n        {\n            int count = iter->getRefData().getCount(false);\n            MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, count > 0 ? 1 : -1);\n            iter->getRefData().setCount(subtractItems(count, 1));\n            mSlots[slot] = newIter;\n        }\n        else\n            mSlots[slot] = iter;\n    }\n}\n\nMWWorld::InventoryStore::InventoryStore()\n : ContainerStore()\n , mInventoryListener(nullptr)\n , mUpdatesEnabled (true)\n , mFirstAutoEquip(true)\n , mSelectedEnchantItem(end())\n{\n    initSlots (mSlots);\n}\n\nMWWorld::InventoryStore::InventoryStore (const InventoryStore& store)\n : ContainerStore (store)\n , mMagicEffects(store.mMagicEffects)\n , mInventoryListener(store.mInventoryListener)\n , mUpdatesEnabled(store.mUpdatesEnabled)\n , mFirstAutoEquip(store.mFirstAutoEquip)\n , mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)\n , mSelectedEnchantItem(end())\n{\n    copySlots (store);\n}\n\nMWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store)\n{\n    if (this == &store)\n        return *this;\n\n    mListener = store.mListener;\n    mInventoryListener = store.mInventoryListener;\n    mMagicEffects = store.mMagicEffects;\n    mFirstAutoEquip = store.mFirstAutoEquip;\n    mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes;\n    mRechargingItemsUpToDate = false;\n    ContainerStore::operator= (store);\n    mSlots.clear();\n    copySlots (store);\n    return *this;\n}\n\nMWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip, bool resolve)\n{\n    const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, allowAutoEquip, resolve);\n\n    // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves\n    if (allowAutoEquip && actorPtr != MWMechanics::getPlayer()\n            && actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())\n    {\n        std::string type = itemPtr.getTypeName();\n        if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name())\n            autoEquip(actorPtr);\n    }\n\n    /*\n        Start of tes3mp change (major)\n\n        Only fire inventory events for actors in loaded cells to avoid crashes\n    */\n    if (mListener && MWBase::Environment::get().getWorld()->isCellActive(*actorPtr.getCell()->getCell()))\n        mListener->itemAdded(*retVal, count);\n    /*\n        End of tes3mp change (major)\n    */\n\n    return retVal;\n}\n\nvoid MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor)\n{\n    if (iterator == end())\n        throw std::runtime_error (\"can't equip end() iterator, use unequip function instead\");\n\n    if (slot<0 || slot>=static_cast<int> (mSlots.size()))\n        throw std::runtime_error (\"slot number out of range\");\n\n    if (iterator.getContainerStore()!=this)\n        throw std::runtime_error (\"attempt to equip an item that is not in the inventory\");\n\n    std::pair<std::vector<int>, bool> slots_;\n\n    slots_ = iterator->getClass().getEquipmentSlots (*iterator);\n\n    if (std::find (slots_.first.begin(), slots_.first.end(), slot)==slots_.first.end())\n        throw std::runtime_error (\"invalid slot\");\n\n    if (mSlots[slot] != end())\n        unequipSlot(slot, actor);\n\n    // unstack item pointed to by iterator if required\n    if (iterator!=end() && !slots_.second && iterator->getRefData().getCount() > 1) // if slots.second is true, item can stay stacked when equipped\n    {\n        unstack(*iterator, actor);\n    }\n\n    mSlots[slot] = iterator;\n\n    flagAsModified();\n\n    fireEquipmentChangedEvent(actor);\n\n    updateMagicEffects(actor);\n}\n\nvoid MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor)\n{\n    mUpdatesEnabled = false;\n    for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot)\n        unequipSlot(slot, actor);\n\n    mUpdatesEnabled = true;\n\n    fireEquipmentChangedEvent(actor);\n    updateMagicEffects(actor);\n}\n\nMWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)\n{\n    return findSlot (slot);\n}\n\nMWWorld::ConstContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) const\n{\n    return findSlot (slot);\n}\n\nMWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot (int slot) const\n{\n    if (slot<0 || slot>=static_cast<int> (mSlots.size()))\n        throw std::runtime_error (\"slot number out of range\");\n\n    if (mSlots[slot]==end())\n        return mSlots[slot];\n\n    if (mSlots[slot]->getRefData().getCount()<1)\n    {\n        // Object has been deleted\n        // This should no longer happen, since the new remove function will unequip first\n\n        /*\n            Start of tes3mp change (major)\n\n            Instead of throwing an error, display an error log message with information about\n            the item\n        */\n        //throw std::runtime_error(\"Invalid slot, make sure you are not calling RefData::setCount for a container object\");\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Invalid slot, make sure you are not calling RefData::setCount for a container object\\n- item was %s\",\n            mSlots[slot]->getCellRef().getRefId().c_str());\n        /*\n            End of tes3mp change (major)\n        */\n    }\n\n    return mSlots[slot];\n}\n\nvoid MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots& slots_)\n{\n    if (!actor.getClass().isNpc())\n    {\n        // In original game creatures do not autoequip weapon, but we need it for weapon sheathing.\n        // The only case when the difference is noticable - when this creature sells weapon.\n        // So just disable weapon autoequipping for creatures which sells weapon.\n        int services = actor.getClass().getServices(actor);\n        bool sellsWeapon = services & (ESM::NPC::Weapon|ESM::NPC::MagicItems);\n        if (sellsWeapon)\n            return;\n    }\n\n    static const ESM::Skill::SkillEnum weaponSkills[] =\n    {\n        ESM::Skill::LongBlade,\n        ESM::Skill::Axe,\n        ESM::Skill::Spear,\n        ESM::Skill::ShortBlade,\n        ESM::Skill::Marksman,\n        ESM::Skill::BluntWeapon\n    };\n    const size_t weaponSkillsLength = sizeof(weaponSkills) / sizeof(weaponSkills[0]);\n\n    bool weaponSkillVisited[weaponSkillsLength] = { false };\n\n    // give arrows/bolt with max damage by default\n    int arrowMax = 0;\n    int boltMax = 0;\n    ContainerStoreIterator arrow(end());\n    ContainerStoreIterator bolt(end());\n\n    // rate ammo\n    for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter)\n    {\n        const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->mBase;\n\n        if (esmWeapon->mData.mType == ESM::Weapon::Arrow)\n        {\n            if (esmWeapon->mData.mChop[1] >= arrowMax)\n            {\n                arrowMax = esmWeapon->mData.mChop[1];\n                arrow = iter;\n            }\n        }\n        else if (esmWeapon->mData.mType == ESM::Weapon::Bolt)\n        {\n            if (esmWeapon->mData.mChop[1] >= boltMax)\n            {\n                boltMax = esmWeapon->mData.mChop[1];\n                bolt = iter;\n            }\n        }\n    }\n\n    // rate weapon\n    for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i)\n    {\n        float max = 0;\n        int maxWeaponSkill = -1;\n\n        for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)\n        {\n            float skillValue = actor.getClass().getSkill(actor, static_cast<int>(weaponSkills[j]));\n            if (skillValue > max && !weaponSkillVisited[j])\n            {\n                max = skillValue;\n                maxWeaponSkill = j;\n            }\n        }\n\n        if (maxWeaponSkill == -1)\n            break;\n\n        max = 0;\n        ContainerStoreIterator weapon(end());\n\n        for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter)\n        {\n            const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->mBase;\n\n            if (MWMechanics::getWeaponType(esmWeapon->mData.mType)->mWeaponClass == ESM::WeaponType::Ammo)\n                continue;\n\n            if (iter->getClass().getEquipmentSkill(*iter) == weaponSkills[maxWeaponSkill])\n            {\n                if (esmWeapon->mData.mChop[1] >= max)\n                {\n                    max = esmWeapon->mData.mChop[1];\n                    weapon = iter;\n                }\n\n                if (esmWeapon->mData.mSlash[1] >= max)\n                {\n                    max = esmWeapon->mData.mSlash[1];\n                    weapon = iter;\n                }\n\n                if (esmWeapon->mData.mThrust[1] >= max)\n                {\n                    max = esmWeapon->mData.mThrust[1];\n                    weapon = iter;\n                }\n            }\n        }\n\n        if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first)\n        {\n            // Do not equip ranged weapons, if there is no suitable ammo\n            bool hasAmmo = true;\n            const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();\n            int type = ref->mBase->mData.mType;\n            int ammotype = MWMechanics::getWeaponType(type)->mAmmoType;\n            if (ammotype == ESM::Weapon::Arrow)\n            {\n                if (arrow == end())\n                    hasAmmo = false;\n                else\n                    slots_[Slot_Ammunition] = arrow;\n            }\n            else if (ammotype == ESM::Weapon::Bolt)\n            {\n                if (bolt == end())\n                    hasAmmo = false;\n                else\n                    slots_[Slot_Ammunition] = bolt;\n            }\n\n            if (hasAmmo)\n            {\n                std::pair<std::vector<int>, bool> itemsSlots = weapon->getClass().getEquipmentSlots (*weapon);\n\n                if (!itemsSlots.first.empty())\n                {\n                    if (!itemsSlots.second)\n                    {\n                        if (weapon->getRefData().getCount() > 1)\n                        {\n                            unstack(*weapon, actor);\n                        }\n                    }\n\n                    int slot = itemsSlots.first.front();\n                    slots_[slot] = weapon;\n\n                    if (ammotype == ESM::Weapon::None)\n                        slots_[Slot_Ammunition] = end();\n                }\n\n                break;\n            }\n        }\n\n        weaponSkillVisited[maxWeaponSkill] = true;\n    }\n}\n\nvoid MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots& slots_)\n{\n    // Only NPCs can wear armor for now.\n    // For creatures we equip only shields.\n    if (!actor.getClass().isNpc())\n    {\n        autoEquipShield(actor, slots_);\n        return;\n    }\n\n    const MWBase::World *world = MWBase::Environment::get().getWorld();\n    const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();\n\n    static float fUnarmoredBase1 = store.find(\"fUnarmoredBase1\")->mValue.getFloat();\n    static float fUnarmoredBase2 = store.find(\"fUnarmoredBase2\")->mValue.getFloat();\n\n    float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored);\n    float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);\n\n    for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter)\n    {\n        Ptr test = *iter;\n\n        switch(test.getClass().canBeEquipped (test, actor).first)\n        {\n            case 0:\n                continue;\n            default:\n                break;\n        }\n\n        if (iter.getType() == ContainerStore::Type_Armor &&\n                test.getClass().getEffectiveArmorRating(test, actor) <= std::max(unarmoredRating, 0.f))\n        {\n            continue;\n        }\n\n        std::pair<std::vector<int>, bool> itemsSlots =\n            iter->getClass().getEquipmentSlots (*iter);\n\n        // checking if current item pointed by iter can be equipped\n        for (int slot : itemsSlots.first)\n        {\n            // if true then it means slot is equipped already\n            // check if slot may require swapping if current item is more valuable\n            if (slots_.at (slot)!=end())\n            {\n                Ptr old = *slots_.at (slot);\n\n                if (iter.getType() == ContainerStore::Type_Armor)\n                {\n                    if (old.getTypeName() == typeid(ESM::Armor).name())\n                    {\n                        if (old.get<ESM::Armor>()->mBase->mData.mType < test.get<ESM::Armor>()->mBase->mData.mType)\n                            continue;\n\n                        if (old.get<ESM::Armor>()->mBase->mData.mType == test.get<ESM::Armor>()->mBase->mData.mType)\n                        {\n                            if (old.getClass().getEffectiveArmorRating(old, actor) >= test.getClass().getEffectiveArmorRating(test, actor))\n                                // old armor had better armor rating\n                                continue;\n                        }\n                    }\n                    // suitable armor should replace already equipped clothing\n                }\n                else if (iter.getType() == ContainerStore::Type_Clothing)\n                {\n                    // if left ring is equipped\n                    if (slot == Slot_LeftRing)\n                    {\n                        // if there is a place for right ring dont swap it\n                        if (slots_.at(Slot_RightRing) == end())\n                        {\n                            continue;\n                        }\n                        else // if right ring is equipped too\n                        {\n                            Ptr rightRing = *slots_.at(Slot_RightRing);\n\n                            // we want to swap cheaper ring only if both are equipped\n                            if (old.getClass().getValue (old) >= rightRing.getClass().getValue (rightRing))\n                                continue;\n                        }\n                    }\n\n                    if (old.getTypeName() == typeid(ESM::Clothing).name())\n                    {\n                        // check value\n                        if (old.getClass().getValue (old) >= test.getClass().getValue (test))\n                            // old clothing was more valuable\n                            continue;\n                    }\n                    else\n                        // suitable clothing should NOT replace already equipped armor\n                        continue;\n                }\n            }\n\n            if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped\n            {\n                // unstack item pointed to by iterator if required\n                if (iter->getRefData().getCount() > 1)\n                {\n                    unstack(*iter, actor);\n                }\n            }\n\n            // if we are here it means item can be equipped or swapped\n            slots_[slot] = iter;\n            break;\n        }\n    }\n}\n\nvoid MWWorld::InventoryStore::autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_)\n{\n    for (ContainerStoreIterator iter(begin(ContainerStore::Type_Armor)); iter != end(); ++iter)\n    {\n        if (iter->get<ESM::Armor>()->mBase->mData.mType != ESM::Armor::Shield)\n            continue;\n        if (iter->getClass().canBeEquipped(*iter, actor).first != 1)\n            continue;\n        std::pair<std::vector<int>, bool> shieldSlots =\n            iter->getClass().getEquipmentSlots(*iter);\n        int slot = shieldSlots.first[0];\n        const ContainerStoreIterator& shield = slots_[slot];\n        if (shield != end()\n                && shield.getType() == Type_Armor && shield->get<ESM::Armor>()->mBase->mData.mType == ESM::Armor::Shield)\n        {\n            if (shield->getClass().getItemHealth(*shield) >= iter->getClass().getItemHealth(*iter))\n                continue;\n        }\n        slots_[slot] = iter;\n    }\n}\n\nvoid MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)\n{\n    /*\n        Start of tes3mp addition\n\n        We need DedicatedPlayers and DedicatedActors to wear exactly what they're wearing on their\n        authority client, so don't auto-equip for them\n    */\n    if (mwmp::PlayerList::isDedicatedPlayer(actor) || mwmp::Main::get().getCellController()->isDedicatedActor(actor))\n        return;\n    /*\n        End of tes3mp addition\n    */\n\n    TSlots slots_;\n    initSlots (slots_);\n\n    // Disable model update during auto-equip\n    mUpdatesEnabled = false;\n\n    // Autoequip clothing, armor and weapons.\n    // Equipping lights is handled in Actors::updateEquippedLight based on environment light.\n    // Note: creatures ignore equipment armor rating and only equip shields\n    // Use custom logic for them - select shield based on its health instead of armor rating\n    autoEquipWeapon(actor, slots_);\n    autoEquipArmor(actor, slots_);\n\n    bool changed = false;\n\n    for (std::size_t i=0; i<slots_.size(); ++i)\n    {\n        if (slots_[i] != mSlots[i])\n        {\n            changed = true;\n            break;\n        }\n    }\n    mUpdatesEnabled = true;\n\n    if (changed)\n    {\n        mSlots.swap (slots_);\n        fireEquipmentChangedEvent(actor);\n        updateMagicEffects(actor);\n        flagAsModified();\n    }\n}\n\nMWWorld::ContainerStoreIterator MWWorld::InventoryStore::getPreferredShield(const MWWorld::Ptr& actor)\n{\n    TSlots slots;\n    initSlots (slots);\n    autoEquipArmor(actor, slots);\n    return slots[Slot_CarriedLeft];\n}\n\nconst MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects() const\n{\n    return mMagicEffects;\n}\n\nvoid MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)\n{\n    // To avoid excessive updates during auto-equip\n    if (!mUpdatesEnabled)\n        return;\n\n    // Delay update until the listener is set up\n    if (!mInventoryListener)\n        return;\n\n    mMagicEffects = MWMechanics::MagicEffects();\n\n    const auto& stats = actor.getClass().getCreatureStats(actor);\n    if (stats.isDead() && stats.isDeathAnimationFinished())\n        return;\n\n    for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter)\n    {\n        if (*iter==end())\n            continue;\n\n        std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter);\n\n        if (!enchantmentId.empty())\n        {\n            const ESM::Enchantment& enchantment =\n                *MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find (enchantmentId);\n\n            if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)\n                continue;\n\n            std::vector<EffectParams> params;\n\n            bool existed = (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().getRefId()) != mPermanentMagicEffectMagnitudes.end());\n            if (!existed)\n            {\n                params.resize(enchantment.mEffects.mList.size());\n\n                int i=0;\n                for (const ESM::ENAMstruct& effect : enchantment.mEffects.mList)\n                {\n                    int delta = effect.mMagnMax - effect.mMagnMin;\n                    // Roll some dice, one for each effect\n                    if (delta)\n                        params[i].mRandom = Misc::Rng::rollDice(delta + 1) / static_cast<float>(delta);\n                    // Try resisting each effect\n                    params[i].mMultiplier = MWMechanics::getEffectMultiplier(effect.mEffectID, actor, actor);\n                    ++i;\n                }\n\n                // Note that using the RefID as a key here is not entirely correct.\n                // Consider equipping the same item twice (e.g. a ring)\n                // However, permanent enchantments with a random magnitude are kind of an exploit anyway,\n                // so it doesn't really matter if both items will get the same magnitude. *Extreme* edge case.\n                mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()] = params;\n            }\n            else\n                params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()];\n\n            int i=0;\n            for (const ESM::ENAMstruct& effect : enchantment.mEffects.mList)\n            {\n                const ESM::MagicEffect *magicEffect =\n                    MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (\n                    effect.mEffectID);\n\n                // Fully resisted or can't be applied to target?\n                if (params[i].mMultiplier == 0 || !MWMechanics::checkEffectTarget(effect.mEffectID, actor, actor, actor == MWMechanics::getPlayer()))\n                {\n                    i++;\n                    continue;\n                }\n\n                float magnitude = effect.mMagnMin + (effect.mMagnMax - effect.mMagnMin) * params[i].mRandom;\n                magnitude *= params[i].mMultiplier;\n\n                if (!existed)\n                {\n                    // During first auto equip, we don't play any sounds.\n                    // Basically we don't want sounds when the actor is first loaded,\n                    // the items should appear as if they'd always been equipped.\n                    mInventoryListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip);\n                }\n\n                if (magnitude)\n                    mMagicEffects.add (effect, magnitude);\n\n                i++;\n            }\n        }\n    }\n\n    // Now drop expired effects\n    for (TEffectMagnitudes::iterator it = mPermanentMagicEffectMagnitudes.begin();\n         it != mPermanentMagicEffectMagnitudes.end();)\n    {\n        bool found = false;\n        for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter)\n        {\n            if (*iter == end())\n                continue;\n            if ((**iter).getCellRef().getRefId() == it->first)\n            {\n                found = true;\n            }\n        }\n        if (!found)\n            mPermanentMagicEffectMagnitudes.erase(it++);\n        else\n            ++it;\n    }\n\n    // Magic effects are normally not updated when paused, but we need this to make resistances work immediately after equipping\n    MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor);\n\n    mFirstAutoEquip = false;\n}\n\nbool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const\n{\n    bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2);\n    if (!canStack)\n        return false;\n\n    // don't stack if either item is currently equipped\n    for (TSlots::const_iterator iter (mSlots.begin());\n        iter!=mSlots.end(); ++iter)\n    {\n        if (*iter != end() && (ptr1 == **iter || ptr2 == **iter))\n        {\n            bool stackWhenEquipped = (*iter)->getClass().getEquipmentSlots(**iter).second;\n            if (!stackWhenEquipped)\n                return false;\n        }\n    }\n\n    return true;\n}\n\nvoid MWWorld::InventoryStore::setSelectedEnchantItem(const ContainerStoreIterator& iterator)\n{\n    mSelectedEnchantItem = iterator;\n}\n\nMWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem()\n{\n    return mSelectedEnchantItem;\n}\n\nint MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement, bool resolve)\n{\n    int retCount = ContainerStore::remove(item, count, actor, equipReplacement, resolve);\n\n    bool wasEquipped = false;\n    if (!item.getRefData().getCount())\n    {\n        for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot)\n        {\n            if (mSlots[slot] == end())\n                continue;\n\n            if (*mSlots[slot] == item)\n            {\n                unequipSlot(slot, actor);\n                wasEquipped = true;\n                break;\n            }\n        }\n    }\n\n    // If an armor/clothing item is removed, try to find a replacement,\n    // but not for the player nor werewolves, and not if the RemoveItem script command\n    // was used (equipReplacement is false)\n    if (equipReplacement && wasEquipped && (actor != MWMechanics::getPlayer())\n            && actor.getClass().isNpc() && !actor.getClass().getNpcStats(actor).isWerewolf())\n    {\n        std::string type = item.getTypeName();\n        if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name())\n            autoEquip(actor);\n    }\n\n    if (item.getRefData().getCount() == 0 && mSelectedEnchantItem != end()\n            && *mSelectedEnchantItem == item)\n    {\n        mSelectedEnchantItem = end();\n    }\n\n    /*\n        Start of tes3mp change (major)\n\n        Only fire inventory events for actors in loaded cells to avoid crashes\n    */\n    if (mListener && MWBase::Environment::get().getWorld()->isCellActive(*actor.getCell()->getCell()))\n        mListener->itemRemoved(item, retCount);\n    /*\n        End of tes3mp change (major)\n    */\n\n    return retCount;\n}\n\nMWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor, bool applyUpdates)\n{\n    if (slot<0 || slot>=static_cast<int> (mSlots.size()))\n        throw std::runtime_error (\"slot number out of range\");\n\n    ContainerStoreIterator it = mSlots[slot];\n\n    if (it != end())\n    {\n        ContainerStoreIterator retval = it;\n\n        // empty this slot\n        mSlots[slot] = end();\n\n        if (it->getRefData().getCount())\n        {\n            retval = restack(*it);\n\n            if (actor == MWMechanics::getPlayer())\n            {\n                // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared\n                const std::string& script = it->getClass().getScript(*it);\n                if (script != \"\")\n                    (*it).getRefData().getLocals().setVarByInt(script, \"onpcequip\", 0);\n            }\n\n            if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it))\n            {\n                mSelectedEnchantItem = end();\n            }\n        }\n\n        if (applyUpdates)\n        {\n            fireEquipmentChangedEvent(actor);\n            updateMagicEffects(actor);\n        }\n\n        return retval;\n    }\n\n    return it;\n}\n\nMWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWorld::Ptr& item, const MWWorld::Ptr& actor)\n{\n    for (int slot=0; slot<MWWorld::InventoryStore::Slots; ++slot)\n    {\n        MWWorld::ContainerStoreIterator equipped = getSlot(slot);\n        if (equipped != end() && *equipped == item)\n            return unequipSlot(slot, actor);\n    }\n\n    throw std::runtime_error (\"attempt to unequip an item that is not currently equipped\");\n}\n\nMWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(const Ptr& item, const Ptr& actor, int count)\n{\n    if (!isEquipped(item))\n        throw std::runtime_error (\"attempt to unequip an item that is not currently equipped\");\n    if (count <= 0)\n        throw std::runtime_error (\"attempt to unequip nothing (count <= 0)\");\n    if (count > item.getRefData().getCount())\n        throw std::runtime_error (\"attempt to unequip more items than equipped\");\n\n    if (count == item.getRefData().getCount())\n        return unequipItem(item, actor);\n\n    // Move items to an existing stack if possible, otherwise split count items out into a new stack.\n    // Moving counts manually here, since ContainerStore's restack can't target unequipped stacks.\n    for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)\n    {\n        if (stacks(*iter, item) && !isEquipped(*iter))\n        {\n            iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), count));\n            item.getRefData().setCount(subtractItems(item.getRefData().getCount(false), count));\n            return iter;\n        }\n    }\n\n    return unstack(item, actor, item.getRefData().getCount() - count);\n}\n\nMWWorld::InventoryStoreListener* MWWorld::InventoryStore::getInvListener()\n{\n    return mInventoryListener;\n}\n\nvoid MWWorld::InventoryStore::setInvListener(InventoryStoreListener *listener, const Ptr& actor)\n{\n    mInventoryListener = listener;\n    updateMagicEffects(actor);\n}\n\nvoid MWWorld::InventoryStore::fireEquipmentChangedEvent(const Ptr& actor)\n{\n    if (!mUpdatesEnabled)\n        return;\n    /*\n        Start of tes3mp change (major)\n\n        Only fire inventory events for local players or for other actors in loaded cells to avoid crashes\n    */\n    if (mInventoryListener)\n    {\n        if (actor == MWMechanics::getPlayer() || MWBase::Environment::get().getWorld()->isCellActive(*actor.getCell()->getCell()))\n        {\n            mInventoryListener->equipmentChanged();\n        }\n    }\n    /*\n        End of tes3mp change (major)\n    */\n\n    // if player, update inventory window\n    /*\n    if (actor == MWMechanics::getPlayer())\n    {\n        MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();\n    }\n    */\n}\n\nvoid MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisitor &visitor)\n{\n    for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter)\n    {\n        if (*iter==end())\n            continue;\n\n        std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter);\n        if (enchantmentId.empty())\n            continue;\n\n        const ESM::Enchantment& enchantment =\n            *MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find (enchantmentId);\n\n        if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)\n            continue;\n\n        if (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().getRefId()) == mPermanentMagicEffectMagnitudes.end())\n            continue;\n\n        int i=0;\n        for (const ESM::ENAMstruct& effect : enchantment.mEffects.mList)\n        {\n            i++;\n            // Don't get spell icon display information for enchantments that weren't actually applied\n            if (mMagicEffects.get(MWMechanics::EffectKey(effect)).getMagnitude() == 0)\n                continue;\n            const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i-1];\n            float magnitude = effect.mMagnMin + (effect.mMagnMax - effect.mMagnMin) * params.mRandom;\n            magnitude *= params.mMultiplier;\n            if (magnitude > 0)\n                visitor.visit(MWMechanics::EffectKey(effect), i-1, (**iter).getClass().getName(**iter), (**iter).getCellRef().getRefId(), -1, magnitude);\n        }\n    }\n}\n\nvoid MWWorld::InventoryStore::purgeEffect(short effectId, bool wholeSpell)\n{\n    for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it)\n    {\n        if (*it != end())\n            purgeEffect(effectId, (*it)->getCellRef().getRefId(), wholeSpell);\n    }\n}\n\nvoid MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId, bool wholeSpell, int effectIndex)\n{\n    TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId);\n    if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end())\n        return;\n\n    for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter)\n    {\n        if (*iter==end())\n            continue;\n\n        if ((*iter)->getCellRef().getRefId() != sourceId)\n            continue;\n\n        std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter);\n\n        if (!enchantmentId.empty())\n        {\n            const ESM::Enchantment& enchantment =\n                *MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find (enchantmentId);\n\n            if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)\n                continue;\n\n            std::vector<EffectParams>& params = effectMagnitudeIt->second;\n\n            int i=0;\n            for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());\n                effectIt!=enchantment.mEffects.mList.end(); ++effectIt, ++i)\n            {\n                if (effectIt->mEffectID != effectId)\n                    continue;\n\n                if (effectIndex >= 0 && effectIndex != i)\n                    continue;\n\n                if (wholeSpell)\n                {\n                    mPermanentMagicEffectMagnitudes.erase(sourceId);\n                    return;\n                }\n\n                float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom;\n                magnitude *= params[i].mMultiplier;\n\n                if (magnitude)\n                    mMagicEffects.add (*effectIt, -magnitude);\n\n                params[i].mMultiplier = 0;\n            }\n        }\n    }\n}\n\nvoid MWWorld::InventoryStore::clear()\n{\n    mSlots.clear();\n    initSlots (mSlots);\n    ContainerStore::clear();\n}\n\nbool MWWorld::InventoryStore::isEquipped(const MWWorld::ConstPtr &item)\n{\n    for (int i=0; i < MWWorld::InventoryStore::Slots; ++i)\n    {\n        if (getSlot(i) != end() && *getSlot(i) == item)\n            return true;\n    }\n    return false;\n}\n\nvoid MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const\n{\n    MWWorld::ContainerStore::writeState(state);\n\n    for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it)\n    {\n        std::vector<std::pair<float, float> > params;\n        for (std::vector<EffectParams>::const_iterator pIt = it->second.begin(); pIt != it->second.end(); ++pIt)\n        {\n            params.emplace_back(pIt->mRandom, pIt->mMultiplier);\n        }\n\n        state.mPermanentMagicEffectMagnitudes[it->first] = params;\n    }\n}\n\nvoid MWWorld::InventoryStore::readState(const ESM::InventoryState &state)\n{\n    MWWorld::ContainerStore::readState(state);\n\n    for (ESM::InventoryState::TEffectMagnitudes::const_iterator it = state.mPermanentMagicEffectMagnitudes.begin();\n         it != state.mPermanentMagicEffectMagnitudes.end(); ++it)\n    {\n        std::vector<EffectParams> params;\n        for (std::vector<std::pair<float, float> >::const_iterator pIt = it->second.begin(); pIt != it->second.end(); ++pIt)\n        {\n            EffectParams p;\n            p.mRandom = pIt->first;\n            p.mMultiplier = pIt->second;\n            params.push_back(p);\n        }\n\n        mPermanentMagicEffectMagnitudes[it->first] = params;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/inventorystore.hpp",
    "content": "#ifndef GAME_MWWORLD_INVENTORYSTORE_H\n#define GAME_MWWORLD_INVENTORYSTORE_H\n\n#include \"containerstore.hpp\"\n\n#include \"../mwmechanics/magiceffects.hpp\"\n\nnamespace ESM\n{\n    struct MagicEffect;\n}\n\nnamespace MWMechanics\n{\n    class NpcStats;\n}\n\nnamespace MWWorld\n{\n    class InventoryStoreListener\n    {\n    public:\n        /**\n         * Fired when items are equipped or unequipped\n         */\n        virtual void equipmentChanged () {}\n\n        /**\n         * @param effect\n         * @param isNew Is this effect new (e.g. the item for it was just now manually equipped)\n         *              or was it loaded from a savegame / initial game state? \\n\n         *              If it isn't new, non-looping VFX should not be played.\n         * @param playSound Play effect sound?\n         */\n        virtual void permanentEffectAdded (const ESM::MagicEffect *magicEffect, bool isNew) {}\n\n        virtual ~InventoryStoreListener() = default;\n    };\n\n    ///< \\brief Variant of the ContainerStore for NPCs\n    class InventoryStore : public ContainerStore\n    {\n        public:\n\n            static constexpr int Slot_Helmet = 0;\n            static constexpr int Slot_Cuirass = 1;\n            static constexpr int Slot_Greaves = 2;\n            static constexpr int Slot_LeftPauldron = 3;\n            static constexpr int Slot_RightPauldron = 4;\n            static constexpr int Slot_LeftGauntlet = 5;\n            static constexpr int Slot_RightGauntlet = 6;\n            static constexpr int Slot_Boots = 7;\n            static constexpr int Slot_Shirt = 8;\n            static constexpr int Slot_Pants = 9;\n            static constexpr int Slot_Skirt = 10;\n            static constexpr int Slot_Robe = 11;\n            static constexpr int Slot_LeftRing = 12;\n            static constexpr int Slot_RightRing = 13;\n            static constexpr int Slot_Amulet = 14;\n            static constexpr int Slot_Belt = 15;\n            static constexpr int Slot_CarriedRight = 16;\n            static constexpr int Slot_CarriedLeft = 17;\n            static constexpr int Slot_Ammunition = 18;\n\n            static constexpr int Slots = 19;\n\n            static constexpr int Slot_NoSlot = -1;\n\n        private:\n\n            MWMechanics::MagicEffects mMagicEffects;\n\n            InventoryStoreListener* mInventoryListener;\n\n            // Enables updates of magic effects and actor model whenever items are equipped or unequipped.\n            // This is disabled during autoequip to avoid excessive updates\n            bool mUpdatesEnabled;\n\n            bool mFirstAutoEquip;\n\n            // Vanilla allows permanent effects with a random magnitude, so it needs to be stored here.\n            // We also need this to only play sounds and particle effects when the item is equipped, rather than on every update.\n            struct EffectParams\n            {\n                // Modifier to scale between min and max magnitude\n                float mRandom;\n                // Multiplier for when an effect was fully or partially resisted\n                float mMultiplier;\n            };\n\n            typedef std::map<std::string, std::vector<EffectParams> > TEffectMagnitudes;\n            TEffectMagnitudes mPermanentMagicEffectMagnitudes;\n\n            typedef std::vector<ContainerStoreIterator> TSlots;\n\n            TSlots mSlots;\n\n            void autoEquipWeapon(const MWWorld::Ptr& actor, TSlots& slots_);\n            void autoEquipArmor(const MWWorld::Ptr& actor, TSlots& slots_);\n            void autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_);\n\n            // selected magic item (for using enchantments of type \"Cast once\" or \"Cast when used\")\n            ContainerStoreIterator mSelectedEnchantItem;\n\n            void copySlots (const InventoryStore& store);\n\n            void initSlots (TSlots& slots_);\n\n            void updateMagicEffects(const Ptr& actor);\n\n            void fireEquipmentChangedEvent(const Ptr& actor);\n\n            void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const override;\n            void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory) override;\n\n            ContainerStoreIterator findSlot (int slot) const;\n\n        public:\n\n            InventoryStore();\n\n            InventoryStore (const InventoryStore& store);\n\n            InventoryStore& operator= (const InventoryStore& store);\n\n            std::unique_ptr<ContainerStore> clone() override { return std::make_unique<InventoryStore>(*this); }\n\n            ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true) override;\n            ///< Add the item pointed to by \\a ptr to this container. (Stacks automatically if needed)\n            /// Auto-equip items if specific conditions are fulfilled and allowAutoEquip is true (see the implementation).\n            ///\n            /// \\note The item pointed to is not required to exist beyond this function call.\n            ///\n            /// \\attention Do not add items to an existing stack by increasing the count instead of\n            /// calling this function!\n            ///\n            /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item.\n\n            void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor);\n            ///< \\warning \\a iterator can not be an end()-iterator, use unequip function instead\n\n            bool isEquipped(const MWWorld::ConstPtr& item);\n            ///< Utility function, returns true if the given item is equipped in any slot\n\n            void setSelectedEnchantItem(const ContainerStoreIterator& iterator);\n            ///< set the selected magic item (for using enchantments of type \"Cast once\" or \"Cast when used\")\n            /// \\note to unset the selected item, call this method with end() iterator\n\n            ContainerStoreIterator getSelectedEnchantItem();\n            ///< @return selected magic item (for using enchantments of type \"Cast once\" or \"Cast when used\")\n            /// \\note if no item selected, return end() iterator\n\n            ContainerStoreIterator getSlot (int slot);\n            ConstContainerStoreIterator getSlot(int slot) const;\n\n            ContainerStoreIterator getPreferredShield(const MWWorld::Ptr& actor);\n\n            void unequipAll(const MWWorld::Ptr& actor);\n            ///< Unequip all currently equipped items.\n\n            void autoEquip (const MWWorld::Ptr& actor);\n            ///< Auto equip items according to stats and item value.\n\n            const MWMechanics::MagicEffects& getMagicEffects() const;\n            ///< Return magic effects from worn items.\n\n            bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const override;\n            ///< @return true if the two specified objects can stack with each other\n\n            using ContainerStore::remove;\n            int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true) override;\n            ///< Remove \\a count item(s) designated by \\a item from this inventory.\n            ///\n            /// @return the number of items actually removed\n\n            ContainerStoreIterator unequipSlot(int slot, const Ptr& actor, bool applyUpdates = true);\n            ///< Unequip \\a slot.\n            ///\n            /// @return an iterator to the item that was previously in the slot\n\n            ContainerStoreIterator unequipItem(const Ptr& item, const Ptr& actor);\n            ///< Unequip an item identified by its Ptr. An exception is thrown\n            /// if the item is not currently equipped.\n            ///\n            /// @return an iterator to the item that was previously in the slot\n            /// (it can be re-stacked so its count may be different than when it\n            /// was equipped).\n\n            ContainerStoreIterator unequipItemQuantity(const Ptr& item, const Ptr& actor, int count);\n            ///< Unequip a specific quantity of an item identified by its Ptr.\n            /// An exception is thrown if the item is not currently equipped,\n            /// if count <= 0, or if count > the item stack size.\n            ///\n            /// @return an iterator to the unequipped items that were previously\n            /// in the slot (they can be re-stacked so its count may be different\n            /// than the requested count).\n\n            void setInvListener (InventoryStoreListener* listener, const Ptr& actor);\n            ///< Set a listener for various events, see \\a InventoryStoreListener\n\n            InventoryStoreListener* getInvListener();\n\n            void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);\n\n            void purgeEffect (short effectId, bool wholeSpell = false);\n            ///< Remove a magic effect\n\n            void purgeEffect (short effectId, const std::string& sourceId, bool wholeSpell = false, int effectIndex=-1);\n            ///< Remove a magic effect\n\n            void clear() override;\n            ///< Empty container.\n\n            void writeState (ESM::InventoryState& state) const override;\n\n            void readState (const ESM::InventoryState& state) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/livecellref.cpp",
    "content": "#include \"livecellref.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/esm/objectstate.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\n#include \"ptr.hpp\"\n#include \"class.hpp\"\n#include \"esmstore.hpp\"\n\nMWWorld::LiveCellRefBase::LiveCellRefBase(const std::string& type, const ESM::CellRef &cref)\n  : mClass(&Class::get(type)), mRef(cref), mData(cref)\n{\n}\n\nvoid MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state)\n{\n    mRef = state.mRef;\n    mData = RefData (state, mData.isDeletedByContentFile());\n\n    Ptr ptr (this);\n\n    if (state.mHasLocals)\n    {\n        std::string scriptId = mClass->getScript (ptr);\n        // Make sure we still have a script. It could have been coming from a content file that is no longer active.\n        if (!scriptId.empty())\n        {\n            if (const ESM::Script* script = MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().search (scriptId))\n            {\n                try\n                {\n                    mData.setLocals (*script);\n                    mData.getLocals().read (state.mLocals, scriptId);\n                }\n                catch (const std::exception& exception)\n                {\n                    Log(Debug::Error)\n                        << \"Error: failed to load state for local script \" << scriptId\n                        << \" because an exception has been thrown: \" << exception.what();\n                }\n            }\n        }\n    }\n\n    mClass->readAdditionalState (ptr, state);\n\n    if (!mRef.getSoul().empty() && !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(mRef.getSoul()))\n    {\n        Log(Debug::Warning) << \"Soul '\" << mRef.getSoul() << \"' not found, removing the soul from soul gem\";\n        mRef.setSoul(std::string());\n    }\n}\n\nvoid MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const\n{\n    mRef.writeState(state);\n\n    ConstPtr ptr (this);\n\n    mData.write (state, mClass->getScript (ptr));\n\n    mClass->writeAdditionalState (ptr, state);\n}\n\nbool MWWorld::LiveCellRefBase::checkStateImp (const ESM::ObjectState& state)\n{\n    return true;\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/livecellref.hpp",
    "content": "#ifndef GAME_MWWORLD_LIVECELLREF_H\n#define GAME_MWWORLD_LIVECELLREF_H\n\n#include <typeinfo>\n\n#include \"cellref.hpp\"\n\n#include \"refdata.hpp\"\n\nnamespace ESM\n{\n    struct ObjectState;\n}\n\nnamespace MWWorld\n{\n    class Ptr;\n    class ESMStore;\n    class Class;\n\n    /// Used to create pointers to hold any type of LiveCellRef<> object.\n    struct LiveCellRefBase\n    {\n        const Class *mClass;\n\n        /** Information about this instance, such as 3D location and rotation\n         * and individual type-dependent data.\n         */\n        MWWorld::CellRef mRef;\n\n        /** runtime-data */\n        RefData mData;\n\n        LiveCellRefBase(const std::string& type, const ESM::CellRef &cref=ESM::CellRef());\n        /* Need this for the class to be recognized as polymorphic */\n        virtual ~LiveCellRefBase() { }\n\n        virtual void load (const ESM::ObjectState& state) = 0;\n        ///< Load state into a LiveCellRef, that has already been initialised with base and class.\n        ///\n        /// \\attention Must not be called with an invalid \\a state.\n\n        virtual void save (ESM::ObjectState& state) const = 0;\n        ///< Save LiveCellRef state into \\a state.\n\n        protected:\n\n            void loadImp (const ESM::ObjectState& state);\n            ///< Load state into a LiveCellRef, that has already been initialised with base and\n            /// class.\n            ///\n            /// \\attention Must not be called with an invalid \\a state.\n\n            void saveImp (ESM::ObjectState& state) const;\n            ///< Save LiveCellRef state into \\a state.\n\n            static bool checkStateImp (const ESM::ObjectState& state);\n            ///< Check if state is valid and report errors.\n            ///\n            /// \\return Valid?\n            ///\n            /// \\note Does not check if the RefId exists.\n    };\n\n    inline bool operator== (const LiveCellRefBase& cellRef, const ESM::RefNum refNum)\n    {\n        return cellRef.mRef.getRefNum()==refNum;\n    }\n\n    /// A reference to one object (of any type) in a cell.\n    ///\n    /// Constructing this with a CellRef instance in the constructor means that\n    /// in practice (where D is RefData) the possibly mutable data is copied\n    /// across to mData. If later adding data (such as position) to CellRef\n    /// this would have to be manually copied across.\n    template <typename X>\n    struct LiveCellRef : public LiveCellRefBase\n    {\n        LiveCellRef(const ESM::CellRef& cref, const X* b = nullptr)\n            : LiveCellRefBase(typeid(X).name(), cref), mBase(b)\n        {}\n\n        LiveCellRef(const X* b = nullptr)\n            : LiveCellRefBase(typeid(X).name()), mBase(b)\n        {}\n\n        // The object that this instance is based on.\n        const X* mBase;\n\n        void load (const ESM::ObjectState& state) override;\n        ///< Load state into a LiveCellRef, that has already been initialised with base and class.\n        ///\n        /// \\attention Must not be called with an invalid \\a state.\n\n        void save (ESM::ObjectState& state) const override;\n        ///< Save LiveCellRef state into \\a state.\n\n        static bool checkState (const ESM::ObjectState& state);\n        ///< Check if state is valid and report errors.\n        ///\n        /// \\return Valid?\n        ///\n        /// \\note Does not check if the RefId exists.\n    };\n\n    template <typename X>\n    void LiveCellRef<X>::load (const ESM::ObjectState& state)\n    {\n        loadImp (state);\n    }\n\n    template <typename X>\n    void LiveCellRef<X>::save (ESM::ObjectState& state) const\n    {\n        saveImp (state);\n    }\n\n    template <typename X>\n    bool LiveCellRef<X>::checkState (const ESM::ObjectState& state)\n    {\n        return checkStateImp (state);\n    }\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/localscripts.cpp",
    "content": "#include \"localscripts.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include \"esmstore.hpp\"\n#include \"cellstore.hpp\"\n#include \"class.hpp\"\n#include \"containerstore.hpp\"\n\nnamespace\n{\n\n    struct AddScriptsVisitor\n    {\n        AddScriptsVisitor(MWWorld::LocalScripts& scripts)\n            : mScripts(scripts)\n        {\n        }\n        MWWorld::LocalScripts& mScripts;\n\n        bool operator()(const MWWorld::Ptr& ptr)\n        {\n            if (ptr.getRefData().isDeleted())\n                return true;\n\n            std::string script = ptr.getClass().getScript(ptr);\n\n            if (!script.empty())\n                mScripts.add(script, ptr);\n\n            return true;\n        }\n    };\n\n    struct AddContainerItemScriptsVisitor\n    {\n        AddContainerItemScriptsVisitor(MWWorld::LocalScripts& scripts)\n            : mScripts(scripts)\n        {\n        }\n        MWWorld::LocalScripts& mScripts;\n\n        bool operator()(const MWWorld::Ptr& containerPtr)\n        {\n            // Ignore containers without generated content\n            if (containerPtr.getTypeName() == typeid(ESM::Container).name() &&\n                containerPtr.getRefData().getCustomData() == nullptr)\n                return true;\n\n            MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr);\n            for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)\n            {\n                std::string script = it->getClass().getScript(*it);\n                if(script != \"\")\n                {\n                    MWWorld::Ptr item = *it;\n                    item.mCell = containerPtr.getCell();\n                    mScripts.add (script, item);\n                }\n            }\n            return true;\n        }\n    };\n\n}\n\nMWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (store)\n{\n    mIter = mScripts.end();\n}\n\nvoid MWWorld::LocalScripts::startIteration()\n{\n    mIter = mScripts.begin();\n}\n\nbool MWWorld::LocalScripts::getNext(std::pair<std::string, Ptr>& script)\n{\n    if (mIter!=mScripts.end())\n    {\n        std::list<std::pair<std::string, Ptr> >::iterator iter = mIter++;\n        script = *iter;\n        return true;\n    }\n    return false;\n}\n\nvoid MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr)\n{\n    if (const ESM::Script *script = mStore.get<ESM::Script>().search (scriptName))\n    {\n        try\n        {\n            ptr.getRefData().setLocals (*script);\n\n            for (std::list<std::pair<std::string, Ptr> >::iterator iter = mScripts.begin(); iter!=mScripts.end(); ++iter)\n                if (iter->second==ptr)\n                {\n                    Log(Debug::Warning) << \"Error: tried to add local script twice for \" << ptr.getCellRef().getRefId();\n                    remove(ptr);\n                    break;\n                }\n\n            mScripts.emplace_back (scriptName, ptr);\n        }\n        catch (const std::exception& exception)\n        {\n            Log(Debug::Error)\n                << \"failed to add local script \" << scriptName\n                << \" because an exception has been thrown: \" << exception.what();\n        }\n    }\n    else\n        Log(Debug::Warning)\n            << \"failed to add local script \" << scriptName\n            << \" because the script does not exist.\";\n}\n\nvoid MWWorld::LocalScripts::addCell (CellStore *cell)\n{\n    AddScriptsVisitor addScriptsVisitor(*this);\n    cell->forEach(addScriptsVisitor);\n\n    AddContainerItemScriptsVisitor addContainerItemScriptsVisitor(*this);\n    cell->forEachType<ESM::NPC>(addContainerItemScriptsVisitor);\n    cell->forEachType<ESM::Creature>(addContainerItemScriptsVisitor);\n    cell->forEachType<ESM::Container>(addContainerItemScriptsVisitor);\n}\n\nvoid MWWorld::LocalScripts::clear()\n{\n    mScripts.clear();\n}\n\nvoid MWWorld::LocalScripts::clearCell (CellStore *cell)\n{\n    std::list<std::pair<std::string, Ptr> >::iterator iter = mScripts.begin();\n\n    while (iter!=mScripts.end())\n    {\n        if (iter->second.mCell==cell)\n        {\n            if (iter==mIter)\n               ++mIter;\n\n            mScripts.erase (iter++);\n        }\n        else\n            ++iter;\n    }\n}\n\nvoid MWWorld::LocalScripts::remove (RefData *ref)\n{\n    for (std::list<std::pair<std::string, Ptr> >::iterator iter = mScripts.begin();\n        iter!=mScripts.end(); ++iter)\n        if (&(iter->second.getRefData()) == ref)\n        {\n            if (iter==mIter)\n                ++mIter;\n\n            mScripts.erase (iter);\n            break;\n        }\n}\n\nvoid MWWorld::LocalScripts::remove (const Ptr& ptr)\n{\n    for (std::list<std::pair<std::string, Ptr> >::iterator iter = mScripts.begin();\n        iter!=mScripts.end(); ++iter)\n        if (iter->second==ptr)\n        {\n            if (iter==mIter)\n                ++mIter;\n\n            mScripts.erase (iter);\n            break;\n        }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/localscripts.hpp",
    "content": "#ifndef GAME_MWWORLD_LOCALSCRIPTS_H\n#define GAME_MWWORLD_LOCALSCRIPTS_H\n\n#include <list>\n#include <string>\n\n#include \"ptr.hpp\"\n\nnamespace MWWorld\n{\n    class ESMStore;\n    class CellStore;\n    class RefData;\n\n    /// \\brief List of active local scripts\n    class LocalScripts\n    {\n            std::list<std::pair<std::string, Ptr> > mScripts;\n            std::list<std::pair<std::string, Ptr> >::iterator mIter;\n            const MWWorld::ESMStore& mStore;\n\n        public:\n\n            LocalScripts (const MWWorld::ESMStore& store);\n\n            void startIteration();\n            ///< Set the iterator to the begin of the script list.\n\n            bool getNext(std::pair<std::string, Ptr>& script);\n            ///< Get next local script\n            /// @return Did we get a script?\n\n            void add (const std::string& scriptName, const Ptr& ptr);\n            ///< Add script to collection of active local scripts.\n\n            void addCell (CellStore *cell);\n            ///< Add all local scripts in a cell.\n\n            void clear();\n            ///< Clear active local scripts collection.\n\n            void clearCell (CellStore *cell);\n            ///< Remove all scripts belonging to \\a cell.\n            \n            void remove (RefData *ref);\n\n            void remove (const Ptr& ptr);\n            ///< Remove script for given reference (ignored if reference does not have a script listed).\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/manualref.cpp",
    "content": "#include \"manualref.hpp\"\n\n#include \"esmstore.hpp\"\n\nnamespace\n{\n\n    template<typename T>\n    void create(const MWWorld::Store<T>& list, const std::string& name, boost::any& refValue, MWWorld::Ptr& ptrValue)\n    {\n        const T* base = list.find(name);\n\n        ESM::CellRef cellRef;\n        cellRef.mRefNum.unset();\n        cellRef.mRefID = name;\n        cellRef.mScale = 1;\n        cellRef.mFactionRank = 0;\n        cellRef.mChargeInt = -1;\n        cellRef.mChargeIntRemainder = 0.0f;\n        cellRef.mGoldValue = 1;\n        cellRef.mEnchantmentCharge = -1;\n        cellRef.mTeleport = false;\n        cellRef.mLockLevel = 0;\n        cellRef.mReferenceBlocked = 0;\n\n        /*\n            Start of tes3mp addition\n\n            Set the mMpNum (unique multiplayer reference number) to 0 by default\n        */\n        cellRef.mMpNum = 0;\n        /*\n            End of tes3mp addition\n        */\n\n        MWWorld::LiveCellRef<T> ref(cellRef, base);\n\n        refValue = ref;\n        ptrValue = MWWorld::Ptr(&boost::any_cast<MWWorld::LiveCellRef<T>&>(refValue), nullptr);\n    }\n}\n\nMWWorld::ManualRef::ManualRef(const MWWorld::ESMStore& store, const std::string& name, const int count)\n{\n    std::string lowerName = Misc::StringUtils::lowerCase(name);\n    switch (store.find(lowerName))\n    {\n    case ESM::REC_ACTI: create(store.get<ESM::Activator>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_ALCH: create(store.get<ESM::Potion>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_APPA: create(store.get<ESM::Apparatus>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_ARMO: create(store.get<ESM::Armor>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_BOOK: create(store.get<ESM::Book>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_CLOT: create(store.get<ESM::Clothing>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_CONT: create(store.get<ESM::Container>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_CREA: create(store.get<ESM::Creature>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_DOOR: create(store.get<ESM::Door>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_INGR: create(store.get<ESM::Ingredient>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_LEVC: create(store.get<ESM::CreatureLevList>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_LEVI: create(store.get<ESM::ItemLevList>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_LIGH: create(store.get<ESM::Light>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_LOCK: create(store.get<ESM::Lockpick>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_MISC: create(store.get<ESM::Miscellaneous>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_NPC_: create(store.get<ESM::NPC>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_PROB: create(store.get<ESM::Probe>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_REPA: create(store.get<ESM::Repair>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_STAT: create(store.get<ESM::Static>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_WEAP: create(store.get<ESM::Weapon>(), lowerName, mRef, mPtr); break;\n    case ESM::REC_BODY: create(store.get<ESM::BodyPart>(), lowerName, mRef, mPtr); break;\n\n    case 0:\n        throw std::logic_error(\"failed to create manual cell ref for \" + lowerName + \" (unknown ID)\");\n\n    default:\n        throw std::logic_error(\"failed to create manual cell ref for \" + lowerName + \" (unknown type)\");\n    }\n\n    mPtr.getRefData().setCount(count);\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/manualref.hpp",
    "content": "#ifndef GAME_MWWORLD_MANUALREF_H\n#define GAME_MWWORLD_MANUALREF_H\n\n#include <boost/any.hpp>\n\n#include \"ptr.hpp\"\n\nnamespace MWWorld\n{\n    /// \\brief Manually constructed live cell ref\n    class ManualRef\n    {\n            boost::any mRef;\n            Ptr mPtr;\n\n            ManualRef (const ManualRef&);\n            ManualRef& operator= (const ManualRef&);\n\n        public:\n            ManualRef(const MWWorld::ESMStore& store, const std::string& name, const int count = 1);\n\n            const Ptr& getPtr() const\n            {\n                return mPtr;\n            }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/nullaction.hpp",
    "content": "#ifndef GAME_MWWORLD_NULLACTION_H\n#define GAME_MWWORLD_NULLACTION_H\n\n#include \"action.hpp\"\n\nnamespace MWWorld\n{\n    /// \\brief Action: do nothing\n    class NullAction : public Action\n    {\n            void executeImp (const Ptr& actor) override {}\n\n            bool isNullAction() override { return true; }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/player.cpp",
    "content": "#include \"player.hpp\"\n\n#include <stdexcept>\n\n#include <components/debug/debuglog.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/player.hpp>\n#include <components/esm/defs.hpp>\n#include <components/esm/loadbsgn.hpp>\n\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n\n#include \"../mwmechanics/movement.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/spellutil.hpp\"\n\n#include \"class.hpp\"\n#include \"ptr.hpp\"\n#include \"cellstore.hpp\"\n\nnamespace MWWorld\n{\n    Player::Player (const ESM::NPC *player)\n      : mCellStore(nullptr),\n        mLastKnownExteriorPosition(0,0,0),\n        mMarkedPosition(ESM::Position()),\n        mMarkedCell(nullptr),\n        mAutoMove(false),\n        mForwardBackward(0),\n        mTeleported(false),\n        mCurrentCrimeId(-1),\n        mPaidCrimeId(-1),\n        mAttackingOrSpell(false),\n        mJumping(false)\n    {\n        ESM::CellRef cellRef;\n        cellRef.blank();\n        cellRef.mRefID = \"player\";\n        mPlayer = LiveCellRef<ESM::NPC>(cellRef, player);\n\n        ESM::Position playerPos = mPlayer.mData.getPosition();\n        playerPos.pos[0] = playerPos.pos[1] = playerPos.pos[2] = 0;\n        mPlayer.mData.setPosition(playerPos);\n    }\n\n    void Player::saveStats()\n    {\n        MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer());\n\n        for (int i=0; i<ESM::Skill::Length; ++i)\n            mSaveSkills[i] = stats.getSkill(i);\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            mSaveAttributes[i] = stats.getAttribute(i);\n    }\n\n    void Player::restoreStats()\n    {\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer());\n        MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer());\n        MWMechanics::DynamicStat<float> health = creatureStats.getDynamic(0);\n        creatureStats.setHealth(int(health.getBase() / gmst.find(\"fWereWolfHealth\")->mValue.getFloat()));\n        for (int i=0; i<ESM::Skill::Length; ++i)\n            npcStats.setSkill(i, mSaveSkills[i]);\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            npcStats.setAttribute(i, mSaveAttributes[i]);\n    }\n\n    void Player::setWerewolfStats()\n    {\n        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();\n        MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer());\n        MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer());\n        MWMechanics::DynamicStat<float> health = creatureStats.getDynamic(0);\n        creatureStats.setHealth(int(health.getBase() * gmst.find(\"fWereWolfHealth\")->mValue.getFloat()));\n        for(size_t i = 0;i < ESM::Attribute::Length;++i)\n        {\n            // Oh, Bethesda. It's \"Intelligence\".\n            std::string name = \"fWerewolf\"+((i==ESM::Attribute::Intelligence) ? std::string(\"Intellegence\") :\n                                            ESM::Attribute::sAttributeNames[i]);\n\n            MWMechanics::AttributeValue value = npcStats.getAttribute(i);\n            value.setBase(int(gmst.find(name)->mValue.getFloat()));\n            npcStats.setAttribute(i, value);\n        }\n\n        for(size_t i = 0;i < ESM::Skill::Length;i++)\n        {\n            // Acrobatics is set separately for some reason.\n            if(i == ESM::Skill::Acrobatics)\n                continue;\n\n            // \"Mercantile\"! >_<\n            std::string name = \"fWerewolf\"+((i==ESM::Skill::Mercantile) ? std::string(\"Merchantile\") :\n                                            ESM::Skill::sSkillNames[i]);\n\n            MWMechanics::SkillValue value = npcStats.getSkill(i);\n            value.setBase(int(gmst.find(name)->mValue.getFloat()));\n            npcStats.setSkill(i, value);\n        }\n    }\n\n    void Player::set(const ESM::NPC *player)\n    {\n        mPlayer.mBase = player;\n    }\n\n    void Player::setCell (MWWorld::CellStore *cellStore)\n    {\n        mCellStore = cellStore;\n    }\n\n    MWWorld::Ptr Player::getPlayer()\n    {\n        MWWorld::Ptr ptr (&mPlayer, mCellStore);\n        return ptr;\n    }\n\n    MWWorld::ConstPtr Player::getConstPlayer() const\n    {\n        MWWorld::ConstPtr ptr (&mPlayer, mCellStore);\n        return ptr;\n    }\n\n    void Player::setBirthSign (const std::string &sign)\n    {\n        mSign = sign;\n    }\n\n    const std::string& Player::getBirthSign() const\n    {\n        return mSign;\n    }\n\n    void Player::setDrawState (MWMechanics::DrawState_ state)\n    {\n         MWWorld::Ptr ptr = getPlayer();\n         ptr.getClass().getNpcStats(ptr).setDrawState (state);\n    }\n\n    bool Player::getAutoMove() const\n    {\n        return mAutoMove;\n    }\n\n    void Player::setAutoMove (bool enable)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n\n        mAutoMove = enable;\n\n        int value = mForwardBackward;\n\n        if (mAutoMove)\n            value = 1;\n\n        ptr.getClass().getMovementSettings(ptr).mPosition[1] = value;\n    }\n\n    void Player::setLeftRight (float value)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n        ptr.getClass().getMovementSettings(ptr).mPosition[0] = value;\n    }\n\n    void Player::setForwardBackward (float value)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n\n        mForwardBackward = value;\n\n        if (mAutoMove)\n            value = 1;\n\n        ptr.getClass().getMovementSettings(ptr).mPosition[1] = value;\n    }\n\n    void Player::setUpDown(int value)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n        ptr.getClass().getMovementSettings(ptr).mPosition[2] = static_cast<float>(value);\n    }\n\n    void Player::setRunState(bool run)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n        ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, run);\n    }\n\n    void Player::setSneak(bool sneak)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n        ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, sneak);\n    }\n\n    void Player::yaw(float yaw)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n        ptr.getClass().getMovementSettings(ptr).mRotation[2] += yaw;\n    }\n    void Player::pitch(float pitch)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n        ptr.getClass().getMovementSettings(ptr).mRotation[0] += pitch;\n    }\n    void Player::roll(float roll)\n    {\n        MWWorld::Ptr ptr = getPlayer();\n        ptr.getClass().getMovementSettings(ptr).mRotation[1] += roll;\n    }\n\n    MWMechanics::DrawState_ Player::getDrawState()\n    {\n         MWWorld::Ptr ptr = getPlayer();\n         return ptr.getClass().getNpcStats(ptr).getDrawState();\n    }\n\n    void Player::activate()\n    {\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n            return;\n\n        MWWorld::Ptr player = getPlayer();\n        const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player);\n        bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();\n        if ((!godmode && playerStats.isParalyzed()) || playerStats.getKnockedDown() || playerStats.isDead())\n            return;\n\n        MWWorld::Ptr toActivate = MWBase::Environment::get().getWorld()->getFacedObject();\n\n        if (toActivate.isEmpty())\n            return;\n\n        if (!toActivate.getClass().hasToolTip(toActivate))\n            return;\n\n        /*\n            Start of tes3mp change (major)\n\n            Disable unilateral activation on this client and expect the server's reply to our\n            packet to do it instead\n        */\n        //MWBase::Environment::get().getWorld()->activate(toActivate, player);\n        /*\n            End of tes3mp change (major)\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_OBJECT_ACTIVATE packet every time an object is activated here\n        */\n        mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n        objectList->reset();\n        objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n        objectList->addObjectActivate(toActivate, player);\n        objectList->sendObjectActivate();\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    bool Player::wasTeleported() const\n    {\n        return mTeleported;\n    }\n\n    void Player::setTeleported(bool teleported)\n    {\n        mTeleported = teleported;\n    }\n\n    void Player::setAttackingOrSpell(bool attackingOrSpell)\n    {\n        mAttackingOrSpell = attackingOrSpell;\n    }\n\n    bool Player::getAttackingOrSpell() const\n    {\n        return mAttackingOrSpell;\n    }\n\n    void Player::setJumping(bool jumping)\n    {\n        mJumping = jumping;\n    }\n\n    bool Player::getJumping() const\n    {\n        return mJumping;\n    }\n\n    bool Player::isInCombat() {\n        return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0;\n    }\n\n    bool Player::enemiesNearby()\n    {\n        return MWBase::Environment::get().getMechanicsManager()->getEnemiesNearby(getPlayer()).size() != 0;\n    }\n\n    void Player::markPosition(CellStore *markedCell, const ESM::Position& markedPosition)\n    {\n        mMarkedCell = markedCell;\n        mMarkedPosition = markedPosition;\n    }\n\n    void Player::getMarkedPosition(CellStore*& markedCell, ESM::Position &markedPosition) const\n    {\n        markedCell = mMarkedCell;\n        if (mMarkedCell)\n            markedPosition = mMarkedPosition;\n    }\n\n    void Player::clear()\n    {\n        mCellStore = nullptr;\n        mSign.clear();\n        mMarkedCell = nullptr;\n        mAutoMove = false;\n        mForwardBackward = 0;\n        mTeleported = false;\n        mAttackingOrSpell = false;\n        mJumping = false;\n        mCurrentCrimeId = -1;\n        mPaidCrimeId = -1;\n        mPreviousItems.clear();\n        mLastKnownExteriorPosition = osg::Vec3f(0,0,0);\n\n        for (int i=0; i<ESM::Skill::Length; ++i)\n        {\n            mSaveSkills[i].setBase(0);\n            mSaveSkills[i].setModifier(0);\n        }\n\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n        {\n            mSaveAttributes[i].setBase(0);\n            mSaveAttributes[i].setModifier(0);\n        }\n\n        mMarkedPosition.pos[0] = 0;\n        mMarkedPosition.pos[1] = 0;\n        mMarkedPosition.pos[2] = 0;\n        mMarkedPosition.rot[0] = 0;\n        mMarkedPosition.rot[1] = 0;\n        mMarkedPosition.rot[2] = 0;\n    }\n\n    void Player::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n    {\n        ESM::Player player;\n\n        mPlayer.save (player.mObject);\n        player.mCellId = mCellStore->getCell()->getCellId();\n\n        player.mCurrentCrimeId = mCurrentCrimeId;\n        player.mPaidCrimeId = mPaidCrimeId;\n\n        player.mBirthsign = mSign;\n\n        player.mLastKnownExteriorPosition[0] = mLastKnownExteriorPosition.x();\n        player.mLastKnownExteriorPosition[1] = mLastKnownExteriorPosition.y();\n        player.mLastKnownExteriorPosition[2] = mLastKnownExteriorPosition.z();\n\n        if (mMarkedCell)\n        {\n            player.mHasMark = true;\n            player.mMarkedPosition = mMarkedPosition;\n            player.mMarkedCell = mMarkedCell->getCell()->getCellId();\n        }\n        else\n            player.mHasMark = false;\n\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            mSaveAttributes[i].writeState(player.mSaveAttributes[i]);\n        for (int i=0; i<ESM::Skill::Length; ++i)\n            mSaveSkills[i].writeState(player.mSaveSkills[i]);\n\n        player.mPreviousItems = mPreviousItems;\n\n        writer.startRecord (ESM::REC_PLAY);\n        player.save (writer);\n        writer.endRecord (ESM::REC_PLAY);\n    }\n\n    bool Player::readRecord (ESM::ESMReader& reader, uint32_t type)\n    {\n        if (type==ESM::REC_PLAY)\n        {\n            ESM::Player player;\n            player.load (reader);\n\n            if (!mPlayer.checkState (player.mObject))\n            {\n                // this is the one object we can not silently drop.\n                throw std::runtime_error (\"invalid player state record (object state)\");\n            }\n\n            if (!player.mObject.mEnabled)\n            {\n                Log(Debug::Warning) << \"Warning: Savegame attempted to disable the player.\";\n                player.mObject.mEnabled = true;\n            }\n\n            mPlayer.load (player.mObject);\n\n            for (int i=0; i<ESM::Attribute::Length; ++i)\n                mSaveAttributes[i].readState(player.mSaveAttributes[i]);\n            for (int i=0; i<ESM::Skill::Length; ++i)\n                mSaveSkills[i].readState(player.mSaveSkills[i]);\n\n            if (player.mObject.mNpcStats.mWerewolfDeprecatedData && player.mObject.mNpcStats.mIsWerewolf)\n            {\n                saveStats();\n                setWerewolfStats();\n            }\n\n            getPlayer().getClass().getCreatureStats(getPlayer()).getAiSequence().clear();\n\n            MWBase::World& world = *MWBase::Environment::get().getWorld();\n\n            try\n            {\n                mCellStore = world.getCell (player.mCellId);\n            }\n            catch (...)\n            {\n                Log(Debug::Warning) << \"Warning: Player cell '\" << player.mCellId.mWorldspace << \"' no longer exists\";\n                // Cell no longer exists. The loader will have to choose a default cell.\n                mCellStore = nullptr;\n            }\n\n            if (!player.mBirthsign.empty())\n            {\n                const ESM::BirthSign* sign = world.getStore().get<ESM::BirthSign>().search (player.mBirthsign);\n                if (!sign)\n                    throw std::runtime_error (\"invalid player state record (birthsign does not exist)\");\n            }\n\n            mCurrentCrimeId = player.mCurrentCrimeId;\n            mPaidCrimeId = player.mPaidCrimeId;\n\n            mSign = player.mBirthsign;\n\n            mLastKnownExteriorPosition.x() = player.mLastKnownExteriorPosition[0];\n            mLastKnownExteriorPosition.y() = player.mLastKnownExteriorPosition[1];\n            mLastKnownExteriorPosition.z() = player.mLastKnownExteriorPosition[2];\n\n            if (player.mHasMark && !player.mMarkedCell.mPaged)\n            {\n                // interior cell -> need to check if it exists (exterior cell will be\n                // generated on the fly)\n\n                if (!world.getStore().get<ESM::Cell>().search (player.mMarkedCell.mWorldspace))\n                    player.mHasMark = false; // drop mark silently\n            }\n\n            if (player.mHasMark)\n            {\n                mMarkedPosition = player.mMarkedPosition;\n                mMarkedCell = world.getCell (player.mMarkedCell);\n            }\n            else\n            {\n                mMarkedCell = nullptr;\n            }\n\n            mForwardBackward = 0;\n            mTeleported = false;\n\n            mPreviousItems = player.mPreviousItems;\n\n            return true;\n        }\n\n        return false;\n    }\n\n    int Player::getNewCrimeId()\n    {\n        return ++mCurrentCrimeId;\n    }\n\n    void Player::recordCrimeId()\n    {\n        mPaidCrimeId = mCurrentCrimeId;\n    }\n\n    int Player::getCrimeId() const\n    {\n        return mPaidCrimeId;\n    }\n\n    void Player::setPreviousItem(const std::string& boundItemId, const std::string& previousItemId)\n    {\n        mPreviousItems[boundItemId] = previousItemId;\n    }\n\n    std::string Player::getPreviousItem(const std::string& boundItemId)\n    {\n        return mPreviousItems[boundItemId];\n    }\n\n    void Player::erasePreviousItem(const std::string& boundItemId)\n    {\n        mPreviousItems.erase(boundItemId);\n    }\n\n    void Player::setSelectedSpell(const std::string& spellId)\n    {\n        Ptr player = getPlayer();\n        InventoryStore& store = player.getClass().getInventoryStore(player);\n        store.setSelectedEnchantItem(store.end());\n        int castChance = int(MWMechanics::getSpellSuccessChance(spellId, player));\n        MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, castChance);\n        MWBase::Environment::get().getWindowManager()->updateSpellWindow();\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/player.hpp",
    "content": "#ifndef GAME_MWWORLD_PLAYER_H\n#define GAME_MWWORLD_PLAYER_H\n\n#include <map>\n\n#include \"../mwworld/refdata.hpp\"\n#include \"../mwworld/livecellref.hpp\"\n\n#include \"../mwmechanics/drawstate.hpp\"\n#include \"../mwmechanics/stat.hpp\"\n\n#include <components/esm/loadskil.hpp>\n#include <components/esm/attr.hpp>\n\nnamespace ESM\n{\n    struct NPC;\n    class ESMWriter;\n    class ESMReader;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWWorld\n{\n    class CellStore;\n    class ConstPtr;\n\n    /// \\brief NPC object representing the player and additional player data\n    class Player\n    {\n        LiveCellRef<ESM::NPC>   mPlayer;\n        MWWorld::CellStore      *mCellStore;\n        std::string             mSign;\n\n        osg::Vec3f mLastKnownExteriorPosition;\n\n        ESM::Position           mMarkedPosition;\n        // If no position was marked, this is nullptr\n        CellStore*              mMarkedCell;\n\n        bool                    mAutoMove;\n        float                   mForwardBackward;\n        bool                    mTeleported;\n\n        int                     mCurrentCrimeId;    // the id assigned witnesses\n        int                     mPaidCrimeId;      // the last id paid off (0 bounty)\n\n        typedef std::map<std::string, std::string> PreviousItems; // previous equipped items, needed for bound spells\n        PreviousItems mPreviousItems;\n\n        // Saved stats prior to becoming a werewolf\n        MWMechanics::SkillValue mSaveSkills[ESM::Skill::Length];\n        MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length];\n\n        bool mAttackingOrSpell;\n        bool mJumping;\n\n    public:\n\n        Player(const ESM::NPC *player);\n\n        void saveStats();\n        void restoreStats();\n        void setWerewolfStats();\n\n        // For mark/recall magic effects\n        void markPosition (CellStore* markedCell, const ESM::Position& markedPosition);\n        void getMarkedPosition (CellStore*& markedCell, ESM::Position& markedPosition) const;\n\n        /// Interiors can not always be mapped to a world position. However\n        /// world position is still required for divine / almsivi magic effects\n        /// and the player arrow on the global map.\n        void setLastKnownExteriorPosition (const osg::Vec3f& position) { mLastKnownExteriorPosition = position; }\n        osg::Vec3f getLastKnownExteriorPosition() const { return mLastKnownExteriorPosition; }\n\n        void set (const ESM::NPC *player);\n\n        void setCell (MWWorld::CellStore *cellStore);\n\n        MWWorld::Ptr getPlayer();\n        MWWorld::ConstPtr getConstPlayer() const;\n\n        void setBirthSign(const std::string &sign);\n        const std::string &getBirthSign() const;\n\n        void setDrawState (MWMechanics::DrawState_ state);\n        MWMechanics::DrawState_ getDrawState(); /// \\todo constness\n\n        /// Activate the object under the crosshair, if any\n        void activate();\n\n        bool getAutoMove() const;\n        void setAutoMove (bool enable);\n\n        void setLeftRight (float value);\n\n        void setForwardBackward (float value);\n        void setUpDown(int value);\n\n        void setRunState(bool run);\n        void setSneak(bool sneak);\n\n        void yaw(float yaw);\n        void pitch(float pitch);\n        void roll(float roll);\n\n        bool wasTeleported() const;\n        void setTeleported(bool teleported);\n\n        void setAttackingOrSpell(bool attackingOrSpell);\n        bool getAttackingOrSpell() const;\n\n        void setJumping(bool jumping);\n        bool getJumping() const;\n\n        ///Checks all nearby actors to see if anyone has an aipackage against you\n        bool isInCombat();\n\n        bool enemiesNearby();\n\n        void clear();\n\n        void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;\n\n        bool readRecord (ESM::ESMReader& reader, uint32_t type);\n\n        int getNewCrimeId();  // get new id for witnesses\n        void recordCrimeId(); // record the paid crime id when bounty is 0\n        int getCrimeId() const;     // get the last paid crime id\n\n        void setPreviousItem(const std::string& boundItemId, const std::string& previousItemId);\n        std::string getPreviousItem(const std::string& boundItemId);\n        void erasePreviousItem(const std::string& boundItemId);\n\n        void setSelectedSpell(const std::string& spellId);\n    };\n}\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/projectilemanager.cpp",
    "content": "#include \"projectilemanager.hpp\"\n\n#include <iomanip>\n\n#include <memory>\n#include <optional>\n#include <osg/PositionAttitudeTransform>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/projectilestate.hpp>\n\n#include <components/misc/constants.hpp>\n#include <components/misc/convert.hpp>\n\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n\n#include <components/sceneutil/controller.hpp>\n#include <components/sceneutil/visitor.hpp>\n#include <components/sceneutil/lightmanager.hpp>\n\n#include \"../mwworld/manualref.hpp\"\n#include \"../mwworld/class.hpp\"\n#include \"../mwworld/esmstore.hpp\"\n#include \"../mwworld/inventorystore.hpp\"\n\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/environment.hpp\"\n\n#include \"../mwmechanics/combat.hpp\"\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n#include \"../mwmechanics/actorutil.hpp\"\n#include \"../mwmechanics/aipackage.hpp\"\n#include \"../mwmechanics/weapontype.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n#include \"../mwrender/vismask.hpp\"\n#include \"../mwrender/renderingmanager.hpp\"\n#include \"../mwrender/util.hpp\"\n\n#include \"../mwsound/sound.hpp\"\n\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwphysics/projectile.hpp\"\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\nnamespace\n{\n    ESM::EffectList getMagicBoltData(std::vector<std::string>& projectileIDs, std::set<std::string>& sounds, float& speed, std::string& texture, std::string& sourceName, const std::string& id)\n    {\n        const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();\n        const ESM::EffectList* effects;\n        if (const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(id)) // check if it's a spell\n        {\n            sourceName = spell->mName;\n            effects = &spell->mEffects;\n        }\n        else // check if it's an enchanted item\n        {\n            MWWorld::ManualRef ref(esmStore, id);\n            MWWorld::Ptr ptr = ref.getPtr();\n            const ESM::Enchantment* ench = esmStore.get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr));\n            sourceName = ptr.getClass().getName(ptr);\n            effects = &ench->mEffects;\n        }\n\n        int count = 0;\n        speed = 0.0f;\n        ESM::EffectList projectileEffects;\n        for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects->mList.begin());\n            iter!=effects->mList.end(); ++iter)\n        {\n            const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (\n                iter->mEffectID);\n\n            // Speed of multi-effect projectiles should be the average of the constituent effects,\n            // based on observation of the original engine.\n            speed += magicEffect->mData.mSpeed;\n            count++;\n\n            if (iter->mRange != ESM::RT_Target)\n                continue;\n\n            if (magicEffect->mBolt.empty())\n                projectileIDs.emplace_back(\"VFX_DefaultBolt\");\n            else\n                projectileIDs.push_back(magicEffect->mBolt);\n\n            static const std::string schools[] = {\n                \"alteration\", \"conjuration\", \"destruction\", \"illusion\", \"mysticism\", \"restoration\"\n            };\n            if (!magicEffect->mBoltSound.empty())\n                sounds.emplace(magicEffect->mBoltSound);\n            else\n                sounds.emplace(schools[magicEffect->mData.mSchool] + \" bolt\");\n            projectileEffects.mList.push_back(*iter);\n        }\n        \n        if (count != 0)\n            speed /= count;\n\n        // the particle texture is only used if there is only one projectile\n        if (projectileEffects.mList.size() == 1) \n        {\n            const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (\n                effects->mList.begin()->mEffectID);\n            texture = magicEffect->mParticle;\n        }\n        \n        if (projectileEffects.mList.size() > 1) // insert a VFX_Multiple projectile if there are multiple projectile effects\n        {\n            const std::string ID = \"VFX_Multiple\" + std::to_string(effects->mList.size());\n            std::vector<std::string>::iterator it;\n            it = projectileIDs.begin();\n            it = projectileIDs.insert(it, ID);\n        }\n        return projectileEffects;\n    }\n\n    osg::Vec4 getMagicBoltLightDiffuseColor(const ESM::EffectList& effects)\n    {\n        // Calculate combined light diffuse color from magical effects\n        osg::Vec4 lightDiffuseColor;\n        float lightDiffuseRed = 0.0f;\n        float lightDiffuseGreen = 0.0f;\n        float lightDiffuseBlue = 0.0f;\n        for (std::vector<ESM::ENAMstruct>::const_iterator iter(effects.mList.begin());\n            iter != effects.mList.end(); ++iter)\n        {\n            const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(\n                iter->mEffectID);\n            lightDiffuseRed += (static_cast<float>(magicEffect->mData.mRed) / 255.f);\n            lightDiffuseGreen += (static_cast<float>(magicEffect->mData.mGreen) / 255.f);\n            lightDiffuseBlue += (static_cast<float>(magicEffect->mData.mBlue) / 255.f);\n        }\n        int numberOfEffects = effects.mList.size();\n        lightDiffuseColor = osg::Vec4(lightDiffuseRed / numberOfEffects\n            , lightDiffuseGreen / numberOfEffects\n            , lightDiffuseBlue / numberOfEffects\n            , 1.0f);\n\n        return lightDiffuseColor;\n    }\n}\n\nnamespace MWWorld\n{\n\n    ProjectileManager::ProjectileManager(osg::Group* parent, Resource::ResourceSystem* resourceSystem,\n                                         MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics)\n        : mParent(parent)\n        , mResourceSystem(resourceSystem)\n        , mRendering(rendering)\n        , mPhysics(physics)\n        , mCleanupTimer(0.0f)\n    {\n\n    }\n\n    /// Rotates an osg::PositionAttitudeTransform over time.\n    class RotateCallback : public osg::NodeCallback\n    {\n    public:\n        RotateCallback(const osg::Vec3f& axis = osg::Vec3f(0,-1,0), float rotateSpeed = osg::PI*2)\n            : mAxis(axis)\n            , mRotateSpeed(rotateSpeed)\n        {\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osg::PositionAttitudeTransform* transform = static_cast<osg::PositionAttitudeTransform*>(node);\n\n            double time = nv->getFrameStamp()->getSimulationTime();\n\n            osg::Quat orient = osg::Quat(time * mRotateSpeed, mAxis);\n            transform->setAttitude(orient);\n\n            traverse(node, nv);\n        }\n\n    private:\n        osg::Vec3f mAxis;\n        float mRotateSpeed;\n    };\n\n\n    void ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient,\n                                        bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture)\n    {\n        state.mNode = new osg::PositionAttitudeTransform;\n        state.mNode->setNodeMask(MWRender::Mask_Effect);\n        state.mNode->setPosition(pos);\n        state.mNode->setAttitude(orient);\n\n        osg::Group* attachTo = state.mNode;\n\n        if (rotate)\n        {\n            osg::ref_ptr<osg::PositionAttitudeTransform> rotateNode (new osg::PositionAttitudeTransform);\n            rotateNode->addUpdateCallback(new RotateCallback());\n            state.mNode->addChild(rotateNode);\n            attachTo = rotateNode;\n        }\n\n        osg::ref_ptr<osg::Node> projectile = mResourceSystem->getSceneManager()->getInstance(model, attachTo);\n\n        if (state.mIdMagic.size() > 1)\n            for (size_t iter = 1; iter != state.mIdMagic.size(); ++iter)\n            {\n                std::ostringstream nodeName;\n                nodeName << \"Dummy\" << std::setw(2) << std::setfill('0') << iter;\n                const ESM::Weapon* weapon = MWBase::Environment::get().getWorld()->getStore().get<ESM::Weapon>().find (state.mIdMagic.at(iter));\n                SceneUtil::FindByNameVisitor findVisitor(nodeName.str());\n                attachTo->accept(findVisitor);\n                if (findVisitor.mFoundNode)\n                    mResourceSystem->getSceneManager()->getInstance(\"meshes\\\\\" + weapon->mModel, findVisitor.mFoundNode);\n            }\n\n        if (createLight)\n        {\n            osg::ref_ptr<osg::Light> projectileLight(new osg::Light);\n            projectileLight->setAmbient(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));\n            projectileLight->setDiffuse(lightDiffuseColor);\n            projectileLight->setSpecular(osg::Vec4(0.0f, 0.0f, 0.0f, 0.0f));\n            projectileLight->setConstantAttenuation(0.f);\n            projectileLight->setLinearAttenuation(0.1f);\n            projectileLight->setQuadraticAttenuation(0.f);\n            projectileLight->setPosition(osg::Vec4(pos, 1.0));\n            \n            SceneUtil::LightSource* projectileLightSource = new SceneUtil::LightSource;\n            projectileLightSource->setNodeMask(MWRender::Mask_Lighting);\n            projectileLightSource->setRadius(66.f);\n            \n            state.mNode->addChild(projectileLightSource);\n            projectileLightSource->setLight(projectileLight);\n        }\n        \n        SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor;\n        state.mNode->accept(disableFreezeOnCullVisitor);\n\n        state.mNode->addCullCallback(new SceneUtil::LightListCallback);\n\n        mParent->addChild(state.mNode);\n\n        state.mEffectAnimationTime.reset(new MWRender::EffectAnimationTime);\n\n        SceneUtil::AssignControllerSourcesVisitor assignVisitor (state.mEffectAnimationTime);\n        state.mNode->accept(assignVisitor);\n\n        MWRender::overrideFirstRootTexture(texture, mResourceSystem, projectile);\n    }\n\n    void ProjectileManager::update(State& state, float duration)\n    {\n        state.mEffectAnimationTime->addTime(duration);\n    }\n\n    void ProjectileManager::launchMagicBolt(const std::string &spellId, const Ptr &caster, const osg::Vec3f& fallbackDirection)\n    {\n        osg::Vec3f pos = caster.getRefData().getPosition().asVec3();\n        if (caster.getClass().isActor())\n        {\n            // Note: we ignore the collision box offset, this is required to make some flying creatures work as intended.\n            pos.z() += mPhysics->getRenderingHalfExtents(caster).z() * 2 * Constants::TorsoHeight;\n        }\n\n        if (MWBase::Environment::get().getWorld()->isUnderwater(caster.getCell(), pos)) // Underwater casting not possible\n            return;\n\n        osg::Quat orient;\n        if (caster.getClass().isActor())\n            orient = osg::Quat(caster.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))\n                    * osg::Quat(caster.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));\n        else\n            orient.makeRotate(osg::Vec3f(0,1,0), osg::Vec3f(fallbackDirection));\n\n        /*\n            Start of tes3mp addition\n\n            If the actor casting this is a LocalPlayer or LocalActor, track their projectile origin so it can be sent\n            in the next PlayerCast or ActorCast packet\n\n            Otherwise, set the projectileOrigin for a DedicatedPlayer or DedicatedActor\n        */\n        mwmp::Cast* localCast = MechanicsHelper::getLocalCast(caster);\n\n        if (localCast)\n        {\n            localCast->hasProjectile = true;\n            localCast->projectileOrigin.origin[0] = pos.x();\n            localCast->projectileOrigin.origin[1] = pos.y();\n            localCast->projectileOrigin.origin[2] = pos.z();\n            localCast->projectileOrigin.orientation[0] = orient.x();\n            localCast->projectileOrigin.orientation[1] = orient.y();\n            localCast->projectileOrigin.orientation[2] = orient.z();\n            localCast->projectileOrigin.orientation[3] = orient.w();\n        }\n        else\n        {\n            mwmp::Cast* dedicatedCast = MechanicsHelper::getDedicatedCast(caster);\n\n            if (dedicatedCast)\n            {\n                pos = osg::Vec3f(dedicatedCast->projectileOrigin.origin[0], dedicatedCast->projectileOrigin.origin[1], dedicatedCast->projectileOrigin.origin[2]);\n                orient = osg::Quat(dedicatedCast->projectileOrigin.orientation[0], dedicatedCast->projectileOrigin.orientation[1], dedicatedCast->projectileOrigin.orientation[2],\n                    dedicatedCast->projectileOrigin.orientation[3]);\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        MagicBoltState state;\n        state.mSpellId = spellId;\n        state.mCasterHandle = caster;\n        if (caster.getClass().isActor())\n            state.mActorId = caster.getClass().getCreatureStats(caster).getActorId();\n        else\n            state.mActorId = -1;\n\n        std::string texture;\n\n        state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);\n\n        // Non-projectile should have been removed by getMagicBoltData\n        if (state.mEffects.mList.empty())\n            return;\n\n        if (!caster.getClass().isActor() && fallbackDirection.length2() <= 0)\n        {\n            Log(Debug::Warning) << \"Unable to launch magic bolt (direction to target is empty)\";\n            return;\n        }\n\n        MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));\n        MWWorld::Ptr ptr = ref.getPtr();\n\n        osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);\n\n        auto model = ptr.getClass().getModel(ptr);\n        createModel(state, model, pos, orient, true, true, lightDiffuseColor, texture);\n\n        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n        for (const std::string &soundid : state.mSoundIds)\n        {\n            MWBase::Sound *sound = sndMgr->playSound3D(pos, soundid, 1.0f, 1.0f,\n                                                       MWSound::Type::Sfx, MWSound::PlayMode::Loop);\n            if (sound)\n                state.mSounds.push_back(sound);\n        }\n\n        // in case there are multiple effects, the model is a dummy without geometry. Use the second effect for physics shape\n        if (state.mIdMagic.size() > 1)\n            model = \"meshes\\\\\" + MWBase::Environment::get().getWorld()->getStore().get<ESM::Weapon>().find(state.mIdMagic.at(1))->mModel;\n        state.mProjectileId = mPhysics->addProjectile(caster, pos, model, true);\n        state.mToDelete = false;\n        mMagicBolts.push_back(state);\n    }\n\n    void ProjectileManager::launchProjectile(Ptr actor, ConstPtr projectile, const osg::Vec3f &pos, const osg::Quat &orient, Ptr bow, float speed, float attackStrength)\n    {\n        ProjectileState state;\n        state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();\n        state.mBowId = bow.getCellRef().getRefId();\n        state.mVelocity = orient * osg::Vec3f(0,1,0) * speed;\n        state.mIdArrow = projectile.getCellRef().getRefId();\n        state.mCasterHandle = actor;\n        state.mAttackStrength = attackStrength;\n        int type = projectile.get<ESM::Weapon>()->mBase->mData.mType;\n        state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown;\n\n        MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().getRefId());\n        MWWorld::Ptr ptr = ref.getPtr();\n\n        const auto model = ptr.getClass().getModel(ptr);\n        createModel(state, model, pos, orient, false, false, osg::Vec4(0,0,0,0));\n        if (!ptr.getClass().getEnchantment(ptr).empty())\n            SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));\n\n        state.mProjectileId = mPhysics->addProjectile(actor, pos, model, false);\n        state.mToDelete = false;\n        mProjectiles.push_back(state);\n    }\n\n    void ProjectileManager::updateCasters()\n    {\n        for (auto& state : mProjectiles)\n            mPhysics->setCaster(state.mProjectileId, state.getCaster());\n\n        for (auto& state : mMagicBolts)\n        {\n            // casters are identified by actor id in the savegame. objects doesn't have one so they can't be identified back.\n            // TODO: should object-type caster be restored from savegame?\n            if (state.mActorId == -1)\n                continue;\n\n            auto caster = state.getCaster();\n            if (caster.isEmpty())\n            {\n                Log(Debug::Error) << \"Couldn't find caster with ID \" << state.mActorId;\n                cleanupMagicBolt(state);\n                continue;\n            }\n            mPhysics->setCaster(state.mProjectileId, caster);\n        }\n    }\n\n    void ProjectileManager::update(float dt)\n    {\n        periodicCleanup(dt);\n        moveProjectiles(dt);\n        moveMagicBolts(dt);\n    }\n\n    void ProjectileManager::periodicCleanup(float dt)\n    {\n        mCleanupTimer -= dt;\n        if (mCleanupTimer <= 0.0f)\n        {\n            mCleanupTimer = 2.0f;\n\n            auto isCleanable = [](const ProjectileManager::State& state) -> bool\n            {\n                const float farawayThreshold = 72000.0f;\n                osg::Vec3 playerPos = MWMechanics::getPlayer().getRefData().getPosition().asVec3();\n                return (state.mNode->getPosition() - playerPos).length2() >= farawayThreshold*farawayThreshold;\n            };\n\n            for (auto& projectileState : mProjectiles)\n            {\n                if (isCleanable(projectileState))\n                    cleanupProjectile(projectileState);\n            }\n\n            for (auto& magicBoltState : mMagicBolts)\n            {\n                if (isCleanable(magicBoltState))\n                    cleanupMagicBolt(magicBoltState);\n            }\n        }\n    }\n\n    void ProjectileManager::moveMagicBolts(float duration)\n    {\n        for (auto& magicBoltState : mMagicBolts)\n        {\n            if (magicBoltState.mToDelete)\n                continue;\n\n            auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);\n            if (!projectile->isActive())\n                continue;\n            // If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame.\n            MWWorld::Ptr caster = magicBoltState.getCaster();\n            if (!caster.isEmpty() && caster.getClass().isActor())\n            {\n                if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead())\n                {\n                    cleanupMagicBolt(magicBoltState);\n                    continue;\n                }\n            }\n\n            osg::Quat orient = magicBoltState.mNode->getAttitude();\n            static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()\n                        .find(\"fTargetSpellMaxSpeed\")->mValue.getFloat();\n            float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed;\n            osg::Vec3f direction = orient * osg::Vec3f(0,1,0);\n            direction.normalize();\n            osg::Vec3f newPos = projectile->getPosition() + direction * duration * speed;\n\n            update(magicBoltState, duration);\n\n            // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.\n            std::vector<MWWorld::Ptr> targetActors;\n            if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())\n                caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);\n            projectile->setValidTargets(targetActors);\n\n            mPhysics->updateProjectile(magicBoltState.mProjectileId, newPos);\n        }\n    }\n\n    void ProjectileManager::moveProjectiles(float duration)\n    {\n        for (auto& projectileState : mProjectiles)\n        {\n            if (projectileState.mToDelete)\n                continue;\n\n            auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);\n            if (!projectile->isActive())\n                continue;\n            // gravity constant - must be way lower than the gravity affecting actors, since we're not\n            // simulating aerodynamics at all\n            projectileState.mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration;\n\n            osg::Vec3f newPos = projectile->getPosition() + projectileState.mVelocity * duration;\n\n            // rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction.\n            if (!projectileState.mThrown)\n            {\n                osg::Quat orient;\n                orient.makeRotate(osg::Vec3f(0,1,0), projectileState.mVelocity);\n                projectileState.mNode->setAttitude(orient);\n            }\n\n            update(projectileState, duration);\n\n            MWWorld::Ptr caster = projectileState.getCaster();\n\n            // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.\n            std::vector<MWWorld::Ptr> targetActors;\n            if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())\n                caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);\n            projectile->setValidTargets(targetActors);\n\n            mPhysics->updateProjectile(projectileState.mProjectileId, newPos);\n        }\n    }\n\n    void ProjectileManager::processHits()\n    {\n        for (auto& projectileState : mProjectiles)\n        {\n            if (projectileState.mToDelete)\n                continue;\n\n            auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);\n\n            const auto pos = projectile->getPosition();\n            projectileState.mNode->setPosition(pos);\n\n            if (projectile->isActive())\n                continue;\n\n            const auto target = projectile->getTarget();\n            auto caster = projectileState.getCaster();\n            assert(target != caster);\n\n            if (caster.isEmpty())\n                caster = target;\n\n            // Try to get a Ptr to the bow that was used. It might no longer exist.\n            MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), projectileState.mIdArrow);\n            MWWorld::Ptr bow = projectileRef.getPtr();\n            if (!caster.isEmpty() && projectileState.mIdArrow != projectileState.mBowId)\n            {\n                MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);\n                MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);\n                if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))\n                    bow = *invIt;\n            }\n            if (projectile->getHitWater())\n                mRendering->emitWaterRipple(pos);\n\n            MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);\n            cleanupProjectile(projectileState);\n        }\n        for (auto& magicBoltState : mMagicBolts)\n        {\n            if (magicBoltState.mToDelete)\n                continue;\n\n            auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);\n\n            const auto pos = projectile->getPosition();\n            magicBoltState.mNode->setPosition(pos);\n            for (const auto& sound : magicBoltState.mSounds)\n                sound->setPosition(pos);\n\n            if (projectile->isActive())\n                continue;\n\n            const auto target = projectile->getTarget();\n            const auto caster = magicBoltState.getCaster();\n            assert(target != caster);\n\n            MWMechanics::CastSpell cast(caster, target);\n            cast.mHitPosition = pos;\n            cast.mId = magicBoltState.mSpellId;\n            cast.mSourceName = magicBoltState.mSourceName;\n            cast.mStack = false;\n            cast.inflict(target, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);\n\n            MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, target, ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);\n            cleanupMagicBolt(magicBoltState);\n        }\n        mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(), [](const State& state) { return state.mToDelete; }),\n                mProjectiles.end());\n        mMagicBolts.erase(std::remove_if(mMagicBolts.begin(), mMagicBolts.end(), [](const State& state) { return state.mToDelete; }),\n                mMagicBolts.end());\n    }\n\n    void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state)\n    {\n        mParent->removeChild(state.mNode);\n        mPhysics->removeProjectile(state.mProjectileId);\n        state.mToDelete = true;\n    }\n\n    void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state)\n    {\n        mParent->removeChild(state.mNode);\n        mPhysics->removeProjectile(state.mProjectileId);\n        state.mToDelete = true;\n        for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++)\n        {\n            MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter));\n        }\n    }\n\n    void ProjectileManager::clear()\n    {\n        for (auto& mProjectile : mProjectiles)\n            cleanupProjectile(mProjectile);\n        mProjectiles.clear();\n\n        for (auto& mMagicBolt : mMagicBolts)\n            cleanupMagicBolt(mMagicBolt);\n        mMagicBolts.clear();\n    }\n\n    void ProjectileManager::write(ESM::ESMWriter &writer, Loading::Listener &progress) const\n    {\n        for (std::vector<ProjectileState>::const_iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)\n        {\n            writer.startRecord(ESM::REC_PROJ);\n\n            ESM::ProjectileState state;\n            state.mId = it->mIdArrow;\n            state.mPosition = ESM::Vector3(osg::Vec3f(it->mNode->getPosition()));\n            state.mOrientation = ESM::Quaternion(osg::Quat(it->mNode->getAttitude()));\n            state.mActorId = it->mActorId;\n\n            state.mBowId = it->mBowId;\n            state.mVelocity = it->mVelocity;\n            state.mAttackStrength = it->mAttackStrength;\n\n            state.save(writer);\n\n            writer.endRecord(ESM::REC_PROJ);\n        }\n\n        for (std::vector<MagicBoltState>::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)\n        {\n            writer.startRecord(ESM::REC_MPRJ);\n\n            ESM::MagicBoltState state;\n            state.mId = it->mIdMagic.at(0);\n            state.mPosition = ESM::Vector3(osg::Vec3f(it->mNode->getPosition()));\n            state.mOrientation = ESM::Quaternion(osg::Quat(it->mNode->getAttitude()));\n            state.mActorId = it->mActorId;\n\n            state.mSpellId = it->mSpellId;\n            state.mSpeed = it->mSpeed;\n\n            state.save(writer);\n\n            writer.endRecord(ESM::REC_MPRJ);\n        }\n    }\n\n    bool ProjectileManager::readRecord(ESM::ESMReader &reader, uint32_t type)\n    {\n        if (type == ESM::REC_PROJ)\n        {\n            ESM::ProjectileState esm;\n            esm.load(reader);\n\n            ProjectileState state;\n            state.mActorId = esm.mActorId;\n            state.mBowId = esm.mBowId;\n            state.mVelocity = esm.mVelocity;\n            state.mIdArrow = esm.mId;\n            state.mAttackStrength = esm.mAttackStrength;\n            state.mToDelete = false;\n\n            std::string model;\n            try\n            {\n                MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);\n                MWWorld::Ptr ptr = ref.getPtr();\n                model = ptr.getClass().getModel(ptr);\n                int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;\n                state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;\n\n                state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, false);\n            }\n            catch(...)\n            {\n                return true;\n            }\n\n            createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), false, false, osg::Vec4(0,0,0,0));\n\n            mProjectiles.push_back(state);\n            return true;\n        }\n        if (type == ESM::REC_MPRJ)\n        {\n            ESM::MagicBoltState esm;\n            esm.load(reader);\n\n            MagicBoltState state;\n            state.mIdMagic.push_back(esm.mId);\n            state.mSpellId = esm.mSpellId;\n            state.mActorId = esm.mActorId;\n            state.mToDelete = false;\n            std::string texture;\n\n            try\n            {\n                state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);\n            }\n            catch(...)\n            {\n                Log(Debug::Warning) << \"Warning: Failed to recreate magic projectile from saved data (id \\\"\" << state.mSpellId << \"\\\" no longer exists?)\";\n                return true;\n            }\n\n            state.mSpeed = esm.mSpeed; // speed is derived from non-projectile effects as well as\n                                       // projectile effects, so we can't calculate it from the save\n                                       // file's effect list, which is already trimmed of non-projectile\n                                       // effects. We need to use the stored value.\n\n            std::string model;\n            try\n            {\n                MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));\n                MWWorld::Ptr ptr = ref.getPtr();\n                model = ptr.getClass().getModel(ptr);\n            }\n            catch(...)\n            {\n                return true;\n            }\n\n            osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);\n            createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);\n            state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, true);\n\n            MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n            for (const std::string &soundid : state.mSoundIds)\n            {\n                MWBase::Sound *sound = sndMgr->playSound3D(esm.mPosition, soundid, 1.0f, 1.0f,\n                                                           MWSound::Type::Sfx, MWSound::PlayMode::Loop);\n                if (sound)\n                    state.mSounds.push_back(sound);\n            }\n\n            mMagicBolts.push_back(state);\n            return true;\n        }\n\n        return false;\n    }\n\n    int ProjectileManager::countSavedGameRecords() const\n    {\n        return mMagicBolts.size() + mProjectiles.size();\n    }\n\n    MWWorld::Ptr ProjectileManager::State::getCaster()\n    {\n        if (!mCasterHandle.isEmpty())\n            return mCasterHandle;\n\n        return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId);\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/projectilemanager.hpp",
    "content": "#ifndef OPENMW_MWWORLD_PROJECTILEMANAGER_H\n#define OPENMW_MWWORLD_PROJECTILEMANAGER_H\n\n#include <string>\n\n#include <osg/ref_ptr>\n#include <osg/PositionAttitudeTransform>\n\n#include <components/esm/effectlist.hpp>\n\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"ptr.hpp\"\n\nnamespace MWPhysics\n{\n    class PhysicsSystem;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace osg\n{\n    class Group;\n    class Quat;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace MWRender\n{\n    class EffectAnimationTime;\n    class RenderingManager;\n}\n\nnamespace MWWorld\n{\n\n    class ProjectileManager\n    {\n    public:\n        ProjectileManager (osg::Group* parent, Resource::ResourceSystem* resourceSystem,\n                MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics);\n\n        /// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used.\n        void launchMagicBolt (const std::string &spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection);\n\n        void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,\n                                       const osg::Vec3f& pos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength);\n\n        void updateCasters();\n\n        void update(float dt);\n\n        void processHits();\n\n        /// Removes all current projectiles. Should be called when switching to a new worldspace.\n        void clear();\n\n        void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;\n        bool readRecord (ESM::ESMReader& reader, uint32_t type);\n        int countSavedGameRecords() const;\n\n    private:\n        osg::ref_ptr<osg::Group> mParent;\n        Resource::ResourceSystem* mResourceSystem;\n        MWRender::RenderingManager* mRendering;\n        MWPhysics::PhysicsSystem* mPhysics;\n        float mCleanupTimer;\n\n        struct State\n        {\n            osg::ref_ptr<osg::PositionAttitudeTransform> mNode;\n            std::shared_ptr<MWRender::EffectAnimationTime> mEffectAnimationTime;\n\n            int mActorId;\n            int mProjectileId;\n\n            // TODO: this will break when the game is saved and reloaded, since there is currently\n            // no way to write identifiers for non-actors to a savegame.\n            MWWorld::Ptr mCasterHandle;\n\n            MWWorld::Ptr getCaster();\n\n            // MW-ids of a magic projectile\n            std::vector<std::string> mIdMagic;\n\n            // MW-id of an arrow projectile\n            std::string mIdArrow;\n\n            bool mToDelete;\n        };\n\n        struct MagicBoltState : public State\n        {\n            std::string mSpellId;\n\n            // Name of item to display as effect source in magic menu (in case we casted an enchantment)\n            std::string mSourceName;\n\n            ESM::EffectList mEffects;\n\n            float mSpeed;\n\n            std::vector<MWBase::Sound*> mSounds;\n            std::set<std::string> mSoundIds;\n        };\n\n        struct ProjectileState : public State\n        {\n            // RefID of the bow or crossbow the actor was using when this projectile was fired (may be empty)\n            std::string mBowId;\n\n            osg::Vec3f mVelocity;\n            float mAttackStrength;\n            bool mThrown;\n        };\n\n        std::vector<MagicBoltState> mMagicBolts;\n        std::vector<ProjectileState> mProjectiles;\n\n        void cleanupProjectile(ProjectileState& state);\n        void cleanupMagicBolt(MagicBoltState& state);\n        void periodicCleanup(float dt);\n\n        void moveProjectiles(float dt);\n        void moveMagicBolts(float dt);\n\n        void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient,\n                            bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = \"\");\n        void update (State& state, float duration);\n\n        void operator=(const ProjectileManager&);\n        ProjectileManager(const ProjectileManager&);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/ptr.cpp",
    "content": "#include \"ptr.hpp\"\n\n#include <cassert>\n\n#include \"containerstore.hpp\"\n#include \"class.hpp\"\n#include \"livecellref.hpp\"\n\nconst std::string& MWWorld::Ptr::getTypeName() const\n{\n    if(mRef != nullptr)\n        return mRef->mClass->getTypeName();\n    throw std::runtime_error(\"Can't get type name from an empty object.\");\n}\n\nMWWorld::LiveCellRefBase *MWWorld::Ptr::getBase() const\n{\n    if (!mRef)\n        throw std::runtime_error (\"Can't access cell ref pointed to by null Ptr\");\n\n    return mRef;\n}\n\nMWWorld::CellRef& MWWorld::Ptr::getCellRef() const\n{\n    assert(mRef);\n\n    return mRef->mRef;\n}\n\nMWWorld::RefData& MWWorld::Ptr::getRefData() const\n{\n    assert(mRef);\n\n    return mRef->mData;\n}\n\nvoid MWWorld::Ptr::setContainerStore (ContainerStore *store)\n{\n    assert (store);\n    assert (!mCell);\n\n    mContainerStore = store;\n}\n\nMWWorld::ContainerStore *MWWorld::Ptr::getContainerStore() const\n{\n    return mContainerStore;\n}\n\nMWWorld::Ptr::operator const void *()\n{\n    return mRef;\n}\n\n// -------------------------------------------------------------------------------\n\nconst std::string &MWWorld::ConstPtr::getTypeName() const\n{\n    if(mRef != nullptr)\n        return mRef->mClass->getTypeName();\n    throw std::runtime_error(\"Can't get type name from an empty object.\");\n}\n\nconst MWWorld::LiveCellRefBase *MWWorld::ConstPtr::getBase() const\n{\n    if (!mRef)\n        throw std::runtime_error (\"Can't access cell ref pointed to by null Ptr\");\n\n    return mRef;\n}\n\nvoid MWWorld::ConstPtr::setContainerStore (const ContainerStore *store)\n{\n    assert (store);\n    assert (!mCell);\n\n    mContainerStore = store;\n}\n\nconst MWWorld::ContainerStore *MWWorld::ConstPtr::getContainerStore() const\n{\n    return mContainerStore;\n}\n\nMWWorld::ConstPtr::operator const void *()\n{\n    return mRef;\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/ptr.hpp",
    "content": "#ifndef GAME_MWWORLD_PTR_H\n#define GAME_MWWORLD_PTR_H\n\n#include <cassert>\n\n#include <string>\n#include <sstream>\n\n#include \"livecellref.hpp\"\n\nnamespace MWWorld\n{\n    class ContainerStore;\n    class CellStore;\n    struct LiveCellRefBase;\n\n    /// \\brief Pointer to a LiveCellRef\n\n    class Ptr\n    {\n        public:\n\n            MWWorld::LiveCellRefBase *mRef;\n            CellStore *mCell;\n            ContainerStore *mContainerStore;\n\n        public:\n            Ptr(MWWorld::LiveCellRefBase *liveCellRef=nullptr, CellStore *cell=nullptr)\n              : mRef(liveCellRef), mCell(cell), mContainerStore(nullptr)\n            {\n            }\n\n            bool isEmpty() const\n            {\n                return mRef == nullptr;\n            }\n\n            const std::string& getTypeName() const;\n\n            const Class& getClass() const\n            {\n                if(mRef != nullptr)\n                    return *(mRef->mClass);\n                throw std::runtime_error(\"Cannot get class of an empty object\");\n            }\n\n            template<typename T>\n            MWWorld::LiveCellRef<T> *get() const\n            {\n                MWWorld::LiveCellRef<T> *ref = dynamic_cast<MWWorld::LiveCellRef<T>*>(mRef);\n                if(ref) return ref;\n\n                std::stringstream str;\n                str<< \"Bad LiveCellRef cast to \"<<typeid(T).name()<<\" from \";\n                /*\n                    Start of tes3mp change (major)\n\n                    Print additional information\n                */\n                if(mRef != nullptr) str<< getTypeName() << \" \" << mRef->mRef.getRefId().c_str() << \" \" << mRef->mRef.getRefNum().mIndex << \"-\" << mRef->mRef.getMpNum();\n                /*\n                    End of tes3mp change (major)\n                */\n\n                else str<< \"an empty object\";\n\n                throw std::runtime_error(str.str());\n            }\n\n            MWWorld::LiveCellRefBase *getBase() const;\n\n            MWWorld::CellRef& getCellRef() const;\n\n            RefData& getRefData() const;\n\n            CellStore *getCell() const\n            {\n                assert(mCell);\n                return mCell;\n            }\n\n            bool isInCell() const\n            {\n                return (mContainerStore == nullptr) && (mCell != nullptr);\n            }\n\n            void setContainerStore (ContainerStore *store);\n            ///< Must not be called on references that are in a cell.\n\n            ContainerStore *getContainerStore() const;\n            ///< May return a 0-pointer, if reference is not in a container.\n\n            operator const void *();\n            ///< Return a 0-pointer, if Ptr is empty; return a non-0-pointer, if Ptr is not empty\n    };\n\n    /// \\brief Pointer to a const LiveCellRef\n    /// @note a Ptr can be implicitely converted to a ConstPtr, but you can not convert a ConstPtr to a Ptr.\n    class ConstPtr\n    {\n    public:\n\n        const MWWorld::LiveCellRefBase *mRef;\n        const CellStore *mCell;\n        const ContainerStore *mContainerStore;\n\n    public:\n        ConstPtr(const MWWorld::LiveCellRefBase *liveCellRef=nullptr, const CellStore *cell=nullptr)\n          : mRef(liveCellRef), mCell(cell), mContainerStore(nullptr)\n        {\n        }\n\n        ConstPtr(const MWWorld::Ptr& ptr)\n            : mRef(ptr.mRef), mCell(ptr.mCell), mContainerStore(ptr.mContainerStore)\n        {\n        }\n\n        bool isEmpty() const\n        {\n            return mRef == nullptr;\n        }\n\n        const std::string& getTypeName() const;\n\n        const Class& getClass() const\n        {\n            if(mRef != nullptr)\n                return *(mRef->mClass);\n            throw std::runtime_error(\"Cannot get class of an empty object\");\n        }\n\n        template<typename T>\n        const MWWorld::LiveCellRef<T> *get() const\n        {\n            const MWWorld::LiveCellRef<T> *ref = dynamic_cast<const MWWorld::LiveCellRef<T>*>(mRef);\n            if(ref) return ref;\n\n            std::stringstream str;\n            str<< \"Bad LiveCellRef cast to \"<<typeid(T).name()<<\" from \";\n            /*\n                Start of tes3mp change (major)\n\n                Print additional information\n            */\n            if(mRef != nullptr) str<< getTypeName() << \" \" << mRef->mRef.getRefId().c_str() << \" \" << mRef->mRef.getRefNum().mIndex << \"-\" << mRef->mRef.getMpNum();\n            /*\n                End of tes3mp change (major)\n            */\n            else str<< \"an empty object\";\n\n            throw std::runtime_error(str.str());\n        }\n\n        const MWWorld::LiveCellRefBase *getBase() const;\n\n        const MWWorld::CellRef& getCellRef() const\n        {\n            assert(mRef);\n            return mRef->mRef;\n        }\n\n        const RefData& getRefData() const\n        {\n            assert(mRef);\n            return mRef->mData;\n        }\n\n        const CellStore *getCell() const\n        {\n            assert(mCell);\n            return mCell;\n        }\n\n        bool isInCell() const\n        {\n            return (mContainerStore == nullptr) && (mCell != nullptr);\n        }\n        \n        void setContainerStore (const ContainerStore *store);\n        ///< Must not be called on references that are in a cell.\n        \n        const ContainerStore *getContainerStore() const;\n        ///< May return a 0-pointer, if reference is not in a container.\n\n        operator const void *();\n        ///< Return a 0-pointer, if Ptr is empty; return a non-0-pointer, if Ptr is not empty\n    };\n\n    inline bool operator== (const Ptr& left, const Ptr& right)\n    {\n        return left.mRef==right.mRef;\n    }\n\n    inline bool operator!= (const Ptr& left, const Ptr& right)\n    {\n        return !(left==right);\n    }\n\n    inline bool operator< (const Ptr& left, const Ptr& right)\n    {\n        return left.mRef<right.mRef;\n    }\n\n    inline bool operator>= (const Ptr& left, const Ptr& right)\n    {\n        return !(left<right);\n    }\n\n    inline bool operator> (const Ptr& left, const Ptr& right)\n    {\n        return right<left;\n    }\n\n    inline bool operator<= (const Ptr& left, const Ptr& right)\n    {\n        return !(left>right);\n    }\n\n    inline bool operator== (const ConstPtr& left, const ConstPtr& right)\n    {\n        return left.mRef==right.mRef;\n    }\n\n    inline bool operator!= (const ConstPtr& left, const ConstPtr& right)\n    {\n        return !(left==right);\n    }\n\n    inline bool operator< (const ConstPtr& left, const ConstPtr& right)\n    {\n        return left.mRef<right.mRef;\n    }\n\n    inline bool operator>= (const ConstPtr& left, const ConstPtr& right)\n    {\n        return !(left<right);\n    }\n\n    inline bool operator> (const ConstPtr& left, const ConstPtr& right)\n    {\n        return right<left;\n    }\n\n    inline bool operator<= (const ConstPtr& left, const ConstPtr& right)\n    {\n        return !(left>right);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/recordcmp.hpp",
    "content": "#ifndef OPENMW_MWWORLD_RECORDCMP_H\n#define OPENMW_MWWORLD_RECORDCMP_H\n\n#include <components/esm/records.hpp>\n\n#include <components/misc/stringops.hpp>\n\nnamespace MWWorld\n{\n    struct RecordCmp\n    {\n        template <class T>\n        bool operator()(const T &x, const T& y) const {\n            return x.mId < y.mId;\n        }\n    };\n\n    template <>\n    inline bool RecordCmp::operator()<ESM::Dialogue>(const ESM::Dialogue &x, const ESM::Dialogue &y) const {\n        return Misc::StringUtils::ciLess(x.mId, y.mId);\n    }\n\n    template <>\n    inline bool RecordCmp::operator()<ESM::Cell>(const ESM::Cell &x, const ESM::Cell &y) const {\n        return Misc::StringUtils::ciLess(x.mName, y.mName);\n    }\n\n    template <>\n    inline bool RecordCmp::operator()<ESM::Pathgrid>(const ESM::Pathgrid &x, const ESM::Pathgrid &y) const {\n        return Misc::StringUtils::ciLess(x.mCell, y.mCell);\n    }\n\n} // end namespace\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/refdata.cpp",
    "content": "#include \"refdata.hpp\"\n\n#include <components/esm/objectstate.hpp>\n\n#include \"customdata.hpp\"\n#include \"cellstore.hpp\"\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n\nnamespace\n{\nenum RefDataFlags\n{\n    Flag_SuppressActivate = 1, // If set, activation will be suppressed and redirected to the OnActivate flag, which can then be handled by a script.\n    Flag_OnActivate = 2,\n    Flag_ActivationBuffered = 4\n};\n}\n\nnamespace MWWorld\n{\n\n    void RefData::copy (const RefData& refData)\n    {\n        mBaseNode = refData.mBaseNode;\n        mLocals = refData.mLocals;\n        mEnabled = refData.mEnabled;\n        mCount = refData.mCount;\n        mPosition = refData.mPosition;\n        mChanged = refData.mChanged;\n        mDeletedByContentFile = refData.mDeletedByContentFile;\n        mFlags = refData.mFlags;\n\n        mAnimationState = refData.mAnimationState;\n\n        mCustomData = refData.mCustomData ? refData.mCustomData->clone() : nullptr;\n    }\n\n    void RefData::cleanup()\n    {\n        mBaseNode = nullptr;\n        mCustomData = nullptr;\n    }\n\n    RefData::RefData()\n    : mBaseNode(nullptr), mDeletedByContentFile(false), mEnabled (true), mCount (1), mCustomData (nullptr), mChanged(false), mFlags(0)\n    {\n        for (int i=0; i<3; ++i)\n        {\n            mPosition.pos[i] = 0;\n            mPosition.rot[i] = 0;\n        }\n    }\n\n    RefData::RefData (const ESM::CellRef& cellRef)\n    : mBaseNode(nullptr), mDeletedByContentFile(false), mEnabled (true),\n      mCount (1), mPosition (cellRef.mPos),\n      mCustomData (nullptr),\n      mChanged(false), mFlags(0) // Loading from ESM/ESP files -> assume unchanged\n    {\n    }\n\n    RefData::RefData (const ESM::ObjectState& objectState, bool deletedByContentFile)\n    : mBaseNode(nullptr), mDeletedByContentFile(deletedByContentFile),\n      mEnabled (objectState.mEnabled != 0),\n      mCount (objectState.mCount),\n      mPosition (objectState.mPosition),\n      mAnimationState(objectState.mAnimationState),\n      mCustomData (nullptr),\n      mChanged(true), mFlags(objectState.mFlags) // Loading from a savegame -> assume changed\n    {\n        // \"Note that the ActivationFlag_UseEnabled is saved to the reference,\n        // which will result in permanently suppressed activation if the reference script is removed.\n        // This occurred when removing the animated containers mod, and the fix in MCP is to reset UseEnabled to true on loading a game.\"\n        mFlags &= (~Flag_SuppressActivate);\n    }\n\n    RefData::RefData (const RefData& refData)\n    : mBaseNode(nullptr), mCustomData (nullptr)\n    {\n        try\n        {\n            copy (refData);\n            mFlags &= ~(Flag_SuppressActivate|Flag_OnActivate|Flag_ActivationBuffered);\n        }\n        catch (...)\n        {\n            cleanup();\n            throw;\n        }\n    }\n\n    void RefData::write (ESM::ObjectState& objectState, const std::string& scriptId) const\n    {\n        objectState.mHasLocals = mLocals.write (objectState.mLocals, scriptId);\n\n        objectState.mEnabled = mEnabled;\n        objectState.mCount = mCount;\n        objectState.mPosition = mPosition;\n        objectState.mFlags = mFlags;\n\n        objectState.mAnimationState = mAnimationState;\n    }\n\n    RefData& RefData::operator= (const RefData& refData)\n    {\n        try\n        {\n            cleanup();\n            copy (refData);\n        }\n        catch (...)\n        {\n            cleanup();\n            throw;\n        }\n\n        return *this;\n    }\n\n    RefData::~RefData()\n    {\n        try\n        {\n            cleanup();\n        }\n        catch (...)\n        {}\n    }\n\n    void RefData::setBaseNode(SceneUtil::PositionAttitudeTransform *base)\n    {\n        mBaseNode = base;\n    }\n\n    SceneUtil::PositionAttitudeTransform* RefData::getBaseNode()\n    {\n        return mBaseNode;\n    }\n\n    const SceneUtil::PositionAttitudeTransform* RefData::getBaseNode() const\n    {\n        return mBaseNode;\n    }\n\n    int RefData::getCount(bool absolute) const\n    {\n        if(absolute)\n            return std::abs(mCount);\n        return mCount;\n    }\n\n    void RefData::setLocals (const ESM::Script& script)\n    {\n        if (mLocals.configure (script) && !mLocals.isEmpty())\n            mChanged = true;\n    }\n\n    void RefData::setCount (int count)\n    {\n        if(count == 0)\n            MWBase::Environment::get().getWorld()->removeRefScript(this);\n\n        mChanged = true;\n\n        mCount = count;\n    }\n\n    void RefData::setDeletedByContentFile(bool deleted)\n    {\n        mDeletedByContentFile = deleted;\n    }\n\n    bool RefData::isDeleted() const\n    {\n        return mDeletedByContentFile || mCount == 0;\n    }\n\n    bool RefData::isDeletedByContentFile() const\n    {\n        return mDeletedByContentFile;\n    }\n\n    MWScript::Locals& RefData::getLocals()\n    {\n        return mLocals;\n    }\n\n    bool RefData::isEnabled() const\n    {\n        return mEnabled;\n    }\n\n    void RefData::enable()\n    {\n        if (!mEnabled)\n        {\n            mChanged = true;\n            mEnabled = true;\n        }\n    }\n\n    void RefData::disable()\n    {\n        if (mEnabled)\n        {\n            mChanged = true;\n            mEnabled = false;\n        }\n    }\n\n    void RefData::setPosition(const ESM::Position& pos)\n    {\n        mChanged = true;\n        mPosition = pos;\n    }\n\n    const ESM::Position& RefData::getPosition() const\n    {\n        return mPosition;\n    }\n\n    void RefData::setCustomData(std::unique_ptr<CustomData>&& value) noexcept\n    {\n        mChanged = true; // We do not currently track CustomData, so assume anything with a CustomData is changed\n        mCustomData = std::move(value);\n    }\n\n    CustomData *RefData::getCustomData()\n    {\n        return mCustomData.get();\n    }\n\n    const CustomData *RefData::getCustomData() const\n    {\n        return mCustomData.get();\n    }\n\n    bool RefData::hasChanged() const\n    {\n        return mChanged || !mAnimationState.empty();\n    }\n\n    bool RefData::activateByScript()\n    {\n        bool ret = (mFlags & Flag_ActivationBuffered);\n        mFlags &= ~(Flag_SuppressActivate|Flag_OnActivate);\n        return ret;\n    }\n\n    bool RefData::activate()\n    {\n        if (mFlags & Flag_SuppressActivate)\n        {\n            mFlags |= Flag_OnActivate|Flag_ActivationBuffered;\n            return false;\n        }\n        else\n        {\n            return true;\n        }\n    }\n\n    bool RefData::onActivate()\n    {\n        bool ret = mFlags & Flag_OnActivate;\n        mFlags |= Flag_SuppressActivate;\n        mFlags &= (~Flag_OnActivate);\n        return ret;\n    }\n\n    const ESM::AnimationState& RefData::getAnimationState() const\n    {\n        return mAnimationState;\n    }\n\n    ESM::AnimationState& RefData::getAnimationState()\n    {\n        return mAnimationState;\n    }\n\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/refdata.hpp",
    "content": "#ifndef GAME_MWWORLD_REFDATA_H\n#define GAME_MWWORLD_REFDATA_H\n\n#include <components/esm/defs.hpp>\n#include <components/esm/animationstate.hpp>\n\n#include \"../mwscript/locals.hpp\"\n#include \"../mwworld/customdata.hpp\"\n\n#include <string>\n#include <memory>\n\nnamespace SceneUtil\n{\n    class PositionAttitudeTransform;\n}\n\nnamespace ESM\n{\n    class Script;\n    class CellRef;\n    struct ObjectState;\n}\n\nnamespace MWWorld\n{\n\n    class CustomData;\n\n    class RefData\n    {\n            SceneUtil::PositionAttitudeTransform* mBaseNode;\n\n            MWScript::Locals mLocals;\n\n            /// separate delete flag used for deletion by a content file\n            /// @note not stored in the save game file.\n            bool mDeletedByContentFile;\n\n            bool mEnabled;\n\n            /// 0: deleted\n            int mCount;\n\n            ESM::Position mPosition;\n\n            ESM::AnimationState mAnimationState;\n\n            std::unique_ptr<CustomData> mCustomData;\n\n            void copy (const RefData& refData);\n\n            void cleanup();\n\n            bool mChanged;\n\n            unsigned int mFlags;\n\n        public:\n\n            RefData();\n\n            /// @param cellRef Used to copy constant data such as position into this class where it can\n            /// be altered without affecting the original data. This makes it possible\n            /// to reset the position as the original data is still held in the CellRef\n            RefData (const ESM::CellRef& cellRef);\n\n            RefData (const ESM::ObjectState& objectState, bool deletedByContentFile);\n            ///< Ignores local variables and custom data (not enough context available here to\n            /// perform these operations).\n\n            RefData (const RefData& refData);\n            RefData (RefData&& other) noexcept = default;\n\n            ~RefData();\n\n            void write (ESM::ObjectState& objectState, const std::string& scriptId = \"\") const;\n            ///< Ignores custom data (not enough context available here to\n            /// perform this operations).\n\n            RefData& operator= (const RefData& refData);\n            RefData& operator= (RefData&& other) noexcept = default;\n\n            /// Return base node (can be a null pointer).\n            SceneUtil::PositionAttitudeTransform* getBaseNode();\n\n            /// Return base node (can be a null pointer).\n            const SceneUtil::PositionAttitudeTransform* getBaseNode() const;\n\n            /// Set base node (can be a null pointer).\n            void setBaseNode (SceneUtil::PositionAttitudeTransform* base);\n\n            int getCount(bool absolute = true) const;\n\n            void setLocals (const ESM::Script& script);\n\n            void setCount (int count);\n            ///< Set object count (an object pile is a simple object with a count >1).\n            ///\n            /// \\warning Do not call setCount() to add or remove objects from a\n            /// container or an actor's inventory. Call ContainerStore::add() or\n            /// ContainerStore::remove() instead.\n\n            /// This flag is only used for content stack loading and will not be stored in the savegame.\n            /// If the object was deleted by gameplay, then use setCount(0) instead.\n            void setDeletedByContentFile(bool deleted);\n\n            /// Returns true if the object was either deleted by the content file or by gameplay.\n            bool isDeleted() const;\n            /// Returns true if the object was deleted by a content file.\n            bool isDeletedByContentFile() const;\n\n            MWScript::Locals& getLocals();\n\n            bool isEnabled() const;\n\n            void enable();\n\n            void disable();\n\n            void setPosition (const ESM::Position& pos);\n            const ESM::Position& getPosition() const;\n\n            void setCustomData(std::unique_ptr<CustomData>&& value) noexcept;\n            ///< Set custom data (potentially replacing old custom data). The ownership of \\a data is\n            /// transferred to this.\n\n            CustomData *getCustomData();\n            ///< May return a 0-pointer. The ownership of the return data object is not transferred.\n\n            const CustomData *getCustomData() const;\n\n            bool activate();\n\n            bool onActivate();\n\n            bool activateByScript();\n\n            bool hasChanged() const;\n            ///< Has this RefData changed since it was originally loaded?\n\n            const ESM::AnimationState& getAnimationState() const;\n            ESM::AnimationState& getAnimationState();\n\n            /*\n                Start of tes3mp addition\n\n                Track the last state communicated to the server for this reference,\n                to avoid packet spam when the server denies our state change request or\n                is slow to reply\n            */\n            enum StateCommunication\n            {\n                None = 0,\n                Enabled = 1,\n                Disabled = 2,\n                Deleted = 3\n            };\n\n        private:\n\n            short mLastCommunicatedState = StateCommunication::None;\n\n        public:\n\n            short getLastCommunicatedState() { return mLastCommunicatedState; };\n\n            void setLastCommunicatedState(short communicationState) { mLastCommunicatedState = communicationState; };\n            /*\n                End of tes3mp addition\n            */\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/scene.cpp",
    "content": "#include \"scene.hpp\"\n\n#include <limits>\n#include <chrono>\n#include <thread>\n\n#include <BulletCollision/CollisionDispatch/btCollisionObject.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n\n#include <components/debug/debuglog.hpp>\n#include <components/loadinglistener/loadinglistener.hpp>\n#include <components/misc/resourcehelpers.hpp>\n#include <components/settings/settings.hpp>\n#include <components/resource/resourcesystem.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/bulletshape.hpp>\n#include <components/sceneutil/unrefqueue.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/detournavigator/navigator.hpp>\n#include <components/detournavigator/debug.hpp>\n#include <components/misc/convert.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/world.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n\n#include \"../mwrender/renderingmanager.hpp\"\n#include \"../mwrender/landmanager.hpp\"\n\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwphysics/actor.hpp\"\n#include \"../mwphysics/object.hpp\"\n#include \"../mwphysics/heightfield.hpp\"\n\n#include \"player.hpp\"\n#include \"localscripts.hpp\"\n#include \"esmstore.hpp\"\n#include \"class.hpp\"\n#include \"cellvisitors.hpp\"\n#include \"cellstore.hpp\"\n#include \"cellpreloader.hpp\"\n\nnamespace\n{\n    using MWWorld::RotationOrder;\n\n    osg::Quat makeActorOsgQuat(const ESM::Position& position)\n    {\n        return osg::Quat(position.rot[2], osg::Vec3(0, 0, -1));\n    }\n\n    osg::Quat makeInversedOrderObjectOsgQuat(const ESM::Position& position)\n    {\n        const float xr = position.rot[0];\n        const float yr = position.rot[1];\n        const float zr = position.rot[2];\n\n        return osg::Quat(xr, osg::Vec3(-1, 0, 0))\n                * osg::Quat(yr, osg::Vec3(0, -1, 0))\n                * osg::Quat(zr, osg::Vec3(0, 0, -1));\n    }\n\n    osg::Quat makeObjectOsgQuat(const ESM::Position& position)\n    {\n        const float xr = position.rot[0];\n        const float yr = position.rot[1];\n        const float zr = position.rot[2];\n\n        return osg::Quat(zr, osg::Vec3(0, 0, -1))\n            * osg::Quat(yr, osg::Vec3(0, -1, 0))\n            * osg::Quat(xr, osg::Vec3(-1, 0, 0));\n    }\n\n    void setNodeRotation(const MWWorld::Ptr& ptr, MWRender::RenderingManager& rendering, RotationOrder order)\n    {\n        if (!ptr.getRefData().getBaseNode())\n            return;\n\n        rendering.rotateObject(ptr,\n            ptr.getClass().isActor()\n            ? makeActorOsgQuat(ptr.getRefData().getPosition())\n            : (order == RotationOrder::inverse\n                ? makeInversedOrderObjectOsgQuat(ptr.getRefData().getPosition())\n                : makeObjectOsgQuat(ptr.getRefData().getPosition()))\n        );\n    }\n\n    std::string getModel(const MWWorld::Ptr &ptr, const VFS::Manager *vfs)\n    {\n        bool useAnim = ptr.getClass().useAnim();\n        std::string model = ptr.getClass().getModel(ptr);\n        if (useAnim)\n            model = Misc::ResourceHelpers::correctActorModelPath(model, vfs);\n\n        const std::string &id = ptr.getCellRef().getRefId();\n        if (id == \"prisonmarker\" || id == \"divinemarker\" || id == \"templemarker\" || id == \"northmarker\")\n            model = \"\"; // marker objects that have a hardcoded function in the game logic, should be hidden from the player\n        return model;\n    }\n\n    void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,\n                   MWRender::RenderingManager& rendering, std::set<ESM::RefNum>& pagedRefs)\n    {\n        if (ptr.getRefData().getBaseNode() || physics.getActor(ptr))\n        {\n            Log(Debug::Warning) << \"Warning: Tried to add \" << ptr.getCellRef().getRefId() << \" to the scene twice\";\n            return;\n        }\n\n        bool useAnim = ptr.getClass().useAnim();\n        std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS());\n\n        const ESM::RefNum& refnum = ptr.getCellRef().getRefNum();\n        if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end())\n            ptr.getClass().insertObjectRendering(ptr, model, rendering);\n        else\n            ptr.getRefData().setBaseNode(new SceneUtil::PositionAttitudeTransform); // FIXME remove this when physics code is fixed not to depend on basenode\n        setNodeRotation(ptr, rendering, RotationOrder::direct);\n\n        ptr.getClass().insertObject (ptr, model, physics);\n\n        if (useAnim)\n            MWBase::Environment::get().getMechanicsManager()->add(ptr);\n\n        if (ptr.getClass().isActor())\n            rendering.addWaterRippleEmitter(ptr);\n\n        // Restore effect particles\n        MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr);\n    }\n\n    void addObject(const MWWorld::Ptr& ptr, const MWPhysics::PhysicsSystem& physics, DetourNavigator::Navigator& navigator)\n    {\n        if (const auto object = physics.getObject(ptr))\n        {\n            if (ptr.getClass().isDoor() && !ptr.getCellRef().getTeleport())\n            {\n                btVector3 aabbMin;\n                btVector3 aabbMax;\n                object->getShapeInstance()->getCollisionShape()->getAabb(btTransform::getIdentity(), aabbMin, aabbMax);\n\n                const auto center = (aabbMax + aabbMin) * 0.5f;\n\n                const auto distanceFromDoor = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 0.5f;\n                const auto toPoint = aabbMax.x() - aabbMin.x() < aabbMax.y() - aabbMin.y()\n                        ? btVector3(distanceFromDoor, 0, 0)\n                        : btVector3(0, distanceFromDoor, 0);\n\n                const auto transform = object->getTransform();\n                const btTransform closedDoorTransform(\n                    Misc::Convert::toBullet(makeObjectOsgQuat(ptr.getCellRef().getPosition())),\n                    transform.getOrigin()\n                );\n\n                const auto start = Misc::Convert::makeOsgVec3f(closedDoorTransform(center + toPoint));\n                const auto startPoint = physics.castRay(start, start - osg::Vec3f(0, 0, 1000), ptr, {},\n                    MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Water);\n                const auto connectionStart = startPoint.mHit ? startPoint.mHitPos : start;\n\n                const auto end = Misc::Convert::makeOsgVec3f(closedDoorTransform(center - toPoint));\n                const auto endPoint = physics.castRay(end, end - osg::Vec3f(0, 0, 1000), ptr, {},\n                    MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Water);\n                const auto connectionEnd = endPoint.mHit ? endPoint.mHitPos : end;\n\n                navigator.addObject(\n                    DetourNavigator::ObjectId(object),\n                    DetourNavigator::DoorShapes(object->getShapeInstance(), connectionStart, connectionEnd),\n                    transform\n                );\n            }\n            else\n            {\n                navigator.addObject(\n                    DetourNavigator::ObjectId(object),\n                    DetourNavigator::ObjectShapes(object->getShapeInstance()),\n                    object->getTransform()\n                );\n            }\n        }\n        else if (physics.getActor(ptr))\n        {\n            navigator.addAgent(MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(ptr));\n        }\n    }\n\n    struct InsertVisitor\n    {\n        MWWorld::CellStore& mCell;\n        Loading::Listener& mLoadingListener;\n        bool mTest;\n\n        std::vector<MWWorld::Ptr> mToInsert;\n\n        InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool test);\n\n        bool operator() (const MWWorld::Ptr& ptr);\n\n        template <class AddObject>\n        void insert(AddObject&& addObject);\n    };\n\n    InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool test)\n    : mCell (cell), mLoadingListener (loadingListener), mTest(test)\n    {}\n\n    bool InsertVisitor::operator() (const MWWorld::Ptr& ptr)\n    {\n        // do not insert directly as we can't modify the cell from within the visitation\n        // CreatureLevList::insertObjectRendering may spawn a new creature\n        mToInsert.push_back(ptr);\n        return true;\n    }\n\n    template <class AddObject>\n    void InsertVisitor::insert(AddObject&& addObject)\n    {\n        for (MWWorld::Ptr& ptr : mToInsert)\n        {\n            if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled())\n            {\n                try\n                {\n                    addObject(ptr);\n                }\n                catch (const std::exception& e)\n                {\n                    std::string error (\"failed to render '\" + ptr.getCellRef().getRefId() + \"': \");\n                    Log(Debug::Error) << error + e.what();\n                }\n            }\n\n            if (!mTest)\n                mLoadingListener.increaseProgress (1);\n        }\n    }\n\n    struct PositionVisitor\n    {\n        bool operator() (const MWWorld::Ptr& ptr)\n        {\n            if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled())\n                ptr.getClass().adjustPosition (ptr, false);\n            return true;\n        }\n    };\n\n    int getCellPositionDistanceToOrigin(const std::pair<int, int>& cellPosition)\n    {\n        return std::abs(cellPosition.first) + std::abs(cellPosition.second);\n    }\n\n}\n\n\nnamespace MWWorld\n{\n\n    void Scene::removeFromPagedRefs(const Ptr &ptr)\n    {\n        const ESM::RefNum& refnum = ptr.getCellRef().getRefNum();\n        if (refnum.hasContentFile() && mPagedRefs.erase(refnum))\n        {\n            if (!ptr.getRefData().getBaseNode()) return;\n            ptr.getClass().insertObjectRendering(ptr, getModel(ptr, mRendering.getResourceSystem()->getVFS()), mRendering);\n            setNodeRotation(ptr, mRendering, RotationOrder::direct);\n            reloadTerrain();\n        }\n    }\n\n    void Scene::updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics)\n    {\n        mRendering.moveObject(ptr, pos);\n        if (movePhysics)\n        {\n            mPhysics->updatePosition(ptr);\n        }\n    }\n\n    void Scene::updateObjectRotation(const Ptr &ptr, RotationOrder order)\n    {\n        setNodeRotation(ptr, mRendering, order);\n        mPhysics->updateRotation(ptr);\n    }\n\n    void Scene::updateObjectScale(const Ptr &ptr)\n    {\n        float scale = ptr.getCellRef().getScale();\n        osg::Vec3f scaleVec (scale, scale, scale);\n        ptr.getClass().adjustScale(ptr, scaleVec, true);\n        mRendering.scaleObject(ptr, scaleVec);\n        mPhysics->updateScale(ptr);\n    }\n\n    void Scene::update (float duration, bool paused)\n    {\n        mPreloader->updateCache(mRendering.getReferenceTime());\n        preloadCells(duration);\n\n        mRendering.update (duration, paused);\n    }\n\n    void Scene::unloadCell (CellStoreCollection::iterator iter, bool test)\n    {\n        if (!test)\n            Log(Debug::Info) << \"Unloading cell \" << (*iter)->getCell()->getDescription();\n\n        const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();\n        ListAndResetObjectsVisitor visitor;\n\n        /*\n            Start of tes3mp addition\n\n            Set a const pointer to the iterator's ESM::Cell here, because\n            (*iter)->getCell() can become invalid later down\n        */\n        const ESM::Cell* cell = (*iter)->getCell();\n        /*\n            End of tes3mp addition\n        */\n\n        (*iter)->forEach(visitor);\n        const auto world = MWBase::Environment::get().getWorld();\n        for (const auto& ptr : visitor.mObjects)\n        {\n            if (const auto object = mPhysics->getObject(ptr))\n                navigator->removeObject(DetourNavigator::ObjectId(object));\n            else if (mPhysics->getActor(ptr))\n            {\n                navigator->removeAgent(world->getPathfindingHalfExtents(ptr));\n                mRendering.removeActorPath(ptr);\n            }\n            mPhysics->remove(ptr);\n        }\n\n        const auto cellX = (*iter)->getCell()->getGridX();\n        const auto cellY = (*iter)->getCell()->getGridY();\n\n        if ((*iter)->getCell()->isExterior())\n        {\n            if (const auto heightField = mPhysics->getHeightField(cellX, cellY))\n                navigator->removeObject(DetourNavigator::ObjectId(heightField));\n            mPhysics->removeHeightField(cellX, cellY);\n        }\n\n        if ((*iter)->getCell()->hasWater())\n            navigator->removeWater(osg::Vec2i(cellX, cellY));\n\n        if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*(*iter)->getCell()))\n            navigator->removePathgrid(*pathgrid);\n\n        const auto player = world->getPlayerPtr();\n        navigator->update(player.getRefData().getPosition().asVec3());\n\n        MWBase::Environment::get().getMechanicsManager()->drop (*iter);\n\n        mRendering.removeCell(*iter);\n        MWBase::Environment::get().getWindowManager()->removeCell(*iter);\n\n        MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter);\n\n        MWBase::Environment::get().getSoundManager()->stopSound (*iter);\n        mActiveCells.erase(*iter);\n\n        /*\n            Start of tes3mp addition\n\n            Store a cell unload for the LocalPlayer\n        */\n        mwmp::Main::get().getLocalPlayer()->storeCellState(*cell, mwmp::CellState::UNLOAD);\n        /*\n            End of tes3mp addition\n        */\n    }\n\n    void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test)\n    {\n        std::pair<CellStoreCollection::iterator, bool> result = mActiveCells.insert(cell);\n\n        if(result.second)\n        {\n            if (test)\n                Log(Debug::Info) << \"Testing cell \" << cell->getCell()->getDescription();\n            else\n                Log(Debug::Info) << \"Loading cell \" << cell->getCell()->getDescription();\n\n            float verts = ESM::Land::LAND_SIZE;\n            float worldsize = ESM::Land::REAL_SIZE;\n\n            const auto world = MWBase::Environment::get().getWorld();\n            const auto navigator = world->getNavigator();\n\n            const int cellX = cell->getCell()->getGridX();\n            const int cellY = cell->getCell()->getGridY();\n\n            // Load terrain physics first...\n            if (!test && cell->getCell()->isExterior())\n            {\n                osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY);\n                const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;\n                if (data)\n                {\n                    mPhysics->addHeightField (data->mHeights, cellX, cellY, worldsize / (verts-1), verts, data->mMinHeight, data->mMaxHeight, land.get());\n                }\n                else\n                {\n                    static std::vector<float> defaultHeight;\n                    defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT);\n                    mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts, ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get());\n                }\n\n                if (const auto heightField = mPhysics->getHeightField(cellX, cellY))\n                    navigator->addObject(DetourNavigator::ObjectId(heightField), heightField, *heightField->getShape(),\n                            heightField->getCollisionObject()->getWorldTransform());\n            }\n\n            if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell->getCell()))\n                navigator->addPathgrid(*cell->getCell(), *pathgrid);\n\n            // register local scripts\n            // do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice\n            MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);\n\n            if (respawn)\n                cell->respawn();\n\n            // ... then references. This is important for adjustPosition to work correctly.\n            insertCell (*cell, loadingListener, test);\n\n            mRendering.addCell(cell);\n            if (!test)\n            {\n                MWBase::Environment::get().getWindowManager()->addCell(cell);\n                bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior();\n                float waterLevel = cell->getWaterLevel();\n                mRendering.setWaterEnabled(waterEnabled);\n                if (waterEnabled)\n                {\n                    mPhysics->enableWater(waterLevel);\n                    mRendering.setWaterHeight(waterLevel);\n\n                    if (cell->getCell()->isExterior())\n                    {\n                        if (const auto heightField = mPhysics->getHeightField(cellX, cellY))\n                            navigator->addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE,\n                                cell->getWaterLevel(), heightField->getCollisionObject()->getWorldTransform());\n                    }\n                    else\n                    {\n                        navigator->addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),\n                            cell->getWaterLevel(), btTransform::getIdentity());\n                    }\n                }\n                else\n                    mPhysics->disableWater();\n\n                const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n\n                navigator->update(player.getRefData().getPosition().asVec3());\n\n                if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))\n                {\n                    mRendering.configureAmbient(cell->getCell());\n                }\n\n                /*\n                    Start of tes3mp addition\n\n                    Store a cell load for the LocalPlayer\n                */\n                mwmp::Main::get().getLocalPlayer()->storeCellState(*cell->getCell(), mwmp::CellState::LOAD);\n                /*\n                    End of tes3mp addition\n                */\n            }\n        }\n\n        mPreloader->notifyLoaded(cell);\n    }\n\n    void Scene::clear()\n    {\n        CellStoreCollection::iterator active = mActiveCells.begin();\n        while (active!=mActiveCells.end())\n            unloadCell (active++);\n        assert(mActiveCells.empty());\n        mCurrentCell = nullptr;\n\n        mPreloader->clear();\n    }\n\n    osg::Vec4i Scene::gridCenterToBounds(const osg::Vec2i& centerCell) const\n    {\n        return osg::Vec4i(centerCell.x()-mHalfGridSize,centerCell.y()-mHalfGridSize,centerCell.x()+mHalfGridSize+1,centerCell.y()+mHalfGridSize+1);\n    }\n\n    osg::Vec2i Scene::getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i* currentGridCenter) const\n    {\n        if (currentGridCenter)\n        {\n            float centerX, centerY;\n            MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true);\n            float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y()));\n            const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold\n            if (distance <= maxDistance)\n                return *currentGridCenter;\n        }\n        osg::Vec2i newCenter;\n        MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newCenter.x(), newCenter.y());\n        return newCenter;\n    }\n\n    void Scene::playerMoved(const osg::Vec3f &pos)\n    {\n        const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();\n        const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n        navigator->updatePlayerPosition(player.getRefData().getPosition().asVec3());\n\n        if (!mCurrentCell || !mCurrentCell->isExterior())\n            return;\n\n        osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter);\n        if (newCell != mCurrentGridCenter)\n            changeCellGrid(pos, newCell.x(), newCell.y());\n    }\n\n    void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent)\n    {\n        CellStoreCollection::iterator active = mActiveCells.begin();\n        while (active!=mActiveCells.end())\n        {\n            if ((*active)->getCell()->isExterior())\n            {\n                if (std::abs (playerCellX-(*active)->getCell()->getGridX())<=mHalfGridSize &&\n                    std::abs (playerCellY-(*active)->getCell()->getGridY())<=mHalfGridSize)\n                {\n                    // keep cells within the new grid\n                    ++active;\n                    continue;\n                }\n            }\n            unloadCell (active++);\n        }\n\n        mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);\n        osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);\n        mRendering.setActiveGrid(newGrid);\n\n        preloadTerrain(pos, true);\n        mPagedRefs.clear();\n        mRendering.getPagedRefnums(newGrid, mPagedRefs);\n\n        std::size_t refsToLoad = 0;\n        std::vector<std::pair<int, int>> cellsPositionsToLoad;\n        // get the number of refs to load\n        for (int x = playerCellX - mHalfGridSize; x <= playerCellX + mHalfGridSize; ++x)\n        {\n            for (int y = playerCellY - mHalfGridSize; y <= playerCellY + mHalfGridSize; ++y)\n            {\n                CellStoreCollection::iterator iter = mActiveCells.begin();\n\n                while (iter!=mActiveCells.end())\n                {\n                    assert ((*iter)->getCell()->isExterior());\n\n                    if (x==(*iter)->getCell()->getGridX() &&\n                        y==(*iter)->getCell()->getGridY())\n                        break;\n\n                    ++iter;\n                }\n\n                if (iter==mActiveCells.end())\n                {\n                    refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count();\n                    cellsPositionsToLoad.emplace_back(x, y);\n                }\n            }\n        }\n\n        Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();\n        Loading::ScopedLoad load(loadingListener);\n        std::string loadingExteriorText = \"#{sLoadingMessage3}\";\n        loadingListener->setLabel(loadingExteriorText);\n        loadingListener->setProgressRange(refsToLoad);\n\n        const auto getDistanceToPlayerCell = [&] (const std::pair<int, int>& cellPosition)\n        {\n            return std::abs(cellPosition.first - playerCellX) + std::abs(cellPosition.second - playerCellY);\n        };\n\n        const auto getCellPositionPriority = [&] (const std::pair<int, int>& cellPosition)\n        {\n            return std::make_pair(getDistanceToPlayerCell(cellPosition), getCellPositionDistanceToOrigin(cellPosition));\n        };\n\n        std::sort(cellsPositionsToLoad.begin(), cellsPositionsToLoad.end(),\n            [&] (const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) {\n                return getCellPositionPriority(lhs) < getCellPositionPriority(rhs);\n            });\n\n        // Load cells\n        for (const auto& cellPosition : cellsPositionsToLoad)\n        {\n            const auto x = cellPosition.first;\n            const auto y = cellPosition.second;\n\n            CellStoreCollection::iterator iter = mActiveCells.begin();\n\n            while (iter != mActiveCells.end())\n            {\n                assert ((*iter)->getCell()->isExterior());\n\n                if (x == (*iter)->getCell()->getGridX() &&\n                    y == (*iter)->getCell()->getGridY())\n                    break;\n\n                ++iter;\n            }\n\n            if (iter == mActiveCells.end())\n            {\n                CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y);\n\n                loadCell (cell, loadingListener, changeEvent);\n            }\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_PLAYER_CELL_STATE packet with all cell states stored in LocalPlayer\n            and then clear them, but only if the player is logged in on the server\n        */\n        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n        {\n            mwmp::Main::get().getLocalPlayer()->sendCellStates();\n            mwmp::Main::get().getLocalPlayer()->clearCellStates();\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        CellStore* current = MWBase::Environment::get().getWorld()->getExterior(playerCellX, playerCellY);\n        MWBase::Environment::get().getWindowManager()->changeCell(current);\n\n        if (changeEvent)\n            mCellChanged = true;\n\n        mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent);\n    }\n\n    void Scene::testExteriorCells()\n    {\n        // Note: temporary disable ICO to decrease memory usage\n        mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(nullptr);\n\n        mRendering.getResourceSystem()->setExpiryDelay(1.f);\n\n        const MWWorld::Store<ESM::Cell> &cells = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>();\n\n        Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();\n        Loading::ScopedLoad load(loadingListener);\n        loadingListener->setProgressRange(cells.getExtSize());\n\n        MWWorld::Store<ESM::Cell>::iterator it = cells.extBegin();\n        int i = 1;\n        for (; it != cells.extEnd(); ++it)\n        {\n            loadingListener->setLabel(\"Testing exterior cells (\"+std::to_string(i)+\"/\"+std::to_string(cells.getExtSize())+\")...\");\n\n            CellStoreCollection::iterator iter = mActiveCells.begin();\n\n            CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY);\n            loadCell (cell, loadingListener, false, true);\n\n            iter = mActiveCells.begin();\n            while (iter != mActiveCells.end())\n            {\n                if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() &&\n                    it->mData.mY == (*iter)->getCell()->getGridY())\n                {\n                    unloadCell(iter, true);\n                    break;\n                }\n\n                ++iter;\n            }\n\n            mRendering.getResourceSystem()->updateCache(mRendering.getReferenceTime());\n            mRendering.getUnrefQueue()->flush(mRendering.getWorkQueue());\n\n            loadingListener->increaseProgress (1);\n            i++;\n        }\n\n        mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(mRendering.getIncrementalCompileOperation());\n        mRendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat(\"cache expiry delay\", \"Cells\"));\n    }\n\n    void Scene::testInteriorCells()\n    {\n        // Note: temporary disable ICO to decrease memory usage\n        mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(nullptr);\n\n        mRendering.getResourceSystem()->setExpiryDelay(1.f);\n\n        const MWWorld::Store<ESM::Cell> &cells = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>();\n\n        Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();\n        Loading::ScopedLoad load(loadingListener);\n        loadingListener->setProgressRange(cells.getIntSize());\n\n        int i = 1;\n        MWWorld::Store<ESM::Cell>::iterator it = cells.intBegin();\n        for (; it != cells.intEnd(); ++it)\n        {\n            loadingListener->setLabel(\"Testing interior cells (\"+std::to_string(i)+\"/\"+std::to_string(cells.getIntSize())+\")...\");\n\n            CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName);\n            loadCell (cell, loadingListener, false, true);\n\n            CellStoreCollection::iterator iter = mActiveCells.begin();\n            while (iter != mActiveCells.end())\n            {\n                assert (!(*iter)->getCell()->isExterior());\n\n                if (it->mName == (*iter)->getCell()->mName)\n                {\n                    unloadCell(iter, true);\n                    break;\n                }\n\n                ++iter;\n            }\n\n            mRendering.getResourceSystem()->updateCache(mRendering.getReferenceTime());\n            mRendering.getUnrefQueue()->flush(mRendering.getWorkQueue());\n\n            loadingListener->increaseProgress (1);\n            i++;\n        }\n\n        mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(mRendering.getIncrementalCompileOperation());\n        mRendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat(\"cache expiry delay\", \"Cells\"));\n    }\n\n    void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos)\n    {\n        mCurrentCell = cell;\n\n        mRendering.enableTerrain(cell->isExterior());\n\n        MWBase::World *world = MWBase::Environment::get().getWorld();\n        MWWorld::Ptr old = world->getPlayerPtr();\n        world->getPlayer().setCell(cell);\n\n        MWWorld::Ptr player = world->getPlayerPtr();\n        mRendering.updatePlayerPtr(player);\n\n        if (adjustPlayerPos) {\n            world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]);\n\n            float x = pos.rot[0];\n            float y = pos.rot[1];\n            float z = pos.rot[2];\n            world->rotateObject(player, x, y, z);\n\n            player.getClass().adjustPosition(player, true);\n        }\n\n        MWBase::Environment::get().getMechanicsManager()->updateCell(old, player);\n        MWBase::Environment::get().getWindowManager()->watchActor(player);\n\n        mPhysics->updatePtr(old, player);\n\n        world->adjustSky();\n\n        mLastPlayerPos = player.getRefData().getPosition().asVec3();\n    }\n\n    Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics,\n                  DetourNavigator::Navigator& navigator)\n    : mCurrentCell (nullptr), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator)\n    , mCellLoadingThreshold(1024.f)\n    , mPreloadDistance(Settings::Manager::getInt(\"preload distance\", \"Cells\"))\n    , mPreloadEnabled(Settings::Manager::getBool(\"preload enabled\", \"Cells\"))\n    , mPreloadExteriorGrid(Settings::Manager::getBool(\"preload exterior grid\", \"Cells\"))\n    , mPreloadDoors(Settings::Manager::getBool(\"preload doors\", \"Cells\"))\n    , mPreloadFastTravel(Settings::Manager::getBool(\"preload fast travel\", \"Cells\"))\n    , mPredictionTime(Settings::Manager::getFloat(\"prediction time\", \"Cells\"))\n    {\n        mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain(), rendering.getLandManager()));\n        mPreloader->setWorkQueue(mRendering.getWorkQueue());\n\n        mPreloader->setUnrefQueue(rendering.getUnrefQueue());\n        mPhysics->setUnrefQueue(rendering.getUnrefQueue());\n\n        rendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat(\"cache expiry delay\", \"Cells\"));\n\n        mPreloader->setExpiryDelay(Settings::Manager::getFloat(\"preload cell expiry delay\", \"Cells\"));\n        mPreloader->setMinCacheSize(Settings::Manager::getInt(\"preload cell cache min\", \"Cells\"));\n        mPreloader->setMaxCacheSize(Settings::Manager::getInt(\"preload cell cache max\", \"Cells\"));\n        mPreloader->setPreloadInstances(Settings::Manager::getBool(\"preload instances\", \"Cells\"));\n    }\n\n    Scene::~Scene()\n    {\n    }\n\n    bool Scene::hasCellChanged() const\n    {\n        return mCellChanged;\n    }\n\n    const Scene::CellStoreCollection& Scene::getActiveCells() const\n    {\n        return mActiveCells;\n    }\n\n    void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)\n    {\n        CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName);\n        bool useFading = (mCurrentCell != nullptr);\n        if (useFading)\n            MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5);\n\n        Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();\n        std::string loadingInteriorText = \"#{sLoadingMessage2}\";\n        loadingListener->setLabel(loadingInteriorText);\n        Loading::ScopedLoad load(loadingListener);\n\n        if(mCurrentCell != nullptr && *mCurrentCell == *cell)\n        {\n            MWBase::World *world = MWBase::Environment::get().getWorld();\n            world->moveObject(world->getPlayerPtr(), position.pos[0], position.pos[1], position.pos[2]);\n\n            float x = position.rot[0];\n            float y = position.rot[1];\n            float z = position.rot[2];\n            world->rotateObject(world->getPlayerPtr(), x, y, z);\n\n            if (adjustPlayerPos)\n                world->getPlayerPtr().getClass().adjustPosition(world->getPlayerPtr(), true);\n            MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);\n            return;\n        }\n\n        Log(Debug::Info) << \"Changing to interior\";\n\n        // unload\n        CellStoreCollection::iterator active = mActiveCells.begin();\n        while (active!=mActiveCells.end())\n            unloadCell (active++);\n\n        loadingListener->setProgressRange(cell->count());\n\n        // Load cell.\n        mPagedRefs.clear();\n        loadCell (cell, loadingListener, changeEvent);\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_PLAYER_CELL_STATE packet with all cell states stored in LocalPlayer\n            and then clear them, but only if the player is logged in on the server\n        */\n        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n        {\n            mwmp::Main::get().getLocalPlayer()->sendCellStates();\n            mwmp::Main::get().getLocalPlayer()->clearCellStates();\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        changePlayerCell(cell, position, adjustPlayerPos);\n\n        // adjust fog\n        mRendering.configureFog(mCurrentCell->getCell());\n\n        // Sky system\n        MWBase::Environment::get().getWorld()->adjustSky();\n\n        if (changeEvent)\n            mCellChanged = true;\n\n        if (useFading)\n            MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);\n\n        MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);\n\n        mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent);\n    }\n\n    void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)\n    {\n        int x = 0;\n        int y = 0;\n\n        MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y);\n\n        if (changeEvent)\n            MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5);\n\n        changeCellGrid(position.asVec3(), x, y, changeEvent);\n\n        CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y);\n        changePlayerCell(current, position, adjustPlayerPos);\n\n        if (changeEvent)\n            MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);\n    }\n\n    CellStore* Scene::getCurrentCell ()\n    {\n        return mCurrentCell;\n    }\n\n    void Scene::markCellAsUnchanged()\n    {\n        mCellChanged = false;\n    }\n\n    void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test)\n    {\n        InsertVisitor insertVisitor (cell, *loadingListener, test);\n        cell.forEach (insertVisitor);\n        insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs); });\n        insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); });\n\n        // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order\n        PositionVisitor posVisitor;\n        cell.forEach (posVisitor);\n    }\n\n    void Scene::addObjectToScene (const Ptr& ptr)\n    {\n        try\n        {\n            addObject(ptr, *mPhysics, mRendering, mPagedRefs);\n            addObject(ptr, *mPhysics, mNavigator);\n            MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale());\n            const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();\n            const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n            navigator->update(player.getRefData().getPosition().asVec3());\n        }\n        catch (std::exception& e)\n        {\n            Log(Debug::Error) << \"failed to render '\" << ptr.getCellRef().getRefId() << \"': \" << e.what();\n        }\n    }\n\n    void Scene::removeObjectFromScene (const Ptr& ptr)\n    {\n        MWBase::Environment::get().getMechanicsManager()->remove (ptr);\n        MWBase::Environment::get().getSoundManager()->stopSound3D (ptr);\n        const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();\n        if (const auto object = mPhysics->getObject(ptr))\n        {\n            navigator->removeObject(DetourNavigator::ObjectId(object));\n            const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n            navigator->update(player.getRefData().getPosition().asVec3());\n        }\n        else if (mPhysics->getActor(ptr))\n        {\n            navigator->removeAgent(MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(ptr));\n        }\n        mPhysics->remove(ptr);\n        mRendering.removeObject (ptr);\n        if (ptr.getClass().isActor())\n            mRendering.removeWaterRippleEmitter(ptr);\n        ptr.getRefData().setBaseNode(nullptr);\n    }\n\n    bool Scene::isCellActive(const CellStore &cell)\n    {\n        CellStoreCollection::iterator active = mActiveCells.begin();\n        while (active != mActiveCells.end()) {\n            if (**active == cell) {\n                return true;\n            }\n            ++active;\n        }\n        return false;\n    }\n\n    Ptr Scene::searchPtrViaActorId (int actorId)\n    {\n        for (CellStoreCollection::const_iterator iter (mActiveCells.begin());\n            iter!=mActiveCells.end(); ++iter)\n            if (Ptr ptr = (*iter)->searchViaActorId (actorId))\n                return ptr;\n\n        return Ptr();\n    }\n\n    class PreloadMeshItem : public SceneUtil::WorkItem\n    {\n    public:\n        PreloadMeshItem(const std::string& mesh, Resource::SceneManager* sceneManager)\n            : mMesh(mesh), mSceneManager(sceneManager)\n        {\n        }\n\n        void doWork() override\n        {\n            try\n            {\n                mSceneManager->getTemplate(mMesh);\n            }\n            catch (std::exception&)\n            {\n            }\n        }\n    private:\n        std::string mMesh;\n        Resource::SceneManager* mSceneManager;\n    };\n\n    void Scene::preload(const std::string &mesh, bool useAnim)\n    {\n        std::string mesh_ = mesh;\n        if (useAnim)\n            mesh_ = Misc::ResourceHelpers::correctActorModelPath(mesh_, mRendering.getResourceSystem()->getVFS());\n\n        if (!mRendering.getResourceSystem()->getSceneManager()->checkLoaded(mesh_, mRendering.getReferenceTime()))\n            mRendering.getWorkQueue()->addWorkItem(new PreloadMeshItem(mesh_, mRendering.getResourceSystem()->getSceneManager()));\n    }\n\n    void Scene::preloadCells(float dt)\n    {\n        if (dt<=1e-06) return;\n        std::vector<PositionCellGrid> exteriorPositions;\n\n        const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n        osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();\n        osg::Vec3f moved = playerPos - mLastPlayerPos;\n        osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime;\n\n        if (mCurrentCell->isExterior())\n            exteriorPositions.emplace_back(predictedPos, gridCenterToBounds(getNewGridCenter(predictedPos, &mCurrentGridCenter)));\n\n        mLastPlayerPos = playerPos;\n\n        if (mPreloadEnabled)\n        {\n            if (mPreloadDoors)\n                preloadTeleportDoorDestinations(playerPos, predictedPos, exteriorPositions);\n            if (mPreloadExteriorGrid)\n                preloadExteriorGrid(playerPos, predictedPos);\n            if (mPreloadFastTravel)\n                preloadFastTravelDestinations(playerPos, predictedPos, exteriorPositions);\n        }\n\n        mPreloader->setTerrainPreloadPositions(exteriorPositions);\n    }\n\n    void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<PositionCellGrid>& exteriorPositions)\n    {\n        std::vector<MWWorld::ConstPtr> teleportDoors;\n        for (const MWWorld::CellStore* cellStore : mActiveCells)\n        {\n            typedef MWWorld::CellRefList<ESM::Door>::List DoorList;\n            const DoorList &doors = cellStore->getReadOnlyDoors().mList;\n            for (auto& door : doors)\n            {\n                if (!door.mRef.getTeleport())\n                {\n                    continue;\n                }\n                teleportDoors.emplace_back(&door, cellStore);\n            }\n        }\n\n        for (const MWWorld::ConstPtr& door : teleportDoors)\n        {\n            float sqrDistToPlayer = (playerPos - door.getRefData().getPosition().asVec3()).length2();\n            sqrDistToPlayer = std::min(sqrDistToPlayer, (predictedPos - door.getRefData().getPosition().asVec3()).length2());\n\n            if (sqrDistToPlayer < mPreloadDistance*mPreloadDistance)\n            {\n                try\n                {\n                    if (!door.getCellRef().getDestCell().empty())\n                        preloadCell(MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell()));\n                    else\n                    {\n                        osg::Vec3f pos = door.getCellRef().getDoorDest().asVec3();\n                        int x,y;\n                        MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y);\n                        preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true);\n                        exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));\n                    }\n                }\n                catch (std::exception&)\n                {\n                    // ignore error for now, would spam the log too much\n                }\n            }\n        }\n    }\n\n    void Scene::preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos)\n    {\n        if (!MWBase::Environment::get().getWorld()->isCellExterior())\n            return;\n\n        int halfGridSizePlusOne = mHalfGridSize + 1;\n\n\n        int cellX,cellY;\n        cellX = mCurrentGridCenter.x(); cellY = mCurrentGridCenter.y();\n\n        float centerX, centerY;\n        MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true);\n\n        for (int dx = -halfGridSizePlusOne; dx <= halfGridSizePlusOne; ++dx)\n        {\n            for (int dy = -halfGridSizePlusOne; dy <= halfGridSizePlusOne; ++dy)\n            {\n                if (dy != halfGridSizePlusOne && dy != -halfGridSizePlusOne && dx != halfGridSizePlusOne && dx != -halfGridSizePlusOne)\n                    continue; // only care about the outer (not yet loaded) part of the grid\n\n                float thisCellCenterX, thisCellCenterY;\n                MWBase::Environment::get().getWorld()->indexToPosition(cellX+dx, cellY+dy, thisCellCenterX, thisCellCenterY, true);\n\n                float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y()));\n                dist = std::min(dist,std::max(std::abs(thisCellCenterX - predictedPos.x()), std::abs(thisCellCenterY - predictedPos.y())));\n                float loadDist = Constants::CellSizeInUnits / 2 + Constants::CellSizeInUnits - mCellLoadingThreshold + mPreloadDistance;\n\n                if (dist < loadDist)\n                    preloadCell(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy));\n            }\n        }\n    }\n\n    void Scene::preloadCell(CellStore *cell, bool preloadSurrounding)\n    {\n        if (preloadSurrounding && cell->isExterior())\n        {\n            int x = cell->getCell()->getGridX();\n            int y = cell->getCell()->getGridY();\n            unsigned int numpreloaded = 0;\n            for (int dx = -mHalfGridSize; dx <= mHalfGridSize; ++dx)\n            {\n                for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy)\n                {\n                    mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(x+dx, y+dy), mRendering.getReferenceTime());\n                    if (++numpreloaded >= mPreloader->getMaxCacheSize())\n                        break;\n                }\n            }\n        }\n        else\n            mPreloader->preload(cell, mRendering.getReferenceTime());\n    }\n\n    void Scene::preloadTerrain(const osg::Vec3f &pos, bool sync)\n    {\n        std::vector<PositionCellGrid> vec;\n        vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));\n        if (sync && mRendering.pagingUnlockCache())\n            mPreloader->abortTerrainPreloadExcept(nullptr);\n        else\n            mPreloader->abortTerrainPreloadExcept(&vec[0]);\n        mPreloader->setTerrainPreloadPositions(vec);\n        if (!sync) return;\n\n        Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();\n        Loading::ScopedLoad load(loadingListener);\n        int progress = 0, initialProgress = -1, progressRange = 0;\n        while (!mPreloader->syncTerrainLoad(vec, progress, progressRange, mRendering.getReferenceTime()))\n        {\n            if (initialProgress == -1)\n            {\n                loadingListener->setLabel(\"#{sLoadingMessage4}\");\n                initialProgress = progress;\n            }\n            if (progress)\n            {\n                loadingListener->setProgressRange(std::max(0, progressRange-initialProgress));\n                loadingListener->setProgress(progress-initialProgress);\n            }\n            else\n                loadingListener->setProgress(0);\n            std::this_thread::sleep_for(std::chrono::milliseconds(5));\n        }\n    }\n\n    void Scene::reloadTerrain()\n    {\n        mPreloader->setTerrainPreloadPositions(std::vector<PositionCellGrid>());\n    }\n\n    struct ListFastTravelDestinationsVisitor\n    {\n        ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos)\n            : mPreloadDist(preloadDist)\n            , mPlayerPos(playerPos)\n        {\n        }\n\n        bool operator()(const MWWorld::Ptr& ptr)\n        {\n            if ((ptr.getRefData().getPosition().asVec3() - mPlayerPos).length2() > mPreloadDist * mPreloadDist)\n                return true;\n\n            if (ptr.getClass().isNpc())\n            {\n                const std::vector<ESM::Transport::Dest>& transport = ptr.get<ESM::NPC>()->mBase->mTransport.mList;\n                mList.insert(mList.begin(), transport.begin(), transport.end());\n            }\n            else\n            {\n                const std::vector<ESM::Transport::Dest>& transport = ptr.get<ESM::Creature>()->mBase->mTransport.mList;\n                mList.insert(mList.begin(), transport.begin(), transport.end());\n            }\n            return true;\n        }\n        float mPreloadDist;\n        osg::Vec3f mPlayerPos;\n        std::vector<ESM::Transport::Dest> mList;\n    };\n\n    void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector<PositionCellGrid>& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time\n    {\n        const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr();\n        ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3());\n\n        for (MWWorld::CellStore* cellStore : mActiveCells)\n        {\n            cellStore->forEachType<ESM::NPC>(listVisitor);\n            cellStore->forEachType<ESM::Creature>(listVisitor);\n        }\n\n        for (ESM::Transport::Dest& dest : listVisitor.mList)\n        {\n            if (!dest.mCellName.empty())\n                preloadCell(MWBase::Environment::get().getWorld()->getInterior(dest.mCellName));\n            else\n            {\n                osg::Vec3f pos = dest.mPos.asVec3();\n                int x,y;\n                MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y);\n                preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true);\n                exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/scene.hpp",
    "content": "#ifndef GAME_MWWORLD_SCENE_H\n#define GAME_MWWORLD_SCENE_H\n\n#include <osg/Vec4i>\n#include <osg/Vec2i>\n\n#include \"ptr.hpp\"\n#include \"globals.hpp\"\n\n#include <set>\n#include <memory>\n#include <unordered_map>\n\n#include <components/misc/constants.hpp>\n\nnamespace osg\n{\n    class Vec3f;\n}\n\nnamespace ESM\n{\n    struct Position;\n}\n\nnamespace Files\n{\n    class Collections;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace DetourNavigator\n{\n    struct Navigator;\n}\n\nnamespace MWRender\n{\n    class SkyManager;\n    class RenderingManager;\n}\n\nnamespace MWPhysics\n{\n    class PhysicsSystem;\n}\n\nnamespace MWWorld\n{\n    class Player;\n    class CellStore;\n    class CellPreloader;\n\n    enum class RotationOrder\n    {\n        direct,\n        inverse\n    };\n\n    class Scene\n    {\n        public:\n\n            typedef std::set<CellStore *> CellStoreCollection;\n\n        private:\n\n            CellStore* mCurrentCell; // the cell the player is in\n            CellStoreCollection mActiveCells;\n            bool mCellChanged;\n            MWPhysics::PhysicsSystem *mPhysics;\n            MWRender::RenderingManager& mRendering;\n            DetourNavigator::Navigator& mNavigator;\n            std::unique_ptr<CellPreloader> mPreloader;\n            float mCellLoadingThreshold;\n            float mPreloadDistance;\n            bool mPreloadEnabled;\n\n            bool mPreloadExteriorGrid;\n            bool mPreloadDoors;\n            bool mPreloadFastTravel;\n            float mPredictionTime;\n\n            static const int mHalfGridSize = Constants::CellGridRadius;\n\n            osg::Vec3f mLastPlayerPos;\n\n            std::set<ESM::RefNum> mPagedRefs;\n\n            void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false);\n            osg::Vec2i mCurrentGridCenter;\n\n            // Load and unload cells as necessary to create a cell grid with \"X\" and \"Y\" in the center\n            void changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent = true);\n\n            typedef std::pair<osg::Vec3f, osg::Vec4i> PositionCellGrid;\n\n            void preloadCells(float dt);\n            void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<PositionCellGrid>& exteriorPositions);\n            void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos);\n            void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<PositionCellGrid>& exteriorPositions);\n\n            osg::Vec4i gridCenterToBounds(const osg::Vec2i &centerCell) const;\n            osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const;\n\n        public:\n\n            Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics,\n                   DetourNavigator::Navigator& navigator);\n\n            ~Scene();\n\n            void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false);\n            void preloadTerrain(const osg::Vec3f& pos, bool sync=false);\n            void reloadTerrain();\n\n            void unloadCell (CellStoreCollection::iterator iter, bool test = false);\n\n            void loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test = false);\n\n            void playerMoved (const osg::Vec3f& pos);\n\n            void changePlayerCell (CellStore* newCell, const ESM::Position& position, bool adjustPlayerPos);\n\n            CellStore *getCurrentCell();\n\n            const CellStoreCollection& getActiveCells () const;\n\n            bool hasCellChanged() const;\n            ///< Has the set of active cells changed, since the last frame?\n\n            void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true);\n            ///< Move to interior cell.\n            /// @param changeEvent Set cellChanged flag?\n\n            void changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true);\n            ///< Move to exterior cell.\n            /// @param changeEvent Set cellChanged flag?\n\n            void clear();\n            ///< Change into a void\n\n            void markCellAsUnchanged();\n\n            void update (float duration, bool paused);\n\n            void addObjectToScene (const Ptr& ptr);\n            ///< Add an object that already exists in the world model to the scene.\n\n            void removeObjectFromScene (const Ptr& ptr);\n            ///< Remove an object from the scene, but not from the world model.\n\n            void removeFromPagedRefs(const Ptr &ptr);\n\n            void updateObjectRotation(const Ptr& ptr, RotationOrder order);\n            void updateObjectScale(const Ptr& ptr);\n            void updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics);\n\n            bool isCellActive(const CellStore &cell);\n\n            Ptr searchPtrViaActorId (int actorId);\n\n            void preload(const std::string& mesh, bool useAnim=false);\n\n            void testExteriorCells();\n            void testInteriorCells();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/store.cpp",
    "content": "#include \"store.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n\n#include <components/loadinglistener/loadinglistener.hpp>\n#include <components/misc/rng.hpp>\n\n#include <iterator>\n#include <stdexcept>\n\nnamespace\n{\n    struct Compare\n    {\n        bool operator()(const ESM::Land *x, const ESM::Land *y) {\n            if (x->mX == y->mX) {\n                return x->mY < y->mY;\n            }\n            return x->mX < y->mX;\n        }\n        bool operator()(const ESM::Land *x, const std::pair<int, int>& y) {\n            if (x->mX == y.first) {\n                return x->mY < y.second;\n            }\n            return x->mX < y.first;\n        }\n    };\n}\n\nnamespace MWWorld\n{\n    RecordId::RecordId(const std::string &id, bool isDeleted)\n        : mId(id), mIsDeleted(isDeleted)\n    {}\n\n    template<typename T> \n    IndexedStore<T>::IndexedStore()\n    {\n    }\n    template<typename T>        \n    typename IndexedStore<T>::iterator IndexedStore<T>::begin() const\n    {\n        return mStatic.begin();\n    }\n    template<typename T>    \n    typename IndexedStore<T>::iterator IndexedStore<T>::end() const\n    {\n        return mStatic.end();\n    }\n    template<typename T>\n    void IndexedStore<T>::load(ESM::ESMReader &esm)\n    {\n        T record;\n        bool isDeleted = false;\n\n        record.load(esm, isDeleted);\n\n        mStatic.insert_or_assign(record.mIndex, record);\n    }\n    template<typename T>\n    int IndexedStore<T>::getSize() const\n    {\n        return mStatic.size();\n    }\n    template<typename T>\n    void IndexedStore<T>::setUp()\n    {\n    }\n    template<typename T>\n    const T *IndexedStore<T>::search(int index) const\n    {\n        typename Static::const_iterator it = mStatic.find(index);\n        if (it != mStatic.end())\n            return &(it->second);\n        return nullptr;\n    }\n    template<typename T>\n    const T *IndexedStore<T>::find(int index) const\n    {\n        const T *ptr = search(index);\n        if (ptr == nullptr)\n        {\n            const std::string msg = T::getRecordType() + \" with index \" + std::to_string(index) + \" not found\";\n            throw std::runtime_error(msg);\n        }\n        return ptr;\n    }\n\n    // Need to instantiate these before they're used\n    template class IndexedStore<ESM::MagicEffect>;\n    template class IndexedStore<ESM::Skill>;\n\n    template<typename T>\n    Store<T>::Store()\n    {\n    }\n\n    template<typename T>\n    Store<T>::Store(const Store<T>& orig)\n        : mStatic(orig.mStatic)\n    {\n    }\n\n    template<typename T>\n    void Store<T>::clearDynamic()\n    {\n        // remove the dynamic part of mShared\n        assert(mShared.size() >= mStatic.size());\n        mShared.erase(mShared.begin() + mStatic.size(), mShared.end());\n        mDynamic.clear();\n    }\n\n    template<typename T>\n    const T *Store<T>::search(const std::string &id) const\n    {\n        std::string idLower = Misc::StringUtils::lowerCase(id);\n\n        typename Dynamic::const_iterator dit = mDynamic.find(idLower);\n        if (dit != mDynamic.end())\n            return &dit->second;\n\n        typename std::map<std::string, T>::const_iterator it = mStatic.find(idLower);\n        if (it != mStatic.end())\n            return &(it->second);\n\n        return nullptr;\n    }\n    template<typename T>\n    const T *Store<T>::searchStatic(const std::string &id) const\n    {\n        std::string idLower = Misc::StringUtils::lowerCase(id);\n        typename std::map<std::string, T>::const_iterator it = mStatic.find(idLower);\n        if (it != mStatic.end())\n            return &(it->second);\n\n        return nullptr;\n    }\n\n    template<typename T>\n    bool Store<T>::isDynamic(const std::string &id) const\n    {\n        typename Dynamic::const_iterator dit = mDynamic.find(id);\n        return (dit != mDynamic.end());\n    }\n    template<typename T>\n    const T *Store<T>::searchRandom(const std::string &id) const\n    {\n        std::vector<const T*> results;\n        std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results),\n                [&id](const T* item)\n                {\n                    return Misc::StringUtils::ciCompareLen(id, item->mId, id.size()) == 0;\n                });\n        if(!results.empty())\n            return results[Misc::Rng::rollDice(results.size())];\n        return nullptr;\n    }\n    template<typename T>\n    const T *Store<T>::find(const std::string &id) const\n    {\n        const T *ptr = search(id);\n        if (ptr == nullptr)\n        {\n            const std::string msg = T::getRecordType() + \" '\" + id + \"' not found\";\n            throw std::runtime_error(msg);\n        }\n        return ptr;\n    }\n    template<typename T>\n    RecordId Store<T>::load(ESM::ESMReader &esm)\n    {\n        T record;\n        bool isDeleted = false;\n\n        record.load(esm, isDeleted);\n        Misc::StringUtils::lowerCaseInPlace(record.mId);\n\n        std::pair<typename Static::iterator, bool> inserted = mStatic.insert_or_assign(record.mId, record);\n        if (inserted.second)\n            mShared.push_back(&inserted.first->second);\n\n        return RecordId(record.mId, isDeleted);\n    }\n    template<typename T>\n    void Store<T>::setUp()\n    {\n    }\n\n    template<typename T>\n    typename Store<T>::iterator Store<T>::begin() const\n    {\n        return mShared.begin(); \n    }\n    template<typename T>\n    typename Store<T>::iterator Store<T>::end() const\n    {\n        return mShared.end();\n    }\n\n    template<typename T>\n    size_t Store<T>::getSize() const\n    {\n        return mShared.size();\n    }\n\n    template<typename T>\n    int Store<T>::getDynamicSize() const\n    {\n        return mDynamic.size();\n    }\n    template<typename T>\n    void Store<T>::listIdentifier(std::vector<std::string> &list) const\n    {\n        list.reserve(list.size() + getSize());\n        typename std::vector<T *>::const_iterator it = mShared.begin();\n        for (; it != mShared.end(); ++it) {\n            list.push_back((*it)->mId);\n        }\n    }\n    template<typename T>\n    T *Store<T>::insert(const T &item, bool overrideOnly)\n    {\n        std::string id = Misc::StringUtils::lowerCase(item.mId);\n        if(overrideOnly)\n        {\n            auto it = mStatic.find(id);\n            if(it == mStatic.end())\n                return nullptr;\n        }\n        std::pair<typename Dynamic::iterator, bool> result = mDynamic.insert_or_assign(id, item);\n        T *ptr = &result.first->second;\n        if (result.second)\n            mShared.push_back(ptr);\n        return ptr;\n    }\n    template<typename T>\n    T *Store<T>::insertStatic(const T &item)\n    {\n        std::string id = Misc::StringUtils::lowerCase(item.mId);\n        std::pair<typename Static::iterator, bool> result = mStatic.insert_or_assign(id, item);\n        T *ptr = &result.first->second;\n        if (result.second)\n            mShared.push_back(ptr);\n        return ptr;\n    }\n    template<typename T>\n    bool Store<T>::eraseStatic(const std::string &id)\n    {\n        std::string idLower = Misc::StringUtils::lowerCase(id);\n\n        typename std::map<std::string, T>::iterator it = mStatic.find(idLower);\n\n        if (it != mStatic.end()) {\n            // delete from the static part of mShared\n            typename std::vector<T *>::iterator sharedIter = mShared.begin();\n            typename std::vector<T *>::iterator end = sharedIter + mStatic.size();\n\n            while (sharedIter != mShared.end() && sharedIter != end) {\n                if((*sharedIter)->mId == idLower) {\n                    mShared.erase(sharedIter);\n                    break;\n                }\n                ++sharedIter;\n            }\n            mStatic.erase(it);\n        }\n\n        return true;\n    }\n\n    template<typename T>\n    bool Store<T>::erase(const std::string &id)\n    {\n        std::string key = Misc::StringUtils::lowerCase(id);\n        typename Dynamic::iterator it = mDynamic.find(key);\n        if (it == mDynamic.end()) {\n            return false;\n        }\n        mDynamic.erase(it);\n\n        // have to reinit the whole shared part\n        assert(mShared.size() >= mStatic.size());\n        mShared.erase(mShared.begin() + mStatic.size(), mShared.end());\n        for (it = mDynamic.begin(); it != mDynamic.end(); ++it) {\n            mShared.push_back(&it->second);\n        }\n        return true;\n    }\n    template<typename T>\n    bool Store<T>::erase(const T &item)\n    {\n        return erase(item.mId);\n    }\n    template<typename T>\n    void Store<T>::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n    {\n        for (typename Dynamic::const_iterator iter (mDynamic.begin()); iter!=mDynamic.end();\n             ++iter)\n        {\n            writer.startRecord (T::sRecordId);\n            iter->second.save (writer);\n            writer.endRecord (T::sRecordId);\n        }\n    }\n    template<typename T>\n    RecordId Store<T>::read(ESM::ESMReader& reader, bool overrideOnly)\n    {\n        T record;\n        bool isDeleted = false;\n\n        record.load (reader, isDeleted);\n        insert (record, overrideOnly);\n\n        return RecordId(record.mId, isDeleted);\n    }\n\n    // LandTexture\n    //=========================================================================\n    Store<ESM::LandTexture>::Store()\n    {\n        mStatic.emplace_back();\n        LandTextureList &ltexl = mStatic[0];\n        // More than enough to hold Morrowind.esm. Extra lists for plugins will we\n        //  added on-the-fly in a different method.\n        ltexl.reserve(128);\n    }\n    const ESM::LandTexture *Store<ESM::LandTexture>::search(size_t index, size_t plugin) const\n    {\n        assert(plugin < mStatic.size());\n        const LandTextureList &ltexl = mStatic[plugin];\n\n        if (index >= ltexl.size())\n            return nullptr;\n        return &ltexl[index];\n    }\n    const ESM::LandTexture *Store<ESM::LandTexture>::find(size_t index, size_t plugin) const\n    {\n        const ESM::LandTexture *ptr = search(index, plugin);\n        if (ptr == nullptr)\n        {\n            const std::string msg = \"Land texture with index \" + std::to_string(index) + \" not found\";\n            throw std::runtime_error(msg);\n        }\n        return ptr;\n    }\n    size_t Store<ESM::LandTexture>::getSize() const\n    {\n        return mStatic.size();\n    }\n    size_t Store<ESM::LandTexture>::getSize(size_t plugin) const\n    {\n        assert(plugin < mStatic.size());\n        return mStatic[plugin].size();\n    }\n    RecordId Store<ESM::LandTexture>::load(ESM::ESMReader &esm, size_t plugin)\n    {\n        ESM::LandTexture lt;\n        bool isDeleted = false;\n\n        lt.load(esm, isDeleted);\n\n        assert(plugin < mStatic.size());\n\n        // Replace texture for records with given ID and index from all plugins.\n        for (unsigned int i=0; i<mStatic.size(); i++)\n        {\n            ESM::LandTexture* tex = const_cast<ESM::LandTexture*>(search(lt.mIndex, i));\n            if (tex)\n            {\n                const std::string texId = Misc::StringUtils::lowerCase(tex->mId);\n                const std::string ltId = Misc::StringUtils::lowerCase(lt.mId);\n                if (texId == ltId)\n                {\n                    tex->mTexture = lt.mTexture;\n                }\n            }\n        }\n\n        LandTextureList &ltexl = mStatic[plugin];\n        if(lt.mIndex + 1 > (int)ltexl.size())\n            ltexl.resize(lt.mIndex+1);\n\n        // Store it\n        ltexl[lt.mIndex] = lt;\n\n        return RecordId(lt.mId, isDeleted);\n    }\n    RecordId Store<ESM::LandTexture>::load(ESM::ESMReader &esm)\n    {\n        return load(esm, esm.getIndex());\n    }\n    Store<ESM::LandTexture>::iterator Store<ESM::LandTexture>::begin(size_t plugin) const\n    {\n        assert(plugin < mStatic.size());\n        return mStatic[plugin].begin();\n    }\n    Store<ESM::LandTexture>::iterator Store<ESM::LandTexture>::end(size_t plugin) const\n    {\n        assert(plugin < mStatic.size());\n        return mStatic[plugin].end();\n    }\n    void Store<ESM::LandTexture>::resize(size_t num)\n    {\n        if (mStatic.size() < num)\n            mStatic.resize(num);\n    }\n    \n    // Land\n    //=========================================================================\n    Store<ESM::Land>::~Store()\n    {\n        for (const ESM::Land* staticLand : mStatic)\n        {\n            delete staticLand;\n        }\n\n    }\n    size_t Store<ESM::Land>::getSize() const\n    {\n        return mStatic.size();\n    }\n    Store<ESM::Land>::iterator Store<ESM::Land>::begin() const\n    {\n        return iterator(mStatic.begin());\n    }\n    Store<ESM::Land>::iterator Store<ESM::Land>::end() const\n    {\n        return iterator(mStatic.end());\n    }\n    const ESM::Land *Store<ESM::Land>::search(int x, int y) const\n    {\n        std::pair<int, int> comp(x,y);\n\n        std::vector<ESM::Land *>::const_iterator it =\n            std::lower_bound(mStatic.begin(), mStatic.end(), comp, Compare());\n\n        if (it != mStatic.end() && (*it)->mX == x && (*it)->mY == y) {\n            return *it;\n        }\n        return nullptr;\n    }\n    const ESM::Land *Store<ESM::Land>::find(int x, int y) const\n    {\n        const ESM::Land *ptr = search(x, y);\n        if (ptr == nullptr)\n        {\n            const std::string msg = \"Land at (\" + std::to_string(x) + \", \" + std::to_string(y) + \") not found\";\n            throw std::runtime_error(msg);\n        }\n        return ptr;\n    }\n    RecordId Store<ESM::Land>::load(ESM::ESMReader &esm)\n    {\n        ESM::Land *ptr = new ESM::Land();\n        bool isDeleted = false;\n\n        ptr->load(esm, isDeleted);\n\n        // Same area defined in multiple plugins? -> last plugin wins\n        // Can't use search() because we aren't sorted yet - is there any other way to speed this up?\n        for (std::vector<ESM::Land*>::iterator it = mStatic.begin(); it != mStatic.end(); ++it)\n        {\n            if ((*it)->mX == ptr->mX && (*it)->mY == ptr->mY)\n            {\n                delete *it;\n                mStatic.erase(it);\n                break;\n            }\n        }\n\n        mStatic.push_back(ptr);\n\n        return RecordId(\"\", isDeleted);\n    }\n    void Store<ESM::Land>::setUp()\n    {\n        // The land is static for given game session, there is no need to refresh it every load.\n        if (mBuilt)\n            return;\n\n        std::sort(mStatic.begin(), mStatic.end(), Compare());\n        mBuilt = true;\n    }\n\n\n    // Cell\n    //=========================================================================\n\n    const ESM::Cell *Store<ESM::Cell>::search(const ESM::Cell &cell) const\n    {\n        if (cell.isExterior()) {\n            return search(cell.getGridX(), cell.getGridY());\n        }\n        return search(cell.mName);\n    }\n    void Store<ESM::Cell>::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell)\n    {\n        //Handling MovedCellRefs, there is no way to do it inside loadcell\n        while (esm.isNextSub(\"MVRF\")) {\n            ESM::CellRef ref;\n            ESM::MovedCellRef cMRef;\n            cell->getNextMVRF(esm, cMRef);\n\n            ESM::Cell *cellAlt = const_cast<ESM::Cell*>(searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1]));\n\n            // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following\n            //  implementation when the oher implementation works as well.\n            bool deleted = false;\n            cell->getNextRef(esm, ref, deleted);\n\n            // Add data required to make reference appear in the correct cell.\n            // We should not need to test for duplicates, as this part of the code is pre-cell merge.\n            cell->mMovedRefs.push_back(cMRef);\n\n            // But there may be duplicates here!\n            ESM::CellRefTracker::iterator iter = std::find_if(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ESM::CellRefTrackerPredicate(ref.mRefNum));\n            if (iter == cellAlt->mLeasedRefs.end())\n                cellAlt->mLeasedRefs.emplace_back(std::move(ref), deleted);\n            else\n                *iter = std::make_pair(std::move(ref), deleted);\n        }\n    }\n    const ESM::Cell *Store<ESM::Cell>::search(const std::string &id) const\n    {\n        ESM::Cell cell;\n        cell.mName = Misc::StringUtils::lowerCase(id);\n\n        std::map<std::string, ESM::Cell>::const_iterator it = mInt.find(cell.mName);\n\n        if (it != mInt.end()) {\n            return &(it->second);\n        }\n\n        DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName);\n        if (dit != mDynamicInt.end()) {\n            return &dit->second;\n        }\n\n        return nullptr;\n    }\n    const ESM::Cell *Store<ESM::Cell>::search(int x, int y) const\n    {\n        ESM::Cell cell;\n        cell.mData.mX = x, cell.mData.mY = y;\n\n        std::pair<int, int> key(x, y);\n        DynamicExt::const_iterator it = mExt.find(key);\n        if (it != mExt.end()) {\n            return &(it->second);\n        }\n\n        DynamicExt::const_iterator dit = mDynamicExt.find(key);\n        if (dit != mDynamicExt.end()) {\n            return &dit->second;\n        }\n\n        return nullptr;\n    }\n    const ESM::Cell *Store<ESM::Cell>::searchStatic(int x, int y) const\n    {\n        ESM::Cell cell;\n        cell.mData.mX = x, cell.mData.mY = y;\n\n        std::pair<int, int> key(x, y);\n        DynamicExt::const_iterator it = mExt.find(key);\n        if (it != mExt.end()) {\n            return &(it->second);\n        }\n        return nullptr;\n    }\n    const ESM::Cell *Store<ESM::Cell>::searchOrCreate(int x, int y)\n    {\n        std::pair<int, int> key(x, y);\n        DynamicExt::const_iterator it = mExt.find(key);\n        if (it != mExt.end()) {\n            return &(it->second);\n        }\n\n        DynamicExt::const_iterator dit = mDynamicExt.find(key);\n        if (dit != mDynamicExt.end()) {\n            return &dit->second;\n        }\n\n        ESM::Cell newCell;\n        newCell.mData.mX = x;\n        newCell.mData.mY = y;\n        newCell.mData.mFlags = ESM::Cell::HasWater;\n        newCell.mAmbi.mAmbient = 0;\n        newCell.mAmbi.mSunlight = 0;\n        newCell.mAmbi.mFog = 0;\n        newCell.mAmbi.mFogDensity = 0;\n        return &mExt.insert(std::make_pair(key, newCell)).first->second;\n    }\n    const ESM::Cell *Store<ESM::Cell>::find(const std::string &id) const\n    {\n        const ESM::Cell *ptr = search(id);\n        if (ptr == nullptr)\n        {\n            const std::string msg = \"Cell '\" + id + \"' not found\";\n            throw std::runtime_error(msg);\n        }\n        return ptr;\n    }\n    const ESM::Cell *Store<ESM::Cell>::find(int x, int y) const\n    {\n        const ESM::Cell *ptr = search(x, y);\n        if (ptr == nullptr)\n        {\n            const std::string msg = \"Exterior at (\" + std::to_string(x) + \", \" + std::to_string(y) + \") not found\";\n            throw std::runtime_error(msg);\n        }\n        return ptr;\n    }\n    void Store<ESM::Cell>::clearDynamic()\n    {\n        setUp();\n    }\n\n    void Store<ESM::Cell>::setUp()\n    {\n        mSharedInt.clear();\n        mSharedInt.reserve(mInt.size());\n        for (auto & [_, cell] : mInt)\n            mSharedInt.push_back(&cell);\n\n        mSharedExt.clear();\n        mSharedExt.reserve(mExt.size());\n        for (auto & [_, cell] : mExt)\n            mSharedExt.push_back(&cell);\n    }\n    RecordId Store<ESM::Cell>::load(ESM::ESMReader &esm)\n    {\n        // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell,\n        //  and we merge all this data into one Cell object. However, we can't simply search for the cell id,\n        //  as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they\n        //  are not available until both cells have been loaded at least partially!\n\n        // All cells have a name record, even nameless exterior cells.\n        ESM::Cell cell;\n        bool isDeleted = false;\n\n        // Load the (x,y) coordinates of the cell, if it is an exterior cell, \n        // so we can find the cell we need to merge with\n        cell.loadNameAndData(esm, isDeleted);\n        std::string idLower = Misc::StringUtils::lowerCase(cell.mName);\n\n        if(cell.mData.mFlags & ESM::Cell::Interior)\n        {\n            // Store interior cell by name, try to merge with existing parent data.\n            ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower));\n            if (oldcell) {\n                // merge new cell into old cell\n                // push the new references on the list of references to manage (saveContext = true)\n                oldcell->mData = cell.mData;\n                oldcell->mName = cell.mName; // merge name just to be sure (ID will be the same, but case could have been changed)\n                oldcell->loadCell(esm, true);\n            } else\n            {\n                // spawn a new cell\n                cell.loadCell(esm, true);\n\n                mInt[idLower] = cell;\n            }\n        }\n        else\n        {\n            // Store exterior cells by grid position, try to merge with existing parent data.\n            ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(cell.getGridX(), cell.getGridY()));\n            if (oldcell) {\n                // merge new cell into old cell\n                oldcell->mData = cell.mData;\n                oldcell->mName = cell.mName;\n                oldcell->loadCell(esm, false);\n\n                // handle moved ref (MVRF) subrecords\n                handleMovedCellRefs (esm, &cell);\n\n                // push the new references on the list of references to manage\n                oldcell->postLoad(esm);\n\n                // merge lists of leased references, use newer data in case of conflict\n                for (ESM::MovedCellRefTracker::const_iterator it = cell.mMovedRefs.begin(); it != cell.mMovedRefs.end(); ++it) {\n                    // remove reference from current leased ref tracker and add it to new cell\n                    ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum);\n                    if (itold != oldcell->mMovedRefs.end())\n                    {\n                        if (it->mTarget[0] != itold->mTarget[0] || it->mTarget[1] != itold->mTarget[1])\n                        {\n                            ESM::Cell *wipecell = const_cast<ESM::Cell*>(search(itold->mTarget[0], itold->mTarget[1]));\n                            ESM::CellRefTracker::iterator it_lease = std::find_if(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), ESM::CellRefTrackerPredicate(it->mRefNum));\n                            if (it_lease != wipecell->mLeasedRefs.end())\n                                wipecell->mLeasedRefs.erase(it_lease);\n                            else\n                                Log(Debug::Error) << \"Error: can't find \" << it->mRefNum.mIndex << \" \" << it->mRefNum.mContentFile  << \" in leasedRefs\";\n                        }\n                        *itold = *it;\n                    }\n                    else\n                        oldcell->mMovedRefs.push_back(*it);\n                }\n\n                // We don't need to merge mLeasedRefs of cell / oldcell. This list is filled when another cell moves a\n                // reference to this cell, so the list for the new cell should be empty. The list for oldcell,\n                // however, could have leased refs in it and so should be kept.\n            } else\n            {\n                // spawn a new cell\n                cell.loadCell(esm, false);\n\n                // handle moved ref (MVRF) subrecords\n                handleMovedCellRefs (esm, &cell);\n\n                // push the new references on the list of references to manage\n                cell.postLoad(esm);\n\n                mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell;\n            }\n        }\n\n        return RecordId(cell.mName, isDeleted);\n    }\n    Store<ESM::Cell>::iterator Store<ESM::Cell>::intBegin() const\n    {\n        return iterator(mSharedInt.begin());\n    }\n    Store<ESM::Cell>::iterator Store<ESM::Cell>::intEnd() const\n    {\n        return iterator(mSharedInt.end());\n    }\n    Store<ESM::Cell>::iterator Store<ESM::Cell>::extBegin() const\n    {\n        return iterator(mSharedExt.begin());\n    }\n    Store<ESM::Cell>::iterator Store<ESM::Cell>::extEnd() const\n    {\n        return iterator(mSharedExt.end());\n    }\n    const ESM::Cell *Store<ESM::Cell>::searchExtByName(const std::string &id) const\n    {\n        const ESM::Cell *cell = nullptr;\n        for (const ESM::Cell *sharedCell : mSharedExt)\n        {\n            if (Misc::StringUtils::ciEqual(sharedCell->mName, id))\n            {\n                if (cell == nullptr ||\n                    (sharedCell->mData.mX > cell->mData.mX) ||\n                    (sharedCell->mData.mX == cell->mData.mX && sharedCell->mData.mY > cell->mData.mY))\n                {\n                    cell = sharedCell;\n                }\n            }\n        }\n        return cell;\n    }\n    const ESM::Cell *Store<ESM::Cell>::searchExtByRegion(const std::string &id) const\n    {\n        const ESM::Cell *cell = nullptr;\n        for (const ESM::Cell *sharedCell : mSharedExt)\n        {\n            if (Misc::StringUtils::ciEqual(sharedCell->mRegion, id))\n            {\n                if (cell == nullptr ||\n                    (sharedCell->mData.mX > cell->mData.mX) ||\n                    (sharedCell->mData.mX == cell->mData.mX && sharedCell->mData.mY > cell->mData.mY))\n                {\n                    cell = sharedCell;\n                }\n            }\n        }\n        return cell;\n    }\n    size_t Store<ESM::Cell>::getSize() const\n    {\n        return mSharedInt.size() + mSharedExt.size();\n    }\n    size_t Store<ESM::Cell>::getExtSize() const\n    {\n        return mSharedExt.size();\n    }\n    size_t Store<ESM::Cell>::getIntSize() const\n    {\n        return mSharedInt.size();\n    }\n    void Store<ESM::Cell>::listIdentifier(std::vector<std::string> &list) const\n    {\n        list.reserve(list.size() + mSharedInt.size());\n\n        for (const ESM::Cell *sharedCell : mSharedInt)\n        {\n            list.push_back(sharedCell->mName);\n        }\n    }\n    /*\n        Start of tes3mp addition\n\n        Make it possible to override a Cell record similarly to how\n        other types of records can be overridden\n    */\n    ESM::Cell *Store<ESM::Cell>::override(const ESM::Cell &cell)\n    {\n        if (search(cell) != 0)\n        {\n            for (auto it = mSharedInt.begin(); it != mSharedInt.end(); ++it)\n            {\n                if (Misc::StringUtils::ciEqual((*it)->mName, cell.mName))\n                {\n                    (*it) = &const_cast<ESM::Cell&>(cell);\n                    break;\n                }\n            }\n\n            for (auto it = mInt.begin(); it != mInt.end(); ++it)\n            {\n                if (Misc::StringUtils::ciEqual((*it).second.mName, cell.mName))\n                {\n                    (*it).second = cell;\n                    return &(*it).second;\n                }\n            }\n        }\n        else\n        {\n            return insert(cell);\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n    ESM::Cell *Store<ESM::Cell>::insert(const ESM::Cell &cell)\n    {\n        if (search(cell) != nullptr)\n        {\n            const std::string cellType = (cell.isExterior()) ? \"exterior\" : \"interior\";\n            throw std::runtime_error(\"Failed to create \" + cellType + \" cell\");\n        }\n        ESM::Cell *ptr;\n        if (cell.isExterior()) {\n            std::pair<int, int> key(cell.getGridX(), cell.getGridY());\n\n            // duplicate insertions are avoided by search(ESM::Cell &)\n            std::pair<DynamicExt::iterator, bool> result =\n                mDynamicExt.insert(std::make_pair(key, cell));\n\n            ptr = &result.first->second;\n            mSharedExt.push_back(ptr);\n        } else {\n            std::string key = Misc::StringUtils::lowerCase(cell.mName);\n\n            // duplicate insertions are avoided by search(ESM::Cell &)\n            std::pair<DynamicInt::iterator, bool> result =\n                mDynamicInt.insert(std::make_pair(key, cell));\n\n            ptr = &result.first->second;\n            mSharedInt.push_back(ptr);\n        }\n        return ptr;\n    }\n    bool Store<ESM::Cell>::erase(const ESM::Cell &cell)\n    {\n        if (cell.isExterior()) {\n            return erase(cell.getGridX(), cell.getGridY());\n        }\n        return erase(cell.mName);\n    }\n    bool Store<ESM::Cell>::erase(const std::string &id)\n    {\n        std::string key = Misc::StringUtils::lowerCase(id);\n        DynamicInt::iterator it = mDynamicInt.find(key);\n\n        if (it == mDynamicInt.end()) {\n            return false;\n        }\n        mDynamicInt.erase(it);\n        mSharedInt.erase(\n            mSharedInt.begin() + mSharedInt.size(),\n            mSharedInt.end()\n        );\n\n        for (it = mDynamicInt.begin(); it != mDynamicInt.end(); ++it) {\n            mSharedInt.push_back(&it->second);\n        }\n\n        return true;\n    }\n    bool Store<ESM::Cell>::erase(int x, int y)\n    {\n        std::pair<int, int> key(x, y);\n        DynamicExt::iterator it = mDynamicExt.find(key);\n\n        if (it == mDynamicExt.end()) {\n            return false;\n        }\n        mDynamicExt.erase(it);\n        mSharedExt.erase(\n            mSharedExt.begin() + mSharedExt.size(),\n            mSharedExt.end()\n        );\n\n        for (it = mDynamicExt.begin(); it != mDynamicExt.end(); ++it) {\n            mSharedExt.push_back(&it->second);\n        }\n\n        return true;\n    }\n\n    \n    // Pathgrid\n    //=========================================================================\n\n    Store<ESM::Pathgrid>::Store()\n        : mCells(nullptr)\n    {\n    }\n\n    void Store<ESM::Pathgrid>::setCells(Store<ESM::Cell>& cells)\n    {\n        mCells = &cells;\n    }\n    RecordId Store<ESM::Pathgrid>::load(ESM::ESMReader &esm)\n    {\n        ESM::Pathgrid pathgrid;\n        bool isDeleted = false;\n\n        pathgrid.load(esm, isDeleted);\n\n        // Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell.\n        // For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name.\n        // mX and mY will be (0,0) for interior cells, but there is also an exterior cell with the coordinates of (0,0), so that doesn't help.\n        // Check whether mCell is an interior cell. This isn't perfect, will break if a Region with the same name as an interior cell is created.\n        // A proper fix should be made for future versions of the file format.\n        bool interior = mCells->search(pathgrid.mCell) != nullptr;\n\n        // Try to overwrite existing record\n        if (interior)\n        {\n            std::pair<Interior::iterator, bool> ret = mInt.insert(std::make_pair(pathgrid.mCell, pathgrid));\n            if (!ret.second)\n                ret.first->second = pathgrid;\n        }\n        else\n        {\n            std::pair<Exterior::iterator, bool> ret = mExt.insert(std::make_pair(std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY), pathgrid));\n            if (!ret.second)\n                ret.first->second = pathgrid;\n        }\n\n        return RecordId(\"\", isDeleted);\n    }\n    /*\n        Start of tes3mp addition\n\n        Make it possible to override a Pathgrid record similarly to how\n        other types of records can be overridden\n    */\n    ESM::Pathgrid* Store<ESM::Pathgrid>::override(const ESM::Pathgrid& pathgrid)\n    {\n        bool interior = mCells->search(pathgrid.mCell) != nullptr;\n\n        // Try to overwrite existing record\n        if (interior)\n        {\n            std::pair<Interior::iterator, bool> ret = mInt.insert(std::make_pair(pathgrid.mCell, pathgrid));\n            if (!ret.second)\n                ret.first->second = pathgrid;\n\n            return &ret.first->second;\n        }\n        else\n        {\n            std::pair<Exterior::iterator, bool> ret = mExt.insert(std::make_pair(std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY), pathgrid));\n            if (!ret.second)\n                ret.first->second = pathgrid;\n\n            return &ret.first->second;\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n    size_t Store<ESM::Pathgrid>::getSize() const\n    {\n        return mInt.size() + mExt.size();\n    }\n    void Store<ESM::Pathgrid>::setUp()\n    {\n    }\n    const ESM::Pathgrid *Store<ESM::Pathgrid>::search(int x, int y) const\n    {\n        Exterior::const_iterator it = mExt.find(std::make_pair(x,y));\n        if (it != mExt.end())\n            return &(it->second);\n        return nullptr;\n    }\n    const ESM::Pathgrid *Store<ESM::Pathgrid>::search(const std::string& name) const\n    {\n        Interior::const_iterator it = mInt.find(name);\n        if (it != mInt.end())\n            return &(it->second);\n        return nullptr;\n    }\n    const ESM::Pathgrid *Store<ESM::Pathgrid>::find(int x, int y) const\n    {\n        const ESM::Pathgrid* pathgrid = search(x,y);\n        if (!pathgrid)\n        {\n            const std::string msg = \"Pathgrid in cell '\" + std::to_string(x) + \" \" + std::to_string(y) + \"' not found\";\n            throw std::runtime_error(msg);\n        }\n        return pathgrid;\n    }\n    const ESM::Pathgrid* Store<ESM::Pathgrid>::find(const std::string& name) const\n    {\n        const ESM::Pathgrid* pathgrid = search(name);\n        if (!pathgrid)\n        {\n            const std::string msg = \"Pathgrid in cell '\" + name + \"' not found\";\n            throw std::runtime_error(msg);\n        }\n        return pathgrid;\n    }\n    const ESM::Pathgrid *Store<ESM::Pathgrid>::search(const ESM::Cell &cell) const\n    {\n        if (!(cell.mData.mFlags & ESM::Cell::Interior))\n            return search(cell.mData.mX, cell.mData.mY);\n        else\n            return search(cell.mName);\n    }\n    const ESM::Pathgrid *Store<ESM::Pathgrid>::find(const ESM::Cell &cell) const\n    {\n        if (!(cell.mData.mFlags & ESM::Cell::Interior))\n            return find(cell.mData.mX, cell.mData.mY);\n        else\n            return find(cell.mName);\n    }\n\n\n    // Skill\n    //=========================================================================\n    \n    Store<ESM::Skill>::Store()\n    {\n    }\n\n\n    // Magic effect\n    //=========================================================================\n\n    Store<ESM::MagicEffect>::Store()\n    {\n    }\n\n\n    // Attribute\n    //=========================================================================\n\n    Store<ESM::Attribute>::Store()\n    {\n        mStatic.reserve(ESM::Attribute::Length);\n    }\n    const ESM::Attribute *Store<ESM::Attribute>::search(size_t index) const\n    {\n        if (index >= mStatic.size()) {\n            return nullptr;\n        }\n        return &mStatic[index];\n    }\n\n    const ESM::Attribute *Store<ESM::Attribute>::find(size_t index) const\n    {\n        const ESM::Attribute *ptr = search(index);\n        if (ptr == nullptr)\n        {\n            const std::string msg = \"Attribute with index \" + std::to_string(index) + \" not found\";\n            throw std::runtime_error(msg);\n        }\n        return ptr;\n    }\n    void Store<ESM::Attribute>::setUp()\n    {\n        for (int i = 0; i < ESM::Attribute::Length; ++i) \n        {\n            ESM::Attribute newAttribute;\n            newAttribute.mId = ESM::Attribute::sAttributeIds[i];\n            newAttribute.mName = ESM::Attribute::sGmstAttributeIds[i];\n            newAttribute.mDescription = ESM::Attribute::sGmstAttributeDescIds[i];\n            mStatic.push_back(newAttribute);\n        }\n    }\n    size_t Store<ESM::Attribute>::getSize() const\n    {\n        return mStatic.size();\n    }\n    Store<ESM::Attribute>::iterator Store<ESM::Attribute>::begin() const\n    {\n        return mStatic.begin();\n    }\n    Store<ESM::Attribute>::iterator Store<ESM::Attribute>::end() const\n    {\n        return mStatic.end();\n    }\n\n    \n    // Dialogue\n    //=========================================================================\n\n\n    template<>\n    void Store<ESM::Dialogue>::setUp()\n    {\n        // DialInfos marked as deleted are kept during the loading phase, so that the linked list\n        // structure is kept intact for inserting further INFOs. Delete them now that loading is done.\n        for (auto & [_, dial] : mStatic)\n            dial.clearDeletedInfos();\n\n        mShared.clear();\n        mShared.reserve(mStatic.size());\n        for (auto & [_, dial] : mStatic)\n            mShared.push_back(&dial);\n    }\n\n    template <>\n    inline RecordId Store<ESM::Dialogue>::load(ESM::ESMReader &esm) {\n        // The original letter case of a dialogue ID is saved, because it's printed\n        ESM::Dialogue dialogue;\n        bool isDeleted = false;\n\n        dialogue.loadId(esm);\n\n        std::string idLower = Misc::StringUtils::lowerCase(dialogue.mId);\n        std::map<std::string, ESM::Dialogue>::iterator found = mStatic.find(idLower);\n        if (found == mStatic.end())\n        {\n            dialogue.loadData(esm, isDeleted);\n            mStatic.insert(std::make_pair(idLower, dialogue));\n        }\n        else\n        {\n            found->second.loadData(esm, isDeleted);\n            dialogue = found->second;\n        }\n\n        return RecordId(dialogue.mId, isDeleted);\n    }\n\n    template<>\n    bool Store<ESM::Dialogue>::eraseStatic(const std::string &id)\n    {\n        auto it = mStatic.find(Misc::StringUtils::lowerCase(id));\n\n        if (it != mStatic.end())\n            mStatic.erase(it);\n\n        return true;\n    }\n\n}\n\ntemplate class MWWorld::Store<ESM::Activator>;\ntemplate class MWWorld::Store<ESM::Apparatus>;\ntemplate class MWWorld::Store<ESM::Armor>;\n//template class MWWorld::Store<ESM::Attribute>;\ntemplate class MWWorld::Store<ESM::BirthSign>;\ntemplate class MWWorld::Store<ESM::BodyPart>;\ntemplate class MWWorld::Store<ESM::Book>;\n//template class MWWorld::Store<ESM::Cell>;\ntemplate class MWWorld::Store<ESM::Class>;\ntemplate class MWWorld::Store<ESM::Clothing>;\ntemplate class MWWorld::Store<ESM::Container>;\ntemplate class MWWorld::Store<ESM::Creature>;\ntemplate class MWWorld::Store<ESM::CreatureLevList>;\ntemplate class MWWorld::Store<ESM::Dialogue>;\ntemplate class MWWorld::Store<ESM::Door>;\ntemplate class MWWorld::Store<ESM::Enchantment>;\ntemplate class MWWorld::Store<ESM::Faction>;\ntemplate class MWWorld::Store<ESM::GameSetting>;\ntemplate class MWWorld::Store<ESM::Global>;\ntemplate class MWWorld::Store<ESM::Ingredient>;\ntemplate class MWWorld::Store<ESM::ItemLevList>;\n//template class MWWorld::Store<ESM::Land>;\n//template class MWWorld::Store<ESM::LandTexture>;\ntemplate class MWWorld::Store<ESM::Light>;\ntemplate class MWWorld::Store<ESM::Lockpick>;\n//template class MWWorld::Store<ESM::MagicEffect>;\ntemplate class MWWorld::Store<ESM::Miscellaneous>;\ntemplate class MWWorld::Store<ESM::NPC>;\n//template class MWWorld::Store<ESM::Pathgrid>;\ntemplate class MWWorld::Store<ESM::Potion>;\ntemplate class MWWorld::Store<ESM::Probe>;\ntemplate class MWWorld::Store<ESM::Race>;\ntemplate class MWWorld::Store<ESM::Region>;\ntemplate class MWWorld::Store<ESM::Repair>;\ntemplate class MWWorld::Store<ESM::Script>;\n//template class MWWorld::Store<ESM::Skill>;\ntemplate class MWWorld::Store<ESM::Sound>;\ntemplate class MWWorld::Store<ESM::SoundGenerator>;\ntemplate class MWWorld::Store<ESM::Spell>;\ntemplate class MWWorld::Store<ESM::StartScript>;\ntemplate class MWWorld::Store<ESM::Static>;\ntemplate class MWWorld::Store<ESM::Weapon>;\n\n"
  },
  {
    "path": "apps/openmw/mwworld/store.hpp",
    "content": "#ifndef OPENMW_MWWORLD_STORE_H\n#define OPENMW_MWWORLD_STORE_H\n\n#include <string>\n#include <vector>\n#include <map>\n\n#include \"recordcmp.hpp\"\n\nnamespace ESM\n{\n    struct Land;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace MWWorld\n{\n    struct RecordId\n    {\n        std::string mId;\n        bool mIsDeleted;\n\n        RecordId(const std::string &id = \"\", bool isDeleted = false);\n    };\n\n    class StoreBase\n    {\n    public:\n        virtual ~StoreBase() {}\n\n        virtual void setUp() {}\n\n        /// List identifiers of records contained in this Store (case-smashed). No-op for Stores that don't use string IDs.\n        virtual void listIdentifier(std::vector<std::string> &list) const {}\n\n        virtual size_t getSize() const = 0;\n        virtual int getDynamicSize() const { return 0; }\n        virtual RecordId load(ESM::ESMReader &esm) = 0;\n\n        virtual bool eraseStatic(const std::string &id) {return false;}\n        virtual void clearDynamic() {}\n\n        virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const {}\n\n        virtual RecordId read (ESM::ESMReader& reader, bool overrideOnly = false) { return RecordId(); }\n        ///< Read into dynamic storage\n    };\n\n    template <class T>\n    class IndexedStore\n    {\n    protected:\n        typedef typename std::map<int, T> Static;\n        Static mStatic;\n\n    public:\n        typedef typename std::map<int, T>::const_iterator iterator;\n\n        IndexedStore();\n\n        iterator begin() const;\n        iterator end() const;\n\n        void load(ESM::ESMReader &esm);\n\n        int getSize() const;\n        void setUp();\n\n        const T *search(int index) const;\n        const T *find(int index) const;\n    };\n\n    template <class T>\n    class SharedIterator\n    {\n        typedef typename std::vector<T *>::const_iterator Iter;\n\n        Iter mIter;\n\n    public:\n        SharedIterator() {}\n\n        SharedIterator(const SharedIterator &orig)\n          : mIter(orig.mIter)\n        {}\n\n        SharedIterator(const Iter &iter)\n          : mIter(iter)\n        {}\n\n        SharedIterator& operator=(const SharedIterator&) = default;\n\n        SharedIterator &operator++() {\n            ++mIter;\n            return *this;\n        }\n\n        SharedIterator operator++(int) {\n            SharedIterator iter = *this;\n            ++mIter;\n\n            return iter;\n        }\n\n        SharedIterator &operator+=(int advance) {\n            mIter += advance;\n            return *this;\n        }\n\n        SharedIterator &operator--() {\n            --mIter;\n            return *this;\n        }\n\n        SharedIterator operator--(int) {\n            SharedIterator iter = *this;\n            --mIter;\n\n            return iter;\n        }\n\n        bool operator==(const SharedIterator &x) const {\n            return mIter == x.mIter;\n        }\n\n        bool operator!=(const SharedIterator &x) const {\n            return !(*this == x);\n        }\n\n        const T &operator*() const {\n            return **mIter;\n        }\n\n        const T *operator->() const {\n            return &(**mIter);\n        }\n    };\n\n    class ESMStore;\n\n    template <class T>\n    class Store : public StoreBase\n    {\n        std::map<std::string, T>      mStatic;\n        std::vector<T *>    mShared; // Preserves the record order as it came from the content files (this\n                                     // is relevant for the spell autocalc code and selection order\n                                     // for heads/hairs in the character creation)\n        std::map<std::string, T> mDynamic;\n\n        typedef std::map<std::string, T> Dynamic;\n        typedef std::map<std::string, T> Static;\n\n        friend class ESMStore;\n\n    public:\n        Store();\n        Store(const Store<T> &orig);\n\n        typedef SharedIterator<T> iterator;\n\n        // setUp needs to be called again after\n        void clearDynamic() override;\n        void setUp() override;\n\n        const T *search(const std::string &id) const;\n        const T *searchStatic(const std::string &id) const;\n\n        /**\n         * Does the record with this ID come from the dynamic store?\n         */\n        bool isDynamic(const std::string &id) const;\n\n        /** Returns a random record that starts with the named ID, or nullptr if not found. */\n        const T *searchRandom(const std::string &id) const;\n\n        const T *find(const std::string &id) const;\n\n        iterator begin() const;\n        iterator end() const;\n\n        size_t getSize() const override;\n        int getDynamicSize() const override;\n\n        /// @note The record identifiers are listed in the order that the records were defined by the content files.\n        void listIdentifier(std::vector<std::string> &list) const override;\n\n        T *insert(const T &item, bool overrideOnly = false);\n        T *insertStatic(const T &item);\n\n        bool eraseStatic(const std::string &id) override;\n        bool erase(const std::string &id);\n        bool erase(const T &item);\n\n        RecordId load(ESM::ESMReader &esm) override;\n        void write(ESM::ESMWriter& writer, Loading::Listener& progress) const override;\n        RecordId read(ESM::ESMReader& reader, bool overrideOnly = false) override;\n    };\n\n    template <>\n    class Store<ESM::LandTexture> : public StoreBase\n    {\n        // For multiple ESM/ESP files we need one list per file.\n        typedef std::vector<ESM::LandTexture> LandTextureList;\n        std::vector<LandTextureList> mStatic;\n\n    public:\n        Store();\n\n        typedef std::vector<ESM::LandTexture>::const_iterator iterator;\n\n        // Must be threadsafe! Called from terrain background loading threads.\n        // Not a big deal here, since ESM::LandTexture can never be modified or inserted/erased\n        const ESM::LandTexture *search(size_t index, size_t plugin) const;\n        const ESM::LandTexture *find(size_t index, size_t plugin) const;\n\n        /// Resize the internal store to hold at least \\a num plugins.\n        void resize(size_t num);\n\n        size_t getSize() const override;\n        size_t getSize(size_t plugin) const;\n\n        RecordId load(ESM::ESMReader &esm, size_t plugin);\n        RecordId load(ESM::ESMReader &esm) override;\n\n        iterator begin(size_t plugin) const;\n        iterator end(size_t plugin) const;\n    };\n\n    template <>\n    class Store<ESM::Land> : public StoreBase\n    {\n        std::vector<ESM::Land *> mStatic;\n\n    public:\n        typedef SharedIterator<ESM::Land> iterator;\n\n        virtual ~Store();\n\n        size_t getSize() const override;\n        iterator begin() const;\n        iterator end() const;\n\n        // Must be threadsafe! Called from terrain background loading threads.\n        // Not a big deal here, since ESM::Land can never be modified or inserted/erased\n        const ESM::Land *search(int x, int y) const;\n        const ESM::Land *find(int x, int y) const;\n\n        RecordId load(ESM::ESMReader &esm) override;\n        void setUp() override;\n    private:\n        bool mBuilt = false;\n    };\n\n    template <>\n    class Store<ESM::Cell> : public StoreBase\n    {\n        struct DynamicExtCmp\n        {\n            bool operator()(const std::pair<int, int> &left, const std::pair<int, int> &right) const {\n                if (left.first == right.first && left.second == right.second)\n                    return false;\n\n                if (left.first == right.first)\n                    return left.second > right.second;\n\n                // Exterior cells are listed in descending, row-major order,\n                // this is a workaround for an ambiguous chargen_plank reference in the vanilla game.\n                // there is one at -22,16 and one at -2,-9, the latter should be used.\n                return left.first > right.first;\n            }\n        };\n\n        typedef std::map<std::string, ESM::Cell>                           DynamicInt;\n        typedef std::map<std::pair<int, int>, ESM::Cell, DynamicExtCmp>    DynamicExt;\n\n        DynamicInt      mInt;\n        DynamicExt      mExt;\n\n        std::vector<ESM::Cell *>    mSharedInt;\n        std::vector<ESM::Cell *>    mSharedExt;\n\n        DynamicInt mDynamicInt;\n        DynamicExt mDynamicExt;\n\n        const ESM::Cell *search(const ESM::Cell &cell) const;\n        void handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell);\n\n    public:\n        typedef SharedIterator<ESM::Cell> iterator;\n\n        const ESM::Cell *search(const std::string &id) const;\n        const ESM::Cell *search(int x, int y) const;\n        const ESM::Cell *searchStatic(int x, int y) const;\n        const ESM::Cell *searchOrCreate(int x, int y);\n\n        const ESM::Cell *find(const std::string &id) const;\n        const ESM::Cell *find(int x, int y) const;\n\n        void clearDynamic() override;\n        void setUp() override;\n\n        RecordId load(ESM::ESMReader &esm) override;\n\n        iterator intBegin() const;\n        iterator intEnd() const;\n        iterator extBegin() const;\n        iterator extEnd() const;\n\n        // Return the northernmost cell in the easternmost column.\n        const ESM::Cell *searchExtByName(const std::string &id) const;\n\n        // Return the northernmost cell in the easternmost column.\n        const ESM::Cell *searchExtByRegion(const std::string &id) const;\n\n        size_t getSize() const override;\n        size_t getExtSize() const;\n        size_t getIntSize() const;\n\n        void listIdentifier(std::vector<std::string> &list) const override;\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to override a Cell record similarly to how\n            other types of records can be overridden\n        */\n        ESM::Cell *override(const ESM::Cell &cell);\n        /*\n            End of tes3mp addition\n        */\n        ESM::Cell *insert(const ESM::Cell &cell);\n\n        bool erase(const ESM::Cell &cell);\n        bool erase(const std::string &id);\n\n        bool erase(int x, int y);\n    };\n\n    template <>\n    class Store<ESM::Pathgrid> : public StoreBase\n    {\n    private:\n        typedef std::map<std::string, ESM::Pathgrid> Interior;\n        typedef std::map<std::pair<int, int>, ESM::Pathgrid> Exterior;\n\n        Interior mInt;\n        Exterior mExt;\n\n        Store<ESM::Cell>* mCells;\n\n    public:\n\n        Store();\n\n        void setCells(Store<ESM::Cell>& cells);\n        RecordId load(ESM::ESMReader &esm) override;\n        size_t getSize() const override;\n\n        void setUp() override;\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to override a Pathgrid record similarly to how\n            other types of records can be overridden\n        */\n        ESM::Pathgrid* override(const ESM::Pathgrid& pathgrid);\n        /*\n            End of tes3mp addition\n        */\n\n        const ESM::Pathgrid *search(int x, int y) const;\n        const ESM::Pathgrid *search(const std::string& name) const;\n        const ESM::Pathgrid *find(int x, int y) const;\n        const ESM::Pathgrid* find(const std::string& name) const;\n        const ESM::Pathgrid *search(const ESM::Cell &cell) const;\n        const ESM::Pathgrid *find(const ESM::Cell &cell) const;\n    };\n\n\n    template <>\n    class Store<ESM::Skill> : public IndexedStore<ESM::Skill>\n    {\n    public:\n        Store();\n    };\n\n    template <>\n    class Store<ESM::MagicEffect> : public IndexedStore<ESM::MagicEffect>\n    {\n    public:\n        Store();\n    };\n\n    template <>\n    class Store<ESM::Attribute> : public IndexedStore<ESM::Attribute>\n    {\n        std::vector<ESM::Attribute> mStatic;\n\n    public:\n        typedef std::vector<ESM::Attribute>::const_iterator iterator;\n\n        Store();\n\n        const ESM::Attribute *search(size_t index) const;\n        const ESM::Attribute *find(size_t index) const;\n\n        void setUp();\n\n        size_t getSize() const;\n        iterator begin() const;\n        iterator end() const;\n    };\n\n    template <>\n    class Store<ESM::WeaponType> : public StoreBase\n    {\n        std::map<int, ESM::WeaponType> mStatic;\n\n    public:\n        typedef std::map<int, ESM::WeaponType>::const_iterator iterator;\n\n        Store();\n\n        const ESM::WeaponType *search(const int id) const;\n        const ESM::WeaponType *find(const int id) const;\n\n        RecordId load(ESM::ESMReader &esm) override { return RecordId(nullptr, false); }\n\n        ESM::WeaponType* insert(const ESM::WeaponType &weaponType);\n\n        void setUp() override;\n\n        size_t getSize() const override;\n        iterator begin() const;\n        iterator end() const;\n    };\n\n\n} //end namespace\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/timestamp.cpp",
    "content": "#include \"timestamp.hpp\"\n\n#include <cmath>\n#include <stdexcept>\n\n#include <components/esm/defs.hpp>\n\nnamespace MWWorld\n{\n    TimeStamp::TimeStamp (float hour, int day)\n    : mHour (hour), mDay (day)\n    {\n        if (hour<0 || hour>=24 || day<0)\n            throw std::runtime_error (\"invalid time stamp\");\n    }\n\n    float TimeStamp::getHour() const\n    {\n        return mHour;\n    }\n\n    int TimeStamp::getDay() const\n    {\n        return mDay;\n    }\n\n    TimeStamp& TimeStamp::operator+= (double hours)\n    {\n        if (hours<0)\n            throw std::runtime_error (\"can't move time stamp backwards in time\");\n\n        hours += mHour;\n\n        mHour = static_cast<float> (std::fmod (hours, 24));\n\n        mDay += static_cast<int>(hours / 24);\n\n        return *this;\n    }\n\n    bool operator== (const TimeStamp& left, const TimeStamp& right)\n    {\n        return left.getHour()==right.getHour() && left.getDay()==right.getDay();\n    }\n\n    bool operator!= (const TimeStamp& left, const TimeStamp& right)\n    {\n        return !(left==right);\n    }\n\n    bool operator< (const TimeStamp& left, const TimeStamp& right)\n    {\n        if (left.getDay()<right.getDay())\n            return true;\n\n        if (left.getDay()>right.getDay())\n            return false;\n\n        return left.getHour()<right.getHour();\n    }\n\n    bool operator<= (const TimeStamp& left, const TimeStamp& right)\n    {\n        return left<right || left==right;\n    }\n\n    bool operator> (const TimeStamp& left, const TimeStamp& right)\n    {\n        return !(left<=right);\n    }\n\n    bool operator>= (const TimeStamp& left, const TimeStamp& right)\n    {\n        return !(left<right);\n    }\n\n    TimeStamp operator+ (const TimeStamp& stamp, double hours)\n    {\n        return TimeStamp (stamp) += hours;\n    }\n\n    TimeStamp operator+ (double hours, const TimeStamp& stamp)\n    {\n        return TimeStamp (stamp) += hours;\n    }\n\n    double operator- (const TimeStamp& left, const TimeStamp& right)\n    {\n        if (left<right)\n            return -(right-left);\n\n        int days = left.getDay() - right.getDay();\n\n        double hours = 0;\n\n        if (left.getHour()<right.getHour())\n        {\n            hours = 24-right.getHour()+left.getHour();\n            --days;\n        }\n        else\n        {\n            hours = left.getHour()-right.getHour();\n        }\n\n        return hours + 24*days;\n    }\n\n    ESM::TimeStamp TimeStamp::toEsm() const\n    {\n        ESM::TimeStamp ret;\n        ret.mDay = mDay;\n        ret.mHour = mHour;\n        return ret;\n    }\n\n    TimeStamp::TimeStamp(const ESM::TimeStamp &esm)\n    {\n        mDay = esm.mDay;\n        mHour = esm.mHour;\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/timestamp.hpp",
    "content": "#ifndef GAME_MWWORLD_TIMESTAMP_H\n#define GAME_MWWORLD_TIMESTAMP_H\n\nnamespace ESM\n{\n    struct TimeStamp;\n}\n\nnamespace MWWorld\n{\n    /// \\brief In-game time stamp\n    ///\n    /// This class is based on the global variables GameHour and DaysPassed.\n    class TimeStamp\n    {\n            float mHour;\n            int mDay;\n\n        public:\n\n            explicit TimeStamp (float hour = 0, int day = 0);\n            ///< \\param hour [0, 23)\n            /// \\param day >=0\n\n            explicit TimeStamp (const ESM::TimeStamp& esm);\n            ESM::TimeStamp toEsm () const;\n\n            float getHour() const;\n\n            int getDay() const;\n\n            TimeStamp& operator+= (double hours);\n            ///< \\param hours >=0\n    };\n\n    bool operator== (const TimeStamp& left, const TimeStamp& right);\n    bool operator!= (const TimeStamp& left, const TimeStamp& right);\n\n    bool operator< (const TimeStamp& left, const TimeStamp& right);\n    bool operator<= (const TimeStamp& left, const TimeStamp& right);\n\n    bool operator> (const TimeStamp& left, const TimeStamp& right);\n    bool operator>= (const TimeStamp& left, const TimeStamp& right);\n\n    TimeStamp operator+ (const TimeStamp& stamp, double hours);\n    TimeStamp operator+ (double hours, const TimeStamp& stamp);\n\n    double operator- (const TimeStamp& left, const TimeStamp& right);\n    ///< Returns the difference between \\a left and \\a right in in-game hours.\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw/mwworld/weather.cpp",
    "content": "#include \"weather.hpp\"\n\n#include <components/misc/rng.hpp>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/weatherstate.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/Worldstate.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwmechanics/actorutil.hpp\"\n\n#include \"../mwsound/sound.hpp\"\n\n#include \"../mwrender/renderingmanager.hpp\"\n#include \"../mwrender/sky.hpp\"\n\n#include \"player.hpp\"\n#include \"esmstore.hpp\"\n#include \"cellstore.hpp\"\n\n#include <cmath>\n\nusing namespace MWWorld;\n\nnamespace\n{\n    static const int invalidWeatherID = -1;\n\n    // linear interpolate between x and y based on factor.\n    float lerp (float x, float y, float factor)\n    {\n        return x * (1-factor) + y * factor;\n    }\n    // linear interpolate between x and y based on factor.\n    osg::Vec4f lerp (const osg::Vec4f& x, const osg::Vec4f& y, float factor)\n    {\n        return x * (1-factor) + y * factor;\n    }\n}\n\ntemplate <typename T>\nT TimeOfDayInterpolator<T>::getValue(const float gameHour, const TimeOfDaySettings& timeSettings, const std::string& prefix) const\n{\n    WeatherSetting setting = timeSettings.getSetting(prefix);\n    float preSunriseTime = setting.mPreSunriseTime;\n    float postSunriseTime = setting.mPostSunriseTime;\n    float preSunsetTime = setting.mPreSunsetTime;\n    float postSunsetTime = setting.mPostSunsetTime;\n\n    // night\n    if (gameHour < timeSettings.mNightEnd - preSunriseTime || gameHour > timeSettings.mNightStart + postSunsetTime)\n        return mNightValue;\n    // sunrise\n    else if (gameHour >= timeSettings.mNightEnd - preSunriseTime && gameHour <= timeSettings.mDayStart + postSunriseTime)\n    {\n        float duration = timeSettings.mDayStart + postSunriseTime - timeSettings.mNightEnd + preSunriseTime;\n        float middle = timeSettings.mNightEnd - preSunriseTime + duration / 2.f;\n\n        if (gameHour <= middle)\n        {\n            // fade in\n            float advance = middle - gameHour;\n            float factor = 0.f;\n            if (duration > 0)\n                factor = advance / duration * 2;\n            return lerp(mSunriseValue, mNightValue, factor);\n        }\n        else\n        {\n            // fade out\n            float advance = gameHour - middle;\n            float factor = 1.f;\n            if (duration > 0)\n                factor = advance / duration * 2;\n            return lerp(mSunriseValue, mDayValue, factor);\n        }\n    }\n    // day\n    else if (gameHour > timeSettings.mDayStart + postSunriseTime && gameHour < timeSettings.mDayEnd - preSunsetTime)\n        return mDayValue;\n    // sunset\n    else if (gameHour >= timeSettings.mDayEnd - preSunsetTime && gameHour <= timeSettings.mNightStart + postSunsetTime)\n    {\n        float duration = timeSettings.mNightStart + postSunsetTime - timeSettings.mDayEnd + preSunsetTime;\n        float middle = timeSettings.mDayEnd - preSunsetTime + duration / 2.f;\n\n        if (gameHour <= middle)\n        {\n            // fade in\n            float advance = middle - gameHour;\n            float factor = 0.f;\n            if (duration > 0)\n                factor = advance / duration * 2;\n            return lerp(mSunsetValue, mDayValue, factor);\n        }\n        else\n        {\n            // fade out\n            float advance = gameHour - middle;\n            float factor = 1.f;\n            if (duration > 0)\n                factor = advance / duration * 2;\n            return lerp(mSunsetValue, mNightValue, factor);\n        }\n    }\n    // shut up compiler\n    return T();\n}\n\n\n\ntemplate class MWWorld::TimeOfDayInterpolator<float>;\ntemplate class MWWorld::TimeOfDayInterpolator<osg::Vec4f>;\n\nWeather::Weather(const std::string& name,\n                 float stormWindSpeed,\n                 float rainSpeed,\n                 float dlFactor,\n                 float dlOffset,\n                 const std::string& particleEffect)\n    : mCloudTexture(Fallback::Map::getString(\"Weather_\" + name + \"_Cloud_Texture\"))\n    , mSkyColor(Fallback::Map::getColour(\"Weather_\" + name +\"_Sky_Sunrise_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Sky_Day_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Sky_Sunset_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Sky_Night_Color\"))\n    , mFogColor(Fallback::Map::getColour(\"Weather_\" + name + \"_Fog_Sunrise_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Fog_Day_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Fog_Sunset_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Fog_Night_Color\"))\n    , mAmbientColor(Fallback::Map::getColour(\"Weather_\" + name + \"_Ambient_Sunrise_Color\"),\n                    Fallback::Map::getColour(\"Weather_\" + name + \"_Ambient_Day_Color\"),\n                    Fallback::Map::getColour(\"Weather_\" + name + \"_Ambient_Sunset_Color\"),\n                    Fallback::Map::getColour(\"Weather_\" + name + \"_Ambient_Night_Color\"))\n    , mSunColor(Fallback::Map::getColour(\"Weather_\" + name + \"_Sun_Sunrise_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Sun_Day_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Sun_Sunset_Color\"),\n                Fallback::Map::getColour(\"Weather_\" + name + \"_Sun_Night_Color\"))\n    , mLandFogDepth(Fallback::Map::getFloat(\"Weather_\" + name + \"_Land_Fog_Day_Depth\"),\n                    Fallback::Map::getFloat(\"Weather_\" + name + \"_Land_Fog_Day_Depth\"),\n                    Fallback::Map::getFloat(\"Weather_\" + name + \"_Land_Fog_Day_Depth\"),\n                    Fallback::Map::getFloat(\"Weather_\" + name + \"_Land_Fog_Night_Depth\"))\n    , mSunDiscSunsetColor(Fallback::Map::getColour(\"Weather_\" + name + \"_Sun_Disc_Sunset_Color\"))\n    , mWindSpeed(Fallback::Map::getFloat(\"Weather_\" + name + \"_Wind_Speed\"))\n    , mCloudSpeed(Fallback::Map::getFloat(\"Weather_\" + name + \"_Cloud_Speed\"))\n    , mGlareView(Fallback::Map::getFloat(\"Weather_\" + name + \"_Glare_View\"))\n    , mIsStorm(mWindSpeed > stormWindSpeed)\n    , mRainSpeed(rainSpeed)\n    , mRainEntranceSpeed(Fallback::Map::getFloat(\"Weather_\" + name + \"_Rain_Entrance_Speed\"))\n    , mRainMaxRaindrops(Fallback::Map::getFloat(\"Weather_\" + name + \"_Max_Raindrops\"))\n    , mRainDiameter(Fallback::Map::getFloat(\"Weather_\" + name + \"_Rain_Diameter\"))\n    , mRainThreshold(Fallback::Map::getFloat(\"Weather_\" + name + \"_Rain_Threshold\"))\n    , mRainMinHeight(Fallback::Map::getFloat(\"Weather_\" + name + \"_Rain_Height_Min\"))\n    , mRainMaxHeight(Fallback::Map::getFloat(\"Weather_\" + name + \"_Rain_Height_Max\"))\n    , mParticleEffect(particleEffect)\n    , mRainEffect(Fallback::Map::getBool(\"Weather_\" + name + \"_Using_Precip\") ? \"meshes\\\\raindrop.nif\" : \"\")\n    , mTransitionDelta(Fallback::Map::getFloat(\"Weather_\" + name + \"_Transition_Delta\"))\n    , mCloudsMaximumPercent(Fallback::Map::getFloat(\"Weather_\" + name + \"_Clouds_Maximum_Percent\"))\n    , mThunderFrequency(Fallback::Map::getFloat(\"Weather_\" + name + \"_Thunder_Frequency\"))\n    , mThunderThreshold(Fallback::Map::getFloat(\"Weather_\" + name + \"_Thunder_Threshold\"))\n    , mThunderSoundID()\n    , mFlashDecrement(Fallback::Map::getFloat(\"Weather_\" + name + \"_Flash_Decrement\"))\n    , mFlashBrightness(0.0f)\n{\n    mDL.FogFactor = dlFactor;\n    mDL.FogOffset = dlOffset;\n    mThunderSoundID[0] = Fallback::Map::getString(\"Weather_\" + name + \"_Thunder_Sound_ID_0\");\n    mThunderSoundID[1] = Fallback::Map::getString(\"Weather_\" + name + \"_Thunder_Sound_ID_1\");\n    mThunderSoundID[2] = Fallback::Map::getString(\"Weather_\" + name + \"_Thunder_Sound_ID_2\");\n    mThunderSoundID[3] = Fallback::Map::getString(\"Weather_\" + name + \"_Thunder_Sound_ID_3\");\n\n    // TODO: support weathers that have both \"Ambient Loop Sound ID\" and \"Rain Loop Sound ID\", need to play both sounds at the same time.\n\n    if (!mRainEffect.empty()) // NOTE: in vanilla, the weathers with rain seem to be hardcoded; changing Using_Precip has no effect\n    {\n        mAmbientLoopSoundID = Fallback::Map::getString(\"Weather_\" + name + \"_Rain_Loop_Sound_ID\");\n        if (mAmbientLoopSoundID.empty()) // default to \"rain\" if not set\n            mAmbientLoopSoundID = \"rain\";\n    }\n    else\n        mAmbientLoopSoundID = Fallback::Map::getString(\"Weather_\" + name + \"_Ambient_Loop_Sound_ID\");\n\n    if (Misc::StringUtils::ciEqual(mAmbientLoopSoundID, \"None\"))\n        mAmbientLoopSoundID.clear();\n}\n\nfloat Weather::transitionDelta() const\n{\n    // Transition Delta describes how quickly transitioning to the weather in question will take, in Hz. Note that the\n    // measurement is in real time, not in-game time.\n    return mTransitionDelta;\n}\n\nfloat Weather::cloudBlendFactor(const float transitionRatio) const\n{\n    // Clouds Maximum Percent affects how quickly the sky transitions from one sky texture to the next.\n    return transitionRatio / mCloudsMaximumPercent;\n}\n\nfloat Weather::calculateThunder(const float transitionRatio, const float elapsedSeconds, const bool isPaused)\n{\n    // When paused, the flash brightness remains the same and no new strikes can occur.\n    if(!isPaused)\n    {\n        // Morrowind doesn't appear to do any calculations unless the transition ratio is higher than the Thunder Threshold.\n        if(transitionRatio >= mThunderThreshold && mThunderFrequency > 0.0f)\n        {\n            flashDecrement(elapsedSeconds);\n\n            if(Misc::Rng::rollProbability() <= thunderChance(transitionRatio, elapsedSeconds))\n            {\n                lightningAndThunder();\n            }\n        }\n        else\n        {\n            mFlashBrightness = 0.0f;\n        }\n    }\n\n    return mFlashBrightness;\n}\n\ninline void Weather::flashDecrement(const float elapsedSeconds)\n{\n   // The Flash Decrement is measured in whole units per second. This means that if the flash brightness was\n   // currently 1.0, then it should take approximately 0.25 seconds to decay to 0.0 (the minimum).\n   float decrement = mFlashDecrement * elapsedSeconds;\n   mFlashBrightness = decrement > mFlashBrightness ? 0.0f : mFlashBrightness - decrement;\n}\n\ninline float Weather::thunderChance(const float transitionRatio, const float elapsedSeconds) const\n{\n   // This formula is reversed from the observation that with Thunder Frequency set to 1, there are roughly 10 strikes\n   // per minute. It doesn't appear to be tied to in game time as Timescale doesn't affect it. Various values of\n   // Thunder Frequency seem to change the average number of strikes in a linear fashion.. During a transition, it appears to\n   // scaled based on how far past it is past the Thunder Threshold.\n   float scaleFactor = (transitionRatio - mThunderThreshold) / (1.0f - mThunderThreshold);\n   return ((mThunderFrequency * 10.0f) / 60.0f) * elapsedSeconds * scaleFactor;\n}\n\ninline void Weather::lightningAndThunder(void)\n{\n    // Morrowind seems to vary the intensity of the brightness based on which of the four sound IDs it selects.\n    // They appear to go from 0 (brightest, closest) to 3 (faintest, farthest). The value of 0.25 per distance\n    // was derived by setting the Flash Decrement to 0.1 and measuring how long each value took to decay to 0.\n    // TODO: Determine the distribution of each distance to see if it's evenly weighted.\n    unsigned int distance = Misc::Rng::rollDice(4);\n    // Flash brightness appears additive, since if multiple strikes occur, it takes longer for it to decay to 0.\n    mFlashBrightness += 1 - (distance * 0.25f);\n    MWBase::Environment::get().getSoundManager()->playSound(mThunderSoundID[distance], 1.0, 1.0);\n}\n\nRegionWeather::RegionWeather(const ESM::Region& region)\n    : mWeather(invalidWeatherID)\n    , mChances()\n{\n    mChances.reserve(10);\n    mChances.push_back(region.mData.mClear);\n    mChances.push_back(region.mData.mCloudy);\n    mChances.push_back(region.mData.mFoggy);\n    mChances.push_back(region.mData.mOvercast);\n    mChances.push_back(region.mData.mRain);\n    mChances.push_back(region.mData.mThunder);\n    mChances.push_back(region.mData.mAsh);\n    mChances.push_back(region.mData.mBlight);\n    mChances.push_back(region.mData.mA);\n    mChances.push_back(region.mData.mB);\n}\n\nRegionWeather::RegionWeather(const ESM::RegionWeatherState& state)\n    : mWeather(state.mWeather)\n    , mChances(state.mChances)\n{\n}\n\nRegionWeather::operator ESM::RegionWeatherState() const\n{\n    ESM::RegionWeatherState state =\n        {\n            mWeather,\n            mChances\n        };\n\n    return state;\n}\n\nvoid RegionWeather::setChances(const std::vector<char>& chances)\n{\n    if(mChances.size() < chances.size())\n    {\n        mChances.reserve(chances.size());\n    }\n\n    int i = 0;\n    for(char chance : chances)\n    {\n        mChances[i] = chance;\n        i++;\n    }\n\n    // Regional weather no longer supports the current type, select a new weather pattern.\n    if((static_cast<size_t>(mWeather) >= mChances.size()) || (mChances[mWeather] == 0))\n    {\n        chooseNewWeather();\n    }\n}\n\nvoid RegionWeather::setWeather(int weatherID)\n{\n    mWeather = weatherID;\n}\n\nint RegionWeather::getWeather()\n{\n    // If the region weather was already set (by ChangeWeather, or by a previous call) then just return that value.\n    // Note that the region weather will be expired periodically when the weather update timer expires.\n    if(mWeather == invalidWeatherID)\n    {\n        chooseNewWeather();\n    }\n\n    return mWeather;\n}\n\nvoid RegionWeather::chooseNewWeather()\n{\n    // All probabilities must add to 100 (responsibility of the user).\n    // If chances A and B has values 30 and 70 then by generating 100 numbers 1..100, 30% will be lesser or equal 30\n    // and 70% will be greater than 30 (in theory).\n    int chance = Misc::Rng::rollDice(100) + 1; // 1..100\n    int sum = 0;\n    int i = 0;\n    for(; static_cast<size_t>(i) < mChances.size(); ++i)\n    {\n        sum += mChances[i];\n        if(chance <= sum)\n        {\n            mWeather = i;\n            return;\n        }\n    }\n\n    // if we hit this path then the chances don't add to 100, choose a default weather instead\n    mWeather = 0;\n}\n\nMoonModel::MoonModel(const std::string& name)\n  : mFadeInStart(Fallback::Map::getFloat(\"Moons_\" + name + \"_Fade_In_Start\"))\n  , mFadeInFinish(Fallback::Map::getFloat(\"Moons_\" + name + \"_Fade_In_Finish\"))\n  , mFadeOutStart(Fallback::Map::getFloat(\"Moons_\" + name + \"_Fade_Out_Start\"))\n  , mFadeOutFinish(Fallback::Map::getFloat(\"Moons_\" + name + \"_Fade_Out_Finish\"))\n  , mAxisOffset(Fallback::Map::getFloat(\"Moons_\" + name + \"_Axis_Offset\"))\n  , mSpeed(Fallback::Map::getFloat(\"Moons_\" + name + \"_Speed\"))\n  , mDailyIncrement(Fallback::Map::getFloat(\"Moons_\" + name + \"_Daily_Increment\"))\n  , mFadeStartAngle(Fallback::Map::getFloat(\"Moons_\" + name + \"_Fade_Start_Angle\"))\n  , mFadeEndAngle(Fallback::Map::getFloat(\"Moons_\" + name + \"_Fade_End_Angle\"))\n  , mMoonShadowEarlyFadeAngle(Fallback::Map::getFloat(\"Moons_\" + name + \"_Moon_Shadow_Early_Fade_Angle\"))\n{\n    // Morrowind appears to have a minimum speed in order to avoid situations where the moon couldn't conceivably\n    // complete a rotation in a single 24 hour period. The value of 180/23 was deduced from reverse engineering.\n    mSpeed = std::min(mSpeed, 180.0f / 23.0f);\n}\n\nMWRender::MoonState MoonModel::calculateState(const TimeStamp& gameTime) const\n{\n    float rotationFromHorizon = angle(gameTime);\n    MWRender::MoonState state =\n        {\n            rotationFromHorizon,\n            mAxisOffset, // Reverse engineered from Morrowind's scene graph rotation matrices.\n            phase(gameTime),\n            shadowBlend(rotationFromHorizon),\n            earlyMoonShadowAlpha(rotationFromHorizon) * hourlyAlpha(gameTime.getHour())\n        };\n\n    return state;\n}\n\ninline float MoonModel::angle(const TimeStamp& gameTime) const\n{\n    // Morrowind's moons start travel on one side of the horizon (let's call it H-rise) and travel 180 degrees to the\n    // opposite horizon (let's call it H-set). Upon reaching H-set, they reset to H-rise until the next moon rise.\n\n    // When calculating the angle of the moon, several cases have to be taken into account:\n    // 1. Moon rises and then sets in one day.\n    // 2. Moon sets and doesn't rise in one day (occurs when the moon rise hour is >= 24).\n    // 3. Moon sets and then rises in one day.\n    float moonRiseHourToday = moonRiseHour(gameTime.getDay());\n    float moonRiseAngleToday = 0;\n\n    if(gameTime.getHour() < moonRiseHourToday)\n    {\n        float moonRiseHourYesterday = moonRiseHour(gameTime.getDay() - 1);\n        if(moonRiseHourYesterday < 24)\n        {\n            float moonRiseAngleYesterday = rotation(24 - moonRiseHourYesterday);\n            if(moonRiseAngleYesterday < 180)\n            {\n                // The moon rose but did not set yesterday, so accumulate yesterday's angle with how much we've travelled today.\n                moonRiseAngleToday = rotation(gameTime.getHour()) + moonRiseAngleYesterday;\n            }\n        }\n    }\n    else\n    {\n        moonRiseAngleToday = rotation(gameTime.getHour() - moonRiseHourToday);\n    }\n\n    if(moonRiseAngleToday >= 180)\n    {\n        // The moon set today, reset the angle to the horizon.\n        moonRiseAngleToday = 0;\n    }\n\n    return moonRiseAngleToday;\n}\n\ninline float MoonModel::moonRiseHour(unsigned int daysPassed) const\n{\n    // This arises from the start date of 16 Last Seed, 427\n    // TODO: Find an alternate formula that doesn't rely on this day being fixed.\n    static const unsigned int startDay = 16;\n\n    // This odd formula arises from the fact that on 16 Last Seed, 17 increments have occurred, meaning\n    // that upon starting a new game, it must only calculate the moon phase as far back as 1 Last Seed.\n    // Note that we don't modulo after adding the latest daily increment because other calculations need to\n    // know if doing so would cause the moon rise to be postponed until the next day (which happens when\n    // the moon rise hour is >= 24 in Morrowind).\n    return mDailyIncrement + std::fmod((daysPassed - 1 + startDay) * mDailyIncrement, 24.0f);\n}\n\ninline float MoonModel::rotation(float hours) const\n{\n    // 15 degrees per hour was reverse engineered from the rotation matrices of the Morrowind scene graph.\n    // Note that this correlates to 360 / 24, which is a full rotation every 24 hours, so speed is a measure\n    // of whole rotations that could be completed in a day.\n    return 15.0f * mSpeed * hours;\n}\n\nMWRender::MoonState::Phase MoonModel::phase(const TimeStamp& gameTime) const\n{\n    // Morrowind starts with a full moon on 16 Last Seed and then begins to wane 17 Last Seed, working on 3 day phase cycle.\n\n    // If the moon didn't rise yet today, use yesterday's moon phase.\n    if(gameTime.getHour() < moonRiseHour(gameTime.getDay()))\n        return static_cast<MWRender::MoonState::Phase>((gameTime.getDay() / 3) % 8);\n    else\n        return static_cast<MWRender::MoonState::Phase>(((gameTime.getDay() + 1) / 3) % 8);\n}\n\ninline float MoonModel::shadowBlend(float angle) const\n{\n    // The Fade End Angle and Fade Start Angle describe a region where the moon transitions from a solid disk\n    // that is roughly the color of the sky, to a textured surface.\n    // Depending on the current angle, the following values describe the ratio between the textured moon\n    // and the solid disk:\n    // 1. From Fade End Angle 1 to Fade Start Angle 1 (during moon rise): 0..1\n    // 2. From Fade Start Angle 1 to Fade Start Angle 2 (between moon rise and moon set): 1 (textured)\n    // 3. From Fade Start Angle 2 to Fade End Angle 2 (during moon set): 1..0\n    // 4. From Fade End Angle 2 to Fade End Angle 1 (between moon set and moon rise): 0 (solid disk)\n    float fadeAngle = mFadeStartAngle - mFadeEndAngle;\n    float fadeEndAngle2 = 180.0f - mFadeEndAngle;\n    float fadeStartAngle2 = 180.0f - mFadeStartAngle;\n    if((angle >= mFadeEndAngle) && (angle < mFadeStartAngle))\n        return (angle - mFadeEndAngle) / fadeAngle;\n    else if((angle >= mFadeStartAngle) && (angle < fadeStartAngle2))\n        return 1.0f;\n    else if((angle >= fadeStartAngle2) && (angle < fadeEndAngle2))\n        return (fadeEndAngle2 - angle) / fadeAngle;\n    else\n        return 0.0f;\n}\n\ninline float MoonModel::hourlyAlpha(float gameHour) const\n{\n    // The Fade Out Start / Finish and Fade In Start / Finish describe the hours at which the moon\n    // appears and disappears.\n    // Depending on the current hour, the following values describe how transparent the moon is.\n    // 1. From Fade Out Start to Fade Out Finish: 1..0\n    // 2. From Fade Out Finish to Fade In Start: 0 (transparent)\n    // 3. From Fade In Start to Fade In Finish: 0..1\n    // 4. From Fade In Finish to Fade Out Start: 1 (solid)\n    if((gameHour >= mFadeOutStart) && (gameHour < mFadeOutFinish))\n        return (mFadeOutFinish - gameHour) / (mFadeOutFinish - mFadeOutStart);\n    else if((gameHour >= mFadeOutFinish) && (gameHour < mFadeInStart))\n        return 0.0f;\n    else if((gameHour >= mFadeInStart) && (gameHour < mFadeInFinish))\n        return (gameHour - mFadeInStart) / (mFadeInFinish - mFadeInStart);\n    else\n        return 1.0f;\n}\n\ninline float MoonModel::earlyMoonShadowAlpha(float angle) const\n{\n    // The Moon Shadow Early Fade Angle describes an arc relative to Fade End Angle.\n    // Depending on the current angle, the following values describe how transparent the moon is.\n    // 1. From Moon Shadow Early Fade Angle 1 to Fade End Angle 1 (during moon rise): 0..1\n    // 2. From Fade End Angle 1 to Fade End Angle 2 (between moon rise and moon set): 1 (solid)\n    // 3. From Fade End Angle 2 to Moon Shadow Early Fade Angle 2 (during moon set): 1..0\n    // 4. From Moon Shadow Early Fade Angle 2 to Moon Shadow Early Fade Angle 1: 0 (transparent)\n    float moonShadowEarlyFadeAngle1 = mFadeEndAngle - mMoonShadowEarlyFadeAngle;\n    float fadeEndAngle2 = 180.0f - mFadeEndAngle;\n    float moonShadowEarlyFadeAngle2 = fadeEndAngle2 + mMoonShadowEarlyFadeAngle;\n    if((angle >= moonShadowEarlyFadeAngle1) && (angle < mFadeEndAngle))\n        return (angle - moonShadowEarlyFadeAngle1) / mMoonShadowEarlyFadeAngle;\n    else if((angle >= mFadeEndAngle) && (angle < fadeEndAngle2))\n        return 1.0f;\n    else if((angle >= fadeEndAngle2) && (angle < moonShadowEarlyFadeAngle2))\n        return (moonShadowEarlyFadeAngle2 - angle) / mMoonShadowEarlyFadeAngle;\n    else\n        return 0.0f;\n}\n\nWeatherManager::WeatherManager(MWRender::RenderingManager& rendering, MWWorld::ESMStore& store)\n    : mStore(store)\n    , mRendering(rendering)\n    , mSunriseTime(Fallback::Map::getFloat(\"Weather_Sunrise_Time\"))\n    , mSunsetTime(Fallback::Map::getFloat(\"Weather_Sunset_Time\"))\n    , mSunriseDuration(Fallback::Map::getFloat(\"Weather_Sunrise_Duration\"))\n    , mSunsetDuration(Fallback::Map::getFloat(\"Weather_Sunset_Duration\"))\n    , mSunPreSunsetTime(Fallback::Map::getFloat(\"Weather_Sun_Pre-Sunset_Time\"))\n    , mNightFade(0, 0, 0, 1)\n    , mHoursBetweenWeatherChanges(Fallback::Map::getFloat(\"Weather_Hours_Between_Weather_Changes\"))\n    , mRainSpeed(Fallback::Map::getFloat(\"Weather_Precip_Gravity\"))\n    , mUnderwaterFog(Fallback::Map::getFloat(\"Water_UnderwaterSunriseFog\"),\n                    Fallback::Map::getFloat(\"Water_UnderwaterDayFog\"),\n                    Fallback::Map::getFloat(\"Water_UnderwaterSunsetFog\"),\n                    Fallback::Map::getFloat(\"Water_UnderwaterNightFog\"))\n    , mWeatherSettings()\n    , mMasser(\"Masser\")\n    , mSecunda(\"Secunda\")\n    , mWindSpeed(0.f)\n    , mCurrentWindSpeed(0.f)\n    , mNextWindSpeed(0.f)\n    , mIsStorm(false)\n    , mPrecipitation(false)\n    , mStormDirection(0,1,0)\n    , mCurrentRegion()\n    , mTimePassed(0)\n    , mFastForward(false)\n    , mWeatherUpdateTime(mHoursBetweenWeatherChanges)\n    , mTransitionFactor(0)\n    , mNightDayMode(Default)\n    , mCurrentWeather(0)\n    , mNextWeather(0)\n    , mQueuedWeather(0)\n    , mRegions()\n    , mResult()\n    , mAmbientSound(nullptr)\n    , mPlayingSoundID()\n{\n    mTimeSettings.mNightStart = mSunsetTime + mSunsetDuration;\n    mTimeSettings.mNightEnd = mSunriseTime;\n    mTimeSettings.mDayStart = mSunriseTime + mSunriseDuration;\n    mTimeSettings.mDayEnd = mSunsetTime;\n\n    mTimeSettings.addSetting(\"Sky\");\n    mTimeSettings.addSetting(\"Ambient\");\n    mTimeSettings.addSetting(\"Fog\");\n    mTimeSettings.addSetting(\"Sun\");\n\n    // Morrowind handles stars settings differently for other ones\n    mTimeSettings.mStarsPostSunsetStart = Fallback::Map::getFloat(\"Weather_Stars_Post-Sunset_Start\");\n    mTimeSettings.mStarsPreSunriseFinish = Fallback::Map::getFloat(\"Weather_Stars_Pre-Sunrise_Finish\");\n    mTimeSettings.mStarsFadingDuration = Fallback::Map::getFloat(\"Weather_Stars_Fading_Duration\");\n\n    WeatherSetting starSetting = {\n        mTimeSettings.mStarsPreSunriseFinish,\n        mTimeSettings.mStarsFadingDuration - mTimeSettings.mStarsPreSunriseFinish,\n        mTimeSettings.mStarsPostSunsetStart,\n        mTimeSettings.mStarsFadingDuration - mTimeSettings.mStarsPostSunsetStart\n    };\n\n    mTimeSettings.mSunriseTransitions[\"Stars\"] = starSetting;\n\n    mWeatherSettings.reserve(10);\n    // These distant land fog factor and offset values are the defaults MGE XE provides. Should be\n    // provided by settings somewhere?\n    addWeather(\"Clear\", 1.0f, 0.0f); // 0\n    addWeather(\"Cloudy\", 0.9f, 0.0f); // 1\n    addWeather(\"Foggy\", 0.2f, 30.0f); // 2\n    addWeather(\"Overcast\", 0.7f, 0.0f); // 3\n    addWeather(\"Rain\", 0.5f, 10.0f); // 4\n    addWeather(\"Thunderstorm\", 0.5f, 20.0f); // 5\n    addWeather(\"Ashstorm\", 0.2f, 50.0f, \"meshes\\\\ashcloud.nif\"); // 6\n    addWeather(\"Blight\", 0.2f, 60.0f, \"meshes\\\\blightcloud.nif\"); // 7\n    addWeather(\"Snow\", 0.5f, 40.0f, \"meshes\\\\snow.nif\"); // 8\n    addWeather(\"Blizzard\", 0.16f, 70.0f, \"meshes\\\\blizzard.nif\"); // 9\n\n    Store<ESM::Region>::iterator it = store.get<ESM::Region>().begin();\n    for(; it != store.get<ESM::Region>().end(); ++it)\n    {\n        std::string regionID = Misc::StringUtils::lowerCase(it->mId);\n        mRegions.insert(std::make_pair(regionID, RegionWeather(*it)));\n    }\n\n    forceWeather(0);\n}\n\nWeatherManager::~WeatherManager()\n{\n    stopSounds();\n}\n\nvoid WeatherManager::changeWeather(const std::string& regionID, const unsigned int weatherID)\n{\n    // In Morrowind, this seems to have the following behavior, when applied to the current region:\n    // - When there is no transition in progress, start transitioning to the new weather.\n    // - If there is a transition in progress, queue up the transition and process it when the current one completes.\n    // - If there is a transition in progress, and a queued transition, overwrite the queued transition.\n    // - If multiple calls to ChangeWeather are made while paused (console up), only the last call will be used,\n    //   meaning that if there was no transition in progress, only the last ChangeWeather will be processed.\n    // If the region isn't current, Morrowind will store the new weather for the region in question.\n\n    if(weatherID < mWeatherSettings.size())\n    {\n        std::string lowerCaseRegionID = Misc::StringUtils::lowerCase(regionID);\n        std::map<std::string, RegionWeather>::iterator it = mRegions.find(lowerCaseRegionID);\n        if(it != mRegions.end())\n        {\n            it->second.setWeather(weatherID);\n            regionalWeatherChanged(it->first, it->second);\n        }\n    }\n}\n\nvoid WeatherManager::modRegion(const std::string& regionID, const std::vector<char>& chances)\n{\n    // Sets the region's probability for various weather patterns. Note that this appears to be saved permanently.\n    // In Morrowind, this seems to have the following behavior when applied to the current region:\n    // - If the region supports the current weather, no change in current weather occurs.\n    // - If the region no longer supports the current weather, and there is no transition in progress, begin to\n    //   transition to a new supported weather type.\n    // - If the region no longer supports the current weather, and there is a transition in progress, queue a\n    //   transition to a new supported weather type.\n\n    std::string lowerCaseRegionID = Misc::StringUtils::lowerCase(regionID);\n    std::map<std::string, RegionWeather>::iterator it = mRegions.find(lowerCaseRegionID);\n    if(it != mRegions.end())\n    {\n        it->second.setChances(chances);\n        regionalWeatherChanged(it->first, it->second);\n    }\n}\n\nvoid WeatherManager::playerTeleported(const std::string& playerRegion, bool isExterior)\n{\n    // If the player teleports to an outdoors cell in a new region (for instance, by travelling), the weather needs to\n    // be changed immediately, and any transitions for the previous region discarded.\n    {\n        std::map<std::string, RegionWeather>::iterator it = mRegions.find(playerRegion);\n        if(it != mRegions.end() && playerRegion != mCurrentRegion)\n        {\n            /*\n                Start of tes3mp addition\n\n                If we've moved to another region, set our weather creation ability to false;\n                the server will set it to true if it wants us creating weather here\n            */\n            setWeatherCreationState(false);\n            /*\n                End of tes3mp addition\n            */\n\n            mCurrentRegion = playerRegion;\n            forceWeather(it->second.getWeather());\n        }\n        /*\n            Start of tes3mp addition\n\n            There's no scenario where we want our weather creation ability to be true in\n            an interior, so set it to false\n        */\n        else if (!isExterior)\n            setWeatherCreationState(false);\n        /*\n            End of tes3mp addition\n        */\n    }\n}\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to set a specific weather state for a region from elsewhere\n    in the code\n*/\nvoid WeatherManager::setRegionWeather(const std::string& region, const int currentWeather, const int nextWeather,\n    const int queuedWeather, const float transitionFactor, bool force)\n{\n    bool isSameRegion = Misc::StringUtils::ciEqual(region, mCurrentRegion);\n\n    // Only ever force weather if we are in the correct region for it\n    if (isSameRegion)\n    {\n        if (force)\n        {\n            mCurrentWeather = currentWeather;\n            mNextWeather = nextWeather;\n            mQueuedWeather = queuedWeather;\n            mTransitionFactor = transitionFactor;\n        }\n        else\n        {\n            // Keep the queued weather in sync if everything else already is\n            if (mCurrentWeather == currentWeather && mNextWeather == nextWeather)\n            {\n                mQueuedWeather = queuedWeather;\n            }\n            // Start moving towards the next weather immediately if it's different from the one we have\n            else if (mNextWeather != nextWeather && nextWeather != -1)\n            {\n                changeWeather(region, nextWeather);\n            }\n            // Otherwise, if the current weather is different from the one we should have, move towards it\n            else if (mCurrentWeather != currentWeather)\n            {\n                changeWeather(region, currentWeather);\n            }\n        }\n    }\n    else\n    {\n        changeWeather(region, currentWeather);\n    }\n}\n/*\n    End of tes3mp addition\n*/\n\nfloat WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed)\n{\n    float targetSpeed = std::min(8.0f * mWeatherSettings[weatherId].mWindSpeed, 70.f);\n    if (currentSpeed == 0.f)\n        currentSpeed = targetSpeed;\n\n    float multiplier = mWeatherSettings[weatherId].mRainEffect.empty() ? 1.f : 0.5f;\n    float updatedSpeed = (Misc::Rng::rollClosedProbability() - 0.5f) * multiplier * targetSpeed + currentSpeed;\n\n    if (updatedSpeed > 0.5f * targetSpeed && updatedSpeed < 2.f * targetSpeed)\n        currentSpeed = updatedSpeed;\n\n    return currentSpeed;\n}\n\nvoid WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior)\n{\n    MWWorld::ConstPtr player = MWMechanics::getPlayer();\n\n    if(!paused || mFastForward)\n    {\n        // Add new transitions when either the player's current external region changes.\n        std::string playerRegion = Misc::StringUtils::lowerCase(player.getCell()->getCell()->mRegion);\n        if(updateWeatherTime() || updateWeatherRegion(playerRegion))\n        {\n            std::map<std::string, RegionWeather>::iterator it = mRegions.find(mCurrentRegion);\n            if(it != mRegions.end())\n            {\n                addWeatherTransition(it->second.getWeather());\n            }\n        }\n\n        updateWeatherTransitions(duration);\n    }\n\n    bool isDay = time.getHour() >= mSunriseTime && time.getHour() <= mTimeSettings.mNightStart;\n    if (isExterior && !isDay)\n        mNightDayMode = ExteriorNight;\n    else if (!isExterior && isDay && mWeatherSettings[mCurrentWeather].mGlareView >= 0.5f)\n        mNightDayMode = InteriorDay;\n    else\n        mNightDayMode = Default;\n\n    if(!isExterior)\n    {\n        mRendering.setSkyEnabled(false);\n        stopSounds();\n        mWindSpeed = 0.f;\n        mCurrentWindSpeed = 0.f;\n        mNextWindSpeed = 0.f;\n        return;\n    }\n\n    calculateWeatherResult(time.getHour(), duration, paused);\n\n    if (!paused)\n    {\n        mWindSpeed = mResult.mWindSpeed;\n        mCurrentWindSpeed = mResult.mCurrentWindSpeed;\n        mNextWindSpeed = mResult.mNextWindSpeed;\n    }\n\n    mIsStorm = mResult.mIsStorm;\n\n    // For some reason Ash Storm is not considered as a precipitation weather in game\n    mPrecipitation = !(mResult.mParticleEffect.empty() && mResult.mRainEffect.empty())\n                                    && mResult.mParticleEffect != \"meshes\\\\ashcloud.nif\";\n\n    if (mIsStorm)\n    {\n        osg::Vec3f stormDirection(0, 1, 0);\n        if (mResult.mParticleEffect == \"meshes\\\\ashcloud.nif\" || mResult.mParticleEffect == \"meshes\\\\blightcloud.nif\")\n        {\n            osg::Vec3f playerPos (MWMechanics::getPlayer().getRefData().getPosition().asVec3());\n            playerPos.z() = 0;\n            osg::Vec3f redMountainPos (25000, 70000, 0);\n            stormDirection = (playerPos - redMountainPos);\n            stormDirection.normalize();\n        }\n        mStormDirection = stormDirection;\n        mRendering.getSkyManager()->setStormDirection(mStormDirection);\n    }\n\n    // disable sun during night\n    if (time.getHour() >= mTimeSettings.mNightStart || time.getHour() <= mSunriseTime)\n        mRendering.getSkyManager()->sunDisable();\n    else\n        mRendering.getSkyManager()->sunEnable();\n\n    // Update the sun direction.  Run it east to west at a fixed angle from overhead.\n    // The sun's speed at day and night may differ, since mSunriseTime and mNightStart\n    // mark when the sun is level with the horizon.\n    {\n        // Shift times into a 24-hour window beginning at mSunriseTime...\n        float adjustedHour = time.getHour();\n        float adjustedNightStart = mTimeSettings.mNightStart;\n        if ( time.getHour() < mSunriseTime )\n            adjustedHour += 24.f;\n        if ( mTimeSettings.mNightStart < mSunriseTime )\n            adjustedNightStart += 24.f;\n\n        const bool is_night = adjustedHour >= adjustedNightStart;\n        const float dayDuration = adjustedNightStart - mSunriseTime;\n        const float nightDuration = 24.f - dayDuration;\n\n        double theta;\n        if ( !is_night )\n        {\n            theta = static_cast<float>(osg::PI) * (adjustedHour - mSunriseTime) / dayDuration;\n        }\n        else\n        {\n            theta = static_cast<float>(osg::PI) - static_cast<float>(osg::PI) * (adjustedHour - adjustedNightStart) / nightDuration;\n        }\n\n        osg::Vec3f final(\n            static_cast<float>(cos(theta)),\n            -0.268f, // approx tan( -15 degrees )\n            static_cast<float>(sin(theta)));\n        mRendering.setSunDirection( final * -1 );\n    }\n\n    float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings, \"Fog\");\n\n    float peakHour = mSunriseTime + (mTimeSettings.mNightStart - mSunriseTime) / 2;\n    float glareFade = 1.f;\n    if (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart)\n        glareFade = 0.f;\n    else if (time.getHour() < peakHour)\n        glareFade = 1.f - (peakHour - time.getHour()) / (peakHour - mSunriseTime);\n    else\n        glareFade = 1.f - (time.getHour() - peakHour) / (mTimeSettings.mNightStart - peakHour);\n\n    mRendering.getSkyManager()->setGlareTimeOfDayFade(glareFade);\n\n    mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time));\n    mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time));\n\n    mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor,\n                            mResult.mDLFogOffset/100.0f, mResult.mFogColor);\n    mRendering.setAmbientColour(mResult.mAmbientColor);\n    mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView * glareFade);\n\n    mRendering.getSkyManager()->setWeather(mResult);\n\n    // Play sounds\n    if (mPlayingSoundID != mResult.mAmbientLoopSoundID)\n    {\n        stopSounds();\n        if (!mResult.mAmbientLoopSoundID.empty())\n            mAmbientSound = MWBase::Environment::get().getSoundManager()->playSound(\n                mResult.mAmbientLoopSoundID, mResult.mAmbientSoundVolume, 1.0,\n                MWSound::Type::Sfx, MWSound::PlayMode::Loop\n            );\n        mPlayingSoundID = mResult.mAmbientLoopSoundID;\n    }\n    else if (mAmbientSound)\n        mAmbientSound->setVolume(mResult.mAmbientSoundVolume);\n}\n\nvoid WeatherManager::stopSounds()\n{\n    if (mAmbientSound)\n        MWBase::Environment::get().getSoundManager()->stopSound(mAmbientSound);\n    mAmbientSound = nullptr;\n    mPlayingSoundID.clear();\n}\n\nfloat WeatherManager::getWindSpeed() const\n{\n    return mWindSpeed;\n}\n\nbool WeatherManager::isInStorm() const\n{\n    return mIsStorm;\n}\n\nosg::Vec3f WeatherManager::getStormDirection() const\n{\n    return mStormDirection;\n}\n\nvoid WeatherManager::advanceTime(double hours, bool incremental)\n{\n    // In Morrowind, when the player sleeps/waits, serves jail time, travels, or trains, all weather transitions are\n    // immediately applied, regardless of whatever transition time might have been remaining.\n    mTimePassed += hours;\n    mFastForward = !incremental ? true : mFastForward;\n}\n\nunsigned int WeatherManager::getWeatherID() const\n{\n    return mCurrentWeather;\n}\n\nNightDayMode WeatherManager::getNightDayMode() const\n{\n    return mNightDayMode;\n}\n\nbool WeatherManager::useTorches(float hour) const\n{\n    bool isDark = hour < mSunriseTime || hour > mTimeSettings.mNightStart;\n\n    return isDark && !mPrecipitation;\n}\n\nvoid WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)\n{\n    ESM::WeatherState state;\n    state.mCurrentRegion = mCurrentRegion;\n    state.mTimePassed = mTimePassed;\n    state.mFastForward = mFastForward;\n    state.mWeatherUpdateTime = mWeatherUpdateTime;\n    state.mTransitionFactor = mTransitionFactor;\n    state.mCurrentWeather = mCurrentWeather;\n    state.mNextWeather = mNextWeather;\n    state.mQueuedWeather = mQueuedWeather;\n\n    std::map<std::string, RegionWeather>::iterator it = mRegions.begin();\n    for(; it != mRegions.end(); ++it)\n    {\n        state.mRegions.insert(std::make_pair(it->first, it->second));\n    }\n\n    writer.startRecord(ESM::REC_WTHR);\n    state.save(writer);\n    writer.endRecord(ESM::REC_WTHR);\n}\n\nbool WeatherManager::readRecord(ESM::ESMReader& reader, uint32_t type)\n{\n    if(ESM::REC_WTHR == type)\n    {\n        static const int oldestCompatibleSaveFormat = 2;\n        if(reader.getFormat() < oldestCompatibleSaveFormat)\n        {\n            // Weather state isn't really all that important, so to preserve older save games, we'll just discard the\n            // older weather records, rather than fail to handle the record.\n            reader.skipRecord();\n        }\n        else\n        {\n            ESM::WeatherState state;\n            state.load(reader);\n\n            mCurrentRegion.swap(state.mCurrentRegion);\n            mTimePassed = state.mTimePassed;\n            mFastForward = state.mFastForward;\n            mWeatherUpdateTime = state.mWeatherUpdateTime;\n            mTransitionFactor = state.mTransitionFactor;\n            mCurrentWeather = state.mCurrentWeather;\n            mNextWeather = state.mNextWeather;\n            mQueuedWeather = state.mQueuedWeather;\n\n            mRegions.clear();\n            importRegions();\n\n            for(std::map<std::string, ESM::RegionWeatherState>::iterator it = state.mRegions.begin(); it != state.mRegions.end(); ++it)\n            {\n                std::map<std::string, RegionWeather>::iterator found = mRegions.find(it->first);\n                if (found != mRegions.end())\n                {\n                    found->second = RegionWeather(it->second);\n                }\n            }\n        }\n\n        return true;\n    }\n\n    return false;\n}\n\nvoid WeatherManager::clear()\n{\n    stopSounds();\n\n    mCurrentRegion = \"\";\n    mTimePassed = 0.0f;\n    mWeatherUpdateTime = 0.0f;\n    forceWeather(0);\n    mRegions.clear();\n    importRegions();\n}\n\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to check whether the local WeatherManager has the\n    ability to create weather changes\n*/\nbool WeatherManager::getWeatherCreationState()\n{\n    return mWeatherCreationState;\n}\n/*\n    End of tes3mp addition\n*/\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to enable and disable the local WeatherManager's ability\n    to create weather changes\n*/\nvoid WeatherManager::setWeatherCreationState(bool state)\n{\n    mWeatherCreationState = state;\n}\n/*\n    End of tes3mp addition\n*/\n\n/*\n    Start of tes3mp addition\n\n    Make it possible to send the current weather in a WorldWeather packet\n    when requested from elsewhere in the code\n*/\nvoid WeatherManager::sendWeather()\n{\n    mwmp::Worldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();\n    worldstate->sendWeather(mCurrentRegion, mCurrentWeather, mNextWeather, mQueuedWeather, mTransitionFactor);\n}\n/*\n    End of tes3mp addition\n*/\n\ninline void WeatherManager::addWeather(const std::string& name,\n                                       float dlFactor, float dlOffset,\n                                       const std::string& particleEffect)\n{\n    static const float fStromWindSpeed = mStore.get<ESM::GameSetting>().find(\"fStromWindSpeed\")->mValue.getFloat();\n\n    Weather weather(name, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect);\n\n    mWeatherSettings.push_back(weather);\n}\n\ninline void WeatherManager::importRegions()\n{\n    for(const ESM::Region& region : mStore.get<ESM::Region>())\n    {\n        std::string regionID = Misc::StringUtils::lowerCase(region.mId);\n        mRegions.insert(std::make_pair(regionID, RegionWeather(region)));\n    }\n}\n\ninline void WeatherManager::regionalWeatherChanged(const std::string& regionID, RegionWeather& region)\n{\n    // If the region is current, then add a weather transition for it.\n    MWWorld::ConstPtr player = MWMechanics::getPlayer();\n    if(player.isInCell())\n    {\n        if(Misc::StringUtils::ciEqual(regionID, mCurrentRegion))\n        {\n            addWeatherTransition(region.getWeather());\n        }\n    }\n}\n\ninline bool WeatherManager::updateWeatherTime()\n{\n    /*\n        Start of tes3mp change (major)\n\n        Avoid creating any weather changes on this client unless approved by the server\n    */\n    if (!mWeatherCreationState)\n        return false;\n    /*\n        End of tes3mp change (major)\n    */\n\n    mWeatherUpdateTime -= mTimePassed;\n\n    mTimePassed = 0.0f;\n    if(mWeatherUpdateTime <= 0.0f)\n    {\n        // Expire all regional weather, so that any call to getWeather() will return a new weather ID.\n        std::map<std::string, RegionWeather>::iterator it = mRegions.begin();\n        for(; it != mRegions.end(); ++it)\n        {\n            it->second.setWeather(invalidWeatherID);\n        }\n\n        mWeatherUpdateTime += mHoursBetweenWeatherChanges;\n\n        return true;\n    }\n\n    return false;\n}\n\ninline bool WeatherManager::updateWeatherRegion(const std::string& playerRegion)\n{\n    if(!playerRegion.empty() && playerRegion != mCurrentRegion)\n    {\n        mCurrentRegion = playerRegion;\n\n        /*\n            Start of tes3mp addition\n\n            If we've moved to another region, set our weather creation ability to false;\n            the server will set it to true if it wants us creating weather here\n        */\n        setWeatherCreationState(false);\n        /*\n            End of tes3mp addition\n        */\n\n        return true;\n    }\n\n    return false;\n}\n\ninline void WeatherManager::updateWeatherTransitions(const float elapsedRealSeconds)\n{\n    /*\n        Start of tes3mp addition\n\n        Track whether an ID_WORLD_WEATHER packet should be sent or not\n    */\n    bool shouldSendPacket = false;\n    /*\n        End of tes3mp addition\n    */\n\n    // When a player chooses to train, wait, or serves jail time, any transitions will be fast forwarded to the last\n    // weather type set, regardless of the remaining transition time.\n    if(!mFastForward && inTransition())\n    {\n        const float delta = mWeatherSettings[mNextWeather].transitionDelta();\n        mTransitionFactor -= elapsedRealSeconds * delta;\n        if(mTransitionFactor <= 0.0f)\n        {\n            mCurrentWeather = mNextWeather;\n            mNextWeather = mQueuedWeather;\n            mQueuedWeather = invalidWeatherID;\n\n            // We may have begun processing the queued transition, so we need to apply the remaining time towards it.\n            if(inTransition())\n            {\n                const float newDelta = mWeatherSettings[mNextWeather].transitionDelta();\n                const float remainingSeconds = -(mTransitionFactor / delta);\n                mTransitionFactor = 1.0f - (remainingSeconds * newDelta);\n            }\n            else\n            {\n                mTransitionFactor = 0.0f;\n            }\n\n            /*\n                Start of tes3mp addition\n\n                The weather is changing, so decide to send an ID_WORLD_WEATHER packet\n            */\n            shouldSendPacket = true;\n            /*\n                End of tes3mp addition\n            */\n        }\n    }\n    else\n    {\n        /*\n            Start of tes3mp addition\n\n            If the weather is changing, decide to send an ID_WORLD_WEATHER packet\n        */\n        if (mQueuedWeather != invalidWeatherID || mNextWeather != invalidWeatherID)\n            shouldSendPacket = true;\n        /*\n            End of tes3mp addition\n        */\n\n        if(mQueuedWeather != invalidWeatherID)\n        {\n            mCurrentWeather = mQueuedWeather;\n        }\n        else if(mNextWeather != invalidWeatherID)\n        {\n            mCurrentWeather = mNextWeather;\n        }\n\n        mNextWeather = invalidWeatherID;\n        mQueuedWeather = invalidWeatherID;\n        mFastForward = false;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Send an ID_WORLD_WEATHER packet every time the weather changes here, but only\n        if we are allowed to create weather changes on this client\n    */\n    if (shouldSendPacket && mWeatherCreationState && !mCurrentRegion.empty())\n    {\n        sendWeather();\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n\ninline void WeatherManager::forceWeather(const int weatherID)\n{\n    mTransitionFactor = 0.0f;\n    mCurrentWeather = weatherID;\n    mNextWeather = invalidWeatherID;\n    mQueuedWeather = invalidWeatherID;\n\n    /*\n        Start of tes3mp addition\n\n        Send an ID_WORLD_WEATHER packet every time the weather changes here, but only\n        if we are allowed to create weather changes on this client\n    */\n    if (!mCurrentRegion.empty() && mWeatherCreationState)\n    {\n        sendWeather();\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n\ninline bool WeatherManager::inTransition()\n{\n    return mNextWeather != invalidWeatherID;\n}\n\ninline void WeatherManager::addWeatherTransition(const int weatherID)\n{\n    // In order to work like ChangeWeather expects, this method begins transitioning to the new weather immediately if\n    // no transition is in progress, otherwise it queues it to be transitioned.\n\n    assert(weatherID >= 0 && static_cast<size_t>(weatherID) < mWeatherSettings.size());\n\n    if(!inTransition() && (weatherID != mCurrentWeather))\n    {\n        mNextWeather = weatherID;\n        mTransitionFactor = 1.0f;\n    }\n    else if(inTransition() && (weatherID != mNextWeather))\n    {\n        mQueuedWeather = weatherID;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Send an ID_WORLD_WEATHER packet every time the weather changes here, but only\n        if we are allowed to create weather changes on this client\n    */\n    if (mWeatherCreationState)\n    {\n        sendWeather();\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n\ninline void WeatherManager::calculateWeatherResult(const float gameHour,\n                                                   const float elapsedSeconds,\n                                                   const bool isPaused)\n{\n    float flash = 0.0f;\n    if(!inTransition())\n    {\n        calculateResult(mCurrentWeather, gameHour);\n        flash = mWeatherSettings[mCurrentWeather].calculateThunder(1.0f, elapsedSeconds, isPaused);\n    }\n    else\n    {\n        calculateTransitionResult(1 - mTransitionFactor, gameHour);\n        float currentFlash = mWeatherSettings[mCurrentWeather].calculateThunder(mTransitionFactor,\n                                                                                elapsedSeconds,\n                                                                                isPaused);\n        float nextFlash = mWeatherSettings[mNextWeather].calculateThunder(1 - mTransitionFactor,\n                                                                          elapsedSeconds,\n                                                                          isPaused);\n        flash = currentFlash + nextFlash;\n    }\n    osg::Vec4f flashColor(flash, flash, flash, 0.0f);\n\n    mResult.mFogColor += flashColor;\n    mResult.mAmbientColor += flashColor;\n    mResult.mSunColor += flashColor;\n}\n\ninline void WeatherManager::calculateResult(const int weatherID, const float gameHour)\n{\n    const Weather& current = mWeatherSettings[weatherID];\n\n    mResult.mCloudTexture = current.mCloudTexture;\n    mResult.mCloudBlendFactor = 0;\n    mResult.mNextWindSpeed = 0;\n    mResult.mWindSpeed = mResult.mCurrentWindSpeed = calculateWindSpeed(weatherID, mWindSpeed);\n    mResult.mBaseWindSpeed = mWeatherSettings[weatherID].mWindSpeed;\n\n    mResult.mCloudSpeed = current.mCloudSpeed;\n    mResult.mGlareView = current.mGlareView;\n    mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;\n    mResult.mAmbientSoundVolume = 1.f;\n    mResult.mPrecipitationAlpha = 1.f;\n\n    mResult.mIsStorm = current.mIsStorm;\n\n    mResult.mRainSpeed = current.mRainSpeed;\n    mResult.mRainEntranceSpeed = current.mRainEntranceSpeed;\n    mResult.mRainDiameter = current.mRainDiameter;\n    mResult.mRainMinHeight = current.mRainMinHeight;\n    mResult.mRainMaxHeight = current.mRainMaxHeight;\n    mResult.mRainMaxRaindrops = current.mRainMaxRaindrops;\n\n    mResult.mParticleEffect = current.mParticleEffect;\n    mResult.mRainEffect = current.mRainEffect;\n\n    mResult.mNight = (gameHour < mSunriseTime || gameHour > mTimeSettings.mNightStart + mTimeSettings.mStarsPostSunsetStart - mTimeSettings.mStarsFadingDuration);\n\n    mResult.mFogDepth = current.mLandFogDepth.getValue(gameHour, mTimeSettings, \"Fog\");\n    mResult.mFogColor = current.mFogColor.getValue(gameHour, mTimeSettings, \"Fog\");\n    mResult.mAmbientColor = current.mAmbientColor.getValue(gameHour, mTimeSettings, \"Ambient\");\n    mResult.mSunColor = current.mSunColor.getValue(gameHour, mTimeSettings, \"Sun\");\n    mResult.mSkyColor = current.mSkyColor.getValue(gameHour, mTimeSettings, \"Sky\");\n    mResult.mNightFade = mNightFade.getValue(gameHour, mTimeSettings, \"Stars\");\n    mResult.mDLFogFactor = current.mDL.FogFactor;\n    mResult.mDLFogOffset = current.mDL.FogOffset;\n\n    WeatherSetting setting = mTimeSettings.getSetting(\"Sun\");\n    float preSunsetTime = setting.mPreSunsetTime;\n\n    if (gameHour >= mTimeSettings.mDayEnd - preSunsetTime)\n    {\n        float factor = 1.f;\n        if (preSunsetTime > 0)\n            factor = (gameHour - (mTimeSettings.mDayEnd - preSunsetTime)) / preSunsetTime;\n        factor = std::min(1.f, factor);\n        mResult.mSunDiscColor = lerp(osg::Vec4f(1,1,1,1), current.mSunDiscSunsetColor, factor);\n        // The SunDiscSunsetColor in the INI isn't exactly the resulting color on screen, most likely because\n        // MW applied the color to the ambient term as well. After the ambient and emissive terms are added together, the fixed pipeline\n        // would then clamp the total lighting to (1,1,1). A noticeable change in color tone can be observed when only one of the color components gets clamped.\n        // Unfortunately that means we can't use the INI color as is, have to replicate the above nonsense.\n        mResult.mSunDiscColor = mResult.mSunDiscColor + osg::componentMultiply(mResult.mSunDiscColor, mResult.mAmbientColor);\n        for (int i=0; i<3; ++i)\n            mResult.mSunDiscColor[i] = std::min(1.f, mResult.mSunDiscColor[i]);\n    }\n    else\n        mResult.mSunDiscColor = osg::Vec4f(1,1,1,1);\n\n    if (gameHour >= mTimeSettings.mDayEnd)\n    {\n        // sunset\n        float fade = std::min(1.f, (gameHour - mTimeSettings.mDayEnd) / (mTimeSettings.mNightStart - mTimeSettings.mDayEnd));\n        fade = fade*fade;\n        mResult.mSunDiscColor.a() = 1.f - fade;\n    }\n    else if (gameHour >= mTimeSettings.mNightEnd && gameHour <= mTimeSettings.mNightEnd + mSunriseDuration / 2.f)\n    {\n        // sunrise\n        mResult.mSunDiscColor.a() = gameHour - mTimeSettings.mNightEnd;\n    }\n    else\n        mResult.mSunDiscColor.a() = 1;\n\n}\n\ninline void WeatherManager::calculateTransitionResult(const float factor, const float gameHour)\n{\n    calculateResult(mCurrentWeather, gameHour);\n    const MWRender::WeatherResult current = mResult;\n    calculateResult(mNextWeather, gameHour);\n    const MWRender::WeatherResult other = mResult;\n\n    mResult.mCloudTexture = current.mCloudTexture;\n    mResult.mNextCloudTexture = other.mCloudTexture;\n    mResult.mCloudBlendFactor = mWeatherSettings[mNextWeather].cloudBlendFactor(factor);\n\n    mResult.mFogColor = lerp(current.mFogColor, other.mFogColor, factor);\n    mResult.mSunColor = lerp(current.mSunColor, other.mSunColor, factor);\n    mResult.mSkyColor = lerp(current.mSkyColor, other.mSkyColor, factor);\n\n    mResult.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor);\n    mResult.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor);\n    mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor);\n    mResult.mDLFogFactor = lerp(current.mDLFogFactor, other.mDLFogFactor, factor);\n    mResult.mDLFogOffset = lerp(current.mDLFogOffset, other.mDLFogOffset, factor);\n\n    mResult.mCurrentWindSpeed = calculateWindSpeed(mCurrentWeather, mCurrentWindSpeed);\n    mResult.mNextWindSpeed = calculateWindSpeed(mNextWeather, mNextWindSpeed);\n    mResult.mBaseWindSpeed = lerp(current.mBaseWindSpeed, other.mBaseWindSpeed, factor);\n\n    mResult.mWindSpeed = lerp(mResult.mCurrentWindSpeed, mResult.mNextWindSpeed, factor);\n    mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor);\n    mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor);\n    mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor);\n\n    mResult.mNight = current.mNight;\n\n    float threshold = mWeatherSettings[mNextWeather].mRainThreshold;\n    if (threshold <= 0)\n        threshold = 0.5f;\n\n    if(factor < threshold)\n    {\n        mResult.mIsStorm = current.mIsStorm;\n        mResult.mParticleEffect = current.mParticleEffect;\n        mResult.mRainEffect = current.mRainEffect;\n        mResult.mRainSpeed = current.mRainSpeed;\n        mResult.mRainEntranceSpeed = current.mRainEntranceSpeed;\n        mResult.mAmbientSoundVolume = 1 - factor / threshold;\n        mResult.mPrecipitationAlpha = mResult.mAmbientSoundVolume;\n        mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;\n        mResult.mRainDiameter = current.mRainDiameter;\n        mResult.mRainMinHeight = current.mRainMinHeight;\n        mResult.mRainMaxHeight = current.mRainMaxHeight;\n        mResult.mRainMaxRaindrops = current.mRainMaxRaindrops;\n    }\n    else\n    {\n        mResult.mIsStorm = other.mIsStorm;\n        mResult.mParticleEffect = other.mParticleEffect;\n        mResult.mRainEffect = other.mRainEffect;\n        mResult.mRainSpeed = other.mRainSpeed;\n        mResult.mRainEntranceSpeed = other.mRainEntranceSpeed;\n        mResult.mAmbientSoundVolume = (factor - threshold) / (1 - threshold);\n        mResult.mPrecipitationAlpha = mResult.mAmbientSoundVolume;\n        mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID;\n\n        mResult.mRainDiameter = other.mRainDiameter;\n        mResult.mRainMinHeight = other.mRainMinHeight;\n        mResult.mRainMaxHeight = other.mRainMaxHeight;\n        mResult.mRainMaxRaindrops = other.mRainMaxRaindrops;\n    }\n}\n\n"
  },
  {
    "path": "apps/openmw/mwworld/weather.hpp",
    "content": "#ifndef GAME_MWWORLD_WEATHER_H\n#define GAME_MWWORLD_WEATHER_H\n\n#include <stdint.h>\n#include <string>\n#include <map>\n\n#include <osg/Vec4f>\n\n#include <components/fallback/fallback.hpp>\n\n#include \"../mwbase/soundmanager.hpp\"\n\n#include \"../mwrender/sky.hpp\"\n\nnamespace ESM\n{\n    struct Region;\n    struct RegionWeatherState;\n    class ESMWriter;\n    class ESMReader;\n}\n\nnamespace MWRender\n{\n    class RenderingManager;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace Fallback\n{\n    class Map;\n}\n\nnamespace MWWorld\n{\n    class TimeStamp;\n\n    enum NightDayMode\n    {\n        Default = 0,\n        ExteriorNight = 1,\n        InteriorDay = 2\n    };\n\n    struct WeatherSetting\n    {\n        float mPreSunriseTime;\n        float mPostSunriseTime;\n        float mPreSunsetTime;\n        float mPostSunsetTime;\n    };\n\n    struct TimeOfDaySettings\n    {\n        float mNightStart;\n        float mNightEnd;\n        float mDayStart;\n        float mDayEnd;\n\n        std::map<std::string, WeatherSetting> mSunriseTransitions;\n\n        float mStarsPostSunsetStart;\n        float mStarsPreSunriseFinish;\n        float mStarsFadingDuration;\n\n        WeatherSetting getSetting(const std::string& type) const\n        {\n            std::map<std::string, WeatherSetting>::const_iterator it = mSunriseTransitions.find(type);\n            if (it != mSunriseTransitions.end())\n            {\n                return it->second;\n            }\n            else\n            {\n                return { 1.f, 1.f, 1.f, 1.f };\n            }\n        }\n\n        void addSetting(const std::string& type)\n        {\n            WeatherSetting setting = {\n                Fallback::Map::getFloat(\"Weather_\" + type + \"_Pre-Sunrise_Time\"),\n                Fallback::Map::getFloat(\"Weather_\" + type + \"_Post-Sunrise_Time\"),\n                Fallback::Map::getFloat(\"Weather_\" + type + \"_Pre-Sunset_Time\"),\n                Fallback::Map::getFloat(\"Weather_\" + type + \"_Post-Sunset_Time\")\n            };\n\n            mSunriseTransitions[type] = setting;\n        }\n    };\n\n    /// Interpolates between 4 data points (sunrise, day, sunset, night) based on the time of day.\n    /// The template value could be a floating point number, or a color.\n    template <typename T>\n    class TimeOfDayInterpolator\n    {\n    public:\n        TimeOfDayInterpolator(const T& sunrise, const T& day, const T& sunset, const T& night)\n            : mSunriseValue(sunrise), mDayValue(day), mSunsetValue(sunset), mNightValue(night)\n        {\n        }\n\n        T getValue (const float gameHour, const TimeOfDaySettings& timeSettings, const std::string& prefix) const;\n\n    private:\n        T mSunriseValue, mDayValue, mSunsetValue, mNightValue;\n    };\n\n    /// Defines a single weather setting (according to INI)\n    class Weather\n    {\n    public:\n        Weather(const std::string& name,\n                float stormWindSpeed,\n                float rainSpeed,\n                float dlFactor,\n                float dlOffset,\n                const std::string& particleEffect);\n\n        std::string mCloudTexture;\n\n        // Sky (atmosphere) color\n        TimeOfDayInterpolator<osg::Vec4f> mSkyColor;\n        // Fog color\n        TimeOfDayInterpolator<osg::Vec4f> mFogColor;\n        // Ambient lighting color\n        TimeOfDayInterpolator<osg::Vec4f> mAmbientColor;\n        // Sun (directional) lighting color\n        TimeOfDayInterpolator<osg::Vec4f> mSunColor;\n\n        // Fog depth/density\n        TimeOfDayInterpolator<float> mLandFogDepth;\n\n        // Color modulation for the sun itself during sunset\n        osg::Vec4f mSunDiscSunsetColor;\n\n        // Used by scripts to animate signs, etc based on the wind (GetWindSpeed)\n        float mWindSpeed;\n\n        // Cloud animation speed multiplier\n        float mCloudSpeed;\n\n        // Value between 0 and 1, defines the strength of the sun glare effect.\n        // Also appears to modify how visible the sun, moons, and stars are for various weather effects.\n        float mGlareView;\n\n        // Fog factor and offset used with distant land rendering.\n        struct {\n            float FogFactor;\n            float FogOffset;\n        } mDL;\n\n        // Sound effect\n        // This is used for Blight, Ashstorm and Blizzard (Bloodmoon)\n        std::string mAmbientLoopSoundID;\n\n        // Is this an ash storm / blight storm? If so, the following will happen:\n        // - The particles and clouds will be oriented so they appear to come from the Red Mountain.\n        // - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation)\n        // - Slower movement when walking against the storm (fStromWalkMult)\n        bool mIsStorm;\n\n        // How fast does rain travel down?\n        // In Morrowind.ini this is set globally, but we may want to change it per weather later.\n        float mRainSpeed;\n\n        // How often does a new rain mesh spawn?\n        float mRainEntranceSpeed;\n\n        // Maximum count of rain particles\n        int mRainMaxRaindrops;\n\n        // Radius of rain effect\n        float mRainDiameter;\n\n        // Transition threshold to spawn rain\n        float mRainThreshold;\n\n        // Height of rain particles spawn\n        float mRainMinHeight;\n        float mRainMaxHeight;\n\n        std::string mParticleEffect;\n\n        std::string mRainEffect;\n\n        // Note: For Weather Blight, there is a \"Disease Chance\" (=0.1) setting. But according to MWSFD this feature\n        // is broken in the vanilla game and was disabled.\n\n        float transitionDelta() const;\n        float cloudBlendFactor(const float transitionRatio) const;\n\n        float calculateThunder(const float transitionRatio, const float elapsedSeconds, const bool isPaused);\n\n    private:\n        float mTransitionDelta;\n        float mCloudsMaximumPercent;\n\n        // Note: In MW, only thunderstorms support these attributes, but in the interest of making weather more\n        // flexible, these settings are imported for all weather types. Only thunderstorms will normally have any\n        // non-zero values.\n        float mThunderFrequency;\n        float mThunderThreshold;\n        std::string mThunderSoundID[4];\n        float mFlashDecrement;\n\n        float mFlashBrightness;\n\n        void flashDecrement(const float elapsedSeconds);\n        float thunderChance(const float transitionRatio, const float elapsedSeconds) const;\n        void lightningAndThunder(void);\n    };\n\n    /// A class for storing a region's weather.\n    class RegionWeather\n    {\n    public:\n        explicit RegionWeather(const ESM::Region& region);\n        explicit RegionWeather(const ESM::RegionWeatherState& state);\n\n        operator ESM::RegionWeatherState() const;\n\n        void setChances(const std::vector<char>& chances);\n\n        void setWeather(int weatherID);\n\n        int getWeather();\n\n    private:\n        int mWeather;\n        std::vector<char> mChances;\n\n        void chooseNewWeather();\n    };\n\n    /// A class that acts as a model for the moons.\n    class MoonModel\n    {\n    public:\n        MoonModel(const std::string& name);\n\n        MWRender::MoonState calculateState(const TimeStamp& gameTime) const;\n\n    private:\n        float mFadeInStart;\n        float mFadeInFinish;\n        float mFadeOutStart;\n        float mFadeOutFinish;\n        float mAxisOffset;\n        float mSpeed;\n        float mDailyIncrement;\n        float mFadeStartAngle;\n        float mFadeEndAngle;\n        float mMoonShadowEarlyFadeAngle;\n\n        float angle(const TimeStamp& gameTime) const;\n        float moonRiseHour(unsigned int daysPassed) const;\n        float rotation(float hours) const;\n        MWRender::MoonState::Phase phase(const TimeStamp& gameTime) const;\n        float shadowBlend(float angle) const;\n        float hourlyAlpha(float gameHour) const;\n        float earlyMoonShadowAlpha(float angle) const;\n    };\n\n    /// Interface for weather settings\n    class WeatherManager\n    {\n    public:\n        // Have to pass fallback and Store, can't use singleton since World isn't fully constructed yet at the time\n        WeatherManager(MWRender::RenderingManager& rendering, MWWorld::ESMStore& store);\n        ~WeatherManager();\n\n        /**\n         * Change the weather in the specified region\n         * @param region that should be changed\n         * @param ID of the weather setting to shift to\n         */\n        void changeWeather(const std::string& regionID, const unsigned int weatherID);\n        void modRegion(const std::string& regionID, const std::vector<char>& chances);\n        void playerTeleported(const std::string& playerRegion, bool isExterior);\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to set a specific weather state for a region from elsewhere\n            in the code\n        */\n        void setRegionWeather(const std::string& region, const int currentWeather, const int nextWeather,\n            const int queuedWeather, const float transitionFactor, bool force);\n        /*\n            End of tes3mp addition\n        */\n\n        /**\n         * Per-frame update\n         * @param duration\n         * @param paused\n         */\n        void update(float duration, bool paused, const TimeStamp& time, bool isExterior);\n\n        void stopSounds();\n\n        float getWindSpeed() const;\n        NightDayMode getNightDayMode() const;\n\n        /// Are we in an ash or blight storm?\n        bool isInStorm() const;\n\n        osg::Vec3f getStormDirection() const;\n\n        void advanceTime(double hours, bool incremental);\n\n        unsigned int getWeatherID() const;\n\n        bool useTorches(float hour) const;\n\n        void write(ESM::ESMWriter& writer, Loading::Listener& progress);\n\n        bool readRecord(ESM::ESMReader& reader, uint32_t type);\n\n        void clear();\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to check whether the local WeatherManager has the\n            ability to create weather changes\n        */\n        bool getWeatherCreationState();\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to enable and disable the local WeatherManager's ability\n            to create weather changes\n        */\n        void setWeatherCreationState(bool state);\n        /*\n            End of tes3mp addition\n        */\n\n        /*\n            Start of tes3mp addition\n\n            Make it possible to send the current weather in a WorldWeather packet\n            when requested from elsewhere in the code\n        */\n        void sendWeather();\n        /*\n            End of tes3mp addition\n        */\n\n    private:\n        MWWorld::ESMStore& mStore;\n        MWRender::RenderingManager& mRendering;\n        float mSunriseTime;\n        float mSunsetTime;\n        float mSunriseDuration;\n        float mSunsetDuration;\n        float mSunPreSunsetTime;\n\n        TimeOfDaySettings mTimeSettings;\n\n        // fading of night skydome\n        TimeOfDayInterpolator<float> mNightFade;\n\n        float mHoursBetweenWeatherChanges;\n        float mRainSpeed;\n\n        // underwater fog not really related to weather, but we handle it here because it's convenient\n        TimeOfDayInterpolator<float> mUnderwaterFog;\n\n        std::vector<Weather> mWeatherSettings;\n        MoonModel mMasser;\n        MoonModel mSecunda;\n\n        float mWindSpeed;\n        float mCurrentWindSpeed;\n        float mNextWindSpeed;\n        bool mIsStorm;\n        bool mPrecipitation;\n        osg::Vec3f mStormDirection;\n\n        std::string mCurrentRegion;\n        float mTimePassed;\n        bool mFastForward;\n        float mWeatherUpdateTime;\n        float mTransitionFactor;\n        NightDayMode mNightDayMode;\n        int mCurrentWeather;\n        int mNextWeather;\n        int mQueuedWeather;\n        std::map<std::string, RegionWeather> mRegions;\n        MWRender::WeatherResult mResult;\n\n        MWBase::Sound *mAmbientSound;\n        std::string mPlayingSoundID;\n\n        /*\n            Start of tes3mp addition\n\n            Track whether the local WeatherManager should be creating any weather changes\n            by itself; when set to false, only weather changes sent by the server are used\n        */\n        bool mWeatherCreationState = false;\n        /*\n            End of tes3mp addition\n        */\n\n        void addWeather(const std::string& name,\n                        float dlFactor, float dlOffset,\n                        const std::string& particleEffect = \"\");\n\n        void importRegions();\n\n        void regionalWeatherChanged(const std::string& regionID, RegionWeather& region);\n        bool updateWeatherTime();\n        bool updateWeatherRegion(const std::string& playerRegion);\n        void updateWeatherTransitions(const float elapsedRealSeconds);\n        void forceWeather(const int weatherID);\n\n        bool inTransition();\n        void addWeatherTransition(const int weatherID);\n\n        void calculateWeatherResult(const float gameHour, const float elapsedSeconds, const bool isPaused);\n        void calculateResult(const int weatherID, const float gameHour);\n        void calculateTransitionResult(const float factor, const float gameHour);\n        float calculateWindSpeed(int weatherId, float currentSpeed);\n    };\n}\n\n#endif // GAME_MWWORLD_WEATHER_H\n"
  },
  {
    "path": "apps/openmw/mwworld/worldimp.cpp",
    "content": "#include \"worldimp.hpp\"\n\n#include <stdio.h>\n\n#include <osg/Group>\n#include <osg/ComputeBoundsVisitor>\n#include <osg/Timer>\n\n#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"../mwmp/Main.hpp\"\n#include \"../mwmp/Networking.hpp\"\n#include \"../mwmp/LocalPlayer.hpp\"\n#include \"../mwmp/PlayerList.hpp\"\n#include \"../mwmp/DedicatedPlayer.hpp\"\n#include \"../mwmp/LocalActor.hpp\"\n#include \"../mwmp/DedicatedActor.hpp\"\n#include \"../mwmp/ObjectList.hpp\"\n#include \"../mwmp/RecordHelper.hpp\"\n#include \"../mwmp/CellController.hpp\"\n#include \"../mwmp/MechanicsHelper.hpp\"\n/*\n    End of tes3mp addition\n*/\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/cellid.hpp>\n#include <components/esm/cellref.hpp>\n\n#include <components/misc/constants.hpp>\n#include <components/misc/resourcehelpers.hpp>\n#include <components/misc/rng.hpp>\n#include <components/misc/convert.hpp>\n\n#include <components/files/collections.hpp>\n\n#include <components/resource/bulletshape.hpp>\n#include <components/resource/resourcesystem.hpp>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n#include <components/detournavigator/debug.hpp>\n#include <components/detournavigator/navigatorimpl.hpp>\n#include <components/detournavigator/navigatorstub.hpp>\n#include <components/detournavigator/recastglobalallocator.hpp>\n\n#include \"../mwbase/environment.hpp\"\n#include \"../mwbase/soundmanager.hpp\"\n#include \"../mwbase/mechanicsmanager.hpp\"\n#include \"../mwbase/windowmanager.hpp\"\n#include \"../mwbase/scriptmanager.hpp\"\n\n#include \"../mwmechanics/creaturestats.hpp\"\n#include \"../mwmechanics/npcstats.hpp\"\n#include \"../mwmechanics/spellcasting.hpp\"\n#include \"../mwmechanics/levelledlist.hpp\"\n#include \"../mwmechanics/combat.hpp\"\n#include \"../mwmechanics/aiavoiddoor.hpp\" //Used to tell actors to avoid doors\n#include \"../mwmechanics/summoning.hpp\"\n\n#include \"../mwrender/animation.hpp\"\n#include \"../mwrender/npcanimation.hpp\"\n#include \"../mwrender/renderingmanager.hpp\"\n#include \"../mwrender/camera.hpp\"\n#include \"../mwrender/vismask.hpp\"\n\n#include \"../mwscript/globalscripts.hpp\"\n\n#include \"../mwclass/door.hpp\"\n\n#include \"../mwphysics/physicssystem.hpp\"\n#include \"../mwphysics/actor.hpp\"\n#include \"../mwphysics/collisiontype.hpp\"\n#include \"../mwphysics/object.hpp\"\n#include \"../mwphysics/constants.hpp\"\n\n#include \"datetimemanager.hpp\"\n#include \"player.hpp\"\n#include \"manualref.hpp\"\n#include \"cellstore.hpp\"\n#include \"containerstore.hpp\"\n#include \"inventorystore.hpp\"\n#include \"actionteleport.hpp\"\n#include \"projectilemanager.hpp\"\n#include \"weather.hpp\"\n\n#include \"contentloader.hpp\"\n#include \"esmloader.hpp\"\n\nnamespace\n{\n\n// Wraps a value to (-PI, PI]\nvoid wrap(float& rad)\n{\n    const float pi = static_cast<float>(osg::PI);\n    if (rad>0)\n        rad = std::fmod(rad+pi, 2.0f*pi)-pi;\n    else\n        rad = std::fmod(rad-pi, 2.0f*pi)+pi;\n}\n\n}\n\nnamespace MWWorld\n{\n    struct GameContentLoader : public ContentLoader\n    {\n        GameContentLoader(Loading::Listener& listener)\n          : ContentLoader(listener)\n        {\n        }\n\n        bool addLoader(const std::string& extension, ContentLoader* loader)\n        {\n            return mLoaders.insert(std::make_pair(extension, loader)).second;\n        }\n\n        void load(const boost::filesystem::path& filepath, int& index) override\n        {\n            LoadersContainer::iterator it(mLoaders.find(Misc::StringUtils::lowerCase(filepath.extension().string())));\n            if (it != mLoaders.end())\n            {\n                it->second->load(filepath, index);\n            }\n            else\n            {\n              std::string msg(\"Cannot load file: \");\n              msg += filepath.string();\n              throw std::runtime_error(msg.c_str());\n            }\n        }\n\n        private:\n          typedef std::map<std::string, ContentLoader*> LoadersContainer;\n          LoadersContainer mLoaders;\n    };\n\n    void World::adjustSky()\n    {\n        if (mSky && (isCellExterior() || isCellQuasiExterior()))\n        {\n            updateSkyDate();\n            mRendering->setSkyEnabled(true);\n        }\n        else\n            mRendering->setSkyEnabled(false);\n    }\n\n    World::World (\n        osgViewer::Viewer* viewer,\n        osg::ref_ptr<osg::Group> rootNode,\n        Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,\n        const Files::Collections& fileCollections,\n        const std::vector<std::string>& contentFiles,\n        const std::vector<std::string>& groundcoverFiles,\n        ToUTF8::Utf8Encoder* encoder, int activationDistanceOverride,\n        const std::string& startCell, const std::string& startupScript,\n        const std::string& resourcePath, const std::string& userDataPath)\n    : mResourceSystem(resourceSystem), mLocalScripts (mStore),\n      mCells (mStore, mEsm), mSky (true),\n      mGodMode(false), mScriptsEnabled(true), mDiscardMovements(true), mContentFiles (contentFiles),\n      mUserDataPath(userDataPath), mShouldUpdateNavigator(false),\n      mActivationDistanceOverride (activationDistanceOverride),\n      mStartCell(startCell), mDistanceToFacedObject(-1.f), mTeleportEnabled(true),\n      mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),\n      mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)\n    {\n        mEsm.resize(contentFiles.size() + groundcoverFiles.size());\n        Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();\n        listener->loadingOn();\n\n        GameContentLoader gameContentLoader(*listener);\n        EsmLoader esmLoader(mStore, mEsm, encoder, *listener);\n\n        gameContentLoader.addLoader(\".esm\", &esmLoader);\n        gameContentLoader.addLoader(\".esp\", &esmLoader);\n        gameContentLoader.addLoader(\".omwgame\", &esmLoader);\n        gameContentLoader.addLoader(\".omwaddon\", &esmLoader);\n        gameContentLoader.addLoader(\".project\", &esmLoader);\n\n        loadContentFiles(fileCollections, contentFiles, groundcoverFiles, gameContentLoader);\n\n        listener->loadingOff();\n\n        // insert records that may not be present in all versions of MW\n        if (mEsm[0].getFormat() == 0)\n            ensureNeededRecords();\n\n        mCurrentDate.reset(new DateTimeManager());\n\n        fillGlobalVariables();\n\n        mStore.setUp(true);\n        mStore.movePlayerRecord();\n\n        mSwimHeightScale = mStore.get<ESM::GameSetting>().find(\"fSwimHeightScale\")->mValue.getFloat();\n\n        mPhysics.reset(new MWPhysics::PhysicsSystem(resourceSystem, rootNode));\n\n        if (auto navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager())\n        {\n            navigatorSettings->mMaxClimb = MWPhysics::sStepSizeUp;\n            navigatorSettings->mMaxSlope = MWPhysics::sMaxSlope;\n            navigatorSettings->mSwimHeightScale = mSwimHeightScale;\n            DetourNavigator::RecastGlobalAllocator::init();\n            mNavigator.reset(new DetourNavigator::NavigatorImpl(*navigatorSettings));\n        }\n        else\n        {\n            mNavigator.reset(new DetourNavigator::NavigatorStub());\n        }\n\n        mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, resourcePath, *mNavigator));\n        mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering.get(), mPhysics.get()));\n        mRendering->preloadCommonAssets();\n\n        mWeatherManager.reset(new MWWorld::WeatherManager(*mRendering, mStore));\n\n        mWorldScene.reset(new Scene(*mRendering.get(), mPhysics.get(), *mNavigator));\n    }\n\n    void World::fillGlobalVariables()\n    {\n        mGlobalVariables.fill (mStore);\n        mCurrentDate->setup(mGlobalVariables);\n    }\n\n    void World::startNewGame (bool bypass)\n    {\n        mGoToJail = false;\n        mLevitationEnabled = true;\n        mTeleportEnabled = true;\n\n        mGodMode = false;\n        mScriptsEnabled = true;\n        mSky = true;\n\n        // Rebuild player\n        setupPlayer();\n\n        renderPlayer();\n        mRendering->getCamera()->reset();\n\n        // we don't want old weather to persist on a new game\n        // Note that if reset later, the initial ChangeWeather that the chargen script calls will be lost.\n        mWeatherManager.reset();\n        mWeatherManager.reset(new MWWorld::WeatherManager(*mRendering.get(), mStore));\n\n        if (!bypass)\n        {\n            // set new game mark\n            mGlobalVariables[\"chargenstate\"].setInteger (1);\n        }\n        else\n            mGlobalVariables[\"chargenstate\"].setInteger (-1);\n\n        if (bypass && !mStartCell.empty())\n        {\n            ESM::Position pos;\n            if (findExteriorPosition (mStartCell, pos))\n            {\n                changeToExteriorCell (pos, true);\n                adjustPosition(getPlayerPtr(), false);\n            }\n            else\n            {\n                findInteriorPosition (mStartCell, pos);\n                changeToInteriorCell (mStartCell, pos, true);\n            }\n        }\n        else\n        {\n            for (int i=0; i<5; ++i)\n                MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();\n            if (!getPlayerPtr().isInCell())\n            {\n                ESM::Position pos;\n\n                /*\n                    Start of tes3mp change (major)\n\n                    Spawn at 0, -7 by default\n                */\n                const int cellSize = Constants::CellSizeInUnits;\n                pos.pos[0] = cellSize / 2;\n                pos.pos[1] = cellSize * -7 + cellSize / 2;\n                pos.pos[2] = 0;\n                pos.rot[0] = 0;\n                pos.rot[1] = 0;\n                pos.rot[2] = 0;\n                mWorldScene->changeToExteriorCell(pos, true);\n                /*\n                    End of tes3mp change (major)\n                */\n            }\n        }\n\n        if (!bypass)\n        {\n            const std::string& video = Fallback::Map::getString(\"Movies_New_Game\");\n            if (!video.empty())\n                MWBase::Environment::get().getWindowManager()->playVideo(video, true);\n        }\n\n        // enable collision\n        if (!mPhysics->toggleCollisionMode())\n            mPhysics->toggleCollisionMode();\n\n        MWBase::Environment::get().getWindowManager()->updatePlayer();\n        mCurrentDate->setup(mGlobalVariables);\n    }\n\n    void World::clear()\n    {\n        mWeatherManager->clear();\n        mRendering->clear();\n        mProjectileManager->clear();\n        mLocalScripts.clear();\n\n        mWorldScene->clear();\n\n        mStore.clearDynamic();\n\n        if (mPlayer)\n        {\n            mPlayer->clear();\n            mPlayer->setCell(nullptr);\n            mPlayer->getPlayer().getRefData() = RefData();\n            mPlayer->set(mStore.get<ESM::NPC>().find (\"player\"));\n        }\n\n        mCells.clear();\n\n        mDoorStates.clear();\n\n        mGoToJail = false;\n        mTeleportEnabled = true;\n        mLevitationEnabled = true;\n        mPlayerTraveling = false;\n        mPlayerInJail = false;\n\n        fillGlobalVariables();\n    }\n\n    int World::countSavedGameRecords() const\n    {\n        return\n            mCells.countSavedGameRecords()\n            +mStore.countSavedGameRecords()\n            +mGlobalVariables.countSavedGameRecords()\n            +mProjectileManager->countSavedGameRecords()\n            +1 // player record\n            +1 // weather record\n            +1 // actorId counter\n            +1 // levitation/teleport enabled state\n            +1; // camera\n    }\n\n    int World::countSavedGameCells() const\n    {\n        return mCells.countSavedGameRecords();\n    }\n\n    void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const\n    {\n        // Active cells could have a dirty fog of war, sync it to the CellStore first\n        for (CellStore* cellstore : mWorldScene->getActiveCells())\n        {\n            MWBase::Environment::get().getWindowManager()->writeFog(cellstore);\n        }\n\n        MWMechanics::CreatureStats::writeActorIdCounter(writer);\n\n        mStore.write (writer, progress); // dynamic Store must be written (and read) before Cells, so that\n                                         // references to custom made records will be recognized\n        mPlayer->write (writer, progress);\n        mCells.write (writer, progress);\n        mGlobalVariables.write (writer, progress);\n        mWeatherManager->write (writer, progress);\n        mProjectileManager->write (writer, progress);\n\n        writer.startRecord(ESM::REC_ENAB);\n        writer.writeHNT(\"TELE\", mTeleportEnabled);\n        writer.writeHNT(\"LEVT\", mLevitationEnabled);\n        writer.endRecord(ESM::REC_ENAB);\n\n        writer.startRecord(ESM::REC_CAM_);\n        writer.writeHNT(\"FIRS\", isFirstPerson());\n        writer.endRecord(ESM::REC_CAM_);\n    }\n\n    void World::readRecord (ESM::ESMReader& reader, uint32_t type,\n        const std::map<int, int>& contentFileMap)\n    {\n        switch (type)\n        {\n            case ESM::REC_ACTC:\n                MWMechanics::CreatureStats::readActorIdCounter(reader);\n                return;\n            case ESM::REC_ENAB:\n                reader.getHNT(mTeleportEnabled, \"TELE\");\n                reader.getHNT(mLevitationEnabled, \"LEVT\");\n                return;\n            case ESM::REC_PLAY:\n                mStore.checkPlayer();\n                mPlayer->readRecord(reader, type);\n                if (getPlayerPtr().isInCell())\n                {\n                    if (getPlayerPtr().getCell()->isExterior())\n                        mWorldScene->preloadTerrain(getPlayerPtr().getRefData().getPosition().asVec3());\n                    mWorldScene->preloadCell(getPlayerPtr().getCell(), true);\n                }\n                break;\n            default:\n                if (!mStore.readRecord (reader, type) &&\n                    !mGlobalVariables.readRecord (reader, type) &&\n                    !mWeatherManager->readRecord (reader, type) &&\n                    !mCells.readRecord (reader, type, contentFileMap)\n                     && !mProjectileManager->readRecord (reader, type)\n                        )\n                {\n                    throw std::runtime_error (\"unknown record in saved game\");\n                }\n                break;\n        }\n    }\n\n    void World::ensureNeededRecords()\n    {\n        std::map<std::string, ESM::Variant> gmst;\n        // Companion (tribunal)\n        gmst[\"sCompanionShare\"] = ESM::Variant(\"Companion Share\");\n        gmst[\"sCompanionWarningMessage\"] = ESM::Variant(\"Warning message\");\n        gmst[\"sCompanionWarningButtonOne\"] = ESM::Variant(\"Button 1\");\n        gmst[\"sCompanionWarningButtonTwo\"] = ESM::Variant(\"Button 2\");\n        gmst[\"sProfitValue\"] = ESM::Variant(\"Profit Value\");\n        gmst[\"sTeleportDisabled\"] = ESM::Variant(\"Teleport disabled\");\n        gmst[\"sLevitateDisabled\"] = ESM::Variant(\"Levitate disabled\");\n\n        // Missing in unpatched MW 1.0\n        gmst[\"sDifficulty\"] = ESM::Variant(\"Difficulty\");\n        gmst[\"fDifficultyMult\"] = ESM::Variant(5.f);\n        gmst[\"sAuto_Run\"] = ESM::Variant(\"Auto Run\");\n        gmst[\"sServiceRefusal\"] = ESM::Variant(\"Service Refusal\");\n        gmst[\"sNeedOneSkill\"] = ESM::Variant(\"Need one skill\");\n        gmst[\"sNeedTwoSkills\"] = ESM::Variant(\"Need two skills\");\n        gmst[\"sEasy\"] = ESM::Variant(\"Easy\");\n        gmst[\"sHard\"] = ESM::Variant(\"Hard\");\n        gmst[\"sDeleteNote\"] = ESM::Variant(\"Delete Note\");\n        gmst[\"sEditNote\"] = ESM::Variant(\"Edit Note\");\n        gmst[\"sAdmireSuccess\"] = ESM::Variant(\"Admire Success\");\n        gmst[\"sAdmireFail\"] = ESM::Variant(\"Admire Fail\");\n        gmst[\"sIntimidateSuccess\"] = ESM::Variant(\"Intimidate Success\");\n        gmst[\"sIntimidateFail\"] = ESM::Variant(\"Intimidate Fail\");\n        gmst[\"sTauntSuccess\"] = ESM::Variant(\"Taunt Success\");\n        gmst[\"sTauntFail\"] = ESM::Variant(\"Taunt Fail\");\n        gmst[\"sBribeSuccess\"] = ESM::Variant(\"Bribe Success\");\n        gmst[\"sBribeFail\"] = ESM::Variant(\"Bribe Fail\");\n        gmst[\"fNPCHealthBarTime\"] = ESM::Variant(5.f);\n        gmst[\"fNPCHealthBarFade\"] = ESM::Variant(1.f);\n        gmst[\"fFleeDistance\"] = ESM::Variant(3000.f);\n        gmst[\"sMaxSale\"] = ESM::Variant(\"Max Sale\");\n        gmst[\"sAnd\"] = ESM::Variant(\"and\");\n\n        // Werewolf (BM)\n        gmst[\"fWereWolfRunMult\"] = ESM::Variant(1.3f);\n        gmst[\"fWereWolfSilverWeaponDamageMult\"] = ESM::Variant(2.f);\n        gmst[\"iWerewolfFightMod\"] = ESM::Variant(100);\n        gmst[\"iWereWolfFleeMod\"] = ESM::Variant(100);\n        gmst[\"iWereWolfLevelToAttack\"] = ESM::Variant(20);\n        gmst[\"iWereWolfBounty\"] = ESM::Variant(1000);\n        gmst[\"fCombatDistanceWerewolfMod\"] = ESM::Variant(0.3f);\n\n        for (const auto &params : gmst)\n        {\n            if (!mStore.get<ESM::GameSetting>().search(params.first))\n            {\n                ESM::GameSetting record;\n                record.mId = params.first;\n                record.mValue = params.second;\n                mStore.insertStatic(record);\n            }\n        }\n\n        std::map<std::string, ESM::Variant> globals;\n        // vanilla Morrowind does not define dayspassed.\n        globals[\"dayspassed\"] = ESM::Variant(1); // but the addons start counting at 1 :(\n        globals[\"werewolfclawmult\"] = ESM::Variant(25.f);\n        globals[\"pcknownwerewolf\"] = ESM::Variant(0);\n\n        // following should exist in all versions of MW, but not necessarily in TCs\n        globals[\"gamehour\"] = ESM::Variant(0.f);\n        globals[\"timescale\"] = ESM::Variant(30.f);\n        globals[\"day\"] = ESM::Variant(1);\n        globals[\"month\"] = ESM::Variant(1);\n        globals[\"year\"] = ESM::Variant(1);\n        globals[\"pcrace\"] = ESM::Variant(0);\n        globals[\"pchascrimegold\"] = ESM::Variant(0);\n        globals[\"pchasgolddiscount\"] = ESM::Variant(0);\n        globals[\"crimegolddiscount\"] = ESM::Variant(0);\n        globals[\"crimegoldturnin\"] = ESM::Variant(0);\n        globals[\"pchasturnin\"] = ESM::Variant(0);\n\n        for (const auto &params : globals)\n        {\n            if (!mStore.get<ESM::Global>().search(params.first))\n            {\n                ESM::Global record;\n                record.mId = params.first;\n                record.mValue = params.second;\n                mStore.insertStatic(record);\n            }\n        }\n\n        std::map<std::string, std::string> statics;\n        // Total conversions from SureAI lack marker records\n        statics[\"divinemarker\"] = \"marker_divine.nif\";\n        statics[\"doormarker\"] = \"marker_arrow.nif\";\n        statics[\"northmarker\"] = \"marker_north.nif\";\n        statics[\"templemarker\"] = \"marker_temple.nif\";\n        statics[\"travelmarker\"] = \"marker_travel.nif\";\n\n        for (const auto &params : statics)\n        {\n            if (!mStore.get<ESM::Static>().search(params.first))\n            {\n                ESM::Static record;\n                record.mId = params.first;\n                record.mModel = params.second;\n                mStore.insertStatic(record);\n            }\n        }\n\n        std::map<std::string, std::string> doors;\n        doors[\"prisonmarker\"] = \"marker_prison.nif\";\n\n        for (const auto &params : doors)\n        {\n            if (!mStore.get<ESM::Door>().search(params.first))\n            {\n                ESM::Door record;\n                record.mId = params.first;\n                record.mModel = params.second;\n                mStore.insertStatic(record);\n            }\n        }\n    }\n\n    World::~World()\n    {\n        // Must be cleared before mRendering is destroyed\n        mProjectileManager->clear();\n    }\n\n    const ESM::Cell *World::getExterior (const std::string& cellName) const\n    {\n        // first try named cells\n        const ESM::Cell *cell = mStore.get<ESM::Cell>().searchExtByName (cellName);\n        if (cell)\n            return cell;\n\n        // didn't work -> now check for regions\n        for (const ESM::Region &region : mStore.get<ESM::Region>())\n        {\n            if (Misc::StringUtils::ciEqual(cellName, region.mName))\n            {\n                return mStore.get<ESM::Cell>().searchExtByRegion(region.mId);\n            }\n        }\n\n        return nullptr;\n    }\n\n    CellStore *World::getExterior (int x, int y)\n    {\n        return mCells.getExterior (x, y);\n    }\n\n    CellStore *World::getInterior (const std::string& name)\n    {\n        return mCells.getInterior (name);\n    }\n\n    CellStore *World::getCell (const ESM::CellId& id)\n    {\n        if (id.mPaged)\n            return getExterior (id.mIndex.mX, id.mIndex.mY);\n        else\n            return getInterior (id.mWorldspace);\n    }\n\n    void World::testExteriorCells()\n    {\n        mWorldScene->testExteriorCells();\n    }\n\n    void World::testInteriorCells()\n    {\n        mWorldScene->testInteriorCells();\n    }\n\n    void World::useDeathCamera()\n    {\n        if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() )\n        {\n            mRendering->getCamera()->togglePreviewMode(false);\n            mRendering->getCamera()->toggleVanityMode(false);\n        }\n        if(mRendering->getCamera()->isFirstPerson())\n            mRendering->getCamera()->toggleViewMode(true);\n    }\n\n    MWWorld::Player& World::getPlayer()\n    {\n        return *mPlayer;\n    }\n\n    const MWWorld::ESMStore& World::getStore() const\n    {\n        return mStore;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to get the World's ESMStore as a non-const\n    */\n    MWWorld::ESMStore& World::getModifiableStore()\n    {\n        return mStore;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    std::vector<ESM::ESMReader>& World::getEsmReader()\n    {\n        return mEsm;\n    }\n\n    LocalScripts& World::getLocalScripts()\n    {\n        return mLocalScripts;\n    }\n\n    bool World::hasCellChanged() const\n    {\n        return mWorldScene->hasCellChanged();\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check whether global variables exist and to create\n        new ones\n    */\n    bool World::hasGlobal(const std::string& name)\n    {\n        return mGlobalVariables.hasRecord(name);\n    }\n\n    void World::createGlobal(const std::string& name, ESM::VarType varType)\n    {\n        ESM::Global global;\n        global.mId = name;\n        global.mValue.setType(varType);\n        mGlobalVariables.addRecord(global);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void World::setGlobalInt (const std::string& name, int value)\n    {\n        bool dateUpdated = mCurrentDate->updateGlobalInt(name, value);\n        if (dateUpdated)\n            updateSkyDate();\n\n        mGlobalVariables[name].setInteger (value);\n    }\n\n    void World::setGlobalFloat (const std::string& name, float value)\n    {\n        bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value);\n        if (dateUpdated)\n            updateSkyDate();\n\n        mGlobalVariables[name].setFloat(value);\n    }\n\n    int World::getGlobalInt (const std::string& name) const\n    {\n        return mGlobalVariables[name].getInteger();\n    }\n\n    float World::getGlobalFloat (const std::string& name) const\n    {\n        return mGlobalVariables[name].getFloat();\n    }\n\n    char World::getGlobalVariableType (const std::string& name) const\n    {\n        return mGlobalVariables.getType (name);\n    }\n\n    std::string World::getMonthName (int month) const\n    {\n        return mCurrentDate->getMonthName(month);\n    }\n\n    std::string World::getCellName (const MWWorld::CellStore *cell) const\n    {\n        if (!cell)\n            cell = mWorldScene->getCurrentCell();\n        return getCellName(cell->getCell());\n    }\n\n    std::string World::getCellName(const ESM::Cell* cell) const\n    {\n        if (cell)\n        {\n            if (!cell->isExterior() || !cell->mName.empty())\n                return cell->mName;\n\n            if (const ESM::Region* region = mStore.get<ESM::Region>().search (cell->mRegion))\n                return region->mName;\n        }\n        return mStore.get<ESM::GameSetting>().find (\"sDefaultCellname\")->mValue.getString();\n    }\n\n    void World::removeRefScript (MWWorld::RefData *ref)\n    {\n        mLocalScripts.remove (ref);\n    }\n\n    Ptr World::searchPtr (const std::string& name, bool activeOnly, bool searchInContainers)\n    {\n        Ptr ret;\n        // the player is always in an active cell.\n        if (name==\"player\")\n        {\n            return mPlayer->getPlayer();\n        }\n\n        std::string lowerCaseName = Misc::StringUtils::lowerCase(name);\n\n        for (CellStore* cellstore : mWorldScene->getActiveCells())\n        {\n            // TODO: caching still doesn't work efficiently here (only works for the one CellStore that the reference is in)\n            Ptr ptr = mCells.getPtr (lowerCaseName, *cellstore, false);\n\n            if (!ptr.isEmpty())\n                return ptr;\n        }\n\n        if (!activeOnly)\n        {\n            ret = mCells.getPtr (lowerCaseName);\n            if (!ret.isEmpty())\n                return ret;\n        }\n\n        if (searchInContainers)\n        {\n            for (CellStore* cellstore : mWorldScene->getActiveCells())\n            {\n                Ptr ptr = cellstore->searchInContainer(lowerCaseName);\n                if (!ptr.isEmpty())\n                    return ptr;\n            }\n        }\n\n        Ptr ptr = mPlayer->getPlayer().getClass()\n            .getContainerStore(mPlayer->getPlayer()).search(lowerCaseName);\n\n        return ptr;\n    }\n\n    Ptr World::getPtr (const std::string& name, bool activeOnly)\n    {\n        Ptr ret = searchPtr(name, activeOnly);\n        if (!ret.isEmpty())\n            return ret;\n        std::string error = \"failed to find an instance of object '\" + name + \"'\";\n        if (activeOnly)\n            error += \" in active cells\";\n        throw std::runtime_error(error);\n    }\n\n    Ptr World::searchPtrViaActorId (int actorId)\n    {\n        // The player is not registered in any CellStore so must be checked manually\n        if (actorId == getPlayerPtr().getClass().getCreatureStats(getPlayerPtr()).getActorId())\n            return getPlayerPtr();\n        /*\n            Start of tes3mp addition\n\n            Make it possible to find dedicated players here as well\n        */\n        else\n        {\n            mwmp::DedicatedPlayer* dedicatedPlayer = mwmp::PlayerList::getPlayer(actorId);\n            if (dedicatedPlayer != nullptr)\n            {\n                return dedicatedPlayer->getPtr();\n            }\n        }\n        /*\n            End of tes3mp addition\n        */\n        // Now search cells\n        return mWorldScene->searchPtrViaActorId (actorId);\n    }\n\n    Ptr World::searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum)\n    {\n        return mCells.getPtr (id, refNum);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to find a Ptr in any active cell based on its refNum and mpNum\n    */\n    Ptr World::searchPtrViaUniqueIndex(int refNum, int mpNum)\n    {\n        for (Scene::CellStoreCollection::const_iterator iter(mWorldScene->getActiveCells().begin());\n            iter != mWorldScene->getActiveCells().end(); ++iter)\n        {\n            CellStore* cellStore = *iter;\n            \n            MWWorld::Ptr ptrFound = cellStore->searchExact(refNum, mpNum);\n\n            if (ptrFound)\n                return ptrFound;\n        }\n\n        return nullptr;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to update all Ptrs in active cells that have a certain refId\n    */\n    void World::updatePtrsWithRefId(std::string refId)\n    {\n        for (Scene::CellStoreCollection::const_iterator iter(mWorldScene->getActiveCells().begin());\n            iter != mWorldScene->getActiveCells().end(); ++iter)\n        {\n            CellStore* cellStore = *iter;\n\n            for (auto &mergedRef : cellStore->getMergedRefs())\n            {\n                if (Misc::StringUtils::ciEqual(refId, mergedRef->mRef.getRefId()))\n                {\n                    MWWorld::Ptr ptr(mergedRef, cellStore);\n\n                    const ESM::Position* position = &ptr.getRefData().getPosition();\n                    const unsigned int refNum = ptr.getCellRef().getRefNum().mIndex;\n                    const unsigned int mpNum = ptr.getCellRef().getMpNum();\n\n                    deleteObject(ptr);\n                    ptr.getCellRef().unsetRefNum();\n                    ptr.getCellRef().setMpNum(0);\n\n                    MWWorld::ManualRef* reference = new MWWorld::ManualRef(getStore(), refId, 1);\n                    MWWorld::Ptr newPtr = placeObject(reference->getPtr(), cellStore, *position);\n                    newPtr.getCellRef().setRefNum(refNum);\n                    newPtr.getCellRef().setMpNum(mpNum);\n\n                    // Update Ptrs for LocalActors and DedicatedActors\n                    if (newPtr.getClass().isActor())\n                    {\n                        if (mwmp::Main::get().getCellController()->isLocalActor(refNum, mpNum))\n                            mwmp::Main::get().getCellController()->getLocalActor(refNum, mpNum)->setPtr(newPtr);\n                        else if (mwmp::Main::get().getCellController()->isDedicatedActor(refNum, mpNum))\n                            mwmp::Main::get().getCellController()->getDedicatedActor(refNum, mpNum)->setPtr(newPtr);\n                    }\n                }\n            }\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    struct FindContainerVisitor\n    {\n        ConstPtr mContainedPtr;\n        Ptr mResult;\n\n        FindContainerVisitor(const ConstPtr& containedPtr) : mContainedPtr(containedPtr) {}\n\n        bool operator() (Ptr ptr)\n        {\n            if (mContainedPtr.getContainerStore() == &ptr.getClass().getContainerStore(ptr))\n            {\n                mResult = ptr;\n                return false;\n            }\n\n            return true;\n        }\n    };\n\n    Ptr World::findContainer(const ConstPtr& ptr)\n    {\n        if (ptr.isInCell())\n            return Ptr();\n\n        Ptr player = getPlayerPtr();\n        if (ptr.getContainerStore() == &player.getClass().getContainerStore(player))\n            return player;\n\n        for (CellStore* cellstore : mWorldScene->getActiveCells())\n        {\n            FindContainerVisitor visitor(ptr);\n            cellstore->forEachType<ESM::Container>(visitor);\n            if (visitor.mResult.isEmpty())\n                cellstore->forEachType<ESM::Creature>(visitor);\n            if (visitor.mResult.isEmpty())\n                cellstore->forEachType<ESM::NPC>(visitor);\n\n            if (!visitor.mResult.isEmpty())\n                return visitor.mResult;\n        }\n\n        return Ptr();\n    }\n\n    void World::addContainerScripts(const Ptr& reference, CellStore * cell)\n    {\n        if( reference.getTypeName()==typeid (ESM::Container).name() ||\n            reference.getTypeName()==typeid (ESM::NPC).name() ||\n            reference.getTypeName()==typeid (ESM::Creature).name())\n        {\n            MWWorld::ContainerStore& container = reference.getClass().getContainerStore(reference);\n            for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)\n            {\n                std::string script = it->getClass().getScript(*it);\n                if(script != \"\")\n                {\n                    MWWorld::Ptr item = *it;\n                    item.mCell = cell;\n                    mLocalScripts.add (script, item);\n                }\n            }\n        }\n    }\n\n    void World::enable (const Ptr& reference)\n    {\n        // enable is a no-op for items in containers\n        if (!reference.isInCell())\n            return;\n\n        if (!reference.getRefData().isEnabled())\n        {\n            reference.getRefData().enable();\n\n            if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount())\n                mWorldScene->addObjectToScene (reference);\n\n            if (reference.getCellRef().getRefNum().hasContentFile())\n            {\n                int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId()));\n                if (mRendering->pagingEnableObject(type, reference, true))\n                    mWorldScene->reloadTerrain();\n            }\n        }\n    }\n\n    void World::removeContainerScripts(const Ptr& reference)\n    {\n        if( reference.getTypeName()==typeid (ESM::Container).name() ||\n            reference.getTypeName()==typeid (ESM::NPC).name() ||\n            reference.getTypeName()==typeid (ESM::Creature).name())\n        {\n            MWWorld::ContainerStore& container = reference.getClass().getContainerStore(reference);\n            for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)\n            {\n                std::string script = it->getClass().getScript(*it);\n                if(script != \"\")\n                {\n                    MWWorld::Ptr item = *it;\n                    mLocalScripts.remove (item);\n                }\n            }\n        }\n    }\n\n    void World::disable (const Ptr& reference)\n    {\n        if (!reference.getRefData().isEnabled())\n            return;\n\n        // disable is a no-op for items in containers\n        if (!reference.isInCell())\n            return;\n\n        if (reference == getPlayerPtr())\n            throw std::runtime_error(\"can not disable player object\");\n\n        reference.getRefData().disable();\n\n        if (reference.getCellRef().getRefNum().hasContentFile())\n        {\n            int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId()));\n            if (mRendering->pagingEnableObject(type, reference, false))\n                mWorldScene->reloadTerrain();\n        }\n\n        if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount())\n            mWorldScene->removeObjectFromScene (reference);\n    }\n\n    void World::advanceTime (double hours, bool incremental)\n    {\n        if (!incremental)\n        {\n            // When we fast-forward time, we should recharge magic items\n            // in all loaded cells, using game world time\n            float duration = hours * 3600;\n            const float timeScaleFactor = getTimeScaleFactor();\n            if (timeScaleFactor != 0.0f)\n                duration /= timeScaleFactor;\n\n            rechargeItems(duration, false);\n        }\n\n        mWeatherManager->advanceTime (hours, incremental);\n        mCurrentDate->advanceTime(hours, mGlobalVariables);\n        updateSkyDate();\n\n        if (!incremental)\n        {\n            mRendering->notifyWorldSpaceChanged();\n            mProjectileManager->clear();\n            mDiscardMovements = true;\n        }\n    }\n\n\n    float World::getTimeScaleFactor() const\n\n    {\n        return mCurrentDate->getTimeScaleFactor();\n    }\n\n    TimeStamp World::getTimeStamp() const\n    {\n        return mCurrentDate->getTimeStamp();\n    }\n\n    ESM::EpochTimeStamp World::getEpochTimeStamp() const\n    {\n        return mCurrentDate->getEpochTimeStamp();\n    }\n\n    bool World::toggleSky()\n    {\n        mSky = !mSky;\n        mRendering->setSkyEnabled(mSky);\n        return mSky;\n    }\n\n    int World::getMasserPhase() const\n    {\n        return mRendering->skyGetMasserPhase();\n    }\n\n    int World::getSecundaPhase() const\n    {\n        return mRendering->skyGetSecundaPhase();\n    }\n\n    void World::setMoonColour (bool red)\n    {\n        mRendering->skySetMoonColour (red);\n    }\n\n    void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)\n    {\n        mPhysics->clearQueuedMovement();\n        mDiscardMovements = true;\n\n        if (changeEvent && mCurrentWorldSpace != cellName)\n        {\n            // changed worldspace\n            mProjectileManager->clear();\n            mRendering->notifyWorldSpaceChanged();\n\n            mCurrentWorldSpace = cellName;\n        }\n\n        removeContainerScripts(getPlayerPtr());\n        mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent);\n        addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());\n        mRendering->getCamera()->instantTransition();\n    }\n\n    void World::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)\n    {\n        mPhysics->clearQueuedMovement();\n        mDiscardMovements = true;\n\n        if (changeEvent && mCurrentWorldSpace != ESM::CellId::sDefaultWorldspace)\n        {\n            // changed worldspace\n            mProjectileManager->clear();\n            mRendering->notifyWorldSpaceChanged();\n        }\n        removeContainerScripts(getPlayerPtr());\n        mWorldScene->changeToExteriorCell(position, adjustPlayerPos, changeEvent);\n        addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());\n        mRendering->getCamera()->instantTransition();\n    }\n\n    void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)\n    {\n        if (!changeEvent)\n            mCurrentWorldSpace = cellId.mWorldspace;\n\n        if (cellId.mPaged)\n            changeToExteriorCell (position, adjustPlayerPos, changeEvent);\n        else\n            changeToInteriorCell (cellId.mWorldspace, position, adjustPlayerPos, changeEvent);\n\n        mCurrentDate->setup(mGlobalVariables);\n    }\n\n    void World::markCellAsUnchanged()\n    {\n        return mWorldScene->markCellAsUnchanged();\n    }\n\n    float World::getMaxActivationDistance ()\n    {\n        if (mActivationDistanceOverride >= 0)\n            return static_cast<float>(mActivationDistanceOverride);\n\n        static const int iMaxActivateDist = mStore.get<ESM::GameSetting>().find(\"iMaxActivateDist\")->mValue.getInteger();\n        return static_cast<float>(iMaxActivateDist);\n    }\n\n    MWWorld::Ptr World::getFacedObject()\n    {\n        MWWorld::Ptr facedObject;\n\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode() &&\n                MWBase::Environment::get().getWindowManager()->isConsoleMode())\n            facedObject = getFacedObject(getMaxActivationDistance() * 50, false);\n        else\n        {\n            float activationDistance = getActivationDistancePlusTelekinesis();\n\n            facedObject = getFacedObject(activationDistance, true);\n\n            if (!facedObject.isEmpty() && !facedObject.getClass().allowTelekinesis(facedObject)\n                && mDistanceToFacedObject > getMaxActivationDistance() && !MWBase::Environment::get().getWindowManager()->isGuiMode())\n                return nullptr;\n        }\n        return facedObject;\n    }\n\n   float World::getDistanceToFacedObject()\n   {\n        return mDistanceToFacedObject;\n   }\n\n    osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const\n    {\n        const MWRender::Animation *anim = mRendering->getAnimation(actor);\n        if(anim)\n        {\n            const osg::Node *node = anim->getNode(\"Head\");\n            if(!node) node = anim->getNode(\"Bip01 Head\");\n            if(node)\n            {\n                osg::NodePathList nodepaths = node->getParentalNodePaths();\n                if(!nodepaths.empty())\n                    return osg::computeLocalToWorld(nodepaths[0]);\n            }\n        }\n        return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());\n    }\n\n    std::pair<MWWorld::Ptr,osg::Vec3f> World::getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector<MWWorld::Ptr> &targets)\n    {\n        const ESM::Position &posdata = ptr.getRefData().getPosition();\n\n        osg::Quat rot = osg::Quat(posdata.rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0,0,-1));\n\n        osg::Vec3f halfExtents = mPhysics->getHalfExtents(ptr);\n\n        // the origin of hitbox is an actor's front, not center\n        distance += halfExtents.y();\n\n        // special cased for better aiming with the camera\n        // if we do not hit anything, will use the default approach as fallback\n        if (ptr == getPlayerPtr())\n        {\n            osg::Vec3f pos = getActorHeadTransform(ptr).getTrans();\n\n            std::pair<MWWorld::Ptr,osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);\n            if(!result.first.isEmpty())\n                return std::make_pair(result.first, result.second);\n        }\n\n        osg::Vec3f pos = ptr.getRefData().getPosition().asVec3();\n\n        // general case, compatible with all types of different creatures\n        // note: we intentionally do *not* use the collision box offset here, this is required to make\n        // some flying creatures work that have their collision box offset in the air\n        pos.z() += halfExtents.z();\n\n        std::pair<MWWorld::Ptr,osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);\n        if(result.first.isEmpty())\n            return std::make_pair(MWWorld::Ptr(), osg::Vec3f());\n\n        return std::make_pair(result.first, result.second);\n    }\n\n    void World::deleteObject (const Ptr& ptr)\n    {\n        if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == nullptr)\n        {\n            if (ptr == getPlayerPtr())\n                throw std::runtime_error(\"can not delete player object\");\n\n            ptr.getRefData().setCount(0);\n\n            if (ptr.isInCell()\n                && mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end()\n                && ptr.getRefData().isEnabled())\n            {\n                mWorldScene->removeObjectFromScene (ptr);\n                mLocalScripts.remove (ptr);\n                removeContainerScripts (ptr);\n            }\n        }\n    }\n\n    void World::undeleteObject(const Ptr& ptr)\n    {\n        if (!ptr.getCellRef().hasContentFile())\n            return;\n        if (ptr.getRefData().isDeleted())\n        {\n            ptr.getRefData().setCount(1);\n            if (mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end()\n                    && ptr.getRefData().isEnabled())\n            {\n                mWorldScene->addObjectToScene(ptr);\n                std::string script = ptr.getClass().getScript(ptr);\n                if (!script.empty())\n                    mLocalScripts.add(script, ptr);\n                addContainerScripts(ptr, ptr.getCell());\n            }\n        }\n    }\n\n    MWWorld::Ptr World::moveObject(const Ptr &ptr, CellStore* newCell, float x, float y, float z, bool movePhysics)\n    {\n        /*\n            Start of tes3mp addition\n\n            If we choose to deny this move because it's part of an unapproved cell change, we should also revert the Ptr back to its\n            original coordinates, so keep track of them\n        */\n        ESM::Position originalPos = ptr.getRefData().getPosition();\n        /*\n            End of tes3mp addition\n        */\n\n        ESM::Position pos = ptr.getRefData().getPosition();\n\n        pos.pos[0] = x;\n        pos.pos[1] = y;\n        pos.pos[2] = z;\n\n        ptr.getRefData().setPosition(pos);\n\n        osg::Vec3f vec(x, y, z);\n\n        CellStore *currCell = ptr.isInCell() ? ptr.getCell() : nullptr; // currCell == nullptr should only happen for player, during initial startup\n        bool isPlayer = ptr == mPlayer->getPlayer();\n        bool haveToMove = isPlayer || (currCell && mWorldScene->isCellActive(*currCell));\n        MWWorld::Ptr newPtr = ptr;\n\n        if (!isPlayer && !currCell)\n           throw std::runtime_error(\"Can not move actor \\\"\" + ptr.getCellRef().getRefId() + \"\\\" to another cell: current cell is nullptr\");\n\n        if (!newCell)\n           throw std::runtime_error(\"Can not move actor \\\"\" + ptr.getCellRef().getRefId() + \"\\\" to another cell: new cell is nullptr\");\n\n        if (currCell != newCell)\n        {\n            /*\n                Start of tes3mp addition\n\n                Check if a DedicatedPlayer or DedicatedActor's new Ptr cell is the same as their packet cell, and deny the Ptr's movement and\n                cell change if it is not\n            */\n            if (mwmp::PlayerList::isDedicatedPlayer(ptr) &&\n                !mwmp::Main::get().getCellController()->isSameCell(mwmp::PlayerList::getPlayer(ptr)->cell, *newCell->getCell()))\n            {\n                ptr.getRefData().setPosition(originalPos);\n                return ptr;\n            }\n            else if (mwmp::Main::get().getCellController()->isDedicatedActor(ptr) &&\n                !mwmp::Main::get().getCellController()->isSameCell(mwmp::Main::get().getCellController()->getDedicatedActor(ptr)->cell, *newCell->getCell()))\n            {\n                ptr.getRefData().setPosition(originalPos);\n                return ptr;\n            }\n            /*\n                End of tes3mp addition\n            */\n\n            removeContainerScripts(ptr);\n\n            if (isPlayer)\n            {\n                if (!newCell->isExterior())\n                {\n                    changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos, false);\n                    removeContainerScripts(getPlayerPtr());\n                }\n                else\n                {\n                    if (mWorldScene->isCellActive(*newCell))\n                        mWorldScene->changePlayerCell(newCell, pos, false);\n                    else\n                        mWorldScene->changeToExteriorCell(pos, false);\n                }\n                addContainerScripts (getPlayerPtr(), newCell);\n                newPtr = getPlayerPtr();\n            }\n            else\n            {\n                bool currCellActive = mWorldScene->isCellActive(*currCell);\n                bool newCellActive = mWorldScene->isCellActive(*newCell);\n                if (!currCellActive && newCellActive)\n                {\n                    newPtr = currCell->moveTo(ptr, newCell);\n                    mWorldScene->addObjectToScene(newPtr);\n\n                    std::string script = newPtr.getClass().getScript(newPtr);\n                    if (!script.empty())\n                    {\n                        mLocalScripts.add(script, newPtr);\n                    }\n                    addContainerScripts(newPtr, newCell);\n                }\n                else if (!newCellActive && currCellActive)\n                {\n                    mWorldScene->removeObjectFromScene(ptr);\n                    mLocalScripts.remove(ptr);\n                    removeContainerScripts (ptr);\n                    haveToMove = false;\n\n                    newPtr = currCell->moveTo(ptr, newCell);\n                    newPtr.getRefData().setBaseNode(nullptr);\n                }\n                else if (!currCellActive && !newCellActive)\n                    newPtr = currCell->moveTo(ptr, newCell);\n                else // both cells active\n                {\n                    newPtr = currCell->moveTo(ptr, newCell);\n\n                    mRendering->updatePtr(ptr, newPtr);\n                    MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr);\n                    mPhysics->updatePtr(ptr, newPtr);\n\n                    MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();\n                    mechMgr->updateCell(ptr, newPtr);\n\n                    std::string script =\n                        ptr.getClass().getScript(ptr);\n                    if (!script.empty())\n                    {\n                        mLocalScripts.remove(ptr);\n                        removeContainerScripts (ptr);\n                        mLocalScripts.add(script, newPtr);\n                        addContainerScripts (newPtr, newCell);\n                    }\n                }\n\n                /*\n                    Start of tes3mp addition\n\n                    Update the Ptrs of LocalActors, DedicatedPlayers and DedicatedActors\n                */\n                if (mwmp::Main::get().getCellController()->isLocalActor(ptr))\n                    mwmp::Main::get().getCellController()->getLocalActor(ptr)->setPtr(newPtr);\n                else if (mwmp::Main::get().getCellController()->isDedicatedActor(ptr))\n                    mwmp::Main::get().getCellController()->getDedicatedActor(ptr)->setPtr(newPtr);\n                else if (mwmp::PlayerList::isDedicatedPlayer(ptr))\n                    mwmp::PlayerList::getPlayer(ptr)->setPtr(newPtr);\n                /*\n                    End of tes3mp addition\n                */\n            }\n\n            MWBase::Environment::get().getWindowManager()->updateConsoleObjectPtr(ptr, newPtr);\n            MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);\n        }\n        if (haveToMove && newPtr.getRefData().getBaseNode())\n        {\n            mWorldScene->updateObjectPosition(newPtr, vec, movePhysics);\n            if (movePhysics)\n            {\n                if (const auto object = mPhysics->getObject(ptr))\n                    updateNavigatorObject(*object);\n            }\n        }\n\n        if (isPlayer)\n            mWorldScene->playerMoved(vec);\n        else\n        {\n            mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);\n            mWorldScene->removeFromPagedRefs(newPtr);\n        }\n\n        return newPtr;\n    }\n\n    MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive)\n    {\n        int cellX, cellY;\n        positionToIndex(x, y, cellX, cellY);\n\n        CellStore* cell = ptr.getCell();\n        CellStore* newCell = getExterior(cellX, cellY);\n        bool isCellActive = getPlayerPtr().isInCell() && getPlayerPtr().getCell()->isExterior() && mWorldScene->isCellActive(*newCell);\n\n        if (cell->isExterior() || (moveToActive && isCellActive && ptr.getClass().isActor()))\n            cell = newCell;\n\n        return moveObject(ptr, cell, x, y, z, movePhysics);\n    }\n\n    MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive, bool ignoreCollisions)\n    {\n        auto* actor = mPhysics->getActor(ptr);\n        osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;\n        if (actor)\n            actor->adjustPosition(vec, ignoreCollisions);\n        if (ptr.getClass().isActor())\n            return moveObject(ptr, newpos.x(), newpos.y(), newpos.z(), false, moveToActive && ptr != getPlayerPtr());\n        return moveObject(ptr, newpos.x(), newpos.y(), newpos.z());\n    }\n\n    void World::scaleObject (const Ptr& ptr, float scale)\n    {\n        if (mPhysics->getActor(ptr))\n            mNavigator->removeAgent(getPathfindingHalfExtents(ptr));\n\n        if (scale != ptr.getCellRef().getScale())\n        {\n            ptr.getCellRef().setScale(scale);\n            mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);\n            mWorldScene->removeFromPagedRefs(ptr);\n        }\n\n        if(ptr.getRefData().getBaseNode() != nullptr)\n            mWorldScene->updateObjectScale(ptr);\n\n        if (mPhysics->getActor(ptr))\n            mNavigator->addAgent(getPathfindingHalfExtents(ptr));\n        else if (const auto object = mPhysics->getObject(ptr))\n            updateNavigatorObject(*object);\n    }\n\n    void World::rotateObjectImp(const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags)\n    {\n        const float pi = static_cast<float>(osg::PI);\n\n        ESM::Position pos = ptr.getRefData().getPosition();\n        float *objRot = pos.rot;\n        if (flags & MWBase::RotationFlag_adjust)\n        {\n            objRot[0] += rot.x();\n            objRot[1] += rot.y();\n            objRot[2] += rot.z();\n        }\n        else\n        {\n            objRot[0] = rot.x();\n            objRot[1] = rot.y();\n            objRot[2] = rot.z();\n        }\n\n        if(ptr.getClass().isActor())\n        {\n            /* HACK? Actors shouldn't really be rotating around X (or Y), but\n             * currently it's done so for rotating the camera, which needs\n             * clamping.\n             */\n            const float half_pi = pi/2.f;\n\n            if(objRot[0] < -half_pi)     objRot[0] = -half_pi;\n            else if(objRot[0] > half_pi) objRot[0] =  half_pi;\n\n            wrap(objRot[1]);\n            wrap(objRot[2]);\n        }\n\n        ptr.getRefData().setPosition(pos);\n\n        mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);\n        mWorldScene->removeFromPagedRefs(ptr);\n\n        if(ptr.getRefData().getBaseNode() != nullptr)\n        {\n            const auto order = flags & MWBase::RotationFlag_inverseOrder\n                ? RotationOrder::inverse : RotationOrder::direct;\n            mWorldScene->updateObjectRotation(ptr, order);\n\n            if (const auto object = mPhysics->getObject(ptr))\n                updateNavigatorObject(*object);\n        }\n    }\n\n    void World::adjustPosition(const Ptr &ptr, bool force)\n    {\n        if (ptr.isEmpty())\n        {\n            Log(Debug::Warning) << \"Unable to adjust position for empty object\";\n            return;\n        }\n\n        osg::Vec3f pos (ptr.getRefData().getPosition().asVec3());\n\n        if(!ptr.getRefData().getBaseNode())\n        {\n            // will be adjusted when Ptr's cell becomes active\n            return;\n        }\n\n        if (!ptr.isInCell())\n        {\n            Log(Debug::Warning) << \"Unable to adjust position for object '\" << ptr.getCellRef().getRefId() << \"' - it has no cell\";\n            return;\n        }\n\n        const float terrainHeight = ptr.getCell()->isExterior() ? getTerrainHeightAt(pos) : -std::numeric_limits<float>::max();\n        pos.z() = std::max(pos.z(), terrainHeight) + 20; // place slightly above terrain. will snap down to ground with code below\n\n        // We still should trace down dead persistent actors - they do not use the \"swimdeath\" animation.\n        bool swims = ptr.getClass().isActor() && isSwimming(ptr) && !(ptr.getClass().isPersistent(ptr) && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished());\n        if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !swims && isActorCollisionEnabled(ptr)))\n        {\n            osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits);\n            pos.z() = std::min(pos.z(), traced.z());\n        }\n\n        moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z());\n    }\n\n    void World::fixPosition()\n    {\n        const MWWorld::Ptr actor = getPlayerPtr();\n        const float distance = 128.f;\n        ESM::Position esmPos = actor.getRefData().getPosition();\n        osg::Quat orientation(esmPos.rot[2], osg::Vec3f(0,0,-1));\n        osg::Vec3f pos (esmPos.asVec3());\n\n        int direction = 0;\n        int fallbackDirections[4] = {direction, (direction+3)%4, (direction+2)%4, (direction+1)%4};\n\n        osg::Vec3f targetPos = pos;\n        for (int i=0; i<4; ++i)\n        {\n            direction = fallbackDirections[i];\n            if (direction == 0) targetPos = pos + (orientation * osg::Vec3f(0,1,0)) * distance;\n            else if(direction == 1) targetPos = pos - (orientation * osg::Vec3f(0,1,0)) * distance;\n            else if(direction == 2) targetPos = pos - (orientation * osg::Vec3f(1,0,0)) * distance;\n            else if(direction == 3) targetPos = pos + (orientation * osg::Vec3f(1,0,0)) * distance;\n\n            // destination is free\n            if (!castRay(pos.x(), pos.y(), pos.z(), targetPos.x(), targetPos.y(), targetPos.z()))\n                break;\n        }\n\n        targetPos.z() += distance / 2.f; // move up a bit to get out from geometry, will snap down later\n        osg::Vec3f traced = mPhysics->traceDown(actor, targetPos, Constants::CellSizeInUnits);\n        if (traced != pos)\n        {\n            esmPos.pos[0] = traced.x();\n            esmPos.pos[1] = traced.y();\n            esmPos.pos[2] = traced.z();\n            MWWorld::ActionTeleport(actor.getCell()->isExterior() ? \"\" : actor.getCell()->getCell()->mName, esmPos, false).execute(actor);\n        }\n    }\n\n    void World::rotateObject (const Ptr& ptr, float x, float y, float z, MWBase::RotationFlags flags)\n    {\n        rotateObjectImp(ptr, osg::Vec3f(x, y, z), flags);\n    }\n\n    void World::rotateWorldObject (const Ptr& ptr, osg::Quat rotate)\n    {\n        if(ptr.getRefData().getBaseNode() != nullptr)\n        {\n            mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);\n            mWorldScene->removeFromPagedRefs(ptr);\n\n            mRendering->rotateObject(ptr, rotate);\n            mPhysics->updateRotation(ptr);\n\n            if (const auto object = mPhysics->getObject(ptr))\n                updateNavigatorObject(*object);\n        }\n    }\n\n    MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos)\n    {\n        return copyObjectToCell(ptr,cell,pos,ptr.getRefData().getCount(),false);\n    }\n\n    MWWorld::Ptr World::safePlaceObject(const ConstPtr &ptr, const ConstPtr &referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance)\n    {\n        ESM::Position ipos = referenceObject.getRefData().getPosition();\n        osg::Vec3f pos(ipos.asVec3());\n        osg::Quat orientation(ipos.rot[2], osg::Vec3f(0,0,-1));\n\n        int fallbackDirections[4] = {direction, (direction+3)%4, (direction+2)%4, (direction+1)%4};\n\n        osg::Vec3f spawnPoint = pos;\n\n        for (int i=0; i<4; ++i)\n        {\n            direction = fallbackDirections[i];\n            if (direction == 0) spawnPoint = pos + (orientation * osg::Vec3f(0,1,0)) * distance;\n            else if(direction == 1) spawnPoint = pos - (orientation * osg::Vec3f(0,1,0)) * distance;\n            else if(direction == 2) spawnPoint = pos - (orientation * osg::Vec3f(1,0,0)) * distance;\n            else if(direction == 3) spawnPoint = pos + (orientation * osg::Vec3f(1,0,0)) * distance;\n\n            if (!ptr.getClass().isActor())\n                break;\n\n            // check if spawn point is safe, fall back to another direction if not\n            spawnPoint.z() += 30; // move up a little to account for slopes, will snap down later\n\n            if (!castRay(spawnPoint.x(), spawnPoint.y(), spawnPoint.z(),\n                                                               pos.x(), pos.y(), pos.z() + 20))\n            {\n                // safe\n                break;\n            }\n        }\n        ipos.pos[0] = spawnPoint.x();\n        ipos.pos[1] = spawnPoint.y();\n        ipos.pos[2] = spawnPoint.z();\n\n        if (referenceObject.getClass().isActor())\n        {\n            ipos.rot[0] = 0;\n            ipos.rot[1] = 0;\n        }\n\n        MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false);\n        adjustPosition(placed, true); // snap to ground\n        return placed;\n    }\n\n    void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const\n    {\n        const int cellSize = Constants::CellSizeInUnits;\n\n        x = static_cast<float>(cellSize * cellX);\n        y = static_cast<float>(cellSize * cellY);\n\n        if (centre)\n        {\n            x += cellSize/2;\n            y += cellSize/2;\n        }\n    }\n\n    void World::positionToIndex (float x, float y, int &cellX, int &cellY) const\n    {\n        cellX = static_cast<int>(std::floor(x / Constants::CellSizeInUnits));\n        cellY = static_cast<int>(std::floor(y / Constants::CellSizeInUnits));\n    }\n\n    void World::queueMovement(const Ptr &ptr, const osg::Vec3f &velocity)\n    {\n        mPhysics->queueObjectMovement(ptr, velocity);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set the inertial force of a Ptr directly\n    */\n    void World::setInertialForce(const Ptr& ptr, const osg::Vec3f &force)\n    {\n        MWPhysics::Actor *actor = mPhysics->getActor(ptr);\n\n        if (actor != nullptr)\n        {\n            actor->setOnGround(false);\n            actor->setInertialForce(force);\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set whether a Ptr is on the ground or not, needed for proper\n        synchronization in multiplayer\n    */\n    void World::setOnGround(const Ptr& ptr, bool onGround)\n    {\n        MWPhysics::Actor* actor = mPhysics->getActor(ptr);\n\n        if (actor != nullptr)\n        {\n            actor->setOnGround(onGround);\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set the physics framerate from elsewhere\n    */\n    void World::setPhysicsFramerate(float physFramerate)\n    {\n        mPhysics->setPhysicsFramerate(physFramerate);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void World::updateAnimatedCollisionShape(const Ptr &ptr)\n    {\n        mPhysics->updateAnimatedCollisionShape(ptr);\n    }\n\n    void World::doPhysics(float duration, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)\n    {\n        mPhysics->stepSimulation();\n        processDoors(duration);\n\n        mProjectileManager->update(duration);\n\n        const auto& results = mPhysics->applyQueuedMovement(duration, mDiscardMovements, frameStart, frameNumber, stats);\n        mProjectileManager->processHits();\n        mDiscardMovements = false;\n\n        for(const auto& actor : results)\n        {\n            // Handle player last, in case a cell transition occurs\n            if(actor != getPlayerPtr())\n            {\n                auto* physactor = mPhysics->getActor(actor);\n                assert(physactor);\n                const auto position = physactor->getSimulationPosition();\n                moveObject(actor, position.x(), position.y(), position.z(), false, false);\n            }\n        }\n\n        const auto player = std::find(results.begin(), results.end(), getPlayerPtr());\n        if (player != results.end())\n        {\n            auto* physactor = mPhysics->getActor(*player);\n            assert(physactor);\n            const auto position = physactor->getSimulationPosition();\n            moveObject(*player, position.x(), position.y(), position.z(), false, false);\n        }\n    }\n\n    void World::updateNavigator()\n    {\n        mPhysics->forEachAnimatedObject([&] (const MWPhysics::Object* object) { updateNavigatorObject(*object); });\n\n        for (const auto& door : mDoorStates)\n            if (const auto object = mPhysics->getObject(door.first))\n                updateNavigatorObject(*object);\n\n        if (mShouldUpdateNavigator)\n        {\n            mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3());\n            mShouldUpdateNavigator = false;\n        }\n    }\n\n    void World::updateNavigatorObject(const MWPhysics::Object& object)\n    {\n        const DetourNavigator::ObjectShapes shapes(object.getShapeInstance());\n        mShouldUpdateNavigator = mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform())\n            || mShouldUpdateNavigator;\n    }\n\n    const MWPhysics::RayCastingInterface* World::getRayCasting() const\n    {\n        return mPhysics.get();\n    }\n\n    bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2)\n    {\n        int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door;\n        bool result = castRay(x1, y1, z1, x2, y2, z2, mask);\n        return result;\n    }\n\n    bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask)\n    {\n        osg::Vec3f a(x1,y1,z1);\n        osg::Vec3f b(x2,y2,z2);\n\n        MWPhysics::RayCastingResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), mask);\n        return result.mHit;\n    }\n\n    bool World::castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore)\n    {\n        return mPhysics->castRay(from, to, ignore, std::vector<MWWorld::Ptr>(), mask).mHit;\n    }\n\n    bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration)\n    {\n        const ESM::Position& objPos = door.getRefData().getPosition();\n        float oldRot = objPos.rot[2];\n\n        float minRot = door.getCellRef().getPosition().rot[2];\n        float maxRot = minRot + osg::DegreesToRadians(90.f);\n\n        float diff = duration * osg::DegreesToRadians(90.f) * (state == MWWorld::DoorState::Opening ? 1 : -1);\n        float targetRot = std::min(std::max(minRot, oldRot + diff), maxRot);\n        rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot, MWBase::RotationFlag_none);\n\n        bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;\n\n        /// \\todo should use convexSweepTest here\n        bool collisionWithActor = false;\n        for (auto& [ptr, point, normal] : mPhysics->getCollisionsPoints(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor))\n        {\n\n            if (ptr.getClass().isActor())\n            {\n                auto localPoint = objPos.asVec3() - point;\n                osg::Vec3f direction = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * localPoint - localPoint;\n                direction.normalize();\n                mPhysics->reportCollision(Misc::Convert::toBullet(point), Misc::Convert::toBullet(normal));\n                if (direction * normal < 0) // door is turning away from actor\n                    continue;\n\n                collisionWithActor = true;\n\n                // Collided with actor, ask actor to try to avoid door\n                if(ptr != getPlayerPtr() )\n                {\n                    MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();\n                    if(seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) //Only add it once\n                        seq.stack(MWMechanics::AiAvoidDoor(door),ptr);\n                }\n\n                // we need to undo the rotation\n                reached = false;\n            }\n        }\n\n        // Cancel door closing sound if collision with actor is detected\n        if (collisionWithActor)\n        {\n            const ESM::Door* ref = door.get<ESM::Door>()->mBase;\n\n            if (state == MWWorld::DoorState::Opening)\n            {\n                const std::string& openSound = ref->mOpenSound;\n                if (!openSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, openSound))\n                    MWBase::Environment::get().getSoundManager()->stopSound3D(door, openSound);\n            }\n            else if (state == MWWorld::DoorState::Closing)\n            {\n                const std::string& closeSound = ref->mCloseSound;\n                if (!closeSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, closeSound))\n                    MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound);\n            }\n\n            rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot, MWBase::RotationFlag_none);\n        }\n\n        return reached;\n    }\n\n    void World::processDoors(float duration)\n    {\n        auto it = mDoorStates.begin();\n        while (it != mDoorStates.end())\n        {\n            if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode())\n            {\n                // The door is no longer in an active cell, or it was disabled.\n                // Erase from mDoorStates, since we no longer need to move it.\n                // Once we load the door's cell again (or re-enable the door), Door::insertObject will reinsert to mDoorStates.\n                mDoorStates.erase(it++);\n            }\n            else\n            {\n                bool reached = rotateDoor(it->first, it->second, duration);\n\n                if (reached)\n                {\n                    // Mark as non-moving\n                    it->first.getClass().setDoorState(it->first, MWWorld::DoorState::Idle);\n                    mDoorStates.erase(it++);\n                }\n                else\n                    ++it;\n            }\n        }\n    }\n\n    void World::setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external)\n    {\n        MWPhysics::Actor *physicActor = mPhysics->getActor(ptr);\n        if (physicActor && physicActor->getCollisionMode() != internal)\n        {\n            physicActor->enableCollisionMode(internal);\n            physicActor->enableCollisionBody(external);\n        }\n    }\n\n    bool World::isActorCollisionEnabled(const MWWorld::Ptr& ptr)\n    {\n        MWPhysics::Actor *physicActor = mPhysics->getActor(ptr);\n        return physicActor && physicActor->getCollisionMode();\n    }\n\n    bool World::toggleCollisionMode()\n    {\n        if (mPhysics->toggleCollisionMode())\n        {\n            adjustPosition(getPlayerPtr(), true);\n            return true;\n        }\n\n        return false;\n    }\n\n    bool World::toggleRenderMode (MWRender::RenderMode mode)\n    {\n        switch (mode)\n        {\n            case MWRender::Render_CollisionDebug:\n                return mPhysics->toggleDebugRendering();\n            default:\n                return mRendering->toggleRenderMode(mode);\n        }\n    }\n\n    const ESM::Potion *World::createRecord (const ESM::Potion& record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::Class *World::createRecord (const ESM::Class& record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::Spell *World::createRecord (const ESM::Spell& record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::Cell *World::createRecord (const ESM::Cell& record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::CreatureLevList *World::createOverrideRecord(const ESM::CreatureLevList &record)\n    {\n        return mStore.overrideRecord(record);\n    }\n\n    const ESM::ItemLevList *World::createOverrideRecord(const ESM::ItemLevList &record)\n    {\n        return mStore.overrideRecord(record);\n    }\n\n    const ESM::Creature *World::createOverrideRecord(const ESM::Creature &record)\n    {\n        return mStore.overrideRecord(record);\n    }\n\n    const ESM::NPC *World::createOverrideRecord(const ESM::NPC &record)\n    {\n        return mStore.overrideRecord(record);\n    }\n\n    const ESM::Container *World::createOverrideRecord(const ESM::Container &record)\n    {\n        return mStore.overrideRecord(record);\n    }\n\n    const ESM::NPC *World::createRecord(const ESM::NPC &record)\n    {\n        bool update = false;\n\n        if (Misc::StringUtils::ciEqual(record.mId, \"player\"))\n        {\n            const ESM::NPC *player =\n                mPlayer->getPlayer().get<ESM::NPC>()->mBase;\n\n            update = record.isMale() != player->isMale() ||\n                     !Misc::StringUtils::ciEqual(record.mRace, player->mRace) ||\n                     !Misc::StringUtils::ciEqual(record.mHead, player->mHead) ||\n                     !Misc::StringUtils::ciEqual(record.mHair, player->mHair);\n        }\n        const ESM::NPC *ret = mStore.insert(record);\n        if (update) {\n            renderPlayer();\n        }\n        return ret;\n    }\n\n    const ESM::Creature *World::createRecord(const ESM::Creature &record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::Armor *World::createRecord (const ESM::Armor& record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::Weapon *World::createRecord (const ESM::Weapon& record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::Clothing *World::createRecord (const ESM::Clothing& record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::Enchantment *World::createRecord (const ESM::Enchantment& record)\n    {\n        return mStore.insert(record);\n    }\n\n    const ESM::Book *World::createRecord (const ESM::Book& record)\n    {\n        return mStore.insert(record);\n    }\n\n    void World::update (float duration, bool paused)\n    {\n        if (mGoToJail && !paused)\n            goToJail();\n\n        // Reset \"traveling\" flag - there was a frame to detect traveling.\n        mPlayerTraveling = false;\n\n        // The same thing for \"in jail\" flag: reset it if:\n        // 1. Player was in jail\n        // 2. Jailing window was closed\n        if (mPlayerInJail && !mGoToJail && !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail))\n            mPlayerInJail = false;\n\n        updateWeather(duration, paused);\n\n        if (!paused)\n        {\n            updateNavigator();\n        }\n\n        updatePlayer();\n\n        mPhysics->debugDraw();\n\n        mWorldScene->update (duration, paused);\n\n        updateSoundListener();\n\n        mSpellPreloadTimer -= duration;\n        if (mSpellPreloadTimer <= 0.f)\n        {\n            mSpellPreloadTimer = 0.1f;\n            preloadSpells();\n        }\n    }\n\n    void World::updatePhysics (float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)\n    {\n        if (!paused)\n        {\n            doPhysics (duration, frameStart, frameNumber, stats);\n        }\n        else\n        {\n            // zero the async stats if we are paused\n            stats.setAttribute(frameNumber, \"physicsworker_time_begin\", 0);\n            stats.setAttribute(frameNumber, \"physicsworker_time_taken\", 0);\n            stats.setAttribute(frameNumber, \"physicsworker_time_end\", 0);\n        }\n    }\n\n    void World::updatePlayer()\n    {\n        MWWorld::Ptr player = getPlayerPtr();\n\n        // TODO: move to MWWorld::Player\n\n        if (player.getCell()->isExterior())\n        {\n            ESM::Position pos = player.getRefData().getPosition();\n            mPlayer->setLastKnownExteriorPosition(pos.asVec3());\n        }\n\n        bool isWerewolf = player.getClass().getNpcStats(player).isWerewolf();\n        bool isFirstPerson = mRendering->getCamera()->isFirstPerson();\n        if (isWerewolf && isFirstPerson)\n        {\n            float werewolfFov = Fallback::Map::getFloat(\"General_Werewolf_FOV\");\n            if (werewolfFov != 0)\n                mRendering->overrideFieldOfView(werewolfFov);\n            MWBase::Environment::get().getWindowManager()->setWerewolfOverlay(true);\n        }\n        else\n        {\n            mRendering->resetFieldOfView();\n            MWBase::Environment::get().getWindowManager()->setWerewolfOverlay(false);\n        }\n\n        // Sink the camera while sneaking\n        bool sneaking = player.getClass().getCreatureStats(getPlayerPtr()).getStance(MWMechanics::CreatureStats::Stance_Sneak);\n        bool swimming = isSwimming(player);\n        bool flying = isFlying(player);\n\n        static const float i1stPersonSneakDelta = mStore.get<ESM::GameSetting>().find(\"i1stPersonSneakDelta\")->mValue.getFloat();\n        if (sneaking && !swimming && !flying)\n            mRendering->getCamera()->setSneakOffset(i1stPersonSneakDelta);\n        else\n            mRendering->getCamera()->setSneakOffset(0.f);\n\n        int blind = 0;\n        auto& magicEffects = player.getClass().getCreatureStats(player).getMagicEffects();\n        if (!mGodMode)\n            blind = static_cast<int>(magicEffects.get(ESM::MagicEffect::Blind).getMagnitude());\n        MWBase::Environment::get().getWindowManager()->setBlindness(std::max(0, std::min(100, blind)));\n\n        int nightEye = static_cast<int>(magicEffects.get(ESM::MagicEffect::NightEye).getMagnitude());\n        mRendering->setNightEyeFactor(std::min(1.f, (nightEye/100.f)));\n    }\n\n    void World::preloadSpells()\n    {\n        std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell();\n        if (!selectedSpell.empty())\n        {\n            const ESM::Spell* spell = mStore.get<ESM::Spell>().search(selectedSpell);\n            if (spell)\n                preloadEffects(&spell->mEffects);\n        }\n        const MWWorld::Ptr& selectedEnchantItem = MWBase::Environment::get().getWindowManager()->getSelectedEnchantItem();\n        if (!selectedEnchantItem.isEmpty())\n        {\n            std::string enchantId = selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem);\n            if (!enchantId.empty())\n            {\n                const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(enchantId);\n                if (ench)\n                    preloadEffects(&ench->mEffects);\n            }\n        }\n        const MWWorld::Ptr& selectedWeapon = MWBase::Environment::get().getWindowManager()->getSelectedWeapon();\n        if (!selectedWeapon.isEmpty())\n        {\n            std::string enchantId = selectedWeapon.getClass().getEnchantment(selectedWeapon);\n            if (!enchantId.empty())\n            {\n                const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(enchantId);\n                if (ench && ench->mData.mType == ESM::Enchantment::WhenStrikes)\n                    preloadEffects(&ench->mEffects);\n            }\n        }\n    }\n\n    void World::updateSoundListener()\n    {\n        const ESM::Position& refpos = getPlayerPtr().getRefData().getPosition();\n        osg::Vec3f listenerPos;\n\n        if (isFirstPerson())\n            listenerPos = mRendering->getCameraPosition();\n        else\n            listenerPos = refpos.asVec3() + osg::Vec3f(0, 0, 1.85f * mPhysics->getHalfExtents(getPlayerPtr()).z());\n\n        osg::Quat listenerOrient = osg::Quat(refpos.rot[1], osg::Vec3f(0,-1,0)) *\n                osg::Quat(refpos.rot[0], osg::Vec3f(-1,0,0)) *\n                osg::Quat(refpos.rot[2], osg::Vec3f(0,0,-1));\n\n        osg::Vec3f forward = listenerOrient * osg::Vec3f(0,1,0);\n        osg::Vec3f up = listenerOrient * osg::Vec3f(0,0,1);\n\n        bool underwater = isUnderwater(getPlayerPtr().getCell(), mRendering->getCameraPosition());\n\n        MWBase::Environment::get().getSoundManager()->setListenerPosDir(listenerPos, forward, up, underwater);\n    }\n\n    void World::updateWindowManager ()\n    {\n        try\n        {\n            // inform the GUI about focused object\n            MWWorld::Ptr object = getFacedObject ();\n\n            // retrieve object dimensions so we know where to place the floating label\n            if (!object.isEmpty ())\n            {\n                osg::BoundingBox bb = mPhysics->getBoundingBox(object);\n                if (!bb.valid() && object.getRefData().getBaseNode())\n                {\n                    osg::ComputeBoundsVisitor computeBoundsVisitor;\n                    computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect));\n                    object.getRefData().getBaseNode()->accept(computeBoundsVisitor);\n                    bb = computeBoundsVisitor.getBoundingBox();\n                }\n                osg::Vec4f screenBounds = mRendering->getScreenBounds(bb);\n                MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords(\n                    screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w());\n            }\n\n            MWBase::Environment::get().getWindowManager()->setFocusObject(object);\n        }\n        catch (std::exception& e)\n        {\n            Log(Debug::Error) << \"Error updating window manager: \" << e.what();\n        }\n    }\n\n    MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer)\n    {\n        const float camDist = mRendering->getCamera()->getCameraDistance();\n        maxDistance += camDist;\n        MWWorld::Ptr facedObject;\n        MWRender::RenderingManager::RayResult rayToObject;\n\n        if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n        {\n            float x, y;\n            MWBase::Environment::get().getWindowManager()->getMousePosition(x, y);\n            rayToObject = mRendering->castCameraToViewportRay(x, y, maxDistance, ignorePlayer);\n        }\n        else\n            rayToObject = mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer);\n\n        facedObject = rayToObject.mHitObject;\n        if (facedObject.isEmpty() && rayToObject.mHitRefnum.hasContentFile())\n        {\n            for (CellStore* cellstore : mWorldScene->getActiveCells())\n            {\n                facedObject = cellstore->searchViaRefNum(rayToObject.mHitRefnum);\n                if (!facedObject.isEmpty()) break;\n            }\n        }\n        if (rayToObject.mHit)\n            mDistanceToFacedObject = (rayToObject.mRatio * maxDistance) - camDist;\n        else\n            mDistanceToFacedObject = -1;\n        return facedObject;\n    }\n\n    bool World::isCellExterior() const\n    {\n        const CellStore *currentCell = mWorldScene->getCurrentCell();\n        if (currentCell)\n        {\n            return currentCell->getCell()->isExterior();\n        }\n        return false;\n    }\n\n    bool World::isCellQuasiExterior() const\n    {\n        const CellStore *currentCell = mWorldScene->getCurrentCell();\n        if (currentCell)\n        {\n            if (!(currentCell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))\n                return false;\n            else\n                return true;\n        }\n        return false;\n    }\n\n    int World::getCurrentWeather() const\n    {\n        return mWeatherManager->getWeatherID();\n    }\n\n    unsigned int World::getNightDayMode() const\n    {\n        return mWeatherManager->getNightDayMode();\n    }\n\n    void World::changeWeather(const std::string& region, const unsigned int id)\n    {\n        mWeatherManager->changeWeather(region, id);\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to set a specific weather state for a region from elsewhere\n        in the code\n    */\n    void World::setRegionWeather(const std::string& region, const unsigned int currentWeather, const unsigned int nextWeather,\n        const unsigned int queuedWeather, const float transitionFactor, bool force)\n    {\n        mWeatherManager->setRegionWeather(region, currentWeather, nextWeather, queuedWeather, transitionFactor, force);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check whether the local WeatherManager has the\n        ability to create weather changes\n    */\n    bool World::getWeatherCreationState()\n    {\n        return mWeatherManager->getWeatherCreationState();\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to enable and disable the local WeatherManager's ability\n        to create weather changes\n    */\n    void World::setWeatherCreationState(bool state)\n    {\n        mWeatherManager->setWeatherCreationState(state);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to send the current weather in a WorldWeather packet\n        when requested from elsewhere in the code\n    */\n    void World::sendWeather()\n    {\n        mWeatherManager->sendWeather();\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    void World::modRegion(const std::string &regionid, const std::vector<char> &chances)\n    {\n        mWeatherManager->modRegion(regionid, chances);\n    }\n\n    osg::Vec2f World::getNorthVector (const CellStore* cell)\n    {\n        MWWorld::ConstPtr northmarker = cell->searchConst(\"northmarker\");\n\n        if (northmarker.isEmpty())\n            return osg::Vec2f(0, 1);\n\n        osg::Quat orient (-northmarker.getRefData().getPosition().rot[2], osg::Vec3f(0,0,1));\n        osg::Vec3f dir = orient * osg::Vec3f(0,1,0);\n        osg::Vec2f d (dir.x(), dir.y());\n        return d;\n    }\n\n    struct GetDoorMarkerVisitor\n    {\n        GetDoorMarkerVisitor(std::vector<World::DoorMarker>& out)\n            : mOut(out)\n        {\n        }\n\n        std::vector<World::DoorMarker>& mOut;\n\n        bool operator()(const MWWorld::Ptr& ptr)\n        {\n            MWWorld::LiveCellRef<ESM::Door>& ref = *static_cast<MWWorld::LiveCellRef<ESM::Door>* >(ptr.getBase());\n\n            if (!ref.mData.isEnabled() || ref.mData.isDeleted())\n                return true;\n\n            if (ref.mRef.getTeleport())\n            {\n                World::DoorMarker newMarker;\n                newMarker.name = MWClass::Door::getDestination(ref);\n\n                ESM::CellId cellid;\n                if (!ref.mRef.getDestCell().empty())\n                {\n                    cellid.mWorldspace = ref.mRef.getDestCell();\n                    cellid.mPaged = false;\n                    cellid.mIndex.mX = 0;\n                    cellid.mIndex.mY = 0;\n                }\n                else\n                {\n                    cellid.mPaged = true;\n                    MWBase::Environment::get().getWorld()->positionToIndex(\n                                ref.mRef.getDoorDest().pos[0],\n                                ref.mRef.getDoorDest().pos[1],\n                                cellid.mIndex.mX,\n                                cellid.mIndex.mY);\n                }\n                newMarker.dest = cellid;\n\n                ESM::Position pos = ref.mData.getPosition ();\n\n                newMarker.x = pos.pos[0];\n                newMarker.y = pos.pos[1];\n                mOut.push_back(newMarker);\n            }\n            return true;\n        }\n    };\n\n    void World::getDoorMarkers (CellStore* cell, std::vector<World::DoorMarker>& out)\n    {\n        GetDoorMarkerVisitor visitor(out);\n        cell->forEachType<ESM::Door>(visitor);\n    }\n\n    void World::setWaterHeight(const float height)\n    {\n        mPhysics->setWaterHeight(height);\n        mRendering->setWaterHeight(height);\n    }\n\n    bool World::toggleWater()\n    {\n        return mRendering->toggleRenderMode(MWRender::Render_Water);\n    }\n\n    bool World::toggleWorld()\n    {\n        return mRendering->toggleRenderMode(MWRender::Render_Scene);\n    }\n\n    bool World::toggleBorders()\n    {\n        return mRendering->toggleBorders();\n    }\n\n    void World::PCDropped (const Ptr& item)\n    {\n        std::string script = item.getClass().getScript(item);\n\n        // Set OnPCDrop Variable on item's script, if it has a script with that variable declared\n        if(script != \"\")\n            item.getRefData().getLocals().setVarByInt(script, \"onpcdrop\", 1);\n    }\n\n    MWWorld::Ptr World::placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount)\n    {\n        const float maxDist = 200.f;\n\n        MWRender::RenderingManager::RayResult result = mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);\n\n        CellStore* cell = getPlayerPtr().getCell();\n\n        ESM::Position pos = getPlayerPtr().getRefData().getPosition();\n        if (result.mHit)\n        {\n            pos.pos[0] = result.mHitPointWorld.x();\n            pos.pos[1] = result.mHitPointWorld.y();\n            pos.pos[2] = result.mHitPointWorld.z();\n        }\n        // We want only the Z part of the player's rotation\n        pos.rot[0] = 0;\n        pos.rot[1] = 0;\n\n        // copy the object and set its count\n        Ptr dropped = copyObjectToCell(object, cell, pos, amount, true);\n\n        // only the player place items in the world, so no need to check actor\n        PCDropped(dropped);\n\n        return dropped;\n    }\n\n    bool World::canPlaceObject(float cursorX, float cursorY)\n    {\n        const float maxDist = 200.f;\n        MWRender::RenderingManager::RayResult result = mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);\n\n        if (result.mHit)\n        {\n            // check if the wanted position is on a flat surface, and not e.g. against a vertical wall\n            if (std::acos((result.mHitNormalWorld/result.mHitNormalWorld.length()) * osg::Vec3f(0,0,1)) >= osg::DegreesToRadians(30.f))\n                return false;\n\n            return true;\n        }\n        else\n            return false;\n    }\n\n\n    Ptr World::copyObjectToCell(const ConstPtr &object, CellStore* cell, ESM::Position pos, int count, bool adjustPos)\n    {\n        if (!cell)\n            throw std::runtime_error(\"copyObjectToCell(): cannot copy object to null cell\");\n        if (cell->isExterior())\n        {\n            int cellX, cellY;\n            positionToIndex(pos.pos[0], pos.pos[1], cellX, cellY);\n            cell = mCells.getExterior(cellX, cellY);\n        }\n\n        MWWorld::Ptr dropped =\n            object.getClass().copyToCell(object, *cell, pos, count);\n\n        // Reset some position values that could be uninitialized if this item came from a container\n        dropped.getCellRef().setPosition(pos);\n        dropped.getCellRef().unsetRefNum();\n\n        if (mWorldScene->isCellActive(*cell)) {\n            if (dropped.getRefData().isEnabled()) {\n                mWorldScene->addObjectToScene(dropped);\n            }\n            std::string script = dropped.getClass().getScript(dropped);\n            if (!script.empty()) {\n                mLocalScripts.add(script, dropped);\n            }\n            addContainerScripts(dropped, cell);\n        }\n\n        if (!object.getClass().isActor() && adjustPos && dropped.getRefData().getBaseNode())\n        {\n            // Adjust position so the location we wanted ends up in the middle of the object bounding box\n            osg::ComputeBoundsVisitor computeBounds;\n            computeBounds.setTraversalMask(~MWRender::Mask_ParticleSystem);\n            dropped.getRefData().getBaseNode()->accept(computeBounds);\n            osg::BoundingBox bounds = computeBounds.getBoundingBox();\n            if (bounds.valid())\n            {\n                bounds.set(bounds._min - pos.asVec3(), bounds._max - pos.asVec3());\n\n                osg::Vec3f adjust (\n                            (bounds.xMin() + bounds.xMax()) / 2,\n                           (bounds.yMin() + bounds.yMax()) / 2,\n                           bounds.zMin()\n                           );\n                pos.pos[0] -= adjust.x();\n                pos.pos[1] -= adjust.y();\n                pos.pos[2] -= adjust.z();\n                moveObject(dropped, pos.pos[0], pos.pos[1], pos.pos[2]);\n            }\n        }\n\n        return dropped;\n    }\n\n    MWWorld::Ptr World::dropObjectOnGround (const Ptr& actor, const ConstPtr& object, int amount)\n    {\n        MWWorld::CellStore* cell = actor.getCell();\n\n        ESM::Position pos =\n            actor.getRefData().getPosition();\n        // We want only the Z part of the actor's rotation\n        pos.rot[0] = 0;\n        pos.rot[1] = 0;\n\n        osg::Vec3f orig = pos.asVec3();\n        orig.z() += 20;\n        osg::Vec3f dir (0, 0, -1);\n\n        float len = 1000000.0;\n\n        MWRender::RenderingManager::RayResult result = mRendering->castRay(orig, orig+dir*len, true, true);\n        if (result.mHit)\n            pos.pos[2] = result.mHitPointWorld.z();\n\n        // copy the object and set its count\n        Ptr dropped = copyObjectToCell(object, cell, pos, amount, true);\n\n        if(actor == mPlayer->getPlayer()) // Only call if dropped by player\n            PCDropped(dropped);\n        return dropped;\n    }\n\n    void World::processChangedSettings(const Settings::CategorySettingVector& settings)\n    {\n        mRendering->processChangedSettings(settings);\n    }\n\n    bool World::isFlying(const MWWorld::Ptr &ptr) const\n    {\n        if(!ptr.getClass().isActor())\n            return false;\n\n        const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);\n\n        if (stats.isDead())\n            return false;\n\n        const bool isPlayer = ptr == getPlayerConstPtr();\n        if (!(isPlayer && mGodMode) && stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0)\n            return false;\n\n        if (ptr.getClass().canFly(ptr))\n            return true;\n\n        if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0\n                && isLevitationEnabled())\n            return true;\n\n        const MWPhysics::Actor* actor = mPhysics->getActor(ptr);\n        if(!actor)\n            return true;\n\n        return false;\n    }\n\n    bool World::isSlowFalling(const MWWorld::Ptr &ptr) const\n    {\n        if(!ptr.getClass().isActor())\n            return false;\n\n        const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);\n        if(stats.getMagicEffects().get(ESM::MagicEffect::SlowFall).getMagnitude() > 0)\n            return true;\n\n        return false;\n    }\n\n    bool World::isSubmerged(const MWWorld::ConstPtr &object) const\n    {\n        return isUnderwater(object, 1.0f/mSwimHeightScale);\n    }\n\n    bool World::isSwimming(const MWWorld::ConstPtr &object) const\n    {\n        return isUnderwater(object, mSwimHeightScale);\n    }\n\n    bool World::isWading(const MWWorld::ConstPtr &object) const\n    {\n        const float kneeDeep = 0.25f;\n        return isUnderwater(object, kneeDeep);\n    }\n\n    bool World::isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const\n    {\n        osg::Vec3f pos (object.getRefData().getPosition().asVec3());\n\n        pos.z() += heightRatio*2*mPhysics->getRenderingHalfExtents(object).z();\n\n        const CellStore *currCell = object.isInCell() ? object.getCell() : nullptr; // currCell == nullptr should only happen for player, during initial startup\n\n        return isUnderwater(currCell, pos);\n    }\n\n    bool World::isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const\n    {\n        if (!cell)\n            return false;\n\n        if (!(cell->getCell()->hasWater())) {\n            return false;\n        }\n        return pos.z() < cell->getWaterLevel();\n    }\n\n    bool World::isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const\n    {\n        const MWWorld::CellStore* cell = target.getCell();\n        if (!cell->getCell()->hasWater())\n            return true;\n\n        float waterlevel = cell->getWaterLevel();\n\n        // SwimHeightScale affects the upper z position an actor can swim to\n        // while in water. Based on observation from the original engine,\n        // the upper z position you get with a +1 SwimHeightScale is the depth\n        // limit for being able to cast water walking on an underwater target.\n        if (isUnderwater(target, mSwimHeightScale + 1) || (isUnderwater(cell, target.getRefData().getPosition().asVec3()) && !mPhysics->canMoveToWaterSurface(target, waterlevel)))\n            return false; // not castable if too deep or if not enough room to move actor to surface\n        else\n            return true;\n    }\n\n    bool World::isOnGround(const MWWorld::Ptr &ptr) const\n    {\n        return mPhysics->isOnGround(ptr);\n    }\n\n    void World::togglePOV(bool force)\n    {\n        mRendering->getCamera()->toggleViewMode(force);\n    }\n\n    bool World::isFirstPerson() const\n    {\n        return mRendering->getCamera()->isFirstPerson();\n    }\n    \n    bool World::isPreviewModeEnabled() const\n    {\n        return mRendering->getCamera()->getMode() == MWRender::Camera::Mode::Preview;\n    }\n\n    void World::togglePreviewMode(bool enable)\n    {\n        mRendering->getCamera()->togglePreviewMode(enable);\n    }\n\n    bool World::toggleVanityMode(bool enable)\n    {\n        return mRendering->getCamera()->toggleVanityMode(enable);\n    }\n\n    void World::disableDeferredPreviewRotation()\n    {\n        mRendering->getCamera()->disableDeferredPreviewRotation();\n    }\n\n    void World::applyDeferredPreviewRotationToPlayer(float dt)\n    {\n        mRendering->getCamera()->applyDeferredPreviewRotationToPlayer(dt);\n    }\n\n    void World::allowVanityMode(bool allow)\n    {\n        mRendering->getCamera()->allowVanityMode(allow);\n    }\n\n    bool World::vanityRotateCamera(float * rot)\n    {\n        if(!mRendering->getCamera()->isVanityOrPreviewModeEnabled())\n            return false;\n\n        mRendering->getCamera()->rotateCamera(rot[0], rot[2], true);\n        return true;\n    }\n\n    void World::adjustCameraDistance(float dist)\n    {\n        mRendering->getCamera()->adjustCameraDistance(dist);\n    }\n\n    void World::saveLoaded()\n    {\n        mStore.validateDynamic();\n    }\n\n    void World::setupPlayer()\n    {\n        const ESM::NPC *player = mStore.get<ESM::NPC>().find(\"player\");\n        if (!mPlayer)\n            mPlayer.reset(new MWWorld::Player(player));\n        else\n        {\n            // Remove the old CharacterController\n            MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr());\n            mNavigator->removeAgent(getPathfindingHalfExtents(getPlayerConstPtr()));\n            mPhysics->remove(getPlayerPtr());\n            mRendering->removePlayer(getPlayerPtr());\n\n            mPlayer->set(player);\n        }\n\n        Ptr ptr = mPlayer->getPlayer();\n        mRendering->setupPlayer(ptr);\n    }\n\n    void World::renderPlayer()\n    {\n        MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr());\n\n        MWWorld::Ptr player = getPlayerPtr();\n\n        mRendering->renderPlayer(player);\n        MWRender::NpcAnimation* anim = static_cast<MWRender::NpcAnimation*>(mRendering->getAnimation(player));\n        player.getClass().getInventoryStore(player).setInvListener(anim, player);\n        player.getClass().getInventoryStore(player).setContListener(anim);\n\n        scaleObject(player, player.getCellRef().getScale()); // apply race height\n        rotateObject(player, 0.f, 0.f, 0.f, MWBase::RotationFlag_inverseOrder | MWBase::RotationFlag_adjust);\n\n        MWBase::Environment::get().getMechanicsManager()->add(getPlayerPtr());\n        MWBase::Environment::get().getWindowManager()->watchActor(getPlayerPtr());\n\n        std::string model = getPlayerPtr().getClass().getModel(getPlayerPtr());\n        model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS());\n        mPhysics->remove(getPlayerPtr());\n        mPhysics->addActor(getPlayerPtr(), model);\n\n        applyLoopingParticles(player);\n\n        mDefaultHalfExtents = mPhysics->getOriginalHalfExtents(getPlayerPtr());\n        mNavigator->addAgent(getPathfindingHalfExtents(getPlayerConstPtr()));\n    }\n\n    World::RestPermitted World::canRest () const\n    {\n        CellStore *currentCell = mWorldScene->getCurrentCell();\n\n        Ptr player = mPlayer->getPlayer();\n        RefData &refdata = player.getRefData();\n        osg::Vec3f playerPos(refdata.getPosition().asVec3());\n\n        const MWPhysics::Actor* actor = mPhysics->getActor(player);\n        if (!actor)\n            throw std::runtime_error(\"can't find player\");\n\n        if(mPlayer->enemiesNearby())\n            return Rest_EnemiesAreNearby;\n\n        if (isUnderwater(currentCell, playerPos) || isWalkingOnWater(player))\n            return Rest_PlayerIsUnderwater;\n\n        float fallHeight = player.getClass().getCreatureStats(player).getFallHeight();\n        float epsilon = 1e-4;\n        if ((actor->getCollisionMode() && (!mPhysics->isOnSolidGround(player) || fallHeight >= epsilon)) || isFlying(player))\n            return Rest_PlayerIsInAir;\n\n        if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || player.getClass().getNpcStats(player).isWerewolf())\n            return Rest_OnlyWaiting;\n\n        return Rest_Allowed;\n    }\n\n    MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr)\n    {\n        auto* animation = mRendering->getAnimation(ptr);\n        if(!animation) {\n            mWorldScene->removeFromPagedRefs(ptr);\n            animation = mRendering->getAnimation(ptr);\n            if(animation)\n                mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);\n        }\n        return animation;\n    }\n\n    const MWRender::Animation* World::getAnimation(const MWWorld::ConstPtr &ptr) const\n    {\n        return mRendering->getAnimation(ptr);\n    }\n\n    void World::screenshot(osg::Image* image, int w, int h)\n    {\n        mRendering->screenshot(image, w, h);\n    }\n\n    bool World::screenshot360(osg::Image* image)\n    {\n        return mRendering->screenshot360(image);\n    }\n\n    void World::activateDoor(const MWWorld::Ptr& door)\n    {\n        auto state = door.getClass().getDoorState(door);\n        switch (state)\n        {\n        case MWWorld::DoorState::Idle:\n            if (door.getRefData().getPosition().rot[2] == door.getCellRef().getPosition().rot[2])\n                state = MWWorld::DoorState::Opening; // if closed, then open\n            else\n                state = MWWorld::DoorState::Closing; // if open, then close\n            break;\n        case MWWorld::DoorState::Closing:\n            state = MWWorld::DoorState::Opening; // if closing, then open\n            break;\n        case MWWorld::DoorState::Opening:\n        default:\n            state = MWWorld::DoorState::Closing; // if opening, then close\n            break;\n        }\n\n        /*\n            Start of tes3mp addition\n\n            Send an ID_DOOR_STATE packet every time a door is activated\n        */\n        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addDoorState(door, state);\n            objectList->sendDoorState();\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        door.getClass().setDoorState(door, state);\n        mDoorStates[door] = state;\n    }\n\n    void World::activateDoor(const Ptr &door, MWWorld::DoorState state)\n    {\n        /*\n            Start of tes3mp addition\n\n            Send an ID_DOOR_STATE packet every time a door is activated\n        */\n        if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())\n        {\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addDoorState(door, state);\n            objectList->sendDoorState();\n        }\n        /*\n            End of tes3mp addition\n        */\n\n        door.getClass().setDoorState(door, state);\n        mDoorStates[door] = state;\n        if (state == MWWorld::DoorState::Idle)\n        {\n            mDoorStates.erase(door);\n            rotateDoor(door, state, 1);\n        }\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Allow the saving of door states without going through World::activateDoor()\n    */\n    void World::saveDoorState(const Ptr &door, MWWorld::DoorState state)\n    {\n        mDoorStates[door] = state;\n        if (state == MWWorld::DoorState::Idle)\n            mDoorStates.erase(door);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to check whether a cell is active\n    */\n    bool World::isCellActive(const ESM::Cell& cell)\n    {\n        const Scene::CellStoreCollection& activeCells = mWorldScene->getActiveCells();\n        mwmp::CellController *cellController = mwmp::Main::get().getCellController();\n\n        for (auto it = activeCells.begin(); it != activeCells.end(); ++it)\n        {\n            if (cellController->isSameCell(cell, *(*it)->getCell()))\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to unload a cell from elsewhere\n    */\n    void World::unloadCell(const ESM::Cell& cell)\n    {\n        if (isCellActive(cell))\n        {\n            const Scene::CellStoreCollection& activeCells = mWorldScene->getActiveCells();\n            mwmp::CellController *cellController = mwmp::Main::get().getCellController();\n            mWorldScene->unloadCell(activeCells.find(cellController->getCellStore(cell)));\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Make it possible to unload all active cells from elsewhere\n    */\n    void World::unloadActiveCells()\n    {\n        const Scene::CellStoreCollection& activeCells = mWorldScene->getActiveCells();\n\n        for (auto it = activeCells.begin(); it != activeCells.end(); ++it)\n        {\n            // Ignore a placeholder interior that a player may currently be in\n            if ((*it)->getCell()->isExterior() || !Misc::StringUtils::ciEqual((*it)->getCell()->getDescription(), RecordHelper::getPlaceholderInteriorCellName()))\n            {\n                mWorldScene->unloadCell(it);\n            }\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    /*\n        Start of tes3mp addition\n\n        Clear the CellStore for a specific Cell from elsewhere\n    */\n    void World::clearCellStore(const ESM::Cell& cell)\n    {\n        mwmp::CellController* cellController = mwmp::Main::get().getCellController();\n        MWWorld::CellStore *cellStore = cellController->getCellStore(cell);\n\n        if (cellStore != nullptr)\n            cellStore->clearMovesToCells();\n        mCells.clear(cell);\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    bool World::getPlayerStandingOn (const MWWorld::ConstPtr& object)\n    {\n        MWWorld::Ptr player = getPlayerPtr();\n        return mPhysics->isActorStandingOn(player, object);\n    }\n\n    bool World::getActorStandingOn (const MWWorld::ConstPtr& object)\n    {\n        std::vector<MWWorld::Ptr> actors;\n        mPhysics->getActorsStandingOn(object, actors);\n        return !actors.empty();\n    }\n\n    void World::getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors)\n    {\n        mPhysics->getActorsStandingOn(object, actors);\n    }\n\n    bool World::getPlayerCollidingWith (const MWWorld::ConstPtr& object)\n    {\n        MWWorld::Ptr player = getPlayerPtr();\n        return mPhysics->isActorCollidingWith(player, object);\n    }\n\n    bool World::getActorCollidingWith (const MWWorld::ConstPtr& object)\n    {\n        std::vector<MWWorld::Ptr> actors;\n        mPhysics->getActorsCollidingWith(object, actors);\n        return !actors.empty();\n    }\n\n    void World::hurtStandingActors(const ConstPtr &object, float healthPerSecond)\n    {\n        /*\n            Start of tes3mp change (major)\n\n            Being in a menu should not prevent actors from being hurt in multiplayer,\n            so that check has been commented out\n        */\n        //if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n        //    return;\n        /*\n            End of tes3mp change (major)\n        */\n\n        std::vector<MWWorld::Ptr> actors;\n        mPhysics->getActorsStandingOn(object, actors);\n        for (const Ptr &actor : actors)\n        {\n            MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n            if (stats.isDead())\n                continue;\n\n            mPhysics->markAsNonSolid (object);\n\n            if (actor == getPlayerPtr() && mGodMode)\n                continue;\n\n            MWMechanics::DynamicStat<float> health = stats.getHealth();\n            health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration());\n            stats.setHealth(health);\n\n            if (healthPerSecond > 0.0f)\n            {\n                if (actor == getPlayerPtr())\n                    MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);\n\n                if (!MWBase::Environment::get().getSoundManager()->getSoundPlaying(actor, \"Health Damage\"))\n                    MWBase::Environment::get().getSoundManager()->playSound3D(actor, \"Health Damage\", 1.0f, 1.0f);\n            }\n        }\n    }\n\n    void World::hurtCollidingActors(const ConstPtr &object, float healthPerSecond)\n    {\n        /*\n            Start of tes3mp change (major)\n\n            Being in a menu should not prevent actors from being hurt in multiplayer,\n            so that check has been commented out\n        */\n        //if (MWBase::Environment::get().getWindowManager()->isGuiMode())\n        //    return;\n        /*\n            End of tes3mp change (major)\n        */\n\n        std::vector<Ptr> actors;\n        mPhysics->getActorsCollidingWith(object, actors);\n        for (const Ptr &actor : actors)\n        {\n            MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n            if (stats.isDead())\n                continue;\n\n            mPhysics->markAsNonSolid (object);\n\n            if (actor == getPlayerPtr() && mGodMode)\n                continue;\n\n            MWMechanics::DynamicStat<float> health = stats.getHealth();\n            health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration());\n            stats.setHealth(health);\n\n            if (healthPerSecond > 0.0f)\n            {\n                if (actor == getPlayerPtr())\n                    MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);\n\n                if (!MWBase::Environment::get().getSoundManager()->getSoundPlaying(actor, \"Health Damage\"))\n                    MWBase::Environment::get().getSoundManager()->playSound3D(actor, \"Health Damage\", 1.0f, 1.0f);\n            }\n        }\n    }\n\n    float World::getWindSpeed()\n    {\n        if (isCellExterior() || isCellQuasiExterior())\n            return mWeatherManager->getWindSpeed();\n        else\n            return 0.f;\n    }\n\n    bool World::isInStorm() const\n    {\n        if (isCellExterior() || isCellQuasiExterior())\n            return mWeatherManager->isInStorm();\n        else\n            return false;\n    }\n\n    osg::Vec3f World::getStormDirection() const\n    {\n        if (isCellExterior() || isCellQuasiExterior())\n            return mWeatherManager->getStormDirection();\n        else\n            return osg::Vec3f(0,1,0);\n    }\n\n    struct GetContainersOwnedByVisitor\n    {\n        GetContainersOwnedByVisitor(const MWWorld::ConstPtr& owner, std::vector<MWWorld::Ptr>& out)\n            : mOwner(owner)\n            , mOut(out)\n        {\n        }\n\n        MWWorld::ConstPtr mOwner;\n        std::vector<MWWorld::Ptr>& mOut;\n\n        bool operator()(const MWWorld::Ptr& ptr)\n        {\n            if (ptr.getRefData().isDeleted())\n                return true;\n\n            // vanilla Morrowind does not allow to sell items from containers with zero capacity\n            if (ptr.getClass().getCapacity(ptr) <= 0.f)\n                return true;\n\n            if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), mOwner.getCellRef().getRefId()))\n                mOut.push_back(ptr);\n\n            return true;\n        }\n    };\n\n    void World::getContainersOwnedBy (const MWWorld::ConstPtr& owner, std::vector<MWWorld::Ptr>& out)\n    {\n        for (CellStore* cellstore : mWorldScene->getActiveCells())\n        {\n            GetContainersOwnedByVisitor visitor (owner, out);\n            cellstore->forEachType<ESM::Container>(visitor);\n        }\n    }\n\n    void World::getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector<MWWorld::Ptr>& out)\n    {\n        for (CellStore* cellstore : mWorldScene->getActiveCells())\n        {\n            cellstore->forEach([&] (const auto& ptr) {\n                if (ptr.getRefData().getBaseNode() && Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), npc.getCellRef().getRefId()))\n                    out.push_back(ptr);\n                return true;\n            });\n        }\n    }\n\n    bool World::getLOS(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& targetActor)\n    {\n        if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled())\n            return false; // cannot get LOS unless both NPC's are enabled\n        if (!targetActor.getRefData().getBaseNode() || !actor.getRefData().getBaseNode())\n            return false; // not in active cell\n\n        return mPhysics->getLineOfSight(actor, targetActor);\n    }\n\n    float World::getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist, bool includeWater)\n    {\n        osg::Vec3f to (dir);\n        to.normalize();\n        to = from + (to * maxDist);\n\n        int collisionTypes = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door;\n        if (includeWater) {\n            collisionTypes |= MWPhysics::CollisionType_Water;\n        }\n        MWPhysics::RayCastingResult result = mPhysics->castRay(from, to, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), collisionTypes);\n\n        if (!result.mHit)\n            return maxDist;\n        else\n            return (result.mHitPos - from).length();\n    }\n\n    void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable)\n    {\n        MWPhysics::Actor *physicActor = mPhysics->getActor(actor);\n        if (physicActor)\n            physicActor->enableCollisionBody(enable);\n    }\n\n    bool World::findInteriorPosition(const std::string &name, ESM::Position &pos)\n    {\n        pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;\n        pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;\n\n        MWWorld::CellStore *cellStore = getInterior(name);\n\n        if (!cellStore)\n            return false;\n\n        std::vector<const MWWorld::CellRef *> sortedDoors;\n        for (const MWWorld::LiveCellRef<ESM::Door>& door : cellStore->getReadOnlyDoors().mList)\n        {\n            if (!door.mRef.getTeleport())\n                continue;\n\n            sortedDoors.push_back(&door.mRef);\n        }\n\n        // Sort teleporting doors alphabetically, first by ID, then by destination cell to make search consistent\n        std::sort(sortedDoors.begin(), sortedDoors.end(), [] (const MWWorld::CellRef *lhs, const MWWorld::CellRef *rhs)\n        {\n            if (lhs->getRefId() != rhs->getRefId())\n                return lhs->getRefId() < rhs->getRefId();\n\n            return lhs->getDestCell() < rhs->getDestCell();\n        });\n\n        for (const MWWorld::CellRef* door : sortedDoors)\n        {\n            MWWorld::CellStore *source = nullptr;\n\n            // door to exterior\n            if (door->getDestCell().empty())\n            {\n                int x, y;\n                ESM::Position doorDest = door->getDoorDest();\n                positionToIndex(doorDest.pos[0], doorDest.pos[1], x, y);\n                source = getExterior(x, y);\n            }\n            // door to interior\n            else\n            {\n                source = getInterior(door->getDestCell());\n            }\n            if (source)\n            {\n                // Find door leading to our current teleport door\n                // and use its destination to position inside cell.\n                for (const MWWorld::LiveCellRef<ESM::Door>& destDoor : source->getReadOnlyDoors().mList)\n                {\n                    if (Misc::StringUtils::ciEqual(name, destDoor.mRef.getDestCell()))\n                    {\n                        /// \\note Using _any_ door pointed to the interior,\n                        /// not the one pointed to current door.\n                        pos = destDoor.mRef.getDoorDest();\n                        pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;\n                        return true;\n                    }\n                }\n            }\n        }\n        // Fall back to the first static location.\n        const MWWorld::CellRefList<ESM::Static>::List &statics = cellStore->getReadOnlyStatics().mList;\n        if (!statics.empty())\n        {\n            pos = statics.begin()->mRef.getPosition();\n            pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;\n            return true;\n        }\n\n        return false;\n    }\n\n    bool World::findExteriorPosition(const std::string &name, ESM::Position &pos)\n    {\n        pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;\n\n        const ESM::Cell *ext = getExterior(name);\n\n        if (!ext && name.find(',') != std::string::npos) {\n            try {\n                int x = std::stoi(name.substr(0, name.find(',')));\n                int y = std::stoi(name.substr(name.find(',')+1));\n                ext = getExterior(x, y)->getCell();\n            }\n            catch (const std::invalid_argument&)\n            {\n                // This exception can be ignored, as this means that name probably refers to a interior cell instead of comma separated coordinates\n            }\n            catch (const std::out_of_range&)\n            {\n                throw std::runtime_error(\"Cell coordinates out of range.\");\n            }\n        }\n\n        if (ext) {\n            int x = ext->getGridX();\n            int y = ext->getGridY();\n            indexToPosition(x, y, pos.pos[0], pos.pos[1], true);\n\n            // Note: Z pos will be adjusted by adjustPosition later\n            pos.pos[2] = 0;\n\n            return true;\n        }\n\n        return false;\n    }\n\n    void World::enableTeleporting(bool enable)\n    {\n        mTeleportEnabled = enable;\n    }\n\n    bool World::isTeleportingEnabled() const\n    {\n        return mTeleportEnabled;\n    }\n\n    void World::enableLevitation(bool enable)\n    {\n        mLevitationEnabled = enable;\n    }\n\n    bool World::isLevitationEnabled() const\n    {\n        return mLevitationEnabled;\n    }\n\n    void World::reattachPlayerCamera()\n    {\n        mRendering->rebuildPtr(getPlayerPtr());\n    }\n\n    bool World::getGodModeState() const\n    {\n        return mGodMode;\n    }\n\n    bool World::toggleGodMode()\n    {\n        mGodMode = !mGodMode;\n\n        return mGodMode;\n    }\n\n    bool World::toggleScripts()\n    {\n        mScriptsEnabled = !mScriptsEnabled;\n        return mScriptsEnabled;\n    }\n\n    bool World::getScriptsEnabled() const\n    {\n        return mScriptsEnabled;\n    }\n\n    void World::loadContentFiles(const Files::Collections& fileCollections,\n        const std::vector<std::string>& content, const std::vector<std::string>& groundcover, ContentLoader& contentLoader)\n    {\n        int idx = 0;\n        for (const std::string &file : content)\n        {\n            boost::filesystem::path filename(file);\n            const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());\n            if (col.doesExist(file))\n            {\n                contentLoader.load(col.getPath(file), idx);\n            }\n            else\n            {\n                std::string message = \"Failed loading \" + file + \": the content file does not exist\";\n                throw std::runtime_error(message);\n            }\n            idx++;\n        }\n\n        ESM::GroundcoverIndex = idx;\n\n        for (const std::string &file : groundcover)\n        {\n            boost::filesystem::path filename(file);\n            const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());\n            if (col.doesExist(file))\n            {\n                contentLoader.load(col.getPath(file), idx);\n            }\n            else\n            {\n                std::string message = \"Failed loading \" + file + \": the groundcover file does not exist\";\n                throw std::runtime_error(message);\n            }\n            idx++;\n        }\n    }\n\n    bool World::startSpellCast(const Ptr &actor)\n    {\n        MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n\n        std::string message;\n        bool fail = false;\n        bool isPlayer = (actor == getPlayerPtr());\n\n        std::string selectedSpell = stats.getSpells().getSelectedSpell();\n\n        if (!selectedSpell.empty())\n        {\n            /*\n                Start of tes3mp addition\n\n                If the spell being cast does not exist on our client, ignore it\n                to avoid framelistener errors\n            */\n            if (getStore().get<ESM::Spell>().search(selectedSpell) == 0)\n                return false;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Always start spells cast by DedicatedPlayers and DedicatedActors,\n                without unilaterally deducting any magicka for them on this client\n            */\n            if (mwmp::PlayerList::isDedicatedPlayer(actor) || mwmp::Main::get().getCellController()->isDedicatedActor(actor))\n                return true;\n            /*\n                End of tes3mp addition\n            */\n\n            const ESM::Spell* spell = mStore.get<ESM::Spell>().find(selectedSpell);\n\n            // Check mana\n            bool godmode = (isPlayer && mGodMode);\n            MWMechanics::DynamicStat<float> magicka = stats.getMagicka();\n            if (spell->mData.mCost > 0 && magicka.getCurrent() < spell->mData.mCost && !godmode)\n            {\n                message = \"#{sMagicInsufficientSP}\";\n                fail = true;\n            }\n\n            // If this is a power, check if it was already used in the last 24h\n            if (!fail && spell->mData.mType == ESM::Spell::ST_Power && !stats.getSpells().canUsePower(spell))\n            {\n                message = \"#{sPowerAlreadyUsed}\";\n                fail = true;\n            }\n\n            // Reduce mana\n            if (!fail && !godmode)\n            {\n                magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost);\n                stats.setMagicka(magicka);\n            }\n        }\n\n        if (isPlayer && fail)\n            MWBase::Environment::get().getWindowManager()->messageBox(message);\n\n        return !fail;\n    }\n\n    void World::castSpell(const Ptr &actor, bool manualSpell)\n    {\n        MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);\n\n        // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.\n        std::vector<MWWorld::Ptr> targetActors;\n        if (!actor.isEmpty() && actor != MWMechanics::getPlayer() && !manualSpell)\n            stats.getAiSequence().getCombatTargets(targetActors);\n\n        const float fCombatDistance = mStore.get<ESM::GameSetting>().find(\"fCombatDistance\")->mValue.getFloat();\n\n        osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3();\n\n        // for player we can take faced object first\n        MWWorld::Ptr target;\n        if (actor == MWMechanics::getPlayer())\n            target = getFacedObject();\n\n        // if the faced object can not be activated, do not use it\n        if (!target.isEmpty() && !target.getClass().hasToolTip(target))\n            target = nullptr;\n\n        if (target.isEmpty())\n        {\n            // For scripted spells we should not use hit contact\n            if (manualSpell)\n            {\n                if (actor != MWMechanics::getPlayer())\n                {\n                    for (const auto& package : stats.getAiSequence())\n                    {\n                        if (package->getTypeId() == MWMechanics::AiPackageTypeId::Cast)\n                        {\n                            target = package->getTarget();\n                            break;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                // For actor targets, we want to use hit contact with bounding boxes.\n                // This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise.\n                // For object targets, we want the detailed shapes (rendering raycast).\n                // If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf.\n                std::pair<MWWorld::Ptr,osg::Vec3f> result1 = getHitContact(actor, fCombatDistance, targetActors);\n\n                // Get the target to use for \"on touch\" effects, using the facing direction from Head node\n                osg::Vec3f origin = getActorHeadTransform(actor).getTrans();\n\n                osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))\n                        * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));\n\n                osg::Vec3f direction = orient * osg::Vec3f(0,1,0);\n                float distance = getMaxActivationDistance();\n                osg::Vec3f dest = origin + direction * distance;\n\n                MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);\n\n                float dist1 = std::numeric_limits<float>::max();\n                float dist2 = std::numeric_limits<float>::max();\n\n                if (!result1.first.isEmpty() && result1.first.getClass().isActor())\n                    dist1 = (origin - result1.second).length();\n                if (result2.mHit)\n                    dist2 = (origin - result2.mHitPointWorld).length();\n\n                if (!result1.first.isEmpty() && result1.first.getClass().isActor())\n                {\n                    target = result1.first;\n                    hitPosition = result1.second;\n                    if (dist1 > getMaxActivationDistance())\n                        target = nullptr;\n                }\n                else if (result2.mHit)\n                {\n                    target = result2.mHitObject;\n                    hitPosition = result2.mHitPointWorld;\n                    if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().hasToolTip(target))\n                        target = nullptr;\n                }\n            }\n        }\n\n        std::string selectedSpell = stats.getSpells().getSelectedSpell();\n\n        MWMechanics::CastSpell cast(actor, target, false, manualSpell);\n        cast.mHitPosition = hitPosition;\n\n        if (!selectedSpell.empty())\n        {\n            const ESM::Spell* spell = mStore.get<ESM::Spell>().find(selectedSpell);\n            cast.cast(spell);\n        }\n        else if (actor.getClass().hasInventoryStore(actor))\n        {\n            MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);\n            if (inv.getSelectedEnchantItem() != inv.end())\n            /*\n                Start of tes3mp change (minor)\n\n                If this actor is a LocalPlayer or LocalActor, get their Cast and prepare\n                it for sending\n\n                Set the cast details before going forward, in case it's a one use item that\n                will get removed (like a scroll)\n            */\n            {\n                mwmp::Cast *localCast = MechanicsHelper::getLocalCast(actor);\n\n                if (localCast)\n                {\n                    MechanicsHelper::resetCast(localCast);\n                    localCast->type = mwmp::Cast::ITEM;\n                    localCast->itemId = inv.getSelectedEnchantItem()->getCellRef().getRefId();\n                    localCast->shouldSend = true;\n                }\n\n                cast.cast(*inv.getSelectedEnchantItem());\n            }\n            /*\n                End of tes3mp addition\n            */\n        }\n    }\n\n    void World::launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,\n                                   const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength)\n    {\n        // An initial position of projectile can be outside shooter's collision box, so any object between shooter and launch position will be ignored.\n        // To avoid this issue, we should check for impact immediately before launch the projectile.\n        // So we cast a 1-yard-length ray from shooter to launch position and check if there are collisions in this area.\n        // TODO: as a better solutuon we should handle projectiles during physics update, not during world update.\n        const osg::Vec3f sourcePos = worldPos + orient * osg::Vec3f(0,-1,0) * 64.f;\n\n        // Early out if the launch position is underwater\n        bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos);\n        if (underwater)\n        {\n            MWMechanics::projectileHit(actor, Ptr(), bow, projectile, worldPos, attackStrength);\n            mRendering->emitWaterRipple(worldPos);\n            return;\n        }\n\n        // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.\n        std::vector<MWWorld::Ptr> targetActors;\n        if (!actor.isEmpty() && actor.getClass().isActor() && actor != MWMechanics::getPlayer())\n            actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);\n\n        // Check for impact, if yes, handle hit, if not, launch projectile\n        MWPhysics::RayCastingResult result = mPhysics->castRay(sourcePos, worldPos, actor, targetActors, 0xff, MWPhysics::CollisionType_Projectile);\n        if (result.mHit)\n            MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength);\n        else\n            mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength);\n    }\n\n    void World::launchMagicBolt (const std::string &spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection)\n    {\n        mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection);\n    }\n\n    void World::updateProjectilesCasters()\n    {\n        mProjectileManager->updateCasters();\n    }\n\n    class ApplyLoopingParticlesVisitor : public MWMechanics::EffectSourceVisitor\n    {\n    private:\n        MWWorld::Ptr mActor;\n\n    public:\n        ApplyLoopingParticlesVisitor(const MWWorld::Ptr& actor)\n            : mActor(actor)\n        {\n        }\n\n        void visit (MWMechanics::EffectKey key, int /*effectIndex*/,\n                            const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,\n                            float /*magnitude*/, float /*remainingTime*/ = -1, float /*totalTime*/ = -1) override\n        {\n            const ESMStore& store = MWBase::Environment::get().getWorld()->getStore();\n            const auto magicEffect = store.get<ESM::MagicEffect>().find(key.mId);\n            if ((magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) == 0)\n                return;\n            const ESM::Static* castStatic;\n            if (!magicEffect->mHit.empty())\n                castStatic = store.get<ESM::Static>().find (magicEffect->mHit);\n            else\n                castStatic = store.get<ESM::Static>().find (\"VFX_DefaultHit\");\n            MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mActor);\n            if (anim && !castStatic->mModel.empty())\n                anim->addEffect(\"meshes\\\\\" + castStatic->mModel, magicEffect->mIndex, /*loop*/true, \"\", magicEffect->mParticle);\n        }\n    };\n\n    void World::applyLoopingParticles(const MWWorld::Ptr& ptr)\n    {\n        const MWWorld::Class &cls = ptr.getClass();\n        if (cls.isActor())\n        {\n            ApplyLoopingParticlesVisitor visitor(ptr);\n            cls.getCreatureStats(ptr).getActiveSpells().visitEffectSources(visitor);\n            cls.getCreatureStats(ptr).getSpells().visitEffectSources(visitor);\n            if (cls.hasInventoryStore(ptr))\n                cls.getInventoryStore(ptr).visitEffectSources(visitor);\n        }\n    }\n\n    const std::vector<std::string>& World::getContentFiles() const\n    {\n        return mContentFiles;\n    }\n\n    void World::breakInvisibility(const Ptr &actor)\n    {\n        actor.getClass().getCreatureStats(actor).getSpells().purgeEffect(ESM::MagicEffect::Invisibility);\n        actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility);\n        if (actor.getClass().hasInventoryStore(actor))\n            actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility);\n\n        // Normally updated once per frame, but here it is kinda important to do it right away.\n        MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor);\n    }\n\n    bool World::useTorches() const\n    {\n        // If we are in exterior, check the weather manager.\n        // In interiors there are no precipitations and sun, so check the ambient\n        // Looks like pseudo-exteriors considered as interiors in this case\n        MWWorld::CellStore* cell = mPlayer->getPlayer().getCell();\n        if (cell->isExterior())\n        {\n            float hour = getTimeStamp().getHour();\n            return mWeatherManager->useTorches(hour);\n        }\n        else\n        {\n            uint32_t ambient = cell->getCell()->mAmbi.mAmbient;\n            int ambientTotal = (ambient & 0xff)\n                    + ((ambient>>8) & 0xff)\n                    + ((ambient>>16) & 0xff);\n            return !(cell->getCell()->mData.mFlags & ESM::Cell::NoSleep) && ambientTotal <= 201;\n        }\n    }\n\n    bool World::findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result)\n    {\n        if (cell->isExterior())\n            return false;\n\n        // Search for a 'nearest' exterior, counting each cell between the starting\n        // cell and the exterior as a distance of 1.  Will fail for isolated interiors.\n        std::set< std::string >checkedCells;\n        std::set< std::string >currentCells;\n        std::set< std::string >nextCells;\n        nextCells.insert( cell->getCell()->mName );\n\n        while ( !nextCells.empty() ) {\n            currentCells = nextCells;\n            nextCells.clear();\n            for (const std::string &currentCell : currentCells)\n            {\n                MWWorld::CellStore *next = getInterior(currentCell);\n                if ( !next ) continue;\n\n                // Check if any door in the cell leads to an exterior directly\n                for (const MWWorld::LiveCellRef<ESM::Door>& ref : next->getReadOnlyDoors().mList)\n                {\n                    if (!ref.mRef.getTeleport()) continue;\n\n                    if (ref.mRef.getDestCell().empty())\n                    {\n                        ESM::Position pos = ref.mRef.getDoorDest();\n                        result = pos.asVec3();\n                        return true;\n                    }\n                    else\n                    {\n                        std::string dest = ref.mRef.getDestCell();\n                        if ( !checkedCells.count(dest) && !currentCells.count(dest) )\n                            nextCells.insert(dest);\n                    }\n                }\n\n                checkedCells.insert(currentCell);\n            }\n        }\n\n        // No luck :(\n        return false;\n    }\n\n    MWWorld::ConstPtr World::getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id )\n    {\n        if ( ptr.getCell()->isExterior() ) {\n            return getClosestMarkerFromExteriorPosition(mPlayer->getLastKnownExteriorPosition(), id);\n        }\n\n        // Search for a 'nearest' marker, counting each cell between the starting\n        // cell and the exterior as a distance of 1.  If an exterior is found, jump\n        // to the nearest exterior marker, without further interior searching.\n        std::set< std::string >checkedCells;\n        std::set< std::string >currentCells;\n        std::set< std::string >nextCells;\n        MWWorld::ConstPtr closestMarker;\n\n        nextCells.insert( ptr.getCell()->getCell()->mName );\n        while ( !nextCells.empty() ) {\n            currentCells = nextCells;\n            nextCells.clear();\n            for (const std::string &cell : currentCells) {\n                MWWorld::CellStore *next = getInterior(cell);\n                checkedCells.insert(cell);\n                if ( !next ) continue;\n\n                closestMarker = next->searchConst( id );\n                if ( !closestMarker.isEmpty() )\n                {\n                    return closestMarker;\n                }\n\n                // Check if any door in the cell leads to an exterior directly\n                for (const MWWorld::LiveCellRef<ESM::Door>& ref : next->getReadOnlyDoors().mList)\n                {\n                    if (!ref.mRef.getTeleport()) continue;\n\n                    if (ref.mRef.getDestCell().empty())\n                    {\n                        osg::Vec3f worldPos = ref.mRef.getDoorDest().asVec3();\n                        return getClosestMarkerFromExteriorPosition(worldPos, id);\n                    }\n                    else\n                    {\n                        std::string dest = ref.mRef.getDestCell();\n                        if ( !checkedCells.count(dest) && !currentCells.count(dest) )\n                            nextCells.insert(dest);\n                    }\n                }\n            }\n        }\n        return MWWorld::Ptr();\n    }\n\n    MWWorld::ConstPtr World::getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ) {\n        MWWorld::ConstPtr closestMarker;\n        float closestDistance = std::numeric_limits<float>::max();\n\n        std::vector<MWWorld::Ptr> markers;\n        mCells.getExteriorPtrs(id, markers);\n        for (const Ptr& marker : markers)\n        {\n            osg::Vec3f markerPos = marker.getRefData().getPosition().asVec3();\n            float distance = (worldPos - markerPos).length2();\n            if (distance < closestDistance)\n            {\n                closestDistance = distance;\n                closestMarker = marker;\n            }\n        }\n\n        return closestMarker;\n    }\n\n    void World::rest(double hours)\n    {\n        mCells.rest(hours);\n    }\n\n    void World::rechargeItems(double duration, bool activeOnly)\n    {\n        MWWorld::Ptr player = getPlayerPtr();\n        player.getClass().getInventoryStore(player).rechargeItems(duration);\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't unilaterally recharge world items on clients\n        */\n        /*\n        if (activeOnly)\n        {\n            for (auto &cell : mWorldScene->getActiveCells())\n            {\n                cell->recharge(duration);\n            }\n        }\n        else\n            mCells.recharge(duration);\n        */\n        /*\n            End of tes3mp change (major)\n        */\n    }\n\n    void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,\n                                          const std::string& id)\n    {\n        MWWorld::ConstPtr closestMarker = getClosestMarker( ptr, id );\n\n        if ( closestMarker.isEmpty() )\n        {\n            Log(Debug::Warning) << \"Failed to teleport: no closest marker found\";\n            return;\n        }\n\n        std::string cellName;\n        if ( !closestMarker.mCell->isExterior() )\n            cellName = closestMarker.mCell->getCell()->mName;\n\n        MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false);\n        action.execute(ptr);\n    }\n\n    void World::updateWeather(float duration, bool paused)\n    {\n        bool isExterior = isCellExterior() || isCellQuasiExterior();\n        if (mPlayer->wasTeleported())\n        {\n            mPlayer->setTeleported(false);\n\n            const std::string playerRegion = Misc::StringUtils::lowerCase(getPlayerPtr().getCell()->getCell()->mRegion);\n            mWeatherManager->playerTeleported(playerRegion, isExterior);\n        }\n\n        const TimeStamp time = getTimeStamp();\n        mWeatherManager->update(duration, paused, time, isExterior);\n    }\n\n    struct AddDetectedReferenceVisitor\n    {\n        AddDetectedReferenceVisitor(std::vector<Ptr>& out, const Ptr& detector, World::DetectionType type, float squaredDist)\n            : mOut(out), mDetector(detector), mSquaredDist(squaredDist), mType(type)\n        {\n        }\n\n        std::vector<Ptr>& mOut;\n        Ptr mDetector;\n        float mSquaredDist;\n        World::DetectionType mType;\n        bool operator() (const MWWorld::Ptr& ptr)\n        {\n            if ((ptr.getRefData().getPosition().asVec3() - mDetector.getRefData().getPosition().asVec3()).length2() >= mSquaredDist)\n                return true;\n\n            if (!ptr.getRefData().isEnabled() || ptr.getRefData().isDeleted())\n                return true;\n\n            // Consider references inside containers as well (except if we are looking for a Creature, they cannot be in containers)\n            bool isContainer = ptr.getClass().getTypeName() == typeid(ESM::Container).name();\n            if (mType != World::Detect_Creature && (ptr.getClass().isActor() || isContainer))\n            {\n                // but ignore containers without resolved content\n                if (isContainer && ptr.getRefData().getCustomData() == nullptr)\n                    return true;\n\n                MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);\n                {\n                    for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)\n                    {\n                        if (needToAdd(*it, mDetector))\n                        {\n                            mOut.push_back(ptr);\n                            return true;\n                        }\n                    }\n                }\n            }\n\n            if (needToAdd(ptr, mDetector))\n                mOut.push_back(ptr);\n\n            return true;\n        }\n\n        bool needToAdd (const MWWorld::Ptr& ptr, const MWWorld::Ptr& detector)\n        {\n            if (mType == World::Detect_Creature)\n            {\n                // If in werewolf form, this detects only NPCs, otherwise only creatures\n                if (detector.getClass().isNpc() && detector.getClass().getNpcStats(detector).isWerewolf())\n                {\n                    if (ptr.getClass().getTypeName() != typeid(ESM::NPC).name())\n                        return false;\n                }\n                else if (ptr.getClass().getTypeName() != typeid(ESM::Creature).name())\n                    return false;\n\n                if (ptr.getClass().getCreatureStats(ptr).isDead())\n                    return false;\n            }\n            if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr))\n                return false;\n            if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty())\n                return false;\n            return true;\n        }\n    };\n\n    void World::listDetectedReferences(const Ptr &ptr, std::vector<Ptr> &out, DetectionType type)\n    {\n        const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects();\n        float dist=0;\n        if (type == World::Detect_Creature)\n            dist = effects.get(ESM::MagicEffect::DetectAnimal).getMagnitude();\n        else if (type == World::Detect_Key)\n            dist = effects.get(ESM::MagicEffect::DetectKey).getMagnitude();\n        else if (type == World::Detect_Enchantment)\n            dist = effects.get(ESM::MagicEffect::DetectEnchantment).getMagnitude();\n\n        if (!dist)\n            return;\n\n        dist = feetToGameUnits(dist);\n\n        AddDetectedReferenceVisitor visitor (out, ptr, type, dist*dist);\n\n        for (CellStore* cellStore : mWorldScene->getActiveCells())\n        {\n            cellStore->forEach(visitor);\n        }\n    }\n\n    float World::feetToGameUnits(float feet)\n    {\n        // Original engine rounds size upward\n        static const int unitsPerFoot = ceil(Constants::UnitsPerFoot);\n        return feet * unitsPerFoot;\n    }\n\n    float World::getActivationDistancePlusTelekinesis()\n    {\n        float telekinesisRangeBonus =\n                    mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects()\n                    .get(ESM::MagicEffect::Telekinesis).getMagnitude();\n        telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus);\n\n        float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus;\n\n        return activationDistance;\n    }\n\n    MWWorld::Ptr World::getPlayerPtr()\n    {\n        return mPlayer->getPlayer();\n    }\n\n    MWWorld::ConstPtr World::getPlayerConstPtr() const\n    {\n        return mPlayer->getConstPlayer();\n    }\n\n    void World::updateDialogueGlobals()\n    {\n        MWWorld::Ptr player = getPlayerPtr();\n        int bounty = player.getClass().getNpcStats(player).getBounty();\n        int playerGold = player.getClass().getContainerStore(player).count(ContainerStore::sGoldId);\n\n        static float fCrimeGoldDiscountMult = mStore.get<ESM::GameSetting>().find(\"fCrimeGoldDiscountMult\")->mValue.getFloat();\n        static float fCrimeGoldTurnInMult = mStore.get<ESM::GameSetting>().find(\"fCrimeGoldTurnInMult\")->mValue.getFloat();\n\n        int discount = static_cast<int>(bounty * fCrimeGoldDiscountMult);\n        int turnIn = static_cast<int>(bounty * fCrimeGoldTurnInMult);\n\n        if (bounty > 0)\n        {\n            discount = std::max(1, discount);\n            turnIn = std::max(1, turnIn);\n        }\n\n        mGlobalVariables[\"pchascrimegold\"].setInteger((bounty <= playerGold) ? 1 : 0);\n\n        mGlobalVariables[\"pchasgolddiscount\"].setInteger((discount <= playerGold) ? 1 : 0);\n        mGlobalVariables[\"crimegolddiscount\"].setInteger(discount);\n\n        mGlobalVariables[\"crimegoldturnin\"].setInteger(turnIn);\n        mGlobalVariables[\"pchasturnin\"].setInteger((turnIn <= playerGold) ? 1 : 0);\n    }\n\n    void World::confiscateStolenItems(const Ptr &ptr)\n    {\n        MWWorld::ConstPtr prisonMarker = getClosestMarker( ptr, \"prisonmarker\" );\n        if ( prisonMarker.isEmpty() )\n        {\n            Log(Debug::Warning) << \"Failed to confiscate items: no closest prison marker found.\";\n            return;\n        }\n        std::string prisonName = prisonMarker.getCellRef().getDestCell();\n        if ( prisonName.empty() )\n        {\n            Log(Debug::Warning) << \"Failed to confiscate items: prison marker not linked to prison interior\";\n            return;\n        }\n        MWWorld::CellStore *prison = getInterior( prisonName );\n        if ( !prison )\n        {\n            Log(Debug::Warning) << \"Failed to confiscate items: failed to load cell \" << prisonName;\n            return;\n        }\n\n        MWWorld::Ptr closestChest = prison->search( \"stolen_goods\" );\n        if (!closestChest.isEmpty()) //Found a close chest\n        {\n            MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest);\n        }\n        else\n           Log(Debug::Warning) << \"Failed to confiscate items: no stolen_goods container found\";\n    }\n\n    void World::goToJail()\n    {\n        if (!mGoToJail)\n        {\n            // Reset bounty and forget the crime now, but don't change cell yet (the player should be able to read the dialog text first)\n            mGoToJail = true;\n            mPlayerInJail = true;\n\n            MWWorld::Ptr player = getPlayerPtr();\n\n            int bounty = player.getClass().getNpcStats(player).getBounty();\n            player.getClass().getNpcStats(player).setBounty(0);\n            mPlayer->recordCrimeId();\n            confiscateStolenItems(player);\n\n            static int iDaysinPrisonMod = mStore.get<ESM::GameSetting>().find(\"iDaysinPrisonMod\")->mValue.getInteger();\n            mDaysInPrison = std::max(1, bounty / iDaysinPrisonMod);\n\n            return;\n        }\n        else\n        {\n            mGoToJail = false;\n\n            MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);\n\n            MWBase::Environment::get().getWindowManager()->goToJail(mDaysInPrison);\n        }\n    }\n\n    bool World::isPlayerInJail() const\n    {\n        return mPlayerInJail;\n    }\n\n    void World::setPlayerTraveling(bool traveling)\n    {\n        mPlayerTraveling = traveling;\n    }\n\n    bool World::isPlayerTraveling() const\n    {\n        return mPlayerTraveling;\n    }\n\n    float World::getTerrainHeightAt(const osg::Vec3f& worldPos) const\n    {\n        return mRendering->getTerrainHeightAt(worldPos);\n    }\n\n    osg::Vec3f World::getHalfExtents(const ConstPtr& object, bool rendering) const\n    {\n        if (!object.getClass().isActor())\n            return mRendering->getHalfExtents(object);\n\n        // Handle actors separately because of bodyparts\n        if (rendering)\n            return mPhysics->getRenderingHalfExtents(object);\n        else\n            return mPhysics->getHalfExtents(object);\n    }\n\n    std::string World::exportSceneGraph(const Ptr &ptr)\n    {\n        std::string file = mUserDataPath + \"/openmw.osgt\";\n        if (!ptr.isEmpty())\n        {\n            mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);\n            mWorldScene->removeFromPagedRefs(ptr);\n        }\n        mRendering->exportSceneGraph(ptr, file, \"Ascii\");\n        return file;\n    }\n\n    void World::spawnRandomCreature(const std::string &creatureList)\n    {\n        const ESM::CreatureLevList* list = mStore.get<ESM::CreatureLevList>().find(creatureList);\n\n        static int iNumberCreatures = mStore.get<ESM::GameSetting>().find(\"iNumberCreatures\")->mValue.getInteger();\n        int numCreatures = 1 + Misc::Rng::rollDice(iNumberCreatures); // [1, iNumberCreatures]\n\n        for (int i=0; i<numCreatures; ++i)\n        {\n            std::string selectedCreature = MWMechanics::getLevelledItem(list, true);\n            if (selectedCreature.empty())\n                continue;\n\n            MWWorld::ManualRef ref(mStore, selectedCreature, 1);\n\n            /*\n                Start of tes3mp change (major)\n\n                Send an ID_OBJECT_SPAWN packet every time a random creature is spawned, then delete\n                the creature and wait for the server to send it back with a unique mpNum of its own\n            */\n            MWWorld::Ptr ptr = safePlaceObject(ref.getPtr(), getPlayerPtr(), getPlayerPtr().getCell(), 0, 220.f);\n\n            mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();\n            objectList->reset();\n            objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;\n            objectList->addObjectSpawn(ptr);\n            objectList->sendObjectSpawn();\n\n            deleteObject(ptr);\n            /*\n                End of tes3mp change (major)\n            */\n        }\n    }\n\n    void World::spawnBloodEffect(const Ptr &ptr, const osg::Vec3f &worldPosition)\n    {\n        if (ptr == getPlayerPtr() && Settings::Manager::getBool(\"hit fader\", \"GUI\"))\n            return;\n\n        std::string texture = Fallback::Map::getString(\"Blood_Texture_\" + std::to_string(ptr.getClass().getBloodTexture(ptr)));\n        if (texture.empty())\n            texture = Fallback::Map::getString(\"Blood_Texture_0\");\n\n        std::string model = \"meshes\\\\\" + Fallback::Map::getString(\"Blood_Model_\" + std::to_string(Misc::Rng::rollDice(3))); // [0, 2]\n\n        mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);\n    }\n\n    void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos, float scale, bool isMagicVFX)\n    {\n        mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX);\n    }\n\n    void World::explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const Ptr& caster, const Ptr& ignore, ESM::RangeType rangeType,\n                             const std::string& id, const std::string& sourceName, const bool fromProjectile)\n    {\n        std::map<MWWorld::Ptr, std::vector<ESM::ENAMstruct> > toApply;\n        for (const ESM::ENAMstruct& effectInfo : effects.mList)\n        {\n            const ESM::MagicEffect* effect = mStore.get<ESM::MagicEffect>().find(effectInfo.mEffectID);\n\n            if (effectInfo.mRange != rangeType || (effectInfo.mArea <= 0 && !ignore.isEmpty() && ignore.getClass().isActor()))\n                continue; // Not right range type, or not area effect and hit an actor\n\n            if (fromProjectile && effectInfo.mArea <= 0)\n                continue; // Don't play explosion for projectiles with 0-area effects\n\n            if (!fromProjectile && effectInfo.mRange == ESM::RT_Touch && !ignore.isEmpty() && !ignore.getClass().isActor() && !ignore.getClass().hasToolTip(ignore))\n                continue; // Don't play explosion for touch spells on non-activatable objects except when spell is from the projectile enchantment\n\n            // Spawn the explosion orb effect\n            const ESM::Static* areaStatic;\n            if (!effect->mArea.empty())\n                areaStatic = mStore.get<ESM::Static>().find (effect->mArea);\n            else\n                areaStatic = mStore.get<ESM::Static>().find (\"VFX_DefaultArea\");\n\n            std::string texture = effect->mParticle;\n\n            if (effectInfo.mArea <= 0)\n            {\n                if (effectInfo.mRange == ESM::RT_Target)\n                    mRendering->spawnEffect(\"meshes\\\\\" + areaStatic->mModel, texture, origin, 1.0f);\n                continue;\n            }\n            else\n                mRendering->spawnEffect(\"meshes\\\\\" + areaStatic->mModel, texture, origin, static_cast<float>(effectInfo.mArea * 2));\n\n            // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now)\n            static const std::string schools[] = {\n                \"alteration\", \"conjuration\", \"destruction\", \"illusion\", \"mysticism\", \"restoration\"\n            };\n            {\n                MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();\n                if(!effect->mAreaSound.empty())\n                    sndMgr->playSound3D(origin, effect->mAreaSound, 1.0f, 1.0f);\n                else\n                    sndMgr->playSound3D(origin, schools[effect->mData.mSchool]+\" area\", 1.0f, 1.0f);\n            }\n            // Get the actors in range of the effect\n            std::vector<MWWorld::Ptr> objects;\n            MWBase::Environment::get().getMechanicsManager()->getObjectsInRange(\n                        origin, feetToGameUnits(static_cast<float>(effectInfo.mArea)), objects);\n            for (const Ptr& affected : objects)\n            {\n                // Ignore actors without collisions here, otherwise it will be possible to hit actors outside processing range.\n                if (affected.getClass().isActor() && !isActorCollisionEnabled(affected))\n                    continue;\n\n                toApply[affected].push_back(effectInfo);\n            }\n        }\n\n        // Now apply the appropriate effects to each actor in range\n        for (auto& applyPair : toApply)\n        {\n            MWWorld::Ptr source = caster;\n            // Vanilla-compatible behaviour of never applying the spell to the caster\n            // (could be changed by mods later)\n            if (applyPair.first == caster)\n                continue;\n\n            if (applyPair.first == ignore)\n                continue;\n\n            if (source.isEmpty())\n                source = applyPair.first;\n\n            MWMechanics::CastSpell cast(source, applyPair.first);\n            cast.mHitPosition = origin;\n            cast.mId = id;\n            cast.mSourceName = sourceName;\n            cast.mStack = false;\n            ESM::EffectList effectsToApply;\n            effectsToApply.mList = applyPair.second;\n            cast.inflict(applyPair.first, caster, effectsToApply, rangeType, false, true);\n        }\n    }\n\n    void World::activate(const Ptr &object, const Ptr &actor)\n    {\n        breakInvisibility(actor);\n\n        if (object.getRefData().activate())\n        {\n            std::shared_ptr<MWWorld::Action> action = (object.getClass().activate(object, actor));\n            action->execute (actor);\n        }\n    }\n\n    struct ResetActorsVisitor\n    {\n        bool operator() (Ptr ptr)\n        {\n            if (ptr.getClass().isActor() && ptr.getCellRef().hasContentFile())\n            {\n                if (ptr.getCell()->movedHere(ptr))\n                    return true;\n\n                const ESM::Position& origPos = ptr.getCellRef().getPosition();\n                MWBase::Environment::get().getWorld()->moveObject(ptr, origPos.pos[0], origPos.pos[1], origPos.pos[2]);\n                MWBase::Environment::get().getWorld()->rotateObject(ptr, origPos.rot[0], origPos.rot[1], origPos.rot[2]);\n                ptr.getClass().adjustPosition(ptr, true);\n            }\n            return true;\n        }\n    };\n    void World::resetActors()\n    {\n        for (CellStore* cellstore : mWorldScene->getActiveCells())\n        {\n            ResetActorsVisitor visitor;\n            cellstore->forEach(visitor);\n        }\n    }\n\n    bool World::isWalkingOnWater(const ConstPtr &actor) const\n    {\n        const MWPhysics::Actor* physicActor = mPhysics->getActor(actor);\n        if (physicActor && physicActor->isWalkingOnWater())\n            return true;\n        return false;\n    }\n\n    osg::Vec3f World::aimToTarget(const ConstPtr &actor, const ConstPtr &target, bool isRangedCombat)\n    {\n        osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();\n        float heightRatio = isRangedCombat ? 2.f * Constants::TorsoHeight : 1.f;\n        weaponPos.z() += mPhysics->getHalfExtents(actor).z() * heightRatio;\n        osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target);\n        return (targetPos - weaponPos);\n    }\n\n    float World::getHitDistance(const ConstPtr &actor, const ConstPtr &target)\n    {\n        osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();\n        osg::Vec3f halfExtents = mPhysics->getHalfExtents(actor);\n        weaponPos.z() += halfExtents.z();\n\n        return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y();\n    }\n\n    void preload(MWWorld::Scene* scene, const ESMStore& store, const std::string& obj)\n    {\n        if (obj.empty())\n            return;\n        try\n        {\n            MWWorld::ManualRef ref(store, obj);\n            std::string model = ref.getPtr().getClass().getModel(ref.getPtr());\n            if (!model.empty())\n                scene->preload(model, ref.getPtr().getClass().useAnim());\n        }\n        catch(std::exception&)\n        {\n        }\n    }\n\n    void World::preloadEffects(const ESM::EffectList *effectList)\n    {\n        for (const ESM::ENAMstruct& effectInfo : effectList->mList)\n        {\n            const ESM::MagicEffect *effect = mStore.get<ESM::MagicEffect>().find(effectInfo.mEffectID);\n\n            if (MWMechanics::isSummoningEffect(effectInfo.mEffectID))\n            {\n                preload(mWorldScene.get(), mStore, \"VFX_Summon_Start\");\n                preload(mWorldScene.get(), mStore, MWMechanics::getSummonedCreature(effectInfo.mEffectID));\n            }\n\n            preload(mWorldScene.get(), mStore, effect->mCasting);\n            preload(mWorldScene.get(), mStore, effect->mHit);\n\n            if (effectInfo.mArea > 0)\n                preload(mWorldScene.get(), mStore, effect->mArea);\n            if (effectInfo.mRange == ESM::RT_Target)\n                preload(mWorldScene.get(), mStore, effect->mBolt);\n        }\n    }\n\n    DetourNavigator::Navigator* World::getNavigator() const\n    {\n        return mNavigator.get();\n    }\n\n    void World::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,\n            const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const\n    {\n        mRendering->updateActorPath(actor, path, halfExtents, start, end);\n    }\n\n    void World::removeActorPath(const MWWorld::ConstPtr& actor) const\n    {\n        mRendering->removeActorPath(actor);\n    }\n\n    void World::setNavMeshNumberToRender(const std::size_t value)\n    {\n        mRendering->setNavMeshNumber(value);\n    }\n\n    osg::Vec3f World::getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const\n    {\n        if (actor.isInCell() && actor.getCell()->isExterior())\n            return mDefaultHalfExtents; // Using default half extents for better performance\n        else\n            return getHalfExtents(actor);\n    }\n\n    bool World::hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const\n    {\n        const auto object = mPhysics->getObject(door);\n\n        if (!object)\n            return false;\n\n        btVector3 aabbMin;\n        btVector3 aabbMax;\n        object->getShapeInstance()->getCollisionShape()->getAabb(btTransform::getIdentity(), aabbMin, aabbMax);\n\n        const auto toLocal = object->getTransform().inverse();\n        const auto localFrom = toLocal(Misc::Convert::toBullet(position));\n        const auto localTo = toLocal(Misc::Convert::toBullet(destination));\n\n        btScalar hitDistance = 1;\n        btVector3 hitNormal;\n        return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal);\n    }\n\n    bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const\n    {\n        return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore);\n    }\n\n    void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const\n    {\n        mNavigator->reportStats(frameNumber, stats);\n        mPhysics->reportStats(frameNumber, stats);\n    }\n\n    void World::updateSkyDate()\n    {\n        ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp();\n        mRendering->skySetDate(currentDate.mDay, currentDate.mMonth);\n    }\n\n    std::vector<MWWorld::Ptr> World::getAll(const std::string& id)\n    {\n        return mCells.getAll(id);\n    }\n}\n"
  },
  {
    "path": "apps/openmw/mwworld/worldimp.hpp",
    "content": "#ifndef GAME_MWWORLD_WORLDIMP_H\n#define GAME_MWWORLD_WORLDIMP_H\n\n#include <osg/ref_ptr>\n\n#include <components/settings/settings.hpp>\n\n#include \"../mwbase/world.hpp\"\n\n#include \"ptr.hpp\"\n#include \"scene.hpp\"\n#include \"esmstore.hpp\"\n#include \"cells.hpp\"\n#include \"localscripts.hpp\"\n#include \"timestamp.hpp\"\n#include \"globals.hpp\"\n#include \"contentloader.hpp\"\n\nnamespace osg\n{\n    class Group;\n    class Stats;\n}\n\nnamespace osgViewer\n{\n    class Viewer;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace SceneUtil\n{\n    class WorkQueue;\n}\n\nnamespace ESM\n{\n    struct Position;\n}\n\nnamespace Files\n{\n    class Collections;\n}\n\nnamespace MWRender\n{\n    class SkyManager;\n    class Animation;\n    class Camera;\n}\n\nnamespace ToUTF8\n{\n    class Utf8Encoder;\n}\n\nnamespace MWPhysics\n{\n    class Object;\n}\n\nnamespace MWWorld\n{\n    class DateTimeManager;\n    class WeatherManager;\n    class Player;\n    class ProjectileManager;\n\n    /// \\brief The game world and its visual representation\n\n    class World final: public MWBase::World\n    {\n        private:\n            Resource::ResourceSystem* mResourceSystem;\n\n            std::vector<ESM::ESMReader> mEsm;\n            MWWorld::ESMStore mStore;\n            LocalScripts mLocalScripts;\n            MWWorld::Globals mGlobalVariables;\n\n            Cells mCells;\n\n            std::string mCurrentWorldSpace;\n\n            std::unique_ptr<MWWorld::Player> mPlayer;\n            std::unique_ptr<MWPhysics::PhysicsSystem> mPhysics;\n            std::unique_ptr<DetourNavigator::Navigator> mNavigator;\n            std::unique_ptr<MWRender::RenderingManager> mRendering;\n            std::unique_ptr<MWWorld::Scene> mWorldScene;\n            std::unique_ptr<MWWorld::WeatherManager> mWeatherManager;\n            std::unique_ptr<MWWorld::DateTimeManager> mCurrentDate;\n            std::shared_ptr<ProjectileManager> mProjectileManager;\n\n            bool mSky;\n            bool mGodMode;\n            bool mScriptsEnabled;\n            bool mDiscardMovements;\n            std::vector<std::string> mContentFiles;\n\n            std::string mUserDataPath;\n\n            osg::Vec3f mDefaultHalfExtents;\n            bool mShouldUpdateNavigator;\n\n            int mActivationDistanceOverride;\n\n            std::string mStartCell;\n\n            float mSwimHeightScale;\n\n            float mDistanceToFacedObject;\n\n            bool mTeleportEnabled;\n            bool mLevitationEnabled;\n            bool mGoToJail;\n            int mDaysInPrison;\n            bool mPlayerTraveling;\n            bool mPlayerInJail;\n\n            float mSpellPreloadTimer;\n\n            std::map<MWWorld::Ptr, MWWorld::DoorState> mDoorStates;\n            ///< only holds doors that are currently moving. 1 = opening, 2 = closing\n\n            // not implemented\n            World (const World&);\n            World& operator= (const World&);\n\n            void updateWeather(float duration, bool paused = false);\n\n            void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags);\n\n            Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos);\n\n            void updateSoundListener();\n            void updatePlayer();\n\n            void preloadSpells();\n\n            MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true);\n\n            /*\n                Start of tes3mp change (major)\n\n                This has been turned into a public method so it can be used in\n                multiplayer's different approach to placing items\n            */\n    public:\n            void PCDropped(const Ptr& item);\n            /*\n                End of tes3mp change (major)\n            */\n\n    private:\n            bool rotateDoor(const Ptr door, DoorState state, float duration);\n\n            void processDoors(float duration);\n            ///< Run physics simulation and modify \\a world accordingly.\n\n            void doPhysics(float duration, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);\n            ///< Run physics simulation and modify \\a world accordingly.\n\n            void updateNavigator();\n\n            void updateNavigatorObject(const MWPhysics::Object& object);\n\n            void ensureNeededRecords();\n\n            void fillGlobalVariables();\n\n            void updateSkyDate();\n\n            /**\n             * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon)\n             * @param fileCollections- Container which holds content file names and their paths\n             * @param content - Container which holds content file names\n             * @param contentLoader -\n             */\n            void loadContentFiles(const Files::Collections& fileCollections,\n                const std::vector<std::string>& content, const std::vector<std::string>& groundcover, ContentLoader& contentLoader);\n\n            float feetToGameUnits(float feet);\n            float getActivationDistancePlusTelekinesis();\n\n            MWWorld::ConstPtr getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id );\n            MWWorld::ConstPtr getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id );\n\n        public:\n            // FIXME\n            void addContainerScripts(const Ptr& reference, CellStore* cell) override;\n            void removeContainerScripts(const Ptr& reference) override;\n\n            World (\n                osgViewer::Viewer* viewer,\n                osg::ref_ptr<osg::Group> rootNode,\n                Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,\n                const Files::Collections& fileCollections,\n                const std::vector<std::string>& contentFiles,\n                const std::vector<std::string>& groundcoverFiles,\n                ToUTF8::Utf8Encoder* encoder, int activationDistanceOverride,\n                const std::string& startCell, const std::string& startupScript,\n                const std::string& resourcePath, const std::string& userDataPath);\n\n            virtual ~World();\n\n            void startNewGame (bool bypass) override;\n            ///< \\param bypass Bypass regular game start.\n\n            void clear() override;\n\n            int countSavedGameRecords() const override;\n            int countSavedGameCells() const override;\n\n            void write (ESM::ESMWriter& writer, Loading::Listener& progress) const override;\n\n            void readRecord (ESM::ESMReader& reader, uint32_t type,\n                const std::map<int, int>& contentFileMap) override;\n\n            CellStore *getExterior (int x, int y) override;\n\n            CellStore *getInterior (const std::string& name) override;\n\n            CellStore *getCell (const ESM::CellId& id) override;\n\n            void testExteriorCells() override;\n            void testInteriorCells() override;\n\n            //switch to POV before showing player's death animation\n            void useDeathCamera() override;\n\n            void setWaterHeight(const float height) override;\n\n            void rotateWorldObject (const MWWorld::Ptr& ptr, osg::Quat rotate) override;\n\n            bool toggleWater() override;\n            bool toggleWorld() override;\n            bool toggleBorders() override;\n\n            void adjustSky() override;\n\n            Player& getPlayer() override;\n            MWWorld::Ptr getPlayerPtr() override;\n            MWWorld::ConstPtr getPlayerConstPtr() const override;\n\n            const MWWorld::ESMStore& getStore() const override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to get the World's ESMStore as a non-const\n            */\n            MWWorld::ESMStore& getModifiableStore() override;\n            /*\n                End of tes3mp addition\n            */\n\n            std::vector<ESM::ESMReader>& getEsmReader() override;\n\n            LocalScripts& getLocalScripts() override;\n\n            bool hasCellChanged() const override;\n            ///< Has the set of active cells changed, since the last frame?\n\n            bool isCellExterior() const override;\n\n            bool isCellQuasiExterior() const override;\n\n            osg::Vec2f getNorthVector (const CellStore* cell) override;\n            ///< get north vector for given interior cell\n\n            void getDoorMarkers (MWWorld::CellStore* cell, std::vector<DoorMarker>& out) override;\n            ///< get a list of teleport door markers for a given cell, to be displayed on the local map\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether global variables exist and to create\n                new ones\n            */\n            bool hasGlobal(const std::string& name);\n\n            void createGlobal(const std::string& name, ESM::VarType varType);\n            /*\n                End of tes3mp addition\n            */\n\n            void setGlobalInt (const std::string& name, int value) override;\n            ///< Set value independently from real type.\n\n            void setGlobalFloat (const std::string& name, float value) override;\n            ///< Set value independently from real type.\n\n            int getGlobalInt (const std::string& name) const override;\n            ///< Get value independently from real type.\n\n            float getGlobalFloat (const std::string& name) const override;\n            ///< Get value independently from real type.\n\n            char getGlobalVariableType (const std::string& name) const override;\n            ///< Return ' ', if there is no global variable with this name.\n\n            std::string getCellName (const MWWorld::CellStore *cell = nullptr) const override;\n            ///< Return name of the cell.\n            ///\n            /// \\note If cell==0, the cell the player is currently in will be used instead to\n            /// generate a name.\n            std::string getCellName(const ESM::Cell* cell) const override;\n\n            void removeRefScript (MWWorld::RefData *ref) override;\n            //< Remove the script attached to ref from mLocalScripts\n\n            Ptr getPtr (const std::string& name, bool activeOnly) override;\n            ///< Return a pointer to a liveCellRef with the given name.\n            /// \\param activeOnly do non search inactive cells.\n\n            Ptr searchPtr (const std::string& name, bool activeOnly, bool searchInContainers = false) override;\n            ///< Return a pointer to a liveCellRef with the given name.\n            /// \\param activeOnly do not search inactive cells.\n\n            Ptr searchPtrViaActorId (int actorId) override;\n            ///< Search is limited to the active cells.\n\n            Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to find a Ptr in any active cell based on its refNum and mpNum\n            */\n            Ptr searchPtrViaUniqueIndex(int refNum, int mpNum) override;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to update all Ptrs in active cells that have a certain refId\n            */\n            void updatePtrsWithRefId(std::string refId) override;\n            /*\n                End of tes3mp addition\n            */\n\n            MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) override;\n            ///< Return a pointer to a liveCellRef which contains \\a ptr.\n            /// \\note Search is limited to the active cells.\n\n            void adjustPosition (const Ptr& ptr, bool force) override;\n            ///< Adjust position after load to be on ground. Must be called after model load.\n            /// @param force do this even if the ptr is flying\n\n            void fixPosition () override;\n            ///< Attempt to fix position so that the player is not stuck inside the geometry.\n\n            void enable (const Ptr& ptr) override;\n\n            void disable (const Ptr& ptr) override;\n\n            void advanceTime (double hours, bool incremental = false) override;\n            ///< Advance in-game time.\n\n            std::string getMonthName (int month = -1) const override;\n            ///< Return name of month (-1: current month)\n\n            TimeStamp getTimeStamp() const override;\n            ///< Return current in-game time and number of day since new game start.\n\n            ESM::EpochTimeStamp getEpochTimeStamp() const override;\n            ///< Return current in-game date and time.\n\n            bool toggleSky() override;\n            ///< \\return Resulting mode\n\n            void changeWeather (const std::string& region, const unsigned int id) override;\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set a specific weather state for a region from elsewhere\n                in the code\n            */\n            void setRegionWeather(const std::string& region, const unsigned int currentWeather, const unsigned int nextWeather,\n                const unsigned int queuedWeather, const float transitionFactor, bool force) override;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether the local WeatherManager has the\n                ability to create weather changes\n            */\n            bool getWeatherCreationState() override;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to enable and disable the local WeatherManager's ability\n                to create weather changes\n            */\n            void setWeatherCreationState(bool state) override;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to send the current weather in a WorldWeather packet\n                when requested from elsewhere in the code\n            */\n            void sendWeather() override;\n            /*\n                End of tes3mp addition\n            */\n\n            int getCurrentWeather() const override;\n\n            unsigned int getNightDayMode() const override;\n\n            int getMasserPhase() const override;\n\n            int getSecundaPhase() const override;\n\n            void setMoonColour (bool red) override;\n\n            void modRegion(const std::string &regionid, const std::vector<char> &chances) override;\n\n            float getTimeScaleFactor() const override;\n\n            void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true) override;\n            ///< Move to interior cell.\n            ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes\n\n            void changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true) override;\n            ///< Move to exterior cell.\n            ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes\n\n            void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true) override;\n            ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes\n\n            const ESM::Cell *getExterior (const std::string& cellName) const override;\n            ///< Return a cell matching the given name or a 0-pointer, if there is no such cell.\n\n            void markCellAsUnchanged() override;\n\n            MWWorld::Ptr getFacedObject() override;\n            ///< Return pointer to the object the player is looking at, if it is within activation range\n\n            float getDistanceToFacedObject() override;\n\n            /// Returns a pointer to the object the provided object would hit (if within the\n            /// specified distance), and the point where the hit occurs. This will attempt to\n            /// use the \"Head\" node as a basis.\n            std::pair<MWWorld::Ptr,osg::Vec3f> getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector<MWWorld::Ptr> &targets) override;\n\n            /// @note No-op for items in containers. Use ContainerStore::removeItem instead.\n            void deleteObject (const Ptr& ptr) override;\n\n            void undeleteObject (const Ptr& ptr) override;\n\n            MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) override;\n            ///< @return an updated Ptr in case the Ptr's cell changes\n\n            MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override;\n            ///< @return an updated Ptr\n\n            MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive, bool ignoreCollisions) override;\n            ///< @return an updated Ptr\n\n            void scaleObject (const Ptr& ptr, float scale) override;\n\n            /// World rotates object, uses radians\n            /// @note Rotations via this method use a different rotation order than the initial rotations in the CS. This\n            /// could be considered a bug, but is needed for MW compatibility.\n            /// \\param adjust indicates rotation should be set or adjusted\n            void rotateObject (const Ptr& ptr, float x, float y, float z,\n                MWBase::RotationFlags flags = MWBase::RotationFlag_inverseOrder) override;\n\n            MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) override;\n            ///< Place an object. Makes a copy of the Ptr.\n\n            MWWorld::Ptr safePlaceObject (const MWWorld::ConstPtr& ptr, const MWWorld::ConstPtr& referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance) override;\n            ///< Place an object in a safe place next to \\a referenceObject. \\a direction and \\a distance specify the wanted placement\n            /// relative to \\a referenceObject (but the object may be placed somewhere else if the wanted location is obstructed).\n\n            float getMaxActivationDistance() override;\n\n            void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false)\n                const override;\n            ///< Convert cell numbers to position.\n\n            void positionToIndex (float x, float y, int &cellX, int &cellY) const override;\n            ///< Convert position to cell numbers\n\n            void queueMovement(const Ptr &ptr, const osg::Vec3f &velocity) override;\n            ///< Queues movement for \\a ptr (in local space), to be applied in the next call to\n            /// doPhysics.\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the inertial force of a Ptr directly\n            */\n            void setInertialForce(const Ptr& ptr, const osg::Vec3f &force);\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set whether a Ptr is on the ground or not, needed for proper\n                synchronization in multiplayer\n            */\n            void setOnGround(const Ptr& ptr, bool onGround);\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to set the physics framerate from elsewhere\n            */\n            void setPhysicsFramerate(float physFramerate);\n            /*\n                End of tes3mp addition\n            */\n\n            void updateAnimatedCollisionShape(const Ptr &ptr) override;\n\n            const MWPhysics::RayCastingInterface* getRayCasting() const override;\n\n            bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) override;\n            ///< cast a Ray and return true if there is an object in the ray path.\n\n            bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override;\n\n            bool castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore) override;\n\n            void setActorCollisionMode(const Ptr& ptr, bool internal, bool external) override;\n            bool isActorCollisionEnabled(const Ptr& ptr) override;\n\n            bool toggleCollisionMode() override;\n            ///< Toggle collision mode for player. If disabled player object should ignore\n            /// collisions and gravity.\n            ///< \\return Resulting mode\n\n            bool toggleRenderMode (MWRender::RenderMode mode) override;\n            ///< Toggle a render mode.\n            ///< \\return Resulting mode\n\n            const ESM::Potion *createRecord (const ESM::Potion& record) override;\n            ///< Create a new record (of type potion) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Spell *createRecord (const ESM::Spell& record) override;\n            ///< Create a new record (of type spell) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Class *createRecord (const ESM::Class& record) override;\n            ///< Create a new record (of type class) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Cell *createRecord (const ESM::Cell& record) override;\n            ///< Create a new record (of type cell) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::NPC *createRecord(const ESM::NPC &record) override;\n            ///< Create a new record (of type npc) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Creature *createRecord(const ESM::Creature &record) override;\n            ///< Create a new record (of type creature) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Armor *createRecord (const ESM::Armor& record) override;\n            ///< Create a new record (of type armor) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Weapon *createRecord (const ESM::Weapon& record) override;\n            ///< Create a new record (of type weapon) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Clothing *createRecord (const ESM::Clothing& record) override;\n            ///< Create a new record (of type clothing) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Enchantment *createRecord (const ESM::Enchantment& record) override;\n            ///< Create a new record (of type enchantment) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::Book *createRecord (const ESM::Book& record) override;\n            ///< Create a new record (of type book) in the ESM store.\n            /// \\return pointer to created record\n\n            const ESM::CreatureLevList *createOverrideRecord (const ESM::CreatureLevList& record) override;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            const ESM::ItemLevList *createOverrideRecord (const ESM::ItemLevList& record) override;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            const ESM::Creature *createOverrideRecord (const ESM::Creature& record) override;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            const ESM::NPC *createOverrideRecord (const ESM::NPC& record) override;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            const ESM::Container *createOverrideRecord (const ESM::Container& record) override;\n            ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.\n            /// \\return pointer to created record\n\n            void update (float duration, bool paused) override;\n            void updatePhysics (float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) override;\n\n            void updateWindowManager () override;\n\n            MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) override;\n            ///< copy and place an object into the gameworld at the specified cursor position\n            /// @param object\n            /// @param cursor X (relative 0-1)\n            /// @param cursor Y (relative 0-1)\n            /// @param number of objects to place\n\n            MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) override;\n            ///< copy and place an object into the gameworld at the given actor's position\n            /// @param actor giving the dropped object position\n            /// @param object\n            /// @param number of objects to place\n\n            bool canPlaceObject(float cursorX, float cursorY) override;\n            ///< @return true if it is possible to place on object at specified cursor location\n\n            void processChangedSettings(const Settings::CategorySettingVector& settings) override;\n\n            bool isFlying(const MWWorld::Ptr &ptr) const override;\n            bool isSlowFalling(const MWWorld::Ptr &ptr) const override;\n            ///Is the head of the creature underwater?\n            bool isSubmerged(const MWWorld::ConstPtr &object) const override;\n            bool isSwimming(const MWWorld::ConstPtr &object) const override;\n            bool isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const override;\n            bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const override;\n            bool isWading(const MWWorld::ConstPtr &object) const override;\n            bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const override;\n            bool isOnGround(const MWWorld::Ptr &ptr) const override;\n\n            osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const override;\n\n            void togglePOV(bool force = false) override;\n\n            bool isFirstPerson() const override;\n            bool isPreviewModeEnabled() const override;\n\n            void togglePreviewMode(bool enable) override;\n\n            bool toggleVanityMode(bool enable) override;\n\n            void allowVanityMode(bool allow) override;\n            bool vanityRotateCamera(float * rot) override;\n            void adjustCameraDistance(float dist) override;\n\n            void applyDeferredPreviewRotationToPlayer(float dt) override;\n            void disableDeferredPreviewRotation() override;\n\n            void saveLoaded() override;\n\n            void setupPlayer() override;\n            void renderPlayer() override;\n\n            /// open or close a non-teleport door (depending on current state)\n            void activateDoor(const MWWorld::Ptr& door) override;\n\n            /// update movement state of a non-teleport door as specified\n            /// @param state see MWClass::setDoorState\n            /// @note throws an exception when invoked on a teleport door\n            void activateDoor(const MWWorld::Ptr& door, MWWorld::DoorState state) override;\n\n            /*\n                Start of tes3mp addition\n\n                Useful self-contained method for saving door states\n            */\n            void saveDoorState(const MWWorld::Ptr& door, MWWorld::DoorState state) override;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to check whether a cell is active\n            */\n            bool isCellActive(const ESM::Cell& cell) override;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to unload a cell from elsewhere\n            */\n            void unloadCell(const ESM::Cell& cell) override;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Make it possible to unload all active cells from elsewhere\n            */\n            void unloadActiveCells() override;\n            /*\n                End of tes3mp addition\n            */\n\n            /*\n                Start of tes3mp addition\n\n                Clear the CellStore for a specific Cell from elsewhere\n            */\n            virtual void clearCellStore(const ESM::Cell& cell) override;\n            /*\n                End of tes3mp addition\n            */\n\n            void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) override; ///< get a list of actors standing on \\a object\n            bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \\a object\n            bool getActorStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if any actor is standing on \\a object\n            bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) override; ///< @return true if the player is colliding with \\a object\n            bool getActorCollidingWith (const MWWorld::ConstPtr& object) override; ///< @return true if any actor is colliding with \\a object\n            void hurtStandingActors (const MWWorld::ConstPtr& object, float dmgPerSecond) override;\n            ///< Apply a health difference to any actors standing on \\a object.\n            /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed.\n            void hurtCollidingActors (const MWWorld::ConstPtr& object, float dmgPerSecond) override;\n            ///< Apply a health difference to any actors colliding with \\a object.\n            /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed.\n\n            float getWindSpeed() override;\n\n            void getContainersOwnedBy (const MWWorld::ConstPtr& npc, std::vector<MWWorld::Ptr>& out) override;\n            ///< get all containers in active cells owned by this Npc\n            void getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector<MWWorld::Ptr>& out) override;\n            ///< get all items in active cells owned by this Npc\n\n            bool getLOS(const MWWorld::ConstPtr& actor,const MWWorld::ConstPtr& targetActor) override;\n            ///< get Line of Sight (morrowind stupid implementation)\n\n            float getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist, bool includeWater = false) override;\n\n            void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override;\n\n            RestPermitted canRest() const override;\n            ///< check if the player is allowed to rest\n\n            void rest(double hours) override;\n            void rechargeItems(double duration, bool activeOnly) override;\n\n            /// \\todo Probably shouldn't be here\n            MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override;\n            const MWRender::Animation* getAnimation(const MWWorld::ConstPtr &ptr) const override;\n            void reattachPlayerCamera() override;\n\n            /// \\todo this does not belong here\n            void screenshot (osg::Image* image, int w, int h) override;\n            bool screenshot360 (osg::Image* image) override;\n\n            /// Find center of exterior cell above land surface\n            /// \\return false if exterior with given name not exists, true otherwise\n            bool findExteriorPosition(const std::string &name, ESM::Position &pos) override;\n\n            /// Find position in interior cell near door entrance\n            /// \\return false if interior with given name not exists, true otherwise\n            bool findInteriorPosition(const std::string &name, ESM::Position &pos) override;\n\n            /// Enables or disables use of teleport spell effects (recall, intervention, etc).\n            void enableTeleporting(bool enable) override;\n\n            /// Returns true if teleport spell effects are allowed.\n            bool isTeleportingEnabled() const override;\n\n            /// Enables or disables use of levitation spell effect.\n            void enableLevitation(bool enable) override;\n\n            /// Returns true if levitation spell effect is allowed.\n            bool isLevitationEnabled() const override;\n\n            bool getGodModeState() const override;\n\n            bool toggleGodMode() override;\n\n            bool toggleScripts() override;\n            bool getScriptsEnabled() const override;\n\n            /**\n             * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met.\n             * @param actor\n             * @return true if the spell can be casted (i.e. the animation should start)\n             */\n            bool startSpellCast (const MWWorld::Ptr& actor) override;\n\n            /**\n             * @brief Cast the actual spell, should be called mid-animation\n             * @param actor\n             */\n            void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) override;\n\n            void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override;\n            void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,\n                                           const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) override;\n            void updateProjectilesCasters() override;\n\n            void applyLoopingParticles(const MWWorld::Ptr& ptr) override;\n\n            const std::vector<std::string>& getContentFiles() const override;\n            void breakInvisibility (const MWWorld::Ptr& actor) override;\n\n            // Allow NPCs to use torches?\n            bool useTorches() const override;\n\n            bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) override;\n\n            /// Teleports \\a ptr to the closest reference of \\a id (e.g. DivineMarker, PrisonMarker, TempleMarker)\n            /// @note id must be lower case\n            void teleportToClosestMarker (const MWWorld::Ptr& ptr,\n                                                  const std::string& id) override;\n\n            /// List all references (filtered by \\a type) detected by \\a ptr. The range\n            /// is determined by the current magnitude of the \"Detect X\" magic effect belonging to \\a type.\n            /// @note This also works for references in containers.\n            void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector<MWWorld::Ptr>& out,\n                                                  DetectionType type) override;\n\n            /// Update the value of some globals according to the world state, which may be used by dialogue entries.\n            /// This should be called when initiating a dialogue.\n            void updateDialogueGlobals() override;\n\n            /// Moves all stolen items from \\a ptr to the closest evidence chest.\n            void confiscateStolenItems(const MWWorld::Ptr& ptr) override;\n\n            void goToJail () override;\n\n            /// Spawn a random creature from a levelled list next to the player\n            void spawnRandomCreature(const std::string& creatureList) override;\n\n            /// Spawn a blood effect for \\a ptr at \\a worldPosition\n            void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override;\n\n            void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) override;\n\n            void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore,\n                                      ESM::RangeType rangeType, const std::string& id, const std::string& sourceName,\n                                      const bool fromProjectile=false) override;\n\n            void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) override;\n\n            /// @see MWWorld::WeatherManager::isInStorm\n            bool isInStorm() const override;\n\n            /// @see MWWorld::WeatherManager::getStormDirection\n            osg::Vec3f getStormDirection() const override;\n\n            /// Resets all actors in the current active cells to their original location within that cell.\n            void resetActors() override;\n\n            bool isWalkingOnWater (const MWWorld::ConstPtr& actor) const override;\n\n            /// Return a vector aiming the actor's weapon towards a target.\n            /// @note The length of the vector is the distance between actor and target.\n            osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat) override;\n\n            /// Return the distance between actor's weapon and target's collision box.\n            float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) override;\n\n            bool isPlayerInJail() const override;\n\n            void setPlayerTraveling(bool traveling) override;\n            bool isPlayerTraveling() const override;\n\n            /// Return terrain height at \\a worldPos position.\n            float getTerrainHeightAt(const osg::Vec3f& worldPos) const override;\n\n            /// Return physical or rendering half extents of the given actor.\n            osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering=false) const override;\n\n            /// Export scene graph to a file and return the filename.\n            /// \\param ptr object to export scene graph for (if empty, export entire scene graph)\n            std::string exportSceneGraph(const MWWorld::Ptr& ptr) override;\n\n            /// Preload VFX associated with this effect list\n            void preloadEffects(const ESM::EffectList* effectList) override;\n\n            DetourNavigator::Navigator* getNavigator() const override;\n\n            void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,\n                    const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const override;\n\n            void removeActorPath(const MWWorld::ConstPtr& actor) const override;\n\n            void setNavMeshNumberToRender(const std::size_t value) override;\n\n            /// Return physical half extents of the given actor to be used in pathfinding\n            osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const override;\n\n            bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override;\n\n            bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const override;\n\n            void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;\n\n            std::vector<MWWorld::Ptr> getAll(const std::string& id) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw-mp/CMakeLists.txt",
    "content": "project(tes3mp-server)\n\noption(ENABLE_BREAKPAD \"Enable Google Breakpad for Crash reporting\" OFF)\n\nif(ENABLE_BREAKPAD)\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -DENABLE_BREAKPAD\")\n    if (UNIX)\n        set(Breakpad_Headers \"${CMAKE_SOURCE_DIR}/extern/breakpad/src/client/linux\")\n        set(Breakpad_Library \"${CMAKE_SOURCE_DIR}/extern/breakpad/src/client/linux/libbreakpad_client.a\")\n    elseif(WIN32)\n        set(Breakpad_Headers \"${CMAKE_SOURCE_DIR}/extern/breakpad/src/client/windows\")\n        set(Breakpad_Library \"-lbreakpad_client\")\n    endif (UNIX)\n    include_directories(${CMAKE_SOURCE_DIR}/extern/breakpad/src ${Breakpad_Headers})\nendif(ENABLE_BREAKPAD)\n\noption(BUILD_WITH_LUA \"Enable Lua language\" ON)\nif(BUILD_WITH_LUA)\n\n    find_package(LuaJit REQUIRED)\n\n    MESSAGE(STATUS \"Found LuaJit_LIBRARIES: ${LuaJit_LIBRARIES}\")\n    MESSAGE(STATUS \"Found LuaJit_INCLUDE_DIRS: ${LuaJit_INCLUDE_DIRS}\")\n\n    set(LuaScript_Sources\n            Script/LangLua/LangLua.cpp\n            Script/LangLua/LuaFunc.cpp)\n    set(LuaScript_Headers ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/LuaBridge ${CMAKE_SOURCE_DIR}/extern/LuaBridge/detail\n            Script/LangLua/LangLua.hpp)\n\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -DENABLE_LUA\")\n    include_directories(SYSTEM ${LuaJit_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/extern/LuaBridge)\nendif(BUILD_WITH_LUA)\n\ninclude_directories(${CMAKE_SOURCE_DIR}/extern/PicoSHA2)\n\nset(NativeScript_Sources\n        Script/LangNative/LangNative.cpp\n        )\nset(NativeScript_Headers\n        Script/LangNative/LangNative.hpp\n        )\n\n# local files\nset(SERVER\n    main.cpp\n    Player.cpp\n    Networking.cpp\n    MasterClient.cpp\n    Cell.cpp\n    CellController.cpp\n    Utils.cpp\n    Script/Script.cpp Script/ScriptFunction.cpp\n    Script/ScriptFunctions.cpp\n\n    Script/Functions/Actors.cpp Script/Functions/Objects.cpp Script/Functions/Miscellaneous.cpp\n    Script/Functions/Worldstate.cpp\n\n    Script/Functions/Books.cpp Script/Functions/Cells.cpp Script/Functions/CharClass.cpp\n    Script/Functions/Chat.cpp Script/Functions/Dialogue.cpp Script/Functions/Factions.cpp\n    Script/Functions/GUI.cpp Script/Functions/Items.cpp Script/Functions/Mechanics.cpp\n    Script/Functions/Positions.cpp Script/Functions/Quests.cpp Script/Functions/RecordsDynamic.cpp\n    Script/Functions/Server.cpp Script/Functions/Settings.cpp Script/Functions/Shapeshift.cpp\n    Script/Functions/Spells.cpp Script/Functions/Stats.cpp Script/Functions/Timer.cpp\n\n    Script/API/TimerAPI.cpp Script/API/PublicFnAPI.cpp\n        ${LuaScript_Sources}\n        ${NativeScript_Sources}\n\n)\n\nset(SERVER_HEADER\n        Script/Types.hpp Script/Script.hpp Script/SystemInterface.hpp\n        Script/ScriptFunction.hpp Script/Platform.hpp Script/Language.hpp\n        Script/ScriptFunctions.hpp Script/API/TimerAPI.hpp Script/API/PublicFnAPI.hpp\n        ${LuaScript_Headers}\n        ${NativeScript_Headers}\n)\nsource_group(tes3mp-server FILES ${SERVER} ${SERVER_HEADER})\n\nset(PROCESSORS_ACTOR\n        processors/actor/ProcessorActorAI.hpp processors/actor/ProcessorActorAnimFlags.hpp\n        processors/actor/ProcessorActorAnimPlay.hpp processors/actor/ProcessorActorAttack.hpp\n        processors/actor/ProcessorActorCast.hpp processors/actor/ProcessorActorCellChange.hpp\n        processors/actor/ProcessorActorDeath.hpp processors/actor/ProcessorActorEquipment.hpp\n        processors/actor/ProcessorActorList.hpp processors/actor/ProcessorActorPosition.hpp\n        processors/actor/ProcessorActorSpeech.hpp processors/actor/ProcessorActorSpellsActive.hpp\n        processors/actor/ProcessorActorStatsDynamic.hpp processors/actor/ProcessorActorTest.hpp\n        )\n\nsource_group(tes3mp-server\\\\processors\\\\actor FILES ${PROCESSORS_ACTOR})\n\nset(PROCESSORS_PLAYER\n        processors/player/ProcessorChatMsg.hpp processors/player/ProcessorGUIMessageBox.hpp\n        processors/player/ProcessorPlayerAnimFlags.hpp processors/player/ProcessorPlayerAnimPlay.hpp\n        processors/player/ProcessorPlayerAttack.hpp processors/player/ProcessorPlayerAttribute.hpp\n        processors/player/ProcessorPlayerBook.hpp processors/player/ProcessorPlayerBounty.hpp\n        processors/player/ProcessorPlayerCast.hpp processors/player/ProcessorPlayerCellChange.hpp\n        processors/player/ProcessorPlayerCellState.hpp processors/player/ProcessorPlayerCharClass.hpp\n        processors/player/ProcessorPlayerCharGen.hpp processors/player/ProcessorPlayerCooldowns.hpp\n        processors/player/ProcessorPlayerDeath.hpp processors/player/ProcessorPlayerDisposition.hpp\n        processors/player/ProcessorPlayerEquipment.hpp processors/player/ProcessorPlayerFaction.hpp\n        processors/player/ProcessorPlayerInput.hpp processors/player/ProcessorPlayerInventory.hpp\n        processors/player/ProcessorPlayerItemUse.hpp processors/player/ProcessorPlayerJournal.hpp\n        processors/player/ProcessorPlayerPlaceholder.hpp processors/player/ProcessorPlayerLevel.hpp\n        processors/player/ProcessorPlayerMiscellaneous.hpp processors/player/ProcessorPlayerPosition.hpp\n        processors/player/ProcessorPlayerQuickKeys.hpp processors/player/ProcessorPlayerRest.hpp\n        processors/player/ProcessorPlayerResurrect.hpp processors/player/ProcessorPlayerShapeshift.hpp\n        processors/player/ProcessorPlayerSkill.hpp processors/player/ProcessorPlayerSpeech.hpp\n        processors/player/ProcessorPlayerSpellbook.hpp processors/player/ProcessorPlayerSpellsActive.hpp\n        processors/player/ProcessorPlayerStatsDynamic.hpp processors/player/ProcessorPlayerTopic.hpp\n        )\n\nsource_group(tes3mp-server\\\\processors\\\\player FILES ${PROCESSORS_PLAYER})\n\nset(PROCESSORS_OBJECT\n        processors/object/ProcessorConsoleCommand.hpp processors/object/ProcessorContainer.hpp\n        processors/object/ProcessorDoorState.hpp processors/object/ProcessorMusicPlay.hpp\n        processors/object/ProcessorObjectActivate.hpp processors/object/ProcessorObjectAnimPlay.hpp\n        processors/object/ProcessorObjectDelete.hpp processors/object/ProcessorObjectDialogueChoice.hpp\n        processors/object/ProcessorObjectHit.hpp processors/object/ProcessorObjectLock.hpp\n        processors/object/ProcessorObjectMiscellaneous.hpp processors/object/ProcessorObjectMove.hpp\n        processors/object/ProcessorObjectPlace.hpp processors/object/ProcessorObjectRestock.hpp\n        processors/object/ProcessorObjectRotate.hpp processors/object/ProcessorObjectScale.hpp\n        processors/object/ProcessorObjectSound.hpp processors/object/ProcessorObjectSpawn.hpp\n        processors/object/ProcessorObjectState.hpp processors/object/ProcessorObjectTrap.hpp\n        processors/object/ProcessorClientScriptLocal.hpp processors/object/ProcessorScriptMemberShort.hpp\n        processors/object/ProcessorVideoPlay.hpp\n        )\n\nsource_group(tes3mp-server\\\\processors\\\\object FILES ${PROCESSORS_OBJECT})\n\nset(PROCESSORS_WORLDSTATE\n        processors/worldstate/ProcessorClientScriptGlobal.hpp processors/worldstate/ProcessorRecordDynamic.hpp\n        processors/worldstate/ProcessorWorldKillCount.hpp processors/worldstate/ProcessorWorldMap.hpp\n        processors/worldstate/ProcessorWorldWeather.hpp\n        )\n\nsource_group(tes3mp-server\\\\processors\\\\worldstate FILES ${PROCESSORS_WORLDSTATE})\n\nset(PROCESSORS\n        processors/ProcessorInitializer.cpp\n        processors/PlayerProcessor.cpp\n        processors/ActorProcessor.cpp\n        processors/ObjectProcessor.cpp\n        processors/WorldstateProcessor.cpp\n        )\n\nsource_group(tes3mp-server\\\\processors FILES ${PROCESSORS})\n\ninclude_directories(\"./\")\ninclude_directories(${CMAKE_SOURCE_DIR}/extern)\n\n# Main executable\n\nadd_executable(tes3mp-server\n        ${SERVER} ${SERVER_HEADER}\n        ${PROCESSORS_ACTOR} ${PROCESSORS_PLAYER} ${PROCESSORS_OBJECT} ${PROCESSORS_WORLDSTATE} ${PROCESSORS}\n        ${APPLE_BUNDLE_RESOURCES}\n        )\n\ntarget_compile_options(tes3mp-server PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/permissive->)\n\nif (OPENMW_MP_BUILD)\n\ttarget_compile_options(tes3mp-server PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/MP>)\nendif()\n\nset_target_properties(tes3mp-server PROPERTIES\n    CXX_STANDARD 17\n    CXX_STANDARD_REQUIRED YES\n    CXX_EXTENSIONS YES\n)\n\nif (UNIX)\n\ttarget_compile_options(tes3mp-server PRIVATE -Wno-ignored-qualifiers)\nendif()\n\ntarget_link_libraries(tes3mp-server\n    #${Boost_SYSTEM_LIBRARY}\n    #${Boost_THREAD_LIBRARY}\n    #${Boost_FILESYSTEM_LIBRARY}\n    #${Boost_PROGRAM_OPTIONS_LIBRARY}\n    ${RakNet_LIBRARY}\n    components\n    ${LuaJit_LIBRARIES}\n    ${Breakpad_Library}\n)\n\nif (UNIX)\n    target_link_libraries(tes3mp-server dl)\n    # Fix for not visible pthreads functions for linker with glibc 2.15\n    if(NOT APPLE)\n        target_link_libraries(tes3mp-server ${CMAKE_THREAD_LIBS_INIT})\n    endif(NOT APPLE)\nendif(UNIX)\n\nif (BUILD_WITH_CODE_COVERAGE)\n  add_definitions (--coverage)\n  target_link_libraries(tes3mp-server gcov)\nendif()\n\nif (MSVC)\n    # Debug version needs increased number of sections beyond 2^16\n    if (CMAKE_CL_64)\n        set (CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} /bigobj\")\n    endif (CMAKE_CL_64)\n    add_definitions(\"-D_USE_MATH_DEFINES\")\nendif (MSVC)\n"
  },
  {
    "path": "apps/openmw-mp/Cell.cpp",
    "content": "#include \"Cell.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <iostream>\n#include \"Player.hpp\"\n#include \"Script/Script.hpp\"\n\nCell::Cell(ESM::Cell cell) : cell(cell)\n{\n    cellActorList.count = 0;\n}\n\nCell::Iterator Cell::begin() const\n{\n    return players.begin();\n}\n\nCell::Iterator Cell::end() const\n{\n    return players.end();\n}\n\nvoid Cell::addPlayer(Player *player)\n{\n    // Ensure the player hasn't already been added\n    auto it = find(begin(), end(), player);\n\n    if (it != end())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Attempt to add %s to Cell %s again was ignored\", player->npc.mName.c_str(), getShortDescription().c_str());\n        return;\n    }\n\n    auto it2 = find(player->cells.begin(), player->cells.end(), this);\n    if (it2 == player->cells.end())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Adding %s to Player %s\", getShortDescription().c_str(), player->npc.mName.c_str());\n\n        player->cells.push_back(this);\n    }\n\n    LOG_APPEND(TimedLog::LOG_INFO, \"- Adding %s to Cell %s\", player->npc.mName.c_str(), getShortDescription().c_str());\n\n    Script::Call<Script::CallbackIdentity(\"OnCellLoad\")>(player->getId(), getShortDescription().c_str());\n\n    players.push_back(player);\n}\n\nvoid Cell::removePlayer(Player *player, bool cleanPlayer)\n{\n    for (Iterator it = begin(); it != end(); it++)\n    {\n        if (*it == player)\n        {\n            if (cleanPlayer)\n            {\n                auto it2 = find(player->cells.begin(), player->cells.end(), this);\n                if (it2 != player->cells.end())\n                {\n                    LOG_APPEND(TimedLog::LOG_INFO, \"- Removing %s from Player %s\", getShortDescription().c_str(), player->npc.mName.c_str());\n\n                    player->cells.erase(it2);\n                }\n            }\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Removing %s from Cell %s\", player->npc.mName.c_str(), getShortDescription().c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnCellUnload\")>(player->getId(), getShortDescription().c_str());\n\n            players.erase(it);\n            return;\n        }\n    }\n}\n\nvoid Cell::readActorList(unsigned char packetID, const mwmp::BaseActorList *newActorList)\n{\n    for (unsigned int i = 0; i < newActorList->count; i++)\n    {\n        mwmp::BaseActor newActor = newActorList->baseActors.at(i);\n        mwmp::BaseActor *cellActor;\n\n        if (containsActor(newActor.refNum, newActor.mpNum))\n        {\n            cellActor = getActor(newActor.refNum, newActor.mpNum);\n\n            switch (packetID)\n            {\n            case ID_ACTOR_POSITION:\n\n                cellActor->hasPositionData = true;\n                cellActor->position = newActor.position;\n                break;\n\n            case ID_ACTOR_STATS_DYNAMIC:\n\n                cellActor->hasStatsDynamicData = true;\n                cellActor->creatureStats.mDynamic[0] = newActor.creatureStats.mDynamic[0];\n                cellActor->creatureStats.mDynamic[1] = newActor.creatureStats.mDynamic[1];\n                cellActor->creatureStats.mDynamic[2] = newActor.creatureStats.mDynamic[2];\n                break;\n            }\n        }\n        else\n            cellActorList.baseActors.push_back(newActor);\n    }\n\n    cellActorList.count = cellActorList.baseActors.size();\n}\n\nbool Cell::containsActor(int refNum, int mpNum)\n{\n    for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)\n    {\n        mwmp::BaseActor actor = cellActorList.baseActors.at(i);\n\n        if (actor.refNum == refNum && actor.mpNum == mpNum)\n            return true;\n    }\n    return false;\n}\n\nmwmp::BaseActor *Cell::getActor(int refNum, int mpNum)\n{\n    for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)\n    {\n        mwmp::BaseActor *actor = &cellActorList.baseActors.at(i);\n\n        if (actor->refNum == refNum && actor->mpNum == mpNum)\n            return actor;\n    }\n    return 0;\n}\n\nvoid Cell::removeActors(const mwmp::BaseActorList *newActorList)\n{\n    for (std::vector<mwmp::BaseActor>::iterator it = cellActorList.baseActors.begin(); it != cellActorList.baseActors.end();)\n    {\n        int refNum = (*it).refNum;\n        int mpNum = (*it).mpNum;\n\n        bool foundActor = false;\n\n        for (unsigned int i = 0; i < newActorList->count; i++)\n        {\n            mwmp::BaseActor newActor = newActorList->baseActors.at(i);\n\n            if (newActor.refNum == refNum && newActor.mpNum == mpNum)\n            {\n                it = cellActorList.baseActors.erase(it);\n                foundActor = true;\n                break;\n            }\n        }\n\n        if (!foundActor)\n            it++;\n    }\n\n    cellActorList.count = cellActorList.baseActors.size();\n}\n\nRakNet::RakNetGUID *Cell::getAuthority()\n{\n    return &authorityGuid;\n}\n\nvoid Cell::setAuthority(const RakNet::RakNetGUID& guid)\n{\n    authorityGuid = guid;\n}\n\nmwmp::BaseActorList *Cell::getActorList()\n{\n    return &cellActorList;\n}\n\nCell::TPlayers Cell::getPlayers() const\n{\n    return players;\n}\n\nvoid Cell::sendToLoaded(mwmp::ActorPacket *actorPacket, mwmp::BaseActorList *baseActorList) const\n{\n    if (players.empty())\n        return;\n\n    std::list <Player*> plList;\n\n    for (auto pl : players)\n    {\n        if (pl != nullptr && !pl->npc.mName.empty())\n            plList.push_back(pl);\n    }\n\n    plList.sort();\n    plList.unique();\n\n    for (auto pl : plList)\n    {\n        if (pl->guid == baseActorList->guid) continue;\n\n        actorPacket->setActorList(baseActorList);\n\n        // Send the packet to this eligible guid\n        actorPacket->Send(pl->guid);\n    }\n}\n\nvoid Cell::sendToLoaded(mwmp::ObjectPacket *objectPacket, mwmp::BaseObjectList *baseObjectList) const\n{\n    if (players.empty())\n        return;\n\n    std::list <Player*> plList;\n\n    for (auto pl : players)\n    {\n        if (pl != nullptr && !pl->npc.mName.empty())\n            plList.push_back(pl);\n    }\n\n    plList.sort();\n    plList.unique();\n\n    for (auto pl : plList)\n    {\n        if (pl->guid == baseObjectList->guid) continue;\n\n        objectPacket->setObjectList(baseObjectList);\n\n        // Send the packet to this eligible guid\n        objectPacket->Send(pl->guid);\n    }\n}\n\nstd::string Cell::getShortDescription() const\n{\n    return cell.getShortDescription();\n}\n"
  },
  {
    "path": "apps/openmw-mp/Cell.hpp",
    "content": "#ifndef OPENMW_SERVERCELL_HPP\n#define OPENMW_SERVERCELL_HPP\n\n#include <deque>\n#include <string>\n#include <components/esm/records.hpp>\n#include <components/openmw-mp/Base/BaseActor.hpp>\n#include <components/openmw-mp/Base/BaseObject.hpp>\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nclass Player;\nclass Cell;\n\nclass Cell\n{\n    friend class CellController;\npublic:\n    Cell(ESM::Cell cell);\n    typedef std::deque<Player*> TPlayers;\n    typedef TPlayers::const_iterator Iterator;\n\n    Iterator begin() const;\n    Iterator end() const;\n\n    void addPlayer(Player *player);\n    void removePlayer(Player *player, bool cleanPlayer = true);\n\n    void readActorList(unsigned char packetID, const mwmp::BaseActorList *newActorList);\n    bool containsActor(int refNum, int mpNum);\n    mwmp::BaseActor *getActor(int refNum, int mpNum);\n    void removeActors(const mwmp::BaseActorList *newActorList);\n\n    RakNet::RakNetGUID *getAuthority();\n    void setAuthority(const RakNet::RakNetGUID& guid);\n    mwmp::BaseActorList *getActorList();\n\n    TPlayers getPlayers() const;\n    void sendToLoaded(mwmp::ActorPacket *actorPacket, mwmp::BaseActorList *baseActorList) const;\n    void sendToLoaded(mwmp::ObjectPacket *objectPacket, mwmp::BaseObjectList *baseObjectList) const;\n\n    std::string getShortDescription() const;\n\n\nprivate:\n    TPlayers players;\n    ESM::Cell cell;\n\n    RakNet::RakNetGUID authorityGuid;\n    mwmp::BaseActorList cellActorList;\n};\n\n\n#endif //OPENMW_SERVERCELL_HPP\n"
  },
  {
    "path": "apps/openmw-mp/CellController.cpp",
    "content": "#include \"CellController.hpp\"\n\n#include <iostream>\n#include \"Cell.hpp\"\n#include \"Player.hpp\"\n#include \"Script/Script.hpp\"\n\nCellController::CellController()\n{\n\n}\n\nCellController::~CellController()\n{\n    for (auto cell : cells)\n        delete cell;\n}\n\nCellController *CellController::sThis = nullptr;\n\nvoid CellController::create()\n{\n    assert(!sThis);\n    sThis = new CellController;\n}\n\nvoid CellController::destroy()\n{\n    assert(sThis);\n    delete sThis;\n    sThis = nullptr;\n}\n\nCellController *CellController::get()\n{\n    assert(sThis);\n    return sThis;\n}\n\nCell *CellController::getCell(ESM::Cell *esmCell)\n{\n    if (esmCell->isExterior())\n        return getCellByXY(esmCell->mData.mX, esmCell->mData.mY);\n    else\n        return getCellByName(esmCell->mName);\n}\n\n\nCell *CellController::getCellByXY(int x, int y)\n{\n    auto it = find_if(cells.begin(), cells.end(), [x, y](const Cell *c)\n    {\n        return c->cell.mData.mX == x && c->cell.mData.mY == y;\n    });\n\n    if (it == cells.end())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Attempt to get Cell at %i, %i failed!\", x, y);\n        return nullptr;\n    }\n\n    return *it;\n}\n\nCell *CellController::getCellByName(std::string cellName)\n{\n    auto it = find_if(cells.begin(), cells.end(), [cellName](const Cell *c)\n    {\n        return c->cell.mName == cellName;\n    });\n\n    if (it == cells.end())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Attempt to get Cell at %s failed!\", cellName.c_str());\n        return nullptr;\n    }\n\n    return *it;\n}\n\nCell *CellController::addCell(ESM::Cell cellData)\n{\n    LOG_APPEND(TimedLog::LOG_INFO, \"- Loaded cells: %d\", cells.size());\n    auto it = find_if(cells.begin(), cells.end(), [cellData](const Cell *c) {\n        // Currently we cannot compare because plugin lists can be loaded in different order\n        //return c->cell.sRecordId == cellData.sRecordId;\n        if (c->cell.isExterior() && cellData.isExterior())\n        {\n            if (c->cell.mData.mX == cellData.mData.mX && c->cell.mData.mY == cellData.mData.mY)\n                return true;\n        }\n        else if (c->cell.mName == cellData.mName)\n            return true;\n\n        return false;\n    });\n\n    Cell *cell;\n    if (it == cells.end())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Adding %s to CellController\", cellData.getShortDescription().c_str());\n\n        cell = new Cell(cellData);\n        cells.push_back(cell);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Found %s in CellController\", cellData.getShortDescription().c_str());\n        cell = *it;\n    }\n\n    return cell;\n}\n\nvoid CellController::removeCell(Cell *cell)\n{\n    if (cell == nullptr)\n        return;\n\n    for (auto it = cells.begin(); it != cells.end();)\n    {\n        if (*it != nullptr && *it == cell)\n        {\n            Script::Call<Script::CallbackIdentity(\"OnCellDeletion\")>(cell->getShortDescription().c_str());\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Removing %s from CellController\", cell->getShortDescription().c_str());\n\n            delete *it;\n            it = cells.erase(it);\n        }\n        else\n            ++it;\n    }\n}\n\nvoid CellController::deletePlayer(Player *player)\n{\n    LOG_APPEND(TimedLog::LOG_INFO, \"- Iterating through Cells from Player %s\", player->npc.mName.c_str());\n\n    std::vector<Cell*> toDelete;\n\n    auto it = player->getCells()->begin();\n    const auto endIter = player->getCells()->end();\n\n    for (; it != endIter; ++it)\n    {\n        Cell *c = *it;\n        c->removePlayer(player, false);\n        if (c->players.empty())\n            toDelete.push_back(c);\n    }\n\n    for (auto &&cell : toDelete)\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Cell %s has no players left\", cell->getShortDescription().c_str());\n        removeCell(cell);\n    }\n}\n\nvoid CellController::update(Player *player)\n{\n    std::vector<Cell*> toDelete;\n\n    for (auto &&cell : player->cellStateChanges)\n    {\n        if (cell.type == mwmp::CellState::LOAD)\n        {\n            Cell *c = addCell(cell.cell);\n            c->addPlayer(player);\n        }\n        else\n        {\n            Cell *c;\n            if (!cell.cell.isExterior())\n                c = getCellByName(cell.cell.mName);\n            else\n                c = getCellByXY(cell.cell.getGridX(), cell.cell.getGridY());\n\n            if (c != nullptr)\n            {\n                c->removePlayer(player);\n                if (c->players.empty())\n                    toDelete.push_back(c);\n            }\n        }\n    }\n    for (auto &&cell : toDelete)\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Cell %s has no players left\", cell->getShortDescription().c_str());\n        removeCell(cell);\n    }\n}\n"
  },
  {
    "path": "apps/openmw-mp/CellController.hpp",
    "content": "#ifndef OPENMW_SERVERCELLCONTROLLER_HPP\n#define OPENMW_SERVERCELLCONTROLLER_HPP\n\n#include <deque>\n#include <string>\n#include <components/esm/records.hpp>\n#include <components/openmw-mp/Base/BaseObject.hpp>\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nclass Player;\nclass Cell;\n\n\nclass CellController\n{\nprivate:\n    CellController();\n    ~CellController();\n\n    CellController(CellController&); // not used\npublic:\n    static void create();\n    static void destroy();\n    static CellController *get();\npublic:\n    typedef std::deque<Cell*> TContainer;\n    typedef TContainer::iterator TIter;\n\n    Cell * addCell(ESM::Cell cell);\n    void removeCell(Cell *);\n\n    void deletePlayer(Player *player);\n\n    Cell *getCell(ESM::Cell *esmCell);\n    Cell *getCellByXY(int x, int y);\n    Cell *getCellByName(std::string cellName);\n\n    void update(Player *player);\n\nprivate:\n    static CellController *sThis;\n    TContainer cells;\n};\n\n#endif //OPENMW_SERVERCELLCONTROLLER_HPP\n"
  },
  {
    "path": "apps/openmw-mp/MasterClient.cpp",
    "content": "#include <RakSleep.h>\n#include <Getche.h>\n#include <sstream>\n#include <iostream>\n#include <thread>\n#include <RakPeerInterface.h>\n#include \"MasterClient.hpp\"\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Version.hpp>\n#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>\n#include \"Networking.hpp\"\n\nusing namespace mwmp;\nusing namespace RakNet;\n\nbool MasterClient::sRun = false;\n\nMasterClient::MasterClient(RakNet::RakPeerInterface *peer, std::string queryAddr, unsigned short queryPort) :\n        masterServer(queryAddr.c_str(), queryPort), peer(peer), pma(peer)\n{\n    timeout = 15000; // every 15 seconds\n    pma.SetSendStream(&writeStream);\n    pma.SetServer(&queryData);\n    updated = true;\n}\n\nvoid MasterClient::SetPlayers(unsigned pl)\n{\n    mutexData.lock();\n    if (queryData.GetPlayers() != pl)\n    {\n        queryData.SetPlayers(pl);\n        updated = true;\n    }\n    mutexData.unlock();\n}\n\nvoid MasterClient::SetMaxPlayers(unsigned pl)\n{\n    mutexData.lock();\n    if (queryData.GetMaxPlayers() != pl)\n    {\n        queryData.SetMaxPlayers(pl);\n        updated = true;\n    }\n    mutexData.unlock();\n}\n\nvoid MasterClient::SetHostname(std::string hostname)\n{\n    mutexData.lock();\n    std::string substr = hostname.substr(0, 200);\n    if (queryData.GetName() != substr)\n    {\n        queryData.SetName(substr.c_str());\n        updated = true;\n    }\n    mutexData.unlock();\n}\n\nvoid MasterClient::SetModname(std::string modname)\n{\n    mutexData.lock();\n    std::string substr = modname.substr(0, 200);\n    if (queryData.GetGameMode() != substr)\n    {\n        queryData.SetGameMode(substr.c_str());\n        updated = true;\n    }\n    mutexData.unlock();\n}\n\nvoid MasterClient::SetRuleString(std::string key, std::string value)\n{\n    mutexData.lock();\n    if (queryData.rules.find(key) == queryData.rules.end() || queryData.rules[key].type != 's'\n        || queryData.rules[key].str != value)\n    {\n        ServerRule rule;\n        rule.str = value;\n        rule.type = ServerRule::Type::string;\n        queryData.rules.insert({key, rule});\n        updated = true;\n    }\n    mutexData.unlock();\n}\n\nvoid MasterClient::SetRuleValue(std::string key, double value)\n{\n    mutexData.lock();\n    if (queryData.rules.find(key) == queryData.rules.end() || queryData.rules[key].type != 'v'\n        || queryData.rules[key].val != value)\n    {\n        ServerRule rule;\n        rule.val = value;\n        rule.type = ServerRule::Type::number;\n        queryData.rules.insert({key, rule});\n        updated = true;\n    }\n    mutexData.unlock();\n}\n\nvoid MasterClient::PushPlugin(Plugin plugin)\n{\n    mutexData.lock();\n    queryData.plugins.push_back(plugin);\n    updated = true;\n    mutexData.unlock();\n}\n\nbool MasterClient::Process(RakNet::Packet *packet)\n{\n    if (!sRun || packet->systemAddress != masterServer)\n        return false;\n\n    BitStream rs(packet->data, packet->length, false);\n    unsigned char pid;\n    rs.Read(pid);\n    switch (pid)\n    {\n        case ID_SND_RECEIPT_ACKED:\n        case ID_CONNECTION_ATTEMPT_FAILED:\n        case ID_CONNECTION_REQUEST_ACCEPTED:\n        case ID_DISCONNECTION_NOTIFICATION:\n            break;\n        case ID_MASTER_QUERY:\n            break;\n        case ID_MASTER_ANNOUNCE:\n            pma.SetReadStream(&rs);\n            pma.Read();\n            if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_KEEP)\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Server data successfully updated on master server\");\n            else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)\n            {\n                if (timeout != 0)\n                {\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Update rate is too low,\"\n                            \" and the master server has deleted information about the server. Trying low rate...\");\n                    if ((timeout - step_rate) >= step_rate)\n                        SetUpdateRate(timeout - step_rate);\n                    updated = true;\n                }\n            }\n            break;\n        default:\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Received wrong packet from master server with id: %d\", packet->data[0]);\n            return false;\n    }\n    return true;\n}\n\nvoid MasterClient::Send(mwmp::PacketMasterAnnounce::Func func)\n{\n    peer->Connect(masterServer.ToString(false), masterServer.GetPort(), TES3MP_MASTERSERVER_PASSW,\n                  strlen(TES3MP_MASTERSERVER_PASSW), 0, 0, 5, 500);\n    bool waitForConnect = true;\n    while (waitForConnect)\n    {\n        ConnectionState state = peer->GetConnectionState(masterServer);\n        switch (state)\n        {\n            case IS_CONNECTED:\n                waitForConnect = false;\n                break;\n            case IS_NOT_CONNECTED:\n            case IS_DISCONNECTED:\n            case IS_SILENTLY_DISCONNECTING:\n            case IS_DISCONNECTING:\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Cannot connect to master server: %s\", masterServer.ToString());\n                return;\n            }\n            case IS_PENDING:\n            case IS_CONNECTING:\n                break;\n        }\n        RakSleep(500);\n    }\n    pma.SetFunc(func);\n    pma.Send(masterServer);\n    updated = false;\n}\n\nvoid MasterClient::Thread()\n{\n    assert(!sRun);\n\n    sRun = true;\n\n    queryData.SetPassword((int) Networking::get().isPassworded());\n    queryData.SetVersion(TES3MP_VERSION);\n\n    auto *players = Players::getPlayers();\n    while (sRun)\n    {\n        SetPlayers((int) players->size());\n\n        auto pIt = players->begin();\n        if (queryData.players.size() != players->size())\n        {\n            queryData.players.clear();\n            updated = true;\n        }\n        else\n        {\n            for (int i = 0; pIt != players->end(); i++, pIt++)\n            {\n                if (queryData.players[i] != pIt->second->npc.mName)\n                {\n                    queryData.players.clear();\n                    updated = true;\n                    break;\n                }\n            }\n        }\n\n        if (updated)\n        {\n            updated = false;\n            if (pIt != players->end())\n            {\n                for (auto player : *players)\n                {\n                    if (!player.second->npc.mName.empty())\n                        queryData.players.push_back(player.second->npc.mName);\n                }\n            }\n            Send(PacketMasterAnnounce::FUNCTION_ANNOUNCE);\n        }\n        else\n            Send(PacketMasterAnnounce::FUNCTION_KEEP);\n        RakSleep(timeout);\n    }\n}\n\nvoid MasterClient::Start()\n{\n    thrQuery = std::thread(&MasterClient::Thread, this);\n}\n\nvoid MasterClient::Stop()\n{\n    if (!sRun)\n        return;\n    sRun = false;\n    if (thrQuery.joinable())\n        thrQuery.join();\n}\n\nvoid MasterClient::SetUpdateRate(unsigned int rate)\n{\n    if (timeout < min_rate)\n        timeout = min_rate;\n    else if (timeout > max_rate)\n        timeout = max_rate;\n    timeout = rate;\n}\n"
  },
  {
    "path": "apps/openmw-mp/MasterClient.hpp",
    "content": "#ifndef OPENMW_MASTERCLIENT_HPP\n#define OPENMW_MASTERCLIENT_HPP\n\n#include <string>\n#include <mutex>\n#include <thread>\n#include <components/openmw-mp/Master/MasterData.hpp>\n#include <RakString.h>\n#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>\n\nclass MasterClient\n{\npublic:\n    static const unsigned int step_rate = 1000;\n    static const unsigned int min_rate = 1000;\n    static const unsigned int max_rate = 60000;\npublic:\n    MasterClient(RakNet::RakPeerInterface *peer, std::string queryAddr, unsigned short queryPort);\n    void SetPlayers(unsigned pl);\n    void SetMaxPlayers(unsigned pl);\n    void SetHostname(std::string hostname);\n    void SetModname(std::string hostname);\n    void SetRuleString(std::string key, std::string value);\n    void SetRuleValue(std::string key, double value);\n    void PushPlugin(Plugin plugin);\n\n    bool Process(RakNet::Packet *packet);\n    void Start();\n    void Stop();\n    void SetUpdateRate(unsigned int rate);\n\nprivate:\n    void Send(mwmp::PacketMasterAnnounce::Func func);\n    void Thread();\nprivate:\n    RakNet::SystemAddress masterServer;\n    RakNet::RakPeerInterface *peer;\n    QueryData queryData;\n    unsigned int timeout;\n    static bool sRun;\n    std::mutex mutexData;\n    std::thread thrQuery;\n    mwmp::PacketMasterAnnounce pma;\n    RakNet::BitStream writeStream;\n    bool updated;\n};\n\n\n#endif //OPENMW_MASTERCLIENT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Networking.cpp",
    "content": "#include \"Player.hpp\"\n#include \"processors/ProcessorInitializer.hpp\"\n#include <RakPeer.h>\n#include <Kbhit.h>\n\n#include <components/misc/stringops.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Version.hpp>\n#include <components/openmw-mp/Packets/PacketPreInit.hpp>\n\n#include <iostream>\n#include <Script/Script.hpp>\n#include <Script/API/TimerAPI.hpp>\n#include <chrono>\n#include <thread>\n#include <csignal>\n\n#include \"Networking.hpp\"\n#include \"MasterClient.hpp\"\n#include \"Cell.hpp\"\n#include \"CellController.hpp\"\n#include \"processors/PlayerProcessor.hpp\"\n#include \"processors/ActorProcessor.hpp\"\n#include \"processors/ObjectProcessor.hpp\"\n#include \"processors/WorldstateProcessor.hpp\"\n\nusing namespace mwmp;\n\nNetworking *Networking::sThis = 0;\n\nstatic int currentMpNum = 0;\nstatic bool dataFileEnforcementState = true;\nstatic bool scriptErrorIgnoringState = false;\nbool killLoop = false;\n\nNetworking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)\n{\n    sThis = this;\n    this->peer = peer;\n    players = Players::getPlayers();\n\n    CellController::create();\n\n    systemPacketController = new SystemPacketController(peer);\n    playerPacketController = new PlayerPacketController(peer);\n    actorPacketController = new ActorPacketController(peer);\n    objectPacketController = new ObjectPacketController(peer);\n    worldstatePacketController = new WorldstatePacketController(peer);\n\n    // Set send stream\n    systemPacketController->SetStream(0, &bsOut);\n    playerPacketController->SetStream(0, &bsOut);\n    actorPacketController->SetStream(0, &bsOut);\n    objectPacketController->SetStream(0, &bsOut);\n    worldstatePacketController->SetStream(0, &bsOut);\n\n    running = true;\n    exitCode = 0;\n\n    Script::Call<Script::CallbackIdentity(\"OnServerInit\")>();\n\n    serverPassword = TES3MP_DEFAULT_PASSW;\n\n    ProcessorInitializer();\n}\n\nNetworking::~Networking()\n{\n    Script::Call<Script::CallbackIdentity(\"OnServerExit\")>(false);\n\n    CellController::destroy();\n\n    sThis = 0;\n    delete systemPacketController;\n    delete playerPacketController;\n    delete actorPacketController;\n    delete objectPacketController;\n    delete worldstatePacketController;\n}\n\nvoid Networking::setServerPassword(std::string password) noexcept\n{\n    serverPassword = password.empty() ? TES3MP_DEFAULT_PASSW : password;\n}\n\nbool Networking::isPassworded() const\n{\n    return serverPassword != TES3MP_DEFAULT_PASSW;\n}\n\nvoid Networking::processSystemPacket(RakNet::Packet *packet)\n{\n    Player *player = Players::getPlayer(packet->guid);\n\n    SystemPacket *myPacket = systemPacketController->GetPacket(packet->data[0]);\n\n    if (packet->data[0] == ID_SYSTEM_HANDSHAKE)\n    {\n        myPacket->setSystem(&baseSystem);\n        myPacket->Read();\n\n        if (!myPacket->isPacketValid())\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Invalid handshake packet from client at %s\", packet->systemAddress.ToString());\n            kickPlayer(player->guid);\n            return;\n        }\n\n        if (player->isHandshaked())\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Wrong handshake with client at %s\", packet->systemAddress.ToString());\n            kickPlayer(player->guid);\n            return;\n        }\n\n        if (baseSystem.serverPassword != serverPassword)\n        {\n            if (isPassworded())\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Wrong server password %s used by client at %s\",\n                    baseSystem.serverPassword.c_str(), packet->systemAddress.ToString());\n                kickPlayer(player->guid);\n                return;\n            }\n            else\n            {\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Client at %s tried to join using password, despite the server not being passworded\",\n                    packet->systemAddress.ToString());\n            }\n        }\n        player->setHandshake();\n        return;\n    }\n}\n\nvoid Networking::processPlayerPacket(RakNet::Packet *packet)\n{\n    Player *player = Players::getPlayer(packet->guid);\n\n    PlayerPacket *myPacket = playerPacketController->GetPacket(packet->data[0]);\n\n    if (!player->isHandshaked())\n    {\n        player->incrementHandshakeAttempts();\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Have not completed handshake with client at %s\", packet->systemAddress.ToString());\n        LOG_APPEND(TimedLog::LOG_WARN, \"- Attempts so far: %i\", player->getHandshakeAttempts());\n\n        if (player->getHandshakeAttempts() > 20)\n            kickPlayer(player->guid, false);\n        else if (player->getHandshakeAttempts() > 5)\n            kickPlayer(player->guid, true);\n\n        return;\n    }\n\n    if (packet->data[0] == ID_LOADED)\n    {\n        player->setLoadState(Player::LOADED);\n\n        unsigned short pid = Players::getPlayer(packet->guid)->getId();\n        Script::Call<Script::CallbackIdentity(\"OnPlayerConnect\")>(pid);\n\n        if (player->getLoadState() == Player::KICKED) // kicked inside in OnPlayerConnect\n        {\n            playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(Players::getPlayer(packet->guid));\n            playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(false);\n            Players::deletePlayer(packet->guid);\n            return;\n        }\n    }\n    else if (packet->data[0] == ID_PLAYER_BASEINFO)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_PLAYER_BASEINFO about %s\", player->npc.mName.c_str());\n\n        myPacket->setPlayer(player);\n        myPacket->Read();\n        myPacket->Send(true);\n    }\n\n    if (player->getLoadState() == Player::NOTLOADED)\n        return;\n    else if (player->getLoadState() == Player::LOADED)\n    {\n        player->setLoadState(Player::POSTLOADED);\n        newPlayer(packet->guid);\n        return;\n    }\n\n\n    if (!PlayerProcessor::Process(*packet))\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled PlayerPacket with identifier %i has arrived\", packet->data[0]);\n\n}\n\nvoid Networking::processActorPacket(RakNet::Packet *packet)\n{\n    Player *player = Players::getPlayer(packet->guid);\n\n    if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)\n        return;\n\n    if (!ActorProcessor::Process(*packet, baseActorList))\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled ActorPacket with identifier %i has arrived\", packet->data[0]);\n\n}\n\nvoid Networking::processObjectPacket(RakNet::Packet *packet)\n{\n    Player *player = Players::getPlayer(packet->guid);\n\n    if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)\n        return;\n\n    if (!ObjectProcessor::Process(*packet, baseObjectList))\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled ObjectPacket with identifier %i has arrived\", packet->data[0]);\n\n}\n\nvoid Networking::processWorldstatePacket(RakNet::Packet *packet)\n{\n    Player *player = Players::getPlayer(packet->guid);\n\n    if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)\n        return;\n\n    if (!WorldstateProcessor::Process(*packet, baseWorldstate))\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled WorldstatePacket with identifier %i has arrived\", packet->data[0]);\n\n}\n\nbool Networking::preInit(RakNet::Packet *packet, RakNet::BitStream &bsIn)\n{\n    if (packet->data[0] != ID_GAME_PREINIT)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"%s sent wrong first packet (ID_GAME_PREINIT was expected)\",\n                           packet->systemAddress.ToString());\n        peer->CloseConnection(packet->systemAddress, true);\n    }\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received ID_GAME_PREINIT from %s\", packet->systemAddress.ToString());\n    PacketPreInit::PluginContainer dataFiles;\n\n    PacketPreInit packetPreInit(peer);\n    packetPreInit.SetReadStream(&bsIn);\n    packetPreInit.setChecksums(&dataFiles);\n    packetPreInit.Read();\n\n    if (!packetPreInit.isPacketValid() || dataFiles.empty())\n    {\n        LOG_APPEND(TimedLog::LOG_ERROR, \"- Packet was invalid\");\n        peer->CloseConnection(packet->systemAddress, false); // close connection without notification\n        return false;\n    }\n\n    auto dataFile = dataFiles.begin();\n    if (samples.size() == dataFiles.size())\n    {\n        for (int i = 0; dataFile != dataFiles.end(); dataFile++, i++)\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"- idx: %i\\tchecksum: %X\\tfile: %s\", i, dataFile->second[0], dataFile->first.c_str());\n            // Check if the filenames match, ignoring case\n            if (Misc::StringUtils::ciEqual(samples[i].first, dataFile->first))\n            {\n                auto &hashList = samples[i].second;\n                // Proceed if no checksums have been listed for this dataFile on the server\n                if (hashList.empty())\n                    continue;\n                auto it = find(hashList.begin(), hashList.end(), dataFile->second[0]);\n                // Break the loop if the client's checksum isn't among those accepted by\n                // the server\n                if (it == hashList.end())\n                    break;\n            }\n            else // name is incorrect\n                break;\n        }\n    }\n    RakNet::BitStream bs;\n    packetPreInit.SetSendStream(&bs);\n\n    // If the loop above was broken, then the client's data files do not match the server's\n    if (dataFileEnforcementState && dataFile != dataFiles.end())\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Client was not allowed to connect due to incompatible data files\");\n        packetPreInit.setChecksums(&samples);\n        packetPreInit.Send(packet->systemAddress);\n        peer->CloseConnection(packet->systemAddress, true);\n    }\n    else\n    {\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Client was allowed to connect\");\n        PacketPreInit::PluginContainer tmp;\n        packetPreInit.setChecksums(&tmp);\n        packetPreInit.Send(packet->systemAddress);\n        Players::newPlayer(packet->guid); // create player if connection allowed\n        systemPacketController->SetStream(&bsIn, nullptr); // and request handshake\n        systemPacketController->GetPacket(ID_SYSTEM_HANDSHAKE)->RequestData(packet->guid);\n        return true;\n    }\n\n    return false;\n}\n\nvoid Networking::update(RakNet::Packet *packet, RakNet::BitStream &bsIn)\n{\n    if (systemPacketController->ContainsPacket(packet->data[0]))\n    {\n        systemPacketController->SetStream(&bsIn, nullptr);\n        processSystemPacket(packet);\n    }\n    else if (playerPacketController->ContainsPacket(packet->data[0]))\n    {\n        playerPacketController->SetStream(&bsIn, nullptr);\n        processPlayerPacket(packet);\n    }\n    else if (actorPacketController->ContainsPacket(packet->data[0]))\n    {\n        actorPacketController->SetStream(&bsIn, 0);\n        processActorPacket(packet);\n    }\n    else if (objectPacketController->ContainsPacket(packet->data[0]))\n    {\n        objectPacketController->SetStream(&bsIn, 0);\n        processObjectPacket(packet);\n    }\n    else if (worldstatePacketController->ContainsPacket(packet->data[0]))\n    {\n        worldstatePacketController->SetStream(&bsIn, 0);\n        processWorldstatePacket(packet);\n    }\n    else\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Unhandled RakNet packet with identifier %i has arrived\", packet->data[0]);\n}\n\nvoid Networking::newPlayer(RakNet::RakNetGUID guid)\n{\n    playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->RequestData(guid);\n    playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->RequestData(guid);\n    playerPacketController->GetPacket(ID_PLAYER_POSITION)->RequestData(guid);\n    playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->RequestData(guid);\n    playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->RequestData(guid);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Sending info about other players to %lu\", guid.g);\n\n    for (TPlayers::iterator pl = players->begin(); pl != players->end(); pl++) //sending other players to new player\n    {\n        // If we are iterating over the new player, don't send the packets below\n        if (pl->first == guid) continue;\n\n        // If an invalid key makes it into the Players map, ignore it\n        else if (pl->first == RakNet::UNASSIGNED_CRABNET_GUID) continue;\n\n        // if player not fully connected\n        else if (pl->second == nullptr) continue;\n\n        // If we are iterating over a player who has inputted their name, proceed\n        else if (pl->second->getLoadState() == Player::POSTLOADED)\n        {\n            playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->setPlayer(pl->second);\n            playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl->second);\n            playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl->second);\n            playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl->second);\n            playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl->second);\n            playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl->second);\n            playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl->second);\n\n            playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->Send(guid);\n            playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(guid);\n            playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->Send(guid);\n            playerPacketController->GetPacket(ID_PLAYER_SKILL)->Send(guid);\n            playerPacketController->GetPacket(ID_PLAYER_POSITION)->Send(guid);\n            playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->Send(guid);\n            playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(guid);\n        }\n    }\n\n    LOG_APPEND(TimedLog::LOG_WARN, \"- Done\");\n\n}\n\nvoid Networking::disconnectPlayer(RakNet::RakNetGUID guid)\n{\n    Player *player = Players::getPlayer(guid);\n    if (!player)\n        return;\n    Script::Call<Script::CallbackIdentity(\"OnPlayerDisconnect\")>(player->getId());\n\n    playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player);\n    playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(true);\n    Players::deletePlayer(guid);\n}\n\nPlayerPacketController *Networking::getPlayerPacketController() const\n{\n    return playerPacketController;\n}\n\nActorPacketController *Networking::getActorPacketController() const\n{\n    return actorPacketController;\n}\n\nObjectPacketController *Networking::getObjectPacketController() const\n{\n    return objectPacketController;\n}\n\nWorldstatePacketController *Networking::getWorldstatePacketController() const\n{\n    return worldstatePacketController;\n}\n\nBaseActorList *Networking::getReceivedActorList()\n{\n    return &baseActorList;\n}\n\nBaseObjectList *Networking::getReceivedObjectList()\n{\n    return &baseObjectList;\n}\n\nBaseWorldstate *Networking::getReceivedWorldstate()\n{\n    return &baseWorldstate;\n}\n\nint Networking::getCurrentMpNum()\n{\n    return currentMpNum;\n}\n\nvoid Networking::setCurrentMpNum(int value)\n{\n    currentMpNum = value;\n}\n\nint Networking::incrementMpNum()\n{\n    currentMpNum++;\n    Script::Call<Script::CallbackIdentity(\"OnMpNumIncrement\")>(currentMpNum);\n    return currentMpNum;\n}\n\nbool Networking::getDataFileEnforcementState()\n{\n    return dataFileEnforcementState;\n}\n\nvoid Networking::setDataFileEnforcementState(bool state)\n{\n    dataFileEnforcementState = state;\n}\n\nbool Networking::getScriptErrorIgnoringState()\n{\n    return scriptErrorIgnoringState;\n}\n\nvoid Networking::setScriptErrorIgnoringState(bool state)\n{\n    scriptErrorIgnoringState = state;\n}\n\nconst Networking &Networking::get()\n{\n    return *sThis;\n}\n\n\nNetworking *Networking::getPtr()\n{\n    return sThis;\n}\n\nRakNet::SystemAddress Networking::getSystemAddress(RakNet::RakNetGUID guid)\n{\n    return peer->GetSystemAddressFromGuid(guid);\n}\n\nvoid Networking::stopServer(int code)\n{\n    running = false;\n    exitCode = code;\n}\n\nvoid signalHandler(int signum) \n{\n    std::cout << \"Interrupt signal (\" << signum << \") received.\\n\";\n    //15 is SIGTERM(Normal OS stop call), 2 is SIGINT(Ctrl+C)\n    if(signum == 15 || signum == 2)\n    {\n        killLoop = true;\n    }\n}\n\nint Networking::mainLoop()\n{\n    RakNet::Packet *packet;\n\n#ifndef _WIN32\n    struct sigaction sigIntHandler;\n    \n    sigIntHandler.sa_handler = signalHandler;\n    sigemptyset(&sigIntHandler.sa_mask);\n    sigIntHandler.sa_flags = 0;\n#endif\n    \n    while (running && !killLoop)\n    {\n#ifndef _WIN32\n        sigaction(SIGTERM, &sigIntHandler, NULL);\n        sigaction(SIGINT, &sigIntHandler, NULL);\n#endif\n        if (kbhit() && getch() == '\\n')\n            break;\n        for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())\n        {\n            if (getMasterClient()->Process(packet))\n                continue;\n\n            switch (packet->data[0])\n            {\n                case ID_REMOTE_DISCONNECTION_NOTIFICATION:\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Client at %s has disconnected\", packet->systemAddress.ToString());\n                    break;\n                case ID_REMOTE_CONNECTION_LOST:\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Client at %s has lost connection\", packet->systemAddress.ToString());\n                    break;\n                case ID_REMOTE_NEW_INCOMING_CONNECTION:\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Client at %s has connected\", packet->systemAddress.ToString());\n                    break;\n                case ID_CONNECTION_REQUEST_ACCEPTED:    // client to server\n                {\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Our connection request has been accepted\");\n                    break;\n                }\n                case ID_NEW_INCOMING_CONNECTION:\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"A connection is incoming from %s\", packet->systemAddress.ToString());\n                    break;\n                case ID_NO_FREE_INCOMING_CONNECTIONS:\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"The server is full\");\n                    break;\n                case ID_DISCONNECTION_NOTIFICATION:\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN,  \"Client at %s has disconnected\", packet->systemAddress.ToString());\n                    disconnectPlayer(packet->guid);\n                    break;\n                case ID_CONNECTION_LOST:\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Client at %s has lost connection\", packet->systemAddress.ToString());\n                    disconnectPlayer(packet->guid);\n                    break;\n                case ID_SND_RECEIPT_ACKED:\n                case ID_CONNECTED_PING:\n                case ID_UNCONNECTED_PING:\n                    break;\n                default:\n                {\n                    RakNet::BitStream bsIn(&packet->data[1], packet->length, false);\n                    bsIn.IgnoreBytes((unsigned int) RakNet::RakNetGUID::size()); // Ignore GUID from received packet\n\n\n                    if (Players::doesPlayerExist(packet->guid))\n                        update(packet, bsIn);\n                    else\n                        preInit(packet, bsIn);\n                    break;\n                }\n            }\n        }\n        TimerAPI::Tick();\n        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n    }\n\n    TimerAPI::Terminate();\n    return exitCode;\n}\n\nvoid Networking::kickPlayer(RakNet::RakNetGUID guid, bool sendNotification)\n{\n    peer->CloseConnection(guid, sendNotification);\n}\n\nvoid Networking::banAddress(const char *ipAddress)\n{\n    peer->AddToBanList(ipAddress);\n}\n\nvoid Networking::unbanAddress(const char *ipAddress)\n{\n    peer->RemoveFromBanList(ipAddress);\n}\n\nunsigned short Networking::numberOfConnections() const\n{\n    return peer->NumberOfConnections();\n}\n\nunsigned int Networking::maxConnections() const\n{\n    return peer->GetMaximumIncomingConnections();\n}\n\nint Networking::getAvgPing(RakNet::AddressOrGUID addr) const\n{\n    return peer->GetAveragePing(addr);\n}\n\nunsigned short Networking::getPort() const\n{\n    return peer->GetMyBoundAddress().GetPort();\n}\n\nMasterClient *Networking::getMasterClient()\n{\n    return mclient;\n}\n\nvoid Networking::InitQuery(std::string queryAddr, unsigned short queryPort)\n{\n    mclient = new MasterClient(peer, queryAddr, queryPort);\n}\n\nvoid Networking::postInit()\n{\n    Script::Call<Script::CallbackIdentity(\"OnRequestDataFileList\")>();\n    Script::Call<Script::CallbackIdentity(\"OnServerPostInit\")>();\n}\n\nPacketPreInit::PluginContainer &Networking::getSamples()\n{\n    return samples;\n}\n"
  },
  {
    "path": "apps/openmw-mp/Networking.hpp",
    "content": "#ifndef OPENMW_NETWORKING_HPP\n#define OPENMW_NETWORKING_HPP\n\n#include <components/openmw-mp/Controllers/SystemPacketController.hpp>\n#include <components/openmw-mp/Controllers/PlayerPacketController.hpp>\n#include <components/openmw-mp/Controllers/ActorPacketController.hpp>\n#include <components/openmw-mp/Controllers/ObjectPacketController.hpp>\n#include <components/openmw-mp/Controllers/WorldstatePacketController.hpp>\n#include <components/openmw-mp/Packets/PacketPreInit.hpp>\n#include \"Player.hpp\"\n\nclass MasterClient;\nnamespace  mwmp\n{\n    class Networking\n    {\n    public:\n        Networking(RakNet::RakPeerInterface *peer);\n        ~Networking();\n\n        void newPlayer(RakNet::RakNetGUID guid);\n        void disconnectPlayer(RakNet::RakNetGUID guid);\n        void kickPlayer(RakNet::RakNetGUID guid, bool sendNotification = true);\n        \n        void banAddress(const char *ipAddress);\n        void unbanAddress(const char *ipAddress);\n        RakNet::SystemAddress getSystemAddress(RakNet::RakNetGUID guid);\n\n        void processSystemPacket(RakNet::Packet *packet);\n        void processPlayerPacket(RakNet::Packet *packet);\n        void processActorPacket(RakNet::Packet *packet);\n        void processObjectPacket(RakNet::Packet *packet);\n        void processWorldstatePacket(RakNet::Packet *packet);\n        void update(RakNet::Packet *packet, RakNet::BitStream &bsIn);\n\n        unsigned short numberOfConnections() const;\n        unsigned int maxConnections() const;\n        int getAvgPing(RakNet::AddressOrGUID) const;\n        unsigned short getPort() const;\n\n        int mainLoop();\n\n        void stopServer(int code);\n\n        SystemPacketController *getSystemPacketController() const;\n        PlayerPacketController *getPlayerPacketController() const;\n        ActorPacketController *getActorPacketController() const;\n        ObjectPacketController *getObjectPacketController() const;\n        WorldstatePacketController *getWorldstatePacketController() const;\n\n        BaseActorList *getReceivedActorList();\n        BaseObjectList *getReceivedObjectList();\n        BaseWorldstate *getReceivedWorldstate();\n\n        int getCurrentMpNum();\n        void setCurrentMpNum(int value);\n        int incrementMpNum();\n\n        bool getDataFileEnforcementState();\n        void setDataFileEnforcementState(bool state);\n\n        bool getScriptErrorIgnoringState();\n        void setScriptErrorIgnoringState(bool state);\n\n        MasterClient *getMasterClient();\n        void InitQuery(std::string queryAddr, unsigned short queryPort);\n        void setServerPassword(std::string passw) noexcept;\n        bool isPassworded() const;\n\n        static const Networking &get();\n        static Networking *getPtr();\n\n        void postInit();\n\n        PacketPreInit::PluginContainer &getSamples();\n    private:\n        bool preInit(RakNet::Packet *packet, RakNet::BitStream &bsIn);\n        std::string serverPassword;\n        static Networking *sThis;\n\n        RakNet::RakPeerInterface *peer;\n        RakNet::BitStream bsOut;\n        TPlayers *players;\n        MasterClient *mclient;\n\n        BaseSystem baseSystem;\n        BaseActorList baseActorList;\n        BaseObjectList baseObjectList;\n        BaseWorldstate baseWorldstate;\n\n        SystemPacketController *systemPacketController;\n        PlayerPacketController *playerPacketController;\n        ActorPacketController *actorPacketController;\n        ObjectPacketController *objectPacketController;\n        WorldstatePacketController *worldstatePacketController;\n\n        bool running;\n        int exitCode;\n        PacketPreInit::PluginContainer samples;\n    };\n}\n\n\n#endif //OPENMW_NETWORKING_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Player.cpp",
    "content": "#include \"Player.hpp\"\n#include \"Networking.hpp\"\n\nTPlayers Players::players;\nTSlots Players::slots;\n\nvoid Players::deletePlayer(RakNet::RakNetGUID guid)\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Deleting player with guid %lu\", guid.g);\n\n    if (players[guid] != 0)\n    {\n        CellController::get()->deletePlayer(players[guid]);\n\n        LOG_APPEND(TimedLog::LOG_INFO, \"- Emptying slot %i\", players[guid]->getId());\n\n        slots[players[guid]->getId()] = 0;\n        delete players[guid];\n        players.erase(guid);\n    }\n}\n\nvoid Players::newPlayer(RakNet::RakNetGUID guid)\n{\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Creating new player with guid %lu\", guid.g);\n\n    players[guid] = new Player(guid);\n    players[guid]->cell.blank();\n    players[guid]->npc.blank();\n    players[guid]->npcStats.blank();\n    players[guid]->creatureStats.blank();\n    players[guid]->charClass.blank();\n    players[guid]->scale = 1;\n    players[guid]->isWerewolf = false;\n\n    for (unsigned int i = 0; i < mwmp::Networking::get().maxConnections(); i++)\n    {\n        if (slots[i] == 0)\n        {\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Storing in slot %i\", i);\n\n            slots[i] = players[guid];\n            slots[i]->setId(i);\n            break;\n        }\n    }\n}\n\nPlayer *Players::getPlayer(RakNet::RakNetGUID guid)\n{\n    auto it = players.find(guid);\n    if (it == players.end())\n        return nullptr;\n    return it->second;\n}\n\nTPlayers *Players::getPlayers()\n{\n    return &players;\n}\n\nunsigned short Players::getLastPlayerId()\n{\n    return slots.rbegin()->first;\n}\n\nPlayer::Player(RakNet::RakNetGUID guid) : BasePlayer(guid)\n{\n    handshakeCounter = 0;\n    loadState = NOTLOADED;\n}\n\nPlayer::~Player()\n{\n\n}\n\nunsigned short Player::getId()\n{\n    return id;\n}\n\nvoid Player::setId(unsigned short id)\n{\n    this->id = id;\n}\n\nbool Player::isHandshaked()\n{\n    return handshakeCounter == std::numeric_limits<int>::max();\n}\n\nvoid Player::setHandshake()\n{\n    handshakeCounter = std::numeric_limits<int>::max();\n}\n\nvoid Player::incrementHandshakeAttempts()\n{\n    handshakeCounter++;\n}\n\nint Player::getHandshakeAttempts()\n{\n    return handshakeCounter;\n}\n\n\nvoid Player::setLoadState(int state)\n{\n    loadState = state;\n}\n\nint Player::getLoadState()\n{\n    return loadState;\n}\n\nPlayer *Players::getPlayer(unsigned short id)\n{\n    auto it = slots.find(id);\n    if (it == slots.end())\n        return nullptr;\n    return it->second;\n}\n\nCellController::TContainer *Player::getCells()\n{\n    return &cells;\n}\n\nvoid Player::sendToLoaded(mwmp::PlayerPacket *myPacket)\n{\n    std::list <Player*> plList;\n\n    for (auto cell : cells)\n        for (auto pl : *cell)\n            plList.push_back(pl);\n\n    plList.sort();\n    plList.unique();\n\n    for (auto pl : plList)\n    {\n        if (pl == this) continue;\n        myPacket->setPlayer(this);\n        myPacket->Send(pl->guid);\n    }\n}\n\nvoid Player::forEachLoaded(std::function<void(Player *pl, Player *other)> func)\n{\n    std::list <Player*> plList;\n\n    for (auto cell : cells)\n    {\n        for (auto pl : *cell)\n        {\n            if (pl != nullptr && !pl->npc.mName.empty())\n                plList.push_back(pl);\n        }\n    }\n\n    plList.sort();\n    plList.unique();\n\n    for (auto pl : plList)\n    {\n        if (pl == this) continue;\n        func(this, pl);\n    }\n}\n\nbool Players::doesPlayerExist(RakNet::RakNetGUID guid)\n{\n    return players.find(guid) != players.end();\n}\n"
  },
  {
    "path": "apps/openmw-mp/Player.hpp",
    "content": "#ifndef OPENMW_PLAYER_HPP\n#define OPENMW_PLAYER_HPP\n\n#include <map>\n#include <string>\n#include <chrono>\n#include <RakNetTypes.h>\n\n#include <components/esm/npcstats.hpp>\n#include <components/esm/cellid.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/loadcell.hpp>\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Base/BasePlayer.hpp>\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n#include \"Cell.hpp\"\n#include \"CellController.hpp\"\n\ntypedef std::map<RakNet::RakNetGUID, Player*> TPlayers;\ntypedef std::map<unsigned short, Player*> TSlots;\n\nclass Players\n{\npublic:\n    static void newPlayer(RakNet::RakNetGUID guid);\n    static void deletePlayer(RakNet::RakNetGUID guid);\n    static Player *getPlayer(RakNet::RakNetGUID guid);\n    static Player *getPlayer(unsigned short id);\n    static TPlayers *getPlayers();\n    static unsigned short getLastPlayerId();\n    static bool doesPlayerExist(RakNet::RakNetGUID guid);\n\nprivate:\n    static TPlayers players;\n    static TSlots slots;\n};\n\nclass Player : public mwmp::BasePlayer\n{\n    friend class Cell;\n    unsigned short id;\npublic:\n\n    enum\n    {\n        NOTLOADED=0,\n        LOADED,\n        POSTLOADED,\n        KICKED\n    };\n    Player(RakNet::RakNetGUID guid);\n\n    unsigned short getId();\n    void setId(unsigned short id);\n\n    bool isHandshaked();\n    int getHandshakeAttempts();\n    void incrementHandshakeAttempts();\n    void setHandshake();\n\n    void setLoadState(int state);\n    int getLoadState();\n\n    virtual ~Player();\n\n    CellController::TContainer *getCells();\n    void sendToLoaded(mwmp::PlayerPacket *myPacket);\n\n    void forEachLoaded(std::function<void(Player *pl, Player *other)> func);\n\nprivate:\n    CellController::TContainer cells;\n    int loadState;\n    int handshakeCounter;\n\n};\n\n#endif //OPENMW_PLAYER_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/API/PublicFnAPI.cpp",
    "content": "#include <Script/ScriptFunction.hpp>\n#include \"PublicFnAPI.hpp\"\n\nstd::unordered_map<std::string, Public *> Public::publics;\n\nPublic::~Public()\n{\n\n}\n\nPublic::Public(ScriptFunc _public, const std::string &name, char ret_type, const std::string &def) : ScriptFunction(_public, ret_type, def)\n{\n    publics.emplace(name, this);\n}\n\nPublic::Public(ScriptFuncLua _public, lua_State *lua, const std::string &name, char ret_type, const std::string &def) : ScriptFunction(\n        _public, lua, ret_type, def)\n{\n    publics.emplace(name, this);\n}\n\nboost::any Public::Call(const std::string &name, const std::vector<boost::any> &args)\n{\n    auto it = publics.find(name);\n    if (it == publics.end())\n        throw std::runtime_error(\"Public with name \\\"\" + name + \"\\\" does not exist\");\n\n    return it->second->ScriptFunction::Call(args);\n}\n\n\nconst std::string &Public::GetDefinition(const std::string &name)\n{\n    auto it = publics.find(name);\n\n    if (it == publics.end())\n        throw std::runtime_error(\"Public with name \\\"\" + name + \"\\\" does not exist\");\n\n    return it->second->def;\n}\n\n\nbool Public::IsLua(const std::string &name)\n{\n#if !defined(ENABLE_LUA)\n    return false;\n#else\n    auto it = publics.find(name);\n    if (it == publics.end())\n        throw std::runtime_error(\"Public with name \\\"\" + name + \"\\\" does not exist\");\n\n    return it->second->script_type == SCRIPT_LUA;\n#endif\n}\n\nvoid Public::DeleteAll()\n{\n    for (auto it = publics.begin(); it != publics.end(); it++)\n    {\n        Public *_public = it->second;\n        delete _public;\n        publics.erase(it);\n    }\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/API/PublicFnAPI.hpp",
    "content": "#ifndef PLUGINSYSTEM3_PUBLICFNAPI_HPP\n#define PLUGINSYSTEM3_PUBLICFNAPI_HPP\n\n#include <unordered_map>\n#include <Script/ScriptFunction.hpp>\n\n\nclass Public : public ScriptFunction\n{\nprivate:\n    ~Public();\n\n    static std::unordered_map<std::string, Public *> publics;\n\n    Public(ScriptFunc _public, const std::string &name, char ret_type, const std::string &def);\n#if defined(ENABLE_LUA)\n    Public(ScriptFuncLua _public, lua_State *lua, const std::string &name, char ret_type, const std::string &def);\n#endif\n\npublic:\n    template<typename... Args>\n    static void MakePublic(Args &&... args)\n    { new Public(std::forward<Args>(args)...); }\n\n    static boost::any Call(const std::string &name, const std::vector<boost::any> &args);\n\n    static const std::string& GetDefinition(const std::string& name);\n\n    static bool IsLua(const std::string &name);\n\n    static void DeleteAll();\n};\n\n#endif //PLUGINSYSTEM3_PUBLICFNAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/API/TimerAPI.cpp",
    "content": "#include \"TimerAPI.hpp\"\n\n#include <chrono>\n\n#include <iostream>\nusing namespace mwmp;\n\nTimer::Timer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args) : ScriptFunction(callback, 'v', def)\n{\n    targetMsec = msec;\n    this->args = args;\n    isEnded = true;\n}\n\n#if defined(ENABLE_LUA)\nTimer::Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args): ScriptFunction(callback, lua, 'v', def)\n{\n    targetMsec = msec;\n    this->args = args;\n    isEnded = true;\n}\n#endif\n\nvoid Timer::Tick()\n{\n    if (isEnded)\n        return;\n\n    const auto duration = std::chrono::system_clock::now().time_since_epoch();\n    const auto time = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();\n\n    if (time - startTime >= targetMsec)\n    {\n        isEnded = true;\n        Call(args);\n    }\n}\n\nbool Timer::IsEnded()\n{\n    return isEnded;\n}\n\nvoid Timer::Stop()\n{\n    isEnded = true;\n}\n\nvoid Timer::Restart(int msec)\n{\n    targetMsec = msec;\n    Start();\n}\n\nvoid Timer::Start()\n{\n    isEnded = false;\n\n    const auto duration = std::chrono::system_clock::now().time_since_epoch();\n    const auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();\n    startTime = msec;\n}\n\nint TimerAPI::pointer = 0;\nstd::unordered_map<int, Timer* > TimerAPI::timers;\n\n#if defined(ENABLE_LUA)\nint TimerAPI::CreateTimerLua(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args)\n{\n    int id = -1;\n\n    for (auto timer : timers)\n    {\n        if (timer.second != nullptr)\n            continue;\n        timer.second = new Timer(lua, callback, msec, def, args);\n        id = timer.first;\n    }\n\n    if (id == -1)\n    {\n        timers[pointer] = new Timer(lua, callback, msec, def, args);\n        id = pointer;\n        pointer++;\n    }\n\n    return id;\n}\n#endif\n\n\nint TimerAPI::CreateTimer(ScriptFunc callback, long msec, const std::string &def, std::vector<boost::any> args)\n{\n    int id = -1;\n\n    for (auto timer : timers)\n    {\n        if (timer.second != nullptr)\n            continue;\n        timer.second = new Timer(callback, msec, def, args);\n        id = timer.first;\n    }\n\n    if (id == -1)\n    {\n        timers[pointer] = new Timer(callback, msec, def, args);\n        id = pointer;\n        pointer++;\n    }\n\n    return id;\n}\n\nvoid TimerAPI::FreeTimer(int timerid)\n{\n\n    try\n    {\n        if (timers.at(timerid) != nullptr)\n        {\n            delete timers[timerid];\n            timers[timerid] = nullptr;\n        }\n    }\n    catch(...)\n    {\n        std::cerr << \"Timer \" << timerid << \" not found!\" << std::endl;\n    }\n}\n\nvoid TimerAPI::ResetTimer(int timerid, long msec)\n{\n    try\n    {\n        timers.at(timerid)->Restart(msec);\n    }\n    catch(...)\n    {\n        std::cerr << \"Timer \" << timerid << \" not found!\" << std::endl;\n    }\n}\n\nvoid TimerAPI::StartTimer(int timerid)\n{\n    try\n    {\n        Timer *timer = timers.at(timerid);\n        if (timer == nullptr)\n            throw 1;\n        timer->Start();\n    }\n    catch(...)\n    {\n        std::cerr << \"Timer \" << timerid << \" not found!\" << std::endl;\n    }\n}\n\nvoid TimerAPI::StopTimer(int timerid)\n{\n    try\n    {\n        timers.at(timerid)->Stop();\n    }\n    catch(...)\n    {\n        std::cerr << \"Timer \" << timerid << \" not found!\" << std::endl;\n    }\n}\n\nbool TimerAPI::IsTimerElapsed(int timerid)\n{\n    bool ret = false;\n    try\n    {\n        ret = timers.at(timerid)->IsEnded();\n    }\n    catch(...)\n    {\n        std::cerr << \"Timer \" << timerid << \" not found!\" << std::endl;\n    }\n    return ret;\n}\n\nvoid TimerAPI::Terminate()\n{\n    for (auto timer : timers)\n    {\n        if (timer.second != nullptr)\n            delete timer.second;\n        timer.second = nullptr;\n    }\n}\n\nvoid TimerAPI::Tick()\n{\n    for (auto timer : timers)\n    {\n        if (timer.second != nullptr)\n            timer.second->Tick();\n    }\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/API/TimerAPI.hpp",
    "content": "#ifndef OPENMW_TIMERAPI_HPP\n#define OPENMW_TIMERAPI_HPP\n\n#include <string>\n\n#include <Script/Script.hpp>\n#include <Script/ScriptFunction.hpp>\n\nnamespace mwmp\n{\n\n    class TimerAPI;\n\n    class Timer: public ScriptFunction\n    {\n        friend class TimerAPI;\n\n    public:\n\n        Timer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args);\n#if defined(ENABLE_LUA)\n        Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args);\n#endif\n        void Tick();\n\n        bool IsEnded();\n        void Stop();\n        void Start();\n        void Restart(int msec);\n    private:\n        double startTime, targetMsec;\n        std::string publ, arg_types;\n        std::vector<boost::any> args;\n        Script *scr;\n        bool isEnded;\n    };\n\n    class TimerAPI\n    {\n    public:\n#if defined(ENABLE_LUA)\n        static int CreateTimerLua(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args);\n#endif\n        static int CreateTimer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args);\n        static void FreeTimer(int timerid);\n        static void ResetTimer(int timerid, long msec);\n        static void StartTimer(int timerid);\n        static void StopTimer(int timerid);\n        static bool IsTimerElapsed(int timerid);\n\n        static void Terminate();\n\n        static void Tick();\n    private:\n        static std::unordered_map<int, Timer* > timers;\n        static int pointer;\n    };\n}\n\n#endif //OPENMW_TIMERAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Actors.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Base/BaseActor.hpp>\n\n#include <apps/openmw-mp/Networking.hpp>\n#include <apps/openmw-mp/Player.hpp>\n#include <apps/openmw-mp/Utils.hpp>\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n\n#include <components/esm/creaturestats.hpp>\n\n#include \"Actors.hpp\"\n\nusing namespace mwmp;\n\nBaseActorList *readActorList;\nBaseActorList writeActorList;\n\nBaseActor tempActor;\nconst BaseActor emptyActor = {};\nstd::vector<ESM::ActiveEffect> storedActorActiveEffects;\n\nstatic std::string tempCellDescription;\n\nvoid ActorFunctions::ReadReceivedActorList() noexcept\n{\n    readActorList = mwmp::Networking::getPtr()->getReceivedActorList();\n}\n\nvoid ActorFunctions::ReadCellActorList(const char* cellDescription) noexcept\n{\n    ESM::Cell esmCell = Utils::getCellFromDescription(cellDescription);\n    Cell *serverCell = CellController::get()->getCell(&esmCell);\n\n    if (serverCell != nullptr)\n        readActorList = serverCell->getActorList();\n    else\n        readActorList = {};\n}\n\nvoid ActorFunctions::ClearActorList() noexcept\n{\n    writeActorList.cell.blank();\n    writeActorList.baseActors.clear();\n}\n\nvoid ActorFunctions::SetActorListPid(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeActorList.guid = player->guid;\n}\n\nvoid ActorFunctions::CopyReceivedActorListToStore() noexcept\n{\n    writeActorList = *readActorList;\n}\n\nunsigned int ActorFunctions::GetActorListSize() noexcept\n{\n    if (readActorList == nullptr)\n        return 0;\n\n    return readActorList->count;\n}\n\nunsigned char ActorFunctions::GetActorListAction() noexcept\n{\n    return readActorList->action;\n}\n\nconst char *ActorFunctions::GetActorCell(unsigned int index) noexcept\n{\n    tempCellDescription = readActorList->baseActors.at(index).cell.getShortDescription();\n    return tempCellDescription.c_str();\n}\n\nconst char *ActorFunctions::GetActorRefId(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).refId.c_str();\n}\n\nunsigned int ActorFunctions::GetActorRefNum(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).refNum;\n}\n\nunsigned int ActorFunctions::GetActorMpNum(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).mpNum;\n}\n\ndouble ActorFunctions::GetActorPosX(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).position.pos[0];\n}\n\ndouble ActorFunctions::GetActorPosY(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).position.pos[1];\n}\n\ndouble ActorFunctions::GetActorPosZ(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).position.pos[2];\n}\n\ndouble ActorFunctions::GetActorRotX(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).position.rot[0];\n}\n\ndouble ActorFunctions::GetActorRotY(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).position.rot[1];\n}\n\ndouble ActorFunctions::GetActorRotZ(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).position.rot[2];\n}\n\ndouble ActorFunctions::GetActorHealthBase(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[0].mBase;\n}\n\ndouble ActorFunctions::GetActorHealthCurrent(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[0].mCurrent;\n}\n\ndouble ActorFunctions::GetActorHealthModified(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[0].mMod;\n}\n\ndouble ActorFunctions::GetActorMagickaBase(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[1].mBase;\n}\n\ndouble ActorFunctions::GetActorMagickaCurrent(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[1].mCurrent;\n}\n\ndouble ActorFunctions::GetActorMagickaModified(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[1].mMod;\n}\n\ndouble ActorFunctions::GetActorFatigueBase(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[2].mBase;\n}\n\ndouble ActorFunctions::GetActorFatigueCurrent(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[2].mCurrent;\n}\n\ndouble ActorFunctions::GetActorFatigueModified(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).creatureStats.mDynamic[2].mMod;\n}\n\nconst char *ActorFunctions::GetActorEquipmentItemRefId(unsigned int index, unsigned short slot) noexcept\n{\n    return readActorList->baseActors.at(index).equipmentItems[slot].refId.c_str();\n}\n\nint ActorFunctions::GetActorEquipmentItemCount(unsigned int index, unsigned short slot) noexcept\n{\n    return readActorList->baseActors.at(index).equipmentItems[slot].count;\n}\n\nint ActorFunctions::GetActorEquipmentItemCharge(unsigned int index, unsigned short slot) noexcept\n{\n    return readActorList->baseActors.at(index).equipmentItems[slot].charge;\n}\n\ndouble ActorFunctions::GetActorEquipmentItemEnchantmentCharge(unsigned int index, unsigned short slot) noexcept\n{\n    return readActorList->baseActors.at(index).equipmentItems[slot].enchantmentCharge;\n}\n\nbool ActorFunctions::DoesActorHavePlayerKiller(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).killer.isPlayer;\n}\n\nint ActorFunctions::GetActorKillerPid(unsigned int index) noexcept\n{\n    Player *player = Players::getPlayer(readActorList->baseActors.at(index).killer.guid);\n\n    if (player != nullptr)\n        return player->getId();\n\n    return -1;\n}\n\nconst char *ActorFunctions::GetActorKillerRefId(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).killer.refId.c_str();\n}\n\nunsigned int ActorFunctions::GetActorKillerRefNum(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).killer.refNum;\n}\n\nunsigned int ActorFunctions::GetActorKillerMpNum(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).killer.mpNum;\n}\n\nconst char *ActorFunctions::GetActorKillerName(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).killer.name.c_str();\n}\n\nunsigned int ActorFunctions::GetActorDeathState(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).deathState;\n}\n\nunsigned int ActorFunctions::GetActorSpellsActiveChangesSize(unsigned int actorIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.size();\n}\n\nunsigned int ActorFunctions::GetActorSpellsActiveChangesAction(unsigned int actorIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.action;\n}\n\nconst char* ActorFunctions::GetActorSpellsActiveId(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).id.c_str();\n}\n\nconst char* ActorFunctions::GetActorSpellsActiveDisplayName(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).params.mDisplayName.c_str();\n}\n\nbool ActorFunctions::GetActorSpellsActiveStackingState(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).isStackingSpell;\n}\n\nunsigned int ActorFunctions::GetActorSpellsActiveEffectCount(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.size();\n}\n\nunsigned int ActorFunctions::GetActorSpellsActiveEffectId(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mEffectId;\n}\n\nint ActorFunctions::GetActorSpellsActiveEffectArg(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mArg;\n}\n\ndouble ActorFunctions::GetActorSpellsActiveEffectMagnitude(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mMagnitude;\n}\n\ndouble ActorFunctions::GetActorSpellsActiveEffectDuration(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mDuration;\n}\n\ndouble ActorFunctions::GetActorSpellsActiveEffectTimeLeft(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mTimeLeft;\n}\n\nbool ActorFunctions::DoesActorSpellsActiveHavePlayerCaster(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).caster.isPlayer;\n}\n\nint ActorFunctions::GetActorSpellsActiveCasterPid(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    Player* caster = Players::getPlayer(readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).caster.guid);\n\n    if (caster != nullptr)\n        return caster->getId();\n\n    return -1;\n}\n\nconst char* ActorFunctions::GetActorSpellsActiveCasterRefId(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).caster.refId.c_str();\n}\n\nunsigned int ActorFunctions::GetActorSpellsActiveCasterRefNum(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).caster.refNum;\n}\n\nunsigned int ActorFunctions::GetActorSpellsActiveCasterMpNum(unsigned int actorIndex, unsigned int spellIndex) noexcept\n{\n    return readActorList->baseActors.at(actorIndex).spellsActiveChanges.activeSpells.at(spellIndex).caster.mpNum;\n}\n\nbool ActorFunctions::DoesActorHavePosition(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).hasPositionData;\n}\n\nbool ActorFunctions::DoesActorHaveStatsDynamic(unsigned int index) noexcept\n{\n    return readActorList->baseActors.at(index).hasStatsDynamicData;\n}\n\nvoid ActorFunctions::SetActorListCell(const char* cellDescription) noexcept\n{\n    writeActorList.cell = Utils::getCellFromDescription(cellDescription);\n}\n\nvoid ActorFunctions::SetActorListAction(unsigned char action) noexcept\n{\n    writeActorList.action = action;\n}\n\nvoid ActorFunctions::SetActorCell(const char* cellDescription) noexcept\n{\n    tempActor.cell = Utils::getCellFromDescription(cellDescription);\n}\n\nvoid ActorFunctions::SetActorRefId(const char* refId) noexcept\n{\n    tempActor.refId = refId;\n}\n\nvoid ActorFunctions::SetActorRefNum(int refNum) noexcept\n{\n    tempActor.refNum = refNum;\n}\n\nvoid ActorFunctions::SetActorMpNum(int mpNum) noexcept\n{\n    tempActor.mpNum = mpNum;\n}\n\nvoid ActorFunctions::SetActorPosition(double x, double y, double z) noexcept\n{\n    tempActor.position.pos[0] = x;\n    tempActor.position.pos[1] = y;\n    tempActor.position.pos[2] = z;\n}\n\nvoid ActorFunctions::SetActorRotation(double x, double y, double z) noexcept\n{\n    tempActor.position.rot[0] = x;\n    tempActor.position.rot[1] = y;\n    tempActor.position.rot[2] = z;\n}\n\nvoid ActorFunctions::SetActorHealthBase(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[0].mBase = value;\n}\n\nvoid ActorFunctions::SetActorHealthCurrent(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[0].mCurrent = value;\n}\n\nvoid ActorFunctions::SetActorHealthModified(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[0].mMod = value;\n}\n\nvoid ActorFunctions::SetActorMagickaBase(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[1].mBase = value;\n}\n\nvoid ActorFunctions::SetActorMagickaCurrent(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[1].mCurrent = value;\n}\n\nvoid ActorFunctions::SetActorMagickaModified(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[1].mMod = value;\n}\n\nvoid ActorFunctions::SetActorFatigueBase(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[2].mBase = value;\n}\n\nvoid ActorFunctions::SetActorFatigueCurrent(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[2].mCurrent = value;\n}\n\nvoid ActorFunctions::SetActorFatigueModified(double value) noexcept\n{\n    tempActor.creatureStats.mDynamic[2].mMod = value;\n}\n\nvoid ActorFunctions::SetActorSound(const char* sound) noexcept\n{\n    tempActor.sound = sound;\n}\n\nvoid ActorFunctions::SetActorDeathState(unsigned int deathState) noexcept\n{\n    tempActor.deathState = deathState;\n}\n\nvoid ActorFunctions::SetActorDeathInstant(bool isInstant) noexcept\n{\n    tempActor.isInstantDeath = isInstant;\n}\n\nvoid ActorFunctions::SetActorSpellsActiveAction(unsigned char action) noexcept\n{\n    tempActor.spellsActiveChanges.action = action;\n}\n\nvoid ActorFunctions::SetActorAIAction(unsigned int action) noexcept\n{\n    tempActor.aiAction = action;\n}\n\nvoid ActorFunctions::SetActorAITargetToPlayer(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    tempActor.hasAiTarget = true;\n    tempActor.aiTarget.isPlayer = true;\n\n    tempActor.aiTarget.guid = player->guid;\n}\n\nvoid ActorFunctions::SetActorAITargetToObject(int refNum, int mpNum) noexcept\n{\n    tempActor.hasAiTarget = true;\n    tempActor.aiTarget.isPlayer = false;\n\n    tempActor.aiTarget.refNum = refNum;\n    tempActor.aiTarget.mpNum = mpNum;\n}\n\nvoid ActorFunctions::SetActorAICoordinates(double x, double y, double z) noexcept\n{\n    tempActor.aiCoordinates.pos[0] = x;\n    tempActor.aiCoordinates.pos[1] = y;\n    tempActor.aiCoordinates.pos[2] = z;\n}\n\nvoid ActorFunctions::SetActorAIDistance(unsigned int distance) noexcept\n{\n    tempActor.aiDistance = distance;\n}\n\nvoid ActorFunctions::SetActorAIDuration(unsigned int duration) noexcept\n{\n    tempActor.aiDuration = duration;\n}\n\nvoid ActorFunctions::SetActorAIRepetition(bool shouldRepeat) noexcept\n{\n    tempActor.aiShouldRepeat = shouldRepeat;\n}\n\nvoid ActorFunctions::EquipActorItem(unsigned short slot, const char *refId, unsigned int count, int charge, double enchantmentCharge) noexcept\n{\n    tempActor.equipmentItems[slot].refId = refId;\n    tempActor.equipmentItems[slot].count = count;\n    tempActor.equipmentItems[slot].charge = charge;\n    tempActor.equipmentItems[slot].enchantmentCharge = enchantmentCharge;\n}\n\nvoid ActorFunctions::UnequipActorItem(unsigned short slot) noexcept\n{\n    ActorFunctions::EquipActorItem(slot, \"\", 0, -1, -1);\n}\n\nvoid ActorFunctions::AddActorSpellActive(const char* spellId, const char* displayName, bool stackingState) noexcept\n{\n    mwmp::ActiveSpell spell;\n    spell.id = spellId;\n    spell.isStackingSpell = stackingState;\n    spell.params.mDisplayName = displayName;\n    spell.params.mEffects = storedActorActiveEffects;\n\n    tempActor.spellsActiveChanges.activeSpells.push_back(spell);\n\n    storedActorActiveEffects.clear();\n}\n\nvoid ActorFunctions::AddActorSpellActiveEffect(int effectId, double magnitude, double duration, double timeLeft, int arg) noexcept\n{\n    ESM::ActiveEffect effect;\n    effect.mEffectId = effectId;\n    effect.mMagnitude = magnitude;\n    effect.mDuration = duration;\n    effect.mTimeLeft = timeLeft;\n    effect.mArg = arg;\n\n    storedActorActiveEffects.push_back(effect);\n}\n\nvoid ActorFunctions::AddActor() noexcept\n{\n    writeActorList.baseActors.push_back(tempActor);\n\n    tempActor = emptyActor;\n}\n\nvoid ActorFunctions::SendActorList() noexcept\n{\n    mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST);\n    actorPacket->setActorList(&writeActorList);\n    actorPacket->Send(writeActorList.guid);\n}\n\nvoid ActorFunctions::SendActorAuthority() noexcept\n{\n    Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n    if (serverCell != nullptr)\n    {\n        serverCell->setAuthority(writeActorList.guid);\n\n        mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AUTHORITY);\n        actorPacket->setActorList(&writeActorList);\n\n        // Always send the packet to everyone on the server, to reduce bugs caused by late-arriving packets\n        actorPacket->Send(false);\n        actorPacket->Send(true);\n    }\n}\n\nvoid ActorFunctions::SendActorPosition(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_POSITION);\n    actorPacket->setActorList(&writeActorList);\n\n    if (!skipAttachedPlayer)\n        actorPacket->Send(writeActorList.guid);\n\n    if (sendToOtherVisitors)\n    {\n        Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n        if (serverCell != nullptr)\n        {\n            serverCell->sendToLoaded(actorPacket, &writeActorList);\n        }\n    }\n}\n\nvoid ActorFunctions::SendActorStatsDynamic(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_STATS_DYNAMIC);\n    actorPacket->setActorList(&writeActorList);\n\n    if (!skipAttachedPlayer)\n        actorPacket->Send(writeActorList.guid);\n\n    if (sendToOtherVisitors)\n    {\n        Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n        if (serverCell != nullptr)\n        {\n            serverCell->sendToLoaded(actorPacket, &writeActorList);\n        }\n    }\n}\n\nvoid ActorFunctions::SendActorEquipment(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_EQUIPMENT);\n    actorPacket->setActorList(&writeActorList);\n\n    if (!skipAttachedPlayer)\n        actorPacket->Send(writeActorList.guid);\n\n    if (sendToOtherVisitors)\n    {\n        Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n        if (serverCell != nullptr)\n        {\n            serverCell->sendToLoaded(actorPacket, &writeActorList);\n        }\n    }\n}\n\nvoid ActorFunctions::SendActorSpellsActiveChanges(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ActorPacket* actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_SPELLS_ACTIVE);\n    actorPacket->setActorList(&writeActorList);\n\n    if (!skipAttachedPlayer)\n        actorPacket->Send(writeActorList.guid);\n\n    if (sendToOtherVisitors)\n    {\n        Cell* serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n        if (serverCell != nullptr)\n        {\n            serverCell->sendToLoaded(actorPacket, &writeActorList);\n        }\n    }\n}\n\nvoid ActorFunctions::SendActorSpeech(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_SPEECH);\n    actorPacket->setActorList(&writeActorList);\n\n    if (!skipAttachedPlayer)\n        actorPacket->Send(writeActorList.guid);\n\n    if (sendToOtherVisitors)\n    {\n        Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n        if (serverCell != nullptr)\n        {\n            serverCell->sendToLoaded(actorPacket, &writeActorList);\n        }\n    }\n}\n\nvoid ActorFunctions::SendActorDeath(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_DEATH);\n    actorPacket->setActorList(&writeActorList);\n\n    if (!skipAttachedPlayer)\n        actorPacket->Send(writeActorList.guid);\n\n    if (sendToOtherVisitors)\n    {\n        Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n        if (serverCell != nullptr)\n        {\n            serverCell->sendToLoaded(actorPacket, &writeActorList);\n        }\n    }\n}\n\nvoid ActorFunctions::SendActorAI(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AI);\n    actorPacket->setActorList(&writeActorList);\n\n    if (!skipAttachedPlayer)\n        actorPacket->Send(writeActorList.guid);\n\n    if (sendToOtherVisitors)\n    {\n        Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n        if (serverCell != nullptr)\n        {\n            serverCell->sendToLoaded(actorPacket, &writeActorList);\n        }\n    }\n}\n\nvoid ActorFunctions::SendActorCellChange(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ActorPacket *actorPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_CELL_CHANGE);\n    actorPacket->setActorList(&writeActorList);\n\n    if (!skipAttachedPlayer)\n        actorPacket->Send(writeActorList.guid);\n\n    if (sendToOtherVisitors)\n    {\n        Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);\n\n        if (serverCell != nullptr)\n        {\n            serverCell->sendToLoaded(actorPacket, &writeActorList);\n        }\n    }\n}\n\n\n// All methods below are deprecated versions of methods from above\n\nvoid ActorFunctions::ReadLastActorList() noexcept\n{\n    ReadReceivedActorList();\n}\n\nvoid ActorFunctions::InitializeActorList(unsigned short pid) noexcept\n{\n    ClearActorList();\n    SetActorListPid(pid);\n}\n\nvoid ActorFunctions::CopyLastActorListToStore() noexcept\n{\n    CopyReceivedActorListToStore();\n}\n\nunsigned int ActorFunctions::GetActorRefNumIndex(unsigned int index) noexcept\n{\n    return GetActorRefNum(index);\n}\n\nunsigned int ActorFunctions::GetActorKillerRefNumIndex(unsigned int index) noexcept\n{\n    return GetActorKillerRefNum(index);\n}\n\nvoid ActorFunctions::SetActorRefNumIndex(int refNum) noexcept\n{\n    tempActor.refNum = refNum;\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Actors.hpp",
    "content": "#ifndef OPENMW_ACTORAPI_HPP\n#define OPENMW_ACTORAPI_HPP\n\n#define ACTORAPI \\\n    {\"ReadReceivedActorList\",                  ActorFunctions::ReadReceivedActorList},\\\n    {\"ReadCellActorList\",                      ActorFunctions::ReadCellActorList},\\\n    \\\n    {\"ClearActorList\",                         ActorFunctions::ClearActorList},\\\n    {\"SetActorListPid\",                        ActorFunctions::SetActorListPid},\\\n    \\\n    {\"CopyReceivedActorListToStore\",           ActorFunctions::CopyReceivedActorListToStore},\\\n    \\\n    {\"GetActorListSize\",                       ActorFunctions::GetActorListSize},\\\n    {\"GetActorListAction\",                     ActorFunctions::GetActorListAction},\\\n    \\\n    {\"GetActorCell\",                           ActorFunctions::GetActorCell},\\\n    {\"GetActorRefId\",                          ActorFunctions::GetActorRefId},\\\n    {\"GetActorRefNum\",                         ActorFunctions::GetActorRefNum},\\\n    {\"GetActorMpNum\",                          ActorFunctions::GetActorMpNum},\\\n    \\\n    {\"GetActorPosX\",                           ActorFunctions::GetActorPosX},\\\n    {\"GetActorPosY\",                           ActorFunctions::GetActorPosY},\\\n    {\"GetActorPosZ\",                           ActorFunctions::GetActorPosZ},\\\n    {\"GetActorRotX\",                           ActorFunctions::GetActorRotX},\\\n    {\"GetActorRotY\",                           ActorFunctions::GetActorRotY},\\\n    {\"GetActorRotZ\",                           ActorFunctions::GetActorRotZ},\\\n    \\\n    {\"GetActorHealthBase\",                     ActorFunctions::GetActorHealthBase},\\\n    {\"GetActorHealthCurrent\",                  ActorFunctions::GetActorHealthCurrent},\\\n    {\"GetActorHealthModified\",                 ActorFunctions::GetActorHealthModified},\\\n    {\"GetActorMagickaBase\",                    ActorFunctions::GetActorMagickaBase},\\\n    {\"GetActorMagickaCurrent\",                 ActorFunctions::GetActorMagickaCurrent},\\\n    {\"GetActorMagickaModified\",                ActorFunctions::GetActorMagickaModified},\\\n    {\"GetActorFatigueBase\",                    ActorFunctions::GetActorFatigueBase},\\\n    {\"GetActorFatigueCurrent\",                 ActorFunctions::GetActorFatigueCurrent},\\\n    {\"GetActorFatigueModified\",                ActorFunctions::GetActorFatigueModified},\\\n    \\\n    {\"GetActorEquipmentItemRefId\",             ActorFunctions::GetActorEquipmentItemRefId},\\\n    {\"GetActorEquipmentItemCount\",             ActorFunctions::GetActorEquipmentItemCount},\\\n    {\"GetActorEquipmentItemCharge\",            ActorFunctions::GetActorEquipmentItemCharge},\\\n    {\"GetActorEquipmentItemEnchantmentCharge\", ActorFunctions::GetActorEquipmentItemEnchantmentCharge},\\\n    \\\n    {\"DoesActorHavePlayerKiller\",              ActorFunctions::DoesActorHavePlayerKiller},\\\n    {\"GetActorKillerPid\",                      ActorFunctions::GetActorKillerPid},\\\n    {\"GetActorKillerRefId\",                    ActorFunctions::GetActorKillerRefId},\\\n    {\"GetActorKillerRefNum\",                   ActorFunctions::GetActorKillerRefNum},\\\n    {\"GetActorKillerMpNum\",                    ActorFunctions::GetActorKillerMpNum},\\\n    {\"GetActorKillerName\",                     ActorFunctions::GetActorKillerName},\\\n    {\"GetActorDeathState\",                     ActorFunctions::GetActorDeathState},\\\n    \\\n    {\"GetActorSpellsActiveChangesSize\",        ActorFunctions::GetActorSpellsActiveChangesSize},\\\n    {\"GetActorSpellsActiveChangesAction\",      ActorFunctions::GetActorSpellsActiveChangesAction},\\\n    {\"GetActorSpellsActiveId\",                 ActorFunctions::GetActorSpellsActiveId},\\\n    {\"GetActorSpellsActiveDisplayName\",        ActorFunctions::GetActorSpellsActiveDisplayName},\\\n    {\"GetActorSpellsActiveStackingState\",      ActorFunctions::GetActorSpellsActiveStackingState},\\\n    {\"GetActorSpellsActiveEffectCount\",        ActorFunctions::GetActorSpellsActiveEffectCount},\\\n    {\"GetActorSpellsActiveEffectId\",           ActorFunctions::GetActorSpellsActiveEffectId},\\\n    {\"GetActorSpellsActiveEffectArg\",          ActorFunctions::GetActorSpellsActiveEffectArg},\\\n    {\"GetActorSpellsActiveEffectMagnitude\",    ActorFunctions::GetActorSpellsActiveEffectMagnitude},\\\n    {\"GetActorSpellsActiveEffectDuration\",     ActorFunctions::GetActorSpellsActiveEffectDuration},\\\n    {\"GetActorSpellsActiveEffectTimeLeft\",     ActorFunctions::GetActorSpellsActiveEffectTimeLeft},\\\n    \\\n    {\"DoesActorSpellsActiveHavePlayerCaster\",  ActorFunctions::DoesActorSpellsActiveHavePlayerCaster},\\\n    {\"GetActorSpellsActiveCasterPid\",          ActorFunctions::GetActorSpellsActiveCasterPid},\\\n    {\"GetActorSpellsActiveCasterRefId\",        ActorFunctions::GetActorSpellsActiveCasterRefId},\\\n    {\"GetActorSpellsActiveCasterRefNum\",       ActorFunctions::GetActorSpellsActiveCasterRefNum},\\\n    {\"GetActorSpellsActiveCasterMpNum\",        ActorFunctions::GetActorSpellsActiveCasterMpNum},\\\n    \\\n    {\"DoesActorHavePosition\",                  ActorFunctions::DoesActorHavePosition},\\\n    {\"DoesActorHaveStatsDynamic\",              ActorFunctions::DoesActorHaveStatsDynamic},\\\n    \\\n    {\"SetActorListCell\",                       ActorFunctions::SetActorListCell},\\\n    {\"SetActorListAction\",                     ActorFunctions::SetActorListAction},\\\n    \\\n    {\"SetActorCell\",                           ActorFunctions::SetActorCell},\\\n    {\"SetActorRefId\",                          ActorFunctions::SetActorRefId},\\\n    {\"SetActorRefNum\",                         ActorFunctions::SetActorRefNum},\\\n    {\"SetActorMpNum\",                          ActorFunctions::SetActorMpNum},\\\n    \\\n    {\"SetActorPosition\",                       ActorFunctions::SetActorPosition},\\\n    {\"SetActorRotation\",                       ActorFunctions::SetActorRotation},\\\n    \\\n    {\"SetActorHealthBase\",                     ActorFunctions::SetActorHealthBase},\\\n    {\"SetActorHealthCurrent\",                  ActorFunctions::SetActorHealthCurrent},\\\n    {\"SetActorHealthModified\",                 ActorFunctions::SetActorHealthModified},\\\n    {\"SetActorMagickaBase\",                    ActorFunctions::SetActorMagickaBase},\\\n    {\"SetActorMagickaCurrent\",                 ActorFunctions::SetActorMagickaCurrent},\\\n    {\"SetActorMagickaModified\",                ActorFunctions::SetActorMagickaModified},\\\n    {\"SetActorFatigueBase\",                    ActorFunctions::SetActorFatigueBase},\\\n    {\"SetActorFatigueCurrent\",                 ActorFunctions::SetActorFatigueCurrent},\\\n    {\"SetActorFatigueModified\",                ActorFunctions::SetActorFatigueModified},\\\n    \\\n    {\"SetActorDeathState\",                     ActorFunctions::SetActorDeathState},\\\n    {\"SetActorDeathInstant\",                   ActorFunctions::SetActorDeathInstant},\\\n    {\"SetActorSound\",                          ActorFunctions::SetActorSound},\\\n    {\"SetActorSpellsActiveAction\",             ActorFunctions::SetActorSpellsActiveAction},\\\n    \\\n    {\"SetActorAIAction\",                       ActorFunctions::SetActorAIAction},\\\n    {\"SetActorAITargetToPlayer\",               ActorFunctions::SetActorAITargetToPlayer},\\\n    {\"SetActorAITargetToObject\",               ActorFunctions::SetActorAITargetToObject},\\\n    {\"SetActorAICoordinates\",                  ActorFunctions::SetActorAICoordinates},\\\n    {\"SetActorAIDistance\",                     ActorFunctions::SetActorAIDistance},\\\n    {\"SetActorAIDuration\",                     ActorFunctions::SetActorAIDuration},\\\n    {\"SetActorAIRepetition\",                   ActorFunctions::SetActorAIRepetition},\\\n    \\\n    {\"EquipActorItem\",                         ActorFunctions::EquipActorItem},\\\n    {\"UnequipActorItem\",                       ActorFunctions::UnequipActorItem},\\\n    \\\n    {\"AddActorSpellActive\",                    ActorFunctions::AddActorSpellActive},\\\n    {\"AddActorSpellActiveEffect\",              ActorFunctions::AddActorSpellActiveEffect},\\\n    \\\n    {\"AddActor\",                               ActorFunctions::AddActor},\\\n    \\\n    {\"SendActorList\",                          ActorFunctions::SendActorList},\\\n    {\"SendActorAuthority\",                     ActorFunctions::SendActorAuthority},\\\n    {\"SendActorPosition\",                      ActorFunctions::SendActorPosition},\\\n    {\"SendActorStatsDynamic\",                  ActorFunctions::SendActorStatsDynamic},\\\n    {\"SendActorEquipment\",                     ActorFunctions::SendActorEquipment},\\\n    {\"SendActorSpellsActiveChanges\",           ActorFunctions::SendActorSpellsActiveChanges},\\\n    {\"SendActorSpeech\",                        ActorFunctions::SendActorSpeech},\\\n    {\"SendActorDeath\",                         ActorFunctions::SendActorDeath},\\\n    {\"SendActorAI\",                            ActorFunctions::SendActorAI},\\\n    {\"SendActorCellChange\",                    ActorFunctions::SendActorCellChange},\\\n    \\\n    {\"ReadLastActorList\",                      ActorFunctions::ReadLastActorList},\\\n    {\"InitializeActorList\",                    ActorFunctions::InitializeActorList},\\\n    {\"CopyLastActorListToStore\",               ActorFunctions::CopyLastActorListToStore},\\\n    {\"GetActorRefNumIndex\",                    ActorFunctions::GetActorRefNumIndex},\\\n    {\"GetActorKillerRefNumIndex\",              ActorFunctions::GetActorKillerRefNumIndex},\\\n    {\"SetActorRefNumIndex\",                    ActorFunctions::SetActorRefNumIndex}\n\nclass ActorFunctions\n{\npublic:\n\n    /**\n    * \\brief Use the last actor list received by the server as the one being read.\n    *\n    * \\return void\n    */\n    static void ReadReceivedActorList() noexcept;\n\n    /**\n    * \\brief Use the temporary actor list stored for a cell as the one being read.\n    *\n    * This type of actor list is used to store actor positions and dynamic stats and is deleted\n    * when the cell is unloaded.\n    *\n    * \\param cellDescription The description of the cell whose actor list should be read.\n    * \\return void\n    */\n    static void ReadCellActorList(const char* cellDescription) noexcept;\n\n    /**\n    * \\brief Clear the data from the actor list stored on the server.\n    *\n    * \\return void\n    */\n    static void ClearActorList() noexcept;\n\n    /**\n    * \\brief Set the pid attached to the ActorList.\n    *\n    * \\param pid The player ID to whom the actor list should be attached.\n    * \\return void\n    */\n    static void SetActorListPid(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Take the contents of the read-only actor list last received by the\n    *        server from a player and move its contents to the stored object list\n    *        that can be sent by the server.\n    *\n    * \\return void\n    */\n    static void CopyReceivedActorListToStore() noexcept;\n\n    /**\n    * \\brief Get the number of indexes in the read actor list.\n    *\n    * \\return The number of indexes.\n    */\n    static unsigned int GetActorListSize() noexcept;\n\n    /**\n    * \\brief Get the action type used in the read actor list.\n    *\n    * \\return The action type (0 for SET, 1 for ADD, 2 for REMOVE, 3 for REQUEST).\n    */\n    static unsigned char GetActorListAction() noexcept;\n\n    /**\n    * \\brief Get the cell description of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The cell description.\n    */\n    static const char *GetActorCell(unsigned int index) noexcept;\n    \n    /**\n    * \\brief Get the refId of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The refId.\n    */\n    static const char *GetActorRefId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refNum of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The refNum.\n    */\n    static unsigned int GetActorRefNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The mpNum.\n    */\n    static unsigned int GetActorMpNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the X position of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The X position.\n    */\n    static double GetActorPosX(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Y position of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The Y position.\n    */\n    static double GetActorPosY(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Z position of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The Z position.\n    */\n    static double GetActorPosZ(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the X rotation of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The X rotation.\n    */\n    static double GetActorRotX(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Y rotation of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The Y rotation.\n    */\n    static double GetActorRotY(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Z rotation of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The Z rotation.\n    */\n    static double GetActorRotZ(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the base health of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The base health.\n    */\n    static double GetActorHealthBase(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the current health of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The current health.\n    */\n    static double GetActorHealthCurrent(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the modified health of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The modified health.\n    */\n    static double GetActorHealthModified(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the base magicka of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The base magicka.\n    */\n    static double GetActorMagickaBase(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the current magicka of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The current magicka.\n    */\n    static double GetActorMagickaCurrent(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the modified magicka of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The modified magicka.\n    */\n    static double GetActorMagickaModified(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the base fatigue of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The base fatigue.\n    */\n    static double GetActorFatigueBase(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the current fatigue of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The current fatigue.\n    */\n    static double GetActorFatigueCurrent(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the modified fatigue of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The modified fatigue.\n    */\n    static double GetActorFatigueModified(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refId of the item in a certain slot of the equipment of the actor at a\n    * certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\param slot The slot of the equipment item.\n    * \\return The refId.\n    */\n    static const char *GetActorEquipmentItemRefId(unsigned int index, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Get the count of the item in a certain slot of the equipment of the actor at a\n    * certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\param slot The slot of the equipment item.\n    * \\return The item count.\n    */\n    static int GetActorEquipmentItemCount(unsigned int index, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Get the charge of the item in a certain slot of the equipment of the actor at a\n    * certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\param slot The slot of the equipment item.\n    * \\return The charge.\n    */\n    static int GetActorEquipmentItemCharge(unsigned int index, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Get the enchantment charge of the item in a certain slot of the equipment of the actor at a\n    * certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\param slot The slot of the equipment item.\n    * \\return The enchantment charge.\n    */\n    static double GetActorEquipmentItemEnchantmentCharge(unsigned int index, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Check whether the killer of the actor at a certain index in the read actor list is a player.\n    *\n    * \\param index The index of the actor.\n    * \\return Whether the actor was killed by a player.\n    */\n    static bool DoesActorHavePlayerKiller(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the player ID of the killer of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The player ID of the killer.\n    */\n    static int GetActorKillerPid(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refId of the actor killer of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The refId of the killer.\n    */\n    static const char *GetActorKillerRefId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refNum of the actor killer of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The refNum of the killer.\n    */\n    static unsigned int GetActorKillerRefNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the actor killer of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The mpNum of the killer.\n    */\n    static unsigned int GetActorKillerMpNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the name of the actor killer of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The name of the killer.\n    */\n    static const char *GetActorKillerName(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the deathState of the actor at a certain index in the read actor list.\n    *\n    * \\param index The index of the actor.\n    * \\return The deathState.\n    */\n    static unsigned int GetActorDeathState(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\return The number of indexes for spells active changes.\n    */\n    static unsigned int GetActorSpellsActiveChangesSize(unsigned int actorIndex) noexcept;\n\n    /**\n    * \\brief Get the action type used in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\return The action type (0 for SET, 1 for ADD, 2 for REMOVE).\n    */\n    static unsigned int GetActorSpellsActiveChangesAction(unsigned int actorIndex) noexcept;\n\n    /**\n    * \\brief Get the spell id at a certain index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return The spell id.\n    */\n    static const char* GetActorSpellsActiveId(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Get the spell display name at a certain index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return The spell display name.\n    */\n    static const char* GetActorSpellsActiveDisplayName(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Get the spell stacking state at a certain index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return The spell stacking state.\n    */\n    static bool GetActorSpellsActiveStackingState(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Get the number of effects at an index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return The number of effects.\n    */\n    static unsigned int GetActorSpellsActiveEffectCount(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Get the id for an effect index at a spell index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The id of the effect.\n    */\n    static unsigned int GetActorSpellsActiveEffectId(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the arg for an effect index at a spell index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The arg of the effect.\n    */\n    static int GetActorSpellsActiveEffectArg(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the magnitude for an effect index at a spell index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The magnitude of the effect.\n    */\n    static double GetActorSpellsActiveEffectMagnitude(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the duration for an effect index at a spell index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The duration of the effect.\n    */\n    static double GetActorSpellsActiveEffectDuration(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the time left for an effect index at a spell index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The time left for the effect.\n    */\n    static double GetActorSpellsActiveEffectTimeLeft(unsigned int actorIndex, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Check whether the spell at a certain index in an actor's latest spells active changes has a player\n    *        as its caster.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return Whether a player is the caster of the spell.\n    */\n    static bool DoesActorSpellsActiveHavePlayerCaster(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Get the player ID of the caster of the spell at a certain index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return The player ID of the caster.\n    */\n    static int GetActorSpellsActiveCasterPid(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Get the refId of the actor caster of the spell at a certain index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return The refId of the caster.\n    */\n    static const char* GetActorSpellsActiveCasterRefId(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Get the refNum of the actor caster of the spell at a certain index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return The refNum of the caster.\n    */\n    static unsigned int GetActorSpellsActiveCasterRefNum(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the actor caster of the spell at a certain index in an actor's latest spells active changes.\n    *\n    * \\param actorIndex The index of the actor.\n    * \\param spellIndex The index of the spell.\n    * \\return The mpNum of the caster.\n    */\n    static unsigned int GetActorSpellsActiveCasterMpNum(unsigned int actorIndex, unsigned int spellIndex) noexcept;\n\n    /**\n    * \\brief Check whether there is any positional data for the actor at a certain index in\n    * the read actor list.\n    *\n    * This is only useful when reading the actor list data recorded for a particular cell.\n    *\n    * \\param index The index of the actor.\n    * \\return Whether the read actor list contains positional data.\n    */\n    static bool DoesActorHavePosition(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether there is any dynamic stats data for the actor at a certain index in\n    * the read actor list.\n    *\n    * This is only useful when reading the actor list data recorded for a particular cell.\n    *\n    * \\param index The index of the actor.\n    * \\return Whether the read actor list contains dynamic stats data.\n    */\n    static bool DoesActorHaveStatsDynamic(unsigned int index) noexcept;\n\n    /**\n    * \\brief Set the cell of the temporary actor list stored on the server.\n    *\n    * The cell is determined to be an exterior cell if it fits the pattern of a number followed\n    * by a comma followed by another number.\n    *\n    * \\param cellDescription The description of the cell.\n    * \\return void\n    */\n    static void SetActorListCell(const char* cellDescription) noexcept;\n\n    /**\n    * \\brief Set the action type of the temporary actor list stored on the server.\n    *\n    * \\param action The action type (0 for SET, 1 for ADD, 2 for REMOVE, 3 for REQUEST).\n    * \\return void\n    */\n    static void SetActorListAction(unsigned char action) noexcept;\n\n    /**\n    * \\brief Set the cell of the temporary actor stored on the server.\n    *\n    * Used for ActorCellChange packets, where a specific actor's cell now differs from that of the\n    * actor list.\n    *\n    * The cell is determined to be an exterior cell if it fits the pattern of a number followed\n    * by a comma followed by another number.\n    *\n    * \\param cellDescription The description of the cell.\n    * \\return void\n    */\n    static void SetActorCell(const char* cellDescription) noexcept;\n\n    /**\n    * \\brief Set the refId of the temporary actor stored on the server.\n    *\n    * \\param refId The refId.\n    * \\return void\n    */\n    static void SetActorRefId(const char* refId) noexcept;\n\n    /**\n    * \\brief Set the refNum of the temporary actor stored on the server.\n    *\n    * \\param refNum The refNum.\n    * \\return void\n    */\n    static void SetActorRefNum(int refNum) noexcept;\n\n    /**\n    * \\brief Set the mpNum of the temporary actor stored on the server.\n    *\n    * \\param mpNum The mpNum.\n    * \\return void\n    */\n    static void SetActorMpNum(int mpNum) noexcept;\n\n    /**\n    * \\brief Set the position of the temporary actor stored on the server.\n    *\n    * \\param x The X position.\n    * \\param y The Y position.\n    * \\param z The Z position.\n    * \\return void\n    */\n    static void SetActorPosition(double x, double y, double z) noexcept;\n\n    /**\n    * \\brief Set the rotation of the temporary actor stored on the server.\n    *\n    * \\param x The X rotation.\n    * \\param y The Y rotation.\n    * \\param z The Z rotation.\n    * \\return void\n    */\n    static void SetActorRotation(double x, double y, double z) noexcept;\n\n    /**\n    * \\brief Set the base health of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorHealthBase(double value) noexcept;\n\n    /**\n    * \\brief Set the current health of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorHealthCurrent(double value) noexcept;\n\n    /**\n    * \\brief Set the modified health of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorHealthModified(double value) noexcept;\n\n    /**\n    * \\brief Set the base magicka of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorMagickaBase(double value) noexcept;\n\n    /**\n    * \\brief Set the current magicka of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorMagickaCurrent(double value) noexcept;\n\n    /**\n    * \\brief Set the modified magicka of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorMagickaModified(double value) noexcept;\n\n    /**\n    * \\brief Set the base fatigue of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorFatigueBase(double value) noexcept;\n\n    /**\n    * \\brief Set the current fatigue of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorFatigueCurrent(double value) noexcept;\n\n    /**\n    * \\brief Set the modified fatigue of the temporary actor stored on the server.\n    *\n    * \\param value The new value.\n    * \\return void\n    */\n    static void SetActorFatigueModified(double value) noexcept;\n\n    /**\n    * \\brief Set the sound of the temporary actor stored on the server.\n    *\n    * \\param sound The sound.\n    * \\return void\n    */\n    static void SetActorSound(const char* sound) noexcept;\n\n    /**\n    * \\brief Set the deathState of the temporary actor stored on the server.\n    *\n    * \\param deathState The deathState.\n    * \\return void\n    */\n    static void SetActorDeathState(unsigned int deathState) noexcept;\n\n    /**\n    * \\brief Set whether the death of the temporary actor stored on the server should\n    *        be instant or not.\n    *\n    * \\param isInstant Whether the death should be instant.\n    * \\return void\n    */\n    static void SetActorDeathInstant(bool isInstant) noexcept;\n\n    /**\n    * \\brief Set the action type in the spells active changes of the temporary actor\n    *        stored on the server.\n    *\n    * \\param action The action (0 for SET, 1 for ADD, 2 for REMOVE).\n    * \\return void\n    */\n    static void SetActorSpellsActiveAction(unsigned char action) noexcept;\n\n    /**\n    * \\brief Set the AI action of the temporary actor stored on the server.\n    *\n    * \\param action The new action.\n    * \\return void\n    */\n    static void SetActorAIAction(unsigned int action) noexcept;\n\n    /**\n    * \\brief Set a player as the AI target of the temporary actor stored on the server.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SetActorAITargetToPlayer(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set another object as the AI target of the temporary actor stored on the server.\n    *\n    * \\param refNum The refNum of the target object.\n    * \\param mpNum The mpNum of the target object.\n    * \\return void\n    */\n    static void SetActorAITargetToObject(int refNum, int mpNum) noexcept;\n\n    /**\n    * \\brief Set the coordinates for the AI package associated with the current AI action.\n    *\n    * \\param x The X coordinate.\n    * \\param y The Y coordinate.\n    * \\param z The Z coordinate.\n    * \\return void\n    */\n    static void SetActorAICoordinates(double x, double y, double z) noexcept;\n\n    /**\n    * \\brief Set the distance of the AI package associated with the current AI action.\n    *\n    * \\param distance The distance of the package.\n    * \\return void\n    */\n    static void SetActorAIDistance(unsigned int distance) noexcept;\n\n    /**\n    * \\brief Set the duration of the AI package associated with the current AI action.\n    *\n    * \\param duration The duration of the package.\n    * \\return void\n    */\n    static void SetActorAIDuration(unsigned int duration) noexcept;\n\n    /**\n    * \\brief Set whether the current AI package should be repeated.\n    *\n    * Note: This only has an effect on the WANDER package.\n    *\n    * \\param shouldRepeat Whether the package should be repeated.\n    * \\return void\n    */\n    static void SetActorAIRepetition(bool shouldRepeat) noexcept;\n\n    /**\n    * \\brief Equip an item in a certain slot of the equipment of the temporary actor stored\n    * on the server.\n    *\n    * \\param slot The equipment slot.\n    * \\param refId The refId of the item.\n    * \\param count The count of the item.\n    * \\param charge The charge of the item.\n    * \\param enchantmentCharge The enchantment charge of the item.\n    * \\return void\n    */\n    static void EquipActorItem(unsigned short slot, const char* refId, unsigned int count, int charge, double enchantmentCharge = -1) noexcept;\n\n    /**\n    * \\brief Unequip the item in a certain slot of the equipment of the temporary actor stored\n    * on the server.\n    *\n    * \\param slot The equipment slot.\n    * \\return void\n    */\n    static void UnequipActorItem(unsigned short slot) noexcept;\n\n    /**\n    * \\brief Add a new active spell to the spells active changes for the temporary actor stored,\n    *        on the server, using the temporary effect values stored so far.\n    *\n    * \\param spellId The spellId of the spell.\n    * \\param displayName The displayName of the spell.\n    * \\param stackingState Whether the spell should stack with other instances of itself.\n    * \\return void\n    */\n    static void AddActorSpellActive(const char* spellId, const char* displayName, bool stackingState) noexcept;\n\n    /**\n    * \\brief Add a new effect to the next active spell that will be added to the temporary actor\n    *        stored on the server.\n    *\n    * \\param effectId The id of the effect.\n    * \\param magnitude The magnitude of the effect.\n    * \\param duration The duration of the effect.\n    * \\param timeLeft The timeLeft for the effect.\n    * \\param arg The arg of the effect when applicable, e.g. the skill used for Fortify Skill or the attribute\n    *            used for Fortify Attribute.\n    * \\return void\n    */\n    static void AddActorSpellActiveEffect(int effectId, double magnitude, double duration, double timeLeft, int arg) noexcept;\n\n    /**\n    * \\brief Add a copy of the server's temporary actor to the server's temporary actor list.\n    *\n    * In the process, the server's temporary actor will automatically be cleared so a new\n    * one can be set up.\n    *\n    * \\return void\n    */\n    static void AddActor() noexcept;\n\n    /**\n    * \\brief Send an ActorList packet.\n    *\n    * It is sent only to the player for whom the current actor list was initialized.\n    *\n    * \\return void\n    */\n    static void SendActorList() noexcept;\n\n    /**\n    * \\brief Send an ActorAuthority packet.\n    *\n    * The player for whom the current actor list was initialized is recorded in the server memory\n    * as the new actor authority for the actor list's cell.\n    *\n    * The packet is sent to that player as well as all other players who have the cell loaded.\n    *\n    * \\return void\n    */\n    static void SendActorAuthority() noexcept;\n\n    /**\n    * \\brief Send an ActorPosition packet.\n    *\n    * \\param sendToOtherVisitors Whether this packet should be sent to cell visitors other\n    *                            than the player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    *\n    * \\return void\n    */\n    static void SendActorPosition(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ActorStatsDynamic packet.\n    *\n    * \\param sendToOtherVisitors Whether this packet should be sent to cell visitors other\n    *                            than the player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    *\n    * \\return void\n    */\n    static void SendActorStatsDynamic(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ActorEquipment packet.\n    *\n    * \\param sendToOtherVisitors Whether this packet should be sent to cell visitors other\n    *                            than the player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    *\n    * \\return void\n    */\n    static void SendActorEquipment(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ActorSpellsActive packet.\n    *\n    * \\param sendToOtherVisitors Whether this packet should be sent to cell visitors other\n    *                            than the player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    *\n    * \\return void\n    */\n    static void SendActorSpellsActiveChanges(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ActorSpeech packet.\n    *\n    * \\param sendToOtherVisitors Whether this packet should be sent to cell visitors other\n    *                            than the player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendActorSpeech(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ActorDeath packet.\n    *\n    * \\param sendToOtherVisitors Whether this packet should be sent to cell visitors other\n    *                            than the player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendActorDeath(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ActorAI packet.\n    *\n    * \\param sendToOtherVisitors Whether this packet should be sent to cell visitors other\n    *                            than the player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendActorAI(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ActorCellChange packet.\n    *\n    * \\param sendToOtherVisitors Whether this packet should be sent to cell visitors other\n    *                            than the player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    *\n    * \\return void\n    */\n    static void SendActorCellChange(bool sendToOtherVisitors, bool skipAttachedPlayer) noexcept;\n\n\n    // All methods below are deprecated versions of methods from above\n\n    static void ReadLastActorList() noexcept;\n    static void InitializeActorList(unsigned short pid) noexcept;\n    static void CopyLastActorListToStore() noexcept;\n    static unsigned int GetActorRefNumIndex(unsigned int index) noexcept;\n    static unsigned int GetActorKillerRefNumIndex(unsigned int index) noexcept;\n    static void SetActorRefNumIndex(int refNum) noexcept;\n};\n\n\n#endif //OPENMW_ACTORAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Books.cpp",
    "content": "#include \"Books.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\nusing namespace mwmp;\n\nvoid BookFunctions::ClearBookChanges(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->bookChanges.clear();\n}\n\nunsigned int BookFunctions::GetBookChangesSize(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->bookChanges.size();\n}\n\nvoid BookFunctions::AddBook(unsigned short pid, const char* bookId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::Book book;\n    book.bookId = bookId;\n\n    player->bookChanges.push_back(book);\n}\n\nconst char *BookFunctions::GetBookId(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->bookChanges.size())\n        return \"invalid\";\n\n    return player->bookChanges.at(index).bookId.c_str();\n}\n\nvoid BookFunctions::SendBookChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOOK);\n\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\n// All methods below are deprecated versions of methods from above\n\nvoid BookFunctions::InitializeBookChanges(unsigned short pid) noexcept\n{\n    ClearBookChanges(pid);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Books.hpp",
    "content": "#ifndef OPENMW_BOOKAPI_HPP\n#define OPENMW_BOOKAPI_HPP\n\n#define BOOKAPI \\\n    {\"ClearBookChanges\",       BookFunctions::ClearBookChanges},\\\n    \\\n    {\"GetBookChangesSize\",     BookFunctions::GetBookChangesSize},\\\n    \\\n    {\"AddBook\",                BookFunctions::AddBook},\\\n    \\\n    {\"GetBookId\",              BookFunctions::GetBookId},\\\n    \\\n    {\"SendBookChanges\",        BookFunctions::SendBookChanges},\\\n    \\\n    {\"InitializeBookChanges\",  BookFunctions::InitializeBookChanges}\n\nclass BookFunctions\n{\npublic:\n\n    /**\n    * \\brief Clear the last recorded book changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerBook packets.\n    *\n    * \\param pid The player ID whose book changes should be used.\n    * \\return void\n    */\n    static void ClearBookChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest book changes.\n    *\n    * \\param pid The player ID whose book changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetBookChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Add a new book to the book changes for a player.\n    *\n    * \\param pid The player ID whose book changes should be used.\n    * \\param bookId The bookId of the book.\n    * \\return void\n    */\n    static void AddBook(unsigned short pid, const char* bookId) noexcept;\n\n    /**\n    * \\brief Get the bookId at a certain index in a player's latest book changes.\n    *\n    * \\param pid The player ID whose book changes should be used.\n    * \\param index The index of the book.\n    * \\return The bookId.\n    */\n    static const char *GetBookId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Send a PlayerBook packet with a player's recorded book changes.\n    *\n    * \\param pid The player ID whose book changes should be used.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendBookChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static void InitializeBookChanges(unsigned short pid) noexcept;\n\n};\n\n#endif //OPENMW_BOOKAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Cells.cpp",
    "content": "#include \"Cells.hpp\"\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Player.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\n#include <iostream>\n\nstatic std::string tempCellDescription;\n\nunsigned int CellFunctions::GetCellStateChangesSize(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->cellStateChanges.size();\n}\n\nunsigned int CellFunctions::GetCellStateType(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->cellStateChanges.at(index).type;\n}\n\nconst char *CellFunctions::GetCellStateDescription(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->cellStateChanges.size())\n        return \"invalid\";\n\n    tempCellDescription = player->cellStateChanges.at(index).cell.getShortDescription();\n    return tempCellDescription.c_str();\n}\n\nconst char *CellFunctions::GetCell(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    tempCellDescription = player->cell.getShortDescription().c_str();\n    return tempCellDescription.c_str();\n}\n\nint CellFunctions::GetExteriorX(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n    return player->cell.mData.mX;\n}\n\nint CellFunctions::GetExteriorY(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n    return player->cell.mData.mY;\n}\n\nbool CellFunctions::IsInExterior(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, false);\n\n    return player->cell.isExterior();\n}\n\nconst char *CellFunctions::GetRegion(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->cell.mRegion.c_str();\n}\n\nbool CellFunctions::IsChangingRegion(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, false);\n\n    return player->isChangingRegion;\n}\n\nvoid CellFunctions::SetCell(unsigned short pid, const char *cellDescription) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Script is moving %s from %s to %s\", player->npc.mName.c_str(),\n                       player->cell.getShortDescription().c_str(), cellDescription);\n\n    player->cell = Utils::getCellFromDescription(cellDescription);\n}\n\nvoid CellFunctions::SetExteriorCell(unsigned short pid, int x, int y) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Script is moving %s from %s to %i,%i\", player->npc.mName.c_str(),\n                       player->cell.getShortDescription().c_str(), x, y);\n\n    // If the player is currently in an interior, turn off the interior flag\n    // from the cell\n    if (!player->cell.isExterior())\n        player->cell.mData.mFlags &= ~ESM::Cell::Interior;\n\n    player->cell.mData.mX = x;\n    player->cell.mData.mY = y;\n}\n\nvoid CellFunctions::SendCell(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CELL_CHANGE);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Cells.hpp",
    "content": "#ifndef OPENMW_CELLAPI_HPP\n#define OPENMW_CELLAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define CELLAPI \\\n    {\"GetCellStateChangesSize\", CellFunctions::GetCellStateChangesSize},\\\n    \\\n    {\"GetCellStateType\",        CellFunctions::GetCellStateType},\\\n    {\"GetCellStateDescription\", CellFunctions::GetCellStateDescription},\\\n    \\\n    {\"GetCell\",                 CellFunctions::GetCell},\\\n    {\"GetExteriorX\",            CellFunctions::GetExteriorX},\\\n    {\"GetExteriorY\",            CellFunctions::GetExteriorY},\\\n    {\"IsInExterior\",            CellFunctions::IsInExterior},\\\n    \\\n    {\"GetRegion\",               CellFunctions::GetRegion},\\\n    {\"IsChangingRegion\",        CellFunctions::IsChangingRegion},\\\n    \\\n    {\"SetCell\",                 CellFunctions::SetCell},\\\n    {\"SetExteriorCell\",         CellFunctions::SetExteriorCell},\\\n    \\\n    {\"SendCell\",                CellFunctions::SendCell}\n\n\nclass CellFunctions\n{\npublic:\n\n    /**\n    * \\brief Get the number of indexes in a player's latest cell state changes.\n    *\n    * \\param pid The player ID whose cell state changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetCellStateChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the cell state type at a certain index in a player's latest cell state changes.\n    *\n    * \\param pid The player ID whose cell state changes should be used.\n    * \\param index The index of the cell state.\n    * \\return The cell state type (0 for LOAD, 1 for UNLOAD).\n    */\n    static unsigned int GetCellStateType(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the cell description at a certain index in a player's latest cell state changes.\n    *\n    * \\param pid The player ID whose cell state changes should be used.\n    * \\param index The index of the cell state.\n    * \\return The cell description.\n    */\n    static const char *GetCellStateDescription(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the cell description of a player's cell.\n    *\n    * \\param pid The player ID.\n    * \\return The cell description.\n    */\n    static const char *GetCell(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the X coordinate of the player's exterior cell.\n    *\n    * \\param pid The player ID.\n    * \\return The X coordinate of the cell.\n    */\n    static int GetExteriorX(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Y coordinate of the player's exterior cell.\n    *\n    * \\param pid The player ID.\n    * \\return The Y coordinate of the cell.\n    */\n    static int GetExteriorY(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Check whether the player is in an exterior cell or not.\n    *\n    * \\param pid The player ID.\n    * \\return Whether the player is in an exterior cell.\n    */\n    static bool IsInExterior(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the region of the player's exterior cell.\n    *\n    * A blank value will be returned if the player is in an interior.\n    *\n    * \\param pid The player ID.\n    * \\return The region.\n    */\n    static const char *GetRegion(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Check whether the player's last cell change has involved a region change.\n    *\n    * \\param pid The player ID.\n    * \\return Whether the player has changed their region.\n    */\n    static bool IsChangingRegion(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the cell of a player.\n    *\n    * This changes the cell recorded for that player in the server memory, but does not by itself\n    * send a packet.\n    *\n    * The cell is determined to be an exterior cell if it fits the pattern of a number followed\n    * by a comma followed by another number.\n    *\n    * \\param pid The player ID.\n    * \\param cellDescription The cell description.\n    * \\return void\n    */\n    static void SetCell(unsigned short pid, const char *cellDescription) noexcept;\n\n    /**\n    * \\brief Set the cell of a player to an exterior cell.\n    *\n    * This changes the cell recorded for that player in the server memory, but does not by itself\n    * send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param x The X coordinate of the cell.\n    * \\param y The Y coordinate of the cell.\n    * \\return void\n    */\n    static void SetExteriorCell(unsigned short pid, int x, int y) noexcept;\n\n    /**\n    * \\brief Send a PlayerCellChange packet about a player.\n    *\n    * It is only sent to the affected player.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendCell(unsigned short pid) noexcept;\n\n};\n\n#endif //OPENMW_CELLAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/CharClass.cpp",
    "content": "#include \"CharClass.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Networking.hpp>\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n\nconst char *CharClassFunctions::GetDefaultClass(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    return player->charClass.mId.c_str();\n}\n\nconst char *CharClassFunctions::GetClassName(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    return player->charClass.mName.c_str();\n}\n\nconst char *CharClassFunctions::GetClassDesc(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    return player->charClass.mDescription.c_str();\n}\n\nint CharClassFunctions::GetClassMajorAttribute(unsigned short pid, unsigned char slot)\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (slot > 1)\n        throw std::invalid_argument(\"Incorrect attribute slot id\");\n\n    return player->charClass.mData.mAttribute[slot];\n}\n\nint CharClassFunctions::GetClassSpecialization(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->charClass.mData.mSpecialization;\n}\n\nint CharClassFunctions::GetClassMajorSkill(unsigned short pid, unsigned char slot)\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (slot > 4)\n        throw std::invalid_argument(\"Incorrect skill slot id\");\n\n    return player->charClass.mData.mSkills[slot][1];\n}\n\nint CharClassFunctions::GetClassMinorSkill(unsigned short pid, unsigned char slot)\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (slot > 4)\n        throw std::invalid_argument(\"Incorrect skill slot id\");\n\n    return player->charClass.mData.mSkills[slot][0];\n}\n\nint CharClassFunctions::IsClassDefault(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return !player->charClass.mId.empty(); // true if default\n}\n\nvoid CharClassFunctions::SetDefaultClass(unsigned short pid, const char *id) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->charClass.mId = id;\n}\nvoid CharClassFunctions::SetClassName(unsigned short pid, const char *name) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->charClass.mName = name;\n    player->charClass.mId = \"\";\n}\nvoid CharClassFunctions::SetClassDesc(unsigned short pid, const char *desc) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->charClass.mDescription = desc;\n}\nvoid CharClassFunctions::SetClassMajorAttribute(unsigned short pid, unsigned char slot, int attrId)\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (slot > 1)\n        throw std::invalid_argument(\"Incorrect attribute slot id\");\n\n    player->charClass.mData.mAttribute[slot] = attrId;\n\n}\nvoid CharClassFunctions::SetClassSpecialization(unsigned short pid, int spec) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->charClass.mData.mSpecialization = spec;\n}\nvoid CharClassFunctions::SetClassMajorSkill(unsigned short pid, unsigned char slot, int skillId)\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (slot > 4)\n        throw std::invalid_argument(\"Incorrect skill slot id\");\n\n    player->charClass.mData.mSkills[slot][1] = skillId;\n}\nvoid CharClassFunctions::SetClassMinorSkill(unsigned short pid, unsigned char slot, int skillId)\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (slot > 4)\n        throw std::invalid_argument(\"Incorrect skill slot id\");\n\n    player->charClass.mData.mSkills[slot][0] = skillId;\n}\n\nvoid CharClassFunctions::SendClass(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARCLASS);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/CharClass.hpp",
    "content": "#ifndef OPENMW_CHARCLASSAPI_HPP\n#define OPENMW_CHARCLASSAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define CHARCLASSAPI \\\n    {\"GetDefaultClass\",        CharClassFunctions::GetDefaultClass},\\\n    {\"GetClassName\",           CharClassFunctions::GetClassName},\\\n    {\"GetClassDesc\",           CharClassFunctions::GetClassDesc},\\\n    {\"GetClassMajorAttribute\", CharClassFunctions::GetClassMajorAttribute},\\\n    {\"GetClassSpecialization\", CharClassFunctions::GetClassSpecialization},\\\n    {\"GetClassMajorSkill\",     CharClassFunctions::GetClassMajorSkill},\\\n    {\"GetClassMinorSkill\",     CharClassFunctions::GetClassMinorSkill},\\\n    {\"IsClassDefault\",         CharClassFunctions::IsClassDefault},\\\n    \\\n    {\"SetDefaultClass\",        CharClassFunctions::SetDefaultClass},\\\n    {\"SetClassName\",           CharClassFunctions::SetClassName},\\\n    {\"SetClassDesc\",           CharClassFunctions::SetClassDesc},\\\n    {\"SetClassMajorAttribute\", CharClassFunctions::SetClassMajorAttribute},\\\n    {\"SetClassSpecialization\", CharClassFunctions::SetClassSpecialization},\\\n    {\"SetClassMajorSkill\",     CharClassFunctions::SetClassMajorSkill},\\\n    {\"SetClassMinorSkill\",     CharClassFunctions::SetClassMinorSkill},\\\n    \\\n    {\"SendClass\",              CharClassFunctions::SendClass}\n\n\nclass CharClassFunctions\n{\npublic:\n\n    /**\n    * \\brief Get the default class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The ID of the default class.\n    */\n    static const char *GetDefaultClass(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the name of the custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The name of the custom class.\n    */\n    static const char *GetClassName(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the description of the custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The description of the custom class.\n    */\n    static const char *GetClassDesc(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the ID of one of the two major attributes of a custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the major attribute (0 or 1).\n    * \\return The ID of the major attribute.\n    */\n    static int GetClassMajorAttribute(unsigned short pid, unsigned char slot);\n\n    /**\n    * \\brief Get the specialization ID of the custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The specialization ID of the custom class (0 for Combat, 1 for Magic, 2 for Stealth).\n    */\n    static int GetClassSpecialization(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the ID of one of the five major skills of a custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the major skill (0 to 4).\n    * \\return The ID of the major skill.\n    */\n    static int GetClassMajorSkill(unsigned short pid, unsigned char slot);\n\n    /**\n    * \\brief Get the ID of one of the five minor skills of a custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the minor skill (0 to 4).\n    * \\return The ID of the minor skill.\n    */\n    static int GetClassMinorSkill(unsigned short pid, unsigned char slot);\n\n    /**\n    * \\brief Check whether the player is using a default class instead of a custom one.\n    *\n    * \\param pid The player ID.\n    * \\return Whether the player is using a default class.\n    */\n    static int IsClassDefault(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the default class used by a player.\n    *\n    * If this is left blank, the custom class data set for the player will be used instead.\n    *\n    * \\param pid The player ID.\n    * \\param id The ID of the default class.\n    * \\return void\n    */\n    static void SetDefaultClass(unsigned short pid, const char *id) noexcept;\n\n    /**\n    * \\brief Set the name of the custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param name The name of the custom class.\n    * \\return void\n    */\n    static void SetClassName(unsigned short pid, const char *name) noexcept;\n\n    /**\n    * \\brief Set the description of the custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param desc The description of the custom class.\n    * \\return void\n    */\n    static void SetClassDesc(unsigned short pid, const char *desc) noexcept;\n\n    /**\n    * \\brief Set the ID of one of the two major attributes of the custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the major attribute (0 or 1).\n    * \\param attrId The ID to use for the attribute.\n    * \\return void\n    */\n    static void SetClassMajorAttribute(unsigned short pid, unsigned char slot, int attrId);\n\n    /**\n    * \\brief Set the specialization of the custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param spec The specialization ID to use (0 for Combat, 1 for Magic, 2 for Stealth).\n    * \\return void\n    */\n    static void SetClassSpecialization(unsigned short pid, int spec) noexcept;\n\n     /**\n     * \\brief Set the ID of one of the five major skills of the custom class used by a player.\n     *\n     * \\param pid The player ID.\n     * \\param slot The slot of the major skill (0 to 4).\n     * \\param skillId The ID to use for the skill.\n     * \\return void\n     */\n    static void SetClassMajorSkill(unsigned short pid, unsigned char slot, int skillId);\n\n    /**\n    * \\brief Set the ID of one of the five minor skills of the custom class used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the minor skill (0 to 4).\n    * \\param skillId The ID to use for the skill.\n    * \\return void\n    */\n    static void SetClassMinorSkill(unsigned short pid, unsigned char slot, int skillId);\n\n    /**\n    * \\brief Send a PlayerCharClass packet about a player.\n    *\n    * It is only sent to the affected player.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendClass(unsigned short pid) noexcept;\n};\n\n#endif //OPENMW_CHARCLASSAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Chat.cpp",
    "content": "#include \"Chat.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\nvoid ChatFunctions::SendMessage(unsigned short pid, const char *message, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->chatMessage = message;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"System: %s\", message);\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ChatFunctions::CleanChatForPid(unsigned short pid)\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->chatMessage.clear();\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n}\n\nvoid ChatFunctions::CleanChat()\n{\n    for (auto player : *Players::getPlayers())\n    {\n        player.second->chatMessage.clear();\n\n        mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);\n        packet->setPlayer(player.second);\n\n        packet->Send(false);\n    }\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Chat.hpp",
    "content": "#ifndef OPENMW_CHATAPI_HPP\n#define OPENMW_CHATAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define CHATAPI \\\n    {\"SendMessage\",       ChatFunctions::SendMessage},\\\n    {\"CleanChatForPid\",   ChatFunctions::CleanChatForPid},\\\n    {\"CleanChat\",         ChatFunctions::CleanChat}\n\nclass ChatFunctions\n{\npublic:\n\n    /**\n    * \\brief Send a message to a certain player.\n    *\n    * \\param pid The player ID.\n    * \\param message The contents of the message.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendMessage(unsigned short pid, const char *message, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Remove all messages from chat for a certain player.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void CleanChatForPid(unsigned short pid);\n\n    /**\n    * \\brief Remove all messages from chat for everyone on the server.\n    *\n    * \\return void\n    */\n    static void CleanChat();\n};\n\n#endif //OPENMW_CHATAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Dialogue.cpp",
    "content": "#include \"Dialogue.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\nusing namespace mwmp;\n\nvoid DialogueFunctions::ClearTopicChanges(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->topicChanges.clear();\n}\n\nunsigned int DialogueFunctions::GetTopicChangesSize(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->topicChanges.size();\n}\n\nvoid DialogueFunctions::AddTopic(unsigned short pid, const char* topicId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::Topic topic;\n    topic.topicId = topicId;\n\n    player->topicChanges.push_back(topic);\n}\n\nconst char *DialogueFunctions::GetTopicId(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->topicChanges.size())\n        return \"invalid\";\n\n    return player->topicChanges.at(index).topicId.c_str();\n}\n\nvoid DialogueFunctions::SendTopicChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_TOPIC);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid DialogueFunctions::PlayAnimation(unsigned short pid, const char* groupname, int mode, int count, bool persist) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->animation.groupname = groupname;\n    player->animation.mode = mode;\n    player->animation.count = count;\n    player->animation.persist = persist;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ANIM_PLAY);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n    player->sendToLoaded(packet);\n}\n\nvoid DialogueFunctions::PlaySpeech(unsigned short pid, const char* sound) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->sound = sound;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SPEECH);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n    player->sendToLoaded(packet);\n}\n\n// All methods below are deprecated versions of methods from above\n\nvoid DialogueFunctions::InitializeTopicChanges(unsigned short pid) noexcept\n{\n    ClearTopicChanges(pid);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Dialogue.hpp",
    "content": "#ifndef OPENMW_DIALOGUEAPI_HPP\n#define OPENMW_DIALOGUEAPI_HPP\n\n#define DIALOGUEAPI \\\n    {\"ClearTopicChanges\",       DialogueFunctions::ClearTopicChanges},\\\n    \\\n    {\"GetTopicChangesSize\",     DialogueFunctions::GetTopicChangesSize},\\\n    \\\n    {\"AddTopic\",                DialogueFunctions::AddTopic},\\\n    \\\n    {\"GetTopicId\",              DialogueFunctions::GetTopicId},\\\n    \\\n    {\"SendTopicChanges\",        DialogueFunctions::SendTopicChanges},\\\n    \\\n    {\"PlayAnimation\",           DialogueFunctions::PlayAnimation},\\\n    {\"PlaySpeech\",              DialogueFunctions::PlaySpeech},\\\n    \\\n    {\"InitializeTopicChanges\",  DialogueFunctions::InitializeTopicChanges}\n\nclass DialogueFunctions\n{\npublic:\n\n    /**\n    * \\brief Clear the last recorded topic changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerTopic packets.\n    *\n    * \\param pid The player ID whose topic changes should be used.\n    * \\return void\n    */\n    static void ClearTopicChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest topic changes.\n    *\n    * \\param pid The player ID whose topic changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetTopicChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Add a new topic to the topic changes for a player.\n    *\n    * \\param pid The player ID whose topic changes should be used.\n    * \\param topicId The topicId of the topic.\n    * \\return void\n    */\n    static void AddTopic(unsigned short pid, const char* topicId) noexcept;\n\n    /**\n    * \\brief Get the topicId at a certain index in a player's latest topic changes.\n    *\n    * \\param pid The player ID whose topic changes should be used.\n    * \\param index The index of the topic.\n    * \\return The topicId.\n    */\n    static const char *GetTopicId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Send a PlayerTopic packet with a player's recorded topic changes.\n    *\n    * \\param pid The player ID whose topic changes should be used.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendTopicChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Play a certain animation on a player's character by sending a PlayerAnimation\n    *        packet.\n    *\n    * \\param pid The player ID of the character playing the animation.\n    * \\param groupname The groupname of the animation.\n    * \\param mode The mode of the animation.\n    * \\param count The number of times the animation should be played.\n    * \\param persist Whether the animation should persist or not.\n    * \\return void\n    */\n    static void PlayAnimation(unsigned short pid, const char* groupname, int mode, int count, bool persist) noexcept;\n\n    /**\n    * \\brief Play a certain sound for a player as spoken by their character by sending\n    *        a PlayerSpeech packet.\n    *\n    * \\param pid The player ID of the character playing the sound.\n    * \\param sound The path of the sound file.\n    * \\return void\n    */\n    static void PlaySpeech(unsigned short pid, const char* sound) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static void InitializeTopicChanges(unsigned short pid) noexcept;\n\n};\n\n#endif //OPENMW_DIALOGUEAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Factions.cpp",
    "content": "#include \"Factions.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\nusing namespace mwmp;\n\nFaction tempFaction;\nconst Faction emptyFaction = {};\n\nvoid FactionFunctions::ClearFactionChanges(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->factionChanges.factions.clear();\n}\n\nunsigned int FactionFunctions::GetFactionChangesSize(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->factionChanges.factions.size();\n}\n\nunsigned char FactionFunctions::GetFactionChangesAction(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->factionChanges.action;\n}\n\nconst char *FactionFunctions::GetFactionId(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->factionChanges.factions.size())\n        return \"invalid\";\n\n    return player->factionChanges.factions.at(index).factionId.c_str();\n}\n\nint FactionFunctions::GetFactionRank(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->factionChanges.factions.at(index).rank;\n}\n\nbool FactionFunctions::GetFactionExpulsionState(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, false);\n\n    return player->factionChanges.factions.at(index).isExpelled;\n}\n\nint FactionFunctions::GetFactionReputation(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->factionChanges.factions.at(index).reputation;\n}\n\nvoid FactionFunctions::SetFactionChangesAction(unsigned short pid, unsigned char action) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->factionChanges.action = action;\n}\n\nvoid FactionFunctions::SetFactionId(const char* factionId) noexcept\n{\n    tempFaction.factionId = factionId;\n}\n\nvoid FactionFunctions::SetFactionRank(unsigned int rank) noexcept\n{\n    tempFaction.rank = rank;\n}\n\nvoid FactionFunctions::SetFactionExpulsionState(bool expulsionState) noexcept\n{\n    tempFaction.isExpelled = expulsionState;\n}\n\nvoid FactionFunctions::SetFactionReputation(int reputation) noexcept\n{\n    tempFaction.reputation = reputation;\n}\n\nvoid FactionFunctions::AddFaction(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->factionChanges.factions.push_back(tempFaction);\n\n    tempFaction = emptyFaction;\n}\n\nvoid FactionFunctions::SendFactionChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_FACTION);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\n// All methods below are deprecated versions of methods from above\n\nvoid FactionFunctions::InitializeFactionChanges(unsigned short pid) noexcept\n{\n    ClearFactionChanges(pid);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Factions.hpp",
    "content": "#ifndef OPENMW_FACTIONAPI_HPP\n#define OPENMW_FACTIONAPI_HPP\n\n#define FACTIONAPI \\\n    {\"ClearFactionChanges\",      FactionFunctions::ClearFactionChanges},\\\n    \\\n    {\"GetFactionChangesSize\",    FactionFunctions::GetFactionChangesSize},\\\n    {\"GetFactionChangesAction\",  FactionFunctions::GetFactionChangesAction},\\\n    \\\n    {\"GetFactionId\",             FactionFunctions::GetFactionId},\\\n    {\"GetFactionRank\",           FactionFunctions::GetFactionRank},\\\n    {\"GetFactionExpulsionState\", FactionFunctions::GetFactionExpulsionState},\\\n    {\"GetFactionReputation\",     FactionFunctions::GetFactionReputation},\\\n    \\\n    {\"SetFactionChangesAction\",  FactionFunctions::SetFactionChangesAction},\\\n    {\"SetFactionId\",             FactionFunctions::SetFactionId},\\\n    {\"SetFactionRank\",           FactionFunctions::SetFactionRank},\\\n    {\"SetFactionExpulsionState\", FactionFunctions::SetFactionExpulsionState},\\\n    {\"SetFactionReputation\",     FactionFunctions::SetFactionReputation},\\\n    \\\n    {\"AddFaction\",               FactionFunctions::AddFaction},\\\n    \\\n    {\"SendFactionChanges\",       FactionFunctions::SendFactionChanges},\\\n    \\\n    {\"InitializeFactionChanges\", FactionFunctions::InitializeFactionChanges}\n\nclass FactionFunctions\n{\npublic:\n\n    /**\n    * \\brief Clear the last recorded faction changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerFaction packets.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\return void\n    */\n    static void ClearFactionChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest faction changes.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetFactionChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the action type used in a player's latest faction changes.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\return The action type (0 for RANK, 1 for EXPULSION, 2 for REPUTATION).\n    */\n    static unsigned char GetFactionChangesAction(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the factionId at a certain index in a player's latest faction changes.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\param index The index of the faction.\n    * \\return The factionId.\n    */\n    static const char *GetFactionId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the rank at a certain index in a player's latest faction changes.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\param index The index of the faction.\n    * \\return The rank.\n    */\n    static int GetFactionRank(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the expulsion state at a certain index in a player's latest faction changes.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\param index The index of the faction.\n    * \\return The expulsion state.\n    */\n    static bool GetFactionExpulsionState(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the reputation at a certain index in a player's latest faction changes.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\param index The index of the faction.\n    * \\return The reputation.\n    */\n    static int GetFactionReputation(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Set the action type in a player's faction changes.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\param action The action (0 for RANK, 1 for EXPULSION, 2 for REPUTATION).\n    * \\return void\n    */\n    static void SetFactionChangesAction(unsigned short pid, unsigned char action) noexcept;\n\n    /**\n    * \\brief Set the factionId of the temporary faction stored on the server.\n    *\n    * \\param factionId The factionId.\n    * \\return void\n    */\n    static void SetFactionId(const char* factionId) noexcept;\n\n    /**\n    * \\brief Set the rank of the temporary faction stored on the server.\n    *\n    * \\param rank The rank.\n    * \\return void\n    */\n    static void SetFactionRank(unsigned int rank) noexcept;\n\n    /**\n    * \\brief Set the expulsion state of the temporary faction stored on the server.\n    *\n    * \\param expulsionState The expulsion state.\n    * \\return void\n    */\n    static void SetFactionExpulsionState(bool expulsionState) noexcept;\n\n    /**\n    * \\brief Set the reputation of the temporary faction stored on the server.\n    *\n    * \\param reputation The reputation.\n    * \\return void\n    */\n    static void SetFactionReputation(int reputation) noexcept;\n\n    /**\n    * \\brief Add the server's temporary faction to the faction changes for a player.\n    *\n    * In the process, the server's temporary faction will automatically be cleared so a new one\n    * can be set up.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\return void\n    */\n    static void AddFaction(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerFaction packet with a player's recorded faction changes.\n    *\n    * \\param pid The player ID whose faction changes should be used.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendFactionChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static void InitializeFactionChanges(unsigned short pid) noexcept;\n\n};\n\n#endif //OPENMW_FACTIONAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/GUI.cpp",
    "content": "#include \"GUI.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\nvoid GUIFunctions::_MessageBox(unsigned short pid, int id, const char *label) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->guiMessageBox.id = id;\n    player->guiMessageBox.label = label;\n    player->guiMessageBox.type = Player::GUIMessageBox::MessageBox;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n}\n\nvoid GUIFunctions::CustomMessageBox(unsigned short pid, int id, const char *label, const char *buttons) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->guiMessageBox.id = id;\n    player->guiMessageBox.label = label;\n    player->guiMessageBox.buttons = buttons;\n    player->guiMessageBox.type = Player::GUIMessageBox::CustomMessageBox;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n}\n\nvoid GUIFunctions::InputDialog(unsigned short pid, int id, const char *label, const char *note) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->guiMessageBox.id = id;\n    player->guiMessageBox.label = label;\n    player->guiMessageBox.note = note;\n    player->guiMessageBox.type = Player::GUIMessageBox::InputDialog;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n}\n\nvoid GUIFunctions::PasswordDialog(unsigned short pid, int id, const char *label, const char *note) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->guiMessageBox.id = id;\n    player->guiMessageBox.label = label;\n    player->guiMessageBox.note = note;\n    player->guiMessageBox.type = Player::GUIMessageBox::PasswordDialog;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n}\n\nvoid GUIFunctions::ListBox(unsigned short pid, int id, const char *label, const char *items)\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->guiMessageBox.id = id;\n    player->guiMessageBox.label = label;\n    player->guiMessageBox.data = items;\n    player->guiMessageBox.type = Player::GUIMessageBox::ListBox;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n}\n\nvoid GUIFunctions::ClearQuickKeyChanges(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->quickKeyChanges.clear();\n}\n\nunsigned int GUIFunctions::GetQuickKeyChangesSize(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->quickKeyChanges.size();\n}\n\nint GUIFunctions::GetQuickKeySlot(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (index >= player->quickKeyChanges.size())\n        return 0;\n\n    return player->quickKeyChanges.at(index).slot;\n}\n\nint GUIFunctions::GetQuickKeyType(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (index >= player->quickKeyChanges.size())\n        return 0;\n\n    return player->quickKeyChanges.at(index).type;\n}\n\nconst char *GUIFunctions::GetQuickKeyItemId(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->quickKeyChanges.size())\n        return \"invalid\";\n\n    return player->quickKeyChanges.at(index).itemId.c_str();\n}\n\nvoid GUIFunctions::AddQuickKey(unsigned short pid, unsigned short slot, int type, const char* itemId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::QuickKey quickKey;\n    quickKey.slot = slot;\n    quickKey.type = type;\n    quickKey.itemId = itemId;\n\n    player->quickKeyChanges.push_back(quickKey);\n}\n\nvoid GUIFunctions::SendQuickKeyChanges(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_QUICKKEYS)->setPlayer(player);\n    mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_QUICKKEYS)->Send(false);\n}\n\nvoid GUIFunctions::SetMapVisibility(unsigned short targetPid, unsigned short affectedPid, unsigned short state) noexcept\n{\n    LOG_MESSAGE(TimedLog::LOG_WARN, \"stub\");\n}\n\nvoid GUIFunctions::SetMapVisibilityAll(unsigned short targetPid, unsigned short state) noexcept\n{\n    LOG_MESSAGE(TimedLog::LOG_WARN, \"stub\");\n}\n\n// All methods below are deprecated versions of methods from above\n\nvoid GUIFunctions::InitializeQuickKeyChanges(unsigned short pid) noexcept\n{\n    ClearQuickKeyChanges(pid);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/GUI.hpp",
    "content": "#ifndef OPENMW_GUIAPI_HPP\n#define OPENMW_GUIAPI_HPP\n\n#define GUIAPI \\\n    {\"MessageBox\",                 GUIFunctions::_MessageBox},\\\n    {\"CustomMessageBox\",           GUIFunctions::CustomMessageBox},\\\n    {\"InputDialog\",                GUIFunctions::InputDialog},\\\n    {\"PasswordDialog\",             GUIFunctions::PasswordDialog},\\\n    {\"ListBox\",                    GUIFunctions::ListBox},\\\n    \\\n    {\"ClearQuickKeyChanges\",       GUIFunctions::ClearQuickKeyChanges},\\\n    \\\n    {\"GetQuickKeyChangesSize\",     GUIFunctions::GetQuickKeyChangesSize},\\\n    \\\n    {\"GetQuickKeySlot\",            GUIFunctions::GetQuickKeySlot},\\\n    {\"GetQuickKeyType\",            GUIFunctions::GetQuickKeyType},\\\n    {\"GetQuickKeyItemId\",          GUIFunctions::GetQuickKeyItemId},\\\n    \\\n    {\"AddQuickKey\",                GUIFunctions::AddQuickKey},\\\n    \\\n    {\"SendQuickKeyChanges\",        GUIFunctions::SendQuickKeyChanges},\\\n    \\\n    {\"SetMapVisibility\",           GUIFunctions::SetMapVisibility},\\\n    {\"SetMapVisibilityAll\",        GUIFunctions::SetMapVisibilityAll},\\\n    \\\n    {\"InitializeQuickKeyChanges\",  GUIFunctions::InitializeQuickKeyChanges}\n\nclass GUIFunctions\n{\npublic:\n\n    /**\n    * \\brief Display a simple messagebox at the bottom of the screen that vanishes\n    *        after a few seconds.\n    *\n    * Note for C++ programmers: do not rename into MessageBox so as to not conflict\n    * with WINAPI's MessageBox.\n    *\n    * \\param pid The player ID for whom the messagebox should appear.\n    * \\param id The numerical ID of the messagebox.\n    * \\param label The text in the messagebox.\n    * \\return void\n    */\n    static void _MessageBox(unsigned short pid, int id, const char *label) noexcept;\n\n    /**\n    * \\brief Display an interactive messagebox at the center of the screen that\n    *        vanishes only when one of its buttons is clicked.\n    *\n    * \\param pid The player ID for whom the messagebox should appear.\n    * \\param id The numerical ID of the messagebox.\n    * \\param label The text in the messagebox.\n    * \\parm buttons The captions of the buttons, separated by semicolons (e.g. \"Yes;No;Maybe\").\n    * \\return void\n    */\n    static void CustomMessageBox(unsigned short pid, int id, const char *label, const char *buttons) noexcept;\n\n    /**\n    * \\brief Display an input dialog at the center of the screen.\n    *\n    * \\param pid The player ID for whom the input dialog should appear.\n    * \\param id The numerical ID of the input dialog.\n    * \\param label The text at the top of the input dialog.\n    * \\parm note The text at the bottom of the input dialog.\n    * \\return void\n    */\n    static void InputDialog(unsigned short pid, int id, const char *label, const char *note) noexcept;\n\n    /**\n    * \\brief Display a password dialog at the center of the screen.\n    *\n    * Although similar to an input dialog, the password dialog replaces all\n    * input characters with asterisks.\n    *\n    * \\param pid The player ID for whom the password dialog should appear.\n    * \\param id The numerical ID of the password dialog.\n    * \\param label The text at the top of the password dialog.\n    * \\parm note The text at the bottom of the password dialog.\n    * \\return void\n    */\n    static void PasswordDialog(unsigned short pid, int id, const char *label, const char *note) noexcept;\n\n    /**\n    * \\brief Display a listbox at the center of the screen where each item takes up\n    *        a row and is selectable, with the listbox only vanishing once the Ok button\n    *        is pressed.\n    *\n    * \\param pid The player ID for whom the listbox should appear.\n    * \\param id The numerical ID of the listbox.\n    * \\param label The text at the top of the listbox.\n    * \\parm items The items in the listbox, separated by newlines (e.g. \"Item 1\\nItem 2\").\n    * \\return void\n    */\n    static void ListBox(unsigned short pid, int id, const char *label, const char *items);\n\n    /**\n    * \\brief Clear the last recorded quick key changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerQuickKeys packets.\n    *\n    * \\param pid The player ID whose quick key changes should be used.\n    * \\return void\n    */\n    static void ClearQuickKeyChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest quick key changes.\n    *\n    * \\param pid The player ID whose quick key changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetQuickKeyChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Add a new quick key to the quick key changes for a player.\n    *\n    * \\param pid The player ID whose quick key changes should be used.\n    * \\param slot The slot to be used.\n    * \\param type The type of the quick key (0 for ITEM, 1 for ITEM_MAGIC, 2 for MAGIC, 3 for UNASSIGNED).\n    * \\param itemId The itemId of the item.\n    * \\return void\n    */\n    static void AddQuickKey(unsigned short pid, unsigned short slot, int type, const char* itemId = \"\") noexcept;\n\n    /**\n    * \\brief Get the slot of the quick key at a certain index in a player's latest quick key changes.\n    *\n    * \\param pid The player ID whose quick key changes should be used.\n    * \\param index The index of the quick key in the quick key changes vector.\n    * \\return The slot.\n    */\n    static int GetQuickKeySlot(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the type of the quick key at a certain index in a player's latest quick key changes.\n    *\n    * \\param pid The player ID whose quick key changes should be used.\n    * \\param index The index of the quick key in the quick key changes vector.\n    * \\return The quick key type.\n    */\n    static int GetQuickKeyType(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the itemId at a certain index in a player's latest quick key changes.\n    *\n    * \\param pid The player ID whose quick key changes should be used.\n    * \\param index The index of the quick key in the quick key changes vector.\n    * \\return The itemId.\n    */\n    static const char *GetQuickKeyItemId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Send a PlayerQuickKeys packet with a player's recorded quick key changes.\n    *\n    * \\param pid The player ID whose quick key changes should be used.\n    * \\return void\n    */\n    static void SendQuickKeyChanges(unsigned short pid) noexcept;\n\n    //state 0 - disallow, 1 - allow\n\n    /**\n    * \\brief Determine whether a player can see the map marker of another player.\n    *\n    * Note: This currently has no effect, and is just an unimplemented stub.\n    *\n    * \\param targetPid The player ID whose map marker should be hidden or revealed.\n    * \\param affectedPid The player ID for whom the map marker will be hidden or revealed.\n    * \\param state The state of the map marker (false to hide, true to reveal).\n    * \\return void\n    */\n    static void SetMapVisibility(unsigned short targetPid, unsigned short affectedPid, unsigned short state) noexcept;\n\n    /**\n    * \\brief Determine whether a player's map marker can be seen by all other players.\n    *\n    * Note: This currently has no effect, and is just an unimplemented stub.\n    *\n    * \\param targetPid The player ID whose map marker should be hidden or revealed.\n    * \\param state The state of the map marker (false to hide, true to reveal).\n    * \\return void\n    */\n    static void SetMapVisibilityAll(unsigned short targetPid, unsigned short state) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static void InitializeQuickKeyChanges(unsigned short pid) noexcept;\n\n};\n\n#endif //OPENMW_GUIAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Items.cpp",
    "content": "#include \"Items.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n#include <apps/openmw/mwworld/inventorystore.hpp>\n\nusing namespace mwmp;\n\nvoid ItemFunctions::ClearInventoryChanges(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->inventoryChanges.items.clear();\n}\n\nint ItemFunctions::GetEquipmentSize() noexcept\n{\n    return MWWorld::InventoryStore::Slots;\n}\n\nunsigned int ItemFunctions::GetEquipmentChangesSize(unsigned short pid) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->equipmentIndexChanges.size();\n}\n\nunsigned int ItemFunctions::GetInventoryChangesSize(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->inventoryChanges.items.size();\n}\n\nunsigned int ItemFunctions::GetInventoryChangesAction(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->inventoryChanges.action;\n}\n\nvoid ItemFunctions::SetInventoryChangesAction(unsigned short pid, unsigned char action) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->inventoryChanges.action = action;\n}\n\nvoid ItemFunctions::EquipItem(unsigned short pid, unsigned short slot, const char *refId, unsigned int count,\n    int charge, double enchantmentCharge) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->equipmentItems[slot].refId = refId;\n    player->equipmentItems[slot].count = count;\n    player->equipmentItems[slot].charge = charge;\n    player->equipmentItems[slot].enchantmentCharge = enchantmentCharge;\n\n    if (!Utils::vectorContains(player->equipmentIndexChanges, slot))\n        player->equipmentIndexChanges.push_back(slot);\n}\n\nvoid ItemFunctions::UnequipItem(unsigned short pid, unsigned short slot) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    ItemFunctions::EquipItem(pid, slot, \"\", 0, -1, -1);\n}\n\nvoid ItemFunctions::AddItemChange(unsigned short pid, const char* refId, unsigned int count, int charge,\n    double enchantmentCharge, const char* soul) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    Item item;\n    item.refId = refId;\n    item.count = count;\n    item.charge = charge;\n    item.enchantmentCharge = enchantmentCharge;\n    item.soul = soul;\n\n    player->inventoryChanges.items.push_back(item);\n}\n\nbool ItemFunctions::HasItemEquipped(unsigned short pid, const char* refId)\n{\n    Player *player;\n    GET_PLAYER(pid, player, false);\n\n    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)\n        if (Misc::StringUtils::ciEqual(player->equipmentItems[slot].refId, refId))\n            return true;\n    return false;\n}\n\nint ItemFunctions::GetEquipmentChangesSlot(unsigned short pid, unsigned int changeIndex) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n\n    return player->equipmentIndexChanges[changeIndex];\n}\n\nconst char *ItemFunctions::GetEquipmentItemRefId(unsigned short pid, unsigned short slot) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->equipmentItems[slot].refId.c_str();\n}\n\nint ItemFunctions::GetEquipmentItemCount(unsigned short pid, unsigned short slot) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->equipmentItems[slot].count;\n}\n\nint ItemFunctions::GetEquipmentItemCharge(unsigned short pid, unsigned short slot) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->equipmentItems[slot].charge;\n}\n\ndouble ItemFunctions::GetEquipmentItemEnchantmentCharge(unsigned short pid, unsigned short slot) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->equipmentItems[slot].enchantmentCharge;\n}\n\nconst char *ItemFunctions::GetInventoryItemRefId(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->inventoryChanges.items.size())\n        return \"invalid\";\n\n    return player->inventoryChanges.items.at(index).refId.c_str();\n}\n\nint ItemFunctions::GetInventoryItemCount(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->inventoryChanges.items.at(index).count;\n}\n\nint ItemFunctions::GetInventoryItemCharge(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->inventoryChanges.items.at(index).charge;\n}\n\ndouble ItemFunctions::GetInventoryItemEnchantmentCharge(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->inventoryChanges.items.at(index).enchantmentCharge;\n}\n\nconst char *ItemFunctions::GetInventoryItemSoul(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->inventoryChanges.items.size())\n        return \"invalid\";\n\n    return player->inventoryChanges.items.at(index).soul.c_str();\n}\n\nconst char *ItemFunctions::GetUsedItemRefId(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    return player->usedItem.refId.c_str();\n}\n\nint ItemFunctions::GetUsedItemCount(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->usedItem.count;\n}\n\nint ItemFunctions::GetUsedItemCharge(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->usedItem.charge;\n}\n\ndouble ItemFunctions::GetUsedItemEnchantmentCharge(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->usedItem.enchantmentCharge;\n}\n\nconst char *ItemFunctions::GetUsedItemSoul(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    return player->usedItem.soul.c_str();\n}\n\nvoid ItemFunctions::SendEquipment(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n    packet->Send(true);\n\n    player->equipmentIndexChanges.clear();\n}\n\nvoid ItemFunctions::SendInventoryChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_INVENTORY);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ItemFunctions::SendItemUse(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ITEM_USE);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n}\n\n// All methods below are deprecated versions of methods from above\n\nvoid ItemFunctions::InitializeInventoryChanges(unsigned short pid) noexcept\n{\n    ClearInventoryChanges(pid);\n}\n\nvoid ItemFunctions::AddItem(unsigned short pid, const char* refId, unsigned int count, int charge,\n    double enchantmentCharge, const char* soul) noexcept\n{\n    AddItemChange(pid, refId, count, charge, enchantmentCharge, soul);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Items.hpp",
    "content": "#ifndef OPENMW_ITEMAPI_HPP\n#define OPENMW_ITEMAPI_HPP\n\n#define ITEMAPI \\\n    {\"ClearInventoryChanges\",                 ItemFunctions::ClearInventoryChanges},\\\n    \\\n    {\"GetEquipmentSize\",                      ItemFunctions::GetEquipmentSize},\\\n    {\"GetEquipmentChangesSize\",               ItemFunctions::GetEquipmentChangesSize},\\\n    {\"GetInventoryChangesSize\",               ItemFunctions::GetInventoryChangesSize},\\\n    {\"GetInventoryChangesAction\",             ItemFunctions::GetInventoryChangesAction},\\\n    \\\n    {\"SetInventoryChangesAction\",             ItemFunctions::SetInventoryChangesAction},\\\n    \\\n    {\"EquipItem\",                             ItemFunctions::EquipItem},\\\n    {\"UnequipItem\",                           ItemFunctions::UnequipItem},\\\n    \\\n    {\"AddItemChange\",                         ItemFunctions::AddItemChange},\\\n    \\\n    {\"HasItemEquipped\",                       ItemFunctions::HasItemEquipped},\\\n    \\\n    {\"GetEquipmentChangesSlot\",                ItemFunctions::GetEquipmentChangesSlot},\\\n    {\"GetEquipmentItemRefId\",                 ItemFunctions::GetEquipmentItemRefId},\\\n    {\"GetEquipmentItemCount\",                 ItemFunctions::GetEquipmentItemCount},\\\n    {\"GetEquipmentItemCharge\",                ItemFunctions::GetEquipmentItemCharge},\\\n    {\"GetEquipmentItemEnchantmentCharge\",     ItemFunctions::GetEquipmentItemEnchantmentCharge},\\\n    \\\n    {\"GetInventoryItemRefId\",                 ItemFunctions::GetInventoryItemRefId},\\\n    {\"GetInventoryItemCount\",                 ItemFunctions::GetInventoryItemCount},\\\n    {\"GetInventoryItemCharge\",                ItemFunctions::GetInventoryItemCharge},\\\n    {\"GetInventoryItemEnchantmentCharge\",     ItemFunctions::GetInventoryItemEnchantmentCharge},\\\n    {\"GetInventoryItemSoul\",                  ItemFunctions::GetInventoryItemSoul},\\\n    \\\n    {\"GetUsedItemRefId\",                      ItemFunctions::GetUsedItemRefId},\\\n    {\"GetUsedItemCount\",                      ItemFunctions::GetUsedItemCount},\\\n    {\"GetUsedItemCharge\",                     ItemFunctions::GetUsedItemCharge},\\\n    {\"GetUsedItemEnchantmentCharge\",          ItemFunctions::GetUsedItemEnchantmentCharge},\\\n    {\"GetUsedItemSoul\",                       ItemFunctions::GetUsedItemSoul},\\\n    \\\n    {\"SendEquipment\",                         ItemFunctions::SendEquipment},\\\n    {\"SendInventoryChanges\",                  ItemFunctions::SendInventoryChanges},\\\n    {\"SendItemUse\",                           ItemFunctions::SendItemUse},\\\n    \\\n    {\"InitializeInventoryChanges\",            ItemFunctions::InitializeInventoryChanges},\\\n    {\"AddItem\",                               ItemFunctions::AddItem}\n\nclass ItemFunctions\n{\npublic:\n\n    /**\n    * \\brief Clear the last recorded inventory changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerInventory packets.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\return void\n    */\n    static void ClearInventoryChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of slots used for equipment.\n    *\n    * The number is 19 before any dehardcoding is done in OpenMW.\n    *\n    * \\return The number of slots.\n    */\n    static int GetEquipmentSize() noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest equipment changes.\n    *\n    * \\param pid The player ID whose equipment changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetEquipmentChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest inventory changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetInventoryChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the action type used in a player's latest inventory changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\return The action type (0 for SET, 1 for ADD, 2 for REMOVE).\n    */\n    static unsigned int GetInventoryChangesAction(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the action type in a player's inventory changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\param action The action (0 for SET, 1 for ADD, 2 for REMOVE).\n    * \\return void\n    */\n    static void SetInventoryChangesAction(unsigned short pid, unsigned char action) noexcept;\n\n    /**\n    * \\brief Equip an item in a certain slot of the equipment of a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The equipment slot.\n    * \\param refId The refId of the item.\n    * \\param count The count of the item.\n    * \\param charge The charge of the item.\n    * \\param enchantmentCharge The enchantment charge of the item.\n    * \\return void\n    */\n    static void EquipItem(unsigned short pid, unsigned short slot, const char* refId, unsigned int count, int charge,\n        double enchantmentCharge = -1) noexcept;\n    \n    /**\n    * \\brief Unequip the item in a certain slot of the equipment of a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The equipment slot.\n    * \\return void\n    */\n    static void UnequipItem(unsigned short pid, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Add an item change to a player's inventory changes.\n    *\n    * \\param pid The player ID.\n    * \\param refId The refId of the item.\n    * \\param count The count of the item.\n    * \\param charge The charge of the item.\n    * \\param enchantmentCharge The enchantment charge of the item.\n    * \\param soul The soul of the item.\n    * \\return void\n    */\n    static void AddItemChange(unsigned short pid, const char* refId, unsigned int count, int charge,\n        double enchantmentCharge, const char* soul) noexcept;\n\n    /**\n    * \\brief Check whether a player has equipped an item with a certain refId in any slot.\n    *\n    * \\param pid The player ID.\n    * \\param refId The refId of the item.\n    * \\return Whether the player has the item equipped.\n    */\n    static bool HasItemEquipped(unsigned short pid, const char* refId);\n\n    /**\n    * \\brief Get the slot used for the equipment item at a specific index in the most recent\n    * equipment changes.\n    *\n    * \\param pid The player ID.\n    * \\param changeIndex The index of the equipment change.\n    * \\return The slot.\n    */\n    static int GetEquipmentChangesSlot(unsigned short pid, unsigned int changeIndex) noexcept;\n\n    /**\n    * \\brief Get the refId of the item in a certain slot of the equipment of a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the equipment item.\n    * \\return The refId.\n    */\n    static const char *GetEquipmentItemRefId(unsigned short pid, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Get the count of the item in a certain slot of the equipment of a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the equipment item.\n    * \\return The item count.\n    */\n    static int GetEquipmentItemCount(unsigned short pid, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Get the charge of the item in a certain slot of the equipment of a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the equipment item.\n    * \\return The charge.\n    */\n    static int GetEquipmentItemCharge(unsigned short pid, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Get the enchantment charge of the item in a certain slot of the equipment of\n    *        a player.\n    *\n    * \\param pid The player ID.\n    * \\param slot The slot of the equipment item.\n    * \\return The enchantment charge.\n    */\n    static double GetEquipmentItemEnchantmentCharge(unsigned short pid, unsigned short slot) noexcept;\n\n    /**\n    * \\brief Get the refId of the item at a certain index in a player's latest inventory\n    *        changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\param index The index of the inventory item.\n    * \\return The refId.\n    */\n    static const char *GetInventoryItemRefId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the count of the item at a certain index in a player's latest inventory\n    *        changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\param index The index of the inventory item.\n    * \\return The item count.\n    */\n    static int GetInventoryItemCount(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the charge of the item at a certain index in a player's latest inventory\n    *        changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\param index The index of the inventory item.\n    * \\return The charge.\n    */\n    static int GetInventoryItemCharge(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the enchantment charge of the item at a certain index in a player's\n    *        latest inventory changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\param index The index of the inventory item.\n    * \\return The enchantment charge.\n    */\n    static double GetInventoryItemEnchantmentCharge(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the soul of the item at a certain index in a player's latest inventory\n    *        changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\param index The index of the inventory item.\n    * \\return The soul.\n    */\n    static const char *GetInventoryItemSoul(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refId of the item last used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The refId.\n    */\n    static const char *GetUsedItemRefId(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the count of the item last used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The item count.\n    */\n    static int GetUsedItemCount(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the charge of the item last used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The charge.\n    */\n    static int GetUsedItemCharge(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the enchantment charge of the item last used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The enchantment charge.\n    */\n    static double GetUsedItemEnchantmentCharge(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the soul of the item last used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The soul.\n    */\n    static const char *GetUsedItemSoul(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerEquipment packet with a player's equipment.\n    *\n    * It is always sent to all players.\n    *\n    * \\param pid The player ID whose equipment should be sent.\n    * \\return void\n    */\n    static void SendEquipment(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerInventory packet with a player's recorded inventory changes.\n    *\n    * \\param pid The player ID whose inventory changes should be used.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendInventoryChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a PlayerItemUse causing a player to use their recorded usedItem.\n    *\n    * \\param pid The player ID affected.\n    * \\return void\n    */\n    static void SendItemUse(unsigned short pid) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static void InitializeInventoryChanges(unsigned short pid) noexcept;\n\n    static void AddItem(unsigned short pid, const char* refId, unsigned int count, int charge,\n        double enchantmentCharge, const char* soul) noexcept;\n\nprivate:\n\n};\n\n#endif //OPENMW_ITEMAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Mechanics.cpp",
    "content": "#include \"Mechanics.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\n#include <iostream>\n\nstatic std::string tempCellDescription;\n\nvoid MechanicsFunctions::ClearAlliedPlayersForPlayer(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->alliedPlayers.clear();\n}\n\nunsigned char MechanicsFunctions::GetMiscellaneousChangeType(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->miscellaneousChangeType;\n}\n\nconst char *MechanicsFunctions::GetMarkCell(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    tempCellDescription = player->cell.getShortDescription().c_str();\n    return tempCellDescription.c_str();\n}\n\ndouble MechanicsFunctions::GetMarkPosX(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->markPosition.pos[0];\n}\n\ndouble MechanicsFunctions::GetMarkPosY(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->markPosition.pos[1];\n}\n\ndouble MechanicsFunctions::GetMarkPosZ(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->markPosition.pos[2];\n}\n\ndouble MechanicsFunctions::GetMarkRotX(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->markPosition.rot[0];\n}\n\ndouble MechanicsFunctions::GetMarkRotZ(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->markPosition.rot[2];\n}\n\nbool MechanicsFunctions::DoesPlayerHavePlayerKiller(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, false);\n\n    return player->killer.isPlayer;\n}\n\nint MechanicsFunctions::GetPlayerKillerPid(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    Player *killer = Players::getPlayer(player->killer.guid);\n\n    if (killer != nullptr)\n        return killer->getId();\n\n    return -1;\n}\n\nconst char *MechanicsFunctions::GetPlayerKillerRefId(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    return player->killer.refId.c_str();\n}\n\nunsigned int MechanicsFunctions::GetPlayerKillerRefNum(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->killer.refNum;\n}\n\nunsigned int MechanicsFunctions::GetPlayerKillerMpNum(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->killer.mpNum;\n}\n\nconst char *MechanicsFunctions::GetPlayerKillerName(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    return player->killer.name.c_str();\n}\n\nconst char *MechanicsFunctions::GetSelectedSpellId(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->selectedSpellId.c_str();\n}\n\nunsigned int MechanicsFunctions::GetDrawState(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, false);\n\n    return player->drawState;\n}\n\nbool MechanicsFunctions::GetSneakState(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, false);\n\n    // TODO: Avoid having to use a magic number here\n    return (player->movementFlags & 8) != 0;\n}\n\nvoid MechanicsFunctions::SetMarkCell(unsigned short pid, const char *cellDescription) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->markCell = Utils::getCellFromDescription(cellDescription);\n}\n\nvoid MechanicsFunctions::SetMarkPos(unsigned short pid, double x, double y, double z) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->markPosition.pos[0] = x;\n    player->markPosition.pos[1] = y;\n    player->markPosition.pos[2] = z;\n}\n\nvoid MechanicsFunctions::SetMarkRot(unsigned short pid, double x, double z) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->markPosition.rot[0] = x;\n    player->markPosition.rot[2] = z;\n}\n\nvoid MechanicsFunctions::SetSelectedSpellId(unsigned short pid, const char *spellId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->selectedSpellId = spellId;\n}\n\nvoid MechanicsFunctions::AddAlliedPlayerForPlayer(unsigned short pid, unsigned short alliedPlayerPid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    Player *alliedPlayer;\n    GET_PLAYER(alliedPlayerPid, alliedPlayer, );\n\n    player->alliedPlayers.push_back(alliedPlayer->guid);\n}\n\nvoid MechanicsFunctions::SendMarkLocation(unsigned short pid)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->miscellaneousChangeType = mwmp::MISCELLANEOUS_CHANGE_TYPE::MARK_LOCATION;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_MISCELLANEOUS);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n}\n\nvoid MechanicsFunctions::SendSelectedSpell(unsigned short pid)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->miscellaneousChangeType = mwmp::MISCELLANEOUS_CHANGE_TYPE::SELECTED_SPELL;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_MISCELLANEOUS);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n}\n\nvoid MechanicsFunctions::SendAlliedPlayers(unsigned short pid, bool sendToOtherPlayers)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ALLY);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid MechanicsFunctions::Jail(unsigned short pid, int jailDays, bool ignoreJailTeleportation, bool ignoreJailSkillIncreases,\n                              const char* jailProgressText, const char* jailEndText) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->jailDays = jailDays;\n    player->ignoreJailTeleportation = ignoreJailTeleportation;\n    player->ignoreJailSkillIncreases = ignoreJailSkillIncreases;\n    player->jailProgressText = jailProgressText;\n    player->jailEndText = jailEndText;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_JAIL);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n}\n\nvoid MechanicsFunctions::Resurrect(unsigned short pid, unsigned int type) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->resurrectType = type;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_RESURRECT);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n    packet->Send(true);\n}\n\n// All methods below are deprecated versions of methods from above\n\nconst char *MechanicsFunctions::GetDeathReason(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (player->killer.isPlayer)\n    {\n        Player *killerPlayer = Players::getPlayer(player->killer.guid);\n\n        if (killerPlayer != nullptr)\n            return killerPlayer->npc.mName.c_str();\n    }\n    else if (!player->killer.name.empty())\n        return player->killer.name.c_str();\n\n    return \"suicide\";\n}\n\nunsigned int MechanicsFunctions::GetPlayerKillerRefNumIndex(unsigned short pid) noexcept\n{\n    return GetPlayerKillerRefNum(pid);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Mechanics.hpp",
    "content": "#ifndef OPENMW_MECHANICSAPI_HPP\n#define OPENMW_MECHANICSAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define MECHANICSAPI \\\n    {\"ClearAlliedPlayersForPlayer\", MechanicsFunctions::ClearAlliedPlayersForPlayer},\\\n    \\\n    {\"GetMiscellaneousChangeType\",  MechanicsFunctions::GetMiscellaneousChangeType},\\\n    \\\n    {\"GetMarkCell\",                 MechanicsFunctions::GetMarkCell},\\\n    {\"GetMarkPosX\",                 MechanicsFunctions::GetMarkPosX},\\\n    {\"GetMarkPosY\",                 MechanicsFunctions::GetMarkPosY},\\\n    {\"GetMarkPosZ\",                 MechanicsFunctions::GetMarkPosZ},\\\n    {\"GetMarkRotX\",                 MechanicsFunctions::GetMarkRotX},\\\n    {\"GetMarkRotZ\",                 MechanicsFunctions::GetMarkRotZ},\\\n    {\"GetSelectedSpellId\",          MechanicsFunctions::GetSelectedSpellId},\\\n    \\\n    {\"DoesPlayerHavePlayerKiller\",  MechanicsFunctions::DoesPlayerHavePlayerKiller},\\\n    {\"GetPlayerKillerPid\",          MechanicsFunctions::GetPlayerKillerPid},\\\n    {\"GetPlayerKillerRefId\",        MechanicsFunctions::GetPlayerKillerRefId},\\\n    {\"GetPlayerKillerRefNum\",       MechanicsFunctions::GetPlayerKillerRefNum},\\\n    {\"GetPlayerKillerMpNum\",        MechanicsFunctions::GetPlayerKillerMpNum},\\\n    {\"GetPlayerKillerName\",         MechanicsFunctions::GetPlayerKillerName},\\\n    \\\n    {\"GetDrawState\",                MechanicsFunctions::GetDrawState},\\\n    {\"GetSneakState\",               MechanicsFunctions::GetSneakState},\\\n    \\\n    {\"SetMarkCell\",                 MechanicsFunctions::SetMarkCell},\\\n    {\"SetMarkPos\",                  MechanicsFunctions::SetMarkPos},\\\n    {\"SetMarkRot\",                  MechanicsFunctions::SetMarkRot},\\\n    {\"SetSelectedSpellId\",          MechanicsFunctions::SetSelectedSpellId},\\\n    \\\n    {\"AddAlliedPlayerForPlayer\",    MechanicsFunctions::AddAlliedPlayerForPlayer},\\\n    \\\n    {\"SendMarkLocation\",            MechanicsFunctions::SendMarkLocation},\\\n    {\"SendSelectedSpell\",           MechanicsFunctions::SendSelectedSpell},\\\n    {\"SendAlliedPlayers\",           MechanicsFunctions::SendAlliedPlayers},\\\n    \\\n    {\"Jail\",                        MechanicsFunctions::Jail},\\\n    {\"Resurrect\",                   MechanicsFunctions::Resurrect},\\\n    \\\n    {\"GetDeathReason\",              MechanicsFunctions::GetDeathReason},\\\n    {\"GetPlayerKillerRefNumIndex\",  MechanicsFunctions::GetPlayerKillerRefNumIndex}\n\nclass MechanicsFunctions\n{\npublic:\n\n    /**\n    * \\brief Clear the list of players who will be regarded as being player's allies.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void ClearAlliedPlayersForPlayer(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the type of a PlayerMiscellaneous packet.\n    *\n    * \\param pid The player ID.\n    * \\return The type.\n    */\n    static unsigned char GetMiscellaneousChangeType(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the cell description of a player's Mark cell.\n    *\n    * \\param pid The player ID.\n    * \\return The cell description.\n    */\n    static const char *GetMarkCell(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the X position of a player's Mark.\n    *\n    * \\param pid The player ID.\n    * \\return The X position.\n    */\n    static double GetMarkPosX(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Y position of a player's Mark.\n    *\n    * \\param pid The player ID.\n    * \\return The Y position.\n    */\n    static double GetMarkPosY(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Z position of a player's Mark.\n    *\n    * \\param pid The player ID.\n    * \\return The Z position.\n    */\n    static double GetMarkPosZ(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the X rotation of a player's Mark.\n    *\n    * \\param pid The player ID.\n    * \\return The X rotation.\n    */\n    static double GetMarkRotX(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Z rotation of a player's Mark.\n    *\n    * \\param pid The player ID.\n    * \\return The X rotation.\n    */\n    static double GetMarkRotZ(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the ID of a player's selected spell.\n    *\n    * \\param pid The player ID.\n    * \\return The spell ID.\n    */\n    static const char *GetSelectedSpellId(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Check whether the killer of a certain player is also a player.\n    *\n    * \\param pid The player ID of the killed player.\n    * \\return Whether the player was killed by another player.\n    */\n    static bool DoesPlayerHavePlayerKiller(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the player ID of the killer of a certain player.\n    *\n    * \\param pid The player ID of the killed player.\n    * \\return The player ID of the killer.\n    */\n    static int GetPlayerKillerPid(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the refId of the actor killer of a certain player.\n    *\n    * \\param pid The player ID of the killed player.\n    * \\return The refId of the killer.\n    */\n    static const char *GetPlayerKillerRefId(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the refNum of the actor killer of a certain player.\n    *\n    * \\param pid The player ID of the killed player.\n    * \\return The refNum of the killer.\n    */\n    static unsigned int GetPlayerKillerRefNum(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the actor killer of a certain player.\n    *\n    * \\param pid The player ID of the killed player.\n    * \\return The mpNum of the killer.\n    */\n    static unsigned int GetPlayerKillerMpNum(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the name of the actor killer of a certain player.\n    *\n    * \\param pid The player ID of the killed player.\n    * \\return The name of the killer.\n    */\n    static const char *GetPlayerKillerName(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the draw state of a player (0 for nothing, 1 for drawn weapon,\n    *        2 for drawn spell).\n    *\n    * \\param pid The player ID.\n    * \\return The draw state.\n    */\n    static unsigned int GetDrawState(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the sneak state of a player.\n    *\n    * \\param pid The player ID.\n    * \\return Whether the player is sneaking.\n    */\n    static bool GetSneakState(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the Mark cell of a player.\n    *\n    * This changes the Mark cell recorded for that player in the server memory, but does not by itself\n    * send a packet.\n    *\n    * The cell is determined to be an exterior cell if it fits the pattern of a number followed\n    * by a comma followed by another number.\n    *\n    * \\param pid The player ID.\n    * \\param cellDescription The cell description.\n    * \\return void\n    */\n    static void SetMarkCell(unsigned short pid, const char *cellDescription) noexcept;\n\n    /**\n    * \\brief Set the Mark position of a player.\n    *\n    * This changes the Mark positional coordinates recorded for that player in the server memory, but\n    * does not by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param x The X position.\n    * \\param y The Y position.\n    * \\param z The Z position.\n    * \\return void\n    */\n    static void SetMarkPos(unsigned short pid, double x, double y, double z) noexcept;\n\n    /**\n    * \\brief Set the Mark rotation of a player.\n    *\n    * This changes the Mark positional coordinates recorded for that player in the server memory, but\n    * does not by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param x The X rotation.\n    * \\param z The Z rotation.\n    * \\return void\n    */\n    static void SetMarkRot(unsigned short pid, double x, double z) noexcept;\n\n    /**\n    * \\brief Set the ID of a player's selected spell.\n    *\n    * This changes the spell ID recorded for that player in the server memory, but does not by itself\n    * send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param spellId The spell ID.\n    * \\return void\n    */\n    static void SetSelectedSpellId(unsigned short pid, const char *spellId) noexcept;\n\n    /**\n    * \\brief Add an ally to a player's list of allied players.\n    *\n    * \\param pid The player ID.\n    * \\param alliedPlayerPid The ally's player ID.\n    * \\return void\n    */\n    static void AddAlliedPlayerForPlayer(unsigned short pid, unsigned short alliedPlayerPid) noexcept;\n\n    /**\n    * \\brief Send a PlayerMiscellaneous packet with a Mark location to a player.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendMarkLocation(unsigned short pid);\n\n    /**\n    * \\brief Send a PlayerMiscellaneous packet with a selected spell ID to a player.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendSelectedSpell(unsigned short pid);\n\n    /**\n    * \\brief Send a PlayerAlly packet with a list of team member IDs to a player.\n    *\n    * \\param pid The player ID.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\return void\n    */\n    static void SendAlliedPlayers(unsigned short pid, bool sendToOtherPlayers);\n\n    /**\n    * \\brief Send a PlayerJail packet about a player.\n    *\n    * This is similar to the player being jailed by a guard, but provides extra parameters for\n    * increased flexibility.\n    *\n    * It is only sent to the player being jailed, as the other players will be informed of the\n    * jailing's actual consequences via other packets sent by the affected client.\n    *\n    * \\param pid The player ID.\n    * \\param jailDays The number of days to spend jailed, where each day affects one skill point.\n    * \\param ignoreJailTeleportation Whether the player being teleported to the nearest jail\n    *                                marker should be overridden.\n    * \\param ignoreJailSkillIncreases Whether the player's Sneak and Security skills should be\n    *                                 prevented from increasing as a result of the jailing,\n    *                                 overriding default behavior.\n    * \\param jailProgressText The text that should be displayed while jailed.\n    * \\param jailEndText The text that should be displayed once the jailing period is over.\n    * \\return void\n    */\n    static void Jail(unsigned short pid, int jailDays, bool ignoreJailTeleportation, bool ignoreJailSkillIncreases,\n                     const char* jailProgressText, const char* jailEndText) noexcept;\n\n    /**\n    * \\brief Send a PlayerResurrect packet about a player.\n    *\n    * This sends the packet to all players connected to the server.\n    *\n    * \\param pid The player ID.\n    * \\param type The type of resurrection (0 for REGULAR, 1 for IMPERIAL_SHRINE,\n    *             2 for TRIBUNAL_TEMPLE).\n    * \\return void\n    */\n    static void Resurrect(unsigned short pid, unsigned int type) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static const char *GetDeathReason(unsigned short pid) noexcept;\n    static unsigned int GetPlayerKillerRefNumIndex(unsigned short pid) noexcept;\n\n};\n\n#endif //OPENMW_MECHANICSAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Miscellaneous.cpp",
    "content": "#include \"Miscellaneous.hpp\"\n\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include <apps/openmw-mp/Networking.hpp>\n\n#include <extern/PicoSHA2/picosha2.h>\n\n#include <iostream>\n#include <random>\n\nstatic std::string tempRandomString;\nstatic std::string tempHashString;\n\nconst char* MiscellaneousFunctions::GenerateRandomString(unsigned int length) noexcept\n{\n    const std::string characters = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n    std::random_device randomDevice;\n    std::mt19937 generator(randomDevice());\n    std::uniform_int_distribution<std::mt19937::result_type> distribution(0, characters.size() - 1);\n    std::string randomString = \"\";\n\n    for (std::size_t i = 0; i < length; ++i)\n    {\n        randomString += characters[distribution(generator)];\n    }\n\n    tempRandomString = randomString;\n    return tempRandomString.c_str();\n}\n\nconst char* MiscellaneousFunctions::GetSHA256Hash(const char* inputString) noexcept\n{\n    tempHashString = picosha2::hash256_hex_string(std::string{inputString});\n    return tempHashString.c_str();\n}\n\nunsigned int MiscellaneousFunctions::GetLastPlayerId() noexcept\n{\n    return Players::getLastPlayerId();\n}\n\nint MiscellaneousFunctions::GetCurrentMpNum() noexcept\n{\n    return mwmp::Networking::getPtr()->getCurrentMpNum();\n}\n\nvoid MiscellaneousFunctions::SetCurrentMpNum(int mpNum) noexcept\n{\n    mwmp::Networking::getPtr()->setCurrentMpNum(mpNum);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Miscellaneous.hpp",
    "content": "#ifndef OPENMW_MISCELLANEOUSAPI_HPP\n#define OPENMW_MISCELLANEOUSAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define MISCELLANEOUSAPI \\\n    {\"GenerateRandomString\",        MiscellaneousFunctions::GenerateRandomString},\\\n    \\\n    {\"GetSHA256Hash\",               MiscellaneousFunctions::GetSHA256Hash},\\\n    \\\n    {\"GetLastPlayerId\",             MiscellaneousFunctions::GetLastPlayerId},\\\n    \\\n    {\"GetCurrentMpNum\",             MiscellaneousFunctions::GetCurrentMpNum},\\\n    {\"SetCurrentMpNum\",             MiscellaneousFunctions::SetCurrentMpNum}\n\nclass MiscellaneousFunctions\n{\npublic:\n\n    /**\n    * \\brief Generate a random string of a particular length that only contains\n    *        letters and numbers.\n    *\n    * \\param length The length of the generated string.\n    * \\return The generated string.\n    */\n    static const char *GenerateRandomString(unsigned int length) noexcept;\n\n    /**\n    * \\brief Get the SHA256 hash corresponding to an input string.\n    * \\details function is not reentrant due to a static variable\n    *\n    * \\param inputString The input string.\n    * \\return The SHA256 hash.\n    */\n    static const char *GetSHA256Hash(const char* inputString) noexcept;\n\n    /**\n    * \\brief Get the last player ID currently connected to the server.\n    * \\details function is not reentrant due to a static variable\n    *\n    * Every player receives a unique numerical index known as their player ID upon joining the\n    * server.\n    *\n    * \\return The player ID.\n    */\n    static unsigned int GetLastPlayerId() noexcept;\n\n    /**\n    * \\brief Get the current (latest) mpNum generated by the server.\n    *\n    * Every object that did not exist in an .ESM or .ESP data file and has instead been placed or\n    * spawned through a server-sent packet has a numerical index known as its mpNum.\n    *\n    * When ObjectPlace and ObjectSpawn packets are received from players, their objects lack mpNums,\n    * so the server assigns them some based on incrementing the server's current mpNum, with the\n    * operation's final mpNum becoming the server's new current mpNum.\n    *\n    * \\return The mpNum.\n    */\n    static int GetCurrentMpNum() noexcept;\n\n    /**\n    * \\brief Set the current (latest) mpNum generated by the server.\n    *\n    * When restarting a server, it is important to revert to the previous current (latest) mpNum\n    * as stored in the server's data, so as to avoid starting over from 0 and ending up assigning\n    * duplicate mpNums to objects.\n    *\n    * \\param mpNum The number that should be used as the new current mpNum.\n    * \\return void\n    */\n    static void SetCurrentMpNum(int mpNum) noexcept;\n};\n\n#endif //OPENMW_MISCELLANEOUSAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Objects.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Base/BaseObject.hpp>\n\n#include <apps/openmw-mp/Networking.hpp>\n#include <apps/openmw-mp/Player.hpp>\n#include <apps/openmw-mp/Utils.hpp>\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n\n#include \"Objects.hpp\"\n\nusing namespace mwmp;\n\nBaseObjectList *readObjectList;\nBaseObjectList writeObjectList;\n\nBaseObject tempObject;\nconst BaseObject emptyObject = {};\n\nContainerItem tempContainerItem;\nconst ContainerItem emptyContainerItem = {};\n\nvoid ObjectFunctions::ReadReceivedObjectList() noexcept\n{\n    readObjectList = mwmp::Networking::getPtr()->getReceivedObjectList();\n}\n\nvoid ObjectFunctions::ClearObjectList() noexcept\n{\n    writeObjectList.cell.blank();\n    writeObjectList.baseObjects.clear();\n    writeObjectList.packetOrigin = mwmp::PACKET_ORIGIN::SERVER_SCRIPT;\n}\n\nvoid ObjectFunctions::SetObjectListPid(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeObjectList.guid = player->guid;\n}\n\nvoid ObjectFunctions::CopyReceivedObjectListToStore() noexcept\n{\n    writeObjectList = *readObjectList;\n}\n\nunsigned int ObjectFunctions::GetObjectListSize() noexcept\n{\n    return readObjectList->baseObjectCount;\n}\n\nunsigned char ObjectFunctions::GetObjectListOrigin() noexcept\n{\n    return readObjectList->packetOrigin;\n}\n\nconst char *ObjectFunctions::GetObjectListClientScript() noexcept\n{\n    return readObjectList->originClientScript.c_str();\n}\n\nunsigned char ObjectFunctions::GetObjectListAction() noexcept\n{\n    return readObjectList->action;\n}\n\nconst char *ObjectFunctions::GetObjectListConsoleCommand() noexcept\n{\n    return readObjectList->consoleCommand.c_str();\n}\n\nunsigned char ObjectFunctions::GetObjectListContainerSubAction() noexcept\n{\n    return readObjectList->containerSubAction;\n}\n\nbool ObjectFunctions::IsObjectPlayer(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).isPlayer;\n}\n\nint ObjectFunctions::GetObjectPid(unsigned int index) noexcept\n{\n    Player *player = Players::getPlayer(readObjectList->baseObjects.at(index).guid);\n\n    if (player != nullptr)\n        return player->getId();\n\n    return -1;\n}\n\nconst char *ObjectFunctions::GetObjectRefId(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).refId.c_str();\n}\n\nunsigned int ObjectFunctions::GetObjectRefNum(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).refNum;\n}\n\nunsigned int ObjectFunctions::GetObjectMpNum(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).mpNum;\n}\n\nint ObjectFunctions::GetObjectCount(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).count;\n}\n\nint ObjectFunctions::GetObjectCharge(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).charge;\n}\n\ndouble ObjectFunctions::GetObjectEnchantmentCharge(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).enchantmentCharge;\n}\n\nconst char *ObjectFunctions::GetObjectSoul(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).soul.c_str();\n}\n\nint ObjectFunctions::GetObjectGoldValue(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).goldValue;\n}\n\ndouble ObjectFunctions::GetObjectScale(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).scale;\n}\n\nconst char *ObjectFunctions::GetObjectSoundId(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).soundId.c_str();\n}\n\nbool ObjectFunctions::GetObjectState(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).objectState;\n}\n\nint ObjectFunctions::GetObjectDoorState(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).doorState;\n}\n\nint ObjectFunctions::GetObjectLockLevel(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).lockLevel;\n}\n\nunsigned int ObjectFunctions::GetObjectDialogueChoiceType(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).dialogueChoiceType;\n}\n\nconst char* ObjectFunctions::GetObjectDialogueChoiceTopic(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).topicId.c_str();\n}\n\nunsigned int ObjectFunctions::GetObjectGoldPool(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).goldPool;\n}\n\ndouble ObjectFunctions::GetObjectLastGoldRestockHour(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).lastGoldRestockHour;\n}\n\nint ObjectFunctions::GetObjectLastGoldRestockDay(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).lastGoldRestockDay;\n}\n\nbool ObjectFunctions::DoesObjectHavePlayerActivating(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).activatingActor.isPlayer;\n}\n\nint ObjectFunctions::GetObjectActivatingPid(unsigned int index) noexcept\n{\n    Player *player = Players::getPlayer(readObjectList->baseObjects.at(index).activatingActor.guid);\n\n    if (player != nullptr)\n        return player->getId();\n\n    return -1;\n}\n\nconst char *ObjectFunctions::GetObjectActivatingRefId(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).activatingActor.refId.c_str();\n}\n\nunsigned int ObjectFunctions::GetObjectActivatingRefNum(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).activatingActor.refNum;\n}\n\nunsigned int ObjectFunctions::GetObjectActivatingMpNum(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).activatingActor.mpNum;\n}\n\nconst char *ObjectFunctions::GetObjectActivatingName(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).activatingActor.name.c_str();\n}\n\nbool ObjectFunctions::GetObjectHitSuccess(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hitAttack.success;\n}\n\ndouble ObjectFunctions::GetObjectHitDamage(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hitAttack.damage;\n}\n\nbool ObjectFunctions::GetObjectHitBlock(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hitAttack.block;\n}\n\nbool ObjectFunctions::GetObjectHitKnockdown(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hitAttack.knockdown;\n}\n\nbool ObjectFunctions::DoesObjectHavePlayerHitting(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hittingActor.isPlayer;\n}\n\nint ObjectFunctions::GetObjectHittingPid(unsigned int index) noexcept\n{\n    Player *player = Players::getPlayer(readObjectList->baseObjects.at(index).hittingActor.guid);\n\n    if (player != nullptr)\n        return player->getId();\n\n    return -1;\n}\n\nconst char *ObjectFunctions::GetObjectHittingRefId(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hittingActor.refId.c_str();\n}\n\nunsigned int ObjectFunctions::GetObjectHittingRefNum(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hittingActor.refNum;\n}\n\nunsigned int ObjectFunctions::GetObjectHittingMpNum(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hittingActor.mpNum;\n}\n\nconst char *ObjectFunctions::GetObjectHittingName(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hittingActor.name.c_str();\n}\n\nbool ObjectFunctions::GetObjectSummonState(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).isSummon;\n}\n\ndouble ObjectFunctions::GetObjectSummonDuration(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).summonDuration;\n}\n\ndouble ObjectFunctions::GetObjectSummonEffectId(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).summonEffectId;\n}\n\nconst char *ObjectFunctions::GetObjectSummonSpellId(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).summonSpellId.c_str();\n}\n\nbool ObjectFunctions::DoesObjectHavePlayerSummoner(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).master.isPlayer;\n}\n\nint ObjectFunctions::GetObjectSummonerPid(unsigned int index) noexcept\n{\n    Player *player = Players::getPlayer(readObjectList->baseObjects.at(index).master.guid);\n    \n    if (player != nullptr)\n        return player->getId();\n\n    return -1;\n}\n\nconst char *ObjectFunctions::GetObjectSummonerRefId(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).master.refId.c_str();\n}\n\nunsigned int ObjectFunctions::GetObjectSummonerRefNum(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).master.refNum;\n}\n\nunsigned int ObjectFunctions::GetObjectSummonerMpNum(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).master.mpNum;\n}\n\ndouble ObjectFunctions::GetObjectPosX(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).position.pos[0];\n}\n\ndouble ObjectFunctions::GetObjectPosY(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).position.pos[1];\n}\n\ndouble ObjectFunctions::GetObjectPosZ(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).position.pos[2];\n}\n\ndouble ObjectFunctions::GetObjectRotX(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).position.rot[0];\n}\n\ndouble ObjectFunctions::GetObjectRotY(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).position.rot[1];\n}\n\ndouble ObjectFunctions::GetObjectRotZ(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).position.rot[2];\n}\n\nconst char *ObjectFunctions::GetVideoFilename(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).videoFilename.c_str();\n}\n\nunsigned int ObjectFunctions::GetClientLocalsSize(unsigned int objectIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex).clientLocals.size();\n}\n\nunsigned int ObjectFunctions::GetClientLocalInternalIndex(unsigned int objectIndex, unsigned int variableIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex).clientLocals.at(variableIndex).internalIndex;\n}\n\nunsigned short ObjectFunctions::GetClientLocalVariableType(unsigned int objectIndex, unsigned int variableIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex).clientLocals.at(variableIndex).variableType;\n}\n\nint ObjectFunctions::GetClientLocalIntValue(unsigned int objectIndex, unsigned int variableIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex).clientLocals.at(variableIndex).intValue;\n}\n\ndouble ObjectFunctions::GetClientLocalFloatValue(unsigned int objectIndex, unsigned int variableIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex).clientLocals.at(variableIndex).floatValue;\n}\n\nunsigned int ObjectFunctions::GetContainerChangesSize(unsigned int objectIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex).containerItemCount;\n}\n\nconst char *ObjectFunctions::GetContainerItemRefId(unsigned int objectIndex, unsigned int itemIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex)\n        .containerItems.at(itemIndex).refId.c_str();\n}\n\nint ObjectFunctions::GetContainerItemCount(unsigned int objectIndex, unsigned int itemIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex)\n        .containerItems.at(itemIndex).count;\n}\n\nint ObjectFunctions::GetContainerItemCharge(unsigned int objectIndex, unsigned int itemIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex)\n        .containerItems.at(itemIndex).charge;\n}\n\ndouble ObjectFunctions::GetContainerItemEnchantmentCharge(unsigned int objectIndex, unsigned int itemIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex)\n        .containerItems.at(itemIndex).enchantmentCharge;\n}\n\nconst char *ObjectFunctions::GetContainerItemSoul(unsigned int objectIndex, unsigned int itemIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex)\n        .containerItems.at(itemIndex).soul.c_str();\n}\n\nint ObjectFunctions::GetContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex) noexcept\n{\n    return readObjectList->baseObjects.at(objectIndex)\n        .containerItems.at(itemIndex).actionCount;\n}\n\nbool ObjectFunctions::DoesObjectHaveContainer(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).hasContainer;\n}\n\nbool ObjectFunctions::IsObjectDroppedByPlayer(unsigned int index) noexcept\n{\n    return readObjectList->baseObjects.at(index).droppedByPlayer;\n}\n\nvoid ObjectFunctions::SetObjectListCell(const char* cellDescription) noexcept\n{\n    writeObjectList.cell = Utils::getCellFromDescription(cellDescription);\n}\n\nvoid ObjectFunctions::SetObjectListAction(unsigned char action) noexcept\n{\n    writeObjectList.action = action;\n}\n\nvoid ObjectFunctions::SetObjectListContainerSubAction(unsigned char containerSubAction) noexcept\n{\n    writeObjectList.containerSubAction = containerSubAction;\n}\n\nvoid ObjectFunctions::SetObjectListConsoleCommand(const char* consoleCommand) noexcept\n{\n    writeObjectList.consoleCommand = consoleCommand;\n}\n\nvoid ObjectFunctions::SetObjectRefId(const char* refId) noexcept\n{\n    tempObject.refId = refId;\n}\n\nvoid ObjectFunctions::SetObjectRefNum(int refNum) noexcept\n{\n    tempObject.refNum = refNum;\n}\n\nvoid ObjectFunctions::SetObjectMpNum(int mpNum) noexcept\n{\n    tempObject.mpNum = mpNum;\n}\n\nvoid ObjectFunctions::SetObjectCount(int count) noexcept\n{\n    tempObject.count = count;\n}\n\nvoid ObjectFunctions::SetObjectCharge(int charge) noexcept\n{\n    tempObject.charge = charge;\n}\n\nvoid ObjectFunctions::SetObjectEnchantmentCharge(double enchantmentCharge) noexcept\n{\n    tempObject.enchantmentCharge = enchantmentCharge;\n}\n\nvoid ObjectFunctions::SetObjectSoul(const char* soul) noexcept\n{\n    tempObject.soul = soul;\n}\n\nvoid ObjectFunctions::SetObjectGoldValue(int goldValue) noexcept\n{\n    tempObject.goldValue = goldValue;\n}\n\nvoid ObjectFunctions::SetObjectScale(double scale) noexcept\n{\n    tempObject.scale = scale;\n}\n\nvoid ObjectFunctions::SetObjectState(bool objectState) noexcept\n{\n    tempObject.objectState = objectState;\n}\n\nvoid ObjectFunctions::SetObjectLockLevel(int lockLevel) noexcept\n{\n    tempObject.lockLevel = lockLevel;\n}\n\nvoid ObjectFunctions::SetObjectDialogueChoiceType(unsigned int dialogueChoiceType) noexcept\n{\n    tempObject.dialogueChoiceType = dialogueChoiceType;\n}\n\nvoid ObjectFunctions::SetObjectDialogueChoiceTopic(const char* topic) noexcept\n{\n    tempObject.topicId = topic;\n}\n\nvoid ObjectFunctions::SetObjectGoldPool(unsigned int goldPool) noexcept\n{\n    tempObject.goldPool = goldPool;\n}\n\nvoid ObjectFunctions::SetObjectLastGoldRestockHour(double lastGoldRestockHour) noexcept\n{\n    tempObject.lastGoldRestockHour = lastGoldRestockHour;\n}\n\nvoid ObjectFunctions::SetObjectLastGoldRestockDay(int lastGoldRestockDay) noexcept\n{\n    tempObject.lastGoldRestockDay = lastGoldRestockDay;\n}\n\nvoid ObjectFunctions::SetObjectDisarmState(bool disarmState) noexcept\n{\n    tempObject.isDisarmed = disarmState;\n}\n\nvoid ObjectFunctions::SetObjectDroppedByPlayerState(bool droppedByPlayer) noexcept\n{\n    tempObject.droppedByPlayer = droppedByPlayer;\n}\n\nvoid ObjectFunctions::SetObjectPosition(double x, double y, double z) noexcept\n{\n    tempObject.position.pos[0] = x;\n    tempObject.position.pos[1] = y;\n    tempObject.position.pos[2] = z;\n}\n\nvoid ObjectFunctions::SetObjectRotation(double x, double y, double z) noexcept\n{\n    tempObject.position.rot[0] = x;\n    tempObject.position.rot[1] = y;\n    tempObject.position.rot[2] = z;\n}\n\nvoid ObjectFunctions::SetObjectSound(const char* soundId, double volume, double pitch) noexcept\n{\n    tempObject.soundId = soundId;\n    tempObject.volume = volume;\n    tempObject.pitch = pitch;\n}\n\nvoid ObjectFunctions::SetObjectSummonState(bool summonState) noexcept\n{\n    tempObject.isSummon = summonState;\n}\n\nvoid ObjectFunctions::SetObjectSummonEffectId(int summonEffectId) noexcept\n{\n    tempObject.summonEffectId = summonEffectId;\n}\n\nvoid ObjectFunctions::SetObjectSummonSpellId(const char* summonSpellId) noexcept\n{\n    tempObject.summonSpellId = summonSpellId;\n}\n\nvoid ObjectFunctions::SetObjectSummonDuration(double summonDuration) noexcept\n{\n    tempObject.summonDuration = summonDuration;\n}\n\nvoid ObjectFunctions::SetObjectSummonerPid(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    tempObject.master.isPlayer = true;\n    tempObject.master.guid = player->guid;\n}\n\nvoid ObjectFunctions::SetObjectSummonerRefNum(int refNum) noexcept\n{\n    tempObject.master.isPlayer = false;\n    tempObject.master.refNum = refNum;\n}\n\nvoid ObjectFunctions::SetObjectSummonerMpNum(int mpNum) noexcept\n{\n    tempObject.master.isPlayer = false;\n    tempObject.master.mpNum = mpNum;\n}\n\nvoid ObjectFunctions::SetObjectActivatingPid(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    tempObject.activatingActor.isPlayer = true;\n    tempObject.activatingActor.guid = player->guid;\n}\n\nvoid ObjectFunctions::SetObjectDoorState(int doorState) noexcept\n{\n    tempObject.doorState = doorState;\n}\n\nvoid ObjectFunctions::SetObjectDoorTeleportState(bool teleportState) noexcept\n{\n    tempObject.teleportState = teleportState;\n}\n\nvoid ObjectFunctions::SetObjectDoorDestinationCell(const char* cellDescription) noexcept\n{\n    tempObject.destinationCell = Utils::getCellFromDescription(cellDescription);\n}\n\nvoid ObjectFunctions::SetObjectDoorDestinationPosition(double x, double y, double z) noexcept\n{\n    tempObject.destinationPosition.pos[0] = x;\n    tempObject.destinationPosition.pos[1] = y;\n    tempObject.destinationPosition.pos[2] = z;\n}\n\nvoid ObjectFunctions::SetObjectDoorDestinationRotation(double x, double z) noexcept\n{\n    tempObject.destinationPosition.rot[0] = x;\n    tempObject.destinationPosition.rot[2] = z;\n}\n\nvoid ObjectFunctions::SetPlayerAsObject(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    tempObject.guid = player->guid;\n    tempObject.isPlayer = true;\n}\n\nvoid ObjectFunctions::SetContainerItemRefId(const char* refId) noexcept\n{\n    tempContainerItem.refId = refId;\n}\n\nvoid ObjectFunctions::SetContainerItemCount(int count) noexcept\n{\n    tempContainerItem.count = count;\n}\n\nvoid ObjectFunctions::SetContainerItemCharge(int charge) noexcept\n{\n    tempContainerItem.charge = charge;\n}\n\nvoid ObjectFunctions::SetContainerItemEnchantmentCharge(double enchantmentCharge) noexcept\n{\n    tempContainerItem.enchantmentCharge = enchantmentCharge;\n}\n\nvoid ObjectFunctions::SetContainerItemSoul(const char* soul) noexcept\n{\n    tempContainerItem.soul = soul;\n}\n\nvoid ObjectFunctions::SetContainerItemActionCountByIndex(unsigned int objectIndex, unsigned int itemIndex, int actionCount) noexcept\n{\n    writeObjectList.baseObjects.at(objectIndex).containerItems.at(itemIndex).actionCount = actionCount;\n}\n\nvoid ObjectFunctions::AddObject() noexcept\n{\n    writeObjectList.baseObjects.push_back(tempObject);\n\n    tempObject = emptyObject;\n}\n\nvoid ObjectFunctions::AddClientLocalInteger(int internalIndex, int intValue, unsigned int variableType) noexcept\n{\n    ClientVariable clientLocal;\n    clientLocal.internalIndex = internalIndex;\n    clientLocal.intValue = intValue;\n    clientLocal.variableType = variableType;\n\n    tempObject.clientLocals.push_back(clientLocal);\n}\n\nvoid ObjectFunctions::AddClientLocalFloat(int internalIndex, double floatValue) noexcept\n{\n    ClientVariable clientLocal;\n    clientLocal.internalIndex = internalIndex;\n    clientLocal.floatValue = floatValue;\n    clientLocal.variableType = mwmp::VARIABLE_TYPE::FLOAT;\n\n    tempObject.clientLocals.push_back(clientLocal);\n}\n\nvoid ObjectFunctions::AddContainerItem() noexcept\n{\n    tempObject.containerItems.push_back(tempContainerItem);\n\n    tempContainerItem = emptyContainerItem;\n}\n\nvoid ObjectFunctions::SendObjectActivate(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_ACTIVATE);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectPlace(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_PLACE);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectSpawn(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_SPAWN);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectDelete(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_DELETE);\n    packet->setObjectList(&writeObjectList);\n    \n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectLock(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_LOCK);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectDialogueChoice(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket* packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_DIALOGUE_CHOICE);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectMiscellaneous(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket* packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_MISCELLANEOUS);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectRestock(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_RESTOCK);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectTrap(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_TRAP);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectScale(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_SCALE);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectSound(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_SOUND);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectState(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_STATE);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectMove(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket* packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_MOVE);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendObjectRotate(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket* packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_ROTATE);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendDoorState(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_DOOR_STATE);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendDoorDestination(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_DOOR_DESTINATION);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendContainer(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CONTAINER);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendVideoPlay(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_VIDEO_PLAY);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendClientScriptLocal(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket* packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CLIENT_SCRIPT_LOCAL);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid ObjectFunctions::SendConsoleCommand(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CONSOLE_COMMAND);\n    packet->setObjectList(&writeObjectList);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\n\n// All methods below are deprecated versions of methods from above\n\nvoid ObjectFunctions::ReadLastObjectList() noexcept\n{\n    ReadReceivedObjectList();\n}\n\nvoid ObjectFunctions::ReadLastEvent() noexcept\n{\n    ReadReceivedObjectList();\n}\n\nvoid ObjectFunctions::InitializeObjectList(unsigned short pid) noexcept\n{\n    ClearObjectList();\n    SetObjectListPid(pid);\n}\n\nvoid ObjectFunctions::InitializeEvent(unsigned short pid) noexcept\n{\n    InitializeObjectList(pid);\n}\n\nvoid ObjectFunctions::CopyLastObjectListToStore() noexcept\n{\n    CopyReceivedObjectListToStore();\n}\n\nunsigned int ObjectFunctions::GetObjectChangesSize() noexcept\n{\n    return GetObjectListSize();\n}\n\nunsigned char ObjectFunctions::GetEventAction() noexcept\n{\n    return GetObjectListAction();\n}\n\nunsigned char ObjectFunctions::GetEventContainerSubAction() noexcept\n{\n    return GetObjectListContainerSubAction();\n}\n\nunsigned int ObjectFunctions::GetObjectRefNumIndex(unsigned int index) noexcept\n{\n    return GetObjectRefNum(index);\n}\n\nunsigned int ObjectFunctions::GetObjectSummonerRefNumIndex(unsigned int index) noexcept\n{\n    return GetObjectSummonerRefNum(index);\n}\n\nvoid ObjectFunctions::SetEventCell(const char* cellDescription) noexcept\n{\n    SetObjectListCell(cellDescription);\n}\n\nvoid ObjectFunctions::SetEventAction(unsigned char action) noexcept\n{\n    SetObjectListAction(action);\n}\n\nvoid ObjectFunctions::SetEventConsoleCommand(const char* consoleCommand) noexcept\n{\n    SetObjectListConsoleCommand(consoleCommand);\n}\n\nvoid ObjectFunctions::SetObjectRefNumIndex(int refNum) noexcept\n{\n    SetObjectRefNum(refNum);\n}\n\nvoid ObjectFunctions::AddWorldObject() noexcept\n{\n    AddObject();\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Objects.hpp",
    "content": "#ifndef OPENMW_OBJECTAPI_HPP\n#define OPENMW_OBJECTAPI_HPP\n\n#define OBJECTAPI \\\n    {\"ReadReceivedObjectList\",                ObjectFunctions::ReadReceivedObjectList},\\\n    \\\n    {\"ClearObjectList\",                       ObjectFunctions::ClearObjectList},\\\n    {\"SetObjectListPid\",                      ObjectFunctions::SetObjectListPid},\\\n    \\\n    {\"CopyReceivedObjectListToStore\",         ObjectFunctions::CopyReceivedObjectListToStore},\\\n    \\\n    {\"GetObjectListSize\",                     ObjectFunctions::GetObjectListSize},\\\n    {\"GetObjectListOrigin\",                   ObjectFunctions::GetObjectListOrigin},\\\n    {\"GetObjectListClientScript\",             ObjectFunctions::GetObjectListClientScript},\\\n    {\"GetObjectListAction\",                   ObjectFunctions::GetObjectListAction},\\\n    {\"GetObjectListConsoleCommand\",           ObjectFunctions::GetObjectListConsoleCommand},\\\n    {\"GetObjectListContainerSubAction\",       ObjectFunctions::GetObjectListContainerSubAction},\\\n    \\\n    {\"IsObjectPlayer\",                        ObjectFunctions::IsObjectPlayer},\\\n    {\"GetObjectPid\",                          ObjectFunctions::GetObjectPid},\\\n    {\"GetObjectRefId\",                        ObjectFunctions::GetObjectRefId},\\\n    {\"GetObjectRefNum\",                       ObjectFunctions::GetObjectRefNum},\\\n    {\"GetObjectMpNum\",                        ObjectFunctions::GetObjectMpNum},\\\n    {\"GetObjectCount\",                        ObjectFunctions::GetObjectCount},\\\n    {\"GetObjectCharge\",                       ObjectFunctions::GetObjectCharge},\\\n    {\"GetObjectEnchantmentCharge\",            ObjectFunctions::GetObjectEnchantmentCharge},\\\n    {\"GetObjectSoul\" ,                        ObjectFunctions::GetObjectSoul},\\\n    {\"GetObjectGoldValue\",                    ObjectFunctions::GetObjectGoldValue},\\\n    {\"GetObjectScale\",                        ObjectFunctions::GetObjectScale},\\\n    {\"GetObjectSoundId\",                      ObjectFunctions::GetObjectSoundId},\\\n    {\"GetObjectState\",                        ObjectFunctions::GetObjectState},\\\n    {\"GetObjectDoorState\",                    ObjectFunctions::GetObjectDoorState},\\\n    {\"GetObjectLockLevel\",                    ObjectFunctions::GetObjectLockLevel},\\\n    {\"GetObjectDialogueChoiceType\",           ObjectFunctions::GetObjectDialogueChoiceType},\\\n    {\"GetObjectDialogueChoiceTopic\",          ObjectFunctions::GetObjectDialogueChoiceTopic},\\\n    {\"GetObjectGoldPool\",                     ObjectFunctions::GetObjectGoldPool},\\\n    {\"GetObjectLastGoldRestockHour\",          ObjectFunctions::GetObjectLastGoldRestockHour},\\\n    {\"GetObjectLastGoldRestockDay\",           ObjectFunctions::GetObjectLastGoldRestockDay},\\\n    \\\n    {\"DoesObjectHavePlayerActivating\",        ObjectFunctions::DoesObjectHavePlayerActivating},\\\n    {\"GetObjectActivatingPid\",                ObjectFunctions::GetObjectActivatingPid},\\\n    {\"GetObjectActivatingRefId\",              ObjectFunctions::GetObjectActivatingRefId},\\\n    {\"GetObjectActivatingRefNum\",             ObjectFunctions::GetObjectActivatingRefNum},\\\n    {\"GetObjectActivatingMpNum\",              ObjectFunctions::GetObjectActivatingMpNum},\\\n    {\"GetObjectActivatingName\",               ObjectFunctions::GetObjectActivatingName},\\\n    \\\n    {\"GetObjectHitSuccess\",                   ObjectFunctions::GetObjectHitSuccess},\\\n    {\"GetObjectHitDamage\",                    ObjectFunctions::GetObjectHitDamage},\\\n    {\"GetObjectHitBlock\",                     ObjectFunctions::GetObjectHitBlock},\\\n    {\"GetObjectHitKnockdown\",                 ObjectFunctions::GetObjectHitKnockdown},\\\n    {\"DoesObjectHavePlayerHitting\",           ObjectFunctions::DoesObjectHavePlayerHitting},\\\n    {\"GetObjectHittingPid\",                   ObjectFunctions::GetObjectHittingPid},\\\n    {\"GetObjectHittingRefId\",                 ObjectFunctions::GetObjectHittingRefId},\\\n    {\"GetObjectHittingRefNum\",                ObjectFunctions::GetObjectHittingRefNum},\\\n    {\"GetObjectHittingMpNum\",                 ObjectFunctions::GetObjectHittingMpNum},\\\n    {\"GetObjectHittingName\",                  ObjectFunctions::GetObjectHittingName},\\\n    \\\n    {\"GetObjectSummonState\",                  ObjectFunctions::GetObjectSummonState},\\\n    {\"GetObjectSummonEffectId\",               ObjectFunctions::GetObjectSummonEffectId},\\\n    {\"GetObjectSummonSpellId\",                ObjectFunctions::GetObjectSummonSpellId},\\\n    {\"GetObjectSummonDuration\",               ObjectFunctions::GetObjectSummonDuration},\\\n    {\"DoesObjectHavePlayerSummoner\",          ObjectFunctions::DoesObjectHavePlayerSummoner},\\\n    {\"GetObjectSummonerPid\",                  ObjectFunctions::GetObjectSummonerPid},\\\n    {\"GetObjectSummonerRefId\",                ObjectFunctions::GetObjectSummonerRefId},\\\n    {\"GetObjectSummonerRefNum\",               ObjectFunctions::GetObjectSummonerRefNum},\\\n    {\"GetObjectSummonerMpNum\",                ObjectFunctions::GetObjectSummonerMpNum},\\\n    \\\n    {\"GetObjectPosX\",                         ObjectFunctions::GetObjectPosX},\\\n    {\"GetObjectPosY\",                         ObjectFunctions::GetObjectPosY},\\\n    {\"GetObjectPosZ\",                         ObjectFunctions::GetObjectPosZ},\\\n    {\"GetObjectRotX\",                         ObjectFunctions::GetObjectRotX},\\\n    {\"GetObjectRotY\",                         ObjectFunctions::GetObjectRotY},\\\n    {\"GetObjectRotZ\",                         ObjectFunctions::GetObjectRotZ},\\\n    \\\n    {\"GetVideoFilename\",                      ObjectFunctions::GetVideoFilename},\\\n    \\\n    {\"GetClientLocalsSize\",                   ObjectFunctions::GetClientLocalsSize},\\\n    {\"GetClientLocalInternalIndex\",           ObjectFunctions::GetClientLocalInternalIndex},\\\n    {\"GetClientLocalVariableType\",            ObjectFunctions::GetClientLocalVariableType},\\\n    {\"GetClientLocalIntValue\",                ObjectFunctions::GetClientLocalIntValue},\\\n    {\"GetClientLocalFloatValue\",              ObjectFunctions::GetClientLocalFloatValue},\\\n    \\\n    {\"GetContainerChangesSize\",               ObjectFunctions::GetContainerChangesSize},\\\n    {\"GetContainerItemRefId\",                 ObjectFunctions::GetContainerItemRefId},\\\n    {\"GetContainerItemCount\",                 ObjectFunctions::GetContainerItemCount},\\\n    {\"GetContainerItemCharge\",                ObjectFunctions::GetContainerItemCharge},\\\n    {\"GetContainerItemEnchantmentCharge\",     ObjectFunctions::GetContainerItemEnchantmentCharge},\\\n    {\"GetContainerItemSoul\",                  ObjectFunctions::GetContainerItemSoul},\\\n    {\"GetContainerItemActionCount\",           ObjectFunctions::GetContainerItemActionCount},\\\n    \\\n    {\"DoesObjectHaveContainer\",               ObjectFunctions::DoesObjectHaveContainer},\\\n    {\"IsObjectDroppedByPlayer\",               ObjectFunctions::IsObjectDroppedByPlayer},\\\n    \\\n    {\"SetObjectListCell\",                     ObjectFunctions::SetObjectListCell},\\\n    {\"SetObjectListAction\",                   ObjectFunctions::SetObjectListAction},\\\n    {\"SetObjectListContainerSubAction\",       ObjectFunctions::SetObjectListContainerSubAction},\\\n    {\"SetObjectListConsoleCommand\",           ObjectFunctions::SetObjectListConsoleCommand},\\\n    \\\n    {\"SetObjectRefId\",                        ObjectFunctions::SetObjectRefId},\\\n    {\"SetObjectRefNum\",                       ObjectFunctions::SetObjectRefNum},\\\n    {\"SetObjectMpNum\",                        ObjectFunctions::SetObjectMpNum},\\\n    {\"SetObjectCount\",                        ObjectFunctions::SetObjectCount},\\\n    {\"SetObjectCharge\",                       ObjectFunctions::SetObjectCharge},\\\n    {\"SetObjectEnchantmentCharge\",            ObjectFunctions::SetObjectEnchantmentCharge},\\\n    {\"SetObjectSoul\",                         ObjectFunctions::SetObjectSoul},\\\n    {\"SetObjectGoldValue\",                    ObjectFunctions::SetObjectGoldValue},\\\n    {\"SetObjectScale\",                        ObjectFunctions::SetObjectScale},\\\n    {\"SetObjectState\",                        ObjectFunctions::SetObjectState},\\\n    {\"SetObjectLockLevel\",                    ObjectFunctions::SetObjectLockLevel},\\\n    {\"SetObjectDialogueChoiceType\",           ObjectFunctions::SetObjectDialogueChoiceType},\\\n    {\"SetObjectDialogueChoiceTopic\",          ObjectFunctions::SetObjectDialogueChoiceTopic},\\\n    {\"SetObjectGoldPool\",                     ObjectFunctions::SetObjectGoldPool},\\\n    {\"SetObjectLastGoldRestockHour\",          ObjectFunctions::SetObjectLastGoldRestockHour},\\\n    {\"SetObjectLastGoldRestockDay\",           ObjectFunctions::SetObjectLastGoldRestockDay},\\\n    {\"SetObjectDisarmState\",                  ObjectFunctions::SetObjectDisarmState},\\\n    {\"SetObjectDroppedByPlayerState\",         ObjectFunctions::SetObjectDroppedByPlayerState},\\\n    {\"SetObjectPosition\",                     ObjectFunctions::SetObjectPosition},\\\n    {\"SetObjectRotation\",                     ObjectFunctions::SetObjectRotation},\\\n    {\"SetObjectSound\",                        ObjectFunctions::SetObjectSound},\\\n    \\\n    {\"SetObjectSummonState\",                  ObjectFunctions::SetObjectSummonState},\\\n    {\"SetObjectSummonEffectId\",               ObjectFunctions::SetObjectSummonEffectId},\\\n    {\"SetObjectSummonSpellId\",                ObjectFunctions::SetObjectSummonSpellId},\\\n    {\"SetObjectSummonDuration\",               ObjectFunctions::SetObjectSummonDuration},\\\n    {\"SetObjectSummonerPid\",                  ObjectFunctions::SetObjectSummonerPid},\\\n    {\"SetObjectSummonerRefNum\",               ObjectFunctions::SetObjectSummonerRefNum},\\\n    {\"SetObjectSummonerMpNum\",                ObjectFunctions::SetObjectSummonerMpNum},\\\n    \\\n    {\"SetObjectActivatingPid\",                ObjectFunctions::SetObjectActivatingPid},\\\n    \\\n    {\"SetObjectDoorState\",                    ObjectFunctions::SetObjectDoorState},\\\n    {\"SetObjectDoorTeleportState\",            ObjectFunctions::SetObjectDoorTeleportState},\\\n    {\"SetObjectDoorDestinationCell\",          ObjectFunctions::SetObjectDoorDestinationCell},\\\n    {\"SetObjectDoorDestinationPosition\",      ObjectFunctions::SetObjectDoorDestinationPosition},\\\n    {\"SetObjectDoorDestinationRotation\",      ObjectFunctions::SetObjectDoorDestinationRotation},\\\n    \\\n    {\"SetPlayerAsObject\",                     ObjectFunctions::SetPlayerAsObject},\\\n    \\\n    {\"SetContainerItemRefId\",                 ObjectFunctions::SetContainerItemRefId},\\\n    {\"SetContainerItemCount\",                 ObjectFunctions::SetContainerItemCount},\\\n    {\"SetContainerItemCharge\",                ObjectFunctions::SetContainerItemCharge},\\\n    {\"SetContainerItemEnchantmentCharge\",     ObjectFunctions::SetContainerItemEnchantmentCharge},\\\n    {\"SetContainerItemSoul\",                  ObjectFunctions::SetContainerItemSoul},\\\n    \\\n    {\"SetContainerItemActionCountByIndex\",    ObjectFunctions::SetContainerItemActionCountByIndex},\\\n    \\\n    {\"AddObject\",                             ObjectFunctions::AddObject},\\\n    {\"AddClientLocalInteger\",                 ObjectFunctions::AddClientLocalInteger},\\\n    {\"AddClientLocalFloat\",                   ObjectFunctions::AddClientLocalFloat},\\\n    {\"AddContainerItem\",                      ObjectFunctions::AddContainerItem},\\\n    \\\n    {\"SendObjectActivate\",                    ObjectFunctions::SendObjectActivate},\\\n    {\"SendObjectPlace\",                       ObjectFunctions::SendObjectPlace},\\\n    {\"SendObjectSpawn\",                       ObjectFunctions::SendObjectSpawn},\\\n    {\"SendObjectDelete\",                      ObjectFunctions::SendObjectDelete},\\\n    {\"SendObjectLock\",                        ObjectFunctions::SendObjectLock},\\\n    {\"SendObjectDialogueChoice\",              ObjectFunctions::SendObjectDialogueChoice},\\\n    {\"SendObjectMiscellaneous\",               ObjectFunctions::SendObjectMiscellaneous},\\\n    {\"SendObjectRestock\",                     ObjectFunctions::SendObjectRestock},\\\n    {\"SendObjectTrap\",                        ObjectFunctions::SendObjectTrap},\\\n    {\"SendObjectScale\",                       ObjectFunctions::SendObjectScale},\\\n    {\"SendObjectSound\",                       ObjectFunctions::SendObjectSound},\\\n    {\"SendObjectState\",                       ObjectFunctions::SendObjectState},\\\n    {\"SendObjectMove\",                        ObjectFunctions::SendObjectMove},\\\n    {\"SendObjectRotate\",                      ObjectFunctions::SendObjectRotate},\\\n    {\"SendDoorState\",                         ObjectFunctions::SendDoorState},\\\n    {\"SendDoorDestination\",                   ObjectFunctions::SendDoorDestination},\\\n    {\"SendContainer\",                         ObjectFunctions::SendContainer},\\\n    {\"SendVideoPlay\",                         ObjectFunctions::SendVideoPlay},\\\n    {\"SendClientScriptLocal\",                 ObjectFunctions::SendClientScriptLocal},\\\n    {\"SendConsoleCommand\",                    ObjectFunctions::SendConsoleCommand},\\\n    \\\n    {\"ReadLastObjectList\",                    ObjectFunctions::ReadLastObjectList},\\\n    {\"ReadLastEvent\",                         ObjectFunctions::ReadLastEvent},\\\n    {\"InitializeObjectList\",                  ObjectFunctions::InitializeObjectList},\\\n    {\"InitializeEvent\",                       ObjectFunctions::InitializeEvent},\\\n    {\"CopyLastObjectListToStore\",             ObjectFunctions::CopyLastObjectListToStore},\\\n    {\"GetObjectChangesSize\",                  ObjectFunctions::GetObjectChangesSize},\\\n    {\"GetEventAction\",                        ObjectFunctions::GetEventAction},\\\n    {\"GetEventContainerSubAction\",            ObjectFunctions::GetEventContainerSubAction},\\\n    {\"GetObjectRefNumIndex\",                  ObjectFunctions::GetObjectRefNumIndex},\\\n    {\"GetObjectSummonerRefNumIndex\",          ObjectFunctions::GetObjectSummonerRefNumIndex},\\\n    {\"SetEventCell\",                          ObjectFunctions::SetEventCell},\\\n    {\"SetEventAction\",                        ObjectFunctions::SetEventAction},\\\n    {\"SetEventConsoleCommand\",                ObjectFunctions::SetEventConsoleCommand},\\\n    {\"SetObjectRefNumIndex\",                  ObjectFunctions::SetObjectRefNumIndex},\\\n    {\"AddWorldObject\",                        ObjectFunctions::AddWorldObject}\n\nclass ObjectFunctions\n{\npublic:\n\n    /**\n    * \\brief Use the last object list received by the server as the one being read.\n    *\n    * \\return void\n    */\n    static void ReadReceivedObjectList() noexcept;\n\n    /**\n    * \\brief Clear the data from the object list stored on the server.\n    *\n    * \\return void\n    */\n    static void ClearObjectList() noexcept;\n\n    /**\n    * \\brief Set the pid attached to the ObjectList.\n    *\n    * \\param pid The player ID to whom the object list should be attached.\n    * \\return void\n    */\n    static void SetObjectListPid(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Take the contents of the read-only object list last received by the\n    *        server from a player and move its contents to the stored object list\n    *        that can be sent by the server.\n    *\n    * \\return void\n    */\n    static void CopyReceivedObjectListToStore() noexcept;\n\n    /**\n    * \\brief Get the number of indexes in the read object list.\n    *\n    * \\return The number of indexes.\n    */\n    static unsigned int GetObjectListSize() noexcept;\n\n    /**\n    * \\brief Get the origin of the read object list.\n    *\n    * \\return The origin (0 for CLIENT_GAMEPLAY, 1 for CLIENT_CONSOLE, 2 for\n    * CLIENT_DIALOGUE, 3 for CLIENT_SCRIPT_LOCAL, 4 for CLIENT_SCRIPT_GLOBAL,\n    * 5 for SERVER_SCRIPT).\n    */\n    static unsigned char GetObjectListOrigin() noexcept;\n\n    /**\n    * \\brief Get the client script that the read object list originated from.\n    *\n    * \\return The ID of the client script.\n    */\n    static const char *GetObjectListClientScript() noexcept;\n\n    /**\n    * \\brief Get the action type used in the read object list.\n    *\n    * \\return The action type (0 for SET, 1 for ADD, 2 for REMOVE, 3 for REQUEST).\n    */\n    static unsigned char GetObjectListAction() noexcept;\n\n    /**\n    * \\brief Get the console command used in the read object list.\n    *\n    * \\return The console command.\n    */\n    static const char *GetObjectListConsoleCommand() noexcept;\n\n    /**\n    * \\brief Get the container subaction type used in the read object list.\n    *\n    * \\return The action type (0 for NONE, 1 for DRAG, 2 for DROP, 3 for TAKE_ALL).\n    */\n    static unsigned char GetObjectListContainerSubAction() noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list is a\n    * player.\n    *\n    * Note: Although most player data and events are dealt with in Player packets,\n    *       object activation is general enough for players themselves to be included\n    *       as objects in ObjectActivate packets.\n    *\n    * \\param index The index of the object.\n    * \\return Whether the object is a player.\n    */\n    static bool IsObjectPlayer(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the player ID of the object at a certain index in the read object list,\n    * only valid if the object is a player.\n    *\n    * Note: Currently, players can only be objects in ObjectActivate and ConsoleCommand\n    *       packets.\n    *\n    * \\param index The index of the object.\n    * \\return The player ID of the object.\n    */\n    static int GetObjectPid(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refId of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The refId.\n    */\n    static const char *GetObjectRefId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refNum of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The refNum.\n    */\n    static unsigned int GetObjectRefNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The mpNum.\n    */\n    static unsigned int GetObjectMpNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the count of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The object count.\n    */\n    static int GetObjectCount(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the charge of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The charge.\n    */\n    static int GetObjectCharge(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the enchantment charge of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The enchantment charge.\n    */\n    static double GetObjectEnchantmentCharge(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the soul of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The soul.\n    */\n    static const char *GetObjectSoul(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the gold value of the object at a certain index in the read object list.\n    *\n    * This is used solely to get the gold value of gold. It is not used for other objects.\n    *\n    * \\param index The index of the object.\n    * \\return The gold value.\n    */\n    static int GetObjectGoldValue(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the object scale of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The object scale.\n    */\n    static double GetObjectScale(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the object sound ID of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The object sound ID.\n    */\n    static const char *GetObjectSoundId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the object state of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The object state.\n    */\n    static bool GetObjectState(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the door state of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The door state.\n    */\n    static int GetObjectDoorState(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the lock level of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The lock level.\n    */\n    static int GetObjectLockLevel(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the dialogue choice type for the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The dialogue choice type.\n    */\n    static unsigned int GetObjectDialogueChoiceType(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the dialogue choice topic for the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The dialogue choice topic.\n    */\n    static const char *GetObjectDialogueChoiceTopic(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the gold pool of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The gold pool.\n    */\n    static unsigned int GetObjectGoldPool(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the hour of the last gold restock of the object at a certain index in the\n    * read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The hour of the last gold restock.\n    */\n    static double GetObjectLastGoldRestockHour(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the day of the last gold restock of the object at a certain index in the\n    * read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The day of the last gold restock.\n    */\n    static int GetObjectLastGoldRestockDay(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list has been\n    * activated by a player.\n    *\n    * \\param index The index of the object.\n    * \\return Whether the object has been activated by a player.\n    */\n    static bool DoesObjectHavePlayerActivating(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the player ID of the player activating the object at a certain index in the\n    * read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The player ID of the activating player.\n    */\n    static int GetObjectActivatingPid(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refId of the actor activating the object at a certain index in the read\n    * object list.\n    *\n    * \\param index The index of the object.\n    * \\return The refId of the activating actor.\n    */\n    static const char *GetObjectActivatingRefId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refNum of the actor activating the object at a certain index in the read\n    * object list.\n    *\n    * \\param index The index of the object.\n    * \\return The refNum of the activating actor.\n    */\n    static unsigned int GetObjectActivatingRefNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the actor activating the object at a certain index in the read\n    * object list.\n    *\n    * \\param index The index of the object.\n    * \\return The mpNum of the activating actor.\n    */\n    static unsigned int GetObjectActivatingMpNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the name of the actor activating the object at a certain index in the read\n    * object list.\n    *\n    * \\param index The index of the object.\n    * \\return The name of the activating actor.\n    */\n    static const char *GetObjectActivatingName(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list has been\n    *        hit successfully.\n    *\n    * \\param index The index of the object.\n    * \\return The success state.\n    */\n    static bool GetObjectHitSuccess(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the damage caused to the object at a certain index in the read object list\n    *        in a hit.\n    *\n    * \\param index The index of the object.\n    * \\return The damage.\n    */\n    static double GetObjectHitDamage(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list has\n    *        blocked the hit on it.\n    *\n    * \\param index The index of the object.\n    * \\return The block state.\n    */\n    static bool GetObjectHitBlock(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list has been\n    *        knocked down.\n    *\n    * \\param index The index of the object.\n    * \\return The knockdown state.\n    */\n    static bool GetObjectHitKnockdown(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list has been\n    * hit by a player.\n    *\n    * \\param index The index of the object.\n    * \\return Whether the object has been hit by a player.\n    */\n    static bool DoesObjectHavePlayerHitting(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the player ID of the player hitting the object at a certain index in the\n    * read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The player ID of the hitting player.\n    */\n    static int GetObjectHittingPid(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refId of the actor hitting the object at a certain index in the read\n    * object list.\n    *\n    * \\param index The index of the object.\n    * \\return The refId of the hitting actor.\n    */\n    static const char *GetObjectHittingRefId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refNum of the actor hitting the object at a certain index in the read\n    * object list.\n    *\n    * \\param index The index of the object.\n    * \\return The refNum of the hitting actor.\n    */\n    static unsigned int GetObjectHittingRefNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the actor hitting the object at a certain index in the read\n    * object list.\n    *\n    * \\param index The index of the object.\n    * \\return The mpNum of the hitting actor.\n    */\n    static unsigned int GetObjectHittingMpNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the name of the actor hitting the object at a certain index in the read\n    * object list.\n    *\n    * \\param index The index of the object.\n    * \\return The name of the hitting actor.\n    */\n    static const char *GetObjectHittingName(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list is a\n    * summon.\n    *\n    * Only living actors can be summoned.\n    *\n    * \\return The summon state.\n    */\n    static bool GetObjectSummonState(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the summon effect ID of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The summon effect ID.\n    */\n    static double GetObjectSummonEffectId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the summon spell ID of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The summon spell ID.\n    */\n    static const char *GetObjectSummonSpellId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the summon duration of the object at a certain index in the read object list.\n    *\n    * Note: Returns -1 if indefinite.\n    *\n    * \\param index The index of the object.\n    * \\return The summon duration.\n    */\n    static double GetObjectSummonDuration(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list has a player\n    * as its summoner.\n    *\n    * Only living actors can be summoned.\n    *\n    * \\param index The index of the object.\n    * \\return Whether a player is the summoner of the object.\n    */\n    static bool DoesObjectHavePlayerSummoner(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the player ID of the summoner of the object at a certain index in the read object\n    * list.\n    *\n    * \\param index The index of the object.\n    * \\return The player ID of the summoner.\n    */\n    static int GetObjectSummonerPid(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refId of the actor summoner of the object at a certain index in the read object\n    * list.\n    *\n    * \\param index The index of the object.\n    * \\return The refId of the summoner.\n    */\n    static const char *GetObjectSummonerRefId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refNum of the actor summoner of the object at a certain index in the read object\n    * list.\n    *\n    * \\param index The index of the object.\n    * \\return The refNum of the summoner.\n    */\n    static unsigned int GetObjectSummonerRefNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the actor summoner of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The mpNum of the summoner.\n    */\n    static unsigned int GetObjectSummonerMpNum(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the X position of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The X position.\n    */\n    static double GetObjectPosX(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Y position of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The Y position.\n    */\n    static double GetObjectPosY(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Z position at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The Z position.\n    */\n    static double GetObjectPosZ(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the X rotation of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The X rotation.\n    */\n    static double GetObjectRotX(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Y rotation of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The Y rotation.\n    */\n    static double GetObjectRotY(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Z rotation of the object at a certain index in the read object list.\n    *\n    * \\param index The index of the object.\n    * \\return The Z rotation.\n    */\n    static double GetObjectRotZ(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the videoFilename of the object at a certain index in the read object list.\n    *\n    * \\return The videoFilename.\n    */\n    static const char *GetVideoFilename(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the number of client local variables of the object at a certain index in the\n    * read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\return The number of client local variables.\n    */\n    static unsigned int GetClientLocalsSize(unsigned int objectIndex) noexcept;\n\n    /**\n    * \\brief Get the internal script index of the client local variable at a certain variableIndex in\n    * the client locals of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param variableIndex The index of the client local.\n    * \\return The internal script index.\n    */\n    static unsigned int GetClientLocalInternalIndex(unsigned int objectIndex, unsigned int variableIndex) noexcept;\n\n    /**\n    * \\brief Get the type of the client local variable at a certain variableIndex in the client locals\n    * of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param variableIndex The index of the client local.\n    * \\return The variable type (0 for INTEGER, 1 for LONG, 2 for FLOAT).\n    */\n    static unsigned short GetClientLocalVariableType(unsigned int objectIndex, unsigned int variableIndex) noexcept;\n\n    /**\n    * \\brief Get the integer value of the client local variable at a certain variableIndex in the client\n    * locals of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param variableIndex The index of the client local.\n    * \\return The integer value.\n    */\n    static int GetClientLocalIntValue(unsigned int objectIndex, unsigned int variableIndex) noexcept;\n\n    /**\n    * \\brief Get the float value of the client local variable at a certain variableIndex in the client\n    * locals of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param variableIndex The index of the client local.\n    * \\return The float value.\n    */\n    static double GetClientLocalFloatValue(unsigned int objectIndex, unsigned int variableIndex) noexcept;\n\n    /**\n    * \\brief Get the number of container item indexes of the object at a certain index in the\n    * read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\return The number of container item indexes.\n    */\n    static unsigned int GetContainerChangesSize(unsigned int objectIndex) noexcept;\n\n    /**\n    * \\brief Get the refId of the container item at a certain itemIndex in the container changes\n    * of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param itemIndex The index of the container item.\n    * \\return The refId.\n    */\n    static const char *GetContainerItemRefId(unsigned int objectIndex, unsigned int itemIndex) noexcept;\n\n    /**\n    * \\brief Get the item count of the container item at a certain itemIndex in the container\n    * changes of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param itemIndex The index of the container item.\n    * \\return The item count.\n    */\n    static int GetContainerItemCount(unsigned int objectIndex, unsigned int itemIndex) noexcept;\n\n    /**\n    * \\brief Get the charge of the container item at a certain itemIndex in the container changes\n    * of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param itemIndex The index of the container item.\n    * \\return The charge.\n    */\n    static int GetContainerItemCharge(unsigned int objectIndex, unsigned int itemIndex) noexcept;\n\n    /**\n    * \\brief Get the enchantment charge of the container item at a certain itemIndex in the container changes\n    * of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param itemIndex The index of the container item.\n    * \\return The enchantment charge.\n    */\n    static double GetContainerItemEnchantmentCharge(unsigned int objectIndex, unsigned int itemIndex) noexcept;\n\n    /**\n    * \\brief Get the soul of the container item at a certain itemIndex in the container changes\n    * of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param itemIndex The index of the container item.\n    * \\return The soul.\n    */\n    static const char *GetContainerItemSoul(unsigned int objectIndex, unsigned int itemIndex) noexcept;\n\n    /**\n    * \\brief Get the action count of the container item at a certain itemIndex in the container\n    * changes of the object at a certain objectIndex in the read object list.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param itemIndex The index of the container item.\n    * \\return The action count.\n    */\n    static int GetContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list has a container.\n    * \n    * Note: Only ObjectLists from ObjectPlace packets contain this information. Objects from\n    *       received ObjectSpawn packets can always be assumed to have a container.\n    *\n    * \\param index The index of the object.\n    * \\return Whether the object has a container.\n    */\n    static bool DoesObjectHaveContainer(unsigned int index) noexcept;\n\n    /**\n    * \\brief Check whether the object at a certain index in the read object list has been\n    *        dropped by a player.\n    *\n    * Note: Only ObjectLists from ObjectPlace packets contain this information.\n    *\n    * \\param index The index of the object.\n    * \\return Whether the object has been dropped by a player.\n    */\n    static bool IsObjectDroppedByPlayer(unsigned int index) noexcept;\n\n    /**\n    * \\brief Set the cell of the temporary object list stored on the server.\n    *\n    * The cell is determined to be an exterior cell if it fits the pattern of a number followed\n    * by a comma followed by another number.\n    *\n    * \\param cellDescription The description of the cell.\n    * \\return void\n    */\n    static void SetObjectListCell(const char* cellDescription) noexcept;\n\n    /**\n    * \\brief Set the action type of the temporary object list stored on the server.\n    *\n    * \\param action The action type (0 for SET, 1 for ADD, 2 for REMOVE, 3 for REQUEST).\n    * \\return void\n    */\n    static void SetObjectListAction(unsigned char action) noexcept;\n\n    /**\n    * \\brief Set the container subaction type of the temporary object list stored on the server.\n    *\n    * \\param subAction The action type (0 for NONE, 1 for DRAG, 2 for DROP, 3 for TAKE_ALL,\n    *                  4 for REPLY_TO_REQUEST, 5 for RESTOCK_RESULT).\n    * \\return void\n    */\n    static void SetObjectListContainerSubAction(unsigned char subAction) noexcept;\n\n    /**\n    * \\brief Set the console command of the temporary object list stored on the server.\n    *\n    * When sent, the command will run once on every object added to the object list. If no objects\n    * have been added, it will run once without any object reference.\n    *\n    * \\param consoleCommand The console command.\n    * \\return void\n    */\n    static void SetObjectListConsoleCommand(const char* consoleCommand) noexcept;\n\n    /**\n    * \\brief Set the refId of the temporary object stored on the server.\n    *\n    * \\param refId The refId.\n    * \\return void\n    */\n    static void SetObjectRefId(const char* refId) noexcept;\n\n    /**\n    * \\brief Set the refNum of the temporary object stored on the server.\n    *\n    * Every object loaded from .ESM and .ESP data files has a unique refNum which needs to be\n    * retained to refer to it in packets.\n    * \n    * On the other hand, objects placed or spawned via the server should always have a refNum\n    * of 0.\n    *\n    * \\param refNum The refNum.\n    * \\return void\n    */\n    static void SetObjectRefNum(int refNum) noexcept;\n\n    /**\n    * \\brief Set the mpNum of the temporary object stored on the server.\n    *\n    * Every object placed or spawned via the server is assigned an mpNum by incrementing the last\n    * mpNum stored on the server. Scripts should take care to ensure that mpNums are kept unique\n    * for these objects.\n    * \n    * Objects loaded from .ESM and .ESP data files should always have an mpNum of 0, because they\n    * have unique refNumes instead.\n    *\n    * \\param mpNum The mpNum.\n    * \\return void\n    */\n    static void SetObjectMpNum(int mpNum) noexcept;\n\n    /**\n    * \\brief Set the object count of the temporary object stored on the server.\n    *\n    * This determines the quantity of an object, with the exception of gold.\n    *\n    * \\param count The object count.\n    * \\return void\n    */\n    static void SetObjectCount(int count) noexcept;\n\n    /**\n    * \\brief Set the charge of the temporary object stored on the server.\n    *\n    * Object durabilities are set through this value.\n    *\n    * \\param charge The charge.\n    * \\return void\n    */\n    static void SetObjectCharge(int charge) noexcept;\n\n    /**\n    * \\brief Set the enchantment charge of the temporary object stored on the server.\n    *\n    * Object durabilities are set through this value.\n    *\n    * \\param enchantmentCharge The enchantment charge.\n    * \\return void\n    */\n    static void SetObjectEnchantmentCharge(double enchantmentCharge) noexcept;\n\n    /**\n    * \\brief Set the soul of the temporary object stored on the server.\n    *\n    * \\param soul The ID of the soul.\n    * \\return void\n    */\n    static void SetObjectSoul(const char* soul) noexcept;\n\n    /**\n    * \\brief Set the gold value of the temporary object stored on the server.\n    *\n    * This is used solely to set the gold value for gold. It has no effect on other objects.\n    *\n    * \\param goldValue The gold value.\n    * \\return void\n    */\n    static void SetObjectGoldValue(int goldValue) noexcept;\n\n    /**\n    * \\brief Set the scale of the temporary object stored on the server.\n    *\n    * Objects are smaller or larger than their default size based on their scale.\n    *\n    * \\param scale The scale.\n    * \\return void\n    */\n    static void SetObjectScale(double scale) noexcept;\n\n    /**\n    * \\brief Set the object state of the temporary object stored on the server.\n    *\n    * Objects are enabled or disabled based on their object state.\n    *\n    * \\param objectState The object state.\n    * \\return void\n    */\n    static void SetObjectState(bool objectState) noexcept;\n\n    /**\n    * \\brief Set the lock level of the temporary object stored on the server.\n    *\n    * \\param lockLevel The lock level.\n    * \\return void\n    */\n    static void SetObjectLockLevel(int lockLevel) noexcept;\n\n    /**\n    * \\brief Set the dialogue choice type of the temporary object stored on the server.\n    *\n    * \\param dialogueChoiceType The dialogue choice type.\n    * \\return void\n    */\n    static void SetObjectDialogueChoiceType(unsigned int dialogueChoiceType) noexcept;\n\n    /**\n    * \\brief Set the dialogue choice topic for the temporary object stored on the server.\n    *\n    * \\param topic The dialogue choice topic.\n    * \\return void\n    */\n    static void SetObjectDialogueChoiceTopic(const char* topic) noexcept;\n\n    /**\n    * \\brief Set the gold pool of the temporary object stored on the server.\n    *\n    * \\param goldPool The gold pool.\n    * \\return void\n    */\n    static void SetObjectGoldPool(unsigned int goldPool) noexcept;\n\n    /**\n    * \\brief Set the hour of the last gold restock of the temporary object stored on the server.\n    *\n    * \\param hour The hour of the last gold restock.\n    * \\return void\n    */\n    static void SetObjectLastGoldRestockHour(double hour) noexcept;\n\n    /**\n    * \\brief Set the day of the last gold restock of the temporary object stored on the server.\n    *\n    * \\param day The day of the last gold restock.\n    * \\return void\n    */\n    static void SetObjectLastGoldRestockDay(int day) noexcept;\n\n    /**\n    * \\brief Set the disarm state of the temporary object stored on the server.\n    *\n    * \\param disarmState The disarmState.\n    * \\return void\n    */\n    static void SetObjectDisarmState(bool disarmState) noexcept;\n\n    /**\n    * \\brief Set the droppedByPlayer state of the temporary object stored on the server.\n    *\n    * \\param dropedByPlayerState Whether the object has been dropped by a player or not.\n    * \\return void\n    */\n    static void SetObjectDroppedByPlayerState(bool dropedByPlayerState) noexcept;\n\n    /**\n    * \\brief Set the position of the temporary object stored on the server.\n    *\n    * \\param x The X position.\n    * \\param y The Y position.\n    * \\param z The Z position.\n    * \\return void\n    */\n    static void SetObjectPosition(double x, double y, double z) noexcept;\n\n    /**\n    * \\brief Set the rotation of the temporary object stored on the server.\n    *\n    * \\param x The X rotation.\n    * \\param y The Y rotation.\n    * \\param z The Z rotation.\n    * \\return void\n    */\n    static void SetObjectRotation(double x, double y, double z) noexcept;\n\n    static void SetObjectSound(const char* soundId, double volume, double pitch) noexcept;\n\n    /**\n    * \\brief Set the summon state of the temporary object stored on the server.\n    *\n    * This only affects living actors and determines whether they are summons of another\n    * living actor.\n    *\n    * \\param summonState The summon state.\n    * \\return void\n    */\n    static void SetObjectSummonState(bool summonState) noexcept;\n\n    /**\n    * \\brief Set the summon effect ID of the temporary object stored on the server.\n    *\n    * \\param summonEffectId The summon effect ID.\n    * \\return void\n    */\n    static void SetObjectSummonEffectId(int summonEffectId) noexcept;\n\n    /**\n    * \\brief Set the summon spell ID of the temporary object stored on the server.\n    *\n    * \\param summonSpellId The summon spell ID.\n    * \\return void\n    */\n    static void SetObjectSummonSpellId(const char* summonSpellId) noexcept;\n\n    /**\n    * \\brief Set the summon duration of the temporary object stored on the server.\n    *\n    * \\param summonDuration The summon duration.\n    * \\return void\n    */\n    static void SetObjectSummonDuration(double summonDuration) noexcept;\n\n    /**\n    * \\brief Set the player ID of the summoner of the temporary object stored on the server.\n    *\n    * \\param pid The player ID of the summoner.\n    * \\return void\n    */\n    static void SetObjectSummonerPid(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the refNum of the actor summoner of the temporary object stored on the server.\n    *\n    * \\param refNum The refNum of the summoner.\n    * \\return void\n    */\n    static void SetObjectSummonerRefNum(int refNum) noexcept;\n\n    /**\n    * \\brief Set the mpNum of the actor summoner of the temporary object stored on the server.\n    *\n    * \\param mpNum The mpNum of the summoner.\n    * \\return void\n    */\n    static void SetObjectSummonerMpNum(int mpNum) noexcept;\n\n    /**\n    * \\brief Set the player ID of the player activating the temporary object stored on the\n    *        server. Currently only used for ObjectActivate packets.\n    *\n    * \\param pid The pid of the player.\n    * \\return void\n    */\n    static void SetObjectActivatingPid(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the door state of the temporary object stored on the server.\n    *\n    * Doors are open or closed based on their door state.\n    *\n    * \\param doorState The door state.\n    * \\return void\n    */\n    static void SetObjectDoorState(int doorState) noexcept;\n\n    /**\n    * \\brief Set the teleport state of the temporary object stored on the server.\n    *\n    * If a door's teleport state is true, interacting with the door teleports a player to its\n    * destination. If it's false, it opens and closes like a regular door.\n    *\n    * \\param teleportState The teleport state.\n    * \\return void\n    */\n    static void SetObjectDoorTeleportState(bool teleportState) noexcept;\n\n    /**\n    * \\brief Set the door destination cell of the temporary object stored on the server.\n    *\n    * The cell is determined to be an exterior cell if it fits the pattern of a number followed\n    * by a comma followed by another number.\n    *\n    * \\param cellDescription The description of the cell.\n    * \\return void\n    */\n    static void SetObjectDoorDestinationCell(const char* cellDescription) noexcept;\n\n    /**\n    * \\brief Set the door destination position of the temporary object stored on the server.\n    *\n    * \\param x The X position.\n    * \\param y The Y position.\n    * \\param z The Z position.\n    * \\return void\n    */\n    static void SetObjectDoorDestinationPosition(double x, double y, double z) noexcept;\n\n    /**\n    * \\brief Set the door destination rotation of the temporary object stored on the server.\n    *\n    * Note: Because this sets the rotation a player will have upon using the door, and rotation\n    *       on the Y axis has no effect on players, the Y value has been omitted as an argument.\n    *\n    * \\param x The X rotation.\n    * \\param z The Z rotation.\n    * \\return void\n    */\n    static void SetObjectDoorDestinationRotation(double x, double z) noexcept;\n\n    /**\n    * \\brief Set a player as the object in the temporary object stored on the server.\n    *        Currently only used for ConsoleCommand packets.\n    *\n    * \\param pid The pid of the player.\n    * \\return void\n    */\n    static void SetPlayerAsObject(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the refId of the temporary container item stored on the server.\n    *\n    * \\param refId The refId.\n    * \\return void\n    */\n    static void SetContainerItemRefId(const char* refId) noexcept;\n\n    /**\n    * \\brief Set the item count of the temporary container item stored on the server.\n    *\n    * \\param count The item count.\n    * \\return void\n    */\n    static void SetContainerItemCount(int count) noexcept;\n\n    /**\n    * \\brief Set the charge of the temporary container item stored on the server.\n    *\n    * \\param charge The charge.\n    * \\return void\n    */\n    static void SetContainerItemCharge(int charge) noexcept;\n\n    /**\n    * \\brief Set the enchantment charge of the temporary container item stored on the server.\n    *\n    * \\param enchantmentCharge The enchantment charge.\n    * \\return void\n    */\n    static void SetContainerItemEnchantmentCharge(double enchantmentCharge) noexcept;\n\n    /**\n    * \\brief Set the soul of the temporary container item stored on the server.\n    *\n    * \\param soul The soul.\n    * \\return void\n    */\n    static void SetContainerItemSoul(const char* soul) noexcept;\n\n    /**\n    * \\brief Set the action count of the container item at a certain itemIndex in the container\n    * changes of the object at a certain objectIndex in the object list stored on the server.\n    *\n    * When resending a received Container packet, this allows you to correct the amount of items\n    * removed from a container by a player when it conflicts with what other players have already\n    * taken.\n    *\n    * \\param objectIndex The index of the object.\n    * \\param itemIndex The index of the container item.\n    * \\param actionCount The action count.\n    * \\return void\n    */\n    static void SetContainerItemActionCountByIndex(unsigned int objectIndex, unsigned int itemIndex, int actionCount) noexcept;\n\n    /**\n    * \\brief Add a copy of the server's temporary object to the server's currently stored object\n    * list.\n    *\n    * In the process, the server's temporary object will automatically be cleared so a new\n    * one can be set up.\n    *\n    * \\return void\n    */\n    static void AddObject() noexcept;\n\n    /**\n    * \\brief Add a client local variable with an integer value to the client locals of the server's\n    * temporary object.\n    *\n    * \\param internalIndex The internal script index of the client local.\n    * \\param variableType The variable type (0 for SHORT, 1 for LONG).\n    * \\param intValue The integer value of the client local.\n    * \\return void\n    */\n    static void AddClientLocalInteger(int internalIndex, int intValue, unsigned int variableType) noexcept;\n\n    /**\n    * \\brief Add a client local variable with a float value to the client locals of the server's\n    * temporary object.\n    *\n    * \\param internalIndex The internal script index of the client local.\n    * \\param floatValue The float value of the client local.\n    * \\return void\n    */\n    static void AddClientLocalFloat(int internalIndex, double floatValue) noexcept;\n\n    /**\n    * \\brief Add a copy of the server's temporary container item to the container changes of the\n    * server's temporary object.\n    *\n    * In the process, the server's temporary container item will automatically be cleared so a new\n    * one can be set up.\n    *\n    * \\return void\n    */\n    static void AddContainerItem() noexcept;\n\n    /**\n    * \\brief Send an ObjectActivate packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectActivate(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectPlace packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectPlace(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectSpawn packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectSpawn(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectDelete packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectDelete(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectLock packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectLock(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectDialogueChoice packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectDialogueChoice(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectMiscellaneous packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectMiscellaneous(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectRestock packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectRestock(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectTrap packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectTrap(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectScale packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectScale(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectSound packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectSound(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectState packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectState(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectMove packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectMove(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send an ObjectRotate packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendObjectRotate(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a DoorState packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendDoorState(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a DoorDestination packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendDoorDestination(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a Container packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendContainer(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a VideoPlay packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendVideoPlay(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a ClientScriptLocal packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendClientScriptLocal(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a ConsoleCommand packet.\n    *\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendConsoleCommand(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n\n    // All methods below are deprecated versions of methods from above\n\n    static void ReadLastObjectList() noexcept;\n    static void ReadLastEvent() noexcept;\n    static void InitializeObjectList(unsigned short pid) noexcept;\n    static void InitializeEvent(unsigned short pid) noexcept;\n    static void CopyLastObjectListToStore() noexcept;\n    static unsigned int GetObjectChangesSize() noexcept;\n    static unsigned char GetEventAction() noexcept;\n    static unsigned char GetEventContainerSubAction() noexcept;\n    static unsigned int GetObjectRefNumIndex(unsigned int index) noexcept;\n    static unsigned int GetObjectSummonerRefNumIndex(unsigned int index) noexcept;\n    static void SetEventCell(const char* cellDescription) noexcept;\n    static void SetEventAction(unsigned char action) noexcept;\n    static void SetEventConsoleCommand(const char* consoleCommand) noexcept;\n    static void SetObjectRefNumIndex(int refNum) noexcept;\n    static void AddWorldObject() noexcept;\n\n};\n\n\n#endif //OPENMW_OBJECTAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Positions.cpp",
    "content": "#include \"Positions.hpp\"\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <apps/openmw-mp/Player.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\n#include <iostream>\n\ndouble PositionFunctions::GetPosX(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->position.pos[0];\n}\n\ndouble PositionFunctions::GetPosY(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->position.pos[1];\n}\n\ndouble PositionFunctions::GetPosZ(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->position.pos[2];\n}\n\ndouble PositionFunctions::GetPreviousCellPosX(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->previousCellPosition.pos[0];\n}\n\ndouble PositionFunctions::GetPreviousCellPosY(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->previousCellPosition.pos[1];\n}\n\ndouble PositionFunctions::GetPreviousCellPosZ(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->previousCellPosition.pos[2];\n}\n\ndouble PositionFunctions::GetRotX(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->position.rot[0];\n}\n\ndouble PositionFunctions::GetRotZ(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->position.rot[2];\n}\n\nvoid PositionFunctions::SetPos(unsigned short pid, double x, double y, double z) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->position.pos[0] = x;\n    player->position.pos[1] = y;\n    player->position.pos[2] = z;\n}\n\nvoid PositionFunctions::SetRot(unsigned short pid, double x, double z) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->position.rot[0] = x;\n    player->position.rot[2] = z;\n}\n\nvoid PositionFunctions::SetMomentum(unsigned short pid, double x, double y, double z) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->momentum.pos[0] = x;\n    player->momentum.pos[1] = y;\n    player->momentum.pos[2] = z;\n}\n\nvoid PositionFunctions::SendPos(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_POSITION);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n}\n\nvoid PositionFunctions::SendMomentum(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_MOMENTUM);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Positions.hpp",
    "content": "#ifndef OPENMW_POSITIONAPI_HPP\n#define OPENMW_POSITIONAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define POSITIONAPI \\\n    {\"GetPosX\",             PositionFunctions::GetPosX},\\\n    {\"GetPosY\",             PositionFunctions::GetPosY},\\\n    {\"GetPosZ\",             PositionFunctions::GetPosZ},\\\n    \\\n    {\"GetPreviousCellPosX\", PositionFunctions::GetPreviousCellPosX},\\\n    {\"GetPreviousCellPosY\", PositionFunctions::GetPreviousCellPosY},\\\n    {\"GetPreviousCellPosZ\", PositionFunctions::GetPreviousCellPosZ},\\\n    \\\n    {\"GetRotX\",             PositionFunctions::GetRotX},\\\n    {\"GetRotZ\",             PositionFunctions::GetRotZ},\\\n    \\\n    {\"SetPos\",              PositionFunctions::SetPos},\\\n    {\"SetRot\",              PositionFunctions::SetRot},\\\n    {\"SetMomentum\",         PositionFunctions::SetMomentum},\\\n    \\\n    {\"SendPos\",             PositionFunctions::SendPos},\\\n    {\"SendMomentum\",        PositionFunctions::SendMomentum}\n\n\nclass PositionFunctions\n{\npublic:\n\n    /**\n    * \\brief Get the X position of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The X position.\n    */\n    static double GetPosX(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Y position of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The Y position.\n    */\n    static double GetPosY(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Z position of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The Z position.\n    */\n    static double GetPosZ(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the X position of a player from before their latest cell change.\n    *\n    * \\param pid The player ID.\n    * \\return The X position.\n    */\n    static double GetPreviousCellPosX(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Y position of a player from before their latest cell change.\n    *\n    * \\param pid The player ID.\n    * \\return The Y position.\n    */\n    static double GetPreviousCellPosY(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Z position of a player from before their latest cell change.\n    *\n    * \\param pid The player ID.\n    * \\return The Z position.\n    */\n    static double GetPreviousCellPosZ(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the X rotation of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The X rotation.\n    */\n    static double GetRotX(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the Z rotation of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The Z rotation.\n    */\n    static double GetRotZ(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the position of a player.\n    *\n    * This changes the positional coordinates recorded for that player in the server memory, but\n    * does not by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param x The X position.\n    * \\param y The Y position.\n    * \\param z The Z position.\n    * \\return void\n    */\n    static void SetPos(unsigned short pid, double x, double y, double z) noexcept;\n\n    /**\n    * \\brief Set the rotation of a player.\n    *\n    * This changes the rotational coordinates recorded for that player in the server memory, but\n    * does not by itself send a packet.\n    *\n    * A player's Y rotation is always 0, which is why there is no Y rotation parameter.\n    *\n    * \\param pid The player ID.\n    * \\param x The X position.\n    * \\param z The Z position.\n    * \\return void\n    */\n    static void SetRot(unsigned short pid, double x, double z) noexcept;\n\n    /**\n    * \\brief Set the momentum of a player.\n    *\n    * This changes the coordinates recorded for that player's momentum in the server memory, but\n    * does not by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param x The X momentum.\n    * \\param y The Y momentum.\n    * \\param z The Z momentum.\n    * \\return void\n    */\n    static void SetMomentum(unsigned short pid, double x, double y, double z) noexcept;\n\n    /**\n    * \\brief Send a PlayerPosition packet about a player.\n    *\n    * It is only sent to the affected player.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendPos(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerMomentum packet about a player.\n    *\n    * It is only sent to the affected player.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendMomentum(unsigned short pid) noexcept;\n};\n\n#endif //OPENMW_POSITIONAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Quests.cpp",
    "content": "#include \"Quests.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\nusing namespace mwmp;\n\nvoid QuestFunctions::ClearJournalChanges(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->journalChanges.clear();\n}\n\nunsigned int QuestFunctions::GetJournalChangesSize(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->journalChanges.size();\n}\n\nvoid QuestFunctions::AddJournalEntry(unsigned short pid, const char* quest, unsigned int index, const char* actorRefId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::JournalItem journalItem;\n    journalItem.type = JournalItem::ENTRY;\n    journalItem.quest = quest;\n    journalItem.index = index;\n    journalItem.actorRefId = actorRefId;\n    journalItem.hasTimestamp = false;\n\n    player->journalChanges.push_back(journalItem);\n}\n\nvoid QuestFunctions::AddJournalEntryWithTimestamp(unsigned short pid, const char* quest, unsigned int index, const char* actorRefId,\n    unsigned int daysPassed, unsigned int month, unsigned int day) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::JournalItem journalItem;\n    journalItem.type = JournalItem::ENTRY;\n    journalItem.quest = quest;\n    journalItem.index = index;\n    journalItem.actorRefId = actorRefId;\n    journalItem.hasTimestamp = true;\n\n    journalItem.timestamp.daysPassed = daysPassed;\n    journalItem.timestamp.month = month;\n    journalItem.timestamp.day = day;\n\n    player->journalChanges.push_back(journalItem);\n}\n\nvoid QuestFunctions::AddJournalIndex(unsigned short pid, const char* quest, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::JournalItem journalItem;\n    journalItem.type = JournalItem::INDEX;\n    journalItem.quest = quest;\n    journalItem.index = index;\n\n    player->journalChanges.push_back(journalItem);\n}\n\nvoid QuestFunctions::SetReputation(unsigned short pid, int value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->npcStats.mReputation = value;\n}\n\nconst char *QuestFunctions::GetJournalItemQuest(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->journalChanges.size())\n        return \"invalid\";\n\n    return player->journalChanges.at(index).quest.c_str();\n}\n\nint QuestFunctions::GetJournalItemIndex(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->journalChanges.at(index).index;\n}\n\nint QuestFunctions::GetJournalItemType(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->journalChanges.at(index).type;\n}\n\nconst char *QuestFunctions::GetJournalItemActorRefId(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->journalChanges.at(index).actorRefId.c_str();\n}\n\nint QuestFunctions::GetReputation(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->npcStats.mReputation;\n}\n\nvoid QuestFunctions::SendJournalChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_JOURNAL);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid QuestFunctions::SendReputation(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_REPUTATION);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\n// All methods below are deprecated versions of methods from above\n\nvoid QuestFunctions::InitializeJournalChanges(unsigned short pid) noexcept\n{\n    ClearJournalChanges(pid);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Quests.hpp",
    "content": "#ifndef OPENMW_QUESTAPI_HPP\n#define OPENMW_QUESTAPI_HPP\n\n#define QUESTAPI \\\n    {\"ClearJournalChanges\",               QuestFunctions::ClearJournalChanges},\\\n    \\\n    {\"GetJournalChangesSize\",             QuestFunctions::GetJournalChangesSize},\\\n    \\\n    {\"AddJournalEntry\",                   QuestFunctions::AddJournalEntry},\\\n    {\"AddJournalEntryWithTimestamp\",      QuestFunctions::AddJournalEntryWithTimestamp},\\\n    {\"AddJournalIndex\",                   QuestFunctions::AddJournalIndex},\\\n    \\\n    {\"SetReputation\",                     QuestFunctions::SetReputation},\\\n    \\\n    {\"GetJournalItemQuest\",               QuestFunctions::GetJournalItemQuest},\\\n    {\"GetJournalItemIndex\",               QuestFunctions::GetJournalItemIndex},\\\n    {\"GetJournalItemType\",                QuestFunctions::GetJournalItemType},\\\n    {\"GetJournalItemActorRefId\",          QuestFunctions::GetJournalItemActorRefId},\\\n    \\\n    {\"GetReputation\",                     QuestFunctions::GetReputation},\\\n    \\\n    {\"SendJournalChanges\",                QuestFunctions::SendJournalChanges},\\\n    {\"SendReputation\",                    QuestFunctions::SendReputation},\\\n    \\\n    {\"InitializeJournalChanges\",          QuestFunctions::InitializeJournalChanges}\n\nclass QuestFunctions\n{\npublic:\n\n    /**\n    * \\brief Clear the last recorded journal changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerJournal packets.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\return void\n    */\n    static void ClearJournalChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest journal changes.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetJournalChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Add a new journal item of type ENTRY to the journal changes for a player,\n    *  with a specific timestamp.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\param quest The quest of the journal item.\n    * \\param index The quest index of the journal item.\n    * \\param actorRefId The actor refId of the journal item.\n    * \\return void\n    */\n    static void AddJournalEntry(unsigned short pid, const char* quest, unsigned int index, const char* actorRefId) noexcept;\n\n    /**\n    * \\brief Add a new journal item of type ENTRY to the journal changes for a player,\n    *  with a specific timestamp.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\param quest The quest of the journal item.\n    * \\param index The quest index of the journal item.\n    * \\param actorRefId The actor refId of the journal item.\n    * \\param daysPassed The daysPassed for the journal item.\n    * \\param month The month for the journal item.\n    * \\param day The day of the month for the journal item.\n    * \\return void\n    */\n    static void AddJournalEntryWithTimestamp(unsigned short pid, const char* quest, unsigned int index, const char* actorRefId,\n        unsigned int daysPassed, unsigned int month, unsigned int day) noexcept;\n\n    /**\n    * \\brief Add a new journal item of type INDEX to the journal changes for a player.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\param quest The quest of the journal item.\n    * \\param index The quest index of the journal item.\n    * \\return void\n    */\n    static void AddJournalIndex(unsigned short pid, const char* quest, unsigned int index) noexcept;\n\n    /**\n    * \\brief Set the reputation of a certain player.\n    *\n    * \\param pid The player ID.\n    * \\param value The reputation.\n    * \\return void\n    */\n    static void SetReputation(unsigned short pid, int value) noexcept;\n\n    /**\n    * \\brief Get the quest at a certain index in a player's latest journal changes.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\param index The index of the journalItem.\n    * \\return The quest.\n    */\n    static const char *GetJournalItemQuest(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the quest index at a certain index in a player's latest journal changes.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\param index The index of the journalItem.\n    * \\return The quest index.\n    */\n    static int GetJournalItemIndex(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the journal item type at a certain index in a player's latest journal changes.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\param index The index of the journalItem.\n    * \\return The type (0 for ENTRY, 1 for INDEX).\n    */\n    static int GetJournalItemType(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the actor refId at a certain index in a player's latest journal changes.\n    *\n    * Every journal change has an associated actor, which is usually the quest giver.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\param index The index of the journalItem.\n    * \\return The actor refId.\n    */\n    static const char *GetJournalItemActorRefId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the a certain player's reputation.\n    *\n    * \\param pid The player ID.\n    * \\return The reputation.\n    */\n    static int GetReputation(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerJournal packet with a player's recorded journal changes.\n    *\n    * \\param pid The player ID whose journal changes should be used.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendJournalChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a PlayerReputation packet with a player's recorded reputation.\n    *\n    * \\param pid The player ID whose reputation should be used.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendReputation(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static void InitializeJournalChanges(unsigned short pid) noexcept;\n\nprivate:\n\n};\n\n#endif //OPENMW_QUESTAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/RecordsDynamic.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Base/BaseWorldstate.hpp>\n\n#include <apps/openmw-mp/Networking.hpp>\n#include <apps/openmw-mp/Player.hpp>\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Script/Functions/Worldstate.hpp>\n#include <fstream>\n\n#include \"RecordsDynamic.hpp\"\n\nusing namespace mwmp;\n\nSpellRecord tempSpell;\nPotionRecord tempPotion;\nEnchantmentRecord tempEnchantment;\nArmorRecord tempArmor;\nBookRecord tempBook;\nClothingRecord tempClothing;\nMiscellaneousRecord tempMiscellaneous;\nWeaponRecord tempWeapon;\nActivatorRecord tempActivator;\nApparatusRecord tempApparatus;\nBodyPartRecord tempBodyPart;\nCellRecord tempCell;\nContainerRecord tempContainer;\nCreatureRecord tempCreature;\nDoorRecord tempDoor;\nIngredientRecord tempIngredient;\nLightRecord tempLight;\nLockpickRecord tempLockpick;\nNpcRecord tempNpc;\nProbeRecord tempProbe;\nRepairRecord tempRepair;\nScriptRecord tempScript;\nStaticRecord tempStatic;\nSoundRecord tempSound;\nGameSettingRecord tempGameSetting;\n\nBaseOverrides tempOverrides;\n\nunsigned int effectCount = 0;\nESM::ENAMstruct tempEffect;\nESM::PartReference tempBodyPartReference;\nmwmp::Item tempInventoryItem;\n\nconst ESM::EffectList emptyEffectList = {};\n\nconst ESM::EffectList& GetRecordEffects(unsigned int recordIndex)\n{\n    unsigned short recordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (recordsType == mwmp::RECORD_TYPE::SPELL)\n        return WorldstateFunctions::readWorldstate->spellRecords.at(recordIndex).data.mEffects;\n    else if (recordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(recordIndex).data.mEffects;\n    else if (recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        return WorldstateFunctions::readWorldstate->enchantmentRecords.at(recordIndex).data.mEffects;\n\n    return emptyEffectList;\n}\n\nvoid RecordsDynamicFunctions::ClearRecords() noexcept\n{\n    WorldstateFunctions::writeWorldstate.spellRecords.clear();\n    WorldstateFunctions::writeWorldstate.potionRecords.clear();\n    WorldstateFunctions::writeWorldstate.enchantmentRecords.clear();\n    WorldstateFunctions::writeWorldstate.armorRecords.clear();\n    WorldstateFunctions::writeWorldstate.bookRecords.clear();\n    WorldstateFunctions::writeWorldstate.clothingRecords.clear();\n    WorldstateFunctions::writeWorldstate.miscellaneousRecords.clear();\n    WorldstateFunctions::writeWorldstate.weaponRecords.clear();\n    WorldstateFunctions::writeWorldstate.activatorRecords.clear();\n    WorldstateFunctions::writeWorldstate.apparatusRecords.clear();\n    WorldstateFunctions::writeWorldstate.bodyPartRecords.clear();\n    WorldstateFunctions::writeWorldstate.cellRecords.clear();\n    WorldstateFunctions::writeWorldstate.containerRecords.clear();\n    WorldstateFunctions::writeWorldstate.creatureRecords.clear();\n    WorldstateFunctions::writeWorldstate.doorRecords.clear();\n    WorldstateFunctions::writeWorldstate.ingredientRecords.clear();\n    WorldstateFunctions::writeWorldstate.lightRecords.clear();\n    WorldstateFunctions::writeWorldstate.lockpickRecords.clear();\n    WorldstateFunctions::writeWorldstate.npcRecords.clear();\n    WorldstateFunctions::writeWorldstate.probeRecords.clear();\n    WorldstateFunctions::writeWorldstate.repairRecords.clear();\n    WorldstateFunctions::writeWorldstate.scriptRecords.clear();\n    WorldstateFunctions::writeWorldstate.staticRecords.clear();\n    WorldstateFunctions::writeWorldstate.soundRecords.clear();\n    WorldstateFunctions::writeWorldstate.gameSettingRecords.clear();\n}\n\nunsigned short RecordsDynamicFunctions::GetRecordType() noexcept\n{\n    return WorldstateFunctions::readWorldstate->recordsType;\n}\n\nunsigned int RecordsDynamicFunctions::GetRecordCount() noexcept\n{\n    return WorldstateFunctions::readWorldstate->recordsCount;\n}\n\nunsigned int RecordsDynamicFunctions::GetRecordEffectCount(unsigned int recordIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.size();\n}\n\nint RecordsDynamicFunctions::GetRecordSubtype(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::SPELL)\n        return WorldstateFunctions::readWorldstate->spellRecords.at(index).data.mData.mType;\n    else if (readRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        return WorldstateFunctions::readWorldstate->enchantmentRecords.at(index).data.mData.mType;\n\n    return -1;\n}\n\nconst char *RecordsDynamicFunctions::GetRecordId(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::SPELL)\n        return WorldstateFunctions::readWorldstate->spellRecords.at(index).data.mId.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).data.mId.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        return WorldstateFunctions::readWorldstate->enchantmentRecords.at(index).data.mId.c_str();\n\n    return \"invalid\";\n}\n\nconst char *RecordsDynamicFunctions::GetRecordBaseId(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::SPELL)\n        return WorldstateFunctions::readWorldstate->spellRecords.at(index).baseId.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).baseId.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        return WorldstateFunctions::readWorldstate->enchantmentRecords.at(index).baseId.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        return WorldstateFunctions::readWorldstate->armorRecords.at(index).baseId.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::BOOK)\n        return WorldstateFunctions::readWorldstate->bookRecords.at(index).baseId.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        return WorldstateFunctions::readWorldstate->clothingRecords.at(index).baseId.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        return WorldstateFunctions::readWorldstate->weaponRecords.at(index).baseId.c_str();\n\n    return \"invalid\";\n}\n\nconst char *RecordsDynamicFunctions::GetRecordName(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::SPELL)\n        return WorldstateFunctions::readWorldstate->spellRecords.at(index).data.mName.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).data.mName.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        return WorldstateFunctions::readWorldstate->armorRecords.at(index).data.mName.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::BOOK)\n        return WorldstateFunctions::readWorldstate->bookRecords.at(index).data.mName.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        return WorldstateFunctions::readWorldstate->clothingRecords.at(index).data.mName.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        return WorldstateFunctions::readWorldstate->weaponRecords.at(index).data.mName.c_str();\n\n    return \"invalid\";\n}\n\nconst char *RecordsDynamicFunctions::GetRecordModel(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).data.mModel.c_str();\n\n    return \"invalid\";\n}\n\nconst char *RecordsDynamicFunctions::GetRecordIcon(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).data.mIcon.c_str();\n\n    return \"invalid\";\n}\n\nconst char *RecordsDynamicFunctions::GetRecordScript(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).data.mScript.c_str();\n\n    return \"invalid\";\n}\n\nconst char *RecordsDynamicFunctions::GetRecordEnchantmentId(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        return WorldstateFunctions::readWorldstate->armorRecords.at(index).data.mEnchant.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::BOOK)\n        return WorldstateFunctions::readWorldstate->bookRecords.at(index).data.mEnchant.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        return WorldstateFunctions::readWorldstate->clothingRecords.at(index).data.mEnchant.c_str();\n    else if (readRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        return WorldstateFunctions::readWorldstate->weaponRecords.at(index).data.mEnchant.c_str();\n\n    return \"invalid\";\n}\n\nint RecordsDynamicFunctions::GetRecordEnchantmentCharge(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        return WorldstateFunctions::readWorldstate->armorRecords.at(index).data.mData.mEnchant;\n    else if (readRecordsType == mwmp::RECORD_TYPE::BOOK)\n        return WorldstateFunctions::readWorldstate->bookRecords.at(index).data.mData.mEnchant;\n    else if (readRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        return WorldstateFunctions::readWorldstate->clothingRecords.at(index).data.mData.mEnchant;\n    else if (readRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        return WorldstateFunctions::readWorldstate->weaponRecords.at(index).data.mData.mEnchant;\n\n    return -1;\n}\n\nint RecordsDynamicFunctions::GetRecordAutoCalc(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).data.mData.mAutoCalc;\n\n    return -1;\n}\n\nint RecordsDynamicFunctions::GetRecordCharge(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        return WorldstateFunctions::readWorldstate->enchantmentRecords.at(index).data.mData.mCharge;\n\n    return -1;\n}\n\nint RecordsDynamicFunctions::GetRecordCost(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::SPELL)\n        return WorldstateFunctions::readWorldstate->spellRecords.at(index).data.mData.mCost;\n    else if (readRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        return WorldstateFunctions::readWorldstate->enchantmentRecords.at(index).data.mData.mCost;\n\n    return -1;\n}\n\nint RecordsDynamicFunctions::GetRecordFlags(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::SPELL)\n        return WorldstateFunctions::readWorldstate->spellRecords.at(index).data.mData.mFlags;\n    else if (readRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        return WorldstateFunctions::readWorldstate->enchantmentRecords.at(index).data.mData.mFlags;\n\n    return -1;\n}\n\nint RecordsDynamicFunctions::GetRecordValue(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).data.mData.mValue;\n\n    return -1;\n}\n\ndouble RecordsDynamicFunctions::GetRecordWeight(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).data.mData.mWeight;\n\n    return -1;\n}\n\nunsigned int RecordsDynamicFunctions::GetRecordQuantity(unsigned int index) noexcept\n{\n    unsigned short readRecordsType = RecordsDynamicFunctions::GetRecordType();\n\n    if (readRecordsType == mwmp::RECORD_TYPE::POTION)\n        return WorldstateFunctions::readWorldstate->potionRecords.at(index).quantity;\n    else if (readRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        return WorldstateFunctions::readWorldstate->weaponRecords.at(index).quantity;\n\n    return 1;\n}\n\nunsigned int RecordsDynamicFunctions::GetRecordEffectId(unsigned int recordIndex, unsigned int effectIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.at(effectIndex).mEffectID;\n}\n\nint RecordsDynamicFunctions::GetRecordEffectAttribute(unsigned int recordIndex, unsigned int effectIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.at(effectIndex).mAttribute;\n}\n\nint RecordsDynamicFunctions::GetRecordEffectSkill(unsigned int recordIndex, unsigned int effectIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.at(effectIndex).mSkill;\n}\n\nunsigned int RecordsDynamicFunctions::GetRecordEffectRangeType(unsigned int recordIndex, unsigned int effectIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.at(effectIndex).mRange;\n}\n\nint RecordsDynamicFunctions::GetRecordEffectArea(unsigned int recordIndex, unsigned int effectIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.at(effectIndex).mArea;\n}\n\nint RecordsDynamicFunctions::GetRecordEffectDuration(unsigned int recordIndex, unsigned int effectIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.at(effectIndex).mDuration;\n}\n\nint RecordsDynamicFunctions::GetRecordEffectMagnitudeMax(unsigned int recordIndex, unsigned int effectIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.at(effectIndex).mMagnMax;\n}\n\nint RecordsDynamicFunctions::GetRecordEffectMagnitudeMin(unsigned int recordIndex, unsigned int effectIndex) noexcept\n{\n    return GetRecordEffects(recordIndex).mList.at(effectIndex).mMagnMin;\n}\n\nvoid RecordsDynamicFunctions::SetRecordType(unsigned int type) noexcept\n{\n    WorldstateFunctions::writeWorldstate.recordsType = type;\n}\n\nvoid RecordsDynamicFunctions::SetRecordId(const char* id) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n        tempSpell.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        tempEnchantment.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n        tempActivator.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n        tempBodyPart.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n        tempContainer.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::DOOR)\n        tempDoor.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n        tempIngredient.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::SCRIPT)\n        tempScript.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::STATIC)\n        tempStatic.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::SOUND)\n        tempSound.data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::GAMESETTING)\n        tempGameSetting.data.mId = id;\n    else\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set id for record type %i which lacks that property\", writeRecordsType);\n}\n\nvoid RecordsDynamicFunctions::SetRecordBaseId(const char* baseId) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n        tempSpell.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        tempEnchantment.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n        tempActivator.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n        tempBodyPart.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CELL)\n        tempCell.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n        tempContainer.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::DOOR)\n        tempDoor.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n        tempIngredient.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::SCRIPT)\n        tempScript.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::STATIC)\n        tempStatic.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::SOUND)\n        tempSound.baseId = baseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::GAMESETTING)\n        tempGameSetting.baseId = baseId;\n    else\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set baseId for record type %i which lacks that property\", writeRecordsType);\n}\n\nvoid RecordsDynamicFunctions::SetRecordInventoryBaseId(const char* inventoryBaseId) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.inventoryBaseId = inventoryBaseId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.inventoryBaseId = inventoryBaseId;\n    else\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set inventoryBaseId for record type %i which lacks that property\", writeRecordsType);\n}\n\nvoid RecordsDynamicFunctions::SetRecordSubtype(unsigned int subtype) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n        tempSpell.data.mData.mType = subtype;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        tempEnchantment.data.mData.mType = subtype;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mData.mType = subtype;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mData.mType = subtype;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mData.mType = subtype;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mData.mType = subtype;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mData.mType = subtype;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n        tempBodyPart.data.mData.mType = subtype;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set subtype for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasSubtype = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordName(const char* name) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n        tempSpell.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n        tempActivator.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CELL)\n        tempCell.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n        tempContainer.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::DOOR)\n        tempDoor.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n        tempIngredient.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mName = name;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mName = name;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set name for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasName = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordModel(const char* model) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n        tempActivator.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n        tempBodyPart.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n        tempContainer.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::DOOR)\n        tempDoor.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n        tempIngredient.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mModel = model;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::STATIC)\n        tempStatic.data.mModel = model;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set model for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasModel = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordIcon(const char* icon) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n        tempIngredient.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mIcon = icon;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mIcon = icon;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set icon for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasIcon = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordScript(const char* script) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n        tempActivator.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n        tempContainer.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::DOOR)\n        tempDoor.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n        tempIngredient.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mScript = script;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mScript = script;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set script for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasScript = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEnchantmentId(const char* enchantmentId) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mEnchant = enchantmentId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mEnchant = enchantmentId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mEnchant = enchantmentId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mEnchant = enchantmentId;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set enchantment id for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasEnchantmentId = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEnchantmentCharge(int enchantmentCharge) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mData.mEnchant = enchantmentCharge;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mData.mEnchant = enchantmentCharge;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mData.mEnchant = enchantmentCharge;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mData.mEnchant = enchantmentCharge;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set enchantment charge for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasEnchantmentCharge = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordAutoCalc(int autoCalc) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mData.mAutoCalc = autoCalc;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n    {\n        if (autoCalc)\n        {\n            tempNpc.data.mFlags |= ESM::NPC::Autocalc;\n            tempNpc.data.mNpdtType = ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;\n        }\n        else\n        {\n            tempNpc.data.mFlags &= ~ESM::NPC::Autocalc;\n            tempNpc.data.mNpdtType = ESM::NPC::NPC_DEFAULT;\n        }\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set autoCalc for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasAutoCalc = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordCharge(int charge) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        tempEnchantment.data.mData.mCharge = charge;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set charge for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasCharge = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordCost(int cost) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n        tempSpell.data.mData.mCost = cost;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        tempEnchantment.data.mData.mCost = cost;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set cost for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasCost = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordFlags(int flags) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n        tempSpell.data.mData.mFlags = flags;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        tempEnchantment.data.mData.mFlags = flags;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mData.mFlags = flags;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n        tempBodyPart.data.mData.mFlags = flags;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n        tempContainer.data.mFlags = flags;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mFlags = flags;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mData.mFlags = flags;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mFlags = flags;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set flags for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasFlags = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordValue(int value) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n        tempIngredient.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mData.mValue = value;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mData.mValue = value;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set value for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasValue = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordWeight(double weight) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n        tempContainer.data.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n        tempIngredient.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mData.mWeight = weight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mData.mWeight = weight;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set weight for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasWeight = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordQuality(double quality) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n        tempApparatus.data.mData.mQuality = quality;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mData.mQuality = quality;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mData.mQuality = quality;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mData.mQuality = quality;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set quality for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasQuality = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordUses(int uses) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n        tempLockpick.data.mData.mUses = uses;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n        tempProbe.data.mData.mUses = uses;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n        tempRepair.data.mData.mUses = uses;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set number of uses for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasUses = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordTime(int time) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mData.mTime = time;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set time for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasTime = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordRadius(int radius) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mData.mRadius = radius;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set radius for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasRadius = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordColor(unsigned int red, unsigned int green, unsigned int blue) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mData.mColor = red + (green << 8) + (blue << 16);\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set color for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasColor = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordArmorRating(int armorRating) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mData.mArmor = armorRating;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set armor rating for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasArmorRating = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordHealth(int health) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mData.mHealth = health;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mData.mHealth = health;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mData.mHealth = health;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mNpdt.mHealth = health;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set health for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasHealth = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordDamageChop(unsigned int minDamage, unsigned int maxDamage) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n    {\n        tempWeapon.data.mData.mChop[0] = minDamage;\n        tempWeapon.data.mData.mChop[1] = maxDamage;\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n    {\n        tempCreature.data.mData.mAttack[0] = minDamage;\n        tempCreature.data.mData.mAttack[1] = maxDamage;\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set chop damage for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasDamageChop = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordDamageSlash(unsigned int minDamage, unsigned int maxDamage) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n    {\n        tempWeapon.data.mData.mSlash[0] = minDamage;\n        tempWeapon.data.mData.mSlash[1] = maxDamage;\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n    {\n        tempCreature.data.mData.mAttack[2] = minDamage;\n        tempCreature.data.mData.mAttack[3] = maxDamage;\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set slash damage for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasDamageSlash = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordDamageThrust(unsigned int minDamage, unsigned int maxDamage) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n    {\n        tempWeapon.data.mData.mThrust[0] = minDamage;\n        tempWeapon.data.mData.mThrust[1] = maxDamage;\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n    {\n        tempCreature.data.mData.mAttack[4] = minDamage;\n        tempCreature.data.mData.mAttack[5] = maxDamage;\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set thrust damage for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasDamageThrust = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordReach(double reach) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mData.mReach = reach;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set reach for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasReach = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordSpeed(double speed) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        tempWeapon.data.mData.mSpeed = speed;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set speed for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasSpeed = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordKeyState(bool keyState) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n        tempMiscellaneous.data.mData.mIsKey = keyState;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set key state for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasKeyState = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordScrollState(bool scrollState) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mData.mIsScroll = scrollState;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set scroll state for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasScrollState = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordSkillId(int skillId) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mData.mSkillId = skillId;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set skill id for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasSkillId = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordText(const char* text) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        tempBook.data.mText = text;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set text for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasText = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordHair(const char* hair) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mHair = hair;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set hair for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasHair = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordHead(const char* head) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mHead = head;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set head for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasHead = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordGender(unsigned int gender) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n    {\n        tempNpc.data.setIsMale(gender);\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set gender for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasGender = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordRace(const char* race) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n        tempBodyPart.data.mRace = race;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mRace = race;\n    else\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set race for record type %i which lacks that property\", writeRecordsType);\n\n    tempOverrides.hasRace = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordClass(const char* charClass) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mClass = charClass;\n    else\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set character class for record type %i which lacks that property\", writeRecordsType);\n}\n\nvoid RecordsDynamicFunctions::SetRecordFaction(const char* faction) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mFaction = faction;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set faction for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasFaction = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordScale(double scale) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mScale = scale;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set level for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasScale = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordBloodType(int bloodType) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mBloodType = bloodType;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set blood type for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasBloodType = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordVampireState(bool vampireState) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n        tempBodyPart.data.mData.mVampire = vampireState;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set vampire state for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasVampireState = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordLevel(int level) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mData.mLevel = level;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mNpdt.mLevel = level;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set level for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasLevel = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordMagicka(int magicka) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mData.mMana = magicka;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mNpdt.mMana = magicka;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set magicka for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasMagicka = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordFatigue(int fatigue) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mData.mFatigue = fatigue;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mNpdt.mFatigue = fatigue;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set fatigue for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasFatigue = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordSoulValue(int soulValue) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mData.mSoul = soulValue;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set soul value for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasSoulValue = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordAIFight(int aiFight) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mAiData.mFight = aiFight;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mAiData.mFight = aiFight;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set AI fight for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasAiFight = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordAIFlee(int aiFlee) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mAiData.mFlee = aiFlee;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mAiData.mFlee = aiFlee;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set AI fight for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasAiFlee = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordAIAlarm(int aiAlarm) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mAiData.mAlarm = aiAlarm;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mAiData.mAlarm = aiAlarm;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set AI fight for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasAiAlarm = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordAIServices(int aiServices) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.data.mAiData.mServices = aiServices;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.data.mAiData.mServices = aiServices;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set AI services for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasAiServices = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordSound(const char* sound) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n        tempLight.data.mSound = sound;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::SOUND)\n        tempSound.data.mSound = sound;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set sound for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasSound = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordVolume(double volume) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SOUND)\n        tempSound.data.mData.mVolume = volume;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set sound for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasVolume = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordMinRange(double minRange) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SOUND)\n        tempSound.data.mData.mMinRange = minRange;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set sound for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasMinRange = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordMaxRange(double maxRange) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SOUND)\n        tempSound.data.mData.mMinRange = maxRange;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set sound for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasMaxRange = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordOpenSound(const char* sound) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::DOOR)\n        tempDoor.data.mOpenSound = sound;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set open sound for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasOpenSound = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordCloseSound(const char* sound) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::DOOR)\n        tempDoor.data.mCloseSound = sound;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set close sound for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasCloseSound = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordScriptText(const char* scriptText) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SCRIPT)\n        tempScript.data.mScriptText = scriptText;\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set script text for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n\n    tempOverrides.hasScriptText = true;\n}\n\nvoid RecordsDynamicFunctions::SetRecordIntegerVariable(int intVar) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::GAMESETTING)\n    {\n        tempGameSetting.variable.variableType = mwmp::INT;\n        tempGameSetting.variable.intValue = intVar;\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set integer variable for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n}\n\nvoid RecordsDynamicFunctions::SetRecordFloatVariable(double floatVar) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::GAMESETTING)\n    {\n        tempGameSetting.variable.variableType = mwmp::FLOAT;\n        tempGameSetting.variable.floatValue = floatVar;\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set float variable for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n}\n\nvoid RecordsDynamicFunctions::SetRecordStringVariable(const char* stringVar) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::GAMESETTING)\n    {\n        tempGameSetting.variable.variableType = mwmp::STRING;\n        tempGameSetting.variable.stringValue = stringVar;\n    }\n    else\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set string variable for record type %i which lacks that property\", writeRecordsType);\n        return;\n    }\n}\n\nvoid RecordsDynamicFunctions::SetRecordIdByIndex(unsigned int index, const char* id) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n        WorldstateFunctions::writeWorldstate.spellRecords.at(index).data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        WorldstateFunctions::writeWorldstate.potionRecords.at(index).data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        WorldstateFunctions::writeWorldstate.enchantmentRecords.at(index).data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        WorldstateFunctions::writeWorldstate.armorRecords.at(index).data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        WorldstateFunctions::writeWorldstate.bookRecords.at(index).data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        WorldstateFunctions::writeWorldstate.clothingRecords.at(index).data.mId = id;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        WorldstateFunctions::writeWorldstate.weaponRecords.at(index).data.mId = id;\n    else\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set id for record type %i which lacks that property\", writeRecordsType);\n}\n\nvoid RecordsDynamicFunctions::SetRecordEnchantmentIdByIndex(unsigned int index, const char* enchantmentId) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        WorldstateFunctions::writeWorldstate.armorRecords.at(index).data.mEnchant = enchantmentId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n        WorldstateFunctions::writeWorldstate.bookRecords.at(index).data.mEnchant = enchantmentId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        WorldstateFunctions::writeWorldstate.clothingRecords.at(index).data.mEnchant = enchantmentId;\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n        WorldstateFunctions::writeWorldstate.weaponRecords.at(index).data.mEnchant = enchantmentId;\n    else\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Tried to set enchantmentId for record type %i which lacks that property\", writeRecordsType);\n}\n\nvoid RecordsDynamicFunctions::SetRecordEffectId(unsigned int effectId) noexcept\n{\n    tempEffect.mEffectID = effectId;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEffectAttribute(int attributeId) noexcept\n{\n    tempEffect.mAttribute = attributeId;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEffectSkill(int skillId) noexcept\n{\n    tempEffect.mSkill = skillId;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEffectRangeType(unsigned int rangeType) noexcept\n{\n    tempEffect.mRange = rangeType;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEffectArea(int area) noexcept\n{\n    tempEffect.mArea = area;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEffectDuration(int duration) noexcept\n{\n    tempEffect.mDuration = duration;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEffectMagnitudeMax(int magnitudeMax) noexcept\n{\n    tempEffect.mMagnMax = magnitudeMax;\n}\n\nvoid RecordsDynamicFunctions::SetRecordEffectMagnitudeMin(int magnitudeMin) noexcept\n{\n    tempEffect.mMagnMin = magnitudeMin;\n}\n\nvoid RecordsDynamicFunctions::SetRecordBodyPartType(unsigned int partType) noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n    {\n        tempBodyPart.data.mData.mPart = partType;\n        tempOverrides.hasBodyPartType = true;\n    }\n    else\n        tempBodyPartReference.mPart = partType;\n}\n\nvoid RecordsDynamicFunctions::SetRecordBodyPartIdForMale(const char* partId) noexcept\n{\n    tempBodyPartReference.mMale = partId;\n}\n\nvoid RecordsDynamicFunctions::SetRecordBodyPartIdForFemale(const char* partId) noexcept\n{\n    tempBodyPartReference.mFemale = partId;\n}\n\nvoid RecordsDynamicFunctions::SetRecordInventoryItemId(const char* itemId) noexcept\n{\n    tempInventoryItem.refId = itemId;\n}\n\nvoid RecordsDynamicFunctions::SetRecordInventoryItemCount(unsigned int count) noexcept\n{\n    tempInventoryItem.count = count;\n}\n\nvoid RecordsDynamicFunctions::AddRecord() noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n    {\n        tempSpell.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.spellRecords.push_back(tempSpell);\n        tempSpell = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n    {\n        tempPotion.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.potionRecords.push_back(tempPotion);\n        tempPotion = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n    {\n        tempEnchantment.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.enchantmentRecords.push_back(tempEnchantment);\n        tempEnchantment = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n    {\n        tempArmor.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.armorRecords.push_back(tempArmor);\n        tempArmor = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BOOK)\n    {\n        tempBook.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.bookRecords.push_back(tempBook);\n        tempBook = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n    {\n        tempClothing.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.clothingRecords.push_back(tempClothing);\n        tempClothing = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n    {\n        tempMiscellaneous.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.miscellaneousRecords.push_back(tempMiscellaneous);\n        tempMiscellaneous = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::WEAPON)\n    {\n        tempWeapon.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.weaponRecords.push_back(tempWeapon);\n        tempWeapon = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n    {\n        tempActivator.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.activatorRecords.push_back(tempActivator);\n        tempActivator = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::APPARATUS)\n    {\n        tempApparatus.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.apparatusRecords.push_back(tempApparatus);\n        tempApparatus = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::BODYPART)\n    {\n        tempBodyPart.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.bodyPartRecords.push_back(tempBodyPart);\n        tempBodyPart = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CELL)\n    {\n        tempCell.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.cellRecords.push_back(tempCell);\n        tempCell = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n    {\n        tempContainer.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.containerRecords.push_back(tempContainer);\n        tempContainer = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n    {\n        tempCreature.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.creatureRecords.push_back(tempCreature);\n        tempCreature = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::DOOR)\n    {\n        tempDoor.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.doorRecords.push_back(tempDoor);\n        tempDoor = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n    {\n        tempIngredient.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.ingredientRecords.push_back(tempIngredient);\n        tempIngredient = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LIGHT)\n    {\n        tempLight.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.lightRecords.push_back(tempLight);\n        tempLight = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::LOCKPICK)\n    {\n        tempLockpick.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.lockpickRecords.push_back(tempLockpick);\n        tempLockpick = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n    {\n        tempNpc.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.npcRecords.push_back(tempNpc);\n        tempNpc = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::PROBE)\n    {\n        tempProbe.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.probeRecords.push_back(tempProbe);\n        tempProbe = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::REPAIR)\n    {\n        tempRepair.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.repairRecords.push_back(tempRepair);\n        tempRepair = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::SCRIPT)\n    {\n        tempScript.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.scriptRecords.push_back(tempScript);\n        tempScript = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::STATIC)\n    {\n        tempStatic.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.staticRecords.push_back(tempStatic);\n        tempStatic = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::SOUND)\n    {\n        tempSound.baseOverrides = tempOverrides;\n        WorldstateFunctions::writeWorldstate.soundRecords.push_back(tempSound);\n        tempSound = {};\n    }\n    else if (writeRecordsType == mwmp::RECORD_TYPE::GAMESETTING)\n    {\n        WorldstateFunctions::writeWorldstate.gameSettingRecords.push_back(tempGameSetting);\n        tempGameSetting = {};\n    }\n\n    effectCount = 0;\n    tempOverrides = {};\n}\n\nvoid RecordsDynamicFunctions::AddRecordEffect() noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::SPELL)\n        tempSpell.data.mEffects.mList.push_back(tempEffect);\n    else if (writeRecordsType == mwmp::RECORD_TYPE::POTION)\n        tempPotion.data.mEffects.mList.push_back(tempEffect);\n    else if (writeRecordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n        tempEnchantment.data.mEffects.mList.push_back(tempEffect);\n    else if (writeRecordsType == mwmp::RECORD_TYPE::INGREDIENT)\n    {\n        const static unsigned int effectCap = sizeof(tempIngredient.data.mData.mEffectID) / sizeof(tempIngredient.data.mData.mEffectID[0]);\n\n        if (effectCount < effectCap)\n        {\n            tempIngredient.data.mData.mEffectID[effectCount] = tempEffect.mEffectID;\n            tempIngredient.data.mData.mAttributes[effectCount] = tempEffect.mAttribute;\n            tempIngredient.data.mData.mSkills[effectCount] = tempEffect.mSkill;\n        }\n        else\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Could not add record effect to temporary ingredient record because the cap of %i effects has been reached\",\n                effectCap);\n        }\n    }\n\n    tempOverrides.hasEffects = true;\n    effectCount++;\n    tempEffect = {};\n}\n\nvoid RecordsDynamicFunctions::AddRecordBodyPart() noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::ARMOR)\n        tempArmor.data.mParts.mParts.push_back(tempBodyPartReference);\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CLOTHING)\n        tempClothing.data.mParts.mParts.push_back(tempBodyPartReference);\n\n    tempOverrides.hasBodyParts = true;\n    tempBodyPart = {};\n}\n\nvoid RecordsDynamicFunctions::AddRecordInventoryItem() noexcept\n{\n    unsigned short writeRecordsType = WorldstateFunctions::writeWorldstate.recordsType;\n\n    if (writeRecordsType == mwmp::RECORD_TYPE::CONTAINER)\n        tempContainer.inventory.push_back(tempInventoryItem);\n    else if (writeRecordsType == mwmp::RECORD_TYPE::CREATURE)\n        tempCreature.inventory.push_back(tempInventoryItem);\n    else if (writeRecordsType == mwmp::RECORD_TYPE::NPC)\n        tempNpc.inventory.push_back(tempInventoryItem);\n\n    tempOverrides.hasInventory = true;\n    tempInventoryItem = {};\n}\n\nvoid RecordsDynamicFunctions::SendRecordDynamic(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    WorldstateFunctions::writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_RECORD_DYNAMIC);\n    packet->setWorldstate(&WorldstateFunctions::writeWorldstate);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/RecordsDynamic.hpp",
    "content": "#ifndef OPENMW_RECORDSDYNAMICAPI_HPP\n#define OPENMW_RECORDSDYNAMICAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define RECORDSDYNAMICAPI \\\n    {\"ClearRecords\",                            RecordsDynamicFunctions::ClearRecords},\\\n    \\\n    {\"GetRecordType\",                           RecordsDynamicFunctions::GetRecordType},\\\n    {\"GetRecordCount\",                          RecordsDynamicFunctions::GetRecordCount},\\\n    {\"GetRecordEffectCount\",                    RecordsDynamicFunctions::GetRecordEffectCount},\\\n    \\\n    {\"GetRecordId\",                             RecordsDynamicFunctions::GetRecordId},\\\n    {\"GetRecordBaseId\",                         RecordsDynamicFunctions::GetRecordBaseId},\\\n    \\\n    {\"GetRecordSubtype\",                        RecordsDynamicFunctions::GetRecordSubtype},\\\n    {\"GetRecordName\",                           RecordsDynamicFunctions::GetRecordName},\\\n    {\"GetRecordModel\",                          RecordsDynamicFunctions::GetRecordModel},\\\n    {\"GetRecordIcon\",                           RecordsDynamicFunctions::GetRecordIcon},\\\n    {\"GetRecordScript\",                         RecordsDynamicFunctions::GetRecordScript},\\\n    {\"GetRecordEnchantmentId\",                  RecordsDynamicFunctions::GetRecordEnchantmentId},\\\n    {\"GetRecordEnchantmentCharge\",              RecordsDynamicFunctions::GetRecordEnchantmentCharge},\\\n    \\\n    {\"GetRecordAutoCalc\",                       RecordsDynamicFunctions::GetRecordAutoCalc},\\\n    {\"GetRecordCharge\",                         RecordsDynamicFunctions::GetRecordCharge},\\\n    {\"GetRecordCost\",                           RecordsDynamicFunctions::GetRecordCost},\\\n    {\"GetRecordFlags\",                          RecordsDynamicFunctions::GetRecordFlags},\\\n    {\"GetRecordValue\",                          RecordsDynamicFunctions::GetRecordValue},\\\n    {\"GetRecordWeight\",                         RecordsDynamicFunctions::GetRecordWeight},\\\n    {\"GetRecordQuantity\",                       RecordsDynamicFunctions::GetRecordQuantity},\\\n    \\\n    {\"GetRecordEffectId\",                       RecordsDynamicFunctions::GetRecordEffectId},\\\n    {\"GetRecordEffectAttribute\",                RecordsDynamicFunctions::GetRecordEffectAttribute},\\\n    {\"GetRecordEffectSkill\",                    RecordsDynamicFunctions::GetRecordEffectSkill},\\\n    {\"GetRecordEffectRangeType\",                RecordsDynamicFunctions::GetRecordEffectRangeType},\\\n    {\"GetRecordEffectArea\",                     RecordsDynamicFunctions::GetRecordEffectArea},\\\n    {\"GetRecordEffectDuration\",                 RecordsDynamicFunctions::GetRecordEffectDuration},\\\n    {\"GetRecordEffectMagnitudeMax\",             RecordsDynamicFunctions::GetRecordEffectMagnitudeMax},\\\n    {\"GetRecordEffectMagnitudeMin\",             RecordsDynamicFunctions::GetRecordEffectMagnitudeMin},\\\n    \\\n    {\"SetRecordType\",                           RecordsDynamicFunctions::SetRecordType},\\\n    \\\n    {\"SetRecordId\",                             RecordsDynamicFunctions::SetRecordId},\\\n    {\"SetRecordBaseId\",                         RecordsDynamicFunctions::SetRecordBaseId},\\\n    {\"SetRecordInventoryBaseId\",                RecordsDynamicFunctions::SetRecordInventoryBaseId},\\\n    \\\n    {\"SetRecordSubtype\",                        RecordsDynamicFunctions::SetRecordSubtype},\\\n    {\"SetRecordName\",                           RecordsDynamicFunctions::SetRecordName},\\\n    {\"SetRecordModel\",                          RecordsDynamicFunctions::SetRecordModel},\\\n    {\"SetRecordIcon\",                           RecordsDynamicFunctions::SetRecordIcon},\\\n    {\"SetRecordScript\",                         RecordsDynamicFunctions::SetRecordScript},\\\n    {\"SetRecordEnchantmentId\",                  RecordsDynamicFunctions::SetRecordEnchantmentId},\\\n    {\"SetRecordEnchantmentCharge\",              RecordsDynamicFunctions::SetRecordEnchantmentCharge},\\\n    \\\n    {\"SetRecordAutoCalc\",                       RecordsDynamicFunctions::SetRecordAutoCalc},\\\n    {\"SetRecordCharge\",                         RecordsDynamicFunctions::SetRecordCharge},\\\n    {\"SetRecordCost\",                           RecordsDynamicFunctions::SetRecordCost},\\\n    {\"SetRecordFlags\",                          RecordsDynamicFunctions::SetRecordFlags},\\\n    {\"SetRecordValue\",                          RecordsDynamicFunctions::SetRecordValue},\\\n    {\"SetRecordWeight\",                         RecordsDynamicFunctions::SetRecordWeight},\\\n    {\"SetRecordQuality\",                        RecordsDynamicFunctions::SetRecordQuality},\\\n    {\"SetRecordUses\",                           RecordsDynamicFunctions::SetRecordUses},\\\n    {\"SetRecordTime\",                           RecordsDynamicFunctions::SetRecordTime},\\\n    {\"SetRecordRadius\",                         RecordsDynamicFunctions::SetRecordRadius},\\\n    {\"SetRecordColor\",                          RecordsDynamicFunctions::SetRecordColor},\\\n    \\\n    {\"SetRecordArmorRating\",                    RecordsDynamicFunctions::SetRecordArmorRating},\\\n    {\"SetRecordHealth\",                         RecordsDynamicFunctions::SetRecordHealth},\\\n    \\\n    {\"SetRecordDamageChop\",                     RecordsDynamicFunctions::SetRecordDamageChop},\\\n    {\"SetRecordDamageSlash\",                    RecordsDynamicFunctions::SetRecordDamageSlash},\\\n    {\"SetRecordDamageThrust\",                   RecordsDynamicFunctions::SetRecordDamageThrust},\\\n    {\"SetRecordReach\",                          RecordsDynamicFunctions::SetRecordReach},\\\n    {\"SetRecordSpeed\",                          RecordsDynamicFunctions::SetRecordSpeed},\\\n    \\\n    {\"SetRecordKeyState\",                       RecordsDynamicFunctions::SetRecordKeyState},\\\n    {\"SetRecordScrollState\",                    RecordsDynamicFunctions::SetRecordScrollState},\\\n    {\"SetRecordSkillId\",                        RecordsDynamicFunctions::SetRecordSkillId},\\\n    {\"SetRecordText\",                           RecordsDynamicFunctions::SetRecordText},\\\n    \\\n    {\"SetRecordHair\",                           RecordsDynamicFunctions::SetRecordHair},\\\n    {\"SetRecordHead\",                           RecordsDynamicFunctions::SetRecordHead},\\\n    {\"SetRecordGender\",                         RecordsDynamicFunctions::SetRecordGender},\\\n    {\"SetRecordRace\",                           RecordsDynamicFunctions::SetRecordRace},\\\n    {\"SetRecordClass\",                          RecordsDynamicFunctions::SetRecordClass},\\\n    {\"SetRecordFaction\",                        RecordsDynamicFunctions::SetRecordFaction},\\\n    \\\n    {\"SetRecordScale\",                          RecordsDynamicFunctions::SetRecordScale},\\\n    {\"SetRecordBloodType\",                      RecordsDynamicFunctions::SetRecordBloodType},\\\n    {\"SetRecordVampireState\",                   RecordsDynamicFunctions::SetRecordVampireState},\\\n    \\\n    {\"SetRecordLevel\",                          RecordsDynamicFunctions::SetRecordLevel},\\\n    {\"SetRecordMagicka\",                        RecordsDynamicFunctions::SetRecordMagicka},\\\n    {\"SetRecordFatigue\",                        RecordsDynamicFunctions::SetRecordFatigue},\\\n    \\\n    {\"SetRecordSoulValue\",                      RecordsDynamicFunctions::SetRecordSoulValue},\\\n    \\\n    {\"SetRecordAIFight\",                        RecordsDynamicFunctions::SetRecordAIFight},\\\n    {\"SetRecordAIFlee\",                         RecordsDynamicFunctions::SetRecordAIFlee},\\\n    {\"SetRecordAIAlarm\",                        RecordsDynamicFunctions::SetRecordAIAlarm},\\\n    {\"SetRecordAIServices\",                     RecordsDynamicFunctions::SetRecordAIServices},\\\n    \\\n    {\"SetRecordSound\",                          RecordsDynamicFunctions::SetRecordSound},\\\n    {\"SetRecordVolume\",                         RecordsDynamicFunctions::SetRecordVolume},\\\n    {\"SetRecordMinRange\",                       RecordsDynamicFunctions::SetRecordMinRange},\\\n    {\"SetRecordMaxRange\",                       RecordsDynamicFunctions::SetRecordMaxRange},\\\n    {\"SetRecordOpenSound\",                      RecordsDynamicFunctions::SetRecordOpenSound},\\\n    {\"SetRecordCloseSound\",                     RecordsDynamicFunctions::SetRecordCloseSound},\\\n    \\\n    {\"SetRecordScriptText\",                     RecordsDynamicFunctions::SetRecordScriptText},\\\n    {\"SetRecordIntegerVariable\",                RecordsDynamicFunctions::SetRecordIntegerVariable},\\\n    {\"SetRecordFloatVariable\",                  RecordsDynamicFunctions::SetRecordFloatVariable},\\\n    {\"SetRecordStringVariable\",                 RecordsDynamicFunctions::SetRecordStringVariable},\\\n    \\\n    {\"SetRecordIdByIndex\",                      RecordsDynamicFunctions::SetRecordIdByIndex},\\\n    {\"SetRecordEnchantmentIdByIndex\",           RecordsDynamicFunctions::SetRecordEnchantmentIdByIndex},\\\n    \\\n    {\"SetRecordEffectId\",                       RecordsDynamicFunctions::SetRecordEffectId},\\\n    {\"SetRecordEffectAttribute\",                RecordsDynamicFunctions::SetRecordEffectAttribute},\\\n    {\"SetRecordEffectSkill\",                    RecordsDynamicFunctions::SetRecordEffectSkill},\\\n    {\"SetRecordEffectRangeType\",                RecordsDynamicFunctions::SetRecordEffectRangeType},\\\n    {\"SetRecordEffectArea\",                     RecordsDynamicFunctions::SetRecordEffectArea},\\\n    {\"SetRecordEffectDuration\",                 RecordsDynamicFunctions::SetRecordEffectDuration},\\\n    {\"SetRecordEffectMagnitudeMax\",             RecordsDynamicFunctions::SetRecordEffectMagnitudeMax},\\\n    {\"SetRecordEffectMagnitudeMin\",             RecordsDynamicFunctions::SetRecordEffectMagnitudeMin},\\\n    \\\n    {\"SetRecordBodyPartType\",                   RecordsDynamicFunctions::SetRecordBodyPartType},\\\n    {\"SetRecordBodyPartIdForMale\",              RecordsDynamicFunctions::SetRecordBodyPartIdForMale},\\\n    {\"SetRecordBodyPartIdForFemale\",            RecordsDynamicFunctions::SetRecordBodyPartIdForFemale},\\\n    \\\n    {\"SetRecordInventoryItemId\",                RecordsDynamicFunctions::SetRecordInventoryItemId},\\\n    {\"SetRecordInventoryItemCount\",             RecordsDynamicFunctions::SetRecordInventoryItemCount},\\\n    \\\n    {\"AddRecord\",                               RecordsDynamicFunctions::AddRecord},\\\n    {\"AddRecordEffect\",                         RecordsDynamicFunctions::AddRecordEffect},\\\n    {\"AddRecordBodyPart\",                       RecordsDynamicFunctions::AddRecordBodyPart},\\\n    {\"AddRecordInventoryItem\",                  RecordsDynamicFunctions::AddRecordInventoryItem},\\\n    \\\n    {\"SendRecordDynamic\",                       RecordsDynamicFunctions::SendRecordDynamic}\n\nclass RecordsDynamicFunctions\n{\npublic:\n\n    /**\n    * \\brief Clear the data from the records stored on the server.\n    *\n    * \\return void\n    */\n    static void ClearRecords() noexcept;\n\n    /**\n    * \\brief Get the type of records in the read worldstate's dynamic records.\n    *\n    * \\return The type of records (0 for SPELL, 1 for POTION, 2 for ENCHANTMENT,\n    *         3 for NPC).\n    */\n    static unsigned short GetRecordType() noexcept;\n\n    /**\n    * \\brief Get the number of records in the read worldstate's dynamic records.\n    *\n    * \\return The number of records.\n    */\n    static unsigned int GetRecordCount() noexcept;\n\n    /**\n    * \\brief Get the number of effects for the record at a certain index in the read\n    * worldstate's current records.\n    *\n    * \\param recordIndex The index of the record.\n    * \\return The number of effects.\n    */\n    static unsigned int GetRecordEffectCount(unsigned int recordIndex) noexcept;\n\n    /**\n    * \\brief Get the id of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The id of the record.\n    */\n    static const char *GetRecordId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the base id (i.e. the id this record should inherit default\n    * values from) of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The base id of the record.\n    */\n    static const char *GetRecordBaseId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the subtype of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The type of the record.\n    */\n    static int GetRecordSubtype(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the name of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The name of the record.\n    */\n    static const char *GetRecordName(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the model of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The model of the record.\n    */\n    static const char *GetRecordModel(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the icon of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The icon of the record.\n    */\n    static const char *GetRecordIcon(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the script of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The script of the record.\n    */\n    static const char *GetRecordScript(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the enchantment id of the record at a certain index in the read\n    * worldstate's dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The enchantment id of the record.\n    */\n    static const char *GetRecordEnchantmentId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the enchantment charge of the record at a certain index in\n    * the read worldstate's dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The enchantment charge of the record.\n    */\n    static int GetRecordEnchantmentCharge(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the auto-calculation flag value of the record at a certain index in\n    * the read worldstate's dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The auto-calculation flag value of the record.\n    */\n    static int GetRecordAutoCalc(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the charge of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The charge of the record.\n    */\n    static int GetRecordCharge(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the cost of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The cost of the record.\n    */\n    static int GetRecordCost(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the flags of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The flags of the spell as an integer.\n    */\n    static int GetRecordFlags(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the value of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The value of the record.\n    */\n    static int GetRecordValue(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the weight of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The weight of the record.\n    */\n    static double GetRecordWeight(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the quantity of the record at a certain index in the read worldstate's\n    * dynamic records of the current type.\n    *\n    * \\param index The index of the record.\n    * \\return The brewed count of the record.\n    */\n    static unsigned int GetRecordQuantity(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the ID of the effect at a certain index in the read worldstate's\n    * current records.\n    *\n    * \\param recordIndex The index of the record.\n    * \\param effectIndex The index of the effect.\n    * \\return The ID of the effect.\n    */\n    static unsigned int GetRecordEffectId(unsigned int recordIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the ID of the attribute modified by the effect at a certain index in the\n    * read worldstate's current records.\n    *\n    * \\param recordIndex The index of the record.\n    * \\param effectIndex The index of the effect.\n    * \\return The attribute ID for the effect.\n    */\n    static int GetRecordEffectAttribute(unsigned int recordIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the ID of the skill modified by the effect at a certain index in the\n    * read worldstate's current records.\n    *\n    * \\param recordIndex The index of the record.\n    * \\param effectIndex The index of the effect.\n    * \\return The skill ID for the effect.\n    */\n    static int GetRecordEffectSkill(unsigned int recordIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the range type of the effect at a certain index in the read worldstate's\n    * current records (0 for self, 1 for touch, 2 for target).\n    *\n    * \\param recordIndex The index of the record.\n    * \\param effectIndex The index of the effect.\n    * \\return The range of the effect.\n    */\n    static unsigned int GetRecordEffectRangeType(unsigned int recordIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the area of the effect at a certain index in the read worldstate's current\n    * records.\n    *\n    * \\param recordIndex The index of the record.\n    * \\param effectIndex The index of the effect.\n    * \\return The area of the effect.\n    */\n    static int GetRecordEffectArea(unsigned int recordIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the duration of the effect at a certain index in the read worldstate's current\n    * records.\n    *\n    * \\param recordIndex The index of the record.\n    * \\param effectIndex The index of the effect.\n    * \\return The duration of the effect.\n    */\n    static int GetRecordEffectDuration(unsigned int recordIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the maximum magnitude of the effect at a certain index in the read\n    * worldstate's current records.\n    *\n    * \\param recordIndex The index of the record.\n    * \\param effectIndex The index of the effect.\n    * \\return The maximum magnitude of the effect.\n    */\n    static int GetRecordEffectMagnitudeMax(unsigned int recordIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the minimum magnitude of the effect at a certain index in the read\n    * worldstate's current records.\n    *\n    * \\param recordIndex The index of the record.\n    * \\param effectIndex The index of the effect.\n    * \\return The minimum magnitude of the effect.\n    */\n    static int GetRecordEffectMagnitudeMin(unsigned int recordIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Set which type of temporary records stored on the server should have\n    * their data changed via setter functions.\n    *\n    * \\param type The type of records.\n    * \\return void\n    */\n    static void SetRecordType(unsigned int type) noexcept;\n\n    /**\n    * \\brief Set the id of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param id The id of the record.\n    * \\return void\n    */\n    static void SetRecordId(const char* id) noexcept;\n\n    /**\n    * \\brief Set the base id (i.e. the id this record should inherit default\n    * values from) of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param baseId The baseId of the record.\n    * \\return void\n    */\n    static void SetRecordBaseId(const char* baseId) noexcept;\n\n    /**\n    * \\brief Set the inventory base id (i.e. the id this record should inherit\n    * its inventory contents from) of the temporary record stored on the server for\n    * the currently specified record type.\n    *\n    * \\param inventoryBaseId The inventoryBaseId of the record.\n    * \\return void\n    */\n    static void SetRecordInventoryBaseId(const char* inventoryBaseId) noexcept;\n\n    /**\n    * \\brief Set the subtype of the temporary record stored on the server for\n    * the currently specified record type.\n    *\n    * \\param subtype The spell type.\n    * \\return void\n    */\n    static void SetRecordSubtype(unsigned int subtype) noexcept;\n\n    /**\n    * \\brief Set the name of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param name The name of the record.\n    * \\return void\n    */\n    static void SetRecordName(const char* name) noexcept;\n\n    /**\n    * \\brief Set the model of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param model The model of the record.\n    * \\return void\n    */\n    static void SetRecordModel(const char* model) noexcept;\n\n    /**\n    * \\brief Set the icon of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param icon The icon of the record.\n    * \\return void\n    */\n    static void SetRecordIcon(const char* icon) noexcept;\n\n    /**\n    * \\brief Set the script of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param script The script of the record.\n    * \\return void\n    */\n    static void SetRecordScript(const char* script) noexcept;\n\n    /**\n    * \\brief Set the enchantment id of the temporary record stored on the server\n    * for the currently specified record type.\n    *\n    * \\param enchantmentId The enchantment id of the record.\n    * \\return void\n    */\n    static void SetRecordEnchantmentId(const char* enchantmentId) noexcept;\n\n    /**\n    * \\brief Set the enchantment charge of the temporary record stored on the server\n    * for the currently specified record type.\n    *\n    * \\param enchantmentCharge The enchantmentCharge of the record.\n    * \\return void\n    */\n    static void SetRecordEnchantmentCharge(int enchantmentCharge) noexcept;\n\n    /**\n    * \\brief Set the auto-calculation flag value of the temporary record stored\n    * on the server for the currently specified record type.\n    *\n    * \\param autoCalc The auto-calculation flag value of the record.\n    * \\return void\n    */\n    static void SetRecordAutoCalc(int autoCalc) noexcept;\n\n    /**\n    * \\brief Set the charge of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param charge The charge of the record.\n    * \\return void\n    */\n    static void SetRecordCharge(int charge) noexcept;\n\n    /**\n    * \\brief Set the cost of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param cost The cost of the record.\n    * \\return void\n    */\n    static void SetRecordCost(int cost) noexcept;\n\n    /**\n    * \\brief Set the flags of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param flags The flags of the record.\n    * \\return void\n    */\n    static void SetRecordFlags(int flags) noexcept;\n\n    /**\n    * \\brief Set the value of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param value The value of the record.\n    * \\return void\n    */\n    static void SetRecordValue(int value) noexcept;\n\n    /**\n    * \\brief Set the weight of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param weight The weight of the record.\n    * \\return void\n    */\n    static void SetRecordWeight(double weight) noexcept;\n\n    /**\n    * \\brief Set the item quality of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param quality The quality of the record.\n    * \\return void\n    */\n    static void SetRecordQuality(double quality) noexcept;\n\n    /**\n    * \\brief Set the number of uses of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param uses The number of uses of the record.\n    * \\return void\n    */\n    static void SetRecordUses(int uses) noexcept;\n\n    /**\n    * \\brief Set the time of the temporary record stored on the server for the currently\n    * specified record type.\n    *\n    * \\param time The time of the record.\n    * \\return void\n    */\n    static void SetRecordTime(int time) noexcept;\n\n    /**\n    * \\brief Set the radius of the temporary record stored on the server for the currently\n    * specified record type.\n    *\n    * \\param radius The radius of the record.\n    * \\return void\n    */\n    static void SetRecordRadius(int radius) noexcept;\n\n    /**\n    * \\brief Set the color of the temporary record stored on the server for the currently\n    * specified record type.\n    *\n    * \\param red The red value of the record.\n    * \\param green The green value of the record.\n    * \\param blue The blue value of the record.\n    * \\return void\n    */\n    static void SetRecordColor(unsigned int red, unsigned int green, unsigned int blue) noexcept;\n\n    /**\n    * \\brief Set the armor rating of the temporary record stored on the server\n    * for the currently specified record type.\n    *\n    * \\param armorRating The armor rating of the record.\n    * \\return void\n    */\n    static void SetRecordArmorRating(int armorRating) noexcept;\n\n    /**\n    * \\brief Set the health of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param health The health of the record.\n    * \\return void\n    */\n    static void SetRecordHealth(int health) noexcept;\n\n    /**\n    * \\brief Set the chop damage of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param minDamage The minimum damage of the record.\n    * \\param maxDamage The maximum damage of the record.\n    * \\return void\n    */\n    static void SetRecordDamageChop(unsigned int minDamage, unsigned int maxDamage) noexcept;\n\n    /**\n    * \\brief Set the slash damage of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param minDamage The minimum damage of the record.\n    * \\param maxDamage The maximum damage of the record.\n    * \\return void\n    */\n    static void SetRecordDamageSlash(unsigned int minDamage, unsigned int maxDamage) noexcept;\n\n    /**\n    * \\brief Set the thrust damage of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param minDamage The minimum damage of the record.\n    * \\param maxDamage The maximum damage of the record.\n    * \\return void\n    */\n    static void SetRecordDamageThrust(unsigned int minDamage, unsigned int maxDamage) noexcept;\n\n    /**\n    * \\brief Set the reach of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param reach The reach of the record.\n    * \\return void\n    */\n    static void SetRecordReach(double reach) noexcept;\n\n    /**\n    * \\brief Set the speed of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param speed The speed of the record.\n    * \\return void\n    */\n    static void SetRecordSpeed(double speed) noexcept;\n\n    /**\n    * \\brief Set whether the temporary record stored on the server for the\n    * currently specified record type is a key.\n    *\n    * Note: This is only applicable to Miscellaneous records.\n    *\n    * \\param keyState Whether the record is a key.\n    * \\return void\n    */\n    static void SetRecordKeyState(bool keyState) noexcept;\n\n    /**\n    * \\brief Set whether the temporary record stored on the server for the\n    * currently specified record type is a scroll.\n    *\n    * Note: This is only applicable to Book records.\n    *\n    * \\param scrollState Whether the record is a scroll.\n    * \\return void\n    */\n    static void SetRecordScrollState(bool scrollState) noexcept;\n\n    /**\n    * \\brief Set the skill ID of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param skillId The skill ID of the record.\n    * \\return void\n    */\n    static void SetRecordSkillId(int skillId) noexcept;\n\n    /**\n    * \\brief Set the text of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param text The text of the record.\n    * \\return void\n    */\n    static void SetRecordText(const char* text) noexcept;\n\n    /**\n    * \\brief Set the hair of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param hair The hair of the record.\n    * \\return void\n    */\n    static void SetRecordHair(const char* hair) noexcept;\n\n    /**\n    * \\brief Set the head of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param head The head of the record.\n    * \\return void\n    */\n    static void SetRecordHead(const char* head) noexcept;\n\n    /**\n    * \\brief Set the gender of the temporary record stored on the server for the\n    * currently specified record type (0 for female, 1 for male).\n    *\n    * \\param gender The gender of the record.\n    * \\return void\n    */\n    static void SetRecordGender(unsigned int gender) noexcept;\n\n    /**\n    * \\brief Set the race of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param race The race of the record.\n    * \\return void\n    */\n    static void SetRecordRace(const char* race) noexcept;\n\n    /**\n    * \\brief Set the character class of the temporary record stored on the server\n    * for the currently specified record type.\n    *\n    * \\param charClass The character class of the record.\n    * \\return void\n    */\n    static void SetRecordClass(const char* charClass) noexcept;\n\n    /**\n    * \\brief Set the faction of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param faction The faction of the record.\n    * \\return void\n    */\n    static void SetRecordFaction(const char* faction) noexcept;\n\n    /**\n    * \\brief Set the scale of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param scale The scale of the record.\n    * \\return void\n    */\n    static void SetRecordScale(double scale) noexcept;\n\n    /**\n    * \\brief Set the blood type of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param bloodType The blood type of the record.\n    * \\return void\n    */\n    static void SetRecordBloodType(int bloodType) noexcept;\n\n    /**\n    * \\brief Set the vampire state of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param vampireState The vampire state of the record.\n    * \\return void\n    */\n    static void SetRecordVampireState(bool vampireState) noexcept;\n\n    /**\n    * \\brief Set the level of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param level The level of the record.\n    * \\return void\n    */\n    static void SetRecordLevel(int level) noexcept;\n\n    /**\n    * \\brief Set the magicka of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param magicka The magicka of the record.\n    * \\return void\n    */\n    static void SetRecordMagicka(int magicka) noexcept;\n\n    /**\n    * \\brief Set the fatigue of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param fatigue The fatigue of the record.\n    * \\return void\n    */\n    static void SetRecordFatigue(int fatigue) noexcept;\n\n    /**\n    * \\brief Set the soul value of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param soulValue The soul value of the record.\n    * \\return void\n    */\n    static void SetRecordSoulValue(int soulValue) noexcept;\n\n    /**\n    * \\brief Set the AI fight value of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param aiFight The AI fight value of the record.\n    * \\return void\n    */\n    static void SetRecordAIFight(int aiFight) noexcept;\n\n    /**\n    * \\brief Set the AI flee value of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param aiFlee The AI flee value of the record.\n    * \\return void\n    */\n    static void SetRecordAIFlee(int aiFlee) noexcept;\n\n    /**\n    * \\brief Set the AI alarm value of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param aiAlarm The AI alarm value of the record.\n    * \\return void\n    */\n    static void SetRecordAIAlarm(int aiAlarm) noexcept;\n\n    /**\n    * \\brief Set the AI services value of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param aiServices The AI services value of the record.\n    * \\return void\n    */\n    static void SetRecordAIServices(int aiServices) noexcept;\n\n    /**\n    * \\brief Set the sound of the temporary record stored on the server for the currently\n    * specified record type.\n    *\n    * \\param sound The sound of the record.\n    * \\return void\n    */\n    static void SetRecordSound(const char* sound) noexcept;\n\n    /**\n    * \\brief Set the volume of the temporary record stored on the server for the currently\n    * specified record type.\n    *\n    * \\param volume The volume of the record.\n    * \\return void\n    */\n    static void SetRecordVolume(double volume) noexcept;\n\n    /**\n    * \\brief Set the minimum range of the temporary record stored on the server for the currently\n    * specified record type.\n    *\n    * \\param minRange The minimum range of the record.\n    * \\return void\n    */\n    static void SetRecordMinRange(double minRange) noexcept;\n\n    /**\n    * \\brief Set the maximum range of the temporary record stored on the server for the currently\n    * specified record type.\n    *\n    * \\param maxRange The maximum range of the record.\n    * \\return void\n    */\n    static void SetRecordMaxRange(double maxRange) noexcept;\n\n    /**\n    * \\brief Set the opening sound of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param sound The opening sound of the record.\n    * \\return void\n    */\n    static void SetRecordOpenSound(const char* sound) noexcept;\n\n    /**\n    * \\brief Set the closing sound of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param sound The closing sound of the record.\n    * \\return void\n    */\n    static void SetRecordCloseSound(const char* sound) noexcept;\n\n    /**\n    * \\brief Set the script text of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param scriptText The script text of the record.\n    * \\return void\n    */\n    static void SetRecordScriptText(const char* scriptText) noexcept;\n\n    /**\n    * \\brief Set the integer variable of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param intVar The integer variable of the record.\n    * \\return void\n    */\n    static void SetRecordIntegerVariable(int intVar) noexcept;\n\n    /**\n    * \\brief Set the float variable of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param floatVar The float variable of the record.\n    * \\return void\n    */\n    static void SetRecordFloatVariable(double floatVar) noexcept;\n\n    /**\n    * \\brief Set the string variable of the temporary record stored on the server for the\n    * currently specified record type.\n    *\n    * \\param stringVar The string variable of the record.\n    * \\return void\n    */\n    static void SetRecordStringVariable(const char* stringVar) noexcept;\n\n    /**\n    * \\brief Set the id of the record at a certain index in the records stored on the server.\n    *\n    * When resending a received RecordsDynamic packet, this allows you to set the server-generated\n    * id of a record without having to clear and recreate the packet.\n    *\n    * \\param index The index of the record.\n    * \\param id The id of the record.\n    * \\return void\n    */\n    static void SetRecordIdByIndex(unsigned int index, const char* id) noexcept;\n\n    /**\n    * \\brief Set the enchantment id of the record at a certain index in the records stored on\n    * the server.\n    *\n    * When resending a received RecordsDynamic packet, this allows you to set the server-generated\n    * enchantment id of a record without having to clear and recreate the packet.\n    *\n    * \\param index The index of the record.\n    * \\param enchantmentId The enchantment id of the record.\n    * \\return void\n    */\n    static void SetRecordEnchantmentIdByIndex(unsigned int index, const char* enchantmentId) noexcept;\n\n    /**\n    * \\brief Set the ID of the temporary effect stored on the server.\n    *\n    * \\param effectId The ID of the effect.\n    * \\return void\n    */\n    static void SetRecordEffectId(unsigned int effectId) noexcept;\n\n    /**\n    * \\brief Set the ID of the attribute modified by the temporary effect stored on\n    * the server.\n    *\n    * \\param attributeId The ID of the attribute.\n    * \\return void\n    */\n    static void SetRecordEffectAttribute(int attributeId) noexcept;\n\n    /**\n    * \\brief Set the ID of the skill modified by the temporary effect stored on the\n    * server.\n    *\n    * \\param skillId The ID of the skill.\n    * \\return void\n    */\n    static void SetRecordEffectSkill(int skillId) noexcept;\n\n    /**\n    * \\brief Set the range type of the temporary effect stored on the server (0 for\n    * self, 1 for touch, 2 for target).\n    *\n    * \\param rangeType The range type of the effect.\n    * \\return void\n    */\n    static void SetRecordEffectRangeType(unsigned int rangeType) noexcept;\n\n    /**\n    * \\brief Set the area of the temporary effect stored on the server.\n    *\n    * \\param area The area of the effect.\n    * \\return void\n    */\n    static void SetRecordEffectArea(int area) noexcept;\n\n    /**\n    * \\brief Set the duration of the temporary effect stored on the server.\n    *\n    * \\param duration The duration of the effect.\n    * \\return void\n    */\n    static void SetRecordEffectDuration(int duration) noexcept;\n\n    /**\n    * \\brief Set the maximum magnitude of the temporary effect stored on the server.\n    *\n    * \\param magnitudeMax The maximum magnitude of the effect.\n    * \\return void\n    */\n    static void SetRecordEffectMagnitudeMax(int magnitudeMax) noexcept;\n\n    /**\n    * \\brief Set the minimum magnitude of the temporary effect stored on the server.\n    *\n    * \\param magnitudeMin The minimum magnitude of the effect.\n    * \\return void\n    */\n    static void SetRecordEffectMagnitudeMin(int magnitudeMin) noexcept;\n\n    /**\n    * \\brief Set the body part type of the temporary body part stored on the server\n    * (which then needs to be added to ARMOR or CLOTHING records) or set the body part\n    * type of the current record if it's a BODYPART.\n    *\n    * \\param partType The type of the body part.\n    * \\return void\n    */\n    static void SetRecordBodyPartType(unsigned int partType) noexcept;\n\n    /**\n    * \\brief Set the id of the male version of the temporary body part stored on the\n    * server.\n    *\n    * \\param partId The id of the body part.\n    * \\return void\n    */\n    static void SetRecordBodyPartIdForMale(const char* partId) noexcept;\n\n    /**\n    * \\brief Set the id of the female version of the temporary body part stored on the\n    * server.\n    *\n    * \\param partId The id of the body part.\n    * \\return void\n    */\n    static void SetRecordBodyPartIdForFemale(const char* partId) noexcept;\n\n    /**\n    * \\brief Set the id of the of the temporary inventory item stored on the server.\n    *\n    * \\param itemId The id of the inventory item.\n    * \\return void\n    */\n    static void SetRecordInventoryItemId(const char* itemId) noexcept;\n\n    /**\n    * \\brief Set the count of the of the temporary inventory item stored on the server.\n    *\n    * \\param count The count of the inventory item.\n    * \\return void\n    */\n    static void SetRecordInventoryItemCount(unsigned int count) noexcept;\n\n    /**\n    * \\brief Add a copy of the server's temporary record of the current specified\n    * type to the stored records.\n    *\n    * In the process, the server's temporary record will automatically be cleared\n    * so a new one can be set up.\n    *\n    * \\return void\n    */\n    static void AddRecord() noexcept;\n\n    /**\n    * \\brief Add a copy of the server's temporary effect to the temporary record\n    * of the current specified type.\n    *\n    * In the process, the server's temporary effect will automatically be cleared\n    * so a new one can be set up.\n    *\n    * \\return void\n    */\n    static void AddRecordEffect() noexcept;\n\n    /**\n    * \\brief Add a copy of the server's temporary body part to the temporary record\n    * of the current specified type.\n    *\n    * In the process, the server's temporary body part will automatically be cleared\n    * so a new one can be set up.\n    *\n    * \\return void\n    */\n    static void AddRecordBodyPart() noexcept;\n\n    /**\n    * \\brief Add a copy of the server's temporary inventory item to the temporary record\n    * of the current specified type.\n    *\n    * In the process, the server's temporary inventory item will automatically be cleared\n    * so a new one can be set up.\n    *\n    * Note: Any items added this way will be ignored if the record already has a valid\n    *       inventoryBaseId.\n    *\n    * \\return void\n    */\n    static void AddRecordInventoryItem() noexcept;\n\n    /**\n    * \\brief Send a RecordDynamic packet with the current specified record type.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendRecordDynamic(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n};\n\n#endif //OPENMW_RECORDSDYNAMICAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Server.cpp",
    "content": "#include \"Server.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/Version.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n#include <apps/openmw-mp/MasterClient.hpp>\n#include <Script/Script.hpp>\n\nstatic std::string tempFilename;\nstatic std::chrono::high_resolution_clock::time_point startupTime = std::chrono::high_resolution_clock::now();\n\nvoid ServerFunctions::LogMessage(unsigned short level, const char *message) noexcept\n{\n    LOG_MESSAGE_SIMPLE(level, \"[Script]: %s\", message);\n}\n\nvoid ServerFunctions::LogAppend(unsigned short level, const char *message) noexcept\n{\n    LOG_APPEND(level, \"[Script]: %s\", message);\n}\n\nvoid ServerFunctions::StopServer(int code) noexcept\n{\n    mwmp::Networking::getPtr()->stopServer(code);\n}\n\nvoid ServerFunctions::Kick(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Kicking player %s (%i)\", player->npc.mName.c_str(), player->getId());\n    mwmp::Networking::getPtr()->kickPlayer(player->guid);\n    player->setLoadState(Player::KICKED);\n}\n\nvoid ServerFunctions::BanAddress(const char *ipAddress) noexcept\n{\n    mwmp::Networking::getPtr()->banAddress(ipAddress);\n}\n\nvoid ServerFunctions::UnbanAddress(const char *ipAddress) noexcept\n{\n    mwmp::Networking::getPtr()->unbanAddress(ipAddress);\n}\n\nbool ServerFunctions::DoesFilePathExist(const char *filePath) noexcept\n{\n    return boost::filesystem::exists(filePath);\n}\n\nconst char *ServerFunctions::GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept\n{\n    if (!boost::filesystem::exists(folderPath)) return \"invalid\";\n\n    boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end\n\n    for (boost::filesystem::directory_iterator itr(folderPath); itr != end_itr; ++itr)\n    {\n        if (Misc::StringUtils::ciEqual(itr->path().filename().string(), filename))\n        {\n            tempFilename = itr->path().filename().string();\n            return tempFilename.c_str();\n        }\n    }\n    return \"invalid\";\n}\n\nconst char* ServerFunctions::GetDataPath() noexcept\n{\n    return Script::GetModDir();\n}\n\nunsigned int ServerFunctions::GetMillisecondsSinceServerStart() noexcept\n{\n    std::chrono::high_resolution_clock::time_point currentTime = std::chrono::high_resolution_clock::now();\n    std::chrono::milliseconds milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startupTime);\n    return milliseconds.count();\n}\n\nconst char *ServerFunctions::GetOperatingSystemType() noexcept\n{\n    static const std::string operatingSystemType = Utils::getOperatingSystemType();\n    return operatingSystemType.c_str();\n}\n\nconst char *ServerFunctions::GetArchitectureType() noexcept\n{\n    static const std::string architectureType = Utils::getArchitectureType();\n    return architectureType.c_str();\n}\n\nconst char *ServerFunctions::GetServerVersion() noexcept\n{\n    return TES3MP_VERSION;\n}\n\nconst char *ServerFunctions::GetProtocolVersion() noexcept\n{\n    static std::string version = std::to_string(TES3MP_PROTO_VERSION);\n    return version.c_str();\n}\n\nint ServerFunctions::GetAvgPing(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, -1);\n    return mwmp::Networking::get().getAvgPing(player->guid);\n}\n\nconst char *ServerFunctions::GetIP(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n    RakNet::SystemAddress addr = mwmp::Networking::getPtr()->getSystemAddress(player->guid);\n    return addr.ToString(false);\n}\n\nunsigned short ServerFunctions::GetPort() noexcept\n{\n    return mwmp::Networking::get().getPort();\n}\n\nunsigned int ServerFunctions::GetMaxPlayers() noexcept\n{\n    return mwmp::Networking::get().maxConnections();\n}\n\nbool ServerFunctions::HasPassword() noexcept\n{\n    return mwmp::Networking::get().isPassworded();\n}\n\nbool ServerFunctions::GetDataFileEnforcementState() noexcept\n{\n    return mwmp::Networking::getPtr()->getDataFileEnforcementState();\n}\n\nbool ServerFunctions::GetScriptErrorIgnoringState() noexcept\n{\n    return mwmp::Networking::getPtr()->getScriptErrorIgnoringState();\n}\n\nvoid ServerFunctions::SetGameMode(const char *gameMode) noexcept\n{\n    if (mwmp::Networking::getPtr()->getMasterClient())\n        mwmp::Networking::getPtr()->getMasterClient()->SetModname(gameMode);\n}\n\nvoid ServerFunctions::SetHostname(const char *name) noexcept\n{\n    if (mwmp::Networking::getPtr()->getMasterClient())\n        mwmp::Networking::getPtr()->getMasterClient()->SetHostname(name);\n}\n\nvoid ServerFunctions::SetServerPassword(const char *password) noexcept\n{\n    mwmp::Networking::getPtr()->setServerPassword(password);\n}\n\nvoid ServerFunctions::SetDataFileEnforcementState(bool state) noexcept\n{\n    mwmp::Networking::getPtr()->setDataFileEnforcementState(state);\n}\n\nvoid ServerFunctions::SetScriptErrorIgnoringState(bool state) noexcept\n{\n    mwmp::Networking::getPtr()->setScriptErrorIgnoringState(state);\n}\n\nvoid ServerFunctions::SetRuleString(const char *key, const char *value) noexcept\n{\n    auto mc = mwmp::Networking::getPtr()->getMasterClient();\n    if (mc)\n        mc->SetRuleString(key, value);\n}\n\nvoid ServerFunctions::SetRuleValue(const char *key, double value) noexcept\n{\n    auto mc = mwmp::Networking::getPtr()->getMasterClient();\n    if (mc)\n        mc->SetRuleValue(key, value);\n}\n\nvoid ServerFunctions::AddDataFileRequirement(const char *dataFilename, const char *checksumString) noexcept\n{\n    auto &samples = mwmp::Networking::getPtr()->getSamples();\n    \n    auto it = std::find_if(samples.begin(), samples.end(), [&dataFilename](mwmp::PacketPreInit::PluginPair &item) {\n        return item.first == dataFilename;\n    });\n\n    if (it != samples.end())\n    {\n        // If this is a filename we've added before, ensure our new checksumString for it isn't empty\n        if (strlen(checksumString) != 0)\n            it->second.push_back((unsigned)std::stoul(checksumString));\n    }\n    else\n    {\n        mwmp::PacketPreInit::HashList checksumList;\n\n        unsigned checksum = 0;\n\n        if (strlen(checksumString) != 0)\n        {\n            checksum = (unsigned) std::stoul(checksumString);\n            checksumList.push_back(checksum);\n        }\n        samples.emplace_back(dataFilename, checksumList);\n\n        auto masterClient = mwmp::Networking::getPtr()->getMasterClient();\n        \n        if (masterClient)\n            masterClient->PushPlugin({dataFilename, checksum});\n    }\n}\n\n// All methods below are deprecated versions of methods from above\n\nbool ServerFunctions::DoesFileExist(const char *filePath) noexcept\n{\n    return DoesFilePathExist(filePath);\n}\n\nconst char* ServerFunctions::GetModDir() noexcept\n{\n    return GetDataPath();\n}\n\nbool ServerFunctions::GetPluginEnforcementState() noexcept\n{\n    return mwmp::Networking::getPtr()->getDataFileEnforcementState();\n}\n\nvoid ServerFunctions::SetPluginEnforcementState(bool state) noexcept\n{\n    SetDataFileEnforcementState(state);\n}\n\nvoid ServerFunctions::AddPluginHash(const char *pluginName, const char *checksumString) noexcept\n{\n    AddDataFileRequirement(pluginName, checksumString);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Server.hpp",
    "content": "#ifndef OPENMW_SERVERAPI_HPP\n#define OPENMW_SERVERAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define SERVERAPI \\\n    {\"LogMessage\",                      ServerFunctions::LogMessage},\\\n    {\"LogAppend\",                       ServerFunctions::LogAppend},\\\n    \\\n    {\"StopServer\",                      ServerFunctions::StopServer},\\\n    \\\n    {\"Kick\",                            ServerFunctions::Kick},\\\n    {\"BanAddress\",                      ServerFunctions::BanAddress},\\\n    {\"UnbanAddress\",                    ServerFunctions::UnbanAddress},\\\n    \\\n    {\"DoesFilePathExist\",               ServerFunctions::DoesFilePathExist},\\\n    {\"GetCaseInsensitiveFilename\",      ServerFunctions::GetCaseInsensitiveFilename},\\\n    {\"GetDataPath\",                     ServerFunctions::GetDataPath},\\\n    {\"GetMillisecondsSinceServerStart\", ServerFunctions::GetMillisecondsSinceServerStart},\\\n    {\"GetOperatingSystemType\",          ServerFunctions::GetOperatingSystemType},\\\n    {\"GetArchitectureType\",             ServerFunctions::GetArchitectureType},\\\n    {\"GetServerVersion\",                ServerFunctions::GetServerVersion},\\\n    {\"GetProtocolVersion\",              ServerFunctions::GetProtocolVersion},\\\n    {\"GetAvgPing\",                      ServerFunctions::GetAvgPing},\\\n    {\"GetIP\",                           ServerFunctions::GetIP},\\\n    {\"GetMaxPlayers\",                   ServerFunctions::GetMaxPlayers},\\\n    {\"GetPort\",                         ServerFunctions::GetPort},\\\n    {\"HasPassword\",                     ServerFunctions::HasPassword},\\\n    {\"GetDataFileEnforcementState\",     ServerFunctions::GetDataFileEnforcementState},\\\n    {\"GetScriptErrorIgnoringState\",     ServerFunctions::GetScriptErrorIgnoringState},\\\n    \\\n    {\"SetGameMode\",                     ServerFunctions::SetGameMode},\\\n    {\"SetHostname\",                     ServerFunctions::SetHostname},\\\n    {\"SetServerPassword\",               ServerFunctions::SetServerPassword},\\\n    {\"SetDataFileEnforcementState\",     ServerFunctions::SetDataFileEnforcementState},\\\n    {\"SetScriptErrorIgnoringState\",     ServerFunctions::SetScriptErrorIgnoringState},\\\n    {\"SetRuleString\",                   ServerFunctions::SetRuleString},\\\n    {\"SetRuleValue\",                    ServerFunctions::SetRuleValue},\\\n    \\\n    {\"AddDataFileRequirement\",          ServerFunctions::AddDataFileRequirement},\\\n    \\\n    {\"DoesFileExist\",                   ServerFunctions::DoesFileExist},\\\n    {\"GetModDir\",                       ServerFunctions::GetModDir},\\\n    {\"GetPluginEnforcementState\",       ServerFunctions::GetPluginEnforcementState},\\\n    {\"SetPluginEnforcementState\",       ServerFunctions::SetPluginEnforcementState},\\\n    {\"AddPluginHash\",                   ServerFunctions::AddPluginHash}\n\nclass ServerFunctions\n{\npublic:\n\n    /**\n    * \\brief Write a log message with its own timestamp.\n    *\n    * It will have \"[Script]:\" prepended to it so as to mark it as a script-generated log message.\n    *\n    * \\param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,\n    *              3 for LOG_ERROR, 4 for LOG_FATAL).\n    * \\param message The message logged.\n    * \\return void\n    */\n    static void LogMessage(unsigned short level, const char *message) noexcept;\n\n    /**\n    * \\brief Write a log message without its own timestamp.\n    *\n    * It will have \"[Script]:\" prepended to it so as to mark it as a script-generated log message.\n    *\n    * \\param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,\n    *              3 for LOG_ERROR, 4 for LOG_FATAL).\n    * \\param message The message logged.\n    * \\return void\n    */\n    static void LogAppend(unsigned short level, const char *message) noexcept;\n\n    /**\n    * \\brief Shut down the server.\n    *\n    * \\param code The shutdown code.\n    * \\return void\n    */\n    static void StopServer(int code) noexcept;\n\n    /**\n    * \\brief Kick a certain player from the server.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void Kick(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Ban a certain IP address from the server.\n    *\n    * \\param ipAddress The IP address.\n    * \\return void\n    */\n    static void BanAddress(const char *ipAddress) noexcept;\n\n    /**\n    * \\brief Unban a certain IP address from the server.\n    *\n    * \\param ipAddress The IP address.\n    * \\return void\n    */\n    static void UnbanAddress(const char *ipAddress) noexcept;\n\n    /**\n    * \\brief Check whether a certain file path exists.\n    *\n    * This will be a case sensitive check on case sensitive filesystems.\n    *\n    * Whenever you want to enforce case insensitivity, use GetCaseInsensitiveFilename() instead.\n    *\n    * \\return Whether the file exists or not.\n    */\n    static bool DoesFilePathExist(const char *filePath) noexcept;\n\n    /**\n    * \\brief Get the first filename in a folder that has a case insensitive match with the filename\n    * argument.\n    *\n    * This is used to retain case insensitivity when opening data files on Linux.\n    *\n    * \\return The filename that matches.\n    */\n    static const char *GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept;\n\n    /**\n    * \\brief Get the path of the server's data folder.\n    *\n    * \\return The data path.\n    */\n    static const char *GetDataPath() noexcept;\n\n    /**\n    * \\brief Get the milliseconds elapsed since the server was started.\n    *\n    * \\return The time since the server's startup in milliseconds.\n    */\n    static unsigned int GetMillisecondsSinceServerStart() noexcept;\n\n    /**\n    * \\brief Get the type of the operating system used by the server.\n    *\n    * Note: Currently, the type can be \"Windows\", \"Linux\", \"OS X\" or \"Unknown OS\".\n    *\n    * \\return The type of the operating system.\n    */\n    static const char *GetOperatingSystemType() noexcept;\n\n    /**\n    * \\brief Get the architecture type used by the server.\n    *\n    * Note: Currently, the type can be \"64-bit\", \"32-bit\", \"ARMv#\" or \"Unknown architecture\".\n    *\n    * \\return The architecture type.\n    */\n    static const char *GetArchitectureType() noexcept;\n\n    /**\n    * \\brief Get the TES3MP version of the server.\n    *\n    * \\return The server version.\n    */\n    static const char *GetServerVersion() noexcept;\n\n    /**\n    * \\brief Get the protocol version of the server.\n    *\n    * \\return The protocol version.\n    */\n    static const char *GetProtocolVersion() noexcept;\n\n    /**\n    * \\brief Get the average ping of a certain player.\n    *\n    * \\param pid The player ID.\n    * \\return The average ping.\n    */\n    static int GetAvgPing(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the IP address of a certain player.\n    *\n    * \\param pid The player ID.\n    * \\return The IP address.\n    */\n    static const char* GetIP(unsigned short pid) noexcept;\n\n    /**\n     * \\brief Get the port used by the server.\n     *\n     * \\return The port.\n     */\n    static unsigned short GetPort() noexcept;\n\n    /**\n     * \\brief Get the maximum number of players.\n     *\n     * \\return Max players\n     */\n    static unsigned int GetMaxPlayers() noexcept;\n\n    /**\n     * \\brief Checking if the server requires a password to connect.\n     *\n     * \\return Whether the server requires a password\n     */\n    static bool HasPassword() noexcept;\n\n    /**\n    * \\brief Get the data file enforcement state of the server.\n    *\n    * If true, clients are required to use the same data files as set for the server.\n    *\n    * \\return The enforcement state.\n    */\n    static bool GetDataFileEnforcementState() noexcept;\n\n    /**\n    * \\brief Get the script error ignoring state of the server.\n    *\n    * If true, script errors will not crash the server.\n    *\n    * \\return The script error ignoring state.\n    */\n    static bool GetScriptErrorIgnoringState() noexcept;\n\n    /**\n    * \\brief Set the game mode of the server, as displayed in the server browser.\n    *\n    * \\param gameMode The new game mode.\n    * \\return void\n    */\n    static void SetGameMode(const char* gameMode) noexcept;\n\n    /**\n    * \\brief Set the name of the server, as displayed in the server browser.\n    *\n    * \\param name The new name.\n    * \\return void\n    */\n    static void SetHostname(const char* name) noexcept;\n\n    /**\n    * \\brief Set the password required to join the server.\n    *\n    * \\param password The password.\n    * \\return void\n    */\n    static void SetServerPassword(const char *password) noexcept;\n\n    /**\n    * \\brief Set the data file enforcement state of the server.\n    *\n    * If true, clients are required to use the same data files as set for the server.\n    *\n    * \\param state The new enforcement state.\n    * \\return void\n    */\n    static void SetDataFileEnforcementState(bool state) noexcept;\n\n    /**\n    * \\brief Set whether script errors should be ignored or not.\n    *\n    * If true, script errors will not crash the server, but could have any number\n    * of unforeseen consequences, which is why this is a highly experimental\n    * setting.\n    *\n    * \\param state The new script error ignoring state.\n    * \\return void\n    */\n    static void SetScriptErrorIgnoringState(bool state) noexcept;\n\n    /**\n    * \\brief Set a rule string for the server details displayed in the server browser.\n    *\n    * \\param key The name of the rule.\n    * \\param value The string value of the rule.\n    * \\return void\n    */\n    static void SetRuleString(const char *key, const char *value) noexcept;\n\n    /**\n    * \\brief Set a rule value for the server details displayed in the server browser.\n    *\n    * \\param key The name of the rule.\n    * \\param value The numerical value of the rule.\n    * \\return void\n    */\n    static void SetRuleValue(const char *key, double value) noexcept;\n\n    /**\n     * \\brief Add a data file and a corresponding CRC32 checksum to the data file loadout\n     *        that connecting clients need to match.\n     *\n     * It can be used multiple times to set multiple checksums for the same data file.\n     *\n     * Note: If an empty string is provided for the checksum, a checksum will not be\n     *       required for that data file.\n     *\n     * @param dataFilename The filename of the data file.\n     * @param checksumString A string with the CRC32 checksum required.\n     */\n    static void AddDataFileRequirement(const char *dataFilename, const char *checksumString) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static bool DoesFileExist(const char *filePath) noexcept;\n    static const char *GetModDir() noexcept;\n    static bool GetPluginEnforcementState() noexcept;\n    static void SetPluginEnforcementState(bool state) noexcept;\n    static void AddPluginHash(const char *pluginName, const char *checksumString) noexcept;\n};\n\n#endif //OPENMW_SERVERAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Settings.cpp",
    "content": "#include \"Settings.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\n#include <iostream>\n\nvoid SettingFunctions::SetDifficulty(unsigned short pid, int difficulty)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->difficulty = difficulty;\n}\n\nvoid SettingFunctions::SetEnforcedLogLevel(unsigned short pid, int enforcedLogLevel)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->enforcedLogLevel = enforcedLogLevel;\n}\n\nvoid SettingFunctions::SetPhysicsFramerate(unsigned short pid, double physicsFramerate)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->physicsFramerate = physicsFramerate;\n}\n\nvoid SettingFunctions::SetConsoleAllowed(unsigned short pid, bool state)\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->consoleAllowed = state;\n}\n\nvoid SettingFunctions::SetBedRestAllowed(unsigned short pid, bool state)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->bedRestAllowed = state;\n}\n\nvoid SettingFunctions::SetWildernessRestAllowed(unsigned short pid, bool state)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->wildernessRestAllowed = state;\n}\n\nvoid SettingFunctions::SetWaitAllowed(unsigned short pid, bool state)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->waitAllowed = state;\n}\n\nvoid SettingFunctions::SetGameSettingValue(unsigned short pid, const char* setting, const char* value) {\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    player->gameSettings[setting] = value;\n}\n\nvoid SettingFunctions::ClearGameSettingValues(unsigned short pid) {\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    player->gameSettings.clear();\n}\n\nvoid SettingFunctions::SetVRSettingValue(unsigned short pid, const char* setting, const char* value) {\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    player->vrSettings[setting] = value;\n}\n\nvoid SettingFunctions::ClearVRSettingValues(unsigned short pid) {\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    player->vrSettings.clear();\n}\n\nvoid SettingFunctions::SendSettings(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GAME_SETTINGS);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Settings.hpp",
    "content": "#ifndef OPENMW_SETTINGSAPI_HPP\n#define OPENMW_SETTINGSAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define SETTINGSAPI \\\n    {\"SetDifficulty\",               SettingFunctions::SetDifficulty},\\\n    {\"SetEnforcedLogLevel\",         SettingFunctions::SetEnforcedLogLevel},\\\n    {\"SetPhysicsFramerate\",         SettingFunctions::SetPhysicsFramerate},\\\n    \\\n    {\"SetConsoleAllowed\",           SettingFunctions::SetConsoleAllowed},\\\n    {\"SetBedRestAllowed\",           SettingFunctions::SetBedRestAllowed},\\\n    {\"SetWildernessRestAllowed\",    SettingFunctions::SetWildernessRestAllowed},\\\n    {\"SetWaitAllowed\",              SettingFunctions::SetWaitAllowed},\\\n    \\\n    {\"SetGameSettingValue\",         SettingFunctions::SetGameSettingValue},\\\n    {\"ClearGameSettingValues\",      SettingFunctions::ClearGameSettingValues},\\\n    \\\n    {\"SetVRSettingValue\",           SettingFunctions::SetVRSettingValue},\\\n    {\"ClearVRSettingValues\",        SettingFunctions::ClearVRSettingValues},\\\n    \\\n    {\"SendSettings\",                SettingFunctions::SendSettings}\n\nclass SettingFunctions\n{\npublic:\n\n    /**\n    * \\brief Set the difficulty for a player.\n    *\n    * This changes the difficulty for that player in the server memory, but does not by itself\n    * send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param difficulty The difficulty.\n    * \\return void\n    */\n    static void SetDifficulty(unsigned short pid, int difficulty);\n\n    /**\n    * \\brief Set the client log level enforced for a player.\n    *\n    * This changes the enforced log level for that player in the server memory, but does not by itself\n    * send a packet.\n    *\n    * Enforcing a certain log level is necessary to prevent players from learning information from\n    * their console window that they are otherwise unable to obtain, such as the locations of\n    * other players.\n    *\n    * If you do not wish to enforce a log level, simply set enforcedLogLevel to -1\n    *\n    * \\param pid The player ID.\n    * \\param enforcedLogLevel The enforced log level.\n    * \\return void\n    */\n    static void SetEnforcedLogLevel(unsigned short pid, int enforcedLogLevel);\n\n    /**\n    * \\brief Set the physics framerate for a player.\n    *\n    * This changes the physics framerate for that player in the server memory, but does not by itself\n    * send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param physicsFramerate The physics framerate.\n    * \\return void\n    */\n    static void SetPhysicsFramerate(unsigned short pid, double physicsFramerate);\n\n    /**\n    * \\brief Set whether the console is allowed for a player.\n    *\n    * This changes the console permission for that player in the server memory, but does not\n    * by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param state The console permission state.\n    * \\return void\n    */\n    static void SetConsoleAllowed(unsigned short pid, bool state);\n\n    /**\n    * \\brief Set whether resting in beds is allowed for a player.\n    *\n    * This changes the resting permission for that player in the server memory, but does not\n    * by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param state The resting permission state.\n    * \\return void\n    */\n    static void SetBedRestAllowed(unsigned short pid, bool state);\n\n    /**\n    * \\brief Set whether resting in the wilderness is allowed for a player.\n    *\n    * This changes the resting permission for that player in the server memory, but does not\n    * by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param state The resting permission state.\n    * \\return void\n    */\n    static void SetWildernessRestAllowed(unsigned short pid, bool state);\n\n    /**\n    * \\brief Set whether waiting is allowed for a player.\n    *\n    * This changes the waiting permission for that player in the server memory, but does not\n    * by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param state The waiting permission state.\n    * \\return void\n    */\n    static void SetWaitAllowed(unsigned short pid, bool state);\n\n    /**\n    * \\brief Set value for a game setting.\n    *\n    * This overrides the setting value set in OpenMW Launcher. Only applies to the Game category.\n    *\n    * \\param pid The player ID.\n    * \\param setting Name of a setting in the Game category\n    * \\param value Value of the setting (as a string)\n    * \\return void\n    */\n    static void SetGameSettingValue(unsigned short pid, const char* setting, const char* value);\n\n    /**\n    * \\brief Clear the Game setting values stored for a player.\n    *\n    * Clear any changes done by SetGameSettingValue()\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void ClearGameSettingValues(unsigned short pid);\n\n    /**\n    * \\brief Set value for a VR setting.\n    *\n    * This overrides the setting value set in OpenMW Launcher. Only applies to the VR category.\n    *\n    * \\param pid The player ID.\n    * \\param setting Name of a setting in the VR category\n    * \\param value Value of the setting (as a string)\n    * \\return void\n    */\n    static void SetVRSettingValue(unsigned short pid, const char* setting, const char* value);\n\n    /**\n    * \\brief Clear the VR setting values stored for a player.\n    *\n    * Clear any changes done by SetVRSettingValue()\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void ClearVRSettingValues(unsigned short pid);\n\n    /**\n    * \\brief Send a PlayerSettings packet to the player affected by it.\n    *\n    * \\param pid The player ID to send it to.\n    * \\return void\n    */\n    static void SendSettings(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n};\n\n#endif //OPENMW_SETTINGSAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Shapeshift.cpp",
    "content": "#include \"Shapeshift.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\n#include <iostream>\n\ndouble ShapeshiftFunctions::GetScale(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->scale;\n}\n\nbool ShapeshiftFunctions::IsWerewolf(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->isWerewolf;\n}\n\nconst char *ShapeshiftFunctions::GetCreatureRefId(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->creatureRefId.c_str();\n}\n\nbool ShapeshiftFunctions::GetCreatureNameDisplayState(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->displayCreatureName;\n}\n\nvoid ShapeshiftFunctions::SetScale(unsigned short pid, double scale) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->scale = scale;\n}\n\nvoid ShapeshiftFunctions::SetWerewolfState(unsigned short pid, bool isWerewolf) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->isWerewolf = isWerewolf;\n}\n\nvoid ShapeshiftFunctions::SetCreatureRefId(unsigned short pid, const char *refId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->creatureRefId = refId;\n}\n\nvoid ShapeshiftFunctions::SetCreatureNameDisplayState(unsigned short pid, bool displayState) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->displayCreatureName = displayState;\n}\n\nvoid ShapeshiftFunctions::SendShapeshift(unsigned short pid)\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SHAPESHIFT);\n    packet->setPlayer(player);\n\n    packet->Send(false);\n    packet->Send(true);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Shapeshift.hpp",
    "content": "#ifndef OPENMW_SHAPESHIFTAPI_HPP\n#define OPENMW_SHAPESHIFTAPI_HPP\n\n#include \"../Types.hpp\"\n\n#define SHAPESHIFTAPI \\\n    {\"GetScale\",                    ShapeshiftFunctions::GetScale},\\\n    {\"IsWerewolf\",                  ShapeshiftFunctions::IsWerewolf},\\\n    {\"GetCreatureRefId\",            ShapeshiftFunctions::GetCreatureRefId},\\\n    {\"GetCreatureNameDisplayState\", ShapeshiftFunctions::GetCreatureNameDisplayState},\\\n    \\\n    {\"SetScale\",                    ShapeshiftFunctions::SetScale},\\\n    {\"SetWerewolfState\",            ShapeshiftFunctions::SetWerewolfState},\\\n    {\"SetCreatureRefId\",            ShapeshiftFunctions::SetCreatureRefId},\\\n    {\"SetCreatureNameDisplayState\", ShapeshiftFunctions::SetCreatureNameDisplayState},\\\n    \\\n    {\"SendShapeshift\",              ShapeshiftFunctions::SendShapeshift}\n\nclass ShapeshiftFunctions\n{\npublic:\n\n    /**\n    * \\brief Get the scale of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The scale.\n    */\n    static double GetScale(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Check whether a player is a werewolf.\n    *\n    * This is based on the last PlayerShapeshift packet received or sent for that player.\n    *\n    * \\param pid The player ID.\n    * \\return The werewolf state.\n    */\n    static bool IsWerewolf(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the refId of the creature the player is disguised as.\n    *\n    * \\param pid The player ID.\n    * \\return The creature refId.\n    */\n    static const char *GetCreatureRefId(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Check whether a player's name is replaced by that of the creature they are\n    *        disguised as when other players hover over them.\n    *\n    * This is based on the last PlayerShapeshift packet received or sent for that player.\n    *\n    * \\param pid The player ID.\n    * \\return The creature name display state.\n    */\n    static bool GetCreatureNameDisplayState(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the scale of a player.\n    *\n    * This changes the scale recorded for that player in the server memory, but\n    * does not by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param scale The new scale.\n    * \\return void\n    */\n    static void SetScale(unsigned short pid, double scale) noexcept;\n\n    /**\n    * \\brief Set the werewolf state of a player.\n    *\n    * This changes the werewolf state recorded for that player in the server memory, but\n    * does not by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param isWerewolf The new werewolf state.\n    * \\return void\n    */\n    static void SetWerewolfState(unsigned short pid, bool isWerewolf) noexcept;\n\n    /**\n    * \\brief Set the refId of the creature a player is disguised as.\n    *\n    * This changes the creature refId recorded for that player in the server memory, but\n    * does not by itself send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param refId The creature refId.\n    * \\return void\n    */\n    static void SetCreatureRefId(unsigned short pid, const char *refId) noexcept;\n\n    /**\n    * \\brief Set whether a player's name is replaced by that of the creature they are\n    *        disguised as when other players hover over them.\n    *\n    * \\param pid The player ID.\n    * \\param displayState The creature name display state.\n    * \\return void\n    */\n    static void SetCreatureNameDisplayState(unsigned short pid, bool displayState) noexcept;\n\n    /**\n    * \\brief Send a PlayerShapeshift packet about a player.\n    *\n    * This sends the packet to all players connected to the server. It is currently used\n    * only to communicate werewolf states.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendShapeshift(unsigned short pid);\n};\n\n#endif //OPENMW_SHAPESHIFTAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Spells.cpp",
    "content": "#include \"Spells.hpp\"\n\n#include <components/misc/stringops.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n\nusing namespace mwmp;\n\nstd::vector<ESM::ActiveEffect> storedActiveEffects;\n\nvoid SpellFunctions::ClearSpellbookChanges(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->spellbookChanges.spells.clear();\n}\n\nvoid SpellFunctions::ClearSpellsActiveChanges(unsigned short pid) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    player->spellsActiveChanges.activeSpells.clear();\n}\n\nvoid SpellFunctions::ClearCooldownChanges(unsigned short pid) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    player->cooldownChanges.clear();\n}\n\nunsigned int SpellFunctions::GetSpellbookChangesSize(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->spellbookChanges.spells.size();\n}\n\nunsigned int SpellFunctions::GetSpellbookChangesAction(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->spellbookChanges.action;\n}\n\nunsigned int SpellFunctions::GetSpellsActiveChangesSize(unsigned short pid) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->spellsActiveChanges.activeSpells.size();\n}\n\nunsigned int SpellFunctions::GetSpellsActiveChangesAction(unsigned short pid) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->spellsActiveChanges.action;\n}\n\nunsigned int SpellFunctions::GetCooldownChangesSize(unsigned short pid) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->cooldownChanges.size();\n}\n\nvoid SpellFunctions::SetSpellbookChangesAction(unsigned short pid, unsigned char action) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->spellbookChanges.action = action;\n}\n\nvoid SpellFunctions::SetSpellsActiveChangesAction(unsigned short pid, unsigned char action) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    player->spellsActiveChanges.action = action;\n}\n\nvoid SpellFunctions::AddSpell(unsigned short pid, const char* spellId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    ESM::Spell spell;\n    spell.mId = spellId;\n\n    player->spellbookChanges.spells.push_back(spell);\n}\n\nvoid SpellFunctions::AddSpellActive(unsigned short pid, const char* spellId, const char* displayName, bool stackingState) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::ActiveSpell spell;\n    spell.id = spellId;\n    spell.isStackingSpell = stackingState;\n    spell.params.mDisplayName = displayName;\n    spell.params.mEffects = storedActiveEffects;\n\n    player->spellsActiveChanges.activeSpells.push_back(spell);\n\n    storedActiveEffects.clear();\n}\n\nvoid SpellFunctions::AddSpellActiveEffect(unsigned short pid, int effectId, double magnitude, double duration, double timeLeft, int arg) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    ESM::ActiveEffect effect;\n    effect.mEffectId = effectId;\n    effect.mMagnitude = magnitude;\n    effect.mDuration = duration;\n    effect.mTimeLeft = timeLeft;\n    effect.mArg = arg;\n\n    storedActiveEffects.push_back(effect);\n}\n\nvoid SpellFunctions::AddCooldownSpell(unsigned short pid, const char* spellId, unsigned int startDay, double startHour) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::SpellCooldown spellCooldown;\n    spellCooldown.id = spellId;\n    spellCooldown.startTimestampDay = startDay;\n    spellCooldown.startTimestampHour = startHour;\n\n    player->cooldownChanges.push_back(spellCooldown);\n}\n\nconst char *SpellFunctions::GetSpellId(unsigned short pid, unsigned int index) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->spellbookChanges.spells.size())\n        return \"invalid\";\n\n    return player->spellbookChanges.spells.at(index).mId.c_str();\n}\n\nconst char* SpellFunctions::GetSpellsActiveId(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return \"invalid\";\n\n    return player->spellsActiveChanges.activeSpells.at(index).id.c_str();\n}\n\nconst char* SpellFunctions::GetSpellsActiveDisplayName(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return \"invalid\";\n\n    return player->spellsActiveChanges.activeSpells.at(index).params.mDisplayName.c_str();\n}\n\nbool SpellFunctions::GetSpellsActiveStackingState(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, false);\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return false;\n\n    return player->spellsActiveChanges.activeSpells.at(index).isStackingSpell;\n}\n\nunsigned int SpellFunctions::GetSpellsActiveEffectCount(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return 0;\n\n    return player->spellsActiveChanges.activeSpells.at(index).params.mEffects.size();\n}\n\nunsigned int SpellFunctions::GetSpellsActiveEffectId(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (spellIndex >= player->spellsActiveChanges.activeSpells.size())\n        return 0;\n\n    return player->spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mEffectId;\n}\n\nint SpellFunctions::GetSpellsActiveEffectArg(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (spellIndex >= player->spellsActiveChanges.activeSpells.size())\n        return 0;\n\n    return player->spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mArg;\n}\n\ndouble SpellFunctions::GetSpellsActiveEffectMagnitude(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (spellIndex >= player->spellsActiveChanges.activeSpells.size())\n        return 0;\n\n    return player->spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mMagnitude;\n}\n\ndouble SpellFunctions::GetSpellsActiveEffectDuration(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (spellIndex >= player->spellsActiveChanges.activeSpells.size())\n        return 0;\n\n    return player->spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mDuration;\n}\n\ndouble SpellFunctions::GetSpellsActiveEffectTimeLeft(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (spellIndex >= player->spellsActiveChanges.activeSpells.size())\n        return 0;\n\n    return player->spellsActiveChanges.activeSpells.at(spellIndex).params.mEffects.at(effectIndex).mTimeLeft;\n}\n\nbool SpellFunctions::DoesSpellsActiveHavePlayerCaster(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, false);\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return false;\n\n    return player->spellsActiveChanges.activeSpells.at(index).caster.isPlayer;\n}\n\nint SpellFunctions::GetSpellsActiveCasterPid(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, -1);\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return -1;\n\n    Player* caster = Players::getPlayer(player->spellsActiveChanges.activeSpells.at(index).caster.guid);\n\n    if (caster != nullptr)\n        return caster->getId();\n\n    return -1;\n}\n\nconst char* SpellFunctions::GetSpellsActiveCasterRefId(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return \"\";\n\n    return player->spellsActiveChanges.activeSpells.at(index).caster.refId.c_str();\n}\n\nunsigned int SpellFunctions::GetSpellsActiveCasterRefNum(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return 0;\n\n    return player->spellsActiveChanges.activeSpells.at(index).caster.refNum;\n}\n\nunsigned int SpellFunctions::GetSpellsActiveCasterMpNum(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (index >= player->spellsActiveChanges.activeSpells.size())\n        return 0;\n\n    return player->spellsActiveChanges.activeSpells.at(index).caster.mpNum;\n}\n\nconst char* SpellFunctions::GetCooldownSpellId(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, \"\");\n\n    if (index >= player->cooldownChanges.size())\n        return \"invalid\";\n\n    return player->cooldownChanges.at(index).id.c_str();\n}\n\nunsigned int SpellFunctions::GetCooldownStartDay(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    if (index >= player->cooldownChanges.size())\n        return 0;\n\n    return player->cooldownChanges.at(index).startTimestampDay;\n}\n\ndouble SpellFunctions::GetCooldownStartHour(unsigned short pid, unsigned int index) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0.0);\n\n    if (index >= player->cooldownChanges.size())\n        return 0.0;\n\n    return player->cooldownChanges.at(index).startTimestampHour;\n}\n\nvoid SpellFunctions::SendSpellbookChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SPELLBOOK);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid SpellFunctions::SendSpellsActiveChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket* packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SPELLS_ACTIVE);\n    packet->setPlayer(player);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid SpellFunctions::SendCooldownChanges(unsigned short pid) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket* packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_COOLDOWNS);\n    packet->setPlayer(player);\n    packet->Send(false);\n}\n\n// All methods below are deprecated versions of methods from above\n\nvoid SpellFunctions::InitializeSpellbookChanges(unsigned short pid) noexcept\n{\n    ClearSpellbookChanges(pid);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Spells.hpp",
    "content": "#ifndef OPENMW_SPELLAPI_HPP\n#define OPENMW_SPELLAPI_HPP\n\n#define SPELLAPI \\\n    {\"ClearSpellbookChanges\",            SpellFunctions::ClearSpellbookChanges},\\\n    {\"ClearSpellsActiveChanges\",         SpellFunctions::ClearSpellsActiveChanges},\\\n    {\"ClearCooldownChanges\",             SpellFunctions::ClearCooldownChanges},\\\n    \\\n    {\"GetSpellbookChangesSize\",          SpellFunctions::GetSpellbookChangesSize},\\\n    {\"GetSpellbookChangesAction\",        SpellFunctions::GetSpellbookChangesAction},\\\n    {\"GetSpellsActiveChangesSize\",       SpellFunctions::GetSpellsActiveChangesSize},\\\n    {\"GetSpellsActiveChangesAction\",     SpellFunctions::GetSpellsActiveChangesAction},\\\n    {\"GetCooldownChangesSize\",           SpellFunctions::GetCooldownChangesSize},\\\n    \\\n    {\"SetSpellbookChangesAction\",        SpellFunctions::SetSpellbookChangesAction},\\\n    {\"SetSpellsActiveChangesAction\",     SpellFunctions::SetSpellsActiveChangesAction},\\\n    \\\n    {\"AddSpell\",                         SpellFunctions::AddSpell},\\\n    {\"AddSpellActive\",                   SpellFunctions::AddSpellActive},\\\n    {\"AddSpellActiveEffect\",             SpellFunctions::AddSpellActiveEffect},\\\n    {\"AddCooldownSpell\",                 SpellFunctions::AddCooldownSpell},\\\n    \\\n    {\"GetSpellId\",                       SpellFunctions::GetSpellId},\\\n    {\"GetSpellsActiveId\",                SpellFunctions::GetSpellsActiveId},\\\n    {\"GetSpellsActiveDisplayName\",       SpellFunctions::GetSpellsActiveDisplayName},\\\n    {\"GetSpellsActiveStackingState\",     SpellFunctions::GetSpellsActiveStackingState},\\\n    {\"GetSpellsActiveEffectCount\",       SpellFunctions::GetSpellsActiveEffectCount},\\\n    {\"GetSpellsActiveEffectId\",          SpellFunctions::GetSpellsActiveEffectId},\\\n    {\"GetSpellsActiveEffectArg\",         SpellFunctions::GetSpellsActiveEffectArg},\\\n    {\"GetSpellsActiveEffectMagnitude\",   SpellFunctions::GetSpellsActiveEffectMagnitude},\\\n    {\"GetSpellsActiveEffectDuration\",    SpellFunctions::GetSpellsActiveEffectDuration},\\\n    {\"GetSpellsActiveEffectTimeLeft\",    SpellFunctions::GetSpellsActiveEffectTimeLeft},\\\n    \\\n    {\"DoesSpellsActiveHavePlayerCaster\", SpellFunctions::DoesSpellsActiveHavePlayerCaster},\\\n    {\"GetSpellsActiveCasterPid\",         SpellFunctions::GetSpellsActiveCasterPid},\\\n    {\"GetSpellsActiveCasterRefId\",       SpellFunctions::GetSpellsActiveCasterRefId},\\\n    {\"GetSpellsActiveCasterRefNum\",      SpellFunctions::GetSpellsActiveCasterRefNum},\\\n    {\"GetSpellsActiveCasterMpNum\",       SpellFunctions::GetSpellsActiveCasterMpNum},\\\n    \\\n    {\"GetCooldownSpellId\",               SpellFunctions::GetCooldownSpellId},\\\n    {\"GetCooldownStartDay\",              SpellFunctions::GetCooldownStartDay},\\\n    {\"GetCooldownStartHour\",             SpellFunctions::GetCooldownStartHour},\\\n    \\\n    {\"SendSpellbookChanges\",             SpellFunctions::SendSpellbookChanges},\\\n    {\"SendSpellsActiveChanges\",          SpellFunctions::SendSpellsActiveChanges},\\\n    {\"SendCooldownChanges\",              SpellFunctions::SendCooldownChanges},\\\n    \\\n    {\"InitializeSpellbookChanges\",     SpellFunctions::InitializeSpellbookChanges}\n\nclass SpellFunctions\n{\npublic:\n\n    /**\n    * \\brief Clear the last recorded spellbook changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerSpellbook packets.\n    *\n    * \\param pid The player ID whose spellbook changes should be used.\n    * \\return void\n    */\n    static void ClearSpellbookChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Clear the last recorded spells active changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerSpellsActive packets.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\return void\n    */\n    static void ClearSpellsActiveChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Clear the last recorded cooldown changes for a player.\n    *\n    * This is used to initialize the sending of new PlayerCooldown packets.\n    *\n    * \\param pid The player ID whose cooldown changes should be used.\n    * \\return void\n    */\n    static void ClearCooldownChanges(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest spellbook changes.\n    *\n    * \\param pid The player ID whose spellbook changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetSpellbookChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the action type used in a player's latest spellbook changes.\n    *\n    * \\param pid The player ID whose spellbook changes should be used.\n    * \\return The action type (0 for SET, 1 for ADD, 2 for REMOVE).\n    */\n    static unsigned int GetSpellbookChangesAction(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\return The number of indexes for spells active changes.\n    */\n    static unsigned int GetSpellsActiveChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the action type used in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\return The action type (0 for SET, 1 for ADD, 2 for REMOVE).\n    */\n    static unsigned int GetSpellsActiveChangesAction(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the number of indexes in a player's latest cooldown changes.\n    *\n    * \\param pid The player ID whose cooldown changes should be used.\n    * \\return The number of indexes.\n    */\n    static unsigned int GetCooldownChangesSize(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the action type in a player's spellbook changes.\n    *\n    * \\param pid The player ID whose spellbook changes should be used.\n    * \\param action The action (0 for SET, 1 for ADD, 2 for REMOVE).\n    * \\return void\n    */\n    static void SetSpellbookChangesAction(unsigned short pid, unsigned char action) noexcept;\n\n    /**\n    * \\brief Set the action type in a player's spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param action The action (0 for SET, 1 for ADD, 2 for REMOVE).\n    * \\return void\n    */\n    static void SetSpellsActiveChangesAction(unsigned short pid, unsigned char action) noexcept;\n\n    /**\n    * \\brief Add a new spell to the spellbook changes for a player.\n    *\n    * \\param pid The player ID whose spellbook changes should be used.\n    * \\param spellId The spellId of the spell.\n    * \\return void\n    */\n    static void AddSpell(unsigned short pid, const char* spellId) noexcept;\n\n    /**\n    * \\brief Add a new active spell to the spells active changes for a player,\n    *        using the temporary effect values stored so far.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param spellId The spellId of the spell.\n    * \\param displayName The displayName of the spell.\n    * \\param stackingState Whether the spell should stack with other instances of itself.\n    * \\return void\n    */\n    static void AddSpellActive(unsigned short pid, const char* spellId, const char* displayName, bool stackingState) noexcept;\n\n    /**\n    * \\brief Add a new effect to the next active spell that will be added to a player.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param effectId The id of the effect.\n    * \\param magnitude The magnitude of the effect.\n    * \\param duration The duration of the effect.\n    * \\param timeLeft The timeLeft for the effect.\n    * \\param arg The arg of the effect when applicable, e.g. the skill used for Fortify Skill or the attribute\n    *            used for Fortify Attribute.\n    * \\return void\n    */\n    static void AddSpellActiveEffect(unsigned short pid, int effectId, double magnitude, double duration, double timeLeft, int arg) noexcept;\n\n    /**\n    * \\brief Add a new cooldown spell to the cooldown changes for a player.\n    *\n    * \\param pid The player ID whose cooldown changes should be used.\n    * \\param spellId The spellId of the spell.\n    * \\param startDay The day on which the cooldown starts.\n    * \\param startHour The hour at which the cooldown starts.\n    * \\return void\n    */\n    static void AddCooldownSpell(unsigned short pid, const char* spellId, unsigned int startDay, double startHour) noexcept;\n\n    /**\n    * \\brief Get the spell id at a certain index in a player's latest spellbook changes.\n    *\n    * \\param pid The player ID whose spellbook changes should be used.\n    * \\param index The index of the spell.\n    * \\return The spell id.\n    */\n    static const char *GetSpellId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the spell id at a certain index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return The spell id.\n    */\n    static const char* GetSpellsActiveId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the spell display name at a certain index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return The spell display name.\n    */\n    static const char* GetSpellsActiveDisplayName(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the spell stacking state at a certain index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return The spell stacking state.\n    */\n    static bool GetSpellsActiveStackingState(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the number of effects at an index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return The number of effects.\n    */\n    static unsigned int GetSpellsActiveEffectCount(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the id for an effect index at a spell index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The id of the effect.\n    */\n    static unsigned int GetSpellsActiveEffectId(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the arg for an effect index at a spell index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The arg of the effect.\n    */\n    static int GetSpellsActiveEffectArg(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the magnitude for an effect index at a spell index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The magnitude of the effect.\n    */\n    static double GetSpellsActiveEffectMagnitude(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the duration for an effect index at a spell index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The duration of the effect.\n    */\n    static double GetSpellsActiveEffectDuration(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Get the time left for an effect index at a spell index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param spellIndex The index of the spell.\n    * \\param effectIndex The index of the effect.\n    * \\return The time left for the effect.\n    */\n    static double GetSpellsActiveEffectTimeLeft(unsigned short pid, unsigned int spellIndex, unsigned int effectIndex) noexcept;\n\n    /**\n    * \\brief Check whether the spell at a certain index in a player's latest spells active changes has a player\n    *        as its caster.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return Whether a player is the caster of the spell.\n    */\n    static bool DoesSpellsActiveHavePlayerCaster(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the player ID of the caster of the spell at a certain index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return The player ID of the caster.\n    */\n    static int GetSpellsActiveCasterPid(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refId of the actor caster of the spell at a certain index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return The refId of the caster.\n    */\n    static const char* GetSpellsActiveCasterRefId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the refNum of the actor caster of the spell at a certain index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return The refNum of the caster.\n    */\n    static unsigned int GetSpellsActiveCasterRefNum(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the mpNum of the actor caster of the spell at a certain index in a player's latest spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param index The index of the spell.\n    * \\return The mpNum of the caster.\n    */\n    static unsigned int GetSpellsActiveCasterMpNum(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the spell id at a certain index in a player's latest cooldown changes.\n    *\n    * \\param pid The player ID whose cooldown changes should be used.\n    * \\param index The index of the cooldown spell.\n    * \\return The spell id.\n    */\n    static const char* GetCooldownSpellId(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the starting day of the cooldown at a certain index in a player's latest cooldown changes.\n    *\n    * \\param pid The player ID whose cooldown changes should be used.\n    * \\param index The index of the cooldown spell.\n    * \\return The starting day of the cooldown.\n    */\n    static unsigned int GetCooldownStartDay(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the starting hour of the cooldown at a certain index in a player's latest cooldown changes.\n    *\n    * \\param pid The player ID whose cooldown changes should be used.\n    * \\param index The index of the cooldown spell.\n    * \\return The starting hour of the cooldown.\n    */\n    static double GetCooldownStartHour(unsigned short pid, unsigned int index) noexcept;\n\n    /**\n    * \\brief Send a PlayerSpellbook packet with a player's recorded spellbook changes.\n    *\n    * \\param pid The player ID whose spellbook changes should be used.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendSpellbookChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a PlayerSpellsActive packet with a player's recorded spells active changes.\n    *\n    * \\param pid The player ID whose spells active changes should be used.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendSpellsActiveChanges(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a PlayerCooldowns packet with a player's recorded cooldown changes.\n    *\n    * \\param pid The player ID whose cooldown changes should be used.\n    * \\return void\n    */\n    static void SendCooldownChanges(unsigned short pid) noexcept;\n\n    // All methods below are deprecated versions of methods from above\n\n    static void InitializeSpellbookChanges(unsigned short pid) noexcept;\n\nprivate:\n\n};\n\n#endif //OPENMW_SPELLAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Stats.cpp",
    "content": "#include \"Stats.hpp\"\n\n#include <iostream>\n\n#include <components/esm/attr.hpp>\n#include <components/esm/loadskil.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Networking.hpp>\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n\nint StatsFunctions::GetAttributeCount() noexcept\n{\n    return ESM::Attribute::Length;\n}\n\nint StatsFunctions::GetSkillCount() noexcept\n{\n    return ESM::Skill::Length;\n}\n\nint StatsFunctions::GetAttributeId(const char *name) noexcept\n{\n    for (int x = 0; x < ESM::Attribute::Length; x++)\n    {\n        if (Misc::StringUtils::ciEqual(name, ESM::Attribute::sAttributeNames[x]))\n        {\n            return x;\n        }\n    }\n\n    return -1;\n}\n\nint StatsFunctions::GetSkillId(const char *name) noexcept\n{\n    for (int x = 0; x < ESM::Skill::Length; x++)\n    {\n        if (Misc::StringUtils::ciEqual(name, ESM::Skill::sSkillNames[x]))\n        {\n            return x;\n        }\n    }\n\n    return -1;\n}\n\nconst char *StatsFunctions::GetAttributeName(unsigned short attributeId) noexcept\n{\n    if (attributeId >= ESM::Attribute::Length)\n        return \"invalid\";\n\n    return ESM::Attribute::sAttributeNames[attributeId].c_str();\n}\n\nconst char *StatsFunctions::GetSkillName(unsigned short skillId) noexcept\n{\n    if (skillId >= ESM::Skill::Length)\n        return \"invalid\";\n\n    return ESM::Skill::sSkillNames[skillId].c_str();\n}\n\nconst char *StatsFunctions::GetName(unsigned short pid) noexcept\n{\n\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->npc.mName.c_str();\n}\n\nconst char *StatsFunctions::GetRace(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->npc.mRace.c_str();\n}\n\nconst char *StatsFunctions::GetHead(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->npc.mHead.c_str();\n}\n\nconst char *StatsFunctions::GetHairstyle(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->npc.mHair.c_str();\n}\n\nint StatsFunctions::GetIsMale(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, false);\n\n    return player->npc.isMale();\n}\n\nconst char* StatsFunctions::GetModel(unsigned short pid) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->npc.mModel.c_str();\n}\n\nconst char *StatsFunctions::GetBirthsign(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->birthsign.c_str();\n}\n\nint StatsFunctions::GetLevel(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->creatureStats.mLevel;\n}\n\nint StatsFunctions::GetLevelProgress(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->npcStats.mLevelProgress;\n}\n\ndouble StatsFunctions::GetHealthBase(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->creatureStats.mDynamic[0].mBase;\n}\n\ndouble StatsFunctions::GetHealthCurrent(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->creatureStats.mDynamic[0].mCurrent;\n}\n\ndouble StatsFunctions::GetMagickaBase(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->creatureStats.mDynamic[1].mBase;\n}\n\ndouble StatsFunctions::GetMagickaCurrent(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->creatureStats.mDynamic[1].mCurrent;\n}\n\ndouble StatsFunctions::GetFatigueBase(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->creatureStats.mDynamic[2].mBase;\n}\n\ndouble StatsFunctions::GetFatigueCurrent(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    return player->creatureStats.mDynamic[2].mCurrent;\n}\n\nint StatsFunctions::GetAttributeBase(unsigned short pid, unsigned short attributeId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (attributeId >= ESM::Attribute::Length)\n        return 0;\n\n    return player->creatureStats.mAttributes[attributeId].mBase;\n}\n\nint StatsFunctions::GetAttributeModifier(unsigned short pid, unsigned short attributeId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (attributeId >= ESM::Attribute::Length)\n        return 0;\n\n    return player->creatureStats.mAttributes[attributeId].mMod;\n}\n\ndouble StatsFunctions::GetAttributeDamage(unsigned short pid, unsigned short attributeId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (attributeId >= ESM::Attribute::Length)\n        return 0;\n\n    return player->creatureStats.mAttributes[attributeId].mDamage;\n}\n\nint StatsFunctions::GetSkillBase(unsigned short pid, unsigned short skillId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (skillId >= ESM::Skill::Length)\n        return 0;\n\n    return player->npcStats.mSkills[skillId].mBase;\n}\n\nint StatsFunctions::GetSkillModifier(unsigned short pid, unsigned short skillId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (skillId >= ESM::Skill::Length)\n        return 0;\n\n    return player->npcStats.mSkills[skillId].mMod;\n}\n\ndouble StatsFunctions::GetSkillDamage(unsigned short pid, unsigned short skillId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (skillId >= ESM::Skill::Length)\n        return 0;\n\n    return player->npcStats.mSkills[skillId].mDamage;\n}\n\ndouble StatsFunctions::GetSkillProgress(unsigned short pid, unsigned short skillId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0.0f);\n\n    if (skillId >= ESM::Skill::Length)\n        return 0;\n\n    return player->npcStats.mSkills[skillId].mProgress;\n}\n\nint StatsFunctions::GetSkillIncrease(unsigned short pid, unsigned int attributeId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    if (attributeId > ESM::Attribute::Length)\n        return 0;\n\n    return player->npcStats.mSkillIncrease[attributeId];\n}\n\nint StatsFunctions::GetBounty(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, 0);\n\n    return player->npcStats.mBounty;\n}\n\nvoid StatsFunctions::SetName(unsigned short pid, const char *name) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (player->npc.mName == name)\n        return;\n\n    player->npc.mName = name;\n}\n\nvoid StatsFunctions::SetRace(unsigned short pid, const char *race) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (player->npc.mRace == race)\n        return;\n\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Setting race for %s: %s -> %s\", player->npc.mName.c_str(),\n                       player->npc.mRace.c_str(), race);\n\n    player->npc.mRace = race;\n}\n\nvoid StatsFunctions::SetHead(unsigned short pid, const char *head) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (player->npc.mHead == head)\n        return;\n\n    player->npc.mHead = head;\n}\n\nvoid StatsFunctions::SetHairstyle(unsigned short pid, const char *hairstyle) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (player->npc.mHair == hairstyle)\n        return;\n\n    player->npc.mHair = hairstyle;\n}\n\nvoid StatsFunctions::SetIsMale(unsigned short pid, int state) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->npc.setIsMale(state > 0 ? true : false);\n}\n\nvoid StatsFunctions::SetModel(unsigned short pid, const char *model) noexcept\n{\n    Player* player;\n    GET_PLAYER(pid, player, );\n\n    if (player->npc.mModel == model)\n        return;\n\n    player->npc.mModel = model;\n}\n\nvoid StatsFunctions::SetBirthsign(unsigned short pid, const char *sign) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    if (player->birthsign == sign)\n        return;\n\n    player->birthsign = sign;\n}\n\nvoid StatsFunctions::SetResetStats(unsigned short pid, bool resetStats) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->resetStats = resetStats;\n}\n\nvoid StatsFunctions::SetLevel(unsigned short pid, int value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->creatureStats.mLevel = value;\n}\n\nvoid StatsFunctions::SetLevelProgress(unsigned short pid, int value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->npcStats.mLevelProgress = value;\n}\n\nvoid StatsFunctions::SetHealthBase(unsigned short pid, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->creatureStats.mDynamic[0].mBase = value;\n\n    if (!Utils::vectorContains(player->statsDynamicIndexChanges, 0))\n        player->statsDynamicIndexChanges.push_back(0);\n}\n\nvoid StatsFunctions::SetHealthCurrent(unsigned short pid, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->creatureStats.mDynamic[0].mCurrent = value;\n\n    if (!Utils::vectorContains(player->statsDynamicIndexChanges, 0))\n        player->statsDynamicIndexChanges.push_back(0);\n}\n\nvoid StatsFunctions::SetMagickaBase(unsigned short pid, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->creatureStats.mDynamic[1].mBase = value;\n\n    if (!Utils::vectorContains(player->statsDynamicIndexChanges, 1))\n        player->statsDynamicIndexChanges.push_back(1);\n}\n\nvoid StatsFunctions::SetMagickaCurrent(unsigned short pid, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->creatureStats.mDynamic[1].mCurrent = value;\n\n    if (!Utils::vectorContains(player->statsDynamicIndexChanges, 1))\n        player->statsDynamicIndexChanges.push_back(1);\n}\n\nvoid StatsFunctions::SetFatigueBase(unsigned short pid, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->creatureStats.mDynamic[2].mBase = value;\n\n    if (!Utils::vectorContains(player->statsDynamicIndexChanges, 2))\n        player->statsDynamicIndexChanges.push_back(2);\n}\n\nvoid StatsFunctions::SetFatigueCurrent(unsigned short pid, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->creatureStats.mDynamic[2].mCurrent = value;\n\n    if (!Utils::vectorContains(player->statsDynamicIndexChanges, 2))\n        player->statsDynamicIndexChanges.push_back(2);\n}\n\nvoid StatsFunctions::SetAttributeBase(unsigned short pid, unsigned short attributeId, int value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (attributeId >= ESM::Attribute::Length)\n        return;\n\n    player->creatureStats.mAttributes[attributeId].mBase = value;\n\n    if (!Utils::vectorContains(player->attributeIndexChanges, attributeId))\n        player->attributeIndexChanges.push_back(attributeId);\n}\n\nvoid StatsFunctions::ClearAttributeModifier(unsigned short pid, unsigned short attributeId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (attributeId >= ESM::Attribute::Length)\n        return;\n\n    player->creatureStats.mAttributes[attributeId].mMod = 0;\n\n    if (!Utils::vectorContains(player->attributeIndexChanges, attributeId))\n        player->attributeIndexChanges.push_back(attributeId);\n}\n\nvoid StatsFunctions::SetAttributeDamage(unsigned short pid, unsigned short attributeId, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    if (attributeId >= ESM::Attribute::Length)\n        return;\n\n    player->creatureStats.mAttributes[attributeId].mDamage = value;\n\n    if (!Utils::vectorContains(player->attributeIndexChanges, attributeId))\n        player->attributeIndexChanges.push_back(attributeId);\n}\n\nvoid StatsFunctions::SetSkillBase(unsigned short pid, unsigned short skillId, int value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (skillId >= ESM::Skill::Length)\n        return;\n\n    player->npcStats.mSkills[skillId].mBase = value;\n\n    if (!Utils::vectorContains(player->skillIndexChanges, skillId))\n        player->skillIndexChanges.push_back(skillId);\n}\n\nvoid StatsFunctions::ClearSkillModifier(unsigned short pid, unsigned short skillId) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (skillId >= ESM::Skill::Length)\n        return;\n\n    player->npcStats.mSkills[skillId].mMod = 0;\n\n    if (!Utils::vectorContains(player->skillIndexChanges, skillId))\n        player->skillIndexChanges.push_back(skillId);\n}\n\nvoid StatsFunctions::SetSkillDamage(unsigned short pid, unsigned short skillId, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    if (skillId >= ESM::Skill::Length)\n        return;\n\n    player->npcStats.mSkills[skillId].mDamage = value;\n\n    if (!Utils::vectorContains(player->skillIndexChanges, skillId))\n        player->skillIndexChanges.push_back(skillId);\n}\n\nvoid StatsFunctions::SetSkillProgress(unsigned short pid, unsigned short skillId, double value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    if (skillId >= ESM::Skill::Length)\n        return;\n\n    player->npcStats.mSkills[skillId].mProgress = value;\n\n    if (!Utils::vectorContains(player->skillIndexChanges, skillId))\n        player->skillIndexChanges.push_back(skillId);\n}\n\nvoid StatsFunctions::SetSkillIncrease(unsigned short pid, unsigned int attributeId, int value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    if (attributeId > ESM::Attribute::Length)\n        return;\n\n    player->npcStats.mSkillIncrease[attributeId] = value;\n\n    if (!Utils::vectorContains(player->attributeIndexChanges, attributeId))\n        player->attributeIndexChanges.push_back(attributeId);\n}\n\nvoid StatsFunctions::SetBounty(unsigned short pid, int value) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    player->npcStats.mBounty = value;\n}\n\nvoid StatsFunctions::SetCharGenStage(unsigned short pid, int currentStage, int endStage) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    player->charGenState.currentStage = currentStage;\n    player->charGenState.endStage = endStage;\n    player->charGenState.isFinished = false;\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARGEN);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n}\n\nvoid StatsFunctions::SendBaseInfo(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BASEINFO);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n    packet->Send(true);\n}\n\nvoid StatsFunctions::SendStatsDynamic(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_STATS_DYNAMIC);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n    packet->Send(true);\n\n    player->statsDynamicIndexChanges.clear();\n}\n\nvoid StatsFunctions::SendAttributes(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ATTRIBUTE);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n    packet->Send(true);\n\n    player->attributeIndexChanges.clear();\n}\n\nvoid StatsFunctions::SendSkills(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player,);\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SKILL);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n    packet->Send(true);\n\n    player->skillIndexChanges.clear();\n}\n\nvoid StatsFunctions::SendLevel(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_LEVEL);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n    packet->Send(true);\n}\n\nvoid StatsFunctions::SendBounty(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOUNTY);\n    packet->setPlayer(player);\n    \n    packet->Send(false);\n    packet->Send(true);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Stats.hpp",
    "content": "#ifndef OPENMW_STATAPI_HPP\n#define OPENMW_STATAPI_HPP\n\n#define STATAPI \\\n    {\"GetAttributeCount\",       StatsFunctions::GetAttributeCount},\\\n    {\"GetSkillCount\",           StatsFunctions::GetSkillCount},\\\n    {\"GetAttributeId\",          StatsFunctions::GetAttributeId},\\\n    {\"GetSkillId\",              StatsFunctions::GetSkillId},\\\n    {\"GetAttributeName\",        StatsFunctions::GetAttributeName},\\\n    {\"GetSkillName\",            StatsFunctions::GetSkillName},\\\n    \\\n    {\"GetName\",                 StatsFunctions::GetName},\\\n    {\"GetRace\",                 StatsFunctions::GetRace},\\\n    {\"GetHead\",                 StatsFunctions::GetHead},\\\n    {\"GetHair\",                 StatsFunctions::GetHairstyle},\\\n    {\"GetIsMale\",               StatsFunctions::GetIsMale},\\\n    {\"GetModel\",                StatsFunctions::GetModel},\\\n    {\"GetBirthsign\",            StatsFunctions::GetBirthsign},\\\n    \\\n    {\"GetLevel\",                StatsFunctions::GetLevel},\\\n    {\"GetLevelProgress\",        StatsFunctions::GetLevelProgress},\\\n    \\\n    {\"GetHealthBase\",           StatsFunctions::GetHealthBase},\\\n    {\"GetHealthCurrent\",        StatsFunctions::GetHealthCurrent},\\\n    \\\n    {\"GetMagickaBase\",          StatsFunctions::GetMagickaBase},\\\n    {\"GetMagickaCurrent\",       StatsFunctions::GetMagickaCurrent},\\\n    \\\n    {\"GetFatigueBase\",          StatsFunctions::GetFatigueBase},\\\n    {\"GetFatigueCurrent\",       StatsFunctions::GetFatigueCurrent},\\\n    \\\n    {\"GetAttributeBase\",        StatsFunctions::GetAttributeBase},\\\n    {\"GetAttributeModifier\",    StatsFunctions::GetAttributeModifier},\\\n    {\"GetAttributeDamage\",      StatsFunctions::GetAttributeDamage},\\\n    \\\n    {\"GetSkillBase\",            StatsFunctions::GetSkillBase},\\\n    {\"GetSkillModifier\",        StatsFunctions::GetSkillModifier},\\\n    {\"GetSkillDamage\",          StatsFunctions::GetSkillDamage},\\\n    {\"GetSkillProgress\",        StatsFunctions::GetSkillProgress},\\\n    {\"GetSkillIncrease\",        StatsFunctions::GetSkillIncrease},\\\n    \\\n    {\"GetBounty\",               StatsFunctions::GetBounty},\\\n    \\\n    {\"SetName\",                 StatsFunctions::SetName},\\\n    {\"SetRace\",                 StatsFunctions::SetRace},\\\n    {\"SetHead\",                 StatsFunctions::SetHead},\\\n    {\"SetHair\",                 StatsFunctions::SetHairstyle},\\\n    {\"SetIsMale\",               StatsFunctions::SetIsMale},\\\n    {\"SetModel\",                StatsFunctions::SetModel},\\\n    {\"SetBirthsign\",            StatsFunctions::SetBirthsign},\\\n    {\"SetResetStats\",           StatsFunctions::SetResetStats},\\\n    \\\n    {\"SetLevel\",                StatsFunctions::SetLevel},\\\n    {\"SetLevelProgress\",        StatsFunctions::SetLevelProgress},\\\n    \\\n    {\"SetHealthBase\",           StatsFunctions::SetHealthBase},\\\n    {\"SetHealthCurrent\",        StatsFunctions::SetHealthCurrent},\\\n    {\"SetMagickaBase\",          StatsFunctions::SetMagickaBase},\\\n    {\"SetMagickaCurrent\",       StatsFunctions::SetMagickaCurrent},\\\n    {\"SetFatigueBase\",          StatsFunctions::SetFatigueBase},\\\n    {\"SetFatigueCurrent\",       StatsFunctions::SetFatigueCurrent},\\\n    \\\n    {\"SetAttributeBase\",        StatsFunctions::SetAttributeBase},\\\n    {\"ClearAttributeModifier\",  StatsFunctions::ClearAttributeModifier},\\\n    {\"SetAttributeDamage\",      StatsFunctions::SetAttributeDamage},\\\n    \\\n    {\"SetSkillBase\",            StatsFunctions::SetSkillBase},\\\n    {\"ClearSkillModifier\",      StatsFunctions::ClearSkillModifier},\\\n    {\"SetSkillDamage\",          StatsFunctions::SetSkillDamage},\\\n    {\"SetSkillProgress\",        StatsFunctions::SetSkillProgress},\\\n    {\"SetSkillIncrease\",        StatsFunctions::SetSkillIncrease},\\\n    \\\n    {\"SetBounty\",               StatsFunctions::SetBounty},\\\n    {\"SetCharGenStage\",         StatsFunctions::SetCharGenStage},\\\n    \\\n    {\"SendBaseInfo\",            StatsFunctions::SendBaseInfo},\\\n    \\\n    {\"SendStatsDynamic\",        StatsFunctions::SendStatsDynamic},\\\n    {\"SendAttributes\",          StatsFunctions::SendAttributes},\\\n    {\"SendSkills\",              StatsFunctions::SendSkills},\\\n    {\"SendLevel\",               StatsFunctions::SendLevel},\\\n    {\"SendBounty\",              StatsFunctions::SendBounty}\n\nclass StatsFunctions\n{\npublic:\n\n    /**\n    * \\brief Get the number of attributes.\n    *\n    * The number is 8 before any dehardcoding is done in OpenMW.\n    *\n    * \\return The number of attributes.\n    */\n    static int GetAttributeCount() noexcept;\n\n    /**\n    * \\brief Get the number of skills.\n    *\n    * The number is 27 before any dehardcoding is done in OpenMW.\n    *\n    * \\return The number of skills.\n    */\n    static int GetSkillCount() noexcept;\n\n    /**\n    * \\brief Get the numerical ID of an attribute with a certain name.\n    *\n    * If an invalid name is used, the ID returned is -1\n    *\n    * \\param name The name of the attribute.\n    * \\return The ID of the attribute.\n    */\n    static int GetAttributeId(const char *name) noexcept;\n\n    /**\n    * \\brief Get the numerical ID of a skill with a certain name.\n    *\n    * If an invalid name is used, the ID returned is -1\n    *\n    * \\param name The name of the skill.\n    * \\return The ID of the skill.\n    */\n    static int GetSkillId(const char *name) noexcept;\n\n    /**\n    * \\brief Get the name of the attribute with a certain numerical ID.\n    *\n    * If an invalid ID is used, \"invalid\" is returned.\n    *\n    * \\param attributeId The ID of the attribute.\n    * \\return The name of the attribute.\n    */\n    static const char *GetAttributeName(unsigned short attributeId) noexcept;\n\n    /**\n    * \\brief Get the name of the skill with a certain numerical ID.\n    *\n    * If an invalid ID is used, \"invalid\" is returned.\n    *\n    * \\param skillId The ID of the skill.\n    * \\return The name of the skill.\n    */\n    static const char *GetSkillName(unsigned short skillId) noexcept;\n\n    /**\n    * \\brief Get the name of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The name of the player.\n    */\n    static const char *GetName(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the race of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The race of the player.\n    */\n    static const char *GetRace(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the head mesh used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The head mesh of the player.\n    */\n    static const char *GetHead(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the hairstyle mesh used by a player.\n    *\n    * \\param pid The player ID.\n    * \\return The hairstyle mesh of the player.\n    */\n    static const char *GetHairstyle(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Check whether a player is male or not.\n    *\n    * \\param pid The player ID.\n    * \\return Whether the player is male.\n    */\n    static int GetIsMale(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the model of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The model of the player.\n    */\n    static const char* GetModel(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the birthsign of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The birthsign of the player.\n    */\n    static const char *GetBirthsign(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the character level of a player.\n    *\n    * \\param pid The player ID.\n    * \\return The level of the player.\n    */\n    static int GetLevel(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the player's progress to their next character level.\n    *\n    * \\param pid The player ID.\n    * \\return The level progress.\n    */\n    static int GetLevelProgress(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the base health of the player.\n    *\n    * \\param pid The player ID.\n    * \\return The base health.\n    */\n    static double GetHealthBase(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the current health of the player.\n    *\n    * \\param pid The player ID.\n    * \\return The current health.\n    */\n    static double GetHealthCurrent(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the base magicka of the player.\n    *\n    * \\param pid The player ID.\n    * \\return The base magicka.\n    */\n    static double GetMagickaBase(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the current magicka of the player.\n    *\n    * \\param pid The player ID.\n    * \\return The current magicka.\n    */\n    static double GetMagickaCurrent(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the base fatigue of the player.\n    *\n    * \\param pid The player ID.\n    * \\return The base fatigue.\n    */\n    static double GetFatigueBase(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the current fatigue of the player.\n    *\n    * \\param pid The player ID.\n    * \\return The current fatigue.\n    */\n    static double GetFatigueCurrent(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Get the base value of a player's attribute.\n    *\n    * \\param pid The player ID.\n    * \\param attributeId The attribute ID.\n    * \\return The base value of the attribute.\n    */\n    static int GetAttributeBase(unsigned short pid, unsigned short attributeId) noexcept;\n\n    /**\n    * \\brief Get the modifier value of a player's attribute.\n    *\n    * \\param pid The player ID.\n    * \\param attributeId The attribute ID.\n    * \\return The modifier value of the attribute.\n    */\n    static int GetAttributeModifier(unsigned short pid, unsigned short attributeId) noexcept;\n\n    /**\n    * \\brief Get the amount of damage (as caused through the Damage Attribute effect)\n    *        to a player's attribute.\n    *\n    * \\param pid The player ID.\n    * \\param attributeId The attribute ID.\n    * \\return The amount of damage to the attribute.\n    */\n    static double GetAttributeDamage(unsigned short pid, unsigned short attributeId) noexcept;\n\n    /**\n    * \\brief Get the base value of a player's skill.\n    *\n    * \\param pid The player ID.\n    * \\param skillId The skill ID.\n    * \\return The base value of the skill.\n    */\n    static int GetSkillBase(unsigned short pid, unsigned short skillId) noexcept;\n\n    /**\n    * \\brief Get the modifier value of a player's skill.\n    *\n    * \\param pid The player ID.\n    * \\param skillId The skill ID.\n    * \\return The modifier value of the skill.\n    */\n    static int GetSkillModifier(unsigned short pid, unsigned short skillId) noexcept;\n\n    /**\n    * \\brief Get the amount of damage (as caused through the Damage Skill effect)\n    *        to a player's skill.\n    *\n    * \\param pid The player ID.\n    * \\param skillId The skill ID.\n    * \\return The amount of damage to the skill.\n    */\n    static double GetSkillDamage(unsigned short pid, unsigned short skillId) noexcept;\n\n    /**\n    * \\brief Get the progress the player has made towards increasing a certain skill by 1.\n    *\n    * \\param pid The player ID.\n    * \\param skillId The skill ID.\n    * \\return The skill progress.\n    */\n    static double GetSkillProgress(unsigned short pid, unsigned short skillId) noexcept;\n\n    /**\n    * \\brief Get the bonus applied to a certain attribute at the next level up as a result\n    *        of associated skill increases.\n    *\n    * Although confusing, the term \"skill increase\" for this is taken from OpenMW itself.\n    *\n    * \\param pid The player ID.\n    * \\param attributeId The attribute ID.\n    * \\return The increase in the attribute caused by skills.\n    */\n    static int GetSkillIncrease(unsigned short pid, unsigned int attributeId) noexcept;\n\n    /**\n    * \\brief Get the bounty of the player.\n    *\n    * \\param pid The player ID.\n    * \\return The bounty.\n    */\n    static int GetBounty(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Set the name of a player.\n    *\n    * \\param pid The player ID.\n    * \\param name The new name of the player.\n    * \\return void\n    */\n    static void SetName(unsigned short pid, const char *name) noexcept;\n\n    /**\n    * \\brief Set the race of a player.\n    *\n    * \\param pid The player ID.\n    * \\param race The new race of the player.\n    * \\return void\n    */\n    static void SetRace(unsigned short pid, const char *race) noexcept;\n\n    /**\n    * \\brief Set the head mesh used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param head The new head mesh of the player.\n    * \\return void\n    */\n    static void SetHead(unsigned short pid, const char *head) noexcept;\n\n    /**\n    * \\brief Set the hairstyle mesh used by a player.\n    *\n    * \\param pid The player ID.\n    * \\param hairstyle The new hairstyle mesh of the player.\n    * \\return void\n    */\n    static void SetHairstyle(unsigned short pid, const char *hairstyle) noexcept;\n\n    /**\n    * \\brief Set whether a player is male or not.\n    *\n    * \\param pid The player ID.\n    * \\param state Whether the player is male.\n    * \\return void\n    */\n    static void SetIsMale(unsigned short pid, int state) noexcept;\n\n    /**\n    * \\brief Set the model of a player.\n    *\n    * \\param pid The player ID.\n    * \\param model The new model of the player.\n    * \\return void\n    */\n    static void SetModel(unsigned short pid, const char *model) noexcept;\n\n    /**\n    * \\brief Set the birthsign of a player.\n    *\n    * \\param pid The player ID.\n    * \\param name The new birthsign of the player.\n    * \\return void\n    */\n    static void SetBirthsign(unsigned short pid, const char *name) noexcept;\n\n    /**\n    * \\brief Set whether the player's stats should be reset based on their\n    *        current race as the result of a PlayerBaseInfo packet.\n    *\n    * This changes the resetState for that player in the server memory, but does not by itself\n    * send a packet.\n    *\n    * \\param pid The player ID.\n    * \\param resetStats The stat reset state.\n    * \\return void\n    */\n    static void SetResetStats(unsigned short pid, bool resetStats) noexcept;\n    \n    /**\n    * \\brief Set the character level of a player.\n    *\n    * \\param pid The player ID.\n    * \\param value The new level of the player.\n    * \\return void\n    */\n    static void SetLevel(unsigned short pid, int value) noexcept;\n\n    /**\n    * \\brief Set the player's progress to their next character level.\n    *\n    * \\param pid The player ID.\n    * \\param value The new level progress of the player.\n    * \\return void\n    */\n    static void SetLevelProgress(unsigned short pid, int value) noexcept;\n\n    /**\n    * \\brief Set the base health of a player.\n    *\n    * \\param pid The player ID.\n    * \\param value The new base health of the player.\n    * \\return void\n    */\n    static void SetHealthBase(unsigned short pid, double value) noexcept;\n\n    /**\n    * \\brief Set the current health of a player.\n    *\n    * \\param pid The player ID.\n    * \\param value The new current health of the player.\n    * \\return void\n    */\n    static void SetHealthCurrent(unsigned short pid, double value) noexcept;\n\n    /**\n    * \\brief Set the base magicka of a player.\n    *\n    * \\param pid The player ID.\n    * \\param value The new base magicka of the player.\n    * \\return void\n    */\n    static void SetMagickaBase(unsigned short pid, double value) noexcept;\n\n    /**\n    * \\brief Set the current magicka of a player.\n    *\n    * \\param pid The player ID.\n    * \\param value The new current magicka of the player.\n    * \\return void\n    */\n    static void SetMagickaCurrent(unsigned short pid, double value) noexcept;\n\n    /**\n    * \\brief Set the base fatigue of a player.\n    *\n    * \\param pid The player ID.\n    * \\param value The new base fatigue of the player.\n    * \\return void\n    */\n    static void SetFatigueBase(unsigned short pid, double value) noexcept;\n\n    /**\n    * \\brief Set the current fatigue of a player.\n    *\n    * \\param pid The player ID.\n    * \\param value The new current fatigue of the player.\n    * \\return void\n    */\n    static void SetFatigueCurrent(unsigned short pid, double value) noexcept;\n\n    /**\n    * \\brief Set the base value of a player's attribute.\n    *\n    * \\param pid The player ID.\n    * \\param attributeId The attribute ID.\n    * \\param value The new base value of the player's attribute.\n    * \\return void\n    */\n    static void SetAttributeBase(unsigned short pid, unsigned short attributeId, int value) noexcept;\n\n    /**\n    * \\brief Clear the modifier value of a player's attribute.\n    *\n    * There's no way to set a modifier to a specific value because it can come from\n    * multiple different sources, but clearing it is a straightforward process that\n    * dispels associated effects on a client and, if necessary, unequips associated\n    * items.\n    *\n    * \\param pid The player ID.\n    * \\param attributeId The attribute ID.\n    * \\return void\n    */\n    static void ClearAttributeModifier(unsigned short pid, unsigned short attributeId) noexcept;\n\n    /**\n    * \\brief Set the amount of damage (as caused through the Damage Attribute effect) to\n    *        a player's attribute.\n    *\n    * \\param pid The player ID.\n    * \\param attributeId The attribute ID.\n    * \\param value The amount of damage to the player's attribute.\n    * \\return void\n    */\n    static void SetAttributeDamage(unsigned short pid, unsigned short attributeId, double value) noexcept;\n\n    /**\n    * \\brief Set the base value of a player's skill.\n    *\n    * \\param pid The player ID.\n    * \\param skillId The skill ID.\n    * \\param value The new base value of the player's skill.\n    * \\return void\n    */\n    static void SetSkillBase(unsigned short pid, unsigned short skillId, int value) noexcept;    \n\n    /**\n    * \\brief Clear the modifier value of a player's skill.\n    *\n    * There's no way to set a modifier to a specific value because it can come from\n    * multiple different sources, but clearing it is a straightforward process that\n    * dispels associated effects on a client and, if necessary, unequips associated\n    * items.\n    *\n    * \\param pid The player ID.\n    * \\param skillId The skill ID.\n    * \\return void\n    */\n    static void ClearSkillModifier(unsigned short pid, unsigned short skillId) noexcept;\n\n    /**\n    * \\brief Set the amount of damage (as caused through the Damage Skill effect) to\n    *        a player's skill.\n    *\n    * \\param pid The player ID.\n    * \\param skillId The skill ID.\n    * \\param value The amount of damage to the player's skill.\n    * \\return void\n    */\n    static void SetSkillDamage(unsigned short pid, unsigned short skillId, double value) noexcept;\n\n    /**\n    * \\brief Set the progress the player has made towards increasing a certain skill by 1.\n    *\n    * \\param pid The player ID.\n    * \\param skillId The skill ID.\n    * \\param value The progress value.\n    * \\return void\n    */\n    static void SetSkillProgress(unsigned short pid, unsigned short skillId, double value) noexcept;\n\n    /**\n    * \\brief Set the bonus applied to a certain attribute at the next level up as a result\n    *        of associated skill increases.\n    *\n    * Although confusing, the term \"skill increase\" for this is taken from OpenMW itself.\n    *\n    * \\param pid The player ID.\n    * \\param attributeId The attribute ID.\n    * \\param value The increase in the attribute caused by skills.\n    * \\return void\n    */\n    static void SetSkillIncrease(unsigned short pid, unsigned int attributeId, int value) noexcept;\n\n    /**\n    * \\brief Set the bounty of a player.\n    *\n    * \\param pid The player ID.\n    * \\param value The new bounty.\n    * \\return void\n    */\n    static void SetBounty(unsigned short pid, int value) noexcept;\n\n    /**\n    * \\brief Set the current and ending stages of character generation for a player.\n    *\n    * This is used to repeat part of character generation or to only go through part of it.\n    *\n    * \\param pid The player ID.\n    * \\param currentStage The new current stage.\n    * \\param endStage The new ending stage.\n    * \\return void\n    */\n    static void SetCharGenStage(unsigned short pid, int currentStage, int endStage) noexcept;\n\n    /**\n    * \\brief Send a PlayerBaseInfo packet with a player's name, race, head mesh,\n    *        hairstyle mesh, birthsign and stat reset state.\n    *\n    * It is always sent to all players.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendBaseInfo(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerStatsDynamic packet with a player's dynamic stats (health,\n    *        magicka and fatigue).\n    *\n    * It is always sent to all players.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendStatsDynamic(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerAttribute packet with a player's attributes and bonuses\n    *        to those attributes at the next level up (the latter being called\n    *        \"skill increases\" as in OpenMW).\n    *\n    * It is always sent to all players.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendAttributes(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerSkill packet with a player's skills.\n    *\n    * It is always sent to all players.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendSkills(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerLevel packet with a player's character level and\n    *        progress towards the next level up\n    *\n    * It is always sent to all players.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendLevel(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a PlayerBounty packet with a player's bounty.\n    *\n    * It is always sent to all players.\n    *\n    * \\param pid The player ID.\n    * \\return void\n    */\n    static void SendBounty(unsigned short pid) noexcept;\n};\n\n#endif //OPENMW_STATAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Timer.cpp",
    "content": "#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <Player.hpp>\n#include <Networking.hpp>\n#include <Script/API/TimerAPI.hpp>\n\nusing namespace mwmp;\n\nint ScriptFunctions::CreateTimer(ScriptFunc callback, int msec) noexcept\n{\n    return mwmp::TimerAPI::CreateTimer(callback, msec, \"\", std::vector<boost::any>());\n}\n\nint ScriptFunctions::CreateTimerEx(ScriptFunc callback, int msec, const char *types, va_list args) noexcept\n{\n    try\n    {\n        std::vector<boost::any> params;\n        Utils::getArguments(params, args, types);\n\n        return mwmp::TimerAPI::CreateTimer(callback, msec, types, params);\n    }\n    catch (...)\n    {\n        return -1;\n    }\n\n}\n\nvoid ScriptFunctions::StartTimer(int timerId) noexcept\n{\n    TimerAPI::StartTimer(timerId);\n}\n\nvoid ScriptFunctions::StopTimer(int timerId) noexcept\n{\n    TimerAPI::StopTimer(timerId);\n}\n\nvoid ScriptFunctions::RestartTimer(int timerId, int msec) noexcept\n{\n    TimerAPI::ResetTimer(timerId, msec);\n}\n\nvoid ScriptFunctions::FreeTimer(int timerId) noexcept\n{\n    TimerAPI::FreeTimer(timerId);\n}\n\nbool ScriptFunctions::IsTimerElapsed(int timerId) noexcept\n{\n    return TimerAPI::IsTimerElapsed(timerId);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Worldstate.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <apps/openmw-mp/Networking.hpp>\n#include <apps/openmw-mp/Player.hpp>\n#include <apps/openmw-mp/Script/ScriptFunctions.hpp>\n#include <apps/openmw-mp/CellController.hpp>\n#include <fstream>\n\n#include <apps/openmw-mp/Utils.hpp>\n\n#include \"Worldstate.hpp\"\n\nusing namespace mwmp;\n\nBaseWorldstate *WorldstateFunctions::readWorldstate;\nBaseWorldstate WorldstateFunctions::writeWorldstate;\n\nvoid WorldstateFunctions::ReadReceivedWorldstate() noexcept\n{\n    readWorldstate = mwmp::Networking::getPtr()->getReceivedWorldstate();\n}\n\nvoid WorldstateFunctions::CopyReceivedWorldstateToStore() noexcept\n{\n    writeWorldstate = *readWorldstate;\n}\n\nvoid WorldstateFunctions::ClearKillChanges() noexcept\n{\n    writeWorldstate.killChanges.clear();\n}\n\nvoid WorldstateFunctions::ClearMapChanges() noexcept\n{\n    writeWorldstate.mapTiles.clear();\n}\n\nvoid WorldstateFunctions::ClearClientGlobals() noexcept\n{\n    writeWorldstate.clientGlobals.clear();\n}\n\nunsigned int WorldstateFunctions::GetKillChangesSize() noexcept\n{\n    return readWorldstate->killChanges.size();\n}\n\nunsigned int WorldstateFunctions::GetMapChangesSize() noexcept\n{\n    return readWorldstate->mapTiles.size();\n}\n\nunsigned int WorldstateFunctions::GetClientGlobalsSize() noexcept\n{\n    return readWorldstate->clientGlobals.size();\n}\n\nconst char *WorldstateFunctions::GetKillRefId(unsigned int index) noexcept\n{\n    return readWorldstate->killChanges.at(index).refId.c_str();\n}\n\nint WorldstateFunctions::GetKillNumber(unsigned int index) noexcept\n{\n    return readWorldstate->killChanges.at(index).number;\n}\n\nconst char *WorldstateFunctions::GetWeatherRegion() noexcept\n{\n    return readWorldstate->weather.region.c_str();\n}\n\nint WorldstateFunctions::GetWeatherCurrent() noexcept\n{\n    return readWorldstate->weather.currentWeather;\n}\n\nint WorldstateFunctions::GetWeatherNext() noexcept\n{\n    return readWorldstate->weather.nextWeather;\n}\n\nint WorldstateFunctions::GetWeatherQueued() noexcept\n{\n    return readWorldstate->weather.queuedWeather;\n}\n\ndouble WorldstateFunctions::GetWeatherTransitionFactor() noexcept\n{\n    return readWorldstate->weather.transitionFactor;\n}\n\nint WorldstateFunctions::GetMapTileCellX(unsigned int index) noexcept\n{\n    return readWorldstate->mapTiles.at(index).x;\n}\n\nint WorldstateFunctions::GetMapTileCellY(unsigned int index) noexcept\n{\n    return readWorldstate->mapTiles.at(index).y;\n}\n\nconst char *WorldstateFunctions::GetClientGlobalId(unsigned int index) noexcept\n{\n    return readWorldstate->clientGlobals.at(index).id.c_str();\n}\n\nunsigned short WorldstateFunctions::GetClientGlobalVariableType(unsigned int index) noexcept\n{\n    return readWorldstate->clientGlobals.at(index).variableType;\n}\n\nint WorldstateFunctions::GetClientGlobalIntValue(unsigned int index) noexcept\n{\n    return readWorldstate->clientGlobals.at(index).intValue;\n}\n\ndouble WorldstateFunctions::GetClientGlobalFloatValue(unsigned int index) noexcept\n{\n    return readWorldstate->clientGlobals.at(index).floatValue;\n}\n\nvoid WorldstateFunctions::SetAuthorityRegion(const char* authorityRegion) noexcept\n{\n    writeWorldstate.authorityRegion = authorityRegion;\n}\n\nvoid WorldstateFunctions::SetWeatherRegion(const char* region) noexcept\n{\n    writeWorldstate.weather.region = region;\n}\n\nvoid WorldstateFunctions::SetWeatherForceState(bool forceState) noexcept\n{\n    writeWorldstate.forceWeather = forceState;\n}\n\nvoid WorldstateFunctions::SetWeatherCurrent(int currentWeather) noexcept\n{\n    writeWorldstate.weather.currentWeather = currentWeather;\n}\n\nvoid WorldstateFunctions::SetWeatherNext(int nextWeather) noexcept\n{\n    writeWorldstate.weather.nextWeather = nextWeather;\n}\n\nvoid WorldstateFunctions::SetWeatherQueued(int queuedWeather) noexcept\n{\n    writeWorldstate.weather.queuedWeather = queuedWeather;\n}\n\nvoid WorldstateFunctions::SetWeatherTransitionFactor(double transitionFactor) noexcept\n{\n    writeWorldstate.weather.transitionFactor = transitionFactor;\n}\n\nvoid WorldstateFunctions::SetHour(double hour) noexcept\n{\n    writeWorldstate.time.hour = hour;\n}\n\nvoid WorldstateFunctions::SetDay(int day) noexcept\n{\n    writeWorldstate.time.day = day;\n}\n\nvoid WorldstateFunctions::SetMonth(int month) noexcept\n{\n    writeWorldstate.time.month = month;\n}\n\nvoid WorldstateFunctions::SetYear(int year) noexcept\n{\n    writeWorldstate.time.year = year;\n}\n\nvoid WorldstateFunctions::SetDaysPassed(int daysPassed) noexcept\n{\n    writeWorldstate.time.daysPassed = daysPassed;\n}\n\nvoid WorldstateFunctions::SetTimeScale(double timeScale) noexcept\n{\n    writeWorldstate.time.timeScale = timeScale;\n}\n\nvoid WorldstateFunctions::SetPlayerCollisionState(bool state) noexcept\n{\n    writeWorldstate.hasPlayerCollision = state;\n}\n\nvoid WorldstateFunctions::SetActorCollisionState(bool state) noexcept\n{\n    writeWorldstate.hasActorCollision = state;\n}\n\nvoid WorldstateFunctions::SetPlacedObjectCollisionState(bool state) noexcept\n{\n    writeWorldstate.hasPlacedObjectCollision = state;\n}\n\nvoid WorldstateFunctions::UseActorCollisionForPlacedObjects(bool useActorCollision) noexcept\n{\n    writeWorldstate.useActorCollisionForPlacedObjects = useActorCollision;\n}\n\nvoid WorldstateFunctions::AddKill(const char* refId, int number) noexcept\n{\n    mwmp::Kill kill;\n    kill.refId = refId;\n    kill.number = number;\n\n    writeWorldstate.killChanges.push_back(kill);\n}\n\nvoid WorldstateFunctions::AddClientGlobalInteger(const char* id, int intValue, unsigned int variableType) noexcept\n{\n    mwmp::ClientVariable clientVariable;\n    clientVariable.id = id;\n    clientVariable.variableType = variableType;\n    clientVariable.intValue = intValue;\n\n    writeWorldstate.clientGlobals.push_back(clientVariable);\n}\n\nvoid WorldstateFunctions::AddClientGlobalFloat(const char* id, double floatValue) noexcept\n{\n    mwmp::ClientVariable clientVariable;\n    clientVariable.id = id;\n    clientVariable.variableType = mwmp::VARIABLE_TYPE::FLOAT;\n    clientVariable.floatValue = floatValue;\n\n    writeWorldstate.clientGlobals.push_back(clientVariable);\n}\n\nvoid WorldstateFunctions::AddSynchronizedClientScriptId(const char *scriptId) noexcept\n{\n    writeWorldstate.synchronizedClientScriptIds.push_back(scriptId);\n}\n\nvoid WorldstateFunctions::AddSynchronizedClientGlobalId(const char *globalId) noexcept\n{\n    writeWorldstate.synchronizedClientGlobalIds.push_back(globalId);\n}\n\nvoid WorldstateFunctions::AddEnforcedCollisionRefId(const char *refId) noexcept\n{\n    writeWorldstate.enforcedCollisionRefIds.push_back(refId);\n}\n\nvoid WorldstateFunctions::AddCellToReset(const char *cellDescription) noexcept\n{\n    ESM::Cell cell = Utils::getCellFromDescription(cellDescription);\n    writeWorldstate.cellsToReset.push_back(cell);\n}\n\nvoid WorldstateFunctions::AddDestinationOverride(const char *oldCellDescription, const char *newCellDescription) noexcept\n{\n    writeWorldstate.destinationOverrides[oldCellDescription] = newCellDescription;\n}\n\nvoid WorldstateFunctions::ClearSynchronizedClientScriptIds() noexcept\n{\n    writeWorldstate.synchronizedClientScriptIds.clear();\n}\n\nvoid WorldstateFunctions::ClearSynchronizedClientGlobalIds() noexcept\n{\n    writeWorldstate.synchronizedClientGlobalIds.clear();\n}\n\nvoid WorldstateFunctions::ClearEnforcedCollisionRefIds() noexcept\n{\n    writeWorldstate.enforcedCollisionRefIds.clear();\n}\n\nvoid WorldstateFunctions::ClearCellsToReset() noexcept\n{\n    writeWorldstate.cellsToReset.clear();\n}\n\nvoid WorldstateFunctions::ClearDestinationOverrides() noexcept\n{\n    writeWorldstate.destinationOverrides.clear();\n}\n\nvoid WorldstateFunctions::SaveMapTileImageFile(unsigned int index, const char *filePath) noexcept\n{\n    if (index >= readWorldstate->mapTiles.size())\n        return;\n\n    const std::vector<char>& imageData = readWorldstate->mapTiles.at(index).imageData;\n\n    std::ofstream outputFile(filePath, std::ios::binary);\n    std::ostream_iterator<char> outputIterator(outputFile);\n    std::copy(imageData.begin(), imageData.end(), outputIterator);\n}\n\nvoid WorldstateFunctions::LoadMapTileImageFile(int cellX, int cellY, const char* filePath) noexcept\n{\n    mwmp::MapTile mapTile;\n    mapTile.x = cellX;\n    mapTile.y = cellY;\n\n    std::ifstream inputFile(filePath, std::ios::binary);\n    mapTile.imageData = std::vector<char>(std::istreambuf_iterator<char>(inputFile), std::istreambuf_iterator<char>());\n\n    if (mapTile.imageData.size() > mwmp::maxImageDataSize)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Error loading image file for map tile: \"\n            \"%s has a size of %i, which is over the maximum allowed of %i!\",\n            filePath, mapTile.imageData.size(), mwmp::maxImageDataSize);\n    }\n    else\n    {\n        writeWorldstate.mapTiles.push_back(mapTile);\n    }\n}\n\nvoid WorldstateFunctions::SendClientScriptGlobal(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_CLIENT_SCRIPT_GLOBAL);\n    packet->setWorldstate(&writeWorldstate);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid WorldstateFunctions::SendClientScriptSettings(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_CLIENT_SCRIPT_SETTINGS);\n    packet->setWorldstate(&writeWorldstate);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid WorldstateFunctions::SendWorldKillCount(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_KILL_COUNT);\n    packet->setWorldstate(&writeWorldstate);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid WorldstateFunctions::SendWorldMap(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_MAP);\n    packet->setWorldstate(&writeWorldstate);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid WorldstateFunctions::SendWorldTime(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);\n    packet->setWorldstate(&writeWorldstate);\n    \n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid WorldstateFunctions::SendWorldWeather(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_WEATHER);\n    packet->setWorldstate(&writeWorldstate);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid WorldstateFunctions::SendWorldCollisionOverride(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_COLLISION_OVERRIDE);\n    packet->setWorldstate(&writeWorldstate);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid WorldstateFunctions::SendCellReset(unsigned short pid, bool sendToOtherPlayers) noexcept\n{\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_CELL_RESET);\n\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    packet->setWorldstate(&writeWorldstate);\n\n    packet->Send(sendToOtherPlayers);\n\n    if (sendToOtherPlayers)\n    {\n        packet->Send(false);\n    }\n}\n\nvoid WorldstateFunctions::SendWorldDestinationOverride(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_DESTINATION_OVERRIDE);\n    packet->setWorldstate(&writeWorldstate);\n\n    if (!skipAttachedPlayer)\n        packet->Send(false);\n    if (sendToOtherPlayers)\n        packet->Send(true);\n}\n\nvoid WorldstateFunctions::SendWorldRegionAuthority(unsigned short pid) noexcept\n{\n    Player *player;\n    GET_PLAYER(pid, player, );\n\n    writeWorldstate.guid = player->guid;\n\n    mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_REGION_AUTHORITY);\n    packet->setWorldstate(&writeWorldstate);\n\n    packet->Send(false);\n\n    // This packet should always be sent to all other players\n    packet->Send(true);\n}\n\n// All methods below are deprecated versions of methods from above\n\nvoid WorldstateFunctions::ReadLastWorldstate() noexcept\n{\n    ReadReceivedWorldstate();\n}\n\nvoid WorldstateFunctions::CopyLastWorldstateToStore() noexcept\n{\n    CopyReceivedWorldstateToStore();\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Functions/Worldstate.hpp",
    "content": "#ifndef OPENMW_WORLDSTATEAPI_HPP\n#define OPENMW_WORLDSTATEAPI_HPP\n\n#include <components/openmw-mp/Base/BaseWorldstate.hpp>\n#include \"../Types.hpp\"\n\n#define WORLDSTATEAPI \\\n    {\"ReadReceivedWorldstate\",            WorldstateFunctions::ReadReceivedWorldstate},\\\n    \\\n    {\"CopyReceivedWorldstateToStore\",     WorldstateFunctions::CopyReceivedWorldstateToStore},\\\n    \\\n    {\"ClearKillChanges\",                  WorldstateFunctions::ClearKillChanges},\\\n    {\"ClearMapChanges\",                   WorldstateFunctions::ClearMapChanges},\\\n    {\"ClearClientGlobals\",                WorldstateFunctions::ClearClientGlobals},\\\n    \\\n    {\"GetKillChangesSize\",                WorldstateFunctions::GetKillChangesSize},\\\n    {\"GetMapChangesSize\",                 WorldstateFunctions::GetMapChangesSize},\\\n    {\"GetClientGlobalsSize\",              WorldstateFunctions::GetClientGlobalsSize},\\\n    \\\n    {\"GetKillRefId\",                      WorldstateFunctions::GetKillRefId},\\\n    {\"GetKillNumber\",                     WorldstateFunctions::GetKillNumber},\\\n    \\\n    {\"GetWeatherRegion\",                  WorldstateFunctions::GetWeatherRegion},\\\n    {\"GetWeatherCurrent\",                 WorldstateFunctions::GetWeatherCurrent},\\\n    {\"GetWeatherNext\",                    WorldstateFunctions::GetWeatherNext},\\\n    {\"GetWeatherQueued\",                  WorldstateFunctions::GetWeatherQueued},\\\n    {\"GetWeatherTransitionFactor\",        WorldstateFunctions::GetWeatherTransitionFactor},\\\n    \\\n    {\"GetMapTileCellX\",                   WorldstateFunctions::GetMapTileCellX},\\\n    {\"GetMapTileCellY\",                   WorldstateFunctions::GetMapTileCellY},\\\n    \\\n    {\"GetClientGlobalId\",                 WorldstateFunctions::GetClientGlobalId},\\\n    {\"GetClientGlobalVariableType\",       WorldstateFunctions::GetClientGlobalVariableType},\\\n    {\"GetClientGlobalIntValue\",           WorldstateFunctions::GetClientGlobalIntValue},\\\n    {\"GetClientGlobalFloatValue\",         WorldstateFunctions::GetClientGlobalFloatValue},\\\n    \\\n    {\"SetAuthorityRegion\",                WorldstateFunctions::SetAuthorityRegion},\\\n    \\\n    {\"SetWeatherRegion\",                  WorldstateFunctions::SetWeatherRegion},\\\n    {\"SetWeatherForceState\",              WorldstateFunctions::SetWeatherForceState},\\\n    {\"SetWeatherCurrent\",                 WorldstateFunctions::SetWeatherCurrent},\\\n    {\"SetWeatherNext\",                    WorldstateFunctions::SetWeatherNext},\\\n    {\"SetWeatherQueued\",                  WorldstateFunctions::SetWeatherQueued},\\\n    {\"SetWeatherTransitionFactor\",        WorldstateFunctions::SetWeatherTransitionFactor},\\\n    \\\n    {\"SetHour\",                           WorldstateFunctions::SetHour},\\\n    {\"SetDay\",                            WorldstateFunctions::SetDay},\\\n    {\"SetMonth\",                          WorldstateFunctions::SetMonth},\\\n    {\"SetYear\",                           WorldstateFunctions::SetYear},\\\n    {\"SetDaysPassed\",                     WorldstateFunctions::SetDaysPassed},\\\n    {\"SetTimeScale\",                      WorldstateFunctions::SetTimeScale},\\\n    \\\n    {\"SetPlayerCollisionState\",           WorldstateFunctions::SetPlayerCollisionState},\\\n    {\"SetActorCollisionState\",            WorldstateFunctions::SetActorCollisionState},\\\n    {\"SetPlacedObjectCollisionState\",     WorldstateFunctions::SetPlacedObjectCollisionState},\\\n    {\"UseActorCollisionForPlacedObjects\", WorldstateFunctions::UseActorCollisionForPlacedObjects},\\\n    \\\n    {\"AddKill\",                           WorldstateFunctions::AddKill},\\\n    {\"AddClientGlobalInteger\",            WorldstateFunctions::AddClientGlobalInteger},\\\n    {\"AddClientGlobalFloat\",              WorldstateFunctions::AddClientGlobalFloat},\\\n    {\"AddSynchronizedClientScriptId\",     WorldstateFunctions::AddSynchronizedClientScriptId},\\\n    {\"AddSynchronizedClientGlobalId\",     WorldstateFunctions::AddSynchronizedClientGlobalId},\\\n    {\"AddEnforcedCollisionRefId\",         WorldstateFunctions::AddEnforcedCollisionRefId},\\\n    {\"AddCellToReset\",                    WorldstateFunctions::AddCellToReset},\\\n    {\"AddDestinationOverride\",            WorldstateFunctions::AddDestinationOverride},\\\n    \\\n    {\"ClearSynchronizedClientScriptIds\",  WorldstateFunctions::ClearSynchronizedClientScriptIds},\\\n    {\"ClearSynchronizedClientGlobalIds\",  WorldstateFunctions::ClearSynchronizedClientGlobalIds},\\\n    {\"ClearEnforcedCollisionRefIds\",      WorldstateFunctions::ClearEnforcedCollisionRefIds},\\\n    {\"ClearCellsToReset\",                 WorldstateFunctions::ClearCellsToReset},\\\n    {\"ClearDestinationOverrides\",         WorldstateFunctions::ClearDestinationOverrides},\\\n    \\\n    {\"SaveMapTileImageFile\",              WorldstateFunctions::SaveMapTileImageFile},\\\n    {\"LoadMapTileImageFile\",              WorldstateFunctions::LoadMapTileImageFile},\\\n    \\\n    {\"SendClientScriptGlobal\",            WorldstateFunctions::SendClientScriptGlobal},\\\n    {\"SendClientScriptSettings\",          WorldstateFunctions::SendClientScriptSettings},\\\n    {\"SendWorldKillCount\",                WorldstateFunctions::SendWorldKillCount},\\\n    {\"SendWorldMap\",                      WorldstateFunctions::SendWorldMap},\\\n    {\"SendWorldTime\",                     WorldstateFunctions::SendWorldTime},\\\n    {\"SendWorldWeather\",                  WorldstateFunctions::SendWorldWeather},\\\n    {\"SendWorldCollisionOverride\",        WorldstateFunctions::SendWorldCollisionOverride},\\\n    {\"SendCellReset\",                     WorldstateFunctions::SendCellReset},\\\n    {\"SendWorldDestinationOverride\",      WorldstateFunctions::SendWorldDestinationOverride},\\\n    {\"SendWorldRegionAuthority\",          WorldstateFunctions::SendWorldRegionAuthority},\\\n    \\\n    {\"ReadLastWorldstate\",                WorldstateFunctions::ReadLastWorldstate},\\\n    {\"CopyLastWorldstateToStore\",         WorldstateFunctions::CopyLastWorldstateToStore}\n\nclass WorldstateFunctions\n{\npublic:\n\n    static mwmp::BaseWorldstate *readWorldstate;\n    static mwmp::BaseWorldstate writeWorldstate;\n\n    /**\n    * \\brief Use the last worldstate received by the server as the one being read.\n    *\n    * \\return void\n    */\n    static void ReadReceivedWorldstate() noexcept;\n\n    /**\n    * \\brief Take the contents of the read-only worldstate last received by the\n    *        server from a player and move its contents to the stored worldstate\n    *        that can be sent by the server.\n    *\n    * \\return void\n    */\n    static void CopyReceivedWorldstateToStore() noexcept;\n\n    /**\n    * \\brief Clear the kill count changes for the write-only worldstate.\n    *\n    * This is used to initialize the sending of new WorldKillCount packets.\n    *\n    * \\return void\n    */\n    static void ClearKillChanges() noexcept;\n\n    /**\n    * \\brief Clear the map changes for the write-only worldstate.\n    *\n    * This is used to initialize the sending of new WorldMap packets.\n    *\n    * \\return void\n    */\n    static void ClearMapChanges() noexcept;\n\n    /**\n    * \\brief Clear the client globals for the write-only worldstate.\n    *\n    * This is used to initialize the sending of new ClientScriptGlobal packets.\n    *\n    * \\return void\n    */\n    static void ClearClientGlobals() noexcept;\n\n    /**\n    * \\brief Get the number of indexes in the read worldstate's kill changes.\n    *\n    * \\return The number of indexes.\n    */\n    static unsigned int GetKillChangesSize() noexcept;\n\n    /**\n    * \\brief Get the number of indexes in the read worldstate's map changes.\n    *\n    * \\return The number of indexes.\n    */\n    static unsigned int GetMapChangesSize() noexcept;\n\n    /**\n    * \\brief Get the number of indexes in the read worldstate's client globals.\n    *\n    * \\return The number of indexes.\n    */\n    static unsigned int GetClientGlobalsSize() noexcept;\n\n    /**\n    * \\brief Get the refId at a certain index in the read worldstate's kill count changes.\n    *\n    * \\param index The index of the kill count.\n    * \\return The refId.\n    */\n    static const char *GetKillRefId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the number of kills at a certain index in the read worldstate's kill count changes.\n    *\n    * \\param index The index of the kill count.\n    * \\return The number of kills.\n    */\n    static int GetKillNumber(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the weather region in the read worldstate.\n    *\n    * \\return The weather region.\n    */\n    static const char *GetWeatherRegion() noexcept;\n\n    /**\n    * \\brief Get the current weather in the read worldstate.\n    *\n    * \\return The current weather.\n    */\n    static int GetWeatherCurrent() noexcept;\n\n    /**\n    * \\brief Get the next weather in the read worldstate.\n    *\n    * \\return The next weather.\n    */\n    static int GetWeatherNext() noexcept;\n\n    /**\n    * \\brief Get the queued weather in the read worldstate.\n    *\n    * \\return The queued weather.\n    */\n    static int GetWeatherQueued() noexcept;\n\n    /**\n    * \\brief Get the transition factor of the weather in the read worldstate.\n    *\n    * \\return The transition factor of the weather.\n    */\n    static double GetWeatherTransitionFactor() noexcept;\n\n    /**\n    * \\brief Get the X coordinate of the cell corresponding to the map tile at a certain index in\n    *        the read worldstate's map tiles.\n    *\n    * \\param index The index of the map tile.\n    * \\return The X coordinate of the cell.\n    */\n    static int GetMapTileCellX(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the Y coordinate of the cell corresponding to the map tile at a certain index in\n    *        the read worldstate's map tiles.\n    *\n    * \\param index The index of the map tile.\n    * \\return The Y coordinate of the cell.\n    */\n    static int GetMapTileCellY(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the id of the global variable at a certain index in the read worldstate's\n    *        client globals.\n    *\n    * \\param index The index of the client global.\n    * \\return The id.\n    */\n    static const char *GetClientGlobalId(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the type of the global variable at a certain index in the read worldstate's\n    *        client globals.\n    *\n    * \\param index The index of the client global.\n    * \\return The variable type (0 for INTEGER, 1 for LONG, 2 for FLOAT).\n    */\n    static unsigned short GetClientGlobalVariableType(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the integer value of the global variable at a certain index in the read\n    *        worldstate's client globals.\n    *\n    * \\param index The index of the client global.\n    * \\return The integer value.\n    */\n    static int GetClientGlobalIntValue(unsigned int index) noexcept;\n\n    /**\n    * \\brief Get the float value of the global variable at a certain index in the read\n    *        worldstate's client globals.\n    *\n    * \\param index The index of the client global.\n    * \\return The float value.\n    */\n    static double GetClientGlobalFloatValue(unsigned int index) noexcept;\n\n    /**\n    * \\brief Set the region affected by the next WorldRegionAuthority packet sent.\n    *\n    * \\param authorityRegion The region.\n    * \\return void\n    */\n    static void SetAuthorityRegion(const char* authorityRegion) noexcept;\n\n    /**\n    * \\brief Set the weather region in the write-only worldstate stored on the server.\n    *\n    * \\param region The region.\n    * \\return void\n    */\n    static void SetWeatherRegion(const char* region) noexcept;\n\n    /**\n    * \\brief Set the weather forcing state in the write-only worldstate stored on the server.\n    *\n    * Players who receive a packet with forced weather will switch to that weather immediately.\n    *\n    * \\param forceState The weather forcing state.\n    * \\return void\n    */\n    static void SetWeatherForceState(bool forceState) noexcept;\n\n    /**\n    * \\brief Set the current weather in the write-only worldstate stored on the server.\n    *\n    * \\param currentWeather The current weather.\n    * \\return void\n    */\n    static void SetWeatherCurrent(int currentWeather) noexcept;\n\n    /**\n    * \\brief Set the next weather in the write-only worldstate stored on the server.\n    *\n    * \\param nextWeather The next weather.\n    * \\return void\n    */\n    static void SetWeatherNext(int nextWeather) noexcept;\n\n    /**\n    * \\brief Set the queued weather in the write-only worldstate stored on the server.\n    *\n    * \\param queuedWeather The queued weather.\n    * \\return void\n    */\n    static void SetWeatherQueued(int queuedWeather) noexcept;\n\n    /**\n    * \\brief Set the transition factor for the weather in the write-only worldstate stored on the server.\n    *\n    * \\param transitionFactor The transition factor.\n    * \\return void\n    */\n    static void SetWeatherTransitionFactor(double transitionFactor) noexcept;\n\n    /**\n    * \\brief Set the world's hour in the write-only worldstate stored on the server.\n    *\n    * \\param hour The hour.\n    * \\return void\n    */\n    static void SetHour(double hour) noexcept;\n\n    /**\n    * \\brief Set the world's day in the write-only worldstate stored on the server.\n    *\n    * \\param day The day.\n    * \\return void\n    */\n    static void SetDay(int day) noexcept;\n\n    /**\n    * \\brief Set the world's month in the write-only worldstate stored on the server.\n    *\n    * \\param month The month.\n    * \\return void\n    */\n    static void SetMonth(int month) noexcept;\n\n    /**\n    * \\brief Set the world's year in the write-only worldstate stored on the server.\n    *\n    * \\param year The year.\n    * \\return void\n    */\n    static void SetYear(int year) noexcept;\n\n    /**\n    * \\brief Set the world's days passed in the write-only worldstate stored on the server.\n    *\n    * \\param daysPassed The days passed.\n    * \\return void\n    */\n    static void SetDaysPassed(int daysPassed) noexcept;\n\n    /**\n    * \\brief Set the world's time scale in the write-only worldstate stored on the server.\n    *\n    * \\param timeScale The time scale.\n    * \\return void\n    */\n    static void SetTimeScale(double timeScale) noexcept;\n\n    /**\n    * \\brief Set the collision state for other players in the write-only worldstate stored\n    *        on the server.\n    *\n    * \\param state The collision state.\n    * \\return void\n    */\n    static void SetPlayerCollisionState(bool state) noexcept;\n\n    /**\n    * \\brief Set the collision state for actors in the write-only worldstate stored on the\n    *        server.\n    *\n    * \\param state The collision state.\n    * \\return void\n    */\n    static void SetActorCollisionState(bool state) noexcept;\n\n    /**\n    * \\brief Set the collision state for placed objects in the write-only worldstate stored\n    *        on the server.\n    *\n    * \\param state The collision state.\n    * \\return void\n    */\n    static void SetPlacedObjectCollisionState(bool state) noexcept;\n\n    /**\n    * \\brief Whether placed objects with collision turned on should use actor collision, i.e.\n    *        whether they should be slippery and prevent players from standing on them.\n    *\n    * \\param useActorCollision Whether to use actor collision.\n    * \\return void\n    */\n    static void UseActorCollisionForPlacedObjects(bool useActorCollision) noexcept;\n\n    /**\n    * \\brief Add a new kill count to the kill count changes.\n    *\n    * \\param refId The refId of the kill count.\n    * \\param number The number of kills in the kill count.\n    * \\return void\n    */\n    static void AddKill(const char* refId, int number) noexcept;\n\n    /**\n    * \\brief Add a new client global integer to the client globals.\n    *\n    * \\param id The id of the client global.\n    * \\param variableType The variable type (0 for SHORT, 1 for LONG).\n    * \\param intValue The integer value of the client global.\n    * \\return void\n    */\n    static void AddClientGlobalInteger(const char* id, int intValue, unsigned int variableType = 0) noexcept;\n\n    /**\n    * \\brief Add a new client global float to the client globals.\n    *\n    * \\param id The id of the client global.\n    * \\param floatValue The float value of the client global.\n    * \\return void\n    */\n    static void AddClientGlobalFloat(const char* id, double floatValue) noexcept;\n\n    /**\n    * \\brief Add an ID to the list of script IDs whose variable changes should be sent to the\n    *        the server by clients.\n    *\n    * \\param scriptId The ID.\n    * \\return void\n    */\n    static void AddSynchronizedClientScriptId(const char* scriptId) noexcept;\n\n    /**\n    * \\brief Add an ID to the list of global IDs whose value changes should be sent to the\n    *        server by clients.\n    *\n    * \\param globalId The ID.\n    * \\return void\n    */\n    static void AddSynchronizedClientGlobalId(const char* globalId) noexcept;\n\n    /**\n    * \\brief Add a refId to the list of refIds for which collision should be enforced\n    *        irrespective of other settings.\n    *\n    * \\param refId The refId.\n    * \\return void\n    */\n    static void AddEnforcedCollisionRefId(const char* refId) noexcept;\n\n    /**\n    * \\brief Add a cell with given cellDescription to the list of cells that should be reset on the client.\n    *\n    * \\return void\n    */\n    static void AddCellToReset(const char * cellDescription) noexcept;\n\n    /**\n    * \\brief Add a destination override containing the cell description for the old cell\n    *        and the new cell.\n    *\n    * \\param oldCellDescription The old cell description.\n    * \\param newCellDescription The new cell description.\n    * \\return void\n    */\n    static void AddDestinationOverride(const char* oldCellDescription, const char* newCellDescription) noexcept;\n\n    /**\n    * \\brief Clear the list of script IDs whose variable changes should be sent to the\n    *        the server by clients.\n    *\n    * \\return void\n    */\n    static void ClearSynchronizedClientScriptIds() noexcept;\n\n    /**\n    * \\brief Clear the list of global IDs whose value changes should be sent to the\n    *        the server by clients.\n    *\n    * \\return void\n    */\n    static void ClearSynchronizedClientGlobalIds() noexcept;\n\n    /**\n    * \\brief Clear the list of refIds for which collision should be enforced irrespective\n    *        of other settings.\n    *\n    * \\return void\n    */\n    static void ClearEnforcedCollisionRefIds() noexcept;\n\n    /**\n    * \\brief Clear the list of cells which should be reset on the client.\n    *\n    * \\return void\n    */\n    static void ClearCellsToReset() noexcept;\n\n    /**\n    * \\brief Clear the list of destination overrides.\n    *\n    * \\return void\n    */\n    static void ClearDestinationOverrides() noexcept;\n\n    /**\n    * \\brief Save the .png image data of the map tile at a certain index in the read worldstate's\n    *        map changes.\n    *\n    * \\param index The index of the map tile.\n    * \\param filePath The file path of the resulting file.\n    * \\return void\n    */\n    static void SaveMapTileImageFile(unsigned int index, const char *filePath) noexcept;\n\n    /**\n    * \\brief Load a .png file as the image data for a map tile and add it to the write-only worldstate\n    *        stored on the server.\n    *\n    * \\param cellX The X coordinate of the cell corresponding to the map tile.\n    * \\param cellY The Y coordinate of the cell corresponding to the map tile.\n    * \\param filePath The file path of the loaded file.\n    * \\return void\n    */\n    static void LoadMapTileImageFile(int cellX, int cellY, const char* filePath) noexcept;\n\n    /**\n    * \\brief Send a ClientScriptGlobal packet with the current client script globals in\n    *        the write-only worldstate.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendClientScriptGlobal(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a ClientScriptSettings packet with the current client script settings in\n    *        the write-only worldstate.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendClientScriptSettings(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a WorldKillCount packet with the current set of kill count changes in the write-only\n    *        worldstate.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendWorldKillCount(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a WorldRegionAuthority packet establishing a certain player as the only one who\n    *        should process certain region-specific events (such as weather changes).\n    *\n    * It is always sent to all players.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\return void\n    */\n    static void SendWorldRegionAuthority(unsigned short pid) noexcept;\n\n    /**\n    * \\brief Send a WorldMap packet with the current set of map changes in the write-only\n    *        worldstate.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendWorldMap(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a WorldTime packet with the current time and time scale in the write-only\n    *        worldstate.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendWorldTime(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a WorldWeather packet with the current weather in the write-only worldstate.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendWorldWeather(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a WorldCollisionOverride packet with the current collision overrides in\n    *        the write-only worldstate.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendWorldCollisionOverride(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n    /**\n    * \\brief Send a CellReset packet with a list of cells,\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\return void\n    */\n    static void SendCellReset(unsigned short pid, bool sendToOtherPlayers) noexcept;\n\n    /**\n    * \\brief Send a WorldDestinationOverride packet with the current destination overrides in\n    *        the write-only worldstate.\n    *\n    * \\param pid The player ID attached to the packet.\n    * \\param sendToOtherPlayers Whether this packet should be sent to players other than the\n    *                           player attached to the packet (false by default).\n    * \\param skipAttachedPlayer Whether the packet should skip being sent to the player attached\n    *                           to the packet (false by default).\n    * \\return void\n    */\n    static void SendWorldDestinationOverride(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;\n\n\n    // All methods below are deprecated versions of methods from above\n\n    static void ReadLastWorldstate() noexcept;\n    static void CopyLastWorldstateToStore() noexcept;\n\n};\n\n#endif //OPENMW_WORLDSTATEAPI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/LangLua/LangLua.cpp",
    "content": "#include <iostream>\n#include \"LangLua.hpp\"\n#include <Script/Script.hpp>\n#include <Script/Types.hpp>\n\nstd::set<std::string> LangLua::packagePath;\nstd::set<std::string> LangLua::packageCPath;\n\nvoid setLuaPath(lua_State* L, const char* path, bool cpath = false)\n{\n    std::string field = cpath ? \"cpath\" : \"path\";\n    lua_getglobal(L, \"package\");\n\n    lua_getfield(L, -1, field.c_str());\n    std::string cur_path = lua_tostring(L, -1);\n    cur_path.append(\";\");\n    cur_path.append(path);\n    lua_pop(L, 1);\n    lua_pushstring(L, cur_path.c_str());\n    lua_setfield(L, -2, field.c_str());\n    lua_pop(L, 1);\n}\n\nlib_t LangLua::GetInterface()\n{\n    return reinterpret_cast<lib_t>(lua);\n}\n\nLangLua::LangLua(lua_State *lua)\n{\n    this->lua = lua;\n}\n\nLangLua::LangLua()\n{\n    lua = luaL_newstate();\n    luaL_openlibs(lua); // load all lua std libs\n\n    std::string p, cp;\n    for (auto& path : packagePath)\n        p += path + ';';\n\n    for (auto& path : packageCPath)\n        cp += path + ';';\n\n    setLuaPath(lua, p.c_str());\n    setLuaPath(lua, cp.c_str(), true);\n\n}\n\nLangLua::~LangLua()\n{\n\n}\n\n// LuaFunctionDispatcher template struct for Lua function dispatch\ntemplate <unsigned int ArgIndex, unsigned int FunctionIndex>\nstruct LuaFunctionDispatcher {\n    // Dispatch Lua function with the given arguments\n    template <typename ReturnType, typename... Args>\n    inline static ReturnType Dispatch(lua_State*&& lua, Args&&... args) noexcept {\n        // Retrieve function data\n        constexpr ScriptFunctionData const& functionData = ScriptFunctions::functions[FunctionIndex];\n        // Retrieve argument from the Lua stack\n        auto argument = luabridge::Stack<typename CharType<functionData.func.types[ArgIndex - 1]>::type>::get(lua, ArgIndex);\n        // Recursively dispatch the Lua function\n        return LuaFunctionDispatcher<ArgIndex - 1, FunctionIndex>::template Dispatch<ReturnType>(\n            std::forward<lua_State*>(lua), argument, std::forward<Args>(args)...);\n    }\n};\n\n// Specialization for LuaFunctionDispatcher when ArgIndex is 0\ntemplate <unsigned int FunctionIndex>\nstruct LuaFunctionDispatcher<0, FunctionIndex> {\n    // Dispatch Lua function with the given arguments\n    template <typename ReturnType, typename... Args>\n    inline static ReturnType Dispatch(lua_State*&&, Args&&... args) noexcept {\n        // Retrieve function data\n        constexpr ScriptFunctionData const& functionData = ScriptFunctions::functions[FunctionIndex];\n        // Call the C++ function using reinterpret_cast\n        return reinterpret_cast<FunctionEllipsis<ReturnType>>(functionData.func.addr)(std::forward<Args>(args)...);\n    }\n};\n\n// Lua function wrapper for functions returning 'void'\ntemplate <unsigned int FunctionIndex>\nstatic typename std::enable_if<ScriptFunctions::functions[FunctionIndex].func.ret == 'v', int>::type LuaFunctionWrapper(lua_State* lua) noexcept {\n    // Dispatch the Lua function\n    LuaFunctionDispatcher<ScriptFunctions::functions[FunctionIndex].func.numargs, FunctionIndex>::template Dispatch<void>(std::forward<lua_State*>(lua));\n    return 0;\n}\n\n// Lua function wrapper for functions with non-void return types\ntemplate <unsigned int FunctionIndex>\nstatic typename std::enable_if<ScriptFunctions::functions[FunctionIndex].func.ret != 'v', int>::type LuaFunctionWrapper(lua_State* lua) noexcept {\n    // Dispatch the Lua function\n    auto result = LuaFunctionDispatcher<ScriptFunctions::functions[FunctionIndex].func.numargs, FunctionIndex>::template Dispatch<\n        typename CharType<ScriptFunctions::functions[FunctionIndex].func.ret>::type>(std::forward<lua_State*>(lua));\n    // Push the result onto the Lua stack\n    luabridge::Stack<typename CharType<ScriptFunctions::functions[FunctionIndex].func.ret>::type>::push(lua, result);\n    return 1;\n}\n\n// Struct for defining Lua functions with names and wrappers\ntemplate <unsigned int FunctionIndex>\nstruct LuaFunctionDefinition {\n    static constexpr LuaFunctionData FunctionInfo{\n       ScriptFunctions::functions[FunctionIndex].name, LuaFunctionWrapper<FunctionIndex>\n    };\n};\n\ntemplate<> struct LuaFunctionDefinition<0> { static constexpr LuaFunctionData FunctionInfo{\"CreateTimer\", LangLua::CreateTimer}; };\ntemplate<> struct LuaFunctionDefinition<1> { static constexpr LuaFunctionData FunctionInfo{\"CreateTimerEx\", LangLua::CreateTimerEx}; };\ntemplate<> struct LuaFunctionDefinition<2> { static constexpr LuaFunctionData FunctionInfo{\"MakePublic\", LangLua::MakePublic}; };\ntemplate<> struct LuaFunctionDefinition<3> { static constexpr LuaFunctionData FunctionInfo{\"CallPublic\", LangLua::CallPublic}; };\n\n\n#ifdef __arm__\ntemplate<std::size_t... Is>\nstruct indices {};\ntemplate<std::size_t N, std::size_t... Is>\nstruct build_indices : build_indices<N-1, N-1, Is...> {};\ntemplate<std::size_t... Is>\nstruct build_indices<0, Is...> : indices<Is...> {};\ntemplate<std::size_t N>\nusing IndicesFor = build_indices<N>;\n\ntemplate<size_t... Indices>\nLuaFuctionData *functions(indices<Indices...>)\n{\n\n    static LuaFuctionData functions_[sizeof...(Indices)]{\n            F_<Indices>::F...\n    };\n\n    static_assert(\n            sizeof(functions_) / sizeof(functions_[0]) ==\n            sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0]),\n            \"Not all functions have been mapped to Lua\");\n\n    return functions_;\n}\n#else\ntemplate<unsigned int I>\nstruct LuaFunctionInitializer\n{\n    constexpr static void Initialize(LuaFunctionData *functions_)\n    {\n        functions_[I] = LuaFunctionDefinition<I>::FunctionInfo;\n        LuaFunctionInitializer<I - 1>::Initialize(functions_);\n    }\n};\n\ntemplate<>\nstruct LuaFunctionInitializer<0>\n{\n    constexpr static void Initialize(LuaFunctionData *functions_)\n    {\n        functions_[0] = LuaFunctionDefinition<0>::FunctionInfo;\n    }\n};\n\ntemplate<size_t LastI>\nLuaFunctionData *GetLuaFunctions()\n{\n    static LuaFunctionData functions_[LastI];\n    LuaFunctionInitializer<LastI - 1>::Initialize(functions_);\n\n    static_assert(\n        sizeof(functions_) / sizeof(functions_[0]) ==\n        sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0]),\n        \"Not all functions have been mapped to Lua\");\n\n    return functions_;\n}\n#endif\n\nvoid LangLua::LoadProgram(const char *filename)\n{\n    int err = 0;\n\n    if ((err =luaL_loadfile(lua, filename)) != 0)\n        throw std::runtime_error(\"Lua script \" + std::string(filename) + \" error (\" + std::to_string(err) + \"): \\\"\" +\n                            std::string(lua_tostring(lua, -1)) + \"\\\"\");\n\n    constexpr auto functions_n = sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0]);\n\n#ifdef __arm__\n    LuaFunctionData *functions_ = GetLuaFunctions(IndicesFor<functions_n>{});\n#else\n    LuaFunctionData *functions_ = GetLuaFunctions<sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0])>();\n#endif\nluabridge::Namespace tes3mp = luabridge::getGlobalNamespace(lua).beginNamespace(\"tes3mp\");\n\nfor (unsigned i = 0; i < functions_n; i++)\n    tes3mp.addCFunction(functions_[i].name, functions_[i].func);\n\ntes3mp.endNamespace();\n\nif ((err = lua_pcall(lua, 0, 0, 0)) != 0) // Run once script for load in memory.\n    throw std::runtime_error(\"Lua script \" + std::string(filename) + \" error (\" + std::to_string(err) + \"): \\\"\" +\n                        std::string(lua_tostring(lua, -1)) + \"\\\"\");\n\n}\n\nint LangLua::FreeProgram()\n{\n    lua_close(lua);\n    return 0;\n}\n\nbool LangLua::IsCallbackPresent(const char *name)\n{\n    return luabridge::getGlobal(lua, name).isFunction();\n}\n\nboost::any LangLua::Call(const char *name, const char *argl, int buf, ...)\n{\n    va_list vargs;\n    va_start(vargs, buf);\n\n    int n_args = (int)(strlen(argl));\n\n    lua_getglobal(lua, name);\n\n    for (int index = 0; index < n_args; index++)\n    {\n        switch (argl[index])\n        {\n            case 'i':\n                luabridge::Stack<unsigned int>::push(lua,va_arg(vargs, unsigned int));\n                break;\n\n            case 'q':\n                luabridge::Stack<signed int>::push(lua,va_arg(vargs, signed int));\n                break;\n\n            case 'l':\n                luabridge::Stack<unsigned long long>::push(lua, va_arg(vargs, unsigned long long));\n                break;\n\n            case 'w':\n                luabridge::Stack<signed long long>::push(lua, va_arg(vargs, signed long long));\n                break;\n\n            case 'f':\n                luabridge::Stack<double>::push(lua, va_arg(vargs, double));\n                break;\n\n            case 'p':\n                luabridge::Stack<void*>::push(lua, va_arg(vargs, void*));\n                break;\n\n            case 's':\n                luabridge::Stack<const char*>::push(lua, va_arg(vargs, const char*));\n                break;\n\n            case 'b':\n                luabridge::Stack<bool>::push(lua, (bool) va_arg(vargs, int));\n                break;\n\n            default:\n                throw std::runtime_error(\"C++ call: Unknown argument identifier \" + argl[index]);\n        }\n    }\n\n    va_end(vargs);\n\n    luabridge::LuaException::pcall(lua, n_args, 1);\n    return boost::any(luabridge::LuaRef::fromStack(lua, -1));\n}\n\nboost::any LangLua::Call(const char *name, const char *argl, const std::vector<boost::any> &args)\n{\n    int n_args = (int)(strlen(argl));\n\n    lua_getglobal(lua, name);\n\n    for (int index = 0; index < n_args; index++)\n    {\n        switch (argl[index])\n        {\n            case 'i':\n                luabridge::Stack<unsigned int>::push(lua, boost::any_cast<unsigned int>(args.at(index)));\n                break;\n\n            case 'q':\n                luabridge::Stack<signed int>::push(lua, boost::any_cast<signed int>(args.at(index)));\n                break;\n\n            case 'l':\n                luabridge::Stack<unsigned long long>::push(lua, boost::any_cast<unsigned long long>(args.at(index)));\n                break;\n\n            case 'w':\n                luabridge::Stack<signed long long>::push(lua, boost::any_cast<signed long long>(args.at(index)));\n                break;\n\n            case 'f':\n                luabridge::Stack<double>::push(lua, boost::any_cast<double>(args.at(index)));\n                break;\n\n            case 'p':\n                luabridge::Stack<void *>::push(lua, boost::any_cast<void *>(args.at(index)));\n                break;\n\n            case 's':\n                luabridge::Stack<const char *>::push(lua, boost::any_cast<const char *>(args.at(index)));\n                break;\n\n            case 'b':\n                luabridge::Stack<bool>::push(lua, boost::any_cast<int>(args.at(index)));\n                break;\n            default:\n                throw std::runtime_error(\"Lua call: Unknown argument identifier \" + argl[index]);\n        }\n    }\n\n    luabridge::LuaException::pcall(lua, n_args, 1);\n    return boost::any(luabridge::LuaRef::fromStack(lua, -1));\n}\n\nvoid LangLua::AddPackagePath(const std::string& path)\n{\n    packagePath.emplace(path);\n}\n\nvoid LangLua::AddPackageCPath(const std::string& path)\n{\n    packageCPath.emplace(path);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/LangLua/LangLua.hpp",
    "content": "#ifndef PLUGINSYSTEM3_LANGLUA_HPP\n#define PLUGINSYSTEM3_LANGLUA_HPP\n\n#include \"lua.hpp\"\n\n#include <extern/LuaBridge/LuaBridge.h>\n#include <LuaBridge.h>\n#include <set>\n\n#include <boost/any.hpp>\n#include \"../ScriptFunction.hpp\"\n#include \"../Language.hpp\"\n\nstruct LuaFunctionData\n{\n    const char* name;\n    lua_CFunction func;\n};\n\nclass LangLua: public Language\n{\npublic:\n    virtual lib_t GetInterface() override;\n    lua_State *lua;\npublic:\n    LangLua();\n    LangLua(lua_State *lua);\n    ~LangLua();\n\n    static void AddPackagePath(const std::string &path);\n    static void AddPackageCPath(const std::string &path);\n\n    static int MakePublic(lua_State *lua) noexcept;\n    static int CallPublic(lua_State *lua);\n\n    static int CreateTimer(lua_State *lua) noexcept;\n    static int CreateTimerEx(lua_State *lua);\n\n    virtual void LoadProgram(const char *filename) override;\n    virtual int FreeProgram() override;\n    virtual bool IsCallbackPresent(const char *name) override;\n    virtual boost::any Call(const char *name, const char *argl, int buf, ...) override;\n    virtual boost::any Call(const char *name, const char *argl, const std::vector<boost::any> &args) override;\nprivate:\n    static std::set<std::string> packageCPath;\n    static std::set<std::string> packagePath;\n};\n\n\n#endif //PLUGINSYSTEM3_LANGLUA_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/LangLua/LuaFunc.cpp",
    "content": "#include <iostream>\n#include \"LangLua.hpp\"\n#include <Script/API/TimerAPI.hpp>\n#include <Script/API/PublicFnAPI.hpp>\n\ninline std::vector<boost::any> DefToVec(lua_State *lua, std::string types, int args_begin, int args_n)\n{\n    std::vector<boost::any> args;\n\n    for (int i = args_begin; i < args_n + args_begin; i++)\n    {\n        switch (types[i - args_begin])\n        {\n            case 'i':\n            {\n                args.emplace_back(luabridge::Stack<unsigned int>::get(lua, i));\n                break;\n            }\n\n            case 'q':\n            {\n                args.emplace_back(luabridge::Stack<signed int>::get(lua, i));\n                break;\n            }\n\n                /*case 'l':\n                {\n                    args.emplace_back(luabridge::Stack<unsigned long long>::get(lua, i));\n                    break;\n                }\n\n                case 'w':\n                {\n                    args.emplace_back(luabridge::Stack<signed long long>::get(lua, i));\n                    break;\n                }*/\n\n            case 'f':\n            {\n                args.emplace_back(luabridge::Stack<double>::get(lua, i));\n                break;\n            }\n\n            case 's':\n            {\n                args.emplace_back(luabridge::Stack<const char*>::get(lua, i));\n                break;\n            }\n\n            default:\n            {\n                std::stringstream ssErr;\n                ssErr << \"Lua: Unknown argument identifier\" << \"\\\"\" << types[i] << \"\\\"\" << std::endl;\n                throw std::runtime_error(ssErr.str());\n            }\n        }\n    }\n    return args;\n}\n\nint LangLua::MakePublic(lua_State *lua) noexcept\n{\n    const char * callback = luabridge::Stack<const char*>::get(lua, 1);\n    const char * name = luabridge::Stack<const char*>::get(lua, 2);\n    char ret_type = luabridge::Stack<char>::get(lua, 3);\n    const char * def = luabridge::Stack<const char*>::get(lua, 4);\n\n    Public::MakePublic(callback, lua, name, ret_type, def);\n    return 0;\n\n}\n\nint LangLua::CallPublic(lua_State *lua)\n{\n    const char * name = luabridge::Stack<const char*>::get(lua, 1);\n\n    int args_n = lua_gettop(lua) - 1;\n\n    std::string types = Public::GetDefinition(name);\n\n    if (args_n  != (long)types.size())\n        throw std::invalid_argument(\"Script call: Number of arguments does not match definition\");\n\n    std::vector<boost::any> args = DefToVec(lua, types, 2, args_n);\n\n    boost::any result = Public::Call(&name[0], args);\n    if (result.empty())\n        return 0;\n\n    if (result.type().hash_code() == typeid(signed int).hash_code())\n        luabridge::Stack<signed int>::push(lua, boost::any_cast<signed int>(result));\n    else if (result.type().hash_code() == typeid(unsigned int).hash_code())\n        luabridge::Stack<unsigned int>::push(lua, boost::any_cast<unsigned int>(result));\n    else if (result.type().hash_code() == typeid(double).hash_code())\n        luabridge::Stack<double>::push(lua, boost::any_cast<double>(result));\n    else if (result.type().hash_code() == typeid(const char*).hash_code())\n        luabridge::Stack<const char*>::push(lua, boost::any_cast<const char*>(result));\n    return 1;\n}\n\nint LangLua::CreateTimer(lua_State *lua) noexcept\n{\n\n    const char * callback= luabridge::Stack<const char*>::get(lua, 1);\n    int msec = luabridge::Stack<int>::get(lua, 2);\n\n    int id = mwmp::TimerAPI::CreateTimerLua(lua, callback, msec, \"\", std::vector<boost::any>());\n    luabridge::push(lua, id);\n    return 1;\n}\n\nint LangLua::CreateTimerEx(lua_State *lua)\n{\n    const char * callback = luabridge::Stack<const char*>::get(lua, 1);\n    int msec = luabridge::Stack<int>::get(lua, 2);\n\n    const char * types = luabridge::Stack<const char*>::get(lua, 3);\n\n    int args_n = (int)lua_strlen(lua, 3);\n\n    std::vector<boost::any> args;\n\n    for (int i = 4; i < args_n + 4; i++)\n    {\n        switch (types[i - 4])\n        {\n            case 'i':\n            {\n                args.emplace_back(luabridge::Stack<unsigned int>::get(lua, i));\n                break;\n            }\n\n            case 'q':\n            {\n                args.emplace_back(luabridge::Stack<signed int>::get(lua, i));\n                break;\n            }\n\n                /*case 'l':\n                {\n                    args.emplace_back(luabridge::Stack<unsigned long long>::get(lua, i));\n                    break;\n                }\n\n                case 'w':\n                {\n                    args.emplace_back(luabridge::Stack<signed long long>::get(lua, i));\n                    break;\n                }*/\n\n            case 'f':\n            {\n                args.emplace_back(luabridge::Stack<double>::get(lua, i));\n                break;\n            }\n\n            case 's':\n            {\n                args.emplace_back(luabridge::Stack<const char*>::get(lua, i));\n                break;\n            }\n\n            default:\n            {\n                std::stringstream ssErr;\n                ssErr << \"Lua: Unknown argument identifier\" << \"\\\"\" << types[i] << \"\\\"\" << std::endl;\n                throw std::runtime_error(ssErr.str());\n            }\n        }\n    }\n\n\n    int id = mwmp::TimerAPI::CreateTimerLua(lua, callback, msec, types, args);\n    luabridge::push(lua, id);\n    return 1;\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/LangNative/LangNative.cpp",
    "content": "#ifndef _WIN32\n    #include <dlfcn.h>\n#endif\n\n#include <stdexcept>\n#include \"LangNative.hpp\"\n#include <Script/SystemInterface.hpp>\n#include <Script/Script.hpp>\n\ntemplate<typename R>\nbool SetScript(lib_t lib, const char *name, R value)\n{\n    SystemInterface<R *> result(lib, name);\n\n    if (result)\n        *result.result = value;\n\n    return result.operator bool();\n}\n\nvoid LangNative::LoadProgram(const char *filename)\n{\n    FILE *file = fopen(filename, \"rb\");\n\n    if (!file)\n        throw std::runtime_error(\"Script not found: \" + std::string(filename));\n\n    fclose(file);\n\n#ifdef _WIN32\n    lib = LoadLibrary(filename);\n#else\n    lib = dlopen(filename, RTLD_LAZY);\n#endif\n\n    if (!lib)\n        throw std::runtime_error(\"Was not able to load C++ script: \" + std::string(filename));\n\n    try\n    {\n\n        const char *prefix = SystemInterface<const char *>(lib, \"prefix\").result;\n        std::string pf(prefix);\n\n        for (const auto &function : ScriptFunctions::functions)\n            if (!SetScript(lib, std::string(pf + function.name).c_str(), function.func.addr))\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_WARN, \"Script function pointer not found: %s\", function.name);\n    }\n    catch (...)\n    {\n        FreeProgram();\n        throw;\n    }\n}\n\nint LangNative::FreeProgram()\n{\n#ifdef _WIN32\n    FreeLibrary(lib);\n#else\n    dlclose(lib);\n#endif\n    return 0;\n}\n\nbool LangNative::IsCallbackPresent(const char *name)\n{\n    return true;\n}\n\nboost::any LangNative::Call(const char *name, const char *argl, int buf, ...)\n{\n    return nullptr;\n}\n\nboost::any LangNative::Call(const char *name, const char *argl, const std::vector<boost::any> &args)\n{\n    return nullptr;\n}\n\n\nlib_t LangNative::GetInterface()\n{\n    return lib;\n}\n\n\nLangNative::LangNative()\n{\n\n}\n\nLangNative::~LangNative()\n{\n\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/LangNative/LangNative.hpp",
    "content": "#ifndef PLUGINSYSTEM3_LANGNATIVE_HPP\n#define PLUGINSYSTEM3_LANGNATIVE_HPP\n\n\n#include <Script/Language.hpp>\n#include <Script/SystemInterface.hpp>\n\nclass LangNative : public Language\n{\n    lib_t lib;\npublic:\n    virtual lib_t GetInterface() override;\n    LangNative();\n    ~LangNative();\n    virtual void LoadProgram(const char *filename) override;\n    virtual int FreeProgram() override;\n    virtual bool IsCallbackPresent(const char *name) override;\n    virtual boost::any Call(const char *name, const char *argl, int buf, ...) override;\n    virtual boost::any Call(const char *name, const char *argl, const std::vector<boost::any> &args) override;\n\n};\n\n\n#endif //PLUGINSYSTEM3_LANGNATIVE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Language.hpp",
    "content": "#ifndef PLUGINSYSTEM3_LANGUAGE_HPP\n#define PLUGINSYSTEM3_LANGUAGE_HPP\n\n#include \"Types.hpp\"\n\n#include <boost/any.hpp>\n#include <vector>\n\nclass Language\n{\npublic:\n    virtual ~Language(){}\n    virtual void LoadProgram(const char* filename) = 0;\n    virtual int FreeProgram() = 0;\n    virtual bool IsCallbackPresent(const char* name) = 0;\n    virtual boost::any Call(const char* name, const char* argl, int buf, ...) = 0;\n    virtual boost::any Call(const char* name, const char* argl, const std::vector<boost::any>& args) = 0;\n\n    virtual lib_t GetInterface() = 0;\n\n};\n\n\n#endif //PLUGINSYSTEM3_LANGUAGE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Platform.hpp",
    "content": "#ifndef PLATFORM_HPP\n#define PLATFORM_HPP\n\n#if _MSC_VER\n#ifdef _M_X86\n#define ARCH_X86\n#endif\n#endif\n\n#if __GNUC__\n#ifdef __i386__\n#define ARCH_X86\n#endif\n#endif\n\n#endif //PLATFORM_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Script.cpp",
    "content": "#include \"Script.hpp\"\n#include \"LangNative/LangNative.hpp\"\n\n#if defined (ENABLE_LUA)\n#include \"LangLua/LangLua.hpp\"\n#endif\n\nScript::ScriptList Script::scripts;\nstd::string Script::moddir;\n\nScript::Script(const char *path)\n{\n    FILE *file = fopen(path, \"rb\");\n\n    if (!file)\n        throw std::runtime_error(\"Script not found: \" + std::string(path));\n\n    fclose(file);\n\n#ifdef _WIN32\n    if (strstr(path, \".dll\"))\n#else\n    if (strstr(path, \".so\"))\n#endif\n    {\n        script_type = SCRIPT_CPP;\n        lang = new LangNative();\n    }\n#if defined (ENABLE_LUA)\n    else if (strstr(path, \".lua\") || strstr(path, \".t\"))\n    {\n        lang = new LangLua();\n        script_type = SCRIPT_LUA;\n    }\n#endif\n    else\n        throw std::runtime_error(\"Script type not recognized: \" + std::string(path));\n\n    try\n    {\n        lang->LoadProgram(path);\n    }\n    catch (...)\n    {\n        lang->FreeProgram();\n        throw;\n    }\n\n}\n\n\nScript::~Script()\n{\n    lang->FreeProgram();\n\n    delete lang;\n}\n\nvoid Script::LoadScripts(char *scripts, const char *base)\n{\n    char *token = strtok(scripts, \",\");\n\n    try\n    {\n        while (token)\n        {\n            char path[4096];\n            snprintf(path, sizeof(path), Utils::convertPath(\"%s/%s/%s\").c_str(), base, \"scripts\", token);\n            Script::scripts.emplace_back(new Script(path));\n            token = strtok(nullptr, \",\");\n        }\n    }\n    catch (...)\n    {\n        UnloadScripts();\n        throw;\n    }\n}\n\nvoid Script::UnloadScripts()\n{\n    //Public::DeleteAll();\n    scripts.clear();\n}\n\nvoid Script::LoadScript(const char *script, const char *base)\n{\n    char path[4096];\n    snprintf(path, sizeof(path), Utils::convertPath(\"%s/%s/%s\").c_str(), base, \"scripts\", script);\n    Script::scripts.emplace_back(new Script(path));\n}\n\nvoid Script::SetModDir(const std::string &moddir)\n{\n    if (Script::moddir.empty()) // do not allow to change in runtime\n        Script::moddir = moddir;\n}\n\nconst char* Script::GetModDir()\n{\n    return moddir.c_str();\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/Script.hpp",
    "content": "#ifndef PLUGINSYSTEM3_SCRIPT_HPP\n#define PLUGINSYSTEM3_SCRIPT_HPP\n\n#include <boost/any.hpp>\n#include <unordered_map>\n#include <memory>\n\n#include \"Types.hpp\"\n#include \"SystemInterface.hpp\"\n#include \"ScriptFunction.hpp\"\n#include \"ScriptFunctions.hpp\"\n#include \"Language.hpp\"\n\n#include \"Networking.hpp\"\n\nclass Script : private ScriptFunctions\n{\n    // http://imgur.com/hU0N4EH\nprivate:\n\n    Language *lang;\n\n    enum\n    {\n        SCRIPT_CPP,\n        SCRIPT_LUA\n    };\n\n    template<typename R>\n    R GetScript(const char *name)\n    {\n        if (script_type == SCRIPT_CPP)\n        {\n            return SystemInterface<R>(lang->GetInterface(), name).result;\n        }\n        else\n        {\n            return reinterpret_cast<R>(lang->IsCallbackPresent(name));\n        }\n    }\n\n    int script_type;\n    std::unordered_map<unsigned int, FunctionEllipsis<void>> callbacks_;\n\n    typedef std::vector<std::unique_ptr<Script>> ScriptList;\n    static ScriptList scripts;\n\n    Script(const char *path);\n\n    Script(const Script&) = delete;\n    Script& operator=(const Script&) = delete;\n\nprotected:\n    static std::string moddir;\npublic:\n    ~Script();\n\n    static void LoadScript(const char *script, const char* base);\n    static void LoadScripts(char* scripts, const char* base);\n    static void UnloadScripts();\n    static void SetModDir(const std::string &moddir);\n    static const char* GetModDir();\n\n    static constexpr ScriptCallbackData const& CallBackData(const unsigned int I, const unsigned int N = 0) {\n        return callbacks[N].index == I ? callbacks[N] : CallBackData(I, N + 1);\n    }\n\n    template<size_t N>\n    static constexpr unsigned int CallbackIdentity(const char(&str)[N])\n    {\n        return Utils::hash(str);\n    }\n\n    template<unsigned int I, bool B = false, typename... Args>\n    static unsigned int Call(Args&&... args) {\n        constexpr ScriptCallbackData const& data = CallBackData(I);\n        static_assert(data.callback.matches(TypeString<typename std::remove_reference<Args>::type...>::value),\n                      \"Wrong number or types of arguments\");\n\n        unsigned int count = 0;\n\n        for (auto& script : scripts)\n        {\n            if (!script->callbacks_.count(I))\n                script->callbacks_.emplace(I, script->GetScript<FunctionEllipsis<void>>(data.name));\n\n            auto callback = script->callbacks_[I];\n\n            if (!callback)\n                continue;\n\n            if (script->script_type == SCRIPT_CPP)\n                (callback)(std::forward<Args>(args)...);\n#if defined (ENABLE_LUA)\n            else if (script->script_type == SCRIPT_LUA)\n            {\n                try\n                {\n                    script->lang->Call(data.name, data.callback.types, B, std::forward<Args>(args)...);\n                }\n                catch (std::exception &e)\n                {\n                    LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, e.what());\n                    Script::Call<Script::CallbackIdentity(\"OnServerScriptCrash\")>(e.what());\n\n                    if (!mwmp::Networking::getPtr()->getScriptErrorIgnoringState())\n                        throw;\n                }\n            }\n#endif\n            ++count;\n        }\n\n        return count;\n    }\n};\n\n#endif //PLUGINSYSTEM3_SCRIPT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/ScriptFunction.cpp",
    "content": "#include<iostream>\n#include <stdexcept>\n#include \"ScriptFunction.hpp\"\n\n#if defined (ENABLE_LUA)\n#include \"LangLua/LangLua.hpp\"\n#endif\n\nScriptFunction::ScriptFunction(ScriptFunc fCpp,char ret_type, const std::string &def) :\n        fCpp(fCpp), ret_type(ret_type), def(def), script_type(SCRIPT_CPP)\n{\n\n}\n#if defined (ENABLE_LUA)\nScriptFunction::ScriptFunction(const ScriptFuncLua &fLua, lua_State *lua, char ret_type, const std::string &def) :\n        fLua({lua, fLua}), ret_type(ret_type), def(def), script_type(SCRIPT_LUA)\n{\n\n}\n#endif\n\n\nScriptFunction::~ScriptFunction()\n{\n#if defined (ENABLE_LUA)\n    if (script_type == SCRIPT_LUA)\n        fLua.name.~ScriptFuncLua();\n#endif\n}\n\nboost::any ScriptFunction::Call(const std::vector<boost::any> &args)\n{\n    boost::any result;\n\n    if (def.length() != args.size())\n        throw std::runtime_error(\"Script call: Number of arguments does not match definition\");\n#if defined (ENABLE_LUA)\n    else if (script_type == SCRIPT_LUA)\n    {\n        LangLua langLua(fLua.lua);\n        boost::any any = langLua.Call(fLua.name.c_str(), def.c_str(), args);\n\n        switch (ret_type)\n        {\n            case 'i':\n                result = boost::any_cast<luabridge::LuaRef>(any).cast<unsigned int>();\n                break;\n            case 'q':\n                result = boost::any_cast<luabridge::LuaRef>(any).cast<signed int>();\n                break;\n            case 'f':\n                result = boost::any_cast<luabridge::LuaRef>(any).cast<double>();\n                break;\n            case 's':\n                result = boost::any_cast<luabridge::LuaRef>(any).cast<const char*>();\n                break;\n            case 'v':\n                result = boost::any();\n                break;\n            default:\n                throw std::runtime_error(\"Lua call: Unknown return type\" + ret_type);\n        }\n\n        lua_settop(fLua.lua, 0);\n    }\n#endif\n\n    return result;\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/ScriptFunction.hpp",
    "content": "#ifndef SCRIPTFUNCTION_HPP\n#define SCRIPTFUNCTION_HPP\n\n#include <boost/any.hpp>\n#include <string>\n#include <vector>\n#if defined (ENABLE_LUA)\n#include \"LangLua/LangLua.hpp\"\n#endif\n\ntypedef unsigned long long(*ScriptFunc)();\n#if defined (ENABLE_LUA)\ntypedef std::string ScriptFuncLua;\n#endif\n\nclass ScriptFunction\n{\nprotected:\n    union\n    {\n        ScriptFunc fCpp;\n#if defined (ENABLE_LUA)\n        struct\n        {\n            lua_State *lua;\n            ScriptFuncLua name;\n        } fLua;\n#endif\n    };\n\nprotected:\n    char ret_type;\n    std::string def;\n    int script_type;\n    enum\n    {\n        SCRIPT_CPP,\n        SCRIPT_LUA\n    };\n\n    ScriptFunction(ScriptFunc fCpp, char ret_type, const std::string &def);\n#if defined (ENABLE_LUA)\n    ScriptFunction(const ScriptFuncLua &fPawn, lua_State *lua, char ret_type, const std::string &def);\n#endif\n    virtual ~ScriptFunction();\n\n    boost::any Call(const std::vector<boost::any> &args);\n};\n\n#endif //SCRIPTFUNCTION_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/ScriptFunctions.cpp",
    "content": "#include \"ScriptFunctions.hpp\"\n#include \"API/PublicFnAPI.hpp\"\n#include <cstdarg>\n#include <iostream>\n#include <apps/openmw-mp/Player.hpp>\n#include <apps/openmw-mp/Networking.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\ntemplate<typename... Types>\nconstexpr char TypeString<Types...>::value[];\nconstexpr ScriptFunctionData ScriptFunctions::functions[];\nconstexpr ScriptCallbackData ScriptFunctions::callbacks[];\n\nvoid ScriptFunctions::MakePublic(ScriptFunc _public, const char *name, char ret_type, const char *def) noexcept\n{\n    Public::MakePublic(_public, name, ret_type, def);\n}\n\nboost::any ScriptFunctions::CallPublic(const char *name, va_list args) noexcept\n{\n    std::vector<boost::any> params;\n\n    try\n    {\n        std::string def = Public::GetDefinition(name);\n        Utils::getArguments(params, args, def);\n\n        return Public::Call(name, params);\n    }\n    catch (...) {}\n\n    return 0;\n}\n"
  },
  {
    "path": "apps/openmw-mp/Script/ScriptFunctions.hpp",
    "content": "#ifndef SCRIPTFUNCTIONS_HPP\n#define SCRIPTFUNCTIONS_HPP\n\n#include <Script/Functions/Actors.hpp>\n#include <Script/Functions/Books.hpp>\n#include <Script/Functions/Cells.hpp>\n#include <Script/Functions/CharClass.hpp>\n#include <Script/Functions/Chat.hpp>\n#include <Script/Functions/Dialogue.hpp>\n#include <Script/Functions/Factions.hpp>\n#include <Script/Functions/GUI.hpp>\n#include <Script/Functions/Items.hpp>\n#include <Script/Functions/Mechanics.hpp>\n#include <Script/Functions/Miscellaneous.hpp>\n#include <Script/Functions/Objects.hpp>\n#include <Script/Functions/Positions.hpp>\n#include <Script/Functions/Quests.hpp>\n#include <Script/Functions/RecordsDynamic.hpp>\n#include <Script/Functions/Shapeshift.hpp>\n#include <Script/Functions/Server.hpp>\n#include <Script/Functions/Settings.hpp>\n#include <Script/Functions/Spells.hpp>\n#include <Script/Functions/Stats.hpp>\n#include <Script/Functions/Worldstate.hpp>\n#include <RakNetTypes.h>\n#include <tuple>\n#include <apps/openmw-mp/Player.hpp>\n#include \"ScriptFunction.hpp\"\n#include \"Types.hpp\"\n\n#include <components/openmw-mp/TimedLog.hpp>\n\n#ifndef __PRETTY_FUNCTION__\n#define __PRETTY_FUNCTION__ __FUNCTION__\n#endif\n\n#define GET_PLAYER(pid, pl, retvalue) \\\n     pl = Players::getPlayer(pid); \\\n     if (player == 0) {\\\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"%s: Player with pid \\'%d\\' not found\\n\", __PRETTY_FUNCTION__, pid);\\\n        /*ScriptFunctions::StopServer(1);*/ \\\n        return retvalue;\\\n}\n\n\nclass ScriptFunctions\n{\npublic:\n\n    static void MakePublic(ScriptFunc _public, const char *name, char ret_type, const char *def) noexcept;\n    static boost::any CallPublic(const char *name, va_list args) noexcept;\n\n     /**\n     * \\brief Create a timer that will run a script function after a certain interval.\n     *\n     * \\param callback The Lua script function.\n     * \\param msec The interval in miliseconds.\n     * \\return The ID of the timer thus created.\n     */\n    static int CreateTimer(ScriptFunc callback, int msec) noexcept;\n\n    /**\n    * \\brief Create a timer that will run a script function after a certain interval and pass\n    *        certain arguments to it.\n    *\n    * Example usage:\n    * - tes3mp.CreateTimerEx(\"OnTimerTest1\", 250, \"i\", 90)\n    * - tes3mp.CreateTimerEx(\"OnTimerTest2\", 500, \"sif\", \"Test string\", 60, 77.321)\n    *\n    * \\param callback The Lua script function.\n    * \\param msec The interval in miliseconds.\n    * \\param types The argument types.\n    * \\param args The arguments.\n    * \\return The ID of the timer thus created.\n    */\n    static int CreateTimerEx(ScriptFunc callback, int msec, const char *types, va_list args) noexcept;\n\n    /**\n    * \\brief Start the timer with a certain ID.\n    *\n    * \\param timerId The timer ID.\n    * \\return void\n    */\n    static void StartTimer(int timerId) noexcept;\n\n    /**\n    * \\brief Stop the timer with a certain ID.\n    *\n    * \\param timerId The timer ID.\n    * \\return void\n    */\n    static void StopTimer(int timerId) noexcept;\n\n    /**\n    * \\brief Restart the timer with a certain ID for a certain interval.\n    *\n    * \\param timerId The timer ID.\n    * \\param msec The interval in miliseconds.\n    * \\return void\n    */\n    static void RestartTimer(int timerId, int msec) noexcept;\n\n    /**\n    * \\brief Free the timer with a certain ID.\n    *\n    * \\param timerId The timer ID.\n    * \\return void\n    */\n    static void FreeTimer(int timerId) noexcept;\n\n    /**\n    * \\brief Check whether a timer is elapsed.\n    *\n    * \\param timerId The timer ID.\n    * \\return Whether the timer is elapsed.\n    */\n    static bool IsTimerElapsed(int timerId) noexcept;\n\n\n    static constexpr ScriptFunctionData functions[]{\n            {\"CreateTimer\",         ScriptFunctions::CreateTimer},\n            {\"CreateTimerEx\",       ScriptFunctions::CreateTimerEx},\n            {\"MakePublic\",          ScriptFunctions::MakePublic},\n            {\"CallPublic\",          ScriptFunctions::CallPublic},\n\n            {\"StartTimer\",          ScriptFunctions::StartTimer},\n            {\"StopTimer\",           ScriptFunctions::StopTimer},\n            {\"RestartTimer\",        ScriptFunctions::RestartTimer},\n            {\"FreeTimer\",           ScriptFunctions::FreeTimer},\n            {\"IsTimerElapsed\",      ScriptFunctions::IsTimerElapsed},\n\n            ACTORAPI,\n            BOOKAPI,\n            CELLAPI,\n            CHARCLASSAPI,\n            CHATAPI,\n            DIALOGUEAPI,\n            FACTIONAPI,\n            GUIAPI,\n            ITEMAPI,\n            MECHANICSAPI,\n            MISCELLANEOUSAPI,\n            POSITIONAPI,\n            QUESTAPI,\n            RECORDSDYNAMICAPI,\n            SHAPESHIFTAPI,\n            SERVERAPI,\n            SETTINGSAPI,\n            SPELLAPI,\n            STATAPI,\n            OBJECTAPI,\n            WORLDSTATEAPI\n    };\n\n    static constexpr ScriptCallbackData callbacks[]{\n            {\"OnServerInit\",             Callback<>()},\n            {\"OnServerPostInit\",         Callback<>()},\n            {\"OnServerExit\",             Callback<bool>()},\n            {\"OnServerScriptCrash\",      Callback<const char*>()},\n            {\"OnPlayerConnect\",          Callback<unsigned short>()},\n            {\"OnPlayerDisconnect\",       Callback<unsigned short>()},\n            {\"OnPlayerDeath\",            Callback<unsigned short>()},\n            {\"OnPlayerResurrect\",        Callback<unsigned short>()},\n            {\"OnPlayerCellChange\",       Callback<unsigned short>()},\n            {\"OnPlayerAttribute\",        Callback<unsigned short>()},\n            {\"OnPlayerSkill\",            Callback<unsigned short>()},\n            {\"OnPlayerLevel\",            Callback<unsigned short>()},\n            {\"OnPlayerBounty\",           Callback<unsigned short>()},\n            {\"OnPlayerReputation\",       Callback<unsigned short>()},\n            {\"OnPlayerEquipment\",        Callback<unsigned short>()},\n            {\"OnPlayerInventory\",        Callback<unsigned short>()},\n            {\"OnPlayerJournal\",          Callback<unsigned short>()},\n            {\"OnPlayerFaction\",          Callback<unsigned short>()},\n            {\"OnPlayerShapeshift\",       Callback<unsigned short>()},\n            {\"OnPlayerSpellbook\",        Callback<unsigned short>()},\n            {\"OnPlayerSpellsActive\",     Callback<unsigned short>()},\n            {\"OnPlayerCooldowns\",        Callback<unsigned short>()},\n            {\"OnPlayerQuickKeys\",        Callback<unsigned short>()},\n            {\"OnPlayerTopic\",            Callback<unsigned short>()},\n            {\"OnPlayerDisposition\",      Callback<unsigned short>()},\n            {\"OnPlayerBook\",             Callback<unsigned short>()},\n            {\"OnPlayerItemUse\",          Callback<unsigned short>()},\n            {\"OnPlayerMiscellaneous\",    Callback<unsigned short>()},\n            {\"OnPlayerInput\",            Callback<unsigned short>()},\n            {\"OnPlayerRest\",             Callback<unsigned short>()},\n            {\"OnRecordDynamic\",          Callback<unsigned short>()},\n            {\"OnCellLoad\",               Callback<unsigned short, const char*>()},\n            {\"OnCellUnload\",             Callback<unsigned short, const char*>()},\n            {\"OnCellDeletion\",           Callback<const char*>()},\n            {\"OnConsoleCommand\",         Callback<unsigned short, const char*>()},\n            {\"OnContainer\",              Callback<unsigned short, const char*>()},\n            {\"OnDoorState\",              Callback<unsigned short, const char*>()},\n            {\"OnObjectActivate\",         Callback<unsigned short, const char*>()},\n            {\"OnObjectHit\",              Callback<unsigned short, const char*>()},\n            {\"OnObjectPlace\",            Callback<unsigned short, const char*>()},\n            {\"OnObjectState\",            Callback<unsigned short, const char*>()},\n            {\"OnObjectSpawn\",            Callback<unsigned short, const char*>()},\n            {\"OnObjectDelete\",           Callback<unsigned short, const char*>()},\n            {\"OnObjectLock\",             Callback<unsigned short, const char*>()},\n            {\"OnObjectDialogueChoice\",   Callback<unsigned short, const char*>()},\n            {\"OnObjectMiscellaneous\",    Callback<unsigned short, const char*>()},\n            {\"OnObjectRestock\",          Callback<unsigned short, const char*>()},\n            {\"OnObjectScale\",            Callback<unsigned short, const char*>()},\n            {\"OnObjectSound\",            Callback<unsigned short, const char*>()},\n            {\"OnObjectTrap\",             Callback<unsigned short, const char*>()},\n            {\"OnVideoPlay\",              Callback<unsigned short, const char*>()},\n            {\"OnActorList\",              Callback<unsigned short, const char*>()},\n            {\"OnActorEquipment\",         Callback<unsigned short, const char*>()},\n            {\"OnActorAI\",                Callback<unsigned short, const char*>()},\n            {\"OnActorDeath\",             Callback<unsigned short, const char*>()},\n            {\"OnActorSpellsActive\",      Callback<unsigned short, const char*>()},\n            {\"OnActorCellChange\",        Callback<unsigned short, const char*>()},\n            {\"OnActorTest\",              Callback<unsigned short, const char*>()},\n            {\"OnPlayerSendMessage\",      Callback<unsigned short, const char*>()},\n            {\"OnPlayerEndCharGen\",       Callback<unsigned short>()},\n            {\"OnGUIAction\",              Callback<unsigned short, int, const char*>()},\n            {\"OnWorldKillCount\",         Callback<unsigned short>()},\n            {\"OnWorldMap\",               Callback<unsigned short>()},\n            {\"OnWorldWeather\",           Callback<unsigned short>()},\n            {\"OnClientScriptLocal\",      Callback<unsigned short, const char*>()},\n            {\"OnClientScriptGlobal\",     Callback<unsigned short>()},\n            {\"OnMpNumIncrement\",         Callback<int>()},\n            {\"OnRequestDataFileList\",    Callback<>()}\n    };\n};\n\n#endif //SCRIPTFUNCTIONS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/SystemInterface.hpp",
    "content": "#ifndef PLUGINSYSTEM3_SYSTEMINTERFACE_HPP\n#define PLUGINSYSTEM3_SYSTEMINTERFACE_HPP\n\n#ifdef _WIN32\n#include <winsock2.h>\n#else\n#include <dlfcn.h>\n#endif\n\n#include \"Types.hpp\"\n\ntemplate<typename R = void*>\nstruct SystemInterface\n{\n\n    union\n    {\n        R result;\n#ifdef _WIN32\n        decltype(GetProcAddress(lib_t(), nullptr)) data;\n#else\n        decltype(dlsym(lib_t(), nullptr)) data;\n#endif\n    };\n\n#ifndef _WIN32\n    static_assert(sizeof(result) == sizeof(data), \"R should have the same size\");\n#endif\n\n    SystemInterface() : data(nullptr) {}\n    explicit operator bool() { return data; }\n\n#ifdef _WIN32\n    SystemInterface(lib_t handle, const char* name) : data(GetProcAddress(handle, name)) {}\n#else\n    SystemInterface(lib_t handle, const char* name) : data(dlsym(handle, name)) {}\n#endif\n};\n#endif //PLUGINSYSTEM3_SYSTEMINTERFACE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Script/Types.hpp",
    "content": "#ifndef TMPTYPES_HPP\n#define TMPTYPES_HPP\n\n\n#include <cstddef>\n#include <cstdint>\n#include <type_traits>\n#include <RakNetTypes.h>\n#include \"Utils.hpp\"\n\n#ifdef _WIN32\n#include <winsock2.h>\n#endif\n\n#ifdef _WIN32\ntypedef HMODULE lib_t;\n#else\ntypedef void* lib_t;\n#endif\n\ntemplate<typename T> struct sizeof_void { enum { value = sizeof(T) }; };\ntemplate<> struct sizeof_void<void> { enum { value = 0 }; };\n\n\ntemplate<typename T, size_t t> struct TypeChar { static_assert(!t, \"Unsupported type in variadic type list\"); };\ntemplate<> struct TypeChar<bool, sizeof(bool)> { enum { value = 'b' }; };\ntemplate<typename T> struct TypeChar<T*, sizeof(void*)> { enum { value = 'p' }; };\ntemplate<> struct TypeChar<double*, sizeof(double*)> { enum { value = 'd' }; };\ntemplate<> struct TypeChar<RakNet::NetworkID**, sizeof(RakNet::NetworkID**)> { enum { value = 'n' }; };\ntemplate<typename T> struct TypeChar<T, sizeof(uint8_t)> { enum { value = std::is_signed<T>::value ? 'q' : 'i' }; };\ntemplate<typename T> struct TypeChar<T, sizeof(uint16_t)> { enum { value = std::is_signed<T>::value ? 'q' : 'i' }; };\ntemplate<typename T> struct TypeChar<T, sizeof(uint32_t)> { enum { value = std::is_signed<T>::value ? 'q' : 'i' }; };\ntemplate<typename T> struct TypeChar<T, sizeof(uint64_t)> { enum { value = std::is_signed<T>::value ? 'w' : 'l' }; };\ntemplate<> struct TypeChar<double, sizeof(double)> { enum { value = 'f' }; };\ntemplate<> struct TypeChar<char*, sizeof(char*)> { enum { value = 's' }; };\ntemplate<> struct TypeChar<const char*, sizeof(const char*)> { enum { value = 's' }; };\ntemplate<> struct TypeChar<void, sizeof_void<void>::value> { enum { value = 'v' }; };\n\ntemplate<const char t> struct CharType { static_assert(!t, \"Unsupported type in variadic type list\"); };\ntemplate<> struct CharType<'b'> { typedef bool type; };\ntemplate<> struct CharType<'p'> { typedef void* type; };\ntemplate<> struct CharType<'d'> { typedef double* type; };\ntemplate<> struct CharType<'n'> { typedef RakNet::NetworkID** type; };\ntemplate<> struct CharType<'q'> { typedef signed int type; };\ntemplate<> struct CharType<'i'> { typedef unsigned int type; };\ntemplate<> struct CharType<'w'> { typedef signed long long type; };\ntemplate<> struct CharType<'l'> { typedef unsigned long long type; };\ntemplate<> struct CharType<'f'> { typedef double type; };\ntemplate<> struct CharType<'s'> { typedef const char* type; };\ntemplate<> struct CharType<'v'> { typedef void type; };\n\ntemplate<typename... Types>\nstruct TypeString {\n    static constexpr char value[sizeof...(Types) + 1] = {\n            TypeChar<Types, sizeof(Types)>::value...\n    };\n};\n\n\ntemplate<typename R, typename... Types>\nusing Function = R(*)(Types...);\n\ntemplate<typename R>\nusing FunctionEllipsis = R(*)(...);\n\nstruct ScriptIdentity\n{\n    const char* types;\n    const char ret;\n    const unsigned int numargs;\n\n    constexpr bool matches(const char* types, const unsigned int N = 0) const\n    {\n        return N < numargs ? this->types[N] == types[N] && matches(types, N + 1) : this->types[N] == types[N];\n    }\n\n    template<typename R, typename... Types>\n    constexpr ScriptIdentity(Function<R, Types...>) : types(TypeString<Types...>::value), ret(TypeChar<R, sizeof_void<R>::value>::value), numargs(sizeof(TypeString<Types...>::value) - 1) {}\n};\n\ntemplate<typename... Types>\nusing Callback = void (*)(Types...);\n\nstruct CallbackIdentity\n{\n    const char* types;\n    const unsigned int numargs;\n\n    constexpr bool matches(const char* types, const unsigned int N = 0) const\n    {\n        return N < numargs ? this->types[N] == types[N] && matches(types, N + 1) : this->types[N] == types[N];\n    }\n\n    template<typename... Types>\n    constexpr CallbackIdentity(Callback<Types...>) : types(TypeString<Types...>::value), numargs(sizeof(TypeString<Types...>::value) - 1) {}\n};\n\n\nstruct ScriptFunctionPointer : public ScriptIdentity\n{\n    void *addr;\n#if (!defined(__clang__) && defined(__GNUC__))\n    template<typename R, typename... Types>\n    constexpr ScriptFunctionPointer(Function<R, Types...> addr) : ScriptIdentity(addr), addr((void*)(addr)) {}\n#else\n    template<typename R, typename... Types>\n    constexpr ScriptFunctionPointer(Function<R, Types...> addr) : ScriptIdentity(addr), addr(addr) {}\n#endif\n};\n\nstruct ScriptFunctionData\n{\n    const char* name;\n    const ScriptFunctionPointer func;\n\n    constexpr ScriptFunctionData(const char* name, ScriptFunctionPointer func) : name(name), func(func) {}\n};\n\nstruct ScriptCallbackData\n{\n    const char* name;\n    const unsigned int index;\n    const CallbackIdentity callback;\n\n    template<size_t N>\n    constexpr ScriptCallbackData(const char(&name)[N], CallbackIdentity _callback) : name(name), index(Utils::hash(name)), callback(_callback) {}\n};\n\n#endif //TMPTYPES_HPP\n"
  },
  {
    "path": "apps/openmw-mp/Utils.cpp",
    "content": "#include \"Utils.hpp\"\n\n#include <cstdarg>\n\nconst std::vector<std::string> Utils::split(const std::string &str, int delimiter)\n{\n    std::string buffer;\n    std::vector<std::string> result;\n\n    for (auto symb:str)\n        if (symb != delimiter)\n            buffer += symb;\n        else if (!buffer.empty())\n        {\n            result.push_back(move(buffer));\n            buffer.clear();\n        }\n    if (!buffer.empty())\n        result.push_back(move(buffer));\n\n    return result;\n}\n\nESM::Cell Utils::getCellFromDescription(std::string cellDescription)\n{\n    ESM::Cell cell;\n    cell.blank();\n\n    static std::regex exteriorCellPattern(\"^(-?\\\\d+), (-?\\\\d+)$\");\n    std::smatch baseMatch;\n\n    if (std::regex_match(cellDescription, baseMatch, exteriorCellPattern))\n    {\n        cell.mData.mFlags &= ~ESM::Cell::Interior;\n\n        // The first sub match is the whole string, so check for a length of 3\n        if (baseMatch.size() == 3)\n        {\n            cell.mData.mX = stoi(baseMatch[1].str());\n            cell.mData.mY = stoi(baseMatch[2].str());\n        }\n    }\n    else\n    {\n        cell.mData.mFlags |= ESM::Cell::Interior;\n        cell.mName = cellDescription;\n    }\n\n    return cell;\n}\n\nvoid Utils::getArguments(std::vector<boost::any> &params, va_list args, const std::string &def)\n{\n    params.reserve(def.length());\n\n    try\n    {\n        for (char c : def)\n        {\n            switch (c)\n            {\n            case 'i':\n                params.emplace_back(va_arg(args, unsigned int));\n                break;\n\n            case 'q':\n                params.emplace_back(va_arg(args, signed int));\n                break;\n\n            case 'l':\n                params.emplace_back(va_arg(args, unsigned long long));\n                break;\n\n            case 'w':\n                params.emplace_back(va_arg(args, signed long long));\n                break;\n\n            case 'f':\n                params.emplace_back(va_arg(args, double));\n                break;\n\n            case 'p':\n                params.emplace_back(va_arg(args, void*));\n                break;\n\n            case 's':\n                params.emplace_back(va_arg(args, const char*));\n                break;\n\n            case 'b':\n                params.emplace_back(va_arg(args, int));\n                break;\n\n            default:\n                throw std::runtime_error(\"C++ call: Unknown argument identifier \" + c);\n            }\n        }\n    }\n\n    catch (...)\n    {\n        va_end(args);\n        throw;\n    }\n    va_end(args);\n}\n"
  },
  {
    "path": "apps/openmw-mp/Utils.hpp",
    "content": "#ifndef OPENMW_UTILS_HPP\n#define OPENMW_UTILS_HPP\n\n#include <cstddef>\n#include <regex>\n#include <vector>\n\n#include <boost/any.hpp>\n\n#include <components/esm/loadcell.hpp>\n\n#include <components/openmw-mp/Utils.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\n#if (!defined(DEBUG_PRINTF) && defined(DEBUG))\n#define DEBUG_PRINTF(...) LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, __VA_ARGS__)\n#else\n#define DEBUG_PRINTF(...)\n#endif\n\n\nnamespace Utils\n{\n    const std::vector<std::string> split(const std::string &str, int delimiter);\n\n    ESM::Cell getCellFromDescription(std::string cellDescription);\n\n    void getArguments(std::vector<boost::any> &params, va_list args, const std::string &def);\n\n    template<size_t N>\n    constexpr unsigned int hash(const char(&str)[N], size_t I = N)\n    {\n        return (I == 1 ? ((2166136261u ^ str[0]) * 16777619u) : ((hash(str, I - 1) ^ str[I - 1]) * 16777619u));\n    }\n\n    inline unsigned int hash(const char *str, std::size_t I)\n    {\n        return (I == 1 ? ((2166136261u ^ str[0]) * 16777619u) : ((hash(str, I - 1) ^ str[I - 1]) * 16777619u));\n    }\n\n    template<typename F, typename T, typename E = void>\n    struct is_static_castable : std::false_type\n    {\n    };\n    template<typename F, typename T>\n    struct is_static_castable<F, T, typename std::conditional<true, void, decltype(static_cast<T>(std::declval<F>()))>::type>\n            : std::true_type\n    {\n    };\n\n    template<typename T, typename F>\n    inline static typename std::enable_if<is_static_castable<F *, T *>::value, T *>::type static_or_dynamic_cast(\n            F *from)\n    { return static_cast<T *>(from); }\n\n    template<typename T, typename F>\n    inline static typename std::enable_if<!is_static_castable<F *, T *>::value, T *>::type static_or_dynamic_cast(\n            F *from)\n    { return dynamic_cast<T *>(from); }\n}\n\n\n#endif //OPENMW_UTILS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/handleInput.cpp",
    "content": "namespace mwmp_input {\n    std::string windowInputBuffer;\n    void handler() {\n        char c;\n#ifndef WIN32\n        while (kbhit()) {\n            c = getch();\n#else // on Windows conio.h getch() and kbhit() are deprecated, use _getch() and _kbhit() instead\n        while (_kbhit()) {\n            c = _getch();\n#endif\n            std::cout << c << std::flush;\n            if (c == '\\n' || c == '\\r') { // handle carriage return as new line on Windows\n                std::cout << std::endl;\n                Script::Call<Script::CallbackIdentity(\"OnServerWindowInput\")>(windowInputBuffer.c_str());\n                windowInputBuffer.assign(\"\");\n            }\n            else if (c == '\\b') {\n                auto size = windowInputBuffer.size();\n                if (size > 0)\n                    windowInputBuffer.erase(size - 1);\n            }\n            else windowInputBuffer += c;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw-mp/main.cpp",
    "content": "#include <iostream>\n\n#include <boost/filesystem/fstream.hpp>\n#include <boost/iostreams/concepts.hpp>\n#include <boost/iostreams/stream_buffer.hpp>\n\n#include <components/files/configurationmanager.hpp>\n#include <components/files/escape.hpp>\n#include <components/settings/settings.hpp>\n#include <components/version/version.hpp>\n\n#include <components/openmw-mp/ErrorMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Utils.hpp>\n#include <components/openmw-mp/Version.hpp>\n\n#include <BitStream.h>\n#include <MessageIdentifiers.h>\n#include <RakPeer.h>\n#include <RakPeerInterface.h>\n\n#include \"Player.hpp\"\n#include \"Networking.hpp\"\n#include \"MasterClient.hpp\"\n#include \"Utils.hpp\"\n\n#include <apps/openmw-mp/Script/Script.hpp>\n\n#ifdef ENABLE_BREAKPAD\n#include <handler/exception_handler.h>\n#endif\n\nusing namespace mwmp;\n\n#ifdef ENABLE_BREAKPAD\ngoogle_breakpad::ExceptionHandler *pHandler = 0;\n#if defined(_WIN32)\nbool DumpCallback(const wchar_t* _dump_dir,const wchar_t* _minidump_id,void* context,EXCEPTION_POINTERS* exinfo,MDRawAssertionInfo* assertion,bool success)\n#elif defined(__linux)\nbool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, bool success)\n#endif\n{\n    // NO STACK USE, NO HEAP USE THERE !!!\n    return success;\n}\n\nvoid breakpad(std::string pathToDump)\n{\n#ifdef _WIN32\n    pHandler = new google_breakpad::ExceptionHandler(\n            L\"crashdumps\\\\\",\n            /*FilterCallback*/ 0,\n            DumpCallback,\n            0,\n            google_breakpad::ExceptionHandler::HANDLER_ALL);\n#else\n    google_breakpad::MinidumpDescriptor md(pathToDump);\n    pHandler = new google_breakpad::ExceptionHandler(\n            md,\n            /*FilterCallback*/ 0,\n            DumpCallback,\n            /*context*/ 0,\n            true,\n            -1\n    );\n#endif\n}\n\nvoid breakpad_close()\n{\n    delete pHandler;\n}\n#else\nvoid breakpad(std::string pathToDump){}\nvoid breakpad_close(){}\n#endif\n\nstd::string loadSettings (Settings::Manager & settings)\n{\n    Files::ConfigurationManager mCfgMgr;\n    // Create the settings manager and load default settings file\n    const std::string localdefault = (mCfgMgr.getLocalPath() / \"tes3mp-server-default.cfg\").string();\n    const std::string globaldefault = (mCfgMgr.getGlobalPath() / \"tes3mp-server-default.cfg\").string();\n\n    // prefer local\n    if (boost::filesystem::exists(localdefault))\n        settings.loadDefault(localdefault, false);\n    else if (boost::filesystem::exists(globaldefault))\n        settings.loadDefault(globaldefault, false);\n    else\n        throw std::runtime_error (\"No default settings file found! Make sure the file \\\"tes3mp-server-default.cfg\\\" was properly installed.\");\n\n    // load user settings if they exist\n    const std::string settingspath = (mCfgMgr.getUserConfigPath() / \"tes3mp-server.cfg\").string();\n    if (boost::filesystem::exists(settingspath))\n        settings.loadUser(settingspath);\n\n    return settingspath;\n}\n\nclass Tee : public boost::iostreams::sink\n{\npublic:\n    Tee(std::ostream &stream, std::ostream &stream2)\n            : out(stream), out2(stream2)\n    {\n    }\n\n    std::streamsize write(const char *str, std::streamsize size)\n    {\n        out.write (str, size);\n        out.flush();\n        out2.write (str, size);\n        out2.flush();\n        return size;\n    }\n\nprivate:\n    std::ostream &out;\n    std::ostream &out2;\n};\n\nboost::program_options::variables_map launchOptions(int argc, char *argv[], Files::ConfigurationManager cfgMgr)\n{\n    namespace bpo = boost::program_options;\n    bpo::variables_map variables;\n    bpo::options_description desc;\n\n    desc.add_options()\n            (\"resources\", bpo::value<Files::EscapeHashString>()->default_value(\"resources\"), \"set resources directory\")\n            (\"no-logs\", bpo::value<bool>()->implicit_value(true)->default_value(false),\n             \"Do not write logs. Useful for daemonizing.\");\n\n    cfgMgr.readConfiguration(variables, desc, true);\n\n    bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).allow_unregistered().run();\n\n    bpo::store(valid_opts, variables);\n    bpo::notify(variables);\n\n    return variables;\n}\n\nint main(int argc, char *argv[])\n{\n    Settings::Manager mgr;\n    Files::ConfigurationManager cfgMgr;\n\n    breakpad(boost::filesystem::path(cfgMgr.getLogPath()).string());\n\n    loadSettings(mgr);\n\n    auto variables = launchOptions(argc, argv, cfgMgr);\n\n    auto version = Version::getOpenmwVersion(variables[\"resources\"].as<Files::EscapeHashString>().toStdString());\n\n    int logLevel = mgr.getInt(\"logLevel\", \"General\");\n    if (logLevel < TimedLog::LOG_VERBOSE || logLevel > TimedLog::LOG_FATAL)\n        logLevel = TimedLog::LOG_VERBOSE;\n\n    // Some objects used to redirect cout and cerr\n    // Scope must be here, so this still works inside the catch block for logging exceptions\n    std::streambuf* cout_rdbuf = std::cout.rdbuf ();\n    std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();\n\n    boost::iostreams::stream_buffer<Tee> coutsb;\n    boost::iostreams::stream_buffer<Tee> cerrsb;\n\n    std::ostream oldcout(cout_rdbuf);\n    std::ostream oldcerr(cerr_rdbuf);\n\n    boost::filesystem::ofstream logfile;\n\n    if (!variables[\"no-logs\"].as<bool>())\n    {\n        // Redirect cout and cerr to tes3mp server log\n\n        logfile.open(boost::filesystem::path(\n                cfgMgr.getLogPath() / \"/tes3mp-server-\" += TimedLog::getFilenameTimestamp() += \".log\"));\n\n        coutsb.open(Tee(logfile, oldcout));\n        cerrsb.open(Tee(logfile, oldcerr));\n\n        std::cout.rdbuf(&coutsb);\n        std::cerr.rdbuf(&cerrsb);\n    }\n\n    LOG_INIT(logLevel);\n\n    int players = mgr.getInt(\"maximumPlayers\", \"General\");\n    std::string address = mgr.getString(\"localAddress\", \"General\");\n    int port = mgr.getInt(\"port\", \"General\");\n\n    std::string password = mgr.getString(\"password\", \"General\");\n\n    std::string pluginHome = mgr.getString(\"home\", \"Plugins\");\n    std::string dataDirectory = Utils::convertPath(pluginHome + \"/data\");\n\n    std::vector<std::string> plugins(Utils::split(mgr.getString(\"plugins\", \"Plugins\"), ','));\n\n    std::string versionInfo = Utils::getVersionInfo(\"TES3MP dedicated server\", TES3MP_VERSION, version.mCommitHash, TES3MP_PROTO_VERSION);\n    LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"%s\", versionInfo.c_str());\n    \n    Script::SetModDir(dataDirectory);\n\n#ifdef ENABLE_LUA\n    LangLua::AddPackagePath(Utils::convertPath(pluginHome + \"/scripts/?.lua\" + \";\"\n        + pluginHome + \"/lib/lua/?.lua\" + \";\"));\n#ifdef _WIN32\n    LangLua::AddPackageCPath(Utils::convertPath(pluginHome + \"/lib/?.dll\"));\n#else\n    LangLua::AddPackageCPath(Utils::convertPath(pluginHome + \"/lib/?.so\"));\n#endif\n\n#endif\n\n    int code;\n\n    RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();\n\n    std::stringstream sstr;\n    sstr << TES3MP_VERSION;\n    sstr << TES3MP_PROTO_VERSION;\n    // Remove carriage returns added to version file on Windows\n    version.mCommitHash.erase(std::remove(version.mCommitHash.begin(), version.mCommitHash.end(), '\\r'), version.mCommitHash.end());\n    sstr << version.mCommitHash;\n\n    peer->SetIncomingPassword(sstr.str().c_str(), (int) sstr.str().size());\n\n    if (RakNet::NonNumericHostString(address.c_str()))\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"You cannot use non-numeric addresses for the server.\");\n        return 1;\n    }\n\n    RakNet::SocketDescriptor sd((unsigned short) port, address.c_str());\n\n    try\n    {\n        for (auto plugin : plugins)\n            Script::LoadScript(plugin.c_str(), pluginHome.c_str());\n\n        switch (peer->Startup((unsigned) players, &sd, 1))\n        {\n            case RakNet::CRABNET_STARTED:\n                break;\n            case RakNet::CRABNET_ALREADY_STARTED:\n                throw std::runtime_error(\"Already started\");\n            case RakNet::INVALID_SOCKET_DESCRIPTORS:\n                throw std::runtime_error(\"Incorrect port or address\");\n            case RakNet::INVALID_MAX_CONNECTIONS:\n                throw std::runtime_error(\"Max players cannot be negative or 0\");\n            case RakNet::SOCKET_FAILED_TO_BIND:\n            case RakNet::SOCKET_PORT_ALREADY_IN_USE:\n            case RakNet::PORT_CANNOT_BE_ZERO:\n                throw std::runtime_error(\"Failed to bind port. Make sure a server isn't already running on that port.\");\n            case RakNet::SOCKET_FAILED_TEST_SEND:\n            case RakNet::SOCKET_FAMILY_NOT_SUPPORTED:\n            case RakNet::FAILED_TO_CREATE_NETWORK_THREAD:\n            case RakNet::COULD_NOT_GENERATE_GUID:\n            case RakNet::STARTUP_OTHER_FAILURE:\n                throw std::runtime_error(\"Cannot start server\");\n        }\n\n        peer->SetMaximumIncomingConnections((unsigned short) (players));\n\n        Networking networking(peer);\n        networking.setServerPassword(password);\n\n        if (mgr.getBool(\"enabled\", \"MasterServer\"))\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Sharing server query info to master enabled.\");\n            std::string masterAddr = mgr.getString(\"address\", \"MasterServer\");\n            int masterPort = mgr.getInt(\"port\", \"MasterServer\");\n            int updateRate = mgr.getInt(\"rate\", \"MasterServer\");\n\n            // Is this an attempt to connect to the official master server at the old port? If so,\n            // redirect it to the correct port for the currently used fork of RakNet\n            if (Misc::StringUtils::ciEqual(masterAddr, \"master.tes3mp.com\") && masterPort == 25560)\n            {\n                masterPort = 25561;\n                LOG_APPEND(TimedLog::LOG_INFO, \"- switching to port %i because the correct official master server for this version is on that port\",\n                    masterPort);\n            }\n\n            if (updateRate < 8000)\n            {\n                updateRate = 8000;\n                LOG_APPEND(TimedLog::LOG_INFO, \"- switching to updateRate %i because the one in the server config was too low\", updateRate);\n            }\n\n            networking.InitQuery(masterAddr, (unsigned short) masterPort);\n            networking.getMasterClient()->SetMaxPlayers((unsigned) players);\n            networking.getMasterClient()->SetUpdateRate((unsigned) updateRate);\n            std::string hostname = mgr.getString(\"hostname\", \"General\");\n            networking.getMasterClient()->SetHostname(hostname);\n            networking.getMasterClient()->SetRuleString(\"CommitHash\", version.mCommitHash.substr(0, 10));\n\n            networking.getMasterClient()->Start();\n        }\n\n        networking.postInit();\n\n        code = networking.mainLoop();\n\n        networking.getMasterClient()->Stop();\n    }\n    catch (std::exception &e)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, e.what());\n        Script::Call<Script::CallbackIdentity(\"OnServerScriptCrash\")>(e.what());\n        throw; //fall through\n    }\n\n    RakNet::RakPeerInterface::DestroyInstance(peer);\n\n    if (code == 0)\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Quitting peacefully.\");\n\n    LOG_QUIT();\n\n    if (!variables[\"no-logs\"].as<bool>())\n    {\n        // Restore cout and cerr\n        std::cout.rdbuf(cout_rdbuf);\n        std::cerr.rdbuf(cerr_rdbuf);\n    }\n\n\n    breakpad_close();\n    return code;\n}\n"
  },
  {
    "path": "apps/openmw-mp/processors/ActorProcessor.cpp",
    "content": "#include \"ActorProcessor.hpp\"\n#include \"Networking.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nvoid ActorProcessor::Do(ActorPacket &packet, Player &player, BaseActorList &actorList)\n{\n    packet.Send(true);\n}\n\nbool ActorProcessor::Process(RakNet::Packet &packet, BaseActorList &actorList) noexcept\n{\n    // Clear our BaseActorList before loading new data in it\n    actorList.cell.blank();\n    actorList.baseActors.clear();\n    actorList.guid = packet.guid;\n\n    for (auto &processor : processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            Player *player = Players::getPlayer(packet.guid);\n            ActorPacket *myPacket = Networking::get().getActorPacketController()->GetPacket(packet.data[0]);\n\n            myPacket->setActorList(&actorList);\n            actorList.isValid = true;\n\n            if (!processor.second->avoidReading)\n                myPacket->Read();\n\n            if (actorList.isValid)\n                processor.second->Do(*myPacket, *player, actorList);\n            else\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Received %s that failed integrity check and was ignored!\", processor.second->strPacketID.c_str());\n\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw-mp/processors/ActorProcessor.hpp",
    "content": "#ifndef OPENMW_ACTORPROCESSOR_HPP\n#define OPENMW_ACTORPROCESSOR_HPP\n\n\n#include <components/openmw-mp/Base/BasePacketProcessor.hpp>\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"Script/Script.hpp\"\n#include \"Player.hpp\"\n\nnamespace mwmp\n{\n    class ActorProcessor : public BasePacketProcessor<ActorProcessor>\n    {\n    public:\n\n        virtual void Do(ActorPacket &packet, Player &player, BaseActorList &actorList);\n\n        static bool Process(RakNet::Packet &packet, BaseActorList &actorList) noexcept;\n    };\n}\n\n#endif //OPENMW_ACTORPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/ObjectProcessor.cpp",
    "content": "#include \"ObjectProcessor.hpp\"\n#include \"Networking.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nvoid ObjectProcessor::Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList)\n{\n    packet.Send(true);\n}\n\nbool ObjectProcessor::Process(RakNet::Packet &packet, BaseObjectList &objectList) noexcept\n{\n    // Clear our BaseObjectList before loading new data in it\n    objectList.cell.blank();\n    objectList.baseObjects.clear();\n    objectList.guid = packet.guid;\n\n    for (auto &processor : processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            Player *player = Players::getPlayer(packet.guid);\n            ObjectPacket *myPacket = Networking::get().getObjectPacketController()->GetPacket(packet.data[0]);\n\n            myPacket->setObjectList(&objectList);\n            objectList.isValid = true;\n\n            if (!processor.second->avoidReading)\n                myPacket->Read();\n\n            if (objectList.isValid)\n                processor.second->Do(*myPacket, *player, objectList);\n            else\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Received %s that failed integrity check and was ignored!\", processor.second->strPacketID.c_str());\n            \n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw-mp/processors/ObjectProcessor.hpp",
    "content": "#ifndef OPENMW_OBJECTPROCESSOR_HPP\n#define OPENMW_OBJECTPROCESSOR_HPP\n\n\n#include <components/openmw-mp/Base/BasePacketProcessor.hpp>\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"Script/Script.hpp\"\n#include \"Player.hpp\"\n\nnamespace mwmp\n{\n    class ObjectProcessor : public BasePacketProcessor<ObjectProcessor>\n    {\n    public:\n\n        virtual void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList);\n\n        static bool Process(RakNet::Packet &packet, BaseObjectList &objectList) noexcept;\n    };\n}\n\n#endif //OPENMW_OBJECTPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/PlayerProcessor.cpp",
    "content": "#include \"PlayerProcessor.hpp\"\n#include \"Networking.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nbool PlayerProcessor::Process(RakNet::Packet &packet) noexcept\n{\n    for (auto &processor : processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            Player *player = Players::getPlayer(packet.guid);\n            PlayerPacket *myPacket = Networking::get().getPlayerPacketController()->GetPacket(packet.data[0]);\n            myPacket->setPlayer(player);\n\n            if (!processor.second->avoidReading)\n                myPacket->Read();\n\n            processor.second->Do(*myPacket, *player);\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw-mp/processors/PlayerProcessor.hpp",
    "content": "#ifndef OPENMW_BASEPLAYERPROCESSOR_HPP\n#define OPENMW_BASEPLAYERPROCESSOR_HPP\n\n#include <components/openmw-mp/Base/BasePacketProcessor.hpp>\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"Player.hpp\"\n\nnamespace mwmp\n{\n    class PlayerProcessor : public BasePacketProcessor<PlayerProcessor>\n    {\n    public:\n\n        virtual void Do(PlayerPacket &packet, Player &player) = 0;\n\n        static bool Process(RakNet::Packet &packet) noexcept;\n    };\n}\n\n#endif //OPENMW_BASEPLAYERPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/ProcessorInitializer.cpp",
    "content": "#include \"ProcessorInitializer.hpp\"\n\n#include \"Networking.hpp\"\n#include \"Script/Script.hpp\"\n\n#include \"PlayerProcessor.hpp\"\n#include \"player/ProcessorChatMsg.hpp\"\n#include \"player/ProcessorGUIMessageBox.hpp\"\n#include \"player/ProcessorPlayerCharGen.hpp\"\n#include \"player/ProcessorPlayerAnimFlags.hpp\"\n#include \"player/ProcessorPlayerAnimPlay.hpp\"\n#include \"player/ProcessorPlayerAttack.hpp\"\n#include \"player/ProcessorPlayerAttribute.hpp\"\n#include \"player/ProcessorPlayerBook.hpp\"\n#include \"player/ProcessorPlayerBounty.hpp\"\n#include \"player/ProcessorPlayerCast.hpp\"\n#include \"player/ProcessorPlayerCellChange.hpp\"\n#include \"player/ProcessorPlayerCellState.hpp\"\n#include \"player/ProcessorPlayerCharClass.hpp\"\n#include \"player/ProcessorPlayerCooldowns.hpp\"\n#include \"player/ProcessorPlayerDeath.hpp\"\n#include \"player/ProcessorPlayerDisposition.hpp\"\n#include \"player/ProcessorPlayerEquipment.hpp\"\n#include \"player/ProcessorPlayerFaction.hpp\"\n#include \"player/ProcessorPlayerInventory.hpp\"\n#include \"player/ProcessorPlayerItemUse.hpp\"\n#include \"player/ProcessorPlayerJournal.hpp\"\n#include \"player/ProcessorPlayerPlaceholder.hpp\"\n#include \"player/ProcessorPlayerInput.hpp\"\n#include \"player/ProcessorPlayerLevel.hpp\"\n#include \"player/ProcessorPlayerMiscellaneous.hpp\"\n#include \"player/ProcessorPlayerPosition.hpp\"\n#include \"player/ProcessorPlayerQuickKeys.hpp\"\n#include \"player/ProcessorPlayerReputation.hpp\"\n#include \"player/ProcessorPlayerRest.hpp\"\n#include \"player/ProcessorPlayerResurrect.hpp\"\n#include \"player/ProcessorPlayerShapeshift.hpp\"\n#include \"player/ProcessorPlayerSkill.hpp\"\n#include \"player/ProcessorPlayerSpeech.hpp\"\n#include \"player/ProcessorPlayerSpellbook.hpp\"\n#include \"player/ProcessorPlayerSpellsActive.hpp\"\n#include \"player/ProcessorPlayerStatsDynamic.hpp\"\n#include \"player/ProcessorPlayerTopic.hpp\"\n#include \"ActorProcessor.hpp\"\n#include \"actor/ProcessorActorList.hpp\"\n#include \"actor/ProcessorActorTest.hpp\"\n#include \"actor/ProcessorActorAI.hpp\"\n#include \"actor/ProcessorActorAnimFlags.hpp\"\n#include \"actor/ProcessorActorAnimPlay.hpp\"\n#include \"actor/ProcessorActorAttack.hpp\"\n#include \"actor/ProcessorActorCast.hpp\"\n#include \"actor/ProcessorActorCellChange.hpp\"\n#include \"actor/ProcessorActorDeath.hpp\"\n#include \"actor/ProcessorActorEquipment.hpp\"\n#include \"actor/ProcessorActorPosition.hpp\"\n#include \"actor/ProcessorActorSpeech.hpp\"\n#include \"actor/ProcessorActorSpellsActive.hpp\"\n#include \"actor/ProcessorActorStatsDynamic.hpp\"\n#include \"ObjectProcessor.hpp\"\n#include \"object/ProcessorConsoleCommand.hpp\"\n#include \"object/ProcessorContainer.hpp\"\n#include \"object/ProcessorDoorState.hpp\"\n#include \"object/ProcessorMusicPlay.hpp\"\n#include \"object/ProcessorObjectActivate.hpp\"\n#include \"object/ProcessorObjectAnimPlay.hpp\"\n#include \"object/ProcessorObjectDelete.hpp\"\n#include \"object/ProcessorObjectDialogueChoice.hpp\"\n#include \"object/ProcessorObjectHit.hpp\"\n#include \"object/ProcessorObjectPlace.hpp\"\n#include \"object/ProcessorObjectLock.hpp\"\n#include \"object/ProcessorObjectMiscellaneous.hpp\"\n#include \"object/ProcessorObjectMove.hpp\"\n#include \"object/ProcessorObjectRestock.hpp\"\n#include \"object/ProcessorObjectRotate.hpp\"\n#include \"object/ProcessorObjectScale.hpp\"\n#include \"object/ProcessorObjectSound.hpp\"\n#include \"object/ProcessorObjectSpawn.hpp\"\n#include \"object/ProcessorObjectState.hpp\"\n#include \"object/ProcessorObjectTrap.hpp\"\n#include \"object/ProcessorClientScriptLocal.hpp\"\n#include \"object/ProcessorScriptMemberShort.hpp\"\n#include \"object/ProcessorVideoPlay.hpp\"\n#include \"WorldstateProcessor.hpp\"\n#include \"worldstate/ProcessorClientScriptGlobal.hpp\"\n#include \"worldstate/ProcessorRecordDynamic.hpp\"\n#include \"worldstate/ProcessorWorldKillCount.hpp\"\n#include \"worldstate/ProcessorWorldMap.hpp\"\n#include \"worldstate/ProcessorWorldWeather.hpp\"\n\nusing namespace mwmp;\n\nvoid ProcessorInitializer()\n{\n    PlayerProcessor::AddProcessor(new ProcessorChatMsg());\n    PlayerProcessor::AddProcessor(new ProcessorGUIMessageBox());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCharGen());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAnimFlags());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAnimPlay());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAttack());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerAttribute());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerBook());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerBounty());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCast());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCellChange());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCellState());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCharClass());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerCooldowns());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerDeath());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerDisposition());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerEquipment());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerFaction());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerInventory());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerItemUse());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerJournal());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerPlaceholder());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerInput());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerLevel());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerMiscellaneous());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerPosition());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerQuickKeys());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerReputation());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerRest());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerResurrect());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerShapeshift());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerSkill());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerSpeech());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerSpellbook());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerSpellsActive());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerStatsDynamic());\n    PlayerProcessor::AddProcessor(new ProcessorPlayerTopic());\n\n    ActorProcessor::AddProcessor(new ProcessorActorList());\n    ActorProcessor::AddProcessor(new ProcessorActorAI());\n    ActorProcessor::AddProcessor(new ProcessorActorAnimFlags());\n    ActorProcessor::AddProcessor(new ProcessorActorAnimPlay());\n    ActorProcessor::AddProcessor(new ProcessorActorAttack());\n    ActorProcessor::AddProcessor(new ProcessorActorCast());\n    ActorProcessor::AddProcessor(new ProcessorActorCellChange());\n    ActorProcessor::AddProcessor(new ProcessorActorDeath());\n    ActorProcessor::AddProcessor(new ProcessorActorEquipment());\n    ActorProcessor::AddProcessor(new ProcessorActorPosition());\n    ActorProcessor::AddProcessor(new ProcessorActorSpeech());\n    ActorProcessor::AddProcessor(new ProcessorActorSpellsActive());\n    ActorProcessor::AddProcessor(new ProcessorActorStatsDynamic());\n    ActorProcessor::AddProcessor(new ProcessorActorTest());\n\n    ObjectProcessor::AddProcessor(new ProcessorConsoleCommand());\n    ObjectProcessor::AddProcessor(new ProcessorContainer());\n    ObjectProcessor::AddProcessor(new ProcessorDoorState());\n    ObjectProcessor::AddProcessor(new ProcessorMusicPlay());\n    ObjectProcessor::AddProcessor(new ProcessorObjectActivate());\n    ObjectProcessor::AddProcessor(new ProcessorObjectAnimPlay());\n    ObjectProcessor::AddProcessor(new ProcessorObjectDelete());\n    ObjectProcessor::AddProcessor(new ProcessorObjectDialogueChoice());\n    ObjectProcessor::AddProcessor(new ProcessorObjectHit());\n    ObjectProcessor::AddProcessor(new ProcessorObjectLock());\n    ObjectProcessor::AddProcessor(new ProcessorObjectMiscellaneous());\n    ObjectProcessor::AddProcessor(new ProcessorObjectMove());\n    ObjectProcessor::AddProcessor(new ProcessorObjectPlace());\n    ObjectProcessor::AddProcessor(new ProcessorObjectRestock());\n    ObjectProcessor::AddProcessor(new ProcessorObjectRotate());\n    ObjectProcessor::AddProcessor(new ProcessorObjectScale());\n    ObjectProcessor::AddProcessor(new ProcessorObjectSound());\n    ObjectProcessor::AddProcessor(new ProcessorObjectSpawn());\n    ObjectProcessor::AddProcessor(new ProcessorObjectState());\n    ObjectProcessor::AddProcessor(new ProcessorObjectTrap());\n    ObjectProcessor::AddProcessor(new ProcessorClientScriptLocal());\n    ObjectProcessor::AddProcessor(new ProcessorScriptMemberShort());\n    ObjectProcessor::AddProcessor(new ProcessorVideoPlay());\n\n    WorldstateProcessor::AddProcessor(new ProcessorClientScriptGlobal());\n    WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldKillCount());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldMap());\n    WorldstateProcessor::AddProcessor(new ProcessorWorldWeather());\n}\n"
  },
  {
    "path": "apps/openmw-mp/processors/ProcessorInitializer.hpp",
    "content": "#ifndef OPENMW_INIT_PROCESSORS_HPP\n#define OPENMW_INIT_PROCESSORS_HPP\n\nvoid ProcessorInitializer();\n\n\n#endif //OPENMW_INIT_PROCESSORS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/WorldstateProcessor.cpp",
    "content": "#include \"WorldstateProcessor.hpp\"\n#include \"Networking.hpp\"\n\nusing namespace mwmp;\n\ntemplate<class T>\ntypename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;\n\nvoid WorldstateProcessor::Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate)\n{\n    packet.Send(true);\n}\n\nbool WorldstateProcessor::Process(RakNet::Packet &packet, BaseWorldstate &worldstate) noexcept\n{\n    worldstate.guid = packet.guid;\n\n    for (auto &processor : processors)\n    {\n        if (processor.first == packet.data[0])\n        {\n            Player *player = Players::getPlayer(packet.guid);\n            WorldstatePacket *myPacket = Networking::get().getWorldstatePacketController()->GetPacket(packet.data[0]);\n\n            myPacket->setWorldstate(&worldstate);\n            worldstate.isValid = true;\n\n            if (!processor.second->avoidReading)\n                myPacket->Read();\n\n            if (worldstate.isValid)\n                processor.second->Do(*myPacket, *player, worldstate);\n            else\n                LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Received %s that failed integrity check and was ignored!\", processor.second->strPacketID.c_str());\n            \n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "apps/openmw-mp/processors/WorldstateProcessor.hpp",
    "content": "#ifndef OPENMW_BASEWORLDSTATEPROCESSOR_HPP\n#define OPENMW_BASEWORLDSTATEPROCESSOR_HPP\n\n#include <components/openmw-mp/Base/BasePacketProcessor.hpp>\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"Player.hpp\"\n\nnamespace mwmp\n{\n    class WorldstateProcessor : public BasePacketProcessor<WorldstateProcessor>\n    {\n    public:\n\n        virtual void Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate);\n\n        static bool Process(RakNet::Packet &packet, BaseWorldstate &worldstate) noexcept;\n    };\n}\n\n#endif //OPENMW_BASEWORLDSTATEPROCESSOR_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorAI.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORAI_HPP\n#define OPENMW_PROCESSORACTORAI_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAI : public ActorProcessor\n    {\n    public:\n        ProcessorActorAI()\n        {\n            BPP_INIT(ID_ACTOR_AI)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr)\n            {\n                Script::Call<Script::CallbackIdentity(\"OnActorAI\")>(player.getId(), actorList.cell.getShortDescription().c_str());\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORAI_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorAnimFlags.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORANIMFLAGS_HPP\n#define OPENMW_PROCESSORACTORANIMFLAGS_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAnimFlags : public ActorProcessor\n    {\n    public:\n        ProcessorActorAnimFlags()\n        {\n            BPP_INIT(ID_ACTOR_ANIM_FLAGS)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n                serverCell->sendToLoaded(&packet, &actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORANIMFLAGS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorAnimPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORANIMPLAY_HPP\n#define OPENMW_PROCESSORACTORANIMPLAY_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAnimPlay : public ActorProcessor\n    {\n    public:\n        ProcessorActorAnimPlay()\n        {\n            BPP_INIT(ID_ACTOR_ANIM_PLAY)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n                serverCell->sendToLoaded(&packet, &actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORANIMPLAY_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorAttack.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORATTACK_HPP\n#define OPENMW_PROCESSORACTORATTACK_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorAttack : public ActorProcessor\n    {\n    public:\n        ProcessorActorAttack()\n        {\n            BPP_INIT(ID_ACTOR_ATTACK)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n                serverCell->sendToLoaded(&packet, &actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORATTACK_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorCast.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORCAST_HPP\n#define OPENMW_PROCESSORACTORCAST_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorCast : public ActorProcessor\n    {\n    public:\n        ProcessorActorCast()\n        {\n            BPP_INIT(ID_ACTOR_CAST)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n                serverCell->sendToLoaded(&packet, &actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORCAST_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorCellChange.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORCELLCHANGE_HPP\n#define OPENMW_PROCESSORACTORCELLCHANGE_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorCellChange : public ActorProcessor\n    {\n    public:\n        ProcessorActorCellChange()\n        {\n            BPP_INIT(ID_ACTOR_CELL_CHANGE)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            bool isAccepted = false;\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr)\n            {\n                bool isFollowerCellChange = false;\n\n                // TODO: Move this check on the Lua side\n                for (unsigned int i = 0; i < actorList.count; i++)\n                {\n                    if (actorList.baseActors.at(i).isFollowerCellChange)\n                    {\n                        isFollowerCellChange = true;\n                        break;\n                    }\n                }\n\n                // If the cell is loaded, only accept regular cell changes from a cell's authority, but accept follower\n                // cell changes from other players\n                if (*serverCell->getAuthority() == actorList.guid || isFollowerCellChange)\n                {\n                    serverCell->removeActors(&actorList);\n                    isAccepted = true;\n                }\n            }\n            // If the cell isn't loaded, the packet must be from dialogue or a script, so accept it\n            else\n            {\n                isAccepted = true;\n            }\n\n            if (isAccepted)\n            {\n                Script::Call<Script::CallbackIdentity(\"OnActorCellChange\")>(player.getId(), actorList.cell.getShortDescription().c_str());\n\n                // Send this to everyone\n                packet.Send(true);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORCELLCHANGE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorDeath.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORDEATH_HPP\n#define OPENMW_PROCESSORACTORDEATH_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorDeath : public ActorProcessor\n    {\n    public:\n        ProcessorActorDeath()\n        {\n            BPP_INIT(ID_ACTOR_DEATH)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n            {\n                Script::Call<Script::CallbackIdentity(\"OnActorDeath\")>(player.getId(), actorList.cell.getShortDescription().c_str());\n\n                serverCell->sendToLoaded(&packet, &actorList);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORDEATH_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorEquipment.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTOREQUIPMENT_HPP\n#define OPENMW_PROCESSORACTOREQUIPMENT_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorEquipment : public ActorProcessor\n    {\n    public:\n        ProcessorActorEquipment()\n        {\n            BPP_INIT(ID_ACTOR_EQUIPMENT)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr)\n            {\n                Script::Call<Script::CallbackIdentity(\"OnActorEquipment\")>(player.getId(), actorList.cell.getShortDescription().c_str());\n\n                serverCell->sendToLoaded(&packet, &actorList);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTOREQUIPMENT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorList.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORLIST_HPP\n#define OPENMW_PROCESSORACTORLIST_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorList : public ActorProcessor\n    {\n    public:\n        ProcessorActorList()\n        {\n            BPP_INIT(ID_ACTOR_LIST)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n            \n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr)\n                serverCell->sendToLoaded(&packet, &actorList);\n\n            Script::Call<Script::CallbackIdentity(\"OnActorList\")>(player.getId(), actorList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORLIST_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorPosition.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORPOSITION_HPP\n#define OPENMW_PROCESSORACTORPOSITION_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorPosition : public ActorProcessor\n    {\n    public:\n        ProcessorActorPosition()\n        {\n            BPP_INIT(ID_ACTOR_POSITION)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n            {\n                serverCell->readActorList(packetID, &actorList);\n                serverCell->sendToLoaded(&packet, &actorList);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORPOSITION_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorSpeech.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORSPEECH_HPP\n#define OPENMW_PROCESSORACTORSPEECH_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorSpeech : public ActorProcessor\n    {\n    public:\n        ProcessorActorSpeech()\n        {\n            BPP_INIT(ID_ACTOR_SPEECH)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n                serverCell->sendToLoaded(&packet, &actorList);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORSPEECH_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorSpellsActive.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORSPELLSACTIVE_HPP\n#define OPENMW_PROCESSORACTORSPELLSACTIVE_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorSpellsActive : public ActorProcessor\n    {\n    public:\n        ProcessorActorSpellsActive()\n        {\n            BPP_INIT(ID_ACTOR_SPELLS_ACTIVE)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n            {\n                Script::Call<Script::CallbackIdentity(\"OnActorSpellsActive\")>(player.getId(), actorList.cell.getShortDescription().c_str());\n\n                serverCell->sendToLoaded(&packet, &actorList);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORSPELLSACTIVE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorStatsDynamic.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORSTATSDYNAMIC_HPP\n#define OPENMW_PROCESSORACTORSTATSDYNAMIC_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorStatsDynamic : public ActorProcessor\n    {\n    public:\n        ProcessorActorStatsDynamic()\n        {\n            BPP_INIT(ID_ACTOR_STATS_DYNAMIC)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr && *serverCell->getAuthority() == actorList.guid)\n            {\n                serverCell->readActorList(packetID, &actorList);\n                serverCell->sendToLoaded(&packet, &actorList);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORSTATSDYNAMIC_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/actor/ProcessorActorTest.hpp",
    "content": "#ifndef OPENMW_PROCESSORACTORTEST_HPP\n#define OPENMW_PROCESSORACTORTEST_HPP\n\n#include \"../ActorProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorActorTest : public ActorProcessor\n    {\n    public:\n        ProcessorActorTest()\n        {\n            BPP_INIT(ID_ACTOR_TEST)\n        }\n\n        void Do(ActorPacket &packet, Player &player, BaseActorList &actorList) override\n        {\n            // Send only to players who have the cell loaded\n            Cell *serverCell = CellController::get()->getCell(&actorList.cell);\n\n            if (serverCell != nullptr)\n                serverCell->sendToLoaded(&packet, &actorList);\n\n            Script::Call<Script::CallbackIdentity(\"OnActorTest\")>(player.getId(), actorList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORACTORTEST_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorClientScriptLocal.hpp",
    "content": "#ifndef OPENMW_PROCESSORCLIENTSCRIPTLOCAL_HPP\n#define OPENMW_PROCESSORCLIENTSCRIPTLOCAL_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorClientScriptLocal : public ObjectProcessor\n    {\n    public:\n        ProcessorClientScriptLocal()\n        {\n            BPP_INIT(ID_CLIENT_SCRIPT_LOCAL)\n        }\n\n        void Do(ObjectPacket& packet, Player& player, BaseObjectList& objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnClientScriptLocal\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORCLIENTSCRIPTLOCAL_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorConsoleCommand.hpp",
    "content": "#ifndef OPENMW_PROCESSORCONSOLECOMMAND_HPP\n#define OPENMW_PROCESSORCONSOLECOMMAND_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorConsoleCommand : public ObjectProcessor\n    {\n    public:\n        ProcessorConsoleCommand()\n        {\n            BPP_INIT(ID_CONSOLE_COMMAND)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnConsoleCommand\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORCONSOLECOMMAND_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorContainer.hpp",
    "content": "#ifndef OPENMW_PROCESSORCONTAINER_HPP\n#define OPENMW_PROCESSORCONTAINER_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorContainer : public ObjectProcessor\n    {\n    public:\n        ProcessorContainer()\n        {\n            BPP_INIT(ID_CONTAINER)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n            LOG_APPEND(TimedLog::LOG_INFO, \"- action: %i\", objectList.action);\n\n            // Don't have any hardcoded sync, and instead expect Lua scripts to forward\n            // container packets to ensure their integrity based on what exists in the\n            // server data\n\n            Script::Call<Script::CallbackIdentity(\"OnContainer\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Finished processing ID_CONTAINER\");\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORCONTAINER_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorDoorState.hpp",
    "content": "#ifndef OPENMW_PROCESSORDOORSTATE_HPP\n#define OPENMW_PROCESSORDOORSTATE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorDoorState : public ObjectProcessor\n    {\n    public:\n        ProcessorDoorState()\n        {\n            BPP_INIT(ID_DOOR_STATE)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            packet.Send(true);\n\n            Script::Call<Script::CallbackIdentity(\"OnDoorState\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORDOORSTATE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorMusicPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSORMUSICPLAY_HPP\n#define OPENMW_PROCESSORMUSICPLAY_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorMusicPlay : public ObjectProcessor\n    {\n    public:\n        ProcessorMusicPlay()\n        {\n            BPP_INIT(ID_MUSIC_PLAY)\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORMUSICPLAY_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectActivate.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTACTIVATE_HPP\n#define OPENMW_PROCESSOROBJECTACTIVATE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n#include <apps/openmw-mp/Networking.hpp>\n\nnamespace mwmp\n{\n    class ProcessorObjectActivate : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectActivate()\n        {\n            BPP_INIT(ID_OBJECT_ACTIVATE)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectActivate\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTACTIVATE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectAnimPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTANIMPLAY_HPP\n#define OPENMW_PROCESSOROBJECTANIMPLAY_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectAnimPlay : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectAnimPlay()\n        {\n            BPP_INIT(ID_OBJECT_ANIM_PLAY)\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTANIMPLAY_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectDelete.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTDELETE_HPP\n#define OPENMW_PROCESSOROBJECTDELETE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectDelete : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectDelete()\n        {\n            BPP_INIT(ID_OBJECT_DELETE)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectDelete\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTDELETE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectDialogueChoice.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTDIALOGUECHOICE_HPP\n#define OPENMW_PROCESSOROBJECTDIALOGUECHOICE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n#include <apps/openmw-mp/Networking.hpp>\n\nnamespace mwmp\n{\n    class ProcessorObjectDialogueChoice : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectDialogueChoice()\n        {\n            BPP_INIT(ID_OBJECT_DIALOGUE_CHOICE)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectDialogueChoice\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTDIALOGUECHOICE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectHit.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTHIT_HPP\n#define OPENMW_PROCESSOROBJECTHIT_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectHit : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectHit()\n        {\n            BPP_INIT(ID_OBJECT_HIT)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectHit\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTHIT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectLock.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTLOCK_HPP\n#define OPENMW_PROCESSOROBJECTLOCK_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectLock : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectLock()\n        {\n            BPP_INIT(ID_OBJECT_LOCK)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n            \n            Script::Call<Script::CallbackIdentity(\"OnObjectLock\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTLOCK_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectMiscellaneous.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTMISCELLANEOUS_HPP\n#define OPENMW_PROCESSOROBJECTMISCELLANEOUS_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectMiscellaneous : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectMiscellaneous()\n        {\n            BPP_INIT(ID_OBJECT_MISCELLANEOUS)\n        }\n\n        void Do(ObjectPacket& packet, Player& player, BaseObjectList& objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectMiscellaneous\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTMISCELLANEOUS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectMove.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTMOVE_HPP\n#define OPENMW_PROCESSOROBJECTMOVE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectMove : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectMove()\n        {\n            BPP_INIT(ID_OBJECT_MOVE)\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTMOVE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectPlace.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTPLACE_HPP\n#define OPENMW_PROCESSOROBJECTPLACE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n#include <apps/openmw-mp/Networking.hpp>\n\nnamespace mwmp\n{\n    class ProcessorObjectPlace : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectPlace()\n        {\n            BPP_INIT(ID_OBJECT_PLACE)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            for (unsigned int i = 0; i < objectList.baseObjectCount; i++)\n            {\n                objectList.baseObjects.at(i).mpNum = mwmp::Networking::getPtr()->incrementMpNum();\n            }\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectPlace\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTPLACE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectRestock.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTRESTOCK_HPP\n#define OPENMW_PROCESSOROBJECTRESTOCK_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectRestock : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectRestock()\n        {\n            BPP_INIT(ID_OBJECT_RESTOCK)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectRestock\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTRESTOCK_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectRotate.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTROTATE_HPP\n#define OPENMW_PROCESSOROBJECTROTATE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectRotate : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectRotate()\n        {\n            BPP_INIT(ID_OBJECT_ROTATE)\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTROTATE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectScale.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTSCALE_HPP\n#define OPENMW_PROCESSOROBJECTSCALE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectScale : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectScale()\n        {\n            BPP_INIT(ID_OBJECT_SCALE)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n            \n            Script::Call<Script::CallbackIdentity(\"OnObjectScale\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTSCALE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectSound.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTSOUND_HPP\n#define OPENMW_PROCESSOROBJECTSOUND_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectSound : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectSound()\n        {\n            BPP_INIT(ID_OBJECT_SOUND)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectSound\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTSOUND_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectSpawn.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTSPAWN_HPP\n#define OPENMW_PROCESSOROBJECTSPAWN_HPP\n\n#include \"../ObjectProcessor.hpp\"\n#include <apps/openmw-mp/Networking.hpp>\n\nnamespace mwmp\n{\n    class ProcessorObjectSpawn : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectSpawn()\n        {\n            BPP_INIT(ID_OBJECT_SPAWN)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            for (unsigned int i = 0; i < objectList.baseObjectCount; i++)\n            {\n                objectList.baseObjects.at(i).mpNum = mwmp::Networking::getPtr()->incrementMpNum();\n            }\n\n            Script::Call<Script::CallbackIdentity(\"OnObjectSpawn\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTSPAWN_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectState.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTSTATE_HPP\n#define OPENMW_PROCESSOROBJECTSTATE_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectState : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectState()\n        {\n            BPP_INIT(ID_OBJECT_STATE)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n            \n            Script::Call<Script::CallbackIdentity(\"OnObjectState\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTSTATE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorObjectTrap.hpp",
    "content": "#ifndef OPENMW_PROCESSOROBJECTTRAP_HPP\n#define OPENMW_PROCESSOROBJECTTRAP_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorObjectTrap : public ObjectProcessor\n    {\n    public:\n        ProcessorObjectTrap()\n        {\n            BPP_INIT(ID_OBJECT_TRAP)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n            \n            Script::Call<Script::CallbackIdentity(\"OnObjectTrap\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSOROBJECTTRAP_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorScriptMemberShort.hpp",
    "content": "#ifndef OPENMW_PROCESSORSCRIPTMEMBERSHORT_HPP\n#define OPENMW_PROCESSORSCRIPTMEMBERSHORT_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorScriptMemberShort : public ObjectProcessor\n    {\n    public:\n        ProcessorScriptMemberShort()\n        {\n            BPP_INIT(ID_SCRIPT_MEMBER_SHORT)\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORSCRIPTMEMBERSHORT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/object/ProcessorVideoPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSORVIDEOPLAY_HPP\n#define OPENMW_PROCESSORVIDEOPLAY_HPP\n\n#include \"../ObjectProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorVideoPlay : public ObjectProcessor\n    {\n    public:\n        ProcessorVideoPlay()\n        {\n            BPP_INIT(ID_VIDEO_PLAY)\n        }\n\n        void Do(ObjectPacket &packet, Player &player, BaseObjectList &objectList) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnVideoPlay\")>(player.getId(), objectList.cell.getShortDescription().c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORVIDEOPLAY_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorChatMsg.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCHATMSG_HPP\n#define OPENMW_PROCESSORPLAYERCHATMSG_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorChatMsg : public PlayerProcessor\n    {\n    public:\n        ProcessorChatMsg()\n        {\n            BPP_INIT(ID_CHAT_MESSAGE)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerSendMessage\")>(player.getId(), player.chatMessage.c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERCHATMSG_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorGUIMessageBox.hpp",
    "content": "#ifndef OPENMW_PROCESSORGUIMESSAGEBOX_HPP\n#define OPENMW_PROCESSORGUIMESSAGEBOX_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorGUIMessageBox : public PlayerProcessor\n    {\n    public:\n        ProcessorGUIMessageBox()\n        {\n            BPP_INIT(ID_GUI_MESSAGEBOX)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnGUIAction\")>(player.getId(), (int)player.guiMessageBox.id,\n                                                                  player.guiMessageBox.data.c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORGUIMESSAGEBOX_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerAnimFlags.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERANIMFLAGS_HPP\n#define OPENMW_PROCESSORPLAYERANIMFLAGS_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAnimFlags : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerAnimFlags()\n        {\n            BPP_INIT(ID_PLAYER_ANIM_FLAGS)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            player.sendToLoaded(&packet);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERANIMFLAGS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerAnimPlay.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERANIMPLAY_HPP\n#define OPENMW_PROCESSORPLAYERANIMPLAY_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAnimPlay : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerAnimPlay()\n        {\n            BPP_INIT(ID_PLAYER_ANIM_PLAY)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            player.sendToLoaded(&packet);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERANIMPLAY_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerAttack.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERATTACK_HPP\n#define OPENMW_PROCESSORPLAYERATTACK_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAttack : public PlayerProcessor\n    {\n        PlayerPacketController *playerController;\n    public:\n        ProcessorPlayerAttack()\n        {\n            BPP_INIT(ID_PLAYER_ATTACK)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            if (!player.creatureStats.mDead)\n            {\n                player.sendToLoaded(&packet);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERATTACK_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerAttribute.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERATTRIBUTE_HPP\n#define OPENMW_PROCESSORPLAYERATTRIBUTE_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerAttribute : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerAttribute()\n        {\n            BPP_INIT(ID_PLAYER_ATTRIBUTE)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            if (!player.creatureStats.mDead)\n            {\n                //myPacket->Send(player, true);\n\n                player.sendToLoaded(&packet);\n\n                Script::Call<Script::CallbackIdentity(\"OnPlayerAttribute\")>(player.getId());\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERATTRIBUTE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerBook.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERBOOK_HPP\n#define OPENMW_PROCESSORPLAYERBOOK_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerBook : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerBook()\n        {\n            BPP_INIT(ID_PLAYER_BOOK)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerBook\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERBOOK_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerBounty.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERBOUNTY_HPP\n#define OPENMW_PROCESSORPLAYERBOUNTY_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerBounty : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerBounty()\n        {\n            BPP_INIT(ID_PLAYER_BOUNTY)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            Script::Call<Script::CallbackIdentity(\"OnPlayerBounty\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERBOUNTY_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerCast.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCAST_HPP\n#define OPENMW_PROCESSORPLAYERCAST_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCast : public PlayerProcessor\n    {\n        PlayerPacketController *playerController;\n    public:\n        ProcessorPlayerCast()\n        {\n            BPP_INIT(ID_PLAYER_CAST)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            if (!player.creatureStats.mDead)\n            {\n                player.sendToLoaded(&packet);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERCAST_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerCellChange.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCELLCHANGE_HPP\n#define OPENMW_PROCESSORPLAYERCELLCHANGE_HPP\n\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw-mp/Networking.hpp\"\n#include \"apps/openmw-mp/Script/Script.hpp\"\n#include <components/openmw-mp/Controllers/PlayerPacketController.hpp>\n\nnamespace mwmp\n{\n    class ProcessorPlayerCellChange : public PlayerProcessor\n    {\n        PlayerPacketController *playerController;\n    public:\n        ProcessorPlayerCellChange()\n        {\n            BPP_INIT(ID_PLAYER_CELL_CHANGE)\n            playerController = Networking::get().getPlayerPacketController();\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Moved to %s\", player.cell.getShortDescription().c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerCellChange\")>(player.getId());\n\n            player.exchangeFullInfo = true;\n\n            player.forEachLoaded([this](Player *pl, Player *other) {\n\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Started information exchange with %s\", other->npc.mName.c_str());\n\n                other->exchangeFullInfo = true;\n\n                playerController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(other);\n                playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(other);\n                playerController->GetPacket(ID_PLAYER_POSITION)->setPlayer(other);\n                playerController->GetPacket(ID_PLAYER_SKILL)->setPlayer(other);\n                playerController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(other);\n                playerController->GetPacket(ID_PLAYER_ANIM_FLAGS)->setPlayer(other);\n                playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->setPlayer(other);\n\n                playerController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(pl->guid);\n                playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->Send(pl->guid);\n                playerController->GetPacket(ID_PLAYER_POSITION)->Send(pl->guid);\n                playerController->GetPacket(ID_PLAYER_SKILL)->Send(pl->guid);\n                playerController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(pl->guid);\n                playerController->GetPacket(ID_PLAYER_ANIM_FLAGS)->Send(pl->guid);\n                playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->Send(pl->guid);\n\n                playerController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl);\n                playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl);\n                playerController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl);\n                playerController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl);\n                playerController->GetPacket(ID_PLAYER_ANIM_FLAGS)->setPlayer(pl);\n                playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->setPlayer(pl);\n\n                playerController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(other->guid);\n                playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->Send(other->guid);\n                playerController->GetPacket(ID_PLAYER_SKILL)->Send(other->guid);\n                playerController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(other->guid);\n                playerController->GetPacket(ID_PLAYER_ANIM_FLAGS)->Send(other->guid);\n                playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->Send(other->guid);\n\n                other->exchangeFullInfo = false;\n\n                LOG_APPEND(TimedLog::LOG_INFO, \"- Finished information exchange with %s\", other->npc.mName.c_str());\n            });\n\n            playerController->GetPacket(ID_PLAYER_POSITION)->setPlayer(&player);\n            playerController->GetPacket(ID_PLAYER_POSITION)->Send();\n            packet.setPlayer(&player);\n            packet.Send(true); //send to other clients\n\n            LOG_APPEND(TimedLog::LOG_INFO, \"- Finished processing ID_PLAYER_CELL_CHANGE\", player.cell.getShortDescription().c_str());\n\n            player.exchangeFullInfo = false;\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERCELLCHANGE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerCellState.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCELLSTATE_HPP\n#define OPENMW_PROCESSORPLAYERCELLSTATE_HPP\n\n#include \"../PlayerProcessor.hpp\"\n#include \"apps/openmw-mp/Networking.hpp\"\n#include \"apps/openmw-mp/Script/Script.hpp\"\n#include <components/openmw-mp/Controllers/PlayerPacketController.hpp>\n\nnamespace mwmp\n{\n    class ProcessorPlayerCellState : public PlayerProcessor\n    {\n        PlayerPacketController *playerController;\n    public:\n        ProcessorPlayerCellState()\n        {\n            BPP_INIT(ID_PLAYER_CELL_STATE)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            CellController::get()->update(&player);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERCELLSTATE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerCharClass.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCHARCLASS_HPP\n#define OPENMW_PROCESSORPLAYERCHARCLASS_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCharClass : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCharClass()\n        {\n            BPP_INIT(ID_PLAYER_CHARCLASS)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERCHARCLASS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerCharGen.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCHARGEN_HPP\n#define OPENMW_PROCESSORPLAYERCHARGEN_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCharGen : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCharGen()\n        {\n            BPP_INIT(ID_PLAYER_CHARGEN)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            if (player.charGenState.currentStage == player.charGenState.endStage)\n                Script::Call<Script::CallbackIdentity(\"OnPlayerEndCharGen\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERCHARGEN_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerCooldowns.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERCOOLDOWNS_HPP\n#define OPENMW_PROCESSORPLAYERCOOLDOWNS_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerCooldowns : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerCooldowns()\n        {\n            BPP_INIT(ID_PLAYER_COOLDOWNS)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerCooldowns\")>(player.getId());\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERCOOLDOWNS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerDeath.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERDEATH_HPP\n#define OPENMW_PROCESSORPLAYERDEATH_HPP\n\n#include \"../PlayerProcessor.hpp\"\n#include <chrono>\n\nnamespace mwmp\n{\n    class ProcessorPlayerDeath : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerDeath()\n        {\n            BPP_INIT(ID_PLAYER_DEATH)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            player.creatureStats.mDead = true;\n\n            packet.Send(true);\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerDeath\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERDEATH_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerDisposition.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERDISPOSITION_HPP\n#define OPENMW_PROCESSORPLAYERDISPOSITION_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerDisposition : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerDisposition()\n        {\n            BPP_INIT(ID_PLAYER_DISPOSITION)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            packet.Send(true);\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerDisposition\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERDISPOSITION_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerEquipment.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYEREQUIPMENT_HPP\n#define OPENMW_PROCESSORPLAYEREQUIPMENT_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerEquipment : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerEquipment()\n        {\n            BPP_INIT(ID_PLAYER_EQUIPMENT)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            player.sendToLoaded(&packet);\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerEquipment\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYEREQUIPMENT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerFaction.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERFACTION_HPP\n#define OPENMW_PROCESSORPLAYERFACTION_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerFaction : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerFaction()\n        {\n            BPP_INIT(ID_PLAYER_FACTION)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerFaction\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERFACTION_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerInput.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERINPUT_HPP\n#define OPENMW_PROCESSORPLAYERINPUT_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerInput : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerInput()\n        {\n            BPP_INIT(ID_PLAYER_INPUT)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerInput\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERINPUT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerInventory.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERINVENTORY_HPP\n#define OPENMW_PROCESSORPLAYERINVENTORY_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerInventory : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerInventory()\n        {\n            BPP_INIT(ID_PLAYER_INVENTORY)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerInventory\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERINVENTORY_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerItemUse.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERITEMUSE_HPP\n#define OPENMW_PROCESSORPLAYERITEMUSE_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerItemUse : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerItemUse()\n        {\n            BPP_INIT(ID_PLAYER_ITEM_USE)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerItemUse\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERITEMUSE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerJournal.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERJOURNAL_HPP\n#define OPENMW_PROCESSORPLAYERJOURNAL_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerJournal : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerJournal()\n        {\n            BPP_INIT(ID_PLAYER_JOURNAL)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerJournal\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERJOURNAL_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerLevel.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERLEVEL_HPP\n#define OPENMW_PROCESSORPLAYERLEVEL_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerLevel : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerLevel()\n        {\n            BPP_INIT(ID_PLAYER_LEVEL)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            if (!player.creatureStats.mDead)\n            {\n                Script::Call<Script::CallbackIdentity(\"OnPlayerLevel\")>(player.getId());\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERLEVEL_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerMiscellaneous.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERMISCELLANEOUS_HPP\n#define OPENMW_PROCESSORPLAYERMISCELLANEOUS_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerMiscellaneous : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerMiscellaneous()\n        {\n            BPP_INIT(ID_PLAYER_MISCELLANEOUS)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerMiscellaneous\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERMISCELLANEOUS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerPlaceholder.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLACEHOLDER_HPP\n#define OPENMW_PROCESSORPLACEHOLDER_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerPlaceholder : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerPlaceholder()\n        {\n            BPP_INIT(ID_WORLD_KILL_COUNT)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLACEHOLDER_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerPosition.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERPOSITION_HPP\n#define OPENMW_PROCESSORPLAYERPOSITION_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerPosition : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerPosition()\n        {\n            BPP_INIT(ID_PLAYER_POSITION)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            player.sendToLoaded(&packet);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERPOSITION_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerQuickKeys.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERQUICKKEYS_HPP\n#define OPENMW_PROCESSORPLAYERQUICKKEYS_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerQuickKeys : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerQuickKeys()\n        {\n            BPP_INIT(ID_PLAYER_QUICKKEYS)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerQuickKeys\")>(player.getId());\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERQUICKKEYS_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerReputation.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERREPUTATION_HPP\n#define OPENMW_PROCESSORPLAYERREPUTATION_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerReputation : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerReputation()\n        {\n            BPP_INIT(ID_PLAYER_REPUTATION)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            Script::Call<Script::CallbackIdentity(\"OnPlayerReputation\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERREPUTATION_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerRest.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERREST_HPP\n#define OPENMW_PROCESSORPLAYERREST_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerRest : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerRest()\n        {\n            BPP_INIT(ID_PLAYER_REST)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerRest\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERREST_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerResurrect.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERRESURRECT_HPP\n#define OPENMW_PROCESSORPLAYERRESURRECT_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerResurrect : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerResurrect()\n        {\n            BPP_INIT(ID_PLAYER_RESURRECT)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_VERBOSE, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            player.creatureStats.mDead = false;\n\n            packet.Send(true);\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerResurrect\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERRESURRECT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerShapeshift.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSHAPESHIFT_HPP\n#define OPENMW_PROCESSORPLAYERSHAPESHIFT_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerShapeshift : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerShapeshift()\n        {\n            BPP_INIT(ID_PLAYER_SHAPESHIFT)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, \"Received %s from %s\", strPacketID.c_str(), player.npc.mName.c_str());\n\n            packet.Send(true);\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerShapeshift\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERSHAPESHIFT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerSkill.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSKILL_HPP\n#define OPENMW_PROCESSORPLAYERSKILL_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerSkill : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerSkill()\n        {\n            BPP_INIT(ID_PLAYER_SKILL)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            if (!player.creatureStats.mDead)\n            {\n                //myPacket->Send(player, true);\n                player.sendToLoaded(&packet);\n\n                Script::Call<Script::CallbackIdentity(\"OnPlayerSkill\")>(player.getId());\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERSKILL_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerSpeech.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSPEECH_HPP\n#define OPENMW_PROCESSORPLAYERSPEECH_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerSpeech : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerSpeech()\n        {\n            BPP_INIT(ID_PLAYER_SPEECH)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            player.sendToLoaded(&packet);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERSPEECH_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerSpellbook.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSPELLBOOK_HPP\n#define OPENMW_PROCESSORPLAYERSPELLBOOK_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerSpellbook : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerSpellbook()\n        {\n            BPP_INIT(ID_PLAYER_SPELLBOOK)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerSpellbook\")>(player.getId());\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERSPELLBOOK_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerSpellsActive.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSPELLSACTIVE_HPP\n#define OPENMW_PROCESSORPLAYERSPELLSACTIVE_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerSpellsActive : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerSpellsActive()\n        {\n            BPP_INIT(ID_PLAYER_SPELLS_ACTIVE)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerSpellsActive\")>(player.getId());\n        }\n    };\n}\n\n\n#endif //OPENMW_PROCESSORPLAYERSPELLSACTIVE_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerStatsDynamic.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERSTATS_DYNAMIC_HPP\n#define OPENMW_PROCESSORPLAYERSTATS_DYNAMIC_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerStatsDynamic : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerStatsDynamic()\n        {\n            BPP_INIT(ID_PLAYER_STATS_DYNAMIC)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            player.sendToLoaded(&packet);\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERSTATS_DYNAMIC_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/player/ProcessorPlayerTopic.hpp",
    "content": "#ifndef OPENMW_PROCESSORPLAYERTOPIC_HPP\n#define OPENMW_PROCESSORPLAYERTOPIC_HPP\n\n#include \"../PlayerProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorPlayerTopic : public PlayerProcessor\n    {\n    public:\n        ProcessorPlayerTopic()\n        {\n            BPP_INIT(ID_PLAYER_TOPIC)\n        }\n\n        void Do(PlayerPacket &packet, Player &player) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnPlayerTopic\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORPLAYERTOPIC_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/worldstate/ProcessorClientScriptGlobal.hpp",
    "content": "#ifndef OPENMW_PROCESSORCLIENTSCRIPTGLOBAL_HPP\n#define OPENMW_PROCESSORCLIENTSCRIPTGLOBAL_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorClientScriptGlobal : public WorldstateProcessor\n    {\n    public:\n        ProcessorClientScriptGlobal()\n        {\n            BPP_INIT(ID_CLIENT_SCRIPT_GLOBAL)\n        }\n\n        void Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnClientScriptGlobal\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORCLIENTSCRIPTGLOBAL_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/worldstate/ProcessorRecordDynamic.hpp",
    "content": "#ifndef OPENMW_PROCESSORRECORDDYNAMIC_HPP\n#define OPENMW_PROCESSORRECORDDYNAMIC_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorRecordDynamic : public WorldstateProcessor\n    {\n    public:\n        ProcessorRecordDynamic()\n        {\n            BPP_INIT(ID_RECORD_DYNAMIC)\n        }\n\n        void Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnRecordDynamic\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORRECORDDYNAMIC_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/worldstate/ProcessorWorldKillCount.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDKILLCOUNT_HPP\n#define OPENMW_PROCESSORWORLDKILLCOUNT_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldKillCount : public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldKillCount()\n        {\n            BPP_INIT(ID_WORLD_KILL_COUNT)\n        }\n\n        void Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnWorldKillCount\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORWORLDKILLCOUNT_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/worldstate/ProcessorWorldMap.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDMAP_HPP\n#define OPENMW_PROCESSORWORLDMAP_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldMap : public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldMap()\n        {\n            BPP_INIT(ID_WORLD_MAP)\n        }\n\n        void Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnWorldMap\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORWORLDMAP_HPP\n"
  },
  {
    "path": "apps/openmw-mp/processors/worldstate/ProcessorWorldWeather.hpp",
    "content": "#ifndef OPENMW_PROCESSORWORLDWEATHER_HPP\n#define OPENMW_PROCESSORWORLDWEATHER_HPP\n\n#include \"../WorldstateProcessor.hpp\"\n\nnamespace mwmp\n{\n    class ProcessorWorldWeather : public WorldstateProcessor\n    {\n    public:\n        ProcessorWorldWeather()\n        {\n            BPP_INIT(ID_WORLD_WEATHER)\n        }\n\n        void Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate) override\n        {\n            DEBUG_PRINTF(strPacketID.c_str());\n\n            Script::Call<Script::CallbackIdentity(\"OnWorldWeather\")>(player.getId());\n        }\n    };\n}\n\n#endif //OPENMW_PROCESSORWORLDWEATHER_HPP\n"
  },
  {
    "path": "apps/openmw_test_suite/CMakeLists.txt",
    "content": "find_package(GTest 1.10 REQUIRED)\nfind_package(GMock 1.10 REQUIRED)\n\nif (GTEST_FOUND AND GMOCK_FOUND)\n    include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})\n    include_directories(SYSTEM ${GMOCK_INCLUDE_DIRS})\n\n    file(GLOB UNITTEST_SRC_FILES\n        ../openmw/mwworld/store.cpp\n        ../openmw/mwworld/esmstore.cpp\n        mwworld/test_store.cpp\n\n        mwdialogue/test_keywordsearch.cpp\n\n        esm/test_fixed_string.cpp\n        esm/variant.cpp\n\n        misc/test_stringops.cpp\n        misc/test_endianness.cpp\n\n        nifloader/testbulletnifloader.cpp\n\n        detournavigator/navigator.cpp\n        detournavigator/settingsutils.cpp\n        detournavigator/recastmeshbuilder.cpp\n        detournavigator/gettilespositions.cpp\n        detournavigator/recastmeshobject.cpp\n        detournavigator/navmeshtilescache.cpp\n        detournavigator/tilecachedrecastmeshmanager.cpp\n\n        settings/parser.cpp\n\n        shader/parsedefines.cpp\n        shader/parsefors.cpp\n        shader/shadermanager.cpp\n    )\n\n    source_group(apps\\\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})\n\n    openmw_add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES})\n\n    target_link_libraries(openmw_test_suite ${GMOCK_LIBRARIES} components)\n    # Fix for not visible pthreads functions for linker with glibc 2.15\n    if (UNIX AND NOT APPLE)\n        target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT})\n    endif()\n\n    if (BUILD_WITH_CODE_COVERAGE)\n        add_definitions(--coverage)\n        target_link_libraries(openmw_test_suite gcov)\n    endif()\n\n    if (MSVC)\n        if (CMAKE_CL_64)\n            # Debug version of openmw_unit_tests needs increased number of sections beyond 2^16\n            # just like openmw and openmw-cs\n            set (CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} /bigobj\")\n        endif (CMAKE_CL_64)\n    endif (MSVC)\n\nendif()\n"
  },
  {
    "path": "apps/openmw_test_suite/detournavigator/gettilespositions.cpp",
    "content": "#include <components/detournavigator/gettilespositions.hpp>\n#include <components/detournavigator/debug.hpp>\n\n#include <gtest/gtest.h>\n#include <gmock/gmock.h>\n\nnamespace\n{\n    using namespace testing;\n    using namespace DetourNavigator;\n\n    struct CollectTilesPositions\n    {\n        std::vector<TilePosition>& mTilesPositions;\n\n        void operator ()(const TilePosition& value)\n        {\n            mTilesPositions.push_back(value);\n        }\n    };\n\n    struct DetourNavigatorGetTilesPositionsTest : Test\n    {\n        Settings mSettings;\n        std::vector<TilePosition> mTilesPositions;\n        CollectTilesPositions mCollect {mTilesPositions};\n\n        DetourNavigatorGetTilesPositionsTest()\n        {\n            mSettings.mBorderSize = 0;\n            mSettings.mCellSize = 0.5;\n            mSettings.mRecastScaleFactor = 1;\n            mSettings.mTileSize = 64;\n        }\n    };\n\n    TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_in_single_tile_should_return_one_tile)\n    {\n        getTilesPositions(osg::Vec3f(2, 2, 0), osg::Vec3f(31, 31, 1), mSettings, mCollect);\n\n        EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));\n    }\n\n    TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_x_bounds_in_two_tiles_should_return_two_tiles)\n    {\n        getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 31, 1), mSettings, mCollect);\n\n        EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(1, 0)));\n    }\n\n    TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_y_bounds_in_two_tiles_should_return_two_tiles)\n    {\n        getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 32, 1), mSettings, mCollect);\n\n        EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(0, 1)));\n    }\n\n    TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_works_only_for_x_and_y_coordinates)\n    {\n        getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 31, 32), mSettings, mCollect);\n\n        EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));\n    }\n\n    TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_should_work_with_negative_coordinates)\n    {\n        getTilesPositions(osg::Vec3f(-31, -31, 0), osg::Vec3f(31, 31, 1), mSettings, mCollect);\n\n        EXPECT_THAT(mTilesPositions, ElementsAre(\n            TilePosition(-1, -1),\n            TilePosition(-1, 0),\n            TilePosition(0, -1),\n            TilePosition(0, 0)\n        ));\n    }\n\n    TEST_F(DetourNavigatorGetTilesPositionsTest, border_size_should_extend_tile_bounds)\n    {\n        mSettings.mBorderSize = 1;\n\n        getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(31.5, 31.5, 1), mSettings, mCollect);\n\n        EXPECT_THAT(mTilesPositions, ElementsAre(\n            TilePosition(-1, -1),\n            TilePosition(-1, 0),\n            TilePosition(-1, 1),\n            TilePosition(0, -1),\n            TilePosition(0, 0),\n            TilePosition(0, 1),\n            TilePosition(1, -1),\n            TilePosition(1, 0),\n            TilePosition(1, 1)\n        ));\n    }\n\n    TEST_F(DetourNavigatorGetTilesPositionsTest, should_apply_recast_scale_factor)\n    {\n        mSettings.mRecastScaleFactor = 0.5;\n\n        getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 32, 1), mSettings, mCollect);\n\n        EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/detournavigator/navigator.cpp",
    "content": "#include \"operators.hpp\"\n\n#include <components/detournavigator/navigatorimpl.hpp>\n#include <components/detournavigator/exceptions.hpp>\n#include <components/misc/rng.hpp>\n#include <components/loadinglistener/loadinglistener.hpp>\n#include <components/resource/bulletshape.hpp>\n\n#include <osg/ref_ptr>\n\n#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n\n#include <gtest/gtest.h>\n#include <gmock/gmock.h>\n\n#include <array>\n#include <deque>\n#include <memory>\n\nMATCHER_P3(Vec3fEq, x, y, z, \"\")\n{\n    return std::abs(arg.x() - x) < 1e-4 && std::abs(arg.y() - y) < 1e-4 && std::abs(arg.z() - z) < 1e-4;\n}\n\nnamespace\n{\n    using namespace testing;\n    using namespace DetourNavigator;\n\n    struct DetourNavigatorNavigatorTest : Test\n    {\n        Settings mSettings;\n        std::unique_ptr<Navigator> mNavigator;\n        osg::Vec3f mPlayerPosition;\n        osg::Vec3f mAgentHalfExtents;\n        osg::Vec3f mStart;\n        osg::Vec3f mEnd;\n        std::deque<osg::Vec3f> mPath;\n        std::back_insert_iterator<std::deque<osg::Vec3f>> mOut;\n        float mStepSize;\n        AreaCosts mAreaCosts;\n        Loading::Listener mListener;\n\n        DetourNavigatorNavigatorTest()\n            : mPlayerPosition(0, 0, 0)\n            , mAgentHalfExtents(29, 29, 66)\n            , mStart(-204, 204, 1)\n            , mEnd(204, -204, 1)\n            , mOut(mPath)\n            , mStepSize(28.333332061767578125f)\n        {\n            mSettings.mEnableWriteRecastMeshToFile = false;\n            mSettings.mEnableWriteNavMeshToFile = false;\n            mSettings.mEnableRecastMeshFileNameRevision = false;\n            mSettings.mEnableNavMeshFileNameRevision = false;\n            mSettings.mBorderSize = 16;\n            mSettings.mCellHeight = 0.2f;\n            mSettings.mCellSize = 0.2f;\n            mSettings.mDetailSampleDist = 6;\n            mSettings.mDetailSampleMaxError = 1;\n            mSettings.mMaxClimb = 34;\n            mSettings.mMaxSimplificationError = 1.3f;\n            mSettings.mMaxSlope = 49;\n            mSettings.mRecastScaleFactor = 0.017647058823529415f;\n            mSettings.mSwimHeightScale = 0.89999997615814208984375f;\n            mSettings.mMaxEdgeLen = 12;\n            mSettings.mMaxNavMeshQueryNodes = 2048;\n            mSettings.mMaxVertsPerPoly = 6;\n            mSettings.mRegionMergeSize = 20;\n            mSettings.mRegionMinSize = 8;\n            mSettings.mTileSize = 64;\n            mSettings.mWaitUntilMinDistanceToPlayer = std::numeric_limits<int>::max();\n            mSettings.mAsyncNavMeshUpdaterThreads = 1;\n            mSettings.mMaxNavMeshTilesCacheSize = 1024 * 1024;\n            mSettings.mMaxPolygonPathSize = 1024;\n            mSettings.mMaxSmoothPathSize = 1024;\n            mSettings.mMaxPolys = 4096;\n            mSettings.mMaxTilesNumber = 512;\n            mSettings.mMinUpdateInterval = std::chrono::milliseconds(50);\n            mNavigator.reset(new NavigatorImpl(mSettings));\n        }\n    };\n\n    template <std::size_t size>\n    std::unique_ptr<btHeightfieldTerrainShape> makeSquareHeightfieldTerrainShape(const std::array<btScalar, size>& values,\n        btScalar heightScale = 1, int upAxis = 2, PHY_ScalarType heightDataType = PHY_FLOAT, bool flipQuadEdges = false)\n    {\n        const int width = static_cast<int>(std::sqrt(size));\n        const btScalar min = *std::min_element(values.begin(), values.end());\n        const btScalar max = *std::max_element(values.begin(), values.end());\n        const btScalar greater = std::max(std::abs(min), std::abs(max));\n        return std::make_unique<btHeightfieldTerrainShape>(width, width, values.data(), heightScale, -greater, greater,\n                                                           upAxis, heightDataType, flipQuadEdges);\n    }\n\n    template <class T>\n    osg::ref_ptr<const Resource::BulletShapeInstance> makeBulletShapeInstance(std::unique_ptr<T>&& shape)\n    {\n        osg::ref_ptr<Resource::BulletShape> bulletShape(new Resource::BulletShape);\n        bulletShape->mCollisionShape = std::move(shape).release();\n        return new Resource::BulletShapeInstance(bulletShape);\n    }\n\n    template <class T>\n    class CollisionShapeInstance\n    {\n    public:\n        CollisionShapeInstance(std::unique_ptr<T>&& shape) : mInstance(makeBulletShapeInstance(std::move(shape))) {}\n\n        T& shape() { return static_cast<T&>(*mInstance->mCollisionShape); }\n        const osg::ref_ptr<const Resource::BulletShapeInstance>& instance() const { return mInstance; }\n\n    private:\n        osg::ref_ptr<const Resource::BulletShapeInstance> mInstance;\n    };\n\n    TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty)\n    {\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut),\n                  Status::NavMeshNotFound);\n        EXPECT_EQ(mPath, std::deque<osg::Vec3f>());\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception)\n    {\n        mNavigator->addAgent(mAgentHalfExtents);\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut),\n                  Status::StartPolygonNotFound);\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent)\n    {\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->removeAgent(mAgentHalfExtents);\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut),\n                  Status::StartPolygonNotFound);\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path)\n    {\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent);\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204.0000152587890625, 204, 1.99998295307159423828125),\n            Vec3fEq(-183.96533203125, 183.9653167724609375, 1.99998819828033447265625),\n            Vec3fEq(-163.930633544921875, 163.9306182861328125, 1.99999344348907470703125),\n            Vec3fEq(-143.8959503173828125, 143.89593505859375, -2.720611572265625),\n            Vec3fEq(-123.86126708984375, 123.86124420166015625, -13.1089687347412109375),\n            Vec3fEq(-103.82657623291015625, 103.8265533447265625, -23.497333526611328125),\n            Vec3fEq(-83.7918853759765625, 83.7918548583984375, -33.885692596435546875),\n            Vec3fEq(-63.757190704345703125, 63.757171630859375, -44.274051666259765625),\n            Vec3fEq(-43.722503662109375, 43.72248077392578125, -54.66241455078125),\n            Vec3fEq(-23.687808990478515625, 23.6877918243408203125, -65.05077362060546875),\n            Vec3fEq(-3.6531188488006591796875, 3.6531002521514892578125, -75.43914031982421875),\n            Vec3fEq(16.3815746307373046875, -16.381591796875, -69.74927520751953125),\n            Vec3fEq(36.416263580322265625, -36.416286468505859375, -60.4739532470703125),\n            Vec3fEq(56.450958251953125, -56.450977325439453125, -51.1986236572265625),\n            Vec3fEq(76.48564910888671875, -76.4856719970703125, -41.92330169677734375),\n            Vec3fEq(96.5203399658203125, -96.52036285400390625, -31.46941375732421875),\n            Vec3fEq(116.55503082275390625, -116.5550537109375, -19.597003936767578125),\n            Vec3fEq(136.5897216796875, -136.5897369384765625, -7.724592685699462890625),\n            Vec3fEq(156.624420166015625, -156.624420166015625, 1.99999535083770751953125),\n            Vec3fEq(176.6591033935546875, -176.65911865234375, 1.99999010562896728515625),\n            Vec3fEq(196.69378662109375, -196.6938018798828125, 1.99998486042022705078125),\n            Vec3fEq(204, -204.0000152587890625, 1.99998295307159423828125)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, add_object_should_change_navmesh)\n    {\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto heightfieldShapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& heightfieldShape = *heightfieldShapePtr;\n        heightfieldShape.setLocalScaling(btVector3(128, 128, 1));\n\n        CollisionShapeInstance compound(std::make_unique<btCompoundShape>());\n        compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&heightfieldShape), nullptr, heightfieldShape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204, 204, 1.99998295307159423828125),\n            Vec3fEq(-183.965301513671875, 183.965301513671875, 1.99998819828033447265625),\n            Vec3fEq(-163.9306182861328125, 163.9306182861328125, 1.99999344348907470703125),\n            Vec3fEq(-143.89593505859375, 143.89593505859375, -2.7206256389617919921875),\n            Vec3fEq(-123.86124420166015625, 123.86124420166015625, -13.1089839935302734375),\n            Vec3fEq(-103.8265533447265625, 103.8265533447265625, -23.4973468780517578125),\n            Vec3fEq(-83.7918548583984375, 83.7918548583984375, -33.885707855224609375),\n            Vec3fEq(-63.75716400146484375, 63.75716400146484375, -44.27407073974609375),\n            Vec3fEq(-43.72247314453125, 43.72247314453125, -54.662433624267578125),\n            Vec3fEq(-23.6877803802490234375, 23.6877803802490234375, -65.0507965087890625),\n            Vec3fEq(-3.653090000152587890625, 3.653090000152587890625, -75.43915557861328125),\n            Vec3fEq(16.3816013336181640625, -16.3816013336181640625, -69.749267578125),\n            Vec3fEq(36.416290283203125, -36.416290283203125, -60.4739532470703125),\n            Vec3fEq(56.450984954833984375, -56.450984954833984375, -51.1986236572265625),\n            Vec3fEq(76.4856719970703125, -76.4856719970703125, -41.92330169677734375),\n            Vec3fEq(96.52036285400390625, -96.52036285400390625, -31.46941375732421875),\n            Vec3fEq(116.5550537109375, -116.5550537109375, -19.597003936767578125),\n            Vec3fEq(136.5897369384765625, -136.5897369384765625, -7.724592685699462890625),\n            Vec3fEq(156.6244354248046875, -156.6244354248046875, 1.99999535083770751953125),\n            Vec3fEq(176.6591339111328125, -176.6591339111328125, 1.99999010562896728515625),\n            Vec3fEq(196.693817138671875, -196.693817138671875, 1.99998486042022705078125),\n            Vec3fEq(204, -204, 1.99998295307159423828125)\n        )) << mPath;\n\n        mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance()), btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        mPath.clear();\n        mOut = std::back_inserter(mPath);\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204, 204, 1.99998295307159423828125),\n            Vec3fEq(-189.9427337646484375, 179.3997802734375, -3.622931003570556640625),\n            Vec3fEq(-175.8854522705078125, 154.7995452880859375, -9.24583911895751953125),\n            Vec3fEq(-161.82818603515625, 130.1993255615234375, -14.86874866485595703125),\n            Vec3fEq(-147.770904541015625, 105.5991058349609375, -20.4916591644287109375),\n            Vec3fEq(-133.7136383056640625, 80.99887847900390625, -26.1145648956298828125),\n            Vec3fEq(-119.65636444091796875, 56.39865875244140625, -31.7374725341796875),\n            Vec3fEq(-105.59909820556640625, 31.798435211181640625, -26.133396148681640625),\n            Vec3fEq(-91.54183197021484375, 7.1982135772705078125, -31.5624217987060546875),\n            Vec3fEq(-77.48455810546875, -17.402008056640625, -26.98972320556640625),\n            Vec3fEq(-63.427295684814453125, -42.00223541259765625, -19.9045581817626953125),\n            Vec3fEq(-42.193531036376953125, -60.761363983154296875, -20.4544773101806640625),\n            Vec3fEq(-20.9597682952880859375, -79.5204925537109375, -23.599918365478515625),\n            Vec3fEq(3.8312885761260986328125, -93.2384033203125, -30.7141361236572265625),\n            Vec3fEq(28.6223468780517578125, -106.95632171630859375, -24.8243885040283203125),\n            Vec3fEq(53.413402557373046875, -120.6742401123046875, -31.3303241729736328125),\n            Vec3fEq(78.20446014404296875, -134.39215087890625, -25.8431549072265625),\n            Vec3fEq(102.99552154541015625, -148.110076904296875, -20.3559894561767578125),\n            Vec3fEq(127.7865753173828125, -161.827972412109375, -14.868824005126953125),\n            Vec3fEq(152.57763671875, -175.5458984375, -9.3816623687744140625),\n            Vec3fEq(177.3686981201171875, -189.2638092041015625, -3.894496917724609375),\n            Vec3fEq(202.1597442626953125, -202.9817047119140625, 1.59266507625579833984375),\n            Vec3fEq(204, -204, 1.99998295307159423828125)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, update_changed_object_should_change_navmesh)\n    {\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto heightfieldShapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& heightfieldShape = *heightfieldShapePtr;\n        heightfieldShape.setLocalScaling(btVector3(128, 128, 1));\n\n        CollisionShapeInstance compound(std::make_unique<btCompoundShape>());\n        compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&heightfieldShape), nullptr, heightfieldShape, btTransform::getIdentity());\n        mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance()), btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204, 204, 1.99998295307159423828125),\n            Vec3fEq(-189.9427337646484375, 179.3997802734375, -3.622931003570556640625),\n            Vec3fEq(-175.8854522705078125, 154.7995452880859375, -9.24583911895751953125),\n            Vec3fEq(-161.82818603515625, 130.1993255615234375, -14.86874866485595703125),\n            Vec3fEq(-147.770904541015625, 105.5991058349609375, -20.4916591644287109375),\n            Vec3fEq(-133.7136383056640625, 80.99887847900390625, -26.1145648956298828125),\n            Vec3fEq(-119.65636444091796875, 56.39865875244140625, -31.7374725341796875),\n            Vec3fEq(-105.59909820556640625, 31.798435211181640625, -26.133396148681640625),\n            Vec3fEq(-91.54183197021484375, 7.1982135772705078125, -31.5624217987060546875),\n            Vec3fEq(-77.48455810546875, -17.402008056640625, -26.98972320556640625),\n            Vec3fEq(-63.427295684814453125, -42.00223541259765625, -19.9045581817626953125),\n            Vec3fEq(-42.193531036376953125, -60.761363983154296875, -20.4544773101806640625),\n            Vec3fEq(-20.9597682952880859375, -79.5204925537109375, -23.599918365478515625),\n            Vec3fEq(3.8312885761260986328125, -93.2384033203125, -30.7141361236572265625),\n            Vec3fEq(28.6223468780517578125, -106.95632171630859375, -24.8243885040283203125),\n            Vec3fEq(53.413402557373046875, -120.6742401123046875, -31.3303241729736328125),\n            Vec3fEq(78.20446014404296875, -134.39215087890625, -25.8431549072265625),\n            Vec3fEq(102.99552154541015625, -148.110076904296875, -20.3559894561767578125),\n            Vec3fEq(127.7865753173828125, -161.827972412109375, -14.868824005126953125),\n            Vec3fEq(152.57763671875, -175.5458984375, -9.3816623687744140625),\n            Vec3fEq(177.3686981201171875, -189.2638092041015625, -3.894496917724609375),\n            Vec3fEq(202.1597442626953125, -202.9817047119140625, 1.59266507625579833984375),\n            Vec3fEq(204, -204, 1.99998295307159423828125)\n        )) << mPath;\n\n        compound.shape().updateChildTransform(0, btTransform(btMatrix3x3::getIdentity(), btVector3(1000, 0, 0)));\n\n        mNavigator->updateObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance()), btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        mPath.clear();\n        mOut = std::back_inserter(mPath);\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204, 204, 1.99998295307159423828125),\n            Vec3fEq(-183.965301513671875, 183.965301513671875, 1.99998819828033447265625),\n            Vec3fEq(-163.9306182861328125, 163.9306182861328125, 1.99999344348907470703125),\n            Vec3fEq(-143.89593505859375, 143.89593505859375, -2.7206256389617919921875),\n            Vec3fEq(-123.86124420166015625, 123.86124420166015625, -13.1089839935302734375),\n            Vec3fEq(-103.8265533447265625, 103.8265533447265625, -23.4973468780517578125),\n            Vec3fEq(-83.7918548583984375, 83.7918548583984375, -33.885707855224609375),\n            Vec3fEq(-63.75716400146484375, 63.75716400146484375, -44.27407073974609375),\n            Vec3fEq(-43.72247314453125, 43.72247314453125, -54.662433624267578125),\n            Vec3fEq(-23.6877803802490234375, 23.6877803802490234375, -65.0507965087890625),\n            Vec3fEq(-3.653090000152587890625, 3.653090000152587890625, -75.43915557861328125),\n            Vec3fEq(16.3816013336181640625, -16.3816013336181640625, -69.749267578125),\n            Vec3fEq(36.416290283203125, -36.416290283203125, -60.4739532470703125),\n            Vec3fEq(56.450984954833984375, -56.450984954833984375, -51.1986236572265625),\n            Vec3fEq(76.4856719970703125, -76.4856719970703125, -41.92330169677734375),\n            Vec3fEq(96.52036285400390625, -96.52036285400390625, -31.46941375732421875),\n            Vec3fEq(116.5550537109375, -116.5550537109375, -19.597003936767578125),\n            Vec3fEq(136.5897369384765625, -136.5897369384765625, -7.724592685699462890625),\n            Vec3fEq(156.6244354248046875, -156.6244354248046875, 1.99999535083770751953125),\n            Vec3fEq(176.6591339111328125, -176.6591339111328125, 1.99999010562896728515625),\n            Vec3fEq(196.693817138671875, -196.693817138671875, 1.99998486042022705078125),\n            Vec3fEq(204, -204, 1.99998295307159423828125)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, for_overlapping_heightfields_should_use_higher)\n    {\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        const std::array<btScalar, 5 * 5> heightfieldData2 {{\n            -25, -25, -25, -25, -25,\n            -25, -25, -25, -25, -25,\n            -25, -25, -25, -25, -25,\n            -25, -25, -25, -25, -25,\n            -25, -25, -25, -25, -25,\n        }};\n        const auto shapePtr2 = makeSquareHeightfieldTerrainShape(heightfieldData2);\n        btHeightfieldTerrainShape& shape2 = *shapePtr2;\n        shape2.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->addObject(ObjectId(&shape2), nullptr, shape2, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204, 204, 1.999981403350830078125),\n            Vec3fEq(-183.965301513671875, 183.965301513671875, -0.428465187549591064453125),\n            Vec3fEq(-163.9306182861328125, 163.9306182861328125, -2.8569104671478271484375),\n            Vec3fEq(-143.89593505859375, 143.89593505859375, -5.28535556793212890625),\n            Vec3fEq(-123.86124420166015625, 123.86124420166015625, -7.7138004302978515625),\n            Vec3fEq(-103.8265533447265625, 103.8265533447265625, -10.142246246337890625),\n            Vec3fEq(-83.7918548583984375, 83.7918548583984375, -12.3704509735107421875),\n            Vec3fEq(-63.75716400146484375, 63.75716400146484375, -14.354084014892578125),\n            Vec3fEq(-43.72247314453125, 43.72247314453125, -16.3377170562744140625),\n            Vec3fEq(-23.6877803802490234375, 23.6877803802490234375, -18.32135009765625),\n            Vec3fEq(-3.653090000152587890625, 3.653090000152587890625, -20.3049831390380859375),\n            Vec3fEq(16.3816013336181640625, -16.3816013336181640625, -19.044734954833984375),\n            Vec3fEq(36.416290283203125, -36.416290283203125, -17.061100006103515625),\n            Vec3fEq(56.450984954833984375, -56.450984954833984375, -15.0774688720703125),\n            Vec3fEq(76.4856719970703125, -76.4856719970703125, -13.0938358306884765625),\n            Vec3fEq(96.52036285400390625, -96.52036285400390625, -11.02784252166748046875),\n            Vec3fEq(116.5550537109375, -116.5550537109375, -8.5993976593017578125),\n            Vec3fEq(136.5897369384765625, -136.5897369384765625, -6.170953273773193359375),\n            Vec3fEq(156.6244354248046875, -156.6244354248046875, -3.74250507354736328125),\n            Vec3fEq(176.6591339111328125, -176.6591339111328125, -1.314060688018798828125),\n            Vec3fEq(196.693817138671875, -196.693817138671875, 1.1143856048583984375),\n            Vec3fEq(204, -204, 1.9999811649322509765625)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, path_should_be_around_avoid_shape)\n    {\n        osg::ref_ptr<Resource::BulletShape> bulletShape(new Resource::BulletShape);\n\n        std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        shapePtr->setLocalScaling(btVector3(128, 128, 1));\n        bulletShape->mCollisionShape = shapePtr.release();\n\n        std::array<btScalar, 5 * 5> heightfieldDataAvoid {{\n            -25, -25, -25, -25, -25,\n            -25, -25, -25, -25, -25,\n            -25, -25, -25, -25, -25,\n            -25, -25, -25, -25, -25,\n            -25, -25, -25, -25, -25,\n        }};\n        auto shapeAvoidPtr = makeSquareHeightfieldTerrainShape(heightfieldDataAvoid);\n        shapeAvoidPtr->setLocalScaling(btVector3(128, 128, 1));\n        bulletShape->mAvoidCollisionShape = shapeAvoidPtr.release();\n\n        osg::ref_ptr<const Resource::BulletShapeInstance> instance(new Resource::BulletShapeInstance(bulletShape));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(instance->getCollisionShape()), ObjectShapes(instance), btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204, 204, 1.99997997283935546875),\n            Vec3fEq(-191.328948974609375, 178.65789794921875, -0.815807759761810302734375),\n            Vec3fEq(-178.65789794921875, 153.3157806396484375, -3.6315968036651611328125),\n            Vec3fEq(-165.986846923828125, 127.9736785888671875, -6.4473857879638671875),\n            Vec3fEq(-153.3157806396484375, 102.6315765380859375, -9.26317310333251953125),\n            Vec3fEq(-140.6447296142578125, 77.28946685791015625, -12.07896137237548828125),\n            Vec3fEq(-127.9736785888671875, 51.947368621826171875, -14.894748687744140625),\n            Vec3fEq(-115.3026275634765625, 26.6052646636962890625, -17.7105388641357421875),\n            Vec3fEq(-102.63158416748046875, 1.2631585597991943359375, -20.5263233184814453125),\n            Vec3fEq(-89.9605712890625, -24.0789661407470703125, -19.591716766357421875),\n            Vec3fEq(-68.54410552978515625, -42.629238128662109375, -19.847625732421875),\n            Vec3fEq(-47.127635955810546875, -61.17951202392578125, -20.1035366058349609375),\n            Vec3fEq(-25.711170196533203125, -79.72978973388671875, -20.359447479248046875),\n            Vec3fEq(-4.294706821441650390625, -98.280059814453125, -20.6153545379638671875),\n            Vec3fEq(17.121753692626953125, -116.83034515380859375, -17.3710460662841796875),\n            Vec3fEq(42.7990570068359375, -128.80755615234375, -14.7094440460205078125),\n            Vec3fEq(68.4763641357421875, -140.7847747802734375, -12.0478420257568359375),\n            Vec3fEq(94.15366363525390625, -152.761993408203125, -9.3862361907958984375),\n            Vec3fEq(119.83097076416015625, -164.7392120361328125, -6.724635601043701171875),\n            Vec3fEq(145.508270263671875, -176.7164306640625, -4.06303119659423828125),\n            Vec3fEq(171.185577392578125, -188.69366455078125, -1.40142619609832763671875),\n            Vec3fEq(196.862884521484375, -200.6708831787109375, 1.2601754665374755859375),\n            Vec3fEq(204, -204, 1.999979496002197265625)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_ground_lower_than_water_with_only_swim_flag)\n    {\n        std::array<btScalar, 5 * 5> heightfieldData {{\n            -50,  -50,  -50,  -50,    0,\n            -50, -100, -150, -100,  -50,\n            -50, -150, -200, -150, -100,\n            -50, -100, -150, -100, -100,\n              0,  -50, -100, -100, -100,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity());\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        mStart.x() = 0;\n        mStart.z() = 300;\n        mEnd.x() = 0;\n        mEnd.z() = 300;\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(0, 204, 185.33331298828125),\n            Vec3fEq(0, 175.6666717529296875, 185.33331298828125),\n            Vec3fEq(0, 147.3333282470703125, 185.33331298828125),\n            Vec3fEq(0, 119, 185.33331298828125),\n            Vec3fEq(0, 90.6666717529296875, 185.33331298828125),\n            Vec3fEq(0, 62.333339691162109375, 185.33331298828125),\n            Vec3fEq(0, 34.00000762939453125, 185.33331298828125),\n            Vec3fEq(0, 5.66667461395263671875, 185.33331298828125),\n            Vec3fEq(0, -22.6666584014892578125, 185.33331298828125),\n            Vec3fEq(0, -50.999988555908203125, 185.33331298828125),\n            Vec3fEq(0, -79.33332061767578125, 185.33331298828125),\n            Vec3fEq(0, -107.666656494140625, 185.33331298828125),\n            Vec3fEq(0, -135.9999847412109375, 185.33331298828125),\n            Vec3fEq(0, -164.33331298828125, 185.33331298828125),\n            Vec3fEq(0, -192.666656494140625, 185.33331298828125),\n            Vec3fEq(0, -204, 185.33331298828125)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water_with_swim_and_walk_flags)\n    {\n        std::array<btScalar, 7 * 7> heightfieldData {{\n            0,    0,    0,    0,    0,    0, 0,\n            0, -100, -100, -100, -100, -100, 0,\n            0, -100, -150, -150, -150, -100, 0,\n            0, -100, -150, -200, -150, -100, 0,\n            0, -100, -150, -150, -150, -100, 0,\n            0, -100, -100, -100, -100, -100, 0,\n            0,    0,    0,    0,    0,    0, 0,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity());\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        mStart.x() = 0;\n        mEnd.x() = 0;\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mOut),\n                  Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(0, 204, -98.000030517578125),\n            Vec3fEq(0, 175.6666717529296875, -108.30306243896484375),\n            Vec3fEq(0, 147.3333282470703125, -118.6060791015625),\n            Vec3fEq(0, 119, -128.90911865234375),\n            Vec3fEq(0, 90.6666717529296875, -139.2121429443359375),\n            Vec3fEq(0, 62.333339691162109375, -143.3333587646484375),\n            Vec3fEq(0, 34.00000762939453125, -143.3333587646484375),\n            Vec3fEq(0, 5.66667461395263671875, -143.3333587646484375),\n            Vec3fEq(0, -22.6666584014892578125, -143.3333587646484375),\n            Vec3fEq(0, -50.999988555908203125, -143.3333587646484375),\n            Vec3fEq(0, -79.33332061767578125, -143.3333587646484375),\n            Vec3fEq(0, -107.666656494140625, -133.0303192138671875),\n            Vec3fEq(0, -135.9999847412109375, -122.72728729248046875),\n            Vec3fEq(0, -164.33331298828125, -112.4242706298828125),\n            Vec3fEq(0, -192.666656494140625, -102.12123870849609375),\n            Vec3fEq(0, -204, -98.00002288818359375)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water_with_max_int_cells_size_and_swim_and_walk_flags)\n    {\n        std::array<btScalar, 7 * 7> heightfieldData {{\n            0,    0,    0,    0,    0,    0, 0,\n            0, -100, -100, -100, -100, -100, 0,\n            0, -100, -150, -150, -150, -100, 0,\n            0, -100, -150, -200, -150, -100, 0,\n            0, -100, -150, -150, -150, -100, 0,\n            0, -100, -100, -100, -100, -100, 0,\n            0,    0,    0,    0,    0,    0, 0,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits<int>::max(), -25, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        mStart.x() = 0;\n        mEnd.x() = 0;\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mOut),\n                  Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(0, 204, -98.000030517578125),\n            Vec3fEq(0, 175.6666717529296875, -108.30306243896484375),\n            Vec3fEq(0, 147.3333282470703125, -118.6060791015625),\n            Vec3fEq(0, 119, -128.90911865234375),\n            Vec3fEq(0, 90.6666717529296875, -139.2121429443359375),\n            Vec3fEq(0, 62.333339691162109375, -143.3333587646484375),\n            Vec3fEq(0, 34.00000762939453125, -143.3333587646484375),\n            Vec3fEq(0, 5.66667461395263671875, -143.3333587646484375),\n            Vec3fEq(0, -22.6666584014892578125, -143.3333587646484375),\n            Vec3fEq(0, -50.999988555908203125, -143.3333587646484375),\n            Vec3fEq(0, -79.33332061767578125, -143.3333587646484375),\n            Vec3fEq(0, -107.666656494140625, -133.0303192138671875),\n            Vec3fEq(0, -135.9999847412109375, -122.72728729248046875),\n            Vec3fEq(0, -164.33331298828125, -112.4242706298828125),\n            Vec3fEq(0, -192.666656494140625, -102.12123870849609375),\n            Vec3fEq(0, -204, -98.00002288818359375)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_ground_when_ground_cross_water_with_only_walk_flag)\n    {\n        std::array<btScalar, 7 * 7> heightfieldData {{\n            0,    0,    0,    0,    0,    0, 0,\n            0, -100, -100, -100, -100, -100, 0,\n            0, -100, -150, -150, -150, -100, 0,\n            0, -100, -150, -200, -150, -100, 0,\n            0, -100, -150, -150, -150, -100, 0,\n            0, -100, -100, -100, -100, -100, 0,\n            0,    0,    0,    0,    0,    0, 0,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity());\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        mStart.x() = 0;\n        mEnd.x() = 0;\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(0, 204, -98.000030517578125),\n            Vec3fEq(10.26930999755859375, 177.59320068359375, -107.4711456298828125),\n            Vec3fEq(20.5386199951171875, 151.1864166259765625, -116.9422607421875),\n            Vec3fEq(30.8079280853271484375, 124.77960968017578125, -126.41339111328125),\n            Vec3fEq(41.077239990234375, 98.37281036376953125, -135.8845062255859375),\n            Vec3fEq(51.346546173095703125, 71.96601104736328125, -138.2003936767578125),\n            Vec3fEq(61.615856170654296875, 45.559215545654296875, -140.0838470458984375),\n            Vec3fEq(71.88516998291015625, 19.1524181365966796875, -141.9673004150390625),\n            Vec3fEq(82.15447235107421875, -7.254379749298095703125, -142.3074798583984375),\n            Vec3fEq(81.04636383056640625, -35.56603240966796875, -142.7104339599609375),\n            Vec3fEq(79.93825531005859375, -63.877685546875, -143.1133880615234375),\n            Vec3fEq(78.83014678955078125, -92.18933868408203125, -138.7660675048828125),\n            Vec3fEq(62.50392913818359375, -115.3460235595703125, -130.237823486328125),\n            Vec3fEq(46.17771148681640625, -138.502716064453125, -121.8172149658203125),\n            Vec3fEq(29.85149383544921875, -161.6594085693359375, -113.39659881591796875),\n            Vec3fEq(13.52527523040771484375, -184.81610107421875, -104.97599029541015625),\n            Vec3fEq(0, -204, -98.00002288818359375)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, update_remove_and_update_then_find_path_should_return_path)\n    {\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        mNavigator->removeObject(ObjectId(&shape));\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204, 204, 1.99998295307159423828125),\n            Vec3fEq(-183.965301513671875, 183.965301513671875, 1.99998819828033447265625),\n            Vec3fEq(-163.9306182861328125, 163.9306182861328125, 1.99999344348907470703125),\n            Vec3fEq(-143.89593505859375, 143.89593505859375, -2.7206256389617919921875),\n            Vec3fEq(-123.86124420166015625, 123.86124420166015625, -13.1089839935302734375),\n            Vec3fEq(-103.8265533447265625, 103.8265533447265625, -23.4973468780517578125),\n            Vec3fEq(-83.7918548583984375, 83.7918548583984375, -33.885707855224609375),\n            Vec3fEq(-63.75716400146484375, 63.75716400146484375, -44.27407073974609375),\n            Vec3fEq(-43.72247314453125, 43.72247314453125, -54.662433624267578125),\n            Vec3fEq(-23.6877803802490234375, 23.6877803802490234375, -65.0507965087890625),\n            Vec3fEq(-3.653090000152587890625, 3.653090000152587890625, -75.43915557861328125),\n            Vec3fEq(16.3816013336181640625, -16.3816013336181640625, -69.749267578125),\n            Vec3fEq(36.416290283203125, -36.416290283203125, -60.4739532470703125),\n            Vec3fEq(56.450984954833984375, -56.450984954833984375, -51.1986236572265625),\n            Vec3fEq(76.4856719970703125, -76.4856719970703125, -41.92330169677734375),\n            Vec3fEq(96.52036285400390625, -96.52036285400390625, -31.46941375732421875),\n            Vec3fEq(116.5550537109375, -116.5550537109375, -19.597003936767578125),\n            Vec3fEq(136.5897369384765625, -136.5897369384765625, -7.724592685699462890625),\n            Vec3fEq(156.6244354248046875, -156.6244354248046875, 1.99999535083770751953125),\n            Vec3fEq(176.6591339111328125, -176.6591339111328125, 1.99999010562896728515625),\n            Vec3fEq(196.693817138671875, -196.693817138671875, 1.99998486042022705078125),\n            Vec3fEq(204, -204, 1.99998295307159423828125)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, update_then_find_random_point_around_circle_should_return_position)\n    {\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        Misc::Rng::init(42);\n\n        const auto result = mNavigator->findRandomPointAroundCircle(mAgentHalfExtents, mStart, 100.0, Flag_walk);\n\n        ASSERT_THAT(result, Optional(Vec3fEq(-198.909332275390625, 123.06096649169921875, 1.99998414516448974609375)))\n            << (result ? *result : osg::Vec3f());\n\n        const auto distance = (*result - mStart).length();\n\n        EXPECT_FLOAT_EQ(distance, 81.105133056640625) << distance;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, multiple_threads_should_lock_tiles)\n    {\n        mSettings.mAsyncNavMeshUpdaterThreads = 2;\n        mNavigator.reset(new NavigatorImpl(mSettings));\n\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto heightfieldShapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& heightfieldShape = *heightfieldShapePtr;\n        heightfieldShape.setLocalScaling(btVector3(128, 128, 1));\n\n        std::vector<CollisionShapeInstance<btBoxShape>> boxes;\n        std::generate_n(std::back_inserter(boxes), 100, [] { return std::make_unique<btBoxShape>(btVector3(20, 20, 100)); });\n\n        mNavigator->addAgent(mAgentHalfExtents);\n\n        mNavigator->addObject(ObjectId(&heightfieldShape), nullptr, heightfieldShape, btTransform::getIdentity());\n\n        for (std::size_t i = 0; i < boxes.size(); ++i)\n        {\n            const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 10, i * 10, i * 10));\n            mNavigator->addObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance()), transform);\n        }\n\n        std::this_thread::sleep_for(std::chrono::microseconds(1));\n\n        for (std::size_t i = 0; i < boxes.size(); ++i)\n        {\n            const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 10 + 1, i * 10 + 1, i * 10 + 1));\n            mNavigator->updateObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance()), transform);\n        }\n\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);\n\n        EXPECT_THAT(mPath, ElementsAre(\n            Vec3fEq(-204, 204, 1.99998295307159423828125),\n            Vec3fEq(-189.9427337646484375, 179.3997802734375, 1.9999866485595703125),\n            Vec3fEq(-175.8854522705078125, 154.7995452880859375, 1.99999034404754638671875),\n            Vec3fEq(-161.82818603515625, 130.1993255615234375, -3.701923847198486328125),\n            Vec3fEq(-147.770904541015625, 105.5991058349609375, -15.67664432525634765625),\n            Vec3fEq(-133.7136383056640625, 80.99887847900390625, -27.6513614654541015625),\n            Vec3fEq(-119.65636444091796875, 56.39865875244140625, -20.1209163665771484375),\n            Vec3fEq(-105.59909820556640625, 31.798435211181640625, -25.0669879913330078125),\n            Vec3fEq(-91.54183197021484375, 7.1982135772705078125, -31.5624217987060546875),\n            Vec3fEq(-77.48455810546875, -17.402008056640625, -26.98972320556640625),\n            Vec3fEq(-63.427295684814453125, -42.00223541259765625, -19.9045581817626953125),\n            Vec3fEq(-42.193531036376953125, -60.761363983154296875, -20.4544773101806640625),\n            Vec3fEq(-20.9597682952880859375, -79.5204925537109375, -23.599918365478515625),\n            Vec3fEq(3.8312885761260986328125, -93.2384033203125, -30.7141361236572265625),\n            Vec3fEq(28.6223468780517578125, -106.95632171630859375, -24.1782474517822265625),\n            Vec3fEq(53.413402557373046875, -120.6742401123046875, -19.4096889495849609375),\n            Vec3fEq(78.20446014404296875, -134.39215087890625, -27.6632633209228515625),\n            Vec3fEq(102.99552154541015625, -148.110076904296875, -15.8613681793212890625),\n            Vec3fEq(127.7865753173828125, -161.827972412109375, -4.059485912322998046875),\n            Vec3fEq(152.57763671875, -175.5458984375, 1.9999904632568359375),\n            Vec3fEq(177.3686981201171875, -189.2638092041015625, 1.9999866485595703125),\n            Vec3fEq(202.1597442626953125, -202.9817047119140625, 1.9999830722808837890625),\n            Vec3fEq(204, -204, 1.99998295307159423828125)\n        )) << mPath;\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, update_changed_multiple_times_object_should_delay_navmesh_change)\n    {\n        std::vector<CollisionShapeInstance<btBoxShape>> shapes;\n        std::generate_n(std::back_inserter(shapes), 100, [] { return std::make_unique<btBoxShape>(btVector3(64, 64, 64)); });\n\n        mNavigator->addAgent(mAgentHalfExtents);\n\n        for (std::size_t i = 0; i < shapes.size(); ++i)\n        {\n            const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32, i * 32, i * 32));\n            mNavigator->addObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance()), transform);\n        }\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        const auto start = std::chrono::steady_clock::now();\n        for (std::size_t i = 0; i < shapes.size(); ++i)\n        {\n            const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 1, i * 32 + 1, i * 32 + 1));\n            mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance()), transform);\n        }\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        for (std::size_t i = 0; i < shapes.size(); ++i)\n        {\n            const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 2, i * 32 + 2, i * 32 + 2));\n            mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance()), transform);\n        }\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        const auto duration = std::chrono::steady_clock::now() - start;\n\n        EXPECT_GT(duration, mSettings.mMinUpdateInterval)\n            << std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(duration).count() << \" ms\";\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, update_then_raycast_should_return_position)\n    {\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& shape = *shapePtr;\n        shape.setLocalScaling(btVector3(128, 128, 1));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        const auto result = mNavigator->raycast(mAgentHalfExtents, mStart, mEnd, Flag_walk);\n\n        ASSERT_THAT(result, Optional(Vec3fEq(mEnd.x(), mEnd.y(), 1.99998295307159423828125)))\n            << (result ? *result : osg::Vec3f());\n    }\n\n    TEST_F(DetourNavigatorNavigatorTest, update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update)\n    {\n        const std::array<btScalar, 5 * 5> heightfieldData {{\n            0,   0,    0,    0,    0,\n            0, -25,  -25,  -25,  -25,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n            0, -25, -100, -100, -100,\n        }};\n        const auto heightfieldShapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);\n        btHeightfieldTerrainShape& heightfieldShape = *heightfieldShapePtr;\n        heightfieldShape.setLocalScaling(btVector3(128, 128, 1));\n\n        CollisionShapeInstance oscillatingBox(std::make_unique<btBoxShape>(btVector3(20, 20, 20)));\n        const btVector3 oscillatingBoxShapePosition(32, 32, 400);\n        CollisionShapeInstance boderBox(std::make_unique<btBoxShape>(btVector3(50, 50, 50)));\n\n        mNavigator->addAgent(mAgentHalfExtents);\n        mNavigator->addObject(ObjectId(&heightfieldShape), nullptr, heightfieldShape, btTransform::getIdentity());\n        mNavigator->addObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance()),\n                              btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition));\n        // add this box to make navmesh bound box independent from oscillatingBoxShape rotations\n        mNavigator->addObject(ObjectId(&boderBox.shape()), ObjectShapes(boderBox.instance()),\n                              btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200)));\n        mNavigator->update(mPlayerPosition);\n        mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n\n        const auto navMeshes = mNavigator->getNavMeshes();\n        ASSERT_EQ(navMeshes.size(), 1);\n        {\n            const auto navMesh = navMeshes.begin()->second->lockConst();\n            ASSERT_EQ(navMesh->getGeneration(), 1);\n            ASSERT_EQ(navMesh->getNavMeshRevision(), 4);\n        }\n\n        for (int n = 0; n < 10; ++n)\n        {\n            const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10),\n                                        oscillatingBoxShapePosition);\n            mNavigator->updateObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance()), transform);\n            mNavigator->update(mPlayerPosition);\n            mNavigator->wait(mListener, WaitConditionType::allJobsDone);\n        }\n\n        ASSERT_EQ(navMeshes.size(), 1);\n        {\n            const auto navMesh = navMeshes.begin()->second->lockConst();\n            ASSERT_EQ(navMesh->getGeneration(), 1);\n            ASSERT_EQ(navMesh->getNavMeshRevision(), 4);\n        }\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp",
    "content": "#include \"operators.hpp\"\n\n#include <components/detournavigator/navmeshtilescache.hpp>\n#include <components/detournavigator/exceptions.hpp>\n#include <components/detournavigator/recastmesh.hpp>\n\n#include <LinearMath/btTransform.h>\n\n#include <gtest/gtest.h>\n\nnamespace DetourNavigator\n{\n    static inline bool operator ==(const NavMeshDataRef& lhs, const NavMeshDataRef& rhs)\n    {\n        return std::make_pair(lhs.mValue, lhs.mSize) == std::make_pair(rhs.mValue, rhs.mSize);\n    }\n}\n\nnamespace\n{\n    using namespace testing;\n    using namespace DetourNavigator;\n\n    struct DetourNavigatorNavMeshTilesCacheTest : Test\n    {\n        const osg::Vec3f mAgentHalfExtents {1, 2, 3};\n        const TilePosition mTilePosition {0, 0};\n        const std::size_t mGeneration = 0;\n        const std::size_t mRevision = 0;\n        const std::vector<int> mIndices {{0, 1, 2}};\n        const std::vector<float> mVertices {{0, 0, 0, 1, 0, 0, 1, 1, 0}};\n        const std::vector<AreaType> mAreaTypes {1, AreaType_ground};\n        const std::vector<RecastMesh::Water> mWater {};\n        const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mWater};\n        const std::vector<OffMeshConnection> mOffMeshConnections {};\n        unsigned char* const mData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData mNavMeshData {mData, 1};\n\n        const size_t cRecastMeshKeySize = mRecastMesh.getIndices().size() * sizeof(int)\n            + mRecastMesh.getVertices().size() * sizeof(float)\n            + mRecastMesh.getAreaTypes().size() * sizeof(AreaType)\n            + mRecastMesh.getWater().size() * sizeof(RecastMesh::Water)\n            + mOffMeshConnections.size() * sizeof(OffMeshConnection);\n\n        const size_t cRecastMeshWithWaterKeySize = cRecastMeshKeySize + sizeof(RecastMesh::Water);\n    };\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_empty_cache_should_return_empty_value)\n    {\n        const std::size_t maxSize = 0;\n        NavMeshTilesCache cache(maxSize);\n\n        EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_for_not_enought_cache_size_should_return_empty_value)\n    {\n        const std::size_t maxSize = 0;\n        NavMeshTilesCache cache(maxSize);\n\n        EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,\n                               std::move(mNavMeshData)));\n        EXPECT_NE(mNavMeshData.mValue, nullptr);\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_return_cached_value)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshKeySize;\n        const std::size_t maxSize = navMeshDataSize + navMeshKeySize;\n        NavMeshTilesCache cache(maxSize);\n\n        const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,\n                                      std::move(mNavMeshData));\n        ASSERT_TRUE(result);\n        EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_existing_element_should_return_cached_element)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshKeySize;\n        const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize);\n        NavMeshTilesCache cache(maxSize);\n        const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData anotherNavMeshData {anotherData, 1};\n\n        cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        EXPECT_EQ(mNavMeshData.mValue, nullptr);\n        const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(anotherNavMeshData));\n        ASSERT_TRUE(result);\n        EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_should_return_cached_value)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshKeySize;\n        const std::size_t maxSize = navMeshDataSize + navMeshKeySize;\n        NavMeshTilesCache cache(maxSize);\n\n        cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections);\n        ASSERT_TRUE(result);\n        EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_agent_half_extents_should_return_empty_value)\n    {\n        const std::size_t maxSize = 1;\n        NavMeshTilesCache cache(maxSize);\n        const osg::Vec3f unexsistentAgentHalfExtents {1, 1, 1};\n\n        cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_tile_position_should_return_empty_value)\n    {\n        const std::size_t maxSize = 1;\n        NavMeshTilesCache cache(maxSize);\n        const TilePosition unexistentTilePosition {1, 1};\n\n        cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_recast_mesh_should_return_empty_value)\n    {\n        const std::size_t maxSize = 1;\n        NavMeshTilesCache cache(maxSize);\n        const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};\n\n        cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize;\n        const std::size_t maxSize = navMeshDataSize + navMeshKeySize;\n        NavMeshTilesCache cache(maxSize);\n\n        const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};\n        const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData anotherNavMeshData {anotherData, 1};\n\n        cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,\n                                      std::move(anotherNavMeshData));\n        ASSERT_TRUE(result);\n        EXPECT_EQ(result.get(), (NavMeshDataRef {anotherData, 1}));\n        EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_used_value)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshKeySize;\n        const std::size_t maxSize = navMeshDataSize + navMeshKeySize;\n        NavMeshTilesCache cache(maxSize);\n\n        const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};\n        const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData anotherNavMeshData {anotherData, 1};\n\n        const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,\n                                     std::move(mNavMeshData));\n        EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,\n                               std::move(anotherNavMeshData)));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_set_value)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize;\n        const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize);\n        NavMeshTilesCache cache(maxSize);\n\n        const std::vector<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,\n            mAreaTypes, leastRecentlySetWater};\n        const auto leastRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1};\n\n        const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};\n        const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,\n            mAreaTypes, mostRecentlySetWater};\n        const auto mostRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1};\n\n        ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, mOffMeshConnections,\n                              std::move(leastRecentlySetNavMeshData)));\n        ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, mOffMeshConnections,\n                              std::move(mostRecentlySetNavMeshData)));\n\n        const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,\n                                      std::move(mNavMeshData));\n        EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));\n\n        EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, mOffMeshConnections));\n        EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize;\n        const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize);\n        NavMeshTilesCache cache(maxSize);\n\n        const std::vector<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,\n            mAreaTypes, leastRecentlyUsedWater};\n        const auto leastRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1};\n\n        const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};\n        const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,\n            mAreaTypes, mostRecentlyUsedWater};\n        const auto mostRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1};\n\n        cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections,\n                  std::move(leastRecentlyUsedNavMeshData));\n        cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections,\n                  std::move(mostRecentlyUsedNavMeshData));\n\n        {\n            const auto value = cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections);\n            ASSERT_TRUE(value);\n            ASSERT_EQ(value.get(), (NavMeshDataRef {leastRecentlyUsedData, 1}));\n        }\n\n        {\n            const auto value = cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections);\n            ASSERT_TRUE(value);\n            ASSERT_EQ(value.get(), (NavMeshDataRef {mostRecentlyUsedData, 1}));\n        }\n\n        const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,\n                                      std::move(mNavMeshData));\n        EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));\n\n        EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections));\n        EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_cache_max_size)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshKeySize;\n        const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize);\n        NavMeshTilesCache cache(maxSize);\n\n        const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};\n        const auto tooLargeData = reinterpret_cast<unsigned char*>(dtAlloc(2, DT_ALLOC_PERM));\n        NavMeshData tooLargeNavMeshData {tooLargeData, 2};\n\n        cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, mOffMeshConnections,\n                               std::move(tooLargeNavMeshData)));\n        EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_size_of_unused_items)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize1 = cRecastMeshKeySize;\n        const std::size_t navMeshKeySize2 = cRecastMeshWithWaterKeySize;\n        const std::size_t maxSize = 2 * navMeshDataSize + navMeshKeySize1 + navMeshKeySize2;\n        NavMeshTilesCache cache(maxSize);\n\n        const std::vector<RecastMesh::Water> anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater};\n        const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData anotherNavMeshData {anotherData, 1};\n\n        const std::vector<RecastMesh::Water> tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};\n        const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices,\n            mAreaTypes, tooLargeWater};\n        const auto tooLargeData = reinterpret_cast<unsigned char*>(dtAlloc(2, DT_ALLOC_PERM));\n        NavMeshData tooLargeNavMeshData {tooLargeData, 2};\n\n        const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,\n                                     std::move(mNavMeshData));\n        ASSERT_TRUE(value);\n        ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,\n                              std::move(anotherNavMeshData)));\n        EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, mOffMeshConnections,\n                               std::move(tooLargeNavMeshData)));\n        EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));\n        EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_used_after_set_then_used_by_get_item_should_left_this_item_available)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshKeySize;\n        const std::size_t maxSize = navMeshDataSize + navMeshKeySize;\n        NavMeshTilesCache cache(maxSize);\n\n        const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices,\n            mAreaTypes, water};\n        const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData anotherNavMeshData {anotherData, 1};\n\n        const auto firstCopy = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        ASSERT_TRUE(firstCopy);\n        {\n            const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections);\n            ASSERT_TRUE(secondCopy);\n        }\n        EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,\n                               std::move(anotherNavMeshData)));\n        EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));\n    }\n\n    TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_twice_used_item_should_left_this_item_available)\n    {\n        const std::size_t navMeshDataSize = 1;\n        const std::size_t navMeshKeySize = cRecastMeshKeySize;\n        const std::size_t maxSize = navMeshDataSize + navMeshKeySize;\n        NavMeshTilesCache cache(maxSize);\n\n        const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};\n        const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};\n        const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));\n        NavMeshData anotherNavMeshData {anotherData, 1};\n\n        cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));\n        const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections);\n        ASSERT_TRUE(firstCopy);\n        {\n            const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections);\n            ASSERT_TRUE(secondCopy);\n        }\n        EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,\n                               std::move(anotherNavMeshData)));\n        EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/detournavigator/operators.hpp",
    "content": "#ifndef OPENMW_TEST_SUITE_DETOURNAVIGATOR_OPERATORS_H\n#define OPENMW_TEST_SUITE_DETOURNAVIGATOR_OPERATORS_H\n\n#include <components/bullethelpers/operators.hpp>\n#include <components/detournavigator/debug.hpp>\n\n#include <deque>\n#include <iomanip>\n#include <iostream>\n#include <limits>\n#include <sstream>\n\n#include <gtest/gtest.h>\n\nnamespace DetourNavigator\n{\n    static inline bool operator ==(const TileBounds& lhs, const TileBounds& rhs)\n    {\n        return lhs.mMin == rhs.mMin && lhs.mMax == rhs.mMax;\n    }\n}\n\nnamespace\n{\n    template <class T>\n    struct Wrapper\n    {\n        const T& mValue;\n    };\n\n    template <class Range>\n    inline testing::Message& writeRange(testing::Message& message, const Range& range, std::size_t newLine)\n    {\n        message << \"{\";\n        std::size_t i = 0;\n        for (const auto& v : range)\n        {\n            if (i++ % newLine == 0)\n                message << \"\\n\";\n            message << Wrapper<typename std::decay<decltype(v)>::type> {v} << \", \";\n        }\n        return message << \"\\n}\";\n    }\n}\n\nnamespace testing\n{\n    template <>\n    inline testing::Message& Message::operator <<(const osg::Vec3f& value)\n    {\n        return (*this) << \"osg::Vec3f(\" << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.x()\n            << \", \" << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.y()\n            << \", \" << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.z()\n            << ')';\n    }\n\n    template <>\n    inline testing::Message& Message::operator <<(const Wrapper<osg::Vec3f>& value)\n    {\n        return (*this) << value.mValue;\n    }\n\n    template <>\n    inline testing::Message& Message::operator <<(const Wrapper<float>& value)\n    {\n        return (*this) << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue;\n    }\n\n    template <>\n    inline testing::Message& Message::operator <<(const Wrapper<int>& value)\n    {\n        return (*this) << value.mValue;\n    }\n\n    template <>\n    inline testing::Message& Message::operator <<(const std::deque<osg::Vec3f>& value)\n    {\n        return writeRange(*this, value, 1);\n    }\n\n    template <>\n    inline testing::Message& Message::operator <<(const std::vector<osg::Vec3f>& value)\n    {\n        return writeRange(*this, value, 1);\n    }\n\n    template <>\n    inline testing::Message& Message::operator <<(const std::vector<float>& value)\n    {\n        return writeRange(*this, value, 3);\n    }\n\n    template <>\n    inline testing::Message& Message::operator <<(const std::vector<int>& value)\n    {\n        return writeRange(*this, value, 3);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp",
    "content": "#include \"operators.hpp\"\n\n#include <components/detournavigator/recastmeshbuilder.hpp>\n#include <components/detournavigator/settings.hpp>\n#include <components/detournavigator/recastmesh.hpp>\n#include <components/detournavigator/exceptions.hpp>\n\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>\n#include <BulletCollision/CollisionShapes/btTriangleMesh.h>\n#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n\n#include <gtest/gtest.h>\n#include <gmock/gmock.h>\n\n#include <array>\n\nnamespace DetourNavigator\n{\n    static inline bool operator ==(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs)\n    {\n        return lhs.mCellSize == rhs.mCellSize && lhs.mTransform == rhs.mTransform;\n    }\n}\n\nnamespace\n{\n    using namespace testing;\n    using namespace DetourNavigator;\n\n    struct DetourNavigatorRecastMeshBuilderTest : Test\n    {\n        Settings mSettings;\n        TileBounds mBounds;\n        const std::size_t mGeneration = 0;\n        const std::size_t mRevision = 0;\n\n        DetourNavigatorRecastMeshBuilderTest()\n        {\n            mSettings.mRecastScaleFactor = 1.0f;\n            mBounds.mMin = osg::Vec2f(-std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon(),\n                                      -std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon());\n            mBounds.mMax = osg::Vec2f(std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon(),\n                                      std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon());\n        }\n    };\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty)\n    {\n        RecastMeshBuilder builder(mSettings, mBounds);\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>());\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>());\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>());\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape)\n    {\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        btBvhTriangleMeshShape shape(&mesh, true);\n\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            1, 0, -1,\n            -1, 0, 1,\n            -1, 0, -1,\n        }));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_bhv_triangle_mesh_shape)\n    {\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        btBvhTriangleMeshShape shape(&mesh, true);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            2, 3, 0,\n            0, 3, 4,\n            0, 3, 0,\n        }));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_heightfield_terrian_shape)\n    {\n        const std::array<btScalar, 4> heightfieldData {{0, 0, 0, 0}};\n        btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            -0.5, 0, -0.5,\n            -0.5, 0, 0.5,\n            0.5, 0, -0.5,\n            0.5, 0, 0.5,\n        }));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 2, 1, 3}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_box_shape_should_produce_12_triangles)\n    {\n        btBoxShape shape(btVector3(1, 1, 2));\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            1, 2, 1,\n            -1, 2, 1,\n            1, 2, -1,\n            -1, 2, -1,\n            1, -2, 1,\n            -1, -2, 1,\n            1, -2, -1,\n            -1, -2, -1,\n        })) << recastMesh->getVertices();\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({\n            0, 2, 3,\n            3, 1, 0,\n            0, 4, 6,\n            6, 2, 0,\n            0, 1, 5,\n            5, 4, 0,\n            7, 5, 1,\n            1, 3, 7,\n            7, 3, 2,\n            2, 6, 7,\n            7, 6, 4,\n            4, 5, 7,\n        })) << recastMesh->getIndices();\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>(12, AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_compound_shape)\n    {\n        btTriangleMesh mesh1;\n        mesh1.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        btBvhTriangleMeshShape triangle1(&mesh1, true);\n        btBoxShape box(btVector3(1, 1, 2));\n        btTriangleMesh mesh2;\n        mesh2.addTriangle(btVector3(1, 1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        btBvhTriangleMeshShape triangle2(&mesh2, true);\n        btCompoundShape shape;\n        shape.addChildShape(btTransform::getIdentity(), &triangle1);\n        shape.addChildShape(btTransform::getIdentity(), &box);\n        shape.addChildShape(btTransform::getIdentity(), &triangle2);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform::getIdentity(),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            -1, -2, -1,\n            -1, -2, 1,\n            -1, 0, -1,\n            -1, 0, 1,\n            -1, 2, -1,\n            -1, 2, 1,\n            1, -2, -1,\n            1, -2, 1,\n            1, 0, -1,\n            1, 0, 1,\n            1, 2, -1,\n            1, 2, 1,\n        })) << recastMesh->getVertices();\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({\n            8, 3, 2,\n            11, 10, 4,\n            4, 5, 11,\n            11, 7, 6,\n            6, 10, 11,\n            11, 5, 1,\n            1, 7, 11,\n            0, 1, 5,\n            5, 4, 0,\n            0, 4, 10,\n            10, 6, 0,\n            0, 6, 7,\n            7, 1, 0,\n            8, 3, 9,\n        })) << recastMesh->getIndices();\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>(14, AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape)\n    {\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        btBvhTriangleMeshShape triangle(&mesh, true);\n        btCompoundShape shape;\n        shape.addChildShape(btTransform::getIdentity(), &triangle);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            2, 3, 0,\n            0, 3, 4,\n            0, 3, 0,\n        }));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape_with_transformed_bhv_triangle_shape)\n    {\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        btBvhTriangleMeshShape triangle(&mesh, true);\n        btCompoundShape shape;\n        shape.addChildShape(btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),\n                            &triangle);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            3, 12, 2,\n            1, 12, 10,\n            1, 12, 2,\n        }));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, without_bounds_add_bhv_triangle_shape_should_not_filter_by_bounds)\n    {\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));\n        btBvhTriangleMeshShape shape(&mesh, true);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform::getIdentity(),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            1, 0, -1,\n            -1, 0, 1,\n            -1, 0, -1,\n            -2, 0, -3,\n            -3, 0, -2,\n            -3, 0, -3,\n        }));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 3, 4, 5}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>(2, AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_bhv_triangle_shape_should_filter_by_bounds)\n    {\n        mSettings.mRecastScaleFactor = 0.1f;\n        mBounds.mMin = osg::Vec2f(-3, -3) * mSettings.mRecastScaleFactor;\n        mBounds.mMax = osg::Vec2f(-2, -2) * mSettings.mRecastScaleFactor;\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));\n        btBvhTriangleMeshShape shape(&mesh, true);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform::getIdentity(),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            -0.2f, 0, -0.3f,\n            -0.3f, 0, -0.2f,\n            -0.3f, 0, -0.3f,\n        }));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_x_bhv_triangle_shape_should_filter_by_bounds)\n    {\n        mBounds.mMin = osg::Vec2f(-5, -5);\n        mBounds.mMax = osg::Vec2f(5, -2);\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(0, -1, -1), btVector3(0, -1, -1), btVector3(0, 1, -1));\n        mesh.addTriangle(btVector3(0, -3, -3), btVector3(0, -3, -2), btVector3(0, -2, -3));\n        btBvhTriangleMeshShape shape(&mesh, true);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform(btQuaternion(btVector3(1, 0, 0),\n            static_cast<btScalar>(-osg::PI_4))),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({\n            0, -0.70710659027099609375, -3.535533905029296875,\n            0, 0.707107067108154296875, -3.535533905029296875,\n            0, 2.384185791015625e-07, -4.24264049530029296875,\n        })));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_y_bhv_triangle_shape_should_filter_by_bounds)\n    {\n        mBounds.mMin = osg::Vec2f(-5, -5);\n        mBounds.mMax = osg::Vec2f(-3, 5);\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, 0, -1), btVector3(-1, 0, 1), btVector3(1, 0, -1));\n        mesh.addTriangle(btVector3(-3, 0, -3), btVector3(-3, 0, -2), btVector3(-2, 0, -3));\n        btBvhTriangleMeshShape shape(&mesh, true);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform(btQuaternion(btVector3(0, 1, 0),\n            static_cast<btScalar>(osg::PI_4))),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({\n            -3.535533905029296875, -0.70710659027099609375, 0,\n            -3.535533905029296875, 0.707107067108154296875, 0,\n            -4.24264049530029296875, 2.384185791015625e-07, 0,\n        })));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_z_bhv_triangle_shape_should_filter_by_bounds)\n    {\n        mBounds.mMin = osg::Vec2f(-5, -5);\n        mBounds.mMax = osg::Vec2f(-1, -1);\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));\n        btBvhTriangleMeshShape shape(&mesh, true);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape),\n            btTransform(btQuaternion(btVector3(0, 0, 1),\n            static_cast<btScalar>(osg::PI_4))),\n            AreaType_ground\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({\n            1.41421353816986083984375, 0, 1.1920928955078125e-07,\n            -1.41421353816986083984375, 0, -1.1920928955078125e-07,\n            1.1920928955078125e-07, 0, -1.41421353816986083984375,\n        })));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, flags_values_should_be_corresponding_to_added_objects)\n    {\n        btTriangleMesh mesh1;\n        mesh1.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        btBvhTriangleMeshShape shape1(&mesh1, true);\n        btTriangleMesh mesh2;\n        mesh2.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));\n        btBvhTriangleMeshShape shape2(&mesh2, true);\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape1),\n            btTransform::getIdentity(),\n            AreaType_ground\n        );\n        builder.addObject(\n            static_cast<const btCollisionShape&>(shape2),\n            btTransform::getIdentity(),\n            AreaType_null\n        );\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            1, 0, -1,\n            -1, 0, 1,\n            -1, 0, -1,\n            -2, 0, -3,\n            -3, 0, -2,\n            -3, 0, -3,\n        }));\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 3, 4, 5}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_null}));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_water_then_get_water_should_return_it)\n    {\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300)));\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getWater(), std::vector<RecastMesh::Water>({\n            RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))}\n        }));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape_with_duplicated_vertices)\n    {\n        btTriangleMesh mesh;\n        mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        mesh.addTriangle(btVector3(1, 1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));\n        btBvhTriangleMeshShape shape(&mesh, true);\n\n        RecastMeshBuilder builder(mSettings, mBounds);\n        builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);\n        const auto recastMesh = std::move(builder).create(mGeneration, mRevision);\n        EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({\n            -1, 0, -1,\n            -1, 0, 1,\n            1, 0, -1,\n            1, 0, 1,\n        })) << recastMesh->getVertices();\n        EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({2, 1, 0, 2, 1, 3}));\n        EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground}));\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/detournavigator/recastmeshobject.cpp",
    "content": "#include \"operators.hpp\"\n\n#include <components/detournavigator/recastmeshobject.hpp>\n\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n\n#include <gtest/gtest.h>\n\nnamespace\n{\n    using namespace testing;\n    using namespace DetourNavigator;\n\n    struct DetourNavigatorRecastMeshObjectTest : Test\n    {\n        btBoxShape mBoxShapeImpl {btVector3(1, 2, 3)};\n        CollisionShape mBoxShape {nullptr, mBoxShapeImpl};\n        btCompoundShape mCompoundShapeImpl {true};\n        CollisionShape mCompoundShape {nullptr, mCompoundShapeImpl};\n        btTransform mTransform {btQuaternion(btVector3(1, 2, 3), 1), btVector3(1, 2, 3)};\n\n        DetourNavigatorRecastMeshObjectTest()\n        {\n            mCompoundShapeImpl.addChildShape(mTransform, std::addressof(mBoxShapeImpl));\n        }\n    };\n\n    TEST_F(DetourNavigatorRecastMeshObjectTest, constructed_object_should_have_shape_and_transform)\n    {\n        const RecastMeshObject object(mBoxShape, mTransform, AreaType_ground);\n        EXPECT_EQ(std::addressof(object.getShape()), std::addressof(mBoxShapeImpl));\n        EXPECT_EQ(object.getTransform(), mTransform);\n    }\n\n    TEST_F(DetourNavigatorRecastMeshObjectTest, update_with_same_transform_for_not_compound_shape_should_return_false)\n    {\n        RecastMeshObject object(mBoxShape, mTransform, AreaType_ground);\n        EXPECT_FALSE(object.update(mTransform, AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshObjectTest, update_with_different_transform_should_return_true)\n    {\n        RecastMeshObject object(mBoxShape, mTransform, AreaType_ground);\n        EXPECT_TRUE(object.update(btTransform::getIdentity(), AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshObjectTest, update_with_different_flags_should_return_true)\n    {\n        RecastMeshObject object(mBoxShape, mTransform, AreaType_ground);\n        EXPECT_TRUE(object.update(mTransform, AreaType_null));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshObjectTest, update_for_compound_shape_with_same_transform_and_not_changed_child_transform_should_return_false)\n    {\n        RecastMeshObject object(mCompoundShape, mTransform, AreaType_ground);\n        EXPECT_FALSE(object.update(mTransform, AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshObjectTest, update_for_compound_shape_with_same_transform_and_changed_child_transform_should_return_true)\n    {\n        RecastMeshObject object(mCompoundShape, mTransform, AreaType_ground);\n        mCompoundShapeImpl.updateChildTransform(0, btTransform::getIdentity());\n        EXPECT_TRUE(object.update(mTransform, AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshObjectTest, repeated_update_for_compound_shape_without_changes_should_return_false)\n    {\n        RecastMeshObject object(mCompoundShape, mTransform, AreaType_ground);\n        mCompoundShapeImpl.updateChildTransform(0, btTransform::getIdentity());\n        object.update(mTransform, AreaType_ground);\n        EXPECT_FALSE(object.update(mTransform, AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorRecastMeshObjectTest, update_for_changed_local_scaling_should_return_true)\n    {\n        RecastMeshObject object(mBoxShape, mTransform, AreaType_ground);\n        mBoxShapeImpl.setLocalScaling(btVector3(2, 2, 2));\n        EXPECT_TRUE(object.update(mTransform, AreaType_ground));\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/detournavigator/settingsutils.cpp",
    "content": "#include \"operators.hpp\"\n\n#include <components/detournavigator/settingsutils.hpp>\n\n#include <gtest/gtest.h>\n\nnamespace\n{\n    using namespace testing;\n    using namespace DetourNavigator;\n\n    struct DetourNavigatorGetTilePositionTest : Test\n    {\n        Settings mSettings;\n\n        DetourNavigatorGetTilePositionTest()\n        {\n            mSettings.mCellSize = 0.5;\n            mSettings.mTileSize = 64;\n        }\n    };\n\n    TEST_F(DetourNavigatorGetTilePositionTest, for_zero_coordinates_should_return_zero_tile_position)\n    {\n        EXPECT_EQ(getTilePosition(mSettings, osg::Vec3f(0, 0, 0)), TilePosition(0, 0));\n    }\n\n    TEST_F(DetourNavigatorGetTilePositionTest, tile_size_should_be_multiplied_by_cell_size)\n    {\n        EXPECT_EQ(getTilePosition(mSettings, osg::Vec3f(32, 0, 0)), TilePosition(1, 0));\n    }\n\n    TEST_F(DetourNavigatorGetTilePositionTest, tile_position_calculates_by_floor)\n    {\n        EXPECT_EQ(getTilePosition(mSettings, osg::Vec3f(31, 0, 0)), TilePosition(0, 0));\n    }\n\n    TEST_F(DetourNavigatorGetTilePositionTest, tile_position_depends_on_x_and_z_coordinates)\n    {\n        EXPECT_EQ(getTilePosition(mSettings, osg::Vec3f(32, 64, 128)), TilePosition(1, 4));\n    }\n\n    TEST_F(DetourNavigatorGetTilePositionTest, tile_position_works_for_negative_coordinates)\n    {\n        EXPECT_EQ(getTilePosition(mSettings, osg::Vec3f(-31, 0, -32)), TilePosition(-1, -1));\n    }\n\n    struct DetourNavigatorMakeTileBoundsTest : Test\n    {\n        Settings mSettings;\n\n        DetourNavigatorMakeTileBoundsTest()\n        {\n            mSettings.mCellSize = 0.5;\n            mSettings.mTileSize = 64;\n        }\n    };\n\n    TEST_F(DetourNavigatorMakeTileBoundsTest, tile_bounds_depend_on_tile_size_and_cell_size)\n    {\n        EXPECT_EQ(makeTileBounds(mSettings, TilePosition(0, 0)), (TileBounds {osg::Vec2f(0, 0), osg::Vec2f(32, 32)}));\n    }\n\n    TEST_F(DetourNavigatorMakeTileBoundsTest, tile_bounds_are_multiplied_by_tile_position)\n    {\n        EXPECT_EQ(makeTileBounds(mSettings, TilePosition(1, 2)), (TileBounds {osg::Vec2f(32, 64), osg::Vec2f(64, 96)}));\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp",
    "content": "#include \"operators.hpp\"\n\n#include <components/detournavigator/tilecachedrecastmeshmanager.hpp>\n#include <components/detournavigator/settingsutils.hpp>\n\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n\n#include <gtest/gtest.h>\n#include <gmock/gmock.h>\n\nnamespace\n{\n    using namespace testing;\n    using namespace DetourNavigator;\n\n    struct DetourNavigatorTileCachedRecastMeshManagerTest : Test\n    {\n        Settings mSettings;\n        std::vector<TilePosition> mChangedTiles;\n\n        DetourNavigatorTileCachedRecastMeshManagerTest()\n        {\n            mSettings.mBorderSize = 16;\n            mSettings.mCellSize = 0.2f;\n            mSettings.mRecastScaleFactor = 0.017647058823529415f;\n            mSettings.mTileSize = 64;\n        }\n\n        void onChangedTile(const TilePosition& tilePosition)\n        {\n            mChangedTiles.push_back(tilePosition);\n        }\n    };\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_empty_should_return_nullptr)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        EXPECT_EQ(manager.getMesh(TilePosition(0, 0)), nullptr);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, has_tile_for_empty_should_return_false)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        EXPECT_FALSE(manager.hasTile(TilePosition(0, 0)));\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_for_empty_should_return_zero)\n    {\n        const TileCachedRecastMeshManager manager(mSettings);\n        EXPECT_EQ(manager.getRevision(), 0);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, for_each_tile_position_for_empty_should_call_none)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        std::size_t calls = 0;\n        manager.forEachTile([&] (const TilePosition&, const CachedRecastMeshManager&) { ++calls; });\n        EXPECT_EQ(calls, 0);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_new_object_should_return_true)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));\n        for (int x = -1; x < 1; ++x)\n            for (int y = -1; y < 1; ++y)\n                ASSERT_TRUE(manager.hasTile(TilePosition(x, y)));\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);\n        EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,\n                                         [&] (const auto& v) { onChangedTile(v); }));\n        EXPECT_THAT(\n            mChangedTiles,\n            ElementsAre(TilePosition(-1, -1), TilePosition(-1, 0), TilePosition(0, -1), TilePosition(0, 0),\n                        TilePosition(1, -1), TilePosition(1, 0))\n        );\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_not_changed_object_should_return_empty)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,\n                                          [&] (const auto& v) { onChangedTile(v); }));\n        EXPECT_EQ(mChangedTiles, std::vector<TilePosition>());\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_nullptr_for_unused_tile)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_recast_mesh_for_each_used_tile)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));\n        const CollisionShape shape(nullptr, boxShape);\n\n        manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);\n        EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(1, 0)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(1, -1)), nullptr);\n\n        manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});\n        EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_nullptr_for_unused_tile)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));\n        const CollisionShape shape(nullptr, boxShape);\n\n        manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);\n        EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr);\n        EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr);\n\n        manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});\n        EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr);\n        EXPECT_EQ(manager.getMesh(TilePosition(1, -1)), nullptr);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_removed_object_should_return_nullptr_for_all_previously_used_tiles)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        manager.removeObject(ObjectId(&boxShape));\n        EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr);\n        EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr);\n        EXPECT_EQ(manager.getMesh(TilePosition(0, -1)), nullptr);\n        EXPECT_EQ(manager.getMesh(TilePosition(0, 0)), nullptr);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_not_changed_object_after_update_should_return_recast_mesh_for_same_tiles)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);\n\n        manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});\n        EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);\n        EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_add_object_new_should_return_incremented_value)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const auto initialRevision = manager.getRevision();\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        EXPECT_EQ(manager.getRevision(), initialRevision + 1);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_add_object_existing_should_return_same_value)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        const auto beforeAddRevision = manager.getRevision();\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        EXPECT_EQ(manager.getRevision(), beforeAddRevision);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_update_moved_object_should_return_incremented_value)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);\n        const auto beforeUpdateRevision = manager.getRevision();\n        manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});\n        EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_update_not_changed_object_should_return_same_value)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        const auto beforeUpdateRevision = manager.getRevision();\n        manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});\n        EXPECT_EQ(manager.getRevision(), beforeUpdateRevision);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_remove_existing_object_should_return_incremented_value)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);\n        const auto beforeRemoveRevision = manager.getRevision();\n        manager.removeObject(ObjectId(&boxShape));\n        EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_remove_absent_object_should_return_same_value)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const auto beforeRemoveRevision = manager.getRevision();\n        manager.removeObject(ObjectId(&manager));\n        EXPECT_EQ(manager.getRevision(), beforeRemoveRevision);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_new_water_should_return_true)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const osg::Vec2i cellPosition(0, 0);\n        const int cellSize = 8192;\n        EXPECT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity()));\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const osg::Vec2i cellPosition(0, 0);\n        const int cellSize = 8192;\n        ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity()));\n        for (int x = -6; x < 6; ++x)\n            for (int y = -6; y < 6; ++y)\n                ASSERT_TRUE(manager.hasTile(TilePosition(x, y)));\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));\n        const osg::Vec2i cellPosition(0, 0);\n        const int cellSize = std::numeric_limits<int>::max();\n        ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity()));\n        for (int x = -6; x < 6; ++x)\n            for (int y = -6; y < 6; ++y)\n                ASSERT_EQ(manager.hasTile(TilePosition(x, y)), -1 <= x && x <= 0 && -1 <= y && y <= 0);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_return_nullopt)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        EXPECT_EQ(manager.removeWater(osg::Vec2i(0, 0)), std::nullopt);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_return_removed_water)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const osg::Vec2i cellPosition(0, 0);\n        const int cellSize = 8192;\n        ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity()));\n        const auto result = manager.removeWater(cellPosition);\n        ASSERT_TRUE(result.has_value());\n        EXPECT_EQ(result->mCellSize, cellSize);\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const osg::Vec2i cellPosition(0, 0);\n        const int cellSize = 8192;\n        ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity()));\n        ASSERT_TRUE(manager.removeWater(cellPosition));\n        for (int x = -6; x < 6; ++x)\n            for (int y = -6; y < 6; ++y)\n                ASSERT_FALSE(manager.hasTile(TilePosition(x, y)));\n    }\n\n    TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles)\n    {\n        TileCachedRecastMeshManager manager(mSettings);\n        const btBoxShape boxShape(btVector3(20, 20, 100));\n        const CollisionShape shape(nullptr, boxShape);\n        ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));\n        const osg::Vec2i cellPosition(0, 0);\n        const int cellSize = 8192;\n        ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity()));\n        ASSERT_TRUE(manager.removeWater(cellPosition));\n        for (int x = -6; x < 6; ++x)\n            for (int y = -6; y < 6; ++y)\n                ASSERT_EQ(manager.hasTile(TilePosition(x, y)), -1 <= x && x <= 0 && -1 <= y && y <= 0);\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/esm/test_fixed_string.cpp",
    "content": "#include <gtest/gtest.h>\n#include \"components/esm/esmcommon.hpp\"\n\nTEST(EsmFixedString, operator__eq_ne)\n{\n    {\n        SCOPED_TRACE(\"asdc == asdc\");\n        ESM::NAME name;\n        name.assign(\"asdc\");\n        char s[4] = {'a', 's', 'd', 'c'};\n        std::string ss(s, 4);\n\n        EXPECT_TRUE(name == s);\n        EXPECT_TRUE(name == ss.c_str());\n        EXPECT_TRUE(name == ss);\n    }\n    {\n        SCOPED_TRACE(\"asdc == asdcx\");\n        ESM::NAME name;\n        name.assign(\"asdc\");\n        char s[5] = {'a', 's', 'd', 'c', 'x'};\n        std::string ss(s, 5);\n\n        EXPECT_TRUE(name != s);\n        EXPECT_TRUE(name != ss.c_str());\n        EXPECT_TRUE(name != ss);\n    }\n    {\n        SCOPED_TRACE(\"asdc == asdc[NULL]\");\n        ESM::NAME name;\n        name.assign(\"asdc\");\n        char s[5] = {'a', 's', 'd', 'c', '\\0'};\n        std::string ss(s, 5);\n\n        EXPECT_TRUE(name == s);\n        EXPECT_TRUE(name == ss.c_str());\n        EXPECT_TRUE(name == ss);\n    }\n}\nTEST(EsmFixedString, operator__eq_ne_const)\n{\n    {\n        SCOPED_TRACE(\"asdc == asdc (const)\");\n        ESM::NAME name;\n        name.assign(\"asdc\");\n        const char s[4] = { 'a', 's', 'd', 'c' };\n        std::string ss(s, 4);\n\n        EXPECT_TRUE(name == s);\n        EXPECT_TRUE(name == ss.c_str());\n        EXPECT_TRUE(name == ss);\n    }\n    {\n        SCOPED_TRACE(\"asdc == asdcx (const)\");\n        ESM::NAME name;\n        name.assign(\"asdc\");\n        const char s[5] = { 'a', 's', 'd', 'c', 'x' };\n        std::string ss(s, 5);\n\n        EXPECT_TRUE(name != s);\n        EXPECT_TRUE(name != ss.c_str());\n        EXPECT_TRUE(name != ss);\n    }\n    {\n        SCOPED_TRACE(\"asdc == asdc[NULL] (const)\");\n        ESM::NAME name;\n        name.assign(\"asdc\");\n        const char s[5] = { 'a', 's', 'd', 'c', '\\0' };\n        std::string ss(s, 5);\n\n        EXPECT_TRUE(name == s);\n        EXPECT_TRUE(name == ss.c_str());\n        EXPECT_TRUE(name == ss);\n    }\n}\n\nTEST(EsmFixedString, empty_strings)\n{\n    {\n        SCOPED_TRACE(\"4 bytes\");\n        ESM::NAME empty = ESM::NAME();\n        EXPECT_TRUE(empty == \"\");\n        EXPECT_TRUE(empty == static_cast<uint32_t>(0));\n        EXPECT_TRUE(empty != \"some string\");\n        EXPECT_TRUE(empty != static_cast<uint32_t>(42));\n    }\n    {\n        SCOPED_TRACE(\"32 bytes\");\n        ESM::NAME32 empty = ESM::NAME32();\n        EXPECT_TRUE(empty == \"\");\n        EXPECT_TRUE(empty != \"some string\");\n    }\n}\n\nTEST(EsmFixedString, struct_size)\n{\n    ASSERT_EQ(4, sizeof(ESM::NAME));\n    ASSERT_EQ(32, sizeof(ESM::NAME32));\n    ASSERT_EQ(64, sizeof(ESM::NAME64));\n}\n\nTEST(EsmFixedString, is_pod)\n{\n     ASSERT_TRUE(std::is_pod<ESM::NAME>::value);\n     ASSERT_TRUE(std::is_pod<ESM::NAME32>::value);\n     ASSERT_TRUE(std::is_pod<ESM::NAME64>::value);\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/esm/variant.cpp",
    "content": "#include <components/esm/variant.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/loadglob.hpp>\n\n#include <gtest/gtest.h>\n#include <gmock/gmock.h>\n\n#include <functional>\n\nnamespace\n{\n    using namespace testing;\n    using namespace ESM;\n\n    Variant makeVariant(VarType type)\n    {\n        Variant v;\n        v.setType(type);\n        return v;\n    }\n\n    Variant makeVariant(VarType type, int value)\n    {\n        Variant v;\n        v.setType(type);\n        v.setInteger(value);\n        return v;\n    }\n\n    TEST(ESMVariantTest, move_constructed_should_have_data)\n    {\n        Variant a(int{42});\n        const Variant b(std::move(a));\n        ASSERT_EQ(b.getInteger(), 42);\n    }\n\n    TEST(ESMVariantTest, copy_constructed_is_equal_to_source)\n    {\n        const Variant a(int{42});\n        const Variant b(a);\n        ASSERT_EQ(a, b);\n    }\n\n    TEST(ESMVariantTest, copy_constructed_does_not_share_data_with_source)\n    {\n        const Variant a(int{42});\n        Variant b(a);\n        b.setInteger(13);\n        ASSERT_EQ(a.getInteger(), 42);\n        ASSERT_EQ(b.getInteger(), 13);\n    }\n\n    TEST(ESMVariantTest, move_assigned_should_have_data)\n    {\n        Variant b;\n        {\n            Variant a(int{42});\n            b = std::move(a);\n        }\n        ASSERT_EQ(b.getInteger(), 42);\n    }\n\n    TEST(ESMVariantTest, copy_assigned_is_equal_to_source)\n    {\n        const Variant a(int{42});\n        Variant b;\n        b = a;\n        ASSERT_EQ(a, b);\n    }\n\n    TEST(ESMVariantTest, not_equal_is_negation_of_equal)\n    {\n        const Variant a(int{42});\n        Variant b;\n        b = a;\n        ASSERT_TRUE(!(a != b));\n    }\n\n    TEST(ESMVariantTest, different_types_are_not_equal)\n    {\n        ASSERT_NE(Variant(int{42}), Variant(float{2.7f}));\n    }\n\n    struct ESMVariantWriteToOStreamTest : TestWithParam<std::tuple<Variant, std::string>> {};\n\n    TEST_P(ESMVariantWriteToOStreamTest, should_write)\n    {\n        const auto [variant, result] = GetParam();\n        std::ostringstream s;\n        s << variant;\n        ASSERT_EQ(s.str(), result);\n    }\n\n    INSTANTIATE_TEST_SUITE_P(VariantAsString, ESMVariantWriteToOStreamTest, Values(\n        std::make_tuple(Variant(), \"variant none\"),\n        std::make_tuple(Variant(int{42}), \"variant long: 42\"),\n        std::make_tuple(Variant(float{2.7f}), \"variant float: 2.7\"),\n        std::make_tuple(Variant(std::string(\"foo\")), \"variant string: \\\"foo\\\"\"),\n        std::make_tuple(makeVariant(VT_Unknown), \"variant unknown\"),\n        std::make_tuple(makeVariant(VT_Short, 42), \"variant short: 42\"),\n        std::make_tuple(makeVariant(VT_Int, 42), \"variant int: 42\")\n    ));\n\n    struct ESMVariantGetTypeTest : Test {};\n\n    TEST(ESMVariantGetTypeTest, default_constructed_should_return_none)\n    {\n        ASSERT_EQ(Variant().getType(), VT_None);\n    }\n\n    TEST(ESMVariantGetTypeTest, for_constructed_from_int_should_return_long)\n    {\n        ASSERT_EQ(Variant(int{}).getType(), VT_Long);\n    }\n\n    TEST(ESMVariantGetTypeTest, for_constructed_from_float_should_return_float)\n    {\n        ASSERT_EQ(Variant(float{}).getType(), VT_Float);\n    }\n\n    TEST(ESMVariantGetTypeTest, for_constructed_from_lvalue_string_should_return_string)\n    {\n        const std::string string;\n        ASSERT_EQ(Variant(string).getType(), VT_String);\n    }\n\n    TEST(ESMVariantGetTypeTest, for_constructed_from_rvalue_string_should_return_string)\n    {\n        ASSERT_EQ(Variant(std::string{}).getType(), VT_String);\n    }\n\n    struct ESMVariantGetIntegerTest : Test {};\n\n    TEST(ESMVariantGetIntegerTest, for_default_constructed_should_throw_exception)\n    {\n        ASSERT_THROW(Variant().getInteger(), std::runtime_error);\n    }\n\n    TEST(ESMVariantGetIntegerTest, for_constructed_from_int_should_return_same_value)\n    {\n        const Variant variant(int{42});\n        ASSERT_EQ(variant.getInteger(), 42);\n    }\n\n    TEST(ESMVariantGetIntegerTest, for_constructed_from_float_should_return_casted_to_int)\n    {\n        const Variant variant(float{2.7});\n        ASSERT_EQ(variant.getInteger(), 2);\n    }\n\n    TEST(ESMVariantGetIntegerTest, for_constructed_from_string_should_throw_exception)\n    {\n        const Variant variant(std::string(\"foo\"));\n        ASSERT_THROW(variant.getInteger(), std::runtime_error);\n    }\n\n    TEST(ESMVariantGetFloatTest, for_default_constructed_should_throw_exception)\n    {\n        ASSERT_THROW(Variant().getFloat(), std::runtime_error);\n    }\n\n    TEST(ESMVariantGetFloatTest, for_constructed_from_int_should_return_casted_to_float)\n    {\n        const Variant variant(int{42});\n        ASSERT_EQ(variant.getFloat(), 42);\n    }\n\n    TEST(ESMVariantGetFloatTest, for_constructed_from_float_should_return_same_value)\n    {\n        const Variant variant(float{2.7f});\n        ASSERT_EQ(variant.getFloat(), 2.7f);\n    }\n\n    TEST(ESMVariantGetFloatTest, for_constructed_from_string_should_throw_exception)\n    {\n        const Variant variant(std::string(\"foo\"));\n        ASSERT_THROW(variant.getFloat(), std::runtime_error);\n    }\n\n    TEST(ESMVariantGetStringTest, for_default_constructed_should_throw_exception)\n    {\n        ASSERT_THROW(Variant().getString(), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantGetStringTest, for_constructed_from_int_should_throw_exception)\n    {\n        const Variant variant(int{42});\n        ASSERT_THROW(variant.getString(), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantGetStringTest, for_constructed_from_float_should_throw_exception)\n    {\n        const Variant variant(float{2.7});\n        ASSERT_THROW(variant.getString(), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantGetStringTest, for_constructed_from_string_should_return_same_value)\n    {\n        const Variant variant(std::string(\"foo\"));\n        ASSERT_EQ(variant.getString(), \"foo\");\n    }\n\n    TEST(ESMVariantSetTypeTest, for_unknown_should_reset_data)\n    {\n        Variant variant(int{42});\n        variant.setType(VT_Unknown);\n        ASSERT_THROW(variant.getInteger(), std::runtime_error);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_none_should_reset_data)\n    {\n        Variant variant(int{42});\n        variant.setType(VT_None);\n        ASSERT_THROW(variant.getInteger(), std::runtime_error);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_same_type_should_not_change_value)\n    {\n        Variant variant(int{42});\n        variant.setType(VT_Long);\n        ASSERT_EQ(variant.getInteger(), 42);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_float_replaced_by_int_should_cast_float_to_int)\n    {\n        Variant variant(float{2.7f});\n        variant.setType(VT_Int);\n        ASSERT_EQ(variant.getInteger(), 2);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_string_replaced_by_int_should_set_default_initialized_data)\n    {\n        Variant variant(std::string(\"foo\"));\n        variant.setType(VT_Int);\n        ASSERT_EQ(variant.getInteger(), 0);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_default_constructed_replaced_by_float_should_set_default_initialized_value)\n    {\n        Variant variant;\n        variant.setType(VT_Float);\n        ASSERT_EQ(variant.getInteger(), 0.0f);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_float_replaced_by_short_should_cast_data_to_int)\n    {\n        Variant variant(float{2.7f});\n        variant.setType(VT_Short);\n        ASSERT_EQ(variant.getInteger(), 2);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_float_replaced_by_long_should_cast_data_to_int)\n    {\n        Variant variant(float{2.7f});\n        variant.setType(VT_Long);\n        ASSERT_EQ(variant.getInteger(), 2);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_int_replaced_by_float_should_cast_data_to_float)\n    {\n        Variant variant(int{42});\n        variant.setType(VT_Float);\n        ASSERT_EQ(variant.getFloat(), 42.0f);\n    }\n\n    TEST(ESMVariantSetTypeTest, for_int_replaced_by_string_should_set_default_initialized_data)\n    {\n        Variant variant(int{42});\n        variant.setType(VT_String);\n        ASSERT_EQ(variant.getString(), \"\");\n    }\n\n    TEST(ESMVariantSetIntegerTest, for_default_constructed_should_throw_exception)\n    {\n        Variant variant;\n        ASSERT_THROW(variant.setInteger(42), std::runtime_error);\n    }\n\n    TEST(ESMVariantSetIntegerTest, for_unknown_should_throw_exception)\n    {\n        Variant variant;\n        variant.setType(VT_Unknown);\n        ASSERT_THROW(variant.setInteger(42), std::runtime_error);\n    }\n\n    TEST(ESMVariantSetIntegerTest, for_default_int_should_change_value)\n    {\n        Variant variant(int{13});\n        variant.setInteger(42);\n        ASSERT_EQ(variant.getInteger(), 42);\n    }\n\n    TEST(ESMVariantSetIntegerTest, for_int_should_change_value)\n    {\n        Variant variant;\n        variant.setType(VT_Int);\n        variant.setInteger(42);\n        ASSERT_EQ(variant.getInteger(), 42);\n    }\n\n    TEST(ESMVariantSetIntegerTest, for_short_should_change_value)\n    {\n        Variant variant;\n        variant.setType(VT_Short);\n        variant.setInteger(42);\n        ASSERT_EQ(variant.getInteger(), 42);\n    }\n\n    TEST(ESMVariantSetIntegerTest, for_float_should_change_value)\n    {\n        Variant variant(float{2.7f});\n        variant.setInteger(42);\n        ASSERT_EQ(variant.getFloat(), 42.0f);\n    }\n\n    TEST(ESMVariantSetIntegerTest, for_string_should_throw_exception)\n    {\n        Variant variant(std::string{});\n        ASSERT_THROW(variant.setInteger(42), std::runtime_error);\n    }\n\n    TEST(ESMVariantSetFloatTest, for_default_constructed_should_throw_exception)\n    {\n        Variant variant;\n        ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error);\n    }\n\n    TEST(ESMVariantSetFloatTest, for_unknown_should_throw_exception)\n    {\n        Variant variant;\n        variant.setType(VT_Unknown);\n        ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error);\n    }\n\n    TEST(ESMVariantSetFloatTest, for_default_int_should_change_value)\n    {\n        Variant variant(int{13});\n        variant.setFloat(2.7f);\n        ASSERT_EQ(variant.getInteger(), 2);\n    }\n\n    TEST(ESMVariantSetFloatTest, for_int_should_change_value)\n    {\n        Variant variant;\n        variant.setType(VT_Int);\n        variant.setFloat(2.7f);\n        ASSERT_EQ(variant.getInteger(), 2);\n    }\n\n    TEST(ESMVariantSetFloatTest, for_short_should_change_value)\n    {\n        Variant variant;\n        variant.setType(VT_Short);\n        variant.setFloat(2.7f);\n        ASSERT_EQ(variant.getInteger(), 2);\n    }\n\n    TEST(ESMVariantSetFloatTest, for_float_should_change_value)\n    {\n        Variant variant(float{2.7f});\n        variant.setFloat(3.14f);\n        ASSERT_EQ(variant.getFloat(), 3.14f);\n    }\n\n    TEST(ESMVariantSetFloatTest, for_string_should_throw_exception)\n    {\n        Variant variant(std::string{});\n        ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error);\n    }\n\n    TEST(ESMVariantSetStringTest, for_default_constructed_should_throw_exception)\n    {\n        Variant variant;\n        ASSERT_THROW(variant.setString(\"foo\"), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantSetStringTest, for_unknown_should_throw_exception)\n    {\n        Variant variant;\n        variant.setType(VT_Unknown);\n        ASSERT_THROW(variant.setString(\"foo\"), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantSetStringTest, for_default_int_should_throw_exception)\n    {\n        Variant variant(int{13});\n        ASSERT_THROW(variant.setString(\"foo\"), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantSetStringTest, for_int_should_throw_exception)\n    {\n        Variant variant;\n        variant.setType(VT_Int);\n        ASSERT_THROW(variant.setString(\"foo\"), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantSetStringTest, for_short_should_throw_exception)\n    {\n        Variant variant;\n        variant.setType(VT_Short);\n        ASSERT_THROW(variant.setString(\"foo\"), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantSetStringTest, for_float_should_throw_exception)\n    {\n        Variant variant(float{2.7f});\n        ASSERT_THROW(variant.setString(\"foo\"), std::bad_variant_access);\n    }\n\n    TEST(ESMVariantSetStringTest, for_string_should_change_value)\n    {\n        Variant variant(std::string(\"foo\"));\n        variant.setString(\"bar\");\n        ASSERT_EQ(variant.getString(), \"bar\");\n    }\n\n    struct WriteToESMTestCase\n    {\n        Variant mVariant;\n        Variant::Format mFormat;\n        std::size_t mDataSize {};\n    };\n\n    std::string write(const Variant& variant, const Variant::Format format)\n    {\n        std::ostringstream out;\n        ESM::ESMWriter writer;\n        writer.save(out);\n        variant.write(writer, format);\n        writer.close();\n        return out.str();\n    }\n\n    Variant read(const Variant::Format format, const std::string& data)\n    {\n        Variant result;\n        ESM::ESMReader reader;\n        reader.open(std::make_shared<std::istringstream>(data), \"\");\n        result.read(reader, format);\n        return result;\n    }\n\n    Variant writeAndRead(const Variant& variant, const Variant::Format format, std::size_t dataSize)\n    {\n        const std::string data = write(variant, format);\n        EXPECT_EQ(data.size(), dataSize);\n        return read(format, data);\n    }\n\n    struct ESMVariantToESMTest : TestWithParam<WriteToESMTestCase> {};\n\n    TEST_P(ESMVariantToESMTest, deserialized_is_equal_to_serialized)\n    {\n        const auto param = GetParam();\n        const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize);\n        ASSERT_EQ(param.mVariant, result);\n    }\n\n    INSTANTIATE_TEST_SUITE_P(VariantAndData, ESMVariantToESMTest, Values(\n        WriteToESMTestCase {Variant(), Variant::Format_Gmst, 324},\n        WriteToESMTestCase {Variant(int{42}), Variant::Format_Global, 345},\n        WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Global, 345},\n        WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Info, 336},\n        WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Local, 336},\n        WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Global, 345},\n        WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Local, 334},\n        WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Info, 336},\n        WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Local, 336}\n    ));\n\n    struct ESMVariantToESMNoneTest : TestWithParam<WriteToESMTestCase> {};\n\n    TEST_P(ESMVariantToESMNoneTest, deserialized_is_none)\n    {\n        const auto param = GetParam();\n        const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize);\n        ASSERT_EQ(Variant(), result);\n    }\n\n    INSTANTIATE_TEST_SUITE_P(VariantAndData, ESMVariantToESMNoneTest, Values(\n        WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Gmst, 336},\n        WriteToESMTestCase {Variant(std::string(\"foo\")), Variant::Format_Gmst, 335},\n        WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Gmst, 336}\n    ));\n\n    struct ESMVariantWriteToESMFailTest : TestWithParam<WriteToESMTestCase> {};\n\n    TEST_P(ESMVariantWriteToESMFailTest, write_is_not_supported)\n    {\n        const auto param = GetParam();\n        std::ostringstream out;\n        ESM::ESMWriter writer;\n        writer.save(out);\n        ASSERT_THROW(param.mVariant.write(writer, param.mFormat), std::runtime_error);\n    }\n\n    INSTANTIATE_TEST_SUITE_P(VariantAndFormat, ESMVariantWriteToESMFailTest, Values(\n        WriteToESMTestCase {Variant(), Variant::Format_Global},\n        WriteToESMTestCase {Variant(), Variant::Format_Info},\n        WriteToESMTestCase {Variant(), Variant::Format_Local},\n        WriteToESMTestCase {Variant(int{42}), Variant::Format_Gmst},\n        WriteToESMTestCase {Variant(int{42}), Variant::Format_Info},\n        WriteToESMTestCase {Variant(int{42}), Variant::Format_Local},\n        WriteToESMTestCase {Variant(std::string(\"foo\")), Variant::Format_Global},\n        WriteToESMTestCase {Variant(std::string(\"foo\")), Variant::Format_Info},\n        WriteToESMTestCase {Variant(std::string(\"foo\")), Variant::Format_Local},\n        WriteToESMTestCase {makeVariant(VT_Unknown), Variant::Format_Global},\n        WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Global},\n        WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Gmst},\n        WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Info}\n    ));\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/misc/test_endianness.cpp",
    "content": "#include <gtest/gtest.h>\n#include \"components/misc/endianness.hpp\"\n\nstruct EndiannessTest : public ::testing::Test {};\n\nTEST_F(EndiannessTest, test_swap_endianness_inplace1)\n{\n  uint8_t zero=0x00;\n  uint8_t ff=0xFF;\n  uint8_t fortytwo=0x42;\n  uint8_t half=128;\n\n  Misc::swapEndiannessInplace(zero);\n  EXPECT_EQ(zero, 0x00);\n\n  Misc::swapEndiannessInplace(ff);\n  EXPECT_EQ(ff, 0xFF);\n\n  Misc::swapEndiannessInplace(fortytwo);\n  EXPECT_EQ(fortytwo, 0x42);\n\n  Misc::swapEndiannessInplace(half);\n  EXPECT_EQ(half, 128);\n}\n\nTEST_F(EndiannessTest, test_swap_endianness_inplace2)\n{\n  uint16_t zero = 0x0000;\n  uint16_t ffff = 0xFFFF;\n  uint16_t n12 = 0x0102;\n  uint16_t fortytwo = 0x0042;\n\n  Misc::swapEndiannessInplace(zero);\n  EXPECT_EQ(zero, 0x0000);\n  Misc::swapEndiannessInplace(zero);\n  EXPECT_EQ(zero, 0x0000);\n\n  Misc::swapEndiannessInplace(ffff);\n  EXPECT_EQ(ffff, 0xFFFF);\n  Misc::swapEndiannessInplace(ffff);\n  EXPECT_EQ(ffff, 0xFFFF);\n\n  Misc::swapEndiannessInplace(n12);\n  EXPECT_EQ(n12, 0x0201);\n  Misc::swapEndiannessInplace(n12);\n  EXPECT_EQ(n12, 0x0102);\n\n  Misc::swapEndiannessInplace(fortytwo);\n  EXPECT_EQ(fortytwo, 0x4200);\n  Misc::swapEndiannessInplace(fortytwo);\n  EXPECT_EQ(fortytwo, 0x0042);\n}\n\nTEST_F(EndiannessTest, test_swap_endianness_inplace4)\n{\n  uint32_t zero = 0x00000000;\n  uint32_t n1234 = 0x01020304;\n  uint32_t ffff = 0xFFFFFFFF;\n\n  Misc::swapEndiannessInplace(zero);\n  EXPECT_EQ(zero, 0x00000000);\n  Misc::swapEndiannessInplace(zero);\n  EXPECT_EQ(zero, 0x00000000);\n\n  Misc::swapEndiannessInplace(n1234);\n  EXPECT_EQ(n1234, 0x04030201);\n  Misc::swapEndiannessInplace(n1234);\n  EXPECT_EQ(n1234, 0x01020304);\n  \n  Misc::swapEndiannessInplace(ffff);\n  EXPECT_EQ(ffff, 0xFFFFFFFF);\n  Misc::swapEndiannessInplace(ffff);\n  EXPECT_EQ(ffff, 0xFFFFFFFF);\n}\n\nTEST_F(EndiannessTest, test_swap_endianness_inplace8)\n{\n  uint64_t zero = 0x0000'0000'0000'0000;\n  uint64_t n1234 = 0x0102'0304'0506'0708;\n  uint64_t ffff = 0xFFFF'FFFF'FFFF'FFFF;\n\n  Misc::swapEndiannessInplace(zero);\n  EXPECT_EQ(zero, 0x0000'0000'0000'0000);\n  Misc::swapEndiannessInplace(zero);\n  EXPECT_EQ(zero, 0x0000'0000'0000'0000);\n  \n  Misc::swapEndiannessInplace(ffff);\n  EXPECT_EQ(ffff, 0xFFFF'FFFF'FFFF'FFFF);\n  Misc::swapEndiannessInplace(ffff);\n  EXPECT_EQ(ffff, 0xFFFF'FFFF'FFFF'FFFF);\n\n  Misc::swapEndiannessInplace(n1234);\n  EXPECT_EQ(n1234, 0x0807'0605'0403'0201);\n  Misc::swapEndiannessInplace(n1234);\n  EXPECT_EQ(n1234, 0x0102'0304'0506'0708);\n}\n\nTEST_F(EndiannessTest, test_swap_endianness_inplace_float)\n{\n  const uint32_t original = 0x4023d70a;\n  const uint32_t expected = 0x0ad72340;\n\n  float number;\n  memcpy(&number, &original, sizeof(original));\n\n  Misc::swapEndiannessInplace(number);\n\n  EXPECT_TRUE(!memcmp(&number, &expected, sizeof(expected)));\n}\n\nTEST_F(EndiannessTest, test_swap_endianness_inplace_double)\n{\n  const uint64_t original = 0x040047ae147ae147ul;\n  const uint64_t expected = 0x47e17a14ae470004ul;\n\n  double number;\n  memcpy(&number, &original, sizeof(original));\n\n  Misc::swapEndiannessInplace(number);\n\n  EXPECT_TRUE(!memcmp(&number, &expected, sizeof(expected)) );\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/misc/test_stringops.cpp",
    "content": "#include <gtest/gtest.h>\n#include \"components/misc/stringops.hpp\"\n\nstruct PartialBinarySearchTest : public ::testing::Test\n{\n  protected:\n    std::vector<std::string> mDataVec;\n    void SetUp() override\n    {\n        const char* data[] = { \"Head\", \"Chest\", \"Tri Head\", \"Tri Chest\", \"Bip01\", \"Tri Bip01\" };\n        mDataVec = std::vector<std::string>(data, data+sizeof(data)/sizeof(data[0]));\n        std::sort(mDataVec.begin(), mDataVec.end(), Misc::StringUtils::ciLess);\n    }\n\n    void TearDown() override\n    {\n    }\n\n    bool matches(const std::string& keyword)\n    {\n        return Misc::StringUtils::partialBinarySearch(mDataVec.begin(), mDataVec.end(), keyword) != mDataVec.end();\n    }\n};\n\nTEST_F(PartialBinarySearchTest, partial_binary_search_test)\n{\n    EXPECT_TRUE( matches(\"Head 01\") );\n    EXPECT_TRUE( matches(\"Head\") );\n    EXPECT_TRUE( matches(\"Tri Head 01\") );\n    EXPECT_TRUE( matches(\"Tri Head\") );\n    EXPECT_TRUE( matches(\"tri head\") );\n    EXPECT_TRUE( matches(\"Tri bip01\") );\n    EXPECT_TRUE( matches(\"bip01\") );\n    EXPECT_TRUE( matches(\"bip01 head\") );\n    EXPECT_TRUE( matches(\"Bip01 L Hand\") );\n    EXPECT_TRUE( matches(\"BIp01 r Clavicle\") );\n    EXPECT_TRUE( matches(\"Bip01 SpiNe1\") );\n\n    EXPECT_FALSE( matches(\"\") );\n    EXPECT_FALSE( matches(\"Node Bip01\") );\n    EXPECT_FALSE( matches(\"Hea\") );\n    EXPECT_FALSE( matches(\" Head\") );\n    EXPECT_FALSE( matches(\"Tri  Head\") );\n}\n\nTEST_F (PartialBinarySearchTest, ci_test)\n{\n    EXPECT_TRUE (Misc::StringUtils::lowerCase(\"ASD\") == \"asd\");\n\n    // test to make sure system locale is not used\n    std::string unicode1 = \"\\u04151 \\u0418\"; // CYRILLIC CAPITAL LETTER IE, CYRILLIC CAPITAL LETTER I\n    EXPECT_TRUE( Misc::StringUtils::lowerCase(unicode1) == unicode1 );\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp",
    "content": "#include <gtest/gtest.h>\n#include \"apps/openmw/mwdialogue/keywordsearch.hpp\"\n\nstruct KeywordSearchTest : public ::testing::Test\n{\n  protected:\n    void SetUp() override\n    {\n    }\n\n    void TearDown() override\n    {\n    }\n};\n\nTEST_F(KeywordSearchTest, keyword_test_conflict_resolution)\n{\n    // test to make sure the longest keyword in a chain of conflicting keywords gets chosen\n    MWDialogue::KeywordSearch<std::string, int> search;\n    search.seed(\"foo bar\", 0);\n    search.seed(\"bar lock\", 0);\n    search.seed(\"lock switch\", 0);\n\n    std::string text = \"foo bar lock switch\";\n\n    std::vector<MWDialogue::KeywordSearch<std::string, int>::Match> matches;\n    search.highlightKeywords(text.begin(), text.end(), matches);\n\n    // Should contain: \"foo bar\", \"lock switch\"\n    ASSERT_TRUE (matches.size() == 2);\n    ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == \"foo bar\");\n    ASSERT_TRUE (std::string(matches.rbegin()->mBeg, matches.rbegin()->mEnd) == \"lock switch\");\n}\n\nTEST_F(KeywordSearchTest, keyword_test_conflict_resolution2)\n{\n    MWDialogue::KeywordSearch<std::string, int> search;\n    search.seed(\"the dwemer\", 0);\n    search.seed(\"dwemer language\", 0);\n\n    std::string text = \"the dwemer language\";\n\n    std::vector<MWDialogue::KeywordSearch<std::string, int>::Match> matches;\n    search.highlightKeywords(text.begin(), text.end(), matches);\n\n    ASSERT_TRUE (matches.size() == 1);\n    ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == \"dwemer language\");\n}\n\n\nTEST_F(KeywordSearchTest, keyword_test_conflict_resolution3)\n{\n    // testing that the longest keyword is chosen, rather than maximizing the\n    // amount of highlighted characters by highlighting the first and last keyword\n    MWDialogue::KeywordSearch<std::string, int> search;\n    search.seed(\"foo bar\", 0);\n    search.seed(\"bar lock\", 0);\n    search.seed(\"lock so\", 0);\n\n    std::string text = \"foo bar lock so\";\n\n    std::vector<MWDialogue::KeywordSearch<std::string, int>::Match> matches;\n    search.highlightKeywords(text.begin(), text.end(), matches);\n\n    ASSERT_TRUE (matches.size() == 1);\n    ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == \"bar lock\");\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/mwworld/test_store.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <boost/filesystem/fstream.hpp>\n\n#include <components/files/configurationmanager.hpp>\n#include <components/files/escape.hpp>\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n#include <components/loadinglistener/loadinglistener.hpp>\n\n#include \"apps/openmw/mwworld/esmstore.hpp\"\n#include \"apps/openmw/mwmechanics/spelllist.hpp\"\n\nnamespace MWMechanics\n{\n    SpellList::SpellList(const std::string& id, int type) : mId(id), mType(type) {}\n}\n\nstatic Loading::Listener dummyListener;\n\n/// Base class for tests of ESMStore that rely on external content files to produce the test results\nstruct ContentFileTest : public ::testing::Test\n{\n  protected:\n\n    void SetUp() override\n    {\n        readContentFiles();\n\n        // load the content files\n        std::vector<ESM::ESMReader> readerList;\n        readerList.resize(mContentFiles.size());\n\n        int index=0;\n        for (const auto & mContentFile : mContentFiles)\n        {\n            ESM::ESMReader lEsm;\n            lEsm.setEncoder(nullptr);\n            lEsm.setIndex(index);\n            lEsm.setGlobalReaderList(&readerList);\n            lEsm.open(mContentFile.string());\n            readerList[index] = lEsm;\n            mEsmStore.load(readerList[index], &dummyListener);\n\n            ++index;\n        }\n\n        mEsmStore.setUp();\n    }\n\n    void TearDown() override\n    {\n    }\n\n    // read absolute path to content files from openmw.cfg\n    void readContentFiles()\n    {\n        boost::program_options::variables_map variables;\n\n        boost::program_options::options_description desc(\"Allowed options\");\n        desc.add_options()\n        (\"data\", boost::program_options::value<Files::EscapePathContainer>()->default_value(Files::EscapePathContainer(), \"data\")->multitoken()->composing())\n        (\"content\", boost::program_options::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), \"\")\n            ->multitoken()->composing(), \"content file(s): esm/esp, or omwgame/omwaddon\")\n        (\"data-local\", boost::program_options::value<Files::EscapePath>()->default_value(Files::EscapePath(), \"\"));\n\n        boost::program_options::notify(variables);\n\n        mConfigurationManager.readConfiguration(variables, desc, true);\n\n        Files::PathContainer dataDirs, dataLocal;\n        if (!variables[\"data\"].empty()) {\n            dataDirs = Files::EscapePath::toPathContainer(variables[\"data\"].as<Files::EscapePathContainer>());\n        }\n\n        Files::PathContainer::value_type local(variables[\"data-local\"].as<Files::EscapePath>().mPath);\n        if (!local.empty()) {\n            dataLocal.push_back(local);\n        }\n\n        mConfigurationManager.processPaths (dataDirs);\n        mConfigurationManager.processPaths (dataLocal, true);\n\n        if (!dataLocal.empty())\n            dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());\n\n        Files::Collections collections (dataDirs, true);\n\n        std::vector<std::string> contentFiles = variables[\"content\"].as<Files::EscapeStringVector>().toStdStringVector();\n        for (auto & contentFile : contentFiles)\n            mContentFiles.push_back(collections.getPath(contentFile));\n    }\n\nprotected:\n    Files::ConfigurationManager mConfigurationManager;\n    MWWorld::ESMStore mEsmStore;\n    std::vector<boost::filesystem::path> mContentFiles;\n};\n\n/// Print results of the dialogue merging process, i.e. the resulting linked list.\nTEST_F(ContentFileTest, dialogue_merging_test)\n{\n    if (mContentFiles.empty())\n    {\n        std::cout << \"No content files found, skipping test\" << std::endl;\n        return;\n    }\n\n    const std::string file = \"test_dialogue_merging.txt\";\n\n    boost::filesystem::ofstream stream;\n    stream.open(file);\n\n    const MWWorld::Store<ESM::Dialogue>& dialStore = mEsmStore.get<ESM::Dialogue>();\n    for (const auto & dial : dialStore)\n    {\n        stream << \"Dialogue: \" << dial.mId << std::endl;\n\n        for (const auto & info : dial.mInfo)\n        {\n            stream << info.mId << std::endl;\n        }\n        stream << std::endl;\n    }\n\n    std::cout << \"dialogue_merging_test successful, results printed to \" << file << std::endl;\n}\n\n// Note: here we don't test records that don't use string names (e.g. Land, Pathgrid, Cell)\n#define RUN_TEST_FOR_TYPES(func, arg1, arg2) \\\n    func<ESM::Activator>(arg1, arg2); \\\n    func<ESM::Apparatus>(arg1, arg2); \\\n    func<ESM::Armor>(arg1, arg2); \\\n    func<ESM::BirthSign>(arg1, arg2); \\\n    func<ESM::BodyPart>(arg1, arg2); \\\n    func<ESM::Book>(arg1, arg2); \\\n    func<ESM::Class>(arg1, arg2); \\\n    func<ESM::Clothing>(arg1, arg2); \\\n    func<ESM::Container>(arg1, arg2); \\\n    func<ESM::Creature>(arg1, arg2); \\\n    func<ESM::CreatureLevList>(arg1, arg2); \\\n    func<ESM::Dialogue>(arg1, arg2); \\\n    func<ESM::Door>(arg1, arg2); \\\n    func<ESM::Enchantment>(arg1, arg2); \\\n    func<ESM::Faction>(arg1, arg2); \\\n    func<ESM::GameSetting>(arg1, arg2); \\\n    func<ESM::Global>(arg1, arg2); \\\n    func<ESM::Ingredient>(arg1, arg2); \\\n    func<ESM::ItemLevList>(arg1, arg2); \\\n    func<ESM::Light>(arg1, arg2); \\\n    func<ESM::Lockpick>(arg1, arg2); \\\n    func<ESM::Miscellaneous>(arg1, arg2); \\\n    func<ESM::NPC>(arg1, arg2); \\\n    func<ESM::Potion>(arg1, arg2); \\\n    func<ESM::Probe>(arg1, arg2); \\\n    func<ESM::Race>(arg1, arg2); \\\n    func<ESM::Region>(arg1, arg2); \\\n    func<ESM::Repair>(arg1, arg2); \\\n    func<ESM::Script>(arg1, arg2); \\\n    func<ESM::Sound>(arg1, arg2); \\\n    func<ESM::SoundGenerator>(arg1, arg2); \\\n    func<ESM::Spell>(arg1, arg2); \\\n    func<ESM::StartScript>(arg1, arg2); \\\n    func<ESM::Weapon>(arg1, arg2);\n\ntemplate <typename T>\nvoid printRecords(MWWorld::ESMStore& esmStore, std::ostream& outStream)\n{\n    const MWWorld::Store<T>& store = esmStore.get<T>();\n    outStream << store.getSize() << \" \" << T::getRecordType() << \" records\" << std::endl;\n\n    for (typename MWWorld::Store<T>::iterator it = store.begin(); it != store.end(); ++it)\n    {\n        const T& record = *it;\n        outStream << record.mId << std::endl;\n    }\n\n    outStream << std::endl;\n}\n\n/// Print some basic diagnostics about the loaded content files, e.g. number of records and names of those records\n/// Also used to test the iteration order of records\nTEST_F(ContentFileTest, content_diagnostics_test)\n{\n    if (mContentFiles.empty())\n    {\n        std::cout << \"No content files found, skipping test\" << std::endl;\n        return;\n    }\n\n    const std::string file = \"test_content_diagnostics.txt\";\n\n    boost::filesystem::ofstream stream;\n    stream.open(file);\n\n    RUN_TEST_FOR_TYPES(printRecords, mEsmStore, stream);\n\n    std::cout << \"diagnostics_test successful, results printed to \" << file << std::endl;\n}\n\n// TODO:\n/// Print results of autocalculated NPC spell lists. Also serves as test for attribute/skill autocalculation which the spell autocalculation heavily relies on\n/// - even incorrect rounding modes can completely change the resulting spell lists.\n/*\nTEST_F(ContentFileTest, autocalc_test)\n{\n    if (mContentFiles.empty())\n    {\n        std::cout << \"No content files found, skipping test\" << std::endl;\n        return;\n    }\n\n\n}\n*/\n\n/// Base class for tests of ESMStore that do not rely on external content files\nstruct StoreTest : public ::testing::Test\n{\nprotected:\n    MWWorld::ESMStore mEsmStore;\n};\n\n\n/// Create an ESM file in-memory containing the specified record.\n/// @param deleted Write record with deleted flag?\ntemplate <typename T>\nFiles::IStreamPtr getEsmFile(T record, bool deleted)\n{\n    ESM::ESMWriter writer;\n    auto* stream = new std::stringstream;\n    writer.setFormat(0);\n    writer.save(*stream);\n    writer.startRecord(T::sRecordId);\n    record.save(writer, deleted);\n    writer.endRecord(T::sRecordId);\n\n    return Files::IStreamPtr(stream);\n}\n\n/// Tests deletion of records.\nTEST_F(StoreTest, delete_test)\n{\n    const std::string recordId = \"foobar\";\n\n    typedef ESM::Apparatus RecordType;\n\n    RecordType record;\n    record.blank();\n    record.mId = recordId;\n\n    ESM::ESMReader reader;\n    std::vector<ESM::ESMReader> readerList;\n    readerList.push_back(reader);\n    reader.setGlobalReaderList(&readerList);\n\n    // master file inserts a record\n    Files::IStreamPtr file = getEsmFile(record, false);\n    reader.open(file, \"filename\");\n    mEsmStore.load(reader, &dummyListener);\n    mEsmStore.setUp();\n\n    ASSERT_TRUE (mEsmStore.get<RecordType>().getSize() == 1);\n\n    // now a plugin deletes it\n    file = getEsmFile(record, true);\n    reader.open(file, \"filename\");\n    mEsmStore.load(reader, &dummyListener);\n    mEsmStore.setUp();\n\n    ASSERT_TRUE (mEsmStore.get<RecordType>().getSize() == 0);\n\n    // now another plugin inserts it again\n    // expected behaviour is the record to reappear rather than staying deleted\n    file = getEsmFile(record, false);\n    reader.open(file, \"filename\");\n    mEsmStore.load(reader, &dummyListener);\n    mEsmStore.setUp();\n\n    ASSERT_TRUE (mEsmStore.get<RecordType>().getSize() == 1);\n}\n\n/// Tests overwriting of records.\nTEST_F(StoreTest, overwrite_test)\n{\n    const std::string recordId = \"foobar\";\n    const std::string recordIdUpper = \"Foobar\";\n\n    typedef ESM::Apparatus RecordType;\n\n    RecordType record;\n    record.blank();\n    record.mId = recordId;\n\n    ESM::ESMReader reader;\n    std::vector<ESM::ESMReader> readerList;\n    readerList.push_back(reader);\n    reader.setGlobalReaderList(&readerList);\n\n    // master file inserts a record\n    Files::IStreamPtr file = getEsmFile(record, false);\n    reader.open(file, \"filename\");\n    mEsmStore.load(reader, &dummyListener);\n    mEsmStore.setUp();\n\n    // now a plugin overwrites it with changed data\n    record.mId = recordIdUpper; // change id to uppercase, to test case smashing while we're at it\n    record.mModel = \"the_new_model\";\n    file = getEsmFile(record, false);\n    reader.open(file, \"filename\");\n    mEsmStore.load(reader, &dummyListener);\n    mEsmStore.setUp();\n\n    // verify that changes were actually applied\n    const RecordType* overwrittenRec = mEsmStore.get<RecordType>().search(recordId);\n\n    ASSERT_TRUE (overwrittenRec != nullptr);\n\n    ASSERT_TRUE (overwrittenRec && overwrittenRec->mModel == \"the_new_model\");\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/nifloader/testbulletnifloader.cpp",
    "content": "#include <components/nifbullet/bulletnifloader.hpp>\n#include <components/bullethelpers/processtrianglecallback.hpp>\n#include <components/nif/node.hpp>\n\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n#include <BulletCollision/CollisionShapes/btTriangleMesh.h>\n\n#include <gtest/gtest.h>\n#include <gmock/gmock.h>\n\n#include <algorithm>\n\nnamespace\n{\n    template <class T>\n    bool compareObjects(const T* lhs, const T* rhs)\n    {\n        return (!lhs && !rhs) || (lhs && rhs && *lhs == *rhs);\n    }\n\n    std::vector<btVector3> getTriangles(const btBvhTriangleMeshShape& shape)\n    {\n        std::vector<btVector3> result;\n        auto callback = BulletHelpers::makeProcessTriangleCallback([&] (btVector3* triangle, int, int) {\n            for (std::size_t i = 0; i < 3; ++i)\n                result.push_back(triangle[i]);\n        });\n        btVector3 aabbMin;\n        btVector3 aabbMax;\n        shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);\n        shape.processAllTriangles(&callback, aabbMin, aabbMax);\n        return result;\n    }\n\n    bool isNear(btScalar lhs, btScalar rhs)\n    {\n        return std::abs(lhs - rhs) <= 1e-5;\n    }\n\n    bool isNear(const btVector3& lhs, const btVector3& rhs)\n    {\n        return std::equal(\n            static_cast<const btScalar*>(lhs),\n            static_cast<const btScalar*>(lhs) + 3,\n            static_cast<const btScalar*>(rhs),\n            [] (btScalar lhs, btScalar rhs) { return isNear(lhs, rhs); }\n        );\n    }\n\n    bool isNear(const btMatrix3x3& lhs, const btMatrix3x3& rhs)\n    {\n        for (int i = 0; i < 3; ++i)\n            if (!isNear(lhs[i], rhs[i]))\n                return false;\n        return true;\n    }\n\n    bool isNear(const btTransform& lhs, const btTransform& rhs)\n    {\n        return isNear(lhs.getOrigin(), rhs.getOrigin()) && isNear(lhs.getBasis(), rhs.getBasis());\n    }\n}\n\nstatic std::ostream& operator <<(std::ostream& stream, const btVector3& value)\n{\n    return stream << \"btVector3 {\"\n        << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getX() << \", \"\n        << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getY() << \", \"\n        << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getZ() << \"}\";\n}\n\nstatic std::ostream& operator <<(std::ostream& stream, const btMatrix3x3& value)\n{\n    stream << \"btMatrix3x3 {\";\n    for (int i = 0; i < 3; ++i)\n         stream << value.getRow(i) << \", \";\n    return stream << \"}\";\n}\n\nstatic std::ostream& operator <<(std::ostream& stream, const btTransform& value)\n{\n    return stream << \"btTransform {\" << value.getBasis() << \", \" << value.getOrigin() << \"}\";\n}\n\nstatic std::ostream& operator <<(std::ostream& stream, const btCollisionShape* value);\n\nstatic std::ostream& operator <<(std::ostream& stream, const btCompoundShape& value)\n{\n    stream << \"btCompoundShape {\" << value.getLocalScaling() << \", \";\n    stream << \"{\";\n    for (int i = 0; i < value.getNumChildShapes(); ++i)\n        stream << value.getChildShape(i) << \", \";\n    stream << \"},\";\n    stream << \"{\";\n    for (int i = 0; i < value.getNumChildShapes(); ++i)\n        stream << value.getChildTransform(i) << \", \";\n    stream << \"}\";\n    return stream << \"}\";\n}\n\nstatic std::ostream& operator <<(std::ostream& stream, const btBoxShape& value)\n{\n    return stream << \"btBoxShape {\" << value.getLocalScaling() << \", \" << value.getHalfExtentsWithoutMargin() << \"}\";\n}\n\nnamespace Resource\n{\n\nstatic std::ostream& operator <<(std::ostream& stream, const TriangleMeshShape& value)\n{\n    stream << \"Resource::TriangleMeshShape {\" << value.getLocalScaling() << \", \"\n        << value.usesQuantizedAabbCompression() << \", \" << value.getOwnsBvh() << \", {\";\n    auto callback = BulletHelpers::makeProcessTriangleCallback([&] (btVector3* triangle, int, int) {\n        for (std::size_t i = 0; i < 3; ++i)\n            stream << triangle[i] << \", \";\n    });\n    btVector3 aabbMin;\n    btVector3 aabbMax;\n    value.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);\n    value.processAllTriangles(&callback, aabbMin, aabbMax);\n    return stream << \"}}\";\n}\n\n}\n\nstatic std::ostream& operator <<(std::ostream& stream, const btCollisionShape& value)\n{\n    switch (value.getShapeType())\n    {\n        case COMPOUND_SHAPE_PROXYTYPE:\n            return stream << static_cast<const btCompoundShape&>(value);\n        case BOX_SHAPE_PROXYTYPE:\n            return stream << static_cast<const btBoxShape&>(value);\n        case TRIANGLE_MESH_SHAPE_PROXYTYPE:\n            if (const auto casted = dynamic_cast<const Resource::TriangleMeshShape*>(&value))\n                return stream << *casted;\n            break;\n    }\n    return stream << \"btCollisionShape {\" << value.getShapeType() << \"}\";\n}\n\nstatic std::ostream& operator <<(std::ostream& stream, const btCollisionShape* value)\n{\n    return value ? stream << \"&\" << *value : stream << \"nullptr\";\n}\n\nnamespace std\n{\n    static std::ostream& operator <<(std::ostream& stream, const map<int, int>& value)\n    {\n        stream << \"std::map<int, int> {\";\n        for (const auto& v : value)\n            stream << \"{\" << v.first << \", \" << v.second << \"},\";\n        return stream << \"}\";\n    }\n}\n\nnamespace Resource\n{\n    static bool operator ==(const Resource::BulletShape& lhs, const Resource::BulletShape& rhs)\n    {\n        return compareObjects(lhs.mCollisionShape, rhs.mCollisionShape)\n            && compareObjects(lhs.mAvoidCollisionShape, rhs.mAvoidCollisionShape)\n            && lhs.mCollisionBox.extents == rhs.mCollisionBox.extents\n            && lhs.mCollisionBox.center == rhs.mCollisionBox.center\n            && lhs.mAnimatedShapes == rhs.mAnimatedShapes;\n    }\n\n    static std::ostream& operator <<(std::ostream& stream, const Resource::BulletShape& value)\n    {\n        return stream << \"Resource::BulletShape {\"\n            << value.mCollisionShape << \", \"\n            << value.mAvoidCollisionShape << \", \"\n            << \"osg::Vec3f {\" << value.mCollisionBox.extents << \"}\" << \", \"\n            << \"osg::Vec3f {\" << value.mCollisionBox.center << \"}\" << \", \"\n            << value.mAnimatedShapes\n            << \"}\";\n    }\n}\n\nstatic bool operator ==(const btCollisionShape& lhs, const btCollisionShape& rhs);\n\nstatic bool operator ==(const btCompoundShape& lhs, const btCompoundShape& rhs)\n{\n    if (lhs.getNumChildShapes() != rhs.getNumChildShapes() || lhs.getLocalScaling() != rhs.getLocalScaling())\n        return false;\n    for (int i = 0; i < lhs.getNumChildShapes(); ++i)\n    {\n        if (!compareObjects(lhs.getChildShape(i), rhs.getChildShape(i))\n                || !isNear(lhs.getChildTransform(i), rhs.getChildTransform(i)))\n            return false;\n    }\n    return true;\n}\n\nstatic bool operator ==(const btBoxShape& lhs, const btBoxShape& rhs)\n{\n    return isNear(lhs.getLocalScaling(), rhs.getLocalScaling())\n        && lhs.getHalfExtentsWithoutMargin() == rhs.getHalfExtentsWithoutMargin();\n}\n\nstatic bool operator ==(const btBvhTriangleMeshShape& lhs, const btBvhTriangleMeshShape& rhs)\n{\n    return isNear(lhs.getLocalScaling(), rhs.getLocalScaling())\n        && lhs.usesQuantizedAabbCompression() == rhs.usesQuantizedAabbCompression()\n        && lhs.getOwnsBvh() == rhs.getOwnsBvh()\n        && getTriangles(lhs) == getTriangles(rhs);\n}\n\nstatic bool operator ==(const btCollisionShape& lhs, const btCollisionShape& rhs)\n{\n    if (lhs.getShapeType() != rhs.getShapeType())\n        return false;\n    switch (lhs.getShapeType())\n    {\n        case COMPOUND_SHAPE_PROXYTYPE:\n            return static_cast<const btCompoundShape&>(lhs) == static_cast<const btCompoundShape&>(rhs);\n        case BOX_SHAPE_PROXYTYPE:\n            return static_cast<const btBoxShape&>(lhs) == static_cast<const btBoxShape&>(rhs);\n        case TRIANGLE_MESH_SHAPE_PROXYTYPE:\n            if (const auto lhsCasted = dynamic_cast<const Resource::TriangleMeshShape*>(&lhs))\n                if (const auto rhsCasted = dynamic_cast<const Resource::TriangleMeshShape*>(&rhs))\n                    return *lhsCasted == *rhsCasted;\n            return false;\n    }\n    return false;\n}\n\nnamespace\n{\n    using namespace testing;\n    using NifBullet::BulletNifLoader;\n\n    void init(Nif::Transformation& value)\n    {\n        value = Nif::Transformation::getIdentity();\n    }\n\n    void init(Nif::Extra& value)\n    {\n        value.next = Nif::ExtraPtr(nullptr);\n    }\n\n    void init(Nif::Named& value)\n    {\n        value.extra = Nif::ExtraPtr(nullptr);\n        value.extralist = Nif::ExtraList();\n        value.controller = Nif::ControllerPtr(nullptr);\n    }\n\n    void init(Nif::Node& value)\n    {\n        init(static_cast<Nif::Named&>(value));\n        value.flags = 0;\n        init(value.trafo);\n        value.hasBounds = false;\n        value.parent = nullptr;\n        value.isBone = false;\n    }\n\n    void init(Nif::NiGeometry& value)\n    {\n        init(static_cast<Nif::Node&>(value));\n        value.data = Nif::NiGeometryDataPtr(nullptr);\n        value.skin = Nif::NiSkinInstancePtr(nullptr);\n    }\n\n    void init(Nif::NiTriShape& value)\n    {\n        init(static_cast<Nif::NiGeometry&>(value));\n        value.recType = Nif::RC_NiTriShape;\n    }\n\n    void init(Nif::NiSkinInstance& value)\n    {\n        value.data = Nif::NiSkinDataPtr(nullptr);\n        value.root = Nif::NodePtr(nullptr);\n    }\n\n    void init(Nif::Controller& value)\n    {\n        value.next = Nif::ControllerPtr(nullptr);\n        value.flags = 0;\n        value.frequency = 0;\n        value.phase = 0;\n        value.timeStart = 0;\n        value.timeStop = 0;\n        value.target = Nif::NamedPtr(nullptr);\n    }\n\n    void copy(const btTransform& src, Nif::Transformation& dst)\n    {\n        dst.pos = osg::Vec3f(src.getOrigin().x(), src.getOrigin().y(), src.getOrigin().z());\n        for (int row = 0; row < 3; ++row)\n            for (int column = 0; column < 3; ++column)\n                dst.rotation.mValues[column][row] = src.getBasis().getRow(row)[column];\n    }\n\n    struct NifFileMock : Nif::File\n    {\n        MOCK_METHOD(Nif::Record*, getRecord, (std::size_t), (const, override));\n        MOCK_METHOD(std::size_t, numRecords, (), (const, override));\n        MOCK_METHOD(Nif::Record*, getRoot, (std::size_t), (const, override));\n        MOCK_METHOD(std::size_t, numRoots, (), (const, override));\n        MOCK_METHOD(std::string, getString, (uint32_t), (const, override));\n        MOCK_METHOD(void, setUseSkinning, (bool), (override));\n        MOCK_METHOD(bool, getUseSkinning, (), (const, override));\n        MOCK_METHOD(std::string, getFilename, (), (const, override));\n        MOCK_METHOD(unsigned int, getVersion, (), (const, override));\n        MOCK_METHOD(unsigned int, getUserVersion, (), (const, override));\n        MOCK_METHOD(unsigned int, getBethVersion, (), (const, override));\n    };\n\n    struct RecordMock : Nif::Record\n    {\n        MOCK_METHOD(void, read, (Nif::NIFStream *nif), (override));\n    };\n\n    struct TestBulletNifLoader : Test\n    {\n        BulletNifLoader mLoader;\n        const StrictMock<const NifFileMock> mNifFile;\n        Nif::Node mNode;\n        Nif::Node mNode2;\n        Nif::NiNode mNiNode;\n        Nif::NiNode mNiNode2;\n        Nif::NiNode mNiNode3;\n        Nif::NiTriShapeData mNiTriShapeData;\n        Nif::NiTriShape mNiTriShape;\n        Nif::NiTriShapeData mNiTriShapeData2;\n        Nif::NiTriShape mNiTriShape2;\n        Nif::NiSkinInstance mNiSkinInstance;\n        Nif::NiStringExtraData mNiStringExtraData;\n        Nif::NiStringExtraData mNiStringExtraData2;\n        Nif::Controller mController;\n        btTransform mTransform {btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3)};\n        btTransform mResultTransform {\n            btMatrix3x3(\n                1, 0, 0,\n                0, 0.82417738437652587890625, 0.56633174419403076171875,\n                0, -0.56633174419403076171875, 0.82417738437652587890625\n            ),\n            btVector3(1, 2, 3)\n        };\n        btTransform mResultTransform2 {\n            btMatrix3x3(\n                1, 0, 0,\n                0, 0.7951543331146240234375, 0.606407105922698974609375,\n                0, -0.606407105922698974609375, 0.7951543331146240234375\n            ),\n            btVector3(4, 8, 12)\n        };\n\n        TestBulletNifLoader()\n        {\n            init(mNode);\n            init(mNode2);\n            init(mNiNode);\n            init(mNiNode2);\n            init(mNiNode3);\n            init(mNiTriShape);\n            init(mNiTriShape2);\n            init(mNiSkinInstance);\n            init(mNiStringExtraData);\n            init(mNiStringExtraData2);\n            init(mController);\n\n            mNiTriShapeData.recType = Nif::RC_NiTriShapeData;\n            mNiTriShapeData.vertices = {osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0)};\n            mNiTriShapeData.triangles = {0, 1, 2};\n            mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData);\n\n            mNiTriShapeData2.recType = Nif::RC_NiTriShapeData;\n            mNiTriShapeData2.vertices = {osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1)};\n            mNiTriShapeData2.triangles = {0, 1, 2};\n            mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2);\n        }\n    };\n\n    TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default)\n    {\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default)\n    {\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_default_root_collision_node_nif_node_should_return_default)\n    {\n        mNode.recType = Nif::RC_RootCollisionNode;\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_default_root_nif_node_and_filename_starting_with_x_should_return_default)\n    {\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"xtest.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounding_box_should_return_shape_with_compound_shape_and_box_inside)\n    {\n        mNode.hasBounds = true;\n        mNode.flags |= Nif::NiNode::Flag_BBoxCollision;\n        mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);\n        mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n        expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);\n        expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);\n        std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());\n        expected.mCollisionShape = shape.release();\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_child_nif_node_with_bounding_box)\n    {\n        mNode.hasBounds = true;\n        mNode.flags |= Nif::NiNode::Flag_BBoxCollision;\n        mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);\n        mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n        expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);\n        expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);\n        std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());\n        expected.mCollisionShape = shape.release();\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_root_and_child_nif_node_with_bounding_box_but_root_without_flag_should_use_child_bounds)\n    {\n        mNode.hasBounds = true;\n        mNode.flags |= Nif::NiNode::Flag_BBoxCollision;\n        mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);\n        mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);\n\n        mNiNode.hasBounds = true;\n        mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNiNode.bounds.box.extents = osg::Vec3f(4, 5, 6);\n        mNiNode.bounds.box.center = osg::Vec3f(-4, -5, -6);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n        expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);\n        expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);\n        std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());\n        expected.mCollisionShape = shape.release();\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_first_with_flag_should_use_first_bounds)\n    {\n        mNode.hasBounds = true;\n        mNode.flags |= Nif::NiNode::Flag_BBoxCollision;\n        mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);\n        mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);\n\n        mNode2.hasBounds = true;\n        mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);\n        mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);\n\n        mNiNode.hasBounds = true;\n        mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9);\n        mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n        expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);\n        expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);\n        std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());\n        expected.mCollisionShape = shape.release();\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds)\n    {\n        mNode.hasBounds = true;\n        mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);\n        mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);\n\n        mNode2.hasBounds = true;\n        mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;\n        mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);\n        mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);\n\n        mNiNode.hasBounds = true;\n        mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9);\n        mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n        expected.mCollisionBox.extents = osg::Vec3f(4, 5, 6);\n        expected.mCollisionBox.center = osg::Vec3f(-4, -5, -6);\n        std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(4, 5, 6)));\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-4, -5, -6)), box.release());\n        expected.mCollisionShape = shape.release();\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape)\n    {\n        mNode.hasBounds = true;\n        mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);\n        mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n        expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);\n        expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_root_node_should_return_shape_with_triangle_mesh_shape)\n    {\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        Resource::BulletShape expected;\n        expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape)\n    {\n        mNiTriShape.hasBounds = true;\n        mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;\n        mNiTriShape.bounds.box.extents = osg::Vec3f(1, 2, 3);\n        mNiTriShape.bounds.box.center = osg::Vec3f(-1, -2, -3);\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n        expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);\n        expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_shape_with_triangle_mesh_shape)\n    {\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        Resource::BulletShape expected;\n        expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_shape_with_triangle_mesh_shape)\n    {\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiNode2)}));\n        mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        Resource::BulletShape expected;\n        expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_shape_with_triangle_mesh_shape_with_all_meshes)\n    {\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({\n            Nif::NodePtr(&mNiTriShape),\n            Nif::NodePtr(&mNiTriShape2)\n        }));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        Resource::BulletShape expected;\n        expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_shape_with_triangle_mesh_shape)\n    {\n        mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"xtest.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        Resource::BulletShape expected;\n        expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_root_node_and_filename_starting_with_x_should_return_shape_with_compound_shape)\n    {\n        copy(mTransform, mNiTriShape.trafo);\n        mNiTriShape.trafo.scale = 3;\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"xtest.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));\n        mesh->setLocalScaling(btVector3(3, 3, 3));\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(mResultTransform, mesh.release());\n        Resource::BulletShape expected;\n        expected.mCollisionShape = shape.release();\n        expected.mAnimatedShapes = {{-1, 0}};\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_should_return_shape_with_compound_shape)\n    {\n        copy(mTransform, mNiTriShape.trafo);\n        mNiTriShape.trafo.scale = 3;\n        mNiTriShape.parent = &mNiNode;\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n        mNiNode.trafo.scale = 4;\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"xtest.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));\n        mesh->setLocalScaling(btVector3(12, 12, 12));\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(mResultTransform2, mesh.release());\n        Resource::BulletShape expected;\n        expected.mCollisionShape = shape.release();\n        expected.mAnimatedShapes = {{-1, 0}};\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_shape_with_compound_shape)\n    {\n        copy(mTransform, mNiTriShape.trafo);\n        mNiTriShape.trafo.scale = 3;\n\n        copy(mTransform, mNiTriShape2.trafo);\n        mNiTriShape2.trafo.scale = 3;\n\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({\n            Nif::NodePtr(&mNiTriShape),\n            Nif::NodePtr(&mNiTriShape2),\n        }));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"xtest.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));\n        mesh->setLocalScaling(btVector3(3, 3, 3));\n\n        std::unique_ptr<btTriangleMesh> triangles2(new btTriangleMesh(false));\n        triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1));\n        std::unique_ptr<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));\n        mesh2->setLocalScaling(btVector3(3, 3, 3));\n\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(mResultTransform, mesh.release());\n        shape->addChildShape(mResultTransform, mesh2.release());\n        Resource::BulletShape expected;\n        expected.mCollisionShape = shape.release();\n        expected.mAnimatedShapes = {{-1, 0}};\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_shape_with_compound_shape)\n    {\n        mController.recType = Nif::RC_NiKeyframeController;\n        mController.flags |= Nif::NiNode::ControllerFlag_Active;\n        copy(mTransform, mNiTriShape.trafo);\n        mNiTriShape.trafo.scale = 3;\n        mNiTriShape.parent = &mNiNode;\n        mNiTriShape.controller = Nif::ControllerPtr(&mController);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n        mNiNode.trafo.scale = 4;\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));\n        mesh->setLocalScaling(btVector3(12, 12, 12));\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(mResultTransform2, mesh.release());\n        Resource::BulletShape expected;\n        expected.mCollisionShape = shape.release();\n        expected.mAnimatedShapes = {{-1, 0}};\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_where_one_with_controller_should_return_shape_with_compound_shape)\n    {\n        mController.recType = Nif::RC_NiKeyframeController;\n        mController.flags |= Nif::NiNode::ControllerFlag_Active;\n        copy(mTransform, mNiTriShape.trafo);\n        mNiTriShape.trafo.scale = 3;\n        copy(mTransform, mNiTriShape2.trafo);\n        mNiTriShape2.trafo.scale = 3;\n        mNiTriShape2.parent = &mNiNode;\n        mNiTriShape2.controller = Nif::ControllerPtr(&mController);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({\n            Nif::NodePtr(&mNiTriShape),\n            Nif::NodePtr(&mNiTriShape2),\n        }));\n        mNiNode.trafo.scale = 4;\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(1, 2, 3), btVector3(4, 2, 3), btVector3(4, 4.632747650146484375, 1.56172335147857666015625));\n        std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));\n        mesh->setLocalScaling(btVector3(1, 1, 1));\n\n        std::unique_ptr<btTriangleMesh> triangles2(new btTriangleMesh(false));\n        triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1));\n        std::unique_ptr<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));\n        mesh2->setLocalScaling(btVector3(12, 12, 12));\n\n        std::unique_ptr<btCompoundShape> shape(new btCompoundShape);\n        shape->addChildShape(mResultTransform2, mesh2.release());\n        shape->addChildShape(btTransform::getIdentity(), mesh.release());\n        Resource::BulletShape expected;\n        expected.mCollisionShape = shape.release();\n        expected.mAnimatedShapes = {{-1, 0}};\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape)\n    {\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n        mNiNode.recType = Nif::RC_AvoidNode;\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        Resource::BulletShape expected;\n        expected.mAvoidCollisionShape = new Resource::TriangleMeshShape(triangles.release(), false);\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape)\n    {\n        mNiTriShape.data = Nif::NiGeometryDataPtr(nullptr);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape)\n    {\n        auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.data.getPtr());\n        data->triangles.clear();\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)\n    {\n        mNiStringExtraData.string = \"NC___\";\n        mNiStringExtraData.recType = Nif::RC_NiStringExtraData;\n        mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)\n    {\n        mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);\n        mNiStringExtraData2.string = \"NC___\";\n        mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;\n        mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape)\n    {\n        mNiStringExtraData.string = \"MRK\";\n        mNiStringExtraData.recType = Nif::RC_NiStringExtraData;\n        mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        Resource::BulletShape expected;\n\n        EXPECT_EQ(*result, expected);\n    }\n\n    TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes)\n    {\n        mNiStringExtraData.string = \"MRK\";\n        mNiStringExtraData.recType = Nif::RC_NiStringExtraData;\n        mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);\n        mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));\n        mNiNode2.recType = Nif::RC_RootCollisionNode;\n        mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiNode2)}));\n        mNiNode.recType = Nif::RC_NiNode;\n\n        EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));\n        EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));\n        EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return(\"test.nif\"));\n        const auto result = mLoader.load(mNifFile);\n\n        std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));\n        triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));\n        Resource::BulletShape expected;\n        expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);\n\n        EXPECT_EQ(*result, expected);\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/openmw_test_suite.cpp",
    "content": "#include <gtest/gtest.h>\n\n#ifdef WIN32\n//we cannot use GTEST_API_ before main if we're building standalone exe application,\n//and we're linking GoogleTest / GoogleMock as DLLs and not linking gtest_main / gmock_main\nint main(int argc, char **argv) {\n#else\nGTEST_API_ int main(int argc, char **argv) {\n#endif\n    testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/settings/parser.cpp",
    "content": "#include <components/settings/parser.hpp>\n\n#include <boost/filesystem/fstream.hpp>\n\n#include <gtest/gtest.h>\n\nnamespace\n{\n    using namespace testing;\n    using namespace Settings;\n\n    struct SettingsFileParserTest : Test\n    {\n        SettingsFileParser mLoader;\n        SettingsFileParser mSaver;\n\n        template <typename F>\n        void withSettingsFile( const std::string& content, F&& f)\n        {\n            const auto path = std::string(UnitTest::GetInstance()->current_test_info()->name()) + \".cfg\";\n\n            {\n                boost::filesystem::ofstream stream;\n                stream.open(path);\n                stream << content;\n                stream.close();\n            }\n\n            f(path);\n        }\n    };\n\n    TEST_F(SettingsFileParserTest, load_empty_file)\n    {\n        const std::string content;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap());\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_single_empty_section)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap());\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_single_section_and_key)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            \"key = value\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key\"), \"value\"}\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_single_section_and_key_and_line_comments)\n    {\n        const std::string content =\n            \"# foo\\n\"\n            \"[Section]\\n\"\n            \"# bar\\n\"\n            \"key = value\\n\"\n            \"# baz\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key\"), \"value\"}\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_single_section_and_key_file_and_inline_section_comment)\n    {\n        const std::string content =\n            \"[Section] # foo\\n\"\n            \"key = value\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_single_section_and_key_and_inline_key_comment)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            \"key = value # foo\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key\"), \"value # foo\"}\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_single_section_and_key_and_whitespaces)\n    {\n        const std::string content =\n            \" [ Section ] \\n\"\n            \" key = value \\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key\"), \"value\"}\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_quoted_string_value)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            R\"(key = \"value\")\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key\"), R\"(\"value\")\"}\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_quoted_string_value_and_eol)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            R\"(key = \"value\"\\n)\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key\"), R\"(\"value\"\\n)\"}\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_empty_value)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            \"key =\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key\"), \"\"}\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_empty_key)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            \"=\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"\"), \"\"}\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_multiple_keys)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            \"key1 = value1\\n\"\n            \"key2 = value2\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key1\"), \"value1\"},\n                {CategorySetting(\"Section\", \"key2\"), \"value2\"},\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_multiple_sections)\n    {\n        const std::string content =\n            \"[Section1]\\n\"\n            \"key1 = value1\\n\"\n            \"[Section2]\\n\"\n            \"key2 = value2\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section1\", \"key1\"), \"value1\"},\n                {CategorySetting(\"Section2\", \"key2\"), \"value2\"},\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_multiple_sections_and_keys)\n    {\n        const std::string content =\n            \"[Section1]\\n\"\n            \"key1 = value1\\n\"\n            \"key2 = value2\\n\"\n            \"[Section2]\\n\"\n            \"key3 = value3\\n\"\n            \"key4 = value4\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section1\", \"key1\"), \"value1\"},\n                {CategorySetting(\"Section1\", \"key2\"), \"value2\"},\n                {CategorySetting(\"Section2\", \"key3\"), \"value3\"},\n                {CategorySetting(\"Section2\", \"key4\"), \"value4\"},\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_repeated_sections)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            \"key1 = value1\\n\"\n            \"[Section]\\n\"\n            \"key2 = value2\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key1\"), \"value1\"},\n                {CategorySetting(\"Section\", \"key2\"), \"value2\"},\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_repeated_keys)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            \"key = value\\n\"\n            \"key = value\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_repeated_keys_in_differrent_sections)\n    {\n        const std::string content =\n            \"[Section1]\\n\"\n            \"key = value\\n\"\n            \"[Section2]\\n\"\n            \"key = value\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section1\", \"key\"), \"value\"},\n                {CategorySetting(\"Section2\", \"key\"), \"value\"},\n            }));\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_unterminated_section)\n    {\n        const std::string content =\n            \"[Section\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_single_empty_section_name)\n    {\n        const std::string content =\n            \"[]\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap());\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_key_and_without_section)\n    {\n        const std::string content =\n            \"key = value\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_key_in_empty_name_section)\n    {\n        const std::string content =\n            \"[]\"\n            \"key = value\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_unterminated_key)\n    {\n        const std::string content =\n            \"[Section]\\n\"\n            \"key\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);\n        });\n    }\n\n    TEST_F(SettingsFileParserTest, file_with_empty_lines)\n    {\n        const std::string content =\n            \"\\n\"\n            \"[Section]\\n\"\n            \"\\n\"\n            \"key = value\\n\"\n            \"\\n\"\n        ;\n\n        withSettingsFile(content, [this] (const auto& path) {\n            CategorySettingValueMap map;\n            mLoader.loadSettingsFile(path, map);\n\n            EXPECT_EQ(map, CategorySettingValueMap({\n                {CategorySetting(\"Section\", \"key\"), \"value\"}\n            }));\n        });\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/shader/parsedefines.cpp",
    "content": "#include <components/shader/shadermanager.hpp>\n\n#include <gtest/gtest.h>\n#include <gmock/gmock.h>\n\nnamespace\n{\n    using namespace testing;\n    using namespace Shader;\n\n    using DefineMap = ShaderManager::DefineMap;\n\n    struct ShaderParseDefinesTest : Test\n    {\n        std::string mSource;\n        const std::string mName = \"shader\";\n        DefineMap mDefines;\n        DefineMap mGlobalDefines;\n    };\n\n    TEST_F(ShaderParseDefinesTest, empty_should_succeed)\n    {\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"\");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_fail_for_absent_define)\n    {\n        mSource = \"@foo\\n\";\n        ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"@foo\\n\");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_replace_by_existing_define)\n    {\n        mDefines[\"foo\"] = \"42\";\n        mSource = \"@foo\\n\";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"42\\n\");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_replace_by_existing_global_define)\n    {\n        mGlobalDefines[\"foo\"] = \"42\";\n        mSource = \"@foo\\n\";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"42\\n\");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_prefer_define_over_global_define)\n    {\n        mDefines[\"foo\"] = \"13\";\n        mGlobalDefines[\"foo\"] = \"42\";\n        mSource = \"@foo\\n\";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"13\\n\");\n    }\n\n    namespace SupportedTerminals\n    {\n        struct ShaderParseDefinesTest : ::ShaderParseDefinesTest, WithParamInterface<char> {};\n\n        TEST_P(ShaderParseDefinesTest, support_defines_terminated_by)\n        {\n            mDefines[\"foo\"] = \"13\";\n            mSource = \"@foo\" + std::string(1, GetParam());\n            ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n            EXPECT_EQ(mSource, \"13\" + std::string(1, GetParam()));\n        }\n\n        INSTANTIATE_TEST_SUITE_P(\n            SupportedTerminals,\n            ShaderParseDefinesTest,\n            Values(' ', '\\n', '\\r', '(', ')', '[', ']', '.', ';', ',')\n        );\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_not_support_define_ending_with_source)\n    {\n        mDefines[\"foo\"] = \"42\";\n        mSource = \"@foo\";\n        ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"@foo\");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_replace_all_matched_values)\n    {\n        mDefines[\"foo\"] = \"42\";\n        mSource = \"@foo @foo \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"42 42 \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_support_define_with_empty_name)\n    {\n        mDefines[\"\"] = \"42\";\n        mSource = \"@ \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"42 \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_replace_all_found_defines)\n    {\n        mDefines[\"foo\"] = \"42\";\n        mDefines[\"bar\"] = \"13\";\n        mDefines[\"baz\"] = \"55\";\n        mSource = \"@foo @bar \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"42 13 \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_fail_on_foreach_without_endforeach)\n    {\n        mSource = \"@foreach \";\n        ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$foreach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_fail_on_endforeach_without_foreach)\n    {\n        mSource = \"@endforeach \";\n        ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$endforeach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_replace_at_sign_by_dollar_for_foreach_endforeach)\n    {\n        mSource = \"@foreach @endforeach \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$foreach $endforeach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_succeed_on_unmatched_nested_foreach)\n    {\n        mSource = \"@foreach @foreach @endforeach \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$foreach $foreach $endforeach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_fail_on_unmatched_nested_endforeach)\n    {\n        mSource = \"@foreach @endforeach @endforeach \";\n        ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$foreach $endforeach $endforeach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_support_nested_foreach)\n    {\n        mSource = \"@foreach @foreach @endforeach @endforeach \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$foreach $foreach $endforeach $endforeach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_support_foreach_variable)\n    {\n        mSource = \"@foreach foo @foo @endforeach \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$foreach foo $foo $endforeach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_not_replace_foreach_variable_by_define)\n    {\n        mDefines[\"foo\"] = \"42\";\n        mSource = \"@foreach foo @foo @endforeach \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$foreach foo $foo $endforeach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_support_nested_foreach_with_variable)\n    {\n        mSource = \"@foreach foo @foo @foreach bar @bar @endforeach @endforeach \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"$foreach foo $foo $foreach bar $bar $endforeach $endforeach \");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_not_support_single_line_comments_for_defines)\n    {\n        mDefines[\"foo\"] = \"42\";\n        mSource = \"@foo // @foo\\n\";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"42 // 42\\n\");\n    }\n\n    TEST_F(ShaderParseDefinesTest, should_not_support_multiline_comments_for_defines)\n    {\n        mDefines[\"foo\"] = \"42\";\n        mSource = \"/* @foo */ @foo \";\n        ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName));\n        EXPECT_EQ(mSource, \"/* 42 */ 42 \");\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/shader/parsefors.cpp",
    "content": "#include <components/shader/shadermanager.hpp>\n\n#include <gtest/gtest.h>\n#include <gmock/gmock.h>\n\nnamespace\n{\n    using namespace testing;\n    using namespace Shader;\n\n    using DefineMap = ShaderManager::DefineMap;\n\n    struct ShaderParseForsTest : Test\n    {\n        std::string mSource;\n        const std::string mName = \"shader\";\n    };\n\n    TEST_F(ShaderParseForsTest, empty_should_succeed)\n    {\n        ASSERT_TRUE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"\");\n    }\n\n    TEST_F(ShaderParseForsTest, should_fail_for_single_escape_symbol)\n    {\n        mSource = \"$\";\n        ASSERT_FALSE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"$\");\n    }\n\n    TEST_F(ShaderParseForsTest, should_fail_on_first_found_escaped_not_foreach)\n    {\n        mSource = \"$foo \";\n        ASSERT_FALSE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"$foo \");\n    }\n\n    TEST_F(ShaderParseForsTest, should_fail_on_absent_foreach_variable)\n    {\n        mSource = \"$foreach \";\n        ASSERT_FALSE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"$foreach \");\n    }\n\n    TEST_F(ShaderParseForsTest, should_fail_on_unmatched_after_variable)\n    {\n        mSource = \"$foreach foo \";\n        ASSERT_FALSE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"$foreach foo \");\n    }\n\n    TEST_F(ShaderParseForsTest, should_fail_on_absent_newline_after_foreach_list)\n    {\n        mSource = \"$foreach foo 1,2,3 \";\n        ASSERT_FALSE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"$foreach foo 1,2,3 \");\n    }\n\n    TEST_F(ShaderParseForsTest, should_fail_on_absent_endforeach_after_newline)\n    {\n        mSource = \"$foreach foo 1,2,3\\n\";\n        ASSERT_FALSE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"$foreach foo 1,2,3\\n\");\n    }\n\n    TEST_F(ShaderParseForsTest, should_replace_complete_foreach_by_line_number)\n    {\n        mSource = \"$foreach foo 1,2,3\\n$endforeach\";\n        ASSERT_TRUE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"\\n#line 3\");\n    }\n\n    TEST_F(ShaderParseForsTest, should_replace_loop_variable)\n    {\n        mSource = \"$foreach foo 1,2,3\\n$foo\\n$endforeach\";\n        ASSERT_TRUE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"1\\n2\\n3\\n\\n#line 4\");\n    }\n\n    TEST_F(ShaderParseForsTest, should_count_line_number_from_existing)\n    {\n        mSource = \"$foreach foo 1,2,3\\n#line 10\\n$foo\\n$endforeach\";\n        ASSERT_TRUE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"#line 10\\n1\\n#line 10\\n2\\n#line 10\\n3\\n\\n#line 12\");\n    }\n\n    TEST_F(ShaderParseForsTest, should_not_support_nested_loops)\n    {\n        mSource = \"$foreach foo 1,2\\n$foo\\n$foreach bar 1,2\\n$bar\\n$endforeach\\n$endforeach\";\n        ASSERT_FALSE(parseFors(mSource, mName));\n        EXPECT_EQ(mSource, \"1\\n1\\n2\\n$foreach bar 1,2\\n1\\n\\n#line 6\\n2\\n2\\n$foreach bar 1,2\\n2\\n\\n#line 6\\n\\n#line 7\");\n    }\n}\n"
  },
  {
    "path": "apps/openmw_test_suite/shader/shadermanager.cpp",
    "content": "#include <components/shader/shadermanager.hpp>\n\n#include <boost/filesystem/fstream.hpp>\n\n#include <gtest/gtest.h>\n\nnamespace\n{\n    using namespace testing;\n    using namespace Shader;\n\n    struct ShaderManagerTest : Test\n    {\n        ShaderManager mManager;\n        ShaderManager::DefineMap mDefines;\n\n        ShaderManagerTest()\n        {\n            mManager.setShaderPath(\".\");\n        }\n\n        template <class F>\n        void withShaderFile(const std::string& content, F&& f)\n        {\n            withShaderFile(\"\", content, std::forward<F>(f));\n        }\n\n        template <class F>\n        void withShaderFile(const std::string& suffix, const std::string& content, F&& f)\n        {\n            const auto path = UnitTest::GetInstance()->current_test_info()->name() + suffix + \".glsl\";\n\n            {\n                boost::filesystem::ofstream stream;\n                stream.open(path);\n                stream << content;\n                stream.close();\n            }\n\n            f(path);\n        }\n    };\n\n    TEST_F(ShaderManagerTest, get_shader_with_empty_content_should_succeed)\n    {\n        const std::string content;\n\n        withShaderFile(content, [this] (const std::string& templateName) {\n            EXPECT_TRUE(mManager.getShader(templateName, {}, osg::Shader::VERTEX));\n        });\n    }\n\n    TEST_F(ShaderManagerTest, get_shader_should_not_change_source_without_template_parameters)\n    {\n        const std::string content =\n            \"#version 120\\n\"\n            \"void main() {}\\n\";\n\n        withShaderFile(content, [&] (const std::string& templateName) {\n            const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX);\n            ASSERT_TRUE(shader);\n            EXPECT_EQ(shader->getShaderSource(), content);\n        });\n    }\n\n    TEST_F(ShaderManagerTest, get_shader_should_replace_includes_with_content)\n    {\n        const std::string content0 =\n            \"void foo() {}\\n\";\n\n        withShaderFile(\"_0\", content0, [&] (const std::string& templateName0) {\n            const std::string content1 =\n                \"#include \\\"\" + templateName0 + \"\\\"\\n\"\n                \"void bar() { foo() }\\n\";\n\n            withShaderFile(\"_1\", content1, [&] (const std::string& templateName1) {\n                const std::string content2 =\n                    \"#version 120\\n\"\n                    \"#include \\\"\" + templateName1 + \"\\\"\\n\"\n                    \"void main() { bar() }\\n\";\n\n                withShaderFile(content2, [&] (const std::string& templateName2) {\n                    const auto shader = mManager.getShader(templateName2, mDefines, osg::Shader::VERTEX);\n                    ASSERT_TRUE(shader);\n                    const std::string expected =\n                        \"#version 120\\n\"\n                        \"#line 0 1\\n\"\n                        \"#line 0 2\\n\"\n                        \"void foo() {}\\n\"\n                        \"\\n\"\n                        \"#line 0 0\\n\"\n                        \"\\n\"\n                        \"void bar() { foo() }\\n\"\n                        \"\\n\"\n                        \"#line 1 0\\n\"\n                        \"\\n\"\n                        \"void main() { bar() }\\n\";\n                    EXPECT_EQ(shader->getShaderSource(), expected);\n                });\n            });\n        });\n    }\n\n    TEST_F(ShaderManagerTest, get_shader_should_replace_defines)\n    {\n        const std::string content =\n            \"#version 120\\n\"\n            \"#define FLAG @flag\\n\"\n            \"void main() {}\\n\"\n        ;\n\n        withShaderFile(content, [&] (const std::string& templateName) {\n            mDefines[\"flag\"] = \"1\";\n            const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX);\n            ASSERT_TRUE(shader);\n            const std::string expected =\n                \"#version 120\\n\"\n                \"#define FLAG 1\\n\"\n                \"void main() {}\\n\";\n            EXPECT_EQ(shader->getShaderSource(), expected);\n        });\n    }\n\n    TEST_F(ShaderManagerTest, get_shader_should_expand_loop)\n    {\n        const std::string content =\n            \"#version 120\\n\"\n            \"@foreach index @list\\n\"\n            \"    varying vec4 foo@index;\\n\"\n            \"@endforeach\\n\"\n            \"void main() {}\\n\"\n        ;\n\n        withShaderFile(content, [&] (const std::string& templateName) {\n            mDefines[\"list\"] = \"1,2,3\";\n            const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX);\n            ASSERT_TRUE(shader);\n            const std::string expected =\n                \"#version 120\\n\"\n                \"    varying vec4 foo1;\\n\"\n                \"    varying vec4 foo2;\\n\"\n                \"    varying vec4 foo3;\\n\"\n                \"\\n\"\n                \"#line 5\\n\"\n                \"void main() {}\\n\";\n            EXPECT_EQ(shader->getShaderSource(), expected);\n        });\n    }\n\n    TEST_F(ShaderManagerTest, get_shader_should_replace_loops_with_conditions)\n    {\n        const std::string content =\n            \"#version 120\\n\"\n            \"@foreach index @list\\n\"\n            \"    varying vec4 foo@index;\\n\"\n            \"@endforeach\\n\"\n            \"void main()\\n\"\n            \"{\\n\"\n            \"#ifdef BAR\\n\"\n            \"@foreach index @list\\n\"\n            \"    foo@index = vec4(1.0);\\n\"\n            \"@endforeach\\n\"\n            \"#elif BAZ\\n\"\n            \"@foreach index @list\\n\"\n            \"    foo@index = vec4(2.0);\\n\"\n            \"@endforeach\\n\"\n            \"#else\\n\"\n            \"@foreach index @list\\n\"\n            \"    foo@index = vec4(3.0);\\n\"\n            \"@endforeach\\n\"\n            \"#endif\\n\"\n            \"}\\n\"\n        ;\n\n        withShaderFile(content, [&] (const std::string& templateName) {\n            mDefines[\"list\"] = \"1,2,3\";\n            const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX);\n            ASSERT_TRUE(shader);\n            const std::string expected =\n                \"#version 120\\n\"\n                \"    varying vec4 foo1;\\n\"\n                \"    varying vec4 foo2;\\n\"\n                \"    varying vec4 foo3;\\n\"\n                \"\\n\"\n                \"#line 5\\n\"\n                \"void main()\\n\"\n                \"{\\n\"\n                \"#ifdef BAR\\n\"\n                \"    foo1 = vec4(1.0);\\n\"\n                \"    foo2 = vec4(1.0);\\n\"\n                \"    foo3 = vec4(1.0);\\n\"\n                \"\\n\"\n                \"#line 11\\n\"\n                \"#elif BAZ\\n\"\n                \"#line 12\\n\"\n                \"    foo1 = vec4(2.0);\\n\"\n                \"    foo2 = vec4(2.0);\\n\"\n                \"    foo3 = vec4(2.0);\\n\"\n                \"\\n\"\n                \"#line 15\\n\"\n                \"#else\\n\"\n                \"#line 16\\n\"\n                \"    foo1 = vec4(3.0);\\n\"\n                \"    foo2 = vec4(3.0);\\n\"\n                \"    foo3 = vec4(3.0);\\n\"\n                \"\\n\"\n                \"#line 19\\n\"\n                \"#endif\\n\"\n                \"#line 20\\n\"\n                \"}\\n\";\n            EXPECT_EQ(shader->getShaderSource(), expected);\n        });\n    }\n\n    TEST_F(ShaderManagerTest, get_shader_should_fail_on_absent_template_parameters_in_single_line_comments)\n    {\n        const std::string content =\n            \"#version 120\\n\"\n            \"// #define FLAG @flag\\n\"\n            \"void main() {}\\n\"\n        ;\n\n        withShaderFile(content, [&] (const std::string& templateName) {\n            EXPECT_FALSE(mManager.getShader(templateName, mDefines, osg::Shader::VERTEX));\n        });\n    }\n\n    TEST_F(ShaderManagerTest, get_shader_should_fail_on_absent_template_parameter_in_multi_line_comments)\n    {\n        const std::string content =\n            \"#version 120\\n\"\n            \"/* #define FLAG @flag */\\n\"\n            \"void main() {}\\n\"\n        ;\n\n        withShaderFile(content, [&] (const std::string& templateName) {\n            EXPECT_FALSE(mManager.getShader(templateName, mDefines, osg::Shader::VERTEX));\n        });\n    }\n}\n"
  },
  {
    "path": "apps/wizard/CMakeLists.txt",
    "content": "\nset(WIZARD\n    componentselectionpage.cpp\n    conclusionpage.cpp\n    existinginstallationpage.cpp\n    importpage.cpp\n    inisettings.cpp\n    installationtargetpage.cpp\n    intropage.cpp\n    languageselectionpage.cpp\n    main.cpp\n    mainwizard.cpp\n    methodselectionpage.cpp\n\n    utils/componentlistwidget.cpp\n)\n\nif(WIN32)\n    list(APPEND WIZARD ${CMAKE_SOURCE_DIR}/files/windows/openmw-wizard.rc)\nendif()\n\nset(WIZARD_HEADER\n    componentselectionpage.hpp\n    conclusionpage.hpp\n    existinginstallationpage.hpp\n    importpage.hpp\n    inisettings.hpp\n    installationtargetpage.hpp\n    intropage.hpp\n    languageselectionpage.hpp\n    mainwizard.hpp\n    methodselectionpage.hpp\n\n    utils/componentlistwidget.hpp\n)\n\n# Headers that must be pre-processed\nset(WIZARD_HEADER_MOC\n    componentselectionpage.hpp\n    conclusionpage.hpp\n    existinginstallationpage.hpp\n    importpage.hpp\n    installationtargetpage.hpp\n    intropage.hpp\n    languageselectionpage.hpp\n    mainwizard.hpp\n    methodselectionpage.hpp\n\n    utils/componentlistwidget.hpp\n)\n\nset(WIZARD_UI\n    ${CMAKE_SOURCE_DIR}/files/ui/wizard/componentselectionpage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/wizard/conclusionpage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/wizard/existinginstallationpage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/wizard/importpage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/wizard/installationtargetpage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/wizard/intropage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/wizard/languageselectionpage.ui\n    ${CMAKE_SOURCE_DIR}/files/ui/wizard/methodselectionpage.ui\n)\n\nif (OPENMW_USE_UNSHIELD)\n    set (WIZARD ${WIZARD} installationpage.cpp unshield/unshieldworker.cpp)\n    set (WIZARD_HEADER ${WIZARD_HEADER} installationpage.hpp unshield/unshieldworker.hpp)\n    set (WIZARD_HEADER_MOC ${WIZARD_HEADER_MOC} installationpage.hpp unshield/unshieldworker.hpp)\n    set (WIZARD_UI ${WIZARD_UI} ${CMAKE_SOURCE_DIR}/files/ui/wizard/installationpage.ui)\n    add_definitions(-DOPENMW_USE_UNSHIELD)\nendif (OPENMW_USE_UNSHIELD)\n\n\nsource_group(wizard FILES ${WIZARD} ${WIZARD_HEADER})\n\nset(QT_USE_QTGUI 1)\n\n# Set some platform specific settings\nif(WIN32)\n    set(GUI_TYPE WIN32)\n    set(QT_USE_QTMAIN TRUE)\nendif(WIN32)\n\nQT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc)\nQT5_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC})\nQT5_WRAP_UI(UI_HDRS ${WIZARD_UI})\n\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR})\n\nif (OPENMW_USE_UNSHIELD)\n    include_directories(${LIBUNSHIELD_INCLUDE_DIRS})\nendif()\n\nopenmw_add_executable(openmw-wizard\n    ${GUI_TYPE}\n    ${WIZARD}\n    ${WIZARD_HEADER}\n    ${RCC_SRCS}\n    ${MOC_SRCS}\n    ${UI_HDRS}\n)\n\ntarget_link_libraries(openmw-wizard\n    components\n)\n\ntarget_link_libraries(openmw-wizard Qt5::Widgets Qt5::Core)\n\nif (OPENMW_USE_UNSHIELD)\n    target_link_libraries(openmw-wizard ${LIBUNSHIELD_LIBRARIES})\nendif()\n\nif(DPKG_PROGRAM)\n    INSTALL(TARGETS openmw-wizard RUNTIME DESTINATION games COMPONENT openmw-wizard)\nendif()\n\nif (BUILD_WITH_CODE_COVERAGE)\n  add_definitions (--coverage)\n  target_link_libraries(openmw-wizard gcov)\nendif()\n\n# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream\nif (CMAKE_SYSTEM_NAME MATCHES \"Linux\")\ntarget_link_libraries(openmw-wizard dl Xt)\nendif()\n\nif (WIN32)\n    INSTALL(TARGETS openmw-wizard RUNTIME DESTINATION \".\")\nendif(WIN32)\n"
  },
  {
    "path": "apps/wizard/componentselectionpage.cpp",
    "content": "#include \"componentselectionpage.hpp\"\n\n#include <QDebug>\n#include <QPushButton>\n#include <QMessageBox>\n\n#include \"mainwizard.hpp\"\n\nWizard::ComponentSelectionPage::ComponentSelectionPage(QWidget *parent) :\n    QWizardPage(parent)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n\n    setCommitPage(true);\n    setButtonText(QWizard::CommitButton, tr(\"&Install\"));\n\n    registerField(QLatin1String(\"installation.components\"), componentsList);\n\n    connect(componentsList, SIGNAL(itemChanged(QListWidgetItem *)),\n            this, SLOT(updateButton(QListWidgetItem *)));\n\n}\n\nvoid Wizard::ComponentSelectionPage::updateButton(QListWidgetItem*)\n{\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true)\n        return; // Morrowind is always checked here\n\n    bool unchecked = true;\n\n    for (int i =0; i < componentsList->count(); ++i) {\n        QListWidgetItem *item = componentsList->item(i);\n\n        if (!item)\n            continue;\n\n        if (item->checkState() == Qt::Checked) {\n            unchecked = false;\n        }\n    }\n\n    if (unchecked) {\n        setCommitPage(false);\n        setButtonText(QWizard::NextButton, tr(\"&Skip\"));\n    } else {\n        setCommitPage(true);\n    }\n}\n\nvoid Wizard::ComponentSelectionPage::initializePage()\n{\n    componentsList->clear();\n\n    QString path(field(QLatin1String(\"installation.path\")).toString());\n\n    QListWidgetItem *morrowindItem = new QListWidgetItem(QLatin1String(\"Morrowind\"));\n    QListWidgetItem *tribunalItem = new QListWidgetItem(QLatin1String(\"Tribunal\"));\n    QListWidgetItem *bloodmoonItem = new QListWidgetItem(QLatin1String(\"Bloodmoon\"));\n\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true)\n    {\n        morrowindItem->setFlags((morrowindItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable);\n        morrowindItem->setData(Qt::CheckStateRole, Qt::Checked);\n        componentsList->addItem(morrowindItem);\n\n        tribunalItem->setFlags(tribunalItem->flags() | Qt::ItemIsUserCheckable);\n        tribunalItem->setData(Qt::CheckStateRole, Qt::Checked);\n        componentsList->addItem(tribunalItem);\n\n        bloodmoonItem->setFlags(bloodmoonItem->flags() | Qt::ItemIsUserCheckable);\n        bloodmoonItem->setData(Qt::CheckStateRole, Qt::Checked);\n        componentsList->addItem(bloodmoonItem);\n    } else {\n\n        if (mWizard->mInstallations[path].hasMorrowind) {\n            morrowindItem->setText(tr(\"Morrowind\\t\\t(installed)\"));\n            morrowindItem->setFlags((morrowindItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable);\n            morrowindItem->setData(Qt::CheckStateRole, Qt::Unchecked);\n        } else {\n            morrowindItem->setText(tr(\"Morrowind\"));\n            morrowindItem->setData(Qt::CheckStateRole, Qt::Checked);\n        }\n\n        componentsList->addItem(morrowindItem);\n\n        if (mWizard->mInstallations[path].hasTribunal) {\n            tribunalItem->setText(tr(\"Tribunal\\t\\t(installed)\"));\n            tribunalItem->setFlags((tribunalItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable);\n            tribunalItem->setData(Qt::CheckStateRole, Qt::Unchecked);\n        } else {\n            tribunalItem->setText(tr(\"Tribunal\"));\n            tribunalItem->setData(Qt::CheckStateRole, Qt::Checked);\n        }\n\n        componentsList->addItem(tribunalItem);\n\n        if (mWizard->mInstallations[path].hasBloodmoon) {\n            bloodmoonItem->setText(tr(\"Bloodmoon\\t\\t(installed)\"));\n            bloodmoonItem->setFlags((bloodmoonItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable);\n            bloodmoonItem->setData(Qt::CheckStateRole, Qt::Unchecked);\n        } else {\n            bloodmoonItem->setText(tr(\"Bloodmoon\"));\n            bloodmoonItem->setData(Qt::CheckStateRole, Qt::Checked);\n        }\n\n        componentsList->addItem(bloodmoonItem);\n    }\n}\n\nbool Wizard::ComponentSelectionPage::validatePage()\n{\n    QStringList components(field(QLatin1String(\"installation.components\")).toStringList());\n    QString path(field(QLatin1String(\"installation.path\")).toString());\n\n//    qDebug() << components << path << mWizard->mInstallations[path];\n\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == false) {\n        if (components.contains(QLatin1String(\"Tribunal\")) && !components.contains(QLatin1String(\"Bloodmoon\")))\n        {\n            if (mWizard->mInstallations[path].hasBloodmoon)\n            {\n                QMessageBox msgBox;\n                msgBox.setWindowTitle(tr(\"About to install Tribunal after Bloodmoon\"));\n                msgBox.setIcon(QMessageBox::Information);\n                msgBox.setStandardButtons(QMessageBox::Cancel);\n                msgBox.setText(tr(\"<html><head/><body><p><b>You are about to install Tribunal</b></p> \\\n                                  <p>Bloodmoon is already installed on your computer.</p> \\\n                                  <p>However, it is recommended that you install Tribunal before Bloodmoon.</p> \\\n                                  <p>Would you like to re-install Bloodmoon?</p></body></html>\"));\n\n                QAbstractButton *reinstallButton = msgBox.addButton(tr(\"Re-install &Bloodmoon\"), QMessageBox::ActionRole);\n                msgBox.exec();\n\n\n                if (msgBox.clickedButton() == reinstallButton) {\n                    // Force reinstallation\n                    mWizard->mInstallations[path].hasBloodmoon = false;\n                    QList<QListWidgetItem*> items = componentsList->findItems(QLatin1String(\"Bloodmoon\"), Qt::MatchStartsWith);\n\n                    for (QListWidgetItem *item : items)\n                    {\n                        item->setText(QLatin1String(\"Bloodmoon\"));\n                        item->setCheckState(Qt::Checked);\n                    }\n\n                    return true;\n                }\n            }\n        }\n    }\n\n    return true;\n}\n\nint Wizard::ComponentSelectionPage::nextId() const\n{\n#ifdef OPENMW_USE_UNSHIELD\n    if (isCommitPage()) {\n        return MainWizard::Page_Installation;\n    } else {\n        return MainWizard::Page_Import;\n    }\n#else\n    return MainWizard::Page_Import;\n#endif\n}\n"
  },
  {
    "path": "apps/wizard/componentselectionpage.hpp",
    "content": "#ifndef COMPONENTSELECTIONPAGE_HPP\n#define COMPONENTSELECTIONPAGE_HPP\n\n#include \"ui_componentselectionpage.h\"\n\nnamespace Wizard\n{\n    class MainWizard;\n\n    class ComponentSelectionPage : public QWizardPage, private Ui::ComponentSelectionPage\n    {\n        Q_OBJECT\n    public:\n        ComponentSelectionPage(QWidget *parent);\n\n        int nextId() const override;\n        bool validatePage() override;\n\n    private slots:\n        void updateButton(QListWidgetItem *item);\n\n    private:\n        MainWizard *mWizard;\n\n    protected:\n        void initializePage() override;\n\n    };\n\n}\n\n#endif // COMPONENTSELECTIONPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/conclusionpage.cpp",
    "content": "#include \"conclusionpage.hpp\"\n\n#include <QDebug>\n\n#include \"mainwizard.hpp\"\n\nWizard::ConclusionPage::ConclusionPage(QWidget *parent) :\n    QWizardPage(parent)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n    setPixmap(QWizard::WatermarkPixmap, QPixmap(QLatin1String(\":/images/intropage-background.png\")));\n}\n\nvoid Wizard::ConclusionPage::initializePage()\n{\n    // Write the path to openmw.cfg\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true) {\n        QString path(field(QLatin1String(\"installation.path\")).toString());\n        mWizard->addInstallation(path);\n    }\n\n    if (!mWizard->mError)\n    {\n        if ((field(QLatin1String(\"installation.retailDisc\")).toBool() == true)\n                || (field(QLatin1String(\"installation.import-settings\")).toBool() == true))\n        {\n            qDebug() << \"IMPORT SETTINGS\";\n            mWizard->runSettingsImporter();\n        }\n    }\n\n    if (!mWizard->mError)\n    {\n        if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true)\n        {\n            textLabel->setText(tr(\"<html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your computer.</p> \\\n                                  <p>Click Finish to close the Wizard.</p></body></html>\"));\n        } else {\n            textLabel->setText(tr(\"<html><head/><body><p>The OpenMW Wizard successfully modified your existing Morrowind installation.</p> \\\n                                  <p>Click Finish to close the Wizard.</p></body></html>\"));\n        }\n    } else {\n        textLabel->setText(tr(\"<html><head/><body><p>The OpenMW Wizard failed to install Morrowind on your computer.</p> \\\n                              <p>Please report any bugs you might have encountered to our \\\n                              <a href=\\\"https://gitlab.com/OpenMW/openmw/issues\\\">bug tracker</a>.<br/>Make sure to include the installation log.</p><br/></body></html>\"));\n    }\n}\n\nint Wizard::ConclusionPage::nextId() const\n{\n    return -1;\n}\n"
  },
  {
    "path": "apps/wizard/conclusionpage.hpp",
    "content": "#ifndef CONCLUSIONPAGE_HPP\n#define CONCLUSIONPAGE_HPP\n\n#include \"ui_conclusionpage.h\"\n\nnamespace Wizard\n{\n    class MainWizard;\n\n    class ConclusionPage : public QWizardPage, private Ui::ConclusionPage\n    {\n        Q_OBJECT\n    public:\n        ConclusionPage(QWidget *parent);\n\n        int nextId() const override;\n\n    private:\n        MainWizard *mWizard;\n\n    protected:\n        void initializePage() override;\n\n    };\n\n}\n\n#endif // CONCLUSIONPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/existinginstallationpage.cpp",
    "content": "#include \"existinginstallationpage.hpp\"\n\n#include <QDebug>\n#include <QMessageBox>\n#include <QFileDialog>\n#include <QFileInfo>\n#include <QFile>\n\n#include \"mainwizard.hpp\"\n\nWizard::ExistingInstallationPage::ExistingInstallationPage(QWidget *parent) :\n    QWizardPage(parent)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n\n    // Add a placeholder item to the list of installations\n    QListWidgetItem *emptyItem = new QListWidgetItem(tr(\"No existing installations detected\"));\n    emptyItem->setFlags(Qt::NoItemFlags);\n\n    installationsList->insertItem(0, emptyItem);\n\n}\n\nvoid Wizard::ExistingInstallationPage::initializePage()\n{\n    // Add the available installation paths\n    QStringList paths(mWizard->mInstallations.keys());\n\n    // Hide the default item if there are installations to choose from\n    installationsList->item(0)->setHidden(!paths.isEmpty());\n\n    for (const QString &path : paths)\n    {\n        if (installationsList->findItems(path, Qt::MatchExactly).isEmpty())\n        {\n            QListWidgetItem *item = new QListWidgetItem(path);\n            installationsList->addItem(item);\n        }\n    }\n\n    connect(installationsList, SIGNAL(currentTextChanged(QString)),\n            this, SLOT(textChanged(QString)));\n\n    connect(installationsList,SIGNAL(itemSelectionChanged()),\n            this, SIGNAL(completeChanged()));\n}\n\nbool Wizard::ExistingInstallationPage::validatePage()\n{\n    // See if Morrowind.ini is detected, if not, ask the user\n    // It can be missing entirely\n    // Or failed to be detected due to the target being a symlink\n\n    QString path(field(QLatin1String(\"installation.path\")).toString());\n    QFile file(mWizard->mInstallations[path].iniPath);\n\n    if (!file.exists()) {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error detecting Morrowind configuration\"));\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setStandardButtons(QMessageBox::Cancel);\n        msgBox.setText(QObject::tr(\"<br><b>Could not find Morrowind.ini</b><br><br> \\\n                                   The Wizard needs to update settings in this file.<br><br> \\\n                                   Press \\\"Browse...\\\" to specify the location manually.<br>\"));\n\n        QAbstractButton *browseButton2 =\n                msgBox.addButton(QObject::tr(\"B&rowse...\"), QMessageBox::ActionRole);\n\n        msgBox.exec();\n\n        QString iniFile;\n        if (msgBox.clickedButton() == browseButton2) {\n            iniFile = QFileDialog::getOpenFileName(\n                        this,\n                        QObject::tr(\"Select configuration file\"),\n                        QDir::currentPath(),\n                        QString(tr(\"Morrowind configuration file (*.ini)\")));\n        }\n\n        if (iniFile.isEmpty()) {\n            return false; // Cancel was clicked;\n        }\n\n        // A proper Morrowind.ini was selected, set it\n        QFileInfo info(iniFile);\n        mWizard->mInstallations[path].iniPath = info.absoluteFilePath();\n    }\n\n    return true;\n}\n\nvoid Wizard::ExistingInstallationPage::on_browseButton_clicked()\n{\n    QString selectedFile = QFileDialog::getOpenFileName(\n                this,\n                tr(\"Select Morrowind.esm (located in Data Files)\"),\n                QDir::currentPath(),\n                QString(tr(\"Morrowind master file (Morrowind.esm)\")),\n                nullptr,\n                QFileDialog::DontResolveSymlinks);\n\n    if (selectedFile.isEmpty())\n        return;\n\n    QFileInfo info(selectedFile);\n\n    if (!info.exists())\n        return;\n\n    if (!mWizard->findFiles(QLatin1String(\"Morrowind\"), info.absolutePath()))\n    {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error detecting Morrowind files\"));\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(QObject::tr(\n            \"<b>Morrowind.bsa</b> is missing!<br>\\\n            Make sure your Morrowind installation is complete.\"\n        ));\n        msgBox.exec();\n        return;\n    }\n\n    QString path(QDir::toNativeSeparators(info.absolutePath()));\n    QList<QListWidgetItem*> items = installationsList->findItems(path, Qt::MatchExactly);\n\n    if (items.isEmpty()) {\n        // Path is not yet in the list, add it\n        mWizard->addInstallation(path);\n\n        // Hide the default item\n        installationsList->item(0)->setHidden(true);\n\n        QListWidgetItem *item = new QListWidgetItem(path);\n        installationsList->addItem(item);\n        installationsList->setCurrentItem(item); // Select it too\n    } else {\n        installationsList->setCurrentItem(items.first());\n    }\n\n    // Update the button\n    emit completeChanged();\n}\n\nvoid Wizard::ExistingInstallationPage::textChanged(const QString &text)\n{\n    // Set the installation path manually, as registerField doesn't work\n    // Because it doesn't accept two widgets operating on a single field\n    if (!text.isEmpty())\n        mWizard->setField(QLatin1String(\"installation.path\"), text);\n}\n\nbool Wizard::ExistingInstallationPage::isComplete() const\n{\n    if (installationsList->selectionModel()->hasSelection()) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nint Wizard::ExistingInstallationPage::nextId() const\n{\n    return MainWizard::Page_LanguageSelection;\n}\n"
  },
  {
    "path": "apps/wizard/existinginstallationpage.hpp",
    "content": "#ifndef EXISTINGINSTALLATIONPAGE_HPP\n#define EXISTINGINSTALLATIONPAGE_HPP\n\n#include \"ui_existinginstallationpage.h\"\n\nnamespace Wizard\n{\n    class MainWizard;\n\n    class ExistingInstallationPage : public QWizardPage, private Ui::ExistingInstallationPage\n    {\n        Q_OBJECT\n    public:\n        ExistingInstallationPage(QWidget *parent);\n\n        int nextId() const override;\n        bool isComplete() const override;\n        bool validatePage() override;\n\n    private slots:\n        void on_browseButton_clicked();\n        void textChanged(const QString &text);\n\n\n    private:\n        MainWizard *mWizard;\n\n    protected:\n        void initializePage() override;\n\n    };\n\n}\n\n#endif // EXISTINGINSTALLATIONPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/importpage.cpp",
    "content": "#include \"importpage.hpp\"\n\n#include \"mainwizard.hpp\"\n\nWizard::ImportPage::ImportPage(QWidget *parent) :\n    QWizardPage(parent)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n\n    registerField(QLatin1String(\"installation.import-settings\"), importCheckBox);\n    registerField(QLatin1String(\"installation.import-addons\"), addonsCheckBox);\n}\n\nint Wizard::ImportPage::nextId() const\n{\n    return MainWizard::Page_Conclusion;\n}\n"
  },
  {
    "path": "apps/wizard/importpage.hpp",
    "content": "#ifndef IMPORTPAGE_HPP\n#define IMPORTPAGE_HPP\n\n#include \"ui_importpage.h\"\n\nnamespace Wizard\n{\n    class MainWizard;\n\n    class ImportPage : public QWizardPage, private Ui::ImportPage\n    {\n        Q_OBJECT\n    public:\n        ImportPage(QWidget *parent);\n\n        int nextId() const override;\n\n    private:\n        MainWizard *mWizard;\n\n    };\n\n}\n\n#endif // IMPORTPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/inisettings.cpp",
    "content": "#include \"inisettings.hpp\"\n\n#include <QTextStream>\n#include <QFile>\n#include <QStringList>\n#include <QString>\n#include <QRegExp>\n#include <QDebug>\n\nWizard::IniSettings::IniSettings()\n{\n}\n\nWizard::IniSettings::~IniSettings()\n{\n}\n\nQStringList Wizard::IniSettings::findKeys(const QString &text)\n{\n    QStringList result;\n\n    for (const QString &key : mSettings.keys())\n    {\n\n        if (key.startsWith(text))\n            result << key;\n\n    }\n\n    return result;\n}\n\nbool Wizard::IniSettings::readFile(QTextStream &stream)\n{\n    // Look for a square bracket, \"'\\\\[\"\n    // that has one or more \"not nothing\" in it, \"([^]]+)\"\n    // and is closed with a square bracket, \"\\\\]\"\n    QRegExp sectionRe(QLatin1String(\"^\\\\[([^]]+)\\\\]\"));\n\n    // Find any character(s) that is/are not equal sign(s), \"[^=]+\"\n    // followed by an optional whitespace, an equal sign, and another optional whitespace, \"\\\\s*=\\\\s*\"\n    // and one or more periods, \"(.+)\"\n    QRegExp keyRe(QLatin1String(\"^([^=]+)\\\\s*=\\\\s*(.+)$\"));\n\n    QString currentSection;\n\n    while (!stream.atEnd())\n    {\n        const QString line(stream.readLine());\n\n        if (line.isEmpty() || line.startsWith(QLatin1Char(';')))\n            continue;\n\n        if (sectionRe.exactMatch(line))\n        {\n            currentSection = sectionRe.cap(1);\n        }\n        else if (keyRe.indexIn(line) != -1)\n        {\n            QString key = keyRe.cap(1).trimmed();\n            QString value = keyRe.cap(2).trimmed();\n\n            // Append the section, but only if there is one\n            if (!currentSection.isEmpty())\n                key = currentSection + QLatin1Char('/') + key;\n\n            mSettings[key] = QVariant(value);\n        }\n    }\n\n    return true;\n}\n\nbool Wizard::IniSettings::writeFile(const QString &path, QTextStream &stream)\n{\n    // Look for a square bracket, \"'\\\\[\"\n    // that has one or more \"not nothing\" in it, \"([^]]+)\"\n    // and is closed with a square bracket, \"\\\\]\"\n    QRegExp sectionRe(QLatin1String(\"^\\\\[([^]]+)\\\\]\"));\n\n    // Find any character(s) that is/are not equal sign(s), \"[^=]+\"\n    // followed by an optional whitespace, an equal sign, and another optional whitespace, \"\\\\s*=\\\\s*\"\n    // and one or more periods, \"(.+)\"\n    QRegExp keyRe(QLatin1String(\"^([^=]+)\\\\s*=\\\\s*(.+)$\"));\n\n    const QStringList keys(mSettings.keys());\n\n    QString currentSection;\n    QString buffer;\n\n    while (!stream.atEnd()) {\n\n        const QString line(stream.readLine());\n\n        if (line.isEmpty() || line.startsWith(QLatin1Char(';'))) {\n            buffer.append(line + QLatin1String(\"\\n\"));\n            continue;\n        }\n\n        if (sectionRe.exactMatch(line)) {\n            buffer.append(line + QLatin1String(\"\\n\"));\n            currentSection = sectionRe.cap(1);\n        } else  if (keyRe.indexIn(line) != -1) {\n            QString key(keyRe.cap(1).trimmed());\n            QString lookupKey(key);\n\n            // Append the section, but only if there is one\n            if (!currentSection.isEmpty())\n                lookupKey = currentSection + QLatin1Char('/') + key;\n\n            buffer.append(key + QLatin1Char('=') + mSettings[lookupKey].toString() + QLatin1String(\"\\n\"));\n            mSettings.remove(lookupKey);\n        }\n    }\n\n    // Add the new settings to the buffer\n    QHashIterator<QString, QVariant> i(mSettings);\n    while (i.hasNext()) {\n        i.next();\n\n        QStringList fullKey(i.key().split(QLatin1Char('/')));\n        QString section(fullKey.at(0));\n        section.prepend(QLatin1Char('['));\n        section.append(QLatin1Char(']'));\n        QString key(fullKey.at(1));\n\n        int index = buffer.lastIndexOf(section);\n        if (index == -1) {\n            // Add the section to the end of the file, because it's not found\n            buffer.append(QString(\"\\n%1\\n\").arg(section));\n            index = buffer.lastIndexOf(section);\n        }\n\n        // Look for the next section\n        index = buffer.indexOf(QLatin1Char('['), index + 1);\n\n        if (index == -1 ) {\n            // We are at the last section, append it to the bottom of the file\n            buffer.append(QString(\"\\n%1=%2\").arg(key, i.value().toString()));\n            mSettings.remove(i.key());\n            continue;\n        } else {\n            // Not at last section, add the key at the index\n            buffer.insert(index - 1, QString(\"\\n%1=%2\").arg(key, i.value().toString()));\n            mSettings.remove(i.key());\n        }\n    }\n\n    // Now we reopen the file, this time we write\n    QFile file(path);\n\n    if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {\n        QTextStream in(&file);\n        in.setCodec(stream.codec());\n\n        // Write the updated buffer to an empty file\n        in << buffer;\n        file.flush();\n        file.close();\n    } else {\n        return false;\n    }\n\n    return true;\n}\n\nbool Wizard::IniSettings::parseInx(const QString &path)\n{\n    QFile file(path);\n\n    if (file.open(QIODevice::ReadOnly))\n    {\n\n        const QByteArray data(file.readAll());\n        const QByteArray pattern(\"\\x21\\x00\\x1A\\x01\\x04\\x00\\x04\\x97\\xFF\\x06\", 10);\n\n        int i = 0;\n        while ((i = data.indexOf(pattern, i)) != -1) {\n\n            int next = data.indexOf(pattern, i + 1);\n            if (next == -1)\n                break;\n\n            QByteArray array(data.mid(i, (next - i)));\n\n            // Skip some invalid entries\n            if (array.contains(\"\\x04\\x96\\xFF\")) {\n                ++i;\n                continue;\n            }\n\n            // Remove the pattern from the beginning\n            array.remove(0, 12);\n\n            int index = array.indexOf(\"\\x06\");\n            const QString section(array.left(index));\n\n            // Figure how many characters to read for the key\n            int length = array.indexOf(\"\\x06\", section.length() + 3) - (section.length() + 3);\n            const QString key(array.mid(section.length() + 3, length));\n\n            QString value(array.mid(section.length() + key.length() + 6));\n\n            // Add the value\n            setValue(section + QLatin1Char('/') + key, QVariant(value));\n\n            ++i;\n        }\n\n        file.close();\n    } else {\n        qDebug() << \"Failed to open INX file: \" << path;\n        return false;\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "apps/wizard/inisettings.hpp",
    "content": "#ifndef INISETTINGS_HPP\n#define INISETTINGS_HPP\n\n#include <QHash>\n#include <QVariant>\n\nclass QTextStream;\n\nnamespace Wizard\n{\n\n    typedef QHash<QString, QVariant> SettingsMap;\n\n    class IniSettings\n    {\n    public:\n        explicit IniSettings();\n        ~IniSettings();\n\n        inline QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const\n        {\n            return mSettings.value(key, defaultValue);\n        }\n\n        inline QList<QVariant> values() const\n        {\n            return mSettings.values();\n        }\n\n        inline void setValue(const QString &key, const QVariant &value)\n        {\n            mSettings.insert(key, value);\n        }\n\n        inline void remove(const QString &key)\n        {\n            mSettings.remove(key);\n        }\n\n        QStringList findKeys(const QString &text);\n\n        bool readFile(QTextStream &stream);\n        bool writeFile(const QString &path, QTextStream &stream);\n\n        bool parseInx(const QString &path);\n\n    private:\n\n        int getLastNewline(const QString &buffer, int from);\n\n        SettingsMap mSettings;\n    };\n\n}\n\n#endif // INISETTINGS_HPP\n"
  },
  {
    "path": "apps/wizard/installationpage.cpp",
    "content": "#include \"installationpage.hpp\"\n\n#include <QDebug>\n#include <QTextCodec>\n#include <QFileInfo>\n#include <QFileDialog>\n#include <QMessageBox>\n\n#include \"mainwizard.hpp\"\n\nWizard::InstallationPage::InstallationPage(QWidget *parent) :\n    QWizardPage(parent)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n\n    mFinished = false;\n\n    mThread = new QThread();\n    mUnshield = new UnshieldWorker();\n    mUnshield->moveToThread(mThread);\n\n    connect(mThread, SIGNAL(started()),\n            mUnshield, SLOT(extract()));\n\n    connect(mUnshield, SIGNAL(finished()),\n            mThread, SLOT(quit()));\n\n    connect(mUnshield, SIGNAL(finished()),\n            this, SLOT(installationFinished()), Qt::QueuedConnection);\n\n    connect(mUnshield, SIGNAL(error(QString, QString)),\n            this, SLOT(installationError(QString, QString)), Qt::QueuedConnection);\n\n    connect(mUnshield, SIGNAL(textChanged(QString)),\n            installProgressLabel, SLOT(setText(QString)), Qt::QueuedConnection);\n\n    connect(mUnshield, SIGNAL(textChanged(QString)),\n            logTextEdit, SLOT(appendPlainText(QString)),  Qt::QueuedConnection);\n\n    connect(mUnshield, SIGNAL(textChanged(QString)),\n            mWizard, SLOT(addLogText(QString)),  Qt::QueuedConnection);\n\n    connect(mUnshield, SIGNAL(progressChanged(int)),\n            installProgressBar, SLOT(setValue(int)),  Qt::QueuedConnection);\n\n    connect(mUnshield, SIGNAL(requestFileDialog(Wizard::Component)),\n            this, SLOT(showFileDialog(Wizard::Component)), Qt::QueuedConnection);\n}\n\nWizard::InstallationPage::~InstallationPage()\n{\n    if (mThread->isRunning()) {\n        mUnshield->stopWorker();\n        mThread->quit();\n        mThread->wait();\n    }\n\n    delete mUnshield;\n    delete mThread;\n}\n\nvoid Wizard::InstallationPage::initializePage()\n{\n    QString path(field(QLatin1String(\"installation.path\")).toString());\n    QStringList components(field(QLatin1String(\"installation.components\")).toStringList());\n\n    logTextEdit->appendPlainText(QString(\"Installing to %1\").arg(path));\n    logTextEdit->appendPlainText(QString(\"Installing %1.\").arg(components.join(\", \")));\n\n    installProgressBar->setMinimum(0);\n\n    // Set the progressbar maximum to a multiple of 100\n    // That way installing all three components would yield 300%\n    // When one component is done the bar will be filled by 33%\n\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true) {\n        installProgressBar->setMaximum((components.count() * 100));\n    } else {\n        if (components.contains(QLatin1String(\"Tribunal\"))\n                && !mWizard->mInstallations[path].hasTribunal)\n            installProgressBar->setMaximum(100);\n\n        if (components.contains(QLatin1String(\"Bloodmoon\"))\n                && !mWizard->mInstallations[path].hasBloodmoon)\n            installProgressBar->setMaximum(installProgressBar->maximum() + 100);\n    }\n\n    startInstallation();\n}\n\nvoid Wizard::InstallationPage::startInstallation()\n{\n    QStringList components(field(QLatin1String(\"installation.components\")).toStringList());\n    QString path(field(QLatin1String(\"installation.path\")).toString());\n\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true)\n    {\n        // Always install Morrowind\n        mUnshield->setInstallComponent(Wizard::Component_Morrowind, true);\n\n        if (components.contains(QLatin1String(\"Tribunal\")))\n            mUnshield->setInstallComponent(Wizard::Component_Tribunal, true);\n\n        if (components.contains(QLatin1String(\"Bloodmoon\")))\n            mUnshield->setInstallComponent(Wizard::Component_Bloodmoon, true);\n    } else {\n        // Morrowind should already be installed\n        mUnshield->setInstallComponent(Wizard::Component_Morrowind, false);\n\n        if (components.contains(QLatin1String(\"Tribunal\"))\n                && !mWizard->mInstallations[path].hasTribunal)\n            mUnshield->setInstallComponent(Wizard::Component_Tribunal, true);\n\n        if (components.contains(QLatin1String(\"Bloodmoon\"))\n                && !mWizard->mInstallations[path].hasBloodmoon)\n            mUnshield->setInstallComponent(Wizard::Component_Bloodmoon, true);\n\n        // Set the location of the Morrowind.ini to update\n        mUnshield->setIniPath(mWizard->mInstallations[path].iniPath);\n        mUnshield->setupSettings();\n    }\n\n    // Set the installation target path\n    mUnshield->setPath(path);\n\n    // Set the right codec to use for Morrowind.ini\n    QString language(field(QLatin1String(\"installation.language\")).toString());\n\n    if (language == QLatin1String(\"Polish\")) {\n        mUnshield->setIniCodec(QTextCodec::codecForName(\"windows-1250\"));\n    } else if (language == QLatin1String(\"Russian\")) {\n        mUnshield->setIniCodec(QTextCodec::codecForName(\"windows-1251\"));\n    }  else {\n        mUnshield->setIniCodec(QTextCodec::codecForName(\"windows-1252\"));\n    }\n\n    mThread->start();\n}\n\nvoid Wizard::InstallationPage::showFileDialog(Wizard::Component component)\n{\n    QString name;\n    switch (component) {\n\n    case Wizard::Component_Morrowind:\n        name = QLatin1String(\"Morrowind\");\n        break;\n    case Wizard::Component_Tribunal:\n        name = QLatin1String(\"Tribunal\");\n        break;\n    case Wizard::Component_Bloodmoon:\n        name = QLatin1String(\"Bloodmoon\");\n        break;\n    }\n    logTextEdit->appendHtml(tr(\"<p>Attempting to install component %1.</p>\").arg(name));\n    mWizard->addLogText(tr(\"Attempting to install component %1.\").arg(name));\n\n    QMessageBox msgBox;\n    msgBox.setWindowTitle(tr(\"%1 Installation\").arg(name));\n    msgBox.setIcon(QMessageBox::Information);\n    msgBox.setText(QObject::tr(\"Select a valid %1 installation media.<br><b>Hint</b>: make sure that it contains at least one <b>.cab</b> file.\").arg(name));\n    msgBox.exec();\n\n    QString path = QFileDialog::getExistingDirectory(this,\n                                                     tr(\"Select %1 installation media\").arg(name),\n                                                     QDir::rootPath());\n\n    if (path.isEmpty()) {\n        logTextEdit->appendHtml(tr(\"<p><br/><span style=\\\"color:red;\\\"> \\\n                                    <b>Error: The installation was aborted by the user</b></span></p>\"));\n\n        mWizard->addLogText(QLatin1String(\"Error: The installation was aborted by the user\"));\n        mWizard->mError = true;\n\n        emit completeChanged();\n        return;\n    }\n\n    mUnshield->setDiskPath(path);\n}\n\nvoid Wizard::InstallationPage::installationFinished()\n{\n    QMessageBox msgBox;\n    msgBox.setWindowTitle(tr(\"Installation finished\"));\n    msgBox.setIcon(QMessageBox::Information);\n    msgBox.setStandardButtons(QMessageBox::Ok);\n    msgBox.setText(tr(\"Installation completed successfully!\"));\n\n    msgBox.exec();\n\n    mFinished = true;\n    emit completeChanged();\n}\n\nvoid Wizard::InstallationPage::installationError(const QString &text, const QString &details)\n{\n    installProgressLabel->setText(tr(\"Installation failed!\"));\n\n    logTextEdit->appendHtml(tr(\"<p><br/><span style=\\\"color:red;\\\"> \\\n                               <b>Error: %1</b></p>\").arg(text));\n    logTextEdit->appendHtml(tr(\"<p><span style=\\\"color:red;\\\"> \\\n                               <b>%1</b></p>\").arg(details));\n\n    mWizard->addLogText(QLatin1String(\"Error: \") + text);\n    mWizard->addLogText(details);\n\n    mWizard->mError = true;\n    QMessageBox msgBox;\n    msgBox.setWindowTitle(tr(\"An error occurred\"));\n    msgBox.setIcon(QMessageBox::Critical);\n    msgBox.setStandardButtons(QMessageBox::Ok);\n    msgBox.setText(tr(\"<html><head/><body><p><b>The Wizard has encountered an error</b></p> \\\n                      <p>The error reported was:</p><p>%1</p> \\\n                      <p>Press &quot;Show Details...&quot; for more information.</p></body></html>\").arg(text));\n\n    msgBox.setDetailedText(details);\n    msgBox.exec();\n\n\n    emit completeChanged();\n}\n\nbool Wizard::InstallationPage::isComplete() const\n{\n    if (!mWizard->mError) {\n        return mFinished;\n    } else {\n        return true;\n    }\n}\n\nint Wizard::InstallationPage::nextId() const\n{\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true) {\n        return MainWizard::Page_Conclusion;\n    } else {\n        if (!mWizard->mError) {\n            return MainWizard::Page_Import;\n        } else {\n            return MainWizard::Page_Conclusion;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/wizard/installationpage.hpp",
    "content": "#ifndef INSTALLATIONPAGE_HPP\n#define INSTALLATIONPAGE_HPP\n\n#include <QWizardPage>\n\n#include \"unshield/unshieldworker.hpp\"\n#include \"ui_installationpage.h\"\n#include \"inisettings.hpp\"\n\nclass QThread;\n\nnamespace Wizard\n{\n    class MainWizard;\n    class IniSettings;\n    class UnshieldWorker;\n\n    class InstallationPage : public QWizardPage, private Ui::InstallationPage\n    {\n        Q_OBJECT\n    public:\n        InstallationPage(QWidget *parent);\n        ~InstallationPage();\n\n        int nextId() const override;\n         bool isComplete() const override;\n\n    private:\n        MainWizard *mWizard;\n        bool mFinished;\n\n        QThread* mThread;\n        UnshieldWorker *mUnshield;\n\n        void startInstallation();\n\n    private slots:\n        void showFileDialog(Wizard::Component component);\n\n        void installationFinished();\n        void installationError(const QString &text, const QString &details);\n\n    protected:\n        void initializePage() override;\n\n    };\n\n}\n\n#endif // INSTALLATIONPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/installationtargetpage.cpp",
    "content": "#include \"installationtargetpage.hpp\"\n\n#include <QDebug>\n#include <QFileDialog>\n#include <QMessageBox>\n\n#include \"mainwizard.hpp\"\n\nWizard::InstallationTargetPage::InstallationTargetPage(QWidget *parent, const Files::ConfigurationManager &cfg) :\n    QWizardPage(parent),\n    mCfgMgr(cfg)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n\n    registerField(QLatin1String(\"installation.path*\"), targetLineEdit);\n}\n\nvoid Wizard::InstallationTargetPage::initializePage()\n{\n    QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str()));\n    path.append(QDir::separator() + QLatin1String(\"basedata\"));\n\n    QDir dir(path);\n    targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath()));\n}\n\nbool Wizard::InstallationTargetPage::validatePage()\n{\n    QString path(field(QLatin1String(\"installation.path\")).toString());\n\n    qDebug() << \"Validating path: \" << path;\n\n    if (!QFile::exists(path)) {\n        QDir dir;\n\n        if (!dir.mkpath(path)) {\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error creating destination\"));\n            msgBox.setIcon(QMessageBox::Warning);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(tr(\"<html><head/><body><p><b>Could not create the destination directory</b></p> \\\n                              <p>Please make sure you have the right permissions \\\n                              and try again, or specify a different location.</p></body></html>\"));\n            msgBox.exec();\n            return false;\n        }\n    }\n\n    QFileInfo info(path);\n\n    if (!info.isWritable()) {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Insufficient permissions\"));\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<html><head/><body><p><b>Could not write to the destination directory</b></p> \\\n                          <p>Please make sure you have the right permissions \\\n                          and try again, or specify a different location.</p></body></html>\"));\n        msgBox.exec();\n        return false;\n    }\n\n    if (mWizard->findFiles(QLatin1String(\"Morrowind\"), path)) {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Destination not empty\"));\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<html><head/><body><p><b>The destination directory is not empty</b></p> \\\n                          <p>An existing Morrowind installation is present in the specified location.</p> \\\n                          <p>Please specify a different location, or go back and select the location as an existing installation.</p></body></html>\"));\n        msgBox.exec();\n        return false;\n    }\n\n    return true;\n}\n\nvoid Wizard::InstallationTargetPage::on_browseButton_clicked()\n{\n    QString selectedPath = QFileDialog::getExistingDirectory(\n                this,\n                tr(\"Select where to install Morrowind\"),\n                QDir::homePath(),\n                QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);\n\n    qDebug() << selectedPath;\n    QFileInfo info(selectedPath);\n    if (!info.exists())\n        return;\n\n    if (info.isWritable())\n        targetLineEdit->setText(info.absoluteFilePath());\n\n}\n\nint Wizard::InstallationTargetPage::nextId() const\n{\n    return MainWizard::Page_LanguageSelection;\n}\n"
  },
  {
    "path": "apps/wizard/installationtargetpage.hpp",
    "content": "#ifndef INSTALLATIONTARGETPAGE_HPP\n#define INSTALLATIONTARGETPAGE_HPP\n\n#include \"ui_installationtargetpage.h\"\n\nnamespace Files\n{\n    struct ConfigurationManager;\n}\n\nnamespace Wizard\n{\n    class MainWizard;\n\n    class InstallationTargetPage : public QWizardPage, private Ui::InstallationTargetPage\n    {\n        Q_OBJECT\n    public:\n        InstallationTargetPage(QWidget *parent, const Files::ConfigurationManager &cfg);\n\n        int nextId() const override;\n        bool validatePage() override;\n\n    private slots:\n        void on_browseButton_clicked();\n\n    private:\n        MainWizard *mWizard;\n        const Files::ConfigurationManager &mCfgMgr;\n\n    protected:\n        void initializePage() override;\n\n    };\n\n}\n\n#endif // INSTALLATIONTARGETPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/intropage.cpp",
    "content": "#include \"intropage.hpp\"\n\n#include \"mainwizard.hpp\"\n\nWizard::IntroPage::IntroPage(QWidget *parent) :\n    QWizardPage(parent)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n    setPixmap(QWizard::WatermarkPixmap, QPixmap(QLatin1String(\":/images/intropage-background.png\")));\n}\n\nint Wizard::IntroPage::nextId() const\n{\n    return MainWizard::Page_MethodSelection;\n}\n"
  },
  {
    "path": "apps/wizard/intropage.hpp",
    "content": "#ifndef INTROPAGE_HPP\n#define INTROPAGE_HPP\n\n#include \"ui_intropage.h\"\n\nnamespace Wizard\n{\n    class MainWizard;\n\n    class IntroPage : public QWizardPage, private Ui::IntroPage\n    {\n        Q_OBJECT\n    public:\n        IntroPage(QWidget *parent);\n\n        int nextId() const override;\n\n    private:\n        MainWizard *mWizard;\n    };\n\n}\n\n#endif // INTROPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/languageselectionpage.cpp",
    "content": "#include \"languageselectionpage.hpp\"\n\n#include \"mainwizard.hpp\"\n\n#include <QDebug>\n\nWizard::LanguageSelectionPage::LanguageSelectionPage(QWidget *parent) :\n    QWizardPage(parent)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n\n    registerField(QLatin1String(\"installation.language\"), languageComboBox);\n}\n\nvoid Wizard::LanguageSelectionPage::initializePage()\n{\n    QStringList languages;\n    languages << QLatin1String(\"English\")\n              << QLatin1String(\"French\")\n              << QLatin1String(\"German\")\n              << QLatin1String(\"Italian\")\n              << QLatin1String(\"Polish\")\n              << QLatin1String(\"Russian\")\n              << QLatin1String(\"Spanish\");\n\n    languageComboBox->addItems(languages);\n}\n\nint Wizard::LanguageSelectionPage::nextId() const\n{\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true) {\n        return MainWizard::Page_ComponentSelection;\n    } else {\n        QString path(field(QLatin1String(\"installation.path\")).toString());\n\n        if (path.isEmpty())\n            return MainWizard::Page_ComponentSelection;\n\n        // Check if we have to install something\n        if (mWizard->mInstallations[path].hasMorrowind == true &&\n                mWizard->mInstallations[path].hasTribunal == true &&\n                mWizard->mInstallations[path].hasBloodmoon == true)\n        {\n            return MainWizard::Page_Import;\n        } else {\n            return MainWizard::Page_ComponentSelection;\n        }\n    }\n}\n"
  },
  {
    "path": "apps/wizard/languageselectionpage.hpp",
    "content": "#ifndef LANGUAGESELECTIONPAGE_HPP\n#define LANGUAGESELECTIONPAGE_HPP\n\n#include \"ui_languageselectionpage.h\"\n\nnamespace Wizard\n{\n    class MainWizard;\n\n    class LanguageSelectionPage : public QWizardPage, private Ui::LanguageSelectionPage\n    {\n        Q_OBJECT\n    public:\n        LanguageSelectionPage(QWidget *parent);\n\n        int nextId() const override;\n\n    private:\n        MainWizard *mWizard;\n\n    protected:\n        void initializePage() override;\n    };\n}\n\n#endif // LANGUAGESELECTIONPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/main.cpp",
    "content": "#include <QApplication>\n#include <QDir>\n\n#include \"mainwizard.hpp\"\n\n#ifdef MAC_OS_X_VERSION_MIN_REQUIRED\n#undef MAC_OS_X_VERSION_MIN_REQUIRED\n// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154\n#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__\n#endif // MAC_OS_X_VERSION_MIN_REQUIRED\n\nint main(int argc, char *argv[])\n{\n\n    QApplication app(argc, argv);\n\n    // Now we make sure the current dir is set to application path\n    QDir dir(QCoreApplication::applicationDirPath());\n\n    #ifdef Q_OS_MAC\n    // force Qt to load only LOCAL plugins, don't touch system Qt installation\n    QDir pluginsPath(QCoreApplication::applicationDirPath());\n    pluginsPath.cdUp();\n    pluginsPath.cd(\"Plugins\");\n\n    QStringList libraryPaths;\n    libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();\n    app.setLibraryPaths(libraryPaths);\n    #endif\n\n    QDir::setCurrent(dir.absolutePath());\n\n    Wizard::MainWizard wizard;\n\n    wizard.show();\n    return app.exec();\n}\n"
  },
  {
    "path": "apps/wizard/mainwizard.cpp",
    "content": "#include \"mainwizard.hpp\"\n\n#include <QDebug>\n\n#include <QTime>\n#include <QCloseEvent>\n#include <QMessageBox>\n#include <QTextCodec>\n#include <QDir>\n\n#include \"intropage.hpp\"\n#include \"methodselectionpage.hpp\"\n#include \"languageselectionpage.hpp\"\n#include \"existinginstallationpage.hpp\"\n#include \"installationtargetpage.hpp\"\n#include \"componentselectionpage.hpp\"\n#include \"importpage.hpp\"\n#include \"conclusionpage.hpp\"\n\n#ifdef OPENMW_USE_UNSHIELD\n#include \"installationpage.hpp\"\n#endif\n\nusing namespace Process;\n\nWizard::MainWizard::MainWizard(QWidget *parent) :\n    QWizard(parent),\n    mInstallations(),\n    mError(false),\n    mGameSettings(mCfgMgr)\n{\n#ifndef Q_OS_MAC\n    setWizardStyle(QWizard::ModernStyle);\n#else\n    setWizardStyle(QWizard::ClassicStyle);\n#endif\n\n    setWindowTitle(tr(\"OpenMW Wizard\"));\n    setWindowIcon(QIcon(QLatin1String(\":/images/openmw-wizard.png\")));\n    setMinimumWidth(550);\n\n    // Set the property for comboboxes to the text instead of index\n    setDefaultProperty(\"QComboBox\", \"currentText\", \"currentIndexChanged\");\n\n    setDefaultProperty(\"ComponentListWidget\", \"mCheckedItems\", \"checkedItemsChanged\");\n\n    mImporterInvoker = new ProcessInvoker();\n\n    connect(mImporterInvoker->getProcess(), SIGNAL(started()),\n            this, SLOT(importerStarted()));\n\n    connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),\n            this, SLOT(importerFinished(int,QProcess::ExitStatus)));\n\n    mLogError = tr(\"<html><head/><body><p><b>Could not open %1 for writing</b></p> \\\n                   <p>Please make sure you have the right permissions \\\n                   and try again.</p></body></html>\");\n\n    setupLog();\n    setupGameSettings();\n    setupLauncherSettings();\n    setupInstallations();\n    setupPages();\n\n    const boost::filesystem::path& installationPath = mCfgMgr.getInstallPath();\n    if (!installationPath.empty())\n    {\n        const boost::filesystem::path& dataPath = installationPath / \"Data Files\";\n        addInstallation(toQString(dataPath));\n    }\n}\n\nWizard::MainWizard::~MainWizard()\n{\n    delete mImporterInvoker;\n}\n\nvoid Wizard::MainWizard::setupLog()\n{\n    QString logPath(toQString(mCfgMgr.getLogPath()));\n    logPath.append(QLatin1String(\"wizard.log\"));\n\n    QFile file(logPath);\n\n    if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error opening Wizard log file\"));\n        msgBox.setIcon(QMessageBox::Critical);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(mLogError.arg(file.fileName()));\n        msgBox.exec();\n        return qApp->quit();\n    }\n\n    addLogText(QString(\"Started OpenMW Wizard on %1\").arg(QDateTime::currentDateTime().toString()));\n\n    qDebug() << logPath;\n}\n\nvoid Wizard::MainWizard::addLogText(const QString &text)\n{\n    QString logPath(toQString(mCfgMgr.getLogPath()));\n    logPath.append(QLatin1String(\"wizard.log\"));\n\n    QFile file(logPath);\n\n    if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error opening Wizard log file\"));\n        msgBox.setIcon(QMessageBox::Critical);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(mLogError.arg(file.fileName()));\n        msgBox.exec();\n        return qApp->quit();\n    }\n\n    if (!file.isSequential())\n        file.seek(file.size());\n\n    QTextStream out(&file);\n\n    if (!text.isEmpty())\n    {\n        out << text << \"\\n\";\n        out.flush();\n    }\n}\n\nvoid Wizard::MainWizard::setupGameSettings()\n{\n    QString userPath(toQString(mCfgMgr.getUserConfigPath()));\n    QString globalPath(toQString(mCfgMgr.getGlobalPath()));\n    QString message(tr(\"<html><head/><body><p><b>Could not open %1 for reading</b></p> \\\n                    <p>Please make sure you have the right permissions \\\n                    and try again.</p></body></html>\"));\n\n    // Load the user config file first, separately\n    // So we can write it properly, uncontaminated\n    QString path(userPath + QLatin1String(\"openmw.cfg\"));\n    QFile file(path);\n\n    qDebug() << \"Loading config file:\" << path.toUtf8().constData();\n\n    if (file.exists()) {\n        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error opening OpenMW configuration file\"));\n            msgBox.setIcon(QMessageBox::Critical);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(message.arg(file.fileName()));\n            msgBox.exec();\n            return qApp->quit();\n        }\n        QTextStream stream(&file);\n        stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n        mGameSettings.readUserFile(stream);\n    }\n\n    file.close();\n\n    // Now the rest\n    QStringList paths;\n    paths.append(userPath + QLatin1String(\"openmw.cfg\"));\n    paths.append(QLatin1String(\"openmw.cfg\"));\n    paths.append(globalPath + QLatin1String(\"openmw.cfg\"));\n\n    for (const QString &path2 : paths)\n    {\n        qDebug() << \"Loading config file:\" << path2.toUtf8().constData();\n\n        file.setFileName(path2);\n        if (file.exists()) {\n            if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n                QMessageBox msgBox;\n                msgBox.setWindowTitle(tr(\"Error opening OpenMW configuration file\"));\n                msgBox.setIcon(QMessageBox::Critical);\n                msgBox.setStandardButtons(QMessageBox::Ok);\n                msgBox.setText(message.arg(file.fileName()));\n\n                return qApp->quit();\n            }\n            QTextStream stream(&file);\n            stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n            mGameSettings.readFile(stream);\n        }\n        file.close();\n    }\n}\n\nvoid Wizard::MainWizard::setupLauncherSettings()\n{\n    QString path(toQString(mCfgMgr.getUserConfigPath()));\n    path.append(QLatin1String(Config::LauncherSettings::sLauncherConfigFileName));\n\n    QString message(tr(\"<html><head/><body><p><b>Could not open %1 for reading</b></p> \\\n                    <p>Please make sure you have the right permissions \\\n                    and try again.</p></body></html>\"));\n\n\n    QFile file(path);\n\n    qDebug() << \"Loading config file:\" << path.toUtf8().constData();\n\n    if (file.exists()) {\n        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error opening OpenMW configuration file\"));\n            msgBox.setIcon(QMessageBox::Critical);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(message.arg(file.fileName()));\n            msgBox.exec();\n            return qApp->quit();\n        }\n        QTextStream stream(&file);\n        stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n        mLauncherSettings.readFile(stream);\n    }\n\n    file.close();\n\n}\n\nvoid Wizard::MainWizard::setupInstallations()\n{\n    // Check if the paths actually contain a Morrowind installation\n    for (const QString& path : mGameSettings.getDataDirs())\n    {\n\n        if (findFiles(QLatin1String(\"Morrowind\"), path))\n            addInstallation(path);\n    }\n}\n\nvoid Wizard::MainWizard::runSettingsImporter()\n{\n    writeSettings();\n\n    QString path(field(QLatin1String(\"installation.path\")).toString());\n\n    QString userPath(toQString(mCfgMgr.getUserConfigPath()));\n    QFile file(userPath + QLatin1String(\"openmw.cfg\"));\n\n    // Construct the arguments to run the importer\n    QStringList arguments;\n\n    // Import plugin selection?\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true\n            || field(QLatin1String(\"installation.import-addons\")).toBool() == true)\n        arguments.append(QLatin1String(\"--game-files\"));\n\n    arguments.append(QLatin1String(\"--encoding\"));\n\n    // Set encoding\n    QString language(field(QLatin1String(\"installation.language\")).toString());\n\n    if (language == QLatin1String(\"Polish\")) {\n        arguments.append(QLatin1String(\"win1250\"));\n    } else if (language == QLatin1String(\"Russian\")) {\n        arguments.append(QLatin1String(\"win1251\"));\n    }  else {\n        arguments.append(QLatin1String(\"win1252\"));\n    }\n\n    // Now the paths\n    arguments.append(QLatin1String(\"--ini\"));\n\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true) {\n        arguments.append(path + QDir::separator() + QLatin1String(\"Morrowind.ini\"));\n    } else {\n        arguments.append(mInstallations[path].iniPath);\n    }\n\n    arguments.append(QLatin1String(\"--cfg\"));\n    arguments.append(userPath + QLatin1String(\"openmw.cfg\"));\n\n    if (!mImporterInvoker->startProcess(QLatin1String(\"openmw-iniimporter\"), arguments, false))\n        return qApp->quit();\n}\n\nvoid Wizard::MainWizard::addInstallation(const QString &path)\n{\n    qDebug() << \"add installation in: \" << path;\n    Installation install;// = new Installation();\n\n    install.hasMorrowind = findFiles(QLatin1String(\"Morrowind\"), path);\n    install.hasTribunal = findFiles(QLatin1String(\"Tribunal\"), path);\n    install.hasBloodmoon = findFiles(QLatin1String(\"Bloodmoon\"), path);\n\n    // Try to autodetect the Morrowind.ini location\n    QDir dir(path);\n    QFile file(dir.filePath(\"Morrowind.ini\"));\n\n    // Try the parent directory\n    // In normal Morrowind installations that's where Morrowind.ini is\n    if (!file.exists()) {\n        dir.cdUp();\n        file.setFileName(dir.filePath(QLatin1String(\"Morrowind.ini\")));\n    }\n\n    if (file.exists())\n        install.iniPath = file.fileName();\n\n    mInstallations.insert(QDir::toNativeSeparators(path), install);\n\n    // Add it to the openmw.cfg too\n    if (!mGameSettings.getDataDirs().contains(path)) {\n        mGameSettings.setMultiValue(QLatin1String(\"data\"), path);\n        mGameSettings.addDataDir(path);\n    }\n}\n\nvoid Wizard::MainWizard::setupPages()\n{\n    setPage(Page_Intro, new IntroPage(this));\n    setPage(Page_MethodSelection, new MethodSelectionPage(this));\n    setPage(Page_LanguageSelection, new LanguageSelectionPage(this));\n    setPage(Page_ExistingInstallation, new ExistingInstallationPage(this));\n    setPage(Page_InstallationTarget, new InstallationTargetPage(this, mCfgMgr));\n    setPage(Page_ComponentSelection, new ComponentSelectionPage(this));\n#ifdef OPENMW_USE_UNSHIELD\n    setPage(Page_Installation, new InstallationPage(this));\n#endif\n    setPage(Page_Import, new ImportPage(this));\n    setPage(Page_Conclusion, new ConclusionPage(this));\n    setStartId(Page_Intro);\n\n}\n\nvoid Wizard::MainWizard::importerStarted()\n{\n}\n\nvoid Wizard::MainWizard::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)\n{\n    if (exitCode != 0 || exitStatus == QProcess::CrashExit)\n        return;\n\n    // Re-read the settings\n    setupGameSettings();\n}\n\nvoid Wizard::MainWizard::accept()\n{\n    writeSettings();\n    QWizard::accept();\n}\n\nvoid Wizard::MainWizard::reject()\n{\n    QMessageBox msgBox;\n    msgBox.setWindowTitle(tr(\"Quit Wizard\"));\n    msgBox.setIcon(QMessageBox::Question);\n    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);\n    msgBox.setText(tr(\"Are you sure you want to exit the Wizard?\"));\n\n    if (msgBox.exec() == QMessageBox::Yes) {\n        QWizard::reject();\n    }\n}\n\nvoid Wizard::MainWizard::writeSettings()\n{\n    // Write the encoding and language settings\n    QString language(field(QLatin1String(\"installation.language\")).toString());\n    mLauncherSettings.setValue(QLatin1String(\"Settings/language\"), language);\n\n    if (language == QLatin1String(\"Polish\")) {\n        mGameSettings.setValue(QLatin1String(\"encoding\"), QLatin1String(\"win1250\"));\n    } else if (language == QLatin1String(\"Russian\")) {\n        mGameSettings.setValue(QLatin1String(\"encoding\"), QLatin1String(\"win1251\"));\n    }  else {\n        mGameSettings.setValue(QLatin1String(\"encoding\"), QLatin1String(\"win1252\"));\n    }\n\n    // Write the installation path so that openmw can find them\n    QString path(field(QLatin1String(\"installation.path\")).toString());\n\n    // Make sure the installation path is the last data= entry\n    mGameSettings.removeDataDir(path);\n    mGameSettings.addDataDir(path);\n\n    QString userPath(toQString(mCfgMgr.getUserConfigPath()));\n    QDir dir(userPath);\n\n    if (!dir.exists()) {\n        if (!dir.mkpath(userPath)) {\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error creating OpenMW configuration directory\"));\n            msgBox.setIcon(QMessageBox::Critical);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(tr(\"<html><head/><body><p><b>Could not create %1</b></p> \\\n                              <p>Please make sure you have the right permissions \\\n                              and try again.</p></body></html>\").arg(userPath));\n            msgBox.exec();\n            return qApp->quit();\n        }\n    }\n\n    // Game settings\n    QFile file(userPath + QLatin1String(\"openmw.cfg\"));\n\n    if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {\n        // File cannot be opened or created\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error writing OpenMW configuration file\"));\n        msgBox.setIcon(QMessageBox::Critical);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<html><head/><body><p><b>Could not open %1 for writing</b></p> \\\n                          <p>Please make sure you have the right permissions \\\n                          and try again.</p></body></html>\").arg(file.fileName()));\n        msgBox.exec();\n        return qApp->quit();\n    }\n\n    QTextStream stream(&file);\n    stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n    mGameSettings.writeFile(stream);\n    file.close();\n\n    // Launcher settings\n    file.setFileName(userPath + QLatin1String(Config::LauncherSettings::sLauncherConfigFileName));\n\n    if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {\n        // File cannot be opened or created\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error writing OpenMW configuration file\"));\n        msgBox.setIcon(QMessageBox::Critical);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<html><head/><body><p><b>Could not open %1 for writing</b></p> \\\n                          <p>Please make sure you have the right permissions \\\n                          and try again.</p></body></html>\").arg(file.fileName()));\n        msgBox.exec();\n        return qApp->quit();\n    }\n\n    stream.setDevice(&file);\n    stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n    mLauncherSettings.writeFile(stream);\n    file.close();\n}\n\nbool Wizard::MainWizard::findFiles(const QString &name, const QString &path)\n{\n    QDir dir(path);\n\n    if (!dir.exists())\n        return false;\n\n    // TODO: add MIME handling to make sure the files are real\n    return (dir.entryList().contains(name + QLatin1String(\".esm\"), Qt::CaseInsensitive)\n            && dir.entryList().contains(name + QLatin1String(\".bsa\"), Qt::CaseInsensitive));\n}\n\nQString Wizard::MainWizard::toQString(const boost::filesystem::path& path)\n{\n    return QString::fromUtf8(path.string().c_str());\n}\n"
  },
  {
    "path": "apps/wizard/mainwizard.hpp",
    "content": "#ifndef MAINWIZARD_HPP\n#define MAINWIZARD_HPP\n\n#include <QWizard>\n\n#include <components/process/processinvoker.hpp>\n\n#ifndef Q_MOC_RUN\n#include <components/files/configurationmanager.hpp>\n\n#include <components/config/gamesettings.hpp>\n#include <components/config/launchersettings.hpp>\n#endif\n\nnamespace Wizard\n{\n    class MainWizard : public QWizard\n    {\n        Q_OBJECT\n\n    public:\n        struct Installation {\n            bool hasMorrowind;\n            bool hasTribunal;\n            bool hasBloodmoon;\n\n            QString iniPath;\n        };\n\n        enum {\n            Page_Intro,\n            Page_MethodSelection,\n            Page_LanguageSelection,\n            Page_ExistingInstallation,\n            Page_InstallationTarget,\n            Page_ComponentSelection,\n            Page_Installation,\n            Page_Import,\n            Page_Conclusion\n        };\n\n        MainWizard(QWidget *parent = nullptr);\n        ~MainWizard();\n\n        bool findFiles(const QString &name, const QString &path);\n        void addInstallation(const QString &path);\n        void runSettingsImporter();\n\n        QMap<QString, Installation> mInstallations;\n\n        Files::ConfigurationManager mCfgMgr;\n\n        Process::ProcessInvoker *mImporterInvoker;\n\n        bool mError;\n\n    public slots:\n        void addLogText(const QString &text);\n\n    private:\n        /// convert boost::filesystem::path to QString\n        QString toQString(const boost::filesystem::path& path);\n\n        void setupLog();\n        void setupGameSettings();\n        void setupLauncherSettings();\n        void setupInstallations();\n        void setupPages();\n\n        void writeSettings();\n\n        Config::GameSettings mGameSettings;\n        Config::LauncherSettings mLauncherSettings;\n\n        QString mLogError;\n\n    private slots:\n\n        void importerStarted();\n        void importerFinished(int exitCode, QProcess::ExitStatus exitStatus);\n\n        void accept() override;\n        void reject() override;\n\n    };\n\n}\n\n#endif // MAINWIZARD_HPP\n"
  },
  {
    "path": "apps/wizard/methodselectionpage.cpp",
    "content": "#include \"methodselectionpage.hpp\"\n#include \"mainwizard.hpp\"\n\n#include <QUrl>\n#include <QDesktopServices>\n\nWizard::MethodSelectionPage::MethodSelectionPage(QWidget *parent) :\n    QWizardPage(parent)\n{\n    mWizard = qobject_cast<MainWizard*>(parent);\n\n    setupUi(this);\n\n#ifndef OPENMW_USE_UNSHIELD\n    retailDiscRadioButton->setEnabled(false);\n    existingLocationRadioButton->setChecked(true);\n    buyLinkButton->released();\n#endif\n    \n    registerField(QLatin1String(\"installation.retailDisc\"), retailDiscRadioButton);\n    \n    connect(buyLinkButton, SIGNAL(released()), this, SLOT(handleBuyButton()));\n}\n\nint Wizard::MethodSelectionPage::nextId() const\n{\n    if (field(QLatin1String(\"installation.retailDisc\")).toBool() == true) {\n        return MainWizard::Page_InstallationTarget;\n    } else {\n        return MainWizard::Page_ExistingInstallation;\n    }\n}\n\nvoid Wizard::MethodSelectionPage::handleBuyButton()\n{\n    QDesktopServices::openUrl(QUrl(\"https://openmw.org/faq/#do_i_need_morrowind\"));\n}\n"
  },
  {
    "path": "apps/wizard/methodselectionpage.hpp",
    "content": "#ifndef METHODSELECTIONPAGE_HPP\n#define METHODSELECTIONPAGE_HPP\n\n#include \"ui_methodselectionpage.h\"\n\nnamespace Wizard\n{\n    class MainWizard;\n\n    class MethodSelectionPage : public QWizardPage, private Ui::MethodSelectionPage\n    {\n        Q_OBJECT\n    public:\n        MethodSelectionPage(QWidget *parent);\n\n        int nextId() const override;\n\n    private slots:\n        void handleBuyButton();\n        \n    private:\n        MainWizard *mWizard;\n\n    };\n\n}\n\n#endif // METHODSELECTIONPAGE_HPP\n"
  },
  {
    "path": "apps/wizard/unshield/unshieldworker.cpp",
    "content": "#include \"unshieldworker.hpp\"\n\n#include <QDebug>\n\n#include <QReadLocker>\n#include <QWriteLocker>\n#include <QFileDialog>\n#include <QFileInfo>\n#include <QStringList>\n#include <QTextStream>\n#include <QTextCodec>\n#include <QFile>\n#include <QDir>\n#include <QDirIterator>\n\nWizard::UnshieldWorker::UnshieldWorker(QObject *parent) :\n    QObject(parent),\n    mIniSettings()\n{\n    unshield_set_log_level(0);\n\n    mPath = QString();\n    mIniPath = QString();\n    mDiskPath = QString();\n\n    // Default to Latin encoding\n    mIniCodec = QTextCodec::codecForName(\"windows-1252\");\n\n    mInstallMorrowind = false;\n    mInstallTribunal = false;\n    mInstallBloodmoon = false;\n\n    mMorrowindDone = false;\n    mTribunalDone = false;\n    mBloodmoonDone = false;\n\n    mStopped = false;\n\n    qRegisterMetaType<Wizard::Component>(\"Wizard::Component\");\n}\n\nWizard::UnshieldWorker::~UnshieldWorker()\n{\n}\n\nvoid Wizard::UnshieldWorker::stopWorker()\n{\n    mStopped = true;\n    mWait.wakeOne();\n}\n\nvoid Wizard::UnshieldWorker::setInstallComponent(Wizard::Component component, bool install)\n{\n    QWriteLocker writeLock(&mLock);\n    switch (component) {\n\n    case Wizard::Component_Morrowind:\n        mInstallMorrowind = install;\n        break;\n    case Wizard::Component_Tribunal:\n        mInstallTribunal = install;\n        break;\n    case Wizard::Component_Bloodmoon:\n        mInstallBloodmoon = install;\n        break;\n    }\n}\n\nbool Wizard::UnshieldWorker::getInstallComponent(Component component)\n{\n    QReadLocker readLock(&mLock);\n    switch (component) {\n\n    case Wizard::Component_Morrowind:\n        return mInstallMorrowind;\n    case Wizard::Component_Tribunal:\n        return mInstallTribunal;\n    case Wizard::Component_Bloodmoon:\n        return mInstallBloodmoon;\n    }\n\n    return false;\n}\n\nvoid Wizard::UnshieldWorker::setComponentDone(Component component, bool done)\n{\n    QWriteLocker writeLock(&mLock);\n    switch (component) {\n\n    case Wizard::Component_Morrowind:\n        mMorrowindDone = done;\n        break;\n    case Wizard::Component_Tribunal:\n        mTribunalDone = done;\n        break;\n    case Wizard::Component_Bloodmoon:\n        mBloodmoonDone = done;\n        break;\n    }\n}\n\nbool Wizard::UnshieldWorker::getComponentDone(Component component)\n{\n    QReadLocker readLock(&mLock);\n    switch (component)\n    {\n\n    case Wizard::Component_Morrowind:\n        return mMorrowindDone;\n    case Wizard::Component_Tribunal:\n        return mTribunalDone;\n    case Wizard::Component_Bloodmoon:\n        return mBloodmoonDone;\n    }\n\n    return false;\n}\n\nvoid Wizard::UnshieldWorker::setPath(const QString &path)\n{\n    QWriteLocker writeLock(&mLock);\n    mPath = path;\n}\n\nvoid Wizard::UnshieldWorker::setIniPath(const QString &path)\n{\n    QWriteLocker writeLock(&mLock);\n    mIniPath = path;\n}\n\nvoid Wizard::UnshieldWorker::setDiskPath(const QString &path)\n{\n    QWriteLocker writeLock(&mLock);\n    mDiskPath = path;\n    mWait.wakeAll();\n}\n\nQString Wizard::UnshieldWorker::getPath()\n{\n    QReadLocker readLock(&mLock);\n    return mPath;\n}\n\nQString Wizard::UnshieldWorker::getIniPath()\n{\n    QReadLocker readLock(&mLock);\n    return mIniPath;\n}\n\nQString Wizard::UnshieldWorker::getDiskPath()\n{\n    QReadLocker readLock(&mLock);\n    return mDiskPath;\n}\n\n\nvoid Wizard::UnshieldWorker::setIniCodec(QTextCodec *codec)\n{\n    QWriteLocker writeLock(&mLock);\n    mIniCodec = codec;\n}\n\nbool Wizard::UnshieldWorker::setupSettings()\n{\n    // Create Morrowind.ini settings map\n    if (getIniPath().isEmpty())\n        return false;\n\n    QFile file(getIniPath());\n\n    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n        emit error(tr(\"Failed to open Morrowind configuration file!\"),\n                   tr(\"Opening %1 failed: %2.\").arg(getIniPath(), file.errorString()));\n        return false;\n    }\n\n    QTextStream stream(&file);\n    stream.setCodec(mIniCodec);\n\n    mIniSettings.readFile(stream);\n\n    return true;\n}\n\nbool Wizard::UnshieldWorker::writeSettings()\n{\n    if (getIniPath().isEmpty())\n        return false;\n\n    QFile file(getIniPath());\n\n    if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {\n        emit error(tr(\"Failed to open Morrowind configuration file!\"),\n                   tr(\"Opening %1 failed: %2.\").arg(getIniPath(), file.errorString()));\n        return false;\n    }\n\n    QTextStream stream(&file);\n    stream.setCodec(mIniCodec);\n\n    if (!mIniSettings.writeFile(getIniPath(), stream)) {\n         emit error(tr(\"Failed to write Morrowind configuration file!\"),\n                    tr(\"Writing to %1 failed: %2.\").arg(getIniPath(), file.errorString()));\n         return false;\n    }\n\n    return true;\n}\n\nbool Wizard::UnshieldWorker::removeDirectory(const QString &dirName)\n{\n    bool result = false;\n    QDir dir(dirName);\n\n    if (dir.exists(dirName))\n    {\n        QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot |\n                                               QDir::System | QDir::Hidden |\n                                               QDir::AllDirs | QDir::Files, QDir::DirsFirst));\n        for (const QFileInfo& info : list)\n        {\n            if (info.isDir()) {\n                result = removeDirectory(info.absoluteFilePath());\n            } else {\n                result = QFile::remove(info.absoluteFilePath());\n            }\n\n            if (!result)\n                return result;\n        }\n\n        result = dir.rmdir(dirName);\n    }\n    return result;\n}\n\nbool Wizard::UnshieldWorker::copyFile(const QString &source, const QString &destination, bool keepSource)\n{\n    QDir dir;\n    QFile file;\n\n    QFileInfo info(destination);\n\n    if (info.exists()) {\n        if (!dir.remove(info.absoluteFilePath()))\n            return false;\n    }\n\n    if (file.copy(source, destination)) {\n        if (!keepSource) {\n            if (!file.remove(source))\n                return false;\n        } else {\n            return true;\n        }\n    } else {\n        return false;\n    }\n\n    return true;\n}\n\nbool Wizard::UnshieldWorker::copyDirectory(const QString &source, const QString &destination, bool keepSource)\n{\n    QDir sourceDir(source);\n    QDir destDir(destination);\n    bool result = true;\n\n    if (!destDir.exists()) {\n        if (!sourceDir.mkpath(destination))\n            return false;\n    }\n\n    destDir.refresh();\n\n    if (!destDir.exists())\n        return false;\n\n    QFileInfoList list(sourceDir.entryInfoList(QDir::NoDotAndDotDot |\n                                                 QDir::System | QDir::Hidden |\n                                                 QDir::AllDirs | QDir::Files, QDir::DirsFirst));\n\n    for (const QFileInfo &info : list)\n    {\n        QString relativePath(info.absoluteFilePath());\n        relativePath.remove(source);\n\n        QString destinationPath(destDir.absolutePath() + relativePath);\n\n        if (info.isSymLink())\n            continue;\n\n        if (info.isDir()) {\n            result = copyDirectory(info.absoluteFilePath(), destinationPath);\n        } else {\n            result = copyFile(info.absoluteFilePath(), destinationPath);\n        }\n    }\n\n    if (!keepSource)\n        return result && removeDirectory(sourceDir.absolutePath());\n\n    return result;\n}\n\nbool Wizard::UnshieldWorker::installFile(const QString &fileName, const QString &path, Qt::MatchFlags flags, bool keepSource)\n{\n    return installFiles(fileName, path, flags, keepSource, true);\n}\n\nbool Wizard::UnshieldWorker::installFiles(const QString &fileName, const QString &path, Qt::MatchFlags flags, bool keepSource, bool single)\n{\n    QDir dir(path);\n\n    if (!dir.exists())\n        return false;\n\n    QStringList files(findFiles(fileName, path, flags));\n\n    for (const QString &file : files)\n    {\n        QFileInfo info(file);\n        emit textChanged(tr(\"Installing: %1\").arg(info.fileName()));\n\n        if (single) {\n            return copyFile(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource);\n        } else {\n            if (!copyFile(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource))\n                return false;\n        }\n    }\n\n    return true;\n}\n\nbool Wizard::UnshieldWorker::installDirectories(const QString &dirName, const QString &path, bool recursive, bool keepSource)\n{\n    QDir dir(path);\n\n    if (!dir.exists())\n        return false;\n\n    QStringList directories(findDirectories(dirName, path, recursive));\n\n    for (const QString &dir : directories)\n    {\n        QFileInfo info(dir);\n        emit textChanged(tr(\"Installing: %1 directory\").arg(info.fileName()));\n        if (!copyDirectory(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource))\n            return false;\n    }\n\n    return true;\n}\n\nvoid Wizard::UnshieldWorker::extract()\n{\n    if (getInstallComponent(Wizard::Component_Morrowind))\n    {\n        if (!getComponentDone(Wizard::Component_Morrowind))\n            if (!setupComponent(Wizard::Component_Morrowind))\n                return;\n    }\n\n    if (getInstallComponent(Wizard::Component_Tribunal))\n    {\n        if (!getComponentDone(Wizard::Component_Tribunal))\n            if (!setupComponent(Wizard::Component_Tribunal))\n                return;\n    }\n\n    if (getInstallComponent(Wizard::Component_Bloodmoon))\n    {\n        if (!getComponentDone(Wizard::Component_Bloodmoon))\n            if (!setupComponent(Wizard::Component_Bloodmoon))\n                return;\n    }\n\n    // Update Morrowind configuration\n    if (getInstallComponent(Wizard::Component_Tribunal))\n    {\n        mIniSettings.setValue(QLatin1String(\"Archives/Archive 0\"), QVariant(QString(\"Tribunal.bsa\")));\n        mIniSettings.setValue(QLatin1String(\"Game Files/GameFile1\"), QVariant(QString(\"Tribunal.esm\")));\n    }\n\n    if (getInstallComponent(Wizard::Component_Bloodmoon))\n    {\n        mIniSettings.setValue(QLatin1String(\"Archives/Archive 0\"), QVariant(QString(\"Bloodmoon.bsa\")));\n        mIniSettings.setValue(QLatin1String(\"Game Files/GameFile1\"), QVariant(QString(\"Bloodmoon.esm\")));\n    }\n\n    if (getInstallComponent(Wizard::Component_Tribunal) &&\n            getInstallComponent(Wizard::Component_Bloodmoon))\n    {\n        mIniSettings.setValue(QLatin1String(\"Archives/Archive 0\"), QVariant(QString(\"Tribunal.bsa\")));\n        mIniSettings.setValue(QLatin1String(\"Archives/Archive 1\"), QVariant(QString(\"Bloodmoon.bsa\")));\n        mIniSettings.setValue(QLatin1String(\"Game Files/GameFile1\"), QVariant(QString(\"Tribunal.esm\")));\n        mIniSettings.setValue(QLatin1String(\"Game Files/GameFile2\"), QVariant(QString(\"Bloodmoon.esm\")));\n    }\n\n    // Write the settings to the Morrowind config file\n    if (!writeSettings())\n        return;\n\n    // Remove the temporary directory\n    removeDirectory(getPath() + QDir::separator() + QLatin1String(\"extract-temp\"));\n\n    // Fill the progress bar\n    int total = 0;\n\n    if (getInstallComponent(Wizard::Component_Morrowind))\n        total = 100;\n\n    if (getInstallComponent(Wizard::Component_Tribunal))\n        total = total + 100;\n\n    if (getInstallComponent(Wizard::Component_Bloodmoon))\n        total = total + 100;\n\n    emit textChanged(tr(\"Installation finished!\"));\n    emit progressChanged(total);\n    emit finished();\n}\n\nbool Wizard::UnshieldWorker::setupComponent(Component component)\n{\n    QString name;\n    switch (component) {\n\n    case Wizard::Component_Morrowind:\n        name = QLatin1String(\"Morrowind\");\n        break;\n    case Wizard::Component_Tribunal:\n        name = QLatin1String(\"Tribunal\");\n        break;\n    case Wizard::Component_Bloodmoon:\n        name = QLatin1String(\"Bloodmoon\");\n        break;\n    }\n\n    if (name.isEmpty()) {\n        emit error(tr(\"Component parameter is invalid!\"), tr(\"An invalid component parameter was supplied.\"));\n        return false;\n    }\n\n    bool found = false;\n    QString cabFile;\n    QDir disk;\n\n    // Keep showing the file dialog until we find the necessary install files\n    while (!found) {\n        if (getDiskPath().isEmpty()) {\n            QReadLocker readLock(&mLock);\n            emit requestFileDialog(component);\n            mWait.wait(&mLock);\n            if(mStopped) {\n                qDebug() << \"We are asked to stop !!\";\n                break;\n            }\n            disk.setPath(getDiskPath());\n        } else {\n            disk.setPath(getDiskPath());\n        }\n\n        QStringList list(findFiles(QLatin1String(\"data1.hdr\"), disk.absolutePath()));\n\n        for (const QString &file : list)\n        {\n\n            qDebug() << \"current archive: \" << file;\n\n            if (component == Wizard::Component_Morrowind)\n            {\n                bool morrowindFound = findInCab(QLatin1String(\"Morrowind.bsa\"), file);\n                bool tribunalFound = findInCab(QLatin1String(\"Tribunal.bsa\"), file);\n                bool bloodmoonFound = findInCab(QLatin1String(\"Bloodmoon.bsa\"), file);\n\n                if (morrowindFound) {\n                    // Check if we have correct archive, other archives have Morrowind.bsa too\n                    if (tribunalFound == bloodmoonFound)\n                    {\n                        cabFile = file;\n                        found = true; // We have a GoTY disk or a Morrowind-only disk\n                    }\n                }\n            } else {\n\n                if (findInCab(name + QLatin1String(\".bsa\"), file)) {\n                    cabFile = file;\n                    found = true;\n                }\n            }\n\n        }\n\n        if (!found)\n        {\n            emit textChanged(tr(\"Failed to find a valid archive containing %1.bsa! Retrying.\").arg(name));\n            QReadLocker readLock(&mLock);\n            emit requestFileDialog(component);\n            mWait.wait(&mLock);\n        }\n    }\n\n    if (installComponent(component, cabFile)) {\n        setComponentDone(component, true);\n        return true;\n    } else {\n        return false;\n    }\n\n    return true;\n}\n\nbool Wizard::UnshieldWorker::installComponent(Component component, const QString &path)\n{\n    QString name;\n    switch (component) {\n\n    case Wizard::Component_Morrowind:\n        name = QLatin1String(\"Morrowind\");\n        break;\n    case Wizard::Component_Tribunal:\n        name = QLatin1String(\"Tribunal\");\n        break;\n    case Wizard::Component_Bloodmoon:\n        name = QLatin1String(\"Bloodmoon\");\n        break;\n    }\n\n    if (name.isEmpty()) {\n        emit error(tr(\"Component parameter is invalid!\"), tr(\"An invalid component parameter was supplied.\"));\n        return false;\n    }\n\n    emit textChanged(tr(\"Installing %1\").arg(name));\n\n    QFileInfo info(path);\n\n    if (!info.exists()) {\n        emit error(tr(\"Installation media path not set!\"), tr(\"The source path for %1 was not set.\").arg(name));\n        return false;\n    }\n\n    // Create temporary extract directory\n    // TODO: Use QTemporaryDir in Qt 5.0\n    QString tempPath(getPath() + QDir::separator() + QLatin1String(\"extract-temp\"));\n    QDir temp;\n\n    // Make sure the temporary folder is empty\n    removeDirectory(tempPath);\n\n    if (!temp.mkpath(tempPath)) {\n        emit error(tr(\"Cannot create temporary directory!\"), tr(\"Failed to create %1.\").arg(tempPath));\n        return false;\n    }\n\n    temp.setPath(tempPath);\n\n    if (!temp.mkdir(name)) {\n        emit error(tr(\"Cannot create temporary directory!\"), tr(\"Failed to create %1.\").arg(temp.absoluteFilePath(name)));\n        return false;\n    }\n\n    if (!temp.cd(name)) {\n        emit error(tr(\"Cannot move into temporary directory!\"), tr(\"Failed to move into %1.\").arg(temp.absoluteFilePath(name)));\n        return false;\n    }\n\n    // Extract the installation files\n    if (!extractCab(info.absoluteFilePath(), temp.absolutePath()))\n        return false;\n\n    // Move the files from the temporary path to the destination folder\n    emit textChanged(tr(\"Moving installation files\"));\n\n    // Install extracted directories\n    QStringList directories;\n    directories << QLatin1String(\"BookArt\")\n                << QLatin1String(\"Fonts\")\n                << QLatin1String(\"Icons\")\n                << QLatin1String(\"Meshes\")\n                << QLatin1String(\"Music\")\n                << QLatin1String(\"Sound\")\n                << QLatin1String(\"Splash\")\n                << QLatin1String(\"Textures\")\n                << QLatin1String(\"Video\");\n\n    for (const QString &dir : directories)\n    {\n        if (!installDirectories(dir, temp.absolutePath())) {\n            emit error(tr(\"Could not install directory!\"),\n                       tr(\"Installing %1 to %2 failed.\").arg(dir, temp.absolutePath()));\n            return false;\n        }\n    }\n\n    // Install directories from disk\n    for (const QString &dir : directories)\n    {\n        if (!installDirectories(dir, info.absolutePath(), false, true)) {\n            emit error(tr(\"Could not install directory!\"),\n                       tr(\"Installing %1 to %2 failed.\").arg(dir, info.absolutePath()));\n            return false;\n        }\n\n    }\n\n    // Install translation files\n    QStringList extensions;\n    extensions << QLatin1String(\".cel\")\n               << QLatin1String(\".top\")\n               << QLatin1String(\".mrk\");\n\n    for (const QString &extension : extensions)\n    {\n        if (!installFiles(extension, info.absolutePath(), Qt::MatchEndsWith)) {\n            emit error(tr(\"Could not install translation file!\"),\n                       tr(\"Failed to install *%1 files.\").arg(extension));\n            return false;\n        }\n    }\n\n    if (component == Wizard::Component_Morrowind)\n    {\n        QStringList files;\n        files << QLatin1String(\"Morrowind.esm\")\n              << QLatin1String(\"Morrowind.bsa\");\n\n        for (const QString &file : files)\n        {\n            if (!installFile(file, temp.absolutePath())) {\n                emit error(tr(\"Could not install Morrowind data file!\"),\n                           tr(\"Failed to install %1.\").arg(file));\n                return false;\n            }\n        }\n\n        // Copy Morrowind configuration file\n        if (!installFile(QLatin1String(\"Morrowind.ini\"), temp.absolutePath())) {\n            emit error(tr(\"Could not install Morrowind configuration file!\"),\n                       tr(\"Failed to install %1.\").arg(QLatin1String(\"Morrowind.ini\")));\n            return false;\n        }\n\n        // Setup Morrowind configuration\n        setIniPath(getPath() + QDir::separator() + QLatin1String(\"Morrowind.ini\"));\n\n        if (!setupSettings())\n            return false;\n    }\n\n    if (component == Wizard::Component_Tribunal)\n    {\n        QFileInfo sounds(temp.absoluteFilePath(QLatin1String(\"Sounds\")));\n        QString dest(getPath() + QDir::separator() + QLatin1String(\"Sound\"));\n\n        if (sounds.exists()) {\n            emit textChanged(tr(\"Installing: Sound directory\"));\n            if (!copyDirectory(sounds.absoluteFilePath(), dest)) {\n                emit error(tr(\"Could not install directory!\"),\n                           tr(\"Installing %1 to %2 failed.\").arg(sounds.absoluteFilePath(), dest));\n                return false;\n            }\n\n        }\n\n        QStringList files;\n        files << QLatin1String(\"Tribunal.esm\")\n              << QLatin1String(\"Tribunal.bsa\");\n\n        for (const QString &file : files)\n        {\n            if (!installFile(file, temp.absolutePath())) {\n                emit error(tr(\"Could not find Tribunal data file!\"),\n                           tr(\"Failed to find %1.\").arg(file));\n                return false;\n            }\n        }\n    }\n\n    if (component == Wizard::Component_Bloodmoon)\n    {\n        QFileInfo original(getPath() + QDir::separator() + QLatin1String(\"Tribunal.esm\"));\n\n        if (original.exists()) {\n            if (!installFile(QLatin1String(\"Tribunal.esm\"), temp.absolutePath())) {\n                emit error(tr(\"Could not find Tribunal patch file!\"),\n                           tr(\"Failed to find %1.\").arg(QLatin1String(\"Tribunal.esm\")));\n                return false;\n            }\n        }\n\n        QStringList files;\n        files << QLatin1String(\"Bloodmoon.esm\")\n              << QLatin1String(\"Bloodmoon.bsa\");\n\n        for (const QString &file : files)\n        {\n            if (!installFile(file, temp.absolutePath())) {\n                emit error(tr(\"Could not find Bloodmoon data file!\"),\n                           tr(\"Failed to find %1.\").arg(file));\n                return false;\n            }\n        }\n\n        // Load Morrowind configuration settings from the setup script\n        QStringList list(findFiles(QLatin1String(\"setup.inx\"), getDiskPath()));\n\n        emit textChanged(tr(\"Updating Morrowind configuration file\"));\n\n        for (const QString &inx : list)\n        {\n             mIniSettings.parseInx(inx);\n        }\n    }\n\n    // Finally, install Data Files directories from temp and disk\n    QStringList datafiles(findDirectories(QLatin1String(\"Data Files\"), temp.absolutePath()));\n    datafiles.append(findDirectories(QLatin1String(\"Data Files\"), info.absolutePath()));\n\n    for (const QString &dir : datafiles)\n    {\n        QFileInfo info(dir);\n        emit textChanged(tr(\"Installing: %1 directory\").arg(info.fileName()));\n\n        if (!copyDirectory(info.absoluteFilePath(), getPath())) {\n            emit error(tr(\"Could not install directory!\"),\n                       tr(\"Installing %1 to %2 failed.\").arg(info.absoluteFilePath(), getPath()));\n            return false;\n        }\n    }\n\n    emit textChanged(tr(\"%1 installation finished!\").arg(name));\n    return true;\n\n}\n\nbool Wizard::UnshieldWorker::extractFile(Unshield *unshield, const QString &destination, const QString &prefix, int index, int counter)\n{\n    bool success = false;\n    QString path(destination);\n    path.append(QDir::separator());\n\n    int directory = unshield_file_directory(unshield, index);\n\n    if (!prefix.isEmpty())\n        path.append(prefix + QDir::separator());\n\n    if (directory >= 0)\n        path.append(QString::fromUtf8(unshield_directory_name(unshield, directory)) + QDir::separator());\n\n    // Ensure the path has the right separators\n    path.replace(QLatin1Char('\\\\'), QDir::separator());\n    path = QDir::toNativeSeparators(path);\n\n    // Ensure the target path exists\n    QDir dir;\n    if (!dir.mkpath(path))\n        return false;\n\n    QString fileName(path);\n    fileName.append(QString::fromUtf8(unshield_file_name(unshield, index)));\n\n    // Calculate the percentage done\n    int progress = (((float) counter / (float) unshield_file_count(unshield)) * 100);\n\n    if (getComponentDone(Wizard::Component_Morrowind))\n        progress = progress + 100;\n\n    if (getComponentDone(Wizard::Component_Tribunal))\n        progress = progress + 100;\n\n    emit textChanged(tr(\"Extracting: %1\").arg(QString::fromUtf8(unshield_file_name(unshield, index))));\n    emit progressChanged(progress);\n\n    QByteArray array(fileName.toUtf8());\n    success = unshield_file_save(unshield, index, array.constData());\n\n    if (!success) {\n        qDebug() << \"error\";\n        dir.remove(fileName);\n    }\n\n    return success;\n}\n\nbool Wizard::UnshieldWorker::extractCab(const QString &cabFile, const QString &destination)\n{\n    bool success = false;\n\n    QByteArray array(cabFile.toUtf8());\n\n    Unshield *unshield;\n    unshield = unshield_open(array.constData());\n\n    if (!unshield) {\n        emit error(tr(\"Failed to open InstallShield Cabinet File.\"), tr(\"Opening %1 failed.\").arg(cabFile));\n        unshield_close(unshield);\n        return false;\n    }\n\n    int counter = 0;\n\n    for (int i=0; i<unshield_file_group_count(unshield); ++i)\n    {\n        UnshieldFileGroup *group = unshield_file_group_get(unshield, i);\n\n        for (size_t j=group->first_file; j<=group->last_file; ++j)\n        {\n            if (mStopped) {\n                qDebug() << \"We're asked to stop!\";\n\n                unshield_close(unshield);\n                return true;\n            }\n\n            if (unshield_file_is_valid(unshield, j)) {\n                success = extractFile(unshield, destination, group->name, j, counter);\n\n                if (!success) {\n                    QString name(QString::fromUtf8(unshield_file_name(unshield, j)));\n\n                    emit error(tr(\"Failed to extract %1.\").arg(name),\n                               tr(\"Complete path: %1\").arg(destination + QDir::separator() + name));\n\n                    unshield_close(unshield);\n                    return false;\n                }\n\n                ++counter;\n            }\n        }\n    }\n\n    unshield_close(unshield);\n    return success;\n}\n\nQString Wizard::UnshieldWorker::findFile(const QString &fileName, const QString &path)\n{\n    return findFiles(fileName, path).first();\n}\n\nQStringList Wizard::UnshieldWorker::findFiles(const QString &fileName, const QString &path, int depth, bool recursive,\n                                              bool directories, Qt::MatchFlags flags)\n{\n    static const int MAXIMUM_DEPTH = 10;\n\n    if (depth >= MAXIMUM_DEPTH) {\n        qWarning(\"Maximum directory depth limit reached.\");\n        return QStringList();\n    }\n\n    QStringList result;\n    QDir dir(path);\n\n    // Prevent parsing over the complete filesystem\n    if (dir == QDir::rootPath())\n        return QStringList();\n\n    if (!dir.exists())\n        return QStringList();\n\n    QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot |\n                                         QDir::AllDirs | QDir::Files, QDir::DirsFirst));\n    for (const QFileInfo& info : list)\n    {\n        if (info.isSymLink())\n            continue;\n\n        if (info.isDir()) {\n            if (directories)\n            {\n                if (!info.fileName().compare(fileName, Qt::CaseInsensitive)) {\n                    result.append(info.absoluteFilePath());\n                } else {\n                    if (recursive)\n                        result.append(findFiles(fileName, info.absoluteFilePath(), depth + 1, recursive, true));\n                }\n            } else {\n                if (recursive)\n                    result.append(findFiles(fileName, info.absoluteFilePath(), depth + 1));\n            }\n        } else {\n            if (directories)\n                break;\n\n            switch (flags) {\n            case Qt::MatchExactly:\n                if (!info.fileName().compare(fileName, Qt::CaseInsensitive))\n                    result.append(info.absoluteFilePath());\n                break;\n            case Qt::MatchEndsWith:\n                if (info.fileName().endsWith(fileName, Qt::CaseInsensitive))\n                    result.append(info.absoluteFilePath());\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\nQStringList Wizard::UnshieldWorker::findDirectories(const QString &dirName, const QString &path, bool recursive)\n{\n    return findFiles(dirName, path, 0, true, true);\n}\n\nbool Wizard::UnshieldWorker::findInCab(const QString &fileName, const QString &cabFile)\n{\n    QByteArray array(cabFile.toUtf8());\n\n    Unshield *unshield;\n    unshield = unshield_open(array.constData());\n\n    if (!unshield) {\n        emit error(tr(\"Failed to open InstallShield Cabinet File.\"), tr(\"Opening %1 failed.\").arg(cabFile));\n        unshield_close(unshield);\n        return false;\n    }\n\n    for (int i=0; i<unshield_file_group_count(unshield); ++i)\n    {\n        UnshieldFileGroup *group = unshield_file_group_get(unshield, i);\n\n        for (size_t j=group->first_file; j<=group->last_file; ++j)\n        {\n\n            if (unshield_file_is_valid(unshield, j)) {\n                QString current(QString::fromUtf8(unshield_file_name(unshield, j)));\n                if (current.toLower() == fileName.toLower()) {\n                    unshield_close(unshield);\n                    return true; // File is found!\n                }\n            }\n        }\n    }\n\n    unshield_close(unshield);\n    return false;\n}\n"
  },
  {
    "path": "apps/wizard/unshield/unshieldworker.hpp",
    "content": "#ifndef UNSHIELDWORKER_HPP\n#define UNSHIELDWORKER_HPP\n\n#include <QObject>\n#include <QThread>\n#include <QMutex>\n#include <QWaitCondition>\n#include <QReadWriteLock>\n#include <QStringList>\n\n#include <libunshield.h>\n\n#include \"../inisettings.hpp\"\n\n\nnamespace Wizard\n{\n    enum Component {\n        Component_Morrowind,\n        Component_Tribunal,\n        Component_Bloodmoon\n    };\n\n    class UnshieldWorker : public QObject\n    {\n        Q_OBJECT\n\n    public:\n        UnshieldWorker(QObject *parent = nullptr);\n        ~UnshieldWorker();\n\n        void stopWorker();\n\n        void setInstallComponent(Wizard::Component component, bool install);\n\n        void setDiskPath(const QString &path);\n\n        void setPath(const QString &path);\n        void setIniPath(const QString &path);\n\n        QString getPath();\n        QString getIniPath();\n\n        void setIniCodec(QTextCodec *codec);\n\n        bool setupSettings();\n\n    private:\n\n        bool writeSettings();\n\n        bool getInstallComponent(Component component);\n\n        QString getDiskPath();\n\n        void setComponentDone(Component component, bool done = true);\n        bool getComponentDone(Component component);\n\n        bool removeDirectory(const QString &dirName);\n\n        bool copyFile(const QString &source, const QString &destination, bool keepSource = true);\n        bool copyDirectory(const QString &source, const QString &destination, bool keepSource = true);\n\n        bool extractCab(const QString &cabFile, const QString &destination);\n        bool extractFile(Unshield *unshield, const QString &destination, const QString &prefix, int index, int counter);\n\n        bool findInCab(const QString &fileName, const QString &cabFile);\n\n        QString findFile(const QString &fileName, const QString &path);\n\n        QStringList findFiles(const QString &fileName, const QString &path, int depth = 0, bool recursive = true,\n                              bool directories = false, Qt::MatchFlags flags = Qt::MatchExactly);\n\n        QStringList findDirectories(const QString &dirName, const QString &path, bool recursive = true);\n\n        bool installFile(const QString &fileName, const QString &path, Qt::MatchFlags flags = Qt::MatchExactly,\n                         bool keepSource = false);\n\n        bool installFiles(const QString &fileName, const QString &path, Qt::MatchFlags flags = Qt::MatchExactly,\n                          bool keepSource = false, bool single = false);\n\n        bool installDirectories(const QString &dirName, const QString &path,\n                                bool recursive = true, bool keepSource = false);\n\n        bool installComponent(Component component, const QString &path);\n        bool setupComponent(Component component);\n\n        bool mInstallMorrowind;\n        bool mInstallTribunal;\n        bool mInstallBloodmoon;\n\n        bool mMorrowindDone;\n        bool mTribunalDone;\n        bool mBloodmoonDone;\n\n        bool mStopped;\n\n        QString mPath;\n        QString mIniPath;\n        QString mDiskPath;\n\n        IniSettings mIniSettings;\n\n        QTextCodec *mIniCodec;\n\n        QWaitCondition mWait;\n\n        QReadWriteLock mLock;\n\n    public slots:\n        void extract();\n\n    signals:\n        void finished();\n        void requestFileDialog(Wizard::Component component);\n\n        void textChanged(const QString &text);\n\n        void error(const QString &text, const QString &details);\n        void progressChanged(int progress);\n\n    };\n}\n\n#endif // UNSHIELDWORKER_HPP\n"
  },
  {
    "path": "apps/wizard/utils/componentlistwidget.cpp",
    "content": "#include \"componentlistwidget.hpp\"\n\n#include <QDebug>\n\nComponentListWidget::ComponentListWidget(QWidget *parent) :\n    QListWidget(parent)\n{\n    mCheckedItems = QStringList();\n\n    connect(this, SIGNAL(itemChanged(QListWidgetItem *)),\n            this, SLOT(updateCheckedItems(QListWidgetItem *)));\n\n    connect(model(), SIGNAL(rowsInserted(QModelIndex, int, int)),\n            this, SLOT(updateCheckedItems(QModelIndex, int, int)));\n}\n\nQStringList ComponentListWidget::checkedItems()\n{\n    mCheckedItems.removeDuplicates();\n    return mCheckedItems;\n}\n\nvoid ComponentListWidget::updateCheckedItems(const QModelIndex &index, int start, int end)\n{\n    updateCheckedItems(item(start));\n}\n\nvoid ComponentListWidget::updateCheckedItems(QListWidgetItem *item)\n{\n    if (!item)\n        return;\n\n    QString text = item->text();\n\n    if (item->checkState() == Qt::Checked) {\n        if (!mCheckedItems.contains(text))\n            mCheckedItems.append(text);\n    } else {\n        if (mCheckedItems.contains(text))\n            mCheckedItems.removeAll(text);\n    }\n\n    mCheckedItems.removeDuplicates();\n\n    emit checkedItemsChanged(mCheckedItems);\n\n}\n"
  },
  {
    "path": "apps/wizard/utils/componentlistwidget.hpp",
    "content": "#ifndef COMPONENTLISTWIDGET_HPP\n#define COMPONENTLISTWIDGET_HPP\n\n#include <QListWidget>\n\nclass ComponentListWidget : public QListWidget\n{\n    Q_OBJECT\n\n    Q_PROPERTY(QStringList mCheckedItems READ checkedItems)\n\npublic:\n    ComponentListWidget(QWidget *parent = nullptr);\n\n    QStringList mCheckedItems;\n    QStringList checkedItems();\n\nsignals:\n    void checkedItemsChanged(const QStringList &items);\n\nprivate slots:\n    void updateCheckedItems(QListWidgetItem *item);\n    void updateCheckedItems(const QModelIndex &index, int start, int end);\n};\n\n#endif // COMPONENTLISTWIDGET_HPP\n"
  },
  {
    "path": "appveyor.yml",
    "content": "version: \"{build}\"\n\nbranches:\n    only:\n        - master\n        - /openmw-.*$/\n        - appveyor\n\nenvironment:\n    matrix:\n        - msvc: 2017\n          APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017\n        - msvc: 2019\n          APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019\n\nplatform:\n#   - Win32\n    - x64\n\nconfiguration:\n#    - Debug\n    - Release\n#    - RelWithDebInfo\n\n# For the Qt, Boost, CMake, etc installs\n#os: Visual Studio 2017\n\n# We want the git revision for versioning,\n# so shallow clones don't work.\nclone_depth: 1\n\ncache:\n    - C:\\projects\\openmw\\deps\\Bullet-2.87-msvc2015-win32.7z\n    - C:\\projects\\openmw\\deps\\Bullet-2.87-msvc2015-win64.7z\n    - C:\\projects\\openmw\\deps\\MyGUI-3.2.2-msvc2015-win32.7z\n    - C:\\projects\\openmw\\deps\\MyGUI-3.2.2-msvc2015-win64.7z\n    - C:\\projects\\openmw\\deps\\OSG-3.4.1-scrawl-msvc2015-win32.7z\n    - C:\\projects\\openmw\\deps\\OSG-3.4.1-scrawl-msvc2015-win64.7z\n    - C:\\projects\\openmw\\deps\\ffmpeg-3.2.4-dev-win32.zip\n    - C:\\projects\\openmw\\deps\\ffmpeg-3.2.4-dev-win64.zip\n    - C:\\projects\\openmw\\deps\\ffmpeg-3.2.4-win32.zip\n    - C:\\projects\\openmw\\deps\\ffmpeg-3.2.4-win64.zip\n    - C:\\projects\\openmw\\deps\\OpenAL-Soft-1.19.1.zip\n    - C:\\projects\\openmw\\deps\\SDL2-2.0.7.zip\n\nclone_folder: C:\\projects\\openmw\n\ninstall:\n    - set PATH=C:\\Program Files\\Git\\mingw64\\bin;%PATH%\n\nbefore_build:\n    - cmd: git submodule update --init --recursive\n    - cmd: sh %APPVEYOR_BUILD_FOLDER%\\CI\\before_script.msvc.sh -c %configuration% -p %PLATFORM% -v %msvc% -V -i %APPVEYOR_BUILD_FOLDER%\\install\n\nbuild_script:\n    - cmd: if %PLATFORM%==Win32 set build=MSVC%msvc%_32\n    - cmd: if %PLATFORM%==x64 set build=MSVC%msvc%_64\n    - cmd: msbuild %build%\\OpenMW.sln /t:Build /p:Configuration=%configuration% /m:2 /logger:\"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll\"\n    - cmd: cmake --install %build% --config %configuration%\n\nafter_build:\n    - cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%.zip %APPVEYOR_BUILD_FOLDER%\\install -xr\"!*.pdb\"\n    #- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%_pdb.zip %APPVEYOR_BUILD_FOLDER%\\MSVC%msvc%_64\\%configuration%\\*.pdb\n\ntest: off\n\n#notifications:\n#    - provider: Email\n#    to:\n#        -\n#    on_build_failure: true\n#    on_build_status_changed: true\n\nartifacts:\n  - path: OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%.zip\n    name: OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%\n  #- path: OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%_pdb.zip\n  #  name: OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%_pdb\n"
  },
  {
    "path": "cmake/COPYING-CMAKE-SCRIPTS",
    "content": "The following files are derived from the Thermite project\n(http://www.thermite3d.org) and are covered under the license below.\n\nFindMYGUI.cmake, FindBullet.cmake\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the copyright\n   notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n3. The name of the author may not be used to endorse or promote products \n   derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\nIN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\nNOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\nTHIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "cmake/CheckBulletPrecision.cmake",
    "content": "set(TMP_ROOT ${CMAKE_BINARY_DIR}/try-compile)\nfile(MAKE_DIRECTORY ${TMP_ROOT})\n\nfile(WRITE ${TMP_ROOT}/checkbullet.cpp\n\"\n#include <BulletCollision/CollisionShapes/btSphereShape.h>\nint main(int argc, char** argv)\n{\n    btSphereShape shape(1.0);\n    btScalar mass(1.0);\n    btVector3 inertia;\n    shape.calculateLocalInertia(mass, inertia);\n    return 0;\n}\n\")\n\nmessage(STATUS \"Checking if Bullet uses double precision\")\n\ntry_compile(RESULT_VAR\n    ${TMP_ROOT}/temp\n    ${TMP_ROOT}/checkbullet.cpp\n    COMPILE_DEFINITIONS \"-DBT_USE_DOUBLE_PRECISION\"\n    LINK_LIBRARIES ${BULLET_LIBRARIES}\n    CMAKE_FLAGS  \"-DINCLUDE_DIRECTORIES=${BULLET_INCLUDE_DIRS}\"\n    )\nset(HAS_DOUBLE_PRECISION_BULLET ${RESULT_VAR})\n"
  },
  {
    "path": "cmake/FindCallFF.cmake",
    "content": "FIND_PATH(CallFF_INCLUDES call.hpp\n        ENV CPATH\n        /usr/include\n        /usr/local/include\n        /opt/local/include\n        $ENV{CallFF_ROOT}/include\n        )\n\nFIND_LIBRARY(CallFF_LIBRARY NAMES callff\n        PATHS\n        ENV LD_LIBRARY_PATH\n        ENV LIBRARY_PATH\n        /usr/lib64\n        /usr/lib\n        /usr/local/lib64\n        /usr/local/lib\n        /opt/local/lib\n        $ENV{CallFF_ROOT}/lib\n        )\n\nif(CallFF_LIBRARY)\n    set(CallFF_LIBRARIES \"${CallFF_LIBRARY}\" CACHE STRING \"CallFF Libraries\")\nendif()\n\nif(CallFF_INCLUDES AND CallFF_LIBRARY)\n    set(CallFF_FOUND TRUE)\nendif(CallFF_INCLUDES AND CallFF_LIBRARY)\n"
  },
  {
    "path": "cmake/FindFFmpeg.cmake",
    "content": "# vim: ts=2 sw=2\n# - Try to find the required ffmpeg components\n#\n# This module accepts the following env variable\n#  FFMPEG_HOME - Can be set to custom install path\n#\n# Once done this will define\n#  FFmpeg_FOUND         - System has the all required components.\n#  FFmpeg_INCLUDE_DIRS  - Include directory necessary for using the required components headers.\n#  FFmpeg_LIBRARIES     - Link these to use the required ffmpeg components.\n#  FFmpeg_DEFINITIONS   - Compiler switches required for using the required ffmpeg components.\n#\n# For each of the components it will additionaly set.\n#   - AVCODEC\n#   - AVDEVICE\n#   - AVFORMAT\n#   - AVUTIL\n#   - POSTPROCESS\n#   - SWSCALE\n#   - SWRESAMPLE\n# the following variables will be defined\n#  FFmpeg_<component>_FOUND        - System has <component>\n#  FFmpeg_<component>_INCLUDE_DIRS - Include directory necessary for using the <component> headers\n#  FFmpeg_<component>_LIBRARIES    - Link these to use <component>\n#  FFmpeg_<component>_DEFINITIONS  - Compiler switches required for using <component>\n#  FFmpeg_<component>_VERSION      - The components version\n#\n# Copyright (c) 2006, Matthias Kretz, <kretz@kde.org>\n# Copyright (c) 2008, Alexander Neundorf, <neundorf@kde.org>\n# Copyright (c) 2011, Michael Jansen, <kde@michael-jansen.biz>\n# Copyright (c) 2016, Roman Proskuryakov, <humbug@deeptown.org>\n#\n# Redistribution and use is allowed according to the terms of the BSD license.\n# For details see the accompanying COPYING-CMAKE-SCRIPTS file.\n\ninclude(LibFindMacros)\ninclude(FindPackageHandleStandardArgs)\n\n# Macro: _internal_find_component\n# Checks for the given component by invoking pkgconfig etc.\nmacro(_internal_find_component _component _pkgconfig _library _header)\n    set(_package_component FFmpeg_${_component})\n    libfind_pkg_detect(${_package_component} ${_pkgconfig}\n        FIND_PATH ${_header}\n            HINTS $ENV{FFMPEG_HOME}\n            PATH_SUFFIXES include ffmpeg\n        FIND_LIBRARY ${_library}\n            HINTS $ENV{FFMPEG_HOME}\n            PATH_SUFFIXES lib\n    )\n    set(${_package_component}_DEFINITIONS  ${${_package_component}_PKGCONF_CFLAGS_OTHER})\n    set(${_package_component}_VERSION      ${${_package_component}_PKGCONF_VERSION})\n    libfind_process(${_package_component})\nendmacro()\n\n\n# setter for 'hashmap'\nmacro(hashmap_set _table _key) # ARGN\n    set(${_table}_${_key} ${ARGN})\nendmacro()\n\n# check for key in 'hashmap'\nmacro(hashmap_exists _table _key _out_var)\n    if (DEFINED ${_table}_${_key})\n        set(${_out_var} TRUE)\n    else()\n        set(${_out_var} FALSE)\n    endif()\nendmacro()\n\n# getter for 'hashmap'\nmacro(hashmap_get _table _key _out_var)\n    set(${_out_var} ${${_table}_${_key}})\nendmacro()\n\n\n# fill 'hashmap' named find_args\nhashmap_set(find_args AVCODEC  libavcodec  avcodec  libavcodec/avcodec.h)\nhashmap_set(find_args AVFORMAT libavformat avformat libavformat/avformat.h)\nhashmap_set(find_args AVDEVICE libavdevice avdevice libavdevice/avdevice.h)\nhashmap_set(find_args AVUTIL   libavutil   avutil   libavutil/avutil.h)\nhashmap_set(find_args SWSCALE  libswscale  swscale  libswscale/swscale.h)\nhashmap_set(find_args POSTPROC libpostproc postproc libpostproc/postprocess.h)\nhashmap_set(find_args SWRESAMPLE  libswresample  swresample  libswresample/swresample.h)\nhashmap_set(find_args AVRESAMPLE  libavresample  avresample  libavresample/avresample.h)\n\n# Check if the required components were found and add their stuff to the FFmpeg_* vars.\nforeach (_component ${FFmpeg_FIND_COMPONENTS})\n    hashmap_exists(find_args ${_component} _known_component)\n    if (NOT _known_component)\n        message(FATAL_ERROR \"Unknown component '${_component}'\")\n    endif()\n    hashmap_get(find_args ${_component} _component_find_args)\n    _internal_find_component(${_component} ${_component_find_args})\n    set(_package_component FFmpeg_${_component})\n    if (${_package_component}_FOUND)\n        list(APPEND FFmpeg_LIBRARIES ${${_package_component}_LIBRARIES})\n        list(APPEND FFmpeg_INCLUDE_DIRS ${${_package_component}_INCLUDE_DIRS})\n        list(APPEND FFmpeg_DEFINITIONS ${${_package_component}_DEFINITIONS})\n    endif ()\nendforeach ()\n\n# Build the include path with duplicates removed.\nif (FFmpeg_INCLUDE_DIRS)\n    list(REMOVE_DUPLICATES FFmpeg_INCLUDE_DIRS)\nendif()\n\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(FFmpeg\n    FOUND_VAR FFmpeg_FOUND\n    HANDLE_COMPONENTS\n    REQUIRED_VARS\n        FFmpeg_LIBRARIES\n        FFmpeg_INCLUDE_DIRS\n)\n"
  },
  {
    "path": "cmake/FindGMock.cmake",
    "content": "# Get the Google C++ Mocking Framework.\n# (This file is almost an copy of the original FindGTest.cmake file for GMock,\n#  feel free to use it as it is or modify it for your own needs.)\n#\n# Defines the following variables:\n#\n#   GMOCK_FOUND - Found or got the Google Mocking framework\n#   GMOCK_INCLUDE_DIRS - GMock include directory\n#\n# Also defines the library variables below as normal variables\n#\n#   GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock_main\n#   GMOCK_LIBRARIES - libgmock\n#   GMOCK_MAIN_LIBRARIES - libgmock-main\n#\n# Accepts the following variables as input:\n#\n#   GMOCK_SRC_DIR -The directory of the gmock sources\n#   GMOCK_VER - The version of the gmock sources to be downloaded\n#\n#-----------------------\n# Example Usage:\n#\n#    set(GMOCK_ROOT \"~/gmock\")\n#    find_package(GMock REQUIRED)\n#    include_directories(${GMOCK_INCLUDE_DIRS})\n#\n#    add_executable(foo foo.cc)\n#    target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES})\n#\n#=============================================================================\n# Copyright (c) 2016 Michel Estermann\n# Copyright (c) 2016 Kamil Strzempowicz\n# Copyright (c) 2011 Matej Svec\n#\n# CMake - Cross Platform Makefile Generator\n# Copyright 2000-2016 Kitware, Inc.\n# Copyright 2000-2011 Insight Software Consortium\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions\n# are met:\n#\n# * Redistributions of source code must retain the above copyright\n#   notice, this list of conditions and the following disclaimer.\n#\n# * Redistributions in binary form must reproduce the above copyright\n#   notice, this list of conditions and the following disclaimer in the\n#   documentation and/or other materials provided with the distribution.\n#\n# * Neither the names of Kitware, Inc., the Insight Software Consortium,\n#   nor the names of their contributors may be used to endorse or promote\n#   products derived from this software without specific prior written\n#   permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n#\n# ------------------------------------------------------------------------------\n#\n# The above copyright and license notice applies to distributions of\n# CMake in source and binary form.  Some source files contain additional\n# notices of original copyright by their contributors; see each source\n# for details.  Third-party software packages supplied with CMake under\n# compatible licenses provide their own copyright notices documented in\n# corresponding subdirectories.\n#\n# ------------------------------------------------------------------------------\n#\n# CMake was initially developed by Kitware with the following sponsorship:\n#\n#  * National Library of Medicine at the National Institutes of Health\n#    as part of the Insight Segmentation and Registration Toolkit (ITK).\n#\n#  * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel\n#    Visualization Initiative.\n#\n#  * National Alliance for Medical Image Computing (NAMIC) is funded by the\n#    National Institutes of Health through the NIH Roadmap for Medical Research,\n#    Grant U54 EB005149.\n#\n#  * Kitware, Inc.\n#=============================================================================\n\nfunction(_gmock_find_library _name)\n    find_library(${_name}\n        NAMES ${ARGN}\n        HINTS\n        ENV GMOCK_ROOT\n        ${GMOCK_ROOT}\n        PATH_SUFFIXES ${_gmock_libpath_suffixes}\n        )\n    mark_as_advanced(${_name})\nendfunction()\n\nif(NOT DEFINED GMOCK_MSVC_SEARCH)\n    set(GMOCK_MSVC_SEARCH MD)\nendif()\n\nset(_gmock_libpath_suffixes lib)\nif(MSVC)\n    if(GMOCK_MSVC_SEARCH STREQUAL \"MD\")\n        list(APPEND _gmock_libpath_suffixes\n            msvc/gmock-md/Debug\n            msvc/gmock-md/Release)\n    elseif(GMOCK_MSVC_SEARCH STREQUAL \"MT\")\n        list(APPEND _gmock_libpath_suffixes\n            msvc/gmock/Debug\n            msvc/gmock/Release)\n    endif()\nendif()\n\nfind_path(GMOCK_INCLUDE_DIR gmock/gmock.h\n    HINTS\n    $ENV{GMOCK_ROOT}/include\n    ${GMOCK_ROOT}/include\n    )\nmark_as_advanced(GMOCK_INCLUDE_DIR)\n\nif(MSVC AND GMOCK_MSVC_SEARCH STREQUAL \"MD\")\n    # The provided /MD project files for Google Mock add -md suffixes to the\n    # library names.\n    _gmock_find_library(GMOCK_LIBRARY gmock-md gmock)\n    _gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd)\n    _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main)\n    _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind)\nelse()\n    _gmock_find_library(GMOCK_LIBRARY gmock)\n    _gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd)\n    _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main)\n    _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind)\n\nendif()\n\nif(NOT TARGET GMock::GMock)\n    add_library(GMock::GMock UNKNOWN IMPORTED)\nendif()\n\nif(NOT TARGET GMock::Main)\n    add_library(GMock::Main UNKNOWN IMPORTED)\nendif()\n\nset(GMOCK_LIBRARY_EXISTS OFF)\n\nif(EXISTS \"${GMOCK_LIBRARY}\" OR EXISTS \"${GMOCK_LIBRARY_DEBUG}\" AND GMOCK_INCLUDE_DIR)\n    set(GMOCK_LIBRARY_EXISTS ON)\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY)\n\ninclude(CMakeFindDependencyMacro)\nfind_dependency(Threads)\n\nset_target_properties(GMock::GMock PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"Threads::Threads\"\n    IMPORTED_LINK_INTERFACE_LANGUAGES \"CXX\"\n    IMPORTED_LOCATION \"${GMOCK_LIBRARY}\")\n\nif(GMOCK_INCLUDE_DIR)\n    set_target_properties(GMock::GMock PROPERTIES\n        INTERFACE_INCLUDE_DIRECTORIES \"${GMOCK_INCLUDE_DIR}\"\n        INTERFACE_SYSTEM_INCLUDE_DIRECTORIES \"${GMOCK_INCLUDE_DIR}\"\n    )\n    if(GMOCK_VER VERSION_LESS \"1.7\")\n        # GMock 1.6 still has GTest as an external link-time dependency,\n        # so just specify it on the link interface.\n        set_property(TARGET GMock::GMock APPEND PROPERTY\n            INTERFACE_LINK_LIBRARIES GTest::GTest)\n    endif()\nendif()\n\nset_target_properties(GMock::Main PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"GMock::GMock\"\n    IMPORTED_LINK_INTERFACE_LANGUAGES \"CXX\"\n    IMPORTED_LOCATION \"${GMOCK_MAIN_LIBRARY}\")\n\nif(GMOCK_FOUND)\n    set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR})\n    set(GMOCK_LIBRARIES GMock::GMock)\n    set(GMOCK_MAIN_LIBRARIES GMock::Main)\n    set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES})\n    if(VERBOSE)\n        message(STATUS \"GMock includes: ${GMOCK_INCLUDE_DIRS}\")\n        message(STATUS \"GMock libs: ${GMOCK_BOTH_LIBRARIES}\")\n    endif()\nendif()\n"
  },
  {
    "path": "cmake/FindLIBUNSHIELD.cmake",
    "content": "# Locate LIBUNSHIELD\n# This module defines\n# LIBUNSHIELD_LIBRARIES\n# LIBUNSHIELD_FOUND, if false, do not try to link to LibUnshield\n# LIBUNSHIELD_INCLUDE_DIRS, where to find the headers\n#\n# Created by Tom Mason (wheybags) for OpenMW (https://openmw.org), based on FindMPG123.cmake\n#\n# Ripped off from other sources. In fact, this file is so generic (I\n# just did a search and replace on another file) that I wonder why the\n# CMake guys haven't wrapped this entire thing in a single\n# function. Do we really need to repeat this stuff for every single\n# library when they all work the same? </today's rant>\n\ninclude(LibFindMacros)\n\nset(POSSIBLE_LOCATIONS\n    ~/Library/Frameworks\n    /Library/Frameworks\n    /usr/local\n    /usr\n    /sw # Fink\n    /opt/local # DarwinPorts\n    /opt/csw # Blastwave\n    /opt\n    /usr/include\n)\n\nlibfind_pkg_detect(LIBUNSHIELD libunshield\n    FIND_PATH libunshield.h\n        HINTS ${POSSIBLE_LOCATIONS}\n    FIND_LIBRARY unshield\n        HINTS ${POSSIBLE_LOCATIONS}\n)\nlibfind_process(LIBUNSHIELD)\n"
  },
  {
    "path": "cmake/FindLZ4.cmake",
    "content": "# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying\n# file Copyright.txt or https://cmake.org/licensing for details.\n\n#[=======================================================================[.rst:\nFindLZ4\n-------\n\nFind the LZ4 include directory and library.\n\nUse this module by invoking find_package with the form::\n\n.. code-block:: cmake\n\n  find_package(LZ4\n    [version]              # Minimum version e.g. 1.8.0\n    [REQUIRED]             # Fail with error if LZ4 is not found\n  )\n\nImported targets\n^^^^^^^^^^^^^^^^\n\nThis module defines the following :prop_tgt:`IMPORTED` targets:\n\n.. variable:: LZ4::LZ4\n\n  Imported target for using the LZ4 library, if found.\n\nResult variables\n^^^^^^^^^^^^^^^^\n\n.. variable:: LZ4_FOUND\n\n  Set to true if LZ4 library found, otherwise false or undefined.\n\n.. variable:: LZ4_INCLUDE_DIRS\n\n  Paths to include directories listed in one variable for use by LZ4 client.\n\n.. variable:: LZ4_LIBRARIES\n\n  Paths to libraries to linked against to use LZ4.\n\n.. variable:: LZ4_VERSION\n\n  The version string of LZ4 found.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nFor users who wish to edit and control the module behavior, this module\nreads hints about search locations from the following variables::\n\n.. variable:: LZ4_INCLUDE_DIR\n\n  Path to LZ4 include directory with ``lz4.h`` header.\n\n.. variable:: LZ4_LIBRARY\n\n  Path to LZ4 library to be linked.\n\nNOTE: The variables above should not usually be used in CMakeLists.txt files!\n\n#]=======================================================================]\n\n### Find library ##############################################################\n\nif(NOT LZ4_LIBRARY)\n    find_library(LZ4_LIBRARY_RELEASE NAMES lz4)\n    find_library(LZ4_LIBRARY_DEBUG NAMES lz4d)\n\n    include(SelectLibraryConfigurations)\n    select_library_configurations(LZ4)\nelse()\n    file(TO_CMAKE_PATH \"${LZ4_LIBRARY}\" LZ4_LIBRARY)\nendif()\n\n### Find include directory ####################################################\nfind_path(LZ4_INCLUDE_DIR NAMES lz4.h)\n\nif(LZ4_INCLUDE_DIR AND EXISTS \"${LZ4_INCLUDE_DIR}/lz4.h\")\n    file(STRINGS \"${LZ4_INCLUDE_DIR}/lz4.h\" _lz4_h_contents\n            REGEX \"#define LZ4_VERSION_[A-Z]+[ ]+[0-9]+\")\n    string(REGEX REPLACE \"#define LZ4_VERSION_MAJOR[ ]+([0-9]+).+\" \"\\\\1\"\n            LZ4_VERSION_MAJOR \"${_lz4_h_contents}\")\n    string(REGEX REPLACE \".+#define LZ4_VERSION_MINOR[ ]+([0-9]+).+\" \"\\\\1\"\n            LZ4_VERSION_MINOR \"${_lz4_h_contents}\")\n    string(REGEX REPLACE \".+#define LZ4_VERSION_RELEASE[ ]+([0-9]+).*\" \"\\\\1\"\n            LZ4_VERSION_RELEASE \"${_lz4_h_contents}\")\n    set(LZ4_VERSION \"${LZ4_VERSION_MAJOR}.${LZ4_VERSION_MINOR}.${LZ4_VERSION_RELEASE}\")\n    unset(_lz4_h_contents)\nendif()\n\n### Set result variables ######################################################\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(LZ4 DEFAULT_MSG\n        LZ4_LIBRARY LZ4_INCLUDE_DIR LZ4_VERSION)\n\nmark_as_advanced(LZ4_INCLUDE_DIR LZ4_LIBRARY)\n\nset(LZ4_LIBRARIES ${LZ4_LIBRARY})\nset(LZ4_INCLUDE_DIRS ${LZ4_INCLUDE_DIR})\n\n### Import targets ############################################################\nif(LZ4_FOUND)\n    if(NOT TARGET LZ4::LZ4)\n        add_library(LZ4::LZ4 UNKNOWN IMPORTED)\n        set_target_properties(LZ4::LZ4 PROPERTIES\n                IMPORTED_LINK_INTERFACE_LANGUAGES \"C\"\n                INTERFACE_INCLUDE_DIRECTORIES \"${LZ4_INCLUDE_DIR}\")\n\n        if(LZ4_LIBRARY_RELEASE)\n            set_property(TARGET LZ4::LZ4 APPEND PROPERTY\n                    IMPORTED_CONFIGURATIONS RELEASE)\n            set_target_properties(LZ4::LZ4 PROPERTIES\n                    IMPORTED_LOCATION_RELEASE \"${LZ4_LIBRARY_RELEASE}\")\n        endif()\n\n        if(LZ4_LIBRARY_DEBUG)\n            set_property(TARGET LZ4::LZ4 APPEND PROPERTY\n                    IMPORTED_CONFIGURATIONS DEBUG)\n            set_target_properties(LZ4::LZ4 PROPERTIES\n                    IMPORTED_LOCATION_DEBUG \"${LZ4_LIBRARY_DEBUG}\")\n        endif()\n\n        if(NOT LZ4_LIBRARY_RELEASE AND NOT LZ4_LIBRARY_DEBUG)\n            set_property(TARGET LZ4::LZ4 APPEND PROPERTY\n                    IMPORTED_LOCATION \"${LZ4_LIBRARY}\")\n        endif()\n    endif()\nendif()\n"
  },
  {
    "path": "cmake/FindLuaJit.cmake",
    "content": "# Once found, defines:\n#  LuaJit_FOUND\n#  LuaJit_INCLUDE_DIRS\n#  LuaJit_LIBRARIES\n\ninclude(LibFindMacros)\n\nlibfind_pkg_detect(LuaJit luajit\n        FIND_PATH luajit.h\n        PATH_SUFFIXES luajit\n        FIND_LIBRARY NAMES luajit-5.1 luajit\n        )\n\nlibfind_process(LuaJit)"
  },
  {
    "path": "cmake/FindMyGUI.cmake",
    "content": "# - Find MyGUI includes and library\n#\n# This module accepts the following env variables\n#  MYGUI_HOME - Can be set to MyGUI install path or Windows build path\n#\n# This module defines\n# MyGUI_INCLUDE_DIRS\n# MyGUI_LIBRARIES, the libraries to link against to use MyGUI.\n# MyGUI_FOUND, If false, do not try to use MyGUI\n#\n# Copyright © 2007, Matt Williams\n#\n# Redistribution and use is allowed according to the terms of the BSD license.\n# For details see the accompanying COPYING-CMAKE-SCRIPTS file.\n\ninclude(LibFindMacros)\n\nif (MYGUI_STATIC)\n    set(MYGUI_STATIC_SUFFIX \"Static\")\nelse()\n    set(MYGUI_STATIC_SUFFIX \"\")\nendif()\n\nlibfind_pkg_detect(MyGUI_Debug MyGUI${MYGUI_STATIC_SUFFIX} MYGUI${MYGUI_STATIC_SUFFIX}\n    FIND_LIBRARY MyGUIEngine_d${MYGUI_STATIC_SUFFIX}\n        HINTS $ENV{MYGUI_HOME}/lib\n        PATH_SUFFIXES \"\" debug\n)\nset(MyGUI_Debug_FIND_QUIETLY TRUE)\nlibfind_process(MyGUI_Debug)\n\nlibfind_pkg_detect(MyGUI MyGUI${MYGUI_STATIC_SUFFIX} MYGUI${MYGUI_STATIC_SUFFIX}\n    FIND_PATH MyGUI.h\n        HINTS $ENV{MYGUI_HOME}/include\n        PATH_SUFFIXES MYGUI MyGUI\n    FIND_LIBRARY MyGUIEngine${MYGUI_STATIC_SUFFIX}\n        HINTS $ENV{MYGUI_HOME}/lib\n        PATH_SUFFIXES \"\" release relwithdebinfo minsizerel\n)\nif (MYGUI_STATIC AND (APPLE OR ANDROID))\n    # we need explicit Freetype libs only on OS X and ANDROID for static build\n    libfind_package(MyGUI Freetype)\nendif()\n\nlibfind_version_n_header(MyGUI\n    NAMES MyGUI_Prerequest.h\n    DEFINES MYGUI_VERSION_MAJOR MYGUI_VERSION_MINOR MYGUI_VERSION_PATCH\n)\nlibfind_process(MyGUI)\n\nif (MyGUI_Debug_FOUND)\n    set(MyGUI_LIBRARIES optimized ${MyGUI_LIBRARIES} debug ${MyGUI_Debug_LIBRARIES})\nendif()\n"
  },
  {
    "path": "cmake/FindOSGPlugins.cmake",
    "content": "# This module accepts the following env variable\n#  OSGPlugins_LIB_DIR - <OpenSceneGraph>/lib/osgPlugins-<X.X.X> , path to search plugins\n#  OSGPlugins_DONT_FIND_DEPENDENCIES - Set to skip also finding png, zlib and jpeg\n#\n# Once done this will define\n#  OSGPlugins_FOUND         - System has the all required components.\n#  OSGPlugins_LIBRARIES     - Link these to use the required osg plugins components.\n#\n# Components:\n#   - osgdb_png\n#   - osgdb_tga\n#   - osgdb_dds\n#   - osgdb_jpeg\n\ninclude(LibFindMacros)\ninclude(Findosg_functions)\n\nif (NOT OSGPlugins_LIB_DIR)\n    unset(OSGPlugins_LIB_DIR)\n    foreach(OSGDB_LIB ${OSGDB_LIBRARY})\n        # Skip library type names\n        if(EXISTS ${OSGDB_LIB} AND NOT IS_DIRECTORY ${OSGDB_LIB})\n            get_filename_component(OSG_LIB_DIR ${OSGDB_LIB} DIRECTORY)\n            list(APPEND OSGPlugins_LIB_DIR \"${OSG_LIB_DIR}/osgPlugins-${OPENSCENEGRAPH_VERSION}\")\n        endif()\n    endforeach(OSGDB_LIB)\nendif()\n\nif (NOT OSGPlugins_LIB_DIR)\n    set(_mode WARNING)\n    if (OSGPlugins_FIND_REQUIRED)\n        set(_mode FATAL_ERROR)\n    endif()\n    message(${_mode} \"OSGPlugins_LIB_DIR variable must be set\")\nendif()\n\nforeach(_library ${OSGPlugins_FIND_COMPONENTS})\n    string(TOUPPER ${_library} _library_uc)\n    set(_component OSGPlugins_${_library})\n\n    # On some systems, notably Debian and Ubuntu, the OSG plugins do not have\n    # the usual \"lib\" prefix. We temporarily add the empty string to the list\n    # of prefixes CMake searches for (via osg_find_library) to support these systems.\n    set(_saved_lib_prefix ${CMAKE_FIND_LIBRARY_PREFIXES}) # save CMAKE_FIND_LIBRARY_PREFIXES\n    list(APPEND CMAKE_FIND_LIBRARY_PREFIXES \"\") # search libraries with no prefix\n    set(${_library_uc}_DIR ${OSGPlugins_LIB_DIR}) # to help function osg_find_library\n    osg_find_library(${_library_uc} ${_library}) # find it into ${_library_uc}_LIBRARIES\n    set(CMAKE_FIND_LIBRARY_PREFIXES ${_saved_lib_prefix}) # restore prefix\n\n    if (${_library_uc}_LIBRARIES)\n        set(${_component}_LIBRARY ${${_library_uc}_LIBRARIES}) # fake as if we call find_library\n    else()\n        set(${_component}_LIBRARY ${_component}_LIBRARY-NOTFOUND)\n    endif()\n\n    list(APPEND OSGPlugins_PROCESS_LIBS ${_component}_LIBRARY)\nendforeach()\n\nif(NOT DEFINED OSGPlugins_DONT_FIND_DEPENDENCIES)\n    foreach(_dependency PNG ZLIB JPEG) # needed by osgdb_png or osgdb_jpeg\n        libfind_package(OSGPlugins ${_dependency})\n        set(${_dependency}_LIBRARY_OPTS ${_dependency}_LIBRARY)\n        #list(APPEND OSGPlugins_PROCESS_LIBS ${_dependency}_LIBRARY)\n    endforeach()\nendif()\n\nlibfind_process(OSGPlugins)\n"
  },
  {
    "path": "cmake/FindRakNet.cmake",
    "content": "# Comes form project edunetgames\n# - Try to find RakNet\n# Once done this will define\n#\n#  RakNet_FOUND - system has RakNet\n#  RakNet_INCLUDES - the RakNet include directory\n#  RakNet_LIBRARY - Link these to use RakNet\n\nFIND_LIBRARY (RakNet_LIBRARY_RELEASE NAMES RakNetLibStatic\n    PATHS\n    ENV LD_LIBRARY_PATH\n    ENV LIBRARY_PATH\n    /usr/lib64\n    /usr/lib\n    /usr/local/lib64\n    /usr/local/lib\n    /opt/local/lib\n    $ENV{RAKNET_ROOT}/lib\n    )\n\t\nFIND_LIBRARY (RakNet_LIBRARY_DEBUG NAMES RakNetLibStaticd\n    PATHS\n    ENV LD_LIBRARY_PATH\n    ENV LIBRARY_PATH\n    /usr/lib64\n    /usr/lib\n    /usr/local/lib64\n    /usr/local/lib\n    /opt/local/lib\n    $ENV{RAKNET_ROOT}/lib\n    )\t\n\t\n\t\n\nFIND_PATH (RakNet_INCLUDES raknet/RakPeer.h\n    ENV CPATH\n    /usr/include\n    /usr/local/include\n    /opt/local/include\n\t$ENV{RAKNET_ROOT}/include\n    )\n \nMESSAGE(STATUS ${RakNet_INCLUDES})\nMESSAGE(STATUS ${RakNet_LIBRARY_RELEASE})\n \nIF(RakNet_INCLUDES AND RakNet_LIBRARY_RELEASE)\n    SET(RakNet_FOUND TRUE)\nENDIF(RakNet_INCLUDES AND RakNet_LIBRARY_RELEASE)\n\nIF(RakNet_FOUND)\n  SET(RakNet_INCLUDES ${RakNet_INCLUDES}/raknet)\n  \n  \n   IF (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE)\n        SET(RakNet_LIBRARY optimized ${RakNet_LIBRARY_RELEASE} debug ${RakNet_LIBRARY_DEBUG})\n\t\tIF(WIN32)\n\t\t\tSET(RakNet_LIBRARY optimized ${RakNet_LIBRARY_RELEASE} debug ${RakNet_LIBRARY_DEBUG} ws2_32.lib)\n\t\tENDIF(WIN32)\n   ELSE()\n        # if there are no configuration types and CMAKE_BUILD_TYPE has no value\n        # then just use the release libraries\n        SET(RakNet_LIBRARY ${RakNet_LIBRARY_RELEASE} )\n\t\tIF(WIN32)\n\t\t\tSET(RakNet_LIBRARY ${RakNet_LIBRARY_RELEASE} ws2_32.lib)\n\t\tENDIF(WIN32)\n   ENDIF()\n   \n  IF(NOT RakNet_FIND_QUIETLY)\n    MESSAGE(STATUS \"Found RakNet_LIBRARY_RELEASE: ${RakNet_LIBRARY_RELEASE}\")\n    MESSAGE(STATUS \"Found RakNet_INCLUDES: ${RakNet_INCLUDES}\")\n  ENDIF(NOT RakNet_FIND_QUIETLY)\nELSE(RakNet_FOUND)\n  IF(RakNet_FIND_REQUIRED)\n    MESSAGE(FATAL_ERROR \"Could not find RakNet\")\n  ENDIF(RakNet_FIND_REQUIRED)\nENDIF(RakNet_FOUND)\n\n"
  },
  {
    "path": "cmake/FindRecastNavigation.cmake",
    "content": "# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying\n# file Copyright.txt or https://cmake.org/licensing for details.\n# Copyright 2021 Bret Curtis for OpenMW\n#[=======================================================================[.rst:\nFindRecastNavigation\n-------\n\nFind the RecastNavigation include directory and library.\n\nUse this module by invoking find_package with the form::\n\n.. code-block:: cmake\n\n  find_package(RecastNavigation\n    [version]              # Minimum version e.g. 1.8.0\n    [REQUIRED]             # Fail with error if RECAST is not found\n  )\n\nImported targets\n^^^^^^^^^^^^^^^^\n\nThis module defines the following :prop_tgt:`IMPORTED` targets:\n\n.. variable:: RecastNavigation::Recast\n\n  Imported target for using the RECAST library, if found.\n\nResult variables\n^^^^^^^^^^^^^^^^\n\n.. variable:: RECAST_FOUND\n\n  Set to true if RECAST library found, otherwise false or undefined.\n\n.. variable:: RECAST_INCLUDE_DIRS\n\n  Paths to include directories listed in one variable for use by RECAST client.\n\n.. variable:: RECAST_LIBRARIES\n\n  Paths to libraries to linked against to use RECAST.\n\n.. variable:: RECAST_VERSION\n\n  The version string of RECAST found.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nFor users who wish to edit and control the module behavior, this module\nreads hints about search locations from the following variables::\n\n.. variable:: RECAST_INCLUDE_DIR\n\n  Path to RECAST include directory with ``Recast.h`` header.\n\n.. variable:: RECAST_LIBRARY\n\n  Path to RECAST library to be linked.\n\nNOTE: The variables above should not usually be used in CMakeLists.txt files!\n\n#]=======================================================================]\n\n### Find libraries ##############################################################\n\nif(NOT RECAST_LIBRARY)\n    find_library(RECAST_LIBRARY_RELEASE NAMES Recast HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)\n    find_library(RECAST_LIBRARY_DEBUG NAMES Recast-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)\n    include(SelectLibraryConfigurations)\n    select_library_configurations(RECAST)\n    mark_as_advanced(RECAST_LIBRARY_RELEASE RECAST_LIBRARY_DEBUG)\nelse()\n    file(TO_CMAKE_PATH \"${RECAST_LIBRARY}\" RECAST_LIBRARY)\nendif()\n\nif(NOT DETOUR_LIBRARY)\n    find_library(DETOUR_LIBRARY_RELEASE NAMES Detour HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)\n    find_library(DETOUR_LIBRARY_DEBUG NAMES Detour-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)\n    include(SelectLibraryConfigurations)\n    select_library_configurations(DETOUR)\n    mark_as_advanced(DETOUR_LIBRARY_RELEASE DETOUR_LIBRARY_DEBUG)\nelse()\n    file(TO_CMAKE_PATH \"${DETOUR_LIBRARY}\" DETOUR_LIBRARY)\nendif()\n\nif(NOT DEBUGUTILS_LIBRARY)\n    find_library(DEBUGUTILS_LIBRARY_RELEASE NAMES DebugUtils HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)\n    find_library(DEBUGUTILS_LIBRARY_DEBUG NAMES DebugUtils-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)\n    include(SelectLibraryConfigurations)\n    select_library_configurations(DEBUGUTILS)\n    mark_as_advanced(DEBUGUTILS_LIBRARY_RELEASE DEBUGUTILS_LIBRARY_DEBUG)\nelse()\n    file(TO_CMAKE_PATH \"${DEBUGUTILS_LIBRARY}\" DEBUGUTILS_LIBRARY)\nendif()\n\n### Find include directory ####################################################\nfind_path(RECAST_INCLUDE_DIR NAMES Recast.h HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES include RECAST include/recastnavigation)\nmark_as_advanced(RECAST_INCLUDE_DIR)\n\nif(RECAST_INCLUDE_DIR AND EXISTS \"${RECAST_INCLUDE_DIR}/Recast.h\")\n    file(STRINGS \"${RECAST_INCLUDE_DIR}/Recast.h\" _Recast_h_contents\n            REGEX \"#define RECAST_VERSION_[A-Z]+[ ]+[0-9]+\")\n    string(REGEX REPLACE \"#define RECAST_VERSION_MAJOR[ ]+([0-9]+).+\" \"\\\\1\"\n            RECAST_VERSION_MAJOR \"${_Recast_h_contents}\")\n    string(REGEX REPLACE \".+#define RECAST_VERSION_MINOR[ ]+([0-9]+).+\" \"\\\\1\"\n            RECAST_VERSION_MINOR \"${_Recast_h_contents}\")\n    string(REGEX REPLACE \".+#define RECAST_VERSION_RELEASE[ ]+([0-9]+).*\" \"\\\\1\"\n            RECAST_VERSION_RELEASE \"${_Recast_h_contents}\")\n    set(RECAST_VERSION \"${RECAST_VERSION_MAJOR}.${RECAST_VERSION_MINOR}.${RECAST_VERSION_RELEASE}\")\n    unset(_Recast_h_contents)\nendif()\n\n#TODO: they don't include a version yet\nset(RECAST_VERSION \"1.5.1\")\n\n### Set result variables ######################################################\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(RecastNavigation DEFAULT_MSG\n        RECAST_LIBRARY RECAST_INCLUDE_DIR RECAST_VERSION)\n\nset(RECAST_LIBRARIES ${RECAST_LIBRARY})\nset(RECAST_INCLUDE_DIRS ${RECAST_INCLUDE_DIR})\n\nset(DETOUR_LIBRARIES ${DETOUR_LIBRARY})\nset(DETOUR_INCLUDE_DIRS ${RECAST_INCLUDE_DIR})\n\nset(DEBUGUTILS_LIBRARIES ${DEBUGUTILS_LIBRARY})\nset(DEBUGUTILS_INCLUDE_DIRS ${RECAST_INCLUDE_DIR})\n\n### Import targets ############################################################\nif(RecastNavigation_FOUND)\n    if(NOT TARGET RecastNavigation::Recast)\n        add_library(RecastNavigation::Recast UNKNOWN IMPORTED)\n        set_target_properties(RecastNavigation::Recast PROPERTIES\n                IMPORTED_LINK_INTERFACE_LANGUAGES \"C\"\n                INTERFACE_INCLUDE_DIRECTORIES \"${RECAST_INCLUDE_DIR}\")\n\n        if(RECAST_LIBRARY_RELEASE)\n            set_property(TARGET RecastNavigation::Recast APPEND PROPERTY\n                    IMPORTED_CONFIGURATIONS RELEASE)\n            set_target_properties(RecastNavigation::Recast PROPERTIES\n                    IMPORTED_LOCATION_RELEASE \"${RECAST_LIBRARY_RELEASE}\")\n        endif()\n\n        if(RECAST_LIBRARY_DEBUG)\n            set_property(TARGET RecastNavigation::Recast APPEND PROPERTY\n                    IMPORTED_CONFIGURATIONS DEBUG)\n            set_target_properties(RecastNavigation::Recast PROPERTIES\n                    IMPORTED_LOCATION_DEBUG \"${RECAST_LIBRARY_DEBUG}\")\n        endif()\n\n        if(NOT RECAST_LIBRARY_RELEASE AND NOT RECAST_LIBRARY_DEBUG)\n            set_property(TARGET RecastNavigation::Recast APPEND PROPERTY\n                    IMPORTED_LOCATION \"${RECAST_LIBRARY}\")\n        endif()\n    endif()\n\n    if(NOT TARGET RecastNavigation::Detour)\n        add_library(RecastNavigation::Detour UNKNOWN IMPORTED)\n        set_target_properties(RecastNavigation::Detour PROPERTIES\n                IMPORTED_LINK_INTERFACE_LANGUAGES \"C\"\n                INTERFACE_INCLUDE_DIRECTORIES \"${DETOUR_INCLUDE_DIR}\")\n\n        if(DETOUR_LIBRARY_RELEASE)\n            set_property(TARGET RecastNavigation::Detour APPEND PROPERTY\n                    IMPORTED_CONFIGURATIONS RELEASE)\n            set_target_properties(RecastNavigation::Detour PROPERTIES\n                    IMPORTED_LOCATION_RELEASE \"${DETOUR_LIBRARY_RELEASE}\")\n        endif()\n\n        if(DETOUR_LIBRARY_DEBUG)\n            set_property(TARGET RecastNavigation::Detour APPEND PROPERTY\n                    IMPORTED_CONFIGURATIONS DEBUG)\n            set_target_properties(RecastNavigation::Detour PROPERTIES\n                    IMPORTED_LOCATION_DEBUG \"${DETOUR_LIBRARY_DEBUG}\")\n        endif()\n\n        if(NOT DETOUR_LIBRARY_RELEASE AND NOT DETOUR_LIBRARY_DEBUG)\n            set_property(TARGET RecastNavigation::Detour APPEND PROPERTY\n                    IMPORTED_LOCATION \"${DETOUR_LIBRARY}\")\n        endif()\n    endif()\n\n    if(NOT TARGET RecastNavigation::DebugUtils)\n        add_library(RecastNavigation::DebugUtils UNKNOWN IMPORTED)\n        set_target_properties(RecastNavigation::DebugUtils PROPERTIES\n                IMPORTED_LINK_INTERFACE_LANGUAGES \"C\"\n                INTERFACE_INCLUDE_DIRECTORIES \"${DEBUGUTILS_INCLUDE_DIR}\")\n\n        if(DEBUGUTILS_LIBRARY_RELEASE)\n            set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY\n                    IMPORTED_CONFIGURATIONS RELEASE)\n            set_target_properties(RecastNavigation::DebugUtils PROPERTIES\n                    IMPORTED_LOCATION_RELEASE \"${DEBUGUTILS_LIBRARY_RELEASE}\")\n        endif()\n\n        if(DEBUGUTILS_LIBRARY_DEBUG)\n            set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY\n                    IMPORTED_CONFIGURATIONS DEBUG)\n            set_target_properties(RecastNavigation::DebugUtils PROPERTIES\n                    IMPORTED_LOCATION_DEBUG \"${DEBUGUTILS_LIBRARY_DEBUG}\")\n        endif()\n\n        if(NOT DEBUGUTILS_LIBRARY_RELEASE AND NOT DEBUGUTILS_LIBRARY_DEBUG)\n            set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY\n                    IMPORTED_LOCATION \"${DEBUGUTILS_LIBRARY}\")\n        endif()\n    endif()\n\nendif()\n"
  },
  {
    "path": "cmake/FindSDL2.cmake",
    "content": "# Locate SDL2 library\n# This module defines\n# SDL2_LIBRARY, the SDL2 library, with no other libraries\n# SDL2_LIBRARIES, the SDL library and required components with compiler flags\n# SDL2_FOUND, if false, do not try to link to SDL2\n# SDL2_INCLUDE_DIR, where to find SDL.h\n# SDL2_VERSION, the version of the found library\n#\n# This module accepts the following env variables\n#  SDL2DIR - Can be set to ./configure --prefix=$SDL2DIR used in building SDL2. l.e.galup 9-20-02\n# This module responds to the the flag:\n# SDL2_BUILDING_LIBRARY\n# If this is defined, then no SDL2_main will be linked in because\n# only applications need main().\n# Otherwise, it is assumed you are building an application and this\n# module will attempt to locate and set the the proper link flags\n# as part of the returned SDL2_LIBRARIES variable.\n#\n# Don't forget to include SDL2main.h and SDL2main.m your project for the\n# OS X framework based version. (Other versions link to -lSDL2main which\n# this module will try to find on your behalf.) Also for OS X, this\n# module will automatically add the -framework Cocoa on your behalf.\n#\n#\n# Modified by Eric Wing.\n# Added code to assist with automated building by using environmental variables\n# and providing a more controlled/consistent search behavior.\n# Added new modifications to recognize OS X frameworks and\n# additional Unix paths (FreeBSD, etc).\n# Also corrected the header search path to follow \"proper\" SDL2 guidelines.\n# Added a search for SDL2main which is needed by some platforms.\n# Added a search for threads which is needed by some platforms.\n# Added needed compile switches for MinGW.\n#\n# On OSX, this will prefer the Framework version (if found) over others.\n# People will have to manually change the cache values of\n# SDL2_LIBRARY to override this selection or set the CMake environment\n# CMAKE_INCLUDE_PATH to modify the search paths.\n#\n# Note that the header path has changed from SDL2/SDL.h to just SDL.h\n# This needed to change because \"proper\" SDL2 convention\n# is #include \"SDL.h\", not <SDL2/SDL.h>. This is done for portability\n# reasons because not all systems place things in SDL2/ (see FreeBSD).\n#\n# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake\n# module with the minor edit of changing \"SDL\" to \"SDL2\" where necessary. This\n# was not created for redistribution, and exists temporarily pending official\n# SDL2 CMake modules.\n\n#=============================================================================\n# Copyright 2003-2009 Kitware, Inc.\n#\n# Distributed under the OSI-approved BSD License (the \"License\");\n# see accompanying file Copyright.txt for details.\n#\n# This software is distributed WITHOUT ANY WARRANTY; without even the\n# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n# See the License for more information.\n#=============================================================================\n# (To distribute this file outside of CMake, substitute the full\n#  License text for the above reference.)\n\ninclude(LibFindMacros)\n\nif (CMAKE_SIZEOF_VOID_P EQUAL 8)\n    set(_sdl_lib_suffix lib/x64)\nelse()\n    set(_sdl_lib_suffix lib/x86)\nendif()\n\nlibfind_pkg_detect(SDL2 sdl2\n    FIND_PATH SDL.h\n        HINTS $ENV{SDL2DIR}\n        PATH_SUFFIXES include SDL2 include/SDL2\n    FIND_LIBRARY SDL2\n        HINTS $ENV{SDL2DIR}\n        PATH_SUFFIXES lib ${_sdl_lib_suffix}\n)\nlibfind_version_n_header(SDL2 NAMES SDL_version.h DEFINES SDL_MAJOR_VERSION SDL_MINOR_VERSION SDL_PATCHLEVEL)\n\nIF(NOT SDL2_BUILDING_LIBRARY AND NOT APPLE)\n    # Non-OS X framework versions expect you to also dynamically link to\n    # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms\n    # seem to provide SDL2main for compatibility even though they don't\n    # necessarily need it.\n    libfind_pkg_detect(SDL2MAIN sdl2\n        FIND_LIBRARY SDL2main\n            HINTS $ENV{SDL2DIR}\n            PATH_SUFFIXES lib ${_sdl_lib_suffix}\n    )\n    set(SDL2MAIN_FIND_QUIETLY TRUE)\n    libfind_process(SDL2MAIN)\n    list(APPEND SDL2_PROCESS_LIBS SDL2MAIN_LIBRARY)\nENDIF()\n\n\nset(SDL2_TARGET_SPECIFIC)\n\nif (APPLE)\n    # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.\n    list(APPEND SDL2_TARGET_SPECIFIC \"-framework Cocoa\")\nelse()\n    # SDL2 may require threads on your system.\n    # The Apple build may not need an explicit flag because one of the\n    # frameworks may already provide it.\n    # But for non-OSX systems, I will use the CMake Threads package.\n    libfind_package(SDL2 Threads)\n    list(APPEND SDL2_TARGET_SPECIFIC ${CMAKE_THREAD_LIBS_INIT})\nendif()\n\n# MinGW needs an additional library, mwindows\n# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows\n# (Actually on second look, I think it only needs one of the m* libraries.)\nif(MINGW)\n    list(APPEND SDL2_TARGET_SPECIFIC mingw32)\nendif()\n\nif(WIN32)\n    list(APPEND SDL2_TARGET_SPECIFIC winmm imm32 version msimg32)\nendif()\n\nset(SDL2_PROCESS_LIBS SDL2_TARGET_SPECIFIC)\n\nlibfind_process(SDL2)\n\nif (SDL2_STATIC AND UNIX AND NOT APPLE)\n    execute_process(COMMAND sdl2-config --static-libs OUTPUT_VARIABLE SDL2_STATIC_FLAGS)\n    string(REGEX REPLACE \"(\\r?\\n)+$\" \"\" SDL2_STATIC_FLAGS \"${SDL2_STATIC_FLAGS}\")\n    set(SDL2_LIBRARIES ${SDL2_STATIC_FLAGS})\nendif()\n"
  },
  {
    "path": "cmake/FindSphinx.cmake",
    "content": "# - This module looks for Sphinx\n# Find the Sphinx documentation generator\n#\n# This modules defines\n#  Sphinx_EXECUTABLE\n#  Sphinx_FOUND\n#  function Sphinx_add_target\n#  function Sphinx_add_targets\n#\n\ninclude(FindPackageHandleStandardArgs)\ninclude(CMakeParseArguments)\n\nset(_sphinx_names sphinx-build)\nforeach(_version 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0 1.6 1.5)\n    list(APPEND _sphinx_names sphinx-build-${_version})\nendforeach()\n\nfind_program(Sphinx_EXECUTABLE\n    NAMES ${_sphinx_names}\n    PATHS\n        /usr/bin\n        /usr/local/bin\n        /opt/local/bin\n    DOC \"Sphinx documentation generator\"\n)\nmark_as_advanced(Sphinx_EXECUTABLE)\n\n\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(Sphinx\n    FOUND_VAR Sphinx_FOUND\n    REQUIRED_VARS Sphinx_EXECUTABLE\n)\n\noption( SPHINX_HTML_OUTPUT \"Build a single HTML with the whole content.\" ON )\noption( SPHINX_DIRHTML_OUTPUT \"Build HTML pages, but with a single directory per document.\" OFF )\noption( SPHINX_HTMLHELP_OUTPUT \"Build HTML pages with additional information for building a documentation collection in htmlhelp.\" OFF )\noption( SPHINX_QTHELP_OUTPUT \"Build HTML pages with additional information for building a documentation collection in qthelp.\" OFF )\noption( SPHINX_DEVHELP_OUTPUT \"Build HTML pages with additional information for building a documentation collection in devhelp.\" OFF )\noption( SPHINX_EPUB_OUTPUT \"Build HTML pages with additional information for building a documentation collection in epub.\" OFF )\noption( SPHINX_LATEX_OUTPUT \"Build LaTeX sources that can be compiled to a PDF document using pdflatex.\" OFF )\noption( SPHINX_MAN_OUTPUT \"Build manual pages in groff format for UNIX systems.\" OFF )\noption( SPHINX_TEXT_OUTPUT \"Build plain text files.\" OFF )\n\nfunction(Sphinx_add_target target_name builder conf source destination)\n    add_custom_target( ${target_name} ALL\n        COMMAND ${Sphinx_EXECUTABLE} -b ${builder} -c ${conf} ${source} ${destination}\n        DEPENDS ${conf}/conf.py\n        COMMENT \"Generating sphinx documentation: ${builder}\"\n    )\n\n    set_property(DIRECTORY APPEND PROPERTY\n        ADDITIONAL_MAKE_CLEAN_FILES ${destination}\n    )\nendfunction()\n\n# Usage:\n#\n# Sphinx_add_targets(NAME <base_targets_name>\n#   SRC_DIR <path_to_doc>/\n#   DST_DIR <path_to_output_result>/\n#   CONFIG_DIR <path_to_conf.py>/\n#   [DEPENDENCIES dep1 dep2 dep3 ...]\n# )\nfunction(Sphinx_add_targets)\n    set(options )\n    set(one_value_keywords NAME SRC_DIR DST_DIR CONFIG_DIR)\n    set(multi_value_keywords DEPENDENCIES)\n    CMAKE_PARSE_ARGUMENTS(OPT \"${options}\" \"${one_value_keywords}\" \"${multi_value_keywords}\" ${ARGN})\n\n    if (NOT OPT_NAME OR NOT OPT_SRC_DIR OR NOT OPT_DST_DIR OR NOT OPT_CONFIG_DIR)\n        message(FATAL_ERROR \"Arguments NAME, SRC_DIR, DST_DIR, CONFIG_DIR are required!\")\n    endif()\n\n    foreach(_generator html dirhtml qthelp devhelp epub latex man text linkcheck)\n        string(TOUPPER ${_generator} _generator_uc)\n        if (SPHINX_${_generator_uc}_OUTPUT)\n            Sphinx_add_target(${OPT_NAME}_${_generator} ${_generator} ${OPT_CONFIG_DIR} ${OPT_SRC_DIR} ${OPT_DST_DIR}/${_generator}/)\n            if (OPT_DEPENDENCIES)\n                add_dependencies(${OPT_NAME}_${_generator} ${OPT_DEPENDENCIES})\n            endif()\n        endif()\n    endforeach()\nendfunction()\n"
  },
  {
    "path": "cmake/FindTinyXML.cmake",
    "content": "# Try to find TinyXML library\n\n# Once done this will define\n#  TinyXML_FOUND         - System has the all required components.\n#  TinyXML_INCLUDE_DIRS  - Include directory necessary for using the required components headers.\n#  TinyXML_LIBRARIES     - Link these to use TinyXML.\n#\n\ninclude(LibFindMacros)\n\nlibfind_pkg_detect(TinyXML tinyxml\n    FIND_PATH tinyxml.h\n    FIND_LIBRARY tinyxml\n)\nlibfind_version_n_header(TinyXML\n    NAMES tinyxml.h\n    CONSTANTS TIXML_MAJOR_VERSION TIXML_MINOR_VERSION TIXML_PATCH_VERSION\n)\nlibfind_process(TinyXML)\n"
  },
  {
    "path": "cmake/GitVersion.cmake",
    "content": "execute_process (\n    COMMAND ${GIT_EXECUTABLE} rev-list --tags --max-count=1\n    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}\n    RESULT_VARIABLE EXITCODE1\n    OUTPUT_VARIABLE TAGHASH\n    OUTPUT_STRIP_TRAILING_WHITESPACE\n    ERROR_QUIET)\n\nexecute_process (\n    COMMAND ${GIT_EXECUTABLE} rev-parse HEAD\n    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}\n    RESULT_VARIABLE EXITCODE2\n    OUTPUT_VARIABLE COMMITHASH\n    OUTPUT_STRIP_TRAILING_WHITESPACE)\n\nstring (COMPARE EQUAL \"${EXITCODE1}:${EXITCODE2}\" \"0:0\" FULL_SUCCESS)\nstring (COMPARE EQUAL \"${EXITCODE2}\" \"0\" COMMIT_SUCCESS)\nif (FULL_SUCCESS)\n    set(OPENMW_VERSION_COMMITHASH \"${COMMITHASH}\")\n    set(OPENMW_VERSION_TAGHASH \"${TAGHASH}\")\n    message(STATUS \"OpenMW version ${OPENMW_VERSION}\")\nelseif (COMMIT_SUCCESS)\n    set(OPENMW_VERSION_COMMITHASH \"${COMMITHASH}\")\n    message(STATUS \"OpenMW version ${OPENMW_VERSION}\")\nelse ()\n    message(WARNING \"Failed to get valid version information from Git\")\nendif ()\n\ninclude(${MACROSFILE})\n\nconfigure_resource_file(${VERSION_IN_FILE} ${VERSION_FILE_PATH_BASE} ${VERSION_FILE_PATH_RELATIVE})\n"
  },
  {
    "path": "cmake/LibFindMacros.cmake",
    "content": "# Version 2.2\n# Public Domain, originally written by Lasse Kärkkäinen <tronic>\n# Maintained at https://github.com/Tronic/cmake-modules\n# Please send your improvements as pull requests on Github.\n\ninclude(CMakeParseArguments)\n\n# Find another package and make it a dependency of the current package.\n# This also automatically forwards the \"REQUIRED\" argument.\n# Usage: libfind_package(<prefix> <another package> [extra args to find_package])\nmacro (libfind_package PREFIX PKG)\n  set(${PREFIX}_args ${PKG} ${ARGN})\n  if (${PREFIX}_FIND_REQUIRED)\n    set(${PREFIX}_args ${${PREFIX}_args} REQUIRED)\n  endif()\n  find_package(${${PREFIX}_args})\n  set(${PREFIX}_DEPENDENCIES ${${PREFIX}_DEPENDENCIES};${PKG})\n  unset(${PREFIX}_args)\nendmacro()\n\n# A simple wrapper to make pkg-config searches a bit easier.\n# Works the same as CMake's internal pkg_search_module but is always quiet.\nmacro (libfind_pkg_search_module)\n  find_package(PkgConfig QUIET)\n  if (PKG_CONFIG_FOUND)\n    pkg_search_module(${ARGN} QUIET)\n  endif()\nendmacro()\n\n# Avoid useless copy&pasta by doing what most simple libraries do anyway:\n# pkg-config, find headers, find library.\n# Usage: libfind_pkg_detect(<prefix> <pkg-config args> FIND_PATH <name> [other args] FIND_LIBRARY <name> [other args])\n# E.g. libfind_pkg_detect(SDL2 sdl2 FIND_PATH SDL.h PATH_SUFFIXES SDL2 FIND_LIBRARY SDL2)\nfunction (libfind_pkg_detect PREFIX)\n  # Parse arguments\n  set(argname pkgargs)\n  foreach (i ${ARGN})\n    if (\"${i}\" STREQUAL \"FIND_PATH\")\n      set(argname pathargs)\n    elseif (\"${i}\" STREQUAL \"FIND_LIBRARY\")\n      set(argname libraryargs)\n    else()\n      set(${argname} ${${argname}} ${i})\n    endif()\n  endforeach()\n  if (NOT pkgargs)\n    message(FATAL_ERROR \"libfind_pkg_detect requires at least a pkg_config package name to be passed.\")\n  endif()\n  # Find library\n  libfind_pkg_search_module(${PREFIX}_PKGCONF ${pkgargs})\n  if (pathargs)\n    find_path(${PREFIX}_INCLUDE_DIR NAMES ${pathargs} HINTS ${${PREFIX}_PKGCONF_INCLUDE_DIRS})\n  endif()\n  if (libraryargs)\n    find_library(${PREFIX}_LIBRARY NAMES ${libraryargs} HINTS ${${PREFIX}_PKGCONF_LIBRARY_DIRS})\n  endif()\nendfunction()\n\n# libfind_header_path(<PREFIX> [PATHS <path> [<path> ...]] NAMES <name> [name ...] VAR <out_var> [QUIET])\n# Get fullpath of the first found header looking inside <PREFIX>_INCLUDE_DIR or in the given PATHS\n# Usage: libfind_header_path(Foobar NAMES foobar/version.h VAR filepath)\nfunction (libfind_header_path PREFIX)\n  set(options QUIET)\n  set(one_value_keywords VAR PATH)\n  set(multi_value_keywords NAMES PATHS)\n  CMAKE_PARSE_ARGUMENTS(OPT \"${options}\" \"${one_value_keywords}\" \"${multi_value_keywords}\" ${ARGN})\n  if (NOT OPT_VAR OR NOT OPT_NAMES)\n    message(FATAL_ERROR \"Arguments VAR, NAMES are required!\")\n  endif()\n  if (OPT_UNPARSED_ARGUMENTS)\n    message(FATAL_ERROR \"Calling function with unused arguments '${OPT_UNPARSED_ARGUMENTS}'!\")\n  endif()\n  if (OPT_QUIET OR ${PREFIX}_FIND_QUIETLY)\n    set(quiet TRUE)\n  endif()\n  set(paths ${OPT_PATHS} ${PREFIX}_INCLUDE_DIR)\n\n  foreach(name ${OPT_NAMES})\n    foreach(path ${paths})\n      set(filepath \"${${path}}/${name}\")\n      # check for existance\n      if (EXISTS ${filepath})\n        set(${OPT_VAR} ${filepath} PARENT_SCOPE) # export path\n        return()\n      endif()\n    endforeach()\n  endforeach()\n\n  # report error if not found\n  set(${OPT_VAR} NOTFOUND PARENT_SCOPE)\n  if (NOT quiet)\n    message(AUTHOR_WARNING \"Unable to find '${OPT_NAMES}'\")\n  endif()\nendfunction()\n\n# libfind_version_n_header(<PREFIX>\n#  NAMES <name> [<name> ...]\n#  DEFINES <define> [<define> ...] | CONSTANTS <const> [<const> ...]\n#  [PATHS <path> [<path> ...]]\n#  [QUIET]\n# )\n# Collect all defines|constants from a header inside <PREFIX>_INCLUDE_DIR or in the given PATHS\n#  output stored to <PREFIX>_VERSION.\n# This function does nothing if the version variable is already defined.\n# Usage: libfind_version_n_header(Foobar NAMES foobar/version.h DEFINES FOOBAR_VERSION_MAJOR FOOBAR_VERSION_MINOR)\nfunction (libfind_version_n_header PREFIX)\n  # Skip processing if we already have a version\n  if (${PREFIX}_VERSION)\n    return()\n  endif()\n\n  set(options QUIET)\n  set(one_value_keywords )\n  set(multi_value_keywords NAMES PATHS DEFINES CONSTANTS)\n  CMAKE_PARSE_ARGUMENTS(OPT \"${options}\" \"${one_value_keywords}\" \"${multi_value_keywords}\" ${ARGN})\n  if (NOT OPT_NAMES OR (NOT OPT_DEFINES AND NOT OPT_CONSTANTS))\n    message(FATAL_ERROR \"Arguments NAMES, DEFINES|CONSTANTS are required!\")\n  endif()\n  if (OPT_DEFINES AND OPT_CONSTANTS)\n    message(FATAL_ERROR \"Either DEFINES or CONSTANTS must be set!\")\n  endif()\n  if (OPT_UNPARSED_ARGUMENTS)\n    message(FATAL_ERROR \"Calling function with unused arguments '${OPT_UNPARSED_ARGUMENTS}'!\")\n  endif()\n  if (OPT_QUIET OR ${PREFIX}_FIND_QUIETLY)\n    set(quiet TRUE)\n    set(force_quiet \"QUIET\") # to propagate argument QUIET\n  endif()\n\n  # Read the header\n  libfind_header_path(${PREFIX} NAMES ${OPT_NAMES} PATHS ${OPT_PATHS} VAR filename ${force_quiet})\n  if (NOT filename)\n    return()\n  endif()\n  file(READ \"${filename}\" header)\n  # Parse for version number\n  unset(version_parts)\n  foreach(define_name ${OPT_DEFINES})\n    string(REGEX MATCH \"# *define +${define_name} +((\\\"([^\\n]*)\\\")|([^ \\n]*))\" match \"${header}\")\n    # No regex match?\n    if (NOT match OR match STREQUAL header)\n      if (NOT quiet)\n        message(AUTHOR_WARNING \"Unable to find \\#define ${define_name} \\\"<version>\\\" from ${filename}\")\n      endif()\n      return()\n    else()\n      list(APPEND version_parts \"${CMAKE_MATCH_3}${CMAKE_MATCH_4}\")\n    endif()\n  endforeach()\n  foreach(constant_name ${OPT_CONSTANTS})\n    string(REGEX MATCH \"${constant_name} *= *((\\\"([^\\;]*)\\\")|([^ \\;]*))\" match \"${header}\")\n    # No regex match?\n    if (NOT match OR match STREQUAL header)\n      if (NOT quiet)\n        message(AUTHOR_WARNING \"Unable to find ${constant_name} = \\\"<version>\\\" from ${filename}\")\n      endif()\n      return()\n    else()\n      list(APPEND version_parts \"${CMAKE_MATCH_3}${CMAKE_MATCH_4}\")\n    endif()\n  endforeach()\n\n  # Export the version string\n  string(REPLACE \";\" \".\" version \"${version_parts}\")\n  set(${PREFIX}_VERSION \"${version}\" PARENT_SCOPE)\nendfunction()\n\n# libfind_version_header(<PREFIX> <HEADER> <DEFINE_NAME> [PATHS <path> [<path> ...]] [QUIET])\n# Extracts a version #define from a version.h file, output stored to <PREFIX>_VERSION.\n# This function does nothing if the version variable is already defined.\n# Usage: libfind_version_header(Foobar foobar/version.h FOOBAR_VERSION_STR)\nfunction (libfind_version_header PREFIX VERSION_H DEFINE_NAME)\n  # Skip processing if we already have a version\n  if (${PREFIX}_VERSION)\n    return()\n  endif()\n\n  set(options QUIET)\n  set(one_value_keywords )\n  set(multi_value_keywords PATHS)\n  CMAKE_PARSE_ARGUMENTS(OPT \"${options}\" \"${one_value_keywords}\" \"${multi_value_keywords}\" ${ARGN})\n  if (OPT_UNPARSED_ARGUMENTS)\n    message(FATAL_ERROR \"Calling function with unused arguments '${OPT_UNPARSED_ARGUMENTS}'!\")\n  endif()\n  if (OPT_QUIET OR ${PREFIX}_FIND_QUIETLY)\n    set(force_quiet \"QUIET\") # to propagate argument QUIET\n  endif()\n\n  libfind_version_n_header(${PREFIX} NAMES ${VERSION_H} PATHS ${OPT_PATHS} DEFINES ${DEFINE_NAME} ${force_quiet})\n  set(${PREFIX}_VERSION \"${${PREFIX}_VERSION}\" PARENT_SCOPE)\nendfunction()\n\n# Do the final processing once the paths have been detected.\n# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain\n# all the variables, each of which contain one include directory.\n# Ditto for ${PREFIX}_PROCESS_LIBS and library files.\n# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.\n# Also handles errors in case library detection was required, etc.\nfunction (libfind_process PREFIX)\n  # Skip processing if already processed during this configuration run\n  if (${PREFIX}_FOUND)\n    return()\n  endif()\n\n  set(found TRUE)  # Start with the assumption that the package was found\n\n  # Did we find any files? Did we miss includes? These are for formatting better error messages.\n  set(some_files FALSE)\n  set(missing_headers FALSE)\n\n  # Shorthands for some variables that we need often\n  set(quiet ${${PREFIX}_FIND_QUIETLY})\n  set(required ${${PREFIX}_FIND_REQUIRED})\n  set(exactver ${${PREFIX}_FIND_VERSION_EXACT})\n  set(findver \"${${PREFIX}_FIND_VERSION}\")\n  set(version \"${${PREFIX}_VERSION}\")\n\n  # Lists of config option names (all, includes, libs)\n  unset(configopts)\n  set(includeopts ${${PREFIX}_PROCESS_INCLUDES})\n  set(libraryopts ${${PREFIX}_PROCESS_LIBS})\n\n  # Process deps to add to\n  foreach (i ${PREFIX} ${${PREFIX}_DEPENDENCIES})\n    if (DEFINED ${i}_INCLUDE_OPTS OR DEFINED ${i}_LIBRARY_OPTS)\n      # The package seems to export option lists that we can use, woohoo!\n      list(APPEND includeopts ${${i}_INCLUDE_OPTS})\n      list(APPEND libraryopts ${${i}_LIBRARY_OPTS})\n    else()\n      # If plural forms don't exist or they equal singular forms\n      if ((NOT DEFINED ${i}_INCLUDE_DIRS AND NOT DEFINED ${i}_LIBRARIES) OR\n          ({i}_INCLUDE_DIR STREQUAL ${i}_INCLUDE_DIRS AND ${i}_LIBRARY STREQUAL ${i}_LIBRARIES))\n        # Singular forms can be used\n        if (DEFINED ${i}_INCLUDE_DIR)\n          list(APPEND includeopts ${i}_INCLUDE_DIR)\n        endif()\n        if (DEFINED ${i}_LIBRARY)\n          list(APPEND libraryopts ${i}_LIBRARY)\n        endif()\n      else()\n        # Oh no, we don't know the option names\n        message(FATAL_ERROR \"We couldn't determine config variable names for ${i} includes and libs. Aieeh!\")\n      endif()\n    endif()\n  endforeach()\n\n  if (includeopts)\n    list(REMOVE_DUPLICATES includeopts)\n  endif()\n\n  if (libraryopts)\n    list(REMOVE_DUPLICATES libraryopts)\n  endif()\n\n  string(REGEX REPLACE \".*[ ;]([^ ;]*(_INCLUDE_DIRS|_LIBRARIES))\" \"\\\\1\" tmp \"${includeopts} ${libraryopts}\")\n  if (NOT tmp STREQUAL \"${includeopts} ${libraryopts}\")\n    message(AUTHOR_WARNING \"Plural form ${tmp} found in config options of ${PREFIX}. This works as before but is now deprecated. Please only use singular forms INCLUDE_DIR and LIBRARY, and update your find scripts for LibFindMacros > 2.0 automatic dependency system (most often you can simply remove the PROCESS variables entirely).\")\n  endif()\n\n  # Include/library names separated by spaces (notice: not CMake lists)\n  unset(includes)\n  unset(libs)\n\n  # Process all includes and set found false if any are missing\n  foreach (i ${includeopts})\n    list(APPEND configopts ${i})\n    if (NOT \"${${i}}\" STREQUAL \"${i}-NOTFOUND\")\n      list(APPEND includes \"${${i}}\")\n    else()\n      set(found FALSE)\n      set(missing_headers TRUE)\n    endif()\n  endforeach()\n\n  # Process all libraries and set found false if any are missing\n  foreach (i ${libraryopts})\n    list(APPEND configopts ${i})\n    if (NOT \"${${i}}\" STREQUAL \"${i}-NOTFOUND\")\n      list(APPEND libs \"${${i}}\")\n    else()\n      set (found FALSE)\n    endif()\n  endforeach()\n\n  # Version checks\n  if (found AND findver)\n    if (NOT version)\n      message(WARNING \"The find module for ${PREFIX} does not provide version information, so we'll just assume that it is OK. Please fix the module or remove package version requirements to get rid of this warning.\")\n    elseif (version VERSION_LESS findver OR (exactver AND NOT version VERSION_EQUAL findver))\n      set(found FALSE)\n      set(version_unsuitable TRUE)\n    endif()\n  endif()\n\n  # If all-OK, hide all config options, export variables, print status and exit\n  if (found)\n    foreach (i ${configopts})\n      mark_as_advanced(${i})\n    endforeach()\n    set (${PREFIX}_INCLUDE_OPTS ${includeopts} PARENT_SCOPE)\n    set (${PREFIX}_LIBRARY_OPTS ${libraryopts} PARENT_SCOPE)\n    set (${PREFIX}_INCLUDE_DIRS ${includes} PARENT_SCOPE)\n    set (${PREFIX}_LIBRARIES ${libs} PARENT_SCOPE)\n    set (${PREFIX}_FOUND TRUE PARENT_SCOPE)\n    if (NOT quiet)\n      message(STATUS \"Found ${PREFIX} ${${PREFIX}_VERSION}\")\n      if (LIBFIND_DEBUG)\n        message(STATUS \"  ${PREFIX}_DEPENDENCIES=${${PREFIX}_DEPENDENCIES}\")\n        message(STATUS \"  ${PREFIX}_INCLUDE_OPTS=${includeopts}\")\n        message(STATUS \"  ${PREFIX}_INCLUDE_DIRS=${includes}\")\n        message(STATUS \"  ${PREFIX}_LIBRARY_OPTS=${libraryopts}\")\n        message(STATUS \"  ${PREFIX}_LIBRARIES=${libs}\")\n      endif()\n    endif()\n    return()\n  endif()\n\n  # Format messages for debug info and the type of error\n  set(vars \"Relevant CMake configuration variables:\\n\")\n  foreach (i ${configopts})\n    mark_as_advanced(CLEAR ${i})\n    set(val ${${i}})\n    if (\"${val}\" STREQUAL \"${i}-NOTFOUND\")\n      set (val \"<not found>\")\n    elseif (val AND NOT EXISTS \"${val}\")\n      set (val \"${val}  (does not exist)\")\n    else()\n      set(some_files TRUE)\n    endif()\n    set(vars \"${vars}  ${i}=${val}\\n\")\n  endforeach()\n  set(vars \"${vars}You may use CMake GUI, cmake -D or ccmake to modify the values. Delete CMakeCache.txt to discard all values and force full re-detection if necessary.\\n\")\n  if (version_unsuitable)\n    set(msg \"${PREFIX} ${${PREFIX}_VERSION} was found but\")\n    if (exactver)\n      set(msg \"${msg} only version ${findver} is acceptable.\")\n    else()\n      set(msg \"${msg} version ${findver} is the minimum requirement.\")\n    endif()\n  else()\n    if (missing_headers)\n      set(msg \"We could not find development headers for ${PREFIX}. Do you have the necessary dev package installed?\")\n    elseif (some_files)\n      set(msg \"We only found some files of ${PREFIX}, not all of them. Perhaps your installation is incomplete or maybe we just didn't look in the right place?\")\n      if(findver)\n        set(msg \"${msg} This could also be caused by incompatible version (if it helps, at least ${PREFIX} ${findver} should work).\")\n      endif()\n    else()\n      set(msg \"We were unable to find package ${PREFIX}.\")\n    endif()\n  endif()\n\n  # Fatal error out if REQUIRED\n  if (required)\n    set(msg \"REQUIRED PACKAGE NOT FOUND\\n${msg} This package is REQUIRED and you need to install it or adjust CMake configuration in order to continue building ${CMAKE_PROJECT_NAME}.\")\n    message(FATAL_ERROR \"${msg}\\n${vars}\")\n  endif()\n  # Otherwise just print a nasty warning\n  if (NOT quiet)\n    message(WARNING \"WARNING: MISSING PACKAGE\\n${msg} This package is NOT REQUIRED and you may ignore this warning but by doing so you may miss some functionality of ${CMAKE_PROJECT_NAME}. \\n${vars}\")\n  endif()\nendfunction()\n\n"
  },
  {
    "path": "cmake/OpenMWMacros.cmake",
    "content": "function(enable_unity_build UB_SUFFIX SOURCE_VARIABLE_NAME)\n    set(files ${SOURCE_VARIABLE_NAME})\n    # Generate a unique filename for the unity build translation unit\n    set(unit_build_file ${CMAKE_CURRENT_BINARY_DIR}/ub_${UB_SUFFIX}.cpp)\n    # Exclude all translation units from compilation\n    set_source_files_properties(${files} PROPERTIES HEADER_FILE_ONLY true)\n    # Open the ub file\n        FILE(WRITE ${unit_build_file} \"// Unity Build generated by CMake\\n\")\n    # Add include statement for each translation unit\n    foreach(source_file ${files} )\n        FILE( APPEND ${unit_build_file} \"#include <${source_file}>\\n\")\n    endforeach(source_file)\n    # Complement list of translation units with the name of ub\n    set(${SOURCE_VARIABLE_NAME} ${${SOURCE_VARIABLE_NAME}} ${unit_build_file} PARENT_SCOPE)\nendfunction(enable_unity_build)\n\n\n\nmacro (add_openmw_dir dir)\n    set (files)\n    set (cppfiles)\n\n    foreach (u ${ARGN})\n\n        # Add cpp and hpp to OPENMW_FILES\n        file (GLOB ALL \"${dir}/${u}.[ch]pp\")\n        foreach (f ${ALL})\n            list (APPEND files \"${f}\")\n            list (APPEND OPENMW_FILES \"${f}\")\n        endforeach (f)\n\n        # Add cpp to unity build\n        file (GLOB ALL \"${dir}/${u}.cpp\")\n        foreach (f ${ALL})\n            list (APPEND cppfiles \"${f}\")\n        endforeach (f)\n\n    endforeach (u)\n\n    if (OPENMW_UNITY_BUILD)\n        enable_unity_build(${newDir} \"${cppfiles}\")\n        list (APPEND OPENMW_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${newDir}.cpp)\n    endif()\n\n    string(REGEX REPLACE \"/\" \"\\\\\\\\\" newDir ${dir})\n    source_group (\"apps\\\\openmw\\\\${newDir}\" FILES ${files})\nendmacro (add_openmw_dir)\n\nmacro (add_component_dir dir)\n    set (files)\n    set (cppfiles)\n\n    foreach (u ${ARGN})\n        file (GLOB ALL \"${dir}/${u}.[ch]pp\")\n\n        foreach (f ${ALL})\n            list (APPEND files \"${f}\")\n            list (APPEND COMPONENT_FILES \"${f}\")\n        endforeach (f)\n\n        # Add cpp to unity build\n        file (GLOB ALL \"${dir}/${u}.cpp\")\n        foreach (f ${ALL})\n            list (APPEND cppfiles \"${f}\")\n        endforeach (f)\n\n    endforeach (u)\n\n    if (OPENMW_UNITY_BUILD)\n        enable_unity_build(${newDir} \"${cppfiles}\")\n        list (APPEND COMPONENT_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${newDir}.cpp)\n    endif()\n\n    string(REGEX REPLACE \"/\" \"\\\\\\\\\" newDir ${dir})\n    source_group (\"components\\\\${newDir}\" FILES ${files})\nendmacro (add_component_dir)\n\nmacro (add_component_qt_dir dir)\nset (files)\nforeach (u ${ARGN})\nfile (GLOB ALL \"${dir}/${u}.[ch]pp\")\nforeach (f ${ALL})\nlist (APPEND files \"${f}\")\nlist (APPEND COMPONENT_FILES \"${f}\")\nendforeach (f)\nfile (GLOB MOC_H \"${dir}/${u}.hpp\")\nforeach (fi ${MOC_H})\nlist (APPEND COMPONENT_MOC_FILES \"${fi}\")\nendforeach (fi)\nendforeach (u)\nsource_group (\"components\\\\${dir}\" FILES ${files})\nendmacro (add_component_qt_dir)\n\nmacro (add_file project type file)\nlist (APPEND ${project}${type} ${file})\nendmacro (add_file)\n\nmacro (add_unit project dir unit)\nadd_file (${project} _HDR ${comp} \"${dir}/${unit}.hpp\")\nadd_file (${project} _SRC ${comp} \"${dir}/${unit}.cpp\")\nendmacro (add_unit)\n\nmacro (add_qt_unit project dir unit)\nadd_file (${project} _HDR ${comp} \"${dir}/${unit}.hpp\")\nadd_file (${project} _HDR_QT ${comp} \"${dir}/${unit}.hpp\")\nadd_file (${project} _SRC ${comp} \"${dir}/${unit}.cpp\")\nendmacro (add_qt_unit)\n\nmacro (add_hdr project dir unit)\nadd_file (${project} _HDR ${comp} \"${dir}/${unit}.hpp\")\nendmacro (add_hdr)\n\nmacro (add_qt_hdr project dir unit)\nadd_file (${project} _HDR ${comp} \"${dir}/${unit}.hpp\")\nadd_file (${project} _HDR_QT ${comp} \"${dir}/${unit}.hpp\")\nendmacro (add_qt_hdr)\n\nmacro (opencs_units dir)\nforeach (u ${ARGN})\nadd_qt_unit (OPENCS ${dir} ${u})\nendforeach (u)\nendmacro (opencs_units)\n\nmacro (opencs_units_noqt dir)\nforeach (u ${ARGN})\nadd_unit (OPENCS ${dir} ${u})\nendforeach (u)\nendmacro (opencs_units_noqt)\n\nmacro (opencs_hdrs dir)\nforeach (u ${ARGN})\nadd_qt_hdr (OPENCS ${dir} ${u})\nendforeach (u)\nendmacro (opencs_hdrs)\n\nmacro (opencs_hdrs_noqt dir)\nforeach (u ${ARGN})\nadd_hdr (OPENCS ${dir} ${u})\nendforeach (u)\nendmacro (opencs_hdrs_noqt)\n\ninclude(CMakeParseArguments)\n\nmacro (openmw_add_executable target)\n\tset(OMW_ADD_EXE_OPTIONS WIN32 MACOSX_BUNDLE EXCLUDE_FROM_ALL)\n\tset(OMW_ADD_EXE_VALUES)\n\tset(OMW_ADD_EXE_MULTI_VALUES)\n\tcmake_parse_arguments(OMW_ADD_EXE \"${OMW_ADD_EXE_OPTIONS}\" \"${OMW_ADD_EXE_VALUES}\" \"${OMW_ADD_EXE_MULTI_VALUES}\" ${ARGN})\n\t\n\tif (OMW_ADD_EXE_WIN32)\n\t\tset(OMW_ADD_EXE_WIN32_VALUE WIN32)\n\tendif (OMW_ADD_EXE_WIN32)\n\t\n\tif (OMW_ADD_EXE_MACOSX_BUNDLE)\n\t\tset(OMW_ADD_EXE_MACOSX_BUNDLE_VALUE MACOSX_BUNDLE)\n\tendif (OMW_ADD_EXE_MACOSX_BUNDLE)\n\t\n\tif (OMW_ADD_EXE_EXCLUDE_FROM_ALL)\n\t\tset(OMW_ADD_EXE_EXCLUDE_FROM_ALL_VALUE EXCLUDE_FROM_ALL)\n\tendif (OMW_ADD_EXE_EXCLUDE_FROM_ALL)\n\t\n\tadd_executable(${target} ${OMW_ADD_EXE_WIN32_VALUE} ${OMW_ADD_EXE_MACOSX_BUNDLE_VALUE} ${OMW_ADD_EXE_EXCLUDE_FROM_ALL_VALUE} ${OMW_ADD_EXE_UNPARSED_ARGUMENTS})\n\t\n\tif (MSVC)\n\t\tif (CMAKE_VERSION VERSION_GREATER 3.8 OR CMAKE_VERSION VERSION_EQUAL 3.8)\n\t\t\tset_target_properties(${target} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY \"$<TARGET_FILE_DIR:${target}>\")\n\t\tendif (CMAKE_VERSION VERSION_GREATER 3.8 OR CMAKE_VERSION VERSION_EQUAL 3.8)\n\tendif (MSVC)\nendmacro (openmw_add_executable)\n\nmacro (get_generator_is_multi_config VALUE)\n\tif (DEFINED generator_is_multi_config_var)\n\t\tset(${VALUE} ${generator_is_multi_config_var})\n\telse (DEFINED generator_is_multi_config_var)\n\t\tif (CMAKE_VERSION VERSION_GREATER 3.9 OR CMAKE_VERSION VERSION_EQUAL 3.9)\n\t\t\tget_cmake_property(${VALUE} GENERATOR_IS_MULTI_CONFIG)\n\t\telse (CMAKE_VERSION VERSION_GREATER 3.9 OR CMAKE_VERSION VERSION_EQUAL 3.9)\n\t\t\tlist(LENGTH CMAKE_CONFIGURATION_TYPES ${VALUE})\n\t\tendif (CMAKE_VERSION VERSION_GREATER 3.9 OR CMAKE_VERSION VERSION_EQUAL 3.9)\n\tendif (DEFINED generator_is_multi_config_var)\nendmacro (get_generator_is_multi_config)\n\nmacro (copy_resource_file source_path destination_dir_base dest_path_relative)\n\tget_generator_is_multi_config(multi_config)\n\tif (multi_config)\n\t\tforeach(cfgtype ${CMAKE_CONFIGURATION_TYPES})\n\t\t\tconfigure_file(${source_path} \"${destination_dir_base}/${cfgtype}/${dest_path_relative}\" COPYONLY)\n\t\tendforeach(cfgtype)\n\telse (multi_config)\n\t\tconfigure_file(${source_path} \"${destination_dir_base}/${dest_path_relative}\" COPYONLY)\n\tendif (multi_config)\nendmacro (copy_resource_file)\n\nmacro (configure_resource_file source_path destination_dir_base dest_path_relative)\n\tget_generator_is_multi_config(multi_config)\n\tif (multi_config)\n\t\tforeach(cfgtype ${CMAKE_CONFIGURATION_TYPES})\n\t\t\tconfigure_file(${source_path} \"${destination_dir_base}/${cfgtype}/${dest_path_relative}\")\n\t\tendforeach(cfgtype)\n\telse (multi_config)\n\t\tconfigure_file(${source_path} \"${destination_dir_base}/${dest_path_relative}\")\n\tendif (multi_config)\nendmacro (configure_resource_file)\n\nmacro (pack_resource_file source_path destination_dir_base dest_path_relative)\n    get_generator_is_multi_config(multi_config)\n    if (multi_config)\n        foreach(cfgtype ${CMAKE_CONFIGURATION_TYPES})\n            execute_process(COMMAND ${CMAKE_COMMAND} \"-DINPUT_FILE=${source_path}\" \"-DOUTPUT_FILE=${destination_dir_base}/${cfgtype}/${dest_path_relative}\" -P \"${CMAKE_SOURCE_DIR}/cmake/base64.cmake\")\n        endforeach(cfgtype)\n    else (multi_config)\n        execute_process(COMMAND ${CMAKE_COMMAND} \"-DINPUT_FILE=${source_path}\" \"-DOUTPUT_FILE=${destination_dir_base}/${dest_path_relative}\" -P \"${CMAKE_SOURCE_DIR}/cmake/base64.cmake\")\n    endif (multi_config)\n    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS \"${source_path}\")\nendmacro (pack_resource_file)\n\nmacro (copy_all_resource_files source_dir destination_dir_base destination_dir_relative files)\n\tforeach (f ${files})\n\t\tget_filename_component(filename ${f} NAME)\n\t\tcopy_resource_file(\"${source_dir}/${f}\" \"${destination_dir_base}\" \"${destination_dir_relative}/${filename}\")\n\tendforeach (f)\nendmacro (copy_all_resource_files)\n"
  },
  {
    "path": "cmake/WholeArchive.cmake",
    "content": "function (get_whole_archive_options OUT_VAR)\n    # We use --whole-archive because OSG plugins use registration.\n    if (CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\" OR CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n        set(${OUT_VAR} -Wl,--whole-archive ${ARGN} -Wl,--no-whole-archive PARENT_SCOPE)\n    elseif (CMAKE_CXX_COMPILER_ID STREQUAL \"AppleClang\")\n        set(${OUT_VAR} -Wl,-all_load ${ARGN} -Wl,-noall_load PARENT_SCOPE)\n    else ()\n        message(FATAL_ERROR \"get_whole_archive_options not implemented for CMAKE_CXX_COMPILER_ID ${CMAKE_CXX_COMPILER_ID}\")\n    endif()\nendfunction ()\n"
  },
  {
    "path": "cmake/base64.cmake",
    "content": "# math(EXPR \"...\") can't parse hex until 3.13\ncmake_minimum_required(VERSION 3.13)\n\nif (NOT DEFINED INPUT_FILE)\n    message(STATUS \"Usage: cmake -DINPUT_FILE=\\\"infile.ext\\\" -DOUTPUT_FILE=\\\"out.txt\\\" -P base64.cmake\")\n    message(FATAL_ERROR \"INPUT_FILE not specified\")\nendif()\n\nif (NOT DEFINED OUTPUT_FILE)\n    message(STATUS \"Usage: cmake -DINPUT_FILE=\\\"infile.ext\\\" -DOUTPUT_FILE=\\\"out.txt\\\" -P base64.cmake\")\n    message(FATAL_ERROR \"OUTPUT_FILE not specified\")\nendif()\n\nif (NOT EXISTS ${INPUT_FILE})\n    message(FATAL_ERROR \"INPUT_FILE ${INPUT_FILE} does not exist\")\nendif()\n\nset(lut \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\")\n\nfile(READ \"${INPUT_FILE}\" hexContent HEX)\n\nset(base64Content \"\")\nwhile(TRUE)\n    string(LENGTH \"${hexContent}\" tailLength)\n    if (tailLength LESS 1)\n        break()\n    endif()\n\n    string(SUBSTRING \"${hexContent}\" 0 6 head)\n    # base64 works on three-byte chunks. Pad.\n    string(LENGTH \"${head}\" headLength)\n    if (headLength LESS 6)\n        set(hexContent \"\")\n        math(EXPR padSize \"6 - ${headLength}\")\n        set(pad \"\")\n        foreach(i RANGE 1 ${padSize})\n            string(APPEND pad \"0\")\n        endforeach()\n        string(APPEND head \"${pad}\")\n    else()\n        string(SUBSTRING \"${hexContent}\" 6 -1 hexContent)\n        set(padSize 0)   \n    endif()\n\n    # get six-bit slices\n    math(EXPR first \"0x${head} >> 18\")\n    math(EXPR second \"(0x${head} & 0x3F000) >> 12\")\n    math(EXPR third \"(0x${head} & 0xFC0) >> 6\")\n    math(EXPR fourth \"0x${head} & 0x3F\")\n\n    # first two characters are always needed to represent the first byte\n    string(SUBSTRING \"${lut}\" ${first} 1 char)\n    string(APPEND base64Content \"${char}\")\n    string(SUBSTRING \"${lut}\" ${second} 1 char)\n    string(APPEND base64Content \"${char}\")\n\n    # if there's no second byte, pad with =\n    if (NOT padSize EQUAL 4)\n        string(SUBSTRING \"${lut}\" ${third} 1 char)\n        string(APPEND base64Content \"${char}\")\n    else()\n        string(APPEND base64Content \"=\")\n    endif()\n\n    # if there's no third byte, pad with =\n    if (padSize EQUAL 0)\n        string(SUBSTRING \"${lut}\" ${fourth} 1 char)\n        string(APPEND base64Content \"${char}\")\n    else()\n        string(APPEND base64Content \"=\")\n    endif()\nendwhile()\n\nfile(WRITE \"${OUTPUT_FILE}\" \"${base64Content}\")"
  },
  {
    "path": "components/CMakeLists.txt",
    "content": "project (Components)\n\n# Version file\nset (VERSION_IN_FILE \"${OpenMW_SOURCE_DIR}/files/version.in\")\nset (VERSION_FILE_PATH_BASE \"${OpenMW_BINARY_DIR}\")\nset (VERSION_FILE_PATH_RELATIVE resources/version)\nif (GIT_CHECKOUT)\n    get_generator_is_multi_config(multi_config)\n    add_custom_target (git-version\n       COMMAND ${CMAKE_COMMAND}\n            -DGIT_EXECUTABLE=${GIT_EXECUTABLE}\n            -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}\n            -DVERSION_IN_FILE=${VERSION_IN_FILE}\n            -DVERSION_FILE_PATH_BASE=${VERSION_FILE_PATH_BASE}\n            -DVERSION_FILE_PATH_RELATIVE=${VERSION_FILE_PATH_RELATIVE}\n            -DOPENMW_VERSION_MAJOR=${OPENMW_VERSION_MAJOR}\n            -DOPENMW_VERSION_MINOR=${OPENMW_VERSION_MINOR}\n            -DOPENMW_VERSION_RELEASE=${OPENMW_VERSION_RELEASE}\n            -DOPENMW_VERSION=${OPENMW_VERSION}\n            -DMACROSFILE=${CMAKE_SOURCE_DIR}/cmake/OpenMWMacros.cmake\n            \"-DCMAKE_CONFIGURATION_TYPES=${CMAKE_CONFIGURATION_TYPES}\"\n            -Dgenerator_is_multi_config_var=${multi_config}\n            -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/GitVersion.cmake\n            VERBATIM)\nelse (GIT_CHECKOUT)\n    configure_resource_file(${VERSION_IN_FILE} ${VERSION_FILE_PATH_BASE} ${VERSION_FILE_PATH_RELATIVE})\nendif (GIT_CHECKOUT)\n\n# source files\n\nadd_component_dir (settings\n    settings parser\n    )\n\n# Start of tes3mp change\n#\n# Don't include certain components in server-only builds\nIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\nadd_component_dir (bsa\n    bsa_file compressedbsafile memorystream\n    )\n\nadd_component_dir (vfs\n    manager archive bsaarchive filesystemarchive registerarchives\n    )\n\nadd_component_dir (resource\n    scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem\n    resourcemanager stats animation\n    )\n\nadd_component_dir (shader\n    shadermanager shadervisitor removedalphafunc\n    )\n\nadd_component_dir (sceneutil\n    clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller\n    lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer\n    actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller\n    )\n\nadd_component_dir (nif\n    controlled effect niftypes record controller extra node record_ptr data niffile property nifkey base nifstream\n    )\n\nadd_component_dir (nifosg\n    nifloader controller particle matrixtransform\n    )\n\nadd_component_dir (nifbullet\n    bulletnifloader\n    )\n# Start of tes3mp change\n#\n# Don't include certain components in server-only builds\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\n\nadd_component_dir (to_utf8\n    to_utf8\n    )\n\nadd_component_dir (esm\n    attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell\n    loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst\n    loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc\n    loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat\n    loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter\n    savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate\n    npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile\n    aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate mappings\n    )\n\n# Start of tes3mp change\n#\n# Don't include certain components in server-only builds\nIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\nadd_component_dir (esmterrain\n    storage\n    )\n# Start of tes3mp change\n#\n# Don't include certain components in server-only builds\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\n\nadd_component_dir (misc\n    constants utf8stream stringops resourcehelpers rng messageformatparser weakcache thread\n    )\n\nadd_component_dir (debug\n    debugging debuglog gldebug\n    )\n\nIF(NOT WIN32 AND NOT APPLE)\n    add_definitions(-DGLOBAL_DATA_PATH=\"${GLOBAL_DATA_PATH}\")\n    add_definitions(-DGLOBAL_CONFIG_PATH=\"${GLOBAL_CONFIG_PATH}\")\nENDIF()\n\nadd_component_dir (files\n    linuxpath androidpath windowspath macospath fixedpath multidircollection collections configurationmanager escape\n    lowlevelfile constrainedfilestream memorystream\n    )\n\nadd_component_dir (compiler\n    context controlparser errorhandler exception exprparser extensions fileparser generator\n    lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler\n    stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser\n    quickfileparser discardparser junkparser\n    )\n\nadd_component_dir (interpreter\n    context controlopcodes genericopcodes installopcodes interpreter localopcodes mathopcodes\n    miscopcodes opcodes runtime types defines\n    )\n\nadd_component_dir (translation\n    translation\n    )\n\n# Start of tes3mp addition\n#\n# Don't include certain components in server-only builds\nIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\nadd_component_dir (terrain\n    storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager compositemaprenderer quadtreeworld quadtreenode viewdata cellborder\n    )\n\nadd_component_dir (loadinglistener\n    loadinglistener\n    )\n\nadd_component_dir (myguiplatform\n    myguirendermanager myguidatamanager myguiplatform myguitexture myguiloglistener additivelayer scalinglayer\n    )\n\nadd_component_dir (widgets\n    box fontwrapper imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets\n    )\n\nadd_component_dir (fontloader\n    fontloader\n    )\n\nadd_component_dir (sdlutil\n    gl4es_init sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper events sdlcursormanager\n    )\n# Start of tes3mp change\n#\n# Don't include certain components in server-only builds\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\n\nadd_component_dir (version\n    version\n    )\n\nadd_component_dir (openmw-mp\n        TimedLog Utils ErrorMessages NetworkMessages Version\n        )\n\nadd_component_dir (openmw-mp/Base\n        BaseActor BaseObject BasePacketProcessor BasePlayer BaseStructs BaseSystem BaseWorldstate\n        )\n\nadd_component_dir (openmw-mp/Controllers\n        SystemPacketController PlayerPacketController ActorPacketController ObjectPacketController WorldstatePacketController\n        )\n\nadd_component_dir(openmw-mp/Master\n        MasterData PacketMasterQuery PacketMasterUpdate PacketMasterAnnounce BaseMasterPacket ProxyMasterPacket\n        )\n\nadd_component_dir (openmw-mp/Packets\n        BasePacket PacketPreInit\n        )\n\nadd_component_dir (openmw-mp/Packets/Actor\n        ActorPacket\n\n        PacketActorList PacketActorAuthority PacketActorTest PacketActorAI PacketActorAnimFlags PacketActorAnimPlay\n        PacketActorAttack PacketActorCast PacketActorCellChange PacketActorDeath PacketActorEquipment PacketActorPosition\n        PacketActorSpeech PacketActorSpellsActive PacketActorStatsDynamic\n        )\n\nadd_component_dir (openmw-mp/Packets/System\n        SystemPacket\n\n        PacketSystemHandshake\n        )\n\nadd_component_dir (openmw-mp/Packets/Player\n        PlayerPacket\n\n        PacketChatMessage PacketGUIBoxes PacketGameSettings\n\n        PacketPlayerBaseInfo PacketPlayerCharGen PacketPlayerAlly PacketPlayerAnimFlags PacketPlayerAnimPlay\n        PacketPlayerAttack PacketPlayerAttribute PacketPlayerBehavior PacketPlayerBook PacketPlayerBounty\n        PacketPlayerCast PacketPlayerCellChange PacketPlayerCellState PacketPlayerClass PacketPlayerCooldowns\n        PacketPlayerDeath PacketPlayerEquipment PacketPlayerFaction PacketPlayerInput PacketPlayerInventory\n        PacketPlayerItemUse PacketPlayerJail PacketPlayerJournal PacketPlayerLevel PacketPlayerMiscellaneous\n        PacketPlayerMomentum PacketPlayerPosition PacketPlayerQuickKeys PacketPlayerReputation PacketPlayerRest\n        PacketPlayerResurrect PacketPlayerShapeshift PacketPlayerSkill PacketPlayerSpeech PacketPlayerSpellbook\n        PacketPlayerSpellsActive PacketPlayerStatsDynamic PacketPlayerTopic\n        )\n\nadd_component_dir (openmw-mp/Packets/Object\n        ObjectPacket\n\n        PacketDoorDestination PacketDoorState\n\n        PacketConsoleCommand PacketContainer PacketObjectActivate PacketObjectAnimPlay PacketObjectAttach\n        PacketObjectDelete PacketObjectDialogueChoice PacketObjectHit PacketObjectLock PacketObjectMove PacketObjectPlace\n        PacketObjectRestock PacketObjectRotate PacketObjectScale PacketObjectSound PacketObjectSpawn PacketObjectState\n        PacketObjectTrap PacketMusicPlay PacketVideoPlay PacketClientScriptLocal PacketScriptMemberShort\n        PacketObjectMiscellaneous\n        )\n\nadd_component_dir (openmw-mp/Packets/Worldstate\n        WorldstatePacket\n\n        PacketCellCreate PacketCellReset PacketClientScriptGlobal PacketClientScriptSettings PacketRecordDynamic\n        PacketWorldCollisionOverride PacketWorldDestinationOverride PacketWorldKillCount PacketWorldMap\n        PacketWorldRegionAuthority PacketWorldTime PacketWorldWeather\n        )\n\nadd_component_dir (fallback\n    fallback validate\n    )\n\n# Start of tes3mp change (major)\n#\n# Don't require the crashcatcher when building on platforms other than Windows or when building only the server,\n# as it causes compilation problems\nIF (BUILD_OPENMW OR BUILD_OPENCS)\n    if(WIN32)\n        add_component_dir (crashcatcher\n            windows_crashcatcher\n            windows_crashmonitor\n            windows_crashshm\n        )\n    endif()\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change (major)\n\n# Start of tes3mp change (major)\n#\n# Don't require the DetourNavigator when building the server\nIF (BUILD_OPENMW OR BUILD_OPENCS)\n    add_component_dir(detournavigator\n            debug\n            makenavmesh\n            findsmoothpath\n            recastmeshbuilder\n            recastmeshmanager\n            cachedrecastmeshmanager\n            navmeshmanager\n            navigatorimpl\n            asyncnavmeshupdater\n            recastmesh\n            tilecachedrecastmeshmanager\n            recastmeshobject\n            navmeshtilescache\n            settings\n            navigator\n            findrandompointaroundcircle\n            raycast\n            navmeshtileview\n            oscillatingrecastmeshobject\n            offmeshconnectionsmanager\n            )\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change (major)\n\nset (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui\n    )\n\n# Start of tes3mp change (major)\n#\n# Don't require Qt when building the server\nIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\nif (USE_QT)\n    add_component_qt_dir (contentselector\n        model/modelitem model/esmfile\n        model/naturalsort model/contentmodel\n        model/loadordererror\n        view/combobox view/contentselector\n        )\n    add_component_qt_dir (config\n        gamesettings\n        launchersettings\n        settingsbase\n        )\n\n    add_component_qt_dir (process\n        processinvoker\n        )\n\n    add_component_dir (misc\n        helpviewer\n        )\n\n    QT5_WRAP_UI(ESM_UI_HDR ${ESM_UI})\n    QT5_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES})\nendif()\n# Start of tes3mp change (major)\n#\n# Don't require Qt when building the server\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\n\nif (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n    if(\"${CMAKE_SYSTEM_PROCESSOR}\" STREQUAL \"x86_64\" AND NOT APPLE)\n        add_definitions(-fPIC)\n    endif()\nendif ()\n\ninclude_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})\n\nadd_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR})\n\n# Start of tes3mp change (major)\n#\n# Don't require graphics related libs when building the server\nIF (BUILD_OPENMW OR BUILD_OPENCS)\ntarget_link_libraries(components\n    # CMake's built-in OSG finder does not use pkgconfig, so we have to\n    # manually ensure the order is correct for inter-library dependencies.\n    # This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.\n    # https://gitlab.kitware.com/cmake/cmake/-/issues/21701\n    ${OSGPARTICLE_LIBRARIES}\n    ${OSGVIEWER_LIBRARIES}\n    ${OSGSHADOW_LIBRARIES}\n    ${OSGANIMATION_LIBRARIES}\n    ${OSGGA_LIBRARIES}\n    ${OSGTEXT_LIBRARIES}\n    ${OSGDB_LIBRARIES}\n    ${OSGUTIL_LIBRARIES}\n    ${OSG_LIBRARIES}\n    ${OPENTHREADS_LIBRARIES}\n\n    ${SDL2_LIBRARIES}\n    ${OPENGL_gl_LIBRARY}\n    ${MyGUI_LIBRARIES}\n    LZ4::LZ4\n    )\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\ntarget_link_libraries(components\n    ${Boost_SYSTEM_LIBRARY}\n    ${Boost_FILESYSTEM_LIBRARY}\n    ${Boost_PROGRAM_OPTIONS_LIBRARY}\n    ${Boost_IOSTREAMS_LIBRARY}\n    )\n# End of tes3mp change (major)\n\n# Start of tes3mp change (major)\n#\n# Don't require RecastNavigation nor Bullet when building the server\nIF (BUILD_OPENMW OR BUILD_OPENCS)\n    target_link_libraries(components\n            RecastNavigation::DebugUtils\n            RecastNavigation::Detour\n            RecastNavigation::Recast\n\n            ${BULLET_LIBRARIES}\n            )\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\ntarget_link_libraries(components Base64)\n# End of tes3mp change (major)\n\nif (WIN32)\n    target_link_libraries(components\n    ${Boost_LOCALE_LIBRARY}\n    ${Boost_ZLIB_LIBRARY})\nendif()\n\n# Start of tes3mp change (major)\n#\n# Don't require Qt when building the server\nIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\nif (USE_QT)\n    target_link_libraries(components Qt5::Widgets Qt5::Core)\nendif()\n# Start of tes3mp change (major)\n#\n# Don't require Qt when building the server\nENDIF (BUILD_OPENMW OR BUILD_OPENCS)\n# End of tes3mp change\n\nif (GIT_CHECKOUT)\n    add_dependencies (components git-version)\nendif (GIT_CHECKOUT)\n\nif (WIN32)\n    target_link_libraries(components shlwapi)\nendif()\n\n# Fix for not visible pthreads functions for linker with glibc 2.15\nif (UNIX AND NOT APPLE)\ntarget_link_libraries(components ${CMAKE_THREAD_LIBS_INIT})\nendif()\n\nif (BUILD_WITH_CODE_COVERAGE)\n    add_definitions(--coverage)\n    target_link_libraries(components gcov)\nendif()\n\n\n# Make the variable accessible for other subdirectories\nset(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE)\n\ntarget_compile_definitions(components PUBLIC BT_USE_DOUBLE_PRECISION)\n"
  },
  {
    "path": "components/bsa/bsa_file.cpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (bsa_file.cpp) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#include \"bsa_file.hpp\"\n\n#include <cassert>\n\n#include <boost/filesystem/path.hpp>\n#include <boost/filesystem/fstream.hpp>\n#include <boost/filesystem/operations.hpp>\n\nusing namespace Bsa;\n\n\n/// Error handling\nvoid BSAFile::fail(const std::string &msg)\n{\n    throw std::runtime_error(\"BSA Error: \" + msg + \"\\nArchive: \" + mFilename);\n}\n\n//the getHash code is from bsapack from ghostwheel\n//the code is also the same as in https://github.com/arviceblot/bsatool_rs/commit/67cb59ec3aaeedc0849222ea387f031c33e48c81\nBSAFile::Hash getHash(const std::string& name)\n{\n    BSAFile::Hash hash;\n    unsigned l = (static_cast<unsigned>(name.size()) >> 1);\n    unsigned sum, off, temp, i, n;\n\n    for (sum = off = i = 0; i < l; i++) {\n        sum ^= (((unsigned)(name[i])) << (off & 0x1F));\n        off += 8;\n    }\n    hash.low = sum;\n\n    for (sum = off = 0; i < name.size(); i++) {\n        temp = (((unsigned)(name[i])) << (off & 0x1F));\n        sum ^= temp;\n        n = temp & 0x1F;\n        sum = (sum << (32 - n)) | (sum >> n);  // binary \"rotate right\"\n        off += 8;\n    }\n    hash.high = sum;\n    return hash;\n}\n\n/// Read header information from the input source\nvoid BSAFile::readHeader()\n{\n    /*\n     * The layout of a BSA archive is as follows:\n     *\n     * - 12 bytes header, contains 3 ints:\n     *         id number - equal to 0x100\n     *         dirsize - size of the directory block (see below)\n     *         numfiles - number of files\n     *\n     * ---------- start of directory block -----------\n     *\n     * - 8 bytes*numfiles, each record contains:\n     *         fileSize\n     *         offset into data buffer (see below)\n     *\n     * - 4 bytes*numfiles, each record is an offset into the following name buffer\n     *\n     * - name buffer, indexed by the previous table, each string is\n     *   null-terminated. Size is (dirsize - 12*numfiles).\n     *\n     * ---------- end of directory block -------------\n     *\n     * - 8*filenum - hash table block, we currently ignore this\n     *\n     * ----------- start of data buffer --------------\n     *\n     * - The rest of the archive is file data, indexed by the\n     *   offsets in the directory block. The offsets start at 0 at\n     *   the beginning of this buffer.\n     *\n     */\n    assert(!mIsLoaded);\n\n    namespace bfs = boost::filesystem;\n    bfs::ifstream input(bfs::path(mFilename), std::ios_base::binary);\n\n    // Total archive size\n    std::streamoff fsize = 0;\n    if(input.seekg(0, std::ios_base::end))\n    {\n        fsize = input.tellg();\n        input.seekg(0);\n    }\n\n    if(fsize < 12)\n        fail(\"File too small to be a valid BSA archive\");\n\n    // Get essential header numbers\n    size_t dirsize, filenum;\n    {\n        // First 12 bytes\n        uint32_t head[3];\n\n        input.read(reinterpret_cast<char*>(head), 12);\n\n        if(head[0] != 0x100)\n            fail(\"Unrecognized BSA header\");\n\n        // Total number of bytes used in size/offset-table + filename\n        // sections.\n        dirsize = head[1];\n\n        // Number of files\n        filenum = head[2];\n    }\n\n    // Each file must take up at least 21 bytes of data in the bsa. So\n    // if files*21 overflows the file size then we are guaranteed that\n    // the archive is corrupt.\n    if((filenum*21 > unsigned(fsize -12)) || (dirsize+8*filenum > unsigned(fsize -12)) )\n        fail(\"Directory information larger than entire archive\");\n\n    // Read the offset info into a temporary buffer\n    std::vector<uint32_t> offsets(3*filenum);\n    input.read(reinterpret_cast<char*>(offsets.data()), 12*filenum);\n\n    // Read the string table\n    mStringBuf.resize(dirsize-12*filenum);\n    input.read(mStringBuf.data(), mStringBuf.size());\n\n    // Check our position\n    assert(input.tellg() == std::streampos(12+dirsize));\n    std::vector<Hash> hashes(filenum);\n    static_assert(sizeof(Hash) == 8);\n    input.read(reinterpret_cast<char*>(hashes.data()), 8*filenum);\n\n    // Calculate the offset of the data buffer. All file offsets are\n    // relative to this. 12 header bytes + directory + hash table\n    // (skipped)\n    size_t fileDataOffset = 12 + dirsize + 8*filenum;\n\n    // Set up the the FileStruct table\n    mFiles.resize(filenum);\n    size_t endOfNameBuffer = 0;\n    for(size_t i=0;i<filenum;i++)\n    {\n        FileStruct &fs = mFiles[i];\n        fs.fileSize = offsets[i*2];\n        fs.offset = static_cast<uint32_t>(offsets[i*2+1] + fileDataOffset);\n        auto namesOffset = offsets[2*filenum+i];\n        fs.setNameInfos(namesOffset, &mStringBuf);\n        fs.hash = hashes[i];\n\n        if (namesOffset >= mStringBuf.size()) {\n            fail(\"Archive contains names offset outside itself\");\n        }\n        const void* end = std::memchr(fs.name(), '\\0', mStringBuf.size()-namesOffset);\n        if (!end) {\n            fail(\"Archive contains non-zero terminated string\");\n        }\n\n        endOfNameBuffer = std::max(endOfNameBuffer, namesOffset + std::strlen(fs.name())+1);\n        assert(endOfNameBuffer <= mStringBuf.size());\n\n        if(fs.offset + fs.fileSize > fsize)\n            fail(\"Archive contains offsets outside itself\");\n\n    }\n    mStringBuf.resize(endOfNameBuffer);\n\n    std::sort(mFiles.begin(), mFiles.end(), [](const FileStruct& left, const FileStruct& right) {\n        return left.offset < right.offset;\n    });\n\n    for (size_t i = 0; i < filenum; i++)\n    {\n        FileStruct& fs = mFiles[i];\n        // Add the file name to the lookup\n        mLookup[fs.name()] = i;\n    }\n\n    mIsLoaded = true;\n}\n\n/// Write header information to the output sink\nvoid Bsa::BSAFile::writeHeader()\n{\n    namespace bfs = boost::filesystem;\n    bfs::fstream output(mFilename, std::ios::binary | std::ios::in | std::ios::out);\n\n    uint32_t head[3];\n    head[0] = 0x100;\n    auto fileDataOffset = mFiles.empty() ? 12 : mFiles.front().offset;\n    head[1] = static_cast<uint32_t>(fileDataOffset - 12 - 8*mFiles.size());\n\n    output.seekp(0, std::ios_base::end);\n\n    head[2] = static_cast<uint32_t>(mFiles.size());\n    output.seekp(0);\n    output.write(reinterpret_cast<char*>(head), 12);\n\n    std::sort(mFiles.begin(), mFiles.end(), [](const FileStruct& left, const FileStruct& right) {\n        return std::make_pair(left.hash.low, left.hash.high) < std::make_pair(right.hash.low, right.hash.high);\n    });\n\n    size_t filenum = mFiles.size();\n    std::vector<uint32_t> offsets(3* filenum);\n    std::vector<Hash> hashes(filenum);\n    for(size_t i=0;i<filenum;i++)\n    {\n        auto& f = mFiles[i];\n        offsets[i*2] = f.fileSize;\n        offsets[i*2+1] = f.offset - fileDataOffset;\n        offsets[2*filenum+i] = f.namesOffset;\n        hashes[i] = f.hash;\n    }\n    output.write(reinterpret_cast<char*>(offsets.data()), sizeof(uint32_t)*offsets.size());\n    output.write(reinterpret_cast<char*>(mStringBuf.data()), mStringBuf.size());\n    output.seekp(fileDataOffset - 8*mFiles.size(), std::ios_base::beg);\n    output.write(reinterpret_cast<char*>(hashes.data()), sizeof(Hash)*hashes.size());\n}\n\n/// Get the index of a given file name, or -1 if not found\nint BSAFile::getIndex(const char *str) const\n{\n    auto it = mLookup.find(str);\n    if(it == mLookup.end())\n        return -1;\n\n    size_t res = it->second;\n    assert(res < mFiles.size());\n    return static_cast<int>(res);\n}\n\n/// Open an archive file.\nvoid BSAFile::open(const std::string &file)\n{\n    if (mIsLoaded)\n        close();\n\n    mFilename = file;\n    if(boost::filesystem::exists(file))\n        readHeader();\n    else\n    {\n        { boost::filesystem::fstream(mFilename, std::ios::binary | std::ios::out); }\n        writeHeader();\n        mIsLoaded = true;\n    }\n}\n\n/// Close the archive, write the updated headers to the file\nvoid Bsa::BSAFile::close()\n{\n    if (mHasChanged)\n        writeHeader();\n\n    mFiles.clear();\n    mStringBuf.clear();\n    mLookup.clear();\n    mIsLoaded = false;\n}\n\nFiles::IStreamPtr BSAFile::getFile(const char *file)\n{\n    assert(file);\n    int i = getIndex(file);\n    if(i == -1)\n        fail(\"File not found: \" + std::string(file));\n\n    const FileStruct &fs = mFiles[i];\n\n    return Files::openConstrainedFileStream (mFilename.c_str (), fs.offset, fs.fileSize);\n}\n\nFiles::IStreamPtr BSAFile::getFile(const FileStruct *file)\n{\n    return Files::openConstrainedFileStream (mFilename.c_str (), file->offset, file->fileSize);\n}\n\nvoid Bsa::BSAFile::addFile(const std::string& filename, std::istream& file)\n{\n    if (!mIsLoaded)\n        fail(\"Unable to add file \" + filename + \" the archive is not opened\");\n    namespace bfs = boost::filesystem;\n\n    auto newStartOfDataBuffer = 12 + (12 + 8) * (mFiles.size() + 1) + mStringBuf.size() + filename.size() + 1;\n    if (mFiles.empty())\n        bfs::resize_file(mFilename, newStartOfDataBuffer);\n\n    bfs::fstream stream(mFilename, std::ios::binary | std::ios::in | std::ios::out);\n\n    FileStruct newFile;\n    file.seekg(0, std::ios::end);\n    newFile.fileSize = static_cast<uint32_t>(file.tellg());\n    newFile.setNameInfos(mStringBuf.size(), &mStringBuf);\n    newFile.hash = getHash(filename);\n\n    if(mFiles.empty())\n        newFile.offset = static_cast<uint32_t>(newStartOfDataBuffer);\n    else\n    {\n        std::vector<char> buffer;\n        while (mFiles.front().offset < newStartOfDataBuffer) {\n            FileStruct& firstFile = mFiles.front();\n            buffer.resize(firstFile.fileSize);\n\n            stream.seekg(firstFile.offset, std::ios::beg);\n            stream.read(buffer.data(), firstFile.fileSize);\n\n            stream.seekp(0, std::ios::end);\n            firstFile.offset = static_cast<uint32_t>(stream.tellp());\n\n            stream.write(buffer.data(), firstFile.fileSize);\n\n            //ensure sort order is preserved\n            std::rotate(mFiles.begin(), mFiles.begin() + 1, mFiles.end());\n        }\n        stream.seekp(0, std::ios::end);\n        newFile.offset = static_cast<uint32_t>(stream.tellp());\n    }\n\n    mStringBuf.insert(mStringBuf.end(), filename.begin(), filename.end());\n    mStringBuf.push_back('\\0');\n    mFiles.push_back(newFile);\n\n    mHasChanged = true;\n\n    mLookup[filename.c_str()] = mFiles.size() - 1;\n\n    stream.seekp(0, std::ios::end);\n    file.seekg(0, std::ios::beg);\n    stream << file.rdbuf();\n}\n"
  },
  {
    "path": "components/bsa/bsa_file.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (bsa_file.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef BSA_BSA_FILE_H\n#define BSA_BSA_FILE_H\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <map>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/files/constrainedfilestream.hpp>\n\n\nnamespace Bsa\n{\n\n/**\n   This class is used to read \"Bethesda Archive Files\", or BSAs.\n */\nclass BSAFile\n{\npublic:\n\n    #pragma pack(push)\n    #pragma pack(1)\n    struct Hash\n    {\n        uint32_t low, high;\n    };\n    #pragma pack(pop)\n\n    /// Represents one file entry in the archive\n    struct FileStruct\n    {\n        void setNameInfos(size_t index,\n            std::vector<char>* stringBuf\n        ) {\n            namesOffset = static_cast<uint32_t>(index);\n            namesBuffer = stringBuf;\n        }\n\n        // File size and offset in file. We store the offset from the\n        // beginning of the file, not the offset into the data buffer\n        // (which is what is stored in the archive.)\n        uint32_t fileSize, offset;\n        Hash hash;\n\n        // Zero-terminated file name\n        const char* name() const { return &(*namesBuffer)[namesOffset]; };\n\n        uint32_t namesOffset = 0;\n        std::vector<char>* namesBuffer = nullptr;\n    };\n    typedef std::vector<FileStruct> FileList;\n\nprotected:\n    bool mHasChanged = false;\n\n    /// Table of files in this archive\n    FileList mFiles;\n\n    /// Filename string buffer\n    std::vector<char> mStringBuf;\n\n    /// True when an archive has been loaded\n    bool mIsLoaded;\n\n    /// Used for error messages\n    std::string mFilename;\n\n    /// Case insensitive string comparison\n    struct iltstr\n    {\n        bool operator()(const std::string& s1, const std::string& s2) const\n        { return Misc::StringUtils::ciLess(s1, s2); }\n    };\n\n    /** A map used for fast file name lookup. The value is the index into\n        the files[] vector above. The iltstr ensures that file name\n        checks are case insensitive.\n    */\n    typedef std::map<std::string, size_t, iltstr> Lookup;\n    Lookup mLookup;\n\n    /// Error handling\n    void fail(const std::string &msg);\n\n    /// Read header information from the input source\n    virtual void readHeader();\n    virtual void writeHeader();\n\n    /// Get the index of a given file name, or -1 if not found\n    /// @note Thread safe.\n    int getIndex(const char *str) const;\n\npublic:\n    /* -----------------------------------\n     * BSA management methods\n     * -----------------------------------\n     */\n\n    BSAFile()\n      : mIsLoaded(false)\n    { }\n\n    virtual ~BSAFile()\n    {\n        close();\n    }\n\n    /// Open an archive file.\n    void open(const std::string &file);\n\n    void close();\n\n    /* -----------------------------------\n     * Archive file routines\n     * -----------------------------------\n     */\n\n    /// Check if a file exists\n    virtual bool exists(const char *file) const\n    { return getIndex(file) != -1; }\n\n    /** Open a file contained in the archive. Throws an exception if the\n        file doesn't exist.\n     * @note Thread safe.\n    */\n    virtual Files::IStreamPtr getFile(const char *file);\n\n    /** Open a file contained in the archive.\n     * @note Thread safe.\n    */\n    virtual Files::IStreamPtr getFile(const FileStruct* file);\n\n    virtual void addFile(const std::string& filename, std::istream& file);\n\n    /// Get a list of all files\n    /// @note Thread safe.\n    const FileList &getList() const\n    { return mFiles; }\n\n    const std::string& getFilename() const\n    {\n        return mFilename;\n    }\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/bsa/compressedbsafile.cpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: http://openmw.sourceforge.net/\n\n  This file (compressedbsafile.cpp) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  http://www.gnu.org/licenses/ .\n\n  Compressed BSA stuff added by cc9cii 2018\n\n */\n#include \"compressedbsafile.hpp\"\n\n#include <stdexcept>\n#include <cassert>\n\n#include <lz4frame.h>\n\n#include <boost/scoped_array.hpp>\n#include <boost/filesystem/path.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <boost/iostreams/filtering_streambuf.hpp>\n#include <boost/iostreams/copy.hpp>\n\n#if defined(_MSC_VER)\n    #pragma warning (push)\n    #pragma warning (disable : 4706)\n    #include <boost/iostreams/filter/zlib.hpp>\n    #pragma warning (pop)\n#else\n    #include <boost/iostreams/filter/zlib.hpp>\n#endif\n\n#include <boost/iostreams/device/array.hpp>\n#include <components/bsa/memorystream.hpp>\n\nnamespace Bsa\n{\n//special marker for invalid records,\n//equal to max uint32_t value\nconst uint32_t CompressedBSAFile::sInvalidOffset = std::numeric_limits<uint32_t>::max();\n\n//bit marking compression on file size\nconst uint32_t CompressedBSAFile::sCompressedFlag = 1u << 30u;\n\n\nCompressedBSAFile::FileRecord::FileRecord() : size(0), offset(sInvalidOffset)\n{ }\n\nbool CompressedBSAFile::FileRecord::isValid() const\n{\n    return offset != sInvalidOffset;\n}\n\nbool CompressedBSAFile::FileRecord::isCompressed(bool bsaCompressedByDefault) const\n{\n    bool recordCompressionFlagEnabled = ((size & sCompressedFlag) == sCompressedFlag);\n\n    //record is compressed when:\n    //- bsaCompressedByDefault flag is set and 30th bit is NOT set, or\n    //- bsaCompressedByDefault flag is NOT set and 30th bit is set\n    //record is NOT compressed when:\n    //- bsaCompressedByDefault flag is NOT set and 30th bit is NOT set, or\n    //- bsaCompressedByDefault flag is set and 30th bit is set\n    return (bsaCompressedByDefault != recordCompressionFlagEnabled);\n}\n\nstd::uint32_t CompressedBSAFile::FileRecord::getSizeWithoutCompressionFlag() const {\n    return size & (~sCompressedFlag);\n}\n\nvoid CompressedBSAFile::getBZString(std::string& str, std::istream& filestream)\n{\n    char size = 0;\n    filestream.read(&size, 1);\n\n    boost::scoped_array<char> buf(new char[size]);\n    filestream.read(buf.get(), size);\n\n    if (buf[size - 1] != 0)\n    {\n        str.assign(buf.get(), size);\n        if (str.size() != ((size_t)size)) {\n            fail(\"getBZString string size mismatch\");\n        }\n    }\n    else\n    {\n        str.assign(buf.get(), size - 1); // don't copy null terminator\n        if (str.size() != ((size_t)size - 1)) {\n            fail(\"getBZString string size mismatch (null terminator)\");\n        }\n    }\n}\n\nCompressedBSAFile::CompressedBSAFile()\n    : mCompressedByDefault(false), mEmbeddedFileNames(false)\n{ }\n\nCompressedBSAFile::~CompressedBSAFile() = default;\n\n/// Read header information from the input source\nvoid CompressedBSAFile::readHeader()\n{\n    assert(!mIsLoaded);\n\n    namespace bfs = boost::filesystem;\n    bfs::ifstream input(bfs::path(mFilename), std::ios_base::binary);\n\n    // Total archive size\n    std::streamoff fsize = 0;\n    if(input.seekg(0, std::ios_base::end))\n    {\n        fsize = input.tellg();\n        input.seekg(0);\n    }\n\n    if(fsize < 36) // header is 36 bytes\n        fail(\"File too small to be a valid BSA archive\");\n\n    // Get essential header numbers\n    //size_t dirsize, filenum;\n    std::uint32_t archiveFlags, folderCount, totalFileNameLength;\n    {\n        // First 36 bytes\n        std::uint32_t header[9];\n\n        input.read(reinterpret_cast<char*>(header), 36);\n\n        if (header[0] != 0x00415342) /*\"BSA\\x00\"*/\n            fail(\"Unrecognized compressed BSA format\");\n        mVersion = header[1];\n        if (mVersion != 0x67 /*TES4*/ && mVersion != 0x68 /*FO3, FNV, TES5*/ && mVersion != 0x69 /*SSE*/)\n            fail(\"Unrecognized compressed BSA version\");\n\n        // header[2] is offset, should be 36 = 0x24 which is the size of the header\n\n        // Oblivion - Meshes.bsa\n        //\n        // 0111 1000 0111 = 0x0787\n        //  ^^^ ^     ^^^\n        //  ||| |     ||+-- has names for dirs  (mandatory?)\n        //  ||| |     |+--- has names for files (mandatory?)\n        //  ||| |     +---- files are compressed by default\n        //  ||| |\n        //  ||| +---------- unknown (TES5: retain strings during startup)\n        //  ||+------------ unknown (TES5: embedded file names)\n        //  |+------------- unknown\n        //  +-------------- unknown\n        //\n        archiveFlags          = header[3];\n        folderCount           = header[4];\n        // header[5] - fileCount\n        // totalFolderNameLength = header[6];\n        totalFileNameLength   = header[7];\n        // header[8]; // fileFlags : an opportunity to optimize here\n\n        mCompressedByDefault = (archiveFlags & 0x4) != 0;\n        if (mVersion == 0x68 || mVersion == 0x69) /*FO3, FNV, TES5, SSE*/\n            mEmbeddedFileNames = (archiveFlags & 0x100) != 0;\n    }\n\n    // folder records\n    std::uint64_t hash;\n    FolderRecord fr;\n    for (std::uint32_t i = 0; i < folderCount; ++i)\n    {\n        input.read(reinterpret_cast<char*>(&hash), 8);\n        input.read(reinterpret_cast<char*>(&fr.count), 4); // not sure purpose of count\n        if (mVersion == 0x69) // SSE\n        {\n            std::uint32_t unknown;\n            input.read(reinterpret_cast<char*>(&unknown), 4);\n            input.read(reinterpret_cast<char*>(&fr.offset), 8);\n        }\n        else\n            input.read(reinterpret_cast<char*>(&fr.offset), 4); // not sure purpose of offset\n\n        auto lb = mFolders.lower_bound(hash);\n        if (lb != mFolders.end() && !(mFolders.key_comp()(hash, lb->first)))\n            fail(\"Archive found duplicate folder name hash\");\n        else\n            mFolders.insert(lb, std::pair<std::uint64_t, FolderRecord>(hash, fr));\n    }\n\n    // file record blocks\n    std::uint64_t fileHash;\n    FileRecord file;\n\n    std::string folder;\n    std::uint64_t folderHash;\n    if ((archiveFlags & 0x1) == 0)\n        folderCount = 1; // TODO: not tested - unit test necessary\n\n    mFiles.clear();\n    std::vector<std::string> fullPaths;\n    \n    for (std::uint32_t i = 0; i < folderCount; ++i)\n    {\n        if ((archiveFlags & 0x1) != 0)\n            getBZString(folder, input);\n\n        folderHash = generateHash(folder, std::string());\n\n        auto iter = mFolders.find(folderHash);\n        if (iter == mFolders.end())\n            fail(\"Archive folder name hash not found\");\n\n        for (std::uint32_t j = 0; j < iter->second.count; ++j)\n        {\n            input.read(reinterpret_cast<char*>(&fileHash), 8);\n            input.read(reinterpret_cast<char*>(&file.size), 4);\n            input.read(reinterpret_cast<char*>(&file.offset), 4);\n\n            auto lb = iter->second.files.lower_bound(fileHash);\n            if (lb != iter->second.files.end() && !(iter->second.files.key_comp()(fileHash, lb->first)))\n                fail(\"Archive found duplicate file name hash\");\n\n            iter->second.files.insert(lb, std::pair<std::uint64_t, FileRecord>(fileHash, file));\n\n            FileStruct fileStruct{};\n            fileStruct.fileSize = file.getSizeWithoutCompressionFlag();\n            fileStruct.offset = file.offset;\n            mFiles.push_back(fileStruct);\n\n            fullPaths.push_back(folder);\n        }\n    }\n\n    // file record blocks\n    if ((archiveFlags & 0x2) != 0)\n    {\n        mStringBuf.resize(totalFileNameLength);\n        input.read(&mStringBuf[0], mStringBuf.size()); // TODO: maybe useful in building a lookup map?\n    }\n\n    size_t mStringBuffOffset = 0;\n    size_t totalStringsSize = 0;\n    for (std::uint32_t fileIndex = 0; fileIndex < mFiles.size(); ++fileIndex) {\n\n        if (mStringBuffOffset >= totalFileNameLength) {\n            fail(\"Corrupted names record in BSA file\");\n        }\n\n        //The vector guarantees that its elements occupy contiguous memory\n        mFiles[fileIndex].setNameInfos(mStringBuffOffset, &mStringBuf);\n\n        fullPaths.at(fileIndex) += \"\\\\\" + std::string(mStringBuf.data() + mStringBuffOffset);\n\n        while (mStringBuffOffset < totalFileNameLength) {\n            if (mStringBuf[mStringBuffOffset] != '\\0') {\n                mStringBuffOffset++;\n            }\n            else {\n                mStringBuffOffset++;\n                break;\n            }\n        }\n        //we want to keep one more 0 character at the end of each string\n        totalStringsSize += fullPaths.at(fileIndex).length() + 1u;\n    }\n    mStringBuf.resize(totalStringsSize);\n\n    mStringBuffOffset = 0;\n    for (std::uint32_t fileIndex = 0u; fileIndex < mFiles.size(); fileIndex++) {\n        size_t stringLength = fullPaths.at(fileIndex).length();\n\n        std::copy(fullPaths.at(fileIndex).c_str(),\n            //plus 1 because we also want to copy 0 at the end of the string\n            fullPaths.at(fileIndex).c_str() + stringLength + 1u,\n            mStringBuf.data() + mStringBuffOffset);\n\n        mFiles[fileIndex].setNameInfos(mStringBuffOffset, &mStringBuf);\n\n        mLookup[reinterpret_cast<char*>(mStringBuf.data() + mStringBuffOffset)] = fileIndex;\n        mStringBuffOffset += stringLength + 1u;\n    }\n\n    if (mStringBuffOffset != mStringBuf.size()) {\n        fail(\"Could not resolve names of files in BSA file\");\n    }\n\n    convertCompressedSizesToUncompressed();\n    mIsLoaded = true;\n}\n\nCompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string& str) const\n{\n    // Force-convert the path into something both Windows and UNIX can handle first\n    // to make sure Boost doesn't think the entire path is the filename on Linux\n    // and subsequently purge it to determine the file folder.\n    std::string path = str;\n    std::replace(path.begin(), path.end(), '\\\\', '/');\n\n    boost::filesystem::path p(path);\n    std::string stem = p.stem().string();\n    std::string ext = p.extension().string();\n    p.remove_filename();\n\n    std::string folder = p.string();\n    std::uint64_t folderHash = generateHash(folder, std::string());\n\n    auto it = mFolders.find(folderHash);\n    if (it == mFolders.end())\n        return FileRecord(); // folder not found, return default which has offset of sInvalidOffset\n\n    std::uint64_t fileHash = generateHash(stem, ext);\n    auto iter = it->second.files.find(fileHash);\n    if (iter == it->second.files.end())\n        return FileRecord(); // file not found, return default which has offset of sInvalidOffset\n\n    return iter->second;\n}\n\nFiles::IStreamPtr CompressedBSAFile::getFile(const FileStruct* file) \n{\n    FileRecord fileRec = getFileRecord(file->name());\n    if (!fileRec.isValid()) {\n        fail(\"File not found: \" + std::string(file->name()));\n    }\n    return getFile(fileRec);\n}\n\nvoid CompressedBSAFile::addFile(const std::string& filename, std::istream& file)\n{\n    assert(false); //not implemented yet\n    fail(\"Add file is not implemented for compressed BSA: \" + filename);\n}\n\nFiles::IStreamPtr CompressedBSAFile::getFile(const char* file)\n{\n    FileRecord fileRec = getFileRecord(file);\n    if (!fileRec.isValid()) {\n        fail(\"File not found: \" + std::string(file));\n    }\n    return getFile(fileRec);\n}\n\nFiles::IStreamPtr CompressedBSAFile::getFile(const FileRecord& fileRecord)\n{\n    size_t size = fileRecord.getSizeWithoutCompressionFlag();\n    size_t uncompressedSize = size;\n    bool compressed = fileRecord.isCompressed(mCompressedByDefault);\n    Files::IStreamPtr streamPtr = Files::openConstrainedFileStream(mFilename.c_str(), fileRecord.offset, size);\n    std::istream* fileStream = streamPtr.get();\n    if (mEmbeddedFileNames)\n    {\n        // Skip over the embedded file name\n        char length = 0;\n        fileStream->read(&length, 1);\n        fileStream->ignore(length);\n        size -= length + sizeof(char);\n    }\n    if (compressed)\n    {\n        fileStream->read(reinterpret_cast<char*>(&uncompressedSize), sizeof(uint32_t));\n        size -= sizeof(uint32_t);\n    }\n    std::shared_ptr<Bsa::MemoryInputStream> memoryStreamPtr = std::make_shared<MemoryInputStream>(uncompressedSize);\n\n    if (compressed)\n    {\n        if (mVersion != 0x69) // Non-SSE: zlib\n        {\n            boost::iostreams::filtering_streambuf<boost::iostreams::input> inputStreamBuf;\n            inputStreamBuf.push(boost::iostreams::zlib_decompressor());\n            inputStreamBuf.push(*fileStream);\n\n            boost::iostreams::basic_array_sink<char> sr(memoryStreamPtr->getRawData(), uncompressedSize);\n            boost::iostreams::copy(inputStreamBuf, sr);\n        }\n        else // SSE: lz4\n        {\n            boost::scoped_array<char> buffer(new char[size]);\n            fileStream->read(buffer.get(), size);\n            LZ4F_decompressionContext_t context = nullptr;\n            LZ4F_createDecompressionContext(&context, LZ4F_VERSION);\n            LZ4F_decompressOptions_t options = {};\n            LZ4F_errorCode_t errorCode = LZ4F_decompress(context, memoryStreamPtr->getRawData(), &uncompressedSize, buffer.get(), &size, &options);\n            if (LZ4F_isError(errorCode))\n                fail(\"LZ4 decompression error (file \" + mFilename + \"): \" + LZ4F_getErrorName(errorCode));\n            errorCode = LZ4F_freeDecompressionContext(context);\n            if (LZ4F_isError(errorCode))\n                fail(\"LZ4 decompression error (file \" + mFilename + \"): \" + LZ4F_getErrorName(errorCode));\n        }\n    }\n    else\n    {\n        fileStream->read(memoryStreamPtr->getRawData(), size);\n    }\n\n    return std::shared_ptr<std::istream>(memoryStreamPtr, (std::istream*)memoryStreamPtr.get());\n}\n\nBsaVersion CompressedBSAFile::detectVersion(std::string filePath)\n{\n    namespace bfs = boost::filesystem;\n    bfs::ifstream input(bfs::path(filePath), std::ios_base::binary);\n\n    // Total archive size\n    std::streamoff fsize = 0;\n    if (input.seekg(0, std::ios_base::end))\n    {\n        fsize = input.tellg();\n        input.seekg(0);\n    }\n\n    if (fsize < 12) {\n        return BSAVER_UNKNOWN;\n    }\n\n    // Get essential header numbers\n\n    // First 12 bytes\n    uint32_t head[3];\n\n    input.read(reinterpret_cast<char*>(head), 12);\n\n    if (head[0] == static_cast<uint32_t>(BSAVER_UNCOMPRESSED)) {\n        return BSAVER_UNCOMPRESSED;\n    }\n\n    if (head[0] == static_cast<uint32_t>(BSAVER_COMPRESSED)) {\n        return BSAVER_COMPRESSED;\n    }\n\n    return BSAVER_UNKNOWN;\n}\n\n//mFiles used by OpenMW expects uncompressed sizes\nvoid CompressedBSAFile::convertCompressedSizesToUncompressed()\n{\n    for (auto & mFile : mFiles)\n    {\n        const FileRecord& fileRecord = getFileRecord(mFile.name());\n        if (!fileRecord.isValid())\n        {\n            fail(\"Could not find file \" + std::string(mFile.name()) + \" in BSA\");\n        }\n\n        if (!fileRecord.isCompressed(mCompressedByDefault))\n        {\n            //no need to fix fileSize in mFiles - uncompressed size already set\n            continue;\n        }\n\n        Files::IStreamPtr dataBegin = Files::openConstrainedFileStream(mFilename.c_str(), fileRecord.offset, fileRecord.getSizeWithoutCompressionFlag());\n\n        if (mEmbeddedFileNames)\n        {\n            std::string embeddedFileName;\n            getBZString(embeddedFileName, *(dataBegin.get()));\n        }\n\n        dataBegin->read(reinterpret_cast<char*>(&(mFile.fileSize)), sizeof(mFile.fileSize));\n    }\n}\n\nstd::uint64_t CompressedBSAFile::generateHash(std::string stem, std::string extension)\n{\n    size_t len = stem.length();\n    if (len == 0)\n        return 0;\n    std::replace(stem.begin(), stem.end(), '/', '\\\\');\n    Misc::StringUtils::lowerCaseInPlace(stem);\n    uint64_t result = stem[len-1] | (len >= 3 ? (stem[len-2] << 8) : 0) | (len << 16) | (stem[0] << 24);\n    if (len >= 4)\n    {\n        uint32_t hash = 0;\n        for (size_t i = 1; i <= len-3; ++i)\n            hash = hash * 0x1003f + stem[i];\n        result += static_cast<uint64_t>(hash) << 32;\n    }\n    if (extension.empty())\n        return result;\n    Misc::StringUtils::lowerCaseInPlace(extension);\n    if (extension == \".kf\")       result |= 0x80;\n    else if (extension == \".nif\") result |= 0x8000;\n    else if (extension == \".dds\") result |= 0x8080;\n    else if (extension == \".wav\") result |= 0x80000000;\n    uint32_t hash = 0;\n    for (const char &c : extension)\n        hash = hash * 0x1003f + c;\n    result += static_cast<uint64_t>(hash) << 32;\n    return result;\n}\n\n} //namespace Bsa\n"
  },
  {
    "path": "components/bsa/compressedbsafile.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: http://openmw.sourceforge.net/\n\n  This file (compressedbsafile.hpp) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  http://www.gnu.org/licenses/ .\n\n  Compressed BSA stuff added by cc9cii 2018\n\n */\n\n#ifndef BSA_COMPRESSED_BSA_FILE_H\n#define BSA_COMPRESSED_BSA_FILE_H\n\n#include <components/bsa/bsa_file.hpp>\n\nnamespace Bsa\n{\n    enum BsaVersion\n    {\n        BSAVER_UNKNOWN = 0x0,\n        BSAVER_UNCOMPRESSED = 0x100,\n        BSAVER_COMPRESSED = 0x415342 //B, S, A\n    };\n\n    class CompressedBSAFile : public BSAFile\n    {\n    private:\n        //special marker for invalid records,\n        //equal to max uint32_t value\n        static const uint32_t sInvalidOffset;\n\n        //bit marking compression on file size\n        static const uint32_t sCompressedFlag;\n\n        struct FileRecord\n        {\n            std::uint32_t size;\n            std::uint32_t offset;\n\n            FileRecord();\n            bool isCompressed(bool bsaCompressedByDefault) const;\n            bool isValid() const;\n            std::uint32_t getSizeWithoutCompressionFlag() const;\n        };\n        \n        //if files in BSA  without 30th bit enabled are compressed\n        bool mCompressedByDefault;\n\n        //if each file record begins with BZ string with file name\n        bool mEmbeddedFileNames;\n\n        std::uint32_t mVersion{0u};\n\n        struct FolderRecord\n        {\n            std::uint32_t count;\n            std::uint64_t offset;\n            std::map<std::uint64_t, FileRecord> files;\n        };\n        std::map<std::uint64_t, FolderRecord> mFolders;\n\n        FileRecord getFileRecord(const std::string& str) const;\n        \n        void getBZString(std::string& str, std::istream& filestream);\n        //mFiles used by OpenMW will contain uncompressed file sizes\n        void convertCompressedSizesToUncompressed();\n        /// \\brief Normalizes given filename or folder and generates format-compatible hash. See https://en.uesp.net/wiki/Tes4Mod:Hash_Calculation.\n        static std::uint64_t generateHash(std::string stem, std::string extension) ;\n        Files::IStreamPtr getFile(const FileRecord& fileRecord);\n    public:\n        CompressedBSAFile();\n        virtual ~CompressedBSAFile();\n\n        //checks version of BSA from file header\n        static BsaVersion detectVersion(std::string filePath);\n\n        /// Read header information from the input source\n        void readHeader() override;\n       \n        Files::IStreamPtr getFile(const char* filePath) override;\n        Files::IStreamPtr getFile(const FileStruct* fileStruct) override;\n        void addFile(const std::string& filename, std::istream& file) override;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/bsa/memorystream.cpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: http://openmw.sourceforge.net/\n\n  This file (memorystream.cpp) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  http://www.gnu.org/licenses/ .\n\n  Compressed BSA upgrade added by Azdul 2019\n\n */\n#include \"memorystream.hpp\"\n\n\nnamespace Bsa\n{\nMemoryInputStreamBuf::MemoryInputStreamBuf(size_t bufferSize) : mBufferPtr(bufferSize)\n{\n    this->setg(mBufferPtr.data(), mBufferPtr.data(), mBufferPtr.data() + bufferSize);\n}\n\nchar* MemoryInputStreamBuf::getRawData() {\n    return mBufferPtr.data();\n}\n\nMemoryInputStream::MemoryInputStream(size_t bufferSize) : \n                   MemoryInputStreamBuf(bufferSize),\n    std::istream(static_cast<std::streambuf*>(this)) {\n\n}\n\nchar* MemoryInputStream::getRawData() {\n    return MemoryInputStreamBuf::getRawData();\n}\n}\n"
  },
  {
    "path": "components/bsa/memorystream.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: http://openmw.sourceforge.net/\n\n  This file (memorystream.hpp) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  http://www.gnu.org/licenses/ .\n\n  Compressed BSA upgrade added by Azdul 2019\n\n */\n\n#ifndef BSA_MEMORY_STREAM_H\n#define BSA_MEMORY_STREAM_H\n\n#include <vector>\n#include <iostream>\n\nnamespace Bsa\n{\n/**\nClass used internally by MemoryInputStream.\n*/\nclass MemoryInputStreamBuf : public std::streambuf {\n\npublic:\n    explicit MemoryInputStreamBuf(size_t bufferSize);\n    virtual char* getRawData();\nprivate:\n    //correct call to delete [] on C++ 11\n    std::vector<char> mBufferPtr;\n};\n\n/**\n    Class replaces Ogre memory streams without introducing any new external dependencies\n    beyond standard library.\n\n    Allows to pass memory buffer as Files::IStreamPtr.\n\n    Memory buffer is freed once the class instance is destroyed.\n */\nclass MemoryInputStream : virtual MemoryInputStreamBuf, std::istream {\npublic:\n    explicit MemoryInputStream(size_t bufferSize);\n    char* getRawData() override;\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/bullethelpers/aabb.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_BULLETHELPERS_AABB_H\n#define OPENMW_COMPONENTS_BULLETHELPERS_AABB_H\n\n#include <LinearMath/btVector3.h>\n#include <LinearMath/btTransform.h>\n#include <BulletCollision/CollisionShapes/btCollisionShape.h>\n#include <BulletCollision/Gimpact/btBoxCollision.h>\n\ninline bool operator==(const btAABB& lhs, const btAABB& rhs)\n{\n    return lhs.m_min == rhs.m_min && lhs.m_max == rhs.m_max;\n}\n\ninline bool operator!=(const btAABB& lhs, const btAABB& rhs)\n{\n    return !(lhs == rhs);\n}\n\nnamespace BulletHelpers\n{\n    inline btAABB getAabb(const btCollisionShape& shape, const btTransform& transform)\n    {\n        btAABB result;\n        shape.getAabb(transform, result.m_min, result.m_max);\n        return result;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/bullethelpers/operators.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_BULLETHELPERS_OPERATORS_H\n#define OPENMW_COMPONENTS_BULLETHELPERS_OPERATORS_H\n\n#include <iomanip>\n#include <limits>\n#include <ostream>\n#include <tuple>\n\n#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <LinearMath/btTransform.h>\n\ninline std::ostream& operator <<(std::ostream& stream, const btVector3& value)\n{\n    return stream << \"btVector3(\" << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.x()\n                  << \", \" << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.y()\n                  << \", \" << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.z()\n                  << ')';\n}\n\ninline std::ostream& operator <<(std::ostream& stream, BroadphaseNativeTypes value)\n{\n    switch (value)\n    {\n#ifndef SHAPE_NAME\n#define SHAPE_NAME(name) case name: return stream << #name;\n        SHAPE_NAME(BOX_SHAPE_PROXYTYPE)\n        SHAPE_NAME(TRIANGLE_SHAPE_PROXYTYPE)\n        SHAPE_NAME(TETRAHEDRAL_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CONVEX_HULL_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CUSTOM_POLYHEDRAL_SHAPE_TYPE)\n        SHAPE_NAME(IMPLICIT_CONVEX_SHAPES_START_HERE)\n        SHAPE_NAME(SPHERE_SHAPE_PROXYTYPE)\n        SHAPE_NAME(MULTI_SPHERE_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CAPSULE_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CONE_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CONVEX_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CYLINDER_SHAPE_PROXYTYPE)\n        SHAPE_NAME(UNIFORM_SCALING_SHAPE_PROXYTYPE)\n        SHAPE_NAME(MINKOWSKI_SUM_SHAPE_PROXYTYPE)\n        SHAPE_NAME(MINKOWSKI_DIFFERENCE_SHAPE_PROXYTYPE)\n        SHAPE_NAME(BOX_2D_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CONVEX_2D_SHAPE_PROXYTYPE)\n        SHAPE_NAME(CUSTOM_CONVEX_SHAPE_TYPE)\n        SHAPE_NAME(CONCAVE_SHAPES_START_HERE)\n        SHAPE_NAME(TRIANGLE_MESH_SHAPE_PROXYTYPE)\n        SHAPE_NAME(SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE)\n        SHAPE_NAME(FAST_CONCAVE_MESH_PROXYTYPE)\n        SHAPE_NAME(TERRAIN_SHAPE_PROXYTYPE)\n        SHAPE_NAME(GIMPACT_SHAPE_PROXYTYPE)\n        SHAPE_NAME(MULTIMATERIAL_TRIANGLE_MESH_PROXYTYPE)\n        SHAPE_NAME(EMPTY_SHAPE_PROXYTYPE)\n        SHAPE_NAME(STATIC_PLANE_PROXYTYPE)\n        SHAPE_NAME(CUSTOM_CONCAVE_SHAPE_TYPE)\n        SHAPE_NAME(CONCAVE_SHAPES_END_HERE)\n        SHAPE_NAME(COMPOUND_SHAPE_PROXYTYPE)\n        SHAPE_NAME(SOFTBODY_SHAPE_PROXYTYPE)\n        SHAPE_NAME(HFFLUID_SHAPE_PROXYTYPE)\n        SHAPE_NAME(HFFLUID_BUOYANT_CONVEX_SHAPE_PROXYTYPE)\n        SHAPE_NAME(INVALID_SHAPE_PROXYTYPE)\n        SHAPE_NAME(MAX_BROADPHASE_COLLISION_TYPES)\n#undef SHAPE_NAME\n#endif\n        default:\n            return stream << \"undefined(\" << static_cast<int>(value) << \")\";\n    }\n}\n\ninline bool operator <(const btVector3& lhs, const btVector3& rhs)\n{\n    return std::tie(lhs.x(), lhs.y(), lhs.z()) < std::tie(rhs.x(), rhs.y(), rhs.z());\n}\n\ninline bool operator <(const btMatrix3x3& lhs, const btMatrix3x3& rhs)\n{\n    return std::tie(lhs[0], lhs[1], lhs[2]) < std::tie(rhs[0], rhs[1], rhs[2]);\n}\n\ninline bool operator <(const btTransform& lhs, const btTransform& rhs)\n{\n    return std::tie(lhs.getBasis(), lhs.getOrigin()) < std::tie(rhs.getBasis(), rhs.getOrigin());\n}\n\n#endif\n"
  },
  {
    "path": "components/bullethelpers/processtrianglecallback.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_BULLETHELPERS_PROCESSTRIANGLECALLBACK_H\n#define OPENMW_COMPONENTS_BULLETHELPERS_PROCESSTRIANGLECALLBACK_H\n\n#include <BulletCollision/CollisionShapes/btTriangleCallback.h>\n\n#include <type_traits>\n\nnamespace BulletHelpers\n{\n    template <class Impl>\n    class ProcessTriangleCallback : public btTriangleCallback\n    {\n    public:\n        explicit ProcessTriangleCallback(Impl impl)\n            : mImpl(std::move(impl))\n        {}\n\n        void processTriangle(btVector3* triangle, int partId, int triangleIndex) override\n        {\n            return mImpl(triangle, partId, triangleIndex);\n        }\n\n    private:\n        Impl mImpl;\n    };\n\n    template <class Impl>\n    ProcessTriangleCallback<typename std::decay<Impl>::type> makeProcessTriangleCallback(Impl&& impl)\n    {\n        return ProcessTriangleCallback<typename std::decay<Impl>::type>(std::forward<Impl>(impl));\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/bullethelpers/transformboundingbox.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_BULLETHELPERS_TRANSFORMBOUNDINGBOX_H\n#define OPENMW_COMPONENTS_BULLETHELPERS_TRANSFORMBOUNDINGBOX_H\n\n#include <LinearMath/btVector3.h>\n#include <LinearMath/btTransform.h>\n\n#include <algorithm>\n\nnamespace BulletHelpers\n{\n    inline btVector3 min(const btVector3& a, const btVector3& b)\n    {\n        return btVector3(std::min(a.x(), b.x()), std::min(a.y(), b.y()), std::min(a.z(), b.z()));\n    }\n\n    inline btVector3 max(const btVector3& a, const btVector3& b)\n    {\n        return btVector3(std::max(a.x(), b.x()), std::max(a.y(), b.y()), std::max(a.z(), b.z()));\n    }\n\n    // http://dev.theomader.com/transform-bounding-boxes/\n    inline void transformBoundingBox(const btTransform& transform, btVector3& aabbMin, btVector3& aabbMax)\n    {\n        const btVector3 xa(transform.getBasis().getColumn(0) * aabbMin.x());\n        const btVector3 xb(transform.getBasis().getColumn(0) * aabbMax.x());\n\n        const btVector3 ya(transform.getBasis().getColumn(1) * aabbMin.y());\n        const btVector3 yb(transform.getBasis().getColumn(1) * aabbMax.y());\n\n        const btVector3 za(transform.getBasis().getColumn(2) * aabbMin.z());\n        const btVector3 zb(transform.getBasis().getColumn(2) * aabbMax.z());\n\n        aabbMin = min(xa, xb) + min(ya, yb) + min(za, zb) + transform.getOrigin();\n        aabbMax = max(xa, xb) + max(ya, yb) + max(za, zb) + transform.getOrigin();\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/context.hpp",
    "content": "#ifndef COMPILER_CONTEXT_H_INCLUDED\n#define COMPILER_CONTEXT_H_INCLUDED\n\n#include <string>\n\nnamespace Compiler\n{\n    class Extensions;\n\n    class Context\n    {\n            const Extensions *mExtensions;\n\n        public:\n\n            Context() : mExtensions (nullptr) {}\n\n            virtual ~Context() = default;\n\n            virtual bool canDeclareLocals() const = 0;\n            ///< Is the compiler allowed to declare local variables?\n\n            void setExtensions (const Extensions *extensions = nullptr)\n            {\n                mExtensions = extensions;\n            }\n\n            const Extensions *getExtensions() const\n            {\n                return mExtensions;\n            }\n\n            virtual char getGlobalType (const std::string& name) const = 0;\n            ///< 'l: long, 's': short, 'f': float, ' ': does not exist.\n\n            virtual std::pair<char, bool> getMemberType (const std::string& name,\n                const std::string& id) const = 0;\n            ///< Return type of member variable \\a name in script \\a id or in script of reference of\n            /// \\a id\n            /// \\return first: 'l: long, 's': short, 'f': float, ' ': does not exist.\n            /// second: true: script of reference\n\n            virtual bool isId (const std::string& name) const = 0;\n            ///< Does \\a name match an ID, that can be referenced?\n\n            virtual bool isJournalId (const std::string& name) const = 0;\n            ///< Does \\a name match a journal ID?\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/controlparser.cpp",
    "content": "#include \"controlparser.hpp\"\n\n#include <algorithm>\n#include <iterator>\n#include <stdexcept>\n\n#include \"scanner.hpp\"\n#include \"generator.hpp\"\n#include \"errorhandler.hpp\"\n#include \"skipparser.hpp\"\n\nnamespace Compiler\n{\n    bool ControlParser::parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (keyword==Scanner::K_endif || keyword==Scanner::K_elseif ||\n            keyword==Scanner::K_else)\n        {\n            std::pair<Codes, Codes> entry;\n\n            if (mState!=IfElseBodyState)\n                mExprParser.append (entry.first);\n\n            std::copy (mCodeBlock.begin(), mCodeBlock.end(),\n                std::back_inserter (entry.second));\n\n            mIfCode.push_back (entry);\n\n            mCodeBlock.clear();\n\n            if (keyword==Scanner::K_endif)\n            {\n                // store code for if-cascade\n                Codes codes;\n\n                for (auto iter (mIfCode.rbegin());\n                    iter!=mIfCode.rend(); ++iter)\n                {\n                    Codes block;\n\n                    if (iter!=mIfCode.rbegin())\n                        Generator::jump (iter->second, static_cast<int>(codes.size()+1));\n\n                    if (!iter->first.empty())\n                    {\n                        // if or elseif\n                        std::copy (iter->first.begin(), iter->first.end(),\n                            std::back_inserter (block));\n                        Generator::jumpOnZero (block, static_cast<int>(iter->second.size()+1));\n                    }\n\n                    std::copy (iter->second.begin(), iter->second.end(),\n                        std::back_inserter (block));\n\n                   std::swap (codes, block);\n\n                   std::copy (block.begin(), block.end(), std::back_inserter (codes));\n                }\n\n                std::copy (codes.begin(), codes.end(), std::back_inserter (mCode));\n\n                mIfCode.clear();\n                mState = IfEndifState;\n            }\n            else if (keyword==Scanner::K_elseif)\n            {\n                mExprParser.reset();\n                scanner.scan (mExprParser);\n\n                mState = IfElseifEndState;\n            }\n            else if (keyword==Scanner::K_else)\n            {\n                mState = IfElseJunkState; /// \\todo should be IfElseEndState; add an option for that\n            }\n\n            return true;\n        }\n        else if (keyword==Scanner::K_if || keyword==Scanner::K_while)\n        {\n            // nested\n            ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals);\n\n            if (parser.parseKeyword (keyword, loc, scanner))\n                scanner.scan (parser);\n\n            parser.appendCode (mCodeBlock);\n\n            return true;\n        }\n        else\n        {\n            mLineParser.reset();\n            if (mLineParser.parseKeyword (keyword, loc, scanner))\n                scanner.scan (mLineParser);\n\n            return true;\n        }\n    }\n\n    bool ControlParser::parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (keyword==Scanner::K_endwhile)\n        {\n            Codes loop;\n\n            Codes expr;\n            mExprParser.append (expr);\n\n            Generator::jump (loop, -static_cast<int> (mCodeBlock.size()+expr.size()));\n\n            std::copy (expr.begin(), expr.end(), std::back_inserter (mCode));\n\n            Codes skip;\n\n            Generator::jumpOnZero (skip, static_cast<int> (mCodeBlock.size()+loop.size()+1));\n\n            std::copy (skip.begin(), skip.end(), std::back_inserter (mCode));\n\n            std::copy (mCodeBlock.begin(), mCodeBlock.end(), std::back_inserter (mCode));\n\n            Codes loop2;\n\n            Generator::jump (loop2, -static_cast<int> (mCodeBlock.size()+expr.size()+skip.size()));\n\n            if (loop.size()!=loop2.size())\n                throw std::logic_error (\n                    \"Internal compiler error: failed to generate a while loop\");\n\n            std::copy (loop2.begin(), loop2.end(), std::back_inserter (mCode));\n\n            mState = WhileEndwhileState;\n            return true;\n        }\n        else if (keyword==Scanner::K_if || keyword==Scanner::K_while)\n        {\n            // nested\n            ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals);\n\n            if (parser.parseKeyword (keyword, loc, scanner))\n                scanner.scan (parser);\n\n            parser.appendCode (mCodeBlock);\n\n            return true;\n        }\n        else\n        {\n            mLineParser.reset();\n            if (mLineParser.parseKeyword (keyword, loc, scanner))\n                scanner.scan (mLineParser);\n\n            return true;\n        }\n    }\n\n    ControlParser::ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,\n        Literals& literals)\n    : Parser (errorHandler, context), mLocals (locals), mLiterals (literals),\n      mLineParser (errorHandler, context, locals, literals, mCodeBlock),\n      mExprParser (errorHandler, context, locals, literals),\n      mState (StartState)\n    {\n\n    }\n\n    void ControlParser::appendCode (std::vector<Interpreter::Type_Code>& code) const\n    {\n        std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));\n    }\n\n    bool ControlParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState ||\n            mState==WhileBodyState)\n        {\n            scanner.putbackName (name, loc);\n            mLineParser.reset();\n            scanner.scan (mLineParser);\n            return true;\n        }\n        else if (mState==IfElseJunkState)\n        {\n            getErrorHandler().warning (\"Extra text after else\", loc);\n            SkipParser skip (getErrorHandler(), getContext());\n            scanner.scan (skip);\n            mState = IfElseBodyState;\n            return true;\n        }\n\n        return Parser::parseName (name, loc, scanner);\n    }\n\n    bool ControlParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mState==StartState)\n        {\n            if (keyword==Scanner::K_if || keyword==Scanner::K_elseif)\n            {\n                if (keyword==Scanner::K_elseif)\n                    getErrorHandler().warning (\"elseif without matching if\", loc);\n\n                mExprParser.reset();\n                scanner.scan (mExprParser);\n\n                mState = IfEndState;\n                return true;\n            }\n            else if (keyword==Scanner::K_while)\n            {\n                mExprParser.reset();\n                scanner.scan (mExprParser);\n\n                mState = WhileEndState;\n                return true;\n            }\n        }\n        else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState)\n        {\n            if (parseIfBody (keyword, loc, scanner))\n                return true;\n        }\n        else if (mState==WhileBodyState)\n        {\n            if ( parseWhileBody (keyword, loc, scanner))\n                return true;\n        }\n        else if (mState==IfElseJunkState)\n        {\n            getErrorHandler().warning (\"Extra text after else\", loc);\n            SkipParser skip (getErrorHandler(), getContext());\n            scanner.scan (skip);\n            mState = IfElseBodyState;\n            return true;\n        }\n\n        return Parser::parseKeyword (keyword, loc, scanner);\n    }\n\n    bool ControlParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (code==Scanner::S_newline)\n        {\n            switch (mState)\n            {\n                case IfEndState: mState = IfBodyState; return true;\n                case IfElseifEndState: mState = IfElseifBodyState; return true;\n                case IfElseEndState: mState = IfElseBodyState; return true;\n                case IfElseJunkState: mState = IfElseBodyState; return true;\n\n                case WhileEndState: mState = WhileBodyState; return true;\n\n                case IfBodyState:\n                case IfElseifBodyState:\n                case IfElseBodyState:\n                case WhileBodyState:\n\n                    return true; // empty line\n\n                case IfEndifState:\n                case WhileEndwhileState:\n\n                    return false;\n\n                default: ;\n            }\n        }\n        else if (mState==IfElseJunkState)\n        {\n            getErrorHandler().warning (\"Extra text after else\", loc);\n            SkipParser skip (getErrorHandler(), getContext());\n            scanner.scan (skip);\n            mState = IfElseBodyState;\n            return true;\n        }\n\n        return Parser::parseSpecial (code, loc, scanner);\n    }\n\n    void ControlParser::reset()\n    {\n        mCode.clear();\n        mCodeBlock.clear();\n        mIfCode.clear();\n        mState = StartState;\n        Parser::reset();\n    }\n}\n"
  },
  {
    "path": "components/compiler/controlparser.hpp",
    "content": "#ifndef COMPILER_CONTROLPARSER_H_INCLUDED\n#define COMPILER_CONTROLPARSER_H_INCLUDED\n\n#include <vector>\n\n#include <components/interpreter/types.hpp>\n\n#include \"parser.hpp\"\n#include \"exprparser.hpp\"\n#include \"lineparser.hpp\"\n\nnamespace Compiler\n{\n    class Locals;\n    class Literals;\n\n    // Control structure parser\n\n    class ControlParser : public Parser\n    {\n            enum State\n            {\n                StartState,\n                IfEndState, IfBodyState,\n                IfElseifEndState, IfElseifBodyState,\n                IfElseEndState, IfElseBodyState,\n                IfEndifState,\n                WhileEndState, WhileBodyState,\n                WhileEndwhileState,\n                IfElseJunkState\n            };\n\n            typedef std::vector<Interpreter::Type_Code> Codes;\n            typedef std::vector<std::pair<Codes, Codes> > IfCodes;\n\n            Locals& mLocals;\n            Literals& mLiterals;\n            Codes mCode;\n            Codes mCodeBlock;\n            IfCodes mIfCode; // condition, body\n            LineParser mLineParser;\n            ExprParser mExprParser;\n            State mState;\n\n            bool parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner);\n\n            bool parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner);\n\n        public:\n\n            ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,\n                Literals& literals);\n\n            void appendCode (std::vector<Interpreter::Type_Code>& code) const;\n            ///< store generated code in \\a code.\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void reset() override;\n            ///< Reset parser to clean state.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/declarationparser.cpp",
    "content": "#include \"declarationparser.hpp\"\n\n#include <components/misc/stringops.hpp>\n\n#include \"scanner.hpp\"\n#include \"errorhandler.hpp\"\n#include \"skipparser.hpp\"\n#include \"locals.hpp\"\n\nCompiler::DeclarationParser::DeclarationParser (ErrorHandler& errorHandler, const Context& context,\n    Locals& locals)\n: Parser (errorHandler, context), mLocals (locals), mState (State_Begin), mType (0)\n{}\n\nbool Compiler::DeclarationParser::parseName (const std::string& name, const TokenLoc& loc,\n    Scanner& scanner)\n{\n    if (mState==State_Name)\n    {\n        std::string name2 = ::Misc::StringUtils::lowerCase (name);\n\n        char type = mLocals.getType (name2);\n\n        if (type!=' ')\n            getErrorHandler().warning (\"Local variable re-declaration\", loc);\n        else\n            mLocals.declare (mType, name2);\n\n        mState = State_End;\n        return true;\n    }\n    else if (mState==State_End)\n    {\n        getErrorHandler().warning (\"Extra text after local variable declaration\", loc);\n        SkipParser skip (getErrorHandler(), getContext());\n        scanner.scan (skip);\n        return false;\n    }\n\n    return Parser::parseName (name, loc, scanner);\n}\n\nbool Compiler::DeclarationParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n{\n    if (mState==State_Begin)\n    {\n        switch (keyword)\n        {\n            case Scanner::K_short: mType = 's'; break;\n            case Scanner::K_long: mType = 'l'; break;\n            case Scanner::K_float: mType = 'f'; break;\n            default: mType = 0;\n        }\n\n        if (mType)\n        {\n            mState = State_Name;\n            return true;\n        }\n    }\n    else if (mState==State_Name)\n    {\n        // allow keywords to be used as local variable names. MW script compiler, you suck!\n        return parseName (loc.mLiteral, loc, scanner);\n    }\n    else if (mState==State_End)\n    {\n        getErrorHandler().warning (\"Extra text after local variable declaration\", loc);\n        SkipParser skip (getErrorHandler(), getContext());\n        scanner.scan (skip);\n        return false;\n    }\n\n    return Parser::parseKeyword (keyword, loc, scanner);\n}\n\nbool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n{\n    if (mState==State_End)\n    {\n        if (code!=Scanner::S_newline)\n        {\n            getErrorHandler().warning (\"Extra text after local variable declaration\", loc);\n            SkipParser skip (getErrorHandler(), getContext());\n            scanner.scan (skip);\n        }\n        return false;\n    }\n\n    return Parser::parseSpecial (code, loc, scanner);\n}\n\nvoid Compiler::DeclarationParser::reset()\n{\n    mState = State_Begin;\n}\n"
  },
  {
    "path": "components/compiler/declarationparser.hpp",
    "content": "#ifndef COMPILER_DECLARATIONPARSER_H_INCLUDED\n#define COMPILER_DECLARATIONPARSER_H_INCLUDED\n\n#include \"parser.hpp\"\n\nnamespace Compiler\n{\n    class Locals;\n\n    class DeclarationParser : public Parser\n    {\n            enum State\n            {\n                State_Begin, State_Name, State_End\n            };\n\n            Locals& mLocals;\n            State mState;\n            char mType;\n\n        public:\n\n            DeclarationParser (ErrorHandler& errorHandler, const Context& context, Locals& locals);\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void reset() override;\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/discardparser.cpp",
    "content": "#include \"discardparser.hpp\"\n\n#include \"scanner.hpp\"\n\nnamespace Compiler\n{\n    DiscardParser::DiscardParser (ErrorHandler& errorHandler, const Context& context)\n    : Parser (errorHandler, context), mState (StartState)\n    {\n\n    }\n\n    bool DiscardParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mState==StartState || mState==CommaState || mState==MinusState)\n        {\n            if (isEmpty())\n                mTokenLoc = loc;\n\n            start();\n            return false;\n        }\n\n        return Parser::parseInt (value, loc, scanner);\n    }\n\n    bool DiscardParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mState==StartState || mState==CommaState || mState==MinusState)\n        {\n            if (isEmpty())\n                mTokenLoc = loc;\n\n            start();\n            return false;\n        }\n\n        return Parser::parseFloat (value, loc, scanner);\n    }\n\n    bool DiscardParser::parseName (const std::string& name, const TokenLoc& loc,\n        Scanner& scanner)\n    {\n        if (mState==StartState || mState==CommaState)\n        {\n            if (isEmpty())\n                mTokenLoc = loc;\n\n            start();\n            return false;\n        }\n\n        return Parser::parseName (name, loc, scanner);\n    }\n\n    bool DiscardParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (code==Scanner::S_comma && mState==StartState)\n        {\n            if (isEmpty())\n                mTokenLoc = loc;\n\n            start();\n\n            mState = CommaState;\n            return true;\n        }\n\n        if (code==Scanner::S_minus && (mState==StartState || mState==CommaState))\n        {\n            if (isEmpty())\n                mTokenLoc = loc;\n\n            start();\n\n            mState = MinusState;\n            return true;\n        }\n\n        return Parser::parseSpecial (code, loc, scanner);\n    }\n\n    void DiscardParser::reset()\n    {\n        mState = StartState;\n        mTokenLoc = TokenLoc();\n        Parser::reset();\n    }\n\n    const TokenLoc& DiscardParser::getTokenLoc() const\n    {\n        return mTokenLoc;\n    }\n}\n"
  },
  {
    "path": "components/compiler/discardparser.hpp",
    "content": "#ifndef COMPILER_DISCARDPARSER_H_INCLUDED\n#define COMPILER_DISCARDPARSER_H_INCLUDED\n\n#include \"parser.hpp\"\n#include \"tokenloc.hpp\"\n\nnamespace Compiler\n{\n    /// \\brief Parse a single optional numeric value or string and discard it\n    class DiscardParser : public Parser\n    {\n            enum State\n            {\n                StartState, CommaState, MinusState\n            };\n\n            State mState;\n            TokenLoc mTokenLoc;\n\n        public:\n\n            DiscardParser (ErrorHandler& errorHandler, const Context& context);\n\n            bool parseInt (int value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle an int token.\n            /// \\return fetch another token?\n\n            bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a float token.\n            /// \\return fetch another token?\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void reset() override;\n            ///< Reset parser to clean state.\n\n            /// Returns TokenLoc object for value. If no value has been parsed, the TokenLoc\n            /// object will be default initialised.\n            const TokenLoc& getTokenLoc() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/errorhandler.cpp",
    "content": "#include \"errorhandler.hpp\"\n\nnamespace Compiler\n{\n    ErrorHandler::ErrorHandler()\n    : mWarnings (0), mErrors (0), mWarningsMode (1), mDowngradeErrors (false) {}\n\n    ErrorHandler::~ErrorHandler() = default;\n\n    // Was compiling successful?\n\n    bool ErrorHandler::isGood() const\n    {\n        return mErrors==0;\n    }\n\n    // Return number of errors\n\n    int ErrorHandler::countErrors() const\n    {\n        return mErrors;\n    }\n\n    // Return number of warnings\n\n    int ErrorHandler::countWarnings() const\n    {\n        return mWarnings;\n    }\n\n    // Generate a warning message.\n\n    void ErrorHandler::warning (const std::string& message, const TokenLoc& loc)\n    {\n        if (mWarningsMode==1 ||\n            // temporarily change from mode 2 to mode 1 if error downgrading is enabled to\n            // avoid infinite recursion\n            (mWarningsMode==2 && mDowngradeErrors))\n        {\n            ++mWarnings;\n            report (message, loc, WarningMessage);\n        }\n        else if (mWarningsMode==2)\n            error (message, loc);\n    }\n\n    // Generate an error message.\n\n    void ErrorHandler::error (const std::string& message, const TokenLoc& loc)\n    {\n        if (mDowngradeErrors)\n        {\n            warning (message, loc);\n            return;\n        }\n\n        ++mErrors;\n        report (message, loc, ErrorMessage);\n    }\n\n    // Generate an error message for an unexpected EOF.\n\n    void ErrorHandler::endOfFile()\n    {\n        ++mErrors;\n        report (\"unexpected end of file\", ErrorMessage);\n    }\n\n    // Remove all previous error/warning events\n\n    void ErrorHandler::reset()\n    {\n        mErrors = mWarnings = 0;\n    }\n\n    void ErrorHandler::setWarningsMode (int mode)\n    {\n        mWarningsMode = mode;\n    }\n\n    void ErrorHandler::downgradeErrors (bool downgrade)\n    {\n        mDowngradeErrors = downgrade;\n    }\n\n\n    ErrorDowngrade::ErrorDowngrade (ErrorHandler& handler) : mHandler (handler)\n    {\n        mHandler.downgradeErrors (true);\n    }\n\n    ErrorDowngrade::~ErrorDowngrade()\n    {\n        mHandler.downgradeErrors (false);\n    }\n\n}\n"
  },
  {
    "path": "components/compiler/errorhandler.hpp",
    "content": "#ifndef COMPILER_ERRORHANDLER_H_INCLUDED\n#define COMPILER_ERRORHANDLER_H_INCLUDED\n\n#include <string>\n\nnamespace Compiler\n{\n    struct TokenLoc;\n\n    /// \\brief Error handling\n    ///\n    /// This class collects errors and provides an interface for reporting them to the user.\n\n    class ErrorHandler\n    {\n            int mWarnings;\n            int mErrors;\n            int mWarningsMode;\n            bool mDowngradeErrors;\n\n        protected:\n\n            enum Type\n            {\n                WarningMessage, ErrorMessage\n            };\n\n        private:\n\n        // mutators\n\n            virtual void report (const std::string& message, const TokenLoc& loc, Type type) = 0;\n            ///< Report error to the user.\n\n            virtual void report (const std::string& message, Type type) = 0;\n            ///< Report a file related error\n\n        public:\n\n            ErrorHandler();\n            ///< constructor\n\n            virtual ~ErrorHandler();\n            ///< destructor\n\n            bool isGood() const;\n            ///< Was compiling successful?\n\n            int countErrors() const;\n            ///< Return number of errors\n\n            int countWarnings() const;\n            ///< Return number of warnings\n\n            void warning (const std::string& message, const TokenLoc& loc);\n            ///< Generate a warning message.\n\n            void error (const std::string& message, const TokenLoc& loc);\n            ///< Generate an error message.\n\n            void endOfFile();\n            ///< Generate an error message for an unexpected EOF.\n\n            virtual void reset();\n            ///< Remove all previous error/warning events\n\n            void setWarningsMode (int mode);\n            ///< // 0 ignore, 1 rate as warning, 2 rate as error\n\n            /// Treat errors as warnings.\n            void downgradeErrors (bool downgrade);\n    };\n\n    class ErrorDowngrade\n    {\n            ErrorHandler& mHandler;\n\n            /// not implemented\n            ErrorDowngrade (const ErrorDowngrade&);\n\n            /// not implemented\n            ErrorDowngrade& operator= (const ErrorDowngrade&);\n\n        public:\n\n            explicit ErrorDowngrade (ErrorHandler& handler);\n\n            ~ErrorDowngrade();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/exception.hpp",
    "content": "#ifndef COMPILER_EXCEPTION_H_INCLUDED\n#define COMPILER_EXCEPTION_H_INCLUDED\n\n#include <exception>\n\nnamespace Compiler\n{\n    /// \\brief Exception: Error while parsing the source\n\n    class SourceException : public std::exception\n    {\n        public:\n\n            const char *what() const noexcept override { return \"Compile error\";} \n            ///< Return error message\n    };\n\n    /// \\brief Exception: File error\n\n    class FileException : public SourceException\n    {\n        public:\n\n            const char *what() const noexcept override { return \"Can't read file\"; }\n            ///< Return error message\n    };\n\n    /// \\brief Exception: EOF condition encountered\n\n    class EOFException : public SourceException\n    {\n        public:\n\n            const char *what() const noexcept override { return \"End of file\"; }\n            ///< Return error message\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/exprparser.cpp",
    "content": "#include \"exprparser.hpp\"\n\n#include <stdexcept>\n#include <cassert>\n#include <algorithm>\n#include <stack>\n#include <iterator>\n#include <sstream>\n\n#include <components/misc/stringops.hpp>\n\n#include \"generator.hpp\"\n#include \"scanner.hpp\"\n#include \"errorhandler.hpp\"\n#include \"locals.hpp\"\n#include \"stringparser.hpp\"\n#include \"extensions.hpp\"\n#include \"context.hpp\"\n#include \"discardparser.hpp\"\n#include \"junkparser.hpp\"\n\nnamespace Compiler\n{\n    int ExprParser::getPriority (char op)\n    {\n        switch (op)\n        {\n            case '(':\n\n                return 0;\n\n            case 'e': // ==\n            case 'n': // !=\n            case 'l': // <\n            case 'L': // <=\n            case 'g': // <\n            case 'G': // >=\n\n                return 1;\n\n            case '+':\n            case '-':\n\n                return 2;\n\n            case '*':\n            case '/':\n\n                return 3;\n\n            case 'm':\n\n                return 4;\n        }\n\n        return 0;\n    }\n\n    char ExprParser::getOperandType (int Index) const\n    {\n        assert (!mOperands.empty());\n        assert (Index>=0);\n        assert (Index<static_cast<int> (mOperands.size()));\n        return mOperands[mOperands.size()-1-Index];\n    }\n\n    char ExprParser::getOperator() const\n    {\n        assert (!mOperators.empty());\n        return mOperators[mOperators.size()-1];\n    }\n\n    bool ExprParser::isOpen() const\n    {\n        return std::find (mOperators.begin(), mOperators.end(), '(')!=mOperators.end();\n    }\n\n    void ExprParser::popOperator()\n    {\n        assert (!mOperators.empty());\n        mOperators.resize (mOperators.size()-1);\n    }\n\n    void ExprParser::popOperand()\n    {\n        assert (!mOperands.empty());\n        mOperands.resize (mOperands.size()-1);\n    }\n\n    void ExprParser::replaceBinaryOperands()\n    {\n        char t1 = getOperandType (1);\n        char t2 = getOperandType();\n\n        popOperand();\n        popOperand();\n\n        if (t1==t2)\n            mOperands.push_back (t1);\n        else if (t1=='f' || t2=='f')\n            mOperands.push_back ('f');\n        else\n            throw std::logic_error (\"Failed to determine result operand type\");\n    }\n\n    void ExprParser::pop()\n    {\n        char op = getOperator();\n\n        switch (op)\n        {\n            case 'm':\n\n                Generator::negate (mCode, getOperandType());\n                popOperator();\n                break;\n\n            case '+':\n\n                Generator::add (mCode, getOperandType (1), getOperandType());\n                popOperator();\n                replaceBinaryOperands();\n                break;\n\n            case '-':\n\n                Generator::sub (mCode, getOperandType (1), getOperandType());\n                popOperator();\n                replaceBinaryOperands();\n                break;\n\n            case '*':\n\n                Generator::mul (mCode, getOperandType (1), getOperandType());\n                popOperator();\n                replaceBinaryOperands();\n                break;\n\n            case '/':\n\n                Generator::div (mCode, getOperandType (1), getOperandType());\n                popOperator();\n                replaceBinaryOperands();\n                break;\n\n            case 'e':\n            case 'n':\n            case 'l':\n            case 'L':\n            case 'g':\n            case 'G':\n\n                Generator::compare (mCode, op, getOperandType (1), getOperandType());\n                popOperator();\n                popOperand();\n                popOperand();\n                mOperands.push_back ('l');\n                break;\n\n            default:\n\n                throw std::logic_error (\"Unknown operator\");\n        }\n    }\n\n    void ExprParser::pushIntegerLiteral (int value)\n    {\n        mNextOperand = false;\n        mOperands.push_back ('l');\n        Generator::pushInt (mCode, mLiterals, value);\n    }\n\n    void ExprParser::pushFloatLiteral (float value)\n    {\n        mNextOperand = false;\n        mOperands.push_back ('f');\n        Generator::pushFloat (mCode, mLiterals, value);\n    }\n\n    void ExprParser::pushBinaryOperator (char c)\n    {\n        while (!mOperators.empty() && getPriority (getOperator())>=getPriority (c))\n            pop();\n\n        mOperators.push_back (c);\n        mNextOperand = true;\n    }\n\n    void ExprParser::close()\n    {\n        while (getOperator()!='(')\n            pop();\n\n        popOperator();\n    }\n\n    int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner)\n    {\n        return parseArguments (arguments, scanner, mCode);\n    }\n\n    bool ExprParser::handleMemberAccess (const std::string& name)\n    {\n        mMemberOp = false;\n\n        std::string name2 = Misc::StringUtils::lowerCase (name);\n        std::string id = Misc::StringUtils::lowerCase (mExplicit);\n\n        std::pair<char, bool> type = getContext().getMemberType (name2, id);\n\n        if (type.first!=' ')\n        {\n            Generator::fetchMember (mCode, mLiterals, type.first, name2, id, !type.second);\n\n            mNextOperand = false;\n            mExplicit.clear();\n            mOperands.push_back (type.first=='f' ? 'f' : 'l');\n            return true;\n        }\n\n        return false;\n    }\n\n    ExprParser::ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,\n        Literals& literals, bool argument)\n    : Parser (errorHandler, context), mLocals (locals), mLiterals (literals),\n      mNextOperand (true), mFirst (true), mArgument (argument), mRefOp (false), mMemberOp (false)\n    {}\n\n    bool ExprParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (!mExplicit.empty())\n            return Parser::parseInt (value, loc, scanner);\n\n        mFirst = false;\n\n        if (mNextOperand)\n        {\n            start();\n\n            pushIntegerLiteral (value);\n            mTokenLoc = loc;\n            return true;\n        }\n        else\n        {\n            // no comma was used between arguments\n            scanner.putbackInt (value, loc);\n            return false;\n        }\n    }\n\n    bool ExprParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (!mExplicit.empty())\n            return Parser::parseFloat (value, loc, scanner);\n\n        mFirst = false;\n\n        if (mNextOperand)\n        {\n            start();\n\n            pushFloatLiteral (value);\n            mTokenLoc = loc;\n            return true;\n        }\n        else\n        {\n            // no comma was used between arguments\n            scanner.putbackFloat (value, loc);\n            return false;\n        }\n    }\n\n    bool ExprParser::parseName (const std::string& name, const TokenLoc& loc,\n        Scanner& scanner)\n    {\n        if (!mExplicit.empty())\n        {\n            if (!mRefOp)\n            {\n                if (mMemberOp && handleMemberAccess (name))\n                    return true;\n\n                return Parser::parseName (name, loc, scanner);\n            }\n            else\n            {\n                mExplicit.clear();\n                getErrorHandler().warning (\"Stray explicit reference\", loc);\n            }\n        }\n\n        mFirst = false;\n\n        if (mNextOperand)\n        {\n            start();\n\n            std::string name2 = Misc::StringUtils::lowerCase (name);\n\n            char type = mLocals.getType (name2);\n\n            if (type!=' ')\n            {\n                Generator::fetchLocal (mCode, type, mLocals.getIndex (name2));\n                mNextOperand = false;\n                mOperands.push_back (type=='f' ? 'f' : 'l');\n                return true;\n            }\n\n            type = getContext().getGlobalType (name2);\n\n            if (type!=' ')\n            {\n                Generator::fetchGlobal (mCode, mLiterals, type, name2);\n                mNextOperand = false;\n                mOperands.push_back (type=='f' ? 'f' : 'l');\n                return true;\n            }\n\n            if (mExplicit.empty() && getContext().isId (name2))\n            {\n                mExplicit = name2;\n                return true;\n            }\n\n            // This is terrible, but of course we must have this for legacy content.\n            // Convert the string to a number even if it's impossible and use it as a number literal.\n            // Can't use stof/atof or to_string out of locale concerns.\n            float number;\n            std::stringstream stream(name2);\n            stream >> number;\n            stream.str(std::string());\n            stream.clear();\n            stream << number;\n\n            pushFloatLiteral(number);\n            mTokenLoc = loc;\n            getErrorHandler().warning (\"Parsing a non-variable string as a number: \" + stream.str(), loc);\n            return true;\n        }\n        else\n        {\n            // no comma was used between arguments\n            scanner.putbackName (name, loc);\n            return false;\n        }\n    }\n\n    bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (const Extensions *extensions = getContext().getExtensions())\n        {\n            std::string argumentType; // ignored\n            bool hasExplicit = false; // ignored\n            if (extensions->isInstruction (keyword, argumentType, hasExplicit))\n            {\n                // pretend this is not a keyword\n                std::string name = loc.mLiteral;\n                if (name.size()>=2 && name[0]=='\"' && name[name.size()-1]=='\"')\n                    name = name.substr (1, name.size()-2);\n                return parseName (name, loc, scanner);\n            }\n        }\n\n        if (keyword==Scanner::K_end || keyword==Scanner::K_begin ||\n            keyword==Scanner::K_short || keyword==Scanner::K_long ||\n            keyword==Scanner::K_float || keyword==Scanner::K_if ||\n            keyword==Scanner::K_endif || keyword==Scanner::K_else ||\n            keyword==Scanner::K_elseif || keyword==Scanner::K_while ||\n            keyword==Scanner::K_endwhile || keyword==Scanner::K_return ||\n            keyword==Scanner::K_messagebox || keyword==Scanner::K_set ||\n            keyword==Scanner::K_to)\n        {\n            return parseName (loc.mLiteral, loc, scanner);\n        }\n\n        mFirst = false;\n\n        if (!mExplicit.empty())\n        {\n            if (mRefOp && mNextOperand)\n            {\n\n                // check for custom extensions\n                if (const Extensions *extensions = getContext().getExtensions())\n                {\n                    char returnType;\n                    std::string argumentType;\n\n                    bool hasExplicit = true;\n                    if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit))\n                    {\n                        if (!hasExplicit)\n                        {\n                            getErrorHandler().warning (\"Stray explicit reference\", loc);\n                            mExplicit.clear();\n                        }\n\n                        start();\n\n                        mTokenLoc = loc;\n                        int optionals = parseArguments (argumentType, scanner);\n\n                        extensions->generateFunctionCode (keyword, mCode, mLiterals, mExplicit,\n                            optionals);\n                        mOperands.push_back (returnType);\n                        mExplicit.clear();\n                        mRefOp = false;\n\n                        mNextOperand = false;\n                        return true;\n                    }\n                }\n            }\n\n            return Parser::parseKeyword (keyword, loc, scanner);\n        }\n\n        if (mNextOperand)\n        {\n            if (keyword==Scanner::K_getsquareroot)\n            {\n                start();\n\n                mTokenLoc = loc;\n                parseArguments (\"f\", scanner);\n\n                Generator::squareRoot (mCode);\n                mOperands.push_back ('f');\n\n                mNextOperand = false;\n                return true;\n            }\n            else\n            {\n                // check for custom extensions\n                if (const Extensions *extensions = getContext().getExtensions())\n                {\n                    start();\n\n                    char returnType;\n                    std::string argumentType;\n\n                    bool hasExplicit = false;\n\n                    if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit))\n                    {\n                        mTokenLoc = loc;\n                        int optionals = parseArguments (argumentType, scanner);\n\n                        extensions->generateFunctionCode (keyword, mCode, mLiterals, \"\", optionals);\n                        mOperands.push_back (returnType);\n\n                        mNextOperand = false;\n                        return true;\n                    }\n                }\n            }\n        }\n        else\n        {\n            // no comma was used between arguments\n            scanner.putbackKeyword (keyword, loc);\n            return false;\n        }\n\n        return Parser::parseKeyword (keyword, loc, scanner);\n    }\n\n    bool ExprParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (!mExplicit.empty())\n        {\n            if (mRefOp && code==Scanner::S_open)\n            {\n                /// \\todo add option to disable this workaround\n                mOperators.push_back ('(');\n                mTokenLoc = loc;\n                return true;\n            }\n\n            if (!mRefOp && code==Scanner::S_ref)\n            {\n                mRefOp = true;\n                return true;\n            }\n\n            if (!mMemberOp && code==Scanner::S_member)\n            {\n                mMemberOp = true;\n                return true;\n            }\n\n            return Parser::parseSpecial (code, loc, scanner);\n        }\n\n        if (code==Scanner::S_comma)\n        {\n            mTokenLoc = loc;\n\n            if (mFirst)\n            {\n                // leading comma\n                mFirst = false;\n                return true;\n            }\n\n            // end marker\n            scanner.putbackSpecial (code, loc);\n            return false;\n        }\n\n        mFirst = false;\n\n        if (code==Scanner::S_newline)\n        {\n            // end marker\n            if (mTokenLoc.mLiteral.empty())\n                mTokenLoc = loc;\n            scanner.putbackSpecial (code, loc);\n            return false;\n        }\n\n        if (code==Scanner::S_minus && mNextOperand)\n        {\n            // unary\n            mOperators.push_back ('m');\n            mTokenLoc = loc;\n            return true;\n        }\n\n        if (code ==Scanner::S_plus && mNextOperand)\n        {\n            // Also unary, but +, just ignore it\n            mTokenLoc = loc;\n            return true;\n        }\n\n        if (code==Scanner::S_open)\n        {\n            if (mNextOperand)\n            {\n                mOperators.push_back ('(');\n                mTokenLoc = loc;\n                return true;\n            }\n            else\n            {\n                // no comma was used between arguments\n                scanner.putbackSpecial (code, loc);\n                return false;\n            }\n        }\n\n        if (code==Scanner::S_close && !mNextOperand)\n        {\n            if (isOpen())\n            {\n                close();\n                return true;\n            }\n\n            mTokenLoc = loc;\n            scanner.putbackSpecial (code, loc);\n            return false;\n        }\n\n        if (!mNextOperand)\n        {\n            mTokenLoc = loc;\n            char c = 0; // comparison\n\n            switch (code)\n            {\n                case Scanner::S_plus: c = '+'; break;\n                case Scanner::S_minus: c = '-'; break;\n                case Scanner::S_mult: pushBinaryOperator ('*'); return true;\n                case Scanner::S_div: pushBinaryOperator ('/'); return true;\n                case Scanner::S_cmpEQ: c = 'e'; break;\n                case Scanner::S_cmpNE: c = 'n'; break;\n                case Scanner::S_cmpLT: c = 'l'; break;\n                case Scanner::S_cmpLE: c = 'L'; break;\n                case Scanner::S_cmpGT: c = 'g'; break;\n                case Scanner::S_cmpGE: c = 'G'; break;\n            }\n\n            if (c)\n            {\n                if (mArgument && !isOpen())\n                {\n                    // expression ends here\n                    // Thank you Morrowind for this rotten syntax :(\n                    scanner.putbackSpecial (code, loc);\n                    return false;\n                }\n\n                pushBinaryOperator (c);\n                return true;\n            }\n        }\n\n        return Parser::parseSpecial (code, loc, scanner);\n    }\n\n    void ExprParser::reset()\n    {\n        mOperands.clear();\n        mOperators.clear();\n        mNextOperand = true;\n        mCode.clear();\n        mFirst = true;\n        mExplicit.clear();\n        mRefOp = false;\n        mMemberOp = false;\n        Parser::reset();\n    }\n\n    char ExprParser::append (std::vector<Interpreter::Type_Code>& code)\n    {\n        if (mOperands.empty() && mOperators.empty())\n        {\n            getErrorHandler().error (\"Missing expression\", mTokenLoc);\n            return 'l';\n        }\n\n        if (mNextOperand || mOperands.empty())\n        {\n            getErrorHandler().error (\"Syntax error in expression\", mTokenLoc);\n            return 'l';\n        }\n\n        while (!mOperators.empty())\n            pop();\n\n        std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));\n\n        assert (mOperands.size()==1);\n        return mOperands[0];\n    }\n\n    int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner,\n        std::vector<Interpreter::Type_Code>& code, int ignoreKeyword)\n    {\n        bool optional = false;\n        int optionalCount = 0;\n\n        ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true);\n        StringParser stringParser (getErrorHandler(), getContext(), mLiterals);\n        DiscardParser discardParser (getErrorHandler(), getContext());\n        JunkParser junkParser (getErrorHandler(), getContext(), ignoreKeyword);\n\n        std::stack<std::vector<Interpreter::Type_Code> > stack;\n\n        for (char argument : arguments)\n        {\n            if (argument=='/')\n            {\n                optional = true;\n            }\n            else if (argument=='S' || argument=='c' || argument=='x')\n            {\n                stringParser.reset();\n\n                if (optional || argument=='x')\n                    stringParser.setOptional (true);\n\n                if (argument=='c') stringParser.smashCase();\n                if (argument=='x') stringParser.discard();\n                scanner.scan (stringParser);\n\n                if ((optional || argument=='x') && stringParser.isEmpty())\n                    break;\n\n                if (argument!='x')\n                {\n                    std::vector<Interpreter::Type_Code> tmp;\n                    stringParser.append (tmp);\n\n                    stack.push (tmp);\n\n                    if (optional)\n                        ++optionalCount;\n                }\n                else\n                    getErrorHandler().warning (\"Extra argument\",\n                        stringParser.getTokenLoc());\n            }\n            else if (argument=='X')\n            {\n                parser.reset();\n\n                parser.setOptional (true);\n\n                scanner.scan (parser);\n\n                if (parser.isEmpty())\n                    break;\n                else\n                    getErrorHandler().warning(\"Extra argument\", parser.getTokenLoc());\n            }\n            else if (argument=='z')\n            {\n                discardParser.reset();\n                discardParser.setOptional (true);\n\n                scanner.scan (discardParser);\n\n                if (discardParser.isEmpty())\n                    break;\n                else\n                    getErrorHandler().warning(\"Extra argument\", discardParser.getTokenLoc());\n            }\n            else if (argument=='j')\n            {\n                /// \\todo disable this when operating in strict mode\n                junkParser.reset();\n\n                scanner.scan (junkParser);\n            }\n            else\n            {\n                parser.reset();\n\n                if (optional)\n                    parser.setOptional (true);\n\n                scanner.scan (parser);\n\n                if (optional && parser.isEmpty())\n                    break;\n\n                std::vector<Interpreter::Type_Code> tmp;\n\n                char type = parser.append (tmp);\n\n                if (type!=argument)\n                    Generator::convert (tmp, type, argument);\n\n                stack.push (tmp);\n\n                if (optional)\n                    ++optionalCount;\n            }\n        }\n\n        while (!stack.empty())\n        {\n            std::vector<Interpreter::Type_Code>& tmp = stack.top();\n\n            std::copy (tmp.begin(), tmp.end(), std::back_inserter (code));\n\n            stack.pop();\n        }\n\n        return optionalCount;\n    }\n\n    const TokenLoc& ExprParser::getTokenLoc() const\n    {\n        return mTokenLoc;\n    }\n}\n"
  },
  {
    "path": "components/compiler/exprparser.hpp",
    "content": "#ifndef COMPILER_EXPRPARSER_H_INCLUDED\n#define COMPILER_EXPRPARSER_H_INCLUDED\n\n#include <vector>\n\n#include <components/interpreter/types.hpp>\n\n#include \"parser.hpp\"\n#include \"tokenloc.hpp\"\n\nnamespace Compiler\n{\n    class Locals;\n    class Literals;\n\n    class ExprParser : public Parser\n    {\n            Locals& mLocals;\n            Literals& mLiterals;\n            std::vector<char> mOperands;\n            std::vector<char> mOperators;\n            bool mNextOperand;\n            TokenLoc mTokenLoc;\n            std::vector<Interpreter::Type_Code> mCode;\n            bool mFirst;\n            bool mArgument;\n            std::string mExplicit;\n            bool mRefOp;\n            bool mMemberOp;\n\n            static int getPriority (char op) ;\n\n            char getOperandType (int Index = 0) const;\n\n            char getOperator() const;\n\n            bool isOpen() const;\n\n            void popOperator();\n\n            void popOperand();\n\n            void replaceBinaryOperands();\n\n            void pop();\n\n            void pushIntegerLiteral (int value);\n\n            void pushFloatLiteral (float value);\n\n            void pushBinaryOperator (char c);\n\n            void close();\n\n            int parseArguments (const std::string& arguments, Scanner& scanner);\n\n            bool handleMemberAccess (const std::string& name);\n\n        public:\n\n            ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,\n                Literals& literals, bool argument = false);\n            ///< constructor\n            /// \\param argument Parser is used to parse function- or instruction-\n            /// arguments (this influences the precedence rules).\n\n            char getType() const;\n            ///< Return type of parsed expression ('l' integer, 'f' float)\n\n            bool parseInt (int value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle an int token.\n            /// \\return fetch another token?\n\n            bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a float token.\n            /// \\return fetch another token?\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void reset() override;\n            ///< Reset parser to clean state.\n\n            char append (std::vector<Interpreter::Type_Code>& code);\n            ///< Generate code for parsed expression.\n            /// \\return Type ('l': integer, 'f': float)\n\n            int parseArguments (const std::string& arguments, Scanner& scanner,\n                std::vector<Interpreter::Type_Code>& code, int ignoreKeyword = -1);\n            ///< Parse sequence of arguments specified by \\a arguments.\n            /// \\param arguments Uses ScriptArgs typedef\n            /// \\see Compiler::ScriptArgs\n            /// \\param invert Store arguments in reverted order.\n            /// \\param ignoreKeyword A keyword that is seen as junk\n            /// \\return number of optional arguments\n\n            const TokenLoc& getTokenLoc() const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/extensions.cpp",
    "content": "#include \"extensions.hpp\"\n\n#include <cassert>\n#include <stdexcept>\n\n#include \"generator.hpp\"\n#include \"literals.hpp\"\n\nnamespace Compiler\n{\n    Extensions::Extensions() : mNextKeywordIndex (-1) {}\n\n    int Extensions::searchKeyword (const std::string& keyword) const\n    {\n        auto iter = mKeywords.find (keyword);\n        if (iter==mKeywords.end())\n            return 0;\n\n        return iter->second;\n    }\n\n    bool Extensions::isFunction (int keyword, ScriptReturn& returnType, ScriptArgs& argumentType,\n        bool& explicitReference) const\n    {\n        auto iter = mFunctions.find (keyword);\n        if (iter==mFunctions.end())\n            return false;\n\n        if (explicitReference && iter->second.mCodeExplicit==-1)\n            explicitReference = false;\n\n        returnType = iter->second.mReturn;\n        argumentType = iter->second.mArguments;\n        return true;\n    }\n\n    bool Extensions::isInstruction (int keyword, ScriptArgs& argumentType,\n        bool& explicitReference) const\n    {\n        auto iter = mInstructions.find (keyword);\n        if (iter==mInstructions.end())\n            return false;\n\n        if (explicitReference && iter->second.mCodeExplicit==-1)\n            explicitReference = false;\n\n        argumentType = iter->second.mArguments;\n        return true;\n    }\n\n    void Extensions::registerFunction (const std::string& keyword, ScriptReturn returnType,\n        const ScriptArgs& argumentType, int code, int codeExplicit)\n    {\n        Function function;\n\n        if (argumentType.find ('/')==std::string::npos)\n        {\n            function.mSegment = 5;\n            assert (code>=33554432 && code<=67108863);\n            assert (codeExplicit==-1 || (codeExplicit>=33554432 && codeExplicit<=67108863));\n        }\n        else\n        {\n            function.mSegment = 3;\n            assert (code>=0x20000 && code<=0x2ffff);\n            assert (codeExplicit==-1 || (codeExplicit>=0x20000 && codeExplicit<=0x2ffff));\n        }\n\n        int keywordIndex = mNextKeywordIndex--;\n\n        mKeywords.insert (std::make_pair (keyword, keywordIndex));\n\n        function.mReturn = returnType;\n        function.mArguments = argumentType;\n        function.mCode = code;\n        function.mCodeExplicit = codeExplicit;\n\n        mFunctions.insert (std::make_pair (keywordIndex, function));\n    }\n\n    void Extensions::registerInstruction (const std::string& keyword,\n        const ScriptArgs& argumentType, int code, int codeExplicit)\n    {\n        Instruction instruction;\n\n        if (argumentType.find ('/')==std::string::npos)\n        {\n            instruction.mSegment = 5;\n            assert (code>=33554432 && code<=67108863);\n            assert (codeExplicit==-1 || (codeExplicit>=33554432 && codeExplicit<=67108863));\n        }\n        else\n        {\n            instruction.mSegment = 3;\n            assert (code>=0x20000 && code<=0x2ffff);\n            assert (codeExplicit==-1 || (codeExplicit>=0x20000 && codeExplicit<=0x2ffff));\n        }\n\n        int keywordIndex = mNextKeywordIndex--;\n\n        mKeywords.insert (std::make_pair (keyword, keywordIndex));\n\n        instruction.mArguments = argumentType;\n        instruction.mCode = code;\n        instruction.mCodeExplicit = codeExplicit;\n\n        mInstructions.insert (std::make_pair (keywordIndex, instruction));\n    }\n\n    void Extensions::generateFunctionCode (int keyword, std::vector<Interpreter::Type_Code>& code,\n        Literals& literals, const std::string& id, int optionalArguments) const\n    {\n        assert (optionalArguments>=0);\n\n        auto iter = mFunctions.find (keyword);\n        if (iter==mFunctions.end())\n            throw std::logic_error (\"unknown custom function keyword\");\n\n        if (optionalArguments && iter->second.mSegment!=3)\n            throw std::logic_error (\"functions with optional arguments must be placed into segment 3\");\n\n        if (!id.empty())\n        {\n            if (iter->second.mCodeExplicit==-1)\n                throw std::logic_error (\"explicit references not supported\");\n\n            int index = literals.addString (id);\n            Generator::pushInt (code, literals, index);\n        }\n\n        switch (iter->second.mSegment)\n        {\n            case 3:\n\n                if (optionalArguments>=256)\n                    throw std::logic_error (\"number of optional arguments is too large for segment 3\");\n\n                code.push_back (Generator::segment3 (\n                    id.empty() ? iter->second.mCode : iter->second.mCodeExplicit,\n                    optionalArguments));\n\n                break;\n\n            case 5:\n\n                code.push_back (Generator::segment5 (\n                    id.empty() ? iter->second.mCode : iter->second.mCodeExplicit));\n\n                break;\n\n            default:\n\n                throw std::logic_error (\"unsupported code segment\");\n        }\n    }\n\n    void Extensions::generateInstructionCode (int keyword,\n        std::vector<Interpreter::Type_Code>& code, Literals& literals, const std::string& id,\n        int optionalArguments) const\n    {\n        assert (optionalArguments>=0);\n\n        auto iter = mInstructions.find (keyword);\n        if (iter==mInstructions.end())\n            throw std::logic_error (\"unknown custom instruction keyword\");\n\n        if (optionalArguments && iter->second.mSegment!=3)\n            throw std::logic_error (\"instructions with optional arguments must be placed into segment 3\");\n\n        if (!id.empty())\n        {\n            if (iter->second.mCodeExplicit==-1)\n                throw std::logic_error (\"explicit references not supported\");\n\n            int index = literals.addString (id);\n            Generator::pushInt (code, literals, index);\n        }\n\n        switch (iter->second.mSegment)\n        {\n            case 3:\n\n                if (optionalArguments>=256)\n                    throw std::logic_error (\"number of optional arguments is too large for segment 3\");\n\n                code.push_back (Generator::segment3 (\n                    id.empty() ? iter->second.mCode : iter->second.mCodeExplicit,\n                    optionalArguments));\n\n                break;\n\n            case 5:\n\n                code.push_back (Generator::segment5 (\n                    id.empty() ? iter->second.mCode : iter->second.mCodeExplicit));\n\n                break;\n\n            default:\n\n                throw std::logic_error (\"unsupported code segment\");\n        }\n    }\n\n    void Extensions::listKeywords (std::vector<std::string>& keywords) const\n    {\n        for (const auto & mKeyword : mKeywords)\n            keywords.push_back (mKeyword.first);\n    }\n}\n"
  },
  {
    "path": "components/compiler/extensions.hpp",
    "content": "#ifndef COMPILER_EXTENSIONS_H_INCLUDED\n#define COMPILER_EXTENSIONS_H_INCLUDED\n\n#include <string>\n#include <map>\n#include <vector>\n\n#include <components/interpreter/types.hpp>\n\nnamespace Compiler\n{\n    class Literals;\n\n    /// Typedef for script arguments string\n    /** Every character reperesents an argument to the command. All arguments are required until a /, after which\n        every argument is optional. <BR>\n        Eg: fff/f represents 3 required floats followed by one optional float <BR>\n        f - Float <BR>\n        c - String, case smashed <BR>\n        l - Integer <BR>\n        s - Short <BR>\n        S - String, case preserved <BR>\n        x - Optional, ignored string argument. Emits a parser warning when this argument is supplied. <BR>\n        X - Optional, ignored numeric expression. Emits a parser warning when this argument is supplied. <BR>\n        z - Optional, ignored string or numeric argument. Emits a parser warning when this argument is supplied. <BR>\n        j - A piece of junk (either . or a specific keyword)\n    **/\n    typedef std::string ScriptArgs;\n\n    /// Typedef for script return char\n    /** The character represents the type of data being returned. <BR>\n        f - float <BR>\n        S - String (Cell names) <BR>\n        l - Integer\n    **/\n    typedef char ScriptReturn;\n\n    /// \\brief Collection of compiler extensions\n    class Extensions\n    {\n\n            struct Function\n            {\n                char mReturn;\n                ScriptArgs mArguments;\n                int mCode;\n                int mCodeExplicit;\n                int mSegment;\n            };\n\n            struct Instruction\n            {\n                ScriptArgs mArguments;\n                int mCode;\n                int mCodeExplicit;\n                int mSegment;\n            };\n\n            int mNextKeywordIndex;\n            std::map<std::string, int> mKeywords;\n            std::map<int, Function> mFunctions;\n            std::map<int, Instruction> mInstructions;\n\n        public:\n\n            Extensions();\n\n            int searchKeyword (const std::string& keyword) const;\n            ///< Return extension keyword code, that is assigned to the string \\a keyword.\n            /// - if no match is found 0 is returned.\n            /// - keyword must be all lower case.\n\n            bool isFunction (int keyword, ScriptReturn& returnType, ScriptArgs& argumentType,\n                bool& explicitReference) const;\n            ///< Is this keyword registered with a function? If yes, return return and argument\n            /// types.\n            /// \\param explicitReference In: has explicit reference; Out: set to false, if\n            /// explicit reference is not available for this instruction.\n\n            bool isInstruction (int keyword, ScriptArgs& argumentType,\n                bool& explicitReference) const;\n            ///< Is this keyword registered with a function? If yes, return argument types.\n            /// \\param explicitReference In: has explicit reference; Out: set to false, if\n            /// explicit reference is not available for this instruction.\n\n            void registerFunction (const std::string& keyword, ScriptReturn returnType,\n                const ScriptArgs& argumentType, int code, int codeExplicit = -1);\n            ///< Register a custom function\n            /// - keyword must be all lower case.\n            /// - keyword must be unique\n            /// - if explicit references are not supported, segment5codeExplicit must be set to -1\n            /// \\note Currently only segment 3 and segment 5 opcodes are supported.\n\n            void registerInstruction (const std::string& keyword,\n                const ScriptArgs& argumentType, int code, int codeExplicit = -1);\n            ///< Register a custom instruction\n            /// - keyword must be all lower case.\n            /// - keyword must be unique\n            /// - if explicit references are not supported, segment5codeExplicit must be set to -1\n            /// \\note Currently only segment 3 and segment 5 opcodes are supported.\n\n            void generateFunctionCode (int keyword, std::vector<Interpreter::Type_Code>& code,\n                Literals& literals, const std::string& id, int optionalArguments) const;\n            ///< Append code for function to \\a code.\n\n            void generateInstructionCode (int keyword, std::vector<Interpreter::Type_Code>& code,\n                Literals& literals, const std::string& id, int optionalArguments) const;\n            ///< Append code for function to \\a code.\n\n            void listKeywords (std::vector<std::string>& keywords) const;\n            ///< Append all known keywords to \\a kaywords.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/extensions0.cpp",
    "content": "#include \"extensions0.hpp\"\n\n#include \"opcodes.hpp\"\n#include \"extensions.hpp\"\n\nnamespace Compiler\n{\n    void registerExtensions (Extensions& extensions, bool consoleOnly)\n    {\n        Ai::registerExtensions (extensions);\n        Animation::registerExtensions (extensions);\n        Cell::registerExtensions (extensions);\n        Container::registerExtensions (extensions);\n        Control::registerExtensions (extensions);\n        Dialogue::registerExtensions (extensions);\n        Gui::registerExtensions (extensions);\n        Misc::registerExtensions (extensions);\n        Sky::registerExtensions (extensions);\n        Sound::registerExtensions (extensions);\n        Stats::registerExtensions (extensions);\n        Transformation::registerExtensions (extensions);\n\n        if (consoleOnly)\n        {\n            Console::registerExtensions (extensions);\n            User::registerExtensions (extensions);\n        }\n    }\n\n    namespace Ai\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerInstruction (\"aiactivate\", \"c/l\", opcodeAIActivate,\n                opcodeAIActivateExplicit);\n            extensions.registerInstruction (\"aitravel\", \"fff/lx\", opcodeAiTravel,\n                opcodeAiTravelExplicit);\n            extensions.registerInstruction (\"aiescort\", \"cffff/l\", opcodeAiEscort,\n                opcodeAiEscortExplicit);\n            extensions.registerInstruction (\"aiescortcell\", \"ccffff/l\", opcodeAiEscortCell,\n                opcodeAiEscortCellExplicit);\n            extensions.registerInstruction (\"aiwander\", \"fff/llllllllll\", opcodeAiWander,\n                opcodeAiWanderExplicit);\n            extensions.registerInstruction (\"aifollow\", \"cffff/llllllll\", opcodeAiFollow,\n                opcodeAiFollowExplicit);\n            extensions.registerInstruction (\"aifollowcell\", \"ccffff/l\", opcodeAiFollowCell,\n                opcodeAiFollowCellExplicit);\n            extensions.registerFunction (\"getaipackagedone\", 'l', \"\", opcodeGetAiPackageDone,\n                opcodeGetAiPackageDoneExplicit);\n            extensions.registerFunction (\"getcurrentaipackage\", 'l', \"\", opcodeGetCurrentAiPackage,\n                opcodeGetCurrentAiPackageExplicit);\n            extensions.registerFunction (\"getdetected\", 'l', \"c\", opcodeGetDetected,\n                opcodeGetDetectedExplicit);\n            extensions.registerInstruction (\"sethello\", \"l\", opcodeSetHello, opcodeSetHelloExplicit);\n            extensions.registerInstruction (\"setfight\", \"l\", opcodeSetFight, opcodeSetFightExplicit);\n            extensions.registerInstruction (\"setflee\", \"l\", opcodeSetFlee, opcodeSetFleeExplicit);\n            extensions.registerInstruction (\"setalarm\", \"l\", opcodeSetAlarm, opcodeSetAlarmExplicit);\n            extensions.registerInstruction (\"modhello\", \"l\", opcodeModHello, opcodeModHelloExplicit);\n            extensions.registerInstruction (\"modfight\", \"l\", opcodeModFight, opcodeModFightExplicit);\n            extensions.registerInstruction (\"modflee\", \"l\", opcodeModFlee, opcodeModFleeExplicit);\n            extensions.registerInstruction (\"modalarm\", \"l\", opcodeModAlarm, opcodeModAlarmExplicit);\n            extensions.registerInstruction (\"toggleai\", \"\", opcodeToggleAI);\n            extensions.registerInstruction (\"tai\", \"\", opcodeToggleAI);\n            extensions.registerInstruction(\"startcombat\", \"c\", opcodeStartCombat, opcodeStartCombatExplicit);\n            extensions.registerInstruction(\"stopcombat\", \"x\", opcodeStopCombat, opcodeStopCombatExplicit);\n            extensions.registerFunction (\"gethello\", 'l', \"\", opcodeGetHello, opcodeGetHelloExplicit);\n            extensions.registerFunction (\"getfight\", 'l', \"\", opcodeGetFight, opcodeGetFightExplicit);\n            extensions.registerFunction (\"getflee\", 'l', \"\", opcodeGetFlee, opcodeGetFleeExplicit);\n            extensions.registerFunction (\"getalarm\", 'l', \"\", opcodeGetAlarm, opcodeGetAlarmExplicit);\n            extensions.registerFunction (\"getlineofsight\", 'l', \"c\", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit);\n            extensions.registerFunction (\"getlos\", 'l', \"c\", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit);\n            extensions.registerFunction(\"gettarget\", 'l', \"c\", opcodeGetTarget, opcodeGetTargetExplicit);\n            extensions.registerInstruction(\"face\", \"ffX\", opcodeFace, opcodeFaceExplicit);\n        }\n    }\n\n    namespace Animation\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerInstruction (\"skipanim\", \"\", opcodeSkipAnim, opcodeSkipAnimExplicit);\n            extensions.registerInstruction (\"playgroup\", \"c/l\", opcodePlayAnim, opcodePlayAnimExplicit);\n            extensions.registerInstruction (\"loopgroup\", \"cl/l\", opcodeLoopAnim, opcodeLoopAnimExplicit);\n        }\n    }\n\n    namespace Cell\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerFunction (\"cellchanged\", 'l', \"\", opcodeCellChanged);\n            extensions.registerInstruction(\"testcells\", \"\", opcodeTestCells);\n            extensions.registerInstruction(\"testinteriorcells\", \"\", opcodeTestInteriorCells);\n            extensions.registerInstruction (\"coc\", \"S\", opcodeCOC);\n            extensions.registerInstruction (\"centeroncell\", \"S\", opcodeCOC);\n            extensions.registerInstruction (\"coe\", \"ll\", opcodeCOE);\n            extensions.registerInstruction (\"centeronexterior\", \"ll\", opcodeCOE);\n            extensions.registerInstruction (\"setwaterlevel\", \"f\", opcodeSetWaterLevel);\n            extensions.registerInstruction (\"modwaterlevel\", \"f\", opcodeModWaterLevel);\n            extensions.registerFunction (\"getinterior\", 'l', \"\", opcodeGetInterior);\n            extensions.registerFunction (\"getpccell\", 'l', \"c\", opcodeGetPCCell);\n            extensions.registerFunction (\"getwaterlevel\", 'f', \"\", opcodeGetWaterLevel);\n        }\n    }\n\n    namespace Console\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n\n        }\n    }\n\n    namespace Container\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerInstruction (\"additem\", \"clX\", opcodeAddItem, opcodeAddItemExplicit);\n            extensions.registerFunction (\"getitemcount\", 'l', \"cX\", opcodeGetItemCount,\n                opcodeGetItemCountExplicit);\n            extensions.registerInstruction (\"removeitem\", \"clX\", opcodeRemoveItem,\n                opcodeRemoveItemExplicit);\n            extensions.registerInstruction (\"equip\", \"cX\", opcodeEquip, opcodeEquipExplicit);\n            extensions.registerFunction (\"getarmortype\", 'l', \"l\", opcodeGetArmorType, opcodeGetArmorTypeExplicit);\n            extensions.registerFunction (\"hasitemequipped\", 'l', \"c\", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit);\n            extensions.registerFunction (\"hassoulgem\", 'l', \"c\", opcodeHasSoulGem, opcodeHasSoulGemExplicit);\n            extensions.registerFunction (\"getweapontype\", 'l', \"\", opcodeGetWeaponType, opcodeGetWeaponTypeExplicit);\n        }\n    }\n\n    namespace Control\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            std::string enable (\"enable\");\n            std::string disable (\"disable\");\n\n            for (int i=0; i<numberOfControls; ++i)\n            {\n                extensions.registerInstruction (enable + controls[i], \"\", opcodeEnable+i);\n                extensions.registerInstruction (disable + controls[i], \"\", opcodeDisable+i);\n                extensions.registerFunction (std::string(\"get\") + controls[i] + std::string(\"disabled\"), 'l', \"\", opcodeGetDisabled+i);\n            }\n\n            extensions.registerInstruction (\"togglecollision\", \"\", opcodeToggleCollision);\n            extensions.registerInstruction (\"tcl\", \"\", opcodeToggleCollision);\n\n            extensions.registerInstruction (\"clearforcerun\", \"\", opcodeClearForceRun,\n                opcodeClearForceRunExplicit);\n            extensions.registerInstruction (\"forcerun\", \"\", opcodeForceRun,\n                opcodeForceRunExplicit);\n\n            extensions.registerInstruction (\"clearforcejump\", \"\", opcodeClearForceJump,\n                opcodeClearForceJumpExplicit);\n            extensions.registerInstruction (\"forcejump\", \"\", opcodeForceJump,\n                opcodeForceJumpExplicit);\n\n            extensions.registerInstruction (\"clearforcemovejump\", \"\", opcodeClearForceMoveJump,\n                opcodeClearForceMoveJumpExplicit);\n            extensions.registerInstruction (\"forcemovejump\", \"\", opcodeForceMoveJump,\n                opcodeForceMoveJumpExplicit);\n\n            extensions.registerInstruction (\"clearforcesneak\", \"\", opcodeClearForceSneak,\n                opcodeClearForceSneakExplicit);\n            extensions.registerInstruction (\"forcesneak\", \"\", opcodeForceSneak,\n                opcodeForceSneakExplicit);\n            extensions.registerFunction (\"getpcrunning\", 'l', \"\", opcodeGetPcRunning);\n            extensions.registerFunction (\"getpcsneaking\", 'l', \"\", opcodeGetPcSneaking);\n            extensions.registerFunction (\"getforcerun\", 'l', \"\", opcodeGetForceRun, opcodeGetForceRunExplicit);\n            extensions.registerFunction (\"getforcejump\", 'l', \"\", opcodeGetForceJump, opcodeGetForceJumpExplicit);\n            extensions.registerFunction (\"getforcemovejump\", 'l', \"\", opcodeGetForceMoveJump, opcodeGetForceMoveJumpExplicit);\n            extensions.registerFunction (\"getforcesneak\", 'l', \"\", opcodeGetForceSneak, opcodeGetForceSneakExplicit);\n        }\n    }\n\n    namespace Dialogue\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerInstruction (\"journal\", \"cl\", opcodeJournal, opcodeJournalExplicit);\n            extensions.registerInstruction (\"setjournalindex\", \"cl\", opcodeSetJournalIndex);\n            extensions.registerFunction (\"getjournalindex\", 'l', \"c\", opcodeGetJournalIndex);\n            extensions.registerInstruction (\"addtopic\", \"S\" , opcodeAddTopic);\n            extensions.registerInstruction (\"choice\", \"j/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl\", opcodeChoice);\n            extensions.registerInstruction(\"forcegreeting\",\"z\",opcodeForceGreeting,\n                opcodeForceGreetingExplicit);\n            extensions.registerInstruction(\"goodbye\", \"\", opcodeGoodbye);\n            extensions.registerInstruction(\"setreputation\", \"l\", opcodeSetReputation,\n                opcodeSetReputationExplicit);\n            extensions.registerInstruction(\"modreputation\", \"l\", opcodeModReputation,\n                opcodeModReputationExplicit);\n            extensions.registerFunction(\"getreputation\", 'l', \"\", opcodeGetReputation,\n                opcodeGetReputationExplicit);\n            extensions.registerFunction(\"samefaction\", 'l', \"\", opcodeSameFaction,\n                opcodeSameFactionExplicit);\n            extensions.registerInstruction(\"modfactionreaction\", \"ccl\", opcodeModFactionReaction);\n            extensions.registerInstruction(\"setfactionreaction\", \"ccl\", opcodeSetFactionReaction);\n            extensions.registerFunction(\"getfactionreaction\", 'l', \"ccX\", opcodeGetFactionReaction);\n            extensions.registerInstruction(\"clearinfoactor\", \"\", opcodeClearInfoActor, opcodeClearInfoActorExplicit);\n        }\n    }\n\n    namespace Gui\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerInstruction (\"enablebirthmenu\", \"\", opcodeEnableBirthMenu);\n            extensions.registerInstruction (\"enableclassmenu\", \"\", opcodeEnableClassMenu);\n            extensions.registerInstruction (\"enablenamemenu\", \"\", opcodeEnableNameMenu);\n            extensions.registerInstruction (\"enableracemenu\", \"\", opcodeEnableRaceMenu);\n            extensions.registerInstruction (\"enablestatreviewmenu\", \"\",\n                opcodeEnableStatsReviewMenu);\n\n            extensions.registerInstruction (\"enableinventorymenu\", \"\", opcodeEnableInventoryMenu);\n            extensions.registerInstruction (\"enablemagicmenu\", \"\", opcodeEnableMagicMenu);\n            extensions.registerInstruction (\"enablemapmenu\", \"\", opcodeEnableMapMenu);\n            extensions.registerInstruction (\"enablestatsmenu\", \"\", opcodeEnableStatsMenu);\n\n            extensions.registerInstruction (\"enablerest\", \"\", opcodeEnableRest);\n            extensions.registerInstruction (\"enablelevelupmenu\", \"\", opcodeEnableLevelupMenu);\n\n            extensions.registerInstruction (\"showrestmenu\", \"\", opcodeShowRestMenu, opcodeShowRestMenuExplicit);\n\n            extensions.registerFunction (\"getbuttonpressed\", 'l', \"\", opcodeGetButtonPressed);\n\n            extensions.registerInstruction (\"togglefogofwar\", \"\", opcodeToggleFogOfWar);\n            extensions.registerInstruction (\"tfow\", \"\", opcodeToggleFogOfWar);\n\n            extensions.registerInstruction (\"togglefullhelp\", \"\", opcodeToggleFullHelp);\n            extensions.registerInstruction (\"tfh\", \"\", opcodeToggleFullHelp);\n\n            extensions.registerInstruction (\"showmap\", \"Sxxxx\", opcodeShowMap);\n            extensions.registerInstruction (\"fillmap\", \"\", opcodeFillMap);\n            extensions.registerInstruction (\"menutest\", \"/l\", opcodeMenuTest);\n            extensions.registerInstruction (\"togglemenus\", \"\", opcodeToggleMenus);\n            extensions.registerInstruction (\"tm\", \"\", opcodeToggleMenus);\n        }\n    }\n\n    namespace Misc\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerFunction (\"menumode\", 'l', \"\", opcodeMenuMode);\n            extensions.registerFunction (\"random\", 'f', \"l\", opcodeRandom);\n            extensions.registerFunction (\"scriptrunning\", 'l', \"c\", opcodeScriptRunning);\n            extensions.registerInstruction (\"startscript\", \"c\", opcodeStartScript, opcodeStartScriptExplicit);\n            extensions.registerInstruction (\"stopscript\", \"c\", opcodeStopScript);\n            extensions.registerFunction (\"getsecondspassed\", 'f', \"\", opcodeGetSecondsPassed);\n            extensions.registerInstruction (\"enable\", \"x\", opcodeEnable, opcodeEnableExplicit);\n            extensions.registerInstruction (\"disable\", \"x\", opcodeDisable, opcodeDisableExplicit);\n            extensions.registerFunction (\"getdisabled\", 'l', \"x\", opcodeGetDisabled, opcodeGetDisabledExplicit);\n            extensions.registerFunction (\"xbox\", 'l', \"\", opcodeXBox);\n            extensions.registerFunction (\"onactivate\", 'l', \"\", opcodeOnActivate, opcodeOnActivateExplicit);\n            extensions.registerInstruction (\"activate\", \"x\", opcodeActivate, opcodeActivateExplicit);\n            extensions.registerInstruction (\"lock\", \"/l\", opcodeLock, opcodeLockExplicit);\n            extensions.registerInstruction (\"unlock\", \"\", opcodeUnlock, opcodeUnlockExplicit);\n            extensions.registerInstruction (\"cast\", \"SS\", opcodeCast, opcodeCastExplicit);\n            extensions.registerInstruction (\"explodespell\", \"S\", opcodeExplodeSpell, opcodeExplodeSpellExplicit);\n            extensions.registerInstruction (\"togglecollisionboxes\", \"\", opcodeToggleCollisionBoxes);\n            extensions.registerInstruction (\"togglecollisiongrid\", \"\", opcodeToggleCollisionDebug);\n            extensions.registerInstruction (\"tcb\", \"\", opcodeToggleCollisionBoxes);\n            extensions.registerInstruction (\"tcg\", \"\", opcodeToggleCollisionDebug);\n            extensions.registerInstruction (\"twf\", \"\", opcodeToggleWireframe);\n            extensions.registerInstruction (\"togglewireframe\", \"\", opcodeToggleWireframe);\n            extensions.registerInstruction (\"fadein\", \"f\", opcodeFadeIn);\n            extensions.registerInstruction (\"fadeout\", \"f\", opcodeFadeOut);\n            extensions.registerInstruction (\"fadeto\", \"ff\", opcodeFadeTo);\n            extensions.registerInstruction (\"togglewater\", \"\", opcodeToggleWater);\n            extensions.registerInstruction (\"twa\", \"\", opcodeToggleWater);\n            extensions.registerInstruction (\"toggleworld\", \"\", opcodeToggleWorld);\n            extensions.registerInstruction (\"tw\", \"\", opcodeToggleWorld);\n            extensions.registerInstruction (\"togglepathgrid\", \"\", opcodeTogglePathgrid);\n            extensions.registerInstruction (\"tpg\", \"\", opcodeTogglePathgrid);\n            extensions.registerInstruction (\"dontsaveobject\", \"\", opcodeDontSaveObject);\n            extensions.registerInstruction (\"pcforce1stperson\", \"\", opcodePcForce1stPerson);\n            extensions.registerInstruction (\"pcforce3rdperson\", \"\", opcodePcForce3rdPerson);\n            extensions.registerFunction (\"pcget3rdperson\", 'l', \"\", opcodePcGet3rdPerson);\n            extensions.registerInstruction (\"togglevanitymode\", \"\", opcodeToggleVanityMode);\n            extensions.registerInstruction (\"tvm\", \"\", opcodeToggleVanityMode);\n            extensions.registerFunction (\"getpcsleep\", 'l', \"\", opcodeGetPcSleep);\n            extensions.registerFunction (\"getpcjumping\", 'l', \"\", opcodeGetPcJumping);\n            extensions.registerInstruction (\"wakeuppc\", \"\", opcodeWakeUpPc);\n            extensions.registerInstruction (\"playbink\", \"Sl\", opcodePlayBink);\n            extensions.registerInstruction (\"payfine\", \"\", opcodePayFine);\n            extensions.registerInstruction (\"payfinethief\", \"\", opcodePayFineThief);\n            extensions.registerInstruction (\"gotojail\", \"\", opcodeGoToJail);\n            extensions.registerFunction (\"getlocked\", 'l', \"\", opcodeGetLocked, opcodeGetLockedExplicit);\n            extensions.registerFunction (\"geteffect\", 'l', \"S\", opcodeGetEffect, opcodeGetEffectExplicit);\n            extensions.registerInstruction (\"addsoulgem\", \"ccX\", opcodeAddSoulGem, opcodeAddSoulGemExplicit);\n            extensions.registerInstruction (\"removesoulgem\", \"c/l\", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit);\n            extensions.registerInstruction (\"drop\", \"cl\", opcodeDrop, opcodeDropExplicit);\n            extensions.registerInstruction (\"dropsoulgem\", \"c\", opcodeDropSoulGem, opcodeDropSoulGemExplicit);\n            extensions.registerFunction (\"getattacked\", 'l', \"\", opcodeGetAttacked, opcodeGetAttackedExplicit);\n            extensions.registerFunction (\"getweapondrawn\", 'l', \"\", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit);\n            extensions.registerFunction (\"getspellreadied\", 'l', \"\", opcodeGetSpellReadied, opcodeGetSpellReadiedExplicit);\n            extensions.registerFunction (\"getspelleffects\", 'l', \"c\", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit);\n            extensions.registerFunction (\"getcurrenttime\", 'f', \"\", opcodeGetCurrentTime);\n            extensions.registerInstruction (\"setdelete\", \"l\", opcodeSetDelete, opcodeSetDeleteExplicit);\n            extensions.registerFunction (\"getsquareroot\", 'f', \"f\", opcodeGetSquareRoot);\n            extensions.registerInstruction (\"fall\", \"\", opcodeFall, opcodeFallExplicit);\n            extensions.registerFunction (\"getstandingpc\", 'l', \"\", opcodeGetStandingPc, opcodeGetStandingPcExplicit);\n            extensions.registerFunction (\"getstandingactor\", 'l', \"\", opcodeGetStandingActor, opcodeGetStandingActorExplicit);\n            extensions.registerFunction (\"getcollidingpc\", 'l', \"\", opcodeGetCollidingPc, opcodeGetCollidingPcExplicit);\n            extensions.registerFunction (\"getcollidingactor\", 'l', \"\", opcodeGetCollidingActor, opcodeGetCollidingActorExplicit);\n            extensions.registerInstruction (\"hurtstandingactor\", \"f\", opcodeHurtStandingActor, opcodeHurtStandingActorExplicit);\n            extensions.registerInstruction (\"hurtcollidingactor\", \"f\", opcodeHurtCollidingActor, opcodeHurtCollidingActorExplicit);\n            extensions.registerFunction (\"getwindspeed\", 'f', \"\", opcodeGetWindSpeed);\n            extensions.registerFunction (\"hitonme\", 'l', \"S\", opcodeHitOnMe, opcodeHitOnMeExplicit);\n            extensions.registerFunction (\"hitattemptonme\", 'l', \"S\", opcodeHitAttemptOnMe, opcodeHitAttemptOnMeExplicit);\n            extensions.registerInstruction (\"disableteleporting\", \"\", opcodeDisableTeleporting);\n            extensions.registerInstruction (\"enableteleporting\", \"\", opcodeEnableTeleporting);\n            extensions.registerInstruction (\"showvars\", \"\", opcodeShowVars, opcodeShowVarsExplicit);\n            extensions.registerInstruction (\"show\", \"c\", opcodeShow, opcodeShowExplicit);\n            extensions.registerInstruction (\"sv\", \"\", opcodeShowVars, opcodeShowVarsExplicit);\n            extensions.registerInstruction(\"tgm\", \"\", opcodeToggleGodMode);\n            extensions.registerInstruction(\"togglegodmode\", \"\", opcodeToggleGodMode);\n            extensions.registerInstruction(\"togglescripts\", \"\", opcodeToggleScripts);\n            extensions.registerInstruction (\"disablelevitation\", \"\", opcodeDisableLevitation);\n            extensions.registerInstruction (\"enablelevitation\", \"\", opcodeEnableLevitation);\n            extensions.registerFunction (\"getpcinjail\", 'l', \"\", opcodeGetPcInJail);\n            extensions.registerFunction (\"getpctraveling\", 'l', \"\", opcodeGetPcTraveling);\n            extensions.registerInstruction (\"betacomment\", \"/S\", opcodeBetaComment, opcodeBetaCommentExplicit);\n            extensions.registerInstruction (\"bc\", \"/S\", opcodeBetaComment, opcodeBetaCommentExplicit);\n            extensions.registerInstruction (\"ori\", \"/S\", opcodeBetaComment, opcodeBetaCommentExplicit); // 'ori' stands for 'ObjectReferenceInfo'\n            extensions.registerInstruction (\"showscenegraph\", \"/l\", opcodeShowSceneGraph, opcodeShowSceneGraphExplicit);\n            extensions.registerInstruction (\"ssg\", \"/l\", opcodeShowSceneGraph, opcodeShowSceneGraphExplicit);\n            extensions.registerInstruction (\"addtolevcreature\", \"ccl\", opcodeAddToLevCreature);\n            extensions.registerInstruction (\"removefromlevcreature\", \"ccl\", opcodeRemoveFromLevCreature);\n            extensions.registerInstruction (\"addtolevitem\", \"ccl\", opcodeAddToLevItem);\n            extensions.registerInstruction (\"removefromlevitem\", \"ccl\", opcodeRemoveFromLevItem);\n            extensions.registerInstruction (\"tb\", \"\", opcodeToggleBorders);\n            extensions.registerInstruction (\"toggleborders\", \"\", opcodeToggleBorders);\n            extensions.registerInstruction (\"togglenavmesh\", \"\", opcodeToggleNavMesh);\n            extensions.registerInstruction (\"tap\", \"\", opcodeToggleActorsPaths);\n            extensions.registerInstruction (\"toggleactorspaths\", \"\", opcodeToggleActorsPaths);\n            extensions.registerInstruction (\"setnavmeshnumber\", \"l\", opcodeSetNavMeshNumberToRender);\n            extensions.registerFunction (\"repairedonme\", 'l', \"S\", opcodeRepairedOnMe, opcodeRepairedOnMeExplicit);\n            extensions.registerInstruction (\"togglerecastmesh\", \"\", opcodeToggleRecastMesh);\n        }\n    }\n\n    namespace Sky\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerInstruction (\"togglesky\", \"\", opcodeToggleSky);\n            extensions.registerInstruction (\"ts\", \"\", opcodeToggleSky);\n            extensions.registerInstruction (\"turnmoonwhite\", \"\", opcodeTurnMoonWhite);\n            extensions.registerInstruction (\"turnmoonred\", \"\", opcodeTurnMoonRed);\n            extensions.registerInstruction (\"changeweather\", \"Sl\", opcodeChangeWeather);\n            extensions.registerFunction (\"getmasserphase\", 'l', \"\", opcodeGetMasserPhase);\n            extensions.registerFunction (\"getsecundaphase\", 'l', \"\", opcodeGetSecundaPhase);\n            extensions.registerFunction (\"getcurrentweather\", 'l', \"\", opcodeGetCurrentWeather);\n            extensions.registerInstruction (\"modregion\", \"S/llllllllllX\", opcodeModRegion);\n        }\n    }\n\n    namespace Sound\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerInstruction (\"say\", \"SS\", opcodeSay, opcodeSayExplicit);\n            extensions.registerFunction (\"saydone\", 'l', \"\", opcodeSayDone, opcodeSayDoneExplicit);\n            extensions.registerInstruction (\"streammusic\", \"S\", opcodeStreamMusic);\n            extensions.registerInstruction (\"playsound\", \"cXX\", opcodePlaySound);\n            extensions.registerInstruction (\"playsoundvp\", \"cff\", opcodePlaySoundVP);\n            extensions.registerInstruction (\"playsound3d\", \"cXX\", opcodePlaySound3D,\n                opcodePlaySound3DExplicit);\n            extensions.registerInstruction (\"playsound3dvp\", \"cff\", opcodePlaySound3DVP,\n                opcodePlaySound3DVPExplicit);\n            extensions.registerInstruction (\"playloopsound3d\", \"cXX\", opcodePlayLoopSound3D,\n                opcodePlayLoopSound3DExplicit);\n            extensions.registerInstruction (\"playloopsound3dvp\", \"cff\", opcodePlayLoopSound3DVP,\n                opcodePlayLoopSound3DVPExplicit);\n            extensions.registerInstruction (\"stopsound\", \"cXX\", opcodeStopSound,\n                opcodeStopSoundExplicit);\n            extensions.registerFunction (\"getsoundplaying\", 'l', \"c\", opcodeGetSoundPlaying,\n                opcodeGetSoundPlayingExplicit);\n        }\n    }\n\n    namespace Stats\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            static const char *attributes[numberOfAttributes] =\n            {\n                \"strength\", \"intelligence\", \"willpower\", \"agility\", \"speed\", \"endurance\",\n                \"personality\", \"luck\"\n            };\n\n            static const char *dynamics[numberOfDynamics] =\n            {\n                \"health\", \"magicka\", \"fatigue\"\n            };\n\n            static const char *skills[numberOfSkills] =\n            {\n                \"block\", \"armorer\", \"mediumarmor\", \"heavyarmor\", \"bluntweapon\",\n                \"longblade\", \"axe\", \"spear\", \"athletics\", \"enchant\", \"destruction\",\n                \"alteration\", \"illusion\", \"conjuration\", \"mysticism\",\n                \"restoration\", \"alchemy\", \"unarmored\", \"security\", \"sneak\",\n                \"acrobatics\", \"lightarmor\", \"shortblade\", \"marksman\",\n                \"mercantile\", \"speechcraft\", \"handtohand\"\n            };\n\n            static const char *magicEffects[numberOfMagicEffects] =\n            {\n                \"resistmagicka\", \"resistfire\", \"resistfrost\", \"resistshock\",\n                \"resistdisease\", \"resistblight\", \"resistcorprus\", \"resistpoison\",\n                \"resistparalysis\", \"resistnormalweapons\", \"waterbreathing\", \"chameleon\",\n                \"waterwalking\", \"swimspeed\", \"superjump\", \"flying\",\n                \"armorbonus\", \"castpenalty\", \"silence\", \"blindness\",\n                \"paralysis\", \"invisible\", \"attackbonus\", \"defendbonus\"\n            };\n\n            std::string get (\"get\");\n            std::string set (\"set\");\n            std::string mod (\"mod\");\n            std::string modCurrent (\"modcurrent\");\n            std::string getRatio (\"getratio\");\n\n            for (int i=0; i<numberOfAttributes; ++i)\n            {\n                extensions.registerFunction (get + attributes[i], 'f', \"\",\n                    opcodeGetAttribute+i, opcodeGetAttributeExplicit+i);\n\n                extensions.registerInstruction (set + attributes[i], \"f\",\n                    opcodeSetAttribute+i, opcodeSetAttributeExplicit+i);\n\n                extensions.registerInstruction (mod + attributes[i], \"f\",\n                    opcodeModAttribute+i, opcodeModAttributeExplicit+i);\n            }\n\n            for (int i=0; i<numberOfDynamics; ++i)\n            {\n                extensions.registerFunction (get + dynamics[i], 'f', \"x\",\n                    opcodeGetDynamic+i, opcodeGetDynamicExplicit+i);\n\n                extensions.registerInstruction (set + dynamics[i], \"f\",\n                    opcodeSetDynamic+i, opcodeSetDynamicExplicit+i);\n\n                extensions.registerInstruction (mod + dynamics[i], \"f\",\n                    opcodeModDynamic+i, opcodeModDynamicExplicit+i);\n\n                extensions.registerInstruction (modCurrent + dynamics[i], \"f\",\n                    opcodeModCurrentDynamic+i, opcodeModCurrentDynamicExplicit+i);\n\n                extensions.registerFunction (get + dynamics[i] + getRatio, 'f', \"\",\n                    opcodeGetDynamicGetRatio+i, opcodeGetDynamicGetRatioExplicit+i);\n            }\n\n            for (int i=0; i<numberOfSkills; ++i)\n            {\n                extensions.registerFunction (get + skills[i], 'f', \"\",\n                    opcodeGetSkill+i, opcodeGetSkillExplicit+i);\n\n                extensions.registerInstruction (set + skills[i], \"f\",\n                    opcodeSetSkill+i, opcodeSetSkillExplicit+i);\n\n                extensions.registerInstruction (mod + skills[i], \"f\",\n                    opcodeModSkill+i, opcodeModSkillExplicit+i);\n            }\n\n            for (int i=0; i<numberOfMagicEffects; ++i)\n            {\n                extensions.registerFunction (get + magicEffects[i], 'l', \"\",\n                    opcodeGetMagicEffect+i, opcodeGetMagicEffectExplicit+i);\n\n                extensions.registerInstruction (set + magicEffects[i], \"l\",\n                    opcodeSetMagicEffect+i, opcodeSetMagicEffectExplicit+i);\n\n                extensions.registerInstruction(mod + magicEffects[i], \"l\",\n                    opcodeModMagicEffect+i, opcodeModMagicEffectExplicit+i);\n            }\n\n            extensions.registerFunction (\"getpccrimelevel\", 'f', \"\", opcodeGetPCCrimeLevel);\n            extensions.registerInstruction (\"setpccrimelevel\", \"f\", opcodeSetPCCrimeLevel);\n            extensions.registerInstruction (\"modpccrimelevel\", \"f\", opcodeModPCCrimeLevel);\n\n            extensions.registerInstruction (\"addspell\", \"cz\", opcodeAddSpell, opcodeAddSpellExplicit);\n            extensions.registerInstruction (\"removespell\", \"cz\", opcodeRemoveSpell,\n                opcodeRemoveSpellExplicit);\n            extensions.registerInstruction (\"removespelleffects\", \"c\", opcodeRemoveSpellEffects,\n                opcodeRemoveSpellEffectsExplicit);\n            extensions.registerInstruction (\"removeeffects\", \"l\", opcodeRemoveEffects,\n                opcodeRemoveEffectsExplicit);\n            extensions.registerInstruction (\"resurrect\", \"\", opcodeResurrect,\n                opcodeResurrectExplicit);\n            extensions.registerFunction (\"getspell\", 'l', \"c\", opcodeGetSpell, opcodeGetSpellExplicit);\n\n            extensions.registerInstruction(\"pcraiserank\",\"/S\",opcodePCRaiseRank, opcodePCRaiseRankExplicit);\n            extensions.registerInstruction(\"pclowerrank\",\"/S\",opcodePCLowerRank, opcodePCLowerRankExplicit);\n            extensions.registerInstruction(\"pcjoinfaction\",\"/S\",opcodePCJoinFaction, opcodePCJoinFactionExplicit);\n            extensions.registerInstruction (\"moddisposition\",\"l\",opcodeModDisposition,\n                opcodeModDispositionExplicit);\n            extensions.registerInstruction (\"setdisposition\",\"l\",opcodeSetDisposition,\n                opcodeSetDispositionExplicit);\n            extensions.registerFunction (\"getdisposition\",'l', \"\",opcodeGetDisposition,\n                opcodeGetDispositionExplicit);\n            extensions.registerFunction(\"getpcrank\",'l',\"/S\",opcodeGetPCRank,opcodeGetPCRankExplicit);\n\n            extensions.registerInstruction(\"setlevel\", \"l\", opcodeSetLevel, opcodeSetLevelExplicit);\n            extensions.registerFunction(\"getlevel\", 'l', \"\", opcodeGetLevel, opcodeGetLevelExplicit);\n\n            extensions.registerFunction(\"getstat\", 'l', \"c\", opcodeGetStat, opcodeGetStatExplicit);\n\n            extensions.registerFunction (\"getdeadcount\", 'l', \"c\", opcodeGetDeadCount);\n\n            extensions.registerFunction (\"getpcfacrep\", 'l', \"/c\", opcodeGetPCFacRep, opcodeGetPCFacRepExplicit);\n            extensions.registerInstruction (\"setpcfacrep\", \"l/c\", opcodeSetPCFacRep, opcodeSetPCFacRepExplicit);\n            extensions.registerInstruction (\"modpcfacrep\", \"l/c\", opcodeModPCFacRep, opcodeModPCFacRepExplicit);\n\n            extensions.registerFunction (\"getcommondisease\", 'l', \"\", opcodeGetCommonDisease,\n                opcodeGetCommonDiseaseExplicit);\n            extensions.registerFunction (\"getblightdisease\", 'l', \"\", opcodeGetBlightDisease,\n                opcodeGetBlightDiseaseExplicit);\n\n            extensions.registerFunction (\"getrace\", 'l', \"c\", opcodeGetRace,\n                opcodeGetRaceExplicit);\n            extensions.registerFunction (\"getwerewolfkills\", 'l', \"\", opcodeGetWerewolfKills);\n            extensions.registerFunction (\"pcexpelled\", 'l', \"/S\", opcodePcExpelled, opcodePcExpelledExplicit);\n            extensions.registerInstruction (\"pcexpell\", \"/S\", opcodePcExpell, opcodePcExpellExplicit);\n            extensions.registerInstruction (\"pcclearexpelled\", \"/S\", opcodePcClearExpelled, opcodePcClearExpelledExplicit);\n            extensions.registerInstruction (\"raiserank\", \"x\", opcodeRaiseRank, opcodeRaiseRankExplicit);\n            extensions.registerInstruction (\"lowerrank\", \"x\", opcodeLowerRank, opcodeLowerRankExplicit);\n\n            extensions.registerFunction (\"ondeath\", 'l', \"\", opcodeOnDeath, opcodeOnDeathExplicit);\n            extensions.registerFunction (\"onmurder\", 'l', \"\", opcodeOnMurder, opcodeOnMurderExplicit);\n            extensions.registerFunction (\"onknockout\", 'l', \"\", opcodeOnKnockout, opcodeOnKnockoutExplicit);\n\n            extensions.registerFunction (\"iswerewolf\", 'l', \"\", opcodeIsWerewolf, opcodeIsWerewolfExplicit);\n\n            extensions.registerInstruction(\"becomewerewolf\", \"\", opcodeBecomeWerewolf, opcodeBecomeWerewolfExplicit);\n            extensions.registerInstruction(\"undowerewolf\", \"\", opcodeUndoWerewolf, opcodeUndoWerewolfExplicit);\n            extensions.registerInstruction(\"setwerewolfacrobatics\", \"\", opcodeSetWerewolfAcrobatics, opcodeSetWerewolfAcrobaticsExplicit);\n        }\n    }\n\n    namespace Transformation\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerFunction(\"getdistance\",'f',\"c\",opcodeGetDistance,opcodeGetDistanceExplicit);\n            extensions.registerInstruction(\"setscale\",\"f\",opcodeSetScale,opcodeSetScaleExplicit);\n            extensions.registerFunction(\"getscale\",'f',\"\",opcodeGetScale,opcodeGetScaleExplicit);\n            extensions.registerInstruction(\"setangle\",\"cf\",opcodeSetAngle,opcodeSetAngleExplicit);\n            extensions.registerFunction(\"getangle\",'f',\"c\",opcodeGetAngle,opcodeGetAngleExplicit);\n            extensions.registerInstruction(\"setpos\",\"cf\",opcodeSetPos,opcodeSetPosExplicit);\n            extensions.registerFunction(\"getpos\",'f',\"c\",opcodeGetPos,opcodeGetPosExplicit);\n            extensions.registerFunction(\"getstartingpos\",'f',\"c\",opcodeGetStartingPos,opcodeGetStartingPosExplicit);\n            extensions.registerInstruction(\"position\",\"ffffz\",opcodePosition,opcodePositionExplicit);\n            extensions.registerInstruction(\"positioncell\",\"ffffcX\",opcodePositionCell,opcodePositionCellExplicit);\n            extensions.registerInstruction(\"placeitemcell\",\"ccffffX\",opcodePlaceItemCell);\n            extensions.registerInstruction(\"placeitem\",\"cffffX\",opcodePlaceItem);\n            extensions.registerInstruction(\"placeatpc\",\"clflX\",opcodePlaceAtPc);\n            extensions.registerInstruction(\"placeatme\",\"clflX\",opcodePlaceAtMe,opcodePlaceAtMeExplicit);\n            extensions.registerInstruction(\"modscale\",\"f\",opcodeModScale,opcodeModScaleExplicit);\n            extensions.registerInstruction(\"rotate\",\"cf\",opcodeRotate,opcodeRotateExplicit);\n            extensions.registerInstruction(\"rotateworld\",\"cf\",opcodeRotateWorld,opcodeRotateWorldExplicit);\n            extensions.registerInstruction(\"setatstart\",\"\",opcodeSetAtStart,opcodeSetAtStartExplicit);\n            extensions.registerInstruction(\"move\",\"cf\",opcodeMove,opcodeMoveExplicit);\n            extensions.registerInstruction(\"moveworld\",\"cf\",opcodeMoveWorld,opcodeMoveWorldExplicit);\n            extensions.registerFunction(\"getstartingangle\",'f',\"c\",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit);\n            extensions.registerInstruction(\"resetactors\",\"\",opcodeResetActors);\n            extensions.registerInstruction(\"fixme\",\"\",opcodeFixme);\n            extensions.registerInstruction(\"ra\",\"\",opcodeResetActors);\n        }\n    }\n\n    namespace User\n    {\n        void registerExtensions (Extensions& extensions)\n        {\n            extensions.registerInstruction (\"user1\", \"\", opcodeUser1);\n            extensions.registerInstruction (\"user2\", \"\", opcodeUser2);\n            extensions.registerInstruction (\"user3\", \"\", opcodeUser3, opcodeUser3);\n            extensions.registerInstruction (\"user4\", \"\", opcodeUser4, opcodeUser4);\n        }\n    }\n}\n"
  },
  {
    "path": "components/compiler/extensions0.hpp",
    "content": "#ifndef COMPILER_EXTENSIONS0_H\n#define COMPILER_EXTENSIONS0_H\n\nnamespace Compiler\n{\n    class Extensions;\n\n    void registerExtensions (Extensions& extensions, bool consoleOnly = false);\n\n    namespace Ai\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Animation\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Cell\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Console\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Container\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Control\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Dialogue\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Gui\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Misc\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Sky\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Sound\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Stats\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace Transformation\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n\n    namespace User\n    {\n        void registerExtensions (Extensions& extensions);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/fileparser.cpp",
    "content": "#include \"fileparser.hpp\"\n\n#include \"tokenloc.hpp\"\n#include \"scanner.hpp\"\n\nnamespace Compiler\n{\n    FileParser::FileParser (ErrorHandler& errorHandler, Context& context)\n    : Parser (errorHandler, context),\n      mScriptParser (errorHandler, context, mLocals, true),\n      mState (BeginState)\n    {}\n\n    std::string FileParser::getName() const\n    {\n        return mName;\n    }\n\n    void FileParser::getCode (std::vector<Interpreter::Type_Code>& code) const\n    {\n        mScriptParser.getCode (code);\n    }\n\n    const Locals& FileParser::getLocals() const\n    {\n        return mLocals;\n    }\n\n    bool FileParser::parseName (const std::string& name, const TokenLoc& loc,\n        Scanner& scanner)\n    {\n        if (mState==NameState)\n        {\n            mName = name;\n            mState = BeginCompleteState;\n            return true;\n        }\n\n        if (mState==EndNameState)\n        {\n            // optional repeated name after end statement\n            if (mName!=name)\n                reportWarning (\"Names for script \" + mName + \" do not match\", loc);\n\n            mState = EndCompleteState;\n            return false; // we are stopping here, because there might be more garbage on the end line,\n                          // that we must ignore.\n                          //\n                          /// \\todo allow this workaround to be disabled for newer scripts\n        }\n\n        if (mState==BeginCompleteState)\n        {\n            reportWarning (\"Stray string (\" + name + \") after begin statement\", loc);\n            return true;\n        }\n\n        return Parser::parseName (name, loc, scanner);\n    }\n\n    bool FileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mState==BeginState && keyword==Scanner::K_begin)\n        {\n            mState = NameState;\n            scanner.enableTolerantNames(); /// \\todo disable\n            return true;\n        }\n\n        if (mState==NameState)\n        {\n            // keywords can be used as script names too. Thank you Morrowind for another\n            // syntactic perversity :(\n            mName = loc.mLiteral;\n            mState = BeginCompleteState;\n            return true;\n        }\n\n        if (mState==EndNameState)\n        {\n            // optional repeated name after end statement\n            if (mName!=loc.mLiteral)\n                reportWarning (\"Names for script \" + mName + \" do not match\", loc);\n\n            mState = EndCompleteState;\n            return false; // we are stopping here, because there might be more garbage on the end line,\n                          // that we must ignore.\n                          //\n                          /// \\todo allow this workaround to be disabled for newer scripts\n        }\n\n        return Parser::parseKeyword (keyword, loc, scanner);\n    }\n\n    bool FileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        // Ignore any junk special characters\n        if (mState == BeginState)\n        {\n            if (code != Scanner::S_newline)\n                reportWarning (\"Stray special character before begin statement\", loc);\n            return true;\n        }\n\n        if (code==Scanner::S_newline)\n        {\n            if (mState==BeginCompleteState)\n            {\n                // parse the script body\n                mScriptParser.reset();\n\n                scanner.scan (mScriptParser);\n\n                mState = EndNameState;\n                return true;\n            }\n\n            if (mState==EndCompleteState || mState==EndNameState)\n            {\n                // we are done here -> ignore the rest of the script\n                return false;\n            }\n        }\n        else if (code==Scanner::S_comma && (mState==NameState || mState==EndNameState))\n        {\n            // ignoring comma (for now)\n            return true;\n        }\n\n        return Parser::parseSpecial (code, loc, scanner);\n    }\n\n    void FileParser::parseEOF (Scanner& scanner)\n    {\n        if (mState!=EndNameState && mState!=EndCompleteState)\n            Parser::parseEOF (scanner);\n    }\n\n    void FileParser::reset()\n    {\n        mState = BeginState;\n        mName.clear();\n        mScriptParser.reset();\n        Parser::reset();\n    }\n}\n"
  },
  {
    "path": "components/compiler/fileparser.hpp",
    "content": "#ifndef COMPILER_FILEPARSER_H_INCLUDED\n#define COMPILER_FILEPARSER_H_INCLUDED\n\n#include \"parser.hpp\"\n#include \"scriptparser.hpp\"\n#include \"locals.hpp\"\n#include \"literals.hpp\"\n\nnamespace Compiler\n{\n    // Top-level parser, to be used for global scripts, local scripts and targeted scripts\n    \n    class FileParser : public Parser\n    {\n            enum State\n            {\n                BeginState, NameState, BeginCompleteState, EndNameState,\n                EndCompleteState\n            };\n            \n            ScriptParser mScriptParser;\n            State mState;\n            std::string mName;\n            Locals mLocals;\n\n        public:\n        \n            FileParser (ErrorHandler& errorHandler, Context& context);\n\n            std::string getName() const;\n            ///< Return script name.\n\n            void getCode (std::vector<Interpreter::Type_Code>& code) const;\n            ///< store generated code in \\a code.\n            \n            const Locals& getLocals() const;\n            ///< get local variable declarations.\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void parseEOF (Scanner& scanner) override;\n            ///< Handle EOF token.    \n            \n            void reset() override;\n            ///< Reset parser to clean state.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/generator.cpp",
    "content": "#include \"generator.hpp\"\n\n#include <cassert>\n#include <algorithm>\n#include <iterator>\n#include <stdexcept>\n\n#include \"literals.hpp\"\n\nnamespace\n{\n    void opPushInt (Compiler::Generator::CodeContainer& code, int value)\n    {\n        code.push_back (Compiler::Generator::segment0 (0, value));\n    }\n\n    void opFetchIntLiteral (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (4));\n    }\n\n    void opFetchFloatLiteral (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (5));\n    }\n\n    void opIntToFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (3));\n    }\n\n    void opFloatToInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (6));\n    }\n\n    void opStoreLocalShort (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (0));\n    }\n\n    void opStoreLocalLong (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (1));\n    }\n\n    void opStoreLocalFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (2));\n    }\n\n    void opNegateInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (7));\n    }\n\n    void opNegateFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (8));\n    }\n\n    void opAddInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (9));\n    }\n\n    void opAddFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (10));\n    }\n\n    void opSubInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (11));\n    }\n\n    void opSubFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (12));\n    }\n\n    void opMulInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (13));\n    }\n\n    void opMulFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (14));\n    }\n\n    void opDivInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (15));\n    }\n\n    void opDivFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (16));\n    }\n\n    void opIntToFloat1 (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (17));\n    }\n\n    void opSquareRoot (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (19));\n    }\n\n    void opReturn (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (20));\n    }\n\n    void opMessageBox (Compiler::Generator::CodeContainer& code, int buttons)\n    {\n        code.push_back (Compiler::Generator::segment3 (0, buttons));\n    }\n\n    void opReport (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (58));\n    }\n\n    void opFetchLocalShort (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (21));\n    }\n\n    void opFetchLocalLong (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (22));\n    }\n\n    void opFetchLocalFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (23));\n    }\n\n    void opJumpForward (Compiler::Generator::CodeContainer& code, int offset)\n    {\n        code.push_back (Compiler::Generator::segment0 (1, offset));\n    }\n\n    void opJumpBackward (Compiler::Generator::CodeContainer& code, int offset)\n    {\n        code.push_back (Compiler::Generator::segment0 (2, offset));\n    }\n\n    /*\n    Currently unused\n    void opSkipOnZero (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (24));\n    }\n    */\n\n    void opSkipOnNonZero (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (25));\n    }\n\n    void opEqualInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (26));\n    }\n\n    void opNonEqualInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (27));\n    }\n\n    void opLessThanInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (28));\n    }\n\n    void opLessOrEqualInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (29));\n    }\n\n    void opGreaterThanInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (30));\n    }\n\n    void opGreaterOrEqualInt (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (31));\n    }\n\n    void opEqualFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (32));\n    }\n\n    void opNonEqualFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (33));\n    }\n\n    void opLessThanFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (34));\n    }\n\n    void opLessOrEqualFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (35));\n    }\n\n    void opGreaterThanFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (36));\n    }\n\n    void opGreaterOrEqualFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (37));\n    }\n\n    void opStoreGlobalShort (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (39));\n    }\n\n    void opStoreGlobalLong (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (40));\n    }\n\n    void opStoreGlobalFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (41));\n    }\n\n    void opFetchGlobalShort (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (42));\n    }\n\n    void opFetchGlobalLong (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (43));\n    }\n\n    void opFetchGlobalFloat (Compiler::Generator::CodeContainer& code)\n    {\n        code.push_back (Compiler::Generator::segment5 (44));\n    }\n\n    void opStoreMemberShort (Compiler::Generator::CodeContainer& code, bool global)\n    {\n        code.push_back (Compiler::Generator::segment5 (global ? 65 : 59));\n    }\n\n    void opStoreMemberLong (Compiler::Generator::CodeContainer& code, bool global)\n    {\n        code.push_back (Compiler::Generator::segment5 (global ? 66 : 60));\n    }\n\n    void opStoreMemberFloat (Compiler::Generator::CodeContainer& code, bool global)\n    {\n        code.push_back (Compiler::Generator::segment5 (global ? 67 : 61));\n    }\n\n    void opFetchMemberShort (Compiler::Generator::CodeContainer& code, bool global)\n    {\n        code.push_back (Compiler::Generator::segment5 (global ? 68 : 62));\n    }\n\n    void opFetchMemberLong (Compiler::Generator::CodeContainer& code, bool global)\n    {\n        code.push_back (Compiler::Generator::segment5 (global ? 69 : 63));\n    }\n\n    void opFetchMemberFloat (Compiler::Generator::CodeContainer& code, bool global)\n    {\n        code.push_back (Compiler::Generator::segment5 (global ? 70 : 64));\n    }\n}\n\nnamespace Compiler::Generator\n    {\n        void pushInt (CodeContainer& code, Literals& literals, int value)\n        {\n            int index = literals.addInteger (value);\n            opPushInt (code, index);\n            opFetchIntLiteral (code);\n        }\n\n        void pushFloat (CodeContainer& code, Literals& literals, float value)\n        {\n            int index = literals.addFloat (value);\n            opPushInt (code, index);\n            opFetchFloatLiteral (code);\n        }\n\n        void pushString (CodeContainer& code, Literals& literals, const std::string& value)\n        {\n            int index = literals.addString (value);\n            opPushInt (code, index);\n        }\n\n        void assignToLocal (CodeContainer& code, char localType,\n            int localIndex, const CodeContainer& value, char valueType)\n        {\n            opPushInt (code, localIndex);\n\n            std::copy (value.begin(), value.end(), std::back_inserter (code));\n\n            if (localType!=valueType)\n            {\n                if (localType=='f' && valueType=='l')\n                {\n                    opIntToFloat (code);\n                }\n                else if ((localType=='l' || localType=='s') && valueType=='f')\n                {\n                    opFloatToInt (code);\n                }\n            }\n\n            switch (localType)\n            {\n                case 'f':\n\n                    opStoreLocalFloat (code);\n                    break;\n\n                case 's':\n\n                    opStoreLocalShort (code);\n                    break;\n\n                case 'l':\n\n                    opStoreLocalLong (code);\n                    break;\n\n                default:\n\n                    assert (0);\n            }\n        }\n\n        void negate (CodeContainer& code, char valueType)\n        {\n            switch (valueType)\n            {\n                case 'l':\n\n                    opNegateInt (code);\n                    break;\n\n                case 'f':\n\n                    opNegateFloat (code);\n                    break;\n\n                default:\n\n                    assert (0);\n            }\n        }\n\n        void add (CodeContainer& code, char valueType1, char valueType2)\n        {\n            if (valueType1=='l' && valueType2=='l')\n            {\n                opAddInt (code);\n            }\n            else\n            {\n                if (valueType1=='l')\n                    opIntToFloat1 (code);\n\n                if (valueType2=='l')\n                    opIntToFloat (code);\n\n                opAddFloat (code);\n            }\n        }\n\n        void sub (CodeContainer& code, char valueType1, char valueType2)\n        {\n            if (valueType1=='l' && valueType2=='l')\n            {\n                opSubInt (code);\n            }\n            else\n            {\n                if (valueType1=='l')\n                    opIntToFloat1 (code);\n\n                if (valueType2=='l')\n                    opIntToFloat (code);\n\n                opSubFloat (code);\n            }\n        }\n\n        void mul (CodeContainer& code, char valueType1, char valueType2)\n        {\n            if (valueType1=='l' && valueType2=='l')\n            {\n                opMulInt (code);\n            }\n            else\n            {\n                if (valueType1=='l')\n                    opIntToFloat1 (code);\n\n                if (valueType2=='l')\n                    opIntToFloat (code);\n\n                opMulFloat (code);\n            }\n        }\n\n        void div (CodeContainer& code, char valueType1, char valueType2)\n        {\n            if (valueType1=='l' && valueType2=='l')\n            {\n                opDivInt (code);\n            }\n            else\n            {\n                if (valueType1=='l')\n                    opIntToFloat1 (code);\n\n                if (valueType2=='l')\n                    opIntToFloat (code);\n\n                opDivFloat (code);\n            }\n        }\n\n        void convert (CodeContainer& code, char fromType, char toType)\n        {\n            if (fromType!=toType)\n            {\n                if (fromType=='f' && toType=='l')\n                    opFloatToInt (code);\n                else if (fromType=='l' && toType=='f')\n                    opIntToFloat (code);\n                else\n                    throw std::logic_error (\"illegal type conversion\");\n            }\n        }\n\n        void squareRoot (CodeContainer& code)\n        {\n            opSquareRoot (code);\n        }\n\n        void exit (CodeContainer& code)\n        {\n            opReturn (code);\n        }\n\n        void message (CodeContainer& code, Literals& literals, const std::string& message,\n            int buttons)\n        {\n            assert (buttons>=0);\n\n            if (buttons>=256)\n                throw std::runtime_error (\"A message box can't have more than 255 buttons\");\n\n            int index = literals.addString (message);\n\n            opPushInt (code, index);\n            opMessageBox (code, buttons);\n        }\n\n        void report (CodeContainer& code, Literals& literals, const std::string& message)\n        {\n            int index = literals.addString (message);\n\n            opPushInt (code, index);\n            opReport (code);\n        }\n\n        void fetchLocal (CodeContainer& code, char localType, int localIndex)\n        {\n            opPushInt (code, localIndex);\n\n            switch (localType)\n            {\n                case 'f':\n\n                    opFetchLocalFloat (code);\n                    break;\n\n                case 's':\n\n                    opFetchLocalShort (code);\n                    break;\n\n                case 'l':\n\n                    opFetchLocalLong (code);\n                    break;\n\n                default:\n\n                    assert (0);\n            }\n        }\n\n        void jump (CodeContainer& code, int offset)\n        {\n            if (offset>0)\n                opJumpForward (code, offset);\n            else if (offset<0)\n                opJumpBackward (code, -offset);\n            else\n                throw std::logic_error (\"infinite loop\");\n        }\n\n        void jumpOnZero (CodeContainer& code, int offset)\n        {\n            opSkipOnNonZero (code);\n\n            if (offset<0)\n                --offset; // compensate for skip instruction\n\n            jump (code, offset);\n        }\n\n        void compare (CodeContainer& code, char op, char valueType1, char valueType2)\n        {\n            if (valueType1=='l' && valueType2=='l')\n            {\n                switch (op)\n                {\n                    case 'e': opEqualInt (code); break;\n                    case 'n': opNonEqualInt (code); break;\n                    case 'l': opLessThanInt (code); break;\n                    case 'L': opLessOrEqualInt (code); break;\n                    case 'g': opGreaterThanInt (code); break;\n                    case 'G': opGreaterOrEqualInt (code); break;\n\n                    default:\n\n                        assert (0);\n                }\n            }\n            else\n            {\n                if (valueType1=='l')\n                    opIntToFloat1 (code);\n\n                if (valueType2=='l')\n                    opIntToFloat (code);\n\n                switch (op)\n                {\n                    case 'e': opEqualFloat (code); break;\n                    case 'n': opNonEqualFloat (code); break;\n                    case 'l': opLessThanFloat (code); break;\n                    case 'L': opLessOrEqualFloat (code); break;\n                    case 'g': opGreaterThanFloat (code); break;\n                    case 'G': opGreaterOrEqualFloat (code); break;\n\n                    default:\n\n                        assert (0);\n                }\n            }\n        }\n\n        void assignToGlobal (CodeContainer& code, Literals& literals, char localType,\n            const std::string& name, const CodeContainer& value, char valueType)\n        {\n            int index = literals.addString (name);\n\n            opPushInt (code, index);\n\n            std::copy (value.begin(), value.end(), std::back_inserter (code));\n\n            if (localType!=valueType)\n            {\n                if (localType=='f' && (valueType=='l' || valueType=='s'))\n                {\n                    opIntToFloat (code);\n                }\n                else if ((localType=='l' || localType=='s') && valueType=='f')\n                {\n                    opFloatToInt (code);\n                }\n            }\n\n            switch (localType)\n            {\n                case 'f':\n\n                    opStoreGlobalFloat (code);\n                    break;\n\n                case 's':\n\n                    opStoreGlobalShort (code);\n                    break;\n\n                case 'l':\n\n                    opStoreGlobalLong (code);\n                    break;\n\n                default:\n\n                    assert (0);\n            }\n        }\n\n        void fetchGlobal (CodeContainer& code, Literals& literals, char localType,\n            const std::string& name)\n        {\n            int index = literals.addString (name);\n\n            opPushInt (code, index);\n\n            switch (localType)\n            {\n                case 'f':\n\n                    opFetchGlobalFloat (code);\n                    break;\n\n                case 's':\n\n                    opFetchGlobalShort (code);\n                    break;\n\n                case 'l':\n\n                    opFetchGlobalLong (code);\n                    break;\n\n                default:\n\n                    assert (0);\n            }\n        }\n\n        void assignToMember (CodeContainer& code, Literals& literals, char localType,\n            const std::string& name, const std::string& id, const CodeContainer& value,\n            char valueType, bool global)\n        {\n            int index = literals.addString (name);\n\n            opPushInt (code, index);\n\n            index = literals.addString (id);\n\n            opPushInt (code, index);\n\n            std::copy (value.begin(), value.end(), std::back_inserter (code));\n\n            if (localType!=valueType)\n            {\n                if (localType=='f' && (valueType=='l' || valueType=='s'))\n                {\n                    opIntToFloat (code);\n                }\n                else if ((localType=='l' || localType=='s') && valueType=='f')\n                {\n                    opFloatToInt (code);\n                }\n            }\n\n            switch (localType)\n            {\n                case 'f':\n\n                    opStoreMemberFloat (code, global);\n                    break;\n\n                case 's':\n\n                    opStoreMemberShort (code, global);\n                    break;\n\n                case 'l':\n\n                    opStoreMemberLong (code, global);\n                    break;\n\n                default:\n\n                    assert (0);\n            }\n        }\n\n        void fetchMember (CodeContainer& code, Literals& literals, char localType,\n            const std::string& name, const std::string& id, bool global)\n        {\n            int index = literals.addString (name);\n\n            opPushInt (code, index);\n\n            index = literals.addString (id);\n\n            opPushInt (code, index);\n\n            switch (localType)\n            {\n                case 'f':\n\n                    opFetchMemberFloat (code, global);\n                    break;\n\n                case 's':\n\n                    opFetchMemberShort (code, global);\n                    break;\n\n                case 'l':\n\n                    opFetchMemberLong (code, global);\n                    break;\n\n                default:\n\n                    assert (0);\n            }\n        }\n    }\n"
  },
  {
    "path": "components/compiler/generator.hpp",
    "content": "#ifndef COMPILER_GENERATOR_H_INCLUDED\n#define COMPILER_GENERATOR_H_INCLUDED\n\n#include <vector>\n#include <string>\n#include <cassert>\n\n#include <components/interpreter/types.hpp>\n\nnamespace Compiler\n{\n    class Literals;\n\n    namespace Generator\n    {\n        typedef std::vector<Interpreter::Type_Code> CodeContainer;\n\n        inline Interpreter::Type_Code segment0 (unsigned int c, unsigned int arg0)\n        {\n            assert (c<64);\n            return (c<<24) | (arg0 & 0xffffff);\n        }\n\n        inline Interpreter::Type_Code segment1 (unsigned int c, unsigned int arg0,\n            unsigned int arg1)\n        {\n            assert (c<64);\n            return 0x40000000 | (c<<24) | ((arg0 & 0xfff)<<12) | (arg1 & 0xfff);\n        }\n\n        inline Interpreter::Type_Code segment2 (unsigned int c, unsigned int arg0)\n        {\n            assert (c<1024);\n            return 0x80000000 | (c<<20) | (arg0 & 0xfffff);\n        }\n\n        inline Interpreter::Type_Code segment3 (unsigned int c, unsigned int arg0)\n        {\n            assert (c<262144);\n            return 0xc0000000 | (c<<8) | (arg0 & 0xff);\n        }\n\n        inline Interpreter::Type_Code segment4 (unsigned int c, unsigned int arg0,\n            unsigned int arg1)\n        {\n            assert (c<1024);\n            return 0xc4000000 | (c<<16) | ((arg0 & 0xff)<<8) | (arg1 & 0xff);\n        }\n\n        inline Interpreter::Type_Code segment5 (unsigned int c)\n        {\n            assert (c<67108864);\n            return 0xc8000000 | c;\n        }\n\n        void pushInt (CodeContainer& code, Literals& literals, int value);\n\n        void pushFloat (CodeContainer& code, Literals& literals, float value);\n\n        void pushString (CodeContainer& code, Literals& literals, const std::string& value);\n\n        void assignToLocal (CodeContainer& code, char localType,\n            int localIndex, const CodeContainer& value, char valueType);\n\n        void negate (CodeContainer& code, char valueType);\n\n        void add (CodeContainer& code, char valueType1, char valueType2);\n\n        void sub (CodeContainer& code, char valueType1, char valueType2);\n\n        void mul (CodeContainer& code, char valueType1, char valueType2);\n\n        void div (CodeContainer& code, char valueType1, char valueType2);\n\n        void convert (CodeContainer& code, char fromType, char toType);\n\n        void squareRoot (CodeContainer& code);\n\n        void exit (CodeContainer& code);\n\n        void message (CodeContainer& code, Literals& literals, const std::string& message,\n            int buttons);\n\n        void report (CodeContainer& code, Literals& literals, const std::string& message);\n\n        void fetchLocal (CodeContainer& code, char localType, int localIndex);\n\n        void jump (CodeContainer& code, int offset);\n\n        void jumpOnZero (CodeContainer& code, int offset);\n\n        void compare (CodeContainer& code, char op, char valueType1, char valueType2);\n\n        void assignToGlobal (CodeContainer& code, Literals& literals, char localType,\n            const std::string& name, const CodeContainer& value, char valueType);\n\n        void fetchGlobal (CodeContainer& code, Literals& literals, char localType,\n            const std::string& name);\n\n        void assignToMember (CodeContainer& code, Literals& literals, char memberType,\n            const std::string& name, const std::string& id, const CodeContainer& value, char valueType, bool global);\n        ///< \\param global Member of a global script instead of a script of a reference.\n\n        void fetchMember (CodeContainer& code, Literals& literals, char memberType,\n            const std::string& name, const std::string& id, bool global);\n        ///< \\param global Member of a global script instead of a script of a reference.\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/junkparser.cpp",
    "content": "#include \"junkparser.hpp\"\n\n#include \"scanner.hpp\"\n\nCompiler::JunkParser::JunkParser (ErrorHandler& errorHandler, const Context& context,\n    int ignoreKeyword)\n: Parser (errorHandler, context), mIgnoreKeyword (ignoreKeyword)\n{}\n\nbool Compiler::JunkParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)\n{\n    scanner.putbackInt (value, loc);\n    return false;\n}\n\nbool Compiler::JunkParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)\n{\n    scanner.putbackFloat (value, loc);\n    return false;\n}\n\nbool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& loc,\n    Scanner& scanner)\n{\n    scanner.putbackName (name, loc);\n    return false;\n}\n\nbool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n{\n    if (keyword==mIgnoreKeyword)\n        reportWarning (\"Ignoring found junk\", loc);\n    else\n        scanner.putbackKeyword (keyword, loc);\n\n    return false;\n}\n\nbool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n{\n    if (code==Scanner::S_member)\n        reportWarning (\"Ignoring found junk\", loc);\n    else\n        scanner.putbackSpecial (code, loc);\n\n    return false;\n}\n"
  },
  {
    "path": "components/compiler/junkparser.hpp",
    "content": "#ifndef COMPILER_JUNKPARSER_H_INCLUDED\n#define COMPILER_JUNKPARSER_H_INCLUDED\n\n#include \"parser.hpp\"\n\nnamespace Compiler\n{\n    /// \\brief Parse an optional single junk token\n    class JunkParser : public Parser\n    {\n            int mIgnoreKeyword;\n\n        public:\n\n            JunkParser (ErrorHandler& errorHandler, const Context& context,\n                int ignoreKeyword = -1);\n\n            bool parseInt (int value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle an int token.\n            /// \\return fetch another token?\n\n            bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a float token.\n            /// \\return fetch another token?\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/lineparser.cpp",
    "content": "#include \"lineparser.hpp\"\n\n#include <memory>\n\n#include <components/misc/stringops.hpp>\n\n#include \"scanner.hpp\"\n#include \"context.hpp\"\n#include \"errorhandler.hpp\"\n#include \"skipparser.hpp\"\n#include \"locals.hpp\"\n#include \"generator.hpp\"\n#include \"extensions.hpp\"\n#include \"declarationparser.hpp\"\n#include \"exception.hpp\"\n\nnamespace Compiler\n{\n    void LineParser::parseExpression (Scanner& scanner, const TokenLoc& loc)\n    {\n        mExprParser.reset();\n\n        if (!mExplicit.empty())\n        {\n            mExprParser.parseName (mExplicit, loc, scanner);\n            if (mState==MemberState)\n                mExprParser.parseSpecial (Scanner::S_member, loc, scanner);\n            else\n                mExprParser.parseSpecial (Scanner::S_ref, loc, scanner);\n        }\n\n        scanner.scan (mExprParser);\n\n        char type = mExprParser.append (mCode);\n        mState = EndState;\n\n        switch (type)\n        {\n            case 'l':\n\n                Generator::report (mCode, mLiterals, \"%d\");\n                break;\n\n            case 'f':\n\n                Generator::report (mCode, mLiterals, \"%f\");\n                break;\n\n            default:\n\n                throw std::runtime_error (\"Unknown expression result type\");\n        }\n    }\n\n    LineParser::LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,\n        Literals& literals, std::vector<Interpreter::Type_Code>& code, bool allowExpression)\n    : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code),\n       mState (BeginState), mReferenceMember(false), mButtons(0), mType(0),\n       mExprParser (errorHandler, context, locals, literals), mAllowExpression (allowExpression)\n    {}\n\n    bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mAllowExpression && mState==BeginState)\n        {\n            scanner.putbackInt (value, loc);\n            parseExpression (scanner, loc);\n            return true;\n        }\n\n        return Parser::parseInt (value, loc, scanner);\n    }\n\n    bool LineParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mAllowExpression && mState==BeginState)\n        {\n            scanner.putbackFloat (value, loc);\n            parseExpression (scanner, loc);\n            return true;\n        }\n\n        return Parser::parseFloat (value, loc, scanner);\n    }\n\n    bool LineParser::parseName (const std::string& name, const TokenLoc& loc,\n        Scanner& scanner)\n    {\n        if (mState==SetState)\n        {\n            std::string name2 = Misc::StringUtils::lowerCase (name);\n            mName = name2;\n\n            // local variable?\n            char type = mLocals.getType (name2);\n            if (type!=' ')\n            {\n                mType = type;\n                mState = SetLocalVarState;\n                return true;\n            }\n\n            type = getContext().getGlobalType (name2);\n            if (type!=' ')\n            {\n                mType = type;\n                mState = SetGlobalVarState;\n                return true;\n            }\n\n            mState = SetPotentialMemberVarState;\n            return true;\n        }\n\n        if (mState==SetMemberVarState)\n        {\n            mMemberName = Misc::StringUtils::lowerCase (name);\n            std::pair<char, bool> type = getContext().getMemberType (mMemberName, mName);\n\n            if (type.first!=' ')\n            {\n                mState = SetMemberVarState2;\n                mType = type.first;\n                mReferenceMember = type.second;\n                return true;\n            }\n\n            getErrorHandler().error (\"Unknown variable\", loc);\n            SkipParser skip (getErrorHandler(), getContext());\n            scanner.scan (skip);\n            return false;\n        }\n\n        if (mState==MessageState || mState==MessageCommaState)\n        {\n            GetArgumentsFromMessageFormat processor;\n            processor.process(name);\n            std::string arguments = processor.getArguments();\n\n            if (!arguments.empty())\n            {\n                mExprParser.reset();\n                mExprParser.parseArguments (arguments, scanner, mCode);\n            }\n\n            mName = name;\n            mButtons = 0;\n\n            mState = MessageButtonState;\n            return true;\n        }\n\n        if (mState==MessageButtonState || mState==MessageButtonCommaState)\n        {\n            Generator::pushString (mCode, mLiterals, name);\n            mState = MessageButtonState;\n            ++mButtons;\n            return true;\n        }\n\n        if (mState==BeginState && getContext().isId (name))\n        {\n            mState = PotentialExplicitState;\n            mExplicit = Misc::StringUtils::lowerCase (name);\n            return true;\n        }\n\n        if (mState==BeginState && mAllowExpression)\n        {\n            std::string name2 = Misc::StringUtils::lowerCase (name);\n\n            char type = mLocals.getType (name2);\n\n            if (type!=' ')\n            {\n                scanner.putbackName (name, loc);\n                parseExpression (scanner, loc);\n                return true;\n            }\n\n            type = getContext().getGlobalType (name2);\n\n            if (type!=' ')\n            {\n                scanner.putbackName (name, loc);\n                parseExpression (scanner, loc);\n                return true;\n            }\n        }\n\n        return Parser::parseName (name, loc, scanner);\n    }\n\n    bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mState==MessageState || mState==MessageCommaState)\n        {\n            if (const Extensions *extensions = getContext().getExtensions())\n            {\n                std::string argumentType; // ignored\n                bool hasExplicit = false; // ignored\n                if (extensions->isInstruction (keyword, argumentType, hasExplicit))\n                {\n                    // pretend this is not a keyword\n                    std::string name = loc.mLiteral;\n                    if (name.size()>=2 && name[0]=='\"' && name[name.size()-1]=='\"')\n                        name = name.substr (1, name.size()-2);\n                    return parseName (name, loc, scanner);\n                }\n            }\n        }\n\n        if (mState==SetMemberVarState)\n        {\n            mMemberName = loc.mLiteral;\n            std::pair<char, bool> type = getContext().getMemberType (mMemberName, mName);\n\n            if (type.first!=' ')\n            {\n                mState = SetMemberVarState2;\n                mType = type.first;\n                mReferenceMember = type.second;\n                return true;\n            }\n        }\n\n        if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to)\n        {\n            getErrorHandler().warning (\"Unknown variable\", loc);\n            SkipParser skip (getErrorHandler(), getContext());\n            scanner.scan (skip);\n            return false;\n        }\n\n        if (mState==SetState)\n        {\n            // allow keywords to be used as variable names when assigning a value to a variable.\n            return parseName (loc.mLiteral, loc, scanner);\n        }\n\n        if (mState==BeginState || mState==ExplicitState)\n        {\n            // check for custom extensions\n            if (const Extensions *extensions = getContext().getExtensions())\n            {\n                std::string argumentType;\n\n                bool hasExplicit = mState==ExplicitState;\n                if (extensions->isInstruction (keyword, argumentType, hasExplicit))\n                {\n                    if (!hasExplicit && mState==ExplicitState)\n                    {\n                        getErrorHandler().warning (\"Stray explicit reference\", loc);\n                        mExplicit.clear();\n                    }\n\n                    try\n                    {\n                        // workaround for broken positioncell instructions.\n                        /// \\todo add option to disable this\n                        std::unique_ptr<ErrorDowngrade> errorDowngrade (nullptr);\n                        if (Misc::StringUtils::lowerCase (loc.mLiteral)==\"positioncell\")\n                            errorDowngrade = std::make_unique<ErrorDowngrade> (getErrorHandler());\n\n                        std::vector<Interpreter::Type_Code> code;\n                        int optionals = mExprParser.parseArguments (argumentType, scanner, code, keyword);\n                        mCode.insert (mCode.end(), code.begin(), code.end());\n                        extensions->generateInstructionCode (keyword, mCode, mLiterals,\n                            mExplicit, optionals);\n                    }\n                    catch (const SourceException&)\n                    {\n                        // Ignore argument exceptions for positioncell.\n                        /// \\todo add option to disable this\n                        if (Misc::StringUtils::lowerCase (loc.mLiteral)==\"positioncell\")\n                        {\n                            SkipParser skip (getErrorHandler(), getContext());\n                            scanner.scan (skip);\n                            return false;\n                        }\n\n                        throw;\n                    }\n\n                    mState = EndState;\n                    return true;\n                }\n            }\n\n            if (const Extensions *extensions = getContext().getExtensions())\n            {\n                char returnType;\n                std::string argumentType;\n\n                bool hasExplicit = mState==ExplicitState;\n\n                if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit))\n                {\n                    if (!hasExplicit && mState==ExplicitState)\n                    {\n                        getErrorHandler().warning (\"Stray explicit reference\", loc);\n                        mExplicit.clear();\n                    }\n\n                    if (mAllowExpression)\n                    {\n                        scanner.putbackKeyword (keyword, loc);\n                        parseExpression (scanner, loc);\n                    }\n                    else\n                    {\n                        std::vector<Interpreter::Type_Code> code;\n                        int optionals = mExprParser.parseArguments (argumentType, scanner, code, keyword);\n                        mCode.insert(mCode.end(), code.begin(), code.end());\n                        extensions->generateFunctionCode (keyword, mCode, mLiterals, mExplicit, optionals);\n                    }\n                    mState = EndState;\n                    return true;\n                }\n            }\n        }\n\n        if (mState==ExplicitState)\n        {\n            // drop stray explicit reference\n            getErrorHandler().warning (\"Stray explicit reference\", loc);\n            mState = BeginState;\n            mExplicit.clear();\n        }\n\n        if (mState==BeginState)\n        {\n            switch (keyword)\n            {\n                case Scanner::K_short:\n                case Scanner::K_long:\n                case Scanner::K_float:\n                {\n                    if (!getContext().canDeclareLocals())\n                    {\n                        getErrorHandler().error(\"Local variables cannot be declared in this context\", loc);\n                        SkipParser skip (getErrorHandler(), getContext());\n                        scanner.scan (skip);\n                        return true;\n                    }\n\n                    DeclarationParser declaration (getErrorHandler(), getContext(), mLocals);\n                    if (declaration.parseKeyword (keyword, loc, scanner))\n                        scanner.scan (declaration);\n\n                    return false;\n                }\n\n                case Scanner::K_set: mState = SetState; return true;\n\n                case Scanner::K_messagebox:\n\n                    mState = MessageState;\n                    scanner.enableStrictKeywords();\n                    return true;\n\n                case Scanner::K_return:\n\n                    Generator::exit (mCode);\n                    mState = EndState;\n                    return true;\n\n                case Scanner::K_else:\n\n                    getErrorHandler().warning (\"Stray else\", loc);\n                    mState = EndState;\n                    return true;\n\n                case Scanner::K_endif:\n\n                    getErrorHandler().warning (\"Stray endif\", loc);\n                    mState = EndState;\n                    return true;\n\n                case Scanner::K_begin:\n\n                    getErrorHandler().warning (\"Stray begin\", loc);\n                    mState = EndState;\n                    return true;\n            }\n        }\n        else if (mState==SetLocalVarState && keyword==Scanner::K_to)\n        {\n            mExprParser.reset();\n            scanner.scan (mExprParser);\n\n            std::vector<Interpreter::Type_Code> code;\n            char type = mExprParser.append (code);\n\n            Generator::assignToLocal (mCode, mLocals.getType (mName),\n                mLocals.getIndex (mName), code, type);\n\n            mState = EndState;\n            return true;\n        }\n        else if (mState==SetGlobalVarState && keyword==Scanner::K_to)\n        {\n            mExprParser.reset();\n            scanner.scan (mExprParser);\n\n            std::vector<Interpreter::Type_Code> code;\n            char type = mExprParser.append (code);\n\n            Generator::assignToGlobal (mCode, mLiterals, mType, mName, code, type);\n\n            mState = EndState;\n            return true;\n        }\n        else if (mState==SetMemberVarState2 && keyword==Scanner::K_to)\n        {\n            mExprParser.reset();\n            scanner.scan (mExprParser);\n\n            std::vector<Interpreter::Type_Code> code;\n            char type = mExprParser.append (code);\n\n            Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type,\n                !mReferenceMember);\n\n            mState = EndState;\n            return true;\n        }\n\n        return Parser::parseKeyword (keyword, loc, scanner);\n    }\n\n    bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (mState==EndState && code==Scanner::S_open)\n        {\n            getErrorHandler().warning (\"Stray '[' or '(' at the end of the line\",\n                loc);\n            return true;\n        }\n\n        if (code==Scanner::S_newline && (mState==EndState || mState==BeginState))\n            return false;\n\n        if (code==Scanner::S_comma && mState==MessageState)\n        {\n            mState = MessageCommaState;\n            return true;\n        }\n\n        if (code==Scanner::S_ref && mState==SetPotentialMemberVarState)\n        {\n            getErrorHandler().warning (\"Stray explicit reference\", loc);\n            mState = SetState;\n            return true;\n        }\n\n        if (code==Scanner::S_ref && mState==PotentialExplicitState)\n        {\n            mState = ExplicitState;\n            return true;\n        }\n\n        if (code==Scanner::S_member && mState==PotentialExplicitState && mAllowExpression)\n        {\n            mState = MemberState;\n            parseExpression (scanner, loc);\n            mState = EndState;\n            return true;\n        }\n\n        if (code==Scanner::S_newline && mState==MessageButtonState)\n        {\n            Generator::message (mCode, mLiterals, mName, mButtons);\n            return false;\n        }\n\n        if (code==Scanner::S_comma && mState==MessageButtonState)\n        {\n            mState = MessageButtonCommaState;\n            return true;\n        }\n\n        if (code==Scanner::S_member && mState==SetPotentialMemberVarState)\n        {\n            mState = SetMemberVarState;\n            return true;\n        }\n\n        if (mAllowExpression && mState==BeginState &&\n            (code==Scanner::S_open || code==Scanner::S_minus || code==Scanner::S_plus))\n        {\n            scanner.putbackSpecial (code, loc);\n            parseExpression (scanner, loc);\n            mState = EndState;\n            return true;\n        }\n\n        return Parser::parseSpecial (code, loc, scanner);\n    }\n\n    void LineParser::reset()\n    {\n        mState = BeginState;\n        mName.clear();\n        mExplicit.clear();\n    }\n\n    void GetArgumentsFromMessageFormat::visitedPlaceholder(Placeholder placeholder, char /*padding*/, int /*width*/, int /*precision*/, Notation /*notation*/)\n    {\n        switch (placeholder)\n        {\n            case StringPlaceholder:\n                mArguments += 'S';\n                break;\n            case IntegerPlaceholder:\n                mArguments += 'l';\n                break;\n            case FloatPlaceholder:\n                mArguments += 'f';\n                break;\n            default:\n                break;\n        }\n    }\n\n}\n"
  },
  {
    "path": "components/compiler/lineparser.hpp",
    "content": "#ifndef COMPILER_LINEPARSER_H_INCLUDED\n#define COMPILER_LINEPARSER_H_INCLUDED\n\n#include <vector>\n#include <string>\n\n#include <components/interpreter/types.hpp>\n#include <components/misc/messageformatparser.hpp>\n\n#include \"parser.hpp\"\n#include \"exprparser.hpp\"\n\nnamespace Compiler\n{\n    class Locals;\n    class Literals;\n\n    /// \\brief Line parser, to be used in console scripts and as part of ScriptParser\n\n    class LineParser : public Parser\n    {\n            enum State\n            {\n                BeginState,\n                SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState,\n                SetMemberVarState, SetMemberVarState2,\n                MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState,\n                EndState, PotentialExplicitState, ExplicitState, MemberState\n            };\n\n            Locals& mLocals;\n            Literals& mLiterals;\n            std::vector<Interpreter::Type_Code>& mCode;\n            State mState;\n            std::string mName;\n            std::string mMemberName;\n            bool mReferenceMember;\n            int mButtons;\n            std::string mExplicit;\n            char mType;\n            ExprParser mExprParser;\n            bool mAllowExpression;\n\n            void parseExpression (Scanner& scanner, const TokenLoc& loc);\n\n        public:\n\n            LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,\n                Literals& literals, std::vector<Interpreter::Type_Code>& code,\n                bool allowExpression = false);\n            ///< \\param allowExpression Allow lines consisting of a naked expression\n            /// (result is send to the messagebox interface)\n\n            bool parseInt (int value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle an int token.\n            /// \\return fetch another token?\n\n            bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a float token.\n            /// \\return fetch another token?\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void reset() override;\n            ///< Reset parser to clean state.\n    };\n\n    class GetArgumentsFromMessageFormat : public ::Misc::MessageFormatParser\n    {\n        private:\n            std::string mArguments;\n\n        protected:\n            void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision, Notation notation) override;\n            void visitedCharacter(char c) override {}\n\n        public:\n            void process(const std::string& message) override\n            {\n                mArguments.clear();\n                ::Misc::MessageFormatParser::process(message);\n            }\n            std::string getArguments() const { return mArguments; }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/literals.cpp",
    "content": "#include \"literals.hpp\"\n\n#include <algorithm>\n\nnamespace Compiler\n{\n    int Literals::getIntegerSize() const\n    {\n        return static_cast<int>(mIntegers.size() * sizeof (Interpreter::Type_Integer));\n    }\n\n    int Literals::getFloatSize() const\n    {\n        return static_cast<int>(mFloats.size() * sizeof (Interpreter::Type_Float));\n    }\n\n    int Literals::getStringSize() const\n    {\n        int size = 0;\n        \n        for (std::vector<std::string>::const_iterator iter (mStrings.begin());\n            iter!=mStrings.end(); ++iter)\n            size += static_cast<int> (iter->size()) + 1;\n            \n        if (size % 4) // padding\n            size += 4 - size % 4;\n            \n        return size;\n    }\n\n    void Literals::append (std::vector<Interpreter::Type_Code>& code) const\n    {\n        for (const int & mInteger : mIntegers)\n            code.push_back (*reinterpret_cast<const Interpreter::Type_Code *> (&mInteger));\n            \n        for (const float & mFloat : mFloats)\n            code.push_back (*reinterpret_cast<const Interpreter::Type_Code *> (&mFloat));\n            \n        int stringBlockSize = getStringSize();\n        int size = static_cast<int> (code.size());\n        \n        code.resize (size+stringBlockSize/4);\n        \n        size_t offset = 0;\n        \n        for (const auto & mString : mStrings)\n        {\n            size_t stringSize = mString.size()+1;\n            \n            std::copy (mString.c_str(), mString.c_str()+stringSize,\n                reinterpret_cast<char *> (&code[size]) + offset);\n            offset += stringSize;\n        }\n    }\n\n    int Literals::addInteger (Interpreter::Type_Integer value)\n    {\n        int index = static_cast<int> (mIntegers.size());\n        \n        mIntegers.push_back (value);\n        \n        return index;    \n    }\n\n    int Literals::addFloat (Interpreter::Type_Float value)\n    {\n        int index = static_cast<int> (mFloats.size());\n        \n        mFloats.push_back (value);\n        \n        return index;    \n    }\n\n    int Literals::addString (const std::string& value)\n    {\n        int index = static_cast<int> (mStrings.size());\n        \n        mStrings.push_back (value);\n        \n        return index;\n    }\n\n    void Literals::clear()\n    {\n        mIntegers.clear();\n        mFloats.clear();\n        mStrings.clear();\n    }\n}\n\n"
  },
  {
    "path": "components/compiler/literals.hpp",
    "content": "#ifndef COMPILER_LITERALS_H_INCLUDED\n#define COMPILER_LITERALS_H_INCLUDED\n\n#include <string>\n#include <vector>\n\n#include <components/interpreter/types.hpp>\n\nnamespace Compiler\n{\n    /// \\brief Literal values.\n    \n    class Literals\n    {\n            std::vector<Interpreter::Type_Integer> mIntegers;\n            std::vector<Interpreter::Type_Float> mFloats;\n            std::vector<std::string> mStrings;\n    \n        public:\n        \n            int getIntegerSize() const;\n            ///< Return size of integer block (in bytes).\n\n            int getFloatSize() const;\n            ///< Return size of float block (in bytes).\n        \n            int getStringSize() const;\n            ///< Return size of string block (in bytes).\n        \n            void append (std::vector<Interpreter::Type_Code>& code) const;\n            ///< Apepnd literal blocks to code.\n            /// \\note code blocks will be padded for 32-bit alignment.\n        \n            int addInteger (Interpreter::Type_Integer value);\n            ///< add integer liternal and return index.\n            \n            int addFloat (Interpreter::Type_Float value);\n            ///< add float literal and return value.\n            \n            int addString (const std::string& value);\n            ///< add string literal and return value.\n        \n            void clear();\n            ///< remove all literals.\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "components/compiler/locals.cpp",
    "content": "#include \"locals.hpp\"\n\n#include <stdexcept>\n#include <algorithm>\n#include <ostream>\n#include <iterator>\n\n#include <components/misc/stringops.hpp>\n\nnamespace Compiler\n{\n    const std::vector<std::string>& Locals::get (char type) const\n    {\n        switch (type)\n        {\n            case 's': return mShorts;\n            case 'l': return mLongs;\n            case 'f': return mFloats;\n        }\n\n        throw std::logic_error (\"Unknown variable type\");\n    }\n\n    int Locals::searchIndex (char type, const std::string& name) const\n    {\n        const std::vector<std::string>& collection = get (type);\n\n        auto iter = std::find (collection.begin(), collection.end(), name);\n\n        if (iter==collection.end())\n            return -1;\n\n        return static_cast<int>(iter-collection.begin());\n    }\n\n    bool Locals::search (char type, const std::string& name) const\n    {\n        return searchIndex (type, name)!=-1;\n    }\n\n    std::vector<std::string>& Locals::get (char type)\n    {\n        switch (type)\n        {\n            case 's': return mShorts;\n            case 'l': return mLongs;\n            case 'f': return mFloats;\n        }\n\n        throw std::logic_error (\"Unknown variable type\");\n    }\n\n    char Locals::getType (const std::string& name) const\n    {\n        if (search ('s', name))\n            return 's';\n\n        if (search ('l', name))\n            return 'l';\n\n        if (search ('f', name))\n            return 'f';\n\n        return ' ';\n    }\n\n    int Locals::getIndex (const std::string& name) const\n    {\n        int index = searchIndex ('s', name);\n\n        if (index!=-1)\n            return index;\n\n        index = searchIndex ('l', name);\n\n        if (index!=-1)\n            return index;\n\n        return searchIndex ('f', name);\n    }\n\n    void Locals::write (std::ostream& localFile) const\n    {\n        localFile\n            << get ('s').size() << ' '\n            << get ('l').size() << ' '\n            << get ('f').size() << std::endl;\n\n        std::copy (get ('s').begin(), get ('s').end(),\n            std::ostream_iterator<std::string> (localFile, \" \"));\n        std::copy (get ('l').begin(), get ('l').end(),\n            std::ostream_iterator<std::string> (localFile, \" \"));\n        std::copy (get ('f').begin(), get ('f').end(),\n            std::ostream_iterator<std::string> (localFile, \" \"));\n    }\n\n    void Locals::declare (char type, const std::string& name)\n    {\n        get (type).push_back (Misc::StringUtils::lowerCase (name));\n    }\n\n    void Locals::clear()\n    {\n        get ('s').clear();\n        get ('l').clear();\n        get ('f').clear();\n    }\n}\n\n"
  },
  {
    "path": "components/compiler/locals.hpp",
    "content": "#ifndef COMPILER_LOCALS_H_INCLUDED\n#define COMPILER_LOCALS_H_INCLUDED\n\n#include <vector>\n#include <string>\n#include <iosfwd>\n\nnamespace Compiler\n{\n    /// \\brief Local variable declarations\n\n    class Locals\n    {\n            std::vector<std::string> mShorts;\n            std::vector<std::string> mLongs;\n            std::vector<std::string> mFloats;\n\n            std::vector<std::string>& get (char type);\n\n        public:\n\n            char getType (const std::string& name) const;\n            ///< 's': short, 'l': long, 'f': float, ' ': does not exist.\n\n            int getIndex (const std::string& name) const;\n            ///< return index for local variable \\a name (-1: does not exist).\n\n            bool search (char type, const std::string& name) const;\n\n            /// Return index for local variable \\a name of type \\a type (-1: variable does not\n            /// exit).\n            int searchIndex (char type, const std::string& name) const;\n\n            const std::vector<std::string>& get (char type) const;\n\n            void write (std::ostream& localFile) const;\n            ///< write declarations to file.\n\n            void declare (char type, const std::string& name);\n            ///< declares a variable.\n\n            void clear();\n            ///< remove all declarations.\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/nullerrorhandler.cpp",
    "content": "#include \"nullerrorhandler.hpp\"\n\nvoid Compiler::NullErrorHandler::report (const std::string& message, const TokenLoc& loc, Type type) {}\n\nvoid Compiler::NullErrorHandler::report (const std::string& message, Type type) {}\n"
  },
  {
    "path": "components/compiler/nullerrorhandler.hpp",
    "content": "#ifndef COMPILER_NULLERRORHANDLER_H_INCLUDED\n#define COMPILER_NULLERRORHANDLER_H_INCLUDED\n\n#include \"errorhandler.hpp\"\n\nnamespace Compiler\n{\n    /// \\brief Error handler implementation: Ignore all error messages\n\n    class NullErrorHandler : public ErrorHandler\n    {\n            void report (const std::string& message, const TokenLoc& loc, Type type) override;\n            ///< Report error to the user.\n\n            void report (const std::string& message, Type type) override;\n            ///< Report a file related error\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/opcodes.cpp",
    "content": "#include \"opcodes.hpp\"\n\nnamespace Compiler::Control\n    {\n        const char *controls[numberOfControls] =\n        {\n            \"playercontrols\", \"playerfighting\", \"playerjumping\", \"playerlooking\", \"playermagic\",\n            \"playerviewswitch\", \"vanitymode\"\n        };\n    }\n"
  },
  {
    "path": "components/compiler/opcodes.hpp",
    "content": "#ifndef COMPILER_OPCODES_H\n#define COMPILER_OPCODES_H\n\nnamespace Compiler\n{\n    namespace Ai\n    {\n        const int opcodeAiTravel = 0x20000;\n        const int opcodeAiTravelExplicit = 0x20001;\n        const int opcodeAiEscort = 0x20002;\n        const int opcodeAiEscortExplicit = 0x20003;\n        const int opcodeGetAiPackageDone = 0x200007c;\n        const int opcodeGetAiPackageDoneExplicit = 0x200007d;\n        const int opcodeGetCurrentAiPackage = 0x20001ef;\n        const int opcodeGetCurrentAiPackageExplicit = 0x20001f0;\n        const int opcodeGetDetected = 0x20001f1;\n        const int opcodeGetDetectedExplicit = 0x20001f2;\n        const int opcodeAiWander = 0x20010;\n        const int opcodeAiWanderExplicit = 0x20011;\n        const int opcodeAIActivate = 0x2001e;\n        const int opcodeAIActivateExplicit = 0x2001f;\n        const int opcodeAiEscortCell = 0x20020;\n        const int opcodeAiEscortCellExplicit = 0x20021;\n        const int opcodeAiFollow = 0x20022;\n        const int opcodeAiFollowExplicit = 0x20023;\n        const int opcodeAiFollowCell = 0x20024;\n        const int opcodeAiFollowCellExplicit = 0x20025;\n        const int opcodeSetHello = 0x200015c;\n        const int opcodeSetHelloExplicit = 0x200015d;\n        const int opcodeSetFight = 0x200015e;\n        const int opcodeSetFightExplicit = 0x200015f;\n        const int opcodeSetFlee = 0x2000160;\n        const int opcodeSetFleeExplicit = 0x2000161;\n        const int opcodeSetAlarm = 0x2000162;\n        const int opcodeSetAlarmExplicit = 0x2000163;\n        const int opcodeModHello = 0x20001b7;\n        const int opcodeModHelloExplicit = 0x20001b8;\n        const int opcodeModFight = 0x20001b9;\n        const int opcodeModFightExplicit = 0x20001ba;\n        const int opcodeModFlee = 0x20001bb;\n        const int opcodeModFleeExplicit = 0x20001bc;\n        const int opcodeModAlarm = 0x20001bd;\n        const int opcodeModAlarmExplicit = 0x20001be;\n        const int opcodeGetHello = 0x20001bf;\n        const int opcodeGetHelloExplicit = 0x20001c0;\n        const int opcodeGetFight = 0x20001c1;\n        const int opcodeGetFightExplicit = 0x20001c2;\n        const int opcodeGetFlee = 0x20001c3;\n        const int opcodeGetFleeExplicit = 0x20001c4;\n        const int opcodeGetAlarm = 0x20001c5;\n        const int opcodeGetAlarmExplicit = 0x20001c6;\n        const int opcodeGetLineOfSight = 0x2000222;\n        const int opcodeGetLineOfSightExplicit = 0x2000223;\n        const int opcodeToggleAI = 0x2000224;\n        const int opcodeGetTarget = 0x2000238;\n        const int opcodeGetTargetExplicit = 0x2000239;\n        const int opcodeStartCombat = 0x200023a;\n        const int opcodeStartCombatExplicit = 0x200023b;\n        const int opcodeStopCombat = 0x200023c;\n        const int opcodeStopCombatExplicit = 0x200023d;\n        const int opcodeFace = 0x200024c;\n        const int opcodeFaceExplicit = 0x200024d;\n    }\n\n    namespace Animation\n    {\n        const int opcodeSkipAnim = 0x2000138;\n        const int opcodeSkipAnimExplicit = 0x2000139;\n        const int opcodePlayAnim = 0x20006;\n        const int opcodePlayAnimExplicit = 0x20007;\n        const int opcodeLoopAnim = 0x20008;\n        const int opcodeLoopAnimExplicit = 0x20009;\n    }\n\n    namespace Cell\n    {\n        const int opcodeCellChanged = 0x2000000;\n        const int opcodeTestCells = 0x200030e;\n        const int opcodeTestInteriorCells = 0x200030f;\n        const int opcodeCOC = 0x2000026;\n        const int opcodeCOE = 0x2000226;\n        const int opcodeGetInterior = 0x2000131;\n        const int opcodeGetPCCell = 0x2000136;\n        const int opcodeGetWaterLevel = 0x2000141;\n        const int opcodeSetWaterLevel = 0x2000142;\n        const int opcodeModWaterLevel = 0x2000143;\n    }\n\n    namespace Console\n    {\n\n    }\n\n    namespace Container\n    {\n        const int opcodeAddItem = 0x2000076;\n        const int opcodeAddItemExplicit = 0x2000077;\n        const int opcodeGetItemCount = 0x2000078;\n        const int opcodeGetItemCountExplicit = 0x2000079;\n        const int opcodeRemoveItem = 0x200007a;\n        const int opcodeRemoveItemExplicit = 0x200007b;\n        const int opcodeEquip = 0x20001b3;\n        const int opcodeEquipExplicit = 0x20001b4;\n        const int opcodeGetArmorType = 0x20001d1;\n        const int opcodeGetArmorTypeExplicit = 0x20001d2;\n        const int opcodeHasItemEquipped = 0x20001d5;\n        const int opcodeHasItemEquippedExplicit = 0x20001d6;\n        const int opcodeHasSoulGem = 0x20001de;\n        const int opcodeHasSoulGemExplicit = 0x20001df;\n        const int opcodeGetWeaponType = 0x20001e0;\n        const int opcodeGetWeaponTypeExplicit = 0x20001e1;\n    }\n\n    namespace Control\n    {\n        const int numberOfControls = 7;\n\n        extern const char *controls[numberOfControls];\n\n        const int opcodeEnable = 0x200007e;\n        const int opcodeDisable = 0x2000085;\n        const int opcodeToggleCollision = 0x2000130;\n        const int opcodeClearForceRun = 0x2000154;\n        const int opcodeClearForceRunExplicit = 0x2000155;\n        const int opcodeForceRun = 0x2000156;\n        const int opcodeForceRunExplicit = 0x2000157;\n        const int opcodeClearForceJump = 0x2000258;\n        const int opcodeClearForceJumpExplicit = 0x2000259;\n        const int opcodeForceJump = 0x200025a;\n        const int opcodeForceJumpExplicit = 0x200025b;\n        const int opcodeClearForceMoveJump = 0x200025c;\n        const int opcodeClearForceMoveJumpExplicit = 0x200025d;\n        const int opcodeForceMoveJump = 0x200025e;\n        const int opcodeForceMoveJumpExplicit = 0x200025f;\n        const int opcodeClearForceSneak = 0x2000158;\n        const int opcodeClearForceSneakExplicit = 0x2000159;\n        const int opcodeForceSneak = 0x200015a;\n        const int opcodeForceSneakExplicit = 0x200015b;\n        const int opcodeGetDisabled = 0x2000175;\n        const int opcodeGetPcRunning = 0x20001c9;\n        const int opcodeGetPcSneaking = 0x20001ca;\n        const int opcodeGetForceRun = 0x20001cb;\n        const int opcodeGetForceSneak = 0x20001cc;\n        const int opcodeGetForceRunExplicit = 0x20001cd;\n        const int opcodeGetForceSneakExplicit = 0x20001ce;\n        const int opcodeGetForceJump = 0x2000260;\n        const int opcodeGetForceMoveJump = 0x2000262;\n        const int opcodeGetForceJumpExplicit = 0x2000261;\n        const int opcodeGetForceMoveJumpExplicit = 0x2000263;\n    }\n\n    namespace Dialogue\n    {\n        const int opcodeJournal = 0x2000133;\n        const int opcodeJournalExplicit = 0x200030b;\n        const int opcodeSetJournalIndex = 0x2000134;\n        const int opcodeGetJournalIndex = 0x2000135;\n        const int opcodeAddTopic = 0x200013a;\n        const int opcodeChoice = 0x2000a;\n        const int opcodeForceGreeting = 0x200014f;\n        const int opcodeForceGreetingExplicit = 0x2000150;\n        const int opcodeGoodbye = 0x2000152;\n        const int opcodeSetReputation = 0x20001ad;\n        const int opcodeModReputation = 0x20001ae;\n        const int opcodeSetReputationExplicit = 0x20001af;\n        const int opcodeModReputationExplicit = 0x20001b0;\n        const int opcodeGetReputation = 0x20001b1;\n        const int opcodeGetReputationExplicit = 0x20001b2;\n        const int opcodeSameFaction = 0x20001b5;\n        const int opcodeSameFactionExplicit = 0x20001b6;\n        const int opcodeModFactionReaction = 0x2000242;\n        const int opcodeSetFactionReaction = 0x20002ff;\n        const int opcodeGetFactionReaction = 0x2000243;\n        const int opcodeClearInfoActor = 0x2000245;\n        const int opcodeClearInfoActorExplicit = 0x2000246;\n    }\n\n    namespace Gui\n    {\n        const int opcodeEnableBirthMenu = 0x200000e;\n        const int opcodeEnableClassMenu = 0x200000f;\n        const int opcodeEnableNameMenu = 0x2000010;\n        const int opcodeEnableRaceMenu = 0x2000011;\n        const int opcodeEnableStatsReviewMenu = 0x2000012;\n        const int opcodeEnableInventoryMenu = 0x2000013;\n        const int opcodeEnableMagicMenu = 0x2000014;\n        const int opcodeEnableMapMenu = 0x2000015;\n        const int opcodeEnableStatsMenu = 0x2000016;\n        const int opcodeEnableRest = 0x2000017;\n        const int opcodeEnableLevelupMenu = 0x2000300;\n        const int opcodeShowRestMenu = 0x2000018;\n        const int opcodeShowRestMenuExplicit = 0x2000234;\n        const int opcodeGetButtonPressed = 0x2000137;\n        const int opcodeToggleFogOfWar = 0x2000145;\n        const int opcodeToggleFullHelp = 0x2000151;\n        const int opcodeShowMap = 0x20001a0;\n        const int opcodeFillMap = 0x20001a1;\n        const int opcodeMenuTest = 0x2002c;\n        const int opcodeToggleMenus = 0x200024b;\n    }\n\n    namespace Misc\n    {\n        const int opcodeXBox = 0x200000c;\n        const int opcodeOnActivate = 0x200000d;\n        const int opcodeOnActivateExplicit = 0x2000306;\n        const int opcodeActivate = 0x2000075;\n        const int opcodeActivateExplicit = 0x2000244;\n        const int opcodeLock = 0x20004;\n        const int opcodeLockExplicit = 0x20005;\n        const int opcodeUnlock = 0x200008c;\n        const int opcodeUnlockExplicit = 0x200008d;\n        const int opcodeToggleCollisionDebug = 0x2000132;\n        const int opcodeToggleCollisionBoxes = 0x20001ac;\n        const int opcodeToggleWireframe = 0x200013b;\n        const int opcodeFadeIn = 0x200013c;\n        const int opcodeFadeOut = 0x200013d;\n        const int opcodeFadeTo = 0x200013e;\n        const int opcodeToggleWater = 0x2000144;\n        const int opcodeToggleWorld = 0x20002f5;\n        const int opcodeTogglePathgrid = 0x2000146;\n        const int opcodeDontSaveObject = 0x2000153;\n        const int opcodePcForce1stPerson = 0x20002f6;\n        const int opcodePcForce3rdPerson = 0x20002f7;\n        const int opcodePcGet3rdPerson = 0x20002f8;\n        const int opcodeToggleVanityMode = 0x2000174;\n        const int opcodeGetPcSleep = 0x200019f;\n        const int opcodeGetPcJumping = 0x2000233;\n        const int opcodeWakeUpPc = 0x20001a2;\n        const int opcodeGetLocked = 0x20001c7;\n        const int opcodeGetLockedExplicit = 0x20001c8;\n        const int opcodeGetEffect = 0x20001cf;\n        const int opcodeGetEffectExplicit = 0x20001d0;\n        const int opcodeBetaComment = 0x2002d;\n        const int opcodeBetaCommentExplicit = 0x2002e;\n        const int opcodeAddSoulGem = 0x20001f3;\n        const int opcodeAddSoulGemExplicit = 0x20001f4;\n        const int opcodeRemoveSoulGem = 0x20027;\n        const int opcodeRemoveSoulGemExplicit = 0x20028;\n        const int opcodeDrop = 0x20001f8;\n        const int opcodeDropExplicit = 0x20001f9;\n        const int opcodeDropSoulGem = 0x20001fa;\n        const int opcodeDropSoulGemExplicit = 0x20001fb;\n        const int opcodeGetAttacked = 0x20001d3;\n        const int opcodeGetAttackedExplicit = 0x20001d4;\n        const int opcodeGetWeaponDrawn = 0x20001d7;\n        const int opcodeGetWeaponDrawnExplicit = 0x20001d8;\n        const int opcodeGetSpellReadied = 0x2000231;\n        const int opcodeGetSpellReadiedExplicit = 0x2000232;\n        const int opcodeGetSpellEffects = 0x20001db;\n        const int opcodeGetSpellEffectsExplicit = 0x20001dc;\n        const int opcodeGetCurrentTime = 0x20001dd;\n        const int opcodeSetDelete = 0x20001e5;\n        const int opcodeSetDeleteExplicit = 0x20001e6;\n        const int opcodeGetSquareRoot = 0x20001e7;\n        const int opcodeFall = 0x200020a;\n        const int opcodeFallExplicit = 0x200020b;\n        const int opcodeGetStandingPc = 0x200020c;\n        const int opcodeGetStandingPcExplicit = 0x200020d;\n        const int opcodeGetStandingActor = 0x200020e;\n        const int opcodeGetStandingActorExplicit = 0x200020f;\n        const int opcodeGetCollidingPc = 0x2000250;\n        const int opcodeGetCollidingPcExplicit = 0x2000251;\n        const int opcodeGetCollidingActor = 0x2000252;\n        const int opcodeGetCollidingActorExplicit = 0x2000253;\n        const int opcodeHurtStandingActor = 0x2000254;\n        const int opcodeHurtStandingActorExplicit = 0x2000255;\n        const int opcodeHurtCollidingActor = 0x2000256;\n        const int opcodeHurtCollidingActorExplicit = 0x2000257;\n        const int opcodeGetWindSpeed = 0x2000212;\n        const int opcodePlayBink = 0x20001f7;\n        const int opcodeGoToJail = 0x2000235;\n        const int opcodePayFine = 0x2000236;\n        const int opcodePayFineThief = 0x2000237;\n        const int opcodeHitOnMe = 0x2000213;\n        const int opcodeHitOnMeExplicit = 0x2000214;\n        const int opcodeHitAttemptOnMe = 0x20002f9;\n        const int opcodeHitAttemptOnMeExplicit = 0x20002fa;\n        const int opcodeDisableTeleporting = 0x2000215;\n        const int opcodeEnableTeleporting = 0x2000216;\n        const int opcodeShowVars = 0x200021d;\n        const int opcodeShowVarsExplicit = 0x200021e;\n        const int opcodeShow = 0x2000304;\n        const int opcodeShowExplicit = 0x2000305;\n        const int opcodeToggleGodMode = 0x200021f;\n        const int opcodeToggleScripts = 0x2000301;\n        const int opcodeDisableLevitation = 0x2000220;\n        const int opcodeEnableLevitation = 0x2000221;\n        const int opcodeCast = 0x2000227;\n        const int opcodeCastExplicit = 0x2000228;\n        const int opcodeExplodeSpell = 0x2000229;\n        const int opcodeExplodeSpellExplicit = 0x200022a;\n        const int opcodeGetPcInJail = 0x200023e;\n        const int opcodeGetPcTraveling = 0x200023f;\n        const int opcodeAddToLevCreature = 0x20002fb;\n        const int opcodeRemoveFromLevCreature = 0x20002fc;\n        const int opcodeAddToLevItem = 0x20002fd;\n        const int opcodeRemoveFromLevItem = 0x20002fe;\n        const int opcodeShowSceneGraph = 0x2002f;\n        const int opcodeShowSceneGraphExplicit = 0x20030;\n        const int opcodeToggleBorders = 0x2000307;\n        const int opcodeToggleNavMesh = 0x2000308;\n        const int opcodeToggleActorsPaths = 0x2000309;\n        const int opcodeSetNavMeshNumberToRender = 0x200030a;\n        const int opcodeRepairedOnMe = 0x200030c;\n        const int opcodeRepairedOnMeExplicit = 0x200030d;\n        const int opcodeToggleRecastMesh = 0x2000310;\n        const int opcodeMenuMode = 0x2000311;\n        const int opcodeRandom = 0x2000312;\n        const int opcodeScriptRunning = 0x2000313;\n        const int opcodeStartScript = 0x2000314;\n        const int opcodeStopScript = 0x2000315;\n        const int opcodeGetSecondsPassed = 0x2000316;\n        const int opcodeEnable = 0x2000317;\n        const int opcodeDisable = 0x2000318;\n        const int opcodeGetDisabled = 0x2000319;\n        const int opcodeEnableExplicit = 0x200031a;\n        const int opcodeDisableExplicit = 0x200031b;\n        const int opcodeGetDisabledExplicit = 0x200031c;\n        const int opcodeStartScriptExplicit = 0x200031d;\n    }\n\n    namespace Sky\n    {\n        const int opcodeToggleSky = 0x2000021;\n        const int opcodeTurnMoonWhite = 0x2000022;\n        const int opcodeTurnMoonRed = 0x2000023;\n        const int opcodeGetMasserPhase = 0x2000024;\n        const int opcodeGetSecundaPhase = 0x2000025;\n        const int opcodeGetCurrentWeather = 0x200013f;\n        const int opcodeChangeWeather = 0x2000140;\n        const int opcodeModRegion = 0x20026;\n    }\n\n    namespace Sound\n    {\n        const int opcodeSay = 0x2000001;\n        const int opcodeSayDone = 0x2000002;\n        const int opcodeStreamMusic = 0x2000003;\n        const int opcodePlaySound = 0x2000004;\n        const int opcodePlaySoundVP = 0x2000005;\n        const int opcodePlaySound3D = 0x2000006;\n        const int opcodePlaySound3DVP = 0x2000007;\n        const int opcodePlayLoopSound3D = 0x2000008;\n        const int opcodePlayLoopSound3DVP = 0x2000009;\n        const int opcodeStopSound = 0x200000a;\n        const int opcodeGetSoundPlaying = 0x200000b;\n\n        const int opcodeSayExplicit = 0x2000019;\n        const int opcodeSayDoneExplicit = 0x200001a;\n        const int opcodePlaySound3DExplicit = 0x200001b;\n        const int opcodePlaySound3DVPExplicit = 0x200001c;\n        const int opcodePlayLoopSound3DExplicit = 0x200001d;\n        const int opcodePlayLoopSound3DVPExplicit = 0x200001e;\n        const int opcodeStopSoundExplicit = 0x200001f;\n        const int opcodeGetSoundPlayingExplicit = 0x2000020;\n    }\n\n    namespace Stats\n    {\n        const int numberOfAttributes = 8;\n        const int numberOfDynamics = 3;\n        const int numberOfSkills = 27;\n\n        const int numberOfMagicEffects = 24;\n\n        const int opcodeGetAttribute = 0x2000027;\n        const int opcodeGetAttributeExplicit = 0x200002f;\n        const int opcodeSetAttribute = 0x2000037;\n        const int opcodeSetAttributeExplicit = 0x200003f;\n        const int opcodeModAttribute = 0x2000047;\n        const int opcodeModAttributeExplicit = 0x200004f;\n\n        const int opcodeGetDynamic = 0x2000057;\n        const int opcodeGetDynamicExplicit = 0x200005a;\n        const int opcodeSetDynamic = 0x200005d;\n        const int opcodeSetDynamicExplicit = 0x2000060;\n        const int opcodeModDynamic = 0x2000063;\n        const int opcodeModDynamicExplicit = 0x2000066;\n        const int opcodeModCurrentDynamic = 0x2000069;\n        const int opcodeModCurrentDynamicExplicit = 0x200006c;\n        const int opcodeGetDynamicGetRatio = 0x200006f;\n        const int opcodeGetDynamicGetRatioExplicit = 0x2000072;\n\n        const int opcodeGetSkill = 0x200008e;\n        const int opcodeGetSkillExplicit = 0x20000a9;\n        const int opcodeSetSkill = 0x20000c4;\n        const int opcodeSetSkillExplicit = 0x20000df;\n        const int opcodeModSkill = 0x20000fa;\n        const int opcodeModSkillExplicit = 0x2000115;\n\n        const int opcodeGetMagicEffect = 0x2000264;\n        const int opcodeGetMagicEffectExplicit = 0x200027c;\n        const int opcodeSetMagicEffect = 0x2000294;\n        const int opcodeSetMagicEffectExplicit = 0x20002ac;\n        const int opcodeModMagicEffect = 0x20002c4;\n        const int opcodeModMagicEffectExplicit = 0x20002dc;\n\n        const int opcodeGetPCCrimeLevel = 0x20001ec;\n        const int opcodeSetPCCrimeLevel = 0x20001ed;\n        const int opcodeModPCCrimeLevel = 0x20001ee;\n\n        const int opcodeAddSpell = 0x2000147;\n        const int opcodeAddSpellExplicit = 0x2000148;\n        const int opcodeRemoveSpell = 0x2000149;\n        const int opcodeRemoveSpellExplicit = 0x200014a;\n        const int opcodeGetSpell = 0x200014b;\n        const int opcodeGetSpellExplicit = 0x200014c;\n\n        const int opcodePCRaiseRank = 0x2000b;\n        const int opcodePCLowerRank = 0x2000c;\n        const int opcodePCJoinFaction = 0x2000d;\n        const int opcodePCRaiseRankExplicit = 0x20029;\n        const int opcodePCLowerRankExplicit = 0x2002a;\n        const int opcodePCJoinFactionExplicit = 0x2002b;\n\n        const int opcodeGetPCRank = 0x2000e;\n        const int opcodeGetPCRankExplicit = 0x2000f;\n        const int opcodeModDisposition = 0x200014d;\n        const int opcodeModDispositionExplicit = 0x200014e;\n        const int opcodeSetDisposition = 0x20001a4;\n        const int opcodeSetDispositionExplicit = 0x20001a5;\n        const int opcodeGetDisposition = 0x20001a6;\n        const int opcodeGetDispositionExplicit = 0x20001a7;\n\n        const int opcodeGetLevel = 0x200018c;\n        const int opcodeGetLevelExplicit = 0x200018d;\n        const int opcodeSetLevel = 0x200018e;\n        const int opcodeSetLevelExplicit = 0x200018f;\n\n        const int opcodeGetDeadCount = 0x20001a3;\n\n        const int opcodeGetPCFacRep = 0x20012;\n        const int opcodeGetPCFacRepExplicit = 0x20013;\n        const int opcodeSetPCFacRep = 0x20014;\n        const int opcodeSetPCFacRepExplicit = 0x20015;\n        const int opcodeModPCFacRep = 0x20016;\n        const int opcodeModPCFacRepExplicit = 0x20017;\n\n        const int opcodeGetCommonDisease = 0x20001a8;\n        const int opcodeGetCommonDiseaseExplicit = 0x20001a9;\n        const int opcodeGetBlightDisease = 0x20001aa;\n        const int opcodeGetBlightDiseaseExplicit = 0x20001ab;\n\n        const int opcodeGetRace = 0x20001d9;\n        const int opcodeGetRaceExplicit = 0x20001da;\n\n        const int opcodePcExpelled = 0x20018;\n        const int opcodePcExpelledExplicit = 0x20019;\n        const int opcodePcExpell = 0x2001a;\n        const int opcodePcExpellExplicit = 0x2001b;\n        const int opcodePcClearExpelled = 0x2001c;\n        const int opcodePcClearExpelledExplicit = 0x2001d;\n        const int opcodeRaiseRank = 0x20001e8;\n        const int opcodeRaiseRankExplicit = 0x20001e9;\n        const int opcodeLowerRank = 0x20001ea;\n        const int opcodeLowerRankExplicit = 0x20001eb;\n        const int opcodeOnDeath = 0x20001fc;\n        const int opcodeOnDeathExplicit = 0x2000205;\n        const int opcodeOnMurder = 0x2000249;\n        const int opcodeOnMurderExplicit = 0x200024a;\n        const int opcodeOnKnockout = 0x2000240;\n        const int opcodeOnKnockoutExplicit = 0x2000241;\n\n        const int opcodeBecomeWerewolf = 0x2000217;\n        const int opcodeBecomeWerewolfExplicit = 0x2000218;\n        const int opcodeUndoWerewolf = 0x2000219;\n        const int opcodeUndoWerewolfExplicit = 0x200021a;\n        const int opcodeSetWerewolfAcrobatics = 0x200021b;\n        const int opcodeSetWerewolfAcrobaticsExplicit = 0x200021c;\n        const int opcodeIsWerewolf = 0x20001fd;\n        const int opcodeIsWerewolfExplicit = 0x20001fe;\n\n        const int opcodeGetWerewolfKills = 0x20001e2;\n\n        const int opcodeRemoveSpellEffects = 0x200022b;\n        const int opcodeRemoveSpellEffectsExplicit = 0x200022c;\n        const int opcodeRemoveEffects = 0x200022d;\n        const int opcodeRemoveEffectsExplicit = 0x200022e;\n        const int opcodeResurrect = 0x200022f;\n        const int opcodeResurrectExplicit = 0x2000230;\n\n        const int opcodeGetStat = 0x200024e;\n        const int opcodeGetStatExplicit = 0x200024f;\n    }\n\n    namespace Transformation\n    {\n        const int opcodeSetScale = 0x2000164;\n        const int opcodeSetScaleExplicit = 0x2000165;\n        const int opcodeSetAngle = 0x2000166;\n        const int opcodeSetAngleExplicit = 0x2000167;\n        const int opcodeGetScale = 0x2000168;\n        const int opcodeGetScaleExplicit = 0x2000169;\n        const int opcodeGetAngle = 0x200016a;\n        const int opcodeGetAngleExplicit = 0x200016b;\n        const int opcodeGetPos = 0x2000190;\n        const int opcodeGetPosExplicit = 0x2000191;\n        const int opcodeSetPos = 0x2000192;\n        const int opcodeSetPosExplicit = 0x2000193;\n        const int opcodeGetStartingPos = 0x2000194;\n        const int opcodeGetStartingPosExplicit = 0x2000195;\n        const int opcodeGetStartingAngle = 0x2000210;\n        const int opcodeGetStartingAngleExplicit = 0x2000211;\n        const int opcodePosition = 0x2000196;\n        const int opcodePositionExplicit = 0x2000197;\n        const int opcodePositionCell = 0x2000198;\n        const int opcodePositionCellExplicit = 0x2000199;\n\n        const int opcodePlaceItemCell = 0x200019a;\n        const int opcodePlaceItem = 0x200019b;\n        const int opcodePlaceAtPc = 0x200019c;\n        const int opcodePlaceAtMe = 0x200019d;\n        const int opcodePlaceAtMeExplicit = 0x200019e;\n        const int opcodeModScale = 0x20001e3;\n        const int opcodeModScaleExplicit = 0x20001e4;\n        const int opcodeRotate = 0x20001ff;\n        const int opcodeRotateExplicit = 0x2000200;\n        const int opcodeRotateWorld = 0x2000201;\n        const int opcodeRotateWorldExplicit = 0x2000202;\n        const int opcodeSetAtStart = 0x2000203;\n        const int opcodeSetAtStartExplicit = 0x2000204;\n        const int opcodeMove = 0x2000206;\n        const int opcodeMoveExplicit = 0x2000207;\n        const int opcodeMoveWorld = 0x2000208;\n        const int opcodeMoveWorldExplicit = 0x2000209;\n        const int opcodeResetActors = 0x20002f4;\n        const int opcodeFixme = 0x2000302;\n        const int opcodeGetDistance = 0x200031e;\n        const int opcodeGetDistanceExplicit = 0x200031f;\n    }\n\n    namespace User\n    {\n        const int opcodeUser1 = 0x200016c;\n        const int opcodeUser2 = 0x200016d;\n        const int opcodeUser3 = 0x200016e;\n        const int opcodeUser3Explicit = 0x200016f;\n        const int opcodeUser4 = 0x2000170;\n        const int opcodeUser4Explicit = 0x2000171;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/output.cpp",
    "content": "#include \"output.hpp\"\n\n#include <cassert>\n#include <algorithm>\n#include <iterator>\n\n#include \"locals.hpp\"\n\nnamespace Compiler\n{\n    Output::Output (Locals& locals) : mLocals (locals) {}\n\n    void Output::getCode (std::vector<Interpreter::Type_Code>& code) const\n    {\n        code.clear();\n        \n        // header\n        code.push_back (static_cast<Interpreter::Type_Code> (mCode.size()));\n        \n        assert (mLiterals.getIntegerSize()%4==0);\n        code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getIntegerSize()/4));\n        \n        assert (mLiterals.getFloatSize()%4==0);\n        code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getFloatSize()/4));\n        \n        assert (mLiterals.getStringSize()%4==0);\n        code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getStringSize()/4));\n        \n        // code\n        std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));\n        \n        // literals\n        mLiterals.append (code);\n    }\n\n    const Literals& Output::getLiterals() const\n    {\n        return mLiterals;\n    }\n            \n    const std::vector<Interpreter::Type_Code>& Output::getCode() const\n    {\n        return mCode;\n    }\n            \n    const Locals& Output::getLocals() const\n    {\n        return mLocals;\n    }\n\n    Literals& Output::getLiterals()\n    {\n        return mLiterals;\n    }\n    \n    std::vector<Interpreter::Type_Code>& Output::getCode()\n    {\n        return mCode;\n    }\n    \n    Locals& Output::getLocals()\n    {\n        return mLocals;\n    }\n    \n    void Output::clear()\n    {\n        mLiterals.clear();\n        mCode.clear();\n        mLocals.clear();\n    }\n}\n\n"
  },
  {
    "path": "components/compiler/output.hpp",
    "content": "#ifndef COMPILER_OUTPUT_H_INCLUDED\n#define COMPILER_OUTPUT_H_INCLUDED\n\n#include \"literals.hpp\"\n\n#include <vector>\n\n#include <components/interpreter/types.hpp>\n\nnamespace Compiler\n{\n    class Locals;\n\n    class Output\n    {\n            Literals mLiterals;\n            std::vector<Interpreter::Type_Code> mCode;            \n            Locals& mLocals;\n                \n        public:\n        \n            Output (Locals& locals);\n    \n            void getCode (std::vector<Interpreter::Type_Code>& code) const;\n            ///< store generated code in \\a code.\n\n            const Literals& getLiterals() const;\n\n            const Locals& getLocals() const;\n            \n            const std::vector<Interpreter::Type_Code>& getCode() const;\n            \n            Literals& getLiterals();\n\n            std::vector<Interpreter::Type_Code>& getCode();\n            \n            Locals& getLocals();\n            \n            void clear();\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "components/compiler/parser.cpp",
    "content": "#include \"parser.hpp\"\n\n#include \"errorhandler.hpp\"\n#include \"exception.hpp\"\n#include \"scanner.hpp\"\n\n#include <components/misc/stringops.hpp>\n\nnamespace Compiler\n{\n    // Report the error and throw an exception.\n\n    void Parser::reportSeriousError (const std::string& message, const TokenLoc& loc)\n    {\n        mErrorHandler.error (message, loc);\n        throw SourceException();\n    }\n\n    // Report the warning without throwing an exception.\n\n    void Parser::reportWarning (const std::string& message, const TokenLoc& loc)\n    {\n        mErrorHandler.warning (message, loc);\n    }\n\n    // Report an unexpected EOF condition.\n\n    void Parser::reportEOF()\n    {\n        mErrorHandler.endOfFile();\n        throw EOFException();\n    }\n\n    // Return error handler\n\n    ErrorHandler& Parser::getErrorHandler()\n    {\n        return mErrorHandler;\n    }\n\n    // Return context\n\n    const Context& Parser::getContext() const\n    {\n        return mContext;\n    }\n\n    std::string Parser::toLower (const std::string& name)\n    {\n        std::string lowerCase = Misc::StringUtils::lowerCase(name);\n\n        return lowerCase;\n    }\n\n    Parser::Parser (ErrorHandler& errorHandler, const Context& context)\n    : mErrorHandler (errorHandler), mContext (context), mOptional (false), mEmpty (true)\n    {}\n\n    // destructor\n\n    Parser::~Parser() = default;\n\n    // Handle an int token.\n    // \\return fetch another token?\n    //\n    // - Default-implementation: Report an error.\n\n    bool Parser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (!(mOptional && mEmpty))\n            reportSeriousError (\"Unexpected numeric value\", loc);\n        else\n            scanner.putbackInt (value, loc);\n\n        return false;\n    }\n\n    // Handle a float token.\n    // \\return fetch another token?\n    //\n    // - Default-implementation: Report an error.\n\n    bool Parser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (!(mOptional && mEmpty))\n            reportSeriousError (\"Unexpected floating point value\", loc);\n        else\n            scanner.putbackFloat (value, loc);\n\n        return false;\n    }\n\n    // Handle a name token.\n    // \\return fetch another token?\n    //\n    // - Default-implementation: Report an error.\n\n    bool Parser::parseName (const std::string& name, const TokenLoc& loc,\n        Scanner& scanner)\n    {\n        if (!(mOptional && mEmpty))\n            reportSeriousError (\"Unexpected name\", loc);\n        else\n            scanner.putbackName (name, loc);\n\n        return false;\n    }\n\n    // Handle a keyword token.\n    // \\return fetch another token?\n    //\n    // - Default-implementation: Report an error.\n\n    bool Parser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (!(mOptional && mEmpty))\n            reportSeriousError (\"Unexpected keyword\", loc);\n        else\n            scanner.putbackKeyword (keyword, loc);\n\n        return false;\n    }\n\n    // Handle a special character token.\n    // \\return fetch another token?\n    //\n    // - Default-implementation: Report an error.\n\n    bool Parser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (!(mOptional && mEmpty))\n            reportSeriousError (\"Unexpected special token\", loc);\n        else\n            scanner.putbackSpecial (code, loc);\n\n        return false;\n    }\n\n    bool Parser::parseComment (const std::string& comment, const TokenLoc& loc, Scanner& scanner)\n    {\n        return true;\n    }\n\n    // Handle an EOF token.\n    //\n    // - Default-implementation: Report an error.\n\n    void Parser::parseEOF (Scanner& scanner)\n    {\n        reportEOF();\n    }\n\n    void Parser::reset()\n    {\n        mOptional = false;\n        mEmpty = true;\n    }\n\n    void Parser::setOptional (bool optional)\n    {\n        mOptional = optional;\n    }\n\n    void Parser::start()\n    {\n        mEmpty = false;\n    }\n\n    bool Parser::isEmpty() const\n    {\n        return mEmpty;\n    }\n}\n"
  },
  {
    "path": "components/compiler/parser.hpp",
    "content": "#ifndef COMPILER_PARSER_H_INCLUDED\n#define COMPILER_PARSER_H_INCLUDED\n\n#include <string>\n\nnamespace Compiler\n{\n    class Scanner;\n    struct TokenLoc;\n    class ErrorHandler;\n    class Context;\n\n    /// \\brief Parser base class\n    ///\n    /// This class defines a callback-parser.\n\n    class Parser\n    {\n            ErrorHandler& mErrorHandler;\n            const Context& mContext;\n            bool mOptional;\n            bool mEmpty;\n\n        protected:\n\n            void reportSeriousError (const std::string& message, const TokenLoc& loc);\n            ///< Report the error and throw a exception.\n\n            void reportWarning (const std::string& message, const TokenLoc& loc);\n            ///< Report the warning without throwing an exception.\n\n            void reportEOF();\n            ///< Report an unexpected EOF condition.\n\n            ErrorHandler& getErrorHandler();\n            ///< Return error handler\n\n            const Context& getContext() const;\n            ///< Return context\n\n            static std::string toLower (const std::string& name);\n\n        public:\n\n            Parser (ErrorHandler& errorHandler, const Context& context);\n            ///< constructor\n\n            virtual ~Parser();\n            ///< destructor\n\n            virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);\n            ///< Handle an int token.\n            /// \\return fetch another token?\n            ///\n            /// - Default-implementation: Report an error.\n\n            virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);\n            ///< Handle a float token.\n            /// \\return fetch another token?\n            ///\n            /// - Default-implementation: Report an error.\n\n            virtual bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner);\n            ///< Handle a name token.\n            /// \\return fetch another token?\n            ///\n            /// - Default-implementation: Report an error.\n\n            virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n            ///\n            /// - Default-implementation: Report an error.\n\n            virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n            ///\n            /// - Default-implementation: Report an error.\n\n            virtual bool parseComment (const std::string& comment, const TokenLoc& loc,\n                Scanner& scanner);\n            ///< Handle comment token.\n            /// \\return fetch another token?\n            ///\n            /// - Default-implementation: ignored (and return true).\n\n            virtual void parseEOF (Scanner& scanner);\n            ///< Handle EOF token.\n            ///\n            /// - Default-implementation: Report an error.\n\n            virtual void reset();\n            ///< Reset parser to clean state.\n\n            void setOptional (bool optional);\n            ///< Optional mode: If nothign has been parsed yet and an unexpected token is delivered, stop\n            /// parsing without raising an exception (after a reset the parser is in non-optional mode).\n\n            void start();\n            ///< Mark parser as non-empty (at least one token has been parser).\n\n            bool isEmpty() const;\n            ///< Has anything been parsed?\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/quickfileparser.cpp",
    "content": "#include \"quickfileparser.hpp\"\n\n#include \"skipparser.hpp\"\n#include \"scanner.hpp\"\n\nCompiler::QuickFileParser::QuickFileParser (ErrorHandler& errorHandler, const Context& context,\n    Locals& locals)\n: Parser (errorHandler, context), mDeclarationParser (errorHandler, context, locals)\n{}\n\nbool Compiler::QuickFileParser::parseName (const std::string& name, const TokenLoc& loc,\n    Scanner& scanner)\n{\n    SkipParser skip (getErrorHandler(), getContext());\n    scanner.scan (skip);\n    return true;\n}\n\nbool Compiler::QuickFileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n{\n    if (keyword==Scanner::K_end)\n        return false;\n\n    if (keyword==Scanner::K_short || keyword==Scanner::K_long || keyword==Scanner::K_float)\n    {\n        mDeclarationParser.reset();\n        scanner.putbackKeyword (keyword, loc);\n        scanner.scan (mDeclarationParser);\n        return true;\n    }\n\n    SkipParser skip (getErrorHandler(), getContext());\n    scanner.scan (skip);\n    return true;\n}\n\nbool Compiler::QuickFileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n{\n    if (code!=Scanner::S_newline)\n    {\n        SkipParser skip (getErrorHandler(), getContext());\n        scanner.scan (skip);\n    }\n\n    return true;\n}\n\nvoid Compiler::QuickFileParser::parseEOF (Scanner& scanner)\n{\n\n}\n"
  },
  {
    "path": "components/compiler/quickfileparser.hpp",
    "content": "#ifndef COMPILER_QUICKFILEPARSER_H_INCLUDED\n#define COMPILER_QUICKFILEPARSER_H_INCLUDED\n\n#include \"parser.hpp\"\n#include \"declarationparser.hpp\"\n\nnamespace Compiler\n{\n    class Locals;\n\n    /// \\brief File parser variant that ignores everything but variable declarations\n    class QuickFileParser : public Parser\n    {\n            DeclarationParser mDeclarationParser;\n\n        public:\n\n            QuickFileParser (ErrorHandler& errorHandler, const Context& context, Locals& locals);\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void parseEOF (Scanner& scanner) override;\n            ///< Handle EOF token.\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "components/compiler/scanner.cpp",
    "content": "#include \"scanner.hpp\"\n\n#include <cassert>\n\n#include \"exception.hpp\"\n#include \"errorhandler.hpp\"\n#include \"parser.hpp\"\n#include \"extensions.hpp\"\n\n#include <components/misc/stringops.hpp>\n\nnamespace Compiler\n{\n    bool Scanner::get (MultiChar& c)\n    {\n        if (!c.getFrom(mStream))\n            return false;\n\n        mPrevLoc = mLoc;\n\n        if (c=='\\n')\n        {\n            mStrictKeywords = false;\n            mTolerantNames = false;\n            mLoc.mColumn = 0;\n            ++mLoc.mLine;\n            mLoc.mLiteral.clear();\n        }\n        else\n        {\n            ++mLoc.mColumn;\n            c.appendTo(mLoc.mLiteral);\n        }\n\n        return true;\n    }\n\n    void Scanner::putback (MultiChar& c)\n    {\n        c.putback(mStream);\n        mLoc = mPrevLoc;\n    }\n\n    bool Scanner::scanToken (Parser& parser)\n    {\n        switch (mPutback)\n        {\n            case Putback_Special:\n\n                mPutback = Putback_None;\n                return parser.parseSpecial (mPutbackCode, mPutbackLoc, *this);\n\n            case Putback_Integer:\n\n                mPutback = Putback_None;\n                return parser.parseInt (mPutbackInteger, mPutbackLoc, *this);\n\n            case Putback_Float:\n\n                mPutback = Putback_None;\n                return parser.parseFloat (mPutbackFloat, mPutbackLoc, *this);\n\n            case Putback_Name:\n\n                mPutback = Putback_None;\n                return parser.parseName (mPutbackName, mPutbackLoc, *this);\n\n            case Putback_Keyword:\n\n                mPutback = Putback_None;\n                return parser.parseKeyword (mPutbackCode, mPutbackLoc, *this);\n\n            case Putback_None:\n\n                break;\n        }\n\n        MultiChar c;\n\n        if (!get (c))\n        {\n            parser.parseEOF (*this);\n            return false;\n        }\n        else if (c==';')\n        {\n            std::string comment;\n\n            c.appendTo(comment);\n\n            while (get (c))\n            {\n                if (c=='\\n')\n                {\n                    putback (c);\n                    break;\n                }\n                else\n                    c.appendTo(comment);\n            }\n\n            TokenLoc loc (mLoc);\n            mLoc.mLiteral.clear();\n\n            return parser.parseComment (comment, loc, *this);\n        }\n        else if (c.isWhitespace())\n        {\n            mLoc.mLiteral.clear();\n            return true;\n        }\n        else if (c==':')\n        {\n            // treat : as a whitespace :(\n            mLoc.mLiteral.clear();\n            return true;\n        }\n        else if (c.isAlpha() || c=='_' || c=='\"')\n        {\n            bool cont = false;\n\n            if (scanName (c, parser, cont))\n            {\n                mLoc.mLiteral.clear();\n                return cont;\n            }\n        }\n        else if (c.isDigit())\n        {\n            bool cont = false;\n\n            if (scanInt (c, parser, cont))\n            {\n                mLoc.mLiteral.clear();\n                return cont;\n            }\n        }\n        else if (c==13) // linux compatibility hack\n        {\n            return true;\n        }\n        else\n        {\n            bool cont = false;\n\n            if (scanSpecial (c, parser, cont))\n            {\n                mLoc.mLiteral.clear();\n                return cont;\n            }\n        }\n\n        TokenLoc loc (mLoc);\n        mLoc.mLiteral.clear();\n\n        mErrorHandler.error (\"Syntax error\", loc);\n        throw SourceException();\n    }\n\n    bool Scanner::scanInt (MultiChar& c, Parser& parser, bool& cont)\n    {\n        assert(c != '\\0');\n        std::string value;\n        c.appendTo(value);\n\n        bool error = false;\n\n        while (get (c))\n        {\n            if (c.isDigit())\n            {\n                c.appendTo(value);\n            }\n            else if (!c.isMinusSign() && isStringCharacter (c))\n            {\n                error = true;\n                c.appendTo(value);\n            }\n            else if (c=='.')\n            {\n                if (error)\n                {\n                    putback (c);\n                    break;\n                }\n                return scanFloat (value, parser, cont);\n            }\n            else\n            {\n                putback (c);\n                break;\n            }\n        }\n\n        if (error)\n        {\n            /// workaround that allows names to begin with digits\n            /// \\todo disable\n            TokenLoc loc (mLoc);\n            mLoc.mLiteral.clear();\n            cont = parser.parseName (value, loc, *this);\n            return true;\n//            return false;\n        }\n\n        TokenLoc loc (mLoc);\n        mLoc.mLiteral.clear();\n\n        std::istringstream stream (value);\n\n        int intValue = 0;\n        stream >> intValue;\n\n        cont = parser.parseInt (intValue, loc, *this);\n        return true;\n    }\n\n    bool Scanner::scanFloat (const std::string& intValue, Parser& parser, bool& cont)\n    {\n        std::string value = intValue + \".\";\n\n        MultiChar c;\n\n        bool empty = intValue.empty() || intValue==\"-\";\n        bool error = false;\n\n        while (get (c))\n        {\n            if (c.isDigit())\n            {\n                c.appendTo(value);\n                empty = false;\n            }\n            else if (c.isAlpha() || c=='_')\n                error = true;\n            else\n            {\n                putback (c);\n                break;\n            }\n        }\n\n        if (empty || error)\n            return false;\n\n        TokenLoc loc (mLoc);\n        mLoc.mLiteral.clear();\n\n        std::istringstream stream (value);\n\n        float floatValue = 0;\n        stream >> floatValue;\n\n        cont = parser.parseFloat (floatValue, loc, *this);\n        return true;\n    }\n\n    static const char *sKeywords[] =\n    {\n        \"begin\", \"end\",\n        \"short\", \"long\", \"float\",\n        \"if\", \"endif\", \"else\", \"elseif\",\n        \"while\", \"endwhile\",\n        \"return\",\n        \"messagebox\",\n        \"set\", \"to\",\n        \"getsquareroot\",\n        nullptr\n    };\n\n    bool Scanner::scanName (MultiChar& c, Parser& parser, bool& cont)\n    {\n        std::string name;\n        c.appendTo(name);\n\n        if (!scanName (name))\n            return false;\n        else if(name.empty())\n            return true;\n\n        TokenLoc loc (mLoc);\n        mLoc.mLiteral.clear();\n\n        if (name.size()>=2 && name[0]=='\"' && name[name.size()-1]=='\"')\n        {\n            name = name.substr (1, name.size()-2);\n// allow keywords enclosed in \"\"\n/// \\todo optionally disable\n            if (mStrictKeywords)\n            {\n                cont = parser.parseName (name, loc, *this);\n                return true;\n            }\n        }\n\n        int i = 0;\n\n        std::string lowerCase = Misc::StringUtils::lowerCase(name);\n        bool isKeyword = false;\n        for (; sKeywords[i]; ++i)\n            if (lowerCase==sKeywords[i])\n            {\n                isKeyword = true;\n                break;\n            }\n\n        // Russian localization and some mods use a quirk - add newline character directly\n        // to compiled bytecode via HEX-editor to implement multiline messageboxes.\n        // Of course, original editor can not compile such script.\n        // Allow messageboxes to bypass the \"incomplete string or name\" error.\n        if (lowerCase == \"messagebox\")\n            enableIgnoreNewlines();\n        else if (isKeyword)\n            mIgnoreNewline = false;\n\n        if (sKeywords[i])\n        {\n            cont = parser.parseKeyword (i, loc, *this);\n            return true;\n        }\n\n        if (mExtensions)\n        {\n            if (int keyword = mExtensions->searchKeyword (lowerCase))\n            {\n                cont = parser.parseKeyword (keyword, loc, *this);\n                return true;\n            }\n        }\n\n        cont = parser.parseName (name, loc, *this);\n\n        return true;\n    }\n\n    bool Scanner::scanName (std::string& name)\n    {\n        MultiChar c;\n        bool error = false;\n\n        while (get (c))\n        {\n            if (!name.empty() && name[0]=='\"')\n            {\n                if (c=='\"')\n                {\n                    c.appendTo(name);\n                    break;\n                }\n// ignoring escape sequences for now, because they are messing up stupid Windows path names.\n//                else if (c=='\\\\')\n//                {\n//                    if (!get (c))\n//                    {\n//                        error = true;\n//                        mErrorHandler.error (\"incomplete escape sequence\", mLoc);\n//                        break;\n//                    }\n//                }\n                else if (c=='\\n')\n                {\n                    if (mIgnoreNewline)\n                        mErrorHandler.warning (\"string contains newline character, make sure that it is intended\", mLoc);\n                    else\n                    {\n                        bool allWhitespace = true;\n                        for (size_t i = 1; i < name.size(); i++)\n                        {\n                            //ignore comments\n                            if (name[i] == ';')\n                                break;\n                            else if (name[i] != '\\t' && name[i] != ' ' && name[i] != '\\r')\n                            {\n                                allWhitespace = false;\n                                break;\n                            }\n                        }\n                        if (allWhitespace)\n                        {\n                            name.clear();\n                            mLoc.mLiteral.clear();\n                            mErrorHandler.warning (\"unterminated empty string\", mLoc);\n                            return true;\n                        }\n\n                        error = true;\n                        mErrorHandler.error (\"incomplete string or name\", mLoc);\n                        break;\n                    }\n                }\n            }\n            else if (!(c=='\"' && name.empty()))\n            {\n                if (!isStringCharacter (c) && !(mTolerantNames && (c=='.' || c == '-')))\n                {\n                    putback (c);\n                    break;\n                }\n            }\n\n            c.appendTo(name);\n        }\n\n        return !error;\n    }\n\n    bool Scanner::scanSpecial (MultiChar& c, Parser& parser, bool& cont)\n    {\n        int special = -1;\n\n        if (c=='\\n')\n            special = S_newline;\n        else if (c=='(' || c=='[') /// \\todo option to disable the use of [ as alias for (\n            special = S_open;\n        else if (c==')' || c==']')  /// \\todo option to disable the use of ] as alias for )\n            special = S_close;\n        else if (c=='.')\n        {\n            // check, if this starts a float literal\n            if (get (c))\n            {\n                putback (c);\n\n                if (c.isDigit())\n                    return scanFloat (\"\", parser, cont);\n            }\n\n            special = S_member;\n        }\n        else if (c=='=')\n        {\n            if (get (c))\n            {\n                /// \\todo hack to allow a space in comparison operators (add option to disable)\n                if (c==' ' && !get (c))\n                    special = S_cmpEQ;\n                else if (c=='=')\n                    special = S_cmpEQ;\n                else if (c == '>' || c == '<')  // Treat => and =< as ==\n                {\n                    special = S_cmpEQ;\n                    mErrorHandler.warning (std::string(\"invalid operator =\") + c.data() + \", treating it as ==\", mLoc);\n                }\n                else\n                {\n                    special = S_cmpEQ;\n                    putback (c);\n//                    return false;\n/// Allow = as synonym for ==. \\todo optionally disable for post-1.0 scripting improvements.\n                }\n            }\n            else\n            {\n                putback (c);\n                return false;\n            }\n        }\n        else if (c=='!')\n        {\n            if (get (c))\n            {\n                /// \\todo hack to allow a space in comparison operators (add option to disable)\n                if (c==' ' && !get (c))\n                    return false;\n\n                if (c=='=')\n                    special = S_cmpNE;\n                else\n                {\n                    putback (c);\n                    return false;\n                }\n            }\n            else\n                return false;\n        }\n        else if (c.isMinusSign())\n        {\n            if (get (c))\n            {\n                if (c=='>')\n                    special = S_ref;\n                else\n                {\n                    putback (c);\n                    special = S_minus;\n                }\n            }\n            else\n                special = S_minus;\n        }\n        else if (c=='<')\n        {\n            if (get (c))\n            {\n                /// \\todo hack to allow a space in comparison operators (add option to disable)\n                if (c==' ' && !get (c))\n                    special = S_cmpLT;\n                else if (c=='=')\n                {\n                    special = S_cmpLE;\n\n                    if (get (c) && c!='=') // <== is a allowed as an alternative to <=  :(\n                        putback (c);\n                }\n                else if (c == '<' || c == '>') // Treat <> and << as <\n                {\n                    special = S_cmpLT;\n                    mErrorHandler.warning (\"Invalid operator, treating it as <\", mLoc);\n                }\n                else\n                {\n                    putback (c);\n                    special = S_cmpLT;\n                }\n            }\n            else\n                special = S_cmpLT;\n        }\n        else if (c=='>')\n        {\n            if (get (c))\n            {\n                /// \\todo hack to allow a space in comparison operators (add option to disable)\n                if (c==' ' && !get (c))\n                    special = S_cmpGT;\n                else if (c=='=')\n                {\n                    special = S_cmpGE;\n\n                    if (get (c) && c!='=') // >== is a allowed as an alternative to >=  :(\n                        putback (c);\n                }\n                else if (c == '<' || c == '>') // Treat >< and >> as >\n                {\n                    special = S_cmpGT;\n                    mErrorHandler.warning (\"Invalid operator, treating it as >\", mLoc);\n                }\n                else\n                {\n                    putback (c);\n                    special = S_cmpGT;\n                }\n            }\n            else\n                special = S_cmpGT;\n        }\n        else if (c==',')\n            special = S_comma;\n        else if (c=='+')\n            special = S_plus;\n        else if (c=='*')\n            special = S_mult;\n        else if (c=='/')\n            special = S_div;\n        else\n            return false;\n\n        if (special==S_newline)\n            mLoc.mLiteral = \"<newline>\";\n\n        TokenLoc loc (mLoc);\n        mLoc.mLiteral.clear();\n\n        cont = parser.parseSpecial (special, loc, *this);\n\n        return true;\n    }\n\n    bool Scanner::isStringCharacter (MultiChar& c, bool lookAhead)\n    {\n        if (lookAhead && c.isMinusSign())\n        {\n            /// \\todo disable this when doing more stricter compiling. Also, find out who is\n            /// responsible for allowing it in the first place and meet up with that person in\n            /// a dark alley.\n            MultiChar next;\n            if (next.peek(mStream) && isStringCharacter (next, false))\n                return true;\n        }\n\n        return c.isAlpha() || c.isDigit() || c=='_' ||\n            /// \\todo disable this when doing more stricter compiling\n            c=='`' || c=='\\'';\n    }\n\n    // constructor\n\n    Scanner::Scanner (ErrorHandler& errorHandler, std::istream& inputStream,\n        const Extensions *extensions)\n    : mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),\n      mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0),\n      mStrictKeywords (false), mTolerantNames (false), mIgnoreNewline(false)\n    {\n    }\n\n    void Scanner::scan (Parser& parser)\n    {\n        while (scanToken (parser));\n    }\n\n    void Scanner::putbackSpecial (int code, const TokenLoc& loc)\n    {\n        mPutback = Putback_Special;\n        mPutbackCode = code;\n        mPutbackLoc = loc;\n    }\n\n    void Scanner::putbackInt (int value, const TokenLoc& loc)\n    {\n        mPutback = Putback_Integer;\n        mPutbackInteger = value;\n        mPutbackLoc = loc;\n    }\n\n    void Scanner::putbackFloat (float value, const TokenLoc& loc)\n    {\n        mPutback = Putback_Float;\n        mPutbackFloat = value;\n        mPutbackLoc = loc;\n    }\n\n    void Scanner::putbackName (const std::string& name, const TokenLoc& loc)\n    {\n        mPutback = Putback_Name;\n        mPutbackName = name;\n        mPutbackLoc = loc;\n    }\n\n    void Scanner::putbackKeyword (int keyword, const TokenLoc& loc)\n    {\n        mPutback = Putback_Keyword;\n        mPutbackCode = keyword;\n        mPutbackLoc = loc;\n    }\n\n    void Scanner::listKeywords (std::vector<std::string>& keywords)\n    {\n        for (int i=0; Compiler::sKeywords[i]; ++i)\n            keywords.emplace_back(Compiler::sKeywords[i]);\n\n        if (mExtensions)\n            mExtensions->listKeywords (keywords);\n    }\n\n    void Scanner::enableIgnoreNewlines()\n    {\n        mIgnoreNewline = true;\n    }\n\n    void Scanner::enableStrictKeywords()\n    {\n        mStrictKeywords = true;\n    }\n\n    void Scanner::enableTolerantNames()\n    {\n        mTolerantNames = true;\n    }\n}\n"
  },
  {
    "path": "components/compiler/scanner.hpp",
    "content": "#ifndef COMPILER_SCANNER_H_INCLUDED\n#define COMPILER_SCANNER_H_INCLUDED\n\n#include <cctype>\n#include <string>\n#include <iosfwd>\n#include <vector>\n#include <sstream>\n\n#include \"tokenloc.hpp\"\n\nnamespace Compiler\n{\n    class ErrorHandler;\n    class Parser;\n    class Extensions;\n\n    /// \\brief Scanner\n    ///\n    /// This class translate a char-stream to a token stream (delivered via\n    /// parser-callbacks).\n\n    class MultiChar\n    {\n    public:\n        MultiChar()\n        {\n            blank();\n        }\n\n        explicit MultiChar(const char ch)\n        {\n            blank();\n            mData[0] = ch;\n\n            mLength = getCharLength(ch);\n        }\n\n        static int getCharLength(const char ch)\n        {\n            unsigned char c = ch;\n            if (c<=127) return 0;\n            else if ((c & 0xE0) == 0xC0) return 1;\n            else if ((c & 0xF0) == 0xE0) return 2;\n            else if ((c & 0xF8) == 0xF0) return 3;\n            else return -1;\n        }\n\n        bool operator== (const char ch)\n        {\n            return mData[0]==ch && mData[1]==0 && mData[2]==0 && mData[3]==0;\n        }\n\n        bool operator== (const MultiChar& ch)\n        {\n            return mData[0]==ch.mData[0] && mData[1]==ch.mData[1] && mData[2]==ch.mData[2] && mData[3]==ch.mData[3];\n        }\n\n        bool operator!= (const char ch)\n        {\n            return mData[0]!=ch || mData[1]!=0 || mData[2]!=0 || mData[3]!=0;\n        }\n\n        bool isWhitespace()\n        {\n            return (mData[0]==' ' || mData[0]=='\\t') && mData[1]==0 && mData[2]==0 && mData[3]==0;\n        }\n\n        bool isDigit()\n        {\n            return std::isdigit(mData[0]) && mData[1]==0 && mData[2]==0 && mData[3]==0;\n        }\n\n        bool isMinusSign()\n        {\n            if (mData[0] == '-' && mData[1] == 0 && mData[2] == 0 && mData[3] == 0)\n                return true;\n\n            return mData[0] == '\\xe2' && mData[1] == '\\x80' && mData[2] == '\\x93' && mData[3] == 0;\n        }\n\n        bool isAlpha()\n        {\n            if (isMinusSign())\n                return false;\n\n            return std::isalpha(mData[0]) || mData[1]!=0 || mData[2]!=0 || mData[3]!=0;\n        }\n\n        void appendTo(std::string& str)\n        {\n            for (int i = 0; i <= mLength; i++)\n                str += mData[i];\n        }\n\n        void putback (std::istream& in)\n        {\n            for (int i = mLength; i >= 0; i--)\n                in.putback (mData[i]);\n        }\n\n        bool getFrom(std::istream& in)\n        {\n            blank();\n\n            char ch = static_cast<char>(in.peek());\n\n            if (!in.good())\n                return false;\n\n            int length = getCharLength(ch);\n            if (length < 0) return false;\n\n            for (int i = 0; i <= length; i++)\n            {\n                in.get (ch);\n\n                if (!in.good())\n                    return false;\n\n                mData[i] = ch;\n            }\n\n            mLength = length;\n\n            return true;\n        }\n\n        bool peek(std::istream& in)\n        {\n            std::streampos p_orig = in.tellg();\n\n            char ch = static_cast<char>(in.peek());\n\n            if (!in.good())\n                return false;\n\n            int length = getCharLength(ch);\n            if (length < 0) return false;\n\n            for (int i = 0; i <= length; i++)\n            {\n                in.get (ch);\n\n                if (!in.good())\n                    return false;\n\n                mData[i] = ch;\n            }\n\n            mLength = length;\n\n            in.seekg(p_orig);\n            return true;\n        };\n\n        void blank()\n        {\n            std::fill(std::begin(mData), std::end(mData), '\\0');\n            mLength = -1;\n        }\n\n        std::string data()\n        {\n            // NB: mLength is the number of the last element in the array\n            return std::string(mData, mLength + 1);\n        }\n\n    private:\n        char mData[4]{};\n        int mLength{};\n    };\n\n    class Scanner\n    {\n            enum putback_type\n            {\n                Putback_None, Putback_Special, Putback_Integer, Putback_Float,\n                Putback_Name, Putback_Keyword\n            };\n\n            ErrorHandler& mErrorHandler;\n            TokenLoc mLoc;\n            TokenLoc mPrevLoc;\n            std::istream& mStream;\n            const Extensions *mExtensions;\n            putback_type mPutback;\n            int mPutbackCode;\n            int mPutbackInteger;\n            float mPutbackFloat;\n            std::string mPutbackName;\n            TokenLoc mPutbackLoc;\n            bool mStrictKeywords;\n            bool mTolerantNames;\n            bool mIgnoreNewline;\n\n        public:\n\n            enum keyword\n            {\n                K_begin, K_end,\n                K_short, K_long, K_float,\n                K_if, K_endif, K_else, K_elseif,\n                K_while, K_endwhile,\n                K_return,\n                K_messagebox,\n                K_set, K_to,\n                K_getsquareroot\n            };\n\n            enum special\n            {\n                S_newline,\n                S_open, S_close,\n                S_cmpEQ, S_cmpNE, S_cmpLT, S_cmpLE, S_cmpGT, S_cmpGE,\n                S_plus, S_minus, S_mult, S_div,\n                S_comma,\n                S_ref,\n                S_member\n            };\n\n        private:\n\n        // not implemented\n\n            Scanner (const Scanner&);\n            Scanner& operator= (const Scanner&);\n\n            bool get (MultiChar& c);\n\n            void putback (MultiChar& c);\n\n            bool scanToken (Parser& parser);\n\n            bool scanInt (MultiChar& c, Parser& parser, bool& cont);\n\n            bool scanFloat (const std::string& intValue, Parser& parser, bool& cont);\n\n            bool scanName (MultiChar& c, Parser& parser, bool& cont);\n\n            /// \\param name May contain the start of the name (one or more characters)\n            bool scanName (std::string& name);\n\n            bool scanSpecial (MultiChar& c, Parser& parser, bool& cont);\n\n            bool isStringCharacter (MultiChar& c, bool lookAhead = true);\n\n        public:\n\n            Scanner (ErrorHandler& errorHandler, std::istream& inputStream,\n                const Extensions *extensions = nullptr);\n            ///< constructor\n\n            void scan (Parser& parser);\n            ///< Scan a token and deliver it to the parser.\n\n            void putbackSpecial (int code, const TokenLoc& loc);\n            ///< put back a special token\n\n            void putbackInt (int value, const TokenLoc& loc);\n            ///< put back an integer token\n\n            void putbackFloat (float value, const TokenLoc& loc);\n            ///< put back a float token\n\n            void putbackName (const std::string& name, const TokenLoc& loc);\n            ///< put back a name token\n\n            void putbackKeyword (int keyword, const TokenLoc& loc);\n            ///< put back a keyword token\n\n            void listKeywords (std::vector<std::string>& keywords);\n            ///< Append all known keywords to \\a keywords.\n\n            /// Treat newline character as a part of script command.\n            ///\n            /// \\attention This mode lasts only until the next keyword is reached.\n            void enableIgnoreNewlines();\n\n            /// Do not accept keywords in quotation marks anymore.\n            ///\n            /// \\attention This mode lasts only until the next newline is reached.\n            void enableStrictKeywords();\n\n            /// Continue parsing a name when hitting a '.' or a '-'\n            ///\n            /// \\attention This mode lasts only until the next newline is reached.\n            void enableTolerantNames();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/scriptparser.cpp",
    "content": "#include \"scriptparser.hpp\"\n\n#include \"scanner.hpp\"\n#include \"skipparser.hpp\"\n#include \"errorhandler.hpp\"\n\nnamespace Compiler\n{\n    ScriptParser::ScriptParser (ErrorHandler& errorHandler, const Context& context,\n        Locals& locals, bool end)\n    : Parser (errorHandler, context), mOutput (locals),\n      mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()),\n      mControlParser (errorHandler, context, locals, mOutput.getLiterals()),\n      mEnd (end)\n    {}\n\n    void ScriptParser::getCode (std::vector<Interpreter::Type_Code>& code) const\n    {\n        mOutput.getCode (code);\n    }\n\n    bool ScriptParser::parseName (const std::string& name, const TokenLoc& loc,\n        Scanner& scanner)\n    {\n        mLineParser.reset();\n        if (mLineParser.parseName (name, loc, scanner))\n            scanner.scan (mLineParser);\n\n        return true;\n    }\n\n    bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (keyword==Scanner::K_while || keyword==Scanner::K_if || keyword==Scanner::K_elseif)\n        {\n            mControlParser.reset();\n            if (mControlParser.parseKeyword (keyword, loc, scanner))\n                scanner.scan (mControlParser);\n\n            mControlParser.appendCode (mOutput.getCode());\n\n            return true;\n        }\n\n        /// \\todo add an option to disable this nonsense\n        if (keyword==Scanner::K_endif)\n        {\n            // surplus endif\n            getErrorHandler().warning (\"endif without matching if/elseif\", loc);\n\n            SkipParser skip (getErrorHandler(), getContext());\n            scanner.scan (skip);\n            return true;\n        }\n\n        if (keyword==Scanner::K_end && mEnd)\n        {\n            return false;\n        }\n\n        mLineParser.reset();\n        if (mLineParser.parseKeyword (keyword, loc, scanner))\n            scanner.scan (mLineParser);\n\n        return true;\n    }\n\n    bool ScriptParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (code==Scanner::S_newline) // empty line\n            return true;\n\n        if (code==Scanner::S_open) /// \\todo Option to switch this off\n        {\n            scanner.putbackSpecial (code, loc);\n            return parseKeyword (Scanner::K_if, loc, scanner);\n        }\n\n        mLineParser.reset();\n        if (mLineParser.parseSpecial (code, loc, scanner))\n            scanner.scan (mLineParser);\n\n        return true;\n    }\n\n    void ScriptParser::parseEOF (Scanner& scanner)\n    {\n        if (mEnd)\n            Parser::parseEOF (scanner);\n    }\n\n    void ScriptParser::reset()\n    {\n        mLineParser.reset();\n        mOutput.clear();\n    }\n}\n"
  },
  {
    "path": "components/compiler/scriptparser.hpp",
    "content": "#ifndef COMPILER_SCRIPTPARSER_H_INCLUDED\n#define COMPILER_SCRIPTPARSER_H_INCLUDED\n\n#include \"parser.hpp\"\n#include \"lineparser.hpp\"\n#include \"controlparser.hpp\"\n#include \"output.hpp\"\n\nnamespace Compiler\n{\n    class Locals;\n\n    // Script parser, to be used in dialogue scripts and as part of FileParser\n\n    class ScriptParser : public Parser\n    {\n            Output mOutput;\n            LineParser mLineParser;\n            ControlParser mControlParser;\n            bool mEnd;\n\n        public:\n\n            /// \\param end of script is marked by end keyword.\n            ScriptParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,\n                bool end = false);\n\n            void getCode (std::vector<Interpreter::Type_Code>& code) const;\n            ///< store generated code in \\a code.\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void parseEOF (Scanner& scanner) override;\n            ///< Handle EOF token.\n\n            void reset() override;\n            ///< Reset parser to clean state.\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "components/compiler/skipparser.cpp",
    "content": "#include \"skipparser.hpp\"\n\n#include \"scanner.hpp\"\n\nnamespace Compiler\n{\n    SkipParser::SkipParser (ErrorHandler& errorHandler, const Context& context)\n    : Parser (errorHandler, context)\n    {}\n\n    bool SkipParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)\n    {\n        return true;\n    }\n\n    bool SkipParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)\n    {\n        return true;\n    }\n\n    bool SkipParser::parseName (const std::string& name, const TokenLoc& loc,\n        Scanner& scanner)\n    {\n        return true;\n    }\n\n    bool SkipParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        return true;\n    }\n\n    bool SkipParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (code==Scanner::S_newline)\n            return false;\n\n        return true;\n    }\n}\n\n"
  },
  {
    "path": "components/compiler/skipparser.hpp",
    "content": "#ifndef COMPILER_SKIPPARSER_H_INCLUDED\n#define COMPILER_SKIPPARSER_H_INCLUDED\n\n#include \"parser.hpp\"\n\nnamespace Compiler\n{\n    // \\brief Skip parser for skipping a line\n    //\n    // This parser is mainly intended for skipping the rest of a faulty line.\n\n    class SkipParser : public Parser\n    {\n        public:\n\n            SkipParser (ErrorHandler& errorHandler, const Context& context);\n\n            bool parseInt (int value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle an int token.\n            /// \\return fetch another token?\n\n            bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a float token.\n            /// \\return fetch another token?\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "components/compiler/streamerrorhandler.cpp",
    "content": "#include \"streamerrorhandler.hpp\"\n\n#include <sstream>\n\n#include <components/debug/debuglog.hpp>\n\n#include \"tokenloc.hpp\"\n\nnamespace Compiler\n{\n    // Report error to the user.\n\n    void StreamErrorHandler::report (const std::string& message, const TokenLoc& loc,\n        Type type)\n    {\n        Debug::Level logLevel = Debug::Info; // Usually script warnings are not too important\n        if (type == ErrorMessage)\n            logLevel = Debug::Error;\n\n        std::stringstream text;\n\n        if (type==ErrorMessage)\n            text << \"Error: \";\n        else\n            text << \"Warning: \";\n\n        if (!mContext.empty())\n            text << mContext << \" \";\n\n        text << \"line \" << loc.mLine+1 << \", column \" << loc.mColumn+1\n             << \" (\" << loc.mLiteral << \"): \" << message;\n\n        Log(logLevel) << text.str();\n    }\n\n    // Report a file related error\n\n    void StreamErrorHandler::report (const std::string& message, Type type)\n    {\n        Debug::Level logLevel = Debug::Info;\n        if (type==ErrorMessage)\n            logLevel = Debug::Error;\n\n        std::stringstream text;\n\n        if (type==ErrorMessage)\n            text << \"Error: \";\n        else\n            text << \"Warning: \";\n\n        if (!mContext.empty())\n            text << mContext << \" \";\n\n        text << \"file: \" << message << std::endl;\n\n        Log(logLevel) << text.str();\n    }\n\n    void StreamErrorHandler::setContext(const std::string &context)\n    {\n        mContext = context;\n    }\n\n    StreamErrorHandler::StreamErrorHandler() = default;\n\n    ContextOverride::ContextOverride(StreamErrorHandler& handler, const std::string& context) : mHandler(handler), mContext(handler.mContext)\n    {\n        mHandler.setContext(context);\n    }\n\n    ContextOverride::~ContextOverride()\n    {\n        mHandler.setContext(mContext);\n    }\n}\n"
  },
  {
    "path": "components/compiler/streamerrorhandler.hpp",
    "content": "#ifndef COMPILER_STREAMERRORHANDLER_H_INCLUDED\n#define COMPILER_STREAMERRORHANDLER_H_INCLUDED\n\n#include <ostream>\n\n#include \"errorhandler.hpp\"\n\nnamespace Compiler\n{\n    class ContextOverride;\n    /// \\brief Error handler implementation: Write errors into logging stream\n\n    class StreamErrorHandler : public ErrorHandler\n    {\n            std::string mContext;\n\n            friend class ContextOverride;\n        // not implemented\n\n            StreamErrorHandler (const StreamErrorHandler&);\n            StreamErrorHandler& operator= (const StreamErrorHandler&);\n\n            void report (const std::string& message, const TokenLoc& loc, Type type) override;\n            ///< Report error to the user.\n\n            void report (const std::string& message, Type type) override;\n            ///< Report a file related error\n\n        public:\n\n            void setContext(const std::string& context);\n\n        // constructors\n\n            StreamErrorHandler ();\n            ///< constructor\n    };\n\n    class ContextOverride\n    {\n            StreamErrorHandler& mHandler;\n            const std::string mContext;\n        public:\n            ContextOverride (StreamErrorHandler& handler, const std::string& context);\n\n            ContextOverride (const ContextOverride&) = delete;\n            ContextOverride& operator= (const ContextOverride&) = delete;\n\n            ~ContextOverride();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/stringparser.cpp",
    "content": "#include \"stringparser.hpp\"\n\n#include <algorithm>\n#include <iterator>\n\n#include <components/misc/stringops.hpp>\n\n#include \"scanner.hpp\"\n#include \"generator.hpp\"\n#include \"context.hpp\"\n#include \"extensions.hpp\"\n\nnamespace Compiler\n{\n    StringParser::StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals)\n    : Parser (errorHandler, context), mLiterals (literals), mState (StartState), mSmashCase (false), mDiscard (false)\n    {\n\n    }\n\n    bool StringParser::parseName (const std::string& name, const TokenLoc& loc,\n        Scanner& scanner)\n    {\n        if (mState==StartState || mState==CommaState)\n        {\n            start();\n            mTokenLoc = loc;\n\n            if (!mDiscard)\n            {\n                if (mSmashCase)\n                    Generator::pushString (mCode, mLiterals, Misc::StringUtils::lowerCase (name));\n                else\n                    Generator::pushString (mCode, mLiterals, name);\n            }\n\n            return false;\n        }\n\n        return Parser::parseName (name, loc, scanner);\n    }\n\n    bool StringParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (const Extensions *extensions = getContext().getExtensions())\n        {\n            std::string argumentType; // ignored\n            bool hasExplicit = false; // ignored\n            if (extensions->isInstruction (keyword, argumentType, hasExplicit))\n            {\n                // pretend this is not a keyword\n                std::string name = loc.mLiteral;\n                if (name.size()>=2 && name[0]=='\"' && name[name.size()-1]=='\"')\n                    name = name.substr (1, name.size()-2);\n                return parseName (name, loc, scanner);\n            }\n        }\n\n        if (keyword==Scanner::K_end || keyword==Scanner::K_begin ||\n            keyword==Scanner::K_short || keyword==Scanner::K_long ||\n            keyword==Scanner::K_float || keyword==Scanner::K_if ||\n            keyword==Scanner::K_endif || keyword==Scanner::K_else ||\n            keyword==Scanner::K_elseif || keyword==Scanner::K_while ||\n            keyword==Scanner::K_endwhile || keyword==Scanner::K_return ||\n            keyword==Scanner::K_messagebox || keyword==Scanner::K_set ||\n            keyword==Scanner::K_to || keyword==Scanner::K_getsquareroot)\n        {\n            return parseName (loc.mLiteral, loc, scanner);\n        }\n\n        return Parser::parseKeyword (keyword, loc, scanner);\n    }\n\n    bool StringParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)\n    {\n        if (code==Scanner::S_comma && mState==StartState)\n        {\n            mState = CommaState;\n            return true;\n        }\n\n        return Parser::parseSpecial (code, loc, scanner);\n    }\n\n    void StringParser::append (std::vector<Interpreter::Type_Code>& code)\n    {\n        std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));\n    }\n\n    void StringParser::reset()\n    {\n        mState = StartState;\n        mCode.clear();\n        mSmashCase = false;\n        mTokenLoc = TokenLoc();\n        mDiscard = false;\n        Parser::reset();\n    }\n\n    void StringParser::smashCase()\n    {\n        mSmashCase = true;\n    }\n\n    const TokenLoc& StringParser::getTokenLoc() const\n    {\n        return mTokenLoc;\n    }\n\n    void StringParser::discard()\n    {\n        mDiscard = true;\n    }\n}\n"
  },
  {
    "path": "components/compiler/stringparser.hpp",
    "content": "#ifndef COMPILER_STRINGPARSER_H_INCLUDED\n#define COMPILER_STRINGPARSER_H_INCLUDED\n\n#include <vector>\n\n#include <components/interpreter/types.hpp>\n\n#include \"parser.hpp\"\n#include \"tokenloc.hpp\"\n\nnamespace Compiler\n{\n    class Literals;\n\n    class StringParser : public Parser\n    {\n            enum State\n            {\n                StartState, CommaState\n            };\n\n            Literals& mLiterals;\n            State mState;\n            std::vector<Interpreter::Type_Code> mCode;\n            bool mSmashCase;\n            TokenLoc mTokenLoc;\n            bool mDiscard;\n\n        public:\n\n            StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals);\n\n            bool parseName (const std::string& name, const TokenLoc& loc,\n                Scanner& scanner) override;\n            ///< Handle a name token.\n            /// \\return fetch another token?\n\n            bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a keyword token.\n            /// \\return fetch another token?\n\n            bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) override;\n            ///< Handle a special character token.\n            /// \\return fetch another token?\n\n            void append (std::vector<Interpreter::Type_Code>& code);\n            ///< Append code for parsed string.\n\n            void smashCase();\n            ///< Transform all scanned strings to lower case\n\n            void reset() override;\n            ///< Reset parser to clean state (this includes the smashCase function).\n\n            /// Returns TokenLoc object for string. If no string has been parsed, the TokenLoc\n            /// object will be default initialised.\n            const TokenLoc& getTokenLoc() const;\n\n            /// If parsing a string, do not add it to the literal table and do not create code\n            /// for it.\n            void discard();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/compiler/tokenloc.hpp",
    "content": "#ifndef COMPILER_TOKENLOC_H_INCLUDED\n#define COMPILER_TOKENLOC_H_INCLUDED\n\n#include <string>\n\nnamespace Compiler\n{\n    /// \\brief Location of a token in a source file\n\n    struct TokenLoc\n    {\n        int mColumn;\n        int mLine;\n        std::string mLiteral;\n\n        TokenLoc() : mColumn (0), mLine (0), mLiteral () {}\n    };\n}\n\n#endif // TOKENLOC_H_INCLUDED\n"
  },
  {
    "path": "components/config/gamesettings.cpp",
    "content": "#include \"gamesettings.hpp\"\n#include \"launchersettings.hpp\"\n\n#include <QTextCodec>\n#include <QDir>\n#include <QRegExp>\n\n#include <components/files/configurationmanager.hpp>\n\nconst char Config::GameSettings::sContentKey[] = \"content\";\n\nConfig::GameSettings::GameSettings(Files::ConfigurationManager &cfg)\n    : mCfgMgr(cfg)\n{\n}\n\nvoid Config::GameSettings::validatePaths()\n{\n    QStringList paths = mSettings.values(QString(\"data\"));\n    Files::PathContainer dataDirs;\n\n    for (const QString &path : paths)\n    {\n        QByteArray bytes = path.toUtf8();\n        dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length())));\n    }\n\n    // Parse the data dirs to convert the tokenized paths\n    mCfgMgr.processPaths(dataDirs);\n    mDataDirs.clear();\n\n    for (auto & dataDir : dataDirs) {\n        QString path = QString::fromUtf8(dataDir.string().c_str());\n\n        QDir dir(path);\n        if (dir.exists())\n            mDataDirs.append(path);\n    }\n\n    // Do the same for data-local\n    QString local = mSettings.value(QString(\"data-local\"));\n    if (local.length() && local.at(0) == QChar('\\\"'))\n    {\n        local.remove(0, 1);\n        local.chop(1);\n    }\n\n    if (local.isEmpty())\n        return;\n\n    dataDirs.clear();\n    QByteArray bytes = local.toUtf8();\n    dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length())));\n\n    mCfgMgr.processPaths(dataDirs);\n\n    if (!dataDirs.empty()) {\n        QString path = QString::fromUtf8(dataDirs.front().string().c_str());\n\n        QDir dir(path);\n        if (dir.exists())\n            mDataLocal = path;\n    }\n}\n\nQStringList Config::GameSettings::values(const QString &key, const QStringList &defaultValues) const\n{\n    if (!mSettings.values(key).isEmpty())\n        return mSettings.values(key);\n    return defaultValues;\n}\n\nbool Config::GameSettings::readFile(QTextStream &stream)\n{\n    return readFile(stream, mSettings);\n}\n\nbool Config::GameSettings::readUserFile(QTextStream &stream)\n{\n    return readFile(stream, mUserSettings);\n}\n\nbool Config::GameSettings::readFile(QTextStream &stream, QMultiMap<QString, QString> &settings)\n{\n    QMultiMap<QString, QString> cache;\n    QRegExp keyRe(\"^([^=]+)\\\\s*=\\\\s*(.+)$\");\n\n    while (!stream.atEnd()) {\n        QString line = stream.readLine();\n\n        if (line.isEmpty() || line.startsWith(\"#\"))\n            continue;\n\n        if (keyRe.indexIn(line) != -1) {\n\n            QString key = keyRe.cap(1).trimmed();\n            QString value = keyRe.cap(2).trimmed();\n\n            // Don't remove composing entries\n            if (key != QLatin1String(\"data\")\n                && key != QLatin1String(\"fallback-archive\")\n                && key != QLatin1String(\"content\")\n                && key != QLatin1String(\"groundcover\")\n                && key != QLatin1String(\"script-blacklist\"))\n                settings.remove(key);\n\n            if (key == QLatin1String(\"data\")\n                || key == QLatin1String(\"data-local\")\n                || key == QLatin1String(\"resources\")\n                || key == QLatin1String(\"load-savegame\"))\n            {\n                // Path line (e.g. 'data=...'), so needs processing to deal with ampersands and quotes\n                // The following is based on boost::io::detail::quoted_manip.hpp, but calling those functions did not work as there are too may QStrings involved\n                QChar delim = '\\\"';\n                QChar escape = '&';\n\n                if (value.at(0) == delim)\n                {\n                    QString valueOriginal = value;\n                    value = \"\";\n\n                    for (QString::const_iterator it = valueOriginal.begin() + 1; it != valueOriginal.end(); ++it)\n                    {\n                        if (*it == escape)\n                            ++it;\n                        else if (*it == delim)\n                            break;\n                        value += *it;\n                    }\n                }\n            }\n\n            QStringList values = cache.values(key);\n            values.append(settings.values(key));\n\n            if (!values.contains(value)) {\n                cache.insert(key, value);\n            }\n        }\n    }\n\n    if (settings.isEmpty()) {\n        settings = cache; // This is the first time we read a file\n        validatePaths();\n        return true;\n    }\n\n    // Merge the changed keys with those which didn't\n    settings.unite(cache);\n    validatePaths();\n\n    return true;\n}\n\nbool Config::GameSettings::writeFile(QTextStream &stream)\n{\n    // Iterate in reverse order to preserve insertion order\n    QMapIterator<QString, QString> i(mUserSettings);\n    i.toBack();\n\n    while (i.hasPrevious()) {\n        i.previous();\n\n        // path lines (e.g. 'data=...') need quotes and ampersands escaping to match how boost::filesystem::path uses boost::io::quoted\n        if (i.key() == QLatin1String(\"data\")\n            || i.key() == QLatin1String(\"data-local\")\n            || i.key() == QLatin1String(\"resources\")\n            || i.key() == QLatin1String(\"load-savegame\"))\n        {\n            stream << i.key() << \"=\";\n\n            // The following is based on boost::io::detail::quoted_manip.hpp, but calling those functions did not work as there are too may QStrings involved\n            QChar delim = '\\\"';\n            QChar escape = '&';\n            QString string = i.value();\n\n            stream << delim;\n            for (auto it : string)\n            {\n                if (it == delim || it == escape)\n                    stream << escape;\n                stream << it;\n            }\n            stream << delim;\n\n            stream << '\\n';\n            continue;\n        }\n\n        stream << i.key() << \"=\" << i.value() << \"\\n\";\n\n    }\n\n    return true;\n}\n\nbool Config::GameSettings::isOrderedLine(const QString& line)\n{\n    return line.contains(QRegExp(\"^\\\\s*fallback-archive\\\\s*=\"))\n           || line.contains(QRegExp(\"^\\\\s*fallback\\\\s*=\"))\n           || line.contains(QRegExp(\"^\\\\s*data\\\\s*=\"))\n           || line.contains(QRegExp(\"^\\\\s*data-local\\\\s*=\"))\n           || line.contains(QRegExp(\"^\\\\s*resources\\\\s*=\"))\n           || line.contains(QRegExp(\"^\\\\s*groundcover\\\\s*=\"))\n           || line.contains(QRegExp(\"^\\\\s*content\\\\s*=\"));\n}\n\n// Policy:\n//\n// - Always ignore a line beginning with '#' or empty lines; added above a config\n//   entry.\n//\n// - If a line in file exists with matching key and first part of value (before ',',\n//   '\\n', etc) also matches, then replace the line with that of mUserSettings.\n// - else remove line\n//\n// - If there is no corresponding line in file, add at the end\n//\n// - Removed content items are saved as comments if the item had any comments.\n//   Content items prepended with '##' are considered previously removed.\n//\nbool Config::GameSettings::writeFileWithComments(QFile &file)\n{\n    QTextStream stream(&file);\n    stream.setCodec(QTextCodec::codecForName(\"UTF-8\"));\n\n    // slurp\n    std::vector<QString> fileCopy;\n    QString line = stream.readLine();\n    while (!line.isNull())\n    {\n        fileCopy.push_back(line);\n        line = stream.readLine();\n    }\n    stream.seek(0);\n\n    // empty file, no comments to keep\n    if (fileCopy.empty())\n        return writeFile(stream);\n\n    // start\n    //   |\n    //   |    +----------------------------------------------------------+\n    //   |    |                                                          |\n    //   v    v                                                          |\n    // skip non-\"ordered\" lines (remove \"ordered\" lines)                 |\n    //   |              ^                                                |\n    //   |              |                                                |\n    //   |      non-\"ordered\" line, write saved comments                 |\n    //   |              ^                                                |\n    //   v              |                                                |\n    // blank or comment line, save in temp buffer <--------+             |\n    //        |                |                           |             |\n    //        |                +------- comment line ------+             |\n    //        v                    (special processing '##')             |\n    //  \"ordered\" line                                                   |\n    //        |                                                          |\n    //        v                                                          |\n    // save in a separate map of comments keyed by \"ordered\" line        |\n    //        |                                                          |\n    //        +----------------------------------------------------------+\n    //\n    //\n    QRegExp settingRegex(\"^([^=]+)\\\\s*=\\\\s*([^,]+)(.*)$\");\n    std::vector<QString> comments;\n    auto commentStart = fileCopy.end();\n    std::map<QString, std::vector<QString> > commentsMap;\n    for (auto iter = fileCopy.begin(); iter != fileCopy.end(); ++iter)\n    {\n        if (isOrderedLine(*iter))\n        {\n            // save in a separate map of comments keyed by \"ordered\" line\n            if (!comments.empty())\n            {\n                if (settingRegex.indexIn(*iter) != -1)\n                {\n                    commentsMap[settingRegex.cap(1)+\"=\"+settingRegex.cap(2)] = comments;\n                    comments.clear();\n                    commentStart = fileCopy.end();\n                }\n                // else do nothing, malformed line\n            }\n\n            *iter = QString(); // \"ordered\" lines to be removed later\n        }\n        else if ((*iter).isEmpty() || (*iter).contains(QRegExp(\"^\\\\s*#\")))\n        {\n            // comment line, save in temp buffer\n            if (comments.empty())\n                commentStart = iter;\n\n            // special removed content processing\n            if ((*iter).contains(QRegExp(\"^##content\\\\s*=\")))\n            {\n                if (!comments.empty())\n                {\n                    commentsMap[*iter] = comments;\n                    comments.clear();\n                    commentStart = fileCopy.end();\n                }\n            }\n            else\n                comments.push_back(*iter);\n\n            *iter = QString(); // assume to be deleted later\n        }\n        else\n        {\n            int index = settingRegex.indexIn(*iter);\n\n            // blank or non-\"ordered\" line, write saved comments\n            if (!comments.empty() && index != -1 && settingRegex.captureCount() >= 2 &&\n                mUserSettings.find(settingRegex.cap(1)) != mUserSettings.end())\n            {\n                if (commentStart == fileCopy.end())\n                    throw std::runtime_error(\"Config::GameSettings: failed to parse settings - iterator is past of end of settings file\");\n\n                for (const auto & comment : comments)\n                {\n                    *commentStart = comment;\n                    ++commentStart;\n                }\n                comments.clear();\n                commentStart = fileCopy.end();\n            }\n\n            // keep blank lines and non-\"ordered\" lines other than comments\n\n            // look for a key in the line\n            if (index == -1 || settingRegex.captureCount() < 2)\n            {\n                // no key or first part of value found in line, replace with a null string which\n                // will be remved later\n                *iter = QString();\n                comments.clear();\n                commentStart = fileCopy.end();\n                continue;\n            }\n\n            // look for a matching key in user settings\n            *iter = QString(); // assume no match\n            QString key = settingRegex.cap(1);\n            QString keyVal = settingRegex.cap(1)+\"=\"+settingRegex.cap(2);\n            QMultiMap<QString, QString>::const_iterator i = mUserSettings.find(key);\n            while (i != mUserSettings.end() && i.key() == key)\n            {\n                QString settingLine = i.key() + \"=\" + i.value();\n                if (settingRegex.indexIn(settingLine) != -1)\n                {\n                    if ((settingRegex.cap(1)+\"=\"+settingRegex.cap(2)) == keyVal)\n                    {\n                        *iter = settingLine;\n                        break;\n                    }\n                }\n                ++i;\n            }\n        }\n    }\n\n    // comments at top of file\n    for (auto & iter : fileCopy)\n    {\n        if (iter.isNull())\n            continue;\n\n        // Below is based on readFile() code, if that changes corresponding change may be\n        // required (for example duplicates may be inserted if the rules don't match)\n        if (/*(*iter).isEmpty() ||*/ iter.contains(QRegExp(\"^\\\\s*#\")))\n        {\n            stream << iter << \"\\n\";\n            continue;\n        }\n    }\n\n    // Iterate in reverse order to preserve insertion order\n    QString settingLine;\n    QMapIterator<QString, QString> it(mUserSettings);\n    it.toBack();\n\n    while (it.hasPrevious())\n    {\n        it.previous();\n\n        if (it.key() == QLatin1String(\"data\")\n            || it.key() == QLatin1String(\"data-local\")\n            || it.key() == QLatin1String(\"resources\")\n            || it.key() == QLatin1String(\"load-savegame\"))\n        {\n            settingLine = it.key() + \"=\";\n\n            // The following is based on boost::io::detail::quoted_manip.hpp, but calling those functions did not work as there are too may QStrings involved\n            QChar delim = '\\\"';\n            QChar escape = '&';\n            QString string = it.value();\n\n            settingLine += delim;\n            for (auto iter : string)\n            {\n                if (iter == delim || iter == escape)\n                    settingLine += escape;\n                settingLine += iter;\n            }\n            settingLine += delim;\n        }\n        else\n            settingLine = it.key() + \"=\" + it.value();\n\n        if (settingRegex.indexIn(settingLine) != -1)\n        {\n            auto i = commentsMap.find(settingRegex.cap(1)+\"=\"+settingRegex.cap(2));\n\n            // check if previous removed content item with comments\n            if (i == commentsMap.end())\n                i = commentsMap.find(\"##\"+settingRegex.cap(1)+\"=\"+settingRegex.cap(2));\n\n            if (i != commentsMap.end())\n            {\n                std::vector<QString> cLines = i->second;\n                for (const auto & cLine : cLines)\n                    stream << cLine << \"\\n\";\n\n                commentsMap.erase(i);\n            }\n        }\n\n        stream << settingLine << \"\\n\";\n    }\n\n    // flush any removed settings\n    if (!commentsMap.empty())\n    {\n        auto i = commentsMap.begin();\n        for (; i != commentsMap.end(); ++i)\n        {\n            if (i->first.contains(QRegExp(\"^\\\\s*content\\\\s*=\")))\n            {\n                std::vector<QString> cLines = i->second;\n                for (const auto & cLine : cLines)\n                    stream << cLine << \"\\n\";\n\n                // mark the content line entry for future preocessing\n                stream << \"##\" << i->first << \"\\n\";\n\n                //commentsMap.erase(i);\n            }\n        }\n    }\n\n    // flush any end comments\n    if (!comments.empty())\n    {\n        for (const auto & comment : comments)\n            stream << comment << \"\\n\";\n    }\n\n    file.resize(file.pos());\n\n    return true;\n}\n\nbool Config::GameSettings::hasMaster()\n{\n    bool result = false;\n    QStringList content = mSettings.values(QString(Config::GameSettings::sContentKey));\n    for (int i = 0; i < content.count(); ++i) \n    {\n        if (content.at(i).endsWith(QLatin1String(\".omwgame\"), Qt::CaseInsensitive) || content.at(i).endsWith(QLatin1String(\".esm\"), Qt::CaseInsensitive)) \n        {\n            result = true;\n            break;\n        }\n    }\n\n    return result;\n}\n\nvoid Config::GameSettings::setContentList(const QStringList& fileNames)\n{\n    remove(sContentKey);\n    for (const QString& fileName : fileNames)\n    {\n        setMultiValue(sContentKey, fileName);\n    }\n}\n\nQStringList Config::GameSettings::getContentList() const\n{\n    // QMap returns multiple rows in LIFO order, so need to reverse\n    return Config::LauncherSettings::reverse(values(sContentKey));\n}\n\nvoid Config::GameSettings::clear()\n{\n    mSettings.clear();\n    mUserSettings.clear();\n    mDataDirs.clear();\n    mDataLocal.clear();\n}\n\n"
  },
  {
    "path": "components/config/gamesettings.hpp",
    "content": "#ifndef GAMESETTINGS_HPP\n#define GAMESETTINGS_HPP\n\n#include <QTextStream>\n#include <QStringList>\n#include <QString>\n#include <QFile>\n#include <QMultiMap>\n\n#include <boost/filesystem/path.hpp>\n\nnamespace Files\n{\n  typedef std::vector<boost::filesystem::path> PathContainer;\n  struct ConfigurationManager;\n}\n\nnamespace Config\n{\n    class GameSettings\n    {\n    public:\n        GameSettings(Files::ConfigurationManager &cfg);\n\n        inline QString value(const QString &key, const QString &defaultValue = QString())\n        {\n            return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);\n        }\n\n\n        inline void setValue(const QString &key, const QString &value)\n        {\n            mSettings.remove(key);\n            mSettings.insert(key, value);\n            mUserSettings.remove(key);\n            mUserSettings.insert(key, value);\n        }\n\n        inline void setMultiValue(const QString &key, const QString &value)\n        {\n            QStringList values = mSettings.values(key);\n            if (!values.contains(value))\n                mSettings.insert(key, value);\n\n            values = mUserSettings.values(key);\n            if (!values.contains(value))\n                mUserSettings.insert(key, value);\n        }\n\n        inline void remove(const QString &key)\n        {\n            mSettings.remove(key);\n            mUserSettings.remove(key);\n        }\n\n        inline QStringList getDataDirs() const { return mDataDirs; }\n\n        inline void removeDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.removeAll(dir); }\n        inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }\n        inline QString getDataLocal() const {return mDataLocal; }\n\n        bool hasMaster();\n\n        QStringList values(const QString &key, const QStringList &defaultValues = QStringList()) const;\n\n        bool readFile(QTextStream &stream);\n        bool readFile(QTextStream &stream, QMultiMap<QString, QString> &settings);\n        bool readUserFile(QTextStream &stream);\n\n        bool writeFile(QTextStream &stream);\n        bool writeFileWithComments(QFile &file);\n\n        void setContentList(const QStringList& fileNames);\n        QStringList getContentList() const;\n\n        void clear();\n\n    private:\n        Files::ConfigurationManager &mCfgMgr;\n\n        void validatePaths();\n        QMultiMap<QString, QString> mSettings;\n        QMultiMap<QString, QString> mUserSettings;\n\n        QStringList mDataDirs;\n        QString mDataLocal;\n\n        static const char sContentKey[];\n\n        static bool isOrderedLine(const QString& line) ;\n    };\n}\n#endif // GAMESETTINGS_HPP\n"
  },
  {
    "path": "components/config/launchersettings.cpp",
    "content": "#include \"launchersettings.hpp\"\n\n#include <QTextStream>\n#include <QString>\n#include <QRegExp>\n#include <QMultiMap>\n\n#include <QDebug>\n\nconst char Config::LauncherSettings::sCurrentContentListKey[] = \"Profiles/currentprofile\";\nconst char Config::LauncherSettings::sLauncherConfigFileName[] = \"launcher.cfg\";\nconst char Config::LauncherSettings::sContentListsSectionPrefix[] = \"Profiles/\";\nconst char Config::LauncherSettings::sContentListSuffix[] = \"/content\";\n\nQStringList Config::LauncherSettings::subKeys(const QString &key)\n{\n    QMultiMap<QString, QString> settings = SettingsBase::getSettings();\n    QStringList keys = settings.uniqueKeys();\n\n    QRegExp keyRe(\"(.+)/\");\n\n    QStringList result;\n\n    for (const QString &currentKey : keys)\n    {\n\n        if (keyRe.indexIn(currentKey) != -1)\n        {\n            QString prefixedKey = keyRe.cap(1);\n\n            if(prefixedKey.startsWith(key))\n            {\n                QString subKey = prefixedKey.remove(key);\n                if (!subKey.isEmpty())\n                    result.append(subKey);\n            }\n        }\n    }\n\n    result.removeDuplicates();\n    return result;\n}\n\n\nbool Config::LauncherSettings::writeFile(QTextStream &stream)\n{\n    QString sectionPrefix;\n    QRegExp sectionRe(\"([^/]+)/(.+)$\");\n    QMultiMap<QString, QString> settings = SettingsBase::getSettings();\n\n    QMapIterator<QString, QString> i(settings);\n    i.toBack();\n\n    while (i.hasPrevious()) {\n        i.previous();\n\n        QString prefix;\n        QString key;\n\n        if (sectionRe.exactMatch(i.key())) {\n             prefix = sectionRe.cap(1);\n             key = sectionRe.cap(2);\n        }\n\n        // Get rid of legacy settings\n        if (key.contains(QChar('\\\\')))\n            continue;\n\n        if (key == QLatin1String(\"CurrentProfile\"))\n            continue;\n\n        if (sectionPrefix != prefix) {\n            sectionPrefix = prefix;\n            stream << \"\\n[\" << prefix << \"]\\n\";\n        }\n\n        stream << key << \"=\" << i.value() << \"\\n\";\n    }\n\n    return true;\n\n}\n\nQStringList Config::LauncherSettings::getContentLists()\n{\n    return subKeys(QString(sContentListsSectionPrefix));\n}\n\nQString Config::LauncherSettings::makeContentListKey(const QString& contentListName)\n{\n    return QString(sContentListsSectionPrefix) + contentListName + QString(sContentListSuffix);\n}\n\nvoid Config::LauncherSettings::setContentList(const GameSettings& gameSettings)\n{\n    // obtain content list from game settings (if present)\n    const QStringList files(gameSettings.getContentList());\n\n    // if openmw.cfg has no content, exit so we don't create an empty content list.\n    if (files.isEmpty())\n    {\n        return;\n    }\n\n    // if any existing profile in launcher matches the content list, make that profile the default\n    for (const QString &listName : getContentLists())\n    {\n        if (isEqual(files, getContentListFiles(listName)))\n        {\n            setCurrentContentListName(listName);\n            return;\n        }\n    }\n\n    // otherwise, add content list\n    QString newContentListName(makeNewContentListName());\n    setCurrentContentListName(newContentListName);\n    setContentList(newContentListName, files);\n}\n\nvoid Config::LauncherSettings::removeContentList(const QString &contentListName)\n{\n    remove(makeContentListKey(contentListName));\n}\n\nvoid Config::LauncherSettings::setCurrentContentListName(const QString &contentListName)\n{\n    remove(QString(sCurrentContentListKey));\n    setValue(QString(sCurrentContentListKey), contentListName);\n}\n\nvoid Config::LauncherSettings::setContentList(const QString& contentListName, const QStringList& fileNames)\n{\n    removeContentList(contentListName);\n    QString key = makeContentListKey(contentListName);\n    for (const QString& fileName : fileNames)\n    {\n        setMultiValue(key, fileName);\n    }\n}\n\nQString Config::LauncherSettings::getCurrentContentListName() const\n{\n    return value(QString(sCurrentContentListKey));\n}\n\nQStringList Config::LauncherSettings::getContentListFiles(const QString& contentListName) const\n{\n    // QMap returns multiple rows in LIFO order, so need to reverse\n    return reverse(getSettings().values(makeContentListKey(contentListName)));\n}\n\nQStringList Config::LauncherSettings::reverse(const QStringList& toReverse)\n{\n    QStringList result;\n    result.reserve(toReverse.size());\n    std::reverse_copy(toReverse.begin(), toReverse.end(), std::back_inserter(result));\n    return result;\n}\n\nbool Config::LauncherSettings::isEqual(const QStringList& list1, const QStringList& list2)\n{\n    if (list1.count() != list2.count())\n    {\n        return false;\n    }\n\n    for (int i = 0; i < list1.count(); ++i)\n    {\n        if (list1.at(i) != list2.at(i))\n        {\n            return false;\n        }\n    }\n\n    // if get here, lists are same\n    return true;\n}\n\nQString Config::LauncherSettings::makeNewContentListName()\n{\n    // basically, use date and time as the name  e.g. YYYY-MM-DDThh:mm:ss\n    time_t rawtime;\n    struct tm * timeinfo;\n\n    time(&rawtime);\n    timeinfo = localtime(&rawtime);\n    int base = 10;\n    QChar zeroPad('0');\n    return QString(\"%1-%2-%3T%4:%5:%6\")\n        .arg(timeinfo->tm_year + 1900, 4).arg(timeinfo->tm_mon + 1, 2, base, zeroPad).arg(timeinfo->tm_mday, 2, base, zeroPad)\n        .arg(timeinfo->tm_hour, 2, base, zeroPad).arg(timeinfo->tm_min, 2, base, zeroPad).arg(timeinfo->tm_sec, 2, base, zeroPad);\n}\n\n\n"
  },
  {
    "path": "components/config/launchersettings.hpp",
    "content": "#ifndef LAUNCHERSETTINGS_HPP\n#define LAUNCHERSETTINGS_HPP\n\n#include \"settingsbase.hpp\"\n#include \"gamesettings.hpp\"\n\nnamespace Config\n{\n    class LauncherSettings : public SettingsBase<QMultiMap<QString, QString> >\n    {\n    public:\n        bool writeFile(QTextStream &stream);\n\n        /// \\return names of all Content Lists in the launcher's .cfg file. \n        QStringList getContentLists();\n\n        /// Set initially selected content list to match values from openmw.cfg, creating if necessary\n        void setContentList(const GameSettings& gameSettings);\n\n        /// Create a Content List (or replace if it already exists)\n        void setContentList(const QString& contentListName, const QStringList& fileNames);\n\n        void removeContentList(const QString &contentListName);\n\n        void setCurrentContentListName(const QString &contentListName);\n\n        QString getCurrentContentListName() const;\n\n        QStringList getContentListFiles(const QString& contentListName) const;\n\n        /// \\return new list that is reversed order of input\n        static QStringList reverse(const QStringList& toReverse);\n\n        static const char sLauncherConfigFileName[];\n    \n    private:\n\n        /// \\return key to use to get/set the files in the specified Content List\n        static QString makeContentListKey(const QString& contentListName);\n\n        /// \\return true if both lists are same\n        static bool isEqual(const QStringList& list1, const QStringList& list2);\n\n        static QString makeNewContentListName();\n\n        QStringList subKeys(const QString &key);\n\n        /// name of entry in launcher.cfg that holds name of currently selected Content List\n        static const char sCurrentContentListKey[];\n\n        /// section of launcher.cfg holding the Content Lists\n        static const char sContentListsSectionPrefix[];\n\n        static const char sContentListSuffix[];\n    };\n}\n#endif // LAUNCHERSETTINGS_HPP\n"
  },
  {
    "path": "components/config/settingsbase.hpp",
    "content": "#ifndef SETTINGSBASE_HPP\n#define SETTINGSBASE_HPP\n\n#include <QTextStream>\n#include <QStringList>\n#include <QString>\n#include <QRegExp>\n#include <QMultiMap>\n\nnamespace Config\n{\n    template <class Map>\n    class SettingsBase\n    {\n\n    public:\n        SettingsBase() { mMultiValue = false; }\n        ~SettingsBase() = default;\n\n        inline QString value(const QString &key, const QString &defaultValue = QString()) const\n        {\n            return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);\n        }\n\n        inline void setValue(const QString &key, const QString &value)\n        {\n            QStringList values = mSettings.values(key);\n            if (!values.contains(value))\n                mSettings.insert(key, value);\n        }\n\n        inline void setMultiValue(const QString &key, const QString &value)\n        {\n            QStringList values = mSettings.values(key);\n            if (!values.contains(value))\n                mSettings.insert(key, value);\n        }\n\n        inline void setMultiValueEnabled(bool enable)\n        {\n            mMultiValue = enable;\n        }\n\n        inline void remove(const QString &key)\n        {\n            mSettings.remove(key);\n        }\n\n        Map getSettings() const {return mSettings;} \n\n        bool readFile(QTextStream &stream)\n        {\n            Map cache;\n\n            QString sectionPrefix;\n\n            QRegExp sectionRe(\"^\\\\[([^]]+)\\\\]\");\n            QRegExp keyRe(\"^([^=]+)\\\\s*=\\\\s*(.+)$\");\n\n            while (!stream.atEnd()) {\n                QString line = stream.readLine();\n\n                if (line.isEmpty() || line.startsWith(\"#\"))\n                    continue;\n\n                if (sectionRe.exactMatch(line)) {\n                    sectionPrefix = sectionRe.cap(1);\n                    sectionPrefix.append(\"/\");\n                    continue;\n                }\n\n                if (keyRe.indexIn(line) != -1) {\n\n                    QString key = keyRe.cap(1).trimmed();\n                    QString value = keyRe.cap(2).trimmed();\n\n                    if (!sectionPrefix.isEmpty())\n                        key.prepend(sectionPrefix);\n\n                    mSettings.remove(key);\n\n                    QStringList values = cache.values(key);\n\n                    if (!values.contains(value)) {\n                        if (mMultiValue) {\n                            cache.insert(key, value);\n                        } else {\n                            cache.remove(key);\n                            cache.insert(key, value);\n                        }\n                    }\n                }\n            }\n\n            if (mSettings.isEmpty()) {\n                mSettings = cache; // This is the first time we read a file\n                return true;\n            }\n\n            // Merge the changed keys with those which didn't\n            mSettings.unite(cache);\n            return true;\n        }\n\n        void clear()\n        {\n            mSettings.clear();\n        }\n\n    private:\n        Map mSettings;\n\n        bool mMultiValue;\n    };\n}\n#endif // SETTINGSBASE_HPP\n"
  },
  {
    "path": "components/contentselector/model/contentmodel.cpp",
    "content": "#include \"contentmodel.hpp\"\n#include \"esmfile.hpp\"\n\n#include <stdexcept>\n\n#include <QDir>\n#include <QTextCodec>\n#include <QDebug>\n\n#include <components/esm/esmreader.hpp>\n\nContentSelectorModel::ContentModel::ContentModel(QObject *parent, QIcon warningIcon) :\n    QAbstractTableModel(parent),\n    mWarningIcon(warningIcon),\n    mMimeType (\"application/omwcontent\"),\n    mMimeTypes (QStringList() << mMimeType),\n    mColumnCount (1),\n    mDropActions (Qt::MoveAction)\n{\n    setEncoding (\"win1252\");\n    uncheckAll();\n}\n\nContentSelectorModel::ContentModel::~ContentModel()\n{\n    qDeleteAll(mFiles);\n    mFiles.clear();\n}\n\nvoid ContentSelectorModel::ContentModel::setEncoding(const QString &encoding)\n{\n    mEncoding = encoding;\n}\n\nint ContentSelectorModel::ContentModel::columnCount(const QModelIndex &parent) const\n{\n    if (parent.isValid())\n        return 0;\n\n    return mColumnCount;\n}\n\nint ContentSelectorModel::ContentModel::rowCount(const QModelIndex &parent) const\n{\n    if(parent.isValid())\n        return 0;\n\n    return mFiles.size();\n}\n\nconst ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) const\n{\n    if (row >= 0 && row < mFiles.size())\n        return mFiles.at(row);\n\n    return nullptr;\n}\n\nContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row)\n{\n    if (row >= 0 && row < mFiles.count())\n        return mFiles.at(row);\n\n    return nullptr;\n}\nconst ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(const QString &name) const\n{\n    EsmFile::FileProperty fp = EsmFile::FileProperty_FileName;\n\n    if (name.contains ('/'))\n        fp = EsmFile::FileProperty_FilePath;\n\n    for (const EsmFile *file : mFiles)\n    {\n        if (name.compare(file->fileProperty (fp).toString(), Qt::CaseInsensitive) == 0)\n            return file;\n    }\n    return nullptr;\n}\n\nQModelIndex ContentSelectorModel::ContentModel::indexFromItem(const EsmFile *item) const\n{\n    //workaround: non-const pointer cast for calls from outside contentmodel/contentselector\n    EsmFile *non_const_file_ptr = const_cast<EsmFile *>(item);\n\n    if (item)\n        return index(mFiles.indexOf(non_const_file_ptr),0);\n\n    return QModelIndex();\n}\n\nQt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index) const\n{\n    if (!index.isValid())\n        return Qt::ItemIsDropEnabled;\n\n    const EsmFile *file = item(index.row());\n\n    if (!file)\n        return Qt::NoItemFlags;\n\n    //game files can always be checked\n    if (file->isGameFile())\n        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;\n\n    Qt::ItemFlags returnFlags;\n\n    // addon can be checked if its gamefile is\n    // ... special case, addon with no dependency can be used with any gamefile.\n    bool gamefileChecked = (file->gameFiles().count() == 0);\n    for (const QString &fileName : file->gameFiles())\n    {\n        for (QListIterator<EsmFile *> dependencyIter(mFiles); dependencyIter.hasNext(); dependencyIter.next())\n        {\n            //compare filenames only.  Multiple instances\n            //of the filename (with different paths) is not relevant here.\n            bool depFound = (dependencyIter.peekNext()->fileName().compare(fileName, Qt::CaseInsensitive) == 0);\n\n            if (!depFound)\n                continue;\n\n            if (!gamefileChecked)\n            {\n                if (isChecked (dependencyIter.peekNext()->filePath()))\n                    gamefileChecked = (dependencyIter.peekNext()->isGameFile());\n            }\n\n            // force it to iterate all files in cases where the current\n            // dependency is a game file to ensure that a later duplicate\n            // game file is / is not checked.\n            // (i.e., break only if it's not a gamefile or the game file has been checked previously)\n            if (gamefileChecked || !(dependencyIter.peekNext()->isGameFile()))\n                break;\n        }\n    }\n\n    if (gamefileChecked)\n    {\n        returnFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;\n    }\n\n    return returnFlags;\n}\n\nQVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid())\n        return QVariant();\n\n    if (index.row() >= mFiles.size())\n        return QVariant();\n\n    const EsmFile *file = item(index.row());\n\n    if (!file)\n        return QVariant();\n\n    const int column = index.column();\n\n    switch (role)\n    {\n    case Qt::DecorationRole:\n    {\n        return isLoadOrderError(file) ? mWarningIcon : QVariant();\n    }\n\n    case Qt::EditRole:\n    case Qt::DisplayRole:\n    {\n        if (column >=0 && column <=EsmFile::FileProperty_GameFile)\n            return file->fileProperty(static_cast<EsmFile::FileProperty>(column));\n\n        return QVariant();\n    }\n\n    case Qt::TextAlignmentRole:\n    {\n        switch (column)\n        {\n        case 0:\n        case 1:\n            return Qt::AlignLeft + Qt::AlignVCenter;\n        case 2:\n        case 3:\n            return Qt::AlignRight + Qt::AlignVCenter;\n        default:\n            return Qt::AlignLeft + Qt::AlignVCenter;\n        }\n    }\n\n    case Qt::ToolTipRole:\n    {\n        if (column != 0)\n            return QVariant();\n\n        return toolTip(file);\n    }\n\n    case Qt::CheckStateRole:\n    {\n        if (file->isGameFile())\n            return QVariant();\n\n        return mCheckStates[file->filePath()];\n    }\n\n    case Qt::UserRole:\n    {\n        if (file->isGameFile())\n            return ContentType_GameFile;\n        else\n            if (flags(index))\n                return ContentType_Addon;\n\n        break;\n    }\n\n    case Qt::UserRole + 1:\n        return isChecked(file->filePath());\n    }\n    return QVariant();\n}\n\nbool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role)\n{\n    if(!index.isValid())\n        return false;\n\n    EsmFile *file = item(index.row());\n    QString fileName = file->fileName();\n    bool success = false;\n\n    switch(role)\n    {\n        case Qt::EditRole:\n        {\n            QStringList list = value.toStringList();\n\n            for (int i = 0; i < EsmFile::FileProperty_GameFile; i++)\n                file->setFileProperty(static_cast<EsmFile::FileProperty>(i), list.at(i));\n\n            for (int i = EsmFile::FileProperty_GameFile; i < list.size(); i++)\n                file->setFileProperty (EsmFile::FileProperty_GameFile, list.at(i));\n\n            emit dataChanged(index, index);\n\n            success = true;\n        }\n        break;\n\n        case Qt::UserRole+1:\n        {\n            success = (flags (index) & Qt::ItemIsEnabled);\n\n            if (success)\n            {\n                success = setCheckState(file->filePath(), value.toBool());\n                emit dataChanged(index, index);\n            }\n        }\n        break;\n\n        case Qt::CheckStateRole:\n        {\n            int checkValue = value.toInt();\n            bool setState = false;\n            if ((checkValue==Qt::Checked) && !isChecked(file->filePath()))\n            {\n                setState = true;\n                success = true;\n            }\n            else if ((checkValue == Qt::Checked) && isChecked (file->filePath()))\n                setState = true;\n            else if (checkValue == Qt::Unchecked)\n                setState = true;\n\n            if (setState)\n            {\n                setCheckState(file->filePath(), success);\n                emit dataChanged(index, index);\n                checkForLoadOrderErrors();\n            }\n            else\n                return success;\n\n            for (EsmFile *file2 : mFiles)\n            {\n                if (file2->gameFiles().contains(fileName, Qt::CaseInsensitive))\n                {\n                    QModelIndex idx = indexFromItem(file2);\n                    emit dataChanged(idx, idx);\n                }\n            }\n\n            success =  true;\n        }\n        break;\n    }\n\n    return success;\n}\n\nbool ContentSelectorModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent)\n{\n    if (parent.isValid())\n        return false;\n\n    beginInsertRows(parent, position, position+rows-1);\n    {\n        for (int row = 0; row < rows; ++row)\n            mFiles.insert(position, new EsmFile);\n\n    } endInsertRows();\n\n    return true;\n}\n\nbool ContentSelectorModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent)\n{\n    if (parent.isValid())\n        return false;\n\n    beginRemoveRows(parent, position, position+rows-1);\n    {\n        for (int row = 0; row < rows; ++row)\n            delete mFiles.takeAt(position);\n\n    } endRemoveRows();\n\n    // at this point we know that drag and drop has finished.\n    checkForLoadOrderErrors();\n    return true;\n}\n\nQt::DropActions ContentSelectorModel::ContentModel::supportedDropActions() const\n{\n    return mDropActions;\n}\n\nQStringList ContentSelectorModel::ContentModel::mimeTypes() const\n{\n    return mMimeTypes;\n}\n\nQMimeData *ContentSelectorModel::ContentModel::mimeData(const QModelIndexList &indexes) const\n{\n    QByteArray encodedData;\n\n    for (const QModelIndex &index : indexes)\n    {\n        if (!index.isValid())\n            continue;\n\n        encodedData.append(item(index.row())->encodedData());\n    }\n\n    QMimeData *mimeData = new QMimeData();\n    mimeData->setData(mMimeType, encodedData);\n\n    return mimeData;\n}\n\nbool ContentSelectorModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)\n{\n    if (action == Qt::IgnoreAction)\n        return true;\n\n    if (column > 0)\n        return false;\n\n    if (!data->hasFormat(mMimeType))\n        return false;\n\n    int beginRow = rowCount();\n\n    if (row != -1)\n        beginRow = row;\n\n    else if (parent.isValid())\n        beginRow = parent.row();\n\n    QByteArray encodedData = data->data(mMimeType);\n    QDataStream stream(&encodedData, QIODevice::ReadOnly);\n\n    while (!stream.atEnd())\n    {\n\n        QString value;\n        QStringList values;\n        QStringList gamefiles;\n\n        for (int i = 0; i < EsmFile::FileProperty_GameFile; ++i)\n        {\n            stream >> value;\n            values << value;\n        }\n\n        stream >> gamefiles;\n\n        insertRows(beginRow, 1);\n\n        QModelIndex idx = index(beginRow++, 0, QModelIndex());\n        setData(idx, QStringList() << values << gamefiles, Qt::EditRole);\n    }\n\n    return true;\n}\n\nvoid ContentSelectorModel::ContentModel::addFile(EsmFile *file)\n{\n    beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count());\n        mFiles.append(file);\n    endInsertRows();\n\n    QModelIndex idx = index (mFiles.size() - 2, 0, QModelIndex());\n\n    emit dataChanged (idx, idx);\n}\n\nvoid ContentSelectorModel::ContentModel::addFiles(const QString &path)\n{\n    QDir dir(path);\n    QStringList filters;\n    filters << \"*.esp\" << \"*.esm\" << \"*.omwgame\" << \"*.omwaddon\";\n    dir.setNameFilters(filters);\n\n    for (const QString &path2 : dir.entryList())\n    {\n        QFileInfo info(dir.absoluteFilePath(path2));\n\n        if (item(info.fileName()))\n            continue;\n\n        try {\n            ESM::ESMReader fileReader;\n            ToUTF8::Utf8Encoder encoder =\n            ToUTF8::calculateEncoding(mEncoding.toStdString());\n            fileReader.setEncoder(&encoder);\n            fileReader.open(std::string(dir.absoluteFilePath(path2).toUtf8().constData()));\n\n            EsmFile *file = new EsmFile(path2);\n         \n            for (std::vector<ESM::Header::MasterData>::const_iterator itemIter = fileReader.getGameFiles().begin();\n                itemIter != fileReader.getGameFiles().end(); ++itemIter)\n                file->addGameFile(QString::fromUtf8(itemIter->name.c_str()));\n\n            file->setAuthor     (QString::fromUtf8(fileReader.getAuthor().c_str()));\n            file->setDate       (info.lastModified());\n            file->setFormat     (fileReader.getFormat());\n            file->setFilePath       (info.absoluteFilePath());\n            file->setDescription(QString::fromUtf8(fileReader.getDesc().c_str()));\n\n            // HACK\n            // Load order constraint of Bloodmoon.esm needing Tribunal.esm is missing\n            // from the file supplied by Bethesda, so we have to add it ourselves\n            if (file->fileName().compare(\"Bloodmoon.esm\", Qt::CaseInsensitive) == 0)\n            {\n                file->addGameFile(QString::fromUtf8(\"Tribunal.esm\"));\n            }\n\n            // Put the file in the table\n            addFile(file);\n\n        } catch(std::runtime_error &e) {\n            // An error occurred while reading the .esp\n            qWarning() << \"Error reading addon file: \" << e.what();\n            continue;\n        }\n\n    }\n\n    sortFiles();\n}\n\nvoid ContentSelectorModel::ContentModel::clearFiles()\n{\n    const int filesCount = mFiles.count();\n\n    if (filesCount > 0) {\n        beginRemoveRows(QModelIndex(), 0, filesCount - 1);\n        mFiles.clear();\n        endRemoveRows();\n    }\n}\n\nQStringList ContentSelectorModel::ContentModel::gameFiles() const\n{\n    QStringList gameFiles;\n    for (const ContentSelectorModel::EsmFile *file : mFiles)\n    {\n        if (file->isGameFile())\n        {\n            gameFiles.append(file->fileName());\n        }\n    }\n    return gameFiles;\n}\n\nvoid ContentSelectorModel::ContentModel::sortFiles()\n{\n    //first, sort the model such that all dependencies are ordered upstream (gamefile) first.\n    bool movedFiles = true;\n    int fileCount = mFiles.size();\n\n    //Dependency sort\n    //iterate until no sorting of files occurs\n    while (movedFiles)\n    {\n        movedFiles = false;\n        //iterate each file, obtaining a reference to it's gamefiles list\n        for (int i = 0; i < fileCount; i++)\n        {\n            QModelIndex idx1 = index (i, 0, QModelIndex());\n            const QStringList &gamefiles = mFiles.at(i)->gameFiles();\n            //iterate each file after the current file, verifying that none of it's\n            //dependencies appear.\n            for (int j = i + 1; j < fileCount; j++)\n            {\n                if (gamefiles.contains(mFiles.at(j)->fileName(), Qt::CaseInsensitive)\n                 || (!mFiles.at(i)->isGameFile() && gamefiles.isEmpty()\n                 && mFiles.at(j)->fileName().compare(\"Morrowind.esm\", Qt::CaseInsensitive) == 0)) // Hack: implicit dependency on Morrowind.esm for dependency-less files\n                {\n                        mFiles.move(j, i);\n\n                        QModelIndex idx2 = index (j, 0, QModelIndex());\n\n                        emit dataChanged (idx1, idx2);\n\n                        movedFiles = true;\n                }\n            }\n            if (movedFiles)\n                break;\n        }\n    }\n}\n\nbool ContentSelectorModel::ContentModel::isChecked(const QString& filepath) const\n{\n    if (mCheckStates.contains(filepath))\n        return (mCheckStates[filepath] == Qt::Checked);\n\n    return false;\n}\n\nbool ContentSelectorModel::ContentModel::isEnabled (QModelIndex index) const\n{\n    return (flags(index) & Qt::ItemIsEnabled);\n}\n\nbool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile *file) const\n{\n    return mPluginsWithLoadOrderError.contains(file->filePath());\n}\n\nvoid ContentSelectorModel::ContentModel::setContentList(const QStringList &fileList)\n{\n    mPluginsWithLoadOrderError.clear();\n    int previousPosition = -1;\n    for (const QString &filepath : fileList)\n    {\n        if (setCheckState(filepath, true))\n        {\n            // as necessary, move plug-ins in visible list to match sequence of supplied filelist\n            const EsmFile* file = item(filepath);\n            int filePosition = indexFromItem(file).row();\n            if (filePosition < previousPosition)\n            {\n                mFiles.move(filePosition, previousPosition);\n                emit dataChanged(index(filePosition, 0, QModelIndex()), index(previousPosition, 0, QModelIndex()));\n            }\n            else\n            {\n                previousPosition = filePosition;\n            }\n        }\n    }\n    checkForLoadOrderErrors();\n}\n\nvoid ContentSelectorModel::ContentModel::checkForLoadOrderErrors()\n{\n    for (int row = 0; row < mFiles.count(); ++row)\n    {\n        EsmFile* file = item(row);\n        bool isRowInError = checkForLoadOrderErrors(file, row).count() != 0;\n        if (isRowInError)\n        {\n            mPluginsWithLoadOrderError.insert(file->filePath());\n        }\n        else\n        {\n            mPluginsWithLoadOrderError.remove(file->filePath());\n        }\n    }\n}\n\nQList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors(const EsmFile *file, int row) const\n{\n    QList<LoadOrderError> errors = QList<LoadOrderError>();\n    for (const QString &dependentfileName : file->gameFiles())\n    {\n        const EsmFile* dependentFile = item(dependentfileName);\n\n        if (!dependentFile)\n        {\n            errors.append(LoadOrderError(LoadOrderError::ErrorCode_MissingDependency, dependentfileName));\n        }\n        else\n        {\n            if (!isChecked(dependentFile->filePath()))\n            {\n                errors.append(LoadOrderError(LoadOrderError::ErrorCode_InactiveDependency, dependentfileName));\n            }\n            if (row < indexFromItem(dependentFile).row())\n            {\n                errors.append(LoadOrderError(LoadOrderError::ErrorCode_LoadOrder, dependentfileName));\n            }\n        }\n    }\n    return errors;\n}\n\nQString ContentSelectorModel::ContentModel::toolTip(const EsmFile *file) const\n{\n    if (isLoadOrderError(file))\n    {\n        QString text(\"<b>\");\n        int index = indexFromItem(item(file->filePath())).row();\n        for (const LoadOrderError& error : checkForLoadOrderErrors(file, index))\n        {\n            text += \"<p>\";\n            text += error.toolTip();\n            text += \"</p>\";\n        }\n        text += (\"</b>\");\n        text += file->toolTip();\n        return text;\n    }\n    else\n    {\n        return file->toolTip();\n    }\n}\n\nvoid ContentSelectorModel::ContentModel::refreshModel()\n{\n    emit dataChanged (index(0,0), index(rowCount()-1,0));\n}\n\nbool ContentSelectorModel::ContentModel::setCheckState(const QString &filepath, bool checkState)\n{\n    if (filepath.isEmpty())\n        return false;\n\n    const EsmFile *file = item(filepath);\n\n    if (!file)\n        return false;\n\n    Qt::CheckState state = Qt::Unchecked;\n\n    if (checkState)\n        state = Qt::Checked;\n\n    mCheckStates[filepath] = state;\n    emit dataChanged(indexFromItem(item(filepath)), indexFromItem(item(filepath)));\n\n    if (file->isGameFile())\n        refreshModel();\n\n    //if we're checking an item, ensure all \"upstream\" files (dependencies) are checked as well.\n    if (state == Qt::Checked)\n    {\n        for (const QString& upstreamName : file->gameFiles())\n        {\n            const EsmFile *upstreamFile = item(upstreamName);\n\n            if (!upstreamFile)\n                continue;\n\n            if (!isChecked(upstreamFile->filePath()))\n                mCheckStates[upstreamFile->filePath()] = Qt::Checked;\n\n            emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile));\n\n        }\n    }\n    //otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked.\n    if (state == Qt::Unchecked)\n    {\n        for (const EsmFile *downstreamFile : mFiles)\n        {\n            QFileInfo fileInfo(filepath);\n            QString filename = fileInfo.fileName();\n\n            if (downstreamFile->gameFiles().contains(filename, Qt::CaseInsensitive))\n            {\n                if (mCheckStates.contains(downstreamFile->filePath()))\n                    mCheckStates[downstreamFile->filePath()] = Qt::Unchecked;\n\n                emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile));\n            }\n        }\n    }\n\n    return true;\n}\n\nContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checkedItems() const\n{\n    ContentFileList list;\n\n    // TODO:\n    // First search for game files and next addons,\n    // so we get more or less correct game files vs addons order.\n    for (EsmFile *file : mFiles)\n        if (isChecked(file->filePath()))\n            list << file;\n\n    return list;\n}\n\nvoid ContentSelectorModel::ContentModel::uncheckAll()\n{\n    emit layoutAboutToBeChanged();\n    mCheckStates.clear();\n    emit layoutChanged();\n}\n"
  },
  {
    "path": "components/contentselector/model/contentmodel.hpp",
    "content": "#ifndef CONTENTMODEL_HPP\n#define CONTENTMODEL_HPP\n\n#include <QAbstractTableModel>\n#include <QStringList>\n#include <QSet>\n#include <QIcon>\n#include \"loadordererror.hpp\"\n\nnamespace ContentSelectorModel\n{\n    class EsmFile;\n\n    typedef QList<EsmFile *> ContentFileList;\n\n    enum ContentType\n    {\n        ContentType_GameFile,\n        ContentType_Addon\n    };\n\n    class ContentModel : public QAbstractTableModel\n    {\n        Q_OBJECT\n    public:\n        explicit ContentModel(QObject *parent, QIcon warningIcon);\n        ~ContentModel();\n\n        void setEncoding(const QString &encoding);\n\n        int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n        int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n        QVariant data(const QModelIndex &index, int role) const override;\n        Qt::ItemFlags flags(const QModelIndex &index) const override;\n        bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;\n\n        bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;\n        bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;\n\n        Qt::DropActions supportedDropActions() const override;\n        QStringList mimeTypes() const override;\n        QMimeData *mimeData(const QModelIndexList &indexes) const override;\n        bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;\n\n        void addFiles(const QString &path);\n        void clearFiles();\n\n        QModelIndex indexFromItem(const EsmFile *item) const;\n        const EsmFile *item(const QString &name) const;\n        const EsmFile *item(int row) const;\n        EsmFile *item(int row);\n        QStringList gameFiles() const;\n\n        bool isEnabled (QModelIndex index) const;\n        bool isChecked(const QString &filepath) const;\n        bool setCheckState(const QString &filepath, bool isChecked);\n        void setContentList(const QStringList &fileList);\n        ContentFileList checkedItems() const;\n        void uncheckAll();\n\n        void refreshModel();\n\n        /// Checks all plug-ins for load order errors and updates mPluginsWithLoadOrderError with plug-ins with issues\n        void checkForLoadOrderErrors();\n\n    private:\n\n        void addFile(EsmFile *file);\n\n        void sortFiles();\n\n        /// Checks a specific plug-in for load order errors\n        /// \\return all errors found for specific plug-in\n        QList<LoadOrderError> checkForLoadOrderErrors(const EsmFile *file, int row) const;\n\n        ///  \\return true if plug-in has a Load Order error\n        bool isLoadOrderError(const EsmFile *file) const;\n\n        QString toolTip(const EsmFile *file) const;\n\n        ContentFileList mFiles;\n        QHash<QString, Qt::CheckState> mCheckStates;\n        QSet<QString> mPluginsWithLoadOrderError;\n        QString mEncoding;\n        QIcon mWarningIcon;\n\n    public:\n\n        QString mMimeType;\n        QStringList mMimeTypes;\n        int mColumnCount;\n        Qt::DropActions mDropActions;\n    };\n}\n#endif // CONTENTMODEL_HPP\n"
  },
  {
    "path": "components/contentselector/model/esmfile.cpp",
    "content": "#include \"esmfile.hpp\"\n\n#include <QMimeData>\n#include <QDataStream>\n\nint ContentSelectorModel::EsmFile::sPropertyCount = 7;\nQString ContentSelectorModel::EsmFile::sToolTip = QString(\"<b>Author:</b> %1<br/> \\\n                                              <b>Version:</b> %2<br/> \\\n                                              <b>Modified:</b> %3<br/> \\\n                                              <b>Path:</b><br/>%4<br/> \\\n                                              <br/><b>Description:</b><br/>%5<br/> \\\n                                              <br/><b>Dependencies: </b>%6<br/>\");\n\n\nContentSelectorModel::EsmFile::EsmFile(QString fileName, ModelItem *parent)\n                : ModelItem(parent), mFileName(fileName), mFormat(0)\n{}\n\nvoid ContentSelectorModel::EsmFile::setFileName(const QString &fileName)\n{\n    mFileName = fileName;\n}\n\nvoid ContentSelectorModel::EsmFile::setAuthor(const QString &author)\n{\n    mAuthor = author;\n}\n\nvoid ContentSelectorModel::EsmFile::setDate(const QDateTime &modified)\n{\n    mModified = modified;\n}\n\nvoid ContentSelectorModel::EsmFile::setFormat(int format)\n{\n    mFormat = format;\n}\n\nvoid ContentSelectorModel::EsmFile::setFilePath(const QString &path)\n{\n    mPath = path;\n}\n\nvoid ContentSelectorModel::EsmFile::setGameFiles(const QStringList &gamefiles)\n{\n    mGameFiles = gamefiles;\n}\n\nvoid ContentSelectorModel::EsmFile::setDescription(const QString &description)\n{\n    mDescription = description;\n}\n\nQByteArray ContentSelectorModel::EsmFile::encodedData() const\n{\n    QByteArray encodedData;\n    QDataStream stream(&encodedData, QIODevice::WriteOnly);\n\n    stream << mFileName << mAuthor << QString::number(mFormat)\n           << mModified.toString() << mPath << mDescription\n           << mGameFiles;\n\n    return encodedData;\n}\n\nbool ContentSelectorModel::EsmFile::isGameFile() const\n{ \n    return (mGameFiles.size() == 0) &&\n        (mFileName.endsWith(QLatin1String(\".esm\"), Qt::CaseInsensitive) || \n        mFileName.endsWith(QLatin1String(\".omwgame\"), Qt::CaseInsensitive));\n}\n\nQVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) const\n{\n    switch (prop)\n    {\n    case FileProperty_FileName:\n        return mFileName;\n        break;\n\n    case FileProperty_Author:\n        return mAuthor;\n        break;\n\n    case FileProperty_Format:\n        return mFormat;\n        break;\n\n    case FileProperty_DateModified:\n        return mModified.toString(Qt::ISODate);\n        break;\n\n    case FileProperty_FilePath:\n        return mPath;\n        break;\n\n    case FileProperty_Description:\n        return mDescription;\n        break;\n\n    case FileProperty_GameFile:\n        return mGameFiles;\n        break;\n\n    default:\n        break;\n    }\n    return QVariant();\n}\nvoid ContentSelectorModel::EsmFile::setFileProperty (const FileProperty prop, const QString &value)\n{\n    switch (prop)\n    {\n    case FileProperty_FileName:\n        mFileName = value;\n        break;\n\n    case FileProperty_Author:\n        mAuthor = value;\n        break;\n\n    case FileProperty_Format:\n        mFormat = value.toInt();\n        break;\n\n    case FileProperty_DateModified:\n        mModified = QDateTime::fromString(value);\n        break;\n\n    case FileProperty_FilePath:\n        mPath = value;\n        break;\n\n    case FileProperty_Description:\n        mDescription = value;\n        break;\n\n    case FileProperty_GameFile:\n        mGameFiles << value;\n        break;\n\n    default:\n        break;\n    }\n}\n"
  },
  {
    "path": "components/contentselector/model/esmfile.hpp",
    "content": "#ifndef ESMFILE_HPP\n#define ESMFILE_HPP\n\n#include <QDateTime>\n#include <QStringList>\n\n#include \"modelitem.hpp\"\n\nclass QMimeData;\n\nnamespace ContentSelectorModel\n{\n    class EsmFile : public ModelItem\n    {\n        Q_OBJECT\n        Q_PROPERTY(QString filename READ fileName)\n\n    public:\n\n        enum FileProperty\n        {\n            FileProperty_FileName       = 0,\n            FileProperty_Author         = 1,\n            FileProperty_Format         = 2,\n            FileProperty_DateModified   = 3,\n            FileProperty_FilePath       = 4,\n            FileProperty_Description    = 5,\n            FileProperty_GameFile       = 6\n        };\n\n        EsmFile(QString fileName = QString(), ModelItem *parent = nullptr);\n     //   EsmFile(const EsmFile &);\n\n        ~EsmFile()\n        {}\n\n        void setFileProperty (const FileProperty prop, const QString &value);\n\n        void setFileName(const QString &fileName);\n        void setAuthor(const QString &author);\n        void setSize(const int size);\n        void setDate(const QDateTime &modified);\n        void setFormat(const int format);\n        void setFilePath(const QString &path);\n        void setGameFiles(const QStringList &gameFiles);\n        void setDescription(const QString &description);\n\n        inline void addGameFile (const QString &name) {mGameFiles.append(name); }\n        QVariant fileProperty (const FileProperty prop) const;\n\n        inline QString fileName() const             { return mFileName; }\n        inline QString author() const               { return mAuthor; }\n        inline QDateTime modified() const           { return mModified; }\n        inline float format() const                 { return mFormat; }\n        inline QString filePath() const                 { return mPath; }\n\n        /// @note Contains file names, not paths.\n        inline const QStringList &gameFiles() const { return mGameFiles; }\n        inline QString description() const          { return mDescription; }\n        inline QString toolTip() const              { return sToolTip.arg(mAuthor)\n                                                             .arg(mFormat)\n                                                             .arg(mModified.toString(Qt::ISODate))\n                                                             .arg(mPath)\n                                                             .arg(mDescription)\n                                                             .arg(mGameFiles.join(\", \"));\n                                                    }\n\n        bool isGameFile() const;\n        QByteArray encodedData() const;\n\n    public:\n        static int sPropertyCount;\n        static QString sToolTip;\n\n    private:\n\n        QString mFileName;\n        QString mAuthor;\n        QDateTime mModified;\n        int mFormat;\n        QString mPath;\n        QStringList mGameFiles;\n        QString mDescription;\n        QString mToolTip;\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/contentselector/model/loadordererror.cpp",
    "content": "#include \"loadordererror.hpp\"\n#include <cassert>\n\nQString ContentSelectorModel::LoadOrderError::sErrorToolTips[ErrorCode_LoadOrder] =\n{\n    QString(\"Unable to find dependent file: %1\"),\n    QString(\"Dependent file needs to be active: %1\"),\n    QString(\"This file needs to load after %1\")\n};\n\nQString ContentSelectorModel::LoadOrderError::toolTip() const\n{\n    assert(mErrorCode);\n    return sErrorToolTips[mErrorCode - 1].arg(mFileName);\n}\n"
  },
  {
    "path": "components/contentselector/model/loadordererror.hpp",
    "content": "#ifndef LOADORDERERROR_HPP\n#define LOADORDERERROR_HPP\n\n#include <QString>\n\nnamespace ContentSelectorModel\n{\n    /// \\brief Details of a suspected Load Order problem a plug-in will have. This is basically a POD.\n    class LoadOrderError\n    {\n    public:\n        enum ErrorCode\n        {\n            ErrorCode_None               = 0,\n            ErrorCode_MissingDependency  = 1,\n            ErrorCode_InactiveDependency = 2,\n            ErrorCode_LoadOrder          = 3\n        };\n\n        inline LoadOrderError() : mErrorCode(ErrorCode_None) {}\n        inline LoadOrderError(ErrorCode errorCode, QString fileName)\n            : mErrorCode(errorCode), mFileName(fileName) {}\n        inline ErrorCode errorCode() const { return mErrorCode; }\n        inline QString fileName() const { return mFileName; }\n        QString toolTip() const;\n\n    private:\n        ErrorCode mErrorCode;\n        QString mFileName;\n        static QString sErrorToolTips[ErrorCode_LoadOrder];\n    };\n}\n\n#endif // LOADORDERERROR_HPP\n"
  },
  {
    "path": "components/contentselector/model/modelitem.cpp",
    "content": "#include \"modelitem.hpp\"\n\nContentSelectorModel::ModelItem::ModelItem(ModelItem *parent)\n    : mParentItem(parent)\n{\n}\n/*\nContentSelectorModel::ModelItem::ModelItem(const ModelItem *parent)\n   // : mParentItem(parent)\n{\n}\n*/\n\nContentSelectorModel::ModelItem::~ModelItem()\n{\n    qDeleteAll(mChildItems);\n}\n\n\nContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::parent() const\n{\n    return mParentItem;\n}\n\nbool ContentSelectorModel::ModelItem::hasFormat(const QString &mimetype) const\n{\n    if (mimetype == \"application/omwcontent\")\n        return true;\n\n    return QMimeData::hasFormat(mimetype);\n}\nint ContentSelectorModel::ModelItem::row() const\n{\n    if (mParentItem)\n        return 1;\n        //return mParentItem->childRow(const_cast<ModelItem*>(this));\n        //return mParentItem->mChildItems.indexOf(const_cast<ModelItem*>(this));\n\n    return -1;\n}\n\n\nint ContentSelectorModel::ModelItem::childCount() const\n{\n    return mChildItems.count();\n}\n\nint ContentSelectorModel::ModelItem::childRow(ModelItem *child) const\n{\n    Q_ASSERT(child);\n\n    return mChildItems.indexOf(child);\n}\n\nContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::child(int row)\n{\n    return mChildItems.value(row);\n}\n\n\nvoid ContentSelectorModel::ModelItem::appendChild(ModelItem *item)\n{\n    mChildItems.append(item);\n}\n\nvoid ContentSelectorModel::ModelItem::removeChild(int row)\n{\n    mChildItems.removeAt(row);\n}\n"
  },
  {
    "path": "components/contentselector/model/modelitem.hpp",
    "content": "#ifndef MODELITEM_HPP\n#define MODELITEM_HPP\n\n#include <QMimeData>\n#include <QList>\n\nnamespace ContentSelectorModel\n{\n    class ModelItem : public QMimeData\n    {\n        Q_OBJECT\n\n    public:\n        ModelItem(ModelItem *parent = nullptr);\n        //ModelItem(const ModelItem *parent = 0);\n\n        ~ModelItem();\n\n        ModelItem *parent() const;\n        int row() const;\n\n        int childCount() const;\n        int childRow(ModelItem *child) const;\n        ModelItem *child(int row);\n\n        void appendChild(ModelItem *child);\n        void removeChild(int row);\n\n        bool hasFormat(const QString &mimetype) const override;\n\n        //virtual bool acceptChild(ModelItem *child);\n\n    protected:\n        ModelItem *mParentItem;\n        QList<ModelItem*> mChildItems;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/contentselector/model/naturalsort.cpp",
    "content": "/*\n * This file contains code found in the QtGui module of the Qt Toolkit.\n * See Qt's qfilesystemmodel source files for more information\n */\n\n#include \"naturalsort.hpp\"\n\nstatic inline QChar getNextChar(const QString &s, int location)\n{\n    return (location < s.length()) ? s.at(location) : QChar();\n}\n\n/*!\n *   Natural number sort, skips spaces.\n *\n *   Examples:\n *   1, 2, 10, 55, 100\n *   01.jpg, 2.jpg, 10.jpg\n *\n *   Note on the algorithm:\n *   Only as many characters as necessary are looked at and at most they all\n *   are looked at once.\n *\n *   Slower then QString::compare() (of course)\n */\nint naturalCompare(const QString &s1, const QString &s2,  Qt::CaseSensitivity cs)\n{\n    for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) {\n        // skip spaces, tabs and 0's\n        QChar c1 = getNextChar(s1, l1);\n        while (c1.isSpace())\n            c1 = getNextChar(s1, ++l1);\n        QChar c2 = getNextChar(s2, l2);\n        while (c2.isSpace())\n            c2 = getNextChar(s2, ++l2);\n\n        if (c1.isDigit() && c2.isDigit()) {\n            while (c1.digitValue() == 0)\n                c1 = getNextChar(s1, ++l1);\n            while (c2.digitValue() == 0)\n                c2 = getNextChar(s2, ++l2);\n\n            int lookAheadLocation1 = l1;\n            int lookAheadLocation2 = l2;\n            int currentReturnValue = 0;\n            // find the last digit, setting currentReturnValue as we go if it isn't equal\n            for (\n                QChar lookAhead1 = c1, lookAhead2 = c2;\n            (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());\n            lookAhead1 = getNextChar(s1, ++lookAheadLocation1),\n                 lookAhead2 = getNextChar(s2, ++lookAheadLocation2)\n            ) {\n                bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();\n                bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();\n                if (!is1ADigit && !is2ADigit)\n                    break;\n                if (!is1ADigit)\n                    return -1;\n                if (!is2ADigit)\n                    return 1;\n                if (currentReturnValue == 0) {\n                    if (lookAhead1 < lookAhead2) {\n                        currentReturnValue = -1;\n                    } else if (lookAhead1 > lookAhead2) {\n                        currentReturnValue = 1;\n                    }\n                }\n            }\n            if (currentReturnValue != 0)\n                return currentReturnValue;\n        }\n\n        if (cs == Qt::CaseInsensitive) {\n            if (!c1.isLower()) c1 = c1.toLower();\n            if (!c2.isLower()) c2 = c2.toLower();\n        }\n        int r = QString::localeAwareCompare(c1, c2);\n        if (r < 0)\n            return -1;\n        if (r > 0)\n            return 1;\n    }\n    // The two strings are the same (02 == 2) so fall back to the normal sort\n    return QString::compare(s1, s2, cs);\n}\n\nbool naturalSortLessThanCS( const QString &left, const QString &right )\n{\n    return (naturalCompare( left, right, Qt::CaseSensitive ) < 0);\n}\n\nbool naturalSortLessThanCI( const QString &left, const QString &right )\n{\n    return (naturalCompare( left, right, Qt::CaseInsensitive ) < 0);\n}\n\nbool naturalSortGreaterThanCS( const QString &left, const QString &right )\n{\n    return (naturalCompare( left, right, Qt::CaseSensitive ) > 0);\n}\n\nbool naturalSortGreaterThanCI( const QString &left, const QString &right )\n{\n    return (naturalCompare( left, right, Qt::CaseInsensitive ) > 0);\n}\n"
  },
  {
    "path": "components/contentselector/model/naturalsort.hpp",
    "content": "#ifndef NATURALSORT_H\n#define NATURALSORT_H\n\n#include <QString>\n\n    bool naturalSortLessThanCS( const QString &left, const QString &right );\n    bool naturalSortLessThanCI( const QString &left, const QString &right );\n    bool naturalSortGreaterThanCS( const QString &left, const QString &right );\n    bool naturalSortGreaterThanCI( const QString &left, const QString &right );\n\n#endif\n"
  },
  {
    "path": "components/contentselector/view/combobox.cpp",
    "content": "#include <QString>\n#include <QKeyEvent>\n\n#include \"combobox.hpp\"\n\nContentSelectorView::ComboBox::ComboBox(QWidget *parent) :\n    QComboBox(parent)\n{\n    mValidator = new QRegExpValidator(QRegExp(\"^[a-zA-Z0-9_]*$\"), this); // Alpha-numeric + underscore\n    setValidator(mValidator);\n    setEditable(true);\n    setCompleter(nullptr);\n    setEnabled (true);\n\n    setInsertPolicy(QComboBox::NoInsert);\n}\n\nvoid ContentSelectorView::ComboBox::paintEvent(QPaintEvent *)\n{\n    QStylePainter painter(this);\n    painter.setPen(palette().color(QPalette::Text));\n\n    // draw the combobox frame, focusrect and selected etc.\n    QStyleOptionComboBox opt;\n    initStyleOption(&opt);\n    painter.drawComplexControl(QStyle::CC_ComboBox, opt);\n\n    // draw the icon and text\n    if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected\n        opt.currentText = mPlaceholderText;\n    painter.drawControl(QStyle::CE_ComboBoxLabel, opt);\n}\n\nvoid ContentSelectorView::ComboBox::setPlaceholderText(const QString &text)\n{\n    mPlaceholderText = text;\n}\n"
  },
  {
    "path": "components/contentselector/view/combobox.hpp",
    "content": "#ifndef COMBOBOX_HPP\n#define COMBOBOX_HPP\n\n#include <QComboBox>\n#include <QStylePainter>\n\nclass QString;\nclass QRegExpValidator;\n\nnamespace ContentSelectorView\n{\n    class ComboBox : public QComboBox\n    {\n        Q_OBJECT\n\n    public:\n        explicit ComboBox (QWidget *parent = nullptr);\n\n        void setPlaceholderText(const QString &text);\n\n    private:\n        QString mPlaceholderText;\n\n    protected:\n        void paintEvent(QPaintEvent *) override;\n        QRegExpValidator *mValidator;\n    };\n}\n\n#endif // COMBOBOX_HPP\n"
  },
  {
    "path": "components/contentselector/view/contentselector.cpp",
    "content": "#include \"contentselector.hpp\"\n\n#include <components/contentselector/model/esmfile.hpp>\n\n#include <QSortFilterProxyModel>\n\n#include <QMenu>\n#include <QContextMenuEvent>\n\n#include <QClipboard>\n#include <QModelIndex>\n\nContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) :\n    QObject(parent)\n{\n    ui.setupUi(parent);\n    ui.addonView->setDragDropMode(QAbstractItemView::InternalMove);\n\n    buildContentModel();\n    buildGameFileView();\n    buildAddonView();\n}\n\nvoid ContentSelectorView::ContentSelector::buildContentModel()\n{\n    QIcon warningIcon(ui.addonView->style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(QSize(16, 15)));\n    mContentModel = new ContentSelectorModel::ContentModel(this, warningIcon);\n}\n\nvoid ContentSelectorView::ContentSelector::buildGameFileView()\n{\n    ui.gameFileView->setVisible (true);\n\n    ui.gameFileView->setPlaceholderText(QString(\"Select a game file...\"));\n\n    connect (ui.gameFileView, SIGNAL (currentIndexChanged(int)),\n             this, SLOT (slotCurrentGameFileIndexChanged(int)));\n\n    ui.gameFileView->setCurrentIndex(-1);\n    ui.gameFileView->setCurrentIndex(0);\n}\n\nclass AddOnProxyModel : public QSortFilterProxyModel\n{\npublic:\n    explicit AddOnProxyModel(QObject* parent = nullptr) :\n        QSortFilterProxyModel(parent)\n    {}\n\n    bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override\n    {\n        static const QString ContentTypeAddon = QString::number((int)ContentSelectorModel::ContentType_Addon);\n\n        QModelIndex nameIndex = sourceModel()->index(sourceRow, 0, sourceParent);\n        const QString userRole = sourceModel()->data(nameIndex, Qt::UserRole).toString();\n\n        return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent) && userRole == ContentTypeAddon;\n    }\n};\n\nvoid ContentSelectorView::ContentSelector::buildAddonView()\n{\n    ui.addonView->setVisible (true);\n\n    mAddonProxyModel = new AddOnProxyModel(this);\n    mAddonProxyModel->setFilterRegExp(searchFilter()->text());\n    mAddonProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);\n    mAddonProxyModel->setDynamicSortFilter (true);\n    mAddonProxyModel->setSourceModel (mContentModel);\n\n    connect(ui.searchFilter, SIGNAL(textEdited(QString)), mAddonProxyModel, SLOT(setFilterWildcard(QString)));\n    connect(ui.searchFilter, SIGNAL(textEdited(QString)), this, SLOT(slotSearchFilterTextChanged(QString)));\n\n    ui.addonView->setModel(mAddonProxyModel);\n\n    connect(ui.addonView, SIGNAL(activated(const QModelIndex&)), this, SLOT(slotAddonTableItemActivated(const QModelIndex&)));\n    connect(mContentModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SIGNAL(signalAddonDataChanged(QModelIndex,QModelIndex)));\n    buildContextMenu();\n}\n\nvoid ContentSelectorView::ContentSelector::buildContextMenu()\n{\n    ui.addonView->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(ui.addonView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(slotShowContextMenu(const QPoint&)));\n\n    mContextMenu = new QMenu(ui.addonView);\n    mContextMenu->addAction(tr(\"&Check Selected\"), this, SLOT(slotCheckMultiSelectedItems()));\n    mContextMenu->addAction(tr(\"&Uncheck Selected\"), this, SLOT(slotUncheckMultiSelectedItems()));\n    mContextMenu->addAction(tr(\"&Copy Path(s) to Clipboard\"), this, SLOT(slotCopySelectedItemsPaths()));\n}\n\nvoid ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList)\n{\n    clearCheckStates();\n\n    for (const QString &filepath : fileList)\n    {\n        const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath);\n        if (file && file->isGameFile())\n        {\n            setGameFile (filepath);\n            break;\n        }\n    }\n\n    setContentList(fileList);\n}\n\nvoid ContentSelectorView::ContentSelector::setGameFile(const QString &filename)\n{\n    int index = -1;\n\n    if (!filename.isEmpty())\n    {\n        const ContentSelectorModel::EsmFile *file = mContentModel->item (filename);\n        index = ui.gameFileView->findText (file->fileName());\n\n        //verify that the current index is also checked in the model\n        if (!mContentModel->setCheckState(filename, true))\n        {\n            //throw error in case file not found?\n            return;\n        }\n    }\n\n    ui.gameFileView->setCurrentIndex(index);\n}\n\nvoid ContentSelectorView::ContentSelector::clearCheckStates()\n{\n    mContentModel->uncheckAll();\n}\n\nvoid ContentSelectorView::ContentSelector::setEncoding(const QString &encoding)\n{\n    mContentModel->setEncoding(encoding);\n}\n\nvoid ContentSelectorView::ContentSelector::setContentList(const QStringList &list)\n{\n    if (list.isEmpty())\n    {\n        slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex());\n    }\n    else\n        mContentModel->setContentList(list);\n}\n\nContentSelectorModel::ContentFileList\n        ContentSelectorView::ContentSelector::selectedFiles() const\n{\n    if (!mContentModel)\n        return ContentSelectorModel::ContentFileList();\n\n    return mContentModel->checkedItems();\n}\n\nvoid ContentSelectorView::ContentSelector::addFiles(const QString &path)\n{\n    mContentModel->addFiles(path);\n\n    // add any game files to the combo box\n    for (const QString& gameFileName : mContentModel->gameFiles())\n    {\n        if (ui.gameFileView->findText(gameFileName) == -1)\n        {\n            ui.gameFileView->addItem(gameFileName);\n        }\n    }\n\n    if (ui.gameFileView->currentIndex() != -1)\n        ui.gameFileView->setCurrentIndex(-1);\n\n    mContentModel->uncheckAll();\n}\n\nvoid ContentSelectorView::ContentSelector::clearFiles()\n{\n    mContentModel->clearFiles();\n}\n\nQString ContentSelectorView::ContentSelector::currentFile() const\n{\n    QModelIndex currentIdx = ui.addonView->currentIndex();\n\n    if (!currentIdx.isValid())\n        return ui.gameFileView->currentText();\n\n    QModelIndex idx = mContentModel->index(mAddonProxyModel->mapToSource(currentIdx).row(), 0, QModelIndex());\n    return mContentModel->data(idx, Qt::DisplayRole).toString();\n}\n\nvoid ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index)\n{\n    static int oldIndex = -1;\n\n    if (index != oldIndex)\n    {\n        if (oldIndex > -1)\n        {\n            setGameFileSelected(oldIndex, false);\n        }\n\n        oldIndex = index;\n\n        setGameFileSelected(index, true);\n        mContentModel->checkForLoadOrderErrors();\n    }\n\n    emit signalCurrentGamefileIndexChanged (index);\n}\n\nvoid ContentSelectorView::ContentSelector::setGameFileSelected(int index, bool selected)\n{\n    QString fileName = ui.gameFileView->itemText(index);\n    const ContentSelectorModel::EsmFile* file = mContentModel->item(fileName);\n    if (file != nullptr)\n    {\n        QModelIndex index2(mContentModel->indexFromItem(file));\n        mContentModel->setData(index2, selected, Qt::UserRole + 1);\n    }\n}\n\nvoid ContentSelectorView::ContentSelector::slotAddonTableItemActivated(const QModelIndex &index)\n{\n    // toggles check state when an AddOn file is double clicked or activated by keyboard\n    QModelIndex sourceIndex = mAddonProxyModel->mapToSource (index);\n\n    if (!mContentModel->isEnabled (sourceIndex))\n        return;\n\n    Qt::CheckState checkState = Qt::Unchecked;\n\n    if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() == Qt::Unchecked)\n        checkState = Qt::Checked;\n\n    mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole);\n}\n\nvoid ContentSelectorView::ContentSelector::slotShowContextMenu(const QPoint& pos)\n{\n    QPoint globalPos = ui.addonView->viewport()->mapToGlobal(pos);\n    mContextMenu->exec(globalPos);\n}\n\nvoid ContentSelectorView::ContentSelector::setCheckStateForMultiSelectedItems(bool checked)\n{\n    Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked;\n    for (const QModelIndex& index : ui.addonView->selectionModel()->selectedIndexes())\n    {\n        QModelIndex sourceIndex = mAddonProxyModel->mapToSource(index);\n        if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() != checkState)\n        {\n            mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole);\n        }\n    }\n}\n\nvoid ContentSelectorView::ContentSelector::slotUncheckMultiSelectedItems()\n{\n    setCheckStateForMultiSelectedItems(false);\n}\n\nvoid ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems()\n{\n    setCheckStateForMultiSelectedItems(true);\n}\n\nvoid ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths()\n{\n    QClipboard *clipboard = QApplication::clipboard();\n    QString filepaths;\n    for (const QModelIndex& index : ui.addonView->selectionModel()->selectedIndexes())\n    {\n        int row = mAddonProxyModel->mapToSource(index).row();\n        const ContentSelectorModel::EsmFile *file = mContentModel->item(row);\n        filepaths += file->filePath() + \"\\n\";\n    }\n\n    if (!filepaths.isEmpty())\n    {\n        clipboard->setText(filepaths);\n    }\n}\n\nvoid ContentSelectorView::ContentSelector::slotSearchFilterTextChanged(const QString& newText)\n{\n    ui.addonView->setDragEnabled(newText.isEmpty());\n}\n"
  },
  {
    "path": "components/contentselector/view/contentselector.hpp",
    "content": "#ifndef CONTENTSELECTOR_HPP\n#define CONTENTSELECTOR_HPP\n\n#include <QDialog>\n\n#include \"ui_contentselector.h\"\n#include <components/contentselector/model/contentmodel.hpp>\n\nclass QSortFilterProxyModel;\n\nnamespace ContentSelectorView\n{\n    class ContentSelector : public QObject\n    {\n        Q_OBJECT\n\n        QMenu *mContextMenu;\n\n    protected:\n\n        ContentSelectorModel::ContentModel *mContentModel;\n        QSortFilterProxyModel *mAddonProxyModel;\n\n    public:\n\n        explicit ContentSelector(QWidget *parent = nullptr);\n\n        QString currentFile() const;\n\n        void addFiles(const QString &path);\n        void clearFiles();\n        void setProfileContent (const QStringList &fileList);\n\n        void clearCheckStates();\n        void setEncoding (const QString &encoding);\n        void setContentList(const QStringList &list);\n\n        ContentSelectorModel::ContentFileList selectedFiles() const;\n\n        void setGameFile (const QString &filename = QString(\"\"));\n\n        bool isGamefileSelected() const\n            { return ui.gameFileView->currentIndex() != -1; }\n\n        QWidget *uiWidget() const\n            { return ui.contentGroupBox; }\n            \n        QToolButton *refreshButton() const  \n            { return ui.refreshButton; }        \n\n        QLineEdit *searchFilter() const\n            { return ui.searchFilter; }\n\n\n   private:\n\n        Ui::ContentSelector ui;\n\n        void buildContentModel();\n        void buildGameFileView();\n        void buildAddonView();\n        void buildContextMenu();\n        void setGameFileSelected(int index, bool selected);\n        void setCheckStateForMultiSelectedItems(bool checked);\n\n    signals:\n        void signalCurrentGamefileIndexChanged (int);\n\n        void signalAddonDataChanged (const QModelIndex& topleft, const QModelIndex& bottomright);\n        void signalSelectedFilesChanged(QStringList selectedFiles);\n\n    private slots:\n\n        void slotCurrentGameFileIndexChanged(int index);\n        void slotAddonTableItemActivated(const QModelIndex& index);\n        void slotShowContextMenu(const QPoint& pos);\n        void slotCheckMultiSelectedItems();\n        void slotUncheckMultiSelectedItems();\n        void slotCopySelectedItemsPaths();\n        void slotSearchFilterTextChanged(const QString& newText);\n    };\n}\n\n#endif // CONTENTSELECTOR_HPP\n"
  },
  {
    "path": "components/crashcatcher/crashcatcher.cpp",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <sys/param.h>\n#include <sys/utsname.h>\n#include <string.h>\n#include <errno.h>\n#include <limits.h>\n\n#include <pthread.h>\n#include <stdbool.h>\n#include <sys/ptrace.h>\n\n#include <components/debug/debuglog.hpp>\n\n#include <boost/filesystem/fstream.hpp>\n#include <boost/filesystem/operations.hpp>\n\nnamespace bfs = boost::filesystem;\n\n#include <SDL_messagebox.h>\n\n#ifdef __linux__\n#include <sys/prctl.h>\n#include <sys/ucontext.h>\n#ifndef PR_SET_PTRACER\n#define PR_SET_PTRACER 0x59616d61\n#endif\n#elif defined (__APPLE__) || defined (__FreeBSD__) || defined(__OpenBSD__)\n#include <signal.h>\n#endif\n\n#if defined(__APPLE__)\n#include <sys/sysctl.h>\n#include <libproc.h>\n#endif\n\n#if defined(__FreeBSD__)\n#include <sys/sysctl.h>\n#include <sys/user.h>\n#endif\n\n#include \"crashcatcher.hpp\"\n\nstatic const char fatal_err[] = \"\\n\\n*** Fatal Error ***\\n\";\nstatic const char pipe_err[] = \"!!! Failed to create pipe\\n\";\nstatic const char fork_err[] = \"!!! Failed to fork debug process\\n\";\nstatic const char exec_err[] = \"!!! Failed to exec debug process\\n\";\n\n#ifndef PATH_MAX /* Not all platforms (GNU Hurd) have this. */\n#   define PATH_MAX 256\n#endif\n\nstatic char argv0[PATH_MAX];\n\nstatic char altstack[SIGSTKSZ];\n\n\nstatic struct {\n    int signum;\n    pid_t pid;\n    int has_siginfo;\n    siginfo_t siginfo;\n    char buf[1024];\n} crash_info;\n\n\nstatic const struct {\n    const char *name;\n    int signum;\n} signals[] = {\n{ \"Segmentation fault\", SIGSEGV },\n{ \"Illegal instruction\", SIGILL },\n{ \"FPU exception\", SIGFPE },\n{ \"System BUS error\", SIGBUS },\n{ nullptr, 0 }\n};\n\nstatic const struct {\n    int code;\n    const char *name;\n} sigill_codes[] = {\n    #if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)\n    { ILL_ILLOPC, \"Illegal opcode\" },\n    { ILL_ILLOPN, \"Illegal operand\" },\n    { ILL_ILLADR, \"Illegal addressing mode\" },\n    { ILL_ILLTRP, \"Illegal trap\" },\n    { ILL_PRVOPC, \"Privileged opcode\" },\n    { ILL_PRVREG, \"Privileged register\" },\n    { ILL_COPROC, \"Coprocessor error\" },\n    { ILL_BADSTK, \"Internal stack error\" },\n    #endif\n    { 0, nullptr }\n};\n\nstatic const struct {\n    int code;\n    const char *name;\n} sigfpe_codes[] = {\n    { FPE_INTDIV, \"Integer divide by zero\" },\n    { FPE_INTOVF, \"Integer overflow\" },\n    { FPE_FLTDIV, \"Floating point divide by zero\" },\n    { FPE_FLTOVF, \"Floating point overflow\" },\n    { FPE_FLTUND, \"Floating point underflow\" },\n    { FPE_FLTRES, \"Floating point inexact result\" },\n    { FPE_FLTINV, \"Floating point invalid operation\" },\n    { FPE_FLTSUB, \"Subscript out of range\" },\n    { 0, nullptr }\n};\n\nstatic const struct {\n    int code;\n    const char *name;\n} sigsegv_codes[] = {\n    #ifndef __FreeBSD__\n    { SEGV_MAPERR, \"Address not mapped to object\" },\n    { SEGV_ACCERR, \"Invalid permissions for mapped object\" },\n    #endif\n    { 0, nullptr }\n};\n\nstatic const struct {\n    int code;\n    const char *name;\n} sigbus_codes[] = {\n    #ifndef __FreeBSD__\n    { BUS_ADRALN, \"Invalid address alignment\" },\n    { BUS_ADRERR, \"Non-existent physical address\" },\n    { BUS_OBJERR, \"Object specific hardware error\" },\n    #endif\n    { 0, nullptr }\n};\n\nstatic int (*cc_user_info)(char*, char*);\n\n\nstatic void gdb_info(pid_t pid)\n{\n    char respfile[64];\n    FILE *f;\n    int fd;\n\n    /*\n     * Create a temp file to put gdb commands into.\n     * Note: POSIX.1-2008 declares that the file should be already created with mode 0600 by default.\n     * Modern systems implement it and suggest to do not touch masks in multithreaded applications.\n     * So CoverityScan warning is valid only for ancient versions of stdlib.\n    */\n    strcpy(respfile, \"/tmp/gdb-respfile-XXXXXX\");\n#ifdef __COVERITY__\n    umask(0600);\n#endif\n    if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, \"w\")) != nullptr)\n    {\n        fprintf(f, \"attach %d\\n\"\n                \"shell echo \\\"\\\"\\n\"\n                \"shell echo \\\"* Loaded Libraries\\\"\\n\"\n                \"info sharedlibrary\\n\"\n                \"shell echo \\\"\\\"\\n\"\n                \"shell echo \\\"* Threads\\\"\\n\"\n                \"info threads\\n\"\n                \"shell echo \\\"\\\"\\n\"\n                \"shell echo \\\"* FPU Status\\\"\\n\"\n                \"info float\\n\"\n                \"shell echo \\\"\\\"\\n\"\n                \"shell echo \\\"* Registers\\\"\\n\"\n                \"info registers\\n\"\n                \"shell echo \\\"\\\"\\n\"\n                \"shell echo \\\"* Backtrace\\\"\\n\"\n                \"thread apply all backtrace full 1000\\n\"\n                \"detach\\n\"\n                \"quit\\n\", pid);\n        fclose(f);\n\n        /* Run gdb and print process info. */\n        char cmd_buf[128];\n        snprintf(cmd_buf, sizeof(cmd_buf), \"gdb --quiet --batch --command=%s\", respfile);\n        printf(\"Executing: %s\\n\", cmd_buf);\n        fflush(stdout);\n\n        int ret = system(cmd_buf);\n\n        if (ret != 0)\n            printf(\"\\nFailed to create a crash report. Please make sure that 'gdb' is installed and present in PATH then crash again.\"\n                   \"\\nCurrent PATH: %s\\n\", getenv(\"PATH\"));\n        fflush(stdout);\n\n        /* Clean up */\n        if (remove(respfile) != 0)\n            Log(Debug::Warning) << \"Warning: can not remove file '\" << respfile << \"': \" << std::strerror(errno);\n    }\n    else\n    {\n        /* Error creating temp file */\n        if(fd >= 0)\n        {\n            if (close(fd) != 0)\n                Log(Debug::Warning) << \"Warning: can not close file '\" << respfile << \"': \" << std::strerror(errno);\n            else if (remove(respfile) != 0)\n                Log(Debug::Warning) << \"Warning: can not remove file '\" << respfile << \"': \" << std::strerror(errno);\n        }\n        printf(\"!!! Could not create gdb command file\\n\");\n    }\n    fflush(stdout);\n}\n\nstatic void sys_info(void)\n{\n#ifdef __unix__\n    struct utsname info;\n    if(uname(&info))\n        printf(\"!!! Failed to get system information\\n\");\n    else\n        printf(\"System: %s %s %s %s %s\\n\",\n               info.sysname, info.nodename, info.release, info.version, info.machine);\n\n    fflush(stdout);\n#endif\n}\n\nstatic size_t safe_write(int fd, const void *buf, size_t len)\n{\n    size_t ret = 0;\n    while(ret < len)\n    {\n        ssize_t rem;\n        if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1)\n        {\n            if(errno == EINTR)\n                continue;\n            break;\n        }\n        ret += rem;\n    }\n    return ret;\n}\n\nstatic void crash_catcher(int signum, siginfo_t *siginfo, void *context)\n{\n    //ucontext_t *ucontext = (ucontext_t*)context;\n    pid_t dbg_pid;\n    int fd[2];\n\n    /* Make sure the effective uid is the real uid */\n    if(getuid() != geteuid())\n    {\n        raise(signum);\n        return;\n    }\n\n    safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1);\n    if(pipe(fd) == -1)\n    {\n        safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1);\n        raise(signum);\n        return;\n    }\n\n    crash_info.signum = signum;\n    crash_info.pid = getpid();\n    crash_info.has_siginfo = !!siginfo;\n    if(siginfo)\n        crash_info.siginfo = *siginfo;\n    if(cc_user_info)\n        cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf));\n\n    /* Fork off to start a crash handler */\n    switch((dbg_pid=fork()))\n    {\n    /* Error */\n    case -1:\n        safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1);\n        raise(signum);\n        return;\n\n    case 0:\n        dup2(fd[0], STDIN_FILENO);\n        close(fd[0]);\n        close(fd[1]);\n\n        execl(argv0, argv0, crash_switch, nullptr);\n\n        safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1);\n        _exit(1);\n\n    default:\n#ifdef __linux__\n        prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0);\n#endif\n        safe_write(fd[1], &crash_info, sizeof(crash_info));\n        close(fd[0]);\n        close(fd[1]);\n\n        /* Wait; we'll be killed when gdb is done */\n        do {\n            int status;\n            if(waitpid(dbg_pid, &status, 0) == dbg_pid &&\n                    (WIFEXITED(status) || WIFSIGNALED(status)))\n            {\n                /* The debug process died before it could kill us */\n                raise(signum);\n                break;\n            }\n        } while(1);\n    }\n}\n\nstatic void crash_handler(const char *logfile)\n{\n    const char *sigdesc = \"\";\n    int i;\n\n    if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1)\n    {\n        fprintf(stderr, \"!!! Failed to retrieve info from crashed process\\n\");\n        exit(1);\n    }\n\n    /* Get the signal description */\n    for(i = 0;signals[i].name;++i)\n    {\n        if(signals[i].signum == crash_info.signum)\n        {\n            sigdesc = signals[i].name;\n            break;\n        }\n    }\n\n    if(crash_info.has_siginfo)\n    {\n        switch(crash_info.signum)\n        {\n        case SIGSEGV:\n            for(i = 0;sigsegv_codes[i].name;++i)\n            {\n                if(sigsegv_codes[i].code == crash_info.siginfo.si_code)\n                {\n                    sigdesc = sigsegv_codes[i].name;\n                    break;\n                }\n            }\n            break;\n\n        case SIGFPE:\n            for(i = 0;sigfpe_codes[i].name;++i)\n            {\n                if(sigfpe_codes[i].code == crash_info.siginfo.si_code)\n                {\n                    sigdesc = sigfpe_codes[i].name;\n                    break;\n                }\n            }\n            break;\n\n        case SIGILL:\n            for(i = 0;sigill_codes[i].name;++i)\n            {\n                if(sigill_codes[i].code == crash_info.siginfo.si_code)\n                {\n                    sigdesc = sigill_codes[i].name;\n                    break;\n                }\n            }\n            break;\n\n        case SIGBUS:\n            for(i = 0;sigbus_codes[i].name;++i)\n            {\n                if(sigbus_codes[i].code == crash_info.siginfo.si_code)\n                {\n                    sigdesc = sigbus_codes[i].name;\n                    break;\n                }\n            }\n            break;\n        }\n    }\n    fprintf(stderr, \"%s (signal %i)\\n\", sigdesc, crash_info.signum);\n    if(crash_info.has_siginfo)\n        fprintf(stderr, \"Address: %p\\n\", crash_info.siginfo.si_addr);\n    fputc('\\n', stderr);\n\n    if(logfile)\n    {\n        /* Create crash log file and redirect shell output to it */\n        if(freopen(logfile, \"wa\", stdout) != stdout)\n        {\n            fprintf(stderr, \"!!! Could not create %s following signal\\n\", logfile);\n            exit(1);\n        }\n        fprintf(stderr, \"Generating %s and killing process %d, please wait... \", logfile, crash_info.pid);\n\n        printf(\"*** Fatal Error ***\\n\"\n               \"%s (signal %i)\\n\", sigdesc, crash_info.signum);\n        if(crash_info.has_siginfo)\n            printf(\"Address: %p\\n\", crash_info.siginfo.si_addr);\n        fputc('\\n', stdout);\n        fflush(stdout);\n    }\n\n    sys_info();\n\n    crash_info.buf[sizeof(crash_info.buf)-1] = '\\0';\n    printf(\"%s\\n\", crash_info.buf);\n    fflush(stdout);\n\n    if(crash_info.pid > 0)\n    {\n        gdb_info(crash_info.pid);\n        kill(crash_info.pid, SIGKILL);\n    }\n\n    // delay between killing of the crashed process and showing the message box to\n    // work around occasional X server lock-up. this can only be a bug in X11 since\n    // even faulty applications shouldn't be able to freeze the X server.\n    usleep(100000);\n\n    if(logfile)\n    {\n        std::string message = \"OpenMW has encountered a fatal error.\\nCrash log saved to '\" + std::string(logfile) + \"'.\\n Please report this to https://gitlab.com/OpenMW/openmw/issues !\";\n        SDL_ShowSimpleMessageBox(0, \"Fatal Error\", message.c_str(), nullptr);\n    }\n\n    exit(0);\n}\n\nstatic void getExecPath(char **argv)\n{\n#if defined (__FreeBSD__)\n    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };\n    size_t size = sizeof(argv0);\n\n    if (sysctl(mib, 4, argv0, &size, nullptr, 0) == 0)\n        return;\n#endif\n\n#if defined (__APPLE__)\n    if(proc_pidpath(getpid(), argv0, sizeof(argv0)) > 0)\n        return;\n#endif\n    int cwdlen;\n    const char *statusPaths[] = {\"/proc/self/exe\", \"/proc/self/file\", \"/proc/curproc/exe\", \"/proc/curproc/file\"};\n    memset(argv0, 0, sizeof(argv0));\n\n    for(const char *path : statusPaths)\n    {\n        if (readlink(path, argv0, sizeof(argv0)) != -1)\n            return;\n    }\n\n    if(argv[0][0] == '/')\n        snprintf(argv0, sizeof(argv0), \"%s\", argv[0]);\n    else if (getcwd(argv0, sizeof(argv0)) != nullptr)\n    {\n        cwdlen = strlen(argv0);\n        snprintf(argv0+cwdlen, sizeof(argv0)-cwdlen, \"/%s\", argv[0]);\n    }\n}\n\nint crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*))\n{\n    struct sigaction sa;\n    stack_t altss;\n    int retval;\n\n    if(argc == 2 && strcmp(argv[1], crash_switch) == 0)\n        crash_handler(logfile);\n\n    cc_user_info = user_info;\n\n    getExecPath(argv);\n\n    /* Set an alternate signal stack so SIGSEGVs caused by stack overflows\n     * still run */\n    altss.ss_sp = altstack;\n    altss.ss_flags = 0;\n    altss.ss_size = sizeof(altstack);\n    sigaltstack(&altss, nullptr);\n\n    memset(&sa, 0, sizeof(sa));\n    sa.sa_sigaction = crash_catcher;\n    sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK;\n    sigemptyset(&sa.sa_mask);\n\n    retval = 0;\n    while(num_signals--)\n    {\n        if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && *signals != SIGABRT &&\n            *signals != SIGBUS) || sigaction(*signals, &sa, nullptr) == -1)\n        {\n            *signals = 0;\n            retval = -1;\n        }\n        ++signals;\n    }\n    return retval;\n}\n\nstatic bool is_debugger_present()\n{\n#if defined (__linux__)\n    bfs::path procstatus = bfs::path(\"/proc/self/status\");\n    if (bfs::exists(procstatus))\n    {\n        bfs::ifstream file((procstatus));\n        while (!file.eof())\n        {\n            std::string word;\n            file >> word;\n            if (word == \"TracerPid:\")\n            {\n                file >> word;\n                return word != \"0\";\n            }\n        }\n    }\n    return false;\n#elif defined(__APPLE__)\n    int junk;\n    int mib[4];\n    struct kinfo_proc info;\n    size_t size;\n\n    // Initialize the flags so that, if sysctl fails for some bizarre\n    // reason, we get a predictable result.\n\n    info.kp_proc.p_flag = 0;\n\n    // Initialize mib, which tells sysctl the info we want, in this case\n    // we're looking for information about a specific process ID.\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC;\n    mib[2] = KERN_PROC_PID;\n    mib[3] = getpid();\n\n    // Call sysctl.\n\n    size = sizeof(info);\n    junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0);\n    assert(junk == 0);\n\n    // We're being debugged if the P_TRACED flag is set.\n\n    return (info.kp_proc.p_flag & P_TRACED) != 0;\n#elif defined(__FreeBSD__)\n    struct kinfo_proc info;\n    size_t size = sizeof(info);\n    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };\n\n    if (sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) == 0)\n        return (info.ki_flag & P_TRACED) != 0;\n    else\n        perror(\"Failed to retrieve process info\");\n    return false;\n#else\n    return false;\n#endif\n}\n\nvoid crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath)\n{\n    if (const auto env = std::getenv(\"OPENMW_DISABLE_CRASH_CATCHER\"))\n        if (std::atol(env) != 0)\n            return;\n    if ((argc == 2 && strcmp(argv[1], crash_switch) == 0) || !is_debugger_present())\n    {\n        int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };\n        if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), nullptr) == -1)\n        {\n            Log(Debug::Warning) << \"Installing crash handler failed\";\n        }\n        else\n            Log(Debug::Info) << \"Crash handler installed\";\n    }\n}\n"
  },
  {
    "path": "components/crashcatcher/crashcatcher.hpp",
    "content": "#ifndef CRASHCATCHER_H\n#define CRASHCATCHER_H\n\n#include <string>\n\n#if (defined(__APPLE__) || (defined(__linux)  &&  !defined(ANDROID)) || (defined(__unix) &&  !defined(ANDROID)) || defined(__posix))\n    #define USE_CRASH_CATCHER 0\n#else\n    #define USE_CRASH_CATCHER 0\n#endif\n\nconstexpr char crash_switch[] = \"--cc-handle-crash\";\n\n#if USE_CRASH_CATCHER\nextern void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath);\n#else\ninline void crashCatcherInstall(int, char **, const std::string &crashLogPath)\n{\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "components/crashcatcher/windows_crashcatcher.cpp",
    "content": "#include <cassert>\n#include <cwchar>\n#include <iostream>\n#include <sstream>\n#include <thread>\n\n#include \"windows_crashcatcher.hpp\"\n#include \"windows_crashmonitor.hpp\"\n#include \"windows_crashshm.hpp\"\n#include <SDL_messagebox.h>\n\nnamespace Crash\n{\n\n    HANDLE duplicateHandle(HANDLE handle)\n    {\n        HANDLE duplicate;\n        if (!DuplicateHandle(GetCurrentProcess(), handle,\n                             GetCurrentProcess(), &duplicate,\n                             0, TRUE, DUPLICATE_SAME_ACCESS))\n        {\n            throw std::runtime_error(\"Crash monitor could not duplicate handle\");\n        }\n        return duplicate;\n    }\n\n    CrashCatcher* CrashCatcher::sInstance = nullptr;\n\n    CrashCatcher::CrashCatcher(int argc, char **argv, const std::string& crashLogPath)\n    {\n        assert(sInstance == nullptr); // don't allow two instances\n\n        sInstance = this;\n\n        HANDLE shmHandle = nullptr;\n        for (int i=0; i<argc; ++i)\n        {\n            if (strcmp(argv[i], \"--crash-monitor\"))\n                continue;\n\n            if (i >= argc - 1)\n                throw std::runtime_error(\"Crash monitor is missing the SHM handle argument\");\n\n            sscanf(argv[i + 1], \"%p\", &shmHandle);\n            break;\n        }\n\n        if (!shmHandle)\n        {\n            setupIpc();\n            startMonitorProcess(crashLogPath);\n            installHandler();\n        }\n        else\n        {\n            CrashMonitor(shmHandle).run();\n            exit(0);\n        }\n    }\n\n    CrashCatcher::~CrashCatcher()\n    {\n        sInstance = nullptr;\n\n        if (mShm && mSignalMonitorEvent)\n        {\n            shmLock();\n            mShm->mEvent = CrashSHM::Event::Shutdown;\n            shmUnlock();\n\n            SetEvent(mSignalMonitorEvent);\n        }\n\n        if (mShmHandle)\n            CloseHandle(mShmHandle);\n    }\n\n    void CrashCatcher::setupIpc()\n    {\n        SECURITY_ATTRIBUTES attributes;\n        ZeroMemory(&attributes, sizeof(attributes));\n        attributes.bInheritHandle = TRUE;\n\n        mSignalAppEvent = CreateEventW(&attributes, FALSE, FALSE, NULL);\n        mSignalMonitorEvent = CreateEventW(&attributes, FALSE, FALSE, NULL);\n\n        mShmHandle = CreateFileMappingW(INVALID_HANDLE_VALUE, &attributes, PAGE_READWRITE, HIWORD(sizeof(CrashSHM)), LOWORD(sizeof(CrashSHM)), NULL);\n        if (mShmHandle == nullptr)\n            throw std::runtime_error(\"Failed to allocate crash catcher shared memory\");\n\n        mShm = reinterpret_cast<CrashSHM*>(MapViewOfFile(mShmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashSHM)));\n        if (mShm == nullptr)\n            throw std::runtime_error(\"Failed to map crash catcher shared memory\");\n\n        mShmMutex = CreateMutexW(&attributes, FALSE, NULL);\n        if (mShmMutex == nullptr)\n            throw std::runtime_error(\"Failed to create crash catcher shared memory mutex\");\n    }\n\n    void CrashCatcher::shmLock()\n    {\n        if (WaitForSingleObject(mShmMutex, CrashCatcherTimeout) != WAIT_OBJECT_0)\n            throw std::runtime_error(\"SHM lock timed out\");\n    }\n\n    void CrashCatcher::shmUnlock()\n    {\n        ReleaseMutex(mShmMutex);\n    }\n\n    void CrashCatcher::waitMonitor()\n    {\n        if (WaitForSingleObject(mSignalAppEvent, CrashCatcherTimeout) != WAIT_OBJECT_0)\n            throw std::runtime_error(\"Waiting for monitor failed\");\n    }\n\n    void CrashCatcher::signalMonitor()\n    {\n        SetEvent(mSignalMonitorEvent);\n    }\n\n    void CrashCatcher::installHandler()\n    {\n        SetUnhandledExceptionFilter(vectoredExceptionHandler);\n    }\n\n    void CrashCatcher::startMonitorProcess(const std::string& crashLogPath)\n    {\n        std::wstring executablePath;\n        DWORD copied = 0;\n        do {\n            executablePath.resize(executablePath.size() + MAX_PATH);\n            copied = GetModuleFileNameW(nullptr, executablePath.data(), static_cast<DWORD>(executablePath.size()));\n        } while (copied >= executablePath.size());\n        executablePath.resize(copied);\n\n        memset(mShm->mStartup.mLogFilePath, 0, sizeof(mShm->mStartup.mLogFilePath));\n        size_t length = crashLogPath.length();\n        if (length >= MAX_LONG_PATH) length = MAX_LONG_PATH - 1;\n        strncpy(mShm->mStartup.mLogFilePath, crashLogPath.c_str(), length);\n        mShm->mStartup.mLogFilePath[length] = '\\0';\n\n        // note that we don't need to lock the SHM here, the other process has not started yet\n        mShm->mEvent = CrashSHM::Event::Startup;\n        mShm->mStartup.mShmMutex = duplicateHandle(mShmMutex);\n        mShm->mStartup.mAppProcessHandle = duplicateHandle(GetCurrentProcess());\n        mShm->mStartup.mSignalApp = duplicateHandle(mSignalAppEvent);\n        mShm->mStartup.mSignalMonitor = duplicateHandle(mSignalMonitorEvent);\n\n        std::wstringstream ss;\n        ss << \"--crash-monitor \" << std::hex << duplicateHandle(mShmHandle);\n        std::wstring arguments(ss.str());\n\n        STARTUPINFOW si;\n        ZeroMemory(&si, sizeof(si));\n\n        PROCESS_INFORMATION pi;\n        ZeroMemory(&pi, sizeof(pi));\n\n        if (!CreateProcessW(executablePath.data(), arguments.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))\n            throw std::runtime_error(\"Could not start crash monitor process\");\n\n        waitMonitor();\n    }\n\n    LONG CrashCatcher::vectoredExceptionHandler(PEXCEPTION_POINTERS info)\n    {\n        switch (info->ExceptionRecord->ExceptionCode)\n        {\n        case EXCEPTION_SINGLE_STEP:\n        case EXCEPTION_BREAKPOINT:\n        case DBG_PRINTEXCEPTION_C:\n            return EXCEPTION_EXECUTE_HANDLER;\n        }\n        if (!sInstance)\n            return EXCEPTION_EXECUTE_HANDLER;\n\n        sInstance->handleVectoredException(info);\n\n        _Exit(1);\n    }\n\n    void CrashCatcher::handleVectoredException(PEXCEPTION_POINTERS info)\n    {\n        shmLock();\n\n        mShm->mEvent = CrashSHM::Event::Crashed;\n        mShm->mCrashed.mThreadId = GetCurrentThreadId();\n        mShm->mCrashed.mContext = *info->ContextRecord;\n        mShm->mCrashed.mExceptionRecord = *info->ExceptionRecord;\n\n        shmUnlock();\n\n        signalMonitor();\n\n        // must remain until monitor has finished\n        waitMonitor();\n\n        std::string message = \"TES3MP has encountered a fatal error.\\nCrash log saved to '\" + std::string(mShm->mStartup.mLogFilePath) + \"'.\\n Please report this to https://github.com/TES3MP/TES3MP/issues !\";\n        SDL_ShowSimpleMessageBox(0, \"Fatal Error\", message.c_str(), nullptr);\n    }\n\n} // namespace Crash\n"
  },
  {
    "path": "components/crashcatcher/windows_crashcatcher.hpp",
    "content": "#ifndef WINDOWS_CRASHCATCHER_HPP\n#define WINDOWS_CRASHCATCHER_HPP\n\n#include <string>\n\n#undef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#include <Windows.h>\n\n#include <components/crashcatcher/crashcatcher.hpp>\n\nnamespace Crash\n{\n\n    // The implementation spawns the current executable as a monitor process which waits\n    // for a global synchronization event which is sent when the parent process crashes.\n    // The monitor process then extracts crash information from the parent process while\n    // the parent process waits for the monitor process to finish. The crashed process\n    // quits and the monitor writes the crash information to a file.\n    //\n    // To detect unexpected shutdowns of the application which are not handled by the\n    // crash handler, the monitor periodically checks the exit code of the parent\n    // process and exits if it does not return STILL_ACTIVE. You can test this by closing\n    // the main openmw process in task manager.\n\n    static constexpr const int CrashCatcherTimeout = 2500;\n\n    struct CrashSHM;\n\n    class CrashCatcher final\n    {\n    public:\n\n        CrashCatcher(int argc, char **argv, const std::string& crashLogPath);\n        ~CrashCatcher();\n\n    private:\n\n        static CrashCatcher* sInstance;\n\n        //  mapped SHM area\n        CrashSHM* mShm = nullptr;\n        // the handle is allocated by the catcher and passed to the monitor\n        // process via the command line which maps the SHM and sends / receives\n        // events through it\n        HANDLE mShmHandle = nullptr;\n        // mutex which guards SHM area\n        HANDLE mShmMutex = nullptr;\n\n        // triggered when the monitor signals the application\n        HANDLE mSignalAppEvent = INVALID_HANDLE_VALUE;\n\n        // triggered when the application wants to wake the monitor process\n        HANDLE mSignalMonitorEvent = INVALID_HANDLE_VALUE;\n\n        void setupIpc();\n\n        void shmLock();\n\n        void shmUnlock();\n\n        void startMonitorProcess(const std::string& crashLogPath);\n\n        void waitMonitor();\n\n        void signalMonitor();\n\n        void installHandler();\n\n        void handleVectoredException(PEXCEPTION_POINTERS info);\n\n    public:\n\n        static LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS info);\n    };\n\n} // namespace Crash\n\n#endif // WINDOWS_CRASHCATCHER_HPP\n"
  },
  {
    "path": "components/crashcatcher/windows_crashmonitor.cpp",
    "content": "#undef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#include <Windows.h>\n#include <Psapi.h>\n\n#include <DbgHelp.h>\n\n#include <iostream>\n#include <memory>\n#include <sstream>\n\n#include \"windows_crashcatcher.hpp\"\n#include \"windows_crashmonitor.hpp\"\n#include \"windows_crashshm.hpp\"\n#include <components/debug/debuglog.hpp>\n\nnamespace Crash\n{\n\n    CrashMonitor::CrashMonitor(HANDLE shmHandle)\n        : mShmHandle(shmHandle)\n    {\n        mShm = reinterpret_cast<CrashSHM*>(MapViewOfFile(mShmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashSHM)));\n        if (mShm == nullptr)\n            throw std::runtime_error(\"Failed to map crash monitor shared memory\");\n\n        // accessing SHM without lock is OK here, the parent waits for a signal before continuing\n\n        mShmMutex = mShm->mStartup.mShmMutex;\n        mAppProcessHandle = mShm->mStartup.mAppProcessHandle;\n        mSignalAppEvent = mShm->mStartup.mSignalApp;\n        mSignalMonitorEvent = mShm->mStartup.mSignalMonitor;\n    }\n\n    CrashMonitor::~CrashMonitor()\n    {\n        if (mShm)\n            UnmapViewOfFile(mShm);\n\n        // the handles received from the app are duplicates, we must close them\n\n        if (mShmHandle)\n            CloseHandle(mShmHandle);\n\n        if (mShmMutex)\n            CloseHandle(mShmMutex);\n\n        if (mSignalAppEvent)\n            CloseHandle(mSignalAppEvent);\n\n        if (mSignalMonitorEvent)\n            CloseHandle(mSignalMonitorEvent);\n    }\n\n    void CrashMonitor::shmLock()\n    {\n        if (WaitForSingleObject(mShmMutex, CrashCatcherTimeout) != WAIT_OBJECT_0)\n            throw std::runtime_error(\"SHM monitor lock timed out\");\n    }\n\n    void CrashMonitor::shmUnlock()\n    {\n        ReleaseMutex(mShmMutex);\n    }\n\n    void CrashMonitor::signalApp() const\n    {\n        SetEvent(mSignalAppEvent);\n    }\n\n    bool CrashMonitor::waitApp() const\n    {\n        return WaitForSingleObject(mSignalMonitorEvent, CrashCatcherTimeout) == WAIT_OBJECT_0;\n    }\n\n    bool CrashMonitor::isAppAlive() const\n    {\n        DWORD code = 0;\n        GetExitCodeProcess(mAppProcessHandle, &code);\n        return code == STILL_ACTIVE;\n    }\n\n    void CrashMonitor::run()\n    {\n        try\n        {\n            // app waits for monitor start up, let it continue\n            signalApp();\n\n            bool running = true;\n            while (isAppAlive() && running)\n            {\n                if (waitApp())\n                {\n                    shmLock();\n\n                    switch (mShm->mEvent)\n                    {\n                    case CrashSHM::Event::None:\n                        break;\n                    case CrashSHM::Event::Crashed:\n                        handleCrash();\n                        running = false;\n                        break;\n                    case CrashSHM::Event::Shutdown:\n                        running = false;\n                        break;\n                    case CrashSHM::Event::Startup:\n                        break;\n                    }\n\n                    shmUnlock();\n                }\n            }\n\n        } \n        catch (...)\n        {\n            Log(Debug::Error) << \"Exception in crash monitor, exiting\";\n        }\n        signalApp();\n    }\n\n    std::wstring utf8ToUtf16(const std::string& utf8)\n    {\n        const int nLenWide = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), nullptr, 0);\n\n        std::wstring utf16;\n        utf16.resize(nLenWide);\n        if (MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), utf16.data(), nLenWide) != nLenWide)\n            return {};\n\n        return utf16;\n    }\n\n    void CrashMonitor::handleCrash()\n    {\n        DWORD processId = GetProcessId(mAppProcessHandle);\n\n        try\n        {\n            HMODULE dbghelp = LoadLibraryA(\"dbghelp.dll\");\n            if (dbghelp == NULL)\n                return;\n    \n            using MiniDumpWirteDumpFn = BOOL (WINAPI*)(\n                HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,\n                PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam\n            );\n    \n            MiniDumpWirteDumpFn miniDumpWriteDump = (MiniDumpWirteDumpFn)GetProcAddress(dbghelp, \"MiniDumpWriteDump\");\n            if (miniDumpWriteDump == NULL)\n                return;\n\n            std::wstring utf16Path = utf8ToUtf16(mShm->mStartup.mLogFilePath);\n            if (utf16Path.empty())\n                return;\n\n            if (utf16Path.length() > MAX_PATH)\n                utf16Path = LR\"(\\\\?\\)\" + utf16Path;\n\n            HANDLE hCrashLog = CreateFileW(utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);\n            if (hCrashLog == NULL || hCrashLog == INVALID_HANDLE_VALUE)\n                return;\n            if (auto err = GetLastError(); err != ERROR_ALREADY_EXISTS && err != 0)\n                return;\n\n            EXCEPTION_POINTERS exp;\n            exp.ContextRecord = &mShm->mCrashed.mContext;\n            exp.ExceptionRecord = &mShm->mCrashed.mExceptionRecord;\n            MINIDUMP_EXCEPTION_INFORMATION infos = {};\n            infos.ThreadId = mShm->mCrashed.mThreadId;\n            infos.ExceptionPointers = &exp;\n            infos.ClientPointers = FALSE;\n            MINIDUMP_TYPE type = (MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithHandleData);\n            miniDumpWriteDump(mAppProcessHandle, processId, hCrashLog, type, &infos, 0, 0);\n        }\n        catch (const std::exception&e)\n        {\n            Log(Debug::Error) << \"CrashMonitor: \" << e.what();\n        }\n        catch (...)\n        {\n            Log(Debug::Error) << \"CrashMonitor: unknown exception\";\n        }\n    }\n\n} // namespace Crash\n"
  },
  {
    "path": "components/crashcatcher/windows_crashmonitor.hpp",
    "content": "#ifndef WINDOWS_CRASHMONITOR_HPP\n#define WINDOWS_CRASHMONITOR_HPP\n\n#include <windef.h>\n\nnamespace Crash\n{\n\nstruct CrashSHM;\n\nclass CrashMonitor final\n{\npublic:\n\n    CrashMonitor(HANDLE shmHandle);\n\n    ~CrashMonitor();\n\n    void run();\n\nprivate:\n\n    HANDLE mAppProcessHandle = nullptr;\n\n    // triggered when the monitor process wants to wake the parent process (received via SHM)\n    HANDLE mSignalAppEvent = nullptr;\n    // triggered when the application wants to wake the monitor process (received via SHM)\n    HANDLE mSignalMonitorEvent = nullptr;\n\n    CrashSHM* mShm = nullptr;\n    HANDLE mShmHandle = nullptr;\n    HANDLE mShmMutex = nullptr;\n\n    void signalApp() const;\n\n    bool waitApp() const;\n\n    bool isAppAlive() const;\n\n    void shmLock();\n\n    void shmUnlock();\n\n    void handleCrash();\n};\n\n} // namespace Crash\n\n#endif // WINDOWS_CRASHMONITOR_HPP\n"
  },
  {
    "path": "components/crashcatcher/windows_crashshm.hpp",
    "content": "#ifndef WINDOWS_CRASHSHM_HPP\n#define WINDOWS_CRASHSHM_HPP\n\n#undef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#include <Windows.h>\n\nnamespace Crash\n{\n\n    // Used to communicate between the app and the monitor, fields are is overwritten with each event.\n    static constexpr const int MAX_LONG_PATH = 0x7fff;\n\n    struct CrashSHM\n    {\n        enum class Event\n        {\n            None,\n            Startup,\n            Crashed,\n            Shutdown\n        };\n\n        Event mEvent;\n\n        struct Startup\n        {\n            HANDLE mAppProcessHandle;\n            HANDLE mSignalApp;\n            HANDLE mSignalMonitor;\n            HANDLE mShmMutex;\n            char mLogFilePath[MAX_LONG_PATH];\n        } mStartup;\n\n        struct Crashed\n        {\n            DWORD mThreadId;\n            CONTEXT mContext;\n            EXCEPTION_RECORD mExceptionRecord;\n        } mCrashed;\n    };\n\n} // namespace Crash\n\n#endif // WINDOWS_CRASHSHM_HPP\n"
  },
  {
    "path": "components/debug/debugging.cpp",
    "content": "#include \"debugging.hpp\"\n\n#include <chrono>\n#include <memory>\n#include <functional>\n\n#include <components/crashcatcher/crashcatcher.hpp>\n\n#ifdef _WIN32\n#   include <components/crashcatcher/windows_crashcatcher.hpp>\n#   undef WIN32_LEAN_AND_MEAN\n#   define WIN32_LEAN_AND_MEAN\n#   include <windows.h>\n#endif\n\nnamespace Debug\n{\n#ifdef _WIN32\n    bool isRedirected(DWORD nStdHandle)\n    {\n        DWORD fileType = GetFileType(GetStdHandle(nStdHandle));\n\n        return (fileType == FILE_TYPE_DISK) || (fileType == FILE_TYPE_PIPE);\n    }\n\n    bool attachParentConsole()\n    {\n        if (GetConsoleWindow() != nullptr)\n            return true;\n\n        bool inRedirected = isRedirected(STD_INPUT_HANDLE);\n        bool outRedirected = isRedirected(STD_OUTPUT_HANDLE);\n        bool errRedirected = isRedirected(STD_ERROR_HANDLE);\n\n        if (AttachConsole(ATTACH_PARENT_PROCESS))\n        {\n            fflush(stdout);\n            fflush(stderr);\n            std::cout.flush();\n            std::cerr.flush();\n\n            // this looks dubious but is really the right way\n            if (!inRedirected)\n            {\n                _wfreopen(L\"CON\", L\"r\", stdin);\n                freopen(\"CON\", \"r\", stdin);\n            }\n            if (!outRedirected)\n            {\n                _wfreopen(L\"CON\", L\"w\", stdout);\n                freopen(\"CON\", \"w\", stdout);\n            }\n            if (!errRedirected)\n            {\n                _wfreopen(L\"CON\", L\"w\", stderr);\n                freopen(\"CON\", \"w\", stderr);\n            }\n\n            return true;\n        }\n\n        return false;\n    }\n#endif\n\n    std::streamsize DebugOutputBase::write(const char *str, std::streamsize size)\n    {\n        if (size <= 0)\n            return size;\n        std::string_view msg{str, size_t(size)};\n\n        // Skip debug level marker\n        Level level = getLevelMarker(str);\n        if (level != NoLevel)\n            msg = msg.substr(1);\n\n        /*\n            Start of tes3mp change (major)\n\n            Don't use these timestamps, as TES3MP has its own\n        */\n        /*\n        char prefix[32];\n        int prefixSize;\n        {\n            prefix[0] = '[';\n            uint64_t ms = std::chrono::duration_cast<std::chrono::milliseconds>(\n                std::chrono::high_resolution_clock::now().time_since_epoch()).count();\n            std::time_t t = ms / 1000;\n            prefixSize = std::strftime(prefix + 1, sizeof(prefix) - 1, \"%T\", std::localtime(&t)) + 1;\n            char levelLetter = \" EWIVD*\"[int(level)];\n            prefixSize += snprintf(prefix + prefixSize, sizeof(prefix) - prefixSize,\n                                   \".%03u %c] \", static_cast<unsigned>(ms % 1000), levelLetter);\n        }\n        */\n        /*\n            End of tes3mp change (major)\n        */\n\n        while (!msg.empty())\n        {\n            if (msg[0] == 0)\n                break;\n            size_t lineSize = 1;\n            while (lineSize < msg.size() && msg[lineSize - 1] != '\\n')\n                lineSize++;\n            /*\n                Start of tes3mp change (major)\n\n                Don't use these timestamps, as TES3MP has its own\n            */\n            //writeImpl(prefix, prefixSize, level);\n            /*\n                End of tes3mp change (major)\n            */\n            writeImpl(msg.data(), lineSize, level);\n            msg = msg.substr(lineSize);\n        }\n\n        return size;\n    }\n\n    Level DebugOutputBase::getLevelMarker(const char *str)\n    {\n        if (unsigned(*str) <= unsigned(Marker))\n        {\n            return Level(*str);\n        }\n\n        return NoLevel;\n    }\n\n    void DebugOutputBase::fillCurrentDebugLevel()\n    {\n        const char* env = getenv(\"OPENMW_DEBUG_LEVEL\");\n        if (env)\n        {\n            std::string value(env);\n            if (value == \"ERROR\")\n                CurrentDebugLevel = Error;\n            else if (value == \"WARNING\")\n                CurrentDebugLevel = Warning;\n            else if (value == \"INFO\")\n                CurrentDebugLevel = Info;\n            else if (value == \"VERBOSE\")\n                CurrentDebugLevel = Verbose;\n            else if (value == \"DEBUG\")\n                CurrentDebugLevel = Debug;\n\n            return;\n        }\n\n        CurrentDebugLevel = Verbose;\n    }\n}\n\nstatic std::unique_ptr<std::ostream> rawStdout = nullptr;\n\nstd::ostream& getRawStdout()\n{\n    return rawStdout ? *rawStdout : std::cout;\n}\n\nint wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& appName)\n{\n#if defined _WIN32\n    (void)Debug::attachParentConsole();\n#endif\n    rawStdout = std::make_unique<std::ostream>(std::cout.rdbuf());\n\n    // Some objects used to redirect cout and cerr\n    // Scope must be here, so this still works inside the catch block for logging exceptions\n    std::streambuf* cout_rdbuf = std::cout.rdbuf ();\n    std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();\n\n#if defined(_WIN32) && defined(_DEBUG)\n    boost::iostreams::stream_buffer<Debug::DebugOutput> sb;\n#else\n    boost::iostreams::stream_buffer<Debug::Tee> coutsb;\n    boost::iostreams::stream_buffer<Debug::Tee> cerrsb;\n    std::ostream oldcout(cout_rdbuf);\n    std::ostream oldcerr(cerr_rdbuf);\n#endif\n\n    const std::string logName = Misc::StringUtils::lowerCase(appName) + \".log\";\n    boost::filesystem::ofstream logfile;\n\n    int ret = 0;\n    try\n    {\n        Files::ConfigurationManager cfgMgr;\n\n#if defined(_WIN32) && defined(_DEBUG)\n        // Redirect cout and cerr to VS debug output when running in debug mode\n        sb.open(Debug::DebugOutput());\n        std::cout.rdbuf (&sb);\n        std::cerr.rdbuf (&sb);\n#else\n        // Redirect cout and cerr to the log file\n        // If we are collecting a stack trace, append to existing log file\n        std::ios_base::openmode mode = std::ios::out;\n        if(argc == 2 && strcmp(argv[1], crash_switch) == 0)\n            mode |= std::ios::app;\n\n        logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName), mode);\n\n        coutsb.open (Debug::Tee(logfile, oldcout));\n        cerrsb.open (Debug::Tee(logfile, oldcerr));\n\n        std::cout.rdbuf (&coutsb);\n        std::cerr.rdbuf (&cerrsb);\n#endif\n\n#if defined(_WIN32)\n        const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + \"-crash.dmp\";\n        Crash::CrashCatcher crashy(argc, argv, (cfgMgr.getLogPath() / crashLogName).make_preferred().string());\n#else\n        const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + \"-crash.log\";\n        // install the crash handler as soon as possible. note that the log path\n        // does not depend on config being read.\n        crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / crashLogName).string());\n#endif\n        ret = innerApplication(argc, argv);\n    }\n    catch (const std::exception& e)\n    {\n#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))\n        if (!isatty(fileno(stdin)))\n#endif\n            SDL_ShowSimpleMessageBox(0, (appName + \": Fatal error\").c_str(), e.what(), nullptr);\n\n        Log(Debug::Error) << \"Error: \" << e.what();\n\n        ret = 1;\n    }\n\n    // Restore cout and cerr\n    std::cout.rdbuf(cout_rdbuf);\n    std::cerr.rdbuf(cerr_rdbuf);\n    Debug::CurrentDebugLevel = Debug::NoLevel;\n\n    return ret;\n}\n"
  },
  {
    "path": "components/debug/debugging.hpp",
    "content": "#ifndef DEBUG_DEBUGGING_H\n#define DEBUG_DEBUGGING_H\n\n#include <boost/filesystem/fstream.hpp>\n#include <boost/iostreams/stream.hpp>\n\n#include <components/files/configurationmanager.hpp>\n\n#include <SDL_messagebox.h>\n\n#include \"debuglog.hpp\"\n\n#if defined _WIN32 && defined _DEBUG\n#   undef WIN32_LEAN_AND_MEAN\n#   define WIN32_LEAN_AND_MEAN\n#   include <windows.h>\n#endif\n\nnamespace Debug\n{\n    // ANSI colors for terminal\n    enum Color\n    {\n        Reset = 0,\n        DarkGray = 90,\n        Red = 91,\n        Yellow = 93\n    };\n\n    class DebugOutputBase : public boost::iostreams::sink\n    {\n    public:\n        DebugOutputBase()\n        {\n            if (CurrentDebugLevel == NoLevel)\n                fillCurrentDebugLevel();\n        }\n\n        virtual std::streamsize write(const char *str, std::streamsize size);\n\n        virtual ~DebugOutputBase() = default;\n\n    protected:\n        static Level getLevelMarker(const char *str);\n\n        static void fillCurrentDebugLevel();\n\n        virtual std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel)\n        {\n            return size;\n        }\n    };\n\n#ifdef _WIN32\n    bool attachParentConsole();\n#endif\n\n#if defined _WIN32 && defined _DEBUG\n    class DebugOutput : public DebugOutputBase\n    {\n    public:\n        std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel)\n        {\n            // Make a copy for null termination\n            std::string tmp (str, static_cast<unsigned int>(size));\n            // Write string to Visual Studio Debug output\n            OutputDebugString (tmp.c_str ());\n            return size;\n        }\n\n        virtual ~DebugOutput() {}\n    };\n#else\n\n    class Tee : public DebugOutputBase\n    {\n    public:\n        Tee(std::ostream &stream, std::ostream &stream2)\n            : out(stream), out2(stream2)\n        {\n            // TODO: check which stream is stderr?\n            mUseColor = useColoredOutput();\n\n            mColors[Error] = Red;\n            mColors[Warning] = Yellow;\n            mColors[Info] = Reset;\n            mColors[Verbose] = DarkGray;\n            mColors[Debug] = DarkGray;\n            mColors[NoLevel] = Reset;\n        }\n\n        std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) override\n        {\n            out.write (str, size);\n            out.flush();\n\n            if(mUseColor)\n            {\n                out2 << \"\\033[0;\" << mColors[debugLevel] << \"m\";\n                out2.write (str, size);\n                out2 << \"\\033[0;\" << Reset << \"m\";\n            }\n            else\n            {\n                out2.write(str, size);\n            }\n            out2.flush();\n\n            return size;\n        }\n\n        virtual ~Tee() {}\n\n    private:\n\n        static bool useColoredOutput()\n        {\n    // Note: cmd.exe in Win10 should support ANSI colors, but in its own way.\n#if defined(_WIN32)\n            return 0;\n#else\n            char *term = getenv(\"TERM\");\n            bool useColor = term && !getenv(\"NO_COLOR\") && isatty(fileno(stderr));\n\n            return useColor;\n#endif\n        }\n\n        std::ostream &out;\n        std::ostream &out2;\n        bool mUseColor;\n\n        std::map<Level, int> mColors;\n    };\n#endif\n}\n\n// Can be used to print messages without timestamps\nstd::ostream& getRawStdout();\n\nint wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& appName);\n\n#endif\n"
  },
  {
    "path": "components/debug/debuglog.cpp",
    "content": "#include \"debuglog.hpp\"\n\nnamespace Debug\n{\n    Level CurrentDebugLevel = Level::NoLevel;\n}\n\nstd::mutex Log::sLock;\n"
  },
  {
    "path": "components/debug/debuglog.hpp",
    "content": "#ifndef DEBUG_LOG_H\n#define DEBUG_LOG_H\n\n#include <mutex>\n#include <iostream>\n\n#include <osg/io_utils>\n\nnamespace Debug\n{\n    enum Level\n    {\n        Error = 1,\n        Warning = 2,\n        Info = 3,\n        Verbose = 4,\n        Debug = 5,\n        Marker = Debug,\n\n        NoLevel = 6 // Do not filter messages in this case\n    };\n\n    extern Level CurrentDebugLevel;\n}\n\nclass Log\n{\n    static std::mutex sLock;\n\n    std::unique_lock<std::mutex> mLock;\npublic:\n    // Locks a global lock while the object is alive\n    Log(Debug::Level level) :\n    mLock(sLock),\n    mLevel(level)\n    {\n        // If the app has no logging system enabled, log level is not specified.\n        // Show all messages without marker - we just use the plain cout in this case.\n        if (Debug::CurrentDebugLevel == Debug::NoLevel)\n            return;\n\n        if (mLevel <= Debug::CurrentDebugLevel)\n            std::cout << static_cast<unsigned char>(mLevel);\n    }\n\n    // Perfect forwarding wrappers to give the chain of objects to cout\n    template<typename T>\n    Log& operator<<(T&& rhs)\n    {\n        if (mLevel <= Debug::CurrentDebugLevel)\n            std::cout << std::forward<T>(rhs);\n\n        return *this;\n    }\n\n    ~Log()\n    {\n        if (mLevel <= Debug::CurrentDebugLevel)\n            std::cout << std::endl;\n    }\n\nprivate:\n    Debug::Level mLevel;\n};\n\n#endif\n"
  },
  {
    "path": "components/debug/gldebug.cpp",
    "content": "// This file is based heavily on code from https://github.com/ThermalPixel/osgdemos/blob/master/osgdebug/EnableGLDebugOperation.cpp\n// The original licence is included below:\n/*\nCopyright (c) 2014, Andreas Klein\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation\nand/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nThe views and conclusions contained in the software and documentation are those\nof the authors and should not be interpreted as representing official policies,\neither expressed or implied, of the FreeBSD Project.\n*/\n\n#include \"gldebug.hpp\"\n\n#include <cstdlib>\n#include <memory>\n\n#include <components/debug/debuglog.hpp>\n\n// OpenGL constants not provided by OSG:\n#include <SDL_opengl_glext.h>\n\nnamespace Debug\n{\n\n    void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)\n    {\n#ifdef GL_DEBUG_OUTPUT\n        std::string srcStr;\n        switch (source)\n        {\n        case GL_DEBUG_SOURCE_API:\n            srcStr = \"API\";\n            break;\n        case GL_DEBUG_SOURCE_WINDOW_SYSTEM:\n            srcStr = \"WINDOW_SYSTEM\";\n            break;\n        case GL_DEBUG_SOURCE_SHADER_COMPILER:\n            srcStr = \"SHADER_COMPILER\";\n            break;\n        case GL_DEBUG_SOURCE_THIRD_PARTY:\n            srcStr = \"THIRD_PARTY\";\n            break;\n        case GL_DEBUG_SOURCE_APPLICATION:\n            srcStr = \"APPLICATION\";\n            break;\n        case GL_DEBUG_SOURCE_OTHER:\n            srcStr = \"OTHER\";\n            break;\n        default:\n            srcStr = \"UNDEFINED\";\n            break;\n        }\n\n        std::string typeStr;\n\n        Level logSeverity = Warning;\n        switch (type)\n        {\n        case GL_DEBUG_TYPE_ERROR:\n            typeStr = \"ERROR\";\n            logSeverity = Error;\n            break;\n        case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:\n            typeStr = \"DEPRECATED_BEHAVIOR\";\n            break;\n        case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:\n            typeStr = \"UNDEFINED_BEHAVIOR\";\n            break;\n        case GL_DEBUG_TYPE_PORTABILITY:\n            typeStr = \"PORTABILITY\";\n            break;\n        case GL_DEBUG_TYPE_PERFORMANCE:\n            typeStr = \"PERFORMANCE\";\n            break;\n        case GL_DEBUG_TYPE_OTHER:\n            typeStr = \"OTHER\";\n            break;\n        default:\n            typeStr = \"UNDEFINED\";\n            break;\n        }\n\n        Log(logSeverity) << \"OpenGL \" << typeStr << \" [\" << srcStr << \"]: \" << message;\n#endif\n    }\n\n    class PushDebugGroup\n    {\n    public:\n        static std::unique_ptr<PushDebugGroup> sInstance;\n\n        void (GL_APIENTRY * glPushDebugGroup) (GLenum source, GLuint id, GLsizei length, const GLchar * message);\n\n        void (GL_APIENTRY * glPopDebugGroup) (void);\n\n        bool valid()\n        {\n            return glPushDebugGroup && glPopDebugGroup;\n        }\n    };\n\n    std::unique_ptr<PushDebugGroup> PushDebugGroup::sInstance{ std::make_unique<PushDebugGroup>() };\n\n    EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation(\"EnableGLDebugOperation\", false)\n    {\n    }\n\n    void EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext)\n    {\n#ifdef GL_DEBUG_OUTPUT\n        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);\n\n        unsigned int contextID = graphicsContext->getState()->getContextID();\n\n        typedef void (GL_APIENTRY *DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam);\n        typedef void (GL_APIENTRY *GLDebugMessageControlFunction)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);\n        typedef void (GL_APIENTRY *GLDebugMessageCallbackFunction)(DEBUGPROC, const void* userParam);\n\n        GLDebugMessageControlFunction glDebugMessageControl = nullptr;\n        GLDebugMessageCallbackFunction glDebugMessageCallback = nullptr;\n\n        if (osg::isGLExtensionSupported(contextID, \"GL_KHR_debug\"))\n        {\n            osg::setGLExtensionFuncPtr(glDebugMessageCallback, \"glDebugMessageCallback\");\n            osg::setGLExtensionFuncPtr(glDebugMessageControl, \"glDebugMessageControl\");\n            osg::setGLExtensionFuncPtr(PushDebugGroup::sInstance->glPushDebugGroup, \"glPushDebugGroup\");\n            osg::setGLExtensionFuncPtr(PushDebugGroup::sInstance->glPopDebugGroup, \"glPopDebugGroup\");\n        }\n        else if (osg::isGLExtensionSupported(contextID, \"GL_ARB_debug_output\"))\n        {\n            osg::setGLExtensionFuncPtr(glDebugMessageCallback, \"glDebugMessageCallbackARB\");\n            osg::setGLExtensionFuncPtr(glDebugMessageControl, \"glDebugMessageControlARB\");\n        }\n        else if (osg::isGLExtensionSupported(contextID, \"GL_AMD_debug_output\"))\n        {\n            osg::setGLExtensionFuncPtr(glDebugMessageCallback, \"glDebugMessageCallbackAMD\");\n            osg::setGLExtensionFuncPtr(glDebugMessageControl, \"glDebugMessageControlAMD\");\n        }\n\n        if (glDebugMessageCallback && glDebugMessageControl)\n        {\n            glEnable(GL_DEBUG_OUTPUT);\n            glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, true);\n            glDebugMessageCallback(debugCallback, nullptr);\n\n            Log(Info) << \"OpenGL debug callback attached.\";\n        }\n        else\n#endif\n            Log(Error) << \"Unable to attach OpenGL debug callback.\";\n    }\n\n    bool shouldDebugOpenGL()\n    {\n        const char* env = std::getenv(\"OPENMW_DEBUG_OPENGL\");\n        if (!env)\n            return false;\n        std::string str(env);\n        if (str.length() == 0)\n            return true;\n\n        return str.find(\"OFF\") == std::string::npos && str.find('0') == std::string::npos && str.find(\"NO\") == std::string::npos;\n    }\n\n    DebugGroup::DebugGroup(const std::string & message, GLuint id)\n    #ifdef GL_DEBUG_OUTPUT\n        : mSource(GL_DEBUG_SOURCE_APPLICATION)\n    #else\n        : mSource(0x824A)\n    #endif\n        , mId(id)\n        , mMessage(message)\n    {\n    }\n\n    DebugGroup::DebugGroup(const DebugGroup & debugGroup, const osg::CopyOp & copyop)\n        : osg::StateAttribute(debugGroup, copyop)\n        , mSource(debugGroup.mSource)\n        , mId(debugGroup.mId)\n        , mMessage(debugGroup.mMessage)\n    {\n    }\n\n    void DebugGroup::apply(osg::State & state) const\n    {\n        if (!PushDebugGroup::sInstance->valid())\n        {\n            Log(Error) << \"OpenGL debug groups not supported on this system, or OPENMW_DEBUG_OPENGL environment variable not set.\";\n            return;\n        }\n\n        auto& attributeVec = state.getAttributeVec(this);\n        auto& lastAppliedStack = sLastAppliedStack[state.getContextID()];\n\n        size_t firstNonMatch = 0;\n        while (firstNonMatch < lastAppliedStack.size()\n               && ((firstNonMatch < attributeVec.size() && lastAppliedStack[firstNonMatch] == attributeVec[firstNonMatch].first)\n                   || lastAppliedStack[firstNonMatch] == this))\n            firstNonMatch++;\n\n        for (size_t i = lastAppliedStack.size(); i > firstNonMatch; --i)\n            lastAppliedStack[i - 1]->pop(state);\n        lastAppliedStack.resize(firstNonMatch);\n\n        lastAppliedStack.reserve(attributeVec.size());\n        for (size_t i = firstNonMatch; i < attributeVec.size(); ++i)\n        {\n            const DebugGroup* group = static_cast<const DebugGroup*>(attributeVec[i].first);\n            group->push(state);\n            lastAppliedStack.push_back(group);\n        }\n        if (!(lastAppliedStack.back() == this))\n        {\n            push(state);\n            lastAppliedStack.push_back(this);\n        }\n    }\n\n    int DebugGroup::compare(const StateAttribute & sa) const\n    {\n        COMPARE_StateAttribute_Types(DebugGroup, sa);\n\n        COMPARE_StateAttribute_Parameter(mSource);\n        COMPARE_StateAttribute_Parameter(mId);\n        COMPARE_StateAttribute_Parameter(mMessage);\n\n        return 0;\n    }\n\n    void DebugGroup::releaseGLObjects(osg::State * state) const\n    {\n        if (state)\n            sLastAppliedStack.erase(state->getContextID());\n        else\n            sLastAppliedStack.clear();\n    }\n\n    bool DebugGroup::isValid() const\n    {\n        return mSource || mId || mMessage.length();\n    }\n\n    void DebugGroup::push(osg::State & state) const\n    {\n        if (isValid())\n            PushDebugGroup::sInstance->glPushDebugGroup(mSource, mId, mMessage.size(), mMessage.c_str());\n    }\n\n    void DebugGroup::pop(osg::State & state) const\n    {\n        if (isValid())\n            PushDebugGroup::sInstance->glPopDebugGroup();\n    }\n\n    std::map<unsigned int, std::vector<const DebugGroup *>> DebugGroup::sLastAppliedStack{};\n\n}\n"
  },
  {
    "path": "components/debug/gldebug.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DEBUG_GLDEBUG_H\n#define OPENMW_COMPONENTS_DEBUG_GLDEBUG_H\n\n#include <osgViewer/ViewerEventHandlers>\n\nnamespace Debug\n{\n    class EnableGLDebugOperation : public osg::GraphicsOperation\n    {\n    public:\n        EnableGLDebugOperation();\n\n        void operator()(osg::GraphicsContext* graphicsContext) override;\n\n    private:\n        OpenThreads::Mutex mMutex;\n    };\n\n    bool shouldDebugOpenGL();\n\n\n    /*\n        Debug groups allow rendering to be annotated, making debugging via APITrace/CodeXL/NSight etc. much clearer.\n\n        Because I've not thought of a quick and clean way of doing it without incurring a small performance cost,\n        there are no uses of this class checked in. For now, add annotations locally when you need them.\n\n        To use this class, add it to a StateSet just like any other StateAttribute. Prefer the string-only constructor.\n        You'll need OPENMW_DEBUG_OPENGL set to true, or shouldDebugOpenGL() redefined to just return true as otherwise\n        the extension function pointers won't get set up. That can maybe be cleaned up in the future.\n\n        Beware that consecutive identical debug groups (i.e. pointers match) won't always get applied due to OSG thinking\n        it's already applied them. Either avoid nesting the same object, add dummy groups so they're not consecutive, or\n        ensure the leaf group isn't identical to its parent.\n    */\n    class DebugGroup : public osg::StateAttribute\n    {\n    public:\n        DebugGroup()\n            : mSource(0)\n            , mId(0)\n            , mMessage(\"\")\n        {}\n\n        DebugGroup(GLenum source, GLuint id, const std::string& message)\n            : mSource(source)\n            , mId(id)\n            , mMessage(message)\n        {}\n\n        DebugGroup(const std::string& message, GLuint id = 0);\n\n        DebugGroup(const DebugGroup& debugGroup, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);\n\n        META_StateAttribute(Debug, DebugGroup, osg::StateAttribute::Type(101));\n\n        void apply(osg::State& state) const override;\n\n        int compare(const StateAttribute& sa) const override;\n\n        void releaseGLObjects(osg::State* state = nullptr) const override;\n\n        virtual bool isValid() const;\n\n    protected:\n        virtual ~DebugGroup() = default;\n\n        virtual void push(osg::State& state) const;\n\n        virtual void pop(osg::State& state) const;\n\n        GLenum mSource;\n        GLuint mId;\n        std::string mMessage;\n\n        static std::map<unsigned int, std::vector<const DebugGroup *>> sLastAppliedStack;\n\n        friend EnableGLDebugOperation;\n    };\n}\n#endif\n"
  },
  {
    "path": "components/detournavigator/areatype.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_AREATYPE_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_AREATYPE_H\n\n#include <Recast.h>\n\nnamespace DetourNavigator\n{\n    enum AreaType : unsigned char\n    {\n        AreaType_null = RC_NULL_AREA,\n        AreaType_water,\n        AreaType_door,\n        AreaType_pathgrid,\n        AreaType_ground = RC_WALKABLE_AREA,\n    };\n\n    struct AreaCosts\n    {\n        float mWater = 1.0f;\n        float mDoor = 2.0f;\n        float mPathgrid = 1.0f;\n        float mGround = 1.0f;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/asyncnavmeshupdater.cpp",
    "content": "#include \"asyncnavmeshupdater.hpp\"\n#include \"debug.hpp\"\n#include \"makenavmesh.hpp\"\n#include \"settings.hpp\"\n#include \"version.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/thread.hpp>\n#include <components/loadinglistener/loadinglistener.hpp>\n\n#include <osg/Stats>\n\n#include <algorithm>\n#include <numeric>\n#include <set>\n\nnamespace\n{\n    using DetourNavigator::ChangeType;\n    using DetourNavigator::TilePosition;\n\n    int getManhattanDistance(const TilePosition& lhs, const TilePosition& rhs)\n    {\n        return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y());\n    }\n\n    int getMinDistanceTo(const TilePosition& position, int maxDistance,\n                         const std::map<osg::Vec3f, std::set<TilePosition>>& tilesPerHalfExtents,\n                         const std::set<std::tuple<osg::Vec3f, TilePosition>>& presentTiles)\n    {\n        int result = maxDistance;\n        for (const auto& [halfExtents, tiles] : tilesPerHalfExtents)\n            for (const TilePosition& tile : tiles)\n                if (presentTiles.find(std::make_tuple(halfExtents, tile)) == presentTiles.end())\n                    result = std::min(result, getManhattanDistance(position, tile));\n        return result;\n    }\n}\n\nnamespace DetourNavigator\n{\n    static std::ostream& operator <<(std::ostream& stream, UpdateNavMeshStatus value)\n    {\n        switch (value)\n        {\n            case UpdateNavMeshStatus::ignored:\n                return stream << \"ignore\";\n            case UpdateNavMeshStatus::removed:\n                return stream << \"removed\";\n            case UpdateNavMeshStatus::added:\n                return stream << \"add\";\n            case UpdateNavMeshStatus::replaced:\n                return stream << \"replaced\";\n            case UpdateNavMeshStatus::failed:\n                return stream << \"failed\";\n            case UpdateNavMeshStatus::lost:\n                return stream << \"lost\";\n            case UpdateNavMeshStatus::cached:\n                return stream << \"cached\";\n            case UpdateNavMeshStatus::unchanged:\n                return stream << \"unchanged\";\n            case UpdateNavMeshStatus::restored:\n                return stream << \"restored\";\n        }\n        return stream << \"unknown(\" << static_cast<unsigned>(value) << \")\";\n    }\n\n    AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,\n            OffMeshConnectionsManager& offMeshConnectionsManager)\n        : mSettings(settings)\n        , mRecastMeshManager(recastMeshManager)\n        , mOffMeshConnectionsManager(offMeshConnectionsManager)\n        , mShouldStop()\n        , mNavMeshTilesCache(settings.mMaxNavMeshTilesCacheSize)\n    {\n        for (std::size_t i = 0; i < mSettings.get().mAsyncNavMeshUpdaterThreads; ++i)\n            mThreads.emplace_back([&] { process(); });\n    }\n\n    AsyncNavMeshUpdater::~AsyncNavMeshUpdater()\n    {\n        mShouldStop = true;\n        std::unique_lock<std::mutex> lock(mMutex);\n        mJobs = decltype(mJobs)();\n        mHasJob.notify_all();\n        lock.unlock();\n        for (auto& thread : mThreads)\n            thread.join();\n    }\n\n    void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents,\n        const SharedNavMeshCacheItem& navMeshCacheItem, const TilePosition& playerTile,\n        const std::map<TilePosition, ChangeType>& changedTiles)\n    {\n        bool playerTileChanged = false;\n        {\n            auto locked = mPlayerTile.lock();\n            playerTileChanged = *locked != playerTile;\n            *locked = playerTile;\n        }\n\n        if (!playerTileChanged && changedTiles.empty())\n            return;\n\n        const std::lock_guard<std::mutex> lock(mMutex);\n\n        if (playerTileChanged)\n            for (auto& job : mJobs)\n                job.mDistanceToPlayer = getManhattanDistance(job.mChangedTile, playerTile);\n\n        for (const auto& changedTile : changedTiles)\n        {\n            if (mPushed[agentHalfExtents].insert(changedTile.first).second)\n            {\n                Job job;\n\n                job.mAgentHalfExtents = agentHalfExtents;\n                job.mNavMeshCacheItem = navMeshCacheItem;\n                job.mChangedTile = changedTile.first;\n                job.mTryNumber = 0;\n                job.mChangeType = changedTile.second;\n                job.mDistanceToPlayer = getManhattanDistance(changedTile.first, playerTile);\n                job.mDistanceToOrigin = getManhattanDistance(changedTile.first, TilePosition {0, 0});\n                job.mProcessTime = job.mChangeType == ChangeType::update\n                    ? mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] + mSettings.get().mMinUpdateInterval\n                    : std::chrono::steady_clock::time_point();\n\n                if (playerTileChanged)\n                {\n                    mJobs.push_back(std::move(job));\n                }\n                else\n                {\n                    const auto it = std::upper_bound(mJobs.begin(), mJobs.end(), job);\n                    mJobs.insert(it, std::move(job));\n                }\n            }\n        }\n\n        if (playerTileChanged)\n            std::sort(mJobs.begin(), mJobs.end());\n\n        Log(Debug::Debug) << \"Posted \" << mJobs.size() << \" navigator jobs\";\n\n        if (!mJobs.empty())\n            mHasJob.notify_all();\n    }\n\n    void AsyncNavMeshUpdater::wait(Loading::Listener& listener, WaitConditionType waitConditionType)\n    {\n        if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0)\n            return;\n        listener.setLabel(\"Building navigation mesh\");\n        const std::size_t initialJobsLeft = getTotalJobs();\n        std::size_t maxProgress = initialJobsLeft + mThreads.size();\n        listener.setProgressRange(maxProgress);\n        switch (waitConditionType)\n        {\n            case WaitConditionType::requiredTilesPresent:\n            {\n                const int minDistanceToPlayer = waitUntilJobsDoneForNotPresentTiles(initialJobsLeft, maxProgress, listener);\n                if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer)\n                {\n                    mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });\n                    listener.setProgress(maxProgress);\n                }\n                break;\n            }\n            case WaitConditionType::allJobsDone:\n                waitUntilAllJobsDone();\n                listener.setProgress(maxProgress);\n                break;\n        }\n    }\n\n    int AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener)\n    {\n        std::size_t prevJobsLeft = initialJobsLeft;\n        std::size_t jobsDone = 0;\n        std::size_t jobsLeft = 0;\n        const int maxDistanceToPlayer = mSettings.get().mWaitUntilMinDistanceToPlayer;\n        const TilePosition playerPosition = *mPlayerTile.lockConst();\n        int minDistanceToPlayer = 0;\n        const auto isDone = [&]\n        {\n            jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe();\n            if (jobsLeft == 0)\n            {\n                minDistanceToPlayer = 0;\n                return true;\n            }\n            minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles);\n            for (const auto& [threadId, queue] : mThreadsQueues)\n                minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed, mPresentTiles);\n            return minDistanceToPlayer >= maxDistanceToPlayer;\n        };\n        std::unique_lock<std::mutex> lock(mMutex);\n        while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone))\n        {\n            if (maxProgress < jobsLeft)\n            {\n                maxProgress = jobsLeft + mThreads.size();\n                listener.setProgressRange(maxProgress);\n                listener.setProgress(jobsDone);\n            }\n            else if (jobsLeft < prevJobsLeft)\n            {\n                const std::size_t newJobsDone = prevJobsLeft - jobsLeft;\n                jobsDone += newJobsDone;\n                prevJobsLeft = jobsLeft;\n                listener.increaseProgress(newJobsDone);\n            }\n        }\n        return minDistanceToPlayer;\n    }\n\n    void AsyncNavMeshUpdater::waitUntilAllJobsDone()\n    {\n        {\n            std::unique_lock<std::mutex> lock(mMutex);\n            mDone.wait(lock, [this] { return mJobs.size() + getTotalThreadJobsUnsafe() == 0; });\n        }\n        mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });\n    }\n\n    void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const\n    {\n        std::size_t jobs = 0;\n\n        {\n            const std::lock_guard<std::mutex> lock(mMutex);\n            jobs = mJobs.size() + getTotalThreadJobsUnsafe();\n        }\n\n        stats.setAttribute(frameNumber, \"NavMesh UpdateJobs\", jobs);\n\n        mNavMeshTilesCache.reportStats(frameNumber, stats);\n    }\n\n    void AsyncNavMeshUpdater::process() noexcept\n    {\n        Log(Debug::Debug) << \"Start process navigator jobs by thread=\" << std::this_thread::get_id();\n        Misc::setCurrentThreadIdlePriority();\n        while (!mShouldStop)\n        {\n            try\n            {\n                if (auto job = getNextJob())\n                {\n                    const auto processed = processJob(*job);\n                    unlockTile(job->mAgentHalfExtents, job->mChangedTile);\n                    if (!processed)\n                        repost(std::move(*job));\n                }\n                else\n                    cleanupLastUpdates();\n            }\n            catch (const std::exception& e)\n            {\n                Log(Debug::Error) << \"AsyncNavMeshUpdater::process exception: \" << e.what();\n            }\n        }\n        Log(Debug::Debug) << \"Stop navigator jobs processing by thread=\" << std::this_thread::get_id();\n    }\n\n    bool AsyncNavMeshUpdater::processJob(const Job& job)\n    {\n        Log(Debug::Debug) << \"Process job for agent=(\" << std::fixed << std::setprecision(2) << job.mAgentHalfExtents << \")\"\n            \" by thread=\" << std::this_thread::get_id();\n\n        const auto start = std::chrono::steady_clock::now();\n\n        const auto firstStart = setFirstStart(start);\n\n        const auto navMeshCacheItem = job.mNavMeshCacheItem.lock();\n\n        if (!navMeshCacheItem)\n            return true;\n\n        const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile);\n        const auto playerTile = *mPlayerTile.lockConst();\n        const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);\n\n        const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile,\n            offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache);\n\n        if (recastMesh != nullptr)\n        {\n            Version navMeshVersion;\n            {\n                const auto locked = navMeshCacheItem->lockConst();\n                navMeshVersion.mGeneration = locked->getGeneration();\n                navMeshVersion.mRevision = locked->getNavMeshRevision();\n            }\n            mRecastMeshManager.get().reportNavMeshChange(job.mChangedTile,\n                Version {recastMesh->getGeneration(), recastMesh->getRevision()},\n                navMeshVersion);\n        }\n\n        if (status == UpdateNavMeshStatus::removed || status == UpdateNavMeshStatus::lost)\n        {\n            const std::scoped_lock lock(mMutex);\n            mPresentTiles.erase(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile));\n        }\n        else if (isSuccess(status) && status != UpdateNavMeshStatus::ignored)\n        {\n            const std::scoped_lock lock(mMutex);\n            mPresentTiles.insert(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile));\n        }\n\n        const auto finish = std::chrono::steady_clock::now();\n\n        writeDebugFiles(job, recastMesh.get());\n\n        using FloatMs = std::chrono::duration<float, std::milli>;\n\n        const auto locked = navMeshCacheItem->lockConst();\n        Log(Debug::Debug) << std::fixed << std::setprecision(2) <<\n            \"Cache updated for agent=(\" << job.mAgentHalfExtents << \")\" <<\n            \" tile=\" << job.mChangedTile <<\n            \" status=\" << status <<\n            \" generation=\" << locked->getGeneration() <<\n            \" revision=\" << locked->getNavMeshRevision() <<\n            \" time=\" << std::chrono::duration_cast<FloatMs>(finish - start).count() << \"ms\" <<\n            \" total_time=\" << std::chrono::duration_cast<FloatMs>(finish - firstStart).count() << \"ms\"\n            \" thread=\" << std::this_thread::get_id();\n\n        return isSuccess(status);\n    }\n\n    std::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getNextJob()\n    {\n        std::unique_lock<std::mutex> lock(mMutex);\n\n        const auto threadId = std::this_thread::get_id();\n        auto& threadQueue = mThreadsQueues[threadId];\n\n        while (true)\n        {\n            const auto hasJob = [&] {\n                return (!mJobs.empty() && mJobs.front().mProcessTime <= std::chrono::steady_clock::now())\n                    || !threadQueue.mJobs.empty();\n            };\n\n            if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob))\n            {\n                mFirstStart.lock()->reset();\n                if (mJobs.empty() && getTotalThreadJobsUnsafe() == 0)\n                    mDone.notify_all();\n                return std::nullopt;\n            }\n\n            Log(Debug::Debug) << \"Got \" << mJobs.size() << \" navigator jobs and \"\n                << threadQueue.mJobs.size() << \" thread jobs by thread=\" << std::this_thread::get_id();\n\n            auto job = threadQueue.mJobs.empty()\n                ? getJob(mJobs, mPushed, true)\n                : getJob(threadQueue.mJobs, threadQueue.mPushed, false);\n\n            if (!job)\n                continue;\n\n            const auto owner = lockTile(job->mAgentHalfExtents, job->mChangedTile);\n\n            if (owner == threadId)\n                return job;\n\n            postThreadJob(std::move(*job), mThreadsQueues[owner]);\n        }\n    }\n\n    std::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate)\n    {\n        const auto now = std::chrono::steady_clock::now();\n\n        if (jobs.front().mProcessTime > now)\n            return {};\n\n        Job job = jobs.front();\n        jobs.pop_front();\n\n        if (changeLastUpdate && job.mChangeType == ChangeType::update)\n            mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] = now;\n\n        const auto it = pushed.find(job.mAgentHalfExtents);\n        it->second.erase(job.mChangedTile);\n        if (it->second.empty())\n            pushed.erase(it);\n\n        return job;\n    }\n\n    void AsyncNavMeshUpdater::writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const\n    {\n        std::string revision;\n        std::string recastMeshRevision;\n        std::string navMeshRevision;\n        if ((mSettings.get().mEnableWriteNavMeshToFile || mSettings.get().mEnableWriteRecastMeshToFile)\n                && (mSettings.get().mEnableRecastMeshFileNameRevision || mSettings.get().mEnableNavMeshFileNameRevision))\n        {\n            revision = \".\" + std::to_string((std::chrono::steady_clock::now()\n                - std::chrono::steady_clock::time_point()).count());\n            if (mSettings.get().mEnableRecastMeshFileNameRevision)\n                recastMeshRevision = revision;\n            if (mSettings.get().mEnableNavMeshFileNameRevision)\n                navMeshRevision = revision;\n        }\n        if (recastMesh && mSettings.get().mEnableWriteRecastMeshToFile)\n            writeToFile(*recastMesh, mSettings.get().mRecastMeshPathPrefix + std::to_string(job.mChangedTile.x())\n                        + \"_\" + std::to_string(job.mChangedTile.y()) + \"_\", recastMeshRevision);\n        if (mSettings.get().mEnableWriteNavMeshToFile)\n            if (const auto shared = job.mNavMeshCacheItem.lock())\n                writeToFile(shared->lockConst()->getImpl(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);\n    }\n\n    std::chrono::steady_clock::time_point AsyncNavMeshUpdater::setFirstStart(const std::chrono::steady_clock::time_point& value)\n    {\n        const auto locked = mFirstStart.lock();\n        if (!*locked)\n            *locked = value;\n        return *locked.get();\n    }\n\n    void AsyncNavMeshUpdater::repost(Job&& job)\n    {\n        if (mShouldStop || job.mTryNumber > 2)\n            return;\n\n        const std::lock_guard<std::mutex> lock(mMutex);\n\n        if (mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second)\n        {\n            ++job.mTryNumber;\n            mJobs.push_back(std::move(job));\n            mHasJob.notify_all();\n        }\n    }\n\n    void AsyncNavMeshUpdater::postThreadJob(Job&& job, Queue& queue)\n    {\n        if (queue.mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second)\n        {\n            queue.mJobs.push_back(std::move(job));\n            mHasJob.notify_all();\n        }\n    }\n\n    std::thread::id AsyncNavMeshUpdater::lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile)\n    {\n        if (mSettings.get().mAsyncNavMeshUpdaterThreads <= 1)\n            return std::this_thread::get_id();\n\n        auto locked = mProcessingTiles.lock();\n\n        auto agent = locked->find(agentHalfExtents);\n        if (agent == locked->end())\n        {\n            const auto threadId = std::this_thread::get_id();\n            locked->emplace(agentHalfExtents, std::map<TilePosition, std::thread::id>({{changedTile, threadId}}));\n            return threadId;\n        }\n\n        auto tile = agent->second.find(changedTile);\n        if (tile == agent->second.end())\n        {\n            const auto threadId = std::this_thread::get_id();\n            agent->second.emplace(changedTile, threadId);\n            return threadId;\n        }\n\n        return tile->second;\n    }\n\n    void AsyncNavMeshUpdater::unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile)\n    {\n        if (mSettings.get().mAsyncNavMeshUpdaterThreads <= 1)\n            return;\n\n        auto locked = mProcessingTiles.lock();\n\n        auto agent = locked->find(agentHalfExtents);\n        if (agent == locked->end())\n            return;\n\n        auto tile = agent->second.find(changedTile);\n        if (tile == agent->second.end())\n            return;\n\n        agent->second.erase(tile);\n\n        if (agent->second.empty())\n            locked->erase(agent);\n\n        if (locked->empty())\n            mProcessed.notify_all();\n    }\n\n    std::size_t AsyncNavMeshUpdater::getTotalJobs() const\n    {\n        const std::scoped_lock lock(mMutex);\n        return mJobs.size() + getTotalThreadJobsUnsafe();\n    }\n\n    std::size_t AsyncNavMeshUpdater::getTotalThreadJobsUnsafe() const\n    {\n        return std::accumulate(mThreadsQueues.begin(), mThreadsQueues.end(), std::size_t(0),\n            [] (auto r, const auto& v) { return r + v.second.mJobs.size(); });\n    }\n\n    void AsyncNavMeshUpdater::cleanupLastUpdates()\n    {\n        const auto now = std::chrono::steady_clock::now();\n\n        const std::lock_guard<std::mutex> lock(mMutex);\n\n        for (auto agent = mLastUpdates.begin(); agent != mLastUpdates.end();)\n        {\n            for (auto tile = agent->second.begin(); tile != agent->second.end();)\n            {\n                if (now - tile->second > mSettings.get().mMinUpdateInterval)\n                    tile = agent->second.erase(tile);\n                else\n                    ++tile;\n            }\n\n            if (agent->second.empty())\n                agent = mLastUpdates.erase(agent);\n            else\n                ++agent;\n        }\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/asyncnavmeshupdater.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H\n\n#include \"navmeshcacheitem.hpp\"\n#include \"offmeshconnectionsmanager.hpp\"\n#include \"tilecachedrecastmeshmanager.hpp\"\n#include \"tileposition.hpp\"\n#include \"navmeshtilescache.hpp\"\n#include \"waitconditiontype.hpp\"\n\n#include <osg/Vec3f>\n\n#include <atomic>\n#include <chrono>\n#include <condition_variable>\n#include <memory>\n#include <mutex>\n#include <deque>\n#include <set>\n#include <thread>\n#include <tuple>\n\nclass dtNavMesh;\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace DetourNavigator\n{\n    enum class ChangeType\n    {\n        remove = 0,\n        mixed = 1,\n        add = 2,\n        update = 3,\n    };\n\n    inline std::ostream& operator <<(std::ostream& stream, ChangeType value)\n    {\n        switch (value) {\n            case ChangeType::remove:\n                return stream << \"ChangeType::remove\";\n            case ChangeType::mixed:\n                return stream << \"ChangeType::mixed\";\n            case ChangeType::add:\n                return stream << \"ChangeType::add\";\n            case ChangeType::update:\n                return stream << \"ChangeType::update\";\n        }\n        return stream << \"ChangeType::\" << static_cast<int>(value);\n    }\n\n    class AsyncNavMeshUpdater\n    {\n    public:\n        AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,\n            OffMeshConnectionsManager& offMeshConnectionsManager);\n        ~AsyncNavMeshUpdater();\n\n        void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem,\n            const TilePosition& playerTile, const std::map<TilePosition, ChangeType>& changedTiles);\n\n        void wait(Loading::Listener& listener, WaitConditionType waitConditionType);\n\n        void reportStats(unsigned int frameNumber, osg::Stats& stats) const;\n\n    private:\n        struct Job\n        {\n            osg::Vec3f mAgentHalfExtents;\n            std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;\n            TilePosition mChangedTile;\n            unsigned mTryNumber;\n            ChangeType mChangeType;\n            int mDistanceToPlayer;\n            int mDistanceToOrigin;\n            std::chrono::steady_clock::time_point mProcessTime;\n\n            std::tuple<std::chrono::steady_clock::time_point, unsigned, ChangeType, int, int> getPriority() const\n            {\n                return std::make_tuple(mProcessTime, mTryNumber, mChangeType, mDistanceToPlayer, mDistanceToOrigin);\n            }\n\n            friend inline bool operator <(const Job& lhs, const Job& rhs)\n            {\n                return lhs.getPriority() < rhs.getPriority();\n            }\n        };\n\n        using Jobs = std::deque<Job>;\n        using Pushed = std::map<osg::Vec3f, std::set<TilePosition>>;\n\n        struct Queue\n        {\n            Jobs mJobs;\n            Pushed mPushed;\n\n            Queue() = default;\n        };\n\n        std::reference_wrapper<const Settings> mSettings;\n        std::reference_wrapper<TileCachedRecastMeshManager> mRecastMeshManager;\n        std::reference_wrapper<OffMeshConnectionsManager> mOffMeshConnectionsManager;\n        std::atomic_bool mShouldStop;\n        mutable std::mutex mMutex;\n        std::condition_variable mHasJob;\n        std::condition_variable mDone;\n        std::condition_variable mProcessed;\n        Jobs mJobs;\n        std::map<osg::Vec3f, std::set<TilePosition>> mPushed;\n        Misc::ScopeGuarded<TilePosition> mPlayerTile;\n        Misc::ScopeGuarded<std::optional<std::chrono::steady_clock::time_point>> mFirstStart;\n        NavMeshTilesCache mNavMeshTilesCache;\n        Misc::ScopeGuarded<std::map<osg::Vec3f, std::map<TilePosition, std::thread::id>>> mProcessingTiles;\n        std::map<osg::Vec3f, std::map<TilePosition, std::chrono::steady_clock::time_point>> mLastUpdates;\n        std::set<std::tuple<osg::Vec3f, TilePosition>> mPresentTiles;\n        std::map<std::thread::id, Queue> mThreadsQueues;\n        std::vector<std::thread> mThreads;\n\n        void process() noexcept;\n\n        bool processJob(const Job& job);\n\n        std::optional<Job> getNextJob();\n\n        std::optional<Job> getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate);\n\n        void postThreadJob(Job&& job, Queue& queue);\n\n        void writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const;\n\n        std::chrono::steady_clock::time_point setFirstStart(const std::chrono::steady_clock::time_point& value);\n\n        void repost(Job&& job);\n\n        std::thread::id lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile);\n\n        void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile);\n\n        inline std::size_t getTotalJobs() const;\n\n        inline std::size_t getTotalThreadJobsUnsafe() const;\n\n        void cleanupLastUpdates();\n\n        int waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener);\n\n        void waitUntilAllJobsDone();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/bounds.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_BOUNDS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_BOUNDS_H\n\n#include <osg/Vec3f>\n\nnamespace DetourNavigator\n{\n    struct Bounds\n    {\n        osg::Vec3f mMin;\n        osg::Vec3f mMax;\n    };\n\n    inline bool isEmpty(const Bounds& value)\n    {\n        return value.mMin == value.mMax;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/cachedrecastmeshmanager.cpp",
    "content": "#include \"cachedrecastmeshmanager.hpp\"\n#include \"debug.hpp\"\n\nnamespace DetourNavigator\n{\n    CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds,\n            std::size_t generation)\n        : mImpl(settings, bounds, generation)\n    {}\n\n    bool CachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,\n                                            const btTransform& transform, const AreaType areaType)\n    {\n        if (!mImpl.addObject(id, shape, transform, areaType))\n            return false;\n        mCached.lock()->reset();\n        return true;\n    }\n\n    bool CachedRecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType)\n    {\n        if (!mImpl.updateObject(id, transform, areaType))\n            return false;\n        mCached.lock()->reset();\n        return true;\n    }\n\n    std::optional<RemovedRecastMeshObject> CachedRecastMeshManager::removeObject(const ObjectId id)\n    {\n        const auto object = mImpl.removeObject(id);\n        if (object)\n            mCached.lock()->reset();\n        return object;\n    }\n\n    bool CachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize,\n        const btTransform& transform)\n    {\n        if (!mImpl.addWater(cellPosition, cellSize, transform))\n            return false;\n        mCached.lock()->reset();\n        return true;\n    }\n\n    std::optional<RecastMeshManager::Water> CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition)\n    {\n        const auto water = mImpl.removeWater(cellPosition);\n        if (water)\n            mCached.lock()->reset();\n        return water;\n    }\n\n    std::shared_ptr<RecastMesh> CachedRecastMeshManager::getMesh()\n    {\n        std::shared_ptr<RecastMesh> cached = *mCached.lock();\n        if (cached != nullptr)\n            return cached;\n        cached = mImpl.getMesh();\n        *mCached.lock() = cached;\n        return cached;\n    }\n\n    bool CachedRecastMeshManager::isEmpty() const\n    {\n        return mImpl.isEmpty();\n    }\n\n    void CachedRecastMeshManager::reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion)\n    {\n        mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion);\n    }\n\n    Version CachedRecastMeshManager::getVersion() const\n    {\n        return mImpl.getVersion();\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/cachedrecastmeshmanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H\n\n#include \"recastmeshmanager.hpp\"\n#include \"version.hpp\"\n\n#include <components/misc/guarded.hpp>\n\nnamespace DetourNavigator\n{\n    class CachedRecastMeshManager\n    {\n    public:\n        CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation);\n\n        bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                       const AreaType areaType);\n\n        bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType);\n\n        bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform);\n\n        std::optional<RecastMeshManager::Water> removeWater(const osg::Vec2i& cellPosition);\n\n        std::optional<RemovedRecastMeshObject> removeObject(const ObjectId id);\n\n        std::shared_ptr<RecastMesh> getMesh();\n\n        bool isEmpty() const;\n\n        void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion);\n\n        Version getVersion() const;\n\n    private:\n        RecastMeshManager mImpl;\n        Misc::ScopeGuarded<std::shared_ptr<RecastMesh>> mCached;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/debug.cpp",
    "content": "#include \"debug.hpp\"\n#include \"exceptions.hpp\"\n#include \"recastmesh.hpp\"\n\n#include <DetourNavMesh.h>\n\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n\nnamespace DetourNavigator\n{\n    void writeToFile(const RecastMesh& recastMesh, const std::string& pathPrefix, const std::string& revision)\n    {\n        const auto path = pathPrefix + \"recastmesh\" + revision + \".obj\";\n        boost::filesystem::ofstream file(boost::filesystem::path(path), std::ios::out);\n        if (!file.is_open())\n            throw NavigatorException(\"Open file failed: \" + path);\n        file.exceptions(std::ios::failbit | std::ios::badbit);\n        file.precision(std::numeric_limits<float>::max_exponent10);\n        std::size_t count = 0;\n        for (auto v : recastMesh.getVertices())\n        {\n            if (count % 3 == 0)\n            {\n                if (count != 0)\n                    file << '\\n';\n                file << 'v';\n            }\n            file << ' ' << v;\n            ++count;\n        }\n        file << '\\n';\n        count = 0;\n        for (auto v : recastMesh.getIndices())\n        {\n            if (count % 3 == 0)\n            {\n                if (count != 0)\n                    file << '\\n';\n                file << 'f';\n            }\n            file << ' ' << (v + 1);\n            ++count;\n        }\n        file << '\\n';\n    }\n\n    void writeToFile(const dtNavMesh& navMesh, const std::string& pathPrefix, const std::string& revision)\n    {\n        const int navMeshSetMagic = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';\n        const int navMeshSetVersion = 1;\n\n        struct NavMeshSetHeader\n        {\n            int magic;\n            int version;\n            int numTiles;\n            dtNavMeshParams params;\n        };\n\n        struct NavMeshTileHeader\n        {\n            dtTileRef tileRef;\n            int dataSize;\n        };\n\n        const auto path = pathPrefix + \"all_tiles_navmesh\" + revision + \".bin\";\n        boost::filesystem::ofstream file(boost::filesystem::path(path), std::ios::out | std::ios::binary);\n        if (!file.is_open())\n            throw NavigatorException(\"Open file failed: \" + path);\n        file.exceptions(std::ios::failbit | std::ios::badbit);\n\n        NavMeshSetHeader header;\n        header.magic = navMeshSetMagic;\n        header.version = navMeshSetVersion;\n        header.numTiles = 0;\n        for (int i = 0; i < navMesh.getMaxTiles(); ++i)\n        {\n            const auto tile = navMesh.getTile(i);\n            if (!tile || !tile->header || !tile->dataSize)\n                continue;\n            header.numTiles++;\n        }\n        header.params = *navMesh.getParams();\n\n        using const_char_ptr = const char*;\n        file.write(const_char_ptr(&header), sizeof(header));\n\n        for (int i = 0; i < navMesh.getMaxTiles(); ++i)\n        {\n            const auto tile = navMesh.getTile(i);\n            if (!tile || !tile->header || !tile->dataSize)\n                continue;\n\n            NavMeshTileHeader tileHeader;\n            tileHeader.tileRef = navMesh.getTileRef(tile);\n            tileHeader.dataSize = tile->dataSize;\n\n            file.write(const_char_ptr(&tileHeader), sizeof(tileHeader));\n            file.write(const_char_ptr(tile->data), tile->dataSize);\n        }\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/debug.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_H\n\n#include \"tilebounds.hpp\"\n#include \"status.hpp\"\n\n#include <osg/io_utils>\n\n#include <components/bullethelpers/operators.hpp>\n#include <components/misc/guarded.hpp>\n\n#include <chrono>\n#include <fstream>\n#include <iomanip>\n#include <iostream>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <thread>\n\nclass dtNavMesh;\n\nnamespace DetourNavigator\n{\n    inline std::ostream& operator <<(std::ostream& stream, const TileBounds& value)\n    {\n        return stream << \"TileBounds {\" << value.mMin << \", \" << value.mMax << \"}\";\n    }\n\n    inline std::ostream& operator <<(std::ostream& stream, Status value)\n    {\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(name) \\\n    case Status::name: return stream << \"DetourNavigator::Status::\"#name;\n        switch (value)\n        {\n            OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(Success)\n            OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(NavMeshNotFound)\n            OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(StartPolygonNotFound)\n            OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(EndPolygonNotFound)\n            OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(MoveAlongSurfaceFailed)\n            OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(FindPathOverPolygonsFailed)\n            OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(GetPolyHeightFailed)\n            OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(InitNavMeshQueryFailed)\n        }\n#undef OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE\n        return stream << \"DetourNavigator::Error::\" << static_cast<int>(value);\n    }\n\n    class RecastMesh;\n\n    void writeToFile(const RecastMesh& recastMesh, const std::string& pathPrefix, const std::string& revision);\n    void writeToFile(const dtNavMesh& navMesh, const std::string& pathPrefix, const std::string& revision);\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/dtstatus.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_DTSTATUS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_DTSTATUS_H\n\n#include <DetourStatus.h>\n\n#include <sstream>\n#include <vector>\n\nnamespace DetourNavigator\n{\n    struct WriteDtStatus\n    {\n        dtStatus status;\n    };\n\n    static const std::vector<std::pair<const dtStatus, const char* const>> dtStatuses {\n        {DT_FAILURE, \"DT_FAILURE\"},\n        {DT_SUCCESS, \"DT_SUCCESS\"},\n        {DT_IN_PROGRESS, \"DT_IN_PROGRESS\"},\n        {DT_WRONG_MAGIC, \"DT_WRONG_MAGIC\"},\n        {DT_WRONG_VERSION, \"DT_WRONG_VERSION\"},\n        {DT_OUT_OF_MEMORY, \"DT_OUT_OF_MEMORY\"},\n        {DT_INVALID_PARAM, \"DT_INVALID_PARAM\"},\n        {DT_BUFFER_TOO_SMALL, \"DT_BUFFER_TOO_SMALL\"},\n        {DT_OUT_OF_NODES, \"DT_OUT_OF_NODES\"},\n        {DT_PARTIAL_RESULT, \"DT_PARTIAL_RESULT\"},\n    };\n\n    inline std::ostream& operator <<(std::ostream& stream, const WriteDtStatus& value)\n    {\n        for (const auto& status : dtStatuses)\n            if (value.status & status.first)\n                stream << status.second << \" \";\n        return stream;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/exceptions.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_EXCEPTIONS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_EXCEPTIONS_H\n\n#include <stdexcept>\n\nnamespace DetourNavigator\n{\n    struct NavigatorException : std::runtime_error\n    {\n        NavigatorException(const std::string& message) : std::runtime_error(message) {}\n        NavigatorException(const char* message) : std::runtime_error(message) {}\n    };\n\n    struct InvalidArgument : std::invalid_argument\n    {\n        InvalidArgument(const std::string& message) : std::invalid_argument(message) {}\n        InvalidArgument(const char* message) : std::invalid_argument(message) {}\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/findrandompointaroundcircle.cpp",
    "content": "#include \"findrandompointaroundcircle.hpp\"\n#include \"settings.hpp\"\n#include \"findsmoothpath.hpp\"\n\n#include <components/misc/rng.hpp>\n\n#include <DetourNavMesh.h>\n#include <DetourNavMeshQuery.h>\n\nnamespace DetourNavigator\n{\n    std::optional<osg::Vec3f> findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents,\n        const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings)\n    {\n        dtNavMeshQuery navMeshQuery;\n        if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes))\n            return std::optional<osg::Vec3f>();\n\n        dtQueryFilter queryFilter;\n        queryFilter.setIncludeFlags(includeFlags);\n\n        dtPolyRef startRef = findNearestPolyExpanding(navMeshQuery, queryFilter, start, halfExtents);\n        if (startRef == 0)\n            return std::optional<osg::Vec3f>();\n\n        dtPolyRef resultRef = 0;\n        osg::Vec3f resultPosition;\n        navMeshQuery.findRandomPointAroundCircle(startRef, start.ptr(), maxRadius, &queryFilter,\n            []() { return Misc::Rng::rollProbability(); }, &resultRef, resultPosition.ptr());\n\n        if (resultRef == 0)\n            return std::optional<osg::Vec3f>();\n\n        return std::optional<osg::Vec3f>(resultPosition);\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/findrandompointaroundcircle.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_FINDRANDOMPOINTAROUNDCIRCLE_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_FINDRANDOMPOINTAROUNDCIRCLE_H\n\n#include \"flags.hpp\"\n\n#include <optional>\n#include <osg/Vec3f>\n\nclass dtNavMesh;\n\nnamespace DetourNavigator\n{\n    struct Settings;\n\n    std::optional<osg::Vec3f> findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents,\n        const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings);\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/findsmoothpath.cpp",
    "content": "#include \"findsmoothpath.hpp\"\n\n#include <components/misc/convert.hpp>\n\n#include <algorithm>\n#include <array>\n\nnamespace DetourNavigator\n{\n    std::vector<dtPolyRef> fixupCorridor(const std::vector<dtPolyRef>& path, const std::vector<dtPolyRef>& visited)\n    {\n        std::vector<dtPolyRef>::const_reverse_iterator furthestVisited;\n\n        // Find furthest common polygon.\n        const auto it = std::find_if(path.rbegin(), path.rend(), [&] (dtPolyRef pathValue)\n        {\n            const auto it = std::find(visited.rbegin(), visited.rend(), pathValue);\n            if (it == visited.rend())\n                return false;\n            furthestVisited = it;\n            return true;\n        });\n\n        // If no intersection found just return current path.\n        if (it == path.rend())\n            return path;\n        const auto furthestPath = it.base() - 1;\n\n        // Concatenate paths.\n\n        // visited: a_1 ... a_n x b_1 ... b_n\n        //      furthestVisited ^\n        //    path: C x D\n        //            ^ furthestPath\n        //  result: x b_n ... b_1 D\n\n        std::vector<dtPolyRef> result;\n        result.reserve(static_cast<std::size_t>(furthestVisited - visited.rbegin())\n                       + static_cast<std::size_t>(path.end() - furthestPath) - 1);\n        std::copy(visited.rbegin(), furthestVisited + 1, std::back_inserter(result));\n        std::copy(furthestPath + 1, path.end(), std::back_inserter(result));\n\n        return result;\n    }\n\n    // This function checks if the path has a small U-turn, that is,\n    // a polygon further in the path is adjacent to the first polygon\n    // in the path. If that happens, a shortcut is taken.\n    // This can happen if the target (T) location is at tile boundary,\n    // and we're (S) approaching it parallel to the tile edge.\n    // The choice at the vertex can be arbitrary,\n    //  +---+---+\n    //  |:::|:::|\n    //  +-S-+-T-+\n    //  |:::|   | <-- the step can end up in here, resulting U-turn path.\n    //  +---+---+\n    std::vector<dtPolyRef> fixupShortcuts(const std::vector<dtPolyRef>& path, const dtNavMeshQuery& navQuery)\n    {\n        if (path.size() < 3)\n            return path;\n\n        // Get connected polygons\n        const dtMeshTile* tile = nullptr;\n        const dtPoly* poly = nullptr;\n        if (dtStatusFailed(navQuery.getAttachedNavMesh()->getTileAndPolyByRef(path[0], &tile, &poly)))\n            return path;\n\n        const std::size_t maxNeis = 16;\n        std::array<dtPolyRef, maxNeis> neis;\n        std::size_t nneis = 0;\n\n        for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)\n        {\n            const dtLink* link = &tile->links[k];\n            if (link->ref != 0)\n            {\n                if (nneis < maxNeis)\n                    neis[nneis++] = link->ref;\n            }\n        }\n\n        // If any of the neighbour polygons is within the next few polygons\n        // in the path, short cut to that polygon directly.\n        const std::size_t maxLookAhead = 6;\n        std::size_t cut = 0;\n        for (std::size_t i = std::min(maxLookAhead, path.size()) - 1; i > 1 && cut == 0; i--)\n        {\n            for (std::size_t j = 0; j < nneis; j++)\n            {\n                if (path[i] == neis[j])\n                {\n                    cut = i;\n                    break;\n                }\n            }\n        }\n        if (cut <= 1)\n            return path;\n\n        std::vector<dtPolyRef> result;\n        const auto offset = cut - 1;\n        result.reserve(1 + path.size() - offset);\n        result.push_back(path.front());\n        std::copy(path.begin() + std::ptrdiff_t(offset), path.end(), std::back_inserter(result));\n        return result;\n    }\n\n    std::optional<SteerTarget> getSteerTarget(const dtNavMeshQuery& navMeshQuery, const osg::Vec3f& startPos,\n            const osg::Vec3f& endPos, const float minTargetDist, const std::vector<dtPolyRef>& path)\n    {\n        // Find steer target.\n        SteerTarget result;\n        constexpr int maxSteerPoints = 3;\n        std::array<float, maxSteerPoints * 3> steerPath;\n        std::array<unsigned char, maxSteerPoints> steerPathFlags;\n        std::array<dtPolyRef, maxSteerPoints> steerPathPolys;\n        int nsteerPath = 0;\n        const dtStatus status = navMeshQuery.findStraightPath(startPos.ptr(), endPos.ptr(), path.data(),\n            static_cast<int>(path.size()), steerPath.data(), steerPathFlags.data(), steerPathPolys.data(),\n            &nsteerPath, maxSteerPoints);\n        if (dtStatusFailed(status))\n            return std::nullopt;\n        assert(nsteerPath >= 0);\n        if (!nsteerPath)\n            return std::nullopt;\n\n        // Find vertex far enough to steer to.\n        std::size_t ns = 0;\n        while (ns < static_cast<std::size_t>(nsteerPath))\n        {\n            // Stop at Off-Mesh link or when point is further than slop away.\n            if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||\n                    !inRange(Misc::Convert::makeOsgVec3f(&steerPath[ns * 3]), startPos, minTargetDist))\n                break;\n            ns++;\n        }\n        // Failed to find good point to steer to.\n        if (ns >= static_cast<std::size_t>(nsteerPath))\n            return std::nullopt;\n\n        dtVcopy(result.steerPos.ptr(), &steerPath[ns * 3]);\n        result.steerPos.y() = startPos[1];\n        result.steerPosFlag = steerPathFlags[ns];\n        result.steerPosRef = steerPathPolys[ns];\n\n        return result;\n    }\n\n    dtPolyRef findNearestPolyExpanding(const dtNavMeshQuery& query, const dtQueryFilter& filter,\n            const osg::Vec3f& center, const osg::Vec3f& halfExtents)\n    {\n        dtPolyRef ref = 0;\n        for (int i = 0; i < 3; ++i)\n        {\n            const dtStatus status = query.findNearestPoly(center.ptr(), (halfExtents * (1 << i)).ptr(), &filter, &ref, nullptr);\n            if (!dtStatusFailed(status) && ref != 0)\n                break;\n        }\n        return ref;\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/findsmoothpath.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_FINDSMOOTHPATH_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_FINDSMOOTHPATH_H\n\n#include \"dtstatus.hpp\"\n#include \"exceptions.hpp\"\n#include \"flags.hpp\"\n#include \"settings.hpp\"\n#include \"settingsutils.hpp\"\n#include \"debug.hpp\"\n#include \"status.hpp\"\n#include \"areatype.hpp\"\n\n#include <DetourCommon.h>\n#include <DetourNavMesh.h>\n#include <DetourNavMeshQuery.h>\n\n#include <osg/Vec3f>\n\n#include <cassert>\n#include <vector>\n\nclass dtNavMesh;\n\nnamespace DetourNavigator\n{\n    struct Settings;\n\n    inline bool inRange(const osg::Vec3f& v1, const osg::Vec3f& v2, const float r)\n    {\n        return (osg::Vec2f(v1.x(), v1.z()) - osg::Vec2f(v2.x(), v2.z())).length() < r;\n    }\n\n    std::vector<dtPolyRef> fixupCorridor(const std::vector<dtPolyRef>& path, const std::vector<dtPolyRef>& visited);\n\n    // This function checks if the path has a small U-turn, that is,\n    // a polygon further in the path is adjacent to the first polygon\n    // in the path. If that happens, a shortcut is taken.\n    // This can happen if the target (T) location is at tile boundary,\n    // and we're (S) approaching it parallel to the tile edge.\n    // The choice at the vertex can be arbitrary,\n    //  +---+---+\n    //  |:::|:::|\n    //  +-S-+-T-+\n    //  |:::|   | <-- the step can end up in here, resulting U-turn path.\n    //  +---+---+\n    std::vector<dtPolyRef> fixupShortcuts(const std::vector<dtPolyRef>& path, const dtNavMeshQuery& navQuery);\n\n    struct SteerTarget\n    {\n        osg::Vec3f steerPos;\n        unsigned char steerPosFlag;\n        dtPolyRef steerPosRef;\n    };\n\n    std::optional<SteerTarget> getSteerTarget(const dtNavMeshQuery& navQuery, const osg::Vec3f& startPos,\n            const osg::Vec3f& endPos, const float minTargetDist, const std::vector<dtPolyRef>& path);\n\n    template <class OutputIterator>\n    class OutputTransformIterator\n    {\n    public:\n        OutputTransformIterator(OutputIterator& impl, const Settings& settings)\n            : mImpl(impl), mSettings(settings)\n        {\n        }\n\n        OutputTransformIterator& operator *()\n        {\n            return *this;\n        }\n\n        OutputTransformIterator& operator ++()\n        {\n            ++mImpl.get();\n            return *this;\n        }\n\n        OutputTransformIterator operator ++(int)\n        {\n            const auto copy = *this;\n            ++(*this);\n            return copy;\n        }\n\n        OutputTransformIterator& operator =(const osg::Vec3f& value)\n        {\n            *mImpl.get() = fromNavMeshCoordinates(mSettings, value);\n            return *this;\n        }\n\n    private:\n        std::reference_wrapper<OutputIterator> mImpl;\n        std::reference_wrapper<const Settings> mSettings;\n    };\n\n    inline bool initNavMeshQuery(dtNavMeshQuery& value, const dtNavMesh& navMesh, const int maxNodes)\n    {\n        const auto status = value.init(&navMesh, maxNodes);\n        return dtStatusSucceed(status);\n    }\n\n    dtPolyRef findNearestPolyExpanding(const dtNavMeshQuery& query, const dtQueryFilter& filter,\n            const osg::Vec3f& center, const osg::Vec3f& halfExtents);\n\n    struct MoveAlongSurfaceResult\n    {\n        osg::Vec3f mResultPos;\n        std::vector<dtPolyRef> mVisited;\n    };\n\n    inline std::optional<MoveAlongSurfaceResult> moveAlongSurface(const dtNavMeshQuery& navMeshQuery,\n        const dtPolyRef startRef, const osg::Vec3f& startPos, const osg::Vec3f& endPos, const dtQueryFilter& filter,\n        const std::size_t maxVisitedSize)\n    {\n        MoveAlongSurfaceResult result;\n        result.mVisited.resize(maxVisitedSize);\n        int visitedNumber = 0;\n        const auto status = navMeshQuery.moveAlongSurface(startRef, startPos.ptr(), endPos.ptr(),\n            &filter, result.mResultPos.ptr(), result.mVisited.data(), &visitedNumber, static_cast<int>(maxVisitedSize));\n        if (!dtStatusSucceed(status))\n            return {};\n        assert(visitedNumber >= 0);\n        assert(visitedNumber <= static_cast<int>(maxVisitedSize));\n        result.mVisited.resize(static_cast<std::size_t>(visitedNumber));\n        return {std::move(result)};\n    }\n\n    inline std::optional<std::vector<dtPolyRef>> findPath(const dtNavMeshQuery& navMeshQuery, const dtPolyRef startRef,\n        const dtPolyRef endRef, const osg::Vec3f& startPos, const osg::Vec3f& endPos, const dtQueryFilter& queryFilter,\n        const std::size_t maxSize)\n    {\n        int pathLen = 0;\n        std::vector<dtPolyRef> result(maxSize);\n        const auto status = navMeshQuery.findPath(startRef, endRef, startPos.ptr(), endPos.ptr(), &queryFilter,\n            result.data(), &pathLen, static_cast<int>(maxSize));\n        if (!dtStatusSucceed(status))\n            return {};\n        assert(pathLen >= 0);\n        assert(static_cast<std::size_t>(pathLen) <= maxSize);\n        result.resize(static_cast<std::size_t>(pathLen));\n        return {std::move(result)};\n    }\n\n    template <class OutputIterator>\n    Status makeSmoothPath(const dtNavMesh& navMesh, const dtNavMeshQuery& navMeshQuery,\n            const dtQueryFilter& filter, const osg::Vec3f& start, const osg::Vec3f& end, const float stepSize,\n            std::vector<dtPolyRef> polygonPath, std::size_t maxSmoothPathSize, OutputIterator& out)\n    {\n        // Iterate over the path to find smooth path on the detail mesh surface.\n        osg::Vec3f iterPos;\n        navMeshQuery.closestPointOnPoly(polygonPath.front(), start.ptr(), iterPos.ptr(), nullptr);\n\n        osg::Vec3f targetPos;\n        navMeshQuery.closestPointOnPoly(polygonPath.back(), end.ptr(), targetPos.ptr(), nullptr);\n\n        constexpr float slop = 0.01f;\n\n        *out++ = iterPos;\n\n        std::size_t smoothPathSize = 1;\n\n        // Move towards target a small advancement at a time until target reached or\n        // when ran out of memory to store the path.\n        while (!polygonPath.empty() && smoothPathSize < maxSmoothPathSize)\n        {\n            // Find location to steer towards.\n            const auto steerTarget = getSteerTarget(navMeshQuery, iterPos, targetPos, slop, polygonPath);\n\n            if (!steerTarget)\n                break;\n\n            const bool endOfPath = bool(steerTarget->steerPosFlag & DT_STRAIGHTPATH_END);\n            const bool offMeshConnection = bool(steerTarget->steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION);\n\n            // Find movement delta.\n            const osg::Vec3f delta = steerTarget->steerPos - iterPos;\n            float len = delta.length();\n            // If the steer target is end of path or off-mesh link, do not move past the location.\n            if ((endOfPath || offMeshConnection) && len < stepSize)\n                len = 1;\n            else\n                len = stepSize / len;\n\n            const osg::Vec3f moveTgt = iterPos + delta * len;\n            const auto result = moveAlongSurface(navMeshQuery, polygonPath.front(), iterPos, moveTgt, filter, 16);\n\n            if (!result)\n                return Status::MoveAlongSurfaceFailed;\n\n            polygonPath = fixupCorridor(polygonPath, result->mVisited);\n            polygonPath = fixupShortcuts(polygonPath, navMeshQuery);\n\n            // Handle end of path and off-mesh links when close enough.\n            if (endOfPath && inRange(result->mResultPos, steerTarget->steerPos, slop))\n            {\n                // Reached end of path.\n                iterPos = targetPos;\n                *out++ = iterPos;\n                ++smoothPathSize;\n                break;\n            }\n            else if (offMeshConnection && inRange(result->mResultPos, steerTarget->steerPos, slop))\n            {\n                // Advance the path up to and over the off-mesh connection.\n                dtPolyRef prevRef = 0;\n                dtPolyRef polyRef = polygonPath.front();\n                std::size_t npos = 0;\n                while (npos < polygonPath.size() && polyRef != steerTarget->steerPosRef)\n                {\n                    prevRef = polyRef;\n                    polyRef = polygonPath[npos];\n                    ++npos;\n                }\n                std::copy(polygonPath.begin() + std::ptrdiff_t(npos), polygonPath.end(), polygonPath.begin());\n                polygonPath.resize(polygonPath.size() - npos);\n\n                // Reached off-mesh connection.\n                osg::Vec3f startPos;\n                osg::Vec3f endPos;\n\n                // Handle the connection.\n                if (dtStatusSucceed(navMesh.getOffMeshConnectionPolyEndPoints(prevRef, polyRef,\n                        startPos.ptr(), endPos.ptr())))\n                {\n                    *out++ = startPos;\n                    ++smoothPathSize;\n\n                    // Hack to make the dotted path not visible during off-mesh connection.\n                    if (smoothPathSize & 1)\n                    {\n                        *out++ = startPos;\n                        ++smoothPathSize;\n                    }\n\n                    // Move position at the other side of the off-mesh link.\n                    if (dtStatusFailed(navMeshQuery.getPolyHeight(polygonPath.front(), endPos.ptr(), &iterPos.y())))\n                        return Status::GetPolyHeightFailed;\n                    iterPos.x() = endPos.x();\n                    iterPos.z() = endPos.z();\n                }\n            }\n\n            if (dtStatusFailed(navMeshQuery.getPolyHeight(polygonPath.front(), result->mResultPos.ptr(), &iterPos.y())))\n                return Status::GetPolyHeightFailed;\n            iterPos.x() = result->mResultPos.x();\n            iterPos.z() = result->mResultPos.z();\n\n            // Store results.\n            *out++ = iterPos;\n            ++smoothPathSize;\n        }\n\n        return Status::Success;\n    }\n\n    template <class OutputIterator>\n    Status findSmoothPath(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const float stepSize,\n            const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts,\n            const Settings& settings, OutputIterator& out)\n    {\n        dtNavMeshQuery navMeshQuery;\n        if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes))\n            return Status::InitNavMeshQueryFailed;\n\n        dtQueryFilter queryFilter;\n        queryFilter.setIncludeFlags(includeFlags);\n        queryFilter.setAreaCost(AreaType_water, areaCosts.mWater);\n        queryFilter.setAreaCost(AreaType_door, areaCosts.mDoor);\n        queryFilter.setAreaCost(AreaType_pathgrid, areaCosts.mPathgrid);\n        queryFilter.setAreaCost(AreaType_ground, areaCosts.mGround);\n\n        dtPolyRef startRef = findNearestPolyExpanding(navMeshQuery, queryFilter, start, halfExtents);\n        if (startRef == 0)\n            return Status::StartPolygonNotFound;\n\n        dtPolyRef endRef = findNearestPolyExpanding(navMeshQuery, queryFilter, end, halfExtents);\n        if (endRef == 0)\n            return Status::EndPolygonNotFound;\n\n        const auto polygonPath = findPath(navMeshQuery, startRef, endRef, start, end, queryFilter,\n                                          settings.mMaxPolygonPathSize);\n\n        if (!polygonPath)\n            return Status::FindPathOverPolygonsFailed;\n\n        if (polygonPath->empty() || polygonPath->back() != endRef)\n            return Status::Success;\n\n        auto outTransform = OutputTransformIterator<OutputIterator>(out, settings);\n        return makeSmoothPath(navMesh, navMeshQuery, queryFilter, start, end, stepSize, std::move(*polygonPath),\n            settings.mMaxSmoothPathSize, outTransform);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/flags.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_FLAGS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_FLAGS_H\n\n#include <ostream>\n\nnamespace DetourNavigator\n{\n    using Flags = unsigned short;\n\n    enum Flag : Flags\n    {\n        Flag_none = 0,\n        Flag_walk = 1 << 0,\n        Flag_swim = 1 << 1,\n        Flag_openDoor = 1 << 2,\n        Flag_usePathgrid = 1 << 3,\n    };\n\n    inline std::ostream& operator <<(std::ostream& stream, const Flag value)\n    {\n        switch (value)\n        {\n            case Flag_none:\n                return stream << \"none\";\n            case Flag_walk:\n                return stream << \"walk\";\n            case Flag_swim:\n                return stream << \"swim\";\n            case Flag_openDoor:\n                return stream << \"openDoor\";\n            case Flag_usePathgrid:\n                return stream << \"usePathgrid\";\n        }\n\n        return stream;\n    }\n\n    struct WriteFlags\n    {\n        Flags mValue;\n\n        friend inline std::ostream& operator <<(std::ostream& stream, const WriteFlags& value)\n        {\n            if (value.mValue == Flag_none)\n            {\n                return stream << Flag_none;\n            }\n            else\n            {\n                bool first = true;\n                for (const auto flag : {Flag_walk, Flag_swim, Flag_openDoor, Flag_usePathgrid})\n                {\n                    if (value.mValue & flag)\n                    {\n                        if (!first)\n                            stream << \" | \";\n                        first = false;\n                        stream << flag;\n                    }\n                }\n\n                return stream;\n            }\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/gettilespositions.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H\n\n#include \"settings.hpp\"\n#include \"settingsutils.hpp\"\n#include \"tileposition.hpp\"\n\n#include <components/misc/convert.hpp>\n\n#include <BulletCollision/CollisionShapes/btCollisionShape.h>\n\n#include <osg/Vec3f>\n\nnamespace DetourNavigator\n{\n    template <class Callback>\n    void getTilesPositions(const osg::Vec3f& aabbMin, const osg::Vec3f& aabbMax,\n        const Settings& settings, Callback&& callback)\n    {\n        auto min = toNavMeshCoordinates(settings, aabbMin);\n        auto max = toNavMeshCoordinates(settings, aabbMax);\n\n        const auto border = getBorderSize(settings);\n        min -= osg::Vec3f(border, border, border);\n        max += osg::Vec3f(border, border, border);\n\n        auto minTile = getTilePosition(settings, min);\n        auto maxTile = getTilePosition(settings, max);\n\n        if (minTile.x() > maxTile.x())\n            std::swap(minTile.x(), maxTile.x());\n\n        if (minTile.y() > maxTile.y())\n            std::swap(minTile.y(), maxTile.y());\n\n        for (int tileX = minTile.x(); tileX <= maxTile.x(); ++tileX)\n            for (int tileY = minTile.y(); tileY <= maxTile.y(); ++tileY)\n                callback(TilePosition {tileX, tileY});\n    }\n\n    template <class Callback>\n    void getTilesPositions(const btCollisionShape& shape, const btTransform& transform,\n        const Settings& settings, Callback&& callback)\n    {\n        btVector3 aabbMin;\n        btVector3 aabbMax;\n        shape.getAabb(transform, aabbMin, aabbMax);\n\n        getTilesPositions(Misc::Convert::makeOsgVec3f(aabbMin), Misc::Convert::makeOsgVec3f(aabbMax), settings, std::forward<Callback>(callback));\n    }\n\n    template <class Callback>\n    void getTilesPositions(const int cellSize, const btTransform& transform,\n        const Settings& settings, Callback&& callback)\n    {\n        const auto halfCellSize = cellSize / 2;\n        auto aabbMin = transform(btVector3(-halfCellSize, -halfCellSize, 0));\n        auto aabbMax = transform(btVector3(halfCellSize, halfCellSize, 0));\n\n        aabbMin.setX(std::min(aabbMin.x(), aabbMax.x()));\n        aabbMin.setY(std::min(aabbMin.y(), aabbMax.y()));\n\n        aabbMax.setX(std::max(aabbMin.x(), aabbMax.x()));\n        aabbMax.setY(std::max(aabbMin.y(), aabbMax.y()));\n\n        getTilesPositions(Misc::Convert::makeOsgVec3f(aabbMin), Misc::Convert::makeOsgVec3f(aabbMax), settings, std::forward<Callback>(callback));\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/makenavmesh.cpp",
    "content": "#include \"makenavmesh.hpp\"\n#include \"debug.hpp\"\n#include \"exceptions.hpp\"\n#include \"recastmesh.hpp\"\n#include \"settings.hpp\"\n#include \"settingsutils.hpp\"\n#include \"sharednavmesh.hpp\"\n#include \"flags.hpp\"\n#include \"navmeshtilescache.hpp\"\n\n#include <components/misc/convert.hpp>\n\n#include <DetourNavMesh.h>\n#include <DetourNavMeshBuilder.h>\n#include <Recast.h>\n#include <RecastAlloc.h>\n\n#include <components/debug/debuglog.hpp>\n\n#include <algorithm>\n#include <iomanip>\n#include <limits>\n#include <array>\n\nnamespace\n{\n    using namespace DetourNavigator;\n\n    void initPolyMeshDetail(rcPolyMeshDetail& value)\n    {\n        value.meshes = nullptr;\n        value.verts = nullptr;\n        value.tris = nullptr;\n    }\n\n    struct PolyMeshDetailStackDeleter\n    {\n        void operator ()(rcPolyMeshDetail* value) const\n        {\n            rcFree(value->meshes);\n            rcFree(value->verts);\n            rcFree(value->tris);\n        }\n    };\n\n    using PolyMeshDetailStackPtr = std::unique_ptr<rcPolyMeshDetail, PolyMeshDetailStackDeleter>;\n\n    struct WaterBounds\n    {\n        osg::Vec3f mMin;\n        osg::Vec3f mMax;\n    };\n\n    WaterBounds getWaterBounds(const RecastMesh::Water& water, const Settings& settings,\n        const osg::Vec3f& agentHalfExtents)\n    {\n        if (water.mCellSize == std::numeric_limits<int>::max())\n        {\n            const auto transform = getSwimLevelTransform(settings, water.mTransform, agentHalfExtents.z());\n            const auto min = toNavMeshCoordinates(settings, Misc::Convert::makeOsgVec3f(transform(btVector3(-1, -1, 0))));\n            const auto max = toNavMeshCoordinates(settings, Misc::Convert::makeOsgVec3f(transform(btVector3(1, 1, 0))));\n            return WaterBounds {\n                osg::Vec3f(-std::numeric_limits<float>::max(), min.y(), -std::numeric_limits<float>::max()),\n                osg::Vec3f(std::numeric_limits<float>::max(), max.y(), std::numeric_limits<float>::max())\n            };\n        }\n        else\n        {\n            const auto transform = getSwimLevelTransform(settings, water.mTransform, agentHalfExtents.z());\n            const auto halfCellSize = water.mCellSize / 2.0f;\n            return WaterBounds {\n                toNavMeshCoordinates(settings, Misc::Convert::makeOsgVec3f(transform(btVector3(-halfCellSize, -halfCellSize, 0)))),\n                toNavMeshCoordinates(settings, Misc::Convert::makeOsgVec3f(transform(btVector3(halfCellSize, halfCellSize, 0))))\n            };\n        }\n    }\n\n    std::vector<float> getOffMeshVerts(const std::vector<OffMeshConnection>& connections)\n    {\n        std::vector<float> result;\n\n        result.reserve(connections.size() * 6);\n\n        const auto add = [&] (const osg::Vec3f& v)\n        {\n            result.push_back(v.x());\n            result.push_back(v.y());\n            result.push_back(v.z());\n        };\n\n        for (const auto& v : connections)\n        {\n            add(v.mStart);\n            add(v.mEnd);\n        }\n\n        return result;\n    }\n\n    Flag getFlag(AreaType areaType)\n    {\n        switch (areaType)\n        {\n            case AreaType_null:\n                return Flag_none;\n            case AreaType_ground:\n                return Flag_walk;\n            case AreaType_water:\n                return Flag_swim;\n            case AreaType_door:\n                return Flag_openDoor;\n            case AreaType_pathgrid:\n                return Flag_usePathgrid;\n        }\n        return Flag_none;\n    }\n\n    std::vector<unsigned char> getOffMeshConAreas(const std::vector<OffMeshConnection>& connections)\n    {\n        std::vector<unsigned char> result;\n        result.reserve(connections.size());\n        std::transform(connections.begin(), connections.end(), std::back_inserter(result),\n                       [] (const OffMeshConnection& v) { return v.mAreaType; });\n        return result;\n    }\n\n    std::vector<unsigned short> getOffMeshFlags(const std::vector<OffMeshConnection>& connections)\n    {\n        std::vector<unsigned short> result;\n        result.reserve(connections.size());\n        std::transform(connections.begin(), connections.end(), std::back_inserter(result),\n                       [] (const OffMeshConnection& v) { return getFlag(v.mAreaType); });\n        return result;\n    }\n\n    rcConfig makeConfig(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax,\n        const Settings& settings)\n    {\n        rcConfig config;\n\n        config.cs = settings.mCellSize;\n        config.ch = settings.mCellHeight;\n        config.walkableSlopeAngle = settings.mMaxSlope;\n        config.walkableHeight = static_cast<int>(std::ceil(getHeight(settings, agentHalfExtents) / config.ch));\n        config.walkableClimb = static_cast<int>(std::floor(getMaxClimb(settings) / config.ch));\n        config.walkableRadius = static_cast<int>(std::ceil(getRadius(settings, agentHalfExtents) / config.cs));\n        config.maxEdgeLen = static_cast<int>(std::round(settings.mMaxEdgeLen / config.cs));\n        config.maxSimplificationError = settings.mMaxSimplificationError;\n        config.minRegionArea = settings.mRegionMinSize * settings.mRegionMinSize;\n        config.mergeRegionArea = settings.mRegionMergeSize * settings.mRegionMergeSize;\n        config.maxVertsPerPoly = settings.mMaxVertsPerPoly;\n        config.detailSampleDist = settings.mDetailSampleDist < 0.9f ? 0 : config.cs * settings.mDetailSampleDist;\n        config.detailSampleMaxError = config.ch * settings.mDetailSampleMaxError;\n        config.borderSize = settings.mBorderSize;\n        config.width = settings.mTileSize + config.borderSize * 2;\n        config.height = settings.mTileSize + config.borderSize * 2;\n        rcVcopy(config.bmin, boundsMin.ptr());\n        rcVcopy(config.bmax, boundsMax.ptr());\n        config.bmin[0] -= getBorderSize(settings);\n        config.bmin[2] -= getBorderSize(settings);\n        config.bmax[0] += getBorderSize(settings);\n        config.bmax[2] += getBorderSize(settings);\n        config.tileSize = settings.mTileSize;\n\n        return config;\n    }\n\n    void createHeightfield(rcContext& context, rcHeightfield& solid, int width, int height, const float* bmin,\n        const float* bmax, const float cs, const float ch)\n    {\n        const auto result = rcCreateHeightfield(&context, solid, width, height, bmin, bmax, cs, ch);\n\n        if (!result)\n            throw NavigatorException(\"Failed to create heightfield for navmesh\");\n    }\n\n    bool rasterizeSolidObjectsTriangles(rcContext& context, const RecastMesh& recastMesh, const rcConfig& config,\n        rcHeightfield& solid)\n    {\n        const osg::Vec2f tileBoundsMin(config.bmin[0], config.bmin[2]);\n        const osg::Vec2f tileBoundsMax(config.bmax[0], config.bmax[2]);\n        std::vector<unsigned char> areas(recastMesh.getAreaTypes().begin(), recastMesh.getAreaTypes().end());\n\n        rcClearUnwalkableTriangles(\n            &context,\n            config.walkableSlopeAngle,\n            recastMesh.getVertices().data(),\n            static_cast<int>(recastMesh.getVerticesCount()),\n            recastMesh.getIndices().data(),\n            static_cast<int>(areas.size()),\n            areas.data()\n        );\n\n        return rcRasterizeTriangles(\n            &context,\n            recastMesh.getVertices().data(),\n            static_cast<int>(recastMesh.getVerticesCount()),\n            recastMesh.getIndices().data(),\n            areas.data(),\n            static_cast<int>(areas.size()),\n            solid,\n            config.walkableClimb\n        );\n    }\n\n    void rasterizeWaterTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,\n        const Settings& settings, const rcConfig& config, rcHeightfield& solid)\n    {\n        const std::array<unsigned char, 2> areas {{AreaType_water, AreaType_water}};\n\n        for (const auto& water : recastMesh.getWater())\n        {\n            const auto bounds = getWaterBounds(water, settings, agentHalfExtents);\n\n            const osg::Vec2f tileBoundsMin(\n                std::min(config.bmax[0], std::max(config.bmin[0], bounds.mMin.x())),\n                std::min(config.bmax[2], std::max(config.bmin[2], bounds.mMin.z()))\n            );\n            const osg::Vec2f tileBoundsMax(\n                std::min(config.bmax[0], std::max(config.bmin[0], bounds.mMax.x())),\n                std::min(config.bmax[2], std::max(config.bmin[2], bounds.mMax.z()))\n            );\n\n            if (tileBoundsMax == tileBoundsMin)\n                continue;\n\n            const std::array<osg::Vec3f, 4> vertices {{\n                osg::Vec3f(tileBoundsMin.x(), bounds.mMin.y(), tileBoundsMin.y()),\n                osg::Vec3f(tileBoundsMin.x(), bounds.mMin.y(), tileBoundsMax.y()),\n                osg::Vec3f(tileBoundsMax.x(), bounds.mMin.y(), tileBoundsMax.y()),\n                osg::Vec3f(tileBoundsMax.x(), bounds.mMin.y(), tileBoundsMin.y()),\n            }};\n\n            std::array<float, 4 * 3> convertedVertices;\n            auto convertedVerticesIt = convertedVertices.begin();\n\n            for (const auto& vertex : vertices)\n                convertedVerticesIt = std::copy(vertex.ptr(), vertex.ptr() + 3, convertedVerticesIt);\n\n            const std::array<int, 6> indices {{\n                0, 1, 2,\n                0, 2, 3,\n            }};\n\n            const auto trianglesRasterized = rcRasterizeTriangles(\n                &context,\n                convertedVertices.data(),\n                static_cast<int>(convertedVertices.size() / 3),\n                indices.data(),\n                areas.data(),\n                static_cast<int>(areas.size()),\n                solid,\n                config.walkableClimb\n            );\n\n            if (!trianglesRasterized)\n                throw NavigatorException(\"Failed to create rasterize water triangles for navmesh\");\n        }\n    }\n\n    bool rasterizeTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,\n        const rcConfig& config, const Settings& settings, rcHeightfield& solid)\n    {\n        if (!rasterizeSolidObjectsTriangles(context, recastMesh, config, solid))\n            return false;\n\n        rasterizeWaterTriangles(context, agentHalfExtents, recastMesh, settings, config, solid);\n\n        return true;\n    }\n\n    void buildCompactHeightfield(rcContext& context, const int walkableHeight, const int walkableClimb,\n                                 rcHeightfield& solid, rcCompactHeightfield& compact)\n    {\n        const auto result = rcBuildCompactHeightfield(&context, walkableHeight,\n            walkableClimb, solid, compact);\n\n        if (!result)\n            throw NavigatorException(\"Failed to build compact heightfield for navmesh\");\n    }\n\n    void erodeWalkableArea(rcContext& context, int walkableRadius, rcCompactHeightfield& compact)\n    {\n        const auto result = rcErodeWalkableArea(&context, walkableRadius, compact);\n\n        if (!result)\n            throw NavigatorException(\"Failed to erode walkable area for navmesh\");\n    }\n\n    void buildDistanceField(rcContext& context, rcCompactHeightfield& compact)\n    {\n        const auto result = rcBuildDistanceField(&context, compact);\n\n        if (!result)\n            throw NavigatorException(\"Failed to build distance field for navmesh\");\n    }\n\n    void buildRegions(rcContext& context, rcCompactHeightfield& compact, const int borderSize,\n        const int minRegionArea, const int mergeRegionArea)\n    {\n        const auto result = rcBuildRegions(&context, compact, borderSize, minRegionArea, mergeRegionArea);\n\n        if (!result)\n            throw NavigatorException(\"Failed to build distance field for navmesh\");\n    }\n\n    void buildContours(rcContext& context, rcCompactHeightfield& compact, const float maxError, const int maxEdgeLen,\n        rcContourSet& contourSet, const int buildFlags = RC_CONTOUR_TESS_WALL_EDGES)\n    {\n        const auto result = rcBuildContours(&context, compact, maxError, maxEdgeLen, contourSet, buildFlags);\n\n        if (!result)\n            throw NavigatorException(\"Failed to build contours for navmesh\");\n    }\n\n    void buildPolyMesh(rcContext& context, rcContourSet& contourSet, const int maxVertsPerPoly, rcPolyMesh& polyMesh)\n    {\n        const auto result = rcBuildPolyMesh(&context, contourSet, maxVertsPerPoly, polyMesh);\n\n        if (!result)\n            throw NavigatorException(\"Failed to build poly mesh for navmesh\");\n    }\n\n    void buildPolyMeshDetail(rcContext& context, const rcPolyMesh& polyMesh, const rcCompactHeightfield& compact,\n        const float sampleDist, const float sampleMaxError, rcPolyMeshDetail& polyMeshDetail)\n    {\n        const auto result = rcBuildPolyMeshDetail(&context, polyMesh, compact, sampleDist, sampleMaxError,\n                                                  polyMeshDetail);\n\n        if (!result)\n            throw NavigatorException(\"Failed to build detail poly mesh for navmesh\");\n    }\n\n    void setPolyMeshFlags(rcPolyMesh& polyMesh)\n    {\n        for (int i = 0; i < polyMesh.npolys; ++i)\n            polyMesh.flags[i] = getFlag(static_cast<AreaType>(polyMesh.areas[i]));\n    }\n\n    bool fillPolyMesh(rcContext& context, const rcConfig& config, rcHeightfield& solid, rcPolyMesh& polyMesh,\n        rcPolyMeshDetail& polyMeshDetail)\n    {\n        rcCompactHeightfield compact;\n        buildCompactHeightfield(context, config.walkableHeight, config.walkableClimb, solid, compact);\n\n        erodeWalkableArea(context, config.walkableRadius, compact);\n        buildDistanceField(context, compact);\n        buildRegions(context, compact, config.borderSize, config.minRegionArea, config.mergeRegionArea);\n\n        rcContourSet contourSet;\n        buildContours(context, compact, config.maxSimplificationError, config.maxEdgeLen, contourSet);\n\n        if (contourSet.nconts == 0)\n            return false;\n\n        buildPolyMesh(context, contourSet, config.maxVertsPerPoly, polyMesh);\n\n        buildPolyMeshDetail(context, polyMesh, compact, config.detailSampleDist, config.detailSampleMaxError,\n                            polyMeshDetail);\n\n        setPolyMeshFlags(polyMesh);\n\n        return true;\n    }\n\n    NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,\n        const std::vector<OffMeshConnection>& offMeshConnections, const TilePosition& tile,\n        const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings)\n    {\n        rcContext context;\n        const auto config = makeConfig(agentHalfExtents, boundsMin, boundsMax, settings);\n\n        rcHeightfield solid;\n        createHeightfield(context, solid, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch);\n\n        if (!rasterizeTriangles(context, agentHalfExtents, recastMesh, config, settings, solid))\n            return NavMeshData();\n\n        rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid);\n        rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid);\n        rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid);\n\n        rcPolyMesh polyMesh;\n        rcPolyMeshDetail polyMeshDetail;\n        initPolyMeshDetail(polyMeshDetail);\n        const PolyMeshDetailStackPtr polyMeshDetailPtr(&polyMeshDetail);\n        if (!fillPolyMesh(context, config, solid, polyMesh, polyMeshDetail))\n            return NavMeshData();\n\n        const auto offMeshConVerts = getOffMeshVerts(offMeshConnections);\n        const std::vector<float> offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents));\n        const std::vector<unsigned char> offMeshConDir(offMeshConnections.size(), 0);\n        const std::vector<unsigned char> offMeshConAreas = getOffMeshConAreas(offMeshConnections);\n        const std::vector<unsigned short> offMeshConFlags = getOffMeshFlags(offMeshConnections);\n\n        dtNavMeshCreateParams params;\n        params.verts = polyMesh.verts;\n        params.vertCount = polyMesh.nverts;\n        params.polys = polyMesh.polys;\n        params.polyAreas = polyMesh.areas;\n        params.polyFlags = polyMesh.flags;\n        params.polyCount = polyMesh.npolys;\n        params.nvp = polyMesh.nvp;\n        params.detailMeshes = polyMeshDetail.meshes;\n        params.detailVerts = polyMeshDetail.verts;\n        params.detailVertsCount = polyMeshDetail.nverts;\n        params.detailTris = polyMeshDetail.tris;\n        params.detailTriCount = polyMeshDetail.ntris;\n        params.offMeshConVerts = offMeshConVerts.data();\n        params.offMeshConRad = offMeshConRad.data();\n        params.offMeshConDir = offMeshConDir.data();\n        params.offMeshConAreas = offMeshConAreas.data();\n        params.offMeshConFlags = offMeshConFlags.data();\n        params.offMeshConUserID = nullptr;\n        params.offMeshConCount = static_cast<int>(offMeshConnections.size());\n        params.walkableHeight = getHeight(settings, agentHalfExtents);\n        params.walkableRadius = getRadius(settings, agentHalfExtents);\n        params.walkableClimb = getMaxClimb(settings);\n        rcVcopy(params.bmin, polyMesh.bmin);\n        rcVcopy(params.bmax, polyMesh.bmax);\n        params.cs = config.cs;\n        params.ch = config.ch;\n        params.buildBvTree = true;\n        params.userId = 0;\n        params.tileX = tile.x();\n        params.tileY = tile.y();\n        params.tileLayer = 0;\n\n        unsigned char* navMeshData;\n        int navMeshDataSize;\n        const auto navMeshDataCreated = dtCreateNavMeshData(&params, &navMeshData, &navMeshDataSize);\n\n        if (!navMeshDataCreated)\n            throw NavigatorException(\"Failed to create navmesh tile data\");\n\n        return NavMeshData(navMeshData, navMeshDataSize);\n    }\n\n\n\n    template <class T>\n    unsigned long getMinValuableBitsNumber(const T value)\n    {\n        unsigned long power = 0;\n        while (power < sizeof(T) * 8 && (static_cast<T>(1) << power) < value)\n            ++power;\n        return power;\n    }\n}\n\nnamespace DetourNavigator\n{\n    NavMeshPtr makeEmptyNavMesh(const Settings& settings)\n    {\n        // Max tiles and max polys affect how the tile IDs are caculated.\n        // There are 22 bits available for identifying a tile and a polygon.\n        const int polysAndTilesBits = 22;\n        const auto polysBits = getMinValuableBitsNumber(settings.mMaxPolys);\n\n        if (polysBits >= polysAndTilesBits)\n            throw InvalidArgument(\"Too many polygons per tile\");\n\n        const auto tilesBits = polysAndTilesBits - polysBits;\n\n        dtNavMeshParams params;\n        std::fill_n(params.orig, 3, 0.0f);\n        params.tileWidth = settings.mTileSize * settings.mCellSize;\n        params.tileHeight = settings.mTileSize * settings.mCellSize;\n        params.maxTiles = 1 << tilesBits;\n        params.maxPolys = 1 << polysBits;\n\n        NavMeshPtr navMesh(dtAllocNavMesh(), &dtFreeNavMesh);\n        const auto status = navMesh->init(&params);\n\n        if (!dtStatusSucceed(status))\n            throw NavigatorException(\"Failed to init navmesh\");\n\n        return navMesh;\n    }\n\n    UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh,\n        const TilePosition& changedTile, const TilePosition& playerTile,\n        const std::vector<OffMeshConnection>& offMeshConnections, const Settings& settings,\n        const SharedNavMeshCacheItem& navMeshCacheItem, NavMeshTilesCache& navMeshTilesCache)\n    {\n        Log(Debug::Debug) << std::fixed << std::setprecision(2) <<\n            \"Update NavMesh with multiple tiles:\" <<\n            \" agentHeight=\" << getHeight(settings, agentHalfExtents) <<\n            \" agentMaxClimb=\" << getMaxClimb(settings) <<\n            \" agentRadius=\" << getRadius(settings, agentHalfExtents) <<\n            \" changedTile=(\" << changedTile << \")\" <<\n            \" playerTile=(\" << playerTile << \")\" <<\n            \" changedTileDistance=\" << getDistance(changedTile, playerTile);\n\n        const auto params = *navMeshCacheItem->lockConst()->getImpl().getParams();\n        const osg::Vec3f origin(params.orig[0], params.orig[1], params.orig[2]);\n\n        if (!recastMesh)\n        {\n            Log(Debug::Debug) << \"Ignore add tile: recastMesh is null\";\n            return navMeshCacheItem->lock()->removeTile(changedTile);\n        }\n\n        auto recastMeshBounds = recastMesh->getBounds();\n\n        for (const auto& water : recastMesh->getWater())\n        {\n            const auto waterBounds = getWaterBounds(water, settings, agentHalfExtents);\n            recastMeshBounds.mMin.y() = std::min(recastMeshBounds.mMin.y(), waterBounds.mMin.y());\n            recastMeshBounds.mMax.y() = std::max(recastMeshBounds.mMax.y(), waterBounds.mMax.y());\n        }\n\n        if (isEmpty(recastMeshBounds))\n        {\n            Log(Debug::Debug) << \"Ignore add tile: recastMesh is empty\";\n            return navMeshCacheItem->lock()->removeTile(changedTile);\n        }\n\n        if (!shouldAddTile(changedTile, playerTile, std::min(settings.mMaxTilesNumber, params.maxTiles)))\n        {\n            Log(Debug::Debug) << \"Ignore add tile: too far from player\";\n            return navMeshCacheItem->lock()->removeTile(changedTile);\n        }\n\n        auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh, offMeshConnections);\n        bool cached = static_cast<bool>(cachedNavMeshData);\n\n        if (!cachedNavMeshData)\n        {\n            const auto tileBounds = makeTileBounds(settings, changedTile);\n            const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), recastMeshBounds.mMin.y() - 1, tileBounds.mMin.y());\n            const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), recastMeshBounds.mMax.y() + 1, tileBounds.mMax.y());\n\n            auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, changedTile,\n                tileBorderMin, tileBorderMax, settings);\n\n            if (!navMeshData.mValue)\n            {\n                Log(Debug::Debug) << \"Ignore add tile: NavMeshData is null\";\n                return navMeshCacheItem->lock()->removeTile(changedTile);\n            }\n\n            cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh,\n                                                      offMeshConnections, std::move(navMeshData));\n\n            if (!cachedNavMeshData)\n            {\n                Log(Debug::Debug) << \"Navigator cache overflow\";\n                return navMeshCacheItem->lock()->updateTile(changedTile, std::move(navMeshData));\n            }\n        }\n\n        const auto updateStatus = navMeshCacheItem->lock()->updateTile(changedTile, std::move(cachedNavMeshData));\n\n        return UpdateNavMeshStatusBuilder(updateStatus).cached(cached).getResult();\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/makenavmesh.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_MAKENAVMESH_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_MAKENAVMESH_H\n\n#include \"offmeshconnectionsmanager.hpp\"\n#include \"navmeshcacheitem.hpp\"\n#include \"tileposition.hpp\"\n#include \"sharednavmesh.hpp\"\n#include \"navmeshtilescache.hpp\"\n\n#include <osg/Vec3f>\n\n#include <memory>\n\nclass dtNavMesh;\n\nnamespace DetourNavigator\n{\n    class RecastMesh;\n    struct Settings;\n\n    inline float getLength(const osg::Vec2i& value)\n    {\n        return std::sqrt(float(osg::square(value.x()) + osg::square(value.y())));\n    }\n\n    inline float getDistance(const TilePosition& lhs, const TilePosition& rhs)\n    {\n        return getLength(lhs - rhs);\n    }\n\n    inline bool shouldAddTile(const TilePosition& changedTile, const TilePosition& playerTile, int maxTiles)\n    {\n        const auto expectedTilesCount = std::ceil(osg::PI * osg::square(getDistance(changedTile, playerTile)));\n        return expectedTilesCount <= maxTiles;\n    }\n\n    NavMeshPtr makeEmptyNavMesh(const Settings& settings);\n\n    UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh,\n        const TilePosition& changedTile, const TilePosition& playerTile,\n        const std::vector<OffMeshConnection>& offMeshConnections, const Settings& settings,\n        const SharedNavMeshCacheItem& navMeshCacheItem, NavMeshTilesCache& navMeshTilesCache);\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/navigator.cpp",
    "content": "#include \"findrandompointaroundcircle.hpp\"\n#include \"navigator.hpp\"\n#include \"raycast.hpp\"\n\nnamespace DetourNavigator\n{\n    std::optional<osg::Vec3f> Navigator::findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents,\n        const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const\n    {\n        const auto navMesh = getNavMesh(agentHalfExtents);\n        if (!navMesh)\n            return std::optional<osg::Vec3f>();\n        const auto settings = getSettings();\n        const auto result = DetourNavigator::findRandomPointAroundCircle(navMesh->lockConst()->getImpl(),\n            toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, start),\n            toNavMeshCoordinates(settings, maxRadius), includeFlags, settings);\n        if (!result)\n            return std::optional<osg::Vec3f>();\n        return std::optional<osg::Vec3f>(fromNavMeshCoordinates(settings, *result));\n    }\n\n    std::optional<osg::Vec3f> Navigator::raycast(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start,\n        const osg::Vec3f& end, const Flags includeFlags) const\n    {\n        const auto navMesh = getNavMesh(agentHalfExtents);\n        if (navMesh == nullptr)\n            return {};\n        const auto settings = getSettings();\n        const auto result = DetourNavigator::raycast(navMesh->lockConst()->getImpl(),\n            toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, start),\n            toNavMeshCoordinates(settings, end), includeFlags, settings);\n        if (!result)\n            return {};\n        return fromNavMeshCoordinates(settings, *result);\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/navigator.hpp",
    "content": "﻿#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATOR_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATOR_H\n\n#include \"findsmoothpath.hpp\"\n#include \"flags.hpp\"\n#include \"settings.hpp\"\n#include \"objectid.hpp\"\n#include \"navmeshcacheitem.hpp\"\n#include \"recastmeshtiles.hpp\"\n#include \"waitconditiontype.hpp\"\n\n#include <components/resource/bulletshape.hpp>\n\nnamespace ESM\n{\n    struct Cell;\n    struct Pathgrid;\n}\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace DetourNavigator\n{\n    struct ObjectShapes\n    {\n        osg::ref_ptr<const Resource::BulletShapeInstance> mShapeInstance;\n\n        ObjectShapes(const osg::ref_ptr<const Resource::BulletShapeInstance>& shapeInstance)\n            : mShapeInstance(shapeInstance)\n        {}\n    };\n\n    struct DoorShapes : ObjectShapes\n    {\n        osg::Vec3f mConnectionStart;\n        osg::Vec3f mConnectionEnd;\n\n        DoorShapes(const osg::ref_ptr<const Resource::BulletShapeInstance>& shapeInstance,\n                   const osg::Vec3f& connectionStart,const osg::Vec3f& connectionEnd)\n            : ObjectShapes(shapeInstance)\n            , mConnectionStart(connectionStart)\n            , mConnectionEnd(connectionEnd)\n        {}\n    };\n\n    /**\n     * @brief Top level interface of detournavigator component. Navigator allows to build a scene with navmesh and find\n     * a path for an agent there. Scene contains agents, geometry objects and water. Agent are distinguished only by\n     * half extents. Each object has unique identifier and could be added, updated or removed. Water could be added once\n     * for each world cell at given level of height. Navmesh builds asynchronously in separate threads. To start build\n     * navmesh call update method.\n     */\n    struct Navigator\n    {\n        virtual ~Navigator() = default;\n\n        /**\n         * @brief addAgent should be called for each agent even if all of them has same half extents.\n         * @param agentHalfExtents allows to setup bounding cylinder for each agent, for each different half extents\n         * there is different navmesh.\n         */\n        virtual void addAgent(const osg::Vec3f& agentHalfExtents) = 0;\n\n        /**\n         * @brief removeAgent should be called for each agent even if all of them has same half extents\n         * @param agentHalfExtents allows determine which agent to remove\n         */\n        virtual void removeAgent(const osg::Vec3f& agentHalfExtents) = 0;\n\n        /**\n         * @brief addObject is used to add object represented by single btHeightfieldTerrainShape and btTransform.\n         * @param id is used to distinguish different objects.\n         * @param holder shape wrapper to keep shape lifetime after object is removed.\n         * @param shape must be wrapped by holder.\n         * @param transform allows to setup object geometry according to its world state.\n         * @return true if object is added, false if there is already object with given id.\n         */\n        virtual bool addObject(const ObjectId id, const osg::ref_ptr<const osg::Object>& holder,\n                               const btHeightfieldTerrainShape& shape, const btTransform& transform) = 0;\n\n        /**\n         * @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes\n         * @param id is used to distinguish different objects\n         * @param shape members must live until object is updated by another shape removed from Navigator\n         * @param transform allows to setup objects geometry according to its world state\n         * @return true if object is added, false if there is already object with given id\n         */\n        virtual bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) = 0;\n\n        /**\n         * @brief addObject is used to add doors.\n         * @param id is used to distinguish different objects.\n         * @param shape members must live until object is updated by another shape or removed from Navigator.\n         * @param transform allows to setup objects geometry according to its world state.\n         * @return true if object is added, false if there is already object with given id.\n         */\n        virtual bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0;\n\n        /**\n         * @brief updateObject replace object geometry by given data.\n         * @param id is used to find object.\n         * @param shape members must live until object is updated by another shape removed from Navigator.\n         * @param transform allows to setup objects geometry according to its world state.\n         * @return true if object is updated, false if there is no object with given id.\n         */\n        virtual bool updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) = 0;\n\n        /**\n         * @brief updateObject replace object geometry by given data.\n         * @param id is used to find object.\n         * @param shape members must live until object is updated by another shape removed from Navigator.\n         * @param transform allows to setup objects geometry according to its world state.\n         * @return true if object is updated, false if there is no object with given id.\n         */\n        virtual bool updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0;\n\n        /**\n         * @brief removeObject to make it no more available at the scene.\n         * @param id is used to find object.\n         * @return true if object is removed, false if there is no object with given id.\n         */\n        virtual bool removeObject(const ObjectId id) = 0;\n\n        /**\n         * @brief addWater is used to set water level at given world cell.\n         * @param cellPosition allows to distinguish cells if there is many in current world.\n         * @param cellSize set cell borders. std::numeric_limits<int>::max() disables cell borders.\n         * @param level set z coordinate of water surface at the scene.\n         * @param transform set global shift of cell center.\n         * @return true if there was no water at given cell if cellSize != std::numeric_limits<int>::max() or there is\n         * at least single object is added to the scene, false if there is already water for given cell or there is no\n         * any other objects.\n         */\n        virtual bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level,\n            const btTransform& transform) = 0;\n\n        /**\n         * @brief removeWater to make it no more available at the scene.\n         * @param cellPosition allows to find cell.\n         * @return true if there was water at given cell.\n         */\n        virtual bool removeWater(const osg::Vec2i& cellPosition) = 0;\n\n        virtual void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) = 0;\n\n        virtual void removePathgrid(const ESM::Pathgrid& pathgrid) = 0;\n\n        /**\n         * @brief update starts background navmesh update using current scene state.\n         * @param playerPosition setup initial point to order build tiles of navmesh.\n         */\n        virtual void update(const osg::Vec3f& playerPosition) = 0;\n\n        /**\n         * @brief updatePlayerPosition starts background navmesh update using current scene state only when player position has been changed.\n         * @param playerPosition setup initial point to order build tiles of navmesh.\n         */\n        virtual void updatePlayerPosition(const osg::Vec3f& playerPosition) = 0;\n\n        /**\n         * @brief disable navigator updates\n         */\n        virtual void setUpdatesEnabled(bool enabled) = 0;\n\n        /**\n         * @brief wait locks thread until tiles are updated from last update call based on passed condition type.\n         * @param waitConditionType defines when waiting will stop\n         */\n        virtual void wait(Loading::Listener& listener, WaitConditionType waitConditionType) = 0;\n\n        /**\n         * @brief findPath fills output iterator with points of scene surfaces to be used for actor to walk through.\n         * @param agentHalfExtents allows to find navmesh for given actor.\n         * @param start path from given point.\n         * @param end path at given point.\n         * @param includeFlags setup allowed surfaces for actor to walk.\n         * @param out the beginning of the destination range.\n         * @return Output iterator to the element in the destination range, one past the last element of found path.\n         * Equal to out if no path is found.\n         */\n        template <class OutputIterator>\n        Status findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start,\n            const osg::Vec3f& end, const Flags includeFlags, const DetourNavigator::AreaCosts& areaCosts,\n            OutputIterator& out) const\n        {\n            static_assert(\n                std::is_same<\n                    typename std::iterator_traits<OutputIterator>::iterator_category,\n                    std::output_iterator_tag\n                >::value,\n                \"out is not an OutputIterator\"\n            );\n            const auto navMesh = getNavMesh(agentHalfExtents);\n            if (!navMesh)\n                return Status::NavMeshNotFound;\n            const auto settings = getSettings();\n            return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents),\n                toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start),\n                toNavMeshCoordinates(settings, end), includeFlags, areaCosts, settings, out);\n        }\n\n        /**\n         * @brief getNavMesh returns navmesh for specific agent half extents\n         * @return navmesh\n         */\n        virtual SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const = 0;\n\n        /**\n         * @brief getNavMeshes returns all current navmeshes\n         * @return map of agent half extents to navmesh\n         */\n        virtual std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const = 0;\n\n        virtual const Settings& getSettings() const = 0;\n\n        virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;\n\n        /**\n         * @brief findRandomPointAroundCircle returns random location on navmesh within the reach of specified location.\n         * @param agentHalfExtents allows to find navmesh for given actor.\n         * @param start path from given point.\n         * @param maxRadius limit maximum distance from start.\n         * @param includeFlags setup allowed surfaces for actor to walk.\n         * @return not empty optional with position if point is found and empty optional if point is not found.\n         */\n        std::optional<osg::Vec3f> findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents,\n            const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const;\n\n        /**\n         * @brief raycast finds farest navmesh point from start on a line from start to end that has path from start.\n         * @param agentHalfExtents allows to find navmesh for given actor.\n         * @param start of the line\n         * @param end of the line\n         * @param includeFlags setup allowed surfaces for actor to walk.\n         * @return not empty optional with position if point is found and empty optional if point is not found.\n         */\n        std::optional<osg::Vec3f> raycast(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start,\n            const osg::Vec3f& end, const Flags includeFlags) const;\n\n        virtual RecastMeshTiles getRecastMeshTiles() = 0;\n\n        virtual float getMaxNavmeshAreaRealRadius() const = 0;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/navigatorimpl.cpp",
    "content": "#include \"navigatorimpl.hpp\"\n#include \"debug.hpp\"\n#include \"settingsutils.hpp\"\n\n#include <components/esm/loadpgrd.hpp>\n#include <components/misc/coordinateconverter.hpp>\n\nnamespace DetourNavigator\n{\n    NavigatorImpl::NavigatorImpl(const Settings& settings)\n        : mSettings(settings)\n        , mNavMeshManager(mSettings)\n        , mUpdatesEnabled(true)\n    {\n    }\n\n    void NavigatorImpl::addAgent(const osg::Vec3f& agentHalfExtents)\n    {\n        if(agentHalfExtents.length2() <= 0)\n            return;\n        ++mAgents[agentHalfExtents];\n        mNavMeshManager.addAgent(agentHalfExtents);\n    }\n\n    void NavigatorImpl::removeAgent(const osg::Vec3f& agentHalfExtents)\n    {\n        const auto it = mAgents.find(agentHalfExtents);\n        if (it == mAgents.end())\n            return;\n        if (it->second > 0)\n            --it->second;\n    }\n\n    bool NavigatorImpl::addObject(const ObjectId id, const osg::ref_ptr<const osg::Object>& holder,\n        const btHeightfieldTerrainShape& shape, const btTransform& transform)\n    {\n        const CollisionShape collisionShape {holder, shape};\n        return mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground);\n    }\n\n    bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)\n    {\n        const CollisionShape collisionShape {shapes.mShapeInstance, *shapes.mShapeInstance->getCollisionShape()};\n        bool result = mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground);\n        if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->getAvoidCollisionShape())\n        {\n            const ObjectId avoidId(avoidShape);\n            const CollisionShape collisionShape {shapes.mShapeInstance, *avoidShape};\n            if (mNavMeshManager.addObject(avoidId, collisionShape, transform, AreaType_null))\n            {\n                updateAvoidShapeId(id, avoidId);\n                result = true;\n            }\n        }\n        return result;\n    }\n\n    bool NavigatorImpl::addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform)\n    {\n        if (addObject(id, static_cast<const ObjectShapes&>(shapes), transform))\n        {\n            const osg::Vec3f start = toNavMeshCoordinates(mSettings, shapes.mConnectionStart);\n            const osg::Vec3f end = toNavMeshCoordinates(mSettings, shapes.mConnectionEnd);\n            mNavMeshManager.addOffMeshConnection(id, start, end, AreaType_door);\n            mNavMeshManager.addOffMeshConnection(id, end, start, AreaType_door);\n            return true;\n        }\n        return false;\n    }\n\n    bool NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)\n    {\n        const CollisionShape collisionShape {shapes.mShapeInstance, *shapes.mShapeInstance->getCollisionShape()};\n        bool result = mNavMeshManager.updateObject(id, collisionShape, transform, AreaType_ground);\n        if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->getAvoidCollisionShape())\n        {\n            const ObjectId avoidId(avoidShape);\n            const CollisionShape collisionShape {shapes.mShapeInstance, *avoidShape};\n            if (mNavMeshManager.updateObject(avoidId, collisionShape, transform, AreaType_null))\n            {\n                updateAvoidShapeId(id, avoidId);\n                result = true;\n            }\n        }\n        return result;\n    }\n\n    bool NavigatorImpl::updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform)\n    {\n        return updateObject(id, static_cast<const ObjectShapes&>(shapes), transform);\n    }\n\n    bool NavigatorImpl::removeObject(const ObjectId id)\n    {\n        bool result = mNavMeshManager.removeObject(id);\n        const auto avoid = mAvoidIds.find(id);\n        if (avoid != mAvoidIds.end())\n            result = mNavMeshManager.removeObject(avoid->second) || result;\n        const auto water = mWaterIds.find(id);\n        if (water != mWaterIds.end())\n            result = mNavMeshManager.removeObject(water->second) || result;\n        mNavMeshManager.removeOffMeshConnections(id);\n        return result;\n    }\n\n    bool NavigatorImpl::addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level,\n        const btTransform& transform)\n    {\n        return mNavMeshManager.addWater(cellPosition, cellSize,\n            btTransform(transform.getBasis(), btVector3(transform.getOrigin().x(), transform.getOrigin().y(), level)));\n    }\n\n    bool NavigatorImpl::removeWater(const osg::Vec2i& cellPosition)\n    {\n        return mNavMeshManager.removeWater(cellPosition);\n    }\n\n    void NavigatorImpl::addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid)\n    {\n        Misc::CoordinateConverter converter(&cell);\n        for (auto edge : pathgrid.mEdges)\n        {\n            const auto src = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV0]));\n            const auto dst = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV1]));\n            mNavMeshManager.addOffMeshConnection(\n                ObjectId(&pathgrid),\n                toNavMeshCoordinates(mSettings, src),\n                toNavMeshCoordinates(mSettings, dst),\n                AreaType_pathgrid\n            );\n        }\n    }\n\n    void NavigatorImpl::removePathgrid(const ESM::Pathgrid& pathgrid)\n    {\n        mNavMeshManager.removeOffMeshConnections(ObjectId(&pathgrid));\n    }\n\n    void NavigatorImpl::update(const osg::Vec3f& playerPosition)\n    {\n        if (!mUpdatesEnabled)\n            return;\n        removeUnusedNavMeshes();\n        for (const auto& v : mAgents)\n            mNavMeshManager.update(playerPosition, v.first);\n    }\n\n    void NavigatorImpl::updatePlayerPosition(const osg::Vec3f& playerPosition)\n    {\n        const TilePosition tilePosition = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition));\n        if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition)\n            return;\n        update(playerPosition);\n        mLastPlayerPosition = tilePosition;\n    }\n\n    void NavigatorImpl::setUpdatesEnabled(bool enabled)\n    {\n        mUpdatesEnabled = enabled;\n    }\n\n    void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType)\n    {\n        mNavMeshManager.wait(listener, waitConditionType);\n    }\n\n    SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const\n    {\n        return mNavMeshManager.getNavMesh(agentHalfExtents);\n    }\n\n    std::map<osg::Vec3f, SharedNavMeshCacheItem> NavigatorImpl::getNavMeshes() const\n    {\n        return mNavMeshManager.getNavMeshes();\n    }\n\n    const Settings& NavigatorImpl::getSettings() const\n    {\n        return mSettings;\n    }\n\n    void NavigatorImpl::reportStats(unsigned int frameNumber, osg::Stats& stats) const\n    {\n        mNavMeshManager.reportStats(frameNumber, stats);\n    }\n\n    RecastMeshTiles NavigatorImpl::getRecastMeshTiles()\n    {\n        return mNavMeshManager.getRecastMeshTiles();\n    }\n\n    void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId)\n    {\n        updateId(id, avoidId, mWaterIds);\n    }\n\n    void NavigatorImpl::updateWaterShapeId(const ObjectId id, const ObjectId waterId)\n    {\n        updateId(id, waterId, mWaterIds);\n    }\n\n    void NavigatorImpl::updateId(const ObjectId id, const ObjectId updateId, std::unordered_map<ObjectId, ObjectId>& ids)\n    {\n        auto inserted = ids.insert(std::make_pair(id, updateId));\n        if (!inserted.second)\n        {\n            mNavMeshManager.removeObject(inserted.first->second);\n            inserted.first->second = updateId;\n        }\n    }\n\n    void NavigatorImpl::removeUnusedNavMeshes()\n    {\n        for (auto it = mAgents.begin(); it != mAgents.end();)\n        {\n            if (it->second == 0 && mNavMeshManager.reset(it->first))\n                it = mAgents.erase(it);\n            else\n                ++it;\n        }\n    }\n\n    float NavigatorImpl::getMaxNavmeshAreaRealRadius() const\n    {\n        const auto& settings = getSettings();\n        return getRealTileSize(settings) * getMaxNavmeshAreaRadius(settings);\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/navigatorimpl.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATORIMPL_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATORIMPL_H\n\n#include \"navigator.hpp\"\n#include \"navmeshmanager.hpp\"\n\n#include <set>\n\nnamespace DetourNavigator\n{\n    class NavigatorImpl final : public Navigator\n    {\n    public:\n        /**\n         * @brief Navigator constructor initializes all internal data. Constructed object is ready to build a scene.\n         * @param settings allows to customize navigator work. Constructor is only place to set navigator settings.\n         */\n        explicit NavigatorImpl(const Settings& settings);\n\n        void addAgent(const osg::Vec3f& agentHalfExtents) override;\n\n        void removeAgent(const osg::Vec3f& agentHalfExtents) override;\n\n        bool addObject(const ObjectId id, const osg::ref_ptr<const osg::Object>& holder,\n            const btHeightfieldTerrainShape& shape, const btTransform& transform) override;\n\n        bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;\n\n        bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;\n\n        bool updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;\n\n        bool updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;\n\n        bool removeObject(const ObjectId id) override;\n\n        bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level,\n            const btTransform& transform) override;\n\n        bool removeWater(const osg::Vec2i& cellPosition) override;\n\n        void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) override;\n\n        void removePathgrid(const ESM::Pathgrid& pathgrid) override;\n\n        void update(const osg::Vec3f& playerPosition) override;\n\n        void updatePlayerPosition(const osg::Vec3f& playerPosition) override;\n\n        void setUpdatesEnabled(bool enabled) override;\n\n        void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override;\n\n        SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override;\n\n        std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const override;\n\n        const Settings& getSettings() const override;\n\n        void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;\n\n        RecastMeshTiles getRecastMeshTiles() override;\n\n        float getMaxNavmeshAreaRealRadius() const override;\n\n    private:\n        Settings mSettings;\n        NavMeshManager mNavMeshManager;\n        bool mUpdatesEnabled;\n        std::optional<TilePosition> mLastPlayerPosition;\n        std::map<osg::Vec3f, std::size_t> mAgents;\n        std::unordered_map<ObjectId, ObjectId> mAvoidIds;\n        std::unordered_map<ObjectId, ObjectId> mWaterIds;\n\n        void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId);\n        void updateWaterShapeId(const ObjectId id, const ObjectId waterId);\n        void updateId(const ObjectId id, const ObjectId waterId, std::unordered_map<ObjectId, ObjectId>& ids);\n        void removeUnusedNavMeshes();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/navigatorstub.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATORSTUB_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATORSTUB_H\n\n#include \"navigator.hpp\"\n\nnamespace Loading\n{\n    class Listener;\n}\n\nnamespace DetourNavigator\n{\n    class NavigatorStub final : public Navigator\n    {\n    public:\n        NavigatorStub() = default;\n\n        void addAgent(const osg::Vec3f& /*agentHalfExtents*/) override {}\n\n        void removeAgent(const osg::Vec3f& /*agentHalfExtents*/) override {}\n\n        bool addObject(const ObjectId /*id*/, const osg::ref_ptr<const osg::Object>& /*holder*/,\n            const btHeightfieldTerrainShape& /*shape*/, const btTransform& /*transform*/) override\n        {\n            return false;\n        }\n\n        bool addObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override\n        {\n            return false;\n        }\n\n        bool addObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override\n        {\n            return false;\n        }\n\n        bool updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override\n        {\n            return false;\n        }\n\n        bool updateObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override\n        {\n            return false;\n        }\n\n        bool removeObject(const ObjectId /*id*/) override\n        {\n            return false;\n        }\n\n        bool addWater(const osg::Vec2i& /*cellPosition*/, const int /*cellSize*/, const btScalar /*level*/,\n            const btTransform& /*transform*/) override\n        {\n            return false;\n        }\n\n        bool removeWater(const osg::Vec2i& /*cellPosition*/) override\n        {\n            return false;\n        }\n\n        void addPathgrid(const ESM::Cell& /*cell*/, const ESM::Pathgrid& /*pathgrid*/) override {}\n\n        void removePathgrid(const ESM::Pathgrid& /*pathgrid*/) override {}\n\n        void update(const osg::Vec3f& /*playerPosition*/) override {}\n\n        void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {};\n\n        void setUpdatesEnabled(bool /*enabled*/) override {}\n\n        void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {}\n\n        SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override\n        {\n            return mEmptyNavMeshCacheItem;\n        }\n\n        std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const override\n        {\n            return std::map<osg::Vec3f, SharedNavMeshCacheItem>();\n        }\n\n        const Settings& getSettings() const override\n        {\n            return mDefaultSettings;\n        }\n\n        void reportStats(unsigned int /*frameNumber*/, osg::Stats& /*stats*/) const override {}\n\n        RecastMeshTiles getRecastMeshTiles() override\n        {\n            return {};\n        }\n\n        float getMaxNavmeshAreaRealRadius() const override\n        {\n            return std::numeric_limits<float>::max();\n        }\n\n    private:\n        Settings mDefaultSettings {};\n        SharedNavMeshCacheItem mEmptyNavMeshCacheItem;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/navmeshcacheitem.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHCACHEITEM_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHCACHEITEM_H\n\n#include \"sharednavmesh.hpp\"\n#include \"tileposition.hpp\"\n#include \"navmeshtilescache.hpp\"\n#include \"dtstatus.hpp\"\n#include \"navmeshtileview.hpp\"\n\n#include <components/misc/guarded.hpp>\n\n#include <DetourNavMesh.h>\n\n#include <map>\n\nnamespace DetourNavigator\n{\n    enum class UpdateNavMeshStatus : unsigned\n    {\n        ignored = 0,\n        removed = 1 << 0,\n        added = 1 << 1,\n        replaced = removed | added,\n        failed = 1 << 2,\n        lost = removed | failed,\n        cached = 1 << 3,\n        unchanged = replaced | cached,\n        restored = added | cached,\n    };\n\n    inline bool isSuccess(UpdateNavMeshStatus value)\n    {\n        return (static_cast<unsigned>(value) & static_cast<unsigned>(UpdateNavMeshStatus::failed)) == 0;\n    }\n\n    class UpdateNavMeshStatusBuilder\n    {\n    public:\n        UpdateNavMeshStatusBuilder() = default;\n\n        explicit UpdateNavMeshStatusBuilder(UpdateNavMeshStatus value)\n            : mResult(value) {}\n\n        UpdateNavMeshStatusBuilder removed(bool value)\n        {\n            if (value)\n                set(UpdateNavMeshStatus::removed);\n            else\n                unset(UpdateNavMeshStatus::removed);\n            return *this;\n        }\n\n        UpdateNavMeshStatusBuilder added(bool value)\n        {\n            if (value)\n                set(UpdateNavMeshStatus::added);\n            else\n                unset(UpdateNavMeshStatus::added);\n            return *this;\n        }\n\n        UpdateNavMeshStatusBuilder failed(bool value)\n        {\n            if (value)\n                set(UpdateNavMeshStatus::failed);\n            else\n                unset(UpdateNavMeshStatus::failed);\n            return *this;\n        }\n\n        UpdateNavMeshStatusBuilder cached(bool value)\n        {\n            if (value)\n                set(UpdateNavMeshStatus::cached);\n            else\n                unset(UpdateNavMeshStatus::cached);\n            return *this;\n        }\n\n        UpdateNavMeshStatus getResult() const\n        {\n            return mResult;\n        }\n\n    private:\n        UpdateNavMeshStatus mResult = UpdateNavMeshStatus::ignored;\n\n        void set(UpdateNavMeshStatus value)\n        {\n            mResult = static_cast<UpdateNavMeshStatus>(static_cast<unsigned>(mResult) | static_cast<unsigned>(value));\n        }\n\n        void unset(UpdateNavMeshStatus value)\n        {\n            mResult = static_cast<UpdateNavMeshStatus>(static_cast<unsigned>(mResult) & ~static_cast<unsigned>(value));\n        }\n    };\n\n    inline unsigned char* getRawData(NavMeshData& navMeshData)\n    {\n        return navMeshData.mValue.get();\n    }\n\n    inline unsigned char* getRawData(NavMeshTilesCache::Value& cachedNavMeshData)\n    {\n        return cachedNavMeshData.get().mValue;\n    }\n\n    inline int getSize(const NavMeshData& navMeshData)\n    {\n        return navMeshData.mSize;\n    }\n\n    inline int getSize(const NavMeshTilesCache::Value& cachedNavMeshData)\n    {\n        return cachedNavMeshData.get().mSize;\n    }\n\n    class NavMeshCacheItem\n    {\n    public:\n        NavMeshCacheItem(const NavMeshPtr& impl, std::size_t generation)\n            : mImpl(impl), mGeneration(generation), mNavMeshRevision(0)\n        {\n        }\n\n        const dtNavMesh& getImpl() const\n        {\n            return *mImpl;\n        }\n\n        std::size_t getGeneration() const\n        {\n            return mGeneration;\n        }\n\n        std::size_t getNavMeshRevision() const\n        {\n            return mNavMeshRevision;\n        }\n\n        template <class T>\n        UpdateNavMeshStatus updateTile(const TilePosition& position, T&& navMeshData)\n        {\n            const dtMeshTile* currentTile = getTile(position);\n            if (currentTile != nullptr\n                && asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(getRawData(navMeshData)))\n            {\n                return UpdateNavMeshStatus::ignored;\n            }\n            const auto removed = removeTileImpl(position);\n            const auto addStatus = addTileImpl(getRawData(navMeshData), getSize(navMeshData));\n            if (dtStatusSucceed(addStatus))\n            {\n                setUsedTile(position, std::forward<T>(navMeshData));\n                return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult();\n            }\n            else\n            {\n                if (removed)\n                    removeUsedTile(position);\n                return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult();\n            }\n        }\n\n        UpdateNavMeshStatus removeTile(const TilePosition& position)\n        {\n            const auto removed = removeTileImpl(position);\n            if (removed)\n                removeUsedTile(position);\n            return UpdateNavMeshStatusBuilder().removed(removed).getResult();\n        }\n\n    private:\n        NavMeshPtr mImpl;\n        std::size_t mGeneration;\n        std::size_t mNavMeshRevision;\n        std::map<TilePosition, std::pair<NavMeshTilesCache::Value, NavMeshData>> mUsedTiles;\n\n        void setUsedTile(const TilePosition& tilePosition, NavMeshTilesCache::Value value)\n        {\n            mUsedTiles[tilePosition] = std::make_pair(std::move(value), NavMeshData());\n            ++mNavMeshRevision;\n        }\n\n        void setUsedTile(const TilePosition& tilePosition, NavMeshData value)\n        {\n            mUsedTiles[tilePosition] = std::make_pair(NavMeshTilesCache::Value(), std::move(value));\n            ++mNavMeshRevision;\n        }\n\n        void removeUsedTile(const TilePosition& tilePosition)\n        {\n            mUsedTiles.erase(tilePosition);\n            ++mNavMeshRevision;\n        }\n\n        dtStatus addTileImpl(unsigned char* data, int size)\n        {\n            const int doNotTransferOwnership = 0;\n            const dtTileRef lastRef = 0;\n            dtTileRef* const result = nullptr;\n            return mImpl->addTile(data, size, doNotTransferOwnership, lastRef, result);\n        }\n\n        bool removeTileImpl(const TilePosition& position)\n        {\n            const int layer = 0;\n            const auto tileRef = mImpl->getTileRefAt(position.x(), position.y(), layer);\n            if (tileRef == 0)\n                return false;\n            unsigned char** const data = nullptr;\n            int* const dataSize = nullptr;\n            return dtStatusSucceed(mImpl->removeTile(tileRef, data, dataSize));\n        }\n\n        const dtMeshTile* getTile(const TilePosition& position) const\n        {\n            const int layer = 0;\n            return mImpl->getTileAt(position.x(), position.y(), layer);\n        }\n    };\n\n    using GuardedNavMeshCacheItem = Misc::ScopeGuarded<NavMeshCacheItem>;\n    using SharedNavMeshCacheItem = std::shared_ptr<GuardedNavMeshCacheItem>;\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/navmeshdata.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHDATA_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHDATA_H\n\n#include <DetourAlloc.h>\n\n#include <algorithm>\n#include <memory>\n\nnamespace DetourNavigator\n{\n    struct NavMeshDataValueDeleter\n    {\n        void operator ()(unsigned char* value) const\n        {\n            dtFree(value);\n        }\n    };\n\n    using NavMeshDataValue = std::unique_ptr<unsigned char, NavMeshDataValueDeleter>;\n\n    struct NavMeshData\n    {\n        NavMeshDataValue mValue;\n        int mSize;\n\n        NavMeshData() = default;\n\n        NavMeshData(unsigned char* value, int size)\n            : mValue(value)\n            , mSize(size)\n        {}\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/navmeshmanager.cpp",
    "content": "#include \"navmeshmanager.hpp\"\n#include \"debug.hpp\"\n#include \"exceptions.hpp\"\n#include \"gettilespositions.hpp\"\n#include \"makenavmesh.hpp\"\n#include \"navmeshcacheitem.hpp\"\n#include \"settings.hpp\"\n#include \"waitconditiontype.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include <DetourNavMesh.h>\n\n#include <iterator>\n\nnamespace\n{\n    using DetourNavigator::ChangeType;\n\n    ChangeType addChangeType(const ChangeType current, const ChangeType add)\n    {\n        return current == add ? current : ChangeType::mixed;\n    }\n\n    /// Safely reset shared_ptr with definite underlying object destrutor call.\n    /// Assuming there is another thread holding copy of this shared_ptr or weak_ptr to this shared_ptr.\n    template <class T>\n    bool resetIfUnique(std::shared_ptr<T>& ptr)\n    {\n        const std::weak_ptr<T> weak(ptr);\n        ptr.reset();\n        if (auto shared = weak.lock())\n        {\n            ptr = std::move(shared);\n            return false;\n        }\n        return true;\n    }\n}\n\nnamespace DetourNavigator\n{\n    NavMeshManager::NavMeshManager(const Settings& settings)\n        : mSettings(settings)\n        , mRecastMeshManager(settings)\n        , mOffMeshConnectionsManager(settings)\n        , mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager)\n    {}\n\n    bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                                   const AreaType areaType)\n    {\n        const btCollisionShape& collisionShape = shape.getShape();\n        if (!mRecastMeshManager.addObject(id, shape, transform, areaType))\n            return false;\n        addChangedTiles(collisionShape, transform, ChangeType::add);\n        return true;\n    }\n\n    bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                                      const AreaType areaType)\n    {\n        return mRecastMeshManager.updateObject(id, shape, transform, areaType,\n            [&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::update); });\n    }\n\n    bool NavMeshManager::removeObject(const ObjectId id)\n    {\n        const auto object = mRecastMeshManager.removeObject(id);\n        if (!object)\n            return false;\n        addChangedTiles(object->mShape, object->mTransform, ChangeType::remove);\n        return true;\n    }\n\n    bool NavMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform)\n    {\n        if (!mRecastMeshManager.addWater(cellPosition, cellSize, transform))\n            return false;\n        addChangedTiles(cellSize, transform, ChangeType::add);\n        return true;\n    }\n\n    bool NavMeshManager::removeWater(const osg::Vec2i& cellPosition)\n    {\n        const auto water = mRecastMeshManager.removeWater(cellPosition);\n        if (!water)\n            return false;\n        addChangedTiles(water->mCellSize, water->mTransform, ChangeType::remove);\n        return true;\n    }\n\n    void NavMeshManager::addAgent(const osg::Vec3f& agentHalfExtents)\n    {\n        auto cached = mCache.find(agentHalfExtents);\n        if (cached != mCache.end())\n            return;\n        mCache.insert(std::make_pair(agentHalfExtents,\n            std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter)));\n        Log(Debug::Debug) << \"cache add for agent=\" << agentHalfExtents;\n    }\n\n    bool NavMeshManager::reset(const osg::Vec3f& agentHalfExtents)\n    {\n        const auto it = mCache.find(agentHalfExtents);\n        if (it == mCache.end())\n            return true;\n        if (!resetIfUnique(it->second))\n            return false;\n        mCache.erase(agentHalfExtents);\n        mChangedTiles.erase(agentHalfExtents);\n        mPlayerTile.erase(agentHalfExtents);\n        mLastRecastMeshManagerRevision.erase(agentHalfExtents);\n        return true;\n    }\n\n    void NavMeshManager::addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end, const AreaType areaType)\n    {\n        mOffMeshConnectionsManager.add(id, OffMeshConnection {start, end, areaType});\n\n        const auto startTilePosition = getTilePosition(mSettings, start);\n        const auto endTilePosition = getTilePosition(mSettings, end);\n\n        addChangedTile(startTilePosition, ChangeType::add);\n\n        if (startTilePosition != endTilePosition)\n            addChangedTile(endTilePosition, ChangeType::add);\n    }\n\n    void NavMeshManager::removeOffMeshConnections(const ObjectId id)\n    {\n        const auto changedTiles = mOffMeshConnectionsManager.remove(id);\n        for (const auto& tile : changedTiles)\n            addChangedTile(tile, ChangeType::update);\n    }\n\n    void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents)\n    {\n        const auto playerTile = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition));\n        auto& lastRevision = mLastRecastMeshManagerRevision[agentHalfExtents];\n        auto lastPlayerTile = mPlayerTile.find(agentHalfExtents);\n        if (lastRevision == mRecastMeshManager.getRevision() && lastPlayerTile != mPlayerTile.end()\n                && lastPlayerTile->second == playerTile)\n            return;\n        lastRevision = mRecastMeshManager.getRevision();\n        if (lastPlayerTile == mPlayerTile.end())\n            lastPlayerTile = mPlayerTile.insert(std::make_pair(agentHalfExtents, playerTile)).first;\n        else\n            lastPlayerTile->second = playerTile;\n        std::map<TilePosition, ChangeType> tilesToPost;\n        const auto cached = getCached(agentHalfExtents);\n        if (!cached)\n        {\n            std::ostringstream stream;\n            stream << \"Agent with half extents is not found: \" << agentHalfExtents;\n            throw InvalidArgument(stream.str());\n        }\n        const auto changedTiles = mChangedTiles.find(agentHalfExtents);\n        {\n            const auto locked = cached->lockConst();\n            const auto& navMesh = locked->getImpl();\n            if (changedTiles != mChangedTiles.end())\n            {\n                for (const auto& tile : changedTiles->second)\n                    if (navMesh.getTileAt(tile.first.x(), tile.first.y(), 0))\n                    {\n                        auto tileToPost = tilesToPost.find(tile.first);\n                        if (tileToPost == tilesToPost.end())\n                            tilesToPost.insert(tile);\n                        else\n                            tileToPost->second = addChangeType(tileToPost->second, tile.second);\n                    }\n            }\n            const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles);\n            mRecastMeshManager.forEachTile([&] (const TilePosition& tile, CachedRecastMeshManager& recastMeshManager)\n            {\n                if (tilesToPost.count(tile))\n                    return;\n                const auto shouldAdd = shouldAddTile(tile, playerTile, maxTiles);\n                const auto presentInNavMesh = bool(navMesh.getTileAt(tile.x(), tile.y(), 0));\n                if (shouldAdd && !presentInNavMesh)\n                    tilesToPost.insert(std::make_pair(tile, ChangeType::add));\n                else if (!shouldAdd && presentInNavMesh)\n                    tilesToPost.insert(std::make_pair(tile, ChangeType::mixed));\n                else\n                    recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0});\n            });\n        }\n        mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost);\n        if (changedTiles != mChangedTiles.end())\n            changedTiles->second.clear();\n        Log(Debug::Debug) << \"Cache update posted for agent=\" << agentHalfExtents <<\n            \" playerTile=\" << lastPlayerTile->second <<\n            \" recastMeshManagerRevision=\" << lastRevision;\n    }\n\n    void NavMeshManager::wait(Loading::Listener& listener, WaitConditionType waitConditionType)\n    {\n        mAsyncNavMeshUpdater.wait(listener, waitConditionType);\n    }\n\n    SharedNavMeshCacheItem NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const\n    {\n        return getCached(agentHalfExtents);\n    }\n\n    std::map<osg::Vec3f, SharedNavMeshCacheItem> NavMeshManager::getNavMeshes() const\n    {\n        return mCache;\n    }\n\n    void NavMeshManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const\n    {\n        mAsyncNavMeshUpdater.reportStats(frameNumber, stats);\n    }\n\n    RecastMeshTiles NavMeshManager::getRecastMeshTiles()\n    {\n        std::vector<TilePosition> tiles;\n        mRecastMeshManager.forEachTile(\n            [&tiles] (const TilePosition& tile, const CachedRecastMeshManager&) { tiles.push_back(tile); });\n        RecastMeshTiles result;\n        std::transform(tiles.begin(), tiles.end(), std::inserter(result, result.end()),\n            [this] (const TilePosition& tile) { return std::make_pair(tile, mRecastMeshManager.getMesh(tile)); });\n        return result;\n    }\n\n    void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,\n            const ChangeType changeType)\n    {\n        getTilesPositions(shape, transform, mSettings,\n            [&] (const TilePosition& v) { addChangedTile(v, changeType); });\n    }\n\n    void NavMeshManager::addChangedTiles(const int cellSize, const btTransform& transform,\n            const ChangeType changeType)\n    {\n        if (cellSize == std::numeric_limits<int>::max())\n            return;\n\n        getTilesPositions(cellSize, transform, mSettings,\n            [&] (const TilePosition& v) { addChangedTile(v, changeType); });\n    }\n\n    void NavMeshManager::addChangedTile(const TilePosition& tilePosition, const ChangeType changeType)\n    {\n        for (const auto& cached : mCache)\n        {\n            auto& tiles = mChangedTiles[cached.first];\n            auto tile = tiles.find(tilePosition);\n            if (tile == tiles.end())\n                tiles.insert(std::make_pair(tilePosition, changeType));\n            else\n                tile->second = addChangeType(tile->second, changeType);\n        }\n    }\n\n    SharedNavMeshCacheItem NavMeshManager::getCached(const osg::Vec3f& agentHalfExtents) const\n    {\n        const auto cached = mCache.find(agentHalfExtents);\n        if (cached != mCache.end())\n            return cached->second;\n        return SharedNavMeshCacheItem();\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/navmeshmanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHMANAGER_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHMANAGER_H\n\n#include \"asyncnavmeshupdater.hpp\"\n#include \"cachedrecastmeshmanager.hpp\"\n#include \"offmeshconnectionsmanager.hpp\"\n#include \"recastmeshtiles.hpp\"\n#include \"waitconditiontype.hpp\"\n\n#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>\n\n#include <osg/Vec3f>\n\n#include <map>\n#include <memory>\n\nclass dtNavMesh;\n\nnamespace DetourNavigator\n{\n    class NavMeshManager\n    {\n    public:\n        NavMeshManager(const Settings& settings);\n\n        bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                       const AreaType areaType);\n\n        bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                          const AreaType areaType);\n\n        bool removeObject(const ObjectId id);\n\n        void addAgent(const osg::Vec3f& agentHalfExtents);\n\n        bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform);\n\n        bool removeWater(const osg::Vec2i& cellPosition);\n\n        bool reset(const osg::Vec3f& agentHalfExtents);\n\n        void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end, const AreaType areaType);\n\n        void removeOffMeshConnections(const ObjectId id);\n\n        void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents);\n\n        void wait(Loading::Listener& listener, WaitConditionType waitConditionType);\n\n        SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const;\n\n        std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const;\n\n        void reportStats(unsigned int frameNumber, osg::Stats& stats) const;\n\n        RecastMeshTiles getRecastMeshTiles();\n\n    private:\n        const Settings& mSettings;\n        TileCachedRecastMeshManager mRecastMeshManager;\n        OffMeshConnectionsManager mOffMeshConnectionsManager;\n        AsyncNavMeshUpdater mAsyncNavMeshUpdater;\n        std::map<osg::Vec3f, SharedNavMeshCacheItem> mCache;\n        std::map<osg::Vec3f, std::map<TilePosition, ChangeType>> mChangedTiles;\n        std::size_t mGenerationCounter = 0;\n        std::map<osg::Vec3f, TilePosition> mPlayerTile;\n        std::map<osg::Vec3f, std::size_t> mLastRecastMeshManagerRevision;\n\n        void addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType);\n\n        void addChangedTiles(const int cellSize, const btTransform& transform, const ChangeType changeType);\n\n        void addChangedTile(const TilePosition& tilePosition, const ChangeType changeType);\n\n        SharedNavMeshCacheItem getCached(const osg::Vec3f& agentHalfExtents) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/navmeshtilescache.cpp",
    "content": "#include \"navmeshtilescache.hpp\"\n\n#include <osg/Stats>\n\n#include <cstring>\n\nnamespace DetourNavigator\n{\n    namespace\n    {\n        inline std::size_t getSize(const RecastMesh& recastMesh,\n                                   const std::vector<OffMeshConnection>& offMeshConnections)\n        {\n            const std::size_t indicesSize = recastMesh.getIndices().size() * sizeof(int);\n            const std::size_t verticesSize = recastMesh.getVertices().size() * sizeof(float);\n            const std::size_t areaTypesSize = recastMesh.getAreaTypes().size() * sizeof(AreaType);\n            const std::size_t waterSize = recastMesh.getWater().size() * sizeof(RecastMesh::Water);\n            const std::size_t offMeshConnectionsSize = offMeshConnections.size() * sizeof(OffMeshConnection);\n            return indicesSize + verticesSize + areaTypesSize + waterSize + offMeshConnectionsSize;\n        }\n    }\n\n    NavMeshTilesCache::NavMeshTilesCache(const std::size_t maxNavMeshDataSize)\n        : mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0),\n          mHitCount(0), mGetCount(0) {}\n\n    NavMeshTilesCache::Value NavMeshTilesCache::get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,\n        const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections)\n    {\n        const std::lock_guard<std::mutex> lock(mMutex);\n\n        ++mGetCount;\n\n        const auto tile = mValues.find(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyView(recastMesh, offMeshConnections)));\n        if (tile == mValues.end())\n            return Value();\n\n        acquireItemUnsafe(tile->second);\n\n        ++mHitCount;\n\n        return Value(*this, tile->second);\n    }\n\n    NavMeshTilesCache::Value NavMeshTilesCache::set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,\n        const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,\n        NavMeshData&& value)\n    {\n        const auto itemSize = static_cast<std::size_t>(value.mSize) + getSize(recastMesh, offMeshConnections);\n\n        const std::lock_guard<std::mutex> lock(mMutex);\n\n        if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize))\n            return Value();\n\n        while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize)\n            removeLeastRecentlyUsed();\n\n        NavMeshKey navMeshKey {\n            RecastMeshData {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()},\n            offMeshConnections\n        };\n\n        const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey), itemSize);\n        const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyRef(iterator->mNavMeshKey)), iterator);\n\n        if (!emplaced.second)\n        {\n            mFreeItems.erase(iterator);\n            acquireItemUnsafe(emplaced.first->second);\n            ++mGetCount;\n            ++mHitCount;\n            return Value(*this, emplaced.first->second);\n        }\n\n        iterator->mNavMeshData = std::move(value);\n        ++iterator->mUseCount;\n        mUsedNavMeshDataSize += itemSize;\n        mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator);\n\n        return Value(*this, iterator);\n    }\n\n    NavMeshTilesCache::Stats NavMeshTilesCache::getStats() const\n    {\n        Stats result;\n        {\n            const std::lock_guard<std::mutex> lock(mMutex);\n            result.mNavMeshCacheSize = mUsedNavMeshDataSize;\n            result.mUsedNavMeshTiles = mBusyItems.size();\n            result.mCachedNavMeshTiles = mFreeItems.size();\n            result.mHitCount = mHitCount;\n            result.mGetCount = mGetCount;\n        }\n        return result;\n    }\n\n    void NavMeshTilesCache::reportStats(unsigned int frameNumber, osg::Stats& out) const\n    {\n        const Stats stats = getStats();\n        out.setAttribute(frameNumber, \"NavMesh CacheSize\", stats.mNavMeshCacheSize);\n        out.setAttribute(frameNumber, \"NavMesh UsedTiles\", stats.mUsedNavMeshTiles);\n        out.setAttribute(frameNumber, \"NavMesh CachedTiles\", stats.mCachedNavMeshTiles);\n        out.setAttribute(frameNumber, \"NavMesh CacheHitRate\", static_cast<double>(stats.mHitCount) / stats.mGetCount * 100.0);\n    }\n\n    void NavMeshTilesCache::removeLeastRecentlyUsed()\n    {\n        const auto& item = mFreeItems.back();\n\n        const auto value = mValues.find(std::make_tuple(item.mAgentHalfExtents, item.mChangedTile, NavMeshKeyRef(item.mNavMeshKey)));\n        if (value == mValues.end())\n            return;\n\n        mUsedNavMeshDataSize -= item.mSize;\n        mFreeNavMeshDataSize -= item.mSize;\n\n        mValues.erase(value);\n        mFreeItems.pop_back();\n    }\n\n    void NavMeshTilesCache::acquireItemUnsafe(ItemIterator iterator)\n    {\n        if (++iterator->mUseCount > 1)\n            return;\n\n        mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator);\n        mFreeNavMeshDataSize -= iterator->mSize;\n    }\n\n    void NavMeshTilesCache::releaseItem(ItemIterator iterator)\n    {\n        if (--iterator->mUseCount > 0)\n            return;\n\n        const std::lock_guard<std::mutex> lock(mMutex);\n\n        mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator);\n        mFreeNavMeshDataSize += iterator->mSize;\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/navmeshtilescache.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H\n\n#include \"offmeshconnection.hpp\"\n#include \"navmeshdata.hpp\"\n#include \"recastmesh.hpp\"\n#include \"tileposition.hpp\"\n\n#include <atomic>\n#include <map>\n#include <list>\n#include <mutex>\n#include <cassert>\n#include <cstring>\n#include <vector>\n\nnamespace osg\n{\n    class Stats;\n}\n\nnamespace DetourNavigator\n{\n    struct NavMeshDataRef\n    {\n        unsigned char* mValue;\n        int mSize;\n    };\n\n    struct RecastMeshData\n    {\n        std::vector<int> mIndices;\n        std::vector<float> mVertices;\n        std::vector<AreaType> mAreaTypes;\n        std::vector<RecastMesh::Water> mWater;\n    };\n\n    inline bool operator <(const RecastMeshData& lhs, const RecastMeshData& rhs)\n    {\n        return std::tie(lhs.mIndices, lhs.mVertices, lhs.mAreaTypes, lhs.mWater)\n                < std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater);\n    }\n\n    inline bool operator <(const RecastMeshData& lhs, const RecastMesh& rhs)\n    {\n        return std::tie(lhs.mIndices, lhs.mVertices, lhs.mAreaTypes, lhs.mWater)\n                < std::tie(rhs.getIndices(), rhs.getVertices(), rhs.getAreaTypes(), rhs.getWater());\n    }\n\n    inline bool operator <(const RecastMesh& lhs, const RecastMeshData& rhs)\n    {\n        return std::tie(lhs.getIndices(), lhs.getVertices(), lhs.getAreaTypes(), lhs.getWater())\n                < std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater);\n    }\n\n    struct NavMeshKey\n    {\n        RecastMeshData mRecastMesh;\n        std::vector<OffMeshConnection> mOffMeshConnections;\n    };\n\n    inline bool operator <(const NavMeshKey& lhs, const NavMeshKey& rhs)\n    {\n        return std::tie(lhs.mRecastMesh, lhs.mOffMeshConnections)\n                < std::tie(rhs.mRecastMesh, rhs.mOffMeshConnections);\n    }\n\n    struct NavMeshKeyRef\n    {\n        std::reference_wrapper<const NavMeshKey> mRef;\n\n        explicit NavMeshKeyRef(const NavMeshKey& ref) : mRef(ref) {}\n    };\n\n    inline bool operator <(const NavMeshKeyRef& lhs, const NavMeshKeyRef& rhs)\n    {\n        return lhs.mRef.get() < rhs.mRef.get();\n    }\n\n    struct NavMeshKeyView\n    {\n        std::reference_wrapper<const RecastMesh> mRecastMesh;\n        std::reference_wrapper<const std::vector<OffMeshConnection>> mOffMeshConnections;\n\n        NavMeshKeyView(const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections)\n            : mRecastMesh(recastMesh), mOffMeshConnections(offMeshConnections) {}\n    };\n\n    inline bool operator <(const NavMeshKeyView& lhs, const NavMeshKey& rhs)\n    {\n        return std::tie(lhs.mRecastMesh.get(), lhs.mOffMeshConnections.get())\n                < std::tie(rhs.mRecastMesh, rhs.mOffMeshConnections);\n    }\n\n    inline bool operator <(const NavMeshKey& lhs, const NavMeshKeyView& rhs)\n    {\n        return std::tie(lhs.mRecastMesh, lhs.mOffMeshConnections)\n                < std::tie(rhs.mRecastMesh.get(), rhs.mOffMeshConnections.get());\n    }\n\n    template <class R>\n    inline bool operator <(const NavMeshKeyRef& lhs, const R& rhs)\n    {\n        return lhs.mRef.get() < rhs;\n    }\n\n    template <class L>\n    inline bool operator <(const L& lhs, const NavMeshKeyRef& rhs)\n    {\n        return lhs < rhs.mRef.get();\n    }\n\n    template <class L, class R>\n    inline bool operator <(const std::tuple<osg::Vec3f, TilePosition, L>& lhs, const std::tuple<osg::Vec3f, TilePosition, R>& rhs)\n    {\n        const auto left = std::tie(std::get<0>(lhs), std::get<1>(lhs));\n        const auto right = std::tie(std::get<0>(rhs), std::get<1>(rhs));\n        return std::tie(left, std::get<2>(lhs)) < std::tie(right, std::get<2>(rhs));\n    }\n\n    class NavMeshTilesCache\n    {\n    public:\n        struct Item\n        {\n            std::atomic<std::int64_t> mUseCount;\n            osg::Vec3f mAgentHalfExtents;\n            TilePosition mChangedTile;\n            NavMeshKey mNavMeshKey;\n            NavMeshData mNavMeshData;\n            std::size_t mSize;\n\n            Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, NavMeshKey&& navMeshKey, std::size_t size)\n                : mUseCount(0)\n                , mAgentHalfExtents(agentHalfExtents)\n                , mChangedTile(changedTile)\n                , mNavMeshKey(navMeshKey)\n                , mSize(size)\n            {}\n        };\n\n        using ItemIterator = std::list<Item>::iterator;\n\n        class Value\n        {\n        public:\n            Value()\n                : mOwner(nullptr), mIterator() {}\n\n            Value(NavMeshTilesCache& owner, ItemIterator iterator)\n                : mOwner(&owner), mIterator(iterator)\n            {\n            }\n\n            Value(const Value& other) = delete;\n\n            Value(Value&& other)\n                : mOwner(other.mOwner), mIterator(other.mIterator)\n            {\n                other.mOwner = nullptr;\n            }\n\n            ~Value()\n            {\n                if (mOwner)\n                    mOwner->releaseItem(mIterator);\n            }\n\n            Value& operator =(const Value& other) = delete;\n\n            Value& operator =(Value&& other)\n            {\n                if (mOwner)\n                    mOwner->releaseItem(mIterator);\n\n                mOwner = other.mOwner;\n                mIterator = other.mIterator;\n\n                other.mOwner = nullptr;\n\n                return *this;\n            }\n\n            NavMeshDataRef get() const\n            {\n                return NavMeshDataRef {mIterator->mNavMeshData.mValue.get(), mIterator->mNavMeshData.mSize};\n            }\n\n            operator bool() const\n            {\n                return mOwner;\n            }\n\n        private:\n            NavMeshTilesCache* mOwner;\n            ItemIterator mIterator;\n        };\n\n        struct Stats\n        {\n            std::size_t mNavMeshCacheSize;\n            std::size_t mUsedNavMeshTiles;\n            std::size_t mCachedNavMeshTiles;\n            std::size_t mHitCount;\n            std::size_t mGetCount;\n        };\n\n        NavMeshTilesCache(const std::size_t maxNavMeshDataSize);\n\n        Value get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,\n            const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections);\n\n        Value set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,\n            const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,\n            NavMeshData&& value);\n\n        Stats getStats() const;\n\n        void reportStats(unsigned int frameNumber, osg::Stats& stats) const;\n\n    private:\n        mutable std::mutex mMutex;\n        std::size_t mMaxNavMeshDataSize;\n        std::size_t mUsedNavMeshDataSize;\n        std::size_t mFreeNavMeshDataSize;\n        std::size_t mHitCount;\n        std::size_t mGetCount;\n        std::list<Item> mBusyItems;\n        std::list<Item> mFreeItems;\n        std::map<std::tuple<osg::Vec3f, TilePosition, NavMeshKeyRef>, ItemIterator, std::less<>> mValues;\n\n        void removeLeastRecentlyUsed();\n\n        void acquireItemUnsafe(ItemIterator iterator);\n\n        void releaseItem(ItemIterator iterator);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/navmeshtileview.cpp",
    "content": "#include \"navmeshtileview.hpp\"\n\n#include <DetourCommon.h>\n#include <DetourNavMesh.h>\n\n#include <algorithm>\n#include <cassert>\n#include <stdexcept>\n#include <tuple>\n\nnamespace\n{\n    template <typename T>\n    struct Ref\n    {\n        T& mRef;\n\n        explicit Ref(T& ref) : mRef(ref) {}\n\n        friend bool operator==(const Ref& lhs, const Ref& rhs)\n        {\n            return lhs.mRef == rhs.mRef;\n        }\n    };\n\n    template <typename T, std::size_t size>\n    struct ArrayRef\n    {\n        T (&mRef)[size];\n\n        explicit ArrayRef(T (&ref)[size]) : mRef(ref) {}\n\n        friend bool operator==(const ArrayRef& lhs, const ArrayRef& rhs)\n        {\n            return std::equal(std::begin(lhs.mRef), std::end(lhs.mRef), std::begin(rhs.mRef));\n        }\n    };\n\n    template <typename T>\n    struct Span\n    {\n        T* mBegin;\n        T* mEnd;\n\n        explicit Span(T* data, int size) : mBegin(data), mEnd(data + static_cast<std::size_t>(size)) {}\n\n        friend bool operator==(const Span& lhs, const Span& rhs)\n        {\n            // size is already equal if headers are equal\n            assert((lhs.mEnd - lhs.mBegin) == (rhs.mEnd - rhs.mBegin));\n            return std::equal(lhs.mBegin, lhs.mEnd, rhs.mBegin);\n        }\n    };\n\n    auto makeTuple(const dtMeshHeader& v)\n    {\n        return std::tuple(\n            v.x,\n            v.y,\n            v.layer,\n            v.userId,\n            v.polyCount,\n            v.vertCount,\n            v.maxLinkCount,\n            v.detailMeshCount,\n            v.detailVertCount,\n            v.detailTriCount,\n            v.bvNodeCount,\n            v.offMeshConCount,\n            v.offMeshBase,\n            v.walkableHeight,\n            v.walkableRadius,\n            v.walkableClimb,\n            v.detailVertCount,\n            ArrayRef(v.bmin),\n            ArrayRef(v.bmax),\n            v.bvQuantFactor\n        );\n    }\n\n    auto makeTuple(const dtPoly& v)\n    {\n        return std::tuple(ArrayRef(v.verts), ArrayRef(v.neis), v.flags, v.vertCount, v.areaAndtype);\n    }\n\n    auto makeTuple(const dtPolyDetail& v)\n    {\n        return std::tuple(v.vertBase, v.triBase, v.vertCount, v.triCount);\n    }\n\n    auto makeTuple(const dtBVNode& v)\n    {\n        return std::tuple(ArrayRef(v.bmin), ArrayRef(v.bmax), v.i);\n    }\n\n    auto makeTuple(const dtOffMeshConnection& v)\n    {\n        return std::tuple(ArrayRef(v.pos), v.rad, v.poly, v.flags, v.side, v.userId);\n    }\n\n    auto makeTuple(const DetourNavigator::NavMeshTileConstView& v)\n    {\n        return std::tuple(\n            Ref(*v.mHeader),\n            Span(v.mPolys, v.mHeader->polyCount),\n            Span(v.mVerts, v.mHeader->vertCount),\n            Span(v.mDetailMeshes, v.mHeader->detailMeshCount),\n            Span(v.mDetailVerts, v.mHeader->detailVertCount),\n            Span(v.mDetailTris, v.mHeader->detailTriCount),\n            Span(v.mBvTree, v.mHeader->bvNodeCount),\n            Span(v.mOffMeshCons, v.mHeader->offMeshConCount)\n        );\n    }\n}\n\ntemplate <class T>\ninline auto operator==(const T& lhs, const T& rhs)\n    -> std::enable_if_t<std::is_same_v<std::void_t<decltype(makeTuple(lhs))>, void>, bool>\n{\n    return makeTuple(lhs) == makeTuple(rhs);\n}\n\nnamespace DetourNavigator\n{\n    NavMeshTileConstView asNavMeshTileConstView(const unsigned char* data)\n    {\n        const dtMeshHeader* header = reinterpret_cast<const dtMeshHeader*>(data);\n\n        if (header->magic != DT_NAVMESH_MAGIC)\n            throw std::logic_error(\"Invalid navmesh magic\");\n\n        if (header->version != DT_NAVMESH_VERSION)\n            throw std::logic_error(\"Invalid navmesh version\");\n\n        // Similar code to https://github.com/recastnavigation/recastnavigation/blob/c5cbd53024c8a9d8d097a4371215e3342d2fdc87/Detour/Source/DetourNavMesh.cpp#L978-L996\n        const int headerSize = dtAlign4(sizeof(dtMeshHeader));\n        const int vertsSize = dtAlign4(sizeof(float) * 3 * header->vertCount);\n        const int polysSize = dtAlign4(sizeof(dtPoly) * header->polyCount);\n        const int linksSize = dtAlign4(sizeof(dtLink) * (header->maxLinkCount));\n        const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail) * header->detailMeshCount);\n        const int detailVertsSize = dtAlign4(sizeof(float) * 3 * header->detailVertCount);\n        const int detailTrisSize = dtAlign4(sizeof(unsigned char) * 4 * header->detailTriCount);\n        const int bvtreeSize = dtAlign4(sizeof(dtBVNode) * header->bvNodeCount);\n        const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection) * header->offMeshConCount);\n\n        const unsigned char* ptr = data + headerSize;\n\n        NavMeshTileConstView view;\n\n        view.mHeader = header;\n        view.mVerts = dtGetThenAdvanceBufferPointer<const float>(ptr, vertsSize);\n        view.mPolys = dtGetThenAdvanceBufferPointer<const dtPoly>(ptr, polysSize);\n        ptr += linksSize;\n        view.mDetailMeshes = dtGetThenAdvanceBufferPointer<const dtPolyDetail>(ptr, detailMeshesSize);\n        view.mDetailVerts = dtGetThenAdvanceBufferPointer<const float>(ptr, detailVertsSize);\n        view.mDetailTris = dtGetThenAdvanceBufferPointer<const unsigned char>(ptr, detailTrisSize);\n        view.mBvTree = dtGetThenAdvanceBufferPointer<const dtBVNode>(ptr, bvtreeSize);\n        view.mOffMeshCons = dtGetThenAdvanceBufferPointer<const dtOffMeshConnection>(ptr, offMeshLinksSize);\n\n        return view;\n    }\n\n    NavMeshTileConstView asNavMeshTileConstView(const dtMeshTile& tile)\n    {\n        NavMeshTileConstView view;\n\n        view.mHeader = tile.header;\n        view.mPolys = tile.polys;\n        view.mVerts = tile.verts;\n        view.mDetailMeshes = tile.detailMeshes;\n        view.mDetailVerts = tile.detailVerts;\n        view.mDetailTris = tile.detailTris;\n        view.mBvTree = tile.bvTree;\n        view.mOffMeshCons = tile.offMeshCons;\n\n        return view;\n    }\n\n    bool operator==(const NavMeshTileConstView& lhs, const NavMeshTileConstView& rhs)\n    {\n        return makeTuple(lhs) == makeTuple(rhs);\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/navmeshtileview.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILEVIEW_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILEVIEW_H\n\nstruct dtMeshHeader;\nstruct dtPoly;\nstruct dtPolyDetail;\nstruct dtBVNode;\nstruct dtOffMeshConnection;\nstruct dtMeshTile;\n\nnamespace DetourNavigator\n{\n    struct NavMeshTileConstView\n    {\n        const dtMeshHeader* mHeader;\n        const dtPoly* mPolys;\n        const float* mVerts;\n        const dtPolyDetail* mDetailMeshes;\n        const float* mDetailVerts;\n        const unsigned char* mDetailTris;\n        const dtBVNode* mBvTree;\n        const dtOffMeshConnection* mOffMeshCons;\n\n        friend bool operator==(const NavMeshTileConstView& lhs, const NavMeshTileConstView& rhs);\n    };\n\n    NavMeshTileConstView asNavMeshTileConstView(const unsigned char* data);\n    NavMeshTileConstView asNavMeshTileConstView(const dtMeshTile& tile);\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/objectid.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OBJECTID_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OBJECTID_H\n\n#include <cstddef>\n#include <unordered_map>\n\nnamespace DetourNavigator\n{\n    class ObjectId\n    {\n    public:\n        template <class T>\n        explicit ObjectId(T* value) noexcept\n            : mValue(reinterpret_cast<std::size_t>(value))\n        {\n        }\n\n        std::size_t value() const noexcept\n        {\n            return mValue;\n        }\n\n        friend bool operator <(const ObjectId lhs, const ObjectId rhs) noexcept\n        {\n            return lhs.mValue < rhs.mValue;\n        }\n\n        friend bool operator ==(const ObjectId lhs, const ObjectId rhs) noexcept\n        {\n            return lhs.mValue == rhs.mValue;\n        }\n\n    private:\n        std::size_t mValue;\n    };\n}\n\nnamespace std\n{\n    template <>\n    struct hash<DetourNavigator::ObjectId>\n    {\n        std::size_t operator ()(const DetourNavigator::ObjectId value) const noexcept\n        {\n            return value.value();\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/offmeshconnection.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTION_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTION_H\n\n#include \"areatype.hpp\"\n\n#include <osg/Vec3f>\n\n#include <tuple>\n\nnamespace DetourNavigator\n{\n    struct OffMeshConnection\n    {\n        osg::Vec3f mStart;\n        osg::Vec3f mEnd;\n        AreaType mAreaType;\n    };\n\n    inline bool operator<(const OffMeshConnection& lhs, const OffMeshConnection& rhs)\n    {\n        return std::tie(lhs.mStart, lhs.mEnd, lhs.mAreaType) < std::tie(rhs.mStart, rhs.mEnd, rhs.mAreaType);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/offmeshconnectionsmanager.cpp",
    "content": "#include \"offmeshconnectionsmanager.hpp\"\n#include \"settings.hpp\"\n#include \"settingsutils.hpp\"\n#include \"tileposition.hpp\"\n#include \"objectid.hpp\"\n#include \"offmeshconnection.hpp\"\n\n#include <algorithm>\n#include <vector>\n#include <set>\n\nnamespace DetourNavigator\n{\n    OffMeshConnectionsManager::OffMeshConnectionsManager(const Settings& settings)\n        : mSettings(settings)\n    {}\n\n    void OffMeshConnectionsManager::add(const ObjectId id, const OffMeshConnection& value)\n    {\n        const auto values = mValues.lock();\n\n        values->mById.insert(std::make_pair(id, value));\n\n        const auto startTilePosition = getTilePosition(mSettings, value.mStart);\n        const auto endTilePosition = getTilePosition(mSettings, value.mEnd);\n\n        values->mByTilePosition[startTilePosition].insert(id);\n\n        if (startTilePosition != endTilePosition)\n            values->mByTilePosition[endTilePosition].insert(id);\n    }\n\n    std::set<TilePosition> OffMeshConnectionsManager::remove(const ObjectId id)\n    {\n        const auto values = mValues.lock();\n\n        const auto byId = values->mById.equal_range(id);\n\n        if (byId.first == byId.second)\n            return {};\n\n        std::set<TilePosition> removed;\n\n        std::for_each(byId.first, byId.second, [&] (const auto& v) {\n            const auto startTilePosition = getTilePosition(mSettings, v.second.mStart);\n            const auto endTilePosition = getTilePosition(mSettings, v.second.mEnd);\n\n            removed.emplace(startTilePosition);\n            if (startTilePosition != endTilePosition)\n                removed.emplace(endTilePosition);\n        });\n\n        for (const TilePosition& tilePosition : removed)\n        {\n            const auto it = values->mByTilePosition.find(tilePosition);\n            if (it == values->mByTilePosition.end())\n                continue;\n            it->second.erase(id);\n            if (it->second.empty())\n                values->mByTilePosition.erase(it);\n        }\n\n        values->mById.erase(byId.first, byId.second);\n\n        return removed;\n    }\n\n    std::vector<OffMeshConnection> OffMeshConnectionsManager::get(const TilePosition& tilePosition)\n    {\n        std::vector<OffMeshConnection> result;\n\n        const auto values = mValues.lock();\n\n        const auto itByTilePosition = values->mByTilePosition.find(tilePosition);\n\n        if (itByTilePosition == values->mByTilePosition.end())\n            return result;\n\n        std::for_each(itByTilePosition->second.begin(), itByTilePosition->second.end(),\n            [&] (const ObjectId v)\n            {\n                const auto byId = values->mById.equal_range(v);\n                std::for_each(byId.first, byId.second, [&] (const auto& v)\n                {\n                    if (getTilePosition(mSettings, v.second.mStart) == tilePosition\n                            || getTilePosition(mSettings, v.second.mEnd) == tilePosition)\n                        result.push_back(v.second);\n                });\n            });\n\n        std::sort(result.begin(), result.end());\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/offmeshconnectionsmanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTIONSMANAGER_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTIONSMANAGER_H\n\n#include \"settings.hpp\"\n#include \"tileposition.hpp\"\n#include \"objectid.hpp\"\n#include \"offmeshconnection.hpp\"\n\n#include <components/misc/guarded.hpp>\n\n#include <map>\n#include <unordered_set>\n#include <vector>\n#include <set>\n\nnamespace DetourNavigator\n{\n    class OffMeshConnectionsManager\n    {\n    public:\n        OffMeshConnectionsManager(const Settings& settings);\n\n        void add(const ObjectId id, const OffMeshConnection& value);\n\n        std::set<TilePosition> remove(const ObjectId id);\n\n        std::vector<OffMeshConnection> get(const TilePosition& tilePosition);\n\n    private:\n        struct Values\n        {\n            std::multimap<ObjectId, OffMeshConnection> mById;\n            std::map<TilePosition, std::unordered_set<ObjectId>> mByTilePosition;\n        };\n\n        const Settings& mSettings;\n        Misc::ScopeGuarded<Values> mValues;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/oscillatingrecastmeshobject.cpp",
    "content": "#include \"oscillatingrecastmeshobject.hpp\"\n#include \"tilebounds.hpp\"\n\n#include <components/bullethelpers/aabb.hpp>\n\n#include <algorithm>\n\nnamespace DetourNavigator\n{\n    namespace\n    {\n        void limitBy(btAABB& aabb, const TileBounds& bounds)\n        {\n            aabb.m_min.setX(std::max(aabb.m_min.x(), static_cast<btScalar>(bounds.mMin.x())));\n            aabb.m_min.setY(std::max(aabb.m_min.y(), static_cast<btScalar>(bounds.mMin.y())));\n            aabb.m_max.setX(std::min(aabb.m_max.x(), static_cast<btScalar>(bounds.mMax.x())));\n            aabb.m_max.setY(std::min(aabb.m_max.y(), static_cast<btScalar>(bounds.mMax.y())));\n        }\n    }\n\n    OscillatingRecastMeshObject::OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision)\n        : mImpl(std::move(impl))\n        , mLastChangeRevision(lastChangeRevision)\n        , mAabb(BulletHelpers::getAabb(mImpl.getShape(), mImpl.getTransform()))\n    {\n    }\n\n    OscillatingRecastMeshObject::OscillatingRecastMeshObject(const RecastMeshObject& impl, std::size_t lastChangeRevision)\n        : mImpl(impl)\n        , mLastChangeRevision(lastChangeRevision)\n        , mAabb(BulletHelpers::getAabb(mImpl.getShape(), mImpl.getTransform()))\n    {\n    }\n\n    bool OscillatingRecastMeshObject::update(const btTransform& transform, const AreaType areaType,\n                                             std::size_t lastChangeRevision, const TileBounds& bounds)\n    {\n        const btTransform oldTransform = mImpl.getTransform();\n        if (!mImpl.update(transform, areaType))\n            return false;\n        if (transform == oldTransform)\n            return true;\n        if (mLastChangeRevision != lastChangeRevision)\n        {\n            mLastChangeRevision = lastChangeRevision;\n            // btAABB doesn't have copy-assignment operator\n            const btAABB aabb = BulletHelpers::getAabb(mImpl.getShape(), transform);\n            mAabb.m_min = aabb.m_min;\n            mAabb.m_max = aabb.m_max;\n            return true;\n        }\n        const btAABB currentAabb = mAabb;\n        mAabb.merge(BulletHelpers::getAabb(mImpl.getShape(), transform));\n        limitBy(mAabb, bounds);\n        return currentAabb != mAabb;\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/oscillatingrecastmeshobject.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H\n\n#include \"areatype.hpp\"\n#include \"recastmeshobject.hpp\"\n#include \"tilebounds.hpp\"\n\n#include <LinearMath/btTransform.h>\n#include <BulletCollision/Gimpact/btBoxCollision.h>\n\nnamespace DetourNavigator\n{\n    class OscillatingRecastMeshObject\n    {\n        public:\n            explicit OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision);\n            explicit OscillatingRecastMeshObject(const RecastMeshObject& impl, std::size_t lastChangeRevision);\n\n            bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision,\n                        const TileBounds& bounds);\n\n            const RecastMeshObject& getImpl() const { return mImpl; }\n\n        private:\n            RecastMeshObject mImpl;\n            std::size_t mLastChangeRevision;\n            btAABB mAabb;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/raycast.cpp",
    "content": "#include \"raycast.hpp\"\n#include \"settings.hpp\"\n#include \"findsmoothpath.hpp\"\n\n#include <DetourNavMesh.h>\n#include <DetourNavMeshQuery.h>\n\n#include <array>\n\nnamespace DetourNavigator\n{\n    std::optional<osg::Vec3f> raycast(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents,\n        const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const Settings& settings)\n    {\n        dtNavMeshQuery navMeshQuery;\n        if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes))\n            return {};\n\n        dtQueryFilter queryFilter;\n        queryFilter.setIncludeFlags(includeFlags);\n\n        dtPolyRef ref = 0;\n        if (dtStatus status = navMeshQuery.findNearestPoly(start.ptr(), halfExtents.ptr(), &queryFilter, &ref, nullptr);\n            dtStatusFailed(status) || ref == 0)\n            return {};\n\n        const unsigned options = 0;\n        std::array<dtPolyRef, 16> path;\n        dtRaycastHit hit;\n        hit.path = path.data();\n        hit.maxPath = path.size();\n        if (dtStatus status = navMeshQuery.raycast(ref, start.ptr(), end.ptr(), &queryFilter, options, &hit);\n            dtStatusFailed(status) || hit.pathCount == 0)\n            return {};\n\n        osg::Vec3f hitPosition;\n        if (dtStatus status = navMeshQuery.closestPointOnPoly(path[hit.pathCount - 1], end.ptr(), hitPosition.ptr(), nullptr);\n            dtStatusFailed(status))\n            return {};\n\n        return hitPosition;\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/raycast.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RAYCAST_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RAYCAST_H\n\n#include \"flags.hpp\"\n\n#include <optional>\n#include <osg/Vec3f>\n\nclass dtNavMesh;\n\nnamespace DetourNavigator\n{\n    struct Settings;\n\n    std::optional<osg::Vec3f> raycast(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents,\n        const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const Settings& settings);\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/recastallocutils.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTALLOCUTILS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTALLOCUTILS_H\n\n#include <RecastAlloc.h>\n\n#include <cstdint>\n\nnamespace DetourNavigator\n{\n    static_assert(sizeof(std::size_t) == sizeof(void*), \"\");\n\n    enum BufferType : std::size_t\n    {\n        BufferType_perm,\n        BufferType_temp,\n        BufferType_unused,\n    };\n\n    inline BufferType* tempPtrBufferType(void* ptr)\n    {\n        return reinterpret_cast<BufferType*>(static_cast<std::size_t*>(ptr) + 1);\n    }\n\n    inline BufferType getTempPtrBufferType(void* ptr)\n    {\n        return *tempPtrBufferType(ptr);\n    }\n\n    inline void setTempPtrBufferType(void* ptr, BufferType value)\n    {\n        *tempPtrBufferType(ptr) = value;\n    }\n\n    inline void** tempPtrPrev(void* ptr)\n    {\n        return static_cast<void**>(ptr);\n    }\n\n    inline void* getTempPtrPrev(void* ptr)\n    {\n        return *tempPtrPrev(ptr);\n    }\n\n    inline void setTempPtrPrev(void* ptr, void* value)\n    {\n        *tempPtrPrev(ptr) = value;\n    }\n\n    inline void* getTempPtrDataPtr(void* ptr)\n    {\n        return reinterpret_cast<void*>(static_cast<std::size_t*>(ptr) + 2);\n    }\n\n    inline BufferType* dataPtrBufferType(void* dataPtr)\n    {\n        return reinterpret_cast<BufferType*>(static_cast<std::size_t*>(dataPtr) - 1);\n    }\n\n    inline BufferType getDataPtrBufferType(void* dataPtr)\n    {\n        return *dataPtrBufferType(dataPtr);\n    }\n\n    inline void setDataPtrBufferType(void* dataPtr, BufferType value)\n    {\n        *dataPtrBufferType(dataPtr) = value;\n    }\n\n    inline void* getTempDataPtrStackPtr(void* dataPtr)\n    {\n        return static_cast<std::size_t*>(dataPtr) - 2;\n    }\n\n    inline void* getPermDataPtrHeapPtr(void* dataPtr)\n    {\n        return static_cast<std::size_t*>(dataPtr) - 1;\n    }\n\n    inline void setPermPtrBufferType(void* ptr, BufferType value)\n    {\n        *static_cast<BufferType*>(ptr) = value;\n    }\n\n    inline void* getPermPtrDataPtr(void* ptr)\n    {\n        return static_cast<std::size_t*>(ptr) + 1;\n    }\n\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/recastglobalallocator.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTGLOBALALLOCATOR_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTGLOBALALLOCATOR_H\n\n#include \"recasttempallocator.hpp\"\n\n#include <cstdlib>\n\nnamespace DetourNavigator\n{\n    class RecastGlobalAllocator\n    {\n    public:\n        static void init()\n        {\n            instance();\n        }\n\n        static void* alloc(size_t size, rcAllocHint hint)\n        {\n            void* result = nullptr;\n            if (rcLikely(hint == RC_ALLOC_TEMP))\n                result = tempAllocator().alloc(size);\n            if (rcUnlikely(!result))\n                result = allocPerm(size);\n            return result;\n        }\n\n        static void free(void* ptr)\n        {\n            if (rcUnlikely(!ptr))\n                return;\n            if (rcLikely(BufferType_temp == getDataPtrBufferType(ptr)))\n                tempAllocator().free(ptr);\n            else\n            {\n                assert(BufferType_perm == getDataPtrBufferType(ptr));\n                std::free(getPermDataPtrHeapPtr(ptr));\n            }\n        }\n\n    private:\n        RecastGlobalAllocator()\n        {\n            rcAllocSetCustom(&RecastGlobalAllocator::alloc, &RecastGlobalAllocator::free);\n        }\n\n        static RecastGlobalAllocator& instance()\n        {\n            static RecastGlobalAllocator value;\n            return value;\n        }\n\n        static RecastTempAllocator& tempAllocator()\n        {\n            static thread_local RecastTempAllocator value(1024ul * 1024ul);\n            return value;\n        }\n\n        static void* allocPerm(size_t size)\n        {\n            const auto ptr = std::malloc(size + sizeof(std::size_t));\n            if (rcUnlikely(!ptr))\n                return ptr;\n            setPermPtrBufferType(ptr, BufferType_perm);\n            return getPermPtrDataPtr(ptr);\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/recastmesh.cpp",
    "content": "#include \"recastmesh.hpp\"\n#include \"exceptions.hpp\"\n\n#include <Recast.h>\n\nnamespace DetourNavigator\n{\n    RecastMesh::RecastMesh(std::size_t generation, std::size_t revision, std::vector<int> indices, std::vector<float> vertices,\n            std::vector<AreaType> areaTypes, std::vector<Water> water)\n        : mGeneration(generation)\n        , mRevision(revision)\n        , mIndices(std::move(indices))\n        , mVertices(std::move(vertices))\n        , mAreaTypes(std::move(areaTypes))\n        , mWater(std::move(water))\n    {\n        if (getTrianglesCount() != mAreaTypes.size())\n            throw InvalidArgument(\"Number of flags doesn't match number of triangles: triangles=\"\n                + std::to_string(getTrianglesCount()) + \", areaTypes=\" + std::to_string(mAreaTypes.size()));\n        if (getVerticesCount())\n            rcCalcBounds(mVertices.data(), static_cast<int>(getVerticesCount()), mBounds.mMin.ptr(), mBounds.mMax.ptr());\n        mIndices.shrink_to_fit();\n        mVertices.shrink_to_fit();\n        mAreaTypes.shrink_to_fit();\n        mWater.shrink_to_fit();\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/recastmesh.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESH_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESH_H\n\n#include \"areatype.hpp\"\n#include \"bounds.hpp\"\n\n#include <components/bullethelpers/operators.hpp>\n\n#include <memory>\n#include <string>\n#include <vector>\n#include <tuple>\n\n#include <LinearMath/btTransform.h>\n\nnamespace DetourNavigator\n{\n    class RecastMesh\n    {\n    public:\n        struct Water\n        {\n            int mCellSize;\n            btTransform mTransform;\n        };\n\n        RecastMesh(std::size_t generation, std::size_t revision, std::vector<int> indices, std::vector<float> vertices,\n            std::vector<AreaType> areaTypes, std::vector<Water> water);\n\n        std::size_t getGeneration() const\n        {\n            return mGeneration;\n        }\n\n        std::size_t getRevision() const\n        {\n            return mRevision;\n        }\n\n        const std::vector<int>& getIndices() const\n        {\n            return mIndices;\n        }\n\n        const std::vector<float>& getVertices() const\n        {\n            return mVertices;\n        }\n\n        const std::vector<AreaType>& getAreaTypes() const\n        {\n            return mAreaTypes;\n        }\n\n        const std::vector<Water>& getWater() const\n        {\n            return mWater;\n        }\n\n        std::size_t getVerticesCount() const\n        {\n            return mVertices.size() / 3;\n        }\n\n        std::size_t getTrianglesCount() const\n        {\n            return mIndices.size() / 3;\n        }\n\n        const Bounds& getBounds() const\n        {\n            return mBounds;\n        }\n\n    private:\n        std::size_t mGeneration;\n        std::size_t mRevision;\n        std::vector<int> mIndices;\n        std::vector<float> mVertices;\n        std::vector<AreaType> mAreaTypes;\n        std::vector<Water> mWater;\n        Bounds mBounds;\n    };\n\n    inline bool operator<(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs)\n    {\n        return std::tie(lhs.mCellSize, lhs.mTransform) < std::tie(rhs.mCellSize, rhs.mTransform);\n    }\n\n    inline bool operator <(const RecastMesh& lhs, const RecastMesh& rhs)\n    {\n        return std::tie(lhs.getIndices(), lhs.getVertices(), lhs.getAreaTypes(), lhs.getWater())\n                < std::tie(rhs.getIndices(), rhs.getVertices(), rhs.getAreaTypes(), rhs.getWater());\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/recastmeshbuilder.cpp",
    "content": "#include \"recastmeshbuilder.hpp\"\n#include \"debug.hpp\"\n#include \"settings.hpp\"\n#include \"settingsutils.hpp\"\n#include \"exceptions.hpp\"\n\n#include <components/bullethelpers/transformboundingbox.hpp>\n#include <components/bullethelpers/processtrianglecallback.hpp>\n#include <components/misc/convert.hpp>\n\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n#include <BulletCollision/CollisionShapes/btConcaveShape.h>\n#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>\n#include <LinearMath/btTransform.h>\n#include <LinearMath/btAabbUtil2.h>\n\n#include <algorithm>\n#include <cassert>\n#include <tuple>\n#include <array>\n\nnamespace DetourNavigator\n{\n    using BulletHelpers::makeProcessTriangleCallback;\n\n    namespace\n    {\n        void optimizeRecastMesh(std::vector<int>& indices, std::vector<float>& vertices)\n        {\n            std::vector<std::tuple<float, float, float>> uniqueVertices;\n            uniqueVertices.reserve(vertices.size() / 3);\n\n            for (std::size_t i = 0, n = vertices.size() / 3; i < n; ++i)\n                uniqueVertices.emplace_back(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);\n\n            std::sort(uniqueVertices.begin(), uniqueVertices.end());\n            const auto end = std::unique(uniqueVertices.begin(), uniqueVertices.end());\n            uniqueVertices.erase(end, uniqueVertices.end());\n\n            if (uniqueVertices.size() == vertices.size() / 3)\n                return;\n\n            for (std::size_t i = 0, n = indices.size(); i < n; ++i)\n            {\n                const auto index = indices[i];\n                const auto vertex = std::make_tuple(vertices[index * 3], vertices[index * 3 + 1], vertices[index * 3 + 2]);\n                const auto it = std::lower_bound(uniqueVertices.begin(), uniqueVertices.end(), vertex);\n                assert(it != uniqueVertices.end());\n                assert(*it == vertex);\n                indices[i] = std::distance(uniqueVertices.begin(), it);\n            }\n\n            vertices.resize(uniqueVertices.size() * 3);\n\n            for (std::size_t i = 0, n = uniqueVertices.size(); i < n; ++i)\n            {\n                vertices[i * 3] = std::get<0>(uniqueVertices[i]);\n                vertices[i * 3 + 1] = std::get<1>(uniqueVertices[i]);\n                vertices[i * 3 + 2] = std::get<2>(uniqueVertices[i]);\n            }\n        }\n    }\n\n    RecastMeshBuilder::RecastMeshBuilder(const Settings& settings, const TileBounds& bounds)\n        : mSettings(settings)\n        , mBounds(bounds)\n    {\n        mBounds.mMin /= mSettings.get().mRecastScaleFactor;\n        mBounds.mMax /= mSettings.get().mRecastScaleFactor;\n    }\n\n    void RecastMeshBuilder::addObject(const btCollisionShape& shape, const btTransform& transform,\n                                      const AreaType areaType)\n    {\n        if (shape.isCompound())\n            return addObject(static_cast<const btCompoundShape&>(shape), transform, areaType);\n        else if (shape.getShapeType() == TERRAIN_SHAPE_PROXYTYPE)\n            return addObject(static_cast<const btHeightfieldTerrainShape&>(shape), transform, areaType);\n        else if (shape.isConcave())\n            return addObject(static_cast<const btConcaveShape&>(shape), transform, areaType);\n        else if (shape.getShapeType() == BOX_SHAPE_PROXYTYPE)\n            return addObject(static_cast<const btBoxShape&>(shape), transform, areaType);\n        std::ostringstream message;\n        message << \"Unsupported shape type: \" << BroadphaseNativeTypes(shape.getShapeType());\n        throw InvalidArgument(message.str());\n    }\n\n    void RecastMeshBuilder::addObject(const btCompoundShape& shape, const btTransform& transform,\n                                      const AreaType areaType)\n    {\n        for (int i = 0, num = shape.getNumChildShapes(); i < num; ++i)\n            addObject(*shape.getChildShape(i), transform * shape.getChildTransform(i), areaType);\n    }\n\n    void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform,\n                                      const AreaType areaType)\n    {\n        return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int)\n        {\n            for (std::size_t i = 3; i > 0; --i)\n                addTriangleVertex(triangle[i - 1]);\n            mAreaTypes.push_back(areaType);\n        }));\n    }\n\n    void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform,\n                                      const AreaType areaType)\n    {\n        return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int)\n        {\n            for (std::size_t i = 0; i < 3; ++i)\n                addTriangleVertex(triangle[i]);\n            mAreaTypes.push_back(areaType);\n        }));\n    }\n\n    void RecastMeshBuilder::addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType)\n    {\n        const auto indexOffset = static_cast<int>(mVertices.size() / 3);\n\n        for (int vertex = 0, count = shape.getNumVertices(); vertex < count; ++vertex)\n        {\n            btVector3 position;\n            shape.getVertex(vertex, position);\n            addVertex(transform(position));\n        }\n\n        const std::array<int, 36> indices {{\n            0, 2, 3,\n            3, 1, 0,\n            0, 4, 6,\n            6, 2, 0,\n            0, 1, 5,\n            5, 4, 0,\n            7, 5, 1,\n            1, 3, 7,\n            7, 3, 2,\n            2, 6, 7,\n            7, 6, 4,\n            4, 5, 7,\n        }};\n\n        std::transform(indices.begin(), indices.end(), std::back_inserter(mIndices),\n            [&] (int index) { return index + indexOffset; });\n\n        std::generate_n(std::back_inserter(mAreaTypes), 12, [=] { return areaType; });\n    }\n\n    void RecastMeshBuilder::addWater(const int cellSize, const btTransform& transform)\n    {\n        mWater.push_back(RecastMesh::Water {cellSize, transform});\n    }\n\n    std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision) &&\n    {\n        optimizeRecastMesh(mIndices, mVertices);\n        std::sort(mWater.begin(), mWater.end());\n        return std::make_shared<RecastMesh>(generation, revision, std::move(mIndices), std::move(mVertices), std::move(mAreaTypes), std::move(mWater));\n    }\n\n    void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform,\n                                      btTriangleCallback&& callback)\n    {\n        btVector3 aabbMin;\n        btVector3 aabbMax;\n\n        shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);\n\n        const btVector3 boundsMin(mBounds.mMin.x(), mBounds.mMin.y(),\n            -std::numeric_limits<btScalar>::max() * std::numeric_limits<btScalar>::epsilon());\n        const btVector3 boundsMax(mBounds.mMax.x(), mBounds.mMax.y(),\n            std::numeric_limits<btScalar>::max() * std::numeric_limits<btScalar>::epsilon());\n\n        auto wrapper = makeProcessTriangleCallback([&] (btVector3* triangle, int partId, int triangleIndex)\n        {\n            std::array<btVector3, 3> transformed;\n            for (std::size_t i = 0; i < 3; ++i)\n                transformed[i] = transform(triangle[i]);\n            if (TestTriangleAgainstAabb2(transformed.data(), boundsMin, boundsMax))\n                callback.processTriangle(transformed.data(), partId, triangleIndex);\n        });\n\n        shape.processAllTriangles(&wrapper, aabbMin, aabbMax);\n    }\n\n    void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform,\n                                      btTriangleCallback&& callback)\n    {\n        using BulletHelpers::transformBoundingBox;\n\n        btVector3 aabbMin;\n        btVector3 aabbMax;\n\n        shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);\n\n        transformBoundingBox(transform, aabbMin, aabbMax);\n\n        aabbMin.setX(std::max(static_cast<btScalar>(mBounds.mMin.x()), aabbMin.x()));\n        aabbMin.setX(std::min(static_cast<btScalar>(mBounds.mMax.x()), aabbMin.x()));\n        aabbMin.setY(std::max(static_cast<btScalar>(mBounds.mMin.y()), aabbMin.y()));\n        aabbMin.setY(std::min(static_cast<btScalar>(mBounds.mMax.y()), aabbMin.y()));\n\n        aabbMax.setX(std::max(static_cast<btScalar>(mBounds.mMin.x()), aabbMax.x()));\n        aabbMax.setX(std::min(static_cast<btScalar>(mBounds.mMax.x()), aabbMax.x()));\n        aabbMax.setY(std::max(static_cast<btScalar>(mBounds.mMin.y()), aabbMax.y()));\n        aabbMax.setY(std::min(static_cast<btScalar>(mBounds.mMax.y()), aabbMax.y()));\n\n        transformBoundingBox(transform.inverse(), aabbMin, aabbMax);\n\n        auto wrapper = makeProcessTriangleCallback([&] (btVector3* triangle, int partId, int triangleIndex)\n        {\n            std::array<btVector3, 3> transformed;\n            for (std::size_t i = 0; i < 3; ++i)\n                transformed[i] = transform(triangle[i]);\n            callback.processTriangle(transformed.data(), partId, triangleIndex);\n        });\n\n        shape.processAllTriangles(&wrapper, aabbMin, aabbMax);\n    }\n\n    void RecastMeshBuilder::addTriangleVertex(const btVector3& worldPosition)\n    {\n        mIndices.push_back(static_cast<int>(mVertices.size() / 3));\n        addVertex(worldPosition);\n    }\n\n    void RecastMeshBuilder::addVertex(const btVector3& worldPosition)\n    {\n        const auto navMeshPosition = toNavMeshCoordinates(mSettings, Misc::Convert::makeOsgVec3f(worldPosition));\n        mVertices.push_back(navMeshPosition.x());\n        mVertices.push_back(navMeshPosition.y());\n        mVertices.push_back(navMeshPosition.z());\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/recastmeshbuilder.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHBUILDER_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHBUILDER_H\n\n#include \"recastmesh.hpp\"\n#include \"tilebounds.hpp\"\n\n#include <LinearMath/btTransform.h>\n\nclass btBoxShape;\nclass btCollisionShape;\nclass btCompoundShape;\nclass btConcaveShape;\nclass btHeightfieldTerrainShape;\nclass btTriangleCallback;\n\nnamespace DetourNavigator\n{\n    struct Settings;\n\n    class RecastMeshBuilder\n    {\n    public:\n        RecastMeshBuilder(const Settings& settings, const TileBounds& bounds);\n\n        void addObject(const btCollisionShape& shape, const btTransform& transform, const AreaType areaType);\n\n        void addObject(const btCompoundShape& shape, const btTransform& transform, const AreaType areaType);\n\n        void addObject(const btConcaveShape& shape, const btTransform& transform, const AreaType areaType);\n\n        void addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, const AreaType areaType);\n\n        void addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType);\n\n        void addWater(const int mCellSize, const btTransform& transform);\n\n        std::shared_ptr<RecastMesh> create(std::size_t generation, std::size_t revision) &&;\n\n    private:\n        std::reference_wrapper<const Settings> mSettings;\n        TileBounds mBounds;\n        std::vector<int> mIndices;\n        std::vector<float> mVertices;\n        std::vector<AreaType> mAreaTypes;\n        std::vector<RecastMesh::Water> mWater;\n\n        void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback);\n\n        void addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, btTriangleCallback&& callback);\n\n        void addTriangleVertex(const btVector3& worldPosition);\n\n        void addVertex(const btVector3& worldPosition);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/recastmeshmanager.cpp",
    "content": "#include \"recastmeshmanager.hpp\"\n#include \"recastmeshbuilder.hpp\"\n\nnamespace DetourNavigator\n{\n    RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation)\n        : mSettings(settings)\n        , mGeneration(generation)\n        , mTileBounds(bounds)\n    {\n    }\n\n    bool RecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                                      const AreaType areaType)\n    {\n        const std::lock_guard lock(mMutex);\n        const auto object = mObjects.lower_bound(id);\n        if (object != mObjects.end() && object->first == id)\n            return false;\n        const auto iterator = mObjectsOrder.emplace(mObjectsOrder.end(),\n            OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1));\n        mObjects.emplace_hint(object, id, iterator);\n        ++mRevision;\n        return true;\n    }\n\n    bool RecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType)\n    {\n        const std::lock_guard lock(mMutex);\n        const auto object = mObjects.find(id);\n        if (object == mObjects.end())\n            return false;\n        const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value()\n                ? mLastNavMeshReportedChange->mRevision : mRevision;\n        if (!object->second->update(transform, areaType, lastChangeRevision, mTileBounds))\n            return false;\n        ++mRevision;\n        return true;\n    }\n\n    std::optional<RemovedRecastMeshObject> RecastMeshManager::removeObject(const ObjectId id)\n    {\n        const std::lock_guard lock(mMutex);\n        const auto object = mObjects.find(id);\n        if (object == mObjects.end())\n            return std::nullopt;\n        const RemovedRecastMeshObject result {object->second->getImpl().getShape(), object->second->getImpl().getTransform()};\n        mObjectsOrder.erase(object->second);\n        mObjects.erase(object);\n        ++mRevision;\n        return result;\n    }\n\n    bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize,\n        const btTransform& transform)\n    {\n        const std::lock_guard lock(mMutex);\n        const auto iterator = mWaterOrder.emplace(mWaterOrder.end(), Water {cellSize, transform});\n        if (!mWater.emplace(cellPosition, iterator).second)\n        {\n            mWaterOrder.erase(iterator);\n            return false;\n        }\n        ++mRevision;\n        return true;\n    }\n\n    std::optional<RecastMeshManager::Water> RecastMeshManager::removeWater(const osg::Vec2i& cellPosition)\n    {\n        const std::lock_guard lock(mMutex);\n        const auto water = mWater.find(cellPosition);\n        if (water == mWater.end())\n            return std::nullopt;\n        ++mRevision;\n        const auto result = *water->second;\n        mWaterOrder.erase(water->second);\n        mWater.erase(water);\n        return result;\n    }\n\n    std::shared_ptr<RecastMesh> RecastMeshManager::getMesh()\n    {\n        RecastMeshBuilder builder(mSettings, mTileBounds);\n        using Object = std::tuple<\n            osg::ref_ptr<const osg::Object>,\n            std::reference_wrapper<const btCollisionShape>,\n            btTransform,\n            AreaType\n        >;\n        std::vector<Object> objects;\n        std::size_t revision;\n        {\n            const std::lock_guard lock(mMutex);\n            for (const auto& v : mWaterOrder)\n                builder.addWater(v.mCellSize, v.mTransform);\n            objects.reserve(mObjectsOrder.size());\n            for (const auto& object : mObjectsOrder)\n            {\n                const RecastMeshObject& impl = object.getImpl();\n                objects.emplace_back(impl.getHolder(), impl.getShape(), impl.getTransform(), impl.getAreaType());\n            }\n            revision = mRevision;\n        }\n        for (const auto& [holder, shape, transform, areaType] : objects)\n            builder.addObject(shape, transform, areaType);\n        return std::move(builder).create(mGeneration, revision);\n    }\n\n    bool RecastMeshManager::isEmpty() const\n    {\n        const std::lock_guard lock(mMutex);\n        return mObjects.empty();\n    }\n\n    void RecastMeshManager::reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion)\n    {\n        if (recastMeshVersion.mGeneration != mGeneration)\n            return;\n        const std::lock_guard lock(mMutex);\n        if (mLastNavMeshReport.has_value() && navMeshVersion < mLastNavMeshReport->mNavMeshVersion)\n            return;\n        mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion};\n        if (!mLastNavMeshReportedChange.has_value()\n                || mLastNavMeshReportedChange->mNavMeshVersion < mLastNavMeshReport->mNavMeshVersion)\n            mLastNavMeshReportedChange = mLastNavMeshReport;\n    }\n\n    Version RecastMeshManager::getVersion() const\n    {\n        const std::lock_guard lock(mMutex);\n        return Version {mGeneration, mRevision};\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/recastmeshmanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H\n\n#include \"oscillatingrecastmeshobject.hpp\"\n#include \"objectid.hpp\"\n#include \"version.hpp\"\n\n#include <LinearMath/btTransform.h>\n\n#include <osg/Vec2i>\n\n#include <list>\n#include <map>\n#include <optional>\n#include <memory>\n#include <mutex>\n\nclass btCollisionShape;\n\nnamespace DetourNavigator\n{\n    struct Settings;\n    class RecastMesh;\n\n    struct RemovedRecastMeshObject\n    {\n        std::reference_wrapper<const btCollisionShape> mShape;\n        btTransform mTransform;\n    };\n\n    class RecastMeshManager\n    {\n    public:\n        struct Water\n        {\n            int mCellSize = 0;\n            btTransform mTransform;\n        };\n\n        RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation);\n\n        bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                       const AreaType areaType);\n\n        bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType);\n\n        bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform);\n\n        std::optional<Water> removeWater(const osg::Vec2i& cellPosition);\n\n        std::optional<RemovedRecastMeshObject> removeObject(const ObjectId id);\n\n        std::shared_ptr<RecastMesh> getMesh();\n\n        bool isEmpty() const;\n\n        void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion);\n\n        Version getVersion() const;\n\n    private:\n        struct Report\n        {\n            std::size_t mRevision;\n            Version mNavMeshVersion;\n        };\n\n        const Settings& mSettings;\n        const std::size_t mGeneration;\n        const TileBounds mTileBounds;\n        mutable std::mutex mMutex;\n        std::size_t mRevision = 0;\n        std::list<OscillatingRecastMeshObject> mObjectsOrder;\n        std::map<ObjectId, std::list<OscillatingRecastMeshObject>::iterator> mObjects;\n        std::list<Water> mWaterOrder;\n        std::map<osg::Vec2i, std::list<Water>::iterator> mWater;\n        std::optional<Report> mLastNavMeshReportedChange;\n        std::optional<Report> mLastNavMeshReport;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/recastmeshobject.cpp",
    "content": "#include \"recastmeshobject.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n\n#include <cassert>\n\nnamespace DetourNavigator\n{\n    namespace\n    {\n        bool updateCompoundObject(const btCompoundShape& shape, const AreaType areaType,\n            std::vector<RecastMeshObject>& children)\n        {\n            assert(static_cast<std::size_t>(shape.getNumChildShapes()) == children.size());\n            bool result = false;\n            for (int i = 0, num = shape.getNumChildShapes(); i < num; ++i)\n            {\n                assert(shape.getChildShape(i) == std::addressof(children[static_cast<std::size_t>(i)].getShape()));\n                result = children[static_cast<std::size_t>(i)].update(shape.getChildTransform(i), areaType) || result;\n            }\n            return result;\n        }\n\n        std::vector<RecastMeshObject> makeChildrenObjects(const osg::ref_ptr<const osg::Object>& holder,\n                                                          const btCompoundShape& shape, const AreaType areaType)\n        {\n            std::vector<RecastMeshObject> result;\n            for (int i = 0, num = shape.getNumChildShapes(); i < num; ++i)\n            {\n                const CollisionShape collisionShape {holder, *shape.getChildShape(i)};\n                result.emplace_back(collisionShape, shape.getChildTransform(i), areaType);\n            }\n            return result;\n        }\n\n        std::vector<RecastMeshObject> makeChildrenObjects(const osg::ref_ptr<const osg::Object>& holder,\n                                                          const btCollisionShape& shape, const AreaType areaType)\n        {\n            if (shape.isCompound())\n                return makeChildrenObjects(holder, static_cast<const btCompoundShape&>(shape), areaType);\n            return std::vector<RecastMeshObject>();\n        }\n    }\n\n    RecastMeshObject::RecastMeshObject(const CollisionShape& shape, const btTransform& transform,\n            const AreaType areaType)\n        : mHolder(shape.getHolder())\n        , mShape(shape.getShape())\n        , mTransform(transform)\n        , mAreaType(areaType)\n        , mLocalScaling(mShape.get().getLocalScaling())\n        , mChildren(makeChildrenObjects(mHolder, mShape.get(), mAreaType))\n    {\n    }\n\n    bool RecastMeshObject::update(const btTransform& transform, const AreaType areaType)\n    {\n        bool result = false;\n        if (!(mTransform == transform))\n        {\n            mTransform = transform;\n            result = true;\n        }\n        if (mAreaType != areaType)\n        {\n            mAreaType = areaType;\n            result = true;\n        }\n        if (!(mLocalScaling == mShape.get().getLocalScaling()))\n        {\n            mLocalScaling = mShape.get().getLocalScaling();\n            result = true;\n        }\n        if (mShape.get().isCompound())\n            result = updateCompoundObject(static_cast<const btCompoundShape&>(mShape.get()), mAreaType, mChildren)\n                    || result;\n        return result;\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/recastmeshobject.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHOBJECT_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHOBJECT_H\n\n#include \"areatype.hpp\"\n\n#include <LinearMath/btTransform.h>\n\n#include <osg/ref_ptr>\n#include <osg/Object>\n\n#include <functional>\n#include <vector>\n\nclass btCollisionShape;\nclass btCompoundShape;\n\nnamespace DetourNavigator\n{\n    class CollisionShape\n    {\n    public:\n        CollisionShape(osg::ref_ptr<const osg::Object> holder, const btCollisionShape& shape)\n            : mHolder(std::move(holder))\n            , mShape(shape)\n        {}\n\n        const osg::ref_ptr<const osg::Object>& getHolder() const { return mHolder; }\n        const btCollisionShape& getShape() const { return mShape; }\n\n    private:\n        osg::ref_ptr<const osg::Object> mHolder;\n        std::reference_wrapper<const btCollisionShape> mShape;\n    };\n\n    class RecastMeshObject\n    {\n        public:\n            RecastMeshObject(const CollisionShape& shape, const btTransform& transform, const AreaType areaType);\n\n            bool update(const btTransform& transform, const AreaType areaType);\n\n            const osg::ref_ptr<const osg::Object>& getHolder() const\n            {\n                return mHolder;\n            }\n\n            const btCollisionShape& getShape() const\n            {\n                return mShape;\n            }\n\n            const btTransform& getTransform() const\n            {\n                return mTransform;\n            }\n\n            AreaType getAreaType() const\n            {\n                return mAreaType;\n            }\n\n        private:\n            osg::ref_ptr<const osg::Object> mHolder;\n            std::reference_wrapper<const btCollisionShape> mShape;\n            btTransform mTransform;\n            AreaType mAreaType;\n            btVector3 mLocalScaling;\n            std::vector<RecastMeshObject> mChildren;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/recastmeshtiles.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHTILE_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHTILE_H\n\n#include \"tileposition.hpp\"\n\n#include <map>\n#include <memory>\n\nnamespace DetourNavigator\n{\n    class RecastMesh;\n\n    using RecastMeshTiles = std::map<TilePosition, std::shared_ptr<RecastMesh>>;\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/recasttempallocator.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTTEMPALLOCATOR_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTTEMPALLOCATOR_H\n\n#include \"recastallocutils.hpp\"\n\n#include <cassert>\n#include <memory>\n#include <vector>\n\nnamespace DetourNavigator\n{\n    class RecastTempAllocator\n    {\n    public:\n        RecastTempAllocator(std::size_t capacity)\n            : mStack(capacity), mTop(mStack.data()), mPrev(nullptr)\n        {}\n\n        void* alloc(std::size_t size)\n        {\n            std::size_t space = mStack.size() - getUsedSize();\n            void* top = mTop;\n            const auto itemSize = 2 * sizeof(std::size_t) + size;\n            if (rcUnlikely(!std::align(sizeof(std::size_t), itemSize, top, space)))\n                return nullptr;\n            setTempPtrBufferType(top, BufferType_temp);\n            setTempPtrPrev(top, mPrev);\n            mTop = static_cast<char*>(top) + itemSize;\n            mPrev = static_cast<char*>(top);\n            return getTempPtrDataPtr(top);\n        }\n\n        void free(void* ptr)\n        {\n            if (rcUnlikely(!ptr))\n                return;\n            assert(BufferType_temp == getDataPtrBufferType(ptr));\n            if (!mPrev || getTempDataPtrStackPtr(ptr) != mPrev)\n            {\n                setDataPtrBufferType(ptr, BufferType_unused);\n                return;\n            }\n            mTop = getTempDataPtrStackPtr(ptr);\n            mPrev = getTempPtrPrev(mTop);\n            while (mPrev && BufferType_unused == getTempPtrBufferType(mPrev))\n            {\n                mTop = mPrev;\n                mPrev = getTempPtrPrev(mTop);\n            }\n            return;\n        }\n\n    private:\n        std::vector<char> mStack;\n        void* mTop;\n        void* mPrev;\n\n        std::size_t getUsedSize() const\n        {\n            return static_cast<std::size_t>(static_cast<char*>(mTop) - mStack.data());\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/settings.cpp",
    "content": "#include \"settings.hpp\"\n\n#include <components/settings/settings.hpp>\n\nnamespace DetourNavigator\n{\n    std::optional<Settings> makeSettingsFromSettingsManager()\n    {\n        if (!::Settings::Manager::getBool(\"enable\", \"Navigator\"))\n            return std::optional<Settings>();\n\n        Settings navigatorSettings;\n\n        navigatorSettings.mBorderSize = ::Settings::Manager::getInt(\"border size\", \"Navigator\");\n        navigatorSettings.mCellHeight = ::Settings::Manager::getFloat(\"cell height\", \"Navigator\");\n        navigatorSettings.mCellSize = ::Settings::Manager::getFloat(\"cell size\", \"Navigator\");\n        navigatorSettings.mDetailSampleDist = ::Settings::Manager::getFloat(\"detail sample dist\", \"Navigator\");\n        navigatorSettings.mDetailSampleMaxError = ::Settings::Manager::getFloat(\"detail sample max error\", \"Navigator\");\n        navigatorSettings.mMaxClimb = 0;\n        navigatorSettings.mMaxSimplificationError = ::Settings::Manager::getFloat(\"max simplification error\", \"Navigator\");\n        navigatorSettings.mMaxSlope = 0;\n        navigatorSettings.mRecastScaleFactor = ::Settings::Manager::getFloat(\"recast scale factor\", \"Navigator\");\n        navigatorSettings.mSwimHeightScale = 0;\n        navigatorSettings.mMaxEdgeLen = ::Settings::Manager::getInt(\"max edge len\", \"Navigator\");\n        navigatorSettings.mMaxNavMeshQueryNodes = ::Settings::Manager::getInt(\"max nav mesh query nodes\", \"Navigator\");\n        navigatorSettings.mMaxPolys = ::Settings::Manager::getInt(\"max polygons per tile\", \"Navigator\");\n        navigatorSettings.mMaxTilesNumber = ::Settings::Manager::getInt(\"max tiles number\", \"Navigator\");\n        navigatorSettings.mMaxVertsPerPoly = ::Settings::Manager::getInt(\"max verts per poly\", \"Navigator\");\n        navigatorSettings.mRegionMergeSize = ::Settings::Manager::getInt(\"region merge size\", \"Navigator\");\n        navigatorSettings.mRegionMinSize = ::Settings::Manager::getInt(\"region min size\", \"Navigator\");\n        navigatorSettings.mTileSize = ::Settings::Manager::getInt(\"tile size\", \"Navigator\");\n        navigatorSettings.mWaitUntilMinDistanceToPlayer = ::Settings::Manager::getInt(\"wait until min distance to player\", \"Navigator\");\n        navigatorSettings.mAsyncNavMeshUpdaterThreads = static_cast<std::size_t>(::Settings::Manager::getInt(\"async nav mesh updater threads\", \"Navigator\"));\n        navigatorSettings.mMaxNavMeshTilesCacheSize = static_cast<std::size_t>(::Settings::Manager::getInt(\"max nav mesh tiles cache size\", \"Navigator\"));\n        navigatorSettings.mMaxPolygonPathSize = static_cast<std::size_t>(::Settings::Manager::getInt(\"max polygon path size\", \"Navigator\"));\n        navigatorSettings.mMaxSmoothPathSize = static_cast<std::size_t>(::Settings::Manager::getInt(\"max smooth path size\", \"Navigator\"));\n        navigatorSettings.mEnableWriteRecastMeshToFile = ::Settings::Manager::getBool(\"enable write recast mesh to file\", \"Navigator\");\n        navigatorSettings.mEnableWriteNavMeshToFile = ::Settings::Manager::getBool(\"enable write nav mesh to file\", \"Navigator\");\n        navigatorSettings.mRecastMeshPathPrefix = ::Settings::Manager::getString(\"recast mesh path prefix\", \"Navigator\");\n        navigatorSettings.mNavMeshPathPrefix = ::Settings::Manager::getString(\"nav mesh path prefix\", \"Navigator\");\n        navigatorSettings.mEnableRecastMeshFileNameRevision = ::Settings::Manager::getBool(\"enable recast mesh file name revision\", \"Navigator\");\n        navigatorSettings.mEnableNavMeshFileNameRevision = ::Settings::Manager::getBool(\"enable nav mesh file name revision\", \"Navigator\");\n        navigatorSettings.mMinUpdateInterval = std::chrono::milliseconds(::Settings::Manager::getInt(\"min update interval ms\", \"Navigator\"));\n\n        return navigatorSettings;\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/settings.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGS_H\n\n#include <chrono>\n#include <optional>\n#include <string>\n\nnamespace DetourNavigator\n{\n    struct Settings\n    {\n        bool mEnableWriteRecastMeshToFile = false;\n        bool mEnableWriteNavMeshToFile = false;\n        bool mEnableRecastMeshFileNameRevision = false;\n        bool mEnableNavMeshFileNameRevision = false;\n        float mCellHeight = 0;\n        float mCellSize = 0;\n        float mDetailSampleDist = 0;\n        float mDetailSampleMaxError = 0;\n        float mMaxClimb = 0;\n        float mMaxSimplificationError = 0;\n        float mMaxSlope = 0;\n        float mRecastScaleFactor = 0;\n        float mSwimHeightScale = 0;\n        int mBorderSize = 0;\n        int mMaxEdgeLen = 0;\n        int mMaxNavMeshQueryNodes = 0;\n        int mMaxPolys = 0;\n        int mMaxTilesNumber = 0;\n        int mMaxVertsPerPoly = 0;\n        int mRegionMergeSize = 0;\n        int mRegionMinSize = 0;\n        int mTileSize = 0;\n        int mWaitUntilMinDistanceToPlayer = 0;\n        std::size_t mAsyncNavMeshUpdaterThreads = 0;\n        std::size_t mMaxNavMeshTilesCacheSize = 0;\n        std::size_t mMaxPolygonPathSize = 0;\n        std::size_t mMaxSmoothPathSize = 0;\n        std::string mRecastMeshPathPrefix;\n        std::string mNavMeshPathPrefix;\n        std::chrono::milliseconds mMinUpdateInterval;\n    };\n\n    std::optional<Settings> makeSettingsFromSettingsManager();\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/settingsutils.hpp",
    "content": "﻿#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H\n\n#include \"settings.hpp\"\n#include \"tilebounds.hpp\"\n#include \"tileposition.hpp\"\n#include \"tilebounds.hpp\"\n\n#include <LinearMath/btTransform.h>\n\n#include <osg/Vec2f>\n#include <osg/Vec2i>\n#include <osg/Vec3f>\n\n#include <algorithm>\n#include <cmath>\n\nnamespace DetourNavigator\n{\n    inline float getHeight(const Settings& settings,const osg::Vec3f& agentHalfExtents)\n    {\n        return 2.0f * agentHalfExtents.z() * settings.mRecastScaleFactor;\n    }\n\n    inline float getMaxClimb(const Settings& settings)\n    {\n        return settings.mMaxClimb * settings.mRecastScaleFactor;\n    }\n\n    inline float getRadius(const Settings& settings, const osg::Vec3f& agentHalfExtents)\n    {\n        return std::max(agentHalfExtents.x(), agentHalfExtents.y()) * std::sqrt(2) * settings.mRecastScaleFactor;\n    }\n\n    inline float toNavMeshCoordinates(const Settings& settings, float value)\n    {\n        return value * settings.mRecastScaleFactor;\n    }\n\n    inline osg::Vec3f toNavMeshCoordinates(const Settings& settings, osg::Vec3f position)\n    {\n        std::swap(position.y(), position.z());\n        return position * settings.mRecastScaleFactor;\n    }\n\n    inline osg::Vec3f fromNavMeshCoordinates(const Settings& settings, osg::Vec3f position)\n    {\n        const auto factor = 1.0f / settings.mRecastScaleFactor;\n        position *= factor;\n        std::swap(position.y(), position.z());\n        return position;\n    }\n\n    inline float getTileSize(const Settings& settings)\n    {\n        return static_cast<float>(settings.mTileSize) * settings.mCellSize;\n    }\n\n    inline TilePosition getTilePosition(const Settings& settings, const osg::Vec3f& position)\n    {\n        return TilePosition(\n            static_cast<int>(std::floor(position.x() / getTileSize(settings))),\n            static_cast<int>(std::floor(position.z() / getTileSize(settings)))\n        );\n    }\n\n    inline TileBounds makeTileBounds(const Settings& settings, const TilePosition& tilePosition)\n    {\n        return TileBounds {\n            osg::Vec2f(tilePosition.x(), tilePosition.y()) * getTileSize(settings),\n            osg::Vec2f(tilePosition.x() + 1, tilePosition.y() + 1) * getTileSize(settings),\n        };\n    }\n\n    inline float getBorderSize(const Settings& settings)\n    {\n        return static_cast<float>(settings.mBorderSize) * settings.mCellSize;\n    }\n\n    inline float getSwimLevel(const Settings& settings, const float agentHalfExtentsZ)\n    {\n        return - settings.mSwimHeightScale * agentHalfExtentsZ;\n    }\n\n    inline btTransform getSwimLevelTransform(const Settings& settings, const btTransform& transform,\n        const float agentHalfExtentsZ)\n    {\n        return btTransform(\n            transform.getBasis(),\n            transform.getOrigin() + btVector3(0, 0, getSwimLevel(settings, agentHalfExtentsZ) - agentHalfExtentsZ)\n        );\n    }\n\n    inline float getRealTileSize(const Settings& settings)\n    {\n        return settings.mTileSize * settings.mCellSize / settings.mRecastScaleFactor;\n    }\n\n    inline float getMaxNavmeshAreaRadius(const Settings& settings)\n    {\n        return std::floor(std::sqrt(settings.mMaxTilesNumber / osg::PI)) - 1;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/sharednavmesh.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SHAREDNAVMESH_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_SHAREDNAVMESH_H\n\n#include <memory>\n\nclass dtNavMesh;\n\nnamespace DetourNavigator\n{\n    using NavMeshPtr = std::shared_ptr<dtNavMesh>;\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/status.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_STATUS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_STATUS_H\n\nnamespace DetourNavigator\n{\n    enum class Status\n    {\n        Success,\n        NavMeshNotFound,\n        StartPolygonNotFound,\n        EndPolygonNotFound,\n        MoveAlongSurfaceFailed,\n        FindPathOverPolygonsFailed,\n        GetPolyHeightFailed,\n        InitNavMeshQueryFailed,\n    };\n\n    constexpr const char* getMessage(Status value)\n    {\n        switch (value)\n        {\n            case Status::Success:\n                return \"success\";\n            case Status::NavMeshNotFound:\n                return \"navmesh is not found\";\n            case Status::StartPolygonNotFound:\n                return \"polygon for start position is not found on navmesh\";\n            case Status::EndPolygonNotFound:\n                return \"polygon for end position is not found on navmesh\";\n            case Status::MoveAlongSurfaceFailed:\n                return \"move along surface on navmesh is failed\";\n            case Status::FindPathOverPolygonsFailed:\n                return \"path over navmesh polygons is not found\";\n            case Status::GetPolyHeightFailed:\n                return \"failed to get polygon height\";\n            case Status::InitNavMeshQueryFailed:\n                return \"failed to init navmesh query\";\n        }\n        return \"unknown error\";\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/tilebounds.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEBOUNDS_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEBOUNDS_H\n\n#include <osg/Vec2f>\n\nnamespace DetourNavigator\n{\n    struct TileBounds\n    {\n        osg::Vec2f mMin;\n        osg::Vec2f mMax;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/tilecachedrecastmeshmanager.cpp",
    "content": "#include \"tilecachedrecastmeshmanager.hpp\"\n#include \"makenavmesh.hpp\"\n#include \"gettilespositions.hpp\"\n#include \"settingsutils.hpp\"\n\n#include <algorithm>\n#include <vector>\n\nnamespace DetourNavigator\n{\n    TileCachedRecastMeshManager::TileCachedRecastMeshManager(const Settings& settings)\n        : mSettings(settings)\n    {}\n\n    bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,\n                                                const btTransform& transform, const AreaType areaType)\n    {\n        std::vector<TilePosition> tilesPositions;\n        const auto border = getBorderSize(mSettings);\n        {\n            auto tiles = mTiles.lock();\n            getTilesPositions(shape.getShape(), transform, mSettings, [&] (const TilePosition& tilePosition)\n                {\n                    if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get()))\n                        tilesPositions.push_back(tilePosition);\n                });\n        }\n        if (tilesPositions.empty())\n            return false;\n        std::sort(tilesPositions.begin(), tilesPositions.end());\n        mObjectsTilesPositions.insert_or_assign(id, std::move(tilesPositions));\n        ++mRevision;\n        return true;\n    }\n\n    std::optional<RemovedRecastMeshObject> TileCachedRecastMeshManager::removeObject(const ObjectId id)\n    {\n        const auto object = mObjectsTilesPositions.find(id);\n        if (object == mObjectsTilesPositions.end())\n            return std::nullopt;\n        std::optional<RemovedRecastMeshObject> result;\n        {\n            auto tiles = mTiles.lock();\n            for (const auto& tilePosition : object->second)\n            {\n                const auto removed = removeTile(id, tilePosition, tiles.get());\n                if (removed && !result)\n                    result = removed;\n            }\n        }\n        if (result)\n            ++mRevision;\n        return result;\n    }\n\n    bool TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize,\n        const btTransform& transform)\n    {\n        const auto border = getBorderSize(mSettings);\n\n        auto& tilesPositions = mWaterTilesPositions[cellPosition];\n\n        bool result = false;\n\n        if (cellSize == std::numeric_limits<int>::max())\n        {\n            const auto tiles = mTiles.lock();\n            for (auto& tile : *tiles)\n            {\n                if (tile.second->addWater(cellPosition, cellSize, transform))\n                {\n                    tilesPositions.push_back(tile.first);\n                    result = true;\n                }\n            }\n        }\n        else\n        {\n            getTilesPositions(cellSize, transform, mSettings, [&] (const TilePosition& tilePosition)\n                {\n                    const auto tiles = mTiles.lock();\n                    auto tile = tiles->find(tilePosition);\n                    if (tile == tiles->end())\n                    {\n                        auto tileBounds = makeTileBounds(mSettings, tilePosition);\n                        tileBounds.mMin -= osg::Vec2f(border, border);\n                        tileBounds.mMax += osg::Vec2f(border, border);\n                        tile = tiles->insert(std::make_pair(tilePosition,\n                                std::make_shared<CachedRecastMeshManager>(mSettings, tileBounds, mTilesGeneration))).first;\n                    }\n                    if (tile->second->addWater(cellPosition, cellSize, transform))\n                    {\n                        tilesPositions.push_back(tilePosition);\n                        result = true;\n                    }\n                });\n        }\n\n        if (result)\n            ++mRevision;\n\n        return result;\n    }\n\n    std::optional<RecastMeshManager::Water> TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition)\n    {\n        const auto object = mWaterTilesPositions.find(cellPosition);\n        if (object == mWaterTilesPositions.end())\n            return std::nullopt;\n        std::optional<RecastMeshManager::Water> result;\n        for (const auto& tilePosition : object->second)\n        {\n            const auto tiles = mTiles.lock();\n            const auto tile = tiles->find(tilePosition);\n            if (tile == tiles->end())\n                continue;\n            const auto tileResult = tile->second->removeWater(cellPosition);\n            if (tile->second->isEmpty())\n            {\n                tiles->erase(tile);\n                ++mTilesGeneration;\n            }\n            if (tileResult && !result)\n                result = tileResult;\n        }\n        if (result)\n            ++mRevision;\n        return result;\n    }\n\n    std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(const TilePosition& tilePosition)\n    {\n        const auto manager = [&] () -> std::shared_ptr<CachedRecastMeshManager>\n        {\n            const auto tiles = mTiles.lock();\n            const auto it = tiles->find(tilePosition);\n            if (it == tiles->end())\n                return nullptr;\n            return it->second;\n        } ();\n        if (manager == nullptr)\n            return nullptr;\n        return manager->getMesh();\n    }\n\n    bool TileCachedRecastMeshManager::hasTile(const TilePosition& tilePosition)\n    {\n        return mTiles.lockConst()->count(tilePosition);\n    }\n\n    std::size_t TileCachedRecastMeshManager::getRevision() const\n    {\n        return mRevision;\n    }\n\n    void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion)\n    {\n        const auto tiles = mTiles.lock();\n        const auto it = tiles->find(tilePosition);\n        if (it == tiles->end())\n            return;\n        it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion);\n    }\n\n    bool TileCachedRecastMeshManager::addTile(const ObjectId id, const CollisionShape& shape,\n        const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition, float border,\n        TilesMap& tiles)\n    {\n        auto tile = tiles.find(tilePosition);\n        if (tile == tiles.end())\n        {\n            auto tileBounds = makeTileBounds(mSettings, tilePosition);\n            tileBounds.mMin -= osg::Vec2f(border, border);\n            tileBounds.mMax += osg::Vec2f(border, border);\n            tile = tiles.insert(std::make_pair(\n                tilePosition, std::make_shared<CachedRecastMeshManager>(mSettings, tileBounds, mTilesGeneration))).first;\n        }\n        return tile->second->addObject(id, shape, transform, areaType);\n    }\n\n    bool TileCachedRecastMeshManager::updateTile(const ObjectId id, const btTransform& transform,\n        const AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles)\n    {\n        const auto tile = tiles.find(tilePosition);\n        return tile != tiles.end() && tile->second->updateObject(id, transform, areaType);\n    }\n\n    std::optional<RemovedRecastMeshObject> TileCachedRecastMeshManager::removeTile(const ObjectId id,\n        const TilePosition& tilePosition, TilesMap& tiles)\n    {\n        const auto tile = tiles.find(tilePosition);\n        if (tile == tiles.end())\n            return std::optional<RemovedRecastMeshObject>();\n        const auto tileResult = tile->second->removeObject(id);\n        if (tile->second->isEmpty())\n        {\n            tiles.erase(tile);\n            ++mTilesGeneration;\n        }\n        return tileResult;\n    }\n}\n"
  },
  {
    "path": "components/detournavigator/tilecachedrecastmeshmanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILECACHEDRECASTMESHMANAGER_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILECACHEDRECASTMESHMANAGER_H\n\n#include \"cachedrecastmeshmanager.hpp\"\n#include \"tileposition.hpp\"\n#include \"settingsutils.hpp\"\n#include \"gettilespositions.hpp\"\n#include \"version.hpp\"\n\n#include <components/misc/guarded.hpp>\n\n#include <algorithm>\n#include <map>\n#include <mutex>\n#include <vector>\n\nnamespace DetourNavigator\n{\n    class TileCachedRecastMeshManager\n    {\n    public:\n        TileCachedRecastMeshManager(const Settings& settings);\n\n        bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                       const AreaType areaType);\n\n        template <class OnChangedTile>\n        bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n            const AreaType areaType, OnChangedTile&& onChangedTile)\n        {\n            const auto object = mObjectsTilesPositions.find(id);\n            if (object == mObjectsTilesPositions.end())\n                return false;\n            auto& currentTiles = object->second;\n            const auto border = getBorderSize(mSettings);\n            bool changed = false;\n            std::vector<TilePosition> newTiles;\n            {\n                auto tiles = mTiles.lock();\n                const auto onTilePosition = [&] (const TilePosition& tilePosition)\n                {\n                    if (std::binary_search(currentTiles.begin(), currentTiles.end(), tilePosition))\n                    {\n                        newTiles.push_back(tilePosition);\n                        if (updateTile(id, transform, areaType, tilePosition, tiles.get()))\n                        {\n                            onChangedTile(tilePosition);\n                            changed = true;\n                        }\n                    }\n                    else if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get()))\n                    {\n                        newTiles.push_back(tilePosition);\n                        onChangedTile(tilePosition);\n                        changed = true;\n                    }\n                };\n                getTilesPositions(shape.getShape(), transform, mSettings, onTilePosition);\n                std::sort(newTiles.begin(), newTiles.end());\n                for (const auto& tile : currentTiles)\n                {\n                    if (!std::binary_search(newTiles.begin(), newTiles.end(), tile) && removeTile(id, tile, tiles.get()))\n                    {\n                        onChangedTile(tile);\n                        changed = true;\n                    }\n                }\n            }\n            if (changed)\n            {\n                currentTiles = std::move(newTiles);\n                ++mRevision;\n            }\n            return changed;\n        }\n\n        std::optional<RemovedRecastMeshObject> removeObject(const ObjectId id);\n\n        bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform);\n\n        std::optional<RecastMeshManager::Water> removeWater(const osg::Vec2i& cellPosition);\n\n        std::shared_ptr<RecastMesh> getMesh(const TilePosition& tilePosition);\n\n        bool hasTile(const TilePosition& tilePosition);\n\n        template <class Function>\n        void forEachTile(Function&& function)\n        {\n            for (auto& [tilePosition, recastMeshManager] : *mTiles.lock())\n                function(tilePosition, *recastMeshManager);\n        }\n\n        std::size_t getRevision() const;\n\n        void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion);\n\n    private:\n        using TilesMap = std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>;\n\n        const Settings& mSettings;\n        Misc::ScopeGuarded<TilesMap> mTiles;\n        std::unordered_map<ObjectId, std::vector<TilePosition>> mObjectsTilesPositions;\n        std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;\n        std::size_t mRevision = 0;\n        std::size_t mTilesGeneration = 0;\n\n        bool addTile(const ObjectId id, const CollisionShape& shape, const btTransform& transform,\n                const AreaType areaType, const TilePosition& tilePosition, float border, TilesMap& tiles);\n\n        bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType,\n                const TilePosition& tilePosition, TilesMap& tiles);\n\n        std::optional<RemovedRecastMeshObject> removeTile(const ObjectId id, const TilePosition& tilePosition,\n                TilesMap& tiles);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/tileposition.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEPOSITION_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEPOSITION_H\n\n#include <osg/Vec2i>\n\nnamespace DetourNavigator\n{\n    using TilePosition = osg::Vec2i;\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/version.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_VERSION_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_VERSION_H\n\n#include <cstddef>\n#include <tuple>\n\nnamespace DetourNavigator\n{\n    struct Version\n    {\n        std::size_t mGeneration;\n        std::size_t mRevision;\n\n        friend inline bool operator<(const Version& lhs, const Version& rhs)\n        {\n            return std::tie(lhs.mGeneration, lhs.mRevision) < std::tie(rhs.mGeneration, rhs.mRevision);\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/detournavigator/waitconditiontype.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_WAITCONDITIONTYPE_H\n#define OPENMW_COMPONENTS_DETOURNAVIGATOR_WAITCONDITIONTYPE_H\n\nnamespace DetourNavigator\n{\n    enum class WaitConditionType\n    {\n        requiredTilesPresent,\n        allJobsDone,\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/doc.hpp",
    "content": "// Note: This is not a regular source file.\n\n/// \\defgroup components Components\n\n/// \\namespace ESMS\n/// \\ingroup components\n/// \\brief ESM/ESP record store\n\n/// \\namespace ESM\n/// \\ingroup components\n/// \\brief ESM/ESP records\n\n/// \\namespace FileFinder\n/// \\ingroup components\n/// \\brief Linux/Windows-path resolving\n\n/// \\namespace ToUTF\n/// \\ingroup components\n/// \\brief Text encoding\n\n/// \\namespace Compiler\n/// \\ingroup components\n/// \\brief script compiler\n\n/// \\namespace Interpreter\n/// \\ingroup components\n/// \\brief script interpreter\n\n// TODO put nif and nifogre in different namespaces (or merge them)\n// TODO put other components into namespaces\n"
  },
  {
    "path": "components/esm/activespells.cpp",
    "content": "#include \"activespells.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM\n{\n\n    void ActiveSpells::save(ESMWriter &esm) const\n    {\n        for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)\n        {\n            esm.writeHNString (\"ID__\", it->first);\n\n            const ActiveSpellParams& params = it->second;\n\n            esm.writeHNT (\"CAST\", params.mCasterActorId);\n            esm.writeHNString (\"DISP\", params.mDisplayName);\n\n            for (std::vector<ActiveEffect>::const_iterator effectIt = params.mEffects.begin(); effectIt != params.mEffects.end(); ++effectIt)\n            {\n                esm.writeHNT (\"MGEF\", effectIt->mEffectId);\n                if (effectIt->mArg != -1)\n                    esm.writeHNT (\"ARG_\", effectIt->mArg);\n                esm.writeHNT (\"MAGN\", effectIt->mMagnitude);\n                esm.writeHNT (\"DURA\", effectIt->mDuration);\n                esm.writeHNT (\"EIND\", effectIt->mEffectIndex);\n                esm.writeHNT (\"LEFT\", effectIt->mTimeLeft);\n            }\n        }\n    }\n\n    void ActiveSpells::load(ESMReader &esm)\n    {\n        int format = esm.getFormat();\n\n        while (esm.isNextSub(\"ID__\"))\n        {\n            std::string spellId = esm.getHString();\n\n            ActiveSpellParams params;\n            esm.getHNT (params.mCasterActorId, \"CAST\");\n            params.mDisplayName = esm.getHNString (\"DISP\");\n\n            // spell casting timestamp, no longer used\n            if (esm.isNextSub(\"TIME\"))\n                esm.skipHSub();\n\n            while (esm.isNextSub(\"MGEF\"))\n            {\n                ActiveEffect effect;\n                esm.getHT(effect.mEffectId);\n                effect.mArg = -1;\n                esm.getHNOT(effect.mArg, \"ARG_\");\n                esm.getHNT (effect.mMagnitude, \"MAGN\");\n                esm.getHNT (effect.mDuration, \"DURA\");\n                effect.mEffectIndex = -1;\n                esm.getHNOT (effect.mEffectIndex, \"EIND\");\n                if (format < 9)\n                    effect.mTimeLeft = effect.mDuration;\n                else\n                    esm.getHNT (effect.mTimeLeft, \"LEFT\");\n\n                params.mEffects.push_back(effect);\n            }\n            mSpells.insert(std::make_pair(spellId, params));\n        }\n    }\n}\n"
  },
  {
    "path": "components/esm/activespells.hpp",
    "content": "#ifndef OPENMW_ESM_ACTIVESPELLS_H\n#define OPENMW_ESM_ACTIVESPELLS_H\n\n#include \"effectlist.hpp\"\n#include \"defs.hpp\"\n\n#include <string>\n#include <map>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // Parameters of an effect concerning lasting effects.\n    // Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc.\n    // It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster.\n    struct ActiveEffect\n    {\n        int mEffectId;\n        float mMagnitude;\n        int mArg; // skill or attribute\n        float mDuration;\n        float mTimeLeft;\n        int mEffectIndex;\n    };\n\n    // format 0, saved games only\n    struct ActiveSpells\n    {\n        struct ActiveSpellParams\n        {\n            std::vector<ActiveEffect> mEffects;\n            std::string mDisplayName;\n            int mCasterActorId;\n        };\n\n        typedef std::multimap<std::string, ActiveSpellParams > TContainer;\n        TContainer mSpells;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/aipackage.cpp",
    "content": "#include \"aipackage.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM\n{\n    void AIData::blank()\n    {\n        mHello = mFight = mFlee = mAlarm = mU1 = mU2 = mU3 = 0;\n        mServices = 0;\n    }\n\n    void AIPackageList::add(ESMReader &esm)\n    {\n        AIPackage pack;\n        if (esm.retSubName() == AI_CNDT) {\n            if (mList.empty()) \n            {\n                esm.fail(\"AIPackge with an AI_CNDT applying to no cell.\");\n            } else {\n                mList.back().mCellName = esm.getHString();\n            }\n        } else if (esm.retSubName() == AI_Wander) {\n            pack.mType = AI_Wander;\n            esm.getHExact(&pack.mWander, 14);\n            mList.push_back(pack);\n        } else if (esm.retSubName() == AI_Travel) {\n            pack.mType = AI_Travel;\n            esm.getHExact(&pack.mTravel, 16);\n            mList.push_back(pack);\n        } else if (esm.retSubName() == AI_Escort ||\n                   esm.retSubName() == AI_Follow)\n        {\n            pack.mType =\n                (esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow;\n                esm.getHExact(&pack.mTarget, 48);\n            mList.push_back(pack);\n        } else if (esm.retSubName() == AI_Activate) {\n            pack.mType = AI_Activate;\n            esm.getHExact(&pack.mActivate, 33);\n            mList.push_back(pack);\n        } else { // not AI package related data, so leave\n             return;\n        }\n\n    }\n\n    void AIPackageList::save(ESMWriter &esm) const\n    {\n        typedef std::vector<AIPackage>::const_iterator PackageIter;\n        for (PackageIter it = mList.begin(); it != mList.end(); ++it) {\n            switch (it->mType) {\n            case AI_Wander:\n                esm.writeHNT(\"AI_W\", it->mWander, sizeof(it->mWander));\n                break;\n\n            case AI_Travel:\n                esm.writeHNT(\"AI_T\", it->mTravel, sizeof(it->mTravel));\n                break;\n\n            case AI_Activate:\n                esm.writeHNT(\"AI_A\", it->mActivate, sizeof(it->mActivate));\n                break;\n\n            case AI_Escort:\n            case AI_Follow: {\n                const char *name = (it->mType == AI_Escort) ? \"AI_E\" : \"AI_F\";\n                esm.writeHNT(name, it->mTarget, sizeof(it->mTarget));\n                esm.writeHNOCString(\"CNDT\", it->mCellName);\n                break;\n            }\n\n            default:\n                break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "components/esm/aipackage.hpp",
    "content": "#ifndef OPENMW_ESM_AIPACKAGE_H\n#define OPENMW_ESM_AIPACKAGE_H\n\n#include <vector>\n#include <string>\n\n#include \"esmcommon.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    #pragma pack(push)\n    #pragma pack(1)\n\n    struct AIData\n    {\n        unsigned short mHello; // This is the base value for greeting distance [0, 65535]\n        unsigned char mFight, mFlee, mAlarm; // These are probabilities [0, 100]\n        char mU1, mU2, mU3; // Unknown values\n        int mServices; // See the Services enum\n\n        void blank();\n        ///< Set record to default state (does not touch the ID).\n    }; // 12 bytes\n\n    struct AIWander\n    {\n        short   mDistance;\n        short   mDuration;\n        unsigned char mTimeOfDay;\n        unsigned char mIdle[8];\n        unsigned char mShouldRepeat;\n    };\n\n    struct AITravel\n    {\n        float   mX, mY, mZ;\n        int     mUnk;\n    };\n\n    struct AITarget\n    {\n        float   mX, mY, mZ;\n        short   mDuration;\n        NAME32  mId;\n        short   mUnk;\n    };\n\n    struct AIActivate\n    {\n        NAME32 mName;\n        unsigned char mUnk;\n    };\n\n    #pragma pack(pop)\n\n    enum\n    {\n        AI_Wander = 0x575f4941,\n        AI_Travel = 0x545f4941,\n        AI_Follow = 0x465f4941,\n        AI_Escort = 0x455f4941,\n        AI_Activate = 0x415f4941,\n        AI_CNDT = 0x54444e43\n    };\n\n    /// \\note Used for storaging packages in a single container\n    /// w/o manual memory allocation accordingly to policy standards\n    struct AIPackage\n    {\n        int mType;\n\n        // Anonymous union\n        union\n        {\n            AIWander mWander;\n            AITravel mTravel;\n            AITarget mTarget;\n            AIActivate mActivate;\n        };\n\n        /// \\note for AITarget only, placed here to stick with union,\n        /// overhead should be not so awful\n        std::string mCellName;\n    };\n\n    struct AIPackageList\n    {\n        std::vector<AIPackage> mList;\n\n        /// Add a single AIPackage, assumes subrecord name was already read\n        void add(ESMReader &esm);\n\n        void save(ESMWriter &esm) const;\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "components/esm/aisequence.cpp",
    "content": "#include \"aisequence.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\n#include <memory>\n\nnamespace ESM\n{\nnamespace AiSequence\n{\n\n    void AiWander::load(ESMReader &esm)\n    {\n        esm.getHNT (mData, \"DATA\");\n        esm.getHNT(mDurationData, \"STAR\"); // was mStartTime\n        mStoredInitialActorPosition = false;\n        if (esm.isNextSub(\"POS_\"))\n        {\n            mStoredInitialActorPosition = true;\n            esm.getHT(mInitialActorPosition);\n        }\n    }\n\n    void AiWander::save(ESMWriter &esm) const\n    {\n        esm.writeHNT (\"DATA\", mData);\n        esm.writeHNT (\"STAR\", mDurationData);\n        if (mStoredInitialActorPosition)\n            esm.writeHNT (\"POS_\", mInitialActorPosition);\n    }\n\n    void AiTravel::load(ESMReader &esm)\n    {\n        esm.getHNT (mData, \"DATA\");\n        esm.getHNOT (mHidden, \"HIDD\");\n    }\n\n    void AiTravel::save(ESMWriter &esm) const\n    {\n        esm.writeHNT (\"DATA\", mData);\n        esm.writeHNT (\"HIDD\", mHidden);\n    }\n\n    void AiEscort::load(ESMReader &esm)\n    {\n        esm.getHNT (mData, \"DATA\");\n        mTargetId = esm.getHNString(\"TARG\");\n        mTargetActorId = -1;\n        esm.getHNOT (mTargetActorId, \"TAID\");\n        esm.getHNT (mRemainingDuration, \"DURA\");\n        mCellId = esm.getHNOString (\"CELL\");\n    }\n\n    void AiEscort::save(ESMWriter &esm) const\n    {\n        esm.writeHNT (\"DATA\", mData);\n        esm.writeHNString (\"TARG\", mTargetId);\n        esm.writeHNT (\"TAID\", mTargetActorId);\n        esm.writeHNT (\"DURA\", mRemainingDuration);\n        if (!mCellId.empty())\n            esm.writeHNString (\"CELL\", mCellId);\n    }\n\n    void AiFollow::load(ESMReader &esm)\n    {\n        esm.getHNT (mData, \"DATA\");\n        mTargetId = esm.getHNString(\"TARG\");\n        mTargetActorId = -1;\n        esm.getHNOT (mTargetActorId, \"TAID\");\n        esm.getHNT (mRemainingDuration, \"DURA\");\n        mCellId = esm.getHNOString (\"CELL\");\n        esm.getHNT (mAlwaysFollow, \"ALWY\");\n        mCommanded = false;\n        esm.getHNOT (mCommanded, \"CMND\");\n        mActive = false;\n        esm.getHNOT (mActive, \"ACTV\");\n    }\n\n    void AiFollow::save(ESMWriter &esm) const\n    {\n        esm.writeHNT (\"DATA\", mData);\n        esm.writeHNString(\"TARG\", mTargetId);\n        esm.writeHNT (\"TAID\", mTargetActorId);\n        esm.writeHNT (\"DURA\", mRemainingDuration);\n        if (!mCellId.empty())\n            esm.writeHNString (\"CELL\", mCellId);\n        esm.writeHNT (\"ALWY\", mAlwaysFollow);\n        esm.writeHNT (\"CMND\", mCommanded);\n        if (mActive)\n            esm.writeHNT(\"ACTV\", mActive);\n    }\n\n    void AiActivate::load(ESMReader &esm)\n    {\n        mTargetId = esm.getHNString(\"TARG\");\n    }\n\n    void AiActivate::save(ESMWriter &esm) const\n    {\n        esm.writeHNString(\"TARG\", mTargetId);\n    }\n\n    void AiCombat::load(ESMReader &esm)\n    {\n        esm.getHNT (mTargetActorId, \"TARG\");\n    }\n\n    void AiCombat::save(ESMWriter &esm) const\n    {\n        esm.writeHNT (\"TARG\", mTargetActorId);\n    }\n\n    void AiPursue::load(ESMReader &esm)\n    {\n        esm.getHNT (mTargetActorId, \"TARG\");\n    }\n\n    void AiPursue::save(ESMWriter &esm) const\n    {\n        esm.writeHNT (\"TARG\", mTargetActorId);\n    }\n\n    AiSequence::~AiSequence()\n    {\n        for (std::vector<AiPackageContainer>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)\n            delete it->mPackage;\n    }\n\n    void AiSequence::save(ESMWriter &esm) const\n    {\n        for (std::vector<AiPackageContainer>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)\n        {\n            esm.writeHNT (\"AIPK\", it->mType);\n            switch (it->mType)\n            {\n            case Ai_Wander:\n                static_cast<const AiWander*>(it->mPackage)->save(esm);\n                break;\n            case Ai_Travel:\n                static_cast<const AiTravel*>(it->mPackage)->save(esm);\n                break;\n            case Ai_Escort:\n                static_cast<const AiEscort*>(it->mPackage)->save(esm);\n                break;\n            case Ai_Follow:\n                static_cast<const AiFollow*>(it->mPackage)->save(esm);\n                break;\n            case Ai_Activate:\n                static_cast<const AiActivate*>(it->mPackage)->save(esm);\n                break;\n            case Ai_Combat:\n                static_cast<const AiCombat*>(it->mPackage)->save(esm);\n                break;\n            case Ai_Pursue:\n                static_cast<const AiPursue*>(it->mPackage)->save(esm);\n                break;\n\n            default:\n                break;\n            }\n        }\n\n        esm.writeHNT (\"LAST\", mLastAiPackage);\n    }\n\n    void AiSequence::load(ESMReader &esm)\n    {\n        while (esm.isNextSub(\"AIPK\"))\n        {\n            int type;\n            esm.getHT(type);\n\n            mPackages.emplace_back();\n            mPackages.back().mType = type;\n\n            switch (type)\n            {\n            case Ai_Wander:\n            {\n                std::unique_ptr<AiWander> ptr (new AiWander());\n                ptr->load(esm);\n                mPackages.back().mPackage = ptr.release();\n                break;\n            }\n            case Ai_Travel:\n            {\n                std::unique_ptr<AiTravel> ptr (new AiTravel());\n                ptr->load(esm);\n                mPackages.back().mPackage = ptr.release();\n                break;\n            }\n            case Ai_Escort:\n            {\n                std::unique_ptr<AiEscort> ptr (new AiEscort());\n                ptr->load(esm);\n                mPackages.back().mPackage = ptr.release();\n                break;\n            }\n            case Ai_Follow:\n            {\n                std::unique_ptr<AiFollow> ptr (new AiFollow());\n                ptr->load(esm);\n                mPackages.back().mPackage = ptr.release();\n                break;\n            }\n            case Ai_Activate:\n            {\n                std::unique_ptr<AiActivate> ptr (new AiActivate());\n                ptr->load(esm);\n                mPackages.back().mPackage = ptr.release();\n                break;\n            }\n            case Ai_Combat:\n            {\n                std::unique_ptr<AiCombat> ptr (new AiCombat());\n                ptr->load(esm);\n                mPackages.back().mPackage = ptr.release();\n                break;\n            }\n            case Ai_Pursue:\n            {\n                std::unique_ptr<AiPursue> ptr (new AiPursue());\n                ptr->load(esm);\n                mPackages.back().mPackage = ptr.release();\n                break;\n            }\n            default:\n                return;\n            }\n        }\n\n        esm.getHNOT (mLastAiPackage, \"LAST\");\n    }\n}\n}\n"
  },
  {
    "path": "components/esm/aisequence.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_ESM_AISEQUENCE_H\n#define OPENMW_COMPONENTS_ESM_AISEQUENCE_H\n\n#include <vector>\n#include <string>\n\n#include \"defs.hpp\"\n\n#include \"util.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    namespace AiSequence\n    {\n\n    // format 0, saved games only\n    // As opposed to AiPackageList, this stores the \"live\" version of AI packages.\n\n    enum AiPackages\n    {\n        Ai_Wander = ESM::FourCC<'W','A','N','D'>::value,\n        Ai_Travel = ESM::FourCC<'T','R','A','V'>::value,\n        Ai_Escort = ESM::FourCC<'E','S','C','O'>::value,\n        Ai_Follow = ESM::FourCC<'F','O','L','L'>::value,\n        Ai_Activate = ESM::FourCC<'A','C','T','I'>::value,\n        Ai_Combat = ESM::FourCC<'C','O','M','B'>::value,\n        Ai_Pursue = ESM::FourCC<'P','U','R','S'>::value\n    };\n\n\n    struct AiPackage\n    {\n        virtual ~AiPackage() {}\n    };\n\n\n#pragma pack(push,1)\n    struct AiWanderData\n    {\n        short   mDistance;\n        short   mDuration;\n        unsigned char mTimeOfDay;\n        unsigned char mIdle[8];\n        unsigned char mShouldRepeat;\n    };\n    struct AiWanderDuration\n    {\n        float mRemainingDuration;\n        int unused;\n    };\n    struct AiTravelData\n    {\n        float   mX, mY, mZ;\n    };\n    struct AiEscortData\n    {\n        float mX, mY, mZ;\n        short   mDuration;\n    };\n\n#pragma pack(pop)\n\n    struct AiWander : AiPackage\n    {\n        AiWanderData mData;\n        AiWanderDuration mDurationData; // was ESM::TimeStamp mStartTime\n\n        bool mStoredInitialActorPosition;\n        ESM::Vector3 mInitialActorPosition;\n\n        /// \\todo add more AiWander state\n\n        void load(ESMReader &esm);\n        void save(ESMWriter &esm) const;\n    };\n\n    struct AiTravel : AiPackage\n    {\n        AiTravelData mData;\n        bool mHidden;\n\n        void load(ESMReader &esm);\n        void save(ESMWriter &esm) const;\n    };\n\n    struct AiEscort : AiPackage\n    {\n        AiEscortData mData;\n\n        int mTargetActorId;\n        std::string mTargetId;\n        std::string mCellId;\n        float mRemainingDuration;\n\n        void load(ESMReader &esm);\n        void save(ESMWriter &esm) const;\n    };\n\n    struct AiFollow : AiPackage\n    {\n        AiEscortData mData;\n\n        int mTargetActorId;\n        std::string mTargetId;\n        std::string mCellId;\n        float mRemainingDuration;\n\n        bool mAlwaysFollow;\n        bool mCommanded;\n\n        bool mActive;\n\n        void load(ESMReader &esm);\n        void save(ESMWriter &esm) const;\n    };\n\n    struct AiActivate : AiPackage\n    {\n        std::string mTargetId;\n\n        void load(ESMReader &esm);\n        void save(ESMWriter &esm) const;\n    };\n\n    struct AiCombat : AiPackage\n    {\n        int mTargetActorId;\n\n        void load(ESMReader &esm);\n        void save(ESMWriter &esm) const;\n    };\n\n    struct AiPursue : AiPackage\n    {\n        int mTargetActorId;\n\n        void load(ESMReader &esm);\n        void save(ESMWriter &esm) const;\n    };\n\n    struct AiPackageContainer\n    {\n        int mType;\n\n        AiPackage* mPackage;\n    };\n\n    struct AiSequence\n    {\n        AiSequence()\n        {\n            mLastAiPackage = -1;\n        }\n        ~AiSequence();\n\n        std::vector<AiPackageContainer> mPackages;\n        int mLastAiPackage;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n\n    private:\n        AiSequence(const AiSequence&);\n        AiSequence& operator=(const AiSequence&);\n    };\n\n    }\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/animationstate.cpp",
    "content": "#include \"animationstate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM\n{\n    bool AnimationState::empty() const\n    {\n        return mScriptedAnims.empty();\n    }\n\n    void AnimationState::load(ESMReader& esm)\n    {\n        mScriptedAnims.clear();\n\n        while (esm.isNextSub(\"ANIS\"))\n        {\n            ScriptedAnimation anim;\n\n            anim.mGroup = esm.getHString();\n            esm.getHNOT(anim.mTime, \"TIME\");\n            esm.getHNOT(anim.mAbsolute, \"ABST\");\n\n            esm.getSubNameIs(\"COUN\");\n            // workaround bug in earlier version where size_t was used\n            esm.getSubHeader();\n            if (esm.getSubSize() == 8)\n                esm.getT(anim.mLoopCount);\n            else\n            {\n                uint32_t loopcount;\n                esm.getT(loopcount);\n                anim.mLoopCount = (uint64_t) loopcount;\n            }\n\n            mScriptedAnims.push_back(anim);\n        }\n    }\n\n    void AnimationState::save(ESMWriter& esm) const\n    {\n        for (ScriptedAnimations::const_iterator iter = mScriptedAnims.begin(); iter != mScriptedAnims.end(); ++iter)\n        {\n            esm.writeHNString(\"ANIS\", iter->mGroup);\n            if (iter->mTime > 0)\n                esm.writeHNT(\"TIME\", iter->mTime);\n            if (iter->mAbsolute)\n                esm.writeHNT(\"ABST\", iter->mAbsolute);\n            esm.writeHNT(\"COUN\", iter->mLoopCount);\n        }\n    }\n}\n"
  },
  {
    "path": "components/esm/animationstate.hpp",
    "content": "#ifndef OPENMW_ESM_ANIMATIONSTATE_H\n#define OPENMW_ESM_ANIMATIONSTATE_H\n\n#include <string>\n#include <vector>\n#include <stdint.h>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n    struct AnimationState\n    {\n        struct ScriptedAnimation\n        {\n            ScriptedAnimation()\n                : mTime(0.f), mAbsolute(false), mLoopCount(0) {}\n\n            std::string mGroup;\n            float mTime;\n            bool mAbsolute;\n            uint64_t mLoopCount;\n        };\n\n        typedef std::vector<ScriptedAnimation> ScriptedAnimations;\n        ScriptedAnimations mScriptedAnims;\n\n        bool empty() const;\n\n        void load(ESMReader& esm);\n        void save(ESMWriter& esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/attr.cpp",
    "content": "#include \"attr.hpp\"\n\nusing namespace ESM;\n\nconst Attribute::AttributeID Attribute::sAttributeIds[Attribute::Length] = {\n    Attribute::Strength,\n    Attribute::Intelligence,\n    Attribute::Willpower,\n    Attribute::Agility,\n    Attribute::Speed,\n    Attribute::Endurance,\n    Attribute::Personality,\n    Attribute::Luck\n};\n\nconst std::string Attribute::sAttributeNames[Attribute::Length] = {\n    \"Strength\",\n    \"Intelligence\",\n    \"Willpower\",\n    \"Agility\",\n    \"Speed\",\n    \"Endurance\",\n    \"Personality\",\n    \"Luck\"\n};\n\nconst std::string Attribute::sGmstAttributeIds[Attribute::Length] = {\n    \"sAttributeStrength\",\n    \"sAttributeIntelligence\",\n    \"sAttributeWillpower\",\n    \"sAttributeAgility\",\n    \"sAttributeSpeed\",\n    \"sAttributeEndurance\",\n    \"sAttributePersonality\",\n    \"sAttributeLuck\"\n};\n\nconst std::string Attribute::sGmstAttributeDescIds[Attribute::Length] = {\n    \"sStrDesc\",\n    \"sIntDesc\",\n    \"sWilDesc\",\n    \"sAgiDesc\",\n    \"sSpdDesc\",\n    \"sEndDesc\",\n    \"sPerDesc\",\n    \"sLucDesc\"\n};\n\nconst std::string Attribute::sAttributeIcons[Attribute::Length] = {\n    \"icons\\\\k\\\\attribute_strength.dds\",\n    \"icons\\\\k\\\\attribute_int.dds\",\n    \"icons\\\\k\\\\attribute_wilpower.dds\",\n    \"icons\\\\k\\\\attribute_agility.dds\",\n    \"icons\\\\k\\\\attribute_speed.dds\",\n    \"icons\\\\k\\\\attribute_endurance.dds\",\n    \"icons\\\\k\\\\attribute_personality.dds\",\n    \"icons\\\\k\\\\attribute_luck.dds\"\n};\n"
  },
  {
    "path": "components/esm/attr.hpp",
    "content": "#ifndef OPENMW_ESM_ATTR_H\n#define OPENMW_ESM_ATTR_H\n\n#include <string>\n\nnamespace ESM {\n\n/*\n * Attribute definitions\n */\n\nstruct Attribute\n{\n    enum AttributeID\n    {\n        Strength = 0,\n        Intelligence = 1,\n        Willpower = 2,\n        Agility = 3,\n        Speed = 4,\n        Endurance = 5,\n        Personality = 6,\n        Luck = 7,\n        Length = 8\n    };\n\n    AttributeID mId;\n    std::string mName, mDescription;\n\n    static const AttributeID sAttributeIds[Length];\n    static const std::string sAttributeNames[Length];\n    static const std::string sGmstAttributeIds[Length];\n    static const std::string sGmstAttributeDescIds[Length];\n    static const std::string sAttributeIcons[Length];\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/cellid.cpp",
    "content": "#include \"cellid.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nconst std::string ESM::CellId::sDefaultWorldspace = \"sys::default\";\n\nvoid ESM::CellId::load (ESMReader &esm)\n{\n    mWorldspace = esm.getHNString (\"SPAC\");\n\n    if (esm.isNextSub (\"CIDX\"))\n    {\n        esm.getHT (mIndex, 8);\n        mPaged = true;\n    }\n    else\n        mPaged = false;\n}\n\nvoid ESM::CellId::save (ESMWriter &esm) const\n{\n    esm.writeHNString (\"SPAC\", mWorldspace);\n\n    if (mPaged)\n        esm.writeHNT (\"CIDX\", mIndex, 8);\n}\n\nbool ESM::operator== (const CellId& left, const CellId& right)\n{\n    return left.mWorldspace==right.mWorldspace && left.mPaged==right.mPaged &&\n        (!left.mPaged || (left.mIndex.mX==right.mIndex.mX && left.mIndex.mY==right.mIndex.mY));\n}\n\nbool ESM::operator!= (const CellId& left, const CellId& right)\n{\n    return !(left==right);\n}\n\nbool ESM::operator < (const CellId& left, const CellId& right)\n{\n    if (left.mPaged < right.mPaged)\n        return true;\n    if (left.mPaged > right.mPaged)\n        return false;\n\n    if (left.mPaged)\n    {\n        if (left.mIndex.mX < right.mIndex.mX)\n            return true;\n        if (left.mIndex.mX > right.mIndex.mX)\n            return false;\n\n        if (left.mIndex.mY < right.mIndex.mY)\n            return true;\n        if (left.mIndex.mY > right.mIndex.mY)\n            return false;\n    }\n\n    return left.mWorldspace < right.mWorldspace;\n}\n"
  },
  {
    "path": "components/esm/cellid.hpp",
    "content": "#ifndef OPENMW_ESM_CELLID_H\n#define OPENMW_ESM_CELLID_H\n\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    struct CellId\n    {\n        struct CellIndex\n        {\n            int mX;\n            int mY;\n        };\n\n        std::string mWorldspace;\n        CellIndex mIndex;\n        bool mPaged;\n\n        static const std::string sDefaultWorldspace;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n\n    bool operator== (const CellId& left, const CellId& right);\n    bool operator!= (const CellId& left, const CellId& right);\n    bool operator< (const CellId& left, const CellId& right);\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/cellref.cpp",
    "content": "#include \"cellref.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM\n{\n    int GroundcoverIndex = std::numeric_limits<int>::max();\n}\n\nvoid ESM::RefNum::load (ESMReader& esm, bool wide, const std::string& tag)\n{\n    if (wide)\n        esm.getHNT (*this, tag.c_str(), 8);\n    else\n        esm.getHNT (mIndex, tag.c_str());\n}\n\nvoid ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const\n{\n    if (wide)\n        esm.writeHNT (tag, *this, 8);\n    else\n    {\n        int refNum = (mIndex & 0xffffff) | ((hasContentFile() ? mContentFile : 0xff)<<24);\n\n        esm.writeHNT (tag, refNum, 4);\n    }\n}\n\n\nvoid ESM::CellRef::load (ESMReader& esm, bool &isDeleted, bool wideRefNum)\n{\n    loadId(esm, wideRefNum);\n    loadData(esm, isDeleted);\n}\n\nvoid ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum)\n{\n    // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that\n    // the following refs are part of a \"temp refs\" section. A temp ref is not being tracked by the moved references system.\n    // Its only purpose is a performance optimization for \"immovable\" things. We don't need this, and it's problematic anyway,\n    // because any item can theoretically be moved by a script.\n    if (esm.isNextSub (\"NAM0\"))\n        esm.skipHSub();\n\n    blank();\n\n    mRefNum.load (esm, wideRefNum);\n\n    mRefID = esm.getHNOString (\"NAME\");\n    if (mRefID.empty())\n    {\n        Log(Debug::Warning) << \"Warning: got CellRef with empty RefId in \" << esm.getName() << \" 0x\" << std::hex << esm.getFileOffset();\n    }\n}\n\nvoid ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted)\n{\n    isDeleted = false;\n\n    bool isLoaded = false;\n    while (!isLoaded && esm.hasMoreSubs())\n    {\n        esm.getSubName();\n        switch (esm.retSubName().intval)\n        {\n            case ESM::FourCC<'U','N','A','M'>::value:\n                esm.getHT(mReferenceBlocked);\n                break;\n            case ESM::FourCC<'X','S','C','L'>::value:\n                esm.getHT(mScale);\n                if (mScale < 0.5)\n                    mScale = 0.5;\n                else if (mScale > 2)\n                    mScale = 2;\n                break;\n            case ESM::FourCC<'A','N','A','M'>::value:\n                mOwner = esm.getHString();\n                break;\n            case ESM::FourCC<'B','N','A','M'>::value:\n                mGlobalVariable = esm.getHString();\n                break;\n            case ESM::FourCC<'X','S','O','L'>::value:\n                mSoul = esm.getHString();\n                break;\n            case ESM::FourCC<'C','N','A','M'>::value:\n                mFaction = esm.getHString();\n                break;\n            case ESM::FourCC<'I','N','D','X'>::value:\n                esm.getHT(mFactionRank);\n                break;\n            case ESM::FourCC<'X','C','H','G'>::value:\n                esm.getHT(mEnchantmentCharge);\n                break;\n            case ESM::FourCC<'I','N','T','V'>::value:\n                esm.getHT(mChargeInt);\n                break;\n            case ESM::FourCC<'N','A','M','9'>::value:\n                esm.getHT(mGoldValue);\n                break;\n            case ESM::FourCC<'D','O','D','T'>::value:\n                esm.getHT(mDoorDest);\n                mTeleport = true;\n                break;\n            case ESM::FourCC<'D','N','A','M'>::value:\n                mDestCell = esm.getHString();\n                break;\n            case ESM::FourCC<'F','L','T','V'>::value:\n                esm.getHT(mLockLevel);\n                break;\n            case ESM::FourCC<'K','N','A','M'>::value:\n                mKey = esm.getHString();\n                break;\n            case ESM::FourCC<'T','N','A','M'>::value:\n                mTrap = esm.getHString();\n                break;\n            case ESM::FourCC<'D','A','T','A'>::value:\n                esm.getHT(mPos, 24);\n                break;\n            case ESM::FourCC<'N','A','M','0'>::value:\n                esm.skipHSub();\n                break;\n            case ESM::SREC_DELE:\n                esm.skipHSub();\n                isDeleted = true;\n                break;\n            default:\n                esm.cacheSubName();\n                isLoaded = true;\n                break;\n        }\n    }\n\n    if (mLockLevel == 0 && !mKey.empty())\n    {\n        mLockLevel = UnbreakableLock;\n        mTrap.clear();\n    }\n}\n\nvoid ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool isDeleted) const\n{\n    mRefNum.save (esm, wideRefNum);\n\n    esm.writeHNCString(\"NAME\", mRefID);\n\n    if (isDeleted) {\n        esm.writeHNCString(\"DELE\", \"\");\n        return;\n    }\n\n    if (mScale != 1.0) {\n        float scale = mScale;\n        if (scale < 0.5)\n            scale = 0.5;\n        else if (scale > 2)\n            scale = 2;\n        esm.writeHNT(\"XSCL\", scale);\n    }\n\n    if (!inInventory)\n        esm.writeHNOCString(\"ANAM\", mOwner);\n\n    esm.writeHNOCString(\"BNAM\", mGlobalVariable);\n    esm.writeHNOCString(\"XSOL\", mSoul);\n\n    if (!inInventory)\n    {\n        esm.writeHNOCString(\"CNAM\", mFaction);\n        if (mFactionRank != -2)\n        {\n            esm.writeHNT(\"INDX\", mFactionRank);\n        }\n    }\n\n    if (mEnchantmentCharge != -1)\n        esm.writeHNT(\"XCHG\", mEnchantmentCharge);\n\n    if (mChargeInt != -1)\n        esm.writeHNT(\"INTV\", mChargeInt);\n\n    if (mGoldValue > 1)\n        esm.writeHNT(\"NAM9\", mGoldValue);\n\n    if (!inInventory && mTeleport)\n    {\n        esm.writeHNT(\"DODT\", mDoorDest);\n        esm.writeHNOCString(\"DNAM\", mDestCell);\n    }\n\n    if (!inInventory && mLockLevel != 0) {\n        esm.writeHNT(\"FLTV\", mLockLevel);\n    }\n\n    if (!inInventory)\n    {\n        esm.writeHNOCString (\"KNAM\", mKey);\n        esm.writeHNOCString (\"TNAM\", mTrap);\n    }\n\n    if (mReferenceBlocked != -1)\n        esm.writeHNT(\"UNAM\", mReferenceBlocked);\n\n    if (!inInventory)\n        esm.writeHNT(\"DATA\", mPos, 24);\n}\n\nvoid ESM::CellRef::blank()\n{\n    mRefNum.unset();\n    mRefID.clear();\n    mScale = 1;\n    mOwner.clear();\n    mGlobalVariable.clear();\n    mSoul.clear();\n    mFaction.clear();\n    mFactionRank = -2;\n    mChargeInt = -1;\n    mChargeIntRemainder = 0.0f;\n    mEnchantmentCharge = -1;\n    mGoldValue = 1;\n    mDestCell.clear();\n    mLockLevel = 0;\n    mKey.clear();\n    mTrap.clear();\n    mReferenceBlocked = -1;\n    mTeleport = false;\n\n    for (int i=0; i<3; ++i)\n    {\n        mDoorDest.pos[i] = 0;\n        mDoorDest.rot[i] = 0;\n        mPos.pos[i] = 0;\n        mPos.rot[i] = 0;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Set the mMpNum (unique multiplayer reference number) to 0 by default\n    */\n    mMpNum = 0;\n    /*\n        End of tes3mp addition\n    */\n}\n"
  },
  {
    "path": "components/esm/cellref.hpp",
    "content": "#ifndef OPENMW_ESM_CELLREF_H\n#define OPENMW_ESM_CELLREF_H\n\n#include <limits>\n#include <string>\n\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    class ESMWriter;\n    class ESMReader;\n\n    const int UnbreakableLock = std::numeric_limits<int>::max();\n    extern int GroundcoverIndex;\n\n    struct RefNum\n    {\n        unsigned int mIndex;\n        int mContentFile;\n\n        void load (ESMReader& esm, bool wide = false, const std::string& tag = \"FRMR\");\n\n        void save (ESMWriter &esm, bool wide = false, const std::string& tag = \"FRMR\") const;\n\n        enum { RefNum_NoContentFile = -1 };\n        inline bool hasContentFile() const { return mContentFile != RefNum_NoContentFile; }\n        inline void unset() { mIndex = 0; mContentFile = RefNum_NoContentFile; }\n\n        // Note: this method should not be used for objects with invalid RefNum\n        // (for example, for objects from disabled plugins in savegames).\n        inline bool fromGroundcoverFile() const { return mContentFile >= GroundcoverIndex; }\n    };\n\n    /* Cell reference. This represents ONE object (of many) inside the\n    cell. The cell references are not loaded as part of the normal\n    loading process, but are rather loaded later on demand when we are\n    setting up a specific cell.\n    */\n\n    class CellRef\n    {\n        public:\n            // Reference number\n            // Note: Currently unused for items in containers\n            RefNum mRefNum;\n\n            /*\n                Start of tes3mp addition\n\n                Keep track of a multiplayer-only number unique to this object\n            */\n            unsigned int mMpNum;\n            /*\n                End of tes3mp addition\n            */\n\n            std::string mRefID;    // ID of object being referenced\n\n            float mScale;          // Scale applied to mesh\n\n            // The NPC that owns this object (and will get angry if you steal it)\n            std::string mOwner;\n\n            // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed\n            // even if it has an Owner field.\n            // Used by bed rent scripts to allow the player to use the bed for the duration of the rent.\n            std::string mGlobalVariable;\n\n            // ID of creature trapped in this soul gem\n            std::string mSoul;\n\n            // The faction that owns this object (and will get angry if\n            // you take it and are not a faction member)\n            std::string mFaction;\n\n            // PC faction rank required to use the item. Sometimes is -1, which means \"any rank\".\n            int mFactionRank;\n\n            // For weapon or armor, this is the remaining item health.\n            // For tools (lockpicks, probes, repair hammer) it is the remaining uses.\n            // For lights it is remaining time.\n            // This could be -1 if the charge was not touched yet (i.e. full).\n            union\n            {\n                int mChargeInt;     // Used by everything except lights\n                float mChargeFloat; // Used only by lights\n            };\n            float mChargeIntRemainder; // Stores amount of charge not subtracted from mChargeInt\n\n            // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full).\n            float mEnchantmentCharge;\n\n            // This is 5 for Gold_005 references, 100 for Gold_100 and so on.\n            int mGoldValue;\n\n            // For doors - true if this door teleports to somewhere else, false\n            // if it should open through animation.\n            bool mTeleport;\n\n            // Teleport location for the door, if this is a teleporting door.\n            Position mDoorDest;\n\n            // Destination cell for doors (optional)\n            std::string mDestCell;\n\n            // Lock level for doors and containers\n            int mLockLevel;\n            std::string mKey, mTrap; // Key and trap ID names, if any\n\n            // This corresponds to the \"Reference Blocked\" checkbox in the construction set,\n            // which prevents editing that reference.\n            // -1 is not blocked, otherwise it is blocked.\n            signed char mReferenceBlocked;\n\n            // Position and rotation of this object within the cell\n            Position mPos;\n\n            /// Calls loadId and loadData\n            void load (ESMReader& esm, bool &isDeleted, bool wideRefNum = false);\n\n            void loadId (ESMReader& esm, bool wideRefNum = false);\n\n            /// Implicitly called by load\n            void loadData (ESMReader& esm, bool &isDeleted);\n\n            void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const;\n\n            void blank();\n    };\n\n    inline bool operator== (const RefNum& left, const RefNum& right)\n    {\n        return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile;\n    }\n\n    inline bool operator< (const RefNum& left, const RefNum& right)\n    {\n        if (left.mIndex<right.mIndex)\n            return true;\n        if (left.mIndex>right.mIndex)\n            return false;\n        return left.mContentFile<right.mContentFile;\n    }\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/cellstate.cpp",
    "content": "#include \"cellstate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::CellState::load (ESMReader &esm)\n{\n    mWaterLevel = 0;\n    esm.getHNOT (mWaterLevel, \"WLVL\");\n\n    mHasFogOfWar = false;\n    esm.getHNOT (mHasFogOfWar, \"HFOW\");\n\n    mLastRespawn.mDay = 0;\n    mLastRespawn.mHour = 0;\n    esm.getHNOT (mLastRespawn, \"RESP\");\n}\n\nvoid ESM::CellState::save (ESMWriter &esm) const\n{\n    if (!mId.mPaged)\n        esm.writeHNT (\"WLVL\", mWaterLevel);\n\n    esm.writeHNT (\"HFOW\", mHasFogOfWar);\n\n    esm.writeHNT (\"RESP\", mLastRespawn);\n}\n"
  },
  {
    "path": "components/esm/cellstate.hpp",
    "content": "#ifndef OPENMW_ESM_CELLSTATE_H\n#define OPENMW_ESM_CELLSTATE_H\n\n#include \"cellid.hpp\"\n\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    /// \\note Does not include references\n    struct CellState\n    {\n        CellId mId;\n\n        float mWaterLevel;\n\n        int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp)\n\n        ESM::TimeStamp mLastRespawn;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/containerstate.cpp",
    "content": "#include \"containerstate.hpp\"\n\nvoid ESM::ContainerState::load (ESMReader &esm)\n{\n    ObjectState::load (esm);\n\n    mInventory.load (esm);\n}\n\nvoid ESM::ContainerState::save (ESMWriter &esm, bool inInventory) const\n{\n    ObjectState::save (esm, inInventory);\n\n    mInventory.save (esm);\n}\n"
  },
  {
    "path": "components/esm/containerstate.hpp",
    "content": "#ifndef OPENMW_ESM_CONTAINERSTATE_H\n#define OPENMW_ESM_CONTAINERSTATE_H\n\n#include \"objectstate.hpp\"\n#include \"inventorystate.hpp\"\n\nnamespace ESM\n{\n    // format 0, saved games only\n\n    struct ContainerState final : public ObjectState\n    {\n        InventoryState mInventory;\n\n        void load (ESMReader &esm) override;\n        void save (ESMWriter &esm, bool inInventory = false) const override;\n\n        ContainerState& asContainerState() override\n        {\n            return *this;\n        }\n        const ContainerState& asContainerState() const override\n        {\n            return *this;\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/controlsstate.cpp",
    "content": "#include \"controlsstate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nESM::ControlsState::ControlsState()\n    : mViewSwitchDisabled(false),\n      mControlsDisabled(false),\n      mJumpingDisabled(false),\n      mLookingDisabled(false),\n      mVanityModeDisabled(false),\n      mWeaponDrawingDisabled(false),\n      mSpellDrawingDisabled(false)\n{\n}\n\nvoid ESM::ControlsState::load(ESM::ESMReader& esm)\n{\n    int flags;\n    esm.getHNT(flags, \"CFLG\");\n\n    mViewSwitchDisabled = flags & ViewSwitchDisabled;\n    mControlsDisabled = flags & ControlsDisabled;\n    mJumpingDisabled = flags & JumpingDisabled;\n    mLookingDisabled = flags & LookingDisabled;\n    mVanityModeDisabled = flags & VanityModeDisabled;\n    mWeaponDrawingDisabled = flags & WeaponDrawingDisabled;\n    mSpellDrawingDisabled = flags & SpellDrawingDisabled;\n}\n\nvoid ESM::ControlsState::save(ESM::ESMWriter& esm) const\n{\n    int flags = 0;\n    if (mViewSwitchDisabled) flags |= ViewSwitchDisabled;\n    if (mControlsDisabled) flags |= ControlsDisabled;\n    if (mJumpingDisabled) flags |= JumpingDisabled;\n    if (mLookingDisabled) flags |= LookingDisabled;\n    if (mVanityModeDisabled) flags |= VanityModeDisabled;\n    if (mWeaponDrawingDisabled) flags |= WeaponDrawingDisabled;\n    if (mSpellDrawingDisabled) flags |= SpellDrawingDisabled;\n\n    esm.writeHNT(\"CFLG\", flags);\n}\n"
  },
  {
    "path": "components/esm/controlsstate.hpp",
    "content": "#ifndef OPENMW_ESM_CONTROLSSTATE_H\n#define OPENMW_ESM_CONTROLSSTATE_H\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    struct ControlsState\n    {\n        ControlsState();\n\n        enum Flags\n        {\n            ViewSwitchDisabled = 0x1,\n            ControlsDisabled = 0x4,\n            JumpingDisabled = 0x1000,\n            LookingDisabled = 0x2000,\n            VanityModeDisabled = 0x4000,\n            WeaponDrawingDisabled = 0x8000,\n            SpellDrawingDisabled = 0x10000\n        };\n\n        bool mViewSwitchDisabled;\n        bool mControlsDisabled;\n        bool mJumpingDisabled;\n        bool mLookingDisabled;\n        bool mVanityModeDisabled;\n        bool mWeaponDrawingDisabled;\n        bool mSpellDrawingDisabled;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/creaturelevliststate.cpp",
    "content": "#include \"creaturelevliststate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM\n{\n\n    void CreatureLevListState::load(ESMReader &esm)\n    {\n        ObjectState::load(esm);\n\n        mSpawnActorId = -1;\n        esm.getHNOT (mSpawnActorId, \"SPAW\");\n\n        mSpawn = false;\n        esm.getHNOT (mSpawn, \"RESP\");\n    }\n\n    void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const\n    {\n        ObjectState::save(esm, inInventory);\n\n        if (mSpawnActorId != -1)\n            esm.writeHNT (\"SPAW\", mSpawnActorId);\n\n        if (mSpawn)\n            esm.writeHNT (\"RESP\", mSpawn);\n    }\n\n}\n"
  },
  {
    "path": "components/esm/creaturelevliststate.hpp",
    "content": "#ifndef OPENMW_ESM_CREATURELEVLISTSTATE_H\n#define OPENMW_ESM_CREATURELEVLISTSTATE_H\n\n#include \"objectstate.hpp\"\n\nnamespace ESM\n{\n    // format 0, saved games only\n\n    struct CreatureLevListState final : public ObjectState\n    {\n        int mSpawnActorId;\n        bool mSpawn;\n\n        void load (ESMReader &esm) override;\n        void save (ESMWriter &esm, bool inInventory = false) const override;\n\n        CreatureLevListState& asCreatureLevListState() override\n        {\n            return *this;\n        }\n        const CreatureLevListState& asCreatureLevListState() const override\n        {\n            return *this;\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/creaturestate.cpp",
    "content": "#include \"creaturestate.hpp\"\n\nvoid ESM::CreatureState::load (ESMReader &esm)\n{\n    ObjectState::load (esm);\n\n    if (mHasCustomState)\n    {\n        mInventory.load (esm);\n\n        mCreatureStats.load (esm);\n    }\n}\n\nvoid ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const\n{\n    ObjectState::save (esm, inInventory);\n\n    if (mHasCustomState)\n    {\n        mInventory.save (esm);\n\n        mCreatureStats.save (esm);\n    }\n}\n\nvoid ESM::CreatureState::blank()\n{\n    ObjectState::blank();\n    mCreatureStats.blank();\n}\n"
  },
  {
    "path": "components/esm/creaturestate.hpp",
    "content": "#ifndef OPENMW_ESM_CREATURESTATE_H\n#define OPENMW_ESM_CREATURESTATE_H\n\n#include \"objectstate.hpp\"\n#include \"inventorystate.hpp\"\n#include \"creaturestats.hpp\"\n\nnamespace ESM\n{\n    // format 0, saved games only\n\n    struct CreatureState final : public ObjectState\n    {\n        InventoryState mInventory;\n        CreatureStats mCreatureStats;\n\n        /// Initialize to default state\n        void blank() override;\n\n        void load (ESMReader &esm) override;\n        void save (ESMWriter &esm, bool inInventory = false) const override;\n\n        CreatureState& asCreatureState() override\n        {\n            return *this;\n        }\n        const CreatureState& asCreatureState() const override\n        {\n            return *this;\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/creaturestats.cpp",
    "content": "#include \"creaturestats.hpp\"\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::CreatureStats::load (ESMReader &esm)\n{\n    bool intFallback = esm.getFormat() < 11;\n    for (int i=0; i<8; ++i)\n        mAttributes[i].load (esm, intFallback);\n\n    for (int i=0; i<3; ++i)\n        mDynamic[i].load (esm);\n\n    mGoldPool = 0;\n    esm.getHNOT (mGoldPool, \"GOLD\");\n\n    mTradeTime.mDay = 0;\n    mTradeTime.mHour = 0;\n    esm.getHNOT (mTradeTime, \"TIME\");\n\n    int flags = 0;\n    mDead = false;\n    mDeathAnimationFinished = false;\n    mDied = false;\n    mMurdered = false;\n    mTalkedTo = false;\n    mAlarmed = false;\n    mAttacked = false;\n    mKnockdown = false;\n    mKnockdownOneFrame = false;\n    mKnockdownOverOneFrame = false;\n    mHitRecovery = false;\n    mBlock = false;\n    mRecalcDynamicStats = false;\n    if (esm.getFormat() < 8)\n    {\n        esm.getHNOT (mDead, \"DEAD\");\n        esm.getHNOT (mDeathAnimationFinished, \"DFNT\");\n        if (esm.getFormat() < 3 && mDead)\n            mDeathAnimationFinished = true;\n        esm.getHNOT (mDied, \"DIED\");\n        esm.getHNOT (mMurdered, \"MURD\");\n        if (esm.isNextSub(\"FRHT\"))\n            esm.skipHSub(); // Friendly hits, no longer used\n        esm.getHNOT (mTalkedTo, \"TALK\");\n        esm.getHNOT (mAlarmed, \"ALRM\");\n        esm.getHNOT (mAttacked, \"ATKD\");\n        if (esm.isNextSub(\"HOST\"))\n            esm.skipHSub(); // Hostile, no longer used\n        if (esm.isNextSub(\"ATCK\"))\n            esm.skipHSub(); // attackingOrSpell, no longer used\n        esm.getHNOT (mKnockdown, \"KNCK\");\n        esm.getHNOT (mKnockdownOneFrame, \"KNC1\");\n        esm.getHNOT (mKnockdownOverOneFrame, \"KNCO\");\n        esm.getHNOT (mHitRecovery, \"HITR\");\n        esm.getHNOT (mBlock, \"BLCK\");\n    }\n    else\n    {\n        esm.getHNOT(flags, \"AFLG\");\n        mDead = flags & Dead;\n        mDeathAnimationFinished = flags & DeathAnimationFinished;\n        mDied = flags & Died;\n        mMurdered = flags & Murdered;\n        mTalkedTo = flags & TalkedTo;\n        mAlarmed = flags & Alarmed;\n        mAttacked = flags & Attacked;\n        mKnockdown = flags & Knockdown;\n        mKnockdownOneFrame = flags & KnockdownOneFrame;\n        mKnockdownOverOneFrame = flags & KnockdownOverOneFrame;\n        mHitRecovery = flags & HitRecovery;\n        mBlock = flags & Block;\n        mRecalcDynamicStats = flags & RecalcDynamicStats;\n    }\n\n    mMovementFlags = 0;\n    esm.getHNOT (mMovementFlags, \"MOVE\");\n\n    if (esm.isNextSub(\"ASTR\"))\n        esm.skipHSub(); // attackStrength, no longer used\n\n    mFallHeight = 0;\n    esm.getHNOT (mFallHeight, \"FALL\");\n\n    mLastHitObject = esm.getHNOString (\"LHIT\");\n\n    mLastHitAttemptObject = esm.getHNOString (\"LHAT\");\n\n    if (esm.getFormat() < 8)\n        esm.getHNOT (mRecalcDynamicStats, \"CALC\");\n\n    mDrawState = 0;\n    esm.getHNOT (mDrawState, \"DRAW\");\n\n    mLevel = 1;\n    esm.getHNOT (mLevel, \"LEVL\");\n\n    mActorId = -1;\n    esm.getHNOT (mActorId, \"ACID\");\n\n    mDeathAnimation = -1;\n    esm.getHNOT (mDeathAnimation, \"DANM\");\n\n    mTimeOfDeath.mDay = 0;\n    mTimeOfDeath.mHour = 0;\n    esm.getHNOT (mTimeOfDeath, \"DTIM\");\n\n    mSpells.load(esm);\n    mActiveSpells.load(esm);\n    mAiSequence.load(esm);\n    mMagicEffects.load(esm);\n\n    while (esm.isNextSub(\"SUMM\"))\n    {\n        int magicEffect;\n        esm.getHT(magicEffect);\n        std::string source = esm.getHNOString(\"SOUR\");\n        int effectIndex = -1;\n        esm.getHNOT (effectIndex, \"EIND\");\n        int actorId;\n        esm.getHNT (actorId, \"ACID\");\n        mSummonedCreatureMap[SummonKey(magicEffect, source, effectIndex)] = actorId;\n    }\n\n    while (esm.isNextSub(\"GRAV\"))\n    {\n        int actorId;\n        esm.getHT(actorId);\n        mSummonGraveyard.push_back(actorId);\n    }\n\n    mHasAiSettings = false;\n    esm.getHNOT(mHasAiSettings, \"AISE\");\n\n    if (mHasAiSettings)\n    {\n        for (int i=0; i<4; ++i)\n            mAiSettings[i].load(esm);\n    }\n\n    while (esm.isNextSub(\"CORP\"))\n    {\n        std::string id = esm.getHString();\n\n        CorprusStats stats;\n        esm.getHNT(stats.mWorsenings, \"WORS\");\n        esm.getHNT(stats.mNextWorsening, \"TIME\");\n\n        mCorprusSpells[id] = stats;\n    }\n}\n\nvoid ESM::CreatureStats::save (ESMWriter &esm) const\n{\n    for (int i=0; i<8; ++i)\n        mAttributes[i].save (esm);\n\n    for (int i=0; i<3; ++i)\n        mDynamic[i].save (esm);\n\n    if (mGoldPool)\n        esm.writeHNT (\"GOLD\", mGoldPool);\n\n    if (mTradeTime.mDay != 0 || mTradeTime.mHour != 0)\n        esm.writeHNT (\"TIME\", mTradeTime);\n\n    int flags = 0;\n    if (mDead) flags |= Dead;\n    if (mDeathAnimationFinished) flags |= DeathAnimationFinished;\n    if (mDied) flags |= Died;\n    if (mMurdered) flags |= Murdered;\n    if (mTalkedTo) flags |= TalkedTo;\n    if (mAlarmed) flags |= Alarmed;\n    if (mAttacked) flags |= Attacked;\n    if (mKnockdown) flags |= Knockdown;\n    if (mKnockdownOneFrame) flags |= KnockdownOneFrame;\n    if (mKnockdownOverOneFrame) flags |= KnockdownOverOneFrame;\n    if (mHitRecovery) flags |= HitRecovery;\n    if (mBlock) flags |= Block;\n    if (mRecalcDynamicStats) flags |= RecalcDynamicStats;\n\n    if (flags)\n        esm.writeHNT (\"AFLG\", flags);\n\n    if (mMovementFlags)\n        esm.writeHNT (\"MOVE\", mMovementFlags);\n\n    if (mFallHeight)\n        esm.writeHNT (\"FALL\", mFallHeight);\n\n    if (!mLastHitObject.empty())\n        esm.writeHNString (\"LHIT\", mLastHitObject);\n\n    if (!mLastHitAttemptObject.empty())\n        esm.writeHNString (\"LHAT\", mLastHitAttemptObject);\n\n    if (mDrawState)\n        esm.writeHNT (\"DRAW\", mDrawState);\n\n    if (mLevel != 1)\n        esm.writeHNT (\"LEVL\", mLevel);\n\n    if (mActorId != -1)\n        esm.writeHNT (\"ACID\", mActorId);\n\n    if (mDeathAnimation != -1)\n        esm.writeHNT (\"DANM\", mDeathAnimation);\n\n    if (mTimeOfDeath.mHour != 0 || mTimeOfDeath.mDay != 0)\n        esm.writeHNT (\"DTIM\", mTimeOfDeath);\n\n    mSpells.save(esm);\n    mActiveSpells.save(esm);\n    mAiSequence.save(esm);\n    mMagicEffects.save(esm);\n\n    for (const auto& summon : mSummonedCreatureMap)\n    {\n        esm.writeHNT (\"SUMM\", summon.first.mEffectId);\n        esm.writeHNString (\"SOUR\", summon.first.mSourceId);\n        int effectIndex = summon.first.mEffectIndex;\n        if (effectIndex != -1)\n            esm.writeHNT (\"EIND\", effectIndex);\n        esm.writeHNT (\"ACID\", summon.second);\n    }\n\n    for (int key : mSummonGraveyard)\n    {\n        esm.writeHNT (\"GRAV\", key);\n    }\n\n    esm.writeHNT(\"AISE\", mHasAiSettings);\n    if (mHasAiSettings)\n    {\n        for (int i=0; i<4; ++i)\n            mAiSettings[i].save(esm);\n    }\n\n    for (const auto& corprusSpell : mCorprusSpells)\n    {\n        esm.writeHNString(\"CORP\", corprusSpell.first);\n\n        const CorprusStats & stats = corprusSpell.second;\n        esm.writeHNT(\"WORS\", stats.mWorsenings);\n        esm.writeHNT(\"TIME\", stats.mNextWorsening);\n    }\n}\n\nvoid ESM::CreatureStats::blank()\n{\n    mTradeTime.mHour = 0;\n    mTradeTime.mDay = 0;\n    mGoldPool = 0;\n    mActorId = -1;\n    mHasAiSettings = false;\n    mDead = false;\n    mDeathAnimationFinished = false;\n    mDied = false;\n    mMurdered = false;\n    mTalkedTo = false;\n    mAlarmed = false;\n    mAttacked = false;\n    mKnockdown = false;\n    mKnockdownOneFrame = false;\n    mKnockdownOverOneFrame = false;\n    mHitRecovery = false;\n    mBlock = false;\n    mMovementFlags = 0;\n    mFallHeight = 0.f;\n    mRecalcDynamicStats = false;\n    mDrawState = 0;\n    mDeathAnimation = -1;\n    mLevel = 1;\n    mCorprusSpells.clear();\n}\n"
  },
  {
    "path": "components/esm/creaturestats.hpp",
    "content": "#ifndef OPENMW_ESM_CREATURESTATS_H\n#define OPENMW_ESM_CREATURESTATS_H\n\n#include <string>\n#include <vector>\n#include <map>\n\n#include \"statstate.hpp\"\n\n#include \"defs.hpp\"\n\n#include \"attr.hpp\"\n#include \"spellstate.hpp\"\n#include \"activespells.hpp\"\n#include \"magiceffects.hpp\"\n#include \"aisequence.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n    struct CreatureStats\n    {\n        struct CorprusStats\n        {\n            int mWorsenings[Attribute::Length];\n            TimeStamp mNextWorsening;\n        };\n\n        StatState<float> mAttributes[Attribute::Length];\n        StatState<float> mDynamic[3];\n\n        MagicEffects mMagicEffects;\n\n        AiSequence::AiSequence mAiSequence;\n\n        bool mHasAiSettings;\n        StatState<int> mAiSettings[4];\n\n        std::map<SummonKey, int> mSummonedCreatureMap;\n        std::vector<int> mSummonGraveyard;\n\n        ESM::TimeStamp mTradeTime;\n        int mGoldPool;\n        int mActorId;\n        //int mHitAttemptActorId;\n\n        enum Flags\n        {\n            Dead                   = 0x0001,\n            DeathAnimationFinished = 0x0002,\n            Died                   = 0x0004,\n            Murdered               = 0x0008,\n            TalkedTo               = 0x0010,\n            Alarmed                = 0x0020,\n            Attacked               = 0x0040,\n            Knockdown              = 0x0080,\n            KnockdownOneFrame      = 0x0100,\n            KnockdownOverOneFrame  = 0x0200,\n            HitRecovery            = 0x0400,\n            Block                  = 0x0800,\n            RecalcDynamicStats     = 0x1000\n        };\n        bool mDead;\n        bool mDeathAnimationFinished;\n        bool mDied;\n        bool mMurdered;\n        bool mTalkedTo;\n        bool mAlarmed;\n        bool mAttacked;\n        bool mKnockdown;\n        bool mKnockdownOneFrame;\n        bool mKnockdownOverOneFrame;\n        bool mHitRecovery;\n        bool mBlock;\n        unsigned int mMovementFlags;\n        float mFallHeight;\n        std::string mLastHitObject;\n        std::string mLastHitAttemptObject;\n        bool mRecalcDynamicStats;\n        int mDrawState;\n        signed char mDeathAnimation;\n        ESM::TimeStamp mTimeOfDeath;\n        int mLevel;\n\n        std::map<std::string, CorprusStats> mCorprusSpells;\n        SpellState mSpells;\n        ActiveSpells mActiveSpells;\n\n        /// Initialize to default state\n        void blank();\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/custommarkerstate.cpp",
    "content": "#include \"custommarkerstate.hpp\"\n\n#include \"esmwriter.hpp\"\n#include \"esmreader.hpp\"\n\nnamespace ESM\n{\n\nvoid CustomMarker::save(ESM::ESMWriter &esm) const\n{\n    esm.writeHNT(\"POSX\", mWorldX);\n    esm.writeHNT(\"POSY\", mWorldY);\n    mCell.save(esm);\n    if (!mNote.empty())\n        esm.writeHNString(\"NOTE\", mNote);\n}\n\nvoid CustomMarker::load(ESM::ESMReader &esm)\n{\n    esm.getHNT(mWorldX, \"POSX\");\n    esm.getHNT(mWorldY, \"POSY\");\n    mCell.load(esm);\n    mNote = esm.getHNOString(\"NOTE\");\n}\n\n}\n"
  },
  {
    "path": "components/esm/custommarkerstate.hpp",
    "content": "#ifndef OPENMW_ESM_CUSTOMMARKERSTATE_H\n#define OPENMW_ESM_CUSTOMMARKERSTATE_H\n\n#include \"cellid.hpp\"\n\nnamespace ESM\n{\n\n// format 0, saved games only\nstruct CustomMarker\n{\n    float mWorldX;\n    float mWorldY;\n\n    ESM::CellId mCell;\n\n    std::string mNote;\n\n    bool operator == (const CustomMarker& other) const\n    {\n        return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY;\n    }\n\n    void load (ESM::ESMReader& reader);\n    void save (ESM::ESMWriter& writer) const;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/debugprofile.cpp",
    "content": "#include \"debugprofile.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nunsigned int ESM::DebugProfile::sRecordId = REC_DBGP;\n\nvoid ESM::DebugProfile::load (ESMReader& esm, bool &isDeleted)\n{\n    isDeleted = false;\n\n    while (esm.hasMoreSubs())\n    {\n        esm.getSubName();\n        switch (esm.retSubName().intval)\n        {\n            case ESM::SREC_NAME:\n                mId = esm.getHString();\n                break;\n            case ESM::FourCC<'D','E','S','C'>::value:\n                mDescription = esm.getHString();\n                break;\n            case ESM::FourCC<'S','C','R','P'>::value:\n                mScriptText = esm.getHString();\n                break;\n            case ESM::FourCC<'F','L','A','G'>::value:\n                esm.getHT(mFlags);\n                break;\n            case ESM::SREC_DELE:\n                esm.skipHSub();\n                isDeleted = true;\n                break;\n            default:\n                esm.fail(\"Unknown subrecord\");\n                break;\n        }\n    }\n}\n\nvoid ESM::DebugProfile::save (ESMWriter& esm, bool isDeleted) const\n{\n    esm.writeHNCString (\"NAME\", mId);\n\n    if (isDeleted)\n    {\n        esm.writeHNCString(\"DELE\", \"\");\n        return;\n    }\n\n    esm.writeHNCString (\"DESC\", mDescription);\n    esm.writeHNCString (\"SCRP\", mScriptText);\n    esm.writeHNT (\"FLAG\", mFlags);\n}\n\nvoid ESM::DebugProfile::blank()\n{\n    mDescription.clear();\n    mScriptText.clear();\n    mFlags = 0;\n}\n"
  },
  {
    "path": "components/esm/debugprofile.hpp",
    "content": "#ifndef COMPONENTS_ESM_DEBUGPROFILE_H\n#define COMPONENTS_ESM_DEBUGPROFILE_H\n\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    struct DebugProfile\n    {\n        static unsigned int sRecordId;\n\n        enum Flags\n        {\n            Flag_Default = 1,       // add to newly opened scene subviews\n            Flag_BypassNewGame = 2, // bypass regular game startup\n            Flag_Global = 4         // make available from main menu (i.e. not location specific)\n        };\n\n        std::string mId;\n\n        std::string mDescription;\n\n        std::string mScriptText;\n\n        unsigned int mFlags;\n\n        void load (ESMReader& esm, bool &isDeleted);\n        void save (ESMWriter& esm, bool isDeleted = false) const;\n\n        /// Set record to default state (does not touch the ID).\n        void blank();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/defs.hpp",
    "content": "#ifndef OPENMW_ESM_DEFS_H\n#define OPENMW_ESM_DEFS_H\n\n#include <stdint.h>\n\n#include <osg/Vec3f>\n\nnamespace ESM\n{\n\nstruct TimeStamp\n{\n    float mHour;\n    int mDay;\n};\n\nstruct EpochTimeStamp\n{\n    float mGameHour;\n    int mDay;\n    int mMonth;\n    int mYear;\n};\n\n// Pixel color value. Standard four-byte rr,gg,bb,aa format.\ntypedef uint32_t Color;\n\nenum Specialization\n{\n    SPC_Combat = 0,\n    SPC_Magic = 1,\n    SPC_Stealth = 2\n};\n\nenum RangeType\n{\n    RT_Self = 0,\n    RT_Touch = 1,\n    RT_Target = 2\n};\n\n#pragma pack(push)\n#pragma pack(1)\n\n// Position and rotation\nstruct Position\n{\n    float pos[3];\n\n    // In radians\n    float rot[3];\n\n    osg::Vec3f asVec3() const\n    {\n        return osg::Vec3f(pos[0], pos[1], pos[2]);\n    }\n\n    osg::Vec3f asRotationVec3() const\n    {\n        return osg::Vec3f(rot[0], rot[1], rot[2]);\n    }\n};\n#pragma pack(pop)\n\nbool inline operator== (const Position& left, const Position& right) noexcept\n{\n    return left.pos[0] == right.pos[0] &&\n           left.pos[1] == right.pos[1] &&\n           left.pos[2] == right.pos[2] &&\n           left.rot[0] == right.rot[0] &&\n           left.rot[1] == right.rot[1] &&\n           left.rot[2] == right.rot[2];\n}\n\nbool inline operator!= (const Position& left, const Position& right) noexcept\n{\n    return left.pos[0] != right.pos[0] ||\n           left.pos[1] != right.pos[1] ||\n           left.pos[2] != right.pos[2] ||\n           left.rot[0] != right.rot[0] ||\n           left.rot[1] != right.rot[1] ||\n           left.rot[2] != right.rot[2];\n}\n\ntemplate <int a, int b, int c, int d>\nstruct FourCC\n{\n    static constexpr unsigned int value = (((((d << 8) | c) << 8) | b) << 8) | a;\n};\n\nenum RecNameInts\n{\n    // format 0 / legacy\n    REC_ACTI = FourCC<'A','C','T','I'>::value,\n    REC_ALCH = FourCC<'A','L','C','H'>::value,\n    REC_APPA = FourCC<'A','P','P','A'>::value,\n    REC_ARMO = FourCC<'A','R','M','O'>::value,\n    REC_BODY = FourCC<'B','O','D','Y'>::value,\n    REC_BOOK = FourCC<'B','O','O','K'>::value,\n    REC_BSGN = FourCC<'B','S','G','N'>::value,\n    REC_CELL = FourCC<'C','E','L','L'>::value,\n    REC_CLAS = FourCC<'C','L','A','S'>::value,\n    REC_CLOT = FourCC<'C','L','O','T'>::value,\n    REC_CNTC = FourCC<'C','N','T','C'>::value,\n    REC_CONT = FourCC<'C','O','N','T'>::value,\n    REC_CREA = FourCC<'C','R','E','A'>::value,\n    REC_CREC = FourCC<'C','R','E','C'>::value,\n    REC_DIAL = FourCC<'D','I','A','L'>::value,\n    REC_DOOR = FourCC<'D','O','O','R'>::value,\n    REC_ENCH = FourCC<'E','N','C','H'>::value,\n    REC_FACT = FourCC<'F','A','C','T'>::value,\n    REC_GLOB = FourCC<'G','L','O','B'>::value,\n    REC_GMST = FourCC<'G','M','S','T'>::value,\n    REC_INFO = FourCC<'I','N','F','O'>::value,\n    REC_INGR = FourCC<'I','N','G','R'>::value,\n    REC_LAND = FourCC<'L','A','N','D'>::value,\n    REC_LEVC = FourCC<'L','E','V','C'>::value,\n    REC_LEVI = FourCC<'L','E','V','I'>::value,\n    REC_LIGH = FourCC<'L','I','G','H'>::value,\n    REC_LOCK = FourCC<'L','O','C','K'>::value,\n    REC_LTEX = FourCC<'L','T','E','X'>::value,\n    REC_MGEF = FourCC<'M','G','E','F'>::value,\n    REC_MISC = FourCC<'M','I','S','C'>::value,\n    REC_NPC_ = FourCC<'N','P','C','_'>::value,\n    REC_NPCC = FourCC<'N','P','C','C'>::value,\n    REC_PGRD = FourCC<'P','G','R','D'>::value,\n    REC_PROB = FourCC<'P','R','O','B'>::value,\n    REC_RACE = FourCC<'R','A','C','E'>::value,\n    REC_REGN = FourCC<'R','E','G','N'>::value,\n    REC_REPA = FourCC<'R','E','P','A'>::value,\n    REC_SCPT = FourCC<'S','C','P','T'>::value,\n    REC_SKIL = FourCC<'S','K','I','L'>::value,\n    REC_SNDG = FourCC<'S','N','D','G'>::value,\n    REC_SOUN = FourCC<'S','O','U','N'>::value,\n    REC_SPEL = FourCC<'S','P','E','L'>::value,\n    REC_SSCR = FourCC<'S','S','C','R'>::value,\n    REC_STAT = FourCC<'S','T','A','T'>::value,\n    REC_WEAP = FourCC<'W','E','A','P'>::value,\n\n    // format 0 - saved games\n    REC_SAVE = FourCC<'S','A','V','E'>::value,\n    REC_JOUR_LEGACY = FourCC<0xa4,'U','O','R'>::value, // \"\\xa4UOR\", rather than \"JOUR\", little oversight when magic numbers were\n                                                       // calculated by hand, needs to be supported for older files now\n    REC_JOUR = FourCC<'J','O','U','R'>::value,\n    REC_QUES = FourCC<'Q','U','E','S'>::value,\n    REC_GSCR = FourCC<'G','S','C','R'>::value,\n    REC_PLAY = FourCC<'P','L','A','Y'>::value,\n    REC_CSTA = FourCC<'C','S','T','A'>::value,\n    REC_GMAP = FourCC<'G','M','A','P'>::value,\n    REC_DIAS = FourCC<'D','I','A','S'>::value,\n    REC_WTHR = FourCC<'W','T','H','R'>::value,\n    REC_KEYS = FourCC<'K','E','Y','S'>::value,\n    REC_DYNA = FourCC<'D','Y','N','A'>::value,\n    REC_ASPL = FourCC<'A','S','P','L'>::value,\n    REC_ACTC = FourCC<'A','C','T','C'>::value,\n    REC_MPRJ = FourCC<'M','P','R','J'>::value,\n    REC_PROJ = FourCC<'P','R','O','J'>::value,\n    REC_DCOU = FourCC<'D','C','O','U'>::value,\n    REC_MARK = FourCC<'M','A','R','K'>::value,\n    REC_ENAB = FourCC<'E','N','A','B'>::value,\n    REC_CAM_ = FourCC<'C','A','M','_'>::value,\n    REC_STLN = FourCC<'S','T','L','N'>::value,\n    REC_INPU = FourCC<'I','N','P','U'>::value,\n\n    // format 1\n    REC_FILT = FourCC<'F','I','L','T'>::value,\n    REC_DBGP = FourCC<'D','B','G','P'>::value ///< only used in project files\n};\n\n/// Common subrecords\nenum SubRecNameInts\n{\n    SREC_DELE = ESM::FourCC<'D','E','L','E'>::value,\n    SREC_NAME = ESM::FourCC<'N','A','M','E'>::value\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/dialoguestate.cpp",
    "content": "#include \"dialoguestate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::DialogueState::load (ESMReader &esm)\n{\n    while (esm.isNextSub (\"TOPI\"))\n        mKnownTopics.push_back (esm.getHString());\n\n    while (esm.isNextSub (\"FACT\"))\n    {\n        std::string faction = esm.getHString();\n\n        while (esm.isNextSub(\"REA2\"))\n        {\n            std::string faction2 = esm.getHString();\n            int reaction;\n            esm.getHNT(reaction, \"INTV\");\n            mChangedFactionReaction[faction][faction2] = reaction;\n        }\n\n        // no longer used\n        while (esm.isNextSub (\"REAC\"))\n        {\n            esm.skipHSub();\n            esm.getSubName();\n            esm.skipHSub();\n        }\n    }\n}\n\nvoid ESM::DialogueState::save (ESMWriter &esm) const\n{\n    for (std::vector<std::string>::const_iterator iter (mKnownTopics.begin());\n        iter!=mKnownTopics.end(); ++iter)\n    {\n        esm.writeHNString (\"TOPI\", *iter);\n    }\n\n    for (std::map<std::string, std::map<std::string, int> >::const_iterator iter = mChangedFactionReaction.begin();\n         iter != mChangedFactionReaction.end(); ++iter)\n    {\n        esm.writeHNString (\"FACT\", iter->first);\n\n        for (std::map<std::string, int>::const_iterator reactIter = iter->second.begin();\n             reactIter != iter->second.end(); ++reactIter)\n        {\n            esm.writeHNString (\"REA2\", reactIter->first);\n            esm.writeHNT (\"INTV\", reactIter->second);\n        }\n    }\n}\n"
  },
  {
    "path": "components/esm/dialoguestate.hpp",
    "content": "#ifndef OPENMW_ESM_DIALOGUESTATE_H\n#define OPENMW_ESM_DIALOGUESTATE_H\n\n#include <string>\n#include <vector>\n#include <map>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    struct DialogueState\n    {\n        // must be lower case topic IDs\n        std::vector<std::string> mKnownTopics;\n\n        // must be lower case faction IDs\n        std::map<std::string, std::map<std::string, int> > mChangedFactionReaction;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/doorstate.cpp",
    "content": "#include \"doorstate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\nnamespace ESM\n{\n\n    void DoorState::load(ESMReader &esm)\n    {\n        ObjectState::load(esm);\n\n        mDoorState = 0;\n        esm.getHNOT (mDoorState, \"ANIM\");\n        if (mDoorState < 0 || mDoorState > 2)\n            Log(Debug::Warning) << \"Dropping invalid door state (\" << mDoorState << \") for door \\\"\" << mRef.mRefID << \"\\\"\";\n    }\n\n    void DoorState::save(ESMWriter &esm, bool inInventory) const\n    {\n        ObjectState::save(esm, inInventory);\n\n        if (mDoorState < 0 || mDoorState > 2)\n        {\n            Log(Debug::Warning) << \"Dropping invalid door state (\" << mDoorState << \") for door \\\"\" << mRef.mRefID << \"\\\"\";\n            return;\n        }\n\n        if (mDoorState != 0)\n            esm.writeHNT (\"ANIM\", mDoorState);\n    }\n\n}\n"
  },
  {
    "path": "components/esm/doorstate.hpp",
    "content": "#ifndef OPENMW_ESM_DOORSTATE_H\n#define OPENMW_ESM_DOORSTATE_H\n\n#include \"objectstate.hpp\"\n\nnamespace ESM\n{\n    // format 0, saved games only\n\n    struct DoorState final : public ObjectState\n    {\n        int mDoorState = 0;\n\n        void load (ESMReader &esm) override;\n        void save (ESMWriter &esm, bool inInventory = false) const override;\n\n        DoorState& asDoorState() override\n        {\n            return *this;\n        }\n        const DoorState& asDoorState() const override\n        {\n            return *this;\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/effectlist.cpp",
    "content": "#include \"effectlist.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM {\n\nvoid EffectList::load(ESMReader &esm)\n{\n    mList.clear();\n    while (esm.isNextSub(\"ENAM\")) {\n        add(esm);\n    }\n}\n\nvoid EffectList::add(ESMReader &esm)\n{\n    ENAMstruct s;\n    esm.getHT(s, 24);\n    mList.push_back(s);\n}\n\nvoid EffectList::save(ESMWriter &esm) const\n{\n    for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) {\n        esm.writeHNT<ENAMstruct>(\"ENAM\", *it, 24);\n    }\n}\n\n} // end namespace\n"
  },
  {
    "path": "components/esm/effectlist.hpp",
    "content": "#ifndef OPENMW_ESM_EFFECTLIST_H\n#define OPENMW_ESM_EFFECTLIST_H\n\n#include <vector>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    #pragma pack(push)\n    #pragma pack(1)\n\n    /** Defines a spell effect. Shared between SPEL (Spells), ALCH\n     (Potions) and ENCH (Item enchantments) records\n     */\n    struct ENAMstruct\n    {\n        // Magical effect, hard-coded ID\n        short mEffectID;\n\n        // Which skills/attributes are affected (for restore/drain spells\n        // etc.)\n        signed char mSkill, mAttribute; // -1 if N/A\n\n        // Other spell parameters\n        int mRange; // 0 - self, 1 - touch, 2 - target (RangeType enum)\n        int mArea, mDuration, mMagnMin, mMagnMax;\n    };\n    #pragma pack(pop)\n\n    /// EffectList, ENAM subrecord\n    struct EffectList\n    {\n        std::vector<ENAMstruct> mList;\n\n        /// Load one effect, assumes subrecord name was already read\n        void add(ESMReader &esm);\n\n        /// Load all effects\n        void load(ESMReader &esm);\n        void save(ESMWriter &esm) const;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/esmcommon.hpp",
    "content": "#ifndef OPENMW_ESM_COMMON_H\n#define OPENMW_ESM_COMMON_H\n\n#include <algorithm>\n#include <string>\n#include <cstring>\n#include <vector>\n\n#include <stdint.h>\n\nnamespace ESM\n{\nenum Version\n  {\n    VER_12 = 0x3f99999a,\n    VER_13 = 0x3fa66666\n  };\n\n\n// CRTP for FIXED_STRING class, a structure used for holding fixed-length strings\ntemplate< template<size_t> class DERIVED, size_t SIZE>\nclass FIXED_STRING_BASE\n{\n    /* The following methods must be implemented in derived classes:\n     *   char const* ro_data() const; // return pointer to ro buffer\n     *   char*       rw_data();       // return pointer to rw buffer\n     */\npublic:\n    enum { size = SIZE };\n\n    template<size_t OTHER_SIZE>\n    bool operator==(char const (&str)[OTHER_SIZE]) const\n    {\n        size_t other_len = strnlen(str, OTHER_SIZE);\n        if (other_len != this->length())\n            return false;\n        return std::strncmp(self()->ro_data(), str, size) == 0;\n    }\n\n    //this operator will not be used for char[N], only for char*\n    template<typename T, typename = typename std::enable_if<std::is_same<T, char>::value>::type>\n    bool operator==(const T* const& str) const\n    {\n        char const* const data = self()->ro_data();\n        for(size_t i = 0; i < size; ++i)\n        {\n            if(data[i] != str[i]) return false;\n            else if(data[i] == '\\0') return true;\n        }\n        return str[size] == '\\0';\n    }\n    bool operator!=(const char* const str) const { return !( (*this) == str ); }\n\n    bool operator==(const std::string& str) const\n    {\n        return (*this) == str.c_str();\n    }\n    bool operator!=(const std::string& str) const { return !( (*this) == str ); }\n\n    static size_t data_size() { return size; }\n    size_t length() const { return strnlen(self()->ro_data(), size); }\n    std::string toString() const { return std::string(self()->ro_data(), this->length()); }\n\n    void assign(const std::string& value)\n    {\n        std::strncpy(self()->rw_data(), value.c_str(), size-1);\n        self()->rw_data()[size-1] = '\\0';\n    }\n\n    void clear() { this->assign(\"\"); }\nprivate:\n    DERIVED<size> const* self() const\n    {\n        return static_cast<DERIVED<size> const*>(this);\n    }\n\n    // write the non-const version in terms of the const version\n    // Effective C++ 3rd ed., Item 3 (p. 24-25)\n    DERIVED<size>* self()\n    {\n        return const_cast<DERIVED<size>*>(static_cast<FIXED_STRING_BASE const*>(this)->self());\n    }\n};\n\n// Generic implementation\ntemplate <size_t SIZE>\nstruct FIXED_STRING : public FIXED_STRING_BASE<FIXED_STRING, SIZE>\n{\n    char data[SIZE];\n\n    char const* ro_data() const { return data; }\n    char*       rw_data() { return data; }\n};\n\n// In the case of SIZE=4, it can be more efficient to match the string\n// as a 32 bit number, therefore the struct is implemented as a union with an int.\ntemplate <>\nstruct FIXED_STRING<4> : public FIXED_STRING_BASE<FIXED_STRING, 4>\n{\n    union {\n        char data[4];\n        uint32_t intval;\n    };\n\n    using FIXED_STRING_BASE::operator==;\n    using FIXED_STRING_BASE::operator!=;\n\n    bool operator==(uint32_t v) const { return v == intval; }\n    bool operator!=(uint32_t v) const { return v != intval; }\n\n    void assign(const std::string& value)\n    {\n        intval = 0;\n        std::memcpy(data, value.data(), std::min(value.size(), sizeof(data)));\n    }\n\n    char const* ro_data() const { return data; }\n    char*       rw_data() { return data; }\n};\n\ntypedef FIXED_STRING<4> NAME;\ntypedef FIXED_STRING<32> NAME32;\ntypedef FIXED_STRING<64> NAME64;\n\n/* This struct defines a file 'context' which can be saved and later\n   restored by an ESMReader instance. It will save the position within\n   a file, and when restored will let you read from that position as\n   if you never left it.\n */\nstruct ESM_Context\n{\n  std::string filename;\n  uint32_t leftRec, leftSub;\n  size_t leftFile;\n  NAME recName, subName;\n  // When working with multiple esX files, we will generate lists of all files that\n  //  actually contribute to a specific cell. Therefore, we need to store the index\n  //  of the file belonging to this contest. See CellStore::(list/load)refs for details.\n  int index;\n  std::vector<int> parentFileIndices;\n\n  // True if subName has been read but not used.\n  bool subCached;\n\n  // File position. Only used for stored contexts, not regularly\n  // updated within the reader itself.\n  size_t filePos;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/esmreader.cpp",
    "content": "#include \"esmreader.hpp\"\n\n#include <stdexcept>\n\nnamespace ESM\n{\n\nusing namespace Misc;\n\n    std::string ESMReader::getName() const\n    {\n        return mCtx.filename;\n    }\n\nESM_Context ESMReader::getContext()\n{\n    // Update the file position before returning\n    mCtx.filePos = mEsm->tellg();\n    return mCtx;\n}\n\nESMReader::ESMReader()\n    : mRecordFlags(0)\n    , mBuffer(50*1024)\n    , mGlobalReaderList(nullptr)\n    , mEncoder(nullptr)\n    , mFileSize(0)\n{\n    clearCtx();\n}\n\nint ESMReader::getFormat() const\n{\n    return mHeader.mFormat;\n}\n\nvoid ESMReader::restoreContext(const ESM_Context &rc)\n{\n    // Reopen the file if necessary\n    if (mCtx.filename != rc.filename)\n        openRaw(rc.filename);\n\n    // Copy the data\n    mCtx = rc;\n\n    // Make sure we seek to the right place\n    mEsm->seekg(mCtx.filePos);\n}\n\nvoid ESMReader::close()\n{\n    mEsm.reset();\n    clearCtx();\n    mHeader.blank();\n}\n\nvoid ESMReader::clearCtx() \n{\n   mCtx.filename.clear();\n   mCtx.leftFile = 0;\n   mCtx.leftRec = 0;\n   mCtx.leftSub = 0;\n   mCtx.subCached = false;\n   mCtx.recName.clear();\n   mCtx.subName.clear();\n}\n\nvoid ESMReader::openRaw(Files::IStreamPtr _esm, const std::string& name)\n{\n    close();\n    mEsm = _esm;\n    mCtx.filename = name;\n    mEsm->seekg(0, mEsm->end);\n    mCtx.leftFile = mFileSize = mEsm->tellg();\n    mEsm->seekg(0, mEsm->beg);\n}\n\nvoid ESMReader::openRaw(const std::string& filename)\n{\n    openRaw(Files::openConstrainedFileStream(filename.c_str()), filename);\n}\n\nvoid ESMReader::open(Files::IStreamPtr _esm, const std::string &name)\n{\n    openRaw(_esm, name);\n\n    if (getRecName() != \"TES3\")\n        fail(\"Not a valid Morrowind file\");\n\n    getRecHeader();\n\n    mHeader.load (*this);\n}\n\nvoid ESMReader::open(const std::string &file)\n{\n    open (Files::openConstrainedFileStream (file.c_str ()), file);\n}\n\nstd::string ESMReader::getHNOString(const char* name)\n{\n    if (isNextSub(name))\n        return getHString();\n    return \"\";\n}\n\nstd::string ESMReader::getHNString(const char* name)\n{\n    getSubNameIs(name);\n    return getHString();\n}\n\nstd::string ESMReader::getHString()\n{\n    getSubHeader();\n\n    // Hack to make MultiMark.esp load. Zero-length strings do not\n    // occur in any of the official mods, but MultiMark makes use of\n    // them. For some reason, they break the rules, and contain a byte\n    // (value 0) even if the header says there is no data. If\n    // Morrowind accepts it, so should we.\n    if (mCtx.leftSub == 0 && !mEsm->peek())\n    {\n        // Skip the following zero byte\n        mCtx.leftRec--;\n        char c;\n        getExact(&c, 1);\n        return \"\";\n    }\n\n    return getString(mCtx.leftSub);\n}\n\nvoid ESMReader::getHExact(void*p, int size)\n{\n    getSubHeader();\n    if (size != static_cast<int> (mCtx.leftSub))\n    {\n        std::stringstream error;\n        error << \"getHExact(): size mismatch (requested \" << size << \", got \" << mCtx.leftSub << \")\";\n        fail(error.str());\n    }\n    getExact(p, size);\n}\n\n// Read the given number of bytes from a named subrecord\nvoid ESMReader::getHNExact(void*p, int size, const char* name)\n{\n    getSubNameIs(name);\n    getHExact(p, size);\n}\n\n// Get the next subrecord name and check if it matches the parameter\nvoid ESMReader::getSubNameIs(const char* name)\n{\n    getSubName();\n    if (mCtx.subName != name)\n        fail(\n                \"Expected subrecord \" + std::string(name) + \" but got \"\n                        + mCtx.subName.toString());\n}\n\nbool ESMReader::isNextSub(const char* name)\n{\n    if (!mCtx.leftRec)\n        return false;\n\n    getSubName();\n\n    // If the name didn't match, then mark the it as 'cached' so it's\n    // available for the next call to getSubName.\n    mCtx.subCached = (mCtx.subName != name);\n\n    // If subCached is false, then subName == name.\n    return !mCtx.subCached;\n}\n\nbool ESMReader::peekNextSub(const char *name)\n{\n    if (!mCtx.leftRec)\n        return false;\n\n    getSubName();\n\n    mCtx.subCached = true;\n    return mCtx.subName == name;\n}\n\nvoid ESMReader::cacheSubName()\n{\n    mCtx.subCached = true;\n}\n\n// Read subrecord name. This gets called a LOT, so I've optimized it\n// slightly.\nvoid ESMReader::getSubName()\n{\n    // If the name has already been read, do nothing\n    if (mCtx.subCached)\n    {\n        mCtx.subCached = false;\n        return;\n    }\n\n    // reading the subrecord data anyway.\n    const int subNameSize = static_cast<int>(mCtx.subName.data_size());\n    getExact(mCtx.subName.rw_data(), subNameSize);\n    mCtx.leftRec -= static_cast<uint32_t>(subNameSize);\n}\n\nvoid ESMReader::skipHSub()\n{\n    getSubHeader();\n    skip(mCtx.leftSub);\n}\n\nvoid ESMReader::skipHSubSize(int size)\n{\n    skipHSub();\n    if (static_cast<int> (mCtx.leftSub) != size)\n        fail(\"skipHSubSize() mismatch\");\n}\n\nvoid ESMReader::skipHSubUntil(const char *name)\n{\n    while (hasMoreSubs() && !isNextSub(name))\n    {\n        mCtx.subCached = false;\n        skipHSub();\n    }\n    if (hasMoreSubs())\n        mCtx.subCached = true;\n}\n\nvoid ESMReader::getSubHeader()\n{\n    if (mCtx.leftRec < 4)\n        fail(\"End of record while reading sub-record header\");\n\n    // Get subrecord size\n    getT(mCtx.leftSub);\n\n    // Adjust number of record bytes left\n    mCtx.leftRec -= mCtx.leftSub + 4;\n}\n\nvoid ESMReader::getSubHeaderIs(int size)\n{\n    getSubHeader();\n    if (size != static_cast<int> (mCtx.leftSub))\n        fail(\"getSubHeaderIs(): Sub header mismatch\");\n}\n\nNAME ESMReader::getRecName()\n{\n    if (!hasMoreRecs())\n        fail(\"No more records, getRecName() failed\");\n    getName(mCtx.recName);\n    mCtx.leftFile -= mCtx.recName.data_size();\n\n    // Make sure we don't carry over any old cached subrecord\n    // names. This can happen in some cases when we skip parts of a\n    // record.\n    mCtx.subCached = false;\n\n    return mCtx.recName;\n}\n\nvoid ESMReader::skipRecord()\n{\n    skip(mCtx.leftRec);\n    mCtx.leftRec = 0;\n    mCtx.subCached = false;\n}\n\nvoid ESMReader::getRecHeader(uint32_t &flags)\n{\n    // General error checking\n    if (mCtx.leftFile < 12)\n        fail(\"End of file while reading record header\");\n    if (mCtx.leftRec)\n        fail(\"Previous record contains unread bytes\");\n\n    getUint(mCtx.leftRec);\n    getUint(flags);// This header entry is always zero\n    getUint(flags);\n    mCtx.leftFile -= 12;\n\n    // Check that sizes add up\n    if (mCtx.leftFile < mCtx.leftRec)\n        fail(\"Record size is larger than rest of file\");\n\n    // Adjust number of bytes mCtx.left in file\n    mCtx.leftFile -= mCtx.leftRec;\n}\n\n/*************************************************************************\n *\n *  Lowest level data reading and misc methods\n *\n *************************************************************************/\n\nvoid ESMReader::getExact(void*x, int size)\n{\n    try\n    {\n        mEsm->read((char*)x, size);\n    }\n    catch (std::exception& e)\n    {\n        fail(std::string(\"Read error: \") + e.what());\n    }\n}\n\nstd::string ESMReader::getString(int size)\n{\n    size_t s = size;\n    if (mBuffer.size() <= s)\n        // Add some extra padding to reduce the chance of having to resize\n        // again later.\n        mBuffer.resize(3*s);\n\n    // And make sure the string is zero terminated\n    mBuffer[s] = 0;\n\n    // read ESM data\n    char *ptr = mBuffer.data();\n    getExact(ptr, size);\n\n    size = static_cast<int>(strnlen(ptr, size));\n\n    // Convert to UTF8 and return\n    if (mEncoder)\n        return mEncoder->getUtf8(ptr, size);\n\n    return std::string (ptr, size);\n}\n\nvoid ESMReader::fail(const std::string &msg)\n{\n    std::stringstream ss;\n\n    ss << \"ESM Error: \" << msg;\n    ss << \"\\n  File: \" << mCtx.filename;\n    ss << \"\\n  Record: \" << mCtx.recName.toString();\n    ss << \"\\n  Subrecord: \" << mCtx.subName.toString();\n    if (mEsm.get())\n        ss << \"\\n  Offset: 0x\" << std::hex << mEsm->tellg();\n    throw std::runtime_error(ss.str());\n}\n\nvoid ESMReader::setEncoder(ToUTF8::Utf8Encoder* encoder)\n{\n    mEncoder = encoder;\n}\n\nsize_t ESMReader::getFileOffset() const\n{\n    return mEsm->tellg();\n}\n\nvoid ESMReader::skip(int bytes)\n{\n    mEsm->seekg(getFileOffset()+bytes);\n}\n\n}\n"
  },
  {
    "path": "components/esm/esmreader.hpp",
    "content": "#ifndef OPENMW_ESM_READER_H\n#define OPENMW_ESM_READER_H\n\n#include <cstdint>\n#include <cassert>\n#include <vector>\n#include <sstream>\n\n#include <components/files/constrainedfilestream.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/to_utf8/to_utf8.hpp>\n\n#include \"esmcommon.hpp\"\n#include \"loadtes3.hpp\"\n\nnamespace ESM {\n\nclass ESMReader\n{\npublic:\n\n  ESMReader();\n\n  /*************************************************************************\n   *\n   *  Information retrieval\n   *\n   *************************************************************************/\n\n  int getVer() const { return mHeader.mData.version; }\n  int getRecordCount() const { return mHeader.mData.records; }\n  float getFVer() const { return (mHeader.mData.version == VER_12) ? 1.2f : 1.3f; }\n  const std::string getAuthor() const { return mHeader.mData.author; }\n  const std::string getDesc() const { return mHeader.mData.desc; }\n  const std::vector<Header::MasterData> &getGameFiles() const { return mHeader.mMaster; }\n  const Header& getHeader() const { return mHeader; }\n  int getFormat() const;\n  const NAME &retSubName() const { return mCtx.subName; }\n  uint32_t getSubSize() const { return mCtx.leftSub; }\n  std::string getName() const;\n\n  /*************************************************************************\n   *\n   *  Opening and closing\n   *\n   *************************************************************************/\n\n  /** Save the current file position and information in a ESM_Context\n      struct\n   */\n  ESM_Context getContext();\n\n  /** Restore a previously saved context */\n  void restoreContext(const ESM_Context &rc);\n\n  /** Close the file, resets all information. After calling close()\n      the structure may be reused to load a new file.\n  */\n  void close();\n\n  /// Raw opening. Opens the file and sets everything up but doesn't\n  /// parse the header.\n  void openRaw(Files::IStreamPtr _esm, const std::string &name);\n\n  /// Load ES file from a new stream, parses the header. Closes the\n  /// currently open file first, if any.\n  void open(Files::IStreamPtr _esm, const std::string &name);\n\n  void open(const std::string &file);\n\n  void openRaw(const std::string &filename);\n\n  /// Get the current position in the file. Make sure that the file has been opened!\n  size_t getFileOffset() const;\n\n  // This is a quick hack for multiple esm/esp files. Each plugin introduces its own\n  //  terrain palette, but ESMReader does not pass a reference to the correct plugin\n  //  to the individual load() methods. This hack allows to pass this reference\n  //  indirectly to the load() method.\n  void setIndex(const int index) { mCtx.index = index;}\n  int getIndex() {return mCtx.index;}\n\n  void setGlobalReaderList(std::vector<ESMReader> *list) {mGlobalReaderList = list;}\n  std::vector<ESMReader> *getGlobalReaderList() {return mGlobalReaderList;}\n\n  void addParentFileIndex(int index) { mCtx.parentFileIndices.push_back(index); }\n  const std::vector<int>& getParentFileIndices() const { return mCtx.parentFileIndices; }\n\n  /*************************************************************************\n   *\n   *  Medium-level reading shortcuts\n   *\n   *************************************************************************/\n\n  // Read data of a given type, stored in a subrecord of a given name\n  template <typename X>\n  void getHNT(X &x, const char* name)\n  {\n    getSubNameIs(name);\n    getHT(x);\n  }\n\n  // Optional version of getHNT\n  template <typename X>\n  void getHNOT(X &x, const char* name)\n  {\n      if(isNextSub(name))\n          getHT(x);\n  }\n\n  // Version with extra size checking, to make sure the compiler\n  // doesn't mess up our struct padding.\n  template <typename X>\n  void getHNT(X &x, const char* name, int size)\n  {\n      assert(sizeof(X) == size);\n      getSubNameIs(name);\n      getHT(x);\n  }\n\n  template <typename X>\n  void getHNOT(X &x, const char* name, int size)\n  {\n      assert(sizeof(X) == size);\n      if(isNextSub(name))\n          getHT(x);\n  }\n\n  // Get data of a given type/size, including subrecord header\n  template <typename X>\n  void getHT(X &x)\n  {\n      getSubHeader();\n      if (mCtx.leftSub != sizeof(X))\n      {\n          std::stringstream error;\n          error << \"getHT(): subrecord size mismatch (requested \" << sizeof(X) << \", got \" << mCtx.leftSub << \")\";\n          fail(error.str());\n      }\n      getT(x);\n  }\n\n  // Version with extra size checking, to make sure the compiler\n  // doesn't mess up our struct padding.\n  template <typename X>\n  void getHT(X &x, int size)\n  {\n      assert(sizeof(X) == size);\n      getHT(x);\n  }\n\n  // Read a string by the given name if it is the next record.\n  std::string getHNOString(const char* name);\n\n  // Read a string with the given sub-record name\n  std::string getHNString(const char* name);\n\n  // Read a string, including the sub-record header (but not the name)\n  std::string getHString();\n\n  // Read the given number of bytes from a subrecord\n  void getHExact(void*p, int size);\n\n  // Read the given number of bytes from a named subrecord\n  void getHNExact(void*p, int size, const char* name);\n\n  /*************************************************************************\n   *\n   *  Low level sub-record methods\n   *\n   *************************************************************************/\n\n  // Get the next subrecord name and check if it matches the parameter\n  void getSubNameIs(const char* name);\n\n  /** Checks if the next sub record name matches the parameter. If it\n      does, it is read into 'subName' just as if getSubName() was\n      called. If not, the read name will still be available for future\n      calls to getSubName(), isNextSub() and getSubNameIs().\n   */\n  bool isNextSub(const char* name);\n\n  bool peekNextSub(const char* name);\n\n  // Store the current subrecord name for the next call of getSubName()\n  void cacheSubName();\n\n  // Read subrecord name. This gets called a LOT, so I've optimized it\n  // slightly.\n  void getSubName();\n\n  // Skip current sub record, including header (but not including\n  // name.)\n  void skipHSub();\n\n  // Skip sub record and check its size\n  void skipHSubSize(int size);\n\n  // Skip all subrecords until the given subrecord or no more subrecords remaining\n  void skipHSubUntil(const char* name);\n\n  /* Sub-record header. This updates leftRec beyond the current\n     sub-record as well. leftSub contains size of current sub-record.\n  */\n  void getSubHeader();\n\n  /** Get sub header and check the size\n   */\n  void getSubHeaderIs(int size);\n\n  /*************************************************************************\n   *\n   *  Low level record methods\n   *\n   *************************************************************************/\n\n  // Get the next record name\n  NAME getRecName();\n\n  // Skip the rest of this record. Assumes the name and header have\n  // already been read\n  void skipRecord();\n\n  /* Read record header. This updatesleftFile BEYOND the data that\n     follows the header, ie beyond the entire record. You should use\n     leftRec to orient yourself inside the record itself.\n  */\n  void getRecHeader() { getRecHeader(mRecordFlags); }\n  void getRecHeader(uint32_t &flags);\n\n  bool hasMoreRecs() const { return mCtx.leftFile > 0; }\n  bool hasMoreSubs() const { return mCtx.leftRec > 0; }\n\n\n  /*************************************************************************\n   *\n   *  Lowest level data reading and misc methods\n   *\n   *************************************************************************/\n\n  template <typename X>\n  void getT(X &x) { getExact(&x, sizeof(X)); }\n\n  void getExact(void*x, int size);\n  void getName(NAME &name) { getT(name); }\n  void getUint(uint32_t &u) { getT(u); }\n\n  // Read the next 'size' bytes and return them as a string. Converts\n  // them from native encoding to UTF8 in the process.\n  std::string getString(int size);\n\n  void skip(int bytes);\n\n  /// Used for error handling\n  void fail(const std::string &msg);\n\n  /// Sets font encoder for ESM strings\n  void setEncoder(ToUTF8::Utf8Encoder* encoder);\n\n  /// Get record flags of last record\n  unsigned int getRecordFlags() { return mRecordFlags; }\n\n  size_t getFileSize() const { return mFileSize; }\n\nprivate:\n  void clearCtx();\n\n  Files::IStreamPtr mEsm;\n\n  ESM_Context mCtx;\n\n  unsigned int mRecordFlags;\n\n  // Special file signifier (see SpecialFile enum above)\n\n  // Buffer for ESM strings\n  std::vector<char> mBuffer;\n\n  Header mHeader;\n\n  std::vector<ESMReader> *mGlobalReaderList;\n  ToUTF8::Utf8Encoder* mEncoder;\n\n  size_t mFileSize;\n\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/esmwriter.cpp",
    "content": "#include \"esmwriter.hpp\"\n\n#include <cassert>\n#include <fstream>\n#include <stdexcept>\n\n#include <components/to_utf8/to_utf8.hpp>\n\nnamespace ESM\n{\n    ESMWriter::ESMWriter()\n        : mRecords()\n        , mStream(nullptr)\n        , mHeaderPos()\n        , mEncoder(nullptr)\n        , mRecordCount(0)\n        , mCounting(true)\n        , mHeader()\n    {}\n\n    unsigned int ESMWriter::getVersion() const\n    {\n        return mHeader.mData.version;\n    }\n\n    void ESMWriter::setVersion(unsigned int ver)\n    {\n        mHeader.mData.version = ver;\n    }\n\n    void ESMWriter::setType(int type)\n    {\n        mHeader.mData.type = type;\n    }\n\n    void ESMWriter::setAuthor(const std::string& auth)\n    {\n        mHeader.mData.author.assign (auth);\n    }\n\n    void ESMWriter::setDescription(const std::string& desc)\n    {\n        mHeader.mData.desc.assign (desc);\n    }\n\n    void ESMWriter::setRecordCount (int count)\n    {\n        mHeader.mData.records = count;\n    }\n\n    void ESMWriter::setFormat (int format)\n    {\n        mHeader.mFormat = format;\n    }\n\n    void ESMWriter::clearMaster()\n    {\n        mHeader.mMaster.clear();\n    }\n\n    void ESMWriter::addMaster(const std::string& name, uint64_t size)\n    {\n        Header::MasterData d;\n        d.name = name;\n        d.size = size;\n        mHeader.mMaster.push_back(d);\n    }\n\n    void ESMWriter::save(std::ostream& file)\n    {\n        mRecordCount = 0;\n        mRecords.clear();\n        mCounting = true;\n        mStream = &file;\n\n        startRecord(\"TES3\", 0);\n\n        mHeader.save (*this);\n\n        endRecord(\"TES3\");\n    }\n\n    void ESMWriter::close()\n    {\n        if (!mRecords.empty())\n            throw std::runtime_error (\"Unclosed record remaining\");\n    }\n\n    void ESMWriter::startRecord(const std::string& name, uint32_t flags)\n    {\n        mRecordCount++;\n\n        writeName(name);\n        RecordData rec;\n        rec.name = name;\n        rec.position = mStream->tellp();\n        rec.size = 0;\n        writeT<uint32_t>(0); // Size goes here\n        writeT<uint32_t>(0); // Unused header?\n        writeT(flags);\n        mRecords.push_back(rec);\n\n        assert(mRecords.back().size == 0);\n    }\n\n    void ESMWriter::startRecord (uint32_t name, uint32_t flags)\n    {\n        std::string type;\n        for (int i=0; i<4; ++i)\n            /// \\todo make endianess agnostic\n            type += reinterpret_cast<const char *> (&name)[i];\n\n        startRecord (type, flags);\n    }\n\n    void ESMWriter::startSubRecord(const std::string& name)\n    {\n        // Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later.\n        assert (mRecords.size() <= 1);\n\n        writeName(name);\n        RecordData rec;\n        rec.name = name;\n        rec.position = mStream->tellp();\n        rec.size = 0;\n        writeT<uint32_t>(0); // Size goes here\n        mRecords.push_back(rec);\n\n        assert(mRecords.back().size == 0);\n    }\n\n    void ESMWriter::endRecord(const std::string& name)\n    {\n        RecordData rec = mRecords.back();\n        assert(rec.name == name);\n        mRecords.pop_back();\n\n        mStream->seekp(rec.position);\n\n        mCounting = false;\n        write (reinterpret_cast<const char*> (&rec.size), sizeof(uint32_t));\n        mCounting = true;\n\n        mStream->seekp(0, std::ios::end);\n\n    }\n\n    void ESMWriter::endRecord (uint32_t name)\n    {\n        std::string type;\n        for (int i=0; i<4; ++i)\n            /// \\todo make endianess agnostic\n            type += reinterpret_cast<const char *> (&name)[i];\n\n        endRecord (type);\n    }\n\n    void ESMWriter::writeHNString(const std::string& name, const std::string& data)\n    {\n        startSubRecord(name);\n        writeHString(data);\n        endRecord(name);\n    }\n\n    void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size)\n    {\n        assert(data.size() <= size);\n        startSubRecord(name);\n        writeHString(data);\n\n        if (data.size() < size)\n        {\n            for (size_t i = data.size(); i < size; ++i)\n                write(\"\\0\",1);\n        }\n\n        endRecord(name);\n    }\n\n    void ESMWriter::writeFixedSizeString(const std::string &data, int size)\n    {\n        std::string string;\n        if (!data.empty())\n            string = mEncoder ? mEncoder->getLegacyEnc(data) : data;\n        string.resize(size);\n        write(string.c_str(), string.size());\n    }\n\n    void ESMWriter::writeHString(const std::string& data)\n    {\n        if (data.size() == 0)\n            write(\"\\0\", 1);\n        else\n        {\n            // Convert to UTF8 and return\n            std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data;\n\n            write(string.c_str(), string.size());\n        }\n    }\n\n    void ESMWriter::writeHCString(const std::string& data)\n    {\n        writeHString(data);\n        if (data.size() > 0 && data[data.size()-1] != '\\0')\n            write(\"\\0\", 1);\n    }\n\n    void ESMWriter::writeName(const std::string& name)\n    {\n        assert((name.size() == 4 && name[3] != '\\0'));\n        write(name.c_str(), name.size());\n    }\n\n    void ESMWriter::write(const char* data, size_t size)\n    {\n        if (mCounting && !mRecords.empty())\n        {\n            for (std::list<RecordData>::iterator it = mRecords.begin(); it != mRecords.end(); ++it)\n                it->size += static_cast<uint32_t>(size);\n        }\n\n        mStream->write(data, size);\n    }\n\n    void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)\n    {\n        mEncoder = encoder;\n    }\n}\n"
  },
  {
    "path": "components/esm/esmwriter.hpp",
    "content": "#ifndef OPENMW_ESM_WRITER_H\n#define OPENMW_ESM_WRITER_H\n\n#include <iosfwd>\n#include <list>\n\n#include \"esmcommon.hpp\"\n#include \"loadtes3.hpp\"\n\nnamespace ToUTF8\n{\n    class Utf8Encoder;\n}\n\nnamespace ESM {\n\nclass ESMWriter\n{\n        struct RecordData\n        {\n            std::string name;\n            std::streampos position;\n            uint32_t size;\n        };\n\n    public:\n\n        ESMWriter();\n\n        unsigned int getVersion() const;\n\n        // Set various header data (ESM::Header::Data). All of the below functions must be called before writing,\n        // otherwise this data will be left uninitialized.\n        void setVersion(unsigned int ver = 0x3fa66666);\n        void setType(int type);\n        void setEncoder(ToUTF8::Utf8Encoder *encoding);\n        void setAuthor(const std::string& author);\n        void setDescription(const std::string& desc);\n\n        // Set the record count for writing it in the file header\n        void setRecordCount (int count);\n        // Counts how many records we have actually written.\n        // It is a good idea to compare this with the value you wrote into the header (setRecordCount)\n        // It should be the record count you set + 1 (1 additional record for the TES3 header)\n        int getRecordCount() { return mRecordCount; }\n        void setFormat (int format);\n\n        void clearMaster();\n\n        void addMaster(const std::string& name, uint64_t size);\n\n        void save(std::ostream& file);\n        ///< Start saving a file by writing the TES3 header.\n\n        void close();\n        ///< \\note Does not close the stream.\n\n        void writeHNString(const std::string& name, const std::string& data);\n        void writeHNString(const std::string& name, const std::string& data, size_t size);\n        void writeHNCString(const std::string& name, const std::string& data)\n        {\n            startSubRecord(name);\n            writeHCString(data);\n            endRecord(name);\n        }\n        void writeHNOString(const std::string& name, const std::string& data)\n        {\n            if (!data.empty())\n                writeHNString(name, data);\n        }\n        void writeHNOCString(const std::string& name, const std::string& data)\n        {\n            if (!data.empty())\n                writeHNCString(name, data);\n        }\n\n        template<typename T>\n        void writeHNT(const std::string& name, const T& data)\n        {\n            startSubRecord(name);\n            writeT(data);\n            endRecord(name);\n        }\n\n        template<typename T, std::size_t size>\n        void writeHNT(const std::string& name, const T (&data)[size])\n        {\n            startSubRecord(name);\n            writeT(data);\n            endRecord(name);\n        }\n\n        // Prevent using writeHNT with strings. This already happened by accident and results in\n        // state being discarded without any error on writing or reading it. :(\n        // writeHNString and friends must be used instead.\n        void writeHNT(const std::string& name, const std::string& data) = delete;\n\n        void writeT(const std::string& data) = delete;\n\n        template<typename T, std::size_t size>\n        void writeHNT(const std::string& name, const T (&data)[size], int) = delete;\n\n        template<typename T>\n        void writeHNT(const std::string& name, const T& data, int size)\n        {\n            startSubRecord(name);\n            writeT(data, size);\n            endRecord(name);\n        }\n\n        template<typename T>\n        void writeT(const T& data)\n        {\n            write((char*)&data, sizeof(T));\n        }\n\n        template<typename T, std::size_t size>\n        void writeT(const T (&data)[size])\n        {\n            write(reinterpret_cast<const char*>(data), size * sizeof(T));\n        }\n\n        template<typename T>\n        void writeT(const T& data, size_t size)\n        {\n            write((char*)&data, size);\n        }\n\n        void startRecord(const std::string& name, uint32_t flags = 0);\n        void startRecord(uint32_t name, uint32_t flags = 0);\n        /// @note Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later.\n        void startSubRecord(const std::string& name);\n        void endRecord(const std::string& name);\n        void endRecord(uint32_t name);\n        void writeFixedSizeString(const std::string& data, int size);\n        void writeHString(const std::string& data);\n        void writeHCString(const std::string& data);\n        void writeName(const std::string& data);\n        void write(const char* data, size_t size);\n\n    private:\n        std::list<RecordData> mRecords;\n        std::ostream* mStream;\n        std::streampos mHeaderPos;\n        ToUTF8::Utf8Encoder* mEncoder;\n        int mRecordCount;\n        bool mCounting;\n\n        Header mHeader;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/filter.cpp",
    "content": "#include \"filter.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nunsigned int ESM::Filter::sRecordId = REC_FILT;\n\nvoid ESM::Filter::load (ESMReader& esm, bool &isDeleted)\n{\n    isDeleted = false;\n\n    while (esm.hasMoreSubs())\n    {\n        esm.getSubName();\n        uint32_t name = esm.retSubName().intval;\n        switch (name)\n        {\n            case ESM::SREC_NAME:\n                mId = esm.getHString();\n                break;\n            case ESM::FourCC<'F','I','L','T'>::value:\n                mFilter = esm.getHString();\n                break;\n            case ESM::FourCC<'D','E','S','C'>::value:\n                mDescription = esm.getHString();\n                break;\n            case ESM::SREC_DELE:\n                esm.skipHSub();\n                isDeleted = true;\n                break;\n            default:\n                esm.fail(\"Unknown subrecord\");\n                break;\n        }\n    }\n}\n\nvoid ESM::Filter::save (ESMWriter& esm, bool isDeleted) const\n{\n    esm.writeHNCString (\"NAME\", mId);\n\n    if (isDeleted)\n    {\n        esm.writeHNCString(\"DELE\", \"\");\n        return;\n    }\n\n    esm.writeHNCString (\"FILT\", mFilter);\n    esm.writeHNCString (\"DESC\", mDescription);\n}\n\nvoid ESM::Filter::blank()\n{\n    mFilter.clear();\n    mDescription.clear();\n}\n"
  },
  {
    "path": "components/esm/filter.hpp",
    "content": "#ifndef COMPONENTS_ESM_FILTER_H\n#define COMPONENTS_ESM_FILTER_H\n\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    struct Filter\n    {\n        static unsigned int sRecordId;\n\n        std::string mId;\n\n        std::string mDescription;\n\n        std::string mFilter;\n\n        void load (ESMReader& esm, bool &isDeleted);\n        void save (ESMWriter& esm, bool isDeleted = false) const;\n\n        void blank();\n        ///< Set record to default state (does not touch the ID).\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/fogstate.cpp",
    "content": "#include \"fogstate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\n#include <osgDB/ReadFile>\n\n#include <components/debug/debuglog.hpp>\n#include <components/files/memorystream.hpp>\n\n#include \"savedgame.hpp\"\n\nvoid convertFogOfWar(std::vector<char>& imageData)\n{\n    if (imageData.empty())\n    {\n        return;\n    }\n\n    osgDB::ReaderWriter* tgaReader = osgDB::Registry::instance()->getReaderWriterForExtension(\"tga\");\n    if (!tgaReader)\n    {\n        Log(Debug::Error) << \"Error: Unable to load fog, can't find a tga ReaderWriter\";\n        return;\n    }\n\n    Files::IMemStream in(&imageData[0], imageData.size());\n\n    osgDB::ReaderWriter::ReadResult result = tgaReader->readImage(in);\n    if (!result.success())\n    {\n        Log(Debug::Error) << \"Error: Failed to read fog: \" << result.message() << \" code \" << result.status();\n        return;\n    }\n\n    osgDB::ReaderWriter* pngWriter = osgDB::Registry::instance()->getReaderWriterForExtension(\"png\");\n    if (!pngWriter)\n    {\n        Log(Debug::Error) << \"Error: Unable to write fog, can't find a png ReaderWriter\";\n        return;\n    }\n\n    std::ostringstream ostream;\n    osgDB::ReaderWriter::WriteResult png = pngWriter->writeImage(*result.getImage(), ostream);\n    if (!png.success())\n    {\n        Log(Debug::Error) << \"Error: Unable to write fog: \" << png.message() << \" code \" << png.status();\n        return;\n    }\n\n    std::string str = ostream.str();\n    imageData = std::vector<char>(str.begin(), str.end());\n}\n\nvoid ESM::FogState::load (ESMReader &esm)\n{\n    esm.getHNOT(mBounds, \"BOUN\");\n    esm.getHNOT(mNorthMarkerAngle, \"ANGL\");\n    int dataFormat = esm.getFormat();\n    while (esm.isNextSub(\"FTEX\"))\n    {\n        esm.getSubHeader();\n        FogTexture tex;\n\n        esm.getT(tex.mX);\n        esm.getT(tex.mY);\n\n        size_t imageSize = esm.getSubSize()-sizeof(int)*2;\n        tex.mImageData.resize(imageSize);\n        esm.getExact(&tex.mImageData[0], imageSize);\n\n        if (dataFormat < 7)\n            convertFogOfWar(tex.mImageData);\n\n        mFogTextures.push_back(tex);\n    }\n}\n\nvoid ESM::FogState::save (ESMWriter &esm, bool interiorCell) const\n{\n    if (interiorCell)\n    {\n        esm.writeHNT(\"BOUN\", mBounds);\n        esm.writeHNT(\"ANGL\", mNorthMarkerAngle);\n    }\n    for (std::vector<FogTexture>::const_iterator it = mFogTextures.begin(); it != mFogTextures.end(); ++it)\n    {\n        esm.startSubRecord(\"FTEX\");\n        esm.writeT(it->mX);\n        esm.writeT(it->mY);\n        esm.write(&it->mImageData[0], it->mImageData.size());\n        esm.endRecord(\"FTEX\");\n    }\n}\n"
  },
  {
    "path": "components/esm/fogstate.hpp",
    "content": "#ifndef OPENMW_ESM_FOGSTATE_H\n#define OPENMW_ESM_FOGSTATE_H\n\n#include <vector>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    struct FogTexture\n    {\n        int mX, mY; // Only used for interior cells\n        std::vector<char> mImageData;\n    };\n\n    // format 0, saved games only\n    // Fog of war state\n    struct FogState\n    {\n        // Only used for interior cells\n        float mNorthMarkerAngle;\n        struct Bounds\n        {\n            float mMinX;\n            float mMinY;\n            float mMaxX;\n            float mMaxY;\n        } mBounds;\n\n        std::vector<FogTexture> mFogTextures;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm, bool interiorCell) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/globalmap.cpp",
    "content": "#include \"globalmap.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nunsigned int ESM::GlobalMap::sRecordId = ESM::REC_GMAP;\n\nvoid ESM::GlobalMap::load (ESMReader &esm)\n{\n    esm.getHNT(mBounds, \"BNDS\");\n\n    esm.getSubNameIs(\"DATA\");\n    esm.getSubHeader();\n    mImageData.resize(esm.getSubSize());\n    esm.getExact(&mImageData[0], mImageData.size());\n\n    while (esm.isNextSub(\"MRK_\"))\n    {\n        esm.getSubHeader();\n        CellId cell;\n        esm.getT(cell.first);\n        esm.getT(cell.second);\n        mMarkers.insert(cell);\n    }\n}\n\nvoid ESM::GlobalMap::save (ESMWriter &esm) const\n{\n    esm.writeHNT(\"BNDS\", mBounds);\n\n    esm.startSubRecord(\"DATA\");\n    esm.write(&mImageData[0], mImageData.size());\n    esm.endRecord(\"DATA\");\n\n    for (std::set<CellId>::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it)\n    {\n        esm.startSubRecord(\"MRK_\");\n        esm.writeT(it->first);\n        esm.writeT(it->second);\n        esm.endRecord(\"MRK_\");\n    }\n}\n"
  },
  {
    "path": "components/esm/globalmap.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_ESM_GLOBALMAP_H\n#define OPENMW_COMPONENTS_ESM_GLOBALMAP_H\n\n#include <vector>\n#include <set>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    ///< \\brief An image containing the explored areas on the global map.\n    struct GlobalMap\n    {\n        static unsigned int sRecordId;\n\n        // The minimum and maximum cell coordinates\n        struct Bounds\n        {\n            int mMinX, mMaxX, mMinY, mMaxY;\n        };\n\n        Bounds mBounds;\n\n        std::vector<char> mImageData;\n\n        typedef std::pair<int, int> CellId;\n        std::set<CellId> mMarkers;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/globalscript.cpp",
    "content": "#include \"globalscript.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::GlobalScript::load (ESMReader &esm)\n{\n    mId = esm.getHNString (\"NAME\");\n\n    mLocals.load (esm);\n\n    mRunning = 0;\n    esm.getHNOT (mRunning, \"RUN_\");\n\n    mTargetRef.unset();\n    mTargetId = esm.getHNOString (\"TARG\");\n    if (esm.peekNextSub(\"FRMR\"))\n        mTargetRef.load(esm, true, \"FRMR\");\n}\n\nvoid ESM::GlobalScript::save (ESMWriter &esm) const\n{\n    esm.writeHNString (\"NAME\", mId);\n\n    mLocals.save (esm);\n\n    if (mRunning)\n        esm.writeHNT (\"RUN_\", mRunning);\n\n    if (!mTargetId.empty())\n    {\n        esm.writeHNOString (\"TARG\", mTargetId);\n        if (mTargetRef.hasContentFile())\n            mTargetRef.save (esm, true, \"FRMR\");\n    }\n}\n"
  },
  {
    "path": "components/esm/globalscript.hpp",
    "content": "#ifndef OPENMW_ESM_GLOBALSCRIPT_H\n#define OPENMW_ESM_GLOBALSCRIPT_H\n\n#include \"locals.hpp\"\n#include \"cellref.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    /// \\brief Storage structure for global script state (only used in saved games)\n\n    struct GlobalScript\n    {\n        std::string mId; /// \\note must be lowercase\n        Locals mLocals;\n        int mRunning;\n        std::string mTargetId; // for targeted scripts\n        RefNum mTargetRef;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/inventorystate.cpp",
    "content": "#include \"inventorystate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\n#include <components/misc/stringops.hpp>\n\nvoid ESM::InventoryState::load (ESMReader &esm)\n{\n    // obsolete\n    int index = 0;\n    while (esm.isNextSub (\"IOBJ\"))\n    {\n        int unused; // no longer used\n        esm.getHT(unused);\n\n        ObjectState state;\n\n        // obsolete\n        if (esm.isNextSub(\"SLOT\"))\n        {\n            int slot;\n            esm.getHT(slot);\n            mEquipmentSlots[index] = slot;\n        }\n\n        state.mRef.loadId(esm, true);\n        state.load (esm);\n\n        if (state.mCount == 0)\n            continue;\n\n        mItems.push_back (state);\n\n        ++index;\n    }\n\n    int itemsCount = 0;\n    esm.getHNOT(itemsCount, \"ICNT\");\n    for (int i = 0; i < itemsCount; i++)\n    {\n        ObjectState state;\n\n        state.mRef.loadId(esm, true);\n        state.load (esm);\n\n        if (state.mCount == 0)\n            continue;\n\n        mItems.push_back (state);\n    }\n\n    //Next item is Levelled item\n    while (esm.isNextSub(\"LEVM\"))\n    {\n        //Get its name\n        std::string id = esm.getHString();\n        int count;\n        std::string parentGroup = \"\";\n        //Then get its count\n        esm.getHNT (count, \"COUN\");\n        //Old save formats don't have information about parent group; check for that\n        if(esm.isNextSub(\"LGRP\"))\n            //Newest saves contain parent group\n            parentGroup = esm.getHString();\n        mLevelledItemMap[std::make_pair(id, parentGroup)] = count;\n    }\n\n    while (esm.isNextSub(\"MAGI\"))\n    {\n        std::string id = esm.getHString();\n\n        std::vector<std::pair<float, float> > params;\n        while (esm.isNextSub(\"RAND\"))\n        {\n            float rand, multiplier;\n            esm.getHT (rand);\n            esm.getHNT (multiplier, \"MULT\");\n            params.emplace_back(rand, multiplier);\n        }\n        mPermanentMagicEffectMagnitudes[id] = params;\n    }\n\n    while (esm.isNextSub(\"EQUI\"))\n    {\n        esm.getSubHeader();\n        int equipIndex;\n        esm.getT(equipIndex);\n        int slot;\n        esm.getT(slot);\n        mEquipmentSlots[equipIndex] = slot;\n    }\n\n    if (esm.isNextSub(\"EQIP\"))\n    {\n        esm.getSubHeader();\n        int slotsCount = 0;\n        esm.getT(slotsCount);\n        for (int i = 0; i < slotsCount; i++)\n        {\n            int equipIndex;\n            esm.getT(equipIndex);\n            int slot;\n            esm.getT(slot);\n            mEquipmentSlots[equipIndex] = slot;\n        }\n    }\n\n    mSelectedEnchantItem = -1;\n    esm.getHNOT(mSelectedEnchantItem, \"SELE\");\n\n    // Old saves had restocking levelled items in a special map\n    // This turns items from that map into negative quantities\n    for(const auto& entry : mLevelledItemMap)\n    {\n        const std::string& id = entry.first.first;\n        const int count = entry.second;\n        for(auto& item : mItems)\n        {\n            if(item.mCount == count && Misc::StringUtils::ciEqual(id, item.mRef.mRefID))\n                item.mCount = -count;\n        }\n    }\n}\n\nvoid ESM::InventoryState::save (ESMWriter &esm) const\n{\n    int itemsCount = static_cast<int>(mItems.size());\n    if (itemsCount > 0)\n    {\n        esm.writeHNT (\"ICNT\", itemsCount);\n        for (const ObjectState& state : mItems)\n        {\n            state.save (esm, true);\n        }\n    }\n\n    for (std::map<std::pair<std::string, std::string>, int>::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it)\n    {\n        esm.writeHNString (\"LEVM\", it->first.first);\n        esm.writeHNT (\"COUN\", it->second);\n        esm.writeHNString(\"LGRP\", it->first.second);\n    }\n\n    for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it)\n    {\n        esm.writeHNString(\"MAGI\", it->first);\n\n        const std::vector<std::pair<float, float> >& params = it->second;\n        for (std::vector<std::pair<float, float> >::const_iterator pIt = params.begin(); pIt != params.end(); ++pIt)\n        {\n            esm.writeHNT (\"RAND\", pIt->first);\n            esm.writeHNT (\"MULT\", pIt->second);\n        }\n    }\n\n    int slotsCount = static_cast<int>(mEquipmentSlots.size());\n    if (slotsCount > 0)\n    {\n        esm.startSubRecord(\"EQIP\");\n        esm.writeT(slotsCount);\n        for (std::map<int, int>::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it)\n        {\n            esm.writeT(it->first);\n            esm.writeT(it->second);\n        }\n        esm.endRecord(\"EQIP\");\n    }\n\n    if (mSelectedEnchantItem != -1)\n        esm.writeHNT (\"SELE\", mSelectedEnchantItem);\n}\n"
  },
  {
    "path": "components/esm/inventorystate.hpp",
    "content": "#ifndef OPENMW_ESM_INVENTORYSTATE_H\n#define OPENMW_ESM_INVENTORYSTATE_H\n\n#include <map>\n\n#include \"objectstate.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    /// \\brief State for inventories and containers\n    struct InventoryState\n    {\n        std::vector<ObjectState> mItems;\n\n        // <Index in mItems, equipment slot>\n        std::map<int, int> mEquipmentSlots;\n\n        std::map<std::pair<std::string, std::string>, int> mLevelledItemMap;\n\n        typedef std::map<std::string, std::vector<std::pair<float, float> > > TEffectMagnitudes;\n        TEffectMagnitudes mPermanentMagicEffectMagnitudes;\n\n        int mSelectedEnchantItem; // For inventories only\n\n        InventoryState() : mSelectedEnchantItem(-1) {}\n        virtual ~InventoryState() {}\n\n        virtual void load (ESMReader &esm);\n        virtual void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/journalentry.cpp",
    "content": "#include \"journalentry.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::JournalEntry::load (ESMReader &esm)\n{\n    esm.getHNOT (mType, \"JETY\");\n    mTopic = esm.getHNString (\"YETO\");\n    mInfo = esm.getHNString (\"YEIN\");\n    mText = esm.getHNString (\"TEXT\");\n\n    if (mType==Type_Journal)\n    {\n        esm.getHNT (mDay, \"JEDA\");\n        esm.getHNT (mMonth, \"JEMO\");\n        esm.getHNT (mDayOfMonth, \"JEDM\");\n    }\n    else if (mType==Type_Topic)\n        mActorName = esm.getHNOString(\"ACT_\");\n}\n\nvoid ESM::JournalEntry::save (ESMWriter &esm) const\n{\n    esm.writeHNT (\"JETY\", mType);\n    esm.writeHNString (\"YETO\", mTopic);\n    esm.writeHNString (\"YEIN\", mInfo);\n    esm.writeHNString (\"TEXT\", mText);\n\n    if (mType==Type_Journal)\n    {\n        esm.writeHNT (\"JEDA\", mDay);\n        esm.writeHNT (\"JEMO\", mMonth);\n        esm.writeHNT (\"JEDM\", mDayOfMonth);\n    }\n    else if (mType==Type_Topic)\n        esm.writeHNString (\"ACT_\", mActorName);\n}\n"
  },
  {
    "path": "components/esm/journalentry.hpp",
    "content": "#ifndef OPENMW_ESM_JOURNALENTRY_H\n#define OPENMW_ESM_JOURNALENTRY_H\n\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    struct JournalEntry\n    {\n        enum Type\n        {\n            Type_Journal = 0,\n            Type_Topic = 1,\n            Type_Quest = 2\n        };\n\n        int mType;\n        std::string mTopic;\n        std::string mInfo;\n        std::string mText;\n        std::string mActorName; // Could also be Actor ID to allow switching of localisation, but since mText is plaintext anyway...\n        int mDay; // time stamp\n        int mMonth;\n        int mDayOfMonth;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/loadacti.cpp",
    "content": "#include \"loadacti.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Activator::sRecordId = REC_ACTI;\n\n    void Activator::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n    }\n    void Activator::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNOCString(\"SCRI\", mScript);\n    }\n\n    void Activator::blank()\n    {\n        mName.clear();\n        mScript.clear();\n        mModel.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadacti.hpp",
    "content": "#ifndef OPENMW_ESM_ACTI_H\n#define OPENMW_ESM_ACTI_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct Activator\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Activator\"; }\n\n    std::string mId, mName, mScript, mModel;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadalch.cpp",
    "content": "#include \"loadalch.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Potion::sRecordId = REC_ALCH;\n\n    void Potion::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mEffects.mList.clear();\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'T','E','X','T'>::value: // not ITEX here for some reason\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'A','L','D','T'>::value:\n                    esm.getHT(mData, 12);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'E','N','A','M'>::value:\n                    mEffects.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing ALDT subrecord\");\n    }\n    void Potion::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"TEXT\", mIcon);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"ALDT\", mData, 12);\n        mEffects.save(esm);\n    }\n\n    void Potion::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mAutoCalc = 0;\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n        mEffects.mList.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadalch.hpp",
    "content": "#ifndef OPENMW_ESM_ALCH_H\n#define OPENMW_ESM_ALCH_H\n\n#include <string>\n\n#include \"effectlist.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Alchemy item (potions)\n */\n\nstruct Potion\n{\n    static unsigned int sRecordId;\n\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Potion\"; }\n\n    struct ALDTstruct\n    {\n        float mWeight;\n        int mValue;\n        int mAutoCalc;\n    };\n    ALDTstruct mData;\n\n    std::string mId, mName, mModel, mIcon, mScript;\n    EffectList mEffects;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n\n    };\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadappa.cpp",
    "content": "#include \"loadappa.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Apparatus::sRecordId = REC_APPA;\n\n    void Apparatus::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'A','A','D','T'>::value:\n                    esm.getHT(mData);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing AADT subrecord\");\n    }\n\n    void Apparatus::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNCString(\"FNAM\", mName);\n        esm.writeHNT(\"AADT\", mData, 16);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNCString(\"ITEX\", mIcon);\n    }\n\n    void Apparatus::blank()\n    {\n        mData.mType = 0;\n        mData.mQuality = 0;\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n        mName.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadappa.hpp",
    "content": "#ifndef OPENMW_ESM_APPA_H\n#define OPENMW_ESM_APPA_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Alchemist apparatus\n */\n\nstruct Apparatus\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Apparatus\"; }\n\n    enum AppaType\n    {\n        MortarPestle = 0,\n        Alembic = 1,\n        Calcinator = 2,\n        Retort = 3\n    };\n\n    struct AADTstruct\n    {\n        int mType;\n        float mQuality;\n        float mWeight;\n        int mValue;\n    };\n\n    AADTstruct mData;\n    std::string mId, mModel, mIcon, mScript, mName;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadarmo.cpp",
    "content": "#include \"loadarmo.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n\n    void PartReferenceList::add(ESMReader &esm)\n    {\n        PartReference pr;\n        esm.getHT(pr.mPart); // The INDX byte\n        pr.mMale = esm.getHNOString(\"BNAM\");\n        pr.mFemale = esm.getHNOString(\"CNAM\");\n        mParts.push_back(pr);\n\n    }\n\n    void PartReferenceList::load(ESMReader &esm)\n    {\n        mParts.clear();\n        while (esm.isNextSub(\"INDX\"))\n        {\n            add(esm);\n        }\n    }\n\n    void PartReferenceList::save(ESMWriter &esm) const\n    {\n        for (std::vector<PartReference>::const_iterator it = mParts.begin(); it != mParts.end(); ++it)\n        {\n            esm.writeHNT(\"INDX\", it->mPart);\n            esm.writeHNOString(\"BNAM\", it->mMale);\n            esm.writeHNOString(\"CNAM\", it->mFemale);\n        }\n    }\n\n    unsigned int Armor::sRecordId = REC_ARMO;\n\n    void Armor::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mParts.mParts.clear();\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'A','O','D','T'>::value:\n                    esm.getHT(mData, 24);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::FourCC<'E','N','A','M'>::value:\n                    mEnchant = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','N','D','X'>::value:\n                    mParts.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing AODT subrecord\");\n    }\n\n    void Armor::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNT(\"AODT\", mData, 24);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n        mParts.save(esm);\n        esm.writeHNOCString(\"ENAM\", mEnchant);\n    }\n\n    void Armor::blank()\n    {\n        mData.mType = 0;\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mHealth = 0;\n        mData.mEnchant = 0;\n        mData.mArmor = 0;\n        mParts.mParts.clear();\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n        mEnchant.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadarmo.hpp",
    "content": "#ifndef OPENMW_ESM_ARMO_H\n#define OPENMW_ESM_ARMO_H\n\n#include <vector>\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nenum PartReferenceType\n{\n    PRT_Head = 0,\n    PRT_Hair = 1,\n    PRT_Neck = 2,\n    PRT_Cuirass = 3,\n    PRT_Groin = 4,\n    PRT_Skirt = 5,\n    PRT_RHand = 6,\n    PRT_LHand = 7,\n    PRT_RWrist = 8,\n    PRT_LWrist = 9,\n    PRT_Shield = 10,\n    PRT_RForearm = 11,\n    PRT_LForearm = 12,\n    PRT_RUpperarm = 13,\n    PRT_LUpperarm = 14,\n    PRT_RFoot = 15,\n    PRT_LFoot = 16,\n    PRT_RAnkle = 17,\n    PRT_LAnkle = 18,\n    PRT_RKnee = 19,\n    PRT_LKnee = 20,\n    PRT_RLeg = 21,\n    PRT_LLeg = 22,\n    PRT_RPauldron = 23,\n    PRT_LPauldron = 24,\n    PRT_Weapon = 25,\n    PRT_Tail = 26,\n\n    PRT_Count = 27\n};\n\n// Reference to body parts\nstruct PartReference\n{\n    unsigned char mPart; // possible values [0, 26]\n    std::string mMale, mFemale;\n};\n\n// A list of references to body parts\nstruct PartReferenceList\n{\n    std::vector<PartReference> mParts;\n\n    /// Load one part, assumes the subrecord name was already read\n    void add(ESMReader &esm);\n\n    /// TODO: remove this method. The ESM format does not guarantee that all Part subrecords follow one another.\n    void load(ESMReader &esm);\n    void save(ESMWriter &esm) const;\n};\n\nstruct Armor\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Armor\"; }\n\n    enum Type\n    {\n        Helmet = 0,\n        Cuirass = 1,\n        LPauldron = 2,\n        RPauldron = 3,\n        Greaves = 4,\n        Boots = 5,\n        LGauntlet = 6,\n        RGauntlet = 7,\n        Shield = 8,\n        LBracer = 9,\n        RBracer = 10\n    };\n\n    struct AODTstruct\n    {\n        int mType;\n        float mWeight;\n        int mValue, mHealth, mEnchant, mArmor;\n    };\n\n    AODTstruct mData;\n    PartReferenceList mParts;\n\n    std::string mId, mName, mModel, mIcon, mScript, mEnchant;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadbody.cpp",
    "content": "#include \"loadbody.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int BodyPart::sRecordId = REC_BODY;\n\n    void BodyPart::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mRace = esm.getHString();\n                    break;\n                case ESM::FourCC<'B','Y','D','T'>::value:\n                    esm.getHT(mData, 4);\n                    hasData = true;\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing BYDT subrecord\");\n    }\n\n    void BodyPart::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mRace);\n        esm.writeHNT(\"BYDT\", mData, 4);\n    }\n\n    void BodyPart::blank()\n    {\n        mData.mPart = 0;\n        mData.mVampire = 0;\n        mData.mFlags = 0;\n        mData.mType = 0;\n\n        mModel.clear();\n        mRace.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadbody.hpp",
    "content": "#ifndef OPENMW_ESM_BODY_H\n#define OPENMW_ESM_BODY_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct BodyPart\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"BodyPart\"; }\n\n    enum MeshPart\n    {\n        MP_Head = 0,\n        MP_Hair = 1,\n        MP_Neck = 2,\n        MP_Chest = 3,\n        MP_Groin = 4,\n        MP_Hand = 5,\n        MP_Wrist = 6,\n        MP_Forearm = 7,\n        MP_Upperarm = 8,\n        MP_Foot = 9,\n        MP_Ankle = 10,\n        MP_Knee = 11,\n        MP_Upperleg = 12,\n        MP_Clavicle = 13,\n        MP_Tail = 14,\n\n        MP_Count = 15\n    };\n\n    enum Flags\n    {\n        BPF_Female = 1,\n        BPF_NotPlayable = 2\n    };\n\n    enum MeshType\n    {\n        MT_Skin = 0,\n        MT_Clothing = 1,\n        MT_Armor = 2\n    };\n\n    struct BYDTstruct\n    {\n        unsigned char mPart; // mesh part\n        unsigned char mVampire; // boolean\n        unsigned char mFlags;\n        unsigned char mType; // mesh type\n    };\n\n    BYDTstruct mData;\n    std::string mId, mModel, mRace;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadbook.cpp",
    "content": "#include \"loadbook.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Book::sRecordId = REC_BOOK;\n\n    void Book::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'B','K','D','T'>::value:\n                    esm.getHT(mData, 20);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::FourCC<'E','N','A','M'>::value:\n                    mEnchant = esm.getHString();\n                    break;\n                case ESM::FourCC<'T','E','X','T'>::value:\n                    mText = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing BKDT subrecord\");\n    }\n    void Book::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"BKDT\", mData, 20);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n        esm.writeHNOString(\"TEXT\", mText);\n        esm.writeHNOCString(\"ENAM\", mEnchant);\n    }\n\n    void Book::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mIsScroll = 0;\n        mData.mSkillId = 0;\n        mData.mEnchant = 0;\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n        mEnchant.clear();\n        mText.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadbook.hpp",
    "content": "#ifndef OPENMW_ESM_BOOK_H\n#define OPENMW_ESM_BOOK_H\n\n#include <string>\n\nnamespace ESM\n{\n/*\n * Books, magic scrolls, notes and so on\n */\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct Book\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Book\"; }\n\n    struct BKDTstruct\n    {\n        float mWeight;\n        int mValue, mIsScroll, mSkillId, mEnchant;\n    };\n\n    BKDTstruct mData;\n    std::string mName, mModel, mIcon, mScript, mEnchant, mText;\n    std::string mId;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadbsgn.cpp",
    "content": "#include \"loadbsgn.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int BirthSign::sRecordId = REC_BSGN;\n\n    void BirthSign::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mPowers.mList.clear();\n\n        bool hasName = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'T','N','A','M'>::value:\n                    mTexture = esm.getHString();\n                    break;\n                case ESM::FourCC<'D','E','S','C'>::value:\n                    mDescription = esm.getHString();\n                    break;\n                case ESM::FourCC<'N','P','C','S'>::value:\n                    mPowers.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n    }\n\n    void BirthSign::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNOCString(\"TNAM\", mTexture);\n        esm.writeHNOCString(\"DESC\", mDescription);\n\n        mPowers.save(esm);\n    }\n\n    void BirthSign::blank()\n    {\n        mName.clear();\n        mDescription.clear();\n        mTexture.clear();\n        mPowers.mList.clear();\n    }\n\n}\n"
  },
  {
    "path": "components/esm/loadbsgn.hpp",
    "content": "#ifndef OPENMW_ESM_BSGN_H\n#define OPENMW_ESM_BSGN_H\n\n#include <string>\n\n#include \"spelllist.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct BirthSign\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"BirthSign\"; }\n\n    std::string mId, mName, mDescription, mTexture;\n\n    // List of powers and abilities that come with this birth sign.\n    SpellList mPowers;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID/index).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadcell.cpp",
    "content": "#include \"loadcell.hpp\"\n\n#include <string>\n#include <list>\n\n#include <boost/concept_check.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n#include \"cellid.hpp\"\n\nnamespace\n{\n    ///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum\n    void adjustRefNum (ESM::RefNum& refNum, ESM::ESMReader& reader)\n    {\n        unsigned int local = (refNum.mIndex & 0xff000000) >> 24;\n\n        // If we have an index value that does not make sense, assume that it was an addition\n        // by the present plugin (but a faulty one)\n        if (local && local <= reader.getParentFileIndices().size())\n        {\n            // If the most significant 8 bits are used, then this reference already exists.\n            // In this case, do not spawn a new reference, but overwrite the old one.\n            refNum.mIndex &= 0x00ffffff; // delete old plugin ID\n            refNum.mContentFile = reader.getParentFileIndices()[local-1];\n        }\n        else\n        {\n            // This is an addition by the present plugin. Set the corresponding plugin index.\n            refNum.mContentFile = reader.getIndex();\n        }\n    }\n}\n\nnamespace ESM\n{\n    unsigned int Cell::sRecordId = REC_CELL;\n\n    // Some overloaded compare operators.\n    bool operator== (const MovedCellRef& ref, const RefNum& refNum)\n    {\n        return ref.mRefNum == refNum;\n    }\n\n    bool operator== (const CellRef& ref, const RefNum& refNum)\n    {\n        return ref.mRefNum == refNum;\n    }\n\n    void Cell::load(ESMReader &esm, bool &isDeleted, bool saveContext)\n    {\n        loadNameAndData(esm, isDeleted);\n        loadCell(esm, saveContext);\n    }\n\n    void Cell::loadNameAndData(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        blank();\n\n        bool hasData = false;\n        bool isLoaded = false;\n        while (!isLoaded && esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    esm.getHT(mData, 12);\n                    hasData = true;\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.cacheSubName();\n                    isLoaded = true;\n                    break;\n            }\n        }\n\n        if (!hasData)\n            esm.fail(\"Missing DATA subrecord\");\n\n        mCellId.mPaged = !(mData.mFlags & Interior);\n\n        if (mCellId.mPaged)\n        {\n            mCellId.mWorldspace = ESM::CellId::sDefaultWorldspace;\n            mCellId.mIndex.mX = mData.mX;\n            mCellId.mIndex.mY = mData.mY;\n        }\n        else\n        {\n            mCellId.mWorldspace = Misc::StringUtils::lowerCase (mName);\n            mCellId.mIndex.mX = 0;\n            mCellId.mIndex.mY = 0;\n        }\n    }\n\n    void Cell::loadCell(ESMReader &esm, bool saveContext)\n    {\n        bool isLoaded = false;\n        mHasAmbi = false;\n        while (!isLoaded && esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::FourCC<'I','N','T','V'>::value:\n                    int waterl;\n                    esm.getHT(waterl);\n                    mWater = static_cast<float>(waterl);\n                    mWaterInt = true;\n                    break;\n                case ESM::FourCC<'W','H','G','T'>::value:\n                    esm.getHT(mWater);\n                    mWaterInt = false;\n                    break;\n                case ESM::FourCC<'A','M','B','I'>::value:\n                    esm.getHT(mAmbi);\n                    mHasAmbi = true;\n                    break;\n                case ESM::FourCC<'R','G','N','N'>::value:\n                    mRegion = esm.getHString();\n                    break;\n                case ESM::FourCC<'N','A','M','5'>::value:\n                    esm.getHT(mMapColor);\n                    break;\n                case ESM::FourCC<'N','A','M','0'>::value:\n                    esm.getHT(mRefNumCounter);\n                    break;\n                default:\n                    esm.cacheSubName();\n                    isLoaded = true;\n                    break;\n            }\n        }\n\n        if (saveContext) \n        {\n            mContextList.push_back(esm.getContext());\n            esm.skipRecord();\n        }\n    }\n\n    void Cell::postLoad(ESMReader &esm)\n    {\n        // Save position of the cell references and move on\n        mContextList.push_back(esm.getContext());\n        esm.skipRecord();\n    }\n\n    void Cell::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mName);\n        esm.writeHNT(\"DATA\", mData, 12);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        if (mData.mFlags & Interior)\n        {\n            if (mWaterInt) {\n                int water =\n                    (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5);\n                esm.writeHNT(\"INTV\", water);\n            } else {\n                esm.writeHNT(\"WHGT\", mWater);\n            }\n\n            if (mData.mFlags & QuasiEx)\n                esm.writeHNOCString(\"RGNN\", mRegion);\n            else\n            {\n                // Try to avoid saving ambient lighting information when it's unnecessary.\n                // This is to fix black lighting in resaved cell records that lack this information.\n                if (mHasAmbi)\n                    esm.writeHNT(\"AMBI\", mAmbi, 16);\n            }\n        }\n        else\n        {\n            esm.writeHNOCString(\"RGNN\", mRegion);\n            if (mMapColor != 0)\n                esm.writeHNT(\"NAM5\", mMapColor);\n        }\n\n        if (mRefNumCounter != 0)\n            esm.writeHNT(\"NAM0\", mRefNumCounter);\n    }\n\n    void Cell::restore(ESMReader &esm, int iCtx) const\n    {\n        esm.restoreContext(mContextList.at (iCtx));\n    }\n\n    std::string Cell::getDescription() const\n    {\n        if (mData.mFlags & Interior)\n            return mName;\n\n        std::string cellGrid = \"(\" + std::to_string(mData.mX) + \", \" + std::to_string(mData.mY) + \")\";\n        if (!mName.empty())\n            return mName + ' ' + cellGrid;\n        // FIXME: should use sDefaultCellname GMST instead, but it's not available in this scope\n        std::string region = !mRegion.empty() ? mRegion : \"Wilderness\";\n\n        return region + ' ' + cellGrid;\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Add a method that returns cell descriptions in OpenMW's previous way, because it was widely\n        used in TES3MP\n    */\n    std::string Cell::getShortDescription() const\n    {\n        if (mData.mFlags & Interior)\n        {\n            return mName;\n        }\n        else\n        {\n            return std::to_string(mData.mX) + \", \" + std::to_string(mData.mY);\n        }\n    }\n    /*\n        End of tes3mp addition\n    */\n\n    bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, bool ignoreMoves, MovedCellRef *mref)\n    {\n        isDeleted = false;\n\n        // TODO: Try and document reference numbering, I don't think this has been done anywhere else.\n        if (!esm.hasMoreSubs())\n            return false;\n\n        // NOTE: We should not need this check. It is a safety check until we have checked\n        // more plugins, and how they treat these moved references.\n        if (esm.isNextSub(\"MVRF\"))\n        {\n            if (ignoreMoves)\n            {\n                esm.getHT (mref->mRefNum.mIndex);\n                esm.getHNOT (mref->mTarget, \"CNDT\");\n                adjustRefNum (mref->mRefNum, esm);\n            }\n            else\n            {\n                // skip rest of cell record (moved references), they are handled elsewhere\n                esm.skipRecord(); // skip MVRF, CNDT\n                return false;\n            }\n        }\n\n        if (esm.peekNextSub(\"FRMR\"))\n        {\n            ref.load (esm, isDeleted);\n\n            // Identify references belonging to a parent file and adapt the ID accordingly.\n            adjustRefNum (ref.mRefNum, esm);\n            return true;\n        }\n        return false;\n    }\n\n    bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)\n    {\n        esm.getHT(mref.mRefNum.mIndex);\n        esm.getHNOT(mref.mTarget, \"CNDT\");\n\n        adjustRefNum (mref.mRefNum, esm);\n\n        return true;\n    }\n\n    void Cell::blank()\n    {\n        mName.clear();\n        mRegion.clear();\n        mWater = 0;\n        mWaterInt = false;\n        mMapColor = 0;\n        mRefNumCounter = 0;\n\n        mData.mFlags = 0;\n        mData.mX = 0;\n        mData.mY = 0;\n\n        mHasAmbi = true;\n        mAmbi.mAmbient = 0;\n        mAmbi.mSunlight = 0;\n        mAmbi.mFog = 0;\n        mAmbi.mFogDensity = 0;\n    }\n\n    const CellId& Cell::getCellId() const\n    {\n        return mCellId;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadcell.hpp",
    "content": "#ifndef OPENMW_ESM_CELL_H\n#define OPENMW_ESM_CELL_H\n\n#include <string>\n#include <vector>\n#include <list>\n\n#include \"esmcommon.hpp\"\n#include \"defs.hpp\"\n#include \"cellref.hpp\"\n#include \"cellid.hpp\"\n\nnamespace MWWorld\n{\n    class ESMStore;\n}\n\nnamespace ESM\n{\nclass ESMReader;\nclass ESMWriter;\n\n/* Moved cell reference tracking object. This mainly stores the target cell\n        of the reference, so we can easily know where it has been moved when another\n        plugin tries to move it independently.\n    Unfortunately, we need to implement this here.\n    */\nclass MovedCellRef\n{\npublic:\n    RefNum mRefNum;\n\n    // Coordinates of target exterior cell\n    int mTarget[2];\n\n    // The content file format does not support moving objects to an interior cell.\n    // The save game format does support moving to interior cells, but uses a different mechanism\n    // (see the MovedRefTracker implementation in MWWorld::CellStore for more details).\n};\n\n/// Overloaded compare operator used to search inside a list of cell refs.\nbool operator==(const MovedCellRef& ref, const RefNum& refNum);\nbool operator==(const CellRef& ref, const RefNum& refNum);\n\ntypedef std::list<MovedCellRef> MovedCellRefTracker;\ntypedef std::list<std::pair<CellRef, bool> > CellRefTracker;\n\nstruct CellRefTrackerPredicate\n{\n    RefNum mRefNum;\n\n    CellRefTrackerPredicate(const RefNum& refNum) : mRefNum(refNum) {}\n    bool operator() (const std::pair<CellRef, bool>& refdelPair) { return refdelPair.first == mRefNum; }\n};\n\n/* Cells hold data about objects, creatures, statics (rocks, walls,\n   buildings) and landscape (for exterior cells). Cells frequently\n   also has other associated LAND and PGRD records. Combined, all this\n   data can be huge, and we cannot load it all at startup. Instead,\n   the strategy we use is to remember the file position of each cell\n   (using ESMReader::getContext()) and jumping back into place\n   whenever we need to load a given cell.\n */\nstruct Cell\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Cell\"; }\n\n  enum Flags\n    {\n      Interior  = 0x01, // Interior cell\n      HasWater  = 0x02, // Does this cell have a water surface\n      NoSleep   = 0x04, // Is it allowed to sleep here (without a bed)\n      QuasiEx   = 0x80  // Behave like exterior (Tribunal+), with\n                        // skybox and weather\n    };\n\n  struct DATAstruct\n  {\n      int mFlags {0};\n      int mX {0}, mY {0};\n  };\n\n  struct AMBIstruct\n  {\n      Color mAmbient {0}, mSunlight {0}, mFog {0};\n      float mFogDensity {0.f};\n  };\n\n  Cell() : mName(\"\"),\n           mRegion(\"\"),\n           mHasAmbi(true),\n           mWater(0),\n           mWaterInt(false),\n           mMapColor(0),\n           mRefNumCounter(0)\n  {}\n\n  // Interior cells are indexed by this (it's the 'id'), for exterior\n  // cells it is optional.\n  std::string mName;\n\n  // Optional region name for exterior and quasi-exterior cells.\n  std::string mRegion;\n\n  std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support\n  DATAstruct mData;\n  CellId mCellId;\n\n  AMBIstruct mAmbi;\n  bool mHasAmbi;\n\n  float mWater; // Water level\n  bool mWaterInt;\n  int mMapColor;\n  // Counter for RefNums. This is only used during content file editing and has no impact on gameplay.\n  // It prevents overwriting previous refNums, even if they were deleted.\n  // as that would collide with refs when a content file is upgraded.\n  int mRefNumCounter;\n\n  // References \"leased\" from another cell (i.e. a different cell\n  //  introduced this ref, and it has been moved here by a plugin)\n  CellRefTracker mLeasedRefs;\n  MovedCellRefTracker mMovedRefs;\n\n  void postLoad(ESMReader &esm);\n\n  // This method is left in for compatibility with esmtool. Parsing moved references currently requires\n  //  passing ESMStore, bit it does not know about this parameter, so we do it this way.\n  void load(ESMReader &esm, bool &isDeleted, bool saveContext = true); // Load everything (except references)\n  void loadNameAndData(ESMReader &esm, bool &isDeleted); // Load NAME and DATAstruct\n  void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except NAME, DATAstruct and references\n\n  void save(ESMWriter &esm, bool isDeleted = false) const;\n\n  bool isExterior() const\n  {\n      return !(mData.mFlags & Interior);\n  }\n\n  int getGridX() const\n  {\n      return mData.mX;\n  }\n\n  int getGridY() const\n  {\n      return mData.mY;\n  }\n\n  bool hasWater() const\n  {\n      return ((mData.mFlags&HasWater) != 0) || isExterior();\n  }\n\n  bool hasAmbient() const\n  {\n      return mHasAmbi;\n  }\n\n  void setHasAmbient(bool hasAmbi)\n  {\n      mHasAmbi = hasAmbi;\n  }\n\n  // Restore the given reader to the stored position. Will try to open\n  // the file matching the stored file name. If you want to read from\n  // somewhere other than the file system, you need to pre-open the\n  // ESMReader, and the filename must match the stored filename\n  // exactly.\n  void restore(ESMReader &esm, int iCtx) const;\n\n  std::string getDescription() const;\n  ///< Return a short string describing the cell (mostly used for debugging/logging purpose)\n\n  /*\n      Start of tes3mp addition\n\n      Add a method that returns cell descriptions in OpenMW's previous way, because it was widely\n      used in TES3MP\n  */\n  std::string getShortDescription() const;\n  /*\n      End of tes3mp addition\n  */\n\n  /* Get the next reference in this cell, if any. Returns false when\n     there are no more references in the cell.\n\n     All fields of the CellRef struct are overwritten. You can safely\n     reuse one memory location without blanking it between calls.\n  */\n  /// \\param ignoreMoves ignore MVRF record and read reference like a regular CellRef.\n  static bool getNextRef(ESMReader &esm, \n                         CellRef &ref, \n                         bool &isDeleted, \n                         bool ignoreMoves = false, \n                         MovedCellRef *mref = nullptr);\n\n  /* This fetches an MVRF record, which is used to track moved references.\n   * Since they are comparably rare, we use a separate method for this.\n   */\n  static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref);\n\n    void blank();\n    ///< Set record to default state (does not touch the ID/index).\n\n    const CellId& getCellId() const;\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadclas.cpp",
    "content": "#include \"loadclas.hpp\"\n\n#include <stdexcept>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Class::sRecordId = REC_CLAS;\n\n    const Class::Specialization Class::sSpecializationIds[3] = {\n      Class::Combat,\n      Class::Magic,\n      Class::Stealth\n    };\n\n    const char *Class::sGmstSpecializationIds[3] = {\n      \"sSpecializationCombat\",\n      \"sSpecializationMagic\",\n      \"sSpecializationStealth\"\n    };\n\n    int& Class::CLDTstruct::getSkill (int index, bool major)\n    {\n        if (index<0 || index>=5)\n            throw std::logic_error (\"skill index out of range\");\n\n        return mSkills[index][major ? 1 : 0];\n    }\n\n    int Class::CLDTstruct::getSkill (int index, bool major) const\n    {\n        if (index<0 || index>=5)\n            throw std::logic_error (\"skill index out of range\");\n\n        return mSkills[index][major ? 1 : 0];\n    }\n\n    void Class::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'C','L','D','T'>::value:\n                    esm.getHT(mData, 60);\n                    if (mData.mIsPlayable > 1)\n                        esm.fail(\"Unknown bool value\");\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'D','E','S','C'>::value:\n                    mDescription = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing CLDT subrecord\");\n    }\n    void Class::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"CLDT\", mData, 60);\n        esm.writeHNOString(\"DESC\", mDescription);\n    }\n\n    void Class::blank()\n    {\n        mName.clear();\n        mDescription.clear();\n\n        mData.mAttribute[0] = mData.mAttribute[1] = 0;\n        mData.mSpecialization = 0;\n        mData.mIsPlayable = 0;\n        mData.mCalc = 0;\n\n        for (int i=0; i<5; ++i)\n            for (int i2=0; i2<2; ++i2)\n                mData.mSkills[i][i2] = 0;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadclas.hpp",
    "content": "#ifndef OPENMW_ESM_CLAS_H\n#define OPENMW_ESM_CLAS_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Character class definitions\n */\n\n// These flags tells us which items should be auto-calculated for this\n// class\nstruct Class\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Class\"; }\n\n    enum AutoCalc\n    {\n        Weapon = 0x00001,\n        Armor = 0x00002,\n        Clothing = 0x00004,\n        Books = 0x00008,\n        Ingredient = 0x00010,\n        Lockpick = 0x00020,\n        Probe = 0x00040,\n        Lights = 0x00080,\n        Apparatus = 0x00100,\n        Repair = 0x00200,\n        Misc = 0x00400,\n        Spells = 0x00800,\n        MagicItems = 0x01000,\n        Potions = 0x02000,\n        Training = 0x04000,\n        Spellmaking = 0x08000,\n        Enchanting = 0x10000,\n        RepairItem = 0x20000\n    };\n\n    enum Specialization\n    {\n        Combat = 0,\n        Magic = 1,\n        Stealth = 2\n    };\n\n    static const Specialization sSpecializationIds[3];\n    static const char *sGmstSpecializationIds[3];\n\n    struct CLDTstruct\n    {\n        int mAttribute[2]; // Attributes that get class bonus\n        int mSpecialization; // 0 = Combat, 1 = Magic, 2 = Stealth\n        int mSkills[5][2]; // Minor and major skills.\n        int mIsPlayable; // 0x0001 - Playable class\n\n        // I have no idea how to autocalculate these items...\n        int mCalc;\n\n        int& getSkill (int index, bool major);\n        ///< Throws an exception for invalid values of \\a index.\n\n        int getSkill (int index, bool major) const;\n        ///< Throws an exception for invalid values of \\a index.\n    }; // 60 bytes\n\n    std::string mId, mName, mDescription;\n    CLDTstruct mData;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n     ///< Set record to default state (does not touch the ID/index).\n\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadclot.cpp",
    "content": "#include \"loadclot.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Clothing::sRecordId = REC_CLOT;\n\n    void Clothing::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mParts.mParts.clear();\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'C','T','D','T'>::value:\n                    esm.getHT(mData, 12);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::FourCC<'E','N','A','M'>::value:\n                    mEnchant = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','N','D','X'>::value:\n                    mParts.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing CTDT subrecord\");\n    }\n\n    void Clothing::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"CTDT\", mData, 12);\n\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n\n        mParts.save(esm);\n\n        esm.writeHNOCString(\"ENAM\", mEnchant);\n    }\n\n    void Clothing::blank()\n    {\n        mData.mType = 0;\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mEnchant = 0;\n        mParts.mParts.clear();\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mEnchant.clear();\n        mScript.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadclot.hpp",
    "content": "#ifndef OPENMW_ESM_CLOT_H\n#define OPENMW_ESM_CLOT_H\n\n#include <string>\n\n#include \"loadarmo.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Clothing\n */\n\nstruct Clothing\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Clothing\"; }\n\n    enum Type\n    {\n        Pants = 0,\n        Shoes = 1,\n        Shirt = 2,\n        Belt = 3,\n        Robe = 4,\n        RGlove = 5,\n        LGlove = 6,\n        Skirt = 7,\n        Ring = 8,\n        Amulet = 9\n    };\n\n    struct CTDTstruct\n    {\n        int mType;\n        float mWeight;\n        unsigned short mValue;\n        unsigned short mEnchant;\n    };\n    CTDTstruct mData;\n\n    PartReferenceList mParts;\n\n    std::string mId, mName, mModel, mIcon, mEnchant, mScript;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadcont.cpp",
    "content": "#include \"loadcont.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n\n    void InventoryList::add(ESMReader &esm)\n    {\n        esm.getSubHeader();\n        ContItem ci;\n        esm.getT(ci.mCount);\n        ci.mItem.assign(esm.getString(32));\n        mList.push_back(ci);\n    }\n\n    void InventoryList::save(ESMWriter &esm) const\n    {\n        for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)\n        {\n            esm.startSubRecord(\"NPCO\");\n            esm.writeT(it->mCount);\n            esm.writeFixedSizeString(it->mItem, 32);\n            esm.endRecord(\"NPCO\");\n        }\n    }\n\n    unsigned int Container::sRecordId = REC_CONT;\n\n    void Container::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mInventory.mList.clear();\n\n        bool hasName = false;\n        bool hasWeight = false;\n        bool hasFlags = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'C','N','D','T'>::value:\n                    esm.getHT(mWeight, 4);\n                    hasWeight = true;\n                    break;\n                case ESM::FourCC<'F','L','A','G'>::value:\n                    esm.getHT(mFlags, 4);\n                    if (mFlags & 0xf4)\n                        esm.fail(\"Unknown flags\");\n                    if (!(mFlags & 0x8))\n                        esm.fail(\"Flag 8 not set\");\n                    hasFlags = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'N','P','C','O'>::value:\n                    mInventory.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasWeight && !isDeleted)\n            esm.fail(\"Missing CNDT subrecord\");\n        if (!hasFlags && !isDeleted)\n            esm.fail(\"Missing FLAG subrecord\");\n    }\n\n    void Container::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"CNDT\", mWeight, 4);\n        esm.writeHNT(\"FLAG\", mFlags, 4);\n\n        esm.writeHNOCString(\"SCRI\", mScript);\n\n        mInventory.save(esm);\n    }\n\n    void Container::blank()\n    {\n        mName.clear();\n        mModel.clear();\n        mScript.clear();\n        mWeight = 0;\n        mFlags = 0x8; // set default flag value\n        mInventory.mList.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadcont.hpp",
    "content": "#ifndef OPENMW_ESM_CONT_H\n#define OPENMW_ESM_CONT_H\n\n#include <string>\n#include <vector>\n\n#include \"esmcommon.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Container definition\n */\n\nstruct ContItem\n{\n    int mCount;\n    std::string mItem;\n};\n\n/// InventoryList, NPCO subrecord\nstruct InventoryList\n{\n    std::vector<ContItem> mList;\n\n    /// Load one item, assumes subrecord name is already read\n    void add(ESMReader &esm);\n\n    void save(ESMWriter &esm) const;\n};\n\nstruct Container\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Container\"; }\n\n    enum Flags\n    {\n        Organic = 1, // Objects cannot be placed in this container\n        Respawn = 2, // Respawns after 4 months\n        Unknown = 8\n    };\n\n    std::string mId, mName, mModel, mScript;\n\n    float mWeight; // Not sure, might be max total weight allowed?\n    int mFlags;\n    InventoryList mInventory;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadcrea.cpp",
    "content": "#include \"loadcrea.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM {\n\n    unsigned int Creature::sRecordId = REC_CREA;\n\n    void Creature::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mPersistent = (esm.getRecordFlags() & 0x0400) != 0;\n\n        mAiPackage.mList.clear();\n        mInventory.mList.clear();\n        mSpells.mList.clear();\n        mTransport.mList.clear();\n\n        mScale = 1.f;\n        mAiData.blank();\n        mAiData.mFight = 90;\n        mAiData.mFlee = 20;\n\n        bool hasName = false;\n        bool hasNpdt = false;\n        bool hasFlags = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'C','N','A','M'>::value:\n                    mOriginal = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'N','P','D','T'>::value:\n                    esm.getHT(mData, 96);\n                    hasNpdt = true;\n                    break;\n                case ESM::FourCC<'F','L','A','G'>::value:\n                    int flags;\n                    esm.getHT(flags);\n                    mFlags = flags & 0xFF;\n                    mBloodType = ((flags >> 8) & 0xFF) >> 2;\n                    hasFlags = true;\n                    break;\n                case ESM::FourCC<'X','S','C','L'>::value:\n                    esm.getHT(mScale);\n                    break;\n                case ESM::FourCC<'N','P','C','O'>::value:\n                    mInventory.add(esm);\n                    break;\n                case ESM::FourCC<'N','P','C','S'>::value:\n                    mSpells.add(esm);\n                    break;\n                case ESM::FourCC<'A','I','D','T'>::value:\n                    esm.getHExact(&mAiData, sizeof(mAiData));\n                    break;\n                case ESM::FourCC<'D','O','D','T'>::value:\n                case ESM::FourCC<'D','N','A','M'>::value:\n                    mTransport.add(esm);\n                    break;\n                case AI_Wander:\n                case AI_Activate:\n                case AI_Escort:\n                case AI_Follow:\n                case AI_Travel:\n                case AI_CNDT:\n                    mAiPackage.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                case ESM::FourCC<'I','N','D','X'>::value:\n                    // seems to occur only in .ESS files, unsure of purpose\n                    int index;\n                    esm.getHT(index);\n                    Log(Debug::Warning) << \"Creature::load: Unhandled INDX \" << index;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasNpdt && !isDeleted)\n            esm.fail(\"Missing NPDT subrecord\");\n        if (!hasFlags && !isDeleted)\n            esm.fail(\"Missing FLAG subrecord\");\n    }\n\n    void Creature::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"CNAM\", mOriginal);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNT(\"NPDT\", mData, 96);\n        esm.writeHNT(\"FLAG\", ((mBloodType << 10) + mFlags));\n        if (mScale != 1.0) {\n            esm.writeHNT(\"XSCL\", mScale);\n        }\n\n        mInventory.save(esm);\n        mSpells.save(esm);\n        esm.writeHNT(\"AIDT\", mAiData, sizeof(mAiData));\n        mTransport.save(esm);\n        mAiPackage.save(esm);\n    }\n\n    void Creature::blank()\n    {\n        mData.mType = 0;\n        mData.mLevel = 0;\n        mData.mStrength = mData.mIntelligence = mData.mWillpower = mData.mAgility =\n            mData.mSpeed = mData.mEndurance = mData.mPersonality = mData.mLuck = 0;\n        mData.mHealth = mData.mMana = mData.mFatigue = 0;\n        mData.mSoul = 0;\n        mData.mCombat = mData.mMagic = mData.mStealth = 0;\n        for (int i=0; i<6; ++i) mData.mAttack[i] = 0;\n        mData.mGold = 0;\n        mBloodType = 0;\n        mFlags = 0;\n        mScale = 1.f;\n        mModel.clear();\n        mName.clear();\n        mScript.clear();\n        mOriginal.clear();\n        mInventory.mList.clear();\n        mSpells.mList.clear();\n        mAiData.blank();\n        mAiData.mFight = 90;\n        mAiData.mFlee = 20;\n        mAiPackage.mList.clear();\n        mTransport.mList.clear();\n    }\n\n    const std::vector<Transport::Dest>& Creature::getTransport() const\n    {\n        return mTransport.mList;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadcrea.hpp",
    "content": "#ifndef OPENMW_ESM_CREA_H\n#define OPENMW_ESM_CREA_H\n\n#include <string>\n\n#include \"loadcont.hpp\"\n#include \"spelllist.hpp\"\n#include \"aipackage.hpp\"\n#include \"transport.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Creature definition\n *\n */\n\nstruct Creature\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Creature\"; }\n\n    // Default is 0x48?\n    enum Flags\n    {\n        Bipedal       = 0x01,\n        Respawn       = 0x02,\n        Weapon        = 0x04, // Has weapon and shield\n        Base          = 0x08, // This flag is set for every actor in Bethesda ESMs\n        Swims         = 0x10,\n        Flies         = 0x20, // Don't know what happens if several\n        Walks         = 0x40, // of these are set\n        Essential     = 0x80\n    };\n\n    enum Type\n    {\n        Creatures = 0,\n        Daedra  = 1,\n        Undead = 2,\n        Humanoid = 3\n    };\n\n    struct NPDTstruct\n    {\n        int mType;\n        // For creatures we obviously have to use ints, not shorts and\n        // bytes like we use for NPCs.... this file format just makes so\n        // much sense! (Still, _much_ easier to decode than the NIFs.)\n        int mLevel;\n        int mStrength,\n            mIntelligence,\n            mWillpower,\n            mAgility,\n            mSpeed,\n            mEndurance,\n            mPersonality,\n            mLuck;\n\n        int mHealth, mMana, mFatigue; // Stats\n        int mSoul; // The creatures soul value (used with soul gems.)\n        // Creatures have generalized combat, magic and stealth stats which substitute for\n        // the specific skills (in the same way as specializations).\n        int mCombat, mMagic, mStealth;\n        int mAttack[6]; // AttackMin1, AttackMax1, ditto2, ditto3\n        int mGold;\n    }; // 96 byte\n\n    NPDTstruct mData;\n\n    int mBloodType;\n    unsigned char mFlags;\n\n    bool mPersistent;\n\n    float mScale;\n\n    std::string mId, mModel, mName, mScript;\n    std::string mOriginal; // Base creature that this is a modification of\n\n    InventoryList mInventory;\n    SpellList mSpells;\n\n    AIData mAiData;\n    AIPackageList mAiPackage;\n    Transport mTransport;\n\n    const std::vector<Transport::Dest>& getTransport() const;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loaddial.cpp",
    "content": "#include \"loaddial.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Dialogue::sRecordId = REC_DIAL;\n\n    void Dialogue::load(ESMReader &esm, bool &isDeleted)\n    {\n        loadId(esm);\n        loadData(esm, isDeleted);\n    }\n\n    void Dialogue::loadId(ESMReader &esm)\n    {\n        mId = esm.getHNString(\"NAME\");\n    }\n\n    void Dialogue::loadData(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::FourCC<'D','A','T','A'>::value:\n                {\n                    esm.getSubHeader();\n                    int size = esm.getSubSize();\n                    if (size == 1)\n                    {\n                        esm.getT(mType);\n                    }\n                    else\n                    {\n                        esm.skip(size);\n                    }\n                    break;\n                }\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    mType = Unknown;\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n    }\n\n    void Dialogue::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n        }\n        else\n        {\n            esm.writeHNT(\"DATA\", mType);\n        }\n    }\n\n    void Dialogue::blank()\n    {\n        mInfo.clear();\n    }\n\n    void Dialogue::readInfo(ESMReader &esm, bool merge)\n    {\n        ESM::DialInfo info;\n        bool isDeleted = false;\n        info.load(esm, isDeleted);\n\n        if (!merge || mInfo.empty())\n        {\n            mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.end(), info), isDeleted);\n            return;\n        }\n\n        InfoContainer::iterator it = mInfo.end();\n\n        LookupMap::iterator lookup;\n        lookup = mLookup.find(info.mId);\n\n        if (lookup != mLookup.end())\n        {\n            it = lookup->second.first;\n            // Since the new version of this record may have changed the next/prev linked list connection, we need to re-insert the record\n            mInfo.erase(it);\n            mLookup.erase(lookup);\n        }\n\n        if (info.mNext.empty())\n        {\n            mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.end(), info), isDeleted);\n            return;\n        }\n        if (info.mPrev.empty())\n        {\n            mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.begin(), info), isDeleted);\n            return;\n        }\n\n        lookup = mLookup.find(info.mPrev);\n        if (lookup != mLookup.end())\n        {\n            it = lookup->second.first;\n\n            mLookup[info.mId] = std::make_pair(mInfo.insert(++it, info), isDeleted);\n            return;\n        }\n\n        lookup = mLookup.find(info.mNext);\n        if (lookup != mLookup.end())\n        {\n            it = lookup->second.first;\n\n            mLookup[info.mId] = std::make_pair(mInfo.insert(it, info), isDeleted);\n            return;\n        }\n\n        Log(Debug::Warning) << \"Warning: Failed to insert info \" << info.mId;\n    }\n\n    void Dialogue::clearDeletedInfos()\n    {\n        LookupMap::const_iterator current = mLookup.begin();\n        LookupMap::const_iterator end = mLookup.end();\n        for (; current != end; ++current)\n        {\n            if (current->second.second)\n            {\n                mInfo.erase(current->second.first);\n            }\n        }\n        mLookup.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loaddial.hpp",
    "content": "#ifndef OPENMW_ESM_DIAL_H\n#define OPENMW_ESM_DIAL_H\n\n#include <string>\n#include <list>\n#include <map>\n\n#include \"loadinfo.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Dialogue topic and journal entries. The actual data is contained in\n * the INFO records following the DIAL.\n */\n\nstruct Dialogue\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Dialogue\"; }\n\n    enum Type\n    {\n        Topic = 0,\n        Voice = 1,\n        Greeting = 2,\n        Persuasion = 3,\n        Journal = 4,\n        Unknown = -1 // Used for deleted dialogues\n    };\n\n    std::string mId;\n    signed char mType;\n\n    typedef std::list<DialInfo> InfoContainer;\n\n    // Parameters: Info ID, (Info iterator, Deleted flag)\n    typedef std::map<std::string, std::pair<InfoContainer::iterator, bool> > LookupMap;\n\n    InfoContainer mInfo;\n\n    // This is only used during the loading phase to speed up DialInfo merging.\n    LookupMap mLookup;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    ///< Loads all sub-records of Dialogue record\n    void loadId(ESMReader &esm);\n    ///< Loads NAME sub-record of Dialogue record\n    void loadData(ESMReader &esm, bool &isDeleted);\n    ///< Loads all sub-records of Dialogue record, except NAME sub-record\n\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    /// Remove all INFOs that are deleted\n    void clearDeletedInfos();\n\n    /// Read the next info record\n    /// @param merge Merge with existing list, or just push each record to the end of the list?\n    void readInfo (ESM::ESMReader& esm, bool merge);\n\n    void blank();\n    ///< Set record to default state (does not touch the ID and does not change the type).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loaddoor.cpp",
    "content": "#include \"loaddoor.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Door::sRecordId = REC_DOOR;\n\n    void Door::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','N','A','M'>::value:\n                    mOpenSound = esm.getHString();\n                    break;\n                case ESM::FourCC<'A','N','A','M'>::value:\n                    mCloseSound = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n    }\n\n    void Door::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"SNAM\", mOpenSound);\n        esm.writeHNOCString(\"ANAM\", mCloseSound);\n    }\n\n    void Door::blank()\n    {\n        mName.clear();\n        mModel.clear();\n        mScript.clear();\n        mOpenSound.clear();\n        mCloseSound.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loaddoor.hpp",
    "content": "#ifndef OPENMW_ESM_DOOR_H\n#define OPENMW_ESM_DOOR_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct Door\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Door\"; }\n\n    std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadench.cpp",
    "content": "#include \"loadench.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Enchantment::sRecordId = REC_ENCH;\n\n    void Enchantment::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n        mEffects.mList.clear();\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'E','N','D','T'>::value:\n                    esm.getHT(mData, 16);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'E','N','A','M'>::value:\n                    mEffects.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing ENDT subrecord\");\n    }\n\n    void Enchantment::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNT(\"ENDT\", mData, 16);\n        mEffects.save(esm);\n    }\n\n    void Enchantment::blank()\n    {\n        mData.mType = 0;\n        mData.mCost = 0;\n        mData.mCharge = 0;\n        mData.mFlags = 0;\n\n        mEffects.mList.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadench.hpp",
    "content": "#ifndef OPENMW_ESM_ENCH_H\n#define OPENMW_ESM_ENCH_H\n\n#include <string>\n\n#include \"effectlist.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Enchantments\n */\n\nstruct Enchantment\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Enchantment\"; }\n\n    enum Type\n    {\n        CastOnce = 0,\n        WhenStrikes = 1,\n        WhenUsed = 2,\n        ConstantEffect = 3\n    };\n\n    enum Flags\n    {\n        Autocalc = 0x01\n    };\n\n    struct ENDTstruct\n    {\n        int mType;\n        int mCost;\n        int mCharge;\n        int mFlags;\n    };\n\n    std::string mId;\n    ENDTstruct mData;\n    EffectList mEffects;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadfact.cpp",
    "content": "#include \"loadfact.hpp\"\n\n#include <stdexcept>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Faction::sRecordId = REC_FACT;\n\n    int& Faction::FADTstruct::getSkill (int index, bool ignored)\n    {\n        if (index<0 || index>=7)\n            throw std::logic_error (\"skill index out of range\");\n\n        return mSkills[index];\n    }\n\n    int Faction::FADTstruct::getSkill (int index, bool ignored) const\n    {\n        if (index<0 || index>=7)\n            throw std::logic_error (\"skill index out of range\");\n\n        return mSkills[index];\n    }\n\n    void Faction::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mReactions.clear();\n        for (int i=0;i<10;++i)\n            mRanks[i].clear();\n\n        int rankCounter = 0;\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'R','N','A','M'>::value:\n                    if (rankCounter >= 10)\n                        esm.fail(\"Rank out of range\");\n                    mRanks[rankCounter++] = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','A','D','T'>::value:\n                    esm.getHT(mData, 240);\n                    if (mData.mIsHidden > 1)\n                        esm.fail(\"Unknown flag!\");\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'A','N','A','M'>::value:\n                {\n                    std::string faction = esm.getHString();\n                    int reaction;\n                    esm.getHNT(reaction, \"INTV\");\n                    mReactions[faction] = reaction;\n                    break;\n                }\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing FADT subrecord\");\n    }\n\n    void Faction::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNOCString(\"FNAM\", mName);\n\n        for (int i = 0; i < 10; i++)\n        {\n            if (mRanks[i].empty())\n                break;\n\n            esm.writeHNString(\"RNAM\", mRanks[i], 32);\n        }\n\n        esm.writeHNT(\"FADT\", mData, 240);\n\n        for (std::map<std::string, int>::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it)\n        {\n            esm.writeHNString(\"ANAM\", it->first);\n            esm.writeHNT(\"INTV\", it->second);\n        }\n    }\n\n    void Faction::blank()\n    {\n        mName.clear();\n        mData.mAttribute[0] = mData.mAttribute[1] = 0;\n        mData.mIsHidden = 0;\n\n        for (int i=0; i<10; ++i)\n        {\n            mData.mRankData[i].mAttribute1 = mData.mRankData[i].mAttribute2 = 0;\n            mData.mRankData[i].mPrimarySkill = mData.mRankData[i].mFavouredSkill = 0;\n            mData.mRankData[i].mFactReaction = 0;\n\n            mRanks[i].clear();\n        }\n\n        for (int i=0; i<7; ++i)\n            mData.mSkills[i] = 0;\n\n        mReactions.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadfact.hpp",
    "content": "#ifndef OPENMW_ESM_FACT_H\n#define OPENMW_ESM_FACT_H\n\n#include <string>\n#include <map>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Faction definitions\n */\n\n// Requirements for each rank\nstruct RankData\n{\n    int mAttribute1, mAttribute2; // Attribute level\n\n    // Skill level (faction skills given in\n    // skillID below.) You need one skill at\n    // level 'mPrimarySkill' and two skills at level\n    // 'mFavouredSkill' to advance to this rank.\n    int mPrimarySkill, mFavouredSkill;\n\n    int mFactReaction; // Reaction from faction members\n};\n\nstruct Faction\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Faction\"; }\n\n    std::string mId, mName;\n\n    struct FADTstruct\n    {\n        // Which attributes we like\n        int mAttribute[2];\n\n        RankData mRankData[10];\n\n        int mSkills[7]; // IDs of skills this faction require\n                        // Each element will either contain an ESM::Skill index, or -1.\n\n        int mIsHidden; // 1 - hidden from player\n\n        int& getSkill (int index, bool ignored = false);\n        ///< Throws an exception for invalid values of \\a index.\n\n        int getSkill (int index, bool ignored = false) const;\n        ///< Throws an exception for invalid values of \\a index.\n    }; // 240 bytes\n\n    FADTstruct mData;\n\n    // <Faction ID, Reaction>\n    std::map<std::string, int> mReactions;\n\n    // Name of faction ranks (may be empty for NPC factions)\n    std::string mRanks[10];\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n     ///< Set record to default state (does not touch the ID/index).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadglob.cpp",
    "content": "#include \"loadglob.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Global::sRecordId = REC_GLOB;\n\n    void Global::load (ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mId = esm.getHNString (\"NAME\");\n\n        if (esm.isNextSub (\"DELE\"))\n        {\n            esm.skipHSub();\n            isDeleted = true;\n        }\n        else\n        {\n            mValue.read (esm, ESM::Variant::Format_Global);\n        }\n    }\n\n    void Global::save (ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString (\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString (\"DELE\", \"\");\n        }\n        else\n        {\n            mValue.write (esm, ESM::Variant::Format_Global);\n        }\n    }\n\n    void Global::blank()\n    {\n        mValue.setType (ESM::VT_None);\n    }\n\n    bool operator== (const Global& left, const Global& right)\n    {\n        return left.mId==right.mId && left.mValue==right.mValue;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadglob.hpp",
    "content": "#ifndef OPENMW_ESM_GLOB_H\n#define OPENMW_ESM_GLOB_H\n\n#include <string>\n\n#include \"variant.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Global script variables\n */\n\nstruct Global\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Global\"; }\n\n    std::string mId;\n    Variant mValue;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\nbool operator== (const Global& left, const Global& right);\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadgmst.cpp",
    "content": "#include \"loadgmst.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int GameSetting::sRecordId = REC_GMST;\n\n    void GameSetting::load (ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false; // GameSetting record can't be deleted now (may be changed in the future)\n\n        mId = esm.getHNString(\"NAME\");\n        mValue.read (esm, ESM::Variant::Format_Gmst);\n    }\n\n    void GameSetting::save (ESMWriter &esm, bool /*isDeleted*/) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n        mValue.write (esm, ESM::Variant::Format_Gmst);\n    }\n\n    void GameSetting::blank()\n    {\n        mValue.setType (ESM::VT_None);\n    }\n\n    bool operator== (const GameSetting& left, const GameSetting& right)\n    {\n        return left.mValue==right.mValue;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadgmst.hpp",
    "content": "#ifndef OPENMW_ESM_GMST_H\n#define OPENMW_ESM_GMST_H\n\n#include <string>\n\n#include \"variant.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n *  Game setting\n *\n */\n\nstruct GameSetting\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"GameSetting\"; }\n\n    std::string mId;\n\n    Variant mValue;\n\n    void load(ESMReader &esm, bool &isDeleted);\n\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\n    bool operator== (const GameSetting& left, const GameSetting& right);\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadinfo.cpp",
    "content": "#include \"loadinfo.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int DialInfo::sRecordId = REC_INFO;\n\n    void DialInfo::load(ESMReader &esm, bool &isDeleted)\n    {\n        mId = esm.getHNString(\"INAM\");\n\n        isDeleted = false;\n\n        mQuestStatus = QS_None;\n        mFactionLess = false;\n\n        mPrev = esm.getHNString(\"PNAM\");\n        mNext = esm.getHNString(\"NNAM\");\n\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    esm.getHT(mData, 12);\n                    break;\n                case ESM::FourCC<'O','N','A','M'>::value:\n                    mActor = esm.getHString();\n                    break;\n                case ESM::FourCC<'R','N','A','M'>::value:\n                    mRace = esm.getHString();\n                    break;\n                case ESM::FourCC<'C','N','A','M'>::value:\n                    mClass = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                {\n                    mFaction = esm.getHString();\n                    if (mFaction == \"FFFF\")\n                    {\n                        mFactionLess = true;\n                    }\n                    break;\n                }\n                case ESM::FourCC<'A','N','A','M'>::value:\n                    mCell = esm.getHString();\n                    break;\n                case ESM::FourCC<'D','N','A','M'>::value:\n                    mPcFaction = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','N','A','M'>::value:\n                    mSound = esm.getHString();\n                    break;\n                case ESM::SREC_NAME:\n                    mResponse = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','C','V','R'>::value:\n                {\n                    SelectStruct ss;\n                    ss.mSelectRule = esm.getHString();\n                    ss.mValue.read(esm, Variant::Format_Info);\n                    mSelects.push_back(ss);\n                    break;\n                }\n                case ESM::FourCC<'B','N','A','M'>::value:\n                    mResultScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'Q','S','T','N'>::value:\n                    mQuestStatus = QS_Name;\n                    esm.skipRecord();\n                    break;\n                case ESM::FourCC<'Q','S','T','F'>::value:\n                    mQuestStatus = QS_Finished;\n                    esm.skipRecord();\n                    break;\n                case ESM::FourCC<'Q','S','T','R'>::value:\n                    mQuestStatus = QS_Restart;\n                    esm.skipRecord();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n    }\n\n    void DialInfo::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"INAM\", mId);\n        esm.writeHNCString(\"PNAM\", mPrev);\n        esm.writeHNCString(\"NNAM\", mNext);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNT(\"DATA\", mData, 12);\n        esm.writeHNOCString(\"ONAM\", mActor);\n        esm.writeHNOCString(\"RNAM\", mRace);\n        esm.writeHNOCString(\"CNAM\", mClass);\n        esm.writeHNOCString(\"FNAM\", mFaction);\n        esm.writeHNOCString(\"ANAM\", mCell);\n        esm.writeHNOCString(\"DNAM\", mPcFaction);\n        esm.writeHNOCString(\"SNAM\", mSound);\n        esm.writeHNOString(\"NAME\", mResponse);\n\n        for (std::vector<SelectStruct>::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it)\n        {\n            esm.writeHNString(\"SCVR\", it->mSelectRule);\n            it->mValue.write (esm, Variant::Format_Info);\n        }\n\n        esm.writeHNOString(\"BNAM\", mResultScript);\n\n        switch(mQuestStatus)\n        {\n        case QS_Name: esm.writeHNT(\"QSTN\",'\\1'); break;\n        case QS_Finished: esm.writeHNT(\"QSTF\", '\\1'); break;\n        case QS_Restart: esm.writeHNT(\"QSTR\", '\\1'); break;\n        default: break;\n        }\n    }\n\n    void DialInfo::blank()\n    {\n        mData.mUnknown1 = 0;\n        mData.mDisposition = 0;\n        mData.mRank = 0;\n        mData.mGender = 0;\n        mData.mPCrank = 0;\n        mData.mUnknown2 = 0;\n\n        mSelects.clear();\n        mPrev.clear();\n        mNext.clear();\n        mActor.clear();\n        mRace.clear();\n        mClass.clear();\n        mFaction.clear();\n        mPcFaction.clear();\n        mCell.clear();\n        mSound.clear();\n        mResponse.clear();\n        mResultScript.clear();\n        mFactionLess = false;\n        mQuestStatus = QS_None;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadinfo.hpp",
    "content": "#ifndef OPENMW_ESM_INFO_H\n#define OPENMW_ESM_INFO_H\n\n#include <string>\n#include <vector>\n\n#include \"defs.hpp\"\n#include \"variant.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Dialogue information. A series of these follow after DIAL records,\n * and form a linked list of dialogue items.\n */\n\nstruct DialInfo\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"DialInfo\"; }\n\n    enum Gender\n    {\n        Male = 0,\n        Female = 1,\n        NA = -1\n    };\n\n    struct DATAstruct\n    {\n        int mUnknown1;\n        union\n        {\n            int mDisposition; // Used for dialogue responses\n            int mJournalIndex;  // Used for journal entries\n        };\n        signed char mRank; // Rank of NPC\n        signed char mGender; // See Gender enum\n        signed char mPCrank; // Player rank\n        signed char mUnknown2;\n    }; // 12 bytes\n    DATAstruct mData;\n\n    // The rules for whether or not we will select this dialog item.\n    struct SelectStruct\n    {\n        std::string mSelectRule; // This has a complicated format\n        Variant mValue;\n    };\n\n    // Journal quest indices (introduced with the quest system in Tribunal)\n    enum QuestStatus\n    {\n        QS_None = 0,\n        QS_Name = 1,\n        QS_Finished = 2,\n        QS_Restart = 3\n    };\n\n    // Rules for when to include this item in the final list of options\n    // visible to the player.\n    std::vector<SelectStruct> mSelects;\n\n    // Id of this, previous and next INFO items\n    std::string mId, mPrev, mNext;\n\n    // Various references used in determining when to select this item.\n    std::string mActor, mRace, mClass, mFaction, mPcFaction, mCell;\n\n    // Sound and text associated with this item\n    std::string mSound, mResponse;\n\n    // Result script (uncompiled) to run whenever this dialog item is\n    // selected\n    std::string mResultScript;\n\n    // ONLY include this item the NPC is not part of any faction.\n    bool mFactionLess;\n\n    // Status of this quest item\n    QuestStatus mQuestStatus;\n\n    // Hexadecimal versions of the various subrecord names.\n    enum SubNames\n    {\n        REC_ONAM = 0x4d414e4f,\n        REC_RNAM = 0x4d414e52,\n        REC_CNAM = 0x4d414e43,\n        REC_FNAM = 0x4d414e46,\n        REC_ANAM = 0x4d414e41,\n        REC_DNAM = 0x4d414e44,\n        REC_SNAM = 0x4d414e53,\n        REC_NAME = 0x454d414e,\n        REC_SCVR = 0x52564353,\n\n        REC_BNAM = 0x4d414e42,\n        REC_QSTN = 0x4e545351,\n        REC_QSTF = 0x46545351,\n        REC_QSTR = 0x52545351,\n        REC_DELE = 0x454c4544\n    };\n\n    void load(ESMReader &esm, bool &isDeleted);\n    ///< Loads Info record\n\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadingr.cpp",
    "content": "#include \"loadingr.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Ingredient::sRecordId = REC_INGR;\n\n    void Ingredient::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','R','D','T'>::value:\n                    esm.getHT(mData, 56);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing IRDT subrecord\");\n\n        // horrible hack to fix broken data in records\n        for (int i=0; i<4; ++i)\n        {\n            if (mData.mEffectID[i] != 85 &&\n                mData.mEffectID[i] != 22 &&\n                mData.mEffectID[i] != 17 &&\n                mData.mEffectID[i] != 79 &&\n                mData.mEffectID[i] != 74)\n            {\n                mData.mAttributes[i] = -1;\n            }\n\n            // is this relevant in cycle from 0 to 4?\n            if (mData.mEffectID[i] != 89 &&\n                mData.mEffectID[i] != 26 &&\n                mData.mEffectID[i] != 21 &&\n                mData.mEffectID[i] != 83 &&\n                mData.mEffectID[i] != 78)\n            {\n                mData.mSkills[i] = -1;\n            }\n        }\n    }\n\n    void Ingredient::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"IRDT\", mData, 56);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n    }\n\n    void Ingredient::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        for (int i=0; i<4; ++i)\n        {\n            mData.mEffectID[i] = 0;\n            mData.mSkills[i] = 0;\n            mData.mAttributes[i] = 0;\n        }\n\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadingr.hpp",
    "content": "#ifndef OPENMW_ESM_INGR_H\n#define OPENMW_ESM_INGR_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Alchemy ingredient\n */\n\nstruct Ingredient\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Ingredient\"; }\n\n    struct IRDTstruct\n    {\n        float mWeight;\n        int mValue;\n        int mEffectID[4]; // Effect, 0 or -1 means none\n        int mSkills[4]; // SkillEnum related to effect\n        int mAttributes[4]; // Attribute related to effect\n    };\n\n    IRDTstruct mData;\n    std::string mId, mName, mModel, mIcon, mScript;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadland.cpp",
    "content": "#include \"loadland.hpp\"\n\n#include <limits>\n#include <utility>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Land::sRecordId = REC_LAND;\n\n    Land::Land()\n        : mFlags(0)\n        , mX(0)\n        , mY(0)\n        , mPlugin(0)\n        , mDataTypes(0)\n        , mLandData(nullptr)\n    {\n    }\n\n    void transposeTextureData(const uint16_t *in, uint16_t *out)\n    {\n        int readPos = 0; //bit ugly, but it works\n        for ( int y1 = 0; y1 < 4; y1++ )\n            for ( int x1 = 0; x1 < 4; x1++ )\n                for ( int y2 = 0; y2 < 4; y2++)\n                    for ( int x2 = 0; x2 < 4; x2++ )\n                        out[(y1*4+y2)*16+(x1*4+x2)] = in[readPos++];\n    }\n\n    Land::~Land()\n    {\n        delete mLandData;\n    }\n\n    void Land::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mPlugin = esm.getIndex();\n\n        bool hasLocation = false;\n        bool isLoaded = false;\n        while (!isLoaded && esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::FourCC<'I','N','T','V'>::value:\n                    esm.getSubHeaderIs(8);\n                    esm.getT<int>(mX);\n                    esm.getT<int>(mY);\n                    hasLocation = true;\n                    break;\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    esm.getHT(mFlags);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.cacheSubName();\n                    isLoaded = true;\n                    break;\n            }\n        }\n\n        if (!hasLocation)\n            esm.fail(\"Missing INTV subrecord\");\n\n        mContext = esm.getContext();\n\n        mLandData = nullptr;\n        std::fill(std::begin(mWnam), std::end(mWnam), 0);\n\n        // Skip the land data here. Load it when the cell is loaded.\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::FourCC<'V','N','M','L'>::value:\n                    esm.skipHSub();\n                    mDataTypes |= DATA_VNML;\n                    break;\n                case ESM::FourCC<'V','H','G','T'>::value:\n                    esm.skipHSub();\n                    mDataTypes |= DATA_VHGT;\n                    break;\n                case ESM::FourCC<'W','N','A','M'>::value:\n                    esm.getHExact(mWnam, sizeof(mWnam));\n                    mDataTypes |= DATA_WNAM;\n                    break;\n                case ESM::FourCC<'V','C','L','R'>::value:\n                    esm.skipHSub();\n                    mDataTypes |= DATA_VCLR;\n                    break;\n                case ESM::FourCC<'V','T','E','X'>::value:\n                    esm.skipHSub();\n                    mDataTypes |= DATA_VTEX;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n    }\n\n    void Land::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.startSubRecord(\"INTV\");\n        esm.writeT(mX);\n        esm.writeT(mY);\n        esm.endRecord(\"INTV\");\n\n        esm.writeHNT(\"DATA\", mFlags);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        if (mLandData)\n        {\n            if (mDataTypes & Land::DATA_VNML) {\n                esm.writeHNT(\"VNML\", mLandData->mNormals);\n            }\n            if (mDataTypes & Land::DATA_VHGT) {\n                VHGT offsets;\n                offsets.mHeightOffset = mLandData->mHeights[0] / HEIGHT_SCALE;\n                offsets.mUnk1 = mLandData->mUnk1;\n                offsets.mUnk2 = mLandData->mUnk2;\n\n                float prevY = mLandData->mHeights[0];\n                int number = 0; // avoid multiplication\n                for (int i = 0; i < LAND_SIZE; ++i) {\n                    float diff = (mLandData->mHeights[number] - prevY) / HEIGHT_SCALE;\n                    offsets.mHeightData[number] =\n                        (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5);\n\n                    float prevX = prevY = mLandData->mHeights[number];\n                    ++number;\n\n                    for (int j = 1; j < LAND_SIZE; ++j) {\n                        diff = (mLandData->mHeights[number] - prevX) / HEIGHT_SCALE;\n                        offsets.mHeightData[number] =\n                            (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5);\n\n                        prevX = mLandData->mHeights[number];\n                        ++number;\n                    }\n                }\n                esm.writeHNT(\"VHGT\", offsets, sizeof(VHGT));\n            }\n            if (mDataTypes & Land::DATA_WNAM)\n            {\n                // Generate WNAM record\n                signed char wnam[LAND_GLOBAL_MAP_LOD_SIZE];\n                constexpr float max = std::numeric_limits<signed char>::max();\n                constexpr float min = std::numeric_limits<signed char>::min();\n                constexpr float vertMult = static_cast<float>(ESM::Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT;\n                for (int row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row)\n                {\n                    for (int col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col)\n                    {\n                        float height = mLandData->mHeights[int(row * vertMult) * ESM::Land::LAND_SIZE + int(col * vertMult)];\n                        height /= height > 0 ? 128.f : 16.f;\n                        height = std::min(max, std::max(min, height));\n                        wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast<signed char>(height);\n                    }\n                }\n                esm.writeHNT(\"WNAM\", wnam);\n            }\n            if (mDataTypes & Land::DATA_VCLR) {\n                esm.writeHNT(\"VCLR\", mLandData->mColours);\n            }\n            if (mDataTypes & Land::DATA_VTEX) {\n                uint16_t vtex[LAND_NUM_TEXTURES];\n                transposeTextureData(mLandData->mTextures, vtex);\n                esm.writeHNT(\"VTEX\", vtex);\n            }\n        }\n\n    }\n\n    void Land::blank()\n    {\n        mPlugin = 0;\n\n        std::fill(std::begin(mWnam), std::end(mWnam), 0);\n\n        if (!mLandData)\n            mLandData = new LandData;\n\n        mLandData->mHeightOffset = 0;\n        std::fill(std::begin(mLandData->mHeights), std::end(mLandData->mHeights), 0);\n        mLandData->mMinHeight = 0;\n        mLandData->mMaxHeight = 0;\n        for (int i = 0; i < LAND_NUM_VERTS; ++i)\n        {\n            mLandData->mNormals[i*3+0] = 0;\n            mLandData->mNormals[i*3+1] = 0;\n            mLandData->mNormals[i*3+2] = 127;\n        }\n        std::fill(std::begin(mLandData->mTextures), std::end(mLandData->mTextures), 0);\n        std::fill(std::begin(mLandData->mColours), std::end(mLandData->mColours), 255);\n        mLandData->mUnk1 = 0;\n        mLandData->mUnk2 = 0;\n        mLandData->mDataLoaded = Land::DATA_VNML | Land::DATA_VHGT | Land::DATA_WNAM |\n            Land::DATA_VCLR | Land::DATA_VTEX;\n        mDataTypes = mLandData->mDataLoaded;\n\n        // No file associated with the land now\n        mContext.filename.clear();\n    }\n\n    void Land::loadData(int flags, LandData* target) const\n    {\n        // Create storage if nothing is loaded\n        if (!target && !mLandData)\n        {\n            mLandData = new LandData;\n        }\n\n        if (!target)\n            target = mLandData;\n\n        // Try to load only available data\n        flags = flags & mDataTypes;\n        // Return if all required data is loaded\n        if ((target->mDataLoaded & flags) == flags) {\n            return;\n        }\n\n        // Copy data to target if no file\n        if (mContext.filename.empty())\n        {\n            // Make sure there is data, and that it doesn't point to the same object.\n            if (mLandData && mLandData != target)\n                *target = *mLandData;\n\n            return;\n        }\n\n        ESM::ESMReader reader;\n        reader.restoreContext(mContext);\n\n        if (reader.isNextSub(\"VNML\")) {\n            condLoad(reader, flags, target->mDataLoaded, DATA_VNML, target->mNormals, sizeof(target->mNormals));\n        }\n\n        if (reader.isNextSub(\"VHGT\")) {\n            VHGT vhgt;\n            if (condLoad(reader, flags, target->mDataLoaded, DATA_VHGT, &vhgt, sizeof(vhgt))) {\n                target->mMinHeight = std::numeric_limits<float>::max();\n                target->mMaxHeight = -std::numeric_limits<float>::max();\n                float rowOffset = vhgt.mHeightOffset;\n                for (int y = 0; y < LAND_SIZE; y++) {\n                    rowOffset += vhgt.mHeightData[y * LAND_SIZE];\n\n                    target->mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE;\n                    if (rowOffset * HEIGHT_SCALE > target->mMaxHeight)\n                        target->mMaxHeight = rowOffset * HEIGHT_SCALE;\n                    if (rowOffset * HEIGHT_SCALE < target->mMinHeight)\n                        target->mMinHeight = rowOffset * HEIGHT_SCALE;\n\n                    float colOffset = rowOffset;\n                    for (int x = 1; x < LAND_SIZE; x++) {\n                        colOffset += vhgt.mHeightData[y * LAND_SIZE + x];\n                        target->mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE;\n\n                        if (colOffset * HEIGHT_SCALE > target->mMaxHeight)\n                            target->mMaxHeight = colOffset * HEIGHT_SCALE;\n                        if (colOffset * HEIGHT_SCALE < target->mMinHeight)\n                            target->mMinHeight = colOffset * HEIGHT_SCALE;\n                    }\n                }\n                target->mUnk1 = vhgt.mUnk1;\n                target->mUnk2 = vhgt.mUnk2;\n            }\n        }\n\n        if (reader.isNextSub(\"WNAM\"))\n            reader.skipHSub();\n\n        if (reader.isNextSub(\"VCLR\"))\n            condLoad(reader, flags, target->mDataLoaded, DATA_VCLR, target->mColours, 3 * LAND_NUM_VERTS);\n        if (reader.isNextSub(\"VTEX\")) {\n            uint16_t vtex[LAND_NUM_TEXTURES];\n            if (condLoad(reader, flags, target->mDataLoaded, DATA_VTEX, vtex, sizeof(vtex))) {\n                transposeTextureData(vtex, target->mTextures);\n            }\n        }\n    }\n\n    void Land::unloadData() const\n    {\n        if (mLandData)\n        {\n            delete mLandData;\n            mLandData = nullptr;\n        }\n    }\n\n    bool Land::condLoad(ESM::ESMReader& reader, int flags, int& targetFlags, int dataFlag, void *ptr, unsigned int size) const\n    {\n        if ((targetFlags & dataFlag) == 0 && (flags & dataFlag) != 0) {\n            reader.getHExact(ptr, size);\n            targetFlags |= dataFlag;\n            return true;\n        }\n        reader.skipHSubSize(size);\n        return false;\n    }\n\n    bool Land::isDataLoaded(int flags) const\n    {\n        return mLandData && (mLandData->mDataLoaded & flags) == flags;\n    }\n\n    Land::Land (const Land& land)\n    : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin),\n      mContext (land.mContext), mDataTypes (land.mDataTypes),\n      mLandData (land.mLandData ? new LandData (*land.mLandData) : nullptr)\n    {\n        std::copy(land.mWnam, land.mWnam + LAND_GLOBAL_MAP_LOD_SIZE, mWnam);\n    }\n\n    Land& Land::operator= (const Land& land)\n    {\n        Land tmp(land);\n        swap(tmp);\n        return *this;\n    }\n\n    void Land::swap (Land& land)\n    {\n        std::swap (mFlags, land.mFlags);\n        std::swap (mX, land.mX);\n        std::swap (mY, land.mY);\n        std::swap (mPlugin, land.mPlugin);\n        std::swap (mContext, land.mContext);\n        std::swap (mDataTypes, land.mDataTypes);\n        std::swap (mLandData, land.mLandData);\n        std::swap (mWnam, land.mWnam);\n    }\n\n    const Land::LandData *Land::getLandData (int flags) const\n    {\n        if (!(flags & mDataTypes))\n            return nullptr;\n\n        loadData (flags);\n        return mLandData;\n    }\n\n    const Land::LandData *Land::getLandData() const\n    {\n        return mLandData;\n    }\n\n    Land::LandData *Land::getLandData()\n    {\n        return mLandData;\n    }\n\n    void Land::add (int flags)\n    {\n        if (!mLandData)\n            mLandData = new LandData;\n\n        mDataTypes |= flags;\n        mLandData->mDataLoaded |= flags;\n    }\n\n    void Land::remove (int flags)\n    {\n        mDataTypes &= ~flags;\n\n        if (mLandData)\n        {\n            mLandData->mDataLoaded &= ~flags;\n\n            if (!mLandData->mDataLoaded)\n            {\n                delete mLandData;\n                mLandData = nullptr;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "components/esm/loadland.hpp",
    "content": "#ifndef OPENMW_ESM_LAND_H\n#define OPENMW_ESM_LAND_H\n\n#include <stdint.h>\n\n#include <components/misc/constants.hpp>\n\n#include \"esmcommon.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Landscape data.\n */\n\nstruct Land\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Land\"; }\n\n    Land();\n    ~Land();\n\n    int mFlags; // Only first four bits seem to be used, don't know what\n    // they mean.\n    int mX, mY; // Map coordinates.\n    int mPlugin; // Plugin index, used to reference the correct material palette.\n\n    // File context. This allows the ESM reader to be 'reset' to this\n    // location later when we are ready to load the full data set.\n    // In the editor, there may not be a file associated with the Land,\n    // in which case the filename will be empty.\n    ESM_Context mContext;\n\n    int mDataTypes;\n\n    enum\n    {\n        DATA_VNML = 1,\n        DATA_VHGT = 2,\n        DATA_WNAM = 4,\n        DATA_VCLR = 8,\n        DATA_VTEX = 16\n    };\n\n    // default height to use in case there is no Land record\n    static const int DEFAULT_HEIGHT = -2048;\n\n    // number of vertices per side\n    static const int LAND_SIZE = 65;\n\n    // cell terrain size in world coords\n    static const int REAL_SIZE = Constants::CellSizeInUnits;\n\n    // total number of vertices\n    static const int LAND_NUM_VERTS = LAND_SIZE * LAND_SIZE;\n\n    static const int HEIGHT_SCALE = 8;\n\n    //number of textures per side of land\n    static const int LAND_TEXTURE_SIZE = 16;\n\n    //total number of textures per land\n    static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE;\n\n    static const int LAND_GLOBAL_MAP_LOD_SIZE = 81;\n\n    static const int LAND_GLOBAL_MAP_LOD_SIZE_SQRT = 9;\n\n#pragma pack(push,1)\n    struct VHGT\n    {\n        float mHeightOffset;\n        int8_t mHeightData[LAND_NUM_VERTS];\n        short mUnk1;\n        char mUnk2;\n    };\n#pragma pack(pop)\n\n    typedef signed char VNML;\n\n    struct LandData\n    {\n        LandData()\n            : mHeightOffset(0)\n            , mMinHeight(0)\n            , mMaxHeight(0)\n            , mUnk1(0)\n            , mUnk2(0)\n            , mDataLoaded(0)\n        {\n        }\n\n        // Initial reference height for the first vertex, only needed for filling mHeights\n        float mHeightOffset;\n        // Height in world space for each vertex\n        float mHeights[LAND_NUM_VERTS];\n        float mMinHeight;\n        float mMaxHeight;\n\n        // 24-bit normals, these aren't always correct though. Edge and corner normals may be garbage.\n        VNML mNormals[LAND_NUM_VERTS * 3];\n\n        // 2D array of texture indices. An index can be used to look up an ESM::LandTexture,\n        // but to do so you must subtract 1 from the index first!\n        // An index of 0 indicates the default texture.\n        uint16_t mTextures[LAND_NUM_TEXTURES];\n\n        // 24-bit RGB color for each vertex\n        unsigned char mColours[3 * LAND_NUM_VERTS];\n\n        // ???\n        short mUnk1;\n        uint8_t mUnk2;\n\n        int mDataLoaded;\n    };\n\n    // low-LOD heightmap (used for rendering the global map)\n    signed char mWnam[LAND_GLOBAL_MAP_LOD_SIZE];\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n\n    /**\n     * Actually loads data into target\n     * If target is nullptr, assumed target is mLandData\n     */\n    void loadData(int flags, LandData* target = nullptr) const;\n\n    /**\n     * Frees memory allocated for mLandData\n     */\n    void unloadData() const;\n\n    /// Check if given data type is loaded\n    bool isDataLoaded(int flags) const;\n\n    /// Sets the flags and creates a LandData if needed\n    void setDataLoaded(int flags);\n\n        Land (const Land& land);\n\n        Land& operator= (const Land& land);\n\n        void swap (Land& land);\n\n        /// Return land data with at least the data types specified in \\a flags loaded (if they\n        /// are available). Will return a 0-pointer if there is no data for any of the\n        /// specified types.\n        const LandData *getLandData (int flags) const;\n\n        /// Return land data without loading first anything. Can return a 0-pointer.\n        const LandData *getLandData() const;\n\n        /// Return land data without loading first anything. Can return a 0-pointer.\n        LandData *getLandData();\n\n        /// \\attention Must not be called on objects that aren't fully loaded.\n        ///\n        /// \\note Added data fields will be uninitialised\n        void add (int flags);\n\n        /// \\attention Must not be called on objects that aren't fully loaded.\n        void remove (int flags);\n\n    private:\n\n        /// Loads data and marks it as loaded\n        /// \\return true if data is actually loaded from file, false otherwise\n        /// including the case when data is already loaded\n        bool condLoad(ESM::ESMReader& reader, int flags, int& targetFlags, int dataFlag, void *ptr, unsigned int size) const;\n\n        mutable LandData *mLandData;\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadlevlist.cpp",
    "content": "#include \"loadlevlist.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    void LevelledListBase::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasList = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    esm.getHT(mFlags);\n                    break;\n                case ESM::FourCC<'N','N','A','M'>::value:\n                    esm.getHT(mChanceNone);\n                    break;\n                case ESM::FourCC<'I','N','D','X'>::value:\n                {\n                    int length = 0;\n                    esm.getHT(length);\n                    mList.resize(length);\n\n                    // If this levelled list was already loaded by a previous content file,\n                    // we overwrite the list. Merging lists should probably be left to external tools,\n                    // with the limited amount of information there is in the records, all merging methods\n                    // will be flawed in some way. For a proper fix the ESM format would have to be changed\n                    // to actually track list changes instead of including the whole list for every file\n                    // that does something with that list.\n                    for (size_t i = 0; i < mList.size(); i++)\n                    {\n                        LevelItem &li = mList[i];\n                        li.mId = esm.getHNString(mRecName);\n                        esm.getHNT(li.mLevel, \"INTV\");\n                    }\n\n                    hasList = true;\n                    break;\n                }\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                {\n                    if (!hasList)\n                    {\n                        // Original engine ignores rest of the record, even if there are items following\n                        mList.clear();\n                        esm.skipRecord();\n                    }\n                    else\n                    {\n                        esm.fail(\"Unknown subrecord\");\n                    }\n                    break;\n                }\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n    }\n\n    void LevelledListBase::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNT(\"DATA\", mFlags);\n        esm.writeHNT(\"NNAM\", mChanceNone);\n        esm.writeHNT<int>(\"INDX\", mList.size());\n\n        for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)\n        {\n            esm.writeHNCString(mRecName, it->mId);\n            esm.writeHNT(\"INTV\", it->mLevel);\n        }\n    }\n\n    void LevelledListBase::blank()\n    {\n        mFlags = 0;\n        mChanceNone = 0;\n        mList.clear();\n    }\n\n    unsigned int CreatureLevList::sRecordId = REC_LEVC;\n\n    unsigned int ItemLevList::sRecordId = REC_LEVI;\n}\n"
  },
  {
    "path": "components/esm/loadlevlist.hpp",
    "content": "#ifndef OPENMW_ESM_LEVLISTS_H\n#define OPENMW_ESM_LEVLISTS_H\n\n#include <string>\n#include <vector>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Levelled lists. Since these have identical layout, I only bothered\n * to implement it once.\n *\n * We should later implement the ability to merge levelled lists from\n * several files.\n */\n\nstruct LevelledListBase\n{\n    int mFlags;\n    unsigned char mChanceNone; // Chance that none are selected (0-100)\n    std::string mId;\n\n    // Record name used to read references. Must be set before load() is\n    // called.\n    const char *mRecName;\n\n    struct LevelItem\n    {\n        std::string mId;\n        short mLevel;\n    };\n\n    std::vector<LevelItem> mList;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\nstruct CreatureLevList: LevelledListBase\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"CreatureLevList\"; }\n\n    enum Flags\n    {\n\n        AllLevels = 0x01  // Calculate from all levels <= player\n                          // level, not just the closest below\n                          // player.\n    };\n\n    CreatureLevList()\n    {\n        mRecName = \"CNAM\";\n    }\n};\n\nstruct ItemLevList: LevelledListBase\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"ItemLevList\"; }\n\n    enum Flags\n    {\n\n        Each = 0x01,      // Select a new item each time this\n                          // list is instantiated, instead of\n                          // giving several identical items\n                          // (used when a container has more\n                          // than one instance of one levelled\n                          // list.)\n        AllLevels = 0x02  // Calculate from all levels <= player\n                          // level, not just the closest below\n                          // player.\n    };\n\n    ItemLevList()\n    {\n        mRecName = \"INAM\";\n    }\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadligh.cpp",
    "content": "#include \"loadligh.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Light::sRecordId = REC_LIGH;\n\n    void Light::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::FourCC<'L','H','D','T'>::value:\n                    esm.getHT(mData, 24);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','N','A','M'>::value:\n                    mSound = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing LHDT subrecord\");\n    }\n    void Light::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n        esm.writeHNT(\"LHDT\", mData, 24);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"SNAM\", mSound);\n    }\n\n    void Light::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mTime = 0;\n        mData.mRadius = 0;\n        mData.mColor = 0;\n        mData.mFlags = 0;\n        mSound.clear();\n        mScript.clear();\n        mModel.clear();\n        mIcon.clear();\n        mName.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadligh.hpp",
    "content": "#ifndef OPENMW_ESM_LIGH_H\n#define OPENMW_ESM_LIGH_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Lights. Includes static light sources and also carryable candles\n * and torches.\n */\n\nstruct Light\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Light\"; }\n\n    enum Flags\n    {\n        Dynamic     = 0x001,\n        Carry       = 0x002, // Can be carried\n        Negative    = 0x004, // Negative light - i.e. darkness\n        Flicker     = 0x008,\n        Fire        = 0x010,\n        OffDefault  = 0x020, // Off by default - does not burn while placed in a cell, but can burn when equipped by an NPC\n        FlickerSlow = 0x040,\n        Pulse       = 0x080,\n        PulseSlow   = 0x100\n    };\n\n    struct LHDTstruct\n    {\n        float mWeight;\n        int mValue;\n        int mTime; // Duration\n        int mRadius;\n        unsigned int mColor; // 4-byte rgba value\n        int mFlags;\n    }; // Size = 24 bytes\n\n    LHDTstruct mData;\n\n    std::string mSound, mScript, mModel, mIcon, mName, mId;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadlock.cpp",
    "content": "#include \"loadlock.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Lockpick::sRecordId = REC_LOCK;\n\n    void Lockpick::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'L','K','D','T'>::value:\n                    esm.getHT(mData, 16);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing LKDT subrecord\");\n    }\n\n    void Lockpick::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n\n        esm.writeHNT(\"LKDT\", mData, 16);\n        esm.writeHNOString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n    }\n\n    void Lockpick::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mQuality = 0;\n        mData.mUses = 0;\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadlock.hpp",
    "content": "#ifndef OPENMW_ESM_LOCK_H\n#define OPENMW_ESM_LOCK_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct Lockpick\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Lockpick\"; }\n\n    struct Data\n    {\n        float mWeight;\n        int mValue;\n\n        float mQuality;\n        int mUses;\n    }; // Size = 16\n\n    Data mData;\n    std::string mId, mName, mModel, mIcon, mScript;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadltex.cpp",
    "content": "#include \"loadltex.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int LandTexture::sRecordId = REC_LTEX;\n\n    void LandTexture::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasIndex = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'I','N','T','V'>::value:\n                    esm.getHT(mIndex);\n                    hasIndex = true;\n                    break;\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    mTexture = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasIndex)\n            esm.fail(\"Missing INTV subrecord\");\n    }\n    void LandTexture::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n        esm.writeHNT(\"INTV\", mIndex);\n        esm.writeHNCString(\"DATA\", mTexture);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n        }\n    }\n\n    void LandTexture::blank()\n    {\n        mId.clear();\n        mTexture.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadltex.hpp",
    "content": "#ifndef OPENMW_ESM_LTEX_H\n#define OPENMW_ESM_LTEX_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Texture used for texturing landscape.\n * They are indexed by 'num', but still use 'id' to override base records.\n * Original editor even does not allow to create new records with existing ID's.\n * TODO: currently OpenMW-CS does not allow to override LTEX records at all.\n */\n\nstruct LandTexture\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"LandTexture\"; }\n\n    // mId is merely a user friendly name for the texture in the editor.\n    std::string mId, mTexture;\n    int mIndex;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    /// Sets the record to the default state. Does not touch the index. Does touch mID.\n    void blank();\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadmgef.cpp",
    "content": "#include \"loadmgef.hpp\"\n\n#include <sstream>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace\n{\n    static const char *sIds[ESM::MagicEffect::Length] =\n    {\n        \"WaterBreathing\",\n        \"SwiftSwim\",\n        \"WaterWalking\",\n        \"Shield\",\n        \"FireShield\",\n        \"LightningShield\",\n        \"FrostShield\",\n        \"Burden\",\n        \"Feather\",\n        \"Jump\",\n        \"Levitate\",\n        \"SlowFall\",\n        \"Lock\",\n        \"Open\",\n        \"FireDamage\",\n        \"ShockDamage\",\n        \"FrostDamage\",\n        \"DrainAttribute\",\n        \"DrainHealth\",\n        \"DrainMagicka\",\n        \"DrainFatigue\",\n        \"DrainSkill\",\n        \"DamageAttribute\",\n        \"DamageHealth\",\n        \"DamageMagicka\",\n        \"DamageFatigue\",\n        \"DamageSkill\",\n        \"Poison\",\n        \"WeaknessToFire\",\n        \"WeaknessToFrost\",\n        \"WeaknessToShock\",\n        \"WeaknessToMagicka\",\n        \"WeaknessToCommonDisease\",\n        \"WeaknessToBlightDisease\",\n        \"WeaknessToCorprusDisease\",\n        \"WeaknessToPoison\",\n        \"WeaknessToNormalWeapons\",\n        \"DisintegrateWeapon\",\n        \"DisintegrateArmor\",\n        \"Invisibility\",\n        \"Chameleon\",\n        \"Light\",\n        \"Sanctuary\",\n        \"NightEye\",\n        \"Charm\",\n        \"Paralyze\",\n        \"Silence\",\n        \"Blind\",\n        \"Sound\",\n        \"CalmHumanoid\",\n        \"CalmCreature\",\n        \"FrenzyHumanoid\",\n        \"FrenzyCreature\",\n        \"DemoralizeHumanoid\",\n        \"DemoralizeCreature\",\n        \"RallyHumanoid\",\n        \"RallyCreature\",\n        \"Dispel\",\n        \"Soultrap\",\n        \"Telekinesis\",\n        \"Mark\",\n        \"Recall\",\n        \"DivineIntervention\",\n        \"AlmsiviIntervention\",\n        \"DetectAnimal\",\n        \"DetectEnchantment\",\n        \"DetectKey\",\n        \"SpellAbsorption\",\n        \"Reflect\",\n        \"CureCommonDisease\",\n        \"CureBlightDisease\",\n        \"CureCorprusDisease\",\n        \"CurePoison\",\n        \"CureParalyzation\",\n        \"RestoreAttribute\",\n        \"RestoreHealth\",\n        \"RestoreMagicka\",\n        \"RestoreFatigue\",\n        \"RestoreSkill\",\n        \"FortifyAttribute\",\n        \"FortifyHealth\",\n        \"FortifyMagicka\",\n        \"FortifyFatigue\",\n        \"FortifySkill\",\n        \"FortifyMaximumMagicka\",\n        \"AbsorbAttribute\",\n        \"AbsorbHealth\",\n        \"AbsorbMagicka\",\n        \"AbsorbFatigue\",\n        \"AbsorbSkill\",\n        \"ResistFire\",\n        \"ResistFrost\",\n        \"ResistShock\",\n        \"ResistMagicka\",\n        \"ResistCommonDisease\",\n        \"ResistBlightDisease\",\n        \"ResistCorprusDisease\",\n        \"ResistPoison\",\n        \"ResistNormalWeapons\",\n        \"ResistParalysis\",\n        \"RemoveCurse\",\n        \"TurnUndead\",\n        \"SummonScamp\",\n        \"SummonClannfear\",\n        \"SummonDaedroth\",\n        \"SummonDremora\",\n        \"SummonAncestralGhost\",\n        \"SummonSkeletalMinion\",\n        \"SummonBonewalker\",\n        \"SummonGreaterBonewalker\",\n        \"SummonBonelord\",\n        \"SummonWingedTwilight\",\n        \"SummonHunger\",\n        \"SummonGoldenSaint\",\n        \"SummonFlameAtronach\",\n        \"SummonFrostAtronach\",\n        \"SummonStormAtronach\",\n        \"FortifyAttack\",\n        \"CommandCreature\",\n        \"CommandHumanoid\",\n        \"BoundDagger\",\n        \"BoundLongsword\",\n        \"BoundMace\",\n        \"BoundBattleAxe\",\n        \"BoundSpear\",\n        \"BoundLongbow\",\n        \"ExtraSpell\",\n        \"BoundCuirass\",\n        \"BoundHelm\",\n        \"BoundBoots\",\n        \"BoundShield\",\n        \"BoundGloves\",\n        \"Corprus\",\n        \"Vampirism\",\n        \"SummonCenturionSphere\",\n        \"SunDamage\",\n        \"StuntedMagicka\",\n\n        // Tribunal only\n        \"SummonFabricant\",\n\n        // Bloodmoon only\n        \"SummonWolf\",\n        \"SummonBear\",\n        \"SummonBonewolf\",\n        \"SummonCreature04\",\n        \"SummonCreature05\"\n    };\n\n    const int NumberOfHardcodedFlags = 143;\n    const int HardcodedFlags[NumberOfHardcodedFlags] = {\n        0x11c8, 0x11c0, 0x11c8, 0x11e0, 0x11e0, 0x11e0, 0x11e0, 0x11d0,\n        0x11c0, 0x11c0, 0x11e0, 0x11c0, 0x11184, 0x11184, 0x1f0, 0x1f0,\n        0x1f0, 0x11d2, 0x11f0, 0x11d0, 0x11d0, 0x11d1, 0x1d2, 0x1f0,\n        0x1d0, 0x1d0, 0x1d1, 0x1f0, 0x11d0, 0x11d0, 0x11d0, 0x11d0,\n        0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x1d0, 0x1d0, 0x11c8,\n        0x31c0, 0x11c0, 0x11c0, 0x11c0, 0x1180, 0x11d8, 0x11d8, 0x11d0,\n        0x11d0, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180,\n        0x11180, 0x11c4, 0x111b8, 0x1040, 0x104c, 0x104c, 0x104c, 0x104c,\n        0x1040, 0x1040, 0x1040, 0x11c0, 0x11c0, 0x1cc, 0x1cc, 0x1cc,\n        0x1cc, 0x1cc, 0x1c2, 0x1c0, 0x1c0, 0x1c0, 0x1c1, 0x11c2,\n        0x11c0, 0x11c0, 0x11c0, 0x11c1, 0x11c0, 0x21192, 0x20190, 0x20190,\n        0x20190, 0x21191, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0,\n        0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x1c0, 0x11190, 0x9048, 0x9048,\n        0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048,\n        0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x11c0, 0x1180, 0x1180,\n        0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x1188, 0x5048,\n        0x5048, 0x5048, 0x5048, 0x5048, 0x1048, 0x104c, 0x1048, 0x40,\n        0x11c8, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048\n    };\n}\n\nnamespace ESM\n{\n    unsigned int MagicEffect::sRecordId = REC_MGEF;\n\nvoid MagicEffect::load(ESMReader &esm, bool &isDeleted)\n{\n    isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future)\n\n    esm.getHNT(mIndex, \"INDX\");\n\n    mId = indexToId (mIndex);\n\n    esm.getHNT(mData, \"MEDT\", 36);\n    if (esm.getFormat() == 0)\n    {\n        // don't allow mods to change fixed flags in the legacy format\n        mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight);\n        if (mIndex>=0 && mIndex<NumberOfHardcodedFlags)\n        mData.mFlags |= HardcodedFlags[mIndex];\n    }\n\n    // vanilla MW accepts the _SND subrecords before or after DESC... I hope\n    // this isn't true for other records, or we have to do a mass-refactor\n    while (esm.hasMoreSubs())\n    {\n        esm.getSubName();\n        switch (esm.retSubName().intval)\n        {\n            case ESM::FourCC<'I','T','E','X'>::value:\n                mIcon = esm.getHString();\n                break;\n            case ESM::FourCC<'P','T','E','X'>::value:\n                mParticle = esm.getHString();\n                break;\n            case ESM::FourCC<'B','S','N','D'>::value:\n                mBoltSound = esm.getHString();\n                break;\n            case ESM::FourCC<'C','S','N','D'>::value:\n                mCastSound = esm.getHString();\n                break;\n            case ESM::FourCC<'H','S','N','D'>::value:\n                mHitSound = esm.getHString();\n                break;\n            case ESM::FourCC<'A','S','N','D'>::value:\n                mAreaSound = esm.getHString();\n                break;\n            case ESM::FourCC<'C','V','F','X'>::value:\n                mCasting = esm.getHString();\n                break;\n            case ESM::FourCC<'B','V','F','X'>::value:\n                mBolt = esm.getHString();\n                break;\n            case ESM::FourCC<'H','V','F','X'>::value:\n                mHit = esm.getHString();\n                break;\n            case ESM::FourCC<'A','V','F','X'>::value:\n                mArea = esm.getHString();\n                break;\n            case ESM::FourCC<'D','E','S','C'>::value:\n                mDescription = esm.getHString();\n                break;\n            default:\n                esm.fail(\"Unknown subrecord\");\n        }\n    }\n}\nvoid MagicEffect::save(ESMWriter &esm, bool /*isDeleted*/) const\n{\n    esm.writeHNT(\"INDX\", mIndex);\n\n    esm.writeHNT(\"MEDT\", mData, 36);\n\n    esm.writeHNOCString(\"ITEX\", mIcon);\n    esm.writeHNOCString(\"PTEX\", mParticle);\n    esm.writeHNOCString(\"BSND\", mBoltSound);\n    esm.writeHNOCString(\"CSND\", mCastSound);\n    esm.writeHNOCString(\"HSND\", mHitSound);\n    esm.writeHNOCString(\"ASND\", mAreaSound);\n\n    esm.writeHNOCString(\"CVFX\", mCasting);\n    esm.writeHNOCString(\"BVFX\", mBolt);\n    esm.writeHNOCString(\"HVFX\", mHit);\n    esm.writeHNOCString(\"AVFX\", mArea);\n\n    esm.writeHNOString(\"DESC\", mDescription);\n}\n\nshort MagicEffect::getResistanceEffect(short effect)\n{\n    // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute\n\n    // <Effect, Effect providing resistance against first effect>\n    static std::map<short, short> effects;\n    if (effects.empty())\n    {\n        effects[DisintegrateArmor] = Sanctuary;\n        effects[DisintegrateWeapon] = Sanctuary;\n\n        for (int i=0; i<5; ++i)\n            effects[DrainAttribute+i] = ResistMagicka;\n        for (int i=0; i<5; ++i)\n            effects[DamageAttribute+i] = ResistMagicka;\n        for (int i=0; i<5; ++i)\n            effects[AbsorbAttribute+i] = ResistMagicka;\n        for (int i=0; i<10; ++i)\n            effects[WeaknessToFire+i] = ResistMagicka;\n\n        effects[Burden] = ResistMagicka;\n        effects[Charm] = ResistMagicka;\n        effects[Silence] = ResistMagicka;\n        effects[Blind] = ResistMagicka;\n        effects[Sound] = ResistMagicka;\n\n        for (int i=0; i<2; ++i)\n        {\n            effects[CalmHumanoid+i] = ResistMagicka;\n            effects[FrenzyHumanoid+i] = ResistMagicka;\n            effects[DemoralizeHumanoid+i] = ResistMagicka;\n            effects[RallyHumanoid+i] = ResistMagicka;\n        }\n\n        effects[TurnUndead] = ResistMagicka;\n\n        effects[FireDamage] = ResistFire;\n        effects[FrostDamage] = ResistFrost;\n        effects[ShockDamage] = ResistShock;\n        effects[Vampirism] = ResistCommonDisease;\n        effects[Corprus] = ResistCorprusDisease;\n        effects[Poison] = ResistPoison;\n        effects[Paralyze] = ResistParalysis;\n    }\n\n    if (effects.find(effect) != effects.end())\n        return effects[effect];\n    else\n        return -1;\n}\n\nshort MagicEffect::getWeaknessEffect(short effect)\n{\n    static std::map<short, short> effects;\n    if (effects.empty())\n    {\n        for (int i=0; i<5; ++i)\n            effects[DrainAttribute+i] = WeaknessToMagicka;\n        for (int i=0; i<5; ++i)\n            effects[DamageAttribute+i] = WeaknessToMagicka;\n        for (int i=0; i<5; ++i)\n            effects[AbsorbAttribute+i] = WeaknessToMagicka;\n        for (int i=0; i<10; ++i)\n            effects[WeaknessToFire+i] = WeaknessToMagicka;\n\n        effects[Burden] = WeaknessToMagicka;\n        effects[Charm] = WeaknessToMagicka;\n        effects[Silence] = WeaknessToMagicka;\n        effects[Blind] = WeaknessToMagicka;\n        effects[Sound] = WeaknessToMagicka;\n\n        for (int i=0; i<2; ++i)\n        {\n            effects[CalmHumanoid+i] = WeaknessToMagicka;\n            effects[FrenzyHumanoid+i] = WeaknessToMagicka;\n            effects[DemoralizeHumanoid+i] = WeaknessToMagicka;\n            effects[RallyHumanoid+i] = WeaknessToMagicka;\n        }\n\n        effects[TurnUndead] = WeaknessToMagicka;\n\n        effects[FireDamage] = WeaknessToFire;\n        effects[FrostDamage] = WeaknessToFrost;\n        effects[ShockDamage] = WeaknessToShock;\n        effects[Vampirism] = WeaknessToCommonDisease;\n        effects[Corprus] = WeaknessToCorprusDisease;\n        effects[Poison] = WeaknessToPoison;\n\n        effects[Paralyze] = -1;\n    }\n\n    if (effects.find(effect) != effects.end())\n        return effects[effect];\n    else\n        return -1;\n}\n\nstatic std::map<short,std::string> genNameMap()\n{\n    // Map effect ID to GMST name\n    // http://www.uesp.net/morrow/hints/mweffects.shtml\n    std::map<short, std::string> names;\n    names[85] =\"sEffectAbsorbAttribute\";\n    names[88] =\"sEffectAbsorbFatigue\";\n    names[86] =\"sEffectAbsorbHealth\";\n    names[87] =\"sEffectAbsorbSpellPoints\";\n    names[89] =\"sEffectAbsorbSkill\";\n    names[63] =\"sEffectAlmsiviIntervention\";\n    names[47] =\"sEffectBlind\";\n    names[123] =\"sEffectBoundBattleAxe\";\n    names[129] =\"sEffectBoundBoots\";\n    names[127] =\"sEffectBoundCuirass\";\n    names[120] =\"sEffectBoundDagger\";\n    names[131] =\"sEffectBoundGloves\";\n    names[128] =\"sEffectBoundHelm\";\n    names[125] =\"sEffectBoundLongbow\";\n    names[126] =\"sEffectExtraSpell\";\n    names[121] =\"sEffectBoundLongsword\";\n    names[122] =\"sEffectBoundMace\";\n    names[130] =\"sEffectBoundShield\";\n    names[124] =\"sEffectBoundSpear\";\n    names[7] =\"sEffectBurden\";\n    names[50] =\"sEffectCalmCreature\";\n    names[49] =\"sEffectCalmHumanoid\";\n    names[40] =\"sEffectChameleon\";\n    names[44] =\"sEffectCharm\";\n    names[118] =\"sEffectCommandCreatures\";\n    names[119] =\"sEffectCommandHumanoids\";\n    names[132] =\"sEffectCorpus\"; // NB this typo. (bethesda made it)\n    names[70] =\"sEffectCureBlightDisease\";\n    names[69] =\"sEffectCureCommonDisease\";\n    names[71] =\"sEffectCureCorprusDisease\";\n    names[73] =\"sEffectCureParalyzation\";\n    names[72] =\"sEffectCurePoison\";\n    names[22] =\"sEffectDamageAttribute\";\n    names[25] =\"sEffectDamageFatigue\";\n    names[23] =\"sEffectDamageHealth\";\n    names[24] =\"sEffectDamageMagicka\";\n    names[26] =\"sEffectDamageSkill\";\n    names[54] =\"sEffectDemoralizeCreature\";\n    names[53] =\"sEffectDemoralizeHumanoid\";\n    names[64] =\"sEffectDetectAnimal\";\n    names[65] =\"sEffectDetectEnchantment\";\n    names[66] =\"sEffectDetectKey\";\n    names[38] =\"sEffectDisintegrateArmor\";\n    names[37] =\"sEffectDisintegrateWeapon\";\n    names[57] =\"sEffectDispel\";\n    names[62] =\"sEffectDivineIntervention\";\n    names[17] =\"sEffectDrainAttribute\";\n    names[20] =\"sEffectDrainFatigue\";\n    names[18] =\"sEffectDrainHealth\";\n    names[19] =\"sEffectDrainSpellpoints\";\n    names[21] =\"sEffectDrainSkill\";\n    names[8] =\"sEffectFeather\";\n    names[14] =\"sEffectFireDamage\";\n    names[4] =\"sEffectFireShield\";\n    names[117] =\"sEffectFortifyAttackBonus\";\n    names[79] =\"sEffectFortifyAttribute\";\n    names[82] =\"sEffectFortifyFatigue\";\n    names[80] =\"sEffectFortifyHealth\";\n    names[81] =\"sEffectFortifySpellpoints\";\n    names[84] =\"sEffectFortifyMagickaMultiplier\";\n    names[83] =\"sEffectFortifySkill\";\n    names[52] =\"sEffectFrenzyCreature\";\n    names[51] =\"sEffectFrenzyHumanoid\";\n    names[16] =\"sEffectFrostDamage\";\n    names[6] =\"sEffectFrostShield\";\n    names[39] =\"sEffectInvisibility\";\n    names[9] =\"sEffectJump\";\n    names[10] =\"sEffectLevitate\";\n    names[41] =\"sEffectLight\";\n    names[5] =\"sEffectLightningShield\";\n    names[12] =\"sEffectLock\";\n    names[60] =\"sEffectMark\";\n    names[43] =\"sEffectNightEye\";\n    names[13] =\"sEffectOpen\";\n    names[45] =\"sEffectParalyze\";\n    names[27] =\"sEffectPoison\";\n    names[56] =\"sEffectRallyCreature\";\n    names[55] =\"sEffectRallyHumanoid\";\n    names[61] =\"sEffectRecall\";\n    names[68] =\"sEffectReflect\";\n    names[100] =\"sEffectRemoveCurse\";\n    names[95] =\"sEffectResistBlightDisease\";\n    names[94] =\"sEffectResistCommonDisease\";\n    names[96] =\"sEffectResistCorprusDisease\";\n    names[90] =\"sEffectResistFire\";\n    names[91] =\"sEffectResistFrost\";\n    names[93] =\"sEffectResistMagicka\";\n    names[98] =\"sEffectResistNormalWeapons\";\n    names[99] =\"sEffectResistParalysis\";\n    names[97] =\"sEffectResistPoison\";\n    names[92] =\"sEffectResistShock\";\n    names[74] =\"sEffectRestoreAttribute\";\n    names[77] =\"sEffectRestoreFatigue\";\n    names[75] =\"sEffectRestoreHealth\";\n    names[76] =\"sEffectRestoreSpellPoints\";\n    names[78] =\"sEffectRestoreSkill\";\n    names[42] =\"sEffectSanctuary\";\n    names[3] =\"sEffectShield\";\n    names[15] =\"sEffectShockDamage\";\n    names[46] =\"sEffectSilence\";\n    names[11] =\"sEffectSlowFall\";\n    names[58] =\"sEffectSoultrap\";\n    names[48] =\"sEffectSound\";\n    names[67] =\"sEffectSpellAbsorption\";\n    names[136] =\"sEffectStuntedMagicka\";\n    names[106] =\"sEffectSummonAncestralGhost\";\n    names[110] =\"sEffectSummonBonelord\";\n    names[108] =\"sEffectSummonLeastBonewalker\";\n    names[134] =\"sEffectSummonCenturionSphere\";\n    names[103] =\"sEffectSummonClannfear\";\n    names[104] =\"sEffectSummonDaedroth\";\n    names[105] =\"sEffectSummonDremora\";\n    names[114] =\"sEffectSummonFlameAtronach\";\n    names[115] =\"sEffectSummonFrostAtronach\";\n    names[113] =\"sEffectSummonGoldenSaint\";\n    names[109] =\"sEffectSummonGreaterBonewalker\";\n    names[112] =\"sEffectSummonHunger\";\n    names[102] =\"sEffectSummonScamp\";\n    names[107] =\"sEffectSummonSkeletalMinion\";\n    names[116] =\"sEffectSummonStormAtronach\";\n    names[111] =\"sEffectSummonWingedTwilight\";\n    names[135] =\"sEffectSunDamage\";\n    names[1] =\"sEffectSwiftSwim\";\n    names[59] =\"sEffectTelekinesis\";\n    names[101] =\"sEffectTurnUndead\";\n    names[133] =\"sEffectVampirism\";\n    names[0] =\"sEffectWaterBreathing\";\n    names[2] =\"sEffectWaterWalking\";\n    names[33] =\"sEffectWeaknesstoBlightDisease\";\n    names[32] =\"sEffectWeaknesstoCommonDisease\";\n    names[34] =\"sEffectWeaknesstoCorprusDisease\";\n    names[28] =\"sEffectWeaknesstoFire\";\n    names[29] =\"sEffectWeaknesstoFrost\";\n    names[31] =\"sEffectWeaknesstoMagicka\";\n    names[36] =\"sEffectWeaknesstoNormalWeapons\";\n    names[35] =\"sEffectWeaknesstoPoison\";\n    names[30] =\"sEffectWeaknesstoShock\";\n\n    // bloodmoon\n    names[138] =\"sEffectSummonCreature01\";\n    names[139] =\"sEffectSummonCreature02\";\n    names[140] =\"sEffectSummonCreature03\";\n    names[141] =\"sEffectSummonCreature04\";\n    names[142] =\"sEffectSummonCreature05\";\n\n    // tribunal\n    names[137] =\"sEffectSummonFabricant\";\n\n    return names;\n}\nconst std::map<short,std::string> MagicEffect::sNames = genNameMap();\n\nconst std::string &MagicEffect::effectIdToString(short effectID)\n{\n    std::map<short,std::string>::const_iterator name = sNames.find(effectID);\n    if(name == sNames.end())\n        throw std::runtime_error(std::string(\"Unimplemented effect ID \")+std::to_string(effectID));\n\n    return name->second;\n}\n\nclass FindSecond {\n    const std::string &mName;\n\npublic:\n    FindSecond(const std::string &name) : mName(name) { }\n\n    bool operator()(const std::pair<short,std::string> &item) const\n    {\n        if(Misc::StringUtils::ciEqual(item.second, mName))\n            return true;\n        return false;\n    }\n};\n\nshort MagicEffect::effectStringToId(const std::string &effect)\n{\n    std::map<short,std::string>::const_iterator name;\n\n    name = std::find_if(sNames.begin(), sNames.end(), FindSecond(effect));\n    if(name == sNames.end())\n        throw std::runtime_error(std::string(\"Unimplemented effect \")+effect);\n\n    return name->first;\n}\n\nMagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const {\n    if ( mData.mFlags & NoMagnitude )\n        return MDT_None;\n    if ( mIndex == 84 )\n        return MDT_TimesInt;\n    if ( mIndex == 59 ||\n        ( mIndex >= 64 && mIndex <= 66) )\n        return MDT_Feet;\n    if ( mIndex == 118 || mIndex == 119 )\n        return MDT_Level;\n    if (   ( mIndex >= 28 && mIndex <= 36 )\n        || ( mIndex >= 90 && mIndex <= 99 )\n        ||   mIndex == 40 || mIndex == 47\n        ||   mIndex == 57 || mIndex == 68 )\n        return MDT_Percentage;\n\n    return MDT_Points;\n}\n\n    void MagicEffect::blank()\n    {\n        mData.mSchool = 0;\n        mData.mBaseCost = 0;\n        mData.mFlags = 0;\n        mData.mRed = 0;\n        mData.mGreen = 0;\n        mData.mBlue = 0;\n        mData.mSpeed = 0;\n\n        mIcon.clear();\n        mParticle.clear();\n        mCasting.clear();\n        mHit.clear();\n        mArea.clear();\n        mBolt.clear();\n        mCastSound.clear();\n        mBoltSound.clear();\n        mHitSound.clear();\n        mAreaSound.clear();\n        mDescription.clear();\n    }\n\n    std::string MagicEffect::indexToId (int index)\n    {\n        std::ostringstream stream;\n\n        if (index!=-1)\n        {\n            stream << \"#\";\n\n            if (index<100)\n            {\n                stream << \"0\";\n\n                if (index<10)\n                    stream << \"0\";\n            }\n\n            stream << index;\n\n            if (index>=0 && index<Length)\n                stream << sIds[index];\n        }\n\n        return stream.str();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadmgef.hpp",
    "content": "#ifndef OPENMW_ESM_MGEF_H\n#define OPENMW_ESM_MGEF_H\n\n#include <string>\n#include <map>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct MagicEffect\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"MagicEffect\"; }\n\n    std::string mId;\n\n    enum Flags\n    {\n        // Originally fixed flags (HardcodedFlags array consists of just these)\n        TargetSkill = 0x1, // Affects a specific skill, which is specified elsewhere in the effect structure.\n        TargetAttribute = 0x2, // Affects a specific attribute, which is specified elsewhere in the effect structure.\n        NoDuration = 0x4, // Has no duration. Only runs effect once on cast.\n        NoMagnitude = 0x8, // Has no magnitude.\n        Harmful = 0x10, // Counts as a negative effect. Interpreted as useful for attack, and is treated as a bad effect in alchemy.\n        ContinuousVfx = 0x20, // The effect's hit particle VFX repeats for the full duration of the spell, rather than occuring once on hit.\n        CastSelf = 0x40, // Allows range - cast on self.\n        CastTouch = 0x80, // Allows range - cast on touch.\n        CastTarget = 0x100, // Allows range - cast on target.\n        AppliedOnce = 0x1000, // An effect that is applied once it lands, instead of continuously. Allows an effect to reduce an attribute below zero; removes the normal minimum effect duration of 1 second.\n        Stealth = 0x2000, // Unused\n        NonRecastable = 0x4000, // Does not land if parent spell is already affecting target. Shows \"you cannot re-cast\" message for self target.\n        IllegalDaedra = 0x8000, // Unused\n        Unreflectable = 0x10000, // Cannot be reflected, the effect always lands normally.\n        CasterLinked = 0x20000, // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells.\n\n        // Originally modifiable flags\n        AllowSpellmaking = 0x200, // Can be used for spellmaking\n        AllowEnchanting = 0x400, // Can be used for enchanting\n        NegativeLight = 0x800 // Unused\n    };\n\n    enum MagnitudeDisplayType\n    {\n        MDT_None,\n        MDT_Feet,\n        MDT_Level,\n        MDT_Percentage,\n        MDT_Points,\n        MDT_TimesInt\n    };\n\n    struct MEDTstruct\n    {\n        int mSchool; // SpellSchool, see defs.hpp\n        float mBaseCost;\n        int mFlags;\n        // Glow color for enchanted items with this effect\n        int mRed, mGreen, mBlue;\n\n        float mUnknown1; // Called \"Size X\" in CS\n        float mSpeed; // Speed of fired projectile\n        float mUnknown2; // Called \"Size Cap\" in CS\n    }; // 36 bytes\n\n    static const std::map<short,std::string> sNames;\n\n    static const std::string &effectIdToString(short effectID);\n    static short effectStringToId(const std::string &effect);\n\n    /// Returns the effect that provides resistance against \\a effect (or -1 if there's none)\n    static short getResistanceEffect(short effect);\n    /// Returns the effect that induces weakness against \\a effect (or -1 if there's none)\n    static short getWeaknessEffect(short effect);\n\n    MagnitudeDisplayType getMagnitudeDisplayType() const;\n\n\n    MEDTstruct mData;\n\n    std::string mIcon, mParticle; // Textures\n    std::string mCasting, mHit, mArea; // ESM::Static\n    std::string mBolt; // ESM::Weapon\n    std::string mCastSound, mBoltSound, mHitSound, mAreaSound; // Sounds\n    std::string mDescription;\n\n    // Index of this magical effect. Corresponds to one of the\n    // hard-coded effects in the original engine:\n    // 0-136 in Morrowind\n    // 137 in Tribunal\n    // 138-140 in Bloodmoon (also changes 64?)\n    // 141-142 are summon effects introduced in bloodmoon, but not used\n    // there. They can be redefined in mods by setting the name in GMST\n    // sEffectSummonCreature04/05 creature id in\n    // sMagicCreature04ID/05ID.\n    int mIndex;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n     /// Set record to default state (does not touch the ID/index).\n    void blank();\n\n    enum Effects\n    {\n        WaterBreathing = 0,\n        SwiftSwim = 1,\n        WaterWalking = 2,\n        Shield = 3,\n        FireShield = 4,\n        LightningShield = 5,\n        FrostShield = 6,\n        Burden = 7,\n        Feather = 8,\n        Jump = 9,\n        Levitate = 10,\n        SlowFall = 11,\n        Lock = 12,\n        Open = 13,\n        FireDamage = 14,\n        ShockDamage = 15,\n        FrostDamage = 16,\n        DrainAttribute = 17,\n        DrainHealth = 18,\n        DrainMagicka = 19,\n        DrainFatigue = 20,\n        DrainSkill = 21,\n        DamageAttribute = 22,\n        DamageHealth = 23,\n        DamageMagicka = 24,\n        DamageFatigue = 25,\n        DamageSkill = 26,\n        Poison = 27,\n        WeaknessToFire = 28,\n        WeaknessToFrost = 29,\n        WeaknessToShock = 30,\n        WeaknessToMagicka = 31,\n        WeaknessToCommonDisease = 32,\n        WeaknessToBlightDisease = 33,\n        WeaknessToCorprusDisease = 34,\n        WeaknessToPoison = 35,\n        WeaknessToNormalWeapons = 36,\n        DisintegrateWeapon = 37,\n        DisintegrateArmor = 38,\n        Invisibility = 39,\n        Chameleon = 40,\n        Light = 41,\n        Sanctuary = 42,\n        NightEye = 43,\n        Charm = 44,\n        Paralyze = 45,\n        Silence = 46,\n        Blind = 47,\n        Sound = 48,\n        CalmHumanoid = 49,\n        CalmCreature = 50,\n        FrenzyHumanoid = 51,\n        FrenzyCreature = 52,\n        DemoralizeHumanoid = 53,\n        DemoralizeCreature = 54,\n        RallyHumanoid = 55,\n        RallyCreature = 56,\n        Dispel = 57,\n        Soultrap = 58,\n        Telekinesis = 59,\n        Mark = 60,\n        Recall = 61,\n        DivineIntervention = 62,\n        AlmsiviIntervention = 63,\n        DetectAnimal = 64,\n        DetectEnchantment = 65,\n        DetectKey = 66,\n        SpellAbsorption = 67,\n        Reflect = 68,\n        CureCommonDisease = 69,\n        CureBlightDisease = 70,\n        CureCorprusDisease = 71,\n        CurePoison = 72,\n        CureParalyzation = 73,\n        RestoreAttribute = 74,\n        RestoreHealth = 75,\n        RestoreMagicka = 76,\n        RestoreFatigue = 77,\n        RestoreSkill = 78,\n        FortifyAttribute = 79,\n        FortifyHealth = 80,\n        FortifyMagicka= 81,\n        FortifyFatigue = 82,\n        FortifySkill = 83,\n        FortifyMaximumMagicka = 84,\n        AbsorbAttribute = 85,\n        AbsorbHealth = 86,\n        AbsorbMagicka = 87,\n        AbsorbFatigue = 88,\n        AbsorbSkill = 89,\n        ResistFire = 90,\n        ResistFrost = 91,\n        ResistShock = 92,\n        ResistMagicka = 93,\n        ResistCommonDisease = 94,\n        ResistBlightDisease = 95,\n        ResistCorprusDisease = 96,\n        ResistPoison = 97,\n        ResistNormalWeapons = 98,\n        ResistParalysis = 99,\n        RemoveCurse = 100,\n        TurnUndead = 101,\n        SummonScamp = 102,\n        SummonClannfear = 103,\n        SummonDaedroth = 104,\n        SummonDremora = 105,\n        SummonAncestralGhost = 106,\n        SummonSkeletalMinion = 107,\n        SummonBonewalker = 108,\n        SummonGreaterBonewalker = 109,\n        SummonBonelord = 110,\n        SummonWingedTwilight = 111,\n        SummonHunger = 112,\n        SummonGoldenSaint = 113,\n        SummonFlameAtronach = 114,\n        SummonFrostAtronach = 115,\n        SummonStormAtronach = 116,\n        FortifyAttack = 117,\n        CommandCreature = 118,\n        CommandHumanoid = 119,\n        BoundDagger = 120,\n        BoundLongsword = 121,\n        BoundMace = 122,\n        BoundBattleAxe = 123,\n        BoundSpear = 124,\n        BoundLongbow = 125,\n        ExtraSpell = 126,\n        BoundCuirass = 127,\n        BoundHelm = 128,\n        BoundBoots = 129,\n        BoundShield = 130,\n        BoundGloves = 131,\n        Corprus = 132,\n        Vampirism = 133,\n        SummonCenturionSphere = 134,\n        SunDamage = 135,\n        StuntedMagicka = 136,\n\n        // Tribunal only\n        SummonFabricant = 137,\n\n        // Bloodmoon only\n        SummonWolf = 138,\n        SummonBear = 139,\n        SummonBonewolf = 140,\n        SummonCreature04 = 141,\n        SummonCreature05 = 142,\n\n        Length\n    };\n\n    static std::string indexToId (int index);\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadmisc.cpp",
    "content": "#include \"loadmisc.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Miscellaneous::sRecordId = REC_MISC;\n\n    void Miscellaneous::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'M','C','D','T'>::value:\n                    esm.getHT(mData, 12);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing MCDT subrecord\");\n    }\n\n    void Miscellaneous::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"MCDT\", mData, 12);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n    }\n\n    void Miscellaneous::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mIsKey = 0;\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadmisc.hpp",
    "content": "#ifndef OPENMW_ESM_MISC_H\n#define OPENMW_ESM_MISC_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Misc inventory items, basically things that have no use but can be\n * carried, bought and sold. It also includes keys.\n */\n\nstruct Miscellaneous\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Miscellaneous\"; }\n\n    struct MCDTstruct\n    {\n        float mWeight;\n        int mValue;\n        int mIsKey; // There are many keys in Morrowind.esm that has this\n                   // set to 0. TODO: Check what this field corresponds to\n                   // in the editor.\n    };\n    MCDTstruct mData;\n\n    std::string mId, mName, mModel, mIcon, mScript;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadnpc.cpp",
    "content": "#include \"loadnpc.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int NPC::sRecordId = REC_NPC_;\n\n    void NPC::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mPersistent = (esm.getRecordFlags() & 0x0400) != 0;\n\n        mSpells.mList.clear();\n        mInventory.mList.clear();\n        mTransport.mList.clear();\n        mAiPackage.mList.clear();\n        mAiData.blank();\n        mAiData.mHello = mAiData.mFight = mAiData.mFlee = 30;\n\n        bool hasName = false;\n        bool hasNpdt = false;\n        bool hasFlags = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'R','N','A','M'>::value:\n                    mRace = esm.getHString();\n                    break;\n                case ESM::FourCC<'C','N','A','M'>::value:\n                    mClass = esm.getHString();\n                    break;\n                case ESM::FourCC<'A','N','A','M'>::value:\n                    mFaction = esm.getHString();\n                    break;\n                case ESM::FourCC<'B','N','A','M'>::value:\n                    mHead = esm.getHString();\n                    break;\n                case ESM::FourCC<'K','N','A','M'>::value:\n                    mHair = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'N','P','D','T'>::value:\n                    hasNpdt = true;\n                    esm.getSubHeader();\n                    if (esm.getSubSize() == 52)\n                    {\n                        mNpdtType = NPC_DEFAULT;\n                        esm.getExact(&mNpdt, 52);\n                    }\n                    else if (esm.getSubSize() == 12)\n                    {\n                        //Reading into temporary NPDTstruct12 object\n                        NPDTstruct12 npdt12;\n                        mNpdtType = NPC_WITH_AUTOCALCULATED_STATS;\n                        esm.getExact(&npdt12, 12);\n\n                        //Clearing the mNdpt struct to initialize all values\n                        blankNpdt();\n                        //Swiching to an internal representation\n                        mNpdt.mLevel = npdt12.mLevel;\n                        mNpdt.mDisposition = npdt12.mDisposition;\n                        mNpdt.mReputation = npdt12.mReputation;\n                        mNpdt.mRank = npdt12.mRank;\n                        mNpdt.mGold = npdt12.mGold;\n                    }\n                    else\n                        esm.fail(\"NPC_NPDT must be 12 or 52 bytes long\");\n                    break;\n                case ESM::FourCC<'F','L','A','G'>::value:\n                    hasFlags = true;\n                    int flags;\n                    esm.getHT(flags);\n                    mFlags = flags & 0xFF;\n                    mBloodType = ((flags >> 8) & 0xFF) >> 2;\n                    break;\n                case ESM::FourCC<'N','P','C','S'>::value:\n                    mSpells.add(esm);\n                    break;\n                case ESM::FourCC<'N','P','C','O'>::value:\n                    mInventory.add(esm);\n                    break;\n                case ESM::FourCC<'A','I','D','T'>::value:\n                    esm.getHExact(&mAiData, sizeof(mAiData));\n                    break;\n                case ESM::FourCC<'D','O','D','T'>::value:\n                case ESM::FourCC<'D','N','A','M'>::value:\n                    mTransport.add(esm);\n                    break;\n                case AI_Wander:\n                case AI_Activate:\n                case AI_Escort:\n                case AI_Follow:\n                case AI_Travel:\n                case AI_CNDT:\n                    mAiPackage.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasNpdt && !isDeleted)\n            esm.fail(\"Missing NPDT subrecord\");\n        if (!hasFlags && !isDeleted)\n            esm.fail(\"Missing FLAG subrecord\");\n    }\n    void NPC::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNOCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNCString(\"RNAM\", mRace);\n        esm.writeHNCString(\"CNAM\", mClass);\n        esm.writeHNCString(\"ANAM\", mFaction);\n        esm.writeHNCString(\"BNAM\", mHead);\n        esm.writeHNCString(\"KNAM\", mHair);\n        esm.writeHNOCString(\"SCRI\", mScript);\n\n        if (mNpdtType == NPC_DEFAULT)\n        {\n            esm.writeHNT(\"NPDT\", mNpdt, 52);\n        }\n        else if (mNpdtType == NPC_WITH_AUTOCALCULATED_STATS)\n        {\n            NPDTstruct12 npdt12;\n            npdt12.mLevel = mNpdt.mLevel;\n            npdt12.mDisposition = mNpdt.mDisposition;\n            npdt12.mReputation = mNpdt.mReputation;\n            npdt12.mRank = mNpdt.mRank;\n            npdt12.mGold = mNpdt.mGold;\n            esm.writeHNT(\"NPDT\", npdt12, 12);\n        }\n\n        esm.writeHNT(\"FLAG\", ((mBloodType << 10) + mFlags));\n\n        mInventory.save(esm);\n        mSpells.save(esm);\n        esm.writeHNT(\"AIDT\", mAiData, sizeof(mAiData));\n\n        mTransport.save(esm);\n\n        mAiPackage.save(esm);\n    }\n\n    bool NPC::isMale() const {\n        return (mFlags & Female) == 0;\n    }\n\n    void NPC::setIsMale(bool value) {\n        mFlags |= Female;\n        if (value) {\n            mFlags ^= Female;\n        }\n    }\n\n    void NPC::blank()\n    {\n        mNpdtType = NPC_DEFAULT;\n        blankNpdt();\n        mBloodType = 0;\n        mFlags = 0;\n        mInventory.mList.clear();\n        mSpells.mList.clear();\n        mAiData.blank();\n        mAiData.mHello = mAiData.mFight = mAiData.mFlee = 30;\n        mTransport.mList.clear();\n        mAiPackage.mList.clear();\n        mName.clear();\n        mModel.clear();\n        mRace.clear();\n        mClass.clear();\n        mFaction.clear();\n        mScript.clear();\n        mHair.clear();\n        mHead.clear();\n    }\n\n    void NPC::blankNpdt()\n    {\n        mNpdt.mLevel = 0;\n        mNpdt.mStrength = mNpdt.mIntelligence = mNpdt.mWillpower = mNpdt.mAgility =\n            mNpdt.mSpeed = mNpdt.mEndurance = mNpdt.mPersonality = mNpdt.mLuck = 0;\n        for (int i=0; i< Skill::Length; ++i) mNpdt.mSkills[i] = 0;\n        mNpdt.mReputation = 0;\n        mNpdt.mHealth = mNpdt.mMana = mNpdt.mFatigue = 0;\n        mNpdt.mDisposition = 0;\n        mNpdt.mUnknown1 = 0;\n        mNpdt.mRank = 0;\n        mNpdt.mUnknown2 = 0;\n        mNpdt.mGold = 0;\n    }\n\n    int NPC::getFactionRank() const\n    {\n        if (mFaction.empty())\n            return -1;\n        else\n            return mNpdt.mRank;\n    }\n\n    const std::vector<Transport::Dest>& NPC::getTransport() const\n    {\n        return mTransport.mList;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadnpc.hpp",
    "content": "#ifndef OPENMW_ESM_NPC_H\n#define OPENMW_ESM_NPC_H\n\n#include <string>\n#include <vector>\n\n#include \"defs.hpp\"\n#include \"loadcont.hpp\"\n#include \"aipackage.hpp\"\n#include \"spelllist.hpp\"\n#include \"loadskil.hpp\"\n#include \"transport.hpp\"\n\nnamespace ESM {\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * NPC definition\n */\n\nstruct NPC\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"NPC\"; }\n\n  // Services\n  enum Services\n    {\n      // This merchant buys:\n      Weapon        = 0x00001,\n      Armor         = 0x00002,\n      Clothing      = 0x00004,\n      Books         = 0x00008,\n      Ingredients   = 0x00010,\n      Picks         = 0x00020,\n      Probes        = 0x00040,\n      Lights        = 0x00080,\n      Apparatus     = 0x00100,\n      RepairItem    = 0x00200,\n      Misc          = 0x00400,\n      Potions       = 0x02000,\n\n      AllItems = Weapon|Armor|Clothing|Books|Ingredients|Picks|Probes|Lights|Apparatus|RepairItem|Misc|Potions,\n\n      // Other services\n      Spells        = 0x00800,\n      MagicItems    = 0x01000,\n      Training      = 0x04000,\n      Spellmaking   = 0x08000,\n      Enchanting    = 0x10000,\n      Repair        = 0x20000\n    };\n\n  enum Flags\n    {\n      Female        = 0x01,\n      Essential     = 0x02,\n      Respawn       = 0x04,\n      Base          = 0x08,\n      Autocalc      = 0x10\n    };\n\n  enum NpcType\n  {\n    NPC_WITH_AUTOCALCULATED_STATS = 12,\n    NPC_DEFAULT = 52\n  };\n\n    #pragma pack(push)\n    #pragma pack(1)\n\n    struct NPDTstruct52\n    {\n        short mLevel;\n        unsigned char mStrength,\n             mIntelligence,\n             mWillpower,\n             mAgility,\n             mSpeed,\n             mEndurance,\n             mPersonality,\n             mLuck;\n\n        // mSkill can grow up to 200, it must be unsigned\n        unsigned char mSkills[Skill::Length];\n\n        char mUnknown1;\n        unsigned short mHealth, mMana, mFatigue;\n        unsigned char mDisposition, mReputation, mRank;\n        char mUnknown2;\n        int mGold;\n    }; // 52 bytes\n\n    //Structure for autocalculated characters.\n    // This is only used for load and save operations.\n    struct NPDTstruct12\n    {\n        short mLevel;\n        // see above\n        unsigned char mDisposition, mReputation, mRank;\n        char mUnknown1, mUnknown2, mUnknown3;\n        int mGold;\n    }; // 12 bytes\n    #pragma pack(pop)\n\n    unsigned char mNpdtType;\n    //Worth noting when saving the struct:\n    // Although we might read a NPDTstruct12 in, we use NPDTstruct52 internally\n    NPDTstruct52 mNpdt;\n\n    int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank\n\n    int mBloodType;\n    unsigned char mFlags;\n\n    bool mPersistent;\n\n    InventoryList mInventory;\n    SpellList mSpells;\n\n    AIData mAiData;\n\n    Transport mTransport;\n\n    const std::vector<Transport::Dest>& getTransport() const;\n\n    AIPackageList     mAiPackage;\n\n    std::string mId, mName, mModel, mRace, mClass, mFaction, mScript;\n\n    // body parts\n    std::string mHair, mHead;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    bool isMale() const;\n\n    void setIsMale(bool value);\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n\n    /// Resets the mNpdt object\n    void blankNpdt();\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadpgrd.cpp",
    "content": "#include \"loadpgrd.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Pathgrid::sRecordId = REC_PGRD;\n\n    Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[3])\n    {\n        mX = static_cast<int>(rhs[0]);\n        mY = static_cast<int>(rhs[1]);\n        mZ = static_cast<int>(rhs[2]);\n        mAutogenerated = 0;\n        mConnectionNum = 0;\n        mUnknown = 0;\n        return *this;\n    }\n    Pathgrid::Point::Point(const float rhs[3])\n    : mX(static_cast<int>(rhs[0])),\n      mY(static_cast<int>(rhs[1])),\n      mZ(static_cast<int>(rhs[2])),\n      mAutogenerated(0),\n      mConnectionNum(0),\n      mUnknown(0)\n    {\n    }\n    Pathgrid::Point::Point():mX(0),mY(0),mZ(0),mAutogenerated(0),\n                             mConnectionNum(0),mUnknown(0)\n    {\n    }\n\n    void Pathgrid::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mPoints.clear();\n        mEdges.clear();\n\n        // keep track of total connections so we can reserve edge vector size\n        int edgeCount = 0;\n\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mCell = esm.getHString();\n                    break;\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    esm.getHT(mData, 12);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'P','G','R','P'>::value:\n                {\n                    esm.getSubHeader();\n                    int size = esm.getSubSize();\n                    // Check that the sizes match up. Size = 16 * s2 (path points)\n                    if (size != static_cast<int> (sizeof(Point) * mData.mS2))\n                        esm.fail(\"Path point subrecord size mismatch\");\n                    else\n                    {\n                        int pointCount = mData.mS2;\n                        mPoints.reserve(pointCount);\n                        for (int i = 0; i < pointCount; ++i)\n                        {\n                            Point p;\n                            esm.getExact(&p, sizeof(Point));\n                            mPoints.push_back(p);\n                            edgeCount += p.mConnectionNum;\n                        }\n                    }\n                    break;\n                }\n                case ESM::FourCC<'P','G','R','C'>::value:\n                {\n                    esm.getSubHeader();\n                    int size = esm.getSubSize();\n                    if (size % sizeof(int) != 0)\n                        esm.fail(\"PGRC size not a multiple of 4\");\n                    else\n                    {\n                        int rawConnNum = size / sizeof(int);\n                        std::vector<int> rawConnections;\n                        rawConnections.reserve(rawConnNum);\n                        for (int i = 0; i < rawConnNum; ++i)\n                        {\n                            int currentValue;\n                            esm.getT(currentValue);\n                            rawConnections.push_back(currentValue);\n                        }\n\n                        std::vector<int>::const_iterator rawIt = rawConnections.begin();\n                        int pointIndex = 0;\n                        mEdges.reserve(edgeCount);\n                        for(PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it, ++pointIndex)\n                        {\n                            unsigned char connectionNum = (*it).mConnectionNum;\n                            if (rawConnections.end() - rawIt < connectionNum)\n                                esm.fail(\"Not enough connections\");\n                            for (int i = 0; i < connectionNum; ++i) {\n                                Edge edge;\n                                edge.mV0 = pointIndex;\n                                edge.mV1 = *rawIt;\n                                ++rawIt;\n                                mEdges.push_back(edge);\n                            }\n                        }\n                    }\n                    break;\n                }\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasData)\n            esm.fail(\"Missing DATA subrecord\");\n    }\n\n    void Pathgrid::save(ESMWriter &esm, bool isDeleted) const\n    {\n        // Correct connection count and sort edges by point\n        // Can probably be optimized\n        PointList correctedPoints = mPoints;\n        std::vector<int> sortedEdges;\n\n        sortedEdges.reserve(mEdges.size());\n\n        for (size_t point = 0; point < correctedPoints.size(); ++point)\n        {\n            correctedPoints[point].mConnectionNum = 0;\n\n            for (EdgeList::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it)\n            {\n                if (static_cast<size_t>(it->mV0) == point)\n                {\n                    sortedEdges.push_back(it->mV1);\n                    ++correctedPoints[point].mConnectionNum;\n                }\n            }\n        }\n\n        // Save\n        esm.writeHNCString(\"NAME\", mCell);\n        esm.writeHNT(\"DATA\", mData, 12);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        if (!correctedPoints.empty())\n        {\n            esm.startSubRecord(\"PGRP\");\n            for (PointList::const_iterator it = correctedPoints.begin(); it != correctedPoints.end(); ++it)\n            {\n                esm.writeT(*it);\n            }\n            esm.endRecord(\"PGRP\");\n        }\n\n        if (!sortedEdges.empty())\n        {\n            esm.startSubRecord(\"PGRC\");\n            for (std::vector<int>::const_iterator it = sortedEdges.begin(); it != sortedEdges.end(); ++it)\n            {\n                esm.writeT(*it);\n            }\n            esm.endRecord(\"PGRC\");\n        }\n    }\n\n    void Pathgrid::blank()\n    {\n        mCell.clear();\n        mData.mX = 0;\n        mData.mY = 0;\n        mData.mS1 = 0;\n        mData.mS2 = 0;\n        mPoints.clear();\n        mEdges.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadpgrd.hpp",
    "content": "#ifndef OPENMW_ESM_PGRD_H\n#define OPENMW_ESM_PGRD_H\n\n#include <string>\n#include <vector>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Path grid.\n */\nstruct Pathgrid\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Pathgrid\"; }\n\n    struct DATAstruct\n    {\n        int mX, mY; // Grid location, matches cell for exterior cells\n        short mS1; // ?? Usually but not always a power of 2. Doesn't seem\n                  // to have any relation to the size of PGRC.\n        short mS2; // Number of path points.\n    }; // 12 bytes\n\n    struct Point // path grid point\n    {\n        int mX, mY, mZ; // Location of point\n        unsigned char mAutogenerated; // autogenerated vs. user coloring flag?\n        unsigned char mConnectionNum; // number of connections for this point\n        short mUnknown;\n        Point& operator=(const float[3]);\n        Point(const float[3]);\n        Point();\n        Point(int x, int y, int z)\n            : mX(x), mY(y), mZ(z)\n            , mAutogenerated(0), mConnectionNum(0), mUnknown(0)\n        {}\n    }; // 16 bytes\n\n    struct Edge // path grid edge\n    {\n        int mV0, mV1; // index of points connected with this edge\n    }; // 8 bytes\n\n    std::string mCell; // Cell name\n    DATAstruct mData;\n\n    typedef std::vector<Point> PointList;\n    PointList mPoints;\n\n    typedef std::vector<Edge> EdgeList;\n    EdgeList mEdges;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadprob.cpp",
    "content": "#include \"loadprob.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Probe::sRecordId = REC_PROB;\n\n    void Probe::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'P','B','D','T'>::value:\n                    esm.getHT(mData, 16);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing PBDT subrecord\");\n    }\n\n    void Probe::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n\n        esm.writeHNT(\"PBDT\", mData, 16);\n        esm.writeHNOString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n    }\n\n    void Probe::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mQuality = 0;\n        mData.mUses = 0;\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadprob.hpp",
    "content": "#ifndef OPENMW_ESM_PROBE_H\n#define OPENMW_ESM_PROBE_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct Probe\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Probe\"; }\n\n    struct Data\n    {\n        float mWeight;\n        int mValue;\n\n        float mQuality;\n        int mUses;\n    }; // Size = 16\n\n    Data mData;\n    std::string mId, mName, mModel, mIcon, mScript;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadrace.cpp",
    "content": "#include \"loadrace.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Race::sRecordId = REC_RACE;\n\n    int Race::MaleFemale::getValue (bool male) const\n    {\n        return male ? mMale : mFemale;\n    }\n\n    int Race::MaleFemaleF::getValue (bool male) const\n    {\n        return static_cast<int>(male ? mMale : mFemale);\n    }\n\n    void Race::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mPowers.mList.clear();\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'R','A','D','T'>::value:\n                    esm.getHT(mData, 140);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'D','E','S','C'>::value:\n                    mDescription = esm.getHString();\n                    break;\n                case ESM::FourCC<'N','P','C','S'>::value:\n                    mPowers.add(esm);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing RADT subrecord\");\n    }\n    void Race::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"RADT\", mData, 140);\n        mPowers.save(esm);\n        esm.writeHNOString(\"DESC\", mDescription);\n    }\n\n    void Race::blank()\n    {\n        mName.clear();\n        mDescription.clear();\n\n        mPowers.mList.clear();\n\n        for (int i=0; i<7; ++i)\n        {\n            mData.mBonus[i].mSkill = -1;\n            mData.mBonus[i].mBonus = 0;\n        }\n\n        for (int i=0; i<8; ++i)\n            mData.mAttributeValues[i].mMale = mData.mAttributeValues[i].mFemale = 1;\n\n        mData.mHeight.mMale = mData.mHeight.mFemale = 1;\n        mData.mWeight.mMale = mData.mWeight.mFemale = 1;\n\n        mData.mFlags = 0;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadrace.hpp",
    "content": "#ifndef OPENMW_ESM_RACE_H\n#define OPENMW_ESM_RACE_H\n\n#include <string>\n\n#include \"spelllist.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Race definition\n */\n\nstruct Race\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Race\"; }\n\n    struct SkillBonus\n    {\n        int mSkill; // SkillEnum\n        int mBonus;\n    };\n\n    struct MaleFemale\n    {\n        int mMale, mFemale;\n\n        int getValue (bool male) const;\n    };\n\n    struct MaleFemaleF\n    {\n        float mMale, mFemale;\n\n        int getValue (bool male) const;\n    };\n\n    enum Flags\n    {\n        Playable = 0x01,\n        Beast = 0x02\n    };\n\n    struct RADTstruct\n    {\n        // List of skills that get a bonus\n        SkillBonus mBonus[7];\n\n        // Attribute values for male/female\n        MaleFemale mAttributeValues[8];\n\n        // The actual eye level height (in game units) is (probably) given\n        // as 'height' times 128. This has not been tested yet.\n        MaleFemaleF mHeight, mWeight;\n\n        int mFlags; // 0x1 - playable, 0x2 - beast race\n\n    }; // Size = 140 bytes\n\n    RADTstruct mData;\n\n    std::string mId, mName, mDescription;\n    SpellList mPowers;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID/index).\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadregn.cpp",
    "content": "#include \"loadregn.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Region::sRecordId = REC_REGN;\n\n    void Region::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'W','E','A','T'>::value:\n                {\n                    esm.getSubHeader();\n                    if (esm.getVer() == VER_12)\n                    {\n                        mData.mA = 0;\n                        mData.mB = 0;\n                        esm.getExact(&mData, sizeof(mData) - 2);\n                    }\n                    else if (esm.getVer() == VER_13)\n                    {\n                        // May include the additional two bytes (but not necessarily)\n                        if (esm.getSubSize() == sizeof(mData))\n                        {\n                            esm.getExact(&mData, sizeof(mData));\n                        }\n                        else\n                        {\n                            mData.mA = 0;\n                            mData.mB = 0;\n                            esm.getExact(&mData, sizeof(mData)-2);\n                        }\n                    }\n                    else\n                    {\n                        esm.fail(\"Don't know what to do in this version\");\n                    }\n                    break;\n                }\n                case ESM::FourCC<'B','N','A','M'>::value:\n                    mSleepList = esm.getHString();\n                    break;\n                case ESM::FourCC<'C','N','A','M'>::value:\n                    esm.getHT(mMapColor);\n                    break;\n                case ESM::FourCC<'S','N','A','M'>::value:\n                {\n                    esm.getSubHeader();\n                    SoundRef sr;\n                    sr.mSound.assign(esm.getString(32));\n                    esm.getT(sr.mChance);\n                    mSoundList.push_back(sr);\n                    break;\n                }\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n    }\n\n    void Region::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNOCString(\"FNAM\", mName);\n\n        if (esm.getVersion() == VER_12)\n            esm.writeHNT(\"WEAT\", mData, sizeof(mData) - 2);\n        else\n            esm.writeHNT(\"WEAT\", mData);\n\n        esm.writeHNOCString(\"BNAM\", mSleepList);\n\n        esm.writeHNT(\"CNAM\", mMapColor);\n        for (std::vector<SoundRef>::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it)\n        {\n            esm.startSubRecord(\"SNAM\");\n            esm.writeFixedSizeString(it->mSound, 32);\n            esm.writeT(it->mChance);\n            esm.endRecord(\"NPCO\");\n        }\n    }\n\n    void Region::blank()\n    {\n        mData.mClear = mData.mCloudy = mData.mFoggy = mData.mOvercast = mData.mRain =\n            mData.mThunder = mData.mAsh = mData.mBlight = mData.mA = mData.mB = 0;\n\n        mMapColor = 0;\n\n        mName.clear();\n        mSleepList.clear();\n        mSoundList.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadregn.hpp",
    "content": "#ifndef OPENMW_ESM_REGN_H\n#define OPENMW_ESM_REGN_H\n\n#include <string>\n#include <vector>\n\n#include \"esmcommon.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Region data\n */\n\nstruct Region\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Region\"; }\n\n#pragma pack(push)\n#pragma pack(1)\n    struct WEATstruct\n    {\n        // These are probabilities that add up to 100\n        unsigned char mClear, mCloudy, mFoggy, mOvercast, mRain, mThunder, mAsh, mBlight,\n        // Unknown weather, probably snow and something. Only\n        // present in file version 1.3.\n        // the engine uses mA as \"snow\" and mB as \"blizard\"\n                mA, mB;\n    }; // 10 bytes\n#pragma pack(pop)\n\n    // Reference to a sound that is played randomly in this region\n    struct SoundRef\n    {\n        std::string   mSound;\n        unsigned char mChance;\n    };\n\n    WEATstruct mData;\n    int mMapColor; // RGBA\n\n    // sleepList refers to a leveled list of creatures you can meet if\n    // you sleep outside in this region.\n    std::string mId, mName, mSleepList;\n\n    std::vector<SoundRef> mSoundList;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID/index).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadrepa.cpp",
    "content": "#include \"loadrepa.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Repair::sRecordId = REC_REPA;\n\n    void Repair::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'R','I','D','T'>::value:\n                    esm.getHT(mData, 16);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing RIDT subrecord\");\n    }\n\n    void Repair::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n\n        esm.writeHNT(\"RIDT\", mData, 16);\n        esm.writeHNOString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n    }\n\n    void Repair::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mQuality = 0;\n        mData.mUses = 0;\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mScript.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadrepa.hpp",
    "content": "#ifndef OPENMW_ESM_REPA_H\n#define OPENMW_ESM_REPA_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct Repair\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Repair\"; }\n\n    struct Data\n    {\n        float mWeight;\n        int mValue;\n\n        int mUses;\n        float mQuality;\n    }; // Size = 16\n\n    Data mData;\n    std::string mId, mName, mModel, mIcon, mScript;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadscpt.cpp",
    "content": "#include \"loadscpt.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Script::sRecordId = REC_SCPT;\n\n    void Script::loadSCVR(ESMReader &esm)\n    {\n        int s = mData.mStringTableSize;\n\n        std::vector<char> tmp (s);\n        // not using getHExact, vanilla doesn't seem to mind unused bytes at the end\n        esm.getSubHeader();\n        int left = esm.getSubSize();\n        if (left < s)\n            esm.fail(\"SCVR string list is smaller than specified\");\n        esm.getExact(tmp.data(), s);\n        if (left > s)\n            esm.skip(left-s); // skip the leftover junk\n\n        // Set up the list of variable names\n        mVarNames.resize(mData.mNumShorts + mData.mNumLongs + mData.mNumFloats);\n\n        // The tmp buffer is a null-byte separated string list, we\n        // just have to pick out one string at a time.\n        char* str = tmp.data();\n        if (tmp.empty())\n        {\n            if (mVarNames.size() > 0)\n                Log(Debug::Warning) << \"SCVR with no variable names\";\n\n            return;\n        }\n\n        // Support '\\r' terminated strings like vanilla.  See Bug #1324.\n        std::replace(tmp.begin(), tmp.end(), '\\r', '\\0');\n        // Avoid heap corruption\n        if (tmp.back() != '\\0')\n        {\n            tmp.emplace_back('\\0');\n            std::stringstream ss;\n            ss << \"Malformed string table\";\n            ss << \"\\n  File: \" << esm.getName();\n            ss << \"\\n  Record: \" << esm.getContext().recName.toString();\n            ss << \"\\n  Subrecord: \" << \"SCVR\";\n            ss << \"\\n  Offset: 0x\" << std::hex << esm.getFileOffset();\n            Log(Debug::Verbose) << ss.str();\n            str = tmp.data();\n        }\n\n        const auto tmpEnd = tmp.data() + tmp.size();\n        for (size_t i = 0; i < mVarNames.size(); i++)\n        {\n            mVarNames[i] = std::string(str);\n            str += mVarNames[i].size() + 1;\n            if (str >= tmpEnd)\n            {\n                if(str > tmpEnd)\n                {\n                    // SCVR subrecord is unused and variable names are determined\n                    // from the script source, so an overflow is not fatal.\n                    std::stringstream ss;\n                    ss << \"String table overflow\";\n                    ss << \"\\n  File: \" << esm.getName();\n                    ss << \"\\n  Record: \" << esm.getContext().recName.toString();\n                    ss << \"\\n  Subrecord: \" << \"SCVR\";\n                    ss << \"\\n  Offset: 0x\" << std::hex << esm.getFileOffset();\n                    Log(Debug::Verbose) << ss.str();\n                }\n                // Get rid of empty strings in the list.\n                mVarNames.resize(i+1);\n                break;\n            }\n        }\n    }\n\n    void Script::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mVarNames.clear();\n\n        bool hasHeader = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::FourCC<'S','C','H','D'>::value:\n                {\n                    esm.getSubHeader();\n                    mId = esm.getString(32);\n                    esm.getT(mData);\n\n                    hasHeader = true;\n                    break;\n                }\n                case ESM::FourCC<'S','C','V','R'>::value:\n                    // list of local variables\n                    loadSCVR(esm);\n                    break;\n                case ESM::FourCC<'S','C','D','T'>::value:\n                {\n                    // compiled script\n                    esm.getSubHeader();\n                    uint32_t subSize = esm.getSubSize();\n\n                    if (subSize != static_cast<uint32_t>(mData.mScriptDataSize))\n                    {\n                        std::stringstream ss;\n                        ss << \"Script data size defined in SCHD subrecord does not match size of SCDT subrecord\";\n                        ss << \"\\n  File: \" << esm.getName();\n                        ss << \"\\n  Offset: 0x\" << std::hex << esm.getFileOffset();\n                        Log(Debug::Verbose) << ss.str();\n                    }\n\n                    mScriptData.resize(subSize);\n                    esm.getExact(mScriptData.data(), mScriptData.size());\n                    break;\n                }\n                case ESM::FourCC<'S','C','T','X'>::value:\n                    mScriptText = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasHeader)\n            esm.fail(\"Missing SCHD subrecord\");\n    }\n\n    void Script::save(ESMWriter &esm, bool isDeleted) const\n    {\n        std::string varNameString;\n        if (!mVarNames.empty())\n            for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)\n                varNameString.append(*it);\n\n        esm.startSubRecord(\"SCHD\");\n        esm.writeFixedSizeString(mId, 32);\n        esm.writeT(mData, 20);\n        esm.endRecord(\"SCHD\");\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        if (!mVarNames.empty())\n        {\n            esm.startSubRecord(\"SCVR\");\n            for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)\n            {\n                esm.writeHCString(*it);\n            }\n            esm.endRecord(\"SCVR\");\n        }\n\n        esm.startSubRecord(\"SCDT\");\n        esm.write(reinterpret_cast<const char *>(mScriptData.data()), mData.mScriptDataSize);\n        esm.endRecord(\"SCDT\");\n\n        esm.writeHNOString(\"SCTX\", mScriptText);\n    }\n\n    void Script::blank()\n    {\n        mData.mNumShorts = mData.mNumLongs = mData.mNumFloats = 0;\n        mData.mScriptDataSize = 0;\n        mData.mStringTableSize = 0;\n\n        mVarNames.clear();\n        mScriptData.clear();\n\n        if (mId.find (\"::\")!=std::string::npos)\n            mScriptText = \"Begin \\\"\" + mId + \"\\\"\\n\\nEnd \" + mId + \"\\n\";\n        else\n            mScriptText = \"Begin \" + mId + \"\\n\\nEnd \" + mId + \"\\n\";\n    }\n\n}\n"
  },
  {
    "path": "components/esm/loadscpt.hpp",
    "content": "#ifndef OPENMW_ESM_SCPT_H\n#define OPENMW_ESM_SCPT_H\n\n#include <string>\n#include <vector>\n\n#include \"esmcommon.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Script definitions\n */\n\nclass Script\n{\npublic:\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Script\"; }\n\n    struct SCHDstruct\n    {\n        /// Data from script-precompling in the editor.\n        /// \\warning Do not use them. OpenCS currently does not precompile scripts.\n        int mNumShorts, mNumLongs, mNumFloats, mScriptDataSize, mStringTableSize;\n    };\n    struct SCHD\n    {\n        std::string         mName;\n        Script::SCHDstruct  mData;\n    };\n\n    std::string mId;\n\n    SCHDstruct mData;\n\n    /// Variable names generated by script-precompiling in the editor.\n    /// \\warning Do not use this field. OpenCS currently does not precompile scripts.\n    std::vector<std::string> mVarNames;\n\n    /// Bytecode generated from script-precompiling in the editor.\n    /// \\warning Do not use this field. OpenCS currently does not precompile scripts.\n    std::vector<unsigned char> mScriptData;\n\n    /// Script source code\n    std::string mScriptText;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID/index).\n\nprivate:\n    void loadSCVR(ESMReader &esm);\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadskil.cpp",
    "content": "#include \"loadskil.hpp\"\n\n#include <sstream>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    const std::string Skill::sSkillNames[Length] = {\n        \"Block\",\n        \"Armorer\",\n        \"Mediumarmor\",\n        \"Heavyarmor\",\n        \"Bluntweapon\",\n        \"Longblade\",\n        \"Axe\",\n        \"Spear\",\n        \"Athletics\",\n        \"Enchant\",\n        \"Destruction\",\n        \"Alteration\",\n        \"Illusion\",\n        \"Conjuration\",\n        \"Mysticism\",\n        \"Restoration\",\n        \"Alchemy\",\n        \"Unarmored\",\n        \"Security\",\n        \"Sneak\",\n        \"Acrobatics\",\n        \"Lightarmor\",\n        \"Shortblade\",\n        \"Marksman\",\n        \"Mercantile\",\n        \"Speechcraft\",\n        \"Handtohand\",\n    };\n    const std::string Skill::sSkillNameIds[Length] = {\n        \"sSkillBlock\",\n        \"sSkillArmorer\",\n        \"sSkillMediumarmor\",\n        \"sSkillHeavyarmor\",\n        \"sSkillBluntweapon\",\n        \"sSkillLongblade\",\n        \"sSkillAxe\",\n        \"sSkillSpear\",\n        \"sSkillAthletics\",\n        \"sSkillEnchant\",\n        \"sSkillDestruction\",\n        \"sSkillAlteration\",\n        \"sSkillIllusion\",\n        \"sSkillConjuration\",\n        \"sSkillMysticism\",\n        \"sSkillRestoration\",\n        \"sSkillAlchemy\",\n        \"sSkillUnarmored\",\n        \"sSkillSecurity\",\n        \"sSkillSneak\",\n        \"sSkillAcrobatics\",\n        \"sSkillLightarmor\",\n        \"sSkillShortblade\",\n        \"sSkillMarksman\",\n        \"sSkillMercantile\",\n        \"sSkillSpeechcraft\",\n        \"sSkillHandtohand\",\n    };\n    const std::string Skill::sIconNames[Length] = {\n        \"combat_block.dds\",\n        \"combat_armor.dds\",\n        \"combat_mediumarmor.dds\",\n        \"combat_heavyarmor.dds\",\n        \"combat_blunt.dds\",\n        \"combat_longblade.dds\",\n        \"combat_axe.dds\",\n        \"combat_spear.dds\",\n        \"combat_athletics.dds\",\n        \"magic_enchant.dds\",\n        \"magic_destruction.dds\",\n        \"magic_alteration.dds\",\n        \"magic_illusion.dds\",\n        \"magic_conjuration.dds\",\n        \"magic_mysticism.dds\",\n        \"magic_restoration.dds\",\n        \"magic_alchemy.dds\",\n        \"magic_unarmored.dds\",\n        \"stealth_security.dds\",\n        \"stealth_sneak.dds\",\n        \"stealth_acrobatics.dds\",\n        \"stealth_lightarmor.dds\",\n        \"stealth_shortblade.dds\",\n        \"stealth_marksman.dds\",\n        \"stealth_mercantile.dds\",\n        \"stealth_speechcraft.dds\",\n        \"stealth_handtohand.dds\",\n    };\n    const std::array<Skill::SkillEnum, Skill::Length> Skill::sSkillIds = {{\n        Block,\n        Armorer,\n        MediumArmor,\n        HeavyArmor,\n        BluntWeapon,\n        LongBlade,\n        Axe,\n        Spear,\n        Athletics,\n        Enchant,\n        Destruction,\n        Alteration,\n        Illusion,\n        Conjuration,\n        Mysticism,\n        Restoration,\n        Alchemy,\n        Unarmored,\n        Security,\n        Sneak,\n        Acrobatics,\n        LightArmor,\n        ShortBlade,\n        Marksman,\n        Mercantile,\n        Speechcraft,\n        HandToHand\n    }};\n\n    unsigned int Skill::sRecordId = REC_SKIL;\n\n    void Skill::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false; // Skill record can't be deleted now (may be changed in the future)\n\n        bool hasIndex = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::FourCC<'I','N','D','X'>::value:\n                    esm.getHT(mIndex);\n                    hasIndex = true;\n                    break;\n                case ESM::FourCC<'S','K','D','T'>::value:\n                    esm.getHT(mData, 24);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'D','E','S','C'>::value:\n                    mDescription = esm.getHString();\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n            }\n        }\n        if (!hasIndex)\n            esm.fail(\"Missing INDX\");\n        if (!hasData)\n            esm.fail(\"Missing SKDT\");\n\n        // create an ID from the index and the name (only used in the editor and likely to change in the\n        // future)\n        mId = indexToId (mIndex);\n    }\n\n    void Skill::save(ESMWriter &esm, bool /*isDeleted*/) const\n    {\n        esm.writeHNT(\"INDX\", mIndex);\n        esm.writeHNT(\"SKDT\", mData, 24);\n        esm.writeHNOString(\"DESC\", mDescription);\n    }\n\n    void Skill::blank()\n    {\n        mData.mAttribute = 0;\n        mData.mSpecialization = 0;\n        mData.mUseValue[0] = mData.mUseValue[1] = mData.mUseValue[2] = mData.mUseValue[3] = 1.0;\n        mDescription.clear();\n    }\n\n    std::string Skill::indexToId (int index)\n    {\n        std::ostringstream stream;\n\n        if (index!=-1)\n        {\n            stream << \"#\";\n\n            if (index<10)\n                stream << \"0\";\n\n            stream << index;\n\n            if (index>=0 && index<Length)\n                stream << sSkillNameIds[index].substr (6);\n        }\n\n        return stream.str();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadskil.hpp",
    "content": "#ifndef OPENMW_ESM_SKIL_H\n#define OPENMW_ESM_SKIL_H\n\n#include <array>\n#include <string>\n\n#include \"defs.hpp\"\n\nnamespace ESM {\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Skill information\n *\n */\n\nstruct Skill\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Skill\"; }\n\n    std::string mId;\n\n  struct SKDTstruct\n  {\n    int mAttribute;     // see defs.hpp\n    int mSpecialization;// 0 - Combat, 1 - Magic, 2 - Stealth\n    float mUseValue[4]; // How much skill improves through use. Meaning\n               // of each field depends on what skill this\n               // is. We should document this better later.\n  }; // Total size: 24 bytes\n  SKDTstruct mData;\n\n  // Skill index. Skils don't have an id (\"NAME\") like most records,\n  // they only have a numerical index that matches one of the\n  // hard-coded skills in the game.\n  int mIndex;\n\n  std::string mDescription;\n\n    enum SkillEnum\n    {\n        Block = 0,\n        Armorer = 1,\n        MediumArmor = 2,\n        HeavyArmor = 3,\n        BluntWeapon = 4,\n        LongBlade = 5,\n        Axe = 6,\n        Spear = 7,\n        Athletics = 8,\n        Enchant = 9,\n        Destruction = 10,\n        Alteration = 11,\n        Illusion = 12,\n        Conjuration = 13,\n        Mysticism = 14,\n        Restoration = 15,\n        Alchemy = 16,\n        Unarmored = 17,\n        Security = 18,\n        Sneak = 19,\n        Acrobatics = 20,\n        LightArmor = 21,\n        ShortBlade = 22,\n        Marksman = 23,\n        Mercantile = 24,\n        Speechcraft = 25,\n        HandToHand = 26,\n        Length\n    };\n  static const std::string sSkillNames[Length];\n  static const std::string sSkillNameIds[Length];\n  static const std::string sIconNames[Length];\n  static const std::array<SkillEnum, Length> sSkillIds;\n\n  void load(ESMReader &esm, bool &isDeleted);\n  void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n     ///< Set record to default state (does not touch the ID/index).\n\n    static std::string indexToId (int index);\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadsndg.cpp",
    "content": "#include \"loadsndg.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int SoundGenerator::sRecordId = REC_SNDG;\n\n    void SoundGenerator::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    esm.getHT(mType, 4);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'C','N','A','M'>::value:\n                    mCreature = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','N','A','M'>::value:\n                    mSound = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing DATA subrecord\");\n    }\n    void SoundGenerator::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNT(\"DATA\", mType, 4);\n        esm.writeHNOCString(\"CNAM\", mCreature);\n        esm.writeHNOCString(\"SNAM\", mSound);\n        \n    }\n\n    void SoundGenerator::blank()\n    {\n        mType = LeftFoot;\n        mCreature.clear();\n        mSound.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadsndg.hpp",
    "content": "#ifndef OPENMW_ESM_SNDG_H\n#define OPENMW_ESM_SNDG_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Sound generator. This describes the sounds a creature make.\n */\n\nstruct SoundGenerator\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"SoundGenerator\"; }\n\n    enum Type\n    {\n        LeftFoot = 0,\n        RightFoot = 1,\n        SwimLeft = 2,\n        SwimRight = 3,\n        Moan = 4,\n        Roar = 5,\n        Scream = 6,\n        Land = 7\n    };\n\n    // Type\n    int mType;\n\n    std::string mId, mCreature, mSound;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadsoun.cpp",
    "content": "#include \"loadsoun.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Sound::sRecordId = REC_SOUN;\n\n    void Sound::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mSound = esm.getHString();\n                    break;\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    esm.getHT(mData, 3);\n                    hasData = true;\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing DATA subrecord\");\n    }\n\n    void Sound::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNOCString(\"FNAM\", mSound);\n        esm.writeHNT(\"DATA\", mData, 3);\n    }\n\n    void Sound::blank()\n    {\n        mSound.clear();\n\n        mData.mVolume = 128;\n        mData.mMinRange = 0;\n        mData.mMaxRange = 255;\n    }\n}\n"
  },
  {
    "path": "components/esm/loadsoun.hpp",
    "content": "#ifndef OPENMW_ESM_SOUN_H\n#define OPENMW_ESM_SOUN_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct SOUNstruct\n{\n    unsigned char mVolume, mMinRange, mMaxRange;\n};\n\nstruct Sound\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Sound\"; }\n\n    SOUNstruct mData;\n    std::string mId, mSound;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID/index).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadspel.cpp",
    "content": "#include \"loadspel.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Spell::sRecordId = REC_SPEL;\n\n    void Spell::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        mEffects.mList.clear();\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'S','P','D','T'>::value:\n                    esm.getHT(mData, 12);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'E','N','A','M'>::value:\n                    ENAMstruct s;\n                    esm.getHT(s, 24);\n                    mEffects.mList.push_back(s);\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing SPDT subrecord\");\n    }\n\n    void Spell::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"SPDT\", mData, 12);\n        mEffects.save(esm);\n    }\n\n    void Spell::blank()\n    {\n        mData.mType = 0;\n        mData.mCost = 0;\n        mData.mFlags = 0;\n\n        mName.clear();\n        mEffects.mList.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadspel.hpp",
    "content": "#ifndef OPENMW_ESM_SPEL_H\n#define OPENMW_ESM_SPEL_H\n\n#include <string>\n\n#include \"effectlist.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\nstruct Spell\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Spell\"; }\n\n    enum SpellType\n    {\n        ST_Spell = 0,   // Normal spell, must be cast and costs mana\n        ST_Ability = 1, // Inert ability, always in effect\n        ST_Blight = 2,  // Blight disease\n        ST_Disease = 3, // Common disease\n        ST_Curse = 4,   // Curse (?)\n        ST_Power = 5    // Power, can use once a day\n    };\n\n    enum Flags\n    {\n        F_Autocalc = 1, // Can be selected by NPC spells auto-calc\n        F_PCStart = 2, // Can be selected by player spells auto-calc\n        F_Always = 4 // Casting always succeeds\n    };\n\n    struct SPDTstruct\n    {\n        int mType; // SpellType\n        int mCost; // Mana cost\n        int mFlags; // Flags\n    };\n\n    SPDTstruct mData;\n    std::string mId, mName;\n    EffectList mEffects;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID/index).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadsscr.cpp",
    "content": "#include \"loadsscr.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int StartScript::sRecordId = REC_SSCR;\n\n    void StartScript::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasData = false;\n        bool hasName = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'D','A','T','A'>::value:\n                    mData = esm.getHString();\n                    hasData = true;\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing DATA\");\n    }\n    void StartScript::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n        }\n        else\n        {\n            esm.writeHNString(\"DATA\", mData);\n        }\n    }\n\n    void StartScript::blank()\n    {\n        mData.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadsscr.hpp",
    "content": "#ifndef OPENMW_ESM_SSCR_H\n#define OPENMW_ESM_SSCR_H\n\n#include <string>\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n Startup script. I think this is simply a 'main' script that is run\n from the begining. The SSCR records contain a DATA identifier which\n is totally useless (TODO: don't remember what it contains exactly,\n document it below later.), and a NAME which is simply a script\n reference.\n */\n\nstruct StartScript\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"StartScript\"; }\n\n    std::string mData;\n    std::string mId;\n\n    // Load a record and add it to the list\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadstat.cpp",
    "content": "#include \"loadstat.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Static::sRecordId = REC_STAT;\n\n    void Static::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n                    break;\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n    }\n    void Static::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n        }\n        else\n        {\n            esm.writeHNCString(\"MODL\", mModel);\n        }\n    }\n\n    void Static::blank()\n    {\n        mModel.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadstat.hpp",
    "content": "#ifndef OPENMW_ESM_STAT_H\n#define OPENMW_ESM_STAT_H\n\n#include <string>\n\nnamespace ESM {\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Definition of static object.\n *\n * A stat record is basically just a reference to a nif file. Some\n * esps seem to contain copies of the STAT entries from the esms, and\n * the esms themselves contain several identical entries. Perhaps all\n * statics referenced in a file is also put in the file? Since we are\n * only reading files it doesn't much matter to us, but it would if we\n * were writing our own ESM/ESPs. You can check some files later when\n * you decode the CELL blocks, if you want to test this hypothesis.\n */\n\nstruct Static\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Static\"; }\n\n    std::string mId, mModel;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n}\n#endif\n"
  },
  {
    "path": "components/esm/loadtes3.cpp",
    "content": "#include \"loadtes3.hpp\"\n\n#include \"esmcommon.hpp\"\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nvoid ESM::Header::blank()\n{\n    mData.version = ESM::VER_13;\n    mData.type = 0;\n    mData.author.clear();\n    mData.desc.clear();\n    mData.records = 0;\n    mFormat = CurrentFormat;\n    mMaster.clear();\n}\n\nvoid ESM::Header::load (ESMReader &esm)\n{\n    if (esm.isNextSub (\"FORM\"))\n    {\n        esm.getHT (mFormat);\n        if (mFormat<0)\n            esm.fail (\"invalid format code\");\n    }\n    else\n        mFormat = 0;\n\n    if (esm.isNextSub(\"HEDR\"))\n    {\n      esm.getSubHeader();\n      esm.getT(mData.version);\n      esm.getT(mData.type);\n      mData.author.assign( esm.getString(32) );\n      mData.desc.assign( esm.getString(256) );\n      esm.getT(mData.records);\n    }\n\n    while (esm.isNextSub (\"MAST\"))\n    {\n        MasterData m;\n        m.name = esm.getHString();\n        esm.getHNT(m.size, \"DATA\");\n        mMaster.push_back (m);\n    }\n\n    if (esm.isNextSub(\"GMDT\"))\n    {\n        esm.getHT(mGameData);\n    }\n    if (esm.isNextSub(\"SCRD\"))\n    {\n        esm.getSubHeader();\n        mSCRD.resize(esm.getSubSize());\n        if (!mSCRD.empty())\n            esm.getExact(mSCRD.data(), mSCRD.size());\n    }\n    if (esm.isNextSub(\"SCRS\"))\n    {\n        esm.getSubHeader();\n        mSCRS.resize(esm.getSubSize());\n        if (!mSCRS.empty())\n            esm.getExact(mSCRS.data(), mSCRS.size());\n    }\n}\n\nvoid ESM::Header::save (ESMWriter &esm)\n{\n    if (mFormat>0)\n        esm.writeHNT (\"FORM\", mFormat);\n\n    esm.startSubRecord(\"HEDR\");\n    esm.writeT(mData.version);\n    esm.writeT(mData.type);\n    esm.writeFixedSizeString(mData.author, 32);\n    esm.writeFixedSizeString(mData.desc, 256);\n    esm.writeT(mData.records);\n    esm.endRecord(\"HEDR\");\n\n    for (const Header::MasterData& data : mMaster)\n    {\n        esm.writeHNCString (\"MAST\", data.name);\n        esm.writeHNT (\"DATA\", data.size);\n    }\n}\n"
  },
  {
    "path": "components/esm/loadtes3.hpp",
    "content": "#ifndef COMPONENT_ESM_TES3_H\n#define COMPONENT_ESM_TES3_H\n\n#include <vector>\n\n#include \"esmcommon.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n#pragma pack(push)\n#pragma pack(1)\n\n    struct Data\n    {\n        /* File format version. This is actually a float, the supported\n            versions are 1.2 and 1.3. These correspond to:\n            1.2 = 0x3f99999a and 1.3 = 0x3fa66666\n        */\n        unsigned int version;\n        int type;           // 0=esp, 1=esm, 32=ess (unused)\n        std::string author; // Author's name\n        std::string desc;   // File description\n        int records;        // Number of records\n    };\n\n    struct GMDT\n    {\n        float mCurrentHealth;\n        float mMaximumHealth;\n        float mHour;\n        unsigned char unknown1[12];\n        NAME64 mCurrentCell;\n        unsigned char unknown2[4];\n        NAME32 mPlayerName;\n    };\n\n#pragma pack(pop)\n\n    /// \\brief File header record\n    struct Header\n    {\n        static const int CurrentFormat = 0; // most recent known format\n\n        // Defines another files (esm or esp) that this file depends upon.\n        struct MasterData\n        {\n            std::string name;\n            uint64_t size;\n        };\n\n        GMDT mGameData; // Used in .ess savegames only\n        std::vector<unsigned char> mSCRD; // Used in .ess savegames only, unknown\n        std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot\n\n        Data mData;\n        int mFormat;\n        std::vector<MasterData> mMaster;\n\n        void blank();\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/loadweap.cpp",
    "content": "#include \"loadweap.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    unsigned int Weapon::sRecordId = REC_WEAP;\n\n    void Weapon::load(ESMReader &esm, bool &isDeleted)\n    {\n        isDeleted = false;\n\n        bool hasName = false;\n        bool hasData = false;\n        while (esm.hasMoreSubs())\n        {\n            esm.getSubName();\n            switch (esm.retSubName().intval)\n            {\n                case ESM::SREC_NAME:\n                    mId = esm.getHString();\n                    hasName = true;\n                    break;\n                case ESM::FourCC<'M','O','D','L'>::value:\n                    mModel = esm.getHString();\n                    break;\n                case ESM::FourCC<'F','N','A','M'>::value:\n                    mName = esm.getHString();\n                    break;\n                case ESM::FourCC<'W','P','D','T'>::value:\n                    esm.getHT(mData, 32);\n                    hasData = true;\n                    break;\n                case ESM::FourCC<'S','C','R','I'>::value:\n                    mScript = esm.getHString();\n                    break;\n                case ESM::FourCC<'I','T','E','X'>::value:\n                    mIcon = esm.getHString();\n                    break;\n                case ESM::FourCC<'E','N','A','M'>::value:\n                    mEnchant = esm.getHString();\n                    break;\n                case ESM::SREC_DELE:\n                    esm.skipHSub();\n                    isDeleted = true;\n                    break;\n                default:\n                    esm.fail(\"Unknown subrecord\");\n            }\n        }\n\n        if (!hasName)\n            esm.fail(\"Missing NAME subrecord\");\n        if (!hasData && !isDeleted)\n            esm.fail(\"Missing WPDT subrecord\");\n    }\n    void Weapon::save(ESMWriter &esm, bool isDeleted) const\n    {\n        esm.writeHNCString(\"NAME\", mId);\n\n        if (isDeleted)\n        {\n            esm.writeHNCString(\"DELE\", \"\");\n            return;\n        }\n\n        esm.writeHNCString(\"MODL\", mModel);\n        esm.writeHNOCString(\"FNAM\", mName);\n        esm.writeHNT(\"WPDT\", mData, 32);\n        esm.writeHNOCString(\"SCRI\", mScript);\n        esm.writeHNOCString(\"ITEX\", mIcon);\n        esm.writeHNOCString(\"ENAM\", mEnchant);\n    }\n\n    void Weapon::blank()\n    {\n        mData.mWeight = 0;\n        mData.mValue = 0;\n        mData.mType = 0;\n        mData.mHealth = 0;\n        mData.mSpeed = 0;\n        mData.mReach = 0;\n        mData.mEnchant = 0;\n        mData.mChop[0] = mData.mChop[1] = 0;\n        mData.mSlash[0] = mData.mSlash[1] = 0;\n        mData.mThrust[0] = mData.mThrust[1] = 0;\n        mData.mFlags = 0;\n\n        mName.clear();\n        mModel.clear();\n        mIcon.clear();\n        mEnchant.clear();\n        mScript.clear();\n    }\n}\n"
  },
  {
    "path": "components/esm/loadweap.hpp",
    "content": "#ifndef OPENMW_ESM_WEAP_H\n#define OPENMW_ESM_WEAP_H\n\n#include <string>\n\n#include \"loadskil.hpp\"\n\nnamespace ESM\n{\n\nclass ESMReader;\nclass ESMWriter;\n\n/*\n * Weapon definition\n */\n\nstruct Weapon\n{\n    static unsigned int sRecordId;\n    /// Return a string descriptor for this record type. Currently used for debugging / error logs only.\n    static std::string getRecordType() { return \"Weapon\"; }\n\n    enum Type\n    {\n        PickProbe = -4,\n        HandToHand = -3,\n        Spell = -2,\n        None = -1,\n        ShortBladeOneHand = 0,\n        LongBladeOneHand = 1,\n        LongBladeTwoHand = 2,\n        BluntOneHand = 3,\n        BluntTwoClose = 4,\n        BluntTwoWide = 5,\n        SpearTwoWide = 6,\n        AxeOneHand = 7,\n        AxeTwoHand = 8,\n        MarksmanBow = 9,\n        MarksmanCrossbow = 10,\n        MarksmanThrown = 11,\n        Arrow = 12,\n        Bolt = 13\n    };\n\n    enum AttackType\n    {\n        AT_Chop,\n        AT_Slash,\n        AT_Thrust\n    };\n\n    enum Flags\n    {\n        Magical = 0x01,\n        Silver = 0x02\n    };\n\n#pragma pack(push)\n#pragma pack(1)\n    struct WPDTstruct\n    {\n        float mWeight;\n        int mValue;\n        short mType;\n        unsigned short mHealth;\n        float mSpeed, mReach;\n        unsigned short mEnchant; // Enchantment points. The real value is mEnchant/10.f\n        unsigned char mChop[2], mSlash[2], mThrust[2]; // Min and max\n        int mFlags;\n    }; // 32 bytes\n#pragma pack(pop)\n\n    WPDTstruct mData;\n\n    std::string mId, mName, mModel, mIcon, mEnchant, mScript;\n\n    void load(ESMReader &esm, bool &isDeleted);\n    void save(ESMWriter &esm, bool isDeleted = false) const;\n\n    void blank();\n    ///< Set record to default state (does not touch the ID).\n};\n\nstruct WeaponType\n{\n    enum Flags\n    {\n        TwoHanded = 0x01,\n        HasHealth = 0x02\n    };\n\n    enum Class\n    {\n        Melee = 0,\n        Ranged = 1,\n        Thrown = 2,\n        Ammo = 3\n    };\n\n    //std::string mDisplayName; // TODO: will be needed later for editor\n    std::string mShortGroup;\n    std::string mLongGroup;\n    std::string mSoundId;\n    std::string mAttachBone;\n    std::string mSheathingBone;\n    ESM::Skill::SkillEnum mSkill;\n    Class mWeaponClass;\n    int mAmmoType;\n    int mFlags;\n};\n\n}\n#endif\n"
  },
  {
    "path": "components/esm/locals.cpp",
    "content": "#include \"locals.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::Locals::load (ESMReader &esm)\n{\n    while (esm.isNextSub (\"LOCA\"))\n    {\n        std::string id = esm.getHString();\n\n        Variant value;\n        value.read (esm, Variant::Format_Local);\n\n        mVariables.emplace_back (id, value);\n    }\n}\n\nvoid ESM::Locals::save (ESMWriter &esm) const\n{\n    for (std::vector<std::pair<std::string, Variant> >::const_iterator iter (mVariables.begin());\n        iter!=mVariables.end(); ++iter)\n    {\n        esm.writeHNString (\"LOCA\", iter->first);\n        iter->second.write (esm, Variant::Format_Local);\n    }\n}\n"
  },
  {
    "path": "components/esm/locals.hpp",
    "content": "#ifndef OPENMW_ESM_LOCALS_H\n#define OPENMW_ESM_LOCALS_H\n\n#include <vector>\n#include <string>\n\n#include \"variant.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    /// \\brief Storage structure for local variables (only used in saved games)\n    ///\n    /// \\note This is not a top-level record.\n\n    struct Locals\n    {\n        std::vector<std::pair<std::string, Variant> > mVariables;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/magiceffects.cpp",
    "content": "#include \"magiceffects.hpp\"\n\n#include \"esmwriter.hpp\"\n#include \"esmreader.hpp\"\n\nnamespace ESM\n{\n\nvoid MagicEffects::save(ESMWriter &esm) const\n{\n    for (std::map<int, int>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)\n    {\n        esm.writeHNT(\"EFID\", it->first);\n        esm.writeHNT(\"BASE\", it->second);\n    }\n}\n\nvoid MagicEffects::load(ESMReader &esm)\n{\n    while (esm.isNextSub(\"EFID\"))\n    {\n        int id, base;\n        esm.getHT(id);\n        esm.getHNT(base, \"BASE\");\n        mEffects.insert(std::make_pair(id, base));\n    }\n}\n\n}\n"
  },
  {
    "path": "components/esm/magiceffects.hpp",
    "content": "#ifndef COMPONENTS_ESM_MAGICEFFECTS_H\n#define COMPONENTS_ESM_MAGICEFFECTS_H\n\n#include <map>\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n    struct MagicEffects\n    {\n        // <Effect Id, Base value>\n        std::map<int, int> mEffects;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n\n    struct SummonKey\n    {\n        SummonKey(int effectId, const std::string& sourceId, int index)\n        {\n            mEffectId = effectId;\n            mSourceId = sourceId;\n            mEffectIndex = index;\n        }\n\n        bool operator==(const SummonKey &other) const\n        {\n            return mEffectId == other.mEffectId &&\n                    mSourceId == other.mSourceId &&\n                    mEffectIndex == other.mEffectIndex;\n        }\n\n        bool operator<(const SummonKey &other) const\n        {\n            if (mEffectId < other.mEffectId)\n                return true;\n            if (mEffectId > other.mEffectId)\n                return false;\n\n            if (mSourceId < other.mSourceId)\n                return true;\n            if (mSourceId > other.mSourceId)\n                return false;\n\n            return mEffectIndex < other.mEffectIndex;\n        }\n\n        int mEffectId;\n        std::string mSourceId;\n        int mEffectIndex;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/mappings.cpp",
    "content": "#include \"mappings.hpp\"\n\n#include <stdexcept>\n\nnamespace ESM\n{\n    ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type)\n    {\n        switch(type)\n        {\n            case ESM::PRT_Head:\n                return ESM::BodyPart::MP_Head;\n            case ESM::PRT_Hair:\n                return ESM::BodyPart::MP_Hair;\n            case ESM::PRT_Neck:\n                return ESM::BodyPart::MP_Neck;\n            case ESM::PRT_Cuirass:\n                return ESM::BodyPart::MP_Chest;\n            case ESM::PRT_Groin:\n                return ESM::BodyPart::MP_Groin;\n            case ESM::PRT_RHand:\n                return ESM::BodyPart::MP_Hand;\n            case ESM::PRT_LHand:\n                return ESM::BodyPart::MP_Hand;\n            case ESM::PRT_RWrist:\n                return ESM::BodyPart::MP_Wrist;\n            case ESM::PRT_LWrist:\n                return ESM::BodyPart::MP_Wrist;\n            case ESM::PRT_RForearm:\n                return ESM::BodyPart::MP_Forearm;\n            case ESM::PRT_LForearm:\n                return ESM::BodyPart::MP_Forearm;\n            case ESM::PRT_RUpperarm:\n                return ESM::BodyPart::MP_Upperarm;\n            case ESM::PRT_LUpperarm:\n                return ESM::BodyPart::MP_Upperarm;\n            case ESM::PRT_RFoot:\n                return ESM::BodyPart::MP_Foot;\n            case ESM::PRT_LFoot:\n                return ESM::BodyPart::MP_Foot;\n            case ESM::PRT_RAnkle:\n                return ESM::BodyPart::MP_Ankle;\n            case ESM::PRT_LAnkle:\n                return ESM::BodyPart::MP_Ankle;\n            case ESM::PRT_RKnee:\n                return ESM::BodyPart::MP_Knee;\n            case ESM::PRT_LKnee:\n                return ESM::BodyPart::MP_Knee;\n            case ESM::PRT_RLeg:\n                return ESM::BodyPart::MP_Upperleg;\n            case ESM::PRT_LLeg:\n                return ESM::BodyPart::MP_Upperleg;\n            case ESM::PRT_Tail:\n                return ESM::BodyPart::MP_Tail;\n            default:\n                throw std::runtime_error(\"PartReferenceType \" +\n                    std::to_string(type) + \" not associated with a mesh part\");\n        }\n    }\n\n    std::string getBoneName(ESM::PartReferenceType type)\n    {\n        switch(type)\n        {\n            case ESM::PRT_Head:\n                return \"head\";\n            case ESM::PRT_Hair:\n                return \"head\"; // This is purposeful.\n            case ESM::PRT_Neck:\n                return \"neck\";\n            case ESM::PRT_Cuirass:\n                return \"chest\";\n            case ESM::PRT_Groin:\n                return \"groin\";\n            case ESM::PRT_Skirt:\n                return \"groin\";\n            case ESM::PRT_RHand:\n                return \"right hand\";\n            case ESM::PRT_LHand:\n                return \"left hand\";\n            case ESM::PRT_RWrist:\n                return \"right wrist\";\n            case ESM::PRT_LWrist:\n                return \"left wrist\";\n            case ESM::PRT_Shield:\n                return \"shield bone\";\n            case ESM::PRT_RForearm:\n                return \"right forearm\";\n            case ESM::PRT_LForearm:\n                return \"left forearm\";\n            case ESM::PRT_RUpperarm:\n                return \"right upper arm\";\n            case ESM::PRT_LUpperarm:\n                return \"left upper arm\";\n            case ESM::PRT_RFoot:\n                return \"right foot\";\n            case ESM::PRT_LFoot:\n                return \"left foot\";\n            case ESM::PRT_RAnkle:\n                return \"right ankle\";\n            case ESM::PRT_LAnkle:\n                return \"left ankle\";\n            case ESM::PRT_RKnee:\n                return \"right knee\";\n            case ESM::PRT_LKnee:\n                return \"left knee\";\n            case ESM::PRT_RLeg:\n                return \"right upper leg\";\n            case ESM::PRT_LLeg:\n                return \"left upper leg\";\n            case ESM::PRT_RPauldron:\n                return \"right clavicle\";\n            case ESM::PRT_LPauldron:\n                return \"left clavicle\";\n            case ESM::PRT_Weapon:\n                return \"weapon bone\";\n            case ESM::PRT_Tail:\n                return \"tail\";\n            default:\n                throw std::runtime_error(\"unknown PartReferenceType\");\n        }\n    }\n\n    std::string getMeshFilter(ESM::PartReferenceType type)\n    {\n        switch(type)\n        {\n            case ESM::PRT_Hair:\n                return \"hair\";\n            default:\n                return getBoneName(type);\n        }\n    }\n}\n"
  },
  {
    "path": "components/esm/mappings.hpp",
    "content": "#ifndef OPENMW_ESM_MAPPINGS_H\n#define OPENMW_ESM_MAPPINGS_H\n\n#include <string>\n\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadbody.hpp>\n\nnamespace ESM\n{\n    ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type);\n    std::string getBoneName(ESM::PartReferenceType type);\n    std::string getMeshFilter(ESM::PartReferenceType type);\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/npcstate.cpp",
    "content": "#include \"npcstate.hpp\"\n\nvoid ESM::NpcState::load (ESMReader &esm)\n{\n    ObjectState::load (esm);\n\n    if (mHasCustomState)\n    {\n        mInventory.load (esm);\n\n        mNpcStats.load (esm);\n\n        mCreatureStats.load (esm);\n    }\n}\n\nvoid ESM::NpcState::save (ESMWriter &esm, bool inInventory) const\n{\n    ObjectState::save (esm, inInventory);\n\n    if (mHasCustomState)\n    {\n        mInventory.save (esm);\n\n        mNpcStats.save (esm);\n\n        mCreatureStats.save (esm);\n    }\n}\n\nvoid ESM::NpcState::blank()\n{\n    ObjectState::blank();\n    mNpcStats.blank();\n    mCreatureStats.blank();\n    mHasCustomState = true;\n}\n"
  },
  {
    "path": "components/esm/npcstate.hpp",
    "content": "#ifndef OPENMW_ESM_NPCSTATE_H\n#define OPENMW_ESM_NPCSTATE_H\n\n#include \"objectstate.hpp\"\n#include \"inventorystate.hpp\"\n#include \"npcstats.hpp\"\n#include \"creaturestats.hpp\"\n\nnamespace ESM\n{\n    // format 0, saved games only\n\n    struct NpcState final : public ObjectState\n    {\n        InventoryState mInventory;\n        NpcStats mNpcStats;\n        CreatureStats mCreatureStats;\n\n        /// Initialize to default state\n        void blank() override;\n\n        void load (ESMReader &esm) override;\n        void save (ESMWriter &esm, bool inInventory = false) const override;\n\n        NpcState& asNpcState() override\n        {\n            return *this;\n        }\n        const NpcState& asNpcState() const override\n        {\n            return *this;\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/npcstats.cpp",
    "content": "#include <cassert>\n\n#include \"npcstats.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nESM::NpcStats::Faction::Faction() : mExpelled (false), mRank (-1), mReputation (0) {}\n\nvoid ESM::NpcStats::load (ESMReader &esm)\n{\n    while (esm.isNextSub (\"FACT\"))\n    {\n        std::string id = esm.getHString();\n\n        Faction faction;\n\n        int expelled = 0;\n        esm.getHNOT (expelled, \"FAEX\");\n\n        if (expelled)\n            faction.mExpelled = true;\n\n        esm.getHNOT (faction.mRank, \"FARA\");\n\n        esm.getHNOT (faction.mReputation, \"FARE\");\n\n        mFactions.insert (std::make_pair (id, faction));\n    }\n\n    mDisposition = 0;\n    esm.getHNOT (mDisposition, \"DISP\");\n\n    bool intFallback = esm.getFormat() < 11;\n    for (int i=0; i<27; ++i)\n        mSkills[i].load (esm, intFallback);\n\n    mWerewolfDeprecatedData = false;\n    if (esm.getFormat() < 8 && esm.peekNextSub(\"STBA\"))\n    {\n        // we have deprecated werewolf skills, stored interleaved\n        // Load into one big vector, then remove every 2nd value\n        mWerewolfDeprecatedData = true;\n        std::vector<ESM::StatState<float> > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0]));\n\n        for (int i=0; i<27; ++i)\n        {\n            ESM::StatState<float> skill;\n            skill.load(esm, intFallback);\n            skills.push_back(skill);\n        }\n\n        int i=0;\n        for (std::vector<ESM::StatState<float> >::iterator it = skills.begin(); it != skills.end(); ++i)\n        {\n            if (i%2 == 1)\n                it = skills.erase(it);\n            else\n                ++it;\n        }\n        assert(skills.size() == 27);\n        std::copy(skills.begin(), skills.end(), mSkills);\n    }\n\n    // No longer used\n    bool hasWerewolfAttributes = false;\n    esm.getHNOT (hasWerewolfAttributes, \"HWAT\");\n    if (hasWerewolfAttributes)\n    {\n        ESM::StatState<int> dummy;\n        for (int i=0; i<8; ++i)\n            dummy.load(esm, intFallback);\n        mWerewolfDeprecatedData = true;\n    }\n\n    mIsWerewolf = false;\n    esm.getHNOT (mIsWerewolf, \"WOLF\");\n\n    mBounty = 0;\n    esm.getHNOT (mBounty, \"BOUN\");\n\n    mReputation = 0;\n    esm.getHNOT (mReputation, \"REPU\");\n\n    mWerewolfKills = 0;\n    esm.getHNOT (mWerewolfKills, \"WKIL\");\n\n    // No longer used\n    if (esm.isNextSub(\"PROF\"))\n        esm.skipHSub(); // int profit\n\n    // No longer used\n    if (esm.isNextSub(\"ASTR\"))\n        esm.skipHSub(); // attackStrength\n\n    mLevelProgress = 0;\n    esm.getHNOT (mLevelProgress, \"LPRO\");\n\n    for (int i = 0; i < 8; ++i)\n        mSkillIncrease[i] = 0;\n    esm.getHNOT (mSkillIncrease, \"INCR\");\n\n    for (int i=0; i<3; ++i)\n        mSpecIncreases[i] = 0;\n    esm.getHNOT (mSpecIncreases, \"SPEC\");\n\n    while (esm.isNextSub (\"USED\"))\n        mUsedIds.push_back (esm.getHString());\n\n    mTimeToStartDrowning = 0;\n    esm.getHNOT (mTimeToStartDrowning, \"DRTI\");\n\n    // No longer used\n    float lastDrowningHit = 0;\n    esm.getHNOT (lastDrowningHit, \"DRLH\");\n\n    // No longer used\n    float levelHealthBonus = 0;\n    esm.getHNOT (levelHealthBonus, \"LVLH\");\n\n    mCrimeId = -1;\n    esm.getHNOT (mCrimeId, \"CRID\");\n}\n\nvoid ESM::NpcStats::save (ESMWriter &esm) const\n{\n    for (std::map<std::string, Faction>::const_iterator iter (mFactions.begin());\n        iter!=mFactions.end(); ++iter)\n    {\n        esm.writeHNString (\"FACT\", iter->first);\n\n        if (iter->second.mExpelled)\n        {\n            int expelled = 1;\n            esm.writeHNT (\"FAEX\", expelled);\n        }\n\n        if (iter->second.mRank >= 0)\n            esm.writeHNT (\"FARA\", iter->second.mRank);\n\n        if (iter->second.mReputation)\n            esm.writeHNT (\"FARE\", iter->second.mReputation);\n    }\n\n    if (mDisposition)\n        esm.writeHNT (\"DISP\", mDisposition);\n\n    for (int i=0; i<27; ++i)\n        mSkills[i].save (esm);\n\n    if (mIsWerewolf)\n        esm.writeHNT (\"WOLF\", mIsWerewolf);\n\n    if (mBounty)\n        esm.writeHNT (\"BOUN\", mBounty);\n\n    if (mReputation)\n        esm.writeHNT (\"REPU\", mReputation);\n\n    if (mWerewolfKills)\n        esm.writeHNT (\"WKIL\", mWerewolfKills);\n\n    if (mLevelProgress)\n        esm.writeHNT (\"LPRO\", mLevelProgress);\n\n    bool saveSkillIncreases = false;\n    for (int i = 0; i < 8; ++i)\n    {\n        if (mSkillIncrease[i] != 0)\n        {\n            saveSkillIncreases = true;\n            break;\n        }\n    }\n    if (saveSkillIncreases)\n        esm.writeHNT (\"INCR\", mSkillIncrease);\n\n    if (mSpecIncreases[0] != 0 ||\n        mSpecIncreases[1] != 0 ||\n        mSpecIncreases[2] != 0)\n    esm.writeHNT (\"SPEC\", mSpecIncreases);\n\n    for (std::vector<std::string>::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end();\n        ++iter)\n        esm.writeHNString (\"USED\", *iter);\n\n    if (mTimeToStartDrowning)\n        esm.writeHNT (\"DRTI\", mTimeToStartDrowning);\n\n    if (mCrimeId != -1)\n        esm.writeHNT (\"CRID\", mCrimeId);\n}\n\nvoid ESM::NpcStats::blank()\n{\n    mWerewolfDeprecatedData = false;\n    mIsWerewolf = false;\n    mDisposition = 0;\n    mBounty = 0;\n    mReputation = 0;\n    mWerewolfKills = 0;\n    mLevelProgress = 0;\n    for (int i=0; i<8; ++i)\n        mSkillIncrease[i] = 0;\n    for (int i=0; i<3; ++i)\n        mSpecIncreases[i] = 0;\n    mTimeToStartDrowning = 20;\n    mCrimeId = -1;\n}\n"
  },
  {
    "path": "components/esm/npcstats.hpp",
    "content": "#ifndef OPENMW_ESM_NPCSTATS_H\n#define OPENMW_ESM_NPCSTATS_H\n\n#include <string>\n#include <vector>\n#include <map>\n\n#include \"statstate.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    struct NpcStats\n    {\n        struct Faction\n        {\n            bool mExpelled;\n            int mRank;\n            int mReputation;\n\n            Faction();\n        };\n\n        bool mIsWerewolf;\n\n        bool mWerewolfDeprecatedData;\n\n        std::map<std::string, Faction> mFactions; // lower case IDs\n        int mDisposition;\n        StatState<float> mSkills[27];\n        int mBounty;\n        int mReputation;\n        int mWerewolfKills;\n        int mLevelProgress;\n        int mSkillIncrease[8];\n        int mSpecIncreases[3];\n        std::vector<std::string> mUsedIds; // lower case IDs\n        float mTimeToStartDrowning;\n        int mCrimeId;\n\n        /// Initialize to default state\n        void blank();\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/objectstate.cpp",
    "content": "#include \"objectstate.hpp\"\n\n#include <stdexcept>\n#include <sstream>\n#include <typeinfo>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::ObjectState::load (ESMReader &esm)\n{\n    mVersion = esm.getFormat();\n\n    bool isDeleted;\n    mRef.loadData(esm, isDeleted);\n\n    mHasLocals = 0;\n    esm.getHNOT (mHasLocals, \"HLOC\");\n\n    if (mHasLocals)\n        mLocals.load (esm);\n\n    mEnabled = 1;\n    esm.getHNOT (mEnabled, \"ENAB\");\n\n    mCount = 1;\n    esm.getHNOT (mCount, \"COUN\");\n\n    mPosition = mRef.mPos;\n    esm.getHNOT (mPosition, \"POS_\", 24);\n\n    if (esm.isNextSub(\"LROT\"))\n        esm.skipHSub(); // local rotation, no longer used\n\n    mFlags = 0;\n    esm.getHNOT (mFlags, \"FLAG\");\n\n    // obsolete\n    int unused;\n    esm.getHNOT(unused, \"LTIM\");\n\n    mAnimationState.load(esm);\n\n    // FIXME: assuming \"false\" as default would make more sense, but also break compatibility with older save files\n    mHasCustomState = true;\n    esm.getHNOT (mHasCustomState, \"HCUS\");\n}\n\nvoid ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const\n{\n    mRef.save (esm, true, inInventory);\n\n    if (mHasLocals)\n    {\n        esm.writeHNT (\"HLOC\", mHasLocals);\n        mLocals.save (esm);\n    }\n\n    if (!mEnabled && !inInventory)\n        esm.writeHNT (\"ENAB\", mEnabled);\n\n    if (mCount!=1)\n        esm.writeHNT (\"COUN\", mCount);\n\n    if (!inInventory && mPosition != mRef.mPos)\n        esm.writeHNT (\"POS_\", mPosition, 24);\n\n    if (mFlags != 0)\n        esm.writeHNT (\"FLAG\", mFlags);\n\n    mAnimationState.save(esm);\n\n    if (!mHasCustomState)\n        esm.writeHNT (\"HCUS\", false);\n}\n\nvoid ESM::ObjectState::blank()\n{\n    mRef.blank();\n    mHasLocals = 0;\n    mEnabled = false;\n    mCount = 1;\n    for (int i=0;i<3;++i)\n    {\n        mPosition.pos[i] = 0;\n        mPosition.rot[i] = 0;\n    }\n    mFlags = 0;\n    mHasCustomState = true;\n}\n\nconst ESM::NpcState& ESM::ObjectState::asNpcState() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to NpcState\";\n    throw std::logic_error(error.str());\n}\n\nESM::NpcState& ESM::ObjectState::asNpcState()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to NpcState\";\n    throw std::logic_error(error.str());\n}\n\nconst ESM::CreatureState& ESM::ObjectState::asCreatureState() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to CreatureState\";\n    throw std::logic_error(error.str());\n}\n\nESM::CreatureState& ESM::ObjectState::asCreatureState()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to CreatureState\";\n    throw std::logic_error(error.str());\n}\n\nconst ESM::ContainerState& ESM::ObjectState::asContainerState() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to ContainerState\";\n    throw std::logic_error(error.str());\n}\n\nESM::ContainerState& ESM::ObjectState::asContainerState()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to ContainerState\";\n    throw std::logic_error(error.str());\n}\n\nconst ESM::DoorState& ESM::ObjectState::asDoorState() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to DoorState\";\n    throw std::logic_error(error.str());\n}\n\nESM::DoorState& ESM::ObjectState::asDoorState()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to DoorState\";\n    throw std::logic_error(error.str());\n}\n\nconst ESM::CreatureLevListState& ESM::ObjectState::asCreatureLevListState() const\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to CreatureLevListState\";\n    throw std::logic_error(error.str());\n}\n\nESM::CreatureLevListState& ESM::ObjectState::asCreatureLevListState()\n{\n    std::stringstream error;\n    error << \"bad cast \" << typeid(this).name() << \" to CreatureLevListState\";\n    throw std::logic_error(error.str());\n}\n\nESM::ObjectState::~ObjectState() {}\n"
  },
  {
    "path": "components/esm/objectstate.hpp",
    "content": "#ifndef OPENMW_ESM_OBJECTSTATE_H\n#define OPENMW_ESM_OBJECTSTATE_H\n\n#include <string>\n#include <vector>\n\n#include \"cellref.hpp\"\n#include \"locals.hpp\"\n#include \"animationstate.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n    struct ContainerState;\n    struct CreatureLevListState;\n    struct CreatureState;\n    struct DoorState;\n    struct NpcState;\n\n    // format 0, saved games only\n\n    ///< \\brief Save state for objects, that do not use custom data\n    struct ObjectState\n    {\n        CellRef mRef;\n\n        unsigned char mHasLocals;\n        Locals mLocals;\n        unsigned char mEnabled;\n        int mCount;\n        ESM::Position mPosition;\n        unsigned int mFlags;\n\n        // Is there any class-specific state following the ObjectState\n        bool mHasCustomState;\n\n        unsigned int mVersion;\n\n        ESM::AnimationState mAnimationState;\n\n        ObjectState()\n        : mHasLocals(0), mEnabled(0), mCount(0)\n        , mFlags(0), mHasCustomState(true), mVersion(0)\n        {}\n\n        /// @note Does not load the CellRef ID, it should already be loaded before calling this method\n        virtual void load (ESMReader &esm);\n\n        virtual void save (ESMWriter &esm, bool inInventory = false) const;\n\n        virtual /// Initialize to default state\n        void blank();\n\n        virtual ~ObjectState();\n\n        virtual const NpcState& asNpcState() const;\n        virtual NpcState& asNpcState();\n\n        virtual const CreatureState& asCreatureState() const;\n        virtual CreatureState& asCreatureState();\n\n        virtual const ContainerState& asContainerState() const;\n        virtual ContainerState& asContainerState();\n\n        virtual const DoorState& asDoorState() const;\n        virtual DoorState& asDoorState();\n\n        virtual const CreatureLevListState& asCreatureLevListState() const;\n        virtual CreatureLevListState& asCreatureLevListState();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/player.cpp",
    "content": "#include \"player.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::Player::load (ESMReader &esm)\n{\n    mObject.mRef.loadId(esm, true);\n    mObject.load (esm);\n\n    mCellId.load (esm);\n\n    esm.getHNT (mLastKnownExteriorPosition, \"LKEP\", 12);\n\n    if (esm.isNextSub (\"MARK\"))\n    {\n        mHasMark = true;\n        esm.getHT (mMarkedPosition, 24);\n        mMarkedCell.load (esm);\n    }\n    else\n        mHasMark = false;\n\n    // Automove, no longer used.\n    if (esm.isNextSub(\"AMOV\"))\n        esm.skipHSub();\n\n    mBirthsign = esm.getHNString (\"SIGN\");\n\n    mCurrentCrimeId = -1;\n    esm.getHNOT (mCurrentCrimeId, \"CURD\");\n    mPaidCrimeId = -1;\n    esm.getHNOT (mPaidCrimeId, \"PAYD\");\n\n    bool checkPrevItems = true;\n    while (checkPrevItems)\n    {\n        std::string boundItemId = esm.getHNOString(\"BOUN\");\n        std::string prevItemId = esm.getHNOString(\"PREV\");\n\n        if (!boundItemId.empty())\n            mPreviousItems[boundItemId] = prevItemId;\n        else\n            checkPrevItems = false;\n    }\n\n    bool intFallback = esm.getFormat() < 11;\n    if (esm.hasMoreSubs())\n    {\n        for (int i=0; i<ESM::Attribute::Length; ++i)\n            mSaveAttributes[i].load(esm, intFallback);\n        for (int i=0; i<ESM::Skill::Length; ++i)\n            mSaveSkills[i].load(esm, intFallback);\n    }\n}\n\nvoid ESM::Player::save (ESMWriter &esm) const\n{\n    mObject.save (esm);\n\n    mCellId.save (esm);\n\n    esm.writeHNT (\"LKEP\", mLastKnownExteriorPosition);\n\n    if (mHasMark)\n    {\n        esm.writeHNT (\"MARK\", mMarkedPosition, 24);\n        mMarkedCell.save (esm);\n    }\n\n    esm.writeHNString (\"SIGN\", mBirthsign);\n\n    esm.writeHNT (\"CURD\", mCurrentCrimeId);\n    esm.writeHNT (\"PAYD\", mPaidCrimeId);\n\n    for (PreviousItems::const_iterator it=mPreviousItems.begin(); it != mPreviousItems.end(); ++it)\n    {\n        esm.writeHNString (\"BOUN\", it->first);\n        esm.writeHNString (\"PREV\", it->second);\n    }\n\n    for (int i=0; i<ESM::Attribute::Length; ++i)\n        mSaveAttributes[i].save(esm);\n    for (int i=0; i<ESM::Skill::Length; ++i)\n        mSaveSkills[i].save(esm);\n}\n"
  },
  {
    "path": "components/esm/player.hpp",
    "content": "#ifndef OPENMW_ESM_PLAYER_H\n#define OPENMW_ESM_PLAYER_H\n\n#include <string>\n\n#include \"npcstate.hpp\"\n#include \"cellid.hpp\"\n#include \"defs.hpp\"\n\n#include \"loadskil.hpp\"\n#include \"attr.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    struct Player\n    {\n        NpcState mObject;\n        CellId mCellId;\n        float mLastKnownExteriorPosition[3];\n        unsigned char mHasMark;\n        ESM::Position mMarkedPosition;\n        CellId mMarkedCell;\n        std::string mBirthsign;\n\n        int mCurrentCrimeId;\n        int mPaidCrimeId;\n\n        StatState<float> mSaveAttributes[ESM::Attribute::Length];\n        StatState<float> mSaveSkills[ESM::Skill::Length];\n\n        typedef std::map<std::string, std::string> PreviousItems; // previous equipped items, needed for bound spells\n        PreviousItems mPreviousItems;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/projectilestate.cpp",
    "content": "#include \"projectilestate.hpp\"\n\n#include \"esmwriter.hpp\"\n#include \"esmreader.hpp\"\n\nnamespace ESM\n{\n\n    void BaseProjectileState::save(ESMWriter &esm) const\n    {\n        esm.writeHNString (\"ID__\", mId);\n        esm.writeHNT (\"VEC3\", mPosition);\n        esm.writeHNT (\"QUAT\", mOrientation);\n        esm.writeHNT (\"ACTO\", mActorId);\n    }\n\n    void BaseProjectileState::load(ESMReader &esm)\n    {\n        mId = esm.getHNString(\"ID__\");\n        esm.getHNT (mPosition, \"VEC3\");\n        esm.getHNT (mOrientation, \"QUAT\");\n        esm.getHNT (mActorId, \"ACTO\");\n    }\n\n    void MagicBoltState::save(ESMWriter &esm) const\n    {\n        BaseProjectileState::save(esm);\n\n        esm.writeHNString (\"SPEL\", mSpellId);\n        esm.writeHNT (\"SPED\", mSpeed);\n    }\n\n    void MagicBoltState::load(ESMReader &esm)\n    {\n        BaseProjectileState::load(esm);\n\n        mSpellId = esm.getHNString(\"SPEL\");\n        if (esm.isNextSub(\"SRCN\")) // for backwards compatibility\n            esm.skipHSub();\n        ESM::EffectList().load(esm); // for backwards compatibility\n        esm.getHNT (mSpeed, \"SPED\");\n        if (esm.isNextSub(\"STCK\")) // for backwards compatibility\n            esm.skipHSub();\n        if (esm.isNextSub(\"SOUN\")) // for backwards compatibility\n            esm.skipHSub();\n    }\n\n    void ProjectileState::save(ESMWriter &esm) const\n    {\n        BaseProjectileState::save(esm);\n\n        esm.writeHNString (\"BOW_\", mBowId);\n        esm.writeHNT (\"VEL_\", mVelocity);\n        esm.writeHNT (\"STR_\", mAttackStrength);\n    }\n\n    void ProjectileState::load(ESMReader &esm)\n    {\n        BaseProjectileState::load(esm);\n\n        mBowId = esm.getHNString (\"BOW_\");\n        esm.getHNT (mVelocity, \"VEL_\");\n\n        mAttackStrength = 1.f;\n        esm.getHNOT(mAttackStrength, \"STR_\");\n    }\n\n}\n"
  },
  {
    "path": "components/esm/projectilestate.hpp",
    "content": "#ifndef OPENMW_ESM_PROJECTILESTATE_H\n#define OPENMW_ESM_PROJECTILESTATE_H\n\n#include <string>\n\n#include <osg/Quat>\n#include <osg/Vec3f>\n\n#include \"effectlist.hpp\"\n\n#include \"util.hpp\"\n\nnamespace ESM\n{\n\n    // format 0, savegames only\n\n    struct BaseProjectileState\n    {\n        std::string mId;\n\n        Vector3 mPosition;\n        Quaternion mOrientation;\n\n        int mActorId;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n\n    struct MagicBoltState : public BaseProjectileState\n    {\n        std::string mSpellId;\n        float mSpeed;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n\n    struct ProjectileState : public BaseProjectileState\n    {\n        std::string mBowId;\n        Vector3 mVelocity;\n        float mAttackStrength;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/queststate.cpp",
    "content": "#include \"queststate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::QuestState::load (ESMReader &esm)\n{\n    mTopic = esm.getHNString (\"YETO\");\n    esm.getHNOT (mState, \"QSTA\");\n    esm.getHNOT (mFinished, \"QFIN\");\n}\n\nvoid ESM::QuestState::save (ESMWriter &esm) const\n{\n    esm.writeHNString (\"YETO\", mTopic);\n    esm.writeHNT (\"QSTA\", mState);\n    esm.writeHNT (\"QFIN\", mFinished);\n}\n"
  },
  {
    "path": "components/esm/queststate.hpp",
    "content": "#ifndef OPENMW_ESM_QUESTSTATE_H\n#define OPENMW_ESM_QUESTSTATE_H\n\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    struct QuestState\n    {\n        std::string mTopic; // lower case id\n        int mState;\n        unsigned char mFinished;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/quickkeys.cpp",
    "content": "#include \"quickkeys.hpp\"\n\n#include \"esmwriter.hpp\"\n#include \"esmreader.hpp\"\n\nnamespace ESM\n{\n\n    void QuickKeys::load(ESMReader &esm)\n    {\n        if (esm.isNextSub(\"KEY_\"))\n            esm.getSubHeader(); // no longer used, because sub-record hierachies do not work properly in esmreader\n\n        while (esm.isNextSub(\"TYPE\"))\n        {\n            int keyType;\n            esm.getHT(keyType);\n            std::string id;\n            id = esm.getHNString(\"ID__\");\n\n            QuickKey key;\n            key.mType = keyType;\n            key.mId = id;\n\n            mKeys.push_back(key);\n\n            if (esm.isNextSub(\"KEY_\"))\n                esm.getSubHeader();  // no longer used, because sub-record hierachies do not work properly in esmreader\n        }\n    }\n\n    void QuickKeys::save(ESMWriter &esm) const\n    {\n        for (std::vector<QuickKey>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it)\n        {\n            esm.writeHNT(\"TYPE\", it->mType);\n            esm.writeHNString(\"ID__\", it->mId);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "components/esm/quickkeys.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_ESM_QUICKKEYS_H\n#define OPENMW_COMPONENTS_ESM_QUICKKEYS_H\n\n#include <vector>\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    struct QuickKeys\n    {\n        struct QuickKey\n        {\n            int mType;\n            std::string mId; // Spell or Item ID\n        };\n\n        std::vector<QuickKey> mKeys;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/records.hpp",
    "content": "#ifndef OPENMW_ESM_RECORDS_H\n#define OPENMW_ESM_RECORDS_H\n\n#include \"defs.hpp\"\n#include \"loadacti.hpp\"\n#include \"loadalch.hpp\"\n#include \"loadappa.hpp\"\n#include \"loadarmo.hpp\"\n#include \"loadbody.hpp\"\n#include \"loadbook.hpp\"\n#include \"loadbsgn.hpp\"\n#include \"loadcell.hpp\"\n#include \"loadclas.hpp\"\n#include \"loadclot.hpp\"\n#include \"loadcont.hpp\"\n#include \"loadcrea.hpp\"\n#include \"loadinfo.hpp\"\n#include \"loaddial.hpp\"\n#include \"loaddoor.hpp\"\n#include \"loadench.hpp\"\n#include \"loadfact.hpp\"\n#include \"loadglob.hpp\"\n#include \"loadgmst.hpp\"\n#include \"loadingr.hpp\"\n#include \"loadland.hpp\"\n#include \"loadlevlist.hpp\"\n#include \"loadligh.hpp\"\n#include \"loadlock.hpp\"\n#include \"loadrepa.hpp\"\n#include \"loadprob.hpp\"\n#include \"loadltex.hpp\"\n#include \"loadmgef.hpp\"\n#include \"loadmisc.hpp\"\n#include \"loadnpc.hpp\"\n#include \"loadpgrd.hpp\"\n#include \"loadrace.hpp\"\n#include \"loadregn.hpp\"\n#include \"loadscpt.hpp\"\n#include \"loadskil.hpp\"\n#include \"loadsndg.hpp\"\n#include \"loadsoun.hpp\"\n#include \"loadspel.hpp\"\n#include \"loadsscr.hpp\"\n#include \"loadstat.hpp\"\n#include \"loadweap.hpp\"\n\n// Special records which are not loaded from ESM\n#include \"attr.hpp\"\n#endif\n"
  },
  {
    "path": "components/esm/savedgame.cpp",
    "content": "#include \"savedgame.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nunsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;\nint ESM::SavedGame::sCurrentFormat = 15;\n\nvoid ESM::SavedGame::load (ESMReader &esm)\n{\n    mPlayerName = esm.getHNString(\"PLNA\");\n    esm.getHNOT (mPlayerLevel, \"PLLE\");\n\n    mPlayerClassId = esm.getHNOString(\"PLCL\");\n    mPlayerClassName = esm.getHNOString(\"PLCN\");\n\n    mPlayerCell = esm.getHNString(\"PLCE\");\n    esm.getHNT (mInGameTime, \"TSTM\", 16);\n    esm.getHNT (mTimePlayed, \"TIME\");\n    mDescription = esm.getHNString (\"DESC\");\n\n    while (esm.isNextSub (\"DEPE\"))\n        mContentFiles.push_back (esm.getHString());\n\n    esm.getSubNameIs(\"SCRN\");\n    esm.getSubHeader();\n    mScreenshot.resize(esm.getSubSize());\n    esm.getExact(mScreenshot.data(), mScreenshot.size());\n}\n\nvoid ESM::SavedGame::save (ESMWriter &esm) const\n{\n    esm.writeHNString (\"PLNA\", mPlayerName);\n    esm.writeHNT (\"PLLE\", mPlayerLevel);\n\n    if (!mPlayerClassId.empty())\n        esm.writeHNString (\"PLCL\", mPlayerClassId);\n    else\n        esm.writeHNString (\"PLCN\", mPlayerClassName);\n\n    esm.writeHNString (\"PLCE\", mPlayerCell);\n    esm.writeHNT (\"TSTM\", mInGameTime, 16);\n    esm.writeHNT (\"TIME\", mTimePlayed);\n    esm.writeHNString (\"DESC\", mDescription);\n\n    for (std::vector<std::string>::const_iterator iter (mContentFiles.begin());\n         iter!=mContentFiles.end(); ++iter)\n         esm.writeHNString (\"DEPE\", *iter);\n\n    esm.startSubRecord(\"SCRN\");\n    esm.write(&mScreenshot[0], mScreenshot.size());\n    esm.endRecord(\"SCRN\");\n}\n"
  },
  {
    "path": "components/esm/savedgame.hpp",
    "content": "#ifndef OPENMW_ESM_SAVEDGAME_H\n#define OPENMW_ESM_SAVEDGAME_H\n\n#include <vector>\n#include <string>\n\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    struct SavedGame\n    {\n        static unsigned int sRecordId;\n\n        static int sCurrentFormat;\n\n        std::vector<std::string> mContentFiles;\n        std::string mPlayerName;\n        int mPlayerLevel;\n\n        // ID of class\n        std::string mPlayerClassId;\n        // Name of the class. When using a custom class, the ID is not really meaningful prior\n        // to loading the savegame, so the name is stored separately.\n        std::string mPlayerClassName;\n\n        std::string mPlayerCell;\n        EpochTimeStamp mInGameTime;\n        double mTimePlayed;\n        std::string mDescription;\n        std::vector<char> mScreenshot; // raw jpg-encoded data\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/spelllist.cpp",
    "content": "#include \"spelllist.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM {\n\nvoid SpellList::add(ESMReader &esm)\n{\n    mList.push_back(esm.getHString());\n}\n\nvoid SpellList::save(ESMWriter &esm) const\n{\n    for (std::vector<std::string>::const_iterator it = mList.begin(); it != mList.end(); ++it) {\n        esm.writeHNString(\"NPCS\", *it, 32);\n    }\n}\n\nbool SpellList::exists(const std::string &spell) const\n{\n    for (std::vector<std::string>::const_iterator it = mList.begin(); it != mList.end(); ++it)\n        if (Misc::StringUtils::ciEqual(*it, spell))\n            return true;\n    return false;\n}\n\n}\n"
  },
  {
    "path": "components/esm/spelllist.hpp",
    "content": "#ifndef OPENMW_ESM_SPELLLIST_H\n#define OPENMW_ESM_SPELLLIST_H\n\n#include <vector>\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    /** A list of references to spells and spell effects. This is shared\n     between the records BSGN, NPC and RACE.\n     NPCS subrecord.\n     */\n    struct SpellList\n    {\n        std::vector<std::string> mList;\n\n        /// Is this spell ID in mList?\n        bool exists(const std::string& spell) const;\n\n        /// Load one spell, assumes the subrecord name was already read\n        void add(ESMReader &esm);\n\n        void save(ESMWriter &esm) const;\n    };\n}\n\n#endif\n\n"
  },
  {
    "path": "components/esm/spellstate.cpp",
    "content": "#include \"spellstate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM\n{\n\n    void SpellState::load(ESMReader &esm)\n    {\n        while (esm.isNextSub(\"SPEL\"))\n        {\n            std::string id = esm.getHString();\n\n            SpellParams state;\n            while (esm.isNextSub(\"INDX\"))\n            {\n                int index;\n                esm.getHT(index);\n\n                float magnitude;\n                esm.getHNT(magnitude, \"RAND\");\n\n                state.mEffectRands[index] = magnitude;\n            }\n\n            while (esm.isNextSub(\"PURG\")) {\n                int index;\n                esm.getHT(index);\n                state.mPurgedEffects.insert(index);\n            }\n\n            mSpells[id] = state;\n        }\n\n        // Obsolete\n        while (esm.isNextSub(\"PERM\"))\n        {\n            std::string spellId = esm.getHString();\n            std::vector<PermanentSpellEffectInfo> permEffectList;\n\n            while (true)\n            {\n                ESM_Context restorePoint = esm.getContext();\n\n                if (!esm.isNextSub(\"EFID\"))\n                    break;\n\n                PermanentSpellEffectInfo info;\n                esm.getHT(info.mId);\n                if (esm.isNextSub(\"BASE\"))\n                {\n                    esm.restoreContext(restorePoint);\n                    return;\n                }\n                else\n                    esm.getHNT(info.mArg, \"ARG_\");\n\n                esm.getHNT(info.mMagnitude, \"MAGN\");\n                permEffectList.push_back(info);\n            }\n            mPermanentSpellEffects[spellId] = permEffectList;\n        }\n\n        // Obsolete\n        while (esm.isNextSub(\"CORP\"))\n        {\n            std::string id = esm.getHString();\n\n            CorprusStats stats;\n            esm.getHNT(stats.mWorsenings, \"WORS\");\n            esm.getHNT(stats.mNextWorsening, \"TIME\");\n\n            mCorprusSpells[id] = stats;\n        }\n\n        while (esm.isNextSub(\"USED\"))\n        {\n            std::string id = esm.getHString();\n            TimeStamp time;\n            esm.getHNT(time, \"TIME\");\n\n            mUsedPowers[id] = time;\n        }\n\n        mSelectedSpell = esm.getHNOString(\"SLCT\");\n    }\n\n    void SpellState::save(ESMWriter &esm) const\n    {\n        for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)\n        {\n            esm.writeHNString(\"SPEL\", it->first);\n\n            const std::map<int, float>& random = it->second.mEffectRands;\n            for (std::map<int, float>::const_iterator rIt = random.begin(); rIt != random.end(); ++rIt)\n            {\n                esm.writeHNT(\"INDX\", rIt->first);\n                esm.writeHNT(\"RAND\", rIt->second);\n            }\n\n            const std::set<int>& purges = it->second.mPurgedEffects;\n            for (std::set<int>::const_iterator pIt = purges.begin(); pIt != purges.end(); ++pIt)\n                esm.writeHNT(\"PURG\", *pIt);\n        }\n\n        for (std::map<std::string, CorprusStats>::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it)\n        {\n            esm.writeHNString(\"CORP\", it->first);\n\n            const CorprusStats & stats = it->second;\n            esm.writeHNT(\"WORS\", stats.mWorsenings);\n            esm.writeHNT(\"TIME\", stats.mNextWorsening);\n        }\n\n        for (std::map<std::string, TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)\n        {\n            esm.writeHNString(\"USED\", it->first);\n            esm.writeHNT(\"TIME\", it->second);\n        }\n\n        if (!mSelectedSpell.empty())\n            esm.writeHNString(\"SLCT\", mSelectedSpell);\n    }\n\n}\n"
  },
  {
    "path": "components/esm/spellstate.hpp",
    "content": "#ifndef OPENMW_ESM_SPELLSTATE_H\n#define OPENMW_ESM_SPELLSTATE_H\n\n#include <map>\n#include <vector>\n#include <string>\n#include <set>\n\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // NOTE: spell ids must be lower case\n    struct SpellState\n    {\n        struct CorprusStats\n        {\n            int mWorsenings;\n            TimeStamp mNextWorsening;\n        };\n\n        struct PermanentSpellEffectInfo\n        {\n            int mId;\n            int mArg;\n            float mMagnitude;\n        };\n\n        struct SpellParams\n        {\n            std::map<int, float> mEffectRands;\n            std::set<int> mPurgedEffects;\n        };\n        typedef std::map<std::string, SpellParams> TContainer;\n        TContainer mSpells;\n\n        // FIXME: obsolete, used only for old saves\n        std::map<std::string, std::vector<PermanentSpellEffectInfo> > mPermanentSpellEffects;\n        std::map<std::string, CorprusStats> mCorprusSpells;\n\n        std::map<std::string, TimeStamp> mUsedPowers;\n\n        std::string mSelectedSpell;\n\n        void load (ESMReader &esm);\n        void save (ESMWriter &esm) const;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/statstate.cpp",
    "content": "#include \"statstate.hpp\"\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nnamespace ESM\n{\n    template<typename T>\n    StatState<T>::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {}\n\n    template<typename T>\n    void StatState<T>::load(ESMReader &esm, bool intFallback)\n    {\n        // We changed stats values from integers to floats; ensure backwards compatibility\n        if (intFallback)\n        {\n            int base = 0;\n            esm.getHNT(base, \"STBA\");\n            mBase = static_cast<T>(base);\n\n            int mod = 0;\n            esm.getHNOT(mod, \"STMO\");\n            mMod = static_cast<T>(mod);\n\n            int current = 0;\n            esm.getHNOT(current, \"STCU\");\n            mCurrent = static_cast<T>(current);\n\n            int oldDamage = 0;\n            esm.getHNOT(oldDamage, \"STDA\");\n            mDamage = static_cast<float>(oldDamage);\n        }\n        else\n        {\n            mBase = 0;\n            esm.getHNT(mBase, \"STBA\");\n\n            mMod = 0;\n            esm.getHNOT(mMod, \"STMO\");\n\n            mCurrent = 0;\n            esm.getHNOT(mCurrent, \"STCU\");\n\n            mDamage = 0;\n            esm.getHNOT(mDamage, \"STDF\");\n\n            mProgress = 0;\n        }\n\n        esm.getHNOT(mDamage, \"STDF\");\n\n        mProgress = 0;\n        esm.getHNOT(mProgress, \"STPR\");\n    }\n\n    template<typename T>\n    void StatState<T>::save(ESMWriter &esm) const\n    {\n        esm.writeHNT(\"STBA\", mBase);\n\n        if (mMod != 0)\n            esm.writeHNT(\"STMO\", mMod);\n\n        if (mCurrent)\n            esm.writeHNT(\"STCU\", mCurrent);\n\n        if (mDamage)\n            esm.writeHNT(\"STDF\", mDamage);\n\n        if (mProgress)\n            esm.writeHNT(\"STPR\", mProgress);\n    }\n}\n\ntemplate struct ESM::StatState<int>;\ntemplate struct ESM::StatState<float>;\n"
  },
  {
    "path": "components/esm/statstate.hpp",
    "content": "#ifndef OPENMW_ESM_STATSTATE_H\n#define OPENMW_ESM_STATSTATE_H\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n\n    template<typename T>\n    struct StatState\n    {\n        T mBase;\n        T mMod; // Note: can either be the modifier, or the modified value.\n                // A bit inconsistent, but we can't fix this without breaking compatibility.\n        T mCurrent;\n        float mDamage;\n        float mProgress;\n\n        StatState();\n\n        void load (ESMReader &esm, bool intFallback = false);\n        void save (ESMWriter &esm) const;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/stolenitems.cpp",
    "content": "#include \"stolenitems.hpp\"\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n\nnamespace ESM\n{\n\n    void StolenItems::write(ESMWriter &esm) const\n    {\n        for (StolenItemsMap::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)\n        {\n            esm.writeHNString(\"NAME\", it->first);\n            for (std::map<std::pair<std::string, bool>, int>::const_iterator ownerIt = it->second.begin();\n                 ownerIt != it->second.end(); ++ownerIt)\n            {\n                if (ownerIt->first.second)\n                    esm.writeHNString(\"FNAM\", ownerIt->first.first);\n                else\n                    esm.writeHNString(\"ONAM\", ownerIt->first.first);\n                esm.writeHNT(\"COUN\", ownerIt->second);\n            }\n        }\n    }\n\n    void StolenItems::load(ESMReader &esm)\n    {\n        while (esm.isNextSub(\"NAME\"))\n        {\n            std::string itemid = esm.getHString();\n\n            std::map<std::pair<std::string, bool>, int> ownerMap;\n            while (esm.isNextSub(\"FNAM\") || esm.isNextSub(\"ONAM\"))\n            {\n                std::string subname = esm.retSubName().toString();\n                std::string owner = esm.getHString();\n                bool isFaction = (subname == \"FNAM\");\n                int count;\n                esm.getHNT(count, \"COUN\");\n                ownerMap.insert(std::make_pair(std::make_pair(owner, isFaction), count));\n            }\n\n            mStolenItems[itemid] = ownerMap;\n        }\n    }\n\n}\n"
  },
  {
    "path": "components/esm/stolenitems.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_ESM_STOLENITEMS_H\n#define OPENMW_COMPONENTS_ESM_STOLENITEMS_H\n\n#include <map>\n#include <string>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    // format 0, saved games only\n    struct StolenItems\n    {\n        typedef std::map<std::string, std::map<std::pair<std::string, bool>, int> > StolenItemsMap;\n        StolenItemsMap mStolenItems;\n\n        void load(ESM::ESMReader& esm);\n        void write(ESM::ESMWriter& esm) const;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/transport.cpp",
    "content": "#include \"transport.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/esm/esmreader.hpp>\n#include <components/esm/esmwriter.hpp>\n\nnamespace ESM\n{\n\n    void Transport::add(ESMReader &esm)\n    {\n        if (esm.retSubName().intval == ESM::FourCC<'D','O','D','T'>::value)\n        {\n            Dest dodt;\n            esm.getHExact(&dodt.mPos, 24);\n            mList.push_back(dodt);\n        }\n        else if (esm.retSubName().intval == ESM::FourCC<'D','N','A','M'>::value)\n        {\n            const std::string name = esm.getHString();\n            if (mList.empty())\n                Log(Debug::Warning) << \"Encountered DNAM record without DODT record, skipped.\";\n            else\n                mList.back().mCellName = name;\n        }\n    }\n\n    void Transport::save(ESMWriter &esm) const\n    {\n        typedef std::vector<Dest>::const_iterator DestIter;\n        for (DestIter it = mList.begin(); it != mList.end(); ++it)\n        {\n            esm.writeHNT(\"DODT\", it->mPos, sizeof(it->mPos));\n            esm.writeHNOCString(\"DNAM\", it->mCellName);\n        }\n    }\n\n}\n"
  },
  {
    "path": "components/esm/transport.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_ESM_TRANSPORT_H\n#define OPENMW_COMPONENTS_ESM_TRANSPORT_H\n\n#include <string>\n#include <vector>\n\n#include \"defs.hpp\"\n\nnamespace ESM\n{\n\n    class ESMReader;\n    class ESMWriter;\n\n    /// List of travel service destination. Shared by CREA and NPC_ records.\n    struct Transport\n    {\n\n        struct Dest\n        {\n            Position    mPos;\n            std::string mCellName;\n        };\n\n        std::vector<Dest> mList;\n\n        /// Load one destination, assumes the subrecord name was already read\n        void add(ESMReader &esm);\n\n        void save(ESMWriter &esm) const;\n\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/util.hpp",
    "content": "#ifndef OPENMW_ESM_UTIL_H\n#define OPENMW_ESM_UTIL_H\n\n#include <osg/Vec3f>\n#include <osg/Quat>\n\nnamespace ESM\n{\n\n// format 0, savegames only\n\nstruct Quaternion\n{\n    float mValues[4];\n\n    Quaternion() {}\n\n    Quaternion(const osg::Quat& q)\n    {\n        mValues[0] = q.w();\n        mValues[1] = q.x();\n        mValues[2] = q.y();\n        mValues[3] = q.z();\n    }\n\n    operator osg::Quat () const\n    {\n        return osg::Quat(mValues[1], mValues[2], mValues[3], mValues[0]);\n    }\n};\n\nstruct Vector3\n{\n    float mValues[3];\n\n    Vector3() {}\n\n    Vector3(const osg::Vec3f& v)\n    {\n        mValues[0] = v.x();\n        mValues[1] = v.y();\n        mValues[2] = v.z();\n    }\n\n    operator osg::Vec3f () const\n    {\n        return osg::Vec3f(mValues[0], mValues[1], mValues[2]);\n    }\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/variant.cpp",
    "content": "#include \"variant.hpp\"\n\n#include <cassert>\n#include <stdexcept>\n\n#include \"esmreader.hpp\"\n#include \"variantimp.hpp\"\n\n#include \"defs.hpp\"\n\nnamespace\n{\n    const uint32_t STRV = ESM::FourCC<'S','T','R','V'>::value;\n    const uint32_t INTV = ESM::FourCC<'I','N','T','V'>::value;\n    const uint32_t FLTV = ESM::FourCC<'F','L','T','V'>::value;\n    const uint32_t STTV = ESM::FourCC<'S','T','T','V'>::value;\n\n    template <typename T, bool orDefault = false>\n    struct GetValue\n    {\n        T operator()(int value) const { return static_cast<T>(value); }\n\n        T operator()(float value) const { return static_cast<T>(value); }\n\n        template <typename V>\n        T operator()(const V&) const\n        {\n            if constexpr (orDefault)\n                return T {};\n            else\n                throw std::runtime_error(\"cannot convert variant\");\n        }\n    };\n\n    template <typename T>\n    struct SetValue\n    {\n        T mValue;\n\n        explicit SetValue(T value) : mValue(value) {}\n\n        void operator()(int& value) const { value = static_cast<int>(mValue); }\n\n        void operator()(float& value) const { value = static_cast<float>(mValue); }\n\n        template <typename V>\n        void operator()(V&) const { throw std::runtime_error(\"cannot convert variant\"); }\n    };\n}\n\nstd::string ESM::Variant::getString() const\n{\n    return std::get<std::string>(mData);\n}\n\nint ESM::Variant::getInteger() const\n{\n    return std::visit(GetValue<int>{}, mData);\n}\n\nfloat ESM::Variant::getFloat() const\n{\n    return std::visit(GetValue<float>{}, mData);\n}\n\nvoid ESM::Variant::read (ESMReader& esm, Format format)\n{\n    // type\n    VarType type = VT_Unknown;\n\n    if (format==Format_Global)\n    {\n        std::string typeId = esm.getHNString (\"FNAM\");\n\n        if (typeId == \"s\")\n            type = VT_Short;\n        else if (typeId == \"l\")\n            type = VT_Long;\n        else if (typeId == \"f\")\n            type = VT_Float;\n        else\n            esm.fail (\"illegal global variable type \" + typeId);\n    }\n    else if (format==Format_Gmst)\n    {\n        if (!esm.hasMoreSubs())\n        {\n            type = VT_None;\n        }\n        else\n        {\n            esm.getSubName();\n            NAME name = esm.retSubName();\n\n\n\n            if (name==STRV)\n            {\n                type = VT_String;\n            }\n            else if (name==INTV)\n            {\n                type = VT_Int;\n            }\n            else if (name==FLTV)\n            {\n                type = VT_Float;\n            }\n            else\n                esm.fail (\"invalid subrecord: \" + name.toString());\n        }\n    }\n    else if (format == Format_Info)\n    {\n        esm.getSubName();\n        NAME name = esm.retSubName();\n\n        if (name==INTV)\n        {\n            type = VT_Int;\n        }\n        else if (name==FLTV)\n        {\n            type = VT_Float;\n        }\n        else\n            esm.fail (\"invalid subrecord: \" + name.toString());\n    }\n    else if (format == Format_Local)\n    {\n        esm.getSubName();\n        NAME name = esm.retSubName();\n\n        if (name==INTV)\n        {\n            type = VT_Int;\n        }\n        else if (name==FLTV)\n        {\n            type = VT_Float;\n        }\n        else if (name==STTV)\n        {\n            type = VT_Short;\n        }\n        else\n            esm.fail (\"invalid subrecord: \" + name.toString());\n    }\n\n    setType (type);\n\n    std::visit(ReadESMVariantValue {esm, format, mType}, mData);\n}\n\nvoid ESM::Variant::write (ESMWriter& esm, Format format) const\n{\n    if (mType==VT_Unknown)\n    {\n        throw std::runtime_error (\"can not serialise variant of unknown type\");\n    }\n    else if (mType==VT_None)\n    {\n        if (format==Format_Global)\n            throw std::runtime_error (\"can not serialise variant of type none to global format\");\n\n        if (format==Format_Info)\n            throw std::runtime_error (\"can not serialise variant of type none to info format\");\n\n        if (format==Format_Local)\n            throw std::runtime_error (\"can not serialise variant of type none to local format\");\n\n        // nothing to do here for GMST format\n    }\n    else\n        std::visit(WriteESMVariantValue {esm, format, mType}, mData);\n}\n\nvoid ESM::Variant::write (std::ostream& stream) const\n{\n    switch (mType)\n    {\n        case VT_Unknown:\n\n            stream << \"variant unknown\";\n            break;\n\n        case VT_None:\n\n            stream << \"variant none\";\n            break;\n\n        case VT_Short:\n\n            stream << \"variant short: \" << std::get<int>(mData);\n            break;\n\n        case VT_Int:\n\n            stream << \"variant int: \" << std::get<int>(mData);\n            break;\n\n        case VT_Long:\n\n            stream << \"variant long: \" << std::get<int>(mData);\n            break;\n\n        case VT_Float:\n\n            stream << \"variant float: \" << std::get<float>(mData);\n            break;\n\n        case VT_String:\n\n            stream << \"variant string: \\\"\" << std::get<std::string>(mData) << \"\\\"\";\n            break;\n    }\n}\n\nvoid ESM::Variant::setType (VarType type)\n{\n    if (type!=mType)\n    {\n        switch (type)\n        {\n            case VT_Unknown:\n            case VT_None:\n                mData = std::monostate {};\n                break;\n\n            case VT_Short:\n            case VT_Int:\n            case VT_Long:\n                mData = std::visit(GetValue<int, true>{}, mData);\n                break;\n\n            case VT_Float:\n                mData = std::visit(GetValue<float, true>{}, mData);\n                break;\n\n            case VT_String:\n                mData = std::string {};\n                break;\n        }\n\n        mType = type;\n    }\n}\n\nvoid ESM::Variant::setString (const std::string& value)\n{\n    std::get<std::string>(mData) = value;\n}\n\nvoid ESM::Variant::setString (std::string&& value)\n{\n    std::get<std::string>(mData) = std::move(value);\n}\n\nvoid ESM::Variant::setInteger (int value)\n{\n    std::visit(SetValue(value), mData);\n}\n\nvoid ESM::Variant::setFloat (float value)\n{\n    std::visit(SetValue(value), mData);\n}\n\nstd::ostream& ESM::operator<< (std::ostream& stream, const Variant& value)\n{\n    value.write (stream);\n    return stream;\n}\n"
  },
  {
    "path": "components/esm/variant.hpp",
    "content": "#ifndef OPENMW_ESM_VARIANT_H\n#define OPENMW_ESM_VARIANT_H\n\n#include <string>\n#include <iosfwd>\n#include <variant>\n#include <tuple>\n\nnamespace ESM\n{\n    class ESMReader;\n    class ESMWriter;\n\n    enum VarType\n    {\n        VT_Unknown = 0,\n        VT_None,\n        VT_Short, // stored as a float, kinda\n        VT_Int,\n        VT_Long, // stored as a float\n        VT_Float,\n        VT_String\n    };\n\n    class Variant\n    {\n            VarType mType;\n            std::variant<std::monostate, int, float, std::string> mData;\n\n        public:\n\n            enum Format\n            {\n                Format_Global,\n                Format_Gmst,\n                Format_Info,\n                Format_Local // local script variables in save game files\n            };\n\n            Variant() : mType (VT_None), mData (std::monostate{}) {}\n\n            explicit Variant(const std::string& value) : mType(VT_String), mData(value) {}\n\n            explicit Variant(std::string&& value) : mType(VT_String), mData(std::move(value)) {}\n\n            explicit Variant(int value) : mType(VT_Long), mData(value) {}\n\n            explicit Variant(float value) : mType(VT_Float), mData(value) {}\n\n            VarType getType() const { return mType; }\n\n            std::string getString() const;\n            ///< Will throw an exception, if value can not be represented as a string.\n\n            int getInteger() const;\n            ///< Will throw an exception, if value can not be represented as an integer (implicit\n            /// casting of float values is permitted).\n\n            float getFloat() const;\n            ///< Will throw an exception, if value can not be represented as a float value.\n\n            void read (ESMReader& esm, Format format);\n\n            void write (ESMWriter& esm, Format format) const;\n\n            void write (std::ostream& stream) const;\n            ///< Write in text format.\n\n            void setType (VarType type);\n\n            void setString (const std::string& value);\n            ///< Will throw an exception, if type is not compatible with string.\n\n            void setString (std::string&& value);\n            ///< Will throw an exception, if type is not compatible with string.\n\n            void setInteger (int value);\n            ///< Will throw an exception, if type is not compatible with integer.\n\n            void setFloat (float value);\n            ///< Will throw an exception, if type is not compatible with float.\n\n            friend bool operator==(const Variant& left, const Variant& right)\n            {\n                return std::tie(left.mType, left.mData) == std::tie(right.mType, right.mData);\n            }\n\n            friend bool operator!=(const Variant& left, const Variant& right)\n            {\n                return !(left == right);\n            }\n    };\n\n    std::ostream& operator<<(std::ostream& stream, const Variant& value);\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/variantimp.cpp",
    "content": "#include \"variantimp.hpp\"\n\n#include <stdexcept>\n#include <cmath>\n\n#include \"esmreader.hpp\"\n#include \"esmwriter.hpp\"\n\nvoid ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, std::string& out)\n{\n    if (type!=VT_String)\n        throw std::logic_error (\"not a string type\");\n\n    if (format==Variant::Format_Global)\n        esm.fail (\"global variables of type string not supported\");\n\n    if (format==Variant::Format_Info)\n        esm.fail (\"info variables of type string not supported\");\n\n    if (format==Variant::Format_Local)\n        esm.fail (\"local variables of type string not supported\");\n\n    // GMST\n    out = esm.getHString();\n}\n\nvoid ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, const std::string& in)\n{\n    if (type!=VT_String)\n        throw std::logic_error (\"not a string type\");\n\n    if (format==Variant::Format_Global)\n        throw std::runtime_error (\"global variables of type string not supported\");\n\n    if (format==Variant::Format_Info)\n        throw std::runtime_error (\"info variables of type string not supported\");\n\n    if (format==Variant::Format_Local)\n        throw std::runtime_error (\"local variables of type string not supported\");\n\n    // GMST\n    esm.writeHNString(\"STRV\", in);\n}\n\nvoid ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, int& out)\n{\n    if (type!=VT_Short && type!=VT_Long && type!=VT_Int)\n        throw std::logic_error (\"not an integer type\");\n\n    if (format==Variant::Format_Global)\n    {\n        float value;\n        esm.getHNT (value, \"FLTV\");\n\n        if (type==VT_Short)\n            if (std::isnan(value))\n                out = 0;\n            else\n                out = static_cast<short> (value);\n        else if (type==VT_Long)\n            out = static_cast<int> (value);\n        else\n            esm.fail (\"unsupported global variable integer type\");\n    }\n    else if (format==Variant::Format_Gmst || format==Variant::Format_Info)\n    {\n        if (type!=VT_Int)\n        {\n            std::ostringstream stream;\n            stream\n                << \"unsupported \" <<(format==Variant::Format_Gmst ? \"gmst\" : \"info\")\n                << \" variable integer type\";\n            esm.fail (stream.str());\n        }\n\n        esm.getHT(out);\n    }\n    else if (format==Variant::Format_Local)\n    {\n        if (type==VT_Short)\n        {\n            short value;\n            esm.getHT(value);\n            out = value;\n        }\n        else if (type==VT_Int)\n        {\n            esm.getHT(out);\n        }\n        else\n            esm.fail(\"unsupported local variable integer type\");\n    }\n}\n\nvoid ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, int in)\n{\n    if (type!=VT_Short && type!=VT_Long && type!=VT_Int)\n        throw std::logic_error (\"not an integer type\");\n\n    if (format==Variant::Format_Global)\n    {\n        if (type==VT_Short || type==VT_Long)\n        {\n            float value = static_cast<float>(in);\n            esm.writeHNString (\"FNAM\", type==VT_Short ? \"s\" : \"l\");\n            esm.writeHNT (\"FLTV\", value);\n        }\n        else\n            throw std::runtime_error (\"unsupported global variable integer type\");\n    }\n    else if (format==Variant::Format_Gmst || format==Variant::Format_Info)\n    {\n        if (type!=VT_Int)\n        {\n            std::ostringstream stream;\n            stream\n                << \"unsupported \" <<(format==Variant::Format_Gmst ? \"gmst\" : \"info\")\n                << \" variable integer type\";\n            throw std::runtime_error (stream.str());\n        }\n\n        esm.writeHNT(\"INTV\", in);\n    }\n    else if (format==Variant::Format_Local)\n    {\n        if (type==VT_Short)\n            esm.writeHNT(\"STTV\", static_cast<short>(in));\n        else if (type == VT_Int)\n            esm.writeHNT(\"INTV\", in);\n        else\n            throw std::runtime_error(\"unsupported local variable integer type\");\n    }\n}\n\nvoid ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, float& out)\n{\n    if (type!=VT_Float)\n        throw std::logic_error (\"not a float type\");\n\n    if (format==Variant::Format_Global)\n    {\n        esm.getHNT(out, \"FLTV\");\n    }\n    else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local)\n    {\n        esm.getHT(out);\n    }\n}\n\nvoid ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, float in)\n{\n    if (type!=VT_Float)\n        throw std::logic_error (\"not a float type\");\n\n    if (format==Variant::Format_Global)\n    {\n        esm.writeHNString (\"FNAM\", \"f\");\n        esm.writeHNT(\"FLTV\", in);\n    }\n    else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local)\n    {\n        esm.writeHNT(\"FLTV\", in);\n    }\n}\n"
  },
  {
    "path": "components/esm/variantimp.hpp",
    "content": "#ifndef OPENMW_ESM_VARIANTIMP_H\n#define OPENMW_ESM_VARIANTIMP_H\n\n#include <string>\n#include <functional>\n\n#include \"variant.hpp\"\n\nnamespace ESM\n{\n    void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, std::string& value);\n\n    void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, float& value);\n\n    void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, int& value);\n\n    void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, const std::string& value);\n\n    void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, float value);\n\n    void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, int value);\n\n    struct ReadESMVariantValue\n    {\n        std::reference_wrapper<ESMReader> mReader;\n        Variant::Format mFormat;\n        VarType mType;\n\n        ReadESMVariantValue(ESMReader& reader, Variant::Format format, VarType type)\n            : mReader(reader), mFormat(format), mType(type) {}\n\n        void operator()(std::monostate) const {}\n\n        template <typename T>\n        void operator()(T& value) const\n        {\n            readESMVariantValue(mReader.get(), mFormat, mType, value);\n        }\n    };\n\n    struct WriteESMVariantValue\n    {\n        std::reference_wrapper<ESMWriter> mWriter;\n        Variant::Format mFormat;\n        VarType mType;\n\n        WriteESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type)\n            : mWriter(writer), mFormat(format), mType(type) {}\n\n        void operator()(std::monostate) const {}\n\n        template <typename T>\n        void operator()(const T& value) const\n        {\n            writeESMVariantValue(mWriter.get(), mFormat, mType, value);\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/esm/weatherstate.cpp",
    "content": "#include \"weatherstate.hpp\"\r\n\r\n#include \"esmreader.hpp\"\r\n#include \"esmwriter.hpp\"\r\n\r\nnamespace\r\n{\r\n    const char* currentRegionRecord     = \"CREG\";\r\n    const char* timePassedRecord        = \"TMPS\";\r\n    const char* fastForwardRecord       = \"FAST\";\r\n    const char* weatherUpdateTimeRecord = \"WUPD\";\r\n    const char* transitionFactorRecord  = \"TRFC\";\r\n    const char* currentWeatherRecord    = \"CWTH\";\r\n    const char* nextWeatherRecord       = \"NWTH\";\r\n    const char* queuedWeatherRecord     = \"QWTH\";\r\n    const char* regionNameRecord        = \"RGNN\";\r\n    const char* regionWeatherRecord     = \"RGNW\";\r\n    const char* regionChanceRecord      = \"RGNC\";\r\n}\r\n\r\nnamespace ESM\r\n{\r\n    void WeatherState::load(ESMReader& esm)\r\n    {\r\n        mCurrentRegion = esm.getHNString(currentRegionRecord);\r\n        esm.getHNT(mTimePassed, timePassedRecord);\r\n        esm.getHNT(mFastForward, fastForwardRecord);\r\n        esm.getHNT(mWeatherUpdateTime, weatherUpdateTimeRecord);\r\n        esm.getHNT(mTransitionFactor, transitionFactorRecord);\r\n        esm.getHNT(mCurrentWeather, currentWeatherRecord);\r\n        esm.getHNT(mNextWeather, nextWeatherRecord);\r\n        esm.getHNT(mQueuedWeather, queuedWeatherRecord);\r\n\r\n        while (esm.isNextSub(regionNameRecord))\r\n        {\r\n            std::string regionID = esm.getHString();\r\n            RegionWeatherState region;\r\n            esm.getHNT(region.mWeather, regionWeatherRecord);\r\n            while (esm.isNextSub(regionChanceRecord))\r\n            {\r\n                char chance;\r\n                esm.getHT(chance);\r\n                region.mChances.push_back(chance);\r\n            }\r\n\r\n            mRegions.insert(std::make_pair(regionID, region));\r\n        }\r\n    }\r\n\r\n    void WeatherState::save(ESMWriter& esm) const\r\n    {\r\n        esm.writeHNCString(currentRegionRecord, mCurrentRegion.c_str());\r\n        esm.writeHNT(timePassedRecord, mTimePassed);\r\n        esm.writeHNT(fastForwardRecord, mFastForward);\r\n        esm.writeHNT(weatherUpdateTimeRecord, mWeatherUpdateTime);\r\n        esm.writeHNT(transitionFactorRecord, mTransitionFactor);\r\n        esm.writeHNT(currentWeatherRecord, mCurrentWeather);\r\n        esm.writeHNT(nextWeatherRecord, mNextWeather);\r\n        esm.writeHNT(queuedWeatherRecord, mQueuedWeather);\r\n\r\n        std::map<std::string, RegionWeatherState>::const_iterator it = mRegions.begin();\r\n        for(; it != mRegions.end(); ++it)\r\n        {\r\n            esm.writeHNCString(regionNameRecord, it->first.c_str());\r\n            esm.writeHNT(regionWeatherRecord, it->second.mWeather);\r\n            for(size_t i = 0; i < it->second.mChances.size(); ++i)\r\n            {\r\n                esm.writeHNT(regionChanceRecord, it->second.mChances[i]);\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "components/esm/weatherstate.hpp",
    "content": "#ifndef OPENMW_ESM_WEATHERSTATE_H\r\n#define OPENMW_ESM_WEATHERSTATE_H\r\n\r\n#include <map>\r\n#include <string>\r\n#include <vector>\r\n\r\nnamespace ESM\r\n{\r\n    class ESMReader;\r\n    class ESMWriter;\r\n\r\n    struct RegionWeatherState\r\n    {\r\n        int mWeather;\r\n        std::vector<char> mChances;\r\n    };\r\n\r\n    struct WeatherState\r\n    {\r\n        std::string mCurrentRegion;\r\n        float mTimePassed;\r\n        bool mFastForward;\r\n        float mWeatherUpdateTime;\r\n        float mTransitionFactor;\r\n        int mCurrentWeather;\r\n        int mNextWeather;\r\n        int mQueuedWeather;\r\n        std::map<std::string, RegionWeatherState> mRegions;\r\n\r\n        void load(ESMReader& esm);\r\n        void save(ESMWriter& esm) const;\r\n    };\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "components/esmterrain/storage.cpp",
    "content": "#include \"storage.hpp\"\n\n#include <set>\n\n#include <osg/Image>\n#include <osg/Plane>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/resourcehelpers.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/vfs/manager.hpp>\n\nnamespace ESMTerrain\n{\n\n    class LandCache\n    {\n    public:\n        typedef std::map<std::pair<int, int>, osg::ref_ptr<const LandObject> > Map;\n        Map mMap;\n    };\n\n    LandObject::LandObject()\n        : mLand(nullptr)\n        , mLoadFlags(0)\n    {\n    }\n\n    LandObject::LandObject(const ESM::Land *land, int loadFlags)\n        : mLand(land)\n        , mLoadFlags(loadFlags)\n    {\n        mLand->loadData(mLoadFlags, &mData);\n    }\n\n    LandObject::LandObject(const LandObject &copy, const osg::CopyOp &copyop)\n        : mLand(nullptr)\n        , mLoadFlags(0)\n    {\n    }\n\n    LandObject::~LandObject()\n    {\n    }\n\n    const float defaultHeight = ESM::Land::DEFAULT_HEIGHT;\n\n    Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps)\n        : mVFS(vfs)\n        , mNormalMapPattern(normalMapPattern)\n        , mNormalHeightMapPattern(normalHeightMapPattern)\n        , mAutoUseNormalMaps(autoUseNormalMaps)\n        , mSpecularMapPattern(specularMapPattern)\n        , mAutoUseSpecularMaps(autoUseSpecularMaps)\n    {\n    }\n\n    bool Storage::getMinMaxHeights(float size, const osg::Vec2f &center, float &min, float &max)\n    {\n        assert (size <= 1 && \"Storage::getMinMaxHeights, chunk size should be <= 1 cell\");\n\n        osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f);\n\n        int cellX = static_cast<int>(std::floor(origin.x()));\n        int cellY = static_cast<int>(std::floor(origin.y()));\n\n        int startRow = (origin.x() - cellX) * ESM::Land::LAND_SIZE;\n        int startColumn = (origin.y() - cellY) * ESM::Land::LAND_SIZE;\n\n        int endRow = startRow + size * (ESM::Land::LAND_SIZE-1) + 1;\n        int endColumn = startColumn + size * (ESM::Land::LAND_SIZE-1) + 1;\n\n        osg::ref_ptr<const LandObject> land = getLand (cellX, cellY);\n        const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;\n        if (data)\n        {\n            min = std::numeric_limits<float>::max();\n            max = -std::numeric_limits<float>::max();\n            for (int row=startRow; row<endRow; ++row)\n            {\n                for (int col=startColumn; col<endColumn; ++col)\n                {\n                    float h = data->mHeights[col*ESM::Land::LAND_SIZE+row];\n                    if (h > max)\n                        max = h;\n                    if (h < min)\n                        min = h;\n                }\n            }\n            return true;\n        }\n\n        min = defaultHeight;\n        max = defaultHeight;\n        return false;\n    }\n\n    void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache)\n    {\n        while (col >= ESM::Land::LAND_SIZE-1)\n        {\n            ++cellY;\n            col -= ESM::Land::LAND_SIZE-1;\n        }\n        while (row >= ESM::Land::LAND_SIZE-1)\n        {\n            ++cellX;\n            row -= ESM::Land::LAND_SIZE-1;\n        }\n        while (col < 0)\n        {\n            --cellY;\n            col += ESM::Land::LAND_SIZE-1;\n        }\n        while (row < 0)\n        {\n            --cellX;\n            row += ESM::Land::LAND_SIZE-1;\n        }\n\n        const LandObject* land = getLand(cellX, cellY, cache);\n        const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr;\n        if (data)\n        {\n            normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];\n            normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];\n            normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];\n            normal.normalize();\n        }\n        else\n            normal = osg::Vec3f(0,0,1);\n    }\n\n    void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row, LandCache& cache)\n    {\n        osg::Vec3f n1,n2,n3,n4;\n        fixNormal(n1, cellX, cellY, col+1, row, cache);\n        fixNormal(n2, cellX, cellY, col-1, row, cache);\n        fixNormal(n3, cellX, cellY, col, row+1, cache);\n        fixNormal(n4, cellX, cellY, col, row-1, cache);\n        normal = (n1+n2+n3+n4);\n        normal.normalize();\n    }\n\n    void Storage::fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache)\n    {\n        if (col == ESM::Land::LAND_SIZE-1)\n        {\n            ++cellY;\n            col = 0;\n        }\n        if (row == ESM::Land::LAND_SIZE-1)\n        {\n            ++cellX;\n            row = 0;\n        }\n\n        const LandObject* land = getLand(cellX, cellY, cache);\n        const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr;\n        if (data)\n        {\n            color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3];\n            color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1];\n            color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2];\n        }\n        else\n        {\n            color.r() = 255;\n            color.g() = 255;\n            color.b() = 255;\n        }\n    }\n\n    void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center,\n                                            osg::ref_ptr<osg::Vec3Array> positions,\n                                            osg::ref_ptr<osg::Vec3Array> normals,\n                                            osg::ref_ptr<osg::Vec4ubArray> colours)\n    {\n        // LOD level n means every 2^n-th vertex is kept\n        size_t increment = static_cast<size_t>(1) << lodLevel;\n\n        osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f);\n\n        int startCellX = static_cast<int>(std::floor(origin.x()));\n        int startCellY = static_cast<int>(std::floor(origin.y()));\n\n        size_t numVerts = static_cast<size_t>(size*(ESM::Land::LAND_SIZE - 1) / increment + 1);\n\n        positions->resize(numVerts*numVerts);\n        normals->resize(numVerts*numVerts);\n        colours->resize(numVerts*numVerts);\n\n        osg::Vec3f normal;\n        osg::Vec4ub color;\n\n        float vertY = 0;\n        float vertX = 0;\n\n        LandCache cache;\n\n        bool alteration = useAlteration();\n\n        float vertY_ = 0; // of current cell corner\n        for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY)\n        {\n            float vertX_ = 0; // of current cell corner\n            for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX)\n            {\n                const LandObject* land = getLand(cellX, cellY, cache);\n                const ESM::Land::LandData *heightData = nullptr;\n                const ESM::Land::LandData *normalData = nullptr;\n                const ESM::Land::LandData *colourData = nullptr;\n                if (land)\n                {\n                    heightData = land->getData(ESM::Land::DATA_VHGT);\n                    normalData = land->getData(ESM::Land::DATA_VNML);\n                    colourData = land->getData(ESM::Land::DATA_VCLR);\n                }\n\n                int rowStart = 0;\n                int colStart = 0;\n                // Skip the first row / column unless we're at a chunk edge,\n                // since this row / column is already contained in a previous cell\n                // This is only relevant if we're creating a chunk spanning multiple cells\n                if (vertY_ != 0)\n                    colStart += increment;\n                if (vertX_ != 0)\n                    rowStart += increment;\n\n                // Only relevant for chunks smaller than (contained in) one cell\n                rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE;\n                colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE;\n                int rowEnd = std::min(static_cast<int>(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast<int>(ESM::Land::LAND_SIZE));\n                int colEnd = std::min(static_cast<int>(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast<int>(ESM::Land::LAND_SIZE));\n\n                vertY = vertY_;\n                for (int col=colStart; col<colEnd; col += increment)\n                {\n                    vertX = vertX_;\n                    for (int row=rowStart; row<rowEnd; row += increment)\n                    {\n                        int srcArrayIndex = col*ESM::Land::LAND_SIZE*3+row*3;\n\n                        assert(row >= 0 && row < ESM::Land::LAND_SIZE);\n                        assert(col >= 0 && col < ESM::Land::LAND_SIZE);\n\n                        assert (vertX < numVerts);\n                        assert (vertY < numVerts);\n\n                        float height = defaultHeight;\n                        if (heightData)\n                            height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row];\n                        if (alteration)\n                            height += getAlteredHeight(col, row);\n                        (*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)]\n                            = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,\n                                         (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,\n                                         height);\n\n                        if (normalData)\n                        {\n                            for (int i=0; i<3; ++i)\n                                normal[i] = normalData->mNormals[srcArrayIndex+i];\n\n                            normal.normalize();\n                        }\n                        else\n                            normal = osg::Vec3f(0,0,1);\n\n                        // Normals apparently don't connect seamlessly between cells\n                        if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)\n                            fixNormal(normal, cellX, cellY, col, row, cache);\n\n                        // some corner normals appear to be complete garbage (z < 0)\n                        if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1))\n                            averageNormal(normal, cellX, cellY, col, row, cache);\n\n                        assert(normal.z() > 0);\n\n                        (*normals)[static_cast<unsigned int>(vertX*numVerts + vertY)] = normal;\n\n                        if (colourData)\n                        {\n                            for (int i=0; i<3; ++i)\n                                color[i] = colourData->mColours[srcArrayIndex+i];\n                        }\n                        else\n                        {\n                            color.r() = 255;\n                            color.g() = 255;\n                            color.b() = 255;\n                        }\n                        if (alteration)\n                            adjustColor(col, row, heightData, color); //Does nothing by default, override in OpenMW-CS\n\n                        // Unlike normals, colors mostly connect seamlessly between cells, but not always...\n                        if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)\n                            fixColour(color, cellX, cellY, col, row, cache);\n\n                        color.a() = 255;\n\n                        (*colours)[static_cast<unsigned int>(vertX*numVerts + vertY)] = color;\n\n                        ++vertX;\n                    }\n                    ++vertY;\n                }\n                vertX_ = vertX;\n            }\n            vertY_ = vertY;\n\n            assert(vertX_ == numVerts); // Ensure we covered whole area\n        }\n        assert(vertY_ == numVerts);  // Ensure we covered whole area\n    }\n\n    Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY,\n                                           int x, int y, LandCache& cache)\n    {\n        // For the first/last row/column, we need to get the texture from the neighbour cell\n        // to get consistent blending at the borders\n        --x;\n        if (x < 0)\n        {\n            --cellX;\n            x += ESM::Land::LAND_TEXTURE_SIZE;\n        }\n        while (x >= ESM::Land::LAND_TEXTURE_SIZE)\n        {\n            ++cellX;\n            x -= ESM::Land::LAND_TEXTURE_SIZE;\n        }\n        while (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not?\n        {\n            ++cellY;\n            y -= ESM::Land::LAND_TEXTURE_SIZE;\n        }\n\n        assert(x<ESM::Land::LAND_TEXTURE_SIZE);\n        assert(y<ESM::Land::LAND_TEXTURE_SIZE);\n\n        const LandObject* land = getLand(cellX, cellY, cache);\n\n        const ESM::Land::LandData *data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr;\n        if (data)\n        {\n            int tex = data->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];\n            if (tex == 0)\n                return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin\n            return std::make_pair(tex, land->getPlugin());\n        }\n        return std::make_pair(0,0);\n    }\n\n    std::string Storage::getTextureName(UniqueTextureId id)\n    {\n        static constexpr char defaultTexture[] = \"textures\\\\_land_default.dds\";\n        if (id.first == 0)\n            return defaultTexture; // Not sure if the default texture really is hardcoded?\n\n        // NB: All vtex ids are +1 compared to the ltex ids\n        const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second);\n        if (!ltex)\n        {\n            Log(Debug::Warning) << \"Warning: Unable to find land texture index \" << id.first-1 << \" in plugin \" << id.second << \", using default texture instead\";\n            return defaultTexture;\n        }\n\n        // this is needed due to MWs messed up texture handling\n        std::string texture = Misc::ResourceHelpers::correctTexturePath(ltex->mTexture, mVFS);\n\n        return texture;\n    }\n\n    void Storage::getBlendmaps(float chunkSize, const osg::Vec2f &chunkCenter, ImageVector &blendmaps, std::vector<Terrain::LayerInfo> &layerList)\n    {\n        osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f);\n        int cellX = static_cast<int>(std::floor(origin.x()));\n        int cellY = static_cast<int>(std::floor(origin.y()));\n\n        int realTextureSize = ESM::Land::LAND_TEXTURE_SIZE+1; // add 1 to wrap around next cell\n\n        int rowStart = (origin.x() - cellX) * realTextureSize;\n        int colStart = (origin.y() - cellY) * realTextureSize;\n\n        const int blendmapSize = (realTextureSize-1) * chunkSize + 1;\n        // We need to upscale the blendmap 2x with nearest neighbor sampling to look like Vanilla\n        const int imageScaleFactor = 2;\n        const int blendmapImageSize = blendmapSize * imageScaleFactor;\n\n        LandCache cache;\n        std::map<UniqueTextureId, unsigned int> textureIndicesMap;\n\n        for (int y=0; y<blendmapSize; y++)\n        {\n            for (int x=0; x<blendmapSize; x++)\n            {\n                UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);\n                std::map<UniqueTextureId, unsigned int>::iterator found = textureIndicesMap.find(id);\n                if (found == textureIndicesMap.end())\n                {\n                    unsigned int layerIndex = layerList.size();\n                    Terrain::LayerInfo info = getLayerInfo(getTextureName(id));\n\n                    // look for existing diffuse map, which may be present when several plugins use the same texture\n                    for (unsigned int i=0; i<layerList.size(); ++i)\n                    {\n                        if (layerList[i].mDiffuseMap == info.mDiffuseMap)\n                        {\n                            layerIndex = i;\n                            break;\n                        }\n                    }\n\n                    found = textureIndicesMap.emplace(id, layerIndex).first;\n\n                    if (layerIndex >= layerList.size())\n                    {\n                        osg::ref_ptr<osg::Image> image (new osg::Image);\n                        image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);\n                        unsigned char* pData = image->data();\n                        memset(pData, 0, image->getTotalDataSize());\n                        blendmaps.emplace_back(image);\n                        layerList.emplace_back(info);\n                    }\n                }\n                unsigned int layerIndex = found->second;\n                unsigned char* pData = blendmaps[layerIndex]->data();\n                int realY = (blendmapSize - y - 1)*imageScaleFactor;\n                int realX = x*imageScaleFactor;\n                pData[((realY+0)*blendmapImageSize + realX + 0)] = 255;\n                pData[((realY+1)*blendmapImageSize + realX + 0)] = 255;\n                pData[((realY+0)*blendmapImageSize + realX + 1)] = 255;\n                pData[((realY+1)*blendmapImageSize + realX + 1)] = 255;\n            }\n        }\n\n        if (blendmaps.size() == 1)\n            blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend\n    }\n\n    float Storage::getHeightAt(const osg::Vec3f &worldPos)\n    {\n        int cellX = static_cast<int>(std::floor(worldPos.x() / float(Constants::CellSizeInUnits)));\n        int cellY = static_cast<int>(std::floor(worldPos.y() / float(Constants::CellSizeInUnits)));\n\n        osg::ref_ptr<const LandObject> land = getLand(cellX, cellY);\n        if (!land)\n            return defaultHeight;\n\n        const ESM::Land::LandData* data = land->getData(ESM::Land::DATA_VHGT);\n        if (!data)\n            return defaultHeight;\n\n        // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition\n\n        // Normalized position in the cell\n        float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits);\n        float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits);\n\n        // get left / bottom points (rounded down)\n        float factor = ESM::Land::LAND_SIZE - 1.0f;\n        float invFactor = 1.0f / factor;\n\n        int startX = static_cast<int>(nX * factor);\n        int startY = static_cast<int>(nY * factor);\n        int endX = startX + 1;\n        int endY = startY + 1;\n\n        endX = std::min(endX, ESM::Land::LAND_SIZE-1);\n        endY = std::min(endY, ESM::Land::LAND_SIZE-1);\n\n        // now get points in terrain space (effectively rounding them to boundaries)\n        float startXTS = startX * invFactor;\n        float startYTS = startY * invFactor;\n        float endXTS = endX * invFactor;\n        float endYTS = endY * invFactor;\n\n        // get parametric from start coord to next point\n        float xParam = (nX - startXTS) * factor;\n        float yParam = (nY - startYTS) * factor;\n\n        /* For even / odd tri strip rows, triangles are this shape:\n        even     odd\n        3---2   3---2\n        | / |   | \\ |\n        0---1   0---1\n        */\n\n        // Build all 4 positions in normalized cell space, using point-sampled height\n        osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / float(Constants::CellSizeInUnits));\n        osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / float(Constants::CellSizeInUnits));\n        osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / float(Constants::CellSizeInUnits));\n        osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / float(Constants::CellSizeInUnits));\n        // define this plane in terrain space\n        osg::Plane plane;\n        // FIXME: deal with differing triangle alignment\n        if (true)\n        {\n            // odd row\n            bool secondTri = ((1.0 - yParam) > xParam);\n            if (secondTri)\n                plane = osg::Plane(v0, v1, v3);\n            else\n                plane = osg::Plane(v1, v2, v3);\n        }\n        /*\n        else\n        {\n            // even row\n            bool secondTri = (yParam > xParam);\n            if (secondTri)\n                plane.redefine(v0, v2, v3);\n            else\n                plane.redefine(v0, v1, v2);\n        }\n        */\n\n        // Solve plane equation for z\n        return (-plane.getNormal().x() * nX\n                -plane.getNormal().y() * nY\n                - plane[3]) / plane.getNormal().z() * Constants::CellSizeInUnits;\n\n    }\n\n    const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache)\n    {\n        LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY));\n        if (found != cache.mMap.end())\n            return found->second;\n        else\n        {\n            found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first;\n            return found->second;\n        }\n    }\n\n    void Storage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const\n    {\n    }\n\n    float Storage::getAlteredHeight(int col, int row) const\n    {\n        return 0;\n    }\n\n    Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture)\n    {\n        std::lock_guard<std::mutex> lock(mLayerInfoMutex);\n\n        // Already have this cached?\n        std::map<std::string, Terrain::LayerInfo>::iterator found = mLayerInfoMap.find(texture);\n        if (found != mLayerInfoMap.end())\n            return found->second;\n\n        Terrain::LayerInfo info;\n        info.mParallax = false;\n        info.mSpecular = false;\n        info.mDiffuseMap = texture;\n\n        if (mAutoUseNormalMaps)\n        {\n            std::string texture_ = texture;\n            Misc::StringUtils::replaceLast(texture_, \".\", mNormalHeightMapPattern + \".\");\n            if (mVFS->exists(texture_))\n            {\n                info.mNormalMap = texture_;\n                info.mParallax = true;\n            }\n            else\n            {\n                texture_ = texture;\n                Misc::StringUtils::replaceLast(texture_, \".\", mNormalMapPattern + \".\");\n                if (mVFS->exists(texture_))\n                    info.mNormalMap = texture_;\n            }\n        }\n\n        if (mAutoUseSpecularMaps)\n        {\n            std::string texture_ = texture;\n            Misc::StringUtils::replaceLast(texture_, \".\", mSpecularMapPattern + \".\");\n            if (mVFS->exists(texture_))\n            {\n                info.mDiffuseMap = texture_;\n                info.mSpecular = true;\n            }\n        }\n\n        mLayerInfoMap[texture] = info;\n\n        return info;\n    }\n\n    float Storage::getCellWorldSize()\n    {\n        return static_cast<float>(ESM::Land::REAL_SIZE);\n    }\n\n    int Storage::getCellVertices()\n    {\n        return ESM::Land::LAND_SIZE;\n    }\n\n    int Storage::getBlendmapScale(float chunkSize)\n    {\n        return ESM::Land::LAND_TEXTURE_SIZE*chunkSize;\n    }\n\n}\n"
  },
  {
    "path": "components/esmterrain/storage.hpp",
    "content": "#ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H\n#define COMPONENTS_ESM_TERRAIN_STORAGE_H\n\n#include <cassert>\n#include <mutex>\n\n#include <components/terrain/storage.hpp>\n\n#include <components/esm/loadland.hpp>\n#include <components/esm/loadltex.hpp>\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace ESMTerrain\n{\n\n    class LandCache;\n\n    /// @brief Wrapper around Land Data with reference counting. The wrapper needs to be held as long as the data is still in use\n    class LandObject : public osg::Object\n    {\n    public:\n        LandObject();\n        LandObject(const ESM::Land* land, int loadFlags);\n        LandObject(const LandObject& copy, const osg::CopyOp& copyop);\n        virtual ~LandObject();\n\n        META_Object(ESMTerrain, LandObject)\n\n        inline const ESM::Land::LandData* getData(int flags) const\n        {\n            if ((mData.mDataLoaded & flags) != flags)\n                return nullptr;\n            return &mData;\n        }\n\n        inline int getPlugin() const\n        {\n            return mLand->mPlugin;\n        }\n\n    private:\n        const ESM::Land* mLand;\n        int mLoadFlags;\n\n        ESM::Land::LandData mData;\n    };\n\n    /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture)\n    ///        into the terrain component, converting it on the fly as needed.\n    class Storage : public Terrain::Storage\n    {\n    public:\n        Storage(const VFS::Manager* vfs, const std::string& normalMapPattern = \"\", const std::string& normalHeightMapPattern = \"\", bool autoUseNormalMaps = false, const std::string& specularMapPattern = \"\", bool autoUseSpecularMaps = false);\n\n        // Not implemented in this class, because we need different Store implementations for game and editor\n        virtual osg::ref_ptr<const LandObject> getLand (int cellX, int cellY)= 0;\n        virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0;\n        /// Get bounds of the whole terrain in cell units\n        void getBounds(float& minX, float& maxX, float& minY, float& maxY) override = 0;\n\n        /// Get the minimum and maximum heights of a terrain region.\n        /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree.\n        ///        Larger chunks can simply merge AABB of children.\n        /// @param size size of the chunk in cell units\n        /// @param center center of the chunk in cell units\n        /// @param min min height will be stored here\n        /// @param max max height will be stored here\n        /// @return true if there was data available for this terrain chunk\n        bool getMinMaxHeights (float size, const osg::Vec2f& center, float& min, float& max) override;\n\n        /// Fill vertex buffers for a terrain chunk.\n        /// @note May be called from background threads. Make sure to only call thread-safe functions from here!\n        /// @note Vertices should be written in row-major order (a row is defined as parallel to the x-axis).\n        ///       The specified positions should be in local space, i.e. relative to the center of the terrain chunk.\n        /// @param lodLevel LOD level, 0 = most detailed\n        /// @param size size of the terrain chunk in cell units\n        /// @param center center of the chunk in cell units\n        /// @param positions buffer to write vertices\n        /// @param normals buffer to write vertex normals\n        /// @param colours buffer to write vertex colours\n        void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center,\n                                osg::ref_ptr<osg::Vec3Array> positions,\n                                osg::ref_ptr<osg::Vec3Array> normals,\n                                osg::ref_ptr<osg::Vec4ubArray> colours) override;\n\n        /// Create textures holding layer blend values for a terrain chunk.\n        /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might\n        ///       have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.\n        /// @note May be called from background threads.\n        /// @param chunkSize size of the terrain chunk in cell units\n        /// @param chunkCenter center of the chunk in cell units\n        /// @param blendmaps created blendmaps will be written here\n        /// @param layerList names of the layer textures used will be written here\n        void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,\n                               std::vector<Terrain::LayerInfo>& layerList) override;\n\n        float getHeightAt (const osg::Vec3f& worldPos) override;\n\n        /// Get the transformation factor for mapping cell units to world units.\n        float getCellWorldSize() override;\n\n        /// Get the number of vertices on one side for each cell. Should be (power of two)+1\n        int getCellVertices() override;\n\n        int getBlendmapScale(float chunkSize) override;\n\n        float getVertexHeight (const ESM::Land::LandData* data, int x, int y)\n        {\n            assert(x < ESM::Land::LAND_SIZE);\n            assert(y < ESM::Land::LAND_SIZE);\n            return data->mHeights[y * ESM::Land::LAND_SIZE + x];\n        }\n\n    private:\n        const VFS::Manager* mVFS;\n\n        inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);\n        inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache);\n        inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);\n\n        inline const LandObject* getLand(int cellX, int cellY, LandCache& cache);\n\n        virtual bool useAlteration() const { return false; }\n        virtual void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const;\n        virtual float getAlteredHeight(int col, int row) const;\n\n        // Since plugins can define new texture palettes, we need to know the plugin index too\n        // in order to retrieve the correct texture name.\n        // pair  <texture id, plugin id>\n        typedef std::pair<short, short> UniqueTextureId;\n\n        inline UniqueTextureId getVtexIndexAt(int cellX, int cellY, int x, int y, LandCache&);\n        std::string getTextureName (UniqueTextureId id);\n\n        std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;\n        std::mutex mLayerInfoMutex;\n\n        std::string mNormalMapPattern;\n        std::string mNormalHeightMapPattern;\n        bool mAutoUseNormalMaps;\n\n        std::string mSpecularMapPattern;\n        bool mAutoUseSpecularMaps;\n\n        Terrain::LayerInfo getLayerInfo(const std::string& texture);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/fallback/fallback.cpp",
    "content": "#include \"fallback.hpp\"\n\n#include <sstream>\n\n#include <components/debug/debuglog.hpp>\n\nnamespace Fallback\n{\n    std::map<std::string,std::string> Map::mFallbackMap;\n\n    void Map::init(const std::map<std::string,std::string>& fallback)\n    {\n        mFallbackMap = fallback;\n    }\n\n    std::string Map::getString(const std::string& fall)\n    {\n        std::map<std::string,std::string>::const_iterator it;\n        if ((it = mFallbackMap.find(fall)) == mFallbackMap.end())\n        {\n            return std::string();\n        }\n        return it->second;\n    }\n\n    float Map::getFloat(const std::string& fall)\n    {\n        const std::string& fallback = getString(fall);\n        if (!fallback.empty())\n        {\n            std::stringstream stream(fallback);\n            float number = 0.f;\n            stream >> number;\n            return number;\n        }\n\n        return 0;\n    }\n\n    int Map::getInt(const std::string& fall)\n    {\n        const std::string& fallback = getString(fall);\n        if (!fallback.empty())\n        {\n            std::stringstream stream(fallback);\n            int number = 0;\n            stream >> number;\n            return number;\n        }\n\n        return 0;\n    }\n\n    bool Map::getBool(const std::string& fall)\n    {\n        const std::string& fallback = getString(fall);\n        return !fallback.empty() && fallback != \"0\";\n    }\n\n    osg::Vec4f Map::getColour(const std::string& fall)\n    {\n        const std::string& sum = getString(fall);\n        if (!sum.empty())\n        {\n            try\n            {\n                std::string ret[3];\n                unsigned int j = 0;\n                for (unsigned int i = 0; i < sum.length(); ++i)\n                {\n                    if(sum[i]==',') j++;\n                    else if (sum[i] != ' ') ret[j]+=sum[i];\n                }\n                return osg::Vec4f(std::stoi(ret[0])/255.f,std::stoi(ret[1])/255.f,std::stoi(ret[2])/255.f, 1.f);    \n            }\n            catch (const std::invalid_argument&)\n            {\n                Log(Debug::Error) << \"Error: '\" << fall << \"' setting value (\" << sum << \") is not a valid color, using middle gray as a fallback\";\n            }\n        }\n\n        return osg::Vec4f(0.5f,0.5f,0.5f,1.f);\n    }\n\n}\n"
  },
  {
    "path": "components/fallback/fallback.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_FALLBACK_H\n#define OPENMW_COMPONENTS_FALLBACK_H\n\n#include <map>\n#include <string>\n\n#include <osg/Vec4f>\n\nnamespace Fallback\n{\n    /// @brief contains settings imported from the Morrowind INI file.\n    class Map\n    {\n            static std::map<std::string,std::string> mFallbackMap;\n        public:\n            static void init(const std::map<std::string,std::string>& fallback);\n\n            static std::string getString(const std::string& fall);\n            static float getFloat(const std::string& fall);\n            static int getInt(const std::string& fall);\n            static bool getBool(const std::string& fall);\n            static osg::Vec4f getColour(const std::string& fall);\n    };\n}\n#endif\n"
  },
  {
    "path": "components/fallback/validate.cpp",
    "content": "#include \"validate.hpp\"\n\nvoid Fallback::validate(boost::any& v, std::vector<std::string> const& tokens, FallbackMap*, int)\n{\n    if (v.empty())\n    {\n        v = boost::any(FallbackMap());\n    }\n\n    FallbackMap *map = boost::any_cast<FallbackMap>(&v);\n\n    for (const auto& token : tokens)\n    {\n        std::string temp = Files::EscapeHashString::processString(token);\n        size_t sep = temp.find(',');\n        if (sep < 1 || sep == temp.length() - 1 || sep == std::string::npos)\n            throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value);\n\n        std::string key(temp.substr(0, sep));\n        std::string value(temp.substr(sep + 1));\n\n        map->mMap[key] = value;\n    }\n}\n"
  },
  {
    "path": "components/fallback/validate.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_FALLBACK_VALIDATE_H\n#define OPENMW_COMPONENTS_FALLBACK_VALIDATE_H\n\n#include <boost/program_options.hpp>\n\n#include <components/files/escape.hpp>\n\n// Parses and validates a fallback map from boost program_options.\n// Note: for boost to pick up the validate function, you need to pull in the namespace e.g.\n// by using namespace Fallback;\n\nnamespace Fallback\n{\n\n    struct FallbackMap {\n        std::map<std::string, std::string> mMap;\n    };\n\n    void validate(boost::any &v, std::vector<std::string> const &tokens, FallbackMap*, int);\n}\n\n#endif\n"
  },
  {
    "path": "components/files/androidpath.cpp",
    "content": "#include \"androidpath.hpp\"\n\n#if defined(__ANDROID__)\n\n#include <jni.h>\n#include <cstdlib>\n#include <cstring>\n#include <pwd.h>\n#include <unistd.h>\n#include <boost/filesystem/fstream.hpp>\n\nstatic const char *g_path_global; //< Path to global directory root, e.g. /data/data/com.libopenmw.openmw\nstatic const char *g_path_user; //< Path to user root, e.g. /sdcard/Android/data/com.libopenmw.openmw\n\n/**\n * \\brief Called by java code to set up directory paths\n */\nextern \"C\" JNIEXPORT void JNICALL Java_ui_activity_GameActivity_getPathToJni(JNIEnv *env, jobject obj, jstring global, jstring user)\n{\n    g_path_global = env->GetStringUTFChars(global, nullptr);\n    g_path_user = env->GetStringUTFChars(user, nullptr);\n}\n\nnamespace Files\n{\n\nAndroidPath::AndroidPath(const std::string& application_name)\n{\n}\n\n// /sdcard/Android/data/com.libopenmw.openmw/config\nboost::filesystem::path AndroidPath::getUserConfigPath() const\n{\n    return boost::filesystem::path(g_path_user) / \"config\";\n}\n\n// /sdcard/Android/data/com.libopenmw.openmw/\n// (so that saves are placed at /sdcard/Android/data/com.libopenmw.openmw/saves)\nboost::filesystem::path AndroidPath::getUserDataPath() const\n{\n    return boost::filesystem::path(g_path_user);\n}\n\n// /data/data/com.libopenmw.openmw/cache\n// (supposed to be \"official\" android cache location)\nboost::filesystem::path AndroidPath::getCachePath() const\n{\n    return boost::filesystem::path(g_path_global) / \"cache\";\n}\n\n// /data/data/com.libopenmw.openmw/files/config\n// (note the addition of \"files\")\nboost::filesystem::path AndroidPath::getGlobalConfigPath() const\n{\n    return boost::filesystem::path(g_path_global) / \"files\" / \"config\";\n}\n\nboost::filesystem::path AndroidPath::getLocalPath() const\n{\n    return boost::filesystem::path(\"./\");\n}\n\n// /sdcard/Android/data/com.libopenmw.openmw\n// (so that the data is at /sdcard/Android/data/com.libopenmw.openmw/data)\nboost::filesystem::path AndroidPath::getGlobalDataPath() const\n{\n    return boost::filesystem::path(g_path_user);\n}\n\nboost::filesystem::path AndroidPath::getInstallPath() const\n{\n    return boost::filesystem::path();\n}\n\n\n} /* namespace Files */\n\n#endif /* defined(__Android__) */\n"
  },
  {
    "path": "components/files/androidpath.hpp",
    "content": "#ifndef COMPONENTS_FILES_ANDROIDPATH_H\n#define COMPONENTS_FILES_ANDROIDPATH_H\n\n#if defined(__ANDROID__)\n\n#include <boost/filesystem.hpp>\n/**\n * \\namespace Files\n */\n\n\nnamespace Files\n{\n\nstruct AndroidPath\n{\n    AndroidPath(const std::string& application_name);\n    \n\n    /**\n     * \\brief Return path to the user directory.\n     */\n    boost::filesystem::path getUserConfigPath() const;\n\n    boost::filesystem::path getUserDataPath() const;\n\n    /**\n     * \\brief Return path to the global (system) directory where config files can be placed.\n     */\n    boost::filesystem::path getGlobalConfigPath() const;\n\n    /**\n     * \\brief Return path to the runtime configuration directory which is the\n     * place where an application was started.\n     */\n    boost::filesystem::path getLocalPath() const;\n\n    /**\n     * \\brief Return path to the global (system) directory where game files can be placed.\n     */\n    boost::filesystem::path getGlobalDataPath() const;\n\n    /**\n     * \\brief\n     */\n    boost::filesystem::path getCachePath() const;\n\n    boost::filesystem::path getInstallPath() const;\n};\n\n} /* namespace Files */\n\n#endif /* defined(__Android__) */\n\n#endif /* COMPONENTS_FILES_ANDROIDPATH_H */\n"
  },
  {
    "path": "components/files/collections.cpp",
    "content": "#include \"collections.hpp\"\n\n#include <components/misc/stringops.hpp>\n\nnamespace Files\n{\n    Collections::Collections()\n        : mDirectories()\n        , mFoldCase(false)\n        , mCollections()\n    {\n    }\n\n    Collections::Collections(const Files::PathContainer& directories, bool foldCase)\n        : mDirectories(directories)\n        , mFoldCase(foldCase)\n        , mCollections()\n    {\n    }\n\n    const MultiDirCollection& Collections::getCollection(const std::string& extension) const\n    {\n        MultiDirCollectionContainer::iterator iter = mCollections.find(extension);\n        if (iter==mCollections.end())\n        {\n            std::pair<MultiDirCollectionContainer::iterator, bool> result =\n                mCollections.insert(std::make_pair(extension, MultiDirCollection(mDirectories, extension, mFoldCase)));\n\n            iter = result.first;\n        }\n\n        return iter->second;\n    }\n\n    boost::filesystem::path Collections::getPath(const std::string& file) const\n    {\n        for (Files::PathContainer::const_iterator iter = mDirectories.begin();\n             iter != mDirectories.end(); ++iter)\n        {\n            for (boost::filesystem::directory_iterator iter2 (*iter);\n                iter2!=boost::filesystem::directory_iterator(); ++iter2)\n            {\n                boost::filesystem::path path = *iter2;\n\n                if (mFoldCase)\n                {\n                    if (Misc::StringUtils::ciEqual(file, path.filename().string()))\n                        return path.string();\n                }\n                else if (path.filename().string() == file)\n                    return path.string();\n            }\n        }\n\n        throw std::runtime_error (\"file \" + file + \" not found\");\n    }\n\n    bool Collections::doesExist(const std::string& file) const\n    {\n        for (Files::PathContainer::const_iterator iter = mDirectories.begin();\n             iter != mDirectories.end(); ++iter)\n        {\n            for (boost::filesystem::directory_iterator iter2 (*iter);\n                iter2!=boost::filesystem::directory_iterator(); ++iter2)\n            {\n                boost::filesystem::path path = *iter2;\n\n                if (mFoldCase)\n                {\n                    if (Misc::StringUtils::ciEqual(file, path.filename().string()))\n                        return true;\n                }\n                else if (path.filename().string() == file)\n                    return true;\n            }\n        }\n\n        return false;\n    }\n\n    const Files::PathContainer& Collections::getPaths() const\n    {\n        return mDirectories;\n    }\n}\n"
  },
  {
    "path": "components/files/collections.hpp",
    "content": "#ifndef COMPONENTS_FILES_COLLECTION_HPP\n#define COMPONENTS_FILES_COLLECTION_HPP\n\n#include <boost/filesystem.hpp>\n\n#include \"multidircollection.hpp\"\n\nnamespace Files\n{\n    class Collections\n    {\n        public:\n            Collections();\n\n            ///< Directories are listed with increasing priority.\n            Collections(const Files::PathContainer& directories, bool foldCase);\n\n            ///< Return a file collection for the given extension. Extension must contain the\n            /// leading dot and must be all lower-case.\n            const MultiDirCollection& getCollection(const std::string& extension) const;\n\n            boost::filesystem::path getPath(const std::string& file) const;\n            ///< Return full path (including filename) of \\a file.\n            ///\n            /// If the file does not exist in any of the collection's\n            /// directories, an exception is thrown. \\a file must include the\n            /// extension.\n\n            bool doesExist(const std::string& file) const;\n            ///< \\return Does a file with the given name exist?\n\n            const Files::PathContainer& getPaths() const;\n\n        private:\n            typedef std::map<std::string, MultiDirCollection> MultiDirCollectionContainer;\n            Files::PathContainer mDirectories;\n\n            bool mFoldCase;\n            mutable MultiDirCollectionContainer mCollections;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/files/configurationmanager.cpp",
    "content": "#include \"configurationmanager.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/files/escape.hpp>\n#include <components/fallback/validate.hpp>\n\n#include <boost/filesystem/fstream.hpp>\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n\nstatic const char* const openmwCfgFile = \"openmw.cfg\";\n\n#if defined(_WIN32) || defined(__WINDOWS__)\nstatic const char* const applicationName = \"OpenMW\";\n#else\nstatic const char* const applicationName = \"openmw\";\n#endif\n\nconst char* const localToken = \"?local?\";\nconst char* const userDataToken = \"?userdata?\";\nconst char* const globalToken = \"?global?\";\n\nConfigurationManager::ConfigurationManager(bool silent)\n    : mFixedPath(applicationName)\n    , mSilent(silent)\n{\n    setupTokensMapping();\n\n    boost::filesystem::create_directories(mFixedPath.getUserConfigPath());\n    boost::filesystem::create_directories(mFixedPath.getUserDataPath());\n\n    mLogPath = mFixedPath.getUserConfigPath();\n\n    mScreenshotPath = mFixedPath.getUserDataPath() / \"screenshots\";\n\n    // probably not necessary but validate the creation of the screenshots directory and fallback to the original behavior if it fails\n    boost::system::error_code dirErr;\n    if (!boost::filesystem::create_directories(mScreenshotPath, dirErr) && !boost::filesystem::is_directory(mScreenshotPath)) {\n        mScreenshotPath = mFixedPath.getUserDataPath();\n    }\n}\n\nConfigurationManager::~ConfigurationManager()\n{\n}\n\nvoid ConfigurationManager::setupTokensMapping()\n{\n    mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath));\n    mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath));\n    mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath));\n}\n\nvoid ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables,\n    boost::program_options::options_description& description, bool quiet)\n{\n    bool silent = mSilent;\n    mSilent = quiet;\n    \n    // User config has the highest priority.\n    auto composingVariables = separateComposingVariables(variables, description);\n    loadConfig(mFixedPath.getUserConfigPath(), variables, description);\n    mergeComposingVariables(variables, composingVariables, description);\n    boost::program_options::notify(variables);\n\n    // read either local or global config depending on type of installation\n    composingVariables = separateComposingVariables(variables, description);\n    bool loaded = loadConfig(mFixedPath.getLocalPath(), variables, description);\n    mergeComposingVariables(variables, composingVariables, description);\n    boost::program_options::notify(variables);\n    if (!loaded)\n    {\n        composingVariables = separateComposingVariables(variables, description);\n        loadConfig(mFixedPath.getGlobalConfigPath(), variables, description);\n        mergeComposingVariables(variables, composingVariables, description);\n        boost::program_options::notify(variables);\n    }\n\n    mSilent = silent;\n}\n\nboost::program_options::variables_map ConfigurationManager::separateComposingVariables(boost::program_options::variables_map & variables, boost::program_options::options_description& description)\n{\n    boost::program_options::variables_map composingVariables;\n    for (auto itr = variables.begin(); itr != variables.end();)\n    {\n        if (description.find(itr->first, false).semantic()->is_composing())\n        {\n            composingVariables.emplace(*itr);\n            itr = variables.erase(itr);\n        }\n        else\n            ++itr;\n    }\n    return composingVariables;\n}\n\nvoid ConfigurationManager::mergeComposingVariables(boost::program_options::variables_map & first, boost::program_options::variables_map & second, boost::program_options::options_description& description)\n{\n    // There are a few places this assumes all variables are present in second, but it's never crashed in the wild, so it looks like that's guaranteed.\n    std::set<std::string> replacedVariables;\n    if (description.find_nothrow(\"replace\", false))\n    {\n        auto replace = second[\"replace\"];\n        if (!replace.defaulted() && !replace.empty())\n        {\n            std::vector<std::string> replaceVector = replace.as<Files::EscapeStringVector>().toStdStringVector();\n            replacedVariables.insert(replaceVector.begin(), replaceVector.end());\n        }\n    }\n    for (const auto& option : description.options())\n    {\n        if (option->semantic()->is_composing())\n        {\n            std::string name = option->canonical_display_name();\n\n            auto firstPosition = first.find(name);\n            if (firstPosition == first.end())\n            {\n                first.emplace(name, second[name]);\n                continue;\n            }\n\n            if (replacedVariables.count(name))\n            {\n                firstPosition->second = second[name];\n                continue;\n            }\n\n            if (second[name].defaulted() || second[name].empty())\n                continue;\n\n            boost::any& firstValue = firstPosition->second.value();\n            const boost::any& secondValue = second[name].value();\n            \n            if (firstValue.type() == typeid(Files::EscapePathContainer))\n            {\n                auto& firstPathContainer = boost::any_cast<Files::EscapePathContainer&>(firstValue);\n                const auto& secondPathContainer = boost::any_cast<const Files::EscapePathContainer&>(secondValue);\n\n                firstPathContainer.insert(firstPathContainer.end(), secondPathContainer.begin(), secondPathContainer.end());\n            }\n            else if (firstValue.type() == typeid(Files::EscapeStringVector))\n            {\n                auto& firstVector = boost::any_cast<Files::EscapeStringVector&>(firstValue);\n                const auto& secondVector = boost::any_cast<const Files::EscapeStringVector&>(secondValue);\n\n                firstVector.mVector.insert(firstVector.mVector.end(), secondVector.mVector.begin(), secondVector.mVector.end());\n            }\n            else if (firstValue.type() == typeid(Fallback::FallbackMap))\n            {\n                auto& firstMap = boost::any_cast<Fallback::FallbackMap&>(firstValue);\n                const auto& secondMap = boost::any_cast<const Fallback::FallbackMap&>(secondValue);\n\n                std::map<std::string, std::string> tempMap(secondMap.mMap);\n                tempMap.merge(firstMap.mMap);\n                firstMap.mMap.swap(tempMap);\n            }\n            else\n                Log(Debug::Error) << \"Unexpected composing variable type. Curse boost and their blasted arcane templates.\";\n        }\n    }\n\n}\n\nvoid ConfigurationManager::processPaths(Files::PathContainer& dataDirs, bool create)\n{\n    std::string path;\n    for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)\n    {\n        path = it->string();\n\n        // Check if path contains a token\n        if (!path.empty() && *path.begin() == '?')\n        {\n            std::string::size_type pos = path.find('?', 1);\n            if (pos != std::string::npos && pos != 0)\n            {\n                TokensMappingContainer::iterator tokenIt = mTokensMapping.find(path.substr(0, pos + 1));\n                if (tokenIt != mTokensMapping.end())\n                {\n                    boost::filesystem::path tempPath(((mFixedPath).*(tokenIt->second))());\n                    if (pos < path.length() - 1)\n                    {\n                        // There is something after the token, so we should\n                        // append it to the path\n                        tempPath /= path.substr(pos + 1, path.length() - pos);\n                    }\n\n                    *it = tempPath;\n                }\n                else\n                {\n                    // Clean invalid / unknown token, it will be removed outside the loop\n                    (*it).clear();\n                }\n            }\n        }\n\n        if (!boost::filesystem::is_directory(*it))\n        {\n            if (create)\n            {\n                try\n                {\n                    boost::filesystem::create_directories (*it);\n                }\n                catch (...) {}\n\n                if (boost::filesystem::is_directory(*it))\n                    continue;\n            }\n\n            (*it).clear();\n        }\n    }\n\n    dataDirs.erase(std::remove_if(dataDirs.begin(), dataDirs.end(),\n        std::bind(&boost::filesystem::path::empty, std::placeholders::_1)), dataDirs.end());\n}\n\nbool ConfigurationManager::loadConfig(const boost::filesystem::path& path,\n    boost::program_options::variables_map& variables,\n    boost::program_options::options_description& description)\n{\n    boost::filesystem::path cfgFile(path);\n    cfgFile /= std::string(openmwCfgFile);\n    if (boost::filesystem::is_regular_file(cfgFile))\n    {\n        if (!mSilent)\n            Log(Debug::Info) << \"Loading config file: \" << cfgFile.string();\n\n        boost::filesystem::ifstream configFileStreamUnfiltered(cfgFile);\n        boost::iostreams::filtering_istream configFileStream;\n        configFileStream.push(escape_hash_filter());\n        configFileStream.push(configFileStreamUnfiltered);\n        if (configFileStreamUnfiltered.is_open())\n        {\n            boost::program_options::store(boost::program_options::parse_config_file(\n                configFileStream, description, true), variables);\n\n            return true;\n        }\n        else\n        {\n            if (!mSilent)\n                Log(Debug::Error) << \"Loading failed.\";\n\n            return false;\n        }\n    }\n    return false;\n}\n\nconst boost::filesystem::path& ConfigurationManager::getGlobalPath() const\n{\n    return mFixedPath.getGlobalConfigPath();\n}\n\nconst boost::filesystem::path& ConfigurationManager::getUserConfigPath() const\n{\n    return mFixedPath.getUserConfigPath();\n}\n\nconst boost::filesystem::path& ConfigurationManager::getUserDataPath() const\n{\n    return mFixedPath.getUserDataPath();\n}\n\nconst boost::filesystem::path& ConfigurationManager::getLocalPath() const\n{\n    return mFixedPath.getLocalPath();\n}\n\nconst boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const\n{\n    return mFixedPath.getGlobalDataPath();\n}\n\nconst boost::filesystem::path& ConfigurationManager::getCachePath() const\n{\n    return mFixedPath.getCachePath();\n}\n\nconst boost::filesystem::path& ConfigurationManager::getInstallPath() const\n{\n    return mFixedPath.getInstallPath();\n}\n\nconst boost::filesystem::path& ConfigurationManager::getLogPath() const\n{\n    return mLogPath;\n}\n\nconst boost::filesystem::path& ConfigurationManager::getScreenshotPath() const\n{\n    return mScreenshotPath;\n}\n\n} /* namespace Cfg */\n"
  },
  {
    "path": "components/files/configurationmanager.hpp",
    "content": "#ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP\n#define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP\n\n#include <map>\n\n#include <boost/program_options.hpp>\n\n#include <components/files/fixedpath.hpp>\n#include <components/files/collections.hpp>\n\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n\n/**\n * \\struct ConfigurationManager\n */\nstruct ConfigurationManager\n{\n    ConfigurationManager(bool silent=false); /// @param silent Emit log messages to cout?\n    virtual ~ConfigurationManager();\n\n    void readConfiguration(boost::program_options::variables_map& variables,\n        boost::program_options::options_description& description, bool quiet=false);\n\n    boost::program_options::variables_map separateComposingVariables(boost::program_options::variables_map& variables, boost::program_options::options_description& description);\n\n    void mergeComposingVariables(boost::program_options::variables_map& first, boost::program_options::variables_map& second, boost::program_options::options_description& description);\n\n    void processPaths(Files::PathContainer& dataDirs, bool create = false);\n    ///< \\param create Try creating the directory, if it does not exist.\n\n    /**< Fixed paths */\n    const boost::filesystem::path& getGlobalPath() const;\n    const boost::filesystem::path& getUserConfigPath() const;\n    const boost::filesystem::path& getLocalPath() const;\n\n    const boost::filesystem::path& getGlobalDataPath() const;\n    const boost::filesystem::path& getUserDataPath() const;\n    const boost::filesystem::path& getLocalDataPath() const;\n    const boost::filesystem::path& getInstallPath() const;\n\n    const boost::filesystem::path& getCachePath() const;\n\n    const boost::filesystem::path& getLogPath() const;\n    const boost::filesystem::path& getScreenshotPath() const;\n\n    private:\n        typedef Files::FixedPath<> FixedPathType;\n\n        typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const;\n        typedef std::map<std::string, path_type_f> TokensMappingContainer;\n\n        bool loadConfig(const boost::filesystem::path& path,\n            boost::program_options::variables_map& variables,\n            boost::program_options::options_description& description);\n\n        void setupTokensMapping();\n\n        FixedPathType mFixedPath;\n\n        boost::filesystem::path mLogPath;\n        boost::filesystem::path mScreenshotPath;\n\n        TokensMappingContainer mTokensMapping;\n\n        bool mSilent;\n};\n} /* namespace Cfg */\n\n#endif /* COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP */\n"
  },
  {
    "path": "components/files/constrainedfilestream.cpp",
    "content": "#include \"constrainedfilestream.hpp\"\n\n#include <streambuf>\n#include <algorithm>\n\n#include \"lowlevelfile.hpp\"\n\nnamespace\n{\n// somewhat arbitrary though 64KB buffers didn't seem to improve performance any\nconst size_t sBufferSize = 8192;\n}\n\nnamespace Files\n{\n    class ConstrainedFileStreamBuf : public std::streambuf\n    {\n\n        size_t mOrigin;\n        size_t mSize;\n\n        LowLevelFile mFile;\n\n        char mBuffer[sBufferSize]{0};\n\n    public:\n        ConstrainedFileStreamBuf(const std::string &fname, size_t start, size_t length)\n        {\n            mFile.open (fname.c_str ());\n            mSize  = length != 0xFFFFFFFF ? length : mFile.size () - start;\n\n            if (start != 0)\n                mFile.seek(start);\n\n            setg(nullptr,nullptr,nullptr);\n\n            mOrigin = start;\n        }\n\n        int_type underflow() override\n        {\n            if(gptr() == egptr())\n            {\n                size_t toRead = std::min((mOrigin+mSize)-(mFile.tell()), sBufferSize);\n                // Read in the next chunk of data, and set the read pointers on success\n                // Failure will throw exception in LowLevelFile\n                size_t got = mFile.read(mBuffer, toRead);\n                setg(&mBuffer[0], &mBuffer[0], &mBuffer[0]+got);\n            }\n            if(gptr() == egptr())\n                return traits_type::eof();\n\n            return traits_type::to_int_type(*gptr());\n        }\n\n        pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override\n        {\n            if((mode&std::ios_base::out) || !(mode&std::ios_base::in))\n                return traits_type::eof();\n\n            // new file position, relative to mOrigin\n            size_t newPos;\n            switch (whence)\n            {\n                case std::ios_base::beg:\n                    newPos = offset;\n                    break;\n                case std::ios_base::cur:\n                    newPos = (mFile.tell() - mOrigin - (egptr() - gptr())) + offset;\n                    break;\n                case std::ios_base::end:\n                    newPos = mSize + offset;\n                    break;\n                default:\n                    return traits_type::eof();\n            }\n\n            if (newPos > mSize)\n                return traits_type::eof();\n\n            mFile.seek(mOrigin+newPos);\n\n            // Clear read pointers so underflow() gets called on the next read attempt.\n            setg(nullptr, nullptr, nullptr);\n\n            return newPos;\n        }\n\n        pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override\n        {\n            if((mode&std::ios_base::out) || !(mode&std::ios_base::in))\n                return traits_type::eof();\n\n            if ((size_t)pos > mSize)\n                return traits_type::eof();\n\n            mFile.seek(mOrigin + pos);\n\n            // Clear read pointers so underflow() gets called on the next read attempt.\n            setg(nullptr, nullptr, nullptr);\n            return pos;\n        }\n\n    };\n\n    ConstrainedFileStream::ConstrainedFileStream(std::unique_ptr<std::streambuf> buf)\n        : std::istream(buf.get())\n        , mBuf(std::move(buf))\n    {\n    }\n\n    IStreamPtr openConstrainedFileStream(const char *filename,\n                                                       size_t start, size_t length)\n    {\n        auto buf = std::unique_ptr<std::streambuf>(new ConstrainedFileStreamBuf(filename, start, length));\n        return IStreamPtr(new ConstrainedFileStream(std::move(buf)));\n    }\n}\n"
  },
  {
    "path": "components/files/constrainedfilestream.hpp",
    "content": "#ifndef OPENMW_CONSTRAINEDFILESTREAM_H\n#define OPENMW_CONSTRAINEDFILESTREAM_H\n\n#include <istream>\n#include <memory>\n\nnamespace Files\n{\n\n/// A file stream constrained to a specific region in the file, specified by the 'start' and 'length' parameters.\nclass ConstrainedFileStream : public std::istream\n{\npublic:\n    ConstrainedFileStream(std::unique_ptr<std::streambuf> buf);\n    virtual ~ConstrainedFileStream() {};\n\nprivate:\n    std::unique_ptr<std::streambuf> mBuf;\n};\n\ntypedef std::shared_ptr<std::istream> IStreamPtr;\n\nIStreamPtr openConstrainedFileStream(const char *filename, size_t start=0, size_t length=0xFFFFFFFF);\n\n}\n\n#endif\n"
  },
  {
    "path": "components/files/escape.cpp",
    "content": "#include \"escape.hpp\"\n\n#include <components/misc/stringops.hpp>\n\nnamespace Files\n{\n    const int escape_hash_filter::sEscape = '@';\n    const int escape_hash_filter::sEscapeIdentifier = 'a';\n    const int escape_hash_filter::sHashIdentifier = 'h';\n\n    escape_hash_filter::escape_hash_filter() : mSeenNonWhitespace(false), mFinishLine(false)\n    {\n    }\n\n    escape_hash_filter::~escape_hash_filter()\n    {\n    }\n\n    unescape_hash_filter::unescape_hash_filter() : expectingIdentifier(false)\n    {\n    }\n\n    unescape_hash_filter::~unescape_hash_filter()\n    {\n    }\n\n    std::string EscapeHashString::processString(const std::string & str)\n    {\n        std::string temp = str;\n\n        static const char hash[] = { escape_hash_filter::sEscape,  escape_hash_filter::sHashIdentifier };\n        Misc::StringUtils::replaceAll(temp, hash, \"#\", 2, 1);\n\n        static const char escape[] = { escape_hash_filter::sEscape,  escape_hash_filter::sEscapeIdentifier };\n        Misc::StringUtils::replaceAll(temp, escape, \"@\", 2, 1);\n\n        return temp;\n    }\n\n    EscapeHashString::EscapeHashString() : mData()\n    {\n    }\n\n    EscapeHashString::EscapeHashString(const std::string & str) : mData(EscapeHashString::processString(str))\n    {\n    }\n\n    EscapeHashString::EscapeHashString(const std::string & str, size_t pos, size_t len) : mData(EscapeHashString::processString(str), pos, len)\n    {\n    }\n\n    EscapeHashString::EscapeHashString(const char * s) : mData(EscapeHashString::processString(std::string(s)))\n    {\n    }\n\n    EscapeHashString::EscapeHashString(const char * s, size_t n) : mData(EscapeHashString::processString(std::string(s)), 0, n)\n    {\n    }\n\n    EscapeHashString::EscapeHashString(size_t n, char c) : mData(n, c)\n    {\n    }\n\n    template <class InputIterator>\n    EscapeHashString::EscapeHashString(InputIterator first, InputIterator last) : mData(EscapeHashString::processString(std::string(first, last)))\n    {\n    }\n\n    std::string EscapeHashString::toStdString() const\n    {\n        return std::string(mData);\n    }\n\n    std::istream & operator>> (std::istream & is, EscapeHashString & eHS)\n    {\n        std::string temp;\n        is >> temp;\n        eHS = EscapeHashString(temp);\n        return is;\n    }\n\n    std::ostream & operator<< (std::ostream & os, const EscapeHashString & eHS)\n    {\n        os << eHS.mData;\n        return os;\n    }\n\n    EscapeStringVector::EscapeStringVector() : mVector()\n    {\n    }\n\n    EscapeStringVector::~EscapeStringVector()\n    {\n    }\n\n    std::vector<std::string> EscapeStringVector::toStdStringVector() const\n    {\n        std::vector<std::string> temp = std::vector<std::string>();\n        for (std::vector<EscapeHashString>::const_iterator it = mVector.begin(); it != mVector.end(); ++it)\n        {\n            temp.push_back(it->toStdString());\n        }\n        return temp;\n    }\n\n    // boost program options validation\n\n    void validate(boost::any &v, const std::vector<std::string> &tokens, Files::EscapeHashString * eHS, int a)\n    {\n        boost::program_options::validators::check_first_occurrence(v);\n\n        if (v.empty())\n            v = boost::any(EscapeHashString(boost::program_options::validators::get_single_string(tokens)));\n    }\n\n    void validate(boost::any &v, const std::vector<std::string> &tokens, EscapeStringVector *, int)\n    {\n        if (v.empty())\n            v = boost::any(EscapeStringVector());\n\n        EscapeStringVector * eSV = boost::any_cast<EscapeStringVector>(&v);\n\n        for (std::vector<std::string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it)\n            eSV->mVector.emplace_back(*it);\n    }\n\n    PathContainer EscapePath::toPathContainer(const EscapePathContainer & escapePathContainer)\n    {\n        PathContainer temp;\n        for (EscapePathContainer::const_iterator it = escapePathContainer.begin(); it != escapePathContainer.end(); ++it)\n            temp.push_back(it->mPath);\n        return temp;\n    }\n\n    std::istream & operator>> (std::istream & istream, EscapePath & escapePath)\n    {\n        boost::iostreams::filtering_istream filteredStream;\n        filteredStream.push(unescape_hash_filter());\n        filteredStream.push(istream);\n\n        filteredStream >> escapePath.mPath;\n\n        return istream;\n    }\n}\n"
  },
  {
    "path": "components/files/escape.hpp",
    "content": "#ifndef COMPONENTS_FILES_ESCAPE_HPP\n#define COMPONENTS_FILES_ESCAPE_HPP\n\n#include <queue>\n\n#include <components/files/multidircollection.hpp>\n\n#include <boost/iostreams/filtering_stream.hpp>\n#include <boost/filesystem/path.hpp>\n#include <boost/program_options.hpp>\n\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n    /**\n    * \\struct escape_hash_filter\n    */\n    struct escape_hash_filter : public boost::iostreams::input_filter\n    {\n        static const int sEscape;\n        static const int sHashIdentifier;\n        static const int sEscapeIdentifier;\n\n        escape_hash_filter();\n        virtual ~escape_hash_filter();\n\n        template <typename Source> int get(Source & src);\n\n    private:\n        std::queue<int> mNext;\n\n        bool mSeenNonWhitespace;\n        bool mFinishLine;\n    };\n\n    template <typename Source>\n    int escape_hash_filter::get(Source & src)\n    {\n        if (mNext.empty())\n        {\n            int character = boost::iostreams::get(src);\n            if (character == boost::iostreams::WOULD_BLOCK)\n            {\n                mNext.push(character);\n            }\n            else if (character == EOF)\n            {\n                mSeenNonWhitespace = false;\n                mFinishLine = false;\n                mNext.push(character);\n            }\n            else if (character == '\\n')\n            {\n                mSeenNonWhitespace = false;\n                mFinishLine = false;\n                mNext.push(character);\n            }\n            else if (mFinishLine)\n            {\n                mNext.push(character);\n            }\n            else if (character == '#')\n            {\n                if (mSeenNonWhitespace)\n                {\n                    mNext.push(sEscape);\n                    mNext.push(sHashIdentifier);\n                }\n                else\n                {\n                    //it's fine being interpreted by Boost as a comment, and so is anything afterwards\n                    mNext.push(character);\n                    mFinishLine = true;\n                }\n            }\n            else if (character == sEscape)\n            {\n                mNext.push(sEscape);\n                mNext.push(sEscapeIdentifier);\n            }\n            else\n            {\n                mNext.push(character);\n            }\n            if (!mSeenNonWhitespace && !isspace(character))\n                mSeenNonWhitespace = true;\n        }\n        int retval = mNext.front();\n        mNext.pop();\n        return retval;\n    }\n\n    struct unescape_hash_filter : public boost::iostreams::input_filter\n    {\n        unescape_hash_filter();\n        virtual ~unescape_hash_filter();\n\n        template <typename Source> int get(Source & src);\n\n    private:\n        bool expectingIdentifier;\n    };\n\n    template <typename Source>\n    int unescape_hash_filter::get(Source & src)\n    {\n        int character;\n        if (!expectingIdentifier)\n            character = boost::iostreams::get(src);\n        else\n        {\n            character = escape_hash_filter::sEscape;\n            expectingIdentifier = false;\n        }\n        if (character == escape_hash_filter::sEscape)\n        {\n            int nextChar = boost::iostreams::get(src);\n            int intended;\n            if (nextChar == escape_hash_filter::sEscapeIdentifier)\n                intended = escape_hash_filter::sEscape;\n            else if (nextChar == escape_hash_filter::sHashIdentifier)\n                intended = '#';\n            else if (nextChar == boost::iostreams::WOULD_BLOCK)\n            {\n                expectingIdentifier = true;\n                intended = nextChar;\n            }\n            else\n                intended = '?';\n            return intended;\n        }\n        else\n            return character;\n    }\n\n    /**\n    * \\class EscapeHashString\n    */\n    class EscapeHashString\n    {\n    private:\n        std::string mData;\n    public:\n        static std::string processString(const std::string & str);\n\n        EscapeHashString();\n        EscapeHashString(const std::string & str);\n        EscapeHashString(const std::string & str, size_t pos, size_t len = std::string::npos);\n        EscapeHashString(const char * s);\n        EscapeHashString(const char * s, size_t n);\n        EscapeHashString(size_t n, char c);\n        template <class InputIterator>\n        EscapeHashString(InputIterator first, InputIterator last);\n\n        std::string toStdString() const;\n\n        friend std::ostream & operator<< (std::ostream & os, const EscapeHashString & eHS);\n    };\n\n    std::istream & operator>> (std::istream & is, EscapeHashString & eHS);\n\n    struct EscapeStringVector\n    {\n        std::vector<Files::EscapeHashString> mVector;\n\n        EscapeStringVector();\n        virtual ~EscapeStringVector();\n\n        std::vector<std::string> toStdStringVector() const;\n    };\n\n    //boost program options validation\n\n    void validate(boost::any &v, const std::vector<std::string> &tokens, Files::EscapeHashString * eHS, int a);\n\n    void validate(boost::any &v, const std::vector<std::string> &tokens, EscapeStringVector *, int);\n\n    struct EscapePath\n    {\n        boost::filesystem::path mPath;\n\n        static PathContainer toPathContainer(const std::vector<EscapePath> & escapePathContainer);\n    };\n\n    typedef std::vector<EscapePath> EscapePathContainer;\n\n    std::istream & operator>> (std::istream & istream, EscapePath & escapePath);\n} /* namespace Files */\n#endif /* COMPONENTS_FILES_ESCAPE_HPP */\n"
  },
  {
    "path": "components/files/fixedpath.hpp",
    "content": "#ifndef COMPONENTS_FILES_FIXEDPATH_HPP\n#define COMPONENTS_FILES_FIXEDPATH_HPP\n\n#include <string>\n#include <boost/filesystem.hpp>\n\n#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)\n#ifndef ANDROID\n    #include <components/files/linuxpath.hpp>\n    namespace Files { typedef LinuxPath TargetPathType; }\n#else\n    #include <components/files/androidpath.hpp>\n    namespace Files { typedef AndroidPath TargetPathType; }\n#endif\n#elif defined(__WIN32) || defined(__WINDOWS__) || defined(_WIN32)\n    #include <components/files/windowspath.hpp>\n    namespace Files { typedef WindowsPath TargetPathType; }\n\n#elif defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__)\n    #include <components/files/macospath.hpp>\n    namespace Files { typedef MacOsPath TargetPathType; }\n\n#else\n    #error \"Unknown platform!\"\n#endif\n\n\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n\n/**\n * \\struct Path\n *\n * \\tparam P - Path strategy class type (depends on target system)\n *\n */\ntemplate\n<\n    class P = TargetPathType\n>\nstruct FixedPath\n{\n    typedef P PathType;\n\n    /**\n     * \\brief Path constructor.\n     *\n     * \\param [in] application_name - Name of the application\n     */\n    FixedPath(const std::string& application_name)\n        : mPath(application_name + \"/\")\n        , mUserConfigPath(mPath.getUserConfigPath())\n        , mUserDataPath(mPath.getUserDataPath())\n        , mGlobalConfigPath(mPath.getGlobalConfigPath())\n        , mLocalPath(mPath.getLocalPath())\n        , mGlobalDataPath(mPath.getGlobalDataPath())\n        , mCachePath(mPath.getCachePath())\n        , mInstallPath(mPath.getInstallPath())\n    {\n    }\n\n    /**\n     * \\brief Return path pointing to the user local configuration directory.\n     */\n    const boost::filesystem::path& getUserConfigPath() const\n    {\n        return mUserConfigPath;\n    }\n\n    const boost::filesystem::path& getUserDataPath() const\n    {\n        return mUserDataPath;\n    }\n\n    /**\n     * \\brief Return path pointing to the global (system) configuration directory.\n     */\n    const boost::filesystem::path& getGlobalConfigPath() const\n    {\n        return mGlobalConfigPath;\n    }\n\n    /**\n     * \\brief Return path pointing to the directory where application was started.\n     */\n    const boost::filesystem::path& getLocalPath() const\n    {\n        return mLocalPath;\n    }\n\n\n    const boost::filesystem::path& getInstallPath() const\n    {\n        return mInstallPath;\n    }\n\n    const boost::filesystem::path& getGlobalDataPath() const\n    {\n        return mGlobalDataPath;\n    }\n\n    const boost::filesystem::path& getCachePath() const\n    {\n        return mCachePath;\n    }\n\n    private:\n        PathType mPath;\n\n        boost::filesystem::path mUserConfigPath;       /**< User path  */\n        boost::filesystem::path mUserDataPath;\n        boost::filesystem::path mGlobalConfigPath;     /**< Global path */\n        boost::filesystem::path mLocalPath;      /**< It is the same directory where application was run */\n\n        boost::filesystem::path mGlobalDataPath;        /**< Global application data path */\n\n        boost::filesystem::path mCachePath;\n\n        boost::filesystem::path mInstallPath;\n\n};\n\n\n} /* namespace Files */\n\n#endif /* COMPONENTS_FILES_FIXEDPATH_HPP */\n"
  },
  {
    "path": "components/files/linuxpath.cpp",
    "content": "#include \"linuxpath.hpp\"\n\n#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)\n\n#include <pwd.h>\n#include <unistd.h>\n#include <boost/filesystem/fstream.hpp>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n\n\nnamespace\n{\n    boost::filesystem::path getUserHome()\n    {\n        const char* dir = getenv(\"HOME\");\n        if (dir == nullptr)\n        {\n            struct passwd* pwd = getpwuid(getuid());\n            if (pwd != nullptr)\n            {\n                dir = pwd->pw_dir;\n            }\n        }\n        if (dir == nullptr)\n            return boost::filesystem::path();\n        else\n            return boost::filesystem::path(dir);\n    }\n\n    boost::filesystem::path getEnv(const std::string& envVariable, const boost::filesystem::path& fallback)\n    {\n        const char* result = getenv(envVariable.c_str());\n        if (!result)\n            return fallback;\n        boost::filesystem::path dir(result);\n        if (dir.empty())\n            return fallback;\n        else\n            return dir;\n    }\n}\n\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n\nLinuxPath::LinuxPath(const std::string& application_name)\n    : mName(application_name)\n{\n    boost::filesystem::path localPath = getLocalPath();\n    if (chdir(localPath.string().c_str()) != 0)\n        Log(Debug::Warning) << \"Error \" << errno << \" when changing current directory\";\n}\n\nboost::filesystem::path LinuxPath::getUserConfigPath() const\n{\n    return getEnv(\"XDG_CONFIG_HOME\", getUserHome() / \".config\") / mName;\n}\n\nboost::filesystem::path LinuxPath::getUserDataPath() const\n{\n    return getEnv(\"XDG_DATA_HOME\", getUserHome() / \".local/share\") / mName;\n}\n\nboost::filesystem::path LinuxPath::getCachePath() const\n{\n    return getEnv(\"XDG_CACHE_HOME\", getUserHome() / \".cache\") / mName;\n}\n\nboost::filesystem::path LinuxPath::getGlobalConfigPath() const\n{\n    boost::filesystem::path globalPath(GLOBAL_CONFIG_PATH);\n    return globalPath / mName;\n}\n\nboost::filesystem::path LinuxPath::getLocalPath() const\n{\n    boost::filesystem::path localPath(\"./\");\n    std::string binPath(pathconf(\".\", _PC_PATH_MAX), '\\0');\n    const char *statusPaths[] = {\"/proc/self/exe\", \"/proc/self/file\", \"/proc/curproc/exe\", \"/proc/curproc/file\"};\n\n    for(const char *path : statusPaths)\n    {\n        if (readlink(path, &binPath[0], binPath.size()) != -1)\n        {\n            localPath = boost::filesystem::path(binPath).parent_path() / \"/\";\n            break;\n        }\n    }\n\n    return localPath;\n}\n\nboost::filesystem::path LinuxPath::getGlobalDataPath() const\n{\n    boost::filesystem::path globalDataPath(GLOBAL_DATA_PATH);\n    return globalDataPath / mName;\n}\n\nboost::filesystem::path LinuxPath::getInstallPath() const\n{\n    boost::filesystem::path installPath;\n\n    boost::filesystem::path homePath = getUserHome();\n\n    if (!homePath.empty())\n    {\n        boost::filesystem::path wineDefaultRegistry(homePath);\n        wineDefaultRegistry /= \".wine/system.reg\";\n\n        if (boost::filesystem::is_regular_file(wineDefaultRegistry))\n        {\n            boost::filesystem::ifstream file(wineDefaultRegistry);\n            bool isRegEntry = false;\n            std::string line;\n            std::string mwpath;\n\n            while (std::getline(file, line))\n            {\n                if (line[0] == '[') // we found an entry\n                {\n                    if (isRegEntry)\n                    {\n                        break;\n                    }\n\n                    isRegEntry = (line.find(\"Softworks\\\\\\\\Morrowind]\") != std::string::npos);\n                }\n                else if (isRegEntry)\n                {\n                    if (line[0] == '\"') // empty line means new registry key\n                    {\n                        std::string key = line.substr(1, line.find('\"', 1) - 1);\n                        if (strcasecmp(key.c_str(), \"Installed Path\") == 0)\n                        {\n                            std::string::size_type valuePos = line.find('=') + 2;\n                            mwpath = line.substr(valuePos, line.rfind('\"') - valuePos);\n\n                            std::string::size_type pos = mwpath.find(\"\\\\\");\n                            while (pos != std::string::npos)\n                            {\n                               mwpath.replace(pos, 2, \"/\");\n                               pos = mwpath.find(\"\\\\\", pos + 1);\n                            }\n                            break;\n                        }\n                    }\n                }\n            }\n\n            if (!mwpath.empty())\n            {\n                // Change drive letter to lowercase, so we could use\n                // ~/.wine/dosdevices symlinks\n                mwpath[0] = Misc::StringUtils::toLower(mwpath[0]);\n                installPath /= homePath;\n                installPath /= \".wine/dosdevices/\";\n                installPath /= mwpath;\n\n                if (!boost::filesystem::is_directory(installPath))\n                {\n                    installPath.clear();\n                }\n            }\n        }\n    }\n\n    return installPath;\n}\n\n} /* namespace Files */\n\n#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) */\n"
  },
  {
    "path": "components/files/linuxpath.hpp",
    "content": "#ifndef COMPONENTS_FILES_LINUXPATH_H\n#define COMPONENTS_FILES_LINUXPATH_H\n\n#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)\n\n#include <boost/filesystem.hpp>\n\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n\n/**\n * \\struct LinuxPath\n */\nstruct LinuxPath\n{\n    LinuxPath(const std::string& application_name);\n\n    /**\n     * \\brief Return path to the user directory.\n     */\n    boost::filesystem::path getUserConfigPath() const;\n\n    boost::filesystem::path getUserDataPath() const;\n\n    /**\n     * \\brief Return path to the global (system) directory where config files can be placed.\n     */\n    boost::filesystem::path getGlobalConfigPath() const;\n\n    /**\n     * \\brief Return path to the runtime configuration directory which is the\n     * place where an application was started.\n     */\n    boost::filesystem::path getLocalPath() const;\n\n    /**\n     * \\brief Return path to the global (system) directory where game files can be placed.\n     */\n    boost::filesystem::path getGlobalDataPath() const;\n\n    /**\n     * \\brief\n     */\n    boost::filesystem::path getCachePath() const;\n\n    /**\n     * \\brief Gets the path of the installed Morrowind version if there is one.\n     */\n    boost::filesystem::path getInstallPath() const;\n\n    std::string mName;\n};\n\n} /* namespace Files */\n\n#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) */\n\n#endif /* COMPONENTS_FILES_LINUXPATH_H */\n"
  },
  {
    "path": "components/files/lowlevelfile.cpp",
    "content": "#include \"lowlevelfile.hpp\"\n\n#include <stdexcept>\n#include <sstream>\n#include <cassert>\n\n#if FILE_API == FILE_API_STDIO\n#include <errno.h>\n#include <string.h>\n#endif\n\n#if FILE_API == FILE_API_POSIX\n#include <sys/types.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <string.h>\n#endif\n\n#if FILE_API == FILE_API_STDIO\n/*\n *\n *  Implementation of LowLevelFile methods using c stdio\n *\n */\n\nLowLevelFile::LowLevelFile ()\n{\n    mHandle = nullptr;\n}\n\nLowLevelFile::~LowLevelFile ()\n{\n    if (mHandle != nullptr)\n        fclose (mHandle);\n}\n\nvoid LowLevelFile::open (char const * filename)\n{\n    assert (mHandle == nullptr);\n\n    mHandle = fopen (filename, \"rb\");\n\n    if (mHandle == nullptr)\n    {\n        std::ostringstream os;\n        os << \"Failed to open '\" << filename << \"' for reading: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n}\n\nvoid LowLevelFile::close ()\n{\n    assert (mHandle != nullptr);\n\n    fclose (mHandle);\n\n    mHandle = nullptr;\n}\n\nsize_t LowLevelFile::size ()\n{\n    assert (mHandle != nullptr);\n\n    long oldPosition = ftell (mHandle);\n    if (oldPosition == -1)\n    {\n        std::ostringstream os;\n        os << \"An ftell() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    if (fseek (mHandle, 0, SEEK_END) != 0)\n    {\n        std::ostringstream os;\n        os << \"An fseek() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    long size = ftell (mHandle);\n    if (size == -1)\n    {\n        std::ostringstream os;\n        os << \"An ftell() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    if (fseek (mHandle, oldPosition, SEEK_SET) != 0)\n    {\n        std::ostringstream os;\n        os << \"An fseek() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    return size_t (size);\n}\n\nvoid LowLevelFile::seek (size_t position)\n{\n    assert (mHandle != nullptr);\n\n    if (fseek (mHandle, position, SEEK_SET) != 0)\n    {\n        std::ostringstream os;\n        os << \"An fseek() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n}\n\nsize_t LowLevelFile::tell ()\n{\n    assert (mHandle != nullptr);\n\n    long position = ftell (mHandle);\n    if (position == -1)\n    {\n        std::ostringstream os;\n        os << \"An ftell() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    return size_t (position);\n}\n\nsize_t LowLevelFile::read (void * data, size_t size)\n{\n    assert (mHandle != nullptr);\n\n    int amount = fread (data, 1, size, mHandle);\n\n    if (amount == 0 && ferror (mHandle))\n    {\n        std::ostringstream os;\n        os << \"An attempt to read \" << size << \" bytes failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    return amount;\n}\n\n#elif FILE_API == FILE_API_POSIX\n/*\n *\n *  Implementation of LowLevelFile methods using posix IO calls\n *\n */\n\nLowLevelFile::LowLevelFile ()\n{\n    mHandle = -1;\n}\n\nLowLevelFile::~LowLevelFile ()\n{\n    if (mHandle != -1)\n        ::close (mHandle);\n}\n\nvoid LowLevelFile::open (char const * filename)\n{\n    assert (mHandle == -1);\n\n#ifdef O_BINARY\n    static const int openFlags = O_RDONLY | O_BINARY;\n#else\n    static const int openFlags = O_RDONLY;\n#endif\n\n    mHandle = ::open (filename, openFlags, 0);\n\n    if (mHandle == -1)\n    {\n        std::ostringstream os;\n        os << \"Failed to open '\" << filename << \"' for reading: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n}\n\nvoid LowLevelFile::close ()\n{\n    assert (mHandle != -1);\n\n    ::close (mHandle);\n\n    mHandle = -1;\n}\n\nsize_t LowLevelFile::size ()\n{\n    assert (mHandle != -1);\n\n    size_t oldPosition = ::lseek (mHandle, 0, SEEK_CUR);\n\n    if (oldPosition == size_t (-1))\n    {\n        std::ostringstream os;\n        os << \"An lseek() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    size_t size = ::lseek (mHandle, 0, SEEK_END);\n\n    if (size == size_t (-1))\n    {\n        std::ostringstream os;\n        os << \"An lseek() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    if (lseek (mHandle, oldPosition, SEEK_SET) == -1)\n    {\n        std::ostringstream os;\n        os << \"An lseek() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    return size;\n}\n\nvoid LowLevelFile::seek (size_t position)\n{\n    assert (mHandle != -1);\n\n    if (::lseek (mHandle, position, SEEK_SET) == -1)\n    {\n        std::ostringstream os;\n        os << \"An lseek() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n}\n\nsize_t LowLevelFile::tell ()\n{\n    assert (mHandle != -1);\n\n    size_t position = ::lseek (mHandle, 0, SEEK_CUR);\n\n    if (position == size_t (-1))\n    {\n        std::ostringstream os;\n        os << \"An lseek() call failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    return position;\n}\n\nsize_t LowLevelFile::read (void * data, size_t size)\n{\n    assert (mHandle != -1);\n\n    int amount = ::read (mHandle, data, size);\n\n    if (amount == -1)\n    {\n        std::ostringstream os;\n        os << \"An attempt to read \" << size << \" bytes failed: \" << strerror(errno);\n        throw std::runtime_error (os.str ());\n    }\n\n    return amount;\n}\n\n#elif FILE_API == FILE_API_WIN32\n\n#include <boost/locale.hpp>\n/*\n *\n *  Implementation of LowLevelFile methods using Win32 API calls\n *\n */\n\nLowLevelFile::LowLevelFile ()\n{\n    mHandle = INVALID_HANDLE_VALUE;\n}\n\nLowLevelFile::~LowLevelFile ()\n{\n    if (mHandle != INVALID_HANDLE_VALUE)\n        CloseHandle (mHandle);\n}\n\nvoid LowLevelFile::open (char const * filename)\n{\n    assert (mHandle == INVALID_HANDLE_VALUE);\n\n    std::wstring wname = boost::locale::conv::utf_to_utf<wchar_t>(filename);\n    HANDLE handle = CreateFileW (wname.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);\n\n    if (handle == INVALID_HANDLE_VALUE)\n    {\n        std::ostringstream os;\n        os << \"Failed to open '\" << filename << \"' for reading: \" << GetLastError();\n        throw std::runtime_error (os.str ());\n    }\n\n    mHandle = handle;\n}\n\nvoid LowLevelFile::close ()\n{\n    assert (mHandle != INVALID_HANDLE_VALUE);\n\n    CloseHandle (mHandle);\n\n    mHandle = INVALID_HANDLE_VALUE;\n}\n\nsize_t LowLevelFile::size ()\n{\n    assert (mHandle != INVALID_HANDLE_VALUE);\n\n    BY_HANDLE_FILE_INFORMATION info;\n\n    if (!GetFileInformationByHandle (mHandle, &info))\n        throw std::runtime_error (\"A query operation on a file failed.\");\n\n    if (info.nFileSizeHigh != 0)\n        throw std::runtime_error (\"Files greater that 4GB are not supported.\");\n\n    return info.nFileSizeLow;\n}\n\nvoid LowLevelFile::seek (size_t position)\n{\n    assert (mHandle != INVALID_HANDLE_VALUE);\n\n    if (SetFilePointer (mHandle, static_cast<LONG>(position), nullptr, SEEK_SET) == INVALID_SET_FILE_POINTER)\n        if (GetLastError () != NO_ERROR)\n            throw std::runtime_error (\"A seek operation on a file failed.\");\n}\n\nsize_t LowLevelFile::tell ()\n{\n    assert (mHandle != INVALID_HANDLE_VALUE);\n\n    DWORD value = SetFilePointer (mHandle, 0, nullptr, SEEK_CUR);\n\n    if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR)\n        throw std::runtime_error (\"A query operation on a file failed.\");\n\n    return value;\n}\n\nsize_t LowLevelFile::read (void * data, size_t size)\n{\n    assert (mHandle != INVALID_HANDLE_VALUE);\n\n    DWORD read;\n\n    if (!ReadFile (mHandle, data, static_cast<DWORD>(size), &read, nullptr))\n        throw std::runtime_error (\"A read operation on a file failed.\");\n\n    return read;\n}\n\n#endif\n"
  },
  {
    "path": "components/files/lowlevelfile.hpp",
    "content": "#ifndef COMPONENTS_FILES_LOWLEVELFILE_HPP\n#define COMPONENTS_FILES_LOWLEVELFILE_HPP\n\n#include <cstdlib>\n\n#define FILE_API_STDIO  0\n#define FILE_API_POSIX  1\n#define FILE_API_WIN32  2\n\n#if defined(__linux) || defined(__unix) || defined(__posix)\n#define FILE_API    FILE_API_POSIX\n#elif defined(_WIN32)\n#define FILE_API    FILE_API_WIN32\n#else\n#define FILE_API    FILE_API_STDIO\n#endif\n\n#if FILE_API == FILE_API_STDIO\n#include <cstdio>\n#elif FILE_API == FILE_API_POSIX\n#elif FILE_API == FILE_API_WIN32\n#include <windows.h>\n#else\n#error Unsupported File API\n#endif\n\nclass LowLevelFile\n{\npublic:\n\n    LowLevelFile ();\n    ~LowLevelFile ();\n\n    void open (char const * filename);\n    void close ();\n\n    size_t size ();\n\n    void seek (size_t Position);\n    size_t tell ();\n\n    size_t read (void * data, size_t size);\n\nprivate:\n#if FILE_API == FILE_API_STDIO\n    FILE* mHandle;\n#elif FILE_API == FILE_API_POSIX\n    int mHandle;\n#elif FILE_API == FILE_API_WIN32\n    HANDLE mHandle;\n#endif\n};\n\n#endif\n"
  },
  {
    "path": "components/files/macospath.cpp",
    "content": "#include \"macospath.hpp\"\n\n#if defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__)\n\n#include <cstdlib>\n#include <pwd.h>\n#include <unistd.h>\n#include <boost/filesystem/fstream.hpp>\n\n#include <components/misc/stringops.hpp>\n\nnamespace\n{\n    boost::filesystem::path getUserHome()\n    {\n        const char* dir = getenv(\"HOME\");\n        if (dir == nullptr)\n        {\n            struct passwd* pwd = getpwuid(getuid());\n            if (pwd != nullptr)\n            {\n                dir = pwd->pw_dir;\n            }\n        }\n        if (dir == nullptr)\n            return boost::filesystem::path();\n        else\n            return boost::filesystem::path(dir);\n    }\n}\n\nnamespace Files\n{\n\nMacOsPath::MacOsPath(const std::string& application_name)\n    : mName(application_name)\n{\n}\n\nboost::filesystem::path MacOsPath::getUserConfigPath() const\n{\n    boost::filesystem::path userPath (getUserHome());\n    userPath /= \"Library/Preferences/\";\n\n    return userPath / mName;\n}\n\nboost::filesystem::path MacOsPath::getUserDataPath() const\n{\n    boost::filesystem::path userPath (getUserHome());\n    userPath /= \"Library/Application Support/\";\n\n    return userPath / mName;\n}\n\nboost::filesystem::path MacOsPath::getGlobalConfigPath() const\n{\n    boost::filesystem::path globalPath(\"/Library/Preferences/\");\n    return globalPath / mName;\n}\n\nboost::filesystem::path MacOsPath::getCachePath() const\n{\n    boost::filesystem::path userPath (getUserHome());\n    userPath /= \"Library/Caches\";\n    return userPath / mName;\n}\n\nboost::filesystem::path MacOsPath::getLocalPath() const\n{\n    return boost::filesystem::path(\"../Resources/\");\n}\n\nboost::filesystem::path MacOsPath::getGlobalDataPath() const\n{\n    boost::filesystem::path globalDataPath(\"/Library/Application Support/\");\n    return globalDataPath / mName;\n}\n\nboost::filesystem::path MacOsPath::getInstallPath() const\n{\n    boost::filesystem::path installPath;\n\n    boost::filesystem::path homePath = getUserHome();\n\n    if (!homePath.empty())\n    {\n        boost::filesystem::path wineDefaultRegistry(homePath);\n        wineDefaultRegistry /= \".wine/system.reg\";\n\n        if (boost::filesystem::is_regular_file(wineDefaultRegistry))\n        {\n            boost::filesystem::ifstream file(wineDefaultRegistry);\n            bool isRegEntry = false;\n            std::string line;\n            std::string mwpath;\n\n            while (std::getline(file, line))\n            {\n                if (line[0] == '[') // we found an entry\n                {\n                    if (isRegEntry)\n                    {\n                        break;\n                    }\n\n                    isRegEntry = (line.find(\"Softworks\\\\\\\\Morrowind]\") != std::string::npos);\n                }\n                else if (isRegEntry)\n                {\n                    if (line[0] == '\"') // empty line means new registry key\n                    {\n                        std::string key = line.substr(1, line.find('\"', 1) - 1);\n                        if (strcasecmp(key.c_str(), \"Installed Path\") == 0)\n                        {\n                            std::string::size_type valuePos = line.find('=') + 2;\n                            mwpath = line.substr(valuePos, line.rfind('\"') - valuePos);\n\n                            std::string::size_type pos = mwpath.find(\"\\\\\");\n                            while (pos != std::string::npos)\n                            {\n                               mwpath.replace(pos, 2, \"/\");\n                               pos = mwpath.find(\"\\\\\", pos + 1);\n                            }\n                            break;\n                        }\n                    }\n                }\n            }\n\n            if (!mwpath.empty())\n            {\n                // Change drive letter to lowercase, so we could use ~/.wine/dosdevice symlinks\n                mwpath[0] = Misc::StringUtils::toLower(mwpath[0]);\n                installPath /= homePath;\n                installPath /= \".wine/dosdevices/\";\n                installPath /= mwpath;\n\n                if (!boost::filesystem::is_directory(installPath))\n                {\n                    installPath.clear();\n                }\n            }\n        }\n    }\n\n    return installPath;\n}\n\n\n} /* namespace Files */\n\n#endif /* defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) */\n"
  },
  {
    "path": "components/files/macospath.hpp",
    "content": "#ifndef COMPONENTS_FILES_MACOSPATH_H\n#define COMPONENTS_FILES_MACOSPATH_H\n\n#if defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__)\n\n#include <boost/filesystem.hpp>\n\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n\n/**\n * \\struct MacOsPath\n */\nstruct MacOsPath\n{\n    MacOsPath(const std::string& application_name);\n\n    /**\n     * \\brief Return path to the local directory.\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getUserConfigPath() const;\n\n    boost::filesystem::path getUserDataPath() const;\n\n    /**\n     * \\brief Return path to the global (system) directory.\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getGlobalConfigPath() const;\n\n    /**\n     * \\brief Return path to the runtime directory which is the\n     * place where an application was started.\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getLocalPath() const;\n\n    /**\n     * \\brief\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getCachePath() const;\n\n    /**\n     * \\brief\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getGlobalDataPath() const;\n\n    boost::filesystem::path getInstallPath() const;\n\n    std::string mName;\n};\n\n} /* namespace Files */\n\n#endif /* defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) */\n\n#endif /* COMPONENTS_FILES_MACOSPATH_H */\n"
  },
  {
    "path": "components/files/memorystream.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_FILES_MEMORYSTREAM_H\n#define OPENMW_COMPONENTS_FILES_MEMORYSTREAM_H\n\n#include <istream>\n\nnamespace Files\n{\n\n    struct MemBuf : std::streambuf\n    {\n        MemBuf(char const* buffer, size_t size)\n            // a streambuf isn't specific to istreams, so we need a non-const pointer :/\n            : bufferStart(const_cast<char*>(buffer))\n            , bufferEnd(bufferStart + size)\n        {\n            this->setg(bufferStart, bufferStart, bufferEnd);\n        }\n\n        pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) override\n        {\n            if (dir == std::ios_base::cur)\n                gbump(off);\n            else\n                setg(bufferStart, (dir == std::ios_base::beg ? bufferStart : bufferEnd) + off, bufferEnd);\n\n            return gptr() - bufferStart;\n        }\n\n        pos_type seekpos(pos_type pos, std::ios_base::openmode which) override\n        {\n            return seekoff(pos, std::ios_base::beg, which);\n        }\n\n    protected:\n        char* bufferStart;\n        char* bufferEnd;\n    };\n\n    /// @brief A variant of std::istream that reads from a constant in-memory buffer.\n    struct IMemStream: virtual MemBuf, std::istream\n    {\n        IMemStream(char const* buffer, size_t size)\n            : MemBuf(buffer, size)\n            , std::istream(static_cast<std::streambuf*>(this))\n        {\n        }\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/files/multidircollection.cpp",
    "content": "#include \"multidircollection.hpp\"\n\n#include <boost/filesystem.hpp>\n\n#include <components/debug/debuglog.hpp>\n\nnamespace Files\n{\n    struct NameEqual\n    {\n        bool mStrict;\n\n        NameEqual (bool strict) : mStrict (strict) {}\n\n        bool operator() (const std::string& left, const std::string& right) const\n        {\n            if (mStrict)\n                return left==right;\n\n            std::size_t len = left.length();\n\n            if (len!=right.length())\n                return false;\n\n            for (std::size_t i=0; i<len; ++i)\n            {\n                char l = Misc::StringUtils::toLower (left[i]);\n                char r = Misc::StringUtils::toLower (right[i]);\n\n                if (l!=r)\n                    return false;\n            }\n\n            return true;\n        }\n    };\n\n    MultiDirCollection::MultiDirCollection(const Files::PathContainer& directories,\n        const std::string& extension, bool foldCase)\n    : mFiles (NameLess (!foldCase))\n    {\n        NameEqual equal (!foldCase);\n\n        for (PathContainer::const_iterator iter = directories.begin();\n            iter!=directories.end(); ++iter)\n        {\n            if (!boost::filesystem::is_directory(*iter))\n            {\n                Log(Debug::Info) << \"Skipping invalid directory: \" << (*iter).string();\n                continue;\n            }\n\n            for (boost::filesystem::directory_iterator dirIter(*iter);\n                    dirIter != boost::filesystem::directory_iterator(); ++dirIter)\n            {\n                boost::filesystem::path path = *dirIter;\n\n                if (!equal (extension, path.extension().string()))\n                    continue;\n\n                std::string filename = path.filename().string();\n\n                TIter result = mFiles.find (filename);\n\n                if (result==mFiles.end())\n                {\n                    mFiles.insert (std::make_pair (filename, path));\n                }\n                else if (result->first==filename)\n                {\n                    mFiles[filename] = path;\n                }\n                else\n                {\n                    // handle case folding\n                    mFiles.erase (result->first);\n                    mFiles.insert (std::make_pair (filename, path));\n                }\n            }\n        }\n    }\n\n    boost::filesystem::path MultiDirCollection::getPath (const std::string& file) const\n    {\n        TIter iter = mFiles.find (file);\n\n        if (iter==mFiles.end())\n            throw std::runtime_error (\"file \" + file + \" not found\");\n\n        return iter->second;\n    }\n\n    bool MultiDirCollection::doesExist (const std::string& file) const\n    {\n        return mFiles.find (file)!=mFiles.end();\n    }\n\n    MultiDirCollection::TIter MultiDirCollection::begin() const\n    {\n        return mFiles.begin();\n    }\n\n    MultiDirCollection::TIter MultiDirCollection::end() const\n    {\n        return mFiles.end();\n    }\n}\n"
  },
  {
    "path": "components/files/multidircollection.hpp",
    "content": "#ifndef COMPONENTS_FILES_MULTIDIRSOLLECTION_HPP\n#define COMPONENTS_FILES_MULTIDIRSOLLECTION_HPP\n\n#include <map>\n#include <vector>\n#include <string>\n#include <cctype>\n\n#include <boost/filesystem/path.hpp>\n\n#include <components/misc/stringops.hpp>\n\nnamespace Files\n{\n    typedef std::vector<boost::filesystem::path> PathContainer;\n\n    struct NameLess\n    {\n        bool mStrict;\n\n        NameLess (bool strict) : mStrict (strict) {}\n\n        bool operator() (const std::string& left, const std::string& right) const\n        {\n            if (mStrict)\n                return left<right;\n\n            std::size_t min = std::min (left.length(), right.length());\n\n            for (std::size_t i=0; i<min; ++i)\n            {\n                char l = Misc::StringUtils::toLower (left[i]);\n                char r = Misc::StringUtils::toLower (right[i]);\n\n                if (l<r)\n                    return true;\n                if (l>r)\n                    return false;\n            }\n\n            return left.length()<right.length();\n        }\n    };\n\n    /// \\brief File collection across several directories\n    ///\n    /// This class lists all files with one specific extensions within one or more\n    /// directories. If the same file appears more than once, the file in the directory\n    /// with the higher priority is used.\n    class MultiDirCollection\n    {\n        public:\n\n            typedef std::map<std::string, boost::filesystem::path, NameLess> TContainer;\n            typedef TContainer::const_iterator TIter;\n\n        private:\n\n            TContainer mFiles;\n\n        public:\n\n            MultiDirCollection (const Files::PathContainer& directories,\n                const std::string& extension, bool foldCase);\n            ///< Directories are listed with increasing priority.\n            /// \\param extension The extension that should be listed in this collection. Must\n            /// contain the leading dot.\n            /// \\param foldCase Ignore filename case\n\n            boost::filesystem::path getPath (const std::string& file) const;\n            ///< Return full path (including filename) of \\a file.\n            ///\n            /// If the file does not exist, an exception is thrown. \\a file must include\n            /// the extension.\n\n            bool doesExist (const std::string& file) const;\n            ///< \\return Does a file with the given name exist?\n\n            TIter begin() const;\n            ///< Return iterator pointing to the first file.\n\n            TIter end() const;\n            ///< Return iterator pointing past the last file.\n\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/files/windowspath.cpp",
    "content": "#include \"windowspath.hpp\"\n\n#if defined(_WIN32) || defined(__WINDOWS__)\n\n#include <cstring>\n\n#include <shlobj.h>\n#include <shlwapi.h>\n#include <winreg.h>\n\n#include <boost/locale.hpp>\nnamespace bconv = boost::locale::conv;\n\n#include <components/debug/debuglog.hpp>\n\n/**\n * FIXME: Someone with Windows system should check this and correct if necessary\n * FIXME: MAX_PATH is irrelevant for extended-length paths, i.e. \\\\?\\...\n */\n\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n\nWindowsPath::WindowsPath(const std::string& application_name)\n    : mName(application_name)\n{\n    /*  Since on Windows boost::path.string() returns string of narrow\n        characters in local encoding, it is required to path::imbue()\n        with UTF-8 encoding (generated for empty name from boost::locale)\n        to handle Unicode in platform-agnostic way using std::string.\n\n        See boost::filesystem and boost::locale reference for details.\n    */\n    boost::filesystem::path::imbue(boost::locale::generator().generate(\"\"));\n\n    boost::filesystem::path localPath = getLocalPath();\n    if (!SetCurrentDirectoryA(localPath.string().c_str()))\n        Log(Debug::Warning) << \"Error \" << GetLastError() << \" when changing current directory\";\n}\n\nboost::filesystem::path WindowsPath::getUserConfigPath() const\n{\n    boost::filesystem::path userPath(\".\");\n\n    WCHAR path[MAX_PATH + 1];\n    memset(path, 0, sizeof(path));\n\n    if(SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, nullptr, 0, path)))\n    {\n        userPath = boost::filesystem::path(bconv::utf_to_utf<char>(path));\n    }\n\n    return userPath / \"My Games\" / mName;\n}\n\nboost::filesystem::path WindowsPath::getUserDataPath() const\n{\n    // Have some chaos, windows people!\n    return getUserConfigPath();\n}\n\nboost::filesystem::path WindowsPath::getGlobalConfigPath() const\n{\n    boost::filesystem::path globalPath(\".\");\n\n    WCHAR path[MAX_PATH + 1];\n    memset(path, 0, sizeof(path));\n\n    if(SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, nullptr, 0, path)))\n    {\n        globalPath = boost::filesystem::path(bconv::utf_to_utf<char>(path));\n    }\n\n    return globalPath / mName;\n}\n\nboost::filesystem::path WindowsPath::getLocalPath() const\n{\n    boost::filesystem::path localPath(\"./\");\n    WCHAR path[MAX_PATH + 1];\n    memset(path, 0, sizeof(path));\n\n    if (GetModuleFileNameW(nullptr, path, MAX_PATH + 1) > 0)\n    {\n        localPath = boost::filesystem::path(bconv::utf_to_utf<char>(path)).parent_path() / \"/\";\n    }\n\n    // lookup exe path\n    return localPath;\n}\n\nboost::filesystem::path WindowsPath::getGlobalDataPath() const\n{\n    return getGlobalConfigPath();\n}\n\nboost::filesystem::path WindowsPath::getCachePath() const\n{\n    return getUserConfigPath() / \"cache\";\n}\n\nboost::filesystem::path WindowsPath::getInstallPath() const\n{\n    boost::filesystem::path installPath(\"\");\n\n    HKEY hKey;\n\n    LPCTSTR regkey = TEXT(\"SOFTWARE\\\\Bethesda Softworks\\\\Morrowind\");\n    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS)\n    {\n        //Key existed, let's try to read the install dir\n        std::vector<char> buf(512);\n        int len = 512;\n\n        if (RegQueryValueEx(hKey, TEXT(\"Installed Path\"), nullptr, nullptr, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS)\n        {\n            installPath = &buf[0];\n        }\n        RegCloseKey(hKey);\n    }\n\n    return installPath;\n}\n\n} /* namespace Files */\n\n#endif /* defined(_WIN32) || defined(__WINDOWS__) */\n"
  },
  {
    "path": "components/files/windowspath.hpp",
    "content": "#ifndef COMPONENTS_FILES_WINDOWSPATH_HPP\n#define COMPONENTS_FILES_WINDOWSPATH_HPP\n\n#if defined(_WIN32) || defined(__WINDOWS__)\n\n#include <boost/filesystem.hpp>\n\n/**\n * \\namespace Files\n */\nnamespace Files\n{\n\n/**\n * \\struct WindowsPath\n */\nstruct WindowsPath\n{\n    /**\n     * \\brief WindowsPath constructor.\n     *\n     * \\param [in] application_name - The name of the application.\n     */\n    WindowsPath(const std::string& application_name);\n\n    /**\n     * \\brief Returns user path i.e.:\n     * \"X:\\Documents And Settings\\<User name>\\My Documents\\My Games\\\"\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getUserConfigPath() const;\n\n    boost::filesystem::path getUserDataPath() const;\n\n    /**\n     * \\brief Returns \"X:\\Program Files\\\"\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getGlobalConfigPath() const;\n\n    /**\n     * \\brief Return local path which is a location where\n     * an application was started\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getLocalPath() const;\n\n    /**\n     * \\brief\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getCachePath() const;\n\n    /**\n     * \\brief Return same path like getGlobalPath\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getGlobalDataPath() const;\n\n    /**\n     * \\brief Gets the path of the installed Morrowind version if there is one.\n     *\n     * \\return boost::filesystem::path\n     */\n    boost::filesystem::path getInstallPath() const;\n\n    std::string mName;\n};\n\n} /* namespace Files */\n\n#endif /* defined(_WIN32) || defined(__WINDOWS__) */\n\n#endif /* COMPONENTS_FILES_WINDOWSPATH_HPP */\n"
  },
  {
    "path": "components/fontloader/fontloader.cpp",
    "content": "#include \"fontloader.hpp\"\n\n#include <stdexcept>\n\n#include <osg/Image>\n\n#include <osgDB/WriteFile>\n\n#include <MyGUI_ResourceManager.h>\n#include <MyGUI_FontManager.h>\n#include <MyGUI_ResourceManualFont.h>\n#include <MyGUI_XmlDocument.h>\n#include <MyGUI_FactoryManager.h>\n#include <MyGUI_RenderManager.h>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/vfs/manager.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/myguiplatform/myguitexture.hpp>\n\n#include <components/settings/settings.hpp>\n\nnamespace\n{\n    unsigned long utf8ToUnicode(const std::string& utf8)\n    {\n        size_t i = 0;\n        unsigned long unicode;\n        size_t numbytes;\n        unsigned char ch = utf8[i++];\n        if (ch <= 0x7F)\n        {\n            unicode = ch;\n            numbytes = 0;\n        }\n        else if (ch <= 0xBF)\n        {\n            throw std::logic_error(\"not a UTF-8 string\");\n        }\n        else if (ch <= 0xDF)\n        {\n            unicode = ch&0x1F;\n            numbytes = 1;\n        }\n        else if (ch <= 0xEF)\n        {\n            unicode = ch&0x0F;\n            numbytes = 2;\n        }\n        else if (ch <= 0xF7)\n        {\n            unicode = ch&0x07;\n            numbytes = 3;\n        }\n        else\n        {\n            throw std::logic_error(\"not a UTF-8 string\");\n        }\n        for (size_t j = 0; j < numbytes; ++j)\n        {\n            ch = utf8[i++];\n            if (ch < 0x80 || ch > 0xBF)\n                throw std::logic_error(\"not a UTF-8 string\");\n            unicode <<= 6;\n            unicode += ch & 0x3F;\n        }\n        if (unicode >= 0xD800 && unicode <= 0xDFFF)\n            throw std::logic_error(\"not a UTF-8 string\");\n        if (unicode > 0x10FFFF)\n            throw std::logic_error(\"not a UTF-8 string\");\n\n        return unicode;\n    }\n\n    // getUtf8, aka the worst function ever written.\n    // This includes various hacks for dealing with Morrowind's .fnt files that are *mostly*\n    // in the expected win12XX encoding, but also have randomly swapped characters sometimes.\n    // Looks like the Morrowind developers found standard encodings too boring and threw in some twists for fun.\n    std::string getUtf8 (unsigned char c, ToUTF8::Utf8Encoder& encoder, ToUTF8::FromType encoding)\n    {\n        if (encoding == ToUTF8::WINDOWS_1250)\n        {\n            // Hacks for polish font\n            unsigned char win1250;\n            std::map<unsigned char, unsigned char> conv;\n            conv[0x80] = 0xc6;\n            conv[0x81] = 0x9c;\n            conv[0x82] = 0xe6;\n            conv[0x83] = 0xb3;\n            conv[0x84] = 0xf1;\n            conv[0x85] = 0xb9;\n            conv[0x86] = 0xbf;\n            conv[0x87] = 0x9f;\n            conv[0x88] = 0xea;\n            conv[0x89] = 0xea;\n            conv[0x8a] = 0x0; // not contained in win1250\n            conv[0x8b] = 0x0; // not contained in win1250\n            conv[0x8c] = 0x8f;\n            conv[0x8d] = 0xaf;\n            conv[0x8e] = 0xa5;\n            conv[0x8f] = 0x8c;\n            conv[0x90] = 0xca;\n            conv[0x93] = 0xa3;\n            conv[0x94] = 0xf6;\n            conv[0x95] = 0xf3;\n            conv[0x96] = 0xaf;\n            conv[0x97] = 0x8f;\n            conv[0x99] = 0xd3;\n            conv[0x9a] = 0xd1;\n            conv[0x9c] = 0x0; // not contained in win1250\n            conv[0xa0] = 0xb9;\n            conv[0xa1] = 0xaf;\n            conv[0xa2] = 0xf3;\n            conv[0xa3] = 0xbf;\n            conv[0xa4] = 0x0; // not contained in win1250\n            conv[0xe1] = 0x8c;\n            // Can't remember if this was supposed to read 0xe2, or is it just an extraneous copypaste?\n            //conv[0xe1] = 0x8c;\n            conv[0xe3] = 0x0; // not contained in win1250\n            conv[0xf5] = 0x0; // not contained in win1250\n\n            if (conv.find(c) != conv.end())\n                win1250 = conv[c];\n            else\n                win1250 = c;\n            return encoder.getUtf8(std::string(1, win1250));\n        }\n        else\n            return encoder.getUtf8(std::string(1, c));\n    }\n\n    void fail (Files::IStreamPtr file, const std::string& fileName, const std::string& message)\n    {\n        std::stringstream error;\n        error << \"Font loading error: \" << message;\n        error << \"\\n  File: \" << fileName;\n        error << \"\\n  Offset: 0x\" << std::hex << file->tellg();\n        throw std::runtime_error(error.str());\n    }\n\n}\n\nnamespace Gui\n{\n\n    FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath, float scalingFactor)\n        : mVFS(vfs)\n        , mUserDataPath(userDataPath)\n        , mFontHeight(16)\n        , mScalingFactor(scalingFactor)\n    {\n        if (encoding == ToUTF8::WINDOWS_1252)\n            mEncoding = ToUTF8::CP437;\n        else\n            mEncoding = encoding;\n\n        int fontSize = Settings::Manager::getInt(\"font size\", \"GUI\");\n        mFontHeight = std::min(std::max(12, fontSize), 20);\n\n        MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate(\"Resource\");\n        MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate(\"Resource\") = MyGUI::newDelegate(this, &FontLoader::loadFontFromXml);\n    }\n\n    FontLoader::~FontLoader()\n    {\n        try\n        {\n            MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate(\"Resource\");\n        }\n        catch(const MyGUI::Exception& e)\n        {\n            Log(Debug::Error) << \"Error in the FontLoader destructor: \" << e.what();\n        }\n\n        for (std::vector<MyGUI::ITexture*>::iterator it = mTextures.begin(); it != mTextures.end(); ++it)\n            delete *it;\n        mTextures.clear();\n\n        for (std::vector<MyGUI::ResourceManualFont*>::iterator it = mFonts.begin(); it != mFonts.end(); ++it)\n        {\n            try\n            {\n                MyGUI::ResourceManager::getInstance().removeByName((*it)->getResourceName());\n            }\n            catch(const MyGUI::Exception& e)\n            {\n                Log(Debug::Error) << \"Error in the destructor: \" << e.what();\n            }\n        }\n\n        mFonts.clear();\n    }\n\n    void FontLoader::loadBitmapFonts(bool exportToFile)\n    {\n        const std::map<std::string, VFS::File*>& index = mVFS->getIndex();\n\n        std::string pattern = \"Fonts/\";\n        mVFS->normalizeFilename(pattern);\n\n        std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(pattern);\n        while (found != index.end())\n        {\n            const std::string& name = found->first;\n            if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern)\n            {\n                size_t pos = name.find_last_of('.');\n                if (pos != std::string::npos && name.compare(pos, name.size()-pos, \".fnt\") == 0)\n                    loadBitmapFont(name, exportToFile);\n            }\n            else\n                break;\n            ++found;\n        }\n    }\n\n    void FontLoader::loadTrueTypeFonts()\n    {\n        osgMyGUI::DataManager* dataManager = dynamic_cast<osgMyGUI::DataManager*>(&osgMyGUI::DataManager::getInstance());\n        if (!dataManager)\n        {\n            Log(Debug::Error) << \"Can not load TrueType fonts: osgMyGUI::DataManager is not available.\";\n            return;\n        }\n\n        const std::string cfg = dataManager->getDataPath(\"\");\n        const std::string fontFile = mUserDataPath + \"/\" + \"Fonts\" + \"/\" + \"openmw_font.xml\";\n        if (!boost::filesystem::exists(fontFile))\n            return;\n\n        dataManager->setResourcePath(mUserDataPath + \"/\" + \"Fonts\");\n        MyGUI::ResourceManager::getInstance().load(\"openmw_font.xml\");\n        dataManager->setResourcePath(cfg);\n    }\n\n\n    typedef struct\n    {\n        float x;\n        float y;\n    } Point;\n\n    typedef struct\n    {\n        float u1; // appears unused, always 0\n        Point top_left;\n        Point top_right;\n        Point bottom_left;\n        Point bottom_right;\n        float width;\n        float height;\n        float u2; // appears unused, always 0\n        float kerning;\n        float ascent;\n    } GlyphInfo;\n\n    void FontLoader::loadBitmapFont(const std::string &fileName, bool exportToFile)\n    {\n        Files::IStreamPtr file = mVFS->get(fileName);\n\n        float fontSize;\n        file->read((char*)&fontSize, sizeof(fontSize));\n        if (!file->good())\n            fail(file, fileName, \"File too small to be a valid font\");\n\n        int one;\n        file->read((char*)&one, sizeof(one));\n        if (!file->good())\n            fail(file, fileName, \"File too small to be a valid font\");\n\n        if (one != 1)\n            fail(file, fileName, \"Unexpected value\");\n\n        file->read((char*)&one, sizeof(one));\n        if (!file->good())\n            fail(file, fileName, \"File too small to be a valid font\");\n\n        if (one != 1)\n            fail(file, fileName, \"Unexpected value\");\n\n        char name_[284];\n        file->read(name_, sizeof(name_));\n        if (!file->good())\n            fail(file, fileName, \"File too small to be a valid font\");\n        std::string name(name_);\n\n        GlyphInfo data[256];\n        file->read((char*)data, sizeof(data));\n        if (!file->good())\n            fail(file, fileName, \"File too small to be a valid font\");\n\n        file.reset();\n\n        // Create the font texture\n        std::string bitmapFilename = \"Fonts/\" + std::string(name) + \".tex\";\n\n        Files::IStreamPtr bitmapFile = mVFS->get(bitmapFilename);\n\n        int width, height;\n        bitmapFile->read((char*)&width, sizeof(int));\n        bitmapFile->read((char*)&height, sizeof(int));\n\n        if (!bitmapFile->good())\n            fail(bitmapFile, bitmapFilename, \"File too small to be a valid bitmap\");\n\n        if (width <= 0 || height <= 0)\n            fail(bitmapFile, bitmapFilename, \"Width and height must be positive\");\n\n        std::vector<char> textureData;\n        textureData.resize(width*height*4);\n        bitmapFile->read(&textureData[0], width*height*4);\n        if (!bitmapFile->good())\n            fail(bitmapFile, bitmapFilename, \"File too small to be a valid bitmap\");\n        bitmapFile.reset();\n\n        std::string resourceName;\n        if (name.size() >= 5 && Misc::StringUtils::ciEqual(name.substr(0, 5), \"magic\"))\n            resourceName = \"Magic Cards\";\n        else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), \"century\"))\n            resourceName = \"Century Gothic\";\n        else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), \"daedric\"))\n            resourceName = \"Daedric\";\n\n        if (exportToFile)\n        {\n            osg::ref_ptr<osg::Image> image = new osg::Image;\n            image->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);\n            assert (image->isDataContiguous());\n            memcpy(image->data(), &textureData[0], textureData.size());\n\n            Log(Debug::Info) << \"Writing \" << resourceName + \".png\";\n            osgDB::writeImageFile(*image, resourceName + \".png\");\n        }\n\n        // Register the font with MyGUI\n        MyGUI::ResourceManualFont* font = static_cast<MyGUI::ResourceManualFont*>(\n                    MyGUI::FactoryManager::getInstance().createObject(\"Resource\", \"ResourceManualFont\"));\n        mFonts.push_back(font);\n\n        MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture(bitmapFilename);\n        tex->createManual(width, height, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8);\n        unsigned char* texData = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write));\n        memcpy(texData, &textureData[0], textureData.size());\n        tex->unlock();\n\n        // Using ResourceManualFont::setTexture, enable for MyGUI 3.2.3\n        /*\n        osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;\n        texture->setImage(image);\n        osgMyGUI::OSGTexture* myguiTex = new osgMyGUI::OSGTexture(texture);\n        mTextures.push_back(myguiTex);\n        font->setTexture(myguiTex);\n        */\n\n        // We need to emulate loading from XML because the data members are private as of mygui 3.2.0\n        MyGUI::xml::Document xmlDocument;\n        MyGUI::xml::ElementPtr root = xmlDocument.createRoot(\"ResourceManualFont\");\n        root->addAttribute(\"name\", resourceName);\n\n        MyGUI::xml::ElementPtr defaultHeight = root->createChild(\"Property\");\n        defaultHeight->addAttribute(\"key\", \"DefaultHeight\");\n        defaultHeight->addAttribute(\"value\", fontSize);\n        MyGUI::xml::ElementPtr source = root->createChild(\"Property\");\n        source->addAttribute(\"key\", \"Source\");\n        source->addAttribute(\"value\", std::string(bitmapFilename));\n        MyGUI::xml::ElementPtr codes = root->createChild(\"Codes\");\n\n        for(int i = 0; i < 256; i++)\n        {\n            float x1 = data[i].top_left.x*width;\n            float y1 = data[i].top_left.y*height;\n            float w  = data[i].top_right.x*width - x1;\n            float h  = data[i].bottom_left.y*height - y1;\n\n            ToUTF8::Utf8Encoder encoder(mEncoding);\n            unsigned long unicodeVal = utf8ToUnicode(getUtf8(i, encoder, mEncoding));\n\n            MyGUI::xml::ElementPtr code = codes->createChild(\"Code\");\n            code->addAttribute(\"index\", unicodeVal);\n            code->addAttribute(\"coord\", MyGUI::utility::toString(x1) + \" \"\n                                        + MyGUI::utility::toString(y1) + \" \"\n                                        + MyGUI::utility::toString(w) + \" \"\n                                        + MyGUI::utility::toString(h));\n            code->addAttribute(\"advance\", data[i].width);\n            code->addAttribute(\"bearing\", MyGUI::utility::toString(data[i].kerning) + \" \"\n                               + MyGUI::utility::toString((fontSize-data[i].ascent)));\n            code->addAttribute(\"size\", MyGUI::IntSize(static_cast<int>(data[i].width), static_cast<int>(data[i].height)));\n\n            // Fall back from unavailable Windows-1252 encoding symbols to similar characters available in the game fonts\n            std::multimap<int, int> additional; // fallback glyph index, unicode\n            additional.insert(std::make_pair(156, 0x00A2)); // cent sign\n            additional.insert(std::make_pair(89, 0x00A5)); // yen sign\n            additional.insert(std::make_pair(221, 0x00A6)); // broken bar\n            additional.insert(std::make_pair(99, 0x00A9)); // copyright sign\n            additional.insert(std::make_pair(97, 0x00AA)); // prima ordinal indicator\n            additional.insert(std::make_pair(60, 0x00AB)); // double left-pointing angle quotation mark\n            additional.insert(std::make_pair(45, 0x00AD)); // soft hyphen\n            additional.insert(std::make_pair(114, 0x00AE)); // registered trademark symbol\n            additional.insert(std::make_pair(45, 0x00AF)); // macron\n            additional.insert(std::make_pair(241, 0x00B1)); // plus-minus sign\n            additional.insert(std::make_pair(50, 0x00B2)); // superscript two\n            additional.insert(std::make_pair(51, 0x00B3)); // superscript three\n            additional.insert(std::make_pair(44, 0x00B8)); // cedilla\n            additional.insert(std::make_pair(49, 0x00B9)); // superscript one\n            additional.insert(std::make_pair(111, 0x00BA)); // primo ordinal indicator\n            additional.insert(std::make_pair(62, 0x00BB)); // double right-pointing angle quotation mark\n            additional.insert(std::make_pair(63, 0x00BF)); // inverted question mark\n            additional.insert(std::make_pair(65, 0x00C6)); // latin capital ae ligature\n            additional.insert(std::make_pair(79, 0x00D8)); // latin capital o with stroke\n            additional.insert(std::make_pair(97, 0x00E6)); // latin small ae ligature\n            additional.insert(std::make_pair(111, 0x00F8)); // latin small o with stroke\n            additional.insert(std::make_pair(79, 0x0152)); // latin capital oe ligature\n            additional.insert(std::make_pair(111, 0x0153)); // latin small oe ligature\n            additional.insert(std::make_pair(83, 0x015A)); // latin capital s with caron\n            additional.insert(std::make_pair(115, 0x015B)); // latin small s with caron\n            additional.insert(std::make_pair(89, 0x0178)); // latin capital y with diaresis\n            additional.insert(std::make_pair(90, 0x017D)); // latin capital z with caron\n            additional.insert(std::make_pair(122, 0x017E)); // latin small z with caron\n            additional.insert(std::make_pair(102, 0x0192)); // latin small f with hook\n            additional.insert(std::make_pair(94, 0x02C6)); // circumflex modifier\n            additional.insert(std::make_pair(126, 0x02DC)); // small tilde\n            additional.insert(std::make_pair(69, 0x0401)); // cyrillic capital io (no diaeresis latin e is available)\n            additional.insert(std::make_pair(137, 0x0451)); // cyrillic small io\n            additional.insert(std::make_pair(45, 0x2012)); // figure dash\n            additional.insert(std::make_pair(45, 0x2013)); // en dash\n            additional.insert(std::make_pair(45, 0x2014)); // em dash\n            additional.insert(std::make_pair(39, 0x2018)); // left single quotation mark\n            additional.insert(std::make_pair(39, 0x2019)); // right single quotation mark\n            additional.insert(std::make_pair(44, 0x201A)); // single low quotation mark\n            additional.insert(std::make_pair(39, 0x201B)); // single high quotation mark (reversed)\n            additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark\n            additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark\n            additional.insert(std::make_pair(44, 0x201E)); // double low quotation mark\n            additional.insert(std::make_pair(34, 0x201F)); // double high quotation mark (reversed)\n            additional.insert(std::make_pair(43, 0x2020)); // dagger\n            additional.insert(std::make_pair(216, 0x2021)); // double dagger (note: this glyph is not available)\n            additional.insert(std::make_pair(46, 0x2026)); // ellipsis\n            additional.insert(std::make_pair(37, 0x2030)); // per mille sign\n            additional.insert(std::make_pair(60, 0x2039)); // single left-pointing angle quotation mark\n            additional.insert(std::make_pair(62, 0x203A)); // single right-pointing angle quotation mark\n            additional.insert(std::make_pair(101, 0x20AC)); // euro sign\n            additional.insert(std::make_pair(84, 0x2122)); // trademark sign\n            additional.insert(std::make_pair(45, 0x2212)); // minus sign\n\n            for (std::multimap<int, int>::iterator it = additional.begin(); it != additional.end(); ++it)\n            {\n                if (it->first != i)\n                    continue;\n                code = codes->createChild(\"Code\");\n                code->addAttribute(\"index\", it->second);\n                code->addAttribute(\"coord\", MyGUI::utility::toString(x1) + \" \"\n                                            + MyGUI::utility::toString(y1) + \" \"\n                                            + MyGUI::utility::toString(w) + \" \"\n                                            + MyGUI::utility::toString(h));\n                code->addAttribute(\"advance\", data[i].width);\n                code->addAttribute(\"bearing\", MyGUI::utility::toString(data[i].kerning) + \" \"\n                                   + MyGUI::utility::toString((fontSize-data[i].ascent)));\n                code->addAttribute(\"size\", MyGUI::IntSize(static_cast<int>(data[i].width), static_cast<int>(data[i].height)));\n            }\n\n            // ASCII vertical bar, use this as text input cursor\n            if (i == 124)\n            {\n                MyGUI::xml::ElementPtr cursorCode = codes->createChild(\"Code\");\n                cursorCode->addAttribute(\"index\", MyGUI::FontCodeType::Cursor);\n                cursorCode->addAttribute(\"coord\", MyGUI::utility::toString(x1) + \" \"\n                                            + MyGUI::utility::toString(y1) + \" \"\n                                            + MyGUI::utility::toString(w) + \" \"\n                                            + MyGUI::utility::toString(h));\n                cursorCode->addAttribute(\"advance\", data[i].width);\n                cursorCode->addAttribute(\"bearing\", MyGUI::utility::toString(data[i].kerning) + \" \"\n                                   + MyGUI::utility::toString((fontSize-data[i].ascent)));\n                cursorCode->addAttribute(\"size\", MyGUI::IntSize(static_cast<int>(data[i].width), static_cast<int>(data[i].height)));\n            }\n\n            // Question mark, use for NotDefined marker (used for glyphs not existing in the font)\n            if (i == 63)\n            {\n                MyGUI::xml::ElementPtr cursorCode = codes->createChild(\"Code\");\n                cursorCode->addAttribute(\"index\", MyGUI::FontCodeType::NotDefined);\n                cursorCode->addAttribute(\"coord\", MyGUI::utility::toString(x1) + \" \"\n                                            + MyGUI::utility::toString(y1) + \" \"\n                                            + MyGUI::utility::toString(w) + \" \"\n                                            + MyGUI::utility::toString(h));\n                cursorCode->addAttribute(\"advance\", data[i].width);\n                cursorCode->addAttribute(\"bearing\", MyGUI::utility::toString(data[i].kerning) + \" \"\n                                   + MyGUI::utility::toString((fontSize-data[i].ascent)));\n                cursorCode->addAttribute(\"size\", MyGUI::IntSize(static_cast<int>(data[i].width), static_cast<int>(data[i].height)));\n            }\n        }\n\n        // These are required as well, but the fonts don't provide them\n        for (int i=0; i<2; ++i)\n        {\n            MyGUI::FontCodeType::Enum type;\n            if(i == 0)\n                type = MyGUI::FontCodeType::Selected;\n            else // if (i == 1)\n                type = MyGUI::FontCodeType::SelectedBack;\n\n            MyGUI::xml::ElementPtr cursorCode = codes->createChild(\"Code\");\n            cursorCode->addAttribute(\"index\", type);\n            cursorCode->addAttribute(\"coord\", \"0 0 0 0\");\n            cursorCode->addAttribute(\"advance\", \"0\");\n            cursorCode->addAttribute(\"bearing\", \"0 0\");\n            cursorCode->addAttribute(\"size\", \"0 0\");\n        }\n\n        if (exportToFile)\n        {\n            Log(Debug::Info) << \"Writing \" << resourceName + \".xml\";\n            xmlDocument.createDeclaration();\n            xmlDocument.save(resourceName + \".xml\");\n        }\n\n        font->deserialization(root, MyGUI::Version(3,2,0));\n\n        // Setup \"book\" version of font as fallback if we will not use TrueType fonts\n        MyGUI::ResourceManualFont* bookFont = static_cast<MyGUI::ResourceManualFont*>(\n                    MyGUI::FactoryManager::getInstance().createObject(\"Resource\", \"ResourceManualFont\"));\n        mFonts.push_back(bookFont);\n        bookFont->deserialization(root, MyGUI::Version(3,2,0));\n        bookFont->setResourceName(\"Journalbook \" + resourceName);\n\n        // Remove automatically registered fonts\n        for (std::vector<MyGUI::ResourceManualFont*>::iterator it = mFonts.begin(); it != mFonts.end();)\n        {\n            if ((*it)->getResourceName() == font->getResourceName())\n            {\n                MyGUI::ResourceManager::getInstance().removeByName(font->getResourceName());\n                it = mFonts.erase(it);\n            }\n            else if ((*it)->getResourceName() == bookFont->getResourceName())\n            {\n                MyGUI::ResourceManager::getInstance().removeByName(bookFont->getResourceName());\n                it = mFonts.erase(it);\n            }\n            else\n                ++it;\n        }\n\n        MyGUI::ResourceManager::getInstance().addResource(font);\n        MyGUI::ResourceManager::getInstance().addResource(bookFont);\n    }\n\n    void FontLoader::loadFontFromXml(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version)\n    {\n        MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator();\n        bool createCopy = false;\n        while (resourceNode.next(\"Resource\"))\n        {\n            std::string type, name;\n            resourceNode->findAttribute(\"type\", type);\n            resourceNode->findAttribute(\"name\", name);\n\n            if (name.empty())\n                continue;\n\n            if (Misc::StringUtils::ciEqual(type, \"ResourceTrueTypeFont\"))\n            {\n                createCopy = true;\n\n                // For TrueType fonts we should override Size and Resolution properties\n                // to allow to configure font size via config file, without need to edit XML files.\n                // Also we should take UI scaling factor in account.\n                int resolution = Settings::Manager::getInt(\"ttf resolution\", \"GUI\");\n                resolution = std::min(960, std::max(48, resolution)) * mScalingFactor;\n\n                MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild(\"Property\");\n                resolutionNode->addAttribute(\"key\", \"Resolution\");\n                resolutionNode->addAttribute(\"value\", std::to_string(resolution));\n\n                MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild(\"Property\");\n                sizeNode->addAttribute(\"key\", \"Size\");\n                sizeNode->addAttribute(\"value\", std::to_string(mFontHeight));\n            }\n            else if (Misc::StringUtils::ciEqual(type, \"ResourceSkin\") ||\n                     Misc::StringUtils::ciEqual(type, \"AutoSizedResourceSkin\"))\n            {\n                // We should adjust line height for MyGUI widgets depending on font size\n                MyGUI::xml::ElementPtr heightNode = resourceNode->createChild(\"Property\");\n                heightNode->addAttribute(\"key\", \"HeightLine\");\n                heightNode->addAttribute(\"value\", std::to_string(mFontHeight+2));\n            }\n        }\n\n        MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version);\n\n        if (createCopy)\n        {\n            std::unique_ptr<MyGUI::xml::Element> copy{_node->createCopy()};\n\n            MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator();\n            while (copyFont.next(\"Resource\"))\n            {\n                std::string type, name;\n                copyFont->findAttribute(\"type\", type);\n                copyFont->findAttribute(\"name\", name);\n\n                if (name.empty())\n                    continue;\n\n                if (Misc::StringUtils::ciEqual(type, \"ResourceTrueTypeFont\"))\n                {\n                    // Since the journal and books use the custom scaling factor depending on resolution,\n                    // setup separate fonts with different Resolution to fit these windows.\n                    // These fonts have an internal prefix.\n                    int resolution = Settings::Manager::getInt(\"ttf resolution\", \"GUI\");\n                    resolution = std::min(960, std::max(48, resolution));\n\n                    float currentX = Settings::Manager::getInt(\"resolution x\", \"Video\");\n                    float currentY = Settings::Manager::getInt(\"resolution y\", \"Video\");\n                    // TODO: read size from openmw_layout.xml somehow\n                    float heightScale = (currentY / 520);\n                    float widthScale = (currentX / 600);\n                    float uiScale = std::min(widthScale, heightScale);\n                    resolution *= uiScale;\n\n                    MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild(\"Property\");\n                    resolutionNode->addAttribute(\"key\", \"Resolution\");\n                    resolutionNode->addAttribute(\"value\", std::to_string(resolution));\n\n                    copyFont->setAttribute(\"name\", \"Journalbook \" + name);\n                }\n            }\n\n            MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy.get(), _file, _version);\n        }\n    }\n\n    int FontLoader::getFontHeight()\n    {\n        return mFontHeight;\n    }\n}\n"
  },
  {
    "path": "components/fontloader/fontloader.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_FONTLOADER_H\n#define OPENMW_COMPONENTS_FONTLOADER_H\n\n#include \"boost/filesystem/operations.hpp\"\n\n#include <MyGUI_XmlDocument.h>\n#include <MyGUI_Version.h>\n\n#include <components/myguiplatform/myguidatamanager.hpp>\n#include <components/to_utf8/to_utf8.hpp>\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace MyGUI\n{\n    class ITexture;\n    class ResourceManualFont;\n}\n\nnamespace Gui\n{\n    /// @brief loads Morrowind's .fnt/.tex fonts for use with MyGUI and OSG\n    /// @note The FontLoader needs to remain in scope as long as you want to use the loaded fonts.\n    class FontLoader\n    {\n    public:\n        FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath, float scalingFactor);\n        ~FontLoader();\n\n        /// @param exportToFile export the converted fonts (Images and XML with glyph metrics) to files?\n        void loadBitmapFonts (bool exportToFile);\n        void loadTrueTypeFonts ();\n\n        void loadFontFromXml(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version);\n\n        int getFontHeight();\n\n    private:\n        ToUTF8::FromType mEncoding;\n        const VFS::Manager* mVFS;\n        std::string mUserDataPath;\n        int mFontHeight;\n        float mScalingFactor;\n\n        std::vector<MyGUI::ITexture*> mTextures;\n        std::vector<MyGUI::ResourceManualFont*> mFonts;\n\n        /// @param exportToFile export the converted font (Image and XML with glyph metrics) to files?\n        void loadBitmapFont (const std::string& fileName, bool exportToFile);\n\n        FontLoader(const FontLoader&);\n        void operator=(const FontLoader&);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/context.hpp",
    "content": "#ifndef INTERPRETER_CONTEXT_H_INCLUDED\n#define INTERPRETER_CONTEXT_H_INCLUDED\n\n#include <string>\n#include <vector>\n\nnamespace Interpreter\n{\n    class Context\n    {\n        public:\n\n            /*\n                Start of tes3mp addition\n\n                Keep an enumeration of the possible context types\n            */\n            enum CONTEXT_TYPE\n            {\n                CONSOLE = 0,\n                DIALOGUE = 1,\n                SCRIPT_LOCAL = 2,\n                SCRIPT_GLOBAL = 3\n            };\n            /*\n                End of tes3mp addition\n            */\n\n            virtual ~Context() {}\n\n            virtual int getLocalShort (int index) const = 0;\n\n            virtual int getLocalLong (int index) const = 0;\n\n            virtual float getLocalFloat (int index) const = 0;\n\n            virtual void setLocalShort (int index, int value) = 0;\n\n            virtual void setLocalLong (int index, int value) = 0;\n\n            virtual void setLocalFloat (int index, float value) = 0;\n\n            virtual void messageBox (const std::string& message,\n                const std::vector<std::string>& buttons) = 0;\n\n            void messageBox (const std::string& message)\n            {\n                std::vector<std::string> empty;\n                messageBox (message, empty);\n            }\n\n            virtual void report (const std::string& message) = 0;\n\n            virtual int getGlobalShort (const std::string& name) const = 0;\n\n            virtual int getGlobalLong (const std::string& name) const = 0;\n\n            virtual float getGlobalFloat (const std::string& name) const = 0;\n\n            virtual void setGlobalShort (const std::string& name, int value) = 0;\n\n            virtual void setGlobalLong (const std::string& name, int value) = 0;\n\n            virtual void setGlobalFloat (const std::string& name, float value) = 0;\n\n            virtual std::vector<std::string> getGlobals () const = 0;\n\n            virtual char getGlobalType (const std::string& name) const = 0;\n\n            virtual std::string getActionBinding(const std::string& action) const = 0;\n\n            virtual std::string getActorName() const = 0;\n\n            virtual std::string getNPCRace() const = 0;\n\n            virtual std::string getNPCClass() const = 0;\n\n            virtual std::string getNPCFaction() const = 0;\n\n            virtual std::string getNPCRank() const = 0;\n\n            virtual std::string getPCName() const = 0;\n\n            virtual std::string getPCRace() const = 0;\n\n            virtual std::string getPCClass() const = 0;\n\n            virtual std::string getPCRank() const = 0;\n\n            virtual std::string getPCNextRank() const = 0;\n\n            virtual int getPCBounty() const = 0;\n\n            virtual std::string getCurrentCellName() const = 0;\n\n            virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const = 0;\n\n            virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const = 0;\n\n            virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const = 0;\n\n            virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global) = 0;\n\n            virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global) = 0;\n\n            virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global)\n                = 0;\n\n            /*\n                Start of tes3mp addition\n\n                Used for tracking and checking the type of this Context, as well as\n                its current script\n            */\n            virtual unsigned short getContextType() const = 0;\n            virtual std::string getCurrentScriptName() const = 0;\n            virtual void trackContextType(unsigned short interpreterType) = 0;\n            virtual void trackCurrentScriptName(const std::string& name) = 0;\n            /*\n                End of tes3mp addition\n            */\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/controlopcodes.hpp",
    "content": "#ifndef INTERPRETER_CONTROLOPCODES_H_INCLUDED\n#define INTERPRETER_CONTROLOPCODES_H_INCLUDED\n\n#include <stdexcept>\n\n#include \"opcodes.hpp\"\n#include \"runtime.hpp\"\n\nnamespace Interpreter\n{\n    class OpReturn : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                runtime.setPC (-1);\n            }\n    };\n\n    class OpSkipZero : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                runtime.pop();\n\n                if (data==0)\n                    runtime.setPC (runtime.getPC()+1);\n            }\n    };\n\n    class OpSkipNonZero : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                runtime.pop();\n\n                if (data!=0)\n                    runtime.setPC (runtime.getPC()+1);\n            }\n    };\n\n    class OpJumpForward : public Opcode1\n    {\n        public:\n\n            void execute (Runtime& runtime, unsigned int arg0) override\n            {\n                if (arg0==0)\n                    throw std::logic_error (\"infinite loop\");\n\n                runtime.setPC (runtime.getPC()+arg0-1);\n            }\n    };\n\n    class OpJumpBackward : public Opcode1\n    {\n        public:\n\n            void execute (Runtime& runtime, unsigned int arg0) override\n            {\n                if (arg0==0)\n                    throw std::logic_error (\"infinite loop\");\n\n                runtime.setPC (runtime.getPC()-arg0-1);\n            }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/defines.cpp",
    "content": "#include \"defines.hpp\"\n\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n\nnamespace Interpreter{\n\n    bool check(const std::string& str, const std::string& escword, unsigned int* i, unsigned int* start)\n    {\n        bool retval = str.find(escword) == 0;\n        if(retval){\n            (*i) += escword.length();\n            (*start) = (*i) + 1;\n        }\n        return retval;\n    }\n\n    std::vector<std::string> globals;\n\n    bool longerStr(const std::string& a, const std::string& b)\n    {\n        return a.length() > b.length();\n    }\n\n    std::string fixDefinesReal(std::string text, bool dialogue, Context& context)\n    {\n        unsigned int start = 0;\n        std::ostringstream retval;\n        for(unsigned int i = 0; i < text.length(); i++)\n        {\n            char eschar = text[i];\n            if(eschar == '%' || eschar == '^')\n            {\n                retval << text.substr(start, i - start);\n                std::string temp = Misc::StringUtils::lowerCase(text.substr(i+1, 100));\n                \n                bool found = false;\n                try\n                {\n                    if(     (found = check(temp, \"actionslideright\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sRight}\");\n                    }\n                    else if((found = check(temp, \"actionreadymagic\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sReady_Magic}\");\n                    }\n                    else if((found = check(temp, \"actionprevweapon\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sPrevWeapon}\");\n                    }\n                    else if((found = check(temp, \"actionnextweapon\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sNextWeapon}\");\n                    }\n                    else if((found = check(temp, \"actiontogglerun\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sAuto_Run}\");\n                    }\n                    else if((found = check(temp, \"actionslideleft\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sLeft}\");\n                    }\n                    else if((found = check(temp, \"actionreadyitem\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sReady_Weapon}\");\n                    }\n                    else if((found = check(temp, \"actionprevspell\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sPrevSpell}\");\n                    }\n                    else if((found = check(temp, \"actionnextspell\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sNextSpell}\");\n                    }\n                    else if((found = check(temp, \"actionrestmenu\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sRestKey}\");\n                    }\n                    else if((found = check(temp, \"actionmenumode\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sInventory}\");\n                    }\n                    else if((found = check(temp, \"actionactivate\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sActivate}\");\n                    }\n                    else if((found = check(temp, \"actionjournal\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sJournal}\");\n                    }\n                    else if((found = check(temp, \"actionforward\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sForward}\");\n                    }\n                    else if((found = check(temp, \"pccrimelevel\", &i, &start))){\n                        retval << context.getPCBounty();\n                    }\n                    else if((found = check(temp, \"actioncrouch\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sCrouch_Sneak}\");\n                    }\n                    else if((found = check(temp, \"actionjump\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sJump}\");\n                    }\n                    else if((found = check(temp, \"actionback\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sBack}\");\n                    }\n                    else if((found = check(temp, \"actionuse\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sUse}\");\n                    }\n                    else if((found = check(temp, \"actionrun\", &i, &start))){\n                        retval << context.getActionBinding(\"#{sRun}\");\n                    }\n                    else if((found = check(temp, \"pcclass\", &i, &start))){\n                        retval << context.getPCClass();\n                    }\n                    else if((found = check(temp, \"pcrace\", &i, &start))){\n                        retval << context.getPCRace();\n                    }\n                    else if((found = check(temp, \"pcname\", &i, &start))){\n                        retval << context.getPCName();\n                    }\n                    else if((found = check(temp, \"cell\", &i, &start))){\n                        retval << context.getCurrentCellName();\n                    }\n\n                    else if(dialogue) { // In Dialogue, not messagebox\n                        if(     (found = check(temp, \"faction\", &i, &start))){\n                            retval << context.getNPCFaction();\n                        }\n                        else if((found = check(temp, \"nextpcrank\", &i, &start))){\n                            retval << context.getPCNextRank();\n                        }\n                        else if((found = check(temp, \"pcnextrank\", &i, &start))){\n                            retval << context.getPCNextRank();\n                        }\n                        else if((found = check(temp, \"pcrank\", &i, &start))){\n                            retval << context.getPCRank();\n                        }\n                        else if((found = check(temp, \"rank\", &i, &start))){\n                            retval << context.getNPCRank();\n                        }\n\n                        else if((found = check(temp, \"class\", &i, &start))){\n                            retval << context.getNPCClass();\n                        }\n                        else if((found = check(temp, \"race\", &i, &start))){\n                            retval << context.getNPCRace();\n                        }\n                        else if((found = check(temp, \"name\", &i, &start))){\n                            retval << context.getActorName();\n                        }\n                    }\n                    else { // In messagebox or book, not dialogue\n\n                        /* empty outside dialogue */\n                        if(     (found = check(temp, \"faction\", &i, &start)));\n                        else if((found = check(temp, \"nextpcrank\", &i, &start)));\n                        else if((found = check(temp, \"pcnextrank\", &i, &start)));\n                        else if((found = check(temp, \"pcrank\", &i, &start)));\n                        else if((found = check(temp, \"rank\", &i, &start)));\n\n                        /* uses pc in messageboxes */\n                        else if((found = check(temp, \"class\", &i, &start))){\n                            retval << context.getPCClass();\n                        }\n                        else if((found = check(temp, \"race\", &i, &start))){\n                            retval << context.getPCRace();\n                        }\n                        else if((found = check(temp, \"name\", &i, &start))){\n                            retval << context.getPCName();\n                        }\n                    }\n\n                    /* Not a builtin, try global variables */\n                    if(!found){\n                        /* if list of globals is empty, grab it and sort it by descending string length */\n                        if(globals.empty()){\n                            globals = context.getGlobals();\n                            sort(globals.begin(), globals.end(), longerStr);\n                        }\n\n                        for(unsigned int j = 0; j < globals.size(); j++){\n                            if(globals[j].length() > temp.length()){ // Just in case there's a global with a huuuge name\n                                temp = text.substr(i+1, globals[j].length());\n                                transform(temp.begin(), temp.end(), temp.begin(), ::tolower);\n                            }\n\n                            found = check(temp, globals[j], &i, &start);\n                            if(found){\n                                char type = context.getGlobalType(globals[j]);\n\n                                switch(type){\n                                    case 's': retval << context.getGlobalShort(globals[j]);  break;\n                                    case 'l': retval << context.getGlobalLong(globals[j]); break;\n                                    case 'f': retval << context.getGlobalFloat(globals[j]); break;\n                                }\n                                break;\n                            }\n                        }\n                    }\n                }\n                catch (std::exception& e)\n                {\n                    Log(Debug::Error) << \"Error: Failed to replace escape character, with the following error: \" << e.what();\n                    Log(Debug::Error) << \"Full text below:\\n\" << text;\n                }\n\n                // Not found, or error\n                if(!found){\n                    /* leave unmodified */\n                    i += 1;\n                    start = i;\n                    retval << eschar;\n                }\n            }\n        }\n        retval << text.substr(start, text.length() - start);\n        return retval.str ();\n    }\n\n    std::string fixDefinesDialog(const std::string& text, Context& context){\n        return fixDefinesReal(text, true, context);\n    }\n\n    std::string fixDefinesMsgBox(const std::string& text, Context& context){\n        return fixDefinesReal(text, false, context);\n    }\n\n    std::string fixDefinesBook(const std::string& text, Context& context){\n        return fixDefinesReal(text, false, context);\n    }\n}\n"
  },
  {
    "path": "components/interpreter/defines.hpp",
    "content": "#ifndef INTERPRETER_DEFINES_H_INCLUDED\n#define INTERPRETER_DEFINES_H_INCLUDED\n\n#include <string>\n#include \"context.hpp\"\n\nnamespace Interpreter{\n    std::string fixDefinesDialog(const std::string& text, Context& context);\n    std::string fixDefinesMsgBox(const std::string& text, Context& context);\n    std::string fixDefinesBook(const std::string& text, Context& context);\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/docs/vmformat.txt",
    "content": "Note: a word is considered to be 32 bit long.\n\nHeader (4 words):\nword: number of words in code block\nword: number of words in integer literal block\nword: number of words in float literal block\nword: number of words in string literal block\n\nBody (variable length):\ncode block\ninteger literal block (contains a collection of 1 word long integers)\nfloat literal block (contains a collection of 1 word long floating point numbers)\nstring literal block (contains a collection of strings of variable length, word-padded)\n\nCode bit-patterns:\n\n3322222222221111111111\n10987654321098765432109876543210\n00ccccccAAAAAAAAAAAAAAAAAAAAAAAA segment 0: 64 opcodes, 1 24-bit argument\n01ccccccAAAAAAAAAAAABBBBBBBBBBBB segment 1: 64 opcodes, 2 12-bit arguments\n10ccccccccccAAAAAAAAAAAAAAAAAAAA segment 2: 1024 opcodes, 1 20-bit argument\n110000ccccccccccccccccccAAAAAAAA segment 3: 262144 opcodes, 1 8-bit argument\n110001ccccccccccAAAAAAAABBBBBBBB segment 4: 1024 opcodes, 2 8-bit arguments\n110010cccccccccccccccccccccccccc segment 5: 67108864 opcodes, no arguments\nother bit-patterns reserved\n\nlegent:\nc: code\nA: argument 0\nB: argument 1\n\nSegment 0:\nop  0: push arg0\nop  1: move pc ahead by arg0\nop  2: move pc back by arg0\nopcodes 3-31 unused\nopcodes 32-63 reserved for extensions\n\nSegment 1:\nopcodes 0-31 unused\nopcodes 32-63 reserved for extensions\n\nSegment 2:\nopcodes 0-511 unused\nopcodes 512-1023 reserved for extensions\n\nSegment 3:\nop    0: show message box with message string literal index in stack[0];\n         buttons (if any) in stack[arg0]..stack[1];\n         additional arguments (if any) in stack[arg0+n]..stack[arg0+1];\n         n is determined according to the message string\n         all arguments are removed from stack\nopcodes 1-131071 unused\nopcodes 131072-262143 reserved for extensions\n\nSegment 4:\nopcodes 0-511 unused\nopcodes 512-1023 reserved for extensions\n\nSegment 5:\nop   0: store stack[0] in local short stack[1] and pop twice\nop   1: store stack[0] in local long stack[1] and pop twice\nop   2: store stack[0] in local float stack[1] and pop twice\nop   3: convert stack[0] from integer to float\nop   4: replace stack[0] with integer literal index stack[0]\nop   5: replace stack[0] with float literal index stack[0]\nop   6: convert stack[0] from float to integer\nop   7: invert sign of int value stack[0]\nop   8: invert sign of float value stack[0]\nop   9: add (integer) stack[0] to stack[1], pop twice, push result\nop  10: add (float) stack[0] to stack[1], pop twice, push result\nop  11: sub (integer) stack[1] from stack[0], pop twice, push result\nop  12: sub (float) stack[1] from stack[0], pop twice, push result\nop  13: mul (integer) stack[0] with stack[1], pop twice, push result\nop  14: mul (float) stack[0] with stack[1], pop twice, push result\nop  15: div (integer) stack[1] by stack[0], pop twice, push result\nop  16: div (float) stack[1] by stack[0], pop twice, push result\nop  17: convert stack[1] from integer to float\nop  18: convert stack[1] from float to integer\nop  19: take square root of stack[0] (float)\nop  20: return\nop  21: replace stack[0] with local short stack[0]\nop  22: replace stack[0] with local long stack[0]\nop  23: replace stack[0] with local float stack[0]\nop  24: skip next instruction if stack[0]==0; pop\nop  25: skip next instruction if stack[0]!=0; pop\nop  26: compare (intger) stack[1] with stack[0]; pop twice; push 1 if equal, 0 else\nop  27: compare (intger) stack[1] with stack[0]; pop twice; push 1 if no equal, 0 else\nop  28: compare (intger) stack[1] with stack[0]; pop twice; push 1 if lesser than, 0 else\nop  29: compare (intger) stack[1] with stack[0]; pop twice; push 1 if lesser or equal, 0 else\nop  30: compare (intger) stack[1] with stack[0]; pop twice; push 1 if greater than, 0 else\nop  31: compare (intger) stack[1] with stack[0]; pop twice; push 1 if greater or equal, 0 else\nop  32: compare (float) stack[1] with stack[0]; pop twice; push 1 if equal, 0 else\nop  33: compare (float) stack[1] with stack[0]; pop twice; push 1 if no equal, 0 else\nop  34: compare (float) stack[1] with stack[0]; pop twice; push 1 if lesser than, 0 else\nop  35: compare (float) stack[1] with stack[0]; pop twice; push 1 if lesser or equal, 0 else\nop  36: compare (float) stack[1] with stack[0]; pop twice; push 1 if greater than, 0 else\nop  37: compare (float) stack[1] with stack[0]; pop twice; push 1 if greater or equal, 0 else\nopcode 38 unused\nop  39: store stack[0] in global short stack[1] and pop twice\nop  40: store stack[0] in global long stack[1] and pop twice\nop  41: store stack[0] in global float stack[1] and pop twice\nop  42: replace stack[0] with global short stack[0]\nop  43: replace stack[0] with global long stack[0]\nop  44: replace stack[0] with global float stack[0]\nopcodes 45-57 unused\nop  58: report string literal index in stack[0];\n         additional arguments (if any) in stack[n]..stack[1];\n         n is determined according to the message string\n         all arguments are removed from stack\nop 59: store stack[0] in member short stack[2] of object with ID stack[1]\nop 60: store stack[0] in member long stack[2] of object with ID stack[1]\nop 61: store stack[0] in member float stack[2] of object with ID stack[1]\nop 62: replace stack[0] with member short stack[1] of object with ID stack[0]\nop 63: replace stack[0] with member short stack[1] of object with ID stack[0]\nop 64: replace stack[0] with member short stack[1] of object with ID stack[0]\nop 65: store stack[0] in member short stack[2] of global script with ID stack[1]\nop 66: store stack[0] in member long stack[2] of global script with ID stack[1]\nop 67: store stack[0] in member float stack[2] of global script with ID stack[1]\nop 68: replace stack[0] with member short stack[1] of global script with ID stack[0]\nop 69: replace stack[0] with member short stack[1] of global script with ID stack[0]\nop 70: replace stack[0] with member short stack[1] of global script with ID stack[0]\nopcodes 71-33554431 unused\nopcodes 33554432-67108863 reserved for extensions\n"
  },
  {
    "path": "components/interpreter/genericopcodes.hpp",
    "content": "#ifndef INTERPRETER_GENERICOPCODES_H_INCLUDED\n#define INTERPRETER_GENERICOPCODES_H_INCLUDED\n\n#include \"opcodes.hpp\"\n#include \"runtime.hpp\"\n\nnamespace Interpreter\n{\n    class OpPushInt : public Opcode1\n    {\n        public:\n        \n            void execute (Runtime& runtime, unsigned int arg0) override\n            {\n                runtime.push (static_cast<Type_Integer> (arg0));\n            }        \n    };\n    \n    class OpIntToFloat : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                Type_Float floatValue = static_cast<Type_Float> (data);\n                runtime[0].mFloat = floatValue;\n            }           \n    };\n    \n    class OpFloatToInt : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                Type_Float data = runtime[0].mFloat;\n                Type_Integer integerValue = static_cast<Type_Integer> (data);\n                runtime[0].mInteger = integerValue;\n            }           \n    };    \n    \n    class OpNegateInt : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                data = -data;\n                runtime[0].mInteger = data;\n            }           \n    };    \n    \n    class OpNegateFloat : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                Type_Float data = runtime[0].mFloat;\n                data = -data;\n                runtime[0].mFloat = data;\n            }           \n    };\n    \n    class OpIntToFloat1 : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[1].mInteger;\n                Type_Float floatValue = static_cast<Type_Float> (data);\n                runtime[1].mFloat = floatValue;\n            }           \n    };\n    \n    class OpFloatToInt1 : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                Type_Float data = runtime[1].mFloat;\n                Type_Integer integerValue = static_cast<Type_Integer> (data);\n                runtime[1].mInteger = integerValue;\n            }           \n    };            \n}\n\n#endif\n\n"
  },
  {
    "path": "components/interpreter/installopcodes.cpp",
    "content": "#include \"installopcodes.hpp\"\n\n#include <functional>\n\n#include \"interpreter.hpp\"\n#include \"genericopcodes.hpp\"\n#include \"localopcodes.hpp\"\n#include \"mathopcodes.hpp\"\n#include \"controlopcodes.hpp\"\n#include \"miscopcodes.hpp\"\n\nnamespace Interpreter\n{\n    void installOpcodes (Interpreter& interpreter)\n    {\n        // generic\n        interpreter.installSegment0 (0, new OpPushInt);\n        interpreter.installSegment5 (3, new OpIntToFloat);\n        interpreter.installSegment5 (6, new OpFloatToInt);\n        interpreter.installSegment5 (7, new OpNegateInt);\n        interpreter.installSegment5 (8, new OpNegateFloat);\n        interpreter.installSegment5 (17, new OpIntToFloat1);\n        interpreter.installSegment5 (18, new OpFloatToInt1);\n\n        // local variables, global variables & literals\n        interpreter.installSegment5 (0, new OpStoreLocalShort);\n        interpreter.installSegment5 (1, new OpStoreLocalLong);\n        interpreter.installSegment5 (2, new OpStoreLocalFloat);\n        interpreter.installSegment5 (4, new OpFetchIntLiteral);\n        interpreter.installSegment5 (5, new OpFetchFloatLiteral);\n        interpreter.installSegment5 (21, new OpFetchLocalShort);\n        interpreter.installSegment5 (22, new OpFetchLocalLong);\n        interpreter.installSegment5 (23, new OpFetchLocalFloat);\n        interpreter.installSegment5 (39, new OpStoreGlobalShort);\n        interpreter.installSegment5 (40, new OpStoreGlobalLong);\n        interpreter.installSegment5 (41, new OpStoreGlobalFloat);\n        interpreter.installSegment5 (42, new OpFetchGlobalShort);\n        interpreter.installSegment5 (43, new OpFetchGlobalLong);\n        interpreter.installSegment5 (44, new OpFetchGlobalFloat);\n        interpreter.installSegment5 (59, new OpStoreMemberShort (false));\n        interpreter.installSegment5 (60, new OpStoreMemberLong (false));\n        interpreter.installSegment5 (61, new OpStoreMemberFloat (false));\n        interpreter.installSegment5 (62, new OpFetchMemberShort (false));\n        interpreter.installSegment5 (63, new OpFetchMemberLong (false));\n        interpreter.installSegment5 (64, new OpFetchMemberFloat (false));\n        interpreter.installSegment5 (65, new OpStoreMemberShort (true));\n        interpreter.installSegment5 (66, new OpStoreMemberLong (true));\n        interpreter.installSegment5 (67, new OpStoreMemberFloat (true));\n        interpreter.installSegment5 (68, new OpFetchMemberShort (true));\n        interpreter.installSegment5 (69, new OpFetchMemberLong (true));\n        interpreter.installSegment5 (70, new OpFetchMemberFloat (true));\n\n        // math\n        interpreter.installSegment5 (9, new OpAddInt<Type_Integer>);\n        interpreter.installSegment5 (10, new OpAddInt<Type_Float>);\n        interpreter.installSegment5 (11, new OpSubInt<Type_Integer>);\n        interpreter.installSegment5 (12, new OpSubInt<Type_Float>);\n        interpreter.installSegment5 (13, new OpMulInt<Type_Integer>);\n        interpreter.installSegment5 (14, new OpMulInt<Type_Float>);\n        interpreter.installSegment5 (15, new OpDivInt<Type_Integer>);\n        interpreter.installSegment5 (16, new OpDivInt<Type_Float>);\n        interpreter.installSegment5 (19, new OpSquareRoot);\n        interpreter.installSegment5 (26,\n            new OpCompare<Type_Integer, std::equal_to<Type_Integer> >);\n        interpreter.installSegment5 (27,\n            new OpCompare<Type_Integer, std::not_equal_to<Type_Integer> >);\n        interpreter.installSegment5 (28,\n            new OpCompare<Type_Integer, std::less<Type_Integer> >);\n        interpreter.installSegment5 (29,\n            new OpCompare<Type_Integer, std::less_equal<Type_Integer> >);\n        interpreter.installSegment5 (30,\n            new OpCompare<Type_Integer, std::greater<Type_Integer> >);\n        interpreter.installSegment5 (31,\n            new OpCompare<Type_Integer, std::greater_equal<Type_Integer> >);\n\n        interpreter.installSegment5 (32,\n            new OpCompare<Type_Float, std::equal_to<Type_Float> >);\n        interpreter.installSegment5 (33,\n            new OpCompare<Type_Float, std::not_equal_to<Type_Float> >);\n        interpreter.installSegment5 (34,\n            new OpCompare<Type_Float, std::less<Type_Float> >);\n        interpreter.installSegment5 (35,\n            new OpCompare<Type_Float, std::less_equal<Type_Float> >);\n        interpreter.installSegment5 (36,\n            new OpCompare<Type_Float, std::greater<Type_Float> >);\n        interpreter.installSegment5 (37,\n            new OpCompare<Type_Float, std::greater_equal<Type_Float> >);\n\n        // control structures\n        interpreter.installSegment5 (20, new OpReturn);\n        interpreter.installSegment5 (24, new OpSkipZero);\n        interpreter.installSegment5 (25, new OpSkipNonZero);\n        interpreter.installSegment0 (1, new OpJumpForward);\n        interpreter.installSegment0 (2, new OpJumpBackward);\n\n        // misc\n        interpreter.installSegment3 (0, new OpMessageBox);\n        interpreter.installSegment5 (58, new OpReport);\n    }\n}\n"
  },
  {
    "path": "components/interpreter/installopcodes.hpp",
    "content": "#ifndef INTERPRETER_INSTALLOPCODES_H_INCLUDED\n#define INTERPRETER_INSTALLOPCODES_H_INCLUDED\n\nnamespace Interpreter\n{\n    class Interpreter;\n    \n    void installOpcodes (Interpreter& interpreter);\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/interpreter.cpp",
    "content": "#include \"interpreter.hpp\"\n\n#include <cassert>\n#include <stdexcept>\n\n#include \"opcodes.hpp\"\n\nnamespace Interpreter\n{\n    void Interpreter::execute (Type_Code code)\n    {\n        unsigned int segSpec = code>>30;\n\n        switch (segSpec)\n        {\n            case 0:\n            {\n                int opcode = code>>24;\n                unsigned int arg0 = code & 0xffffff;\n\n                std::map<int, Opcode1 *>::iterator iter = mSegment0.find (opcode);\n\n                if (iter==mSegment0.end())\n                    abortUnknownCode (0, opcode);\n\n                iter->second->execute (mRuntime, arg0);\n\n                return;\n            }\n\n            case 2:\n            {\n                int opcode = (code>>20) & 0x3ff;\n                unsigned int arg0 = code & 0xfffff;\n\n                std::map<int, Opcode1 *>::iterator iter = mSegment2.find (opcode);\n\n                if (iter==mSegment2.end())\n                    abortUnknownCode (2, opcode);\n\n                iter->second->execute (mRuntime, arg0);\n\n                return;\n            }\n        }\n\n        segSpec = code>>26;\n\n        switch (segSpec)\n        {\n            case 0x30:\n            {\n                int opcode = (code>>8) & 0x3ffff;\n                unsigned int arg0 = code & 0xff;\n\n                std::map<int, Opcode1 *>::iterator iter = mSegment3.find (opcode);\n\n                if (iter==mSegment3.end())\n                    abortUnknownCode (3, opcode);\n\n                iter->second->execute (mRuntime, arg0);\n\n                return;\n            }\n\n            case 0x32:\n            {\n                int opcode = code & 0x3ffffff;\n\n                std::map<int, Opcode0 *>::iterator iter = mSegment5.find (opcode);\n\n                if (iter==mSegment5.end())\n                    abortUnknownCode (5, opcode);\n\n                iter->second->execute (mRuntime);\n\n                return;\n            }\n        }\n\n        abortUnknownSegment (code);\n    }\n\n    void Interpreter::abortUnknownCode (int segment, int opcode)\n    {\n        const std::string error = \"unknown opcode \" + std::to_string(opcode) + \" in segment \" + std::to_string(segment);\n        throw std::runtime_error (error);\n    }\n\n    void Interpreter::abortUnknownSegment (Type_Code code)\n    {\n        const std::string error = \"opcode outside of the allocated segment range: \" + std::to_string(code);\n        throw std::runtime_error (error);\n    }\n\n    void Interpreter::begin()\n    {\n        if (mRunning)\n        {\n            mCallstack.push (mRuntime);\n            mRuntime.clear();\n        }\n        else\n        {\n            mRunning = true;\n        }\n    }\n\n    void Interpreter::end()\n    {\n        if (mCallstack.empty())\n        {\n            mRuntime.clear();\n            mRunning = false;\n        }\n        else\n        {\n            mRuntime = mCallstack.top();\n            mCallstack.pop();\n        }\n    }\n\n    Interpreter::Interpreter() : mRunning (false)\n    {}\n\n    Interpreter::~Interpreter()\n    {\n        for (std::map<int, Opcode1 *>::iterator iter (mSegment0.begin());\n            iter!=mSegment0.end(); ++iter)\n            delete iter->second;\n\n        for (std::map<int, Opcode1 *>::iterator iter (mSegment2.begin());\n            iter!=mSegment2.end(); ++iter)\n            delete iter->second;\n\n        for (std::map<int, Opcode1 *>::iterator iter (mSegment3.begin());\n            iter!=mSegment3.end(); ++iter)\n            delete iter->second;\n\n        for (std::map<int, Opcode0 *>::iterator iter (mSegment5.begin());\n            iter!=mSegment5.end(); ++iter)\n            delete iter->second;\n    }\n\n    void Interpreter::installSegment0 (int code, Opcode1 *opcode)\n    {\n        assert(mSegment0.find(code) == mSegment0.end());\n        mSegment0.insert (std::make_pair (code, opcode));\n    }\n\n    void Interpreter::installSegment2 (int code, Opcode1 *opcode)\n    {\n        assert(mSegment2.find(code) == mSegment2.end());\n        mSegment2.insert (std::make_pair (code, opcode));\n    }\n\n    void Interpreter::installSegment3 (int code, Opcode1 *opcode)\n    {\n        assert(mSegment3.find(code) == mSegment3.end());\n        mSegment3.insert (std::make_pair (code, opcode));\n    }\n\n    void Interpreter::installSegment5 (int code, Opcode0 *opcode)\n    {\n        assert(mSegment5.find(code) == mSegment5.end());\n        mSegment5.insert (std::make_pair (code, opcode));\n    }\n\n    void Interpreter::run (const Type_Code *code, int codeSize, Context& context)\n    {\n        assert (codeSize>=4);\n\n        begin();\n\n        try\n        {\n            mRuntime.configure (code, codeSize, context);\n\n            int opcodes = static_cast<int> (code[0]);\n\n            const Type_Code *codeBlock = code + 4;\n\n            while (mRuntime.getPC()>=0 && mRuntime.getPC()<opcodes)\n            {\n                Type_Code runCode = codeBlock[mRuntime.getPC()];\n                mRuntime.setPC (mRuntime.getPC()+1);\n                execute (runCode);\n            }\n        }\n        catch (...)\n        {\n            end();\n            throw;\n        }\n\n        end();\n    }\n}\n"
  },
  {
    "path": "components/interpreter/interpreter.hpp",
    "content": "#ifndef INTERPRETER_INTERPRETER_H_INCLUDED\n#define INTERPRETER_INTERPRETER_H_INCLUDED\n\n#include <map>\n#include <stack>\n\n#include \"runtime.hpp\"\n#include \"types.hpp\"\n\nnamespace Interpreter\n{\n    class Opcode0;\n    class Opcode1;\n\n    class Interpreter\n    {\n            std::stack<Runtime> mCallstack;\n            bool mRunning;\n            Runtime mRuntime;\n            std::map<int, Opcode1 *> mSegment0;\n            std::map<int, Opcode1 *> mSegment2;\n            std::map<int, Opcode1 *> mSegment3;\n            std::map<int, Opcode0 *> mSegment5;\n\n            // not implemented\n            Interpreter (const Interpreter&);\n            Interpreter& operator= (const Interpreter&);\n\n            void execute (Type_Code code);\n\n            void abortUnknownCode (int segment, int opcode);\n\n            void abortUnknownSegment (Type_Code code);\n\n            void begin();\n\n            void end();\n\n        public:\n\n            Interpreter();\n\n            ~Interpreter();\n\n            void installSegment0 (int code, Opcode1 *opcode);\n            ///< ownership of \\a opcode is transferred to *this.\n\n            void installSegment2 (int code, Opcode1 *opcode);\n            ///< ownership of \\a opcode is transferred to *this.\n\n            void installSegment3 (int code, Opcode1 *opcode);\n            ///< ownership of \\a opcode is transferred to *this.\n\n            void installSegment5 (int code, Opcode0 *opcode);\n            ///< ownership of \\a opcode is transferred to *this.\n\n            void run (const Type_Code *code, int codeSize, Context& context);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/localopcodes.hpp",
    "content": "#ifndef INTERPRETER_LOCALOPCODES_H_INCLUDED\n#define INTERPRETER_LOCALOPCODES_H_INCLUDED\n\n#include \"opcodes.hpp\"\n#include \"runtime.hpp\"\n#include \"context.hpp\"\n\nnamespace Interpreter\n{\n    class OpStoreLocalShort : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                int index = runtime[1].mInteger;\n\n                runtime.getContext().setLocalShort (index, data);\n\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpStoreLocalLong : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                int index = runtime[1].mInteger;\n\n                runtime.getContext().setLocalLong (index, data);\n\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpStoreLocalFloat : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Float data = runtime[0].mFloat;\n                int index = runtime[1].mInteger;\n\n                runtime.getContext().setLocalFloat (index, data);\n\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpFetchIntLiteral : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer intValue = runtime.getIntegerLiteral (runtime[0].mInteger);\n                runtime[0].mInteger = intValue;\n            }\n    };\n\n    class OpFetchFloatLiteral : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Float floatValue = runtime.getFloatLiteral (runtime[0].mInteger);\n                runtime[0].mFloat = floatValue;\n            }\n    };\n\n    class OpFetchLocalShort : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                int index = runtime[0].mInteger;\n                int value = runtime.getContext().getLocalShort (index);\n                runtime[0].mInteger = value;\n            }\n    };\n\n    class OpFetchLocalLong : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                int index = runtime[0].mInteger;\n                int value = runtime.getContext().getLocalLong (index);\n                runtime[0].mInteger = value;\n            }\n    };\n\n    class OpFetchLocalFloat : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                int index = runtime[0].mInteger;\n                float value = runtime.getContext().getLocalFloat (index);\n                runtime[0].mFloat = value;\n            }\n    };\n\n    class OpStoreGlobalShort : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                int index = runtime[1].mInteger;\n\n                std::string name = runtime.getStringLiteral (index);\n\n                runtime.getContext().setGlobalShort (name, data);\n\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpStoreGlobalLong : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                int index = runtime[1].mInteger;\n\n                std::string name = runtime.getStringLiteral (index);\n\n                runtime.getContext().setGlobalLong (name, data);\n\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpStoreGlobalFloat : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Float data = runtime[0].mFloat;\n                int index = runtime[1].mInteger;\n\n                std::string name = runtime.getStringLiteral (index);\n\n                runtime.getContext().setGlobalFloat (name, data);\n\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpFetchGlobalShort : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                int index = runtime[0].mInteger;\n                std::string name = runtime.getStringLiteral (index);\n                Type_Integer value = runtime.getContext().getGlobalShort (name);\n                runtime[0].mInteger = value;\n            }\n    };\n\n    class OpFetchGlobalLong : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                int index = runtime[0].mInteger;\n                std::string name = runtime.getStringLiteral (index);\n                Type_Integer value = runtime.getContext().getGlobalLong (name);\n                runtime[0].mInteger = value;\n            }\n    };\n\n    class OpFetchGlobalFloat : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                int index = runtime[0].mInteger;\n                std::string name = runtime.getStringLiteral (index);\n                Type_Float value = runtime.getContext().getGlobalFloat (name);\n                runtime[0].mFloat = value;\n            }\n    };\n\n    class OpStoreMemberShort : public Opcode0\n    {\n            bool mGlobal;\n\n        public:\n\n            OpStoreMemberShort (bool global) : mGlobal (global) {}\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                Type_Integer index = runtime[1].mInteger;\n                std::string id = runtime.getStringLiteral (index);\n                index = runtime[2].mInteger;\n                std::string variable = runtime.getStringLiteral (index);\n\n                runtime.getContext().setMemberShort (id, variable, data, mGlobal);\n\n                runtime.pop();\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpStoreMemberLong : public Opcode0\n    {\n            bool mGlobal;\n\n        public:\n\n            OpStoreMemberLong (bool global) : mGlobal (global) {}\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer data = runtime[0].mInteger;\n                Type_Integer index = runtime[1].mInteger;\n                std::string id = runtime.getStringLiteral (index);\n                index = runtime[2].mInteger;\n                std::string variable = runtime.getStringLiteral (index);\n\n                runtime.getContext().setMemberLong (id, variable, data, mGlobal);\n\n                runtime.pop();\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpStoreMemberFloat : public Opcode0\n    {\n            bool mGlobal;\n\n        public:\n\n            OpStoreMemberFloat (bool global) : mGlobal (global) {}\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Float data = runtime[0].mFloat;\n                Type_Integer index = runtime[1].mInteger;\n                std::string id = runtime.getStringLiteral (index);\n                index = runtime[2].mInteger;\n                std::string variable = runtime.getStringLiteral (index);\n\n                runtime.getContext().setMemberFloat (id, variable, data, mGlobal);\n\n                runtime.pop();\n                runtime.pop();\n                runtime.pop();\n            }\n    };\n\n    class OpFetchMemberShort : public Opcode0\n    {\n            bool mGlobal;\n\n        public:\n\n            OpFetchMemberShort (bool global) : mGlobal (global) {}\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer index = runtime[0].mInteger;\n                std::string id = runtime.getStringLiteral (index);\n                index = runtime[1].mInteger;\n                std::string variable = runtime.getStringLiteral (index);\n                runtime.pop();\n\n                int value = runtime.getContext().getMemberShort (id, variable, mGlobal);\n                runtime[0].mInteger = value;\n            }\n    };\n\n    class OpFetchMemberLong : public Opcode0\n    {\n            bool mGlobal;\n\n        public:\n\n            OpFetchMemberLong (bool global) : mGlobal (global) {}\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer index = runtime[0].mInteger;\n                std::string id = runtime.getStringLiteral (index);\n                index = runtime[1].mInteger;\n                std::string variable = runtime.getStringLiteral (index);\n                runtime.pop();\n\n                int value = runtime.getContext().getMemberLong (id, variable, mGlobal);\n                runtime[0].mInteger = value;\n            }\n    };\n\n    class OpFetchMemberFloat : public Opcode0\n    {\n            bool mGlobal;\n\n        public:\n\n            OpFetchMemberFloat (bool global) : mGlobal (global) {}\n\n            void execute (Runtime& runtime) override\n            {\n                Type_Integer index = runtime[0].mInteger;\n                std::string id = runtime.getStringLiteral (index);\n                index = runtime[1].mInteger;\n                std::string variable = runtime.getStringLiteral (index);\n                runtime.pop();\n\n                float value = runtime.getContext().getMemberFloat (id, variable, mGlobal);\n                runtime[0].mFloat = value;\n            }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/mathopcodes.hpp",
    "content": "#ifndef INTERPRETER_MATHOPCODES_H_INCLUDED\n#define INTERPRETER_MATHOPCODES_H_INCLUDED\n\n#include <stdexcept>\n#include <cmath>\n\n#include \"opcodes.hpp\"\n#include \"runtime.hpp\"\n\nnamespace Interpreter\n{\n    template<typename T>\n    class OpAddInt : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                T result = getData<T> (runtime[1]) + getData<T> (runtime[0]);\n                \n                runtime.pop();\n                \n                getData<T> (runtime[0]) = result;\n            }           \n    };\n\n    template<typename T>\n    class OpSubInt : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                T result = getData<T> (runtime[1]) - getData<T> (runtime[0]);\n                \n                runtime.pop();\n\n                getData<T> (runtime[0]) = result;\n            }           \n    };\n\n    template<typename T>\n    class OpMulInt : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                T result = getData<T> (runtime[1]) * getData<T> (runtime[0]);\n                \n                runtime.pop();\n\n                getData<T> (runtime[0]) = result;\n            }           \n    };\n\n    template<typename T>\n    class OpDivInt : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                T left = getData<T> (runtime[0]);\n            \n                if (left==0)\n                    throw std::runtime_error (\"division by zero\");\n            \n                T result = getData<T> (runtime[1]) / left;\n                \n                runtime.pop();\n\n                getData<T> (runtime[0]) = result;\n            }           \n    };\n    \n    class OpSquareRoot : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                Type_Float value = runtime[0].mFloat;\n                \n                if (value<0)\n                    throw std::runtime_error (\n                        \"square root of negative number (we aren't that imaginary)\");\n                \n                value = std::sqrt (value);\n                \n                runtime[0].mFloat = value;\n            }           \n    };    \n    \n    template<typename T, typename C>\n    class OpCompare : public Opcode0\n    {\n        public:\n        \n            void execute (Runtime& runtime) override\n            {\n                int result = C() (getData<T> (runtime[1]), getData<T> (runtime[0]));\n                \n                runtime.pop();\n                \n                runtime[0].mInteger = result;\n            }           \n    };    \n}\n\n#endif\n\n"
  },
  {
    "path": "components/interpreter/miscopcodes.hpp",
    "content": "#ifndef INTERPRETER_MISCOPCODES_H_INCLUDED\n#define INTERPRETER_MISCOPCODES_H_INCLUDED\n\n#include <stdexcept>\n#include <vector>\n#include <string>\n#include <sstream>\n#include <algorithm>\n\n#include \"opcodes.hpp\"\n#include \"runtime.hpp\"\n#include \"defines.hpp\"\n\n#include <components/misc/messageformatparser.hpp>\n\nnamespace Interpreter\n{\n    class RuntimeMessageFormatter : public Misc::MessageFormatParser\n    {\n        private:\n            std::string mFormattedMessage;\n            Runtime& mRuntime;\n\n        protected:\n            void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision, Notation notation) override\n            {\n                std::ostringstream out;\n                out.fill(padding);\n                if (width != -1)\n                    out.width(width);\n                if (precision != -1)\n                    out.precision(precision);\n\n                switch (placeholder)\n                {\n                    case StringPlaceholder:\n                        {\n                            int index = mRuntime[0].mInteger;\n                            mRuntime.pop();\n\n                            out << mRuntime.getStringLiteral(index);\n                            mFormattedMessage += out.str();\n                        }\n                        break;\n                    case IntegerPlaceholder:\n                        {\n                            Type_Integer value = mRuntime[0].mInteger;\n                            mRuntime.pop();\n\n                            out << value;\n                            mFormattedMessage += out.str();\n                        }\n                        break;\n                    case FloatPlaceholder:\n                        {\n                            float value = mRuntime[0].mFloat;\n                            mRuntime.pop();\n\n                            if (notation == FixedNotation)\n                            {\n                                out << std::fixed << value;\n                                mFormattedMessage += out.str();\n                            }\n                            else if (notation == ShortestNotation)\n                            {\n                                out << value;\n                                std::string standard = out.str();\n\n                                out.str(std::string());\n                                out.clear();\n\n                                out << std::scientific << value;\n                                std::string scientific = out.str();\n\n                                mFormattedMessage += standard.length() < scientific.length() ? standard : scientific;\n                            }\n                            else\n                            {\n                                out << std::scientific << value;\n                                mFormattedMessage += out.str();\n                            }\n                        }\n                        break;\n                    default:\n                        break;\n                }\n            }\n\n            void visitedCharacter(char c) override\n            {\n                mFormattedMessage += c;\n            }\n\n        public:\n            RuntimeMessageFormatter(Runtime& runtime)\n                : mRuntime(runtime)\n            {\n            }\n\n            void process(const std::string& message) override\n            {\n                mFormattedMessage.clear();\n                MessageFormatParser::process(message);\n            }\n\n            std::string getFormattedMessage() const\n            {\n                return mFormattedMessage;\n            }\n    };\n\n    inline std::string formatMessage (const std::string& message, Runtime& runtime)\n    {\n        RuntimeMessageFormatter formatter(runtime);\n        formatter.process(message);\n\n        std::string formattedMessage = formatter.getFormattedMessage();\n        formattedMessage = fixDefinesMsgBox(formattedMessage, runtime.getContext());\n        return formattedMessage;\n    }\n\n    class OpMessageBox : public Opcode1\n    {\n        public:\n\n            void execute (Runtime& runtime, unsigned int arg0) override\n            {\n                // message\n                int index = runtime[0].mInteger;\n                runtime.pop();\n                std::string message = runtime.getStringLiteral (index);\n\n                // buttons\n                std::vector<std::string> buttons;\n\n                for (std::size_t i=0; i<arg0; ++i)\n                {\n                    index = runtime[0].mInteger;\n                    runtime.pop();\n                    buttons.push_back (runtime.getStringLiteral (index));\n                }\n\n                std::reverse (buttons.begin(), buttons.end());\n\n                // handle additional parameters\n                std::string formattedMessage = formatMessage (message, runtime);\n\n                runtime.getContext().messageBox (formattedMessage, buttons);\n            }\n    };\n\n    class OpReport : public Opcode0\n    {\n        public:\n\n            void execute (Runtime& runtime) override\n            {\n                // message\n                int index = runtime[0].mInteger;\n                runtime.pop();\n                std::string message = runtime.getStringLiteral (index);\n\n                // handle additional parameters\n                std::string formattedMessage = formatMessage (message, runtime);\n\n                runtime.getContext().report (formattedMessage);\n            }\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/opcodes.hpp",
    "content": "#ifndef INTERPRETER_OPCODES_H_INCLUDED\n#define INTERPRETER_OPCODES_H_INCLUDED\n\nnamespace Interpreter\n{\n    class Runtime;\n\n    /// opcode for 0 arguments\n    class Opcode0\n    {\n        public:\n        \n            virtual void execute (Runtime& runtime) = 0;\n            \n            virtual ~Opcode0() {}\n    };\n\n    /// opcode for 1 argument\n    class Opcode1\n    {\n        public:\n        \n            virtual void execute (Runtime& runtime, unsigned int arg0) = 0;\n            \n            virtual ~Opcode1() {}\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/runtime.cpp",
    "content": "#include \"runtime.hpp\"\n\n#include <stdexcept>\n#include <cassert>\n#include <cstring>\n\nnamespace Interpreter\n{\n    Runtime::Runtime() : mContext (nullptr), mCode (nullptr), mCodeSize(0), mPC (0) {}\n\n    int Runtime::getPC() const\n    {\n        return mPC;\n    }\n\n    int Runtime::getIntegerLiteral (int index) const\n    {\n        if (index < 0 || index >= static_cast<int> (mCode[1]))\n            throw std::out_of_range(\"out of range\");\n\n        const Type_Code *literalBlock = mCode + 4 + mCode[0];\n\n        return *reinterpret_cast<const int *> (&literalBlock[index]);\n    }\n\n    float Runtime::getFloatLiteral (int index) const\n    {\n        if (index < 0 || index >= static_cast<int> (mCode[2]))\n            throw std::out_of_range(\"out of range\");\n\n        const Type_Code *literalBlock = mCode + 4 + mCode[0] + mCode[1];\n\n        return *reinterpret_cast<const float *> (&literalBlock[index]);\n    }\n\n    std::string Runtime::getStringLiteral (int index) const\n    {\n        if (index < 0 || static_cast<int> (mCode[3]) <= 0)\n            throw std::out_of_range(\"out of range\");\n\n        const char *literalBlock =\n            reinterpret_cast<const char *> (mCode + 4 + mCode[0] + mCode[1] + mCode[2]);\n\n        int offset = 0;\n\n        for (; index; --index)\n        {\n            offset += static_cast<int>(std::strlen (literalBlock+offset)) + 1;\n            if (offset / 4 >= static_cast<int> (mCode[3]))\n                throw std::out_of_range(\"out of range\");\n        }\n\n        return literalBlock+offset;\n    }\n\n    void Runtime::configure (const Type_Code *code, int codeSize, Context& context)\n    {\n        clear();\n\n        mContext = &context;\n        mCode = code;\n        mCodeSize = codeSize;\n        mPC = 0;\n    }\n\n    void Runtime::clear()\n    {\n        mContext = nullptr;\n        mCode = nullptr;\n        mCodeSize = 0;\n        mStack.clear();\n    }\n\n    void Runtime::setPC (int PC)\n    {\n        mPC = PC;\n    }\n\n    void Runtime::push (const Data& data)\n    {\n        mStack.push_back (data);\n    }\n\n    void Runtime::push (Type_Integer value)\n    {\n        Data data;\n        data.mInteger = value;\n        push (data);\n    }\n\n    void Runtime::push (Type_Float value)\n    {\n        Data data;\n        data.mFloat = value;\n        push (data);\n    }\n\n    void Runtime::pop()\n    {\n        if (mStack.empty())\n            throw std::runtime_error (\"stack underflow\");\n\n        mStack.resize (mStack.size()-1);\n    }\n\n    Data& Runtime::operator[] (int Index)\n    {\n        if (Index<0 || Index>=static_cast<int> (mStack.size()))\n            throw std::runtime_error (\"stack index out of range\");\n\n        return mStack[mStack.size()-Index-1];\n    }\n\n    Context& Runtime::getContext()\n    {\n        assert (mContext);\n        return *mContext;\n    }\n}\n"
  },
  {
    "path": "components/interpreter/runtime.hpp",
    "content": "#ifndef INTERPRETER_RUNTIME_H_INCLUDED\n#define INTERPRETER_RUNTIME_H_INCLUDED\n\n#include <vector>\n#include <string>\n\n#include \"types.hpp\"\n\nnamespace Interpreter\n{\n    class Context;\n\n    /// Runtime data and engine interface\n\n    class Runtime\n    {\n            Context *mContext;\n            const Type_Code *mCode;\n            int mCodeSize;\n            int mPC;\n            std::vector<Data> mStack;\n\n        public:\n\n            Runtime ();\n\n            int getPC() const;\n            ///< return program counter.\n\n            int getIntegerLiteral (int index) const;\n\n            float getFloatLiteral (int index) const;\n\n            std::string getStringLiteral (int index) const;\n\n            void configure (const Type_Code *code, int codeSize, Context& context);\n            ///< \\a context and \\a code must exist as least until either configure, clear or\n            /// the destructor is called. \\a codeSize is given in 32-bit words.\n\n            void clear();\n\n            void setPC (int PC);\n            ///< set program counter.\n\n            void push (const Data& data);\n            ///< push data on stack\n\n            void push (Type_Integer value);\n            ///< push integer data on stack.\n\n            void push (Type_Float value);\n            ///< push float data on stack.\n\n            void pop();\n            ///< pop stack\n\n            Data& operator[] (int Index);\n            ///< Access stack member, counted from the top.\n\n            Context& getContext();\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/interpreter/types.hpp",
    "content": "#ifndef INTERPRETER_TYPES_H_INCLUDED\n#define INTERPRETER_TYPES_H_INCLUDED\n\n#include <stdexcept>\n\nnamespace Interpreter\n{\n    typedef unsigned int Type_Code; // 32 bit\n\n    typedef unsigned int Type_Data; // 32 bit\n    \n    typedef short Type_Short; // 16 bit\n    \n    typedef int Type_Integer; // 32 bit\n    \n    typedef float Type_Float; // 32 bit\n    \n    union Data\n    {\n        Type_Integer mInteger;\n        Type_Float mFloat;\n    };\n    \n    template<typename T>\n    T& getData (Data& data)\n    {\n        throw std::runtime_error (\"unsupported data type\");\n    }\n    \n    template<>\n    inline Type_Integer& getData (Data& data)\n    {\n        return data.mInteger;\n    }\n    \n    template<>\n    inline Type_Float& getData (Data& data)\n    {\n        return data.mFloat;\n    }    \n}\n\n#endif\n\n"
  },
  {
    "path": "components/loadinglistener/loadinglistener.hpp",
    "content": "#ifndef COMPONENTS_LOADINGLISTENER_H\n#define COMPONENTS_LOADINGLISTENER_H\n\n#include <string>\n\nnamespace Loading\n{\n    class Listener\n    {\n    public:\n        /// Set a text label to show on the loading screen.\n        /// @param label The label\n        /// @param important Is the label considered important to show?\n        /// @note \"non-important\" labels may not show on screen if the loading process went so fast\n        /// that the implementation decided not to show a loading screen at all. \"important\" labels\n        /// will show in a separate message-box if the loading screen was not shown.\n        virtual void setLabel (const std::string& label, bool important=false) {}\n\n        /// Start a loading sequence. Must call loadingOff() when done.\n        /// @note To get the loading screen to actually update, you must call setProgress / increaseProgress periodically.\n        /// @note It is best to use the ScopedLoad object instead of using loadingOn()/loadingOff() directly,\n        ///  so that the loading is exception safe.\n        virtual void loadingOn(bool visible=true) {}\n        virtual void loadingOff() {}\n\n        /// Set the total range of progress (e.g. the number of objects to load).\n        virtual void setProgressRange (size_t range) {}\n        /// Set current progress. Valid range is [0, progressRange)\n        virtual void setProgress (size_t value) {}\n        /// Increase current progress, default by 1.\n        virtual void increaseProgress (size_t increase = 1) {}\n\n        virtual ~Listener() = default;\n    };\n\n    /// @brief Used for stopping a loading sequence when the object goes out of scope\n    struct ScopedLoad\n    {\n        ScopedLoad(Listener* l) : mListener(l) { mListener->loadingOn(); }\n        ~ScopedLoad() { mListener->loadingOff(); }\n        Listener* mListener;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/algorithm.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_ALGORITHM_H\n#define OPENMW_COMPONENTS_MISC_ALGORITHM_H\n\n#include <iterator>\n#include <type_traits>\n\nnamespace Misc\n{\n    template <typename Iterator, typename BinaryPredicate, typename Function>\n    inline Iterator forEachUnique(Iterator begin, Iterator end, BinaryPredicate predicate, Function function)\n    {\n        static_assert(\n            std::is_base_of_v<\n                std::forward_iterator_tag,\n                typename std::iterator_traits<Iterator>::iterator_category\n            >\n        );\n        if (begin == end)\n            return begin;\n        function(*begin);\n        auto last = begin;\n        ++begin;\n        while (begin != end)\n        {\n            if (!predicate(*begin, *last))\n            {\n                function(*begin);\n                last = begin;\n            }\n            ++begin;\n        }\n        return begin;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/barrier.hpp",
    "content": "#ifndef OPENMW_BARRIER_H\n#define OPENMW_BARRIER_H\n\n#include <condition_variable>\n#include <mutex>\n\nnamespace Misc\n{\n    /// @brief Synchronize several threads\n    class Barrier\n    {\n        public:\n            /// @param count number of threads to wait on\n            explicit Barrier(int count) : mThreadCount(count), mRendezvousCount(0), mGeneration(0)\n            {}\n\n            /// @brief stop execution of threads until count distinct threads reach this point\n            /// @param func callable to be executed once after all threads have met\n            template <class Callback>\n            void wait(Callback&& func)\n            {\n                std::unique_lock lock(mMutex);\n\n                ++mRendezvousCount;\n                const int currentGeneration = mGeneration;\n                if (mRendezvousCount == mThreadCount)\n                {\n                    ++mGeneration;\n                    mRendezvousCount = 0;\n                    func();\n                    mRendezvous.notify_all();\n                }\n                else\n                {\n                    mRendezvous.wait(lock, [&]() { return mGeneration != currentGeneration; });\n                }\n            }\n\n        private:\n            int mThreadCount;\n            int mRendezvousCount;\n            int mGeneration;\n            mutable std::mutex mMutex;\n            std::condition_variable mRendezvous;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/budgetmeasurement.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_BUDGETMEASUREMENT_H\n#define OPENMW_COMPONENTS_MISC_BUDGETMEASUREMENT_H\n\n\nnamespace Misc\n{\n\nclass BudgetMeasurement\n{\n    std::array<float, 4> mBudgetHistory;\n    std::array<unsigned int, 4> mBudgetStepCount;\n\npublic:\n    BudgetMeasurement(const float default_expense)\n    {\n        mBudgetHistory = {default_expense, default_expense, default_expense, default_expense};\n        mBudgetStepCount = {1, 1, 1, 1};\n    }\n\n    void reset(const float default_expense)\n    {\n        mBudgetHistory = {default_expense, default_expense, default_expense, default_expense};\n        mBudgetStepCount = {1, 1, 1, 1};\n    }\n\n    void update(double delta, unsigned int stepCount, size_t cursor)\n    {\n        mBudgetHistory[cursor%4] = delta;\n        mBudgetStepCount[cursor%4] = stepCount;\n    }\n\n    double get() const\n    {\n        float sum = (mBudgetHistory[0] + mBudgetHistory[1] + mBudgetHistory[2] + mBudgetHistory[3]);\n        unsigned int stepCountSum = (mBudgetStepCount[0] + mBudgetStepCount[1] + mBudgetStepCount[2] + mBudgetStepCount[3]);\n        return sum/float(stepCountSum);\n    }\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/constants.hpp",
    "content": "#ifndef OPENMW_CONSTANTS_H\n#define OPENMW_CONSTANTS_H\n\n#include <string>\n\nnamespace Constants\n{\n\n// The game uses 64 units per yard\nconst float UnitsPerMeter = 69.99125109f;\nconst float UnitsPerFoot = 21.33333333f;\n\n// Sound speed in meters per second\nconst float SoundSpeedInAir = 343.3f;\nconst float SoundSpeedUnderwater = 1484.0f;\n\n// Gravity constant in m/sec^2\n// Note: 8.96 m/sec^2 = 9.8 yards/sec^2\n// Probaly original engine's developers just forgot\n// that their engine uses yards instead of meters\n// and used standart gravity value as it is\nconst float GravityConst = 8.96f;\n\n// Size of one exterior cell in game units\nconst int CellSizeInUnits = 8192;\n\n// Size of active cell grid in cells (it is a square with the (2 * CellGridRadius + 1) cells side)\nconst int CellGridRadius = 1;\n\n// A label to mark night/day visual switches\nconst std::string NightDayLabel = \"NightDaySwitch\";\n\n// A label to mark visual switches for herbalism feature\nconst std::string HerbalismLabel = \"HerbalismSwitch\";\n\n// Percentage height at which projectiles are spawned from an actor\nconst float TorsoHeight = 0.75f;\n\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/convert.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_CONVERT_H\n#define OPENMW_COMPONENTS_MISC_CONVERT_H\n\n#include <components/esm/loadpgrd.hpp>\n\n#include <LinearMath/btTransform.h>\n#include <LinearMath/btVector3.h>\n#include <LinearMath/btQuaternion.h>\n#include <osg/Vec3f>\n#include <osg/Quat>\n\nnamespace Misc\n{\nnamespace Convert\n{\n    inline osg::Vec3f makeOsgVec3f(const float* values)\n    {\n        return osg::Vec3f(values[0], values[1], values[2]);\n    }\n\n    inline osg::Vec3f makeOsgVec3f(const btVector3& value)\n    {\n        return osg::Vec3f(value.x(), value.y(), value.z());\n    }\n\n    inline osg::Vec3f makeOsgVec3f(const ESM::Pathgrid::Point& value)\n    {\n        return osg::Vec3f(value.mX, value.mY, value.mZ);\n    }\n\n    inline btVector3 toBullet(const osg::Vec3f& vec)\n    {\n        return btVector3(vec.x(), vec.y(), vec.z());\n    }\n\n    inline btQuaternion toBullet(const osg::Quat& quat)\n    {\n        return btQuaternion(quat.x(), quat.y(), quat.z(), quat.w());\n    }\n\n    inline osg::Vec3f toOsg(const btVector3& vec)\n    {\n        return osg::Vec3f(vec.x(), vec.y(), vec.z());\n    }\n\n    inline osg::Quat toOsg(const btQuaternion& quat)\n    {\n        return osg::Quat(quat.x(), quat.y(), quat.z(), quat.w());\n    }\n}\n}\n\n#endif"
  },
  {
    "path": "components/misc/coordinateconverter.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H\n#define OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H\n\n#include <components/esm/defs.hpp>\n#include <components/esm/loadcell.hpp>\n#include <components/esm/loadland.hpp>\n#include <components/esm/loadpgrd.hpp>\n\nnamespace Misc\n{\n    /// \\brief convert coordinates between world and local cell\n    class CoordinateConverter\n    {\n        public:\n            CoordinateConverter(bool exterior, int cellX, int cellY)\n                : mCellX(exterior ? cellX * ESM::Land::REAL_SIZE : 0),\n                  mCellY(exterior ? cellY * ESM::Land::REAL_SIZE : 0)\n            {\n            }\n\n            explicit CoordinateConverter(const ESM::Cell* cell)\n                : CoordinateConverter(cell->isExterior(), cell->mData.mX, cell->mData.mY)\n            {\n            }\n\n            /// in-place conversion from local to world\n            void toWorld(ESM::Pathgrid::Point& point) const\n            {\n                point.mX += mCellX;\n                point.mY += mCellY;\n            }\n\n            ESM::Pathgrid::Point toWorldPoint(ESM::Pathgrid::Point point) const\n            {\n                toWorld(point);\n                return point;\n            }\n\n            /// in-place conversion from local to world\n            void toWorld(osg::Vec3f& point) const\n            {\n                point.x() += static_cast<float>(mCellX);\n                point.y() += static_cast<float>(mCellY);\n            }\n\n            /// in-place conversion from world to local\n            void toLocal(osg::Vec3f& point) const\n            {\n                point.x() -= static_cast<float>(mCellX);\n                point.y() -= static_cast<float>(mCellY);\n            }\n\n            osg::Vec3f toLocalVec3(const osg::Vec3f& point) const\n            {\n                return osg::Vec3f(\n                    point.x() - static_cast<float>(mCellX),\n                    point.y() - static_cast<float>(mCellY),\n                    point.z()\n                );\n            }\n\n        private:\n            int mCellX;\n            int mCellY;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/debugging.hpp",
    "content": "#ifndef MISC_DEBUGGING_H\n#define MISC_DEBUGGING_H\n\n#include <boost/filesystem/fstream.hpp>\n#include <boost/iostreams/stream.hpp>\n\n/*\n    Start of tes3mp addition\n\n    Include additional headers for multiplayer purposes\n*/\n#include <components/openmw-mp/TimedLog.hpp>\n/*\n    End of tes3mp addition\n*/\n\n#include <SDL_messagebox.h>\n\nnamespace Misc\n{\n#if defined(_WIN32) && defined(_DEBUG)\n\n    class DebugOutput : public boost::iostreams::sink\n    {\n    public:\n        std::streamsize write(const char *str, std::streamsize size)\n        {\n            // Make a copy for null termination\n            std::string tmp (str, static_cast<unsigned int>(size));\n            // Write string to Visual Studio Debug output\n            OutputDebugString (tmp.c_str ());\n            return size;\n        }\n    };\n#else\n    class Tee : public boost::iostreams::sink\n    {\n    public:\n        Tee(std::ostream &stream, std::ostream &stream2)\n            : out(stream), out2(stream2)\n        {\n        }\n\n        std::streamsize write(const char *str, std::streamsize size)\n        {\n            out.write (str, size);\n            out.flush();\n            out2.write (str, size);\n            out2.flush();\n            return size;\n        }\n\n    private:\n        std::ostream &out;\n        std::ostream &out2;\n    };\n#endif\n}\n\nint wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& logName)\n{\n    // Some objects used to redirect cout and cerr\n    // Scope must be here, so this still works inside the catch block for logging exceptions\n    std::streambuf* cout_rdbuf = std::cout.rdbuf ();\n    std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();\n\n#if !(defined(_WIN32) && defined(_DEBUG))\n    boost::iostreams::stream_buffer<Misc::Tee> coutsb;\n    boost::iostreams::stream_buffer<Misc::Tee> cerrsb;\n#endif\n\n    std::ostream oldcout(cout_rdbuf);\n    std::ostream oldcerr(cerr_rdbuf);\n\n    boost::filesystem::ofstream logfile;\n\n    int ret = 0;\n    try\n    {\n        Files::ConfigurationManager cfgMgr;\n#if defined(_WIN32) && defined(_DEBUG)\n        // Redirect cout and cerr to VS debug output when running in debug mode\n        boost::iostreams::stream_buffer<Misc::DebugOutput> sb;\n        sb.open(Misc::DebugOutput());\n        std::cout.rdbuf (&sb);\n        std::cerr.rdbuf (&sb);\n#else\n        // Redirect cout and cerr to the log file\n        logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName));\n\n        coutsb.open (Misc::Tee(logfile, oldcout));\n        cerrsb.open (Misc::Tee(logfile, oldcerr));\n\n        std::cout.rdbuf (&coutsb);\n        std::cerr.rdbuf (&cerrsb);\n\n        /*\n            Start of tes3mp addition\n\n            Initialize the logger added for multiplayer\n        */\n        LOG_INIT(TimedLog::LOG_INFO);\n        /*\n            End of tes3mp addition\n        */\n#endif\n        ret = innerApplication(argc, argv);\n    }\n    catch (std::exception& e)\n    {\n#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))\n        if (!isatty(fileno(stdin)))\n#endif\n            SDL_ShowSimpleMessageBox(0, \"OpenMW: Fatal error\", e.what(), NULL);\n\n        std::cerr << \"\\nERROR: \" << e.what() << std::endl;\n\n        ret = 1;\n    }\n\n    // Restore cout and cerr\n    std::cout.rdbuf(cout_rdbuf);\n    std::cerr.rdbuf(cerr_rdbuf);\n\n    return ret;\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/endianness.hpp",
    "content": "#ifndef COMPONENTS_MISC_ENDIANNESS_H\n#define COMPONENTS_MISC_ENDIANNESS_H\n\n#include <cstdint>\n#include <cstring>\n#include <type_traits>\n\nnamespace Misc\n{\n\n    // Two-way conversion little-endian <-> big-endian\n    template <typename T>\n    void swapEndiannessInplace(T& v)\n    {\n        static_assert(std::is_arithmetic_v<T>);\n        static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);\n\n        if constexpr (sizeof(T) == 2)\n        {\n            uint16_t v16;\n            std::memcpy(&v16, &v, sizeof(T));\n            v16 = (v16 >> 8) | (v16 << 8);\n            std::memcpy(&v, &v16, sizeof(T));\n        }\n        if constexpr (sizeof(T) == 4)\n        {\n            uint32_t v32;\n            std::memcpy(&v32, &v, sizeof(T));\n            v32 = (v32 >> 24) | ((v32 >> 8) & 0xff00) | ((v32 & 0xff00) << 8) | (v32 << 24);\n            std::memcpy(&v, &v32, sizeof(T));\n        }\n        if constexpr (sizeof(T) == 8)\n        {\n            uint64_t v64;\n            std::memcpy(&v64, &v, sizeof(T));\n            v64 = (v64 >> 56) | ((v64 & 0x00ff'0000'0000'0000) >> 40) | ((v64 & 0x0000'ff00'0000'0000) >> 24)\n                | ((v64 & 0x0000'00ff'0000'0000) >> 8) | ((v64 & 0x0000'0000'ff00'0000) << 8)\n                | ((v64 & 0x0000'0000'00ff'0000) << 24) | ((v64 & 0x0000'0000'0000'ff00) << 40) | (v64 << 56);\n            std::memcpy(&v, &v64, sizeof(T));\n        }\n    }\n\n    #ifdef _WIN32\n    constexpr bool IS_LITTLE_ENDIAN = true;\n    constexpr bool IS_BIG_ENDIAN = false;\n    #else\n    constexpr bool IS_LITTLE_ENDIAN = __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__;\n    constexpr bool IS_BIG_ENDIAN = __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__;\n    #endif\n\n    // Usage: swapEndiannessInplaceIf<IS_BIG_ENDIAN>(v)  - native to little-endian or back\n    //        swapEndiannessInplaceIf<IS_LITTLE_ENDIAN>(v)  - native to big-endian or back\n    template <bool C, typename T>\n    void swapEndiannessInplaceIf(T& v)\n    {\n        static_assert(std::is_arithmetic_v<T>);\n        static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);\n        if constexpr (C)\n            swapEndiannessInplace(v);\n    }\n\n    template <typename T>\n    T toLittleEndian(T v)\n    {\n        swapEndiannessInplaceIf<IS_BIG_ENDIAN>(v);\n        return v;\n    }\n    template <typename T>\n    T fromLittleEndian(T v)\n    {\n        swapEndiannessInplaceIf<IS_BIG_ENDIAN>(v);\n        return v;\n    }\n\n    template <typename T>\n    T toBigEndian(T v)\n    {\n        swapEndiannessInplaceIf<IS_LITTLE_ENDIAN>(v);\n        return v;\n    }\n    template <typename T>\n    T fromBigEndian(T v)\n    {\n        swapEndiannessInplaceIf<IS_LITTLE_ENDIAN>(v);\n        return v;\n    }\n\n}\n\n#endif // COMPONENTS_MISC_ENDIANNESS_H\n"
  },
  {
    "path": "components/misc/frameratelimiter.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_FRAMERATELIMITER_H\n#define OPENMW_COMPONENTS_MISC_FRAMERATELIMITER_H\n\n#include <chrono>\n#include <thread>\n\nnamespace Misc\n{\n    class FrameRateLimiter\n    {\n        public:\n            template <class Rep, class Ratio>\n            explicit FrameRateLimiter(std::chrono::duration<Rep, Ratio> maxFrameDuration,\n                                      std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now())\n                : mMaxFrameDuration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(maxFrameDuration))\n                , mLastMeasurement(now)\n                , mLastFrameDuration(0)\n            {}\n\n            std::chrono::steady_clock::duration getLastFrameDuration() const\n            {\n                return mLastFrameDuration;\n            }\n\n            void limit(std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now())\n            {\n                const auto passed = now - mLastMeasurement;\n                const auto left = mMaxFrameDuration - passed;\n                if (left > left.zero())\n                {\n                    std::this_thread::sleep_for(left);\n                    mLastMeasurement = now + left;\n                    mLastFrameDuration = mMaxFrameDuration;\n                }\n                else\n                {\n                    mLastMeasurement = now;\n                    mLastFrameDuration = passed;\n                }\n            }\n\n        private:\n            std::chrono::steady_clock::duration mMaxFrameDuration;\n            std::chrono::steady_clock::time_point mLastMeasurement;\n            std::chrono::steady_clock::duration mLastFrameDuration;\n    };\n\n    inline Misc::FrameRateLimiter makeFrameRateLimiter(float frameRateLimit)\n    {\n        if (frameRateLimit > 0.0f)\n            return Misc::FrameRateLimiter(std::chrono::duration<float>(1.0f / frameRateLimit));\n        else\n            return Misc::FrameRateLimiter(std::chrono::steady_clock::duration::zero());\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/guarded.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_GUARDED_H\n#define OPENMW_COMPONENTS_MISC_GUARDED_H\n\n#include <mutex>\n#include <memory>\n#include <condition_variable>\n\nnamespace Misc\n{\n    template <class T>\n    class Locked\n    {\n        public:\n            Locked(std::mutex& mutex, T& value)\n                : mLock(mutex), mValue(value)\n            {}\n\n            T& get() const\n            {\n                return mValue.get();\n            }\n\n            T* operator ->() const\n            {\n                return std::addressof(get());\n            }\n\n            T& operator *() const\n            {\n                return get();\n            }\n\n        private:\n            std::unique_lock<std::mutex> mLock;\n            std::reference_wrapper<T> mValue;\n    };\n\n    template <class T>\n    class ScopeGuarded\n    {\n        public:\n            ScopeGuarded()\n                : mMutex()\n                , mValue()\n            {}\n\n            ScopeGuarded(const T& value)\n                : mMutex()\n                , mValue(value)\n            {}\n\n            ScopeGuarded(T&& value)\n                : mMutex()\n                , mValue(std::move(value))\n            {}\n\n            template <class ... Args>\n            ScopeGuarded(Args&& ... args)\n                : mMutex()\n                , mValue(std::forward<Args>(args) ...)\n            {}\n\n            ScopeGuarded(const ScopeGuarded& other)\n                : mMutex()\n                , mValue(other.lock().get())\n            {}\n\n            ScopeGuarded(ScopeGuarded&& other)\n                : mMutex()\n                , mValue(std::move(other.lock().get()))\n            {}\n\n            Locked<T> lock()\n            {\n                return Locked<T>(mMutex, mValue);\n            }\n\n            Locked<const T> lockConst() const\n            {\n                return Locked<const T>(mMutex, mValue);\n            }\n\n            template <class Predicate>\n            void wait(std::condition_variable& cv, Predicate&& predicate)\n            {\n                std::unique_lock<std::mutex> lock(mMutex);\n                cv.wait(lock, [&] { return predicate(mValue); });\n            }\n\n        private:\n            mutable std::mutex mMutex;\n            T mValue;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/hash.hpp",
    "content": "#ifndef MISC_HASH_H\n#define MISC_HASH_H\n\nnamespace Misc\n{\n    /// Implemented similar to the boost::hash_combine\n    template <class T>\n    inline void hashCombine(std::size_t& seed, const T& v)\n    {\n        std::hash<T> hasher;\n        seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);\n    }\n}\n\n#endif"
  },
  {
    "path": "components/misc/helpviewer.cpp",
    "content": "#include \"helpviewer.hpp\"\n\n#include <QString>\n#include <QDesktopServices>\n#include <QUrl>\n\nvoid Misc::HelpViewer::openHelp(const char* url)\n{\n    QString link {OPENMW_DOC_BASEURL};\n    link.append(url);\n    QDesktopServices::openUrl(QUrl(link));\n}\n"
  },
  {
    "path": "components/misc/helpviewer.hpp",
    "content": "#pragma once\n\nnamespace Misc {\n    namespace HelpViewer {\n        void openHelp(const char* url);\n    }\n}\n"
  },
  {
    "path": "components/misc/mathutil.hpp",
    "content": "#ifndef MISC_MATHUTIL_H\n#define MISC_MATHUTIL_H\n\n#include <osg/Math>\n#include <osg/Vec2f>\n\nnamespace Misc\n{\n\n    /// Normalizes given angle to the range [-PI, PI]. E.g. PI*3/2 -> -PI/2.\n    inline double normalizeAngle(double angle)\n    {\n        double fullTurns = angle / (2 * osg::PI) + 0.5;\n        return (fullTurns - floor(fullTurns) - 0.5) * (2 * osg::PI);\n    }\n    \n    /// Rotates given 2d vector counterclockwise. Angle is in radians.\n    inline osg::Vec2f rotateVec2f(osg::Vec2f vec, float angle)\n    {\n        float s = std::sin(angle);\n        float c = std::cos(angle);\n        return osg::Vec2f(vec.x() * c + vec.y() * -s, vec.x() * s + vec.y() * c);\n    }\n\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/messageformatparser.cpp",
    "content": "#include \"messageformatparser.hpp\"\n\nnamespace Misc\n{\n    MessageFormatParser::~MessageFormatParser() {}\n\n    void MessageFormatParser::process(const std::string& m)\n    {\n        for (unsigned int i = 0; i < m.size(); ++i)\n        {\n            if (m[i] == '%')\n            {\n                if (++i < m.size())\n                {\n                    if (m[i] == '%')\n                        visitedCharacter('%');\n                    else\n                    {\n                        char pad = ' ';\n                        if (m[i] == '0' || m[i] == ' ')\n                        {\n                            pad = m[i];\n                            ++i;\n                        }\n\n                        int width = 0;\n                        bool widthSet = false;\n                        while (i < m.size() && m[i] >= '0' && m[i] <= '9')\n                        {\n                            width = width * 10 + (m[i] - '0');\n                            widthSet = true;\n                            ++i;\n                        }\n\n                        if (i < m.size())\n                        {\n                            int precision = -1;\n                            if (m[i] == '.')\n                            {\n                                precision = 0;\n                                while (++i < m.size() && m[i] >= '0' && m[i] <= '9')\n                                {\n                                    precision = precision * 10 + (m[i] - '0');\n                                }\n                            }\n\n                            if (i < m.size())\n                            {\n                                width = (widthSet) ? width : -1;\n\n                                if (m[i] == 'S' || m[i] == 's')\n                                    visitedPlaceholder(StringPlaceholder, pad, width, precision, FixedNotation);\n                                else if (m[i] == 'd' || m[i] == 'i')\n                                    visitedPlaceholder(IntegerPlaceholder, pad, width, precision, FixedNotation);\n                                else if (m[i] == 'f' || m[i] == 'F')\n                                    visitedPlaceholder(FloatPlaceholder, pad, width, precision, FixedNotation);\n                                else if (m[i] == 'e' || m[i] == 'E')\n                                    visitedPlaceholder(FloatPlaceholder, pad, width, precision, ScientificNotation);\n                                else if (m[i] == 'g' || m[i] == 'G')\n                                    visitedPlaceholder(FloatPlaceholder, pad, width, precision, ShortestNotation);\n                            }\n                        }\n                    }\n                }\n            }\n            else\n            {\n                visitedCharacter(m[i]);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "components/misc/messageformatparser.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_MESSAGEFORMATPARSER_H\n#define OPENMW_COMPONENTS_MISC_MESSAGEFORMATPARSER_H\n\n#include <string>\n\nnamespace Misc\n{\n    class MessageFormatParser\n    {\n        protected:\n            enum Placeholder\n            {\n                StringPlaceholder,\n                IntegerPlaceholder,\n                FloatPlaceholder\n            };\n\n            enum Notation\n            {\n                FixedNotation,\n                ScientificNotation,\n                ShortestNotation\n            };\n\n            virtual void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision, Notation notation) = 0;\n            virtual void visitedCharacter(char c) = 0;\n\n        public:\n            virtual ~MessageFormatParser();\n\n            virtual void process(const std::string& message);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/objectpool.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_OBJECTPOOL_H\n#define OPENMW_COMPONENTS_MISC_OBJECTPOOL_H\n\n#include <deque>\n#include <memory>\n#include <vector>\n\nnamespace Misc\n{\n    template <class T>\n    class ObjectPool;\n\n    template <class T>\n    class ObjectPtrDeleter\n    {\n        public:\n            ObjectPtrDeleter(std::nullptr_t)\n                : mPool(nullptr) {}\n\n            ObjectPtrDeleter(ObjectPool<T>& pool)\n                : mPool(&pool) {}\n\n            void operator()(T* object) const\n            {\n                mPool->recycle(object);\n            }\n\n        private:\n            ObjectPool<T>* mPool;\n    };\n\n    template <class T>\n    struct ObjectPtr final : std::unique_ptr<T, ObjectPtrDeleter<T>>\n    {\n        using std::unique_ptr<T, ObjectPtrDeleter<T>>::unique_ptr;\n        using std::unique_ptr<T, ObjectPtrDeleter<T>>::operator=;\n\n        ObjectPtr()\n            : ObjectPtr(nullptr) {}\n\n        ObjectPtr(std::nullptr_t)\n            : std::unique_ptr<T, ObjectPtrDeleter<T>>(nullptr, nullptr) {}\n    };\n\n    template <class T>\n    class ObjectPool\n    {\n        friend class ObjectPtrDeleter<T>;\n\n        public:\n            ObjectPool()\n                : mObjects(std::make_unique<std::deque<T>>()) {}\n\n            ObjectPtr<T> get()\n            {\n                T* object;\n\n                if (!mUnused.empty())\n                {\n                    object = mUnused.back();\n                    mUnused.pop_back();\n                }\n                else\n                {\n                    mObjects->emplace_back();\n                    object = &mObjects->back();\n                }\n\n                return ObjectPtr<T>(object, ObjectPtrDeleter<T>(*this));\n            }\n\n        private:\n            std::unique_ptr<std::deque<T>> mObjects;\n            std::vector<T*> mUnused;\n\n            void recycle(T* object)\n            {\n                mUnused.push_back(object);\n            }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/resourcehelpers.cpp",
    "content": "#include \"resourcehelpers.hpp\"\n\n#include <sstream>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/vfs/manager.hpp>\n\nnamespace\n{\n\n\n    struct MatchPathSeparator\n    {\n        bool operator()( char ch ) const\n        {\n            return ch == '\\\\' || ch == '/';\n        }\n    };\n\n    std::string\n    getBasename( std::string const& pathname )\n    {\n        return std::string(\n            std::find_if( pathname.rbegin(), pathname.rend(),\n                          MatchPathSeparator() ).base(),\n            pathname.end() );\n    }\n\n}\n\nbool Misc::ResourceHelpers::changeExtensionToDds(std::string &path)\n{\n    std::string::size_type pos = path.rfind('.');\n    if(pos != std::string::npos && path.compare(pos, path.length() - pos, \".dds\") != 0)\n    {\n        path.replace(pos, path.length(), \".dds\");\n        return true;\n    }\n    return false;\n}\n\nstd::string Misc::ResourceHelpers::correctResourcePath(const std::string &topLevelDirectory, const std::string &resPath, const VFS::Manager* vfs)\n{\n    /* Bethesda at some point converted all their BSA\n     * textures from tga to dds for increased load speed, but all\n     * texture file name references were kept as .tga.\n     */\n\n    std::string prefix1 = topLevelDirectory + '\\\\';\n    std::string prefix2 = topLevelDirectory + '/';\n\n    std::string correctedPath = resPath;\n    Misc::StringUtils::lowerCaseInPlace(correctedPath);\n\n    // Apparently, leading separators are allowed\n    while (correctedPath.size() && (correctedPath[0] == '/' || correctedPath[0] == '\\\\'))\n        correctedPath.erase(0, 1);\n\n    if(correctedPath.compare(0, prefix1.size(), prefix1.data()) != 0 &&\n       correctedPath.compare(0, prefix2.size(), prefix2.data()) != 0)\n        correctedPath = prefix1 + correctedPath;\n\n    std::string origExt = correctedPath;\n\n    // since we know all (GOTY edition or less) textures end\n    // in .dds, we change the extension\n    bool changedToDds = changeExtensionToDds(correctedPath);\n    if (vfs->exists(correctedPath))\n        return correctedPath;\n    // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods)\n    // verify, and revert if false (this call succeeds quickly, but fails slowly)\n    if (changedToDds && vfs->exists(origExt))\n        return origExt;\n\n    // fall back to a resource in the top level directory if it exists\n    std::string fallback = topLevelDirectory + \"\\\\\" + getBasename(correctedPath);\n    if (vfs->exists(fallback))\n        return fallback;\n\n    if (changedToDds)\n    {\n        fallback = topLevelDirectory + \"\\\\\" + getBasename(origExt);\n        if (vfs->exists(fallback))\n            return fallback;\n    }\n\n    return correctedPath;\n}\n\nstd::string Misc::ResourceHelpers::correctTexturePath(const std::string &resPath, const VFS::Manager* vfs)\n{\n    static const std::string dir = \"textures\";\n    return correctResourcePath(dir, resPath, vfs);\n}\n\nstd::string Misc::ResourceHelpers::correctIconPath(const std::string &resPath, const VFS::Manager* vfs)\n{\n    static const std::string dir = \"icons\";\n    return correctResourcePath(dir, resPath, vfs);\n}\n\nstd::string Misc::ResourceHelpers::correctBookartPath(const std::string &resPath, const VFS::Manager* vfs)\n{\n    static const std::string dir = \"bookart\";\n    std::string image = correctResourcePath(dir, resPath, vfs);\n\n    return image;\n}\n\nstd::string Misc::ResourceHelpers::correctBookartPath(const std::string &resPath, int width, int height, const VFS::Manager* vfs)\n{\n    std::string image = correctBookartPath(resPath, vfs);\n\n    // Apparently a bug with some morrowind versions, they reference the image without the size suffix.\n    // So if the image isn't found, try appending the size.\n    if (!vfs->exists(image))\n    {\n        std::stringstream str;\n        str << image.substr(0, image.rfind('.')) << \"_\" << width << \"_\" << height << image.substr(image.rfind('.'));\n        image = Misc::ResourceHelpers::correctBookartPath(str.str(), vfs);\n    }\n\n    return image;\n}\n\nstd::string Misc::ResourceHelpers::correctActorModelPath(const std::string &resPath, const VFS::Manager* vfs)\n{\n    std::string mdlname = resPath;\n    std::string::size_type p = mdlname.find_last_of(\"/\\\\\");\n    if(p != std::string::npos)\n        mdlname.insert(mdlname.begin()+p+1, 'x');\n    else\n        mdlname.insert(mdlname.begin(), 'x');\n    if(!vfs->exists(mdlname))\n    {\n        return resPath;\n    }\n    return mdlname;\n}\n"
  },
  {
    "path": "components/misc/resourcehelpers.hpp",
    "content": "#ifndef MISC_RESOURCEHELPERS_H\n#define MISC_RESOURCEHELPERS_H\n\n#include <string>\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace Misc\n{\n    // Workarounds for messy resource handling in vanilla morrowind\n    // The below functions are provided on a opt-in basis, instead of built into the VFS,\n    // so we have the opportunity to use proper resource handling for content created in OpenMW-CS.\n    namespace ResourceHelpers\n    {\n        bool changeExtensionToDds(std::string &path);\n        std::string correctResourcePath(const std::string &topLevelDirectory, const std::string &resPath, const VFS::Manager* vfs);\n        std::string correctTexturePath(const std::string &resPath, const VFS::Manager* vfs);\n        std::string correctIconPath(const std::string &resPath, const VFS::Manager* vfs);\n        std::string correctBookartPath(const std::string &resPath, const VFS::Manager* vfs);\n        std::string correctBookartPath(const std::string &resPath, int width, int height, const VFS::Manager* vfs);\n        /// Use \"xfoo.nif\" instead of \"foo.nif\" if available\n        std::string correctActorModelPath(const std::string &resPath, const VFS::Manager* vfs);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/rng.cpp",
    "content": "#include \"rng.hpp\"\n\n#include <chrono>\n#include <random>\n\nnamespace\n{\n    Misc::Rng::Seed sSeed;\n}\n\nnamespace Misc\n{\n\n    Rng::Seed::Seed() {}\n\n    Rng::Seed::Seed(unsigned int seed)\n    {\n        mGenerator.seed(seed);\n    }\n\n    Rng::Seed& Rng::getSeed()\n    {\n        return sSeed;\n    }\n\n    void Rng::init(unsigned int seed)\n    {\n        sSeed.mGenerator.seed(seed);\n    }\n\n    float Rng::rollProbability(Seed& seed)\n    {\n        return std::uniform_real_distribution<float>(0, 1 - std::numeric_limits<float>::epsilon())(seed.mGenerator);\n    }\n\n    float Rng::rollClosedProbability(Seed& seed)\n    {\n        return std::uniform_real_distribution<float>(0, 1)(seed.mGenerator);\n    }\n\n    int Rng::rollDice(int max, Seed& seed)\n    {\n        return max > 0 ? std::uniform_int_distribution<int>(0, max - 1)(seed.mGenerator) : 0;\n    }\n\n    unsigned int Rng::generateDefaultSeed()\n    {\n        return static_cast<unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count());\n    }\n\n    float Rng::deviate(float mean, float deviation, Seed& seed)\n    {\n        return std::uniform_real_distribution<float>(mean - deviation, mean + deviation)(seed.mGenerator);\n    }\n}\n"
  },
  {
    "path": "components/misc/rng.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_RNG_H\n#define OPENMW_COMPONENTS_MISC_RNG_H\n\n#include <cassert>\n#include <random>\n\nnamespace Misc\n{\n\n/*\n  Provides central implementation of the RNG logic\n*/\nclass Rng\n{\npublic:\n    class Seed\n    {\n        std::mt19937 mGenerator;\n    public:\n        Seed();\n        Seed(const Seed&) = delete;\n        Seed(unsigned int seed);\n        friend class Rng;\n    };\n\n    static Seed& getSeed();\n\n    /// seed the RNG\n    static void init(unsigned int seed = generateDefaultSeed());\n\n    /// return value in range [0.0f, 1.0f)  <- note open upper range.\n    static float rollProbability(Seed& seed = getSeed());\n  \n    /// return value in range [0.0f, 1.0f]  <- note closed upper range.\n    static float rollClosedProbability(Seed& seed = getSeed());\n\n    /// return value in range [0, max)  <- note open upper range.\n    static int rollDice(int max, Seed& seed = getSeed());\n\n    /// return value in range [0, 99]\n    static int roll0to99(Seed& seed = getSeed()) { return rollDice(100, seed); }\n\n    /// returns default seed for RNG\n    static unsigned int generateDefaultSeed();\n\n    static float deviate(float mean, float deviation, Seed& seed = getSeed());\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/stringops.hpp",
    "content": "#ifndef MISC_STRINGOPS_H\n#define MISC_STRINGOPS_H\n\n#include <cctype>\n#include <string>\n#include <algorithm>\n\n#include \"utf8stream.hpp\"\n\nnamespace Misc\n{\nclass StringUtils\n{\n    struct ci\n    {\n        bool operator()(char x, char y) const {\n            return toLower(x) < toLower(y);\n        }\n    };\n\n    // Allow to convert complex arguments to C-style strings for format() function\n    template <typename T>\n    static T argument(T value) noexcept\n    {\n        return value;\n    }\n\n    template <typename T>\n    static T const * argument(std::basic_string<T> const & value) noexcept\n    {\n        return value.c_str();\n    }\n\npublic:\n\n    /// Plain and simple locale-unaware toLower. Anything from A to Z is lower-cased, multibyte characters are unchanged.\n    /// Don't use std::tolower(char, locale&) because that is abysmally slow.\n    /// Don't use tolower(int) because that depends on global locale.\n    static char toLower(char c)\n    {\n        return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;\n    }\n\n    static Utf8Stream::UnicodeChar toLowerUtf8(Utf8Stream::UnicodeChar ch)\n    {\n        // Russian alphabet\n        if (ch >= 0x0410 && ch < 0x0430)\n            return ch + 0x20;\n\n        // Cyrillic IO character\n        if (ch == 0x0401)\n            return ch + 0x50;\n\n        // Latin alphabet\n        if (ch >= 0x41 && ch < 0x60)\n            return ch + 0x20;\n\n        // Deutch characters\n        if (ch == 0xc4 || ch == 0xd6 || ch == 0xdc)\n            return ch + 0x20;\n        if (ch == 0x1e9e)\n            return 0xdf;\n\n        // TODO: probably we will need to support characters from other languages\n\n        return ch;\n    }\n\n    static std::string lowerCaseUtf8(const std::string str)\n    {\n        if (str.empty())\n            return str;\n\n        // Decode string as utf8 characters, convert to lower case and pack them to string\n        std::string out;\n        Utf8Stream stream (str.c_str());\n        while (!stream.eof ())\n        {\n            Utf8Stream::UnicodeChar character = toLowerUtf8(stream.peek());\n\n            if (character <= 0x7f)\n                out.append(1, static_cast<char>(character));\n            else if (character <= 0x7ff)\n            {\n                out.append(1, static_cast<char>(0xc0 | ((character >> 6) & 0x1f)));\n                out.append(1, static_cast<char>(0x80 | (character & 0x3f)));\n            }\n            else if (character <= 0xffff)\n            {\n                out.append(1, static_cast<char>(0xe0 | ((character >> 12) & 0x0f)));\n                out.append(1, static_cast<char>(0x80 | ((character >> 6) & 0x3f)));\n                out.append(1, static_cast<char>(0x80 | (character & 0x3f)));\n            }\n            else\n            {\n                out.append(1, static_cast<char>(0xf0 | ((character >> 18) & 0x07)));\n                out.append(1, static_cast<char>(0x80 | ((character >> 12) & 0x3f)));\n                out.append(1, static_cast<char>(0x80 | ((character >> 6) & 0x3f)));\n                out.append(1, static_cast<char>(0x80 | (character & 0x3f)));\n            }\n\n            stream.consume();\n        }\n\n        return out;\n    }\n\n    static bool ciLess(const std::string &x, const std::string &y) {\n        return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci());\n    }\n\n    static bool ciEqual(const std::string &x, const std::string &y) {\n        if (x.size() != y.size()) {\n            return false;\n        }\n        std::string::const_iterator xit = x.begin();\n        std::string::const_iterator yit = y.begin();\n        for (; xit != x.end(); ++xit, ++yit) {\n            if (toLower(*xit) != toLower(*yit)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    static int ciCompareLen(const std::string &x, const std::string &y, size_t len)\n    {\n        std::string::const_iterator xit = x.begin();\n        std::string::const_iterator yit = y.begin();\n        for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len)\n        {\n            char left = *xit;\n            char right = *yit;\n            if (left == right)\n                continue;\n\n            left = toLower(left);\n            right = toLower(right);\n            int res = left - right;\n            if(res != 0)\n                return (res > 0) ? 1 : -1;\n        }\n        if(len > 0)\n        {\n            if(xit != x.end())\n                return 1;\n            if(yit != y.end())\n                return -1;\n        }\n        return 0;\n    }\n\n    /// Transforms input string to lower case w/o copy\n    static void lowerCaseInPlace(std::string &inout) {\n        for (unsigned int i=0; i<inout.size(); ++i)\n            inout[i] = toLower(inout[i]);\n    }\n\n    /// Returns lower case copy of input string\n    static std::string lowerCase(const std::string &in)\n    {\n        std::string out = in;\n        lowerCaseInPlace(out);\n        return out;\n    }\n\n    struct CiComp\n    {\n        bool operator()(const std::string& left, const std::string& right) const\n        {\n            return ciLess(left, right);\n        }\n    };\n\n\n    /// Performs a binary search on a sorted container for a string that 'key' starts with\n    template<typename Iterator, typename T>\n    static Iterator partialBinarySearch(Iterator begin, Iterator end, const T& key)\n    {\n        const Iterator notFound = end;\n\n        while(begin < end)\n        {\n            const Iterator middle = begin + (std::distance(begin, end) / 2);\n\n            int comp = Misc::StringUtils::ciCompareLen((*middle), key, (*middle).size());\n\n            if(comp == 0)\n                return middle;\n            else if(comp > 0)\n                end = middle;\n            else\n                begin = middle + 1;\n        }\n\n        return notFound;\n    }\n\n    /** @brief Replaces all occurrences of a string in another string.\n     *\n     * @param str The string to operate on.\n     * @param what The string to replace.\n     * @param with The replacement string.\n     * @param whatLen The length of the string to replace.\n     * @param withLen The length of the replacement string.\n     *\n     * @return A reference to the string passed in @p str.\n     */\n    static std::string &replaceAll(std::string &str, const char *what, const char *with,\n                                   std::size_t whatLen=std::string::npos, std::size_t withLen=std::string::npos)\n    {\n        if (whatLen == std::string::npos)\n            whatLen = strlen(what);\n\n        if (withLen == std::string::npos)\n            withLen = strlen(with);\n\n        std::size_t found;\n        std::size_t offset = 0;\n        while((found = str.find(what, offset, whatLen)) != std::string::npos)\n        {\n              str.replace(found, whatLen, with, withLen);\n              offset = found + withLen;\n        }\n        return str;\n    }\n\n    // Requires some C++11 features:\n    // 1. std::string needs to be contiguous\n    // 2. std::snprintf with zero size (second argument) returns an output string size\n    // 3. variadic templates support\n    template <typename ... Args>\n    static std::string format(const char* fmt, Args const & ... args)\n    {\n        auto size = std::snprintf(nullptr, 0, fmt, argument(args) ...);\n        // Note: sprintf also writes a trailing null character. We should remove it.\n        std::string ret(size+1, '\\0');\n        std::sprintf(&ret[0], fmt, argument(args) ...);\n        ret.erase(size);\n\n        return ret;\n    }\n\n    template <typename ... Args>\n    static std::string format(const std::string& fmt, Args const & ... args)\n    {\n        return format(fmt.c_str(), args ...);\n    }\n\n    static inline void trim(std::string &s)\n    {\n        // left trim\n        s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch)\n        {\n            return !std::isspace(ch);\n        }));\n\n        // right trim\n        s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch)\n        {\n            return !std::isspace(ch);\n        }).base(), s.end());\n    }\n\n    template <class Container>\n    static inline void split(const std::string& str, Container& cont, const std::string& delims = \" \")\n    {\n        std::size_t current, previous = 0;\n        current = str.find_first_of(delims);\n        while (current != std::string::npos)\n        {\n            cont.push_back(str.substr(previous, current - previous));\n            previous = current + 1;\n            current = str.find_first_of(delims, previous);\n        }\n        cont.push_back(str.substr(previous, current - previous));\n    }\n\n    // TODO: use the std::string_view once we will use the C++17.\n    // It should allow us to avoid data copying while we still will support both string and literal arguments.\n\n    static inline void replaceAll(std::string& data, std::string toSearch, std::string replaceStr)\n    {\n        size_t pos = data.find(toSearch);\n\n        while( pos != std::string::npos)\n        {\n            data.replace(pos, toSearch.size(), replaceStr);\n            pos = data.find(toSearch, pos + replaceStr.size());\n        }\n    }\n\n     static inline void replaceLast(std::string& str, std::string substr, std::string with)\n     {\n         size_t pos = str.rfind(substr);\n         if (pos == std::string::npos)\n             return;\n\n         str.replace(pos, substr.size(), with);\n     }\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/thread.cpp",
    "content": "#include \"thread.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include <cstring>\n#include <thread>\n\n#ifdef __linux__\n\n#include <pthread.h>\n#include <sched.h>\n\nnamespace Misc\n{\n    void setCurrentThreadIdlePriority()\n    {\n        sched_param param;\n        param.sched_priority = 0;\n        if (pthread_setschedparam(pthread_self(), SCHED_IDLE, &param) == 0)\n            Log(Debug::Verbose) << \"Using idle priority for thread=\" << std::this_thread::get_id();\n        else\n            Log(Debug::Warning) << \"Failed to set idle priority for thread=\" << std::this_thread::get_id() << \": \" << std::strerror(errno);\n    }\n}\n\n#elif defined(WIN32)\n\n#undef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n\n#include <windows.h>\n\nnamespace Misc\n{\n    void setCurrentThreadIdlePriority()\n    {\n        if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST))\n            Log(Debug::Verbose) << \"Using idle priority for thread=\" << std::this_thread::get_id();\n        else\n            Log(Debug::Warning) << \"Failed to set idle priority for thread=\" << std::this_thread::get_id() << \": \" << GetLastError();\n    }\n}\n\n#elif defined(__FreeBSD__)\n\n#include <sys/types.h>\n#include <sys/rtprio.h>\n\nnamespace Misc\n{\n    void setCurrentThreadIdlePriority()\n    {\n        struct rtprio prio;\n        prio.type = RTP_PRIO_IDLE;\n        prio.prio = RTP_PRIO_MAX;\n        if (rtprio_thread(RTP_SET, 0, &prio) == 0)\n            Log(Debug::Verbose) << \"Using idle priority for thread=\" << std::this_thread::get_id();\n        else\n            Log(Debug::Warning) << \"Failed to set idle priority for thread=\" << std::this_thread::get_id() << \": \" << std::strerror(errno);\n    }\n}\n\n#else\n\nnamespace Misc\n{\n    void setCurrentThreadIdlePriority()\n    {\n        Log(Debug::Warning) << \"Idle thread priority is not supported on this system\";\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/thread.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_THREAD_H\n#define OPENMW_COMPONENTS_MISC_THREAD_H\n\n#include <thread>\n\nnamespace Misc\n{\n    void setCurrentThreadIdlePriority();\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/timer.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MISC_TIMER_H\n#define OPENMW_COMPONENTS_MISC_TIMER_H\n\n#include \"rng.hpp\"\n\nnamespace Misc\n{\n    enum class TimerStatus\n    {\n        Waiting,\n        Elapsed,\n    };\n\n    class DeviatingPeriodicTimer\n    {\n        public:\n            explicit DeviatingPeriodicTimer(float period, float deviation, float timeLeft)\n                : mPeriod(period), mDeviation(deviation), mTimeLeft(timeLeft)\n            {}\n\n            TimerStatus update(float duration)\n            {\n                if (mTimeLeft > 0)\n                {\n                    mTimeLeft -= duration;\n                    return TimerStatus::Waiting;\n                }\n\n                mTimeLeft = Rng::deviate(mPeriod, mDeviation);\n                return TimerStatus::Elapsed;\n            }\n\n            void reset(float timeLeft) { mTimeLeft = timeLeft; }\n\n        private:\n            const float mPeriod;\n            const float mDeviation;\n            float mTimeLeft;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/misc/utf8stream.hpp",
    "content": "#ifndef MISC_UTF8ITER_HPP\n#define MISC_UTF8ITER_HPP\n\n#include <cstdint>\n#include <cstring>\n#include <tuple>\n\nclass Utf8Stream\n{\npublic:\n\n    typedef uint32_t UnicodeChar;\n    typedef unsigned char const * Point;\n\n    //static const unicode_char sBadChar = 0xFFFFFFFF; gcc can't handle this\n    static UnicodeChar sBadChar () { return UnicodeChar (0xFFFFFFFF); }\n\n    Utf8Stream (Point begin, Point end) :\n        cur (begin), nxt (begin), end (end), val(Utf8Stream::sBadChar())\n    {\n    }\n\n    Utf8Stream (const char * str) :\n        cur ((unsigned char*) str), nxt ((unsigned char*) str), end ((unsigned char*) str + strlen(str)), val(Utf8Stream::sBadChar())\n    {\n    }\n\n    Utf8Stream (std::pair <Point, Point> range) :\n        cur (range.first), nxt (range.first), end (range.second), val(Utf8Stream::sBadChar())\n    {\n    }\n\n    bool eof () const\n    {\n        return cur == end;\n    }\n\n    Point current () const\n    {\n        return cur;\n    }\n\n    UnicodeChar peek ()\n    {\n        if (cur == nxt)\n            next ();\n        return val;\n    }\n\n    UnicodeChar consume ()\n    {\n        if (cur == nxt)\n            next ();\n        cur = nxt;\n        return val;\n    }\n\n    static std::pair <UnicodeChar, Point> decode (Point cur, Point end)\n    {\n        if ((*cur & 0x80) == 0)\n        {\n            UnicodeChar chr = *cur++;\n\n            return std::make_pair (chr, cur);\n        }\n\n        int octets;\n        UnicodeChar chr;\n\n        std::tie (octets, chr) = octet_count (*cur++);\n\n        if (octets > 5)\n            return std::make_pair (sBadChar(), cur);\n\n        Point eoc = cur + octets;\n\n        if (eoc > end)\n            return std::make_pair (sBadChar(), cur);\n\n        while (cur != eoc)\n        {\n            if ((*cur & 0xC0) != 0x80) // check continuation mark\n                return std::make_pair (sBadChar(), cur);\n\n            chr = (chr << 6) | UnicodeChar ((*cur++) & 0x3F);\n        }\n\n        return std::make_pair (chr, cur);\n    }\n\nprivate:\n\n    static std::pair <int, UnicodeChar> octet_count (unsigned char octet)\n    {\n        int octets;\n\n        unsigned char mark = 0xC0;\n        unsigned char mask = 0xE0;\n\n        for (octets = 1; octets <= 5; ++octets)\n        {\n            if ((octet & mask) == mark)\n                break;\n\n            mark = (mark >> 1) | 0x80;\n            mask = (mask >> 1) | 0x80;\n        }\n\n        return std::make_pair (octets, octet & ~mask);\n    }\n\n    void next ()\n    {\n        std::tie (val, nxt) = decode (nxt, end);\n    }\n\n    Point cur;\n    Point nxt;\n    Point end;\n    UnicodeChar val;\n};\n\n#endif\n"
  },
  {
    "path": "components/misc/weakcache.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_WEAKCACHE_HPP\n#define OPENMW_COMPONENTS_WEAKCACHE_HPP\n\n#include <memory>\n#include <unordered_map>\n#include <vector>\n\nnamespace Misc\n{\n    /// \\class WeakCache\n    /// Provides a container to weakly store pointers to shared data.\n    template <typename Key, typename T>\n    class WeakCache\n    {\n    public:\n        using WeakPtr = std::weak_ptr<T>;\n        using StrongPtr = std::shared_ptr<T>;\n        using Map = std::unordered_map<Key, WeakPtr>;\n\n        class iterator\n        {\n        public:\n            iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end);\n            iterator& operator++();\n            bool operator==(const iterator& other);\n            bool operator!=(const iterator& other);\n            StrongPtr operator*();\n        private:\n            WeakCache* mCache;\n            typename Map::iterator mCurrent, mEnd;\n            StrongPtr mPtr;\n        };\n\n        /// Stores a weak pointer to the item.\n        void insert(Key key, StrongPtr value, bool prune=true);\n\n        /// Retrieves the item associated with the key.\n        /// \\return An item or null.\n        StrongPtr get(Key key);\n\n        iterator begin();\n        iterator end();\n\n        /// Removes known invalid entries\n        void prune();\n\n    private:\n        Map mData;\n        std::vector<Key> mDirty;\n    };\n\n\n    template <typename Key, typename T>\n    WeakCache<Key, T>::iterator::iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end)\n        : mCache(cache)\n        , mCurrent(current)\n        , mEnd(end)\n    {\n        // Move to 1st available valid item\n        for ( ; mCurrent != mEnd; ++mCurrent)\n        {\n            mPtr = mCurrent->second.lock();\n            if (mPtr) break;\n            else mCache->mDirty.push_back(mCurrent->first);\n        }\n    }\n\n    template <typename Key, typename T>\n    typename WeakCache<Key, T>::iterator& WeakCache<Key, T>::iterator::operator++()\n    {\n        auto next = mCurrent;\n        ++next;\n        return *this = iterator(mCache, next, mEnd);\n    }\n\n    template <typename Key, typename T>\n    bool WeakCache<Key, T>::iterator::operator==(const iterator& other)\n    {\n        return mCurrent == other.mCurrent;\n    }\n\n    template <typename Key, typename T>\n    bool WeakCache<Key, T>::iterator::operator!=(const iterator& other)\n    {\n        return !(*this == other);\n    }\n\n    template <typename Key, typename T>\n    typename WeakCache<Key, T>::StrongPtr WeakCache<Key, T>::iterator::operator*()\n    {\n        return mPtr;\n    }\n\n\n    template <typename Key, typename T>\n    void WeakCache<Key, T>::insert(Key key, StrongPtr value, bool shouldPrune)\n    {\n        mData[key] = WeakPtr(value);\n        if (shouldPrune) prune();\n    }\n\n    template <typename Key, typename T>\n    typename WeakCache<Key, T>::StrongPtr WeakCache<Key, T>::get(Key key)\n    {\n        auto searchIt = mData.find(key);\n        if (searchIt != mData.end())\n            return searchIt->second.lock();\n        else\n            return StrongPtr();\n    }\n\n    template <typename Key, typename T>\n    typename WeakCache<Key, T>::iterator WeakCache<Key, T>::begin()\n    {\n        return iterator(this, mData.begin(), mData.end());\n    }\n\n    template <typename Key, typename T>\n    typename WeakCache<Key, T>::iterator WeakCache<Key, T>::end()\n    {\n        return iterator(this, mData.end(), mData.end());\n    }\n\n    template <typename Key, typename T>\n    void WeakCache<Key, T>::prune()\n    {\n        // Remove empty entries\n        for (auto& key : mDirty)\n        {\n            auto it = mData.find(key);\n            if (it != mData.end() && it->second.use_count() == 0)\n                mData.erase(it);\n        }\n        mDirty.clear();\n    }\n}\n\n#endif\n"
  },
  {
    "path": "components/myguiplatform/additivelayer.cpp",
    "content": "#include \"additivelayer.hpp\"\n\n#include <osg/BlendFunc>\n#include <osg/StateSet>\n\n#include \"myguirendermanager.hpp\"\n\nnamespace osgMyGUI\n{\n\n    AdditiveLayer::AdditiveLayer()\n    {\n        mStateSet = new osg::StateSet;\n        mStateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE));\n    }\n\n    AdditiveLayer::~AdditiveLayer()\n    {\n        // defined in .cpp file since we can't delete incomplete types\n    }\n\n    void AdditiveLayer::renderToTarget(MyGUI::IRenderTarget *_target, bool _update)\n    {\n        RenderManager& renderManager = static_cast<RenderManager&>(MyGUI::RenderManager::getInstance());\n\n        renderManager.setInjectState(mStateSet.get());\n\n        MyGUI::OverlappedLayer::renderToTarget(_target, _update);\n\n        renderManager.setInjectState(nullptr);\n    }\n\n}\n"
  },
  {
    "path": "components/myguiplatform/additivelayer.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_ADDITIVELAYER\n#define OPENMW_COMPONENTS_MYGUIPLATFORM_ADDITIVELAYER\n\n#include <MyGUI_OverlappedLayer.h>\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class StateSet;\n}\n\nnamespace osgMyGUI\n{\n\n    /// @brief A Layer rendering with additive blend mode.\n    class AdditiveLayer final : public MyGUI::OverlappedLayer\n    {\n    public:\n        MYGUI_RTTI_DERIVED( AdditiveLayer )\n\n        AdditiveLayer();\n        ~AdditiveLayer() override;\n\n        void renderToTarget(MyGUI::IRenderTarget* _target, bool _update) override;\n\n    private:\n        osg::ref_ptr<osg::StateSet> mStateSet;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/myguiplatform/myguicompat.h",
    "content": "#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H\n#define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H\n\n#include <MyGUI_Prerequest.h>\n\n#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)\n    #define OPENMW_MYGUI_CONST_GETTER_3_4_1 const\n#else\n    #define OPENMW_MYGUI_CONST_GETTER_3_4_1\n#endif\n\n#endif  // OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H\n"
  },
  {
    "path": "components/myguiplatform/myguidatamanager.cpp",
    "content": "#include \"myguidatamanager.hpp\"\n\n#include <MyGUI_DataFileStream.h>\n\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <components/debug/debuglog.hpp>\n\nnamespace osgMyGUI\n{\n\nvoid DataManager::setResourcePath(const std::string &path)\n{\n    mResourcePath = path;\n}\n\nMyGUI::IDataStream *DataManager::getData(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1\n{\n    std::string fullpath = getDataPath(name);\n    std::unique_ptr<boost::filesystem::ifstream> stream;\n    stream.reset(new boost::filesystem::ifstream);\n    stream->open(fullpath, std::ios::binary);\n    if (stream->fail())\n    {\n        Log(Debug::Error) << \"DataManager::getData: Failed to open '\" << name << \"'\";\n        return nullptr;\n    }\n    return new MyGUI::DataFileStream(stream.release());\n}\n\nvoid DataManager::freeData(MyGUI::IDataStream *data)\n{\n    delete data;\n}\n\nbool DataManager::isDataExist(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1\n{\n    std::string fullpath = mResourcePath + \"/\" + name;\n    return boost::filesystem::exists(fullpath);\n}\n\nconst MyGUI::VectorString &DataManager::getDataListNames(const std::string &pattern) OPENMW_MYGUI_CONST_GETTER_3_4_1\n{\n    // TODO: pattern matching (unused?)\n    static MyGUI::VectorString strings;\n    strings.clear();\n    strings.push_back(getDataPath(pattern));\n    return strings;\n}\n\nconst std::string &DataManager::getDataPath(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1\n{\n    static std::string result;\n    result.clear();\n    if (!isDataExist(name))\n    {\n        return result;\n    }\n    result = mResourcePath + \"/\" + name;\n    return result;\n}\n\n}\n"
  },
  {
    "path": "components/myguiplatform/myguidatamanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIDATAMANAGER_H\n#define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIDATAMANAGER_H\n\n#include <MyGUI_DataManager.h>\n\n#include \"myguicompat.h\"\n\nnamespace osgMyGUI\n{\n\nclass DataManager : public MyGUI::DataManager\n{\npublic:\n    void initialise() {}\n    void shutdown() {}\n\n    void setResourcePath(const std::string& path);\n\n    /** Get data stream from specified resource name.\n        @param _name Resource name (usually file name).\n    */\n    MyGUI::IDataStream* getData(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override;\n\n    /** Free data stream.\n        @param _data Data stream.\n    */\n    void freeData(MyGUI::IDataStream* _data) override;\n\n    /** Is data with specified name exist.\n        @param _name Resource name.\n    */\n    bool isDataExist(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override;\n\n    /** Get all data names with names that matches pattern.\n        @param _pattern Pattern to match (for example \"*.layout\").\n    */\n    const MyGUI::VectorString& getDataListNames(const std::string& _pattern) OPENMW_MYGUI_CONST_GETTER_3_4_1 override;\n\n    /** Get full path to data.\n        @param _name Resource name.\n        @return Return full path to specified data.\n    */\n    const std::string& getDataPath(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override;\n\nprivate:\n    std::string mResourcePath;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/myguiplatform/myguiloglistener.cpp",
    "content": "#include \"myguiloglistener.hpp\"\r\n\r\n#include <iomanip>\r\n\r\n#include <components/debug/debuglog.hpp>\r\n\r\nnamespace osgMyGUI\r\n{\r\n    void CustomLogListener::open()\r\n    {\r\n        mStream.open(boost::filesystem::path(mFileName), std::ios_base::out);\r\n        if (!mStream.is_open())\r\n            Log(Debug::Error) << \"Unable to create MyGUI log with path \" << mFileName;\r\n    }\r\n\r\n    void CustomLogListener::close()\r\n    {\r\n        if (mStream.is_open())\r\n            mStream.close();\r\n    }\r\n\r\n    void CustomLogListener::flush()\r\n    {\r\n        if (mStream.is_open())\r\n            mStream.flush();\r\n    }\r\n\r\n    void CustomLogListener::log(const std::string& _section, MyGUI::LogLevel _level, const struct tm* _time, const std::string& _message, const char* _file, int _line)\r\n    {\r\n        if (mStream.is_open())\r\n        {\r\n            const char* separator = \"  |  \";\r\n            mStream << std::setw(2) << std::setfill('0') << _time->tm_hour << \":\"\r\n                << std::setw(2) << std::setfill('0') << _time->tm_min << \":\"\r\n                << std::setw(2) << std::setfill('0') << _time->tm_sec << separator\r\n                << _section << separator << _level.print() << separator\r\n                << _message << separator << _file << separator << _line << std::endl;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "components/myguiplatform/myguiloglistener.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_LOGLISTENER_H\r\n#define OPENMW_COMPONENTS_MYGUIPLATFORM_LOGLISTENER_H\r\n\r\n#include <string>\r\n#include <boost/filesystem/fstream.hpp>\r\n\r\n#include <MyGUI_ILogListener.h>\r\n#include <MyGUI_LogSource.h>\r\n#include <MyGUI_ConsoleLogListener.h>\r\n#include <MyGUI_LevelLogFilter.h>\r\n\r\nnamespace osgMyGUI\r\n{\r\n\r\n    /// \\brief  Custom MyGUI::ILogListener interface implementation\r\n    /// being able to portably handle UTF-8 encoded path.\r\n    /// \\todo try patching MyGUI to make this easier\r\n    class CustomLogListener : public MyGUI::ILogListener\r\n    {\r\n    public:\r\n        CustomLogListener(const std::string &name)\r\n            : mFileName(name)\r\n        {}\r\n\r\n        ~CustomLogListener() {}\r\n\r\n        void open() override;\r\n        void close() override;\r\n        void flush() override;\r\n\r\n        void log(const std::string& _section, MyGUI::LogLevel _level, const struct tm* _time, const std::string& _message, const char* _file, int _line) override;\r\n\r\n        const std::string& getFileName() const { return mFileName; }\r\n\r\n    private:\r\n        boost::filesystem::ofstream mStream;\r\n        std::string mFileName;\r\n    };\r\n\r\n    /// \\brief Helper class holding data that required during\r\n    /// MyGUI log creation\r\n    class LogFacility\r\n    {\r\n        MyGUI::ConsoleLogListener  mConsole;\r\n        CustomLogListener   mFile;\r\n        MyGUI::LevelLogFilter      mFilter;\r\n        MyGUI::LogSource           mSource;\r\n\r\n    public:\r\n\r\n        LogFacility(const std::string &output, bool console)\r\n          : mFile(output)\r\n        {\r\n            mConsole.setEnabled(console);\r\n            mFilter.setLoggingLevel(MyGUI::LogLevel::Info);\r\n\r\n            mSource.addLogListener(&mFile);\r\n            mSource.addLogListener(&mConsole);\r\n            mSource.setLogFilter(&mFilter);\r\n\r\n            mSource.open();\r\n        }\r\n\r\n        MyGUI::LogSource *getSource() { return &mSource; }\r\n    };\r\n\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "components/myguiplatform/myguiplatform.cpp",
    "content": "#include \"myguiplatform.hpp\"\n\n#include \"myguirendermanager.hpp\"\n#include \"myguidatamanager.hpp\"\n#include \"myguiloglistener.hpp\"\n\nnamespace osgMyGUI\n{\n\nPlatform::Platform(osgViewer::Viewer *viewer, osg::Group *guiRoot, Resource::ImageManager *imageManager, float uiScalingFactor)\n    : mRenderManager(nullptr)\n    , mDataManager(nullptr)\n    , mLogManager(nullptr)\n    , mLogFacility(nullptr)\n{\n    mLogManager = new MyGUI::LogManager();\n    mRenderManager = new RenderManager(viewer, guiRoot, imageManager, uiScalingFactor);\n    mDataManager = new DataManager();\n}\n\nPlatform::~Platform()\n{\n    delete mRenderManager;\n    mRenderManager = nullptr;\n    delete mDataManager;\n    mDataManager = nullptr;\n    delete mLogManager;\n    mLogManager = nullptr;\n    delete mLogFacility;\n    mLogFacility = nullptr;\n}\n\nvoid Platform::initialise(const std::string &resourcePath, const std::string &_logName)\n{\n    if (!_logName.empty() && !mLogFacility)\n    {\n        mLogFacility = new LogFacility(_logName, false);\n        mLogManager->addLogSource(mLogFacility->getSource());\n    }\n\n    mDataManager->setResourcePath(resourcePath);\n\n    mRenderManager->initialise();\n    mDataManager->initialise();\n}\n\nvoid Platform::shutdown()\n{\n    mRenderManager->shutdown();\n    mDataManager->shutdown();\n}\n\nRenderManager *Platform::getRenderManagerPtr()\n{\n    return mRenderManager;\n}\n\nDataManager *Platform::getDataManagerPtr()\n{\n    return mDataManager;\n}\n\n\n}\n"
  },
  {
    "path": "components/myguiplatform/myguiplatform.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIPLATFORM_H\n#define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIPLATFORM_H\n\n#include <string>\n\nnamespace osgViewer\n{\n    class Viewer;\n}\nnamespace osg\n{\n    class Group;\n}\nnamespace Resource\n{\n    class ImageManager;\n}\nnamespace MyGUI\n{\n    class LogManager;\n}\n\nnamespace osgMyGUI\n{\n\n    class RenderManager;\n    class DataManager;\n    class LogFacility;\n\n    class Platform\n    {\n    public:\n        Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ImageManager* imageManager, float uiScalingFactor);\n\n        ~Platform();\n\n        void initialise(const std::string& resourcePath, const std::string& _logName = \"MyGUI.log\");\n\n        void shutdown();\n\n        RenderManager* getRenderManagerPtr();\n\n        DataManager* getDataManagerPtr();\n\n    private:\n        RenderManager* mRenderManager;\n        DataManager* mDataManager;\n        MyGUI::LogManager* mLogManager;\n        LogFacility* mLogFacility;\n\n        void operator=(const Platform&);\n        Platform(const Platform&);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/myguiplatform/myguirendermanager.cpp",
    "content": "#include \"myguirendermanager.hpp\"\n\n#include <MyGUI_Gui.h>\n#include <MyGUI_Timer.h>\n\n#include <osg/Drawable>\n#include <osg/BlendFunc>\n#include <osg/Texture2D>\n#include <osg/TexMat>\n#include <osg/ValueObject>\n\n#include <osgViewer/Viewer>\n\n#include <osgGA/GUIEventHandler>\n\n#include <components/resource/imagemanager.hpp>\n\n#include <components/debug/debuglog.hpp>\n\n#include \"myguicompat.h\"\n#include \"myguitexture.hpp\"\n\n#define MYGUI_PLATFORM_LOG_SECTION \"Platform\"\n#define MYGUI_PLATFORM_LOG(level, text) MYGUI_LOGGING(MYGUI_PLATFORM_LOG_SECTION, level, text)\n\n#define MYGUI_PLATFORM_EXCEPT(dest) do { \\\n    MYGUI_PLATFORM_LOG(Critical, dest); \\\n    std::ostringstream stream; \\\n    stream << dest << \"\\n\"; \\\n    MYGUI_BASE_EXCEPT(stream.str().c_str(), \"MyGUI\"); \\\n} while(0)\n\n#define MYGUI_PLATFORM_ASSERT(exp, dest) do { \\\n    if ( ! (exp) ) \\\n    { \\\n        MYGUI_PLATFORM_LOG(Critical, dest); \\\n        std::ostringstream stream; \\\n        stream << dest << \"\\n\"; \\\n        MYGUI_BASE_EXCEPT(stream.str().c_str(), \"MyGUI\"); \\\n    } \\\n} while(0)\n\nnamespace osgMyGUI\n{\n\nclass Drawable : public osg::Drawable {\n    osgMyGUI::RenderManager *mParent;\n    osg::ref_ptr<osg::StateSet> mStateSet;\n\npublic:\n\n    // Stage 0: update widget animations and controllers. Run during the Update traversal.\n    class FrameUpdate : public osg::Drawable::UpdateCallback\n    {\n    public:\n        FrameUpdate()\n            : mRenderManager(nullptr)\n        {\n        }\n\n        void setRenderManager(osgMyGUI::RenderManager* renderManager)\n        {\n            mRenderManager = renderManager;\n        }\n\n        void update(osg::NodeVisitor*, osg::Drawable*) override\n        {\n            if (mRenderManager)\n                mRenderManager->update();\n        }\n\n    private:\n        osgMyGUI::RenderManager* mRenderManager;\n    };\n\n    // Stage 1: collect draw calls. Run during the Cull traversal.\n    class CollectDrawCalls : public osg::Drawable::CullCallback\n    {\n    public:\n        CollectDrawCalls()\n            : mRenderManager(nullptr)\n        {\n        }\n\n        void setRenderManager(osgMyGUI::RenderManager* renderManager)\n        {\n            mRenderManager = renderManager;\n        }\n\n        bool cull(osg::NodeVisitor*, osg::Drawable*, osg::State*) const override\n        {\n            if (!mRenderManager)\n                return false;\n\n            mRenderManager->collectDrawCalls();\n            return false;\n        }\n\n    private:\n        osgMyGUI::RenderManager* mRenderManager;\n    };\n\n    // Stage 2: execute the draw calls. Run during the Draw traversal. May run in parallel with the update traversal of the next frame.\n    void drawImplementation(osg::RenderInfo &renderInfo) const override\n    {\n        osg::State *state = renderInfo.getState();\n\n        state->pushStateSet(mStateSet);\n        state->apply();\n\n        state->disableAllVertexArrays();\n        state->setClientActiveTextureUnit(0);\n        glEnableClientState(GL_VERTEX_ARRAY);\n        glEnableClientState(GL_TEXTURE_COORD_ARRAY);\n        glEnableClientState(GL_COLOR_ARRAY);\n\n        mReadFrom = (mReadFrom+1)%sNumBuffers;\n        const std::vector<Batch>& vec = mBatchVector[mReadFrom];\n        for (std::vector<Batch>::const_iterator it = vec.begin(); it != vec.end(); ++it)\n        {\n            const Batch& batch = *it;\n            osg::VertexBufferObject *vbo = batch.mVertexBuffer;\n\n            if (batch.mStateSet)\n            {\n                state->pushStateSet(batch.mStateSet);\n                state->apply();\n            }\n\n            osg::Texture2D* texture = batch.mTexture;\n            if(texture)\n                state->applyTextureAttribute(0, texture);\n\n            osg::GLBufferObject* bufferobject = state->isVertexBufferObjectSupported() ? vbo->getOrCreateGLBufferObject(state->getContextID()) : nullptr;\n            if (bufferobject)\n            {\n                state->bindVertexBufferObject(bufferobject);\n\n                glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), reinterpret_cast<char*>(0));\n                glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), reinterpret_cast<char*>(12));\n                glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), reinterpret_cast<char*>(16));\n            }\n            else\n            {\n                glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)vbo->getArray(0)->getDataPointer());\n                glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)vbo->getArray(0)->getDataPointer() + 12);\n                glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)vbo->getArray(0)->getDataPointer() + 16);\n            }\n\n            glDrawArrays(GL_TRIANGLES, 0, batch.mVertexCount);\n\n            if (batch.mStateSet)\n            {\n                state->popStateSet();\n                state->apply();\n            }\n        }\n\n        glDisableClientState(GL_VERTEX_ARRAY);\n        glDisableClientState(GL_TEXTURE_COORD_ARRAY);\n        glDisableClientState(GL_COLOR_ARRAY);\n\n        state->popStateSet();\n\n        state->unbindVertexBufferObject();\n        state->dirtyAllVertexArrays();\n        state->disableAllVertexArrays();\n    }\n\npublic:\n    Drawable(osgMyGUI::RenderManager *parent = nullptr)\n        : mParent(parent)\n        , mWriteTo(0)\n        , mReadFrom(0)\n    {\n        setSupportsDisplayList(false);\n\n        osg::ref_ptr<CollectDrawCalls> collectDrawCalls = new CollectDrawCalls;\n        collectDrawCalls->setRenderManager(mParent);\n        setCullCallback(collectDrawCalls);\n\n        osg::ref_ptr<FrameUpdate> frameUpdate = new FrameUpdate;\n        frameUpdate->setRenderManager(mParent);\n        setUpdateCallback(frameUpdate);\n\n        mStateSet = new osg::StateSet;\n        mStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n        mStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);\n        mStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n        mStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);\n\n        // need to flip tex coords since MyGUI uses DirectX convention of top left image origin\n        osg::Matrix flipMat;\n        flipMat.preMultTranslate(osg::Vec3f(0,1,0));\n        flipMat.preMultScale(osg::Vec3f(1,-1,1));\n        mStateSet->setTextureAttribute(0, new osg::TexMat(flipMat), osg::StateAttribute::ON);\n    }\n    Drawable(const Drawable &copy, const osg::CopyOp &copyop=osg::CopyOp::SHALLOW_COPY)\n        : osg::Drawable(copy, copyop)\n        , mParent(copy.mParent)\n        , mStateSet(copy.mStateSet)\n        , mWriteTo(0)\n        , mReadFrom(0)\n    {\n    }\n\n    // Defines the necessary information for a draw call\n    struct Batch\n    {\n        // May be empty\n        osg::ref_ptr<osg::Texture2D> mTexture;\n\n        osg::ref_ptr<osg::VertexBufferObject> mVertexBuffer;\n        // need to hold on to this too as the mVertexBuffer does not hold a ref to its own array\n        osg::ref_ptr<osg::Array> mArray;\n\n        // optional\n        osg::ref_ptr<osg::StateSet> mStateSet;\n\n        size_t mVertexCount;\n    };\n\n    void addBatch(const Batch& batch)\n    {\n        mBatchVector[mWriteTo].push_back(batch);\n    }\n\n    void clear()\n    {\n        mWriteTo = (mWriteTo+1)%sNumBuffers;\n        mBatchVector[mWriteTo].clear();\n    }\n\n    META_Object(osgMyGUI, Drawable)\n\nprivate:\n    // 2 would be enough in most cases, use 4 to get stereo working\n    static const int sNumBuffers = 4;\n\n    // double buffering approach, to avoid the need for synchronization with the draw thread\n    std::vector<Batch> mBatchVector[sNumBuffers];\n\n    int mWriteTo;\n    mutable int mReadFrom;\n};\n\nclass OSGVertexBuffer : public MyGUI::IVertexBuffer\n{\n    osg::ref_ptr<osg::VertexBufferObject> mBuffer[2];\n    osg::ref_ptr<osg::UByteArray> mVertexArray[2];\n\n    size_t mNeedVertexCount;\n\n    unsigned int mCurrentBuffer;\n    bool mUsed; // has the mCurrentBuffer been submitted to the rendering thread\n\n    void destroy();\n    osg::UByteArray* create();\n\npublic:\n    OSGVertexBuffer();\n    virtual ~OSGVertexBuffer() {}\n\n    void markUsed();\n\n    osg::Array* getVertexArray();\n    osg::VertexBufferObject* getVertexBuffer();\n\n    void setVertexCount(size_t count) override;\n    size_t getVertexCount() OPENMW_MYGUI_CONST_GETTER_3_4_1 override;\n\n    MyGUI::Vertex *lock() override;\n    void unlock() override;\n\n};\n\nOSGVertexBuffer::OSGVertexBuffer()\n  : mNeedVertexCount(0)\n  , mCurrentBuffer(0)\n  , mUsed(false)\n{\n}\n\nvoid OSGVertexBuffer::markUsed()\n{\n    mUsed = true;\n}\n\nvoid OSGVertexBuffer::setVertexCount(size_t count)\n{\n    if(count == mNeedVertexCount)\n        return;\n\n    mNeedVertexCount = count;\n}\n\nsize_t OSGVertexBuffer::getVertexCount() OPENMW_MYGUI_CONST_GETTER_3_4_1\n{\n    return mNeedVertexCount;\n}\n\nMyGUI::Vertex *OSGVertexBuffer::lock()\n{\n    if (mUsed)\n    {\n        mCurrentBuffer = (mCurrentBuffer+1)%2;\n        mUsed = false;\n    }\n    osg::UByteArray* array = mVertexArray[mCurrentBuffer];\n    if (!array)\n    {\n        array = create();\n    }\n    else if (array->size() != mNeedVertexCount * sizeof(MyGUI::Vertex))\n    {\n        array->resize(mNeedVertexCount * sizeof(MyGUI::Vertex));\n    }\n\n    return (MyGUI::Vertex*)&(*array)[0];\n}\n\nvoid OSGVertexBuffer::unlock()\n{\n    mVertexArray[mCurrentBuffer]->dirty();\n    mBuffer[mCurrentBuffer]->dirty();\n}\n\nosg::UByteArray* OSGVertexBuffer::create()\n{\n    mVertexArray[mCurrentBuffer] = new osg::UByteArray(mNeedVertexCount*sizeof(MyGUI::Vertex));\n\n    mBuffer[mCurrentBuffer] = new osg::VertexBufferObject;\n    mBuffer[mCurrentBuffer]->setDataVariance(osg::Object::DYNAMIC);\n    mBuffer[mCurrentBuffer]->setUsage(GL_DYNAMIC_DRAW);\n    // NB mBuffer does not own the array\n    mBuffer[mCurrentBuffer]->setArray(0, mVertexArray[mCurrentBuffer].get());\n\n    return mVertexArray[mCurrentBuffer];\n}\n\nosg::Array* OSGVertexBuffer::getVertexArray()\n{\n    return mVertexArray[mCurrentBuffer];\n}\n\nosg::VertexBufferObject* OSGVertexBuffer::getVertexBuffer()\n{\n    return mBuffer[mCurrentBuffer];\n}\n\n// ---------------------------------------------------------------------------\n\nRenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* imageManager, float scalingFactor)\n  : mViewer(viewer)\n  , mSceneRoot(sceneroot)\n  , mImageManager(imageManager)\n  , mUpdate(false)\n  , mIsInitialise(false)\n  , mInvScalingFactor(1.f)\n  , mInjectState(nullptr)\n{\n    if (scalingFactor != 0.f)\n        mInvScalingFactor = 1.f / scalingFactor;\n}\n\nRenderManager::~RenderManager()\n{\n    MYGUI_PLATFORM_LOG(Info, \"* Shutdown: \"<<getClassTypeName());\n\n    if(mGuiRoot.valid())\n        mSceneRoot->removeChild(mGuiRoot.get());\n    mGuiRoot = nullptr;\n    mSceneRoot = nullptr;\n    mViewer = nullptr;\n\n    destroyAllResources();\n\n    MYGUI_PLATFORM_LOG(Info, getClassTypeName()<<\" successfully shutdown\");\n    mIsInitialise = false;\n}\n\n\nvoid RenderManager::initialise()\n{\n    MYGUI_PLATFORM_ASSERT(!mIsInitialise, getClassTypeName()<<\" initialised twice\");\n    MYGUI_PLATFORM_LOG(Info, \"* Initialise: \"<<getClassTypeName());\n\n    mVertexFormat = MyGUI::VertexColourType::ColourABGR;\n\n    mUpdate = false;\n\n    mDrawable = new Drawable(this);\n\n    osg::ref_ptr<osg::Camera> camera = new osg::Camera();\n    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);\n    camera->setProjectionResizePolicy(osg::Camera::FIXED);\n    camera->setProjectionMatrix(osg::Matrix::identity());\n    camera->setViewMatrix(osg::Matrix::identity());\n    camera->setRenderOrder(osg::Camera::POST_RENDER);\n    camera->setClearMask(GL_NONE);\n    mDrawable->setCullingActive(false);\n    camera->addChild(mDrawable.get());\n\n    mGuiRoot = camera;\n    mSceneRoot->addChild(mGuiRoot.get());\n\n    osg::ref_ptr<osg::Viewport> vp = mViewer->getCamera()->getViewport();\n    setViewSize(vp->width(), vp->height());\n\n    MYGUI_PLATFORM_LOG(Info, getClassTypeName()<<\" successfully initialized\");\n    mIsInitialise = true;\n}\n\nvoid RenderManager::shutdown()\n{\n    mGuiRoot->removeChildren(0, mGuiRoot->getNumChildren());\n    mSceneRoot->removeChild(mGuiRoot);\n}\n\nMyGUI::IVertexBuffer* RenderManager::createVertexBuffer()\n{\n    return new OSGVertexBuffer();\n}\n\nvoid RenderManager::destroyVertexBuffer(MyGUI::IVertexBuffer *buffer)\n{\n    delete buffer;\n}\n\n\nvoid RenderManager::begin()\n{\n    mDrawable->clear();\n    // variance will be recomputed based on textures being rendered in this frame\n    mDrawable->setDataVariance(osg::Object::STATIC);\n}\n\nvoid RenderManager::doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *texture, size_t count)\n{\n    Drawable::Batch batch;\n    batch.mVertexCount = count;\n    batch.mVertexBuffer = static_cast<OSGVertexBuffer*>(buffer)->getVertexBuffer();\n    batch.mArray = static_cast<OSGVertexBuffer*>(buffer)->getVertexArray();\n    static_cast<OSGVertexBuffer*>(buffer)->markUsed();\n    bool premultipliedAlpha = false;\n    if (texture)\n    {\n        batch.mTexture = static_cast<OSGTexture*>(texture)->getTexture();\n        if (batch.mTexture->getDataVariance() == osg::Object::DYNAMIC)\n            mDrawable->setDataVariance(osg::Object::DYNAMIC); // only for this frame, reset in begin()\n        batch.mTexture->getUserValue(\"premultiplied alpha\", premultipliedAlpha);\n    }\n    if (mInjectState)\n        batch.mStateSet = mInjectState;\n    else if (premultipliedAlpha)\n    {\n        // This is hacky, but MyGUI made it impossible to use a custom layer for a nested node, so state couldn't be injected 'properly'\n        osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet();\n        stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::ONE, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));\n        batch.mStateSet = stateSet;\n    }\n\n    mDrawable->addBatch(batch);\n}\n\nvoid RenderManager::setInjectState(osg::StateSet* stateSet)\n{\n    mInjectState = stateSet;\n}\n\nvoid RenderManager::end()\n{\n}\n\nvoid RenderManager::update()\n{\n    static MyGUI::Timer timer;\n    static unsigned long last_time = timer.getMilliseconds();\n    unsigned long now_time = timer.getMilliseconds();\n    unsigned long time = now_time - last_time;\n\n    onFrameEvent((float)((double)(time) / (double)1000));\n\n    last_time = now_time;\n}\n\nvoid RenderManager::collectDrawCalls()\n{\n    begin();\n    onRenderToTarget(this, mUpdate);\n    end();\n\n    mUpdate = false;\n}\n\nvoid RenderManager::setViewSize(int width, int height)\n{\n    if(width < 1) width = 1;\n    if(height < 1) height = 1;\n\n    mGuiRoot->setViewport(0, 0, width, height);\n\n    mViewSize.set(width * mInvScalingFactor, height * mInvScalingFactor);\n\n    mInfo.maximumDepth = 1;\n    mInfo.hOffset = 0;\n    mInfo.vOffset = 0;\n    mInfo.aspectCoef = float(mViewSize.height) / float(mViewSize.width);\n    mInfo.pixScaleX = 1.0f / float(mViewSize.width);\n    mInfo.pixScaleY = 1.0f / float(mViewSize.height);\n\n    onResizeView(mViewSize);\n    mUpdate = true;\n}\n\n\nbool RenderManager::isFormatSupported(MyGUI::PixelFormat /*format*/, MyGUI::TextureUsage /*usage*/)\n{\n    return true;\n}\n\nMyGUI::ITexture* RenderManager::createTexture(const std::string &name)\n{\n    MapTexture::iterator item = mTextures.find(name);\n    if (item != mTextures.end())\n    {\n        delete item->second;\n        mTextures.erase(item);\n    }\n\n    OSGTexture* texture = new OSGTexture(name, mImageManager);\n    mTextures.insert(std::make_pair(name, texture));\n    return texture;\n}\n\nvoid RenderManager::destroyTexture(MyGUI::ITexture *texture)\n{\n    if(texture == nullptr)\n        return;\n\n    MapTexture::iterator item = mTextures.find(texture->getName());\n    MYGUI_PLATFORM_ASSERT(item != mTextures.end(), \"Texture '\"<<texture->getName()<<\"' not found\");\n\n    mTextures.erase(item);\n    delete texture;\n}\n\nMyGUI::ITexture* RenderManager::getTexture(const std::string &name)\n{\n    if (name.empty())\n        return nullptr;\n\n    MapTexture::const_iterator item = mTextures.find(name);\n    if(item == mTextures.end())\n    {\n        MyGUI::ITexture* tex = createTexture(name);\n        tex->loadFromFile(name);\n        return tex;\n    }\n    return item->second;\n}\n\nvoid RenderManager::destroyAllResources()\n{\n    for (MapTexture::iterator it = mTextures.begin(); it != mTextures.end(); ++it)\n        delete it->second;\n    mTextures.clear();\n}\n\nbool RenderManager::checkTexture(MyGUI::ITexture* _texture)\n{\n    // We support external textures that aren't registered via this manager, so can't implement this method sensibly.\n    return true;\n}\n\n#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)\nvoid RenderManager::registerShader(\n    const std::string& _shaderName,\n    const std::string& _vertexProgramFile,\n    const std::string& _fragmentProgramFile)\n{\n    MYGUI_PLATFORM_LOG(Warning, \"osgMyGUI::RenderManager::registerShader is not implemented\");\n}\n#endif\n\n}\n"
  },
  {
    "path": "components/myguiplatform/myguirendermanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIRENDERMANAGER_H\n#define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIRENDERMANAGER_H\n\n#include <MyGUI_RenderManager.h>\n\n#include <osg/ref_ptr>\n\n#include \"myguicompat.h\"\n\nnamespace Resource\n{\n    class ImageManager;\n}\n\nnamespace osgViewer\n{\n    class Viewer;\n}\n\nnamespace osg\n{\n    class Group;\n    class Camera;\n    class RenderInfo;\n    class StateSet;\n}\n\nnamespace osgMyGUI\n{\n\nclass Drawable;\n\nclass RenderManager : public MyGUI::RenderManager, public MyGUI::IRenderTarget\n{\n    osg::ref_ptr<osgViewer::Viewer> mViewer;\n    osg::ref_ptr<osg::Group> mSceneRoot;\n    osg::ref_ptr<Drawable> mDrawable;\n    Resource::ImageManager* mImageManager;\n\n    MyGUI::IntSize mViewSize;\n    bool mUpdate;\n    MyGUI::VertexColourType mVertexFormat;\n    MyGUI::RenderTargetInfo mInfo;\n\n    typedef std::map<std::string, MyGUI::ITexture*> MapTexture;\n    MapTexture mTextures;\n\n    bool mIsInitialise;\n\n    osg::ref_ptr<osg::Camera> mGuiRoot;\n\n    float mInvScalingFactor;\n\n    osg::StateSet* mInjectState;\n\n    void destroyAllResources();\n\npublic:\n    RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* imageManager, float scalingFactor);\n    virtual ~RenderManager();\n\n    void initialise();\n    void shutdown();\n\n    void setScalingFactor(float factor);\n\n    static RenderManager& getInstance() { return *getInstancePtr(); }\n    static RenderManager* getInstancePtr()\n    { return static_cast<RenderManager*>(MyGUI::RenderManager::getInstancePtr()); }\n\n    /** @see RenderManager::getViewSize */\n    const MyGUI::IntSize& getViewSize() const override { return mViewSize; }\n\n    /** @see RenderManager::getVertexFormat */\n    MyGUI::VertexColourType getVertexFormat() OPENMW_MYGUI_CONST_GETTER_3_4_1 override\n    { return mVertexFormat; }\n\n    /** @see RenderManager::isFormatSupported */\n    bool isFormatSupported(MyGUI::PixelFormat format, MyGUI::TextureUsage usage) override;\n\n    /** @see RenderManager::createVertexBuffer */\n    MyGUI::IVertexBuffer* createVertexBuffer() override;\n    /** @see RenderManager::destroyVertexBuffer */\n    void destroyVertexBuffer(MyGUI::IVertexBuffer *buffer) override;\n\n    /** @see RenderManager::createTexture */\n    MyGUI::ITexture* createTexture(const std::string &name) override;\n    /** @see RenderManager::destroyTexture */\n    void destroyTexture(MyGUI::ITexture* _texture) override;\n    /** @see RenderManager::getTexture */\n    MyGUI::ITexture* getTexture(const std::string &name) override;\n\n    // Called by the update traversal\n    void update();\n\n    // Called by the cull traversal\n    /** @see IRenderTarget::begin */\n    void begin() override;\n    /** @see IRenderTarget::end */\n    void end() override;\n    /** @see IRenderTarget::doRender */\n    void doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *texture, size_t count) override;\n\n    /** specify a StateSet to inject for rendering. The StateSet will be used by future doRender calls until you reset it to nullptr again. */\n    void setInjectState(osg::StateSet* stateSet);\n\n    /** @see IRenderTarget::getInfo */\n    const MyGUI::RenderTargetInfo& getInfo() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mInfo; }\n\n    bool checkTexture(MyGUI::ITexture* _texture);\n\n    // setViewSize() is a part of MyGUI::RenderManager interface since 3.4.0 release\n#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 4, 0)\n    void setViewSize(int width, int height);\n#else\n    void setViewSize(int width, int height) override;\n#endif\n\n    // registerShader() is a part of MyGUI::RenderManager interface since 3.4.1 release\n#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)\n    void registerShader(const std::string& _shaderName, const std::string& _vertexProgramFile, const std::string& _fragmentProgramFile) override;\n#endif\n\n/*internal:*/\n\n    void collectDrawCalls();\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/myguiplatform/myguitexture.cpp",
    "content": "#include \"myguitexture.hpp\"\n\n#include <stdexcept>\n\n#include <osg/Texture2D>\n\n#include <components/debug/debuglog.hpp>\n#include <components/resource/imagemanager.hpp>\n\nnamespace osgMyGUI\n{\n\n    OSGTexture::OSGTexture(const std::string &name, Resource::ImageManager* imageManager)\n      : mName(name)\n      , mImageManager(imageManager)\n      , mFormat(MyGUI::PixelFormat::Unknow)\n      , mUsage(MyGUI::TextureUsage::Default)\n      , mNumElemBytes(0)\n      , mWidth(0)\n      , mHeight(0)\n    {\n    }\n\n    OSGTexture::OSGTexture(osg::Texture2D *texture)\n        : mImageManager(nullptr)\n        , mTexture(texture)\n        , mFormat(MyGUI::PixelFormat::Unknow)\n        , mUsage(MyGUI::TextureUsage::Default)\n        , mNumElemBytes(0)\n        , mWidth(texture->getTextureWidth())\n        , mHeight(texture->getTextureHeight())\n    {\n    }\n\n    OSGTexture::~OSGTexture()\n    {\n    }\n\n    void OSGTexture::createManual(int width, int height, MyGUI::TextureUsage usage, MyGUI::PixelFormat format)\n    {\n        GLenum glfmt = GL_NONE;\n        size_t numelems = 0;\n        switch(format.getValue())\n        {\n            case MyGUI::PixelFormat::L8:\n                glfmt = GL_LUMINANCE;\n                numelems = 1;\n                break;\n            case MyGUI::PixelFormat::L8A8:\n                glfmt = GL_LUMINANCE_ALPHA;\n                numelems = 2;\n                break;\n            case MyGUI::PixelFormat::R8G8B8:\n                glfmt = GL_RGB;\n                numelems = 3;\n                break;\n            case MyGUI::PixelFormat::R8G8B8A8:\n                glfmt = GL_RGBA;\n                numelems = 4;\n                break;\n        }\n        if(glfmt == GL_NONE)\n            throw std::runtime_error(\"Texture format not supported\");\n\n        mTexture = new osg::Texture2D();\n        mTexture->setTextureSize(width, height);\n        mTexture->setSourceFormat(glfmt);\n        mTexture->setSourceType(GL_UNSIGNED_BYTE);\n\n        mWidth = width;\n        mHeight = height;\n\n        mTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n        mTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n        mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n        mFormat = format;\n        mUsage = usage;\n        mNumElemBytes = numelems;\n    }\n\n    void OSGTexture::destroy()\n    {\n        mTexture = nullptr;\n        mFormat = MyGUI::PixelFormat::Unknow;\n        mUsage = MyGUI::TextureUsage::Default;\n        mNumElemBytes = 0;\n        mWidth = 0;\n        mHeight = 0;\n    }\n\n    void OSGTexture::loadFromFile(const std::string &fname)\n    {\n        if (!mImageManager)\n            throw std::runtime_error(\"No imagemanager set\");\n\n        osg::ref_ptr<osg::Image> image (mImageManager->getImage(fname));\n        mTexture = new osg::Texture2D(image);\n        mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n        mTexture->setTextureWidth(image->s());\n        mTexture->setTextureHeight(image->t());\n        // disable mip-maps\n        mTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);\n\n        mWidth = image->s();\n        mHeight = image->t();\n\n        mUsage = MyGUI::TextureUsage::Static;\n    }\n\n    void OSGTexture::saveToFile(const std::string &fname)\n    {\n        Log(Debug::Warning) << \"Would save image to file \" << fname;\n    }\n\n    void *OSGTexture::lock(MyGUI::TextureUsage /*access*/)\n    {\n        if (!mTexture.valid())\n            throw std::runtime_error(\"Texture is not created\");\n        if (mLockedImage.valid())\n            throw std::runtime_error(\"Texture already locked\");\n\n        mLockedImage = new osg::Image();\n        mLockedImage->allocateImage(\n            mTexture->getTextureWidth(), mTexture->getTextureHeight(), mTexture->getTextureDepth(),\n            mTexture->getSourceFormat(), mTexture->getSourceType()\n        );\n\n        return mLockedImage->data();\n    }\n\n    void OSGTexture::unlock()\n    {\n        if (!mLockedImage.valid())\n            throw std::runtime_error(\"Texture not locked\");\n\n        mLockedImage->flipVertical();\n\n        // mTexture might be in use by the draw thread, so create a new texture instead and use that.\n        osg::ref_ptr<osg::Texture2D> newTexture = new osg::Texture2D;\n        newTexture->setTextureSize(getWidth(), getHeight());\n        newTexture->setSourceFormat(mTexture->getSourceFormat());\n        newTexture->setSourceType(mTexture->getSourceType());\n        newTexture->setFilter(osg::Texture::MIN_FILTER, mTexture->getFilter(osg::Texture::MIN_FILTER));\n        newTexture->setFilter(osg::Texture::MAG_FILTER, mTexture->getFilter(osg::Texture::MAG_FILTER));\n        newTexture->setWrap(osg::Texture::WRAP_S, mTexture->getWrap(osg::Texture::WRAP_S));\n        newTexture->setWrap(osg::Texture::WRAP_T, mTexture->getWrap(osg::Texture::WRAP_T));\n        newTexture->setImage(mLockedImage.get());\n        // Tell the texture it can get rid of the image for static textures (since\n        // they aren't expected to update much at all).\n        newTexture->setUnRefImageDataAfterApply(mUsage.isValue(MyGUI::TextureUsage::Static) ? true : false);\n\n        mTexture = newTexture;\n\n        mLockedImage = nullptr;\n    }\n\n    // Render-to-texture not currently implemented.\n    MyGUI::IRenderTarget* OSGTexture::getRenderTarget()\n    {\n        return nullptr;\n    }\n\n#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)\n    void OSGTexture::setShader(const std::string& _shaderName)\n    { Log(Debug::Warning) << \"OSGTexture::setShader is not implemented\"; }\n#endif\n}\n"
  },
  {
    "path": "components/myguiplatform/myguitexture.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUITEXTURE_H\n#define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUITEXTURE_H\n\n#include <MyGUI_ITexture.h>\n\n#include <osg/ref_ptr>\n\n#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)\n    #define OPENMW_MYGUI_CONST_GETTER_3_4_1 const\n#else\n    #define OPENMW_MYGUI_CONST_GETTER_3_4_1\n#endif\n\nnamespace osg\n{\n    class Image;\n    class Texture2D;\n}\n\nnamespace Resource\n{\n    class ImageManager;\n}\n\nnamespace osgMyGUI\n{\n\n    class OSGTexture : public MyGUI::ITexture {\n        std::string mName;\n        Resource::ImageManager* mImageManager;\n\n        osg::ref_ptr<osg::Image> mLockedImage;\n        osg::ref_ptr<osg::Texture2D> mTexture;\n        MyGUI::PixelFormat mFormat;\n        MyGUI::TextureUsage mUsage;\n        size_t mNumElemBytes;\n\n        int mWidth;\n        int mHeight;\n\n    public:\n        OSGTexture(const std::string &name, Resource::ImageManager* imageManager);\n        OSGTexture(osg::Texture2D* texture);\n        virtual ~OSGTexture();\n\n        const std::string& getName() const override { return mName; }\n\n        void createManual(int width, int height, MyGUI::TextureUsage usage, MyGUI::PixelFormat format) override;\n        void loadFromFile(const std::string &fname) override;\n        void saveToFile(const std::string &fname) override;\n\n        void destroy() override;\n\n        void* lock(MyGUI::TextureUsage access) override;\n        void unlock() override;\n        bool isLocked() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mLockedImage.valid(); }\n\n        int getWidth() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mWidth; }\n        int getHeight() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mHeight; }\n\n        MyGUI::PixelFormat getFormat() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mFormat; }\n        MyGUI::TextureUsage getUsage() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mUsage; }\n        size_t getNumElemBytes() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mNumElemBytes; }\n\n        MyGUI::IRenderTarget *getRenderTarget() override;\n\n    // setShader() is a part of MyGUI::RenderManager interface since 3.4.1 release\n#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)\n    void setShader(const std::string& _shaderName) override;\n#endif\n\n    /*internal:*/\n        osg::Texture2D *getTexture() const { return mTexture.get(); }\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/myguiplatform/scalinglayer.cpp",
    "content": "#include \"scalinglayer.hpp\"\n\n#include <MyGUI_RenderManager.h>\n#include <algorithm>\n\n#include \"myguicompat.h\"\n\nnamespace osgMyGUI\n{\n\n    /// @brief the ProxyRenderTarget allows to adjust the pixel scale and offset for a \"source\" render target.\n    class ProxyRenderTarget : public MyGUI::IRenderTarget\n    {\n    public:\n        /// @param target The target to render to.\n        /// @param viewSize The size of the underlying layer node to render.\n        /// @param hoffset The horizontal rendering offset, specified as an offset from the left screen edge in range 0-1.\n        /// @param voffset The vertical rendering offset, specified as an offset from the top screen edge in range 0-1.\n        ProxyRenderTarget(MyGUI::IRenderTarget* target, MyGUI::IntSize viewSize, float hoffset, float voffset)\n            : mTarget(target)\n            , mViewSize(viewSize)\n            , mHOffset(hoffset)\n            , mVOffset(voffset)\n        {\n        }\n\n        void begin() override\n        {\n            mTarget->begin();\n        }\n\n        void end() override\n        {\n            mTarget->end();\n        }\n\n        void doRender(MyGUI::IVertexBuffer* _buffer, MyGUI::ITexture* _texture, size_t _count) override\n        {\n            mTarget->doRender(_buffer, _texture, _count);\n        }\n\n        const MyGUI::RenderTargetInfo& getInfo() OPENMW_MYGUI_CONST_GETTER_3_4_1 override\n        {\n            mInfo = mTarget->getInfo();\n            mInfo.hOffset = mHOffset;\n            mInfo.vOffset = mVOffset;\n            mInfo.pixScaleX = 1.f / mViewSize.width;\n            mInfo.pixScaleY = 1.f / mViewSize.height;\n            return mInfo;\n        }\n\n    private:\n        MyGUI::IRenderTarget* mTarget;\n        MyGUI::IntSize mViewSize;\n        float mHOffset, mVOffset;\n        mutable MyGUI::RenderTargetInfo mInfo;\n    };\n\n    MyGUI::ILayerItem *ScalingLayer::getLayerItemByPoint(int _left, int _top) const\n    {\n        screenToLayerCoords(_left, _top);\n\n        return OverlappedLayer::getLayerItemByPoint(_left, _top);\n    }\n\n    void ScalingLayer::screenToLayerCoords(int& _left, int& _top) const\n    {\n        float scale = getScaleFactor();\n        if (scale <= 0.f)\n            return;\n\n        MyGUI::IntSize globalViewSize = MyGUI::RenderManager::getInstance().getViewSize();\n\n        _left -= globalViewSize.width/2;\n        _top -= globalViewSize.height/2;\n\n        _left = static_cast<int>(_left/scale);\n        _top = static_cast<int>(_top/scale);\n\n        _left += mViewSize.width/2;\n        _top += mViewSize.height/2;\n    }\n\n    float ScalingLayer::getScaleFactor() const\n    {\n        MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        float w = static_cast<float>(viewSize.width);\n        float h = static_cast<float>(viewSize.height);\n\n        float heightScale = (h / mViewSize.height);\n        float widthScale = (w / mViewSize.width);\n        return std::min(widthScale, heightScale);\n    }\n\n    MyGUI::IntPoint ScalingLayer::getPosition(int _left, int _top) const\n    {\n        screenToLayerCoords(_left, _top);\n        return MyGUI::IntPoint(_left, _top);\n    }\n\n    void ScalingLayer::renderToTarget(MyGUI::IRenderTarget *_target, bool _update)\n    {\n        MyGUI::IntSize globalViewSize = MyGUI::RenderManager::getInstance().getViewSize();\n        MyGUI::IntSize viewSize = globalViewSize;\n        float scale = getScaleFactor();\n        viewSize.width = static_cast<int>(viewSize.width / scale);\n        viewSize.height = static_cast<int>(viewSize.height / scale);\n\n        float hoffset = (globalViewSize.width - mViewSize.width*getScaleFactor())/2.f / static_cast<float>(globalViewSize.width);\n        float voffset = (globalViewSize.height - mViewSize.height*getScaleFactor())/2.f / static_cast<float>(globalViewSize.height);\n\n        ProxyRenderTarget proxy(_target, viewSize, hoffset, voffset);\n\n        MyGUI::OverlappedLayer::renderToTarget(&proxy, _update);\n    }\n\n    void ScalingLayer::resizeView(const MyGUI::IntSize &_viewSize)\n    {\n        // do nothing\n    }\n\n    void ScalingLayer::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version)\n    {\n        MyGUI::OverlappedLayer::deserialization(_node, _version);\n\n        MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator();\n        while (info.next())\n        {\n            if (info->getName() == \"Property\")\n            {\n                const std::string& key = info->findAttribute(\"key\");\n                const std::string& value = info->findAttribute(\"value\");\n\n                if (key == \"Size\")\n                {\n                    mViewSize = MyGUI::IntSize::parse(value);\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "components/myguiplatform/scalinglayer.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_SCALINGLAYER\n#define OPENMW_COMPONENTS_MYGUIPLATFORM_SCALINGLAYER\n\n#include <MyGUI_OverlappedLayer.h>\n\nnamespace osgMyGUI\n{\n\n    ///@brief A Layer that lays out and renders widgets in screen-relative coordinates. The \"Size\" property determines the size of the virtual screen,\n    /// which is then upscaled to the real screen size during rendering. The aspect ratio is kept intact, adding blanks to the sides when necessary.\n    class ScalingLayer final : public MyGUI::OverlappedLayer\n    {\n    public:\n        MYGUI_RTTI_DERIVED(ScalingLayer)\n\n        void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) override;\n\n        MyGUI::ILayerItem* getLayerItemByPoint(int _left, int _top) const override;\n        MyGUI::IntPoint getPosition(int _left, int _top) const override;\n        void renderToTarget(MyGUI::IRenderTarget* _target, bool _update) override;\n\n        void resizeView(const MyGUI::IntSize& _viewSize) override;\n\n    private:\n        void screenToLayerCoords(int& _left, int& _top) const;\n        float getScaleFactor() const;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/nif/base.hpp",
    "content": "///This file holds the main classes of NIF Records used by everything else.\n#ifndef OPENMW_COMPONENTS_NIF_BASE_HPP\n#define OPENMW_COMPONENTS_NIF_BASE_HPP\n\n#include \"record.hpp\"\n#include \"niffile.hpp\"\n#include \"recordptr.hpp\"\n#include \"nifstream.hpp\"\n#include \"nifkey.hpp\"\n\nnamespace Nif\n{\n// An extra data record. All the extra data connected to an object form a linked list.\nstruct Extra : public Record\n{\n    std::string name;\n    ExtraPtr next; // Next extra data record in the list\n\n    void read(NIFStream *nif) override\n    {\n        if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))\n            name = nif->getString();\n        else if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))\n        {\n            next.read(nif);\n            nif->getUInt(); // Size of the record\n        }\n    }\n\n    void post(NIFFile *nif) override { next.post(nif); }\n};\n\nstruct Controller : public Record\n{\n    ControllerPtr next;\n    int flags;\n    float frequency, phase;\n    float timeStart, timeStop;\n    NamedPtr target;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\n/// Has name, extra-data and controller\nstruct Named : public Record\n{\n    std::string name;\n    ExtraPtr extra;\n    ExtraList extralist;\n    ControllerPtr controller;\n\n    void read(NIFStream *nif) override\n    {\n        name = nif->getString();\n        if (nif->getVersion() < NIFStream::generateVersion(10,0,1,0))\n            extra.read(nif);\n        else\n            extralist.read(nif);\n        controller.read(nif);\n    }\n\n    void post(NIFFile *nif) override\n    {\n        extra.post(nif);\n        extralist.post(nif);\n        controller.post(nif);\n    }\n};\nusing NiSequenceStreamHelper = Named;\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/controlled.cpp",
    "content": "#include \"controlled.hpp\"\n\n#include \"data.hpp\"\n\nnamespace Nif\n{\n\n    void NiSourceTexture::read(NIFStream *nif)\n    {\n        Named::read(nif);\n\n        external = nif->getChar() != 0;\n        bool internal = false;\n        if (external)\n            filename = nif->getString();\n        else\n        {\n            if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,3))\n                internal = nif->getChar();\n            if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n                filename = nif->getString(); // Original file path of the internal texture\n        }\n        if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,3))\n        {\n            if (!external && internal)\n                data.read(nif);\n        }\n        else\n        {\n            data.read(nif);\n        }\n\n        pixel = nif->getUInt();\n        mipmap = nif->getUInt();\n        alpha = nif->getUInt();\n\n        nif->getChar(); // always 1\n        if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,103))\n            nif->getBoolean(); // Direct rendering\n        if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,4))\n            nif->getBoolean(); // NiPersistentSrcTextureRendererData is used instead of NiPixelData\n    }\n\n    void NiSourceTexture::post(NIFFile *nif)\n    {\n        Named::post(nif);\n        data.post(nif);\n    }\n\n    void BSShaderTextureSet::read(NIFStream *nif)\n    {\n        nif->getSizedStrings(textures, nif->getUInt());\n    }\n\n    void NiParticleModifier::read(NIFStream *nif)\n    {\n        next.read(nif);\n        controller.read(nif);\n    }\n\n    void NiParticleModifier::post(NIFFile *nif)\n    {\n        next.post(nif);\n        controller.post(nif);\n    }\n\n    void NiParticleGrowFade::read(NIFStream *nif)\n    {\n        NiParticleModifier::read(nif);\n        growTime = nif->getFloat();\n        fadeTime = nif->getFloat();\n    }\n\n    void NiParticleColorModifier::read(NIFStream *nif)\n    {\n        NiParticleModifier::read(nif);\n        data.read(nif);\n    }\n\n    void NiParticleColorModifier::post(NIFFile *nif)\n    {\n        NiParticleModifier::post(nif);\n        data.post(nif);\n    }\n\n    void NiGravity::read(NIFStream *nif)\n    {\n        NiParticleModifier::read(nif);\n\n        mDecay = nif->getFloat();\n        mForce = nif->getFloat();\n        mType = nif->getUInt();\n        mPosition = nif->getVector3();\n        mDirection = nif->getVector3();\n    }\n\n    void NiParticleCollider::read(NIFStream *nif)\n    {\n        NiParticleModifier::read(nif);\n\n        mBounceFactor = nif->getFloat();\n        if (nif->getVersion() >= NIFStream::generateVersion(4,2,2,0))\n        {\n            // Unused in NifSkope. Need to figure out what these do.\n            /*bool spawnOnCollision = */nif->getBoolean();\n            /*bool dieOnCollision = */nif->getBoolean();\n        }\n    }\n\n    void NiPlanarCollider::read(NIFStream *nif)\n    {\n        NiParticleCollider::read(nif);\n\n        /*unknown*/nif->getFloat();\n\n        for (int i=0;i<10;++i)\n            /*unknown*/nif->getFloat();\n\n        mPlaneNormal = nif->getVector3();\n        mPlaneDistance = nif->getFloat();\n    }\n\n    void NiParticleRotation::read(NIFStream *nif)\n    {\n        NiParticleModifier::read(nif);\n\n        /*\n           byte (0 or 1)\n           float (1)\n           float*3\n        */\n        nif->skip(17);\n    }\n\n    void NiSphericalCollider::read(NIFStream* nif)\n    {\n        NiParticleCollider::read(nif);\n\n        mRadius = nif->getFloat();\n        mCenter = nif->getVector3();\n    }\n\n}\n"
  },
  {
    "path": "components/nif/controlled.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (controlled.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef OPENMW_COMPONENTS_NIF_CONTROLLED_HPP\n#define OPENMW_COMPONENTS_NIF_CONTROLLED_HPP\n\n#include \"base.hpp\"\n\nnamespace Nif\n{\n\nstruct NiSourceTexture : public Named\n{\n    // Is this an external (references a separate texture file) or\n    // internal (data is inside the nif itself) texture?\n    bool external;\n\n    std::string filename; // In case of external textures\n    NiPixelDataPtr data;  // In case of internal textures\n\n    /* Pixel layout\n        0 - Palettised\n        1 - High color 16\n        2 - True color 32\n        3 - Compressed\n        4 - Bumpmap\n        5 - Default */\n    unsigned int pixel;\n\n    /* Mipmap format\n        0 - no\n        1 - yes\n        2 - default */\n    unsigned int mipmap;\n\n    /* Alpha\n        0 - none\n        1 - binary\n        2 - smooth\n        3 - default (use material alpha, or multiply material with texture if present)\n    */\n    unsigned int alpha;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct BSShaderTextureSet : public Record\n{\n    enum TextureType\n    {\n        TextureType_Base = 0,\n        TextureType_Normal = 1,\n        TextureType_Glow = 2,\n        TextureType_Parallax = 3,\n        TextureType_Env = 4,\n        TextureType_EnvMask = 5,\n        TextureType_Subsurface = 6,\n        TextureType_BackLighting = 7\n    };\n    std::vector<std::string> textures;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiParticleModifier : public Record\n{\n    NiParticleModifierPtr next;\n    ControllerPtr controller;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiParticleGrowFade : public NiParticleModifier\n{\n    float growTime;\n    float fadeTime;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiParticleColorModifier : public NiParticleModifier\n{\n    NiColorDataPtr data;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiGravity : public NiParticleModifier\n{\n    float mForce;\n    /* 0 - Wind (fixed direction)\n     * 1 - Point (fixed origin)\n     */\n    int mType;\n    float mDecay;\n    osg::Vec3f mPosition;\n    osg::Vec3f mDirection;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiParticleCollider : public NiParticleModifier\n{\n    float mBounceFactor;\n    void read(NIFStream *nif) override;\n};\n\n// NiPinaColada\nstruct NiPlanarCollider : public NiParticleCollider\n{\n    void read(NIFStream *nif) override;\n\n    osg::Vec3f mPlaneNormal;\n    float mPlaneDistance;\n};\n\nstruct NiSphericalCollider : public NiParticleCollider\n{\n    float mRadius;\n    osg::Vec3f mCenter;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiParticleRotation : public NiParticleModifier\n{\n    void read(NIFStream *nif) override;\n};\n\n\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/controller.cpp",
    "content": "#include \"controller.hpp\"\n\n#include \"node.hpp\"\n#include \"data.hpp\"\n\nnamespace Nif\n{\n\n    void Controller::read(NIFStream *nif)\n    {\n        next.read(nif);\n\n        flags = nif->getUShort();\n\n        frequency = nif->getFloat();\n        phase = nif->getFloat();\n        timeStart = nif->getFloat();\n        timeStop = nif->getFloat();\n\n        target.read(nif);\n    }\n\n    void Controller::post(NIFFile *nif)\n    {\n        Record::post(nif);\n        next.post(nif);\n        target.post(nif);\n    }\n\n    void NiParticleSystemController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n\n        velocity = nif->getFloat();\n        velocityRandom = nif->getFloat();\n        verticalDir = nif->getFloat();\n        verticalAngle = nif->getFloat();\n        horizontalDir = nif->getFloat();\n        horizontalAngle = nif->getFloat();\n        /*normal?*/ nif->getVector3();\n        /*color?*/ nif->getVector4();\n        size = nif->getFloat();\n        startTime = nif->getFloat();\n        stopTime = nif->getFloat();\n        nif->getChar();\n        emitRate = nif->getFloat();\n        lifetime = nif->getFloat();\n        lifetimeRandom = nif->getFloat();\n\n        emitFlags = nif->getUShort();\n        offsetRandom = nif->getVector3();\n\n        emitter.read(nif);\n\n        /* Unknown Short, 0?\n         * Unknown Float, 1.0?\n         * Unknown Int, 1?\n         * Unknown Int, 0?\n         * Unknown Short, 0?\n         */\n        nif->skip(16);\n\n        numParticles = nif->getUShort();\n        activeCount = nif->getUShort();\n\n        particles.resize(numParticles);\n        for(size_t i = 0;i < particles.size();i++)\n        {\n            particles[i].velocity = nif->getVector3();\n            nif->getVector3(); /* unknown */\n            particles[i].lifetime = nif->getFloat();\n            particles[i].lifespan = nif->getFloat();\n            particles[i].timestamp = nif->getFloat();\n            nif->getUShort(); /* unknown */\n            particles[i].vertex = nif->getUShort();\n        }\n\n        nif->getUInt(); /* -1? */\n        affectors.read(nif);\n        colliders.read(nif);\n        nif->getChar();\n    }\n\n    void NiParticleSystemController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        emitter.post(nif);\n        affectors.post(nif);\n        colliders.post(nif);\n    }\n\n    void NiMaterialColorController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n        if (nif->getVersion() > NIFStream::generateVersion(10,1,0,103))\n            interpolator.read(nif);\n        // Two bits that correspond to the controlled material color.\n        // 00: Ambient\n        // 01: Diffuse\n        // 10: Specular\n        // 11: Emissive\n        if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n            targetColor = nif->getUShort() & 3;\n        else\n            targetColor = (flags >> 4) & 3;\n        if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))\n            data.read(nif);\n    }\n\n    void NiMaterialColorController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        interpolator.post(nif);\n        data.post(nif);\n    }\n\n    void NiLookAtController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n        if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n            lookAtFlags = nif->getUShort();\n        target.read(nif);\n    }\n\n    void NiLookAtController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        target.post(nif);\n    }\n\n    void NiPathController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n\n        bankDir = nif->getInt();\n        maxBankAngle = nif->getFloat();\n        smoothing = nif->getFloat();\n        followAxis = nif->getShort();\n        posData.read(nif);\n        floatData.read(nif);\n    }\n\n    void NiPathController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n\n        posData.post(nif);\n        floatData.post(nif);\n    }\n\n    void NiUVController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n\n        uvSet = nif->getUShort();\n        data.read(nif);\n    }\n\n    void NiUVController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        data.post(nif);\n    }\n\n    void NiKeyframeController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n        if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))\n            data.read(nif);\n        else\n            interpolator.read(nif);\n    }\n\n    void NiKeyframeController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        data.post(nif);\n        interpolator.post(nif);\n    }\n\n    void NiFloatInterpController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n        if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))\n            data.read(nif);\n        else\n            interpolator.read(nif);\n    }\n\n    void NiFloatInterpController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        data.post(nif);\n        interpolator.post(nif);\n    }\n\n    void NiGeomMorpherController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n        if (nif->getVersion() >= NIFFile::NIFVersion::VER_OB_OLD)\n            /*bool updateNormals = !!*/nif->getUShort();\n        data.read(nif);\n        if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)\n        {\n            /*bool alwaysActive = */nif->getChar(); // Always 0\n            if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,106))\n            {\n                if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)\n                {\n                    interpolators.read(nif);\n                    if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0) && nif->getBethVersion() > 9)\n                    {\n                        unsigned int numUnknown = nif->getUInt();\n                        nif->skip(4 * numUnknown);\n                    }\n                }\n                else\n                {\n                    // TODO: handle weighted interpolators\n                    unsigned int numInterps = nif->getUInt();\n                    nif->skip(8 * numInterps);\n                }\n            }\n        }\n    }\n\n    void NiGeomMorpherController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        data.post(nif);\n        interpolators.post(nif);\n    }\n\n    void NiVisController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n        data.read(nif);\n    }\n\n    void NiVisController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        data.post(nif);\n    }\n\n    void NiFlipController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n        if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0))\n            mInterpolator.read(nif);\n        mTexSlot = nif->getUInt();\n        if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))\n        {\n            timeStart = nif->getFloat();\n            mDelta = nif->getFloat();\n        }\n        mSources.read(nif);\n    }\n\n    void NiFlipController::post(NIFFile *nif)\n    {\n        Controller::post(nif);\n        mInterpolator.post(nif);\n        mSources.post(nif);\n    }\n\n    void bhkBlendController::read(NIFStream *nif)\n    {\n        Controller::read(nif);\n        nif->getUInt(); // Zero\n    }\n\n    void NiPoint3Interpolator::read(NIFStream *nif)\n    {\n        defaultVal = nif->getVector3();\n        data.read(nif);\n    }\n\n    void NiPoint3Interpolator::post(NIFFile *nif)\n    {\n        data.post(nif);\n    }\n\n    void NiBoolInterpolator::read(NIFStream *nif)\n    {\n        defaultVal = nif->getBoolean();\n        data.read(nif);\n    }\n\n    void NiBoolInterpolator::post(NIFFile *nif)\n    {\n        data.post(nif);\n    }\n\n    void NiFloatInterpolator::read(NIFStream *nif)\n    {\n        defaultVal = nif->getFloat();\n        data.read(nif);\n    }\n\n    void NiFloatInterpolator::post(NIFFile *nif)\n    {\n        data.post(nif);\n    }\n\n    void NiTransformInterpolator::read(NIFStream *nif)\n    {\n        defaultPos = nif->getVector3();\n        defaultRot = nif->getQuaternion();\n        defaultScale = nif->getFloat();\n        if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,109))\n        {\n            if (!nif->getBoolean())\n                defaultPos = osg::Vec3f();\n            if (!nif->getBoolean())\n                defaultRot = osg::Quat();\n            if (!nif->getBoolean())\n                defaultScale = 1.f;\n        }\n        data.read(nif);\n    }\n\n    void NiTransformInterpolator::post(NIFFile *nif)\n    {\n        data.post(nif);\n    }\n\n    void NiColorInterpolator::read(NIFStream *nif)\n    {\n        defaultVal = nif->getVector4();\n        data.read(nif);\n    }\n\n    void NiColorInterpolator::post(NIFFile *nif)\n    {\n        data.post(nif);\n    }\n\n}\n"
  },
  {
    "path": "components/nif/controller.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (controller.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef OPENMW_COMPONENTS_NIF_CONTROLLER_HPP\n#define OPENMW_COMPONENTS_NIF_CONTROLLER_HPP\n\n#include \"base.hpp\"\n\nnamespace Nif\n{\n\nstruct NiParticleSystemController : public Controller\n{\n    struct Particle {\n        osg::Vec3f velocity;\n        float lifetime;\n        float lifespan;\n        float timestamp;\n        unsigned short vertex;\n    };\n\n    float velocity;\n    float velocityRandom;\n\n    float verticalDir; // 0=up, pi/2=horizontal, pi=down\n    float verticalAngle;\n    float horizontalDir;\n    float horizontalAngle;\n\n    float size;\n    float startTime;\n    float stopTime;\n\n    float emitRate;\n    float lifetime;\n    float lifetimeRandom;\n\n    enum EmitFlags\n    {\n        NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise,\n                           // we calculate an emit rate so that the maximum number of particles\n                           // in the system (numParticles) is never exceeded.\n    };\n    int emitFlags;\n\n    osg::Vec3f offsetRandom;\n\n    NodePtr emitter;\n\n    int numParticles;\n    int activeCount;\n    std::vector<Particle> particles;\n\n    NiParticleModifierPtr affectors;\n    NiParticleModifierPtr colliders;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\nusing NiBSPArrayController = NiParticleSystemController;\n\nstruct NiMaterialColorController : public Controller\n{\n    NiPoint3InterpolatorPtr interpolator;\n    NiPosDataPtr data;\n    unsigned int targetColor;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiPathController : public Controller\n{\n    NiPosDataPtr posData;\n    NiFloatDataPtr floatData;\n\n    enum Flags\n    {\n        Flag_OpenCurve      = 0x020,\n        Flag_AllowFlip      = 0x040,\n        Flag_Bank           = 0x080,\n        Flag_ConstVelocity  = 0x100,\n        Flag_Follow         = 0x200,\n        Flag_FlipFollowAxis = 0x400\n    };\n\n    int bankDir;\n    float maxBankAngle, smoothing;\n    short followAxis;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiLookAtController : public Controller\n{\n    NodePtr target;\n    unsigned short lookAtFlags{0};\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiUVController : public Controller\n{\n    NiUVDataPtr data;\n    unsigned int uvSet;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiKeyframeController : public Controller\n{\n    NiKeyframeDataPtr data;\n    NiTransformInterpolatorPtr interpolator;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiFloatInterpController : public Controller\n{\n    NiFloatDataPtr data;\n    NiFloatInterpolatorPtr interpolator;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiAlphaController : public NiFloatInterpController { };\nstruct NiRollController : public NiFloatInterpController { };\n\nstruct NiGeomMorpherController : public Controller\n{\n    NiMorphDataPtr data;\n    NiFloatInterpolatorList interpolators;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiVisController : public Controller\n{\n    NiVisDataPtr data;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiFlipController : public Controller\n{\n    NiFloatInterpolatorPtr mInterpolator;\n    int mTexSlot; // NiTexturingProperty::TextureType\n    float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources\n    NiSourceTextureList mSources;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct bhkBlendController : public Controller\n{\n    void read(NIFStream *nif) override;\n};\n\nstruct Interpolator : public Record { };\n\nstruct NiPoint3Interpolator : public Interpolator\n{\n    osg::Vec3f defaultVal;\n    NiPosDataPtr data;\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiBoolInterpolator : public Interpolator\n{\n    bool defaultVal;\n    NiBoolDataPtr data;\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiFloatInterpolator : public Interpolator\n{\n    float defaultVal;\n    NiFloatDataPtr data;\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiTransformInterpolator : public Interpolator\n{\n    osg::Vec3f defaultPos;\n    osg::Quat defaultRot;\n    float defaultScale;\n    NiKeyframeDataPtr data;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiColorInterpolator : public Interpolator\n{\n    osg::Vec4f defaultVal;\n    NiColorDataPtr data;\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/data.cpp",
    "content": "#include \"data.hpp\"\n#include \"node.hpp\"\n\nnamespace Nif\n{\nvoid NiSkinInstance::read(NIFStream *nif)\n{\n    data.read(nif);\n    if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,101))\n        partitions.read(nif);\n    root.read(nif);\n    bones.read(nif);\n}\n\nvoid NiSkinInstance::post(NIFFile *nif)\n{\n    data.post(nif);\n    partitions.post(nif);\n    root.post(nif);\n    bones.post(nif);\n\n    if(data.empty() || root.empty())\n        nif->fail(\"NiSkinInstance missing root or data\");\n\n    size_t bnum = bones.length();\n    if(bnum != data->bones.size())\n        nif->fail(\"Mismatch in NiSkinData bone count\");\n\n    for(size_t i=0; i<bnum; i++)\n    {\n        if(bones[i].empty())\n            nif->fail(\"Oops: Missing bone! Don't know how to handle this.\");\n        bones[i]->setBone();\n    }\n}\n\nvoid NiGeometryData::read(NIFStream *nif)\n{\n    if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,114))\n        nif->getInt(); // Group ID. (Almost?) always 0.\n\n    int verts = nif->getUShort();\n\n    if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n        nif->skip(2); // Keep flags and compress flags\n\n    if (nif->getBoolean())\n        nif->getVector3s(vertices, verts);\n\n    unsigned int dataFlags = 0;\n    if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))\n        dataFlags = nif->getUShort();\n\n    if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)\n        nif->getUInt(); // Material CRC\n\n    if (nif->getBoolean())\n    {\n        nif->getVector3s(normals, verts);\n        if (dataFlags & 0x1000)\n        {\n            nif->getVector3s(tangents, verts);\n            nif->getVector3s(bitangents, verts);\n        }\n    }\n\n    center = nif->getVector3();\n    radius = nif->getFloat();\n\n    if (nif->getBoolean())\n        nif->getVector4s(colors, verts);\n\n    unsigned int numUVs = dataFlags;\n    if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))\n        numUVs = nif->getUShort();\n\n    // In Morrowind this field only corresponds to the number of UV sets.\n    // In later games only the first 6 bits are used as a count and the rest are flags.\n    if (nif->getVersion() > NIFFile::NIFVersion::VER_MW)\n    {\n        numUVs &= 0x3f;\n        if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0)\n            numUVs &= 0x1;\n    }\n\n    bool hasUVs = true;\n    if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)\n        hasUVs = nif->getBoolean();\n    if (hasUVs)\n    {\n        uvlist.resize(numUVs);\n        for (unsigned int i = 0; i < numUVs; i++)\n        {\n            nif->getVector2s(uvlist[i], verts);\n            // flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin\n            for (unsigned int uv=0; uv<uvlist[i].size(); ++uv)\n            {\n                uvlist[i][uv] = osg::Vec2f(uvlist[i][uv].x(), 1.f - uvlist[i][uv].y());\n            }\n        }\n    }\n\n    if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))\n        nif->getUShort(); // Consistency flags\n\n    if (nif->getVersion() >= NIFStream::generateVersion(20,0,0,4))\n        nif->skip(4); // Additional data\n}\n\nvoid NiTriShapeData::read(NIFStream *nif)\n{\n    NiGeometryData::read(nif);\n\n    /*int tris =*/ nif->getUShort();\n\n    // We have three times as many vertices as triangles, so this\n    // is always equal to tris*3.\n    int cnt = nif->getInt();\n    bool hasTriangles = true;\n    if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)\n        hasTriangles = nif->getBoolean();\n    if (hasTriangles)\n        nif->getUShorts(triangles, cnt);\n\n    // Read the match list, which lists the vertices that are equal to\n    // vertices. We don't actually need need this for anything, so\n    // just skip it.\n    unsigned short verts = nif->getUShort();\n    for (unsigned short i=0; i < verts; i++)\n    {\n        // Number of vertices matching vertex 'i'\n        int num = nif->getUShort();\n        nif->skip(num * sizeof(short));\n    }\n}\n\nvoid NiTriStripsData::read(NIFStream *nif)\n{\n    NiGeometryData::read(nif);\n\n    // Every strip with n points defines n-2 triangles, so this should be unnecessary.\n    /*int tris =*/ nif->getUShort();\n    // Number of triangle strips\n    int numStrips = nif->getUShort();\n\n    std::vector<unsigned short> lengths;\n    nif->getUShorts(lengths, numStrips);\n\n    // \"Has Strips\" flag. Exceptionally useful.\n    bool hasStrips = true;\n    if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)\n        hasStrips = nif->getBoolean();\n    if (!hasStrips || !numStrips)\n        return;\n\n    strips.resize(numStrips);\n    for (int i = 0; i < numStrips; i++)\n        nif->getUShorts(strips[i], lengths[i]);\n}\n\nvoid NiLinesData::read(NIFStream *nif)\n{\n    NiGeometryData::read(nif);\n    size_t num = vertices.size();\n    std::vector<char> flags;\n    nif->getChars(flags, num);\n    // Can't construct a line from a single vertex.\n    if (num < 2)\n        return;\n    // Convert connectivity flags into usable geometry. The last element needs special handling.\n    for (size_t i = 0; i < num-1; ++i)\n    {\n        if (flags[i] & 1)\n        {\n            lines.emplace_back(i);\n            lines.emplace_back(i+1);\n        }\n    }\n    // If there are just two vertices, they can be connected twice. Probably isn't critical.\n    if (flags[num-1] & 1)\n    {\n        lines.emplace_back(num-1);\n        lines.emplace_back(0);\n    }\n}\n\nvoid NiParticlesData::read(NIFStream *nif)\n{\n    NiGeometryData::read(nif);\n\n    // Should always match the number of vertices\n    if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)\n        numParticles = nif->getUShort();\n\n    if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))\n        std::fill(particleRadii.begin(), particleRadii.end(), nif->getFloat());\n    else if (nif->getBoolean())\n        nif->getFloats(particleRadii, vertices.size());\n    activeCount = nif->getUShort();\n\n    // Particle sizes\n    if (nif->getBoolean())\n        nif->getFloats(sizes, vertices.size());\n\n    if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0) && nif->getBoolean())\n        nif->getQuaternions(rotations, vertices.size());\n    if (nif->getVersion() >= NIFStream::generateVersion(20,0,0,4))\n    {\n        if (nif->getBoolean())\n            nif->getFloats(rotationAngles, vertices.size());\n        if (nif->getBoolean())\n            nif->getVector3s(rotationAxes, vertices.size());\n    }\n\n}\n\nvoid NiRotatingParticlesData::read(NIFStream *nif)\n{\n    NiParticlesData::read(nif);\n\n    if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0) && nif->getBoolean())\n        nif->getQuaternions(rotations, vertices.size());\n}\n\nvoid NiPosData::read(NIFStream *nif)\n{\n    mKeyList = std::make_shared<Vector3KeyMap>();\n    mKeyList->read(nif);\n}\n\nvoid NiUVData::read(NIFStream *nif)\n{\n    for(int i = 0;i < 4;i++)\n    {\n        mKeyList[i] = std::make_shared<FloatKeyMap>();\n        mKeyList[i]->read(nif);\n    }\n}\n\nvoid NiFloatData::read(NIFStream *nif)\n{\n    mKeyList = std::make_shared<FloatKeyMap>();\n    mKeyList->read(nif);\n}\n\nvoid NiPixelData::read(NIFStream *nif)\n{\n    fmt = (Format)nif->getUInt();\n\n    if (nif->getVersion() < NIFStream::generateVersion(10,4,0,2))\n    {\n        for (unsigned int i = 0; i < 4; ++i)\n            colorMask[i] = nif->getUInt();\n        bpp = nif->getUInt();\n        nif->skip(8); // \"Old Fast Compare\". Whatever that means.\n        if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n            pixelTiling = nif->getUInt();\n    }\n    else // TODO: see if anything from here needs to be implemented\n    {\n        bpp = nif->getChar();\n        nif->skip(4); // Renderer hint\n        nif->skip(4); // Extra data\n        nif->skip(4); // Flags\n        pixelTiling = nif->getUInt();\n        if (nif->getVersion() >= NIFStream::generateVersion(20,3,0,4))\n            sRGB = nif->getBoolean();\n        nif->skip(4*10); // Channel data\n    }\n\n    palette.read(nif);\n\n    numberOfMipmaps = nif->getUInt();\n\n    // Bytes per pixel, should be bpp / 8\n    /* int bytes = */ nif->getUInt();\n\n    for(unsigned int i=0; i<numberOfMipmaps; i++)\n    {\n        // Image size and offset in the following data field\n        Mipmap m;\n        m.width = nif->getUInt();\n        m.height = nif->getUInt();\n        m.dataOffset = nif->getUInt();\n        mipmaps.push_back(m);\n    }\n\n    // Read the data\n    unsigned int numPixels = nif->getUInt();\n    bool hasFaces = nif->getVersion() >= NIFStream::generateVersion(10,4,0,2);\n    unsigned int numFaces = hasFaces ? nif->getUInt() : 1;\n    if (numPixels && numFaces)\n        nif->getUChars(data, numPixels * numFaces);\n}\n\nvoid NiPixelData::post(NIFFile *nif)\n{\n    palette.post(nif);\n}\n\nvoid NiColorData::read(NIFStream *nif)\n{\n    mKeyMap = std::make_shared<Vector4KeyMap>();\n    mKeyMap->read(nif);\n}\n\nvoid NiVisData::read(NIFStream *nif)\n{\n    int count = nif->getInt();\n    mVis.resize(count);\n    for(size_t i = 0;i < mVis.size();i++)\n    {\n        mVis[i].time = nif->getFloat();\n        mVis[i].isSet = (nif->getChar() != 0);\n    }\n}\n\nvoid NiSkinData::read(NIFStream *nif)\n{\n    trafo.rotation = nif->getMatrix3();\n    trafo.pos = nif->getVector3();\n    trafo.scale = nif->getFloat();\n\n    int boneNum = nif->getInt();\n    if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))\n        partitions.read(nif);\n\n    // Has vertex weights flag\n    if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean())\n        return;\n\n    bones.resize(boneNum);\n    for (BoneInfo &bi : bones)\n    {\n        bi.trafo.rotation = nif->getMatrix3();\n        bi.trafo.pos = nif->getVector3();\n        bi.trafo.scale = nif->getFloat();\n        bi.boundSphereCenter = nif->getVector3();\n        bi.boundSphereRadius = nif->getFloat();\n\n        // Number of vertex weights\n        bi.weights.resize(nif->getUShort());\n        for(size_t j = 0;j < bi.weights.size();j++)\n        {\n            bi.weights[j].vertex = nif->getUShort();\n            bi.weights[j].weight = nif->getFloat();\n        }\n    }\n}\n\nvoid NiSkinData::post(NIFFile *nif)\n{\n    partitions.post(nif);\n}\n\nvoid NiSkinPartition::read(NIFStream *nif)\n{\n    unsigned int num = nif->getUInt();\n    data.resize(num);\n    for (auto& partition : data)\n        partition.read(nif);\n}\n\nvoid NiSkinPartition::Partition::read(NIFStream *nif)\n{\n    size_t numVertices = nif->getUShort();\n    size_t numTriangles = nif->getUShort();\n    size_t numBones = nif->getUShort();\n    size_t numStrips = nif->getUShort();\n    size_t bonesPerVertex = nif->getUShort();\n    if (numBones)\n        nif->getUShorts(bones, numBones);\n\n    bool hasVertexMap = true;\n    if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n        hasVertexMap = nif->getBoolean();\n    if (hasVertexMap && numVertices)\n        nif->getUShorts(vertexMap, numVertices);\n\n    bool hasVertexWeights = true;\n    if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n        hasVertexWeights = nif->getBoolean();\n    if (hasVertexWeights && numVertices && bonesPerVertex)\n        nif->getFloats(weights, numVertices * bonesPerVertex);\n\n    std::vector<unsigned short> stripLengths;\n    if (numStrips)\n        nif->getUShorts(stripLengths, numStrips);\n\n    bool hasFaces = true;\n    if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n        hasFaces = nif->getBoolean();\n    if (hasFaces)\n    {\n        if (numStrips)\n        {\n            strips.resize(numStrips);\n            for (size_t i = 0; i < numStrips; i++)\n                nif->getUShorts(strips[i], stripLengths[i]);\n        }\n        else if (numTriangles)\n            nif->getUShorts(triangles, numTriangles * 3);\n    }\n    bool hasBoneIndices = nif->getChar() != 0;\n    if (hasBoneIndices && numVertices && bonesPerVertex)\n        nif->getChars(boneIndices, numVertices * bonesPerVertex);\n    if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)\n    {\n        nif->getChar(); // LOD level\n        nif->getBoolean(); // Global VB\n    }\n}\n\nvoid NiMorphData::read(NIFStream *nif)\n{\n    int morphCount = nif->getInt();\n    int vertCount  = nif->getInt();\n    nif->getChar(); // Relative targets, always 1\n\n    mMorphs.resize(morphCount);\n    for(int i = 0;i < morphCount;i++)\n    {\n        mMorphs[i].mKeyFrames = std::make_shared<FloatKeyMap>();\n        mMorphs[i].mKeyFrames->read(nif, true, /*morph*/true);\n        nif->getVector3s(mMorphs[i].mVertices, vertCount);\n    }\n}\n\nvoid NiKeyframeData::read(NIFStream *nif)\n{\n    mRotations = std::make_shared<QuaternionKeyMap>();\n    mRotations->read(nif);\n    if(mRotations->mInterpolationType == InterpolationType_XYZ)\n    {\n        //Chomp unused float\n        if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))\n            nif->getFloat();\n        mXRotations = std::make_shared<FloatKeyMap>();\n        mYRotations = std::make_shared<FloatKeyMap>();\n        mZRotations = std::make_shared<FloatKeyMap>();\n        mXRotations->read(nif, true);\n        mYRotations->read(nif, true);\n        mZRotations->read(nif, true);\n    }\n    mTranslations = std::make_shared<Vector3KeyMap>();\n    mTranslations->read(nif);\n    mScales = std::make_shared<FloatKeyMap>();\n    mScales->read(nif);\n}\n\nvoid NiPalette::read(NIFStream *nif)\n{\n    unsigned int alphaMask = !nif->getChar() ? 0xFF000000 : 0;\n    // Fill the entire palette with black even if there isn't enough entries.\n    colors.resize(256);\n    unsigned int numEntries = nif->getUInt();\n    for (unsigned int i = 0; i < numEntries; i++)\n        colors[i] = nif->getUInt() | alphaMask;\n}\n\nvoid NiStringPalette::read(NIFStream *nif)\n{\n    palette = nif->getString();\n    if (nif->getUInt() != palette.size())\n        nif->file->warn(\"Failed size check in NiStringPalette\");\n}\n\nvoid NiBoolData::read(NIFStream *nif)\n{\n    mKeyList = std::make_shared<ByteKeyMap>();\n    mKeyList->read(nif);\n}\n\n} // Namespace\n"
  },
  {
    "path": "components/nif/data.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (data.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef OPENMW_COMPONENTS_NIF_DATA_HPP\n#define OPENMW_COMPONENTS_NIF_DATA_HPP\n\n#include \"base.hpp\"\n\n#include \"niftypes.hpp\" // Transformation\n\nnamespace Nif\n{\n\n// Common ancestor for several data classes\nstruct NiGeometryData : public Record\n{\n    std::vector<osg::Vec3f> vertices, normals, tangents, bitangents;\n    std::vector<osg::Vec4f> colors;\n    std::vector< std::vector<osg::Vec2f> > uvlist;\n    osg::Vec3f center;\n    float radius;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiTriShapeData : public NiGeometryData\n{\n    // Triangles, three vertex indices per triangle\n    std::vector<unsigned short> triangles;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiTriStripsData : public NiGeometryData\n{\n    // Triangle strips, series of vertex indices.\n    std::vector<std::vector<unsigned short>> strips;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiLinesData : public NiGeometryData\n{\n    // Lines, series of indices that correspond to connected vertices.\n    std::vector<unsigned short> lines;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiParticlesData : public NiGeometryData\n{\n    int numParticles{0};\n\n    int activeCount{0};\n\n    std::vector<float> particleRadii, sizes, rotationAngles;\n    std::vector<osg::Quat> rotations;\n    std::vector<osg::Vec3f> rotationAxes;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiRotatingParticlesData : public NiParticlesData\n{\n    void read(NIFStream *nif) override;\n};\n\nstruct NiPosData : public Record\n{\n    Vector3KeyMapPtr mKeyList;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiUVData : public Record\n{\n    FloatKeyMapPtr mKeyList[4];\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiFloatData : public Record\n{\n    FloatKeyMapPtr mKeyList;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiPixelData : public Record\n{\n    enum Format\n    {\n        NIPXFMT_RGB8,\n        NIPXFMT_RGBA8,\n        NIPXFMT_PAL8,\n        NIPXFMT_PALA8,\n        NIPXFMT_DXT1,\n        NIPXFMT_DXT3,\n        NIPXFMT_DXT5,\n        NIPXFMT_DXT5_ALT\n    };\n    Format fmt{NIPXFMT_RGB8};\n\n    unsigned int colorMask[4]{0};\n    unsigned int bpp{0}, pixelTiling{0};\n    bool sRGB{false};\n\n    NiPalettePtr palette;\n    unsigned int numberOfMipmaps{0};\n\n    struct Mipmap\n    {\n        int width, height;\n        int dataOffset;\n    };\n    std::vector<Mipmap> mipmaps;\n\n    std::vector<unsigned char> data;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiColorData : public Record\n{\n    Vector4KeyMapPtr mKeyMap;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiVisData : public Record\n{\n    struct VisData {\n        float time;\n        bool isSet;\n    };\n    std::vector<VisData> mVis;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiSkinInstance : public Record\n{\n    NiSkinDataPtr data;\n    NiSkinPartitionPtr partitions;\n    NodePtr root;\n    NodeList bones;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiSkinData : public Record\n{\n    struct VertWeight\n    {\n        unsigned short vertex;\n        float weight;\n    };\n\n    struct BoneInfo\n    {\n        Transformation trafo;\n        osg::Vec3f boundSphereCenter;\n        float boundSphereRadius;\n        std::vector<VertWeight> weights;\n    };\n\n    Transformation trafo;\n    std::vector<BoneInfo> bones;\n    NiSkinPartitionPtr partitions;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiSkinPartition : public Record\n{\n    struct Partition\n    {\n        std::vector<unsigned short> bones;\n        std::vector<unsigned short> vertexMap;\n        std::vector<float> weights;\n        std::vector<std::vector<unsigned short>> strips;\n        std::vector<unsigned short> triangles;\n        std::vector<char> boneIndices;\n        void read(NIFStream *nif);\n    };\n    std::vector<Partition> data;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiMorphData : public Record\n{\n    struct MorphData {\n        FloatKeyMapPtr mKeyFrames;\n        std::vector<osg::Vec3f> mVertices;\n    };\n    std::vector<MorphData> mMorphs;\n\n    void read(NIFStream *nif) override;\n};\n\n\nstruct NiKeyframeData : public Record\n{\n    QuaternionKeyMapPtr mRotations;\n\n    // may be NULL\n    FloatKeyMapPtr mXRotations;\n    FloatKeyMapPtr mYRotations;\n    FloatKeyMapPtr mZRotations;\n\n    Vector3KeyMapPtr mTranslations;\n    FloatKeyMapPtr mScales;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiPalette : public Record\n{\n    // 32-bit RGBA colors that correspond to 8-bit indices\n    std::vector<unsigned int> colors;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiStringPalette : public Record\n{\n    std::string palette;\n    void read(NIFStream *nif) override;\n};\n\nstruct NiBoolData : public Record\n{\n    ByteKeyMapPtr mKeyList;\n    void read(NIFStream *nif) override;\n};\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/effect.cpp",
    "content": "#include \"effect.hpp\"\n\n#include \"node.hpp\"\n\nnamespace Nif\n{\n\nvoid NiLight::read(NIFStream *nif)\n{\n    NiDynamicEffect::read(nif);\n\n    dimmer = nif->getFloat();\n    ambient = nif->getVector3();\n    diffuse = nif->getVector3();\n    specular = nif->getVector3();\n}\n\nvoid NiTextureEffect::read(NIFStream *nif)\n{\n    NiDynamicEffect::read(nif);\n\n    // Model Projection Matrix\n    nif->skip(3 * 3 * sizeof(float));\n\n    // Model Projection Transform\n    nif->skip(3 * sizeof(float));\n\n    // Texture Filtering\n    nif->skip(4);\n\n    // Max anisotropy samples\n    if (nif->getVersion() >= NIFStream::generateVersion(20,5,0,4))\n        nif->skip(2);\n\n    clamp = nif->getUInt();\n\n    textureType = (TextureType)nif->getUInt();\n\n    coordGenType = (CoordGenType)nif->getUInt();\n\n    texture.read(nif);\n\n    nif->skip(1); // Use clipping plane\n    nif->skip(16); // Clipping plane dimensions vector\n    if (nif->getVersion() <= NIFStream::generateVersion(10,2,0,0))\n        nif->skip(4); // PS2-specific shorts\n    if (nif->getVersion() <= NIFStream::generateVersion(4,1,0,12))\n        nif->skip(2); // Unknown short\n}\n\nvoid NiTextureEffect::post(NIFFile *nif)\n{\n    NiDynamicEffect::post(nif);\n    texture.post(nif);\n}\n\nvoid NiPointLight::read(NIFStream *nif)\n{\n    NiLight::read(nif);\n\n    constantAttenuation = nif->getFloat();\n    linearAttenuation = nif->getFloat();\n    quadraticAttenuation = nif->getFloat();\n}\n\nvoid NiSpotLight::read(NIFStream *nif)\n{\n    NiPointLight::read(nif);\n\n    cutoff = nif->getFloat();\n    exponent = nif->getFloat();\n}\n\n}\n"
  },
  {
    "path": "components/nif/effect.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (effect.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef OPENMW_COMPONENTS_NIF_EFFECT_HPP\n#define OPENMW_COMPONENTS_NIF_EFFECT_HPP\n\n#include \"node.hpp\"\n\nnamespace Nif\n{\n\nstruct NiDynamicEffect : public Node\n{\n    void read(NIFStream *nif) override\n    {\n        Node::read(nif);\n        if (nif->getVersion() >= nif->generateVersion(10,1,0,106)\n         && nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)\n            nif->getBoolean(); // Switch state\n        unsigned int numAffectedNodes = nif->getUInt();\n        for (unsigned int i=0; i<numAffectedNodes; ++i)\n            nif->getUInt(); // ref to another Node\n    }\n};\n\n// Used as base for NiAmbientLight, NiDirectionalLight, NiPointLight and NiSpotLight.\nstruct NiLight : NiDynamicEffect\n{\n    float dimmer;\n    osg::Vec3f ambient;\n    osg::Vec3f diffuse;\n    osg::Vec3f specular;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiPointLight : public NiLight\n{\n    float constantAttenuation;\n    float linearAttenuation;\n    float quadraticAttenuation;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiSpotLight : public NiPointLight\n{\n    float cutoff;\n    float exponent;\n    void read(NIFStream *nif) override;\n};\n\nstruct NiTextureEffect : NiDynamicEffect\n{\n    NiSourceTexturePtr texture;\n    unsigned int clamp;\n\n    enum TextureType\n    {\n        Projected_Light = 0,\n        Projected_Shadow = 1,\n        Environment_Map = 2,\n        Fog_Map = 3\n    };\n    TextureType textureType;\n\n    enum CoordGenType\n    {\n        World_Parallel = 0,\n        World_Perspective,\n        Sphere_Map,\n        Specular_Cube_Map,\n        Diffuse_Cube_Map\n    };\n    CoordGenType coordGenType;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/extra.cpp",
    "content": "#include \"extra.hpp\"\n\nnamespace Nif\n{\n\nvoid NiStringExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n    string = nif->getString();\n}\n\nvoid NiTextKeyExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n\n    int keynum = nif->getInt();\n    list.resize(keynum);\n    for(int i=0; i<keynum; i++)\n    {\n        list[i].time = nif->getFloat();\n        list[i].text = nif->getString();\n    }\n}\n\nvoid NiVertWeightsExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n\n    nif->skip(nif->getUShort() * sizeof(float)); // vertex weights I guess\n}\n\nvoid NiIntegerExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n\n    data = nif->getUInt();\n}\n\nvoid NiIntegersExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n\n    unsigned int num = nif->getUInt();\n    if (num)\n        nif->getUInts(data, num);\n}\n\nvoid NiBinaryExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n    unsigned int size = nif->getUInt();\n    if (size)\n        nif->getChars(data, size);\n}\n\nvoid NiBooleanExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n    data = nif->getBoolean();\n}\n\nvoid NiVectorExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n    data = nif->getVector4();\n}\n\nvoid NiFloatExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n\n    data = nif->getFloat();\n}\n\nvoid NiFloatsExtraData::read(NIFStream *nif)\n{\n    Extra::read(nif);\n    unsigned int num = nif->getUInt();\n    if (num)\n        nif->getFloats(data, num);\n}\n\nvoid BSBound::read(NIFStream *nif)\n{\n    Extra::read(nif);\n    center = nif->getVector3();\n    halfExtents = nif->getVector3();\n}\n\n}\n"
  },
  {
    "path": "components/nif/extra.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (extra.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef OPENMW_COMPONENTS_NIF_EXTRA_HPP\n#define OPENMW_COMPONENTS_NIF_EXTRA_HPP\n\n#include \"base.hpp\"\n\nnamespace Nif\n{\n\nstruct NiVertWeightsExtraData : public Extra\n{\n    void read(NIFStream *nif) override;\n};\n\nstruct NiTextKeyExtraData : public Extra\n{\n    struct TextKey\n    {\n        float time;\n        std::string text;\n    };\n    std::vector<TextKey> list;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiStringExtraData : public Extra\n{\n    /* Two known meanings:\n       \"MRK\" - marker, only visible in the editor, not rendered in-game\n       \"NCO\" - no collision\n    */\n    std::string string;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiIntegerExtraData : public Extra\n{\n    unsigned int data;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiIntegersExtraData : public Extra\n{\n    std::vector<unsigned int> data;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiBinaryExtraData : public Extra\n{\n    std::vector<char> data;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiBooleanExtraData : public Extra\n{\n    bool data;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiVectorExtraData : public Extra\n{\n    osg::Vec4f data;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiFloatExtraData : public Extra\n{\n    float data;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiFloatsExtraData : public Extra\n{\n    std::vector<float> data;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct BSBound : public Extra\n{\n    osg::Vec3f center, halfExtents;\n\n    void read(NIFStream *nif) override;\n};\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/niffile.cpp",
    "content": "#include \"niffile.hpp\"\n#include \"effect.hpp\"\n\n#include <array>\n#include <map>\n#include <sstream>\n\nnamespace Nif\n{\n\n/// Open a NIF stream. The name is used for error messages.\nNIFFile::NIFFile(Files::IStreamPtr stream, const std::string &name)\n    : filename(name)\n{\n    parse(stream);\n}\n\nNIFFile::~NIFFile()\n{\n    for (Record* record : records)\n        delete record;\n}\n\ntemplate <typename NodeType> static Record* construct() { return new NodeType; }\n\nstruct RecordFactoryEntry {\n\n    using create_t = Record* (*)();\n\n    create_t        mCreate;\n    RecordType      mType;\n\n};\n\n///These are all the record types we know how to read.\nstatic std::map<std::string,RecordFactoryEntry> makeFactory()\n{\n    std::map<std::string,RecordFactoryEntry> factory;\n    factory[\"NiNode\"]                       = {&construct <NiNode>                      , RC_NiNode                     };\n    factory[\"NiSwitchNode\"]                 = {&construct <NiSwitchNode>                , RC_NiSwitchNode               };\n    factory[\"NiLODNode\"]                    = {&construct <NiLODNode>                   , RC_NiLODNode                  };\n    factory[\"AvoidNode\"]                    = {&construct <NiNode>                      , RC_AvoidNode                  };\n    factory[\"NiCollisionSwitch\"]            = {&construct <NiNode>                      , RC_NiCollisionSwitch          };\n    factory[\"NiBSParticleNode\"]             = {&construct <NiNode>                      , RC_NiBSParticleNode           };\n    factory[\"NiBSAnimationNode\"]            = {&construct <NiNode>                      , RC_NiBSAnimationNode          };\n    factory[\"NiBillboardNode\"]              = {&construct <NiNode>                      , RC_NiBillboardNode            };\n    factory[\"NiTriShape\"]                   = {&construct <NiTriShape>                  , RC_NiTriShape                 };\n    factory[\"NiTriStrips\"]                  = {&construct <NiTriStrips>                 , RC_NiTriStrips                };\n    factory[\"NiLines\"]                      = {&construct <NiLines>                     , RC_NiLines                    };\n    factory[\"NiParticles\"]                  = {&construct <NiParticles>                 , RC_NiParticles                };\n    factory[\"NiRotatingParticles\"]          = {&construct <NiParticles>                 , RC_NiParticles                };\n    factory[\"NiAutoNormalParticles\"]        = {&construct <NiParticles>                 , RC_NiParticles                };\n    factory[\"NiCamera\"]                     = {&construct <NiCamera>                    , RC_NiCamera                   };\n    factory[\"RootCollisionNode\"]            = {&construct <NiNode>                      , RC_RootCollisionNode          };\n    factory[\"NiTexturingProperty\"]          = {&construct <NiTexturingProperty>         , RC_NiTexturingProperty        };\n    factory[\"NiFogProperty\"]                = {&construct <NiFogProperty>               , RC_NiFogProperty              };\n    factory[\"NiMaterialProperty\"]           = {&construct <NiMaterialProperty>          , RC_NiMaterialProperty         };\n    factory[\"NiZBufferProperty\"]            = {&construct <NiZBufferProperty>           , RC_NiZBufferProperty          };\n    factory[\"NiAlphaProperty\"]              = {&construct <NiAlphaProperty>             , RC_NiAlphaProperty            };\n    factory[\"NiVertexColorProperty\"]        = {&construct <NiVertexColorProperty>       , RC_NiVertexColorProperty      };\n    factory[\"NiShadeProperty\"]              = {&construct <NiShadeProperty>             , RC_NiShadeProperty            };\n    factory[\"NiDitherProperty\"]             = {&construct <NiDitherProperty>            , RC_NiDitherProperty           };\n    factory[\"NiWireframeProperty\"]          = {&construct <NiWireframeProperty>         , RC_NiWireframeProperty        };\n    factory[\"NiSpecularProperty\"]           = {&construct <NiSpecularProperty>          , RC_NiSpecularProperty         };\n    factory[\"NiStencilProperty\"]            = {&construct <NiStencilProperty>           , RC_NiStencilProperty          };\n    factory[\"NiVisController\"]              = {&construct <NiVisController>             , RC_NiVisController            };\n    factory[\"NiGeomMorpherController\"]      = {&construct <NiGeomMorpherController>     , RC_NiGeomMorpherController    };\n    factory[\"NiKeyframeController\"]         = {&construct <NiKeyframeController>        , RC_NiKeyframeController       };\n    factory[\"NiAlphaController\"]            = {&construct <NiAlphaController>           , RC_NiAlphaController          };\n    factory[\"NiRollController\"]             = {&construct <NiRollController>            , RC_NiRollController           };\n    factory[\"NiUVController\"]               = {&construct <NiUVController>              , RC_NiUVController             };\n    factory[\"NiPathController\"]             = {&construct <NiPathController>            , RC_NiPathController           };\n    factory[\"NiMaterialColorController\"]    = {&construct <NiMaterialColorController>   , RC_NiMaterialColorController  };\n    factory[\"NiBSPArrayController\"]         = {&construct <NiBSPArrayController>        , RC_NiBSPArrayController       };\n    factory[\"NiParticleSystemController\"]   = {&construct <NiParticleSystemController>  , RC_NiParticleSystemController };\n    factory[\"NiFlipController\"]             = {&construct <NiFlipController>            , RC_NiFlipController           };\n    factory[\"NiAmbientLight\"]               = {&construct <NiLight>                     , RC_NiLight                    };\n    factory[\"NiDirectionalLight\"]           = {&construct <NiLight>                     , RC_NiLight                    };\n    factory[\"NiPointLight\"]                 = {&construct <NiPointLight>                , RC_NiLight                    };\n    factory[\"NiSpotLight\"]                  = {&construct <NiSpotLight>                 , RC_NiLight                    };\n    factory[\"NiTextureEffect\"]              = {&construct <NiTextureEffect>             , RC_NiTextureEffect            };\n    factory[\"NiVertWeightsExtraData\"]       = {&construct <NiVertWeightsExtraData>      , RC_NiVertWeightsExtraData     };\n    factory[\"NiTextKeyExtraData\"]           = {&construct <NiTextKeyExtraData>          , RC_NiTextKeyExtraData         };\n    factory[\"NiStringExtraData\"]            = {&construct <NiStringExtraData>           , RC_NiStringExtraData          };\n    factory[\"NiGravity\"]                    = {&construct <NiGravity>                   , RC_NiGravity                  };\n    factory[\"NiPlanarCollider\"]             = {&construct <NiPlanarCollider>            , RC_NiPlanarCollider           };\n    factory[\"NiSphericalCollider\"]          = {&construct <NiSphericalCollider>         , RC_NiSphericalCollider        };\n    factory[\"NiParticleGrowFade\"]           = {&construct <NiParticleGrowFade>          , RC_NiParticleGrowFade         };\n    factory[\"NiParticleColorModifier\"]      = {&construct <NiParticleColorModifier>     , RC_NiParticleColorModifier    };\n    factory[\"NiParticleRotation\"]           = {&construct <NiParticleRotation>          , RC_NiParticleRotation         };\n    factory[\"NiFloatData\"]                  = {&construct <NiFloatData>                 , RC_NiFloatData                };\n    factory[\"NiTriShapeData\"]               = {&construct <NiTriShapeData>              , RC_NiTriShapeData             };\n    factory[\"NiTriStripsData\"]              = {&construct <NiTriStripsData>             , RC_NiTriStripsData            };\n    factory[\"NiLinesData\"]                  = {&construct <NiLinesData>                 , RC_NiLinesData                };\n    factory[\"NiVisData\"]                    = {&construct <NiVisData>                   , RC_NiVisData                  };\n    factory[\"NiColorData\"]                  = {&construct <NiColorData>                 , RC_NiColorData                };\n    factory[\"NiPixelData\"]                  = {&construct <NiPixelData>                 , RC_NiPixelData                };\n    factory[\"NiMorphData\"]                  = {&construct <NiMorphData>                 , RC_NiMorphData                };\n    factory[\"NiKeyframeData\"]               = {&construct <NiKeyframeData>              , RC_NiKeyframeData             };\n    factory[\"NiSkinData\"]                   = {&construct <NiSkinData>                  , RC_NiSkinData                 };\n    factory[\"NiUVData\"]                     = {&construct <NiUVData>                    , RC_NiUVData                   };\n    factory[\"NiPosData\"]                    = {&construct <NiPosData>                   , RC_NiPosData                  };\n    factory[\"NiParticlesData\"]              = {&construct <NiParticlesData>             , RC_NiParticlesData            };\n    factory[\"NiRotatingParticlesData\"]      = {&construct <NiRotatingParticlesData>     , RC_NiParticlesData            };\n    factory[\"NiAutoNormalParticlesData\"]    = {&construct <NiParticlesData>             , RC_NiParticlesData            };\n    factory[\"NiSequenceStreamHelper\"]       = {&construct <NiSequenceStreamHelper>      , RC_NiSequenceStreamHelper     };\n    factory[\"NiSourceTexture\"]              = {&construct <NiSourceTexture>             , RC_NiSourceTexture            };\n    factory[\"NiSkinInstance\"]               = {&construct <NiSkinInstance>              , RC_NiSkinInstance             };\n    factory[\"NiLookAtController\"]           = {&construct <NiLookAtController>          , RC_NiLookAtController         };\n    factory[\"NiPalette\"]                    = {&construct <NiPalette>                   , RC_NiPalette                  };\n    factory[\"NiIntegerExtraData\"]           = {&construct <NiIntegerExtraData>          , RC_NiIntegerExtraData         };\n    factory[\"NiIntegersExtraData\"]          = {&construct <NiIntegersExtraData>         , RC_NiIntegersExtraData        };\n    factory[\"NiBinaryExtraData\"]            = {&construct <NiBinaryExtraData>           , RC_NiBinaryExtraData          };\n    factory[\"NiBooleanExtraData\"]           = {&construct <NiBooleanExtraData>          , RC_NiBooleanExtraData         };\n    factory[\"NiVectorExtraData\"]            = {&construct <NiVectorExtraData>           , RC_NiVectorExtraData          };\n    factory[\"NiColorExtraData\"]             = {&construct <NiVectorExtraData>           , RC_NiColorExtraData           };\n    factory[\"NiFloatExtraData\"]             = {&construct <NiFloatExtraData>            , RC_NiFloatExtraData           };\n    factory[\"NiFloatsExtraData\"]            = {&construct <NiFloatsExtraData>           , RC_NiFloatsExtraData          };\n    factory[\"NiStringPalette\"]              = {&construct <NiStringPalette>             , RC_NiStringPalette            };\n    factory[\"NiBoolData\"]                   = {&construct <NiBoolData>                  , RC_NiBoolData                 };\n    factory[\"NiSkinPartition\"]              = {&construct <NiSkinPartition>             , RC_NiSkinPartition            };\n    factory[\"BSXFlags\"]                     = {&construct <NiIntegerExtraData>          , RC_BSXFlags                   };\n    factory[\"BSBound\"]                      = {&construct <BSBound>                     , RC_BSBound                    };\n    factory[\"NiTransformData\"]              = {&construct <NiKeyframeData>              , RC_NiKeyframeData             };\n    factory[\"BSFadeNode\"]                   = {&construct <NiNode>                      , RC_NiNode                     };\n    factory[\"bhkBlendController\"]           = {&construct <bhkBlendController>          , RC_bhkBlendController         };\n    factory[\"NiFloatInterpolator\"]          = {&construct <NiFloatInterpolator>         , RC_NiFloatInterpolator        };\n    factory[\"NiBoolInterpolator\"]           = {&construct <NiBoolInterpolator>          , RC_NiBoolInterpolator         };\n    factory[\"NiPoint3Interpolator\"]         = {&construct <NiPoint3Interpolator>        , RC_NiPoint3Interpolator       };\n    factory[\"NiTransformController\"]        = {&construct <NiKeyframeController>        , RC_NiKeyframeController       };\n    factory[\"NiTransformInterpolator\"]      = {&construct <NiTransformInterpolator>     , RC_NiTransformInterpolator    };\n    factory[\"NiColorInterpolator\"]          = {&construct <NiColorInterpolator>         , RC_NiColorInterpolator        };\n    factory[\"BSShaderTextureSet\"]           = {&construct <BSShaderTextureSet>          , RC_BSShaderTextureSet         };\n    factory[\"BSLODTriShape\"]                = {&construct <BSLODTriShape>               , RC_BSLODTriShape              };\n    factory[\"BSShaderProperty\"]             = {&construct <BSShaderProperty>            , RC_BSShaderProperty           };\n    factory[\"BSShaderPPLightingProperty\"]   = {&construct <BSShaderPPLightingProperty>  , RC_BSShaderPPLightingProperty };\n    factory[\"BSShaderNoLightingProperty\"]   = {&construct <BSShaderNoLightingProperty>  , RC_BSShaderNoLightingProperty };\n    return factory;\n}\n\n///Make the factory map used for parsing the file\nstatic const std::map<std::string,RecordFactoryEntry> factories = makeFactory();\n\nstd::string NIFFile::printVersion(unsigned int version)\n{\n    int major = (version >> 24) & 0xFF;\n    int minor = (version >> 16) & 0xFF;\n    int patch = (version >> 8) & 0xFF;\n    int rev = version & 0xFF;\n\n    std::stringstream stream;\n    stream << major << \".\" << minor << \".\" << patch << \".\" << rev;\n    return stream.str();\n}\n\nvoid NIFFile::parse(Files::IStreamPtr stream)\n{\n    NIFStream nif (this, stream);\n\n    // Check the header string\n    std::string head = nif.getVersionString();\n    static const std::array<std::string, 2> verStrings =\n    {\n        \"NetImmerse File Format\",\n        \"Gamebryo File Format\"\n    };\n    bool supported = false;\n    for (const std::string& verString : verStrings)\n    {\n        supported = (head.compare(0, verString.size(), verString) == 0);\n        if (supported)\n            break;\n    }\n    if (!supported)\n        fail(\"Invalid NIF header: \" + head);\n\n    supported = false;\n\n    // Get BCD version\n    ver = nif.getUInt();\n    // 4.0.0.0 is an older, practically identical version of the format.\n    // It's not used by Morrowind assets but Morrowind supports it.\n    static const std::array<uint32_t, 2> supportedVers =\n    {\n        NIFStream::generateVersion(4,0,0,0),\n        VER_MW\n    };\n    for (uint32_t supportedVer : supportedVers)\n    {\n        supported = (ver == supportedVer);\n        if (supported)\n            break;\n    }\n    if (!supported)\n    {\n        if (sLoadUnsupportedFiles)\n            warn(\"Unsupported NIF version: \" + printVersion(ver) + \". Proceed with caution!\");\n        else\n            fail(\"Unsupported NIF version: \" + printVersion(ver));\n    }\n\n    // NIF data endianness\n    if (ver >= NIFStream::generateVersion(20,0,0,4))\n    {\n        unsigned char endianness = nif.getChar();\n        if (endianness == 0)\n            fail(\"Big endian NIF files are unsupported\");\n    }\n\n    // User version\n    if (ver > NIFStream::generateVersion(10,0,1,8))\n        userVer = nif.getUInt();\n\n    // Number of records\n    const std::size_t recNum = nif.getUInt();\n    records.resize(recNum);\n\n    // Bethesda stream header\n    // It contains Bethesda format version and (useless) export information\n    if (ver == VER_OB_OLD ||\n       (userVer >= 3 && ((ver == VER_OB || ver == VER_BGS)\n    || (ver >= NIFStream::generateVersion(10,1,0,0) && ver <= NIFStream::generateVersion(20,0,0,4) && userVer <= 11))))\n    {\n        bethVer = nif.getUInt();\n        nif.getExportString(); // Author\n        if (bethVer > BETHVER_FO4)\n            nif.getUInt(); // Unknown\n        nif.getExportString(); // Process script\n        nif.getExportString(); // Export script\n        if (bethVer == BETHVER_FO4)\n            nif.getExportString(); // Max file path\n    }\n\n    std::vector<std::string> recTypes;\n    std::vector<unsigned short> recTypeIndices;\n\n    const bool hasRecTypeListings = ver >= NIFStream::generateVersion(5,0,0,1);\n    if (hasRecTypeListings)\n    {\n        unsigned short recTypeNum = nif.getUShort();\n        if (recTypeNum) // Record type list\n            nif.getSizedStrings(recTypes, recTypeNum);\n        if (recNum) // Record type mapping for each record\n            nif.getUShorts(recTypeIndices, recNum);\n        if (ver >= NIFStream::generateVersion(5,0,0,6)) // Groups\n        {\n            if (ver >= NIFStream::generateVersion(20,1,0,1)) // String table\n            {\n                if (ver >= NIFStream::generateVersion(20,2,0,5) && recNum) // Record sizes\n                {\n                    std::vector<unsigned int> recSizes; // Currently unused\n                    nif.getUInts(recSizes, recNum);\n                }\n                const std::size_t stringNum = nif.getUInt();\n                nif.getUInt(); // Max string length\n                if (stringNum)\n                    nif.getSizedStrings(strings, stringNum);\n            }\n            std::vector<unsigned int> groups; // Currently unused\n            unsigned int groupNum = nif.getUInt();\n            if (groupNum)\n                nif.getUInts(groups, groupNum);\n        }\n    }\n\n    const bool hasRecordSeparators = ver >= NIFStream::generateVersion(10,0,0,0) && ver < NIFStream::generateVersion(10,2,0,0);\n    for (std::size_t i = 0; i < recNum; i++)\n    {\n        Record *r = nullptr;\n\n        std::string rec = hasRecTypeListings ? recTypes[recTypeIndices[i]] : nif.getString();\n        if(rec.empty())\n        {\n            std::stringstream error;\n            error << \"Record number \" << i << \" out of \" << recNum << \" is blank.\";\n            fail(error.str());\n        }\n\n        // Record separator. Some Havok records in Oblivion do not have it.\n        if (hasRecordSeparators && rec.compare(0, 3, \"bhk\"))\n        {\n            if (nif.getInt())\n            {\n                std::stringstream warning;\n                warning << \"Record number \" << i << \" out of \" << recNum << \" is preceded by a non-zero separator.\";\n                warn(warning.str());\n            }\n        }\n\n        std::map<std::string,RecordFactoryEntry>::const_iterator entry = factories.find(rec);\n\n        if (entry != factories.end())\n        {\n            r = entry->second.mCreate ();\n            r->recType = entry->second.mType;\n        }\n        else\n            fail(\"Unknown record type \" + rec);\n\n        if (!supported)\n            Log(Debug::Verbose) << \"NIF Debug: Reading record of type \" << rec << \", index \" << i << \" (\" << filename << \")\";\n\n        assert(r != nullptr);\n        assert(r->recType != RC_MISSING);\n        r->recName = rec;\n        r->recIndex = i;\n        records[i] = r;\n        r->read(&nif);\n    }\n\n    const std::size_t rootNum = nif.getUInt();\n    roots.resize(rootNum);\n\n    //Determine which records are roots\n    for (std::size_t i = 0; i < rootNum; i++)\n    {\n        int idx = nif.getInt();\n        if (idx >= 0 && static_cast<std::size_t>(idx) < records.size())\n        {\n            roots[i] = records[idx];\n        }\n        else\n        {\n            roots[i] = nullptr;\n            warn(\"Root \" + std::to_string(i + 1) + \" does not point to a record: index \" + std::to_string(idx));\n        }\n    }\n\n    // Once parsing is done, do post-processing.\n    for (Record* record : records)\n        record->post(this);\n}\n\nvoid NIFFile::setUseSkinning(bool skinning)\n{\n    mUseSkinning = skinning;\n}\n\nbool NIFFile::getUseSkinning() const\n{\n    return mUseSkinning;\n}\n\nbool NIFFile::sLoadUnsupportedFiles = false;\n\nvoid NIFFile::setLoadUnsupportedFiles(bool load)\n{\n    sLoadUnsupportedFiles = load;\n}\n\n}\n"
  },
  {
    "path": "components/nif/niffile.hpp",
    "content": "///Main header for reading .nif files\n\n#ifndef OPENMW_COMPONENTS_NIF_NIFFILE_HPP\n#define OPENMW_COMPONENTS_NIF_NIFFILE_HPP\n\n#include <stdexcept>\n#include <vector>\n\n#include <components/debug/debuglog.hpp>\n#include <components/files/constrainedfilestream.hpp>\n\n#include \"record.hpp\"\n\nnamespace Nif\n{\n\nstruct File\n{\n    virtual ~File() = default;\n\n    virtual Record *getRecord(size_t index) const = 0;\n\n    virtual size_t numRecords() const = 0;\n\n    virtual Record *getRoot(size_t index = 0) const = 0;\n\n    virtual size_t numRoots() const = 0;\n\n    virtual std::string getString(uint32_t index) const = 0;\n\n    virtual void setUseSkinning(bool skinning) = 0;\n\n    virtual bool getUseSkinning() const = 0;\n\n    virtual std::string getFilename() const = 0;\n\n    virtual unsigned int getVersion() const = 0;\n\n    virtual unsigned int getUserVersion() const = 0;\n\n    virtual unsigned int getBethVersion() const = 0;\n};\n\nclass NIFFile final : public File\n{\n    /// File version, user version, Bethesda version\n    unsigned int ver = 0;\n    unsigned int userVer = 0;\n    unsigned int bethVer = 0;\n\n    /// File name, used for error messages and opening the file\n    std::string filename;\n\n    /// Record list\n    std::vector<Record*> records;\n\n    /// Root list.  This is a select portion of the pointers from records\n    std::vector<Record*> roots;\n\n    /// String table\n    std::vector<std::string> strings;\n\n    bool mUseSkinning = false;\n\n    static bool sLoadUnsupportedFiles;\n\n    /// Parse the file\n    void parse(Files::IStreamPtr stream);\n\n    /// Get the file's version in a human readable form\n    ///\\returns A string containing a human readable NIF version number\n    std::string printVersion(unsigned int version);\n\n    ///Private Copy Constructor\n    NIFFile (NIFFile const &);\n    ///\\overload\n    void operator = (NIFFile const &);\n\npublic:\n    // For generic versions NIFStream::generateVersion() is used instead\n    enum NIFVersion\n    {\n        VER_MW         = 0x04000002,    // 4.0.0.2. Main Morrowind NIF version.\n        VER_OB_OLD     = 0x0A000102,    // 10.0.1.2. Main older Oblivion NIF version.\n        VER_OB         = 0x14000005,    // 20.0.0.5. Main Oblivion NIF version.\n        VER_BGS        = 0x14020007     // 20.2.0.7. Main Fallout 3/4/76/New Vegas and Skyrim/SkyrimSE NIF version.\n    };\n    enum BethVersion\n    {\n        BETHVER_FO3      = 34,          // Fallout 3\n        BETHVER_FO4      = 130          // Fallout 4\n    };\n\n    /// Used if file parsing fails\n    void fail(const std::string &msg) const\n    {\n        std::string err = \" NIFFile Error: \" + msg;\n        err += \"\\nFile: \" + filename;\n        throw std::runtime_error(err);\n    }\n    /// Used when something goes wrong, but not catastrophically so\n    void warn(const std::string &msg) const\n    {\n        Log(Debug::Warning) << \" NIFFile Warning: \" << msg << \"\\nFile: \" << filename;\n    }\n\n    /// Open a NIF stream. The name is used for error messages.\n    NIFFile(Files::IStreamPtr stream, const std::string &name);\n    ~NIFFile();\n\n    /// Get a given record\n    Record *getRecord(size_t index) const override\n    {\n        Record *res = records.at(index);\n        return res;\n    }\n    /// Number of records\n    size_t numRecords() const override { return records.size(); }\n\n    /// Get a given root\n    Record *getRoot(size_t index=0) const override\n    {\n        Record *res = roots.at(index);\n        return res;\n    }\n    /// Number of roots\n    size_t numRoots() const override { return roots.size(); }\n\n    /// Get a given string from the file's string table\n    std::string getString(uint32_t index) const override\n    {\n        if (index == std::numeric_limits<uint32_t>::max())\n            return std::string();\n        return strings.at(index);\n    }\n\n    /// Set whether there is skinning contained in this NIF file.\n    /// @note This is just a hint for users of the NIF file and has no effect on the loading procedure.\n    void setUseSkinning(bool skinning) override;\n\n    bool getUseSkinning() const override;\n\n    /// Get the name of the file\n    std::string getFilename() const override { return filename; }\n\n    /// Get the version of the NIF format used\n    unsigned int getVersion() const override { return ver; }\n\n    /// Get the user version of the NIF format used\n    unsigned int getUserVersion() const override { return userVer; }\n\n    /// Get the Bethesda version of the NIF format used\n    unsigned int getBethVersion() const override { return bethVer; }\n\n    static void setLoadUnsupportedFiles(bool load);\n};\nusing NIFFilePtr = std::shared_ptr<const Nif::NIFFile>;\n\n\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/nifkey.hpp",
    "content": "///File to handle keys used by nif file records\n\n#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP\n#define OPENMW_COMPONENTS_NIF_NIFKEY_HPP\n\n#include \"nifstream.hpp\"\n\n#include <sstream>\n#include <map>\n\n#include \"niffile.hpp\"\n\nnamespace Nif\n{\n\nenum InterpolationType\n{\n    InterpolationType_Unknown = 0,\n    InterpolationType_Linear = 1,\n    InterpolationType_Quadratic = 2,\n    InterpolationType_TBC = 3,\n    InterpolationType_XYZ = 4,\n    InterpolationType_Constant = 5\n};\n\ntemplate<typename T>\nstruct KeyT {\n    T mValue;\n    T mInTan; // Only for Quadratic interpolation, and never for QuaternionKeyList\n    T mOutTan; // Only for Quadratic interpolation, and never for QuaternionKeyList\n\n    // FIXME: Implement TBC interpolation\n    /*\n    float mTension;    // Only for TBC interpolation\n    float mBias;       // Only for TBC interpolation\n    float mContinuity; // Only for TBC interpolation\n    */\n};\nusing FloatKey = KeyT<float>;\nusing Vector3Key = KeyT<osg::Vec3f>;\nusing Vector4Key = KeyT<osg::Vec4f>;\nusing QuaternionKey = KeyT<osg::Quat>;\n\ntemplate<typename T, T (NIFStream::*getValue)()>\nstruct KeyMapT {\n    using MapType = std::map<float, KeyT<T>>;\n\n    using ValueType = T;\n    using KeyType = KeyT<T>;\n\n    unsigned int mInterpolationType = InterpolationType_Linear;\n    MapType mKeys;\n\n    //Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)\n    void read(NIFStream *nif, bool force = false, bool morph = false)\n    {\n        assert(nif);\n\n        mInterpolationType = InterpolationType_Unknown;\n\n        if (morph && nif->getVersion() >= NIFStream::generateVersion(10,1,0,106))\n            nif->getString(); // Frame name\n\n        size_t count = nif->getUInt();\n        if (count == 0 && !force && !morph)\n            return;\n\n        if (morph && nif->getVersion() > NIFStream::generateVersion(10,1,0,0))\n        {\n            if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,104) &&\n                nif->getVersion() <= NIFStream::generateVersion(20,1,0,2) && nif->getBethVersion() < 10)\n                nif->getFloat(); // Legacy weight\n            return;\n        }\n\n        mKeys.clear();\n\n        mInterpolationType = nif->getUInt();\n\n        KeyType key = {};\n        NIFStream &nifReference = *nif;\n\n        if (mInterpolationType == InterpolationType_Linear\n         || mInterpolationType == InterpolationType_Constant)\n        {\n            for(size_t i = 0;i < count;i++)\n            {\n                float time = nif->getFloat();\n                readValue(nifReference, key);\n                mKeys[time] = key;\n            }\n        }\n        else if (mInterpolationType == InterpolationType_Quadratic)\n        {\n            for(size_t i = 0;i < count;i++)\n            {\n                float time = nif->getFloat();\n                readQuadratic(nifReference, key);\n                mKeys[time] = key;\n            }\n        }\n        else if (mInterpolationType == InterpolationType_TBC)\n        {\n            for(size_t i = 0;i < count;i++)\n            {\n                float time = nif->getFloat();\n                readTBC(nifReference, key);\n                mKeys[time] = key;\n            }\n        }\n        //XYZ keys aren't actually read here.\n        //data.hpp sees that the last type read was InterpolationType_XYZ and:\n        //    Eats a floating point number, then\n        //    Re-runs the read function 3 more times.\n        //        When it does that it's reading in a bunch of InterpolationType_Linear keys, not InterpolationType_XYZ.\n        else if(mInterpolationType == InterpolationType_XYZ)\n        {\n            //Don't try to read XYZ keys into the wrong part\n            if ( count != 1 )\n            {\n                std::stringstream error;\n                error << \"XYZ_ROTATION_KEY count should always be '1' .  Retrieved Value: \"\n                      << count;\n                nif->file->fail(error.str());\n            }\n        }\n        else if (mInterpolationType == InterpolationType_Unknown)\n        {\n            if (count != 0)\n                nif->file->fail(\"Interpolation type 0 doesn't work with keys\");\n        }\n        else\n        {\n            std::stringstream error;\n            error << \"Unhandled interpolation type: \" << mInterpolationType;\n            nif->file->fail(error.str());\n        }\n    }\n\nprivate:\n    static void readValue(NIFStream &nif, KeyT<T> &key)\n    {\n        key.mValue = (nif.*getValue)();\n    }\n\n    template <typename U>\n    static void readQuadratic(NIFStream &nif, KeyT<U> &key)\n    {\n        readValue(nif, key);\n        key.mInTan = (nif.*getValue)();\n        key.mOutTan = (nif.*getValue)();\n    }\n\n    static void readQuadratic(NIFStream &nif, KeyT<osg::Quat> &key)\n    {\n        readValue(nif, key);\n    }\n\n    static void readTBC(NIFStream &nif, KeyT<T> &key)\n    {\n        readValue(nif, key);\n        /*key.mTension = */nif.getFloat();\n        /*key.mBias = */nif.getFloat();\n        /*key.mContinuity = */nif.getFloat();\n    }\n};\nusing FloatKeyMap = KeyMapT<float,&NIFStream::getFloat>;\nusing Vector3KeyMap = KeyMapT<osg::Vec3f,&NIFStream::getVector3>;\nusing Vector4KeyMap = KeyMapT<osg::Vec4f,&NIFStream::getVector4>;\nusing QuaternionKeyMap = KeyMapT<osg::Quat,&NIFStream::getQuaternion>;\nusing ByteKeyMap = KeyMapT<char,&NIFStream::getChar>;\n\nusing FloatKeyMapPtr = std::shared_ptr<FloatKeyMap>;\nusing Vector3KeyMapPtr = std::shared_ptr<Vector3KeyMap>;\nusing Vector4KeyMapPtr = std::shared_ptr<Vector4KeyMap>;\nusing QuaternionKeyMapPtr = std::shared_ptr<QuaternionKeyMap>;\nusing ByteKeyMapPtr = std::shared_ptr<ByteKeyMap>;\n\n} // Namespace\n#endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP\n"
  },
  {
    "path": "components/nif/nifstream.cpp",
    "content": "#include \"nifstream.hpp\"\n//For error reporting\n#include \"niffile.hpp\"\n\nnamespace Nif\n{\n    osg::Quat NIFStream::getQuaternion()\n    {\n        float f[4];\n        readLittleEndianBufferOfType<4, float>(inp, f);\n        osg::Quat quat;\n        quat.w() = f[0];\n        quat.x() = f[1];\n        quat.y() = f[2];\n        quat.z() = f[3];\n        return quat;\n    }\n\n    Transformation NIFStream::getTrafo()\n    {\n        Transformation t;\n        t.pos = getVector3();\n        t.rotation = getMatrix3();\n        t.scale = getFloat();\n        return t;\n    }\n\n    ///Booleans in 4.0.0.2 (Morrowind format) and earlier are 4 byte, while in 4.1.0.0+ they're 1 byte.\n    bool NIFStream::getBoolean()\n    {\n        return getVersion() < generateVersion(4,1,0,0) ? getInt() != 0 : getChar() != 0;\n    }\n\n    ///Read in a string, either from the string table using the index or from the stream using the specified length\n    std::string NIFStream::getString()\n    {\n        return getVersion() < generateVersion(20,1,0,1) ? getSizedString() : file->getString(getUInt());\n    }\n\n\n    // Convenience utility functions: get the versions of the currently read file\n    unsigned int NIFStream::getVersion() const { return file->getVersion(); }\n    unsigned int NIFStream::getUserVersion() const { return file->getBethVersion(); }\n    unsigned int NIFStream::getBethVersion() const { return file->getBethVersion(); }\n}\n"
  },
  {
    "path": "components/nif/nifstream.hpp",
    "content": "///Functions used to read raw binary data from .nif files\n\n#ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP\n#define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP\n\n#include <cassert>\n#include <stdint.h>\n#include <stdexcept>\n#include <vector>\n#include <typeinfo>\n#include <type_traits>\n\n#include <components/files/constrainedfilestream.hpp>\n#include <components/misc/endianness.hpp>\n\n#include <osg/Vec3f>\n#include <osg/Vec4f>\n#include <osg/Quat>\n\n#include \"niftypes.hpp\"\n\nnamespace Nif\n{\n\nclass NIFFile;\n\ntemplate <std::size_t numInstances, typename T> inline void readLittleEndianBufferOfType(Files::IStreamPtr &pIStream, T* dest)\n{\n    static_assert(std::is_arithmetic_v<T>, \"Buffer element type is not arithmetic\");\n    pIStream->read((char*)dest, numInstances * sizeof(T));\n    if (pIStream->bad())\n        throw std::runtime_error(\"Failed to read little endian typed (\" + std::string(typeid(T).name()) + \") buffer of \"\n                                 + std::to_string(numInstances) + \" instances\");\n    if constexpr (Misc::IS_BIG_ENDIAN)\n        for (std::size_t i = 0; i < numInstances; i++)\n            Misc::swapEndiannessInplace(dest[i]);\n}\n\ntemplate <typename T> inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr &pIStream, T* dest, std::size_t numInstances)\n{\n    static_assert(std::is_arithmetic_v<T>, \"Buffer element type is not arithmetic\");\n    pIStream->read((char*)dest, numInstances * sizeof(T));\n    if (pIStream->bad())\n        throw std::runtime_error(\"Failed to read little endian dynamic buffer of \" + std::to_string(numInstances) + \" instances\");\n    if constexpr (Misc::IS_BIG_ENDIAN)\n        for (std::size_t i = 0; i < numInstances; i++)\n            Misc::swapEndiannessInplace(dest[i]);\n}\ntemplate<typename type> type inline readLittleEndianType(Files::IStreamPtr &pIStream)\n{\n    type val;\n    readLittleEndianBufferOfType<1, type>(pIStream, &val);\n    return val;\n}\n\nclass NIFStream\n{\n    /// Input stream\n    Files::IStreamPtr inp;\n\npublic:\n\n    NIFFile * const file;\n\n    NIFStream (NIFFile * file, Files::IStreamPtr inp): inp (inp), file (file) {}\n\n    void skip(size_t size) { inp->ignore(size); }\n\n    char getChar()\n    {\n        return readLittleEndianType<char>(inp);\n    }\n\n    short getShort()\n    {\n        return readLittleEndianType<short>(inp);\n    }\n\n    unsigned short getUShort()\n    {\n        return readLittleEndianType<unsigned short>(inp);\n    }\n\n    int getInt()\n    {\n        return readLittleEndianType<int>(inp);\n    }\n\n    unsigned int getUInt()\n    {\n        return readLittleEndianType<unsigned int>(inp);\n    }\n\n    float getFloat()\n    {\n        return readLittleEndianType<float>(inp);\n    }\n\n    osg::Vec2f getVector2()\n    {\n        osg::Vec2f vec;\n        readLittleEndianBufferOfType<2,float>(inp, vec._v);\n        return vec;\n    }\n\n    osg::Vec3f getVector3()\n    {\n        osg::Vec3f vec;\n        readLittleEndianBufferOfType<3, float>(inp, vec._v);\n        return vec;\n    }\n\n    osg::Vec4f getVector4()\n    {\n        osg::Vec4f vec;\n        readLittleEndianBufferOfType<4, float>(inp, vec._v);\n        return vec;\n    }\n\n    Matrix3 getMatrix3()\n    {\n        Matrix3 mat;\n        readLittleEndianBufferOfType<9, float>(inp, (float*)&mat.mValues);\n        return mat;\n    }\n\n    osg::Quat getQuaternion();\n\n    Transformation getTrafo();\n\n    bool getBoolean();\n\n    std::string getString();\n\n    unsigned int getVersion() const;\n    unsigned int getUserVersion() const;\n    unsigned int getBethVersion() const;\n\n    // Convert human-readable version numbers into a number that can be compared.\n    static constexpr uint32_t generateVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t rev)\n    {\n        return (major << 24) + (minor << 16) + (patch << 8) + rev;\n    }\n\n    ///Read in a string of the given length\n    std::string getSizedString(size_t length)\n    {\n        std::string str(length, '\\0');\n        inp->read(str.data(), length);\n        if (inp->bad())\n            throw std::runtime_error(\"Failed to read sized string of \" + std::to_string(length) + \" chars\");\n        return str;\n    }\n    ///Read in a string of the length specified in the file\n    std::string getSizedString()\n    {\n        size_t size = readLittleEndianType<uint32_t>(inp);\n        return getSizedString(size);\n    }\n\n    ///Specific to Bethesda headers, uses a byte for length\n    std::string getExportString()\n    {\n        size_t size = static_cast<size_t>(readLittleEndianType<uint8_t>(inp));\n        return getSizedString(size);\n    }\n\n    ///This is special since the version string doesn't start with a number, and ends with \"\\n\"\n    std::string getVersionString()\n    {\n        std::string result;\n        std::getline(*inp, result);\n        if (inp->bad())\n            throw std::runtime_error(\"Failed to read version string\");\n        return result;\n    }\n\n    void getChars(std::vector<char> &vec, size_t size)\n    {\n        vec.resize(size);\n        readLittleEndianDynamicBufferOfType<char>(inp, vec.data(), size);\n    }\n\n    void getUChars(std::vector<unsigned char> &vec, size_t size)\n    {\n        vec.resize(size);\n        readLittleEndianDynamicBufferOfType<unsigned char>(inp, vec.data(), size);\n    }\n\n    void getUShorts(std::vector<unsigned short> &vec, size_t size)\n    {\n        vec.resize(size);\n        readLittleEndianDynamicBufferOfType<unsigned short>(inp, vec.data(), size);\n    }\n\n    void getFloats(std::vector<float> &vec, size_t size)\n    {\n        vec.resize(size);\n        readLittleEndianDynamicBufferOfType<float>(inp, vec.data(), size);\n    }\n\n    void getInts(std::vector<int> &vec, size_t size)\n    {\n        vec.resize(size);\n        readLittleEndianDynamicBufferOfType<int>(inp, vec.data(), size);\n    }\n\n    void getUInts(std::vector<unsigned int> &vec, size_t size)\n    {\n        vec.resize(size);\n        readLittleEndianDynamicBufferOfType<unsigned int>(inp, vec.data(), size);\n    }\n\n    void getVector2s(std::vector<osg::Vec2f> &vec, size_t size)\n    {\n        vec.resize(size);\n        /* The packed storage of each Vec2f is 2 floats exactly */\n        readLittleEndianDynamicBufferOfType<float>(inp,(float*)vec.data(), size*2);\n    }\n\n    void getVector3s(std::vector<osg::Vec3f> &vec, size_t size)\n    {\n        vec.resize(size);\n        /* The packed storage of each Vec3f is 3 floats exactly */\n        readLittleEndianDynamicBufferOfType<float>(inp, (float*)vec.data(), size*3);\n    }\n\n    void getVector4s(std::vector<osg::Vec4f> &vec, size_t size)\n    {\n        vec.resize(size);\n        /* The packed storage of each Vec4f is 4 floats exactly */\n        readLittleEndianDynamicBufferOfType<float>(inp, (float*)vec.data(), size*4);\n    }\n\n    void getQuaternions(std::vector<osg::Quat> &quat, size_t size)\n    {\n        quat.resize(size);\n        for (size_t i = 0;i < quat.size();i++)\n            quat[i] = getQuaternion();\n    }\n\n    void getStrings(std::vector<std::string> &vec, size_t size)\n    {\n        vec.resize(size);\n        for (size_t i = 0; i < vec.size(); i++)\n            vec[i] = getString();\n    }\n    /// We need to use this when the string table isn't actually initialized.\n    void getSizedStrings(std::vector<std::string> &vec, size_t size)\n    {\n        vec.resize(size);\n        for (size_t i = 0; i < vec.size(); i++)\n            vec[i] = getSizedString();\n    }\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/nif/niftypes.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (nif_types.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef OPENMW_COMPONENTS_NIF_NIFTYPES_HPP\n#define OPENMW_COMPONENTS_NIF_NIFTYPES_HPP\n\n#include <osg/Vec3f>\n#include <osg/Matrixf>\n\n// Common types used in NIF files\n\nnamespace Nif\n{\n\nstruct Matrix3\n{\n    float mValues[3][3];\n\n    Matrix3()\n    {\n        for (int i=0;i<3;++i)\n            for (int j=0;j<3;++j)\n                mValues[i][j] = (i==j) ? 1.f : 0.f;\n    }\n\n    bool isIdentity() const\n    {\n        for (int i=0;i<3;++i)\n            for (int j=0;j<3;++j)\n                if ((i==j) != (mValues[i][j] == 1))\n                    return false;\n        return true;\n    }\n};\n\nstruct Transformation\n{\n    osg::Vec3f pos;\n    Matrix3 rotation; // this can contain scale components too, including negative and nonuniform scales\n    float scale;\n\n    osg::Matrixf toMatrix() const\n    {\n        osg::Matrixf transform;\n        transform.setTrans(pos);\n\n        for (int i=0;i<3;++i)\n            for (int j=0;j<3;++j)\n                transform(j,i) = rotation.mValues[i][j] * scale; // NB column/row major difference\n\n        return transform;\n    }\n\n    bool isIdentity() const\n    {\n        return pos == osg::Vec3f(0,0,0)\n                && rotation.isIdentity() && scale == 1.f;\n    }\n\n    static const Transformation& getIdentity()\n    {\n        static const Transformation identity = {\n            osg::Vec3f(), Matrix3(), 1.0f\n        };\n        return identity;\n    }\n};\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/node.cpp",
    "content": ""
  },
  {
    "path": "components/nif/node.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_NIF_NODE_HPP\n#define OPENMW_COMPONENTS_NIF_NODE_HPP\n\n#include \"controlled.hpp\"\n#include \"extra.hpp\"\n#include \"data.hpp\"\n#include \"property.hpp\"\n#include \"niftypes.hpp\"\n#include \"controller.hpp\"\n#include \"base.hpp\"\n\n#include <components/misc/stringops.hpp>\n\nnamespace Nif\n{\n\nstruct NiNode;\n\nstruct NiBoundingVolume\n{\n    enum Type\n    {\n        SPHERE_BV = 0,\n        BOX_BV = 1,\n        CAPSULE_BV = 2,\n        LOZENGE_BV = 3,\n        UNION_BV = 4,\n        HALFSPACE_BV = 5\n    };\n\n    struct NiSphereBV\n    {\n        osg::Vec3f center;\n        float radius{0.f};\n    };\n\n    struct NiBoxBV\n    {\n        osg::Vec3f center;\n        Matrix3 axis;\n        osg::Vec3f extents;\n    };\n\n    struct NiCapsuleBV\n    {\n        osg::Vec3f center, axis;\n        float extent{0.f}, radius{0.f};\n    };\n\n    struct NiLozengeBV\n    {\n        float radius{0.f}, extent0{0.f}, extent1{0.f};\n        osg::Vec3f center, axis0, axis1;\n    };\n\n    struct NiHalfSpaceBV\n    {\n        osg::Vec3f center, normal;\n    };\n\n    unsigned int type;\n    NiSphereBV sphere;\n    NiBoxBV box;\n    NiCapsuleBV capsule;\n    NiLozengeBV lozenge;\n    std::vector<NiBoundingVolume> children;\n    NiHalfSpaceBV plane;\n    void read(NIFStream* nif)\n    {\n        type = nif->getUInt();\n        switch (type)\n        {\n            case SPHERE_BV:\n            {\n                sphere.center = nif->getVector3();\n                sphere.radius = nif->getFloat();\n                break;\n            }\n            case BOX_BV:\n            {\n                box.center = nif->getVector3();\n                box.axis = nif->getMatrix3();\n                box.extents = nif->getVector3();\n                break;\n            }\n            case CAPSULE_BV:\n            {\n                capsule.center = nif->getVector3();\n                capsule.axis = nif->getVector3();\n                capsule.extent = nif->getFloat();\n                capsule.radius = nif->getFloat();\n                break;\n            }\n            case LOZENGE_BV:\n            {\n                lozenge.radius = nif->getFloat();\n                lozenge.extent0 = nif->getFloat();\n                lozenge.extent1 = nif->getFloat();\n                lozenge.center = nif->getVector3();\n                lozenge.axis0 = nif->getVector3();\n                lozenge.axis1 = nif->getVector3();\n                break;\n            }\n            case UNION_BV:\n            {\n                unsigned int numChildren = nif->getUInt();\n                if (numChildren == 0)\n                    break;\n                children.resize(numChildren);\n                for (NiBoundingVolume& child : children)\n                    child.read(nif);\n                break;\n            }\n            case HALFSPACE_BV:\n            {\n                plane.center = nif->getVector3();\n                plane.normal = nif->getVector3();\n                break;\n            }\n            default:\n            {\n                std::stringstream error;\n                error << \"Unhandled NiBoundingVolume type: \" << type;\n                nif->file->fail(error.str());\n            }\n        }\n    }\n};\n\n/** A Node is an object that's part of the main NIF tree. It has\n    parent node (unless it's the root), and transformation (location\n    and rotation) relative to it's parent.\n */\nstruct Node : public Named\n{\n    // Node flags. Interpretation depends somewhat on the type of node.\n    unsigned int flags;\n    Transformation trafo;\n    osg::Vec3f velocity; // Unused? Might be a run-time game state\n    PropertyList props;\n\n    // Bounding box info\n    bool hasBounds{false};\n    NiBoundingVolume bounds;\n\n    void read(NIFStream *nif) override\n    {\n        Named::read(nif);\n\n        flags = nif->getBethVersion() <= 26 ? nif->getUShort() : nif->getUInt();\n        trafo = nif->getTrafo();\n        if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))\n            velocity = nif->getVector3();\n        if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)\n            props.read(nif);\n\n        if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))\n            hasBounds = nif->getBoolean();\n        if (hasBounds)\n            bounds.read(nif);\n        // Reference to the collision object in Gamebryo files.\n        if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))\n            nif->skip(4);\n\n        parent = nullptr;\n\n        isBone = false;\n    }\n\n    void post(NIFFile *nif) override\n    {\n        Named::post(nif);\n        props.post(nif);\n    }\n\n    // Parent node, or nullptr for the root node. As far as I'm aware, only\n    // NiNodes (or types derived from NiNodes) can be parents.\n    NiNode *parent;\n\n    bool isBone{false};\n\n    void setBone()\n    {\n        isBone = true;\n    }\n};\n\nstruct NiNode : Node\n{\n    NodeList children;\n    NodeList effects;\n\n    enum Flags {\n        Flag_Hidden = 0x0001,\n        Flag_MeshCollision = 0x0002,\n        Flag_BBoxCollision = 0x0004,\n        Flag_ActiveCollision = 0x0020\n    };\n    enum BSAnimFlags {\n        AnimFlag_AutoPlay = 0x0020\n    };\n    enum BSParticleFlags {\n        ParticleFlag_AutoPlay = 0x0020,\n        ParticleFlag_LocalSpace = 0x0080\n    };\n    enum ControllerFlags {\n        ControllerFlag_Active = 0x8\n    };\n\n    void read(NIFStream *nif) override\n    {\n        Node::read(nif);\n        children.read(nif);\n        if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)\n            effects.read(nif);\n\n        // Discard transformations for the root node, otherwise some meshes\n        // occasionally get wrong orientation. Only for NiNode-s for now, but\n        // can be expanded if needed.\n        if (0 == recIndex && !Misc::StringUtils::ciEqual(name, \"bip01\"))\n        {\n            static_cast<Nif::Node*>(this)->trafo = Nif::Transformation::getIdentity();\n        }\n    }\n\n    void post(NIFFile *nif) override\n    {\n        Node::post(nif);\n        children.post(nif);\n        effects.post(nif);\n\n        for(size_t i = 0;i < children.length();i++)\n        {\n            // Why would a unique list of children contain empty refs?\n            if(!children[i].empty())\n                children[i]->parent = this;\n        }\n    }\n};\n\nstruct NiGeometry : Node\n{\n    /* Possible flags:\n        0x40 - mesh has no vertex normals ?\n\n        Only flags included in 0x47 (ie. 0x01, 0x02, 0x04 and 0x40) have\n        been observed so far.\n    */\n\n    struct MaterialData\n    {\n        std::vector<std::string> names;\n        std::vector<int> extra;\n        unsigned int active{0};\n        bool needsUpdate{false};\n        void read(NIFStream *nif)\n        {\n            if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))\n                return;\n            unsigned int num = 0;\n            if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,3))\n                num = nif->getBoolean(); // Has Shader\n            else if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))\n                num = nif->getUInt();\n            if (num)\n            {\n                nif->getStrings(names, num);\n                nif->getInts(extra, num);\n            }\n            if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))\n                active = nif->getUInt();\n            if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)\n                needsUpdate = nif->getBoolean();\n        }\n    };\n\n    NiGeometryDataPtr data;\n    NiSkinInstancePtr skin;\n    MaterialData material;\n    BSShaderPropertyPtr shaderprop;\n    NiAlphaPropertyPtr alphaprop;\n\n    void read(NIFStream *nif) override\n    {\n        Node::read(nif);\n        data.read(nif);\n        skin.read(nif);\n        material.read(nif);\n        if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)\n        {\n            shaderprop.read(nif);\n            alphaprop.read(nif);\n        }\n    }\n\n    void post(NIFFile *nif) override\n    {\n        Node::post(nif);\n        data.post(nif);\n        skin.post(nif);\n        shaderprop.post(nif);\n        alphaprop.post(nif);\n        if (recType != RC_NiParticles && !skin.empty())\n            nif->setUseSkinning(true);\n    }\n};\n\nstruct NiTriShape : NiGeometry {};\nstruct BSLODTriShape : NiTriShape\n{\n    unsigned int lod0, lod1, lod2;\n    void read(NIFStream *nif) override\n    {\n        NiTriShape::read(nif);\n        lod0 = nif->getUInt();\n        lod1 = nif->getUInt();\n        lod2 = nif->getUInt();\n    }\n};\nstruct NiTriStrips : NiGeometry {};\nstruct NiLines : NiGeometry {};\nstruct NiParticles : NiGeometry { };\n\nstruct NiCamera : Node\n{\n    struct Camera\n    {\n        unsigned short cameraFlags{0};\n\n        // Camera frustrum\n        float left, right, top, bottom, nearDist, farDist;\n\n        // Viewport\n        float vleft, vright, vtop, vbottom;\n\n        // Level of detail modifier\n        float LOD;\n\n        // Orthographic projection usage flag\n        bool orthographic{false};\n\n        void read(NIFStream *nif)\n        {\n            if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n                cameraFlags = nif->getUShort();\n            left = nif->getFloat();\n            right = nif->getFloat();\n            top = nif->getFloat();\n            bottom = nif->getFloat();\n            nearDist = nif->getFloat();\n            farDist = nif->getFloat();\n            if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n                orthographic = nif->getBoolean();\n            vleft = nif->getFloat();\n            vright = nif->getFloat();\n            vtop = nif->getFloat();\n            vbottom = nif->getFloat();\n\n            LOD = nif->getFloat();\n        }\n    };\n    Camera cam;\n\n    void read(NIFStream *nif) override\n    {\n        Node::read(nif);\n\n        cam.read(nif);\n\n        nif->getInt(); // -1\n        nif->getInt(); // 0\n        if (nif->getVersion() >= NIFStream::generateVersion(4,2,1,0))\n            nif->getInt(); // 0\n    }\n};\n\n// A node used as the base to switch between child nodes, such as for LOD levels.\nstruct NiSwitchNode : public NiNode\n{\n    unsigned int switchFlags{0};\n    unsigned int initialIndex{0};\n\n    void read(NIFStream *nif) override\n    {\n        NiNode::read(nif);\n        if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n            switchFlags = nif->getUShort();\n        initialIndex = nif->getUInt();\n    }\n};\n\nstruct NiLODNode : public NiSwitchNode\n{\n    osg::Vec3f lodCenter;\n\n    struct LODRange\n    {\n        float minRange;\n        float maxRange;\n    };\n    std::vector<LODRange> lodLevels;\n\n    void read(NIFStream *nif) override\n    {\n        NiSwitchNode::read(nif);\n        if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))\n            lodCenter = nif->getVector3();\n        else if (nif->getVersion() > NIFStream::generateVersion(10,0,1,0))\n        {\n            nif->skip(4); // NiLODData, unsupported at the moment\n            return;\n        }\n\n        unsigned int numLodLevels = nif->getUInt();\n        for (unsigned int i=0; i<numLodLevels; ++i)\n        {\n            LODRange r;\n            r.minRange = nif->getFloat();\n            r.maxRange = nif->getFloat();\n            lodLevels.push_back(r);\n        }\n    }\n};\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/property.cpp",
    "content": "#include \"property.hpp\"\n\n#include \"data.hpp\"\n#include \"controlled.hpp\"\n\nnamespace Nif\n{\n\nvoid NiTexturingProperty::Texture::read(NIFStream *nif)\n{\n    inUse = nif->getBoolean();\n    if(!inUse) return;\n\n    texture.read(nif);\n    if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)\n    {\n        clamp = nif->getInt();\n        nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible\n    }\n    else\n    {\n        clamp = nif->getUShort() & 0xF;\n    }\n    // Max anisotropy. I assume we'll always only use the global anisotropy setting.\n    if (nif->getVersion() >= NIFStream::generateVersion(20,5,0,4))\n        nif->getUShort();\n\n    if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)\n        uvSet = nif->getUInt();\n\n    // Two PS2-specific shorts.\n    if (nif->getVersion() < NIFStream::generateVersion(10,4,0,2))\n        nif->skip(4);\n    if (nif->getVersion() <= NIFStream::generateVersion(4,1,0,18))\n        nif->skip(2); // Unknown short\n    else if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))\n    {\n        if (nif->getBoolean()) // Has texture transform\n        {\n            nif->getVector2(); // UV translation\n            nif->getVector2(); // UV scale\n            nif->getFloat(); // W axis rotation\n            nif->getUInt(); // Transform method\n            nif->getVector2(); // Texture rotation origin\n        }\n    }\n}\n\nvoid NiTexturingProperty::Texture::post(NIFFile *nif)\n{\n    texture.post(nif);\n}\n\nvoid NiTexturingProperty::read(NIFStream *nif)\n{\n    Property::read(nif);\n    if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD || nif->getVersion() >= NIFStream::generateVersion(20,1,0,2))\n        flags = nif->getUShort();\n    if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,1))\n        apply = nif->getUInt();\n\n    unsigned int numTextures = nif->getUInt();\n\n    if (!numTextures)\n        return;\n\n    textures.resize(numTextures);\n    for (unsigned int i = 0; i < numTextures; i++)\n    {\n        textures[i].read(nif);\n        if (i == 5 && textures[5].inUse) // Bump map settings\n        {\n            envMapLumaBias = nif->getVector2();\n            bumpMapMatrix = nif->getVector4();\n        }\n        else if (i == 7 && textures[7].inUse && nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))\n            /*float parallaxOffset = */nif->getFloat();\n    }\n\n    if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))\n    {\n        unsigned int numShaderTextures = nif->getUInt();\n        shaderTextures.resize(numShaderTextures);\n        for (unsigned int i = 0; i < numShaderTextures; i++)\n        {\n            shaderTextures[i].read(nif);\n            if (shaderTextures[i].inUse)\n                nif->getUInt(); // Unique identifier\n        }\n    }\n}\n\nvoid NiTexturingProperty::post(NIFFile *nif)\n{\n    Property::post(nif);\n    for (size_t i = 0; i < textures.size(); i++)\n        textures[i].post(nif);\n    for (size_t i = 0; i < shaderTextures.size(); i++)\n        shaderTextures[i].post(nif);\n}\n\nvoid BSShaderProperty::read(NIFStream *nif)\n{\n    NiShadeProperty::read(nif);\n    if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)\n    {\n        type = nif->getUInt();\n        flags1 = nif->getUInt();\n        flags2 = nif->getUInt();\n        envMapIntensity = nif->getFloat();\n    }\n}\n\nvoid BSShaderLightingProperty::read(NIFStream *nif)\n{\n    BSShaderProperty::read(nif);\n    if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)\n        clamp = nif->getUInt();\n}\n\nvoid BSShaderPPLightingProperty::read(NIFStream *nif)\n{\n    BSShaderLightingProperty::read(nif);\n    textureSet.read(nif);\n    if (nif->getBethVersion() <= 14)\n        return;\n    refraction.strength = nif->getFloat();\n    refraction.period = nif->getInt();\n    if (nif->getBethVersion() <= 24)\n        return;\n    parallax.passes = nif->getFloat();\n    parallax.scale = nif->getFloat();\n}\n\nvoid BSShaderPPLightingProperty::post(NIFFile *nif)\n{\n    BSShaderLightingProperty::post(nif);\n    textureSet.post(nif);\n}\n\nvoid BSShaderNoLightingProperty::read(NIFStream *nif)\n{\n    BSShaderLightingProperty::read(nif);\n    filename = nif->getSizedString();\n    if (nif->getBethVersion() >= 27)\n        falloffParams = nif->getVector4();\n}\n\nvoid NiFogProperty::read(NIFStream *nif)\n{\n    Property::read(nif);\n    mFlags = nif->getUShort();\n    mFogDepth = nif->getFloat();\n    mColour = nif->getVector3();\n}\n\nvoid S_MaterialProperty::read(NIFStream *nif)\n{\n    if (nif->getBethVersion() < 26)\n    {\n        ambient = nif->getVector3();\n        diffuse = nif->getVector3();\n    }\n    specular = nif->getVector3();\n    emissive = nif->getVector3();\n    glossiness = nif->getFloat();\n    alpha = nif->getFloat();\n    if (nif->getBethVersion() >= 22)\n        emissiveMult = nif->getFloat();\n}\n\nvoid S_VertexColorProperty::read(NIFStream *nif)\n{\n    vertmode = nif->getInt();\n    lightmode = nif->getInt();\n}\n\nvoid S_AlphaProperty::read(NIFStream *nif)\n{\n    threshold = nif->getChar();\n}\n\nvoid S_StencilProperty::read(NIFStream *nif)\n{\n    if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)\n    {\n        enabled = nif->getChar();\n        compareFunc = nif->getInt();\n        stencilRef = nif->getUInt();\n        stencilMask = nif->getUInt();\n        failAction = nif->getInt();\n        zFailAction = nif->getInt();\n        zPassAction = nif->getInt();\n        drawMode = nif->getInt();\n    }\n    else\n    {\n        unsigned short flags = nif->getUShort();\n        enabled = flags & 0x1;\n        failAction = (flags >> 1) & 0x7;\n        zFailAction = (flags >> 4) & 0x7;\n        zPassAction = (flags >> 7) & 0x7;\n        drawMode = (flags >> 10) & 0x3;\n        compareFunc = (flags >> 12) & 0x7;\n        stencilRef = nif->getUInt();\n        stencilMask = nif->getUInt();\n    }\n}\n\n\n\n}\n"
  },
  {
    "path": "components/nif/property.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (property.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef OPENMW_COMPONENTS_NIF_PROPERTY_HPP\n#define OPENMW_COMPONENTS_NIF_PROPERTY_HPP\n\n#include \"base.hpp\"\n\nnamespace Nif\n{\n\nstruct Property : public Named { };\n\nstruct NiTexturingProperty : public Property\n{\n    unsigned short flags{0u};\n\n    // A sub-texture\n    struct Texture\n    {\n        /* Clamp mode\n        0 - clampS clampT\n        1 - clampS wrapT\n        2 - wrapS clampT\n        3 - wrapS wrapT\n        */\n\n        bool inUse;\n        NiSourceTexturePtr texture;\n\n        unsigned int clamp, uvSet;\n\n        void read(NIFStream *nif);\n        void post(NIFFile *nif);\n    };\n\n    /* Apply mode:\n        0 - replace\n        1 - decal\n        2 - modulate\n        3 - hilight  // These two are for PS2 only?\n        4 - hilight2\n    */\n    unsigned int apply{0};\n\n    /*\n     * The textures in this list are as follows:\n     *\n     * 0 - Base texture\n     * 1 - Dark texture\n     * 2 - Detail texture\n     * 3 - Gloss texture\n     * 4 - Glow texture\n     * 5 - Bump map texture\n     * 6 - Decal texture\n     */\n    enum TextureType\n    {\n        BaseTexture = 0,\n        DarkTexture = 1,\n        DetailTexture = 2,\n        GlossTexture = 3,\n        GlowTexture = 4,\n        BumpTexture = 5,\n        DecalTexture = 6,\n    };\n\n    std::vector<Texture> textures;\n    std::vector<Texture> shaderTextures;\n\n    osg::Vec2f envMapLumaBias;\n    osg::Vec4f bumpMapMatrix;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct NiFogProperty : public Property\n{\n    unsigned short mFlags;\n    float mFogDepth;\n    osg::Vec3f mColour;\n\n    void read(NIFStream *nif) override;\n};\n\n// These contain no other data than the 'flags' field\nstruct NiShadeProperty : public Property\n{\n    unsigned short flags{0u};\n    void read(NIFStream *nif) override\n    {\n        Property::read(nif);\n        if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)\n            flags = nif->getUShort();\n    }\n};\n\nstruct BSShaderProperty : public NiShadeProperty\n{\n    enum BSShaderType\n    {\n        SHADER_TALL_GRASS = 0,\n        SHADER_DEFAULT = 1,\n        SHADER_SKY = 10,\n        SHADER_SKIN = 14,\n        SHADER_WATER = 17,\n        SHADER_LIGHTING30 = 29,\n        SHADER_TILE = 32,\n        SHADER_NOLIGHTING = 33\n    };\n\n    unsigned int type{0u}, flags1{0u}, flags2{0u};\n    float envMapIntensity{0.f};\n    void read(NIFStream *nif) override;\n};\n\nstruct BSShaderLightingProperty : public BSShaderProperty\n{\n    unsigned int clamp{0u};\n    void read(NIFStream *nif) override;\n};\n\nstruct BSShaderPPLightingProperty : public BSShaderLightingProperty\n{\n    BSShaderTextureSetPtr textureSet;\n    struct RefractionSettings\n    {\n        float strength{0.f};\n        int period{0};\n    };\n    struct ParallaxSettings\n    {\n        float passes{0.f};\n        float scale{0.f};\n    };\n    RefractionSettings refraction;\n    ParallaxSettings parallax;\n\n    void read(NIFStream *nif) override;\n    void post(NIFFile *nif) override;\n};\n\nstruct BSShaderNoLightingProperty : public BSShaderLightingProperty\n{\n    std::string filename;\n    osg::Vec4f falloffParams;\n\n    void read(NIFStream *nif) override;\n};\n\nstruct NiDitherProperty : public Property\n{\n    unsigned short flags;\n    void read(NIFStream* nif) override\n    {\n        Property::read(nif);\n        flags = nif->getUShort();\n    }\n};\n\nstruct NiZBufferProperty : public Property\n{\n    unsigned short flags;\n    unsigned int testFunction;\n    void read(NIFStream *nif) override\n    {\n        Property::read(nif);\n        flags = nif->getUShort();\n        testFunction = (flags >> 2) & 0x7;\n        if (nif->getVersion() >= NIFStream::generateVersion(4,1,0,12) && nif->getVersion() <= NIFFile::NIFVersion::VER_OB)\n            testFunction = nif->getUInt();\n    }\n};\n\nstruct NiSpecularProperty : public Property\n{\n    unsigned short flags;\n    void read(NIFStream* nif) override\n    {\n        Property::read(nif);\n        flags = nif->getUShort();\n    }\n};\n\nstruct NiWireframeProperty : public Property\n{\n    unsigned short flags;\n    void read(NIFStream* nif) override\n    {\n        Property::read(nif);\n        flags = nif->getUShort();\n    }\n};\n\n// The rest are all struct-based\ntemplate <typename T>\nstruct StructPropT : Property\n{\n    T data;\n    unsigned short flags;\n\n    void read(NIFStream *nif) override\n    {\n        Property::read(nif);\n        flags = nif->getUShort();\n        data.read(nif);\n    }\n};\n\nstruct S_MaterialProperty\n{\n    // The vector components are R,G,B\n    osg::Vec3f ambient{1.f,1.f,1.f}, diffuse{1.f,1.f,1.f};\n    osg::Vec3f specular, emissive;\n    float glossiness{0.f}, alpha{0.f}, emissiveMult{1.f};\n\n    void read(NIFStream *nif);\n};\n\nstruct S_VertexColorProperty\n{\n    /* Vertex mode:\n        0 - source ignore\n        1 - source emmisive\n        2 - source amb diff\n\n        Lighting mode\n        0 - lighting emmisive\n        1 - lighting emmisive ambient/diffuse\n    */\n    int vertmode, lightmode;\n\n    void read(NIFStream *nif);\n};\n\nstruct S_AlphaProperty\n{\n    /*\n        In NiAlphaProperty, the flags have the following meaning:\n\n        Bit 0 : alpha blending enable\n        Bits 1-4 : source blend mode\n        Bits 5-8 : destination blend mode\n        Bit 9 : alpha test enable\n        Bit 10-12 : alpha test mode\n        Bit 13 : no sorter flag ( disables triangle sorting )\n\n        blend modes (glBlendFunc):\n        0000 GL_ONE\n        0001 GL_ZERO\n        0010 GL_SRC_COLOR\n        0011 GL_ONE_MINUS_SRC_COLOR\n        0100 GL_DST_COLOR\n        0101 GL_ONE_MINUS_DST_COLOR\n        0110 GL_SRC_ALPHA\n        0111 GL_ONE_MINUS_SRC_ALPHA\n        1000 GL_DST_ALPHA\n        1001 GL_ONE_MINUS_DST_ALPHA\n        1010 GL_SRC_ALPHA_SATURATE\n\n        test modes (glAlphaFunc):\n        000 GL_ALWAYS\n        001 GL_LESS\n        010 GL_EQUAL\n        011 GL_LEQUAL\n        100 GL_GREATER\n        101 GL_NOTEQUAL\n        110 GL_GEQUAL\n        111 GL_NEVER\n\n        Taken from:\n        http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html\n    */\n\n    // Tested against when certain flags are set (see above.)\n    unsigned char threshold;\n\n    void read(NIFStream *nif);\n};\n\n/*\n    Docs taken from:\n    http://niftools.sourceforge.net/doc/nif/NiStencilProperty.html\n */\nstruct S_StencilProperty\n{\n    // Is stencil test enabled?\n    unsigned char enabled;\n\n    /*\n        0   TEST_NEVER\n        1   TEST_LESS\n        2   TEST_EQUAL\n        3   TEST_LESS_EQUAL\n        4   TEST_GREATER\n        5   TEST_NOT_EQUAL\n        6   TEST_GREATER_EQUAL\n        7   TEST_NEVER (though nifskope comment says TEST_ALWAYS, but ingame it is TEST_NEVER)\n     */\n    int compareFunc;\n    unsigned stencilRef;\n    unsigned stencilMask;\n    /*\n        Stencil test fail action, depth test fail action and depth test pass action:\n        0   ACTION_KEEP\n        1   ACTION_ZERO\n        2   ACTION_REPLACE\n        3   ACTION_INCREMENT\n        4   ACTION_DECREMENT\n        5   ACTION_INVERT\n     */\n    int failAction;\n    int zFailAction;\n    int zPassAction;\n    /*\n        Face draw mode:\n        0   DRAW_CCW_OR_BOTH\n        1   DRAW_CCW        [default]\n        2   DRAW_CW\n        3   DRAW_BOTH\n     */\n    int drawMode;\n\n    void read(NIFStream *nif);\n};\n\nstruct NiAlphaProperty : public StructPropT<S_AlphaProperty> { };\nstruct NiVertexColorProperty : public StructPropT<S_VertexColorProperty> { };\nstruct NiStencilProperty : public Property\n{\n    S_StencilProperty data;\n    unsigned short flags{0u};\n\n    void read(NIFStream *nif) override\n    {\n        Property::read(nif);\n        if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)\n            flags = nif->getUShort();\n        data.read(nif);\n    }\n};\n\nstruct NiMaterialProperty : public Property\n{\n    S_MaterialProperty data;\n    unsigned short flags{0u};\n\n    void read(NIFStream *nif) override\n    {\n        Property::read(nif);\n        if (nif->getVersion() >= NIFStream::generateVersion(3,0,0,0)\n         && nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)\n            flags = nif->getUShort();\n        data.read(nif);\n    }\n};\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/record.hpp",
    "content": "/*\n  OpenMW - The completely unofficial reimplementation of Morrowind\n  Copyright (C) 2008-2010  Nicolay Korslund\n  Email: < korslund@gmail.com >\n  WWW: https://openmw.org/\n\n  This file (record.h) is part of the OpenMW package.\n\n  OpenMW is distributed as free software: you can redistribute it\n  and/or modify it under the terms of the GNU General Public License\n  version 3, as published by the Free Software Foundation.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  version 3 along with this program. If not, see\n  https://www.gnu.org/licenses/ .\n\n */\n\n#ifndef OPENMW_COMPONENTS_NIF_RECORD_HPP\n#define OPENMW_COMPONENTS_NIF_RECORD_HPP\n\n#include <string>\n\nnamespace Nif\n{\n\nclass NIFFile;\nclass NIFStream;\n\nenum RecordType\n{\n  RC_MISSING = 0,\n  RC_NiNode,\n  RC_NiSwitchNode,\n  RC_NiLODNode,\n  RC_NiBillboardNode,\n  RC_AvoidNode,\n  RC_NiCollisionSwitch,\n  RC_NiTriShape,\n  RC_NiTriStrips,\n  RC_NiLines,\n  RC_NiParticles,\n  RC_NiBSParticleNode,\n  RC_NiCamera,\n  RC_NiTexturingProperty,\n  RC_NiFogProperty,\n  RC_NiMaterialProperty,\n  RC_NiZBufferProperty,\n  RC_NiAlphaProperty,\n  RC_NiVertexColorProperty,\n  RC_NiShadeProperty,\n  RC_NiDitherProperty,\n  RC_NiWireframeProperty,\n  RC_NiSpecularProperty,\n  RC_NiStencilProperty,\n  RC_NiVisController,\n  RC_NiGeomMorpherController,\n  RC_NiKeyframeController,\n  RC_NiAlphaController,\n  RC_NiRollController,\n  RC_NiUVController,\n  RC_NiPathController,\n  RC_NiMaterialColorController,\n  RC_NiBSPArrayController,\n  RC_NiParticleSystemController,\n  RC_NiFlipController,\n  RC_NiBSAnimationNode,\n  RC_NiLight,\n  RC_NiTextureEffect,\n  RC_NiVertWeightsExtraData,\n  RC_NiTextKeyExtraData,\n  RC_NiStringExtraData,\n  RC_NiGravity,\n  RC_NiPlanarCollider,\n  RC_NiParticleGrowFade,\n  RC_NiParticleColorModifier,\n  RC_NiParticleRotation,\n  RC_NiFloatData,\n  RC_NiTriShapeData,\n  RC_NiTriStripsData,\n  RC_NiLinesData,\n  RC_NiVisData,\n  RC_NiColorData,\n  RC_NiPixelData,\n  RC_NiMorphData,\n  RC_NiKeyframeData,\n  RC_NiSkinData,\n  RC_NiUVData,\n  RC_NiPosData,\n  RC_NiRotatingParticlesData,\n  RC_NiParticlesData,\n  RC_NiSequenceStreamHelper,\n  RC_NiSourceTexture,\n  RC_NiSkinInstance,\n  RC_RootCollisionNode,\n  RC_NiSphericalCollider,\n  RC_NiLookAtController,\n  RC_NiPalette,\n  RC_NiIntegerExtraData,\n  RC_NiIntegersExtraData,\n  RC_NiBinaryExtraData,\n  RC_NiBooleanExtraData,\n  RC_NiVectorExtraData,\n  RC_NiColorExtraData,\n  RC_NiFloatExtraData,\n  RC_NiFloatsExtraData,\n  RC_NiStringPalette,\n  RC_NiBoolData,\n  RC_NiSkinPartition,\n  RC_BSXFlags,\n  RC_BSBound,\n  RC_bhkBlendController,\n  RC_NiFloatInterpolator,\n  RC_NiPoint3Interpolator,\n  RC_NiBoolInterpolator,\n  RC_NiTransformInterpolator,\n  RC_NiColorInterpolator,\n  RC_BSShaderTextureSet,\n  RC_BSLODTriShape,\n  RC_BSShaderProperty,\n  RC_BSShaderPPLightingProperty,\n  RC_BSShaderNoLightingProperty\n};\n\n/// Base class for all records\nstruct Record\n{\n    // Record type and type name\n    int recType{RC_MISSING};\n    std::string recName;\n    unsigned int recIndex{~0u};\n\n    Record() = default;\n\n    /// Parses the record from file\n    virtual void read(NIFStream *nif) = 0;\n\n    /// Does post-processing, after the entire tree is loaded\n    virtual void post(NIFFile *nif) {}\n\n    virtual ~Record() {}\n};\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nif/recordptr.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_NIF_RECORDPTR_HPP\n#define OPENMW_COMPONENTS_NIF_RECORDPTR_HPP\n\n#include \"niffile.hpp\"\n#include \"nifstream.hpp\"\n#include <vector>\n\nnamespace Nif\n{\n\n/** A reference to another record. It is read as an index from the\n    NIF, and later looked up in the index table to get an actual\n    pointer.\n*/\ntemplate <class X>\nclass RecordPtrT\n{\n    union {\n        intptr_t index;\n        X* ptr;\n    };\n\npublic:\n    RecordPtrT() : index(-2) {}\n\n    RecordPtrT(X* ptr) : ptr(ptr) {}\n\n    /// Read the index from the nif\n    void read(NIFStream *nif)\n    {\n        // Can only read the index once\n        assert(index == -2);\n\n        // Store the index for later\n        index = nif->getInt();\n        assert(index >= -1);\n    }\n\n    /// Resolve index to pointer\n    void post(NIFFile *nif)\n    {\n        if(index < 0)\n            ptr = nullptr;\n        else\n        {\n            Record *r = nif->getRecord(index);\n            // And cast it\n            ptr = dynamic_cast<X*>(r);\n            assert(ptr != nullptr);\n        }\n    }\n\n    /// Look up the actual object from the index\n    const X* getPtr() const\n    {\n        assert(ptr != nullptr);\n        return ptr;\n    }\n    X* getPtr()\n    {\n        assert(ptr != nullptr);\n        return ptr;\n    }\n\n    const X& get() const\n    { return *getPtr(); }\n    X& get()\n    { return *getPtr(); }\n\n    /// Syntactic sugar\n    const X* operator->() const\n    { return getPtr(); }\n    X* operator->()\n    { return getPtr(); }\n\n    /// Pointers are allowed to be empty\n    bool empty() const\n    { return ptr == nullptr; }\n};\n\n/** A list of references to other records. These are read as a list,\n    and later converted to pointers as needed. Not an optimized\n    implementation.\n */\ntemplate <class X>\nclass RecordListT\n{\n    typedef RecordPtrT<X> Ptr;\n    std::vector<Ptr> list;\n\npublic:\n    RecordListT() = default;\n\n    RecordListT(std::vector<Ptr> list)\n        : list(std::move(list))\n    {}\n\n    void read(NIFStream *nif)\n    {\n        int len = nif->getInt();\n        list.resize(len);\n\n        for(size_t i=0;i < list.size();i++)\n            list[i].read(nif);\n    }\n\n    void post(NIFFile *nif)\n    {\n        for(size_t i=0;i < list.size();i++)\n            list[i].post(nif);\n    }\n\n    const Ptr& operator[](size_t index) const\n    { return list.at(index); }\n    Ptr& operator[](size_t index)\n    { return list.at(index); }\n\n    size_t length() const\n    { return list.size(); }\n};\n\n\nstruct Node;\nstruct Extra;\nstruct Property;\nstruct NiUVData;\nstruct NiPosData;\nstruct NiVisData;\nstruct Controller;\nstruct Named;\nstruct NiSkinData;\nstruct NiFloatData;\nstruct NiMorphData;\nstruct NiPixelData;\nstruct NiColorData;\nstruct NiKeyframeData;\nstruct NiTriStripsData;\nstruct NiSkinInstance;\nstruct NiSourceTexture;\nstruct NiPalette;\nstruct NiParticleModifier;\nstruct NiBoolData;\nstruct NiSkinPartition;\nstruct NiFloatInterpolator;\nstruct NiPoint3Interpolator;\nstruct NiTransformInterpolator;\nstruct BSShaderTextureSet;\nstruct NiGeometryData;\nstruct BSShaderProperty;\nstruct NiAlphaProperty;\n\nusing NodePtr = RecordPtrT<Node>;\nusing ExtraPtr = RecordPtrT<Extra>;\nusing NiUVDataPtr = RecordPtrT<NiUVData>;\nusing NiPosDataPtr = RecordPtrT<NiPosData>;\nusing NiVisDataPtr = RecordPtrT<NiVisData>;\nusing ControllerPtr = RecordPtrT<Controller>;\nusing NamedPtr = RecordPtrT<Named>;\nusing NiSkinDataPtr = RecordPtrT<NiSkinData>;\nusing NiMorphDataPtr = RecordPtrT<NiMorphData>;\nusing NiPixelDataPtr = RecordPtrT<NiPixelData>;\nusing NiFloatDataPtr = RecordPtrT<NiFloatData>;\nusing NiColorDataPtr = RecordPtrT<NiColorData>;\nusing NiKeyframeDataPtr = RecordPtrT<NiKeyframeData>;\nusing NiSkinInstancePtr = RecordPtrT<NiSkinInstance>;\nusing NiSourceTexturePtr = RecordPtrT<NiSourceTexture>;\nusing NiPalettePtr = RecordPtrT<NiPalette>;\nusing NiParticleModifierPtr = RecordPtrT<NiParticleModifier>;\nusing NiBoolDataPtr = RecordPtrT<NiBoolData>;\nusing NiSkinPartitionPtr = RecordPtrT<NiSkinPartition>;\nusing NiFloatInterpolatorPtr = RecordPtrT<NiFloatInterpolator>;\nusing NiPoint3InterpolatorPtr = RecordPtrT<NiPoint3Interpolator>;\nusing NiTransformInterpolatorPtr = RecordPtrT<NiTransformInterpolator>;\nusing BSShaderTextureSetPtr = RecordPtrT<BSShaderTextureSet>;\nusing NiGeometryDataPtr = RecordPtrT<NiGeometryData>;\nusing BSShaderPropertyPtr = RecordPtrT<BSShaderProperty>;\nusing NiAlphaPropertyPtr = RecordPtrT<NiAlphaProperty>;\n\nusing NodeList = RecordListT<Node>;\nusing PropertyList = RecordListT<Property>;\nusing ExtraList = RecordListT<Extra>;\nusing NiSourceTextureList = RecordListT<NiSourceTexture>;\nusing NiFloatInterpolatorList = RecordListT<NiFloatInterpolator>;\nusing NiTriStripsDataList = RecordListT<NiTriStripsData>;\n\n} // Namespace\n#endif\n"
  },
  {
    "path": "components/nifbullet/bulletnifloader.cpp",
    "content": "#include \"bulletnifloader.hpp\"\n\n#include <vector>\n\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <BulletCollision/CollisionShapes/btTriangleMesh.h>\n#include <BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/misc/convert.hpp>\n#include <components/misc/stringops.hpp>\n\n#include <components/nif/node.hpp>\n#include <components/nif/data.hpp>\n#include <components/nif/extra.hpp>\n\nnamespace\n{\n\nosg::Matrixf getWorldTransform(const Nif::Node *node)\n{\n    if(node->parent != nullptr)\n        return node->trafo.toMatrix() * getWorldTransform(node->parent);\n    return node->trafo.toMatrix();\n}\n\nbool pathFileNameStartsWithX(const std::string& path)\n{\n    const std::size_t slashpos = path.find_last_of(\"/\\\\\");\n    const std::size_t letterPos = slashpos == std::string::npos ? 0 : slashpos + 1;\n    return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X');\n}\n\nvoid fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform)\n{\n    const std::vector<osg::Vec3f> &vertices = data.vertices;\n    const std::vector<unsigned short> &triangles = data.triangles;\n    mesh.preallocateVertices(static_cast<int>(vertices.size()));\n    mesh.preallocateIndices(static_cast<int>(triangles.size()));\n\n    for (std::size_t i = 0; i < triangles.size(); i += 3)\n    {\n        mesh.addTriangle(\n            Misc::Convert::toBullet(vertices[triangles[i + 0]] * transform),\n            Misc::Convert::toBullet(vertices[triangles[i + 1]] * transform),\n            Misc::Convert::toBullet(vertices[triangles[i + 2]] * transform)\n        );\n    }\n}\n\nvoid fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf &transform)\n{\n    const std::vector<osg::Vec3f> &vertices = data.vertices;\n    const std::vector<std::vector<unsigned short>> &strips = data.strips;\n    mesh.preallocateVertices(static_cast<int>(vertices.size()));\n    int numTriangles = 0;\n    for (const std::vector<unsigned short>& strip : strips)\n    {\n        // Each strip with N points contains information about N-2 triangles.\n        if (strip.size() >= 3)\n            numTriangles += static_cast<int>(strip.size()-2);\n    }\n    mesh.preallocateIndices(static_cast<int>(numTriangles));\n\n    // It's triangulation time. Totally not a NifSkope spell ripoff.\n    for (const std::vector<unsigned short>& strip : strips)\n    {\n        // Can't make a triangle from less than 3 points.\n        if (strip.size() < 3)\n            continue;\n\n        unsigned short a = strip[0], b = strip[0], c = strip[1];\n        for (size_t i = 2; i < strip.size(); i++)\n        {\n            a = b;\n            b = c;\n            c = strip[i];\n            if (a != b && b != c && a != c)\n            {\n                if (i%2==0)\n                {\n                    mesh.addTriangle(\n                        Misc::Convert::toBullet(vertices[a] * transform),\n                        Misc::Convert::toBullet(vertices[b] * transform),\n                        Misc::Convert::toBullet(vertices[c] * transform)\n                    );\n                }\n                else\n                {\n                    mesh.addTriangle(\n                        Misc::Convert::toBullet(vertices[a] * transform),\n                        Misc::Convert::toBullet(vertices[c] * transform),\n                        Misc::Convert::toBullet(vertices[b] * transform)\n                    );\n                }\n            }\n        }\n    }\n}\n\nvoid fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiGeometry* geometry, const osg::Matrixf &transform = osg::Matrixf())\n{\n    if (geometry->recType == Nif::RC_NiTriShape || geometry->recType == Nif::RC_BSLODTriShape)\n        fillTriangleMesh(mesh, static_cast<const Nif::NiTriShapeData&>(geometry->data.get()), transform);\n    else if (geometry->recType == Nif::RC_NiTriStrips)\n        fillTriangleMesh(mesh, static_cast<const Nif::NiTriStripsData&>(geometry->data.get()), transform);\n}\n\n}\n\nnamespace NifBullet\n{\n\nosg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)\n{\n    mShape = new Resource::BulletShape;\n\n    mCompoundShape.reset();\n    mStaticMesh.reset();\n    mAvoidStaticMesh.reset();\n\n    const size_t numRoots = nif.numRoots();\n    std::vector<const Nif::Node*> roots;\n    for (size_t i = 0; i < numRoots; ++i)\n    {\n        const Nif::Record* r = nif.getRoot(i);\n        if (!r)\n            continue;\n        const Nif::Node* node = dynamic_cast<const Nif::Node*>(r);\n        if (node)\n            roots.emplace_back(node);\n    }\n    const std::string filename = nif.getFilename();\n    if (roots.empty())\n    {\n        warn(\"Found no root nodes in NIF file \" + filename);\n        return mShape;\n    }\n\n    // Try to find a valid bounding box first. If one's found for any root node, use that.\n    for (const Nif::Node* node : roots)\n    {\n        if (findBoundingBox(node, filename))\n        {\n            const btVector3 extents = Misc::Convert::toBullet(mShape->mCollisionBox.extents);\n            const btVector3 center = Misc::Convert::toBullet(mShape->mCollisionBox.center);\n            std::unique_ptr<btCompoundShape> compound (new btCompoundShape);\n            std::unique_ptr<btBoxShape> boxShape(new btBoxShape(extents));\n            btTransform transform = btTransform::getIdentity();\n            transform.setOrigin(center);\n            compound->addChildShape(transform, boxShape.get());\n            boxShape.release();\n\n            mShape->mCollisionShape = compound.release();\n            return mShape;\n        }\n    }\n    // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource).\n    // assume all nodes in the file will be animated\n    const bool isAnimated = pathFileNameStartsWithX(filename);\n\n    // If there's no bounding box, we'll have to generate a Bullet collision shape\n    // from the collision data present in every root node.\n    for (const Nif::Node* node : roots)\n    {\n        bool autogenerated = hasAutoGeneratedCollision(node);\n        handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated);\n    }\n\n    if (mCompoundShape)\n    {\n        if (mStaticMesh)\n        {\n            btTransform trans;\n            trans.setIdentity();\n            std::unique_ptr<btCollisionShape> child(new Resource::TriangleMeshShape(mStaticMesh.get(), true));\n            mCompoundShape->addChildShape(trans, child.get());\n            child.release();\n            mStaticMesh.release();\n        }\n        mShape->mCollisionShape = mCompoundShape.release();\n    }\n    else if (mStaticMesh)\n    {\n        mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh.get(), true);\n        mStaticMesh.release();\n    }\n\n    if (mAvoidStaticMesh)\n    {\n        mShape->mAvoidCollisionShape = new Resource::TriangleMeshShape(mAvoidStaticMesh.get(), false);\n        mAvoidStaticMesh.release();\n    }\n\n    return mShape;\n}\n\n// Find a boundingBox in the node hierarchy.\n// Return: use bounding box for collision?\nbool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string& filename)\n{\n    if (node->hasBounds)\n    {\n        unsigned int type = node->bounds.type;\n        switch (type)\n        {\n            case Nif::NiBoundingVolume::Type::BOX_BV:\n                mShape->mCollisionBox.extents = node->bounds.box.extents;\n                mShape->mCollisionBox.center = node->bounds.box.center;\n                break;\n            default:\n            {\n                std::stringstream warning;\n                warning << \"Unsupported NiBoundingVolume type \" << type << \" in node \" << node->recIndex;\n                warning << \" in file \" << filename;\n                warn(warning.str());\n            }\n        }\n\n        if (node->flags & Nif::NiNode::Flag_BBoxCollision)\n        {\n            return true;\n        }\n    }\n\n    const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);\n    if(ninode)\n    {\n        const Nif::NodeList &list = ninode->children;\n        for(size_t i = 0;i < list.length();i++)\n        {\n            if(!list[i].empty())\n            {\n                if (findBoundingBox(list[i].getPtr(), filename))\n                    return true;\n            }\n        }\n    }\n    return false;\n}\n\nbool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode)\n{\n    const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(rootNode);\n    if(ninode)\n    {\n        const Nif::NodeList &list = ninode->children;\n        for(size_t i = 0;i < list.length();i++)\n        {\n            if(!list[i].empty())\n            {\n                if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode)\n                    return false;\n            }\n        }\n    }\n    return true;\n}\n\nvoid BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *node, int flags,\n        bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid)\n{\n    // TODO: allow on-the fly collision switching via toggling this flag\n    if (node->recType == Nif::RC_NiCollisionSwitch && !(node->flags & Nif::NiNode::Flag_ActiveCollision))\n        return;\n\n    // Accumulate the flags from all the child nodes. This works for all\n    // the flags we currently use, at least.\n    flags |= node->flags;\n\n    if (!node->controller.empty() && node->controller->recType == Nif::RC_NiKeyframeController\n            && (node->controller->flags & Nif::NiNode::ControllerFlag_Active))\n        isAnimated = true;\n\n    isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode);\n\n    // Don't collide with AvoidNode shapes\n    avoid = avoid || (node->recType == Nif::RC_AvoidNode);\n\n    // We encountered a RootCollisionNode inside autogenerated mesh. It is not right.\n    if (node->recType == Nif::RC_RootCollisionNode && autogenerated)\n        Log(Debug::Info) << \"RootCollisionNode is not attached to the root node in \" << fileName << \". Treating it as a common NiTriShape.\";\n\n    // Check for extra data\n    for (Nif::ExtraPtr e = node->extra; !e.empty(); e = e->next)\n    {\n        if (e->recType == Nif::RC_NiStringExtraData)\n        {\n            // String markers may contain important information\n            // affecting the entire subtree of this node\n            Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e.getPtr();\n\n            if (Misc::StringUtils::ciCompareLen(sd->string, \"NC\", 2) == 0)\n            {\n                // No collision. Use an internal flag setting to mark this.\n                flags |= 0x800;\n            }\n            else if (sd->string == \"MRK\" && autogenerated)\n            {\n                // Marker can still have collision if the model explicitely specifies it via a RootCollisionNode.\n                return;\n            }\n\n        }\n    }\n\n    if (isCollisionNode)\n    {\n        // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape!\n        // It must be ignored completely.\n        // (occurs in tr_ex_imp_wall_arch_04.nif)\n        if(!node->hasBounds && (node->recType == Nif::RC_NiTriShape\n                                || node->recType == Nif::RC_NiTriStrips\n                                || node->recType == Nif::RC_BSLODTriShape))\n        {\n            handleNiTriShape(node, flags, getWorldTransform(node), isAnimated, avoid);\n        }\n    }\n\n    // For NiNodes, loop through children\n    const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);\n    if(ninode)\n    {\n        const Nif::NodeList &list = ninode->children;\n        for(size_t i = 0;i < list.length();i++)\n        {\n            if(!list[i].empty())\n                handleNode(fileName, list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated, avoid);\n        }\n    }\n}\n\nvoid BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, const osg::Matrixf &transform,\n                                       bool isAnimated, bool avoid)\n{\n    assert(nifNode != nullptr);\n\n    // If the object was marked \"NCO\" earlier, it shouldn't collide with\n    // anything. So don't do anything.\n    if ((flags & 0x800))\n        return;\n\n    auto niGeometry = static_cast<const Nif::NiGeometry*>(nifNode);\n    if (niGeometry->data.empty() || niGeometry->data->vertices.empty())\n        return;\n\n    if (niGeometry->recType == Nif::RC_NiTriShape || niGeometry->recType == Nif::RC_BSLODTriShape)\n    {\n        if (niGeometry->data->recType != Nif::RC_NiTriShapeData)\n            return;\n\n        auto data = static_cast<const Nif::NiTriShapeData*>(niGeometry->data.getPtr());\n        if (data->triangles.empty())\n            return;\n    }\n    else if (niGeometry->recType == Nif::RC_NiTriStrips)\n    {\n        if (niGeometry->data->recType != Nif::RC_NiTriStripsData)\n            return;\n\n        auto data = static_cast<const Nif::NiTriStripsData*>(niGeometry->data.getPtr());\n        if (data->strips.empty())\n            return;\n    }\n\n    if (!niGeometry->skin.empty())\n        isAnimated = false;\n\n    if (isAnimated)\n    {\n        if (!mCompoundShape)\n            mCompoundShape.reset(new btCompoundShape);\n\n        std::unique_ptr<btTriangleMesh> childMesh(new btTriangleMesh);\n\n        fillTriangleMesh(*childMesh, niGeometry);\n\n        std::unique_ptr<Resource::TriangleMeshShape> childShape(new Resource::TriangleMeshShape(childMesh.get(), true));\n        childMesh.release();\n\n        float scale = nifNode->trafo.scale;\n        const Nif::Node* parent = nifNode;\n        while (parent->parent)\n        {\n            parent = parent->parent;\n            scale *= parent->trafo.scale;\n        }\n        osg::Quat q = transform.getRotate();\n        osg::Vec3f v = transform.getTrans();\n        childShape->setLocalScaling(btVector3(scale, scale, scale));\n\n        btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z()));\n\n        mShape->mAnimatedShapes.emplace(nifNode->recIndex, mCompoundShape->getNumChildShapes());\n\n        mCompoundShape->addChildShape(trans, childShape.get());\n        childShape.release();\n    }\n    else if (avoid)\n    {\n        if (!mAvoidStaticMesh)\n            mAvoidStaticMesh.reset(new btTriangleMesh(false));\n\n        fillTriangleMesh(*mAvoidStaticMesh, niGeometry, transform);\n    }\n    else\n    {\n        if (!mStaticMesh)\n            mStaticMesh.reset(new btTriangleMesh(false));\n\n        // Static shape, just transform all vertices into position\n        fillTriangleMesh(*mStaticMesh, niGeometry, transform);\n    }\n}\n\n} // namespace NifBullet\n"
  },
  {
    "path": "components/nifbullet/bulletnifloader.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_NIFBULLET_BULLETNIFLOADER_HPP\n#define OPENMW_COMPONENTS_NIFBULLET_BULLETNIFLOADER_HPP\n\n#include <cassert>\n#include <string>\n#include <set>\n#include <map>\n\n#include <osg/Matrixf>\n#include <osg/BoundingBox>\n#include <osg/ref_ptr>\n#include <osg/Referenced>\n\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n\n#include <components/debug/debuglog.hpp>\n#include <components/nif/niffile.hpp>\n#include <components/resource/bulletshape.hpp>\n\nclass btTriangleMesh;\nclass btCompoundShape;\nclass btCollisionShape;\n\nnamespace Nif\n{\n    struct Node;\n    struct Transformation;\n    struct NiTriShape;\n    struct NiTriStrips;\n}\n\nnamespace NifBullet\n{\n\n/**\n*Load bulletShape from NIF files.\n*/\nclass BulletNifLoader\n{\npublic:\n    void warn(const std::string &msg)\n    {\n        Log(Debug::Warning) << \"NIFLoader: Warn: \" << msg;\n    }\n\n    void fail(const std::string &msg)\n    {\n        Log(Debug::Error) << \"NIFLoader: Fail: \"<< msg;\n        abort();\n    }\n\n    osg::ref_ptr<Resource::BulletShape> load(const Nif::File& file);\n\nprivate:\n    bool findBoundingBox(const Nif::Node* node, const std::string& filename);\n\n    void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode,\n                    bool isAnimated=false, bool autogenerated=false, bool avoid=false);\n\n    bool hasAutoGeneratedCollision(const Nif::Node *rootNode);\n\n    void handleNiTriShape(const Nif::Node *nifNode, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid);\n\n    std::unique_ptr<btCompoundShape> mCompoundShape;\n\n    std::unique_ptr<btTriangleMesh> mStaticMesh;\n\n    std::unique_ptr<btTriangleMesh> mAvoidStaticMesh;\n\n    osg::ref_ptr<Resource::BulletShape> mShape;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/nifosg/controller.cpp",
    "content": "#include \"controller.hpp\"\n\n#include <osg/MatrixTransform>\n#include <osg/TexMat>\n#include <osg/Material>\n#include <osg/Texture2D>\n\n#include <osgParticle/Emitter>\n\n#include <components/nif/data.hpp>\n#include <components/sceneutil/morphgeometry.hpp>\n\n#include \"matrixtransform.hpp\"\n\nnamespace NifOsg\n{\n\nControllerFunction::ControllerFunction(const Nif::Controller *ctrl)\n    : mFrequency(ctrl->frequency)\n    , mPhase(ctrl->phase)\n    , mStartTime(ctrl->timeStart)\n    , mStopTime(ctrl->timeStop)\n    , mExtrapolationMode(static_cast<ExtrapolationMode>((ctrl->flags&0x6) >> 1))\n{\n}\n\nfloat ControllerFunction::calculate(float value) const\n{\n    float time = mFrequency * value + mPhase;\n    if (time >= mStartTime && time <= mStopTime)\n        return time;\n    switch (mExtrapolationMode)\n    {\n    case Cycle:\n    {\n        float delta = mStopTime - mStartTime;\n        if ( delta <= 0 )\n            return mStartTime;\n        float cycles = ( time - mStartTime ) / delta;\n        float remainder = ( cycles - std::floor( cycles ) ) * delta;\n        return mStartTime + remainder;\n    }\n    case Reverse:\n    {\n        float delta = mStopTime - mStartTime;\n        if ( delta <= 0 )\n            return mStartTime;\n\n        float cycles = ( time - mStartTime ) / delta;\n        float remainder = ( cycles - std::floor( cycles ) ) * delta;\n\n        // Even number of cycles?\n        if ( ( static_cast<int>(std::fabs( std::floor( cycles ) )) % 2 ) == 0 )\n            return mStartTime + remainder;\n\n        return mStopTime - remainder;\n    }\n    case Constant:\n    default:\n        return std::min(mStopTime, std::max(mStartTime, time));\n    }\n}\n\nfloat ControllerFunction::getMaximum() const\n{\n    return mStopTime;\n}\n\nKeyframeController::KeyframeController()\n{\n}\n\nKeyframeController::KeyframeController(const KeyframeController &copy, const osg::CopyOp &copyop)\n    : SceneUtil::KeyframeController(copy, copyop)\n    , mRotations(copy.mRotations)\n    , mXRotations(copy.mXRotations)\n    , mYRotations(copy.mYRotations)\n    , mZRotations(copy.mZRotations)\n    , mTranslations(copy.mTranslations)\n    , mScales(copy.mScales)\n{\n}\n\nKeyframeController::KeyframeController(const Nif::NiKeyframeData *data)\n    : mRotations(data->mRotations)\n    , mXRotations(data->mXRotations, 0.f)\n    , mYRotations(data->mYRotations, 0.f)\n    , mZRotations(data->mZRotations, 0.f)\n    , mTranslations(data->mTranslations, osg::Vec3f())\n    , mScales(data->mScales, 1.f)\n{\n}\n\nKeyframeController::KeyframeController(const Nif::NiTransformInterpolator* interpolator)\n    : mRotations(interpolator->data->mRotations, interpolator->defaultRot)\n    , mXRotations(interpolator->data->mXRotations, 0.f)\n    , mYRotations(interpolator->data->mYRotations, 0.f)\n    , mZRotations(interpolator->data->mZRotations, 0.f)\n    , mTranslations(interpolator->data->mTranslations, interpolator->defaultPos)\n    , mScales(interpolator->data->mScales, interpolator->defaultScale)\n{\n}\n\nKeyframeController::KeyframeController(const float scale, const osg::Vec3f& pos, const osg::Quat& rot)\n    : mRotations(Nif::QuaternionKeyMapPtr(), rot)\n    , mXRotations(Nif::FloatKeyMapPtr(), 0.f)\n    , mYRotations(Nif::FloatKeyMapPtr(), 0.f)\n    , mZRotations(Nif::FloatKeyMapPtr(), 0.f)\n    , mTranslations(Nif::Vector3KeyMapPtr(), pos)\n    , mScales(Nif::FloatKeyMapPtr(), scale)\n{\n}\n\nosg::Quat KeyframeController::getXYZRotation(float time) const\n{\n    float xrot = 0, yrot = 0, zrot = 0;\n    if (!mXRotations.empty())\n        xrot = mXRotations.interpKey(time);\n    if (!mYRotations.empty())\n        yrot = mYRotations.interpKey(time);\n    if (!mZRotations.empty())\n        zrot = mZRotations.interpKey(time);\n    osg::Quat xr(xrot, osg::Vec3f(1,0,0));\n    osg::Quat yr(yrot, osg::Vec3f(0,1,0));\n    osg::Quat zr(zrot, osg::Vec3f(0,0,1));\n    return (xr*yr*zr);\n}\n\nosg::Vec3f KeyframeController::getTranslation(float time) const\n{\n    if(!mTranslations.empty())\n        return mTranslations.interpKey(time);\n    return osg::Vec3f();\n}\n\nvoid KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv)\n{\n    if (hasInput())\n    {\n        NifOsg::MatrixTransform* trans = static_cast<NifOsg::MatrixTransform*>(node);\n        osg::Matrix mat = trans->getMatrix();\n\n        float time = getInputValue(nv);\n\n        Nif::Matrix3& rot = trans->mRotationScale;\n\n        bool setRot = false;\n        if(!mRotations.empty())\n        {\n            mat.setRotate(mRotations.interpKey(time));\n            setRot = true;\n        }\n        else if (!mXRotations.empty() || !mYRotations.empty() || !mZRotations.empty())\n        {\n            mat.setRotate(getXYZRotation(time));\n            setRot = true;\n        }\n        else\n        {\n            // no rotation specified, use the previous value\n            for (int i=0;i<3;++i)\n                for (int j=0;j<3;++j)\n                    mat(j,i) = rot.mValues[i][j]; // NB column/row major difference\n        }\n\n        if (setRot) // copy the new values back\n            for (int i=0;i<3;++i)\n                for (int j=0;j<3;++j)\n                    rot.mValues[i][j] = mat(j,i); // NB column/row major difference\n\n        float& scale = trans->mScale;\n        if(!mScales.empty())\n            scale = mScales.interpKey(time);\n\n        for (int i=0;i<3;++i)\n            for (int j=0;j<3;++j)\n                mat(i,j) *= scale;\n\n        if(!mTranslations.empty())\n            mat.setTrans(mTranslations.interpKey(time));\n\n        trans->setMatrix(mat);\n    }\n\n    traverse(node, nv);\n}\n\nGeomMorpherController::GeomMorpherController()\n{\n}\n\nGeomMorpherController::GeomMorpherController(const GeomMorpherController &copy, const osg::CopyOp &copyop)\n    : osg::Drawable::UpdateCallback(copy, copyop)\n    , Controller(copy)\n    , mKeyFrames(copy.mKeyFrames)\n{\n}\n\nGeomMorpherController::GeomMorpherController(const Nif::NiGeomMorpherController* ctrl)\n{\n    if (ctrl->interpolators.length() == 0)\n    {\n        if (ctrl->data.empty())\n            return;\n        for (const auto& morph : ctrl->data->mMorphs)\n           mKeyFrames.emplace_back(morph.mKeyFrames);\n    }\n    else\n    {\n        for (size_t i = 0; i < ctrl->interpolators.length(); ++i)\n        {\n            if (!ctrl->interpolators[i].empty())\n                mKeyFrames.emplace_back(ctrl->interpolators[i].getPtr());\n            else\n                mKeyFrames.emplace_back();\n        }\n    }\n}\n\nvoid GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable)\n{\n    SceneUtil::MorphGeometry* morphGeom = static_cast<SceneUtil::MorphGeometry*>(drawable);\n    if (hasInput())\n    {\n        if (mKeyFrames.size() <= 1)\n            return;\n        float input = getInputValue(nv);\n        int i = 0;\n        for (std::vector<FloatInterpolator>::iterator it = mKeyFrames.begin()+1; it != mKeyFrames.end(); ++it,++i)\n        {\n            float val = 0;\n            if (!(*it).empty())\n                val = it->interpKey(input);\n\n            SceneUtil::MorphGeometry::MorphTarget& target = morphGeom->getMorphTarget(i);\n            if (target.getWeight() != val)\n            {\n                target.setWeight(val);\n                morphGeom->dirty();\n            }\n        }\n    }\n}\n\nUVController::UVController()\n{\n}\n\nUVController::UVController(const Nif::NiUVData *data, const std::set<int>& textureUnits)\n    : mUTrans(data->mKeyList[0], 0.f)\n    , mVTrans(data->mKeyList[1], 0.f)\n    , mUScale(data->mKeyList[2], 1.f)\n    , mVScale(data->mKeyList[3], 1.f)\n    , mTextureUnits(textureUnits)\n{\n}\n\nUVController::UVController(const UVController& copy, const osg::CopyOp& copyop)\n    : osg::Object(copy, copyop), StateSetUpdater(copy, copyop), Controller(copy)\n    , mUTrans(copy.mUTrans)\n    , mVTrans(copy.mVTrans)\n    , mUScale(copy.mUScale)\n    , mVScale(copy.mVScale)\n    , mTextureUnits(copy.mTextureUnits)\n{\n}\n\nvoid UVController::setDefaults(osg::StateSet *stateset)\n{\n    osg::ref_ptr<osg::TexMat> texMat (new osg::TexMat);\n    for (std::set<int>::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it)\n        stateset->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON);\n}\n\nvoid UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)\n{\n    if (hasInput())\n    {\n        float value = getInputValue(nv);\n\n        // First scale the UV relative to its center, then apply the offset.\n        // U offset is flipped regardless of the graphics library,\n        // while V offset is flipped to account for OpenGL Y axis convention.\n        osg::Vec3f uvOrigin(0.5f, 0.5f, 0.f);\n        osg::Vec3f uvScale(mUScale.interpKey(value), mVScale.interpKey(value), 1.f);\n        osg::Vec3f uvTrans(-mUTrans.interpKey(value), -mVTrans.interpKey(value), 0.f);\n\n        osg::Matrixf mat = osg::Matrixf::translate(uvOrigin);\n        mat.preMultScale(uvScale);\n        mat.preMultTranslate(-uvOrigin);\n        mat.setTrans(mat.getTrans() + uvTrans);\n\n        // setting once is enough because all other texture units share the same TexMat (see setDefaults).\n        if (!mTextureUnits.empty())\n        {\n            osg::TexMat* texMat = static_cast<osg::TexMat*>(stateset->getTextureAttribute(*mTextureUnits.begin(), osg::StateAttribute::TEXMAT));\n            texMat->setMatrix(mat);\n        }\n    }\n}\n\nVisController::VisController(const Nif::NiVisData *data, unsigned int mask)\n    : mData(data->mVis)\n    , mMask(mask)\n{\n}\n\nVisController::VisController()\n    : mMask(0)\n{\n}\n\nVisController::VisController(const VisController &copy, const osg::CopyOp &copyop)\n    : osg::NodeCallback(copy, copyop)\n    , Controller(copy)\n    , mData(copy.mData)\n    , mMask(copy.mMask)\n{\n}\n\nbool VisController::calculate(float time) const\n{\n    if(mData.size() == 0)\n        return true;\n\n    for(size_t i = 1;i < mData.size();i++)\n    {\n        if(mData[i].time > time)\n            return mData[i-1].isSet;\n    }\n    return mData.back().isSet;\n}\n\nvoid VisController::operator() (osg::Node* node, osg::NodeVisitor* nv)\n{\n    if (hasInput())\n    {\n        bool vis = calculate(getInputValue(nv));\n        node->setNodeMask(vis ? ~0 : mMask);\n    }\n    traverse(node, nv);\n}\n\nRollController::RollController(const Nif::NiFloatData *data)\n    : mData(data->mKeyList, 1.f)\n{\n}\n\nRollController::RollController(const Nif::NiFloatInterpolator* interpolator)\n    : mData(interpolator)\n{\n}\n\nRollController::RollController(const RollController &copy, const osg::CopyOp &copyop)\n    : osg::NodeCallback(copy, copyop)\n    , Controller(copy)\n    , mData(copy.mData)\n    , mStartingTime(copy.mStartingTime)\n{\n}\n\nvoid RollController::operator() (osg::Node* node, osg::NodeVisitor* nv)\n{\n    traverse(node, nv);\n\n    if (hasInput())\n    {\n        double newTime = nv->getFrameStamp()->getSimulationTime();\n        double duration = newTime - mStartingTime;\n        mStartingTime = newTime;\n\n        float value = mData.interpKey(getInputValue(nv));\n        osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);\n        osg::Matrix matrix = transform->getMatrix();\n\n        // Rotate around \"roll\" axis.\n        // Note: in original game rotation speed is the framerate-dependent in a very tricky way.\n        // Do not replicate this behaviour until we will really need it.\n        // For now consider controller's current value as an angular speed in radians per 1/60 seconds.\n        matrix = osg::Matrix::rotate(value * duration * 60.f, 0, 0, 1) * matrix;\n        transform->setMatrix(matrix);\n    }\n}\n\nAlphaController::AlphaController()\n{\n}\n\nAlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial)\n    : mData(data->mKeyList, 1.f)\n    , mBaseMaterial(baseMaterial)\n{\n\n}\n\nAlphaController::AlphaController(const Nif::NiFloatInterpolator* interpolator, const osg::Material* baseMaterial)\n    : mData(interpolator)\n    , mBaseMaterial(baseMaterial)\n{\n}\n\nAlphaController::AlphaController(const AlphaController &copy, const osg::CopyOp &copyop)\n    : StateSetUpdater(copy, copyop), Controller(copy)\n    , mData(copy.mData)\n    , mBaseMaterial(copy.mBaseMaterial)\n{\n}\n\nvoid AlphaController::setDefaults(osg::StateSet *stateset)\n{\n    stateset->setAttribute(static_cast<osg::Material*>(mBaseMaterial->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::StateAttribute::ON);\n}\n\nvoid AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)\n{\n    if (hasInput())\n    {\n        float value = mData.interpKey(getInputValue(nv));\n        osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));\n        osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);\n        diffuse.a() = value;\n        mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);\n    }\n}\n\nMaterialColorController::MaterialColorController()\n{\n}\n\nMaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial)\n    : mData(data->mKeyList, osg::Vec3f(1,1,1))\n    , mTargetColor(color)\n    , mBaseMaterial(baseMaterial)\n{\n}\n\nMaterialColorController::MaterialColorController(const Nif::NiPoint3Interpolator* interpolator, TargetColor color, const osg::Material* baseMaterial)\n    : mData(interpolator)\n    , mTargetColor(color)\n    , mBaseMaterial(baseMaterial)\n{\n}\n\nMaterialColorController::MaterialColorController(const MaterialColorController &copy, const osg::CopyOp &copyop)\n    : StateSetUpdater(copy, copyop), Controller(copy)\n    , mData(copy.mData)\n    , mTargetColor(copy.mTargetColor)\n    , mBaseMaterial(copy.mBaseMaterial)\n{\n}\n\nvoid MaterialColorController::setDefaults(osg::StateSet *stateset)\n{\n    stateset->setAttribute(static_cast<osg::Material*>(mBaseMaterial->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::StateAttribute::ON);\n}\n\nvoid MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)\n{\n    if (hasInput())\n    {\n        osg::Vec3f value = mData.interpKey(getInputValue(nv));\n        osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));\n        switch (mTargetColor)\n        {\n            case Diffuse:\n            {\n                osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);\n                diffuse.set(value.x(), value.y(), value.z(), diffuse.a());\n                mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);\n                break;\n            }\n            case Specular:\n            {\n                osg::Vec4f specular = mat->getSpecular(osg::Material::FRONT_AND_BACK);\n                specular.set(value.x(), value.y(), value.z(), specular.a());\n                mat->setSpecular(osg::Material::FRONT_AND_BACK, specular);\n                break;\n            }\n            case Emissive:\n            {\n                osg::Vec4f emissive = mat->getEmission(osg::Material::FRONT_AND_BACK);\n                emissive.set(value.x(), value.y(), value.z(), emissive.a());\n                mat->setEmission(osg::Material::FRONT_AND_BACK, emissive);\n                break;\n            }\n            case Ambient:\n            default:\n            {\n                osg::Vec4f ambient = mat->getAmbient(osg::Material::FRONT_AND_BACK);\n                ambient.set(value.x(), value.y(), value.z(), ambient.a());\n                mat->setAmbient(osg::Material::FRONT_AND_BACK, ambient);\n            }\n        }\n    }\n}\n\nFlipController::FlipController(const Nif::NiFlipController *ctrl, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)\n    : mTexSlot(0) // always affects diffuse\n    , mDelta(ctrl->mDelta)\n    , mTextures(textures)\n{\n    if (!ctrl->mInterpolator.empty())\n        mData = ctrl->mInterpolator.getPtr();\n}\n\nFlipController::FlipController(int texSlot, float delta, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)\n    : mTexSlot(texSlot)\n    , mDelta(delta)\n    , mTextures(textures)\n{\n}\n\nFlipController::FlipController(const FlipController &copy, const osg::CopyOp &copyop)\n    : StateSetUpdater(copy, copyop)\n    , Controller(copy)\n    , mTexSlot(copy.mTexSlot)\n    , mDelta(copy.mDelta)\n    , mTextures(copy.mTextures)\n    , mData(copy.mData)\n{\n}\n\nvoid FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)\n{\n    if (hasInput() && !mTextures.empty())\n    {\n        int curTexture = 0;\n        if (mDelta != 0)\n            curTexture = int(getInputValue(nv) / mDelta) % mTextures.size();\n        else\n            curTexture = int(mData.interpKey(getInputValue(nv))) % mTextures.size();\n        stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]);\n    }\n}\n\nParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController *ctrl)\n    : mEmitStart(ctrl->startTime), mEmitStop(ctrl->stopTime)\n{\n}\n\nParticleSystemController::ParticleSystemController()\n    : mEmitStart(0.f), mEmitStop(0.f)\n{\n}\n\nParticleSystemController::ParticleSystemController(const ParticleSystemController &copy, const osg::CopyOp &copyop)\n    : osg::NodeCallback(copy, copyop)\n    , Controller(copy)\n    , mEmitStart(copy.mEmitStart)\n    , mEmitStop(copy.mEmitStop)\n{\n}\n\nvoid ParticleSystemController::operator() (osg::Node* node, osg::NodeVisitor* nv)\n{\n    osgParticle::ParticleProcessor* emitter = static_cast<osgParticle::ParticleProcessor*>(node);\n    if (hasInput())\n    {\n        float time = getInputValue(nv);\n        emitter->getParticleSystem()->setFrozen(false);\n        emitter->setEnabled(time >= mEmitStart && time < mEmitStop);\n    }\n    else\n        emitter->getParticleSystem()->setFrozen(true);\n    traverse(node, nv);\n}\n\nPathController::PathController(const PathController &copy, const osg::CopyOp &copyop)\n    : osg::NodeCallback(copy, copyop)\n    , Controller(copy)\n    , mPath(copy.mPath)\n    , mPercent(copy.mPercent)\n    , mFlags(copy.mFlags)\n{\n}\n\nPathController::PathController(const Nif::NiPathController* ctrl)\n    : mPath(ctrl->posData->mKeyList, osg::Vec3f())\n    , mPercent(ctrl->floatData->mKeyList, 1.f)\n    , mFlags(ctrl->flags)\n{\n}\n\nfloat PathController::getPercent(float time) const\n{\n    float percent = mPercent.interpKey(time);\n    if (percent < 0.f)\n        percent = std::fmod(percent, 1.f) + 1.f;\n    else if (percent > 1.f)\n        percent = std::fmod(percent, 1.f);\n    return percent;\n}\n\nvoid PathController::operator() (osg::Node* node, osg::NodeVisitor* nv)\n{\n    if (mPath.empty() || mPercent.empty() || !hasInput())\n    {\n        traverse(node, nv);\n        return;\n    }\n\n    osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(node);\n    osg::Matrix mat = trans->getMatrix();\n\n    float time = getInputValue(nv);\n    float percent = getPercent(time);\n    osg::Vec3f pos(mPath.interpKey(percent));\n    mat.setTrans(pos);\n    trans->setMatrix(mat);\n\n    traverse(node, nv);\n}\n\n}\n"
  },
  {
    "path": "components/nifosg/controller.hpp",
    "content": "#ifndef COMPONENTS_NIFOSG_CONTROLLER_H\n#define COMPONENTS_NIFOSG_CONTROLLER_H\n\n#include <components/nif/niffile.hpp>\n#include <components/nif/nifkey.hpp>\n#include <components/nif/controller.hpp>\n#include <components/nif/data.hpp>\n\n#include <components/sceneutil/keyframe.hpp>\n#include <components/sceneutil/statesetupdater.hpp>\n\n#include <set>\n#include <type_traits>\n\n#include <osg/Texture2D>\n\n#include <osg/StateSet>\n#include <osg/NodeCallback>\n#include <osg/Drawable>\n\n\nnamespace osg\n{\n    class Material;\n}\n\nnamespace NifOsg\n{\n\n    // interpolation of keyframes\n    template <typename MapT>\n    class ValueInterpolator\n    {\n        typename MapT::MapType::const_iterator retrieveKey(float time) const\n        {\n            // retrieve the current position in the map, optimized for the most common case\n            // where time moves linearly along the keyframe track\n            if (mLastHighKey != mKeys->mKeys.end())\n            {\n                if (time > mLastHighKey->first)\n                {\n                    // try if we're there by incrementing one\n                    ++mLastLowKey;\n                    ++mLastHighKey;\n                }\n                if (mLastHighKey != mKeys->mKeys.end() && time >= mLastLowKey->first && time <= mLastHighKey->first)\n                    return mLastHighKey;\n            }\n\n            return mKeys->mKeys.lower_bound(time);\n        }\n\n    public:\n        using ValueT = typename MapT::ValueType;\n\n        ValueInterpolator() = default;\n\n        template<\n            class T,\n            typename = std::enable_if_t<\n                std::conjunction_v<\n                    std::disjunction<\n                        std::is_same<ValueT, float>,\n                        std::is_same<ValueT, osg::Vec3f>,\n                        std::is_same<ValueT, bool>,\n                        std::is_same<ValueT, osg::Vec4f>\n                    >,\n                    std::is_same<decltype(T::defaultVal), ValueT>\n                >,\n                T\n            >\n        >\n        ValueInterpolator(const T* interpolator) : mDefaultVal(interpolator->defaultVal)\n        {\n            if (interpolator->data.empty())\n                return;\n            mKeys = interpolator->data->mKeyList;\n            if (mKeys)\n            {\n                mLastLowKey = mKeys->mKeys.end();\n                mLastHighKey = mKeys->mKeys.end();\n            }\n        }\n\n        ValueInterpolator(std::shared_ptr<const MapT> keys, ValueT defaultVal = ValueT())\n            : mKeys(keys)\n            , mDefaultVal(defaultVal)\n        {\n            if (keys)\n            {\n                mLastLowKey = mKeys->mKeys.end();\n                mLastHighKey = mKeys->mKeys.end();\n            }\n        }\n\n        ValueT interpKey(float time) const\n        {\n            if (empty())\n                return mDefaultVal;\n\n            const typename MapT::MapType & keys = mKeys->mKeys;\n\n            if(time <= keys.begin()->first)\n                return keys.begin()->second.mValue;\n\n            typename MapT::MapType::const_iterator it = retrieveKey(time);\n\n            // now do the actual interpolation\n            if (it != keys.end())\n            {\n                // cache for next time\n                mLastHighKey = it;\n                mLastLowKey = --it;\n\n                float a = (time - mLastLowKey->first) / (mLastHighKey->first - mLastLowKey->first);\n\n                return interpolate(mLastLowKey->second, mLastHighKey->second, a, mKeys->mInterpolationType);\n            }\n\n            return keys.rbegin()->second.mValue;\n        }\n\n        bool empty() const\n        {\n            return !mKeys || mKeys->mKeys.empty();\n        }\n\n    private:\n        template <typename ValueType>\n        ValueType interpolate(const Nif::KeyT<ValueType>& a, const Nif::KeyT<ValueType>& b, float fraction, unsigned int type) const\n        {\n            switch (type)\n            {\n                case Nif::InterpolationType_Constant:\n                    return fraction > 0.5f ? b.mValue : a.mValue;\n                case Nif::InterpolationType_Quadratic:\n                {\n                    // Using a cubic Hermite spline.\n                    // b1(t) = 2t^3  - 3t^2 + 1\n                    // b2(t) = -2t^3 + 3t^2\n                    // b3(t) = t^3 - 2t^2 + t\n                    // b4(t) = t^3 - t^2\n                    // f(t) = a.mValue * b1(t) + b.mValue * b2(t) + a.mOutTan * b3(t) + b.mInTan * b4(t)\n                    const float t = fraction;\n                    const float t2 = t * t;\n                    const float t3 = t2 * t;\n                    const float b1 = 2.f * t3 - 3.f * t2 + 1;\n                    const float b2 = -2.f * t3 + 3.f * t2;\n                    const float b3 = t3 - 2.f * t2 + t;\n                    const float b4 = t3 - t2;\n                    return a.mValue * b1 + b.mValue * b2 + a.mOutTan * b3 + b.mInTan * b4;\n                }\n                // TODO: Implement TBC interpolation\n                default:\n                    return a.mValue + ((b.mValue - a.mValue) * fraction);\n            }\n        }\n        osg::Quat interpolate(const Nif::KeyT<osg::Quat>& a, const Nif::KeyT<osg::Quat>& b, float fraction, unsigned int type) const\n        {\n            switch (type)\n            {\n                case Nif::InterpolationType_Constant:\n                    return fraction > 0.5f ? b.mValue : a.mValue;\n                // TODO: Implement Quadratic and TBC interpolation\n                default:\n                {\n                    osg::Quat result;\n                    result.slerp(fraction, a.mValue, b.mValue);\n                    return result;\n                }\n            }\n        }\n\n        mutable typename MapT::MapType::const_iterator mLastLowKey;\n        mutable typename MapT::MapType::const_iterator mLastHighKey;\n\n        std::shared_ptr<const MapT> mKeys;\n\n        ValueT mDefaultVal = ValueT();\n    };\n\n    using QuaternionInterpolator = ValueInterpolator<Nif::QuaternionKeyMap>;\n    using FloatInterpolator = ValueInterpolator<Nif::FloatKeyMap>;\n    using Vec3Interpolator = ValueInterpolator<Nif::Vector3KeyMap>;\n    using Vec4Interpolator = ValueInterpolator<Nif::Vector4KeyMap>;\n\n    class ControllerFunction : public SceneUtil::ControllerFunction\n    {\n    private:\n        float mFrequency;\n        float mPhase;\n        float mStartTime;\n        float mStopTime;\n        enum ExtrapolationMode\n        {\n            Cycle = 0,\n            Reverse = 1,\n            Constant = 2\n        };\n        ExtrapolationMode mExtrapolationMode;\n\n    public:\n        ControllerFunction(const Nif::Controller *ctrl);\n\n        float calculate(float value) const override;\n\n        float getMaximum() const override;\n    };\n\n    /// Must be set on a SceneUtil::MorphGeometry.\n    class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller\n    {\n    public:\n        GeomMorpherController(const Nif::NiGeomMorpherController* ctrl);\n        GeomMorpherController();\n        GeomMorpherController(const GeomMorpherController& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, GeomMorpherController)\n\n        void update(osg::NodeVisitor* nv, osg::Drawable* drawable) override;\n\n    private:\n        std::vector<FloatInterpolator> mKeyFrames;\n    };\n\n    class KeyframeController : public SceneUtil::KeyframeController\n    {\n    public:\n        // This is used if there's no interpolator but there is data (Morrowind meshes).\n        KeyframeController(const Nif::NiKeyframeData *data);\n        // This is used if the interpolator has data.\n        KeyframeController(const Nif::NiTransformInterpolator* interpolator);\n        // This is used if there are default values available (e.g. from a data-less interpolator).\n        // If there's neither keyframe data nor an interpolator a KeyframeController must not be created.\n        KeyframeController(const float scale, const osg::Vec3f& pos, const osg::Quat& rot);\n\n        KeyframeController();\n        KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, KeyframeController)\n\n        osg::Vec3f getTranslation(float time) const override;\n\n        void operator() (osg::Node*, osg::NodeVisitor*) override;\n\n    private:\n        QuaternionInterpolator mRotations;\n\n        FloatInterpolator mXRotations;\n        FloatInterpolator mYRotations;\n        FloatInterpolator mZRotations;\n\n        Vec3Interpolator mTranslations;\n        FloatInterpolator mScales;\n\n        osg::Quat getXYZRotation(float time) const;\n    };\n\n    class UVController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller\n    {\n    public:\n        UVController();\n        UVController(const UVController&,const osg::CopyOp&);\n        UVController(const Nif::NiUVData *data, const std::set<int>& textureUnits);\n\n        META_Object(NifOsg,UVController)\n\n        void setDefaults(osg::StateSet* stateset) override;\n        void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) override;\n\n    private:\n        FloatInterpolator mUTrans;\n        FloatInterpolator mVTrans;\n        FloatInterpolator mUScale;\n        FloatInterpolator mVScale;\n        std::set<int> mTextureUnits;\n    };\n\n    class VisController : public osg::NodeCallback, public SceneUtil::Controller\n    {\n    private:\n        std::vector<Nif::NiVisData::VisData> mData;\n        unsigned int mMask;\n\n        bool calculate(float time) const;\n\n    public:\n        VisController(const Nif::NiVisData *data, unsigned int mask);\n        VisController();\n        VisController(const VisController& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, VisController)\n\n        void operator() (osg::Node* node, osg::NodeVisitor* nv) override;\n    };\n\n    class RollController : public osg::NodeCallback, public SceneUtil::Controller\n    {\n    private:\n        FloatInterpolator mData;\n        double mStartingTime{0};\n\n    public:\n        RollController(const Nif::NiFloatData *data);\n        RollController(const Nif::NiFloatInterpolator* interpolator);\n        RollController() = default;\n        RollController(const RollController& copy, const osg::CopyOp& copyop);\n\n        void operator() (osg::Node* node, osg::NodeVisitor* nv) override;\n\n        META_Object(NifOsg, RollController)\n    };\n\n    class AlphaController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller\n    {\n    private:\n        FloatInterpolator mData;\n        osg::ref_ptr<const osg::Material> mBaseMaterial;\n    public:\n        AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial);\n        AlphaController(const Nif::NiFloatInterpolator* interpolator, const osg::Material* baseMaterial);\n        AlphaController();\n        AlphaController(const AlphaController& copy, const osg::CopyOp& copyop);\n\n        void setDefaults(osg::StateSet* stateset) override;\n\n        void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override;\n\n        META_Object(NifOsg, AlphaController)\n    };\n\n    class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller\n    {\n    public:\n        enum TargetColor\n        {\n            Ambient  = 0,\n            Diffuse  = 1,\n            Specular = 2,\n            Emissive = 3\n        };\n        MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial);\n        MaterialColorController(const Nif::NiPoint3Interpolator* interpolator, TargetColor color, const osg::Material* baseMaterial);\n        MaterialColorController();\n        MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, MaterialColorController)\n\n        void setDefaults(osg::StateSet* stateset) override;\n\n        void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override;\n\n    private:\n        Vec3Interpolator mData;\n        TargetColor mTargetColor = Ambient;\n        osg::ref_ptr<const osg::Material> mBaseMaterial;\n    };\n\n    class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller\n    {\n    private:\n        int mTexSlot{0};\n        float mDelta{0.f};\n        std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;\n        FloatInterpolator mData;\n\n    public:\n        FlipController(const Nif::NiFlipController* ctrl, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures);\n        FlipController(int texSlot, float delta, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures);\n        FlipController() = default;\n        FlipController(const FlipController& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, FlipController)\n\n        std::vector<osg::ref_ptr<osg::Texture2D> >& getTextures() { return mTextures; }\n\n        void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) override;\n    };\n\n    class ParticleSystemController : public osg::NodeCallback, public SceneUtil::Controller\n    {\n    public:\n        ParticleSystemController(const Nif::NiParticleSystemController* ctrl);\n        ParticleSystemController();\n        ParticleSystemController(const ParticleSystemController& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, ParticleSystemController)\n\n        void operator() (osg::Node* node, osg::NodeVisitor* nv) override;\n\n    private:\n        float mEmitStart;\n        float mEmitStop;\n    };\n\n    class PathController : public osg::NodeCallback, public SceneUtil::Controller\n    {\n    public:\n        PathController(const Nif::NiPathController* ctrl);\n        PathController() = default;\n        PathController(const PathController& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, PathController)\n\n        void operator() (osg::Node*, osg::NodeVisitor*) override;\n\n    private:\n        Vec3Interpolator mPath;\n        FloatInterpolator mPercent;\n        int mFlags{0};\n\n        float getPercent(float time) const;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/nifosg/matrixtransform.cpp",
    "content": "#include \"matrixtransform.hpp\"\n\nnamespace NifOsg\n{\n    MatrixTransform::MatrixTransform()\n        : osg::MatrixTransform()\n    {\n    }\n\n    MatrixTransform::MatrixTransform(const Nif::Transformation &trafo)\n        : osg::MatrixTransform(trafo.toMatrix())\n        , mScale(trafo.scale)\n        , mRotationScale(trafo.rotation)\n    {\n    }\n\n    MatrixTransform::MatrixTransform(const MatrixTransform &copy, const osg::CopyOp &copyop)\n        : osg::MatrixTransform(copy, copyop)\n        , mScale(copy.mScale)\n        , mRotationScale(copy.mRotationScale)\n    {\n    }\n}\n"
  },
  {
    "path": "components/nifosg/matrixtransform.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_NIFOSG_MATRIXTRANSFORM_H\n#define OPENMW_COMPONENTS_NIFOSG_MATRIXTRANSFORM_H\n\n#include <components/nif/niftypes.hpp>\n\n#include <osg/MatrixTransform>\n\nnamespace NifOsg\n{\n\n    class MatrixTransform : public osg::MatrixTransform\n    {\n    public:\n        MatrixTransform();\n        MatrixTransform(const Nif::Transformation &trafo);\n        MatrixTransform(const MatrixTransform &copy, const osg::CopyOp &copyop);\n\n        META_Node(NifOsg, MatrixTransform)\n\n        // Hack: account for Transform differences between OSG and NIFs.\n        // OSG uses a 4x4 matrix, NIF's use a 3x3 rotationScale, float scale, and vec3 position.\n        // Decomposing the original components from the 4x4 matrix isn't possible, which causes\n        // problems when a KeyframeController wants to change only one of these components. So\n        // we store the scale and rotation components separately here.\n        float mScale{0.f};\n        Nif::Matrix3 mRotationScale;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/nifosg/nifloader.cpp",
    "content": "#include \"nifloader.hpp\"\n\n#include <mutex>\n\n#include <osg/Matrixf>\n#include <osg/Geometry>\n#include <osg/Array>\n#include <osg/LOD>\n#include <osg/Switch>\n#include <osg/TexGen>\n#include <osg/ValueObject>\n\n// resource\n#include <components/debug/debuglog.hpp>\n#include <components/misc/constants.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/misc/resourcehelpers.hpp>\n#include <components/resource/imagemanager.hpp>\n#include <components/sceneutil/util.hpp>\n\n// particle\n#include <osgParticle/ParticleSystem>\n#include <osgParticle/ParticleSystemUpdater>\n#include <osgParticle/ConstantRateCounter>\n#include <osgParticle/BoxPlacer>\n#include <osgParticle/ModularProgram>\n\n#include <osg/BlendFunc>\n#include <osg/AlphaFunc>\n#include <osg/Depth>\n#include <osg/PolygonMode>\n#include <osg/FrontFace>\n#include <osg/Stencil>\n#include <osg/Material>\n#include <osg/Texture2D>\n#include <osg/TexEnv>\n#include <osg/TexEnvCombine>\n\n#include <components/nif/node.hpp>\n#include <components/nif/effect.hpp>\n#include <components/sceneutil/skeleton.hpp>\n#include <components/sceneutil/riggeometry.hpp>\n#include <components/sceneutil/morphgeometry.hpp>\n\n#include \"matrixtransform.hpp\"\n#include \"particle.hpp\"\n\nnamespace\n{\n\n    void getAllNiNodes(const Nif::Node* node, std::vector<int>& outIndices)\n    {\n        const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(node);\n        if (ninode)\n        {\n            outIndices.push_back(ninode->recIndex);\n            for (unsigned int i=0; i<ninode->children.length(); ++i)\n                if (!ninode->children[i].empty())\n                    getAllNiNodes(ninode->children[i].getPtr(), outIndices);\n        }\n    }\n\n    bool isTypeGeometry(int type)\n    {\n        switch (type)\n        {\n            case Nif::RC_NiTriShape:\n            case Nif::RC_NiTriStrips:\n            case Nif::RC_NiLines:\n            case Nif::RC_BSLODTriShape:\n                return true;\n        }\n        return false;\n    }\n\n    // Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the node hierarchy above it.\n    void collectDrawableProperties(const Nif::Node* nifNode, std::vector<const Nif::Property*>& out)\n    {\n        if (nifNode->parent)\n            collectDrawableProperties(nifNode->parent, out);\n        const Nif::PropertyList& props = nifNode->props;\n        for (size_t i = 0; i <props.length();++i)\n        {\n            if (!props[i].empty())\n            {\n                switch (props[i]->recType)\n                {\n                case Nif::RC_NiMaterialProperty:\n                case Nif::RC_NiVertexColorProperty:\n                case Nif::RC_NiSpecularProperty:\n                case Nif::RC_NiAlphaProperty:\n                    out.push_back(props[i].getPtr());\n                    break;\n                default:\n                    break;\n                }\n            }\n        }\n\n        auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode);\n        if (geometry)\n        {\n            if (!geometry->shaderprop.empty())\n                out.emplace_back(geometry->shaderprop.getPtr());\n            if (!geometry->alphaprop.empty())\n                out.emplace_back(geometry->alphaprop.getPtr());\n        }\n    }\n\n    // NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale\n    // set just like a regular MatrixTransform, but the rotation set will be overridden in order to face the camera.\n    // Must be set as a cull callback.\n    class BillboardCallback : public osg::NodeCallback\n    {\n    public:\n        BillboardCallback()\n        {\n        }\n        BillboardCallback(const BillboardCallback& copy, const osg::CopyOp& copyop)\n            : osg::NodeCallback(copy, copyop)\n        {\n        }\n\n        META_Object(NifOsg, BillboardCallback)\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n\n            osg::Matrix modelView = *cv->getModelViewMatrix();\n\n            // attempt to preserve scale\n            float mag[3];\n            for (int i=0;i<3;++i)\n            {\n                mag[i] = std::sqrt(modelView(0,i) * modelView(0,i) + modelView(1,i) * modelView(1,i) + modelView(2,i) * modelView(2,i));\n            }\n\n            modelView.setRotate(osg::Quat());\n            modelView(0,0) = mag[0];\n            modelView(1,1) = mag[1];\n            modelView(2,2) = mag[2];\n\n            cv->pushModelViewMatrix(new osg::RefMatrix(modelView), osg::Transform::RELATIVE_RF);\n\n            traverse(node, nv);\n\n            cv->popModelViewMatrix();\n        }\n    };\n\n    void extractTextKeys(const Nif::NiTextKeyExtraData *tk, SceneUtil::TextKeyMap &textkeys)\n    {\n        for(size_t i = 0;i < tk->list.size();i++)\n        {\n            const std::string &str = tk->list[i].text;\n            std::string::size_type pos = 0;\n            while(pos < str.length())\n            {\n                if(::isspace(str[pos]))\n                {\n                    pos++;\n                    continue;\n                }\n\n                std::string::size_type nextpos = std::min(str.find('\\r', pos), str.find('\\n', pos));\n                if(nextpos != std::string::npos)\n                {\n                    do {\n                        nextpos--;\n                    } while(nextpos > pos && ::isspace(str[nextpos]));\n                    nextpos++;\n                }\n                else if(::isspace(*str.rbegin()))\n                {\n                    std::string::const_iterator last = str.end();\n                    do {\n                        --last;\n                    } while(last != str.begin() && ::isspace(*last));\n                    nextpos = std::distance(str.begin(), ++last);\n                }\n                std::string result = str.substr(pos, nextpos-pos);\n                Misc::StringUtils::lowerCaseInPlace(result);\n                textkeys.emplace(tk->list[i].time, std::move(result));\n\n                pos = nextpos;\n            }\n        }\n    }\n}\n\nnamespace NifOsg\n{\n    bool Loader::sShowMarkers = false;\n\n    void Loader::setShowMarkers(bool show)\n    {\n        sShowMarkers = show;\n    }\n\n    bool Loader::getShowMarkers()\n    {\n        return sShowMarkers;\n    }\n\n    unsigned int Loader::sHiddenNodeMask = 0;\n\n    void Loader::setHiddenNodeMask(unsigned int mask)\n    {\n        sHiddenNodeMask = mask;\n    }\n    unsigned int Loader::getHiddenNodeMask()\n    {\n        return sHiddenNodeMask;\n    }\n\n    unsigned int Loader::sIntersectionDisabledNodeMask = ~0u;\n\n    void Loader::setIntersectionDisabledNodeMask(unsigned int mask)\n    {\n        sIntersectionDisabledNodeMask = mask;\n    }\n\n    unsigned int Loader::getIntersectionDisabledNodeMask()\n    {\n        return sIntersectionDisabledNodeMask;\n    }\n\n    class LoaderImpl\n    {\n    public:\n        /// @param filename used for warning messages.\n        LoaderImpl(const std::string& filename, unsigned int ver, unsigned int userver, unsigned int bethver)\n            : mFilename(filename), mVersion(ver), mUserVersion(userver), mBethVersion(bethver)\n        {\n\n        }\n        std::string mFilename;\n        unsigned int mVersion, mUserVersion, mBethVersion;\n\n        size_t mFirstRootTextureIndex{~0u};\n        bool mFoundFirstRootTexturingProperty = false;\n\n        bool mHasNightDayLabel = false;\n        bool mHasHerbalismLabel = false;\n\n        // This is used to queue emitters that weren't attached to their node yet.\n        std::vector<std::pair<size_t, osg::ref_ptr<Emitter>>> mEmitterQueue;\n\n        static void loadKf(Nif::NIFFilePtr nif, SceneUtil::KeyframeHolder& target)\n        {\n            const Nif::NiSequenceStreamHelper *seq = nullptr;\n            const size_t numRoots = nif->numRoots();\n            for (size_t i = 0; i < numRoots; ++i)\n            {\n                const Nif::Record *r = nif->getRoot(i);\n                if (r && r->recType == Nif::RC_NiSequenceStreamHelper)\n                {\n                    seq = static_cast<const Nif::NiSequenceStreamHelper*>(r);\n                    break;\n                }\n            }\n\n            if (!seq)\n            {\n                nif->warn(\"Found no NiSequenceStreamHelper root record\");\n                return;\n            }\n\n            Nif::ExtraPtr extra = seq->extra;\n            if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData)\n            {\n                nif->warn(\"First extra data was not a NiTextKeyExtraData, but a \"+\n                          (extra.empty() ? std::string(\"nil\") : extra->recName)+\".\");\n                return;\n            }\n\n            extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), target.mTextKeys);\n\n            extra = extra->next;\n            Nif::ControllerPtr ctrl = seq->controller;\n            for(;!extra.empty() && !ctrl.empty();(extra=extra->next),(ctrl=ctrl->next))\n            {\n                if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController)\n                {\n                    nif->warn(\"Unexpected extra data \"+extra->recName+\" with controller \"+ctrl->recName);\n                    continue;\n                }\n\n                // Vanilla seems to ignore the \"active\" flag for NiKeyframeController,\n                // so we don't want to skip inactive controllers here.\n\n                const Nif::NiStringExtraData *strdata = static_cast<const Nif::NiStringExtraData*>(extra.getPtr());\n                const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());\n\n                if (key->data.empty() && key->interpolator.empty())\n                    continue;\n\n                osg::ref_ptr<SceneUtil::KeyframeController> callback(handleKeyframeController(key));\n                callback->setFunction(std::shared_ptr<NifOsg::ControllerFunction>(new NifOsg::ControllerFunction(key)));\n\n                if (!target.mKeyframeControllers.emplace(strdata->string, callback).second)\n                    Log(Debug::Verbose) << \"Controller \" << strdata->string << \" present more than once in \" << nif->getFilename() << \", ignoring later version\";\n            }\n        }\n\n        osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager)\n        {\n            const size_t numRoots = nif->numRoots();\n            std::vector<const Nif::Node*> roots;\n            for (size_t i = 0; i < numRoots; ++i)\n            {\n                const Nif::Record* r = nif->getRoot(i);\n                if (!r)\n                    continue;\n                const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r);\n                if (nifNode)\n                    roots.emplace_back(nifNode);\n            }\n            if (roots.empty())\n                nif->fail(\"Found no root nodes\");\n\n            osg::ref_ptr<SceneUtil::TextKeyMapHolder> textkeys (new SceneUtil::TextKeyMapHolder);\n\n            osg::ref_ptr<osg::Group> created(new osg::Group);\n            created->setDataVariance(osg::Object::STATIC);\n            for (const Nif::Node* root : roots)\n            {\n                auto node = handleNode(root, nullptr, imageManager, std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys);\n                created->addChild(node);\n            }\n            if (mHasNightDayLabel)\n                created->getOrCreateUserDataContainer()->addDescription(Constants::NightDayLabel);\n            if (mHasHerbalismLabel)\n                created->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel);\n\n            // Attach particle emitters to their nodes which should all be loaded by now.\n            handleQueuedParticleEmitters(created, nif);\n\n            if (nif->getUseSkinning())\n            {\n                osg::ref_ptr<SceneUtil::Skeleton> skel = new SceneUtil::Skeleton;\n                skel->setStateSet(created->getStateSet());\n                skel->setName(created->getName());\n                for (unsigned int i=0; i < created->getNumChildren(); ++i)\n                    skel->addChild(created->getChild(i));\n                created->removeChildren(0, created->getNumChildren());\n                created = skel;\n            }\n\n            if (!textkeys->mTextKeys.empty())\n                created->getOrCreateUserDataContainer()->addUserObject(textkeys);\n\n            return created;\n        }\n\n        void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<unsigned int>& boundTextures, int animflags)\n        {\n            const Nif::PropertyList& props = nifNode->props;\n            for (size_t i = 0; i <props.length(); ++i)\n            {\n                if (!props[i].empty())\n                {\n                    // Get the lowest numbered recIndex of the NiTexturingProperty root node.\n                    // This is what is overridden when a spell effect \"particle texture\" is used.\n                    if (nifNode->parent == nullptr && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty)\n                    {\n                        mFirstRootTextureIndex = props[i].getPtr()->recIndex;\n                        mFoundFirstRootTexturingProperty = true;\n                    }\n                    else if (props[i].getPtr()->recType == Nif::RC_NiTexturingProperty)\n                    {\n                        if (props[i].getPtr()->recIndex == mFirstRootTextureIndex)\n                            applyTo->setUserValue(\"overrideFx\", 1);\n                    }\n                    handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags);\n                }\n            }\n\n            auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode);\n            // NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property\n            if (geometry && !geometry->shaderprop.empty())\n                handleProperty(geometry->shaderprop.getPtr(), applyTo, composite, imageManager, boundTextures, animflags);\n        }\n\n        void setupController(const Nif::Controller* ctrl, SceneUtil::Controller* toSetup, int animflags)\n        {\n            bool autoPlay = animflags & Nif::NiNode::AnimFlag_AutoPlay;\n            if (autoPlay)\n                toSetup->setSource(std::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));\n\n            toSetup->setFunction(std::shared_ptr<ControllerFunction>(new ControllerFunction(ctrl)));\n        }\n\n        osg::ref_ptr<osg::LOD> handleLodNode(const Nif::NiLODNode* niLodNode)\n        {\n            osg::ref_ptr<osg::LOD> lod (new osg::LOD);\n            lod->setName(niLodNode->name);\n            lod->setCenterMode(osg::LOD::USER_DEFINED_CENTER);\n            lod->setCenter(niLodNode->lodCenter);\n            for (unsigned int i=0; i<niLodNode->lodLevels.size(); ++i)\n            {\n                const Nif::NiLODNode::LODRange& range = niLodNode->lodLevels[i];\n                lod->setRange(i, range.minRange, range.maxRange);\n            }\n            lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);\n            return lod;\n        }\n\n        osg::ref_ptr<osg::Switch> handleSwitchNode(const Nif::NiSwitchNode* niSwitchNode)\n        {\n            osg::ref_ptr<osg::Switch> switchNode (new osg::Switch);\n            switchNode->setName(niSwitchNode->name);\n            switchNode->setNewChildDefaultValue(false);\n            switchNode->setSingleChildOn(niSwitchNode->initialIndex);\n            return switchNode;\n        }\n\n        osg::ref_ptr<osg::Image> handleSourceTexture(const Nif::NiSourceTexture* st, Resource::ImageManager* imageManager)\n        {\n            if (!st)\n                return nullptr;\n\n            osg::ref_ptr<osg::Image> image;\n            if (!st->external && !st->data.empty())\n            {\n                image = handleInternalTexture(st->data.getPtr());\n            }\n            else\n            {\n                std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, imageManager->getVFS());\n                image = imageManager->getImage(filename);\n            }\n            return image;\n        }\n\n        void handleEffect(const Nif::Node* nifNode, osg::Node* node, Resource::ImageManager* imageManager)\n        {\n            if (nifNode->recType != Nif::RC_NiTextureEffect)\n            {\n                Log(Debug::Info) << \"Unhandled effect \" << nifNode->recName << \" in \" << mFilename;\n                return;\n            }\n\n            const Nif::NiTextureEffect* textureEffect = static_cast<const Nif::NiTextureEffect*>(nifNode);\n            if (textureEffect->textureType != Nif::NiTextureEffect::Environment_Map)\n            {\n                Log(Debug::Info) << \"Unhandled NiTextureEffect type \" << textureEffect->textureType << \" in \" << mFilename;\n                return;\n            }\n\n            if (textureEffect->texture.empty())\n            {\n                Log(Debug::Info) << \"NiTextureEffect missing source texture in \" << mFilename;\n                return;\n            }\n\n            osg::ref_ptr<osg::TexGen> texGen (new osg::TexGen);\n            switch (textureEffect->coordGenType)\n            {\n            case Nif::NiTextureEffect::World_Parallel:\n                texGen->setMode(osg::TexGen::OBJECT_LINEAR);\n                break;\n            case Nif::NiTextureEffect::World_Perspective:\n                texGen->setMode(osg::TexGen::EYE_LINEAR);\n                break;\n            case Nif::NiTextureEffect::Sphere_Map:\n                texGen->setMode(osg::TexGen::SPHERE_MAP);\n                break;\n            default:\n                Log(Debug::Info) << \"Unhandled NiTextureEffect coordGenType \" << textureEffect->coordGenType << \" in \" << mFilename;\n                return;\n            }\n\n            osg::ref_ptr<osg::Image> image (handleSourceTexture(textureEffect->texture.getPtr(), imageManager));\n            osg::ref_ptr<osg::Texture2D> texture2d (new osg::Texture2D(image));\n            if (image)\n                texture2d->setTextureSize(image->s(), image->t());\n            texture2d->setName(\"envMap\");\n            bool wrapT = textureEffect->clamp & 0x1;\n            bool wrapS = (textureEffect->clamp >> 1) & 0x1;\n            texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);\n            texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);\n\n            int texUnit = 3; // FIXME\n\n            osg::StateSet* stateset = node->getOrCreateStateSet();\n            stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);\n            stateset->setTextureAttributeAndModes(texUnit, texGen, osg::StateAttribute::ON);\n            stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON);\n\n            stateset->addUniform(new osg::Uniform(\"envMapColor\", osg::Vec4f(1,1,1,1)));\n        }\n\n        // Get a default dataVariance for this node to be used as a hint by optimization (post)routines\n        osg::ref_ptr<osg::Group> createNode(const Nif::Node* nifNode)\n        {\n            osg::ref_ptr<osg::Group> node;\n            osg::Object::DataVariance dataVariance = osg::Object::UNSPECIFIED;\n\n            switch (nifNode->recType)\n            {\n            case Nif::RC_NiBillboardNode:\n                dataVariance = osg::Object::DYNAMIC;\n                break;\n            default:\n                // The Root node can be created as a Group if no transformation is required.\n                // This takes advantage of the fact root nodes can't have additional controllers\n                // loaded from an external .kf file (original engine just throws \"can't find node\" errors if you try).\n                if (!nifNode->parent && nifNode->controller.empty() && nifNode->trafo.isIdentity())\n                    node = new osg::Group;\n\n                dataVariance = nifNode->isBone ? osg::Object::DYNAMIC : osg::Object::STATIC;\n\n                break;\n            }\n            if (!node)\n                node = new NifOsg::MatrixTransform(nifNode->trafo);\n\n            if (nifNode->recType == Nif::RC_NiCollisionSwitch && !(nifNode->flags & Nif::NiNode::Flag_ActiveCollision))\n            {\n                node->setNodeMask(Loader::getIntersectionDisabledNodeMask());\n                // This node must not be combined with another node.\n                dataVariance = osg::Object::DYNAMIC;\n            }\n\n            node->setDataVariance(dataVariance);\n\n            return node;\n        }\n\n        osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager,\n                                std::vector<unsigned int> boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, SceneUtil::TextKeyMap* textKeys, osg::Node* rootNode=nullptr)\n        {\n            if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, \"Bounding Box\"))\n                return nullptr;\n\n            osg::ref_ptr<osg::Group> node = createNode(nifNode);\n\n            if (nifNode->recType == Nif::RC_NiBillboardNode)\n            {\n                node->addCullCallback(new BillboardCallback);\n            }\n\n            node->setName(nifNode->name);\n\n            if (parentNode)\n                parentNode->addChild(node);\n\n            if (!rootNode)\n                rootNode = node;\n\n            // The original NIF record index is used for a variety of features:\n            // - finding the correct emitter node for a particle system\n            // - establishing connections to the animated collision shapes, which are handled in a separate loader\n            // - finding a random child NiNode in NiBspArrayController\n            node->setUserValue(\"recIndex\", nifNode->recIndex);\n\n            std::vector<Nif::ExtraPtr> extraCollection;\n\n            for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next)\n                extraCollection.emplace_back(e);\n\n            for (size_t i = 0; i < nifNode->extralist.length(); ++i)\n            {\n                Nif::ExtraPtr e = nifNode->extralist[i];\n                if (!e.empty())\n                    extraCollection.emplace_back(e);\n            }\n\n            for (const auto& e : extraCollection)\n            {\n                if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys)\n                {\n                    const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());\n                    extractTextKeys(tk, *textKeys);\n                }\n                else if(e->recType == Nif::RC_NiStringExtraData)\n                {\n                    const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr());\n                    // String markers may contain important information\n                    // affecting the entire subtree of this obj\n                    if(sd->string == \"MRK\" && !Loader::getShowMarkers())\n                    {\n                        // Marker objects. These meshes are only visible in the editor.\n                        hasMarkers = true;\n                    }\n                    else if(sd->string == \"BONE\")\n                    {\n                        node->getOrCreateUserDataContainer()->addDescription(\"CustomBone\");\n                    }\n                }\n            }\n\n            if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode)\n                animflags = nifNode->flags;\n\n            // Hide collision shapes, but don't skip the subgraph\n            // We still need to animate the hidden bones so the physics system can access them\n            if (nifNode->recType == Nif::RC_RootCollisionNode)\n            {\n                skipMeshes = true;\n                node->setNodeMask(Loader::getHiddenNodeMask());\n            }\n\n            // We can skip creating meshes for hidden nodes if they don't have a VisController that\n            // might make them visible later\n            if (nifNode->flags & Nif::NiNode::Flag_Hidden)\n            {\n                bool hasVisController = false;\n                for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)\n                {\n                    hasVisController |= (ctrl->recType == Nif::RC_NiVisController);\n                    if (hasVisController)\n                        break;\n                }\n\n                if (!hasVisController)\n                    skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating collision shapes\n\n                node->setNodeMask(Loader::getHiddenNodeMask());\n            }\n\n            osg::ref_ptr<SceneUtil::CompositeStateSetUpdater> composite = new SceneUtil::CompositeStateSetUpdater;\n\n            applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags);\n\n            const bool isGeometry = isTypeGeometry(nifNode->recType);\n\n            if (isGeometry && !skipMeshes)\n            {\n                const std::string nodeName = Misc::StringUtils::lowerCase(nifNode->name);\n                static const std::string markerName = \"tri editormarker\";\n                static const std::string shadowName = \"shadow\";\n                static const std::string shadowName2 = \"tri shadow\";\n                const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName);\n                if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2))\n                {\n                    Nif::NiSkinInstancePtr skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin;\n\n                    if (skin.empty())\n                        handleGeometry(nifNode, node, composite, boundTextures, animflags);\n                    else\n                        handleSkinnedGeometry(nifNode, node, composite, boundTextures, animflags);\n\n                    if (!nifNode->controller.empty())\n                        handleMeshControllers(nifNode, node, composite, boundTextures, animflags);\n                }\n            }\n\n            if (nifNode->recType == Nif::RC_NiParticles)\n                handleParticleSystem(nifNode, node, composite, animflags);\n\n            if (composite->getNumControllers() > 0)\n            {\n                osg::Callback *cb = composite;\n                if (composite->getNumControllers() == 1)\n                    cb = composite->getController(0);\n                if (animflags & Nif::NiNode::AnimFlag_AutoPlay)\n                    node->addCullCallback(cb);\n                else\n                    node->addUpdateCallback(cb); // have to remain as UpdateCallback so AssignControllerSourcesVisitor can find it.\n            }\n\n            bool isAnimated = false;\n            handleNodeControllers(nifNode, node, animflags, isAnimated);\n            hasAnimatedParents |= isAnimated;\n            // Make sure empty nodes and animated shapes are not optimized away so the physics system can find them.\n            if (isAnimated || (hasAnimatedParents && ((skipMeshes || hasMarkers) || isGeometry)))\n                node->setDataVariance(osg::Object::DYNAMIC);\n\n            // LOD and Switch nodes must be wrapped by a transform (the current node) to support transformations properly\n            // and we need to attach their children to the osg::LOD/osg::Switch nodes\n            // but we must return that transform to the caller of handleNode instead of the actual LOD/Switch nodes.\n            osg::ref_ptr<osg::Group> currentNode = node;\n\n            if (nifNode->recType == Nif::RC_NiSwitchNode)\n            {\n                const Nif::NiSwitchNode* niSwitchNode = static_cast<const Nif::NiSwitchNode*>(nifNode);\n                osg::ref_ptr<osg::Switch> switchNode = handleSwitchNode(niSwitchNode);\n                node->addChild(switchNode);\n                if (niSwitchNode->name == Constants::NightDayLabel)\n                    mHasNightDayLabel = true;\n                else if (niSwitchNode->name == Constants::HerbalismLabel)\n                    mHasHerbalismLabel = true;\n\n                currentNode = switchNode;\n            }\n            else if (nifNode->recType == Nif::RC_NiLODNode)\n            {\n                const Nif::NiLODNode* niLodNode = static_cast<const Nif::NiLODNode*>(nifNode);\n                osg::ref_ptr<osg::LOD> lodNode = handleLodNode(niLodNode);\n                node->addChild(lodNode);\n                currentNode = lodNode;\n            }\n\n            const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(nifNode);\n            if(ninode)\n            {\n                const Nif::NodeList &effects = ninode->effects;\n                for (size_t i = 0; i < effects.length(); ++i)\n                {\n                    if (!effects[i].empty())\n                        handleEffect(effects[i].getPtr(), currentNode, imageManager);\n                }\n\n                const Nif::NodeList &children = ninode->children;\n                for(size_t i = 0;i < children.length();++i)\n                {\n                    if(!children[i].empty())\n                        handleNode(children[i].getPtr(), currentNode, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode);\n                }\n            }\n\n            return node;\n        }\n\n        void handleMeshControllers(const Nif::Node *nifNode, osg::Node* node, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int> &boundTextures, int animflags)\n        {\n            for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)\n            {\n                if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))\n                    continue;\n                if (ctrl->recType == Nif::RC_NiUVController)\n                {\n                    const Nif::NiUVController *niuvctrl = static_cast<const Nif::NiUVController*>(ctrl.getPtr());\n                    if (niuvctrl->data.empty())\n                        continue;\n                    const unsigned int uvSet = niuvctrl->uvSet;\n                    std::set<int> texUnits;\n                    // UVController should work only for textures which use a given UV Set, usually 0.\n                    for (unsigned int i=0; i<boundTextures.size(); ++i)\n                    {\n                        if (boundTextures[i] == uvSet)\n                            texUnits.insert(i);\n                    }\n\n                    osg::ref_ptr<UVController> uvctrl = new UVController(niuvctrl->data.getPtr(), texUnits);\n                    setupController(niuvctrl, uvctrl, animflags);\n                    composite->addController(uvctrl);\n                }\n            }\n        }\n\n        static osg::ref_ptr<KeyframeController> handleKeyframeController(const Nif::NiKeyframeController* keyctrl)\n        {\n            osg::ref_ptr<NifOsg::KeyframeController> ctrl;\n            if (!keyctrl->interpolator.empty())\n            {\n                const Nif::NiTransformInterpolator* interp = keyctrl->interpolator.getPtr();\n                if (!interp->data.empty())\n                    ctrl = new NifOsg::KeyframeController(interp);\n                else\n                    ctrl = new NifOsg::KeyframeController(interp->defaultScale, interp->defaultPos, interp->defaultRot);\n            }\n            else if (!keyctrl->data.empty())\n            {\n                ctrl = new NifOsg::KeyframeController(keyctrl->data.getPtr());\n            }\n            return ctrl;\n        }\n\n        void handleNodeControllers(const Nif::Node* nifNode, osg::Node* node, int animflags, bool& isAnimated)\n        {\n            for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)\n            {\n                if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))\n                    continue;\n                if (ctrl->recType == Nif::RC_NiKeyframeController)\n                {\n                    const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());\n                    if (key->data.empty() && key->interpolator.empty())\n                        continue;\n                    osg::ref_ptr<KeyframeController> callback(handleKeyframeController(key));\n                    setupController(key, callback, animflags);\n                    node->addUpdateCallback(callback);\n                    isAnimated = true;\n                }\n                else if (ctrl->recType == Nif::RC_NiPathController)\n                {\n                    const Nif::NiPathController *path = static_cast<const Nif::NiPathController*>(ctrl.getPtr());\n                    if (path->posData.empty() || path->floatData.empty())\n                        continue;\n                    osg::ref_ptr<PathController> callback(new PathController(path));\n                    setupController(path, callback, animflags);\n                    node->addUpdateCallback(callback);\n                    isAnimated = true;\n                }\n                else if (ctrl->recType == Nif::RC_NiVisController)\n                {\n                    const Nif::NiVisController *visctrl = static_cast<const Nif::NiVisController*>(ctrl.getPtr());\n                    if (visctrl->data.empty())\n                        continue;\n                    osg::ref_ptr<VisController> callback(new VisController(visctrl->data.getPtr(), Loader::getHiddenNodeMask()));\n                    setupController(visctrl, callback, animflags);\n                    node->addUpdateCallback(callback);\n                }\n                else if (ctrl->recType == Nif::RC_NiRollController)\n                {\n                    const Nif::NiRollController *rollctrl = static_cast<const Nif::NiRollController*>(ctrl.getPtr());\n                    if (rollctrl->data.empty() && rollctrl->interpolator.empty())\n                        continue;\n                    osg::ref_ptr<RollController> callback;\n                    if (!rollctrl->interpolator.empty())\n                        callback = new RollController(rollctrl->interpolator.getPtr());\n                    else // if (!rollctrl->data.empty())\n                        callback = new RollController(rollctrl->data.getPtr());\n                    setupController(rollctrl, callback, animflags);\n                    node->addUpdateCallback(callback);\n                    isAnimated = true;\n                }\n                else if (ctrl->recType == Nif::RC_NiGeomMorpherController\n                      || ctrl->recType == Nif::RC_NiParticleSystemController\n                      || ctrl->recType == Nif::RC_NiBSPArrayController\n                      || ctrl->recType == Nif::RC_NiUVController)\n                {\n                    // These controllers are handled elsewhere\n                }\n                else\n                    Log(Debug::Info) << \"Unhandled controller \" << ctrl->recName << \" on node \" << nifNode->recIndex << \" in \" << mFilename;\n            }\n        }\n\n        void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial)\n        {\n            for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next)\n            {\n                if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))\n                    continue;\n                if (ctrl->recType == Nif::RC_NiAlphaController)\n                {\n                    const Nif::NiAlphaController* alphactrl = static_cast<const Nif::NiAlphaController*>(ctrl.getPtr());\n                    if (alphactrl->data.empty() && alphactrl->interpolator.empty())\n                        continue;\n                    osg::ref_ptr<AlphaController> osgctrl;\n                    if (!alphactrl->interpolator.empty())\n                        osgctrl = new AlphaController(alphactrl->interpolator.getPtr(), baseMaterial);\n                    else // if (!alphactrl->data.empty())\n                        osgctrl = new AlphaController(alphactrl->data.getPtr(), baseMaterial);\n                    setupController(alphactrl, osgctrl, animflags);\n                    composite->addController(osgctrl);\n                }\n                else if (ctrl->recType == Nif::RC_NiMaterialColorController)\n                {\n                    const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());\n                    if (matctrl->data.empty() && matctrl->interpolator.empty())\n                        continue;\n                    osg::ref_ptr<MaterialColorController> osgctrl;\n                    auto targetColor = static_cast<MaterialColorController::TargetColor>(matctrl->targetColor);\n                    if (!matctrl->interpolator.empty())\n                        osgctrl = new MaterialColorController(matctrl->interpolator.getPtr(), targetColor, baseMaterial);\n                    else // if (!matctrl->data.empty())\n                        osgctrl = new MaterialColorController(matctrl->data.getPtr(), targetColor, baseMaterial);\n                    setupController(matctrl, osgctrl, animflags);\n                    composite->addController(osgctrl);\n                }\n                else\n                    Log(Debug::Info) << \"Unexpected material controller \" << ctrl->recType << \" in \" << mFilename;\n            }\n        }\n\n        void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, osg::StateSet *stateset, int animflags)\n        {\n            for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next)\n            {\n                if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))\n                    continue;\n                if (ctrl->recType == Nif::RC_NiFlipController)\n                {\n                    const Nif::NiFlipController* flipctrl = static_cast<const Nif::NiFlipController*>(ctrl.getPtr());\n                    std::vector<osg::ref_ptr<osg::Texture2D> > textures;\n\n                    // inherit wrap settings from the target slot\n                    osg::Texture2D* inherit = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(0, osg::StateAttribute::TEXTURE));\n                    osg::Texture2D::WrapMode wrapS = osg::Texture2D::REPEAT;\n                    osg::Texture2D::WrapMode wrapT = osg::Texture2D::REPEAT;\n                    if (inherit)\n                    {\n                        wrapS = inherit->getWrap(osg::Texture2D::WRAP_S);\n                        wrapT = inherit->getWrap(osg::Texture2D::WRAP_T);\n                    }\n\n                    for (unsigned int i=0; i<flipctrl->mSources.length(); ++i)\n                    {\n                        Nif::NiSourceTexturePtr st = flipctrl->mSources[i];\n                        if (st.empty())\n                            continue;\n\n                        osg::ref_ptr<osg::Image> image (handleSourceTexture(st.getPtr(), imageManager));\n                        osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D(image));\n                        if (image)\n                            texture->setTextureSize(image->s(), image->t());\n                        texture->setWrap(osg::Texture::WRAP_S, wrapS);\n                        texture->setWrap(osg::Texture::WRAP_T, wrapT);\n                        textures.push_back(texture);\n                    }\n                    osg::ref_ptr<FlipController> callback(new FlipController(flipctrl, textures));\n                    setupController(ctrl.getPtr(), callback, animflags);\n                    composite->addController(callback);\n                }\n                else\n                    Log(Debug::Info) << \"Unexpected texture controller \" << ctrl->recName << \" in \" << mFilename;\n            }\n        }\n\n        void handleParticlePrograms(Nif::NiParticleModifierPtr affectors, Nif::NiParticleModifierPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf)\n        {\n            osgParticle::ModularProgram* program = new osgParticle::ModularProgram;\n            attachTo->addChild(program);\n            program->setParticleSystem(partsys);\n            program->setReferenceFrame(rf);\n            for (; !affectors.empty(); affectors = affectors->next)\n            {\n                if (affectors->recType == Nif::RC_NiParticleGrowFade)\n                {\n                    const Nif::NiParticleGrowFade *gf = static_cast<const Nif::NiParticleGrowFade*>(affectors.getPtr());\n                    program->addOperator(new GrowFadeAffector(gf->growTime, gf->fadeTime));\n                }\n                else if (affectors->recType == Nif::RC_NiGravity)\n                {\n                    const Nif::NiGravity* gr = static_cast<const Nif::NiGravity*>(affectors.getPtr());\n                    program->addOperator(new GravityAffector(gr));\n                }\n                else if (affectors->recType == Nif::RC_NiParticleColorModifier)\n                {\n                    const Nif::NiParticleColorModifier *cl = static_cast<const Nif::NiParticleColorModifier*>(affectors.getPtr());\n                    if (cl->data.empty())\n                        continue;\n                    const Nif::NiColorData *clrdata = cl->data.getPtr();\n                    program->addOperator(new ParticleColorAffector(clrdata));\n                }\n                else if (affectors->recType == Nif::RC_NiParticleRotation)\n                {\n                    // unused\n                }\n                else\n                    Log(Debug::Info) << \"Unhandled particle modifier \" << affectors->recName << \" in \" << mFilename;\n            }\n            for (; !colliders.empty(); colliders = colliders->next)\n            {\n                if (colliders->recType == Nif::RC_NiPlanarCollider)\n                {\n                    const Nif::NiPlanarCollider* planarcollider = static_cast<const Nif::NiPlanarCollider*>(colliders.getPtr());\n                    program->addOperator(new PlanarCollider(planarcollider));\n                }\n                else if (colliders->recType == Nif::RC_NiSphericalCollider)\n                {\n                    const Nif::NiSphericalCollider* sphericalcollider = static_cast<const Nif::NiSphericalCollider*>(colliders.getPtr());\n                    program->addOperator(new SphericalCollider(sphericalcollider));\n                }\n                else\n                    Log(Debug::Info) << \"Unhandled particle collider \" << colliders->recName << \" in \" << mFilename;\n            }\n        }\n\n        // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors.\n        void handleParticleInitialState(const Nif::Node* nifNode, ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl)\n        {\n            auto particleNode = static_cast<const Nif::NiParticles*>(nifNode);\n            if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData)\n            {\n                partsys->setQuota(partctrl->numParticles);\n                return;\n            }\n\n            auto particledata = static_cast<const Nif::NiParticlesData*>(particleNode->data.getPtr());\n            partsys->setQuota(particledata->numParticles);\n\n            osg::BoundingBox box;\n\n            int i=0;\n            for (const auto& particle : partctrl->particles)\n            {\n                if (i++ >= particledata->activeCount)\n                    break;\n\n                if (particle.lifespan <= 0)\n                    continue;\n\n                if (particle.vertex >= particledata->vertices.size())\n                    continue;\n\n                ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime));\n\n                osgParticle::Particle* created = partsys->createParticle(&particletemplate);\n                created->setLifeTime(particle.lifespan);\n\n                // Note this position and velocity is not correct for a particle system with absolute reference frame,\n                // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up post-load in the SceneManager.\n                created->setVelocity(particle.velocity);\n                const osg::Vec3f& position = particledata->vertices[particle.vertex];\n                created->setPosition(position);\n\n                osg::Vec4f partcolor (1.f,1.f,1.f,1.f);\n                if (particle.vertex < particledata->colors.size())\n                    partcolor = particledata->colors[particle.vertex];\n\n                float size = partctrl->size;\n                if (particle.vertex < particledata->sizes.size())\n                    size *= particledata->sizes[particle.vertex];\n\n                created->setSizeRange(osgParticle::rangef(size, size));\n                box.expandBy(osg::BoundingSphere(position, size));\n            }\n\n            // radius may be used to force a larger bounding box\n            box.expandBy(osg::BoundingSphere(osg::Vec3(0,0,0), particledata->radius));\n\n            partsys->setInitialBound(box);\n        }\n\n        osg::ref_ptr<Emitter> handleParticleEmitter(const Nif::NiParticleSystemController* partctrl)\n        {\n            std::vector<int> targets;\n            if (partctrl->recType == Nif::RC_NiBSPArrayController)\n            {\n                getAllNiNodes(partctrl->emitter.getPtr(), targets);\n            }\n\n            osg::ref_ptr<Emitter> emitter = new Emitter(targets);\n\n            osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter;\n            if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust)\n                counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate);\n            else if (partctrl->lifetime == 0 && partctrl->lifetimeRandom == 0)\n                counter->setNumberOfParticlesPerSecondToCreate(0);\n            else\n                counter->setNumberOfParticlesPerSecondToCreate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2));\n\n            emitter->setCounter(counter);\n\n            ParticleShooter* shooter = new ParticleShooter(partctrl->velocity - partctrl->velocityRandom*0.5f,\n                                                           partctrl->velocity + partctrl->velocityRandom*0.5f,\n                                                           partctrl->horizontalDir, partctrl->horizontalAngle,\n                                                           partctrl->verticalDir, partctrl->verticalAngle,\n                                                           partctrl->lifetime, partctrl->lifetimeRandom);\n            emitter->setShooter(shooter);\n\n            osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer;\n            placer->setXRange(-partctrl->offsetRandom.x() / 2.f, partctrl->offsetRandom.x() / 2.f);\n            placer->setYRange(-partctrl->offsetRandom.y() / 2.f, partctrl->offsetRandom.y() / 2.f);\n            placer->setZRange(-partctrl->offsetRandom.z() / 2.f, partctrl->offsetRandom.z() / 2.f);\n\n            emitter->setPlacer(placer);\n            return emitter;\n        }\n\n        void handleQueuedParticleEmitters(osg::Group* rootNode, Nif::NIFFilePtr nif)\n        {\n            for (const auto& emitterPair : mEmitterQueue)\n            {\n                size_t recIndex = emitterPair.first;\n                FindGroupByRecIndex findEmitterNode(recIndex);\n                rootNode->accept(findEmitterNode);\n                osg::Group* emitterNode = findEmitterNode.mFound;\n                if (!emitterNode)\n                {\n                    nif->warn(\"Failed to find particle emitter emitter node (node record index \" + std::to_string(recIndex) + \")\");\n                    continue;\n                }\n\n                // Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node\n                // actually causes the emitter to stop firing. Convenient, because MW behaves this way too!\n                emitterNode->addChild(emitterPair.second);\n            }\n            mEmitterQueue.clear();\n        }\n\n        void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags)\n        {\n            osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);\n            partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT);\n\n            const Nif::NiParticleSystemController* partctrl = nullptr;\n            for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)\n            {\n                if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))\n                    continue;\n                if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController)\n                    partctrl = static_cast<Nif::NiParticleSystemController*>(ctrl.getPtr());\n            }\n            if (!partctrl)\n            {\n                Log(Debug::Info) << \"No particle controller found in \" << mFilename;\n                return;\n            }\n\n            osgParticle::ParticleProcessor::ReferenceFrame rf = (animflags & Nif::NiNode::ParticleFlag_LocalSpace)\n                    ? osgParticle::ParticleProcessor::RELATIVE_RF\n                    : osgParticle::ParticleProcessor::ABSOLUTE_RF;\n\n            // HACK: ParticleSystem has no setReferenceFrame method\n            if (rf == osgParticle::ParticleProcessor::ABSOLUTE_RF)\n            {\n                partsys->getOrCreateUserDataContainer()->addDescription(\"worldspace\");\n            }\n\n            partsys->setParticleScaleReferenceFrame(osgParticle::ParticleSystem::LOCAL_COORDINATES);\n\n            handleParticleInitialState(nifNode, partsys, partctrl);\n\n            partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->size, partctrl->size));\n            partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(osg::Vec4f(1.f,1.f,1.f,1.f), osg::Vec4f(1.f,1.f,1.f,1.f)));\n            partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f));\n\n            partsys->setFreezeOnCull(true);\n\n            if (!partctrl->emitter.empty())\n            {\n                osg::ref_ptr<Emitter> emitter = handleParticleEmitter(partctrl);\n                emitter->setParticleSystem(partsys);\n                emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF);\n\n                // The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later moment.\n                // If the emitter node is placed later than the particle node, it'll have a single frame delay in particle processing.\n                // But that shouldn't be a game-breaking issue.\n                mEmitterQueue.emplace_back(partctrl->emitter->recIndex, emitter);\n\n                osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));\n                setupController(partctrl, callback, animflags);\n                emitter->setUpdateCallback(callback);\n\n                if (!(animflags & Nif::NiNode::ParticleFlag_AutoPlay))\n                {\n                    partsys->setFrozen(true);\n                }\n\n                // Due to odd code in the ParticleSystemUpdater, particle systems will not be updated in the first frame\n                // So do that update manually\n                osg::NodeVisitor nv;\n                partsys->update(0.0, nv);\n            }\n\n            // affectors should be attached *after* the emitter in the scene graph for correct update order\n            // attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct\n            // localToWorldMatrix for transforming to particle space\n            handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf);\n\n            std::vector<const Nif::Property*> drawableProps;\n            collectDrawableProperties(nifNode, drawableProps);\n            applyDrawableProperties(parentNode, drawableProps, composite, true, animflags);\n\n            // particle system updater (after the emitters and affectors in the scene graph)\n            // I think for correct culling needs to be *before* the ParticleSystem, though osg examples do it the other way\n            osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater;\n            updater->addParticleSystem(partsys);\n            parentNode->addChild(updater);\n\n            osg::Node* toAttach = partsys.get();\n\n            if (rf == osgParticle::ParticleProcessor::RELATIVE_RF)\n                parentNode->addChild(toAttach);\n            else\n            {\n                osg::MatrixTransform* trans = new osg::MatrixTransform;\n                trans->setUpdateCallback(new InverseWorldMatrix);\n                trans->addChild(toAttach);\n                parentNode->addChild(trans);\n            }\n            // create partsys stateset in order to pass in ShaderVisitor like all other Drawables\n            partsys->getOrCreateStateSet();\n        }\n\n        void handleNiGeometryData(osg::Geometry *geometry, const Nif::NiGeometryData* data, const std::vector<unsigned int>& boundTextures, const std::string& name)\n        {\n            const auto& vertices = data->vertices;\n            const auto& normals = data->normals;\n            const auto& colors = data->colors;\n            if (!vertices.empty())\n                geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data()));\n            if (!normals.empty())\n                geometry->setNormalArray(new osg::Vec3Array(normals.size(), normals.data()), osg::Array::BIND_PER_VERTEX);\n            if (!colors.empty())\n                geometry->setColorArray(new osg::Vec4Array(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX);\n\n            const auto& uvlist = data->uvlist;\n            int textureStage = 0;\n            for (const unsigned int uvSet : boundTextures)\n            {\n                if (uvSet >= uvlist.size())\n                {\n                    Log(Debug::Verbose) << \"Out of bounds UV set \" << uvSet << \" on shape \\\"\" << name << \"\\\" in \" << mFilename;\n                    if (!uvlist.empty())\n                        geometry->setTexCoordArray(textureStage, new osg::Vec2Array(uvlist[0].size(), uvlist[0].data()), osg::Array::BIND_PER_VERTEX);\n                    continue;\n                }\n\n                geometry->setTexCoordArray(textureStage, new osg::Vec2Array(uvlist[uvSet].size(), uvlist[uvSet].data()), osg::Array::BIND_PER_VERTEX);\n                textureStage++;\n            }\n        }\n\n        void handleNiGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)\n        {\n            const Nif::NiGeometry* niGeometry = static_cast<const Nif::NiGeometry*>(nifNode);\n            if (niGeometry->data.empty())\n                return;\n            const Nif::NiGeometryData* niGeometryData = niGeometry->data.getPtr();\n\n            if (niGeometry->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_BSLODTriShape)\n            {\n                if (niGeometryData->recType != Nif::RC_NiTriShapeData)\n                    return;\n                auto triangles = static_cast<const Nif::NiTriShapeData*>(niGeometryData)->triangles;\n                if (triangles.empty())\n                    return;\n                geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, triangles.size(),\n                                                                        (unsigned short*)triangles.data()));\n            }\n            else if (niGeometry->recType == Nif::RC_NiTriStrips)\n            {\n                if (niGeometryData->recType != Nif::RC_NiTriStripsData)\n                    return;\n                auto data = static_cast<const Nif::NiTriStripsData*>(niGeometryData);\n                bool hasGeometry = false;\n                for (const auto& strip : data->strips)\n                {\n                    if (strip.size() < 3)\n                        continue;\n                    geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(),\n                                                                            (unsigned short*)strip.data()));\n                    hasGeometry = true;\n                }\n                if (!hasGeometry)\n                    return;\n            }\n            else if (niGeometry->recType == Nif::RC_NiLines)\n            {\n                if (niGeometryData->recType != Nif::RC_NiLinesData)\n                    return;\n                auto data = static_cast<const Nif::NiLinesData*>(niGeometryData);\n                const auto& line = data->lines;\n                if (line.empty())\n                    return;\n                geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(),\n                                                                        (unsigned short*)line.data()));\n            }\n            handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->name);\n\n            // osg::Material properties are handled here for two reasons:\n            // - if there are no vertex colors, we need to disable colorMode.\n            // - there are 3 \"overlapping\" nif properties that all affect the osg::Material, handling them\n            //   above the actual renderable would be tedious.\n            std::vector<const Nif::Property*> drawableProps;\n            collectDrawableProperties(nifNode, drawableProps);\n            applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->colors.empty(), animflags);\n        }\n\n        void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)\n        {\n            assert(isTypeGeometry(nifNode->recType));\n            osg::ref_ptr<osg::Geometry> geom (new osg::Geometry);\n            handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags);\n            // If the record had no valid geometry data in it, early-out\n            if (geom->empty())\n                return;\n            osg::ref_ptr<osg::Drawable> drawable;\n            for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)\n            {\n                if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))\n                    continue;\n                if(ctrl->recType == Nif::RC_NiGeomMorpherController)\n                {\n                    const Nif::NiGeomMorpherController* nimorphctrl = static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());\n                    if (nimorphctrl->data.empty())\n                        continue;\n                    drawable = handleMorphGeometry(nimorphctrl, geom, parentNode, composite, boundTextures, animflags);\n\n                    osg::ref_ptr<GeomMorpherController> morphctrl = new GeomMorpherController(nimorphctrl);\n                    setupController(ctrl.getPtr(), morphctrl, animflags);\n                    drawable->setUpdateCallback(morphctrl);\n                    break;\n                }\n            }\n            if (!drawable.get())\n                drawable = geom;\n            drawable->setName(nifNode->name);\n            parentNode->addChild(drawable);\n        }\n\n        osg::ref_ptr<osg::Drawable> handleMorphGeometry(const Nif::NiGeomMorpherController* morpher, osg::ref_ptr<osg::Geometry> sourceGeometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)\n        {\n            osg::ref_ptr<SceneUtil::MorphGeometry> morphGeom = new SceneUtil::MorphGeometry;\n            morphGeom->setSourceGeometry(sourceGeometry);\n\n            const std::vector<Nif::NiMorphData::MorphData>& morphs = morpher->data.getPtr()->mMorphs;\n            if (morphs.empty())\n                return morphGeom;\n            // Note we are not interested in morph 0, which just contains the original vertices\n            for (unsigned int i = 1; i < morphs.size(); ++i)\n                morphGeom->addMorphTarget(new osg::Vec3Array(morphs[i].mVertices.size(), morphs[i].mVertices.data()), 0.f);\n\n            return morphGeom;\n        }\n\n        void handleSkinnedGeometry(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite,\n                                          const std::vector<unsigned int>& boundTextures, int animflags)\n        {\n            assert(isTypeGeometry(nifNode->recType));\n            osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);\n            handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags);\n            if (geometry->empty())\n                return;\n            osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);\n            rig->setSourceGeometry(geometry);\n            rig->setName(nifNode->name);\n\n            // Assign bone weights\n            osg::ref_ptr<SceneUtil::RigGeometry::InfluenceMap> map (new SceneUtil::RigGeometry::InfluenceMap);\n\n            const Nif::NiSkinInstance *skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin.getPtr();\n            const Nif::NiSkinData *data = skin->data.getPtr();\n            const Nif::NodeList &bones = skin->bones;\n            for(size_t i = 0;i < bones.length();i++)\n            {\n                std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->name);\n\n                SceneUtil::RigGeometry::BoneInfluence influence;\n                const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[i].weights;\n                for(size_t j = 0;j < weights.size();j++)\n                {\n                    influence.mWeights.emplace_back(weights[j].vertex, weights[j].weight);\n                }\n                influence.mInvBindMatrix = data->bones[i].trafo.toMatrix();\n                influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius);\n\n                map->mData.emplace_back(boneName, influence);\n            }\n            rig->setInfluenceMap(map);\n\n            parentNode->addChild(rig);\n        }\n\n        osg::BlendFunc::BlendFuncMode getBlendMode(int mode)\n        {\n            switch(mode)\n            {\n            case 0: return osg::BlendFunc::ONE;\n            case 1: return osg::BlendFunc::ZERO;\n            case 2: return osg::BlendFunc::SRC_COLOR;\n            case 3: return osg::BlendFunc::ONE_MINUS_SRC_COLOR;\n            case 4: return osg::BlendFunc::DST_COLOR;\n            case 5: return osg::BlendFunc::ONE_MINUS_DST_COLOR;\n            case 6: return osg::BlendFunc::SRC_ALPHA;\n            case 7: return osg::BlendFunc::ONE_MINUS_SRC_ALPHA;\n            case 8: return osg::BlendFunc::DST_ALPHA;\n            case 9: return osg::BlendFunc::ONE_MINUS_DST_ALPHA;\n            case 10: return osg::BlendFunc::SRC_ALPHA_SATURATE;\n            default:\n                Log(Debug::Info) << \"Unexpected blend mode: \"<< mode << \" in \" << mFilename;\n                return osg::BlendFunc::SRC_ALPHA;\n            }\n        }\n\n        osg::AlphaFunc::ComparisonFunction getTestMode(int mode)\n        {\n            switch (mode)\n            {\n            case 0: return osg::AlphaFunc::ALWAYS;\n            case 1: return osg::AlphaFunc::LESS;\n            case 2: return osg::AlphaFunc::EQUAL;\n            case 3: return osg::AlphaFunc::LEQUAL;\n            case 4: return osg::AlphaFunc::GREATER;\n            case 5: return osg::AlphaFunc::NOTEQUAL;\n            case 6: return osg::AlphaFunc::GEQUAL;\n            case 7: return osg::AlphaFunc::NEVER;\n            default:\n                Log(Debug::Info) << \"Unexpected blend mode: \" << mode << \" in \" << mFilename;\n                return osg::AlphaFunc::LEQUAL;\n            }\n        }\n\n        osg::Stencil::Function getStencilFunction(int func)\n        {\n            switch (func)\n            {\n            case 0: return osg::Stencil::NEVER;\n            case 1: return osg::Stencil::LESS;\n            case 2: return osg::Stencil::EQUAL;\n            case 3: return osg::Stencil::LEQUAL;\n            case 4: return osg::Stencil::GREATER;\n            case 5: return osg::Stencil::NOTEQUAL;\n            case 6: return osg::Stencil::GEQUAL;\n            case 7: return osg::Stencil::NEVER; // NifSkope says this is GL_ALWAYS, but in MW it's GL_NEVER\n            default:\n                Log(Debug::Info) << \"Unexpected stencil function: \" << func << \" in \" << mFilename;\n                return osg::Stencil::NEVER;\n            }\n        }\n\n        osg::Stencil::Operation getStencilOperation(int op)\n        {\n            switch (op)\n            {\n            case 0: return osg::Stencil::KEEP;\n            case 1: return osg::Stencil::ZERO;\n            case 2: return osg::Stencil::REPLACE;\n            case 3: return osg::Stencil::INCR;\n            case 4: return osg::Stencil::DECR;\n            case 5: return osg::Stencil::INVERT;\n            default:\n                Log(Debug::Info) << \"Unexpected stencil operation: \" << op << \" in \" << mFilename;\n                return osg::Stencil::KEEP;\n            }\n        }\n\n        osg::ref_ptr<osg::Image> handleInternalTexture(const Nif::NiPixelData* pixelData)\n        {\n            osg::ref_ptr<osg::Image> image (new osg::Image);\n\n            GLenum pixelformat = 0;\n            switch (pixelData->fmt)\n            {\n            case Nif::NiPixelData::NIPXFMT_RGB8:\n            case Nif::NiPixelData::NIPXFMT_PAL8:\n                pixelformat = GL_RGB;\n                break;\n            case Nif::NiPixelData::NIPXFMT_RGBA8:\n            case Nif::NiPixelData::NIPXFMT_PALA8:\n                pixelformat = GL_RGBA;\n                break;\n            default:\n                Log(Debug::Info) << \"Unhandled internal pixel format \" << pixelData->fmt << \" in \" << mFilename;\n                return nullptr;\n            }\n\n            if (pixelData->mipmaps.empty())\n                return nullptr;\n\n            int width = 0;\n            int height = 0;\n\n            std::vector<unsigned int> mipmapVector;\n            for (unsigned int i=0; i<pixelData->mipmaps.size(); ++i)\n            {\n                const Nif::NiPixelData::Mipmap& mip = pixelData->mipmaps[i];\n\n                size_t mipSize = mip.height * mip.width * pixelData->bpp / 8;\n                if (mipSize + mip.dataOffset > pixelData->data.size())\n                {\n                    Log(Debug::Info) << \"Internal texture's mipmap data out of bounds, ignoring texture\";\n                    return nullptr;\n                }\n\n                if (i != 0)\n                    mipmapVector.push_back(mip.dataOffset);\n                else\n                {\n                    width = mip.width;\n                    height = mip.height;\n                }\n            }\n\n            if (width <= 0 || height <= 0)\n            {\n                Log(Debug::Info) << \"Internal Texture Width and height must be non zero, ignoring texture\";\n                return nullptr;\n            }\n\n            const std::vector<unsigned char>& pixels = pixelData->data;\n            switch (pixelData->fmt)\n            {\n            case Nif::NiPixelData::NIPXFMT_RGB8:\n            case Nif::NiPixelData::NIPXFMT_RGBA8:\n            {\n                unsigned char* data = new unsigned char[pixels.size()];\n                memcpy(data, pixels.data(), pixels.size());\n                image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);\n                break;\n            }\n            case Nif::NiPixelData::NIPXFMT_PAL8:\n            case Nif::NiPixelData::NIPXFMT_PALA8:\n            {\n                if (pixelData->palette.empty() || pixelData->bpp != 8)\n                {\n                    Log(Debug::Info) << \"Palettized texture in \" << mFilename << \" is invalid, ignoring\";\n                    return nullptr;\n                }\n                // We're going to convert the indices that pixel data contains\n                // into real colors using the palette.\n                const auto& palette = pixelData->palette->colors;\n                const int numChannels = pixelformat == GL_RGBA ? 4 : 3;\n                unsigned char* data = new unsigned char[pixels.size() * numChannels];\n                unsigned char* pixel = data;\n                for (unsigned char index : pixels)\n                {\n                    memcpy(pixel, &palette[index], sizeof(unsigned char) * numChannels);\n                    pixel += numChannels;\n                }\n                image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);\n                break;\n            }\n            default:\n                return nullptr;\n            }\n\n            image->setMipmapLevels(mipmapVector);\n            image->flipVertical();\n\n            return image;\n        }\n\n        osg::ref_ptr<osg::TexEnvCombine> createEmissiveTexEnv()\n        {\n            osg::ref_ptr<osg::TexEnvCombine> texEnv(new osg::TexEnvCombine);\n            texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE);\n            texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);\n            texEnv->setCombine_RGB(osg::TexEnvCombine::ADD);\n            texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);\n            texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE);\n            return texEnv;\n        }\n\n        void handleTextureProperty(const Nif::NiTexturingProperty* texprop, const std::string& nodeName, osg::StateSet* stateset, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<unsigned int>& boundTextures, int animflags)\n        {\n            if (!boundTextures.empty())\n            {\n                // overriding a parent NiTexturingProperty, so remove what was previously bound\n                for (unsigned int i=0; i<boundTextures.size(); ++i)\n                    stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);\n                boundTextures.clear();\n            }\n\n            // If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the shadow casting shader will need to be updated accordingly.\n            for (size_t i=0; i<texprop->textures.size(); ++i)\n            {\n                if (texprop->textures[i].inUse || (i == Nif::NiTexturingProperty::BaseTexture && !texprop->controller.empty()))\n                {\n                    switch(i)\n                    {\n                        //These are handled later on\n                        case Nif::NiTexturingProperty::BaseTexture:\n                        case Nif::NiTexturingProperty::GlowTexture:\n                        case Nif::NiTexturingProperty::DarkTexture:\n                        case Nif::NiTexturingProperty::BumpTexture:\n                        case Nif::NiTexturingProperty::DetailTexture:\n                        case Nif::NiTexturingProperty::DecalTexture:\n                            break;\n                        case Nif::NiTexturingProperty::GlossTexture:\n                        {\n                            // Not used by the vanilla engine. MCP (Morrowind Code Patch) adds an option to use Gloss maps:\n                            // \"- Gloss map fix. Morrowind removed gloss map entries from model files after loading them. This stops Morrowind from removing them.\"\n                            // Log(Debug::Info) << \"NiTexturingProperty::GlossTexture in \" << mFilename << \" not currently used.\";\n                            continue;\n                        }\n                        default:\n                        {\n                            Log(Debug::Info) << \"Unhandled texture stage \" << i << \" on shape \\\"\" << nodeName << \"\\\" in \" << mFilename;\n                            continue;\n                        }\n                    }\n\n                    unsigned int uvSet = 0;\n                    // create a new texture, will later attempt to share using the SharedStateManager\n                    osg::ref_ptr<osg::Texture2D> texture2d;\n                    if (texprop->textures[i].inUse)\n                    {\n                        const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i];\n                        if(tex.texture.empty() && texprop->controller.empty())\n                        {\n                            if (i == 0)\n                                Log(Debug::Warning) << \"Base texture is in use but empty on shape \\\"\" << nodeName << \"\\\" in \" << mFilename;\n                            continue;\n                        }\n\n                        if (!tex.texture.empty())\n                        {\n                            const Nif::NiSourceTexture *st = tex.texture.getPtr();\n                            osg::ref_ptr<osg::Image> image = handleSourceTexture(st, imageManager);\n                            texture2d = new osg::Texture2D(image);\n                            if (image)\n                                texture2d->setTextureSize(image->s(), image->t());\n                        }\n                        else\n                            texture2d = new osg::Texture2D;\n\n                        bool wrapT = tex.clamp & 0x1;\n                        bool wrapS = (tex.clamp >> 1) & 0x1;\n\n                        texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);\n                        texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);\n\n                        uvSet = tex.uvSet;\n                    }\n                    else\n                    {\n                        // Texture only comes from NiFlipController, so tex is ignored, set defaults\n                        texture2d = new osg::Texture2D;\n                        texture2d->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n                        texture2d->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n                        uvSet = 0;\n                    }\n\n                    unsigned int texUnit = boundTextures.size();\n\n                    stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);\n\n                    if (i == Nif::NiTexturingProperty::GlowTexture)\n                    {\n                        stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON);\n                    }\n                    else if (i == Nif::NiTexturingProperty::DarkTexture)\n                    {\n                        osg::TexEnv* texEnv = new osg::TexEnv;\n                        texEnv->setMode(osg::TexEnv::MODULATE);\n                        stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON);\n                    }\n                    else if (i == Nif::NiTexturingProperty::DetailTexture)\n                    {\n                        osg::TexEnvCombine* texEnv = new osg::TexEnvCombine;\n                        texEnv->setScale_RGB(2.f);\n                        texEnv->setCombine_Alpha(osg::TexEnvCombine::MODULATE);\n                        texEnv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);\n                        texEnv->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);\n                        texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);\n                        texEnv->setSource1_Alpha(osg::TexEnvCombine::TEXTURE);\n                        texEnv->setCombine_RGB(osg::TexEnvCombine::MODULATE);\n                        texEnv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);\n                        texEnv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);\n                        texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);\n                        texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE);\n                        stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON);\n                    }\n                    else if (i == Nif::NiTexturingProperty::BumpTexture)\n                    {\n                        // Set this texture to Off by default since we can't render it with the fixed-function pipeline\n                        stateset->setTextureMode(texUnit, GL_TEXTURE_2D, osg::StateAttribute::OFF);\n                        osg::Matrix2 bumpMapMatrix(texprop->bumpMapMatrix.x(), texprop->bumpMapMatrix.y(),\n                                                   texprop->bumpMapMatrix.z(), texprop->bumpMapMatrix.w());\n                        stateset->addUniform(new osg::Uniform(\"bumpMapMatrix\", bumpMapMatrix));\n                        stateset->addUniform(new osg::Uniform(\"envMapLumaBias\", texprop->envMapLumaBias));\n                    }\n                    else if (i == Nif::NiTexturingProperty::DecalTexture)\n                    {\n                         osg::TexEnvCombine* texEnv = new osg::TexEnvCombine;\n                         texEnv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE);\n                         texEnv->setSource0_RGB(osg::TexEnvCombine::TEXTURE);\n                         texEnv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);\n                         texEnv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS);\n                         texEnv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);\n                         texEnv->setSource2_RGB(osg::TexEnvCombine::TEXTURE);\n                         texEnv->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA);\n                         texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE);\n                         texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);\n                         texEnv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);\n                         stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON);\n                    }\n\n                    switch (i)\n                    {\n                    case Nif::NiTexturingProperty::BaseTexture:\n                        texture2d->setName(\"diffuseMap\");\n                        break;\n                    case Nif::NiTexturingProperty::BumpTexture:\n                        texture2d->setName(\"bumpMap\");\n                        break;\n                    case Nif::NiTexturingProperty::GlowTexture:\n                        texture2d->setName(\"emissiveMap\");\n                        break;\n                    case Nif::NiTexturingProperty::DarkTexture:\n                        texture2d->setName(\"darkMap\");\n                        break;\n                    case Nif::NiTexturingProperty::DetailTexture:\n                        texture2d->setName(\"detailMap\");\n                        break;\n                    case Nif::NiTexturingProperty::DecalTexture:\n                        texture2d->setName(\"decalMap\");\n                        break;\n                    default:\n                        break;\n                    }\n\n                    boundTextures.push_back(uvSet);\n                }\n            }\n            handleTextureControllers(texprop, composite, imageManager, stateset, animflags);\n        }\n\n        void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, unsigned int clamp, const std::string& nodeName, osg::StateSet* stateset, Resource::ImageManager* imageManager, std::vector<unsigned int>& boundTextures)\n        {\n            if (!boundTextures.empty())\n            {\n                for (unsigned int i = 0; i < boundTextures.size(); ++i)\n                    stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);\n                boundTextures.clear();\n            }\n\n            const unsigned int uvSet = 0;\n\n            for (size_t i = 0; i < textureSet->textures.size(); ++i)\n            {\n                if (textureSet->textures[i].empty())\n                    continue;\n                switch(i)\n                {\n                    case Nif::BSShaderTextureSet::TextureType_Base:\n                    case Nif::BSShaderTextureSet::TextureType_Normal:\n                    case Nif::BSShaderTextureSet::TextureType_Glow:\n                        break;\n                    default:\n                    {\n                        Log(Debug::Info) << \"Unhandled texture stage \" << i << \" on shape \\\"\" << nodeName << \"\\\" in \" << mFilename;\n                        continue;\n                    }\n                }\n                std::string filename = Misc::ResourceHelpers::correctTexturePath(textureSet->textures[i], imageManager->getVFS());\n                osg::ref_ptr<osg::Image> image = imageManager->getImage(filename);\n                osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);\n                if (image)\n                    texture2d->setTextureSize(image->s(), image->t());\n                bool wrapT = clamp & 0x1;\n                bool wrapS = (clamp >> 1) & 0x1;\n                texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);\n                texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);\n                unsigned int texUnit = boundTextures.size();\n                stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);\n                // BSShaderTextureSet presence means there's no need for FFP support for the affected node\n                switch (i)\n                {\n                    case Nif::BSShaderTextureSet::TextureType_Base:\n                        texture2d->setName(\"diffuseMap\");\n                        break;\n                    case Nif::BSShaderTextureSet::TextureType_Normal:\n                        texture2d->setName(\"normalMap\");\n                        break;\n                    case Nif::BSShaderTextureSet::TextureType_Glow:\n                        texture2d->setName(\"emissiveMap\");\n                        break;\n                }\n                boundTextures.emplace_back(uvSet);\n            }\n        }\n\n        const std::string& getNVShaderPrefix(unsigned int type) const\n        {\n            static const std::map<unsigned int, std::string> mapping =\n            {\n                {Nif::BSShaderProperty::SHADER_TALL_GRASS, std::string()},\n                {Nif::BSShaderProperty::SHADER_DEFAULT,    \"nv_default\"},\n                {Nif::BSShaderProperty::SHADER_SKY,        std::string()},\n                {Nif::BSShaderProperty::SHADER_SKIN,       std::string()},\n                {Nif::BSShaderProperty::SHADER_WATER,      std::string()},\n                {Nif::BSShaderProperty::SHADER_LIGHTING30, std::string()},\n                {Nif::BSShaderProperty::SHADER_TILE,       std::string()},\n                {Nif::BSShaderProperty::SHADER_NOLIGHTING, \"nv_nolighting\"},\n            };\n            auto prefix = mapping.find(type);\n            if (prefix == mapping.end())\n                Log(Debug::Warning) << \"Unknown shader type \" << type << \" in \" << mFilename;\n            else if (prefix->second.empty())\n                Log(Debug::Warning) << \"Unhandled shader type \" << type << \" in \" << mFilename;\n            else\n                return prefix->second;\n\n            return mapping.at(Nif::BSShaderProperty::SHADER_DEFAULT);\n        }\n\n        void handleProperty(const Nif::Property *property,\n                            osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<unsigned int>& boundTextures, int animflags)\n        {\n            switch (property->recType)\n            {\n            case Nif::RC_NiStencilProperty:\n            {\n                const Nif::NiStencilProperty* stencilprop = static_cast<const Nif::NiStencilProperty*>(property);\n                osg::ref_ptr<osg::FrontFace> frontFace = new osg::FrontFace;\n                switch (stencilprop->data.drawMode)\n                {\n                case 2:\n                    frontFace->setMode(osg::FrontFace::CLOCKWISE);\n                    break;\n                case 0:\n                case 1:\n                default:\n                    frontFace->setMode(osg::FrontFace::COUNTER_CLOCKWISE);\n                    break;\n                }\n                frontFace = shareAttribute(frontFace);\n\n                osg::StateSet* stateset = node->getOrCreateStateSet();\n                stateset->setAttribute(frontFace, osg::StateAttribute::ON);\n                stateset->setMode(GL_CULL_FACE, stencilprop->data.drawMode == 3 ? osg::StateAttribute::OFF\n                                                                                : osg::StateAttribute::ON);\n\n                if (stencilprop->data.enabled != 0)\n                {\n                    osg::ref_ptr<osg::Stencil> stencil = new osg::Stencil;\n                    stencil->setFunction(getStencilFunction(stencilprop->data.compareFunc), stencilprop->data.stencilRef, stencilprop->data.stencilMask);\n                    stencil->setStencilFailOperation(getStencilOperation(stencilprop->data.failAction));\n                    stencil->setStencilPassAndDepthFailOperation(getStencilOperation(stencilprop->data.zFailAction));\n                    stencil->setStencilPassAndDepthPassOperation(getStencilOperation(stencilprop->data.zPassAction));\n                    stencil = shareAttribute(stencil);\n\n                    stateset->setAttributeAndModes(stencil, osg::StateAttribute::ON);\n                }\n                break;\n            }\n            case Nif::RC_NiWireframeProperty:\n            {\n                const Nif::NiWireframeProperty* wireprop = static_cast<const Nif::NiWireframeProperty*>(property);\n                osg::ref_ptr<osg::PolygonMode> mode = new osg::PolygonMode;\n                mode->setMode(osg::PolygonMode::FRONT_AND_BACK, wireprop->flags == 0 ? osg::PolygonMode::FILL\n                                                                                     : osg::PolygonMode::LINE);\n                mode = shareAttribute(mode);\n                node->getOrCreateStateSet()->setAttributeAndModes(mode, osg::StateAttribute::ON);\n                break;\n            }\n            case Nif::RC_NiZBufferProperty:\n            {\n                const Nif::NiZBufferProperty* zprop = static_cast<const Nif::NiZBufferProperty*>(property);\n                osg::StateSet* stateset = node->getOrCreateStateSet();\n                // Depth test flag\n                stateset->setMode(GL_DEPTH_TEST, zprop->flags&1 ? osg::StateAttribute::ON\n                                                                : osg::StateAttribute::OFF);\n                osg::ref_ptr<osg::Depth> depth = new osg::Depth;\n                // Depth write flag\n                depth->setWriteMask((zprop->flags>>1)&1);\n                // Morrowind ignores depth test function\n                depth = shareAttribute(depth);\n                stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);\n                break;\n            }\n            // OSG groups the material properties that NIFs have separate, so we have to parse them all again when one changed\n            case Nif::RC_NiMaterialProperty:\n            case Nif::RC_NiVertexColorProperty:\n            case Nif::RC_NiSpecularProperty:\n            {\n                // Handled on drawable level so we know whether vertex colors are available\n                break;\n            }\n            case Nif::RC_NiAlphaProperty:\n            {\n                // Handled on drawable level to prevent RenderBin nesting issues\n                break;\n            }\n            case Nif::RC_NiTexturingProperty:\n            {\n                const Nif::NiTexturingProperty* texprop = static_cast<const Nif::NiTexturingProperty*>(property);\n                osg::StateSet* stateset = node->getOrCreateStateSet();\n                handleTextureProperty(texprop, node->getName(), stateset, composite, imageManager, boundTextures, animflags);\n                break;\n            }\n            case Nif::RC_BSShaderPPLightingProperty:\n            {\n                auto texprop = static_cast<const Nif::BSShaderPPLightingProperty*>(property);\n                bool shaderRequired = true;\n                node->setUserValue(\"shaderPrefix\", getNVShaderPrefix(texprop->type));\n                node->setUserValue(\"shaderRequired\", shaderRequired);\n                osg::StateSet* stateset = node->getOrCreateStateSet();\n                if (!texprop->textureSet.empty())\n                {\n                    auto textureSet = texprop->textureSet.getPtr();\n                    handleTextureSet(textureSet, texprop->clamp, node->getName(), stateset, imageManager, boundTextures);\n                }\n                handleTextureControllers(texprop, composite, imageManager, stateset, animflags);\n                break;\n            }\n            case Nif::RC_BSShaderNoLightingProperty:\n            {\n                auto texprop = static_cast<const Nif::BSShaderNoLightingProperty*>(property);\n                bool shaderRequired = true;\n                node->setUserValue(\"shaderPrefix\", getNVShaderPrefix(texprop->type));\n                node->setUserValue(\"shaderRequired\", shaderRequired);\n                osg::StateSet* stateset = node->getOrCreateStateSet();\n                if (!texprop->filename.empty())\n                {\n                    if (!boundTextures.empty())\n                    {\n                        for (unsigned int i = 0; i < boundTextures.size(); ++i)\n                            stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);\n                        boundTextures.clear();\n                    }\n                    std::string filename = Misc::ResourceHelpers::correctTexturePath(texprop->filename, imageManager->getVFS());\n                    osg::ref_ptr<osg::Image> image = imageManager->getImage(filename);\n                    osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);\n                    texture2d->setName(\"diffuseMap\");\n                    if (image)\n                        texture2d->setTextureSize(image->s(), image->t());\n                    bool wrapT = texprop->clamp & 0x1;\n                    bool wrapS = (texprop->clamp >> 1) & 0x1;\n                    texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);\n                    texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);\n                    const unsigned int texUnit = 0;\n                    const unsigned int uvSet = 0;\n                    stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);\n                    boundTextures.push_back(uvSet);\n                }\n                if (mBethVersion >= 27)\n                {\n                    stateset->addUniform(new osg::Uniform(\"useFalloff\", true));\n                    stateset->addUniform(new osg::Uniform(\"falloffParams\", texprop->falloffParams));\n                }\n                else\n                {\n                    stateset->addUniform(new osg::Uniform(\"useFalloff\", false));\n                }\n                handleTextureControllers(texprop, composite, imageManager, stateset, animflags);\n                break;\n            }\n            // unused by mw\n            case Nif::RC_NiShadeProperty:\n            case Nif::RC_NiDitherProperty:\n            case Nif::RC_NiFogProperty:\n            {\n                break;\n            }\n            default:\n                Log(Debug::Info) << \"Unhandled \" << property->recName << \" in \" << mFilename;\n                break;\n            }\n        }\n\n        struct CompareStateAttribute\n        {\n            bool operator() (const osg::ref_ptr<osg::StateAttribute>& left, const osg::ref_ptr<osg::StateAttribute>& right) const\n            {\n                return left->compare(*right) < 0;\n            }\n        };\n\n        // global sharing of State Attributes will reduce the number of GL calls as the osg::State will check by pointer to see if state is the same\n        template <class Attribute>\n        Attribute* shareAttribute(const osg::ref_ptr<Attribute>& attr)\n        {\n            typedef std::set<osg::ref_ptr<Attribute>, CompareStateAttribute> Cache;\n            static Cache sCache;\n            static std::mutex sMutex;\n            std::lock_guard<std::mutex> lock(sMutex);\n            typename Cache::iterator found = sCache.find(attr);\n            if (found == sCache.end())\n                found = sCache.insert(attr).first;\n            return *found;\n        }\n\n        void applyDrawableProperties(osg::Node* node, const std::vector<const Nif::Property*>& properties, SceneUtil::CompositeStateSetUpdater* composite,\n                                             bool hasVertexColors, int animflags)\n        {\n            osg::StateSet* stateset = node->getOrCreateStateSet();\n\n            // Specular lighting is enabled by default, but there's a quirk...\n            bool specEnabled = true;\n            osg::ref_ptr<osg::Material> mat (new osg::Material);\n            mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF);\n\n            // NIF material defaults don't match OpenGL defaults\n            mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n            mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n\n            bool hasMatCtrl = false;\n\n            int lightmode = 1;\n            float emissiveMult = 1.f;\n\n            for (const Nif::Property* property : properties)\n            {\n                switch (property->recType)\n                {\n                case Nif::RC_NiSpecularProperty:\n                {\n                    // Specular property can turn specular lighting off.\n                    auto specprop = static_cast<const Nif::NiSpecularProperty*>(property);\n                    specEnabled = specprop->flags & 1;\n                    break;\n                }\n                case Nif::RC_NiMaterialProperty:\n                {\n                    const Nif::NiMaterialProperty* matprop = static_cast<const Nif::NiMaterialProperty*>(property);\n\n                    mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.diffuse, matprop->data.alpha));\n                    mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.ambient, 1.f));\n                    mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.emissive, 1.f));\n                    emissiveMult = matprop->data.emissiveMult;\n\n                    mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f));\n                    mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness);\n\n                    if (!matprop->controller.empty())\n                    {\n                        hasMatCtrl = true;\n                        handleMaterialControllers(matprop, composite, animflags, mat);\n                    }\n\n                    break;\n                }\n                case Nif::RC_NiVertexColorProperty:\n                {\n                    const Nif::NiVertexColorProperty* vertprop = static_cast<const Nif::NiVertexColorProperty*>(property);\n                    lightmode = vertprop->data.lightmode;\n\n                    switch (vertprop->data.vertmode)\n                    {\n                        case 0:\n                            mat->setColorMode(osg::Material::OFF);\n                            break;\n                        case 1:\n                            mat->setColorMode(osg::Material::EMISSION);\n                            break;\n                        case 2:\n                            if (lightmode != 0)\n                                mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);\n                            else\n                                mat->setColorMode(osg::Material::OFF);\n                            break;\n                    }\n                    break;\n                }\n                case Nif::RC_NiAlphaProperty:\n                {\n                    const Nif::NiAlphaProperty* alphaprop = static_cast<const Nif::NiAlphaProperty*>(property);\n                    if (alphaprop->flags&1)\n                    {\n                        osg::ref_ptr<osg::BlendFunc> blendFunc (new osg::BlendFunc(getBlendMode((alphaprop->flags>>1)&0xf),\n                                                                                   getBlendMode((alphaprop->flags>>5)&0xf)));\n                        // on AMD hardware, alpha still seems to be stored with an RGBA framebuffer with OpenGL.\n                        // This might be mandated by the OpenGL 2.1 specification section 2.14.9, or might be a bug.\n                        // Either way, D3D8.1 doesn't do that, so adapt the destination factor.\n                        if (blendFunc->getDestination() == GL_DST_ALPHA)\n                            blendFunc->setDestination(GL_ONE);\n                        blendFunc = shareAttribute(blendFunc);\n                        stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);\n\n                        bool noSort = (alphaprop->flags>>13)&1;\n                        if (!noSort)\n                            stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n                        else\n                            stateset->setRenderBinToInherit();\n                    }\n                    else\n                    {\n                        stateset->removeAttribute(osg::StateAttribute::BLENDFUNC);\n                        stateset->removeMode(GL_BLEND);\n                        stateset->setRenderBinToInherit();\n                    }\n\n                    if((alphaprop->flags>>9)&1)\n                    {\n                        osg::ref_ptr<osg::AlphaFunc> alphaFunc (new osg::AlphaFunc(getTestMode((alphaprop->flags>>10)&0x7), alphaprop->data.threshold/255.f));\n                        alphaFunc = shareAttribute(alphaFunc);\n                        stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON);\n                    }\n                    else\n                    {\n                        stateset->removeAttribute(osg::StateAttribute::ALPHAFUNC);\n                        stateset->removeMode(GL_ALPHA_TEST);\n                    }\n                    break;\n                }\n                }\n            }\n\n            // While NetImmerse and Gamebryo support specular lighting, Morrowind has its support disabled.\n            if (mVersion <= Nif::NIFFile::NIFVersion::VER_MW || !specEnabled)\n                mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f));\n\n            if (lightmode == 0)\n            {\n                osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);\n                diffuse = osg::Vec4f(0,0,0,diffuse.a());\n                mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);\n                mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f());\n            }\n\n            // If we're told to use vertex colors but there are none to use, use a default color instead.\n            if (!hasVertexColors)\n            {\n                switch (mat->getColorMode())\n                {\n                case osg::Material::AMBIENT:\n                    mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n                    break;\n                case osg::Material::AMBIENT_AND_DIFFUSE:\n                    mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n                    mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n                    break;\n                case osg::Material::EMISSION:\n                    mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));\n                    break;\n                default:\n                    break;\n                }\n                mat->setColorMode(osg::Material::OFF);\n            }\n\n            if (!hasMatCtrl && mat->getColorMode() == osg::Material::OFF\n                    && mat->getEmission(osg::Material::FRONT_AND_BACK) == osg::Vec4f(0,0,0,1)\n                    && mat->getDiffuse(osg::Material::FRONT_AND_BACK) == osg::Vec4f(1,1,1,1)\n                    && mat->getAmbient(osg::Material::FRONT_AND_BACK) == osg::Vec4f(1,1,1,1)\n                    && mat->getShininess(osg::Material::FRONT_AND_BACK) == 0\n                    && mat->getSpecular(osg::Material::FRONT_AND_BACK) == osg::Vec4f(0.f, 0.f, 0.f, 0.f))\n            {\n                // default state, skip\n                return;\n            }\n\n            mat = shareAttribute(mat);\n\n            stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);\n            stateset->addUniform(new osg::Uniform(\"emissiveMult\", emissiveMult));\n        }\n\n    };\n\n    osg::ref_ptr<osg::Node> Loader::load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager)\n    {\n        LoaderImpl impl(file->getFilename(), file->getVersion(), file->getUserVersion(), file->getBethVersion());\n        return impl.load(file, imageManager);\n    }\n\n    void Loader::loadKf(Nif::NIFFilePtr kf, SceneUtil::KeyframeHolder& target)\n    {\n        LoaderImpl impl(kf->getFilename(), kf->getVersion(), kf->getUserVersion(), kf->getBethVersion());\n        impl.loadKf(kf, target);\n    }\n\n}\n"
  },
  {
    "path": "components/nifosg/nifloader.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_NIFOSG_LOADER\n#define OPENMW_COMPONENTS_NIFOSG_LOADER\n\n#include <components/nif/niffile.hpp>\n#include <components/sceneutil/keyframe.hpp>\n#include <components/sceneutil/textkeymap.hpp>\n\n#include <osg/ref_ptr>\n#include <osg/Referenced>\n\n#include \"controller.hpp\"\n\nnamespace osg\n{\n    class Node;\n}\n\nnamespace Resource\n{\n    class ImageManager;\n}\n\nnamespace NifOsg\n{\n    /// The main class responsible for loading NIF files into an OSG-Scenegraph.\n    /// @par This scene graph is self-contained and can be cloned using osg::clone if desired. Particle emitters\n    ///      and programs hold a pointer to their ParticleSystem, which would need to be manually updated when cloning.\n    class Loader\n    {\n    public:\n        /// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton if so.\n        static osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager);\n\n        /// Load keyframe controllers from the given kf file.\n        static void loadKf(Nif::NIFFilePtr kf, SceneUtil::KeyframeHolder& target);\n\n        /// Set whether or not nodes marked as \"MRK\" should be shown.\n        /// These should be hidden ingame, but visible in the editor.\n        /// Default: false.\n        static void setShowMarkers(bool show);\n\n        static bool getShowMarkers();\n\n        /// Set the mask to use for hidden nodes. The default is 0, i.e. updates to those nodes can no longer happen.\n        /// If you need to run animations or physics for hidden nodes, you may want to set this to a non-zero mask and remove exactly that mask from the camera's cull mask.\n        static void setHiddenNodeMask(unsigned int mask);\n        static unsigned int getHiddenNodeMask();\n\n        // Set the mask to use for nodes that ignore the crosshair intersection. The default is the default node mask.\n        // This is used for NiCollisionSwitch nodes with NiCollisionSwitch state set to disabled.\n        static void setIntersectionDisabledNodeMask(unsigned int mask);\n        static unsigned int getIntersectionDisabledNodeMask();\n\n    private:\n        static unsigned int sHiddenNodeMask;\n        static unsigned int sIntersectionDisabledNodeMask;\n        static bool sShowMarkers;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/nifosg/particle.cpp",
    "content": "#include \"particle.hpp\"\n\n#include <limits>\n\n#include <osg/Version>\n#include <osg/MatrixTransform>\n#include <osg/Geometry>\n#include <osg/ValueObject>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/rng.hpp>\n#include <components/nif/controlled.hpp>\n#include <components/nif/data.hpp>\n\nnamespace NifOsg\n{\n\nParticleSystem::ParticleSystem()\n    : osgParticle::ParticleSystem()\n    , mQuota(std::numeric_limits<int>::max())\n{\n    mNormalArray = new osg::Vec3Array(1);\n    mNormalArray->setBinding(osg::Array::BIND_OVERALL);\n    (*mNormalArray.get())[0] = osg::Vec3(0.3, 0.3, 0.3);\n}\n\nParticleSystem::ParticleSystem(const ParticleSystem &copy, const osg::CopyOp &copyop)\n    : osgParticle::ParticleSystem(copy, copyop)\n    , mQuota(copy.mQuota)\n{\n    mNormalArray = new osg::Vec3Array(1);\n    mNormalArray->setBinding(osg::Array::BIND_OVERALL);\n    (*mNormalArray.get())[0] = osg::Vec3(0.3, 0.3, 0.3);\n\n    // For some reason the osgParticle constructor doesn't copy the particles\n    for (int i=0;i<copy.numParticles()-copy.numDeadParticles();++i)\n        ParticleSystem::createParticle(copy.getParticle(i));\n}\n\nvoid ParticleSystem::setQuota(int quota)\n{\n    mQuota = quota;\n}\n\nosgParticle::Particle* ParticleSystem::createParticle(const osgParticle::Particle *ptemplate)\n{\n    if (numParticles()-numDeadParticles() < mQuota)\n        return osgParticle::ParticleSystem::createParticle(ptemplate);\n    return nullptr;\n}\n\nvoid ParticleSystem::drawImplementation(osg::RenderInfo& renderInfo) const\n{\n    osg::State & state = *renderInfo.getState();\n#if OSG_MIN_VERSION_REQUIRED(3, 5, 6)\n    if(state.useVertexArrayObject(getUseVertexArrayObject()))\n    {\n        state.getCurrentVertexArrayState()->assignNormalArrayDispatcher();\n        state.getCurrentVertexArrayState()->setNormalArray(state, mNormalArray);\n    }\n    else\n    {\n        state.getAttributeDispatchers().activateNormalArray(mNormalArray);\n    }\n#else\n     state.Normal(0.3, 0.3, 0.3);\n#endif\n     osgParticle::ParticleSystem::drawImplementation(renderInfo);\n}\n\nvoid InverseWorldMatrix::operator()(osg::Node *node, osg::NodeVisitor *nv)\n{\n    if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)\n    {\n        osg::NodePath path = nv->getNodePath();\n        path.pop_back();\n\n        osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(node);\n\n        osg::Matrix mat = osg::computeLocalToWorld( path );\n        mat.orthoNormalize(mat); // don't undo the scale\n        mat.invert(mat);\n        trans->setMatrix(mat);\n    }\n    traverse(node,nv);\n}\n\nParticleShooter::ParticleShooter(float minSpeed, float maxSpeed, float horizontalDir, float horizontalAngle, float verticalDir, float verticalAngle, float lifetime, float lifetimeRandom)\n    : mMinSpeed(minSpeed), mMaxSpeed(maxSpeed), mHorizontalDir(horizontalDir)\n    , mHorizontalAngle(horizontalAngle), mVerticalDir(verticalDir), mVerticalAngle(verticalAngle)\n    , mLifetime(lifetime), mLifetimeRandom(lifetimeRandom)\n{\n}\n\nParticleShooter::ParticleShooter()\n    : mMinSpeed(0.f), mMaxSpeed(0.f), mHorizontalDir(0.f)\n    , mHorizontalAngle(0.f), mVerticalDir(0.f), mVerticalAngle(0.f)\n    , mLifetime(0.f), mLifetimeRandom(0.f)\n{\n}\n\nParticleShooter::ParticleShooter(const ParticleShooter &copy, const osg::CopyOp &copyop)\n    : osgParticle::Shooter(copy, copyop)\n{\n    mMinSpeed = copy.mMinSpeed;\n    mMaxSpeed = copy.mMaxSpeed;\n    mHorizontalDir = copy.mHorizontalDir;\n    mHorizontalAngle = copy.mHorizontalAngle;\n    mVerticalDir = copy.mVerticalDir;\n    mVerticalAngle = copy.mVerticalAngle;\n    mLifetime = copy.mLifetime;\n    mLifetimeRandom = copy.mLifetimeRandom;\n}\n\nvoid ParticleShooter::shoot(osgParticle::Particle *particle) const\n{\n    float hdir = mHorizontalDir + mHorizontalAngle * (2.f * Misc::Rng::rollClosedProbability() - 1.f);\n    float vdir = mVerticalDir + mVerticalAngle * (2.f * Misc::Rng::rollClosedProbability() - 1.f);\n\n    osg::Vec3f dir = (osg::Quat(vdir, osg::Vec3f(0,1,0)) * osg::Quat(hdir, osg::Vec3f(0,0,1)))\n             * osg::Vec3f(0,0,1);\n\n    float vel = mMinSpeed + (mMaxSpeed - mMinSpeed) * Misc::Rng::rollClosedProbability();\n    particle->setVelocity(dir * vel);\n\n    // Not supposed to set this here, but there doesn't seem to be a better way of doing it\n    particle->setLifeTime(std::max(std::numeric_limits<float>::epsilon(), mLifetime + mLifetimeRandom * Misc::Rng::rollClosedProbability()));\n}\n\nGrowFadeAffector::GrowFadeAffector(float growTime, float fadeTime)\n    : mGrowTime(growTime)\n    , mFadeTime(fadeTime)\n    , mCachedDefaultSize(0.f)\n{\n}\n\nGrowFadeAffector::GrowFadeAffector()\n    : mGrowTime(0.f)\n    , mFadeTime(0.f)\n    , mCachedDefaultSize(0.f)\n{\n\n}\n\nGrowFadeAffector::GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop)\n    : osgParticle::Operator(copy, copyop)\n{\n    mGrowTime = copy.mGrowTime;\n    mFadeTime = copy.mFadeTime;\n    mCachedDefaultSize = copy.mCachedDefaultSize;\n}\n\nvoid GrowFadeAffector::beginOperate(osgParticle::Program *program)\n{\n    mCachedDefaultSize = program->getParticleSystem()->getDefaultParticleTemplate().getSizeRange().minimum;\n}\n\nvoid GrowFadeAffector::operate(osgParticle::Particle* particle, double /* dt */)\n{\n    float size = mCachedDefaultSize;\n    if (particle->getAge() < mGrowTime && mGrowTime != 0.f)\n        size *= particle->getAge() / mGrowTime;\n    if (particle->getLifeTime() - particle->getAge() < mFadeTime && mFadeTime != 0.f)\n        size *= (particle->getLifeTime() - particle->getAge()) / mFadeTime;\n    particle->setSizeRange(osgParticle::rangef(size, size));\n}\n\nParticleColorAffector::ParticleColorAffector(const Nif::NiColorData *clrdata)\n    : mData(clrdata->mKeyMap, osg::Vec4f(1,1,1,1))\n{\n}\n\nParticleColorAffector::ParticleColorAffector()\n{\n\n}\n\nParticleColorAffector::ParticleColorAffector(const ParticleColorAffector &copy, const osg::CopyOp &copyop)\n    : osgParticle::Operator(copy, copyop)\n{\n    mData = copy.mData;\n}\n\nvoid ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */)\n{\n    assert(particle->getLifeTime() > 0);\n    float time = static_cast<float>(particle->getAge()/particle->getLifeTime());\n    osg::Vec4f color = mData.interpKey(time);\n    float alpha = color.a();\n    color.a() = 1.0f;\n\n    particle->setColorRange(osgParticle::rangev4(color, color));\n    particle->setAlphaRange(osgParticle::rangef(alpha, alpha));\n}\n\nGravityAffector::GravityAffector(const Nif::NiGravity *gravity)\n    : mForce(gravity->mForce)\n    , mType(static_cast<ForceType>(gravity->mType))\n    , mPosition(gravity->mPosition)\n    , mDirection(gravity->mDirection)\n    , mDecay(gravity->mDecay)\n{\n}\n\nGravityAffector::GravityAffector()\n    : mForce(0), mType(Type_Wind), mDecay(0.f)\n{\n\n}\n\nGravityAffector::GravityAffector(const GravityAffector &copy, const osg::CopyOp &copyop)\n    : osgParticle::Operator(copy, copyop)\n{\n    mForce = copy.mForce;\n    mType = copy.mType;\n    mPosition = copy.mPosition;\n    mDirection = copy.mDirection;\n    mDecay = copy.mDecay;\n    mCachedWorldPosition = copy.mCachedWorldPosition;\n    mCachedWorldDirection = copy.mCachedWorldDirection;\n}\n\nvoid GravityAffector::beginOperate(osgParticle::Program* program)\n{\n    bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF);\n\n    if (mType == Type_Point || mDecay != 0.f) // we don't need the position for Wind gravity, except if decay is being applied\n        mCachedWorldPosition = absolute ? program->transformLocalToWorld(mPosition) : mPosition;\n\n    mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection;\n    mCachedWorldDirection.normalize();\n}\n\nvoid GravityAffector::operate(osgParticle::Particle *particle, double dt)\n{\n    const float magic = 1.6f;\n    switch (mType)\n    {\n        case Type_Wind:\n        {\n            float decayFactor = 1.f;\n            if (mDecay != 0.f)\n            {\n                osg::Plane gravityPlane(mCachedWorldDirection, mCachedWorldPosition);\n                float distance = std::abs(gravityPlane.distance(particle->getPosition()));\n                decayFactor = std::exp(-1.f * mDecay * distance);\n            }\n\n            particle->addVelocity(mCachedWorldDirection * mForce * dt * decayFactor * magic);\n\n            break;\n        }\n        case Type_Point:\n        {\n            osg::Vec3f diff = mCachedWorldPosition - particle->getPosition();\n\n            float decayFactor = 1.f;\n            if (mDecay != 0.f)\n                decayFactor = std::exp(-1.f * mDecay * diff.length());\n\n            diff.normalize();\n\n            particle->addVelocity(diff * mForce * dt * decayFactor * magic);\n            break;\n        }\n    }\n}\n\nEmitter::Emitter()\n    : osgParticle::Emitter()\n{\n}\n\nEmitter::Emitter(const Emitter &copy, const osg::CopyOp &copyop)\n    : osgParticle::Emitter(copy, copyop)\n    , mTargets(copy.mTargets)\n    , mPlacer(copy.mPlacer)\n    , mShooter(copy.mShooter)\n    // need a deep copy because the remainder is stored in the object\n    , mCounter(static_cast<osgParticle::Counter*>(copy.mCounter->clone(osg::CopyOp::DEEP_COPY_ALL)))\n{\n}\n\nEmitter::Emitter(const std::vector<int> &targets)\n    : mTargets(targets)\n{\n}\n\nvoid Emitter::setShooter(osgParticle::Shooter *shooter)\n{\n    mShooter = shooter;\n}\n\nvoid Emitter::setPlacer(osgParticle::Placer *placer)\n{\n    mPlacer = placer;\n}\n\nvoid Emitter::setCounter(osgParticle::Counter *counter)\n{\n    mCounter = counter;\n}\n\nvoid Emitter::emitParticles(double dt)\n{\n    int n = mCounter->numParticlesToCreate(dt);\n    if (n == 0)\n        return;\n\n    osg::Matrix worldToPs;\n\n    // maybe this could be optimized by halting at the lowest common ancestor of the particle and emitter nodes\n    osg::NodePathList partsysNodePaths = getParticleSystem()->getParentalNodePaths();\n    if (!partsysNodePaths.empty())\n    {\n        osg::Matrix psToWorld = osg::computeLocalToWorld(partsysNodePaths[0]);\n        worldToPs = osg::Matrix::inverse(psToWorld);\n    }\n\n    const osg::Matrix& ltw = getLocalToWorldMatrix();\n    osg::Matrix emitterToPs = ltw * worldToPs;\n\n    if (!mTargets.empty())\n    {\n        int randomIndex = Misc::Rng::rollClosedProbability() * (mTargets.size() - 1);\n        int randomRecIndex = mTargets[randomIndex];\n\n        // we could use a map here for faster lookup\n        FindGroupByRecIndex visitor(randomRecIndex);\n        getParent(0)->accept(visitor);\n\n        if (!visitor.mFound)\n        {\n            Log(Debug::Info) << \"Can't find emitter node\" << randomRecIndex;\n            return;\n        }\n\n        osg::NodePath path = visitor.mFoundPath;\n        path.erase(path.begin());\n        emitterToPs = osg::computeLocalToWorld(path) * emitterToPs;\n    }\n\n    emitterToPs.orthoNormalize(emitterToPs);\n\n    for (int i=0; i<n; ++i)\n    {\n        osgParticle::Particle* P = getParticleSystem()->createParticle(nullptr);\n        if (P)\n        {\n            mPlacer->place(P);\n\n            mShooter->shoot(P);\n\n            P->transformPositionVelocity(emitterToPs);\n        }\n    }\n}\n\nFindGroupByRecIndex::FindGroupByRecIndex(unsigned int recIndex)\n    : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n    , mFound(nullptr)\n    , mRecIndex(recIndex)\n{\n}\n\nvoid FindGroupByRecIndex::apply(osg::Node &node)\n{\n    applyNode(node);\n}\n\nvoid FindGroupByRecIndex::apply(osg::MatrixTransform &node)\n{\n    applyNode(node);\n}\n\nvoid FindGroupByRecIndex::apply(osg::Geometry &node)\n{\n    applyNode(node);\n}\n\nvoid FindGroupByRecIndex::applyNode(osg::Node &searchNode)\n{\n    unsigned int recIndex;\n    if (searchNode.getUserValue(\"recIndex\", recIndex) && mRecIndex == recIndex)\n    {\n        osg::Group* group = searchNode.asGroup();\n        if (!group)\n            group = searchNode.getParent(0);\n\n        mFound = group;\n        mFoundPath = getNodePath();\n        return;\n    }\n    traverse(searchNode);\n}\n\nPlanarCollider::PlanarCollider(const Nif::NiPlanarCollider *collider)\n    : mBounceFactor(collider->mBounceFactor)\n    , mPlane(-collider->mPlaneNormal, collider->mPlaneDistance)\n{\n}\n\nPlanarCollider::PlanarCollider()\n    : mBounceFactor(0.f)\n{\n}\n\nPlanarCollider::PlanarCollider(const PlanarCollider &copy, const osg::CopyOp &copyop)\n    : osgParticle::Operator(copy, copyop)\n    , mBounceFactor(copy.mBounceFactor)\n    , mPlane(copy.mPlane)\n    , mPlaneInParticleSpace(copy.mPlaneInParticleSpace)\n{\n}\n\nvoid PlanarCollider::beginOperate(osgParticle::Program *program)\n{\n    mPlaneInParticleSpace = mPlane;\n    if (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF)\n        mPlaneInParticleSpace.transform(program->getLocalToWorldMatrix());\n}\n\nvoid PlanarCollider::operate(osgParticle::Particle *particle, double dt)\n{\n    float dotproduct = particle->getVelocity() * mPlaneInParticleSpace.getNormal();\n\n    if (dotproduct > 0)\n    {\n        osg::BoundingSphere bs(particle->getPosition(), 0.f);\n        if (mPlaneInParticleSpace.intersect(bs) == 1)\n        {\n            osg::Vec3 reflectedVelocity = particle->getVelocity() - mPlaneInParticleSpace.getNormal() * (2 * dotproduct);\n            reflectedVelocity *= mBounceFactor;\n            particle->setVelocity(reflectedVelocity);\n        }\n    }\n}\n\nSphericalCollider::SphericalCollider(const Nif::NiSphericalCollider* collider)\n    : mBounceFactor(collider->mBounceFactor),\n      mSphere(collider->mCenter, collider->mRadius)\n{\n}\n\nSphericalCollider::SphericalCollider()\n    : mBounceFactor(1.0f)\n{\n\n}\n\nSphericalCollider::SphericalCollider(const SphericalCollider& copy, const osg::CopyOp& copyop)\n    : osgParticle::Operator(copy, copyop)\n    , mBounceFactor(copy.mBounceFactor)\n    , mSphere(copy.mSphere)\n    , mSphereInParticleSpace(copy.mSphereInParticleSpace)\n{\n\n}\n\nvoid SphericalCollider::beginOperate(osgParticle::Program* program)\n{\n    mSphereInParticleSpace = mSphere;\n    if (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF)\n        mSphereInParticleSpace.center() = program->transformLocalToWorld(mSphereInParticleSpace.center());\n}\n\nvoid SphericalCollider::operate(osgParticle::Particle* particle, double dt)\n{\n    osg::Vec3f cent = (particle->getPosition() - mSphereInParticleSpace.center()); // vector from sphere center to particle\n\n    bool insideSphere = cent.length2() <= mSphereInParticleSpace.radius2();\n\n    if (insideSphere\n            || (cent * particle->getVelocity() < 0.0f)) // if outside, make sure the particle is flying towards the sphere\n    {\n        // Collision test (finding point of contact) is performed by solving a quadratic equation:\n        // ||vec(cent) + vec(vel)*k|| = R      /^2\n        // k^2 + 2*k*(vec(cent)*vec(vel))/||vec(vel)||^2 + (||vec(cent)||^2 - R^2)/||vec(vel)||^2 = 0\n\n        float b = -(cent * particle->getVelocity()) / particle->getVelocity().length2();\n\n        osg::Vec3f u = cent + particle->getVelocity() * b;\n\n        if (insideSphere\n                || (u.length2() < mSphereInParticleSpace.radius2()))\n        {\n            float d = (mSphereInParticleSpace.radius2() - u.length2()) / particle->getVelocity().length2();\n            float k = insideSphere ? (std::sqrt(d) + b) : (b - std::sqrt(d));\n\n            if (k < dt)\n            {\n                // collision detected; reflect off the tangent plane\n                osg::Vec3f contact = particle->getPosition() + particle->getVelocity() * k;\n\n                osg::Vec3 normal = (contact - mSphereInParticleSpace.center());\n                normal.normalize();\n\n                float dotproduct = particle->getVelocity() * normal;\n\n                osg::Vec3 reflectedVelocity = particle->getVelocity() - normal * (2 * dotproduct);\n                reflectedVelocity *= mBounceFactor;\n                particle->setVelocity(reflectedVelocity);\n            }\n        }\n    }\n}\n\n}\n"
  },
  {
    "path": "components/nifosg/particle.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_NIFOSG_PARTICLE_H\n#define OPENMW_COMPONENTS_NIFOSG_PARTICLE_H\n\n#include <osgParticle/Particle>\n#include <osgParticle/Shooter>\n#include <osgParticle/Operator>\n#include <osgParticle/Emitter>\n#include <osgParticle/Placer>\n#include <osgParticle/Counter>\n\n#include <osg/NodeCallback>\n\n#include \"controller.hpp\" // ValueInterpolator\n\nnamespace Nif\n{\n    struct NiGravity;\n    struct NiPlanarCollider;\n    struct NiSphericalCollider;\n    struct NiColorData;\n}\n\nnamespace NifOsg\n{\n\n    // Subclass ParticleSystem to support a limit on the number of active particles.\n    class ParticleSystem : public osgParticle::ParticleSystem\n    {\n    public:\n        ParticleSystem();\n        ParticleSystem(const ParticleSystem& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, ParticleSystem)\n\n        osgParticle::Particle* createParticle(const osgParticle::Particle *ptemplate) override;\n\n        void setQuota(int quota);\n\n        void drawImplementation(osg::RenderInfo& renderInfo) const override;\n\n    private:\n        int mQuota;\n        osg::ref_ptr<osg::Vec3Array> mNormalArray;\n    };\n\n    // HACK: Particle doesn't allow setting the initial age, but we need this for loading the particle system state\n    class ParticleAgeSetter : public osgParticle::Particle\n    {\n    public:\n        ParticleAgeSetter(float age)\n            : Particle()\n        {\n            _t0 = age;\n        }\n    };\n\n    // Node callback used to set the inverse of the parent's world matrix on the MatrixTransform\n    // that the callback is attached to. Used for certain particle systems,\n    // so that the particles do not move with the node they are attached to.\n    class InverseWorldMatrix : public osg::NodeCallback\n    {\n    public:\n        InverseWorldMatrix()\n        {\n        }\n        InverseWorldMatrix(const InverseWorldMatrix& copy, const osg::CopyOp& op)\n            : osg::Object(), osg::NodeCallback()\n        {\n        }\n\n        META_Object(NifOsg, InverseWorldMatrix)\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override;\n    };\n\n    class ParticleShooter : public osgParticle::Shooter\n    {\n    public:\n        ParticleShooter(float minSpeed, float maxSpeed, float horizontalDir, float horizontalAngle, float verticalDir, float verticalAngle,\n                        float lifetime, float lifetimeRandom);\n        ParticleShooter();\n        ParticleShooter(const ParticleShooter& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);\n\n        ParticleShooter& operator=(const ParticleShooter&) = delete;\n\n        META_Object(NifOsg, ParticleShooter)\n\n        void shoot(osgParticle::Particle* particle) const override;\n\n    private:\n        float mMinSpeed;\n        float mMaxSpeed;\n        float mHorizontalDir;\n        float mHorizontalAngle;\n        float mVerticalDir;\n        float mVerticalAngle;\n        float mLifetime;\n        float mLifetimeRandom;\n    };\n\n    class PlanarCollider : public osgParticle::Operator\n    {\n    public:\n        PlanarCollider(const Nif::NiPlanarCollider* collider);\n        PlanarCollider();\n        PlanarCollider(const PlanarCollider& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, PlanarCollider)\n\n        void beginOperate(osgParticle::Program* program) override;\n        void operate(osgParticle::Particle* particle, double dt) override;\n\n    private:\n        float mBounceFactor;\n        osg::Plane mPlane;\n        osg::Plane mPlaneInParticleSpace;\n    };\n\n    class SphericalCollider : public osgParticle::Operator\n    {\n    public:\n        SphericalCollider(const Nif::NiSphericalCollider* collider);\n        SphericalCollider();\n        SphericalCollider(const SphericalCollider& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, SphericalCollider)\n\n        void beginOperate(osgParticle::Program* program) override;\n        void operate(osgParticle::Particle* particle, double dt) override;\n    private:\n        float mBounceFactor;\n        osg::BoundingSphere mSphere;\n        osg::BoundingSphere mSphereInParticleSpace;\n    };\n\n    class GrowFadeAffector : public osgParticle::Operator\n    {\n    public:\n        GrowFadeAffector(float growTime, float fadeTime);\n        GrowFadeAffector();\n        GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);\n\n        GrowFadeAffector& operator=(const GrowFadeAffector&) = delete;\n\n        META_Object(NifOsg, GrowFadeAffector)\n\n        void beginOperate(osgParticle::Program* program) override;\n        void operate(osgParticle::Particle* particle, double dt) override;\n\n    private:\n        float mGrowTime;\n        float mFadeTime;\n\n        float mCachedDefaultSize;\n    };\n\n    class ParticleColorAffector : public osgParticle::Operator\n    {\n    public:\n        ParticleColorAffector(const Nif::NiColorData* clrdata);\n        ParticleColorAffector();\n        ParticleColorAffector(const ParticleColorAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);\n\n        ParticleColorAffector& operator=(const ParticleColorAffector&) = delete;\n\n        META_Object(NifOsg, ParticleColorAffector)\n\n        void operate(osgParticle::Particle* particle, double dt) override;\n\n    private:\n        Vec4Interpolator mData;\n    };\n\n    class GravityAffector : public osgParticle::Operator\n    {\n    public:\n        GravityAffector(const Nif::NiGravity* gravity);\n        GravityAffector();\n        GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);\n\n        GravityAffector& operator=(const GravityAffector&) = delete;\n\n        META_Object(NifOsg, GravityAffector)\n\n        void operate(osgParticle::Particle* particle, double dt) override;\n        void beginOperate(osgParticle::Program *) override ;\n\n    private:\n        float mForce;\n        enum ForceType {\n            Type_Wind,\n            Type_Point\n        };\n        ForceType mType;\n        osg::Vec3f mPosition;\n        osg::Vec3f mDirection;\n        float mDecay;\n        osg::Vec3f mCachedWorldPosition;\n        osg::Vec3f mCachedWorldDirection;\n    };\n\n    // NodeVisitor to find a Group node with the given record index, stored in the node's user data container.\n    // Alternatively, returns the node's parent Group if that node is not a Group (i.e. a leaf node).\n    class FindGroupByRecIndex : public osg::NodeVisitor\n    {\n    public:\n        FindGroupByRecIndex(unsigned int recIndex);\n\n        void apply(osg::Node &node) override;\n\n        // Technically not required as the default implementation would trickle down to apply(Node&) anyway,\n        // but we'll shortcut instead to avoid the chain of virtual function calls\n        void apply(osg::MatrixTransform& node) override;\n        void apply(osg::Geometry& node) override;\n\n        void applyNode(osg::Node& searchNode);\n\n        osg::Group* mFound;\n        osg::NodePath mFoundPath;\n    private:\n        unsigned int mRecIndex;\n    };\n\n    // Subclass emitter to support randomly choosing one of the child node's transforms for the emit position of new particles.\n    class Emitter : public osgParticle::Emitter\n    {\n    public:\n        Emitter(const std::vector<int>& targets);\n        Emitter();\n        Emitter(const Emitter& copy, const osg::CopyOp& copyop);\n\n        META_Object(NifOsg, Emitter)\n\n        void emitParticles(double dt) override;\n\n        void setShooter(osgParticle::Shooter* shooter);\n        void setPlacer(osgParticle::Placer* placer);\n        void setCounter(osgParticle::Counter* counter);\n\n    private:\n        // NIF Record indices\n        std::vector<int> mTargets;\n\n        osg::ref_ptr<osgParticle::Placer> mPlacer;\n        osg::ref_ptr<osgParticle::Shooter> mShooter;\n        osg::ref_ptr<osgParticle::Counter> mCounter;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/openmw-mp/Base/BaseActor.hpp",
    "content": "#ifndef OPENMW_BASEACTOR_HPP\n#define OPENMW_BASEACTOR_HPP\n\n#include <components/esm/loadcell.hpp>\n\n#include <components/openmw-mp/Base/BaseStructs.hpp>\n\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    class BaseActor\n    {\n    public:\n\n        BaseActor()\n        {\n            hasPositionData = false;\n            hasStatsDynamicData = false;\n        }\n\n        std::string refId = \"\";\n        unsigned int refNum;\n        unsigned int mpNum;\n\n        ESM::Position position;\n        ESM::Position direction;\n\n        ESM::Cell cell;\n\n        unsigned int movementFlags;\n        char drawState;\n        bool isFlying;\n\n        std::string sound;\n\n        SimpleCreatureStats creatureStats;\n\n        Animation animation;\n        char deathState;\n        bool isInstantDeath = false;\n        Attack attack;\n        Cast cast;\n\n        Target killer;\n\n        bool isFollowerCellChange;\n\n        bool hasAiTarget;\n        Target aiTarget;\n        unsigned int aiAction;\n        unsigned int aiDistance;\n        unsigned int aiDuration;\n        bool aiShouldRepeat;\n        ESM::Position aiCoordinates;\n\n        bool hasPositionData;\n        bool hasStatsDynamicData;\n\n        Item equipmentItems[19];\n        SpellsActiveChanges spellsActiveChanges;\n    };\n\n    class BaseActorList\n    {\n    public:\n\n        BaseActorList()\n        {\n\n        }\n\n        enum ACTOR_ACTION\n        {\n            SET = 0,\n            ADD = 1,\n            REMOVE = 2,\n            REQUEST = 3\n        };\n\n        enum AI_ACTION\n        {\n            CANCEL = 0,\n            ACTIVATE = 1,\n            COMBAT = 2,\n            ESCORT = 3,\n            FOLLOW = 4,\n            TRAVEL = 5,\n            WANDER = 6\n        };\n\n        RakNet::RakNetGUID guid;\n\n        std::vector<BaseActor> baseActors;\n\n        unsigned int count;\n\n        ESM::Cell cell;\n\n        unsigned char action; // 0 - Clear and set in entirety, 1 - Add item, 2 - Remove item, 3 - Request items\n\n        bool isValid;\n    };\n}\n\n#endif //OPENMW_BASEACTOR_HPP\n"
  },
  {
    "path": "components/openmw-mp/Base/BaseObject.hpp",
    "content": "#ifndef OPENMW_BASEEVENT_HPP\n#define OPENMW_BASEEVENT_HPP\n\n#include <components/esm/loadcell.hpp>\n#include <components/openmw-mp/Base/BaseStructs.hpp>\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    struct ContainerItem\n    {\n        std::string refId;\n        int count;\n        int charge;\n        double enchantmentCharge;\n        std::string soul;\n\n        int actionCount;\n\n        inline bool operator==(const ContainerItem& rhs)\n        {\n            return refId == rhs.refId && count == rhs.count && charge == rhs.charge &&\n                enchantmentCharge == rhs.enchantmentCharge && soul == rhs.soul;\n        }\n    };\n\n    struct BaseObject\n    {\n        std::string refId = \"\";\n        unsigned int refNum;\n        unsigned int mpNum;\n        int count;\n        int charge;\n        double enchantmentCharge;\n        std::string soul;\n        int goldValue;\n\n        ESM::Position position;\n\n        bool objectState;\n        int lockLevel;\n        float scale;\n\n        unsigned char dialogueChoiceType;\n        std::string topicId;\n        int guiId;\n\n        std::string soundId;\n        float volume;\n        float pitch;\n\n        unsigned int goldPool;\n        float lastGoldRestockHour;\n        int lastGoldRestockDay;\n\n\n        int doorState;\n        bool teleportState;\n        ESM::Cell destinationCell;\n        ESM::Position destinationPosition;\n\n        std::string musicFilename;\n\n        std::string videoFilename;\n        bool allowSkipping;\n\n        std::string animGroup;\n        int animMode;\n\n        bool isDisarmed;\n        bool droppedByPlayer;\n\n        Target activatingActor;\n        Target hittingActor;\n        Attack hitAttack;\n\n        bool isSummon;\n        int summonEffectId;\n        std::string summonSpellId;\n        float summonDuration;\n        Target master;\n\n        bool hasContainer;\n\n        std::vector<ClientVariable> clientLocals;\n        std::vector<ContainerItem> containerItems;\n        unsigned int containerItemCount;\n\n        RakNet::RakNetGUID guid; // only for object lists that can also include players\n        bool isPlayer;\n    };\n\n    class BaseObjectList\n    {\n    public:\n\n        BaseObjectList(RakNet::RakNetGUID guid) : guid(guid)\n        {\n\n        }\n\n        BaseObjectList()\n        {\n\n        }\n\n        enum WORLD_ACTION\n        {\n            SET = 0,\n            ADD = 1,\n            REMOVE = 2,\n            REQUEST = 3\n        };\n\n        enum CONTAINER_SUBACTION\n        {\n            NONE = 0,\n            DRAG = 1,\n            DROP = 2,\n            TAKE_ALL = 3,\n            REPLY_TO_REQUEST = 4,\n            RESTOCK_RESULT = 5\n        };\n\n        RakNet::RakNetGUID guid;\n        \n        std::vector<BaseObject> baseObjects;\n        unsigned int baseObjectCount;\n\n        ESM::Cell cell;\n        std::string consoleCommand;\n\n        unsigned char packetOrigin; // 0 - Gameplay, 1 - Console, 2 - Client script, 3 - Server script\n        std::string originClientScript;\n\n        unsigned char action; // 0 - Clear and set in entirety, 1 - Add item, 2 - Remove item, 3 - Request items\n        unsigned char containerSubAction; // 0 - None, 1 - Drag, 2 - Drop, 3 - Take all, 4 - Reply to request\n\n        bool isValid;\n    };\n}\n\n#endif //OPENMW_BASEEVENT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Base/BasePacketProcessor.hpp",
    "content": "#ifndef OPENMW_BASEPACKETPROCESSOR_HPP\n#define OPENMW_BASEPACKETPROCESSOR_HPP\n\n#include <string>\n#include <memory>\n#include <unordered_map>\n#include <stdexcept>\n\n#define BPP_INIT(packet_id) packetID = packet_id; strPacketID = #packet_id; className = typeid(this).name(); avoidReading = false;\n\ntemplate<typename Proccessor>\nclass BasePacketProcessor\n{\npublic:\n    typedef std::unordered_map<unsigned char, std::unique_ptr<Proccessor>> processors_t;\n    unsigned char GetPacketID()\n    {\n        return packetID;\n    }\n    std::string GetNameOfID()\n    {\n        return strPacketID;\n    }\n\n    std::string GetClassName()\n    {\n        return className;\n    }\n\n    static void AddProcessor(Proccessor *processor)\n    {\n        for (auto &p : processors)\n        {\n            if (processor->packetID == p.first)\n                throw std::logic_error(\"processor \" + p.second->strPacketID + \" already registered. Check \" +\n                                       processor->className + \" and \" + p.second->className);\n        }\n        processors.insert(typename processors_t::value_type(processor->GetPacketID(), processor));\n    }\nprotected:\n    unsigned char packetID;\n    std::string strPacketID;\n    std::string className;\n    bool avoidReading;\n    static processors_t processors;\n};\n\n#endif //OPENMW_BASEPACKETPROCESSOR_HPP\n"
  },
  {
    "path": "components/openmw-mp/Base/BasePlayer.hpp",
    "content": "#ifndef OPENMW_BASEPLAYER_HPP\n#define OPENMW_BASEPLAYER_HPP\n\n#include <components/esm/loadcell.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/npcstats.hpp>\n#include <components/esm/creaturestats.hpp>\n#include <components/esm/loadclas.hpp>\n#include <components/esm/loadspel.hpp>\n\n#include <components/openmw-mp/Base/BaseStructs.hpp>\n\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    struct CurrentContainer\n    {\n        std::string refId;\n        unsigned int refNum;\n        unsigned int mpNum;\n        bool loot;\n    };\n\n    struct JournalItem\n    {\n        std::string quest;\n        int index;\n        enum JOURNAL_ITEM_TYPE\n        {\n            ENTRY = 0,\n            INDEX = 1\n        };\n\n        std::string actorRefId;\n\n        bool hasTimestamp;\n        mwmp::Time timestamp;\n\n        int type; // 0 - An entire entry, 1 - An index\n    };\n\n    struct Faction\n    {\n        std::string factionId;\n        int rank;\n        int reputation;\n        bool isExpelled;\n    };\n\n    struct Topic\n    {\n        std::string topicId;\n    };\n\n    struct Book\n    {\n        std::string bookId;\n    };\n\n    struct QuickKey\n    {\n        std::string itemId;\n\n        enum QUICKKEY_TYPE\n        {   \n            ITEM = 0,\n            MAGIC = 1,\n            ITEM_MAGIC = 2,\n            UNASSIGNED = 3\n        };\n\n        unsigned short slot;\n        int type;\n    };\n\n    struct CellState\n    {\n        ESM::Cell cell;\n\n        enum CELL_STATE_ACTION\n        {\n            LOAD = 0,\n            UNLOAD = 1\n        };\n\n        int type; // 0 - Cell load, 1 - Cell unload\n    };\n\n    struct FactionChanges\n    {\n        std::vector<Faction> factions;\n\n        enum FACTION_ACTION\n        {\n            RANK = 0,\n            EXPULSION = 1,\n            REPUTATION = 2\n        };\n\n        int action; // 0 - Rank, 1 - Expulsion state, 2 - Faction reputation\n    };\n\n    struct InventoryChanges\n    {\n        std::vector<Item> items;\n        enum ACTION_TYPE\n        {\n            SET = 0,\n            ADD,\n            REMOVE\n        };\n        int action; // 0 - Clear and set in entirety, 1 - Add item, 2 - Remove item\n    };\n\n    struct SpellbookChanges\n    {\n        std::vector<ESM::Spell> spells;\n        enum ACTION_TYPE\n        {\n            SET = 0,\n            ADD,\n            REMOVE\n        };\n        int action; // 0 - Clear and set in entirety, 1 - Add spell, 2 - Remove spell\n    };\n\n    enum RESURRECT_TYPE\n    {\n        REGULAR = 0,\n        IMPERIAL_SHRINE,\n        TRIBUNAL_TEMPLE\n    };\n\n    enum MISCELLANEOUS_CHANGE_TYPE\n    {\n        MARK_LOCATION = 0,\n        SELECTED_SPELL\n    };\n\n    class BasePlayer\n    {\n    public:\n\n        struct CharGenState\n        {\n            int currentStage, endStage;\n            bool isFinished;\n        };\n\n        struct GUIMessageBox\n        {\n            int id;\n            int type;\n            enum GUI_TYPE\n            {\n                MessageBox = 0,\n                CustomMessageBox,\n                InputDialog,\n                PasswordDialog,\n                ListBox\n            };\n            std::string label;\n            std::string note;\n            std::string buttons;\n\n            std::string data;\n        };\n\n        BasePlayer(RakNet::RakNetGUID guid) : guid(guid)\n        {\n            inventoryChanges.action = 0;\n            spellbookChanges.action = 0;\n\n            exchangeFullInfo = false;\n            displayCreatureName = false;\n            resetStats = false;\n            enforcedLogLevel = -1;\n        }\n\n        BasePlayer()\n        {\n\n        }\n\n        RakNet::RakNetGUID guid;\n\n        GUIMessageBox guiMessageBox;\n\n        // Track only the indexes of the attributes that have been changed,\n        // with the attribute values themselves being stored in creatureStats.mAttributes\n        std::vector<uint8_t> attributeIndexChanges;\n\n        // Track only the indexes of the skills that have been changed,\n        // with the skill values themselves being stored in npcStats.mSkills\n        std::vector<uint8_t> skillIndexChanges;\n\n        // Track only the indexes of the dynamic states that have been changed,\n        // with the dynamicStats themselves being stored in creatureStats.mDynamic\n        std::vector<uint8_t> statsDynamicIndexChanges;\n\n        // Track only the indexes of the equipment items that have been changed,\n        // with the items themselves being stored in equipmentItems\n        std::vector<int> equipmentIndexChanges;\n\n        bool exchangeFullInfo;\n\n        InventoryChanges inventoryChanges;\n        SpellbookChanges spellbookChanges;\n        std::vector<SpellCooldown> cooldownChanges;\n        SpellsActiveChanges spellsActiveChanges;\n        std::vector<QuickKey> quickKeyChanges;\n        std::vector<JournalItem> journalChanges;\n        FactionChanges factionChanges;\n        std::vector<Topic> topicChanges;\n        std::vector<Book> bookChanges;\n        std::vector<CellState> cellStateChanges;\n\n        std::vector<RakNet::RakNetGUID> alliedPlayers;\n        CurrentContainer currentContainer;\n\n        int difficulty = 0;\n        int enforcedLogLevel;\n        float physicsFramerate = 60.0;\n        bool consoleAllowed = false;\n        bool bedRestAllowed = true;\n        bool wildernessRestAllowed = true;\n        bool waitAllowed = true;\n\n        bool ignorePosPacket;\n\n        unsigned int movementFlags = 0;\n        char drawState;\n        bool isJumping = false;\n        bool isFlying = false;\n        bool hasTcl = false;\n\n        ESM::Position position;\n        ESM::Position direction;\n        ESM::Position previousCellPosition;\n        ESM::Position momentum;\n        ESM::Cell cell;\n        ESM::NPC npc;\n        ESM::NpcStats npcStats;\n        ESM::Creature creature;\n        ESM::CreatureStats creatureStats;\n        ESM::Class charClass;\n        Item equipmentItems[19];\n        Attack attack;\n        Cast cast;\n        std::string birthsign;\n        std::string chatMessage;\n        CharGenState charGenState;\n        std::map<std::string, std::string> gameSettings;\n        std::map<std::string, std::string> vrSettings;\n\n        std::string sound;\n        Animation animation;\n        char deathState;\n\n        bool resetStats = false;\n        float scale = 1;\n        bool isWerewolf = false;\n\n        bool displayCreatureName;\n        std::string creatureRefId;\n\n        bool isChangingRegion;\n\n        Target killer;\n\n        int jailDays;\n        bool ignoreJailTeleportation;\n        bool ignoreJailSkillIncreases;\n        std::string jailProgressText;\n        std::string jailEndText;\n\n        unsigned int resurrectType;\n        unsigned int miscellaneousChangeType;\n\n        ESM::Cell markCell;\n        ESM::Position markPosition;\n        std::string selectedSpellId;\n\n        mwmp::Item usedItem;\n        bool usingItemMagic;\n        char itemUseDrawState;\n    };\n}\n\n#endif //OPENMW_BASEPLAYER_HPP\n"
  },
  {
    "path": "components/openmw-mp/Base/BaseStructs.hpp",
    "content": "#ifndef OPENMW_BASESTRUCTS_HPP\n#define OPENMW_BASESTRUCTS_HPP\n\n#include <string>\n\n#include <components/esm/activespells.hpp>\n#include <components/esm/loadcell.hpp>\n#include <components/esm/statstate.hpp>\n\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    namespace DialogueChoiceType\n    {\n        enum DIALOGUE_CHOICE\n        {\n            TOPIC,\n            PERSUASION,\n            COMPANION_SHARE,\n            BARTER,\n            SPELLS,\n            TRAVEL,\n            SPELLMAKING,\n            ENCHANTING,\n            TRAINING,\n            REPAIR\n        };\n    }\n\n    enum PACKET_ORIGIN\n    {\n        CLIENT_GAMEPLAY = 0,\n        CLIENT_CONSOLE = 1,\n        CLIENT_DIALOGUE = 2,\n        CLIENT_SCRIPT_LOCAL = 3,\n        CLIENT_SCRIPT_GLOBAL = 4,\n        SERVER_SCRIPT = 5\n    };\n\n    enum VARIABLE_TYPE\n    {\n        SHORT,\n        LONG,\n        FLOAT,\n        INT,\n        STRING\n    };\n\n    struct ClientVariable\n    {\n        std::string id;\n        int internalIndex;\n\n        char variableType;\n\n        int intValue;\n        float floatValue;\n        std::string stringValue;\n    };\n\n    struct Time\n    {\n        float hour;\n        int day;\n        int month;\n        int year;\n\n        int daysPassed;\n        float timeScale;\n    };\n\n    struct Item\n    {\n        std::string refId;\n        int count;\n        int charge;\n        float enchantmentCharge;\n        std::string soul;\n\n        inline bool operator==(const Item& rhs)\n        {\n            return refId == rhs.refId && count == rhs.count && charge == rhs.charge &&\n                enchantmentCharge == rhs.enchantmentCharge && soul == rhs.soul;\n        }\n    };\n\n    struct ProjectileOrigin\n    {\n        float origin[3];\n        float orientation[4];\n    };\n    \n    struct Target\n    {\n        bool isPlayer;\n\n        std::string refId;\n        unsigned int refNum;\n        unsigned int mpNum;\n\n        std::string name; // Remove this once the server can get names corresponding to different refIds\n\n        RakNet::RakNetGUID guid;\n    };\n\n    class Attack\n    {\n    public:\n\n        Target target;\n\n        enum TYPE\n        {\n            MELEE = 0,\n            RANGED\n        };\n\n        char type;\n        std::string attackAnimation;\n\n        std::string rangedWeaponId;\n        std::string rangedAmmoId;\n\n        ESM::Position hitPosition;\n        ProjectileOrigin projectileOrigin;\n\n        float damage = 0;\n        float attackStrength = 0;\n\n        bool isHit = false;\n        bool success = false;\n        bool block = false;\n        \n        bool pressed = false;\n        bool instant = false;\n        bool knockdown = false;\n        bool applyWeaponEnchantment = false;\n        bool applyAmmoEnchantment = false;\n\n        bool shouldSend = false;\n    };\n\n    class Cast\n    {\n    public:\n\n        Target target;\n\n        char type; // 0 - regular magic, 1 - item magic\n        enum TYPE\n        {\n            REGULAR = 0,\n            ITEM\n        };\n\n        std::string spellId; // id of spell (e.g. \"fireball\")\n        std::string itemId;\n\n        bool hasProjectile = false;\n        ProjectileOrigin projectileOrigin;\n\n        bool isHit;\n        bool success;\n        bool pressed;\n        bool instant;\n\n        bool shouldSend;\n    };\n\n    struct SpellCooldown\n    {\n        std::string id;\n        int startTimestampDay;\n        double startTimestampHour;\n    };\n\n    struct ActiveSpell\n    {\n        std::string id;\n        bool isStackingSpell;\n        int timestampDay;\n        double timestampHour;\n        Target caster;\n        ESM::ActiveSpells::ActiveSpellParams params;\n    };\n\n    struct SpellsActiveChanges\n    {\n        std::vector<ActiveSpell> activeSpells;\n        enum ACTION_TYPE\n        {\n            SET = 0,\n            ADD,\n            REMOVE\n        };\n        int action; // 0 - Clear and set in entirety, 1 - Add spell, 2 - Remove spell\n    };\n\n    struct Animation\n    {\n        std::string groupname;\n        int mode;\n        int count;\n        bool persist;\n    };\n\n    struct SimpleCreatureStats\n    {\n        ESM::StatState<float> mDynamic[3];\n        bool mDead;\n        bool mDeathAnimationFinished;\n    };\n}\n\n#endif //OPENMW_BASESTRUCTS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Base/BaseSystem.hpp",
    "content": "#ifndef OPENMW_BASESYSTEM_HPP\n#define OPENMW_BASESYSTEM_HPP\n\n#include <string>\n\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    class BaseSystem\n    {\n    public:\n\n        BaseSystem(RakNet::RakNetGUID guid) : guid(guid)\n        {\n\n        }\n\n        BaseSystem()\n        {\n\n        }\n\n        RakNet::RakNetGUID guid;\n        std::string playerName;\n        std::string serverPassword;\n\n    };\n}\n\n#endif //OPENMW_BASESYSTEM_HPP\n"
  },
  {
    "path": "components/openmw-mp/Base/BaseWorldstate.hpp",
    "content": "#ifndef OPENMW_BASEWORLDSTATE_HPP\n#define OPENMW_BASEWORLDSTATE_HPP\n\n#include <map>\n#include <vector>\n\n#include <components/esm/loadacti.hpp>\n#include <components/esm/loadalch.hpp>\n#include <components/esm/loadappa.hpp>\n#include <components/esm/loadarmo.hpp>\n#include <components/esm/loadbody.hpp>\n#include <components/esm/loadbook.hpp>\n#include <components/esm/loadclot.hpp>\n#include <components/esm/loadcont.hpp>\n#include <components/esm/loadcrea.hpp>\n#include <components/esm/loaddoor.hpp>\n#include <components/esm/loadench.hpp>\n#include <components/esm/loadgmst.hpp>\n#include <components/esm/loadingr.hpp>\n#include <components/esm/loadligh.hpp>\n#include <components/esm/loadlock.hpp>\n#include <components/esm/loadmisc.hpp>\n#include <components/esm/loadnpc.hpp>\n#include <components/esm/loadprob.hpp>\n#include <components/esm/loadrepa.hpp>\n#include <components/esm/loadscpt.hpp>\n#include <components/esm/loadspel.hpp>\n#include <components/esm/loadstat.hpp>\n#include <components/esm/loadweap.hpp>\n#include <components/esm/loadsoun.hpp>\n\n#include <components/openmw-mp/Base/BaseStructs.hpp>\n\n#include <RakNetTypes.h>\n\nnamespace mwmp\n{\n    enum RECORD_TYPE\n    {\n        ACTIVATOR,\n        APPARATUS,\n        ARMOR,\n        BODYPART,\n        BOOK,\n        CELL,\n        CLOTHING,\n        CONTAINER,\n        CREATURE,\n        DOOR,\n        ENCHANTMENT,\n        GAMESETTING,\n        INGREDIENT,\n        LIGHT,\n        LOCKPICK,\n        MISCELLANEOUS,\n        NPC,\n        POTION,\n        PROBE,\n        REPAIR,\n        SCRIPT,\n        SOUND,\n        SPELL,\n        STATIC,\n        WEAPON\n    };\n\n    // When using an existing record as a base, this struct tracks which changes\n    // need to be made to it\n    //\n    // Note: These can't be replaced with checks for empty strings or numerical\n    //       values of 0 because you want to be able to blank out strings or\n    //       set values of 0 through overrides, i.e. if someone is in the\n    //       Mages Guild faction, you want to be able to remove them from it\n    //       by using a blank faction string as an override\n    //\n    //       There are, however, a few values that are not allowed to be blanked\n    //       out in a record, such as races or classes for NPCs, and those\n    //       should rely on checks for empty strings instead of having a\n    //       boolean here\n    struct BaseOverrides\n    {\n        bool hasSubtype = false;\n        bool hasName = false;\n        bool hasModel = false;\n        bool hasIcon = false;\n        bool hasScript = false;\n        bool hasEnchantmentId = false;\n        bool hasEnchantmentCharge = false;\n\n        bool hasEffects = false;\n        bool hasBodyParts = false;\n        bool hasInventory = false;\n\n        bool hasAutoCalc = false;\n        bool hasCharge = false;\n        bool hasCost = false;\n        bool hasFlags = false;\n        bool hasValue = false;\n        bool hasWeight = false;\n        bool hasQuality = false;\n        bool hasUses = false;\n        bool hasTime = false;\n        bool hasRadius = false;\n        bool hasColor = false;\n\n        bool hasArmorRating = false;\n        bool hasHealth = false;\n\n        bool hasDamageChop = false;\n        bool hasDamageSlash = false;\n        bool hasDamageThrust = false;\n        bool hasReach = false;\n        bool hasSpeed = false;\n\n        bool hasKeyState = false;\n        bool hasScrollState = false;\n        bool hasSkillId = false;\n        bool hasText = false;\n\n        bool hasHair = false;\n        bool hasHead = false;\n        bool hasGender = false;\n        bool hasRace = false;\n        bool hasFaction = false;\n\n        bool hasScale = false;\n        bool hasBloodType = false;\n        bool hasBodyPartType = false;\n        bool hasVampireState = false;\n\n        bool hasLevel = false;\n        bool hasMagicka = false;\n        bool hasFatigue = false;\n\n        bool hasSoulValue = false;\n\n        bool hasAiFight = false;\n        bool hasAiFlee = false;\n        bool hasAiAlarm = false;\n        bool hasAiServices = false;\n\n        bool hasSound = false;\n        bool hasOpenSound = false;\n        bool hasCloseSound = false;\n\n        bool hasScriptText = false;\n\n        bool hasVolume = false;\n        bool hasMinRange = false;\n        bool hasMaxRange = false;\n    };\n\n    struct ActivatorRecord\n    {\n        ESM::Activator data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct ApparatusRecord\n    {\n        ESM::Apparatus data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct ArmorRecord\n    {\n        ESM::Armor data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct BodyPartRecord\n    {\n        ESM::BodyPart data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct BookRecord\n    {\n        ESM::Book data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct CellRecord\n    {\n        ESM::Cell data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct ClothingRecord\n    {\n        ESM::Clothing data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct ContainerRecord\n    {\n        ESM::Container data;\n        std::string baseId;\n        std::vector<mwmp::Item> inventory;\n        BaseOverrides baseOverrides;\n    };\n\n    struct CreatureRecord\n    {\n        ESM::Creature data;\n        std::string baseId;\n        std::string inventoryBaseId;\n        std::vector<mwmp::Item> inventory;\n        BaseOverrides baseOverrides;\n    };\n\n    struct DoorRecord\n    {\n        ESM::Door data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct EnchantmentRecord\n    {\n        ESM::Enchantment data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct GameSettingRecord\n    {\n        ESM::GameSetting data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n        ClientVariable variable;\n    };\n\n    struct IngredientRecord\n    {\n        ESM::Ingredient data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct LightRecord\n    {\n        ESM::Light data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct LockpickRecord\n    {\n        ESM::Lockpick data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct MiscellaneousRecord\n    {\n        ESM::Miscellaneous data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct NpcRecord\n    {\n        ESM::NPC data;\n        std::string baseId;\n        std::string inventoryBaseId;\n        std::vector<mwmp::Item> inventory;\n        BaseOverrides baseOverrides;\n    };\n\n    struct PotionRecord\n    {\n        ESM::Potion data;\n        unsigned int quantity = 1;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct ProbeRecord\n    {\n        ESM::Probe data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct RepairRecord\n    {\n        ESM::Repair data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct ScriptRecord\n    {\n        ESM::Script data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct SoundRecord\n    {\n        ESM::Sound data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct SpellRecord\n    {\n        ESM::Spell data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct StaticRecord\n    {\n        ESM::Static data;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    struct WeaponRecord\n    {\n        ESM::Weapon data;\n        unsigned int quantity = 1;\n        std::string baseId;\n        BaseOverrides baseOverrides;\n    };\n\n    static const int maxImageDataSize = 1800;\n\n    struct MapTile\n    {\n        int x;\n        int y;\n        std::vector<char> imageData;\n    };\n\n    struct Weather\n    {\n        std::string region;\n        int currentWeather;\n        int nextWeather;\n        int queuedWeather;\n        float transitionFactor;\n    };\n\n    struct Kill\n    {\n        std::string refId;\n        int number;\n    };\n\n    class BaseWorldstate\n    {\n    public:\n\n        BaseWorldstate()\n        {\n            time.year = -1;\n            time.month = -1;\n            time.day = -1;\n            time.hour = -1;\n\n            time.daysPassed = -1;\n            time.timeScale = -1;\n        }\n\n        RakNet::RakNetGUID guid;\n\n        mwmp::Time time;\n        std::vector<std::string> synchronizedClientScriptIds;\n        std::vector<std::string> synchronizedClientGlobalIds;\n\n        std::vector<ClientVariable> clientGlobals;\n\n        bool hasPlayerCollision;\n        bool hasActorCollision;\n        bool hasPlacedObjectCollision;\n        bool useActorCollisionForPlacedObjects;\n\n        std::string authorityRegion;\n\n        std::vector<Kill> killChanges;\n        std::vector<std::string> enforcedCollisionRefIds;\n        std::map<std::string, std::string> destinationOverrides;\n\n        std::vector<MapTile> mapTiles;\n\n        bool forceWeather;\n        Weather weather;\n\n        unsigned short recordsType;\n        unsigned int recordsCount;\n\n        std::vector<ActivatorRecord> activatorRecords;\n        std::vector<ApparatusRecord> apparatusRecords;\n        std::vector<ArmorRecord> armorRecords;\n        std::vector<BodyPartRecord> bodyPartRecords;\n        std::vector<BookRecord> bookRecords;\n        std::vector<CellRecord> cellRecords;\n        std::vector<ClothingRecord> clothingRecords;\n        std::vector<ContainerRecord> containerRecords;\n        std::vector<CreatureRecord> creatureRecords;\n        std::vector<DoorRecord> doorRecords;\n        std::vector<EnchantmentRecord> enchantmentRecords;\n        std::vector<GameSettingRecord> gameSettingRecords;\n        std::vector<IngredientRecord> ingredientRecords;\n        std::vector<LightRecord> lightRecords;\n        std::vector<LockpickRecord> lockpickRecords;\n        std::vector<MiscellaneousRecord> miscellaneousRecords;\n        std::vector<NpcRecord> npcRecords;\n        std::vector<PotionRecord> potionRecords;\n        std::vector<ProbeRecord> probeRecords;\n        std::vector<RepairRecord> repairRecords;\n        std::vector<ScriptRecord> scriptRecords;\n        std::vector<SoundRecord> soundRecords;\n        std::vector<SpellRecord> spellRecords;\n        std::vector<StaticRecord> staticRecords;\n        std::vector<WeaponRecord> weaponRecords;\n\n        std::vector<ESM::Cell> cellsToReset;\n\n        bool isValid;\n    };\n}\n\n#endif //OPENMW_BASEWORLDSTATE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Controllers/ActorPacketController.cpp",
    "content": "#include \"../Packets/Actor/PacketActorList.hpp\"\n#include \"../Packets/Actor/PacketActorAuthority.hpp\"\n#include \"../Packets/Actor/PacketActorTest.hpp\"\n#include \"../Packets/Actor/PacketActorAI.hpp\"\n#include \"../Packets/Actor/PacketActorAnimFlags.hpp\"\n#include \"../Packets/Actor/PacketActorAnimPlay.hpp\"\n#include \"../Packets/Actor/PacketActorAttack.hpp\"\n#include \"../Packets/Actor/PacketActorCast.hpp\"\n#include \"../Packets/Actor/PacketActorCellChange.hpp\"\n#include \"../Packets/Actor/PacketActorDeath.hpp\"\n#include \"../Packets/Actor/PacketActorEquipment.hpp\"\n#include \"../Packets/Actor/PacketActorPosition.hpp\"\n#include \"../Packets/Actor/PacketActorSpeech.hpp\"\n#include \"../Packets/Actor/PacketActorSpellsActive.hpp\"\n#include \"../Packets/Actor/PacketActorStatsDynamic.hpp\"\n\n\n#include \"ActorPacketController.hpp\"\n\ntemplate <typename T>\ninline void AddPacket(mwmp::ActorPacketController::packets_t *packets, RakNet::RakPeerInterface *peer)\n{\n    T *packet = new T(peer);\n    typedef mwmp::ActorPacketController::packets_t::value_type value_t;\n    packets->insert(value_t(packet->GetPacketID(), value_t::second_type(packet)));\n}\n\nmwmp::ActorPacketController::ActorPacketController(RakNet::RakPeerInterface *peer)\n{\n    AddPacket<PacketActorList>(&packets, peer);\n    AddPacket<PacketActorAuthority>(&packets, peer);\n    AddPacket<PacketActorTest>(&packets, peer);\n    AddPacket<PacketActorAI>(&packets, peer);\n    AddPacket<PacketActorAnimFlags>(&packets, peer);\n    AddPacket<PacketActorAnimPlay>(&packets, peer);\n    AddPacket<PacketActorAttack>(&packets, peer);\n    AddPacket<PacketActorCast>(&packets, peer);\n    AddPacket<PacketActorCellChange>(&packets, peer);\n    AddPacket<PacketActorDeath>(&packets, peer);\n    AddPacket<PacketActorEquipment>(&packets, peer);\n    AddPacket<PacketActorPosition>(&packets, peer);\n    AddPacket<PacketActorSpeech>(&packets, peer);\n    AddPacket<PacketActorSpellsActive>(&packets, peer);\n    AddPacket<PacketActorStatsDynamic>(&packets, peer);\n}\n\n\nmwmp::ActorPacket *mwmp::ActorPacketController::GetPacket(RakNet::MessageID id)\n{\n    return packets[(unsigned char)id].get();\n}\n\nvoid mwmp::ActorPacketController::SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream)\n{\n    for(const auto &packet : packets)\n        packet.second->SetStreams(inStream, outStream);\n}\n\nbool mwmp::ActorPacketController::ContainsPacket(RakNet::MessageID id)\n{\n    for(const auto &packet : packets)\n    {\n        if (packet.first == id)\n            return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "components/openmw-mp/Controllers/ActorPacketController.hpp",
    "content": "#ifndef OPENMW_ACTORPACKETCONTROLLER_HPP\n#define OPENMW_ACTORPACKETCONTROLLER_HPP\n\n\n#include <RakPeerInterface.h>\n#include \"../Packets/Actor/ActorPacket.hpp\"\n#include <unordered_map>\n#include <memory>\n\nnamespace mwmp\n{\n    class ActorPacketController\n    {\n    public:\n        ActorPacketController(RakNet::RakPeerInterface *peer);\n        ActorPacket *GetPacket(RakNet::MessageID id);\n        void SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream);\n\n        bool ContainsPacket(RakNet::MessageID id);\n\n        typedef std::unordered_map<unsigned char, std::unique_ptr<ActorPacket> > packets_t;\n    private:\n        packets_t packets;\n    };\n}\n\n#endif //OPENMW_ACTORPACKETCONTROLLER_HPP\n"
  },
  {
    "path": "components/openmw-mp/Controllers/ObjectPacketController.cpp",
    "content": "#include \"../Packets/Object/PacketObjectActivate.hpp\"\n#include \"../Packets/Object/PacketObjectAnimPlay.hpp\"\n#include \"../Packets/Object/PacketObjectAttach.hpp\"\n#include \"../Packets/Object/PacketObjectDelete.hpp\"\n#include \"../Packets/Object/PacketObjectDialogueChoice.hpp\"\n#include \"../Packets/Object/PacketObjectHit.hpp\"\n#include \"../Packets/Object/PacketObjectLock.hpp\"\n#include \"../Packets/Object/PacketObjectMiscellaneous.hpp\"\n#include \"../Packets/Object/PacketObjectMove.hpp\"\n#include \"../Packets/Object/PacketObjectPlace.hpp\"\n#include \"../Packets/Object/PacketObjectRestock.hpp\"\n#include \"../Packets/Object/PacketObjectRotate.hpp\"\n#include \"../Packets/Object/PacketObjectScale.hpp\"\n#include \"../Packets/Object/PacketObjectSound.hpp\"\n#include \"../Packets/Object/PacketObjectSpawn.hpp\"\n#include \"../Packets/Object/PacketObjectState.hpp\"\n#include \"../Packets/Object/PacketObjectTrap.hpp\"\n\n#include \"../Packets/Object/PacketContainer.hpp\"\n#include \"../Packets/Object/PacketDoorDestination.hpp\"\n#include \"../Packets/Object/PacketDoorState.hpp\"\n#include \"../Packets/Object/PacketMusicPlay.hpp\"\n#include \"../Packets/Object/PacketVideoPlay.hpp\"\n\n#include \"../Packets/Object/PacketConsoleCommand.hpp\"\n#include \"../Packets/Object/PacketClientScriptLocal.hpp\"\n#include \"../Packets/Object/PacketScriptMemberShort.hpp\"\n\n#include \"ObjectPacketController.hpp\"\n\ntemplate <typename T>\ninline void AddPacket(mwmp::ObjectPacketController::packets_t *packets, RakNet::RakPeerInterface *peer)\n{\n    T *packet = new T(peer);\n    typedef mwmp::ObjectPacketController::packets_t::value_type value_t;\n    packets->insert(value_t(packet->GetPacketID(), value_t::second_type(packet)));\n}\n\nmwmp::ObjectPacketController::ObjectPacketController(RakNet::RakPeerInterface *peer)\n{\n    AddPacket<PacketObjectActivate>(&packets, peer);\n    AddPacket<PacketObjectAnimPlay>(&packets, peer);\n    AddPacket<PacketObjectAttach>(&packets, peer);\n    AddPacket<PacketObjectDelete>(&packets, peer);\n    AddPacket<PacketObjectDialogueChoice>(&packets, peer);\n    AddPacket<PacketObjectHit>(&packets, peer);\n    AddPacket<PacketObjectLock>(&packets, peer);\n    AddPacket<PacketObjectMiscellaneous>(&packets, peer);\n    AddPacket<PacketObjectMove>(&packets, peer);\n    AddPacket<PacketObjectPlace>(&packets, peer);\n    AddPacket<PacketObjectRestock>(&packets, peer);\n    AddPacket<PacketObjectRotate>(&packets, peer);\n    AddPacket<PacketObjectScale>(&packets, peer);\n    AddPacket<PacketObjectSound>(&packets, peer);\n    AddPacket<PacketObjectSpawn>(&packets, peer);\n    AddPacket<PacketObjectState>(&packets, peer);\n    AddPacket<PacketObjectTrap>(&packets, peer);\n    \n    AddPacket<PacketContainer>(&packets, peer);\n    AddPacket<PacketDoorDestination>(&packets, peer);\n    AddPacket<PacketDoorState>(&packets, peer);\n    AddPacket<PacketMusicPlay>(&packets, peer);\n    AddPacket<PacketVideoPlay>(&packets, peer);\n\n    AddPacket<PacketConsoleCommand>(&packets, peer);\n    AddPacket<PacketClientScriptLocal>(&packets, peer);\n    AddPacket<PacketScriptMemberShort>(&packets, peer);\n}\n\n\nmwmp::ObjectPacket *mwmp::ObjectPacketController::GetPacket(RakNet::MessageID id)\n{\n    return packets[(unsigned char)id].get();\n}\n\nvoid mwmp::ObjectPacketController::SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream)\n{\n    for(const auto &packet : packets)\n        packet.second->SetStreams(inStream, outStream);\n}\n\nbool mwmp::ObjectPacketController::ContainsPacket(RakNet::MessageID id)\n{\n    for(const auto &packet : packets)\n    {\n        if (packet.first == id)\n            return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "components/openmw-mp/Controllers/ObjectPacketController.hpp",
    "content": "#ifndef OPENMW_OBJECTPACKETCONTROLLER_HPP\n#define OPENMW_OBJECTPACKETCONTROLLER_HPP\n\n\n#include <RakPeerInterface.h>\n#include \"../Packets/Object/ObjectPacket.hpp\"\n#include <unordered_map>\n#include <memory>\n\nnamespace mwmp\n{\n    class ObjectPacketController\n    {\n    public:\n        ObjectPacketController(RakNet::RakPeerInterface *peer);\n        ObjectPacket *GetPacket(RakNet::MessageID id);\n        void SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream);\n\n        bool ContainsPacket(RakNet::MessageID id);\n\n        typedef std::unordered_map<unsigned char, std::unique_ptr<ObjectPacket> > packets_t;\n    private:\n        packets_t packets;\n    };\n}\n\n#endif //OPENMW_OBJECTPACKETCONTROLLER_HPP\n"
  },
  {
    "path": "components/openmw-mp/Controllers/PlayerPacketController.cpp",
    "content": "#include \"../Packets/Player/PacketDisconnect.hpp\"\n#include \"../Packets/Player/PacketChatMessage.hpp\"\n#include \"../Packets/Player/PacketPlayerCharGen.hpp\"\n#include \"../Packets/Player/PacketGUIBoxes.hpp\"\n#include \"../Packets/Player/PacketLoaded.hpp\"\n#include \"../Packets/Player/PacketGameSettings.hpp\"\n#include \"../Packets/Player/PacketPlayerSpellsActive.hpp\"\n#include \"../Packets/Player/PacketPlayerAlly.hpp\"\n#include \"../Packets/Player/PacketPlayerAnimFlags.hpp\"\n#include \"../Packets/Player/PacketPlayerAnimPlay.hpp\"\n#include \"../Packets/Player/PacketPlayerAttack.hpp\"\n#include \"../Packets/Player/PacketPlayerAttribute.hpp\"\n#include \"../Packets/Player/PacketPlayerBaseInfo.hpp\"\n#include \"../Packets/Player/PacketPlayerBehavior.hpp\"\n#include \"../Packets/Player/PacketPlayerBook.hpp\"\n#include \"../Packets/Player/PacketPlayerBounty.hpp\"\n#include \"../Packets/Player/PacketPlayerCast.hpp\"\n#include \"../Packets/Player/PacketPlayerCellChange.hpp\"\n#include \"../Packets/Player/PacketPlayerCellState.hpp\"\n#include \"../Packets/Player/PacketPlayerClass.hpp\"\n#include \"../Packets/Player/PacketPlayerCooldowns.hpp\"\n#include \"../Packets/Player/PacketPlayerDeath.hpp\"\n#include \"../Packets/Player/PacketPlayerEquipment.hpp\"\n#include \"../Packets/Player/PacketPlayerFaction.hpp\"\n#include \"../Packets/Player/PacketPlayerInput.hpp\"\n#include \"../Packets/Player/PacketPlayerInventory.hpp\"\n#include \"../Packets/Player/PacketPlayerItemUse.hpp\"\n#include \"../Packets/Player/PacketPlayerJail.hpp\"\n#include \"../Packets/Player/PacketPlayerJournal.hpp\"\n#include \"../Packets/Player/PacketPlayerLevel.hpp\"\n#include \"../Packets/Player/PacketPlayerMiscellaneous.hpp\"\n#include \"../Packets/Player/PacketPlayerMomentum.hpp\"\n#include \"../Packets/Player/PacketPlayerPosition.hpp\"\n#include \"../Packets/Player/PacketPlayerQuickKeys.hpp\"\n#include \"../Packets/Player/PacketPlayerReputation.hpp\"\n#include \"../Packets/Player/PacketPlayerRest.hpp\"\n#include \"../Packets/Player/PacketPlayerResurrect.hpp\"\n#include \"../Packets/Player/PacketPlayerShapeshift.hpp\"\n#include \"../Packets/Player/PacketPlayerSkill.hpp\"\n#include \"../Packets/Player/PacketPlayerSpeech.hpp\"\n#include \"../Packets/Player/PacketPlayerSpellbook.hpp\"\n#include \"../Packets/Player/PacketPlayerStatsDynamic.hpp\"\n#include \"../Packets/Player/PacketPlayerTopic.hpp\"\n\n#include \"PlayerPacketController.hpp\"\n\ntemplate <typename T>\ninline void AddPacket(mwmp::PlayerPacketController::packets_t *packets, RakNet::RakPeerInterface *peer)\n{\n    T *packet = new T(peer);\n    typedef mwmp::PlayerPacketController::packets_t::value_type value_t;\n    packets->insert(value_t(packet->GetPacketID(), value_t::second_type(packet)));\n}\n\nmwmp::PlayerPacketController::PlayerPacketController(RakNet::RakPeerInterface *peer)\n{\n    AddPacket<PacketDisconnect>(&packets, peer);\n    AddPacket<PacketChatMessage>(&packets, peer);\n    AddPacket<PacketGUIBoxes>(&packets, peer);\n    AddPacket<PacketLoaded>(&packets, peer);\n    AddPacket<PacketGameSettings>(&packets, peer);\n    AddPacket<PacketPlayerSpellsActive>(&packets, peer);\n\n    AddPacket<PacketPlayerAlly>(&packets, peer);\n    AddPacket<PacketPlayerAnimFlags>(&packets, peer);\n    AddPacket<PacketPlayerAnimPlay>(&packets, peer);\n    AddPacket<PacketPlayerAttack>(&packets, peer);\n    AddPacket<PacketPlayerAttribute>(&packets, peer);\n    AddPacket<PacketPlayerBaseInfo>(&packets, peer);\n    AddPacket<PacketPlayerBehavior>(&packets, peer);\n    AddPacket<PacketPlayerBook>(&packets, peer);\n    AddPacket<PacketPlayerBounty>(&packets, peer);\n    AddPacket<PacketPlayerCast>(&packets, peer);\n    AddPacket<PacketPlayerCellChange>(&packets, peer);\n    AddPacket<PacketPlayerCellState>(&packets, peer);\n    AddPacket<PacketPlayerCharGen>(&packets, peer);\n    AddPacket<PacketPlayerClass>(&packets, peer);\n    AddPacket<PacketPlayerCooldowns>(&packets, peer);\n    AddPacket<PacketPlayerDeath>(&packets, peer);\n    AddPacket<PacketPlayerEquipment>(&packets, peer);\n    AddPacket<PacketPlayerFaction>(&packets, peer);\n    AddPacket<PacketPlayerInput>(&packets, peer);\n    AddPacket<PacketPlayerInventory>(&packets, peer);\n    AddPacket<PacketPlayerItemUse>(&packets, peer);\n    AddPacket<PacketPlayerJail>(&packets, peer);\n    AddPacket<PacketPlayerJournal>(&packets, peer);\n    AddPacket<PacketPlayerLevel>(&packets, peer);\n    AddPacket<PacketPlayerMiscellaneous>(&packets, peer);\n    AddPacket<PacketPlayerMomentum>(&packets, peer);\n    AddPacket<PacketPlayerPosition>(&packets, peer);\n    AddPacket<PacketPlayerQuickKeys>(&packets, peer);\n    AddPacket<PacketPlayerReputation>(&packets, peer);\n    AddPacket<PacketPlayerRest>(&packets, peer);\n    AddPacket<PacketPlayerResurrect>(&packets, peer);\n    AddPacket<PacketPlayerShapeshift>(&packets, peer);\n    AddPacket<PacketPlayerSkill>(&packets, peer);\n    AddPacket<PacketPlayerSpeech>(&packets, peer);\n    AddPacket<PacketPlayerSpellbook>(&packets, peer);\n    AddPacket<PacketPlayerStatsDynamic>(&packets, peer);\n    AddPacket<PacketPlayerTopic>(&packets, peer);\n}\n\n\nmwmp::PlayerPacket *mwmp::PlayerPacketController::GetPacket(RakNet::MessageID id)\n{\n    return packets[(unsigned char)id].get();\n}\n\nvoid mwmp::PlayerPacketController::SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream)\n{\n    for(const auto &packet : packets)\n        packet.second->SetStreams(inStream, outStream);\n}\n\nbool mwmp::PlayerPacketController::ContainsPacket(RakNet::MessageID id)\n{\n    for(const auto &packet : packets)\n    {\n        if (packet.first == id)\n            return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "components/openmw-mp/Controllers/PlayerPacketController.hpp",
    "content": "#ifndef OPENMW_PLAYERPACKETCONTROLLER_HPP\n#define OPENMW_PLAYERPACKETCONTROLLER_HPP\n\n\n#include <RakPeerInterface.h>\n#include \"../Packets/Player/PlayerPacket.hpp\"\n#include <unordered_map>\n#include <memory>\n\nnamespace mwmp\n{\n    class PlayerPacketController\n    {\n    public:\n        PlayerPacketController(RakNet::RakPeerInterface *peer);\n        PlayerPacket *GetPacket(RakNet::MessageID id);\n        void SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream);\n\n        bool ContainsPacket(RakNet::MessageID id);\n\n        typedef std::unordered_map<unsigned char, std::unique_ptr<PlayerPacket> > packets_t;\n    private:\n        packets_t packets;\n    };\n}\n\n#endif //OPENMW_PLAYERPACKETCONTROLLER_HPP\n"
  },
  {
    "path": "components/openmw-mp/Controllers/SystemPacketController.cpp",
    "content": "#include \"../Packets/System/PacketSystemHandshake.hpp\"\n\n#include \"SystemPacketController.hpp\"\n\ntemplate <typename T>\ninline void AddPacket(mwmp::SystemPacketController::packets_t *packets, RakNet::RakPeerInterface *peer)\n{\n    T *packet = new T(peer);\n    typedef mwmp::SystemPacketController::packets_t::value_type value_t;\n    packets->insert(value_t(packet->GetPacketID(), value_t::second_type(packet)));\n}\n\nmwmp::SystemPacketController::SystemPacketController(RakNet::RakPeerInterface *peer)\n{\n    AddPacket<PacketSystemHandshake>(&packets, peer);\n}\n\n\nmwmp::SystemPacket *mwmp::SystemPacketController::GetPacket(RakNet::MessageID id)\n{\n    return packets[(unsigned char)id].get();\n}\n\nvoid mwmp::SystemPacketController::SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream)\n{\n    for(const auto &packet : packets)\n        packet.second->SetStreams(inStream, outStream);\n}\n\nbool mwmp::SystemPacketController::ContainsPacket(RakNet::MessageID id)\n{\n    for(const auto &packet : packets)\n    {\n        if (packet.first == id)\n            return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "components/openmw-mp/Controllers/SystemPacketController.hpp",
    "content": "#ifndef OPENMW_SYSTEMPACKETCONTROLLER_HPP\n#define OPENMW_SYSTEMPACKETCONTROLLER_HPP\n\n\n#include <RakPeerInterface.h>\n#include \"../Packets/System/SystemPacket.hpp\"\n#include <unordered_map>\n#include <memory>\n\nnamespace mwmp\n{\n    class SystemPacketController\n    {\n    public:\n        SystemPacketController(RakNet::RakPeerInterface *peer);\n        SystemPacket *GetPacket(RakNet::MessageID id);\n        void SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream);\n\n        bool ContainsPacket(RakNet::MessageID id);\n\n        typedef std::unordered_map<unsigned char, std::unique_ptr<SystemPacket> > packets_t;\n    private:\n        packets_t packets;\n    };\n}\n\n#endif //OPENMW_SYSTEMPACKETCONTROLLER_HPP\n"
  },
  {
    "path": "components/openmw-mp/Controllers/WorldstatePacketController.cpp",
    "content": "#include \"../Packets/Worldstate/PacketCellReset.hpp\"\n#include \"../Packets/Worldstate/PacketClientScriptGlobal.hpp\"\n#include \"../Packets/Worldstate/PacketClientScriptSettings.hpp\"\n#include \"../Packets/Worldstate/PacketRecordDynamic.hpp\"\n#include \"../Packets/Worldstate/PacketWorldCollisionOverride.hpp\"\n#include \"../Packets/Worldstate/PacketWorldDestinationOverride.hpp\"\n#include \"../Packets/Worldstate/PacketWorldKillCount.hpp\"\n#include \"../Packets/Worldstate/PacketWorldMap.hpp\"\n#include \"../Packets/Worldstate/PacketWorldRegionAuthority.hpp\"\n#include \"../Packets/Worldstate/PacketWorldTime.hpp\"\n#include \"../Packets/Worldstate/PacketWorldWeather.hpp\"\n\n#include \"WorldstatePacketController.hpp\"\n\ntemplate <typename T>\ninline void AddPacket(mwmp::WorldstatePacketController::packets_t *packets, RakNet::RakPeerInterface *peer)\n{\n    T *packet = new T(peer);\n    typedef mwmp::WorldstatePacketController::packets_t::value_type value_t;\n    packets->insert(value_t(packet->GetPacketID(), value_t::second_type(packet)));\n}\n\nmwmp::WorldstatePacketController::WorldstatePacketController(RakNet::RakPeerInterface *peer)\n{\n    AddPacket<PacketCellReset>(&packets, peer);\n    AddPacket<PacketClientScriptGlobal>(&packets, peer);\n    AddPacket<PacketClientScriptSettings>(&packets, peer);\n    AddPacket<PacketRecordDynamic>(&packets, peer);\n    AddPacket<PacketWorldCollisionOverride>(&packets, peer);\n    AddPacket<PacketWorldDestinationOverride>(&packets, peer);\n    AddPacket<PacketWorldKillCount>(&packets, peer);\n    AddPacket<PacketWorldMap>(&packets, peer);\n    AddPacket<PacketWorldRegionAuthority>(&packets, peer);\n    AddPacket<PacketWorldTime>(&packets, peer);\n    AddPacket<PacketWorldWeather>(&packets, peer);\n}\n\n\nmwmp::WorldstatePacket *mwmp::WorldstatePacketController::GetPacket(RakNet::MessageID id)\n{\n    return packets[(unsigned char)id].get();\n}\n\nvoid mwmp::WorldstatePacketController::SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream)\n{\n    for(const auto &packet : packets)\n        packet.second->SetStreams(inStream, outStream);\n}\n\nbool mwmp::WorldstatePacketController::ContainsPacket(RakNet::MessageID id)\n{\n    for(const auto &packet : packets)\n    {\n        if (packet.first == id)\n            return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "components/openmw-mp/Controllers/WorldstatePacketController.hpp",
    "content": "#ifndef OPENMW_WORLDSTATEPACKETCONTROLLER_HPP\n#define OPENMW_WORLDSTATEPACKETCONTROLLER_HPP\n\n\n#include <RakPeerInterface.h>\n#include \"../Packets/Worldstate/WorldstatePacket.hpp\"\n#include <unordered_map>\n#include <memory>\n\nnamespace mwmp\n{\n    class WorldstatePacketController\n    {\n    public:\n        WorldstatePacketController(RakNet::RakPeerInterface *peer);\n        WorldstatePacket *GetPacket(RakNet::MessageID id);\n        void SetStream(RakNet::BitStream *inStream, RakNet::BitStream *outStream);\n\n        bool ContainsPacket(RakNet::MessageID id);\n\n        typedef std::unordered_map<unsigned char, std::unique_ptr<WorldstatePacket> > packets_t;\n    private:\n        packets_t packets;\n    };\n}\n\n#endif //OPENMW_WORLDSTATEPACKETCONTROLLER_HPP\n"
  },
  {
    "path": "components/openmw-mp/ErrorMessages.hpp",
    "content": "#ifndef OPENMW_ERRORMESSAGES_HPP\n#define OPENMW_ERRORMESSAGES_HPP\n\n#define TES3MP_CREDITS_ERROR (\"To respect the hard work that has gone into creating TES3MP, you are\\n\"\\\n    \"required to keep an unmodified tes3mp-credits file corresponding to this\\n\"\\\n    \"version of the code in the same folder as this executable. It can have either\\n\"\\\n    \"an .md or a .txt extension.\")\n\n#endif //OPENMW_ERRORMESSAGES_HPP\n"
  },
  {
    "path": "components/openmw-mp/Master/MasterData.hpp",
    "content": "#ifndef NEWMASTERPROTO_MASTERDATA_HPP\n#define NEWMASTERPROTO_MASTERDATA_HPP\n\n#include <string>\n#include <utility>\n#include <vector>\n#include <map>\n#include <list>\n#include <MessageIdentifiers.h>\n\nenum MASTER_PACKETS\n{\n    ID_MASTER_QUERY = ID_USER_PACKET_ENUM,\n    ID_MASTER_UPDATE,\n    ID_MASTER_ANNOUNCE\n};\n\nstruct ServerRule\n{\n    enum Type : char\n    {\n        string = 's',\n        number = 'v'\n    } type;\n\n    std::string str;\n    double val;\n};\n\nstruct Plugin\n{\n    std::string name;\n    unsigned hash;\n    Plugin(std::string name = \"\", unsigned hash = 0): name(std::move(name)), hash(hash) {};\n};\n\nstruct QueryData\n{\n    QueryData()\n    {\n        rules[\"name\"].type = ServerRule::Type::string;\n        rules[\"name\"].str = \"\";\n        rules[\"version\"].type = ServerRule::Type::string;\n        rules[\"version\"].str = \"\";\n        rules[\"players\"].type = ServerRule::Type::number;\n        rules[\"players\"].val = 0;\n        rules[\"maxPlayers\"].type = ServerRule::Type::number;\n        rules[\"maxPlayers\"].val = 0;\n        rules[\"gamemode\"].type = ServerRule::Type::string;\n        rules[\"gamemode\"].str = \"\";\n        rules[\"passw\"].type = ServerRule::Type::number;\n        rules[\"passw\"].val = 0;\n    }\n    const char *GetName() const { return rules.at(\"name\").str.c_str(); }\n    void SetName(const char *name) { rules[\"name\"].str = name; }\n\n    const char *GetVersion() const { return rules.at(\"version\").str.c_str(); }\n    void SetVersion(const char *version) { rules[\"version\"].str = version; }\n\n    int GetPlayers() const { return (int) rules.at(\"players\").val; }\n    void SetPlayers(int value) { rules[\"players\"].val = value; }\n\n    int GetMaxPlayers() const { return (int) rules.at(\"maxPlayers\").val; }\n    void SetMaxPlayers(int value) { rules[\"maxPlayers\"].val = value; }\n\n    const char *GetGameMode() const { return rules.at(\"gamemode\").str.c_str(); }\n    void SetGameMode(const char *str) { rules[\"gamemode\"].str = str; }\n\n    void SetPassword(int value) { rules[\"passw\"].val = value; };\n    int GetPassword() const { return (int) rules.at(\"passw\").val; }\n\n\n    std::vector<std::string> players;\n    std::map<std::string, ServerRule> rules;\n    std::vector<Plugin> plugins;\n    const static int predefinedRules = 6;\n    const static int maxRules = 128;\n    const static int maxUserRules = maxRules - predefinedRules;\n    const static int maxPlayers = 100; // will shown only maxPlayers players\n    const static int maxPlugins = 1000;\n    const static int maxStringLength = 256;\n};\n\n#endif //NEWMASTERPROTO_MASTERDATA_HPP\n"
  },
  {
    "path": "components/openmw-mp/Master/PacketMasterAnnounce.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <iostream>\n#include \"PacketMasterAnnounce.hpp\"\n#include \"ProxyMasterPacket.hpp\"\n\nusing namespace mwmp;\nusing namespace RakNet;\n\nPacketMasterAnnounce::PacketMasterAnnounce(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    packetID = ID_MASTER_ANNOUNCE;\n    orderChannel = CHANNEL_MASTER;\n    reliability = RELIABLE_ORDERED_WITH_ACK_RECEIPT;\n}\n\nvoid PacketMasterAnnounce::Packet(BitStream *newBitstream, bool send)\n{\n    bs = newBitstream;\n    if (send)\n        bs->Write(packetID);\n\n    RW(func, send);\n\n    if (func == FUNCTION_ANNOUNCE)\n        ProxyMasterPacket::addServer(this, *server, send);\n}\n\nvoid PacketMasterAnnounce::SetServer(QueryData *_server)\n{\n    server = _server;\n}\n\nvoid PacketMasterAnnounce::SetFunc(uint32_t _func)\n{\n    func = _func;\n}\n\nint PacketMasterAnnounce::GetFunc()\n{\n    return func;\n}\n"
  },
  {
    "path": "components/openmw-mp/Master/PacketMasterAnnounce.hpp",
    "content": "#ifndef OPENMW_PACKETMASTERANNOUNCE_HPP\n#define OPENMW_PACKETMASTERANNOUNCE_HPP\n\n#include \"../Packets/BasePacket.hpp\"\n#include \"MasterData.hpp\"\n\nnamespace mwmp\n{\n    class ProxyMasterPacket;\n    class PacketMasterAnnounce : public BasePacket\n    {\n        friend class ProxyMasterPacket;\n    public:\n        explicit PacketMasterAnnounce(RakNet::RakPeerInterface *peer);\n\n        void Packet(RakNet::BitStream *newBitstream, bool send) override;\n\n        void SetServer(QueryData *server);\n        void SetFunc(uint32_t keep);\n        int GetFunc();\n\n        enum Func\n        {\n            FUNCTION_DELETE = 0,\n            FUNCTION_ANNOUNCE,\n            FUNCTION_KEEP\n        };\n    private:\n        QueryData *server;\n        uint32_t func;\n    };\n}\n\n#endif //OPENMW_PACKETMASTERANNOUNCE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Master/PacketMasterQuery.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <iostream>\n#include \"MasterData.hpp\"\n#include \"PacketMasterQuery.hpp\"\n#include \"ProxyMasterPacket.hpp\"\n\n\nusing namespace mwmp;\nusing namespace RakNet;\n\nPacketMasterQuery::PacketMasterQuery(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    packetID = ID_MASTER_QUERY;\n    orderChannel = CHANNEL_MASTER;\n    reliability = RELIABLE_ORDERED_WITH_ACK_RECEIPT;\n}\n\nvoid PacketMasterQuery::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    bs = newBitstream;\n    if (send)\n        bs->Write(packetID);\n\n    int32_t serversCount = servers->size();\n\n    RW(serversCount, send);\n\n    std::map<SystemAddress, QueryData>::iterator serverIt;\n    if (send)\n        serverIt = servers->begin();\n\n    QueryData server;\n    std::string addr;\n    uint16_t port;\n    while (serversCount--)\n    {\n        if (send)\n        {\n            addr = serverIt->first.ToString(false);\n            port = serverIt->first.GetPort();\n            server = serverIt->second;\n        }\n        RW(addr, send);\n        RW(port, send);\n\n        ProxyMasterPacket::addServer(this, server, send);\n\n        if(addr.empty())\n        {\n            std::cerr << \"Address empty. Aborting PacketMasterQuery::Packet\" << std::endl;\n            return;\n        }\n\n        if (send)\n            serverIt++;\n        else\n            servers->insert(std::pair<SystemAddress, QueryData>(SystemAddress(addr.c_str(), port), server));\n    }\n\n}\n\nvoid PacketMasterQuery::SetServers(std::map<SystemAddress, QueryData> *serverMap)\n{\n    servers = serverMap;\n}\n"
  },
  {
    "path": "components/openmw-mp/Master/PacketMasterQuery.hpp",
    "content": "#ifndef OPENMW_PACKETMASTERQUERY_HPP\n#define OPENMW_PACKETMASTERQUERY_HPP\n\n#include \"../Packets/BasePacket.hpp\"\n#include \"MasterData.hpp\"\n\nnamespace mwmp\n{\n    class ProxyMasterPacket;\n    class PacketMasterQuery : public BasePacket\n    {\n        friend class ProxyMasterPacket;\n    public:\n        explicit PacketMasterQuery(RakNet::RakPeerInterface *peer);\n\n        void Packet(RakNet::BitStream *newBitstream, bool send) override;\n\n        void SetServers(std::map<RakNet::SystemAddress, QueryData> *serverMap);\n    private:\n        std::map<RakNet::SystemAddress, QueryData> *servers;\n    };\n}\n\n#endif //OPENMW_PACKETMASTERQUERY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Master/PacketMasterUpdate.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketMasterUpdate.hpp\"\n#include \"ProxyMasterPacket.hpp\"\n\nusing namespace mwmp;\nusing namespace RakNet;\n\nPacketMasterUpdate::PacketMasterUpdate(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    packetID = ID_MASTER_UPDATE;\n    orderChannel = CHANNEL_MASTER;\n    reliability = RELIABLE_ORDERED_WITH_ACK_RECEIPT;\n}\n\nvoid PacketMasterUpdate::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    bs = newBitstream;\n    if (send)\n        bs->Write(packetID);\n\n    std::string addr = server->first.ToString(false);\n    uint16_t port = server->first.GetPort();\n\n    RW(addr, send);\n    RW(port, send);\n\n    if (!send)\n        server->first = SystemAddress(addr.c_str(), port);\n\n    ProxyMasterPacket::addServer(this, server->second, send);\n\n}\n\nvoid PacketMasterUpdate::SetServer(std::pair<RakNet::SystemAddress, QueryData> *serverPair)\n{\n    server = serverPair;\n}\n"
  },
  {
    "path": "components/openmw-mp/Master/PacketMasterUpdate.hpp",
    "content": "#ifndef OPENMW_PACKETMASTERUPDATE_HPP\n#define OPENMW_PACKETMASTERUPDATE_HPP\n\n#include \"../Packets/BasePacket.hpp\"\n#include \"MasterData.hpp\"\n\nnamespace mwmp\n{\n    class ProxyMasterPacket;\n    class PacketMasterUpdate : public BasePacket\n    {\n        friend class ProxyMasterPacket;\n    public:\n        explicit PacketMasterUpdate(RakNet::RakPeerInterface *peer);\n\n        void Packet(RakNet::BitStream *newBitstream, bool send) override;\n\n        void SetServer(std::pair<RakNet::SystemAddress, QueryData> *serverPair);\n    private:\n        std::pair<RakNet::SystemAddress, QueryData> *server;\n    };\n}\n\n#endif //OPENMW_PACKETMASTERUPDATE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Master/ProxyMasterPacket.hpp",
    "content": "//\n// Created by koncord on 22.04.17.\n//\n\n#ifndef OPENMW_PROXYMASTERPACKET_HPP\n#define OPENMW_PROXYMASTERPACKET_HPP\n\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n#include \"MasterData.hpp\"\n#include <iostream>\n\nnamespace mwmp\n{\n    class ProxyMasterPacket : public BasePacket\n    {\n    private:\n        explicit ProxyMasterPacket(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n        {\n        }\n\n    public:\n        template<class Packet>\n        static void addServer(Packet *packet, QueryData &server, bool send)\n        {\n            int32_t rulesSize = server.rules.size();\n            packet->RW(rulesSize, send);\n\n            if (rulesSize > QueryData::maxRules)\n                rulesSize = 0;\n\n            std::map<std::string, ServerRule>::iterator ruleIt;\n            if (send)\n                ruleIt = server.rules.begin();\n\n            while (rulesSize--)\n            {\n                ServerRule *rule = nullptr;\n                std::string key;\n                if (send)\n                {\n                    key = ruleIt->first;\n                    rule = &ruleIt->second;\n                }\n\n                packet->RW(key, send, false, QueryData::maxStringLength);\n                if (!send)\n                {\n                    ruleIt = server.rules.insert(std::pair<std::string, ServerRule>(key, ServerRule())).first;\n                    rule = &ruleIt->second;\n                }\n\n                packet->RW(rule->type, send);\n\n                if (rule->type == ServerRule::Type::string)\n                    packet->RW(rule->str, send, QueryData::maxStringLength);\n                else\n                    packet->RW(rule->val, send);\n\n                if (send)\n                    ruleIt++;\n            }\n\n            std::vector<std::string>::iterator plIt;\n\n            int32_t playersCount = server.players.size();\n            packet->RW(playersCount, send);\n\n            if (playersCount > QueryData::maxPlayers)\n                playersCount = 0;\n\n            if (!send)\n            {\n                server.players.clear();\n                server.players.resize(playersCount);\n            }\n\n            for(auto &&player : server.players)\n                packet->RW(player, send, false, QueryData::maxStringLength);\n\n\n            int32_t pluginsCount = server.plugins.size();\n            packet->RW(pluginsCount, send);\n\n            if (pluginsCount > QueryData::maxPlugins)\n                pluginsCount = 0;\n\n            if (!send)\n            {\n                server.plugins.clear();\n                server.plugins.resize(pluginsCount);\n            }\n\n            for (auto &&plugin : server.plugins)\n            {\n                packet->RW(plugin.name, send, false, QueryData::maxStringLength);\n                packet->RW(plugin.hash, send);\n            }\n        }\n    };\n}\n\n#endif //OPENMW_PROXYMASTERPACKET_HPP\n"
  },
  {
    "path": "components/openmw-mp/NetworkMessages.hpp",
    "content": "#ifndef OPENMW_NETWORKMESSAGES_HPP\n#define OPENMW_NETWORKMESSAGES_HPP\n\n#include <MessageIdentifiers.h>\n\nenum GameMessages\n{\n    _ID_UNUSED = ID_USER_PACKET_ENUM+1,\n    ID_USER_MYID,\n    ID_USER_DISCONNECTED,\n    ID_CHAT_MESSAGE,\n\n    ID_SYSTEM_HANDSHAKE,\n    ID_LOADED,\n    ID_GUI_MESSAGEBOX,\n    \n    ID_PLAYER_BASEINFO,\n    ID_PLAYER_BEHAVIOR,\n    ID_PLAYER_CHARGEN,\n    ID_PLAYER_SPELLS_ACTIVE,\n    ID_PLAYER_ANIM_FLAGS,\n    ID_PLAYER_ANIM_PLAY,\n    ID_PLAYER_ATTACK,\n    ID_PLAYER_ATTRIBUTE,\n    ID_PLAYER_BOOK,\n    ID_PLAYER_BOUNTY,\n    ID_PLAYER_CELL_CHANGE,\n    ID_PLAYER_CELL_STATE,\n    ID_PLAYER_CHARCLASS,\n    ID_PLAYER_DEATH,\n    ID_PLAYER_DISPOSITION,\n    ID_PLAYER_EQUIPMENT,\n    ID_PLAYER_FACTION,\n    ID_PLAYER_INPUT,\n    ID_PLAYER_INVENTORY,\n    ID_PLAYER_JAIL,\n    ID_PLAYER_JOURNAL,\n    ID_WORLD_KILL_COUNT,\n    ID_PLAYER_LEVEL,\n    ID_PLAYER_MISCELLANEOUS,\n    ID_PLAYER_MOMENTUM,\n    ID_PLAYER_POSITION,\n    ID_PLAYER_QUICKKEYS,\n    ID_WORLD_REGION_AUTHORITY,\n    ID_PLAYER_REPUTATION,\n    ID_PLAYER_RESURRECT,\n    ID_PLAYER_REST,\n    ID_PLAYER_SHAPESHIFT,\n    ID_PLAYER_SKILL,\n    ID_PLAYER_SPEECH,\n    ID_PLAYER_SPELLBOOK,\n    ID_PLAYER_STATS_DYNAMIC,\n    ID_PLAYER_TOPIC,\n\n    ID_ACTOR_LIST,\n    ID_ACTOR_AUTHORITY,\n    ID_ACTOR_TEST,\n    ID_ACTOR_AI,\n    ID_ACTOR_ANIM_FLAGS,\n    ID_ACTOR_ANIM_PLAY,\n    ID_ACTOR_ATTACK,\n    ID_ACTOR_CELL_CHANGE,\n    ID_ACTOR_DEATH,\n    ID_ACTOR_EQUIPMENT,\n    ID_ACTOR_CAST,\n    ID_ACTOR_POSITION,\n    ID_ACTOR_SPEECH,\n    ID_ACTOR_STATS_DYNAMIC,\n\n    ID_OBJECT_ACTIVATE,\n    ID_OBJECT_ANIM_PLAY,\n    ID_OBJECT_ATTACH,\n    ID_OBJECT_SOUND,\n    ID_OBJECT_DELETE,\n    ID_OBJECT_LOCK,\n    ID_OBJECT_MOVE,\n    ID_OBJECT_PLACE,\n    ID_OBJECT_HIT,\n    ID_OBJECT_ROTATE,\n    ID_OBJECT_SCALE,\n    ID_OBJECT_SPAWN,\n    ID_OBJECT_STATE,\n    ID_OBJECT_TRAP,\n    \n    ID_CONSOLE_COMMAND,\n    ID_CONTAINER,\n    ID_DOOR_DESTINATION,\n    ID_DOOR_STATE,\n    ID_MUSIC_PLAY,\n    ID_VIDEO_PLAY,\n\n    ID_CLIENT_SCRIPT_LOCAL,\n    ID_OBJECT_DIALOGUE_CHOICE,\n    ID_SCRIPT_MEMBER_SHORT,\n    ID_OBJECT_MISCELLANEOUS,\n    ID_CLIENT_SCRIPT_GLOBAL,\n    ID_OBJECT_RESTOCK,\n\n    ID_GAME_SETTINGS,\n    ID_GAME_PREINIT,\n\n    ID_CLIENT_SCRIPT_SETTINGS,\n    ID_CELL_RESET,\n    ID_RECORD_DYNAMIC,\n    ID_WORLD_COLLISION_OVERRIDE,\n    ID_WORLD_MAP,\n    ID_WORLD_TIME,\n    ID_WORLD_WEATHER,\n\n    ID_PLAYER_ITEM_USE,\n    ID_PLAYER_CAST,\n    ID_PLAYER_ALLY,\n    ID_WORLD_DESTINATION_OVERRIDE,\n    ID_ACTOR_SPELLS_ACTIVE,\n    ID_PLAYER_COOLDOWNS,\n    ID_PLACEHOLDER\n};\n\nenum OrderingChannel\n{\n    CHANNEL_SYSTEM = 0,\n    CHANNEL_ACTOR,\n    CHANNEL_PLAYER,\n    CHANNEL_OBJECT,\n    CHANNEL_MASTER,\n    CHANNEL_WORLDSTATE\n};\n\n\n#endif //OPENMW_NETWORKMESSAGES_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/ActorPacket.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <PacketPriority.h>\n#include <RakPeer.h>\n#include \"ActorPacket.hpp\"\n\nusing namespace mwmp;\n\nActorPacket::ActorPacket(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    packetID = 0;\n    priority = HIGH_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n    orderChannel = CHANNEL_ACTOR;\n    this->peer = peer;\n}\n\nActorPacket::~ActorPacket()\n{\n\n}\n\nvoid ActorPacket::setActorList(BaseActorList *newActorList)\n{\n    actorList = newActorList;\n    guid = actorList->guid;\n}\n\nvoid ActorPacket::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    if (!PacketHeader(newBitstream, send))\n        return;\n\n    BaseActor actor;\n\n    for (unsigned int i = 0; i < actorList->count; i++)\n    {\n        if (send)\n            actor = actorList->baseActors.at(i);\n\n        RW(actor.refNum, send);\n        RW(actor.mpNum, send);\n\n        Actor(actor, send);\n\n        if (!send)\n            actorList->baseActors.push_back(actor);\n    }\n}\n\nbool ActorPacket::PacketHeader(RakNet::BitStream *newBitstream, bool send)\n{\n    BasePacket::Packet(newBitstream, send);\n\n    RW(actorList->cell.mData, send, true);\n    RW(actorList->cell.mName, send, true);\n\n    if (send)\n        actorList->count = (unsigned int)(actorList->baseActors.size());\n    else\n        actorList->baseActors.clear();\n\n    RW(actorList->count, send);\n\n    if (actorList->count > maxActors)\n    {\n        actorList->isValid = false;\n        return false;\n    }\n\n    return true;\n}\n\n\nvoid ActorPacket::Actor(BaseActor &actor, bool send)\n{\n\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/ActorPacket.hpp",
    "content": "#ifndef OPENMW_ACTORPACKET_HPP\n#define OPENMW_ACTORPACKET_HPP\n\n#include <string>\n#include <RakNetTypes.h>\n#include <BitStream.h>\n#include <PacketPriority.h>\n#include <components/openmw-mp/Base/BaseActor.hpp>\n\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n\n\nnamespace mwmp\n{\n    class ActorPacket : public BasePacket\n    {\n    public:\n        ActorPacket(RakNet::RakPeerInterface *peer);\n\n        ~ActorPacket();\n\n        void setActorList(BaseActorList *newActorList);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    protected:\n        bool PacketHeader(RakNet::BitStream *newBitstream, bool send);\n        virtual void Actor(BaseActor &actor, bool send);\n        BaseActorList *actorList;\n        static const int maxActors = 3000;\n    };\n}\n\n#endif //OPENMW_ACTORPACKET_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAI.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorAI.hpp\"\n\nusing namespace mwmp;\n\nPacketActorAI::PacketActorAI(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_AI;\n}\n\nvoid PacketActorAI::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.aiAction, send);\n\n    if (actor.aiAction != mwmp::BaseActorList::CANCEL)\n    {\n        if (actor.aiAction == mwmp::BaseActorList::WANDER)\n        {\n            RW(actor.aiDistance, send);\n            RW(actor.aiShouldRepeat, send);\n        }\n\n        if (actor.aiAction == mwmp::BaseActorList::ESCORT || actor.aiAction == mwmp::BaseActorList::WANDER)\n            RW(actor.aiDuration, send);\n\n        if (actor.aiAction == mwmp::BaseActorList::ESCORT || actor.aiAction == mwmp::BaseActorList::TRAVEL)\n            RW(actor.aiCoordinates, send);\n\n        if (actor.aiAction == mwmp::BaseActorList::ACTIVATE || actor.aiAction == mwmp::BaseActorList::COMBAT ||\n            actor.aiAction == mwmp::BaseActorList::ESCORT || actor.aiAction == mwmp::BaseActorList::FOLLOW)\n        {\n            RW(actor.hasAiTarget, send);\n\n            if (actor.hasAiTarget)\n            {\n                RW(actor.aiTarget.isPlayer, send);\n\n                if (actor.aiTarget.isPlayer)\n                {\n                    RW(actor.aiTarget.guid, send);\n                }\n                else\n                {\n                    RW(actor.aiTarget.refId, send, true);\n                    RW(actor.aiTarget.refNum, send);\n                    RW(actor.aiTarget.mpNum, send);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAI.hpp",
    "content": "#ifndef OPENMW_PACKETACTORAI_HPP\n#define OPENMW_PACKETACTORAI_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorAI : public ActorPacket\n    {\n    public:\n        PacketActorAI(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORAI_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAnimFlags.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorAnimFlags.hpp\"\n\nusing namespace mwmp;\n\nPacketActorAnimFlags::PacketActorAnimFlags(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_ANIM_FLAGS;\n}\n\nvoid PacketActorAnimFlags::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.movementFlags, send);\n    RW(actor.drawState, send);\n    RW(actor.isFlying, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAnimFlags.hpp",
    "content": "#ifndef OPENMW_PACKETACTORANIMFLAGS_HPP\n#define OPENMW_PACKETACTORANIMFLAGS_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorAnimFlags : public ActorPacket\n    {\n    public:\n        PacketActorAnimFlags(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORANIMFLAGS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAnimPlay.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorAnimPlay.hpp\"\n\nusing namespace mwmp;\n\nPacketActorAnimPlay::PacketActorAnimPlay(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_ANIM_PLAY;\n}\n\nvoid PacketActorAnimPlay::Actor(BaseActor &actor, bool send)\n{\n\n    RW(actor.animation.groupname, send);\n    RW(actor.animation.mode, send);\n    RW(actor.animation.count, send);\n    RW(actor.animation.persist, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAnimPlay.hpp",
    "content": "#ifndef OPENMW_PACKETACTORANIMPLAY_HPP\n#define OPENMW_PACKETACTORANIMPLAY_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorAnimPlay : public ActorPacket\n    {\n    public:\n        PacketActorAnimPlay(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORANIMPLAY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAttack.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorAttack.hpp\"\n\nusing namespace mwmp;\n\nPacketActorAttack::PacketActorAttack(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_ATTACK;\n}\n\nvoid PacketActorAttack::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.attack.target.isPlayer, send);\n\n    if (actor.attack.target.isPlayer)\n    {\n        RW(actor.attack.target.guid, send);\n    }\n    else\n    {\n        RW(actor.attack.target.refId, send, true);\n        RW(actor.attack.target.refNum, send);\n        RW(actor.attack.target.mpNum, send);\n    }\n\n    RW(actor.attack.type, send);\n\n    RW(actor.attack.pressed, send);\n    RW(actor.attack.success, send);\n\n    RW(actor.attack.isHit, send);\n\n    if (actor.attack.type == mwmp::Attack::MELEE)\n    {\n        RW(actor.attack.attackAnimation, send, true);\n    }\n    else if (actor.attack.type == mwmp::Attack::RANGED)\n    {\n        RW(actor.attack.attackStrength, send);\n        RW(actor.attack.rangedWeaponId, send, true);\n        RW(actor.attack.rangedAmmoId, send, true);\n\n        RW(actor.attack.projectileOrigin.origin[0], send);\n        RW(actor.attack.projectileOrigin.origin[1], send);\n        RW(actor.attack.projectileOrigin.origin[2], send);\n        RW(actor.attack.projectileOrigin.orientation[0], send);\n        RW(actor.attack.projectileOrigin.orientation[1], send);\n        RW(actor.attack.projectileOrigin.orientation[2], send);\n        RW(actor.attack.projectileOrigin.orientation[3], send);\n    }\n\n    if (actor.attack.isHit)\n    {\n        RW(actor.attack.damage, send);\n        RW(actor.attack.block, send);\n        RW(actor.attack.knockdown, send);\n        RW(actor.attack.applyWeaponEnchantment, send);\n\n        if (actor.attack.type == mwmp::Attack::RANGED)\n            RW(actor.attack.applyAmmoEnchantment, send);\n\n        RW(actor.attack.hitPosition.pos[0], send);\n        RW(actor.attack.hitPosition.pos[1], send);\n        RW(actor.attack.hitPosition.pos[2], send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAttack.hpp",
    "content": "#ifndef OPENMW_PACKETACTORATTACK_HPP\n#define OPENMW_PACKETACTORATTACK_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorAttack : public ActorPacket\n    {\n    public:\n        PacketActorAttack(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORATTACK_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAuthority.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketActorAuthority.hpp\"\n\nusing namespace mwmp;\n\nPacketActorAuthority::PacketActorAuthority(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_AUTHORITY;\n}\n\nvoid PacketActorAuthority::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    BasePacket::Packet(newBitstream, send);\n\n    RW(actorList->cell.mData, send, true);\n    RW(actorList->cell.mName, send, true);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorAuthority.hpp",
    "content": "#ifndef OPENMW_PACKETACTORAUTHORITY_HPP\n#define OPENMW_PACKETACTORAUTHORITY_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorAuthority : public ActorPacket\n    {\n    public:\n        PacketActorAuthority(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORAUTHORITY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorCast.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorCast.hpp\"\n\nusing namespace mwmp;\n\nPacketActorCast::PacketActorCast(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_CAST;\n}\n\nvoid PacketActorCast::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.cast.target.isPlayer, send);\n\n    if (actor.cast.target.isPlayer)\n    {\n        RW(actor.cast.target.guid, send);\n    }\n    else\n    {\n        RW(actor.cast.target.refId, send, true);\n        RW(actor.cast.target.refNum, send);\n        RW(actor.cast.target.mpNum, send);\n    }\n\n    RW(actor.cast.type, send);\n\n    if (actor.cast.type == mwmp::Cast::ITEM)\n        RW(actor.cast.itemId, send, true);\n    else\n    {\n        RW(actor.cast.pressed, send);\n        RW(actor.cast.success, send);\n\n        RW(actor.cast.instant, send);\n        RW(actor.cast.spellId, send, true);\n    }\n\n    RW(actor.cast.hasProjectile, send);\n\n    if (actor.cast.hasProjectile)\n    {\n        RW(actor.cast.projectileOrigin.origin[0], send);\n        RW(actor.cast.projectileOrigin.origin[1], send);\n        RW(actor.cast.projectileOrigin.origin[2], send);\n        RW(actor.cast.projectileOrigin.orientation[0], send);\n        RW(actor.cast.projectileOrigin.orientation[1], send);\n        RW(actor.cast.projectileOrigin.orientation[2], send);\n        RW(actor.cast.projectileOrigin.orientation[3], send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorCast.hpp",
    "content": "#ifndef OPENMW_PACKETACTORCAST_HPP\n#define OPENMW_PACKETACTORCAST_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorCast : public ActorPacket\n    {\n    public:\n        PacketActorCast(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORCAST_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorCellChange.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorCellChange.hpp\"\n\nusing namespace mwmp;\n\nPacketActorCellChange::PacketActorCellChange(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_CELL_CHANGE;\n}\n\nvoid PacketActorCellChange::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.cell.mData, send, true);\n    RW(actor.cell.mName, send, true);\n\n    RW(actor.position, send, true);\n    RW(actor.direction, send, true);\n\n    RW(actor.isFollowerCellChange, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorCellChange.hpp",
    "content": "#ifndef OPENMW_PACKETACTORCELLCHANGE_HPP\n#define OPENMW_PACKETACTORCELLCHANGE_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorCellChange : public ActorPacket\n    {\n    public:\n        PacketActorCellChange(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORCELLCHANGE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorDeath.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorDeath.hpp\"\n\nusing namespace mwmp;\n\nPacketActorDeath::PacketActorDeath(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_DEATH;\n}\n\nvoid PacketActorDeath::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.refId, send);\n\n    RW(actor.deathState, send);\n    RW(actor.isInstantDeath, send);\n    RW(actor.killer.isPlayer, send);\n\n    if (actor.killer.isPlayer)\n    {\n        RW(actor.killer.guid, send);\n    }\n    else\n    {\n        RW(actor.killer.refId, send, true);\n        RW(actor.killer.refNum, send);\n        RW(actor.killer.mpNum, send);\n\n        RW(actor.killer.name, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorDeath.hpp",
    "content": "#ifndef OPENMW_PACKETACTORDEATH_HPP\n#define OPENMW_PACKETACTORDEATH_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorDeath : public ActorPacket\n    {\n    public:\n        PacketActorDeath(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORDEATH_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorEquipment.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorEquipment.hpp\"\n\nusing namespace mwmp;\n\nPacketActorEquipment::PacketActorEquipment(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_EQUIPMENT;\n}\n\nvoid PacketActorEquipment::Actor(BaseActor &actor, bool send)\n{\n    for (auto &&equipmentItem : actor.equipmentItems)\n    {\n        RW(equipmentItem.refId, send);\n        RW(equipmentItem.count, send);\n        RW(equipmentItem.charge, send);\n        RW(equipmentItem.enchantmentCharge, send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorEquipment.hpp",
    "content": "#ifndef OPENMW_PACKETACTOREQUIPMENT_HPP\n#define OPENMW_PACKETACTOREQUIPMENT_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorEquipment : public ActorPacket\n    {\n    public:\n        PacketActorEquipment(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTOREQUIPMENT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorList.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketActorList.hpp\"\n\nusing namespace mwmp;\n\nPacketActorList::PacketActorList(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_LIST;\n}\n\nvoid PacketActorList::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    if (!ActorPacket::PacketHeader(newBitstream, send))\n        return;\n\n    RW(actorList->action, send);\n\n    BaseActor actor;\n\n    for (unsigned int i = 0; i < actorList->count; i++)\n    {\n        if (send)\n            actor = actorList->baseActors.at(i);\n\n        RW(actor.refId, send);\n        RW(actor.refNum, send);\n        RW(actor.mpNum, send);\n\n        if (actor.refId.empty() || (actor.refNum != 0 && actor.mpNum != 0))\n        {\n            actorList->isValid = false;\n            return;\n        }\n\n        if (!send)\n            actorList->baseActors.push_back(actor);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorList.hpp",
    "content": "#ifndef OPENMW_PACKETACTORLIST_HPP\n#define OPENMW_PACKETACTORLIST_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorList : public ActorPacket\n    {\n    public:\n        PacketActorList(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORLIST_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorPosition.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorPosition.hpp\"\n\nusing namespace mwmp;\n\nPacketActorPosition::PacketActorPosition(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_POSITION;\n}\n\nvoid PacketActorPosition::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.position, send, true);\n    RW(actor.direction, send, true);\n\n    actor.hasPositionData = true;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorPosition.hpp",
    "content": "#ifndef OPENMW_PACKETACTORPOSITION_HPP\n#define OPENMW_PACKETACTORPOSITION_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorPosition : public ActorPacket\n    {\n    public:\n        PacketActorPosition(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORPOSITION_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorSpeech.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorSpeech.hpp\"\n\nusing namespace mwmp;\n\nPacketActorSpeech::PacketActorSpeech(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_SPEECH;\n}\n\nvoid PacketActorSpeech::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.sound, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorSpeech.hpp",
    "content": "#ifndef OPENMW_PACKETACTORSPEECH_HPP\n#define OPENMW_PACKETACTORSPEECH_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorSpeech : public ActorPacket\n    {\n    public:\n        PacketActorSpeech(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORSPEECH_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorSpellsActive.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorSpellsActive.hpp\"\n\nusing namespace mwmp;\n\nPacketActorSpellsActive::PacketActorSpellsActive(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_SPELLS_ACTIVE;\n}\n\nvoid PacketActorSpellsActive::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.spellsActiveChanges.action, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(actor.spellsActiveChanges.activeSpells.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        actor.spellsActiveChanges.activeSpells.clear();\n        actor.spellsActiveChanges.activeSpells.resize(count);\n    }\n\n    for (auto&& activeSpell : actor.spellsActiveChanges.activeSpells)\n    {\n        RW(activeSpell.id, send, true);\n        RW(activeSpell.isStackingSpell, send);\n        RW(activeSpell.timestampDay, send);\n        RW(activeSpell.timestampHour, send);\n        RW(activeSpell.params.mDisplayName, send, true);\n\n        RW(activeSpell.caster.isPlayer, send);\n\n        if (activeSpell.caster.isPlayer)\n        {\n            RW(activeSpell.caster.guid, send);\n        }\n        else\n        {\n            RW(activeSpell.caster.refId, send, true);\n            RW(activeSpell.caster.refNum, send);\n            RW(activeSpell.caster.mpNum, send);\n        }\n\n        uint32_t effectCount;\n\n        if (send)\n            effectCount = static_cast<uint32_t>(activeSpell.params.mEffects.size());\n\n        RW(effectCount, send);\n\n        if (effectCount > maxEffects)\n        {\n            return;\n        }\n\n        if (!send)\n        {\n            activeSpell.params.mEffects.clear();\n            activeSpell.params.mEffects.resize(effectCount);\n        }\n\n        for (auto&& effect : activeSpell.params.mEffects)\n        {\n            RW(effect.mEffectId, send);\n            RW(effect.mArg, send);\n            RW(effect.mMagnitude, send);\n            RW(effect.mDuration, send);\n            RW(effect.mTimeLeft, send);\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorSpellsActive.hpp",
    "content": "#ifndef OPENMW_PACKETACTORSPELLSACTIVE_HPP\n#define OPENMW_PACKETACTORSPELLSACTIVE_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorSpellsActive : public ActorPacket\n    {\n    public:\n        PacketActorSpellsActive(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n\n    protected:\n        static const int maxEffects = 20;\n    };\n}\n\n#endif //OPENMW_PACKETACTORSPELLSACTIVE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorStatsDynamic.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/esm/creaturestats.hpp>\n#include \"PacketActorStatsDynamic.hpp\"\n\nusing namespace mwmp;\n\nPacketActorStatsDynamic::PacketActorStatsDynamic(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_STATS_DYNAMIC;\n}\n\nvoid PacketActorStatsDynamic::Actor(BaseActor &actor, bool send)\n{\n    RW(actor.creatureStats.mDynamic, send);\n\n    actor.hasStatsDynamicData = true;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorStatsDynamic.hpp",
    "content": "#ifndef OPENMW_PACKETACTORSTATSDYNAMIC_HPP\n#define OPENMW_PACKETACTORSTATSDYNAMIC_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorStatsDynamic : public ActorPacket\n    {\n    public:\n        PacketActorStatsDynamic(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORSTATSDYNAMIC_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorTest.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketActorTest.hpp\"\n\nusing namespace mwmp;\n\nPacketActorTest::PacketActorTest(RakNet::RakPeerInterface *peer) : ActorPacket(peer)\n{\n    packetID = ID_ACTOR_TEST;\n}\n\nvoid PacketActorTest::Actor(BaseActor &actor, bool send)\n{\n\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Actor/PacketActorTest.hpp",
    "content": "#ifndef OPENMW_PACKETACTORTEST_HPP\n#define OPENMW_PACKETACTORTEST_HPP\n\n#include <components/openmw-mp/Packets/Actor/ActorPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketActorTest : public ActorPacket\n    {\n    public:\n        PacketActorTest(RakNet::RakPeerInterface *peer);\n\n        virtual void Actor(BaseActor &actor, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETACTORTEST_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/BasePacket.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <PacketPriority.h>\n#include <RakPeer.h>\n#include \"BasePacket.hpp\"\n\nusing namespace mwmp;\n\nBasePacket::BasePacket(RakNet::RakPeerInterface *peer)\n{\n    packetID = 0;\n    priority = HIGH_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n    orderChannel = CHANNEL_SYSTEM;\n    this->peer = peer;\n}\n\nvoid BasePacket::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    bs = newBitstream;\n    packetValid = true;\n\n    if (send)\n    {\n        bs->Write(packetID);\n        bs->Write(guid);\n    }\n}\n\nvoid BasePacket::SetReadStream(RakNet::BitStream *bitStream)\n{\n    bsRead = bitStream;\n}\n\nvoid BasePacket::SetSendStream(RakNet::BitStream *bitStream)\n{\n    bsSend = bitStream;\n}\n\nvoid BasePacket::SetStreams(RakNet::BitStream *inStream, RakNet::BitStream *outStream)\n{\n    if (inStream != nullptr)\n        bsRead = inStream;\n    if (outStream != nullptr)\n        bsSend = outStream;\n}\n\nuint32_t BasePacket::RequestData(RakNet::RakNetGUID targetGuid)\n{\n    bsSend->ResetWritePointer();\n    bsSend->Write(packetID);\n    bsSend->Write(targetGuid);\n    return peer->Send(bsSend, HIGH_PRIORITY, RELIABLE_ORDERED, orderChannel, targetGuid, false);\n}\n\nuint32_t BasePacket::Send(RakNet::AddressOrGUID destination)\n{\n    bsSend->ResetWritePointer();\n    Packet(bsSend, true);\n    return peer->Send(bsSend, priority, reliability, orderChannel, destination, false);\n}\n\nuint32_t BasePacket::Send(bool toOther)\n{\n    bsSend->ResetWritePointer();\n    Packet(bsSend, true);\n    return peer->Send(bsSend, priority, reliability, orderChannel, guid, toOther);\n}\n\nvoid BasePacket::Read()\n{\n    Packet(bsRead, false);\n}\n\nvoid BasePacket::setGUID(RakNet::RakNetGUID newGuid)\n{\n    guid = newGuid;\n}\n\nRakNet::RakNetGUID BasePacket::getGUID()\n{\n    return guid;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/BasePacket.hpp",
    "content": "#ifndef OPENMW_BASEPACKET_HPP\n#define OPENMW_BASEPACKET_HPP\n\n#include <string>\n#include <RakNetTypes.h>\n#include <BitStream.h>\n#include <PacketPriority.h>\n\n\nnamespace mwmp\n{\n    class BasePacket\n    {\n    public:\n        explicit BasePacket(RakNet::RakPeerInterface *peer);\n\n        virtual ~BasePacket() = default;\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n        virtual uint32_t Send(bool toOtherPlayers = true);\n        virtual uint32_t Send(RakNet::AddressOrGUID destination);\n        virtual void Read();\n\n        void setGUID(RakNet::RakNetGUID newGuid);\n        RakNet::RakNetGUID getGUID();\n\n        void SetReadStream(RakNet::BitStream *bitStream);\n        void SetSendStream(RakNet::BitStream *bitStream);\n        void SetStreams(RakNet::BitStream *inStream, RakNet::BitStream *outStream);\n        virtual uint32_t RequestData(RakNet::RakNetGUID targetGuid);\n\n        static inline uint32_t headerSize()\n        {\n            return static_cast<uint32_t>(1 + RakNet::RakNetGUID::size()); // packetID + RakNetGUID (uint64_t)\n        }\n\n        uint8_t GetPacketID() const\n        {\n            return packetID;\n        }\n\n        bool isPacketValid() const\n        {\n            return packetValid;\n        }\n\n    protected:\n        template<class templateType>\n        bool RW(templateType &data, uint32_t size, bool write)\n        {\n            if (write)\n                bs->Write(data, size);\n            else\n                return bs->Read(data, size);\n            return true;\n        }\n\n        template<class templateType>\n        bool RW(templateType &data, bool write, bool compress = 0)\n        {\n            if (write)\n            {\n                if (compress)\n                    bs->WriteCompressed(data);\n                else\n                    bs->Write(data);\n                return true;\n            }\n            else\n            {\n                if (compress)\n                    return bs->ReadCompressed(data);\n                else\n                    return bs->Read(data);\n            }\n        }\n\n        bool RW(bool &data, bool write)\n        {\n            if (write)\n                bs->Write(data);\n            else\n                return bs->Read(data);\n            return true;\n        }\n\n        const static uint32_t maxStrSize = 64 * 1024; // 64 KiB\n\n        bool RW(std::string &str, bool write, bool compress = false, std::string::size_type maxSize = maxStrSize)\n        {\n            bool res = true;\n            if (write)\n            {\n                if (compress)\n                    RakNet::RakString::SerializeCompressed(str.substr(0, maxSize).c_str(), bs); // todo: remove extra copy of string\n                else\n                {\n                    RakNet::RakString rstr;\n                    rstr.AppendBytes(str.c_str(), str.size() > maxSize ? maxSize : str.size());\n                    bs->Write(rstr);\n                }\n            }\n            else\n            {\n                RakNet::RakString rstr;\n                if (compress)\n                    res = rstr.DeserializeCompressed(bs);\n                else\n                    res = bs->Read(rstr);\n\n                if (res)\n                {\n                    rstr.Truncate(rstr.GetLength() > maxSize ? maxSize : rstr.GetLength());\n                    str = rstr.C_String();\n                }\n                else\n                    str = std::string();\n            }\n            return res;\n        }\n\n    protected:\n        uint8_t packetID;\n        PacketReliability reliability;\n        PacketPriority priority;\n        int8_t orderChannel;\n        RakNet::BitStream *bsRead, *bsSend, *bs;\n        RakNet::RakPeerInterface *peer;\n        RakNet::RakNetGUID guid;\n        bool packetValid;\n    };\n}\n\n#endif //OPENMW_BASEPACKET_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/ObjectPacket.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <PacketPriority.h>\n#include <RakPeer.h>\n#include \"ObjectPacket.hpp\"\n\nusing namespace mwmp;\n\nObjectPacket::ObjectPacket(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    hasCellData = false;\n    packetID = 0;\n    priority = HIGH_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n    orderChannel = CHANNEL_OBJECT;\n    this->peer = peer;\n}\n\nObjectPacket::~ObjectPacket()\n{\n\n}\n\nvoid ObjectPacket::setObjectList(BaseObjectList *newObjectList)\n{\n    objectList = newObjectList;\n    guid = objectList->guid;\n}\n\nvoid ObjectPacket::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    if (!PacketHeader(newBitstream, send))\n        return;\n\n    BaseObject baseObject;\n    for (unsigned int i = 0; i < objectList->baseObjectCount; i++)\n    {\n        if (send)\n            baseObject = objectList->baseObjects.at(i);\n\n        Object(baseObject, send);\n\n        if (!send)\n            objectList->baseObjects.push_back(baseObject);\n    }\n}\n\nbool ObjectPacket::PacketHeader(RakNet::BitStream *newBitstream, bool send)\n{\n    BasePacket::Packet(newBitstream, send);\n\n    RW(objectList->packetOrigin, send);\n\n    if (objectList->packetOrigin == mwmp::CLIENT_SCRIPT_LOCAL || objectList->packetOrigin == mwmp::CLIENT_SCRIPT_GLOBAL)\n        RW(objectList->originClientScript, send, true);\n\n    if (send)\n        objectList->baseObjectCount = (unsigned int)(objectList->baseObjects.size());\n    else\n        objectList->baseObjects.clear();\n\n    RW(objectList->baseObjectCount, send);\n\n    if (objectList->baseObjectCount > maxObjects)\n    {\n        objectList->isValid = false;\n        return false;\n    }\n\n    if (hasCellData)\n    {\n        RW(objectList->cell.mData, send, true);\n        RW(objectList->cell.mName, send, true);\n    }\n\n    return true;\n}\n\nvoid ObjectPacket::Object(BaseObject &baseObject, bool send)\n{\n    RW(baseObject.refId, send, true);\n    RW(baseObject.refNum, send);\n    RW(baseObject.mpNum, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/ObjectPacket.hpp",
    "content": "#ifndef OPENMW_OBJECTPACKET_HPP\n#define OPENMW_OBJECTPACKET_HPP\n\n#include <string>\n#include <RakNetTypes.h>\n#include <BitStream.h>\n#include <PacketPriority.h>\n#include <components/openmw-mp/Base/BaseObject.hpp>\n\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n\n\nnamespace mwmp\n{\n    class ObjectPacket : public BasePacket\n    {\n    public:\n        ObjectPacket(RakNet::RakPeerInterface *peer);\n\n        ~ObjectPacket();\n\n        void setObjectList(BaseObjectList *newObjectList);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n\n    protected:\n        virtual void Object(BaseObject &baseObject, bool send);\n        bool PacketHeader(RakNet::BitStream *newBitstream, bool send);\n        BaseObjectList *objectList;\n        static const int maxObjects = 3000;\n        bool hasCellData;\n    };\n}\n\n#endif //OPENMW_OBJECTPACKET_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketClientScriptLocal.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketClientScriptLocal.hpp\"\n\nusing namespace mwmp;\n\nPacketClientScriptLocal::PacketClientScriptLocal(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_CLIENT_SCRIPT_LOCAL;\n    hasCellData = true;\n}\n\nvoid PacketClientScriptLocal::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n\n    uint32_t clientLocalsCount;\n\n    if (send)\n        clientLocalsCount = static_cast<uint32_t>(baseObject.clientLocals.size());\n\n    RW(clientLocalsCount, send);\n\n    if (!send)\n    {\n        baseObject.clientLocals.clear();\n        baseObject.clientLocals.resize(clientLocalsCount);\n    }\n\n    for (auto&& clientLocal : baseObject.clientLocals)\n    {\n        RW(clientLocal.internalIndex, send);\n        RW(clientLocal.variableType, send);\n\n        if (clientLocal.variableType == mwmp::VARIABLE_TYPE::SHORT || clientLocal.variableType == mwmp::VARIABLE_TYPE::LONG)\n            RW(clientLocal.intValue, send);\n        else if (clientLocal.variableType == mwmp::VARIABLE_TYPE::FLOAT)\n            RW(clientLocal.floatValue, send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketClientScriptLocal.hpp",
    "content": "#ifndef OPENMW_PACKETCLIENTSCRIPTLOCAL_HPP\n#define OPENMW_PACKETCLIENTSCRIPTLOCAL_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketClientScriptLocal : public ObjectPacket\n    {\n    public:\n        PacketClientScriptLocal(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &obj, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETCLIENTSCRIPTLOCAL_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketConsoleCommand.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketConsoleCommand.hpp\"\n\nusing namespace mwmp;\n\nPacketConsoleCommand::PacketConsoleCommand(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_CONSOLE_COMMAND;\n    hasCellData = true;\n}\n\nvoid PacketConsoleCommand::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    if (!PacketHeader(newBitstream, send))\n        return;\n\n    RW(objectList->consoleCommand, send, true);\n\n    BaseObject baseObject;\n    for (unsigned int i = 0; i < objectList->baseObjectCount; i++)\n    {\n        if (send)\n            baseObject = objectList->baseObjects.at(i);\n\n        RW(baseObject.isPlayer, send);\n\n        if (baseObject.isPlayer)\n            RW(baseObject.guid, send);\n        else\n            Object(baseObject, send);\n\n        if (!send)\n            objectList->baseObjects.push_back(baseObject);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketConsoleCommand.hpp",
    "content": "#ifndef OPENMW_PACKETCONSOLECOMMAND_HPP\n#define OPENMW_PACKETCONSOLECOMMAND_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketConsoleCommand : public ObjectPacket\n    {\n    public:\n        PacketConsoleCommand(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETCONSOLECOMMAND_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketContainer.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketContainer.hpp\"\n\nusing namespace mwmp;\n\nPacketContainer::PacketContainer(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_CONTAINER;\n    hasCellData = true;\n}\n\nvoid PacketContainer::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    if (!PacketHeader(newBitstream, send))\n        return;\n\n    RW(objectList->action, send);\n    RW(objectList->containerSubAction, send);\n\n    BaseObject baseObject;\n    for (unsigned int i = 0; i < objectList->baseObjectCount; i++)\n    {\n        if (send)\n        {\n            baseObject = objectList->baseObjects.at(i);\n            baseObject.containerItemCount = (unsigned int) (baseObject.containerItems.size());\n        }\n        else\n            baseObject.containerItems.clear();\n\n        Object(baseObject, send);\n\n        RW(baseObject.containerItemCount, send);\n\n        if (baseObject.containerItemCount > maxObjects || baseObject.refId.empty() || (baseObject.refNum != 0 && baseObject.mpNum != 0))\n        {\n            objectList->isValid = false;\n            return;\n        }\n\n        ContainerItem containerItem;\n\n        for (unsigned int j = 0; j < baseObject.containerItemCount; j++)\n        {\n            if (send)\n                containerItem = baseObject.containerItems.at(j);\n\n            RW(containerItem.refId, send, true);\n            RW(containerItem.count, send);\n            RW(containerItem.charge, send);\n            RW(containerItem.enchantmentCharge, send);\n            RW(containerItem.soul, send, true);\n            RW(containerItem.actionCount, send);\n\n            if (!send)\n                baseObject.containerItems.push_back(containerItem);\n        }\n        if (!send)\n            objectList->baseObjects.push_back(baseObject);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketContainer.hpp",
    "content": "#ifndef OPENMW_PACKETCONTAINER_HPP\n#define OPENMW_PACKETCONTAINER_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketContainer : public ObjectPacket\n    {\n    public:\n        PacketContainer(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETCONTAINER_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketDoorDestination.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketDoorDestination.hpp\"\n\nusing namespace mwmp;\n\nPacketDoorDestination::PacketDoorDestination(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_DOOR_DESTINATION;\n    hasCellData = true;\n}\n\nvoid PacketDoorDestination::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n\n    RW(baseObject.teleportState, send);\n\n    if (baseObject.teleportState)\n    {\n        RW(baseObject.destinationCell.mData, send, true);\n        RW(baseObject.destinationCell.mName, send, true);\n\n        RW(baseObject.destinationPosition.pos, send, true);\n        RW(baseObject.destinationPosition.rot[0], send, true);\n        RW(baseObject.destinationPosition.rot[2], send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketDoorDestination.hpp",
    "content": "#ifndef OPENMW_PACKETDOORDESTINATION_HPP\n#define OPENMW_PACKETDOORDESTINATION_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketDoorDestination : public ObjectPacket\n    {\n    public:\n        PacketDoorDestination(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETDOORDESTINATION_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketDoorState.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketDoorState.hpp\"\n\nusing namespace mwmp;\n\nPacketDoorState::PacketDoorState(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_DOOR_STATE;\n    hasCellData = true;\n}\n\nvoid PacketDoorState::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.doorState, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketDoorState.hpp",
    "content": "#ifndef OPENMW_PACKETDOORSTATE_HPP\n#define OPENMW_PACKETDOORSTATE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketDoorState : public ObjectPacket\n    {\n    public:\n        PacketDoorState(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETDOORSTATE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketMusicPlay.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketMusicPlay.hpp\"\n\nusing namespace mwmp;\n\nPacketMusicPlay::PacketMusicPlay(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_MUSIC_PLAY;\n}\n\nvoid PacketMusicPlay::Object(BaseObject &baseObject, bool send)\n{\n    RW(baseObject.musicFilename, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketMusicPlay.hpp",
    "content": "#ifndef OPENMW_PACKETMUSICPLAY_HPP\n#define OPENMW_PACKETMUSICPLAY_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketMusicPlay : public ObjectPacket\n    {\n    public:\n        PacketMusicPlay(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETMUSICPLAY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectActivate.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectActivate.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectActivate::PacketObjectActivate(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_ACTIVATE;\n    hasCellData = true;\n}\n\nvoid PacketObjectActivate::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    if (!PacketHeader(newBitstream, send))\n        return;\n\n    BaseObject baseObject;\n    for (unsigned int i = 0; i < objectList->baseObjectCount; i++)\n    {\n        if (send)\n            baseObject = objectList->baseObjects.at(i);\n\n        RW(baseObject.isPlayer, send);\n\n        if (baseObject.isPlayer)\n            RW(baseObject.guid, send);\n        else\n            Object(baseObject, send);\n\n        RW(baseObject.activatingActor.isPlayer, send);\n\n        if (baseObject.activatingActor.isPlayer)\n        {\n            RW(baseObject.activatingActor.guid, send);\n        }\n        else\n        {\n            RW(baseObject.activatingActor.refId, send, true);\n            RW(baseObject.activatingActor.refNum, send);\n            RW(baseObject.activatingActor.mpNum, send);\n\n            RW(baseObject.activatingActor.name, send);\n        }\n\n        if (!send)\n            objectList->baseObjects.push_back(baseObject);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectActivate.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTACTIVATE_HPP\n#define OPENMW_PACKETOBJECTACTIVATE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectActivate : public ObjectPacket\n    {\n    public:\n        PacketObjectActivate(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTACTIVATE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectAnimPlay.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectAnimPlay.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectAnimPlay::PacketObjectAnimPlay(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_ANIM_PLAY;\n    hasCellData = true;\n}\n\nvoid PacketObjectAnimPlay::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.animGroup, send);\n    RW(baseObject.animMode, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectAnimPlay.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTANIMPLAY_HPP\n#define OPENMW_PACKETOBJECTANIMPLAY_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectAnimPlay : public ObjectPacket\n    {\n    public:\n        PacketObjectAnimPlay(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTANIMPLAY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectAttach.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectAttach.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectAttach::PacketObjectAttach(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_ATTACH;\n    hasCellData = true;\n}\n\nvoid PacketObjectAttach::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    // Placeholder\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectAttach.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTATTACH_HPP\n#define OPENMW_PACKETOBJECTATTACH_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectAttach : public ObjectPacket\n    {\n    public:\n        PacketObjectAttach(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTATTACH_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectDelete.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectDelete.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectDelete::PacketObjectDelete(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_DELETE;\n    hasCellData = true;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectDelete.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTDELETE_HPP\n#define OPENMW_PACKETOBJECTDELETE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectDelete : public ObjectPacket\n    {\n    public:\n        PacketObjectDelete(RakNet::RakPeerInterface *peer);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTDELETE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectDialogueChoice.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectDialogueChoice.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectDialogueChoice::PacketObjectDialogueChoice(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_DIALOGUE_CHOICE;\n    hasCellData = true;\n}\n\nvoid PacketObjectDialogueChoice::Object(BaseObject& baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.dialogueChoiceType, send);\n\n    if (baseObject.dialogueChoiceType == DialogueChoiceType::TOPIC)\n        RW(baseObject.topicId, send, true);\n\n    RW(baseObject.guiId, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectDialogueChoice.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTDIALOGUECHOICE_HPP\n#define OPENMW_PACKETOBJECTDIALOGUECHOICE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectDialogueChoice : public ObjectPacket\n    {\n    public:\n        PacketObjectDialogueChoice(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject& baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTDIALOGUECHOICE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectHit.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectHit.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectHit::PacketObjectHit(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_HIT;\n    hasCellData = true;\n}\n\nvoid PacketObjectHit::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    if (!PacketHeader(newBitstream, send))\n        return;\n\n    BaseObject baseObject;\n    for (unsigned int i = 0; i < objectList->baseObjectCount; i++)\n    {\n        if (send)\n            baseObject = objectList->baseObjects.at(i);\n\n        RW(baseObject.isPlayer, send);\n\n        if (baseObject.isPlayer)\n            RW(baseObject.guid, send);\n        else\n            Object(baseObject, send);\n\n        RW(baseObject.hittingActor.isPlayer, send);\n\n        if (baseObject.hittingActor.isPlayer)\n        {\n            RW(baseObject.hittingActor.guid, send);\n        }\n        else\n        {\n            RW(baseObject.hittingActor.refId, send, true);\n            RW(baseObject.hittingActor.refNum, send);\n            RW(baseObject.hittingActor.mpNum, send);\n\n            RW(baseObject.hittingActor.name, send);\n        }\n\n        RW(baseObject.hitAttack.success, send);\n\n        if (baseObject.hitAttack.success)\n        {\n            RW(baseObject.hitAttack.damage, send);\n            RW(baseObject.hitAttack.block, send);\n            RW(baseObject.hitAttack.knockdown, send);\n        }\n\n        if (!send)\n            objectList->baseObjects.push_back(baseObject);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectHit.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTHIT_HPP\n#define OPENMW_PACKETOBJECTHIT_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectHit : public ObjectPacket\n    {\n    public:\n        PacketObjectHit(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTHIT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectLock.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectLock.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectLock::PacketObjectLock(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_LOCK;\n    hasCellData = true;\n}\n\nvoid PacketObjectLock::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.lockLevel, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectLock.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTLOCK_HPP\n#define OPENMW_PACKETOBJECTLOCK_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectLock : public ObjectPacket\n    {\n    public:\n        PacketObjectLock(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTLOCK_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectMiscellaneous.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectMiscellaneous.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectMiscellaneous::PacketObjectMiscellaneous(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_MISCELLANEOUS;\n    hasCellData = true;\n}\n\nvoid PacketObjectMiscellaneous::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.goldPool, send);\n    RW(baseObject.lastGoldRestockHour, send);\n    RW(baseObject.lastGoldRestockDay, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectMiscellaneous.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTMISCELLANEOUS_HPP\n#define OPENMW_PACKETOBJECTMISCELLANEOUS_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectMiscellaneous : public ObjectPacket\n    {\n    public:\n        PacketObjectMiscellaneous(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &obj, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTMISCELLANEOUS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectMove.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectMove.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectMove::PacketObjectMove(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_MOVE;\n    hasCellData = true;\n}\n\nvoid PacketObjectMove::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.position.pos, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectMove.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTMOVE_HPP\n#define OPENMW_PACKETOBJECTMOVE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectMove : public ObjectPacket\n    {\n    public:\n        PacketObjectMove(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTMOVE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectPlace.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectPlace.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectPlace::PacketObjectPlace(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_PLACE;\n    hasCellData = true;\n}\n\nvoid PacketObjectPlace::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.count, send);\n    RW(baseObject.charge, send);\n    RW(baseObject.enchantmentCharge, send);\n    RW(baseObject.soul, send, true);\n    RW(baseObject.goldValue, send);\n    RW(baseObject.position, send);\n    RW(baseObject.droppedByPlayer, send);\n    RW(baseObject.hasContainer, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectPlace.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTPLACE_HPP\n#define OPENMW_PACKETOBJECTPLACE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectPlace : public ObjectPacket\n    {\n    public:\n        PacketObjectPlace(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTPLACE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectRestock.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectRestock.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectRestock::PacketObjectRestock(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_RESTOCK;\n    hasCellData = true;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectRestock.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTRESTOCK_HPP\n#define OPENMW_PACKETOBJECTRESTOCK_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectRestock : public ObjectPacket\n    {\n    public:\n        PacketObjectRestock(RakNet::RakPeerInterface *peer);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTRESTOCK_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectRotate.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectRotate.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectRotate::PacketObjectRotate(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_ROTATE;\n    hasCellData = true;\n}\n\nvoid PacketObjectRotate::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.position.rot[0], send);\n    RW(baseObject.position.rot[1], send);\n    RW(baseObject.position.rot[2], send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectRotate.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTROTATE_HPP\n#define OPENMW_PACKETOBJECTROTATE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectRotate : public ObjectPacket\n    {\n    public:\n        PacketObjectRotate(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTROTATE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectScale.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectScale.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectScale::PacketObjectScale(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_SCALE;\n    hasCellData = true;\n}\n\nvoid PacketObjectScale::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.scale, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectScale.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTSCALE_HPP\n#define OPENMW_PACKETOBJECTSCALE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectScale : public ObjectPacket\n    {\n    public:\n        PacketObjectScale(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTSCALE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectSound.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectSound.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectSound::PacketObjectSound(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_SOUND;\n    hasCellData = true;\n}\n\nvoid PacketObjectSound::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    if (!PacketHeader(newBitstream, send))\n        return;\n\n    BaseObject baseObject;\n    for (unsigned int i = 0; i < objectList->baseObjectCount; i++)\n    {\n        if (send)\n            baseObject = objectList->baseObjects.at(i);\n\n        RW(baseObject.isPlayer, send);\n\n        if (baseObject.isPlayer)\n            RW(baseObject.guid, send);\n        else\n            Object(baseObject, send);\n\n        RW(baseObject.soundId, send, true);\n        RW(baseObject.volume, send);\n        RW(baseObject.pitch, send);\n\n        if (!send)\n            objectList->baseObjects.push_back(baseObject);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectSound.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTSOUND_HPP\n#define OPENMW_PACKETOBJECTSOUND_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectSound : public ObjectPacket\n    {\n    public:\n        PacketObjectSound(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTSOUND_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectSpawn.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectSpawn.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectSpawn::PacketObjectSpawn(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_SPAWN;\n    hasCellData = true;\n}\n\nvoid PacketObjectSpawn::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.position, send);\n\n    RW(baseObject.isSummon, send);\n\n    if (baseObject.isSummon)\n    {\n        RW(baseObject.summonEffectId, send);\n        RW(baseObject.summonSpellId, send, true);\n        RW(baseObject.summonDuration, send);\n\n        RW(baseObject.master.isPlayer, send);\n\n        if (baseObject.master.isPlayer)\n        {\n            RW(baseObject.master.guid, send);\n        }\n        else\n        {\n            RW(baseObject.master.refId, send, true);\n            RW(baseObject.master.refNum, send);\n            RW(baseObject.master.mpNum, send);\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectSpawn.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTSPAWN_HPP\n#define OPENMW_PACKETOBJECTSPAWN_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectSpawn : public ObjectPacket\n    {\n    public:\n        PacketObjectSpawn(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTSPAWN_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectState.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectState.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectState::PacketObjectState(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_STATE;\n    hasCellData = true;\n}\n\nvoid PacketObjectState::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.objectState, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectState.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTSTATE_HPP\n#define OPENMW_PACKETOBJECTSTATE_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectState : public ObjectPacket\n    {\n    public:\n        PacketObjectState(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTSTATE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectTrap.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketObjectTrap.hpp\"\n\nusing namespace mwmp;\n\nPacketObjectTrap::PacketObjectTrap(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_OBJECT_TRAP;\n    hasCellData = true;\n}\n\nvoid PacketObjectTrap::Object(BaseObject &baseObject, bool send)\n{\n    ObjectPacket::Object(baseObject, send);\n    RW(baseObject.isDisarmed, send);\n\n    if (!baseObject.isDisarmed)\n        RW(baseObject.position, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketObjectTrap.hpp",
    "content": "#ifndef OPENMW_PACKETOBJECTTRAP_HPP\n#define OPENMW_PACKETOBJECTTRAP_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketObjectTrap : public ObjectPacket\n    {\n    public:\n        PacketObjectTrap(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &baseObject, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETOBJECTTRAP_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketScriptMemberShort.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketScriptMemberShort.hpp\"\n\nusing namespace mwmp;\n\nPacketScriptMemberShort::PacketScriptMemberShort(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_SCRIPT_MEMBER_SHORT;\n}\n\nvoid PacketScriptMemberShort::Object(BaseObject &baseObject, bool send)\n{\n    //RW(baseObject.refId, send);\n    //RW(baseObject.index, send);\n    //RW(baseObject.shortVal, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketScriptMemberShort.hpp",
    "content": "#ifndef OPENMW_PACKETSCRIPTMEMBERSHORT_HPP\n#define OPENMW_PACKETSCRIPTMEMBERSHORT_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketScriptMemberShort : public ObjectPacket\n    {\n    public:\n        PacketScriptMemberShort(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &obj, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETSCRIPTMEMBERSHORT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketVideoPlay.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketVideoPlay.hpp\"\n\nusing namespace mwmp;\n\nPacketVideoPlay::PacketVideoPlay(RakNet::RakPeerInterface *peer) : ObjectPacket(peer)\n{\n    packetID = ID_VIDEO_PLAY;\n}\n\nvoid PacketVideoPlay::Object(BaseObject &baseObject, bool send)\n{\n    RW(baseObject.videoFilename, send, true);\n    RW(baseObject.allowSkipping, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Object/PacketVideoPlay.hpp",
    "content": "#ifndef OPENMW_PACKETVIDEOPLAY_HPP\n#define OPENMW_PACKETVIDEOPLAY_HPP\n\n#include <components/openmw-mp/Packets/Object/ObjectPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketVideoPlay : public ObjectPacket\n    {\n    public:\n        PacketVideoPlay(RakNet::RakPeerInterface *peer);\n\n        virtual void Object(BaseObject &obj, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETVIDEOPLAY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/PacketPreInit.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketPreInit.hpp\"\n\nmwmp::PacketPreInit::PacketPreInit(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    packetID = ID_GAME_PREINIT;\n}\n\nvoid mwmp::PacketPreInit::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    BasePacket::Packet(newBitstream, send);\n\n    const RakNet::BitSize_t packetSize = bs->GetNumberOfBytesUsed();\n    uint32_t expectedPacketSize = BasePacket::headerSize() + sizeof(uint32_t);\n    if (!send && expectedPacketSize > packetSize)\n    {\n        LOG_MESSAGE(TimedLog::LOG_ERROR, \"Wrong packet size %d when expected %d\", packetSize, expectedPacketSize);\n        packetValid = false;\n        return;\n    }\n\n    uint32_t numberOfChecksums = checksums->size();\n    RW(numberOfChecksums, send);\n\n    if (numberOfChecksums > maxPlugins)\n    {\n        LOG_MESSAGE(TimedLog::LOG_ERROR, \"Wrong number of checksums %d when maximum is %d\", numberOfChecksums, maxPlugins);\n        packetValid = false;\n        return;\n    }\n\n    struct NAS\n    {\n        uint32_t hashN;\n        uint32_t strSize;\n    };\n\n    std::vector<NAS> NumberOfHashesAndStrSizes(numberOfChecksums);\n\n    PluginContainer::const_iterator checksumIt = checksums->begin();\n\n    for (auto &&nas : NumberOfHashesAndStrSizes)\n    {\n        if (send)\n        {\n            nas.strSize = checksumIt->first.size();\n            nas.hashN = checksumIt++->second.size();\n        }\n        RW(nas, send);\n\n        expectedPacketSize += nas.strSize + nas.hashN;\n\n        if (nas.strSize > pluginNameMaxLength)\n            LOG_MESSAGE(TimedLog::LOG_ERROR, \"Wrong string length %d when maximum length is %d\",\n                        nas.strSize,\n                        pluginNameMaxLength);\n        else if (nas.hashN > maxHashes)\n            LOG_MESSAGE(TimedLog::LOG_ERROR, \"Wrong  number of hashes %d when maximum is %d\", nas.hashN, maxHashes);\n        else\n            continue;\n        packetValid = false;\n        return;\n    }\n\n    if (!send && expectedPacketSize == packetSize) // server accepted plugin list via sending \"empty\" packet\n        return;\n\n    if (!send && expectedPacketSize > packetSize)\n    {\n        LOG_MESSAGE(TimedLog::LOG_ERROR, \"Wrong packet size %d when expected %d\", packetSize, expectedPacketSize);\n        packetValid = false;\n        return;\n    }\n\n    checksums->resize(numberOfChecksums);\n\n    auto numberOfHashesIt = NumberOfHashesAndStrSizes.cbegin();\n\n    for (auto &&checksum : *checksums)\n    {\n        RW(checksum.first, send, false, numberOfHashesIt->strSize);\n\n        checksum.second.resize(numberOfHashesIt->hashN);\n        for (auto &&hash : checksum.second)\n            RW(hash, send);\n        ++numberOfHashesIt;\n    }\n}\n\nvoid mwmp::PacketPreInit::setChecksums(mwmp::PacketPreInit::PluginContainer *newChecksums)\n{\n    checksums = newChecksums;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/PacketPreInit.hpp",
    "content": "#ifndef OPENMW_PACKETPREINIT_HPP\n#define OPENMW_PACKETPREINIT_HPP\n\n#include <vector>\n#include \"BasePacket.hpp\"\n\n\nnamespace mwmp\n{\n    class PacketPreInit : public BasePacket\n    {\n    public:\n        typedef std::vector<uint32_t> HashList;\n        typedef std::pair<std::string, HashList> PluginPair;\n        typedef std::vector<PluginPair> PluginContainer;\n\n        PacketPreInit(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n        void setChecksums(PluginContainer *checksums);\n    private:\n        PluginContainer *checksums;\n        const static uint32_t maxPlugins = 1000;\n        const static uint32_t pluginNameMaxLength = 256;\n        const static uint32_t maxHashes = 50;\n    };\n}\n\n\n#endif //OPENMW_PACKETPREINIT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketChatMessage.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketChatMessage.hpp\"\n\nmwmp::PacketChatMessage::PacketChatMessage(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_CHAT_MESSAGE;\n    orderChannel = CHANNEL_SYSTEM;\n}\n\nvoid mwmp::PacketChatMessage::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->chatMessage, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketChatMessage.hpp",
    "content": "#ifndef OPENMW_PACKETCHATMESSAGE_HPP\n#define OPENMW_PACKETCHATMESSAGE_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketChatMessage : public PlayerPacket\n    {\n    public:\n        PacketChatMessage(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETCHATMESSAGE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketDisconnect.hpp",
    "content": "#ifndef OPENMW_PACKETDISCONNECT_HPP\n#define OPENMW_PACKETDISCONNECT_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nnamespace mwmp\n{\n    class PacketDisconnect : public PlayerPacket\n    {\n    public:\n        PacketDisconnect(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n        {\n            packetID = ID_USER_DISCONNECTED;\n            orderChannel = CHANNEL_SYSTEM;\n        }\n    };\n}\n\n#endif //OPENMW_PACKETDISCONNECT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketGUIBoxes.cpp",
    "content": "#include \"PacketGUIBoxes.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketGUIBoxes::PacketGUIBoxes(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_GUI_MESSAGEBOX;\n    orderChannel = CHANNEL_SYSTEM;\n}\n\nvoid PacketGUIBoxes::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->guiMessageBox.id, send);\n    RW(player->guiMessageBox.type, send);\n    RW(player->guiMessageBox.label, send);\n\n    RW(player->guiMessageBox.data, send, true);\n\n    if (player->guiMessageBox.type == BasePlayer::GUIMessageBox::CustomMessageBox)\n        RW(player->guiMessageBox.buttons, send);\n    else if (player->guiMessageBox.type == BasePlayer::GUIMessageBox::InputDialog ||\n        player->guiMessageBox.type == BasePlayer::GUIMessageBox::PasswordDialog)\n        RW(player->guiMessageBox.note, send);\n}\n\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketGUIBoxes.hpp",
    "content": "#ifndef OPENMW_PACKETGUIBOXES_HPP\n#define OPENMW_PACKETGUIBOXES_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketGUIBoxes : public PlayerPacket\n    {\n    public:\n        PacketGUIBoxes(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETGUIBOXES_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketGameSettings.cpp",
    "content": "#include \"PacketGameSettings.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketGameSettings::PacketGameSettings(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_GAME_SETTINGS;\n    orderChannel = CHANNEL_SYSTEM;\n}\n\nvoid PacketGameSettings::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->difficulty, send);\n    RW(player->consoleAllowed, send);\n    RW(player->bedRestAllowed, send);\n    RW(player->wildernessRestAllowed, send);\n    RW(player->waitAllowed, send);\n    RW(player->enforcedLogLevel, send);\n    RW(player->physicsFramerate, send);\n\n    std::string mapIndex;\n    std::string mapValue;\n\n    uint32_t gameSettingCount = static_cast<uint32_t>(player->gameSettings.size());\n    RW(gameSettingCount, send);\n\n    if (send)\n    {\n        for (auto&& gameSetting : player->gameSettings)\n        {\n            mapIndex = gameSetting.first;\n            mapValue = gameSetting.second;\n            RW(mapIndex, send, false);\n            RW(mapValue, send, false);\n        }\n    }\n    else\n    {\n        player->gameSettings.clear();\n        for (unsigned int n = 0; n < gameSettingCount; n++)\n        {\n            RW(mapIndex, send, false);\n            RW(mapValue, send, false);\n            player->gameSettings[mapIndex] = mapValue;\n        }\n    }\n\n    uint32_t vrSettingCount = static_cast<uint32_t>(player->vrSettings.size());\n    RW(vrSettingCount, send);\n\n    if (send)\n    {\n        for (auto&& vrSetting : player->vrSettings)\n        {\n            mapIndex = vrSetting.first;\n            mapValue = vrSetting.second;\n            RW(mapIndex, send, false);\n            RW(mapValue, send, false);\n        }\n    }\n    else\n    {\n        player->vrSettings.clear();\n        for (unsigned int n = 0; n < vrSettingCount; n++)\n        {\n            RW(mapIndex, send, false);\n            RW(mapValue, send, false);\n            player->vrSettings[mapIndex] = mapValue;\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketGameSettings.hpp",
    "content": "#ifndef OPENMW_PACKETGAMESETTINGS_HPP\n#define OPENMW_PACKETGAMESETTINGS_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nnamespace mwmp\n{\n    class PacketGameSettings: public PlayerPacket\n    {\n    public:\n        PacketGameSettings(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETGAMESETTINGS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketLoaded.hpp",
    "content": "#ifndef OPENMW_PACKETLOADED_HPP\n#define OPENMW_PACKETLOADED_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketLoaded : public PlayerPacket\n    {\n    public:\n        PacketLoaded(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n        {\n            packetID = ID_LOADED;\n            orderChannel = CHANNEL_SYSTEM;\n        }\n    };\n}\n\n#endif //OPENMW_PACKETLOADED_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAlly.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerAlly.hpp\"\n\nmwmp::PacketPlayerAlly::PacketPlayerAlly(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_ALLY;\n}\n\nvoid mwmp::PacketPlayerAlly::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->alliedPlayers.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->alliedPlayers.clear();\n        player->alliedPlayers.resize(count);\n    }\n\n    for (auto &&teamPlayerGuid : player->alliedPlayers)\n    {\n        RW(teamPlayerGuid, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAlly.hpp",
    "content": "#ifndef OPENMW_PACKETALLY_HPP\n#define OPENMW_PACKETALLY_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerAlly : public PlayerPacket\n    {\n    public:\n        PacketPlayerAlly(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETALLY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAnimFlags.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerAnimFlags.hpp\"\n\nmwmp::PacketPlayerAnimFlags::PacketPlayerAnimFlags(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_ANIM_FLAGS;\n}\n\nvoid mwmp::PacketPlayerAnimFlags::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->movementFlags, send);\n    RW(player->drawState, send);\n    RW(player->isJumping, send);\n    RW(player->isFlying, send);\n    RW(player->hasTcl, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAnimFlags.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERANIMFLAGS_HPP\n#define OPENMW_PACKETPLAYERANIMFLAGS_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerAnimFlags : public PlayerPacket\n    {\n    public:\n        PacketPlayerAnimFlags(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERANIMFLAGS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAnimPlay.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerAnimPlay.hpp\"\n\nmwmp::PacketPlayerAnimPlay::PacketPlayerAnimPlay(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_ANIM_PLAY;\n}\n\nvoid mwmp::PacketPlayerAnimPlay::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->animation.groupname, send);\n    RW(player->animation.mode, send);\n    RW(player->animation.count, send);\n    RW(player->animation.persist, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAnimPlay.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERANIMPLAY_HPP\n#define OPENMW_PACKETPLAYERANIMPLAY_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerAnimPlay : public PlayerPacket\n    {\n    public:\n        PacketPlayerAnimPlay(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERANIMPLAY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAttack.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerAttack.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerAttack::PacketPlayerAttack(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_ATTACK;\n}\n\nvoid PacketPlayerAttack::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->attack.target.isPlayer, send);\n\n    if (player->attack.target.isPlayer)\n    {\n        RW(player->attack.target.guid, send);\n    }\n    else\n    {\n        RW(player->attack.target.refId, send, true);\n        RW(player->attack.target.refNum, send);\n        RW(player->attack.target.mpNum, send);\n    }\n\n    RW(player->attack.type, send);\n\n    RW(player->attack.pressed, send);\n    RW(player->attack.success, send);\n\n    RW(player->attack.isHit, send);\n\n    if (player->attack.type == mwmp::Attack::MELEE)\n    {\n        RW(player->attack.attackAnimation, send, true);\n    }\n    else if (player->attack.type == mwmp::Attack::RANGED)\n    {\n        RW(player->attack.attackStrength, send);\n        RW(player->attack.rangedWeaponId, send, true);\n        RW(player->attack.rangedAmmoId, send, true);\n\n        RW(player->attack.projectileOrigin.origin[0], send);\n        RW(player->attack.projectileOrigin.origin[1], send);\n        RW(player->attack.projectileOrigin.origin[2], send);\n        RW(player->attack.projectileOrigin.orientation[0], send);\n        RW(player->attack.projectileOrigin.orientation[1], send);\n        RW(player->attack.projectileOrigin.orientation[2], send);\n        RW(player->attack.projectileOrigin.orientation[3], send);\n    }\n\n    if (player->attack.isHit)\n    {\n        RW(player->attack.damage, send);\n        RW(player->attack.block, send);\n        RW(player->attack.knockdown, send);\n        RW(player->attack.applyWeaponEnchantment, send);\n\n        if (player->attack.type == mwmp::Attack::RANGED)\n            RW(player->attack.applyAmmoEnchantment, send);\n\n        RW(player->attack.hitPosition.pos[0], send);\n        RW(player->attack.hitPosition.pos[1], send);\n        RW(player->attack.hitPosition.pos[2], send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAttack.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERATTACK_HPP\n#define OPENMW_PACKETPLAYERATTACK_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerAttack : public PlayerPacket\n    {\n    public:\n        PacketPlayerAttack(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERATTACK_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAttribute.cpp",
    "content": "#include \"PacketPlayerAttribute.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerAttribute::PacketPlayerAttribute(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_ATTRIBUTE;\n}\n\nvoid PacketPlayerAttribute::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->exchangeFullInfo, send);\n\n    if (player->exchangeFullInfo)\n    {\n        RW(player->creatureStats.mAttributes, send);\n        RW(player->npcStats.mSkillIncrease, send);\n    }\n    else\n    {\n        uint32_t count;\n\n        if (send)\n            count = static_cast<uint32_t>(player->attributeIndexChanges.size());\n\n        RW(count, send);\n\n        if (!send)\n        {\n            player->attributeIndexChanges.clear();\n            player->attributeIndexChanges.resize(count);\n        }\n\n        for (auto &&attributeIndex : player->attributeIndexChanges)\n        {\n            RW(attributeIndex, send);\n\n            if (attributeIndex >= 8)\n            {\n                packetValid = false;\n                return;\n            }\n\n            RW(player->creatureStats.mAttributes[attributeIndex], send);\n            RW(player->npcStats.mSkillIncrease[attributeIndex], send);\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerAttribute.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERATTRIBUTE_HPP\n#define OPENMW_PACKETPLAYERATTRIBUTE_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerAttribute : public PlayerPacket\n    {\n    public:\n        const static int AttributeCount = 8;\n        PacketPlayerAttribute(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERATTRIBUTE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp",
    "content": "#include \"PacketPlayerBaseInfo.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerBaseInfo::PacketPlayerBaseInfo(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_BASEINFO;\n}\n\nvoid PacketPlayerBaseInfo::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->npc.mName, send, true);\n    RW(player->npc.mModel, send, true);\n    RW(player->npc.mRace, send, true);\n    RW(player->npc.mHair, send, true);\n    RW(player->npc.mHead, send, true);\n\n    RW(player->npc.mFlags, send);\n\n    RW(player->birthsign, send, true);\n\n    RW(player->resetStats, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERBASEINFO_HPP\n#define OPENMW_PACKETPLAYERBASEINFO_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerBaseInfo : public PlayerPacket\n    {\n    public:\n        PacketPlayerBaseInfo(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERBASEINFO_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerBehavior.cpp",
    "content": "#include \"PacketPlayerBehavior.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerBehavior::PacketPlayerBehavior(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_BEHAVIOR;\n}\n\nvoid PacketPlayerBehavior::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    // Placeholder\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerBehavior.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERBEHAVIOR_HPP\n#define OPENMW_PACKETPLAYERBEHAVIOR_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerBehavior : public PlayerPacket\n    {\n    public:\n        PacketPlayerBehavior(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERBEHAVIOR_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerBook.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerBook.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerBook::PacketPlayerBook(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_BOOK;\n}\n\nvoid PacketPlayerBook::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->bookChanges.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->bookChanges.clear();\n        player->bookChanges.resize(count);\n    }\n\n    for (auto &&book : player->bookChanges)\n    {\n        RW(book.bookId, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerBook.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERBOOK_HPP\n#define OPENMW_PACKETPLAYERBOOK_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerBook : public PlayerPacket\n    {\n    public:\n        PacketPlayerBook(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERBOOK_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerBounty.cpp",
    "content": "#include \"PacketPlayerBounty.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerBounty::PacketPlayerBounty(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_BOUNTY;\n}\n\nvoid PacketPlayerBounty::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->npcStats.mBounty, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerBounty.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERBOUNTY_HPP\n#define OPENMW_PACKETPLAYERBOUNTY_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerBounty : public PlayerPacket\n    {\n    public:\n        PacketPlayerBounty(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERBOUNTY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCast.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerCast.hpp\"\n\n#include <components/openmw-mp/TimedLog.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerCast::PacketPlayerCast(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_CAST;\n}\n\nvoid PacketPlayerCast::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->cast.target.isPlayer, send);\n\n    if (player->cast.target.isPlayer)\n    {\n        RW(player->cast.target.guid, send);\n    }\n    else\n    {\n        RW(player->cast.target.refId, send, true);\n        RW(player->cast.target.refNum, send);\n        RW(player->cast.target.mpNum, send);\n    }\n\n    RW(player->cast.type, send);\n\n    if (player->cast.type == mwmp::Cast::ITEM)\n        RW(player->cast.itemId, send, true);\n    else\n    {\n        RW(player->cast.pressed, send);\n        RW(player->cast.success, send);\n\n        RW(player->cast.instant, send);\n        RW(player->cast.spellId, send, true);\n    }\n\n    RW(player->cast.hasProjectile, send);\n\n    if (player->cast.hasProjectile)\n    {\n        RW(player->cast.projectileOrigin.origin[0], send);\n        RW(player->cast.projectileOrigin.origin[1], send);\n        RW(player->cast.projectileOrigin.origin[2], send);\n        RW(player->cast.projectileOrigin.orientation[0], send);\n        RW(player->cast.projectileOrigin.orientation[1], send);\n        RW(player->cast.projectileOrigin.orientation[2], send);\n        RW(player->cast.projectileOrigin.orientation[3], send);\n        RW(player->position, send);\n        RW(player->direction, send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCast.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERCAST_HPP\n#define OPENMW_PACKETPLAYERCAST_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerCast : public PlayerPacket\n    {\n    public:\n        PacketPlayerCast(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERCAST_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCellChange.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerCellChange.hpp\"\n\n\nmwmp::PacketPlayerCellChange::PacketPlayerCellChange(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_CELL_CHANGE;\n    priority = IMMEDIATE_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n}\n\nvoid mwmp::PacketPlayerCellChange::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->cell.mData, send, true);\n    RW(player->cell.mName, send, true);\n\n    RW(player->previousCellPosition.pos, send, true);\n\n    RW(player->isChangingRegion, send);\n\n    if (player->isChangingRegion)\n        RW(player->cell.mRegion, send, true);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCellChange.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERCELLCHANGE_HPP\n#define OPENMW_PACKETPLAYERCELLCHANGE_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerCellChange : public PlayerPacket\n    {\n    public:\n        PacketPlayerCellChange(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERCELLCHANGE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCellState.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerCellState.hpp\"\n\n\nmwmp::PacketPlayerCellState::PacketPlayerCellState(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_CELL_STATE;\n    priority = IMMEDIATE_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n}\n\nvoid mwmp::PacketPlayerCellState::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->cellStateChanges.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->cellStateChanges.clear();\n        player->cellStateChanges.resize(count);\n    }\n\n    for (auto &&cellState : player->cellStateChanges)\n    {\n        RW(cellState.type, send);\n        RW(cellState.cell.mData, send, true);\n        RW(cellState.cell.mName, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCellState.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERCELLSTATE_HPP\n#define OPENMW_PACKETPLAYERCELLSTATE_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerCellState : public PlayerPacket\n    {\n    public:\n        PacketPlayerCellState(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERCELLSTATE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCharGen.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerCharGen.hpp\"\n\nmwmp::PacketPlayerCharGen::PacketPlayerCharGen(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_CHARGEN;\n}\n\nvoid mwmp::PacketPlayerCharGen::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->charGenState, send);\n\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCharGen.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERCHARGEN_HPP\n#define OPENMW_PACKETPLAYERCHARGEN_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerCharGen : public PlayerPacket\n    {\n    public:\n        PacketPlayerCharGen(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERCHARGEN_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerClass.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerClass.hpp\"\n\nmwmp::PacketPlayerClass::PacketPlayerClass(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_CHARCLASS;\n}\n\nvoid mwmp::PacketPlayerClass::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream,  send);\n\n    RW(player->charClass.mId, send);\n\n    if (player->charClass.mId.empty()) // custom class\n    {\n        RW(player->charClass.mName, send, true);\n        RW(player->charClass.mDescription, send, true);\n        RW(player->charClass.mData, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerClass.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERCLASS_HPP\n#define OPENMW_PACKETPLAYERCLASS_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerClass : public PlayerPacket\n    {\n    public:\n        PacketPlayerClass(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERCLASS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCooldowns.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerCooldowns.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerCooldowns::PacketPlayerCooldowns(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_COOLDOWNS;\n}\n\nvoid PacketPlayerCooldowns::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->cooldownChanges.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->cooldownChanges.clear();\n        player->cooldownChanges.resize(count);\n    }\n\n    for (auto &&spell : player->cooldownChanges)\n    {\n        RW(spell.id, send, true);\n        RW(spell.startTimestampDay, send);\n        RW(spell.startTimestampHour, send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerCooldowns.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERCOOLDOWNS_HPP\n#define OPENMW_PACKETPLAYERCOOLDOWNS_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerCooldowns : public PlayerPacket\n    {\n    public:\n        PacketPlayerCooldowns(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERCOOLDOWNS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerDeath.cpp",
    "content": "#include \"PacketPlayerDeath.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerDeath::PacketPlayerDeath(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_DEATH;\n}\n\nvoid PacketPlayerDeath::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->deathState, send);\n    RW(player->killer.isPlayer, send);\n\n    if (player->killer.isPlayer)\n    {\n        RW(player->killer.guid, send);\n    }\n    else\n    {\n        RW(player->killer.refId, send, true);\n        RW(player->killer.refNum, send);\n        RW(player->killer.mpNum, send);\n\n        RW(player->killer.name, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerDeath.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERDEATH_HPP\n#define OPENMW_PACKETPLAYERDEATH_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerDeath: public PlayerPacket\n    {\n    public:\n        PacketPlayerDeath(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERDEATH_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerEquipment.cpp",
    "content": "#include \"PacketPlayerEquipment.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerEquipment::PacketPlayerEquipment(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_EQUIPMENT;\n}\n\nvoid PacketPlayerEquipment::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->exchangeFullInfo, send);\n\n    if (player->exchangeFullInfo)\n    {\n        for (auto &&equipmentItem : player->equipmentItems)\n        {\n            ExchangeItemInformation(equipmentItem, send);\n        }\n    }\n    else\n    {\n        uint32_t count;\n        if (send)\n            count = static_cast<uint32_t>(player->equipmentIndexChanges.size());\n\n        RW(count, send);\n\n        if (!send)\n        {\n            player->equipmentIndexChanges.clear();\n            player->equipmentIndexChanges.resize(count);\n        }\n\n        for (auto &&equipmentIndex : player->equipmentIndexChanges)\n        {\n            RW(equipmentIndex, send);\n            ExchangeItemInformation(player->equipmentItems[equipmentIndex], send);\n        }\n    }\n}\n\nvoid PacketPlayerEquipment::ExchangeItemInformation(Item &item, bool send)\n{\n    RW(item.refId, send, true);\n    RW(item.count, send);\n    RW(item.charge, send);\n    RW(item.enchantmentCharge, send);\n}\n\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerEquipment.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYEREQUIPMENT_HPP\n#define OPENMW_PACKETPLAYEREQUIPMENT_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerEquipment : public PlayerPacket\n    {\n    public:\n        PacketPlayerEquipment(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n        void ExchangeItemInformation(Item &item, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYEREQUIPMENT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerFaction.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerFaction.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerFaction::PacketPlayerFaction(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_FACTION;\n}\n\nvoid PacketPlayerFaction::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->factionChanges.action, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->factionChanges.factions.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->factionChanges.factions.clear();\n        player->factionChanges.factions.resize(count);\n    }\n\n    for (auto &&faction : player->factionChanges.factions)\n    {\n        RW(faction.factionId, send, true);\n\n        if (player->factionChanges.action == FactionChanges::RANK)\n            RW(faction.rank, send);\n\n        if (player->factionChanges.action == FactionChanges::EXPULSION)\n            RW(faction.isExpelled, send);\n\n        if (player->factionChanges.action == FactionChanges::REPUTATION)\n            RW(faction.reputation, send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerFaction.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERFACTION_HPP\n#define OPENMW_PACKETPLAYERFACTION_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerFaction : public PlayerPacket\n    {\n    public:\n        PacketPlayerFaction(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERFACTION_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerInput.cpp",
    "content": "#include \"PacketPlayerInput.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerInput::PacketPlayerInput(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_INPUT;\n}\n\nvoid PacketPlayerInput::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    // Placeholder\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerInput.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERINPUT_HPP\n#define OPENMW_PACKETPLAYERINPUT_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerInput : public PlayerPacket\n    {\n    public:\n        PacketPlayerInput(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERINPUT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerInventory.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerInventory.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerInventory::PacketPlayerInventory(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_INVENTORY;\n}\n\nvoid PacketPlayerInventory::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->inventoryChanges.action, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->inventoryChanges.items.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->inventoryChanges.items.clear();\n        player->inventoryChanges.items.resize(count);\n    }\n\n    for (auto &&item : player->inventoryChanges.items)\n    {\n        RW(item.refId, send, true);\n        RW(item.count, send);\n        RW(item.charge, send);\n        RW(item.enchantmentCharge, send);\n        RW(item.soul, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerInventory.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERINVENTORY_HPP\n#define OPENMW_PACKETPLAYERINVENTORY_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerInventory : public PlayerPacket\n    {\n    public:\n        PacketPlayerInventory(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERINVENTORY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerItemUse.cpp",
    "content": "#include \"PacketPlayerItemUse.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerItemUse::PacketPlayerItemUse(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_ITEM_USE;\n}\n\nvoid PacketPlayerItemUse::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->usedItem.refId, send, true);\n    RW(player->usedItem.count, send);\n    RW(player->usedItem.charge, send);\n    RW(player->usedItem.enchantmentCharge, send);\n    RW(player->usedItem.soul, send, true);\n\n    RW(player->usingItemMagic, send);\n    RW(player->itemUseDrawState, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerItemUse.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERITEMUSE_HPP\n#define OPENMW_PACKETPLAYERITEMUSE_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerItemUse : public PlayerPacket\n    {\n    public:\n        PacketPlayerItemUse(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERITEMUSE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerJail.cpp",
    "content": "#include \"PacketPlayerJail.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerJail::PacketPlayerJail(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_JAIL;\n}\n\nvoid PacketPlayerJail::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->jailDays, send);\n    RW(player->ignoreJailTeleportation, send);\n    RW(player->ignoreJailSkillIncreases, send);\n    RW(player->jailProgressText, send, true);\n    RW(player->jailEndText, send, true);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerJail.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERJAIL_HPP\n#define OPENMW_PACKETPLAYERJAIL_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerJail : public PlayerPacket\n    {\n    public:\n        PacketPlayerJail(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERJAIL_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerJournal.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerJournal.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerJournal::PacketPlayerJournal(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_JOURNAL;\n}\n\nvoid PacketPlayerJournal::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->journalChanges.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->journalChanges.clear();\n        player->journalChanges.resize(count);\n    }\n\n    for (auto &&journalItem : player->journalChanges)\n    {\n        RW(journalItem.type, send);\n        RW(journalItem.quest, send, true);\n        RW(journalItem.index, send);\n\n        if (journalItem.type == JournalItem::ENTRY)\n        {\n            RW(journalItem.actorRefId, send, true);\n\n            RW(journalItem.hasTimestamp, send);\n\n            if (journalItem.hasTimestamp)\n            {\n                RW(journalItem.timestamp.daysPassed, send);\n                RW(journalItem.timestamp.month, send);\n                RW(journalItem.timestamp.day, send);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerJournal.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERJOURNAL_HPP\n#define OPENMW_PACKETPLAYERJOURNAL_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerJournal : public PlayerPacket\n    {\n    public:\n        PacketPlayerJournal(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERJOURNAL_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerLevel.cpp",
    "content": "#include \"PacketPlayerLevel.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerLevel::PacketPlayerLevel(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_LEVEL;\n}\n\nvoid PacketPlayerLevel::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->creatureStats.mLevel, send);\n\n    RW(player->npcStats.mLevelProgress, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerLevel.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERLEVEL_HPP\n#define OPENMW_PACKETPLAYERLEVEL_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerLevel : public PlayerPacket\n    {\n    public:\n        PacketPlayerLevel(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERLEVEL_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerMiscellaneous.cpp",
    "content": "#include \"PacketPlayerMiscellaneous.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerMiscellaneous::PacketPlayerMiscellaneous(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_MISCELLANEOUS;\n}\n\nvoid PacketPlayerMiscellaneous::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->miscellaneousChangeType, send);\n\n    if (player->miscellaneousChangeType == mwmp::MISCELLANEOUS_CHANGE_TYPE::MARK_LOCATION)\n    {\n        RW(player->markCell.mData, send, true);\n        RW(player->markCell.mName, send, true);\n\n        RW(player->markPosition.pos, send);\n        RW(player->markPosition.rot[0], send);\n        RW(player->markPosition.rot[2], send);\n    }\n    else if (player->miscellaneousChangeType == mwmp::MISCELLANEOUS_CHANGE_TYPE::SELECTED_SPELL)\n        RW(player->selectedSpellId, send, true);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerMiscellaneous.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERMISCELLANEOUS_HPP\n#define OPENMW_PACKETPLAYERMISCELLANEOUS_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerMiscellaneous : public PlayerPacket\n    {\n    public:\n        PacketPlayerMiscellaneous(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERMISCELLANEOUS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerMomentum.cpp",
    "content": "#include \"PacketPlayerMomentum.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerMomentum::PacketPlayerMomentum(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_MOMENTUM;\n    priority = MEDIUM_PRIORITY;\n}\n\nvoid PacketPlayerMomentum::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n    \n    RW(player->momentum.pos, send, true);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerMomentum.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERMOMENTUM_HPP\n#define OPENMW_PACKETPLAYERMOMENTUM_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerMomentum : public PlayerPacket\n    {\n    public:\n        PacketPlayerMomentum(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERMOMENTUM_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerPosition.cpp",
    "content": "#include \"PacketPlayerPosition.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerPosition::PacketPlayerPosition(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_POSITION;\n    priority = MEDIUM_PRIORITY;\n    //reliability = UNRELIABLE_SEQUENCED;\n}\n\nvoid PacketPlayerPosition::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->position, send, 1);\n    RW(player->direction, send, 1);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerPosition.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERPOSITION_HPP\n#define OPENMW_PACKETPLAYERPOSITION_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerPosition : public PlayerPacket\n    {\n    public:\n        PacketPlayerPosition(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERPOSITION_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerQuickKeys.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerQuickKeys.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerQuickKeys::PacketPlayerQuickKeys(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_QUICKKEYS;\n}\n\nvoid PacketPlayerQuickKeys::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->quickKeyChanges.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->quickKeyChanges.clear();\n        player->quickKeyChanges.resize(count);\n    }\n\n    for (auto &&quickKey : player->quickKeyChanges)\n    {\n        RW(quickKey.type, send);\n        RW(quickKey.slot, send);\n\n        if (quickKey.type != QuickKey::UNASSIGNED)\n            RW(quickKey.itemId, send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerQuickKeys.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERQUICKKEYS_HPP\n#define OPENMW_PACKETPLAYERQUICKKEYS_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerQuickKeys : public PlayerPacket\n    {\n    public:\n        PacketPlayerQuickKeys(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERQUICKKEYS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerRegionAuthority.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketWorldRegionAuthority.hpp\"\n\nmwmp::PacketWorldRegionAuthority::PacketWorldRegionAuthority(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_WORLD_REGION_AUTHORITY;\n    priority = IMMEDIATE_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n}\n\nvoid mwmp::PacketWorldRegionAuthority::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->authorityRegion, send, true);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerReputation.cpp",
    "content": "#include \"PacketPlayerReputation.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerReputation::PacketPlayerReputation(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_REPUTATION;\n}\n\nvoid PacketPlayerReputation::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->npcStats.mReputation, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerReputation.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERREPUTATION_HPP\n#define OPENMW_PACKETPLAYERREPUTATION_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerReputation : public PlayerPacket\n    {\n    public:\n        PacketPlayerReputation(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERREPUTATION_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerRest.cpp",
    "content": "#include \"PacketPlayerRest.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerRest::PacketPlayerRest(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_REST;\n}\n\nvoid PacketPlayerRest::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    // Placeholder to be filled in later\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerRest.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERREST_HPP\n#define OPENMW_PACKETPLAYERREST_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerRest : public PlayerPacket\n    {\n    public:\n        PacketPlayerRest(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERREST_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerResurrect.cpp",
    "content": "#include \"PacketPlayerResurrect.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerResurrect::PacketPlayerResurrect(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_RESURRECT;\n}\n\nvoid PacketPlayerResurrect::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->resurrectType, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerResurrect.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERRESURRECT_HPP\n#define OPENMW_PACKETPLAYERRESURRECT_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerResurrect : public PlayerPacket\n    {\n    public:\n        PacketPlayerResurrect(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERRESURRECT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp",
    "content": "#include \"PacketPlayerShapeshift.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerShapeshift::PacketPlayerShapeshift(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_SHAPESHIFT;\n}\n\nvoid PacketPlayerShapeshift::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->scale, send);\n    RW(player->isWerewolf, send);\n\n    RW(player->displayCreatureName, send);\n    RW(player->creatureRefId, send, true);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerShapeshift.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERSHAPESHIFT_HPP\n#define OPENMW_PACKETPLAYERSHAPESHIFT_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerShapeshift : public PlayerPacket\n    {\n    public:\n        PacketPlayerShapeshift(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERSHAPESHIFT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerSkill.cpp",
    "content": "#include \"PacketPlayerSkill.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/esm/creaturestats.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerSkill::PacketPlayerSkill(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_SKILL;\n}\n\nvoid PacketPlayerSkill::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->exchangeFullInfo, send);\n\n    if (player->exchangeFullInfo)\n    {\n        RW(player->npcStats.mSkills, send);\n    }\n    else\n    {\n        uint32_t count;\n\n        if (send)\n            count = static_cast<uint32_t>(player->skillIndexChanges.size());\n\n        RW(count, send);\n\n        if (!send)\n        {\n            player->skillIndexChanges.clear();\n            player->skillIndexChanges.resize(count);\n        }\n\n        for (auto &&skillId : player->skillIndexChanges)\n        {\n            RW(skillId, send);\n            if (skillId >= 27)\n            {\n                packetValid = false;\n                return;\n            }\n            RW(player->npcStats.mSkills[skillId], send);\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerSkill.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERSKILL_HPP\n#define OPENMW_PACKETPLAYERSKILL_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerSkill : public PlayerPacket\n    {\n    public:\n        const static int SkillCount = 27;\n        const static int AttributeCount = 8;\n        PacketPlayerSkill(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERSKILL_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerSpeech.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerSpeech.hpp\"\n\nmwmp::PacketPlayerSpeech::PacketPlayerSpeech(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_SPEECH;\n}\n\nvoid mwmp::PacketPlayerSpeech::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->sound, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerSpeech.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERSPEECH_HPP\n#define OPENMW_PACKETPLAYERSPEECH_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerSpeech : public PlayerPacket\n    {\n    public:\n        PacketPlayerSpeech(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERSPEECH_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerSpellbook.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerSpellbook.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerSpellbook::PacketPlayerSpellbook(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_SPELLBOOK;\n}\n\nvoid PacketPlayerSpellbook::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->spellbookChanges.action, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->spellbookChanges.spells.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->spellbookChanges.spells.clear();\n        player->spellbookChanges.spells.resize(count);\n    }\n\n    for (auto &&spell : player->spellbookChanges.spells)\n    {\n        RW(spell.mId, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerSpellbook.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERSPELLBOOK_HPP\n#define OPENMW_PACKETPLAYERSPELLBOOK_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerSpellbook : public PlayerPacket\n    {\n    public:\n        PacketPlayerSpellbook(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERSPELLBOOK_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp",
    "content": "#include \"PacketPlayerSpellsActive.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerSpellsActive::PacketPlayerSpellsActive(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_SPELLS_ACTIVE;\n}\n\nvoid PacketPlayerSpellsActive::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->spellsActiveChanges.action, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->spellsActiveChanges.activeSpells.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->spellsActiveChanges.activeSpells.clear();\n        player->spellsActiveChanges.activeSpells.resize(count);\n    }\n\n    for (auto&& activeSpell : player->spellsActiveChanges.activeSpells)\n    {\n        RW(activeSpell.id, send, true);\n        RW(activeSpell.isStackingSpell, send);\n        RW(activeSpell.timestampDay, send);\n        RW(activeSpell.timestampHour, send);\n        RW(activeSpell.params.mDisplayName, send, true);\n\n        RW(activeSpell.caster.isPlayer, send);\n\n        if (activeSpell.caster.isPlayer)\n        {\n            RW(activeSpell.caster.guid, send);\n        }\n        else\n        {\n            RW(activeSpell.caster.refId, send, true);\n            RW(activeSpell.caster.refNum, send);\n            RW(activeSpell.caster.mpNum, send);\n        }\n\n        uint32_t effectCount;\n\n        if (send)\n            effectCount = static_cast<uint32_t>(activeSpell.params.mEffects.size());\n\n        RW(effectCount, send);\n\n        if (effectCount > maxEffects)\n        {\n            return;\n        }\n\n        if (!send)\n        {\n            activeSpell.params.mEffects.clear();\n            activeSpell.params.mEffects.resize(effectCount);\n        }\n\n        for (auto&& effect : activeSpell.params.mEffects)\n        {\n            RW(effect.mEffectId, send);\n            RW(effect.mArg, send);\n            RW(effect.mMagnitude, send);\n            RW(effect.mDuration, send);\n            RW(effect.mTimeLeft, send);\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERSPELLSACTIVE_HPP\n#define OPENMW_PACKETPLAYERSPELLSACTIVE_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerSpellsActive : public PlayerPacket\n    {\n    public:\n        PacketPlayerSpellsActive(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n\n    protected:\n        static const int maxEffects = 20;\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERSPELLSACTIVE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerStatsDynamic.cpp",
    "content": "#include \"PacketPlayerStatsDynamic.hpp\"\n\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketPlayerStatsDynamic::PacketPlayerStatsDynamic(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_STATS_DYNAMIC;\n}\n\nvoid PacketPlayerStatsDynamic::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    RW(player->exchangeFullInfo, send);\n\n    if (player->exchangeFullInfo)\n    {\n        RW(player->creatureStats.mDynamic, send);\n    }\n    else\n    {\n        uint32_t count;\n\n        if (send)\n            count = static_cast<uint32_t>(player->statsDynamicIndexChanges.size());\n\n        RW(count, send);\n\n        if (!send)\n        {\n            player->statsDynamicIndexChanges.clear();\n            player->statsDynamicIndexChanges.resize(count);\n        }\n\n        for (auto &&statsDynamicIndex : player->statsDynamicIndexChanges)\n        {\n            RW(statsDynamicIndex, send);\n            if (statsDynamicIndex >= 3)\n            {\n                packetValid = false;\n                return;\n            }\n            RW(player->creatureStats.mDynamic[statsDynamicIndex], send);\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerStatsDynamic.hpp",
    "content": "#ifndef OPENMW_PACKETSTATSDYNAMIC_HPP\n#define OPENMW_PACKETSTATSDYNAMIC_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerStatsDynamic : public PlayerPacket\n    {\n    public:\n        PacketPlayerStatsDynamic(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETSTATSDYNAMIC_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerTopic.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketPlayerTopic.hpp\"\n\nusing namespace mwmp;\n\nPacketPlayerTopic::PacketPlayerTopic(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)\n{\n    packetID = ID_PLAYER_TOPIC;\n}\n\nvoid PacketPlayerTopic::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    PlayerPacket::Packet(newBitstream, send);\n\n    uint32_t count;\n\n    if (send)\n        count = static_cast<uint32_t>(player->topicChanges.size());\n\n    RW(count, send);\n\n    if (!send)\n    {\n        player->topicChanges.clear();\n        player->topicChanges.resize(count);\n    }\n\n    for (auto &&topic : player->topicChanges)\n    {\n        RW(topic.topicId, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PacketPlayerTopic.hpp",
    "content": "#ifndef OPENMW_PACKETPLAYERTOPIC_HPP\n#define OPENMW_PACKETPLAYERTOPIC_HPP\n\n#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketPlayerTopic : public PlayerPacket\n    {\n    public:\n        PacketPlayerTopic(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETPLAYERTOPIC_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PlayerPacket.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <PacketPriority.h>\n#include <RakPeer.h>\n#include \"PlayerPacket.hpp\"\n\nusing namespace mwmp;\n\nPlayerPacket::PlayerPacket(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    packetID = 0;\n    priority = HIGH_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n    orderChannel = CHANNEL_PLAYER;\n    this->peer = peer;\n}\n\nPlayerPacket::~PlayerPacket()\n{\n\n}\n\nvoid PlayerPacket::setPlayer(BasePlayer *newPlayer)\n{\n    player = newPlayer;\n    guid = player->guid;\n}\n\nBasePlayer *PlayerPacket::getPlayer()\n{\n    return player;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Player/PlayerPacket.hpp",
    "content": "#ifndef OPENMW_PLAYERPACKET_HPP\n#define OPENMW_PLAYERPACKET_HPP\n\n#include <string>\n#include <RakNetTypes.h>\n#include <BitStream.h>\n#include <PacketPriority.h>\n#include <components/openmw-mp/Base/BasePlayer.hpp>\n\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n\nnamespace mwmp\n{\n    class PlayerPacket : public BasePacket\n    {\n    public:\n        PlayerPacket(RakNet::RakPeerInterface *peer);\n\n        ~PlayerPacket();\n\n        void setPlayer(BasePlayer *newPlayer);\n        BasePlayer *getPlayer();\n\n    protected:\n        BasePlayer *player;\n\n    };\n}\n\n#endif //OPENMW_PLAYERPACKET_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/System/PacketSystemHandshake.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketSystemHandshake.hpp\"\n\nusing namespace mwmp;\n\nPacketSystemHandshake::PacketSystemHandshake(RakNet::RakPeerInterface *peer) : SystemPacket(peer)\n{\n    packetID = ID_SYSTEM_HANDSHAKE;\n    orderChannel = CHANNEL_SYSTEM;\n}\n\nvoid PacketSystemHandshake::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    SystemPacket::Packet(newBitstream, send);\n\n    if (!RW(system->playerName, send, true, maxNameLength) ||\n        !RW(system->serverPassword, send, true, maxPasswordLength))\n    {\n        packetValid = false;\n        return;\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/System/PacketSystemHandshake.hpp",
    "content": "#ifndef OPENMW_PACKETSYSTEMHANDSHAKE_HPP\n#define OPENMW_PACKETSYSTEMHANDSHAKE_HPP\n\n#include <components/openmw-mp/Packets/System/SystemPacket.hpp>\n\nnamespace mwmp\n{\n    class PacketSystemHandshake : public SystemPacket\n    {\n    public:\n        PacketSystemHandshake(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n\n        const static uint32_t maxNameLength = 256;\n        const static uint32_t maxPasswordLength = 256;\n    };\n}\n\n#endif //OPENMW_PACKETSYSTEMHANDSHAKE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/System/SystemPacket.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <PacketPriority.h>\n#include <RakPeer.h>\n#include \"SystemPacket.hpp\"\n\nusing namespace mwmp;\n\nSystemPacket::SystemPacket(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    packetID = 0;\n    priority = HIGH_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n    orderChannel = CHANNEL_SYSTEM;\n    this->peer = peer;\n}\n\nSystemPacket::~SystemPacket()\n{\n\n}\n\nvoid SystemPacket::setSystem(BaseSystem *newSystem)\n{\n    system = newSystem;\n    guid = system->guid;\n}\n\nBaseSystem *SystemPacket::getSystem()\n{\n    return system;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/System/SystemPacket.hpp",
    "content": "#ifndef OPENMW_SYSTEMPACKET_HPP\n#define OPENMW_SYSTEMPACKET_HPP\n\n#include <string>\n#include <RakNetTypes.h>\n#include <BitStream.h>\n#include <PacketPriority.h>\n#include <components/openmw-mp/Base/BaseSystem.hpp>\n\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n\nnamespace mwmp\n{\n    class SystemPacket : public BasePacket\n    {\n    public:\n        SystemPacket(RakNet::RakPeerInterface *peer);\n\n        ~SystemPacket();\n\n        void setSystem(BaseSystem *newSystem);\n        BaseSystem *getSystem();\n\n    protected:\n        BaseSystem *system;\n\n    };\n}\n\n#endif //OPENMW_SYSTEMPACKET_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketCellReset.cpp",
    "content": "#include \"PacketCellReset.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketCellReset::PacketCellReset(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_CELL_RESET;\n    orderChannel = CHANNEL_SYSTEM;\n}\n\nvoid PacketCellReset::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    uint32_t cellCount;\n\n    if (send)\n        cellCount = static_cast<uint32_t>(worldstate->cellsToReset.size());\n\n    RW(cellCount, send);\n\n    if (!send)\n    {\n        worldstate->cellsToReset.clear();\n        worldstate->cellsToReset.resize(cellCount);\n    }\n\n    for (auto &&cellToReset : worldstate->cellsToReset)\n    {\n        RW(cellToReset.mData, send, true);\n        RW(cellToReset.mName, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketCellReset.hpp",
    "content": "#ifndef OPENMW_PACKETCELLRESET_HPP\n#define OPENMW_PACKETCELLRESET_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nnamespace mwmp\n{\n    class PacketCellReset: public WorldstatePacket\n    {\n    public:\n        PacketCellReset(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETCELLRESET_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketClientScriptGlobal.cpp",
    "content": "#include \"PacketClientScriptGlobal.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketClientScriptGlobal::PacketClientScriptGlobal(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_CLIENT_SCRIPT_GLOBAL;\n    orderChannel = CHANNEL_WORLDSTATE;\n}\n\nvoid PacketClientScriptGlobal::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    uint32_t clientGlobalsCount;\n\n    if (send)\n        clientGlobalsCount = static_cast<uint32_t>(worldstate->clientGlobals.size());\n\n    RW(clientGlobalsCount, send);\n\n    if (!send)\n    {\n        worldstate->clientGlobals.clear();\n        worldstate->clientGlobals.resize(clientGlobalsCount);\n    }\n\n    for (auto &&clientGlobal : worldstate->clientGlobals)\n    {\n        RW(clientGlobal.id, send, true);\n        RW(clientGlobal.variableType, send);\n\n        if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::SHORT || clientGlobal.variableType == mwmp::VARIABLE_TYPE::LONG)\n            RW(clientGlobal.intValue, send);\n        else if (clientGlobal.variableType == mwmp::VARIABLE_TYPE::FLOAT)\n            RW(clientGlobal.floatValue, send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketClientScriptGlobal.hpp",
    "content": "#ifndef OPENMW_PACKETCLIENTSCRIPTGLOBAL_HPP\n#define OPENMW_PACKETCLIENTSCRIPTGLOBAL_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nnamespace mwmp\n{\n    class PacketClientScriptGlobal: public WorldstatePacket\n    {\n    public:\n        PacketClientScriptGlobal(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETCLIENTSCRIPTGLOBAL_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketClientScriptSettings.cpp",
    "content": "#include \"PacketClientScriptSettings.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketClientScriptSettings::PacketClientScriptSettings(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_CLIENT_SCRIPT_SETTINGS;\n    orderChannel = CHANNEL_WORLDSTATE;\n}\n\nvoid PacketClientScriptSettings::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    uint32_t clientScriptsCount;\n\n    if (send)\n        clientScriptsCount = static_cast<uint32_t>(worldstate->synchronizedClientScriptIds.size());\n\n    RW(clientScriptsCount, send);\n\n    if (!send)\n    {\n        worldstate->synchronizedClientScriptIds.clear();\n        worldstate->synchronizedClientScriptIds.resize(clientScriptsCount);\n    }\n\n    for (auto &&clientScriptId : worldstate->synchronizedClientScriptIds)\n    {\n        RW(clientScriptId, send, true);\n    }\n\n    uint32_t clientGlobalsCount;\n\n    if (send)\n        clientGlobalsCount = static_cast<uint32_t>(worldstate->synchronizedClientGlobalIds.size());\n\n    RW(clientGlobalsCount, send);\n\n    if (!send)\n    {\n        worldstate->synchronizedClientGlobalIds.clear();\n        worldstate->synchronizedClientGlobalIds.resize(clientGlobalsCount);\n    }\n\n    for (auto &&clientGlobalId : worldstate->synchronizedClientGlobalIds)\n    {\n        RW(clientGlobalId, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketClientScriptSettings.hpp",
    "content": "#ifndef OPENMW_PACKETCLIENTSCRIPTSETTINGS_HPP\n#define OPENMW_PACKETCLIENTSCRIPTSETTINGS_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n\nnamespace mwmp\n{\n    class PacketClientScriptSettings : public WorldstatePacket\n    {\n    public:\n        PacketClientScriptSettings(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETCLIENTSCRIPTSETTINGS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketRecordDynamic.cpp",
    "content": "#include \"PacketRecordDynamic.hpp\"\n\n#include <components/openmw-mp/TimedLog.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/Utils.hpp>\n\nusing namespace mwmp;\n\nPacketRecordDynamic::PacketRecordDynamic(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_RECORD_DYNAMIC;\n    orderChannel = CHANNEL_WORLDSTATE;\n}\n\nvoid PacketRecordDynamic::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    RW(worldstate->recordsType, send);\n\n    if (send)\n    {\n        // These can be created by players through gameplay and should be checked first\n        if (worldstate->recordsType == mwmp::RECORD_TYPE::SPELL)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->spellRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::POTION)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->potionRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->enchantmentRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::ARMOR)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->armorRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::BOOK)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->bookRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::CLOTHING)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->clothingRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->miscellaneousRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::WEAPON)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->weaponRecords);\n        // These can only be created by the server\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->activatorRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::APPARATUS)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->apparatusRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::BODYPART)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->bodyPartRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::CELL)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->cellRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::CONTAINER)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->containerRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::CREATURE)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->creatureRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::DOOR)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->doorRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::GAMESETTING)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->gameSettingRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::INGREDIENT)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->ingredientRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::LIGHT)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->lightRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::LOCKPICK)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->lockpickRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::NPC)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->npcRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::PROBE)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->probeRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::REPAIR)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->repairRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::SCRIPT)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->scriptRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::STATIC)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->staticRecords);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::SOUND)\n            worldstate->recordsCount = Utils::getVectorSize(worldstate->soundRecords);\n        else\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Processed invalid ID_RECORD_DYNAMIC packet about unimplemented recordsType %i\",\n                worldstate->recordsType);\n            return;\n        }\n    }\n\n    RW(worldstate->recordsCount, send);\n\n    if (worldstate->recordsCount > maxRecords)\n    {\n        LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Processed invalid ID_RECORD_DYNAMIC packet with %i records, above the maximum of %i\",\n            worldstate->recordsCount, maxRecords);\n        LOG_APPEND(TimedLog::LOG_ERROR, \"- The packet was ignored after that point\");\n        return;\n    }\n\n    if (!send)\n    {\n        if (worldstate->recordsType == mwmp::RECORD_TYPE::SPELL)\n            Utils::resetVector(worldstate->spellRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::POTION)\n            Utils::resetVector(worldstate->potionRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n            Utils::resetVector(worldstate->enchantmentRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::ARMOR)\n            Utils::resetVector(worldstate->armorRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::BOOK)\n            Utils::resetVector(worldstate->bookRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::CLOTHING)\n            Utils::resetVector(worldstate->clothingRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n            Utils::resetVector(worldstate->miscellaneousRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::WEAPON)\n            Utils::resetVector(worldstate->weaponRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n            Utils::resetVector(worldstate->activatorRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::APPARATUS)\n            Utils::resetVector(worldstate->apparatusRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::BODYPART)\n            Utils::resetVector(worldstate->bodyPartRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::CELL)\n            Utils::resetVector(worldstate->cellRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::CONTAINER)\n            Utils::resetVector(worldstate->containerRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::CREATURE)\n            Utils::resetVector(worldstate->creatureRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::DOOR)\n            Utils::resetVector(worldstate->doorRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::GAMESETTING)\n            Utils::resetVector(worldstate->gameSettingRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::INGREDIENT)\n            Utils::resetVector(worldstate->ingredientRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::LOCKPICK)\n            Utils::resetVector(worldstate->lockpickRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::LIGHT)\n            Utils::resetVector(worldstate->lightRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::NPC)\n            Utils::resetVector(worldstate->npcRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::PROBE)\n            Utils::resetVector(worldstate->probeRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::REPAIR)\n            Utils::resetVector(worldstate->repairRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::SCRIPT)\n            Utils::resetVector(worldstate->scriptRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::SOUND)\n            Utils::resetVector(worldstate->soundRecords, worldstate->recordsCount);\n        else if (worldstate->recordsType == mwmp::RECORD_TYPE::STATIC)\n            Utils::resetVector(worldstate->staticRecords, worldstate->recordsCount);\n    }\n\n    if (worldstate->recordsType == mwmp::RECORD_TYPE::SPELL)\n    {\n        for (auto &&record : worldstate->spellRecords)\n        {\n            auto &&recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mData.mType, send);\n            RW(recordData.mData.mCost, send);\n            RW(recordData.mData.mFlags, send);\n            ProcessEffects(recordData.mEffects, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasSubtype, send);\n                RW(overrides.hasCost, send);\n                RW(overrides.hasFlags, send);\n                RW(overrides.hasEffects, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::POTION)\n    {\n        for (auto &&record : worldstate->potionRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.quantity, send);\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mWeight, send);\n            RW(recordData.mData.mValue, send);\n            RW(recordData.mData.mAutoCalc, send);\n            RW(recordData.mScript, send, true);\n            ProcessEffects(recordData.mEffects, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasAutoCalc, send);\n                RW(overrides.hasScript, send);\n                RW(overrides.hasEffects, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)\n    {\n        for (auto &&record : worldstate->enchantmentRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mData.mType, send);\n            RW(recordData.mData.mCost, send);\n            RW(recordData.mData.mCharge, send);\n            RW(recordData.mData.mFlags, send);\n            ProcessEffects(recordData.mEffects, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasSubtype, send);\n                RW(overrides.hasCost, send);\n                RW(overrides.hasCharge, send);\n                RW(overrides.hasFlags, send);\n                RW(overrides.hasEffects, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::ARMOR)\n    {\n        for (auto &&record : worldstate->armorRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mType, send);\n            RW(recordData.mData.mWeight, send);\n            RW(recordData.mData.mValue, send);\n            RW(recordData.mData.mHealth, send);\n            RW(recordData.mData.mArmor, send);\n            RW(recordData.mData.mEnchant, send);\n            RW(recordData.mEnchant, send, true);\n            RW(recordData.mScript, send, true);\n            ProcessBodyParts(recordData.mParts, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasSubtype, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasHealth, send);\n                RW(overrides.hasArmorRating, send);\n                RW(overrides.hasEnchantmentCharge, send);\n                RW(overrides.hasEnchantmentId, send);\n                RW(overrides.hasScript, send);\n                RW(overrides.hasBodyParts, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::BOOK)\n    {\n        for (auto &&record : worldstate->bookRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mText, send, true);\n            RW(recordData.mData.mWeight, send);\n            RW(recordData.mData.mValue, send);\n            RW(recordData.mData.mIsScroll, send);\n            RW(recordData.mData.mSkillId, send);\n            RW(recordData.mData.mEnchant, send);\n            RW(recordData.mEnchant, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasText, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasScrollState, send);\n                RW(overrides.hasSkillId, send);\n                RW(overrides.hasEnchantmentCharge, send);\n                RW(overrides.hasEnchantmentId, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::CLOTHING)\n    {\n        for (auto &&record : worldstate->clothingRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mType, send);\n            RW(recordData.mData.mWeight, send);\n            RW(recordData.mData.mValue, send);\n            RW(recordData.mData.mEnchant, send);\n            RW(recordData.mEnchant, send, true);\n            RW(recordData.mScript, send, true);\n            ProcessBodyParts(recordData.mParts, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasSubtype, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasEnchantmentCharge, send);\n                RW(overrides.hasEnchantmentId, send);\n                RW(overrides.hasScript, send);\n                RW(overrides.hasBodyParts, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)\n    {\n        for (auto &&record : worldstate->miscellaneousRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mWeight, send);\n            RW(recordData.mData.mValue, send);\n            RW(recordData.mData.mIsKey, send);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasKeyState, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::WEAPON)\n    {\n        for (auto &&record : worldstate->weaponRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.quantity, send);\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mType, send);\n            RW(recordData.mData.mWeight, send);\n            RW(recordData.mData.mValue, send);\n            RW(recordData.mData.mHealth, send);\n            RW(recordData.mData.mSpeed, send);\n            RW(recordData.mData.mReach, send);\n            RW(recordData.mData.mChop[0], send);\n            RW(recordData.mData.mChop[1], send);\n            RW(recordData.mData.mSlash[0], send);\n            RW(recordData.mData.mSlash[1], send);\n            RW(recordData.mData.mThrust[0], send);\n            RW(recordData.mData.mThrust[1], send);\n            RW(recordData.mData.mFlags, send);\n            RW(recordData.mData.mEnchant, send);\n            RW(recordData.mEnchant, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasSubtype, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasHealth, send);\n                RW(overrides.hasSpeed, send);\n                RW(overrides.hasReach, send);\n                RW(overrides.hasDamageChop, send);\n                RW(overrides.hasDamageSlash, send);\n                RW(overrides.hasDamageThrust, send);\n                RW(overrides.hasFlags, send);\n                RW(overrides.hasEnchantmentCharge, send);\n                RW(overrides.hasEnchantmentId, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::ACTIVATOR)\n    {\n        for (auto &&record : worldstate->activatorRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::APPARATUS)\n    {\n        for (auto &&record : worldstate->apparatusRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mType, send, true);\n            RW(recordData.mData.mWeight, send, true);\n            RW(recordData.mData.mValue, send, true);\n            RW(recordData.mData.mQuality, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasSubtype, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasQuality, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::BODYPART)\n    {\n        for (auto &&record : worldstate->bodyPartRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mRace, send, true);\n            RW(recordData.mData.mType, send);\n            RW(recordData.mData.mPart, send);\n            RW(recordData.mData.mVampire, send);\n            RW(recordData.mData.mFlags, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasModel, send);\n                RW(overrides.hasRace, send);\n                RW(overrides.hasSubtype, send);\n                RW(overrides.hasBodyPartType, send);\n                RW(overrides.hasVampireState, send);\n                RW(overrides.hasFlags, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::CELL)\n    {\n        for (auto &&record : worldstate->cellRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mName, send, true);\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::CONTAINER)\n    {\n        for (auto &&record : worldstate->containerRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mWeight, send);\n            RW(recordData.mFlags, send);\n            RW(recordData.mScript, send, true);\n            ProcessInventoryList(record.inventory, recordData.mInventory, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasFlags, send);\n                RW(overrides.hasScript, send);\n                RW(overrides.hasInventory, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::CREATURE)\n    {\n        for (auto &&record : worldstate->creatureRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(record.inventoryBaseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mScale, send);\n            RW(recordData.mBloodType, send);\n            RW(recordData.mData.mType, send);\n            RW(recordData.mData.mLevel, send);\n            RW(recordData.mData.mHealth, send);\n            RW(recordData.mData.mMana, send);\n            RW(recordData.mData.mFatigue, send);\n            RW(recordData.mData.mSoul, send);\n            RW(recordData.mData.mAttack[0], send);\n            RW(recordData.mData.mAttack[1], send);\n            RW(recordData.mData.mAttack[2], send);\n            RW(recordData.mData.mAttack[3], send);\n            RW(recordData.mData.mAttack[4], send);\n            RW(recordData.mData.mAttack[5], send);\n            RW(recordData.mAiData.mFight, send);\n            RW(recordData.mAiData.mFlee, send);\n            RW(recordData.mAiData.mAlarm, send);\n            RW(recordData.mAiData.mServices, send);\n            RW(recordData.mFlags, send);\n            RW(recordData.mScript, send, true);\n            ProcessInventoryList(record.inventory, recordData.mInventory, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasScale, send);\n                RW(overrides.hasBloodType, send);\n                RW(overrides.hasSubtype, send);\n                RW(overrides.hasLevel, send);\n                RW(overrides.hasHealth, send);\n                RW(overrides.hasMagicka, send);\n                RW(overrides.hasFatigue, send);\n                RW(overrides.hasSoulValue, send);\n                RW(overrides.hasDamageChop, send);\n                RW(overrides.hasDamageSlash, send);\n                RW(overrides.hasDamageThrust, send);\n                RW(overrides.hasAiFight, send);\n                RW(overrides.hasAiFlee, send);\n                RW(overrides.hasAiAlarm, send);\n                RW(overrides.hasAiServices, send);\n                RW(overrides.hasFlags, send);\n                RW(overrides.hasScript, send);\n                RW(overrides.hasInventory, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::DOOR)\n    {\n        for (auto &&record : worldstate->doorRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mOpenSound, send, true);\n            RW(recordData.mCloseSound, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasOpenSound, send);\n                RW(overrides.hasCloseSound, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::GAMESETTING)\n    {\n        for (auto&& record : worldstate->gameSettingRecords)\n        {\n            auto& recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(record.variable.variableType, send, true);\n\n            short variableType = record.variable.variableType;\n\n            if (variableType == mwmp::VARIABLE_TYPE::INT)\n            {\n                RW(record.variable.intValue, send);\n                recordData.mValue.setType(ESM::VarType::VT_Int);\n                recordData.mValue.setInteger(record.variable.intValue);\n            }\n            else if (variableType == mwmp::VARIABLE_TYPE::FLOAT)\n            {\n                RW(record.variable.floatValue, send);\n\n                if (variableType == mwmp::VARIABLE_TYPE::FLOAT)\n                    recordData.mValue.setType(ESM::VarType::VT_Float);\n\n                recordData.mValue.setFloat(record.variable.floatValue);\n            }\n            else if (variableType == mwmp::VARIABLE_TYPE::STRING)\n            {\n                RW(record.variable.stringValue, send, true);\n                recordData.mValue.setType(ESM::VarType::VT_String);\n                recordData.mValue.setString(record.variable.stringValue);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::INGREDIENT)\n    {\n        for (auto &&record : worldstate->ingredientRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mWeight, send);\n            RW(recordData.mData.mValue, send);\n            RW(recordData.mData.mEffectID, send);\n            RW(recordData.mData.mAttributes, send);\n            RW(recordData.mData.mSkills, send);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasEffects, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::LIGHT)\n    {\n        for (auto &&record : worldstate->lightRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mSound, send, true);\n            RW(recordData.mData.mWeight, send, true);\n            RW(recordData.mData.mValue, send, true);\n            RW(recordData.mData.mTime, send, true);\n            RW(recordData.mData.mRadius, send, true);\n            RW(recordData.mData.mColor, send, true);\n            RW(recordData.mData.mFlags, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasSound, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasTime, send);\n                RW(overrides.hasRadius, send);\n                RW(overrides.hasColor, send);\n                RW(overrides.hasFlags, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::LOCKPICK)\n    {\n        for (auto &&record : worldstate->lockpickRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mWeight, send, true);\n            RW(recordData.mData.mValue, send, true);\n            RW(recordData.mData.mQuality, send, true);\n            RW(recordData.mData.mUses, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasQuality, send);\n                RW(overrides.hasUses, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::NPC)\n    {\n        for (auto &&record : worldstate->npcRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(record.inventoryBaseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mFlags, send);\n            RW(recordData.mRace, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mHair, send, true);\n            RW(recordData.mHead, send, true);\n            RW(recordData.mClass, send, true);\n            RW(recordData.mFaction, send, true);\n            RW(recordData.mScript, send, true);\n            RW(recordData.mNpdt.mLevel, send);\n            RW(recordData.mNpdt.mHealth, send);\n            RW(recordData.mNpdt.mMana, send);\n            RW(recordData.mNpdt.mFatigue, send);\n            RW(recordData.mAiData.mFight, send);\n            RW(recordData.mAiData.mFlee, send);\n            RW(recordData.mAiData.mAlarm, send);\n            RW(recordData.mAiData.mServices, send);\n\n            RW(recordData.mNpdtType, send);\n            ProcessInventoryList(record.inventory, recordData.mInventory, send);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasGender, send);\n                RW(overrides.hasFlags, send);\n                RW(overrides.hasRace, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasHair, send);\n                RW(overrides.hasHead, send);\n                RW(overrides.hasFaction, send);\n                RW(overrides.hasScript, send);\n                RW(overrides.hasLevel, send);\n                RW(overrides.hasHealth, send);\n                RW(overrides.hasMagicka, send);\n                RW(overrides.hasFatigue, send);\n                RW(overrides.hasAiFight, send);\n                RW(overrides.hasAiFlee, send);\n                RW(overrides.hasAiAlarm, send);\n                RW(overrides.hasAiServices, send);\n                RW(overrides.hasAutoCalc, send);\n                RW(overrides.hasInventory, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::PROBE)\n    {\n        for (auto &&record : worldstate->probeRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mWeight, send, true);\n            RW(recordData.mData.mValue, send, true);\n            RW(recordData.mData.mQuality, send, true);\n            RW(recordData.mData.mUses, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasQuality, send);\n                RW(overrides.hasUses, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::REPAIR)\n    {\n        for (auto &&record : worldstate->repairRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mName, send, true);\n            RW(recordData.mModel, send, true);\n            RW(recordData.mIcon, send, true);\n            RW(recordData.mData.mWeight, send, true);\n            RW(recordData.mData.mValue, send, true);\n            RW(recordData.mData.mQuality, send, true);\n            RW(recordData.mData.mUses, send, true);\n            RW(recordData.mScript, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasName, send);\n                RW(overrides.hasModel, send);\n                RW(overrides.hasIcon, send);\n                RW(overrides.hasWeight, send);\n                RW(overrides.hasValue, send);\n                RW(overrides.hasQuality, send);\n                RW(overrides.hasUses, send);\n                RW(overrides.hasScript, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::SCRIPT)\n    {\n        for (auto &&record : worldstate->scriptRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mScriptText, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasScriptText, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::STATIC)\n    {\n        for (auto &&record : worldstate->staticRecords)\n        {\n            auto &recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mModel, send, true);\n\n            if (!record.baseId.empty())\n            {\n                auto &&overrides = record.baseOverrides;\n                RW(overrides.hasModel, send);\n            }\n        }\n    }\n    else if (worldstate->recordsType == mwmp::RECORD_TYPE::SOUND)\n    {\n        for (auto&& record : worldstate->soundRecords)\n        {\n            auto& recordData = record.data;\n\n            RW(record.baseId, send, true);\n            RW(recordData.mId, send, true);\n            RW(recordData.mSound, send, true);\n            RW(recordData.mData.mVolume, send);\n            RW(recordData.mData.mMinRange, send);\n            RW(recordData.mData.mMaxRange, send);\n\n            if (!record.baseId.empty())\n            {\n                auto&& overrides = record.baseOverrides;\n                RW(overrides.hasSound, send);\n                RW(overrides.hasVolume, send);\n                RW(overrides.hasMinRange, send);\n                RW(overrides.hasMaxRange, send);\n            }\n        }\n    }\n}\n\nvoid PacketRecordDynamic::ProcessEffects(ESM::EffectList &effectList, bool send)\n{\n    uint32_t effectCount;\n\n    if (send)\n        effectCount = static_cast<uint32_t>(effectList.mList.size());\n\n    RW(effectCount, send);\n\n    if (effectCount > maxEffects)\n    {\n        return;\n    }\n\n    if (!send)\n    {\n        effectList.mList.clear();\n        effectList.mList.resize(effectCount);\n    }\n\n    for (auto &&effect : effectList.mList)\n    {\n        RW(effect.mEffectID, send);\n        RW(effect.mAttribute, send);\n        RW(effect.mSkill, send);\n        RW(effect.mRange, send);\n        RW(effect.mArea, send);\n        RW(effect.mDuration, send);\n        RW(effect.mMagnMax, send);\n        RW(effect.mMagnMin, send);\n    }\n}\n\nvoid PacketRecordDynamic::ProcessBodyParts(ESM::PartReferenceList &partList, bool send)\n{\n    uint32_t partCount;\n\n    if (send)\n        partCount = static_cast<uint32_t>(partList.mParts.size());\n\n    RW(partCount, send);\n\n    if (partCount > maxParts)\n    {\n        return;\n    }\n\n    if (!send)\n    {\n        partList.mParts.clear();\n        partList.mParts.resize(partCount);\n    }\n\n    for (auto &&part : partList.mParts)\n    {\n        RW(part.mPart, send);\n        RW(part.mMale, send, true);\n        RW(part.mFemale, send, true);\n    }\n}\n\n// ESM::InventoryList has a strange structure that makes it hard to read in packets directly, so we just deal with it\n// here with the help of a separate mwmp::Item vector\nvoid PacketRecordDynamic::ProcessInventoryList(std::vector<mwmp::Item> &inventory, ESM::InventoryList &inventoryList, bool send)\n{\n    uint32_t itemCount;\n\n    if (send)\n        itemCount = static_cast<uint32_t>(inventory.size());\n\n    RW(itemCount, send);\n\n    if (itemCount > maxItems)\n    {\n        return;\n    }\n\n    if (!send)\n    {\n        inventory.clear();\n        inventory.resize(itemCount);\n        inventoryList.mList.clear();\n    }\n\n    for (auto &&item : inventory)\n    {\n        RW(item.refId, send, true);\n        RW(item.count, send, true);\n\n        if (!send)\n        {\n            ESM::ContItem contItem;\n            contItem.mItem.assign(item.refId);\n            contItem.mCount = item.count;\n            inventoryList.mList.push_back(contItem);\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketRecordDynamic.hpp",
    "content": "#ifndef OPENMW_PACKETRECORDDYNAMIC_HPP\n#define OPENMW_PACKETRECORDDYNAMIC_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nnamespace mwmp\n{\n    class PacketRecordDynamic: public WorldstatePacket\n    {\n    public:\n        PacketRecordDynamic(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n\n        void ProcessEffects(ESM::EffectList &effectList, bool send);\n        void ProcessBodyParts(ESM::PartReferenceList &bodyPartList, bool send);\n        void ProcessInventoryList(std::vector<mwmp::Item> &inventory, ESM::InventoryList &inventoryList, bool send);\n\n    protected:\n        static const int maxRecords = 3000;\n        static const int maxEffects = 100;\n        static const int maxParts = 7;\n        static const int maxItems = 1000;\n    };\n}\n\n#endif //OPENMW_PACKETRECORDDYNAMIC_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldCollisionOverride.cpp",
    "content": "#include \"PacketWorldCollisionOverride.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketWorldCollisionOverride::PacketWorldCollisionOverride(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_WORLD_COLLISION_OVERRIDE;\n    orderChannel = CHANNEL_WORLDSTATE;\n}\n\nvoid PacketWorldCollisionOverride::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    RW(worldstate->hasPlayerCollision, send);\n    RW(worldstate->hasActorCollision, send);\n    RW(worldstate->hasPlacedObjectCollision, send);\n    RW(worldstate->useActorCollisionForPlacedObjects, send);\n\n    uint32_t enforcedCollisionCount;\n\n    if (send)\n        enforcedCollisionCount = static_cast<uint32_t>(worldstate->enforcedCollisionRefIds.size());\n\n    RW(enforcedCollisionCount, send);\n\n    if (!send)\n    {\n        worldstate->enforcedCollisionRefIds.clear();\n        worldstate->enforcedCollisionRefIds.resize(enforcedCollisionCount);\n    }\n\n    for (auto &&enforcedCollisionRefId : worldstate->enforcedCollisionRefIds)\n    {\n        RW(enforcedCollisionRefId, send, true);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldCollisionOverride.hpp",
    "content": "#ifndef OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP\n#define OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n\nnamespace mwmp\n{\n    class PacketWorldCollisionOverride : public WorldstatePacket\n    {\n    public:\n        PacketWorldCollisionOverride(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldDestinationOverride.cpp",
    "content": "#include \"PacketWorldDestinationOverride.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\n#include <components/openmw-mp/TimedLog.hpp>\n\nusing namespace mwmp;\n\nPacketWorldDestinationOverride::PacketWorldDestinationOverride(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_WORLD_DESTINATION_OVERRIDE;\n    orderChannel = CHANNEL_WORLDSTATE;\n}\n\nvoid PacketWorldDestinationOverride::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    uint32_t destinationCount;\n\n    if (send)\n        destinationCount = static_cast<uint32_t>(worldstate->destinationOverrides.size());\n\n    RW(destinationCount, send);\n\n    if (!send)\n    {\n        worldstate->destinationOverrides.clear();\n    }\n\n    std::string mapIndex;\n    std::string mapValue;\n\n    if (send)\n    {\n        for (auto &&destinationOverride : worldstate->destinationOverrides)\n        {\n            mapIndex = destinationOverride.first;\n            mapValue = destinationOverride.second;\n            RW(mapIndex, send, false);\n            RW(mapValue, send, false);\n        }\n    }\n    else\n    {\n        for (unsigned int n = 0; n < destinationCount; n++)\n        {\n            RW(mapIndex, send, false);\n            RW(mapValue, send, false);\n            worldstate->destinationOverrides[mapIndex] = mapValue;\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldDestinationOverride.hpp",
    "content": "#ifndef OPENMW_PACKETWORLDDESTINATIONOVERRIDE_HPP\n#define OPENMW_PACKETWORLDDESTINATIONOVERRIDE_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n\nnamespace mwmp\n{\n    class PacketWorldDestinationOverride : public WorldstatePacket\n    {\n    public:\n        PacketWorldDestinationOverride(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETWORLDDESTINATIONOVERRIDE_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldKillCount.cpp",
    "content": "#include \"PacketWorldKillCount.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketWorldKillCount::PacketWorldKillCount(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_WORLD_KILL_COUNT;\n    orderChannel = CHANNEL_SYSTEM;\n}\n\nvoid PacketWorldKillCount::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    uint32_t killChangesCount;\n\n    if (send)\n        killChangesCount = static_cast<uint32_t>(worldstate->killChanges.size());\n\n    RW(killChangesCount, send);\n\n    if (!send)\n    {\n        worldstate->killChanges.clear();\n        worldstate->killChanges.resize(killChangesCount);\n    }\n\n    for (auto &&killChange : worldstate->killChanges)\n    {\n        RW(killChange.refId, send, true);\n        RW(killChange.number, send);\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldKillCount.hpp",
    "content": "#ifndef OPENMW_PACKETWORLDKILLCOUNT_HPP\n#define OPENMW_PACKETWORLDKILLCOUNT_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nnamespace mwmp\n{\n    class PacketWorldKillCount: public WorldstatePacket\n    {\n    public:\n        PacketWorldKillCount(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETWORLDKILLCOUNT_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldMap.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <components/openmw-mp/TimedLog.hpp>\n#include \"PacketWorldMap.hpp\"\n\nusing namespace mwmp;\n\nPacketWorldMap::PacketWorldMap(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_WORLD_MAP;\n}\n\nvoid PacketWorldMap::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    uint32_t changesCount;\n\n    if (send)\n        changesCount = static_cast<uint32_t>(worldstate->mapTiles.size());\n\n    RW(changesCount, send);\n\n    if (!send)\n    {\n        worldstate->mapTiles.clear();\n        worldstate->mapTiles.resize(changesCount);\n    }\n\n    for (auto &&mapTile : worldstate->mapTiles)\n    {\n        RW(mapTile.x, send);\n        RW(mapTile.y, send);\n\n        uint32_t imageDataSize;\n\n        if (send)\n            imageDataSize = static_cast<uint32_t>(mapTile.imageData.size());\n\n        RW(imageDataSize, send);\n\n        if (imageDataSize > mwmp::maxImageDataSize)\n        {\n            LOG_MESSAGE_SIMPLE(TimedLog::LOG_ERROR, \"Processed invalid ID_WORLD_MAP packet where tile %i, %i had an imageDataSize of %i\",\n                mapTile.x, mapTile.y, imageDataSize);\n            LOG_APPEND(TimedLog::LOG_ERROR, \"- The packet was ignored after that point\");\n            return;\n        }\n\n        if (!send)\n        {\n            mapTile.imageData.clear();\n            mapTile.imageData.resize(imageDataSize);\n        }\n\n        for (auto &&imageChar : mapTile.imageData)\n        {\n            RW(imageChar, send);\n        }\n    }\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldMap.hpp",
    "content": "#ifndef OPENMW_PACKETWORLDMAP_HPP\n#define OPENMW_PACKETWORLDMAP_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n\nnamespace mwmp\n{\n    class PacketWorldMap : public WorldstatePacket\n    {\n    public:\n        PacketWorldMap(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETWORLDMAP_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldRegionAuthority.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketWorldRegionAuthority.hpp\"\n\nmwmp::PacketWorldRegionAuthority::PacketWorldRegionAuthority(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_WORLD_REGION_AUTHORITY;\n    // Make sure the priority is lower than PlayerCellChange's, so it doesn't get sent before it\n    priority = HIGH_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n}\n\nvoid mwmp::PacketWorldRegionAuthority::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    RW(worldstate->authorityRegion, send, true);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldRegionAuthority.hpp",
    "content": "#ifndef OPENMW_PACKETWORLDREGIONAUTHORITY_HPP\n#define OPENMW_PACKETWORLDREGIONAUTHORITY_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n\nnamespace mwmp\n{\n    class PacketWorldRegionAuthority : public WorldstatePacket\n    {\n    public:\n        PacketWorldRegionAuthority(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETWORLDREGIONAUTHORITY_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldTime.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include \"PacketWorldTime.hpp\"\n\nusing namespace mwmp;\n\nPacketWorldTime::PacketWorldTime(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_WORLD_TIME;\n    orderChannel = CHANNEL_WORLDSTATE;\n}\n\nvoid PacketWorldTime::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    RW(worldstate->time.hour, send);\n    RW(worldstate->time.day, send);\n    RW(worldstate->time.month, send);\n    RW(worldstate->time.year, send);\n\n    RW(worldstate->time.daysPassed, send);\n    RW(worldstate->time.timeScale, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldTime.hpp",
    "content": "#ifndef OPENMW_PACKETWORLDTIME_HPP\n#define OPENMW_PACKETWORLDTIME_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n\nnamespace mwmp\n{\n    class PacketWorldTime : public WorldstatePacket\n    {\n    public:\n        PacketWorldTime(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETWORLDTIME_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldWeather.cpp",
    "content": "#include \"PacketWorldWeather.hpp\"\n#include <components/openmw-mp/NetworkMessages.hpp>\n\nusing namespace mwmp;\n\nPacketWorldWeather::PacketWorldWeather(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)\n{\n    packetID = ID_WORLD_WEATHER;\n    orderChannel = CHANNEL_WORLDSTATE;\n}\n\nvoid PacketWorldWeather::Packet(RakNet::BitStream *newBitstream, bool send)\n{\n    WorldstatePacket::Packet(newBitstream, send);\n\n    RW(worldstate->forceWeather, send);\n    RW(worldstate->weather.region, send, true);\n    RW(worldstate->weather.currentWeather, send);\n    RW(worldstate->weather.nextWeather, send);\n    RW(worldstate->weather.queuedWeather, send);\n    RW(worldstate->weather.transitionFactor, send);\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/PacketWorldWeather.hpp",
    "content": "#ifndef OPENMW_PACKETWORLDWEATHER_HPP\n#define OPENMW_PACKETWORLDWEATHER_HPP\n\n#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>\n\nnamespace mwmp\n{\n    class PacketWorldWeather : public WorldstatePacket\n    {\n    public:\n        PacketWorldWeather(RakNet::RakPeerInterface *peer);\n\n        virtual void Packet(RakNet::BitStream *newBitstream, bool send);\n    };\n}\n\n#endif //OPENMW_PACKETWORLDWEATHER_HPP\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/WorldstatePacket.cpp",
    "content": "#include <components/openmw-mp/NetworkMessages.hpp>\n#include <PacketPriority.h>\n#include <RakPeer.h>\n#include \"WorldstatePacket.hpp\"\n\nusing namespace mwmp;\n\nWorldstatePacket::WorldstatePacket(RakNet::RakPeerInterface *peer) : BasePacket(peer)\n{\n    packetID = 0;\n    priority = HIGH_PRIORITY;\n    reliability = RELIABLE_ORDERED;\n    orderChannel = CHANNEL_WORLDSTATE;\n    this->peer = peer;\n}\n\nWorldstatePacket::~WorldstatePacket()\n{\n\n}\n\nvoid WorldstatePacket::setWorldstate(BaseWorldstate *newWorldstate)\n{\n    worldstate = newWorldstate;\n    guid = worldstate->guid;\n}\n\nBaseWorldstate *WorldstatePacket::getWorldstate()\n{\n    return worldstate;\n}\n"
  },
  {
    "path": "components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp",
    "content": "#ifndef OPENMW_WORLDSTATEPACKET_HPP\n#define OPENMW_WORLDSTATEPACKET_HPP\n\n#include <string>\n#include <RakNetTypes.h>\n#include <BitStream.h>\n#include <PacketPriority.h>\n#include <components/openmw-mp/Base/BaseWorldstate.hpp>\n\n#include <components/openmw-mp/Packets/BasePacket.hpp>\n\nnamespace mwmp\n{\n    class WorldstatePacket : public BasePacket\n    {\n    public:\n        WorldstatePacket(RakNet::RakPeerInterface *peer);\n\n        ~WorldstatePacket();\n\n        void setWorldstate(BaseWorldstate *newWorldstate);\n        BaseWorldstate *getWorldstate();\n\n    protected:\n        BaseWorldstate *worldstate;\n\n    };\n}\n\n#endif //OPENMW_WORLDSTATEPACKET_HPP\n"
  },
  {
    "path": "components/openmw-mp/TimedLog.cpp",
    "content": "#include <cstdarg>\n#include <iostream>\n#include <cstring>\n#include <ctime>\n#include <cstdio>\n#include <sstream>\n#include <vector>\n#include <boost/lexical_cast.hpp>\n#include \"TimedLog.hpp\"\n\nTimedLog *TimedLog::sTimedLog = nullptr;\n\nTimedLog::TimedLog(int logLevel) : logLevel(logLevel)\n{\n\n}\n\nvoid TimedLog::Create(int logLevel)\n{\n    if (sTimedLog != nullptr)\n        return;\n    sTimedLog = new TimedLog(logLevel);\n}\n\nvoid TimedLog::Delete()\n{\n    if (sTimedLog == nullptr)\n        return;\n    delete sTimedLog;\n    sTimedLog = nullptr;\n}\n\nconst TimedLog &TimedLog::Get()\n{\n    return *sTimedLog;\n}\n\nint TimedLog::GetLevel()\n{\n    return sTimedLog->logLevel;\n}\n\nvoid TimedLog::SetLevel(int level)\n{\n    sTimedLog->logLevel = level;\n}\n\nconst char* getTime()\n{\n    time_t t = time(0);\n    struct tm *tm = localtime(&t);\n    static char result[20];\n    sprintf(result, \"%.4d-%.2d-%.2d %.2d:%.2d:%.2d\",\n            1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,\n            tm->tm_hour, tm->tm_min, tm->tm_sec);\n    return result;\n}\n\nvoid TimedLog::print(int level, bool hasPrefix, const char *file, int line, const char *message, ...) const\n{\n    if (level < logLevel) return;\n    std::stringstream sstr;\n\n    if (hasPrefix)\n    {\n\n        sstr << \"[\" << getTime() << \"] \";\n\n        if (file != 0 && line != 0)\n        {\n            sstr << \"[\" << file << \":\";\n            sstr << line << \"] \";\n        }\n\n        sstr << \"[\";\n        switch (level)\n        {\n        case LOG_WARN:\n            sstr << \"WARN\";\n            break;\n        case LOG_ERROR:\n            sstr << \"ERR\";\n            break;\n        case LOG_FATAL:\n            sstr << \"FATAL\";\n            break;\n        default:\n            sstr << \"INFO\";\n        }\n        sstr << \"]: \";\n\n    }\n\n    sstr << message;\n    char back = *sstr.str().rbegin();\n    if (back != '\\n')\n        sstr << '\\n';\n    va_list args;\n    va_start(args, message);\n    std::vector<char> buf((unsigned long) (vsnprintf(nullptr, 0, sstr.str().c_str(), args) + 1));\n    va_end(args);\n    va_start(args, message);\n    vsnprintf(buf.data(), buf.size(), sstr.str().c_str(), args);\n    va_end(args);\n    std::cout << buf.data() << std::flush;\n}\n\nstd::string TimedLog::getFilenameTimestamp()\n{\n    time_t rawtime = time(0);\n    struct tm *timeinfo = localtime(&rawtime);\n    char buffer[25];\n    strftime(buffer, 25, \"%Y-%m-%d-%H_%M_%S\", timeinfo);\n    std::string timestamp(buffer);\n    return timestamp;\n}\n"
  },
  {
    "path": "components/openmw-mp/TimedLog.hpp",
    "content": "#ifndef OPENMW_LOG_HPP\n#define OPENMW_LOG_HPP\n\n#include <boost/filesystem.hpp>\n\n#ifdef __GNUC__\n#pragma GCC system_header\n#endif\n\n#if defined(NOLOGS)\n#define LOG_INIT(logLevel)\n#define LOG_QUIT()\n#define LOG_MESSAGE(level, msg, ...)\n#define LOG_MESSAGE_SIMPLE(level, msg, ...)\n#else\n#define LOG_INIT(logLevel) TimedLog::Create(logLevel)\n#define LOG_QUIT() TimedLog::Delete()\n#if defined(_MSC_VER)\n#define LOG_MESSAGE(level, msg, ...) TimedLog::Get().print((level), (1), (__FILE__), (__LINE__), (msg), __VA_ARGS__)\n#define LOG_MESSAGE_SIMPLE(level, msg, ...) TimedLog::Get().print((level), (1), (0), (0), (msg), __VA_ARGS__)\n#define LOG_APPEND(level, msg, ...) TimedLog::Get().print((level), (0), (0), (0), (msg), __VA_ARGS__)\n#else\n#define LOG_MESSAGE(level, msg, args...) TimedLog::Get().print((level), (1), (__FILE__), (__LINE__), (msg), ##args)\n#define LOG_MESSAGE_SIMPLE(level, msg, args...) TimedLog::Get().print((level), (1), (0), (0), (msg), ##args)\n#define LOG_APPEND(level, msg, args...) TimedLog::Get().print((level), (0), (0), (0), (msg), ##args)\n#endif\n#endif\n\nclass TimedLog\n{\npublic:\n    enum\n    {\n        LOG_VERBOSE = 0,\n        LOG_INFO,\n        LOG_WARN,\n        LOG_ERROR,\n        LOG_FATAL\n    };\n    static void Create(int logLevel);\n    static void Delete();\n    static const TimedLog &Get();\n    static int GetLevel();\n    static void SetLevel(int level);\n    void print(int level, bool hasPrefix, const char *file, int line, const char *message, ...) const;\n\n    static std::string getFilenameTimestamp();\nprivate:\n    TimedLog(int logLevel);\n    /// Not implemented\n    TimedLog(const TimedLog &) = delete;\n    /// Not implemented\n    TimedLog &operator=(TimedLog &) = delete;\n    static TimedLog *sTimedLog;\n    int logLevel;\n};\n\n\n#endif //OPENMW_LOG_HPP\n"
  },
  {
    "path": "components/openmw-mp/Utils.cpp",
    "content": "#include \"Utils.hpp\"\n\n#include <cstdio>\n#include <cstring>\n#include <ctime>\n#include <cmath>\n#include <memory>\n#include <iostream>\n#include <sstream>\n#include <boost/crc.hpp>\n#include <boost/filesystem/fstream.hpp>\n#include <iomanip>\n\n#ifdef _WIN32\nint setenv(const char *name, const char *value, int overwrite)\n{\n    printf(\"%s: %s\\n\", name, value);\n    return _putenv_s(name, value);\n}\n#endif\n\n\nstd::string Utils::convertPath(std::string str)\n{\n#if defined(_WIN32)\n#define _SEP_ '\\\\'\n#elif defined(__APPLE__)\n#define _SEP_ ':'\n#endif\n\n#if defined(_WIN32) || defined(__APPLE__)\n    replace(str.begin(), str.end(), '/', _SEP_);\n#endif //defined(_WIN32) || defined(__APPLE__)\n    return str;\n\n#undef _SEP_\n}\n\nbool Utils::doesFileHaveChecksum(std::string filePath, unsigned int requiredChecksum)\n{\n    unsigned int fileChecksum = crc32Checksum(filePath);\n\n    if (fileChecksum == requiredChecksum)\n        return true;\n\n    return false;\n}\n\nvoid Utils::timestamp()\n{\n    time_t ltime;\n    ltime = time(0);\n    char t[32];\n    snprintf(t, sizeof(t), \"[%s\", asctime(localtime(&ltime)));\n    char* newline = strchr(t, '\\n');\n    *newline = ']';\n    strcat(t, \" \");\n    printf(\"%s\", t);\n}\n\n// Based on http://stackoverflow.com/questions/1637587/c-libcurl-console-progress-bar\nint Utils::progressFunc(double TotalToDownload, double NowDownloaded)\n{\n    // how wide you want the progress meter to be\n    int totaldotz=40;\n    double fractiondownloaded = NowDownloaded / TotalToDownload;\n    // part of the progressmeter that's already \"full\"\n    int dotz = round(fractiondownloaded * totaldotz);\n\n    // create the \"meter\"\n    int ii=0;\n    printf(\"%3.0f%% [\",fractiondownloaded*100);\n    // part  that's full already\n    for ( ; ii < dotz;ii++) {\n        printf(\"=\");\n    }\n    // remaining part (spaces)\n    for ( ; ii < totaldotz;ii++) {\n        printf(\" \");\n    }\n    // and back to line begin - do not forget the fflush to avoid output buffering problems!\n    printf(\"]\\r\");\n    fflush(stdout);\n    return 1;\n}\n\nbool Utils::compareDoubles(double a, double b, double epsilon)\n{\n    return fabs(a - b) < epsilon;\n}\n\nbool Utils::compareFloats(float a, float b, float epsilon)\n{\n    return fabs(a - b) < epsilon;\n}\n\n// Based on https://stackoverflow.com/a/1489873\nunsigned int Utils::getNumberOfDigits(int integer)\n{\n    int digits = 0;\n    if (integer < 0) digits = 1;\n    while (integer) {\n        integer /= 10;\n        digits++;\n    }\n    return digits;\n}\n\n\n\nstd::string Utils::toString(int num)\n{\n    std::ostringstream stream;\n    stream << num;\n    return stream.str();\n}\n\nstd::string Utils::replaceString(const std::string& source, const char* find, const char* replace)\n{\n    unsigned int find_len = strlen(find);\n    unsigned int replace_len = strlen(replace);\n    size_t pos = 0;\n\n    std::string dest = source;\n\n    while ((pos = dest.find(find, pos)) != std::string::npos)\n    {\n        dest.replace(pos, find_len, replace);\n        pos += replace_len;\n    }\n\n    return dest;\n}\n\nstd::string& Utils::removeExtension(std::string& file)\n{\n    size_t pos = file.find_last_of('.');\n\n    if (pos)\n        file = file.substr(0, pos);\n\n    return file;\n}\n\nlong int Utils::getFileLength(const char* file)\n{\n    FILE* _file = fopen(file, \"rb\");\n\n    if (!_file)\n        return 0;\n\n    fseek(_file, 0, SEEK_END);\n    long int size = ftell(_file);\n    fclose(_file);\n\n    return size;\n}\n\nunsigned int ::Utils::crc32Checksum(const std::string &file)\n{\n    boost::crc_32_type  crc32;\n    boost::filesystem::ifstream  ifs(file, std::ios_base::binary);\n    if (ifs)\n    {\n        do\n        {\n            char buffer[1024];\n\n            ifs.read(buffer, 1024);\n            crc32.process_bytes( buffer, ifs.gcount() );\n        } while (ifs);\n    }\n    return crc32.checksum();\n}\n\nstd::string Utils::getOperatingSystemType()\n{\n#if defined(_WIN32)\n    return \"Windows\";\n#elif defined(__linux)\n    return \"Linux\";\n#elif defined(__APPLE__)\n    return \"OS X\";\n#else\n    return \"Unknown OS\";\n#endif\n}\n\nstd::string Utils::getArchitectureType()\n{\n#if defined(__x86_64__) || defined(_M_X64)\n    return \"64-bit\";\n#elif defined(__i386__) || defined(_M_I86) || defined(_M_IX86)\n    return \"32-bit\";\n#elif defined(__ARM_ARCH)\n    std::string architectureType = \"ARMv\" + __ARM_ARCH;\n#ifdef __aarch64__\n    architectureType = architectureType + \" 64-bit\";\n#else\n    architectureType = architectureType + \" 32-bit\";\n#endif\n    return architectureType;\n#else\n    return \"Unknown architecture\";\n#endif\n}\n\nstd::string Utils::getVersionInfo(std::string appName, std::string version, std::string commitHash, int protocol)\n{\n    std::stringstream stream;\n\n    stream << appName << \" \" << version << \" (\" << getOperatingSystemType() << \" \" << getArchitectureType() << \")\" << std::endl;\n    stream << \"Protocol version: \" << protocol << std::endl;\n    stream << \"Oldest compatible commit hash: \" << commitHash.substr(0, 10) << std::endl;\n    stream << \"------------------------------------------------------------\" << std::endl;\n\n    return stream.str();\n}\n\nvoid Utils::printWithWidth(std::ostringstream &sstr, std::string str, size_t width)\n{\n    sstr << std::left << std::setw(width) << std::setfill(' ') << str;\n}\n\nstd::string Utils::intToHexStr(unsigned val)\n{\n    std::ostringstream sstr;\n    sstr << \"0x\" << std::setfill('0') << std::setw(8) << std::uppercase << std::hex << val;\n    return sstr.str();\n}\n\nunsigned int Utils::hexStrToInt(std::string hexString)\n{\n    unsigned int intValue;\n    sscanf(hexString.c_str(), \"%x\", &intValue);\n    return intValue;\n}\n"
  },
  {
    "path": "components/openmw-mp/Utils.hpp",
    "content": "#ifndef UTILS_HPP\n#define UTILS_HPP\n\n#include <algorithm>\n#include <cstdint>\n#include <string>\n#include <sstream>\n#include <vector>\n\n#if (defined __WIN32__ || defined _WIN32 || defined WIN32)\n#define __WINDOWS\n#endif\n\n#ifdef __WINDOWS\nint setenv(const char *name, const char *value, int overwrite);\n#endif\n\nnamespace Utils\n{\n    std::string convertPath(std::string str);\n    bool doesFileHaveChecksum(std::string filePath, unsigned int requiredChecksum);\n\n    void timestamp();\n\n    int progressFunc(double TotalToDownload, double NowDownloaded);\n\n    unsigned int getNumberOfDigits(int integer);\n    bool compareDoubles(double a, double b, double epsilon = 0.005);\n    bool compareFloats(float a, float b, float epsilon = 0.005f);\n\n    template <class Type, class Type2>\n    bool vectorContains(const std::vector<Type> &vectorChecked, const Type2 &value)\n    {\n        return std::find(vectorChecked.begin(), vectorChecked.end(), value) != vectorChecked.end();\n    }\n\n    template <class Type>\n    uint32_t getVectorSize(const std::vector<Type> &vectorChecked)\n    {\n        return static_cast<uint32_t>(vectorChecked.size());\n    }\n\n    template <class Type>\n    void resetVector(std::vector<Type> &vectorInput, uint32_t newSize)\n    {\n        vectorInput.clear();\n        vectorInput.resize(newSize);\n    }\n\n    std::string replaceString(const std::string &source, const char *find, const char *replace);\n\n    std::string toString(int num);\n\n    std::string &removeExtension(std::string &file);\n\n    long int getFileLength(const char *file);\n\n    unsigned int crc32Checksum(const std::string &file);\n\n    std::string getOperatingSystemType();\n    std::string getArchitectureType();\n\n    std::string getVersionInfo(std::string appName, std::string version, std::string commitHash, int protocol);\n\n    void printWithWidth(std::ostringstream &sstr, std::string str, size_t width);\n\n    std::string intToHexStr(unsigned val);\n    unsigned int hexStrToInt(std::string hexString);\n}\n#endif //UTILS_HPP\n"
  },
  {
    "path": "components/openmw-mp/Version.hpp",
    "content": "#ifndef OPENMW_VERSION_HPP\n#define OPENMW_VERSION_HPP\n\n#define TES3MP_VERSION \"0.8.1\"\n#define TES3MP_PROTO_VERSION 10\n\n#define TES3MP_DEFAULT_PASSW \"blankpassword\"\n#define TES3MP_MASTERSERVER_PASSW \"12345\"\n\n\n#endif //OPENMW_VERSION_HPP\n"
  },
  {
    "path": "components/process/processinvoker.cpp",
    "content": "#include \"processinvoker.hpp\"\n\n#include <QMessageBox>\n#include <QStringList>\n#include <QString>\n#include <QDir>\n#include <QDebug>\n#include <QCoreApplication>\n\nProcess::ProcessInvoker::ProcessInvoker()\n{\n    mProcess = new QProcess(this);\n\n    connect(mProcess, SIGNAL(error(QProcess::ProcessError)),\n            this, SLOT(processError(QProcess::ProcessError)));\n\n    connect(mProcess, SIGNAL(finished(int,QProcess::ExitStatus)),\n            this, SLOT(processFinished(int,QProcess::ExitStatus)));\n\n\n    mName = QString();\n    mArguments = QStringList();\n}\n\nProcess::ProcessInvoker::~ProcessInvoker()\n{\n}\n\n//void Process::ProcessInvoker::setProcessName(const QString &name)\n//{\n//    mName = name;\n//}\n\n//void Process::ProcessInvoker::setProcessArguments(const QStringList &arguments)\n//{\n//    mArguments = arguments;\n//}\n\nQProcess* Process::ProcessInvoker::getProcess()\n{\n    return mProcess;\n}\n\n//QString Process::ProcessInvoker::getProcessName()\n//{\n//    return mName;\n//}\n\n//QStringList Process::ProcessInvoker::getProcessArguments()\n//{\n//    return mArguments;\n//}\n\nbool Process::ProcessInvoker::startProcess(const QString &name, const QStringList &arguments, bool detached)\n{\n//    mProcess = new QProcess(this);\n    mName = name;\n    mArguments = arguments;\n\n    QString path(name);\n#ifdef Q_OS_WIN\n    path.append(QLatin1String(\".exe\"));\n#elif defined(Q_OS_MAC)\n    QDir dir(QCoreApplication::applicationDirPath());\n    path = dir.absoluteFilePath(name);\n#else\n    path.prepend(QLatin1String(\"./\"));\n#endif\n\n    QFileInfo info(path);\n\n    if (!info.exists()) {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error starting executable\"));\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<html><head/><body><p><b>Could not find %1</b></p> \\\n                          <p>The application is not found.</p> \\\n                          <p>Please make sure OpenMW is installed correctly and try again.</p></body></html>\").arg(info.fileName()));\n        msgBox.exec();\n        return false;\n    }\n\n    if (!info.isExecutable()) {\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error starting executable\"));\n        msgBox.setIcon(QMessageBox::Warning);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<html><head/><body><p><b>Could not start %1</b></p> \\\n                          <p>The application is not executable.</p> \\\n                          <p>Please make sure you have the right permissions and try again.</p></body></html>\").arg(info.fileName()));\n        msgBox.exec();\n        return false;\n    }\n\n    // Start the executable\n    if (detached) {\n        if (!mProcess->startDetached(path, arguments)) {\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error starting executable\"));\n            msgBox.setIcon(QMessageBox::Critical);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(tr(\"<html><head/><body><p><b>Could not start %1</b></p> \\\n                              <p>An error occurred while starting %1.</p> \\\n                              <p>Press \\\"Show Details...\\\" for more information.</p></body></html>\").arg(info.fileName()));\n            msgBox.setDetailedText(mProcess->errorString());\n            msgBox.exec();\n            return false;\n        }\n    } else {\n        mProcess->start(path, arguments);\n\n        /*\n        if (!mProcess->waitForFinished()) {\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error starting executable\"));\n            msgBox.setIcon(QMessageBox::Critical);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(tr(\"<html><head/><body><p><b>Could not start %1</b></p> \\\n                              <p>An error occurred while starting %1.</p> \\\n                              <p>Press \\\"Show Details...\\\" for more information.</p></body></html>\").arg(info.fileName()));\n            msgBox.setDetailedText(mProcess->errorString());\n            msgBox.exec();\n\n            return false;\n        }\n\n        if (mProcess->exitCode() != 0 || mProcess->exitStatus() == QProcess::CrashExit) {\n            QString error(mProcess->readAllStandardError());\n            error.append(tr(\"\\nArguments:\\n\"));\n            error.append(arguments.join(\" \"));\n\n            QMessageBox msgBox;\n            msgBox.setWindowTitle(tr(\"Error running executable\"));\n            msgBox.setIcon(QMessageBox::Critical);\n            msgBox.setStandardButtons(QMessageBox::Ok);\n            msgBox.setText(tr(\"<html><head/><body><p><b>Executable %1 returned an error</b></p> \\\n                              <p>An error occurred while running %1.</p> \\\n                              <p>Press \\\"Show Details...\\\" for more information.</p></body></html>\").arg(info.fileName()));\n            msgBox.setDetailedText(error);\n            msgBox.exec();\n\n            return false;\n        }\n        */\n    }\n\n    return true;\n\n}\n\nvoid Process::ProcessInvoker::processError(QProcess::ProcessError error)\n{\n    QMessageBox msgBox;\n    msgBox.setWindowTitle(tr(\"Error running executable\"));\n    msgBox.setIcon(QMessageBox::Critical);\n    msgBox.setStandardButtons(QMessageBox::Ok);\n    msgBox.setText(tr(\"<html><head/><body><p><b>Executable %1 returned an error</b></p> \\\n                      <p>An error occurred while running %1.</p> \\\n                      <p>Press \\\"Show Details...\\\" for more information.</p></body></html>\").arg(mName));\n    msgBox.setDetailedText(mProcess->errorString());\n    msgBox.exec();\n\n}\n\nvoid Process::ProcessInvoker::processFinished(int exitCode, QProcess::ExitStatus exitStatus)\n{\n    if (exitCode != 0 || exitStatus == QProcess::CrashExit) {\n        QString error(mProcess->readAllStandardError());\n        error.append(tr(\"\\nArguments:\\n\"));\n        error.append(mArguments.join(\" \"));\n\n        QMessageBox msgBox;\n        msgBox.setWindowTitle(tr(\"Error running executable\"));\n        msgBox.setIcon(QMessageBox::Critical);\n        msgBox.setStandardButtons(QMessageBox::Ok);\n        msgBox.setText(tr(\"<html><head/><body><p><b>Executable %1 returned an error</b></p> \\\n                          <p>An error occurred while running %1.</p> \\\n                          <p>Press \\\"Show Details...\\\" for more information.</p></body></html>\").arg(mName));\n        msgBox.setDetailedText(error);\n        msgBox.exec();\n    }\n}\n"
  },
  {
    "path": "components/process/processinvoker.hpp",
    "content": "#ifndef PROCESSINVOKER_HPP\n#define PROCESSINVOKER_HPP\n\n#include <QStringList>\n#include <QString>\n#include <QProcess>\n\nnamespace Process\n{\n    class ProcessInvoker : public QObject\n    {\n        Q_OBJECT\n\n    public:\n\n        ProcessInvoker();\n        ~ProcessInvoker();\n\n//        void setProcessName(const QString &name);\n//        void setProcessArguments(const QStringList &arguments);\n\n        QProcess* getProcess();\n//        QString getProcessName();\n//        QStringList getProcessArguments();\n\n//        inline bool startProcess(bool detached = false) { return startProcess(mName, mArguments, detached); }\n        inline bool startProcess(const QString &name, bool detached = false) { return startProcess(name, QStringList(), detached); }\n        bool startProcess(const QString &name, const QStringList &arguments, bool detached = false);\n\n    private:\n        QProcess *mProcess;\n\n        QString mName;\n        QStringList mArguments;\n\n    private slots:\n        void processError(QProcess::ProcessError error);\n        void processFinished(int exitCode, QProcess::ExitStatus exitStatus);\n\n    };\n}\n\n#endif // PROCESSINVOKER_HPP\n"
  },
  {
    "path": "components/resource/animation.cpp",
    "content": "#include <components/resource/animation.hpp>\n\n#include <osg/ref_ptr>\n#include <osgAnimation/Channel>\n\nnamespace Resource\n{\n    Animation::Animation(const Animation& anim, const osg::CopyOp& copyop): osg::Object(anim, copyop),\n        mDuration(0.0f),\n        mStartTime(0.0f)\n    {\n        const osgAnimation::ChannelList& channels = anim.getChannels();\n        for (const auto& channel: channels)\n            addChannel(channel.get()->clone());\n    }\n\n    void Animation::addChannel(osg::ref_ptr<osgAnimation::Channel> pChannel)\n    {\n        mChannels.push_back(pChannel);\n    }\n\n    std::vector<osg::ref_ptr<osgAnimation::Channel>>& Animation::getChannels()\n    {\n        return mChannels;\n    }\n\n    const std::vector<osg::ref_ptr<osgAnimation::Channel>>& Animation::getChannels() const\n    {\n        return mChannels;\n    }\n\n    bool Animation::update (double time)\n    {\n        for (const auto& channel: mChannels)\n        {\n            channel->update(time, 1.0f, 0);\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "components/resource/animation.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_ANIMATION_HPP\n#define OPENMW_COMPONENTS_RESOURCE_ANIMATION_HPP\n\n#include <vector>\n\n#include <osg/Node>\n#include <osg/Object>\n#include <osgAnimation/Channel>\n\nnamespace Resource\n{\n    /// Stripped down class of osgAnimation::Animation, only needed for OSG's plugin formats like dae\n    class Animation : public osg::Object\n    {\n        public:\n            META_Object(Resource, Animation)\n\n            Animation() :\n                mDuration(0.0), mStartTime(0) {}\n\n            Animation(const Animation&, const osg::CopyOp&);\n            ~Animation() {}\n\n            void addChannel (osg::ref_ptr<osgAnimation::Channel> pChannel);\n\n            std::vector<osg::ref_ptr<osgAnimation::Channel>>& getChannels();\n\n            const std::vector<osg::ref_ptr<osgAnimation::Channel>>& getChannels() const;\n\n            bool update (double time);\n\n        protected:\n            double mDuration;\n            double mStartTime;\n            std::vector<osg::ref_ptr<osgAnimation::Channel>> mChannels;\n        };\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/bulletshape.cpp",
    "content": "#include \"bulletshape.hpp\"\n\n#include <stdexcept>\n#include <string>\n\n#include <BulletCollision/CollisionShapes/btBoxShape.h>\n#include <BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h>\n#include <BulletCollision/CollisionShapes/btCompoundShape.h>\n#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>\n\nnamespace Resource\n{\n\nBulletShape::BulletShape()\n    : mCollisionShape(nullptr)\n    , mAvoidCollisionShape(nullptr)\n{\n\n}\n\nBulletShape::BulletShape(const BulletShape &copy, const osg::CopyOp &copyop)\n    : mCollisionShape(duplicateCollisionShape(copy.mCollisionShape))\n    , mAvoidCollisionShape(duplicateCollisionShape(copy.mAvoidCollisionShape))\n    , mCollisionBox(copy.mCollisionBox)\n    , mAnimatedShapes(copy.mAnimatedShapes)\n{\n}\n\nBulletShape::~BulletShape()\n{\n    deleteShape(mAvoidCollisionShape);\n    deleteShape(mCollisionShape);\n}\n\nvoid BulletShape::deleteShape(btCollisionShape* shape)\n{\n    if(shape!=nullptr)\n    {\n        if(shape->isCompound())\n        {\n            btCompoundShape* ms = static_cast<btCompoundShape*>(shape);\n            int a = ms->getNumChildShapes();\n            for(int i=0; i <a;i++)\n                deleteShape(ms->getChildShape(i));\n        }\n        delete shape;\n    }\n}\n\nbtCollisionShape* BulletShape::duplicateCollisionShape(const btCollisionShape *shape) const\n{\n    if(shape->isCompound())\n    {\n        const btCompoundShape *comp = static_cast<const btCompoundShape*>(shape);\n        btCompoundShape *newShape = new btCompoundShape;\n\n        int numShapes = comp->getNumChildShapes();\n        for(int i = 0;i < numShapes;++i)\n        {\n            btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i));\n            btTransform trans = comp->getChildTransform(i);\n            newShape->addChildShape(trans, child);\n        }\n\n        return newShape;\n    }\n\n    if(const btBvhTriangleMeshShape* trishape = dynamic_cast<const btBvhTriangleMeshShape*>(shape))\n    {\n        btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(const_cast<btBvhTriangleMeshShape*>(trishape), btVector3(1.f, 1.f, 1.f));\n        return newShape;\n    }\n\n    if (const btBoxShape* boxshape = dynamic_cast<const btBoxShape*>(shape))\n    {\n        return new btBoxShape(*boxshape);\n    }\n\n    if (shape->getShapeType() == TERRAIN_SHAPE_PROXYTYPE)\n        return new btHeightfieldTerrainShape(static_cast<const btHeightfieldTerrainShape&>(*shape));\n\n    throw std::logic_error(std::string(\"Unhandled Bullet shape duplication: \")+shape->getName());\n}\n\nbtCollisionShape *BulletShape::getCollisionShape() const\n{\n    return mCollisionShape;\n}\n\nbtCollisionShape *BulletShape::getAvoidCollisionShape() const\n{\n    return mAvoidCollisionShape;\n}\n\nvoid BulletShape::setLocalScaling(const btVector3& scale)\n{\n    mCollisionShape->setLocalScaling(scale);\n    if (mAvoidCollisionShape)\n        mAvoidCollisionShape->setLocalScaling(scale);\n}\n\nosg::ref_ptr<BulletShapeInstance> BulletShape::makeInstance() const\n{\n    osg::ref_ptr<BulletShapeInstance> instance (new BulletShapeInstance(this));\n    return instance;\n}\n\nBulletShapeInstance::BulletShapeInstance(osg::ref_ptr<const BulletShape> source)\n    : BulletShape()\n    , mSource(source)\n{\n    mCollisionBox = source->mCollisionBox;\n\n    mAnimatedShapes = source->mAnimatedShapes;\n\n    if (source->mCollisionShape)\n        mCollisionShape = duplicateCollisionShape(source->mCollisionShape);\n\n    if (source->mAvoidCollisionShape)\n        mAvoidCollisionShape = duplicateCollisionShape(source->mAvoidCollisionShape);\n}\n\n}\n"
  },
  {
    "path": "components/resource/bulletshape.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H\n#define OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H\n\n#include <map>\n\n#include <osg/Object>\n#include <osg/ref_ptr>\n#include <osg/Vec3f>\n\n#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>\n\nclass btCollisionShape;\n\nnamespace Resource\n{\n\n    class BulletShapeInstance;\n    class BulletShape : public osg::Object\n    {\n    public:\n        BulletShape();\n        BulletShape(const BulletShape& copy, const osg::CopyOp& copyop);\n        virtual ~BulletShape();\n\n        META_Object(Resource, BulletShape)\n\n        btCollisionShape* mCollisionShape;\n        btCollisionShape* mAvoidCollisionShape;\n\n        struct CollisionBox\n        {\n            osg::Vec3f extents;\n            osg::Vec3f center;\n        };\n        // Used for actors and projectiles. mCollisionShape is used for actors only when we need to autogenerate collision box for creatures.\n        // For now, use one file <-> one resource for simplicity.\n        CollisionBox mCollisionBox;\n\n        // Stores animated collision shapes. If any collision nodes in the NIF are animated, then mCollisionShape\n        // will be a btCompoundShape (which consists of one or more child shapes).\n        // In this map, for each animated collision shape,\n        // we store the node's record index mapped to the child index of the shape in the btCompoundShape.\n        std::map<int, int> mAnimatedShapes;\n\n        osg::ref_ptr<BulletShapeInstance> makeInstance() const;\n\n        btCollisionShape* duplicateCollisionShape(const btCollisionShape* shape) const;\n\n        btCollisionShape* getCollisionShape() const;\n\n        btCollisionShape* getAvoidCollisionShape() const;\n\n        void setLocalScaling(const btVector3& scale);\n\n    private:\n\n        void deleteShape(btCollisionShape* shape);\n    };\n\n\n    // An instance of a BulletShape that may have its own unique scaling set on the mCollisionShape.\n    // Vertex data is shallow-copied where possible. A ref_ptr to the original shape is held to keep vertex pointers intact.\n    class BulletShapeInstance : public BulletShape\n    {\n    public:\n        BulletShapeInstance(osg::ref_ptr<const BulletShape> source);\n\n    private:\n        osg::ref_ptr<const BulletShape> mSource;\n    };\n\n    // Subclass btBhvTriangleMeshShape to auto-delete the meshInterface\n    struct TriangleMeshShape : public btBvhTriangleMeshShape\n    {\n        TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression, bool buildBvh = true)\n            : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression, buildBvh)\n        {\n        }\n\n        virtual ~TriangleMeshShape()\n        {\n            delete getTriangleInfoMap();\n            delete m_meshInterface;\n        }\n    };\n\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/bulletshapemanager.cpp",
    "content": "#include \"bulletshapemanager.hpp\"\n\n#include <osg/NodeVisitor>\n#include <osg/TriangleFunctor>\n#include <osg/Transform>\n#include <osg/Drawable>\n#include <osg/Version>\n\n#include <BulletCollision/CollisionShapes/btTriangleMesh.h>\n\n#include <components/sceneutil/visitor.hpp>\n#include <components/vfs/manager.hpp>\n\n#include <components/nifbullet/bulletnifloader.hpp>\n\n#include \"bulletshape.hpp\"\n#include \"scenemanager.hpp\"\n#include \"niffilemanager.hpp\"\n#include \"objectcache.hpp\"\n#include \"multiobjectcache.hpp\"\n\nnamespace Resource\n{\n\nstruct GetTriangleFunctor\n{\n    GetTriangleFunctor()\n        : mTriMesh(nullptr)\n    {\n    }\n\n    void setTriMesh(btTriangleMesh* triMesh)\n    {\n        mTriMesh = triMesh;\n    }\n\n    void setMatrix(const osg::Matrixf& matrix)\n    {\n        mMatrix = matrix;\n    }\n\n    inline btVector3 toBullet(const osg::Vec3f& vec)\n    {\n        return btVector3(vec.x(), vec.y(), vec.z());\n    }\n\n#if OSG_MIN_VERSION_REQUIRED(3,5,6)\n    void inline operator()( const osg::Vec3 v1, const osg::Vec3 v2, const osg::Vec3 v3 )\n#else\n    void inline operator()( const osg::Vec3 v1, const osg::Vec3 v2, const osg::Vec3 v3, bool _temp )\n#endif\n    {\n        if (mTriMesh)\n            mTriMesh->addTriangle( toBullet(mMatrix.preMult(v1)), toBullet(mMatrix.preMult(v2)), toBullet(mMatrix.preMult(v3)));\n    }\n\n    btTriangleMesh* mTriMesh;\n    osg::Matrixf mMatrix;\n};\n\n/// Creates a BulletShape out of a Node hierarchy.\nclass NodeToShapeVisitor : public osg::NodeVisitor\n{\npublic:\n    NodeToShapeVisitor()\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        , mTriangleMesh(nullptr)\n    {\n\n    }\n\n    void apply(osg::Drawable &drawable) override\n    {\n        if (!mTriangleMesh)\n            mTriangleMesh.reset(new btTriangleMesh);\n\n        osg::Matrixf worldMat = osg::computeLocalToWorld(getNodePath());\n        osg::TriangleFunctor<GetTriangleFunctor> functor;\n        functor.setTriMesh(mTriangleMesh.get());\n        functor.setMatrix(worldMat);\n        drawable.accept(functor);\n    }\n\n    osg::ref_ptr<BulletShape> getShape()\n    {\n        if (!mTriangleMesh)\n            return osg::ref_ptr<BulletShape>();\n\n        osg::ref_ptr<BulletShape> shape (new BulletShape);\n        btBvhTriangleMeshShape* triangleMeshShape = new TriangleMeshShape(mTriangleMesh.release(), true);\n        btVector3 aabbMin = triangleMeshShape->getLocalAabbMin();\n        btVector3 aabbMax = triangleMeshShape->getLocalAabbMax();\n        shape->mCollisionBox.extents[0] = (aabbMax[0] - aabbMin[0]) / 2.0f;\n        shape->mCollisionBox.extents[1] = (aabbMax[1] - aabbMin[1]) / 2.0f;\n        shape->mCollisionBox.extents[2] = (aabbMax[2] - aabbMin[2]) / 2.0f;\n        shape->mCollisionBox.center = osg::Vec3f( (aabbMax[0] + aabbMin[0]) / 2.0f,\n                                                  (aabbMax[1] + aabbMin[1]) / 2.0f,\n                                                  (aabbMax[2] + aabbMin[2]) / 2.0f );\n        shape->mCollisionShape = triangleMeshShape;\n\n        return shape;\n    }\n\nprivate:\n    std::unique_ptr<btTriangleMesh> mTriangleMesh;\n};\n\nBulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager)\n    : ResourceManager(vfs)\n    , mInstanceCache(new MultiObjectCache)\n    , mSceneManager(sceneMgr)\n    , mNifFileManager(nifFileManager)\n{\n\n}\n\nBulletShapeManager::~BulletShapeManager()\n{\n\n}\n\nosg::ref_ptr<const BulletShape> BulletShapeManager::getShape(const std::string &name)\n{\n    std::string normalized = name;\n    mVFS->normalizeFilename(normalized);\n\n    osg::ref_ptr<BulletShape> shape;\n    osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);\n    if (obj)\n        shape = osg::ref_ptr<BulletShape>(static_cast<BulletShape*>(obj.get()));\n    else\n    {\n        size_t extPos = normalized.find_last_of('.');\n        std::string ext;\n        if (extPos != std::string::npos && extPos+1 < normalized.size())\n            ext = normalized.substr(extPos+1);\n\n        if (ext == \"nif\")\n        {\n            NifBullet::BulletNifLoader loader;\n            shape = loader.load(*mNifFileManager->get(normalized));\n        }\n        else\n        {\n            // TODO: support .bullet shape files\n\n            osg::ref_ptr<const osg::Node> constNode (mSceneManager->getTemplate(normalized));\n            osg::ref_ptr<osg::Node> node (const_cast<osg::Node*>(constNode.get())); // const-trickery required because there is no const version of NodeVisitor\n\n            // Check first if there's a custom collision node\n            unsigned int visitAllNodesMask = 0xffffffff;\n            SceneUtil::FindByNameVisitor nameFinder(\"Collision\");\n            nameFinder.setTraversalMask(visitAllNodesMask);\n            nameFinder.setNodeMaskOverride(visitAllNodesMask);\n            node->accept(nameFinder);\n            if (nameFinder.mFoundNode)\n            {\n                NodeToShapeVisitor visitor;\n                visitor.setTraversalMask(visitAllNodesMask);\n                visitor.setNodeMaskOverride(visitAllNodesMask);\n                nameFinder.mFoundNode->accept(visitor);\n                shape = visitor.getShape();\n            }\n\n            // Generate a collision shape from the mesh\n            if (!shape)\n            {\n                NodeToShapeVisitor visitor;\n                node->accept(visitor);\n                shape = visitor.getShape();\n                if (!shape)\n                    return osg::ref_ptr<BulletShape>();\n            }\n        }\n\n        mCache->addEntryToObjectCache(normalized, shape);\n    }\n    return shape;\n}\n\nosg::ref_ptr<BulletShapeInstance> BulletShapeManager::cacheInstance(const std::string &name)\n{\n    std::string normalized = name;\n    mVFS->normalizeFilename(normalized);\n\n    osg::ref_ptr<BulletShapeInstance> instance = createInstance(normalized);\n    if (instance)\n        mInstanceCache->addEntryToObjectCache(normalized, instance.get());\n    return instance;\n}\n\nosg::ref_ptr<BulletShapeInstance> BulletShapeManager::getInstance(const std::string &name)\n{\n    std::string normalized = name;\n    mVFS->normalizeFilename(normalized);\n\n    osg::ref_ptr<osg::Object> obj = mInstanceCache->takeFromObjectCache(normalized);\n    if (obj.get())\n        return static_cast<BulletShapeInstance*>(obj.get());\n    else\n        return createInstance(normalized);\n}\n\nosg::ref_ptr<BulletShapeInstance> BulletShapeManager::createInstance(const std::string &name)\n{\n    osg::ref_ptr<const BulletShape> shape = getShape(name);\n    if (shape)\n        return shape->makeInstance();\n    else\n        return osg::ref_ptr<BulletShapeInstance>();\n}\n\nvoid BulletShapeManager::updateCache(double referenceTime)\n{\n    ResourceManager::updateCache(referenceTime);\n\n    mInstanceCache->removeUnreferencedObjectsInCache();\n}\n\nvoid BulletShapeManager::clearCache()\n{\n    ResourceManager::clearCache();\n\n    mInstanceCache->clear();\n}\n\nvoid BulletShapeManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n{\n    stats->setAttribute(frameNumber, \"Shape\", mCache->getCacheSize());\n    stats->setAttribute(frameNumber, \"Shape Instance\", mInstanceCache->getCacheSize());\n}\n\n}\n"
  },
  {
    "path": "components/resource/bulletshapemanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H\n#define OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H\n\n#include <map>\n#include <string>\n\n#include <osg/ref_ptr>\n\n#include \"bulletshape.hpp\"\n#include \"resourcemanager.hpp\"\n\nnamespace Resource\n{\n    class SceneManager;\n    class NifFileManager;\n\n    class BulletShape;\n    class BulletShapeInstance;\n\n    class MultiObjectCache;\n\n    /// Handles loading, caching and \"instancing\" of bullet shapes.\n    /// A shape 'instance' is a clone of another shape, with the goal of setting a different scale on this instance.\n    /// @note May be used from any thread.\n    class BulletShapeManager : public ResourceManager\n    {\n    public:\n        BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager);\n        ~BulletShapeManager();\n\n        /// @note May return a null pointer if the object has no shape.\n        osg::ref_ptr<const BulletShape> getShape(const std::string& name);\n\n        /// Create an instance of the given shape and cache it for later use, so that future calls to getInstance() can simply return\n        /// the cached instance instead of having to create a new one.\n        /// @note The returned ref_ptr may be kept by the caller to ensure that the instance stays in cache for as long as needed.\n        osg::ref_ptr<BulletShapeInstance> cacheInstance(const std::string& name);\n\n        /// @note May return a null pointer if the object has no shape.\n        osg::ref_ptr<BulletShapeInstance> getInstance(const std::string& name);\n\n        /// @see ResourceManager::updateCache\n        void updateCache(double referenceTime) override;\n\n        void clearCache() override;\n\n        void reportStats(unsigned int frameNumber, osg::Stats *stats) const override;\n\n    private:\n        osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);\n\n        osg::ref_ptr<MultiObjectCache> mInstanceCache;\n        SceneManager* mSceneManager;\n        NifFileManager* mNifFileManager;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/imagemanager.cpp",
    "content": "#include \"imagemanager.hpp\"\n\n#include <cassert>\n#include <osgDB/Registry>\n\n#include <components/debug/debuglog.hpp>\n#include <components/vfs/manager.hpp>\n\n#include \"objectcache.hpp\"\n\n#ifdef OSG_LIBRARY_STATIC\n// This list of plugins should match with the list in the top-level CMakelists.txt.\nUSE_OSGPLUGIN(png)\nUSE_OSGPLUGIN(tga)\nUSE_OSGPLUGIN(dds)\nUSE_OSGPLUGIN(jpeg)\nUSE_OSGPLUGIN(bmp)\nUSE_OSGPLUGIN(osg)\nUSE_SERIALIZER_WRAPPER_LIBRARY(osg)\n#endif\n\nnamespace\n{\n\n    osg::ref_ptr<osg::Image> createWarningImage()\n    {\n        osg::ref_ptr<osg::Image> warningImage = new osg::Image;\n\n        int width = 8, height = 8;\n        warningImage->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);\n        assert (warningImage->isDataContiguous());\n        unsigned char* data = warningImage->data();\n        for (int i=0;i<width*height;++i)\n        {\n            data[3*i] = (255);\n            data[3*i+1] = (0);\n            data[3*i+2] = (255);\n        }\n        return warningImage;\n    }\n\n}\n\nnamespace Resource\n{\n\n    ImageManager::ImageManager(const VFS::Manager *vfs)\n        : ResourceManager(vfs)\n        , mWarningImage(createWarningImage())\n        , mOptions(new osgDB::Options(\"dds_flip dds_dxt1_detect_rgba ignoreTga2Fields\"))\n    {\n    }\n\n    ImageManager::~ImageManager()\n    {\n\n    }\n\n    bool checkSupported(osg::Image* image, const std::string& filename)\n    {\n        switch(image->getPixelFormat())\n        {\n            case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT):\n            case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT):\n            case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT):\n            case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT):\n            {\n                osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);\n                if (exts && !exts->isTextureCompressionS3TCSupported\n                        // This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a patch to OSG.\n                        && !osg::isGLExtensionSupported(0, \"GL_S3_s3tc\"))\n                {\n                    return false;\n                }\n                break;\n            }\n            // not bothering with checks for other compression formats right now, we are unlikely to ever use those anyway\n            default:\n                return true;\n        }\n        return true;\n    }\n\n    osg::ref_ptr<osg::Image> ImageManager::getImage(const std::string &filename)\n    {\n        std::string normalized = filename;\n        mVFS->normalizeFilename(normalized);\n\n        osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);\n        if (obj)\n            return osg::ref_ptr<osg::Image>(static_cast<osg::Image*>(obj.get()));\n        else\n        {\n            Files::IStreamPtr stream;\n            try\n            {\n                stream = mVFS->get(normalized.c_str());\n            }\n            catch (std::exception& e)\n            {\n                Log(Debug::Error) << \"Failed to open image: \" << e.what();\n                mCache->addEntryToObjectCache(normalized, mWarningImage);\n                return mWarningImage;\n            }\n\n            size_t extPos = normalized.find_last_of('.');\n            std::string ext;\n            if (extPos != std::string::npos && extPos+1 < normalized.size())\n                ext = normalized.substr(extPos+1);\n            osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);\n            if (!reader)\n            {\n                Log(Debug::Error) << \"Error loading \" << filename << \": no readerwriter for '\" << ext << \"' found\";\n                mCache->addEntryToObjectCache(normalized, mWarningImage);\n                return mWarningImage;\n            }\n\n            bool killAlpha = false;\n            if (reader->supportedExtensions().count(\"tga\"))\n            {\n                // Morrowind ignores the alpha channel of 16bpp TGA files even when the header says not to\n                unsigned char header[18];\n                stream->read((char*)header, 18);\n                if (stream->gcount() != 18)\n                {\n                    Log(Debug::Error) << \"Error loading \" << filename << \": couldn't read TGA header\";\n                    mCache->addEntryToObjectCache(normalized, mWarningImage);\n                    return mWarningImage;\n                }\n                int type = header[2];\n                int depth;\n                if (type == 1 || type == 9)\n                    depth = header[7];\n                else\n                    depth = header[16];\n                int alphaBPP = header[17] & 0x0F;\n                killAlpha = depth == 16 && alphaBPP == 1;\n                stream->seekg(0);\n            }\n\n            osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions);\n            if (!result.success())\n            {\n                Log(Debug::Error) << \"Error loading \" << filename << \": \" << result.message() << \" code \" << result.status();\n                mCache->addEntryToObjectCache(normalized, mWarningImage);\n                return mWarningImage;\n            }\n\n            osg::ref_ptr<osg::Image> image = result.getImage();\n\n            image->setFileName(normalized);\n            if (!checkSupported(image, filename))\n            {\n                static bool uncompress = (getenv(\"OPENMW_DECOMPRESS_TEXTURES\") != nullptr);\n                if (!uncompress)\n                {\n                    Log(Debug::Error) << \"Error loading \" << filename << \": no S3TC texture compression support installed\";\n                    mCache->addEntryToObjectCache(normalized, mWarningImage);\n                    return mWarningImage;\n                }\n                else\n                {\n                    // decompress texture in software if not supported by GPU\n                    // requires update to getColor() to be released with OSG 3.6\n                    osg::ref_ptr<osg::Image> newImage = new osg::Image;\n                    newImage->setFileName(image->getFileName());\n                    newImage->allocateImage(image->s(), image->t(), image->r(), image->isImageTranslucent() ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE);\n                    for (int s=0; s<image->s(); ++s)\n                        for (int t=0; t<image->t(); ++t)\n                            for (int r=0; r<image->r(); ++r)\n                                newImage->setColor(image->getColor(s,t,r), s,t,r);\n                    image = newImage;\n                }\n            }\n            else if (killAlpha)\n            {\n                osg::ref_ptr<osg::Image> newImage = new osg::Image;\n                newImage->setFileName(image->getFileName());\n                newImage->allocateImage(image->s(), image->t(), image->r(), GL_RGB, GL_UNSIGNED_BYTE);\n                // OSG just won't write the alpha as there's nowhere to put it.\n                for (int s = 0; s < image->s(); ++s)\n                    for (int t = 0; t < image->t(); ++t)\n                        for (int r = 0; r < image->r(); ++r)\n                            newImage->setColor(image->getColor(s, t, r), s, t, r);\n                image = newImage;\n            }\n\n            mCache->addEntryToObjectCache(normalized, image);\n            return image;\n        }\n    }\n\n    osg::Image *ImageManager::getWarningImage()\n    {\n        return mWarningImage;\n    }\n\n    void ImageManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n    {\n        stats->setAttribute(frameNumber, \"Image\", mCache->getCacheSize());\n    }\n\n}\n"
  },
  {
    "path": "components/resource/imagemanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_IMAGEMANAGER_H\n#define OPENMW_COMPONENTS_RESOURCE_IMAGEMANAGER_H\n\n#include <string>\n#include <map>\n\n#include <osg/ref_ptr>\n#include <osg/Image>\n#include <osg/Texture2D>\n\n#include \"resourcemanager.hpp\"\n\nnamespace osgDB\n{\n    class Options;\n}\n\nnamespace Resource\n{\n\n    /// @brief Handles loading/caching of Images.\n    /// @note May be used from any thread.\n    class ImageManager : public ResourceManager\n    {\n    public:\n        ImageManager(const VFS::Manager* vfs);\n        ~ImageManager();\n\n        /// Create or retrieve an Image\n        /// Returns the dummy image if the given image is not found.\n        osg::ref_ptr<osg::Image> getImage(const std::string& filename);\n\n        osg::Image* getWarningImage();\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;\n\n    private:\n        osg::ref_ptr<osg::Image> mWarningImage;\n        osg::ref_ptr<osgDB::Options> mOptions;\n\n        ImageManager(const ImageManager&);\n        void operator = (const ImageManager&);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/keyframemanager.cpp",
    "content": "#include \"keyframemanager.hpp\"\n\n#include <components/vfs/manager.hpp>\n\n#include <osgAnimation/Animation>\n#include <osgAnimation/BasicAnimationManager>\n#include <osgAnimation/Channel>\n\n#include <components/nifosg/nifloader.hpp>\n#include <components/sceneutil/keyframe.hpp>\n#include <components/sceneutil/osgacontroller.hpp>\n#include <components/misc/stringops.hpp>\n\n#include \"animation.hpp\"\n#include \"objectcache.hpp\"\n#include \"scenemanager.hpp\"\n\nnamespace Resource\n{\n\n    RetrieveAnimationsVisitor::RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager,\n        const std::string& normalized, const VFS::Manager* vfs) :\n        osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mTarget(target), mAnimationManager(animationManager), mNormalized(normalized), mVFS(vfs) {}\n\n    void RetrieveAnimationsVisitor::apply(osg::Node& node)\n    {\n        if (node.libraryName() == std::string(\"osgAnimation\") && node.className() == std::string(\"Bone\") && Misc::StringUtils::lowerCase(node.getName()) == std::string(\"bip01\"))\n            {\n                osg::ref_ptr<SceneUtil::OsgAnimationController> callback = new SceneUtil::OsgAnimationController();\n\n                std::vector<SceneUtil::EmulatedAnimation> emulatedAnimations;\n\n                for (auto animation : mAnimationManager->getAnimationList())\n                {\n                    if (animation)\n                    {\n                        if (animation->getName() == \"Default\") //\"Default\" is osg dae plugin's default naming scheme for unnamed animations\n                        {\n                            animation->setName(std::string(\"idle\")); // animation naming scheme \"idle: start\" and \"idle: stop\" is the default idle animation that OpenMW seems to want to play\n                        }\n\n                        osg::ref_ptr<Resource::Animation> mergedAnimationTrack = new Resource::Animation;\n                        std::string animationName = animation->getName();\n                        mergedAnimationTrack->setName(animationName);\n\n                        const osgAnimation::ChannelList& channels = animation->getChannels();\n                        for (const auto& channel: channels)\n                        {\n                            mergedAnimationTrack->addChannel(channel.get()->clone()); // is ->clone needed?\n                        }\n\n                        callback->addMergedAnimationTrack(mergedAnimationTrack);\n\n                        float startTime = animation->getStartTime();\n                        float stopTime = startTime + animation->getDuration();\n\n                        SceneUtil::EmulatedAnimation emulatedAnimation;\n                        emulatedAnimation.mStartTime = startTime;\n                        emulatedAnimation.mStopTime = stopTime;\n                        emulatedAnimation.mName = animationName;\n                        emulatedAnimations.emplace_back(emulatedAnimation);\n                    }\n                }\n\n                // mTextKeys is a nif-thing, used by OpenMW's animation system\n                // Format is likely \"AnimationName: [Keyword_optional] [Start OR Stop]\"\n                // AnimationNames are keywords like idle2, idle3... AiPackages and various mechanics control which animations are played\n                // Keywords can be stuff like Loop, Equip, Unequip, Block, InventoryHandtoHand, InventoryWeaponOneHand, PickProbe, Slash, Thrust, Chop... even \"Slash Small Follow\"\n                // osgAnimation formats should have a .txt file with the same name, each line holding a textkey and whitespace separated time value\n                // e.g. idle: start 0.0333\n                try\n                {\n                    Files::IStreamPtr textKeysFile = mVFS->get(changeFileExtension(mNormalized, \"txt\"));\n                    std::string line;\n                    while ( getline (*textKeysFile, line) )\n                    {\n                        mTarget.mTextKeys.emplace(parseTimeSignature(line), parseTextKey(line));\n                    }\n                }\n                catch (std::exception&)\n                {\n                    Log(Debug::Warning) << \"No textkey file found for \" << mNormalized;\n                }\n\n                callback->setEmulatedAnimations(emulatedAnimations);\n                mTarget.mKeyframeControllers.emplace(node.getName(), callback);\n            }\n\n        traverse(node);\n    }\n\n    std::string RetrieveAnimationsVisitor::parseTextKey(const std::string& line)\n    {\n        size_t spacePos = line.find_last_of(' ');\n        if (spacePos != std::string::npos)\n             return line.substr(0, spacePos);\n        return \"\";\n    }\n\n    double RetrieveAnimationsVisitor::parseTimeSignature(const std::string& line)\n    {\n        size_t spacePos = line.find_last_of(' ');\n        double time = 0.0;\n        if (spacePos != std::string::npos && spacePos + 1 < line.size())\n             time = std::stod(line.substr(spacePos + 1));\n        return time;\n    }\n\n    std::string RetrieveAnimationsVisitor::changeFileExtension(const std::string file, const std::string ext)\n    {\n        size_t extPos = file.find_last_of('.');\n        if (extPos != std::string::npos && extPos+1 < file.size())\n        {\n            return file.substr(0, extPos + 1) + ext;\n        }\n        return file;\n    }\n\n}\n\nnamespace Resource\n{\n\n    KeyframeManager::KeyframeManager(const VFS::Manager* vfs, SceneManager* sceneManager)\n        : ResourceManager(vfs)\n        , mSceneManager(sceneManager)\n    {\n    }\n\n    KeyframeManager::~KeyframeManager()\n    {\n    }\n\n    osg::ref_ptr<const SceneUtil::KeyframeHolder> KeyframeManager::get(const std::string &name)\n    {\n        std::string normalized = name;\n        mVFS->normalizeFilename(normalized);\n\n        osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);\n        if (obj)\n            return osg::ref_ptr<const SceneUtil::KeyframeHolder>(static_cast<SceneUtil::KeyframeHolder*>(obj.get()));\n        else\n        {\n            osg::ref_ptr<SceneUtil::KeyframeHolder> loaded (new SceneUtil::KeyframeHolder);\n            std::string ext = Resource::getFileExtension(normalized);\n            if (ext == \"kf\")\n            {\n                NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get());\n            }\n            else\n            {\n                osg::ref_ptr<osg::Node> scene = const_cast<osg::Node*> ( mSceneManager->getTemplate(normalized).get() );\n                osg::ref_ptr<osgAnimation::BasicAnimationManager> bam = dynamic_cast<osgAnimation::BasicAnimationManager*> (scene->getUpdateCallback());\n                if (bam)\n                {\n                    Resource::RetrieveAnimationsVisitor rav(*loaded.get(), bam, normalized, mVFS);\n                    scene->accept(rav);\n                }\n            }\n            mCache->addEntryToObjectCache(normalized, loaded);\n            return loaded;\n        }\n    }\n\n    void KeyframeManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n    {\n        stats->setAttribute(frameNumber, \"Keyframe\", mCache->getCacheSize());\n    }\n\n\n\n}\n"
  },
  {
    "path": "components/resource/keyframemanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_KEYFRAMEMANAGER_H\n#define OPENMW_COMPONENTS_KEYFRAMEMANAGER_H\n\n#include <osg/ref_ptr>\n#include <osgAnimation/BasicAnimationManager>\n#include <string>\n\n#include <components/sceneutil/keyframe.hpp>\n\n#include \"resourcemanager.hpp\"\n\nnamespace Resource\n{\n    /// @brief extract animations to OpenMW's animation system\n    class RetrieveAnimationsVisitor : public osg::NodeVisitor\n    {\n        public:\n            RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager,\n                const std::string& normalized, const VFS::Manager* vfs);\n\n            virtual void apply(osg::Node& node) override;\n\n        private:\n\n            std::string changeFileExtension(const std::string file, const std::string ext);\n            std::string parseTextKey(const std::string& line);\n            double parseTimeSignature(const std::string& line);\n\n            SceneUtil::KeyframeHolder& mTarget;\n            osg::ref_ptr<osgAnimation::BasicAnimationManager> mAnimationManager;\n            std::string mNormalized;\n            const VFS::Manager* mVFS;\n\n    };\n}\n\nnamespace Resource\n{\n\n    class SceneManager;\n\n    /// @brief Managing of keyframe resources\n    /// @note May be used from any thread.\n    class KeyframeManager : public ResourceManager\n    {\n    public:\n        KeyframeManager(const VFS::Manager* vfs, SceneManager* sceneManager);\n        ~KeyframeManager();\n\n        /// Retrieve a read-only keyframe resource by name (case-insensitive).\n        /// @note Throws an exception if the resource is not found.\n        osg::ref_ptr<const SceneUtil::KeyframeHolder> get(const std::string& name);\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;\n    private:\n        SceneManager* mSceneManager;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/multiobjectcache.cpp",
    "content": "#include \"multiobjectcache.hpp\"\n\n#include <vector>\n\n#include <osg/Object>\n\nnamespace Resource\n{\n\n    MultiObjectCache::MultiObjectCache()\n    {\n\n    }\n\n    MultiObjectCache::~MultiObjectCache()\n    {\n\n    }\n\n    void MultiObjectCache::removeUnreferencedObjectsInCache()\n    {\n        std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n\n            // Remove unreferenced entries from object cache\n            ObjectCacheMap::iterator oitr = _objectCache.begin();\n            while(oitr != _objectCache.end())\n            {\n                if (oitr->second->referenceCount() <= 1)\n                {\n                    objectsToRemove.push_back(oitr->second);\n                    _objectCache.erase(oitr++);\n                }\n                else\n                {\n                    ++oitr;\n                }\n            }\n        }\n\n        // note, actual unref happens outside of the lock\n        objectsToRemove.clear();\n    }\n\n    void MultiObjectCache::clear()\n    {\n        std::lock_guard<std::mutex> lock(_objectCacheMutex);\n        _objectCache.clear();\n    }\n\n    void MultiObjectCache::addEntryToObjectCache(const std::string &filename, osg::Object *object)\n    {\n        if (!object)\n        {\n            OSG_ALWAYS << \" trying to add NULL object to cache for \" << filename << std::endl;\n            return;\n        }\n        std::lock_guard<std::mutex> lock(_objectCacheMutex);\n        _objectCache.insert(std::make_pair(filename, object));\n    }\n\n    osg::ref_ptr<osg::Object> MultiObjectCache::takeFromObjectCache(const std::string &fileName)\n    {\n        std::lock_guard<std::mutex> lock(_objectCacheMutex);\n        ObjectCacheMap::iterator found = _objectCache.find(fileName);\n        if (found == _objectCache.end())\n            return osg::ref_ptr<osg::Object>();\n        else\n        {\n            osg::ref_ptr<osg::Object> object = found->second;\n            _objectCache.erase(found);\n            return object;\n        }\n    }\n\n    void MultiObjectCache::releaseGLObjects(osg::State *state)\n    {\n        std::lock_guard<std::mutex> lock(_objectCacheMutex);\n\n        for(ObjectCacheMap::iterator itr = _objectCache.begin();\n            itr != _objectCache.end();\n            ++itr)\n        {\n            osg::Object* object = itr->second.get();\n            object->releaseGLObjects(state);\n        }\n    }\n\n    unsigned int MultiObjectCache::getCacheSize() const\n    {\n        std::lock_guard<std::mutex> lock(_objectCacheMutex);\n        return _objectCache.size();\n    }\n\n}\n"
  },
  {
    "path": "components/resource/multiobjectcache.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MULTIOBJECTCACHE_H\n#define OPENMW_COMPONENTS_MULTIOBJECTCACHE_H\n\n#include <map>\n#include <string>\n#include <mutex>\n\n#include <osg/ref_ptr>\n#include <osg/Referenced>\n\nnamespace osg\n{\n    class Object;\n    class State;\n}\n\nnamespace Resource\n{\n\n    /// @brief Cache for \"non reusable\" objects.\n    class MultiObjectCache : public osg::Referenced\n    {\n    public:\n        MultiObjectCache();\n        ~MultiObjectCache();\n\n        void removeUnreferencedObjectsInCache();\n\n        /** Remove all objects from the cache. */\n        void clear();\n\n        void addEntryToObjectCache(const std::string& filename, osg::Object* object);\n\n        /** Take an Object from cache. Return nullptr if no object found. */\n        osg::ref_ptr<osg::Object> takeFromObjectCache(const std::string& fileName);\n\n        /** call releaseGLObjects on all objects attached to the object cache.*/\n        void releaseGLObjects(osg::State* state);\n\n        unsigned int getCacheSize() const;\n\n    protected:\n\n        typedef std::multimap<std::string, osg::ref_ptr<osg::Object> >             ObjectCacheMap;\n\n        ObjectCacheMap                          _objectCache;\n        mutable std::mutex                      _objectCacheMutex;\n\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/niffilemanager.cpp",
    "content": "#include \"niffilemanager.hpp\"\n\n#include <osg/Object>\n#include <osg/Stats>\n\n#include <components/vfs/manager.hpp>\n\n#include \"objectcache.hpp\"\n\nnamespace Resource\n{\n\n    class NifFileHolder : public osg::Object\n    {\n    public:\n        NifFileHolder(const Nif::NIFFilePtr& file)\n            : mNifFile(file)\n        {\n        }\n        NifFileHolder(const NifFileHolder& copy, const osg::CopyOp& copyop)\n            : mNifFile(copy.mNifFile)\n        {\n        }\n\n        NifFileHolder()\n        {\n        }\n\n        META_Object(Resource, NifFileHolder)\n\n        Nif::NIFFilePtr mNifFile;\n    };\n\n    NifFileManager::NifFileManager(const VFS::Manager *vfs)\n        : ResourceManager(vfs)\n    {\n    }\n\n    NifFileManager::~NifFileManager()\n    {\n\n    }\n\n\n    Nif::NIFFilePtr NifFileManager::get(const std::string &name)\n    {\n        osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(name);\n        if (obj)\n            return static_cast<NifFileHolder*>(obj.get())->mNifFile;\n        else\n        {\n            Nif::NIFFilePtr file (new Nif::NIFFile(mVFS->get(name), name));\n            obj = new NifFileHolder(file);\n            mCache->addEntryToObjectCache(name, obj);\n            return file;\n        }\n    }\n\n    void NifFileManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n    {\n        stats->setAttribute(frameNumber, \"Nif\", mCache->getCacheSize());\n    }\n\n}\n"
  },
  {
    "path": "components/resource/niffilemanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_NIFFILEMANAGER_H\n#define OPENMW_COMPONENTS_RESOURCE_NIFFILEMANAGER_H\n\n#include <osg/ref_ptr>\n\n#include <components/nif/niffile.hpp>\n\n#include \"resourcemanager.hpp\"\n\nnamespace Resource\n{\n\n    /// @brief Handles caching of NIFFiles.\n    /// @note May be used from any thread.\n    class NifFileManager : public ResourceManager\n    {\n    public:\n        NifFileManager(const VFS::Manager* vfs);\n        ~NifFileManager();\n\n        /// Retrieve a NIF file from the cache, or load it from the VFS if not cached yet.\n        /// @note For performance reasons the NifFileManager does not handle case folding, needs\n        /// to be done in advance by other managers accessing the NifFileManager.\n        Nif::NIFFilePtr get(const std::string& name);\n\n        void reportStats(unsigned int frameNumber, osg::Stats *stats) const override;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/objectcache.hpp",
    "content": "// Resource ObjectCache for OpenMW, forked from osgDB ObjectCache by Robert Osfield, see copyright notice below.\n// Changes:\n// - removeExpiredObjectsInCache no longer keeps a lock while the unref happens.\n// - template allows customized KeyType.\n// - objects with uninitialized time stamp are not removed.\n\n/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield\n *\n * This library is open source and may be redistributed and/or modified under\n * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or\n * (at your option) any later version.  The full license is in LICENSE file\n * included with this distribution, and on the openscenegraph.org website.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * OpenSceneGraph Public License for more details.\n*/\n\n#ifndef OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE\n#define OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE\n\n#include <osg/Referenced>\n#include <osg/ref_ptr>\n#include <osg/Node>\n\n#include <string>\n#include <map>\n#include <mutex>\n\nnamespace osg\n{\n    class Object;\n    class State;\n    class NodeVisitor;\n}\n\nnamespace Resource {\n\ntemplate <typename KeyType>\nclass GenericObjectCache : public osg::Referenced\n{\n    public:\n\n        GenericObjectCache()\n            : osg::Referenced(true) {}\n\n        /** For each object in the cache which has an reference count greater than 1\n          * (and therefore referenced by elsewhere in the application) set the time stamp\n          * for that object in the cache to specified time.\n          * This would typically be called once per frame by applications which are doing database paging,\n          * and need to prune objects that are no longer required.\n          * The time used should be taken from the FrameStamp::getReferenceTime().*/\n        void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime)\n        {\n            // look for objects with external references and update their time stamp.\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            for(typename ObjectCacheMap::iterator itr=_objectCache.begin(); itr!=_objectCache.end(); ++itr)\n            {\n                // If ref count is greater than 1, the object has an external reference.\n                // If the timestamp is yet to be initialized, it needs to be updated too.\n                if (itr->second.first->referenceCount()>1 || itr->second.second == 0.0)\n                    itr->second.second = referenceTime;\n            }\n        }\n\n        /** Removed object in the cache which have a time stamp at or before the specified expiry time.\n          * This would typically be called once per frame by applications which are doing database paging,\n          * and need to prune objects that are no longer required, and called after the a called\n          * after the call to updateTimeStampOfObjectsInCacheWithExternalReferences(expirtyTime).*/\n        void removeExpiredObjectsInCache(double expiryTime)\n        {\n            std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;\n            {\n                std::lock_guard<std::mutex> lock(_objectCacheMutex);\n                // Remove expired entries from object cache\n                typename ObjectCacheMap::iterator oitr = _objectCache.begin();\n                while(oitr != _objectCache.end())\n                {\n                    if (oitr->second.second<=expiryTime)\n                    {\n                        objectsToRemove.push_back(oitr->second.first);\n                        _objectCache.erase(oitr++);\n                    }\n                    else\n                        ++oitr;\n                }\n            }\n            // note, actual unref happens outside of the lock\n            objectsToRemove.clear();\n        }\n\n        /** Remove all objects in the cache regardless of having external references or expiry times.*/\n        void clear()\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            _objectCache.clear();\n        }\n\n        /** Add a key,object,timestamp triple to the Registry::ObjectCache.*/\n        void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0)\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            _objectCache[key]=ObjectTimeStampPair(object,timestamp);\n        }\n\n        /** Remove Object from cache.*/\n        void removeFromObjectCache(const KeyType& key)\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            typename ObjectCacheMap::iterator itr = _objectCache.find(key);\n            if (itr!=_objectCache.end()) _objectCache.erase(itr);\n        }\n\n        /** Get an ref_ptr<Object> from the object cache*/\n        osg::ref_ptr<osg::Object> getRefFromObjectCache(const KeyType& key)\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            typename ObjectCacheMap::iterator itr = _objectCache.find(key);\n            if (itr!=_objectCache.end())\n                return itr->second.first;\n            else return nullptr;\n        }\n\n        /** Check if an object is in the cache, and if it is, update its usage time stamp. */\n        bool checkInObjectCache(const KeyType& key, double timeStamp)\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            typename ObjectCacheMap::iterator itr = _objectCache.find(key);\n            if (itr!=_objectCache.end())\n            {\n                itr->second.second = timeStamp;\n                return true;\n            }\n            else return false;\n        }\n\n        /** call releaseGLObjects on all objects attached to the object cache.*/\n        void releaseGLObjects(osg::State* state)\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)\n            {\n                osg::Object* object = itr->second.first.get();\n                object->releaseGLObjects(state);\n            }\n        }\n\n        /** call node->accept(nv); for all nodes in the objectCache. */\n        void accept(osg::NodeVisitor& nv)\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)\n            {\n                osg::Object* object = itr->second.first.get();\n                if (object)\n                {\n                    osg::Node* node = dynamic_cast<osg::Node*>(object);\n                    if (node)\n                        node->accept(nv);\n                }\n            }\n        }\n\n        /** call operator()(KeyType, osg::Object*) for each object in the cache. */\n        template <class Functor>\n        void call(Functor& f)\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it)\n                f(it->first, it->second.first.get());\n        }\n\n        /** Get the number of objects in the cache. */\n        unsigned int getCacheSize() const\n        {\n            std::lock_guard<std::mutex> lock(_objectCacheMutex);\n            return _objectCache.size();\n        }\n\n    protected:\n\n        virtual ~GenericObjectCache() {}\n\n        typedef std::pair<osg::ref_ptr<osg::Object>, double >           ObjectTimeStampPair;\n        typedef std::map<KeyType, ObjectTimeStampPair >             ObjectCacheMap;\n\n        ObjectCacheMap                          _objectCache;\n        mutable std::mutex                      _objectCacheMutex;\n\n};\n\nclass ObjectCache : public GenericObjectCache<std::string>\n{\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/resourcemanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_MANAGER_H\n#define OPENMW_COMPONENTS_RESOURCE_MANAGER_H\n\n#include <osg/ref_ptr>\n\n#include \"objectcache.hpp\"\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace osg\n{\n    class Stats;\n    class State;\n}\n\nnamespace Resource\n{\n\n    class BaseResourceManager\n    {\n    public:\n        virtual ~BaseResourceManager() {}\n        virtual void updateCache(double referenceTime) {}\n        virtual void clearCache() {}\n        virtual void setExpiryDelay(double expiryDelay) {}\n        virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const {}\n        virtual void releaseGLObjects(osg::State* state) {}\n    };\n\n    /// @brief Base class for managers that require a virtual file system and object cache.\n    /// @par This base class implements clearing of the cache, but populating it and what it's used for is up to the individual sub classes.\n    template <class KeyType>\n    class GenericResourceManager : public BaseResourceManager\n    {\n    public:\n        typedef GenericObjectCache<KeyType> CacheType;\n\n        GenericResourceManager(const VFS::Manager* vfs)\n            : mVFS(vfs)\n            , mCache(new CacheType)\n            , mExpiryDelay(0.0)\n        {\n        }\n\n        virtual ~GenericResourceManager() {}\n\n        /// Clear cache entries that have not been referenced for longer than expiryDelay.\n        void updateCache(double referenceTime) override\n        {\n            mCache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);\n            mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay);\n        }\n\n        /// Clear all cache entries.\n        void clearCache() override { mCache->clear(); }\n\n        /// How long to keep objects in cache after no longer being referenced.\n        void setExpiryDelay (double expiryDelay) override { mExpiryDelay = expiryDelay; }\n\n        const VFS::Manager* getVFS() const { return mVFS; }\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override {}\n\n        void releaseGLObjects(osg::State* state) override { mCache->releaseGLObjects(state); }\n\n    protected:\n        const VFS::Manager* mVFS;\n        osg::ref_ptr<CacheType> mCache;\n        double mExpiryDelay;\n    };\n\n\n    class ResourceManager : public GenericResourceManager<std::string>\n    {\n    public:\n        ResourceManager(const VFS::Manager* vfs) : GenericResourceManager<std::string>(vfs) {}\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/resourcesystem.cpp",
    "content": "#include \"resourcesystem.hpp\"\n\n#include <algorithm>\n\n#include \"scenemanager.hpp\"\n#include \"imagemanager.hpp\"\n#include \"niffilemanager.hpp\"\n#include \"keyframemanager.hpp\"\n\nnamespace Resource\n{\n\n    ResourceSystem::ResourceSystem(const VFS::Manager *vfs)\n        : mVFS(vfs)\n    {\n        mNifFileManager.reset(new NifFileManager(vfs));\n        mImageManager.reset(new ImageManager(vfs));\n        mSceneManager.reset(new SceneManager(vfs, mImageManager.get(), mNifFileManager.get()));\n        mKeyframeManager.reset(new KeyframeManager(vfs, mSceneManager.get()));\n\n        addResourceManager(mNifFileManager.get());\n        addResourceManager(mKeyframeManager.get());\n        // note, scene references images so add images afterwards for correct implementation of updateCache()\n        addResourceManager(mSceneManager.get());\n        addResourceManager(mImageManager.get());\n    }\n\n    ResourceSystem::~ResourceSystem()\n    {\n        // this has to be defined in the .cpp file as we can't delete incomplete types\n\n        mResourceManagers.clear();\n    }\n\n    SceneManager* ResourceSystem::getSceneManager()\n    {\n        return mSceneManager.get();\n    }\n\n    ImageManager* ResourceSystem::getImageManager()\n    {\n        return mImageManager.get();\n    }\n\n    NifFileManager* ResourceSystem::getNifFileManager()\n    {\n        return mNifFileManager.get();\n    }\n\n    KeyframeManager* ResourceSystem::getKeyframeManager()\n    {\n        return mKeyframeManager.get();\n    }\n\n    void ResourceSystem::setExpiryDelay(double expiryDelay)\n    {\n        for (std::vector<BaseResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)\n            (*it)->setExpiryDelay(expiryDelay);\n\n        // NIF files aren't needed any more once the converted objects are cached in SceneManager / BulletShapeManager,\n        // so no point in using an expiry delay\n        mNifFileManager->setExpiryDelay(0.0);\n    }\n\n    void ResourceSystem::updateCache(double referenceTime)\n    {\n        for (std::vector<BaseResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)\n            (*it)->updateCache(referenceTime);\n    }\n\n    void ResourceSystem::clearCache()\n    {\n        for (std::vector<BaseResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)\n            (*it)->clearCache();\n    }\n\n    void ResourceSystem::addResourceManager(BaseResourceManager *resourceMgr)\n    {\n        mResourceManagers.push_back(resourceMgr);\n    }\n\n    void ResourceSystem::removeResourceManager(BaseResourceManager *resourceMgr)\n    {\n        std::vector<BaseResourceManager*>::iterator found = std::find(mResourceManagers.begin(), mResourceManagers.end(), resourceMgr);\n        if (found != mResourceManagers.end())\n            mResourceManagers.erase(found);\n    }\n\n    const VFS::Manager* ResourceSystem::getVFS() const\n    {\n        return mVFS;\n    }\n\n    void ResourceSystem::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n    {\n        for (std::vector<BaseResourceManager*>::const_iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)\n            (*it)->reportStats(frameNumber, stats);\n    }\n\n    void ResourceSystem::releaseGLObjects(osg::State *state)\n    {\n        for (std::vector<BaseResourceManager*>::const_iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)\n            (*it)->releaseGLObjects(state);\n    }\n\n}\n"
  },
  {
    "path": "components/resource/resourcesystem.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H\n#define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H\n\n#include <memory>\n#include <vector>\n\nnamespace VFS\n{\n    class Manager;\n}\n\nnamespace osg\n{\n    class Stats;\n    class State;\n}\n\nnamespace Resource\n{\n\n    class SceneManager;\n    class ImageManager;\n    class NifFileManager;\n    class KeyframeManager;\n    class BaseResourceManager;\n\n    /// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems.\n    /// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but\n    ///     are built around the use of a single virtual file system.\n    class ResourceSystem\n    {\n    public:\n        ResourceSystem(const VFS::Manager* vfs);\n        ~ResourceSystem();\n\n        SceneManager* getSceneManager();\n        ImageManager* getImageManager();\n        NifFileManager* getNifFileManager();\n        KeyframeManager* getKeyframeManager();\n\n        /// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced.\n        /// @note May be called from any thread if you do not add or remove resource managers at that point.\n        void updateCache(double referenceTime);\n\n        /// Indicates to each resource manager to clear the entire cache.\n        /// @note May be called from any thread if you do not add or remove resource managers at that point.\n        void clearCache();\n\n        /// Add this ResourceManager to be handled by the ResourceSystem.\n        /// @note Does not transfer ownership.\n        void addResourceManager(BaseResourceManager* resourceMgr);\n        /// @note Do nothing if resourceMgr does not exist.\n        /// @note Does not delete resourceMgr.\n        void removeResourceManager(BaseResourceManager* resourceMgr);\n\n        /// How long to keep objects in cache after no longer being referenced.\n        void setExpiryDelay(double expiryDelay);\n\n        /// @note May be called from any thread.\n        const VFS::Manager* getVFS() const;\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const;\n\n        /// Call releaseGLObjects for each resource manager.\n        void releaseGLObjects(osg::State* state);\n\n    private:\n        std::unique_ptr<SceneManager> mSceneManager;\n        std::unique_ptr<ImageManager> mImageManager;\n        std::unique_ptr<NifFileManager> mNifFileManager;\n        std::unique_ptr<KeyframeManager> mKeyframeManager;\n\n        // Store the base classes separately to get convenient access to the common interface\n        // Here users can register their own resourcemanager as well\n        std::vector<BaseResourceManager*> mResourceManagers;\n\n        const VFS::Manager* mVFS;\n\n        ResourceSystem(const ResourceSystem&);\n        void operator = (const ResourceSystem&);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/scenemanager.cpp",
    "content": "#include \"scenemanager.hpp\"\n\n#include <cstdlib>\n\n#include <osg/Node>\n#include <osg/UserDataContainer>\n\n#include <osgParticle/ParticleSystem>\n\n#include <osgUtil/IncrementalCompileOperation>\n\n#include <osgDB/SharedStateManager>\n#include <osgDB/Registry>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/nifosg/nifloader.hpp>\n#include <components/nif/niffile.hpp>\n\n#include <components/misc/stringops.hpp>\n\n#include <components/vfs/manager.hpp>\n\n#include <components/sceneutil/clone.hpp>\n#include <components/sceneutil/util.hpp>\n#include <components/sceneutil/controller.hpp>\n#include <components/sceneutil/optimizer.hpp>\n#include <components/sceneutil/visitor.hpp>\n\n#include <components/shader/shadervisitor.hpp>\n#include <components/shader/shadermanager.hpp>\n\n#include \"imagemanager.hpp\"\n#include \"niffilemanager.hpp\"\n#include \"objectcache.hpp\"\n#include \"multiobjectcache.hpp\"\n\nnamespace\n{\n\n    class InitWorldSpaceParticlesCallback : public osg::NodeCallback\n    {\n    public:\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osgParticle::ParticleSystem* partsys = static_cast<osgParticle::ParticleSystem*>(node);\n\n            // HACK: Ignore the InverseWorldMatrix transform the particle system is attached to\n            if (partsys->getNumParents() && partsys->getParent(0)->getNumParents())\n                transformInitialParticles(partsys, partsys->getParent(0)->getParent(0));\n\n            node->removeUpdateCallback(this);\n        }\n\n        void transformInitialParticles(osgParticle::ParticleSystem* partsys, osg::Node* node)\n        {\n            osg::NodePathList nodepaths = node->getParentalNodePaths();\n            if (nodepaths.empty())\n                return;\n            osg::Matrixf worldMat = osg::computeLocalToWorld(nodepaths[0]);\n            worldMat.orthoNormalize(worldMat); // scale is already applied on the particle node\n            for (int i=0; i<partsys->numParticles(); ++i)\n            {\n                partsys->getParticle(i)->transformPositionVelocity(worldMat);\n            }\n\n            // transform initial bounds to worldspace\n            osg::BoundingSphere sphere(partsys->getInitialBound());\n            SceneUtil::transformBoundingSphere(worldMat, sphere);\n            osg::BoundingBox box;\n            box.expandBy(sphere);\n            partsys->setInitialBound(box);\n        }\n\n    };\n\n    class InitParticlesVisitor : public osg::NodeVisitor\n    {\n    public:\n        /// @param mask The node mask to set on ParticleSystem nodes.\n        InitParticlesVisitor(unsigned int mask)\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mMask(mask)\n        {\n        }\n\n        bool isWorldSpaceParticleSystem(osgParticle::ParticleSystem* partsys)\n        {\n            // HACK: ParticleSystem has no getReferenceFrame()\n            return (partsys->getUserDataContainer()\n                    && partsys->getUserDataContainer()->getNumDescriptions() > 0\n                    && partsys->getUserDataContainer()->getDescriptions()[0] == \"worldspace\");\n        }\n\n        void apply(osg::Drawable& drw) override\n        {\n            if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(&drw))\n            {\n                if (isWorldSpaceParticleSystem(partsys))\n                {\n                    partsys->addUpdateCallback(new InitWorldSpaceParticlesCallback);\n                }\n                partsys->setNodeMask(mMask);\n            }\n        }\n\n    private:\n        unsigned int mMask;\n    };\n}\n\nnamespace Resource\n{\n    void TemplateMultiRef::addRef(const osg::Node* node)\n    {\n        mObjects.emplace_back(node);\n    }\n\n    class SharedStateManager : public osgDB::SharedStateManager\n    {\n    public:\n        unsigned int getNumSharedTextures() const\n        {\n            return _sharedTextureList.size();\n        }\n\n        unsigned int getNumSharedStateSets() const\n        {\n            return _sharedStateSetList.size();\n        }\n\n        void clearCache()\n        {\n            std::lock_guard<OpenThreads::Mutex> lock(_listMutex);\n            _sharedTextureList.clear();\n            _sharedStateSetList.clear();\n        }\n    };\n\n    /// Set texture filtering settings on textures contained in a FlipController.\n    class SetFilterSettingsControllerVisitor : public SceneUtil::ControllerVisitor\n    {\n    public:\n        SetFilterSettingsControllerVisitor(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy)\n            : mMinFilter(minFilter)\n            , mMagFilter(magFilter)\n            , mMaxAnisotropy(maxAnisotropy)\n        {\n        }\n\n        void visit(osg::Node& node, SceneUtil::Controller& ctrl) override\n        {\n            if (NifOsg::FlipController* flipctrl = dynamic_cast<NifOsg::FlipController*>(&ctrl))\n            {\n                for (std::vector<osg::ref_ptr<osg::Texture2D> >::iterator it = flipctrl->getTextures().begin(); it != flipctrl->getTextures().end(); ++it)\n                {\n                    osg::Texture* tex = *it;\n                    tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter);\n                    tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter);\n                    tex->setMaxAnisotropy(mMaxAnisotropy);\n                }\n            }\n        }\n\n    private:\n        osg::Texture::FilterMode mMinFilter;\n        osg::Texture::FilterMode mMagFilter;\n        int mMaxAnisotropy;\n    };\n\n    /// Set texture filtering settings on textures contained in StateSets.\n    class SetFilterSettingsVisitor : public osg::NodeVisitor\n    {\n    public:\n        SetFilterSettingsVisitor(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy)\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mMinFilter(minFilter)\n            , mMagFilter(magFilter)\n            , mMaxAnisotropy(maxAnisotropy)\n        {\n        }\n\n        void apply(osg::Node& node) override\n        {\n            osg::StateSet* stateset = node.getStateSet();\n            if (stateset)\n                applyStateSet(stateset);\n\n            traverse(node);\n        }\n\n        void applyStateSet(osg::StateSet* stateset)\n        {\n            const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList();\n            for(unsigned int unit=0;unit<texAttributes.size();++unit)\n            {\n                osg::StateAttribute *texture = stateset->getTextureAttribute(unit, osg::StateAttribute::TEXTURE);\n                if (texture)\n                    applyStateAttribute(texture);\n            }\n        }\n\n        void applyStateAttribute(osg::StateAttribute* attr)\n        {\n            osg::Texture* tex = attr->asTexture();\n            if (tex)\n            {\n                tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter);\n                tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter);\n                tex->setMaxAnisotropy(mMaxAnisotropy);\n            }\n        }\n    private:\n        osg::Texture::FilterMode mMinFilter;\n        osg::Texture::FilterMode mMagFilter;\n        int mMaxAnisotropy;\n    };\n\n\n\n    SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)\n        : ResourceManager(vfs)\n        , mShaderManager(new Shader::ShaderManager)\n        , mForceShaders(false)\n        , mClampLighting(true)\n        , mAutoUseNormalMaps(false)\n        , mAutoUseSpecularMaps(false)\n        , mApplyLightingToEnvMaps(false)\n        , mLightingMethod(SceneUtil::LightingMethod::FFP)\n        , mConvertAlphaTestToAlphaToCoverage(false)\n        , mInstanceCache(new MultiObjectCache)\n        , mSharedStateManager(new SharedStateManager)\n        , mImageManager(imageManager)\n        , mNifFileManager(nifFileManager)\n        , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)\n        , mMagFilter(osg::Texture::LINEAR)\n        , mMaxAnisotropy(1)\n        , mUnRefImageDataAfterApply(false)\n        , mParticleSystemMask(~0u)\n    {\n    }\n\n    void SceneManager::setForceShaders(bool force)\n    {\n        mForceShaders = force;\n    }\n\n    bool SceneManager::getForceShaders() const\n    {\n        return mForceShaders;\n    }\n\n    void SceneManager::recreateShaders(osg::ref_ptr<osg::Node> node, const std::string& shaderPrefix, bool translucentFramebuffer, bool forceShadersForNode)\n    {\n        osg::ref_ptr<Shader::ShaderVisitor> shaderVisitor(createShaderVisitor(shaderPrefix, translucentFramebuffer));\n        shaderVisitor->setAllowedToModifyStateSets(false);\n        if (forceShadersForNode)\n            shaderVisitor->setForceShaders(true);\n        node->accept(*shaderVisitor);\n    }\n\n    void SceneManager::reinstateRemovedState(osg::ref_ptr<osg::Node> node)\n    {\n        osg::ref_ptr<Shader::ReinstateRemovedStateVisitor> reinstateRemovedStateVisitor = new Shader::ReinstateRemovedStateVisitor(false);\n        node->accept(*reinstateRemovedStateVisitor);\n    }\n\n    void SceneManager::setClampLighting(bool clamp)\n    {\n        mClampLighting = clamp;\n    }\n\n    bool SceneManager::getClampLighting() const\n    {\n        return mClampLighting;\n    }\n\n    void SceneManager::setAutoUseNormalMaps(bool use)\n    {\n        mAutoUseNormalMaps = use;\n    }\n\n    void SceneManager::setNormalMapPattern(const std::string &pattern)\n    {\n        mNormalMapPattern = pattern;\n    }\n\n    void SceneManager::setNormalHeightMapPattern(const std::string &pattern)\n    {\n        mNormalHeightMapPattern = pattern;\n    }\n\n    void SceneManager::setAutoUseSpecularMaps(bool use)\n    {\n        mAutoUseSpecularMaps = use;\n    }\n\n    void SceneManager::setSpecularMapPattern(const std::string &pattern)\n    {\n        mSpecularMapPattern = pattern;\n    }\n\n    void SceneManager::setApplyLightingToEnvMaps(bool apply)\n    {\n        mApplyLightingToEnvMaps = apply;\n    }\n\n    void SceneManager::setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported)\n    {\n        mSupportedLightingMethods = supported;\n    }\n\n    bool SceneManager::isSupportedLightingMethod(SceneUtil::LightingMethod method) const\n    {\n        return mSupportedLightingMethods[static_cast<int>(method)];\n    }\n\n    void SceneManager::setLightingMethod(SceneUtil::LightingMethod method)\n    {\n        mLightingMethod = method;\n    }\n\n    SceneUtil::LightingMethod SceneManager::getLightingMethod() const\n    {\n        return mLightingMethod;\n    }\n    \n    void SceneManager::setConvertAlphaTestToAlphaToCoverage(bool convert)\n    {\n        mConvertAlphaTestToAlphaToCoverage = convert;\n    }\n\n    SceneManager::~SceneManager()\n    {\n        // this has to be defined in the .cpp file as we can't delete incomplete types\n    }\n\n    Shader::ShaderManager &SceneManager::getShaderManager()\n    {\n        return *mShaderManager.get();\n    }\n\n    void SceneManager::setShaderPath(const std::string &path)\n    {\n        mShaderManager->setShaderPath(path);\n    }\n\n    bool SceneManager::checkLoaded(const std::string &name, double timeStamp)\n    {\n        std::string normalized = name;\n        mVFS->normalizeFilename(normalized);\n\n        return mCache->checkInObjectCache(normalized, timeStamp);\n    }\n\n    /// @brief Callback to read image files from the VFS.\n    class ImageReadCallback : public osgDB::ReadFileCallback\n    {\n    public:\n        ImageReadCallback(Resource::ImageManager* imageMgr)\n            : mImageManager(imageMgr)\n        {\n        }\n\n        osgDB::ReaderWriter::ReadResult readImage(const std::string& filename, const osgDB::Options* options) override\n        {\n            try\n            {\n                return osgDB::ReaderWriter::ReadResult(mImageManager->getImage(filename), osgDB::ReaderWriter::ReadResult::FILE_LOADED);\n            }\n            catch (std::exception& e)\n            {\n                return osgDB::ReaderWriter::ReadResult(e.what());\n            }\n        }\n\n    private:\n        Resource::ImageManager* mImageManager;\n    };\n\n    osg::ref_ptr<osg::Node> load (const std::string& normalizedFilename, const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)\n    {\n        std::string ext = Resource::getFileExtension(normalizedFilename);\n        if (ext == \"nif\")\n            return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager);\n        else\n        {\n            osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);\n            if (!reader)\n            {\n                std::stringstream errormsg;\n                errormsg << \"Error loading \" << normalizedFilename << \": no readerwriter for '\" << ext << \"' found\" << std::endl;\n                throw std::runtime_error(errormsg.str());\n            }\n\n            osg::ref_ptr<osgDB::Options> options (new osgDB::Options);\n            // Set a ReadFileCallback so that image files referenced in the model are read from our virtual file system instead of the osgDB.\n            // Note, for some formats (.obj/.mtl) that reference other (non-image) files a findFileCallback would be necessary.\n            // but findFileCallback does not support virtual files, so we can't implement it.\n            options->setReadFileCallback(new ImageReadCallback(imageManager));\n            if (ext == \"dae\") options->setOptionString(\"daeUseSequencedTextureUnits\");\n\n            osgDB::ReaderWriter::ReadResult result = reader->readNode(*vfs->get(normalizedFilename), options);\n            if (!result.success())\n            {\n                std::stringstream errormsg;\n                errormsg << \"Error loading \" << normalizedFilename << \": \" << result.message() << \" code \" << result.status() << std::endl;\n                throw std::runtime_error(errormsg.str());\n            }\n\n            // Recognize and hide collision node\n            unsigned int hiddenNodeMask = 0;\n            SceneUtil::FindByNameVisitor nameFinder(\"Collision\");\n            result.getNode()->accept(nameFinder);\n            if (nameFinder.mFoundNode)\n                nameFinder.mFoundNode->setNodeMask(hiddenNodeMask);\n\n            return result.getNode();\n        }\n    }\n\n    class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback\n    {\n    public:\n        bool isReservedName(const std::string& name) const\n        {\n            if (name.empty())\n                return false;\n\n            static std::vector<std::string> reservedNames;\n            if (reservedNames.empty())\n            {\n                const char* reserved[] = {\"Head\", \"Neck\", \"Chest\", \"Groin\", \"Right Hand\", \"Left Hand\", \"Right Wrist\", \"Left Wrist\", \"Shield Bone\", \"Right Forearm\", \"Left Forearm\", \"Right Upper Arm\",\n                                          \"Left Upper Arm\", \"Right Foot\", \"Left Foot\", \"Right Ankle\", \"Left Ankle\", \"Right Knee\", \"Left Knee\", \"Right Upper Leg\", \"Left Upper Leg\", \"Right Clavicle\",\n                                          \"Left Clavicle\", \"Weapon Bone\", \"Tail\", \"Bip01\", \"Root Bone\", \"BoneOffset\", \"AttachLight\", \"Arrow\", \"Camera\", \"Collision\", \"Right_Wrist\", \"Left_Wrist\",\n                                          \"Shield_Bone\", \"Right_Forearm\", \"Left_Forearm\", \"Right_Upper_Arm\", \"Left_Clavicle\", \"Weapon_Bone\", \"Root_Bone\"};\n\n                reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));\n\n                for (unsigned int i=0; i<sizeof(reserved)/sizeof(reserved[0]); ++i)\n                    reservedNames.push_back(std::string(\"Tri \") + reserved[i]);\n\n                std::sort(reservedNames.begin(), reservedNames.end(), Misc::StringUtils::ciLess);\n            }\n\n            std::vector<std::string>::iterator it = Misc::StringUtils::partialBinarySearch(reservedNames.begin(), reservedNames.end(), name);\n            return it != reservedNames.end();\n        }\n\n        bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const override\n        {\n            if (option & SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS)\n            {\n                if (node->asGeometry() && node->className() == std::string(\"Geometry\"))\n                    return true;\n                else\n                    return false; //ParticleSystem would have to convert space of all the processors, RigGeometry would have to convert bones... theoretically possible, but very complicated\n            }\n            return (option & optimizer->getPermissibleOptimizationsForObject(node))!=0;\n        }\n\n        bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const override\n        {\n            if (node->getNumDescriptions()>0) return false;\n            if (node->getDataVariance() == osg::Object::DYNAMIC) return false;\n            if (isReservedName(node->getName())) return false;\n\n            return (option & optimizer->getPermissibleOptimizationsForObject(node))!=0;\n        }\n    };\n\n    bool canOptimize(const std::string& filename)\n    {\n        size_t slashpos = filename.find_last_of(\"\\\\/\");\n        if (slashpos != std::string::npos && slashpos+1 < filename.size())\n        {\n            std::string basename = filename.substr(slashpos+1);\n            // xmesh.nif can not be optimized because there are keyframes added in post\n            if (!basename.empty() && basename[0] == 'x')\n                return false;\n\n            // NPC skeleton files can not be optimized because of keyframes added in post\n            // (most of them are usually named like 'xbase_anim.nif' anyway, but not all of them :( )\n            if (basename.compare(0, 9, \"base_anim\") == 0 || basename.compare(0, 4, \"skin\") == 0)\n                return false;\n        }\n\n        // For spell VFX, DummyXX nodes must remain intact. Not adding those to reservedNames to avoid being overly cautious - instead, decide on filename\n        if (filename.find(\"vfx_pattern\") != std::string::npos)\n            return false;\n        return true;\n    }\n\n    unsigned int getOptimizationOptions()\n    {\n        using namespace SceneUtil;\n        const char* env = getenv(\"OPENMW_OPTIMIZE\");\n        unsigned int options = Optimizer::FLATTEN_STATIC_TRANSFORMS|Optimizer::REMOVE_REDUNDANT_NODES|Optimizer::MERGE_GEOMETRY;\n        if (env)\n        {\n            std::string str(env);\n\n            if(str.find(\"OFF\")!=std::string::npos || str.find('0')!= std::string::npos) options = 0;\n\n            if(str.find(\"~FLATTEN_STATIC_TRANSFORMS\")!=std::string::npos) options ^= Optimizer::FLATTEN_STATIC_TRANSFORMS;\n            else if(str.find(\"FLATTEN_STATIC_TRANSFORMS\")!=std::string::npos) options |= Optimizer::FLATTEN_STATIC_TRANSFORMS;\n\n            if(str.find(\"~REMOVE_REDUNDANT_NODES\")!=std::string::npos) options ^= Optimizer::REMOVE_REDUNDANT_NODES;\n            else if(str.find(\"REMOVE_REDUNDANT_NODES\")!=std::string::npos) options |= Optimizer::REMOVE_REDUNDANT_NODES;\n\n            if(str.find(\"~MERGE_GEOMETRY\")!=std::string::npos) options ^= Optimizer::MERGE_GEOMETRY;\n            else if(str.find(\"MERGE_GEOMETRY\")!=std::string::npos) options |= Optimizer::MERGE_GEOMETRY;\n        }\n        return options;\n    }\n\n    void SceneManager::shareState(osg::ref_ptr<osg::Node> node) {\n        mSharedStateMutex.lock();\n        mSharedStateManager->share(node.get());\n        mSharedStateMutex.unlock();\n    }\n\n    osg::ref_ptr<const osg::Node> SceneManager::getTemplate(const std::string &name, bool compile)\n    {\n        std::string normalized = name;\n        mVFS->normalizeFilename(normalized);\n\n        osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);\n        if (obj)\n            return osg::ref_ptr<const osg::Node>(static_cast<osg::Node*>(obj.get()));\n        else\n        {\n            osg::ref_ptr<osg::Node> loaded;\n            try\n            {\n                loaded = load(normalized, mVFS, mImageManager, mNifFileManager);\n            }\n            catch (std::exception& e)\n            {\n                static const char * const sMeshTypes[] = { \"nif\", \"osg\", \"osgt\", \"osgb\", \"osgx\", \"osg2\", \"dae\" };\n\n                for (unsigned int i=0; i<sizeof(sMeshTypes)/sizeof(sMeshTypes[0]); ++i)\n                {\n                    normalized = \"meshes/marker_error.\" + std::string(sMeshTypes[i]);\n                    if (mVFS->exists(normalized))\n                    {\n                        Log(Debug::Error) << \"Failed to load '\" << name << \"': \" << e.what() << \", using marker_error.\" << sMeshTypes[i] << \" instead\";\n                        loaded = load(normalized, mVFS, mImageManager, mNifFileManager);\n                        break;\n                    }\n                }\n\n                if (!loaded)\n                    throw;\n            }\n\n            // set filtering settings\n            SetFilterSettingsVisitor setFilterSettingsVisitor(mMinFilter, mMagFilter, mMaxAnisotropy);\n            loaded->accept(setFilterSettingsVisitor);\n            SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor(mMinFilter, mMagFilter, mMaxAnisotropy);\n            loaded->accept(setFilterSettingsControllerVisitor);\n\n            osg::ref_ptr<Shader::ShaderVisitor> shaderVisitor (createShaderVisitor());\n            loaded->accept(*shaderVisitor);\n\n            // share state\n            // do this before optimizing so the optimizer will be able to combine nodes more aggressively\n            // note, because StateSets will be shared at this point, StateSets can not be modified inside the optimizer\n            mSharedStateMutex.lock();\n            mSharedStateManager->share(loaded.get());\n            mSharedStateMutex.unlock();\n\n            if (canOptimize(normalized))\n            {\n                SceneUtil::Optimizer optimizer;\n                optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);\n\n                static const unsigned int options = getOptimizationOptions();\n\n                optimizer.optimize(loaded, options);\n            }\n\n            if (compile && mIncrementalCompileOperation)\n                mIncrementalCompileOperation->add(loaded);\n            else\n                loaded->getBound();\n\n            mCache->addEntryToObjectCache(normalized, loaded);\n            return loaded;\n        }\n    }\n\n    osg::ref_ptr<osg::Node> SceneManager::cacheInstance(const std::string &name)\n    {\n        std::string normalized = name;\n        mVFS->normalizeFilename(normalized);\n\n        osg::ref_ptr<osg::Node> node = createInstance(normalized);\n\n        // Note: osg::clone() does not calculate bound volumes.\n        // Do it immediately, otherwise we will need to update them for all objects\n        // during first update traversal, what may lead to stuttering during cell transitions\n        node->getBound();\n\n        mInstanceCache->addEntryToObjectCache(normalized, node.get());\n        return node;\n    }\n\n    osg::ref_ptr<osg::Node> SceneManager::createInstance(const std::string& name)\n    {\n        osg::ref_ptr<const osg::Node> scene = getTemplate(name);\n        return createInstance(scene);\n    }\n\n    osg::ref_ptr<osg::Node> SceneManager::createInstance(const osg::Node *base)\n    {\n        osg::ref_ptr<osg::Node> cloned = static_cast<osg::Node*>(base->clone(SceneUtil::CopyOp()));\n\n        // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache\n        cloned->getOrCreateUserDataContainer()->addUserObject(new TemplateRef(base));\n\n        // we can skip any scene graphs without update callbacks since we know that particle emitters will have an update callback set\n        if (cloned->getNumChildrenRequiringUpdateTraversal() > 0)\n        {\n            InitParticlesVisitor visitor (mParticleSystemMask);\n            cloned->accept(visitor);\n        }\n\n        return cloned;\n    }\n\n    osg::ref_ptr<osg::Node> SceneManager::getInstance(const std::string &name)\n    {\n        std::string normalized = name;\n        mVFS->normalizeFilename(normalized);\n\n        osg::ref_ptr<osg::Object> obj = mInstanceCache->takeFromObjectCache(normalized);\n        if (obj.get())\n            return static_cast<osg::Node*>(obj.get());\n\n        return createInstance(normalized);\n\n    }\n\n    osg::ref_ptr<osg::Node> SceneManager::getInstance(const std::string &name, osg::Group* parentNode)\n    {\n        osg::ref_ptr<osg::Node> cloned = getInstance(name);\n        attachTo(cloned, parentNode);\n        return cloned;\n    }\n\n    void SceneManager::attachTo(osg::Node *instance, osg::Group *parentNode) const\n    {\n        parentNode->addChild(instance);\n    }\n\n    void SceneManager::releaseGLObjects(osg::State *state)\n    {\n        mCache->releaseGLObjects(state);\n        mInstanceCache->releaseGLObjects(state);\n\n        mShaderManager->releaseGLObjects(state);\n\n        std::lock_guard<std::mutex> lock(mSharedStateMutex);\n        mSharedStateManager->releaseGLObjects(state);\n    }\n\n    void SceneManager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation *ico)\n    {\n        mIncrementalCompileOperation = ico;\n    }\n\n    osgUtil::IncrementalCompileOperation *SceneManager::getIncrementalCompileOperation()\n    {\n        return mIncrementalCompileOperation.get();\n    }\n\n    Resource::ImageManager* SceneManager::getImageManager()\n    {\n        return mImageManager;\n    }\n\n    void SceneManager::setParticleSystemMask(unsigned int mask)\n    {\n        mParticleSystemMask = mask;\n    }\n\n    void SceneManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter,\n                                           const std::string &mipmap, int maxAnisotropy)\n    {\n        osg::Texture::FilterMode min = osg::Texture::LINEAR;\n        osg::Texture::FilterMode mag = osg::Texture::LINEAR;\n\n        if(magfilter == \"nearest\")\n            mag = osg::Texture::NEAREST;\n        else if(magfilter != \"linear\")\n            Log(Debug::Warning) << \"Warning: Invalid texture mag filter: \"<< magfilter;\n\n        if(minfilter == \"nearest\")\n            min = osg::Texture::NEAREST;\n        else if(minfilter != \"linear\")\n            Log(Debug::Warning) << \"Warning: Invalid texture min filter: \"<< minfilter;\n\n        if(mipmap == \"nearest\")\n        {\n            if(min == osg::Texture::NEAREST)\n                min = osg::Texture::NEAREST_MIPMAP_NEAREST;\n            else if(min == osg::Texture::LINEAR)\n                min = osg::Texture::LINEAR_MIPMAP_NEAREST;\n        }\n        else if(mipmap != \"none\")\n        {\n            if(mipmap != \"linear\")\n                Log(Debug::Warning) << \"Warning: Invalid texture mipmap: \" << mipmap;\n            if(min == osg::Texture::NEAREST)\n                min = osg::Texture::NEAREST_MIPMAP_LINEAR;\n            else if(min == osg::Texture::LINEAR)\n                min = osg::Texture::LINEAR_MIPMAP_LINEAR;\n        }\n\n        mMinFilter = min;\n        mMagFilter = mag;\n        mMaxAnisotropy = std::max(1, maxAnisotropy);\n\n        SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor (mMinFilter, mMagFilter, mMaxAnisotropy);\n        SetFilterSettingsVisitor setFilterSettingsVisitor (mMinFilter, mMagFilter, mMaxAnisotropy);\n\n        mCache->accept(setFilterSettingsVisitor);\n        mCache->accept(setFilterSettingsControllerVisitor);\n    }\n\n    void SceneManager::applyFilterSettings(osg::Texture *tex)\n    {\n        tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter);\n        tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter);\n        tex->setMaxAnisotropy(mMaxAnisotropy);\n    }\n\n    void SceneManager::setUnRefImageDataAfterApply(bool unref)\n    {\n        mUnRefImageDataAfterApply = unref;\n    }\n\n    void SceneManager::updateCache(double referenceTime)\n    {\n        ResourceManager::updateCache(referenceTime);\n\n        mInstanceCache->removeUnreferencedObjectsInCache();\n\n        mSharedStateMutex.lock();\n        mSharedStateManager->prune();\n        mSharedStateMutex.unlock();\n\n        if (mIncrementalCompileOperation)\n        {\n            std::lock_guard<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());\n            osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile();\n            for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();)\n            {\n                int refcount = (*it)->_subgraphToCompile->referenceCount();\n                if ((*it)->_subgraphToCompile->asDrawable()) refcount -= 1; // ref by CompileList.\n                if (refcount <= 2) // ref by ObjectCache + ref by _subgraphToCompile.\n                {\n                    // no other ref = not needed anymore.\n                    it = sets.erase(it);\n                }\n                else\n                    ++it;\n            }\n        }\n    }\n\n    void SceneManager::clearCache()\n    {\n        ResourceManager::clearCache();\n\n        std::lock_guard<std::mutex> lock(mSharedStateMutex);\n        mSharedStateManager->clearCache();\n        mInstanceCache->clear();\n    }\n\n    void SceneManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n    {\n        if (mIncrementalCompileOperation)\n        {\n            std::lock_guard<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());\n            stats->setAttribute(frameNumber, \"Compiling\", mIncrementalCompileOperation->getToCompile().size());\n        }\n\n        {\n            std::lock_guard<std::mutex> lock(mSharedStateMutex);\n            stats->setAttribute(frameNumber, \"Texture\", mSharedStateManager->getNumSharedTextures());\n            stats->setAttribute(frameNumber, \"StateSet\", mSharedStateManager->getNumSharedStateSets());\n        }\n\n        stats->setAttribute(frameNumber, \"Node\", mCache->getCacheSize());\n        stats->setAttribute(frameNumber, \"Node Instance\", mInstanceCache->getCacheSize());\n    }\n\n    Shader::ShaderVisitor *SceneManager::createShaderVisitor(const std::string& shaderPrefix, bool translucentFramebuffer)\n    {\n        Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, shaderPrefix);\n        shaderVisitor->setForceShaders(mForceShaders);\n        shaderVisitor->setAutoUseNormalMaps(mAutoUseNormalMaps);\n        shaderVisitor->setNormalMapPattern(mNormalMapPattern);\n        shaderVisitor->setNormalHeightMapPattern(mNormalHeightMapPattern);\n        shaderVisitor->setAutoUseSpecularMaps(mAutoUseSpecularMaps);\n        shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);\n        shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps);\n        shaderVisitor->setConvertAlphaTestToAlphaToCoverage(mConvertAlphaTestToAlphaToCoverage);\n        shaderVisitor->setTranslucentFramebuffer(translucentFramebuffer);\n        return shaderVisitor;\n    }\n\n    std::string getFileExtension(const std::string& file)\n    {\n        size_t extPos = file.find_last_of('.');\n        if (extPos != std::string::npos && extPos+1 < file.size())\n            return file.substr(extPos+1);\n        return std::string();\n    }\n}\n"
  },
  {
    "path": "components/resource/scenemanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_SCENEMANAGER_H\n#define OPENMW_COMPONENTS_RESOURCE_SCENEMANAGER_H\n\n#include <string>\n#include <map>\n#include <memory>\n#include <mutex>\n\n#include <osg/ref_ptr>\n#include <osg/Node>\n#include <osg/Texture>\n\n#include \"resourcemanager.hpp\"\n\n#include <components/sceneutil/lightmanager.hpp>\n\nnamespace Resource\n{\n    class ImageManager;\n    class NifFileManager;\n    class SharedStateManager;\n}\n\nnamespace osgUtil\n{\n    class IncrementalCompileOperation;\n}\n\nnamespace osgDB\n{\n    class SharedStateManager;\n}\n\nnamespace Shader\n{\n    class ShaderManager;\n    class ShaderVisitor;\n}\n\nnamespace Resource\n{\n    class TemplateRef : public osg::Object\n    {\n    public:\n        TemplateRef(const Object* object) : mObject(object) {}\n        TemplateRef() {}\n        TemplateRef(const TemplateRef& copy, const osg::CopyOp&) : mObject(copy.mObject) {}\n\n        META_Object(Resource, TemplateRef)\n\n    private:\n        osg::ref_ptr<const Object> mObject;\n    };\n\n    class TemplateMultiRef : public osg::Object\n    {\n    public:\n        TemplateMultiRef() {}\n        TemplateMultiRef(const TemplateMultiRef& copy, const osg::CopyOp&) : mObjects(copy.mObjects) {}\n        void addRef(const osg::Node* node);\n\n        META_Object(Resource, TemplateMultiRef)\n\n    private:\n        std::vector<osg::ref_ptr<const Object>> mObjects;\n    };\n\n    class MultiObjectCache;\n\n    /// @brief Handles loading and caching of scenes, e.g. .nif files or .osg files\n    /// @note Some methods of the scene manager can be used from any thread, see the methods documentation for more details.\n    class SceneManager : public ResourceManager\n    {\n    public:\n        SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager);\n        ~SceneManager();\n\n        Shader::ShaderManager& getShaderManager();\n\n        /// Re-create shaders for this node, need to call this if alpha testing, texture stages or vertex color mode have changed.\n        void recreateShaders(osg::ref_ptr<osg::Node> node, const std::string& shaderPrefix = \"objects\", bool translucentFramebuffer = false, bool forceShadersForNode = false);\n\n        /// Applying shaders to a node may replace some fixed-function state.\n        /// This restores it.\n        /// When editing such state, it should be reinstated before the edits, and shaders should be recreated afterwards.\n        void reinstateRemovedState(osg::ref_ptr<osg::Node> node);\n\n        /// @see ShaderVisitor::setForceShaders\n        void setForceShaders(bool force);\n        bool getForceShaders() const;\n\n        void setClampLighting(bool clamp);\n        bool getClampLighting() const;\n\n        /// @see ShaderVisitor::setAutoUseNormalMaps\n        void setAutoUseNormalMaps(bool use);\n\n        /// @see ShaderVisitor::setNormalMapPattern\n        void setNormalMapPattern(const std::string& pattern);\n\n        /// @see ShaderVisitor::setNormalHeightMapPattern\n        void setNormalHeightMapPattern(const std::string& pattern);\n\n        void setAutoUseSpecularMaps(bool use);\n\n        void setSpecularMapPattern(const std::string& pattern);\n\n        void setApplyLightingToEnvMaps(bool apply);\n\n        void setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported);\n        bool isSupportedLightingMethod(SceneUtil::LightingMethod method) const;\n\n        void setLightingMethod(SceneUtil::LightingMethod method);\n        SceneUtil::LightingMethod getLightingMethod() const;\n        \n        void setConvertAlphaTestToAlphaToCoverage(bool convert);\n\n        void setShaderPath(const std::string& path);\n\n        /// Check if a given scene is loaded and if so, update its usage timestamp to prevent it from being unloaded\n        bool checkLoaded(const std::string& name, double referenceTime);\n\n        /// Get a read-only copy of this scene \"template\"\n        /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead.\n        ///  If even the error marker mesh can not be found, an exception is thrown.\n        /// @note Thread safe.\n        osg::ref_ptr<const osg::Node> getTemplate(const std::string& name, bool compile=true);\n\n        /// Create an instance of the given scene template and cache it for later use, so that future calls to getInstance() can simply\n        /// return this cached object instead of creating a new one.\n        /// @note The returned ref_ptr may be kept around by the caller to ensure that the object stays in cache for as long as needed.\n        /// @note Thread safe.\n        osg::ref_ptr<osg::Node> cacheInstance(const std::string& name);\n\n        osg::ref_ptr<osg::Node> createInstance(const std::string& name);\n\n        osg::ref_ptr<osg::Node> createInstance(const osg::Node* base);\n        void shareState(osg::ref_ptr<osg::Node> node);\n        /// Get an instance of the given scene template\n        /// @see getTemplate\n        /// @note Thread safe.\n        osg::ref_ptr<osg::Node> getInstance(const std::string& name);\n\n        /// Get an instance of the given scene template and immediately attach it to a parent node\n        /// @see getTemplate\n        /// @note Not thread safe, unless parentNode is not part of the main scene graph yet.\n        osg::ref_ptr<osg::Node> getInstance(const std::string& name, osg::Group* parentNode);\n\n        /// Attach the given scene instance to the given parent node\n        /// @note You should have the parentNode in its intended position before calling this method,\n        ///       so that world space particles of the \\a instance get transformed correctly.\n        /// @note Assumes the given instance was not attached to any parents before.\n        /// @note Not thread safe, unless parentNode is not part of the main scene graph yet.\n        void attachTo(osg::Node* instance, osg::Group* parentNode) const;\n\n        /// Manually release created OpenGL objects for the given graphics context. This may be required\n        /// in cases where multiple contexts are used over the lifetime of the application.\n        void releaseGLObjects(osg::State* state) override;\n\n        /// Set up an IncrementalCompileOperation for background compiling of loaded scenes.\n        void setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico);\n\n        osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation();\n\n        Resource::ImageManager* getImageManager();\n\n        /// @param mask The node mask to apply to loaded particle system nodes.\n        void setParticleSystemMask(unsigned int mask);\n\n        /// @warning It is unsafe to call this method while the draw thread is using textures! call Viewer::stopThreading first.\n        void setFilterSettings(const std::string &magfilter, const std::string &minfilter,\n                               const std::string &mipmap, int maxAnisotropy);\n\n        /// Apply filter settings to the given texture. Note, when loading an object through this scene manager (i.e. calling getTemplate or createInstance)\n        /// the filter settings are applied automatically. This method is provided for textures that were created outside of the SceneManager.\n        void applyFilterSettings (osg::Texture* tex);\n\n        /// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts,\n        /// otherwise should be disabled to reduce memory usage.\n        void setUnRefImageDataAfterApply(bool unref);\n\n        /// @see ResourceManager::updateCache\n        void updateCache(double referenceTime) override;\n\n        void clearCache() override;\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;\n\n    private:\n\n        Shader::ShaderVisitor* createShaderVisitor(const std::string& shaderPrefix = \"objects\", bool translucentFramebuffer = false);\n\n        std::unique_ptr<Shader::ShaderManager> mShaderManager;\n        bool mForceShaders;\n        bool mClampLighting;\n        bool mAutoUseNormalMaps;\n        std::string mNormalMapPattern;\n        std::string mNormalHeightMapPattern;\n        bool mAutoUseSpecularMaps;\n        std::string mSpecularMapPattern;\n        bool mApplyLightingToEnvMaps;\n        SceneUtil::LightingMethod mLightingMethod;\n        SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;\n        bool mConvertAlphaTestToAlphaToCoverage;\n\n        osg::ref_ptr<MultiObjectCache> mInstanceCache;\n\n        osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;\n        mutable std::mutex mSharedStateMutex;\n\n        Resource::ImageManager* mImageManager;\n        Resource::NifFileManager* mNifFileManager;\n\n        osg::Texture::FilterMode mMinFilter;\n        osg::Texture::FilterMode mMagFilter;\n        int mMaxAnisotropy;\n        bool mUnRefImageDataAfterApply;\n\n        osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;\n\n        unsigned int mParticleSystemMask;\n\n        SceneManager(const SceneManager&);\n        void operator = (const SceneManager&);\n    };\n\n    std::string getFileExtension(const std::string& file);\n}\n\n#endif\n"
  },
  {
    "path": "components/resource/stats.cpp",
    "content": "#include \"stats.hpp\"\n\n#include <sstream>\n#include <iomanip>\n#include <algorithm>\n\n#include <osg/PolygonMode>\n\n#include <osgText/Text>\n\n#include <osgDB/Registry>\n\n#include <osgViewer/Viewer>\n#include <osgViewer/Renderer>\n\n#include <components/myguiplatform/myguidatamanager.hpp>\n\nnamespace Resource\n{\n\nstatic bool collectStatRendering = false;\nstatic bool collectStatCameraObjects = false;\nstatic bool collectStatViewerObjects = false;\nstatic bool collectStatResource = false;\nstatic bool collectStatGPU = false;\nstatic bool collectStatEvent = false;\nstatic bool collectStatFrameRate = false;\nstatic bool collectStatUpdate = false;\nstatic bool collectStatEngine = false;\n\nstatic void setupStatCollection()\n{\n    const char* envList = getenv(\"OPENMW_OSG_STATS_LIST\");\n    if (envList == nullptr)\n        return;\n\n    std::string_view kwList(envList);\n\n    auto kwBegin = kwList.begin();\n\n    while (kwBegin != kwList.end())\n    {\n        auto kwEnd = std::find(kwBegin, kwList.end(), ';');\n\n        const auto kw = kwList.substr(std::distance(kwList.begin(), kwBegin), std::distance(kwBegin, kwEnd));\n\n        if (kw.compare(\"gpu\") == 0)\n            collectStatGPU = true;\n        else if (kw.compare(\"event\") == 0)\n            collectStatEvent = true;\n        else if (kw.compare(\"frame_rate\") == 0)\n            collectStatFrameRate = true;\n        else if (kw.compare(\"update\") == 0)\n            collectStatUpdate = true;\n        else if (kw.compare(\"engine\") == 0)\n            collectStatEngine = true;\n        else if (kw.compare(\"rendering\") == 0)\n            collectStatRendering = true;\n        else if (kw.compare(\"cameraobjects\") == 0)\n            collectStatCameraObjects = true;\n        else if (kw.compare(\"viewerobjects\") == 0)\n            collectStatViewerObjects = true;\n        else if (kw.compare(\"resource\") == 0)\n            collectStatResource = true;\n        else if (kw.compare(\"times\") == 0)\n        {\n            collectStatGPU = true;\n            collectStatEvent = true;\n            collectStatFrameRate = true;\n            collectStatUpdate = true;\n            collectStatEngine = true;\n            collectStatRendering = true;\n        }\n\n        if (kwEnd == kwList.end())\n            break;\n\n        kwBegin = std::next(kwEnd);\n    }\n}\n\nStatsHandler::StatsHandler(bool offlineCollect):\n    _key(osgGA::GUIEventAdapter::KEY_F4),\n    _initialized(false),\n    _statsType(false),\n    _offlineCollect(offlineCollect),\n    _statsWidth(1280.0f),\n    _statsHeight(1024.0f),\n    _font(\"\"),\n    _characterSize(18.0f)\n{\n    _camera = new osg::Camera;\n    _camera->getOrCreateStateSet()->setGlobalDefaults();\n    _camera->setRenderer(new osgViewer::Renderer(_camera.get()));\n    _camera->setProjectionResizePolicy(osg::Camera::FIXED);\n\n    _resourceStatsChildNum = 0;\n\n    if (osgDB::Registry::instance()->getReaderWriterForExtension(\"ttf\"))\n        _font = osgMyGUI::DataManager::getInstance().getDataPath(\"DejaVuLGCSansMono.ttf\");\n}\n\nProfiler::Profiler(bool offlineCollect):\n    _offlineCollect(offlineCollect)\n{\n    if (osgDB::Registry::instance()->getReaderWriterForExtension(\"ttf\"))\n        _font = osgMyGUI::DataManager::getInstance().getDataPath(\"DejaVuLGCSansMono.ttf\");\n    else\n        _font = \"\";\n\n    _characterSize = 18;\n\n    setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3);\n    setupStatCollection();\n}\n\nbool Profiler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)\n{\n    osgViewer::ViewerBase* viewer = nullptr;\n\n    bool handled = StatsHandler::handle(ea, aa);\n\n    auto* view = dynamic_cast<osgViewer::View*>(&aa);\n    if (view)\n        viewer = view->getViewerBase();\n\n    if (viewer)\n    {\n        // Add/remove openmw stats to the osd as necessary\n        viewer->getViewerStats()->collectStats(\"engine\", _statsType >= StatsHandler::StatsType::VIEWER_STATS);\n\n        if (_offlineCollect)\n            CollectStatistics(viewer);\n    }\n    return handled;\n}\n\nbool StatsHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)\n{\n    if (ea.getHandled()) return false;\n\n    switch(ea.getEventType())\n    {\n        case(osgGA::GUIEventAdapter::KEYDOWN):\n        {\n            if (ea.getKey()== _key)\n            {\n                osgViewer::View* myview = dynamic_cast<osgViewer::View*>(&aa);\n                if (!myview) return false;\n\n                osgViewer::ViewerBase* viewer = myview->getViewerBase();\n\n                toggle(viewer);\n\n                if (_offlineCollect)\n                    CollectStatistics(viewer);\n\n                aa.requestRedraw();\n                return true;\n            }\n            break;\n        }\n        case osgGA::GUIEventAdapter::RESIZE:\n        {\n            setWindowSize(ea.getWindowWidth(), ea.getWindowHeight());\n            break;\n        }\n        default:\n            break;\n    }\n    return false;\n}\n\nvoid StatsHandler::setWindowSize(int width, int height)\n{\n    if (width <= 0 || height <= 0)\n        return;\n\n    _camera->setViewport(0, 0, width, height);\n    if (fabs(height*_statsWidth) <= fabs(width*_statsHeight))\n    {\n        _camera->setProjectionMatrix(osg::Matrix::ortho2D(_statsWidth - width*_statsHeight/height, _statsWidth,0.0,_statsHeight));\n    }\n    else\n    {\n        _camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0,_statsWidth,_statsHeight-height*_statsWidth/width,_statsHeight));\n    }\n}\n\nvoid StatsHandler::toggle(osgViewer::ViewerBase *viewer)\n{\n    if (!_initialized)\n    {\n        setUpHUDCamera(viewer);\n        setUpScene(viewer);\n    }\n\n    _statsType = !_statsType;\n\n    if (!_statsType)\n    {\n        _camera->setNodeMask(0);\n        _switch->setAllChildrenOff();\n\n        viewer->getViewerStats()->collectStats(\"resource\", false);\n    }\n    else\n    {\n        _camera->setNodeMask(0xffffffff);\n        _switch->setSingleChildOn(_resourceStatsChildNum);\n\n        viewer->getViewerStats()->collectStats(\"resource\", true);\n    }\n}\n\nvoid StatsHandler::setUpHUDCamera(osgViewer::ViewerBase* viewer)\n{\n    // Try GraphicsWindow first so we're likely to get the main viewer window\n    osg::GraphicsContext* context = dynamic_cast<osgViewer::GraphicsWindow*>(_camera->getGraphicsContext());\n\n    if (!context)\n    {\n        osgViewer::Viewer::Windows windows;\n        viewer->getWindows(windows);\n\n        if (!windows.empty()) context = windows.front();\n        else\n        {\n            // No GraphicsWindows were found, so let's try to find a GraphicsContext\n            context = _camera->getGraphicsContext();\n\n            if (!context)\n            {\n                osgViewer::Viewer::Contexts contexts;\n                viewer->getContexts(contexts);\n\n                if (contexts.empty()) return;\n\n                context = contexts.front();\n            }\n        }\n    }\n\n    _camera->setGraphicsContext(context);\n\n    _camera->setRenderOrder(osg::Camera::POST_RENDER, 11);\n\n    _camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);\n    _camera->setViewMatrix(osg::Matrix::identity());\n    setWindowSize(context->getTraits()->width, context->getTraits()->height);\n\n    // only clear the depth buffer\n    _camera->setClearMask(0);\n    _camera->setAllowEventFocus(false);\n\n    _camera->setRenderer(new osgViewer::Renderer(_camera.get()));\n\n    _initialized = true;\n}\n\nosg::Geometry* createBackgroundRectangle(const osg::Vec3& pos, const float width, const float height, osg::Vec4& color)\n{\n    osg::StateSet *ss = new osg::StateSet;\n\n    osg::Geometry* geometry = new osg::Geometry;\n\n    geometry->setUseDisplayList(false);\n    geometry->setStateSet(ss);\n\n    osg::Vec3Array* vertices = new osg::Vec3Array;\n    geometry->setVertexArray(vertices);\n\n    vertices->push_back(osg::Vec3(pos.x(), pos.y(), 0));\n    vertices->push_back(osg::Vec3(pos.x(), pos.y()-height,0));\n    vertices->push_back(osg::Vec3(pos.x()+width, pos.y()-height,0));\n    vertices->push_back(osg::Vec3(pos.x()+width, pos.y(),0));\n\n    osg::Vec4Array* colors = new osg::Vec4Array;\n    colors->push_back(color);\n    geometry->setColorArray(colors, osg::Array::BIND_OVERALL);\n\n    osg::DrawElementsUShort *base =  new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_FAN,0);\n    base->push_back(0);\n    base->push_back(1);\n    base->push_back(2);\n    base->push_back(3);\n\n    geometry->addPrimitiveSet(base);\n\n    return geometry;\n}\n\nclass ResourceStatsTextDrawCallback : public osg::Drawable::DrawCallback\n{\npublic:\n    ResourceStatsTextDrawCallback(osg::Stats* stats, const std::vector<std::string>& statNames)\n        : mStats(stats)\n        , mStatNames(statNames)\n    {\n    }\n\n    void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const override\n    {\n        if (!mStats) return;\n\n        osgText::Text* text = (osgText::Text*)(drawable);\n\n        std::ostringstream viewStr;\n        viewStr.setf(std::ios::left, std::ios::adjustfield);\n        viewStr.width(14);\n        // Used fixed formatting, as scientific will switch to \"...e+..\" notation for\n        // large numbers of vertices/drawables/etc.\n        viewStr.setf(std::ios::fixed);\n        viewStr.precision(0);\n\n        unsigned int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber()-1;\n\n        for (const auto& statName : mStatNames.get())\n        {\n            if (statName.empty())\n                viewStr << std::endl;\n            else\n            {\n                double value = 0.0;\n                if (mStats->getAttribute(frameNumber, statName, value))\n                    viewStr << std::setw(8) << value << std::endl;\n                else\n                    viewStr << std::setw(8) << \".\" << std::endl;\n            }\n        }\n\n        text->setText(viewStr.str());\n\n        text->drawImplementation(renderInfo);\n    }\n\n    osg::ref_ptr<osg::Stats> mStats;\n    std::reference_wrapper<const std::vector<std::string>> mStatNames;\n};\n\nvoid StatsHandler::setUpScene(osgViewer::ViewerBase *viewer)\n{\n    _switch = new osg::Switch;\n\n    _camera->addChild(_switch);\n\n    osg::StateSet* stateset = _switch->getOrCreateStateSet();\n    stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);\n    stateset->setMode(GL_BLEND,osg::StateAttribute::ON);\n    stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);\n#ifdef OSG_GL1_AVAILABLE\n    stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED);\n#endif\n\n    osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3);\n    osg::Vec4 staticTextColor(1.0, 1.0, 0.0f, 1.0);\n    osg::Vec4 dynamicTextColor(1.0, 1.0, 1.0f, 1.0);\n    float backgroundMargin = 5;\n    float backgroundSpacing = 3;\n\n    // resource stats\n    {\n        osg::Group* group = new osg::Group;\n        group->setCullingActive(false);\n        _resourceStatsChildNum = _switch->getNumChildren();\n        _switch->addChild(group, false);\n\n        static const std::vector<std::string> statNames({\n            \"FrameNumber\",\n            \"\",\n            \"Compiling\",\n            \"UnrefQueue\",\n            \"WorkQueue\",\n            \"WorkThread\",\n            \"\",\n            \"Texture\",\n            \"StateSet\",\n            \"Node\",\n            \"Node Instance\",\n            \"Shape\",\n            \"Shape Instance\",\n            \"Image\",\n            \"Nif\",\n            \"Keyframe\",\n            \"\",\n            \"Groundcover Chunk\",\n            \"Object Chunk\",\n            \"Terrain Chunk\",\n            \"Terrain Texture\",\n            \"Land\",\n            \"Composite\",\n            \"\",\n            \"NavMesh UpdateJobs\",\n            \"NavMesh CacheSize\",\n            \"NavMesh UsedTiles\",\n            \"NavMesh CachedTiles\",\n            \"NavMesh CacheHitRate\",\n            \"\",\n            \"Mechanics Actors\",\n            \"Mechanics Objects\",\n            \"\",\n            \"Physics Actors\",\n            \"Physics Objects\",\n            \"Physics HeightFields\",\n        });\n\n        static const auto longest = std::max_element(statNames.begin(), statNames.end(),\n            [] (const std::string& lhs, const std::string& rhs) { return lhs.size() < rhs.size(); });\n        const float statNamesWidth = 13 * _characterSize + 2 * backgroundMargin;\n        const float statTextWidth = 7 * _characterSize + 2 * backgroundMargin;\n        const float statHeight = statNames.size() * _characterSize + 2 * backgroundMargin;\n        osg::Vec3 pos(_statsWidth - statNamesWidth - backgroundSpacing - statTextWidth, statHeight, 0.0f);\n\n        group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0),\n                                                        statNamesWidth,\n                                                        statHeight,\n                                                        backgroundColor));\n\n        osg::ref_ptr<osgText::Text> staticText = new osgText::Text;\n        group->addChild( staticText.get() );\n        staticText->setColor(staticTextColor);\n        staticText->setFont(_font);\n        staticText->setCharacterSize(_characterSize);\n        staticText->setPosition(pos);\n\n        std::ostringstream viewStr;\n        viewStr.clear();\n        viewStr.setf(std::ios::left, std::ios::adjustfield);\n        viewStr.width(longest->size());\n        for (const auto& statName : statNames)\n        {\n            viewStr << statName << std::endl;\n        }\n\n        staticText->setText(viewStr.str());\n\n        pos.x() += statNamesWidth + backgroundSpacing;\n\n        group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0),\n                                                        statTextWidth,\n                                                        statHeight,\n                                                        backgroundColor));\n\n        osg::ref_ptr<osgText::Text> statsText = new osgText::Text;\n        group->addChild( statsText.get() );\n\n        statsText->setColor(dynamicTextColor);\n        statsText->setFont(_font);\n        statsText->setCharacterSize(_characterSize);\n        statsText->setPosition(pos);\n        statsText->setText(\"\");\n        statsText->setDrawCallback(new ResourceStatsTextDrawCallback(viewer->getViewerStats(), statNames));\n    }\n}\n\n\nvoid StatsHandler::getUsage(osg::ApplicationUsage &usage) const\n{\n    usage.addKeyboardMouseBinding(_key, \"On screen resource usage stats.\");\n}\n\nvoid CollectStatistics(osgViewer::ViewerBase* viewer)\n{\n    osgViewer::Viewer::Cameras cameras;\n    viewer->getCameras(cameras);\n    for (auto* camera : cameras)\n    {\n        if (collectStatGPU)           camera->getStats()->collectStats(\"gpu\", true);\n        if (collectStatRendering)     camera->getStats()->collectStats(\"rendering\", true);\n        if (collectStatCameraObjects) camera->getStats()->collectStats(\"scene\", true);\n    }\n    if (collectStatEvent)         viewer->getViewerStats()->collectStats(\"event\", true);\n    if (collectStatFrameRate)     viewer->getViewerStats()->collectStats(\"frame_rate\", true);\n    if (collectStatUpdate)        viewer->getViewerStats()->collectStats(\"update\", true);\n    if (collectStatResource)      viewer->getViewerStats()->collectStats(\"resource\", true);\n    if (collectStatViewerObjects) viewer->getViewerStats()->collectStats(\"scene\", true);\n    if (collectStatEngine)        viewer->getViewerStats()->collectStats(\"engine\", true);\n}\n\n}\n"
  },
  {
    "path": "components/resource/stats.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_STATS_H\n#define OPENMW_COMPONENTS_RESOURCE_STATS_H\n\n#include <osgViewer/ViewerEventHandlers>\n\nnamespace osgViewer\n{\n    class ViewerBase;\n}\n\nnamespace osg\n{\n    class Switch;\n}\n\nnamespace Resource\n{\n    class Profiler : public osgViewer::StatsHandler\n    {\n    public:\n        Profiler(bool offlineCollect);\n        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override;\n\n    private:\n        bool _offlineCollect;\n    };\n\n    class StatsHandler : public osgGA::GUIEventHandler\n    {\n    public:\n        StatsHandler(bool offlineCollect);\n\n        void setKey(int key) { _key = key; }\n        int getKey() const { return _key; }\n\n        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override;\n\n        void setWindowSize(int w, int h);\n\n        void toggle(osgViewer::ViewerBase* viewer);\n\n        void setUpHUDCamera(osgViewer::ViewerBase* viewer);\n        void setUpScene(osgViewer::ViewerBase* viewer);\n\n        /** Get the keyboard and mouse usage of this manipulator.*/\n        void getUsage(osg::ApplicationUsage& usage) const override;\n\n    private:\n        osg::ref_ptr<osg::Switch> _switch;\n        int _key;\n        osg::ref_ptr<osg::Camera>  _camera;\n        bool _initialized;\n        bool _statsType;\n        bool _offlineCollect;\n\n        float                               _statsWidth;\n        float                               _statsHeight;\n\n        std::string                         _font;\n        float                               _characterSize;\n\n        int _resourceStatsChildNum;\n\n    };\n\n    void CollectStatistics(osgViewer::ViewerBase* viewer);\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/actorutil.cpp",
    "content": "#include \"actorutil.hpp\"\n\n#include <components/settings/settings.hpp>\n\nnamespace SceneUtil\n{\n    std::string getActorSkeleton(bool firstPerson, bool isFemale, bool isBeast, bool isWerewolf)\n    {\n        if (!firstPerson)\n        {\n            if (isWerewolf)\n                return Settings::Manager::getString(\"wolfskin\", \"Models\");\n            else if (isBeast)\n                return Settings::Manager::getString(\"baseanimkna\", \"Models\");\n            else if (isFemale)\n                return Settings::Manager::getString(\"baseanimfemale\", \"Models\");\n            else\n                return Settings::Manager::getString(\"baseanim\", \"Models\");\n        }\n        else\n        {\n            if (isWerewolf)\n                return Settings::Manager::getString(\"wolfskin1st\", \"Models\");\n            else if (isBeast)\n                return Settings::Manager::getString(\"baseanimkna1st\", \"Models\");\n            else if (isFemale)\n                return Settings::Manager::getString(\"baseanimfemale1st\", \"Models\");\n            else\n                return Settings::Manager::getString(\"xbaseanim1st\", \"Models\");\n        }\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/actorutil.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP\n#define OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP\n\n#include <string>\n\nnamespace SceneUtil\n{\n    std::string getActorSkeleton(bool firstPerson, bool female, bool beast, bool werewolf);\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/agentpath.cpp",
    "content": "#include \"agentpath.hpp\"\n#include \"detourdebugdraw.hpp\"\n\n#include <components/detournavigator/settings.hpp>\n\n#include <algorithm>\n\nnamespace\n{\n    void drawAgent(duDebugDraw& debugDraw, const osg::Vec3f& pos, const float radius, const float height,\n            const float climb, const unsigned color)\n    {\n        debugDraw.depthMask(false);\n\n        duDebugDrawCylinderWire(&debugDraw, pos.x() - radius, pos.z() + 0.02f, pos.y() - radius, pos.x() + radius,\n            pos.z() + height, pos.y() + radius, color, radius * 0.2f);\n\n        duDebugDrawCircle(&debugDraw, pos.x(), pos.z() + climb, pos.y(), radius, duRGBA(0, 0 , 0, 64), radius * 0.1f);\n\n        const auto colb = duRGBA(0, 0, 0, 196);\n        debugDraw.begin(DU_DRAW_LINES);\n        debugDraw.vertex(pos.x(), pos.z() - climb, pos.y(), colb);\n        debugDraw.vertex(pos.x(), pos.z() + climb, pos.y(), colb);\n        debugDraw.vertex(pos.x() - radius / 2, pos.z() + 0.02f, pos.y(), colb);\n        debugDraw.vertex(pos.x() + radius / 2, pos.z() + 0.02f, pos.y(), colb);\n        debugDraw.vertex(pos.x(), pos.z() + 0.02f, pos.y() - radius / 2, colb);\n        debugDraw.vertex(pos.x(), pos.z() + 0.02f, pos.y() + radius / 2, colb);\n        debugDraw.end();\n\n        debugDraw.depthMask(true);\n    }\n}\n\nnamespace SceneUtil\n{\n    osg::ref_ptr<osg::Group> createAgentPathGroup(const std::deque<osg::Vec3f>& path,\n            const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end,\n            const DetourNavigator::Settings& settings)\n    {\n        using namespace DetourNavigator;\n\n        const osg::ref_ptr<osg::Group> group(new osg::Group);\n\n        DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 0), 1);\n\n        const auto agentRadius = halfExtents.x();\n        const auto agentHeight = 2.0f * halfExtents.z();\n        const auto agentClimb = settings.mMaxClimb;\n        const auto startColor = duRGBA(128, 25, 0, 192);\n        const auto endColor = duRGBA(51, 102, 0, 129);\n\n        drawAgent(debugDraw, start, agentRadius, agentHeight, agentClimb, startColor);\n        drawAgent(debugDraw, end, agentRadius, agentHeight, agentClimb, endColor);\n\n        const auto pathColor = duRGBA(0, 0, 0, 220);\n\n        debugDraw.depthMask(false);\n\n        debugDraw.begin(osg::PrimitiveSet::LINE_STRIP, agentRadius * 0.5f);\n        debugDraw.vertex(osg::Vec3f(start.x(), start.z() + agentClimb, start.y()).ptr(), startColor);\n        std::for_each(path.begin(), path.end(),\n            [&] (const osg::Vec3f& v) { debugDraw.vertex(osg::Vec3f(v.x(), v.z() + agentClimb, v.y()).ptr(), pathColor); });\n        debugDraw.vertex(osg::Vec3f(end.x(), end.z() + agentClimb, end.y()).ptr(), endColor);\n        debugDraw.end();\n\n        debugDraw.depthMask(true);\n\n        return group;\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/agentpath.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_AGENTPATH_H\n#define OPENMW_COMPONENTS_SCENEUTIL_AGENTPATH_H\n\n#include <osg/ref_ptr>\n\n#include <deque>\n\nnamespace osg\n{\n    class Group;\n    class Vec3f;\n}\n\nnamespace DetourNavigator\n{\n    struct Settings;\n}\n\nnamespace SceneUtil\n{\n    osg::ref_ptr<osg::Group> createAgentPathGroup(const std::deque<osg::Vec3f>& path,\n            const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end,\n            const DetourNavigator::Settings& settings);\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/attach.cpp",
    "content": "#include \"attach.hpp\"\n\n#include <stdexcept>\n\n#include <osg/NodeVisitor>\n#include <osg/Group>\n#include <osg/Geometry>\n#include <osg/FrontFace>\n#include <osg/PositionAttitudeTransform>\n#include <osg/MatrixTransform>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n\n#include <components/sceneutil/skeleton.hpp>\n\n#include \"visitor.hpp\"\n\nnamespace SceneUtil\n{\n\n    class CopyRigVisitor : public osg::NodeVisitor\n    {\n    public:\n        CopyRigVisitor(osg::ref_ptr<osg::Group> parent, const std::string& filter)\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mParent(parent)\n            , mFilter(Misc::StringUtils::lowerCase(filter))\n        {\n            mFilter2 = \"tri \" + mFilter;\n        }\n\n        void apply(osg::MatrixTransform& node) override\n        {\n            traverse(node);\n        }\n        void apply(osg::Node& node) override\n        {\n            traverse(node);\n        }\n        void apply(osg::Group& node) override\n        {\n            traverse(node);\n        }\n\n        void apply(osg::Drawable& drawable) override\n        {\n            if (!filterMatches(drawable.getName()))\n                return;\n\n            osg::Node* node = &drawable;\n            while (node->getNumParents())\n            {\n                osg::Group* parent = node->getParent(0);\n                if (!parent || !filterMatches(parent->getName()))\n                    break;\n                node = parent;\n            }\n            mToCopy.emplace(node);\n        }\n\n        void doCopy()\n        {\n            for (const osg::ref_ptr<osg::Node>& node : mToCopy)\n            {\n                if (node->getNumParents() > 1)\n                    Log(Debug::Error) << \"Error CopyRigVisitor: node has \" << node->getNumParents() << \" parents\";\n                while (node->getNumParents())\n                    node->getParent(0)->removeChild(node);\n\n                mParent->addChild(node);\n            }\n            mToCopy.clear();\n        }\n\n    private:\n\n        bool filterMatches(const std::string& name) const\n        {\n            std::string lowerName = Misc::StringUtils::lowerCase(name);\n            return (lowerName.size() >= mFilter.size() && lowerName.compare(0, mFilter.size(), mFilter) == 0)\n                || (lowerName.size() >= mFilter2.size() && lowerName.compare(0, mFilter2.size(), mFilter2) == 0);\n        }\n\n        using NodeSet = std::set<osg::ref_ptr<osg::Node>>;\n        NodeSet mToCopy;\n\n        osg::ref_ptr<osg::Group> mParent;\n        std::string mFilter;\n        std::string mFilter2;\n    };\n\n    void mergeUserData(osg::UserDataContainer* source, osg::Object* target)\n    {\n        if (!target->getUserDataContainer())\n            target->setUserDataContainer(source);\n        else\n        {\n            for (unsigned int i=0; i<source->getNumUserObjects(); ++i)\n                target->getUserDataContainer()->addUserObject(source->getUserObject(i));\n        }\n    }\n\n    osg::ref_ptr<osg::Node> attach(osg::ref_ptr<osg::Node> toAttach, osg::Node *master, const std::string &filter, osg::Group* attachNode)\n    {\n        if (dynamic_cast<SceneUtil::Skeleton*>(toAttach.get()))\n        {\n            osg::ref_ptr<osg::Group> handle = new osg::Group;\n\n            CopyRigVisitor copyVisitor(handle, filter);\n            toAttach->accept(copyVisitor);\n            copyVisitor.doCopy();\n\n            if (handle->getNumChildren() == 1)\n            {\n                osg::ref_ptr<osg::Node> newHandle = handle->getChild(0);\n                handle->removeChild(newHandle);\n                master->asGroup()->addChild(newHandle);\n                mergeUserData(toAttach->getUserDataContainer(), newHandle);\n                return newHandle;\n            }\n            else\n            {\n                master->asGroup()->addChild(handle);\n                handle->setUserDataContainer(toAttach->getUserDataContainer());\n                return handle;\n            }\n        }\n        else\n        {\n            FindByNameVisitor findBoneOffset(\"BoneOffset\");\n            toAttach->accept(findBoneOffset);\n\n            osg::ref_ptr<osg::PositionAttitudeTransform> trans;\n\n            if (findBoneOffset.mFoundNode)\n            {\n                osg::MatrixTransform* boneOffset = dynamic_cast<osg::MatrixTransform*>(findBoneOffset.mFoundNode);\n                if (!boneOffset)\n                    throw std::runtime_error(\"BoneOffset must be a MatrixTransform\");\n\n                trans = new osg::PositionAttitudeTransform;\n                trans->setPosition(boneOffset->getMatrix().getTrans());\n                // The BoneOffset rotation seems to be incorrect\n                trans->setAttitude(osg::Quat(osg::DegreesToRadians(-90.f), osg::Vec3f(1,0,0)));\n\n                // Now that we used it, get rid of the redundant node.\n                if (boneOffset->getNumChildren() == 0 && boneOffset->getNumParents() == 1)\n                    boneOffset->getParent(0)->removeChild(boneOffset);\n            }\n\n            if (attachNode->getName().find(\"Left\") != std::string::npos)\n            {\n                if (!trans)\n                    trans = new osg::PositionAttitudeTransform;\n                trans->setScale(osg::Vec3f(-1.f, 1.f, 1.f));\n\n                // Need to invert culling because of the negative scale\n                // Note: for absolute correctness we would need to check the current front face for every mesh then invert it\n                // However MW isn't doing this either, so don't. Assuming all meshes are using backface culling is more efficient.\n                static osg::ref_ptr<osg::StateSet> frontFaceStateSet;\n                if (!frontFaceStateSet)\n                {\n                    frontFaceStateSet = new osg::StateSet;\n                    osg::FrontFace* frontFace = new osg::FrontFace;\n                    frontFace->setMode(osg::FrontFace::CLOCKWISE);\n                    frontFaceStateSet->setAttributeAndModes(frontFace, osg::StateAttribute::ON);\n                }\n                trans->setStateSet(frontFaceStateSet);\n            }\n\n            if (trans)\n            {\n                attachNode->addChild(trans);\n                trans->addChild(toAttach);\n                return trans;\n            }\n            else\n            {\n                attachNode->addChild(toAttach);\n                return toAttach;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "components/sceneutil/attach.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_ATTACH_H\n#define OPENMW_COMPONENTS_SCENEUTIL_ATTACH_H\n\n#include <string>\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class Node;\n    class Group;\n}\n\nnamespace SceneUtil\n{\n\n    /// Attach parts of the \\a toAttach scenegraph to the \\a master scenegraph, using the specified filter and attachment node.\n    /// If the \\a toAttach scene graph contains skinned objects, we will attach only those (filtered by the \\a filter).\n    /// Otherwise, just attach all of the toAttach scenegraph to the attachment node on the master scenegraph, with no filtering.\n    /// @note The master scene graph is expected to include a skeleton.\n    /// @return A newly created node that is directly attached to the master scene graph\n    osg::ref_ptr<osg::Node> attach(osg::ref_ptr<osg::Node> toAttach, osg::Node* master, const std::string& filter, osg::Group* attachNode);\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/clone.cpp",
    "content": "#include \"clone.hpp\"\n\n#include <osg/StateSet>\n\n#include <osgAnimation/Bone>\n#include <osgAnimation/Skeleton>\n#include <osgAnimation/MorphGeometry>\n#include <osgAnimation/RigGeometry>\n\n#include <osgParticle/ParticleProcessor>\n#include <osgParticle/ParticleSystemUpdater>\n#include <osgParticle/Emitter>\n\n#include <components/sceneutil/morphgeometry.hpp>\n#include <components/sceneutil/riggeometry.hpp>\n\nnamespace SceneUtil\n{\n\n    CopyOp::CopyOp()\n    {\n        setCopyFlags(osg::CopyOp::DEEP_COPY_NODES\n                     // Controller might need different inputs per scene instance\n                     | osg::CopyOp::DEEP_COPY_CALLBACKS\n                     | osg::CopyOp::DEEP_COPY_USERDATA);\n    }\n\n    osg::Node* CopyOp::operator ()(const osg::Node* node) const\n    {\n        if (const osgParticle::ParticleProcessor* processor = dynamic_cast<const osgParticle::ParticleProcessor*>(node))\n            return operator()(processor);\n        if (const osgParticle::ParticleSystemUpdater* updater = dynamic_cast<const osgParticle::ParticleSystemUpdater*>(node))\n        {\n            osgParticle::ParticleSystemUpdater* cloned = new osgParticle::ParticleSystemUpdater(*updater, osg::CopyOp::SHALLOW_COPY);\n            mUpdaterToOldPs[cloned] = updater->getParticleSystem(0);\n            return cloned;\n        }\n\n        if (dynamic_cast<const osgAnimation::Bone*>(node) || dynamic_cast<const osgAnimation::Skeleton*>(node))\n        {\n            return osg::clone(node, *this);\n        }\n        return osg::CopyOp::operator()(node);\n    }\n\n    osg::Drawable* CopyOp::operator ()(const osg::Drawable* drawable) const\n    {\n        if (const osgParticle::ParticleSystem* partsys = dynamic_cast<const osgParticle::ParticleSystem*>(drawable))\n            return operator()(partsys);\n\n        if (dynamic_cast<const SceneUtil::RigGeometry*>(drawable) || dynamic_cast<const SceneUtil::MorphGeometry*>(drawable) || dynamic_cast<const osgAnimation::RigGeometry*>(drawable) || dynamic_cast<const osgAnimation::MorphGeometry*>(drawable))\n        {\n            return static_cast<osg::Drawable*>(drawable->clone(*this));\n        }\n\n        return osg::CopyOp::operator()(drawable);\n    }\n\n    osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const\n    {\n        osgParticle::ParticleProcessor* cloned = static_cast<osgParticle::ParticleProcessor*>(processor->clone(osg::CopyOp::DEEP_COPY_CALLBACKS));\n        for (const auto& oldPsNewPsPair : mOldPsToNewPs)\n        {\n            if (processor->getParticleSystem() == oldPsNewPsPair.first)\n            {\n                cloned->setParticleSystem(oldPsNewPsPair.second);\n                return cloned;\n            }\n        }\n\n        mProcessorToOldPs[cloned] = processor->getParticleSystem();\n        return cloned;\n    }\n\n    osgParticle::ParticleSystem* CopyOp::operator ()(const osgParticle::ParticleSystem* partsys) const\n    {\n        osgParticle::ParticleSystem* cloned = static_cast<osgParticle::ParticleSystem*>(partsys->clone(*this));\n\n        for (const auto& processorPsPair : mProcessorToOldPs)\n        {\n            if (processorPsPair.second == partsys)\n            {\n                processorPsPair.first->setParticleSystem(cloned);\n            }\n        }\n        for (const auto& updaterPsPair : mUpdaterToOldPs)\n        {\n            if (updaterPsPair.second == partsys)\n            {\n                osgParticle::ParticleSystemUpdater* updater = updaterPsPair.first;\n                updater->removeParticleSystem(updater->getParticleSystem(0));\n                updater->addParticleSystem(cloned);\n            }\n        }\n        // In rare situations a particle processor may be placed after the particle system in the scene graph.\n        mOldPsToNewPs[partsys] = cloned;\n\n        return cloned;\n    }\n\n}\n"
  },
  {
    "path": "components/sceneutil/clone.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_CLONE_H\n#define OPENMW_COMPONENTS_SCENEUTIL_CLONE_H\n\n#include <map>\n\n#include <osg/CopyOp>\n\nnamespace osgParticle\n{\n    class ParticleProcessor;\n    class ParticleSystem;\n    class ParticleSystemUpdater;\n}\n\nnamespace SceneUtil\n{\n\n    /// @par Defines the cloning behaviour we need:\n    /// * Assigns updated ParticleSystem pointers on cloned emitters and programs.\n    /// * Deep copies RigGeometry and MorphGeometry so they can animate without affecting clones.\n    /// @warning Do not use an object of this class for more than one copy operation.\n    class CopyOp : public osg::CopyOp\n    {\n    public:\n        CopyOp();\n\n        virtual osgParticle::ParticleSystem* operator() (const osgParticle::ParticleSystem* partsys) const;\n        virtual osgParticle::ParticleProcessor* operator() (const osgParticle::ParticleProcessor* processor) const;\n\n        osg::Node* operator() (const osg::Node* node) const override;\n        osg::Drawable* operator() (const osg::Drawable* drawable) const override;\n\n    private:\n        // maps new pointers to their old pointers\n        // a little messy, but I think this should be the most efficient way\n        mutable std::map<osgParticle::ParticleProcessor*, const osgParticle::ParticleSystem*> mProcessorToOldPs;\n        mutable std::map<osgParticle::ParticleSystemUpdater*, const osgParticle::ParticleSystem*> mUpdaterToOldPs;\n        mutable std::map<const osgParticle::ParticleSystem*, osgParticle::ParticleSystem*> mOldPsToNewPs;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/controller.cpp",
    "content": "#include \"controller.hpp\"\n\n#include <algorithm>\n\n#include \"statesetupdater.hpp\"\n\n#include <osg/Drawable>\n#include <osg/Geometry>\n#include <osg/MatrixTransform>\n#include <osg/NodeCallback>\n\nnamespace SceneUtil\n{\n\n\n    Controller::Controller()\n    {\n    }\n\n    bool Controller::hasInput() const\n    {\n        return mSource.get() != nullptr;\n    }\n\n    float Controller::getInputValue(osg::NodeVisitor* nv)\n    {\n        if (mFunction)\n            return mFunction->calculate(mSource->getValue(nv));\n        else\n            return mSource->getValue(nv);\n    }\n\n    void Controller::setSource(std::shared_ptr<ControllerSource> source)\n    {\n        mSource = source;\n    }\n\n    void Controller::setFunction(std::shared_ptr<ControllerFunction> function)\n    {\n        mFunction = function;\n    }\n\n    std::shared_ptr<ControllerSource> Controller::getSource() const\n    {\n        return mSource;\n    }\n\n    std::shared_ptr<ControllerFunction> Controller::getFunction() const\n    {\n        return mFunction;\n    }\n\n    FrameTimeSource::FrameTimeSource()\n    {\n    }\n\n    float FrameTimeSource::getValue(osg::NodeVisitor *nv)\n    {\n        return nv->getFrameStamp()->getSimulationTime();\n    }\n\n    ControllerVisitor::ControllerVisitor()\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n    {\n\n    }\n\n    void ControllerVisitor::apply(osg::Node &node)\n    {\n        applyNode(node);\n    }\n\n    void ControllerVisitor::apply(osg::MatrixTransform &node)\n    {\n        applyNode(node);\n    }\n\n    void ControllerVisitor::apply(osg::Geometry &node)\n    {\n        applyNode(node);\n    }\n\n    void ControllerVisitor::applyNode(osg::Node &node)\n    {\n        osg::Callback* callback = node.getUpdateCallback();\n        while (callback)\n        {\n            if (Controller* ctrl = dynamic_cast<Controller*>(callback))\n                visit(node, *ctrl);\n            if (CompositeStateSetUpdater* composite = dynamic_cast<CompositeStateSetUpdater*>(callback))\n            {\n                for (unsigned int i=0; i<composite->getNumControllers(); ++i)\n                {\n                    StateSetUpdater* statesetcontroller = composite->getController(i);\n                    if (Controller* ctrl = dynamic_cast<Controller*>(statesetcontroller))\n                        visit(node, *ctrl);\n                }\n            }\n\n            callback = callback->getNestedCallback();\n        }\n\n        if (node.getNumChildrenRequiringUpdateTraversal() > 0)\n            traverse(node);\n    }\n\n    AssignControllerSourcesVisitor::AssignControllerSourcesVisitor()\n        : ControllerVisitor()\n    {\n    }\n\n    AssignControllerSourcesVisitor::AssignControllerSourcesVisitor(std::shared_ptr<ControllerSource> toAssign)\n        : ControllerVisitor()\n        , mToAssign(toAssign)\n    {\n    }\n\n    void AssignControllerSourcesVisitor::visit(osg::Node&, Controller &ctrl)\n    {\n        if (!ctrl.getSource())\n            ctrl.setSource(mToAssign);\n    }\n\n    FindMaxControllerLengthVisitor::FindMaxControllerLengthVisitor()\n        : SceneUtil::ControllerVisitor()\n        , mMaxLength(0)\n    {\n    }\n\n    void FindMaxControllerLengthVisitor::visit(osg::Node &, Controller &ctrl)\n    {\n        if (ctrl.getFunction())\n            mMaxLength = std::max(mMaxLength, ctrl.getFunction()->getMaximum());\n    }\n\n    float FindMaxControllerLengthVisitor::getMaxLength() const\n    {\n        return mMaxLength;\n    }\n\n}\n"
  },
  {
    "path": "components/sceneutil/controller.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_CONTROLLER_H\n#define OPENMW_COMPONENTS_SCENEUTIL_CONTROLLER_H\n\n#include <memory>\n#include <osg/NodeVisitor>\n\nnamespace SceneUtil\n{\n\n    class ControllerSource\n    {\n    public:\n        virtual ~ControllerSource() { }\n        virtual float getValue(osg::NodeVisitor* nv) = 0;\n    };\n\n    class FrameTimeSource : public ControllerSource\n    {\n    public:\n        FrameTimeSource();\n        float getValue(osg::NodeVisitor* nv) override;\n    };\n\n    /// @note ControllerFunctions may be shared - you should not hold any state in it. That is why all its methods are declared const.\n    class ControllerFunction\n    {\n    public:\n        virtual ~ControllerFunction() = default;\n\n        virtual float calculate(float input) const = 0;\n\n        /// Get the \"stop time\" of the controller function, typically the maximum of the calculate() function.\n        /// May not be meaningful for all types of controller functions.\n        virtual float getMaximum() const = 0;\n    };\n\n    class Controller\n    {\n    public:\n        Controller();\n        virtual ~Controller() {}\n\n        bool hasInput() const;\n\n        float getInputValue(osg::NodeVisitor* nv);\n\n        void setSource(std::shared_ptr<ControllerSource> source);\n        void setFunction(std::shared_ptr<ControllerFunction> function);\n\n        std::shared_ptr<ControllerSource> getSource() const;\n        std::shared_ptr<ControllerFunction> getFunction() const;\n\n    private:\n        std::shared_ptr<ControllerSource> mSource;\n\n        // The source value gets passed through this function before it's passed on to the DestValue.\n        std::shared_ptr<ControllerFunction> mFunction;\n    };\n\n    /// Pure virtual base class - visit() all controllers that are attached as UpdateCallbacks in a scene graph.\n    class ControllerVisitor : public osg::NodeVisitor\n    {\n    public:\n        ControllerVisitor();\n\n        void apply(osg::Node& node) override;\n\n        // Technically not required as the default implementation would trickle down to apply(Node&) anyway,\n        // but we'll shortcut instead to avoid the chain of virtual function calls\n        void apply(osg::MatrixTransform& node) override;\n        void apply(osg::Geometry& node) override;\n\n        void applyNode(osg::Node& node);\n\n        virtual void visit(osg::Node& node, Controller& ctrl) = 0;\n    };\n\n    class AssignControllerSourcesVisitor : public ControllerVisitor\n    {\n    public:\n        AssignControllerSourcesVisitor();\n        AssignControllerSourcesVisitor(std::shared_ptr<ControllerSource> toAssign);\n\n        /// Assign the wanted ControllerSource. May be overridden in derived classes.\n        /// By default assigns the ControllerSource passed to the constructor of this class if no ControllerSource is assigned to that controller yet.\n        void visit(osg::Node& node, Controller& ctrl) override;\n\n    private:\n        std::shared_ptr<ControllerSource> mToAssign;\n    };\n\n    /// Finds the maximum of all controller functions in the given scene graph\n    class FindMaxControllerLengthVisitor : public ControllerVisitor\n    {\n    public:\n        FindMaxControllerLengthVisitor();\n\n        void visit(osg::Node& , Controller& ctrl) override;\n\n        float getMaxLength() const;\n\n    private:\n        float mMaxLength;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/detourdebugdraw.cpp",
    "content": "#include \"detourdebugdraw.hpp\"\n#include \"util.hpp\"\n\n#include <components/detournavigator/debug.hpp>\n\n#include <osg/BlendFunc>\n#include <osg/Group>\n#include <osg/LineWidth>\n\nnamespace\n{\n    using DetourNavigator::operator<<;\n\n    osg::PrimitiveSet::Mode toOsgPrimitiveSetMode(duDebugDrawPrimitives value)\n    {\n        switch (value)\n        {\n            case DU_DRAW_POINTS:\n                return osg::PrimitiveSet::POINTS;\n            case DU_DRAW_LINES:\n                return osg::PrimitiveSet::LINES;\n            case DU_DRAW_TRIS:\n                return osg::PrimitiveSet::TRIANGLES;\n            case DU_DRAW_QUADS:\n                return osg::PrimitiveSet::QUADS;\n        }\n        throw std::logic_error(\"Can't convert duDebugDrawPrimitives to osg::PrimitiveSet::Mode, value=\"\n                               + std::to_string(value));\n    }\n\n}\n\nnamespace SceneUtil\n{\n    DebugDraw::DebugDraw(osg::Group& group, const osg::Vec3f& shift, float recastInvertedScaleFactor)\n        : mGroup(group)\n        , mShift(shift)\n        , mRecastInvertedScaleFactor(recastInvertedScaleFactor)\n        , mDepthMask(false)\n        , mMode(osg::PrimitiveSet::POINTS)\n        , mSize(1.0f)\n    {\n    }\n\n    void DebugDraw::depthMask(bool state)\n    {\n        mDepthMask = state;\n    }\n\n    void DebugDraw::texture(bool)\n    {\n    }\n\n    void DebugDraw::begin(osg::PrimitiveSet::Mode mode, float size)\n    {\n        mMode = mode;\n        mVertices = new osg::Vec3Array;\n        mColors = new osg::Vec4Array;\n        mSize = size * mRecastInvertedScaleFactor;\n    }\n\n    void DebugDraw::begin(duDebugDrawPrimitives prim, float size)\n    {\n        begin(toOsgPrimitiveSetMode(prim), size);\n    }\n\n    void DebugDraw::vertex(const float* pos, unsigned color)\n    {\n        vertex(pos[0], pos[1], pos[2], color);\n    }\n\n    void DebugDraw::vertex(const float x, const float y, const float z, unsigned color)\n    {\n        addVertex(osg::Vec3f(x, y, z));\n        addColor(SceneUtil::colourFromRGBA(color));\n    }\n\n    void DebugDraw::vertex(const float* pos, unsigned color, const float* uv)\n    {\n        vertex(pos[0], pos[1], pos[2], color, uv[0], uv[1]);\n    }\n\n    void DebugDraw::vertex(const float x, const float y, const float z, unsigned color, const float, const float)\n    {\n        addVertex(osg::Vec3f(x, y, z));\n        addColor(SceneUtil::colourFromRGBA(color));\n    }\n\n    void DebugDraw::end()\n    {\n        osg::ref_ptr<osg::StateSet> stateSet(new osg::StateSet);\n        stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);\n        stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n        stateSet->setMode(GL_DEPTH, (mDepthMask ? osg::StateAttribute::ON : osg::StateAttribute::OFF));\n        stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);\n        stateSet->setAttributeAndModes(new osg::LineWidth(mSize));\n        stateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));\n\n        osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);\n        geometry->setStateSet(stateSet);\n        geometry->setVertexArray(mVertices);\n        geometry->setColorArray(mColors, osg::Array::BIND_PER_VERTEX);\n        geometry->addPrimitiveSet(new osg::DrawArrays(mMode, 0, static_cast<int>(mVertices->size())));\n\n        mGroup.addChild(geometry);\n        mColors.release();\n        mVertices.release();\n    }\n\n    void DebugDraw::addVertex(osg::Vec3f&& position)\n    {\n        std::swap(position.y(), position.z());\n        mVertices->push_back(position * mRecastInvertedScaleFactor + mShift);\n    }\n\n    void DebugDraw::addColor(osg::Vec4f&& value)\n    {\n        mColors->push_back(value);\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/detourdebugdraw.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_DETOURDEBUGDRAW_H\n#define OPENMW_COMPONENTS_SCENEUTIL_DETOURDEBUGDRAW_H\n\n#include <DebugDraw.h>\n\n#include <osg/Geometry>\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace SceneUtil\n{\n    class DebugDraw : public duDebugDraw\n    {\n    public:\n        DebugDraw(osg::Group& group, const osg::Vec3f& shift, float recastInvertedScaleFactor);\n\n        void depthMask(bool state) override;\n\n        void texture(bool state) override;\n\n        void begin(osg::PrimitiveSet::Mode mode, float size);\n\n        void begin(duDebugDrawPrimitives prim, float size) override;\n\n        void vertex(const float* pos, unsigned int color) override;\n\n        void vertex(const float x, const float y, const float z, unsigned int color) override;\n\n        void vertex(const float* pos, unsigned int color, const float* uv) override;\n\n        void vertex(const float x, const float y, const float z, unsigned int color,\n                const float u, const float v) override;\n\n        void end() override;\n\n    private:\n        osg::Group& mGroup;\n        osg::Vec3f mShift;\n        float mRecastInvertedScaleFactor;\n        bool mDepthMask;\n        osg::PrimitiveSet::Mode mMode;\n        float mSize;\n        osg::ref_ptr<osg::Vec3Array> mVertices;\n        osg::ref_ptr<osg::Vec4Array> mColors;\n\n        void addVertex(osg::Vec3f&& position);\n\n        void addColor(osg::Vec4f&& value);\n    };\n}\n\n#endif"
  },
  {
    "path": "components/sceneutil/keyframe.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_KEYFRAME_HPP\n#define OPENMW_COMPONENTS_SCENEUTIL_KEYFRAME_HPP\n\n#include <map>\n\n#include <osg/Node>\n\n#include <components/sceneutil/controller.hpp>\n#include <components/sceneutil/textkeymap.hpp>\n#include <components/resource/animation.hpp>\n\nnamespace SceneUtil\n{\n    class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller\n    {\n    public:\n        KeyframeController() {}\n\n        KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop)\n            : osg::NodeCallback(copy, copyop)\n            , SceneUtil::Controller(copy)\n        {}\n        META_Object(SceneUtil, KeyframeController)\n\n        virtual osg::Vec3f getTranslation(float time) const  { return osg::Vec3f(); }\n\n        virtual void operator() (osg::Node* node, osg::NodeVisitor* nodeVisitor) override { traverse(node, nodeVisitor); }\n    };\n\n    /// Wrapper object containing an animation track as a ref-countable osg::Object.\n    struct TextKeyMapHolder : public osg::Object\n    {\n    public:\n        TextKeyMapHolder() {}\n        TextKeyMapHolder(const TextKeyMapHolder& copy, const osg::CopyOp& copyop)\n            : osg::Object(copy, copyop)\n            , mTextKeys(copy.mTextKeys)\n        {}\n\n        TextKeyMap mTextKeys;\n\n        META_Object(SceneUtil, TextKeyMapHolder)\n\n    };\n\n    /// Wrapper object containing the animation track and its KeyframeControllers.\n    class KeyframeHolder : public osg::Object\n    {\n    public:\n        KeyframeHolder() {}\n        KeyframeHolder(const KeyframeHolder& copy, const osg::CopyOp& copyop)\n            : mTextKeys(copy.mTextKeys)\n            , mKeyframeControllers(copy.mKeyframeControllers)\n        {\n        }\n\n        TextKeyMap mTextKeys;\n\n        META_Object(SceneUtil, KeyframeHolder)\n\n        /// Controllers mapped to node name.\n        typedef std::map<std::string, osg::ref_ptr<const KeyframeController> > KeyframeControllerMap;\n        KeyframeControllerMap mKeyframeControllers;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/lightcontroller.cpp",
    "content": "#include \"lightcontroller.hpp\"\n\n#include <cmath>\n\n#include <osg/NodeVisitor>\n\n#include <components/sceneutil/lightmanager.hpp>\n\n#include <components/misc/rng.hpp>\n\nnamespace SceneUtil\n{\n\n    LightController::LightController()\n        : mType(LT_Normal)\n        , mPhase(0.25f + Misc::Rng::rollClosedProbability() * 0.75f)\n        , mBrightness(0.675f)\n        , mStartTime(0.0)\n        , mLastTime(0.0)\n        , mTicksToAdvance(0.f)\n    {\n    }\n\n    void LightController::setType(LightController::LightType type)\n    {\n        mType = type;\n    }\n\n    void LightController::operator ()(osg::Node* node, osg::NodeVisitor* nv)\n    {\n        double time = nv->getFrameStamp()->getSimulationTime();\n        if (mStartTime == 0)\n            mStartTime = time;\n\n        // disabled early out, light state needs to be set every frame regardless of change, due to the double buffering\n        //if (time == mLastTime)\n        //    return;\n\n        if (mType == LT_Normal)\n        {\n            static_cast<SceneUtil::LightSource*>(node)->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor);\n            traverse(node, nv);\n            return;\n        }\n\n        // Updating flickering at 15 FPS like vanilla.\n        constexpr float updateRate = 15.f;\n        mTicksToAdvance = static_cast<float>(time - mStartTime - mLastTime) * updateRate * 0.25f + mTicksToAdvance * 0.75f;\n        mLastTime = time - mStartTime;\n\n        float speed = (mType == LT_Flicker || mType == LT_Pulse) ? 0.1f : 0.05f;\n        if (mBrightness >= mPhase)\n            mBrightness -= mTicksToAdvance * speed;\n        else\n            mBrightness += mTicksToAdvance * speed;\n\n        if (std::abs(mBrightness - mPhase) < speed)\n        {\n            if (mType == LT_Flicker || mType == LT_FlickerSlow)\n                mPhase = 0.25f + Misc::Rng::rollClosedProbability() * 0.75f;\n            else // if (mType == LT_Pulse || mType == LT_PulseSlow)\n                mPhase = mPhase <= 0.5f ? 1.f : 0.25f;\n        }\n\n        auto* lightSource = static_cast<SceneUtil::LightSource*>(node);\n        lightSource->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * mBrightness * lightSource->getActorFade());\n\n        traverse(node, nv);\n    }\n\n    void LightController::setDiffuse(const osg::Vec4f& color)\n    {\n        mDiffuseColor = color;\n    }\n\n}\n"
  },
  {
    "path": "components/sceneutil/lightcontroller.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_LIGHTCONTROLLER_H\n#define OPENMW_COMPONENTS_SCENEUTIL_LIGHTCONTROLLER_H\n\n#include <osg/NodeCallback>\n#include <osg/Vec4f>\n\nnamespace SceneUtil\n{\n\n    /// @brief Controller class to handle a pulsing and/or flickering light\n    /// @note Must be set on a SceneUtil::LightSource.\n    class LightController : public osg::NodeCallback\n    {\n    public:\n        enum LightType {\n            LT_Normal,\n            LT_Flicker,\n            LT_FlickerSlow,\n            LT_Pulse,\n            LT_PulseSlow\n        };\n\n        LightController();\n\n        void setType(LightType type);\n\n        void setDiffuse(const osg::Vec4f& color);\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override;\n\n    private:\n        LightType mType;\n        osg::Vec4f mDiffuseColor;\n        float mPhase;\n        float mBrightness;\n        double mStartTime;\n        double mLastTime;\n        float mTicksToAdvance;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/lightmanager.cpp",
    "content": "#include \"lightmanager.hpp\"\n\n#include <array>\n\n#include <osg/BufferObject>\n#include <osg/BufferIndexBinding>\n#include <osg/Endian>\n#include <osg/Version>\n#include <osg/ValueObject>\n\n#include <osgUtil/CullVisitor>\n\n#include <components/sceneutil/util.hpp>\n\n#include <components/misc/hash.hpp>\n#include <components/misc/stringops.hpp>\n\n#include <components/debug/debuglog.hpp>\n\nnamespace\n{\n    bool sortLights(const SceneUtil::LightManager::LightSourceViewBound* left, const SceneUtil::LightManager::LightSourceViewBound* right)\n    {\n        static auto constexpr illuminationBias = 81.f;\n        return left->mViewBound.center().length2() - left->mViewBound.radius2()*illuminationBias < right->mViewBound.center().length2() - right->mViewBound.radius2()*illuminationBias;\n    }\n\n    float getLightRadius(const osg::Light* light)\n    {\n        float value = 0.0;\n        light->getUserValue(\"radius\", value);\n        return value;\n    }\n\n    void setLightRadius(osg::Light* light, float value)\n    {\n        light->setUserValue(\"radius\", value);\n    }\n\n    void configurePosition(osg::Matrixf& mat, const osg::Vec4& pos)\n    {\n        mat(0, 0) = pos.x();\n        mat(0, 1) = pos.y();\n        mat(0, 2) = pos.z();\n    }\n\n    void configureAmbient(osg::Matrixf& mat, const osg::Vec4& color)\n    {\n        mat(1, 0) = color.r();\n        mat(1, 1) = color.g();\n        mat(1, 2) = color.b();\n    }\n\n    void configureDiffuse(osg::Matrixf& mat, const osg::Vec4& color)\n    {\n        mat(2, 0) = color.r();\n        mat(2, 1) = color.g();\n        mat(2, 2) = color.b();\n    }\n\n    void configureSpecular(osg::Matrixf& mat, const osg::Vec4& color)\n    {\n        mat(3, 0) = color.r();\n        mat(3, 1) = color.g();\n        mat(3, 2) = color.b();\n        mat(3, 3) = color.a();\n    }\n\n    void configureAttenuation(osg::Matrixf& mat, float c, float l, float q, float r)\n    {\n        mat(0, 3) = c;\n        mat(1, 3) = l;\n        mat(2, 3) = q;\n        mat(3, 3) = r;\n    }\n\n    bool isReflectionCamera(osg::Camera* camera)\n    {\n        return (camera->getName() == \"ReflectionCamera\");\n    }\n}\n\nnamespace SceneUtil\n{\n    static int sLightId = 0;\n\n    // Handles a GLSL shared layout by using configured offsets and strides to fill a continuous buffer, making the data upload to GPU simpler.\n    class LightBuffer : public osg::Referenced\n    {\n    public:\n\n        enum LayoutOffset\n        {\n            Diffuse,\n            DiffuseSign,\n            Ambient,\n            Specular,\n            Position,\n            AttenuationRadius\n        };\n\n        LightBuffer(int count)\n            : mData(new osg::FloatArray(3*4*count))\n            , mEndian(osg::getCpuByteOrder())\n            , mCount(count)\n            , mCachedSunPos(osg::Vec4())\n        {\n        }\n\n        LightBuffer(const LightBuffer&) = delete;\n\n        void setDiffuse(int index, const osg::Vec4& value)\n        {\n            // Deal with negative lights (negative diffuse) by passing a sign bit in the unused alpha component\n            auto positiveColor = value;\n            unsigned int signBit = 1;\n            if (value[0] < 0)\n            {\n                positiveColor *= -1.0;\n                signBit = ~0u;\n            }\n            unsigned int packedColor = asRGBA(positiveColor);\n            std::memcpy(&(*mData)[getOffset(index, Diffuse)], &packedColor, sizeof(unsigned int));\n            std::memcpy(&(*mData)[getOffset(index, DiffuseSign)], &signBit, sizeof(unsigned int));\n        }\n\n        void setAmbient(int index, const osg::Vec4& value)\n        {\n            unsigned int packed = asRGBA(value);\n            std::memcpy(&(*mData)[getOffset(index, Ambient)], &packed, sizeof(unsigned int));\n        }\n\n        void setSpecular(int index, const osg::Vec4& value)\n        {\n            unsigned int packed = asRGBA(value);\n            std::memcpy(&(*mData)[getOffset(index, Specular)], &packed, sizeof(unsigned int));\n        }\n\n        void setPosition(int index, const osg::Vec4& value)\n        {\n            std::memcpy(&(*mData)[getOffset(index, Position)], value.ptr(), sizeof(osg::Vec4f));\n        }\n\n        void setAttenuationRadius(int index, const osg::Vec4& value)\n        {\n            std::memcpy(&(*mData)[getOffset(index, AttenuationRadius)], value.ptr(), sizeof(osg::Vec4f));\n        }\n\n        auto& getData()\n        {\n            return mData;\n        }\n\n        void dirty()\n        {\n            mData->dirty();\n        }\n\n        static constexpr int queryBlockSize(int sz)\n        {\n            return 3 * osg::Vec4::num_components * sizeof(GLfloat) * sz;\n        }\n\n        void setCachedSunPos(const osg::Vec4& pos)\n        {\n            mCachedSunPos = pos;\n        }\n\n        void uploadCachedSunPos(const osg::Matrix& viewMat)\n        {\n            osg::Vec4 viewPos = mCachedSunPos * viewMat;\n            std::memcpy(&(*mData)[getOffset(0, Position)], viewPos.ptr(), sizeof(osg::Vec4f));\n        }\n\n        unsigned int asRGBA(const osg::Vec4& value) const\n        {\n            return mEndian == osg::BigEndian ? value.asABGR() : value.asRGBA();\n        }\n\n        int getOffset(int index, LayoutOffset slot) const\n        {\n            return mOffsets.get(index, slot);\n        }\n\n        void configureLayout(int offsetColors, int offsetPosition, int offsetAttenuationRadius, int size, int stride)\n        {\n            const Offsets offsets(offsetColors, offsetPosition, offsetAttenuationRadius, stride);\n\n            // Copy cloned data using current layout into current data using new layout.\n            // This allows to preserve osg::FloatArray buffer object in mData.\n            const auto data = mData->asVector();\n            mData->resizeArray(static_cast<unsigned>(size));\n            for (int i = 0; i < mCount; ++i)\n            {\n                std::memcpy(&(*mData)[offsets.get(i, Diffuse)], data.data() + getOffset(i, Diffuse), sizeof(osg::Vec4f));\n                std::memcpy(&(*mData)[offsets.get(i, Position)], data.data() + getOffset(i, Position), sizeof(osg::Vec4f));\n                std::memcpy(&(*mData)[offsets.get(i, AttenuationRadius)], data.data() + getOffset(i, AttenuationRadius), sizeof(osg::Vec4f));\n            }\n            mOffsets = offsets;\n        }\n\n    private:\n        class Offsets\n        {\n            public:\n                Offsets()\n                    : mStride(12)\n                {\n                    mValues[Diffuse] = 0;\n                    mValues[Ambient] = 1;\n                    mValues[Specular] = 2;\n                    mValues[DiffuseSign] = 3;\n                    mValues[Position] = 4;\n                    mValues[AttenuationRadius] = 8;\n                }\n\n                Offsets(int offsetColors, int offsetPosition, int offsetAttenuationRadius, int stride)\n                    : mStride((offsetAttenuationRadius + sizeof(GLfloat) * osg::Vec4::num_components + stride) / 4)\n                {\n                    constexpr auto sizeofFloat = sizeof(GLfloat);\n                    const auto diffuseOffset = offsetColors / sizeofFloat;\n\n                    mValues[Diffuse] = diffuseOffset;\n                    mValues[Ambient] = diffuseOffset + 1;\n                    mValues[Specular] = diffuseOffset + 2;\n                    mValues[DiffuseSign] = diffuseOffset + 3;\n                    mValues[Position] = offsetPosition / sizeofFloat;\n                    mValues[AttenuationRadius] = offsetAttenuationRadius / sizeofFloat;\n                }\n\n                int get(int index, LayoutOffset slot) const\n                {\n                    return mStride * index + mValues[slot];\n                }\n\n            private:\n                int mStride;\n                std::array<int, 6> mValues;\n        };\n\n        osg::ref_ptr<osg::FloatArray> mData;\n        osg::Endian mEndian;\n        int mCount;\n        Offsets mOffsets;\n        osg::Vec4 mCachedSunPos;\n    };\n\n    class LightStateCache\n    {\n    public:\n        std::vector<osg::Light*> lastAppliedLight;\n    };\n\n    LightStateCache* getLightStateCache(size_t contextid, size_t size = 8)\n    {\n        static std::vector<LightStateCache> cacheVector;\n        if (cacheVector.size() < contextid+1)\n            cacheVector.resize(contextid+1);\n        cacheVector[contextid].lastAppliedLight.resize(size);\n        return &cacheVector[contextid];\n    }\n\n    void configureStateSetSunOverride(LightManager* lightManager, const osg::Light* light, osg::StateSet* stateset, int mode)\n    {\n        auto method = lightManager->getLightingMethod();\n        switch (method)\n        {\n        case LightingMethod::FFP:\n            {\n                break;\n            }\n        case LightingMethod::PerObjectUniform:\n            {\n                osg::Matrixf lightMat;\n                configurePosition(lightMat, light->getPosition());\n                configureAmbient(lightMat, light->getAmbient());\n                configureDiffuse(lightMat, light->getDiffuse());\n                configureSpecular(lightMat, light->getSpecular());\n\n                osg::ref_ptr<osg::Uniform> uni = new osg::Uniform(osg::Uniform::FLOAT_MAT4, \"LightBuffer\", lightManager->getMaxLights());\n                uni->setElement(0, lightMat);\n                stateset->addUniform(uni, mode);\n                break;\n            }\n        case LightingMethod::SingleUBO:\n            {\n                osg::ref_ptr<LightBuffer> buffer = new LightBuffer(lightManager->getMaxLightsInScene());\n\n                buffer->setDiffuse(0, light->getDiffuse());\n                buffer->setAmbient(0, light->getAmbient());\n                buffer->setSpecular(0, light->getSpecular());\n                buffer->setPosition(0, light->getPosition());\n\n                osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;\n                buffer->getData()->setBufferObject(ubo);\n#if OSG_VERSION_GREATER_OR_EQUAL(3,5,7)\n                osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), buffer->getData(), 0, buffer->getData()->getTotalDataSize());\n#else\n                osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), ubo, 0, buffer->getData()->getTotalDataSize());\n#endif\n                stateset->setAttributeAndModes(ubb, mode);\n\n                break;\n            }\n        }\n    }\n\n    class DisableLight : public osg::StateAttribute\n    {\n    public:\n        DisableLight() : mIndex(0) {}\n        DisableLight(int index) : mIndex(index) {}\n\n        DisableLight(const DisableLight& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)\n            : osg::StateAttribute(copy,copyop), mIndex(copy.mIndex) {}\n\n        osg::Object* cloneType() const override { return new DisableLight(mIndex); }\n        osg::Object* clone(const osg::CopyOp& copyop) const override { return new DisableLight(*this,copyop); }\n        bool isSameKindAs(const osg::Object* obj) const override { return dynamic_cast<const DisableLight *>(obj)!=nullptr; }\n        const char* libraryName() const override { return \"SceneUtil\"; }\n        const char* className() const override { return \"DisableLight\"; }\n        Type getType() const override { return LIGHT; }\n\n        unsigned int getMember() const override\n        {\n            return mIndex;\n        }\n\n        bool getModeUsage(ModeUsage & usage) const override\n        {\n            usage.usesMode(GL_LIGHT0 + mIndex);\n            return true;\n        }\n\n        int compare(const StateAttribute &sa) const override\n        {\n            throw std::runtime_error(\"DisableLight::compare: unimplemented\");\n        }\n\n        void apply(osg::State& state) const override\n        {\n            int lightNum = GL_LIGHT0 + mIndex;\n            glLightfv(lightNum, GL_AMBIENT, mNullptr.ptr());\n            glLightfv(lightNum, GL_DIFFUSE, mNullptr.ptr());\n            glLightfv(lightNum, GL_SPECULAR, mNullptr.ptr());\n\n            LightStateCache* cache = getLightStateCache(state.getContextID());\n            cache->lastAppliedLight[mIndex] = nullptr;\n        }\n\n    private:\n        size_t mIndex;\n        osg::Vec4f mNullptr;\n    };\n\n    class FFPLightStateAttribute : public osg::StateAttribute\n    {\n    public:\n        FFPLightStateAttribute() : mIndex(0) {}\n        FFPLightStateAttribute(size_t index, const std::vector<osg::ref_ptr<osg::Light> >& lights) : mIndex(index), mLights(lights) {}\n\n        FFPLightStateAttribute(const FFPLightStateAttribute& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)\n            : osg::StateAttribute(copy,copyop), mIndex(copy.mIndex), mLights(copy.mLights) {}\n\n        unsigned int getMember() const override\n        {\n            return mIndex;\n        }\n\n        bool getModeUsage(ModeUsage & usage) const override\n        {\n            for (size_t i = 0; i < mLights.size(); ++i)\n                usage.usesMode(GL_LIGHT0 + mIndex + i);\n            return true;\n        }\n\n        int compare(const StateAttribute &sa) const override\n        {\n            throw std::runtime_error(\"FFPLightStateAttribute::compare: unimplemented\");\n        }\n\n        META_StateAttribute(NifOsg, FFPLightStateAttribute, osg::StateAttribute::LIGHT)\n\n        void apply(osg::State& state) const override\n        {\n            if (mLights.empty())\n                return;\n            osg::Matrix modelViewMatrix = state.getModelViewMatrix();\n\n            state.applyModelViewMatrix(state.getInitialViewMatrix());\n\n            LightStateCache* cache = getLightStateCache(state.getContextID());\n\n            for (size_t i = 0; i < mLights.size(); ++i)\n            {\n                osg::Light* current = cache->lastAppliedLight[i+mIndex];\n                if (current != mLights[i].get())\n                {\n                    applyLight((GLenum)((int)GL_LIGHT0 + i + mIndex), mLights[i].get());\n                    cache->lastAppliedLight[i+mIndex] = mLights[i].get();\n                }\n            }\n\n            state.applyModelViewMatrix(modelViewMatrix);\n        }\n\n        void applyLight(GLenum lightNum, const osg::Light* light) const\n        {\n            glLightfv(lightNum, GL_AMBIENT, light->getAmbient().ptr());\n            glLightfv(lightNum, GL_DIFFUSE, light->getDiffuse().ptr());\n            glLightfv(lightNum, GL_SPECULAR, light->getSpecular().ptr());\n            glLightfv(lightNum, GL_POSITION, light->getPosition().ptr());\n            // TODO: enable this once spot lights are supported\n            // need to transform SPOT_DIRECTION by the world matrix?\n            //glLightfv(lightNum, GL_SPOT_DIRECTION, light->getDirection().ptr());\n            //glLightf(lightNum, GL_SPOT_EXPONENT, light->getSpotExponent());\n            //glLightf(lightNum, GL_SPOT_CUTOFF, light->getSpotCutoff());\n            glLightf(lightNum, GL_CONSTANT_ATTENUATION, light->getConstantAttenuation());\n            glLightf(lightNum, GL_LINEAR_ATTENUATION, light->getLinearAttenuation());\n            glLightf(lightNum, GL_QUADRATIC_ATTENUATION, light->getQuadraticAttenuation());\n        }\n\n    private:\n        size_t mIndex;\n        std::vector<osg::ref_ptr<osg::Light>> mLights;\n    };\n\n    LightManager* findLightManager(const osg::NodePath& path)\n    {\n        for (size_t i = 0; i < path.size(); ++i)\n        {\n            if (LightManager* lightManager = dynamic_cast<LightManager*>(path[i]))\n                return lightManager;\n        }\n        return nullptr;\n    }\n\n    class LightStateAttributePerObjectUniform : public osg::StateAttribute\n    {\n    public:\n        LightStateAttributePerObjectUniform() : mLightManager(nullptr) {}\n        LightStateAttributePerObjectUniform(const std::vector<osg::ref_ptr<osg::Light>>& lights, LightManager* lightManager) :  mLights(lights), mLightManager(lightManager) {}\n\n        LightStateAttributePerObjectUniform(const LightStateAttributePerObjectUniform& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)\n            : osg::StateAttribute(copy,copyop), mLights(copy.mLights), mLightManager(copy.mLightManager) {}\n\n        int compare(const StateAttribute &sa) const override\n        {\n            throw std::runtime_error(\"LightStateAttributePerObjectUniform::compare: unimplemented\");\n        }\n\n        META_StateAttribute(NifOsg, LightStateAttributePerObjectUniform, osg::StateAttribute::LIGHT)\n\n        void resize(int numLights)\n        {\n            mLights.resize(std::min(static_cast<size_t>(numLights), mLights.size()));\n        }\n\n        void apply(osg::State &state) const override\n        {\n            osg::StateSet* stateSet = mLightManager->getStateSet();\n            if (!stateSet)\n                return;\n\n            auto* lightUniform = stateSet->getUniform(\"LightBuffer\");\n            for (size_t i = 0; i < mLights.size(); ++i)\n            {\n                auto light = mLights[i];\n                osg::Matrixf lightMat;\n\n                configurePosition(lightMat, light->getPosition() * state.getInitialViewMatrix());\n                configureAmbient(lightMat, light->getAmbient());\n                configureDiffuse(lightMat, light->getDiffuse());\n                configureAttenuation(lightMat, light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation(), getLightRadius(light));\n\n                lightUniform->setElement(i+1, lightMat);\n            }\n\n            auto sun = mLightManager->getSunlightBuffer(state.getFrameStamp()->getFrameNumber());\n            configurePosition(sun, osg::Vec4(sun(0,0), sun(0,1), sun(0,2), 0.0) * state.getInitialViewMatrix());\n            lightUniform->setElement(0, sun);\n\n            lightUniform->dirty();\n        }\n\n    private:\n        std::vector<osg::ref_ptr<osg::Light>> mLights;\n        LightManager* mLightManager;\n    };\n\n    struct StateSetGenerator\n    {\n        LightManager* mLightManager;\n\n        virtual ~StateSetGenerator() {}\n\n        virtual osg::ref_ptr<osg::StateSet> generate(const LightManager::LightList& lightList, size_t frameNum) = 0;\n\n        virtual void update(osg::StateSet* stateset, const LightManager::LightList& lightList, size_t frameNum) {}\n    };\n\n    struct StateSetGeneratorFFP : StateSetGenerator\n    {\n        osg::ref_ptr<osg::StateSet> generate(const LightManager::LightList& lightList, size_t frameNum) override\n        {\n            osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;\n\n            std::vector<osg::ref_ptr<osg::Light>> lights;\n            lights.reserve(lightList.size());\n            for (size_t i = 0; i < lightList.size(); ++i)\n                lights.emplace_back(lightList[i]->mLightSource->getLight(frameNum));\n\n            // the first light state attribute handles the actual state setting for all lights\n            // it's best to batch these up so that we don't need to touch the modelView matrix more than necessary\n            // don't use setAttributeAndModes, that does not support light indices!\n            stateset->setAttribute(new FFPLightStateAttribute(mLightManager->getStartLight(), std::move(lights)), osg::StateAttribute::ON);\n\n            for (size_t i = 0; i < lightList.size(); ++i)\n                stateset->setMode(GL_LIGHT0 + mLightManager->getStartLight() + i, osg::StateAttribute::ON);\n\n            // need to push some dummy attributes to ensure proper state tracking\n            // lights need to reset to their default when the StateSet is popped\n            for (size_t i = 1; i < lightList.size(); ++i)\n                stateset->setAttribute(mLightManager->getDummies()[i + mLightManager->getStartLight()].get(), osg::StateAttribute::ON);\n\n            return stateset;\n        }\n    };\n\n    struct StateSetGeneratorSingleUBO : StateSetGenerator\n    {\n        osg::ref_ptr<osg::StateSet> generate(const LightManager::LightList& lightList, size_t frameNum) override\n        {\n            osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;\n\n            osg::ref_ptr<osg::IntArray> indices = new osg::IntArray(mLightManager->getMaxLights());\n            osg::ref_ptr<osg::Uniform> indicesUni = new osg::Uniform(osg::Uniform::Type::INT, \"PointLightIndex\", indices->size());\n            int pointCount = 0;\n\n            for (size_t i = 0; i < lightList.size(); ++i)\n            {\n                int bufIndex = mLightManager->getLightIndexMap(frameNum)[lightList[i]->mLightSource->getId()];\n                indices->at(pointCount++) = bufIndex;\n            }\n            indicesUni->setArray(indices);\n            stateset->addUniform(indicesUni);\n            stateset->addUniform(new osg::Uniform(\"PointLightCount\", pointCount));\n\n            return stateset;\n        }\n\n        // Cached statesets must be revalidated in case the light indices change. There is no actual link between\n        // a light's ID and the buffer index it will eventually be assigned (or reassigned) to.\n        void update(osg::StateSet* stateset, const LightManager::LightList& lightList, size_t frameNum) override\n        {\n            int newCount = 0;\n            int oldCount;\n\n            auto uOldArray = stateset->getUniform(\"PointLightIndex\");\n            auto uOldCount = stateset->getUniform(\"PointLightCount\");\n\n            uOldCount->get(oldCount);\n\n            // max lights count can change during runtime\n            oldCount = std::min(mLightManager->getMaxLights(), oldCount);\n\n            auto& lightData = mLightManager->getLightIndexMap(frameNum);\n\n            for (int i = 0; i < oldCount; ++i)\n            {\n                auto* lightSource = lightList[i]->mLightSource;\n                auto it = lightData.find(lightSource->getId());\n                if (it != lightData.end())\n                    uOldArray->setElement(newCount++, it->second);\n            }\n\n            uOldArray->dirty();\n            uOldCount->set(newCount);\n        }\n    };\n\n    struct StateSetGeneratorPerObjectUniform : StateSetGenerator\n    {\n        osg::ref_ptr<osg::StateSet> generate(const LightManager::LightList& lightList, size_t frameNum) override\n        {\n            osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;\n\n            std::vector<osg::ref_ptr<osg::Light>> lights(lightList.size());\n\n            for (size_t i = 0; i < lightList.size(); ++i)\n            {\n                auto* light = lightList[i]->mLightSource->getLight(frameNum);\n                lights[i] = light;\n                setLightRadius(light, lightList[i]->mLightSource->getRadius());\n            }\n\n            stateset->setAttributeAndModes(new LightStateAttributePerObjectUniform(std::move(lights), mLightManager), osg::StateAttribute::ON);\n\n            stateset->addUniform(new osg::Uniform(\"PointLightCount\", static_cast<int>(lightList.size() + 1)));\n\n            return stateset;\n        }\n    };\n\n    // Set on a LightSource. Adds the light source to its light manager for the current frame.\n    // This allows us to keep track of the current lights in the scene graph without tying creation & destruction to the manager.\n    class CollectLightCallback : public osg::NodeCallback\n    {\n    public:\n        CollectLightCallback()\n            : mLightManager(nullptr) { }\n\n        CollectLightCallback(const CollectLightCallback& copy, const osg::CopyOp& copyop)\n            : osg::NodeCallback(copy, copyop)\n            , mLightManager(nullptr) { }\n\n        META_Object(SceneUtil, SceneUtil::CollectLightCallback)\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            if (!mLightManager)\n            {\n                mLightManager = findLightManager(nv->getNodePath());\n\n                if (!mLightManager)\n                    throw std::runtime_error(\"can't find parent LightManager\");\n            }\n\n            mLightManager->addLight(static_cast<LightSource*>(node), osg::computeLocalToWorld(nv->getNodePath()), nv->getTraversalNumber());\n\n            traverse(node, nv);\n        }\n\n    private:\n        LightManager* mLightManager;\n    };\n\n    // Set on a LightManager. Clears the data from the previous frame.\n    class LightManagerUpdateCallback : public osg::NodeCallback\n    {\n    public:\n        LightManagerUpdateCallback()\n            { }\n\n        LightManagerUpdateCallback(const LightManagerUpdateCallback& copy, const osg::CopyOp& copyop)\n            : osg::NodeCallback(copy, copyop)\n            { }\n\n        META_Object(SceneUtil, LightManagerUpdateCallback)\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            LightManager* lightManager = static_cast<LightManager*>(node);\n            lightManager->update(nv->getTraversalNumber());\n\n            traverse(node, nv);\n        }\n    };\n\n    class LightManagerCullCallback : public osg::NodeCallback\n    {\n    public:\n        LightManagerCullCallback(LightManager* lightManager) : mLightManager(lightManager), mLastFrameNumber(0) {}\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n\n            if (mLastFrameNumber != cv->getTraversalNumber())\n            {\n                mLastFrameNumber = cv->getTraversalNumber();\n\n                if (mLightManager->getLightingMethod() == LightingMethod::SingleUBO)\n                {\n                    auto stateset = mLightManager->getStateSet();\n                    auto bo = mLightManager->getLightBuffer(mLastFrameNumber);\n\n#if OSG_VERSION_GREATER_OR_EQUAL(3,5,7)\n                    osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), bo->getData(), 0, bo->getData()->getTotalDataSize());\n#else\n                    osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), bo->getData()->getBufferObject(), 0, bo->getData()->getTotalDataSize());\n#endif\n                    stateset->setAttributeAndModes(ubb, osg::StateAttribute::ON);\n                }\n\n                auto sun = mLightManager->getSunlight();\n\n                if (sun)\n                {\n                    // we must defer uploading the transformation to view-space position to deal with different cameras (e.g. reflection RTT).\n                    if (mLightManager->getLightingMethod() == LightingMethod::PerObjectUniform)\n                    {\n                        osg::Matrixf lightMat;\n                        configurePosition(lightMat, sun->getPosition());\n                        configureAmbient(lightMat, sun->getAmbient());\n                        configureDiffuse(lightMat, sun->getDiffuse());\n                        configureSpecular(lightMat, sun->getSpecular());\n                        mLightManager->setSunlightBuffer(lightMat, mLastFrameNumber);\n                    }\n                    else\n                    {\n                        auto buf = mLightManager->getLightBuffer(mLastFrameNumber);\n\n                        buf->setCachedSunPos(sun->getPosition());\n                        buf->setAmbient(0, sun->getAmbient());\n                        buf->setDiffuse(0, sun->getDiffuse());\n                        buf->setSpecular(0, sun->getSpecular());\n                    }\n                }\n            }\n\n            traverse(node, nv);\n        }\n\n    private:\n        LightManager* mLightManager;\n        size_t mLastFrameNumber;\n    };\n\n    class LightManagerStateAttribute : public osg::StateAttribute\n    {\n    public:\n        LightManagerStateAttribute()\n            : mLightManager(nullptr)\n            , mInitLayout(false)\n        {\n        }\n\n        LightManagerStateAttribute(LightManager* lightManager)\n        : mLightManager(lightManager)\n        , mDummyProgram(new osg::Program)\n        , mInitLayout(false)\n        {\n            static const std::string dummyVertSource = generateDummyShader(mLightManager->getMaxLightsInScene());\n\n            // Needed to query the layout of the buffer object. The layout specifier needed to use the std140 layout is not reliably\n            // available, regardless of extensions, until GLSL 140.\n            mDummyProgram->addShader(new osg::Shader(osg::Shader::VERTEX, dummyVertSource));\n            mDummyProgram->addBindUniformBlock(\"LightBufferBinding\", static_cast<int>(Shader::UBOBinding::LightBuffer));\n        }\n\n        LightManagerStateAttribute(const LightManagerStateAttribute& copy, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)\n            : osg::StateAttribute(copy,copyop), mLightManager(copy.mLightManager), mInitLayout(copy.mInitLayout) {}\n\n        int compare(const StateAttribute &sa) const override\n        {\n            throw std::runtime_error(\"LightManagerStateAttribute::compare: unimplemented\");\n        }\n\n        META_StateAttribute(NifOsg, LightManagerStateAttribute, osg::StateAttribute::LIGHT)\n\n        void initSharedLayout(osg::GLExtensions* ext, int handle) const\n        {\n            constexpr std::array<unsigned int, 1> index = { static_cast<unsigned int>(Shader::UBOBinding::LightBuffer) };\n            int totalBlockSize = -1;\n            int stride = -1;\n\n            ext->glGetActiveUniformBlockiv(handle, 0, GL_UNIFORM_BLOCK_DATA_SIZE, &totalBlockSize);\n            ext->glGetActiveUniformsiv(handle, index.size(), index.data(), GL_UNIFORM_ARRAY_STRIDE, &stride);\n\n            std::array<const char*, 3> names = {\n                \"LightBuffer[0].packedColors\",\n                \"LightBuffer[0].position\",\n                \"LightBuffer[0].attenuation\",\n            };\n            std::vector<unsigned int> indices(names.size());\n            std::vector<int> offsets(names.size());\n\n            ext->glGetUniformIndices(handle, names.size(), names.data(), indices.data());\n            ext->glGetActiveUniformsiv(handle, indices.size(), indices.data(), GL_UNIFORM_OFFSET, offsets.data());\n\n            for (int i = 0; i < 2; ++i)\n                mLightManager->getLightBuffer(i)->configureLayout(offsets[0], offsets[1], offsets[2], totalBlockSize, stride);\n        }\n\n        void apply(osg::State& state) const override\n        {\n            if (!mInitLayout)\n            {\n                mDummyProgram->apply(state);\n                auto handle = mDummyProgram->getPCP(state)->getHandle();\n                auto* ext = state.get<osg::GLExtensions>();\n\n                int activeUniformBlocks = 0;\n                ext->glGetProgramiv(handle, GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks);\n\n                // wait until the UBO binding is created\n                if (activeUniformBlocks > 0)\n                {\n                    initSharedLayout(ext, handle);\n                    mInitLayout = true;\n                }\n            }\n            mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->uploadCachedSunPos(state.getInitialViewMatrix());\n            mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->dirty();\n        }\n\n    private:\n\n        std::string generateDummyShader(int maxLightsInScene)\n        {\n            const std::string define = \"@maxLightsInScene\";\n\n            std::string shader = R\"GLSL(\n                #version 120\n                #extension GL_ARB_uniform_buffer_object : require\n                struct LightData {\n                   ivec4 packedColors;\n                   vec4 position;\n                   vec4 attenuation;\n                };\n                uniform LightBufferBinding {\n                   LightData LightBuffer[@maxLightsInScene];\n                };\n                void main()\n                {\n                    gl_Position = vec4(0.0);\n                }\n            )GLSL\";\n\n            shader.replace(shader.find(define), define.length(), std::to_string(maxLightsInScene));\n            return shader;\n        }\n\n        LightManager* mLightManager;\n        osg::ref_ptr<osg::Program> mDummyProgram;\n        mutable bool mInitLayout;\n    };\n\n    const std::unordered_map<std::string, LightingMethod> LightManager::mLightingMethodSettingMap = {\n         {\"legacy\", LightingMethod::FFP}\n        ,{\"shaders compatibility\", LightingMethod::PerObjectUniform}\n        ,{\"shaders\", LightingMethod::SingleUBO}\n    };\n\n    LightingMethod LightManager::getLightingMethodFromString(const std::string& value)\n    {\n        auto it = LightManager::mLightingMethodSettingMap.find(value);\n        if (it != LightManager::mLightingMethodSettingMap.end())\n            return it->second;\n\n        constexpr const char* fallback = \"shaders compatibility\";\n        Log(Debug::Warning) << \"Unknown lighting method '\" << value << \"', returning fallback '\" << fallback << \"'\";\n        return LightingMethod::PerObjectUniform;\n    }\n\n    std::string LightManager::getLightingMethodString(LightingMethod method)\n    {\n        for (const auto& p : LightManager::mLightingMethodSettingMap)\n            if (p.second == method)\n                return p.first;\n        return \"\";\n    }\n\n    LightManager::~LightManager()\n    {\n        getOrCreateStateSet()->removeAttribute(osg::StateAttribute::LIGHT);\n    }\n\n    LightManager::LightManager(bool ffp)\n        : mStartLight(0)\n        , mLightingMask(~0u)\n        , mSun(nullptr)\n        , mPointLightRadiusMultiplier(1.f)\n        , mPointLightFadeEnd(0.f)\n        , mPointLightFadeStart(0.f)\n    {\n        osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);\n        bool supportsUBO = exts && exts->isUniformBufferObjectSupported;\n        bool supportsGPU4 = exts && exts->isGpuShader4Supported;\n\n        mSupported[static_cast<int>(LightingMethod::FFP)] = true;\n        mSupported[static_cast<int>(LightingMethod::PerObjectUniform)] = true;\n        mSupported[static_cast<int>(LightingMethod::SingleUBO)] = supportsUBO && supportsGPU4;\n\n        setUpdateCallback(new LightManagerUpdateCallback);\n\n        if (ffp)\n        {\n            initFFP(mFFPMaxLights);\n            return;\n        }\n\n        std::string lightingMethodString = Settings::Manager::getString(\"lighting method\", \"Shaders\");\n        auto lightingMethod = LightManager::getLightingMethodFromString(lightingMethodString);\n\n        static bool hasLoggedWarnings = false;\n\n        if (lightingMethod == LightingMethod::SingleUBO && !hasLoggedWarnings)\n        {\n            if (!supportsUBO)\n                Log(Debug::Warning) << \"GL_ARB_uniform_buffer_object not supported: switching to shader compatibility lighting mode\";\n            if (!supportsGPU4)\n                Log(Debug::Warning) << \"GL_EXT_gpu_shader4 not supported: switching to shader compatibility lighting mode\";\n            hasLoggedWarnings = true;\n        }\n\n        int targetLights = std::clamp(Settings::Manager::getInt(\"max lights\", \"Shaders\"), mMaxLightsLowerLimit, mMaxLightsUpperLimit);\n\n        if (!supportsUBO || !supportsGPU4 || lightingMethod == LightingMethod::PerObjectUniform)\n            initPerObjectUniform(targetLights);\n        else\n            initSingleUBO(targetLights);\n\n        updateSettings();\n\n        getOrCreateStateSet()->addUniform(new osg::Uniform(\"PointLightCount\", 0));\n\n        addCullCallback(new LightManagerCullCallback(this));\n    }\n\n    LightManager::LightManager(const LightManager &copy, const osg::CopyOp &copyop)\n        : osg::Group(copy, copyop)\n        , mStartLight(copy.mStartLight)\n        , mLightingMask(copy.mLightingMask)\n        , mSun(copy.mSun)\n        , mLightingMethod(copy.mLightingMethod)\n        , mPointLightRadiusMultiplier(copy.mPointLightRadiusMultiplier)\n        , mPointLightFadeEnd(copy.mPointLightFadeEnd)\n        , mPointLightFadeStart(copy.mPointLightFadeStart)\n        , mMaxLights(copy.mMaxLights)\n    {\n    }\n\n    LightingMethod LightManager::getLightingMethod() const\n    {\n        return mLightingMethod;\n    }\n\n    bool LightManager::usingFFP() const\n    {\n        return mLightingMethod == LightingMethod::FFP;\n    }\n\n    int LightManager::getMaxLights() const\n    {\n        return mMaxLights;\n    }\n\n    void LightManager::setMaxLights(int value)\n    {\n        mMaxLights = value;\n    }\n\n    int LightManager::getMaxLightsInScene() const\n    {\n        static constexpr int max = 16384 / LightBuffer::queryBlockSize(1);\n        return max;\n    }\n\n    Shader::ShaderManager::DefineMap LightManager::getLightDefines() const\n    {\n        Shader::ShaderManager::DefineMap defines;\n\n        defines[\"maxLights\"] = std::to_string(getMaxLights());\n        defines[\"maxLightsInScene\"] = std::to_string(getMaxLightsInScene());\n        defines[\"lightingMethodFFP\"] = getLightingMethod() == LightingMethod::FFP ? \"1\" : \"0\";\n        defines[\"lightingMethodPerObjectUniform\"] = getLightingMethod() == LightingMethod::PerObjectUniform ? \"1\" : \"0\";\n        defines[\"lightingMethodUBO\"] = getLightingMethod() == LightingMethod::SingleUBO ? \"1\" : \"0\";\n        defines[\"useUBO\"] = std::to_string(getLightingMethod() == LightingMethod::SingleUBO);\n        // exposes bitwise operators\n        defines[\"useGPUShader4\"] = std::to_string(getLightingMethod() == LightingMethod::SingleUBO);\n        defines[\"getLight\"] = getLightingMethod() == LightingMethod::FFP ? \"gl_LightSource\" : \"LightBuffer\";\n        defines[\"startLight\"] =  getLightingMethod() == LightingMethod::SingleUBO ? \"0\" : \"1\";\n        defines[\"endLight\"] = getLightingMethod() == LightingMethod::FFP ? defines[\"maxLights\"] : \"PointLightCount\";\n\n        return defines;\n    }\n\n    void LightManager::processChangedSettings(const Settings::CategorySettingVector& changed)\n    {\n        updateSettings();\n    }\n\n    void LightManager::updateMaxLights()\n    {\n        if (usingFFP())\n            return;\n\n        int targetLights = std::clamp(Settings::Manager::getInt(\"max lights\", \"Shaders\"), mMaxLightsLowerLimit, mMaxLightsUpperLimit);\n        setMaxLights(targetLights);\n\n        if (getLightingMethod() == LightingMethod::PerObjectUniform)\n        {\n            auto* prevUniform = getStateSet()->getUniform(\"LightBuffer\");\n            osg::ref_ptr<osg::Uniform> newUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, \"LightBuffer\", getMaxLights());\n\n            for (int i = 0; i < getMaxLights(); ++i)\n            {\n                osg::Matrixf prevLightData;\n                prevUniform->getElement(i, prevLightData);\n                newUniform->setElement(i, prevLightData);\n            }\n\n            getStateSet()->removeUniform(prevUniform);\n            getStateSet()->addUniform(newUniform);\n\n            for (int i = 0; i < 2; ++i)\n            {\n                for (auto& pair : mStateSetCache[i])\n                    static_cast<LightStateAttributePerObjectUniform*>(pair.second->getAttribute(osg::StateAttribute::LIGHT))->resize(getMaxLights());\n                mStateSetCache[i].clear();\n            }\n        }\n        else\n        {\n            for (int i = 0; i < 2; ++i)\n            {\n                for (auto& pair : mStateSetCache[i])\n                {\n                    auto& stateset = pair.second;\n                    osg::Uniform* uOldArray = stateset->getUniform(\"PointLightIndex\");\n                    osg::Uniform* uOldCount = stateset->getUniform(\"PointLightCount\");\n\n                    int prevCount;\n                    uOldCount->get(prevCount);\n                    int newCount = std::min(getMaxLights(), prevCount);\n                    uOldCount->set(newCount);\n\n                    osg::ref_ptr<osg::IntArray> newArray = uOldArray->getIntArray();\n                    newArray->resize(newCount);\n\n                    stateset->removeUniform(uOldArray);\n                    stateset->addUniform(new osg::Uniform(\"PointLightIndex\", newArray));\n                }\n                mStateSetCache[i].clear();\n            }\n        }\n    }\n\n    void LightManager::updateSettings()\n    {\n        if (getLightingMethod() == LightingMethod::FFP)\n            return;\n\n        mPointLightRadiusMultiplier = std::clamp(Settings::Manager::getFloat(\"light bounds multiplier\", \"Shaders\"), 0.f, 5.f);\n\n        mPointLightFadeEnd = std::max(0.f, Settings::Manager::getFloat(\"maximum light distance\", \"Shaders\"));\n        if (mPointLightFadeEnd > 0)\n        {\n            mPointLightFadeStart = std::clamp(Settings::Manager::getFloat(\"light fade start\", \"Shaders\"), 0.f, 1.f);\n            mPointLightFadeStart = mPointLightFadeEnd * mPointLightFadeStart;\n        }\n    }\n\n    void LightManager::initFFP(int targetLights)\n    {\n        setLightingMethod(LightingMethod::FFP);\n        setMaxLights(targetLights);\n\n        for (int i = 0; i < getMaxLights(); ++i)\n            mDummies.push_back(new FFPLightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light>>()));\n    }\n\n    void LightManager::initPerObjectUniform(int targetLights)\n    {\n        auto* stateset = getOrCreateStateSet();\n\n        setLightingMethod(LightingMethod::PerObjectUniform);\n        setMaxLights(targetLights);\n\n        // ensures sunlight element in our uniform array is updated when there are no point lights in scene\n        stateset->setAttributeAndModes(new LightStateAttributePerObjectUniform({}, this), osg::StateAttribute::ON);\n        stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, \"LightBuffer\", getMaxLights()));\n    }\n\n    void LightManager::initSingleUBO(int targetLights)\n    {\n        setLightingMethod(LightingMethod::SingleUBO);\n        setMaxLights(targetLights);\n\n        for (int i = 0; i < 2; ++i)\n        {\n            mLightBuffers[i] = new LightBuffer(getMaxLightsInScene());\n\n            osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;\n            ubo->setUsage(GL_STREAM_DRAW);\n\n            mLightBuffers[i]->getData()->setBufferObject(ubo);\n        }\n\n        getOrCreateStateSet()->setAttribute(new LightManagerStateAttribute(this), osg::StateAttribute::ON);\n    }\n\n    void LightManager::setLightingMethod(LightingMethod method)\n    {\n        mLightingMethod = method;\n        switch (method)\n        {\n        case LightingMethod::FFP:\n            mStateSetGenerator = std::make_unique<StateSetGeneratorFFP>();\n            break;\n        case LightingMethod::SingleUBO:\n            mStateSetGenerator = std::make_unique<StateSetGeneratorSingleUBO>();\n            break;\n        case LightingMethod::PerObjectUniform:\n            mStateSetGenerator = std::make_unique<StateSetGeneratorPerObjectUniform>();\n            break;\n        }\n        mStateSetGenerator->mLightManager = this;\n    }\n\n    void LightManager::setLightingMask(size_t mask)\n    {\n        mLightingMask = mask;\n    }\n\n    size_t LightManager::getLightingMask() const\n    {\n        return mLightingMask;\n    }\n\n    void LightManager::setStartLight(int start)\n    {\n        mStartLight = start;\n\n        if (!usingFFP()) return;\n\n        // Set default light state to zero\n        // This is necessary because shaders don't respect glDisable(GL_LIGHTX) so in addition to disabling\n        // we'll have to set a light state that has no visible effect\n        for (int i = start; i < getMaxLights(); ++i)\n        {\n            osg::ref_ptr<DisableLight> defaultLight (new DisableLight(i));\n            getOrCreateStateSet()->setAttributeAndModes(defaultLight, osg::StateAttribute::OFF);\n        }\n    }\n\n    int LightManager::getStartLight() const\n    {\n        return mStartLight;\n    }\n\n    void LightManager::update(size_t frameNum)\n    {\n        getLightIndexMap(frameNum).clear();\n        mLights.clear();\n        mLightsInViewSpace.clear();\n\n        // Do an occasional cleanup for orphaned lights.\n        for (int i = 0; i < 2; ++i)\n        {\n            if (mStateSetCache[i].size() > 5000)\n                mStateSetCache[i].clear();\n        }\n    }\n\n    void LightManager::addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum)\n    {\n        LightSourceTransform l;\n        l.mLightSource = lightSource;\n        l.mWorldMatrix = worldMat;\n        osg::Vec3f pos = osg::Vec3f(worldMat.getTrans().x(), worldMat.getTrans().y(), worldMat.getTrans().z());\n        lightSource->getLight(frameNum)->setPosition(osg::Vec4f(pos, 1.f));\n\n        mLights.push_back(l);\n    }\n\n    void LightManager::setSunlight(osg::ref_ptr<osg::Light> sun)\n    {\n        if (usingFFP()) return;\n\n        mSun = sun;\n    }\n\n    osg::ref_ptr<osg::Light> LightManager::getSunlight()\n    {\n        return mSun;\n    }\n\n    osg::ref_ptr<osg::StateSet> LightManager::getLightListStateSet(const LightList& lightList, size_t frameNum, const osg::RefMatrix* viewMatrix)\n    {\n        // possible optimization: return a StateSet containing all requested lights plus some extra lights (if a suitable one exists)\n        size_t hash = 0;\n        for (size_t i = 0; i < lightList.size(); ++i)\n        {\n            auto id = lightList[i]->mLightSource->getId();\n            Misc::hashCombine(hash, id);\n\n            if (getLightingMethod() != LightingMethod::SingleUBO)\n                continue;\n\n            if (getLightIndexMap(frameNum).find(id) != getLightIndexMap(frameNum).end())\n                continue;\n\n            int index = getLightIndexMap(frameNum).size() + 1;\n            updateGPUPointLight(index, lightList[i]->mLightSource, frameNum, viewMatrix);\n            getLightIndexMap(frameNum).emplace(lightList[i]->mLightSource->getId(), index);\n        }\n\n        auto& stateSetCache = mStateSetCache[frameNum%2];\n\n        auto found = stateSetCache.find(hash);\n        if (found != stateSetCache.end())\n        {\n            mStateSetGenerator->update(found->second, lightList, frameNum);\n            return found->second;\n        }\n\n        auto stateset = mStateSetGenerator->generate(lightList, frameNum);\n        stateSetCache.emplace(hash, stateset);\n        return stateset;\n    }\n\n    const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix, size_t frameNum)\n    {\n        bool isReflection = isReflectionCamera(camera);\n        osg::observer_ptr<osg::Camera> camPtr (camera);\n        auto it = mLightsInViewSpace.find(camPtr);\n\n        if (it == mLightsInViewSpace.end())\n        {\n            it = mLightsInViewSpace.insert(std::make_pair(camPtr, LightSourceViewBoundCollection())).first;\n\n            for (const auto& transform : mLights)\n            {\n                osg::Matrixf worldViewMat = transform.mWorldMatrix * (*viewMatrix);\n\n                float radius = transform.mLightSource->getRadius();\n\n                osg::BoundingSphere viewBound = osg::BoundingSphere(osg::Vec3f(0,0,0), radius * mPointLightRadiusMultiplier);\n                transformBoundingSphere(worldViewMat, viewBound);\n\n                if (!isReflection && mPointLightFadeEnd != 0.f)\n                {\n                    const float fadeDelta = mPointLightFadeEnd - mPointLightFadeStart;\n                    float fade = 1 - std::clamp((viewBound.center().length() - mPointLightFadeStart) / fadeDelta, 0.f, 1.f);\n                    if (fade == 0.f)\n                        continue;\n\n                    auto* light = transform.mLightSource->getLight(frameNum);\n                    light->setDiffuse(light->getDiffuse() * fade);\n                }\n\n                LightSourceViewBound l;\n                l.mLightSource = transform.mLightSource;\n                l.mViewBound = viewBound;\n                it->second.push_back(l);\n            }\n        }\n\n        if (getLightingMethod() == LightingMethod::SingleUBO)\n        {\n            if (it->second.size() > static_cast<size_t>(getMaxLightsInScene() - 1))\n            {\n                auto sorter = [] (const LightSourceViewBound& left, const LightSourceViewBound& right) {\n                    return left.mViewBound.center().length2() - left.mViewBound.radius2() < right.mViewBound.center().length2() - right.mViewBound.radius2();\n                };\n                std::sort(it->second.begin() + 1, it->second.end(), sorter);\n                it->second.erase((it->second.begin() + 1) + (getMaxLightsInScene() - 2), it->second.end());\n            }\n        }\n\n        return it->second;\n    }\n\n    void LightManager::updateGPUPointLight(int index, LightSource* lightSource, size_t frameNum,const osg::RefMatrix* viewMatrix)\n    {\n        auto* light = lightSource->getLight(frameNum);\n        auto& buf = getLightBuffer(frameNum);\n        buf->setDiffuse(index, light->getDiffuse());\n        buf->setAmbient(index, light->getAmbient());\n        buf->setAttenuationRadius(index, osg::Vec4(light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation(), lightSource->getRadius()));\n        buf->setPosition(index, light->getPosition() * (*viewMatrix));\n    }\n\n    LightSource::LightSource()\n        : mRadius(0.f)\n        , mActorFade(1.f)\n    {\n        setUpdateCallback(new CollectLightCallback);\n        mId = sLightId++;\n    }\n\n    LightSource::LightSource(const LightSource &copy, const osg::CopyOp &copyop)\n        : osg::Node(copy, copyop)\n        , mRadius(copy.mRadius)\n        , mActorFade(copy.mActorFade)\n    {\n        mId = sLightId++;\n\n        for (int i = 0; i < 2; ++i)\n            mLight[i] = new osg::Light(*copy.mLight[i].get(), copyop);\n    }\n\n    void LightListCallback::operator()(osg::Node *node, osg::NodeVisitor *nv)\n    {\n        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n\n        bool pushedState = pushLightState(node, cv);\n        traverse(node, nv);\n        if (pushedState)\n            cv->popStateSet();\n    }\n\n    bool LightListCallback::pushLightState(osg::Node *node, osgUtil::CullVisitor *cv)\n    {\n        if (!mLightManager)\n        {\n            mLightManager = findLightManager(cv->getNodePath());\n            if (!mLightManager)\n                return false;\n        }\n\n        if (!(cv->getTraversalMask() & mLightManager->getLightingMask()))\n            return false;\n\n        // Possible optimizations:\n        // - cull list of lights by the camera frustum\n        // - organize lights in a quad tree\n\n\n        // update light list if necessary\n        // makes sure we don't update it more than once per frame when rendering with multiple cameras\n        if (mLastFrameNumber != cv->getTraversalNumber())\n        {\n            mLastFrameNumber = cv->getTraversalNumber();\n\n            // Don't use Camera::getViewMatrix, that one might be relative to another camera!\n            const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix();\n            const std::vector<LightManager::LightSourceViewBound>& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix, mLastFrameNumber);\n\n            // get the node bounds in view space\n            // NB do not node->getBound() * modelView, that would apply the node's transformation twice\n            osg::BoundingSphere nodeBound;\n            osg::Transform* transform = node->asTransform();\n            if (transform)\n            {\n                for (size_t i = 0; i < transform->getNumChildren(); ++i)\n                    nodeBound.expandBy(transform->getChild(i)->getBound());\n            }\n            else\n                nodeBound = node->getBound();\n            osg::Matrixf mat = *cv->getModelViewMatrix();\n            transformBoundingSphere(mat, nodeBound);\n\n            mLightList.clear();\n            for (size_t i = 0; i < lights.size(); ++i)\n            {\n                const LightManager::LightSourceViewBound& l = lights[i];\n\n                if (mIgnoredLightSources.count(l.mLightSource))\n                    continue;\n\n                if (l.mViewBound.intersects(nodeBound))\n                    mLightList.push_back(&l);\n            }\n        }\n        if (!mLightList.empty())\n        {\n            size_t maxLights = mLightManager->getMaxLights() - mLightManager->getStartLight();\n\n            osg::StateSet* stateset = nullptr;\n\n            if (mLightList.size() > maxLights)\n            {\n                // remove lights culled by this camera\n                LightManager::LightList lightList = mLightList;\n                for (auto it = lightList.begin(); it != lightList.end() && lightList.size() > maxLights;)\n                {\n                    osg::CullStack::CullingStack& stack = cv->getModelViewCullingStack();\n\n                    osg::BoundingSphere bs = (*it)->mViewBound;\n                    bs._radius = bs._radius * 2.0;\n                    osg::CullingSet& cullingSet = stack.front();\n                    if (cullingSet.isCulled(bs))\n                    {\n                        it = lightList.erase(it);\n                        continue;\n                    }\n                    else\n                        ++it;\n                }\n\n                if (lightList.size() > maxLights)\n                {\n                    // sort by proximity to camera, then get rid of furthest away lights\n                    std::sort(lightList.begin(), lightList.end(), sortLights);\n                    while (lightList.size() > maxLights)\n                        lightList.pop_back();\n                }\n                stateset = mLightManager->getLightListStateSet(lightList, cv->getTraversalNumber(), cv->getCurrentRenderStage()->getInitialViewMatrix());\n            }\n            else\n                stateset = mLightManager->getLightListStateSet(mLightList, cv->getTraversalNumber(), cv->getCurrentRenderStage()->getInitialViewMatrix());\n\n\n            cv->pushStateSet(stateset);\n            return true;\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "components/sceneutil/lightmanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H\n#define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H\n\n#include <set>\n#include <unordered_set>\n#include <unordered_map>\n#include <memory>\n#include <array>\n\n#include <osg/Light>\n\n#include <osg/Group>\n#include <osg/NodeVisitor>\n#include <osg/observer_ptr>\n\n#include <components/shader/shadermanager.hpp>\n\n#include <components/settings/settings.hpp>\n\nnamespace osgUtil\n{\n    class CullVisitor;\n}\n\nnamespace SceneUtil\n{\n    class LightBuffer;\n    struct StateSetGenerator;\n\n    enum class LightingMethod\n    {\n        FFP,\n        PerObjectUniform,\n        SingleUBO,\n    };\n\n    /// LightSource managed by a LightManager.\n    /// @par Typically used for point lights. Spot lights are not supported yet. Directional lights affect the whole scene\n    ///     so do not need to be managed by a LightManager - so for directional lights use a plain osg::LightSource instead.\n    /// @note LightSources must be decorated by a LightManager node in order to have an effect. Typical use would\n    ///     be one LightManager as the root of the scene graph.\n    /// @note One needs to attach LightListCallback's to the scene to have objects receive lighting from LightSources.\n    ///     See the documentation of LightListCallback for more information.\n    /// @note The position of the contained osg::Light is automatically updated based on the LightSource's world position.\n    class LightSource : public osg::Node\n    {\n        // double buffered osg::Light's, since one of them may be in use by the draw thread at any given time\n        osg::ref_ptr<osg::Light> mLight[2];\n\n        // LightSource will affect objects within this radius\n        float mRadius;\n\n        int mId;\n\n        float mActorFade;\n\n    public:\n\n        META_Node(SceneUtil, LightSource)\n\n        LightSource();\n\n        LightSource(const LightSource& copy, const osg::CopyOp& copyop);\n\n        float getRadius() const\n        {\n            return mRadius;\n        }\n\n        /// The LightSource will affect objects within this radius.\n        void setRadius(float radius)\n        {\n            mRadius = radius;\n        }\n\n        void setActorFade(float alpha)\n        {\n            mActorFade = alpha;\n        }\n\n        float getActorFade() const\n        {\n            return mActorFade;\n        }\n\n        /// Get the osg::Light safe for modification in the given frame.\n        /// @par May be used externally to animate the light's color/attenuation properties,\n        /// and is used internally to synchronize the light's position with the position of the LightSource.\n        osg::Light* getLight(size_t frame)\n        {\n            return mLight[frame % 2];\n        }\n\n        /// @warning It is recommended not to replace an existing osg::Light, because there might still be\n        /// references to it in the light StateSet cache that are associated with this LightSource's ID.\n        /// These references will stay valid due to ref_ptr but will point to the old object.\n        /// @warning Do not modify the \\a light after you've called this function.\n        void setLight(osg::Light* light)\n        {\n            mLight[0] = light;\n            mLight[1] = new osg::Light(*light);\n        }\n\n        /// Get the unique ID for this light source.\n        int getId() const\n        {\n            return mId;\n        }\n    };\n\n    /// @brief Decorator node implementing the rendering of any number of LightSources that can be anywhere in the subgraph.\n    class LightManager : public osg::Group\n    {\n    public:\n        static LightingMethod getLightingMethodFromString(const std::string& value);\n        /// Returns string as used in settings file, or the empty string if the method is undefined\n        static std::string getLightingMethodString(LightingMethod method);\n\n        struct LightSourceTransform\n        {\n            LightSource* mLightSource;\n            osg::Matrixf mWorldMatrix;\n        };\n\n        struct LightSourceViewBound\n        {\n            LightSource* mLightSource;\n            osg::BoundingSphere mViewBound;\n        };\n\n        using LightList = std::vector<const LightSourceViewBound*>;\n        using SupportedMethods = std::array<bool, 3>;\n\n        META_Node(SceneUtil, LightManager)\n\n        LightManager(bool ffp = true);\n\n        LightManager(const LightManager& copy, const osg::CopyOp& copyop);\n\n        ~LightManager();\n\n        /// @param mask This mask is compared with the current Camera's cull mask to determine if lighting is desired.\n        /// By default, it's ~0u i.e. always on.\n        /// If you have some views that do not require lighting, then set the Camera's cull mask to not include\n        /// the lightingMask for a much faster cull and rendering.\n        void setLightingMask(size_t mask);\n        size_t getLightingMask() const;\n\n        /// Set the first light index that should be used by this manager, typically the number of directional lights in the scene.\n        void setStartLight(int start);\n        int getStartLight() const;\n\n        /// Internal use only, called automatically by the LightManager's UpdateCallback\n        void update(size_t frameNum);\n\n        /// Internal use only, called automatically by the LightSource's UpdateCallback\n        void addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum);\n\n        const std::vector<LightSourceViewBound>& getLightsInViewSpace(osg::Camera* camera, const osg::RefMatrix* viewMatrix, size_t frameNum);\n\n        osg::ref_ptr<osg::StateSet> getLightListStateSet(const LightList& lightList, size_t frameNum, const osg::RefMatrix* viewMatrix);\n\n        void setSunlight(osg::ref_ptr<osg::Light> sun);\n        osg::ref_ptr<osg::Light> getSunlight();\n\n        bool usingFFP() const;\n\n        LightingMethod getLightingMethod() const;\n\n        int getMaxLights() const;\n\n        int getMaxLightsInScene() const;\n\n        auto& getDummies() { return mDummies; }\n\n        auto& getLightIndexMap(size_t frameNum) { return mLightIndexMaps[frameNum%2]; }\n\n        auto& getLightBuffer(size_t frameNum) { return mLightBuffers[frameNum%2]; }\n\n        osg::Matrixf getSunlightBuffer(size_t frameNum) const { return mSunlightBuffers[frameNum%2]; }\n        void setSunlightBuffer(const osg::Matrixf& buffer, size_t frameNum) { mSunlightBuffers[frameNum%2] = buffer; }\n\n        SupportedMethods getSupportedLightingMethods() { return mSupported; }\n\n        std::map<std::string, std::string> getLightDefines() const;\n\n        void processChangedSettings(const Settings::CategorySettingVector& changed);\n\n        /// Not thread safe, it is the responsibility of the caller to stop/start threading on the viewer\n        void updateMaxLights();\n\n    private:\n        void initFFP(int targetLights);\n        void initPerObjectUniform(int targetLights);\n        void initSingleUBO(int targetLights);\n\n        void updateSettings();\n\n        void setLightingMethod(LightingMethod method);\n        void setMaxLights(int value);\n\n        void updateGPUPointLight(int index, LightSource* lightSource, size_t frameNum, const osg::RefMatrix* viewMatrix);\n\n        std::vector<LightSourceTransform> mLights;\n\n        using LightSourceViewBoundCollection = std::vector<LightSourceViewBound>;\n        std::map<osg::observer_ptr<osg::Camera>, LightSourceViewBoundCollection> mLightsInViewSpace;\n\n        // < Light list hash , StateSet >\n        using LightStateSetMap = std::map<size_t, osg::ref_ptr<osg::StateSet>>;\n        LightStateSetMap mStateSetCache[2];\n\n        std::vector<osg::ref_ptr<osg::StateAttribute>> mDummies;\n\n        int mStartLight;\n\n        size_t mLightingMask;\n\n        osg::ref_ptr<osg::Light> mSun;\n\n        osg::ref_ptr<LightBuffer> mLightBuffers[2];\n\n        osg::Matrixf mSunlightBuffers[2];\n\n        // < Light ID , Buffer Index >\n        using LightIndexMap = std::unordered_map<int, int>;\n        LightIndexMap mLightIndexMaps[2];\n\n        std::unique_ptr<StateSetGenerator> mStateSetGenerator;\n\n        LightingMethod mLightingMethod;\n\n        float mPointLightRadiusMultiplier;\n        float mPointLightFadeEnd;\n        float mPointLightFadeStart;\n\n        int mMaxLights;\n\n        SupportedMethods mSupported;\n\n        static constexpr auto mMaxLightsLowerLimit = 2;\n        static constexpr auto mMaxLightsUpperLimit = 64;\n        static constexpr auto mFFPMaxLights = 8;\n\n        static const std::unordered_map<std::string, LightingMethod> mLightingMethodSettingMap;\n    };\n\n    /// To receive lighting, objects must be decorated by a LightListCallback. Light list callbacks must be added via\n    /// node->addCullCallback(new LightListCallback). Once a light list callback is added to a node, that node and all\n    /// its child nodes can receive lighting.\n    /// @par The placement of these LightListCallbacks affects the granularity of light lists. Having too fine grained\n    /// light lists can result in degraded performance. Too coarse grained light lists can result in lights no longer\n    /// rendering when the size of a light list exceeds the OpenGL limit on the number of concurrent lights (8). A good\n    /// starting point is to attach a LightListCallback to each game object's base node.\n    /// @note Not thread safe for CullThreadPerCamera threading mode.\n    /// @note Due to lack of OSG support, the callback does not work on Drawables.\n    class LightListCallback : public osg::NodeCallback\n    {\n    public:\n        LightListCallback()\n            : mLightManager(nullptr)\n            , mLastFrameNumber(0)\n        {}\n        LightListCallback(const LightListCallback& copy, const osg::CopyOp& copyop)\n            : osg::Object(copy, copyop), osg::NodeCallback(copy, copyop)\n            , mLightManager(copy.mLightManager)\n            , mLastFrameNumber(0)\n            , mIgnoredLightSources(copy.mIgnoredLightSources)\n        {}\n\n        META_Object(SceneUtil, LightListCallback)\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override;\n\n        bool pushLightState(osg::Node* node, osgUtil::CullVisitor* nv);\n\n        std::set<SceneUtil::LightSource*>& getIgnoredLightSources() { return mIgnoredLightSources; }\n\n    private:\n        LightManager* mLightManager;\n        size_t mLastFrameNumber;\n        LightManager::LightList mLightList;\n        std::set<SceneUtil::LightSource*> mIgnoredLightSources;\n    };\n\n    void configureStateSetSunOverride(LightManager* lightManager, const osg::Light* light, osg::StateSet* stateset, int mode = osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/lightutil.cpp",
    "content": "#include \"lightutil.hpp\"\n\n#include <osg/Light>\n#include <osg/Group>\n#include <osg/ComputeBoundsVisitor>\n\n#include <components/esm/loadligh.hpp>\n#include <components/fallback/fallback.hpp>\n\n#include \"lightmanager.hpp\"\n#include \"lightcontroller.hpp\"\n#include \"util.hpp\"\n#include \"visitor.hpp\"\n#include \"positionattitudetransform.hpp\"\n\nnamespace SceneUtil\n{\n\n    void configureLight(osg::Light *light, float radius, bool isExterior)\n    {\n        float quadraticAttenuation = 0.f;\n        float linearAttenuation = 0.f;\n        float constantAttenuation = 0.f;\n\n        static const bool useConstant = Fallback::Map::getBool(\"LightAttenuation_UseConstant\");\n        static const bool useLinear = Fallback::Map::getBool(\"LightAttenuation_UseLinear\");\n        static const bool useQuadratic = Fallback::Map::getBool(\"LightAttenuation_UseQuadratic\");\n        static const float constantValue = Fallback::Map::getFloat(\"LightAttenuation_ConstantValue\");\n        static const float linearValue = Fallback::Map::getFloat(\"LightAttenuation_LinearValue\");\n        static const float quadraticValue = Fallback::Map::getFloat(\"LightAttenuation_QuadraticValue\");\n        static const float linearRadiusMult = Fallback::Map::getFloat(\"LightAttenuation_LinearRadiusMult\");\n        static const float quadraticRadiusMult = Fallback::Map::getFloat(\"LightAttenuation_QuadraticRadiusMult\");\n        static const int linearMethod = Fallback::Map::getInt(\"LightAttenuation_LinearMethod\");\n        static const int quadraticMethod = Fallback::Map::getInt(\"LightAttenuation_QuadraticMethod\");\n        static const bool outQuadInLin = Fallback::Map::getBool(\"LightAttenuation_OutQuadInLin\");\n\n        if (useConstant)\n            constantAttenuation = constantValue;\n\n        if (useLinear)\n        {\n            linearAttenuation = linearMethod == 0 ? linearValue : 0.01f;\n            float r = radius * linearRadiusMult;\n            if (r && (linearMethod == 1 || linearMethod == 2))\n                linearAttenuation = linearValue / std::pow(r, linearMethod);\n        }\n\n        if (useQuadratic && (!outQuadInLin || isExterior))\n        {\n            quadraticAttenuation = quadraticMethod == 0 ? quadraticValue : 0.01f;\n            float r = radius * quadraticRadiusMult;\n            if (r && (quadraticMethod == 1 || quadraticMethod == 2))\n                quadraticAttenuation = quadraticValue / std::pow(r, quadraticMethod);\n        }\n\n        light->setConstantAttenuation(constantAttenuation);\n        light->setLinearAttenuation(linearAttenuation);\n        light->setQuadraticAttenuation(quadraticAttenuation);\n    }\n\n    osg::ref_ptr<LightSource> addLight(osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior)\n    {\n        SceneUtil::FindByNameVisitor visitor(\"AttachLight\");\n        node->accept(visitor);\n\n        osg::Group* attachTo = nullptr;\n        if (visitor.mFoundNode)\n        {\n            attachTo = visitor.mFoundNode;\n        }\n        else\n        {\n            osg::ComputeBoundsVisitor computeBound;\n            computeBound.setTraversalMask(~partsysMask);\n            // We want the bounds of all children of the node, ignoring the node's local transformation\n            // So do a traverse(), not accept()\n            computeBound.traverse(*node);\n\n            // PositionAttitudeTransform seems to be slightly faster than MatrixTransform\n            osg::ref_ptr<SceneUtil::PositionAttitudeTransform> trans(new SceneUtil::PositionAttitudeTransform);\n            trans->setPosition(computeBound.getBoundingBox().center());\n\n            node->addChild(trans);\n\n            attachTo = trans;\n        }\n\n        osg::ref_ptr<LightSource> lightSource = createLightSource(esmLight, lightMask, isExterior, osg::Vec4f(0,0,0,1));\n        attachTo->addChild(lightSource);\n        return lightSource;\n    }\n\n    osg::ref_ptr<LightSource> createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient)\n    {\n        osg::ref_ptr<SceneUtil::LightSource> lightSource (new SceneUtil::LightSource);\n        osg::ref_ptr<osg::Light> light (new osg::Light);\n        lightSource->setNodeMask(lightMask);\n\n        float radius = esmLight->mData.mRadius;\n        lightSource->setRadius(radius);\n\n        configureLight(light, radius, isExterior);\n\n        osg::Vec4f diffuse = SceneUtil::colourFromRGB(esmLight->mData.mColor);\n        if (esmLight->mData.mFlags & ESM::Light::Negative)\n        {\n            diffuse *= -1;\n            diffuse.a() = 1;\n        }\n        light->setDiffuse(diffuse);\n        light->setAmbient(ambient);\n        light->setSpecular(osg::Vec4f(0,0,0,0));\n\n        lightSource->setLight(light);\n\n        osg::ref_ptr<SceneUtil::LightController> ctrl (new SceneUtil::LightController);\n        ctrl->setDiffuse(light->getDiffuse());\n        if (esmLight->mData.mFlags & ESM::Light::Flicker)\n            ctrl->setType(SceneUtil::LightController::LT_Flicker);\n        if (esmLight->mData.mFlags & ESM::Light::FlickerSlow)\n            ctrl->setType(SceneUtil::LightController::LT_FlickerSlow);\n        if (esmLight->mData.mFlags & ESM::Light::Pulse)\n            ctrl->setType(SceneUtil::LightController::LT_Pulse);\n        if (esmLight->mData.mFlags & ESM::Light::PulseSlow)\n            ctrl->setType(SceneUtil::LightController::LT_PulseSlow);\n\n        lightSource->addUpdateCallback(ctrl);\n\n        return lightSource;\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/lightutil.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_LIGHTUTIL_H\n#define OPENMW_COMPONENTS_LIGHTUTIL_H\n\n#include <osg/ref_ptr>\n#include <osg/Vec4f>\n\nnamespace osg\n{\n    class Group;\n    class Light;\n}\n\nnamespace ESM\n{\n    struct Light;\n}\n\nnamespace SceneUtil\n{\n    class LightSource;\n\n    /// @brief Set up global attenuation settings for an osg::Light.\n    /// @param radius The radius of the light source.\n    /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use.\n    void configureLight (osg::Light *light, float radius, bool isExterior);\n\n    /// @brief Convert an ESM::Light to a SceneUtil::LightSource, and add it to a sub graph.\n    /// @note If the sub graph contains a node named \"AttachLight\" (case insensitive), then the light is added to that.\n    /// Otherwise, the light is added in the center of the node's bounds.\n    /// @param node The sub graph to add a light to\n    /// @param esmLight The light definition coming from the game files containing radius, color, flicker, etc.\n    /// @param partsysMask Node mask to ignore when computing the sub graph's bounding box.\n    /// @param lightMask Mask to assign to the newly created LightSource.\n    /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use.\n    osg::ref_ptr<LightSource> addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior);\n\n    /// @brief Convert an ESM::Light to a SceneUtil::LightSource, and return it.\n    /// @param esmLight The light definition coming from the game files containing radius, color, flicker, etc.\n    /// @param lightMask Mask to assign to the newly created LightSource.\n    /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use.\n    /// @param ambient Ambient component of the light.\n    osg::ref_ptr<LightSource> createLightSource (const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1));\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/morphgeometry.cpp",
    "content": "#include \"morphgeometry.hpp\"\n\n#include <cassert>\n\n#include <osg/Version>\n\nnamespace SceneUtil\n{\n\nMorphGeometry::MorphGeometry()\n    : mLastFrameNumber(0)\n    , mDirty(true)\n    , mMorphedBoundingBox(false)\n{\n\n}\n\nMorphGeometry::MorphGeometry(const MorphGeometry &copy, const osg::CopyOp &copyop)\n    : osg::Drawable(copy, copyop)\n    , mMorphTargets(copy.mMorphTargets)\n    , mLastFrameNumber(0)\n    , mDirty(true)\n    , mMorphedBoundingBox(false)\n{\n    setSourceGeometry(copy.getSourceGeometry());\n}\n\nvoid MorphGeometry::setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeom)\n{\n    mSourceGeometry = sourceGeom;\n\n    for (unsigned int i=0; i<2; ++i)\n    {\n        mGeometry[i] = new osg::Geometry(*mSourceGeometry, osg::CopyOp::SHALLOW_COPY);\n\n        const osg::Geometry& from = *mSourceGeometry;\n        osg::Geometry& to = *mGeometry[i];\n        to.setSupportsDisplayList(false);\n        to.setUseVertexBufferObjects(true);\n        to.setCullingActive(false); // make sure to disable culling since that's handled by this class\n\n        // vertices are modified every frame, so we need to deep copy them.\n        // assign a dedicated VBO to make sure that modifications don't interfere with source geometry's VBO.\n        osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);\n        vbo->setUsage(GL_DYNAMIC_DRAW_ARB);\n\n        osg::ref_ptr<osg::Array> vertexArray = static_cast<osg::Array*>(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL));\n        if (vertexArray)\n        {\n            vertexArray->setVertexBufferObject(vbo);\n            to.setVertexArray(vertexArray);\n        }\n    }\n}\n\nvoid MorphGeometry::addMorphTarget(osg::Vec3Array *offsets, float weight)\n{\n    mMorphTargets.push_back(MorphTarget(offsets, weight));\n    mMorphedBoundingBox = false;\n    dirty();\n}\n\nvoid MorphGeometry::dirty()\n{\n    mDirty = true;\n    if (!mMorphedBoundingBox)\n        dirtyBound();\n}\n\nosg::ref_ptr<osg::Geometry> MorphGeometry::getSourceGeometry() const\n{\n    return mSourceGeometry;\n}\n\nvoid MorphGeometry::accept(osg::NodeVisitor &nv)\n{\n    if (!nv.validNodeMask(*this))\n        return;\n\n    nv.pushOntoNodePath(this);\n\n    if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)\n        cull(&nv);\n    else\n        nv.apply(*this);\n\n    nv.popFromNodePath();\n}\n\nvoid MorphGeometry::accept(osg::PrimitiveFunctor& func) const\n{\n    getGeometry(mLastFrameNumber)->accept(func);\n}\n\nosg::BoundingBox MorphGeometry::computeBoundingBox() const\n{\n    bool anyMorphTarget = false;\n    for (unsigned int i=0; i<mMorphTargets.size(); ++i)\n        if (mMorphTargets[i].getWeight() > 0)\n        {\n            anyMorphTarget = true;\n            break;\n        }\n\n    // before the MorphGeometry has started animating, we will use a regular bounding box (this is required\n    // for correct object placements, which uses the bounding box)\n    if (!mMorphedBoundingBox && !anyMorphTarget)\n    {\n        return mSourceGeometry->getBoundingBox();\n    }\n    // once it animates, use a bounding box that encompasses all possible animations so as to avoid recalculating\n    else\n    {\n        mMorphedBoundingBox = true;\n\n        osg::Vec3Array& sourceVerts = *static_cast<osg::Vec3Array*>(mSourceGeometry->getVertexArray());\n        std::vector<osg::BoundingBox> vertBounds(sourceVerts.size());\n\n        // Since we don't know what combinations of morphs are being applied we need to keep track of a bounding box for each vertex.\n        // The minimum/maximum of the box is the minimum/maximum offset the vertex can have from its starting position.\n\n        // Start with zero offsets which will happen when no morphs are applied.\n        for (unsigned int i=0; i<vertBounds.size(); ++i)\n            vertBounds[i].set(osg::Vec3f(0,0,0), osg::Vec3f(0,0,0));\n\n        for (unsigned int i = 0; i < mMorphTargets.size(); ++i)\n        {\n            const osg::Vec3Array& offsets = *mMorphTargets[i].getOffsets();\n            for (unsigned int j=0; j<offsets.size() && j<vertBounds.size(); ++j)\n            {\n                osg::BoundingBox& bounds = vertBounds[j];\n                bounds.expandBy(bounds._max + offsets[j]);\n                bounds.expandBy(bounds._min + offsets[j]);\n            }\n        }\n\n        osg::BoundingBox box;\n        for (unsigned int i=0; i<vertBounds.size(); ++i)\n        {\n            vertBounds[i]._max += sourceVerts[i];\n            vertBounds[i]._min += sourceVerts[i];\n            box.expandBy(vertBounds[i]);\n        }\n        return box;\n    }\n}\n\nvoid MorphGeometry::cull(osg::NodeVisitor *nv)\n{\n    if (mLastFrameNumber == nv->getTraversalNumber() || !mDirty)\n    {\n        osg::Geometry& geom = *getGeometry(mLastFrameNumber);\n        nv->pushOntoNodePath(&geom);\n        nv->apply(geom);\n        nv->popFromNodePath();\n        return;\n    }\n\n    mDirty = false;\n    mLastFrameNumber = nv->getTraversalNumber();\n    osg::Geometry& geom = *getGeometry(mLastFrameNumber);\n\n    const osg::Vec3Array* positionSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getVertexArray());\n    osg::Vec3Array* positionDst = static_cast<osg::Vec3Array*>(geom.getVertexArray());\n    assert(positionSrc->size() == positionDst->size());\n    for (unsigned int vertex=0; vertex<positionSrc->size(); ++vertex)\n        (*positionDst)[vertex] = (*positionSrc)[vertex];\n\n    for (unsigned int i=0; i<mMorphTargets.size(); ++i)\n    {\n        float weight = mMorphTargets[i].getWeight();\n        if (weight == 0.f)\n            continue;\n        const osg::Vec3Array* offsets = mMorphTargets[i].getOffsets();\n        for (unsigned int vertex=0; vertex<positionSrc->size(); ++vertex)\n            (*positionDst)[vertex] += (*offsets)[vertex] * weight;\n    }\n\n    positionDst->dirty();\n\n#if OSG_MIN_VERSION_REQUIRED(3, 5, 6)\n    geom.dirtyGLObjects();\n#endif\n\n    nv->pushOntoNodePath(&geom);\n    nv->apply(geom);\n    nv->popFromNodePath();\n}\n\nosg::Geometry* MorphGeometry::getGeometry(unsigned int frame) const\n{\n    return mGeometry[frame%2];\n}\n\n\n}\n"
  },
  {
    "path": "components/sceneutil/morphgeometry.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_MORPHGEOMETRY_H\n#define OPENMW_COMPONENTS_MORPHGEOMETRY_H\n\n#include <osg/Geometry>\n\nnamespace SceneUtil\n{\n\n    /// @brief Vertex morphing implementation.\n    /// @note The internal Geometry used for rendering is double buffered, this allows updates to be done in a thread safe way while\n    /// not compromising rendering performance. This is crucial when using osg's default threading model of DrawThreadPerContext.\n    class MorphGeometry : public osg::Drawable\n    {\n    public:\n        MorphGeometry();\n        MorphGeometry(const MorphGeometry& copy, const osg::CopyOp& copyop);\n\n        META_Object(SceneUtil, MorphGeometry)\n\n        /// Initialize this geometry from the source geometry.\n        /// @note The source geometry will not be modified.\n        void setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeom);\n\n        // Currently empty as this is difficult to implement. Technically we would need to compile both internal geometries in separate frames but this method is only called once. Alternatively we could compile just the static parts of the model.\n        void compileGLObjects(osg::RenderInfo& renderInfo) const override {}\n\n        class MorphTarget\n        {\n        protected:\n            osg::ref_ptr<osg::Vec3Array> mOffsets;\n            float mWeight;\n        public:\n            MorphTarget(osg::Vec3Array* offsets, float w = 1.0) : mOffsets(offsets), mWeight(w) {}\n            void setWeight(float weight) { mWeight = weight; }\n            float getWeight() const { return mWeight; }\n            osg::Vec3Array* getOffsets() { return mOffsets.get(); }\n            const osg::Vec3Array* getOffsets() const { return mOffsets.get(); }\n            void setOffsets(osg::Vec3Array* offsets) { mOffsets = offsets; }\n        };\n\n        typedef std::vector<MorphTarget> MorphTargetList;\n\n        virtual void addMorphTarget( osg::Vec3Array* offsets, float weight = 1.0 );\n\n        /** Set the MorphGeometry dirty.*/\n        void dirty();\n\n        /** Get the list of MorphTargets.*/\n        const MorphTargetList& getMorphTargetList() const { return mMorphTargets; }\n\n        /** Get the list of MorphTargets. Warning if you modify this array you will have to call dirty() */\n        MorphTargetList& getMorphTargetList() { return mMorphTargets; }\n\n        /** Return the \\c MorphTarget at position \\c i.*/\n        inline const MorphTarget& getMorphTarget( unsigned int i ) const { return mMorphTargets[i]; }\n\n        /** Return the \\c MorphTarget at position \\c i.*/\n        inline MorphTarget& getMorphTarget( unsigned int i ) { return mMorphTargets[i]; }\n\n        osg::ref_ptr<osg::Geometry> getSourceGeometry() const;\n\n        void accept(osg::NodeVisitor &nv) override;\n        bool supports(const osg::PrimitiveFunctor&) const override { return true; }\n        void accept(osg::PrimitiveFunctor&) const override;\n\n        osg::BoundingBox computeBoundingBox() const override;\n\n    private:\n        void cull(osg::NodeVisitor* nv);\n\n        MorphTargetList mMorphTargets;\n\n        osg::ref_ptr<osg::Geometry> mSourceGeometry;\n\n        osg::ref_ptr<osg::Geometry> mGeometry[2];\n        osg::Geometry* getGeometry(unsigned int frame) const;\n\n        unsigned int mLastFrameNumber;\n        bool mDirty; // Have any morph targets changed?\n\n        mutable bool mMorphedBoundingBox;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/mwshadowtechnique.cpp",
    "content": "/* This file is based on OpenSceneGraph's src/osgShadow/ViewDependentShadowMap.cpp.\n * Where applicable, any changes made are covered by OpenMW's GPL 3 license, not the OSGPL.\n * The original copyright notice is listed below.\n */\n\n/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2011 Robert Osfield\n *\n * This library is open source and may be redistributed and/or modified under\n * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or\n * (at your option) any later version.  The full license is in LICENSE file\n * included with this distribution, and on the openscenegraph.org website.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * OpenSceneGraph Public License for more details.\n*/\n\n#include \"mwshadowtechnique.hpp\"\n\n#include <osgShadow/ShadowedScene>\n#include <osg/CullFace>\n#include <osg/Geometry>\n#include <osg/io_utils>\n#include <osg/Depth>\n\n#include <sstream>\n#include \"shadowsbin.hpp\"\n\nnamespace {\n\nusing namespace osgShadow;\nusing namespace SceneUtil;\n\n#define dbl_max std::numeric_limits<double>::max()\n\n//////////////////////////////////////////////////////////////////\n// fragment shader\n//\n#if 0\nstatic const char fragmentShaderSource_withBaseTexture[] =\n        \"uniform sampler2D baseTexture;                                          \\n\"\n        \"uniform sampler2DShadow shadowTexture;                                  \\n\"\n        \"                                                                        \\n\"\n        \"void main(void)                                                         \\n\"\n        \"{                                                                       \\n\"\n        \"  vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor;     \\n\"\n        \"  vec4 color = texture2D( baseTexture, gl_TexCoord[0].xy );                                            \\n\"\n        \"  color *= mix( colorAmbientEmissive, gl_Color, shadow2DProj( shadowTexture, gl_TexCoord[1] ).r );     \\n\"\n        \"  gl_FragColor = color;                                                                                \\n\"\n        \"} \\n\";\n#else\nstatic const char fragmentShaderSource_withBaseTexture[] =\n        \"uniform sampler2D baseTexture;                                          \\n\"\n        \"uniform int baseTextureUnit;                                            \\n\"\n        \"uniform sampler2DShadow shadowTexture0;                                 \\n\"\n        \"uniform int shadowTextureUnit0;                                         \\n\"\n        \"                                                                        \\n\"\n        \"void main(void)                                                         \\n\"\n        \"{                                                                       \\n\"\n        \"  vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor;     \\n\"\n        \"  vec4 color = texture2D( baseTexture, gl_TexCoord[baseTextureUnit].xy );                                              \\n\"\n        \"  color *= mix( colorAmbientEmissive, gl_Color, shadow2DProj( shadowTexture0, gl_TexCoord[shadowTextureUnit0] ).r );     \\n\"\n        \"  gl_FragColor = color;                                                                                                \\n\"\n        \"} \\n\";\n\nstatic const char fragmentShaderSource_withBaseTexture_twoShadowMaps[] =\n        \"uniform sampler2D baseTexture;                                          \\n\"\n        \"uniform int baseTextureUnit;                                            \\n\"\n        \"uniform sampler2DShadow shadowTexture0;                                 \\n\"\n        \"uniform int shadowTextureUnit0;                                         \\n\"\n        \"uniform sampler2DShadow shadowTexture1;                                 \\n\"\n        \"uniform int shadowTextureUnit1;                                         \\n\"\n        \"                                                                        \\n\"\n        \"void main(void)                                                         \\n\"\n        \"{                                                                       \\n\"\n        \"  vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor;     \\n\"\n        \"  vec4 color = texture2D( baseTexture, gl_TexCoord[baseTextureUnit].xy );              \\n\"\n        \"  float shadow0 = shadow2DProj( shadowTexture0, gl_TexCoord[shadowTextureUnit0] ).r;   \\n\"\n        \"  float shadow1 = shadow2DProj( shadowTexture1, gl_TexCoord[shadowTextureUnit1] ).r;   \\n\"\n        \"  color *= mix( colorAmbientEmissive, gl_Color, shadow0*shadow1 );                     \\n\"\n        \"  gl_FragColor = color;                                                                \\n\"\n        \"} \\n\";\n#endif\n\nstd::string debugVertexShaderSource = \"void main(void){gl_Position = gl_Vertex; gl_TexCoord[0]=gl_MultiTexCoord0;}\";\nstd::string debugFragmentShaderSource =\n        \"uniform sampler2D texture;                                              \\n\"\n        \"                                                                        \\n\"\n        \"void main(void)                                                         \\n\"\n        \"{                                                                       \\n\"\n#if 1\n        \"    float f = texture2D(texture, gl_TexCoord[0].xy).r;                  \\n\"\n        \"                                                                        \\n\"\n        \"    f = 256.0 * f;                                                      \\n\"\n        \"    float fC = floor( f ) / 256.0;                                      \\n\"\n        \"                                                                        \\n\"\n        \"    f = 256.0 * fract( f );                                             \\n\"\n        \"    float fS = floor( f ) / 256.0;                                      \\n\"\n        \"                                                                        \\n\"\n        \"    f = 256.0 * fract( f );                                             \\n\"\n        \"    float fH = floor( f ) / 256.0;                                      \\n\"\n        \"                                                                        \\n\"\n        \"    fS *= 0.5;                                                          \\n\"\n        \"    fH = ( fH  * 0.34 + 0.66 ) * ( 1.0 - fS );                          \\n\"\n        \"                                                                        \\n\"\n        \"    vec3 rgb = vec3( ( fC > 0.5 ? ( 1.0 - fC ) : fC ),                  \\n\"\n        \"                     abs( fC - 0.333333 ),                              \\n\"\n        \"                     abs( fC - 0.666667 ) );                            \\n\"\n        \"                                                                        \\n\"\n        \"    rgb = min( vec3( 1.0, 1.0, 1.0 ), 3.0 * rgb );                      \\n\"\n        \"                                                                        \\n\"\n        \"    float fMax = max( max( rgb.r, rgb.g ), rgb.b );                     \\n\"\n        \"    fMax = 1.0 / fMax;                                                  \\n\"\n        \"                                                                        \\n\"\n        \"    vec3 color = fMax * rgb;                                            \\n\"\n        \"                                                                        \\n\"\n        \"    gl_FragColor =  vec4( fS + fH * color, 1 );                         \\n\"\n#else\n        \"    gl_FragColor = texture2D(texture, gl_TexCoord[0].xy);               \\n\"\n#endif\n        \"}                                                                       \\n\";\n\nstd::string debugFrustumVertexShaderSource = \"varying float depth; uniform mat4 transform; void main(void){gl_Position = transform * gl_Vertex; depth = gl_Position.z / gl_Position.w;}\";\nstd::string debugFrustumFragmentShaderSource =\n        \"varying float depth;                                                    \\n\"\n        \"                                                                        \\n\"\n        \"void main(void)                                                         \\n\"\n        \"{                                                                       \\n\"\n#if 1\n        \"    float f = depth;                                                    \\n\"\n        \"                                                                        \\n\"\n        \"    f = 256.0 * f;                                                      \\n\"\n        \"    float fC = floor( f ) / 256.0;                                      \\n\"\n        \"                                                                        \\n\"\n        \"    f = 256.0 * fract( f );                                             \\n\"\n        \"    float fS = floor( f ) / 256.0;                                      \\n\"\n        \"                                                                        \\n\"\n        \"    f = 256.0 * fract( f );                                             \\n\"\n        \"    float fH = floor( f ) / 256.0;                                      \\n\"\n        \"                                                                        \\n\"\n        \"    fS *= 0.5;                                                          \\n\"\n        \"    fH = ( fH  * 0.34 + 0.66 ) * ( 1.0 - fS );                          \\n\"\n        \"                                                                        \\n\"\n        \"    vec3 rgb = vec3( ( fC > 0.5 ? ( 1.0 - fC ) : fC ),                  \\n\"\n        \"                     abs( fC - 0.333333 ),                              \\n\"\n        \"                     abs( fC - 0.666667 ) );                            \\n\"\n        \"                                                                        \\n\"\n        \"    rgb = min( vec3( 1.0, 1.0, 1.0 ), 3.0 * rgb );                      \\n\"\n        \"                                                                        \\n\"\n        \"    float fMax = max( max( rgb.r, rgb.g ), rgb.b );                     \\n\"\n        \"    fMax = 1.0 / fMax;                                                  \\n\"\n        \"                                                                        \\n\"\n        \"    vec3 color = fMax * rgb;                                            \\n\"\n        \"                                                                        \\n\"\n        \"    gl_FragColor =  vec4( fS + fH * color, 1 );                         \\n\"\n#else\n        \"    gl_FragColor = vec4(0.0, 0.0, 1.0, 0.0);                            \\n\"\n#endif\n        \"}                                                                       \\n\";\n\n\ntemplate<class T>\nclass RenderLeafTraverser : public T\n{\npublic:\n\n    RenderLeafTraverser()\n    {\n    }\n\n    void traverse(const osgUtil::RenderStage* rs)\n    {\n        traverse(static_cast<const osgUtil::RenderBin*>(rs));\n    }\n\n    void traverse(const osgUtil::RenderBin* renderBin)\n    {\n        const osgUtil::RenderBin::RenderBinList& rbl = renderBin->getRenderBinList();\n        for(osgUtil::RenderBin::RenderBinList::const_iterator itr = rbl.begin();\n            itr != rbl.end();\n            ++itr)\n        {\n            traverse(itr->second.get());\n        }\n\n        const osgUtil::RenderBin::RenderLeafList& rll = renderBin->getRenderLeafList();\n        for(osgUtil::RenderBin::RenderLeafList::const_iterator itr = rll.begin();\n            itr != rll.end();\n            ++itr)\n        {\n            handle(*itr);\n        }\n\n        const osgUtil::RenderBin::StateGraphList& rgl = renderBin->getStateGraphList();\n        for(osgUtil::RenderBin::StateGraphList::const_iterator itr = rgl.begin();\n            itr != rgl.end();\n            ++itr)\n        {\n            traverse(*itr);\n        }\n\n    }\n\n    void traverse(const osgUtil::StateGraph* stateGraph)\n    {\n        const osgUtil::StateGraph::ChildList& cl = stateGraph->_children;\n        for(osgUtil::StateGraph::ChildList::const_iterator itr = cl.begin();\n            itr != cl.end();\n            ++itr)\n        {\n            traverse(itr->second.get());\n        }\n\n        const osgUtil::StateGraph::LeafList& ll = stateGraph->_leaves;\n        for(osgUtil::StateGraph::LeafList::const_iterator itr = ll.begin();\n            itr != ll.end();\n            ++itr)\n        {\n            handle(itr->get());\n        }\n    }\n\n    inline void handle(const osgUtil::RenderLeaf* renderLeaf)\n    {\n        this->operator()(renderLeaf);\n    }\n};\n\n///////////////////////////////////////////////////////////////////////////////////////////////\n//\n// VDSMCameraCullCallback\n//\nclass VDSMCameraCullCallback : public osg::NodeCallback\n{\n    public:\n\n        VDSMCameraCullCallback(MWShadowTechnique* vdsm, osg::Polytope& polytope);\n\n        void operator()(osg::Node*, osg::NodeVisitor* nv) override;\n\n        osg::RefMatrix* getProjectionMatrix() { return _projectionMatrix.get(); }\n        osgUtil::RenderStage* getRenderStage() { return _renderStage.get(); }\n\n    protected:\n\n        MWShadowTechnique*                      _vdsm;\n        osg::ref_ptr<osg::RefMatrix>            _projectionMatrix;\n        osg::ref_ptr<osgUtil::RenderStage>      _renderStage;\n        osg::Polytope                           _polytope;\n};\n\nVDSMCameraCullCallback::VDSMCameraCullCallback(MWShadowTechnique* vdsm, osg::Polytope& polytope):\n    _vdsm(vdsm),\n    _polytope(polytope)\n{\n}\n\nvoid VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)\n{\n    osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);\n    osg::Camera* camera = node->asCamera();\n    OSG_INFO<<\"VDSMCameraCullCallback::operator()(osg::Node* \"<<camera<<\", osg::NodeVisitor* \"<<cv<<\")\"<<std::endl;\n\n#if 1\n    if (!_polytope.empty())\n    {\n        OSG_INFO<<\"Pushing custom Polytope\"<<std::endl;\n\n        osg::CullingSet& cs = cv->getProjectionCullingStack().back();\n\n        cs.setFrustum(_polytope);\n\n        cv->pushCullingSet();\n    }\n#endif\n    // bin has to go inside camera cull or the rendertexture stage will override it\n    static osg::ref_ptr<osg::StateSet> ss;\n    if (!ss)\n    {\n        ShadowsBinAdder adder(\"ShadowsBin\", _vdsm->getCastingPrograms());\n        ss = new osg::StateSet;\n        ss->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, \"ShadowsBin\", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS);\n    }\n    cv->pushStateSet(ss);\n    if (_vdsm->getShadowedScene())\n    {\n        _vdsm->getShadowedScene()->osg::Group::traverse(*nv);\n    }\n    cv->popStateSet();\n#if 1\n    if (!_polytope.empty())\n    {\n        OSG_INFO<<\"Popping custom Polytope\"<<std::endl;\n        cv->popCullingSet();\n    }\n#endif\n\n    _renderStage = cv->getCurrentRenderBin()->getStage();\n\n    OSG_INFO<<\"VDSM second : _renderStage = \"<<_renderStage<<std::endl;\n\n    if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)\n    {\n        // make sure that the near plane is computed correctly.\n        cv->computeNearPlane();\n\n        osg::Matrixd projection = *(cv->getProjectionMatrix());\n\n        OSG_INFO<<\"RTT Projection matrix \"<<projection<<std::endl;\n\n        osg::Matrix::value_type left, right, bottom, top, zNear, zFar;\n        osg::Matrix::value_type epsilon = 1e-6;\n        if (fabs(projection(0,3))<epsilon  && fabs(projection(1,3))<epsilon  && fabs(projection(2,3))<epsilon )\n        {\n            projection.getOrtho(left, right,\n                                bottom, top,\n                                zNear,  zFar);\n\n            OSG_INFO<<\"Ortho zNear=\"<<zNear<<\", zFar=\"<<zFar<<std::endl;\n        }\n        else\n        {\n            projection.getFrustum(left, right,\n                                bottom, top,\n                                zNear,  zFar);\n\n            OSG_INFO<<\"Frustum zNear=\"<<zNear<<\", zFar=\"<<zFar<<std::endl;\n        }\n\n        OSG_INFO<<\"Calculated zNear = \"<<cv->getCalculatedNearPlane()<<\", zFar = \"<<cv->getCalculatedFarPlane()<<std::endl;\n\n        zNear = osg::maximum(zNear, cv->getCalculatedNearPlane());\n        zFar = osg::minimum(zFar, cv->getCalculatedFarPlane());\n\n        cv->setCalculatedNearPlane(zNear);\n        cv->setCalculatedFarPlane(zFar);\n\n        cv->clampProjectionMatrix(projection, zNear, zFar);\n\n        //OSG_INFO<<\"RTT zNear = \"<<zNear<<\", zFar = \"<<zFar<<std::endl;\n        OSG_INFO<<\"RTT Projection matrix after clamping \"<<projection<<std::endl;\n\n        camera->setProjectionMatrix(projection);\n    }\n\n    _projectionMatrix = cv->getProjectionMatrix();\n}\n\n} // namespace\n\nMWShadowTechnique::ComputeLightSpaceBounds::ComputeLightSpaceBounds(osg::Viewport* viewport, const osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix) :\n    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN)\n{\n    setCullingMode(osg::CullSettings::VIEW_FRUSTUM_CULLING);\n\n    pushViewport(viewport);\n    pushProjectionMatrix(new osg::RefMatrix(projectionMatrix));\n    pushModelViewMatrix(new osg::RefMatrix(viewMatrix), osg::Transform::ABSOLUTE_RF);\n\n    setName(\"SceneUtil::MWShadowTechnique::ComputeLightSpaceBounds,AcceptedByComponentsTerrainQuadTreeWorld\");\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Node& node)\n{\n    if (isCulled(node)) return;\n\n    // push the culling mode.\n    pushCurrentMask();\n\n    traverse(node);\n\n    // pop the culling mode.\n    popCurrentMask();\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Drawable& drawable)\n{\n    if (isCulled(drawable)) return;\n\n    // push the culling mode.\n    pushCurrentMask();\n\n    updateBound(drawable.getBoundingBox());\n\n    // pop the culling mode.\n    popCurrentMask();\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::apply(Terrain::QuadTreeWorld & quadTreeWorld)\n{\n    // For now, just expand the bounds fully as terrain will fill them up and possible ways to detect which terrain definitely won't cast shadows aren't implemented.\n\n    update(osg::Vec3(-1.0, -1.0, 0.0));\n    update(osg::Vec3(1.0, 1.0, 0.0));\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Billboard&)\n{\n    OSG_INFO << \"Warning Billboards not yet supported\" << std::endl;\n    return;\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Projection&)\n{\n    // projection nodes won't affect a shadow map so their subgraphs should be ignored\n    return;\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Transform& transform)\n{\n    if (isCulled(transform)) return;\n\n    // push the culling mode.\n    pushCurrentMask();\n\n    // absolute transforms won't affect a shadow map so their subgraphs should be ignored.\n    if (transform.getReferenceFrame() == osg::Transform::RELATIVE_RF)\n    {\n        osg::ref_ptr<osg::RefMatrix> matrix = new osg::RefMatrix(*getModelViewMatrix());\n        transform.computeLocalToWorldMatrix(*matrix, this);\n        pushModelViewMatrix(matrix.get(), transform.getReferenceFrame());\n\n        traverse(transform);\n\n        popModelViewMatrix();\n    }\n\n    // pop the culling mode.\n    popCurrentMask();\n\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Camera&)\n{\n    // camera nodes won't affect a shadow map so their subgraphs should be ignored\n    return;\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::updateBound(const osg::BoundingBox& bb)\n{\n    if (!bb.valid()) return;\n\n    const osg::Matrix& matrix = *getModelViewMatrix() * *getProjectionMatrix();\n\n    update(bb.corner(0) * matrix);\n    update(bb.corner(1) * matrix);\n    update(bb.corner(2) * matrix);\n    update(bb.corner(3) * matrix);\n    update(bb.corner(4) * matrix);\n    update(bb.corner(5) * matrix);\n    update(bb.corner(6) * matrix);\n    update(bb.corner(7) * matrix);\n}\n\nvoid MWShadowTechnique::ComputeLightSpaceBounds::update(const osg::Vec3& v)\n{\n    if (v.z()<-1.0f)\n    {\n        //OSG_NOTICE<<\"discarding(\"<<v<<\")\"<<std::endl;\n        return;\n    }\n    float x = v.x();\n    if (x<-1.0f) x = -1.0f;\n    if (x>1.0f) x = 1.0f;\n    float y = v.y();\n    if (y<-1.0f) y = -1.0f;\n    if (y>1.0f) y = 1.0f;\n    _bb.expandBy(osg::Vec3(x, y, v.z()));\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////\n//\n// LightData\n//\nMWShadowTechnique::LightData::LightData(MWShadowTechnique::ViewDependentData* vdd):\n    _viewDependentData(vdd),\n    directionalLight(false)\n{\n}\n\nvoid MWShadowTechnique::LightData::setLightData(osg::RefMatrix* lm, const osg::Light* l, const osg::Matrixd& modelViewMatrix)\n{\n    lightMatrix = lm;\n    light = l;\n\n    lightPos = light->getPosition();\n    directionalLight = (light->getPosition().w()== 0.0);\n    if (directionalLight)\n    {\n        lightPos3.set(0.0, 0.0, 0.0); // directional light has no destinct position\n        lightDir.set(-lightPos.x(), -lightPos.y(), -lightPos.z());\n        lightDir.normalize();\n        OSG_INFO<<\"   Directional light, lightPos=\"<<lightPos<<\", lightDir=\"<<lightDir<<std::endl;\n        if (lightMatrix.valid() && *lightMatrix != osg::Matrixf(modelViewMatrix))\n        {\n            OSG_INFO<<\"   Light matrix \"<<*lightMatrix<<std::endl;\n            osg::Matrix lightToLocalMatrix(*lightMatrix * osg::Matrix::inverse(modelViewMatrix) );\n            lightDir = osg::Matrix::transform3x3( lightDir, lightToLocalMatrix );\n            lightDir.normalize();\n            OSG_INFO<<\"   new LightDir =\"<<lightDir<<std::endl;\n        }\n    }\n    else\n    {\n        OSG_INFO<<\"   Positional light, lightPos=\"<<lightPos<<std::endl;\n        lightDir = light->getDirection();\n        lightDir.normalize();\n        if (lightMatrix.valid())\n        {\n            OSG_INFO<<\"   Light matrix \"<<*lightMatrix<<std::endl;\n            osg::Matrix lightToLocalMatrix(*lightMatrix * osg::Matrix::inverse(modelViewMatrix) );\n            lightPos = lightPos * lightToLocalMatrix;\n            lightDir = osg::Matrix::transform3x3( lightDir, lightToLocalMatrix );\n            lightDir.normalize();\n            OSG_INFO<<\"   new LightPos =\"<<lightPos<<std::endl;\n            OSG_INFO<<\"   new LightDir =\"<<lightDir<<std::endl;\n        }\n        lightPos3.set(lightPos.x()/lightPos.w(), lightPos.y()/lightPos.w(), lightPos.z()/lightPos.w());\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////\n//\n// ShadowData\n//\nMWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData* vdd):\n    _viewDependentData(vdd),\n    _textureUnit(0)\n{\n\n    const ShadowSettings* settings = vdd->getViewDependentShadowMap()->getShadowedScene()->getShadowSettings();\n\n    bool debug = settings->getDebugDraw();\n\n    // set up texgen\n    _texgen = new osg::TexGen;\n\n    // set up the texture\n    _texture = new osg::Texture2D;\n\n    osg::Vec2s textureSize = debug ? osg::Vec2s(512,512) : settings->getTextureSize();\n    _texture->setTextureSize(textureSize.x(), textureSize.y());\n\n    if (debug)\n    {\n        _texture->setInternalFormat(GL_RGB);\n    }\n    else\n    {\n        _texture->setInternalFormat(GL_DEPTH_COMPONENT);\n        _texture->setShadowComparison(true);\n        _texture->setShadowTextureMode(osg::Texture2D::LUMINANCE);\n    }\n\n    _texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);\n    _texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);\n\n    // the shadow comparison should fail if object is outside the texture\n    _texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);\n    _texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);\n    _texture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));\n    //_texture->setBorderColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));\n\n    // set up the camera\n    _camera = new osg::Camera;\n    _camera->setName(\"ShadowCamera\");\n    _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);\n#ifndef __APPLE__ // workaround shadow issue on macOS, https://gitlab.com/OpenMW/openmw/-/issues/6057\n    _camera->setImplicitBufferAttachmentMask(0, 0);\n#endif\n    //_camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));\n    _camera->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));\n\n    //_camera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);\n    //_camera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_PRIMITIVES);\n\n    // Now we are using Depth Clamping, we want to not cull things on the wrong side of the near plane.\n    // When the near and far planes are computed, OSG always culls anything on the wrong side of the near plane, even if it's told not to.\n    // Even if that weren't an issue, the near plane can't go past any shadow receivers or the depth-clamped fragments which ended up on the near plane can't cast shadows on those receivers.\n    // Unfortunately, this change will make shadows have less depth precision when there are no casters outside the view frustum.\n    // TODO: Find a better solution. E.g. detect when there are no casters outside the view frustum, write a new cull visitor that does all the wacky things we'd need it to.\n    _camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);\n\n    // switch off small feature culling as this can cull out geometry that will still be large enough once perspective correction takes effect.\n    _camera->setCullingMode(_camera->getCullingMode() & ~osg::CullSettings::SMALL_FEATURE_CULLING);\n\n    // set viewport\n    _camera->setViewport(0,0,textureSize.x(),textureSize.y());\n\n\n    if (debug)\n    {\n        // clear just the depth buffer\n        _camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);\n\n        // render after the main camera\n        _camera->setRenderOrder(osg::Camera::POST_RENDER);\n\n        // attach the texture and use it as the color buffer.\n        //_camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get());\n        _camera->attach(osg::Camera::COLOR_BUFFER, _texture.get());\n    }\n    else\n    {\n        // clear the depth and colour bufferson each clear.\n        _camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);\n\n        // set the camera to render before the main camera.\n        _camera->setRenderOrder(osg::Camera::PRE_RENDER);\n\n        // tell the camera to use OpenGL frame buffer object where supported.\n        _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);\n\n        // attach the texture and use it as the color buffer.\n        _camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get());\n        //_camera->attach(osg::Camera::COLOR_BUFFER, _texture.get());\n    }\n}\n\nvoid MWShadowTechnique::ShadowData::releaseGLObjects(osg::State* state) const\n{\n    OSG_INFO<<\"MWShadowTechnique::ShadowData::releaseGLObjects\"<<std::endl;\n    _texture->releaseGLObjects(state);\n    _camera->releaseGLObjects(state);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Frustum\n//\nMWShadowTechnique::Frustum::Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar):\n    corners(8),\n    faces(6),\n    edges(12)\n{\n    projectionMatrix = *(cv->getProjectionMatrix());\n    modelViewMatrix = *(cv->getModelViewMatrix());\n\n    OSG_INFO<<\"Projection matrix \"<<projectionMatrix<<std::endl;\n\n    if (cv->getComputeNearFarMode()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)\n    {\n        osg::Matrix::value_type zNear = osg::maximum<osg::Matrix::value_type>(cv->getCalculatedNearPlane(),minZNear);\n        osg::Matrix::value_type zFar = osg::minimum<osg::Matrix::value_type>(cv->getCalculatedFarPlane(),maxZFar);\n\n        cv->clampProjectionMatrix(projectionMatrix, zNear, zFar);\n\n        OSG_INFO<<\"zNear = \"<<zNear<<\", zFar = \"<<zFar<<std::endl;\n        OSG_INFO<<\"Projection matrix after clamping \"<<projectionMatrix<<std::endl;\n    }\n\n    corners[0].set(-1.0,-1.0,-1.0);\n    corners[1].set(1.0,-1.0,-1.0);\n    corners[2].set(1.0,-1.0,1.0);\n    corners[3].set(-1.0,-1.0,1.0);\n    corners[4].set(-1.0,1.0,-1.0);\n    corners[5].set(1.0,1.0,-1.0);\n    corners[6].set(1.0,1.0,1.0);\n    corners[7].set(-1.0,1.0,1.0);\n\n    osg::Matrixd clipToWorld;\n    clipToWorld.invert(modelViewMatrix * projectionMatrix);\n\n    // transform frustum corners from clipspace to world coords, and compute center\n    for(Vertices::iterator itr = corners.begin();\n        itr != corners.end();\n        ++itr)\n    {\n        *itr = (*itr) * clipToWorld;\n\n        OSG_INFO<<\"   corner \"<<*itr<<std::endl;\n    }\n\n    // compute eye point\n    eye = osg::Vec3d(0.0,0.0,0.0) * osg::Matrix::inverse(modelViewMatrix);\n\n    // compute center and the frustumCenterLine\n    centerNearPlane = (corners[0]+corners[1]+corners[5]+corners[4])*0.25;\n    centerFarPlane = (corners[3]+corners[2]+corners[6]+corners[7])*0.25;\n    center = (centerNearPlane+centerFarPlane)*0.5;\n    frustumCenterLine = centerFarPlane-centerNearPlane;\n    frustumCenterLine.normalize();\n\n    OSG_INFO<<\"   center \"<<center<<std::endl;\n\n    faces[0].push_back(0);\n    faces[0].push_back(3);\n    faces[0].push_back(7);\n    faces[0].push_back(4);\n\n    faces[1].push_back(1);\n    faces[1].push_back(5);\n    faces[1].push_back(6);\n    faces[1].push_back(2);\n\n    faces[2].push_back(0);\n    faces[2].push_back(1);\n    faces[2].push_back(2);\n    faces[2].push_back(3);\n\n    faces[3].push_back(4);\n    faces[3].push_back(7);\n    faces[3].push_back(6);\n    faces[3].push_back(5);\n\n    faces[4].push_back(0);\n    faces[4].push_back(4);\n    faces[4].push_back(5);\n    faces[4].push_back(1);\n\n    faces[5].push_back(2);\n    faces[5].push_back(6);\n    faces[5].push_back(7);\n    faces[5].push_back(3);\n\n    edges[0].push_back(0); edges[0].push_back(1); // corner points on edge\n    edges[0].push_back(2); edges[0].push_back(4); // faces on edge\n\n    edges[1].push_back(1); edges[1].push_back(2); // corner points on edge\n    edges[1].push_back(2); edges[1].push_back(1); // faces on edge\n\n    edges[2].push_back(2); edges[2].push_back(3); // corner points on edge\n    edges[2].push_back(2); edges[2].push_back(5); // faces on edge\n\n    edges[3].push_back(3); edges[3].push_back(0); // corner points on edge\n    edges[3].push_back(2); edges[3].push_back(0); // faces on edge\n\n\n    edges[4].push_back(0); edges[4].push_back(4); // corner points on edge\n    edges[4].push_back(0); edges[4].push_back(4); // faces on edge\n\n    edges[5].push_back(1); edges[5].push_back(5); // corner points on edge\n    edges[5].push_back(4); edges[5].push_back(1); // faces on edge\n\n    edges[6].push_back(2); edges[6].push_back(6); // corner points on edge\n    edges[6].push_back(1); edges[6].push_back(5); // faces on edge\n\n    edges[7].push_back(3); edges[7].push_back(7); // corner points on edge\n    edges[7].push_back(5); edges[7].push_back(0); // faces on edge\n\n\n    edges[8].push_back(4); edges[8].push_back(5); // corner points on edge\n    edges[8].push_back(3); edges[8].push_back(4); // faces on edge\n\n    edges[9].push_back(5); edges[9].push_back(6); // corner points on edge\n    edges[9].push_back(3); edges[9].push_back(1); // faces on edge\n\n    edges[10].push_back(6);edges[10].push_back(7); // corner points on edge\n    edges[10].push_back(3);edges[10].push_back(5); // faces on edge\n\n    edges[11].push_back(7); edges[11].push_back(4); // corner points on edge\n    edges[11].push_back(3); edges[11].push_back(0); // faces on edge\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////\n//\n// ViewDependentData\n//\nMWShadowTechnique::ViewDependentData::ViewDependentData(MWShadowTechnique* vdsm):\n    _viewDependentShadowMap(vdsm)\n{\n    OSG_INFO<<\"ViewDependentData::ViewDependentData()\"<<this<<std::endl;\n    for (auto& perFrameStateset : _stateset)\n        perFrameStateset = new osg::StateSet;\n}\n\nvoid MWShadowTechnique::ViewDependentData::releaseGLObjects(osg::State* state) const\n{\n    for(ShadowDataList::const_iterator itr = _shadowDataList.begin();\n        itr != _shadowDataList.end();\n        ++itr)\n    {\n        (*itr)->releaseGLObjects(state);\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////\n//\n// MWShadowTechnique\n//\nMWShadowTechnique::MWShadowTechnique():\n    ShadowTechnique(),\n    _enableShadows(false),\n    _debugHud(nullptr),\n    _castingPrograms{ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }\n{\n    _shadowRecievingPlaceholderStateSet = new osg::StateSet;\n    mSetDummyStateWhenDisabled = false;\n}\n\nMWShadowTechnique::MWShadowTechnique(const MWShadowTechnique& vdsm, const osg::CopyOp& copyop):\n    ShadowTechnique(vdsm,copyop)\n    , _castingPrograms(vdsm._castingPrograms)\n{\n    _shadowRecievingPlaceholderStateSet = new osg::StateSet;\n    _enableShadows = vdsm._enableShadows;\n    mSetDummyStateWhenDisabled = vdsm.mSetDummyStateWhenDisabled;\n}\n\nMWShadowTechnique::~MWShadowTechnique()\n{\n}\n\n\nvoid MWShadowTechnique::init()\n{\n    if (!_shadowedScene) return;\n\n    OSG_INFO<<\"MWShadowTechnique::init()\"<<std::endl;\n\n    createShaders();\n\n    _dirty = false;\n}\n\nvoid MWShadowTechnique::cleanSceneGraph()\n{\n    OSG_INFO<<\"MWShadowTechnique::cleanSceneGraph()\"<<std::endl;\n}\n\nvoid MWShadowTechnique::enableShadows()\n{\n    _enableShadows = true;\n}\n\nvoid MWShadowTechnique::disableShadows(bool setDummyState)\n{\n    _enableShadows = false;\n    mSetDummyStateWhenDisabled = setDummyState;\n}\n\nvoid SceneUtil::MWShadowTechnique::enableDebugHUD()\n{\n    _debugHud = new DebugHUD(getShadowedScene()->getShadowSettings()->getNumShadowMapsPerLight());\n}\n\nvoid SceneUtil::MWShadowTechnique::disableDebugHUD()\n{\n    _debugHud = nullptr;\n}\n\nvoid SceneUtil::MWShadowTechnique::setSplitPointUniformLogarithmicRatio(double ratio)\n{\n    _splitPointUniformLogRatio = ratio;\n}\n\nvoid SceneUtil::MWShadowTechnique::setSplitPointDeltaBias(double bias)\n{\n    _splitPointDeltaBias = bias;\n}\n\nvoid SceneUtil::MWShadowTechnique::setPolygonOffset(float factor, float units)\n{\n    _polygonOffsetFactor = factor;\n    _polygonOffsetUnits = units;\n\n    if (_polygonOffset)\n    {\n        _polygonOffset->setFactor(factor);\n        _polygonOffset->setUnits(units);\n    }\n}\n\nvoid SceneUtil::MWShadowTechnique::setShadowFadeStart(float shadowFadeStart)\n{\n    _shadowFadeStart = shadowFadeStart;\n}\n\nvoid SceneUtil::MWShadowTechnique::enableFrontFaceCulling()\n{\n    _useFrontFaceCulling = true;\n\n    if (_shadowCastingStateSet)\n    {\n        _shadowCastingStateSet->setAttribute(new osg::CullFace(osg::CullFace::FRONT), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);\n        _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n    }\n}\n\nvoid SceneUtil::MWShadowTechnique::disableFrontFaceCulling()\n{\n    _useFrontFaceCulling = false;\n\n    if (_shadowCastingStateSet)\n    {\n        _shadowCastingStateSet->removeAttribute(osg::StateAttribute::CULLFACE);\n        _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);\n    }\n}\n\nvoid SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & shaderManager)\n{\n    // This can't be part of the constructor as OSG mandates that there be a trivial constructor available\n\n    osg::ref_ptr<osg::Shader> castingVertexShader = shaderManager.getShader(\"shadowcasting_vertex.glsl\", {}, osg::Shader::VERTEX);\n    osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);\n    std::string useGPUShader4 = exts && exts->isGpuShader4Supported ? \"1\" : \"0\";\n    for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc)\n    {\n        auto& program = _castingPrograms[alphaFunc - GL_NEVER];\n        program = new osg::Program();\n        program->addShader(castingVertexShader);\n        program->addShader(shaderManager.getShader(\"shadowcasting_fragment.glsl\", { {\"alphaFunc\", std::to_string(alphaFunc)},\n                                                                                    {\"alphaToCoverage\", \"0\"},\n                                                                                    {\"adjustCoverage\", \"1\"},\n                                                                                    {\"useGPUShader4\", useGPUShader4}\n                                                                                  }, osg::Shader::FRAGMENT));\n    }\n}\n\nMWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/)\n{\n    return new ViewDependentData(this);\n}\n\nMWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(osgUtil::CullVisitor* cv)\n{\n    std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex);\n    ViewDependentDataMap::iterator itr = _viewDependentDataMap.find(cv);\n    if (itr!=_viewDependentDataMap.end()) return itr->second.get();\n\n    osg::ref_ptr<ViewDependentData> vdd = createViewDependentData(cv);\n    _viewDependentDataMap[cv] = vdd;\n    return vdd.release();\n}\n\nvoid MWShadowTechnique::update(osg::NodeVisitor& nv)\n{\n    OSG_INFO<<\"MWShadowTechnique::update(osg::NodeVisitor& \"<<&nv<<\")\"<<std::endl;\n    _shadowedScene->osg::Group::traverse(nv);\n}\n\nvoid MWShadowTechnique::cull(osgUtil::CullVisitor& cv)\n{\n    if (!_enableShadows)\n    {\n        if (mSetDummyStateWhenDisabled)\n        {\n            osg::ref_ptr<osg::StateSet> dummyState = new osg::StateSet();\n\n            ShadowSettings* settings = getShadowedScene()->getShadowSettings();\n            int baseUnit = settings->getBaseShadowTextureUnit();\n            int endUnit = baseUnit + settings->getNumShadowMapsPerLight();\n            for (int i = baseUnit; i < endUnit; ++i)\n            {\n                dummyState->setTextureAttributeAndModes(i, _fallbackShadowMapTexture, osg::StateAttribute::ON);\n                dummyState->addUniform(new osg::Uniform((\"shadowTexture\" + std::to_string(i - baseUnit)).c_str(), i));\n                dummyState->addUniform(new osg::Uniform((\"shadowTextureUnit\" + std::to_string(i - baseUnit)).c_str(), i));\n            }\n\n            cv.pushStateSet(dummyState);\n        }\n\n        _shadowedScene->osg::Group::traverse(cv);\n\n        if (mSetDummyStateWhenDisabled)\n            cv.popStateSet();\n\n        return;\n    }\n\n    OSG_INFO<<std::endl<<std::endl<<\"MWShadowTechnique::cull(osg::CullVisitor&\"<<&cv<<\")\"<<std::endl;\n\n    if (!_shadowCastingStateSet)\n    {\n        OSG_INFO<<\"Warning, init() has not yet been called so ShadowCastingStateSet has not been setup yet, unable to create shadows.\"<<std::endl;\n        _shadowedScene->osg::Group::traverse(cv);\n        return;\n    }\n\n    ViewDependentData* vdd = getViewDependentData(&cv);\n\n    if (!vdd)\n    {\n        OSG_INFO<<\"Warning, now ViewDependentData created, unable to create shadows.\"<<std::endl;\n        _shadowedScene->osg::Group::traverse(cv);\n        return;\n    }\n\n    ShadowSettings* settings = getShadowedScene()->getShadowSettings();\n\n    OSG_INFO<<\"cv->getProjectionMatrix()=\"<<*cv.getProjectionMatrix()<<std::endl;\n\n    osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode();\n\n    osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix();\n\n    // check whether this main views projection is perspective or orthographic\n    bool orthographicViewFrustum = viewProjectionMatrix(0,3)==0.0 &&\n                                   viewProjectionMatrix(1,3)==0.0 &&\n                                   viewProjectionMatrix(2,3)==0.0;\n\n    double minZNear = 0.0;\n    double maxZFar = dbl_max;\n\n    if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)\n    {\n        double left, right, top, bottom;\n        if (orthographicViewFrustum)\n        {\n            viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar);\n        }\n        else\n        {\n            viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar);\n        }\n        OSG_INFO<<\"minZNear=\"<<minZNear<<\", maxZFar=\"<<maxZFar<<std::endl;\n    }\n\n    // set the compute near/far mode to the highest quality setting to ensure we push the near plan out as far as possible\n    if (settings->getComputeNearFarModeOverride()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)\n    {\n        cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride());\n    }\n\n    // 1. Traverse main scene graph\n    auto* shadowReceiverStateSet = vdd->getStateSet(cv.getTraversalNumber());\n    shadowReceiverStateSet->clear();\n    cv.pushStateSet(shadowReceiverStateSet);\n\n    cullShadowReceivingScene(&cv);\n\n    cv.popStateSet();\n\n    if (cv.getComputeNearFarMode()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)\n    {\n        OSG_INFO<<\"Just done main subgraph traversak\"<<std::endl;\n        // make sure that the near plane is computed correctly so that any projection matrix computations\n        // are all done correctly.\n        cv.computeNearPlane();\n    }\n\n    // clamp the minZNear and maxZFar to those provided by ShadowSettings\n    maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar);\n    if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();\n\n    //OSG_NOTICE<<\"maxZFar \"<<maxZFar<<std::endl;\n\n    // Workaround for absurdly huge viewing distances where OSG would otherwise push the near plane out.\n    cv.setNearFarRatio(minZNear / maxZFar);\n\n    Frustum frustum(&cv, minZNear, maxZFar);\n    if (_debugHud)\n    {\n        osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array();\n        for (osg::Vec3d &vertex : frustum.corners)\n            vertexArray->push_back((osg::Vec3)vertex);\n        _debugHud->setFrustumVertices(vertexArray, cv.getTraversalNumber());\n    }\n\n    double reducedNear, reducedFar;\n    if (cv.getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)\n    {\n        reducedNear = osg::maximum<double>(cv.getCalculatedNearPlane(), minZNear);\n        reducedFar = osg::minimum<double>(cv.getCalculatedFarPlane(), maxZFar);\n    }\n    else\n    {\n        reducedNear = minZNear;\n        reducedFar = maxZFar;\n    }\n\n    // return compute near far mode back to it's original settings\n    cv.setComputeNearFarMode(cachedNearFarMode);\n\n    OSG_INFO<<\"frustum.eye=\"<<frustum.eye<<\", frustum.centerNearPlane, \"<<frustum.centerNearPlane<<\" distance = \"<<(frustum.eye-frustum.centerNearPlane).length()<<std::endl;\n\n\n    // 2. select active light sources\n    //    create a list of light sources + their matrices to place them\n    selectActiveLights(&cv, vdd);\n\n\n    unsigned int pos_x = 0;\n    unsigned int textureUnit = settings->getBaseShadowTextureUnit();\n    unsigned int numValidShadows = 0;\n\n    ShadowDataList& sdl = vdd->getShadowDataList();\n    ShadowDataList previous_sdl;\n    previous_sdl.swap(sdl);\n\n    unsigned int numShadowMapsPerLight = settings->getNumShadowMapsPerLight();\n\n    LightDataList& pll = vdd->getLightDataList();\n    for(LightDataList::iterator itr = pll.begin();\n        itr != pll.end();\n        ++itr)\n    {\n        // 3. create per light/per shadow map division of lightspace/frustum\n        //    create a list of light/shadow map data structures\n\n        LightData& pl = **itr;\n\n        // 3.1 compute light space polytope\n        //\n        osg::Polytope polytope = computeLightViewFrustumPolytope(frustum, pl);\n\n        // if polytope is empty then no rendering.\n        if (polytope.empty())\n        {\n            OSG_NOTICE<<\"Polytope empty no shadow to render\"<<std::endl;\n            continue;\n        }\n\n        // 3.2 compute RTT camera view+projection matrix settings\n        //\n        osg::Matrixd projectionMatrix;\n        osg::Matrixd viewMatrix;\n        if (!computeShadowCameraSettings(frustum, pl, projectionMatrix, viewMatrix))\n        {\n            OSG_NOTICE<<\"No valid Camera settings, no shadow to render\"<<std::endl;\n            continue;\n        }\n\n        // if we are using multiple shadow maps and CastShadowTraversalMask is being used\n        // traverse the scene to compute the extents of the objects\n        if (/*numShadowMapsPerLight>1 &&*/ _shadowedScene->getCastsShadowTraversalMask()!=0xffffffff)\n        {\n            // osg::ElapsedTime timer;\n\n            osg::ref_ptr<osg::Viewport> viewport = new osg::Viewport(0,0,2048,2048);\n            ComputeLightSpaceBounds clsb(viewport.get(), projectionMatrix, viewMatrix);\n            clsb.setTraversalMask(_shadowedScene->getCastsShadowTraversalMask());\n\n            osg::Matrixd invertModelView;\n            invertModelView.invert(viewMatrix);\n            osg::Polytope local_polytope(polytope);\n            local_polytope.transformProvidingInverse(invertModelView);\n\n            osg::CullingSet& cs = clsb.getProjectionCullingStack().back();\n            cs.setFrustum(local_polytope);\n            clsb.pushCullingSet();\n\n            _shadowedScene->accept(clsb);\n\n            // OSG_NOTICE<<\"Extents of LightSpace \"<<clsb._bb.xMin()<<\", \"<<clsb._bb.xMax()<<\", \"<<clsb._bb.yMin()<<\", \"<<clsb._bb.yMax()<<\", \"<<clsb._bb.zMin()<<\", \"<<clsb._bb.zMax()<<std::endl;\n            // OSG_NOTICE<<\"  time \"<<timer.elapsedTime_m()<<\"ms, mask = \"<<std::hex<<_shadowedScene->getCastsShadowTraversalMask()<<std::endl;\n\n            if (clsb._bb.xMin()>-1.0f || clsb._bb.xMax()<1.0f || clsb._bb.yMin()>-1.0f || clsb._bb.yMax()<1.0f)\n            {\n                // OSG_NOTICE<<\"Need to clamp projection matrix\"<<std::endl;\n\n#if 1\n                double xMid = (clsb._bb.xMin()+clsb._bb.xMax())*0.5f;\n                double xRange = clsb._bb.xMax()-clsb._bb.xMin();\n#else\n                double xMid = 0.0;\n                double xRange = 2.0;\n#endif\n                double yMid = (clsb._bb.yMin()+clsb._bb.yMax())*0.5f;\n                double yRange = (clsb._bb.yMax()-clsb._bb.yMin());\n\n                osg::Matrixd cornerConverter = osg::Matrixd::inverse(projectionMatrix) * osg::Matrixd::inverse(viewMatrix) * *cv.getModelViewMatrix();\n                double minZ = dbl_max;\n                double maxZ = -dbl_max;\n                clsb._bb._max[2] = 1.0;\n                for (unsigned int i = 0; i < 8; i++)\n                {\n                    osg::Vec3 corner = clsb._bb.corner(i);\n                    corner = corner * cornerConverter;\n\n                    maxZ = osg::maximum<double>(maxZ, -corner.z());\n                    minZ = osg::minimum<double>(minZ, -corner.z());\n                }\n                reducedNear = osg::maximum<double>(reducedNear, minZ);\n                reducedFar = osg::minimum<double>(reducedFar, maxZ);\n\n                // OSG_NOTICE<<\"  xMid=\"<<xMid<<\", yMid=\"<<yMid<<\", xRange=\"<<xRange<<\", yRange=\"<<yRange<<std::endl;\n\n                projectionMatrix =\n                    projectionMatrix *\n                    osg::Matrixd::translate(osg::Vec3d(-xMid,-yMid,0.0)) *\n                    osg::Matrixd::scale(osg::Vec3d(2.0/xRange, 2.0/yRange,1.0));\n\n            }\n\n        }\n\n#if 0\n        double splitPoint = 0.0;\n\n        if (numShadowMapsPerLight>1)\n        {\n            osg::Vec3d eye_v = frustum.eye * viewMatrix;\n            osg::Vec3d center_v = frustum.center * viewMatrix;\n            osg::Vec3d viewdir_v = center_v-eye_v; viewdir_v.normalize();\n            osg::Vec3d lightdir(0.0,0.0,-1.0);\n\n            double dotProduct_v = lightdir * viewdir_v;\n            double angle = acosf(dotProduct_v);\n\n            osg::Vec3d eye_ls = eye_v * projectionMatrix;\n\n            OSG_INFO<<\"Angle between view vector and eye \"<<osg::RadiansToDegrees(angle)<<std::endl;\n            OSG_INFO<<\"eye_ls=\"<<eye_ls<<std::endl;\n\n            if (eye_ls.y()>=-1.0 && eye_ls.y()<=1.0)\n            {\n                OSG_INFO<<\"Eye point inside light space clip region   \"<<std::endl;\n                splitPoint = 0.0;\n            }\n            else\n            {\n                double n = -1.0-eye_ls.y();\n                double f = 1.0-eye_ls.y();\n                double sqrt_nf = sqrt(n*f);\n                double mid = eye_ls.y()+sqrt_nf;\n                double ratioOfMidToUseForSplit = 0.8;\n                splitPoint = mid * ratioOfMidToUseForSplit;\n\n                OSG_INFO<<\"  n=\"<<n<<\", f=\"<<f<<\", sqrt_nf=\"<<sqrt_nf<<\" mid=\"<<mid<<std::endl;\n            }\n        }\n#endif\n\n        // 4. For each light/shadow map\n        for (unsigned int sm_i=0; sm_i<numShadowMapsPerLight; ++sm_i)\n        {\n            osg::ref_ptr<ShadowData> sd;\n\n            if (previous_sdl.empty())\n            {\n                OSG_INFO<<\"Create new ShadowData\"<<std::endl;\n                sd = new ShadowData(vdd);\n            }\n            else\n            {\n                OSG_INFO<<\"Taking ShadowData from from of previous_sdl\"<<std::endl;\n                sd = previous_sdl.front();\n                previous_sdl.erase(previous_sdl.begin());\n            }\n\n            osg::ref_ptr<osg::Camera> camera = sd->_camera;\n\n            camera->setProjectionMatrix(projectionMatrix);\n            camera->setViewMatrix(viewMatrix);\n\n            if (settings->getDebugDraw())\n            {\n                camera->getViewport()->x() = pos_x;\n                pos_x += static_cast<unsigned int>(camera->getViewport()->width()) + 40;\n            }\n\n            // transform polytope in model coords into light spaces eye coords.\n            osg::Matrixd invertModelView;\n            invertModelView.invert(camera->getViewMatrix());\n\n            osg::Polytope local_polytope(polytope);\n            local_polytope.transformProvidingInverse(invertModelView);\n\n            double cascaseNear = reducedNear;\n            double cascadeFar = reducedFar;\n            if (numShadowMapsPerLight>1)\n            {\n                // compute the start and end range in non-dimensional coords\n#if 0\n                double r_start = (sm_i==0) ? -1.0 : (double(sm_i)/double(numShadowMapsPerLight)*2.0-1.0);\n                double r_end = (sm_i+1==numShadowMapsPerLight) ? 1.0 : (double(sm_i+1)/double(numShadowMapsPerLight)*2.0-1.0);\n#elif 0\n\n                // hardwired for 2 splits\n                double r_start = (sm_i==0) ? -1.0 : splitPoint;\n                double r_end = (sm_i+1==numShadowMapsPerLight) ? 1.0 : splitPoint;\n#else\n                double r_start, r_end;\n\n                // split system based on the original Parallel Split Shadow Maps paper.\n                double n = reducedNear;\n                double f = reducedFar;\n                double i = double(sm_i);\n                double m = double(numShadowMapsPerLight);\n                if (sm_i == 0)\n                    r_start = -1.0;\n                else\n                {\n                    // compute the split point in main camera view\n                    double ciLog = n * pow(f / n, i / m);\n                    double ciUniform = n + (f - n) * i / m;\n                    double ci = _splitPointUniformLogRatio * ciLog + (1.0 - _splitPointUniformLogRatio) * ciUniform + _splitPointDeltaBias;\n                    cascaseNear = ci;\n\n                    // work out where this is in light space\n                    osg::Vec3d worldSpacePos = frustum.eye + frustum.frustumCenterLine * ci;\n                    osg::Vec3d lightSpacePos = worldSpacePos * viewMatrix * projectionMatrix;\n                    r_start = lightSpacePos.y();\n                }\n\n                if (sm_i + 1 == numShadowMapsPerLight)\n                    r_end = 1.0;\n                else\n                {\n                    // compute the split point in main camera view\n                    double ciLog = n * pow(f / n, (i + 1) / m);\n                    double ciUniform = n + (f - n) * (i + 1) / m;\n                    double ci = _splitPointUniformLogRatio * ciLog + (1.0 - _splitPointUniformLogRatio) * ciUniform + _splitPointDeltaBias;\n                    cascadeFar = ci;\n                    \n                    // work out where this is in light space\n                    osg::Vec3d worldSpacePos = frustum.eye + frustum.frustumCenterLine * ci;\n                    osg::Vec3d lightSpacePos = worldSpacePos * viewMatrix * projectionMatrix;\n                    r_end = lightSpacePos.y();\n                }\n#endif\n                // for all by the last shadowmap shift the r_end so that it overlaps slightly with the next shadowmap\n                // to prevent a seam showing through between the shadowmaps\n                if (sm_i+1<numShadowMapsPerLight) r_end+=0.01;\n\n\n                // We can't add these clipping planes with cascaded shadow maps as they wouldn't be parallel to the light direction.\n\n                if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT && sm_i>0)\n                {\n                    // not the first shadowmap so insert a polytope to clip the scene from before r_start\n\n                    // plane in clip space coords\n                    osg::Plane plane(0.0,1.0,0.0,-r_start);\n\n                    // transform into eye coords\n                    plane.transformProvidingInverse(projectionMatrix);\n                    local_polytope.getPlaneList().push_back(plane);\n\n                    //OSG_NOTICE<<\"Adding r_start plane \"<<plane<<std::endl;\n\n                }\n\n                if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT && sm_i+1<numShadowMapsPerLight)\n                {\n                    // not the last shadowmap so insert a polytope to clip the scene from beyond r_end\n\n                    // plane in clip space coords\n                    osg::Plane plane(0.0,-1.0,0.0,r_end);\n\n                    // transform into eye coords\n                    plane.transformProvidingInverse(projectionMatrix);\n                    local_polytope.getPlaneList().push_back(plane);\n\n                    //OSG_NOTICE<<\"Adding r_end plane \"<<plane<<std::endl;\n                }\n\n                local_polytope.setupMask();\n\n                if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT)\n                {\n                    // OSG_NOTICE<<\"Need to adjust RTT camera projection and view matrix here, r_start=\"<<r_start<<\", r_end=\"<<r_end<<std::endl;\n                    // OSG_NOTICE<<\"  textureUnit = \"<<textureUnit<<std::endl;\n\n                    double mid_r = (r_start + r_end)*0.5;\n                    double range_r = (r_end - r_start);\n\n                    // OSG_NOTICE<<\"  mid_r = \"<<mid_r<<\", range_r = \"<<range_r<<std::endl;\n\n                    camera->setProjectionMatrix(\n                        camera->getProjectionMatrix() *\n                        osg::Matrixd::translate(osg::Vec3d(0.0,-mid_r,0.0)) *\n                        osg::Matrixd::scale(osg::Vec3d(1.0,2.0/range_r,1.0)));\n                    \n                }\n            }\n\n            std::vector<osg::Plane> extraPlanes;\n            if (settings->getMultipleShadowMapHint() == ShadowSettings::CASCADED)\n            {\n                cropShadowCameraToMainFrustum(frustum, camera, cascaseNear, cascadeFar, extraPlanes);\n                for (const auto& plane : extraPlanes)\n                    local_polytope.getPlaneList().push_back(plane);\n                local_polytope.setupMask();\n            }\n            else\n                cropShadowCameraToMainFrustum(frustum, camera, reducedNear, reducedFar, extraPlanes);\n\n            osg::ref_ptr<VDSMCameraCullCallback> vdsmCallback = new VDSMCameraCullCallback(this, local_polytope);\n            camera->setCullCallback(vdsmCallback.get());\n\n            // 4.3 traverse RTT camera\n            //\n\n            cv.pushStateSet(_shadowCastingStateSet.get());\n\n            cullShadowCastingScene(&cv, camera.get());\n\n            cv.popStateSet();\n\n            if (!orthographicViewFrustum && settings->getShadowMapProjectionHint()==ShadowSettings::PERSPECTIVE_SHADOW_MAP)\n            {\n                {\n                    osg::Matrix validRegionMatrix = cv.getCurrentCamera()->getInverseViewMatrix() *  camera->getViewMatrix() * camera->getProjectionMatrix();\n\n                    std::string validRegionUniformName = \"validRegionMatrix\" + std::to_string(sm_i);\n                    osg::ref_ptr<osg::Uniform> validRegionUniform;\n\n                    for (auto uniform : _uniforms[cv.getTraversalNumber() % 2])\n                    {\n                        if (uniform->getName() == validRegionUniformName)\n                            validRegionUniform = uniform;\n                    }\n\n                    if (!validRegionUniform)\n                    {\n                        validRegionUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, validRegionUniformName);\n                        _uniforms[cv.getTraversalNumber() % 2].push_back(validRegionUniform);\n                    }\n\n                    validRegionUniform->set(validRegionMatrix);\n                }\n\n                if (settings->getMultipleShadowMapHint() == ShadowSettings::CASCADED)\n                    adjustPerspectiveShadowMapCameraSettings(vdsmCallback->getRenderStage(), frustum, pl, camera.get(), cascaseNear, cascadeFar);\n                else\n                    adjustPerspectiveShadowMapCameraSettings(vdsmCallback->getRenderStage(), frustum, pl, camera.get(), reducedNear, reducedFar);\n                if (vdsmCallback->getProjectionMatrix())\n                {\n                    vdsmCallback->getProjectionMatrix()->set(camera->getProjectionMatrix());\n                }\n            }\n\n            // 4.4 compute main scene graph TexGen + uniform settings + setup state\n            //\n            assignTexGenSettings(&cv, camera.get(), textureUnit, sd->_texgen.get());\n\n            // mark the light as one that has active shadows and requires shaders\n            pl.textureUnits.push_back(textureUnit);\n\n            // pass on shadow data to ShadowDataList\n            sd->_textureUnit = textureUnit;\n\n            if (textureUnit >= 8)\n            {\n                OSG_NOTICE<<\"Shadow texture unit is invalid for texgen, will not be used.\"<<std::endl;\n            }\n            else\n            {\n                sdl.push_back(sd);\n            }\n\n            // increment counters.\n            ++textureUnit;\n            ++numValidShadows ;\n\n            if (_debugHud)\n                _debugHud->draw(sd->_texture, sm_i, camera->getViewMatrix() * camera->getProjectionMatrix(), cv);\n        }\n    }\n\n    if (numValidShadows>0)\n    {\n        prepareStateSetForRenderingShadow(*vdd, cv.getTraversalNumber());\n    }\n\n    // OSG_NOTICE<<\"End of shadow setup Projection matrix \"<<*cv.getProjectionMatrix()<<std::endl;\n}\n\nbool MWShadowTechnique::selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const\n{\n    OSG_INFO<<\"selectActiveLights\"<<std::endl;\n\n    LightDataList& pll = vdd->getLightDataList();\n\n    LightDataList previous_ldl;\n    previous_ldl.swap(pll);\n\n    //MR testing giving a specific light\n    osgUtil::RenderStage * rs = cv->getCurrentRenderBin()->getStage();\n\n    OSG_INFO<<\"selectActiveLights osgUtil::RenderStage=\"<<rs<<std::endl;\n\n    osg::Matrixd modelViewMatrix = *(cv->getModelViewMatrix());\n\n    osgUtil::PositionalStateContainer::AttrMatrixList& aml =\n        rs->getPositionalStateContainer()->getAttrMatrixList();\n\n\n    const ShadowSettings* settings = getShadowedScene()->getShadowSettings();\n\n    for(osgUtil::PositionalStateContainer::AttrMatrixList::reverse_iterator itr = aml.rbegin();\n        itr != aml.rend();\n        ++itr)\n    {\n        const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());\n        if (light && light->getLightNum() >= 0)\n        {\n            // is LightNum matched to that defined in settings\n            if (settings && settings->getLightNum()>=0 && light->getLightNum()!=settings->getLightNum()) continue;\n\n            LightDataList::iterator pll_itr = pll.begin();\n            for(; pll_itr != pll.end(); ++pll_itr)\n            {\n                if ((*pll_itr)->light->getLightNum()==light->getLightNum()) break;\n            }\n\n            if (pll_itr==pll.end())\n            {\n                OSG_INFO<<\"Light num \"<<light->getLightNum()<<std::endl;\n                LightData* ld = new LightData(vdd);\n                ld->setLightData(itr->second.get(), light, modelViewMatrix);\n                pll.push_back(ld);\n            }\n            else\n            {\n                OSG_INFO<<\"Light num \"<<light->getLightNum()<<\" already used, ignore light\"<<std::endl;\n            }\n        }\n    }\n\n    return !pll.empty();\n}\n\nvoid MWShadowTechnique::createShaders()\n{\n    OSG_INFO<<\"MWShadowTechnique::createShaders()\"<<std::endl;\n\n    unsigned int _baseTextureUnit = 0;\n\n    _shadowCastingStateSet = new osg::StateSet;\n\n    ShadowSettings* settings = getShadowedScene()->getShadowSettings();\n\n    if (!settings->getDebugDraw())\n    {\n        // note soft (attribute only no mode override) setting. When this works ?\n        // 1. for objects prepared for backface culling\n        //    because they usually also set CullFace and CullMode on in their state\n        //    For them we override CullFace but CullMode remains set by them\n        // 2. For one faced, trees, and similar objects which cannot use\n        //    backface nor front face so they usually use CullMode off set here.\n        //    In this case we will draw them in their entirety.\n\n        if (_useFrontFaceCulling)\n        {\n            _shadowCastingStateSet->setAttribute(new osg::CullFace(osg::CullFace::FRONT), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);\n\n            // make sure GL_CULL_FACE is off by default\n            // we assume that if object has cull face attribute set to back\n            // it will also set cull face mode ON so no need for override\n            _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n        }\n        else\n            _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);\n    }\n\n    _polygonOffset = new osg::PolygonOffset(_polygonOffsetFactor, _polygonOffsetUnits);\n    _shadowCastingStateSet->setAttribute(_polygonOffset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);\n    _shadowCastingStateSet->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);\n\n\n    osg::ref_ptr<osg::Uniform> baseTextureSampler = new osg::Uniform(\"baseTexture\",(int)_baseTextureUnit);\n    osg::ref_ptr<osg::Uniform> baseTextureUnit = new osg::Uniform(\"baseTextureUnit\",(int)_baseTextureUnit);\n\n    osg::ref_ptr<osg::Uniform> maxDistance = new osg::Uniform(\"maximumShadowMapDistance\", (float)settings->getMaximumShadowMapDistance());\n    osg::ref_ptr<osg::Uniform> fadeStart = new osg::Uniform(\"shadowFadeStart\", (float)_shadowFadeStart);\n\n    for (auto& perFrameUniformList : _uniforms)\n    {\n        perFrameUniformList.clear();\n        perFrameUniformList.push_back(baseTextureSampler);\n        perFrameUniformList.emplace_back(baseTextureUnit.get());\n        perFrameUniformList.push_back(maxDistance);\n        perFrameUniformList.push_back(fadeStart);\n    }\n\n    for(unsigned int sm_i=0; sm_i<settings->getNumShadowMapsPerLight(); ++sm_i)\n    {\n        {\n            std::stringstream sstr;\n            sstr<<\"shadowTexture\"<<sm_i;\n            osg::ref_ptr<osg::Uniform> shadowTextureSampler = new osg::Uniform(sstr.str().c_str(),(int)(settings->getBaseShadowTextureUnit()+sm_i));\n            for (auto& perFrameUniformList : _uniforms)\n                perFrameUniformList.emplace_back(shadowTextureSampler.get());\n        }\n\n        {\n            std::stringstream sstr;\n            sstr<<\"shadowTextureUnit\"<<sm_i;\n            osg::ref_ptr<osg::Uniform> shadowTextureUnit = new osg::Uniform(sstr.str().c_str(),(int)(settings->getBaseShadowTextureUnit()+sm_i));\n            for (auto& perFrameUniformList : _uniforms)\n                perFrameUniformList.emplace_back(shadowTextureUnit.get());\n        }\n    }\n\n    switch(settings->getShaderHint())\n    {\n        case(ShadowSettings::NO_SHADERS):\n        {\n            OSG_INFO<<\"No shaders provided by, user must supply own shaders\"<<std::endl;\n            break;\n        }\n        case(ShadowSettings::PROVIDE_VERTEX_AND_FRAGMENT_SHADER):\n        case(ShadowSettings::PROVIDE_FRAGMENT_SHADER):\n        {\n            _program = new osg::Program;\n\n            //osg::ref_ptr<osg::Shader> fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_noBaseTexture);\n            if (settings->getNumShadowMapsPerLight()==2)\n            {\n                _program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture_twoShadowMaps));\n            }\n            else\n            {\n                _program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture));\n            }\n\n            break;\n        }\n    }\n\n    {\n        osg::ref_ptr<osg::Image> image = new osg::Image;\n        image->allocateImage( 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE );\n        *(osg::Vec4ub*)image->data() = osg::Vec4ub( 0xFF, 0xFF, 0xFF, 0xFF );\n\n        _fallbackBaseTexture = new osg::Texture2D(image.get());\n        _fallbackBaseTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT);\n        _fallbackBaseTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT);\n        _fallbackBaseTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);\n        _fallbackBaseTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);\n\n        _fallbackShadowMapTexture = new osg::Texture2D(image.get());\n        _fallbackShadowMapTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT);\n        _fallbackShadowMapTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT);\n        _fallbackShadowMapTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);\n        _fallbackShadowMapTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);\n        _fallbackShadowMapTexture->setShadowComparison(true);\n        _fallbackShadowMapTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS);\n\n    }\n\n    if (!_castingPrograms[GL_ALWAYS - GL_NEVER])\n        OSG_NOTICE << \"Shadow casting shader has not been set up. Remember to call setupCastingShader(Shader::ShaderManager &)\" << std::endl;\n\n    // Always use the GL_ALWAYS shader as the shadows bin will change it if necessary\n    _shadowCastingStateSet->setAttributeAndModes(_castingPrograms[GL_ALWAYS - GL_NEVER], osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);\n    // The casting program uses a sampler, so to avoid undefined behaviour, we must bind a dummy texture in case no other is supplied\n    _shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);\n    _shadowCastingStateSet->addUniform(new osg::Uniform(\"useDiffuseMapForShadowAlpha\", true));\n    _shadowCastingStateSet->addUniform(new osg::Uniform(\"alphaTestShadows\", false));\n    osg::ref_ptr<osg::Depth> depth = new osg::Depth;\n    depth->setWriteMask(true);\n    _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n    _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON);\n\n    // TODO: compare performance when alpha testing is handled here versus using a discard in the fragment shader\n}\n\nosg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight)\n{\n    OSG_INFO<<\"computeLightViewFrustumPolytope()\"<<std::endl;\n\n    osg::Polytope polytope;\n    polytope.setToUnitFrustum();\n\n    polytope.transformProvidingInverse( frustum.projectionMatrix );\n    polytope.transformProvidingInverse( frustum.modelViewMatrix );\n\n    osg::Polytope lightVolumePolytope;\n\n    if (positionedLight.directionalLight)\n    {\n        osg::Polytope::PlaneList& planes = polytope.getPlaneList();\n        osg::Polytope::ClippingMask selector_mask = 0x1;\n        osg::Polytope::ClippingMask result_mask = 0x0;\n        for(unsigned int i=0; i<planes.size(); ++i, selector_mask <<= 1)\n        {\n            OSG_INFO<<\"      plane \"<<planes[i]<<\"  planes[\"<<i<<\"].dotProductNormal(lightDir)=\"<<planes[i].dotProductNormal(positionedLight.lightDir);\n            if (planes[i].dotProductNormal(positionedLight.lightDir)>=0.0)\n            {\n                OSG_INFO<<\"     Need remove side \"<<i<<std::endl;\n            }\n            else\n            {\n                OSG_INFO<<std::endl;\n                lightVolumePolytope.add(planes[i]);\n                result_mask = result_mask | selector_mask;\n            }\n        }\n\n        OSG_INFO<<\"    planes.size() = \"<<planes.size()<<std::endl;\n        OSG_INFO<<\"    planes.getResultMask() = \"<<polytope.getResultMask()<<std::endl;\n        OSG_INFO<<\"    resultMask = \"<<result_mask<<std::endl;\n        polytope.setResultMask(result_mask);\n    }\n    else\n    {\n        const osg::Polytope::PlaneList& planes = polytope.getPlaneList();\n        osg::Polytope::ClippingMask selector_mask = 0x1;\n        osg::Polytope::ClippingMask result_mask = 0x0;\n        for(unsigned int i=0; i<planes.size(); ++i, selector_mask <<= 1)\n        {\n\n            double d = planes[i].distance(positionedLight.lightPos3);\n            OSG_INFO<<\"      plane \"<<planes[i]<<\"  planes[\"<<i<<\"].distance(lightPos3)=\"<<d;\n            if (d<0.0)\n            {\n                OSG_INFO<<\"     Need remove side \"<<i<<std::endl;\n            }\n            else\n            {\n                OSG_INFO<<std::endl;\n                lightVolumePolytope.add(planes[i]);\n                result_mask = result_mask | selector_mask;\n            }\n        }\n        OSG_INFO<<\"    planes.size() = \"<<planes.size()<<std::endl;\n        OSG_INFO<<\"    planes.getResultMask() = \"<<polytope.getResultMask()<<std::endl;\n        OSG_INFO<<\"    resultMask = \"<<result_mask<<std::endl;\n        polytope.setResultMask(result_mask);\n    }\n\n    OSG_INFO<<\"Which frustum edges are active?\"<<std::endl;\n    for(unsigned int i=0; i<12; ++i)\n    {\n        Frustum::Indices& indices = frustum.edges[i];\n\n        unsigned int corner_a = indices[0];\n        unsigned int corner_b = indices[1];\n        unsigned int face_a = indices[2];\n        unsigned int face_b = indices[3];\n        bool face_a_active = (polytope.getResultMask()&(0x1<<face_a))!=0;\n        bool face_b_active = (polytope.getResultMask()&(0x1<<face_b))!=0;\n        unsigned int numActive = 0;\n        if (face_a_active) ++numActive;\n        if (face_b_active) ++numActive;\n        if (numActive==1)\n        {\n\n            osg::Plane boundaryPlane;\n\n            if (positionedLight.directionalLight)\n            {\n                osg::Vec3d normal = (frustum.corners[corner_b]-frustum.corners[corner_a])^positionedLight.lightDir;\n                normal.normalize();\n                boundaryPlane.set(normal, frustum.corners[corner_a]);\n            }\n            else\n            {\n                boundaryPlane.set(positionedLight.lightPos3, frustum.corners[corner_a], frustum.corners[corner_b]);\n            }\n\n            OSG_INFO<<\"Boundary Edge \"<<i<<\", corner_a=\"<<corner_a<<\", corner_b=\"<<corner_b<<\", face_a_active=\"<<face_a_active<<\", face_b_active=\"<<face_b_active;\n            if (boundaryPlane.distance(frustum.center)<0.0)\n            {\n                boundaryPlane.flip();\n                OSG_INFO<<\", flipped boundary edge \"<<boundaryPlane<<std::endl;\n            }\n            else\n            {\n                OSG_INFO<<\", no need to flip boundary edge \"<<boundaryPlane<<std::endl;\n            }\n            lightVolumePolytope.add(boundaryPlane);\n        }\n        else OSG_INFO<<\"Internal Edge \"<<i<<\", corner_a=\"<<corner_a<<\", corner_b=\"<<corner_b<<\", face_a_active=\"<<face_a_active<<\", face_b_active=\"<<face_b_active<<std::endl;\n    }\n\n\n    const osg::Polytope::PlaneList& planes = lightVolumePolytope.getPlaneList();\n    for(unsigned int i=0; i<planes.size(); ++i)\n    {\n        OSG_INFO<<\"      plane \"<<planes[i]<<\"  \"<<((lightVolumePolytope.getResultMask() & (0x1<<i))?\"on\":\"off\")<<std::endl;\n    }\n\n    return lightVolumePolytope;\n}\n\nbool MWShadowTechnique::computeShadowCameraSettings(Frustum& frustum, LightData& positionedLight, osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix)\n{\n    OSG_INFO<<\"standardShadowMapCameraSettings()\"<<std::endl;\n\n    osg::Vec3d lightSide;\n\n    const ShadowSettings* settings = getShadowedScene()->getShadowSettings();\n\n    double dotProduct_v = positionedLight.lightDir * frustum.frustumCenterLine;\n    double gamma_v = acos(dotProduct_v);\n    if (gamma_v<osg::DegreesToRadians(settings->getPerspectiveShadowMapCutOffAngle()) || gamma_v>osg::DegreesToRadians(180.0-settings->getPerspectiveShadowMapCutOffAngle()))\n    {\n        OSG_INFO<<\"View direction and Light direction below tolerance\"<<std::endl;\n        osg::Vec3d viewSide = osg::Matrixd::transform3x3(frustum.modelViewMatrix, osg::Vec3d(1.0,0.0,0.0));\n        lightSide = positionedLight.lightDir ^ (viewSide ^ positionedLight.lightDir);\n        lightSide.normalize();\n    }\n    else\n    {\n        lightSide = positionedLight.lightDir ^ frustum.frustumCenterLine;\n        lightSide.normalize();\n    }\n\n    osg::Vec3d lightUp = lightSide ^ positionedLight.lightDir;\n\n#if 0\n    OSG_NOTICE<<\"positionedLight.lightDir=\"<<positionedLight.lightDir<<std::endl;\n    OSG_NOTICE<<\"lightSide=\"<<lightSide<<std::endl;\n    OSG_NOTICE<<\"lightUp=\"<<lightUp<<std::endl;\n#endif\n\n\n    if (positionedLight.directionalLight)\n    {\n        double xMin=0.0, xMax=0.0;\n        double yMin=0.0, yMax=0.0;\n        double zMin=0.0, zMax=0.0;\n\n        for(Frustum::Vertices::iterator itr = frustum.corners.begin();\n            itr != frustum.corners.end();\n            ++itr)\n        {\n            osg::Vec3d cornerDelta(*itr - frustum.center);\n            osg::Vec3d cornerInLightCoords(cornerDelta*lightSide,\n                                           cornerDelta*lightUp,\n                                           cornerDelta*positionedLight.lightDir);\n\n            OSG_INFO<<\"    corner =\"<<*itr<<\" in lightcoords \"<<cornerInLightCoords<<std::endl;\n\n            xMin = osg::minimum( xMin, cornerInLightCoords.x());\n            xMax = osg::maximum( xMax, cornerInLightCoords.x());\n            yMin = osg::minimum( yMin, cornerInLightCoords.y());\n            yMax = osg::maximum( yMax, cornerInLightCoords.y());\n            zMin = osg::minimum( zMin, cornerInLightCoords.z());\n            zMax = osg::maximum( zMax, cornerInLightCoords.z());\n        }\n\n        OSG_INFO<<\"before bs xMin=\"<<xMin<<\", xMax=\"<<xMax<<\", yMin=\"<<yMin<<\", yMax=\"<<yMax<<\", zMin=\"<<zMin<<\", zMax=\"<<zMax<<std::endl;\n\n        osg::BoundingSphere bs = _shadowedScene->getBound();\n        osg::Vec3d modelCenterRelativeFrustumCenter(bs.center()-frustum.center);\n        osg::Vec3d modelCenterInLightCoords(modelCenterRelativeFrustumCenter*lightSide,\n                                            modelCenterRelativeFrustumCenter*lightUp,\n                                            modelCenterRelativeFrustumCenter*positionedLight.lightDir);\n\n        OSG_INFO<<\"modelCenterInLight=\"<<modelCenterInLightCoords<<\" radius=\"<<bs.radius()<<std::endl;\n        double radius(bs.radius());\n\n        xMin = osg::maximum(xMin, modelCenterInLightCoords.x()-radius);\n        xMax = osg::minimum(xMax, modelCenterInLightCoords.x()+radius);\n        yMin = osg::maximum(yMin, modelCenterInLightCoords.y()-radius);\n        yMax = osg::minimum(yMax, modelCenterInLightCoords.y()+radius);\n        zMin = modelCenterInLightCoords.z()-radius;\n        zMax = osg::minimum(zMax, modelCenterInLightCoords.z()+radius);\n\n        OSG_INFO<<\"after bs xMin=\"<<xMin<<\", xMax=\"<<xMax<<\", yMin=\"<<yMin<<\", yMax=\"<<yMax<<\", zMin=\"<<zMin<<\", zMax=\"<<zMax<<std::endl;\n\n        if (xMin>=xMax || yMin>=yMax || zMin>=zMax)\n        {\n            OSG_INFO<<\"Warning nothing available to create shadows\"<<zMax<<std::endl;\n            return false;\n        }\n        else\n        {\n            projectionMatrix.makeOrtho(xMin,xMax, yMin, yMax,0.0,zMax-zMin);\n            viewMatrix.makeLookAt(frustum.center+positionedLight.lightDir*zMin, frustum.center+positionedLight.lightDir*zMax, lightUp);\n        }\n    }\n    else\n    {\n        double zMax=-dbl_max;\n\n        OSG_INFO<<\"lightDir = \"<<positionedLight.lightDir<<std::endl;\n        OSG_INFO<<\"lightPos3 = \"<<positionedLight.lightPos3<<std::endl;\n        for(Frustum::Vertices::iterator itr = frustum.corners.begin();\n            itr != frustum.corners.end();\n            ++itr)\n        {\n            osg::Vec3d cornerDelta(*itr - positionedLight.lightPos3);\n            osg::Vec3d cornerInLightCoords(cornerDelta*lightSide,\n                                           cornerDelta*lightUp,\n                                           cornerDelta*positionedLight.lightDir);\n\n            OSG_INFO<<\"   cornerInLightCoords= \"<<cornerInLightCoords<<std::endl;\n\n            zMax = osg::maximum( zMax, cornerInLightCoords.z());\n        }\n\n        OSG_INFO<<\"zMax = \"<<zMax<<std::endl;\n\n        if (zMax<0.0)\n        {\n            // view frustum entirely behind light\n            return false;\n        }\n\n        double minRatio = 0.0001;\n        double zMin=zMax*minRatio;\n\n        double fov = positionedLight.light->getSpotCutoff() * 2.0;\n        if(fov < 180.0)   // spotlight\n        {\n            projectionMatrix.makePerspective(fov, 1.0, zMin, zMax);\n            viewMatrix.makeLookAt(positionedLight.lightPos3,\n                                          positionedLight.lightPos3+positionedLight.lightDir, lightUp);\n        }\n        else\n        {\n            double fovMAX = 160.0f;\n            fov = 0.0;\n\n            // calculate the max FOV from the corners of the frustum relative to the light position\n            for(Frustum::Vertices::iterator itr = frustum.corners.begin();\n                itr != frustum.corners.end();\n                ++itr)\n            {\n                osg::Vec3d cornerDelta(*itr - positionedLight.lightPos3);\n                double length = cornerDelta.length();\n\n                if (length==0.0) fov = osg::minimum(fov, 180.0);\n                else\n                {\n                    double dotProduct = cornerDelta*positionedLight.lightDir;\n                    double angle = 2.0*osg::RadiansToDegrees( acos(dotProduct/length) );\n                    fov = osg::maximum(fov, angle);\n                }\n            }\n\n            OSG_INFO<<\"Computed fov = \"<<fov<<std::endl;\n\n            if (fov>fovMAX)\n            {\n                OSG_INFO<<\"Clampping fov = \"<<fov<<std::endl;\n                fov=fovMAX;\n            }\n\n            projectionMatrix.makePerspective(fov, 1.0, zMin, zMax);\n            viewMatrix.makeLookAt(positionedLight.lightPos3,\n                                  positionedLight.lightPos3+positionedLight.lightDir, lightUp);\n\n        }\n    }\n    return true;\n}\n\nstruct ConvexHull\n{\n    typedef std::vector<osg::Vec3d> Vertices;\n    typedef std::pair< osg::Vec3d, osg::Vec3d > Edge;\n    typedef std::list< Edge > Edges;\n\n    Edges _edges;\n\n    bool valid() const { return !_edges.empty(); }\n\n    void setToFrustum(MWShadowTechnique::Frustum& frustum)\n    {\n        _edges.push_back( Edge(frustum.corners[0],frustum.corners[1]) );\n        _edges.push_back( Edge(frustum.corners[1],frustum.corners[2]) );\n        _edges.push_back( Edge(frustum.corners[2],frustum.corners[3]) );\n        _edges.push_back( Edge(frustum.corners[3],frustum.corners[0]) );\n\n        _edges.push_back( Edge(frustum.corners[4],frustum.corners[5]) );\n        _edges.push_back( Edge(frustum.corners[5],frustum.corners[6]) );\n        _edges.push_back( Edge(frustum.corners[6],frustum.corners[7]) );\n        _edges.push_back( Edge(frustum.corners[7],frustum.corners[4]) );\n\n        _edges.push_back( Edge(frustum.corners[0],frustum.corners[4]) );\n        _edges.push_back( Edge(frustum.corners[1],frustum.corners[5]) );\n        _edges.push_back( Edge(frustum.corners[2],frustum.corners[6]) );\n        _edges.push_back( Edge(frustum.corners[3],frustum.corners[7]) );\n    }\n\n    struct ConvexHull2D\n    {\n        // Implementation based on https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain#C++\n        typedef osg::Vec3d Point;\n\n        static double cross(const Point &O, const Point &A, const Point &B)\n        {\n            return (A.x() - O.x())*(B.y() - O.y()) - (A.y() - O.y())*(B.x() - O.x());\n        }\n\n        // Calculates the 2D convex hull and returns it as a vector containing the points in CCW order with the first and last point being the same.\n        static std::vector<Point> convexHull(std::set<Point> &P)\n        {\n            size_t n = P.size(), k = 0;\n            if (n <= 3)\n                return std::vector<Point>(P.cbegin(), P.cend());\n\n            std::vector<Point> H(2 * n);\n\n            // Points are already sorted in a std::set\n\n            // Build lower hull\n            for (auto pItr = P.cbegin(); pItr != P.cend(); ++pItr)\n            {\n                while (k >= 2 && cross(H[k - 2], H[k - 1], *pItr) <= 0)\n                    k--;\n                H[k++] = *pItr;\n            }\n\n            // Build upper hull\n            size_t t = k + 1;\n            for (auto pItr = std::next(P.crbegin()); pItr != P.crend(); ++pItr)\n            {\n                while (k >= t && cross(H[k - 2], H[k - 1], *pItr) <= 0)\n                    k--;\n                H[k++] = *pItr;\n            }\n\n            H.resize(k - 1);\n            return H;\n        }\n    };\n\n    Vertices findInternalEdges(osg::Vec3d mainVertex, Vertices connectedVertices)\n    {\n        Vertices internalEdgeVertices;\n        for (const auto& vertex : connectedVertices)\n        {\n            osg::Matrixd matrix;\n            osg::Vec3d dir = vertex - mainVertex;\n            matrix.makeLookAt(mainVertex, vertex, dir.z() == 0 ? osg::Vec3d(0, 0, 1) : osg::Vec3d(1, 0, 0));\n            Vertices testVertices;\n            for (const auto& testVertex : connectedVertices)\n            {\n                if (vertex != testVertex)\n                    testVertices.push_back(testVertex);\n            }\n            std::vector<double> bearings;\n            for (const auto& testVertex : testVertices)\n            {\n                osg::Vec3d transformedVertex = testVertex * matrix;\n                bearings.push_back(atan2(transformedVertex.y(), transformedVertex.x()));\n            }\n            std::sort(bearings.begin(), bearings.end());\n            bool keep = false;\n            for (auto itr = bearings.begin(); itr + 1 != bearings.end(); ++itr)\n            {\n                if (*itr + osg::PI < *(itr + 1))\n                {\n                    keep = true;\n                    break;\n                }\n            }\n            if (!keep && bearings[0] + osg::PI > bearings.back())\n                keep = true;\n            if (!keep)\n                internalEdgeVertices.push_back(vertex);\n        }\n        return internalEdgeVertices;\n    }\n\n    void extendTowardsNegativeZ()\n    {\n        typedef std::set<osg::Vec3d> VertexSet;\n\n        // Collect the set of vertices\n        VertexSet vertices;\n        for (const Edge& edge : _edges)\n        {\n            vertices.insert(edge.first);\n            vertices.insert(edge.second);\n        }\n\n        if (vertices.size() == 0)\n            return;\n\n        // Get the vertices contributing to the 2D convex hull\n        Vertices extremeVertices = ConvexHull2D::convexHull(vertices);\n        VertexSet extremeVerticesSet(extremeVertices.cbegin(), extremeVertices.cend());\n\n        // Add their extrusions to the final edge collection\n        // We extrude as far as -1.5 as the coordinate space shouldn't ever put any shadow casters further than -1.0\n        Edges finalEdges;\n        // Add edges towards -Z\n        for (auto vertex : extremeVertices)\n            finalEdges.push_back(Edge(vertex, osg::Vec3d(vertex.x(), vertex.y(), -1.5)));\n        // Add edge loop to 'seal' the hull\n        for (auto itr = extremeVertices.cbegin(); itr != extremeVertices.cend() - 1; ++itr)\n            finalEdges.push_back(Edge(osg::Vec3d(itr->x(), itr->y(), -1.5), osg::Vec3d((itr + 1)->x(), (itr + 1)->y(), -1.5)));\n        // The convex hull algorithm we are using sometimes places a point at both ends of the vector, so we don't always need to add the last edge separately.\n        if (extremeVertices.front() != extremeVertices.back())\n            finalEdges.push_back(Edge(osg::Vec3d(extremeVertices.front().x(), extremeVertices.front().y(), -1.5), osg::Vec3d(extremeVertices.back().x(), extremeVertices.back().y(), -1.5)));\n\n        // Remove internal edges connected to extreme vertices\n        for (auto vertex : extremeVertices)\n        {\n            Vertices connectedVertices;\n            for (const Edge& edge : _edges)\n            {\n                if (edge.first == vertex)\n                    connectedVertices.push_back(edge.second);\n                else if (edge.second == vertex)\n                    connectedVertices.push_back(edge.first);\n            }\n            connectedVertices.push_back(osg::Vec3d(vertex.x(), vertex.y(), -1.5));\n\n            Vertices unwantedEdgeEnds = findInternalEdges(vertex, connectedVertices);\n            for (auto edgeEnd : unwantedEdgeEnds)\n            {\n                for (auto itr = _edges.begin(); itr != _edges.end();)\n                {\n                    if (*itr == Edge(vertex, edgeEnd))\n                    {\n                        itr = _edges.erase(itr);\n                        break;\n                    }\n                    else if (*itr == Edge(edgeEnd, vertex))\n                    {\n                        itr = _edges.erase(itr);\n                        break;\n                    }\n                    else\n                        ++itr;\n                }\n            }\n        }\n\n        // Gather connected vertices\n        VertexSet unprocessedConnectedVertices(extremeVertices.begin(), extremeVertices.end());\n        VertexSet connectedVertices;\n        while (unprocessedConnectedVertices.size() > 0)\n        {\n            osg::Vec3d vertex = *unprocessedConnectedVertices.begin();\n            unprocessedConnectedVertices.erase(unprocessedConnectedVertices.begin());\n            connectedVertices.insert(vertex);\n            for (const Edge& edge : _edges)\n            {\n                osg::Vec3d otherEnd;\n                if (edge.first == vertex)\n                    otherEnd = edge.second;\n                else if (edge.second == vertex)\n                    otherEnd = edge.first;\n                else\n                    continue;\n\n                if (connectedVertices.count(otherEnd))\n                    continue;\n\n                unprocessedConnectedVertices.insert(otherEnd);\n            }\n        }\n\n        for (const Edge& edge : _edges)\n        {\n            if (connectedVertices.count(edge.first) || connectedVertices.count(edge.second))\n                finalEdges.push_back(edge);\n        }\n\n        _edges = finalEdges;\n    }\n\n    void transform(const osg::Matrixd& m)\n    {\n        for(Edges::iterator itr = _edges.begin();\n            itr != _edges.end();\n            ++itr)\n        {\n            itr->first = itr->first * m;\n            itr->second = itr->second * m;\n        }\n    }\n\n    void clip(const osg::Plane& plane)\n    {\n        Vertices intersections;\n\n        // OSG_NOTICE<<\"clip(\"<<plane<<\") edges.size()=\"<<_edges.size()<<std::endl;\n        for(Edges::iterator itr = _edges.begin();\n            itr != _edges.end();\n            )\n        {\n            double d0 = plane.distance(itr->first);\n            double d1 = plane.distance(itr->second);\n            if (d0<0.0 && d1<0.0)\n            {\n                // OSG_NOTICE<<\"  Edge completely outside, removing\"<<std::endl;\n                Edges::iterator to_delete_itr = itr;\n                ++itr;\n                _edges.erase(to_delete_itr);\n            }\n            else if (d0>=0.0 && d1>=0.0)\n            {\n                // OSG_NOTICE<<\"  Edge completely inside\"<<std::endl;\n                ++itr;\n            }\n            else\n            {\n                osg::Vec3d& v0 = itr->first;\n                osg::Vec3d& v1 = itr->second;\n                osg::Vec3d intersection = v0 - (v1-v0)*(d0/(d1-d0));\n                intersections.push_back(intersection);\n                // OSG_NOTICE<<\"  Edge across clip plane, v0=\"<<v0<<\", v1=\"<<v1<<\", intersection= \"<<intersection<<std::endl;\n                if (d0<0.0)\n                {\n                    // move first vertex on edge\n                    v0 = intersection;\n                }\n                else\n                {\n                    // move second vertex on edge\n                    v1 = intersection;\n                }\n\n                ++itr;\n            }\n        }\n        // OSG_NOTICE<<\"After clipping, have \"<<intersections.size()<<\" to insert\"<<std::endl;\n\n        if (intersections.size() < 2)\n        {\n            return;\n        }\n\n        if (intersections.size() == 2)\n        {\n            _edges.push_back( Edge(intersections[0], intersections[1]) );\n            return;\n        }\n\n        if (intersections.size() == 3)\n        {\n            _edges.push_back( Edge(intersections[0], intersections[1]) );\n            _edges.push_back( Edge(intersections[1], intersections[2]) );\n            _edges.push_back( Edge(intersections[2], intersections[0]) );\n            return;\n        }\n\n        // more than 3 intersections so have to sort them in clockwise order so that\n        // we can generate the edges correctly.\n\n        osg::Vec3d normal(plane.getNormal());\n\n        osg::Vec3d side_x = osg::Vec3d(1.0,0.0,0.0) ^ normal;\n        osg::Vec3d side_y = osg::Vec3d(0.0,1.0,0.0) ^ normal;\n        osg::Vec3d side = (side_x.length2()>=side_y.length2()) ? side_x : side_y;\n        side.normalize();\n\n        osg::Vec3d up = side ^ normal;\n        up.normalize();\n\n        osg::Vec3d center;\n        for(Vertices::iterator itr = intersections.begin();\n            itr != intersections.end();\n            ++itr)\n        {\n            center += *itr;\n\n            center.x() = osg::maximum(center.x(), -dbl_max);\n            center.y() = osg::maximum(center.y(), -dbl_max);\n            center.z() = osg::maximum(center.z(), -dbl_max);\n\n            center.x() = osg::minimum(center.x(), dbl_max);\n            center.y() = osg::minimum(center.y(), dbl_max);\n            center.z() = osg::minimum(center.z(), dbl_max);\n        }\n\n        center /= double(intersections.size());\n\n        typedef std::map<double, std::list<std::pair<osg::Vec3d, double>>> VertexMap;\n        VertexMap vertexMap;\n        for(Vertices::iterator itr = intersections.begin();\n            itr != intersections.end();\n            ++itr)\n        {\n            osg::Vec3d dv = (*itr-center);\n            double h = dv * side;\n            double v = dv * up;\n            double angle = atan2(h,v);\n            // OSG_NOTICE<<\"angle = \"<<osg::RadiansToDegrees(angle)<<\", h=\"<<h<<\" v= \"<<v<<std::endl;\n\n            // We need to make sure all intersections are added to the list in the right order, even if they're so far away that their angles work out the same.\n            double sortValue;\n            if (angle < osg::DegreesToRadians(-135.0) || angle > osg::DegreesToRadians(135.0))\n                sortValue = -h;\n            else if (angle < osg::DegreesToRadians(-45.0))\n                sortValue = v;\n            else if (angle < osg::DegreesToRadians(45.0))\n                sortValue = h;\n            else\n                sortValue = -v;\n            if (vertexMap.count(angle))\n            {\n                auto listItr = vertexMap[angle].begin();\n                while (listItr != vertexMap[angle].end() && listItr->second < sortValue)\n                    ++listItr;\n                vertexMap[angle].insert(listItr, std::make_pair(*itr, sortValue));\n            }\n            else\n                vertexMap[angle].push_back(std::make_pair(*itr, sortValue));\n        }\n\n        osg::Vec3d previous_v = vertexMap.rbegin()->second.back().first;\n        for(VertexMap::iterator itr = vertexMap.begin();\n            itr != vertexMap.end();\n            ++itr)\n        {\n            for (auto vertex : itr->second)\n            {\n                _edges.push_back(Edge(previous_v, vertex.first));\n                previous_v = vertex.first;\n            }\n        }\n\n        // OSG_NOTICE<<\"  after clip(\"<<plane<<\") edges.size()=\"<<_edges.size()<<std::endl;\n    }\n\n    void clip(const osg::Polytope& polytope)\n    {\n        const osg::Polytope::PlaneList& planes = polytope.getPlaneList();\n        for(osg::Polytope::PlaneList::const_iterator itr = planes.begin();\n            itr != planes.end();\n            ++itr)\n        {\n            clip(*itr);\n        }\n    }\n\n    double min(unsigned int index) const\n    {\n        double m = dbl_max;\n        for(Edges::const_iterator itr = _edges.begin();\n            itr != _edges.end();\n            ++itr)\n        {\n            const Edge& edge = *itr;\n            if (edge.first[index]<m) m = edge.first[index];\n            if (edge.second[index]<m) m = edge.second[index];\n        }\n        return m;\n    }\n\n    double max(unsigned int index) const\n    {\n        double m = -dbl_max;\n        for(Edges::const_iterator itr = _edges.begin();\n            itr != _edges.end();\n            ++itr)\n        {\n            const Edge& edge = *itr;\n            if (edge.first[index]>m) m = edge.first[index];\n            if (edge.second[index]>m) m = edge.second[index];\n        }\n        return m;\n    }\n\n    double minRatio(const osg::Vec3d& eye, unsigned int index) const\n    {\n        double m = dbl_max;\n        osg::Vec3d delta;\n        double ratio;\n        for(Edges::const_iterator itr = _edges.begin();\n            itr != _edges.end();\n            ++itr)\n        {\n            const Edge& edge = *itr;\n\n            delta = edge.first-eye;\n            ratio = delta[index]/delta[1];\n            if (ratio<m) m = ratio;\n\n            delta = edge.second-eye;\n            ratio = delta[index]/delta[1];\n            if (ratio<m) m = ratio;\n        }\n        return m;\n    }\n\n    double maxRatio(const osg::Vec3d& eye, unsigned int index) const\n    {\n        double m = -dbl_max;\n        osg::Vec3d delta;\n        double ratio;\n        for(Edges::const_iterator itr = _edges.begin();\n            itr != _edges.end();\n            ++itr)\n        {\n            const Edge& edge = *itr;\n\n            delta = edge.first-eye;\n            ratio = delta[index]/delta[1];\n            if (ratio>m) m = ratio;\n\n            delta = edge.second-eye;\n            ratio = delta[index]/delta[1];\n            if (ratio>m) m = ratio;\n        }\n        return m;\n    }\n\n    void output(std::ostream& out)\n    {\n        out<<\"ConvexHull\"<<std::endl;\n        for(Edges::const_iterator itr = _edges.begin();\n            itr != _edges.end();\n            ++itr)\n        {\n            const Edge& edge = *itr;\n            out<<\"   edge (\"<<edge.first<<\") (\"<<edge.second<<\")\"<<std::endl;\n        }\n    }\n};\n\n\nstruct RenderLeafBounds\n{\n    RenderLeafBounds():\n        computeRatios(false),\n        numRenderLeaf(0),\n        n(0.0),\n        previous_modelview(0),\n        clip_min_x(-1.0), clip_max_x(1.0),\n        clip_min_y(-1.0), clip_max_y(1.0),\n        clip_min_z(-1.0), clip_max_z(1.0),\n        clip_min_x_ratio(-dbl_max), clip_max_x_ratio(dbl_max),\n        clip_min_z_ratio(-dbl_max), clip_max_z_ratio(dbl_max),\n        min_x_ratio(dbl_max), max_x_ratio(-dbl_max),\n        min_z_ratio(dbl_max), max_z_ratio(-dbl_max),\n        min_x(1.0), max_x(-1.0),\n        min_y(1.0), max_y(-1.0),\n        min_z(1.0), max_z(-1.0)\n    {\n        //OSG_NOTICE<<std::endl<<\"RenderLeafBounds\"<<std::endl;\n    }\n\n    void set(const osg::Matrixd& p)\n    {\n        computeRatios = false;\n        light_p = p;\n        clip_min_x = -dbl_max; clip_max_x = dbl_max;\n        clip_min_y = -dbl_max; clip_max_y = dbl_max;\n        clip_min_z = -dbl_max; clip_max_z = dbl_max;\n        min_x = dbl_max; max_x = -dbl_max;\n        min_y = dbl_max; max_y = -dbl_max;\n        min_z = dbl_max; max_z = -dbl_max;\n    }\n\n    void set(const osg::Matrixd& p, osg::Vec3d& e_ls, double nr)\n    {\n        computeRatios = true;\n        light_p = p;\n        eye_ls = e_ls;\n        n = nr;\n    }\n\n    void operator() (const osgUtil::RenderLeaf* renderLeaf)\n    {\n        ++numRenderLeaf;\n\n        if (renderLeaf->_modelview.get()!=previous_modelview)\n        {\n            previous_modelview = renderLeaf->_modelview.get();\n            if (previous_modelview)\n            {\n                light_mvp.mult(*renderLeaf->_modelview, light_p);\n            }\n            else\n            {\n                // no modelview matrix (such as when LightPointNode is in the scene graph) so assume\n                // that modelview matrix is indentity.\n                light_mvp = light_p;\n            }\n            // OSG_INFO<<\"Computing new light_mvp \"<<light_mvp<<std::endl;\n        }\n        else\n        {\n            // OSG_INFO<<\"Reusing light_mvp \"<<light_mvp<<std::endl;\n        }\n\n        const osg::BoundingBox& bb = renderLeaf->_drawable->getBoundingBox();\n        if (bb.valid())\n        {\n            // OSG_NOTICE<<\"checked extents of \"<<renderLeaf->_drawable->getName()<<std::endl;\n            handle(osg::Vec3d(bb.xMin(),bb.yMin(),bb.zMin()));\n            handle(osg::Vec3d(bb.xMax(),bb.yMin(),bb.zMin()));\n            handle(osg::Vec3d(bb.xMin(),bb.yMax(),bb.zMin()));\n            handle(osg::Vec3d(bb.xMax(),bb.yMax(),bb.zMin()));\n            handle(osg::Vec3d(bb.xMin(),bb.yMin(),bb.zMax()));\n            handle(osg::Vec3d(bb.xMax(),bb.yMin(),bb.zMax()));\n            handle(osg::Vec3d(bb.xMin(),bb.yMax(),bb.zMax()));\n            handle(osg::Vec3d(bb.xMax(),bb.yMax(),bb.zMax()));\n        }\n        else\n        {\n            OSG_INFO<<\"bb invalid\"<<std::endl;\n        }\n    }\n\n    void handle(const osg::Vec3d& v)\n    {\n        osg::Vec3d ls = v * light_mvp;\n\n        // OSG_NOTICE<<\"   corner v=\"<<v<<\", ls=\"<<ls<<std::endl;\n\n        if (computeRatios)\n        {\n            osg::Vec3d delta = ls-eye_ls;\n\n            double x_ratio, z_ratio;\n            if (delta.y()>n)\n            {\n                x_ratio = delta.x()/delta.y();\n                z_ratio = delta.z()/delta.y();\n            }\n            else\n            {\n                x_ratio = delta.x()/n;\n                z_ratio = delta.z()/n;\n            }\n\n            if (x_ratio<min_x_ratio) min_x_ratio = x_ratio;\n            if (x_ratio>max_x_ratio) max_x_ratio = x_ratio;\n            if (z_ratio<min_z_ratio) min_z_ratio = z_ratio;\n            if (z_ratio>max_z_ratio) max_z_ratio = z_ratio;\n        }\n\n        // clip to the light space\n        if (ls.x()<clip_min_x) ls.x()=clip_min_x;\n        if (ls.x()>clip_max_x) ls.x()=clip_max_x;\n        if (ls.y()<clip_min_y) ls.y()=clip_min_y;\n        if (ls.y()>clip_max_y) ls.y()=clip_max_y;\n        if (ls.z()<clip_min_z) ls.z()=clip_min_z;\n        if (ls.z()>clip_max_z) ls.z()=clip_max_z;\n\n        // compute the xyz range.\n        if (ls.x()<min_x) min_x=ls.x();\n        if (ls.x()>max_x) max_x=ls.x();\n        if (ls.y()<min_y) min_y=ls.y();\n        if (ls.y()>max_y) max_y=ls.y();\n        if (ls.z()<min_z) { min_z=ls.z(); /* OSG_NOTICE<<\" - \";*/ }\n        if (ls.z()>max_z) { max_z=ls.z(); /* OSG_NOTICE<<\" + \";*/ }\n\n        // OSG_NOTICE<<\"   bb.z() in ls = \"<<ls.z()<<std::endl;\n\n    }\n\n    bool                computeRatios;\n\n    unsigned int        numRenderLeaf;\n\n    osg::Matrixd        light_p;\n    osg::Vec3d          eye_ls;\n    double              n;\n\n    osg::Matrixd        light_mvp;\n    osg::RefMatrix*     previous_modelview;\n\n    double clip_min_x, clip_max_x;\n    double clip_min_y, clip_max_y;\n    double clip_min_z, clip_max_z;\n\n    double clip_min_x_ratio, clip_max_x_ratio;\n    double clip_min_z_ratio, clip_max_z_ratio;\n\n    double min_x_ratio, max_x_ratio;\n    double min_z_ratio, max_z_ratio;\n    double min_x, max_x;\n    double min_y, max_y;\n    double min_z, max_z;\n};\n\nbool MWShadowTechnique::cropShadowCameraToMainFrustum(Frustum& frustum, osg::Camera* camera, double viewNear, double viewFar, std::vector<osg::Plane>& planeList)\n{\n    osg::Matrixd light_p = camera->getProjectionMatrix();\n    osg::Matrixd light_v = camera->getViewMatrix();\n    osg::Matrixd light_vp = light_v * light_p;\n    osg::Matrixd oldLightP = light_p;\n    \n    ConvexHull convexHull;\n    convexHull.setToFrustum(frustum);\n\n    osg::Vec3d nearPoint = frustum.eye + frustum.frustumCenterLine * viewNear;\n    osg::Vec3d farPoint = frustum.eye + frustum.frustumCenterLine * viewFar;\n\n    double nearDist = -frustum.frustumCenterLine * nearPoint;\n    double farDist = frustum.frustumCenterLine * farPoint;\n\n    convexHull.clip(osg::Plane(frustum.frustumCenterLine, nearDist));\n    convexHull.clip(osg::Plane(-frustum.frustumCenterLine, farDist));\n\n    convexHull.transform(light_vp);\n\n    double xMin = -1.0, xMax = 1.0;\n    double yMin = -1.0, yMax = 1.0;\n    double zMin = -1.0, zMax = 1.0;\n\n    if (convexHull.valid())\n    {\n        xMin = osg::maximum(-1.0, convexHull.min(0));\n        xMax = osg::minimum(1.0, convexHull.max(0));\n        yMin = osg::maximum(-1.0, convexHull.min(1));\n        yMax = osg::minimum(1.0, convexHull.max(1));\n        zMin = osg::maximum(-1.0, convexHull.min(2));\n        zMax = osg::minimum(1.0, convexHull.max(2));\n    }\n    else\n        return false;\n\n    if (xMin != -1.0 || yMin != -1.0 || zMin != -1.0 ||\n        xMax != 1.0 || yMax != 1.0 || zMax != 1.0)\n    {\n        osg::Matrix m;\n        m.makeTranslate(osg::Vec3d(-0.5*(xMax + xMin),\n                                   -0.5*(yMax + yMin),\n                                   -0.5*(zMax + zMin)));\n\n        m.postMultScale(osg::Vec3d(2.0 / (xMax - xMin),\n                                   2.0 / (yMax - yMin),\n                                   2.0 / (zMax - zMin)));\n\n        light_p.postMult(m);\n        camera->setProjectionMatrix(light_p);\n\n        convexHull.transform(osg::Matrixd::inverse(oldLightP));\n\n        xMin = convexHull.min(0);\n        xMax = convexHull.max(0);\n        yMin = convexHull.min(1);\n        yMax = convexHull.max(1);\n        zMin = convexHull.min(2);\n\n        planeList.emplace_back(0.0, -1.0, 0.0, yMax);\n        planeList.emplace_back(0.0, 1.0, 0.0, -yMin);\n        planeList.emplace_back(-1.0, 0.0, 0.0, xMax);\n        planeList.emplace_back(1.0, 0.0, 0.0, -xMin);\n        // In view space, the light is at the most positive value, and we want to cull stuff beyond the minimum value.\n        planeList.emplace_back(0.0, 0.0, 1.0, -zMin);\n        // Don't add a zMax culling plane - we still want those objects, but don't care about their depth buffer value.\n    }\n\n    return true;\n}\n\nbool MWShadowTechnique::adjustPerspectiveShadowMapCameraSettings(osgUtil::RenderStage* renderStage, Frustum& frustum, LightData& /*positionedLight*/, osg::Camera* camera, double viewNear, double viewFar)\n{\n    const ShadowSettings* settings = getShadowedScene()->getShadowSettings();\n\n    //frustum.projectionMatrix;\n    //frustum.modelViewMatrix;\n\n    osg::Matrixd light_p = camera->getProjectionMatrix();\n    osg::Matrixd light_v = camera->getViewMatrix();\n    osg::Matrixd light_vp = light_v * light_p;\n    osg::Vec3d lightdir(0.0,0.0,-1.0);\n\n    // check whether this light space projection is perspective or orthographic.\n    bool orthographicLightSpaceProjection = light_p(0,3)==0.0 && light_p(1,3)==0.0 && light_p(2,3)==0.0;\n\n    if (!orthographicLightSpaceProjection)\n    {\n        OSG_INFO<<\"perspective light space projection not yet supported.\"<<std::endl;\n        return false;\n    }\n\n\n    //OSG_NOTICE<<\"light_v=\"<<light_v<<std::endl;\n    //OSG_NOTICE<<\"light_p=\"<<light_p<<std::endl;\n\n    ConvexHull convexHull;\n    convexHull.setToFrustum(frustum);\n\n    osg::Vec3d nearPoint = frustum.eye + frustum.frustumCenterLine * viewNear;\n    osg::Vec3d farPoint = frustum.eye + frustum.frustumCenterLine * viewFar;\n\n    double nearDist = -frustum.frustumCenterLine * nearPoint;\n    double farDist = frustum.frustumCenterLine * farPoint;\n\n    convexHull.clip(osg::Plane(frustum.frustumCenterLine, nearDist));\n    convexHull.clip(osg::Plane(-frustum.frustumCenterLine, farDist));\n\n#if 0\n    OSG_NOTICE<<\"ws ConvexHull xMin=\"<<convexHull.min(0)<<\", xMax=\"<<convexHull.max(0)<<std::endl;\n    OSG_NOTICE<<\"ws ConvexHull yMin=\"<<convexHull.min(1)<<\", yMax=\"<<convexHull.max(1)<<std::endl;\n    OSG_NOTICE<<\"ws ConvexHull zMin=\"<<convexHull.min(2)<<\", zMax=\"<<convexHull.max(2)<<std::endl;\n\n    convexHull.output(osg::notify(osg::NOTICE));\n#endif\n\n    convexHull.transform(light_vp);\n\n    ConvexHull convexHullUnextended = convexHull;\n\n    convexHull.extendTowardsNegativeZ();\n\n#if 0\n    convexHull.output(osg::notify(osg::NOTICE));\n\n    OSG_NOTICE<<\"ls ConvexHull xMin=\"<<convexHull.min(0)<<\", xMax=\"<<convexHull.max(0)<<std::endl;\n    OSG_NOTICE<<\"ls ConvexHull yMin=\"<<convexHull.min(1)<<\", yMax=\"<<convexHull.max(1)<<std::endl;\n    OSG_NOTICE<<\"ls ConvexHull zMin=\"<<convexHull.min(2)<<\", zMax=\"<<convexHull.max(2)<<std::endl;\n#endif\n\n#if 0\n    // only applicable when the light space contains the whole model contained in the view frustum.\n    {\n        convexHull.clip(osg::Plane(0.0,0.0,1,1.0)); // clip by near plane of light space.\n        convexHull.clip(osg::Plane(0.0,0.0,-1,1.0));  // clip by far plane of light space.\n    }\n#endif\n\n#if 1\n    if (renderStage)\n    {\n#if 1\n        osg::ElapsedTime timer;\n#endif\n\n        RenderLeafTraverser<RenderLeafBounds> rli;\n        rli.set(light_p);\n        rli.traverse(renderStage);\n\n        if (rli.numRenderLeaf==0)\n        {\n            return false;\n        }\n#if 0\n        OSG_NOTICE<<\"New Time for RenderLeafTraverser \"<<timer.elapsedTime_m()<<\"ms, number of render leaves \"<<rli.numRenderLeaf<<std::endl;\n        OSG_NOTICE<<\"   scene bounds min_x=\"<<rli.min_x<<\", max_x=\"<<rli.max_x<<std::endl;\n        OSG_NOTICE<<\"   scene bounds min_y=\"<<rli.min_y<<\", max_y=\"<<rli.max_y<<std::endl;\n        OSG_NOTICE<<\"   scene bounds min_z=\"<<rli.min_z<<\", max_z=\"<<rli.max_z<<std::endl;\n#endif\n\n#if 0\n        double widest_x = osg::maximum(fabs(rli.min_x), fabs(rli.max_x));\n        double widest_y = osg::maximum(fabs(rli.min_y), fabs(rli.max_y));\n        double widest_z = osg::maximum(fabs(rli.min_z), fabs(rli.max_z));\n#endif\n\n#if 1\n#if 1\n        convexHull.clip(osg::Plane(1.0,0.0,0.0,-rli.min_x));\n        convexHull.clip(osg::Plane(-1.0,0.0,0.0,rli.max_x));\n\n        convexHullUnextended.clip(osg::Plane(1.0, 0.0, 0.0, -rli.min_x));\n        convexHullUnextended.clip(osg::Plane(-1.0, 0.0, 0.0, rli.max_x));\n#else\n        convexHull.clip(osg::Plane(1.0,0.0,0.0,widest_x));\n        convexHull.clip(osg::Plane(-1.0,0.0,0.0,widest_x));\n#endif\n#if 1\n        convexHull.clip(osg::Plane(0.0,1.0,0.0,-rli.min_y));\n        convexHull.clip(osg::Plane(0.0,-1.0,0.0,rli.max_y));\n\n        convexHullUnextended.clip(osg::Plane(0.0, 1.0, 0.0, -rli.min_y));\n        convexHullUnextended.clip(osg::Plane(0.0, -1.0, 0.0, rli.max_y));\n#endif\n#endif\n\n#if 1\n        convexHull.clip(osg::Plane(0.0,0.0,1.0,-rli.min_z));\n        convexHull.clip(osg::Plane(0.0,0.0,-1.0,rli.max_z));\n\n        convexHullUnextended.clip(osg::Plane(0.0, 0.0, 1.0, -rli.min_z));\n        convexHullUnextended.clip(osg::Plane(0.0, 0.0, -1.0, rli.max_z));\n#elif 0\n        convexHull.clip(osg::Plane(0.0,0.0,1.0,1.0));\n        convexHull.clip(osg::Plane(0.0,0.0,-1.0,1.0));\n#endif\n\n#if 0\n        OSG_NOTICE<<\"widest_x = \"<<widest_x<<std::endl;\n        OSG_NOTICE<<\"widest_y = \"<<widest_y<<std::endl;\n        OSG_NOTICE<<\"widest_z = \"<<widest_z<<std::endl;\n#endif\n    }\n#endif\n\n#if 0\n    convexHull.output(osg::notify(osg::NOTICE));\n\n    OSG_NOTICE<<\"after clipped ls ConvexHull xMin=\"<<convexHull.min(0)<<\", xMax=\"<<convexHull.max(0)<<std::endl;\n    OSG_NOTICE<<\"after clipped ls ConvexHull yMin=\"<<convexHull.min(1)<<\", yMax=\"<<convexHull.max(1)<<std::endl;\n    OSG_NOTICE<<\"after clipped ls ConvexHull zMin=\"<<convexHull.min(2)<<\", zMax=\"<<convexHull.max(2)<<std::endl;\n#endif\n\n    double xMin=-1.0, xMax=1.0;\n    double yMin=-1.0, yMax=1.0;\n    double zMin=-1.0, zMax=1.0;\n\n    if (convexHull.valid())\n    {\n        double widest_x = osg::maximum(fabs(convexHull.min(0)), fabs(convexHull.max(0)));\n        xMin = osg::maximum(-1.0,-widest_x);\n        xMax = osg::minimum(1.0,widest_x);\n        yMin = osg::maximum(-1.0,convexHull.min(1));\n        yMax = osg::minimum(1.0,convexHull.max(1));\n    }\n    else\n    {\n        // clipping of convex hull has invalidated it, so reset it so later checks on it provide valid results.\n        convexHull.setToFrustum(frustum);\n        convexHull.transform(light_vp);\n    }\n\n#if 0\n    OSG_NOTICE<<\"xMin = \"<<xMin<<\", \\txMax = \"<<xMax<<std::endl;\n    OSG_NOTICE<<\"yMin = \"<<yMin<<\", \\tyMax = \"<<yMax<<std::endl;\n    OSG_NOTICE<<\"zMin = \"<<zMin<<\", \\tzMax = \"<<zMax<<std::endl;\n#endif\n\n#if 1\n    // we always want the lightspace to include the computed near plane.\n    zMin = -1.0;\n    if (xMin!=-1.0 || yMin!=-1.0 || zMin!=-1.0 ||\n        xMax!=1.0 || yMax!=1.0 || zMax!=1.0)\n    {\n        osg::Matrix m;\n        m.makeTranslate(osg::Vec3d(-0.5*(xMax+xMin),\n                                    -0.5*(yMax+yMin),\n                                    -0.5*(zMax+zMin)));\n\n        m.postMultScale(osg::Vec3d(2.0/(xMax-xMin),\n                                   2.0/(yMax-yMin),\n                                   2.0/(zMax-zMin)));\n\n        convexHull.transform(m);\n        convexHullUnextended.transform(m);\n        light_p.postMult(m);\n        light_vp = light_v * light_p;\n\n#if 0\n        OSG_NOTICE<<\"Adjusting projection matrix \"<<m<<std::endl;\n        convexHull.output(osg::notify(osg::NOTICE));\n#endif\n        camera->setProjectionMatrix(light_p);\n    }\n\n#endif\n\n    osg::Vec3d eye_v = frustum.eye * light_v;\n    //osg::Vec3d centerNearPlane_v = frustum.centerNearPlane * light_v;\n    osg::Vec3d center_v = frustum.center * light_v;\n    osg::Vec3d viewdir_v = center_v-eye_v; viewdir_v.normalize();\n\n    double dotProduct_v = lightdir * viewdir_v;\n    double gamma_v = acos(dotProduct_v);\n    if (gamma_v<osg::DegreesToRadians(settings->getPerspectiveShadowMapCutOffAngle()) || gamma_v>osg::DegreesToRadians(180-settings->getPerspectiveShadowMapCutOffAngle()))\n    {\n        // OSG_NOTICE<<\"Light and view vectors near parallel - use standard shadow map.\"<<std::endl;\n        return true;\n    }\n\n    //OSG_NOTICE<<\"gamma=\"<<osg::RadiansToDegrees(gamma_v)<<std::endl;\n    //OSG_NOTICE<<\"eye_v=\"<<eye_v<<std::endl;\n    //OSG_NOTICE<<\"viewdir_v=\"<<viewdir_v<<std::endl;\n\n    osg::Vec3d eye_ls = frustum.eye * light_vp;\n#if 0\n    if (eye_ls.y()>-1.0)\n    {\n        OSG_NOTICE<<\"Eye point within light space - use standard shadow map.\"<<std::endl;\n        return true;\n    }\n#endif\n\n    //osg::Vec3d centerNearPlane_ls = frustum.centerNearPlane * light_vp;\n    //osg::Vec3d centerFarPlane_ls = frustum.centerFarPlane * light_vp;\n    osg::Vec3d center_ls = frustum.center * light_vp;\n    osg::Vec3d viewdir_ls = center_ls-eye_ls; viewdir_ls.normalize();\n\n    osg::Vec3d side = lightdir ^ viewdir_ls; side.normalize();\n    osg::Vec3d up = side ^ lightdir;\n\n    double d = 2.0;\n\n    double alpha = osg::DegreesToRadians(30.0);\n    double n = tan(alpha)*tan(osg::PI_2-gamma_v)*tan(osg::PI_2-gamma_v);\n    //double n = tan(alpha)*tan(osg::PI_2-gamma_v);\n\n    //OSG_NOTICE<<\"n = \"<<n<<\", eye_ls.y()=\"<<eye_ls.y()<<\", eye_v=\"<<eye_v<<\", eye=\"<<frustum.eye<<std::endl;\n    double min_n = osg::maximum(-1.0-eye_ls.y(), settings->getMinimumShadowMapNearFarRatio());\n    if (n<min_n)\n    {\n        //OSG_NOTICE<<\"Clamping n to eye point\"<<std::endl;\n        n=min_n;\n    }\n\n    //n = min_n;\n\n    //n = 0.01;\n\n    //n = z_n;\n\n    double f = n+d;\n\n    double a = (f+n)/(f-n);\n    double b = -2.0*f*n/(f-n);\n\n    osg::Vec3d virtual_eye(0.0,-1.0-n, eye_ls.z());\n\n    osg::Matrixd lightView;\n    lightView.makeLookAt(virtual_eye, virtual_eye+lightdir, up);\n\n#if 0\n    OSG_NOTICE<<\"n = \"<<n<<\", f=\"<<f<<std::endl;\n    OSG_NOTICE<<\"eye_ls = \"<<eye_ls<<\", virtual_eye=\"<<virtual_eye<<std::endl;\n    OSG_NOTICE<<\"frustum.eyes=\"<<frustum.eye<<std::endl;\n#endif\n\n    double min_x_ratio = 0.0;\n    double max_x_ratio = 0.0;\n    double min_z_ratio = dbl_max;\n    double max_z_ratio = -dbl_max;\n\n    min_x_ratio = convexHull.valid() ? convexHull.minRatio(virtual_eye,0) : -dbl_max;\n    max_x_ratio = convexHull.valid() ? convexHull.maxRatio(virtual_eye,0) : dbl_max;\n    //min_z_ratio = convexHull.minRatio(virtual_eye,2);\n    //max_z_ratio = convexHull.maxRatio(virtual_eye,2);\n\n    if (convexHullUnextended.valid())\n    {\n        min_z_ratio = convexHullUnextended.minRatio(virtual_eye, 2);\n        max_z_ratio = convexHullUnextended.maxRatio(virtual_eye, 2);\n    }\n\n#if 0\n    OSG_NOTICE<<\"convexHull min_x_ratio = \"<<min_x_ratio<<std::endl;\n    OSG_NOTICE<<\"convexHull max_x_ratio = \"<<max_x_ratio<<std::endl;\n    OSG_NOTICE<<\"convexHull min_z_ratio = \"<<min_z_ratio<<std::endl;\n    OSG_NOTICE<<\"convexHull max_z_ratio = \"<<max_z_ratio<<std::endl;\n#endif\n\n    #if 1\n    if (renderStage)\n    {\n#if 1\n        osg::ElapsedTime timer;\n#endif\n\n        RenderLeafTraverser<RenderLeafBounds> rli;\n        rli.set(light_p, virtual_eye, n);\n        rli.traverse(renderStage);\n\n        if (rli.numRenderLeaf==0)\n        {\n            return false;\n        }\n\n#if 0\n        OSG_NOTICE<<\"Time for RenderLeafTraverser \"<<timer.elapsedTime_m()<<\"ms, number of render leaves \"<<rli.numRenderLeaf<<std::endl;\n        OSG_NOTICE<<\"scene bounds min_x=\"<<rli.min_x<<\", max_x=\"<<rli.max_x<<std::endl;\n        OSG_NOTICE<<\"scene bounds min_y=\"<<rli.min_y<<\", max_y=\"<<rli.max_y<<std::endl;\n        OSG_NOTICE<<\"scene bounds min_z=\"<<rli.min_z<<\", max_z=\"<<rli.max_z<<std::endl;\n        OSG_NOTICE<<\"min_x_ratio=\"<<rli.min_x_ratio<<\", max_x_ratio=\"<<rli.max_x_ratio<<std::endl;\n        OSG_NOTICE<<\"min_z_ratio=\"<<rli.min_z_ratio<<\", max_z_ratio=\"<<rli.max_z_ratio<<std::endl;\n#endif\n        if (rli.min_x_ratio>min_x_ratio) min_x_ratio = rli.min_x_ratio;\n        if (rli.max_x_ratio<max_x_ratio) max_x_ratio = rli.max_x_ratio;\n\n        if (min_z_ratio == dbl_max || rli.min_z_ratio > min_z_ratio)\n            min_z_ratio = rli.min_z_ratio;\n        if (max_z_ratio == -dbl_max || rli.max_z_ratio < max_z_ratio)\n            max_z_ratio = rli.max_z_ratio;\n    }\n#endif\n    double best_x_ratio = osg::maximum(fabs(min_x_ratio),fabs(max_x_ratio));\n    double best_z_ratio = osg::maximum(fabs(min_z_ratio),fabs(max_z_ratio));\n\n    //best_z_ratio = osg::maximum(1.0, best_z_ratio);\n#if 0\n    OSG_NOTICE<<\"min_x_ratio = \"<<min_x_ratio<<std::endl;\n    OSG_NOTICE<<\"max_x_ratio = \"<<max_x_ratio<<std::endl;\n    OSG_NOTICE<<\"best_x_ratio = \"<<best_x_ratio<<std::endl;\n    OSG_NOTICE<<\"min_z_ratio = \"<<min_z_ratio<<std::endl;\n    OSG_NOTICE<<\"max_z_ratio = \"<<max_z_ratio<<std::endl;\n    OSG_NOTICE<<\"best_z_ratio = \"<<best_z_ratio<<std::endl;\n#endif\n\n    //best_z_ratio *= 10.0;\n\n    osg::Matrixd lightPerspective( 1.0/best_x_ratio,  0.0, 0.0,  0.0,\n                                   0.0,  a,   0.0,  1.0,\n                                   0.0,  0.0, 1.0/best_z_ratio,  0.0,\n                                   0.0,  b,   0.0,  0.0 );\n    osg::Matrixd light_persp = light_p * lightView * lightPerspective;\n\n#if 0\n    OSG_NOTICE<<\"light_p = \"<<light_p<<std::endl;\n    OSG_NOTICE<<\"lightView = \"<<lightView<<std::endl;\n    OSG_NOTICE<<\"lightPerspective = \"<<lightPerspective<<std::endl;\n    OSG_NOTICE<<\"light_persp result = \"<<light_persp<<std::endl;\n#endif\n    camera->setProjectionMatrix(light_persp);\n\n    return true;\n}\n\nbool MWShadowTechnique::assignTexGenSettings(osgUtil::CullVisitor* cv, osg::Camera* camera, unsigned int textureUnit, osg::TexGen* texgen)\n{\n    OSG_INFO<<\"assignTexGenSettings() textureUnit=\"<<textureUnit<<\" texgen=\"<<texgen<<std::endl;\n\n    texgen->setMode(osg::TexGen::EYE_LINEAR);\n\n    // compute the matrix which takes a vertex from local coords into tex coords\n    // We actually use two matrices one used to define texgen\n    // and second that will be used as modelview when appling to OpenGL\n    texgen->setPlanesFromMatrix( camera->getProjectionMatrix() *\n                                 osg::Matrix::translate(1.0,1.0,1.0) *\n                                 osg::Matrix::scale(0.5,0.5,0.5) );\n\n    // Place texgen with modelview which removes big offsets (making it float friendly)\n    osg::ref_ptr<osg::RefMatrix> refMatrix =\n        new osg::RefMatrix( camera->getInverseViewMatrix() * (*(cv->getModelViewMatrix())) );\n\n    osgUtil::RenderStage* currentStage = cv->getCurrentRenderBin()->getStage();\n    currentStage->getPositionalStateContainer()->addPositionedTextureAttribute( textureUnit, refMatrix.get(), texgen );\n    return true;\n}\n\nvoid MWShadowTechnique::cullShadowReceivingScene(osgUtil::CullVisitor* cv) const\n{\n    OSG_INFO<<\"cullShadowReceivingScene()\"<<std::endl;\n\n    // record the traversal mask on entry so we can reapply it later.\n    unsigned int traversalMask = cv->getTraversalMask();\n\n    cv->setTraversalMask( traversalMask & _shadowedScene->getShadowSettings()->getReceivesShadowTraversalMask() );\n\n    _shadowedScene->osg::Group::traverse(*cv);\n\n    cv->setTraversalMask( traversalMask );\n\n    return;\n}\n\nvoid MWShadowTechnique::cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const\n{\n    OSG_INFO<<\"cullShadowCastingScene()\"<<std::endl;\n\n    // record the traversal mask on entry so we can reapply it later.\n    unsigned int traversalMask = cv->getTraversalMask();\n\n    cv->setTraversalMask( traversalMask & _shadowedScene->getShadowSettings()->getCastsShadowTraversalMask() );\n\n        if (camera) camera->accept(*cv);\n\n    cv->setTraversalMask( traversalMask );\n\n    return;\n}\n\nosg::StateSet* MWShadowTechnique::prepareStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const\n{\n    OSG_INFO<<\"   prepareStateSetForRenderingShadow() \"<<vdd.getStateSet(traversalNumber)<<std::endl;\n\n    osg::ref_ptr<osg::StateSet> stateset = vdd.getStateSet(traversalNumber);\n\n    stateset->clear();\n\n    stateset->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);\n\n    for(const auto& uniform : _uniforms[traversalNumber % 2])\n    {\n        OSG_INFO<<\"addUniform(\"<<uniform->getName()<<\")\"<<std::endl;\n        stateset->addUniform(uniform);\n    }\n\n    if (_program.valid())\n    {\n        stateset->setAttribute(_program.get());\n    }\n\n    LightDataList& pll = vdd.getLightDataList();\n    for(LightDataList::iterator itr = pll.begin();\n        itr != pll.end();\n        ++itr)\n    {\n        // 3. create per light/per shadow map division of lightspace/frustum\n        //    create a list of light/shadow map data structures\n\n        LightData& pl = (**itr);\n\n        // if no texture units have been activated for this light then no shadow state required.\n        if (pl.textureUnits.empty()) continue;\n\n        for(LightData::ActiveTextureUnits::iterator atu_itr = pl.textureUnits.begin();\n            atu_itr != pl.textureUnits.end();\n            ++atu_itr)\n        {\n            OSG_INFO<<\"   Need to assign state for \"<<*atu_itr<<std::endl;\n        }\n\n    }\n\n    const ShadowSettings* settings = getShadowedScene()->getShadowSettings();\n    unsigned int shadowMapModeValue = settings->getUseOverrideForShadowMapTexture() ?\n                                            osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE :\n                                            osg::StateAttribute::ON;\n\n\n    ShadowDataList& sdl = vdd.getShadowDataList();\n    for(ShadowDataList::iterator itr = sdl.begin();\n        itr != sdl.end();\n        ++itr)\n    {\n        // 3. create per light/per shadow map division of lightspace/frustum\n        //    create a list of light/shadow map data structures\n\n        ShadowData& sd = (**itr);\n\n        OSG_INFO<<\"   ShadowData for \"<<sd._textureUnit<<std::endl;\n\n        stateset->setTextureAttributeAndModes(sd._textureUnit, sd._texture.get(), shadowMapModeValue);\n\n        stateset->setTextureMode(sd._textureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);\n        stateset->setTextureMode(sd._textureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);\n        stateset->setTextureMode(sd._textureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);\n        stateset->setTextureMode(sd._textureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);\n    }\n\n    return stateset;\n}\n\nvoid MWShadowTechnique::resizeGLObjectBuffers(unsigned int /*maxSize*/)\n{\n    // the way that ViewDependentData is mapped shouldn't\n}\n\nvoid MWShadowTechnique::releaseGLObjects(osg::State* state) const\n{\n    std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex);\n    for(ViewDependentDataMap::const_iterator itr = _viewDependentDataMap.begin();\n        itr != _viewDependentDataMap.end();\n        ++itr)\n    {\n        ViewDependentData* vdd = itr->second.get();\n        if (vdd)\n        {\n            vdd->releaseGLObjects(state);\n        }\n    }\n    if (_debugHud)\n        _debugHud->releaseGLObjects(state);\n}\n\nclass DoubleBufferCallback : public osg::Callback\n{\npublic:\n    DoubleBufferCallback(osg::NodeList &children) : mChildren(children) {}\n\n    bool run(osg::Object* node, osg::Object* visitor) override\n    {\n        // We can't use a static cast as NodeVisitor virtually inherits from Object\n        osg::ref_ptr<osg::NodeVisitor> nodeVisitor = visitor->asNodeVisitor();\n        unsigned int traversalNumber = nodeVisitor->getTraversalNumber();\n        mChildren[traversalNumber % 2]->accept(*nodeVisitor);\n\n        return true;\n    }\n\nprotected:\n    osg::NodeList mChildren;\n};\n\nSceneUtil::MWShadowTechnique::DebugHUD::DebugHUD(int numberOfShadowMapsPerLight) : mDebugProgram(new osg::Program)\n{\n    osg::ref_ptr<osg::Shader> vertexShader = new osg::Shader(osg::Shader::VERTEX, debugVertexShaderSource);\n    mDebugProgram->addShader(vertexShader);\n    osg::ref_ptr<osg::Shader> fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, debugFragmentShaderSource);\n    mDebugProgram->addShader(fragmentShader);\n\n    osg::ref_ptr<osg::Program> frustumProgram = new osg::Program;\n    vertexShader = new osg::Shader(osg::Shader::VERTEX, debugFrustumVertexShaderSource);\n    frustumProgram->addShader(vertexShader);\n    fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, debugFrustumFragmentShaderSource);\n    frustumProgram->addShader(fragmentShader);\n\n    for (auto& frustumGeometry : mFrustumGeometries)\n    {\n        frustumGeometry = new osg::Geometry();\n        frustumGeometry->setCullingActive(false);\n\n        frustumGeometry->getOrCreateStateSet()->setAttributeAndModes(frustumProgram, osg::StateAttribute::ON);\n    }\n\n    osg::ref_ptr<osg::DrawElementsUShort> frustumDrawElements = new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP);\n    for (auto & geom : mFrustumGeometries)\n        geom->addPrimitiveSet(frustumDrawElements);\n    frustumDrawElements->push_back(0);\n    frustumDrawElements->push_back(1);\n    frustumDrawElements->push_back(2);\n    frustumDrawElements->push_back(3);\n    frustumDrawElements->push_back(0);\n    frustumDrawElements->push_back(4);\n    frustumDrawElements->push_back(5);\n    frustumDrawElements->push_back(6);\n    frustumDrawElements->push_back(7);\n    frustumDrawElements->push_back(4);\n\n    frustumDrawElements = new osg::DrawElementsUShort(osg::PrimitiveSet::LINES);\n    for (auto & geom : mFrustumGeometries)\n        geom->addPrimitiveSet(frustumDrawElements);\n    frustumDrawElements->push_back(1);\n    frustumDrawElements->push_back(5);\n    frustumDrawElements->push_back(2);\n    frustumDrawElements->push_back(6);\n    frustumDrawElements->push_back(3);\n    frustumDrawElements->push_back(7);\n\n    for (int i = 0; i < numberOfShadowMapsPerLight; ++i)\n        addAnotherShadowMap();\n}\n\nvoid SceneUtil::MWShadowTechnique::DebugHUD::draw(osg::ref_ptr<osg::Texture2D> texture, unsigned int shadowMapNumber, const osg::Matrixd &matrix, osgUtil::CullVisitor& cv)\n{\n    // It might be possible to change shadow settings at runtime\n    if (shadowMapNumber > mDebugCameras.size())\n        addAnotherShadowMap();\n    \n    osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet();\n    stateSet->setTextureAttributeAndModes(sDebugTextureUnit, texture, osg::StateAttribute::ON);\n\n    auto frustumUniform = mFrustumUniforms[cv.getTraversalNumber() % 2][shadowMapNumber];\n    frustumUniform->set(matrix);\n    stateSet->addUniform(frustumUniform);\n\n    // Some of these calls may be superfluous.\n    unsigned int traversalMask = cv.getTraversalMask();\n    cv.setTraversalMask(mDebugGeometry[shadowMapNumber]->getNodeMask());\n    cv.pushStateSet(stateSet);\n    mDebugCameras[shadowMapNumber]->accept(cv);\n    cv.popStateSet();\n    cv.setTraversalMask(traversalMask);\n\n    // cv.getState()->setCheckForGLErrors(osg::State::ONCE_PER_ATTRIBUTE);\n}\n\nvoid SceneUtil::MWShadowTechnique::DebugHUD::releaseGLObjects(osg::State* state) const\n{\n    for (auto const& camera : mDebugCameras)\n        camera->releaseGLObjects(state);\n    mDebugProgram->releaseGLObjects(state);\n    for (auto const& node : mDebugGeometry)\n        node->releaseGLObjects(state);\n    for (auto const& node : mFrustumTransforms)\n        node->releaseGLObjects(state);\n    for (auto const& node : mFrustumGeometries)\n        node->releaseGLObjects(state);\n}\n\nvoid SceneUtil::MWShadowTechnique::DebugHUD::setFrustumVertices(osg::ref_ptr<osg::Vec3Array> vertices, unsigned int traversalNumber)\n{\n    mFrustumGeometries[traversalNumber % 2]->setVertexArray(vertices);\n}\n\nvoid SceneUtil::MWShadowTechnique::DebugHUD::addAnotherShadowMap()\n{\n    unsigned int shadowMapNumber = mDebugCameras.size();\n\n    mDebugCameras.push_back(new osg::Camera);\n    mDebugCameras[shadowMapNumber]->setViewport(200 * shadowMapNumber, 0, 200, 200);\n    mDebugCameras[shadowMapNumber]->setRenderOrder(osg::Camera::POST_RENDER);\n    mDebugCameras[shadowMapNumber]->setClearColor(osg::Vec4(1.0, 1.0, 0.0, 1.0));\n    mDebugCameras[shadowMapNumber]->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n\n    mDebugGeometry.emplace_back(osg::createTexturedQuadGeometry(osg::Vec3(-1, -1, 0), osg::Vec3(2, 0, 0), osg::Vec3(0, 2, 0)));\n    mDebugGeometry[shadowMapNumber]->setCullingActive(false);\n    mDebugCameras[shadowMapNumber]->addChild(mDebugGeometry[shadowMapNumber]);\n    osg::ref_ptr<osg::StateSet> stateSet = mDebugGeometry[shadowMapNumber]->getOrCreateStateSet();\n    stateSet->setAttributeAndModes(mDebugProgram, osg::StateAttribute::ON);\n    osg::ref_ptr<osg::Uniform> textureUniform = new osg::Uniform(\"texture\", sDebugTextureUnit);\n    //textureUniform->setType(osg::Uniform::SAMPLER_2D);\n    stateSet->addUniform(textureUniform.get());\n\n    mFrustumTransforms.push_back(new osg::Group);\n    osg::NodeList frustumGeometryNodeList(mFrustumGeometries.cbegin(), mFrustumGeometries.cend());\n    mFrustumTransforms[shadowMapNumber]->setCullCallback(new DoubleBufferCallback(frustumGeometryNodeList));\n    mFrustumTransforms[shadowMapNumber]->setCullingActive(false);\n    mDebugCameras[shadowMapNumber]->addChild(mFrustumTransforms[shadowMapNumber]);\n\n    for(auto& uniformVector : mFrustumUniforms)\n        uniformVector.push_back(new osg::Uniform(osg::Uniform::FLOAT_MAT4, \"transform\"));\n}\n"
  },
  {
    "path": "components/sceneutil/mwshadowtechnique.hpp",
    "content": "/* This file is based on OpenSceneGraph's include/osgShadow/ViewDependentShadowMap.\n * Where applicable, any changes made are covered by OpenMW's GPL 3 license, not the OSGPL.\n * The original copyright notice is listed below.\n */\n\n/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2011 Robert Osfield\n *\n * This library is open source and may be redistributed and/or modified under\n * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or\n * (at your option) any later version.  The full license is in LICENSE file\n * included with this distribution, and on the openscenegraph.org website.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * OpenSceneGraph Public License for more details.\n*/\n\n#ifndef COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H\n#define COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H 1\n\n#include <array>\n#include <mutex>\n\n#include <osg/Camera>\n#include <osg/Material>\n#include <osg/MatrixTransform>\n#include <osg/LightSource>\n#include <osg/PolygonOffset>\n\n#include <osgShadow/ShadowTechnique>\n\n#include <components/shader/shadermanager.hpp>\n#include <components/terrain/quadtreeworld.hpp>\n\nnamespace SceneUtil {\n\n    /** ViewDependentShadowMap provides an base implementation of view dependent shadow mapping techniques.*/\n    class MWShadowTechnique : public osgShadow::ShadowTechnique\n    {\n    public:\n        MWShadowTechnique();\n\n        MWShadowTechnique(const MWShadowTechnique& vdsm, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);\n\n        META_Object(SceneUtil, MWShadowTechnique);\n\n        /** initialize the ShadowedScene and local cached data structures.*/\n        void init() override;\n\n        /** run the update traversal of the ShadowedScene and update any loca chached data structures.*/\n        void update(osg::NodeVisitor& nv) override;\n\n        /** run the cull traversal of the ShadowedScene and set up the rendering for this ShadowTechnique.*/\n        void cull(osgUtil::CullVisitor& cv) override;\n\n        /** Resize any per context GLObject buffers to specified size. */\n        void resizeGLObjectBuffers(unsigned int maxSize) override;\n\n        /** If State is non-zero, this function releases any associated OpenGL objects for\n        * the specified graphics context. Otherwise, releases OpenGL objects\n        * for all graphics contexts. */\n        void releaseGLObjects(osg::State* = 0) const override;\n\n        /** Clean scene graph from any shadow technique specific nodes, state and drawables.*/\n        void cleanSceneGraph() override;\n\n        virtual void enableShadows();\n\n        virtual void disableShadows(bool setDummyState = false);\n\n        virtual void enableDebugHUD();\n\n        virtual void disableDebugHUD();\n\n        virtual void setSplitPointUniformLogarithmicRatio(double ratio);\n\n        virtual void setSplitPointDeltaBias(double bias);\n\n        virtual void setPolygonOffset(float factor, float units);\n\n        virtual void setShadowFadeStart(float shadowFadeStart);\n\n        virtual void enableFrontFaceCulling();\n\n        virtual void disableFrontFaceCulling();\n\n        virtual void setupCastingShader(Shader::ShaderManager &shaderManager);\n\n        class ComputeLightSpaceBounds : public osg::NodeVisitor, public osg::CullStack\n        {\n        public:\n            ComputeLightSpaceBounds(osg::Viewport* viewport, const osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix);\n\n            void apply(osg::Node& node) override;\n\n            void apply(osg::Drawable& drawable) override;\n\n            void apply(Terrain::QuadTreeWorld& quadTreeWorld);\n\n            void apply(osg::Billboard&) override;\n\n            void apply(osg::Projection&) override;\n\n            void apply(osg::Transform& transform) override;\n\n            void apply(osg::Camera&) override;\n\n            using osg::NodeVisitor::apply;\n\n            void updateBound(const osg::BoundingBox& bb);\n\n            void update(const osg::Vec3& v);\n\n            osg::BoundingBox _bb;\n        };\n\n        struct Frustum\n        {\n            Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar);\n\n            osg::Matrixd projectionMatrix;\n            osg::Matrixd modelViewMatrix;\n\n            typedef std::vector<osg::Vec3d> Vertices;\n            Vertices corners;\n\n            typedef std::vector<unsigned int> Indices;\n            typedef std::vector<Indices> Faces;\n            Faces faces;\n\n            typedef std::vector<Indices> Edges;\n            Edges edges;\n\n            osg::Vec3d eye;\n            osg::Vec3d centerNearPlane;\n            osg::Vec3d centerFarPlane;\n            osg::Vec3d center;\n            osg::Vec3d frustumCenterLine;\n        };\n\n        // forward declare\n        class ViewDependentData;\n\n        struct LightData : public osg::Referenced\n        {\n            LightData(ViewDependentData* vdd);\n\n            virtual void setLightData(osg::RefMatrix* lm, const osg::Light* l, const osg::Matrixd& modelViewMatrix);\n\n            ViewDependentData*                  _viewDependentData;\n\n            osg::ref_ptr<osg::RefMatrix>        lightMatrix;\n            osg::ref_ptr<const osg::Light>      light;\n\n            osg::Vec4d                          lightPos;\n            osg::Vec3d                          lightPos3;\n            osg::Vec3d                          lightDir;\n            bool                                directionalLight;\n\n            typedef std::vector<unsigned int> ActiveTextureUnits;\n            ActiveTextureUnits                   textureUnits;\n        };\n\n        typedef std::list< osg::ref_ptr<LightData> > LightDataList;\n\n        struct ShadowData : public osg::Referenced\n        {\n            ShadowData(ViewDependentData* vdd);\n\n            virtual void releaseGLObjects(osg::State* = 0) const;\n\n            ViewDependentData*                  _viewDependentData;\n\n            unsigned int                        _textureUnit;\n            osg::ref_ptr<osg::Texture2D>        _texture;\n            osg::ref_ptr<osg::TexGen>           _texgen;\n            osg::ref_ptr<osg::Camera>           _camera;\n        };\n\n        typedef std::list< osg::ref_ptr<ShadowData> > ShadowDataList;\n\n\n        class ViewDependentData : public osg::Referenced\n        {\n        public:\n            ViewDependentData(MWShadowTechnique* vdsm);\n\n            const MWShadowTechnique* getViewDependentShadowMap() const { return _viewDependentShadowMap; }\n\n            LightDataList& getLightDataList() { return _lightDataList; }\n\n            ShadowDataList& getShadowDataList() { return _shadowDataList; }\n\n            osg::StateSet* getStateSet(unsigned int traversalNumber) { return _stateset[traversalNumber % 2].get(); }\n\n            virtual void releaseGLObjects(osg::State* = 0) const;\n\n        protected:\n            virtual ~ViewDependentData() {}\n\n            MWShadowTechnique*          _viewDependentShadowMap;\n\n            std::array<osg::ref_ptr<osg::StateSet>, 2> _stateset;\n\n            LightDataList               _lightDataList;\n            ShadowDataList              _shadowDataList;\n        };\n\n        virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv);\n\n        ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv);\n\n\n\n        virtual void createShaders();\n\n        virtual std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER + 1> getCastingPrograms() const { return _castingPrograms; }\n\n        virtual bool selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const;\n\n        virtual osg::Polytope computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight);\n\n        virtual bool computeShadowCameraSettings(Frustum& frustum, LightData& positionedLight, osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix);\n\n        virtual bool cropShadowCameraToMainFrustum(Frustum& frustum, osg::Camera* camera, double viewNear, double viewFar, std::vector<osg::Plane>& planeList);\n\n        virtual bool adjustPerspectiveShadowMapCameraSettings(osgUtil::RenderStage* renderStage, Frustum& frustum, LightData& positionedLight, osg::Camera* camera, double viewNear, double viewFar);\n\n        virtual bool assignTexGenSettings(osgUtil::CullVisitor* cv, osg::Camera* camera, unsigned int textureUnit, osg::TexGen* texgen);\n\n        virtual void cullShadowReceivingScene(osgUtil::CullVisitor* cv) const;\n\n        virtual void cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const;\n\n        virtual osg::StateSet* prepareStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const;\n\n    protected:\n        virtual ~MWShadowTechnique();\n\n        typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr<ViewDependentData> >  ViewDependentDataMap;\n        mutable std::mutex                      _viewDependentDataMapMutex;\n        ViewDependentDataMap                    _viewDependentDataMap;\n\n        osg::ref_ptr<osg::StateSet>             _shadowRecievingPlaceholderStateSet;\n\n        osg::ref_ptr<osg::StateSet>             _shadowCastingStateSet;\n        osg::ref_ptr<osg::PolygonOffset>        _polygonOffset;\n        osg::ref_ptr<osg::Texture2D>            _fallbackBaseTexture;\n        osg::ref_ptr<osg::Texture2D>            _fallbackShadowMapTexture;\n\n        typedef std::vector< osg::ref_ptr<osg::Uniform> > Uniforms;\n        std::array<Uniforms, 2>                 _uniforms;\n        osg::ref_ptr<osg::Program>              _program;\n\n        bool                                    _enableShadows;\n        bool                                    mSetDummyStateWhenDisabled;\n\n        double                                  _splitPointUniformLogRatio = 0.5;\n        double                                  _splitPointDeltaBias = 0.0;\n\n        float                                   _polygonOffsetFactor = 1.1;\n        float                                   _polygonOffsetUnits = 4.0;\n\n        bool                                    _useFrontFaceCulling = true;\n\n        float                                   _shadowFadeStart = 0.0;\n\n        class DebugHUD final : public osg::Referenced\n        {\n        public:\n            DebugHUD(int numberOfShadowMapsPerLight);\n\n            void draw(osg::ref_ptr<osg::Texture2D> texture, unsigned int shadowMapNumber, const osg::Matrixd &matrix, osgUtil::CullVisitor& cv);\n\n            void releaseGLObjects(osg::State* state = 0) const;\n\n            void setFrustumVertices(osg::ref_ptr<osg::Vec3Array> vertices, unsigned int traversalNumber);\n        protected:\n            void addAnotherShadowMap();\n\n            static const int sDebugTextureUnit = 0;\n\n            std::vector<osg::ref_ptr<osg::Camera>> mDebugCameras;\n            osg::ref_ptr<osg::Program> mDebugProgram;\n            std::vector<osg::ref_ptr<osg::Node>> mDebugGeometry;\n            std::vector<osg::ref_ptr<osg::Group>> mFrustumTransforms;\n            std::array<std::vector<osg::ref_ptr<osg::Uniform>>, 2> mFrustumUniforms;\n            std::array<osg::ref_ptr<osg::Geometry>, 2> mFrustumGeometries;\n        };\n\n        osg::ref_ptr<DebugHUD>                  _debugHud;\n        std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER + 1> _castingPrograms;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/navmesh.cpp",
    "content": "#include \"navmesh.hpp\"\n#include \"detourdebugdraw.hpp\"\n\n#include <components/detournavigator/settings.hpp>\n\n#include <DetourDebugDraw.h>\n\n#include <osg/Group>\n\nnamespace SceneUtil\n{\n    osg::ref_ptr<osg::Group> createNavMeshGroup(const dtNavMesh& navMesh, const DetourNavigator::Settings& settings)\n    {\n        const osg::ref_ptr<osg::Group> group(new osg::Group);\n        DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 10), 1.0f / settings.mRecastScaleFactor);\n        dtNavMeshQuery navMeshQuery;\n        navMeshQuery.init(&navMesh, settings.mMaxNavMeshQueryNodes);\n        duDebugDrawNavMeshWithClosedList(&debugDraw, navMesh, navMeshQuery,\n                                         DU_DRAWNAVMESH_OFFMESHCONS | DU_DRAWNAVMESH_CLOSEDLIST);\n        return group;\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/navmesh.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_NAVMESH_H\n#define OPENMW_COMPONENTS_SCENEUTIL_NAVMESH_H\n\n#include <osg/ref_ptr>\n\nclass dtNavMesh;\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace DetourNavigator\n{\n    struct Settings;\n}\n\nnamespace SceneUtil\n{\n    osg::ref_ptr<osg::Group> createNavMeshGroup(const dtNavMesh& navMesh, const DetourNavigator::Settings& settings);\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/optimizer.cpp",
    "content": "/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield\n *\n * This library is open source and may be redistributed and/or modified under\n * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or\n * (at your option) any later version.  The full license is in LICENSE file\n * included with this distribution, and on the openscenegraph.org website.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * OpenSceneGraph Public License for more details.\n*/\n\n/* Modified for OpenMW */\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"optimizer.hpp\"\n\n#include <osg/Version>\n#include <osg/Transform>\n#include <osg/MatrixTransform>\n#include <osg/PositionAttitudeTransform>\n#include <osg/LOD>\n#include <osg/Billboard>\n#include <osg/Geometry>\n#include <osg/Notify>\n#include <osg/Timer>\n#include <osg/io_utils>\n#include <osg/Depth>\n\n#include <osgUtil/TransformAttributeFunctor>\n#include <osgUtil/Statistics>\n#include <osgUtil/MeshOptimizers>\n\n#include <typeinfo>\n#include <algorithm>\n#include <numeric>\n\n#include <iterator>\n\nusing namespace osgUtil;\n\nnamespace SceneUtil\n{\n\nvoid Optimizer::reset()\n{\n}\n\nvoid Optimizer::optimize(osg::Node* node, unsigned int options)\n{\n    StatsVisitor stats;\n\n    if (osg::getNotifyLevel()>=osg::INFO)\n    {\n        node->accept(stats);\n        stats.totalUpStats();\n        OSG_NOTICE<<std::endl<<\"Stats before:\"<<std::endl;\n        stats.print(osg::notify(osg::NOTICE));\n    }\n\n    if (options & FLATTEN_STATIC_TRANSFORMS)\n    {\n        OSG_INFO<<\"Optimizer::optimize() doing FLATTEN_STATIC_TRANSFORMS\"<<std::endl;\n\n        int i=0;\n        bool result = false;\n        do\n        {\n            OSG_DEBUG << \"** RemoveStaticTransformsVisitor *** Pass \"<<i<<std::endl;\n            FlattenStaticTransformsVisitor fstv(this);\n            node->accept(fstv);\n            result = fstv.removeTransforms(node);\n            ++i;\n        } while (result);\n\n        // now combine any adjacent static transforms.\n        CombineStaticTransformsVisitor cstv(this);\n        node->accept(cstv);\n        cstv.removeTransforms(node);\n    }\n\n    if (options & REMOVE_REDUNDANT_NODES)\n    {\n        OSG_INFO<<\"Optimizer::optimize() doing REMOVE_REDUNDANT_NODES\"<<std::endl;\n\n        RemoveEmptyNodesVisitor renv(this);\n        node->accept(renv);\n        renv.removeEmptyNodes();\n\n        RemoveRedundantNodesVisitor rrnv(this);\n        node->accept(rrnv);\n        rrnv.removeRedundantNodes();\n\n        MergeGroupsVisitor mgrp(this);\n        node->accept(mgrp);\n    }\n\n    if (options & MERGE_GEOMETRY)\n    {\n        OSG_INFO<<\"Optimizer::optimize() doing MERGE_GEOMETRY\"<<std::endl;\n\n        osg::Timer_t startTick = osg::Timer::instance()->tick();\n\n        MergeGeometryVisitor mgv(this);\n        mgv.setTargetMaximumNumberOfVertices(1000000);\n        mgv.setMergeAlphaBlending(_mergeAlphaBlending);\n        mgv.setViewPoint(_viewPoint);\n        node->accept(mgv);\n\n        osg::Timer_t endTick = osg::Timer::instance()->tick();\n\n        OSG_INFO<<\"MERGE_GEOMETRY took \"<<osg::Timer::instance()->delta_s(startTick,endTick)<<std::endl;\n    }\n\n    if (options & VERTEX_POSTTRANSFORM)\n    {\n        OSG_INFO<<\"Optimizer::optimize() doing VERTEX_POSTTRANSFORM\"<<std::endl;\n        VertexCacheVisitor vcv;\n        node->accept(vcv);\n        vcv.optimizeVertices();\n    }\n\n    if (options & VERTEX_PRETRANSFORM)\n    {\n        OSG_INFO<<\"Optimizer::optimize() doing VERTEX_PRETRANSFORM\"<<std::endl;\n        VertexAccessOrderVisitor vaov;\n        node->accept(vaov);\n        vaov.optimizeOrder();\n    }\n\n    if (osg::getNotifyLevel()>=osg::INFO)\n    {\n        stats.reset();\n        node->accept(stats);\n        stats.totalUpStats();\n        OSG_NOTICE<<std::endl<<\"Stats after:\"<<std::endl;\n        stats.print(osg::notify(osg::NOTICE));\n    }\n}\n\n\n\n\n////////////////////////////////////////////////////////////////////////////\n// Flatten static transforms\n////////////////////////////////////////////////////////////////////////////\n\nclass CollectLowestTransformsVisitor : public BaseOptimizerVisitor\n{\n    public:\n\n\n        CollectLowestTransformsVisitor(Optimizer* optimizer=0):\n                    BaseOptimizerVisitor(optimizer,Optimizer::FLATTEN_STATIC_TRANSFORMS),\n                    _transformFunctor(osg::Matrix())\n        {\n            setTraversalMode(osg::NodeVisitor::TRAVERSE_PARENTS);\n        }\n\n        void apply(osg::Node& node) override\n        {\n            if (node.getNumParents())\n            {\n                traverse(node);\n            }\n            else\n            {\n                // for all current objects mark a nullptr transform for them.\n                registerWithCurrentObjects(0);\n            }\n        }\n\n        void apply(osg::LOD& lod) override\n        {\n            _currentObjectList.push_back(&lod);\n\n            traverse(lod);\n\n            _currentObjectList.pop_back();\n        }\n\n        void apply(osg::Transform& transform) override\n        {\n            // for all current objects associated this transform with them.\n            registerWithCurrentObjects(&transform);\n        }\n\n        void apply(osg::Geode& geode) override\n        {\n            traverse(geode);\n        }\n\n        void apply(osg::Billboard& geode) override\n        {\n            traverse(geode);\n        }\n\n        void collectDataFor(osg::Node* node)\n        {\n            _currentObjectList.push_back(node);\n\n            node->accept(*this);\n\n            _currentObjectList.pop_back();\n        }\n\n        void collectDataFor(osg::Billboard* billboard)\n        {\n            _currentObjectList.push_back(billboard);\n\n            billboard->accept(*this);\n\n            _currentObjectList.pop_back();\n        }\n\n        void collectDataFor(osg::Drawable* drawable)\n        {\n            _currentObjectList.push_back(drawable);\n\n            const osg::Drawable::ParentList& parents = drawable->getParents();\n            for(osg::Drawable::ParentList::const_iterator itr=parents.begin();\n                itr!=parents.end();\n                ++itr)\n            {\n                (*itr)->accept(*this);\n            }\n\n            _currentObjectList.pop_back();\n        }\n\n        void setUpMaps();\n        void disableTransform(osg::Transform* transform);\n        bool removeTransforms(osg::Node* nodeWeCannotRemove);\n\n        inline bool isOperationPermissibleForObject(const osg::Object* object) const\n        {\n            const osg::Node* node = object->asNode();\n            if (node)\n            {\n                const osg::Drawable* drawable = node->asDrawable();\n                if (drawable)\n                     return isOperationPermissibleForObject(drawable);\n                else\n                    return isOperationPermissibleForObject(node);\n            }\n            return true;\n        }\n\n        inline bool isOperationPermissibleForObject(const osg::Drawable* drawable) const\n        {\n            return BaseOptimizerVisitor::isOperationPermissibleForObject(drawable);\n        }\n\n        inline bool isOperationPermissibleForObject(const osg::Node* node) const\n        {\n            return BaseOptimizerVisitor::isOperationPermissibleForObject(node);\n        }\n\n    protected:\n\n        struct TransformStruct\n        {\n            typedef std::set<osg::Object*> ObjectSet;\n\n            TransformStruct():_canBeApplied(true) {}\n\n            void add(osg::Object* obj)\n            {\n                _objectSet.insert(obj);\n            }\n\n            bool        _canBeApplied;\n            ObjectSet   _objectSet;\n        };\n\n        struct ObjectStruct\n        {\n            typedef std::set<osg::Transform*> TransformSet;\n\n            ObjectStruct():_canBeApplied(true),_moreThanOneMatrixRequired(false) {}\n\n            void add(osg::Transform* transform, bool canOptimize)\n            {\n                if (transform)\n                {\n                    if (!canOptimize) _moreThanOneMatrixRequired=true;\n                    else if (transform->getReferenceFrame()!=osg::Transform::RELATIVE_RF) _moreThanOneMatrixRequired=true;\n                    else\n                    {\n                        if (_transformSet.empty()) transform->computeLocalToWorldMatrix(_firstMatrix,0);\n                        else\n                        {\n                            osg::Matrix matrix;\n                            transform->computeLocalToWorldMatrix(matrix,0);\n                            if (_firstMatrix!=matrix) _moreThanOneMatrixRequired=true;\n                        }\n                    }\n                }\n                else\n                {\n                    if (!_transformSet.empty())\n                    {\n                        if (!_firstMatrix.isIdentity()) _moreThanOneMatrixRequired=true;\n                    }\n\n                }\n                _transformSet.insert(transform);\n            }\n\n            bool            _canBeApplied;\n            bool            _moreThanOneMatrixRequired;\n            osg::Matrix     _firstMatrix;\n            TransformSet    _transformSet;\n        };\n\n\n        void registerWithCurrentObjects(osg::Transform* transform)\n        {\n            for(ObjectList::iterator itr=_currentObjectList.begin();\n                itr!=_currentObjectList.end();\n                ++itr)\n            {\n                _objectMap[*itr].add(transform, transform && isOperationPermissibleForObject(transform));\n            }\n        }\n\n        typedef std::map<osg::Transform*,TransformStruct>   TransformMap;\n        typedef std::map<osg::Object*,ObjectStruct>         ObjectMap;\n        typedef std::vector<osg::Object*>                   ObjectList;\n\n        void disableObject(osg::Object* object)\n        {\n            disableObject(_objectMap.find(object));\n        }\n\n        void disableObject(ObjectMap::iterator itr);\n        void doTransform(osg::Object* obj,osg::Matrix& matrix);\n\n        osgUtil::TransformAttributeFunctor _transformFunctor;\n        TransformMap    _transformMap;\n        ObjectMap       _objectMap;\n        ObjectList      _currentObjectList;\n\n};\n\n\nvoid CollectLowestTransformsVisitor::doTransform(osg::Object* obj,osg::Matrix& matrix)\n{\n    osg::Node* node = obj->asNode();\n    if (!node)\n        return;\n    osg::Drawable* drawable = node->asDrawable();\n    if (drawable)\n    {\n        osgUtil::TransformAttributeFunctor tf(matrix);\n        drawable->accept(tf);\n\n        osg::Geometry *geom = drawable->asGeometry();\n        osg::Vec4Array* tangents = geom ? dynamic_cast<osg::Vec4Array*>(geom->getTexCoordArray(7)) : nullptr;\n        if (tangents)\n        {\n            for (unsigned int i=0; i<tangents->size(); ++i)\n            {\n                osg::Vec4f& itr = (*tangents)[i];\n                osg::Vec3f vec3 (itr.x(), itr.y(), itr.z());\n                vec3 = osg::Matrix::transform3x3(tf._im, vec3);\n                vec3.normalize();\n                itr = osg::Vec4f(vec3.x(), vec3.y(), vec3.z(), itr.w());\n            }\n        }\n\n        drawable->dirtyBound();\n        drawable->dirtyDisplayList();\n\n        return;\n    }\n\n    osg::LOD* lod = dynamic_cast<osg::LOD*>(obj);\n    if (lod)\n    {\n        osg::Matrix matrix_no_trans = matrix;\n        matrix_no_trans.setTrans(0.0f,0.0f,0.0f);\n\n        osg::Vec3 v111(1.0f,1.0f,1.0f);\n        osg::Vec3 new_v111 = v111*matrix_no_trans;\n        float ratio = new_v111.length()/v111.length();\n\n        // move center point.\n        lod->setCenter(lod->getCenter()*matrix);\n\n        // adjust ranges to new scale.\n        for(unsigned int i=0;i<lod->getNumRanges();++i)\n        {\n            lod->setRange(i,lod->getMinRange(i)*ratio,lod->getMaxRange(i)*ratio);\n        }\n\n        lod->dirtyBound();\n        return;\n    }\n\n    osg::Billboard* billboard = dynamic_cast<osg::Billboard*>(obj);\n    if (billboard)\n    {\n        osg::Matrix matrix_no_trans = matrix;\n        matrix_no_trans.setTrans(0.0f,0.0f,0.0f);\n\n        osgUtil::TransformAttributeFunctor tf(matrix_no_trans);\n\n        osg::Vec3 axis = osg::Matrix::transform3x3(tf._im,billboard->getAxis());\n        axis.normalize();\n        billboard->setAxis(axis);\n\n        osg::Vec3 normal = osg::Matrix::transform3x3(tf._im,billboard->getNormal());\n        normal.normalize();\n        billboard->setNormal(normal);\n\n\n        for(unsigned int i=0;i<billboard->getNumDrawables();++i)\n        {\n            billboard->setPosition(i,billboard->getPosition(i)*matrix);\n            billboard->getDrawable(i)->accept(tf);\n            billboard->getDrawable(i)->dirtyBound();\n        }\n\n        billboard->dirtyBound();\n\n        return;\n    }\n}\n\nvoid CollectLowestTransformsVisitor::disableObject(ObjectMap::iterator itr)\n{\n    if (itr==_objectMap.end())\n    {\n        return;\n    }\n\n    if (itr->second._canBeApplied)\n    {\n        // we haven't been disabled yet so we need to disable,\n        itr->second._canBeApplied = false;\n\n        // and then inform everybody we have been disabled.\n        for(ObjectStruct::TransformSet::iterator titr = itr->second._transformSet.begin();\n            titr != itr->second._transformSet.end();\n            ++titr)\n        {\n            disableTransform(*titr);\n        }\n    }\n}\n\nvoid CollectLowestTransformsVisitor::disableTransform(osg::Transform* transform)\n{\n    TransformMap::iterator itr=_transformMap.find(transform);\n    if (itr==_transformMap.end())\n    {\n        return;\n    }\n\n    if (itr->second._canBeApplied)\n    {\n\n        // we haven't been disabled yet so we need to disable,\n        itr->second._canBeApplied = false;\n        // and then inform everybody we have been disabled.\n        for(TransformStruct::ObjectSet::iterator oitr = itr->second._objectSet.begin();\n            oitr != itr->second._objectSet.end();\n            ++oitr)\n        {\n            disableObject(*oitr);\n        }\n    }\n}\n\nvoid CollectLowestTransformsVisitor::setUpMaps()\n{\n    // create the TransformMap from the ObjectMap\n    ObjectMap::iterator oitr;\n    for(oitr=_objectMap.begin();\n        oitr!=_objectMap.end();\n        ++oitr)\n    {\n        osg::Object* object = oitr->first;\n        ObjectStruct& os = oitr->second;\n\n        for(ObjectStruct::TransformSet::iterator titr = os._transformSet.begin();\n            titr != os._transformSet.end();\n            ++titr)\n        {\n            _transformMap[*titr].add(object);\n        }\n    }\n\n    // disable all the objects which have more than one matrix associated\n    // with them, and then disable all transforms which have an object associated\n    // them that can't be applied, and then disable all objects which have\n    // disabled transforms associated, recursing until all disabled\n    // associativity.\n    // and disable all objects that the operation is not permisable for)\n    for(oitr=_objectMap.begin();\n        oitr!=_objectMap.end();\n        ++oitr)\n    {\n        osg::Object* object = oitr->first;\n        ObjectStruct& os = oitr->second;\n        if (os._canBeApplied)\n        {\n            if (os._moreThanOneMatrixRequired || !isOperationPermissibleForObject(object))\n            {\n                disableObject(oitr);\n            }\n        }\n    }\n\n}\n\nbool CollectLowestTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)\n{\n    // transform the objects that can be applied.\n    for(ObjectMap::iterator oitr=_objectMap.begin();\n        oitr!=_objectMap.end();\n        ++oitr)\n    {\n        osg::Object* object = oitr->first;\n        ObjectStruct& os = oitr->second;\n        if (os._canBeApplied)\n        {\n            doTransform(object,os._firstMatrix);\n        }\n    }\n\n\n    bool transformRemoved = false;\n\n    // clean up the transforms.\n    for(TransformMap::iterator titr=_transformMap.begin();\n        titr!=_transformMap.end();\n        ++titr)\n    {\n        if (titr->first!=0 && titr->second._canBeApplied)\n        {\n            if (titr->first!=nodeWeCannotRemove)\n            {\n                transformRemoved = true;\n\n                osg::ref_ptr<osg::Transform> transform = titr->first;\n                osg::ref_ptr<osg::Group>     group = new osg::Group;\n                group->setName( transform->getName() );\n                group->setDataVariance(osg::Object::STATIC);\n                group->setNodeMask(transform->getNodeMask());\n                group->setStateSet(transform->getStateSet());\n                group->setUpdateCallback(transform->getUpdateCallback());\n                group->setEventCallback(transform->getEventCallback());\n                group->setCullCallback(transform->getCullCallback());\n                group->setUserDataContainer(transform->getUserDataContainer());\n                group->setDescriptions(transform->getDescriptions());\n                for(unsigned int i=0;i<transform->getNumChildren();++i)\n                {\n                    group->addChild(transform->getChild(i));\n                }\n\n                for(int i2=transform->getNumParents()-1;i2>=0;--i2)\n                {\n                    transform->getParent(i2)->replaceChild(transform.get(),group.get());\n                }\n            }\n            else\n            {\n                osg::MatrixTransform* mt = titr->first->asMatrixTransform();\n                if (mt) mt->setMatrix(osg::Matrix::identity());\n                else\n                {\n                    osg::PositionAttitudeTransform* pat = titr->first->asPositionAttitudeTransform();\n                    if (pat)\n                    {\n                        pat->setPosition(osg::Vec3(0.0f,0.0f,0.0f));\n                        pat->setAttitude(osg::Quat());\n                        pat->setPivotPoint(osg::Vec3(0.0f,0.0f,0.0f));\n                    }\n                    else\n                    {\n                        OSG_WARN<<\"Warning:: during Optimize::CollectLowestTransformsVisitor::removeTransforms(Node*)\"<<std::endl;\n                        OSG_WARN<<\"          unhandled of setting of indentity matrix on \"<< titr->first->className()<<std::endl;\n                        OSG_WARN<<\"          model will appear in the incorrect position.\"<<std::endl;\n                    }\n                }\n\n            }\n        }\n    }\n    _objectMap.clear();\n    _transformMap.clear();\n\n    return transformRemoved;\n}\n\nvoid Optimizer::FlattenStaticTransformsVisitor::apply(osg::Node& node)\n{\n    traverse(node);\n}\n\nbool needvbo(const osg::Geometry* geom)\n{\n#if OSG_MIN_VERSION_REQUIRED(3,5,6)\n    return true;\n#else\n    return geom->getUseVertexBufferObjects();\n#endif\n}\n\nosg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom)\n{\n    array = static_cast<osg::Array*>(array->clone(osg::CopyOp::DEEP_COPY_ALL));\n    if (!vbo && needvbo(geom))\n        vbo = new osg::VertexBufferObject;\n    if (vbo)\n        array->setVertexBufferObject(vbo);\n    return array;\n}\n\nvoid Optimizer::FlattenStaticTransformsVisitor::apply(osg::Drawable& drawable)\n{\n    osg::Geometry *geometry = drawable.asGeometry();\n    if((geometry) && (isOperationPermissibleForObject(&drawable)))\n    {\n        osg::VertexBufferObject* vbo = nullptr;\n        if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1)\n            geometry->setVertexArray(cloneArray(geometry->getVertexArray(), vbo, geometry));\n        if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1)\n            geometry->setNormalArray(cloneArray(geometry->getNormalArray(), vbo, geometry));\n        if(geometry->getTexCoordArray(7) && geometry->getTexCoordArray(7)->referenceCount() > 1) // tangents\n            geometry->setTexCoordArray(7, cloneArray(geometry->getTexCoordArray(7), vbo, geometry));\n    }\n    _drawableSet.insert(&drawable);\n}\n\nvoid Optimizer::FlattenStaticTransformsVisitor::apply(osg::Billboard& billboard)\n{\n    if (!_transformStack.empty())\n    {\n        _billboardSet.insert(&billboard);\n    }\n}\n\nvoid Optimizer::FlattenStaticTransformsVisitor::apply(osg::Transform& transform)\n{\n    if (!_transformStack.empty())\n    {\n        // we need to disable any transform higher in the list.\n        _transformSet.insert(_transformStack.back());\n    }\n\n    _transformStack.push_back(&transform);\n\n    // simple traverse the children as if this Transform didn't exist.\n    traverse(transform);\n\n    _transformStack.pop_back();\n}\n\nbool Optimizer::FlattenStaticTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)\n{\n    CollectLowestTransformsVisitor cltv(_optimizer);\n\n    for(NodeSet::iterator nitr=_excludedNodeSet.begin();\n        nitr!=_excludedNodeSet.end();\n        ++nitr)\n    {\n        cltv.collectDataFor(*nitr);\n    }\n\n    for(DrawableSet::iterator ditr=_drawableSet.begin();\n        ditr!=_drawableSet.end();\n        ++ditr)\n    {\n        cltv.collectDataFor(*ditr);\n    }\n\n    for(BillboardSet::iterator bitr=_billboardSet.begin();\n        bitr!=_billboardSet.end();\n        ++bitr)\n    {\n        cltv.collectDataFor(*bitr);\n    }\n\n    cltv.setUpMaps();\n\n    for(TransformSet::iterator titr=_transformSet.begin();\n        titr!=_transformSet.end();\n        ++titr)\n    {\n        cltv.disableTransform(*titr);\n    }\n\n\n    return cltv.removeTransforms(nodeWeCannotRemove);\n}\n\n////////////////////////////////////////////////////////////////////////////\n// CombineStaticTransforms\n////////////////////////////////////////////////////////////////////////////\n\nvoid Optimizer::CombineStaticTransformsVisitor::apply(osg::MatrixTransform& transform)\n{\n    if (transform.getDataVariance()==osg::Object::STATIC &&\n        transform.getNumChildren()==1 &&\n        transform.getChild(0)->asTransform()!=0 &&\n        transform.getChild(0)->asTransform()->asMatrixTransform()!=0 &&\n        transform.getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC &&\n        isOperationPermissibleForObject(&transform) && isOperationPermissibleForObject(transform.getChild(0)))\n    {\n        _transformSet.insert(&transform);\n    }\n\n    traverse(transform);\n}\n\nbool Optimizer::CombineStaticTransformsVisitor::removeTransforms(osg::Node* nodeWeCannotRemove)\n{\n    if (nodeWeCannotRemove && nodeWeCannotRemove->asTransform()!=0 && nodeWeCannotRemove->asTransform()->asMatrixTransform()!=0)\n    {\n        // remove topmost node from transform set if it exists there.\n        TransformSet::iterator itr = _transformSet.find(nodeWeCannotRemove->asTransform()->asMatrixTransform());\n        if (itr!=_transformSet.end()) _transformSet.erase(itr);\n    }\n\n    bool transformRemoved = false;\n\n    while (!_transformSet.empty())\n    {\n        // get the first available transform to combine.\n        osg::ref_ptr<osg::MatrixTransform> transform = *_transformSet.begin();\n        _transformSet.erase(_transformSet.begin());\n\n        if (transform->getNumChildren()==1 &&\n            transform->getChild(0)->asTransform()!=0 &&\n            transform->getChild(0)->asTransform()->asMatrixTransform()!=0 &&\n            transform->getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC)\n        {\n            // now combine with its child.\n            osg::MatrixTransform* child = transform->getChild(0)->asTransform()->asMatrixTransform();\n\n            osg::Matrix newMatrix = child->getMatrix()*transform->getMatrix();\n            child->setMatrix(newMatrix);\n            if (transform->getStateSet())\n            {\n                if(child->getStateSet()) child->getStateSet()->merge(*transform->getStateSet());\n                else child->setStateSet(transform->getStateSet());\n            }\n\n            transformRemoved = true;\n\n            osg::Node::ParentList parents = transform->getParents();\n            for(osg::Node::ParentList::iterator pitr=parents.begin();\n                pitr!=parents.end();\n                ++pitr)\n            {\n                (*pitr)->replaceChild(transform.get(),child);\n            }\n\n        }\n\n    }\n    return transformRemoved;\n}\n\n////////////////////////////////////////////////////////////////////////////\n// RemoveEmptyNodes.\n////////////////////////////////////////////////////////////////////////////\n\nvoid Optimizer::RemoveEmptyNodesVisitor::apply(osg::Group& group)\n{\n    if (group.getNumParents()>0)\n    {\n        // only remove empty groups, but not empty occluders.\n        if (group.getNumChildren()==0 && isOperationPermissibleForObject(&group) &&\n            (typeid(group)==typeid(osg::Group) || (group.asTransform())) &&\n            (group.getNumChildrenRequiringUpdateTraversal()==0 && group.getNumChildrenRequiringEventTraversal()==0) )\n        {\n            _redundantNodeList.insert(&group);\n        }\n    }\n    traverse(group);\n}\n\nvoid Optimizer::RemoveEmptyNodesVisitor::removeEmptyNodes()\n{\n\n    NodeList newEmptyGroups;\n\n    // keep iterator through until scene graph is cleaned of empty nodes.\n    while (!_redundantNodeList.empty())\n    {\n        for(NodeList::iterator itr=_redundantNodeList.begin();\n            itr!=_redundantNodeList.end();\n            ++itr)\n        {\n\n            osg::ref_ptr<osg::Node> nodeToRemove = (*itr);\n\n            // take a copy of parents list since subsequent removes will modify the original one.\n            osg::Node::ParentList parents = nodeToRemove->getParents();\n\n            for(osg::Node::ParentList::iterator pitr=parents.begin();\n                pitr!=parents.end();\n                ++pitr)\n            {\n                osg::Group* parent = *pitr;\n                if (!parent->asSwitch() && !dynamic_cast<osg::LOD*>(parent))\n                {\n                    parent->removeChild(nodeToRemove.get());\n                    if (parent->getNumChildren()==0 && isOperationPermissibleForObject(parent)) newEmptyGroups.insert(parent);\n                }\n            }\n        }\n\n        _redundantNodeList.clear();\n        _redundantNodeList.swap(newEmptyGroups);\n    }\n}\n\n\n////////////////////////////////////////////////////////////////////////////\n// RemoveRedundantNodes.\n////////////////////////////////////////////////////////////////////////////\n\nbool Optimizer::RemoveRedundantNodesVisitor::isOperationPermissible(osg::Node& node)\n{\n    return node.getNumParents()>0 &&\n           !node.getStateSet() &&\n           !node.getCullCallback() &&\n           !node.getEventCallback() &&\n           !node.getUpdateCallback() &&\n           isOperationPermissibleForObject(&node);\n}\n\nvoid Optimizer::RemoveRedundantNodesVisitor::apply(osg::LOD& lod)\n{\n    // don't remove any direct children of the LOD because they are used to define each LOD level.\n    for (unsigned int i=0; i<lod.getNumChildren(); ++i)\n        traverse(*lod.getChild(i));\n}\n\nvoid Optimizer::RemoveRedundantNodesVisitor::apply(osg::Switch& switchNode)\n{\n    // We should keep all switch child nodes since they reflect different switch states.\n    for (unsigned int i=0; i<switchNode.getNumChildren(); ++i)\n        traverse(*switchNode.getChild(i));\n}\n\nvoid Optimizer::RemoveRedundantNodesVisitor::apply(osg::Group& group)\n{\n    if (typeid(group)==typeid(osg::Group) &&\n        isOperationPermissible(group))\n    {\n        _redundantNodeList.insert(&group);\n    }\n\n    traverse(group);\n}\n\n\n\nvoid Optimizer::RemoveRedundantNodesVisitor::apply(osg::Transform& transform)\n{\n    if (transform.getDataVariance()==osg::Object::STATIC &&\n        isOperationPermissible(transform))\n    {\n        osg::Matrix matrix;\n        transform.computeWorldToLocalMatrix(matrix,nullptr);\n        if (matrix.isIdentity())\n        {\n            _redundantNodeList.insert(&transform);\n        }\n    }\n    traverse(transform);\n}\n\n\nvoid Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes()\n{\n\n    for(NodeList::iterator itr=_redundantNodeList.begin();\n        itr!=_redundantNodeList.end();\n        ++itr)\n    {\n        osg::ref_ptr<osg::Group> group = (*itr)->asGroup();\n        if (group.valid())\n        {\n            // take a copy of parents list since subsequent removes will modify the original one.\n            osg::Node::ParentList parents = group->getParents();\n\n            for(osg::Node::ParentList::iterator pitr=parents.begin();\n                pitr!=parents.end();\n                ++pitr)\n            {\n                unsigned int childIndex = (*pitr)->getChildIndex(group);\n                for (unsigned int i=0; i<group->getNumChildren(); ++i)\n                {\n                    osg::Node* child = group->getChild(i);\n                    (*pitr)->insertChild(childIndex++, child);\n                }\n\n                (*pitr)->removeChild(group);\n            }\n\n            group->removeChildren(0, group->getNumChildren());\n        }\n        else\n        {\n            OSG_WARN<<\"Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes() - failed dynamic_cast\"<<std::endl;\n        }\n    }\n    _redundantNodeList.clear();\n}\n\n\n\n////////////////////////////////////////////////////////////////////////////\n// code to merge geometry object which share, state, and attribute bindings.\n////////////////////////////////////////////////////////////////////////////\n\n#define COMPARE_BINDING(lhs, rhs) \\\n        if (osg::getBinding(lhs)<osg::getBinding(rhs)) return true; \\\n        if (osg::getBinding(rhs)<osg::getBinding(lhs)) return false;\n\n\nstruct LessGeometry\n{\n    bool operator() (const osg::ref_ptr<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& rhs) const\n    {\n        if (lhs->getStateSet()<rhs->getStateSet()) return true;\n        if (rhs->getStateSet()<lhs->getStateSet()) return false;\n\n        COMPARE_BINDING(lhs->getNormalArray(), rhs->getNormalArray())\n        COMPARE_BINDING(lhs->getColorArray(), rhs->getColorArray())\n        COMPARE_BINDING(lhs->getSecondaryColorArray(), rhs->getSecondaryColorArray())\n        COMPARE_BINDING(lhs->getFogCoordArray(), rhs->getFogCoordArray())\n\n\n        if (lhs->getNumTexCoordArrays()<rhs->getNumTexCoordArrays()) return true;\n        if (rhs->getNumTexCoordArrays()<lhs->getNumTexCoordArrays()) return false;\n\n        // therefore lhs->getNumTexCoordArrays()==rhs->getNumTexCoordArrays()\n\n        unsigned int i;\n        for(i=0;i<lhs->getNumTexCoordArrays();++i)\n        {\n            if (rhs->getTexCoordArray(i))\n            {\n                if (!lhs->getTexCoordArray(i)) return true;\n            }\n            else if (lhs->getTexCoordArray(i)) return false;\n        }\n\n        for(i=0;i<lhs->getNumVertexAttribArrays();++i)\n        {\n            if (rhs->getVertexAttribArray(i))\n            {\n                if (!lhs->getVertexAttribArray(i)) return true;\n            }\n            else if (lhs->getVertexAttribArray(i)) return false;\n        }\n\n\n        if (osg::getBinding(lhs->getNormalArray())==osg::Array::BIND_OVERALL)\n        {\n            // assumes that the bindings and arrays are set up correctly, this\n            // should be the case after running computeCorrectBindingsAndArraySizes();\n            const osg::Array* lhs_normalArray = lhs->getNormalArray();\n            const osg::Array* rhs_normalArray = rhs->getNormalArray();\n            if (lhs_normalArray->getType()<rhs_normalArray->getType()) return true;\n            if (rhs_normalArray->getType()<lhs_normalArray->getType()) return false;\n            switch(lhs_normalArray->getType())\n            {\n            case(osg::Array::Vec3bArrayType):\n                if ((*static_cast<const osg::Vec3bArray*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3bArray*>(rhs_normalArray))[0]) return true;\n                if ((*static_cast<const osg::Vec3bArray*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3bArray*>(lhs_normalArray))[0]) return false;\n                break;\n            case(osg::Array::Vec3sArrayType):\n                if ((*static_cast<const osg::Vec3sArray*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3sArray*>(rhs_normalArray))[0]) return true;\n                if ((*static_cast<const osg::Vec3sArray*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3sArray*>(lhs_normalArray))[0]) return false;\n                break;\n            case(osg::Array::Vec3ArrayType):\n                if ((*static_cast<const osg::Vec3Array*>(lhs_normalArray))[0]<(*static_cast<const osg::Vec3Array*>(rhs_normalArray))[0]) return true;\n                if ((*static_cast<const osg::Vec3Array*>(rhs_normalArray))[0]<(*static_cast<const osg::Vec3Array*>(lhs_normalArray))[0]) return false;\n                break;\n            default:\n                break;\n            }\n        }\n\n        if (osg::getBinding(lhs->getColorArray())==osg::Array::BIND_OVERALL)\n        {\n            const osg::Array* lhs_colorArray = lhs->getColorArray();\n            const osg::Array* rhs_colorArray = rhs->getColorArray();\n            if (lhs_colorArray->getType()<rhs_colorArray->getType()) return true;\n            if (rhs_colorArray->getType()<lhs_colorArray->getType()) return false;\n            switch(lhs_colorArray->getType())\n            {\n                case(osg::Array::Vec4ubArrayType):\n                    if ((*static_cast<const osg::Vec4ubArray*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec4ubArray*>(rhs_colorArray))[0]) return true;\n                    if ((*static_cast<const osg::Vec4ubArray*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec4ubArray*>(lhs_colorArray))[0]) return false;\n                    break;\n                case(osg::Array::Vec3ArrayType):\n                    if ((*static_cast<const osg::Vec3Array*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec3Array*>(rhs_colorArray))[0]) return true;\n                    if ((*static_cast<const osg::Vec3Array*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec3Array*>(lhs_colorArray))[0]) return false;\n                    break;\n                case(osg::Array::Vec4ArrayType):\n                    if ((*static_cast<const osg::Vec4Array*>(lhs_colorArray))[0]<(*static_cast<const osg::Vec4Array*>(rhs_colorArray))[0]) return true;\n                    if ((*static_cast<const osg::Vec4Array*>(rhs_colorArray))[0]<(*static_cast<const osg::Vec4Array*>(lhs_colorArray))[0]) return false;\n                    break;\n                default:\n                    break;\n            }\n\n        }\n\n        return false;\n\n    }\n};\n\nstruct LessGeometryViewPoint\n{\n    osg::Vec3f _viewPoint;\n    bool operator() (const osg::ref_ptr<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& rhs) const\n    {\n        float len1 = (lhs->getBoundingBox().center() - _viewPoint).length2();\n        float len2 = (rhs->getBoundingBox().center() - _viewPoint).length2();\n        return len2 < len1;\n    }\n};\n\nstruct LessGeometryPrimitiveType\n{\n    bool operator() (const osg::ref_ptr<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& rhs) const\n    {\n        for(unsigned int i=0;\n            i<lhs->getNumPrimitiveSets() && i<rhs->getNumPrimitiveSets();\n            ++i)\n        {\n            if (lhs->getPrimitiveSet(i)->getType()<rhs->getPrimitiveSet(i)->getType()) return true;\n            else if (rhs->getPrimitiveSet(i)->getType()<lhs->getPrimitiveSet(i)->getType()) return false;\n\n            if (lhs->getPrimitiveSet(i)->getMode()<rhs->getPrimitiveSet(i)->getMode()) return true;\n            else if (rhs->getPrimitiveSet(i)->getMode()<lhs->getPrimitiveSet(i)->getMode()) return false;\n\n        }\n        return lhs->getNumPrimitiveSets()<rhs->getNumPrimitiveSets();\n    }\n};\n\n\n/// Shortcut to get size of an array, even if pointer is nullptr.\ninline unsigned int getSize(const osg::Array * a) { return a ? a->getNumElements() : 0; }\n\n/// When merging geometries, tests if two arrays can be merged, regarding to their number of components, and the number of vertices.\nbool isArrayCompatible(unsigned int numVertice1, unsigned int numVertice2, const osg::Array* compare1, const osg::Array* compare2)\n{\n    // Sumed up truth table:\n    //  If array (1 or 2) not empty and vertices empty => error, should not happen (allows simplification in formulae below)\n    //  If one side has both vertices and array, and the other side has only vertices => then arrays cannot be merged\n    //  Else, arrays can be merged\n    //assert(numVertice1 || !getSize(compare1));\n    //assert(numVertice2 || !getSize(compare2));\n    return !(   (numVertice1 && !getSize(compare1) && getSize(compare2))\n             || (numVertice2 && !getSize(compare2) && getSize(compare1)) );\n}\n\n/// Return true only if both geometries have same array type and if arrays (such as TexCoords) are compatible (i.e. both empty or both filled)\nbool isAbleToMerge(const osg::Geometry& g1, const osg::Geometry& g2)\n{\n    unsigned int numVertice1( getSize(g1.getVertexArray()) );\n    unsigned int numVertice2( getSize(g2.getVertexArray()) );\n\n    // first verify arrays size\n    if (!isArrayCompatible(numVertice1,numVertice2,g1.getNormalArray(),g2.getNormalArray()) ||\n        !isArrayCompatible(numVertice1,numVertice2,g1.getColorArray(),g2.getColorArray()) ||\n        !isArrayCompatible(numVertice1,numVertice2,g1.getSecondaryColorArray(),g2.getSecondaryColorArray()) ||\n        !isArrayCompatible(numVertice1,numVertice2,g1.getFogCoordArray(),g2.getFogCoordArray()) ||\n        g1.getNumTexCoordArrays()!=g2.getNumTexCoordArrays()) return false;\n\n    for (unsigned int eachTexCoordArray=0;eachTexCoordArray<g1.getNumTexCoordArrays();++eachTexCoordArray)\n    {\n        if (!isArrayCompatible(numVertice1,numVertice2,g1.getTexCoordArray(eachTexCoordArray),g2.getTexCoordArray(eachTexCoordArray))) return false;\n    }\n\n    // then verify data type compatibility\n    if (g1.getVertexArray() && g2.getVertexArray() && g1.getVertexArray()->getDataType()!=g2.getVertexArray()->getDataType()) return false;\n    if (g1.getNormalArray() && g2.getNormalArray() && g1.getNormalArray()->getDataType()!=g2.getNormalArray()->getDataType()) return false;\n    if (g1.getColorArray() && g2.getColorArray() && g1.getColorArray()->getDataType()!=g2.getColorArray()->getDataType()) return false;\n    if (g1.getSecondaryColorArray() && g2.getSecondaryColorArray() && g1.getSecondaryColorArray()->getDataType()!=g2.getSecondaryColorArray()->getDataType()) return false;\n    if (g1.getFogCoordArray() && g2.getNormalArray() && g1.getFogCoordArray()->getDataType()!=g2.getFogCoordArray()->getDataType()) return false;\n    return true;\n}\n\n\nvoid Optimizer::MergeGeometryVisitor::pushStateSet(osg::StateSet *stateSet)\n{\n    _stateSetStack.push_back(stateSet);\n    checkAlphaBlendingActive();\n}\n\nvoid Optimizer::MergeGeometryVisitor::popStateSet()\n{\n    _stateSetStack.pop_back();\n    checkAlphaBlendingActive();\n}\n\nvoid Optimizer::MergeGeometryVisitor::checkAlphaBlendingActive()\n{\n    int renderingHint = 0;\n    bool override = false;\n    for (std::vector<osg::StateSet*>::const_iterator it = _stateSetStack.begin(); it != _stateSetStack.end(); ++it)\n    {\n        osg::StateSet* stateSet = *it;\n        osg::StateSet::RenderBinMode mode = stateSet->getRenderBinMode();\n        if (override && !(mode & osg::StateSet::PROTECTED_RENDERBIN_DETAILS))\n            continue;\n        if (mode & osg::StateSet::USE_RENDERBIN_DETAILS)\n            renderingHint = stateSet->getRenderingHint();\n        if (mode & osg::StateSet::OVERRIDE_RENDERBIN_DETAILS)\n            override = true;\n    }\n    // Can't merge Geometry that are using a transparent sorting bin as that would cause the sorting to break.\n    _alphaBlendingActive = renderingHint == osg::StateSet::TRANSPARENT_BIN;\n}\n\nvoid Optimizer::MergeGeometryVisitor::apply(osg::Group &group)\n{\n    if (group.getStateSet())\n        pushStateSet(group.getStateSet());\n\n    if (!_alphaBlendingActive || _mergeAlphaBlending)\n        mergeGroup(group);\n\n    traverse(group);\n\n    if (group.getStateSet())\n        popStateSet();\n}\n\nosg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObject*& ebo, const osg::Geometry* geom)\n{\n    if (ps->referenceCount() <= 1)\n        return ps;\n    ps = static_cast<osg::PrimitiveSet*>(ps->clone(osg::CopyOp::DEEP_COPY_ALL));\n\n    osg::DrawElements* drawElements = ps->getDrawElements();\n    if (!drawElements) return ps;\n\n    if (!ebo && needvbo(geom))\n        ebo = new osg::ElementBufferObject;\n    if (ebo)\n        drawElements->setElementBufferObject(ebo);\n\n    return ps;\n}\n\nbool containsSharedPrimitives(const osg::Geometry* geom)\n{\n    for (unsigned int i=0; i<geom->getNumPrimitiveSets(); ++i)\n        if (geom->getPrimitiveSet(i)->referenceCount() > 1) return true;\n    return false;\n}\n\nbool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group)\n{\n    if (!isOperationPermissibleForObject(&group)) return false;\n\n    if (group.getNumChildren()>=2)\n    {\n\n        typedef std::vector< osg::ref_ptr<osg::Geometry> >                          DuplicateList;\n        typedef std::vector< osg::ref_ptr<osg::Node> >                              Nodes;\n        typedef std::map< osg::ref_ptr<osg::Geometry> ,DuplicateList,LessGeometry>  GeometryDuplicateMap;\n\n        typedef std::vector<DuplicateList> MergeList;\n\n        GeometryDuplicateMap geometryDuplicateMap;\n        Nodes standardChildren;\n\n        unsigned int i;\n        for(i=0;i<group.getNumChildren();++i)\n        {\n            osg::Node* child = group.getChild(i);\n            osg::Geometry* geom = child->asGeometry();\n            if (geom)\n            {\n                if (\n                    geom->getDataVariance()!=osg::Object::DYNAMIC &&\n                    isOperationPermissibleForObject(geom))\n                {\n                    geometryDuplicateMap[geom].push_back(geom);\n                }\n                else\n                {\n                    standardChildren.push_back(geom);\n                }\n            }\n            else\n            {\n                standardChildren.push_back(child);\n            }\n        }\n\n        // first try to group geometries with the same properties\n        // (i.e. array types) to avoid loss of data during merging\n        MergeList mergeListChecked;        // List of drawables just before merging, grouped by \"compatibility\" and vertex limit\n        MergeList mergeList;            // Intermediate list of drawables, grouped ony by \"compatibility\"\n        for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin();\n            itr!=geometryDuplicateMap.end();\n            ++itr)\n        {\n            if (itr->second.empty()) continue;\n            if (itr->second.size()==1)\n            {\n                mergeList.push_back(DuplicateList());\n                DuplicateList* duplicateList = &mergeList.back();\n                duplicateList->push_back(itr->second[0]);\n                continue;\n            }\n\n            std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType());\n\n            // initialize the temporary list by pushing the first geometry\n            MergeList mergeListTmp;\n            mergeListTmp.push_back(DuplicateList());\n            DuplicateList* duplicateList = &mergeListTmp.back();\n            duplicateList->push_back(itr->second[0]);\n\n            for(DuplicateList::iterator dupItr=itr->second.begin()+1;\n                dupItr!=itr->second.end();\n                ++dupItr)\n            {\n                osg::Geometry* geomToPush = dupItr->get();\n\n                // try to group geomToPush with another geometry\n                MergeList::iterator eachMergeList=mergeListTmp.begin();\n                for(;eachMergeList!=mergeListTmp.end();++eachMergeList)\n                {\n                    if (!eachMergeList->empty() && eachMergeList->front()!=nullptr\n                        && isAbleToMerge(*eachMergeList->front(),*geomToPush))\n                    {\n                        eachMergeList->push_back(geomToPush);\n                        break;\n                    }\n                }\n\n                // if no suitable group was found, then a new one is created\n                if (eachMergeList==mergeListTmp.end())\n                {\n                    mergeListTmp.push_back(DuplicateList());\n                    duplicateList = &mergeListTmp.back();\n                    duplicateList->push_back(geomToPush);\n                }\n            }\n\n            // copy the group in the mergeListChecked\n            for(MergeList::iterator eachMergeList=mergeListTmp.begin();eachMergeList!=mergeListTmp.end();++eachMergeList)\n            {\n                mergeListChecked.push_back(*eachMergeList);\n            }\n        }\n\n        // then build merge list using _targetMaximumNumberOfVertices\n        bool needToDoMerge = false;\n        // dequeue each DuplicateList when vertices limit is reached or when all elements has been checked\n        for(MergeList::iterator itr=mergeListChecked.begin(); itr!=mergeListChecked.end(); ++itr)\n        {\n            DuplicateList& duplicateList(*itr);\n            if (duplicateList.size()==0)\n            {\n                continue;\n            }\n\n            if (duplicateList.size()==1)\n            {\n                mergeList.push_back(duplicateList);\n                continue;\n            }\n\n            unsigned int totalNumberVertices = 0;\n            DuplicateList subset;\n            for(DuplicateList::iterator ditr = duplicateList.begin();\n                ditr != duplicateList.end();\n                ++ditr)\n            {\n                osg::Geometry* geometry = ditr->get();\n                unsigned int numVertices = (geometry->getVertexArray() ? geometry->getVertexArray()->getNumElements() : 0);\n                if ((totalNumberVertices+numVertices)>_targetMaximumNumberOfVertices && !subset.empty())\n                {\n                    mergeList.push_back(subset);\n                    subset.clear();\n                    totalNumberVertices = 0;\n                }\n                totalNumberVertices += numVertices;\n                subset.push_back(geometry);\n                if (subset.size()>1) needToDoMerge = true;\n            }\n            if (!subset.empty()) mergeList.push_back(subset);\n        }\n\n        if (needToDoMerge)\n        {\n            // to avoid performance issues associated with incrementally removing a large number children, we remove them all and add back the ones we need.\n            group.removeChildren(0, group.getNumChildren());\n\n            for(Nodes::iterator itr = standardChildren.begin();\n                itr != standardChildren.end();\n                ++itr)\n            {\n                group.addChild(*itr);\n            }\n\n            // now do the merging of geometries\n            for(MergeList::iterator mitr = mergeList.begin();\n                mitr != mergeList.end();\n                ++mitr)\n            {\n                DuplicateList& duplicateList = *mitr;\n                if (!duplicateList.empty())\n                {\n                    if (_alphaBlendingActive)\n                    {\n                        LessGeometryViewPoint lgvp;\n                        lgvp._viewPoint = _viewPoint;\n                        std::sort(duplicateList.begin(), duplicateList.end(), lgvp);\n                    }\n                    DuplicateList::iterator ditr = duplicateList.begin();\n                    osg::ref_ptr<osg::Geometry> lhs = *ditr++;\n                    group.addChild(lhs.get());\n                    for(;\n                        ditr != duplicateList.end();\n                        ++ditr)\n                    {\n                        mergeGeometry(*lhs, **ditr);\n                    }\n                }\n            }\n        }\n\n    }\n\n\n    // convert all polygon primitives which has 3 indices into TRIANGLES, 4 indices into QUADS.\n    unsigned int i;\n    for(i=0;i<group.getNumChildren();++i)\n    {\n        osg::Drawable* drawable = group.getChild(i)->asDrawable();\n        if (!drawable)\n            continue;\n        osg::Geometry* geom = drawable->asGeometry();\n        osg::ElementBufferObject* ebo = nullptr;\n        if (geom)\n        {\n            osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();\n            for(osg::Geometry::PrimitiveSetList::iterator itr=primitives.begin();\n                itr!=primitives.end();\n                ++itr)\n            {\n                osg::PrimitiveSet* prim = itr->get();\n                if (prim->getMode()==osg::PrimitiveSet::POLYGON)\n                {\n                    if (prim->getNumIndices()==3)\n                    {\n                        prim = clonePrimitive(prim, ebo, geom); (*itr) = prim;\n                        prim->setMode(osg::PrimitiveSet::TRIANGLES);\n                    }\n                    else if (prim->getNumIndices()==4)\n                    {\n                        prim = clonePrimitive(prim, ebo, geom); (*itr) = prim;\n                        prim->setMode(osg::PrimitiveSet::QUADS);\n                    }\n                }\n            }\n        }\n    }\n\n    // now merge any compatible primitives.\n    for(i=0;i<group.getNumChildren();++i)\n    {\n        osg::Drawable* drawable = group.getChild(i)->asDrawable();\n        if (!drawable)\n            continue;\n        osg::Geometry* geom = drawable->asGeometry();\n        osg::ElementBufferObject* ebo = nullptr;\n        if (geom)\n        {\n            if (geom->getNumPrimitiveSets()>0 &&\n                osg::getBinding(geom->getNormalArray())!=osg::Array::BIND_PER_PRIMITIVE_SET &&\n                osg::getBinding(geom->getColorArray())!=osg::Array::BIND_PER_PRIMITIVE_SET &&\n                osg::getBinding(geom->getSecondaryColorArray())!=osg::Array::BIND_PER_PRIMITIVE_SET &&\n                osg::getBinding(geom->getFogCoordArray())!=osg::Array::BIND_PER_PRIMITIVE_SET)\n            {\n\n#if 1\n                bool doneCombine = false;\n\n                std::set<osg::PrimitiveSet*> toremove;\n\n                osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();\n                unsigned int lhsNo=0;\n                unsigned int rhsNo=1;\n                while(rhsNo<primitives.size())\n                {\n                    osg::PrimitiveSet* lhs = primitives[lhsNo].get();\n                    osg::PrimitiveSet* rhs = primitives[rhsNo].get();\n\n                    bool combine = false;\n\n                    if (lhs->getType()==rhs->getType() &&\n                        lhs->getMode()==rhs->getMode())\n                    {\n\n                        switch(lhs->getMode())\n                        {\n                        case(osg::PrimitiveSet::POINTS):\n                        case(osg::PrimitiveSet::LINES):\n                        case(osg::PrimitiveSet::TRIANGLES):\n                        case(osg::PrimitiveSet::QUADS):\n                            combine = true;\n                            break;\n                        }\n\n                    }\n\n                    if (combine)\n                    {\n                        lhs = clonePrimitive(lhs, ebo, geom);\n                        primitives[lhsNo] = lhs;\n\n                        switch(lhs->getType())\n                        {\n                        case(osg::PrimitiveSet::DrawArraysPrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawArrays*>(lhs)),*(static_cast<osg::DrawArrays*>(rhs)));\n                            break;\n                        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawArrayLengths*>(lhs)),*(static_cast<osg::DrawArrayLengths*>(rhs)));\n                            break;\n                        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUByte*>(lhs)),*(static_cast<osg::DrawElementsUByte*>(rhs)));\n                            break;\n                        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUShort*>(lhs)),*(static_cast<osg::DrawElementsUShort*>(rhs)));\n                            break;\n                        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUInt*>(lhs)),*(static_cast<osg::DrawElementsUInt*>(rhs)));\n                            break;\n                        default:\n                            combine = false;\n                            break;\n                        }\n                    }\n\n                    if (combine)\n                    {\n                        // make this primitive set as invalid and needing cleaning up.\n                        toremove.insert(rhs);\n                        doneCombine = true;\n                        ++rhsNo;\n                    }\n                    else\n                    {\n                        lhsNo = rhsNo;\n                        ++rhsNo;\n                    }\n                }\n\n    #if 1\n                if (doneCombine)\n                {\n                    // now need to clean up primitiveset so it no longer contains the rhs combined primitives.\n                    // first swap with a empty primitiveSet to empty it completely.\n                    osg::Geometry::PrimitiveSetList oldPrimitives;\n                    primitives.swap(oldPrimitives);\n\n                    // now add the active primitive sets\n                    for(osg::Geometry::PrimitiveSetList::iterator pitr = oldPrimitives.begin();\n                        pitr != oldPrimitives.end();\n                        ++pitr)\n                    {\n                        if (!toremove.count(*pitr)) primitives.push_back(*pitr);\n                    }\n                }\n    #endif\n\n#else\n\n                osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList();\n                unsigned int primNo=0;\n                while(primNo+1<primitives.size())\n                {\n                    osg::PrimitiveSet* lhs = primitives[primNo].get();\n                    osg::PrimitiveSet* rhs = primitives[primNo+1].get();\n\n                    bool combine = false;\n\n                    if (lhs->getType()==rhs->getType() &&\n                        lhs->getMode()==rhs->getMode())\n                    {\n\n                        switch(lhs->getMode())\n                        {\n                        case(osg::PrimitiveSet::POINTS):\n                        case(osg::PrimitiveSet::LINES):\n                        case(osg::PrimitiveSet::TRIANGLES):\n                        case(osg::PrimitiveSet::QUADS):\n                            combine = true;\n                            break;\n                        }\n\n                    }\n\n                    if (combine)\n                    {\n\n                        switch(lhs->getType())\n                        {\n                        case(osg::PrimitiveSet::DrawArraysPrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawArrays*>(lhs)),*(static_cast<osg::DrawArrays*>(rhs)));\n                            break;\n                        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawArrayLengths*>(lhs)),*(static_cast<osg::DrawArrayLengths*>(rhs)));\n                            break;\n                        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUByte*>(lhs)),*(static_cast<osg::DrawElementsUByte*>(rhs)));\n                            break;\n                        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUShort*>(lhs)),*(static_cast<osg::DrawElementsUShort*>(rhs)));\n                            break;\n                        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):\n                            combine = mergePrimitive(*(static_cast<osg::DrawElementsUInt*>(lhs)),*(static_cast<osg::DrawElementsUInt*>(rhs)));\n                            break;\n                        default:\n                            break;\n                        }\n                    }\n                    if (combine)\n                    {\n                        primitives.erase(primitives.begin()+primNo+1);\n                    }\n\n                    if (!combine)\n                    {\n                        primNo++;\n                    }\n                }\n#endif\n                if (doneCombine && !geom->containsSharedArrays() && !containsSharedPrimitives(geom))\n                {\n                    // prefer to use vbo for merged geometries as vbo uses less memory than display lists.\n                    geom->setUseVertexBufferObjects(true);\n                    geom->setUseDisplayList(false);\n                }\n                if (_alphaBlendingActive && _mergeAlphaBlending && !geom->getStateSet())\n                {\n                    osg::Depth* d = new osg::Depth;\n                    d->setWriteMask(0);\n                    geom->getOrCreateStateSet()->setAttribute(d);\n                }\n            }\n        }\n\n\n    }\n\n//    geode.dirtyBound();\n\n\n    return false;\n}\n\nclass MergeArrayVisitor : public osg::ArrayVisitor\n{\n    protected:\n        osg::Array* _lhs;\n    public:\n        MergeArrayVisitor() :\n            _lhs(0) {}\n\n\n        /// try to merge the content of two arrays.\n        bool merge(osg::Array* lhs,osg::Array* rhs)\n        {\n            if (lhs==0 || rhs==0) return true;\n            if (lhs->getType()!=rhs->getType()) return false;\n\n            _lhs = lhs;\n\n            rhs->accept(*this);\n            return true;\n        }\n\n        template<typename T>\n        void _merge(T& rhs)\n        {\n            T* lhs = static_cast<T*>(_lhs);\n            lhs->insert(lhs->end(),rhs.begin(),rhs.end());\n        }\n\n        void apply(osg::Array&) override { OSG_WARN << \"Warning: Optimizer's MergeArrayVisitor cannot merge Array type.\" << std::endl; }\n\n        void apply(osg::ByteArray& rhs) override { _merge(rhs); }\n        void apply(osg::ShortArray& rhs) override { _merge(rhs); }\n        void apply(osg::IntArray& rhs) override { _merge(rhs); }\n        void apply(osg::UByteArray& rhs) override { _merge(rhs); }\n        void apply(osg::UShortArray& rhs) override { _merge(rhs); }\n        void apply(osg::UIntArray& rhs) override { _merge(rhs); }\n\n        void apply(osg::Vec4ubArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec3ubArray& rhs) override{ _merge(rhs); }\n        void apply(osg::Vec2ubArray& rhs) override { _merge(rhs); }\n\n        void apply(osg::Vec4usArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec3usArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec2usArray& rhs) override { _merge(rhs); }\n\n        void apply(osg::FloatArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec2Array& rhs) override { _merge(rhs); }\n        void apply(osg::Vec3Array& rhs) override { _merge(rhs); }\n        void apply(osg::Vec4Array& rhs) override { _merge(rhs); }\n\n        void apply(osg::DoubleArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec2dArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec3dArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec4dArray& rhs) override { _merge(rhs); }\n\n        void apply(osg::Vec2bArray&  rhs) override { _merge(rhs); }\n        void apply(osg::Vec3bArray&  rhs) override { _merge(rhs); }\n        void apply(osg::Vec4bArray&  rhs) override { _merge(rhs); }\n\n        void apply(osg::Vec2sArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec3sArray& rhs) override { _merge(rhs); }\n        void apply(osg::Vec4sArray& rhs) override { _merge(rhs); }\n};\n\nbool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs)\n{\n    MergeArrayVisitor merger;\n    osg::VertexBufferObject* vbo = nullptr;\n    unsigned int base = 0;\n    if (lhs.getVertexArray() && rhs.getVertexArray())\n    {\n        base = lhs.getVertexArray()->getNumElements();\n        if (lhs.getVertexArray()->referenceCount() > 1)\n            lhs.setVertexArray(cloneArray(lhs.getVertexArray(), vbo, &lhs));\n        if (!merger.merge(lhs.getVertexArray(),rhs.getVertexArray()))\n        {\n            OSG_DEBUG << \"MergeGeometry: vertex array not merged. Some data may be lost.\" <<std::endl;\n        }\n    }\n    else if (rhs.getVertexArray())\n    {\n        base = 0;\n        lhs.setVertexArray(rhs.getVertexArray());\n    }\n\n\n    if (lhs.getNormalArray() && rhs.getNormalArray() && lhs.getNormalArray()->getBinding()!=osg::Array::BIND_OVERALL)\n    {\n        if (lhs.getNormalArray()->referenceCount() > 1)\n            lhs.setNormalArray(cloneArray(lhs.getNormalArray(), vbo, &lhs));\n        if (!merger.merge(lhs.getNormalArray(),rhs.getNormalArray()))\n        {\n            OSG_DEBUG << \"MergeGeometry: normal array not merged. Some data may be lost.\" <<std::endl;\n        }\n    }\n    else if (rhs.getNormalArray())\n    {\n        lhs.setNormalArray(rhs.getNormalArray());\n    }\n\n\n    if (lhs.getColorArray() && rhs.getColorArray() && lhs.getColorArray()->getBinding()!=osg::Array::BIND_OVERALL)\n    {\n        if (lhs.getColorArray()->referenceCount() > 1)\n            lhs.setColorArray(cloneArray(lhs.getColorArray(), vbo, &lhs));\n        if (!merger.merge(lhs.getColorArray(),rhs.getColorArray()))\n        {\n            OSG_DEBUG << \"MergeGeometry: color array not merged. Some data may be lost.\" <<std::endl;\n        }\n    }\n    else if (rhs.getColorArray())\n    {\n        lhs.setColorArray(rhs.getColorArray());\n    }\n\n    if (lhs.getSecondaryColorArray() && rhs.getSecondaryColorArray() && lhs.getSecondaryColorArray()->getBinding()!=osg::Array::BIND_OVERALL)\n    {\n        if (lhs.getSecondaryColorArray()->referenceCount() > 1)\n            lhs.setSecondaryColorArray(cloneArray(lhs.getSecondaryColorArray(), vbo, &lhs));\n        if (!merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray()))\n        {\n            OSG_DEBUG << \"MergeGeometry: secondary color array not merged. Some data may be lost.\" <<std::endl;\n        }\n    }\n    else if (rhs.getSecondaryColorArray())\n    {\n        lhs.setSecondaryColorArray(rhs.getSecondaryColorArray());\n    }\n\n    if (lhs.getFogCoordArray() && rhs.getFogCoordArray() && lhs.getFogCoordArray()->getBinding()!=osg::Array::BIND_OVERALL)\n    {\n        if (lhs.getFogCoordArray()->referenceCount() > 1)\n            lhs.setFogCoordArray(cloneArray(lhs.getFogCoordArray(), vbo, &lhs));\n        if (!merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray()))\n        {\n            OSG_DEBUG << \"MergeGeometry: fog coord array not merged. Some data may be lost.\" <<std::endl;\n        }\n    }\n    else if (rhs.getFogCoordArray())\n    {\n        lhs.setFogCoordArray(rhs.getFogCoordArray());\n    }\n\n\n    unsigned int unit;\n    for(unit=0;unit<lhs.getNumTexCoordArrays();++unit)\n    {\n        if (!lhs.getTexCoordArray(unit)) continue;\n        if (lhs.getTexCoordArray(unit)->referenceCount() > 1)\n            lhs.setTexCoordArray(unit, cloneArray(lhs.getTexCoordArray(unit), vbo, &lhs));\n        if (!merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit)))\n        {\n            OSG_DEBUG << \"MergeGeometry: tex coord array not merged. Some data may be lost.\" <<std::endl;\n        }\n    }\n\n    for(unit=0;unit<lhs.getNumVertexAttribArrays();++unit)\n    {\n        if (!lhs.getVertexAttribArray(unit)) continue;\n        if (lhs.getVertexAttribArray(unit)->referenceCount() > 1)\n            lhs.setVertexAttribArray(unit, cloneArray(lhs.getVertexAttribArray(unit), vbo, &lhs));\n        if (!merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit)))\n        {\n            OSG_DEBUG << \"MergeGeometry: vertex attrib array not merged. Some data may be lost.\" <<std::endl;\n        }\n    }\n\n    // shift the indices of the incoming primitives to account for the pre existing geometry.\n    osg::ElementBufferObject* ebo = nullptr;\n    osg::Geometry::PrimitiveSetList::iterator primItr;\n    for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr)\n    {\n        osg::PrimitiveSet* primitive = primItr->get();\n\n        switch(primitive->getType())\n        {\n        case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType):\n            {\n                osg::DrawElementsUByte* primitiveUByte = static_cast<osg::DrawElementsUByte*>(primitive);\n                unsigned int currentMaximum = 0;\n                for(osg::DrawElementsUByte::iterator eitr=primitiveUByte->begin();\n                    eitr!=primitiveUByte->end();\n                    ++eitr)\n                {\n                    currentMaximum = osg::maximum(currentMaximum,(unsigned int)*eitr);\n                }\n                if ((base+currentMaximum)>=65536)\n                {\n                    // must promote to a DrawElementsUInt\n                    osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode());\n                    if (needvbo(&lhs))\n                    {\n                        if (!ebo) ebo = new osg::ElementBufferObject;\n                         new_primitive->setElementBufferObject(ebo);\n                    }\n                    std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive));\n                    new_primitive->offsetIndices(base);\n                    (*primItr) = new_primitive;\n                } else if ((base+currentMaximum)>=256)\n                {\n                    // must promote to a DrawElementsUShort\n                    osg::DrawElementsUShort* new_primitive = new osg::DrawElementsUShort(primitive->getMode());\n                    if (needvbo(&lhs))\n                    {\n                        if (!ebo) ebo = new osg::ElementBufferObject;\n                         new_primitive->setElementBufferObject(ebo);\n                    }\n                    std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive));\n                    new_primitive->offsetIndices(base);\n                    (*primItr) = new_primitive;\n                }\n                else\n                {\n                    (*primItr) = clonePrimitive(primitive, ebo, &lhs);\n                    (*primItr)->offsetIndices(base);\n                }\n            }\n            break;\n\n        case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType):\n            {\n                osg::DrawElementsUShort* primitiveUShort = static_cast<osg::DrawElementsUShort*>(primitive);\n                unsigned int currentMaximum = 0;\n                for(osg::DrawElementsUShort::iterator eitr=primitiveUShort->begin();\n                    eitr!=primitiveUShort->end();\n                    ++eitr)\n                {\n                    currentMaximum = osg::maximum(currentMaximum,(unsigned int)*eitr);\n                }\n                if ((base+currentMaximum)>=65536)\n                {\n                    // must promote to a DrawElementsUInt\n                    osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode());\n                    if (needvbo(&lhs))\n                    {\n                        if (!ebo) ebo = new osg::ElementBufferObject;\n                         new_primitive->setElementBufferObject(ebo);\n                    }\n                    std::copy(primitiveUShort->begin(),primitiveUShort->end(),std::back_inserter(*new_primitive));\n                    new_primitive->offsetIndices(base);\n                    (*primItr) = new_primitive;\n                }\n                else\n                {\n                    (*primItr) = clonePrimitive(primitive, ebo, &lhs);\n                    (*primItr)->offsetIndices(base);\n                }\n            }\n            break;\n\n        case(osg::PrimitiveSet::DrawArraysPrimitiveType):\n        case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType):\n        case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType):\n        default:\n            (*primItr) = clonePrimitive(primitive, ebo, &lhs);\n            (*primItr)->offsetIndices(base);\n            break;\n        }\n    }\n\n    for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr)\n    {\n        lhs.addPrimitiveSet(primItr->get());\n    }\n\n    lhs.dirtyBound();\n    lhs.dirtyDisplayList();\n\n    if (osg::UserDataContainer* rhsUserData = rhs.getUserDataContainer())\n        for (unsigned int i=0; i<rhsUserData->getNumUserObjects(); ++i)\n            lhs.getOrCreateUserDataContainer()->addUserObject(rhsUserData->getUserObject(i));\n\n    return true;\n}\n\nbool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs)\n{\n    if (lhs.getFirst()+lhs.getCount()==rhs.getFirst())\n    {\n        lhs.setCount(lhs.getCount()+rhs.getCount());\n        return true;\n    }\n    return false;\n}\n\nbool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawArrayLengths& lhs,osg::DrawArrayLengths& rhs)\n{\n    int lhs_count = std::accumulate(lhs.begin(),lhs.end(),0);\n\n    if (lhs.getFirst()+lhs_count==rhs.getFirst())\n    {\n        lhs.insert(lhs.end(),rhs.begin(),rhs.end());\n        return true;\n    }\n    return false;\n}\n\nbool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUByte& lhs,osg::DrawElementsUByte& rhs)\n{\n    lhs.insert(lhs.end(),rhs.begin(),rhs.end());\n    return true;\n}\n\nbool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUShort& lhs,osg::DrawElementsUShort& rhs)\n{\n    lhs.insert(lhs.end(),rhs.begin(),rhs.end());\n    return true;\n}\n\nbool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUInt& lhs,osg::DrawElementsUInt& rhs)\n{\n    lhs.insert(lhs.end(),rhs.begin(),rhs.end());\n    return true;\n}\n\n\n\nbool Optimizer::MergeGroupsVisitor::isOperationPermissible(osg::Group& node)\n{\n    return !node.getCullCallback() &&\n           !node.getEventCallback() &&\n           !node.getUpdateCallback() &&\n            isOperationPermissibleForObject(&node) &&\n           typeid(node)==typeid(osg::Group);\n}\n\nvoid Optimizer::MergeGroupsVisitor::apply(osg::LOD &lod)\n{\n    // don't merge the direct children of the LOD because they are used to define each LOD level.\n    traverse(lod);\n}\n\nvoid Optimizer::MergeGroupsVisitor::apply(osg::Switch &switchNode)\n{\n    // We should keep all switch child nodes since they reflect different switch states.\n    traverse(switchNode);\n}\n\nvoid Optimizer::MergeGroupsVisitor::apply(osg::Group &group)\n{\n    if (group.getNumChildren() <= 1)\n        traverse(group);\n    else\n    {\n        typedef std::map<osg::StateSet*, std::set<osg::Group*> > GroupMap;\n        GroupMap childGroups;\n        for (unsigned int i=0; i<group.getNumChildren(); ++i)\n        {\n            osg::Node* child = group.getChild(i);\n            osg::Group* childGroup = child->asGroup();\n            if (childGroup && isOperationPermissible(*childGroup))\n            {\n                childGroups[childGroup->getStateSet()].insert(childGroup);\n            }\n        }\n\n        for (GroupMap::iterator it = childGroups.begin(); it != childGroups.end(); ++it)\n        {\n            const std::set<osg::Group*>& groupSet = it->second;\n            if (groupSet.size() <= 1)\n                continue;\n            else\n            {\n                osg::Group* first = *groupSet.begin();\n                for (std::set<osg::Group*>::const_iterator groupIt = ++groupSet.begin(); groupIt != groupSet.end(); ++groupIt)\n                {\n                    osg::Group* toMerge = *groupIt;\n                    for (unsigned int i=0; i<toMerge->getNumChildren(); ++i)\n                        first->addChild(toMerge->getChild(i));\n                    toMerge->removeChildren(0, toMerge->getNumChildren());\n                    group.removeChild(toMerge);\n                }\n            }\n        }\n        traverse(group);\n    }\n}\n\n}\n"
  },
  {
    "path": "components/sceneutil/optimizer.hpp",
    "content": "/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield\n *\n * This library is open source and may be redistributed and/or modified under\n * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or\n * (at your option) any later version.  The full license is in LICENSE file\n * included with this distribution, and on the openscenegraph.org website.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * OpenSceneGraph Public License for more details.\n*/\n\n/* Modified for OpenMW */\n\n#ifndef OPENMW_OSGUTIL_OPTIMIZER\n#define OPENMW_OSGUTIL_OPTIMIZER\n\n#include <osg/NodeVisitor>\n#include <osg/Matrix>\n#include <osg/Geometry>\n#include <osg/Transform>\n#include <osg/Texture2D>\n\n//#include <osgUtil/Export>\n\n#include <set>\n\n//namespace osgUtil {\nnamespace SceneUtil {\n\n// forward declare\nclass Optimizer;\n\n/** Helper base class for implementing Optimizer techniques.*/\nclass BaseOptimizerVisitor : public osg::NodeVisitor\n{\n    public:\n\n        BaseOptimizerVisitor(Optimizer* optimizer, unsigned int operation):\n            osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),\n            _optimizer(optimizer),\n            _operationType(operation)\n        {\n            setNodeMaskOverride(0xffffffff);\n        }\n\n        inline bool isOperationPermissibleForObject(const osg::StateSet* object) const;\n        inline bool isOperationPermissibleForObject(const osg::StateAttribute* object) const;\n        inline bool isOperationPermissibleForObject(const osg::Drawable* object) const;\n        inline bool isOperationPermissibleForObject(const osg::Node* object) const;\n\n    protected:\n\n        Optimizer*      _optimizer;\n        unsigned int _operationType;\n};\n\n/** Traverses scene graph to improve efficiency. See OptimizationOptions.\n  * For example of usage see examples/osgimpostor or osgviewer.\n  */\n\nclass Optimizer\n{\n\n    public:\n\n        Optimizer() : _mergeAlphaBlending(false) {}\n        virtual ~Optimizer() {}\n\n        enum OptimizationOptions\n        {\n            FLATTEN_STATIC_TRANSFORMS = (1 << 0),\n            REMOVE_REDUNDANT_NODES =    (1 << 1),\n            REMOVE_LOADED_PROXY_NODES = (1 << 2),\n            COMBINE_ADJACENT_LODS =     (1 << 3),\n            SHARE_DUPLICATE_STATE =     (1 << 4),\n            MERGE_GEOMETRY =            (1 << 5),\n            CHECK_GEOMETRY =            (1 << 6), // deprecated, currently no-op\n            MAKE_FAST_GEOMETRY =        (1 << 7),\n            SPATIALIZE_GROUPS =         (1 << 8),\n            COPY_SHARED_NODES =         (1 << 9),\n            TRISTRIP_GEOMETRY =         (1 << 10),\n            TESSELLATE_GEOMETRY =       (1 << 11),\n            OPTIMIZE_TEXTURE_SETTINGS = (1 << 12),\n            MERGE_GEODES =              (1 << 13),\n            FLATTEN_BILLBOARDS =        (1 << 14),\n            TEXTURE_ATLAS_BUILDER =     (1 << 15),\n            STATIC_OBJECT_DETECTION =   (1 << 16),\n            FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS = (1 << 17),\n            INDEX_MESH =                (1 << 18),\n            VERTEX_POSTTRANSFORM =      (1 << 19),\n            VERTEX_PRETRANSFORM =       (1 << 20),\n            DEFAULT_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS |\n                                REMOVE_REDUNDANT_NODES |\n                                REMOVE_LOADED_PROXY_NODES |\n                                COMBINE_ADJACENT_LODS |\n                                SHARE_DUPLICATE_STATE |\n                                MERGE_GEOMETRY |\n                                MAKE_FAST_GEOMETRY |\n                                CHECK_GEOMETRY |\n                                OPTIMIZE_TEXTURE_SETTINGS |\n                                STATIC_OBJECT_DETECTION,\n            ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS |\n                                REMOVE_REDUNDANT_NODES |\n                                REMOVE_LOADED_PROXY_NODES |\n                                COMBINE_ADJACENT_LODS |\n                                SHARE_DUPLICATE_STATE |\n                                MERGE_GEODES |\n                                MERGE_GEOMETRY |\n                                MAKE_FAST_GEOMETRY |\n                                CHECK_GEOMETRY |\n                                SPATIALIZE_GROUPS |\n                                COPY_SHARED_NODES |\n                                TRISTRIP_GEOMETRY |\n                                OPTIMIZE_TEXTURE_SETTINGS |\n                                TEXTURE_ATLAS_BUILDER |\n                                STATIC_OBJECT_DETECTION\n        };\n\n        void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; }\n        void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; }\n\n        /** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/\n        void reset();\n\n        /** Traverse the node and its subgraph with a series of optimization\n          * visitors, specified by the OptimizationOptions.*/\n        virtual void optimize(osg::Node* node, unsigned int options);\n\n\n        /** Callback for customizing what operations are permitted on objects in the scene graph.*/\n        struct IsOperationPermissibleForObjectCallback : public osg::Referenced\n        {\n            virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::StateSet* stateset,unsigned int option) const\n            {\n                return optimizer->isOperationPermissibleForObjectImplementation(stateset,option);\n            }\n\n            virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::StateAttribute* attribute,unsigned int option) const\n            {\n                return optimizer->isOperationPermissibleForObjectImplementation(attribute,option);\n            }\n\n            virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::Drawable* drawable,unsigned int option) const\n            {\n                return optimizer->isOperationPermissibleForObjectImplementation(drawable,option);\n            }\n\n            virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::Node* node,unsigned int option) const\n            {\n                return optimizer->isOperationPermissibleForObjectImplementation(node,option);\n            }\n\n        };\n\n        /** Set the callback for customizing what operations are permitted on objects in the scene graph.*/\n        void setIsOperationPermissibleForObjectCallback(IsOperationPermissibleForObjectCallback* callback) { _isOperationPermissibleForObjectCallback=callback; }\n\n        /** Get the callback for customizing what operations are permitted on objects in the scene graph.*/\n        IsOperationPermissibleForObjectCallback* getIsOperationPermissibleForObjectCallback() { return _isOperationPermissibleForObjectCallback.get(); }\n\n        /** Get the callback for customizing what operations are permitted on objects in the scene graph.*/\n        const IsOperationPermissibleForObjectCallback* getIsOperationPermissibleForObjectCallback() const { return _isOperationPermissibleForObjectCallback.get(); }\n\n\n        inline void setPermissibleOptimizationsForObject(const osg::Object* object, unsigned int options)\n        {\n            _permissibleOptimizationsMap[object] = options;\n        }\n\n        inline unsigned int getPermissibleOptimizationsForObject(const osg::Object* object) const\n        {\n            PermissibleOptimizationsMap::const_iterator itr = _permissibleOptimizationsMap.find(object);\n            if (itr!=_permissibleOptimizationsMap.end()) return itr->second;\n            else return 0xffffffff;\n        }\n\n\n        inline bool isOperationPermissibleForObject(const osg::StateSet* object, unsigned int option) const\n        {\n            if (_isOperationPermissibleForObjectCallback.valid())\n                return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);\n            else\n                return isOperationPermissibleForObjectImplementation(object,option);\n        }\n\n        inline bool isOperationPermissibleForObject(const osg::StateAttribute* object, unsigned int option) const\n        {\n            if (_isOperationPermissibleForObjectCallback.valid())\n                return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);\n            else\n                return isOperationPermissibleForObjectImplementation(object,option);\n        }\n\n        inline bool isOperationPermissibleForObject(const osg::Drawable* object, unsigned int option) const\n        {\n            if (_isOperationPermissibleForObjectCallback.valid())\n                return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);\n            else\n                return isOperationPermissibleForObjectImplementation(object,option);\n        }\n\n        inline bool isOperationPermissibleForObject(const osg::Node* object, unsigned int option) const\n        {\n            if (_isOperationPermissibleForObjectCallback.valid())\n                return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);\n            else\n                return isOperationPermissibleForObjectImplementation(object,option);\n        }\n\n        bool isOperationPermissibleForObjectImplementation(const osg::StateSet* stateset, unsigned int option) const\n        {\n            return (option & getPermissibleOptimizationsForObject(stateset))!=0;\n        }\n\n        bool isOperationPermissibleForObjectImplementation(const osg::StateAttribute* attribute, unsigned int option) const\n        {\n            return (option & getPermissibleOptimizationsForObject(attribute))!=0;\n        }\n\n        bool isOperationPermissibleForObjectImplementation(const osg::Drawable* drawable, unsigned int option) const\n        {\n            if (option & (REMOVE_REDUNDANT_NODES|MERGE_GEOMETRY))\n            {\n                if (drawable->getUserData()) return false;\n                if (drawable->getUpdateCallback()) return false;\n                if (drawable->getEventCallback()) return false;\n                if (drawable->getCullCallback()) return false;\n            }\n            return (option & getPermissibleOptimizationsForObject(drawable))!=0;\n        }\n\n        bool isOperationPermissibleForObjectImplementation(const osg::Node* node, unsigned int option) const\n        {\n            if (option & (REMOVE_REDUNDANT_NODES|COMBINE_ADJACENT_LODS|FLATTEN_STATIC_TRANSFORMS))\n            {\n                if (node->getUserData()) return false;\n                if (node->getUpdateCallback()) return false;\n                if (node->getEventCallback()) return false;\n                if (node->getCullCallback()) return false;\n                if (node->getNumDescriptions()>0) return false;\n                if (node->getStateSet()) return false;\n                if (node->getNodeMask()!=0xffffffff) return false;\n                // if (!node->getName().empty()) return false;\n            }\n\n            return (option & getPermissibleOptimizationsForObject(node))!=0;\n        }\n\n    protected:\n\n        osg::ref_ptr<IsOperationPermissibleForObjectCallback> _isOperationPermissibleForObjectCallback;\n\n        typedef std::map<const osg::Object*,unsigned int> PermissibleOptimizationsMap;\n        PermissibleOptimizationsMap _permissibleOptimizationsMap;\n\n        osg::Vec3f _viewPoint;\n        bool _mergeAlphaBlending;\n\n    public:\n\n        /** Flatten Static Transform nodes by applying their transform to the\n          * geometry on the leaves of the scene graph, then removing the\n          * now redundant transforms.  Static transformed subgraphs that have multiple\n          * parental paths above them are not flattened, if you require this then\n          * the subgraphs have to be duplicated - for this use the\n          * FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor. */\n        class FlattenStaticTransformsVisitor : public BaseOptimizerVisitor\n        {\n            public:\n\n                FlattenStaticTransformsVisitor(Optimizer* optimizer=0):\n                    BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS) {}\n\n                void apply(osg::Node& geode) override;\n                void apply(osg::Drawable& drawable) override;\n                void apply(osg::Billboard& geode) override;\n                void apply(osg::Transform& transform) override;\n\n                bool removeTransforms(osg::Node* nodeWeCannotRemove);\n\n            protected:\n\n                typedef std::vector<osg::Transform*>                TransformStack;\n                typedef std::set<osg::Drawable*>                    DrawableSet;\n                typedef std::set<osg::Billboard*>                   BillboardSet;\n                typedef std::set<osg::Node* >                       NodeSet;\n                typedef std::set<osg::Transform*>                   TransformSet;\n\n                TransformStack  _transformStack;\n                NodeSet         _excludedNodeSet;\n                DrawableSet     _drawableSet;\n                BillboardSet    _billboardSet;\n                TransformSet    _transformSet;\n        };\n\n\n        /** Combine Static Transform nodes that sit above one another.*/\n        class CombineStaticTransformsVisitor : public BaseOptimizerVisitor\n        {\n            public:\n\n                CombineStaticTransformsVisitor(Optimizer* optimizer=0):\n                    BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS) {}\n\n                void apply(osg::MatrixTransform& transform) override;\n\n                bool removeTransforms(osg::Node* nodeWeCannotRemove);\n\n            protected:\n\n                typedef std::set<osg::MatrixTransform*> TransformSet;\n                TransformSet  _transformSet;\n        };\n\n        /** Remove rendundant nodes, such as groups with one single child.*/\n        class RemoveEmptyNodesVisitor : public BaseOptimizerVisitor\n        {\n            public:\n\n\n                typedef std::set<osg::Node*> NodeList;\n                NodeList                     _redundantNodeList;\n\n                RemoveEmptyNodesVisitor(Optimizer* optimizer=0):\n                    BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {}\n\n                void apply(osg::Group& group) override;\n\n                void removeEmptyNodes();\n\n        };\n\n        /** Remove redundant nodes, such as groups with one single child.*/\n        class RemoveRedundantNodesVisitor : public BaseOptimizerVisitor\n        {\n            public:\n\n                typedef std::set<osg::Node*> NodeList;\n                NodeList                     _redundantNodeList;\n\n                RemoveRedundantNodesVisitor(Optimizer* optimizer=0):\n                    BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {}\n\n                void apply(osg::Group& group) override;\n                void apply(osg::Transform& transform) override;\n                void apply(osg::LOD& lod) override;\n                void apply(osg::Switch& switchNode) override;\n\n                bool isOperationPermissible(osg::Node& node);\n\n                void removeRedundantNodes();\n\n        };\n\n        /** Merge adjacent Groups that have the same StateSet. */\n        class MergeGroupsVisitor : public SceneUtil::BaseOptimizerVisitor\n        {\n        public:\n            MergeGroupsVisitor(SceneUtil::Optimizer* optimizer)\n                : BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES)\n            {\n            }\n\n            bool isOperationPermissible(osg::Group& node);\n\n            void apply(osg::Group& group) override;\n            void apply(osg::LOD& lod) override;\n            void apply(osg::Switch& switchNode) override;\n        };\n\n        class MergeGeometryVisitor : public BaseOptimizerVisitor\n        {\n            public:\n\n                /// default to traversing all children.\n                MergeGeometryVisitor(Optimizer* optimizer=0) :\n                    BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY),\n                    _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false) {}\n\n                void setMergeAlphaBlending(bool merge)\n                {\n                    _mergeAlphaBlending = merge;\n                }\n                void setViewPoint(const osg::Vec3f& viewPoint)\n                {\n                    _viewPoint = viewPoint;\n                }\n\n                void setTargetMaximumNumberOfVertices(unsigned int num)\n                {\n                    _targetMaximumNumberOfVertices = num;\n                }\n\n                unsigned int getTargetMaximumNumberOfVertices() const\n                {\n                    return _targetMaximumNumberOfVertices;\n                }\n\n                void pushStateSet(osg::StateSet* stateSet);\n                void popStateSet();\n                void checkAlphaBlendingActive();\n\n                void apply(osg::Group& group) override;\n                void apply(osg::Billboard&) override { /* don't do anything*/ }\n\n                bool mergeGroup(osg::Group& group);\n\n                static bool mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs);\n\n                static bool mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs);\n                static bool mergePrimitive(osg::DrawArrayLengths& lhs,osg::DrawArrayLengths& rhs);\n                static bool mergePrimitive(osg::DrawElementsUByte& lhs,osg::DrawElementsUByte& rhs);\n                static bool mergePrimitive(osg::DrawElementsUShort& lhs,osg::DrawElementsUShort& rhs);\n                static bool mergePrimitive(osg::DrawElementsUInt& lhs,osg::DrawElementsUInt& rhs);\n\n            protected:\n\n                unsigned int _targetMaximumNumberOfVertices;\n                std::vector<osg::StateSet*> _stateSetStack;\n                bool _alphaBlendingActive;\n                bool _mergeAlphaBlending;\n                osg::Vec3f _viewPoint;\n        };\n\n};\n\ninline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::StateSet* object) const\n{\n    return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) :  true;\n}\n\ninline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::StateAttribute* object) const\n{\n    return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) :  true;\n}\n\ninline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::Drawable* object) const\n{\n    return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) :  true;\n}\n\ninline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::Node* object) const\n{\n    return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) :  true;\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/osgacontroller.cpp",
    "content": "#include <components/sceneutil/osgacontroller.hpp>\n\n#include <osg/Geode>\n#include <osg/Node>\n#include <osg/NodeVisitor>\n#include <osg/ref_ptr>\n#include <osg/StateSet>\n\n#include <osgAnimation/Animation>\n#include <osgAnimation/AnimationUpdateCallback>\n#include <osgAnimation/Channel>\n#include <osgAnimation/BasicAnimationManager>\n#include <osgAnimation/Bone>\n#include <osgAnimation/Sampler>\n#include <osgAnimation/Skeleton>\n#include <osgAnimation/RigGeometry>\n#include <osgAnimation/UpdateMatrixTransform>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/resource/animation.hpp>\n#include <components/sceneutil/controller.hpp>\n#include <components/sceneutil/keyframe.hpp>\n\nnamespace SceneUtil\n{\n    LinkVisitor::LinkVisitor() : osg::NodeVisitor( TRAVERSE_ALL_CHILDREN )\n    {\n        mAnimation = nullptr;\n    }\n\n    void LinkVisitor::link(osgAnimation::UpdateMatrixTransform* umt)\n    {\n        const osgAnimation::ChannelList& channels = mAnimation->getChannels();\n        for (const auto& channel: channels)\n        {\n            const std::string& channelName = channel->getName();\n            const std::string& channelTargetName = channel->getTargetName();\n\n            if (channelTargetName != umt->getName()) continue;\n\n            // check if we can link a StackedTransformElement to the current Channel\n            for (auto stackedTransform : umt->getStackedTransforms())\n            {\n                osgAnimation::StackedTransformElement* element = stackedTransform.get();\n                if (element && !element->getName().empty() && channelName == element->getName())\n                {\n                    osgAnimation::Target* target = element->getOrCreateTarget();\n                    if (target)\n                    {\n                        channel->setTarget(target);\n                    }\n                }\n            }\n        }\n    }\n\n    void LinkVisitor::handle_stateset(osg::StateSet* stateset)\n    {\n        if (!stateset)\n            return;\n        const osg::StateSet::AttributeList& attributeList = stateset->getAttributeList();\n        for (auto attribute : attributeList)\n        {\n            osg::StateAttribute* sattr = attribute.second.first.get();\n            osgAnimation::UpdateMatrixTransform* umt = dynamic_cast<osgAnimation::UpdateMatrixTransform*>(sattr->getUpdateCallback()); //Can this even be in sa?\n            if (umt) link(umt);\n        }\n    }\n\n    void LinkVisitor::setAnimation(Resource::Animation* animation)\n    {\n        mAnimation = animation;\n    }\n\n    void LinkVisitor::apply(osg::Node& node)\n    {\n        osg::StateSet* st = node.getStateSet();\n        if (st)\n        handle_stateset(st);\n\n        osg::Callback* cb = node.getUpdateCallback();\n        while (cb)\n        {\n            osgAnimation::UpdateMatrixTransform* umt = dynamic_cast<osgAnimation::UpdateMatrixTransform*>(cb);\n            if (umt)\n                if (Misc::StringUtils::lowerCase(node.getName()) != \"bip01\") link(umt);\n            cb = cb->getNestedCallback();\n        }\n\n        traverse( node );\n    }\n\n    void LinkVisitor::apply(osg::Geode& node)\n    {\n        for (unsigned int i = 0; i < node.getNumDrawables(); i++)\n        {\n            osg::Drawable* drawable = node.getDrawable(i);\n            if (drawable && drawable->getStateSet())\n                handle_stateset(drawable->getStateSet());\n        }\n        apply(static_cast<osg::Node&>(node));\n    }\n\n    OsgAnimationController::OsgAnimationController(const OsgAnimationController &copy, const osg::CopyOp &copyop) : SceneUtil::KeyframeController(copy, copyop)\n    , mEmulatedAnimations(copy.mEmulatedAnimations)\n    {\n        mLinker = nullptr;\n        for (const auto& mergedAnimationTrack : copy.mMergedAnimationTracks)\n        {\n            Resource::Animation* copiedAnimationTrack = static_cast<Resource::Animation*>(mergedAnimationTrack.get()->clone(copyop));\n            mMergedAnimationTracks.emplace_back(copiedAnimationTrack);\n        }\n    }\n\n    osg::Vec3f OsgAnimationController::getTranslation(float time) const\n    {\n        osg::Vec3f translationValue;\n        std::string animationName;\n        float newTime = time;\n\n        //Find the correct animation based on time\n        for (const EmulatedAnimation& emulatedAnimation : mEmulatedAnimations)\n        {\n            if (time >= emulatedAnimation.mStartTime && time <= emulatedAnimation.mStopTime)\n            {\n                newTime = time - emulatedAnimation.mStartTime;\n                animationName = emulatedAnimation.mName;\n            }\n        }\n\n        //Find the root transform track in animation\n        for (const auto& mergedAnimationTrack : mMergedAnimationTracks)\n        {\n            if (mergedAnimationTrack->getName() != animationName) continue;\n\n            const osgAnimation::ChannelList& channels = mergedAnimationTrack->getChannels();\n\n            for (const auto& channel: channels)\n            {\n                if (channel->getTargetName() != \"bip01\" || channel->getName() != \"transform\") continue;\n\n                if ( osgAnimation::MatrixLinearSampler* templateSampler = dynamic_cast<osgAnimation::MatrixLinearSampler*> (channel->getSampler()) )\n                {\n                    osg::Matrixf matrix;\n                    templateSampler->getValueAt(newTime, matrix);\n                    translationValue = matrix.getTrans();\n                    return osg::Vec3f(translationValue[0], translationValue[1], translationValue[2]);\n                }\n            }\n        }\n\n        return osg::Vec3f();\n    }\n\n    void OsgAnimationController::update(float time, std::string animationName)\n    {\n        for (const auto& mergedAnimationTrack : mMergedAnimationTracks)\n        {\n            if (mergedAnimationTrack->getName() == animationName) mergedAnimationTrack->update(time);\n        }\n    }\n\n    void OsgAnimationController::operator() (osg::Node* node, osg::NodeVisitor* nv)\n    {\n        if (hasInput())\n        {\n            if (mNeedToLink)\n            {\n                for (const auto& mergedAnimationTrack : mMergedAnimationTracks)\n                {\n                    if (!mLinker.valid()) mLinker = new LinkVisitor();\n                    mLinker->setAnimation(mergedAnimationTrack);\n                    node->accept(*mLinker);\n                }\n                mNeedToLink = false;\n            }\n\n            float time = getInputValue(nv);\n\n            for (const EmulatedAnimation& emulatedAnimation : mEmulatedAnimations)\n            {\n                if (time > emulatedAnimation.mStartTime && time < emulatedAnimation.mStopTime)\n                {\n                    update(time - emulatedAnimation.mStartTime, emulatedAnimation.mName);\n                }\n            }\n        }\n\n        traverse(node, nv);\n    }\n\n    void OsgAnimationController::setEmulatedAnimations(std::vector<EmulatedAnimation> emulatedAnimations)\n    {\n        mEmulatedAnimations = emulatedAnimations;\n    }\n\n    void OsgAnimationController::addMergedAnimationTrack(osg::ref_ptr<Resource::Animation> animationTrack)\n    {\n        mMergedAnimationTracks.emplace_back(animationTrack);\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/osgacontroller.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_OSGACONTROLLER_HPP\n#define OPENMW_COMPONENTS_SCENEUTIL_OSGACONTROLLER_HPP\n\n#include <osg/Node>\n#include <osg/NodeVisitor>\n#include <osg/StateSet>\n#include <osg/ref_ptr>\n#include <osgAnimation/Animation>\n#include <osgAnimation/AnimationUpdateCallback>\n#include <osgAnimation/Channel>\n#include <osgAnimation/BasicAnimationManager>\n#include <osgAnimation/StackedTransform>\n#include <osgAnimation/UpdateMatrixTransform>\n\n#include <components/sceneutil/controller.hpp>\n#include <components/sceneutil/keyframe.hpp>\n#include <components/resource/animation.hpp>\n\nnamespace SceneUtil\n{\n    struct EmulatedAnimation\n    {\n        float mStartTime;\n        float mStopTime;\n        std::string mName;\n    };\n\n    class LinkVisitor : public osg::NodeVisitor\n    {\n        public:\n            LinkVisitor();\n\n            virtual void link(osgAnimation::UpdateMatrixTransform* umt);\n\n            virtual void handle_stateset(osg::StateSet* stateset);\n\n            virtual void setAnimation(Resource::Animation* animation);\n\n            virtual void apply(osg::Node& node) override;\n\n            virtual void apply(osg::Geode& node) override;\n\n        protected:\n            Resource::Animation* mAnimation;\n    };\n\n    class OsgAnimationController : public SceneUtil::KeyframeController\n    {\n    public:\n        /// @brief Handles the animation for osgAnimation formats\n        OsgAnimationController() {};\n\n        OsgAnimationController(const OsgAnimationController& copy, const osg::CopyOp& copyop);\n\n        META_Object(SceneUtil, OsgAnimationController)\n\n        /// @brief Handles the location of the instance\n        osg::Vec3f getTranslation(float time) const override;\n\n        /// @brief Calls animation track update()\n        void update(float time, std::string animationName);\n\n        /// @brief Called every frame for osgAnimation\n        void operator() (osg::Node*, osg::NodeVisitor*) override;\n\n        /// @brief Sets details of the animations\n        void setEmulatedAnimations(std::vector<EmulatedAnimation> emulatedAnimations);\n\n        /// @brief Adds an animation track to a model\n        void addMergedAnimationTrack(osg::ref_ptr<Resource::Animation> animationTrack);\n\n    private:\n        bool mNeedToLink = true;\n        osg::ref_ptr<LinkVisitor> mLinker;\n        std::vector<osg::ref_ptr<Resource::Animation>> mMergedAnimationTracks; // Used only by osgAnimation-based formats (e.g. dae)\n        std::vector<EmulatedAnimation> mEmulatedAnimations;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/pathgridutil.cpp",
    "content": "#include \"pathgridutil.hpp\"\n\n#include <osg/Geometry>\n\n#include <components/esm/loadpgrd.hpp>\n\nnamespace SceneUtil\n{\n    const unsigned short DiamondVertexCount = 6;\n    const unsigned short DiamondIndexCount = 24;\n    const unsigned short DiamondWireframeIndexCount = 24;\n\n    const unsigned short DiamondConnectorVertexCount = 4;\n\n    const unsigned short DiamondTotalVertexCount = DiamondVertexCount + DiamondConnectorVertexCount;\n\n    const float DiamondWireframeScalar = 1.1f;\n\n    const osg::Vec3f DiamondPoints[DiamondVertexCount] =\n    {\n        osg::Vec3f( 0.f, 0.f, DiamondHalfHeight * 2.f),\n        osg::Vec3f(-DiamondHalfWidth, -DiamondHalfWidth,  DiamondHalfHeight),\n        osg::Vec3f(-DiamondHalfWidth,  DiamondHalfWidth,  DiamondHalfHeight),\n        osg::Vec3f( DiamondHalfWidth, -DiamondHalfWidth,  DiamondHalfHeight),\n        osg::Vec3f( DiamondHalfWidth,  DiamondHalfWidth,  DiamondHalfHeight),\n        osg::Vec3f( 0.f, 0.f, 0.f)\n    };\n\n    const unsigned short DiamondIndices[DiamondIndexCount] =\n    {\n        0, 2, 1,\n        0, 1, 3,\n        0, 3, 4,\n        0, 4, 2,\n        5, 1, 2,\n        5, 3, 1,\n        5, 4, 3,\n        5, 2, 4\n    };\n\n    const unsigned short DiamondWireframeIndices[DiamondWireframeIndexCount] =\n    {\n        0, 1,\n        0, 2,\n        0, 3,\n        0, 4,\n        1, 2,\n        2, 4,\n        4, 3,\n        3, 1,\n        5, 1,\n        5, 2,\n        5, 3,\n        5, 4\n    };\n\n    const unsigned short DiamondConnectorVertices[DiamondConnectorVertexCount] =\n    {\n        1, 2, 3, 4\n    };\n\n    const osg::Vec4f DiamondColors[DiamondVertexCount] =\n    {\n        osg::Vec4f(0.f, 0.f, 1.f, 1.f),\n        osg::Vec4f(0.f, .05f, .95f, 1.f),\n        osg::Vec4f(0.f, .1f, .95f, 1.f),\n        osg::Vec4f(0.f, .15f, .95f, 1.f),\n        osg::Vec4f(0.f, .2f, .95f, 1.f),\n        osg::Vec4f(0.f, .25f, 9.f, 1.f)\n    };\n\n    const osg::Vec4f DiamondEdgeColor = osg::Vec4f(0.5f, 1.f, 1.f, 1.f);\n    const osg::Vec4f DiamondWireColor = osg::Vec4f(0.72f, 0.f, 0.96f, 1.f);\n    const osg::Vec4f DiamondFocusWireColor = osg::Vec4f(0.91f, 0.66f, 1.f, 1.f);\n\n    osg::ref_ptr<osg::Geometry> createPathgridGeometry(const ESM::Pathgrid& pathgrid)\n    {\n        const unsigned short PointCount = static_cast<unsigned short>(pathgrid.mPoints.size());\n        const size_t EdgeCount = pathgrid.mEdges.size();\n\n        const unsigned short VertexCount = PointCount * DiamondTotalVertexCount;\n        const unsigned short ColorCount = VertexCount;\n        const size_t PointIndexCount = PointCount * DiamondIndexCount;\n        const size_t EdgeIndexCount = EdgeCount * 2;\n\n        osg::ref_ptr<osg::Geometry> gridGeometry = new osg::Geometry();\n\n        if (PointIndexCount || EdgeIndexCount)\n        {\n            osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(VertexCount);\n            osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(ColorCount);\n            osg::ref_ptr<osg::DrawElementsUShort> pointIndices =\n                new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, PointIndexCount);\n            osg::ref_ptr<osg::DrawElementsUShort> lineIndices =\n                new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, EdgeIndexCount);\n\n            // Add each point/node\n            for (unsigned short pointIndex = 0; pointIndex < PointCount; ++pointIndex)\n            {\n                const ESM::Pathgrid::Point& point = pathgrid.mPoints[pointIndex];\n                osg::Vec3f position = osg::Vec3f(point.mX, point.mY, point.mZ);\n\n                unsigned short vertexOffset = pointIndex * DiamondTotalVertexCount;\n                unsigned short indexOffset = pointIndex * DiamondIndexCount;\n\n                // Point\n                for (unsigned short i = 0; i < DiamondVertexCount; ++i)\n                {\n                    (*vertices)[vertexOffset + i] = position + DiamondPoints[i];\n                    (*colors)[vertexOffset + i] = DiamondColors[i];\n                }\n\n                for (unsigned short i = 0; i < DiamondIndexCount; ++i)\n                {\n                    pointIndices->setElement(indexOffset + i, vertexOffset + DiamondIndices[i]);\n                }\n\n                // Connectors\n                vertexOffset += DiamondVertexCount;\n                for (unsigned short i = 0; i < DiamondConnectorVertexCount; ++i)\n                {\n                    (*vertices)[vertexOffset + i] = position + DiamondPoints[DiamondConnectorVertices[i]];\n                    (*colors)[vertexOffset + i] = DiamondEdgeColor;\n                }\n            }\n\n            // Add edges\n            unsigned short lineIndex = 0;\n\n            for (ESM::Pathgrid::EdgeList::const_iterator edge = pathgrid.mEdges.begin();\n                edge != pathgrid.mEdges.end(); ++edge)\n            {\n                if (edge->mV0 == edge->mV1 || edge->mV0 < 0 || edge->mV0 >= PointCount ||\n                    edge->mV1 < 0 || edge->mV1 >= PointCount)\n                    continue;\n\n                const ESM::Pathgrid::Point& from = pathgrid.mPoints[edge->mV0];\n                const ESM::Pathgrid::Point& to = pathgrid.mPoints[edge->mV1];\n\n                osg::Vec3f fromPos = osg::Vec3f(from.mX, from.mY, from.mZ);\n                osg::Vec3f toPos = osg::Vec3f(to.mX, to.mY, to.mZ);\n                osg::Vec3f dir = toPos - fromPos;\n                dir.normalize();\n\n                osg::Quat rot(static_cast<float>(-osg::PI_2), osg::Vec3f(0, 0, 1));\n                dir = rot * dir;\n\n                unsigned short diamondIndex = 0;\n                if (dir.isNaN())\n                    diamondIndex = 0;\n                else if (dir.y() >= 0 && dir.x() > 0)\n                    diamondIndex = 3;\n                else if (dir.x() <= 0 && dir.y() > 0)\n                    diamondIndex = 1;\n                else if (dir.y() <= 0 && dir.x() < 0)\n                    diamondIndex = 0;\n                else if (dir.x() >= 0 && dir.y() < 0)\n                    diamondIndex = 2;\n\n                unsigned short fromIndex = static_cast<unsigned short>(edge->mV0);\n                unsigned short toIndex = static_cast<unsigned short>(edge->mV1);\n\n                lineIndices->setElement(lineIndex++, fromIndex * DiamondTotalVertexCount + DiamondVertexCount + diamondIndex);\n                lineIndices->setElement(lineIndex++, toIndex * DiamondTotalVertexCount + DiamondVertexCount + diamondIndex);\n            }\n\n            lineIndices->resize(lineIndex);\n\n            gridGeometry->setVertexArray(vertices);\n            gridGeometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);\n            if (PointIndexCount)\n                gridGeometry->addPrimitiveSet(pointIndices);\n            if (EdgeIndexCount)\n                gridGeometry->addPrimitiveSet(lineIndices);\n            gridGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n        }\n        return gridGeometry;\n    }\n\n    osg::ref_ptr<osg::Geometry> createPathgridSelectedWireframe(const ESM::Pathgrid& pathgrid,\n        const std::vector<unsigned short>& selected)\n    {\n        const unsigned short PointCount = selected.size();\n\n        const unsigned short VertexCount = PointCount * DiamondVertexCount;\n        const unsigned short ColorCount = VertexCount;\n        const size_t IndexCount = PointCount * DiamondWireframeIndexCount;\n\n        osg::ref_ptr<osg::Geometry> wireframeGeometry = new osg::Geometry();\n\n        if (IndexCount)\n        {\n            osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(VertexCount);\n            osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(ColorCount);\n            osg::ref_ptr<osg::DrawElementsUShort> indices =\n                new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, IndexCount);\n\n            osg::Vec3f wireOffset = osg::Vec3f(0, 0, (1 - DiamondWireframeScalar) * DiamondHalfHeight);\n\n            // Add each point/node\n            for (unsigned short it = 0; it < PointCount; ++it)\n            {\n                const ESM::Pathgrid::Point& point = pathgrid.mPoints[selected[it]];\n                osg::Vec3f position = osg::Vec3f(point.mX, point.mY, point.mZ) + wireOffset;\n\n                unsigned short vertexOffset = it * DiamondVertexCount;\n                unsigned short indexOffset = it * DiamondWireframeIndexCount;\n\n                // Point\n                for (unsigned short i = 0; i < DiamondVertexCount; ++i)\n                {\n                    (*vertices)[vertexOffset + i] = position + DiamondPoints[i] * DiamondWireframeScalar;\n\n                    if (it == PointCount - 1)\n                        (*colors)[vertexOffset + i] = DiamondFocusWireColor;\n                    else\n                        (*colors)[vertexOffset + i] = DiamondWireColor;\n                }\n\n                for (unsigned short i = 0; i < DiamondWireframeIndexCount; ++i)\n                {\n                    indices->setElement(indexOffset + i, vertexOffset + DiamondWireframeIndices[i]);\n                }\n            }\n\n            wireframeGeometry->setVertexArray(vertices);\n            wireframeGeometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);\n            wireframeGeometry->addPrimitiveSet(indices);\n            wireframeGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n        }\n        return wireframeGeometry;\n    }\n\n    unsigned short getPathgridNode(unsigned short vertexIndex)\n    {\n        return vertexIndex / (DiamondVertexCount + DiamondConnectorVertexCount);\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/pathgridutil.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_PATHGRIDUTIL_H\n#define OPENMW_COMPONENTS_PATHGRIDUTIL_H\n\n#include <osg/ref_ptr>\n#include <osg/Geometry>\n\nnamespace ESM\n{\n    struct Pathgrid;\n}\n\nnamespace SceneUtil\n{\n    const float DiamondHalfHeight = 40.f;\n    const float DiamondHalfWidth = 16.f;\n\n    osg::ref_ptr<osg::Geometry> createPathgridGeometry(const ESM::Pathgrid& pathgrid);\n\n    osg::ref_ptr<osg::Geometry> createPathgridSelectedWireframe(const ESM::Pathgrid& pathgrid,\n        const std::vector<unsigned short>& selected);\n\n    unsigned short getPathgridNode(unsigned short vertexIndex);\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/positionattitudetransform.cpp",
    "content": "#include \"positionattitudetransform.hpp\"\n\nnamespace SceneUtil\n{\n\nPositionAttitudeTransform::PositionAttitudeTransform():\n    _scale(1.0,1.0,1.0)\n{\n}\n\nbool PositionAttitudeTransform::computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor*) const\n{\n    if (_referenceFrame==RELATIVE_RF)\n    {\n        matrix.preMultTranslate(_position);\n        matrix.preMultRotate(_attitude);\n        matrix.preMultScale(_scale);\n    }\n    else // absolute\n    {\n        matrix.makeRotate(_attitude);\n        matrix.postMultTranslate(_position);\n        matrix.preMultScale(_scale);\n    }\n    return true;\n}\n\n\nbool PositionAttitudeTransform::computeWorldToLocalMatrix(osg::Matrix& matrix, osg::NodeVisitor*) const\n{\n    if (_scale.x() == 0.0 || _scale.y() == 0.0 || _scale.z() == 0.0)\n        return false;\n\n    if (_referenceFrame==RELATIVE_RF)\n    {\n        matrix.postMultTranslate(-_position);\n        matrix.postMultRotate(_attitude.inverse());\n        matrix.postMultScale(osg::Vec3f(1.0/_scale.x(), 1.0/_scale.y(), 1.0/_scale.z()));\n    }\n    else // absolute\n    {\n        matrix.makeRotate(_attitude.inverse());\n        matrix.preMultTranslate(-_position);\n        matrix.postMultScale(osg::Vec3f(1.0/_scale.x(), 1.0/_scale.y(), 1.0/_scale.z()));\n    }\n    return true;\n}\n\n}\n"
  },
  {
    "path": "components/sceneutil/positionattitudetransform.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_POSITIONATTITUDE_TRANSFORM_H\n#define OPENMW_COMPONENTS_POSITIONATTITUDE_TRANSFORM_H\n\n#include <osg/Transform>\n\nnamespace SceneUtil\n{\n\n/// @brief A customized version of osg::PositionAttitudeTransform optimized for speed.\n/// Uses single precision values. Also removed _pivotPoint which we don't need.\nclass PositionAttitudeTransform : public osg::Transform\n{\n    public :\n        PositionAttitudeTransform();\n\n        PositionAttitudeTransform(const PositionAttitudeTransform& pat,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY):\n            Transform(pat,copyop),\n            _position(pat._position),\n            _attitude(pat._attitude),\n            _scale(pat._scale){}\n\n\n        META_Node(SceneUtil, PositionAttitudeTransform)\n\n        inline void setPosition(const osg::Vec3f& pos) { _position = pos; dirtyBound(); }\n        inline const osg::Vec3f& getPosition() const { return _position; }\n\n        inline void setAttitude(const osg::Quat& quat) { _attitude = quat; dirtyBound(); }\n        inline const osg::Quat& getAttitude() const { return _attitude; }\n\n        inline void setScale(const osg::Vec3f& scale) { _scale = scale; dirtyBound(); }\n        inline const osg::Vec3f& getScale() const { return _scale; }\n\n        bool computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const override;\n        bool computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const override;\n\n\n    protected :\n        virtual ~PositionAttitudeTransform() {}\n\n        osg::Vec3f _position;\n        osg::Quat _attitude;\n        osg::Vec3f _scale;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/recastmesh.cpp",
    "content": "#include \"navmesh.hpp\"\n#include \"detourdebugdraw.hpp\"\n\n#include <components/detournavigator/settings.hpp>\n#include <components/detournavigator/recastmesh.hpp>\n\n#include <RecastDebugDraw.h>\n\n#include <osg/Group>\n\nnamespace\n{\n    std::vector<float> calculateNormals(const std::vector<float>& vertices, const std::vector<int>& indices)\n    {\n        std::vector<float> result(indices.size());\n        for (std::size_t i = 0, n = indices.size(); i < n; i += 3)\n        {\n            const float* v0_ptr = &vertices[indices[i] * 3];\n            const float* v1_ptr = &vertices[indices[i + 1] * 3];\n            const float* v2_ptr = &vertices[indices[i + 2] * 3];\n            const osg::Vec3f v0(v0_ptr[0], v0_ptr[1], v0_ptr[2]);\n            const osg::Vec3f v1(v1_ptr[0], v1_ptr[1], v1_ptr[2]);\n            const osg::Vec3f v2(v2_ptr[0], v2_ptr[1], v2_ptr[2]);\n            const osg::Vec3f e0 = v1 - v0;\n            const osg::Vec3f e1 = v2 - v0;\n            osg::Vec3f normal = e0 ^ e1;\n            normal.normalize();\n            for (std::size_t j = 0; j < 3; ++j)\n                result[i + j] = normal[j];\n        }\n        return result;\n    }\n}\n\nnamespace SceneUtil\n{\n    osg::ref_ptr<osg::Group> createRecastMeshGroup(const DetourNavigator::RecastMesh& recastMesh,\n        const DetourNavigator::Settings& settings)\n    {\n        const osg::ref_ptr<osg::Group> group(new osg::Group);\n        DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 0), 1.0f / settings.mRecastScaleFactor);\n        const auto normals = calculateNormals(recastMesh.getVertices(), recastMesh.getIndices());\n        const auto texScale = 1.0f / (settings.mCellSize * 10.0f);\n        duDebugDrawTriMesh(&debugDraw, recastMesh.getVertices().data(), recastMesh.getVerticesCount(),\n            recastMesh.getIndices().data(), normals.data(), recastMesh.getTrianglesCount(), nullptr, texScale);\n        return group;\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/recastmesh.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_RECASTMESH_H\n#define OPENMW_COMPONENTS_SCENEUTIL_RECASTMESH_H\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class Group;\n}\n\nnamespace DetourNavigator\n{\n    class RecastMesh;\n    struct Settings;\n}\n\nnamespace SceneUtil\n{\n    osg::ref_ptr<osg::Group> createRecastMeshGroup(const DetourNavigator::RecastMesh& recastMesh,\n        const DetourNavigator::Settings& settings);\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/riggeometry.cpp",
    "content": "#include \"riggeometry.hpp\"\n\n#include <osg/Version>\n\n#include <components/debug/debuglog.hpp>\n\n#include \"skeleton.hpp\"\n#include \"util.hpp\"\n\nnamespace\n{\n    inline void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, const float weight, osg::Matrixf& result)\n    {\n        osg::Matrixf m = invBindMatrix * matrix;\n        float* ptr = m.ptr();\n        float* ptrresult = result.ptr();\n        ptrresult[0] += ptr[0] * weight;\n        ptrresult[1] += ptr[1] * weight;\n        ptrresult[2] += ptr[2] * weight;\n\n        ptrresult[4] += ptr[4] * weight;\n        ptrresult[5] += ptr[5] * weight;\n        ptrresult[6] += ptr[6] * weight;\n\n        ptrresult[8] += ptr[8] * weight;\n        ptrresult[9] += ptr[9] * weight;\n        ptrresult[10] += ptr[10] * weight;\n\n        ptrresult[12] += ptr[12] * weight;\n        ptrresult[13] += ptr[13] * weight;\n        ptrresult[14] += ptr[14] * weight;\n    }\n}\n\nnamespace SceneUtil\n{\n\nRigGeometry::RigGeometry()\n    : mSkeleton(nullptr)\n    , mLastFrameNumber(0)\n    , mBoundsFirstFrame(true)\n{\n    setNumChildrenRequiringUpdateTraversal(1);\n    // update done in accept(NodeVisitor&)\n}\n\nRigGeometry::RigGeometry(const RigGeometry &copy, const osg::CopyOp &copyop)\n    : Drawable(copy, copyop)\n    , mSkeleton(nullptr)\n    , mInfluenceMap(copy.mInfluenceMap)\n    , mBone2VertexVector(copy.mBone2VertexVector)\n    , mBoneSphereVector(copy.mBoneSphereVector)\n    , mLastFrameNumber(0)\n    , mBoundsFirstFrame(true)\n{\n    setSourceGeometry(copy.mSourceGeometry);\n    setNumChildrenRequiringUpdateTraversal(1);\n}\n\nvoid RigGeometry::setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeometry)\n{\n    mSourceGeometry = sourceGeometry;\n\n    for (unsigned int i=0; i<2; ++i)\n    {\n        const osg::Geometry& from = *sourceGeometry;\n        mGeometry[i] = new osg::Geometry(from, osg::CopyOp::SHALLOW_COPY);\n        osg::Geometry& to = *mGeometry[i];\n        to.setSupportsDisplayList(false);\n        to.setUseVertexBufferObjects(true);\n        to.setCullingActive(false); // make sure to disable culling since that's handled by this class\n        to.setComputeBoundingBoxCallback(new CopyBoundingBoxCallback());\n        to.setComputeBoundingSphereCallback(new CopyBoundingSphereCallback());\n\n        // vertices and normals are modified every frame, so we need to deep copy them.\n        // assign a dedicated VBO to make sure that modifications don't interfere with source geometry's VBO.\n        osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);\n        vbo->setUsage(GL_DYNAMIC_DRAW_ARB);\n\n        osg::ref_ptr<osg::Array> vertexArray = static_cast<osg::Array*>(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL));\n        if (vertexArray)\n        {\n            vertexArray->setVertexBufferObject(vbo);\n            to.setVertexArray(vertexArray);\n        }\n\n        if (const osg::Array* normals = from.getNormalArray())\n        {\n            osg::ref_ptr<osg::Array> normalArray = static_cast<osg::Array*>(normals->clone(osg::CopyOp::DEEP_COPY_ALL));\n            if (normalArray)\n            {\n                normalArray->setVertexBufferObject(vbo);\n                to.setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX);\n            }\n        }\n\n        if (const osg::Vec4Array* tangents = dynamic_cast<const osg::Vec4Array*>(from.getTexCoordArray(7)))\n        {\n            mSourceTangents = tangents;\n            osg::ref_ptr<osg::Array> tangentArray = static_cast<osg::Array*>(tangents->clone(osg::CopyOp::DEEP_COPY_ALL));\n            tangentArray->setVertexBufferObject(vbo);\n            to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX);\n        }\n        else\n            mSourceTangents = nullptr;\n    }\n}\n\nosg::ref_ptr<osg::Geometry> RigGeometry::getSourceGeometry() const\n{\n    return mSourceGeometry;\n}\n\nbool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv)\n{\n    const osg::NodePath& path = nv->getNodePath();\n    for (osg::NodePath::const_reverse_iterator it = path.rbegin(); it != path.rend(); ++it)\n    {\n        osg::Node* node = *it;\n        if (Skeleton* skel = dynamic_cast<Skeleton*>(node))\n        {\n            mSkeleton = skel;\n            break;\n        }\n    }\n\n    if (!mSkeleton)\n    {\n        Log(Debug::Error) << \"Error: A RigGeometry did not find its parent skeleton\";\n        return false;\n    }\n\n    if (!mInfluenceMap)\n    {\n        Log(Debug::Error) << \"Error: No InfluenceMap set on RigGeometry\";\n        return false;\n    }\n\n    mBoneNodesVector.clear();\n    for (auto& bonePair : mBoneSphereVector->mData)\n    {\n        const std::string& boneName = bonePair.first;\n        Bone* bone = mSkeleton->getBone(boneName);\n        if (!bone)\n        {\n            mBoneNodesVector.push_back(nullptr);\n            Log(Debug::Error) << \"Error: RigGeometry did not find bone \" << boneName;\n            continue;\n        }\n\n        mBoneNodesVector.push_back(bone);\n    }\n\n    for (auto& pair : mBone2VertexVector->mData)\n    {\n        for (auto &weight : pair.first)\n        {\n            const std::string& boneName = weight.first.first;\n            Bone* bone = mSkeleton->getBone(boneName);\n            if (!bone)\n            {\n                mBoneNodesVector.push_back(nullptr);\n                Log(Debug::Error) << \"Error: RigGeometry did not find bone \" << boneName;\n                continue;\n            }\n\n            mBoneNodesVector.push_back(bone);\n        }\n    }\n\n    return true;\n}\n\nvoid RigGeometry::cull(osg::NodeVisitor* nv)\n{\n    if (!mSkeleton)\n    {\n        Log(Debug::Error) << \"Error: RigGeometry rendering with no skeleton, should have been initialized by UpdateVisitor\";\n        // try to recover anyway, though rendering is likely to be incorrect.\n        if (!initFromParentSkeleton(nv))\n            return;\n    }\n\n    unsigned int traversalNumber = nv->getTraversalNumber();\n    if (mLastFrameNumber == traversalNumber || (mLastFrameNumber != 0 && !mSkeleton->getActive()))\n    {\n        osg::Geometry& geom = *getGeometry(mLastFrameNumber);\n        nv->pushOntoNodePath(&geom);\n        nv->apply(geom);\n        nv->popFromNodePath();\n        return;\n    }\n    mLastFrameNumber = traversalNumber;\n    osg::Geometry& geom = *getGeometry(mLastFrameNumber);\n\n    mSkeleton->updateBoneMatrices(traversalNumber);\n\n    // skinning\n    const osg::Vec3Array* positionSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getVertexArray());\n    const osg::Vec3Array* normalSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getNormalArray());\n    const osg::Vec4Array* tangentSrc = mSourceTangents;\n\n    osg::Vec3Array* positionDst = static_cast<osg::Vec3Array*>(geom.getVertexArray());\n    osg::Vec3Array* normalDst = static_cast<osg::Vec3Array*>(geom.getNormalArray());\n    osg::Vec4Array* tangentDst = static_cast<osg::Vec4Array*>(geom.getTexCoordArray(7));\n\n    int index = mBoneSphereVector->mData.size();\n    for (auto &pair : mBone2VertexVector->mData)\n    {\n        osg::Matrixf resultMat (0, 0, 0, 0,\n                                0, 0, 0, 0,\n                                0, 0, 0, 0,\n                                0, 0, 0, 1);\n\n        for (auto &weight : pair.first)\n        {\n            Bone* bone = mBoneNodesVector[index];\n            if (bone == nullptr)\n                continue;\n\n            accumulateMatrix(weight.first.second, bone->mMatrixInSkeletonSpace, weight.second, resultMat);\n            index++;\n        }\n\n        if (mGeomToSkelMatrix)\n            resultMat *= (*mGeomToSkelMatrix);\n\n        for (auto &vertex : pair.second)\n        {\n            (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]);\n            if (normalDst)\n                (*normalDst)[vertex] = osg::Matrixf::transform3x3((*normalSrc)[vertex], resultMat);\n\n            if (tangentDst)\n            {\n                const osg::Vec4f& srcTangent = (*tangentSrc)[vertex];\n                osg::Vec3f transformedTangent = osg::Matrixf::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat);\n                (*tangentDst)[vertex] = osg::Vec4f(transformedTangent, srcTangent.w());\n            }\n        }\n    }\n\n    positionDst->dirty();\n    if (normalDst)\n        normalDst->dirty();\n    if (tangentDst)\n        tangentDst->dirty();\n\n#if OSG_MIN_VERSION_REQUIRED(3, 5, 6)\n    geom.dirtyGLObjects();\n#endif\n\n    nv->pushOntoNodePath(&geom);\n    nv->apply(geom);\n    nv->popFromNodePath();\n}\n\nvoid RigGeometry::updateBounds(osg::NodeVisitor *nv)\n{\n    if (!mSkeleton)\n    {\n        if (!initFromParentSkeleton(nv))\n            return;\n    }\n\n    if (!mSkeleton->getActive() && !mBoundsFirstFrame)\n        return;\n    mBoundsFirstFrame = false;\n\n    mSkeleton->updateBoneMatrices(nv->getTraversalNumber());\n\n    updateGeomToSkelMatrix(nv->getNodePath());\n\n    osg::BoundingBox box;\n\n    int index = 0;\n    for (auto& boundPair : mBoneSphereVector->mData)\n    {\n        Bone* bone = mBoneNodesVector[index];\n        if (bone == nullptr)\n            continue;\n\n        index++;\n        osg::BoundingSpheref bs = boundPair.second;\n        if (mGeomToSkelMatrix)\n            transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs);\n        else\n            transformBoundingSphere(bone->mMatrixInSkeletonSpace, bs);\n        box.expandBy(bs);\n    }\n\n    if (box != _boundingBox)\n    {\n        _boundingBox = box;\n        _boundingSphere = osg::BoundingSphere(_boundingBox);\n        _boundingSphereComputed = true;\n        for (unsigned int i=0; i<getNumParents(); ++i)\n            getParent(i)->dirtyBound();\n\n        for (unsigned int i = 0; i < 2; ++i)\n        {\n            osg::Geometry& geom = *mGeometry[i];\n            static_cast<CopyBoundingBoxCallback*>(geom.getComputeBoundingBoxCallback())->boundingBox = _boundingBox;\n            static_cast<CopyBoundingSphereCallback*>(geom.getComputeBoundingSphereCallback())->boundingSphere = _boundingSphere;\n            geom.dirtyBound();\n        }\n    }\n}\n\nvoid RigGeometry::updateGeomToSkelMatrix(const osg::NodePath& nodePath)\n{\n    bool foundSkel = false;\n    osg::ref_ptr<osg::RefMatrix> geomToSkelMatrix;\n    for (osg::NodePath::const_iterator it = nodePath.begin(); it != nodePath.end(); ++it)\n    {\n        osg::Node* node = *it;\n        if (!foundSkel)\n        {\n            if (node == mSkeleton)\n                foundSkel = true;\n        }\n        else\n        {\n            if (osg::Transform* trans = node->asTransform())\n            {\n                if (!geomToSkelMatrix)\n                    geomToSkelMatrix = new osg::RefMatrix;\n                trans->computeWorldToLocalMatrix(*geomToSkelMatrix, nullptr);\n            }\n        }\n    }\n    if (geomToSkelMatrix && !geomToSkelMatrix->isIdentity())\n        mGeomToSkelMatrix = geomToSkelMatrix;\n}\n\nvoid RigGeometry::setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap)\n{\n    mInfluenceMap = influenceMap;\n\n    typedef std::map<unsigned short, std::vector<BoneWeight> > Vertex2BoneMap;\n    Vertex2BoneMap vertex2BoneMap;\n    mBoneSphereVector = new BoneSphereVector;\n    mBoneSphereVector->mData.reserve(mInfluenceMap->mData.size());\n    mBone2VertexVector = new Bone2VertexVector;\n    for (auto& influencePair : mInfluenceMap->mData)\n    {\n        const std::string& boneName = influencePair.first;\n        const BoneInfluence& bi = influencePair.second;\n        mBoneSphereVector->mData.emplace_back(boneName, bi.mBoundSphere);\n\n        for (auto& weightPair: bi.mWeights)\n        {\n            std::vector<BoneWeight>& vec = vertex2BoneMap[weightPair.first];\n\n            vec.emplace_back(std::make_pair(boneName, bi.mInvBindMatrix), weightPair.second);\n        }\n    }\n\n    Bone2VertexMap bone2VertexMap;\n    for (auto& vertexPair : vertex2BoneMap)\n    {\n        bone2VertexMap[vertexPair.second].emplace_back(vertexPair.first);\n    }\n\n    mBone2VertexVector->mData.reserve(bone2VertexMap.size());\n    mBone2VertexVector->mData.assign(bone2VertexMap.begin(), bone2VertexMap.end());\n}\n\nvoid RigGeometry::accept(osg::NodeVisitor &nv)\n{\n    if (!nv.validNodeMask(*this))\n        return;\n\n    nv.pushOntoNodePath(this);\n\n    if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)\n        cull(&nv);\n    else if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)\n        updateBounds(&nv);\n    else\n        nv.apply(*this);\n\n    nv.popFromNodePath();\n}\n\nvoid RigGeometry::accept(osg::PrimitiveFunctor& func) const\n{\n    getGeometry(mLastFrameNumber)->accept(func);\n}\n\nosg::Geometry* RigGeometry::getGeometry(unsigned int frame) const\n{\n    return mGeometry[frame%2].get();\n}\n\n\n}\n"
  },
  {
    "path": "components/sceneutil/riggeometry.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_NIFOSG_RIGGEOMETRY_H\n#define OPENMW_COMPONENTS_NIFOSG_RIGGEOMETRY_H\n\n#include <osg/Geometry>\n#include <osg/Matrixf>\n\nnamespace SceneUtil\n{\n    class Skeleton;\n    class Bone;\n\n    /// @brief Mesh skinning implementation.\n    /// @note A RigGeometry may be attached directly to a Skeleton, or somewhere below a Skeleton.\n    /// Note though that the RigGeometry ignores any transforms below the Skeleton, so the attachment point is not that important.\n    /// @note The internal Geometry used for rendering is double buffered, this allows updates to be done in a thread safe way while\n    /// not compromising rendering performance. This is crucial when using osg's default threading model of DrawThreadPerContext.\n    class RigGeometry : public osg::Drawable\n    {\n    public:\n        RigGeometry();\n        RigGeometry(const RigGeometry& copy, const osg::CopyOp& copyop);\n\n        META_Object(SceneUtil, RigGeometry)\n\n        // Currently empty as this is difficult to implement. Technically we would need to compile both internal geometries in separate frames but this method is only called once. Alternatively we could compile just the static parts of the model.\n        void compileGLObjects(osg::RenderInfo& renderInfo) const override {}\n\n        struct BoneInfluence\n        {\n            osg::Matrixf mInvBindMatrix;\n            osg::BoundingSpheref mBoundSphere;\n            // <vertex index, weight>\n            std::vector<std::pair<unsigned short, float>> mWeights;\n        };\n\n        struct InfluenceMap : public osg::Referenced\n        {\n            std::vector<std::pair<std::string, BoneInfluence>> mData;\n        };\n\n        void setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap);\n\n        /// Initialize this geometry from the source geometry.\n        /// @note The source geometry will not be modified.\n        void setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeom);\n\n        osg::ref_ptr<osg::Geometry> getSourceGeometry() const;\n\n        void accept(osg::NodeVisitor &nv) override;\n        bool supports(const osg::PrimitiveFunctor&) const override{ return true; }\n        void accept(osg::PrimitiveFunctor&) const override;\n\n        struct CopyBoundingBoxCallback : osg::Drawable::ComputeBoundingBoxCallback\n        {\n            osg::BoundingBox boundingBox;\n\n            osg::BoundingBox computeBound(const osg::Drawable&) const override { return boundingBox; }\n        };\n\n        struct CopyBoundingSphereCallback : osg::Node::ComputeBoundingSphereCallback\n        {\n            osg::BoundingSphere boundingSphere;\n\n            osg::BoundingSphere computeBound(const osg::Node&) const override { return boundingSphere; }\n        };\n\n    private:\n        void cull(osg::NodeVisitor* nv);\n        void updateBounds(osg::NodeVisitor* nv);\n\n        osg::ref_ptr<osg::Geometry> mGeometry[2];\n        osg::Geometry* getGeometry(unsigned int frame) const;\n\n        osg::ref_ptr<osg::Geometry> mSourceGeometry;\n        osg::ref_ptr<const osg::Vec4Array> mSourceTangents;\n        Skeleton* mSkeleton;\n\n        osg::ref_ptr<osg::RefMatrix> mGeomToSkelMatrix;\n\n        osg::ref_ptr<InfluenceMap> mInfluenceMap;\n\n        typedef std::pair<std::string, osg::Matrixf> BoneBindMatrixPair;\n\n        typedef std::pair<BoneBindMatrixPair, float> BoneWeight;\n\n        typedef std::vector<unsigned short> VertexList;\n\n        typedef std::map<std::vector<BoneWeight>, VertexList> Bone2VertexMap;\n\n        struct Bone2VertexVector : public osg::Referenced\n        {\n            std::vector<std::pair<std::vector<BoneWeight>, VertexList>> mData;\n        };\n        osg::ref_ptr<Bone2VertexVector> mBone2VertexVector;\n\n        struct BoneSphereVector : public osg::Referenced\n        {\n            std::vector<std::pair<std::string, osg::BoundingSpheref>> mData;\n        };\n        osg::ref_ptr<BoneSphereVector> mBoneSphereVector;\n        std::vector<Bone*> mBoneNodesVector;\n\n        unsigned int mLastFrameNumber;\n        bool mBoundsFirstFrame;\n\n        bool initFromParentSkeleton(osg::NodeVisitor* nv);\n\n        void updateGeomToSkelMatrix(const osg::NodePath& nodePath);\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/serialize.cpp",
    "content": "#include \"serialize.hpp\"\n\n#include <osgDB/ObjectWrapper>\n#include <osgDB/Registry>\n\n#include <components/nifosg/matrixtransform.hpp>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include <components/sceneutil/skeleton.hpp>\n#include <components/sceneutil/riggeometry.hpp>\n#include <components/sceneutil/morphgeometry.hpp>\n\nnamespace SceneUtil\n{\n\ntemplate <class Cls>\nstatic osg::Object* createInstanceFunc() { return new Cls; }\n\nclass PositionAttitudeTransformSerializer : public osgDB::ObjectWrapper\n{\npublic:\n    PositionAttitudeTransformSerializer()\n        : osgDB::ObjectWrapper(createInstanceFunc<SceneUtil::PositionAttitudeTransform>, \"SceneUtil::PositionAttitudeTransform\", \"osg::Object osg::Node osg::Group osg::Transform SceneUtil::PositionAttitudeTransform\")\n    {\n        addSerializer( new osgDB::PropByRefSerializer< SceneUtil::PositionAttitudeTransform, osg::Vec3f >(\n            \"position\", osg::Vec3f(), &SceneUtil::PositionAttitudeTransform::getPosition, &SceneUtil::PositionAttitudeTransform::setPosition), osgDB::BaseSerializer::RW_VEC3F );\n        addSerializer( new osgDB::PropByRefSerializer< SceneUtil::PositionAttitudeTransform, osg::Quat >(\n            \"attitude\", osg::Quat(), &SceneUtil::PositionAttitudeTransform::getAttitude, &SceneUtil::PositionAttitudeTransform::setAttitude), osgDB::BaseSerializer::RW_QUAT );\n        addSerializer( new osgDB::PropByRefSerializer< SceneUtil::PositionAttitudeTransform, osg::Vec3f >(\n            \"scale\", osg::Vec3f(), &SceneUtil::PositionAttitudeTransform::getScale, &SceneUtil::PositionAttitudeTransform::setScale), osgDB::BaseSerializer::RW_VEC3F );\n    }\n};\n\nclass SkeletonSerializer : public osgDB::ObjectWrapper\n{\npublic:\n    SkeletonSerializer()\n        : osgDB::ObjectWrapper(createInstanceFunc<SceneUtil::Skeleton>, \"SceneUtil::Skeleton\", \"osg::Object osg::Node osg::Group SceneUtil::Skeleton\")\n    {\n    }\n};\n\nclass RigGeometrySerializer : public osgDB::ObjectWrapper\n{\npublic:\n    RigGeometrySerializer()\n        : osgDB::ObjectWrapper(createInstanceFunc<SceneUtil::RigGeometry>, \"SceneUtil::RigGeometry\", \"osg::Object osg::Node osg::Drawable SceneUtil::RigGeometry\")\n    {\n    }\n};\n\nclass MorphGeometrySerializer : public osgDB::ObjectWrapper\n{\npublic:\n    MorphGeometrySerializer()\n        : osgDB::ObjectWrapper(createInstanceFunc<SceneUtil::MorphGeometry>, \"SceneUtil::MorphGeometry\", \"osg::Object osg::Node osg::Drawable SceneUtil::MorphGeometry\")\n    {\n    }\n};\n\nclass LightManagerSerializer : public osgDB::ObjectWrapper\n{\npublic:\n    LightManagerSerializer()\n        : osgDB::ObjectWrapper(createInstanceFunc<osg::Group>, \"SceneUtil::LightManager\", \"osg::Object osg::Node osg::Group SceneUtil::LightManager\")\n    {\n    }\n};\n\nclass CameraRelativeTransformSerializer : public osgDB::ObjectWrapper\n{\npublic:\n    CameraRelativeTransformSerializer()\n        : osgDB::ObjectWrapper(createInstanceFunc<osg::Group>, \"MWRender::CameraRelativeTransform\", \"osg::Object osg::Node osg::Group MWRender::CameraRelativeTransform\")\n    {\n    }\n};\n\nclass MatrixTransformSerializer : public osgDB::ObjectWrapper\n{\npublic:\n    MatrixTransformSerializer()\n        : osgDB::ObjectWrapper(createInstanceFunc<NifOsg::MatrixTransform>, \"NifOsg::MatrixTransform\", \"osg::Object osg::Node osg::Transform osg::MatrixTransform NifOsg::MatrixTransform\")\n    {\n    }\n};\n\nosgDB::ObjectWrapper* makeDummySerializer(const std::string& classname)\n{\n    return new osgDB::ObjectWrapper(createInstanceFunc<osg::DummyObject>, classname, \"osg::Object\");\n}\n\nclass GeometrySerializer : public osgDB::ObjectWrapper\n{\npublic:\n    GeometrySerializer()\n        : osgDB::ObjectWrapper(createInstanceFunc<osg::Drawable>, \"osg::Geometry\", \"osg::Object osg::Drawable osg::Geometry\")\n    {\n    }\n};\n\nvoid registerSerializers()\n{\n    static bool done = false;\n    if (!done)\n    {\n        osgDB::ObjectWrapperManager* mgr = osgDB::Registry::instance()->getObjectWrapperManager();\n        mgr->addWrapper(new PositionAttitudeTransformSerializer);\n        mgr->addWrapper(new SkeletonSerializer);\n        mgr->addWrapper(new RigGeometrySerializer);\n        mgr->addWrapper(new MorphGeometrySerializer);\n        mgr->addWrapper(new LightManagerSerializer);\n        mgr->addWrapper(new CameraRelativeTransformSerializer);\n        mgr->addWrapper(new MatrixTransformSerializer);\n\n        // Don't serialize Geometry data as we are more interested in the overall structure rather than tons of vertex data that would make the file large and hard to read.\n        mgr->removeWrapper(mgr->findWrapper(\"osg::Geometry\"));\n        mgr->addWrapper(new GeometrySerializer);\n\n        // ignore the below for now to avoid warning spam\n        const char* ignore[] = {\n            \"MWRender::PtrHolder\",\n            \"Resource::TemplateRef\",\n            \"Resource::TemplateMultiRef\",\n            \"SceneUtil::CompositeStateSetUpdater\",\n            \"SceneUtil::LightListCallback\",\n            \"SceneUtil::LightManagerUpdateCallback\",\n            \"SceneUtil::UpdateRigBounds\",\n            \"SceneUtil::UpdateRigGeometry\",\n            \"SceneUtil::LightSource\",\n            \"SceneUtil::StateSetUpdater\",\n            \"SceneUtil::DisableLight\",\n            \"SceneUtil::MWShadowTechnique\",\n            \"SceneUtil::TextKeyMapHolder\",\n            \"Shader::RemovedAlphaFunc\",\n            \"NifOsg::LightManagerStateAttribute\",\n            \"NifOsg::FlipController\",\n            \"NifOsg::KeyframeController\",\n            \"NifOsg::Emitter\",\n            \"NifOsg::ParticleColorAffector\",\n            \"NifOsg::ParticleSystem\",\n            \"NifOsg::GravityAffector\",\n            \"NifOsg::GrowFadeAffector\",\n            \"NifOsg::InverseWorldMatrix\",\n            \"NifOsg::StaticBoundingBoxCallback\",\n            \"NifOsg::GeomMorpherController\",\n            \"NifOsg::UpdateMorphGeometry\",\n            \"NifOsg::UVController\",\n            \"NifOsg::VisController\",\n            \"osgMyGUI::Drawable\",\n            \"osg::DrawCallback\",\n            \"osg::UniformBufferObject\",\n            \"osgOQ::ClearQueriesCallback\",\n            \"osgOQ::RetrieveQueriesCallback\",\n            \"osg::DummyObject\"\n        };\n        for (size_t i=0; i<sizeof(ignore)/sizeof(ignore[0]); ++i)\n        {\n            mgr->addWrapper(makeDummySerializer(ignore[i]));\n        }\n\n\n        done = true;\n    }\n}\n\n}\n"
  },
  {
    "path": "components/sceneutil/serialize.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_SERIALIZE_H\n#define OPENMW_COMPONENTS_SCENEUTIL_SERIALIZE_H\n\nnamespace SceneUtil\n{\n\n    /// Register osg node serializers for certain SceneUtil classes if not already done so\n    void registerSerializers();\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/shadow.cpp",
    "content": "#include \"shadow.hpp\"\n\n#include <osgShadow/ShadowedScene>\n\n#include <components/misc/stringops.hpp>\n#include <components/settings/settings.hpp>\n\nnamespace SceneUtil\n{\n    using namespace osgShadow;\n\n    void ShadowManager::setupShadowSettings()\n    {\n        mEnableShadows = Settings::Manager::getBool(\"enable shadows\", \"Shadows\");\n\n        if (!mEnableShadows)\n        {\n            mShadowTechnique->disableShadows();\n            return;\n        }\n        \n        mShadowTechnique->enableShadows();\n\n        mShadowSettings->setLightNum(0);\n        mShadowSettings->setReceivesShadowTraversalMask(~0u);\n\n        int numberOfShadowMapsPerLight = Settings::Manager::getInt(\"number of shadow maps\", \"Shadows\");\n        numberOfShadowMapsPerLight = std::max(1, std::min(numberOfShadowMapsPerLight, 8));\n\n        mShadowSettings->setNumShadowMapsPerLight(numberOfShadowMapsPerLight);\n        mShadowSettings->setBaseShadowTextureUnit(8 - numberOfShadowMapsPerLight);\n\n        const float maximumShadowMapDistance = Settings::Manager::getFloat(\"maximum shadow map distance\", \"Shadows\");\n        if (maximumShadowMapDistance > 0)\n        {\n            const float shadowFadeStart = std::min(std::max(0.f, Settings::Manager::getFloat(\"shadow fade start\", \"Shadows\")), 1.f);\n            mShadowSettings->setMaximumShadowMapDistance(maximumShadowMapDistance);\n            mShadowTechnique->setShadowFadeStart(maximumShadowMapDistance * shadowFadeStart);\n        }\n\n        mShadowSettings->setMinimumShadowMapNearFarRatio(Settings::Manager::getFloat(\"minimum lispsm near far ratio\", \"Shadows\"));\n\n        std::string computeSceneBounds = Settings::Manager::getString(\"compute scene bounds\", \"Shadows\");\n        if (Misc::StringUtils::lowerCase(computeSceneBounds) == \"primitives\")\n            mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES);\n        else if (Misc::StringUtils::lowerCase(computeSceneBounds) == \"bounds\")\n            mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);\n\n        int mapres = Settings::Manager::getInt(\"shadow map resolution\", \"Shadows\");\n        mShadowSettings->setTextureSize(osg::Vec2s(mapres, mapres));\n\n        mShadowTechnique->setSplitPointUniformLogarithmicRatio(Settings::Manager::getFloat(\"split point uniform logarithmic ratio\", \"Shadows\"));\n        mShadowTechnique->setSplitPointDeltaBias(Settings::Manager::getFloat(\"split point bias\", \"Shadows\"));\n\n        mShadowTechnique->setPolygonOffset(Settings::Manager::getFloat(\"polygon offset factor\", \"Shadows\"), Settings::Manager::getFloat(\"polygon offset units\", \"Shadows\"));\n\n        if (Settings::Manager::getBool(\"use front face culling\", \"Shadows\"))\n            mShadowTechnique->enableFrontFaceCulling();\n        else\n            mShadowTechnique->disableFrontFaceCulling();\n\n        if (Settings::Manager::getBool(\"allow shadow map overlap\", \"Shadows\"))\n            mShadowSettings->setMultipleShadowMapHint(osgShadow::ShadowSettings::CASCADED);\n        else\n            mShadowSettings->setMultipleShadowMapHint(osgShadow::ShadowSettings::PARALLEL_SPLIT);\n\n        if (Settings::Manager::getBool(\"enable debug hud\", \"Shadows\"))\n            mShadowTechnique->enableDebugHUD();\n        else\n            mShadowTechnique->disableDebugHUD();\n    }\n\n    void ShadowManager::disableShadowsForStateSet(osg::ref_ptr<osg::StateSet> stateset)\n    {\n        if (!Settings::Manager::getBool(\"enable shadows\", \"Shadows\"))\n            return;\n\n        int numberOfShadowMapsPerLight = Settings::Manager::getInt(\"number of shadow maps\", \"Shadows\");\n        numberOfShadowMapsPerLight = std::max(1, std::min(numberOfShadowMapsPerLight, 8));\n\n        int baseShadowTextureUnit = 8 - numberOfShadowMapsPerLight;\n        \n        osg::ref_ptr<osg::Image> fakeShadowMapImage = new osg::Image();\n        fakeShadowMapImage->allocateImage(1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT);\n        *(float*)fakeShadowMapImage->data() = std::numeric_limits<float>::infinity();\n        osg::ref_ptr<osg::Texture> fakeShadowMapTexture = new osg::Texture2D(fakeShadowMapImage);\n        fakeShadowMapTexture->setShadowComparison(true);\n        fakeShadowMapTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS);\n        for (int i = baseShadowTextureUnit; i < baseShadowTextureUnit + numberOfShadowMapsPerLight; ++i)\n        {\n            stateset->setTextureAttributeAndModes(i, fakeShadowMapTexture, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);\n            stateset->addUniform(new osg::Uniform((\"shadowTexture\" + std::to_string(i - baseShadowTextureUnit)).c_str(), i));\n            stateset->addUniform(new osg::Uniform((\"shadowTextureUnit\" + std::to_string(i - baseShadowTextureUnit)).c_str(), i));\n        }\n    }\n\n    ShadowManager::ShadowManager(osg::ref_ptr<osg::Group> sceneRoot, osg::ref_ptr<osg::Group> rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager) : mShadowedScene(new osgShadow::ShadowedScene),\n        mShadowTechnique(new MWShadowTechnique),\n        mOutdoorShadowCastingMask(outdoorShadowCastingMask),\n        mIndoorShadowCastingMask(indoorShadowCastingMask)\n    {\n        mShadowedScene->setShadowTechnique(mShadowTechnique);\n\n        mShadowedScene->addChild(sceneRoot);\n        rootNode->addChild(mShadowedScene);\n        mShadowedScene->setNodeMask(sceneRoot->getNodeMask());\n\n        mShadowSettings = mShadowedScene->getShadowSettings();\n        setupShadowSettings();\n\n        mShadowTechnique->setupCastingShader(shaderManager);\n\n        enableOutdoorMode();\n    }\n\n    Shader::ShaderManager::DefineMap ShadowManager::getShadowDefines()\n    {\n        if (!mEnableShadows)\n            return getShadowsDisabledDefines();\n\n        Shader::ShaderManager::DefineMap definesWithShadows;\n\n        definesWithShadows[\"shadows_enabled\"] = \"1\";\n\n        for (unsigned int i = 0; i < mShadowSettings->getNumShadowMapsPerLight(); ++i)\n            definesWithShadows[\"shadow_texture_unit_list\"] += std::to_string(i) + \",\";\n        // remove extra comma\n        definesWithShadows[\"shadow_texture_unit_list\"] = definesWithShadows[\"shadow_texture_unit_list\"].substr(0, definesWithShadows[\"shadow_texture_unit_list\"].length() - 1);\n\n        definesWithShadows[\"shadowMapsOverlap\"] = Settings::Manager::getBool(\"allow shadow map overlap\", \"Shadows\") ? \"1\" : \"0\";\n\n        definesWithShadows[\"useShadowDebugOverlay\"] = Settings::Manager::getBool(\"enable debug overlay\", \"Shadows\") ? \"1\" : \"0\";\n\n        // switch this to reading settings if it's ever exposed to the user\n        definesWithShadows[\"perspectiveShadowMaps\"] = mShadowSettings->getShadowMapProjectionHint() == ShadowSettings::PERSPECTIVE_SHADOW_MAP ? \"1\" : \"0\";\n\n        definesWithShadows[\"disableNormalOffsetShadows\"] = Settings::Manager::getFloat(\"normal offset distance\", \"Shadows\") == 0.0 ? \"1\" : \"0\";\n\n        definesWithShadows[\"shadowNormalOffset\"] = std::to_string(Settings::Manager::getFloat(\"normal offset distance\", \"Shadows\"));\n\n        definesWithShadows[\"limitShadowMapDistance\"] = Settings::Manager::getFloat(\"maximum shadow map distance\", \"Shadows\") > 0 ? \"1\" : \"0\";\n\n        return definesWithShadows;\n    }\n\n    Shader::ShaderManager::DefineMap ShadowManager::getShadowsDisabledDefines()\n    {\n        Shader::ShaderManager::DefineMap definesWithoutShadows;\n\n        definesWithoutShadows[\"shadows_enabled\"] = \"0\";\n\n        definesWithoutShadows[\"shadow_texture_unit_list\"] = \"\";\n\n        definesWithoutShadows[\"shadowMapsOverlap\"] = \"0\";\n\n        definesWithoutShadows[\"useShadowDebugOverlay\"] = \"0\";\n\n        definesWithoutShadows[\"perspectiveShadowMaps\"] = \"0\";\n\n        definesWithoutShadows[\"disableNormalOffsetShadows\"] = \"0\";\n\n        definesWithoutShadows[\"shadowNormalOffset\"] = \"0.0\";\n\n        definesWithoutShadows[\"limitShadowMapDistance\"] = \"0\";\n\n        return definesWithoutShadows;\n    }\n\n    void ShadowManager::enableIndoorMode()\n    {\n        if (Settings::Manager::getBool(\"enable indoor shadows\", \"Shadows\"))\n            mShadowSettings->setCastsShadowTraversalMask(mIndoorShadowCastingMask);\n        else\n            mShadowTechnique->disableShadows(true);\n    }\n\n    void ShadowManager::enableOutdoorMode()\n    {\n        if (mEnableShadows)\n            mShadowTechnique->enableShadows();\n        mShadowSettings->setCastsShadowTraversalMask(mOutdoorShadowCastingMask);\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/shadow.hpp",
    "content": "#ifndef COMPONENTS_SCENEUTIL_SHADOW_H\n#define COMPONENTS_SCENEUTIL_SHADOW_H\n\n#include <osgShadow/ShadowSettings>\n#include <osgShadow/ShadowedScene>\n\n#include <components/shader/shadermanager.hpp>\n\n#include \"mwshadowtechnique.hpp\"\n\nnamespace SceneUtil\n{\n    class ShadowManager\n    {\n    public:\n        static void disableShadowsForStateSet(osg::ref_ptr<osg::StateSet> stateSet);\n\n        static Shader::ShaderManager::DefineMap getShadowsDisabledDefines();\n\n        ShadowManager(osg::ref_ptr<osg::Group> sceneRoot, osg::ref_ptr<osg::Group> rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager);\n\n        void setupShadowSettings();\n\n        Shader::ShaderManager::DefineMap getShadowDefines();\n\n        void enableIndoorMode();\n\n        void enableOutdoorMode();\n    protected:\n        bool mEnableShadows;\n\n        osg::ref_ptr<osgShadow::ShadowedScene> mShadowedScene;\n        osg::ref_ptr<osgShadow::ShadowSettings> mShadowSettings;\n        osg::ref_ptr<MWShadowTechnique> mShadowTechnique;\n\n        unsigned int mOutdoorShadowCastingMask;\n        unsigned int mIndoorShadowCastingMask;\n    };\n}\n\n#endif //COMPONENTS_SCENEUTIL_SHADOW_H\n"
  },
  {
    "path": "components/sceneutil/shadowsbin.cpp",
    "content": "#include \"shadowsbin.hpp\"\n#include <unordered_set>\n#include <osg/StateSet>\n#include <osg/AlphaFunc>\n#include <osg/Material>\n#include <osg/Program>\n#include <osgUtil/StateGraph>\n\nusing namespace osgUtil;\n\nnamespace\n{\n    template <typename T>\n    inline void accumulateState(T& currentValue, T newValue, bool& isOverride, unsigned int overrideFlags)\n    {\n        if (isOverride && !(overrideFlags & osg::StateAttribute::PROTECTED)) return;\n\n        if (overrideFlags & osg::StateAttribute::OVERRIDE)\n            isOverride = true;\n\n        currentValue = newValue;\n    }\n\n    inline void accumulateModeState(const osg::StateSet* ss, bool& currentValue, bool& isOverride, int mode)\n    {\n        const osg::StateSet::ModeList& l = ss->getModeList();\n        osg::StateSet::ModeList::const_iterator mf = l.find(mode);\n        if (mf == l.end())\n            return;\n        unsigned int flags = mf->second;\n        bool newValue = flags & osg::StateAttribute::ON;\n        accumulateState(currentValue, newValue, isOverride, flags);\n    }\n\n    inline bool materialNeedShadows(osg::Material* m)\n    {\n        // I'm pretty sure this needs to check the colour mode - vertex colours might override this value.\n        return m->getDiffuse(osg::Material::FRONT).a() > 0.5;\n    }\n}\n\nnamespace SceneUtil\n{\n\nstd::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER + 1> ShadowsBin::sCastingPrograms = {\n    nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr\n};\n\nShadowsBin::ShadowsBin()\n{\n    mNoTestStateSet = new osg::StateSet;\n    mNoTestStateSet->addUniform(new osg::Uniform(\"useDiffuseMapForShadowAlpha\", false));\n    mNoTestStateSet->addUniform(new osg::Uniform(\"alphaTestShadows\", false));\n\n    mShaderAlphaTestStateSet = new osg::StateSet;\n    mShaderAlphaTestStateSet->addUniform(new osg::Uniform(\"alphaTestShadows\", true));\n    mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE);\n\n    for (size_t i = 0; i < sCastingPrograms.size(); ++i)\n    {\n        mAlphaFuncShaders[i] = new osg::StateSet;\n        mAlphaFuncShaders[i]->setAttribute(sCastingPrograms[i], osg::StateAttribute::ON | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE);\n    }\n}\n\nStateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set<StateGraph*>& uninterestingCache, bool cullFaceOverridden)\n{\n    std::vector<StateGraph*> return_path;\n    State state;\n    StateGraph* sg_new = sg;\n    do\n    {\n        if (uninterestingCache.find(sg_new) != uninterestingCache.end())\n            break;\n        return_path.push_back(sg_new);\n        sg_new = sg_new->_parent;\n    } while (sg_new && sg_new != root);\n\n    for(auto itr=return_path.rbegin(); itr!=return_path.rend(); ++itr)\n    {\n        const osg::StateSet* ss = (*itr)->getStateSet();\n        if (!ss)\n            continue;\n\n        accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND);\n\n        const osg::StateSet::AttributeList& attributes = ss->getAttributeList();\n        osg::StateSet::AttributeList::const_iterator found = attributes.find(std::make_pair(osg::StateAttribute::MATERIAL, 0));\n        if (found != attributes.end())\n        {\n            const osg::StateSet::RefAttributePair& rap = found->second;\n            accumulateState(state.mMaterial, static_cast<osg::Material*>(rap.first.get()), state.mMaterialOverride, rap.second);\n            if (state.mMaterial && !materialNeedShadows(state.mMaterial))\n                state.mMaterial = nullptr;\n        }\n\n        found = attributes.find(std::make_pair(osg::StateAttribute::ALPHAFUNC, 0));\n        if (found != attributes.end())\n        {\n            // As force shaders is on, we know this is really a RemovedAlphaFunc\n            const osg::StateSet::RefAttributePair& rap = found->second;\n            accumulateState(state.mAlphaFunc, static_cast<osg::AlphaFunc*>(rap.first.get()), state.mAlphaFuncOverride, rap.second);\n        }\n\n        if (!cullFaceOverridden)\n        {\n            // osg::FrontFace specifies triangle winding, not front-face culling. We can't safely reparent anything under it unless GL_CULL_FACE is off or we flip face culling.\n            found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0));\n            if (found != attributes.end())\n                state.mImportantState = true;\n        }\n\n        if ((*itr) != sg && !state.interesting())\n            uninterestingCache.insert(*itr);\n    }\n\n    if (!state.needShadows())\n        return nullptr;\n\n    if (!state.needTexture() && !state.mImportantState)\n    {\n        for (RenderLeaf* leaf : sg->_leaves)\n        {\n            leaf->_parent = root;\n            root->_leaves.push_back(leaf);\n        }\n        return nullptr;\n    }\n\n    if (state.mAlphaBlend)\n    {\n        sg_new = sg->find_or_insert(mShaderAlphaTestStateSet);\n        sg_new->_leaves = std::move(sg->_leaves);\n        for (RenderLeaf* leaf : sg_new->_leaves)\n            leaf->_parent = sg_new;\n        sg = sg_new;\n    }\n\n    // GL_ALWAYS is set by default by mwshadowtechnique\n    if (state.mAlphaFunc && state.mAlphaFunc->getFunction() != GL_ALWAYS)\n    {\n        sg_new = sg->find_or_insert(mAlphaFuncShaders[state.mAlphaFunc->getFunction() - GL_NEVER]);\n        sg_new->_leaves = std::move(sg->_leaves);\n        for (RenderLeaf* leaf : sg_new->_leaves)\n            leaf->_parent = sg_new;\n        sg = sg_new;\n    }\n\n    return sg;\n}\n\nvoid ShadowsBin::addPrototype(const std::string & name, const std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER + 1>& castingPrograms)\n{\n    sCastingPrograms = castingPrograms;\n    osg::ref_ptr<osgUtil::RenderBin> bin(new ShadowsBin);\n    osgUtil::RenderBin::addRenderBinPrototype(name, bin);\n}\n\ninline bool ShadowsBin::State::needTexture() const\n{\n    return mAlphaBlend || (mAlphaFunc && mAlphaFunc->getFunction() != GL_ALWAYS);\n}\n\nbool ShadowsBin::State::needShadows() const\n{\n    if (mAlphaFunc && mAlphaFunc->getFunction() == GL_NEVER)\n        return false;\n    // other alpha func + material combinations might be skippable\n    if (mAlphaBlend && mMaterial)\n        return materialNeedShadows(mMaterial);\n    return true;\n}\n\nvoid ShadowsBin::sortImplementation()\n{\n    // The cull visitor contains a stategraph.\n    // When a stateset is pushed, it's added/found as a child of the current stategraph node, then that node becomes the new current stategraph node.\n    // When a drawable is added, the current stategraph node is added to the current renderbin (if it's not there already) and the drawable is added as a renderleaf to the stategraph\n    // This means our list only contains stategraph nodes with directly-attached renderleaves, but they might have parents with more state set that needs to be considered.\n    if (!_stateGraphList.size())\n        return;\n    StateGraph* root = _stateGraphList[0];\n    while (root->_parent)\n    {\n        root = root->_parent;\n        const osg::StateSet* ss = root->getStateSet();\n        if (ss->getMode(GL_NORMALIZE) & osg::StateAttribute::ON // that is root stategraph of renderingmanager cpp\n           || ss->getAttribute(osg::StateAttribute::VIEWPORT)) // fallback to rendertarget's sg just in case\n            break;\n        if (!root->_parent)\n            return;\n    }\n    StateGraph* noTestRoot = root->find_or_insert(mNoTestStateSet.get());\n    // noTestRoot is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state\n\n    bool cullFaceOverridden = false;\n    while (root->_parent)\n    {\n        root = root->_parent;\n        if (!root->getStateSet())\n            continue;\n        unsigned int cullFaceFlags = root->getStateSet()->getMode(GL_CULL_FACE);\n        if (cullFaceFlags & osg::StateAttribute::OVERRIDE && !(cullFaceFlags & osg::StateAttribute::ON))\n        {\n            cullFaceOverridden = true;\n            break;\n        }\n    }\n\n    noTestRoot->_leaves.reserve(_stateGraphList.size());\n    StateGraphList newList;\n    std::unordered_set<StateGraph*> uninterestingCache;\n    for (StateGraph* graph : _stateGraphList)\n    {\n        // Render leaves which shouldn't use the diffuse map for shadow alpha but do cast shadows become children of root, so graph is now empty. Don't add to newList.\n        // Graphs containing just render leaves which don't cast shadows are discarded. Don't add to newList.\n        // Graphs containing other leaves need to be in newList.\n        StateGraph* graphToAdd = cullStateGraph(graph, noTestRoot, uninterestingCache, cullFaceOverridden);\n        if (graphToAdd)\n            newList.push_back(graphToAdd);\n    }\n    if (!noTestRoot->_leaves.empty())\n        newList.push_back(noTestRoot);\n    _stateGraphList = newList;\n}\n\n}\n"
  },
  {
    "path": "components/sceneutil/shadowsbin.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H\n#define OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H\n#include <array>\n#include <unordered_set>\n#include <osgUtil/RenderBin>\n\nnamespace osg\n{\n    class Material;\n    class AlphaFunc;\n}\n\nnamespace SceneUtil\n{\n\n    /// renderbin which culls redundant state for shadow map rendering\n    class ShadowsBin : public osgUtil::RenderBin\n    {\n    private:\n        static std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER + 1> sCastingPrograms;\n\n        osg::ref_ptr<osg::StateSet> mNoTestStateSet;\n        osg::ref_ptr<osg::StateSet> mShaderAlphaTestStateSet;\n\n        std::array<osg::ref_ptr<osg::StateSet>, GL_ALWAYS - GL_NEVER + 1> mAlphaFuncShaders;\n    public:\n        META_Object(SceneUtil, ShadowsBin)\n        ShadowsBin();\n        ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop)\n            : osgUtil::RenderBin(rhs, copyop)\n            , mNoTestStateSet(rhs.mNoTestStateSet)\n            , mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet)\n            , mAlphaFuncShaders(rhs.mAlphaFuncShaders)\n            {}\n\n        void sortImplementation() override;\n\n        struct State\n        {\n            State()\n                : mAlphaBlend(false)\n                , mAlphaBlendOverride(false)\n                , mAlphaFunc(nullptr)\n                , mAlphaFuncOverride(false)\n                , mMaterial(nullptr)\n                , mMaterialOverride(false)\n                , mImportantState(false)\n                {}\n\n            bool mAlphaBlend;\n            bool mAlphaBlendOverride;\n            osg::AlphaFunc* mAlphaFunc;\n            bool mAlphaFuncOverride;\n            osg::Material* mMaterial;\n            bool mMaterialOverride;\n            bool mImportantState;\n            bool needTexture() const;\n            bool needShadows() const;\n            // A state is interesting if there's anything about it that might affect whether we can optimise child state\n            bool interesting() const\n            {\n                return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaFuncOverride || mMaterialOverride || mImportantState;\n            }\n        };\n\n        osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set<osgUtil::StateGraph*>& uninteresting, bool cullFaceOverridden);\n\n        static void addPrototype(const std::string& name, const std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER + 1>& castingPrograms);\n    };\n\n    class ShadowsBinAdder\n    {\n        public:\n        ShadowsBinAdder(const std::string& name, const std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER + 1>& castingPrograms){ ShadowsBin::addPrototype(name, castingPrograms); }\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/skeleton.cpp",
    "content": "#include \"skeleton.hpp\"\n\n#include <osg/Transform>\n#include <osg/MatrixTransform>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n\nnamespace SceneUtil\n{\n\nclass InitBoneCacheVisitor : public osg::NodeVisitor\n{\npublic:\n    InitBoneCacheVisitor(std::map<std::string, std::pair<osg::NodePath, osg::MatrixTransform*> >& cache)\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        , mCache(cache)\n    {\n    }\n\n    void apply(osg::Transform &node) override\n    {\n        osg::MatrixTransform* bone = node.asMatrixTransform();\n        if (!bone)\n            return;\n\n        mCache[Misc::StringUtils::lowerCase(bone->getName())] = std::make_pair(getNodePath(), bone);\n\n        traverse(node);\n    }\nprivate:\n    std::map<std::string, std::pair<osg::NodePath, osg::MatrixTransform*> >& mCache;\n};\n\nSkeleton::Skeleton()\n    : mBoneCacheInit(false)\n    , mNeedToUpdateBoneMatrices(true)\n    , mActive(Active)\n    , mLastFrameNumber(0)\n    , mLastCullFrameNumber(0)\n{\n\n}\n\nSkeleton::Skeleton(const Skeleton &copy, const osg::CopyOp &copyop)\n    : osg::Group(copy, copyop)\n    , mBoneCacheInit(false)\n    , mNeedToUpdateBoneMatrices(true)\n    , mActive(copy.mActive)\n    , mLastFrameNumber(0)\n    , mLastCullFrameNumber(0)\n{\n\n}\n\nBone* Skeleton::getBone(const std::string &name)\n{\n    if (!mBoneCacheInit)\n    {\n        InitBoneCacheVisitor visitor(mBoneCache);\n        accept(visitor);\n        mBoneCacheInit = true;\n    }\n\n    BoneCache::iterator found = mBoneCache.find(Misc::StringUtils::lowerCase(name));\n    if (found == mBoneCache.end())\n        return nullptr;\n\n    // find or insert in the bone hierarchy\n\n    if (!mRootBone.get())\n    {\n        mRootBone.reset(new Bone);\n    }\n\n    const osg::NodePath& path = found->second.first;\n    Bone* bone = mRootBone.get();\n    for (osg::NodePath::const_iterator it = path.begin(); it != path.end(); ++it)\n    {\n        osg::MatrixTransform* matrixTransform = dynamic_cast<osg::MatrixTransform*>(*it);\n        if (!matrixTransform)\n            continue;\n\n        Bone* child = nullptr;\n        for (unsigned int i=0; i<bone->mChildren.size(); ++i)\n        {\n            if (bone->mChildren[i]->mNode == *it)\n            {\n                child = bone->mChildren[i];\n                break;\n            }\n        }\n\n        if (!child)\n        {\n            child = new Bone;\n            bone->mChildren.push_back(child);\n            mNeedToUpdateBoneMatrices = true;\n        }\n        bone = child;\n\n        bone->mNode = matrixTransform;\n    }\n\n    return bone;\n}\n\nvoid Skeleton::updateBoneMatrices(unsigned int traversalNumber)\n{\n    if (traversalNumber != mLastFrameNumber)\n        mNeedToUpdateBoneMatrices = true;\n\n    mLastFrameNumber = traversalNumber;\n\n    if (mNeedToUpdateBoneMatrices)\n    {\n        if (mRootBone.get())\n        {\n            for (unsigned int i=0; i<mRootBone->mChildren.size(); ++i)\n                mRootBone->mChildren[i]->update(nullptr);\n        }\n\n        mNeedToUpdateBoneMatrices = false;\n    }\n}\n\nvoid Skeleton::setActive(ActiveType active)\n{\n    mActive = active;\n}\n\nbool Skeleton::getActive() const\n{\n    return mActive != Inactive;\n}\n\nvoid Skeleton::markDirty()\n{\n    mLastFrameNumber = 0;\n    mBoneCache.clear();\n    mBoneCacheInit = false;\n}\n\nvoid Skeleton::traverse(osg::NodeVisitor& nv)\n{\n    if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)\n    {\n        if (mActive == Inactive && mLastFrameNumber != 0)\n            return;\n        if (mActive == SemiActive && mLastFrameNumber != 0 && mLastCullFrameNumber+3 <= nv.getTraversalNumber())\n            return;\n    }\n    else if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)\n        mLastCullFrameNumber = nv.getTraversalNumber();\n\n    osg::Group::traverse(nv);\n}\n\nvoid Skeleton::childInserted(unsigned int)\n{\n    markDirty();\n}\n\nvoid Skeleton::childRemoved(unsigned int, unsigned int)\n{\n    markDirty();\n}\n\nBone::Bone()\n    : mNode(nullptr)\n{\n}\n\nBone::~Bone()\n{\n    for (unsigned int i=0; i<mChildren.size(); ++i)\n        delete mChildren[i];\n    mChildren.clear();\n}\n\nvoid Bone::update(const osg::Matrixf* parentMatrixInSkeletonSpace)\n{\n    if (!mNode)\n    {\n        Log(Debug::Error) << \"Error: Bone without node\";\n        return;\n    }\n    if (parentMatrixInSkeletonSpace)\n        mMatrixInSkeletonSpace = mNode->getMatrix() * (*parentMatrixInSkeletonSpace);\n    else\n        mMatrixInSkeletonSpace = mNode->getMatrix();\n\n    for (unsigned int i=0; i<mChildren.size(); ++i)\n    {\n        mChildren[i]->update(&mMatrixInSkeletonSpace);\n    }\n}\n\n}\n"
  },
  {
    "path": "components/sceneutil/skeleton.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_NIFOSG_SKELETON_H\n#define OPENMW_COMPONENTS_NIFOSG_SKELETON_H\n\n#include <osg/Group>\n\n#include <memory>\n\nnamespace SceneUtil\n{\n\n    /// @brief Defines a Bone hierarchy, used for updating of skeleton-space bone matrices.\n    /// @note To prevent unnecessary updates, only bones that are used for skinning will be added to this hierarchy.\n    class Bone\n    {\n    public:\n        Bone();\n        ~Bone();\n\n        osg::Matrixf mMatrixInSkeletonSpace;\n\n        osg::MatrixTransform* mNode;\n\n        std::vector<Bone*> mChildren;\n\n        /// Update the skeleton-space matrix of this bone and all its children.\n        void update(const osg::Matrixf* parentMatrixInSkeletonSpace);\n\n    private:\n        Bone(const Bone&);\n        void operator=(const Bone&);\n    };\n\n    /// @brief Handles the bone matrices for any number of child RigGeometries.\n    /// @par Bones should be created as osg::MatrixTransform children of the skeleton.\n    /// To be a referenced by a RigGeometry, a bone needs to have a unique name.\n    class Skeleton : public osg::Group\n    {\n    public:\n        Skeleton();\n        Skeleton(const Skeleton& copy, const osg::CopyOp& copyop);\n\n        META_Node(SceneUtil, Skeleton)\n\n        /// Retrieve a bone by name.\n        Bone* getBone(const std::string& name);\n\n        /// Request an update of bone matrices. May be a no-op if already updated in this frame.\n        void updateBoneMatrices(unsigned int traversalNumber);\n\n        enum ActiveType\n        {\n            Inactive=0,\n            SemiActive, /// Like Active, but don't bother with Update (including new bounding box) if we're off-screen\n            Active\n        };\n\n        /// Set the skinning active flag. Inactive skeletons will not have their child rigs updated.\n        /// You should set this flag to false if you know that bones are not currently moving.\n        void setActive(ActiveType active);\n\n        bool getActive() const;\n\n        void traverse(osg::NodeVisitor& nv) override;\n\n        void markDirty();\n\n        void childInserted(unsigned int) override;\n        void childRemoved(unsigned int, unsigned int) override;\n\n    private:\n        // The root bone is not a \"real\" bone, it has no corresponding node in the scene graph.\n        // As far as the scene graph goes we support multiple root bones.\n        std::unique_ptr<Bone> mRootBone;\n\n        typedef std::map<std::string, std::pair<osg::NodePath, osg::MatrixTransform*> > BoneCache;\n        BoneCache mBoneCache;\n        bool mBoneCacheInit;\n\n        bool mNeedToUpdateBoneMatrices;\n\n        ActiveType mActive;\n\n        unsigned int mLastFrameNumber;\n        unsigned int mLastCullFrameNumber;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/statesetupdater.cpp",
    "content": "#include \"statesetupdater.hpp\"\n\n#include <osg/Node>\n#include <osg/NodeVisitor>\n#include <osgUtil/CullVisitor>\n\nnamespace SceneUtil\n{\n\n    void StateSetUpdater::operator()(osg::Node* node, osg::NodeVisitor* nv)\n    {\n        bool isCullVisitor = nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR;\n        if (!mStateSets[0])\n        {\n            for (int i=0; i<2; ++i)\n            {\n                if (!isCullVisitor)\n                    mStateSets[i] = new osg::StateSet(*node->getOrCreateStateSet(), osg::CopyOp::SHALLOW_COPY); // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first in setDefaults\n                else\n                    mStateSets[i] = new osg::StateSet;\n                setDefaults(mStateSets[i]);\n            }\n        }\n\n        osg::ref_ptr<osg::StateSet> stateset = mStateSets[nv->getTraversalNumber()%2];\n        apply(stateset, nv);\n\n        if (!isCullVisitor)\n            node->setStateSet(stateset);\n        else\n            static_cast<osgUtil::CullVisitor*>(nv)->pushStateSet(stateset);\n\n        traverse(node, nv);\n\n        if (isCullVisitor)\n            static_cast<osgUtil::CullVisitor*>(nv)->popStateSet();\n    }\n\n    void StateSetUpdater::reset()\n    {\n        mStateSets[0] = nullptr;\n        mStateSets[1] = nullptr;\n    }\n\n    StateSetUpdater::StateSetUpdater()\n    {\n    }\n\n    StateSetUpdater::StateSetUpdater(const StateSetUpdater &copy, const osg::CopyOp &copyop)\n        : osg::NodeCallback(copy, copyop)\n    {\n    }\n\n    // ----------------------------------------------------------------------------------\n\n    void CompositeStateSetUpdater::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)\n    {\n        for (unsigned int i=0; i<mCtrls.size(); ++i)\n            mCtrls[i]->apply(stateset, nv);\n    }\n\n    void CompositeStateSetUpdater::setDefaults(osg::StateSet *stateset)\n    {\n        for (unsigned int i=0; i<mCtrls.size(); ++i)\n            mCtrls[i]->setDefaults(stateset);\n    }\n\n    CompositeStateSetUpdater::CompositeStateSetUpdater()\n    {\n    }\n\n    CompositeStateSetUpdater::CompositeStateSetUpdater(const CompositeStateSetUpdater &copy, const osg::CopyOp &copyop)\n        : StateSetUpdater(copy, copyop)\n    {\n        for (unsigned int i=0; i<copy.mCtrls.size(); ++i)\n            mCtrls.emplace_back(osg::clone(copy.mCtrls[i].get(), copyop));\n    }\n\n    unsigned int CompositeStateSetUpdater::getNumControllers()\n    {\n        return mCtrls.size();\n    }\n\n    StateSetUpdater* CompositeStateSetUpdater::getController(int i)\n    {\n        return mCtrls[i];\n    }\n\n    void CompositeStateSetUpdater::addController(StateSetUpdater *ctrl)\n    {\n        mCtrls.emplace_back(ctrl);\n    }\n\n}\n"
  },
  {
    "path": "components/sceneutil/statesetupdater.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_STATESETCONTROLLER_H\n#define OPENMW_COMPONENTS_SCENEUTIL_STATESETCONTROLLER_H\n\n#include <osg/NodeCallback>\n\nnamespace SceneUtil\n{\n\n    /// @brief Implements efficient per-frame updating of StateSets.\n    /// @par With a naive update there would be race conditions when the OSG draw thread of the last frame\n    ///     queues up a StateSet that we want to modify for the next frame. To solve this we could set the StateSet to\n    ///     DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw\n    ///     traversals run in parallel can yield up to 200% framerates.\n    /// @par Race conditions are prevented using a \"double buffering\" scheme - we have two StateSets that take turns,\n    ///     one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame.\n    /// @par Must be set as UpdateCallback or CullCallback on a Node. If set as a CullCallback, the StateSetUpdater operates on an empty StateSet, otherwise it operates on a clone of the node's existing StateSet.\n    /// @note Do not add the same StateSetUpdater to multiple nodes.\n    /// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater.\n    class StateSetUpdater : public osg::NodeCallback\n    {\n    public:\n        StateSetUpdater();\n        StateSetUpdater(const StateSetUpdater& copy, const osg::CopyOp& copyop);\n\n        META_Object(SceneUtil, StateSetUpdater)\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override;\n\n        /// Apply state - to override in derived classes\n        /// @note Due to the double buffering approach you *have* to apply all state\n        /// even if it has not changed since the last frame.\n        virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) {}\n\n        /// Set default state - optionally override in derived classes\n        /// @par May be used e.g. to allocate StateAttributes.\n        virtual void setDefaults(osg::StateSet* stateset) {}\n\n    protected:\n        /// Reset mStateSets, forcing a setDefaults() on the next frame. Can be used to change the defaults if needed.\n        void reset();\n\n    private:\n        osg::ref_ptr<osg::StateSet> mStateSets[2];\n    };\n\n    /// @brief A variant of the StateSetController that can be made up of multiple controllers all controlling the same target.\n    class CompositeStateSetUpdater : public StateSetUpdater\n    {\n    public:\n        CompositeStateSetUpdater();\n        CompositeStateSetUpdater(const CompositeStateSetUpdater& copy, const osg::CopyOp& copyop);\n\n        META_Object(SceneUtil, CompositeStateSetUpdater)\n\n        unsigned int getNumControllers();\n        StateSetUpdater* getController(int i);\n\n        void addController(StateSetUpdater* ctrl);\n\n        void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override;\n\n    protected:\n\n        void setDefaults(osg::StateSet *stateset) override;\n\n        std::vector<osg::ref_ptr<StateSetUpdater> > mCtrls;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/textkeymap.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_TEXTKEYMAP\n#define OPENMW_COMPONENTS_SCENEUTIL_TEXTKEYMAP\n\n#include <algorithm>\n#include <map>\n#include <set>\n#include <string>\n\nnamespace SceneUtil\n{\n    class TextKeyMap\n    {\n    public:\n        using ConstIterator = std::multimap<float, std::string>::const_iterator;\n\n        auto begin() const noexcept\n        {\n            return mTextKeyByTime.begin();\n        }\n\n        auto end() const noexcept\n        {\n            return mTextKeyByTime.end();\n        }\n\n        auto rbegin() const noexcept\n        {\n            return mTextKeyByTime.rbegin();\n        }\n\n        auto rend() const noexcept\n        {\n            return mTextKeyByTime.rend();\n        }\n\n        auto lowerBound(float time) const\n        {\n            return mTextKeyByTime.lower_bound(time);\n        }\n\n        auto upperBound(float time) const\n        {\n            return mTextKeyByTime.upper_bound(time);\n        }\n\n        void emplace(float time, std::string&& textKey)\n        {\n            const auto separator = textKey.find(\": \");\n            if (separator != std::string::npos)\n                mGroups.emplace(textKey.substr(0, separator));\n\n            mTextKeyByTime.emplace(time, std::move(textKey));\n        }\n\n        bool empty() const noexcept\n        {\n            return mTextKeyByTime.empty();\n        }\n\n        auto findGroupStart(const std::string &groupName) const\n        {\n            return std::find_if(mTextKeyByTime.begin(), mTextKeyByTime.end(), IsGroupStart{groupName});\n        }\n\n        bool hasGroupStart(const std::string &groupName) const\n        {\n            return mGroups.count(groupName) > 0;\n        }\n\n    private:\n        struct IsGroupStart\n        {\n            const std::string &mGroupName;\n\n            bool operator ()(const std::multimap<float, std::string>::value_type& value) const\n            {\n                return value.second.compare(0, mGroupName.size(), mGroupName) == 0 &&\n                        value.second.compare(mGroupName.size(), 2, \": \") == 0;\n            }\n        };\n\n        std::set<std::string> mGroups;\n        std::multimap<float, std::string> mTextKeyByTime;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/unrefqueue.cpp",
    "content": "#include \"unrefqueue.hpp\"\n\n//#include <osg/Timer>\n\n//#include <components/debug/debuglog.hpp>\n\nnamespace SceneUtil\n{\n    void UnrefWorkItem::doWork()\n    {\n        mObjects.clear();\n    }\n\n    UnrefQueue::UnrefQueue()\n    {\n        mWorkItem = new UnrefWorkItem;\n    }\n\n    void UnrefQueue::push(const osg::Referenced *obj)\n    {\n        mWorkItem->mObjects.emplace_back(obj);\n    }\n\n    void UnrefQueue::flush(SceneUtil::WorkQueue *workQueue)\n    {\n        if (mWorkItem->mObjects.empty())\n            return;\n\n        workQueue->addWorkItem(mWorkItem, true);\n\n        mWorkItem = new UnrefWorkItem;\n    }\n\n    unsigned int UnrefQueue::getNumItems() const\n    {\n        return mWorkItem->mObjects.size();\n    }\n\n}\n"
  },
  {
    "path": "components/sceneutil/unrefqueue.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_UNREFQUEUE_H\n#define OPENMW_COMPONENTS_UNREFQUEUE_H\n\n#include <deque>\n\n#include <osg/ref_ptr>\n#include <osg/Referenced>\n\n#include <components/sceneutil/workqueue.hpp>\n\nnamespace SceneUtil\n{\n    class WorkQueue;\n    \n    class UnrefWorkItem : public SceneUtil::WorkItem\n    {\n    public:\n        std::deque<osg::ref_ptr<const osg::Referenced> > mObjects;\n        void doWork() override;\n    };\n\n    /// @brief Handles unreferencing of objects through the WorkQueue. Typical use scenario\n    /// would be the main thread pushing objects that are no longer needed, and the background thread deleting them.\n    class UnrefQueue : public osg::Referenced\n    {\n    public:\n        UnrefQueue();\n\n        /// Adds an object to the list of objects to be unreferenced. Call from the main thread.\n        void push(const osg::Referenced* obj);\n\n        /// Adds a WorkItem to the given WorkQueue that will clear the list of objects in a worker thread, thus unreferencing them.\n        /// Call from the main thread.\n        void flush(SceneUtil::WorkQueue* workQueue);\n\n        unsigned int getNumItems() const;\n\n    private:\n        osg::ref_ptr<UnrefWorkItem> mWorkItem;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/util.cpp",
    "content": "#include \"util.hpp\"\n\n#include <algorithm>\n#include <sstream>\n#include <iomanip>\n\n#include <osg/Node>\n#include <osg/NodeVisitor>\n#include <osg/TexGen>\n#include <osg/TexEnvCombine>\n#include <osg/Version>\n\n#include <components/resource/imagemanager.hpp>\n#include <components/resource/scenemanager.hpp>\n#include <components/settings/settings.hpp>\n\nnamespace SceneUtil\n{\n\nclass FindLowestUnusedTexUnitVisitor : public osg::NodeVisitor\n{\npublic:\n    FindLowestUnusedTexUnitVisitor()\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        , mLowestUnusedTexUnit(0)\n    {\n    }\n\n    void apply(osg::Node& node) override\n    {\n        if (osg::StateSet* stateset = node.getStateSet())\n            mLowestUnusedTexUnit = std::max(mLowestUnusedTexUnit, int(stateset->getTextureAttributeList().size()));\n\n        traverse(node);\n    }\n    int mLowestUnusedTexUnit;\n};\n\nGlowUpdater::GlowUpdater(int texUnit, const osg::Vec4f& color, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures,\n    osg::Node* node, float duration, Resource::ResourceSystem* resourcesystem)\n    : mTexUnit(texUnit)\n    , mColor(color)\n    , mOriginalColor(color)\n    , mTextures(textures)\n    , mNode(node)\n    , mDuration(duration)\n    , mOriginalDuration(duration)\n    , mStartingTime(0)\n    , mResourceSystem(resourcesystem)\n    , mColorChanged(false)\n    , mDone(false)\n{\n}\n\nvoid GlowUpdater::setDefaults(osg::StateSet *stateset)\n{\n    if (mDone)\n        removeTexture(stateset);\n    else\n    {\n        stateset->setTextureMode(mTexUnit, GL_TEXTURE_2D, osg::StateAttribute::ON);\n        osg::TexGen* texGen = new osg::TexGen;\n        texGen->setMode(osg::TexGen::SPHERE_MAP);\n\n        stateset->setTextureAttributeAndModes(mTexUnit, texGen, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n\n        osg::TexEnvCombine* texEnv = new osg::TexEnvCombine;\n        texEnv->setSource0_RGB(osg::TexEnvCombine::CONSTANT);\n        texEnv->setConstantColor(mColor);\n        texEnv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE);\n        texEnv->setSource2_RGB(osg::TexEnvCombine::TEXTURE);\n        texEnv->setOperand2_RGB(osg::TexEnvCombine::SRC_COLOR);\n\n        stateset->setTextureAttributeAndModes(mTexUnit, texEnv, osg::StateAttribute::ON);\n        stateset->addUniform(new osg::Uniform(\"envMapColor\", mColor));\n    }\n}\n\nvoid GlowUpdater::removeTexture(osg::StateSet* stateset)\n{\n    stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXTURE);\n    stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXGEN);\n    stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXENV);\n    stateset->removeTextureMode(mTexUnit, GL_TEXTURE_2D);\n    stateset->removeUniform(\"envMapColor\");\n\n    osg::StateSet::TextureAttributeList& list = stateset->getTextureAttributeList();\n    while (list.size() && list.rbegin()->empty())\n        list.pop_back();\n}\n\nvoid GlowUpdater::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)\n{\n    if (mColorChanged){\n        this->reset();\n        setDefaults(stateset);\n        mColorChanged = false;\n    }\n    if (mDone)\n        return;\n\n    // Set the starting time to measure glow duration from if this is a temporary glow\n    if ((mDuration >= 0) && mStartingTime == 0)\n        mStartingTime = nv->getFrameStamp()->getSimulationTime();\n\n    float time = nv->getFrameStamp()->getSimulationTime();\n    int index = (int)(time*16) % mTextures.size();\n    stateset->setTextureAttribute(mTexUnit, mTextures[index], osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);\n\n    if ((mDuration >= 0) && (time - mStartingTime > mDuration)) // If this is a temporary glow and it has finished its duration\n    {\n        if (mOriginalDuration >= 0) // if this glowupdater was a temporary glow since its creation\n        {\n            removeTexture(stateset);\n            this->reset();\n            mDone = true;\n            // normally done in StateSetUpdater::operator(), but needs doing here so the shader visitor sees the right StateSet\n            mNode->setStateSet(stateset);\n            mResourceSystem->getSceneManager()->recreateShaders(mNode);\n        }\n        if (mOriginalDuration < 0) // if this glowupdater was originally a permanent glow\n        {\n            mDuration = mOriginalDuration;\n            mStartingTime = 0;\n            mColor = mOriginalColor;\n            this->reset();\n            setDefaults(stateset);\n        }\n    }\n}\n\nbool GlowUpdater::isPermanentGlowUpdater()\n{\n    return (mDuration < 0);\n}\n\nbool GlowUpdater::isDone()\n{\n    return mDone;\n}\n\nvoid GlowUpdater::setColor(const osg::Vec4f& color)\n{\n    mColor = color;\n    mColorChanged = true;\n}\n\nvoid GlowUpdater::setDuration(float duration)\n{\n    mDuration = duration;\n}\n\nvoid transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere)\n{\n    osg::BoundingSphere::vec_type xdash = bsphere._center;\n    xdash.x() += bsphere._radius;\n    xdash = xdash*matrix;\n\n    osg::BoundingSphere::vec_type ydash = bsphere._center;\n    ydash.y() += bsphere._radius;\n    ydash = ydash*matrix;\n\n    osg::BoundingSphere::vec_type zdash = bsphere._center;\n    zdash.z() += bsphere._radius;\n    zdash = zdash*matrix;\n\n    bsphere._center = bsphere._center*matrix;\n\n    xdash -= bsphere._center;\n    osg::BoundingSphere::value_type sqrlen_xdash = xdash.length2();\n\n    ydash -= bsphere._center;\n    osg::BoundingSphere::value_type sqrlen_ydash = ydash.length2();\n\n    zdash -= bsphere._center;\n    osg::BoundingSphere::value_type sqrlen_zdash = zdash.length2();\n\n    bsphere._radius = sqrlen_xdash;\n    if (bsphere._radius<sqrlen_ydash) bsphere._radius = sqrlen_ydash;\n    if (bsphere._radius<sqrlen_zdash) bsphere._radius = sqrlen_zdash;\n    bsphere._radius = sqrtf(bsphere._radius);\n}\n\nosg::Vec4f colourFromRGB(unsigned int clr)\n{\n    osg::Vec4f colour(((clr >> 0) & 0xFF) / 255.0f,\n                      ((clr >> 8) & 0xFF) / 255.0f,\n                      ((clr >> 16) & 0xFF) / 255.0f, 1.f);\n    return colour;\n}\n\nosg::Vec4f colourFromRGBA(unsigned int value)\n{\n    return osg::Vec4f(makeOsgColorComponent(value, 0), makeOsgColorComponent(value, 8),\n                      makeOsgColorComponent(value, 16), makeOsgColorComponent(value, 24));\n}\n\nfloat makeOsgColorComponent(unsigned int value, unsigned int shift)\n{\n    return float((value >> shift) & 0xFFu) / 255.0f;\n}\n\nbool hasUserDescription(const osg::Node* node, const std::string pattern)\n{\n    if (node == nullptr)\n        return false;\n\n    const osg::UserDataContainer* udc = node->getUserDataContainer();\n    if (udc && udc->getNumDescriptions() > 0)\n    {\n        for (auto& descr : udc->getDescriptions())\n        {\n            if (descr == pattern)\n                return true;\n        }\n    }\n\n    return false;\n}\n\nosg::ref_ptr<GlowUpdater> addEnchantedGlow(osg::ref_ptr<osg::Node> node, Resource::ResourceSystem* resourceSystem, osg::Vec4f glowColor, float glowDuration)\n{\n    std::vector<osg::ref_ptr<osg::Texture2D> > textures;\n    for (int i=0; i<32; ++i)\n    {\n        std::stringstream stream;\n        stream << \"textures/magicitem/caust\";\n        stream << std::setw(2);\n        stream << std::setfill('0');\n        stream << i;\n        stream << \".dds\";\n\n        osg::ref_ptr<osg::Image> image = resourceSystem->getImageManager()->getImage(stream.str());\n        osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(image));\n        tex->setName(\"envMap\");\n        tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT);\n        tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT);\n        resourceSystem->getSceneManager()->applyFilterSettings(tex);\n        textures.push_back(tex);\n    }\n\n    FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor;\n    node->accept(findLowestUnusedTexUnitVisitor);\n    int texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit;\n\n    osg::ref_ptr<GlowUpdater> glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, resourceSystem);\n    node->addUpdateCallback(glowUpdater);\n\n    // set a texture now so that the ShaderVisitor can find it\n    osg::ref_ptr<osg::StateSet> writableStateSet = nullptr;\n    if (!node->getStateSet())\n        writableStateSet = node->getOrCreateStateSet();\n    else\n    {\n        writableStateSet = new osg::StateSet(*node->getStateSet(), osg::CopyOp::SHALLOW_COPY);\n        node->setStateSet(writableStateSet);\n    }\n    writableStateSet->setTextureAttributeAndModes(texUnit, textures.front(), osg::StateAttribute::ON);\n    writableStateSet->addUniform(new osg::Uniform(\"envMapColor\", glowColor));\n    resourceSystem->getSceneManager()->recreateShaders(node);\n\n    return glowUpdater;\n}\n\nbool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg::Camera::BufferComponent buffer, osg::Texture * texture, unsigned int level, unsigned int face, bool mipMapGeneration)\n{\n#if OSG_VERSION_LESS_THAN(3, 6, 6)\n    // hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028\n    osg::ref_ptr<osg::GLExtensions> extensions = osg::GLExtensions::Get(0, false);\n    if (extensions)\n        extensions->glRenderbufferStorageMultisampleCoverageNV = nullptr;\n#endif\n    unsigned int samples = 0;\n    unsigned int colourSamples = 0;\n    bool addMSAAIntermediateTarget = Settings::Manager::getBool(\"antialias alpha test\", \"Shaders\") && Settings::Manager::getInt(\"antialiasing\", \"Video\") > 1;\n    if (addMSAAIntermediateTarget)\n    {\n        // Alpha-to-coverage requires a multisampled framebuffer.\n        // OSG will set that up automatically and resolve it to the specified single-sample texture for us.\n        // For some reason, two samples are needed, at least with some drivers.\n        samples = 2;\n        colourSamples = 1;\n    }\n    camera->attach(buffer, texture, level, face, mipMapGeneration, samples, colourSamples);\n    return addMSAAIntermediateTarget;\n}\n\n}\n"
  },
  {
    "path": "components/sceneutil/util.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_UTIL_H\n#define OPENMW_COMPONENTS_SCENEUTIL_UTIL_H\n\n#include <osg/Matrix>\n#include <osg/BoundingSphere>\n#include <osg/Camera>\n#include <osg/NodeCallback>\n#include <osg/Texture2D>\n#include <osg/Vec4f>\n\n#include <components/resource/resourcesystem.hpp>\n\n#include \"statesetupdater.hpp\"\n\nnamespace SceneUtil\n{\n    class GlowUpdater : public SceneUtil::StateSetUpdater\n    {\n    public:\n        GlowUpdater(int texUnit, const osg::Vec4f& color, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures,\n            osg::Node* node, float duration, Resource::ResourceSystem* resourcesystem);\n\n        void setDefaults(osg::StateSet *stateset) override;\n\n        void removeTexture(osg::StateSet* stateset);\n        void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) override;\n\n        bool isPermanentGlowUpdater();\n\n        bool isDone();\n\n        void setColor(const osg::Vec4f& color);\n\n        void setDuration(float duration);\n\n    private:\n        int mTexUnit;\n        osg::Vec4f mColor;\n        osg::Vec4f mOriginalColor; // for restoring the color of a permanent glow after a temporary glow on the object finishes\n        std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;\n        osg::Node* mNode;\n        float mDuration;\n        float mOriginalDuration; // for recording that this is originally a permanent glow if it is changed to a temporary one\n        float mStartingTime;\n        Resource::ResourceSystem* mResourceSystem;\n        bool mColorChanged;\n        bool mDone;\n    };\n\n    // Transform a bounding sphere by a matrix\n    // based off private code in osg::Transform\n    // TODO: patch osg to make public\n    void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere);\n\n    osg::Vec4f colourFromRGB (unsigned int clr);\n\n    osg::Vec4f colourFromRGBA (unsigned int value);\n\n    float makeOsgColorComponent (unsigned int value, unsigned int shift);\n\n    bool hasUserDescription(const osg::Node* node, const std::string pattern);\n\n    osg::ref_ptr<GlowUpdater> addEnchantedGlow(osg::ref_ptr<osg::Node> node, Resource::ResourceSystem* resourceSystem, osg::Vec4f glowColor, float glowDuration=-1);\n\n    // Alpha-to-coverage requires a multisampled framebuffer, so we need to set that up for RTTs\n    bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg::Camera::BufferComponent buffer, osg::Texture* texture, unsigned int level = 0, unsigned int face = 0, bool mipMapGeneration = false);\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/visitor.cpp",
    "content": "#include \"visitor.hpp\"\n\n#include <osg/Drawable>\n#include <osg/MatrixTransform>\n\n#include <osgParticle/ParticleSystem>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/misc/stringops.hpp>\n\nnamespace SceneUtil\n{\n\n    bool FindByNameVisitor::checkGroup(osg::Group &group)\n    {\n        if (Misc::StringUtils::ciEqual(group.getName(), mNameToFind))\n        {\n            mFoundNode = &group;\n            return true;\n        }\n        return false;\n    }\n\n    void FindByClassVisitor::apply(osg::Node &node)\n    {\n        if (Misc::StringUtils::ciEqual(node.className(), mNameToFind))\n            mFoundNodes.push_back(&node);\n\n        traverse(node);\n    }\n\n    void FindByNameVisitor::apply(osg::Group &group)\n    {\n        if (!checkGroup(group))\n            traverse(group);\n    }\n\n    void FindByNameVisitor::apply(osg::MatrixTransform &node)\n    {\n        if (!checkGroup(node))\n            traverse(node);\n    }\n\n    void FindByNameVisitor::apply(osg::Geometry&)\n    {\n    }\n\n    void DisableFreezeOnCullVisitor::apply(osg::MatrixTransform &node)\n    {\n        traverse(node);\n    }\n\n    void DisableFreezeOnCullVisitor::apply(osg::Drawable& drw)\n    {\n        if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(&drw))\n            partsys->setFreezeOnCull(false);\n    }\n\n    void NodeMapVisitor::apply(osg::MatrixTransform& trans)\n    {\n        // Take transformation for first found node in file\n        std::string originalNodeName = Misc::StringUtils::lowerCase(trans.getName());\n\n        if (trans.libraryName() == std::string(\"osgAnimation\"))\n        {\n            // Convert underscores to whitespaces as a workaround for Collada (OpenMW's animation system uses whitespace-separated names)\n            std::string underscore = \"_\";\n            std::size_t foundUnderscore = originalNodeName.find(underscore);\n\n            if (foundUnderscore != std::string::npos)\n                std::replace(originalNodeName.begin(), originalNodeName.end(), '_', ' ');\n        }\n\n        const std::string nodeName = originalNodeName;\n\n        mMap.emplace(nodeName, &trans);\n\n        traverse(trans);\n    }\n\n    void RemoveVisitor::remove()\n    {\n        for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)\n        {\n            if (!it->second->removeChild(it->first))\n                Log(Debug::Error) << \"error removing \" << it->first->getName();\n        }\n    }\n\n    void CleanObjectRootVisitor::apply(osg::Drawable& drw)\n    {\n        applyDrawable(drw);\n    }\n\n    void CleanObjectRootVisitor::apply(osg::Group& node)\n    {\n        applyNode(node);\n    }\n\n    void CleanObjectRootVisitor::apply(osg::MatrixTransform& node)\n    {\n        applyNode(node);\n    }\n\n    void CleanObjectRootVisitor::apply(osg::Node& node)\n    {\n        applyNode(node);\n    }\n\n    void CleanObjectRootVisitor::applyNode(osg::Node& node)\n    {\n        if (node.getStateSet())\n            node.setStateSet(nullptr);\n\n        if (node.getNodeMask() == 0x1 && node.getNumParents() == 1)\n            mToRemove.emplace_back(&node, node.getParent(0));\n        else\n            traverse(node);\n    }\n\n    void CleanObjectRootVisitor::applyDrawable(osg::Node& node)\n    {\n        osg::NodePath::iterator parent = getNodePath().end()-2;\n        // We know that the parent is a Group because only Groups can have children.\n        osg::Group* parentGroup = static_cast<osg::Group*>(*parent);\n\n        // Try to prune nodes that would be empty after the removal\n        if (parent != getNodePath().begin())\n        {\n            // This could be extended to remove the parent's parent, and so on if they are empty as well.\n            // But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance.\n            osg::Group* parentParent = static_cast<osg::Group*>(*(parent - 1));\n            if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC)\n            {\n                mToRemove.emplace_back(parentGroup, parentParent);\n                return;\n            }\n        }\n\n        mToRemove.emplace_back(&node, parentGroup);\n    }\n\n    void RemoveTriBipVisitor::apply(osg::Drawable& drw)\n    {\n        applyImpl(drw);\n    }\n\n    void RemoveTriBipVisitor::apply(osg::Group& node)\n    {\n        traverse(node);\n    }\n\n    void RemoveTriBipVisitor::apply(osg::MatrixTransform& node)\n    {\n        traverse(node);\n    }\n\n    void RemoveTriBipVisitor::applyImpl(osg::Node& node)\n    {\n        const std::string toFind = \"tri bip\";\n        if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0)\n        {\n            osg::Group* parent = static_cast<osg::Group*>(*(getNodePath().end()-2));\n            // Not safe to remove in apply(), since the visitor is still iterating the child list\n            mToRemove.emplace_back(&node, parent);\n        }\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/visitor.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H\n#define OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H\n\n#include <osg/MatrixTransform>\n#include <osg/NodeVisitor>\n\n// Commonly used scene graph visitors\nnamespace SceneUtil\n{\n\n    // Find a Group by name, case-insensitive\n    // If not found, mFoundNode will be nullptr\n    class FindByNameVisitor : public osg::NodeVisitor\n    {\n    public:\n        FindByNameVisitor(const std::string& nameToFind)\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mNameToFind(nameToFind)\n            , mFoundNode(nullptr)\n        {\n        }\n\n        void apply(osg::Group& group) override;\n        void apply(osg::MatrixTransform& node) override;\n        void apply(osg::Geometry& node) override;\n\n        bool checkGroup(osg::Group& group);\n\n        std::string mNameToFind;\n        osg::Group* mFoundNode;\n    };\n\n    class FindByClassVisitor : public osg::NodeVisitor\n    {\n    public:\n        FindByClassVisitor(const std::string& nameToFind)\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n            , mNameToFind(nameToFind)\n        {\n        }\n\n        void apply(osg::Node &node) override;\n\n        std::string mNameToFind;\n        std::vector<osg::Node *> mFoundNodes;\n    };\n\n    // Disable freezeOnCull for all visited particlesystems\n    class DisableFreezeOnCullVisitor : public osg::NodeVisitor\n    {\n    public:\n        DisableFreezeOnCullVisitor()\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        {\n        }\n\n        void apply(osg::MatrixTransform& node) override;\n\n        void apply(osg::Drawable& drw) override;\n    };\n\n    /// Maps names to nodes\n    class NodeMapVisitor : public osg::NodeVisitor\n    {\n    public:\n        typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;\n\n        NodeMapVisitor(NodeMap& map)\n            : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)\n            , mMap(map)\n        {\n        }\n\n        void apply(osg::MatrixTransform& trans) override;\n\n    private:\n        NodeMap& mMap;\n    };\n\n    /// @brief Base class for visitors that remove nodes from a scene graph.\n    /// Subclasses need to fill the mToRemove vector.\n    /// To use, node->accept(removeVisitor); removeVisitor.remove();\n    class RemoveVisitor : public osg::NodeVisitor\n    {\n    public:\n        RemoveVisitor()\n            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        {\n        }\n\n        void remove();\n\n    protected:\n        // <node to remove, parent node to remove it from>\n        typedef std::vector<std::pair<osg::Node*, osg::Group*> > RemoveVec;\n        std::vector<std::pair<osg::Node*, osg::Group*> > mToRemove;\n    };\n\n    // Removes all drawables from a graph.\n    class CleanObjectRootVisitor : public RemoveVisitor\n    {\n    public:\n        void apply(osg::Drawable& drw) override;\n        void apply(osg::Group& node) override;\n        void apply(osg::MatrixTransform& node) override;\n        void apply(osg::Node& node) override;\n\n        void applyNode(osg::Node& node);\n        void applyDrawable(osg::Node& node);\n    };\n\n    class RemoveTriBipVisitor : public RemoveVisitor\n    {\n    public:\n        void apply(osg::Drawable& drw) override;\n        void apply(osg::Group& node) override;\n        void apply(osg::MatrixTransform& node) override;\n\n        void applyImpl(osg::Node& node);\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/waterutil.cpp",
    "content": "#include \"waterutil.hpp\"\n\n#include <osg/Depth>\n#include <osg/Geometry>\n#include <osg/Material>\n#include <osg/StateSet>\n\nnamespace SceneUtil\n{\n    // disable nonsense test against a worldsize bb what will always pass\n    class WaterBoundCallback : public osg::Drawable::ComputeBoundingBoxCallback\n    {\n         osg::BoundingBox computeBound(const osg::Drawable&) const override { return osg::BoundingBox(); }\n    };\n\n    osg::ref_ptr<osg::Geometry> createWaterGeometry(float size, int segments, float textureRepeats)\n    {\n        osg::ref_ptr<osg::Vec3Array> verts (new osg::Vec3Array);\n        osg::ref_ptr<osg::Vec2Array> texcoords (new osg::Vec2Array);\n\n        // some drivers don't like huge triangles, so we do some subdivisons\n        // a paged solution would be even better\n        const float step = size/segments;\n        const float texCoordStep = textureRepeats / segments;\n        for (int x=0; x<segments; ++x)\n        {\n            for (int y=0; y<segments; ++y)\n            {\n                float x1 = -size/2.f + x*step;\n                float y1 = -size/2.f + y*step;\n                float x2 = x1 + step;\n                float y2 = y1 + step;\n\n                verts->push_back(osg::Vec3f(x1, y2, 0.f));\n                verts->push_back(osg::Vec3f(x1, y1, 0.f));\n                verts->push_back(osg::Vec3f(x2, y1, 0.f));\n                verts->push_back(osg::Vec3f(x2, y2, 0.f));\n\n                float u1 = x*texCoordStep;\n                float v1 = y*texCoordStep;\n                float u2 = u1 + texCoordStep;\n                float v2 = v1 + texCoordStep;\n\n                texcoords->push_back(osg::Vec2f(u1, v2));\n                texcoords->push_back(osg::Vec2f(u1, v1));\n                texcoords->push_back(osg::Vec2f(u2, v1));\n                texcoords->push_back(osg::Vec2f(u2, v2));\n            }\n        }\n\n        osg::ref_ptr<osg::Geometry> waterGeom (new osg::Geometry);\n        waterGeom->setVertexArray(verts);\n        waterGeom->setTexCoordArray(0, texcoords);\n\n        osg::ref_ptr<osg::Vec3Array> normal (new osg::Vec3Array);\n        normal->push_back(osg::Vec3f(0,0,1));\n        waterGeom->setNormalArray(normal, osg::Array::BIND_OVERALL);\n\n        waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size()));\n        waterGeom->setComputeBoundingBoxCallback(new WaterBoundCallback);\n        waterGeom->setCullingActive(false);\n        return waterGeom;\n    }\n\n    osg::ref_ptr<osg::StateSet> createSimpleWaterStateSet(float alpha, int renderBin)\n    {\n        osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);\n\n        osg::ref_ptr<osg::Material> material (new osg::Material);\n        material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));\n        material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, alpha));\n        material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f));\n        material->setColorMode(osg::Material::OFF);\n        stateset->setAttributeAndModes(material, osg::StateAttribute::ON);\n\n        stateset->setMode(GL_BLEND, osg::StateAttribute::ON);\n        stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n\n        osg::ref_ptr<osg::Depth> depth (new osg::Depth);\n        depth->setWriteMask(false);\n        stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);\n\n        stateset->setRenderBinDetails(renderBin, \"RenderBin\");\n\n        // Let the shader know we're dealing with simple water here.\n        stateset->addUniform(new osg::Uniform(\"simpleWater\", true));\n\n        return stateset;\n    }\n}\n"
  },
  {
    "path": "components/sceneutil/waterutil.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_WATERUTIL_H\n#define OPENMW_COMPONENTS_WATERUTIL_H\n\n#include <osg/ref_ptr>\n\nnamespace osg\n{\n    class Geometry;\n    class StateSet;\n}\n\nnamespace SceneUtil\n{\n    osg::ref_ptr<osg::Geometry> createWaterGeometry(float size, int segments, float textureRepeats);\n\n    osg::ref_ptr<osg::StateSet> createSimpleWaterStateSet(float alpha, int renderBin);\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/workqueue.cpp",
    "content": "#include \"workqueue.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include <numeric>\n\nnamespace SceneUtil\n{\n\nvoid WorkItem::waitTillDone()\n{\n    if (mDone)\n        return;\n\n    std::unique_lock<std::mutex> lock(mMutex);\n    while (!mDone)\n    {\n        mCondition.wait(lock);\n    }\n}\n\nvoid WorkItem::signalDone()\n{\n    {\n        std::unique_lock<std::mutex> lock(mMutex);\n        mDone = true;\n    }\n    mCondition.notify_all();\n}\n\nbool WorkItem::isDone() const\n{\n    return mDone;\n}\n\nWorkQueue::WorkQueue(int workerThreads)\n    : mIsReleased(false)\n{\n    for (int i=0; i<workerThreads; ++i)\n        mThreads.emplace_back(std::make_unique<WorkThread>(*this));\n}\n\nWorkQueue::~WorkQueue()\n{\n    {\n        std::unique_lock<std::mutex> lock(mMutex);\n        while (!mQueue.empty())\n            mQueue.pop_back();\n        mIsReleased = true;\n        mCondition.notify_all();\n    }\n\n    mThreads.clear();\n}\n\nvoid WorkQueue::addWorkItem(osg::ref_ptr<WorkItem> item, bool front)\n{\n    if (item->isDone())\n    {\n        Log(Debug::Error) << \"Error: trying to add a work item that is already completed\";\n        return;\n    }\n\n    std::unique_lock<std::mutex> lock(mMutex);\n    if (front)\n        mQueue.push_front(item);\n    else\n        mQueue.push_back(item);\n    mCondition.notify_one();\n}\n\nosg::ref_ptr<WorkItem> WorkQueue::removeWorkItem()\n{\n    std::unique_lock<std::mutex> lock(mMutex);\n    while (mQueue.empty() && !mIsReleased)\n    {\n        mCondition.wait(lock);\n    }\n    if (!mQueue.empty())\n    {\n        osg::ref_ptr<WorkItem> item = mQueue.front();\n        mQueue.pop_front();\n        return item;\n    }\n    else\n        return nullptr;\n}\n\nunsigned int WorkQueue::getNumItems() const\n{\n    std::unique_lock<std::mutex> lock(mMutex);\n    return mQueue.size();\n}\n\nunsigned int WorkQueue::getNumActiveThreads() const\n{\n    return std::accumulate(mThreads.begin(), mThreads.end(), 0u,\n        [] (auto r, const auto& t) { return r + t->isActive(); });\n}\n\nWorkThread::WorkThread(WorkQueue& workQueue)\n    : mWorkQueue(&workQueue)\n    , mActive(false)\n    , mThread([this] { run(); })\n{\n}\n\nWorkThread::~WorkThread()\n{\n    mThread.join();\n}\n\nvoid WorkThread::run()\n{\n    while (true)\n    {\n        osg::ref_ptr<WorkItem> item = mWorkQueue->removeWorkItem();\n        if (!item)\n            return;\n        mActive = true;\n        item->doWork();\n        item->signalDone();\n        mActive = false;\n    }\n}\n\nbool WorkThread::isActive() const\n{\n    return mActive;\n}\n\n}\n"
  },
  {
    "path": "components/sceneutil/workqueue.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SCENEUTIL_WORKQUEUE_H\n#define OPENMW_COMPONENTS_SCENEUTIL_WORKQUEUE_H\n\n#include <osg/Referenced>\n#include <osg/ref_ptr>\n\n#include <atomic>\n#include <queue>\n#include <thread>\n#include <mutex>\n#include <condition_variable>\n\nnamespace SceneUtil\n{\n\n    class WorkItem : public osg::Referenced\n    {\n    public:\n        /// Override in a derived WorkItem to perform actual work.\n        virtual void doWork() {}\n\n        bool isDone() const;\n\n        /// Wait until the work is completed. Usually called from the main thread.\n        void waitTillDone();\n\n        /// Internal use by the WorkQueue.\n        void signalDone();\n\n        /// Set abort flag in order to return from doWork() as soon as possible. May not be respected by all WorkItems.\n        virtual void abort() {}\n\n    private:\n        std::atomic_bool mDone {false};\n        std::mutex mMutex;\n        std::condition_variable mCondition;\n    };\n\n    class WorkThread;\n\n    /// @brief A work queue that users can push work items onto, to be completed by one or more background threads.\n    /// @note Work items will be processed in the order that they were given in, however\n    /// if multiple work threads are involved then it is possible for a later item to complete before earlier items.\n    class WorkQueue : public osg::Referenced\n    {\n    public:\n        WorkQueue(int numWorkerThreads=1);\n        ~WorkQueue();\n\n        /// Add a new work item to the back of the queue.\n        /// @par The work item's waitTillDone() method may be used by the caller to wait until the work is complete.\n        /// @param front If true, add item to the front of the queue. If false (default), add to the back.\n        void addWorkItem(osg::ref_ptr<WorkItem> item, bool front=false);\n\n        /// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added.\n        /// If the workqueue is in the process of being destroyed, may return nullptr.\n        /// @par Used internally by the WorkThread.\n        osg::ref_ptr<WorkItem> removeWorkItem();\n\n        unsigned int getNumItems() const;\n\n        unsigned int getNumActiveThreads() const;\n\n    private:\n        bool mIsReleased;\n        std::deque<osg::ref_ptr<WorkItem> > mQueue;\n\n        mutable std::mutex mMutex;\n        std::condition_variable mCondition;\n\n        std::vector<std::unique_ptr<WorkThread>> mThreads;\n    };\n\n    /// Internally used by WorkQueue.\n    class WorkThread\n    {\n    public:\n        WorkThread(WorkQueue& workQueue);\n\n        ~WorkThread();\n\n        bool isActive() const;\n\n    private:\n        WorkQueue* mWorkQueue;\n        std::atomic<bool> mActive;\n        std::thread mThread;\n\n        void run();\n    };\n\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sceneutil/writescene.cpp",
    "content": "#include \"writescene.hpp\"\n\n#include <stdexcept>\n\n#include <osgDB/Registry>\n\n#include <boost/filesystem/fstream.hpp>\n\n#include \"serialize.hpp\"\n\nvoid SceneUtil::writeScene(osg::Node *node, const std::string& filename, const std::string& format)\n{\n    registerSerializers();\n\n    osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(\"osgt\");\n    if (!rw)\n        throw std::runtime_error(\"can not find readerwriter for \" + format);\n\n    boost::filesystem::ofstream stream;\n    stream.open(filename);\n\n    osg::ref_ptr<osgDB::Options> options = new osgDB::Options;\n    options->setPluginStringData(\"fileType\", format);\n    options->setPluginStringData(\"WriteImageHint\", \"UseExternal\");\n\n    rw->writeNode(*node, stream, options);\n}\n"
  },
  {
    "path": "components/sceneutil/writescene.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_WRITESCENE_H\n#define OPENMW_COMPONENTS_WRITESCENE_H\n\n#include <string>\n\nnamespace osg\n{\n    class Node;\n}\n\nnamespace SceneUtil\n{\n\n    void writeScene(osg::Node* node, const std::string& filename, const std::string& format);\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sdlutil/events.hpp",
    "content": "#ifndef _SFO_EVENTS_H\n#define _SFO_EVENTS_H\n\n#include <SDL_types.h>\n#include <SDL_events.h>\n\n////////////\n// Events //\n////////////\n\nnamespace SDLUtil\n{\n\n/** Extended mouse event struct where we treat the wheel like an axis, like everyone expects */\nstruct MouseMotionEvent : SDL_MouseMotionEvent {\n\n    Sint32 zrel;\n    Sint32 z;\n};\n\n\n///////////////\n// Listeners //\n///////////////\n\nclass MouseListener\n{\npublic:\n    virtual ~MouseListener() {}\n    virtual void mouseMoved( const MouseMotionEvent &arg ) = 0;\n    virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;\n    virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;\n    virtual void mouseWheelMoved( const SDL_MouseWheelEvent &arg) = 0;\n};\n\nclass SensorListener\n{\npublic:\n    virtual ~SensorListener() {}\n    virtual void sensorUpdated(const SDL_SensorEvent &arg) = 0;\n    virtual void displayOrientationChanged() = 0;\n};\n\nclass KeyListener\n{\npublic:\n    virtual ~KeyListener() {}\n    virtual void textInput (const SDL_TextInputEvent& arg) {}\n    virtual void keyPressed(const SDL_KeyboardEvent &arg) = 0;\n    virtual void keyReleased(const SDL_KeyboardEvent &arg) = 0;\n};\n\nclass ControllerListener\n{\npublic:\n    virtual ~ControllerListener() {}\n    /** @remarks Joystick button down event */\n    virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &evt) = 0;\n\n    /** @remarks Joystick button up event */\n    virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &evt) = 0;\n\n    /** @remarks Joystick axis moved event */\n    virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) = 0;\n\n    /** @remarks Joystick Added **/\n    virtual void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg) = 0;\n\n    /** @remarks Joystick Removed **/\n    virtual void controllerRemoved(const SDL_ControllerDeviceEvent &arg) = 0;\n\n};\n\nclass WindowListener\n{\npublic:\n    virtual ~WindowListener() {}\n\n    /** @remarks The window's visibility changed */\n    virtual void windowVisibilityChange( bool visible ) {}\n\n    virtual void windowClosed () {}\n\n    virtual void windowResized (int x, int y) {}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sdlutil/gl4es_init.cpp",
    "content": "// EGL does not work reliably for feature detection.\n// Instead, we initialize gl4es manually.\n#ifdef OPENMW_GL4ES_MANUAL_INIT\n#include \"gl4es_init.h\"\n\n// For glHint\n#include <GL/gl.h>\n\nextern \"C\" {\n\n#include <gl4es/gl4esinit.h>\n#include <gl4es/gl4eshint.h>\n\nstatic SDL_Window *gWindow;\n\nvoid openmw_gl4es_GetMainFBSize(int *width, int *height)\n{\n    SDL_GetWindowSize(gWindow, width, height);\n}\n\nvoid openmw_gl4es_init(SDL_Window *window)\n{\n    gWindow = window;\n    set_getprocaddress(SDL_GL_GetProcAddress);\n    set_getmainfbsize(openmw_gl4es_GetMainFBSize);\n    initialize_gl4es();\n\n    // merge glBegin/glEnd in beams and console\n    glHint(GL_BEGINEND_HINT_GL4ES, 1);\n    // dxt unpacked to 16-bit looks ugly\n    glHint(GL_AVOID16BITS_HINT_GL4ES, 1);\n}\n\n}  // extern \"C\"\n\n#endif  // OPENMW_GL4ES_MANUAL_INIT\n"
  },
  {
    "path": "components/sdlutil/gl4es_init.h",
    "content": "#ifndef OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H\n#define OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H\n#ifdef OPENMW_GL4ES_MANUAL_INIT\n#include <SDL_video.h>\n\n// Must be called once SDL video mode has been set,\n// which creates a context.\n//\n// GL4ES can then query the context for features and extensions.\nextern \"C\" void openmw_gl4es_init(SDL_Window *window);\n\n#endif  // OPENMW_GL4ES_MANUAL_INIT\n#endif  // OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H\n"
  },
  {
    "path": "components/sdlutil/imagetosurface.cpp",
    "content": "#include \"imagetosurface.hpp\"\n\n#include <osg/Image>\n#include <SDL_surface.h>\n\nnamespace SDLUtil\n{\n\nSurfaceUniquePtr imageToSurface(osg::Image *image, bool flip)\n{\n    int width = image->s();\n    int height = image->t();\n    SDL_Surface* surface = SDL_CreateRGBSurface(0, width, height, 32, 0xFF000000,0x00FF0000,0x0000FF00,0x000000FF);\n\n    for(int x = 0; x < width; ++x)\n        for(int y = 0; y < height; ++y)\n        {\n            osg::Vec4f clr = image->getColor(x, flip ? ((height-1)-y) : y);\n            int bpp = surface->format->BytesPerPixel;\n            Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;\n            *(Uint32*)(p) = SDL_MapRGBA(surface->format, static_cast<Uint8>(clr.r() * 255),\n                static_cast<Uint8>(clr.g() * 255), static_cast<Uint8>(clr.b() * 255), static_cast<Uint8>(clr.a() * 255));\n        }\n\n    return SurfaceUniquePtr(surface, SDL_FreeSurface);\n}\n\n}\n"
  },
  {
    "path": "components/sdlutil/imagetosurface.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SDLUTIL_IMAGETOSURFACE_H\n#define OPENMW_COMPONENTS_SDLUTIL_IMAGETOSURFACE_H\n\n#include <memory>\n\nstruct SDL_Surface;\n\nnamespace osg\n{\n    class Image;\n}\n\nnamespace SDLUtil\n{\n    typedef std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)> SurfaceUniquePtr;\n\n    /// Convert an osg::Image to an SDL_Surface.\n    SurfaceUniquePtr imageToSurface(osg::Image* image, bool flip=false);\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sdlutil/sdlcursormanager.cpp",
    "content": "#include \"sdlcursormanager.hpp\"\n\n#include <cassert>\n#include <stdexcept>\n#include <cstdlib>\n\n#include <SDL_mouse.h>\n#include <SDL_endian.h>\n#include <SDL_render.h>\n#include <SDL_hints.h>\n\n#include <osg/GraphicsContext>\n#include <osg/Geometry>\n#include <osg/Texture2D>\n#include <osg/TexMat>\n#include <osg/Version>\n#include <osgViewer/GraphicsWindow>\n\n#include <components/debug/debuglog.hpp>\n\n#include \"imagetosurface.hpp\"\n\n#if defined(OSG_LIBRARY_STATIC) && !defined(ANDROID)\n// Sets the default windowing system interface according to the OS.\n// Necessary for OpenSceneGraph to do some things, like decompression.\nUSE_GRAPHICSWINDOW()\n#endif\n\nnamespace CursorDecompression\n{\n    // macOS builds use the OSG fork that includes DXTC commit\n    #if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 8) || defined(__APPLE__)\n    static const bool DXTCSupported = true;\n    #else\n    static const bool DXTCSupported = false;\n    #endif\n\n    class MyGraphicsContext {\n        public:\n            MyGraphicsContext(int w, int h)\n            {\n                osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;\n                traits->x = 0;\n                traits->y = 0;\n                traits->width = w;\n                traits->height = h;\n                traits->red = 8;\n                traits->green = 8;\n                traits->blue = 8;\n                traits->alpha = 8;\n                traits->windowDecoration = false;\n                traits->doubleBuffer = false;\n                traits->sharedContext = nullptr;\n                traits->pbuffer = true;\n\n                osg::GraphicsContext::ScreenIdentifier si;\n                si.readDISPLAY();\n                if (si.displayNum<0) si.displayNum = 0;\n                traits->displayNum = si.displayNum;\n                traits->screenNum = si.screenNum;\n                traits->hostName = si.hostName;\n\n                _gc = osg::GraphicsContext::createGraphicsContext(traits.get());\n\n                if (!_gc)\n                {\n                    Log(Debug::Warning) << \"Failed to create pbuffer, failing back to normal graphics window.\";\n\n                    traits->pbuffer = false;\n                    _gc = osg::GraphicsContext::createGraphicsContext(traits.get());\n                    if (!_gc)\n                        throw std::runtime_error(\"Failed to create graphics context for image decompression\");\n                }\n\n                if (_gc.valid())\n                {\n                    _gc->realize();\n                    _gc->makeCurrent();\n                }\n            }\n\n            osg::ref_ptr<osg::GraphicsContext> getContext()\n            {\n                return _gc;\n            }\n\n            bool valid() const { return _gc.valid() && _gc->isRealized(); }\n\n        private:\n            osg::ref_ptr<osg::GraphicsContext> _gc;\n    };\n\n    SDLUtil::SurfaceUniquePtr hardwareDecompress (osg::ref_ptr<osg::Image> source, float rotDegrees)\n    {\n        int width = source->s();\n        int height = source->t();\n\n        MyGraphicsContext context(width, height);\n\n        osg::ref_ptr<osg::State> state = context.getContext()->getState();\n\n        osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;\n        texture->setImage(source);\n        texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER);\n        texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER);\n        texture->setBorderColor(osg::Vec4f(0.f, 0.f, 0.f, 0.f));\n\n        osg::ref_ptr<osg::TexMat> texmat = new osg::TexMat;\n        osg::Matrix texRot (osg::Matrix::identity());\n        float theta ( osg::DegreesToRadians(-rotDegrees) );\n        float cosTheta = std::cos(theta);\n        float sinTheta = std::sin(theta);\n\n        texRot(0,0) = cosTheta;\n        texRot(1,0) = -sinTheta;\n        texRot(0,1) = sinTheta;\n        texRot(1,1) = cosTheta;\n        // Offset center of rotation to center of texture\n        texRot(3,0) = 0.5f + ( (-0.5f * cosTheta) - (-0.5f * sinTheta) );\n        texRot(3,1) = 0.5f + ( (-0.5f * sinTheta) + (-0.5f * cosTheta) );\n\n        texmat->setMatrix(texRot);\n\n        state->applyTextureAttribute(0, texmat);\n\n        osg::ref_ptr<osg::RefMatrix> identity (new osg::RefMatrix(osg::Matrix::identity()));\n        state->applyModelViewMatrix(identity);\n        state->applyProjectionMatrix(identity);\n\n        state->applyMode(GL_TEXTURE_2D, true);\n        state->applyTextureAttribute(0, texture);\n\n        osg::ref_ptr<osg::Image> resultImage = new osg::Image;\n        resultImage->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);\n\n        osg::RenderInfo renderInfo;\n        renderInfo.setState(state);\n\n        glViewport(0, 0, width, height);\n\n        osg::ref_ptr<osg::Geometry> geom;\n\n        geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,-1,0), osg::Vec3(2,0,0), osg::Vec3(0,2,0));\n\n        geom->drawImplementation(renderInfo);\n\n        glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, resultImage->data());\n\n        geom->releaseGLObjects();\n        source->releaseGLObjects();\n        texture->releaseGLObjects();\n\n        return SDLUtil::imageToSurface(resultImage, true);\n    }\n\n    SDLUtil::SurfaceUniquePtr softwareDecompress (osg::ref_ptr<osg::Image> source, float rotDegrees)\n    {\n        int width = source->s();\n        int height = source->t();\n        bool useAlpha = source->isImageTranslucent();\n\n        osg::ref_ptr<osg::Image> decompressedImage = new osg::Image;\n        decompressedImage->setFileName(source->getFileName());\n        decompressedImage->allocateImage(width, height, 1, useAlpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE);\n        for (int s=0; s<width; ++s)\n            for (int t=0; t<height; ++t)\n                decompressedImage->setColor(source->getColor(s,t,0), s,t,0);\n\n        Uint32 redMask = 0x000000ff;\n        Uint32 greenMask = 0x0000ff00;\n        Uint32 blueMask = 0x00ff0000;\n        Uint32 alphaMask = useAlpha ? 0xff000000 : 0;\n\n        SDL_Surface *cursorSurface = SDL_CreateRGBSurfaceFrom(decompressedImage->data(),\n            width,\n            height,\n            decompressedImage->getPixelSizeInBits(),\n            decompressedImage->getRowSizeInBytes(),\n            redMask,\n            greenMask,\n            blueMask,\n            alphaMask);\n\n        SDL_Surface *targetSurface = SDL_CreateRGBSurface(0, width, height, 32, redMask, greenMask, blueMask, alphaMask);\n        SDL_Renderer *renderer = SDL_CreateSoftwareRenderer(targetSurface);\n\n        SDL_RenderClear(renderer);\n\n        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, \"1\");\n        SDL_Texture *cursorTexture = SDL_CreateTextureFromSurface(renderer, cursorSurface);\n\n        SDL_RenderCopyEx(renderer, cursorTexture, nullptr, nullptr, -rotDegrees, nullptr, SDL_FLIP_VERTICAL);\n\n        SDL_DestroyTexture(cursorTexture);\n        SDL_FreeSurface(cursorSurface);\n        SDL_DestroyRenderer(renderer);\n\n        return SDLUtil::SurfaceUniquePtr(targetSurface, SDL_FreeSurface);\n    }\n\n}\n\nnamespace SDLUtil\n{\n\n    SDLCursorManager::SDLCursorManager() :\n        mEnabled(false),\n        mInitialized(false)\n    {\n    }\n\n    SDLCursorManager::~SDLCursorManager()\n    {\n        CursorMap::const_iterator curs_iter = mCursorMap.begin();\n\n        while(curs_iter != mCursorMap.end())\n        {\n            SDL_FreeCursor(curs_iter->second);\n            ++curs_iter;\n        }\n\n        mCursorMap.clear();\n    }\n\n    void SDLCursorManager::setEnabled(bool enabled)\n    {\n        if(mInitialized && enabled == mEnabled)\n            return;\n\n        mInitialized = true;\n        mEnabled = enabled;\n\n        //turn on hardware cursors\n        if(enabled)\n        {\n            _setGUICursor(mCurrentCursor);\n        }\n        //turn off hardware cursors\n        else\n        {\n            SDL_ShowCursor(SDL_FALSE);\n        }\n    }\n\n    void SDLCursorManager::cursorChanged(const std::string& name)\n    {\n        mCurrentCursor = name;\n        _setGUICursor(name);\n    }\n\n    void SDLCursorManager::_setGUICursor(const std::string &name)\n    {\n        auto it = mCursorMap.find(name);\n        if (it != mCursorMap.end())\n            SDL_SetCursor(it->second);\n    }\n\n    void SDLCursorManager::createCursor(const std::string& name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y)\n    {\n#ifndef ANDROID\n        _createCursorFromResource(name, rotDegrees, image, hotspot_x, hotspot_y);\n#endif\n    }\n\n    void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y)\n    {\n        if (mCursorMap.find(name) != mCursorMap.end())\n            return;\n\n        static bool forceSoftwareDecompression = (getenv(\"OPENMW_DECOMPRESS_TEXTURES\") != nullptr);\n\n        SurfaceUniquePtr (*decompressionFunction)(osg::ref_ptr<osg::Image>, float);\n        if (forceSoftwareDecompression || CursorDecompression::DXTCSupported) {\n            decompressionFunction = CursorDecompression::softwareDecompress;\n        } else {\n            decompressionFunction = CursorDecompression::hardwareDecompress;\n        }\n\n        try {\n            auto surface = decompressionFunction(image, static_cast<float>(rotDegrees));\n\n            //set the cursor and store it for later\n            SDL_Cursor* curs = SDL_CreateColorCursor(surface.get(), hotspot_x, hotspot_y);\n\n            mCursorMap.insert(CursorMap::value_type(std::string(name), curs));\n        } catch (std::exception& e) {\n            Log(Debug::Warning) << e.what();\n            Log(Debug::Warning) << \"Using default cursor.\";\n            return;\n        }\n    }\n\n}\n"
  },
  {
    "path": "components/sdlutil/sdlcursormanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SDLUTIL_SDLCURSORMANAGER_H\n#define OPENMW_COMPONENTS_SDLUTIL_SDLCURSORMANAGER_H\n\n#include <map>\n#include <string>\n\n#include <SDL_types.h>\n\nstruct SDL_Cursor;\nstruct SDL_Surface;\n\nnamespace osg\n{\n    class Image;\n}\n\nnamespace SDLUtil\n{\n    class SDLCursorManager\n    {\n    public:\n        SDLCursorManager();\n        virtual ~SDLCursorManager();\n\n        /// \\brief sets whether to actively manage cursors or not\n        virtual void setEnabled(bool enabled);\n\n        /// \\brief Tell the manager that the cursor has changed, giving the\n        ///        name of the cursor we changed to (\"arrow\", \"ibeam\", etc)\n        virtual void cursorChanged(const std::string &name);\n\n        virtual void createCursor(const std::string &name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y);\n\n    private:\n        void _createCursorFromResource(const std::string &name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y);\n        void _putPixel(SDL_Surface *surface, int x, int y, Uint32 pixel);\n\n        void _setGUICursor(const std::string& name);\n\n        typedef std::map<std::string, SDL_Cursor*> CursorMap;\n        CursorMap mCursorMap;\n\n        std::string mCurrentCursor;\n        bool mEnabled;\n        bool mInitialized;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/sdlutil/sdlgraphicswindow.cpp",
    "content": "#include \"sdlgraphicswindow.hpp\"\n\n#include <SDL_video.h>\n\n#ifdef OPENMW_GL4ES_MANUAL_INIT\n#include \"gl4es_init.h\"\n#endif\n\nnamespace SDLUtil\n{\n\nGraphicsWindowSDL2::~GraphicsWindowSDL2()\n{\n    close(true);\n}\n\nGraphicsWindowSDL2::GraphicsWindowSDL2(osg::GraphicsContext::Traits *traits)\n    : mWindow(nullptr)\n    , mContext(nullptr)\n    , mValid(false)\n    , mRealized(false)\n    , mOwnsWindow(false)\n{\n    _traits = traits;\n\n    init();\n    if(GraphicsWindowSDL2::valid())\n    {\n        setState(new osg::State);\n        getState()->setGraphicsContext(this);\n\n        if(_traits.valid() && _traits->sharedContext.valid())\n        {\n            getState()->setContextID(_traits->sharedContext->getState()->getContextID());\n            incrementContextIDUsageCount(getState()->getContextID());\n        }\n        else\n        {\n            getState()->setContextID(osg::GraphicsContext::createNewContextID());\n        }\n    }\n}\n\n\nbool GraphicsWindowSDL2::setWindowDecorationImplementation(bool flag)\n{\n    if(!mWindow) return false;\n\n    SDL_SetWindowBordered(mWindow, flag ? SDL_TRUE : SDL_FALSE);\n    return true;\n}\n\nbool GraphicsWindowSDL2::setWindowRectangleImplementation(int x, int y, int width, int height)\n{\n    if(!mWindow) return false;\n\n    SDL_SetWindowPosition(mWindow, x, y);\n    SDL_SetWindowSize(mWindow, width, height);\n    return true;\n}\n\nvoid GraphicsWindowSDL2::setWindowName(const std::string &name)\n{\n    if(!mWindow) return;\n\n    SDL_SetWindowTitle(mWindow, name.c_str());\n    _traits->windowName = name;\n}\n\nvoid GraphicsWindowSDL2::setCursor(MouseCursor mouseCursor)\n{\n    _traits->useCursor = false;\n}\n\n\nvoid GraphicsWindowSDL2::init()\n{\n    if(mValid) return;\n\n    if(!_traits.valid())\n        return;\n\n    WindowData *inheritedWindowData = dynamic_cast<WindowData*>(_traits->inheritedWindowData.get());\n    mWindow = inheritedWindowData ? inheritedWindowData->mWindow : nullptr;\n\n    mOwnsWindow = (mWindow == nullptr);\n    if(mOwnsWindow)\n    {\n        OSG_FATAL<<\"Error: No SDL window provided.\"<<std::endl;\n        return;\n    }\n\n    // SDL will change the current context when it creates a new one, so we\n    // have to get the current one to be able to restore it afterward.\n    SDL_Window *oldWin = SDL_GL_GetCurrentWindow();\n    SDL_GLContext oldCtx = SDL_GL_GetCurrentContext();\n   \n#if defined(ANDROID) || defined(OPENMW_GL4ES_MANUAL_INIT)\n    int major = 1;\n    int minor = 1;\n    char *ver = getenv(\"OPENMW_GLES_VERSION\");\n\n    if (ver && strcmp(ver, \"2\") == 0) {\n        major = 2;\n        minor = 0;\n    } else if (ver && strcmp(ver, \"3\") == 0) {\n        major = 3;\n        minor = 2;\n    }\n\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);\n#endif\n    \n    mContext = SDL_GL_CreateContext(mWindow);\n    if(!mContext)\n    {\n        OSG_FATAL<< \"Error: Unable to create OpenGL graphics context: \"<<SDL_GetError() <<std::endl;\n        return;\n    }\n\n#ifdef OPENMW_GL4ES_MANUAL_INIT\n    openmw_gl4es_init(mWindow);\n#endif\n\n    setSwapInterval(_traits->vsync);\n\n    // Update traits with what we've actually been given\n    // Use intermediate to avoid signed/unsigned mismatch\n    int intermediateLocation;\n    SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &intermediateLocation);\n    _traits->red = intermediateLocation;\n    SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &intermediateLocation);\n    _traits->green = intermediateLocation;\n    SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &intermediateLocation);\n    _traits->blue = intermediateLocation;\n    SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &intermediateLocation);\n    _traits->alpha = intermediateLocation;\n    SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &intermediateLocation);\n    _traits->depth = intermediateLocation;\n    SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &intermediateLocation);\n    _traits->stencil = intermediateLocation;\n\n    SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &intermediateLocation);\n    _traits->doubleBuffer = intermediateLocation;\n    SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &intermediateLocation);\n    _traits->sampleBuffers = intermediateLocation;\n    SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &intermediateLocation);\n    _traits->samples = intermediateLocation;\n\n    SDL_GL_MakeCurrent(oldWin, oldCtx);\n\n    mValid = true;\n\n    getEventQueue()->syncWindowRectangleWithGraphicsContext();\n}\n\n\nbool GraphicsWindowSDL2::realizeImplementation()\n{\n    if(mRealized)\n    {\n        OSG_NOTICE<< \"GraphicsWindowSDL2::realizeImplementation() Already realized\" <<std::endl;\n        return true;\n    }\n\n    if(!mValid) init();\n    if(!mValid) return false;\n\n    SDL_ShowWindow(mWindow);\n\n    getEventQueue()->syncWindowRectangleWithGraphicsContext();\n\n    mRealized = true;\n\n    return true;\n}\n\nbool GraphicsWindowSDL2::makeCurrentImplementation()\n{\n    if(!mRealized)\n    {\n        OSG_WARN<<\"Warning: GraphicsWindow not realized, cannot do makeCurrent.\"<<std::endl;\n        return false;\n    }\n\n    return SDL_GL_MakeCurrent(mWindow, mContext)==0;\n}\n\nbool GraphicsWindowSDL2::releaseContextImplementation()\n{\n    if(!mRealized)\n    {\n        OSG_WARN<< \"Warning: GraphicsWindow not realized, cannot do releaseContext.\" <<std::endl;\n        return false;\n    }\n\n    return SDL_GL_MakeCurrent(nullptr, nullptr)==0;\n}\n\n\nvoid GraphicsWindowSDL2::closeImplementation()\n{\n    if(mContext)\n        SDL_GL_DeleteContext(mContext);\n    mContext = nullptr;\n\n    if(mWindow && mOwnsWindow)\n        SDL_DestroyWindow(mWindow);\n    mWindow = nullptr;\n\n    mValid = false;\n    mRealized = false;\n}\n\nvoid GraphicsWindowSDL2::swapBuffersImplementation()\n{\n    if(!mRealized) return;\n\n    SDL_GL_SwapWindow(mWindow);\n}\n\nvoid GraphicsWindowSDL2::setSyncToVBlank(bool on)\n{\n    SDL_Window *oldWin = SDL_GL_GetCurrentWindow();\n    SDL_GLContext oldCtx = SDL_GL_GetCurrentContext();\n\n    SDL_GL_MakeCurrent(mWindow, mContext);\n\n    setSwapInterval(on);\n\n    SDL_GL_MakeCurrent(oldWin, oldCtx);\n}\n\nvoid GraphicsWindowSDL2::setSwapInterval(bool enable)\n{\n    if (enable)\n    {\n        if (SDL_GL_SetSwapInterval(-1) == -1)\n        {\n            OSG_NOTICE << \"Adaptive vsync unsupported\" << std::endl;\n            if (SDL_GL_SetSwapInterval(1) == -1)\n            {\n                OSG_NOTICE << \"Vertical synchronization unsupported, disabling\" << std::endl;\n                SDL_GL_SetSwapInterval(0);\n            }\n        }\n    }\n    else\n    {\n        SDL_GL_SetSwapInterval(0);\n    }\n}\n\nvoid GraphicsWindowSDL2::raiseWindow()\n{\n    SDL_RaiseWindow(mWindow);\n}\n\n}\n"
  },
  {
    "path": "components/sdlutil/sdlgraphicswindow.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SDLUTIL_SDLGRAPHICSWINDOW_H\n#define OPENMW_COMPONENTS_SDLUTIL_SDLGRAPHICSWINDOW_H\n\n#include <SDL_video.h>\n\n#include <osgViewer/GraphicsWindow>\n\nnamespace SDLUtil\n{\n\nclass GraphicsWindowSDL2 : public osgViewer::GraphicsWindow\n{\n    SDL_Window*     mWindow;\n    SDL_GLContext   mContext;\n\n    bool            mValid;\n    bool            mRealized;\n    bool            mOwnsWindow;\n\n    void init();\n\n    virtual ~GraphicsWindowSDL2();\n\npublic:\n    GraphicsWindowSDL2(osg::GraphicsContext::Traits *traits);\n\n    bool isSameKindAs(const Object* object) const override { return dynamic_cast<const GraphicsWindowSDL2*>(object)!=nullptr; }\n    const char* libraryName() const override { return \"osgViewer\"; }\n    const char* className() const override { return \"GraphicsWindowSDL2\"; }\n\n    bool valid() const override { return mValid; }\n\n    /** Realise the GraphicsContext.*/\n    bool realizeImplementation()override ;\n\n    /** Return true if the graphics context has been realised and is ready to use.*/\n    bool isRealizedImplementation() const override { return mRealized; }\n\n    /** Close the graphics context.*/\n    void closeImplementation() override;\n\n    /** Make this graphics context current.*/\n    bool makeCurrentImplementation() override;\n\n    /** Release the graphics context.*/\n    bool releaseContextImplementation() override;\n\n    /** Swap the front and back buffers.*/\n    void swapBuffersImplementation() override;\n\n    /** Set sync-to-vblank. */\n    void setSyncToVBlank(bool on) override;\n\n    /** Set Window decoration.*/\n    bool setWindowDecorationImplementation(bool flag) override;\n\n    /** Raise specified window */\n    void raiseWindow() override;\n\n    /** Set the window's position and size.*/\n    bool setWindowRectangleImplementation(int x, int y, int width, int height) override;\n\n    /** Set the name of the window */\n    void setWindowName(const std::string &name) override;\n\n    /** Set mouse cursor to a specific shape.*/\n    void setCursor(MouseCursor cursor) override;\n\n    /** Get focus.*/\n    void grabFocus() override {}\n\n    /** Get focus on if the pointer is in this window.*/\n    void grabFocusIfPointerInWindow() override {}\n\n    /** WindowData is used to pass in the SDL2 window handle attached to the GraphicsContext::Traits structure. */\n    struct WindowData : public osg::Referenced\n    {\n        WindowData(SDL_Window *window) : mWindow(window)\n        { }\n\n        SDL_Window *mWindow;\n    };\n\nprivate:\n    void setSwapInterval(bool enable);\n};\n\n}\n\n#endif /* OSGGRAPHICSWINDOW_H */\n"
  },
  {
    "path": "components/sdlutil/sdlinputwrapper.cpp",
    "content": "#include \"sdlinputwrapper.hpp\"\n\n#include <components/debug/debuglog.hpp>\n#include <components/settings/settings.hpp>\n\n#include <osgViewer/Viewer>\n\nnamespace SDLUtil\n{\n\nInputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> viewer, bool grab) :\n        mSDLWindow(window),\n        mViewer(viewer),\n        mMouseListener(nullptr),\n        mSensorListener(nullptr),\n        mKeyboardListener(nullptr),\n        mWindowListener(nullptr),\n        mConListener(nullptr),\n        mWarpX(0),\n        mWarpY(0),\n        mWarpCompensate(false),\n        mWrapPointer(false),\n        mAllowGrab(grab),\n        mWantMouseVisible(false),\n        mWantGrab(false),\n        mWantRelative(false),\n        mGrabPointer(false),\n        mMouseRelative(false),\n        mFirstMouseMove(true),\n        mMouseZ(0),\n        mMouseX(0),\n        mMouseY(0),\n        mWindowHasFocus(true),\n        mMouseInWindow(true)\n    {\n        Uint32 flags = SDL_GetWindowFlags(mSDLWindow);\n        mWindowHasFocus = (flags & SDL_WINDOW_INPUT_FOCUS);\n        mMouseInWindow = (flags & SDL_WINDOW_MOUSE_FOCUS);\n    }\n\n    InputWrapper::~InputWrapper()\n    {\n    }\n\n    void InputWrapper::capture(bool windowEventsOnly)\n    {\n        mViewer->getEventQueue()->frame(0.f);\n\n        SDL_PumpEvents();\n\n        SDL_Event evt;\n\n        if (windowEventsOnly)\n        {\n            // During loading, handle window events, discard button presses and keep others for later\n            while (SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT))\n                handleWindowEvent(evt);\n\n            SDL_FlushEvent(SDL_KEYDOWN);\n            SDL_FlushEvent(SDL_CONTROLLERBUTTONDOWN);\n            SDL_FlushEvent(SDL_MOUSEBUTTONDOWN);\n\n            return;\n        }\n\n        while(SDL_PollEvent(&evt))\n        {\n            switch(evt.type)\n            {\n                case SDL_MOUSEMOTION:\n                    // Ignore this if it happened due to a warp\n                    if(!_handleWarpMotion(evt.motion))\n                    {\n                        // If in relative mode, don't trigger events unless window has focus\n                        if (!mWantRelative || mWindowHasFocus)\n                            mMouseListener->mouseMoved(_packageMouseMotion(evt));\n\n                        // Try to keep the mouse inside the window\n                        if (mWindowHasFocus)\n                            _wrapMousePointer(evt.motion);\n                    }\n                    break;\n                case SDL_MOUSEWHEEL:\n                    mMouseListener->mouseMoved(_packageMouseMotion(evt));\n                    mMouseListener->mouseWheelMoved(evt.wheel);\n                    break;\n                case SDL_SENSORUPDATE:\n                    mSensorListener->sensorUpdated(evt.sensor);\n                    break;\n                case SDL_MOUSEBUTTONDOWN:\n                    mMouseListener->mousePressed(evt.button, evt.button.button);\n                    break;\n                case SDL_MOUSEBUTTONUP:\n                    mMouseListener->mouseReleased(evt.button, evt.button.button);\n                    break;\n                case SDL_KEYDOWN:\n                    mKeyboardListener->keyPressed(evt.key);\n\n                    if (!isModifierHeld(KMOD_ALT) && evt.key.keysym.sym >= SDLK_F1 && evt.key.keysym.sym <= SDLK_F12)\n                    {\n                        mViewer->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_F1 + (evt.key.keysym.sym - SDLK_F1));\n                    }\n\n                    break;\n                case SDL_KEYUP:\n                    if (!evt.key.repeat)\n                    {\n                        mKeyboardListener->keyReleased(evt.key);\n\n                        if (!isModifierHeld(KMOD_ALT) && evt.key.keysym.sym >= SDLK_F1 && evt.key.keysym.sym <= SDLK_F12)\n                            mViewer->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_F1 + (evt.key.keysym.sym - SDLK_F1));\n                    }\n\n                    break;\n                case SDL_TEXTEDITING:\n                    break;\n                case SDL_TEXTINPUT:\n                    mKeyboardListener->textInput(evt.text);\n                    break;\n                case SDL_KEYMAPCHANGED:\n                    break;\n                case SDL_JOYHATMOTION: //As we manage everything with GameController, don't even bother with these.\n                case SDL_JOYAXISMOTION:\n                case SDL_JOYBUTTONDOWN:\n                case SDL_JOYBUTTONUP:\n                case SDL_JOYDEVICEADDED:\n                case SDL_JOYDEVICEREMOVED:\n                    break;\n                case SDL_CONTROLLERDEVICEADDED:\n                    if(mConListener)\n                        mConListener->controllerAdded(1, evt.cdevice); //We only support one joystick, so give everything a generic deviceID\n                    break;\n                case SDL_CONTROLLERDEVICEREMOVED:\n                    if(mConListener)\n                        mConListener->controllerRemoved(evt.cdevice);\n                    break;\n                case SDL_CONTROLLERBUTTONDOWN:\n                    if(mConListener)\n                        mConListener->buttonPressed(1, evt.cbutton);\n                    break;\n                case SDL_CONTROLLERBUTTONUP:\n                    if(mConListener)\n                        mConListener->buttonReleased(1, evt.cbutton);\n                    break;\n                case SDL_CONTROLLERAXISMOTION:\n                    if(mConListener)\n                        mConListener->axisMoved(1, evt.caxis);\n                    break;\n                case SDL_WINDOWEVENT:\n                    handleWindowEvent(evt);\n                    break;\n                case SDL_QUIT:\n                    if (mWindowListener)\n                        mWindowListener->windowClosed();\n                    break;\n                case SDL_DISPLAYEVENT:\n                    switch (evt.display.event)\n                    {\n                        case SDL_DISPLAYEVENT_ORIENTATION:\n                            if (mSensorListener && evt.display.display == (unsigned int) Settings::Manager::getInt(\"screen\", \"Video\"))\n                                mSensorListener->displayOrientationChanged();\n                            break;\n                        default:\n                            break;\n                    }\n                    break;\n                case SDL_CLIPBOARDUPDATE:\n                    break; // We don't need this event, clipboard is retrieved on demand\n\n                case SDL_FINGERDOWN:\n                case SDL_FINGERUP:\n                case SDL_FINGERMOTION:\n                case SDL_DOLLARGESTURE:\n                case SDL_DOLLARRECORD:\n                case SDL_MULTIGESTURE:\n                    // No use for touch & gesture events\n                    break;\n\n                case SDL_APP_WILLENTERBACKGROUND:\n                case SDL_APP_WILLENTERFOREGROUND:\n                case SDL_APP_DIDENTERBACKGROUND:\n                case SDL_APP_DIDENTERFOREGROUND:\n                    // We do not need background/foreground switch event for mobile devices so far\n                    break;\n\n                case SDL_APP_TERMINATING:\n                    // There is nothing we can do here.\n                    break;\n\n                case SDL_APP_LOWMEMORY:\n                    Log(Debug::Warning) << \"System reports that free RAM on device is running low. You may encounter an unexpected behaviour.\";\n                    break;\n\n                default:\n                    Log(Debug::Info) << \"Unhandled SDL event of type 0x\" << std::hex << evt.type;\n                    break;\n            }\n        }\n    }\n\n    void InputWrapper::handleWindowEvent(const SDL_Event& evt)\n    {\n        switch (evt.window.event) {\n            case SDL_WINDOWEVENT_ENTER:\n                mMouseInWindow = true;\n                updateMouseSettings();\n                break;\n            case SDL_WINDOWEVENT_LEAVE:\n                mMouseInWindow = false;\n                updateMouseSettings();\n                break;\n            case SDL_WINDOWEVENT_MOVED:\n                // I'm not sure what OSG is using the window position for, but I don't think it's needed,\n                // so we ignore window moved events (improves window movement performance)\n                break;\n            case SDL_WINDOWEVENT_SIZE_CHANGED:\n                int w,h;\n                SDL_GetWindowSize(mSDLWindow, &w, &h);\n                int x,y;\n                SDL_GetWindowPosition(mSDLWindow, &x,&y);\n                mViewer->getCamera()->getGraphicsContext()->resized(x,y,w,h);\n\n                mViewer->getEventQueue()->windowResize(x,y,w,h);\n\n                if (mWindowListener)\n                    mWindowListener->windowResized(w, h);\n\n                break;\n\n            case SDL_WINDOWEVENT_RESIZED:\n                // This should also fire SIZE_CHANGED, so no need to handle\n                break;\n\n            case SDL_WINDOWEVENT_FOCUS_GAINED:\n                mWindowHasFocus = true;\n                updateMouseSettings();\n                break;\n            case SDL_WINDOWEVENT_FOCUS_LOST:\n                mWindowHasFocus = false;\n                updateMouseSettings();\n                break;\n            case SDL_WINDOWEVENT_CLOSE:\n                break;\n            case SDL_WINDOWEVENT_SHOWN:\n            case SDL_WINDOWEVENT_RESTORED:\n                if (mWindowListener)\n                    mWindowListener->windowVisibilityChange(true);\n                break;\n            case SDL_WINDOWEVENT_HIDDEN:\n            case SDL_WINDOWEVENT_MINIMIZED:\n                if (mWindowListener)\n                    mWindowListener->windowVisibilityChange(false);\n                break;\n        }\n    }\n\n    bool InputWrapper::isModifierHeld(int mod)\n    {\n        return (SDL_GetModState() & mod) != 0;\n    }\n\n    bool InputWrapper::isKeyDown(SDL_Scancode key)\n    {\n        return (SDL_GetKeyboardState(nullptr)[key]) != 0;\n    }\n\n    /// \\brief Moves the mouse to the specified point within the viewport\n    void InputWrapper::warpMouse(int x, int y)\n    {\n        SDL_WarpMouseInWindow(mSDLWindow, x, y);\n        mWarpCompensate = true;\n        mWarpX = x;\n        mWarpY = y;\n    }\n\n    /// \\brief Locks the pointer to the window\n    void InputWrapper::setGrabPointer(bool grab)\n    {\n        mWantGrab = grab;\n        updateMouseSettings();\n    }\n\n    /// \\brief Set the mouse to relative positioning. Doesn't move the cursor\n    ///        and disables mouse acceleration.\n    void InputWrapper::setMouseRelative(bool relative)\n    {\n        mWantRelative = relative;\n        updateMouseSettings();\n    }\n\n    void InputWrapper::setMouseVisible(bool visible)\n    {\n        mWantMouseVisible = visible;\n        updateMouseSettings();\n    }\n\n    void InputWrapper::updateMouseSettings()\n    {\n        mGrabPointer = mWantGrab && mMouseInWindow && mWindowHasFocus;\n        SDL_SetWindowGrab(mSDLWindow, mGrabPointer && mAllowGrab ? SDL_TRUE : SDL_FALSE);\n\n        SDL_ShowCursor(mWantMouseVisible || !mWindowHasFocus);\n\n        bool relative = mWantRelative && mMouseInWindow && mWindowHasFocus;\n        if(mMouseRelative == relative)\n            return;\n\n        mMouseRelative = relative;\n\n        mWrapPointer = false;\n\n        // eep, wrap the pointer manually if the input driver doesn't support\n        // relative positioning natively\n        // also use wrapping if no-grab was specified in options (SDL_SetRelativeMouseMode\n        // appears to eat the mouse cursor when pausing in a debugger)\n        bool success = mAllowGrab && SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0;\n        if(relative && !success)\n            mWrapPointer = true;\n\n        //now remove all mouse events using the old setting from the queue\n        SDL_PumpEvents();\n        SDL_FlushEvent(SDL_MOUSEMOTION);\n    }\n\n    /// \\brief Internal method for ignoring relative motions as a side effect\n    ///        of warpMouse()\n    bool InputWrapper::_handleWarpMotion(const SDL_MouseMotionEvent& evt)\n    {\n        if(!mWarpCompensate)\n            return false;\n\n        //this was a warp event, signal the caller to eat it.\n        if(evt.x == mWarpX && evt.y == mWarpY)\n        {\n            mWarpCompensate = false;\n            return true;\n        }\n\n        return false;\n    }\n\n    /// \\brief Wrap the mouse to the viewport\n    void InputWrapper::_wrapMousePointer(const SDL_MouseMotionEvent& evt)\n    {\n        //don't wrap if we don't want relative movements, support relative\n        //movements natively, or aren't grabbing anyways\n        if(!mMouseRelative || !mWrapPointer || !mGrabPointer)\n            return;\n\n        int width = 0;\n        int height = 0;\n\n        SDL_GetWindowSize(mSDLWindow, &width, &height);\n\n        const int FUDGE_FACTOR_X = width/4;\n        const int FUDGE_FACTOR_Y = height/4;\n\n        //warp the mouse if it's about to go outside the window\n        if(evt.x - FUDGE_FACTOR_X < 0  || evt.x + FUDGE_FACTOR_X > width\n                || evt.y - FUDGE_FACTOR_Y < 0 || evt.y + FUDGE_FACTOR_Y > height)\n        {\n            warpMouse(width / 2, height / 2);\n        }\n    }\n\n    /// \\brief Package mouse and mousewheel motions into a single event\n    MouseMotionEvent InputWrapper::_packageMouseMotion(const SDL_Event &evt)\n    {\n        MouseMotionEvent pack_evt = {};\n        pack_evt.x = mMouseX;\n        pack_evt.y = mMouseY;\n        pack_evt.z = mMouseZ;\n\n        if(evt.type == SDL_MOUSEMOTION)\n        {\n            pack_evt.x = mMouseX = evt.motion.x;\n            pack_evt.y = mMouseY = evt.motion.y;\n            pack_evt.xrel = evt.motion.xrel;\n            pack_evt.yrel = evt.motion.yrel;\n            pack_evt.type = SDL_MOUSEMOTION;\n            if (mFirstMouseMove)\n            {\n                // first event should be treated as non-relative, since there's no point of reference\n                // SDL then (incorrectly) uses (0,0) as point of reference, on Linux at least...\n                pack_evt.xrel = pack_evt.yrel = 0;\n                mFirstMouseMove = false;\n            }\n        }\n        else if(evt.type == SDL_MOUSEWHEEL)\n        {\n            mMouseZ += pack_evt.zrel = (evt.wheel.y * 120);\n            pack_evt.z = mMouseZ;\n            pack_evt.type = SDL_MOUSEWHEEL;\n        }\n        else\n        {\n            throw std::runtime_error(\"Tried to package non-motion event!\");\n        }\n\n        return pack_evt;\n    }\n}\n"
  },
  {
    "path": "components/sdlutil/sdlinputwrapper.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SDLUTIL_SDLINPUTWRAPPER_H\n#define OPENMW_COMPONENTS_SDLUTIL_SDLINPUTWRAPPER_H\n\n#include <map>\n\n#include <osg/ref_ptr>\n\n#include <SDL_events.h>\n#include <SDL_version.h>\n\n#include \"events.hpp\"\n\nnamespace osgViewer\n{\n    class Viewer;\n}\n\nnamespace SDLUtil\n{\n    /// \\brief A wrapper around SDL's event queue, mostly used for handling input-related events.\n    class InputWrapper\n    {\n    public:\n        InputWrapper(SDL_Window *window, osg::ref_ptr<osgViewer::Viewer> viewer, bool grab);\n        ~InputWrapper();\n\n        void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; }\n        void setSensorEventCallback(SensorListener* listen) { mSensorListener = listen; }\n        void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; }\n        void setWindowEventCallback(WindowListener* listen) { mWindowListener = listen; }\n        void setControllerEventCallback(ControllerListener* listen) { mConListener = listen; }\n\n        void capture(bool windowEventsOnly);\n        bool isModifierHeld(int mod);\n        bool isKeyDown(SDL_Scancode key);\n\n        void setMouseVisible (bool visible);\n        void setMouseRelative(bool relative);\n        bool getMouseRelative() { return mMouseRelative; }\n        void setGrabPointer(bool grab);\n\n        void warpMouse(int x, int y);\n\n        void updateMouseSettings();\n\n    private:\n        void handleWindowEvent(const SDL_Event& evt);\n\n        bool _handleWarpMotion(const SDL_MouseMotionEvent& evt);\n        void _wrapMousePointer(const SDL_MouseMotionEvent &evt);\n        MouseMotionEvent _packageMouseMotion(const SDL_Event& evt);\n\n        SDL_Window* mSDLWindow;\n        osg::ref_ptr<osgViewer::Viewer> mViewer;\n\n        MouseListener* mMouseListener;\n        SensorListener* mSensorListener;\n        KeyListener* mKeyboardListener;\n        WindowListener* mWindowListener;\n        ControllerListener* mConListener;\n\n        Uint16 mWarpX;\n        Uint16 mWarpY;\n        bool mWarpCompensate;\n        bool mWrapPointer;\n\n        bool mAllowGrab;\n        bool mWantMouseVisible;\n        bool mWantGrab;\n        bool mWantRelative;\n        bool mGrabPointer;\n        bool mMouseRelative;\n\n        bool mFirstMouseMove;\n\n        Sint32 mMouseZ;\n        Sint32 mMouseX;\n        Sint32 mMouseY;\n\n        bool mWindowHasFocus;\n        bool mMouseInWindow;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/sdlutil/sdlvideowrapper.cpp",
    "content": "#include \"sdlvideowrapper.hpp\"\n\n#include <components/debug/debuglog.hpp>\n\n#include <osgViewer/Viewer>\n\n#include <SDL_video.h>\n\nnamespace SDLUtil\n{\n\n    VideoWrapper::VideoWrapper(SDL_Window *window, osg::ref_ptr<osgViewer::Viewer> viewer)\n        : mWindow(window)\n        , mViewer(viewer)\n        , mGamma(1.f)\n        , mContrast(1.f)\n        , mHasSetGammaContrast(false)\n    {\n        SDL_GetWindowGammaRamp(mWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]);\n    }\n\n    VideoWrapper::~VideoWrapper()\n    {\n        SDL_SetWindowFullscreen(mWindow, 0);\n\n        // If user hasn't touched the defaults no need to restore\n        if (mHasSetGammaContrast)\n            SDL_SetWindowGammaRamp(mWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]);\n    }\n\n    void VideoWrapper::setSyncToVBlank(bool sync)\n    {\n        osgViewer::Viewer::Windows windows;\n        mViewer->getWindows(windows);\n        mViewer->stopThreading();\n        for (osgViewer::Viewer::Windows::iterator it = windows.begin(); it != windows.end(); ++it)\n        {\n            osgViewer::GraphicsWindow* win = *it;\n            win->setSyncToVBlank(sync);\n        }\n        mViewer->startThreading();\n    }\n\n    void VideoWrapper::setGammaContrast(float gamma, float contrast)\n    {\n        if (gamma == mGamma && contrast == mContrast)\n            return;\n\n        mGamma = gamma;\n        mContrast = contrast;\n\n        mHasSetGammaContrast = true;\n\n        Uint16 red[256], green[256], blue[256];\n        for (int i = 0; i < 256; i++)\n        {\n            float k = i/256.0f;\n            k = (k - 0.5f) * contrast + 0.5f;\n            k = pow(k, 1.f/gamma);\n            k *= 256;\n            float value = k*256;\n            if (value > 65535)  value = 65535;\n            else if (value < 0) value = 0;\n\n            red[i] = green[i] = blue[i] = static_cast<Uint16>(value);\n        }\n        if (SDL_SetWindowGammaRamp(mWindow, red, green, blue) < 0)\n            Log(Debug::Warning) << \"Couldn't set gamma: \" << SDL_GetError();\n    }\n\n    void VideoWrapper::setVideoMode(int width, int height, bool fullscreen, bool windowBorder)\n    {\n        SDL_SetWindowFullscreen(mWindow, 0);\n\n        if (SDL_GetWindowFlags(mWindow) & SDL_WINDOW_MAXIMIZED)\n            SDL_RestoreWindow(mWindow);\n\n        if (fullscreen)\n        {\n            SDL_DisplayMode mode;\n            SDL_GetWindowDisplayMode(mWindow, &mode);\n            mode.w = width;\n            mode.h = height;\n            SDL_SetWindowDisplayMode(mWindow, &mode);\n            SDL_SetWindowFullscreen(mWindow, fullscreen);\n        }\n        else\n        {\n            SDL_SetWindowSize(mWindow, width, height);\n            SDL_SetWindowBordered(mWindow, windowBorder ? SDL_TRUE : SDL_FALSE);\n\n            centerWindow();\n        }\n    }\n\n    void VideoWrapper::centerWindow()\n    {\n        // Resize breaks the sdl window in some cases; see issue: #5539\n        SDL_Rect rect{};\n        int x = 0;\n        int y = 0;\n        int w = 0;\n        int h = 0;\n        auto index = SDL_GetWindowDisplayIndex(mWindow);\n        SDL_GetDisplayBounds(index, &rect);\n        SDL_GetWindowSize(mWindow, &w, &h);\n\n        x = rect.x;\n        y = rect.y;\n\n        // Center dimensions that do not fill the screen\n        if (w < rect.w)\n        {\n            x = rect.x + rect.w / 2 - w / 2;\n        }\n        if (h < rect.h)\n        {\n            y = rect.y + rect.h / 2 - h / 2;\n        }\n\n        SDL_SetWindowPosition(mWindow, x, y);\n    }\n\n}\n"
  },
  {
    "path": "components/sdlutil/sdlvideowrapper.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SDLUTIL_SDLVIDEOWRAPPER_H\n#define OPENMW_COMPONENTS_SDLUTIL_SDLVIDEOWRAPPER_H\n\n#include <osg/ref_ptr>\n\n#include <SDL_types.h>\n\nstruct SDL_Window;\n\nnamespace osgViewer\n{\n    class Viewer;\n}\n\nnamespace SDLUtil\n{\n\n    class VideoWrapper\n    {\n    public:\n        VideoWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> viewer);\n        ~VideoWrapper();\n\n        void setSyncToVBlank(bool sync);\n\n        void setGammaContrast(float gamma, float contrast);\n\n        void setVideoMode(int width, int height, bool fullscreen, bool windowBorder);\n\n        void centerWindow();\n\n    private:\n        SDL_Window* mWindow;\n        osg::ref_ptr<osgViewer::Viewer> mViewer;\n\n        float mGamma;\n        float mContrast;\n        bool mHasSetGammaContrast;\n\n        // Store system gamma ramp on window creation. Restore system gamma ramp on exit\n        Uint16 mOldSystemGammaRamp[256*3];\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/settings/categories.hpp",
    "content": "#ifndef COMPONENTS_SETTINGS_CATEGORIES_H\n#define COMPONENTS_SETTINGS_CATEGORIES_H\n\n#include <map>\n#include <set>\n#include <string>\n#include <utility>\n\nnamespace Settings\n{\n    using CategorySetting = std::pair<std::string, std::string>;\n    using CategorySettingVector = std::set<std::pair<std::string, std::string>>;\n    using CategorySettingValueMap = std::map<CategorySetting, std::string>;\n}\n\n#endif // COMPONENTS_SETTINGS_CATEGORIES_H\n"
  },
  {
    "path": "components/settings/parser.cpp",
    "content": "#include \"parser.hpp\"\n\n#include <sstream>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n\n#include <boost/filesystem/fstream.hpp>\n\n#include <Base64.h>\n\nvoid Settings::SettingsFileParser::loadSettingsFile(const std::string& file, CategorySettingValueMap& settings, bool base64Encoded)\n{\n    mFile = file;\n    boost::filesystem::ifstream fstream;\n    fstream.open(boost::filesystem::path(file));\n    auto stream = std::ref<std::istream>(fstream);\n\n    std::istringstream decodedStream;\n    if (base64Encoded)\n    {\n        std::string base64String(std::istreambuf_iterator<char>(fstream), {});\n        std::string decodedString;\n        auto result = Base64::Base64::Decode(base64String, decodedString);\n        if (!result.empty())\n            fail(\"Could not decode Base64 file: \" + result);\n        // Move won't do anything until C++20, but won't hurt to do it anyway.\n        decodedStream.str(std::move(decodedString));\n        stream = std::ref<std::istream>(decodedStream);\n    }\n\n    Log(Debug::Info) << \"Loading settings file: \" << file;\n    std::string currentCategory;\n    mLine = 0;\n    while (!stream.get().eof() && !stream.get().fail())\n    {\n        ++mLine;\n        std::string line;\n        std::getline( stream.get(), line );\n\n        size_t i = 0;\n        if (!skipWhiteSpace(i, line))\n            continue;\n\n        if (line[i] == '#') // skip comment\n            continue;\n\n        if (line[i] == '[')\n        {\n            size_t end = line.find(']', i);\n            if (end == std::string::npos)\n                fail(\"unterminated category\");\n\n            currentCategory = line.substr(i+1, end - (i+1));\n            Misc::StringUtils::trim(currentCategory);\n            i = end+1;\n        }\n\n        if (!skipWhiteSpace(i, line))\n            continue;\n\n        if (currentCategory.empty())\n            fail(\"empty category name\");\n\n        size_t settingEnd = line.find('=', i);\n        if (settingEnd == std::string::npos)\n            fail(\"unterminated setting name\");\n\n        std::string setting = line.substr(i, (settingEnd-i));\n        Misc::StringUtils::trim(setting);\n\n        size_t valueBegin = settingEnd+1;\n        std::string value = line.substr(valueBegin);\n        Misc::StringUtils::trim(value);\n\n        if (settings.insert(std::make_pair(std::make_pair(currentCategory, setting), value)).second == false)\n            fail(std::string(\"duplicate setting: [\" + currentCategory + \"] \" + setting));\n    }\n}\n\nvoid Settings::SettingsFileParser::saveSettingsFile(const std::string& file, const CategorySettingValueMap& settings)\n{\n    using CategorySettingStatusMap = std::map<CategorySetting, bool>;\n\n    // No options have been written to the file yet.\n    CategorySettingStatusMap written;\n    for (auto it = settings.begin(); it != settings.end(); ++it) {\n        written[it->first] = false;\n    }\n\n    // Have we substantively changed the settings file?\n    bool changed = false;\n\n    // Were there any lines at all in the file?\n    bool existing = false;\n\n    // Is an entirely blank line queued to be added?\n    bool emptyLineQueued = false;\n\n    // The category/section we're currently in.\n    std::string currentCategory;\n\n    // Open the existing settings.cfg file to copy comments.  This might not be the same file\n    // as the output file if we're copying the setting from the default settings.cfg for the\n    // first time.  A minor change in API to pass the source file might be in order here.\n    boost::filesystem::ifstream istream;\n    boost::filesystem::path ipath(file);\n    istream.open(ipath);\n\n    // Create a new string stream to write the current settings to.  It's likely that the\n    // input file and the output file are the same, so this stream serves as a temporary file\n    // of sorts.  The setting files aren't very large so there's no performance issue.\n    std::stringstream ostream;\n\n    // For every line in the input file...\n    while (!istream.eof() && !istream.fail()) {\n        std::string line;\n        std::getline(istream, line);\n\n        // The current character position in the line.\n        size_t i = 0;\n\n        // An empty line was queued.\n        if (emptyLineQueued)\n        {\n            emptyLineQueued = false;\n            // We're still going through the current category, so we should copy it.\n            if (currentCategory.empty() || istream.eof() || line[i] != '[')\n                ostream << std::endl;\n        }\n\n        // Don't add additional newlines at the end of the file otherwise.\n        if (istream.eof()) continue;\n\n        // Queue entirely blank lines to add them if desired.\n        if (!skipWhiteSpace(i, line))\n        {\n            emptyLineQueued = true;\n            continue;\n        }\n\n        // There were at least some comments in the input file.\n        existing = true;\n\n        // Copy comments.\n        if (line[i] == '#') {\n            ostream << line << std::endl;\n            continue;\n        }\n\n        // Category heading.\n        if (line[i] == '[') {\n            size_t end = line.find(']', i);\n            // This should never happen unless the player edited the file while playing.\n            if (end == std::string::npos) {\n                ostream << \"# unterminated category: \" << line << std::endl;\n                changed = true;\n                continue;\n            }\n\n            if (!currentCategory.empty())\n            {\n                // Ensure that all options in the current category have been written.\n                for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit)\n                {\n                    if (mit->second == false && mit->first.first == currentCategory)\n                    {\n                        Log(Debug::Verbose) << \"Added new setting: [\" << currentCategory << \"] \"\n                                << mit->first.second << \" = \" << settings.at(mit->first);\n                        ostream << mit->first.second << \" = \" << settings.at(mit->first) << std::endl;\n                        mit->second = true;\n                        changed = true;\n                    }\n                }\n                // Add an empty line after the last option in a category.\n                ostream << std::endl;\n            }\n\n            // Update the current category.\n            currentCategory = line.substr(i+1, end - (i+1));\n            Misc::StringUtils::trim(currentCategory);\n\n            // Write the (new) current category to the file.\n            ostream << \"[\" << currentCategory << \"]\" << std::endl;\n            // Log(Debug::Verbose) << \"Wrote category: \" << currentCategory;\n\n            // A setting can apparently follow the category on an input line.  That's rather\n            // inconvenient, since it makes it more likely to have duplicative sections,\n            // which our algorithm doesn't like.  Do the best we can.\n            i = end+1;\n        }\n\n        // Truncate trailing whitespace, since we're changing the file anayway.\n        if (!skipWhiteSpace(i, line))\n            continue;\n\n        // If we've found settings before the first category, something's wrong.  This\n        // should never happen unless the player edited the file while playing, since\n        // the loadSettingsFile() logic rejects it.\n        if (currentCategory.empty()) {\n            ostream << \"# empty category name: \" << line << std::endl;\n            changed = true;\n            continue;\n        }\n\n        // Which setting was at this location in the input file?\n        size_t settingEnd = line.find('=', i);\n        // This should never happen unless the player edited the file while playing.\n        if (settingEnd == std::string::npos) {\n            ostream << \"# unterminated setting name: \" << line << std::endl;\n            changed = true;\n            continue;\n        }\n        std::string setting = line.substr(i, (settingEnd-i));\n        Misc::StringUtils::trim(setting);\n\n        // Get the existing value so we can see if we've changed it.\n        size_t valueBegin = settingEnd+1;\n        std::string value = line.substr(valueBegin);\n        Misc::StringUtils::trim(value);\n\n        // Construct the setting map key to determine whether the setting has already been\n        // written to the file.\n        CategorySetting key = std::make_pair(currentCategory, setting);\n        CategorySettingStatusMap::iterator finder = written.find(key);\n\n        // Settings not in the written map are definitely invalid.  Currently, this can only\n        // happen if the player edited the file while playing, because loadSettingsFile()\n        // will accept anything and pass it along in the map, but in the future, we might\n        // want to handle invalid settings more gracefully here.\n        if (finder == written.end()) {\n            ostream << \"# invalid setting: \" << line << std::endl;\n            changed = true;\n            continue;\n        }\n\n        // Write the current value of the setting to the file.  The key must exist in the\n        // settings map because of how written was initialized and finder != end().\n        ostream << setting << \" = \" << settings.at(key) << std::endl;\n        // Mark that setting as written.\n        finder->second = true;\n        // Did we really change it?\n        if (value != settings.at(key)) {\n            Log(Debug::Verbose) << \"Changed setting: [\" << currentCategory << \"] \"\n                    << setting << \" = \" << settings.at(key);\n            changed = true;\n        }\n        // No need to write the current line, because we just emitted a replacement.\n\n        // Curiously, it appears that comments at the ends of lines with settings are not\n        // allowed, and the comment becomes part of the value.  Was that intended?\n    }\n\n    // We're done with the input stream file.\n    istream.close();\n\n    // Ensure that all options in the current category have been written.  We must complete\n    // the current category at the end of the file before moving on to any new categories.\n    for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) {\n        if (mit->second == false && mit->first.first == currentCategory) {\n            Log(Debug::Verbose) << \"Added new setting: [\" << mit->first.first << \"] \"\n                    << mit->first.second << \" = \" << settings.at(mit->first);\n            ostream << mit->first.second << \" = \" << settings.at(mit->first) << std::endl;\n            mit->second = true;\n            changed = true;\n        }\n    }\n\n    // If there was absolutely nothing in the file (or more likely the file didn't\n    // exist), start the newly created file with a helpful comment.\n    if (!existing) {\n        ostream << \"# This is the OpenMW user 'settings.cfg' file.  This file only contains\" << std::endl;\n        ostream << \"# explicitly changed settings.  If you would like to revert a setting\" << std::endl;\n        ostream << \"# to its default, simply remove it from this file.  For available\" << std::endl;\n        ostream << \"# settings, see the file 'files/settings-default.cfg' in our source repo or the documentation at:\" << std::endl;\n        ostream << \"#\" << std::endl;\n        ostream << \"#   https://openmw.readthedocs.io/en/master/reference/modding/settings/index.html\" << std::endl;\n    }\n\n    // We still have one more thing to do before we're completely done writing the file.\n    // It's possible that there are entirely new categories, or that the input file had\n    // disappeared completely, so we need ensure that all settings are written to the file\n    // regardless of those issues.\n    for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) {\n        // If the setting hasn't been written yet.\n        if (mit->second == false) {\n            // If the catgory has changed, write a new category header.\n            if (mit->first.first != currentCategory) {\n                currentCategory = mit->first.first;\n                Log(Debug::Verbose) << \"Created new setting section: \" << mit->first.first;\n                ostream << std::endl;\n                ostream << \"[\" << currentCategory << \"]\" << std::endl;\n            }\n            Log(Debug::Verbose) << \"Added new setting: [\" << mit->first.first << \"] \"\n                    << mit->first.second << \" = \" << settings.at(mit->first);\n            // Then write the setting.  No need to mark it as written because we're done.\n            ostream << mit->first.second << \" = \" << settings.at(mit->first) << std::endl;\n            changed = true;\n        }\n    }\n\n    // Now install the newly written file in the requested place.\n    if (changed) {\n        Log(Debug::Info) << \"Updating settings file: \" << ipath;\n        boost::filesystem::ofstream ofstream;\n        ofstream.open(ipath);\n        ofstream << ostream.rdbuf();\n        ofstream.close();\n    }\n}\n\nbool Settings::SettingsFileParser::skipWhiteSpace(size_t& i, std::string& str)\n{\n    while (i < str.size() && std::isspace(str[i], std::locale::classic()))\n    {\n        ++i;\n    }\n    return i < str.size();\n}\n\nvoid Settings::SettingsFileParser::fail(const std::string& message)\n{\n    std::stringstream error;\n    error << \"Error on line \" << mLine << \" in \" << mFile << \":\\n\" << message;\n    throw std::runtime_error(error.str());\n}\n"
  },
  {
    "path": "components/settings/parser.hpp",
    "content": "#ifndef COMPONENTS_SETTINGS_PARSER_H\n#define COMPONENTS_SETTINGS_PARSER_H\n\n#include \"categories.hpp\"\n\n#include <string>\n\nnamespace Settings\n{\n    class SettingsFileParser\n    {\n    public:\n        void loadSettingsFile(const std::string& file, CategorySettingValueMap& settings, bool base64encoded = false);\n\n        void saveSettingsFile(const std::string& file, const CategorySettingValueMap& settings);\n\n    private:\n        /// Increment i until it longer points to a whitespace character\n        /// in the string or has reached the end of the string.\n        /// @return false if we have reached the end of the string\n        bool skipWhiteSpace(size_t& i, std::string& str);\n\n        void fail(const std::string& message);\n\n        std::string mFile;\n        int mLine = 0;\n    };\n}\n\n#endif // _COMPONENTS_SETTINGS_PARSER_H\n"
  },
  {
    "path": "components/settings/settings.cpp",
    "content": "#include \"settings.hpp\"\n#include \"parser.hpp\"\n\n#include <sstream>\n\n#include <components/misc/stringops.hpp>\n\nnamespace Settings\n{\n\nCategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap();\nCategorySettingValueMap Manager::mUserSettings = CategorySettingValueMap();\nCategorySettingVector Manager::mChangedSettings = CategorySettingVector();\n\nvoid Manager::clear()\n{\n    mDefaultSettings.clear();\n    mUserSettings.clear();\n    mChangedSettings.clear();\n}\n\n/*\n    Start of tes3mp change (major)\n\n    Add a base64encoded argument to this function to allow unencoded files to still be opened\n*/\nvoid Manager::loadDefault(const std::string &file, bool base64encoded)\n{\n    SettingsFileParser parser;\n    parser.loadSettingsFile(file, mDefaultSettings, base64encoded);\n}\n/*\n    End of tes3mp change (major)\n*/\n\nvoid Manager::loadUser(const std::string &file)\n{\n    SettingsFileParser parser;\n    parser.loadSettingsFile(file, mUserSettings);\n}\n\nvoid Manager::saveUser(const std::string &file)\n{\n    SettingsFileParser parser;\n    parser.saveSettingsFile(file, mUserSettings);\n}\n\nstd::string Manager::getString(const std::string &setting, const std::string &category)\n{\n    CategorySettingValueMap::key_type key = std::make_pair(category, setting);\n    CategorySettingValueMap::iterator it = mUserSettings.find(key);\n    if (it != mUserSettings.end())\n        return it->second;\n\n    it = mDefaultSettings.find(key);\n    if (it != mDefaultSettings.end())\n        return it->second;\n\n    throw std::runtime_error(std::string(\"Trying to retrieve a non-existing setting: \") + setting\n                             + \".\\nMake sure the defaults.bin file was properly installed.\");\n}\n\nfloat Manager::getFloat (const std::string& setting, const std::string& category)\n{\n    const std::string& value = getString(setting, category);\n    std::stringstream stream(value);\n    float number = 0.f;\n    stream >> number;\n    return number;\n}\n\ndouble Manager::getDouble (const std::string& setting, const std::string& category)\n{\n    const std::string& value = getString(setting, category);\n    std::stringstream stream(value);\n    double number = 0.0;\n    stream >> number;\n    return number;\n}\n\nint Manager::getInt (const std::string& setting, const std::string& category)\n{\n    const std::string& value = getString(setting, category);\n    std::stringstream stream(value);\n    int number = 0;\n    stream >> number;\n    return number;\n}\n\nbool Manager::getBool (const std::string& setting, const std::string& category)\n{\n    const std::string& string = getString(setting, category);\n    return Misc::StringUtils::ciEqual(string, \"true\");\n}\n\nosg::Vec2f Manager::getVector2 (const std::string& setting, const std::string& category)\n{\n    const std::string& value = getString(setting, category);\n    std::stringstream stream(value);\n    float x, y;\n    stream >> x >> y;\n    if (stream.fail())\n        throw std::runtime_error(std::string(\"Can't parse 2d vector: \" + value));\n    return osg::Vec2f(x, y);\n}\n\nosg::Vec3f Manager::getVector3 (const std::string& setting, const std::string& category)\n{\n    const std::string& value = getString(setting, category);\n    std::stringstream stream(value);\n    float x, y, z;\n    stream >> x >> y >> z;\n    if (stream.fail())\n        throw std::runtime_error(std::string(\"Can't parse 3d vector: \" + value));\n    return osg::Vec3f(x, y, z);\n}\n\nvoid Manager::setString(const std::string &setting, const std::string &category, const std::string &value)\n{\n    CategorySettingValueMap::key_type key = std::make_pair(category, setting);\n\n    CategorySettingValueMap::iterator found = mUserSettings.find(key);\n    if (found != mUserSettings.end())\n    {\n        if (found->second == value)\n            return;\n    }\n\n    mUserSettings[key] = value;\n\n    mChangedSettings.insert(key);\n}\n\nvoid Manager::setInt (const std::string& setting, const std::string& category, const int value)\n{\n    std::ostringstream stream;\n    stream << value;\n    setString(setting, category, stream.str());\n}\n\nvoid Manager::setFloat (const std::string &setting, const std::string &category, const float value)\n{\n    std::ostringstream stream;\n    stream << value;\n    setString(setting, category, stream.str());\n}\n\nvoid Manager::setDouble (const std::string &setting, const std::string &category, const double value)\n{\n    std::ostringstream stream;\n    stream << value;\n    setString(setting, category, stream.str());\n}\n\nvoid Manager::setBool(const std::string &setting, const std::string &category, const bool value)\n{\n    setString(setting, category, value ? \"true\" : \"false\");\n}\n\nvoid Manager::setVector2 (const std::string &setting, const std::string &category, const osg::Vec2f value)\n{\n    std::ostringstream stream;\n    stream << value.x() << \" \" << value.y();\n    setString(setting, category, stream.str());\n}\n\nvoid Manager::setVector3 (const std::string &setting, const std::string &category, const osg::Vec3f value)\n{\n    std::ostringstream stream;\n    stream << value.x() << ' ' << value.y() << ' ' << value.z();\n    setString(setting, category, stream.str());\n}\n\nvoid Manager::resetPendingChange(const std::string &setting, const std::string &category)\n{\n    CategorySettingValueMap::key_type key = std::make_pair(category, setting);\n    mChangedSettings.erase(key);\n}\n\nconst CategorySettingVector Manager::getPendingChanges()\n{\n    return mChangedSettings;\n}\n\nvoid Manager::resetPendingChanges()\n{\n    mChangedSettings.clear();\n}\n\n}\n"
  },
  {
    "path": "components/settings/settings.hpp",
    "content": "#ifndef COMPONENTS_SETTINGS_H\n#define COMPONENTS_SETTINGS_H\n\n#include \"categories.hpp\"\n\n#include <set>\n#include <map>\n#include <string>\n#include <osg/Vec2f>\n#include <osg/Vec3f>\n\nnamespace Settings\n{\n    ///\n    /// \\brief Settings management (can change during runtime)\n    ///\n    class Manager\n    {\n    public:\n        static CategorySettingValueMap mDefaultSettings;\n        static CategorySettingValueMap mUserSettings;\n\n        static CategorySettingVector mChangedSettings;\n        ///< tracks all the settings that were changed since the last apply() call\n\n        void clear();\n        ///< clears all settings and default settings\n\n        /*\n            Start of tes3mp change (major)\n\n            Add a base64encoded argument to this function to allow unencoded files to still be opened\n        */\n        void loadDefault (const std::string& file, bool base64encoded = true);\n        ///< load file as the default settings (can be overridden by user settings)\n        /*\n            End of tes3mp change (major)\n        */\n\n        void loadUser (const std::string& file);\n        ///< load file as user settings\n\n        void saveUser (const std::string& file);\n        ///< save user settings to file\n\n        static void resetPendingChange(const std::string &setting, const std::string &category);\n\n        static void resetPendingChanges();\n\n        static const CategorySettingVector getPendingChanges();\n        ///< returns the list of changed settings and then clears it\n\n        static int getInt (const std::string& setting, const std::string& category);\n        static float getFloat (const std::string& setting, const std::string& category);\n        static double getDouble (const std::string& setting, const std::string& category);\n        static std::string getString (const std::string& setting, const std::string& category);\n        static bool getBool (const std::string& setting, const std::string& category);\n        static osg::Vec2f getVector2 (const std::string& setting, const std::string& category);\n        static osg::Vec3f getVector3 (const std::string& setting, const std::string& category);\n\n        static void setInt (const std::string& setting, const std::string& category, const int value);\n        static void setFloat (const std::string& setting, const std::string& category, const float value);\n        static void setDouble (const std::string& setting, const std::string& category, const double value);\n        static void setString (const std::string& setting, const std::string& category, const std::string& value);\n        static void setBool (const std::string& setting, const std::string& category, const bool value);\n        static void setVector2 (const std::string& setting, const std::string& category, const osg::Vec2f value);\n        static void setVector3 (const std::string& setting, const std::string& category, const osg::Vec3f value);\n    };\n\n}\n\n#endif // _COMPONENTS_SETTINGS_H\n"
  },
  {
    "path": "components/shader/removedalphafunc.cpp",
    "content": "#include \"removedalphafunc.hpp\"\n\n#include <cassert>\n\n#include <osg/State>\n\nnamespace Shader\n{\n    std::array<osg::ref_ptr<RemovedAlphaFunc>, GL_ALWAYS - GL_NEVER + 1> RemovedAlphaFunc::sInstances{\n        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr\n    };\n\n    osg::ref_ptr<RemovedAlphaFunc> RemovedAlphaFunc::getInstance(GLenum func)\n    {\n        assert(func >= GL_NEVER && func <= GL_ALWAYS);\n        if (!sInstances[func - GL_NEVER])\n            sInstances[func - GL_NEVER] = new RemovedAlphaFunc(static_cast<osg::AlphaFunc::ComparisonFunction>(func), 1.0);\n        return sInstances[func - GL_NEVER];\n    }\n}\n"
  },
  {
    "path": "components/shader/removedalphafunc.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H\n#define OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H\n\n#include <array>\n\n#include <osg/AlphaFunc>\n\nnamespace Shader\n{\n    // State attribute used when shader visitor replaces the deprecated alpha function with a shader\n    // Prevents redundant glAlphaFunc calls and lets the shadowsbin know the stateset had alpha testing\n    class RemovedAlphaFunc : public osg::AlphaFunc\n    {\n    public:\n        // Get a singleton-like instance with the right func (but a default threshold)\n        static osg::ref_ptr<RemovedAlphaFunc> getInstance(GLenum func);\n\n        RemovedAlphaFunc()\n            : osg::AlphaFunc()\n        {}\n\n        RemovedAlphaFunc(ComparisonFunction func, float ref)\n            : osg::AlphaFunc(func, ref)\n        {}\n\n        RemovedAlphaFunc(const RemovedAlphaFunc& raf, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY)\n            : osg::AlphaFunc(raf, copyop)\n        {}\n\n        META_StateAttribute(Shader, RemovedAlphaFunc, ALPHAFUNC);\n\n        void apply(osg::State& state) const override {}\n\n    protected:\n        virtual ~RemovedAlphaFunc() = default;\n\n        static std::array<osg::ref_ptr<RemovedAlphaFunc>, GL_ALWAYS - GL_NEVER + 1> sInstances;\n    };\n}\n#endif //OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H\n"
  },
  {
    "path": "components/shader/shadermanager.cpp",
    "content": "#include \"shadermanager.hpp\"\n\n#include <fstream>\n#include <algorithm>\n#include <sstream>\n\n#include <osg/Program>\n\n#include <boost/filesystem/path.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <components/sceneutil/lightmanager.hpp>\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n\nnamespace Shader\n{\n\n    ShaderManager::ShaderManager()\n        : mLightingMethod(SceneUtil::LightingMethod::FFP)\n    {\n    }\n\n    void ShaderManager::setShaderPath(const std::string &path)\n    {\n        mPath = path;\n    }\n\n    void ShaderManager::setLightingMethod(SceneUtil::LightingMethod method)\n    {\n        mLightingMethod = method;\n    }\n\n    bool addLineDirectivesAfterConditionalBlocks(std::string& source)\n    {\n        for (size_t position = 0; position < source.length(); )\n        {\n            size_t foundPos = source.find(\"#endif\", position);\n            foundPos = std::min(foundPos, source.find(\"#elif\", position));\n            foundPos = std::min(foundPos, source.find(\"#else\", position));\n\n            if (foundPos == std::string::npos)\n                break;\n\n            foundPos = source.find_first_of(\"\\n\\r\", foundPos);\n            foundPos = source.find_first_not_of(\"\\n\\r\", foundPos);\n\n            if (foundPos == std::string::npos)\n                break;\n\n            size_t lineDirectivePosition = source.rfind(\"#line\", foundPos);\n            int lineNumber;\n            if (lineDirectivePosition != std::string::npos)\n            {\n                size_t lineNumberStart = lineDirectivePosition + std::string(\"#line \").length();\n                size_t lineNumberEnd = source.find_first_not_of(\"0123456789\", lineNumberStart);\n                std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);\n                lineNumber = std::stoi(lineNumberString) - 1;\n            }\n            else\n            {\n                lineDirectivePosition = 0;\n                lineNumber = 1;\n            }\n            lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\\n');\n\n            source.replace(foundPos, 0, \"#line \" + std::to_string(lineNumber) + \"\\n\");\n\n            position = foundPos;\n        }\n\n        return true;\n    }\n\n    // Recursively replaces include statements with the actual source of the included files.\n    // Adjusts #line statements accordingly and detects cyclic includes.\n    // includingFiles is the set of files that include this file directly or indirectly, and is intentionally not a reference to allow automatic cleanup.\n    static bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set<boost::filesystem::path> includingFiles)\n    {\n        // An include is cyclic if it is being included by itself\n        if (includingFiles.insert(shaderPath/fileName).second == false)\n        {\n            Log(Debug::Error) << \"Shader \" << fileName << \" error: Detected cyclic #includes\";\n            return false;\n        }\n\n        Misc::StringUtils::replaceAll(source, \"\\r\\n\", \"\\n\");\n\n        size_t foundPos = 0;\n        while ((foundPos = source.find(\"#include\")) != std::string::npos)\n        {\n            size_t start = source.find('\"', foundPos);\n            if (start == std::string::npos || start == source.size() - 1)\n            {\n                Log(Debug::Error) << \"Shader \" << fileName << \" error: Invalid #include\";\n                return false;\n            }\n            size_t end = source.find('\"', start + 1);\n            if (end == std::string::npos)\n            {\n                Log(Debug::Error) << \"Shader \" << fileName << \" error: Invalid #include\";\n                return false;\n            }\n            std::string includeFilename = source.substr(start + 1, end - (start + 1));\n            boost::filesystem::path includePath = shaderPath / includeFilename;\n\n            // Determine the line number that will be used for the #line directive following the included source\n            size_t lineDirectivePosition = source.rfind(\"#line\", foundPos);\n            int lineNumber;\n            if (lineDirectivePosition != std::string::npos)\n            {\n                size_t lineNumberStart = lineDirectivePosition + std::string(\"#line \").length();\n                size_t lineNumberEnd = source.find_first_not_of(\"0123456789\", lineNumberStart);\n                std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);\n                lineNumber = std::stoi(lineNumberString) - 1;\n            }\n            else\n            {\n                lineDirectivePosition = 0;\n                lineNumber = 0;\n            }\n            lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\\n');\n\n            // Include the file recursively\n            boost::filesystem::ifstream includeFstream;\n            includeFstream.open(includePath);\n            if (includeFstream.fail())\n            {\n                Log(Debug::Error) << \"Shader \" << fileName << \" error: Failed to open include \" << includePath.string();\n                return false;\n            }\n            int includedFileNumber = fileNumber++;\n\n            std::stringstream buffer;\n            buffer << includeFstream.rdbuf();\n            std::string stringRepresentation = buffer.str();\n            if (!addLineDirectivesAfterConditionalBlocks(stringRepresentation)\n                || !parseIncludes(shaderPath, stringRepresentation, includeFilename, fileNumber, includingFiles))\n            {\n                Log(Debug::Error) << \"In file included from \" << fileName << \".\" << lineNumber;\n                return false;\n            }\n\n            std::stringstream toInsert;\n            toInsert << \"#line 0 \" << includedFileNumber << \"\\n\" << stringRepresentation << \"\\n#line \" << lineNumber << \" 0\\n\";\n\n            source.replace(foundPos, (end - foundPos + 1), toInsert.str());\n        }\n        return true;\n    }\n\n    bool parseFors(std::string& source, const std::string& templateName)\n    {\n        const char escapeCharacter = '$';\n        size_t foundPos = 0;\n        while ((foundPos = source.find(escapeCharacter)) != std::string::npos)\n        {\n            size_t endPos = source.find_first_of(\" \\n\\r()[].;,\", foundPos);\n            if (endPos == std::string::npos)\n            {\n                Log(Debug::Error) << \"Shader \" << templateName << \" error: Unexpected EOF\";\n                return false;\n            }\n            std::string command = source.substr(foundPos + 1, endPos - (foundPos + 1));\n            if (command != \"foreach\")\n            {\n                Log(Debug::Error) << \"Shader \" << templateName << \" error: Unknown shader directive: $\" << command;\n                return false;\n            }\n\n            size_t iterNameStart = endPos + 1;\n            size_t iterNameEnd = source.find_first_of(\" \\n\\r()[].;,\", iterNameStart);\n            if (iterNameEnd == std::string::npos)\n            {\n                Log(Debug::Error) << \"Shader \" << templateName << \" error: Unexpected EOF\";\n                return false;\n            }\n            std::string iteratorName = \"$\" + source.substr(iterNameStart, iterNameEnd - iterNameStart);\n\n            size_t listStart = iterNameEnd + 1;\n            size_t listEnd = source.find_first_of(\"\\n\\r\", listStart);\n            if (listEnd == std::string::npos)\n            {\n                Log(Debug::Error) << \"Shader \" << templateName << \" error: Unexpected EOF\";\n                return false;\n            }\n            std::string list = source.substr(listStart, listEnd - listStart);\n            std::vector<std::string> listElements;\n            if (list != \"\")\n                Misc::StringUtils::split (list, listElements, \",\");\n\n            size_t contentStart = source.find_first_not_of(\"\\n\\r\", listEnd);\n            size_t contentEnd = source.find(\"$endforeach\", contentStart);\n            if (contentEnd == std::string::npos)\n            {\n                Log(Debug::Error) << \"Shader \" << templateName << \" error: Unexpected EOF\";\n                return false;\n            }\n            std::string content = source.substr(contentStart, contentEnd - contentStart);\n\n            size_t overallEnd = contentEnd + std::string(\"$endforeach\").length();\n\n            size_t lineDirectivePosition = source.rfind(\"#line\", overallEnd);\n            int lineNumber;\n            if (lineDirectivePosition != std::string::npos)\n            {\n                size_t lineNumberStart = lineDirectivePosition + std::string(\"#line \").length();\n                size_t lineNumberEnd = source.find_first_not_of(\"0123456789\", lineNumberStart);\n                std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);\n                lineNumber = std::stoi(lineNumberString);\n            }\n            else\n            {\n                lineDirectivePosition = 0;\n                lineNumber = 2;\n            }\n            lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + overallEnd, '\\n');\n\n            std::string replacement = \"\";\n            for (std::vector<std::string>::const_iterator element = listElements.cbegin(); element != listElements.cend(); element++)\n            {\n                std::string contentInstance = content;\n                size_t foundIterator;\n                while ((foundIterator = contentInstance.find(iteratorName)) != std::string::npos)\n                    contentInstance.replace(foundIterator, iteratorName.length(), *element);\n                replacement += contentInstance;\n            }\n            replacement += \"\\n#line \" + std::to_string(lineNumber);\n            source.replace(foundPos, overallEnd - foundPos, replacement);\n        }\n\n        return true;\n    }\n\n    bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines,\n        const ShaderManager::DefineMap& globalDefines, const std::string& templateName)\n    {\n        const char escapeCharacter = '@';\n        size_t foundPos = 0;\n        std::vector<std::string> forIterators;\n        while ((foundPos = source.find(escapeCharacter)) != std::string::npos)\n        {\n            size_t endPos = source.find_first_of(\" \\n\\r()[].;,\", foundPos);\n            if (endPos == std::string::npos)\n            {\n                Log(Debug::Error) << \"Shader \" << templateName << \" error: Unexpected EOF\";\n                return false;\n            }\n            std::string define = source.substr(foundPos+1, endPos - (foundPos+1));\n            ShaderManager::DefineMap::const_iterator defineFound = defines.find(define);\n            ShaderManager::DefineMap::const_iterator globalDefineFound = globalDefines.find(define);\n            if (define == \"foreach\")\n            {\n                source.replace(foundPos, 1, \"$\");\n                size_t iterNameStart = endPos + 1;\n                size_t iterNameEnd = source.find_first_of(\" \\n\\r()[].;,\", iterNameStart);\n                if (iterNameEnd == std::string::npos)\n                {\n                    Log(Debug::Error) << \"Shader \" << templateName << \" error: Unexpected EOF\";\n                    return false;\n                }\n                forIterators.push_back(source.substr(iterNameStart, iterNameEnd - iterNameStart));\n            }\n            else if (define == \"endforeach\")\n            {\n                source.replace(foundPos, 1, \"$\");\n                if (forIterators.empty())\n                {\n                    Log(Debug::Error) << \"Shader \" << templateName << \" error: endforeach without foreach\";\n                    return false;\n                }\n                else\n                    forIterators.pop_back();\n            }\n            else if (std::find(forIterators.begin(), forIterators.end(), define) != forIterators.end())\n            {\n                source.replace(foundPos, 1, \"$\");\n            }\n            else if (defineFound != defines.end())\n            {\n                source.replace(foundPos, endPos - foundPos, defineFound->second);\n            }\n            else if (globalDefineFound != globalDefines.end())\n            {\n                source.replace(foundPos, endPos - foundPos, globalDefineFound->second);\n            }\n            else\n            {\n                Log(Debug::Error) << \"Shader \" << templateName << \" error: Undefined \" << define;\n                return false;\n            }\n        }\n        return true;\n    }\n\n    osg::ref_ptr<osg::Shader> ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType)\n    {\n        std::lock_guard<std::mutex> lock(mMutex);\n\n        // read the template if we haven't already\n        TemplateMap::iterator templateIt = mShaderTemplates.find(templateName);\n        if (templateIt == mShaderTemplates.end())\n        {\n            boost::filesystem::path path = (boost::filesystem::path(mPath) / templateName);\n            boost::filesystem::ifstream stream;\n            stream.open(path);\n            if (stream.fail())\n            {\n                Log(Debug::Error) << \"Failed to open \" << path.string();\n                return nullptr;\n            }\n            std::stringstream buffer;\n            buffer << stream.rdbuf();\n\n            // parse includes\n            int fileNumber = 1;\n            std::string source = buffer.str();\n            if (!addLineDirectivesAfterConditionalBlocks(source)\n                || !parseIncludes(boost::filesystem::path(mPath), source, templateName, fileNumber, {}))\n                return nullptr;\n\n            templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first;\n        }\n\n        ShaderMap::iterator shaderIt = mShaders.find(std::make_pair(templateName, defines));\n        if (shaderIt == mShaders.end())\n        {\n            std::string shaderSource = templateIt->second;\n            if (!parseDefines(shaderSource, defines, mGlobalDefines, templateName) || !parseFors(shaderSource, templateName))\n            {\n                // Add to the cache anyway to avoid logging the same error over and over.\n                mShaders.insert(std::make_pair(std::make_pair(templateName, defines), nullptr));\n                return nullptr;\n            }\n\n            osg::ref_ptr<osg::Shader> shader (new osg::Shader(shaderType));\n            shader->setShaderSource(shaderSource);\n            // Assign a unique name to allow the SharedStateManager to compare shaders efficiently\n            static unsigned int counter = 0;\n            shader->setName(std::to_string(counter++));\n\n            shaderIt = mShaders.insert(std::make_pair(std::make_pair(templateName, defines), shader)).first;\n        }\n        return shaderIt->second;\n    }\n\n    osg::ref_ptr<osg::Program> ShaderManager::getProgram(osg::ref_ptr<osg::Shader> vertexShader, osg::ref_ptr<osg::Shader> fragmentShader)\n    {\n        std::lock_guard<std::mutex> lock(mMutex);\n        ProgramMap::iterator found = mPrograms.find(std::make_pair(vertexShader, fragmentShader));\n        if (found == mPrograms.end())\n        {\n            osg::ref_ptr<osg::Program> program (new osg::Program);\n            program->addShader(vertexShader);\n            program->addShader(fragmentShader);\n            program->addBindAttribLocation(\"aOffset\", 6);\n            program->addBindAttribLocation(\"aRotation\", 7);\n            if (mLightingMethod == SceneUtil::LightingMethod::SingleUBO)\n                program->addBindUniformBlock(\"LightBufferBinding\", static_cast<int>(UBOBinding::LightBuffer));\n            found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first;\n        }\n        return found->second;\n    }\n\n    ShaderManager::DefineMap ShaderManager::getGlobalDefines()\n    {\n        return DefineMap(mGlobalDefines);\n    }\n\n    void ShaderManager::setGlobalDefines(DefineMap & globalDefines)\n    {\n        mGlobalDefines = globalDefines;\n        for (auto shaderMapElement: mShaders)\n        {\n            std::string templateId = shaderMapElement.first.first;\n            ShaderManager::DefineMap defines = shaderMapElement.first.second;\n            osg::ref_ptr<osg::Shader> shader = shaderMapElement.second;\n            if (shader == nullptr)\n                // I'm not sure how to handle a shader that was already broken as there's no way to get a potential replacement to the nodes that need it.\n                continue;\n            std::string shaderSource = mShaderTemplates[templateId];\n            if (!parseDefines(shaderSource, defines, mGlobalDefines, templateId) || !parseFors(shaderSource, templateId))\n                // We just broke the shader and there's no way to force existing objects back to fixed-function mode as we would when creating the shader.\n                // If we put a nullptr in the shader map, we just lose the ability to put a working one in later.\n                continue;\n            shader->setShaderSource(shaderSource);\n        }\n    }\n\n    void ShaderManager::releaseGLObjects(osg::State *state)\n    {\n        std::lock_guard<std::mutex> lock(mMutex);\n        for (auto shader : mShaders)\n        {\n            if (shader.second != nullptr)\n                shader.second->releaseGLObjects(state);\n        }\n        for (auto program : mPrograms)\n            program.second->releaseGLObjects(state);\n    }\n\n}\n"
  },
  {
    "path": "components/shader/shadermanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SHADERMANAGER_H\n#define OPENMW_COMPONENTS_SHADERMANAGER_H\n\n#include <string>\n#include <map>\n#include <mutex>\n\n#include <osg/ref_ptr>\n\n#include <osg/Shader>\n\n#include <osgViewer/Viewer>\n\n#include <components/sceneutil/lightmanager.hpp>\n\nnamespace Resource\n{\n    class SceneManager;\n}\n\nnamespace SceneUtil\n{\n    enum class LightingMethod;\n}\n\nnamespace Shader\n{\n\n    enum class UBOBinding\n    {\n        LightBuffer\n    };\n\n    /// @brief Reads shader template files and turns them into a concrete shader, based on a list of define's.\n    /// @par Shader templates can get the value of a define with the syntax @define.\n    class ShaderManager\n    {\n    public:\n\n        ShaderManager();\n\n        void setShaderPath(const std::string& path);\n\n        void setLightingMethod(SceneUtil::LightingMethod method);\n\n        typedef std::map<std::string, std::string> DefineMap;\n\n        /// Create or retrieve a shader instance.\n        /// @param shaderTemplate The filename of the shader template.\n        /// @param defines Define values that can be retrieved by the shader template.\n        /// @param shaderType The type of shader (usually vertex or fragment shader).\n        /// @note May return nullptr on failure.\n        /// @note Thread safe.\n        osg::ref_ptr<osg::Shader> getShader(const std::string& templateName, const DefineMap& defines, osg::Shader::Type shaderType);\n\n        osg::ref_ptr<osg::Program> getProgram(osg::ref_ptr<osg::Shader> vertexShader, osg::ref_ptr<osg::Shader> fragmentShader);\n\n        /// Get (a copy of) the DefineMap used to construct all shaders\n        DefineMap getGlobalDefines();\n\n        /// Set the DefineMap used to construct all shaders\n        /// @param defines The DefineMap to use\n        /// @note This will change the source code for any shaders already created, potentially causing problems if they're being used to render a frame. It is recommended that any associated Viewers have their threading stopped while this function is running if any shaders are in use.\n        void setGlobalDefines(DefineMap & globalDefines);\n\n        void releaseGLObjects(osg::State* state);\n\n    private:\n        std::string mPath;\n\n        DefineMap mGlobalDefines;\n\n        // <name, code>\n        typedef std::map<std::string, std::string> TemplateMap;\n        TemplateMap mShaderTemplates;\n\n        typedef std::pair<std::string, DefineMap> MapKey;\n        typedef std::map<MapKey, osg::ref_ptr<osg::Shader> > ShaderMap;\n        ShaderMap mShaders;\n\n        typedef std::map<std::pair<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> >, osg::ref_ptr<osg::Program> > ProgramMap;\n        ProgramMap mPrograms;\n\n        SceneUtil::LightingMethod mLightingMethod;\n\n        std::mutex mMutex;\n    };\n\n    bool parseFors(std::string& source, const std::string& templateName);\n\n    bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines,\n        const ShaderManager::DefineMap& globalDefines, const std::string& templateName);\n}\n\n#endif\n"
  },
  {
    "path": "components/shader/shadervisitor.cpp",
    "content": "#include \"shadervisitor.hpp\"\n\n#include <osg/AlphaFunc>\n#include <osg/Geometry>\n#include <osg/GLExtensions>\n#include <osg/Material>\n#include <osg/Multisample>\n#include <osg/Texture>\n#include <osg/ValueObject>\n\n#include <osgUtil/TangentSpaceGenerator>\n\n#include <components/debug/debuglog.hpp>\n#include <components/misc/stringops.hpp>\n#include <components/resource/imagemanager.hpp>\n#include <components/vfs/manager.hpp>\n#include <components/sceneutil/riggeometry.hpp>\n#include <components/sceneutil/morphgeometry.hpp>\n\n#include \"removedalphafunc.hpp\"\n#include \"shadermanager.hpp\"\n\nnamespace Shader\n{\n    class AddedState : public osg::Object\n    {\n    public:\n        AddedState() = default;\n        AddedState(const AddedState& rhs, const osg::CopyOp& copyOp)\n            : osg::Object(rhs, copyOp)\n            , mUniforms(rhs.mUniforms)\n            , mModes(rhs.mModes)\n            , mAttributes(rhs.mAttributes)\n        {\n        }\n\n        void addUniform(const std::string& name) { mUniforms.emplace(name); }\n        void setMode(osg::StateAttribute::GLMode mode) { mModes.emplace(mode); }\n        void setAttribute(osg::StateAttribute::TypeMemberPair typeMemberPair) { mAttributes.emplace(typeMemberPair); }\n\n        void setAttribute(const osg::StateAttribute* attribute)\n        {\n            mAttributes.emplace(attribute->getTypeMemberPair());\n        }\n        template<typename T>\n        void setAttribute(osg::ref_ptr<T> attribute) { setAttribute(attribute.get()); }\n\n        void setAttributeAndModes(const osg::StateAttribute* attribute)\n        {\n            setAttribute(attribute);\n            InterrogateModesHelper helper(this);\n            attribute->getModeUsage(helper);\n        }\n        template<typename T>\n        void setAttributeAndModes(osg::ref_ptr<T> attribute) { setAttributeAndModes(attribute.get()); }\n\n        bool hasUniform(const std::string& name) { return mUniforms.count(name); }\n        bool hasMode(osg::StateAttribute::GLMode mode) { return mModes.count(mode); }\n        bool hasAttribute(osg::StateAttribute::TypeMemberPair typeMemberPair) { return mAttributes.count(typeMemberPair); }\n        bool hasAttribute(osg::StateAttribute::Type type, unsigned int member) { return hasAttribute(osg::StateAttribute::TypeMemberPair(type, member)); }\n\n        const std::set<osg::StateAttribute::TypeMemberPair>& getAttributes() { return mAttributes; }\n\n        bool empty()\n        {\n            return mUniforms.empty() && mModes.empty() && mAttributes.empty();\n        }\n\n        META_Object(Shader, AddedState)\n\n    private:\n        class InterrogateModesHelper : public osg::StateAttribute::ModeUsage\n        {\n        public:\n            InterrogateModesHelper(AddedState* tracker) : mTracker(tracker) {}\n            void usesMode(osg::StateAttribute::GLMode mode) override { mTracker->setMode(mode); }\n            void usesTextureMode(osg::StateAttribute::GLMode mode) override {}\n\n        private:\n            AddedState* mTracker;\n        };\n\n        std::unordered_set<std::string> mUniforms;\n        std::unordered_set<osg::StateAttribute::GLMode> mModes;\n        std::set<osg::StateAttribute::TypeMemberPair> mAttributes;\n    };\n\n    ShaderVisitor::ShaderRequirements::ShaderRequirements()\n        : mShaderRequired(false)\n        , mColorMode(0)\n        , mMaterialOverridden(false)\n        , mAlphaTestOverridden(false)\n        , mAlphaBlendOverridden(false)\n        , mAlphaFunc(GL_ALWAYS)\n        , mAlphaRef(1.0)\n        , mAlphaBlend(false)\n        , mNormalHeight(false)\n        , mTexStageRequiringTangents(-1)\n        , mNode(nullptr)\n    {\n    }\n\n    ShaderVisitor::ShaderRequirements::~ShaderRequirements()\n    {\n\n    }\n\n    ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string &defaultShaderPrefix)\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        , mForceShaders(false)\n        , mAllowedToModifyStateSets(true)\n        , mAutoUseNormalMaps(false)\n        , mAutoUseSpecularMaps(false)\n        , mApplyLightingToEnvMaps(false)\n        , mConvertAlphaTestToAlphaToCoverage(false)\n        , mTranslucentFramebuffer(false)\n        , mShaderManager(shaderManager)\n        , mImageManager(imageManager)\n        , mDefaultShaderPrefix(defaultShaderPrefix)\n    {\n        mRequirements.emplace_back();\n    }\n\n    void ShaderVisitor::setForceShaders(bool force)\n    {\n        mForceShaders = force;\n    }\n\n    void ShaderVisitor::apply(osg::Node& node)\n    {\n        if (node.getStateSet())\n        {\n            pushRequirements(node);\n            applyStateSet(node.getStateSet(), node);\n            traverse(node);\n            popRequirements();\n        }\n        else\n            traverse(node);\n    }\n\n    osg::StateSet* getWritableStateSet(osg::Node& node)\n    {\n        if (!node.getStateSet())\n            return node.getOrCreateStateSet();\n\n        osg::ref_ptr<osg::StateSet> newStateSet = new osg::StateSet(*node.getStateSet(), osg::CopyOp::SHALLOW_COPY);\n        node.setStateSet(newStateSet);\n        return newStateSet.get();\n    }\n\n    osg::UserDataContainer* getWritableUserDataContainer(osg::Object& object)\n    {\n        if (!object.getUserDataContainer())\n            return object.getOrCreateUserDataContainer();\n\n        osg::ref_ptr<osg::UserDataContainer> newUserData = static_cast<osg::UserDataContainer *>(object.getUserDataContainer()->clone(osg::CopyOp::SHALLOW_COPY));\n        object.setUserDataContainer(newUserData);\n        return newUserData.get();\n    }\n\n    osg::StateSet* getRemovedState(osg::StateSet& stateSet)\n    {\n        if (!stateSet.getUserDataContainer())\n            return nullptr;\n\n        return static_cast<osg::StateSet *>(stateSet.getUserDataContainer()->getUserObject(\"removedState\"));\n    }\n\n    void updateRemovedState(osg::UserDataContainer& userData, osg::StateSet* removedState)\n    {\n        unsigned int index = userData.getUserObjectIndex(\"removedState\");\n        if (index < userData.getNumUserObjects())\n            userData.setUserObject(index, removedState);\n        else\n            userData.addUserObject(removedState);\n        removedState->setName(\"removedState\");\n    }\n\n    AddedState* getAddedState(osg::StateSet& stateSet)\n    {\n        if (!stateSet.getUserDataContainer())\n            return nullptr;\n\n        return static_cast<AddedState*>(stateSet.getUserDataContainer()->getUserObject(\"addedState\"));\n    }\n\n    void updateAddedState(osg::UserDataContainer& userData, AddedState* addedState)\n    {\n        unsigned int index = userData.getUserObjectIndex(\"addedState\");\n        if (index < userData.getNumUserObjects())\n            userData.setUserObject(index, addedState);\n        else\n            userData.addUserObject(addedState);\n        addedState->setName(\"addedState\");\n    }\n\n    const char* defaultTextures[] = { \"diffuseMap\", \"normalMap\", \"emissiveMap\", \"darkMap\", \"detailMap\", \"envMap\", \"specularMap\", \"decalMap\", \"bumpMap\" };\n    bool isTextureNameRecognized(const std::string& name)\n    {\n        for (unsigned int i=0; i<sizeof(defaultTextures)/sizeof(defaultTextures[0]); ++i)\n            if (name == defaultTextures[i])\n                return true;\n        return false;\n    }\n\n    void ShaderVisitor::applyStateSet(osg::ref_ptr<osg::StateSet> stateset, osg::Node& node)\n    {\n        osg::StateSet* writableStateSet = nullptr;\n        if (mAllowedToModifyStateSets)\n            writableStateSet = node.getStateSet();\n        const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList();\n        bool shaderRequired = false;\n        if (node.getUserValue(\"shaderRequired\", shaderRequired) && shaderRequired)\n            mRequirements.back().mShaderRequired = true;\n\n        if (!texAttributes.empty())\n        {\n            const osg::Texture* diffuseMap = nullptr;\n            const osg::Texture* normalMap = nullptr;\n            const osg::Texture* specularMap = nullptr;\n            const osg::Texture* bumpMap = nullptr;\n            for(unsigned int unit=0;unit<texAttributes.size();++unit)\n            {\n                const osg::StateAttribute *attr = stateset->getTextureAttribute(unit, osg::StateAttribute::TEXTURE);\n                if (attr)\n                {\n                    const osg::Texture* texture = attr->asTexture();\n                    if (texture)\n                    {\n                        std::string texName = texture->getName();\n                        if ((texName.empty() || !isTextureNameRecognized(texName)) && unit == 0)\n                            texName = \"diffuseMap\";\n\n                        if (texName == \"normalHeightMap\")\n                        {\n                            mRequirements.back().mNormalHeight = true;\n                            texName = \"normalMap\";\n                        }\n\n                        if (!texName.empty())\n                        {\n                            mRequirements.back().mTextures[unit] = texName;\n                            if (texName == \"normalMap\")\n                            {\n                                mRequirements.back().mTexStageRequiringTangents = unit;\n                                mRequirements.back().mShaderRequired = true;\n                                if (!writableStateSet)\n                                    writableStateSet = getWritableStateSet(node);\n                                // normal maps are by default off since the FFP can't render them, now that we'll use shaders switch to On\n                                writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON);\n                                normalMap = texture;\n                            }\n                            else if (texName == \"diffuseMap\")\n                                diffuseMap = texture;\n                            else if (texName == \"specularMap\")\n                                specularMap = texture;\n                            else if (texName == \"bumpMap\")\n                            {\n                                bumpMap = texture;\n                                mRequirements.back().mShaderRequired = true;\n                                if (!writableStateSet)\n                                    writableStateSet = getWritableStateSet(node);\n                                // Bump maps are off by default as well\n                                writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON);\n                            }\n                            else if (texName == \"envMap\" && mApplyLightingToEnvMaps)\n                            {\n                                mRequirements.back().mShaderRequired = true;\n                            }\n                        }\n                        else if (!mTranslucentFramebuffer)\n                            Log(Debug::Error) << \"ShaderVisitor encountered unknown texture \" << texture;\n                    }\n                }\n            }\n\n            if (mAutoUseNormalMaps && diffuseMap != nullptr && normalMap == nullptr && diffuseMap->getImage(0))\n            {\n                std::string normalMapFileName = diffuseMap->getImage(0)->getFileName();\n\n                osg::ref_ptr<osg::Image> image;\n                bool normalHeight = false;\n                std::string normalHeightMap = normalMapFileName;\n                Misc::StringUtils::replaceLast(normalHeightMap, \".\", mNormalHeightMapPattern + \".\");\n                if (mImageManager.getVFS()->exists(normalHeightMap))\n                {\n                    image = mImageManager.getImage(normalHeightMap);\n                    normalHeight = true;\n                }\n                else\n                {\n                    Misc::StringUtils::replaceLast(normalMapFileName, \".\", mNormalMapPattern + \".\");\n                    if (mImageManager.getVFS()->exists(normalMapFileName))\n                    {\n                        image = mImageManager.getImage(normalMapFileName);\n                    }\n                }\n                // Avoid using the auto-detected normal map if it's already being used as a bump map.\n                // It's probably not an actual normal map.\n                bool hasNamesakeBumpMap = image && bumpMap && bumpMap->getImage(0) && image->getFileName() == bumpMap->getImage(0)->getFileName();\n\n                if (!hasNamesakeBumpMap && image)\n                {\n                    osg::ref_ptr<osg::Texture2D> normalMapTex (new osg::Texture2D(image));\n                    normalMapTex->setTextureSize(image->s(), image->t());\n                    normalMapTex->setWrap(osg::Texture::WRAP_S, diffuseMap->getWrap(osg::Texture::WRAP_S));\n                    normalMapTex->setWrap(osg::Texture::WRAP_T, diffuseMap->getWrap(osg::Texture::WRAP_T));\n                    normalMapTex->setFilter(osg::Texture::MIN_FILTER, diffuseMap->getFilter(osg::Texture::MIN_FILTER));\n                    normalMapTex->setFilter(osg::Texture::MAG_FILTER, diffuseMap->getFilter(osg::Texture::MAG_FILTER));\n                    normalMapTex->setMaxAnisotropy(diffuseMap->getMaxAnisotropy());\n                    normalMapTex->setName(\"normalMap\");\n\n                    int unit = texAttributes.size();\n                    if (!writableStateSet)\n                        writableStateSet = getWritableStateSet(node);\n                    writableStateSet->setTextureAttributeAndModes(unit, normalMapTex, osg::StateAttribute::ON);\n                    mRequirements.back().mTextures[unit] = \"normalMap\";\n                    mRequirements.back().mTexStageRequiringTangents = unit;\n                    mRequirements.back().mShaderRequired = true;\n                    mRequirements.back().mNormalHeight = normalHeight;\n                }\n            }\n            if (mAutoUseSpecularMaps && diffuseMap != nullptr && specularMap == nullptr && diffuseMap->getImage(0))\n            {\n                std::string specularMapFileName = diffuseMap->getImage(0)->getFileName();\n                Misc::StringUtils::replaceLast(specularMapFileName, \".\", mSpecularMapPattern + \".\");\n                if (mImageManager.getVFS()->exists(specularMapFileName))\n                {\n                    osg::ref_ptr<osg::Image> image (mImageManager.getImage(specularMapFileName));\n                    osg::ref_ptr<osg::Texture2D> specularMapTex (new osg::Texture2D(image));\n                    specularMapTex->setTextureSize(image->s(), image->t());\n                    specularMapTex->setWrap(osg::Texture::WRAP_S, diffuseMap->getWrap(osg::Texture::WRAP_S));\n                    specularMapTex->setWrap(osg::Texture::WRAP_T, diffuseMap->getWrap(osg::Texture::WRAP_T));\n                    specularMapTex->setFilter(osg::Texture::MIN_FILTER, diffuseMap->getFilter(osg::Texture::MIN_FILTER));\n                    specularMapTex->setFilter(osg::Texture::MAG_FILTER, diffuseMap->getFilter(osg::Texture::MAG_FILTER));\n                    specularMapTex->setMaxAnisotropy(diffuseMap->getMaxAnisotropy());\n                    specularMapTex->setName(\"specularMap\");\n\n                    int unit = texAttributes.size();\n                    if (!writableStateSet)\n                        writableStateSet = getWritableStateSet(node);\n                    writableStateSet->setTextureAttributeAndModes(unit, specularMapTex, osg::StateAttribute::ON);\n                    mRequirements.back().mTextures[unit] = \"specularMap\";\n                    mRequirements.back().mShaderRequired = true;\n                }\n            }\n\n            if (diffuseMap)\n            {\n                if (!writableStateSet)\n                    writableStateSet = getWritableStateSet(node);\n                // We probably shouldn't construct a new version of this each time as Uniforms use pointer comparison for early-out.\n                // Also it should probably belong to the shader manager or be applied by the shadows bin\n                writableStateSet->addUniform(new osg::Uniform(\"useDiffuseMapForShadowAlpha\", true));\n            }\n        }\n\n        const osg::StateSet::AttributeList& attributes = stateset->getAttributeList();\n        osg::StateSet::AttributeList removedAttributes;\n        if (osg::ref_ptr<osg::StateSet> removedState = getRemovedState(*stateset))\n            removedAttributes = removedState->getAttributeList();\n        osg::ref_ptr<AddedState> addedState = getAddedState(*stateset);\n\n        for (const auto* attributeMap : std::initializer_list<const osg::StateSet::AttributeList*>{ &attributes, &removedAttributes })\n        {\n            for (osg::StateSet::AttributeList::const_iterator it = attributeMap->begin(); it != attributeMap->end(); ++it)\n            {\n                if (addedState && attributeMap != &removedAttributes && addedState->hasAttribute(it->first))\n                    continue;\n                if (it->first.first == osg::StateAttribute::MATERIAL)\n                {\n                    // This should probably be moved out of ShaderRequirements and be applied directly now it's a uniform instead of a define\n                    if (!mRequirements.back().mMaterialOverridden || it->second.second & osg::StateAttribute::PROTECTED)\n                    {\n                        if (it->second.second & osg::StateAttribute::OVERRIDE)\n                            mRequirements.back().mMaterialOverridden = true;\n\n                        const osg::Material* mat = static_cast<const osg::Material*>(it->second.first.get());\n\n                        int colorMode;\n                        switch (mat->getColorMode())\n                        {\n                        case osg::Material::OFF:\n                            colorMode = 0;\n                            break;\n                        case osg::Material::EMISSION:\n                            colorMode = 1;\n                            break;\n                        default:\n                        case osg::Material::AMBIENT_AND_DIFFUSE:\n                            colorMode = 2;\n                            break;\n                        case osg::Material::AMBIENT:\n                            colorMode = 3;\n                            break;\n                        case osg::Material::DIFFUSE:\n                            colorMode = 4;\n                            break;\n                        case osg::Material::SPECULAR:\n                            colorMode = 5;\n                            break;\n                        }\n\n                        mRequirements.back().mColorMode = colorMode;\n                    }\n                }\n                else if (it->first.first == osg::StateAttribute::ALPHAFUNC)\n                {\n                    if (!mRequirements.back().mAlphaTestOverridden || it->second.second & osg::StateAttribute::PROTECTED)\n                    {\n                        if (it->second.second & osg::StateAttribute::OVERRIDE)\n                            mRequirements.back().mAlphaTestOverridden = true;\n\n                        const osg::AlphaFunc* alpha = static_cast<const osg::AlphaFunc*>(it->second.first.get());\n                        mRequirements.back().mAlphaFunc = alpha->getFunction();\n                        mRequirements.back().mAlphaRef = alpha->getReferenceValue();\n                    }\n                }\n            }\n        }\n\n        unsigned int alphaBlend = stateset->getMode(GL_BLEND);\n        if (alphaBlend != osg::StateAttribute::INHERIT && (!mRequirements.back().mAlphaBlendOverridden || alphaBlend & osg::StateAttribute::PROTECTED))\n        {\n            if (alphaBlend & osg::StateAttribute::OVERRIDE)\n                mRequirements.back().mAlphaBlendOverridden = true;\n\n            mRequirements.back().mAlphaBlend = alphaBlend & osg::StateAttribute::ON;\n        }\n    }\n\n    void ShaderVisitor::pushRequirements(osg::Node& node)\n    {\n        mRequirements.push_back(mRequirements.back());\n        mRequirements.back().mNode = &node;\n    }\n\n    void ShaderVisitor::popRequirements()\n    {\n        mRequirements.pop_back();\n    }\n\n    void ShaderVisitor::createProgram(const ShaderRequirements &reqs)\n    {\n        if (!reqs.mShaderRequired && !mForceShaders)\n        {\n            ensureFFP(*reqs.mNode);\n            return;\n        }\n\n        osg::Node& node = *reqs.mNode;\n        osg::StateSet* writableStateSet = nullptr;\n        if (mAllowedToModifyStateSets)\n            writableStateSet = node.getOrCreateStateSet();\n        else\n            writableStateSet = getWritableStateSet(node);\n        osg::ref_ptr<AddedState> addedState = new AddedState;\n        osg::ref_ptr<AddedState> previousAddedState = getAddedState(*writableStateSet);\n        if (!previousAddedState)\n            previousAddedState = new AddedState;\n\n        ShaderManager::DefineMap defineMap;\n        for (unsigned int i=0; i<sizeof(defaultTextures)/sizeof(defaultTextures[0]); ++i)\n        {\n            defineMap[defaultTextures[i]] = \"0\";\n            defineMap[std::string(defaultTextures[i]) + std::string(\"UV\")] = \"0\";\n        }\n        for (std::map<int, std::string>::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt)\n        {\n            defineMap[texIt->second] = \"1\";\n            defineMap[texIt->second + std::string(\"UV\")] = std::to_string(texIt->first);\n        }\n\n        defineMap[\"parallax\"] = reqs.mNormalHeight ? \"1\" : \"0\";\n\n        writableStateSet->addUniform(new osg::Uniform(\"colorMode\", reqs.mColorMode));\n        addedState->addUniform(\"colorMode\");\n\n        defineMap[\"alphaFunc\"] = std::to_string(reqs.mAlphaFunc);\n\n        // back up removed state in case recreateShaders gets rid of the shader later\n        osg::ref_ptr<osg::StateSet> removedState;\n        if ((removedState = getRemovedState(*writableStateSet)) && !mAllowedToModifyStateSets)\n            removedState = new osg::StateSet(*removedState, osg::CopyOp::SHALLOW_COPY);\n        if (!removedState)\n            removedState = new osg::StateSet();\n\n        defineMap[\"alphaToCoverage\"] = \"0\";\n        defineMap[\"adjustCoverage\"] = \"0\";\n        if (reqs.mAlphaFunc != osg::AlphaFunc::ALWAYS)\n        {\n            writableStateSet->addUniform(new osg::Uniform(\"alphaRef\", reqs.mAlphaRef));\n            addedState->addUniform(\"alphaRef\");\n\n            if (!removedState->getAttributePair(osg::StateAttribute::ALPHAFUNC))\n            {\n                const auto* alphaFunc = writableStateSet->getAttributePair(osg::StateAttribute::ALPHAFUNC);\n                if (alphaFunc && !previousAddedState->hasAttribute(osg::StateAttribute::ALPHAFUNC, 0))\n                    removedState->setAttribute(alphaFunc->first, alphaFunc->second);\n            }\n            // This prevents redundant glAlphaFunc calls while letting the shadows bin still see the test\n            writableStateSet->setAttribute(RemovedAlphaFunc::getInstance(reqs.mAlphaFunc), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);\n            addedState->setAttribute(RemovedAlphaFunc::getInstance(reqs.mAlphaFunc));\n\n            // Blending won't work with A2C as we use the alpha channel for coverage. gl_SampleCoverage from ARB_sample_shading would save the day, but requires GLSL 130\n            if (mConvertAlphaTestToAlphaToCoverage && !reqs.mAlphaBlend)\n            {\n                writableStateSet->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, osg::StateAttribute::ON);\n                addedState->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);\n                defineMap[\"alphaToCoverage\"] = \"1\";\n            }\n\n            // Adjusting coverage isn't safe with blending on as blending requires the alpha to be intact.\n            // Maybe we could also somehow (e.g. userdata) detect when the diffuse map has coverage-preserving mip maps in the future\n            if (!reqs.mAlphaBlend)\n                defineMap[\"adjustCoverage\"] = \"1\";\n\n            // Preventing alpha tested stuff shrinking as lower mip levels are used requires knowing the texture size\n            osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);\n            if (exts && exts->isGpuShader4Supported)\n                defineMap[\"useGPUShader4\"] = \"1\";\n            // We could fall back to a texture size uniform if EXT_gpu_shader4 is missing\n        }\n\n        if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT && !previousAddedState->hasMode(GL_ALPHA_TEST))\n            removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST));\n        // This disables the deprecated fixed-function alpha test\n        writableStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);\n        addedState->setMode(GL_ALPHA_TEST);\n\n        if (!removedState->getModeList().empty() || !removedState->getAttributeList().empty())\n        {\n            // user data is normally shallow copied so shared with the original stateset\n            osg::ref_ptr<osg::UserDataContainer> writableUserData;\n            if (mAllowedToModifyStateSets)\n                writableUserData = writableStateSet->getOrCreateUserDataContainer();\n            else\n                writableUserData = getWritableUserDataContainer(*writableStateSet);\n\n            updateRemovedState(*writableUserData, removedState);\n        }\n\n        if (!addedState->empty())\n        {\n            // user data is normally shallow copied so shared with the original stateset\n            osg::ref_ptr<osg::UserDataContainer> writableUserData;\n            if (mAllowedToModifyStateSets)\n                writableUserData = writableStateSet->getOrCreateUserDataContainer();\n            else\n                writableUserData = getWritableUserDataContainer(*writableStateSet);\n\n            updateAddedState(*writableUserData, addedState);\n        }\n\n        defineMap[\"translucentFramebuffer\"] = mTranslucentFramebuffer ? \"1\" : \"0\";\n\n        std::string shaderPrefix;\n        if (!node.getUserValue(\"shaderPrefix\", shaderPrefix))\n            shaderPrefix = mDefaultShaderPrefix;\n\n        osg::ref_ptr<osg::Shader> vertexShader (mShaderManager.getShader(shaderPrefix + \"_vertex.glsl\", defineMap, osg::Shader::VERTEX));\n        osg::ref_ptr<osg::Shader> fragmentShader (mShaderManager.getShader(shaderPrefix + \"_fragment.glsl\", defineMap, osg::Shader::FRAGMENT));\n\n        if (vertexShader && fragmentShader)\n        {\n            auto program = mShaderManager.getProgram(vertexShader, fragmentShader);\n            writableStateSet->setAttributeAndModes(program, osg::StateAttribute::ON);\n            addedState->setAttributeAndModes(program);\n\n            for (std::map<int, std::string>::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt)\n            {\n                writableStateSet->addUniform(new osg::Uniform(texIt->second.c_str(), texIt->first), osg::StateAttribute::ON);\n                addedState->addUniform(texIt->second);\n            }\n        }\n    }\n\n    void ShaderVisitor::ensureFFP(osg::Node& node)\n    {\n        if (!node.getStateSet() || !node.getStateSet()->getAttribute(osg::StateAttribute::PROGRAM))\n            return;\n        osg::StateSet* writableStateSet = nullptr;\n        if (mAllowedToModifyStateSets)\n            writableStateSet = node.getStateSet();\n        else\n            writableStateSet = getWritableStateSet(node);\n\n        // user data is normally shallow copied so shared with the original stateset - we'll need to copy before edits\n        osg::ref_ptr<osg::UserDataContainer> writableUserData;\n\n        if (osg::ref_ptr<AddedState> addedState = getAddedState(*writableStateSet))\n        {\n            if (mAllowedToModifyStateSets)\n                writableUserData = writableStateSet->getUserDataContainer();\n            else\n                writableUserData = getWritableUserDataContainer(*writableStateSet);\n\n            unsigned int index = writableUserData->getUserObjectIndex(\"addedState\");\n            writableUserData->removeUserObject(index);\n\n            // O(n log n) to use StateSet::removeX, but this is O(n)\n            for (auto itr = writableStateSet->getUniformList().begin(); itr != writableStateSet->getUniformList().end();)\n            {\n                if (addedState->hasUniform(itr->first))\n                    writableStateSet->getUniformList().erase(itr++);\n                else\n                    ++itr;\n            }\n\n            for (auto itr = writableStateSet->getModeList().begin(); itr != writableStateSet->getModeList().end();)\n            {\n                if (addedState->hasMode(itr->first))\n                    writableStateSet->getModeList().erase(itr++);\n                else\n                    ++itr;\n            }\n\n            // StateAttributes track the StateSets they're attached to\n            // We don't have access to the function to do that, and can't call removeAttribute with an iterator\n            for (const auto& [type, member] : addedState->getAttributes())\n                writableStateSet->removeAttribute(type, member);\n        }\n\n\n        if (osg::ref_ptr<osg::StateSet> removedState = getRemovedState(*writableStateSet))\n        {\n            if (!writableUserData)\n            {\n            if (mAllowedToModifyStateSets)\n                writableUserData = writableStateSet->getUserDataContainer();\n            else\n                writableUserData = getWritableUserDataContainer(*writableStateSet);\n            }\n            \n            unsigned int index = writableUserData->getUserObjectIndex(\"removedState\");\n            writableUserData->removeUserObject(index);\n\n            writableStateSet->merge(*removedState);\n        }\n    }\n\n    bool ShaderVisitor::adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs)\n    {\n        bool useShader = reqs.mShaderRequired || mForceShaders;\n        bool generateTangents = reqs.mTexStageRequiringTangents != -1;\n        bool changed = false;\n\n        if (mAllowedToModifyStateSets && (useShader || generateTangents))\n        {\n            // make sure that all UV sets are there\n            for (std::map<int, std::string>::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it)\n            {\n                if (sourceGeometry.getTexCoordArray(it->first) == nullptr)\n                {\n                    sourceGeometry.setTexCoordArray(it->first, sourceGeometry.getTexCoordArray(0));\n                    changed = true;\n                }\n            }\n\n            if (generateTangents)\n            {\n                osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator (new osgUtil::TangentSpaceGenerator);\n                generator->generate(&sourceGeometry, reqs.mTexStageRequiringTangents);\n\n                sourceGeometry.setTexCoordArray(7, generator->getTangentArray(), osg::Array::BIND_PER_VERTEX);\n                changed = true;\n            }\n        }\n        return changed;\n    }\n\n    void ShaderVisitor::apply(osg::Geometry& geometry)\n    {\n        bool needPop = (geometry.getStateSet() != nullptr);\n        if (geometry.getStateSet()) // TODO: check if stateset affects shader permutation before pushing it\n        {\n            pushRequirements(geometry);\n            applyStateSet(geometry.getStateSet(), geometry);\n        }\n\n        if (!mRequirements.empty())\n        {\n            const ShaderRequirements& reqs = mRequirements.back();\n\n            adjustGeometry(geometry, reqs);\n\n            createProgram(reqs);\n        }\n        else\n            ensureFFP(geometry);\n\n        if (needPop)\n            popRequirements();\n    }\n\n    void ShaderVisitor::apply(osg::Drawable& drawable)\n    {\n        // non-Geometry drawable (e.g. particle system)\n        bool needPop = (drawable.getStateSet() != nullptr);\n\n        if (drawable.getStateSet())\n        {\n            pushRequirements(drawable);\n            applyStateSet(drawable.getStateSet(), drawable);\n        }\n\n        if (!mRequirements.empty())\n        {\n            const ShaderRequirements& reqs = mRequirements.back();\n            createProgram(reqs);\n\n            if (auto rig = dynamic_cast<SceneUtil::RigGeometry*>(&drawable))\n            {\n                osg::ref_ptr<osg::Geometry> sourceGeometry = rig->getSourceGeometry();\n                if (sourceGeometry && adjustGeometry(*sourceGeometry, reqs))\n                    rig->setSourceGeometry(sourceGeometry);\n            }\n            else if (auto morph = dynamic_cast<SceneUtil::MorphGeometry*>(&drawable))\n            {\n                osg::ref_ptr<osg::Geometry> sourceGeometry = morph->getSourceGeometry();\n                if (sourceGeometry && adjustGeometry(*sourceGeometry, reqs))\n                    morph->setSourceGeometry(sourceGeometry);\n            }\n        }\n        else\n            ensureFFP(drawable);\n\n        if (needPop)\n            popRequirements();\n    }\n\n    void ShaderVisitor::setAllowedToModifyStateSets(bool allowed)\n    {\n        mAllowedToModifyStateSets = allowed;\n    }\n\n    void ShaderVisitor::setAutoUseNormalMaps(bool use)\n    {\n        mAutoUseNormalMaps = use;\n    }\n\n    void ShaderVisitor::setNormalMapPattern(const std::string &pattern)\n    {\n        mNormalMapPattern = pattern;\n    }\n\n    void ShaderVisitor::setNormalHeightMapPattern(const std::string &pattern)\n    {\n        mNormalHeightMapPattern = pattern;\n    }\n\n    void ShaderVisitor::setAutoUseSpecularMaps(bool use)\n    {\n        mAutoUseSpecularMaps = use;\n    }\n\n    void ShaderVisitor::setSpecularMapPattern(const std::string &pattern)\n    {\n        mSpecularMapPattern = pattern;\n    }\n\n    void ShaderVisitor::setApplyLightingToEnvMaps(bool apply)\n    {\n        mApplyLightingToEnvMaps = apply;\n    }\n\n    void ShaderVisitor::setConvertAlphaTestToAlphaToCoverage(bool convert)\n    {\n        mConvertAlphaTestToAlphaToCoverage = convert;\n    }\n\n    void ShaderVisitor::setTranslucentFramebuffer(bool translucent)\n    {\n        mTranslucentFramebuffer = translucent;\n    }\n\n    ReinstateRemovedStateVisitor::ReinstateRemovedStateVisitor(bool allowedToModifyStateSets)\n        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)\n        , mAllowedToModifyStateSets(allowedToModifyStateSets)\n    {\n    }\n\n    void ReinstateRemovedStateVisitor::apply(osg::Node& node)\n    {\n        if (node.getStateSet())\n        {\n            osg::ref_ptr<osg::StateSet> removedState = getRemovedState(*node.getStateSet());\n            if (removedState)\n            {\n                osg::ref_ptr<osg::StateSet> writableStateSet;\n                if (mAllowedToModifyStateSets)\n                    writableStateSet = node.getStateSet();\n                else\n                    writableStateSet = getWritableStateSet(node);\n\n                // user data is normally shallow copied so shared with the original stateset\n                osg::ref_ptr<osg::UserDataContainer> writableUserData;\n                if (mAllowedToModifyStateSets)\n                    writableUserData = writableStateSet->getUserDataContainer();\n                else\n                    writableUserData = getWritableUserDataContainer(*writableStateSet);\n                unsigned int index = writableUserData->getUserObjectIndex(\"removedState\");\n                writableUserData->removeUserObject(index);\n\n                for (const auto&[mode, value] : removedState->getModeList())\n                    writableStateSet->setMode(mode, value);\n\n                for (const auto& attribute : removedState->getAttributeList())\n                    writableStateSet->setAttribute(attribute.second.first, attribute.second.second);\n            }\n        }\n\n        traverse(node);\n    }\n\n}\n"
  },
  {
    "path": "components/shader/shadervisitor.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_SHADERVISITOR_H\n#define OPENMW_COMPONENTS_SHADERVISITOR_H\n\n#include <osg/NodeVisitor>\n\nnamespace Resource\n{\n    class ImageManager;\n}\n\nnamespace Shader\n{\n\n    class ShaderManager;\n\n    /// @brief Adjusts the given subgraph to render using shaders.\n    class ShaderVisitor : public osg::NodeVisitor\n    {\n    public:\n        ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string& defaultShaderPrefix);\n\n        /// By default, only bump mapped objects will have a shader added to them.\n        /// Setting force = true will cause all objects to render using shaders, regardless of having a bump map.\n        void setForceShaders(bool force);\n\n        /// Set if we are allowed to modify StateSets encountered in the graph (default true).\n        /// @par If set to false, then instead of modifying, the StateSet will be cloned and this new StateSet will be assigned to the node.\n        /// @par This option is useful when the ShaderVisitor is run on a \"live\" subgraph that may have already been submitted for rendering.\n        void setAllowedToModifyStateSets(bool allowed);\n\n        /// Automatically use normal maps if a file with suitable name exists (see normal map pattern).\n        void setAutoUseNormalMaps(bool use);\n\n        void setNormalMapPattern(const std::string& pattern);\n        void setNormalHeightMapPattern(const std::string& pattern);\n\n        void setAutoUseSpecularMaps(bool use);\n\n        void setSpecularMapPattern(const std::string& pattern);\n\n        void setApplyLightingToEnvMaps(bool apply);\n\n        void setConvertAlphaTestToAlphaToCoverage(bool convert);\n\n        void setTranslucentFramebuffer(bool translucent);\n\n        void apply(osg::Node& node) override;\n\n        void apply(osg::Drawable& drawable) override;\n        void apply(osg::Geometry& geometry) override;\n\n        void applyStateSet(osg::ref_ptr<osg::StateSet> stateset, osg::Node& node);\n\n        void pushRequirements(osg::Node& node);\n        void popRequirements();\n\n    private:\n        bool mForceShaders;\n        bool mAllowedToModifyStateSets;\n\n        bool mAutoUseNormalMaps;\n        std::string mNormalMapPattern;\n        std::string mNormalHeightMapPattern;\n\n        bool mAutoUseSpecularMaps;\n        std::string mSpecularMapPattern;\n\n        bool mApplyLightingToEnvMaps;\n\n        bool mConvertAlphaTestToAlphaToCoverage;\n\n        bool mTranslucentFramebuffer;\n\n        ShaderManager& mShaderManager;\n        Resource::ImageManager& mImageManager;\n\n        struct ShaderRequirements\n        {\n            ShaderRequirements();\n            ~ShaderRequirements();\n\n            // <texture stage, texture name>\n            std::map<int, std::string> mTextures;\n\n            bool mShaderRequired;\n\n            int mColorMode;\n            \n            bool mMaterialOverridden;\n            bool mAlphaTestOverridden;\n            bool mAlphaBlendOverridden;\n\n            GLenum mAlphaFunc;\n            float mAlphaRef;\n            bool mAlphaBlend;\n\n            bool mNormalHeight; // true if normal map has height info in alpha channel\n\n            // -1 == no tangents required\n            int mTexStageRequiringTangents;\n\n            // the Node that requested these requirements\n            osg::Node* mNode;\n        };\n        std::vector<ShaderRequirements> mRequirements;\n\n        std::string mDefaultShaderPrefix;\n\n        void createProgram(const ShaderRequirements& reqs);\n        void ensureFFP(osg::Node& node);\n        bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);\n    };\n\n    class ReinstateRemovedStateVisitor : public osg::NodeVisitor\n    {\n    public:\n        ReinstateRemovedStateVisitor(bool allowedToModifyStateSets);\n\n        void apply(osg::Node& node) override;\n\n    private:\n        bool mAllowedToModifyStateSets;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/buffercache.cpp",
    "content": "#include \"buffercache.hpp\"\n\n#include <cassert>\n\n#include <osg/PrimitiveSet>\n\n#include \"defs.hpp\"\n\nnamespace\n{\n\ntemplate <typename IndexArrayType>\nosg::ref_ptr<IndexArrayType> createIndexBuffer(unsigned int flags, unsigned int verts)\n{\n    // LOD level n means every 2^n-th vertex is kept\n    size_t lodLevel = (flags >> (4*4));\n\n    size_t lodDeltas[4];\n    for (int i=0; i<4; ++i)\n        lodDeltas[i] = (flags >> (4*i)) & (0xf);\n\n    bool anyDeltas = (lodDeltas[Terrain::North] || lodDeltas[Terrain::South] || lodDeltas[Terrain::West] || lodDeltas[Terrain::East]);\n\n    size_t increment = static_cast<size_t>(1) << lodLevel;\n    assert(increment < verts);\n\n    osg::ref_ptr<IndexArrayType> indices (new IndexArrayType(osg::PrimitiveSet::TRIANGLES));\n    indices->reserve((verts-1)*(verts-1)*2*3 / increment);\n\n    size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1;\n    // If any edge needs stitching we'll skip all edges at this point,\n    // mainly because stitching one edge would have an effect on corners and on the adjacent edges\n    if (anyDeltas)\n    {\n        colStart += increment;\n        colEnd -= increment;\n        rowEnd -= increment;\n        rowStart += increment;\n    }\n    for (size_t row = rowStart; row < rowEnd; row += increment)\n    {\n        for (size_t col = colStart; col < colEnd; col += increment)\n        {\n            // diamond pattern\n            if ((row + col%2) % 2 == 1)\n            {\n                indices->push_back(verts*(col+increment)+row);\n                indices->push_back(verts*(col+increment)+row+increment);\n                indices->push_back(verts*col+row+increment);\n\n                indices->push_back(verts*col+row);\n                indices->push_back(verts*(col+increment)+row);\n                indices->push_back(verts*(col)+row+increment);\n            }\n            else\n            {\n                indices->push_back(verts*col+row);\n                indices->push_back(verts*(col+increment)+row+increment);\n                indices->push_back(verts*col+row+increment);\n\n                indices->push_back(verts*col+row);\n                indices->push_back(verts*(col+increment)+row);\n                indices->push_back(verts*(col+increment)+row+increment);\n            }\n        }\n    }\n\n    size_t innerStep = increment;\n    if (anyDeltas)\n    {\n        // Now configure LOD transitions at the edges - this is pretty tedious,\n        // and some very long and boring code, but it works great\n\n        // South\n        size_t row = 0;\n        size_t outerStep = static_cast<size_t>(1) << (lodDeltas[Terrain::South] + lodLevel);\n        for (size_t col = 0; col < verts-1; col += outerStep)\n        {\n            indices->push_back(verts*col+row);\n            indices->push_back(verts*(col+outerStep)+row);\n            // Make sure not to touch the right edge\n            if (col+outerStep == verts-1)\n                indices->push_back(verts*(col+outerStep-innerStep)+row+innerStep);\n            else\n                indices->push_back(verts*(col+outerStep)+row+innerStep);\n\n            for (size_t i = 0; i < outerStep; i += innerStep)\n            {\n                // Make sure not to touch the left or right edges\n                if (col+i == 0 || col+i == verts-1-innerStep)\n                    continue;\n                indices->push_back(verts*(col)+row);\n                indices->push_back(verts*(col+i+innerStep)+row+innerStep);\n                indices->push_back(verts*(col+i)+row+innerStep);\n            }\n        }\n\n        // North\n        row = verts-1;\n        outerStep = size_t(1) << (lodDeltas[Terrain::North] + lodLevel);\n        for (size_t col = 0; col < verts-1; col += outerStep)\n        {\n            indices->push_back(verts*(col+outerStep)+row);\n            indices->push_back(verts*col+row);\n            // Make sure not to touch the left edge\n            if (col == 0)\n                indices->push_back(verts*(col+innerStep)+row-innerStep);\n            else\n                indices->push_back(verts*col+row-innerStep);\n\n            for (size_t i = 0; i < outerStep; i += innerStep)\n            {\n                // Make sure not to touch the left or right edges\n                if (col+i == 0 || col+i == verts-1-innerStep)\n                    continue;\n                indices->push_back(verts*(col+i)+row-innerStep);\n                indices->push_back(verts*(col+i+innerStep)+row-innerStep);\n                indices->push_back(verts*(col+outerStep)+row);\n            }\n        }\n\n        // West\n        size_t col = 0;\n        outerStep = size_t(1) << (lodDeltas[Terrain::West] + lodLevel);\n        for (row = 0; row < verts-1; row += outerStep)\n        {\n            indices->push_back(verts*col+row+outerStep);\n            indices->push_back(verts*col+row);\n            // Make sure not to touch the top edge\n            if (row+outerStep == verts-1)\n                indices->push_back(verts*(col+innerStep)+row+outerStep-innerStep);\n            else\n                indices->push_back(verts*(col+innerStep)+row+outerStep);\n\n            for (size_t i = 0; i < outerStep; i += innerStep)\n            {\n                // Make sure not to touch the top or bottom edges\n                if (row+i == 0 || row+i == verts-1-innerStep)\n                    continue;\n                indices->push_back(verts*col+row);\n                indices->push_back(verts*(col+innerStep)+row+i);\n                indices->push_back(verts*(col+innerStep)+row+i+innerStep);\n            }\n        }\n\n        // East\n        col = verts-1;\n        outerStep = size_t(1) << (lodDeltas[Terrain::East] + lodLevel);\n        for (row = 0; row < verts-1; row += outerStep)\n        {\n            indices->push_back(verts*col+row);\n            indices->push_back(verts*col+row+outerStep);\n            // Make sure not to touch the bottom edge\n            if (row == 0)\n                indices->push_back(verts*(col-innerStep)+row+innerStep);\n            else\n                indices->push_back(verts*(col-innerStep)+row);\n\n            for (size_t i = 0; i < outerStep; i += innerStep)\n            {\n                // Make sure not to touch the top or bottom edges\n                if (row+i == 0 || row+i == verts-1-innerStep)\n                    continue;\n                indices->push_back(verts*col+row+outerStep);\n                indices->push_back(verts*(col-innerStep)+row+i+innerStep);\n                indices->push_back(verts*(col-innerStep)+row+i);\n            }\n        }\n    }\n\n    return indices;\n}\n\n}\n\nnamespace Terrain\n{\n\n    osg::ref_ptr<osg::Vec2Array> BufferCache::getUVBuffer(unsigned int numVerts)\n    {\n        std::lock_guard<std::mutex> lock(mUvBufferMutex);\n        if (mUvBufferMap.find(numVerts) != mUvBufferMap.end())\n        {\n            return mUvBufferMap[numVerts];\n        }\n\n        int vertexCount = numVerts * numVerts;\n\n        osg::ref_ptr<osg::Vec2Array> uvs (new osg::Vec2Array(osg::Array::BIND_PER_VERTEX));\n        uvs->reserve(vertexCount);\n\n        for (unsigned int col = 0; col < numVerts; ++col)\n        {\n            for (unsigned int row = 0; row < numVerts; ++row)\n            {\n                uvs->push_back(osg::Vec2f(col / static_cast<float>(numVerts-1),\n                                          ((numVerts-1) - row) / static_cast<float>(numVerts-1)));\n            }\n        }\n\n        // Assign a VBO here to enable state sharing between different Geometries.\n        uvs->setVertexBufferObject(new osg::VertexBufferObject);\n\n        mUvBufferMap[numVerts] = uvs;\n        return uvs;\n    }\n\n    osg::ref_ptr<osg::DrawElements> BufferCache::getIndexBuffer(unsigned int numVerts, unsigned int flags)\n    {\n        std::pair<int, int> id = std::make_pair(numVerts, flags);\n        std::lock_guard<std::mutex> lock(mIndexBufferMutex);\n\n        if (mIndexBufferMap.find(id) != mIndexBufferMap.end())\n        {\n            return mIndexBufferMap[id];\n        }\n\n        osg::ref_ptr<osg::DrawElements> buffer;\n\n        if (numVerts*numVerts <= (0xffffu))\n            buffer = createIndexBuffer<osg::DrawElementsUShort>(flags, numVerts);\n        else\n            buffer = createIndexBuffer<osg::DrawElementsUInt>(flags, numVerts);\n\n        // Assign a EBO here to enable state sharing between different Geometries.\n        buffer->setElementBufferObject(new osg::ElementBufferObject);\n\n        mIndexBufferMap[id] = buffer;\n        return buffer;\n    }\n\n    void BufferCache::clearCache()\n    {\n        {\n            std::lock_guard<std::mutex> lock(mIndexBufferMutex);\n            mIndexBufferMap.clear();\n        }\n        {\n            std::lock_guard<std::mutex> lock(mUvBufferMutex);\n            mUvBufferMap.clear();\n        }\n    }\n\n    void BufferCache::releaseGLObjects(osg::State *state)\n    {\n        {\n            std::lock_guard<std::mutex> lock(mIndexBufferMutex);\n            for (auto indexbuffer : mIndexBufferMap)\n                indexbuffer.second->releaseGLObjects(state);\n        }\n        {\n            std::lock_guard<std::mutex> lock(mUvBufferMutex);\n            for (auto uvbuffer : mUvBufferMap)\n                uvbuffer.second->releaseGLObjects(state);\n        }\n    }\n\n}\n"
  },
  {
    "path": "components/terrain/buffercache.hpp",
    "content": "#ifndef COMPONENTS_TERRAIN_BUFFERCACHE_H\n#define COMPONENTS_TERRAIN_BUFFERCACHE_H\n\n#include <osg/ref_ptr>\n#include <osg/Array>\n#include <osg/PrimitiveSet>\n\n#include <map>\n#include <mutex>\n\nnamespace Terrain\n{\n\n    /// @brief Implements creation and caching of vertex buffers for terrain chunks.\n    class BufferCache\n    {\n    public:\n        /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each)\n        ///              next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices)\n        /// @note Thread safe.\n        osg::ref_ptr<osg::DrawElements> getIndexBuffer (unsigned int numVerts, unsigned int flags);\n\n        /// @note Thread safe.\n        osg::ref_ptr<osg::Vec2Array> getUVBuffer(unsigned int numVerts);\n\n        void clearCache();\n\n        void releaseGLObjects(osg::State* state);\n\n    private:\n        // Index buffers are shared across terrain batches where possible. There is one index buffer for each\n        // combination of LOD deltas and index buffer LOD we may need.\n        std::map<std::pair<int, int>, osg::ref_ptr<osg::DrawElements> > mIndexBufferMap;\n        std::mutex mIndexBufferMutex;\n\n        std::map<int, osg::ref_ptr<osg::Vec2Array> > mUvBufferMap;\n        std::mutex mUvBufferMutex;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/cellborder.cpp",
    "content": "#include \"cellborder.hpp\"\n\n#include <osg/Material>\n#include <osg/PolygonMode>\n#include <osg/Geometry>\n#include <osg/Geode>\n\n#include \"world.hpp\"\n#include \"../esm/loadland.hpp\"\n\nnamespace Terrain\n{\n\nCellBorder::CellBorder(Terrain::World *world, osg::Group *root, int borderMask):\n    mWorld(world),\n    mRoot(root),\n    mBorderMask(borderMask)\n{\n}\n\nvoid CellBorder::createCellBorderGeometry(int x, int y)\n{\n    const int cellSize = ESM::Land::REAL_SIZE;\n    const int borderSegments = 40;\n    const float offset = 10.0;\n\n    osg::Vec3 cellCorner = osg::Vec3(x * cellSize,y * cellSize,0);\n\n    osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;\n    osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;\n    osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;\n\n    normals->push_back(osg::Vec3(0.0f,-1.0f, 0.0f));\n\n    float borderStep = cellSize / ((float) borderSegments);\n\n    for (int i = 0; i <= 2 * borderSegments; ++i)\n    {\n        osg::Vec3f pos = i < borderSegments ?\n            osg::Vec3(i * borderStep,0.0f,0.0f) :\n            osg::Vec3(cellSize,(i - borderSegments) * borderStep,0.0f);\n\n        pos += cellCorner;\n        pos += osg::Vec3f(0,0,mWorld->getHeightAt(pos) + offset);\n\n        vertices->push_back(pos);\n\n        osg::Vec4f col = i % 2 == 0 ?\n            osg::Vec4f(0,0,0,1) :\n            osg::Vec4f(1,1,0,1);\n\n        colors->push_back(col);\n    }\n\n    osg::ref_ptr<osg::Geometry> border = new osg::Geometry;\n    border->setVertexArray(vertices.get());\n    border->setNormalArray(normals.get());\n    border->setNormalBinding(osg::Geometry::BIND_OVERALL);\n    border->setColorArray(colors.get());\n    border->setColorBinding(osg::Geometry::BIND_PER_VERTEX);\n\n    border->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP,0,vertices->size()));\n\n    osg::ref_ptr<osg::Geode> borderGeode = new osg::Geode;\n    borderGeode->addDrawable(border.get());\n\n    osg::StateSet *stateSet = borderGeode->getOrCreateStateSet();\n    osg::ref_ptr<osg::Material> material (new osg::Material);\n    material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);\n    stateSet->setAttribute(material);\n\n    osg::PolygonMode* polygonmode = new osg::PolygonMode;\n    polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);\n    stateSet->setAttributeAndModes(polygonmode,osg::StateAttribute::ON);\n\n    borderGeode->setNodeMask(mBorderMask);\n\n    mRoot->addChild(borderGeode);\n\n    mCellBorderNodes[std::make_pair(x,y)] = borderGeode;\n}\n\nvoid CellBorder::destroyCellBorderGeometry(int x, int y)\n{\n    CellGrid::iterator it = mCellBorderNodes.find(std::make_pair(x,y));\n\n    if (it == mCellBorderNodes.end())\n        return;\n\n    osg::ref_ptr<osg::Node> borderNode = it->second;\n    mRoot->removeChild(borderNode);\n\n    mCellBorderNodes.erase(it);\n}\n\nvoid CellBorder::destroyCellBorderGeometry()\n{\n    for (const auto& v : mCellBorderNodes)\n        mRoot->removeChild(v.second);\n    mCellBorderNodes.clear();\n}\n\n}\n"
  },
  {
    "path": "components/terrain/cellborder.hpp",
    "content": "#ifndef GAME_RENDER_CELLBORDER\n#define GAME_RENDER_CELLBORDER\n\n#include <map>\n#include <osg/Group>\n\nnamespace Terrain\n{\n    class World;\n\n    /**\n     * @Brief Handles the debug cell borders.\n     */\n    class CellBorder\n    {\n    public:\n        typedef std::map<std::pair<int, int>, osg::ref_ptr<osg::Node> > CellGrid; \n\n        CellBorder(Terrain::World *world, osg::Group *root, int borderMask);\n\n        void createCellBorderGeometry(int x, int y);\n        void destroyCellBorderGeometry(int x, int y);\n\n        /**\n          Destroys the geometry for all borders.\n        */\n        void destroyCellBorderGeometry();\n\n    protected:\n        Terrain::World *mWorld;\n        osg::Group *mRoot;\n\n        CellGrid mCellBorderNodes;\n        int mBorderMask;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/chunkmanager.cpp",
    "content": "#include \"chunkmanager.hpp\"\n\n#include <sstream>\n\n#include <osg/Texture2D>\n#include <osg/ClusterCullingCallback>\n#include <osg/Material>\n\n#include <osgUtil/IncrementalCompileOperation>\n\n#include <components/resource/objectcache.hpp>\n#include <components/resource/scenemanager.hpp>\n\n#include <components/sceneutil/lightmanager.hpp>\n\n#include \"terraindrawable.hpp\"\n#include \"material.hpp\"\n#include \"storage.hpp\"\n#include \"texturemanager.hpp\"\n#include \"compositemaprenderer.hpp\"\n\nnamespace Terrain\n{\n\nChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer)\n    : GenericResourceManager<ChunkId>(nullptr)\n    , mStorage(storage)\n    , mSceneManager(sceneMgr)\n    , mTextureManager(textureManager)\n    , mCompositeMapRenderer(renderer)\n    , mNodeMask(0)\n    , mCompositeMapSize(512)\n    , mCompositeMapLevel(1.f)\n    , mMaxCompGeometrySize(1.f)\n{\n    mMultiPassRoot = new osg::StateSet;\n    mMultiPassRoot->setRenderingHint(osg::StateSet::OPAQUE_BIN);\n    osg::ref_ptr<osg::Material> material (new osg::Material);\n    material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);\n    mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON);\n}\n\nosg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)\n{\n    ChunkId id = std::make_tuple(center, lod, lodFlags);\n    osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);\n    if (obj)\n        return obj->asNode();\n    else\n    {\n        osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile);\n        mCache->addEntryToObjectCache(id, node.get());\n        return node;\n    }\n}\n\nvoid ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n{\n    stats->setAttribute(frameNumber, \"Terrain Chunk\", mCache->getCacheSize());\n}\n\nvoid ChunkManager::clearCache()\n{\n    GenericResourceManager<ChunkId>::clearCache();\n\n    mBufferCache.clearCache();\n}\n\nvoid ChunkManager::releaseGLObjects(osg::State *state)\n{\n    GenericResourceManager<ChunkId>::releaseGLObjects(state);\n    mBufferCache.releaseGLObjects(state);\n}\n\nosg::ref_ptr<osg::Texture2D> ChunkManager::createCompositeMapRTT()\n{\n    osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;\n    texture->setTextureWidth(mCompositeMapSize);\n    texture->setTextureHeight(mCompositeMapSize);\n    texture->setInternalFormat(GL_RGB);\n    texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);\n    texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);\n    texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n    texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n\n    return texture;\n}\n\nvoid ChunkManager::createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& compositeMap)\n{\n    if (chunkSize > mMaxCompGeometrySize)\n    {\n        createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMap);\n        createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMap);\n        createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y()+texCoords.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMap);\n        createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y()+texCoords.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMap);\n    }\n    else\n    {\n        float left = texCoords.x()*2.f-1;\n        float top = texCoords.y()*2.f-1;\n        float width = texCoords.z()*2.f;\n        float height = texCoords.w()*2.f;\n\n        std::vector<osg::ref_ptr<osg::StateSet> > passes = createPasses(chunkSize, chunkCenter, true);\n        for (std::vector<osg::ref_ptr<osg::StateSet> >::iterator it = passes.begin(); it != passes.end(); ++it)\n        {\n            osg::ref_ptr<osg::Geometry> geom = osg::createTexturedQuadGeometry(osg::Vec3(left,top,0), osg::Vec3(width,0,0), osg::Vec3(0,height,0));\n            geom->setUseDisplayList(false); // don't bother making a display list for an object that is just rendered once.\n            geom->setUseVertexBufferObjects(false);\n            geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);\n\n            geom->setStateSet(*it);\n\n            compositeMap.mDrawables.emplace_back(geom);\n        }\n    }\n}\n\nstd::vector<osg::ref_ptr<osg::StateSet> > ChunkManager::createPasses(float chunkSize, const osg::Vec2f &chunkCenter, bool forCompositeMap)\n{\n    std::vector<LayerInfo> layerList;\n    std::vector<osg::ref_ptr<osg::Image> > blendmaps;\n    mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList);\n\n    bool useShaders = mSceneManager->getForceShaders();\n    if (!mSceneManager->getClampLighting())\n        useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps\n\n    std::vector<TextureLayer> layers;\n    {\n        for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)\n        {\n            TextureLayer textureLayer;\n            textureLayer.mParallax = it->mParallax;\n            textureLayer.mSpecular = it->mSpecular;\n\n            textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap);\n\n            if (!forCompositeMap && !it->mNormalMap.empty())\n                textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap);\n\n            if (it->requiresShaders())\n                useShaders = true;\n\n            layers.push_back(textureLayer);\n        }\n    }\n\n    if (forCompositeMap)\n        useShaders = false;\n\n    std::vector<osg::ref_ptr<osg::Texture2D> > blendmapTextures;\n    for (std::vector<osg::ref_ptr<osg::Image> >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)\n    {\n        osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);\n        texture->setImage(*it);\n        texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);\n        texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);\n        texture->setResizeNonPowerOfTwoHint(false);\n        blendmapTextures.push_back(texture);\n    }\n\n    float blendmapScale = mStorage->getBlendmapScale(chunkSize);\n\n    return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale);\n}\n\nosg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile)\n{\n    osg::ref_ptr<osg::Vec3Array> positions (new osg::Vec3Array);\n    osg::ref_ptr<osg::Vec3Array> normals (new osg::Vec3Array);\n    osg::ref_ptr<osg::Vec4ubArray> colors (new osg::Vec4ubArray);\n    colors->setNormalize(true);\n\n    osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);\n    positions->setVertexBufferObject(vbo);\n    normals->setVertexBufferObject(vbo);\n    colors->setVertexBufferObject(vbo);\n\n    mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors);\n\n    osg::ref_ptr<TerrainDrawable> geometry (new TerrainDrawable);\n    geometry->setVertexArray(positions);\n    geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX);\n    geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);\n    geometry->setUseDisplayList(false);\n    geometry->setUseVertexBufferObjects(true);\n\n    if (chunkSize <= 1.f)\n        geometry->setLightListCallback(new SceneUtil::LightListCallback);\n\n    unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1;\n\n    geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags));\n\n    bool useCompositeMap = chunkSize >= mCompositeMapLevel;\n    unsigned int numUvSets = useCompositeMap ? 1 : 2;\n\n    geometry->setTexCoordArrayList(osg::Geometry::ArrayList(numUvSets, mBufferCache.getUVBuffer(numVerts)));\n\n    geometry->createClusterCullingCallback();\n\n    geometry->setStateSet(mMultiPassRoot);\n\n    if (useCompositeMap)\n    {\n        osg::ref_ptr<CompositeMap> compositeMap = new CompositeMap;\n        compositeMap->mTexture = createCompositeMapRTT();\n\n        createCompositeMapGeometry(chunkSize, chunkCenter, osg::Vec4f(0,0,1,1), *compositeMap);\n\n        mCompositeMapRenderer->addCompositeMap(compositeMap.get(), false);\n\n        geometry->setCompositeMap(compositeMap);\n        geometry->setCompositeMapRenderer(mCompositeMapRenderer);\n\n        TextureLayer layer;\n        layer.mDiffuseMap = compositeMap->mTexture;\n        layer.mParallax = false;\n        layer.mSpecular = false;\n        geometry->setPasses(::Terrain::createPasses(mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), &mSceneManager->getShaderManager(), std::vector<TextureLayer>(1, layer), std::vector<osg::ref_ptr<osg::Texture2D> >(), 1.f, 1.f));\n    }\n    else\n    {\n        geometry->setPasses(createPasses(chunkSize, chunkCenter, false));\n    }\n\n    geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts);\n\n    if (compile && mSceneManager->getIncrementalCompileOperation())\n    {\n        mSceneManager->getIncrementalCompileOperation()->add(geometry);\n    }\n    geometry->setNodeMask(mNodeMask);\n\n    return geometry;\n}\n\n}\n"
  },
  {
    "path": "components/terrain/chunkmanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_TERRAIN_CHUNKMANAGER_H\n#define OPENMW_COMPONENTS_TERRAIN_CHUNKMANAGER_H\n\n#include <tuple>\n\n#include <components/resource/resourcemanager.hpp>\n\n#include \"buffercache.hpp\"\n#include \"quadtreeworld.hpp\"\n\nnamespace osg\n{\n    class Group;\n    class Texture2D;\n}\n\nnamespace Resource\n{\n    class SceneManager;\n}\n\nnamespace Terrain\n{\n\n    class TextureManager;\n    class CompositeMapRenderer;\n    class Storage;\n    class CompositeMap;\n\n    typedef std::tuple<osg::Vec2f, unsigned char, unsigned int> ChunkId; // Center, Lod, Lod Flags\n\n    /// @brief Handles loading and caching of terrain chunks\n    class ChunkManager : public Resource::GenericResourceManager<ChunkId>, public QuadTreeWorld::ChunkManager\n    {\n    public:\n        ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer);\n\n        osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;\n\n        void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; }\n        void setCompositeMapLevel(float level) { mCompositeMapLevel = level; }\n        void setMaxCompositeGeometrySize(float maxCompGeometrySize) { mMaxCompGeometrySize = maxCompGeometrySize; }\n\n        void setNodeMask(unsigned int mask) { mNodeMask = mask; }\n        unsigned int getNodeMask() override { return mNodeMask; }\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;\n\n        void clearCache() override;\n\n        void releaseGLObjects(osg::State* state) override;\n\n    private:\n        osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile);\n\n        osg::ref_ptr<osg::Texture2D> createCompositeMapRTT();\n\n        void createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& map);\n\n        std::vector<osg::ref_ptr<osg::StateSet> > createPasses(float chunkSize, const osg::Vec2f& chunkCenter, bool forCompositeMap);\n\n        Terrain::Storage* mStorage;\n        Resource::SceneManager* mSceneManager;\n        TextureManager* mTextureManager;\n        CompositeMapRenderer* mCompositeMapRenderer;\n        BufferCache mBufferCache;\n\n        osg::ref_ptr<osg::StateSet> mMultiPassRoot;\n\n        unsigned int mNodeMask;\n\n        unsigned int mCompositeMapSize;\n        float mCompositeMapLevel;\n        float mMaxCompGeometrySize;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/compositemaprenderer.cpp",
    "content": "#include \"compositemaprenderer.hpp\"\n\n#include <osg/FrameBufferObject>\n#include <osg/Texture2D>\n#include <osg/RenderInfo>\n\n#include <components/sceneutil/unrefqueue.hpp>\n#include <components/sceneutil/workqueue.hpp>\n\n#include <algorithm>\n\nnamespace Terrain\n{\n\nCompositeMapRenderer::CompositeMapRenderer()\n    : mTargetFrameRate(120)\n    , mMinimumTimeAvailable(0.0025)\n{\n    setSupportsDisplayList(false);\n    setCullingActive(false);\n\n    mFBO = new osg::FrameBufferObject;\n\n    mUnrefQueue = new SceneUtil::UnrefQueue;\n\n    getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);\n}\n\nCompositeMapRenderer::~CompositeMapRenderer()\n{\n}\n\nvoid CompositeMapRenderer::setWorkQueue(SceneUtil::WorkQueue* workQueue)\n{\n    mWorkQueue = workQueue;\n}\n\nvoid CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const\n{\n    double dt = mTimer.time_s();\n    dt = std::min(dt, 0.2);\n    mTimer.setStartTick();\n    double targetFrameTime = 1.0/static_cast<double>(mTargetFrameRate);\n    double conservativeTimeRatio(0.75);\n    double availableTime = std::max((targetFrameTime - dt)*conservativeTimeRatio,\n                                    mMinimumTimeAvailable);\n\n    if (mWorkQueue)\n        mUnrefQueue->flush(mWorkQueue.get());\n\n    std::lock_guard<std::mutex> lock(mMutex);\n\n    if (mImmediateCompileSet.empty() && mCompileSet.empty())\n        return;\n\n    while (!mImmediateCompileSet.empty())\n    {\n        osg::ref_ptr<CompositeMap> node = *mImmediateCompileSet.begin();\n        mImmediateCompileSet.erase(node);\n\n        mMutex.unlock();\n        compile(*node, renderInfo, nullptr);\n        mMutex.lock();\n    }\n\n    double timeLeft = availableTime;\n\n    while (!mCompileSet.empty() && timeLeft > 0)\n    {\n        osg::ref_ptr<CompositeMap> node = *mCompileSet.begin();\n        mCompileSet.erase(node);\n\n        mMutex.unlock();\n        compile(*node, renderInfo, &timeLeft);\n        mMutex.lock();\n\n        if (node->mCompiled < node->mDrawables.size())\n        {\n            // We did not compile the map fully.\n            // Place it back to queue to continue work in the next time.\n            mCompileSet.insert(node);\n        }\n    }\n    mTimer.setStartTick();\n}\n\nvoid CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo &renderInfo, double* timeLeft) const\n{\n    // if there are no more external references we can assume the texture is no longer required\n    if (compositeMap.mTexture->referenceCount() <= 1)\n    {\n        compositeMap.mCompiled = compositeMap.mDrawables.size();\n        return;\n    }\n\n    osg::Timer timer;\n    osg::State& state = *renderInfo.getState();\n    osg::GLExtensions* ext = state.get<osg::GLExtensions>();\n\n    if (!mFBO)\n        return;\n\n    if (!ext->isFrameBufferObjectSupported)\n        return;\n\n    osg::FrameBufferAttachment attach (compositeMap.mTexture);\n    mFBO->setAttachment(osg::Camera::COLOR_BUFFER, attach);\n    mFBO->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);\n\n    GLenum status = ext->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);\n\n    if (status != GL_FRAMEBUFFER_COMPLETE_EXT)\n    {\n        GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0;\n        ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);\n        OSG_ALWAYS << \"Error attaching FBO\" << std::endl;\n        return;\n    }\n\n    // inform State that Texture attribute has changed due to compiling of FBO texture\n    // should OSG be doing this on its own?\n    state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), osg::StateAttribute::TEXTURE);\n\n    for (unsigned int i=compositeMap.mCompiled; i<compositeMap.mDrawables.size(); ++i)\n    {\n        osg::Drawable* drw = compositeMap.mDrawables[i];\n        osg::StateSet* stateset = drw->getStateSet();\n\n        if (stateset)\n            renderInfo.getState()->pushStateSet(stateset);\n\n        renderInfo.getState()->apply();\n\n        glViewport(0,0,compositeMap.mTexture->getTextureWidth(), compositeMap.mTexture->getTextureHeight());\n        drw->drawImplementation(renderInfo);\n\n        if (stateset)\n            renderInfo.getState()->popStateSet();\n\n        ++compositeMap.mCompiled;\n\n        if (mWorkQueue)\n        {\n            mUnrefQueue->push(compositeMap.mDrawables[i]);\n        }\n        compositeMap.mDrawables[i] = nullptr;\n\n        if (timeLeft)\n        {\n            *timeLeft -= timer.time_s();\n            timer.setStartTick();\n\n            if (*timeLeft <= 0)\n                break;\n        }\n    }\n    if (compositeMap.mCompiled == compositeMap.mDrawables.size())\n        compositeMap.mDrawables = std::vector<osg::ref_ptr<osg::Drawable>>();\n\n    state.haveAppliedAttribute(osg::StateAttribute::VIEWPORT);\n\n    GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0;\n    ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);\n}\n\nvoid CompositeMapRenderer::setMinimumTimeAvailableForCompile(double time)\n{\n    mMinimumTimeAvailable = time;\n}\n\nvoid CompositeMapRenderer::setTargetFrameRate(float framerate)\n{\n    mTargetFrameRate = framerate;\n}\n\nvoid CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool immediate)\n{\n    std::lock_guard<std::mutex> lock(mMutex);\n    if (immediate)\n        mImmediateCompileSet.insert(compositeMap);\n    else\n        mCompileSet.insert(compositeMap);\n}\n\nvoid CompositeMapRenderer::setImmediate(CompositeMap* compositeMap)\n{\n    std::lock_guard<std::mutex> lock(mMutex);\n    CompileSet::iterator found = mCompileSet.find(compositeMap);\n    if (found == mCompileSet.end())\n        return;\n    else\n    {\n        mImmediateCompileSet.insert(compositeMap);\n        mCompileSet.erase(found);\n    }\n}\n\nunsigned int CompositeMapRenderer::getCompileSetSize() const\n{\n    std::lock_guard<std::mutex> lock(mMutex);\n    return mCompileSet.size();\n}\n\nCompositeMap::CompositeMap()\n    : mCompiled(0)\n{\n}\n\nCompositeMap::~CompositeMap()\n{\n\n}\n\n\n\n}\n"
  },
  {
    "path": "components/terrain/compositemaprenderer.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_TERRAIN_COMPOSITEMAPRENDERER_H\n#define OPENMW_COMPONENTS_TERRAIN_COMPOSITEMAPRENDERER_H\n\n#include <osg/Drawable>\n\n#include <set>\n#include <mutex>\n\nnamespace osg\n{\n    class FrameBufferObject;\n    class RenderInfo;\n    class Texture2D;\n}\n\nnamespace SceneUtil\n{\n    class UnrefQueue;\n    class WorkQueue;\n}\n\nnamespace Terrain\n{\n\n    class CompositeMap : public osg::Referenced\n    {\n    public:\n        CompositeMap();\n        ~CompositeMap();\n        std::vector<osg::ref_ptr<osg::Drawable> > mDrawables;\n        osg::ref_ptr<osg::Texture2D> mTexture;\n        unsigned int mCompiled;\n    };\n\n    /**\n     * @brief The CompositeMapRenderer is responsible for updating composite map textures in a blocking or non-blocking way.\n     */\n    class CompositeMapRenderer : public osg::Drawable\n    {\n    public:\n        CompositeMapRenderer();\n        ~CompositeMapRenderer();\n\n        void drawImplementation(osg::RenderInfo& renderInfo) const override;\n\n        void compile(CompositeMap& compositeMap, osg::RenderInfo& renderInfo, double* timeLeft) const;\n\n        /// Set a WorkQueue to delete compiled composite map layers in the background thread\n        void setWorkQueue(SceneUtil::WorkQueue* workQueue);\n\n        /// Set the available time in seconds for compiling (non-immediate) composite maps each frame\n        void setMinimumTimeAvailableForCompile(double time);\n\n        /// If current frame rate is higher than this, the extra time will be set aside to do more compiling\n        void setTargetFrameRate(float framerate);\n\n        /// Add a composite map to be rendered\n        void addCompositeMap(CompositeMap* map, bool immediate=false);\n\n        /// Mark this composite map to be required for the current frame\n        void setImmediate(CompositeMap* map);\n\n        unsigned int getCompileSetSize() const;\n\n    private:\n        float mTargetFrameRate;\n        double mMinimumTimeAvailable;\n        mutable osg::Timer mTimer;\n\n        osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;\n        osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;\n\n        typedef std::set<osg::ref_ptr<CompositeMap> > CompileSet;\n\n        mutable CompileSet mCompileSet;\n        mutable CompileSet mImmediateCompileSet;\n\n        mutable std::mutex mMutex;\n\n        osg::ref_ptr<osg::FrameBufferObject> mFBO;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/defs.hpp",
    "content": "#ifndef COMPONENTS_TERRAIN_DEFS_HPP\n#define COMPONENTS_TERRAIN_DEFS_HPP\n\n#include <string>\n\nnamespace Terrain\n{\n\n    enum Direction\n    {\n        North = 0,\n        East = 1,\n        South = 2,\n        West = 3\n    };\n\n    struct LayerInfo\n    {\n        std::string mDiffuseMap;\n        std::string mNormalMap;\n        bool mParallax; // Height info in normal map alpha channel?\n        bool mSpecular; // Specular info in diffuse map alpha channel?\n\n        bool requiresShaders() const { return !mNormalMap.empty() || mSpecular; }\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/material.cpp",
    "content": "#include \"material.hpp\"\n\n#include <osg/Fog>\n#include <osg/Depth>\n#include <osg/TexEnvCombine>\n#include <osg/Texture2D>\n#include <osg/TexMat>\n#include <osg/BlendFunc>\n\n#include <components/shader/shadermanager.hpp>\n\n#include <mutex>\n\nnamespace\n{\n    class BlendmapTexMat\n    {\n    public:\n        static const osg::ref_ptr<osg::TexMat>& value(const int blendmapScale)\n        {\n            static BlendmapTexMat instance;\n            return instance.get(blendmapScale);\n        }\n\n        const osg::ref_ptr<osg::TexMat>& get(const int blendmapScale)\n        {\n            const std::lock_guard<std::mutex> lock(mMutex);\n            auto texMat = mTexMatMap.find(blendmapScale);\n            if (texMat == mTexMatMap.end())\n            {\n                osg::Matrixf matrix;\n                float scale = (blendmapScale/(static_cast<float>(blendmapScale)+1.f));\n                matrix.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f));\n                matrix.preMultScale(osg::Vec3f(scale, scale, 1.f));\n                matrix.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f));\n                // We need to nudge the blendmap to look like vanilla.\n                // This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the blendmap, apparently.\n                matrix.preMultTranslate(osg::Vec3f(1.0f/blendmapScale/4.0f, 1.0f/blendmapScale/4.0f, 0.f));\n\n                texMat = mTexMatMap.insert(std::make_pair(blendmapScale, new osg::TexMat(matrix))).first;\n            }\n            return texMat->second;\n        }\n\n    private:\n        std::mutex mMutex;\n        std::map<float, osg::ref_ptr<osg::TexMat>> mTexMatMap;\n    };\n\n    class LayerTexMat\n    {\n    public:\n        static const osg::ref_ptr<osg::TexMat>& value(const float layerTileSize)\n        {\n            static LayerTexMat instance;\n            return instance.get(layerTileSize);\n        }\n\n        const osg::ref_ptr<osg::TexMat>& get(const float layerTileSize)\n        {\n            const std::lock_guard<std::mutex> lock(mMutex);\n            auto texMat = mTexMatMap.find(layerTileSize);\n            if (texMat == mTexMatMap.end())\n            {\n                texMat = mTexMatMap.insert(std::make_pair(layerTileSize,\n                    new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize, layerTileSize, 1.f))))).first;\n            }\n            return texMat->second;\n        }\n\n    private:\n        std::mutex mMutex;\n        std::map<float, osg::ref_ptr<osg::TexMat>> mTexMatMap;\n    };\n\n    class EqualDepth\n    {\n    public:\n        static const osg::ref_ptr<osg::Depth>& value()\n        {\n            static EqualDepth instance;\n            return instance.mValue;\n        }\n\n    private:\n        osg::ref_ptr<osg::Depth> mValue;\n\n        EqualDepth()\n            : mValue(new osg::Depth)\n        {\n            mValue->setFunction(osg::Depth::EQUAL);\n        }\n    };\n\n    class LequalDepth\n    {\n    public:\n        static const osg::ref_ptr<osg::Depth>& value()\n        {\n            static LequalDepth instance;\n            return instance.mValue;\n        }\n\n    private:\n        osg::ref_ptr<osg::Depth> mValue;\n\n        LequalDepth()\n            : mValue(new osg::Depth)\n        {\n            mValue->setFunction(osg::Depth::LEQUAL);\n        }\n    };\n\n    class BlendFuncFirst\n    {\n    public:\n        static const osg::ref_ptr<osg::BlendFunc>& value()\n        {\n            static BlendFuncFirst instance;\n            return instance.mValue;\n        }\n\n    private:\n        osg::ref_ptr<osg::BlendFunc> mValue;\n\n        BlendFuncFirst()\n            : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ZERO))\n        {\n        }\n    };\n\n    class BlendFunc\n    {\n    public:\n        static const osg::ref_ptr<osg::BlendFunc>& value()\n        {\n            static BlendFunc instance;\n            return instance.mValue;\n        }\n\n    private:\n        osg::ref_ptr<osg::BlendFunc> mValue;\n\n        BlendFunc()\n            : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE))\n        {\n        }\n    };\n\n    class TexEnvCombine\n    {\n    public:\n        static const osg::ref_ptr<osg::TexEnvCombine>& value()\n        {\n            static TexEnvCombine instance;\n            return instance.mValue;\n        }\n\n    private:\n        osg::ref_ptr<osg::TexEnvCombine> mValue;\n\n        TexEnvCombine()\n            : mValue(new osg::TexEnvCombine)\n        {\n            mValue->setCombine_RGB(osg::TexEnvCombine::REPLACE);\n            mValue->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);\n        }\n    };\n}\n\nnamespace Terrain\n{\n    std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Shader::ShaderManager* shaderManager, const std::vector<TextureLayer> &layers,\n                                                           const std::vector<osg::ref_ptr<osg::Texture2D> > &blendmaps, int blendmapScale, float layerTileSize)\n    {\n        std::vector<osg::ref_ptr<osg::StateSet> > passes;\n\n        unsigned int blendmapIndex = 0;\n        unsigned int passIndex = 0;\n        for (std::vector<TextureLayer>::const_iterator it = layers.begin(); it != layers.end(); ++it)\n        {\n            bool firstLayer = (it == layers.begin());\n\n            osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);\n\n            if (!blendmaps.empty())\n            {\n                stateset->setMode(GL_BLEND, osg::StateAttribute::ON);\n                stateset->setRenderBinDetails(passIndex++, \"RenderBin\");\n                if (!firstLayer)\n                {\n                    stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON);\n                    stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON);\n                }\n                else\n                {\n                    stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON);\n                    stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON);\n                }\n            }\n\n            int texunit = 0;\n\n            if (useShaders)\n            {\n                stateset->setTextureAttributeAndModes(texunit, it->mDiffuseMap);\n\n                if (layerTileSize != 1.f)\n                    stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);\n\n                stateset->addUniform(new osg::Uniform(\"diffuseMap\", texunit));\n\n                if (!blendmaps.empty())\n                {\n                    ++texunit;\n                    osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);\n\n                    stateset->setTextureAttributeAndModes(texunit, blendmap.get());\n                    stateset->setTextureAttributeAndModes(texunit, BlendmapTexMat::value(blendmapScale));\n                    stateset->addUniform(new osg::Uniform(\"blendMap\", texunit));\n                }\n\n                if (it->mNormalMap)\n                {\n                    ++texunit;\n                    stateset->setTextureAttributeAndModes(texunit, it->mNormalMap);\n                    stateset->addUniform(new osg::Uniform(\"normalMap\", texunit));\n                }\n\n                Shader::ShaderManager::DefineMap defineMap;\n                defineMap[\"normalMap\"] = (it->mNormalMap) ? \"1\" : \"0\";\n                defineMap[\"blendMap\"] = (!blendmaps.empty()) ? \"1\" : \"0\";\n                defineMap[\"specularMap\"] = it->mSpecular ? \"1\" : \"0\";\n                defineMap[\"parallax\"] = (it->mNormalMap && it->mParallax) ? \"1\" : \"0\";\n\n                osg::ref_ptr<osg::Shader> vertexShader = shaderManager->getShader(\"terrain_vertex.glsl\", defineMap, osg::Shader::VERTEX);\n                osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader(\"terrain_fragment.glsl\", defineMap, osg::Shader::FRAGMENT);\n                if (!vertexShader || !fragmentShader)\n                {\n                    // Try again without shader. Error already logged by above\n                    return createPasses(false, shaderManager, layers, blendmaps, blendmapScale, layerTileSize);\n                }\n\n                stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader));\n                stateset->addUniform(new osg::Uniform(\"colorMode\", 2));\n            }\n            else\n            {\n                // Add the actual layer texture\n                osg::ref_ptr<osg::Texture2D> tex = it->mDiffuseMap;\n                stateset->setTextureAttributeAndModes(texunit, tex.get());\n\n                if (layerTileSize != 1.f)\n                    stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);\n\n                ++texunit;\n\n                // Multiply by the alpha map\n                if (!blendmaps.empty())\n                {\n                    osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);\n\n                    stateset->setTextureAttributeAndModes(texunit, blendmap.get());\n\n                    // This is to map corner vertices directly to the center of a blendmap texel.\n                    stateset->setTextureAttributeAndModes(texunit, BlendmapTexMat::value(blendmapScale));\n                    stateset->setTextureAttributeAndModes(texunit, TexEnvCombine::value(), osg::StateAttribute::ON);\n\n                    ++texunit;\n                }\n\n            }\n\n            passes.push_back(stateset);\n        }\n        return passes;\n    }\n\n}\n"
  },
  {
    "path": "components/terrain/material.hpp",
    "content": "#ifndef COMPONENTS_TERRAIN_MATERIAL_H\n#define COMPONENTS_TERRAIN_MATERIAL_H\n\n#include <osg/StateSet>\n\n#include \"defs.hpp\"\n\nnamespace osg\n{\n    class Texture2D;\n}\n\nnamespace Shader\n{\n    class ShaderManager;\n}\n\nnamespace Terrain\n{\n\n    struct TextureLayer\n    {\n        osg::ref_ptr<osg::Texture2D> mDiffuseMap;\n        osg::ref_ptr<osg::Texture2D> mNormalMap; // optional\n        bool mParallax;\n        bool mSpecular;\n    };\n\n    std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Shader::ShaderManager* shaderManager,\n                                                           const std::vector<TextureLayer>& layers,\n                                                           const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/quadtreenode.cpp",
    "content": "#include \"quadtreenode.hpp\"\n\n#include <cassert>\n\n#include <osgUtil/CullVisitor>\n\n#include \"defs.hpp\"\n#include \"viewdata.hpp\"\n\nnamespace Terrain\n{\n\nChildDirection reflect(ChildDirection dir, Direction dir2)\n{\n    assert(dir != Root);\n    const int lookupTable[4][4] =\n    {\n        // NW  NE  SW  SE\n        {  SW, SE, NW, NE }, // N\n        {  NE, NW, SE, SW }, // E\n        {  SW, SE, NW, NE }, // S\n        {  NE, NW, SE, SW }  // W\n    };\n    return (ChildDirection)lookupTable[dir2][dir];\n}\n\nbool adjacent(ChildDirection dir, Direction dir2)\n{\n    assert(dir != Root);\n    const bool lookupTable[4][4] =\n    {\n        // NW    NE    SW     SE\n        {  true, true, false, false }, // N\n        {  false, true, false, true }, // E\n        {  false, false, true, true }, // S\n        {  true, false, true, false }  // W\n    };\n    return lookupTable[dir2][dir];\n}\n\nQuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir)\n{\n    if (currentNode->getDirection() == Root)\n        return nullptr; // Arrived at root node, the root node does not have neighbours\n\n    QuadTreeNode* nextNode;\n    if (adjacent(currentNode->getDirection(), dir))\n        nextNode = searchNeighbour(currentNode->getParent(), dir);\n    else\n        nextNode = currentNode->getParent();\n\n    if (nextNode && nextNode->getNumChildren())\n        return nextNode->getChild(reflect(currentNode->getDirection(), dir));\n    else\n        return nullptr;\n}\n\nQuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float size, const osg::Vec2f& center)\n    : mParent(parent)\n    , mDirection(direction)\n    , mValidBounds(false)\n    , mSize(size)\n    , mCenter(center)\n{\n    for (unsigned int i=0; i<4; ++i)\n        mNeighbours[i] = nullptr;\n}\n\nQuadTreeNode::~QuadTreeNode()\n{\n}\n\nQuadTreeNode *QuadTreeNode::getNeighbour(Direction dir)\n{\n    return mNeighbours[dir];\n}\n\nfloat QuadTreeNode::distance(const osg::Vec3f& v) const\n{\n    const osg::BoundingBox& box = getBoundingBox();\n    if (box.contains(v))\n        return 0;\n    else\n    {\n        osg::Vec3f maxDist(0,0,0);\n        if (v.x() < box.xMin())\n            maxDist.x() = box.xMin() - v.x();\n        else if (v.x() > box.xMax())\n            maxDist.x() = v.x() - box.xMax();\n        if (v.y() < box.yMin())\n            maxDist.y() = box.yMin() - v.y();\n        else if (v.y() > box.yMax())\n            maxDist.y() = v.y() - box.yMax();\n        if (v.z() < box.zMin())\n            maxDist.z() = box.zMin() - v.z();\n        else if (v.z() > box.zMax())\n            maxDist.z() = v.z() - box.zMax();\n        return maxDist.length();\n    }\n}\n\nvoid QuadTreeNode::initNeighbours()\n{\n    for (int i=0; i<4; ++i)\n        mNeighbours[i] = searchNeighbour(this, (Direction)i);\n\n    for (unsigned int i=0; i<getNumChildren(); ++i)\n        getChild(i)->initNeighbours();\n}\n\nvoid QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback)\n{\n    if (!hasValidBounds())\n        return;\n    LodCallback::ReturnValue lodResult = lodCallback->isSufficientDetail(this, distance(viewPoint));\n    if (lodResult == LodCallback::StopTraversal)\n        return;\n    else if (lodResult == LodCallback::Deeper && getNumChildren())\n    {\n        for (unsigned int i=0; i<getNumChildren(); ++i)\n            getChild(i)->traverseNodes(vd, viewPoint, lodCallback);\n    }\n    else\n        vd->add(this);\n}\n\nvoid QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox)\n{\n    mBoundingBox = boundingBox;\n    mValidBounds = boundingBox.valid();\n}\n\nconst osg::BoundingBox &QuadTreeNode::getBoundingBox() const\n{\n    return mBoundingBox;\n}\n\nfloat QuadTreeNode::getSize() const\n{\n    return mSize;\n}\n\nconst osg::Vec2f &QuadTreeNode::getCenter() const\n{\n    return mCenter;\n}\n\n}\n"
  },
  {
    "path": "components/terrain/quadtreenode.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_TERRAIN_QUADTREENODE_H\n#define OPENMW_COMPONENTS_TERRAIN_QUADTREENODE_H\n\n#include <osg/Group>\n\n#include \"defs.hpp\"\n\nnamespace Terrain\n{\n\n    enum ChildDirection\n    {\n        NW = 0,\n        NE = 1,\n        SW = 2,\n        SE = 3,\n        Root\n    };\n\n    class QuadTreeNode;\n    class LodCallback : public osg::Referenced\n    {\n    public:\n        virtual ~LodCallback() {}\n\n        enum ReturnValue\n        {\n            Deeper,\n            StopTraversal,\n            StopTraversalAndUse\n        };\n        virtual ReturnValue isSufficientDetail(QuadTreeNode *node, float dist) = 0;\n    };\n\n    class ViewData;\n\n    class QuadTreeNode : public osg::Group\n    {\n    public:\n        QuadTreeNode(QuadTreeNode* parent, ChildDirection dir, float size, const osg::Vec2f& center);\n        virtual ~QuadTreeNode();\n\n        inline QuadTreeNode* getParent() { return mParent; }\n        inline QuadTreeNode* getChild(unsigned int i) { return static_cast<QuadTreeNode*>(Group::getChild(i)); }\n        inline unsigned int getNumChildren() const override { return _children.size(); }\n\n        // osg::Group::addChild() does a lot of unrelated stuff, but we just really want to add a child node.\n        void addChildNode(QuadTreeNode* child)\n        {\n            // QuadTree node should not contain more than 4 child nodes.\n            // Reserve enough space if this node is supposed to have child nodes.\n            _children.reserve(4);\n            _children.push_back(child);\n            child->addParent(this);\n        };\n\n        float distance(const osg::Vec3f& v) const;\n\n        /// Returns our direction relative to the parent node, or Root if we are the root node.\n        ChildDirection getDirection() { return mDirection; }\n\n        /// Get neighbour node in this direction\n        QuadTreeNode* getNeighbour (Direction dir);\n\n        /// Initialize neighbours - do this after the quadtree is built\n        void initNeighbours();\n\n        void setBoundingBox(const osg::BoundingBox& boundingBox);\n        const osg::BoundingBox& getBoundingBox() const;\n        bool hasValidBounds() const { return mValidBounds; }\n\n        /// size in cell coordinates\n        float getSize() const;\n\n        /// center in cell coordinates\n        const osg::Vec2f& getCenter() const;\n\n        /// Traverse nodes according to LOD selection.\n        void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback);\n\n    private:\n        QuadTreeNode* mParent;\n\n        QuadTreeNode* mNeighbours[4];\n\n        ChildDirection mDirection;\n\n        osg::BoundingBox mBoundingBox;\n        bool mValidBounds;\n        float mSize;\n        osg::Vec2f mCenter;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/quadtreeworld.cpp",
    "content": "#include \"quadtreeworld.hpp\"\n\n#include <osgUtil/CullVisitor>\n#include <osg/ShapeDrawable>\n#include <osg/PolygonMode>\n\n#include <limits>\n#include <sstream>\n\n#include <components/misc/constants.hpp>\n#include <components/sceneutil/mwshadowtechnique.hpp>\n#include <components/sceneutil/positionattitudetransform.hpp>\n\n#include \"quadtreenode.hpp\"\n#include \"storage.hpp\"\n#include \"viewdata.hpp\"\n#include \"chunkmanager.hpp\"\n#include \"compositemaprenderer.hpp\"\n#include \"terraindrawable.hpp\"\n\nnamespace\n{\n\n    bool isPowerOfTwo(int x)\n    {\n        return ( (x > 0) && ((x & (x - 1)) == 0) );\n    }\n\n    int nextPowerOfTwo (int v)\n    {\n        if (isPowerOfTwo(v)) return v;\n        int depth=0;\n        while(v)\n        {\n            v >>= 1;\n            depth++;\n        }\n        return 1 << depth;\n    }\n\n    int Log2( unsigned int n )\n    {\n        int targetlevel = 0;\n        while (n >>= 1) ++targetlevel;\n        return targetlevel;\n    }\n\n}\n\nnamespace Terrain\n{\n\nclass DefaultLodCallback : public LodCallback\n{\npublic:\n    DefaultLodCallback(float factor, float minSize, float viewDistance, const osg::Vec4i& grid)\n        : mFactor(factor)\n        , mMinSize(minSize)\n        , mViewDistance(viewDistance)\n        , mActiveGrid(grid)\n    {\n    }\n\n    ReturnValue isSufficientDetail(QuadTreeNode* node, float dist) override\n    {\n        const osg::Vec2f& center = node->getCenter();\n        bool activeGrid = (center.x() > mActiveGrid.x() && center.y() > mActiveGrid.y() && center.x() < mActiveGrid.z() && center.y() < mActiveGrid.w());\n        if (dist > mViewDistance && !activeGrid) // for Scene<->ObjectPaging sync the activegrid must remain loaded\n            return StopTraversal;\n        if (node->getSize()>1)\n        {\n            float halfSize = node->getSize()/2;\n            osg::Vec4i nodeBounds (static_cast<int>(center.x() - halfSize), static_cast<int>(center.y() - halfSize), static_cast<int>(center.x() + halfSize), static_cast<int>(center.y() + halfSize));\n            bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) < std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) < std::min(nodeBounds.w(), mActiveGrid.w()));\n            // to prevent making chunks who will cross the activegrid border\n            if (intersects)\n                return Deeper;\n        }\n\n        int nativeLodLevel = Log2(static_cast<unsigned int>(node->getSize()/mMinSize));\n        int lodLevel = Log2(static_cast<unsigned int>(dist/(Constants::CellSizeInUnits*mMinSize*mFactor)));\n\n        return nativeLodLevel <= lodLevel ? StopTraversalAndUse : Deeper;\n    }\n\nprivate:\n    float mFactor;\n    float mMinSize;\n    float mViewDistance;\n    osg::Vec4i mActiveGrid;\n};\n\nclass RootNode : public QuadTreeNode\n{\npublic:\n    RootNode(float size, const osg::Vec2f& center)\n        : QuadTreeNode(nullptr, Root, size, center)\n        , mWorld(nullptr)\n    {\n    }\n\n    void setWorld(QuadTreeWorld* world)\n    {\n        mWorld = world;\n    }\n\n    void accept(osg::NodeVisitor &nv) override\n    {\n        if (!nv.validNodeMask(*this))\n            return;\n        nv.pushOntoNodePath(this);\n        mWorld->accept(nv);\n        nv.popFromNodePath();\n    }\n\nprivate:\n    QuadTreeWorld* mWorld;\n};\n\nclass QuadTreeBuilder\n{\npublic:\n    QuadTreeBuilder(Terrain::Storage* storage, float minSize)\n        : mStorage(storage)\n        , mMinX(0.f), mMaxX(0.f), mMinY(0.f), mMaxY(0.f)\n        , mMinSize(minSize)\n    {\n    }\n\n    void build()\n    {\n        mStorage->getBounds(mMinX, mMaxX, mMinY, mMaxY);\n\n        int origSizeX = static_cast<int>(mMaxX - mMinX);\n        int origSizeY = static_cast<int>(mMaxY - mMinY);\n\n        // Dividing a quad tree only works well for powers of two, so round up to the nearest one\n        int size = nextPowerOfTwo(std::max(origSizeX, origSizeY));\n\n        float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f;\n        float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f;\n\n        mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY));\n        addChildren(mRootNode);\n\n        mRootNode->initNeighbours();\n        float cellWorldSize = mStorage->getCellWorldSize();\n        mRootNode->setInitialBound(osg::BoundingSphere(osg::BoundingBox(osg::Vec3(mMinX*cellWorldSize, mMinY*cellWorldSize, 0), osg::Vec3(mMaxX*cellWorldSize, mMaxY*cellWorldSize, 0))));\n    }\n\n    void addChildren(QuadTreeNode* parent)\n    {\n        float halfSize = parent->getSize()/2.f;\n        osg::BoundingBox boundingBox;\n        for (unsigned int i=0; i<4; ++i)\n        {\n            osg::ref_ptr<QuadTreeNode> child = addChild(parent, static_cast<ChildDirection>(i), halfSize);\n            if (child)\n            {\n                boundingBox.expandBy(child->getBoundingBox());\n                parent->addChildNode(child);\n            }\n        }\n\n        if (!boundingBox.valid())\n            parent->removeChildren(0, 4);\n        else\n            parent->setBoundingBox(boundingBox);\n    }\n\n    osg::ref_ptr<QuadTreeNode> addChild(QuadTreeNode* parent, ChildDirection direction, float size)\n    {\n        float halfSize = size/2.f;\n        osg::Vec2f center;\n        switch (direction)\n        {\n        case SW:\n            center = parent->getCenter() + osg::Vec2f(-halfSize,-halfSize);\n            break;\n        case SE:\n            center = parent->getCenter() + osg::Vec2f(halfSize, -halfSize);\n            break;\n        case NW:\n            center = parent->getCenter() + osg::Vec2f(-halfSize, halfSize);\n            break;\n        case NE:\n            center = parent->getCenter() + osg::Vec2f(halfSize, halfSize);\n            break;\n        default:\n            break;\n        }\n\n        osg::ref_ptr<QuadTreeNode> node = new QuadTreeNode(parent, direction, size, center);\n\n        if (center.x() - halfSize > mMaxX\n                || center.x() + halfSize < mMinX\n                || center.y() - halfSize > mMaxY\n                || center.y() + halfSize < mMinY )\n            // Out of bounds of the actual terrain - this will happen because\n            // we rounded the size up to the next power of two\n        {\n            // Still create and return an empty node so as to not break the assumption that each QuadTreeNode has either 4 or 0 children.\n            return node;\n        }\n\n        // Do not add child nodes for default cells without data.\n        // size = 1 means that the single shape covers the whole cell.\n        if (node->getSize() == 1 && !mStorage->hasData(center.x()-0.5, center.y()-0.5))\n            return node;\n\n        if (node->getSize() <= mMinSize)\n        {\n            // We arrived at a leaf.\n            // Since the tree is used for LOD level selection instead of culling, we do not need to load the actual height data here.\n            constexpr float minZ = -std::numeric_limits<float>::max();\n            constexpr float maxZ = std::numeric_limits<float>::max();\n            float cellWorldSize = mStorage->getCellWorldSize();\n            osg::BoundingBox boundingBox(osg::Vec3f((center.x()-halfSize)*cellWorldSize, (center.y()-halfSize)*cellWorldSize, minZ),\n                                    osg::Vec3f((center.x()+halfSize)*cellWorldSize, (center.y()+halfSize)*cellWorldSize, maxZ));\n            node->setBoundingBox(boundingBox);\n            return node;\n        }\n        else\n        {\n            addChildren(node);\n            return node;\n        }\n    }\n\n    osg::ref_ptr<RootNode> getRootNode()\n    {\n        return mRootNode;\n    }\n\nprivate:\n    Terrain::Storage* mStorage;\n\n    float mMinX, mMaxX, mMinY, mMaxY;\n    float mMinSize;\n\n    osg::ref_ptr<RootNode> mRootNode;\n};\n\nQuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize)\n    : TerrainGrid(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask)\n    , mViewDataMap(new ViewDataMap)\n    , mQuadTreeBuilt(false)\n    , mLodFactor(lodFactor)\n    , mVertexLodMod(vertexLodMod)\n    , mViewDistance(std::numeric_limits<float>::max())\n    , mMinSize(1/8.f)\n{\n    mChunkManager->setCompositeMapSize(compMapResolution);\n    mChunkManager->setCompositeMapLevel(compMapLevel);\n    mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize);\n    mChunkManagers.push_back(mChunkManager.get());\n}\n\nQuadTreeWorld::QuadTreeWorld(osg::Group *parent, Storage *storage, unsigned int nodeMask, float lodFactor, float chunkSize)\n    : TerrainGrid(parent, storage, nodeMask)\n    , mViewDataMap(new ViewDataMap)\n    , mQuadTreeBuilt(false)\n    , mLodFactor(lodFactor)\n    , mVertexLodMod(0)\n    , mViewDistance(std::numeric_limits<float>::max())\n    , mMinSize(chunkSize)\n{\n}\n\nQuadTreeWorld::~QuadTreeWorld()\n{\n}\n\n/// get the level of vertex detail to render this node at, expressed relative to the native resolution of the data set.\nunsigned int getVertexLod(QuadTreeNode* node, int vertexLodMod)\n{\n    int lod = Log2(int(node->getSize()));\n    if (vertexLodMod > 0)\n    {\n        lod = std::max(0, lod-vertexLodMod);\n    }\n    else if (vertexLodMod < 0)\n    {\n        float size = node->getSize();\n        // Stop to simplify at this level since with size = 1 the node already covers the whole cell and has getCellVertices() vertices.\n        while (size < 1)\n        {\n            size *= 2;\n            vertexLodMod = std::min(0, vertexLodMod+1);\n        }\n        lod += std::abs(vertexLodMod);\n    }\n    return lod;\n}\n\n/// get the flags to use for stitching in the index buffer so that chunks of different LOD connect seamlessly\nunsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, const ViewData* vd)\n{\n    unsigned int lodFlags = 0;\n    for (unsigned int i=0; i<4; ++i)\n    {\n        QuadTreeNode* neighbour = node->getNeighbour(static_cast<Direction>(i));\n\n        // If the neighbour isn't currently rendering itself,\n        // go up until we find one. NOTE: We don't need to go down,\n        // because in that case neighbour's detail would be higher than\n        // our detail and the neighbour would handle stitching by itself.\n        while (neighbour && !vd->contains(neighbour))\n            neighbour = neighbour->getParent();\n        int lod = 0;\n        if (neighbour)\n            lod = getVertexLod(neighbour, vertexLodMod);\n\n        if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are -\n            lod = 0;         // neighbours with more detail will do the stitching themselves\n        // Use 4 bits for each LOD delta\n        if (lod > 0)\n        {\n            lodFlags |= static_cast<unsigned int>(lod - ourLod) << (4*i);\n        }\n    }\n    return lodFlags;\n}\n\nvoid loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector<QuadTreeWorld::ChunkManager*>& chunkManagers, bool compile)\n{\n    if (!vd->hasChanged() && entry.mRenderingNode)\n        return;\n\n    int ourLod = getVertexLod(entry.mNode, vertexLodMod);\n\n    if (vd->hasChanged())\n    {\n        // have to recompute the lodFlags in case a neighbour has changed LOD.\n        unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vertexLodMod, vd);\n        if (lodFlags != entry.mLodFlags)\n        {\n            entry.mRenderingNode = nullptr;\n            entry.mLodFlags = lodFlags;\n        }\n    }\n\n    if (!entry.mRenderingNode)\n    {\n        osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;\n        pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f));\n\n        const osg::Vec2f& center = entry.mNode->getCenter();\n        bool activeGrid = (center.x() > gridbounds.x() && center.y() > gridbounds.y() && center.x() < gridbounds.z() && center.y() < gridbounds.w());\n\n        for (QuadTreeWorld::ChunkManager* m : chunkManagers)\n        {\n            osg::ref_ptr<osg::Node> n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, activeGrid, vd->getViewPoint(), compile);\n            if (n) pat->addChild(n);\n        }\n        entry.mRenderingNode = pat;\n    }\n}\n\nvoid updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil::CullVisitor* cv, float cellworldsize, bool outofworld)\n{\n    if (!(cv->getTraversalMask() & callback->getCullMask()))\n        return;\n    float lowZ = std::numeric_limits<float>::max();\n    float highZ = callback->getHighZ();\n    if (cv->getEyePoint().z() <= highZ || outofworld)\n    {\n        callback->setLowZ(-std::numeric_limits<float>::max());\n        return;\n    }\n    cv->pushCurrentMask();\n    static bool debug = getenv(\"OPENMW_WATER_CULLING_DEBUG\") != nullptr;\n    for (unsigned int i=0; i<vd->getNumEntries(); ++i)\n    {\n        ViewData::Entry& entry = vd->getEntry(i);\n        osg::BoundingBox bb = static_cast<TerrainDrawable*>(entry.mRenderingNode->asGroup()->getChild(0))->getWaterBoundingBox();\n        if (!bb.valid())\n            continue;\n        osg::Vec3f ofs (entry.mNode->getCenter().x()*cellworldsize, entry.mNode->getCenter().y()*cellworldsize, 0.f);\n        bb._min += ofs; bb._max += ofs;\n        bb._min.z() = highZ;\n        bb._max.z() = highZ;\n        if (cv->isCulled(bb))\n            continue;\n        lowZ = bb._min.z();\n\n        if (!debug)\n            break;\n        osg::Box* b = new osg::Box;\n        b->set(bb.center(), bb._max - bb.center());\n        osg::ShapeDrawable* drw = new osg::ShapeDrawable(b);\n        static osg::ref_ptr<osg::StateSet> stateset = nullptr;\n        if (!stateset)\n        {\n            stateset = new osg::StateSet;\n            stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);\n            stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);\n            stateset->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE), osg::StateAttribute::ON);\n            osg::Material* m = new osg::Material;\n            m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,1,1));\n            m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1));\n            m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1));\n            stateset->setAttributeAndModes(m, osg::StateAttribute::ON);\n            stateset->setRenderBinDetails(100,\"RenderBin\");\n        }\n        drw->setStateSet(stateset);\n        drw->accept(*cv);\n    }\n    callback->setLowZ(lowZ);\n    cv->popCurrentMask();\n}\n\nvoid QuadTreeWorld::accept(osg::NodeVisitor &nv)\n{\n    bool isCullVisitor = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR;\n    if (!isCullVisitor && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR)\n    {\n        if (nv.getName().find(\"AcceptedByComponentsTerrainQuadTreeWorld\") != std::string::npos)\n        {\n            if (nv.getName().find(\"SceneUtil::MWShadowTechnique::ComputeLightSpaceBounds\") != std::string::npos)\n            {\n                SceneUtil::MWShadowTechnique::ComputeLightSpaceBounds* clsb = static_cast<SceneUtil::MWShadowTechnique::ComputeLightSpaceBounds*>(&nv);\n                clsb->apply(*this);\n            }\n            else\n                nv.apply(*mRootNode);\n        }\n        return;\n    }\n\n    osg::Object * viewer = isCullVisitor ? static_cast<osgUtil::CullVisitor*>(&nv)->getCurrentCamera() : nullptr;\n    bool needsUpdate = true;\n    ViewData *vd = mViewDataMap->getViewData(viewer, nv.getViewPoint(), mActiveGrid, needsUpdate);\n\n    if (needsUpdate)\n    {\n        vd->reset();\n        DefaultLodCallback lodCallback(mLodFactor, mMinSize, mViewDistance, mActiveGrid);\n        mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback);\n    }\n\n    const float cellWorldSize = mStorage->getCellWorldSize();\n\n    for (unsigned int i=0; i<vd->getNumEntries(); ++i)\n    {\n        ViewData::Entry& entry = vd->getEntry(i);\n        loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers, false);\n        entry.mRenderingNode->accept(nv);\n    }\n\n    if (mHeightCullCallback && isCullVisitor)\n        updateWaterCullingView(mHeightCullCallback, vd, static_cast<osgUtil::CullVisitor*>(&nv), mStorage->getCellWorldSize(), !isGridEmpty());\n\n    vd->markUnchanged();\n\n    double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0;\n    if (referenceTime != 0.0)\n    {\n        vd->setLastUsageTimeStamp(referenceTime);\n        mViewDataMap->clearUnusedViews(referenceTime);\n    }\n}\n\nvoid QuadTreeWorld::ensureQuadTreeBuilt()\n{\n    std::lock_guard<std::mutex> lock(mQuadTreeMutex);\n    if (mQuadTreeBuilt)\n        return;\n\n    QuadTreeBuilder builder(mStorage, mMinSize);\n    builder.build();\n\n    mRootNode = builder.getRootNode();\n    mRootNode->setWorld(this);\n    mQuadTreeBuilt = true;\n}\n\nvoid QuadTreeWorld::enable(bool enabled)\n{\n    if (enabled)\n    {\n        ensureQuadTreeBuilt();\n\n        if (!mRootNode->getNumParents())\n            mTerrainRoot->addChild(mRootNode);\n    }\n\n    if (mRootNode)\n        mRootNode->setNodeMask(enabled ? ~0 : 0);\n}\n\nView* QuadTreeWorld::createView()\n{\n    return mViewDataMap->createIndependentView();\n}\n\nvoid QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic<bool> &abort, std::atomic<int> &progress, int& progressTotal)\n{\n    ensureQuadTreeBuilt();\n\n    ViewData* vd = static_cast<ViewData*>(view);\n    vd->setViewPoint(viewPoint);\n    vd->setActiveGrid(grid);\n    DefaultLodCallback lodCallback(mLodFactor, mMinSize, mViewDistance, grid);\n    mRootNode->traverseNodes(vd, viewPoint, &lodCallback);\n\n    if (!progressTotal)\n        for (unsigned int i=0; i<vd->getNumEntries(); ++i)\n            progressTotal += vd->getEntry(i).mNode->getSize();\n\n    const float cellWorldSize = mStorage->getCellWorldSize();\n    for (unsigned int i=0; i<vd->getNumEntries() && !abort; ++i)\n    {\n        ViewData::Entry& entry = vd->getEntry(i);\n        loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true);\n        progress += entry.mNode->getSize();\n    }\n    vd->markUnchanged();\n}\n\nbool QuadTreeWorld::storeView(const View* view, double referenceTime)\n{\n    return mViewDataMap->storeView(static_cast<const ViewData*>(view), referenceTime);\n}\n\nvoid QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats)\n{\n    if (mCompositeMapRenderer)\n        stats->setAttribute(frameNumber, \"Composite\", mCompositeMapRenderer->getCompileSetSize());\n}\n\nvoid QuadTreeWorld::loadCell(int x, int y)\n{\n    // fallback behavior only for undefined cells (every other is already handled in quadtree)\n    float dummy;\n    if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x+0.5, y+0.5), dummy, dummy))\n        TerrainGrid::loadCell(x,y);\n    else\n        World::loadCell(x,y);\n}\n\nvoid QuadTreeWorld::unloadCell(int x, int y)\n{\n    // fallback behavior only for undefined cells (every other is already handled in quadtree)\n    float dummy;\n    if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x+0.5, y+0.5), dummy, dummy))\n        TerrainGrid::unloadCell(x,y);\n    else\n        World::unloadCell(x,y);\n}\n\nvoid QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m)\n{\n    mChunkManagers.push_back(m);\n    mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask());\n}\n\nvoid QuadTreeWorld::rebuildViews()\n{\n    mViewDataMap->rebuildViews();\n}\n\n}\n"
  },
  {
    "path": "components/terrain/quadtreeworld.hpp",
    "content": "#ifndef COMPONENTS_TERRAIN_QUADTREEWORLD_H\n#define COMPONENTS_TERRAIN_QUADTREEWORLD_H\n\n#include \"world.hpp\"\n#include \"terraingrid.hpp\"\n\n#include <mutex>\n\nnamespace osg\n{\n    class NodeVisitor;\n}\n\nnamespace Terrain\n{\n    class RootNode;\n    class ViewDataMap;\n\n    /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD.\n    class QuadTreeWorld : public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell)\n    {\n    public:\n        QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask, int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize);\n\n        QuadTreeWorld(osg::Group *parent, Storage *storage, unsigned int nodeMask, float lodFactor, float chunkSize);\n\n        ~QuadTreeWorld();\n\n        void accept(osg::NodeVisitor& nv);\n\n        void enable(bool enabled) override;\n\n        void setViewDistance(float distance) override { mViewDistance = distance; }\n\n        void cacheCell(View *view, int x, int y) override {}\n        /// @note Not thread safe.\n        void loadCell(int x, int y) override;\n        /// @note Not thread safe.\n        void unloadCell(int x, int y) override;\n\n        View* createView() override;\n        void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic<bool>& abort, std::atomic<int>& progress, int& progressRange) override;\n        bool storeView(const View* view, double referenceTime) override;\n        void rebuildViews() override;\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) override;\n\n        class ChunkManager\n        {\n        public:\n            virtual ~ChunkManager(){}\n            virtual osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) = 0;\n            virtual unsigned int getNodeMask() { return 0; }\n        };\n        void addChunkManager(ChunkManager*);\n\n    private:\n        void ensureQuadTreeBuilt();\n\n        osg::ref_ptr<RootNode> mRootNode;\n\n        osg::ref_ptr<ViewDataMap> mViewDataMap;\n\n        std::vector<ChunkManager*> mChunkManagers;\n\n        std::mutex mQuadTreeMutex;\n        bool mQuadTreeBuilt;\n        float mLodFactor;\n        int mVertexLodMod;\n        float mViewDistance;\n        float mMinSize;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/storage.hpp",
    "content": "#ifndef COMPONENTS_TERRAIN_STORAGE_H\n#define COMPONENTS_TERRAIN_STORAGE_H\n\n#include <vector>\n\n#include <osg/Vec2f>\n#include <osg/Vec3f>\n#include <osg/ref_ptr>\n#include <osg/Array>\n\n#include \"defs.hpp\"\n\nnamespace osg\n{\n    class Image;\n}\n\nnamespace Terrain\n{\n    /// We keep storage of terrain data abstract here since we need different implementations for game and editor\n    /// @note The implementation must be thread safe.\n    class Storage\n    {\n    public:\n        virtual ~Storage() {}\n\n    public:\n        /// Get bounds of the whole terrain in cell units\n        virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0;\n\n        /// Return true if there is land data for this cell\n        /// May be overriden for a faster implementation\n        virtual bool hasData(int cellX, int cellY)\n        {\n            float dummy;\n            return getMinMaxHeights(1, osg::Vec2f(cellX+0.5, cellY+0.5), dummy, dummy);\n        }\n\n        /// Get the minimum and maximum heights of a terrain region.\n        /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree.\n        ///        Larger chunks can simply merge AABB of children.\n        /// @param size size of the chunk in cell units\n        /// @param center center of the chunk in cell units\n        /// @param min min height will be stored here\n        /// @param max max height will be stored here\n        /// @return true if there was data available for this terrain chunk\n        virtual bool getMinMaxHeights (float size, const osg::Vec2f& center, float& min, float& max) = 0;\n\n        /// Fill vertex buffers for a terrain chunk.\n        /// @note May be called from background threads. Make sure to only call thread-safe functions from here!\n        /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue.\n        /// @note Vertices should be written in row-major order (a row is defined as parallel to the x-axis).\n        ///       The specified positions should be in local space, i.e. relative to the center of the terrain chunk.\n        /// @param lodLevel LOD level, 0 = most detailed\n        /// @param size size of the terrain chunk in cell units\n        /// @param center center of the chunk in cell units\n        /// @param positions buffer to write vertices\n        /// @param normals buffer to write vertex normals\n        /// @param colours buffer to write vertex colours\n        virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center,\n                                osg::ref_ptr<osg::Vec3Array> positions,\n                                osg::ref_ptr<osg::Vec3Array> normals,\n                                osg::ref_ptr<osg::Vec4ubArray> colours) = 0;\n\n        typedef std::vector<osg::ref_ptr<osg::Image> > ImageVector;\n        /// Create textures holding layer blend values for a terrain chunk.\n        /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might\n        ///       have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.\n        /// @note May be called from background threads. Make sure to only call thread-safe functions from here!\n        /// @param chunkSize size of the terrain chunk in cell units\n        /// @param chunkCenter center of the chunk in cell units\n        /// @param blendmaps created blendmaps will be written here\n        /// @param layerList names of the layer textures used will be written here\n        virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,\n                               std::vector<LayerInfo>& layerList) = 0;\n\n        virtual float getHeightAt (const osg::Vec3f& worldPos) = 0;\n\n        /// Get the transformation factor for mapping cell units to world units.\n        virtual float getCellWorldSize() = 0;\n\n        /// Get the number of vertices on one side for each cell. Should be (power of two)+1\n        virtual int getCellVertices() = 0;\n\n        virtual int getBlendmapScale(float chunkSize) = 0;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/terraindrawable.cpp",
    "content": "#include \"terraindrawable.hpp\"\n\n#include <osg/ClusterCullingCallback>\n#include <osgUtil/CullVisitor>\n\n#include <components/sceneutil/lightmanager.hpp>\n\n#include \"compositemaprenderer.hpp\"\n\nnamespace Terrain\n{\n\nTerrainDrawable::TerrainDrawable()\n{\n\n}\n\nTerrainDrawable::~TerrainDrawable()\n{\n\n}\n\nTerrainDrawable::TerrainDrawable(const TerrainDrawable &copy, const osg::CopyOp &copyop)\n    : osg::Geometry(copy, copyop)\n    , mPasses(copy.mPasses)\n    , mLightListCallback(copy.mLightListCallback)\n{\n\n}\n\nvoid TerrainDrawable::accept(osg::NodeVisitor &nv)\n{\n    if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)\n    {\n        osg::Geometry::accept(nv);\n    }\n    else if (nv.validNodeMask(*this))\n    {\n        nv.pushOntoNodePath(this);\n        cull(static_cast<osgUtil::CullVisitor*>(&nv));\n        nv.popFromNodePath();\n    }\n}\n\ninline float distance(const osg::Vec3& coord,const osg::Matrix& matrix)\n{\n    return -((float)coord[0]*(float)matrix(0,2)+(float)coord[1]*(float)matrix(1,2)+(float)coord[2]*(float)matrix(2,2)+matrix(3,2));\n}\n\n//canot use ClusterCullingCallback::cull: viewpoint != eyepoint\n// !osgfixpotential!\nbool clusterCull(osg::ClusterCullingCallback* cb, const osg::Vec3f& eyePoint, bool shadowcam)\n{\n    float _deviation = cb->getDeviation();\n    const osg::Vec3& _controlPoint = cb->getControlPoint();\n    osg::Vec3 _normal = cb->getNormal();\n    if (shadowcam) _normal = _normal * -1; //inverting for shadowcam frontfaceculing\n    float _radius = cb->getRadius();\n    if (_deviation<=-1.0f) return false;\n    osg::Vec3 eye_cp = eyePoint - _controlPoint;\n    float radius = eye_cp.length();\n    if (radius<_radius) return false;\n    float deviation = (eye_cp * _normal)/radius;\n    return deviation < _deviation;\n}\n\nvoid TerrainDrawable::cull(osgUtil::CullVisitor *cv)\n{\n    const osg::BoundingBox& bb = getBoundingBox();\n\n    if (_cullingActive && cv->isCulled(getBoundingBox()))\n        return;\n\n    bool shadowcam = cv->getCurrentCamera()->getName() == \"ShadowCamera\";\n\n    if (cv->getCullingMode() & osg::CullStack::CLUSTER_CULLING && clusterCull(mClusterCullingCallback, cv->getEyePoint(), shadowcam))\n        return;\n\n    osg::RefMatrix& matrix = *cv->getModelViewMatrix();\n\n    if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR && bb.valid())\n    {\n        if (!cv->updateCalculatedNearFar(matrix, *this, false))\n            return;\n    }\n\n    float depth = bb.valid() ? distance(bb.center(),matrix) : 0.0f;\n    if (osg::isNaN(depth))\n        return;\n\n    if (shadowcam)\n    {\n        cv->addDrawableAndDepth(this, &matrix, depth);\n        return;\n    }\n\n    if (mCompositeMap)\n    {\n        mCompositeMapRenderer->setImmediate(mCompositeMap);\n        mCompositeMap = nullptr;\n    }\n\n    bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv);\n\n    osg::StateSet* stateset = getStateSet();\n    if (stateset)\n        cv->pushStateSet(stateset);\n\n    for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it)\n    {\n        cv->pushStateSet(*it);\n        cv->addDrawableAndDepth(this, &matrix, depth);\n        cv->popStateSet();\n    }\n\n    if (stateset)\n        cv->popStateSet();\n    if (pushedLight)\n        cv->popStateSet();\n}\n\nvoid TerrainDrawable::createClusterCullingCallback()\n{\n    mClusterCullingCallback = new osg::ClusterCullingCallback(this);\n}\n\nvoid TerrainDrawable::setPasses(const TerrainDrawable::PassVector &passes)\n{\n    mPasses = passes;\n}\n\nvoid TerrainDrawable::setLightListCallback(SceneUtil::LightListCallback *lightListCallback)\n{\n    mLightListCallback = lightListCallback;\n}\n\nvoid TerrainDrawable::setupWaterBoundingBox(float waterheight, float margin)\n{\n    osg::Vec3Array* vertices = static_cast<osg::Vec3Array*>(getVertexArray());\n    for (unsigned int i=0; i<vertices->size(); ++i)\n    {\n        const osg::Vec3f& vertex = (*vertices)[i];\n        if (vertex.z() <= waterheight)\n            mWaterBoundingBox.expandBy(vertex);\n    }\n    if (mWaterBoundingBox.valid())\n    {\n        const osg::BoundingBox& bb = getBoundingBox();\n        mWaterBoundingBox.xMin() = std::max(bb.xMin(), mWaterBoundingBox.xMin() - margin);\n        mWaterBoundingBox.yMin() = std::max(bb.yMin(), mWaterBoundingBox.yMin() - margin);\n        mWaterBoundingBox.xMax() = std::min(bb.xMax(), mWaterBoundingBox.xMax() + margin);\n        mWaterBoundingBox.xMax() = std::min(bb.xMax(), mWaterBoundingBox.xMax() + margin);\n    }\n}\n\nvoid TerrainDrawable::compileGLObjects(osg::RenderInfo &renderInfo) const\n{\n    for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it)\n    {\n        osg::StateSet* stateset = *it;\n        stateset->compileGLObjects(*renderInfo.getState());\n    }\n\n    osg::Geometry::compileGLObjects(renderInfo);\n}\n\n}\n\n"
  },
  {
    "path": "components/terrain/terraindrawable.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_TERRAIN_DRAWABLE_H\n#define OPENMW_COMPONENTS_TERRAIN_DRAWABLE_H\n\n#include <osg/Geometry>\n\nnamespace osg\n{\n    class ClusterCullingCallback;\n}\n\nnamespace osgUtil\n{\n    class CullVisitor;\n}\n\nnamespace SceneUtil\n{\n    class LightListCallback;\n}\n\nnamespace Terrain\n{\n\n    class CompositeMap;\n    class CompositeMapRenderer;\n\n    /**\n     * Subclass of Geometry that supports built in multi-pass rendering and built in LightListCallback.\n     */\n    class TerrainDrawable : public osg::Geometry\n    {\n    public:\n        osg::Object* cloneType() const override { return new TerrainDrawable (); }\n        osg::Object* clone(const osg::CopyOp& copyop) const override { return new TerrainDrawable (*this,copyop); }\n        bool isSameKindAs(const osg::Object* obj) const override { return dynamic_cast<const TerrainDrawable *>(obj)!=nullptr; }\n        const char* className() const override { return \"TerrainDrawable\"; }\n        const char* libraryName() const override { return \"Terrain\"; }\n\n        TerrainDrawable();\n        ~TerrainDrawable(); // has to be defined in the cpp file because we only forward declared some members.\n        TerrainDrawable(const TerrainDrawable& copy, const osg::CopyOp& copyop);\n\n        void accept(osg::NodeVisitor &nv) override;\n        void cull(osgUtil::CullVisitor* cv);\n\n        typedef std::vector<osg::ref_ptr<osg::StateSet> > PassVector;\n        void setPasses (const PassVector& passes);\n\n        void setLightListCallback(SceneUtil::LightListCallback* lightListCallback);\n\n        void createClusterCullingCallback();\n\n        void compileGLObjects(osg::RenderInfo& renderInfo) const override;\n\n        void setupWaterBoundingBox(float waterheight, float margin);\n        const osg::BoundingBox& getWaterBoundingBox() const { return mWaterBoundingBox; }\n\n        void setCompositeMap(CompositeMap* map) { mCompositeMap = map; }\n        void setCompositeMapRenderer(CompositeMapRenderer* renderer) { mCompositeMapRenderer = renderer; }\n\n    private:\n        osg::BoundingBox mWaterBoundingBox;\n        PassVector mPasses;\n\n        osg::ref_ptr<osg::ClusterCullingCallback> mClusterCullingCallback;\n\n        osg::ref_ptr<SceneUtil::LightListCallback> mLightListCallback;\n        osg::ref_ptr<CompositeMap> mCompositeMap;\n        osg::ref_ptr<CompositeMapRenderer> mCompositeMapRenderer;\n    };\n\n}\n\n\n#endif\n"
  },
  {
    "path": "components/terrain/terraingrid.cpp",
    "content": "#include \"terraingrid.hpp\"\n\n#include <memory>\n\n#include <osg/Group>\n#include <osg/ComputeBoundsVisitor>\n\n#include <components/sceneutil/positionattitudetransform.hpp>\n#include \"chunkmanager.hpp\"\n#include \"compositemaprenderer.hpp\"\n#include \"storage.hpp\"\nnamespace Terrain\n{\n\nclass MyView : public View\n{\npublic:\n    osg::ref_ptr<osg::Node> mLoaded;\n\n    void reset() override {}\n};\n\nTerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask)\n    : Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask)\n    , mNumSplits(4)\n{\n}\n\nTerrainGrid::TerrainGrid(osg::Group* parent, Storage* storage, unsigned int nodeMask)\n    : Terrain::World(parent, storage, nodeMask)\n    , mNumSplits(4)\n{\n}\n\nTerrainGrid::~TerrainGrid()\n{\n    while (!mGrid.empty())\n    {\n        TerrainGrid::unloadCell(mGrid.begin()->first.first, mGrid.begin()->first.second);\n    }\n}\n\nvoid TerrainGrid::cacheCell(View* view, int x, int y)\n{\n    osg::Vec2f center(x+0.5f, y+0.5f);\n    static_cast<MyView*>(view)->mLoaded =  buildTerrain(nullptr, 1.f, center);\n}\n\nosg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter)\n{\n    if (chunkSize * mNumSplits > 1.f)\n    {\n        // keep splitting\n        osg::ref_ptr<osg::Group> group (new osg::Group);\n        if (parent)\n            parent->addChild(group);\n\n        float newChunkSize = chunkSize/2.f;\n        buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize/2.f, newChunkSize/2.f));\n        buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize/2.f, -newChunkSize/2.f));\n        buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize/2.f, newChunkSize/2.f));\n        buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize/2.f, -newChunkSize/2.f));\n        return group;\n    }\n    else\n    {\n        osg::ref_ptr<osg::Node> node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f(), true);\n        if (!node)\n            return nullptr;\n\n        const float cellWorldSize = mStorage->getCellWorldSize();\n        osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;\n        pat->setPosition(osg::Vec3f(chunkCenter.x()*cellWorldSize, chunkCenter.y()*cellWorldSize, 0.f));\n        pat->addChild(node);\n        if (parent)\n            parent->addChild(pat);\n        return pat;\n    }\n}\n\nvoid TerrainGrid::loadCell(int x, int y)\n{\n    if (mGrid.find(std::make_pair(x, y)) != mGrid.end())\n        return; // already loaded\n\n    osg::Vec2f center(x+0.5f, y+0.5f);\n    osg::ref_ptr<osg::Node> terrainNode = buildTerrain(nullptr, 1.f, center);\n    if (!terrainNode)\n        return; // no terrain defined\n\n    TerrainGrid::World::loadCell(x,y);\n\n    mTerrainRoot->addChild(terrainNode);\n\n    mGrid[std::make_pair(x,y)] = terrainNode;\n    updateWaterCulling();\n}\n\nvoid TerrainGrid::unloadCell(int x, int y)\n{\n    CellBorder::CellGrid::iterator it = mGrid.find(std::make_pair(x,y));\n    if (it == mGrid.end())\n        return;\n\n    Terrain::World::unloadCell(x,y);\n\n    osg::ref_ptr<osg::Node> terrainNode = it->second;\n    mTerrainRoot->removeChild(terrainNode);\n\n    mGrid.erase(it);\n    updateWaterCulling();\n}\n\nvoid TerrainGrid::updateWaterCulling()\n{\n    if (!mHeightCullCallback) return;\n\n    osg::ComputeBoundsVisitor computeBoundsVisitor;\n    mTerrainRoot->accept(computeBoundsVisitor);\n    float lowZ = computeBoundsVisitor.getBoundingBox()._min.z();\n    mHeightCullCallback->setLowZ(lowZ);\n}\n\nView *TerrainGrid::createView()\n{\n    return new MyView;\n}\n\n}\n"
  },
  {
    "path": "components/terrain/terraingrid.hpp",
    "content": "#ifndef COMPONENTS_TERRAIN_TERRAINGRID_H\n#define COMPONENTS_TERRAIN_TERRAINGRID_H\n\n#include <map>\n\n#include <osg/Vec2f>\n\n#include \"world.hpp\"\n\nnamespace Terrain\n{\n\n    /// @brief Simple terrain implementation that loads cells in a grid, with no LOD. Only requested cells are loaded.\n    class TerrainGrid : public Terrain::World\n    {\n    public:\n        TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask=~0u, unsigned int borderMask=0);\n        TerrainGrid(osg::Group* parent, Storage* storage, unsigned int nodeMask=~0u);\n        ~TerrainGrid();\n\n        void cacheCell(View* view, int x, int y) override;\n\n        /// @note Not thread safe.\n        void loadCell(int x, int y) override;\n\n        /// @note Not thread safe.\n        void unloadCell(int x, int y) override;\n\n        View* createView() override;\n\n    protected:\n        bool isGridEmpty() const { return mGrid.empty(); }\n\n    private:\n        osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter);\n        void updateWaterCulling();\n\n        // split each ESM::Cell into mNumSplits*mNumSplits terrain chunks\n        unsigned int mNumSplits;\n\n        CellBorder::CellGrid mGrid;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/texturemanager.cpp",
    "content": "#include \"texturemanager.hpp\"\n\n#include <osg/Stats>\n#include <osg/Texture2D>\n\n#include <components/resource/scenemanager.hpp>\n#include <components/resource/imagemanager.hpp>\n#include <components/resource/objectcache.hpp>\n\nnamespace Terrain\n{\n\nTextureManager::TextureManager(Resource::SceneManager *sceneMgr)\n    : ResourceManager(sceneMgr->getVFS())\n    , mSceneManager(sceneMgr)\n{\n\n}\n\nstruct UpdateTextureFilteringFunctor\n{\n    UpdateTextureFilteringFunctor(Resource::SceneManager* sceneMgr)\n        : mSceneManager(sceneMgr)\n    {\n    }\n    Resource::SceneManager* mSceneManager;\n\n    void operator()(std::string, osg::Object* obj)\n    {\n        mSceneManager->applyFilterSettings(static_cast<osg::Texture2D*>(obj));\n    }\n};\n\nvoid TextureManager::updateTextureFiltering()\n{\n    UpdateTextureFilteringFunctor f(mSceneManager);\n    mCache->call(f);\n}\n\nosg::ref_ptr<osg::Texture2D> TextureManager::getTexture(const std::string &name)\n{\n    // don't bother with case folding, since there is only one way of referring to terrain textures we can assume the case is always the same\n    osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(name);\n    if (obj)\n        return static_cast<osg::Texture2D*>(obj.get());\n    else\n    {\n        osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D(mSceneManager->getImageManager()->getImage(name)));\n        texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n        texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n        mSceneManager->applyFilterSettings(texture);\n        mCache->addEntryToObjectCache(name, texture.get());\n        return texture;\n    }\n}\n\nvoid TextureManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const\n{\n    stats->setAttribute(frameNumber, \"Terrain Texture\", mCache->getCacheSize());\n}\n\n\n\n}\n"
  },
  {
    "path": "components/terrain/texturemanager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_TERRAIN_TEXTUREMANAGER_H\n#define OPENMW_COMPONENTS_TERRAIN_TEXTUREMANAGER_H\n\n#include <string>\n\n#include <components/resource/resourcemanager.hpp>\n\nnamespace Resource\n{\n    class SceneManager;\n}\n\nnamespace osg\n{\n    class Texture2D;\n}\n\nnamespace Terrain\n{\n\n    class TextureManager : public Resource::ResourceManager\n    {\n    public:\n        TextureManager(Resource::SceneManager* sceneMgr);\n\n        void updateTextureFiltering();\n\n        osg::ref_ptr<osg::Texture2D> getTexture(const std::string& name);\n\n        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;\n\n    private:\n        Resource::SceneManager* mSceneManager;\n\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/viewdata.cpp",
    "content": "#include \"viewdata.hpp\"\n\n#include \"quadtreenode.hpp\"\n\nnamespace Terrain\n{\n\nViewData::ViewData()\n    : mNumEntries(0)\n    , mLastUsageTimeStamp(0.0)\n    , mChanged(false)\n    , mHasViewPoint(false)\n    , mWorldUpdateRevision(0)\n{\n\n}\n\nViewData::~ViewData()\n{\n\n}\n\nvoid ViewData::copyFrom(const ViewData& other)\n{\n    mNumEntries = other.mNumEntries;\n    mEntries = other.mEntries;\n    mChanged = other.mChanged;\n    mHasViewPoint = other.mHasViewPoint;\n    mViewPoint = other.mViewPoint;\n    mActiveGrid = other.mActiveGrid;\n    mWorldUpdateRevision = other.mWorldUpdateRevision;\n}\n\nvoid ViewData::add(QuadTreeNode *node)\n{\n    unsigned int index = mNumEntries++;\n\n    if (index+1 > mEntries.size())\n        mEntries.resize(index+1);\n\n    Entry& entry = mEntries[index];\n    if (entry.set(node))\n        mChanged = true;\n}\n\nunsigned int ViewData::getNumEntries() const\n{\n    return mNumEntries;\n}\n\nViewData::Entry &ViewData::getEntry(unsigned int i)\n{\n    return mEntries[i];\n}\n\nbool ViewData::hasChanged() const\n{\n    return mChanged;\n}\n\nbool ViewData::hasViewPoint() const\n{\n    return mHasViewPoint;\n}\n\nvoid ViewData::setViewPoint(const osg::Vec3f &viewPoint)\n{\n    mViewPoint = viewPoint;\n    mHasViewPoint = true;\n}\n\nconst osg::Vec3f& ViewData::getViewPoint() const\n{\n    return mViewPoint;\n}\n\nvoid ViewData::reset()\n{\n    // clear any unused entries\n    for (unsigned int i=mNumEntries; i<mEntries.size(); ++i)\n        mEntries[i].set(nullptr);\n\n    // reset index for next frame\n    mNumEntries = 0;\n    mChanged = false;\n}\n\nvoid ViewData::clear()\n{\n    for (unsigned int i=0; i<mEntries.size(); ++i)\n        mEntries[i].set(nullptr);\n    mNumEntries = 0;\n    mLastUsageTimeStamp = 0;\n    mChanged = false;\n    mHasViewPoint = false;\n}\n\nbool ViewData::suitableToUse(const osg::Vec4i &activeGrid) const\n{\n    return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries();\n}\n\nbool ViewData::contains(QuadTreeNode *node) const\n{\n    for (unsigned int i=0; i<mNumEntries; ++i)\n        if (mEntries[i].mNode == node)\n            return true;\n    return false;\n}\n\nViewData::Entry::Entry()\n    : mNode(nullptr)\n    , mLodFlags(0)\n{\n\n}\n\nbool ViewData::Entry::set(QuadTreeNode *node)\n{\n    if (node == mNode)\n        return false;\n    else\n    {\n        mNode = node;\n        // clear cached data\n        mRenderingNode = nullptr;\n        return true;\n    }\n}\n\nViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate)\n{\n    ViewerMap::const_iterator found = mViewers.find(viewer);\n    ViewData* vd = nullptr;\n    if (found == mViewers.end())\n    {\n        vd = createOrReuseView();\n        mViewers[viewer] = vd;\n    }\n    else\n        vd = found->second;\n    needsUpdate = false;\n\n    if (!vd->suitableToUse(activeGrid) || (vd->getViewPoint()-viewPoint).length2() >= mReuseDistance*mReuseDistance || vd->getWorldUpdateRevision() < mWorldUpdateRevision)\n    {\n        float shortestDist = viewer ? mReuseDistance*mReuseDistance : std::numeric_limits<float>::max();\n        const ViewData* mostSuitableView = nullptr;\n        for (const ViewData* other : mUsedViews)\n        {\n            if (other->suitableToUse(activeGrid) && other->getWorldUpdateRevision() >= mWorldUpdateRevision)\n            {\n                float dist = (viewPoint-other->getViewPoint()).length2();\n                if (dist < shortestDist)\n                {\n                    shortestDist = dist;\n                    mostSuitableView = other;\n                }\n            }\n        }\n        if (mostSuitableView && mostSuitableView != vd)\n        {\n            vd->copyFrom(*mostSuitableView);\n            return vd;\n        }\n        else if (!mostSuitableView)\n        {\n            vd->setViewPoint(viewPoint);\n            needsUpdate = true;\n        }\n    }\n    if (!vd->suitableToUse(activeGrid))\n    {\n        vd->setViewPoint(viewPoint);\n        vd->setActiveGrid(activeGrid);\n        needsUpdate = true;\n    }\n    return vd;\n}\n\nbool ViewDataMap::storeView(const ViewData* view, double referenceTime)\n{\n    if (view->getWorldUpdateRevision() < mWorldUpdateRevision)\n        return false;\n    ViewData* store = createOrReuseView();\n    store->copyFrom(*view);\n    store->setLastUsageTimeStamp(referenceTime);\n    return true;\n}\n\nViewData *ViewDataMap::createOrReuseView()\n{\n    ViewData* vd = nullptr;\n    if (mUnusedViews.size())\n    {\n        vd = mUnusedViews.front();\n        mUnusedViews.pop_front();\n    }\n    else\n    {\n        mViewVector.emplace_back();\n        vd = &mViewVector.back();\n    }\n    mUsedViews.push_back(vd);\n    vd->setWorldUpdateRevision(mWorldUpdateRevision);\n    return vd;\n}\n\nViewData *ViewDataMap::createIndependentView() const\n{\n    ViewData* vd = new ViewData;\n    vd->setWorldUpdateRevision(mWorldUpdateRevision);\n    return vd;\n}\n\nvoid ViewDataMap::clearUnusedViews(double referenceTime)\n{\n    for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end(); )\n    {\n        if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)\n            mViewers.erase(it++);\n        else\n            ++it;\n    }\n    for (std::deque<ViewData*>::iterator it = mUsedViews.begin(); it != mUsedViews.end(); )\n    {\n        if ((*it)->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)\n        {\n            (*it)->clear();\n            mUnusedViews.push_back(*it);\n            it = mUsedViews.erase(it);\n        }\n        else\n            ++it;\n    }\n}\n\nvoid ViewDataMap::rebuildViews()\n{\n    ++mWorldUpdateRevision;\n}\n\n}\n"
  },
  {
    "path": "components/terrain/viewdata.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_TERRAIN_VIEWDATA_H\n#define OPENMW_COMPONENTS_TERRAIN_VIEWDATA_H\n\n#include <vector>\n#include <deque>\n\n#include <osg/Node>\n\n#include \"world.hpp\"\n\nnamespace Terrain\n{\n\n    class QuadTreeNode;\n\n    class ViewData : public View\n    {\n    public:\n        ViewData();\n        ~ViewData();\n\n        void add(QuadTreeNode* node);\n\n        void reset() override;\n\n        bool suitableToUse(const osg::Vec4i& activeGrid) const;\n\n        void clear();\n\n        bool contains(QuadTreeNode* node) const;\n\n        void copyFrom(const ViewData& other);\n\n        struct Entry\n        {\n            Entry();\n\n            bool set(QuadTreeNode* node);\n\n            QuadTreeNode* mNode;\n\n            unsigned int mLodFlags;\n            osg::ref_ptr<osg::Node> mRenderingNode;\n        };\n\n        unsigned int getNumEntries() const;\n\n        Entry& getEntry(unsigned int i);\n\n        double getLastUsageTimeStamp() const { return mLastUsageTimeStamp; }\n        void setLastUsageTimeStamp(double timeStamp) { mLastUsageTimeStamp = timeStamp; }\n\n        /// @return Have any nodes changed since the last frame\n        bool hasChanged() const;\n        void markUnchanged() { mChanged = false; }\n\n        bool hasViewPoint() const;\n\n        void setViewPoint(const osg::Vec3f& viewPoint);\n        const osg::Vec3f& getViewPoint() const;\n\n        void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} }\n        const osg::Vec4i &getActiveGrid() const { return mActiveGrid;}\n\n        unsigned int getWorldUpdateRevision() const { return mWorldUpdateRevision; }\n        void setWorldUpdateRevision(int updateRevision) { mWorldUpdateRevision = updateRevision; }\n\n    private:\n        std::vector<Entry> mEntries;\n        unsigned int mNumEntries;\n        double mLastUsageTimeStamp;\n        bool mChanged;\n        osg::Vec3f mViewPoint;\n        bool mHasViewPoint;\n        osg::Vec4i mActiveGrid;\n        unsigned int mWorldUpdateRevision;\n    };\n\n    class ViewDataMap : public osg::Referenced\n    {\n    public:\n        ViewDataMap()\n            : mReuseDistance(150) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused.\n                                  // this value also serves as a threshold for when a newly loaded LOD gets unloaded again so that if you hover around an LOD transition point the LODs won't keep loading and unloading all the time.\n            , mExpiryDelay(1.f)\n            , mWorldUpdateRevision(0)\n        {}\n\n        ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate);\n\n        ViewData* createOrReuseView();\n        ViewData* createIndependentView() const;\n\n        void clearUnusedViews(double referenceTime);\n        void rebuildViews();\n        bool storeView(const ViewData* view, double referenceTime);\n\n    private:\n        std::list<ViewData> mViewVector;\n\n        typedef std::map<osg::ref_ptr<osg::Object>, ViewData*> ViewerMap;\n        ViewerMap mViewers;\n\n        float mReuseDistance;\n        float mExpiryDelay; // time in seconds for unused view to be removed\n\n        unsigned int mWorldUpdateRevision;\n\n        std::deque<ViewData*> mUsedViews;\n        std::deque<ViewData*> mUnusedViews;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/terrain/world.cpp",
    "content": "#include \"world.hpp\"\n\n#include <osg/Group>\n#include <osg/Camera>\n\n#include <components/resource/resourcesystem.hpp>\n\n#include \"storage.hpp\"\n#include \"texturemanager.hpp\"\n#include \"chunkmanager.hpp\"\n#include \"compositemaprenderer.hpp\"\n\nnamespace Terrain\n{\n\nWorld::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask)\n    : mStorage(storage)\n    , mParent(parent)\n    , mResourceSystem(resourceSystem)\n    , mBorderVisible(false)\n    , mHeightCullCallback(new HeightCullCallback)\n{\n    mTerrainRoot = new osg::Group;\n    mTerrainRoot->setNodeMask(nodeMask);\n    mTerrainRoot->setName(\"Terrain Root\");\n\n    osg::ref_ptr<osg::Camera> compositeCam = new osg::Camera;\n    compositeCam->setRenderOrder(osg::Camera::PRE_RENDER, -1);\n    compositeCam->setProjectionMatrix(osg::Matrix::identity());\n    compositeCam->setViewMatrix(osg::Matrix::identity());\n    compositeCam->setReferenceFrame(osg::Camera::ABSOLUTE_RF);\n    compositeCam->setClearMask(0);\n    compositeCam->setNodeMask(preCompileMask);\n    mCompositeMapCamera = compositeCam;\n\n    compileRoot->addChild(compositeCam);\n\n    mCompositeMapRenderer = new CompositeMapRenderer;\n    compositeCam->addChild(mCompositeMapRenderer);\n\n    mParent->addChild(mTerrainRoot);\n\n    mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager()));\n    mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer));\n    mChunkManager->setNodeMask(nodeMask);\n    mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask));\n\n    mResourceSystem->addResourceManager(mChunkManager.get());\n    mResourceSystem->addResourceManager(mTextureManager.get());\n}\n\nWorld::World(osg::Group* parent, Storage* storage, unsigned int nodeMask)\n    : mStorage(storage)\n    , mParent(parent)\n    , mCompositeMapCamera(nullptr)\n    , mCompositeMapRenderer(nullptr)\n    , mResourceSystem(nullptr)\n    , mTextureManager(nullptr)\n    , mChunkManager(nullptr)\n    , mCellBorder(nullptr)\n    , mBorderVisible(false)\n    , mHeightCullCallback(nullptr)\n{\n    mTerrainRoot = new osg::Group;\n    mTerrainRoot->setNodeMask(nodeMask);\n\n    mParent->addChild(mTerrainRoot);\n}\n\nWorld::~World()\n{\n    if (mResourceSystem && mChunkManager)\n        mResourceSystem->removeResourceManager(mChunkManager.get());\n    if (mResourceSystem && mTextureManager)\n        mResourceSystem->removeResourceManager(mTextureManager.get());\n\n    mParent->removeChild(mTerrainRoot);\n\n    if (mCompositeMapCamera && mCompositeMapRenderer)\n    {\n        mCompositeMapCamera->removeChild(mCompositeMapRenderer);\n        mCompositeMapCamera->getParent(0)->removeChild(mCompositeMapCamera);\n    }\n}\n\nvoid World::setWorkQueue(SceneUtil::WorkQueue* workQueue)\n{\n    mCompositeMapRenderer->setWorkQueue(workQueue);\n}\n\nvoid World::setBordersVisible(bool visible)\n{\n    mBorderVisible = visible;\n\n    if (visible)\n    {\n        for (std::set<std::pair<int,int>>::iterator it = mLoadedCells.begin(); it != mLoadedCells.end(); ++it)\n            mCellBorder->createCellBorderGeometry(it->first,it->second);\n    }\n    else\n        mCellBorder->destroyCellBorderGeometry();\n}\n\nvoid World::loadCell(int x, int y)\n{\n    if (mBorderVisible)\n        mCellBorder->createCellBorderGeometry(x,y);\n\n    mLoadedCells.insert(std::pair<int,int>(x,y));\n}\n\nvoid World::unloadCell(int x, int y)\n{\n    if (mBorderVisible)\n        mCellBorder->destroyCellBorderGeometry(x,y);\n\n    mLoadedCells.erase(std::pair<int,int>(x,y));\n}\n\nvoid World::setTargetFrameRate(float rate)\n{\n    mCompositeMapRenderer->setTargetFrameRate(rate);\n}\n\nfloat World::getHeightAt(const osg::Vec3f &worldPos)\n{\n    return mStorage->getHeightAt(worldPos);\n}\n\nvoid World::updateTextureFiltering()\n{\n    if (mTextureManager)\n        mTextureManager->updateTextureFiltering();\n}\n\nvoid World::clearAssociatedCaches()\n{\n    if (mChunkManager)\n        mChunkManager->clearCache();\n}\n\nosg::Callback* World::getHeightCullCallback(float highz, unsigned int mask)\n{\n    if (!mHeightCullCallback) return nullptr;\n\n    mHeightCullCallback->setHighZ(highz);\n    mHeightCullCallback->setCullMask(mask);\n    return mHeightCullCallback;\n}\n\n}\n"
  },
  {
    "path": "components/terrain/world.hpp",
    "content": "#ifndef COMPONENTS_TERRAIN_WORLD_H\n#define COMPONENTS_TERRAIN_WORLD_H\n\n#include <osg/ref_ptr>\n#include <osg/Referenced>\n#include <osg/Vec3f>\n#include <osg/NodeCallback>\n\n#include <atomic>\n#include <limits>\n#include <memory>\n#include <set>\n\n#include \"defs.hpp\"\n#include \"cellborder.hpp\"\n\nnamespace osg\n{\n    class Group;\n    class Stats;\n    class Node;\n    class Object;\n}\n\nnamespace Resource\n{\n    class ResourceSystem;\n}\n\nnamespace SceneUtil\n{\n    class WorkQueue;\n}\n\nnamespace Terrain\n{\n    class Storage;\n\n    class TextureManager;\n    class ChunkManager;\n    class CompositeMapRenderer;\n\n    class HeightCullCallback : public osg::NodeCallback\n    {\n    public:\n        void setLowZ(float z)\n        {\n            mLowZ = z;\n        }\n        float getLowZ() const\n        {\n            return mLowZ;\n        }\n\n        void setHighZ(float highZ)\n        {\n            mHighZ = highZ;\n        }\n        float getHighZ() const\n        {\n            return mHighZ;\n        }\n\n        void setCullMask(unsigned int mask)\n        {\n            mMask = mask;\n        }\n        unsigned int getCullMask() const\n        {\n            return mMask;\n        }\n\n        void operator()(osg::Node* node, osg::NodeVisitor* nv) override\n        {\n            if (mLowZ <= mHighZ)\n                traverse(node, nv);\n        }\n    private:\n        float mLowZ{-std::numeric_limits<float>::max()};\n        float mHighZ{std::numeric_limits<float>::max()};\n        unsigned int mMask{~0u};\n    };\n\n    /**\n     * @brief A View is a collection of rendering objects that are visible from a given camera/intersection.\n     * The base View class is part of the interface for usage in conjunction with preload feature.\n     */\n    class View : public osg::Referenced\n    {\n    public:\n        virtual ~View() {}\n\n        /// Reset internal structure so that the next addition to the view will override the previous frame's contents.\n        virtual void reset() = 0;\n    };\n\n    /**\n     * @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed\n     *  is up to the implementation.\n     */\n    class World\n    {\n    public:\n        /// @note takes ownership of \\a storage\n        /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..)\n        /// @param nodeMask mask for the terrain root\n        /// @param preCompileMask mask for pre compiling textures\n        World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask);\n        World(osg::Group* parent, Storage* storage, unsigned int nodeMask);\n        virtual ~World();\n\n        /// Set a WorkQueue to delete objects in the background thread.\n        void setWorkQueue(SceneUtil::WorkQueue* workQueue);\n\n        /// See CompositeMapRenderer::setTargetFrameRate\n        void setTargetFrameRate(float rate);\n\n        /// Apply the scene manager's texture filtering settings to all cached textures.\n        /// @note Thread safe.\n        void updateTextureFiltering();\n\n        float getHeightAt (const osg::Vec3f& worldPos);\n\n        /// Clears the cached land and landtexture data.\n        /// @note Thread safe.\n        virtual void clearAssociatedCaches();\n\n        /// Load a terrain cell and store it in the View for later use.\n        /// @note Thread safe.\n        virtual void cacheCell(View* view, int x, int y) {}\n\n        /// Load the cell into the scene graph.\n        /// @note Not thread safe.\n        virtual void loadCell(int x, int y);\n\n        /// Remove the cell from the scene graph.\n        /// @note Not thread safe.\n        virtual void unloadCell(int x, int y);\n\n        virtual void enable(bool enabled) {}\n\n        virtual void setBordersVisible(bool visible);\n        virtual bool getBordersVisible() { return mBorderVisible; }\n\n        /// Create a View to use with preload feature. The caller is responsible for deleting the view.\n        /// @note Thread safe.\n        virtual View* createView() { return nullptr; }\n\n        /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads.\n\n        virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic<bool>& abort, std::atomic<int>& progress, int& progressRange) {}\n\n        /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it.\n        /// @note Not thread safe.\n        virtual bool storeView(const View* view, double referenceTime) {return true;}\n\n        virtual void rebuildViews() {}\n\n        virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}\n\n        virtual void setViewDistance(float distance) {}\n\n        Storage* getStorage() { return mStorage; }\n\n        osg::Callback* getHeightCullCallback(float highz, unsigned int mask);\n\n        void setActiveGrid(const osg::Vec4i &grid) { mActiveGrid = grid; }\n\n    protected:\n        Storage* mStorage;\n\n        osg::ref_ptr<osg::Group> mParent;\n        osg::ref_ptr<osg::Group> mTerrainRoot;\n\n        osg::ref_ptr<osg::Group> mCompositeMapCamera;\n        osg::ref_ptr<CompositeMapRenderer> mCompositeMapRenderer;\n\n        Resource::ResourceSystem* mResourceSystem;\n\n        std::unique_ptr<TextureManager> mTextureManager;\n        std::unique_ptr<ChunkManager> mChunkManager;\n\n        std::unique_ptr<CellBorder> mCellBorder;\n\n        bool mBorderVisible;\n\n        std::set<std::pair<int,int>> mLoadedCells;\n        osg::ref_ptr<HeightCullCallback> mHeightCullCallback;\n\n        osg::Vec4i mActiveGrid;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/to_utf8/.gitignore",
    "content": "gen_iconv\n"
  },
  {
    "path": "components/to_utf8/gen_iconv.cpp",
    "content": "// This program generates the file tables_gen.hpp\n\n#include <iostream>\n\n#include <iconv.h>\n#include <cassert>\n\nvoid tab() { std::cout << \"   \"; }\n\n// write one number with a space in front of it and a comma after it\nvoid num(char i, bool last)\n{\n  // Convert i to its integer value, i.e. -128 to 127. Printing it directly\n  // would result in non-printable characters in the source code, which is bad.\n  std::cout << \" \" << static_cast<int>(i);\n  if(!last) std::cout << \",\";\n}\n\n// Write one table entry (UTF8 value), 1-5 bytes\nvoid writeChar(char *value, int length, bool last, const std::string &comment=\"\")\n{\n  assert(length >= 1 && length <= 5);\n  tab();\n  num(length, false);\n  for(int i=0;i<5;i++)\n    num(value[i], last && i==4);\n\n  if(comment != \"\")\n    std::cout << \" // \" << comment;\n\n  std::cout << std::endl;\n}\n\n// What to write on missing characters\nvoid writeMissing(bool last)\n{\n  // Just write a space character\n  char value[5];\n  value[0] = ' ';\n  for(int i=1; i<5; i++)\n    value[i] = 0;\n  writeChar(value, 1, last, \"not part of this charset\");\n}\n\nint write_table(const std::string &charset, const std::string &tableName)\n{\n  // Write table header\n  std::cout << \"static signed char \" << tableName << \"[] =\\n{\\n\";\n\n  // Open conversion system\n  iconv_t cd = iconv_open (\"UTF-8\", charset.c_str());\n\n  // Convert each character from 0 to 255\n  for(int i=0; i<256; i++)\n    {\n      bool last = (i==255);\n\n      char input = i;\n      char *iptr = &input;\n      size_t ileft = 1;\n\n      char output[5];\n      for(int k=0; k<5; k++) output[k] = 0;\n      char *optr = output;\n      size_t oleft = 5;\n\n      size_t res = iconv(cd, &iptr, &ileft, &optr, &oleft);\n\n      if(res) writeMissing(last);\n      else writeChar(output, 5-oleft, last);\n    }\n\n  iconv_close (cd);\n\n  // Finish table\n  std::cout << \"};\\n\";\n\n  return 0;\n}\n\nint main()\n{\n  // Write header guard\n  std::cout << \"#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\\n\\n\";\n\n  // Write namespace\n  std::cout << \"namespace ToUTF8\\n{\\n\\n\";\n\n  // Central European and Eastern European languages that use Latin script, such as\n  // Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian.\n  std::cout << \"\\n/// Central European and Eastern European languages that use Latin script,\"\n               \"\\n/// such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian,\"\n               \"\\n/// Serbian (Latin script), Romanian and Albanian.\"\n               \"\\n\";\n  write_table(\"WINDOWS-1250\", \"windows_1250\");\n\n  // Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n  std::cout << \"\\n/// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic\"\n               \"\\n/// and other languages\"\n               \"\\n\";\n  write_table(\"WINDOWS-1251\", \"windows_1251\");\n\n  // English\n  std::cout << \"\\n/// Latin alphabet used by English and some other Western languages\"\n               \"\\n\";\n  write_table(\"WINDOWS-1252\", \"windows_1252\");\n\n  write_table(\"CP437\", \"cp437\");\n\n  // Close namespace\n  std::cout << \"\\n}\\n\\n\";\n\n  // Close header guard\n  std::cout << \"#endif\\n\\n\";\n\n  return 0;\n}\n"
  },
  {
    "path": "components/to_utf8/tables_gen.hpp",
    "content": "#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\n\nnamespace ToUTF8\n{\n\n\n/// Central European and Eastern European languages that use Latin script,\n/// such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian,\n/// Serbian (Latin script), Romanian and Albanian.\nstatic signed char windows_1250[] =\n{\n    1, 0, 0, 0, 0, 0,\n    1, 1, 0, 0, 0, 0,\n    1, 2, 0, 0, 0, 0,\n    1, 3, 0, 0, 0, 0,\n    1, 4, 0, 0, 0, 0,\n    1, 5, 0, 0, 0, 0,\n    1, 6, 0, 0, 0, 0,\n    1, 7, 0, 0, 0, 0,\n    1, 8, 0, 0, 0, 0,\n    1, 9, 0, 0, 0, 0,\n    1, 10, 0, 0, 0, 0,\n    1, 11, 0, 0, 0, 0,\n    1, 12, 0, 0, 0, 0,\n    1, 13, 0, 0, 0, 0,\n    1, 14, 0, 0, 0, 0,\n    1, 15, 0, 0, 0, 0,\n    1, 16, 0, 0, 0, 0,\n    1, 17, 0, 0, 0, 0,\n    1, 18, 0, 0, 0, 0,\n    1, 19, 0, 0, 0, 0,\n    1, 20, 0, 0, 0, 0,\n    1, 21, 0, 0, 0, 0,\n    1, 22, 0, 0, 0, 0,\n    1, 23, 0, 0, 0, 0,\n    1, 24, 0, 0, 0, 0,\n    1, 25, 0, 0, 0, 0,\n    1, 26, 0, 0, 0, 0,\n    1, 27, 0, 0, 0, 0,\n    1, 28, 0, 0, 0, 0,\n    1, 29, 0, 0, 0, 0,\n    1, 30, 0, 0, 0, 0,\n    1, 31, 0, 0, 0, 0,\n    1, 32, 0, 0, 0, 0,\n    1, 33, 0, 0, 0, 0,\n    1, 34, 0, 0, 0, 0,\n    1, 35, 0, 0, 0, 0,\n    1, 36, 0, 0, 0, 0,\n    1, 37, 0, 0, 0, 0,\n    1, 38, 0, 0, 0, 0,\n    1, 39, 0, 0, 0, 0,\n    1, 40, 0, 0, 0, 0,\n    1, 41, 0, 0, 0, 0,\n    1, 42, 0, 0, 0, 0,\n    1, 43, 0, 0, 0, 0,\n    1, 44, 0, 0, 0, 0,\n    1, 45, 0, 0, 0, 0,\n    1, 46, 0, 0, 0, 0,\n    1, 47, 0, 0, 0, 0,\n    1, 48, 0, 0, 0, 0,\n    1, 49, 0, 0, 0, 0,\n    1, 50, 0, 0, 0, 0,\n    1, 51, 0, 0, 0, 0,\n    1, 52, 0, 0, 0, 0,\n    1, 53, 0, 0, 0, 0,\n    1, 54, 0, 0, 0, 0,\n    1, 55, 0, 0, 0, 0,\n    1, 56, 0, 0, 0, 0,\n    1, 57, 0, 0, 0, 0,\n    1, 58, 0, 0, 0, 0,\n    1, 59, 0, 0, 0, 0,\n    1, 60, 0, 0, 0, 0,\n    1, 61, 0, 0, 0, 0,\n    1, 62, 0, 0, 0, 0,\n    1, 63, 0, 0, 0, 0,\n    1, 64, 0, 0, 0, 0,\n    1, 65, 0, 0, 0, 0,\n    1, 66, 0, 0, 0, 0,\n    1, 67, 0, 0, 0, 0,\n    1, 68, 0, 0, 0, 0,\n    1, 69, 0, 0, 0, 0,\n    1, 70, 0, 0, 0, 0,\n    1, 71, 0, 0, 0, 0,\n    1, 72, 0, 0, 0, 0,\n    1, 73, 0, 0, 0, 0,\n    1, 74, 0, 0, 0, 0,\n    1, 75, 0, 0, 0, 0,\n    1, 76, 0, 0, 0, 0,\n    1, 77, 0, 0, 0, 0,\n    1, 78, 0, 0, 0, 0,\n    1, 79, 0, 0, 0, 0,\n    1, 80, 0, 0, 0, 0,\n    1, 81, 0, 0, 0, 0,\n    1, 82, 0, 0, 0, 0,\n    1, 83, 0, 0, 0, 0,\n    1, 84, 0, 0, 0, 0,\n    1, 85, 0, 0, 0, 0,\n    1, 86, 0, 0, 0, 0,\n    1, 87, 0, 0, 0, 0,\n    1, 88, 0, 0, 0, 0,\n    1, 89, 0, 0, 0, 0,\n    1, 90, 0, 0, 0, 0,\n    1, 91, 0, 0, 0, 0,\n    1, 92, 0, 0, 0, 0,\n    1, 93, 0, 0, 0, 0,\n    1, 94, 0, 0, 0, 0,\n    1, 95, 0, 0, 0, 0,\n    1, 96, 0, 0, 0, 0,\n    1, 97, 0, 0, 0, 0,\n    1, 98, 0, 0, 0, 0,\n    1, 99, 0, 0, 0, 0,\n    1, 100, 0, 0, 0, 0,\n    1, 101, 0, 0, 0, 0,\n    1, 102, 0, 0, 0, 0,\n    1, 103, 0, 0, 0, 0,\n    1, 104, 0, 0, 0, 0,\n    1, 105, 0, 0, 0, 0,\n    1, 106, 0, 0, 0, 0,\n    1, 107, 0, 0, 0, 0,\n    1, 108, 0, 0, 0, 0,\n    1, 109, 0, 0, 0, 0,\n    1, 110, 0, 0, 0, 0,\n    1, 111, 0, 0, 0, 0,\n    1, 112, 0, 0, 0, 0,\n    1, 113, 0, 0, 0, 0,\n    1, 114, 0, 0, 0, 0,\n    1, 115, 0, 0, 0, 0,\n    1, 116, 0, 0, 0, 0,\n    1, 117, 0, 0, 0, 0,\n    1, 118, 0, 0, 0, 0,\n    1, 119, 0, 0, 0, 0,\n    1, 120, 0, 0, 0, 0,\n    1, 121, 0, 0, 0, 0,\n    1, 122, 0, 0, 0, 0,\n    1, 123, 0, 0, 0, 0,\n    1, 124, 0, 0, 0, 0,\n    1, 125, 0, 0, 0, 0,\n    1, 126, 0, 0, 0, 0,\n    1, 127, 0, 0, 0, 0,\n    3, -30, -126, -84, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    3, -30, -128, -102, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    3, -30, -128, -98, 0, 0,\n    3, -30, -128, -90, 0, 0,\n    3, -30, -128, -96, 0, 0,\n    3, -30, -128, -95, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    3, -30, -128, -80, 0, 0,\n    2, -59, -96, 0, 0, 0,\n    3, -30, -128, -71, 0, 0,\n    2, -59, -102, 0, 0, 0,\n    2, -59, -92, 0, 0, 0,\n    2, -59, -67, 0, 0, 0,\n    2, -59, -71, 0, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    3, -30, -128, -104, 0, 0,\n    3, -30, -128, -103, 0, 0,\n    3, -30, -128, -100, 0, 0,\n    3, -30, -128, -99, 0, 0,\n    3, -30, -128, -94, 0, 0,\n    3, -30, -128, -109, 0, 0,\n    3, -30, -128, -108, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    3, -30, -124, -94, 0, 0,\n    2, -59, -95, 0, 0, 0,\n    3, -30, -128, -70, 0, 0,\n    2, -59, -101, 0, 0, 0,\n    2, -59, -91, 0, 0, 0,\n    2, -59, -66, 0, 0, 0,\n    2, -59, -70, 0, 0, 0,\n    2, -62, -96, 0, 0, 0,\n    2, -53, -121, 0, 0, 0,\n    2, -53, -104, 0, 0, 0,\n    2, -59, -127, 0, 0, 0,\n    2, -62, -92, 0, 0, 0,\n    2, -60, -124, 0, 0, 0,\n    2, -62, -90, 0, 0, 0,\n    2, -62, -89, 0, 0, 0,\n    2, -62, -88, 0, 0, 0,\n    2, -62, -87, 0, 0, 0,\n    2, -59, -98, 0, 0, 0,\n    2, -62, -85, 0, 0, 0,\n    2, -62, -84, 0, 0, 0,\n    2, -62, -83, 0, 0, 0,\n    2, -62, -82, 0, 0, 0,\n    2, -59, -69, 0, 0, 0,\n    2, -62, -80, 0, 0, 0,\n    2, -62, -79, 0, 0, 0,\n    2, -53, -101, 0, 0, 0,\n    2, -59, -126, 0, 0, 0,\n    2, -62, -76, 0, 0, 0,\n    2, -62, -75, 0, 0, 0,\n    2, -62, -74, 0, 0, 0,\n    2, -62, -73, 0, 0, 0,\n    2, -62, -72, 0, 0, 0,\n    2, -60, -123, 0, 0, 0,\n    2, -59, -97, 0, 0, 0,\n    2, -62, -69, 0, 0, 0,\n    2, -60, -67, 0, 0, 0,\n    2, -53, -99, 0, 0, 0,\n    2, -60, -66, 0, 0, 0,\n    2, -59, -68, 0, 0, 0,\n    2, -59, -108, 0, 0, 0,\n    2, -61, -127, 0, 0, 0,\n    2, -61, -126, 0, 0, 0,\n    2, -60, -126, 0, 0, 0,\n    2, -61, -124, 0, 0, 0,\n    2, -60, -71, 0, 0, 0,\n    2, -60, -122, 0, 0, 0,\n    2, -61, -121, 0, 0, 0,\n    2, -60, -116, 0, 0, 0,\n    2, -61, -119, 0, 0, 0,\n    2, -60, -104, 0, 0, 0,\n    2, -61, -117, 0, 0, 0,\n    2, -60, -102, 0, 0, 0,\n    2, -61, -115, 0, 0, 0,\n    2, -61, -114, 0, 0, 0,\n    2, -60, -114, 0, 0, 0,\n    2, -60, -112, 0, 0, 0,\n    2, -59, -125, 0, 0, 0,\n    2, -59, -121, 0, 0, 0,\n    2, -61, -109, 0, 0, 0,\n    2, -61, -108, 0, 0, 0,\n    2, -59, -112, 0, 0, 0,\n    2, -61, -106, 0, 0, 0,\n    2, -61, -105, 0, 0, 0,\n    2, -59, -104, 0, 0, 0,\n    2, -59, -82, 0, 0, 0,\n    2, -61, -102, 0, 0, 0,\n    2, -59, -80, 0, 0, 0,\n    2, -61, -100, 0, 0, 0,\n    2, -61, -99, 0, 0, 0,\n    2, -59, -94, 0, 0, 0,\n    2, -61, -97, 0, 0, 0,\n    2, -59, -107, 0, 0, 0,\n    2, -61, -95, 0, 0, 0,\n    2, -61, -94, 0, 0, 0,\n    2, -60, -125, 0, 0, 0,\n    2, -61, -92, 0, 0, 0,\n    2, -60, -70, 0, 0, 0,\n    2, -60, -121, 0, 0, 0,\n    2, -61, -89, 0, 0, 0,\n    2, -60, -115, 0, 0, 0,\n    2, -61, -87, 0, 0, 0,\n    2, -60, -103, 0, 0, 0,\n    2, -61, -85, 0, 0, 0,\n    2, -60, -101, 0, 0, 0,\n    2, -61, -83, 0, 0, 0,\n    2, -61, -82, 0, 0, 0,\n    2, -60, -113, 0, 0, 0,\n    2, -60, -111, 0, 0, 0,\n    2, -59, -124, 0, 0, 0,\n    2, -59, -120, 0, 0, 0,\n    2, -61, -77, 0, 0, 0,\n    2, -61, -76, 0, 0, 0,\n    2, -59, -111, 0, 0, 0,\n    2, -61, -74, 0, 0, 0,\n    2, -61, -73, 0, 0, 0,\n    2, -59, -103, 0, 0, 0,\n    2, -59, -81, 0, 0, 0,\n    2, -61, -70, 0, 0, 0,\n    2, -59, -79, 0, 0, 0,\n    2, -61, -68, 0, 0, 0,\n    2, -61, -67, 0, 0, 0,\n    2, -59, -93, 0, 0, 0,\n    2, -53, -103, 0, 0, 0\n};\n\n/// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic\n/// and other languages\nstatic signed char windows_1251[] =\n{\n    1, 0, 0, 0, 0, 0,\n    1, 1, 0, 0, 0, 0,\n    1, 2, 0, 0, 0, 0,\n    1, 3, 0, 0, 0, 0,\n    1, 4, 0, 0, 0, 0,\n    1, 5, 0, 0, 0, 0,\n    1, 6, 0, 0, 0, 0,\n    1, 7, 0, 0, 0, 0,\n    1, 8, 0, 0, 0, 0,\n    1, 9, 0, 0, 0, 0,\n    1, 10, 0, 0, 0, 0,\n    1, 11, 0, 0, 0, 0,\n    1, 12, 0, 0, 0, 0,\n    1, 13, 0, 0, 0, 0,\n    1, 14, 0, 0, 0, 0,\n    1, 15, 0, 0, 0, 0,\n    1, 16, 0, 0, 0, 0,\n    1, 17, 0, 0, 0, 0,\n    1, 18, 0, 0, 0, 0,\n    1, 19, 0, 0, 0, 0,\n    1, 20, 0, 0, 0, 0,\n    1, 21, 0, 0, 0, 0,\n    1, 22, 0, 0, 0, 0,\n    1, 23, 0, 0, 0, 0,\n    1, 24, 0, 0, 0, 0,\n    1, 25, 0, 0, 0, 0,\n    1, 26, 0, 0, 0, 0,\n    1, 27, 0, 0, 0, 0,\n    1, 28, 0, 0, 0, 0,\n    1, 29, 0, 0, 0, 0,\n    1, 30, 0, 0, 0, 0,\n    1, 31, 0, 0, 0, 0,\n    1, 32, 0, 0, 0, 0,\n    1, 33, 0, 0, 0, 0,\n    1, 34, 0, 0, 0, 0,\n    1, 35, 0, 0, 0, 0,\n    1, 36, 0, 0, 0, 0,\n    1, 37, 0, 0, 0, 0,\n    1, 38, 0, 0, 0, 0,\n    1, 39, 0, 0, 0, 0,\n    1, 40, 0, 0, 0, 0,\n    1, 41, 0, 0, 0, 0,\n    1, 42, 0, 0, 0, 0,\n    1, 43, 0, 0, 0, 0,\n    1, 44, 0, 0, 0, 0,\n    1, 45, 0, 0, 0, 0,\n    1, 46, 0, 0, 0, 0,\n    1, 47, 0, 0, 0, 0,\n    1, 48, 0, 0, 0, 0,\n    1, 49, 0, 0, 0, 0,\n    1, 50, 0, 0, 0, 0,\n    1, 51, 0, 0, 0, 0,\n    1, 52, 0, 0, 0, 0,\n    1, 53, 0, 0, 0, 0,\n    1, 54, 0, 0, 0, 0,\n    1, 55, 0, 0, 0, 0,\n    1, 56, 0, 0, 0, 0,\n    1, 57, 0, 0, 0, 0,\n    1, 58, 0, 0, 0, 0,\n    1, 59, 0, 0, 0, 0,\n    1, 60, 0, 0, 0, 0,\n    1, 61, 0, 0, 0, 0,\n    1, 62, 0, 0, 0, 0,\n    1, 63, 0, 0, 0, 0,\n    1, 64, 0, 0, 0, 0,\n    1, 65, 0, 0, 0, 0,\n    1, 66, 0, 0, 0, 0,\n    1, 67, 0, 0, 0, 0,\n    1, 68, 0, 0, 0, 0,\n    1, 69, 0, 0, 0, 0,\n    1, 70, 0, 0, 0, 0,\n    1, 71, 0, 0, 0, 0,\n    1, 72, 0, 0, 0, 0,\n    1, 73, 0, 0, 0, 0,\n    1, 74, 0, 0, 0, 0,\n    1, 75, 0, 0, 0, 0,\n    1, 76, 0, 0, 0, 0,\n    1, 77, 0, 0, 0, 0,\n    1, 78, 0, 0, 0, 0,\n    1, 79, 0, 0, 0, 0,\n    1, 80, 0, 0, 0, 0,\n    1, 81, 0, 0, 0, 0,\n    1, 82, 0, 0, 0, 0,\n    1, 83, 0, 0, 0, 0,\n    1, 84, 0, 0, 0, 0,\n    1, 85, 0, 0, 0, 0,\n    1, 86, 0, 0, 0, 0,\n    1, 87, 0, 0, 0, 0,\n    1, 88, 0, 0, 0, 0,\n    1, 89, 0, 0, 0, 0,\n    1, 90, 0, 0, 0, 0,\n    1, 91, 0, 0, 0, 0,\n    1, 92, 0, 0, 0, 0,\n    1, 93, 0, 0, 0, 0,\n    1, 94, 0, 0, 0, 0,\n    1, 95, 0, 0, 0, 0,\n    1, 96, 0, 0, 0, 0,\n    1, 97, 0, 0, 0, 0,\n    1, 98, 0, 0, 0, 0,\n    1, 99, 0, 0, 0, 0,\n    1, 100, 0, 0, 0, 0,\n    1, 101, 0, 0, 0, 0,\n    1, 102, 0, 0, 0, 0,\n    1, 103, 0, 0, 0, 0,\n    1, 104, 0, 0, 0, 0,\n    1, 105, 0, 0, 0, 0,\n    1, 106, 0, 0, 0, 0,\n    1, 107, 0, 0, 0, 0,\n    1, 108, 0, 0, 0, 0,\n    1, 109, 0, 0, 0, 0,\n    1, 110, 0, 0, 0, 0,\n    1, 111, 0, 0, 0, 0,\n    1, 112, 0, 0, 0, 0,\n    1, 113, 0, 0, 0, 0,\n    1, 114, 0, 0, 0, 0,\n    1, 115, 0, 0, 0, 0,\n    1, 116, 0, 0, 0, 0,\n    1, 117, 0, 0, 0, 0,\n    1, 118, 0, 0, 0, 0,\n    1, 119, 0, 0, 0, 0,\n    1, 120, 0, 0, 0, 0,\n    1, 121, 0, 0, 0, 0,\n    1, 122, 0, 0, 0, 0,\n    1, 123, 0, 0, 0, 0,\n    1, 124, 0, 0, 0, 0,\n    1, 125, 0, 0, 0, 0,\n    1, 126, 0, 0, 0, 0,\n    1, 127, 0, 0, 0, 0,\n    2, -48, -126, 0, 0, 0,\n    2, -48, -125, 0, 0, 0,\n    3, -30, -128, -102, 0, 0,\n    2, -47, -109, 0, 0, 0,\n    3, -30, -128, -98, 0, 0,\n    3, -30, -128, -90, 0, 0,\n    3, -30, -128, -96, 0, 0,\n    3, -30, -128, -95, 0, 0,\n    3, -30, -126, -84, 0, 0,\n    3, -30, -128, -80, 0, 0,\n    2, -48, -119, 0, 0, 0,\n    3, -30, -128, -71, 0, 0,\n    2, -48, -118, 0, 0, 0,\n    2, -48, -116, 0, 0, 0,\n    2, -48, -117, 0, 0, 0,\n    2, -48, -113, 0, 0, 0,\n    2, -47, -110, 0, 0, 0,\n    3, -30, -128, -104, 0, 0,\n    3, -30, -128, -103, 0, 0,\n    3, -30, -128, -100, 0, 0,\n    3, -30, -128, -99, 0, 0,\n    3, -30, -128, -94, 0, 0,\n    3, -30, -128, -109, 0, 0,\n    3, -30, -128, -108, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    3, -30, -124, -94, 0, 0,\n    2, -47, -103, 0, 0, 0,\n    3, -30, -128, -70, 0, 0,\n    2, -47, -102, 0, 0, 0,\n    2, -47, -100, 0, 0, 0,\n    2, -47, -101, 0, 0, 0,\n    2, -47, -97, 0, 0, 0,\n    2, -62, -96, 0, 0, 0,\n    2, -48, -114, 0, 0, 0,\n    2, -47, -98, 0, 0, 0,\n    2, -48, -120, 0, 0, 0,\n    2, -62, -92, 0, 0, 0,\n    2, -46, -112, 0, 0, 0,\n    2, -62, -90, 0, 0, 0,\n    2, -62, -89, 0, 0, 0,\n    2, -48, -127, 0, 0, 0,\n    2, -62, -87, 0, 0, 0,\n    2, -48, -124, 0, 0, 0,\n    2, -62, -85, 0, 0, 0,\n    2, -62, -84, 0, 0, 0,\n    2, -62, -83, 0, 0, 0,\n    2, -62, -82, 0, 0, 0,\n    2, -48, -121, 0, 0, 0,\n    2, -62, -80, 0, 0, 0,\n    2, -62, -79, 0, 0, 0,\n    2, -48, -122, 0, 0, 0,\n    2, -47, -106, 0, 0, 0,\n    2, -46, -111, 0, 0, 0,\n    2, -62, -75, 0, 0, 0,\n    2, -62, -74, 0, 0, 0,\n    2, -62, -73, 0, 0, 0,\n    2, -47, -111, 0, 0, 0,\n    3, -30, -124, -106, 0, 0,\n    2, -47, -108, 0, 0, 0,\n    2, -62, -69, 0, 0, 0,\n    2, -47, -104, 0, 0, 0,\n    2, -48, -123, 0, 0, 0,\n    2, -47, -107, 0, 0, 0,\n    2, -47, -105, 0, 0, 0,\n    2, -48, -112, 0, 0, 0,\n    2, -48, -111, 0, 0, 0,\n    2, -48, -110, 0, 0, 0,\n    2, -48, -109, 0, 0, 0,\n    2, -48, -108, 0, 0, 0,\n    2, -48, -107, 0, 0, 0,\n    2, -48, -106, 0, 0, 0,\n    2, -48, -105, 0, 0, 0,\n    2, -48, -104, 0, 0, 0,\n    2, -48, -103, 0, 0, 0,\n    2, -48, -102, 0, 0, 0,\n    2, -48, -101, 0, 0, 0,\n    2, -48, -100, 0, 0, 0,\n    2, -48, -99, 0, 0, 0,\n    2, -48, -98, 0, 0, 0,\n    2, -48, -97, 0, 0, 0,\n    2, -48, -96, 0, 0, 0,\n    2, -48, -95, 0, 0, 0,\n    2, -48, -94, 0, 0, 0,\n    2, -48, -93, 0, 0, 0,\n    2, -48, -92, 0, 0, 0,\n    2, -48, -91, 0, 0, 0,\n    2, -48, -90, 0, 0, 0,\n    2, -48, -89, 0, 0, 0,\n    2, -48, -88, 0, 0, 0,\n    2, -48, -87, 0, 0, 0,\n    2, -48, -86, 0, 0, 0,\n    2, -48, -85, 0, 0, 0,\n    2, -48, -84, 0, 0, 0,\n    2, -48, -83, 0, 0, 0,\n    2, -48, -82, 0, 0, 0,\n    2, -48, -81, 0, 0, 0,\n    2, -48, -80, 0, 0, 0,\n    2, -48, -79, 0, 0, 0,\n    2, -48, -78, 0, 0, 0,\n    2, -48, -77, 0, 0, 0,\n    2, -48, -76, 0, 0, 0,\n    2, -48, -75, 0, 0, 0,\n    2, -48, -74, 0, 0, 0,\n    2, -48, -73, 0, 0, 0,\n    2, -48, -72, 0, 0, 0,\n    2, -48, -71, 0, 0, 0,\n    2, -48, -70, 0, 0, 0,\n    2, -48, -69, 0, 0, 0,\n    2, -48, -68, 0, 0, 0,\n    2, -48, -67, 0, 0, 0,\n    2, -48, -66, 0, 0, 0,\n    2, -48, -65, 0, 0, 0,\n    2, -47, -128, 0, 0, 0,\n    2, -47, -127, 0, 0, 0,\n    2, -47, -126, 0, 0, 0,\n    2, -47, -125, 0, 0, 0,\n    2, -47, -124, 0, 0, 0,\n    2, -47, -123, 0, 0, 0,\n    2, -47, -122, 0, 0, 0,\n    2, -47, -121, 0, 0, 0,\n    2, -47, -120, 0, 0, 0,\n    2, -47, -119, 0, 0, 0,\n    2, -47, -118, 0, 0, 0,\n    2, -47, -117, 0, 0, 0,\n    2, -47, -116, 0, 0, 0,\n    2, -47, -115, 0, 0, 0,\n    2, -47, -114, 0, 0, 0,\n    2, -47, -113, 0, 0, 0\n};\n\n/// Latin alphabet used by English and some other Western languages\nstatic signed char windows_1252[] =\n{\n    1, 0, 0, 0, 0, 0,\n    1, 1, 0, 0, 0, 0,\n    1, 2, 0, 0, 0, 0,\n    1, 3, 0, 0, 0, 0,\n    1, 4, 0, 0, 0, 0,\n    1, 5, 0, 0, 0, 0,\n    1, 6, 0, 0, 0, 0,\n    1, 7, 0, 0, 0, 0,\n    1, 8, 0, 0, 0, 0,\n    1, 9, 0, 0, 0, 0,\n    1, 10, 0, 0, 0, 0,\n    1, 11, 0, 0, 0, 0,\n    1, 12, 0, 0, 0, 0,\n    1, 13, 0, 0, 0, 0,\n    1, 14, 0, 0, 0, 0,\n    1, 15, 0, 0, 0, 0,\n    1, 16, 0, 0, 0, 0,\n    1, 17, 0, 0, 0, 0,\n    1, 18, 0, 0, 0, 0,\n    1, 19, 0, 0, 0, 0,\n    1, 20, 0, 0, 0, 0,\n    1, 21, 0, 0, 0, 0,\n    1, 22, 0, 0, 0, 0,\n    1, 23, 0, 0, 0, 0,\n    1, 24, 0, 0, 0, 0,\n    1, 25, 0, 0, 0, 0,\n    1, 26, 0, 0, 0, 0,\n    1, 27, 0, 0, 0, 0,\n    1, 28, 0, 0, 0, 0,\n    1, 29, 0, 0, 0, 0,\n    1, 30, 0, 0, 0, 0,\n    1, 31, 0, 0, 0, 0,\n    1, 32, 0, 0, 0, 0,\n    1, 33, 0, 0, 0, 0,\n    1, 34, 0, 0, 0, 0,\n    1, 35, 0, 0, 0, 0,\n    1, 36, 0, 0, 0, 0,\n    1, 37, 0, 0, 0, 0,\n    1, 38, 0, 0, 0, 0,\n    1, 39, 0, 0, 0, 0,\n    1, 40, 0, 0, 0, 0,\n    1, 41, 0, 0, 0, 0,\n    1, 42, 0, 0, 0, 0,\n    1, 43, 0, 0, 0, 0,\n    1, 44, 0, 0, 0, 0,\n    1, 45, 0, 0, 0, 0,\n    1, 46, 0, 0, 0, 0,\n    1, 47, 0, 0, 0, 0,\n    1, 48, 0, 0, 0, 0,\n    1, 49, 0, 0, 0, 0,\n    1, 50, 0, 0, 0, 0,\n    1, 51, 0, 0, 0, 0,\n    1, 52, 0, 0, 0, 0,\n    1, 53, 0, 0, 0, 0,\n    1, 54, 0, 0, 0, 0,\n    1, 55, 0, 0, 0, 0,\n    1, 56, 0, 0, 0, 0,\n    1, 57, 0, 0, 0, 0,\n    1, 58, 0, 0, 0, 0,\n    1, 59, 0, 0, 0, 0,\n    1, 60, 0, 0, 0, 0,\n    1, 61, 0, 0, 0, 0,\n    1, 62, 0, 0, 0, 0,\n    1, 63, 0, 0, 0, 0,\n    1, 64, 0, 0, 0, 0,\n    1, 65, 0, 0, 0, 0,\n    1, 66, 0, 0, 0, 0,\n    1, 67, 0, 0, 0, 0,\n    1, 68, 0, 0, 0, 0,\n    1, 69, 0, 0, 0, 0,\n    1, 70, 0, 0, 0, 0,\n    1, 71, 0, 0, 0, 0,\n    1, 72, 0, 0, 0, 0,\n    1, 73, 0, 0, 0, 0,\n    1, 74, 0, 0, 0, 0,\n    1, 75, 0, 0, 0, 0,\n    1, 76, 0, 0, 0, 0,\n    1, 77, 0, 0, 0, 0,\n    1, 78, 0, 0, 0, 0,\n    1, 79, 0, 0, 0, 0,\n    1, 80, 0, 0, 0, 0,\n    1, 81, 0, 0, 0, 0,\n    1, 82, 0, 0, 0, 0,\n    1, 83, 0, 0, 0, 0,\n    1, 84, 0, 0, 0, 0,\n    1, 85, 0, 0, 0, 0,\n    1, 86, 0, 0, 0, 0,\n    1, 87, 0, 0, 0, 0,\n    1, 88, 0, 0, 0, 0,\n    1, 89, 0, 0, 0, 0,\n    1, 90, 0, 0, 0, 0,\n    1, 91, 0, 0, 0, 0,\n    1, 92, 0, 0, 0, 0,\n    1, 93, 0, 0, 0, 0,\n    1, 94, 0, 0, 0, 0,\n    1, 95, 0, 0, 0, 0,\n    1, 96, 0, 0, 0, 0,\n    1, 97, 0, 0, 0, 0,\n    1, 98, 0, 0, 0, 0,\n    1, 99, 0, 0, 0, 0,\n    1, 100, 0, 0, 0, 0,\n    1, 101, 0, 0, 0, 0,\n    1, 102, 0, 0, 0, 0,\n    1, 103, 0, 0, 0, 0,\n    1, 104, 0, 0, 0, 0,\n    1, 105, 0, 0, 0, 0,\n    1, 106, 0, 0, 0, 0,\n    1, 107, 0, 0, 0, 0,\n    1, 108, 0, 0, 0, 0,\n    1, 109, 0, 0, 0, 0,\n    1, 110, 0, 0, 0, 0,\n    1, 111, 0, 0, 0, 0,\n    1, 112, 0, 0, 0, 0,\n    1, 113, 0, 0, 0, 0,\n    1, 114, 0, 0, 0, 0,\n    1, 115, 0, 0, 0, 0,\n    1, 116, 0, 0, 0, 0,\n    1, 117, 0, 0, 0, 0,\n    1, 118, 0, 0, 0, 0,\n    1, 119, 0, 0, 0, 0,\n    1, 120, 0, 0, 0, 0,\n    1, 121, 0, 0, 0, 0,\n    1, 122, 0, 0, 0, 0,\n    1, 123, 0, 0, 0, 0,\n    1, 124, 0, 0, 0, 0,\n    1, 125, 0, 0, 0, 0,\n    1, 126, 0, 0, 0, 0,\n    1, 127, 0, 0, 0, 0,\n    3, -30, -126, -84, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    3, -30, -128, -102, 0, 0,\n    2, -58, -110, 0, 0, 0,\n    3, -30, -128, -98, 0, 0,\n    3, -30, -128, -90, 0, 0,\n    3, -30, -128, -96, 0, 0,\n    3, -30, -128, -95, 0, 0,\n    2, -53, -122, 0, 0, 0,\n    3, -30, -128, -80, 0, 0,\n    2, -59, -96, 0, 0, 0,\n    3, -30, -128, -71, 0, 0,\n    2, -59, -110, 0, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    2, -59, -67, 0, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    3, -30, -128, -104, 0, 0,\n    3, -30, -128, -103, 0, 0,\n    3, -30, -128, -100, 0, 0,\n    3, -30, -128, -99, 0, 0,\n    3, -30, -128, -94, 0, 0,\n    3, -30, -128, -109, 0, 0,\n    3, -30, -128, -108, 0, 0,\n    2, -53, -100, 0, 0, 0,\n    3, -30, -124, -94, 0, 0,\n    2, -59, -95, 0, 0, 0,\n    3, -30, -128, -70, 0, 0,\n    2, -59, -109, 0, 0, 0,\n    1, 32, 0, 0, 0, 0, // not part of this charset\n    2, -59, -66, 0, 0, 0,\n    2, -59, -72, 0, 0, 0,\n    2, -62, -96, 0, 0, 0,\n    2, -62, -95, 0, 0, 0,\n    2, -62, -94, 0, 0, 0,\n    2, -62, -93, 0, 0, 0,\n    2, -62, -92, 0, 0, 0,\n    2, -62, -91, 0, 0, 0,\n    2, -62, -90, 0, 0, 0,\n    2, -62, -89, 0, 0, 0,\n    2, -62, -88, 0, 0, 0,\n    2, -62, -87, 0, 0, 0,\n    2, -62, -86, 0, 0, 0,\n    2, -62, -85, 0, 0, 0,\n    2, -62, -84, 0, 0, 0,\n    2, -62, -83, 0, 0, 0,\n    2, -62, -82, 0, 0, 0,\n    2, -62, -81, 0, 0, 0,\n    2, -62, -80, 0, 0, 0,\n    2, -62, -79, 0, 0, 0,\n    2, -62, -78, 0, 0, 0,\n    2, -62, -77, 0, 0, 0,\n    2, -62, -76, 0, 0, 0,\n    2, -62, -75, 0, 0, 0,\n    2, -62, -74, 0, 0, 0,\n    2, -62, -73, 0, 0, 0,\n    2, -62, -72, 0, 0, 0,\n    2, -62, -71, 0, 0, 0,\n    2, -62, -70, 0, 0, 0,\n    2, -62, -69, 0, 0, 0,\n    2, -62, -68, 0, 0, 0,\n    2, -62, -67, 0, 0, 0,\n    2, -62, -66, 0, 0, 0,\n    2, -62, -65, 0, 0, 0,\n    2, -61, -128, 0, 0, 0,\n    2, -61, -127, 0, 0, 0,\n    2, -61, -126, 0, 0, 0,\n    2, -61, -125, 0, 0, 0,\n    2, -61, -124, 0, 0, 0,\n    2, -61, -123, 0, 0, 0,\n    2, -61, -122, 0, 0, 0,\n    2, -61, -121, 0, 0, 0,\n    2, -61, -120, 0, 0, 0,\n    2, -61, -119, 0, 0, 0,\n    2, -61, -118, 0, 0, 0,\n    2, -61, -117, 0, 0, 0,\n    2, -61, -116, 0, 0, 0,\n    2, -61, -115, 0, 0, 0,\n    2, -61, -114, 0, 0, 0,\n    2, -61, -113, 0, 0, 0,\n    2, -61, -112, 0, 0, 0,\n    2, -61, -111, 0, 0, 0,\n    2, -61, -110, 0, 0, 0,\n    2, -61, -109, 0, 0, 0,\n    2, -61, -108, 0, 0, 0,\n    2, -61, -107, 0, 0, 0,\n    2, -61, -106, 0, 0, 0,\n    2, -61, -105, 0, 0, 0,\n    2, -61, -104, 0, 0, 0,\n    2, -61, -103, 0, 0, 0,\n    2, -61, -102, 0, 0, 0,\n    2, -61, -101, 0, 0, 0,\n    2, -61, -100, 0, 0, 0,\n    2, -61, -99, 0, 0, 0,\n    2, -61, -98, 0, 0, 0,\n    2, -61, -97, 0, 0, 0,\n    2, -61, -96, 0, 0, 0,\n    2, -61, -95, 0, 0, 0,\n    2, -61, -94, 0, 0, 0,\n    2, -61, -93, 0, 0, 0,\n    2, -61, -92, 0, 0, 0,\n    2, -61, -91, 0, 0, 0,\n    2, -61, -90, 0, 0, 0,\n    2, -61, -89, 0, 0, 0,\n    2, -61, -88, 0, 0, 0,\n    2, -61, -87, 0, 0, 0,\n    2, -61, -86, 0, 0, 0,\n    2, -61, -85, 0, 0, 0,\n    2, -61, -84, 0, 0, 0,\n    2, -61, -83, 0, 0, 0,\n    2, -61, -82, 0, 0, 0,\n    2, -61, -81, 0, 0, 0,\n    2, -61, -80, 0, 0, 0,\n    2, -61, -79, 0, 0, 0,\n    2, -61, -78, 0, 0, 0,\n    2, -61, -77, 0, 0, 0,\n    2, -61, -76, 0, 0, 0,\n    2, -61, -75, 0, 0, 0,\n    2, -61, -74, 0, 0, 0,\n    2, -61, -73, 0, 0, 0,\n    2, -61, -72, 0, 0, 0,\n    2, -61, -71, 0, 0, 0,\n    2, -61, -70, 0, 0, 0,\n    2, -61, -69, 0, 0, 0,\n    2, -61, -68, 0, 0, 0,\n    2, -61, -67, 0, 0, 0,\n    2, -61, -66, 0, 0, 0,\n    2, -61, -65, 0, 0, 0\n};\nstatic signed char cp437[] =\n{\n    1, 0, 0, 0, 0, 0,\n    1, 1, 0, 0, 0, 0,\n    1, 2, 0, 0, 0, 0,\n    1, 3, 0, 0, 0, 0,\n    1, 4, 0, 0, 0, 0,\n    1, 5, 0, 0, 0, 0,\n    1, 6, 0, 0, 0, 0,\n    1, 7, 0, 0, 0, 0,\n    1, 8, 0, 0, 0, 0,\n    1, 9, 0, 0, 0, 0,\n    1, 10, 0, 0, 0, 0,\n    1, 11, 0, 0, 0, 0,\n    1, 12, 0, 0, 0, 0,\n    1, 13, 0, 0, 0, 0,\n    1, 14, 0, 0, 0, 0,\n    1, 15, 0, 0, 0, 0,\n    1, 16, 0, 0, 0, 0,\n    1, 17, 0, 0, 0, 0,\n    1, 18, 0, 0, 0, 0,\n    1, 19, 0, 0, 0, 0,\n    1, 20, 0, 0, 0, 0,\n    1, 21, 0, 0, 0, 0,\n    1, 22, 0, 0, 0, 0,\n    1, 23, 0, 0, 0, 0,\n    1, 24, 0, 0, 0, 0,\n    1, 25, 0, 0, 0, 0,\n    1, 26, 0, 0, 0, 0,\n    1, 27, 0, 0, 0, 0,\n    1, 28, 0, 0, 0, 0,\n    1, 29, 0, 0, 0, 0,\n    1, 30, 0, 0, 0, 0,\n    1, 31, 0, 0, 0, 0,\n    1, 32, 0, 0, 0, 0,\n    1, 33, 0, 0, 0, 0,\n    1, 34, 0, 0, 0, 0,\n    1, 35, 0, 0, 0, 0,\n    1, 36, 0, 0, 0, 0,\n    1, 37, 0, 0, 0, 0,\n    1, 38, 0, 0, 0, 0,\n    1, 39, 0, 0, 0, 0,\n    1, 40, 0, 0, 0, 0,\n    1, 41, 0, 0, 0, 0,\n    1, 42, 0, 0, 0, 0,\n    1, 43, 0, 0, 0, 0,\n    1, 44, 0, 0, 0, 0,\n    1, 45, 0, 0, 0, 0,\n    1, 46, 0, 0, 0, 0,\n    1, 47, 0, 0, 0, 0,\n    1, 48, 0, 0, 0, 0,\n    1, 49, 0, 0, 0, 0,\n    1, 50, 0, 0, 0, 0,\n    1, 51, 0, 0, 0, 0,\n    1, 52, 0, 0, 0, 0,\n    1, 53, 0, 0, 0, 0,\n    1, 54, 0, 0, 0, 0,\n    1, 55, 0, 0, 0, 0,\n    1, 56, 0, 0, 0, 0,\n    1, 57, 0, 0, 0, 0,\n    1, 58, 0, 0, 0, 0,\n    1, 59, 0, 0, 0, 0,\n    1, 60, 0, 0, 0, 0,\n    1, 61, 0, 0, 0, 0,\n    1, 62, 0, 0, 0, 0,\n    1, 63, 0, 0, 0, 0,\n    1, 64, 0, 0, 0, 0,\n    1, 65, 0, 0, 0, 0,\n    1, 66, 0, 0, 0, 0,\n    1, 67, 0, 0, 0, 0,\n    1, 68, 0, 0, 0, 0,\n    1, 69, 0, 0, 0, 0,\n    1, 70, 0, 0, 0, 0,\n    1, 71, 0, 0, 0, 0,\n    1, 72, 0, 0, 0, 0,\n    1, 73, 0, 0, 0, 0,\n    1, 74, 0, 0, 0, 0,\n    1, 75, 0, 0, 0, 0,\n    1, 76, 0, 0, 0, 0,\n    1, 77, 0, 0, 0, 0,\n    1, 78, 0, 0, 0, 0,\n    1, 79, 0, 0, 0, 0,\n    1, 80, 0, 0, 0, 0,\n    1, 81, 0, 0, 0, 0,\n    1, 82, 0, 0, 0, 0,\n    1, 83, 0, 0, 0, 0,\n    1, 84, 0, 0, 0, 0,\n    1, 85, 0, 0, 0, 0,\n    1, 86, 0, 0, 0, 0,\n    1, 87, 0, 0, 0, 0,\n    1, 88, 0, 0, 0, 0,\n    1, 89, 0, 0, 0, 0,\n    1, 90, 0, 0, 0, 0,\n    1, 91, 0, 0, 0, 0,\n    1, 92, 0, 0, 0, 0,\n    1, 93, 0, 0, 0, 0,\n    1, 94, 0, 0, 0, 0,\n    1, 95, 0, 0, 0, 0,\n    1, 96, 0, 0, 0, 0,\n    1, 97, 0, 0, 0, 0,\n    1, 98, 0, 0, 0, 0,\n    1, 99, 0, 0, 0, 0,\n    1, 100, 0, 0, 0, 0,\n    1, 101, 0, 0, 0, 0,\n    1, 102, 0, 0, 0, 0,\n    1, 103, 0, 0, 0, 0,\n    1, 104, 0, 0, 0, 0,\n    1, 105, 0, 0, 0, 0,\n    1, 106, 0, 0, 0, 0,\n    1, 107, 0, 0, 0, 0,\n    1, 108, 0, 0, 0, 0,\n    1, 109, 0, 0, 0, 0,\n    1, 110, 0, 0, 0, 0,\n    1, 111, 0, 0, 0, 0,\n    1, 112, 0, 0, 0, 0,\n    1, 113, 0, 0, 0, 0,\n    1, 114, 0, 0, 0, 0,\n    1, 115, 0, 0, 0, 0,\n    1, 116, 0, 0, 0, 0,\n    1, 117, 0, 0, 0, 0,\n    1, 118, 0, 0, 0, 0,\n    1, 119, 0, 0, 0, 0,\n    1, 120, 0, 0, 0, 0,\n    1, 121, 0, 0, 0, 0,\n    1, 122, 0, 0, 0, 0,\n    1, 123, 0, 0, 0, 0,\n    1, 124, 0, 0, 0, 0,\n    1, 125, 0, 0, 0, 0,\n    1, 126, 0, 0, 0, 0,\n    1, 127, 0, 0, 0, 0,\n    2, -61, -121, 0, 0, 0,\n    2, -61, -68, 0, 0, 0,\n    2, -61, -87, 0, 0, 0,\n    2, -61, -94, 0, 0, 0,\n    2, -61, -92, 0, 0, 0,\n    2, -61, -96, 0, 0, 0,\n    2, -61, -91, 0, 0, 0,\n    2, -61, -89, 0, 0, 0,\n    2, -61, -86, 0, 0, 0,\n    2, -61, -85, 0, 0, 0,\n    2, -61, -88, 0, 0, 0,\n    2, -61, -81, 0, 0, 0,\n    2, -61, -82, 0, 0, 0,\n    2, -61, -84, 0, 0, 0,\n    2, -61, -124, 0, 0, 0,\n    2, -61, -123, 0, 0, 0,\n    2, -61, -119, 0, 0, 0,\n    2, -61, -90, 0, 0, 0,\n    2, -61, -122, 0, 0, 0,\n    2, -61, -76, 0, 0, 0,\n    2, -61, -74, 0, 0, 0,\n    2, -61, -78, 0, 0, 0,\n    2, -61, -69, 0, 0, 0,\n    2, -61, -71, 0, 0, 0,\n    2, -61, -65, 0, 0, 0,\n    2, -61, -106, 0, 0, 0,\n    2, -61, -100, 0, 0, 0,\n    2, -62, -94, 0, 0, 0,\n    2, -62, -93, 0, 0, 0,\n    2, -62, -91, 0, 0, 0,\n    3, -30, -126, -89, 0, 0,\n    2, -58, -110, 0, 0, 0,\n    2, -61, -95, 0, 0, 0,\n    2, -61, -83, 0, 0, 0,\n    2, -61, -77, 0, 0, 0,\n    2, -61, -70, 0, 0, 0,\n    2, -61, -79, 0, 0, 0,\n    2, -61, -111, 0, 0, 0,\n    2, -62, -86, 0, 0, 0,\n    2, -62, -70, 0, 0, 0,\n    2, -62, -65, 0, 0, 0,\n    3, -30, -116, -112, 0, 0,\n    2, -62, -84, 0, 0, 0,\n    2, -62, -67, 0, 0, 0,\n    2, -62, -68, 0, 0, 0,\n    2, -62, -95, 0, 0, 0,\n    2, -62, -85, 0, 0, 0,\n    2, -62, -69, 0, 0, 0,\n    3, -30, -106, -111, 0, 0,\n    3, -30, -106, -110, 0, 0,\n    3, -30, -106, -109, 0, 0,\n    3, -30, -108, -126, 0, 0,\n    3, -30, -108, -92, 0, 0,\n    3, -30, -107, -95, 0, 0,\n    3, -30, -107, -94, 0, 0,\n    3, -30, -107, -106, 0, 0,\n    3, -30, -107, -107, 0, 0,\n    3, -30, -107, -93, 0, 0,\n    3, -30, -107, -111, 0, 0,\n    3, -30, -107, -105, 0, 0,\n    3, -30, -107, -99, 0, 0,\n    3, -30, -107, -100, 0, 0,\n    3, -30, -107, -101, 0, 0,\n    3, -30, -108, -112, 0, 0,\n    3, -30, -108, -108, 0, 0,\n    3, -30, -108, -76, 0, 0,\n    3, -30, -108, -84, 0, 0,\n    3, -30, -108, -100, 0, 0,\n    3, -30, -108, -128, 0, 0,\n    3, -30, -108, -68, 0, 0,\n    3, -30, -107, -98, 0, 0,\n    3, -30, -107, -97, 0, 0,\n    3, -30, -107, -102, 0, 0,\n    3, -30, -107, -108, 0, 0,\n    3, -30, -107, -87, 0, 0,\n    3, -30, -107, -90, 0, 0,\n    3, -30, -107, -96, 0, 0,\n    3, -30, -107, -112, 0, 0,\n    3, -30, -107, -84, 0, 0,\n    3, -30, -107, -89, 0, 0,\n    3, -30, -107, -88, 0, 0,\n    3, -30, -107, -92, 0, 0,\n    3, -30, -107, -91, 0, 0,\n    3, -30, -107, -103, 0, 0,\n    3, -30, -107, -104, 0, 0,\n    3, -30, -107, -110, 0, 0,\n    3, -30, -107, -109, 0, 0,\n    3, -30, -107, -85, 0, 0,\n    3, -30, -107, -86, 0, 0,\n    3, -30, -108, -104, 0, 0,\n    3, -30, -108, -116, 0, 0,\n    3, -30, -106, -120, 0, 0,\n    3, -30, -106, -124, 0, 0,\n    3, -30, -106, -116, 0, 0,\n    3, -30, -106, -112, 0, 0,\n    3, -30, -106, -128, 0, 0,\n    2, -50, -79, 0, 0, 0,\n    2, -61, -97, 0, 0, 0,\n    2, -50, -109, 0, 0, 0,\n    2, -49, -128, 0, 0, 0,\n    2, -50, -93, 0, 0, 0,\n    2, -49, -125, 0, 0, 0,\n    2, -62, -75, 0, 0, 0,\n    2, -49, -124, 0, 0, 0,\n    2, -50, -90, 0, 0, 0,\n    2, -50, -104, 0, 0, 0,\n    2, -50, -87, 0, 0, 0,\n    2, -50, -76, 0, 0, 0,\n    3, -30, -120, -98, 0, 0,\n    2, -49, -122, 0, 0, 0,\n    2, -50, -75, 0, 0, 0,\n    3, -30, -120, -87, 0, 0,\n    3, -30, -119, -95, 0, 0,\n    2, -62, -79, 0, 0, 0,\n    3, -30, -119, -91, 0, 0,\n    3, -30, -119, -92, 0, 0,\n    3, -30, -116, -96, 0, 0,\n    3, -30, -116, -95, 0, 0,\n    2, -61, -73, 0, 0, 0,\n    3, -30, -119, -120, 0, 0,\n    2, -62, -80, 0, 0, 0,\n    3, -30, -120, -103, 0, 0,\n    2, -62, -73, 0, 0, 0,\n    3, -30, -120, -102, 0, 0,\n    3, -30, -127, -65, 0, 0,\n    2, -62, -78, 0, 0, 0,\n    3, -30, -106, -96, 0, 0,\n    2, -62, -96, 0, 0, 0\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "components/to_utf8/tests/.gitignore",
    "content": "*_test\n"
  },
  {
    "path": "components/to_utf8/tests/output/to_utf8_test.out",
    "content": "original:  Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам?\nconverted: Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам?\noriginal:  Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger.\nconverted: Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger.\n"
  },
  {
    "path": "components/to_utf8/tests/test.sh",
    "content": "#!/bin/bash\n\nmake || exit\n\nmkdir -p output\n\nPROGS=*_test\n\nfor a in $PROGS; do\n    if [ -f \"output/$a.out\" ]; then\n        echo \"Running $a:\"\n        ./$a | diff output/$a.out -\n    else\n        echo \"Creating $a.out\"\n        ./$a > \"output/$a.out\"\n        git add \"output/$a.out\"\n    fi\ndone\n"
  },
  {
    "path": "components/to_utf8/tests/test_data/french-utf8.txt",
    "content": "Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger."
  },
  {
    "path": "components/to_utf8/tests/test_data/french-win1252.txt",
    "content": "Vous lui donnez le gteau sans protester avant daller chercher tous vos amis et de revenir vous venger."
  },
  {
    "path": "components/to_utf8/tests/test_data/russian-utf8.txt",
    "content": "Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам?"
  },
  {
    "path": "components/to_utf8/tests/test_data/russian-win1251.txt",
    "content": "    , ,               ?"
  },
  {
    "path": "components/to_utf8/tests/to_utf8_test.cpp",
    "content": "#include <iostream>\n#include <fstream>\n#include <cassert>\n#include <stdexcept>\n\n#include \"../to_utf8.hpp\"\n\nstd::string getFirstLine(const std::string &filename);\nvoid testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile,\n                 const std::string &utf8File);\n\n/// Test character encoding conversion to and from UTF-8\nvoid testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile,\n                 const std::string &utf8File)\n{\n    // get some test data\n    std::string legacyEncLine = getFirstLine(legacyEncFile);\n    std::string utf8Line = getFirstLine(utf8File);\n\n    // create an encoder for specified character encoding\n    ToUTF8::Utf8Encoder encoder (encoding);\n\n    // convert text to UTF-8\n    std::string convertedUtf8Line = encoder.getUtf8(legacyEncLine);\n\n    std::cout << \"original:  \" << utf8Line          << std::endl;\n    std::cout << \"converted: \" << convertedUtf8Line << std::endl;\n\n    // check correctness\n    assert(convertedUtf8Line == utf8Line);\n\n    // convert UTF-8 text to legacy encoding\n    std::string convertedLegacyEncLine = encoder.getLegacyEnc(utf8Line);\n    // check correctness\n    assert(convertedLegacyEncLine == legacyEncLine);\n}\n\nstd::string getFirstLine(const std::string &filename)\n{\n    std::string line;\n    std::ifstream text (filename.c_str());\n\n    if (!text.is_open())\n    {\n        throw std::runtime_error(\"Unable to open file \" + filename);\n    }\n\n    std::getline(text, line);\n    text.close();\n\n    return line;\n}\n\nint main()\n{\n    testEncoder(ToUTF8::WINDOWS_1251, \"test_data/russian-win1251.txt\", \"test_data/russian-utf8.txt\");\n    testEncoder(ToUTF8::WINDOWS_1252, \"test_data/french-win1252.txt\", \"test_data/french-utf8.txt\");\n    return 0;\n}\n"
  },
  {
    "path": "components/to_utf8/to_utf8.cpp",
    "content": "#include \"to_utf8.hpp\"\n\n#include <vector>\n#include <cassert>\n#include <stdexcept>\n\n#include <components/debug/debuglog.hpp>\n\n/* This file contains the code to translate from WINDOWS-1252 (native\n   charset used in English version of Morrowind) to UTF-8. The library\n   is designed to be extened to support more source encodings later,\n   which means that we may add support for Russian, Polish and Chinese\n   files and so on.\n\n   The code does not depend on any external library at\n   runtime. Instead, it uses a pregenerated table made with iconv (see\n   gen_iconv.cpp and the Makefile) which is located in tables_gen.hpp.\n\n   This is both faster and uses less dependencies. The tables would\n   only need to be regenerated if we are adding support more input\n   encodings. As such, there is no need to make the generator code\n   platform independent.\n\n   The library is optimized for the case of pure ASCII input strings,\n   which is the vast majority of cases at least for the English\n   version. A test of my version of Morrowind.esm got 130 non-ASCII vs\n   236195 ASCII strings, or less than 0.06% of strings containing\n   non-ASCII characters.\n\n   To optmize for this, ff the first pass of the string does not find\n   any non-ASCII characters, the entire string is passed along without\n   any modification.\n\n   Most of the non-ASCII strings are books, and are quite large. (The\n   non-ASCII characters are typically starting and ending quotation\n   marks.) Within these, almost all the characters are ASCII. For this\n   purpose, the library is also optimized for mostly-ASCII contents\n   even in the cases where some conversion is necessary.\n */\n\n\n// Generated tables\n#include \"tables_gen.hpp\"\n\nusing namespace ToUTF8;\n\nUtf8Encoder::Utf8Encoder(const FromType sourceEncoding):\n    mOutput(50*1024)\n{\n    switch (sourceEncoding)\n    {\n        case ToUTF8::WINDOWS_1252:\n        {\n            translationArray = ToUTF8::windows_1252;\n            break;\n        }\n        case ToUTF8::WINDOWS_1250:\n        {\n            translationArray = ToUTF8::windows_1250;\n            break;\n        }\n        case ToUTF8::WINDOWS_1251:\n        {\n            translationArray = ToUTF8::windows_1251;\n            break;\n        }\n        case ToUTF8::CP437:\n        {\n            translationArray = ToUTF8::cp437;\n            break;\n        }\n\n        default:\n        {\n            assert(0);\n        }\n    }\n}\n\nstd::string Utf8Encoder::getUtf8(const char* input, size_t size)\n{\n    // Double check that the input string stops at some point (it might\n    // contain zero terminators before this, inside its own data, which\n    // is also ok.)\n    assert(input[size] == 0);\n\n    // Note: The rest of this function is designed for single-character\n    // input encodings only. It also assumes that the input encoding\n    // shares its first 128 values (0-127) with ASCII. There are no plans\n    // to add more encodings to this module (we are using utf8 for new\n    // content files), so that shouldn't be an issue.\n\n    // Compute output length, and check for pure ascii input at the same\n    // time.\n    bool ascii;\n    size_t outlen = getLength(input, ascii);\n\n    // If we're pure ascii, then don't bother converting anything.\n    if(ascii)\n        return std::string(input, outlen);\n\n    // Make sure the output is large enough\n    resize(outlen);\n    char *out = &mOutput[0];\n\n    // Translate\n    while (*input)\n        copyFromArray(*(input++), out);\n\n    // Make sure that we wrote the correct number of bytes\n    assert((out-&mOutput[0]) == (int)outlen);\n\n    // And make extra sure the output is null terminated\n    assert(mOutput.size() > outlen);\n    assert(mOutput[outlen] == 0);\n\n    // Return a string\n    return std::string(&mOutput[0], outlen);\n}\n\nstd::string Utf8Encoder::getLegacyEnc(const char *input, size_t size)\n{\n    // Double check that the input string stops at some point (it might\n    // contain zero terminators before this, inside its own data, which\n    // is also ok.)\n    assert(input[size] == 0);\n\n    // TODO: The rest of this function is designed for single-character\n    // input encodings only. It also assumes that the input the input\n    // encoding shares its first 128 values (0-127) with ASCII. These\n    // conditions must be checked again if you add more input encodings\n    // later.\n\n    // Compute output length, and check for pure ascii input at the same\n    // time.\n    bool ascii;\n    size_t outlen = getLength2(input, ascii);\n\n    // If we're pure ascii, then don't bother converting anything.\n    if(ascii)\n        return std::string(input, outlen);\n\n    // Make sure the output is large enough\n    resize(outlen);\n    char *out = &mOutput[0];\n\n    // Translate\n    while(*input)\n        copyFromArray2(input, out);\n\n    // Make sure that we wrote the correct number of bytes\n    assert((out-&mOutput[0]) == (int)outlen);\n\n    // And make extra sure the output is null terminated\n    assert(mOutput.size() > outlen);\n    assert(mOutput[outlen] == 0);\n\n    // Return a string\n    return std::string(&mOutput[0], outlen);\n}\n\n// Make sure the output vector is large enough for 'size' bytes,\n// including a terminating zero after it.\nvoid Utf8Encoder::resize(size_t size)\n{\n    if (mOutput.size() <= size)\n        // Add some extra padding to reduce the chance of having to resize\n        // again later.\n        mOutput.resize(3*size);\n\n    // And make sure the string is zero terminated\n    mOutput[size] = 0;\n}\n\n/** Get the total length length needed to decode the given string with\n  the given translation array. The arrays are encoded with 6 bytes\n  per character, with the first giving the length and the next 5 the\n  actual data.\n\n  The function serves a dual purpose for optimization reasons: it\n  checks if the input is pure ascii (all values are <= 127). If this\n  is the case, then the ascii parameter is set to true, and the\n  caller can optimize for this case.\n */\nsize_t Utf8Encoder::getLength(const char* input, bool &ascii)\n{\n    ascii = true;\n    size_t len = 0;\n    const char* ptr = input;\n    unsigned char inp = *ptr;\n\n    // Do away with the ascii part of the string first (this is almost\n    // always the entire string.)\n    while (inp && inp < 128)\n        inp = *(++ptr);\n    len += (ptr-input);\n\n    // If we're not at the null terminator at this point, then there\n    // were some non-ascii characters to deal with. Go to slow-mode for\n    // the rest of the string.\n    if (inp)\n    {\n        ascii = false;\n        while (inp)\n        {\n            // Find the translated length of this character in the\n            // lookup table.\n            len += translationArray[inp*6];\n            inp = *(++ptr);\n        }\n    }\n    return len;\n}\n\n// Translate one character 'ch' using the translation array 'arr', and\n// advance the output pointer accordingly.\nvoid Utf8Encoder::copyFromArray(unsigned char ch, char* &out)\n{\n    // Optimize for ASCII values\n    if (ch < 128)\n    {\n        *(out++) = ch;\n        return;\n    }\n\n    const signed char *in = translationArray + ch*6;\n    int len = *(in++);\n    for (int i=0; i<len; i++)\n        *(out++) = *(in++);\n}\n\nsize_t Utf8Encoder::getLength2(const char* input, bool &ascii)\n{\n    ascii = true;\n    size_t len = 0;\n    const char* ptr = input;\n    unsigned char inp = *ptr;\n\n    // Do away with the ascii part of the string first (this is almost\n    // always the entire string.)\n    while (inp && inp < 128)\n        inp = *(++ptr);\n    len += (ptr-input);\n\n    // If we're not at the null terminator at this point, then there\n    // were some non-ascii characters to deal with. Go to slow-mode for\n    // the rest of the string.\n    if (inp)\n    {\n        ascii = false;\n        while(inp)\n        {\n            len += 1;\n            // Find the translated length of this character in the\n            // lookup table.\n            switch(inp)\n            {\n                case 0xe2: len -= 2; break;\n                case 0xc2:\n                case 0xcb:\n                case 0xc4:\n                case 0xc6:\n                case 0xc3:\n                case 0xd0:\n                case 0xd1:\n                case 0xd2:\n                case 0xc5: len -= 1; break;\n            }\n\n            inp = *(++ptr);\n        }\n    }\n    return len;\n}\n\nvoid Utf8Encoder::copyFromArray2(const char*& chp, char* &out)\n{\n    unsigned char ch = *(chp++);\n    // Optimize for ASCII values\n    if (ch < 128)\n    {\n        *(out++) = ch;\n        return;\n    }\n\n    int len = 1;\n    switch (ch)\n    {\n        case 0xe2: len = 3; break;\n        case 0xc2:\n        case 0xcb:\n        case 0xc4:\n        case 0xc6:\n        case 0xc3:\n        case 0xd0:\n        case 0xd1:\n        case 0xd2:\n        case 0xc5: len = 2; break;\n    }\n\n    if (len == 1) // There is no 1 length utf-8 glyph that is not 0x20 (empty space)\n    {\n        *(out++) = ch;\n        return;\n    }\n\n    unsigned char ch2 = *(chp++);\n    unsigned char ch3 = '\\0';\n    if (len == 3)\n        ch3 = *(chp++);\n\n    for (int i = 128; i < 256; i++)\n    {\n        unsigned char b1 = translationArray[i*6 + 1], b2 = translationArray[i*6 + 2], b3 = translationArray[i*6 + 3];\n        if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3))\n        {\n            *(out++) = (char)i;\n            return;\n        }\n    }\n\n    Log(Debug::Info) << \"Could not find glyph \" << std::hex << (int)ch << \" \" << (int)ch2 << \" \" << (int)ch3;\n\n    *(out++) = ch; // Could not find glyph, just put whatever\n}\n\nToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName)\n{\n    if (encodingName == \"win1250\")\n        return ToUTF8::WINDOWS_1250;\n    else if (encodingName == \"win1251\")\n        return ToUTF8::WINDOWS_1251;\n    else if (encodingName == \"win1252\")\n        return ToUTF8::WINDOWS_1252;\n    else\n        throw std::runtime_error(std::string(\"Unknown encoding '\") + encodingName + std::string(\"', see openmw --help for available options.\"));\n}\n\nstd::string ToUTF8::encodingUsingMessage(const std::string& encodingName)\n{\n    if (encodingName == \"win1250\")\n        return \"Using Central and Eastern European font encoding.\";\n    else if (encodingName == \"win1251\")\n        return \"Using Cyrillic font encoding.\";\n    else if (encodingName == \"win1252\")\n        return \"Using default (English) font encoding.\";\n    else\n        throw std::runtime_error(std::string(\"Unknown encoding '\") + encodingName + std::string(\"', see openmw --help for available options.\"));\n}\n"
  },
  {
    "path": "components/to_utf8/to_utf8.hpp",
    "content": "#ifndef COMPONENTS_TOUTF8_H\n#define COMPONENTS_TOUTF8_H\n\n#include <string>\n#include <cstring>\n#include <vector>\n\nnamespace ToUTF8\n{\n    // These are all the currently supported code pages\n    enum FromType\n    {\n        WINDOWS_1250,      // Central ane Eastern European languages\n        WINDOWS_1251,      // Cyrillic languages\n        WINDOWS_1252,       // Used by English version of Morrowind (and\n            // probably others)\n        CP437           // Used for fonts (*.fnt) if data files encoding is 1252. Otherwise, uses the same encoding as the data files.\n    };\n\n    FromType calculateEncoding(const std::string& encodingName);\n    std::string encodingUsingMessage(const std::string& encodingName);\n\n    // class\n\n    class Utf8Encoder\n    {\n        public:\n            Utf8Encoder(FromType sourceEncoding);\n\n            // Convert to UTF8 from the previously given code page.\n            std::string getUtf8(const char *input, size_t size);\n            inline std::string getUtf8(const std::string &str)\n            {\n                return getUtf8(str.c_str(), str.size());\n            }\n\n            std::string getLegacyEnc(const char *input, size_t size);\n            inline std::string getLegacyEnc(const std::string &str)\n            {\n                return getLegacyEnc(str.c_str(), str.size());\n            }\n\n        private:\n            void resize(size_t size);\n            size_t getLength(const char* input, bool &ascii);\n            void copyFromArray(unsigned char chp, char* &out);\n            size_t getLength2(const char* input, bool &ascii);\n            void copyFromArray2(const char*& chp, char* &out);\n\n            std::vector<char> mOutput;\n            signed char* translationArray;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/translation/translation.cpp",
    "content": "#include \"translation.hpp\"\n\n#include <boost/filesystem/fstream.hpp>\n\nnamespace Translation\n{\n    Storage::Storage()\n        : mEncoder(nullptr)\n    {\n    }\n\n    void Storage::loadTranslationData(const Files::Collections& dataFileCollections,\n                                      const std::string& esmFileName)\n    {\n        std::string esmNameNoExtension(Misc::StringUtils::lowerCase(esmFileName));\n        //changing the extension\n        size_t dotPos = esmNameNoExtension.rfind('.');\n        if (dotPos != std::string::npos)\n            esmNameNoExtension.resize(dotPos);\n\n        loadData(mCellNamesTranslations, esmNameNoExtension, \".cel\", dataFileCollections);\n        loadData(mPhraseForms, esmNameNoExtension, \".top\", dataFileCollections);\n        loadData(mTopicIDs, esmNameNoExtension, \".mrk\", dataFileCollections);\n    }\n\n    void Storage::loadData(ContainerType& container,\n                           const std::string& fileNameNoExtension,\n                           const std::string& extension,\n                           const Files::Collections& dataFileCollections)\n    {\n        std::string fileName = fileNameNoExtension + extension;\n\n        if (dataFileCollections.getCollection (extension).doesExist (fileName))\n        {\n            boost::filesystem::ifstream stream (\n                dataFileCollections.getCollection (extension).getPath (fileName));\n\n            if (!stream.is_open())\n                throw std::runtime_error (\"failed to open translation file: \" + fileName);\n\n            loadDataFromStream(container, stream);\n        }\n    }\n\n    void Storage::loadDataFromStream(ContainerType& container, std::istream& stream)\n    {\n        std::string line;\n        while (!stream.eof() && !stream.fail())\n        {\n            std::getline( stream, line );\n            if (!line.empty() && *line.rbegin() == '\\r')\n              line.resize(line.size() - 1);\n\n            if (!line.empty())\n            {\n                line = mEncoder->getUtf8(line);\n\n                size_t tab_pos = line.find('\\t');\n                if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1)\n                {\n                    std::string key = line.substr(0, tab_pos);\n                    std::string value = line.substr(tab_pos + 1);\n\n                    if (!key.empty() && !value.empty())\n                        container.insert(std::make_pair(key, value));\n                }\n            }\n        }\n    }\n\n    std::string Storage::translateCellName(const std::string& cellName) const\n    {\n        std::map<std::string, std::string>::const_iterator entry =\n            mCellNamesTranslations.find(cellName);\n\n        if (entry == mCellNamesTranslations.end())\n            return cellName;\n\n        return entry->second;\n    }\n\n    std::string Storage::topicID(const std::string& phrase) const\n    {\n        std::string result = topicStandardForm(phrase);\n\n        //seeking for the topic ID\n        std::map<std::string, std::string>::const_iterator topicIDIterator =\n            mTopicIDs.find(result);\n\n        if (topicIDIterator != mTopicIDs.end())\n            result = topicIDIterator->second;\n\n        return result;\n    }\n\n    std::string Storage::topicStandardForm(const std::string& phrase) const\n    {\n        std::map<std::string, std::string>::const_iterator phraseFormsIterator =\n            mPhraseForms.find(phrase);\n\n        if (phraseFormsIterator != mPhraseForms.end())\n            return phraseFormsIterator->second;\n        else\n            return phrase;\n    }\n\n    void Storage::setEncoder(ToUTF8::Utf8Encoder* encoder)\n    {\n        mEncoder = encoder;\n    }\n\n    bool Storage::hasTranslation() const\n    {\n        return !mCellNamesTranslations.empty() ||\n               !mTopicIDs.empty() ||\n               !mPhraseForms.empty();\n    }\n\n    /*\n        Start of tes3mp addition\n\n        Get the localized version of an English topic ID\n    */\n    std::string Storage::getLocalizedTopicId(const std::string& englishTopicId) const\n    {\n        for (std::map<std::string, std::string>::const_iterator it = mTopicIDs.begin(); it != mTopicIDs.end(); ++it)\n        {\n            if (Misc::StringUtils::ciEqual(englishTopicId, it->second))\n                return it->first;\n        }\n\n        return \"\";\n    }\n    /*\n        End of tes3mp addition\n    */\n}\n"
  },
  {
    "path": "components/translation/translation.hpp",
    "content": "#ifndef COMPONENTS_TRANSLATION_DATA_H\n#define COMPONENTS_TRANSLATION_DATA_H\n\n#include <components/to_utf8/to_utf8.hpp>\n#include <components/files/collections.hpp>\n\nnamespace Translation\n{\n    class Storage\n    {\n    public:\n        Storage();\n\n        void loadTranslationData(const Files::Collections& dataFileCollections,\n                                 const std::string& esmFileName);\n\n        std::string translateCellName(const std::string& cellName) const;\n        std::string topicID(const std::string& phrase) const;\n\n        // Standard form usually means nominative case\n        std::string topicStandardForm(const std::string& phrase) const;\n\n        void setEncoder(ToUTF8::Utf8Encoder* encoder);\n\n        bool hasTranslation() const;\n\n        /*\n            Start of tes3mp addition\n\n            Get the localized version of an English topic ID\n        */\n        std::string getLocalizedTopicId(const std::string& englishTopicId) const;\n        /*\n            End of tes3mp addition\n        */\n\n    private:\n        typedef std::map<std::string, std::string> ContainerType;\n\n        void loadData(ContainerType& container,\n                      const std::string& fileNameNoExtension,\n                      const std::string& extension,\n                      const Files::Collections& dataFileCollections);\n\n        void loadDataFromStream(ContainerType& container, std::istream& stream);\n\n\n        ToUTF8::Utf8Encoder* mEncoder;\n        ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/version/version.cpp",
    "content": "#include \"version.hpp\"\n\n#include <boost/filesystem/path.hpp>\n#include <boost/filesystem/fstream.hpp>\n\nnamespace Version\n{\n\nVersion getOpenmwVersion(const std::string &resourcePath)\n{\n    boost::filesystem::path path (resourcePath + \"/version\");\n\n    boost::filesystem::ifstream stream (path);\n\n    Version v;\n    std::getline(stream, v.mVersion);\n    std::getline(stream, v.mCommitHash);\n    std::getline(stream, v.mTagHash);\n    return v;\n}\n\nstd::string Version::describe()\n{\n    std::string str = \"OpenMW version \" + mVersion;\n    std::string rev = mCommitHash;\n    if (!rev.empty())\n    {\n        rev = rev.substr(0, 10);\n        str += \"\\nRevision: \" + rev;\n    }\n    return str;\n}\n\nstd::string getOpenmwVersionDescription(const std::string &resourcePath)\n{\n    Version v = getOpenmwVersion(resourcePath);\n    return v.describe();\n}\n\n}\n"
  },
  {
    "path": "components/version/version.hpp",
    "content": "#ifndef VERSION_HPP\n#define VERSION_HPP\n\n#include <string>\n\nnamespace Version\n{\n\n    struct Version\n    {\n        std::string mVersion;\n        std::string mCommitHash;\n        std::string mTagHash;\n\n        std::string describe();\n    };\n\n    /// Read OpenMW version from the version file located in resourcePath.\n    Version getOpenmwVersion(const std::string& resourcePath);\n\n    /// Helper function to getOpenmwVersion and describe() it\n    std::string getOpenmwVersionDescription(const std::string& resourcePath);\n\n}\n\n\n#endif // VERSION_HPP\n\n"
  },
  {
    "path": "components/vfs/archive.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_ARCHIVE_H\n#define OPENMW_COMPONENTS_RESOURCE_ARCHIVE_H\n\n#include <map>\n\n#include <components/files/constrainedfilestream.hpp>\n\nnamespace VFS\n{\n\n    class File\n    {\n    public:\n        virtual ~File() {}\n\n        virtual Files::IStreamPtr open() = 0;\n    };\n\n    class Archive\n    {\n    public:\n        virtual ~Archive() {}\n\n        /// List all resources contained in this archive, and run the resource names through the given normalize function.\n        virtual void listResources(std::map<std::string, File*>& out, char (*normalize_function) (char)) = 0;\n\n        /// True if this archive contains the provided normalized file.\n        virtual bool contains(const std::string& file, char (*normalize_function) (char)) const = 0;\n\n        virtual std::string getDescription() const = 0;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/vfs/bsaarchive.cpp",
    "content": "#include \"bsaarchive.hpp\"\n#include <components/bsa/compressedbsafile.hpp>\n#include <memory>\n\nnamespace VFS\n{\n\nBsaArchive::BsaArchive(const std::string &filename)\n{\n    Bsa::BsaVersion bsaVersion = Bsa::CompressedBSAFile::detectVersion(filename);\n\n    if (bsaVersion == Bsa::BSAVER_COMPRESSED) {\n        mFile = std::make_unique<Bsa::CompressedBSAFile>(Bsa::CompressedBSAFile());\n    }\n    else {\n        mFile = std::make_unique<Bsa::BSAFile>(Bsa::BSAFile());\n    }\n\n    mFile->open(filename);\n\n    const Bsa::BSAFile::FileList &filelist = mFile->getList();\n    for(Bsa::BSAFile::FileList::const_iterator it = filelist.begin();it != filelist.end();++it)\n    {\n        mResources.emplace_back(&*it, mFile.get());\n    }\n}\n\nBsaArchive::~BsaArchive() {\n}\n\nvoid BsaArchive::listResources(std::map<std::string, File *> &out, char (*normalize_function)(char))\n{\n    for (std::vector<BsaArchiveFile>::iterator it = mResources.begin(); it != mResources.end(); ++it)\n    {\n        std::string ent = it->mInfo->name();\n        std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function);\n\n        out[ent] = &*it;\n    }\n}\n\nbool BsaArchive::contains(const std::string& file, char (*normalize_function)(char)) const\n{\n    for (const auto& it : mResources)\n    {\n        std::string ent = it.mInfo->name();\n        std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function);\n        if(file == ent)\n            return true;\n    }\n    return false;\n}\n\nstd::string BsaArchive::getDescription() const\n{\n    return std::string{\"BSA: \"} + mFile->getFilename();\n}\n\n// ------------------------------------------------------------------------------\n\nBsaArchiveFile::BsaArchiveFile(const Bsa::BSAFile::FileStruct *info, Bsa::BSAFile* bsa)\n    : mInfo(info)\n    , mFile(bsa)\n{\n\n}\n\nFiles::IStreamPtr BsaArchiveFile::open()\n{\n    return mFile->getFile(mInfo);\n}\n\n}\n"
  },
  {
    "path": "components/vfs/bsaarchive.hpp",
    "content": "#ifndef VFS_BSAARCHIVE_HPP_\n#define VFS_BSAARCHIVE_HPP_\n\n#include \"archive.hpp\"\n\n#include <components/bsa/bsa_file.hpp>\n\nnamespace VFS\n{\n    class BsaArchiveFile : public File\n    {\n    public:\n        BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, Bsa::BSAFile* bsa);\n\n        Files::IStreamPtr open() override;\n\n        const Bsa::BSAFile::FileStruct* mInfo;\n        Bsa::BSAFile* mFile;\n    };\n\n    class BsaArchive : public Archive\n    {\n    public:\n        BsaArchive(const std::string& filename);\n        virtual ~BsaArchive();\n        void listResources(std::map<std::string, File*>& out, char (*normalize_function) (char)) override;\n        bool contains(const std::string& file, char (*normalize_function) (char)) const override;\n        std::string getDescription() const override;\n\n    private:\n        std::unique_ptr<Bsa::BSAFile> mFile;\n        std::vector<BsaArchiveFile> mResources;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/vfs/filesystemarchive.cpp",
    "content": "#include \"filesystemarchive.hpp\"\n\n#include <boost/filesystem.hpp>\n\n#include <components/debug/debuglog.hpp>\n\nnamespace VFS\n{\n\n    FileSystemArchive::FileSystemArchive(const std::string &path)\n        : mBuiltIndex(false)\n        , mPath(path)\n    {\n\n    }\n\n    void FileSystemArchive::listResources(std::map<std::string, File *> &out, char (*normalize_function)(char))\n    {\n        if (!mBuiltIndex)\n        {\n            typedef boost::filesystem::recursive_directory_iterator directory_iterator;\n\n            directory_iterator end;\n\n            size_t prefix = mPath.size ();\n\n            if (mPath.size () > 0 && mPath [prefix - 1] != '\\\\' && mPath [prefix - 1] != '/')\n                ++prefix;\n\n            for (directory_iterator i (mPath); i != end; ++i)\n            {\n                if(boost::filesystem::is_directory (*i))\n                    continue;\n\n                std::string proper = i->path ().string ();\n\n                FileSystemArchiveFile file(proper);\n\n                std::string searchable;\n\n                std::transform(proper.begin() + prefix, proper.end(), std::back_inserter(searchable), normalize_function);\n\n                if (!mIndex.insert (std::make_pair (searchable, file)).second)\n                    Log(Debug::Warning) << \"Warning: found duplicate file for '\" << proper << \"', please check your file system for two files with the same name in different cases.\";\n            }\n\n            mBuiltIndex = true;\n        }\n\n        for (index::iterator it = mIndex.begin(); it != mIndex.end(); ++it)\n        {\n            out[it->first] = &it->second;\n        }\n    }\n\n    bool FileSystemArchive::contains(const std::string& file, char (*normalize_function)(char)) const\n    {\n        return mIndex.find(file) != mIndex.end();\n    }\n\n    std::string FileSystemArchive::getDescription() const\n    {\n        return std::string{\"DIR: \"} + mPath;\n    }\n\n    // ----------------------------------------------------------------------------------\n\n    FileSystemArchiveFile::FileSystemArchiveFile(const std::string &path)\n        : mPath(path)\n    {\n    }\n\n    Files::IStreamPtr FileSystemArchiveFile::open()\n    {\n        return Files::openConstrainedFileStream(mPath.c_str());\n    }\n\n}\n"
  },
  {
    "path": "components/vfs/filesystemarchive.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCE_FILESYSTEMARCHIVE_H\n#define OPENMW_COMPONENTS_RESOURCE_FILESYSTEMARCHIVE_H\n\n#include \"archive.hpp\"\n\nnamespace VFS\n{\n\n    class FileSystemArchiveFile : public File\n    {\n    public:\n        FileSystemArchiveFile(const std::string& path);\n\n        Files::IStreamPtr open() override;\n\n    private:\n        std::string mPath;\n\n    };\n\n    class FileSystemArchive : public Archive\n    {\n    public:\n        FileSystemArchive(const std::string& path);\n\n        void listResources(std::map<std::string, File*>& out, char (*normalize_function) (char)) override;\n\n        bool contains(const std::string& file, char (*normalize_function) (char)) const override;\n\n        std::string getDescription() const override;\n\n    private:\n        typedef std::map <std::string, FileSystemArchiveFile> index;\n        index mIndex;\n\n        bool mBuiltIndex;\n        std::string mPath;\n\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/vfs/manager.cpp",
    "content": "#include \"manager.hpp\"\n\n#include <stdexcept>\n\n#include <components/misc/stringops.hpp>\n\n#include \"archive.hpp\"\n\nnamespace\n{\n\n    char strict_normalize_char(char ch)\n    {\n        return ch == '\\\\' ? '/' : ch;\n    }\n\n    char nonstrict_normalize_char(char ch)\n    {\n        return ch == '\\\\' ? '/' : Misc::StringUtils::toLower(ch);\n    }\n\n    void normalize_path(std::string& path, bool strict)\n    {\n        char (*normalize_char)(char) = strict ? &strict_normalize_char : &nonstrict_normalize_char;\n        std::transform(path.begin(), path.end(), path.begin(), normalize_char);\n    }\n\n}\n\nnamespace VFS\n{\n\n    Manager::Manager(bool strict)\n        : mStrict(strict)\n    {\n\n    }\n\n    Manager::~Manager()\n    {\n        reset();\n    }\n\n    void Manager::reset()\n    {\n        mIndex.clear();\n        for (std::vector<Archive*>::iterator it = mArchives.begin(); it != mArchives.end(); ++it)\n            delete *it;\n        mArchives.clear();\n    }\n\n    void Manager::addArchive(Archive *archive)\n    {\n        mArchives.push_back(archive);\n    }\n\n    void Manager::buildIndex()\n    {\n        mIndex.clear();\n\n        for (std::vector<Archive*>::const_iterator it = mArchives.begin(); it != mArchives.end(); ++it)\n            (*it)->listResources(mIndex, mStrict ? &strict_normalize_char : &nonstrict_normalize_char);\n    }\n\n    Files::IStreamPtr Manager::get(const std::string &name) const\n    {\n        std::string normalized = name;\n        normalize_path(normalized, mStrict);\n\n        return getNormalized(normalized);\n    }\n\n    Files::IStreamPtr Manager::getNormalized(const std::string &normalizedName) const\n    {\n        std::map<std::string, File*>::const_iterator found = mIndex.find(normalizedName);\n        if (found == mIndex.end())\n            throw std::runtime_error(\"Resource '\" + normalizedName + \"' not found\");\n        return found->second->open();\n    }\n\n    bool Manager::exists(const std::string &name) const\n    {\n        std::string normalized = name;\n        normalize_path(normalized, mStrict);\n\n        return mIndex.find(normalized) != mIndex.end();\n    }\n\n    const std::map<std::string, File*>& Manager::getIndex() const\n    {\n        return mIndex;\n    }\n\n    void Manager::normalizeFilename(std::string &name) const\n    {\n        normalize_path(name, mStrict);\n    }\n\n    std::string Manager::getArchive(const std::string& name) const\n    {\n        std::string normalized = name;\n        normalize_path(normalized, mStrict);\n        for(auto it = mArchives.rbegin(); it != mArchives.rend(); ++it)\n        {\n            if((*it)->contains(normalized, mStrict ? &strict_normalize_char : &nonstrict_normalize_char))\n                return (*it)->getDescription();\n        }\n        return {};\n    }\n}\n"
  },
  {
    "path": "components/vfs/manager.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_RESOURCEMANAGER_H\n#define OPENMW_COMPONENTS_RESOURCEMANAGER_H\n\n#include <components/files/constrainedfilestream.hpp>\n\n#include <vector>\n#include <map>\n\nnamespace VFS\n{\n\n    class Archive;\n    class File;\n\n    /// @brief The main class responsible for loading files from a virtual file system.\n    /// @par Various archive types (e.g. directories on the filesystem, or compressed archives)\n    /// can be registered, and will be merged into a single file tree. If the same filename is\n    /// contained in multiple archives, the last added archive will have priority.\n    /// @par Most of the methods in this class are considered thread-safe, see each method documentation for details.\n    class Manager\n    {\n    public:\n        /// @param strict Use strict path handling? If enabled, no case folding will\n        /// be done, but slash/backslash conversions are always done.\n        Manager(bool strict);\n\n        ~Manager();\n\n        // Empty the file index and unregister archives.\n        void reset();\n\n        /// Register the given archive. All files contained in it will be added to the index on the next buildIndex() call.\n        /// @note Takes ownership of the given pointer.\n        void addArchive(Archive* archive);\n\n        /// Build the file index. Should be called when all archives have been registered.\n        void buildIndex();\n\n        /// Does a file with this name exist?\n        /// @note May be called from any thread once the index has been built.\n        bool exists(const std::string& name) const;\n\n        /// Get a complete list of files from all archives\n        /// @note May be called from any thread once the index has been built.\n        const std::map<std::string, File*>& getIndex() const;\n\n        /// Normalize the given filename, making slashes/backslashes consistent, and lower-casing if mStrict is false.\n        /// @note May be called from any thread once the index has been built.\n        void normalizeFilename(std::string& name) const;\n\n        /// Retrieve a file by name.\n        /// @note Throws an exception if the file can not be found.\n        /// @note May be called from any thread once the index has been built.\n        Files::IStreamPtr get(const std::string& name) const;\n\n        /// Retrieve a file by name (name is already normalized).\n        /// @note Throws an exception if the file can not be found.\n        /// @note May be called from any thread once the index has been built.\n        Files::IStreamPtr getNormalized(const std::string& normalizedName) const;\n\n        std::string getArchive(const std::string& name) const;\n    private:\n        bool mStrict;\n\n        std::vector<Archive*> mArchives;\n\n        std::map<std::string, File*> mIndex;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/vfs/registerarchives.cpp",
    "content": "#include \"registerarchives.hpp\"\n\n#include <set>\n#include <sstream>\n\n#include <components/debug/debuglog.hpp>\n\n#include <components/vfs/manager.hpp>\n#include <components/vfs/bsaarchive.hpp>\n#include <components/vfs/filesystemarchive.hpp>\n\nnamespace VFS\n{\n\n    void registerArchives(VFS::Manager *vfs, const Files::Collections &collections, const std::vector<std::string> &archives, bool useLooseFiles)\n    {\n        const Files::PathContainer& dataDirs = collections.getPaths();\n\n        for (std::vector<std::string>::const_iterator archive = archives.begin(); archive != archives.end(); ++archive)\n        {\n            if (collections.doesExist(*archive))\n            {\n                // Last BSA has the highest priority\n                const std::string archivePath = collections.getPath(*archive).string();\n                Log(Debug::Info) << \"Adding BSA archive \" << archivePath;\n\n                vfs->addArchive(new BsaArchive(archivePath));\n            }\n            else\n            {\n                std::stringstream message;\n                message << \"Archive '\" << *archive << \"' not found\";\n                throw std::runtime_error(message.str());\n            }\n        }\n\n        if (useLooseFiles)\n        {\n            std::set<boost::filesystem::path> seen;\n            for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)\n            {\n                if (seen.insert(*iter).second)\n                {\n                    Log(Debug::Info) << \"Adding data directory \" << iter->string();\n                    // Last data dir has the highest priority\n                    vfs->addArchive(new FileSystemArchive(iter->string()));\n                }\n                else\n                    Log(Debug::Info) << \"Ignoring duplicate data directory \" << iter->string();\n            }\n        }\n\n        vfs->buildIndex();\n    }\n\n}\n"
  },
  {
    "path": "components/vfs/registerarchives.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_VFS_REGISTER_ARCHIVES_H\n#define OPENMW_COMPONENTS_VFS_REGISTER_ARCHIVES_H\n\n#include <components/files/collections.hpp>\n\nnamespace VFS\n{\n    class Manager;\n\n    /// @brief Register BSA and file system archives based on the given OpenMW configuration.\n    void registerArchives (VFS::Manager* vfs, const Files::Collections& collections,\n        const std::vector<std::string>& archives, bool useLooseFiles);\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/box.cpp",
    "content": "#include \"box.hpp\"\n\n#include <MyGUI_EditText.h>\n\nnamespace Gui\n{\n\n    void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w)\n    {\n        MyGUI::Widget * parent = w->getParent();\n        if (parent != nullptr)\n        {\n            if (mExpandDirection.isLeft())\n            {\n                int hdiff = getRequestedSize ().width - w->getSize().width;\n                w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0));\n            }\n            w->setSize(getRequestedSize ());\n\n            while (parent != nullptr)\n            {\n                Box * b = dynamic_cast<Box*>(parent);\n                if (b)\n                    b->notifyChildrenSizeChanged();\n                else\n                    break;\n                parent = parent->getParent();\n            }\n        }\n    }\n\n\n    MyGUI::IntSize AutoSizedTextBox::getRequestedSize()\n    {\n        return getCaption().empty() ? MyGUI::IntSize{0, 0} : getTextSize();\n    }\n\n    void AutoSizedTextBox::setCaption(const MyGUI::UString& _value)\n    {\n        TextBox::setCaption(_value);\n\n        notifySizeChange (this);\n    }\n\n    void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value)\n    {\n        if (_key == \"ExpandDirection\")\n        {\n            mExpandDirection = MyGUI::Align::parse (_value);\n        }\n        else\n        {\n            Gui::TextBox::setPropertyOverride (_key, _value);\n        }\n    }\n\n    int AutoSizedEditBox::getWidth()\n    {\n        // If the widget has the one short text line, we can shrink widget to avoid a lot of empty space.\n        int textWidth = mMaxWidth;\n\n        if (mShrink)\n        {\n            // MyGUI needs to know the widget size for wordwrapping, but we will know the widget size only after wordwrapping.\n            // To solve this issue, use the maximum tooltip width first for wordwrapping, then resize widget to its actual used space.\n            if (mWasResized)\n            {\n                int maxLineWidth = 0;\n                const MyGUI::VectorLineInfo & lines = getSubWidgetText()->castType<MyGUI::EditText>()->getLineInfo();\n                for (unsigned int i = 0; i < lines.size(); ++i)\n                    maxLineWidth = std::max(maxLineWidth, lines[i].width);\n\n                textWidth = std::min(maxLineWidth, textWidth);\n            }\n            else\n            {\n                mWasResized = true;\n            }\n        }\n\n        return textWidth;\n    }\n\n    MyGUI::IntSize AutoSizedEditBox::getRequestedSize()\n    {\n        if (getAlign().isHStretch())\n            throw std::runtime_error(\"AutoSizedEditBox can't have HStretch align (\" + getName() + \")\");\n        return MyGUI::IntSize(getWidth(), getTextSize().height);\n    }\n\n    void AutoSizedEditBox::setCaption(const MyGUI::UString& _value)\n    {\n        EditBox::setCaption(_value);\n        mWasResized = false;\n\n        notifySizeChange (this);\n    }\n\n    void AutoSizedEditBox::initialiseOverride()\n    {\n        mMaxWidth = getSize().width;\n        Base::initialiseOverride();\n        setNeedKeyFocus(false);\n        setEditStatic(true);\n    }\n\n    void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value)\n    {\n        if (_key == \"ExpandDirection\")\n        {\n            mExpandDirection = MyGUI::Align::parse (_value);\n        }\n        else if (_key == \"Shrink\")\n        {\n            mShrink = MyGUI::utility::parseValue<bool>(_value);\n        }\n        else\n        {\n            Gui::EditBox::setPropertyOverride (_key, _value);\n        }\n    }\n\n    MyGUI::IntSize AutoSizedButton::getRequestedSize()\n    {\n        MyGUI::IntSize padding(24, 8);\n        if (isUserString(\"TextPadding\"))\n            padding = MyGUI::IntSize::parse(getUserString(\"TextPadding\"));\n\n        MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(padding.width,padding.height);\n        return size;\n    }\n\n    void AutoSizedButton::setCaption(const MyGUI::UString& _value)\n    {\n        Button::setCaption(_value);\n\n        notifySizeChange (this);\n    }\n\n    void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value)\n    {\n        if (_key == \"ExpandDirection\")\n        {\n            mExpandDirection = MyGUI::Align::parse (_value);\n        }\n        else\n        {\n            Gui::Button::setPropertyOverride (_key, _value);\n        }\n    }\n    Box::Box()\n        : mSpacing(4)\n        , mPadding(0)\n        , mAutoResize(false)\n    {\n    }\n\n    void Box::notifyChildrenSizeChanged ()\n    {\n        align();\n    }\n\n    bool Box::_setPropertyImpl(const std::string& _key, const std::string& _value)\n    {\n        if (_key == \"Spacing\")\n            mSpacing = MyGUI::utility::parseValue<int>(_value);\n        else if (_key == \"Padding\")\n            mPadding = MyGUI::utility::parseValue<int>(_value);\n        else if (_key == \"AutoResize\")\n            mAutoResize = MyGUI::utility::parseValue<bool>(_value);\n        else\n            return false;\n\n        return true;\n    }\n\n    void HBox::align ()\n    {\n        unsigned int count = getChildCount ();\n        size_t h_stretched_count = 0;\n        int total_width = 0;\n        int total_height = 0;\n        std::vector< std::pair<MyGUI::IntSize, bool> > sizes;\n        sizes.resize(count);\n\n        for (unsigned int i = 0; i < count; ++i)\n        {\n            MyGUI::Widget* w = getChildAt(i);\n            bool hstretch = w->getUserString (\"HStretch\") == \"true\";\n            bool hidden = w->getUserString(\"Hidden\") == \"true\";\n            if (hidden)\n                continue;\n            h_stretched_count += hstretch;\n            AutoSizedWidget* aw = dynamic_cast<AutoSizedWidget*>(w);\n            if (aw)\n            {\n                sizes[i] = std::make_pair(aw->getRequestedSize (), hstretch);\n                total_width += aw->getRequestedSize ().width;\n                total_height = std::max(total_height, aw->getRequestedSize ().height);\n            }\n            else\n            {\n                sizes[i] = std::make_pair(w->getSize(), hstretch);\n                total_width += w->getSize().width;\n                if (!(w->getUserString(\"VStretch\") == \"true\"))\n                    total_height = std::max(total_height, w->getSize().height);\n            }\n\n            if (i != count-1)\n                total_width += mSpacing;\n        }\n\n        if (mAutoResize && (total_width+mPadding*2 != getClientCoord().width || total_height+mPadding*2 != getClientCoord().height))\n        {\n            int xmargin = getSize().width - getClientCoord().width;\n            int ymargin = getSize().height - getClientCoord().height;\n            setSize(MyGUI::IntSize(total_width+mPadding*2 + xmargin, total_height+mPadding*2 + ymargin));\n            return;\n        }\n\n\n        int curX = 0;\n        for (unsigned int i = 0; i < count; ++i)\n        {\n            if (i == 0)\n                curX += mPadding;\n\n            MyGUI::Widget* w = getChildAt(i);\n\n            bool hidden = w->getUserString(\"Hidden\") == \"true\";\n            if (hidden)\n                continue;\n\n            bool vstretch = w->getUserString (\"VStretch\") == \"true\";\n            int max_height = getClientCoord().height - mPadding*2;\n            int height = vstretch ? max_height : sizes[i].first.height;\n\n            MyGUI::IntCoord widgetCoord;\n            widgetCoord.left = curX;\n            widgetCoord.top = mPadding + (getClientCoord().height-mPadding*2 - height) / 2;\n\n            int width = 0;\n            if (sizes[i].second)\n            {\n                if (h_stretched_count == 0)\n                    throw std::logic_error(\"unexpected\");\n                width = sizes[i].first.width + (getClientCoord().width-mPadding*2 - total_width)/h_stretched_count;\n            }\n            else\n                width = sizes[i].first.width;\n\n            widgetCoord.width = width;\n            widgetCoord.height = height;\n            w->setCoord(widgetCoord);\n            curX += width;\n\n            if (i != count-1)\n                curX += mSpacing;\n        }\n    }\n\n    void HBox::setPropertyOverride(const std::string& _key, const std::string& _value)\n    {\n        if (!Box::_setPropertyImpl (_key, _value))\n            MyGUI::Widget::setPropertyOverride(_key, _value);\n    }\n\n    void HBox::setSize (const MyGUI::IntSize& _value)\n    {\n        MyGUI::Widget::setSize (_value);\n        align();\n    }\n\n    void HBox::setCoord (const MyGUI::IntCoord& _value)\n    {\n        MyGUI::Widget::setCoord (_value);\n        align();\n    }\n\n    void HBox::initialiseOverride()\n    {\n        Base::initialiseOverride();\n        MyGUI::Widget* client = nullptr;\n        assignWidget(client, \"Client\");\n        setWidgetClient(client);\n    }\n\n    void HBox::onWidgetCreated(MyGUI::Widget* _widget)\n    {\n        align();\n    }\n\n    MyGUI::IntSize HBox::getRequestedSize ()\n    {\n        MyGUI::IntSize size(0,0);\n        for (unsigned int i = 0; i < getChildCount (); ++i)\n        {\n            bool hidden = getChildAt(i)->getUserString(\"Hidden\") == \"true\";\n            if (hidden)\n                continue;\n\n            AutoSizedWidget* w = dynamic_cast<AutoSizedWidget*>(getChildAt(i));\n            if (w)\n            {\n                MyGUI::IntSize requested = w->getRequestedSize ();\n                size.height = std::max(size.height, requested.height);\n                size.width = size.width + requested.width;\n                if (i != getChildCount()-1)\n                    size.width += mSpacing;\n            }\n            else\n            {\n                MyGUI::IntSize requested = getChildAt(i)->getSize ();\n                size.height = std::max(size.height, requested.height);\n\n                if (getChildAt(i)->getUserString(\"HStretch\") != \"true\")\n                    size.width = size.width + requested.width;\n\n                if (i != getChildCount()-1)\n                    size.width += mSpacing;\n            }\n            size.height += mPadding*2;\n            size.width += mPadding*2;\n        }\n        return size;\n    }\n\n\n\n\n    void VBox::align ()\n    {\n        unsigned int count = getChildCount ();\n        size_t v_stretched_count = 0;\n        int total_height = 0;\n        int total_width = 0;\n        std::vector< std::pair<MyGUI::IntSize, bool> > sizes;\n        sizes.resize(count);\n        for (unsigned int i = 0; i < count; ++i)\n        {\n            MyGUI::Widget* w = getChildAt(i);\n\n            bool hidden = w->getUserString(\"Hidden\") == \"true\";\n            if (hidden)\n                continue;\n\n            bool vstretch = w->getUserString (\"VStretch\") == \"true\";\n            v_stretched_count += vstretch;\n            AutoSizedWidget* aw = dynamic_cast<AutoSizedWidget*>(w);\n            if (aw)\n            {\n                sizes[i] = std::make_pair(aw->getRequestedSize (), vstretch);\n                total_height += aw->getRequestedSize ().height;\n                total_width = std::max(total_width, aw->getRequestedSize ().width);\n            }\n            else\n            {\n                sizes[i] = std::make_pair(w->getSize(), vstretch);\n                total_height += w->getSize().height;\n\n                if (!(w->getUserString(\"HStretch\") == \"true\"))\n                    total_width = std::max(total_width, w->getSize().width);\n            }\n\n            if (i != count-1)\n                total_height += mSpacing;\n        }\n\n        if (mAutoResize && (total_width+mPadding*2 != getClientCoord().width || total_height+mPadding*2 != getClientCoord().height))\n        {\n            int xmargin = getSize().width - getClientCoord().width;\n            int ymargin = getSize().height - getClientCoord().height;\n            setSize(MyGUI::IntSize(total_width+mPadding*2 + xmargin, total_height+mPadding*2 + ymargin));\n            return;\n        }\n\n        int curY = 0;\n        for (unsigned int i = 0; i < count; ++i)\n        {\n            if (i==0)\n                curY += mPadding;\n\n            MyGUI::Widget* w = getChildAt(i);\n\n            bool hidden = w->getUserString(\"Hidden\") == \"true\";\n            if (hidden)\n                continue;\n\n            bool hstretch = w->getUserString (\"HStretch\") == \"true\";\n            int maxWidth = getClientCoord().width - mPadding*2;\n            int width = hstretch ? maxWidth : sizes[i].first.width;\n\n            MyGUI::IntCoord widgetCoord;\n            widgetCoord.top = curY;\n            widgetCoord.left = mPadding + (getClientCoord().width-mPadding*2 - width) / 2;\n\n            int height = 0;\n            if (sizes[i].second)\n            {\n                if (v_stretched_count == 0)\n                    throw std::logic_error(\"unexpected\");\n                height = sizes[i].first.height + (getClientCoord().height-mPadding*2 - total_height)/v_stretched_count;\n            }\n            else\n                height = sizes[i].first.height;\n\n            widgetCoord.height = height;\n            widgetCoord.width = width;\n            w->setCoord(widgetCoord);\n            curY += height;\n\n            if (i != count-1)\n                curY += mSpacing;\n        }\n    }\n\n    void VBox::setPropertyOverride(const std::string& _key, const std::string& _value)\n    {\n        if (!Box::_setPropertyImpl (_key, _value))\n            MyGUI::Widget::setPropertyOverride(_key, _value);\n    }\n\n    void VBox::setSize (const MyGUI::IntSize& _value)\n    {\n        MyGUI::Widget::setSize (_value);\n        align();\n    }\n\n    void VBox::setCoord (const MyGUI::IntCoord& _value)\n    {\n        MyGUI::Widget::setCoord (_value);\n        align();\n    }\n\n    void VBox::initialiseOverride()\n    {\n        Base::initialiseOverride();\n        MyGUI::Widget* client = nullptr;\n        assignWidget(client, \"Client\");\n        setWidgetClient(client);\n    }\n\n    MyGUI::IntSize VBox::getRequestedSize ()\n    {\n        MyGUI::IntSize size(0,0);\n        for (unsigned int i = 0; i < getChildCount (); ++i)\n        {\n            bool hidden = getChildAt(i)->getUserString(\"Hidden\") == \"true\";\n            if (hidden)\n                continue;\n\n            AutoSizedWidget* w = dynamic_cast<AutoSizedWidget*>(getChildAt(i));\n            if (w)\n            {\n                MyGUI::IntSize requested = w->getRequestedSize ();\n                size.width = std::max(size.width, requested.width);\n                size.height = size.height + requested.height;\n                if (i != getChildCount()-1)\n                    size.height += mSpacing;\n            }\n            else\n            {\n                MyGUI::IntSize requested = getChildAt(i)->getSize ();\n                size.width = std::max(size.width, requested.width);\n\n                if (getChildAt(i)->getUserString(\"VStretch\") != \"true\")\n                    size.height = size.height + requested.height;\n\n                if (i != getChildCount()-1)\n                    size.height += mSpacing;\n            }\n            size.height += mPadding*2;\n            size.width += mPadding*2;\n        }\n        return size;\n    }\n\n    void VBox::onWidgetCreated(MyGUI::Widget* _widget)\n    {\n        align();\n    }\n\n    Spacer::Spacer()\n    {\n        setUserString(\"HStretch\", \"true\");\n        setUserString(\"VStretch\", \"true\");\n    }\n\n}\n"
  },
  {
    "path": "components/widgets/box.hpp",
    "content": "#ifndef OPENMW_WIDGETS_BOX_H\n#define OPENMW_WIDGETS_BOX_H\n\n#include <MyGUI_Widget.h>\n#include <MyGUI_TextBox.h>\n#include <MyGUI_EditBox.h>\n#include <MyGUI_ListBox.h>\n#include <MyGUI_Button.h>\n\n#include \"fontwrapper.hpp\"\n\nnamespace Gui\n{\n    class Button : public FontWrapper<MyGUI::Button>\n    {\n        MYGUI_RTTI_DERIVED( Button )\n    };\n\n    class TextBox : public FontWrapper<MyGUI::TextBox>\n    {\n        MYGUI_RTTI_DERIVED( TextBox )\n    };\n\n    class EditBox : public FontWrapper<MyGUI::EditBox>\n    {\n        MYGUI_RTTI_DERIVED( EditBox )\n    };\n\n    class AutoSizedWidget\n    {\n    public:\n        AutoSizedWidget() : mExpandDirection(MyGUI::Align::Right) {}\n\n        virtual MyGUI::IntSize getRequestedSize() = 0;\n\n        virtual ~AutoSizedWidget() = default;\n\n    protected:\n        void notifySizeChange(MyGUI::Widget* w);\n\n        MyGUI::Align mExpandDirection;\n    };\n\n    class AutoSizedTextBox : public AutoSizedWidget, public TextBox\n    {\n        MYGUI_RTTI_DERIVED( AutoSizedTextBox )\n\n    public:\n        MyGUI::IntSize getRequestedSize() override;\n        void setCaption(const MyGUI::UString& _value) override;\n\n    protected:\n        void setPropertyOverride(const std::string& _key, const std::string& _value) override;\n        std::string mFontSize;\n    };\n\n    class AutoSizedEditBox : public AutoSizedWidget, public EditBox\n    {\n        MYGUI_RTTI_DERIVED( AutoSizedEditBox )\n\n    public:\n\n        MyGUI::IntSize getRequestedSize() override;\n        void setCaption(const MyGUI::UString& _value) override;\n\n        void initialiseOverride() override;\n\n    protected:\n        void setPropertyOverride(const std::string& _key, const std::string& _value) override;\n        int getWidth();\n        std::string mFontSize;\n        bool mShrink = false;\n        bool mWasResized = false;\n        int mMaxWidth = 0;\n    };\n\n    class AutoSizedButton : public AutoSizedWidget, public Button\n    {\n        MYGUI_RTTI_DERIVED( AutoSizedButton )\n\n    public:\n        MyGUI::IntSize getRequestedSize() override;\n        void setCaption(const MyGUI::UString& _value) override;\n\n    protected:\n        void setPropertyOverride(const std::string& _key, const std::string& _value) override;\n        std::string mFontSize;\n    };\n\n    /**\n     * @brief A container widget that automatically sizes its children\n     * @note the box being an AutoSizedWidget as well allows to put boxes inside a box\n     */\n    class Box : public AutoSizedWidget\n    {\n    public:\n        Box();\n\n        virtual ~Box() = default;\n\n        void notifyChildrenSizeChanged();\n\n    protected:\n        virtual void align() = 0;\n\n        virtual bool _setPropertyImpl(const std::string& _key, const std::string& _value);\n\n        int mSpacing; // how much space to put between elements\n\n        int mPadding; // outer padding\n\n        bool mAutoResize; // auto resize the box so that it exactly fits all elements\n    };\n\n    class Spacer : public AutoSizedWidget, public MyGUI::Widget\n    {\n        MYGUI_RTTI_DERIVED( Spacer )\n    public:\n        Spacer();\n\n        MyGUI::IntSize getRequestedSize() override { return MyGUI::IntSize(0,0); }\n    };\n\n    class HBox : public Box, public MyGUI::Widget\n    {\n        MYGUI_RTTI_DERIVED( HBox )\n\n    public:\n        void setSize (const MyGUI::IntSize &_value) override;\n        void setCoord (const MyGUI::IntCoord &_value) override;\n\n    protected:\n        void initialiseOverride() override;\n\n        void align() override;\n        MyGUI::IntSize getRequestedSize() override;\n\n        void setPropertyOverride(const std::string& _key, const std::string& _value) override;\n\n        void onWidgetCreated(MyGUI::Widget* _widget) override;\n    };\n\n    class VBox : public Box, public MyGUI::Widget\n    {\n        MYGUI_RTTI_DERIVED( VBox)\n\n    public:\n        void setSize (const MyGUI::IntSize &_value) override;\n        void setCoord (const MyGUI::IntCoord &_value) override;\n\n    protected:\n        void initialiseOverride() override;\n\n        void align() override;\n        MyGUI::IntSize getRequestedSize() override;\n\n        void setPropertyOverride(const std::string& _key, const std::string& _value) override;\n\n        void onWidgetCreated(MyGUI::Widget* _widget) override;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/fontwrapper.hpp",
    "content": "#ifndef OPENMW_WIDGETS_WRAPPER_H\n#define OPENMW_WIDGETS_WRAPPER_H\n\n#include \"widgets.hpp\"\n\n#include <components/settings/settings.hpp>\n\nnamespace Gui\n{\n    template<class T>\n    class FontWrapper : public T\n    {\n    public:\n        void setFontName(const std::string& name) override\n        {\n            T::setFontName(name);\n            T::setPropertyOverride (\"FontHeight\", getFontSize());\n        }\n\n    protected:\n        void setPropertyOverride(const std::string& _key, const std::string& _value) override\n        {\n            T::setPropertyOverride (_key, _value);\n\n            // There is a bug in MyGUI: when it initializes the FontName property, it reset the font height.\n            // We should restore it.\n            if (_key == \"FontName\")\n            {\n                T::setPropertyOverride (\"FontHeight\", getFontSize());\n            }\n        }\n\n    private:\n        static int clamp(const int& value, const int& lowBound, const int& highBound)\n        {\n            return std::min(std::max(lowBound, value), highBound);\n        }\n\n        std::string getFontSize()\n        {\n            // Note: we can not use the FontLoader here, so there is a code duplication a bit.\n            static const std::string fontSize = std::to_string(clamp(Settings::Manager::getInt(\"font size\", \"GUI\"), 12, 20));\n            return fontSize;\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/imagebutton.cpp",
    "content": "#include \"imagebutton.hpp\"\n\n#include <MyGUI_RenderManager.h>\n\n#include <components/debug/debuglog.hpp>\n\nnamespace Gui\n{\n\n    bool ImageButton::sDefaultNeedKeyFocus = true;\n\n    ImageButton::ImageButton()\n        : Base()\n        , mMouseFocus(false)\n        , mMousePress(false)\n        , mKeyFocus(false)\n        , mUseWholeTexture(true)\n        , mTextureRect(MyGUI::IntCoord(0, 0, 0, 0))\n    {\n        setNeedKeyFocus(sDefaultNeedKeyFocus);\n    }\n\n    void ImageButton::setDefaultNeedKeyFocus(bool enabled)\n    {\n        sDefaultNeedKeyFocus = enabled;\n    }\n\n    void ImageButton::setTextureRect(MyGUI::IntCoord coord)\n    {\n        mTextureRect = coord;\n        mUseWholeTexture = (coord == MyGUI::IntCoord(0, 0, 0, 0));\n        updateImage();\n    }\n\n    void ImageButton::setPropertyOverride(const std::string &_key, const std::string &_value)\n    {\n        if (_key == \"ImageHighlighted\")\n            mImageHighlighted = _value;\n        else if (_key == \"ImagePushed\")\n            mImagePushed = _value;\n        else if (_key == \"ImageNormal\")\n        {\n            if (mImageNormal == \"\")\n            {\n                setImageTexture(_value);\n            }\n            mImageNormal = _value;\n        }\n        else if (_key == \"TextureRect\")\n        {\n            mTextureRect = MyGUI::IntCoord::parse(_value);\n            mUseWholeTexture = (mTextureRect == MyGUI::IntCoord(0, 0, 0, 0));\n        }\n        else\n            ImageBox::setPropertyOverride(_key, _value);\n    }\n    void ImageButton::onMouseSetFocus(Widget* _old)\n    {\n        mMouseFocus = true;\n        updateImage();\n        Base::onMouseSetFocus(_old);\n    }\n\n    void ImageButton::onMouseLostFocus(Widget* _new)\n    {\n        mMouseFocus = false;\n        updateImage();\n        Base::onMouseLostFocus(_new);\n    }\n\n    void ImageButton::onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id)\n    {\n        if (_id == MyGUI::MouseButton::Left)\n        {\n            mMousePress = true;\n            updateImage();\n        }\n        Base::onMouseButtonPressed(_left, _top, _id);\n    }\n\n    void ImageButton::updateImage()\n    {\n        std::string textureName = mImageNormal;\n        if (mMousePress)\n            textureName = mImagePushed;\n        else if (mMouseFocus || mKeyFocus)\n            textureName = mImageHighlighted;\n\n        if (!mUseWholeTexture)\n        {\n            int scale = 1.f;\n            MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(textureName);\n            if (texture && getHeight() != 0)\n                scale = texture->getHeight() / getHeight();\n\n            setImageTile(MyGUI::IntSize(mTextureRect.width * scale, mTextureRect.height * scale));\n            MyGUI::IntCoord scaledSize(mTextureRect.left * scale, mTextureRect.top * scale, mTextureRect.width * scale, mTextureRect.height * scale);\n            setImageCoord(scaledSize);\n        }\n\n        setImageTexture(textureName);\n    }\n\n    MyGUI::IntSize ImageButton::getRequestedSize()\n    {\n        MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(mImageNormal);\n        if (!texture)\n        {\n            Log(Debug::Error) << \"ImageButton: can't find image \" << mImageNormal;\n            return MyGUI::IntSize(0,0);\n        }\n\n        if (mUseWholeTexture)\n            return MyGUI::IntSize(texture->getWidth(), texture->getHeight());\n\n        return MyGUI::IntSize(mTextureRect.width, mTextureRect.height);\n    }\n\n    void ImageButton::setImage(const std::string &image)\n    {\n        size_t extpos = image.find_last_of('.');\n        std::string imageNoExt = image.substr(0, extpos);\n\n        std::string ext = image.substr(extpos);\n\n        mImageNormal = imageNoExt + \"_idle\" + ext;\n        mImageHighlighted = imageNoExt + \"_over\" + ext;\n        mImagePushed = imageNoExt + \"_pressed\" + ext;\n\n        updateImage();\n    }\n\n    void ImageButton::onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id)\n    {\n        if (_id == MyGUI::MouseButton::Left)\n        {\n            mMousePress = false;\n            updateImage();\n        }\n\n        Base::onMouseButtonReleased(_left, _top, _id);\n    }\n\n    void ImageButton::onKeySetFocus(MyGUI::Widget *_old)\n    {\n        mKeyFocus = true;\n        updateImage();\n    }\n\n    void ImageButton::onKeyLostFocus(MyGUI::Widget *_new)\n    {\n        mKeyFocus = false;\n        updateImage();\n    }\n}\n"
  },
  {
    "path": "components/widgets/imagebutton.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_WIDGETS_IMAGEBUTTON_H\n#define OPENMW_COMPONENTS_WIDGETS_IMAGEBUTTON_H\n\n#include <MyGUI_ImageBox.h>\n\nnamespace Gui\n{\n\n    /**\n     * @brief allows using different image textures depending on the button state\n     */\n    class ImageButton final : public MyGUI::ImageBox\n    {\n        MYGUI_RTTI_DERIVED(ImageButton)\n\n    public:\n        MyGUI::IntSize getRequestedSize();\n\n        ImageButton();\n\n        static void setDefaultNeedKeyFocus(bool enabled);\n\n        /// Set mImageNormal, mImageHighlighted and mImagePushed based on file convention (image_idle.ext, image_over.ext and image_pressed.ext)\n        void setImage(const std::string& image);\n\n        void setTextureRect(MyGUI::IntCoord coord);\n\n    private:\n        void updateImage();\n\n        static bool sDefaultNeedKeyFocus;\n\n    protected:\n        void setPropertyOverride(const std::string& _key, const std::string& _value) override;\n        void onMouseLostFocus(MyGUI::Widget* _new) override;\n        void onMouseSetFocus(MyGUI::Widget* _old) override;\n        void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) override;\n        void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) override;\n        void onKeySetFocus(MyGUI::Widget* _old) override;\n        void onKeyLostFocus(MyGUI::Widget* _new) override;\n\n        std::string mImageHighlighted;\n        std::string mImageNormal;\n        std::string mImagePushed;\n\n        bool mMouseFocus;\n        bool mMousePress;\n        bool mKeyFocus;\n        bool mUseWholeTexture;\n\n        MyGUI::IntCoord mTextureRect;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/list.cpp",
    "content": "#include \"list.hpp\"\n\n#include <MyGUI_Gui.h>\n#include <MyGUI_Button.h>\n#include <MyGUI_ImageBox.h>\n\nnamespace Gui\n{\n\n    MWList::MWList()\n        : mScrollView(nullptr)\n        , mClient(nullptr)\n        , mItemHeight(0)\n    {\n    }\n\n    void MWList::initialiseOverride()\n    {\n        Base::initialiseOverride();\n\n        assignWidget(mClient, \"Client\");\n        if (mClient == nullptr)\n            mClient = this;\n\n        mScrollView = mClient->createWidgetReal<MyGUI::ScrollView>(\n            \"MW_ScrollView\", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0),\n            MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + \"_ScrollView\");\n    }\n\n    void MWList::addItem(const std::string& name)\n    {\n        mItems.push_back(name);\n    }\n\n    void MWList::addSeparator()\n    {\n        mItems.emplace_back(\"\");\n    }\n\n    void MWList::adjustSize()\n    {\n        redraw();\n    }\n\n    void MWList::redraw(bool scrollbarShown)\n    {\n        const int _scrollBarWidth = 20; // fetch this from skin?\n        const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0;\n        const int spacing = 3;\n        int viewPosition = -mScrollView->getViewOffset().top;\n\n        while (mScrollView->getChildCount())\n        {\n            MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0));\n        }\n\n        mItemHeight = 0;\n        int i=0;\n        for (std::vector<std::string>::const_iterator it=mItems.begin();\n            it!=mItems.end(); ++it)\n        {\n            if (*it != \"\")\n            {\n                if (mListItemSkin.empty())\n                    return;\n                MyGUI::Button* button = mScrollView->createWidget<MyGUI::Button>(\n                    mListItemSkin, MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24),\n                    MyGUI::Align::Left | MyGUI::Align::Top, getName() + \"_item_\" + (*it));\n                button->setCaption((*it));\n                button->getSubWidgetText()->setWordWrap(true);\n                button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left);\n                button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheelMoved);\n                button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected);\n                button->setNeedKeyFocus(true);\n\n                int height = button->getTextSize().height;\n                button->setSize(MyGUI::IntSize(button->getSize().width, height));\n                button->setUserData(i);\n\n                mItemHeight += height + spacing;\n            }\n            else\n            {\n                MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>(\"MW_HLine\",\n                    MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth() - scrollBarWidth - 4, 18),\n                    MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);\n                separator->setNeedMouseFocus(false);\n\n                mItemHeight += 18 + spacing;\n            }\n            ++i;\n        }\n\n        // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden\n        mScrollView->setVisibleVScroll(false);\n        mScrollView->setCanvasSize(mClient->getSize().width, std::max(mItemHeight, mClient->getSize().height));\n        mScrollView->setVisibleVScroll(true);\n\n        if (!scrollbarShown && mItemHeight > mClient->getSize().height)\n            redraw(true);\n\n        int viewRange = mScrollView->getCanvasSize().height;\n        if(viewPosition > viewRange)\n            viewPosition = viewRange;\n        mScrollView->setViewOffset(MyGUI::IntPoint(0, -viewPosition));\n    }\n\n    void MWList::setPropertyOverride(const std::string &_key, const std::string &_value)\n    {\n        if (_key == \"ListItemSkin\")\n            mListItemSkin = _value;\n        else\n            Base::setPropertyOverride(_key, _value);\n    }\n\n    unsigned int MWList::getItemCount()\n    {\n        return static_cast<unsigned int>(mItems.size());\n    }\n\n    std::string MWList::getItemNameAt(unsigned int at)\n    {\n        assert(at < mItems.size() && \"List item out of bounds\");\n        return mItems[at];\n    }\n\n    void MWList::removeItem(const std::string& name)\n    {\n        assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() );\n        mItems.erase( std::find(mItems.begin(), mItems.end(), name) );\n    }\n\n    void MWList::clear()\n    {\n        mItems.clear();\n    }\n\n    void MWList::onMouseWheelMoved(MyGUI::Widget* _sender, int _rel)\n    {\n        //NB view offset is negative\n        if (mScrollView->getViewOffset().top + _rel*0.3f > 0)\n            mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));\n        else\n            mScrollView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mScrollView->getViewOffset().top + _rel*0.3)));\n    }\n\n    void MWList::onItemSelected(MyGUI::Widget* _sender)\n    {\n        std::string name = _sender->castType<MyGUI::Button>()->getCaption();\n        int id = *_sender->getUserData<int>();\n        eventItemSelected(name, id);\n        eventWidgetSelected(_sender);\n    }\n\n    MyGUI::Button *MWList::getItemWidget(const std::string& name)\n    {\n        return mScrollView->findWidget (getName() + \"_item_\" + name)->castType<MyGUI::Button>();\n    }\n\n    void MWList::scrollToTop()\n    {\n        mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));\n    }\n}\n"
  },
  {
    "path": "components/widgets/list.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_WIDGETS_LIST_HPP\n#define OPENMW_COMPONENTS_WIDGETS_LIST_HPP\n\n#include <MyGUI_ScrollView.h>\n\nnamespace Gui\n{\n    /**\n     * \\brief a very simple list widget that supports word-wrapping entries\n     * \\note if the width or height of the list changes, you must call adjustSize() method\n     */\n    class MWList : public MyGUI::Widget\n    {\n        MYGUI_RTTI_DERIVED(MWList)\n    public:\n        MWList();\n\n        typedef MyGUI::delegates::CMultiDelegate2<const std::string&, int> EventHandle_StringInt;\n        typedef MyGUI::delegates::CMultiDelegate1<MyGUI::Widget*> EventHandle_Widget;\n\n        /**\n         * Event: Item selected with the mouse.\n         * signature: void method(std::string itemName, int index)\n         */\n        EventHandle_StringInt eventItemSelected;\n\n        /**\n         * Event: Item selected with the mouse.\n         * signature: void method(MyGUI::Widget* sender)\n         */\n        EventHandle_Widget eventWidgetSelected;\n\n\n        /**\n         * Call after the size of the list changed, or items were inserted/removed\n         */\n        void adjustSize();\n\n        void addItem(const std::string& name);\n        void addSeparator(); ///< add a seperator between the current and the next item.\n        void removeItem(const std::string& name);\n        unsigned int getItemCount();\n        std::string getItemNameAt(unsigned int at); ///< \\attention if there are separators, this method will return \"\" at the place where the separator is\n        void clear();\n\n        MyGUI::Button* getItemWidget(const std::string& name);\n        ///< get widget for an item name, useful to set up tooltip\n\n        void scrollToTop();\n\n        void setPropertyOverride(const std::string& _key, const std::string& _value) override;\n\n    protected:\n        void initialiseOverride() override;\n\n        void redraw(bool scrollbarShown = false);\n\n        void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel);\n        void onItemSelected(MyGUI::Widget* _sender);\n\n    private:\n        MyGUI::ScrollView* mScrollView;\n        MyGUI::Widget* mClient;\n        std::string mListItemSkin;\n\n        std::vector<std::string> mItems;\n\n        int mItemHeight; // height of all items\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/numericeditbox.cpp",
    "content": "#include <stdexcept>\n\n#include \"numericeditbox.hpp\"\n\nnamespace Gui\n{\n\n    void NumericEditBox::initialiseOverride()\n    {\n        Base::initialiseOverride();\n        eventEditTextChange += MyGUI::newDelegate(this, &NumericEditBox::onEditTextChange);\n\n        mValue = 0;\n        setCaption(\"0\");\n    }\n\n    void NumericEditBox::shutdownOverride()\n    {\n        Base::shutdownOverride();\n        eventEditTextChange -= MyGUI::newDelegate(this, &NumericEditBox::onEditTextChange);\n    }\n\n    void NumericEditBox::onEditTextChange(MyGUI::EditBox *sender)\n    {\n        std::string newCaption = sender->getCaption();\n        if (newCaption.empty())\n        {\n            return;\n        }\n\n        try\n        {\n            mValue = std::stoi(newCaption);\n            int capped = std::min(mMaxValue, std::max(mValue, mMinValue));\n            if (capped != mValue)\n            {\n                mValue = capped;\n                setCaption(MyGUI::utility::toString(mValue));\n            }\n        }\n        catch (const std::invalid_argument&)\n        {\n            setCaption(MyGUI::utility::toString(mValue));\n        }\n        catch (const std::out_of_range&)\n        {\n            setCaption(MyGUI::utility::toString(mValue));\n        }\n\n        eventValueChanged(mValue);\n    }\n\n    void NumericEditBox::setValue(int value)\n    {\n        if (value != mValue)\n        {\n            setCaption(MyGUI::utility::toString(value));\n            mValue = value;\n        }\n    }\n\n    int NumericEditBox::getValue()\n    {\n        return mValue;\n    }\n\n    void NumericEditBox::setMinValue(int minValue)\n    {\n        mMinValue = minValue;\n    }\n\n    void NumericEditBox::setMaxValue(int maxValue)\n    {\n        mMaxValue = maxValue;\n    }\n\n    void NumericEditBox::onKeyLostFocus(MyGUI::Widget* _new)\n    {\n        Base::onKeyLostFocus(_new);\n        setCaption(MyGUI::utility::toString(mValue));\n    }\n\n    void NumericEditBox::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char character)\n    {\n        if (key == MyGUI::KeyCode::ArrowUp)\n        {\n            setValue(std::min(mValue+1, mMaxValue));\n            eventValueChanged(mValue);\n        }\n        else if (key == MyGUI::KeyCode::ArrowDown)\n        {\n            setValue(std::max(mValue-1, mMinValue));\n            eventValueChanged(mValue);\n        }\n        else if (character == 0 || (character >= '0' && character <= '9'))\n            Base::onKeyButtonPressed(key, character);\n    }\n\n}\n"
  },
  {
    "path": "components/widgets/numericeditbox.hpp",
    "content": "#ifndef OPENMW_NUMERIC_EDIT_BOX_H\n#define OPENMW_NUMERIC_EDIT_BOX_H\n\n#include <MyGUI_EditBox.h>\n\n#include \"fontwrapper.hpp\"\n\nnamespace Gui\n{\n\n    /**\n     * @brief A variant of the EditBox that only allows integer inputs\n     */\n    class NumericEditBox final : public FontWrapper<MyGUI::EditBox>\n    {\n        MYGUI_RTTI_DERIVED(NumericEditBox)\n\n    public:\n        NumericEditBox()\n            : mValue(0), mMinValue(std::numeric_limits<int>::min()),\n            mMaxValue(std::numeric_limits<int>::max())\n        {\n        }\n\n        void initialiseOverride() override;\n        void shutdownOverride() override;\n\n        typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_ValueChanged;\n        EventHandle_ValueChanged eventValueChanged;\n\n        /// @note Does not trigger eventValueChanged\n        void setValue (int value);\n        int getValue();\n\n        void setMinValue(int minValue);\n        void setMaxValue(int maxValue);\n    private:\n        void onEditTextChange(MyGUI::EditBox* sender);\n        void onKeyLostFocus(MyGUI::Widget* _new) override;\n        void onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char character) override;\n\n        int mValue;\n\n        int mMinValue;\n        int mMaxValue;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/sharedstatebutton.cpp",
    "content": "#include \"sharedstatebutton.hpp\"\n\nnamespace Gui\n{\n\n    SharedStateButton::SharedStateButton()\n        : mIsMousePressed(false)\n        , mIsMouseFocus(false)\n    {\n    }\n\n    void SharedStateButton::shutdownOverride()\n    {\n        ButtonGroup group = mSharedWith; // make a copy so that we don't nuke the vector during iteration\n        for (ButtonGroup::iterator it = group.begin(); it != group.end(); ++it)\n        {\n            (*it)->shareStateWith(ButtonGroup());\n        }\n    }\n\n    void SharedStateButton::shareStateWith(ButtonGroup shared)\n    {\n        mSharedWith = shared;\n    }\n\n    void SharedStateButton::onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id)\n    {\n        mIsMousePressed = true;\n        Base::onMouseButtonPressed(_left, _top, _id);\n        updateButtonState();\n    }\n\n    void SharedStateButton::onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id)\n    {\n        mIsMousePressed = false;\n        Base::onMouseButtonReleased(_left, _top, _id);\n        updateButtonState();\n    }\n\n    void SharedStateButton::onMouseSetFocus(MyGUI::Widget *_old)\n    {\n        mIsMouseFocus = true;\n        Base::onMouseSetFocus(_old);\n        updateButtonState();\n    }\n\n    void SharedStateButton::onMouseLostFocus(MyGUI::Widget *_new)\n    {\n        mIsMouseFocus = false;\n        Base::onMouseLostFocus(_new);\n        updateButtonState();\n    }\n\n    void SharedStateButton::baseUpdateEnable()\n    {\n        Base::baseUpdateEnable();\n        updateButtonState();\n    }\n\n    void SharedStateButton::setStateSelected(bool _value)\n    {\n        Base::setStateSelected(_value);\n        updateButtonState();\n\n        for (ButtonGroup::iterator it = mSharedWith.begin(); it != mSharedWith.end(); ++it)\n        {\n            (*it)->MyGUI::Button::setStateSelected(getStateSelected());\n        }\n    }\n\n    bool SharedStateButton::_setState(const std::string &_value)\n    {\n        bool ret = _setWidgetState(_value);\n        if (ret)\n        {\n            for (ButtonGroup::iterator it = mSharedWith.begin(); it != mSharedWith.end(); ++it)\n            {\n                (*it)->_setWidgetState(_value);\n            }\n        }\n        return ret;\n    }\n\n    void SharedStateButton::updateButtonState()\n    {\n        if (getStateSelected())\n        {\n            if (!getInheritedEnabled())\n            {\n                if (!_setState(\"disabled_checked\"))\n                    _setState(\"disabled\");\n            }\n            else if (mIsMousePressed)\n            {\n                if (!_setState(\"pushed_checked\"))\n                    _setState(\"pushed\");\n            }\n            else if (mIsMouseFocus)\n            {\n                if (!_setState(\"highlighted_checked\"))\n                    _setState(\"pushed\");\n            }\n            else\n                _setState(\"normal_checked\");\n        }\n        else\n        {\n            if (!getInheritedEnabled())\n                _setState(\"disabled\");\n            else if (mIsMousePressed)\n                _setState(\"pushed\");\n            else if (mIsMouseFocus)\n                _setState(\"highlighted\");\n            else\n                _setState(\"normal\");\n        }\n    }\n\n    void SharedStateButton::createButtonGroup(ButtonGroup group)\n    {\n        for (ButtonGroup::iterator it = group.begin(); it != group.end(); ++it)\n        {\n            (*it)->shareStateWith(group);\n        }\n    }\n\n}\n"
  },
  {
    "path": "components/widgets/sharedstatebutton.hpp",
    "content": "#ifndef OPENMW_WIDGETS_SHAREDSTATEBUTTON_HPP\n#define OPENMW_WIDGETS_SHAREDSTATEBUTTON_HPP\n\n#include <MyGUI_Button.h>\n\n#include \"fontwrapper.hpp\"\n\nnamespace Gui\n{\n\n    class SharedStateButton;\n\n    typedef std::vector<SharedStateButton*> ButtonGroup;\n\n    /// @brief A button that applies its own state changes to other widgets, to do this you define it as part of a ButtonGroup.\n    class SharedStateButton final : public FontWrapper<MyGUI::Button>\n    {\n    MYGUI_RTTI_DERIVED(SharedStateButton)\n\n    public:\n        SharedStateButton();\n\n    protected:\n        void updateButtonState();\n\n        void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) override;\n        void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) override;\n        void onMouseSetFocus(MyGUI::Widget* _old) override;\n        void onMouseLostFocus(MyGUI::Widget* _new) override;\n        void baseUpdateEnable() override;\n\n        void shutdownOverride() override;\n\n        bool _setState(const std::string &_value);\n\n    public:\n        void shareStateWith(ButtonGroup shared);\n\n        /// @note The ButtonGroup connection will be destroyed when any widget in the group gets destroyed.\n        static void createButtonGroup(ButtonGroup group);\n\n        //! Set button selected state\n        void setStateSelected(bool _value);\n\n    private:\n        ButtonGroup mSharedWith;\n\n        bool mIsMousePressed;\n        bool mIsMouseFocus;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/tags.cpp",
    "content": "#include \"tags.hpp\"\n\n#include <components/fallback/fallback.hpp>\n\n#include <MyGUI_Colour.h>\n\nnamespace Gui\n{\n\nbool replaceTag(const MyGUI::UString& tag, MyGUI::UString& out)\n{\n    std::string fontcolour = \"fontcolour=\";\n    size_t fontcolourLength = fontcolour.length();\n\n    std::string fontcolourhtml = \"fontcolourhtml=\";\n    size_t fontcolourhtmlLength = fontcolourhtml.length();\n\n    if (tag.compare(0, fontcolourLength, fontcolour) == 0)\n    {\n        std::string fallbackName = \"FontColor_color_\" + tag.substr(fontcolourLength);\n        std::string str = Fallback::Map::getString(fallbackName);\n        if (str.empty())\n            throw std::runtime_error(\"Unknown fallback name: \" + fallbackName);\n\n        std::string ret[3];\n        unsigned int j=0;\n        for(unsigned int i=0;i<str.length();++i)\n        {\n            if(str[i]==',') j++;\n            else if (str[i] != ' ') ret[j]+=str[i];\n        }\n        MyGUI::Colour col (MyGUI::utility::parseInt(ret[0])/255.f,MyGUI::utility::parseInt(ret[1])/255.f,MyGUI::utility::parseInt(ret[2])/255.f);\n        out = col.print();\n        return true;\n    }\n    else if (tag.compare(0, fontcolourhtmlLength, fontcolourhtml) == 0)\n    {\n        std::string fallbackName = \"FontColor_color_\" + tag.substr(fontcolourhtmlLength);\n        std::string str = Fallback::Map::getString(fallbackName);\n        if (str.empty())\n            throw std::runtime_error(\"Unknown fallback name: \" + fallbackName);\n\n        std::string ret[3];\n        unsigned int j=0;\n        for(unsigned int i=0;i<str.length();++i)\n        {\n            if(str[i]==',') j++;\n            else if (str[i] != ' ') ret[j]+=str[i];\n        }\n        std::stringstream html;\n        html << \"#\" << std::hex << MyGUI::utility::parseInt(ret[0]) << MyGUI::utility::parseInt(ret[1]) << MyGUI::utility::parseInt(ret[2]);\n        out = html.str();\n        return true;\n    }\n    return false;\n\n}\n\n}\n"
  },
  {
    "path": "components/widgets/tags.hpp",
    "content": "#ifndef OPENMW_WIDGETS_TAGS_H\n#define OPENMW_WIDGETS_TAGS_H\n\n#include <MyGUI_UString.h>\n#include <string>\n#include <map>\n\nnamespace Gui\n{\n\n/// Try to replace a tag. Returns true on success and writes the result to \\a out.\nbool replaceTag (const MyGUI::UString& tag, MyGUI::UString& out);\n\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/widgets.cpp",
    "content": "#include \"widgets.hpp\"\n\n#include <MyGUI_FactoryManager.h>\n\n#include \"list.hpp\"\n#include \"numericeditbox.hpp\"\n#include \"box.hpp\"\n#include \"imagebutton.hpp\"\n#include \"sharedstatebutton.hpp\"\n#include \"windowcaption.hpp\"\n\nnamespace Gui\n{\n\n    void registerAllWidgets()\n    {\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::MWList>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::HBox>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::Spacer>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::VBox>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::EditBox>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::TextBox>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::AutoSizedTextBox>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::AutoSizedEditBox>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::AutoSizedButton>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::Button>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::ImageButton>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::NumericEditBox>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::SharedStateButton>(\"Widget\");\n        MyGUI::FactoryManager::getInstance().registerFactory<Gui::WindowCaption>(\"Widget\");\n    }\n\n}\n"
  },
  {
    "path": "components/widgets/widgets.hpp",
    "content": "#ifndef OPENMW_COMPONENTS_WIDGETS_H\n#define OPENMW_COMPONENTS_WIDGETS_H\n\nextern int GuiFontHeight;\n\nnamespace Gui\n{\n    /// Register all widgets from this component with MyGUI's factory manager.\n    void registerAllWidgets();\n\n}\n\n#endif\n"
  },
  {
    "path": "components/widgets/windowcaption.cpp",
    "content": "#include \"windowcaption.hpp\"\n\n#include <stdexcept>\n\nnamespace Gui\n{\n\n    WindowCaption::WindowCaption()\n        : mLeft(nullptr)\n        , mRight(nullptr)\n        , mClient(nullptr)\n    {\n    }\n\n    void WindowCaption::initialiseOverride()\n    {\n        Base::initialiseOverride();\n\n        assignWidget(mLeft, \"Left\");\n        assignWidget(mRight, \"Right\");\n\n        assignWidget(mClient, \"Client\");\n        if (!mClient)\n            throw std::runtime_error(\"WindowCaption needs an EditBox Client widget in its skin\");\n    }\n\n    void WindowCaption::setCaption(const MyGUI::UString &_value)\n    {\n        EditBox::setCaption(_value);\n        align();\n    }\n\n    void WindowCaption::setSize(const MyGUI::IntSize& _value)\n    {\n        Base::setSize(_value);\n        align();\n    }\n\n    void WindowCaption::setCoord(const MyGUI::IntCoord& _value)\n    {\n        Base::setCoord(_value);\n        align();\n    }\n\n    void WindowCaption::align()\n    {\n        MyGUI::IntSize textSize = getTextSize();\n        MyGUI::Widget* caption = mClient;\n        caption->setSize(textSize.width + 24, caption->getHeight());\n\n        int barwidth = (getWidth()-caption->getWidth())/2;\n        caption->setPosition(barwidth, caption->getTop());\n        if (mLeft)\n            mLeft->setCoord(0, mLeft->getTop(), barwidth, mLeft->getHeight());\n        if (mRight)\n            mRight->setCoord(barwidth + caption->getWidth(), mRight->getTop(), barwidth, mRight->getHeight());\n    }\n\n}\n"
  },
  {
    "path": "components/widgets/windowcaption.hpp",
    "content": "#ifndef OPENMW_WIDGETS_WINDOWCAPTION_H\n#define OPENMW_WIDGETS_WINDOWCAPTION_H\n\n#include <MyGUI_EditBox.h>\n\nnamespace Gui\n{\n\n    /// Window caption that automatically adjusts \"Left\" and \"Right\" widgets in its skin\n    /// based on the text size of the caption in the middle\n    class WindowCaption final : public MyGUI::EditBox\n    {\n        MYGUI_RTTI_DERIVED(WindowCaption)\n    public:\n        WindowCaption();\n\n        void setCaption(const MyGUI::UString &_value) override;\n        void initialiseOverride() override;\n\n        void setSize(const MyGUI::IntSize& _value) override;\n        void setCoord(const MyGUI::IntCoord& _value) override;\n\n    private:\n        MyGUI::Widget* mLeft;\n        MyGUI::Widget* mRight;\n        MyGUI::Widget* mClient;\n\n        void align();\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "docs/Doxyfile.cmake",
    "content": "# Doxyfile 1.8.7\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the config file\n# that follow. The default is UTF-8 which is also the encoding used for all text\n# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv\n# built into libc) for the transcoding. See https://www.gnu.org/software/libiconv\n# for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = OpenMW\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         =\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify an logo or icon that is included in\n# the documentation. The maximum height of the logo should not exceed 55 pixels\n# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo\n# to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = @OpenMW_BINARY_DIR@/docs/Doxygen\n\n# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a\n# new page for each member. If set to NO, the documentation of a member will be\n# part of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:\\n\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\n# newlines.\n\nALIASES                =\n\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\n# will allow you to use the command class in the itcl::class meaning.\n\nTCL_SUBST              =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, Javascript,\n# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:\n# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:\n# Fortran. In the later case the parser tries to guess whether the code is fixed\n# or free formatted code, this is the default for Fortran type files), VHDL. For\n# instance to make doxygen treat .inc files as Fortran files (default is PHP),\n# and .f files as C (default is Fortran), use: inc=Fortran f=C.\n#\n# Note For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by by putting a % sign in front of the word\n# or globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = YES\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES, then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = YES\n\n# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. When set to YES local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = YES\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO these classes will be included in the various overviews. This option has\n# no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# (class|struct|union) declarations. If set to NO these declarations will be\n# included in the documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the\n# todo list. This list is created by putting \\todo commands in the\n# documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the\n# test list. This list is created by putting \\test commands in the\n# documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES the list\n# will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. Do not use file names with spaces, bibtex cannot handle them. See\n# also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = NO\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO doxygen will only warn about wrong or incomplete parameter\n# documentation, but not about the absence of documentation.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces.\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = @OpenMW_SOURCE_DIR@/apps \\\n                         @OpenMW_SOURCE_DIR@/components \\\n                         @OpenMW_SOURCE_DIR@/libs \\\n                         @OpenMW_BINARY_DIR@/docs/mainpage.hpp\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see: https://www.gnu.org/software/libiconv) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank the\n# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,\n# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,\n# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,\n# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,\n# *.qsf, *.as and *.js.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.java \\\n                         *.ii \\\n                         *.ixx \\\n                         *.ipp \\\n                         *.i++ \\\n                         *.inl \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.idl \\\n                         *.odl \\\n                         *.cs \\\n                         *.php \\\n                         *.php3 \\\n                         *.inc \\\n                         *.m \\\n                         *.mm \\\n                         *.dox \\\n                         *.py \\\n                         *.f90 \\\n                         *.f \\\n                         *.vhd \\\n                         *.vhdl\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER ) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# function all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES, then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the config file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = NO\n\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-\n# defined cascading style sheet that is included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefor more robust against future updates.\n# Doxygen will copy the style sheet file to the output directory. For an example\n# see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the stylesheet and background images according to\n# this color. Hue is specified as an angle on a colorwheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use grayscales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see: https://developer.apple.com/xcode/), introduced with\n# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html\n# for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"OpenMW Documentation\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.openmw\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.openmw\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = OpenMW\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler ( hhc.exe). If non-empty\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated (\n# YES) or that it should be included in the master .chm file ( NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated (\n# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.openmw\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.openmw\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine-tune the look of the index. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side Javascript for the rendering\n# instead of using prerendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = YES\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# https://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment.\n# The default value is: https://cdn.mathjax.org/mathjax/latest.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = https://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see: https://docs.mathjax.org/en/latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using Javascript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer ( doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer ( doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: https://xapian.org/). See the section \"External Indexing and\n# Searching\" for details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when enabling USE_PDFLATEX this option is only used for generating\n# bitmaps for formulas in the HTML output, but not in the Makefile that is\n# written to the output directory.\n# The default file is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4wide\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. To get the times font for\n# instance you can specify\n# EXTRA_PACKAGES=times\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will\n# replace them by respectively the title of the page, the current date and time,\n# only the current date, the version number of doxygen, the project name (see\n# PROJECT_NAME), or the project number (see PROJECT_NUMBER).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# chapter. If it is left blank doxygen will generate a standard footer.\n#\n# Note: Only use a user-defined footer if you know what you are doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES to get a\n# higher quality PDF documentation.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's config\n# file, i.e. a series of assignments. You only have to provide replacements,\n# missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's config file. A template extensions file can be generated\n# using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen\n# Definitions (see http://autogen.sourceforge.net) file that captures the structure of\n# the code including all documentation. Note that this feature is still\n# experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names\n# in the source code. If set to NO only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES the includes files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES all external class will be listed in the\n# class index. If set to NO only the inherited external classes will be listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in\n# the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of 'which perl').\n# The default file (with absolute path) is: /usr/bin/perl.\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. Doxygen will then run the mscgen tool (see:\n# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the\n# documentation. The MSCGEN_PATH tag allows you to specify the directory where\n# the mscgen tool resides. If left empty the tool is assumed to be found in the\n# default search path.\n\nMSCGEN_PATH            =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES, the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = NO\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: YES.\n\nHAVE_DOT               = YES\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font n the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = FreeSans\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = YES\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = YES\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = YES\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot.\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,\n# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,\n# gif:cairo:gd, gif:gd, gif:gd:gd and svg.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = svg\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "docs/DoxyfilePages.cmake",
    "content": "# Doxyfile 1.5.8\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project\n#\n# All text after a hash (#) is considered a comment and will be ignored\n# The format is:\n#       TAG = value [value, ...]\n# For lists items can also be appended using:\n#       TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\" \")\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the config file\n# that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv for the list of possible encodings.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded\n# by quotes) that should identify the project.\n\nPROJECT_NAME           = OpenMW\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number.\n# This could be handy for archiving the generated documentation or\n# if some version control system is used.\n\nPROJECT_NUMBER         =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)\n# base path where the generated documentation will be put.\n# If a relative path is entered, it will be relative to the location\n# where doxygen was started. If left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = @DOXYGEN_PAGES_OUTPUT_DIR@\n\n# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create\n# 4096 sub-directories (in 2 levels) under the output directory of each output\n# format and will distribute the generated files over these directories.\n# Enabling this option can be useful when feeding doxygen a huge amount of\n# source files, where putting all generated files in the same directory would\n# otherwise cause performance problems for the file system.\n\nCREATE_SUBDIRS         = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# The default language is English, other supported languages are:\n# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,\n# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,\n# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,\n# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,\n# Spanish, Swedish, and Ukrainian.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will\n# include brief member descriptions after the members that are listed in\n# the file and class documentation (similar to JavaDoc).\n# Set to NO to disable this.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend\n# the brief description of a member or function before the detailed description.\n# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator\n# that is used to form the text in various listings. Each string\n# in this list, if found as the leading text of the brief description, will be\n# stripped from the text and the result after processing the whole list, is\n# used as the annotated text. Otherwise, the brief description is used as-is.\n# If left blank, the following values are used (\"$name\" is automatically\n# replaced with the name of the entity): \"The $name class\" \"The $name widget\"\n# \"The $name file\" \"is\" \"provides\" \"specifies\" \"contains\"\n# \"represents\" \"a\" \"an\" \"the\"\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# Doxygen will generate a detailed section even if there is only a brief\n# description.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full\n# path before files name in the file list and in the header files. If set\n# to NO the shortest path that makes the file name unique will be used.\n\nFULL_PATH_NAMES        = YES\n\n# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag\n# can be used to strip a user-defined part of the path. Stripping is\n# only done if one of the specified strings matches the left-hand part of\n# the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the\n# path to strip.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of\n# the path mentioned in the documentation of a class, which tells\n# the reader which header file to include in order to use a class.\n# If left blank only the name of the header file containing the class\n# definition is used. Otherwise one should specify the include paths that\n# are normally passed to the compiler using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter\n# (but less readable) file names. This can be useful is your file systems\n# doesn't support long names like on DOS, Mac, or CD-ROM.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen\n# will interpret the first line (until the first dot) of a JavaDoc-style\n# comment as the brief description. If set to NO, the JavaDoc\n# comments will behave just like regular Qt-style comments\n# (thus requiring an explicit @brief command for a brief description.)\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then Doxygen will\n# interpret the first line (until the first dot) of a Qt-style\n# comment as the brief description. If set to NO, the comments\n# will behave just like regular Qt-style comments (thus requiring\n# an explicit \\brief command for a brief description.)\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen\n# treat a multi-line C++ special comment block (i.e. a block of //! or ///\n# comments) as a brief description. This used to be the default behaviour.\n# The new default is to treat a multi-line C++ comment block as a detailed\n# description. Set this tag to YES if you prefer the old behaviour instead.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented\n# member inherits the documentation from any documented member that it\n# re-implements.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce\n# a new page for each member. If set to NO, the documentation of a member will\n# be part of the file/class/namespace that contains it.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab.\n# Doxygen uses this value to replace tabs by spaces in code fragments.\n\nTAB_SIZE               = 8\n\n# This tag can be used to specify a number of aliases that acts\n# as commands in the documentation. An alias has the form \"name=value\".\n# For example adding \"sideeffect=\\par Side Effects:\\n\" will allow you to\n# put the command \\sideeffect (or @sideeffect) in the documentation, which\n# will result in a user-defined paragraph with heading \"Side Effects:\".\n# You can put \\n's in the value part of an alias to insert newlines.\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C\n# sources only. Doxygen will then generate output that is more tailored for C.\n# For instance, some of the names that are used will be different. The list\n# of all members will be omitted, etc.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java\n# sources only. Doxygen will then generate output that is more tailored for\n# Java. For instance, namespaces will be presented as packages, qualified\n# scopes will look different, etc.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources only. Doxygen will then generate output that is more tailored for\n# Fortran.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for\n# VHDL.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it parses.\n# With this tag you can assign which parser to use for a given extension.\n# Doxygen has a built-in mapping, but you can override or extend it using this tag.\n# The format is ext=language, where ext is a file extension, and language is one of\n# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,\n# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat\n# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C\n\nEXTENSION_MAPPING      =\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should\n# set this tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.\n# func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.\n# Doxygen will parse them like normal C++ but will assume all classes use public\n# instead of private inheritance when no explicit protection keyword is present.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate getter\n# and setter methods for a property. Setting this option to YES (the default)\n# will make doxygen to replace the get and set methods by a property in the\n# documentation. This will only work if the methods are indeed getting or\n# setting a simple type. If this is not the case, or you want to show the\n# methods anyway, you should set this option to NO.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES, then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# Set the SUBGROUPING tag to YES (the default) to allow class member groups of\n# the same type (for instance a group of public functions) to be put as a\n# subgroup of that type (e.g. under the Public Functions section). Set it to\n# NO to prevent subgrouping. Alternatively, this can be done per class using\n# the \\nosubgrouping command.\n\nSUBGROUPING            = YES\n\n# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum\n# is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically\n# be useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to\n# determine which symbols to keep in memory and which to flush to disk.\n# When the cache is full, less often used symbols will be written to disk.\n# For small to medium size projects (<1000 input files) the default value is\n# probably good enough. For larger projects a too small cache size can cause\n# doxygen to be busy swapping symbols to and from disk most of the time\n# causing a significant performance penality.\n# If the system has enough physical memory increasing the cache will improve the\n# performance by keeping more symbols in memory. Note that the value works on\n# a logarithmic scale so increasing the size by one will rougly double the\n# memory usage. The cache size is given by this formula:\n# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,\n# corresponding to a cache size of 2^16 = 65536 symbols\n\nSYMBOL_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in\n# documentation are documented, even if no documentation was available.\n# Private class members and static file members will be hidden unless\n# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES all private members of a class\n# will be included in the documentation.\n\nEXTRACT_PRIVATE        = YES\n\n# If the EXTRACT_STATIC tag is set to YES all static members of a file\n# will be included in the documentation.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)\n# defined locally in source files will be included in the documentation.\n# If set to NO only classes defined in header files are included.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. When set to YES local\n# methods, which are defined in the implementation section but not in\n# the interface are included in the documentation.\n# If set to NO (the default) only methods in the interface are included.\n\nEXTRACT_LOCAL_METHODS  = YES\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base\n# name of the file that contains the anonymous namespace. By default\n# anonymous namespace are hidden.\n\nEXTRACT_ANON_NSPACES   = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all\n# undocumented members of documented classes, files or namespaces.\n# If set to NO (the default) these members will be included in the\n# various overviews, but no documentation section is generated.\n# This option has no effect if EXTRACT_ALL is enabled.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy.\n# If set to NO (the default) these classes will be included in the various\n# overviews. This option has no effect if EXTRACT_ALL is enabled.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all\n# friend (class|struct|union) declarations.\n# If set to NO (the default) these declarations will be included in the\n# documentation.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any\n# documentation blocks found inside the body of a function.\n# If set to NO (the default) these blocks will be appended to the\n# function's detailed documentation block.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation\n# that is typed after a \\internal command is included. If the tag is set\n# to NO (the default) then the documentation will be excluded.\n# Set it to YES to include the internal documentation.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate\n# file names in lower-case letters. If set to YES upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen\n# will show members with their full class and namespace scopes in the\n# documentation. If set to YES the scope will be hidden.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen\n# will put a list of the files that are included by a file in the documentation\n# of that file.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]\n# is inserted in the documentation for inline members.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen\n# will sort the (detailed) documentation of file and class members\n# alphabetically by member name. If set to NO the members will appear in\n# declaration order.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the\n# brief documentation of file, namespace and class members alphabetically\n# by member name. If set to NO (the default) the members will appear in\n# declaration order.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the\n# hierarchy of group names into alphabetical order. If set to NO (the default)\n# the group names will appear in their defined order.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be\n# sorted by fully-qualified names, including namespaces. If set to\n# NO (the default), the class list will be sorted only by class name,\n# not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the\n# alphabetical list.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or\n# disable (NO) the todo list. This list is created by putting \\todo\n# commands in the documentation.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or\n# disable (NO) the test list. This list is created by putting \\test\n# commands in the documentation.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or\n# disable (NO) the bug list. This list is created by putting \\bug\n# commands in the documentation.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or\n# disable (NO) the deprecated list. This list is created by putting\n# \\deprecated commands in the documentation.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional\n# documentation sections, marked by \\if sectionname ... \\endif.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines\n# the initial value of a variable or define consists of for it to appear in\n# the documentation. If the initializer consists of more lines than specified\n# here it will be hidden. Use a value of 0 to hide initializers completely.\n# The appearance of the initializer of individual variables and defines in the\n# documentation can be controlled using \\showinitializer or \\hideinitializer\n# command in the documentation regardless of this setting.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated\n# at the bottom of the documentation of classes and structs. If set to YES the\n# list will mention the files that were used to generate the documentation.\n\nSHOW_USED_FILES        = YES\n\n# If the sources in your project are distributed over multiple directories\n# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy\n# in the documentation. The default is NO.\n\nSHOW_DIRECTORIES       = NO\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page.\n# This will remove the Files entry from the Quick Index and from the\n# Folder Tree View (if specified). The default is YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the\n# Namespaces page.  This will remove the Namespaces entry from the Quick Index\n# and from the Folder Tree View (if specified). The default is YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command <command> <input-file>, where <command> is the value of\n# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file\n# provided by doxygen. Whatever the program writes to standard output\n# is used as the file version. See the manual for examples.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by\n# doxygen. The layout file controls the global structure of the generated output files\n# in an output format independent way. The create the layout file that represents\n# doxygen's defaults, run doxygen with the -l option. You can optionally specify a\n# file name after the option, if omitted DoxygenLayout.xml will be used as the name\n# of the layout file.\n\nLAYOUT_FILE            =\n\n#---------------------------------------------------------------------------\n# configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated\n# by doxygen. Possible values are YES and NO. If left blank NO is used.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated by doxygen. Possible values are YES and NO. If left blank\n# NO is used.\n\nWARNINGS               = YES\n\n# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings\n# for undocumented members. If EXTRACT_ALL is set to YES then this flag will\n# automatically be disabled.\n\nWARN_IF_UNDOCUMENTED   = NO\n\n# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some\n# parameters in a documented function, or documenting parameters that\n# don't exist or using markup commands wrongly.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be abled to get warnings for\n# functions that are documented, but have no documentation for their parameters\n# or return value. If set to NO (the default) doxygen will only warn about\n# wrong or incomplete parameter documentation, but not about the absence of\n# documentation.\n\nWARN_NO_PARAMDOC       = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that\n# doxygen can produce. The string should contain the $file, $line, and $text\n# tags, which will be replaced by the file and line number from which the\n# warning originated and the warning text. Optionally the format may contain\n# $version, which will be replaced by the version of the file (if it could\n# be obtained via FILE_VERSION_FILTER)\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning\n# and error messages should be written. If left blank the output is written\n# to stderr.\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag can be used to specify the files and/or directories that contain\n# documented source files. You may enter file names like \"myfile.cpp\" or\n# directories like \"/usr/src/myproject\". Separate the files or directories\n# with spaces.\n\nINPUT                  = @OpenMW_SOURCE_DIR@/apps \\\n                         @OpenMW_SOURCE_DIR@/components \\\n                         @OpenMW_SOURCE_DIR@/libs \\\n                         @OpenMW_BINARY_DIR@/docs/mainpage.hpp\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is\n# also the default input encoding. Doxygen uses libiconv (or the iconv built\n# into libc) for the transcoding. See https://www.gnu.org/software/libiconv for\n# the list of possible encodings.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp\n# and *.h) to filter out the source-files in the directories. If left\n# blank the following patterns are tested:\n# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx\n# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.java \\\n                         *.ii \\\n                         *.ixx \\\n                         *.ipp \\\n                         *.i++ \\\n                         *.inl \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.idl \\\n                         *.odl \\\n                         *.cs \\\n                         *.php \\\n                         *.php3 \\\n                         *.inc \\\n                         *.m \\\n                         *.mm \\\n                         *.dox \\\n                         *.py \\\n                         *.f90 \\\n                         *.f \\\n                         *.vhd \\\n                         *.vhdl\n\n# The RECURSIVE tag can be used to turn specify whether or not subdirectories\n# should be searched for input files as well. Possible values are YES and NO.\n# If left blank NO is used.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used select whether or not files or\n# directories that are symbolic links (a Unix filesystem feature) are excluded\n# from the input.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories. Note that the wildcards are matched\n# against the file with absolute path, so to exclude all test directories\n# for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or\n# directories that contain example code fragments that are included (see\n# the \\include command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp\n# and *.h) to filter out the source-files in the directories. If left\n# blank all files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude\n# commands irrespective of the value of the RECURSIVE tag.\n# Possible values are YES and NO. If left blank NO is used.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or\n# directories that contain image that are included in the documentation (see\n# the \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command <filter> <input-file>, where <filter>\n# is the value of the INPUT_FILTER tag, and <input-file> is the name of an\n# input file. Doxygen will then use the output that the filter program writes\n# to standard output.  If FILTER_PATTERNS is specified, this tag will be\n# ignored.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis.  Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match.  The filters are a list of the form:\n# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further\n# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER\n# is applied to all files.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will be used to filter the input files when producing source\n# files to browse (i.e. when SOURCE_BROWSER is set to YES).\n\nFILTER_SOURCE_FILES    = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will\n# be generated. Documented entities will be cross-referenced with these sources.\n# Note: To get rid of all source code in the generated output, make sure also\n# VERBATIM_HEADERS is set to NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body\n# of functions and classes directly in the documentation.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct\n# doxygen to hide any special comment blocks from generated source code\n# fragments. Normal C and C++ comments will always remain visible.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES\n# then for each documented function all documented\n# functions referencing it will be listed.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES\n# then for each documented function all documented entities\n# called/used by that function will be listed.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)\n# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from\n# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will\n# link to the source code.  Otherwise they will link to the documentation.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code\n# will point to the HTML generated by the htags(1) tool instead of doxygen\n# built-in source browser. The htags tool is part of GNU's global source\n# tagging system (see https://www.gnu.org/software/global/global.html). You\n# will need version 4.8.6 or higher.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen\n# will generate a verbatim copy of the header file for each class for\n# which an include is specified. Set to NO to disable this.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index\n# of all compounds will be generated. Enable this if the project\n# contains a lot of classes, structs, unions or interfaces.\n\nALPHABETICAL_INDEX     = NO\n\n# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then\n# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns\n# in which this list will be split (can be a number in the range [1..20])\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all\n# classes will be put under the same header in the alphabetical index.\n# The IGNORE_PREFIX tag can be used to specify one or more prefixes that\n# should be ignored while generating the index headers.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES (the default) Doxygen will\n# generate HTML output.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `html' will be used as the default path.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for\n# each generated HTML page (for example: .htm,.php,.asp). If it is left blank\n# doxygen will generate files with .html extension.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a personal HTML header for\n# each generated HTML page. If it is left blank doxygen will generate a\n# standard header.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a personal HTML footer for\n# each generated HTML page. If it is left blank doxygen will generate a\n# standard footer.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading\n# style sheet that is used by each HTML page. It can be used to\n# fine-tune the look of the HTML output. If the tag is left blank doxygen\n# will generate a default style sheet. Note that doxygen will try to copy\n# the style sheet file to the HTML output directory, so don't put your own\n# stylesheet in the HTML output directory as well, or it will be erased!\n\nHTML_STYLESHEET        =\n\n# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,\n# files or namespaces will be aligned in HTML using tables. If set to\n# NO a bullet list will be used.\n\nHTML_ALIGN_MEMBERS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded. For this to work a browser that supports\n# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox\n# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files\n# will be generated that can be used as input for Apple's Xcode 3\n# integrated development environment, introduced with OSX 10.5 (Leopard).\n# To create a documentation set, doxygen will generate a Makefile in the\n# HTML output directory. Running make will produce the docset in that\n# directory and running \"make install\" will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find\n# it at startup.\n# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.\n\nGENERATE_DOCSET        = NO\n\n# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the\n# feed. A documentation feed provides an umbrella under which multiple\n# documentation sets from a single provider (such as a company or product suite)\n# can be grouped.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that\n# should uniquely identify the documentation set bundle. This should be a\n# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen\n# will append .docset to the name.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# If the GENERATE_HTMLHELP tag is set to YES, additional index files\n# will be generated that can be used as input for tools like the\n# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)\n# of the generated HTML documentation.\n\nGENERATE_HTMLHELP      = NO\n\n# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can\n# be used to specify the file name of the resulting .chm file. You\n# can add a path in front of the file if the result should not be\n# written to the html output directory.\n\nCHM_FILE               =\n\n# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can\n# be used to specify the location (absolute path including file name) of\n# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run\n# the HTML help compiler on the generated index.hhp.\n\nHHC_LOCATION           =\n\n# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag\n# controls if a separate .chi index file is generated (YES) or that\n# it should be included in the master .chm file (NO).\n\nGENERATE_CHI           = NO\n\n# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING\n# is used to encode HtmlHelp index (hhk), content (hhc) and project file\n# content.\n\nCHM_INDEX_ENCODING     =\n\n# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag\n# controls whether a binary table of contents is generated (YES) or a\n# normal table of contents (NO) in the .chm file.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members\n# to the contents of the HTML help documentation and to the tree view.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER\n# are set, an additional index file will be generated that can be used as input for\n# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated\n# HTML documentation.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can\n# be used to specify the file name of the resulting .qch file.\n# The path specified is relative to the HTML output folder.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating\n# Qt Help Project output. For more information please see\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace\n\nQHP_NAMESPACE          =\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating\n# Qt Help Project output. For more information please see\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.\n# For more information please see\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see\n# <a href=\"https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters\">Qt Help Project / Custom Filters</a>.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's\n# filter section matches.\n# <a href=\"https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes\">Qt Help Project / Filter Attributes</a>.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can\n# be used to specify the location of Qt's qhelpgenerator.\n# If non-empty doxygen will try to run qhelpgenerator on the generated\n# .qhp file.\n\nQHG_LOCATION           =\n\n# The DISABLE_INDEX tag can be used to turn on/off the condensed index at\n# top of each HTML page. The value NO (the default) enables the index and\n# the value YES disables it.\n\nDISABLE_INDEX          = NO\n\n# This tag can be used to set the number of enum values (range [1..20])\n# that doxygen will group on one line in the generated HTML documentation.\n\nENUM_VALUES_PER_LINE   = 4\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information.\n# If the tag value is set to FRAME, a side panel will be generated\n# containing a tree-like index structure (just like the one that\n# is generated for HTML Help). For this to work a browser that supports\n# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,\n# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are\n# probably better off using the HTML help feature. Other possible values\n# for this tag are: HIERARCHIES, which will generate the Groups, Directories,\n# and Class Hierarchy pages using a tree view instead of an ordered list;\n# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which\n# disables this behavior completely. For backwards compatibility with previous\n# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE\n# respectively.\n\nGENERATE_TREEVIEW      = NONE\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be\n# used to set the initial width (in pixels) of the frame in which the tree\n# is shown.\n\nTREEVIEW_WIDTH         = 250\n\n# Use this tag to change the font size of Latex formulas included\n# as images in the HTML documentation. The default is 10. Note that\n# when you change the font size after a successful doxygen run you need\n# to manually remove any form_*.png images from the HTML output directory\n# to force them to be regenerated.\n\nFORMULA_FONTSIZE       = 10\n\n#---------------------------------------------------------------------------\n# configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will\n# generate Latex output.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `latex' will be used as the default path.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked. If left blank `latex' will be used as the default command name.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to\n# generate index for LaTeX. If left blank `makeindex' will be used as the\n# default command name.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact\n# LaTeX documents. This may be useful for small projects and may help to\n# save some trees in general.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used\n# by the printer. Possible values are: a4, a4wide, letter, legal and\n# executive. If left blank a4wide will be used.\n\nPAPER_TYPE             = a4wide\n\n# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX\n# packages that should be included in the LaTeX output.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for\n# the generated latex document. The header should contain everything until\n# the first chapter. If it is left blank doxygen will generate a\n# standard header. Notice: only use this tag if you know what you are doing!\n\nLATEX_HEADER           =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated\n# is prepared for conversion to pdf (using ps2pdf). The pdf file will\n# contain links (just like the HTML output) instead of page references\n# This makes the output suitable for online browsing using a pdf viewer.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of\n# plain latex in the generated Makefile. Set this option to YES to get a\n# higher quality PDF documentation.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\\\batchmode.\n# command to the generated LaTeX files. This will instruct LaTeX to keep\n# running if errors occur, instead of asking the user for help.\n# This option is also used when generating formulas in HTML.\n\nLATEX_BATCHMODE        = NO\n\n# If LATEX_HIDE_INDICES is set to YES then doxygen will not\n# include the index chapters (such as File Index, Compound Index, etc.)\n# in the output.\n\nLATEX_HIDE_INDICES     = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output\n# The RTF output is optimized for Word 97 and may not look very pretty with\n# other RTF readers or editors.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `rtf' will be used as the default path.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES Doxygen generates more compact\n# RTF documents. This may be useful for small projects and may help to\n# save some trees in general.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated\n# will contain hyperlink fields. The RTF file will\n# contain links (just like the HTML output) instead of page references.\n# This makes the output suitable for online browsing using WORD or other\n# programs which support those fields.\n# Note: wordpad (write) and others do not support links.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# config file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an rtf document.\n# Syntax is similar to doxygen's config file.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES (the default) Doxygen will\n# generate man pages\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `man' will be used as the default path.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to\n# the generated man pages (default is the subroutine's section .3)\n\nMAN_EXTENSION          = .3\n\n# If the MAN_LINKS tag is set to YES and Doxygen generates man output,\n# then it will generate one additional man file for each entity\n# documented in the real man page(s). These additional files\n# only source the real man page, but without them the man command\n# would be unable to find the correct page. The default is NO.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES Doxygen will\n# generate an XML file that captures the structure of\n# the code including all documentation.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `xml' will be used as the default path.\n\nXML_OUTPUT             = xml\n\n# The XML_SCHEMA tag can be used to specify an XML schema,\n# which can be used by a validating XML parser to check the\n# syntax of the XML files.\n\nXML_SCHEMA             =\n\n# The XML_DTD tag can be used to specify an XML DTD,\n# which can be used by a validating XML parser to check the\n# syntax of the XML files.\n\nXML_DTD                =\n\n# If the XML_PROGRAMLISTING tag is set to YES Doxygen will\n# dump the program listings (including syntax highlighting\n# and cross-referencing information) to the XML output. Note that\n# enabling this will significantly increase the size of the XML output.\n\nXML_PROGRAMLISTING     = YES\n\n#---------------------------------------------------------------------------\n# configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will\n# generate an AutoGen Definitions (see autogen.sf.net) file\n# that captures the structure of the code including all\n# documentation. Note that this feature is still experimental\n# and incomplete at the moment.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES Doxygen will\n# generate a Perl module file that captures the structure of\n# the code including all documentation. Note that this\n# feature is still experimental and incomplete at the\n# moment.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES Doxygen will generate\n# the necessary Makefile rules, Perl scripts and LaTeX code to be able\n# to generate PDF and DVI output from the Perl module output.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be\n# nicely formatted so it can be parsed by a human reader.  This is useful\n# if you want to understand what is going on.  On the other hand, if this\n# tag is set to NO the size of the Perl module output will be much smaller\n# and Perl will parse it just the same.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file\n# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.\n# This is useful so different doxyrules.make files included by the same\n# Makefile don't overwrite each other's variables.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will\n# evaluate all C-preprocessor directives found in the sources and include\n# files.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro\n# names in the source code. If set to NO (the default) only conditional\n# compilation will be performed. Macro expansion can be done in a controlled\n# way by setting EXPAND_ONLY_PREDEF to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES\n# then the macro expansion is limited to the macros specified with the\n# PREDEFINED and EXPAND_AS_DEFINED tags.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files\n# in the INCLUDE_PATH (see below) will be search if a #include is found.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by\n# the preprocessor.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will\n# be used.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that\n# are defined before the preprocessor is started (similar to the -D option of\n# gcc). The argument of the tag is a list of macros of the form: name\n# or name=definition (no spaces). If the definition and the = are\n# omitted =1 is assumed. To prevent a macro definition from being\n# undefined via #undef or recursively expanded use the := operator\n# instead of the = operator.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then\n# this tag can be used to specify a list of macro names that should be expanded.\n# The macro definition that is found in the sources will be used.\n# Use the PREDEFINED tag if you want to use a different macro definition.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then\n# doxygen's preprocessor will remove all function-like macros that are alone\n# on a line, have an all uppercase name, and do not end with a semicolon. Such\n# function macros are typically used for boiler-plate code, and will confuse\n# the parser if not removed.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration::additions related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES option can be used to specify one or more tagfiles.\n# Optionally an initial location of the external documentation\n# can be added for each tagfile. The format of a tag file without\n# this location is as follows:\n#   TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n#   TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where \"loc1\" and \"loc2\" can be relative or absolute paths or\n# URLs. If a location is present for each tag, the installdox tool\n# does not have to be run to correct the links.\n# Note that each tag file must have a unique name\n# (where the name does NOT include the path)\n# If a tag file is not located in the directory in which doxygen\n# is run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create\n# a tag file that is based on the input files it reads.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES all external classes will be listed\n# in the class index. If set to NO only the inherited external classes\n# will be listed.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will\n# be listed.\n\nEXTERNAL_GROUPS        = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of `which perl').\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will\n# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base\n# or super classes. Setting the tag to NO turns the diagrams off. Note that\n# this option is superseded by the HAVE_DOT option below. This is only a\n# fallback. It is recommended to install and use dot, since it yields more\n# powerful graphs.\n\nCLASS_DIAGRAMS         = NO\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. Doxygen will then run the mscgen tool (see\n# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the\n# documentation. The MSCGEN_PATH tag allows you to specify the directory where\n# the mscgen tool resides. If left empty the tool is assumed to be found in the\n# default search path.\n\nMSCGEN_PATH            =\n\n# If set to YES, the inheritance and collaboration graphs will hide\n# inheritance and usage relations if the target is undocumented\n# or is not a class.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz, a graph visualization\n# toolkit from AT&T and Lucent Bell Labs. The other options in this section\n# have no effect if this option is set to NO (the default)\n\nHAVE_DOT               = YES\n\n# By default doxygen will write a font called FreeSans.ttf to the output\n# directory and reference it in all dot files that doxygen generates. This\n# font does not include all possible unicode characters however, so when you need\n# these (or just want a differently looking font) you can specify the font name\n# using DOT_FONTNAME. You need need to make sure dot is able to find the font,\n# which can be done by putting it in a standard location or by setting the\n# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory\n# containing the font.\n\nDOT_FONTNAME           = FreeSans\n\n# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.\n# The default size is 10pt.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the output directory to look for the\n# FreeSans.ttf font (which doxygen will put there itself). If you specify a\n# different font using DOT_FONTNAME you can set the path where dot\n# can find it using this tag.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen\n# will generate a graph for each documented class showing the direct and\n# indirect inheritance relations. Setting this tag to YES will force the\n# the CLASS_DIAGRAMS tag to NO.\n\nCLASS_GRAPH            = NO\n\n# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen\n# will generate a graph for each documented class showing the direct and\n# indirect implementation dependencies (inheritance, containment, and\n# class references variables) of the class with other documented classes.\n\nCOLLABORATION_GRAPH    = NO\n\n# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen\n# will generate a graph for groups, showing the direct groups dependencies\n\nGROUP_GRAPHS           = NO\n\n# If the UML_LOOK tag is set to YES doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n\nUML_LOOK               = NO\n\n# If set to YES, the inheritance and collaboration graphs will show the\n# relations between templates and their instances.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT\n# tags are set to YES then doxygen will generate a graph for each documented\n# file showing the direct and indirect include dependencies of the file with\n# other documented files.\n\nINCLUDE_GRAPH          = NO\n\n# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and\n# HAVE_DOT tags are set to YES then doxygen will generate a graph for each\n# documented header file showing the documented files that directly or\n# indirectly include this file.\n\nINCLUDED_BY_GRAPH      = NO\n\n# If the CALL_GRAPH and HAVE_DOT options are set to YES then\n# doxygen will generate a call dependency graph for every global function\n# or class method. Note that enabling this option will significantly increase\n# the time of a run. So in most cases it will be better to enable call graphs\n# for selected functions only using the \\callgraph command.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then\n# doxygen will generate a caller dependency graph for every global function\n# or class method. Note that enabling this option will significantly increase\n# the time of a run. So in most cases it will be better to enable caller\n# graphs for selected functions only using the \\callergraph command.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen\n# will graphical hierarchy of all classes instead of a textual one.\n\nGRAPHICAL_HIERARCHY    = NO\n\n# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES\n# then doxygen will show the dependencies a directory has on other directories\n# in a graphical way. The dependency relations are determined by the #include\n# relations between the files in the directories.\n\nDIRECTORY_GRAPH        = NO\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. Possible values are png, jpg, or gif\n# If left blank png will be used.\n\nDOT_IMAGE_FORMAT       = png\n\n# The tag DOT_PATH can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the\n# \\dotfile command).\n\nDOTFILE_DIRS           =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of\n# nodes that will be shown in the graph. If the number of nodes in a graph\n# becomes larger than this value, doxygen will truncate the graph, which is\n# visualized by representing a node as a red box. Note that doxygen if the\n# number of direct children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note\n# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the\n# graphs generated by dot. A depth value of 3 means that only nodes reachable\n# from the root by following a path via at most 3 edges will be shown. Nodes\n# that lay further from the root node will be omitted. Note that setting this\n# option to 1 or 2 may greatly reduce the computation time needed for large\n# code bases. Also note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not\n# seem to support this out of the box. Warning: Depending on the platform used,\n# enabling this option may lead to badly anti-aliased labels on the edges of\n# a graph (i.e. they become hard to read).\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10)\n# support this, this feature is disabled by default.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will\n# generate a legend page explaining the meaning of the various boxes and\n# arrows in the dot generated graphs.\n\nGENERATE_LEGEND        = NO\n\n# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will\n# remove the intermediate dot files that are used to generate\n# the various graphs.\n\nDOT_CLEANUP            = YES\n\n#---------------------------------------------------------------------------\n# Options related to the search engine\n#---------------------------------------------------------------------------\n\n# The SEARCHENGINE tag specifies whether or not a search engine should be\n# used. If set to NO the values of all tags below this one will be ignored.\n\nSEARCHENGINE           = NO\n"
  },
  {
    "path": "docs/license/RussoOne Font License.txt",
    "content": "Copyright (c) 2011-2012, Jovanny Lemonad (jovanny.ru), with Reserved Font Name \"Russo\"\r\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\r\nThis license is copied below, and is also available with a FAQ at:\r\nhttp://scripts.sil.org/OFL\r\n\r\n\r\n-----------------------------------------------------------\r\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\r\n-----------------------------------------------------------\r\n\r\nPREAMBLE\r\nThe goals of the Open Font License (OFL) are to stimulate worldwide\r\ndevelopment of collaborative font projects, to support the font creation\r\nefforts of academic and linguistic communities, and to provide a free and\r\nopen framework in which fonts may be shared and improved in partnership\r\nwith others.\r\n\r\nThe OFL allows the licensed fonts to be used, studied, modified and\r\nredistributed freely as long as they are not sold by themselves. The\r\nfonts, including any derivative works, can be bundled, embedded, \r\nredistributed and/or sold with any software provided that any reserved\r\nnames are not used by derivative works. The fonts and derivatives,\r\nhowever, cannot be released under any other type of license. The\r\nrequirement for fonts to remain under this license does not apply\r\nto any document created using the fonts or their derivatives.\r\n\r\nDEFINITIONS\r\n\"Font Software\" refers to the set of files released by the Copyright\r\nHolder(s) under this license and clearly marked as such. This may\r\ninclude source files, build scripts and documentation.\r\n\r\n\"Reserved Font Name\" refers to any names specified as such after the\r\ncopyright statement(s).\r\n\r\n\"Original Version\" refers to the collection of Font Software components as\r\ndistributed by the Copyright Holder(s).\r\n\r\n\"Modified Version\" refers to any derivative made by adding to, deleting,\r\nor substituting -- in part or in whole -- any of the components of the\r\nOriginal Version, by changing formats or by porting the Font Software to a\r\nnew environment.\r\n\r\n\"Author\" refers to any designer, engineer, programmer, technical\r\nwriter or other person who contributed to the Font Software.\r\n\r\nPERMISSION & CONDITIONS\r\nPermission is hereby granted, free of charge, to any person obtaining\r\na copy of the Font Software, to use, study, copy, merge, embed, modify,\r\nredistribute, and sell modified and unmodified copies of the Font\r\nSoftware, subject to the following conditions:\r\n\r\n1) Neither the Font Software nor any of its individual components,\r\nin Original or Modified Versions, may be sold by itself.\r\n\r\n2) Original or Modified Versions of the Font Software may be bundled,\r\nredistributed and/or sold with any software, provided that each copy\r\ncontains the above copyright notice and this license. These can be\r\nincluded either as stand-alone text files, human-readable headers or\r\nin the appropriate machine-readable metadata fields within text or\r\nbinary files as long as those fields can be easily viewed by the user.\r\n\r\n3) No Modified Version of the Font Software may use the Reserved Font\r\nName(s) unless explicit written permission is granted by the corresponding\r\nCopyright Holder. This restriction only applies to the primary font name as\r\npresented to the users.\r\n\r\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\r\nSoftware shall not be used to promote, endorse or advertise any\r\nModified Version, except to acknowledge the contribution(s) of the\r\nCopyright Holder(s) and the Author(s) or with their explicit written\r\npermission.\r\n\r\n5) The Font Software, modified or unmodified, in part or in whole,\r\nmust be distributed entirely under this license, and must not be\r\ndistributed under any other license. The requirement for fonts to\r\nremain under this license does not apply to any document created\r\nusing the Font Software.\r\n\r\nTERMINATION\r\nThis license becomes null and void if any of the above conditions are\r\nnot met.\r\n\r\nDISCLAIMER\r\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\r\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\r\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\r\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\r\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\r\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\r\nOTHER DEALINGS IN THE FONT SOFTWARE.\r\n"
  },
  {
    "path": "docs/mainpage.hpp.cmake",
    "content": "/// \\mainpage\n///\n/// This is the source documentation for:\n///\n/// OpenMW @OPENMW_VERSION@\n"
  },
  {
    "path": "docs/openmw-stage1.md",
    "content": "# Preamble\n\nThis document outlines the development of OpenMW after the 1.0 release.\n\nFor the first couple of feature releases after 1.0 we should focus on giving mod developers additional tools so they can produce interesting mods with features not supported in vanilla Morrowind. If we look at the use of Morrowind (e.g. on Twitch) we see that people still overwhelmingly make use of the vanilla engine instead of OpenMW. We want to make OpenMW the default choice when playing Morrowind-content. The way to achieve this goal are interesting and desirable mods that require OpenMW.\n\nNow is not the time for sweeping architectural changes. With 1.0 we acquired an enormous advantage over vanilla Morrowind (full source access) and now we should cash in on that advantage by pushing out large numbers of features that are directly visible to the user.\n\nAt the same time we want to stay true to our origins. While we want to broaden the scope of OpenMW and make it into a more general purpose engine (in the sense that it is used for more than just playing Morrowind), it should still be a Morrowindesque engine. The general direction should be to widen and to generalise. We aim to evolve, not to revolutionise.\n\nOur goal here is to make OpenMW into a general purpose engine, but a general purpose 1st/3rd person real-time RPG engine. We do not attempt to support other genres or other flavours of RPG.\n\nThe development of OpenMW will hopefully continue for a long time and we can not reasonably hope to sketch out its entire future development in a single design document. Therefore this document should be seen as stage 1 of the post 1.0 development only. It may last us 6 months or a year or several years, depending on how much development activity we can achieve. But eventually there will be a stage 2 design document.\n\n# Definitions\n\n## Instances & Objects\n\nThe terminology regarding instances and objects is somewhat confusing since there are several variants of it that are incompatible with each other. Throughout this document we will use the following variant:\n\n* Instance: The sub-records placed within a cell record. Mostly called references in the vanilla Morrowind terminology. In C++ OOP we would rather call this an object or an instance.\n* Object: Records like doors, activators, containers and so on. Mostly called IDs in the vanilla Morrowind terminology. In C++ OOP we would rather call this a class.\n\nIn a few cases the term \"object\" will be used in the classical OOP sense. Any such mentioning will be accompanied by a statement about the different use.\n\n## Deprecation\n\nThis document declares some features and functions as deprecated. This usage is somewhat different from the usual meaning in software development. Usually something being deprecated not only means that its use is discouraged but also that it may be removed in later versions. In OpenMW we can not ever do the later because that would break compatibility with older content.\n\nInstead a feature being deprecated shall mean the following:\n\n* We discourage the use of the feature (and may reinforce this position by non-intrusive warning messages at some point)\n* We will not extend the feature to work with other enhancements of OpenMW.\n\nA hypothetical example: We deprecate the MessageBox instruction. If we introduce new variable types, the MessageBox text formatting will not be extended to cover these.\n\n## Script Instructions and Script Functions\n\nWe define a script instruction as a statement that does not have any return value and therefore can't be used as part of an expression. We define script functions as a function that does have a return value. Sometimes the term script instruction is used to mean both instructions and functions (since we do not have a term that covers both).\n\nWe also use the term script function for user defined scripts that are used as functions.\n\nThe meaning should be clear from context.\n\n# Organisation & Procedures\n\nThis document describes the core enhancements for the next couple of feature releases after 1.0. This does not mean that these releases are limited to what is described here. We have a whole feature request forum full of ideas and many issues on the bug-tracker. But we need to be careful here about what we add and what not.\n\nWith 1.0 we have a clear goal in front of us. After 1.0 things are less clear. We need to consider that any feature we add now and that influences the file formats will stay forever. We can not ever remove features of this kind because that would break compatibility with older content files. Even with features that don't influence the file format we should consider carefully so the engine does not end up as a bloated mess.\n\n## Versioning\n\nWe continue with the semantic versioning scheme, meaning the next feature releases will be 1.1.0, 1.2.0, 1.3.0 and so on.\n\nWe will only jump to 2.0.0 if a fundamental change is made (e.g. multiplayer). Otherwise we will continue, even if that means we end up with version 1.314159.0 eventually.\n\nOpenMW and OpenMW-CS will be kept in sync after 1.0.\n\n**Important**: If OpenMW reaches 1.0 before OpenMW-CS, we need to wait for OpenMW-CS to catch up before we continue with feature development.\n\n## Bug Fix Releases\n\nWe may decide to have regular bug fix releases in parallel to the feature release development, i.e. while we work on 1.2.0 there could be 1.1.1, 1.1.2, 1.1.3 and so on. If OpenMW is adopted more widely and the development of feature releases takes a longer time this approach might be beneficial to the end users. We would then continue to develop on the release branch of the feature release which then becomes a maintenance branch. This could either happen via (i) merging bug fix commits into the maintenance branch and then periodically merge the maintenance branch into master or (ii) merge bug fix commits both into the maintenance branch and master.\n\nHowever this approach means additional work and would be of limited usefulness if we have very quick feature releases. We should hear the people involved in the release process on this topic before we make a decision, because they would be the ones having to do most of the additional work.\n\n## Roadmap\n\nWe will continue with the revised roadmap scheme. This means we add a new label (stage1). After we are finished discussing this design document and have made necessary adjustments we will cut it up into individual tasks and add them to the tracker with the stage1 label. This label will then take the role of the current 1.0 label.\nConfirmed bug reports also get tagged with stage1. Other issues (feature requests and tasks) only after we have approved them for near future development.\nWe will most likely not have a separate stage1 label for the editor, since for the bulk of the changes (The Grand De-hardcoding) most tasks we will require changes to both OpenMW and the editor.\n\n# Content File Format & Namespaces\n\nWe will continue with the same general structure of the content file format. All changes will be specific to individual record types.\n\nWe already have a new version counter in the omwaddon and omwgame file formats. We will continue to increase this counter with every new feature release that requires additions or modifications to the file format.\n\nThe ability to read files of older formats must be maintained. The ability of the editor to write older file formats is optional and unless there is strong demand and enough manpower we will most like not support this feature.\n\n## Records\n\nRecords from older file formats are adjusted and converted during the load process (both in OpenMW and OpenMW-CS). The data structures in OpenMW and OpenMW-CS will always match the most up to date version.\n\nThe following cases of changes to the record format have been identified:\n\n* New optional records: No action on load required since the older formats won't have this kind of record.\n* New mandatory records: Records are build from cfg values or/and GMST values. Typically such a record will merge several values from these sources. We create these records only if the omwgame file is of a version prior to the introduction of this record. We need to modify the record when omwaddon files provide their own record for the value sources (i.e. GMST records).\n* Existing records with new optional fields: No action on load required. We already support this.\n* Existing records with new mandatory fields: Same as with new mandatory records.\n* Existing record that needs to be fully converted into a different record type: Conversion needs to be performed on load.\n* Existing record that is switched over from integer-based to ID-based indexing (this is a special case of the case above)\n* Existing records is split into two records: Split needs to be performed on load.\n\nThere have been some concerns regarding the efficiency of string based IDs (vs. integer). While there hasn't been an evidence indicating that this is actually a problem in OpenMW, it is important to point out that the move from integer to string does not mean that such a problem could not be addressed.\n\nWe already have plans to introduce a new string type for IDs, which deals with the case-issue (current std::string based implementation is error-prone and results in some very verbose code).\n\nIf we are careful when designing this new string class it will be easy to swap out the implementation from a string based one to an integer based one, where the actual strings are stored in a hash-table (similar to the FName class in Unreal Engine 4).\n\nIf we allow older format content files to depend on newer format content files the loading process will become more complicated (see the example given in the \"New mandatory records\" case). We could avoid these complications by forbidding content files from depending on content files with newer format. This limitation however would have a noticeable usability impact. This issue needs to be discussed further.\n\nAll new records will be identified by a unique string ID. We will not add any new record types that are indexed by integer values (e.g. skills) or other schemes. Descriptions of new record type in this document will usually leave out the ID field.\n\n## Namespaces\n\nWithin the editor we already have limited pre-1.0 support for namespaces (using C++ syntax). We will continue to expand on that. Namespaces are case-insensitive.\n\nNamespaces will serve as a tool to avoid naming conflicts between content files and to organise records within a content file.\n\nWe encourage a clean use of namespaces, meaning there will be no equivalent of 'using namespace'.\n\nWe will not enforce the use of a single namespace (not accounting for sub-namespaces) per content file. But we will strongly encourage this approach.\n\nThere are four special namespaces:\n\n* sys: This is equivalent to std in C++. In general only we are allowed to add new IDs to it (e.g. new GMSTs).\n* project: Records within this namespace are saved in a local project file, but may also be presented to OpenMW when starting it through OpenMW-CS.\n* session: Records with this namespace exist only for the duration of a single editing sessions and are currently not presented to OpenMW.\n* default: Previously called sys::default (see section about cells and worldspaces)\n\nTo encourage at least some degree of stylistic uniformity we will use only all lower case names for namespaces in all official documentation.\n\nHaving a record with the same ID as a namespace is allowed (see section about cells & worldspaces for an example).\n\n# Meta-Data & File Management\n\n## Identifying Meta-Data\n\nIdentifying meta-data specifies the identity of the content file in a formal way that can be utilised by various tools.\n\nThere are four parts to this meta data:\n\n* Name: A unique name that identifies the content file across all versions and all languages\n* Version: A unique version identifier. A uniform versioning scheme is required here to avoid unnecessary complexity in the tools. Most likely we will use the same semantic scheme as with OpenMW itself.\n* Language: see section about localisation\n* Multiplayer: see subsection about multiplayer in misc section\n\nThere are two possible ways to encode this meta data:\n\n1. In a new record or sub-record\n2. In the file name\n\nThe record option is more flexible and allows for extensions at a later point with the least amount of disruption. It also avoids unnecessarily cryptic file names. On the other hand we need unique file names anyway and storing the information in the file name avoids redundancy. This needs to be discussed further.\n\nThere is a large number of possible uses for this kind of meta data. Here are some examples:\n\n* Simplifying the content file selection within the launcher: All entries for the same content file can be grouped into a single row. We can then add two columns for version and language that contain a combo box with the available options. That should reduce the amount of clutter significantly and also should help to avoid user errors because it is now impossible to select two versions of the same file at once.\n* We can add a language setting to the launcher and make it automatically select the preferred language for content files (where available).\n* Tools for porting content files from one version of a dependency to another will benefit from being able to automatically identify that two different dependencies are actually just different versions of the same file.\n\n## Non-Identifying Meta-Data\n\n### License\n\nWe provide the user with a reasonable list of license choices (including a custom one where the user can provide his own license text). When the content file is packaged by the editor, a copy of the license text is included automatically. For newly created content files this field should be set to a sensible default (definitely not custom). Furthermore the merge tool can check for possible license conflicts.\n\nThis enhancement will hopefully cut down on the license wild-growth within the Morrowind modding community. If we want to get fancy with this feature we could also add a tool that helps the user to decide on a license.\n\n### List of Authors\n\nCurrently we have a single text field for the authors. This is suboptimal for community projects. With the current solution the best a community can do is to insert 'The x Team' and then maintain an external list.\n\nA better solution is to replace this field with a list of names that can be automatically merged (including checking for duplicates) when merging content files.\nThe editor can be configured with a user name, which is automatically inserted when creating a new content file or modifying an existing content file.\n\n## Resources Files\n\n### Problem\n\nCurrently all resources files of all installed content files are dumped into a single directory. This poses several problems:\n\n* A content file that comes with resources files that override other resources files can not be simple switched off by disabling it in the launcher.\n* Without external record keeping there is no way of knowing which resources files to include when packaging a content file for release. This is a particularly big problem if we want to automate this process.\n* Automated tools for checking for missing resources won't be able to look at the resources for the currently selected stack of content files only, which means some problems may remain undetected.\n\n### Data Paths\n\nSome people have started to put different content files into separate directories, each with its own data path. This is not a solution.\n\nThe selection of data path is a separate stage from the selection of content files. More precisely the available selection of content files depends on the selection of data paths. This results in a two stage content selection mechanism, which is undesirable from a usability perspective.\n\n### Solution\n\nThe solution is to group resources files within the data directory structure.\n\nThe first proposed solution (putting all resources files into a single archive with the same name as the content file) received a lot of criticism during forum discussions.\n\nAs an alternative we could create a single content directory (per content file) within the data directory. This content directory would then contain the content file and the usual directory structure (Icons, Meshes, Music, ...).\n\nWe may offer one of these solutions or both. This needs to be discussed further.\n\nIn the forum the following flaw in the archive solution has been brought up (applies equally to the directory solution):\n\nIn some cases one may want to use only the resources from a content file without the actual content file and license reason may stop the repackaging in a form that allows this kind of usage within the new system.\n\nThere is an easy fix for this issue: We add a flag to the dependency list in each content file. If this flag is set for a dependency, only the resources are used and the actual content file is ignored.\n\n### Importing & Backwards-Compatibility\n\nMost of the current content exists in a form that is not compatible with the new system.\n\nIn most cases this can easily be resolved by adding an importer tool to the launcher, that takes the content and copies it into the desired organisation scheme (archive or directory).\n\nFor other content (pre-existing data directories and Morrowind, since Morrowind and its add-ons are installed into the same directory) we need to maintain a level of compatibility with the old approach, that can be switched on and off as needed.\n\n### Resources Packages\n\nDuring the discussion about the new content file organisation scheme (omwgame, omwaddon) there was some criticism about the scheme not allowing plug-ins with no dependencies.\n\nThis is not a problem, because such plug-ins are generally useless. A plug-in without any dependency can not modify or reference any record. That was not such a large issue with vanilla Morrowind, because in this case the plug-in could have still have depended on things like attributes or dynamic stats. However in OpenMW these will all turn into records and therefore can not be accessed from within an omwaddon file without dependencies. This will only get more extreme as the de-hardcoding progresses further. An omwaddon file without dependencies can do literally nothing, which makes it useless by definition.\n\nBut there is one exception. Some members of the mod community have provided resources packages (meshes and similar) that can be used by other content developers. Under our new resources scheme these would have to be accompanied by an omwaddon file. This is not a good solution because of two reasons:\n\n* a omwgame file may need to depend on such a resource pack (currently impossible)\n* a resource pack can be used by omwaddon files that depend on any omwgame file (also currently impossible)\n\nTherefore we will introduce resources packs as a third kind of content type. Resources packs do not have dependencies and they can be placed in any position in the load order (provided the load order fulfils the dependency requirements of the content files depending on the resources pack). This will require some adjustments to the content file selector GUI.\n\nNote that resources from a resources pack can still override resources from other resources packs and other content files earlier in the load order. Therefore the load order is highly relevant for resources packs too.\n\nA resources pack consist of an omwres content file and the associated resources files. If we choose the file-name variant for identifying meta-data and the archive implementation path (vs sub-directory), we could theoretically skip the omwres content file and just make the resources archive the omwres file instead. However this is not a good idea, because that would leave us no way to carry along author and license information.\n\n## Packaging\n\nThe editor will be enhanced with a packaging tool that can take a content file and its associated resources files and package them into a form suitable for distribution.\n\nThe question now is what is a suitable form. For Linux the first option that comes to mind are the respective software package management systems. This of course does not help Windows and Mac users. While there is the obvious and very reasonable answer (\"Get a real operating system!\") this seems unnecessarily harsh to Mac users and is overall not helpful.\n\nTherefore we will develop our own cross-platform package format for OpenMW content. The exact scope still needs to be determined, but it could be as simple as an archive that holds the content file and the associated resources files. Giving this archive type a unique file extension (e.g. omwpack) would then allow the launcher to pick up opening requests (i.e. double click on the file) and install the content (optionally including a check for dependencies).\n\n# Scripting\n\n**Note**: Extensions to the scripting language in form of new instructions and functions are distributed over this entire document. In some cases, features may require additional functions that return the value of certain fields of certain records. These functions are usually not listed explicitly and are left as an exercise to the reader.\n\n## Language Version\n\nThe version of the language used in a script is determined by a single integer number which is stored in a new subrecord within the script record. Note that this is different from earlier concepts, which used a more complicated version identifier and stored it within the actual script.\n\nScripts that are missing this subrecord are considered version 0 (legacy). New scripts (when created in OpenMW-CS) default to the most up-to-date version. The version can be selected from a combo box in the script subview. We may add a user setting that hides the combo box UI if the version is the most up to date one.\n\nThe version number is incremented whenever an addition to the scripting language is made (at most once per feature release). A new feature release is not required to increment the version number, if no feature changes have been made to the language in this release.\n\nFrom version 1 on, all workarounds for bad scripts will be disabled. This should not break anything for well-written scripts. In general, moving to a new language version should usually at most require minor fixes.\n\nSince old scripts will remain with the old language version (unless there is a need to modify them and use newer features), this scheme will not break compatibility for existing content.\n\n## Error Handling\n\nWe will continue with the existing runtime error handling scheme (i.e., stopping the script execution on a runtime error). Introducing exception handling instead would be overkill.\n\nWe will do our best to avoid crashes as a result of broken scripts. That means (among others) that we need to put an artificial (configurable) limit on function call depth to avoid crashes and lock-ups from endless script recursion. We may also decide to put a similar limit on loops.\n\n## Variable Types\n\nThe types `long` and `float` remain unchanged.\n\nWe will flag `short` as deprecated. Currently `short` has no advantage over `long` (it uses the same amount of memory in OpenMW). For anything but a very specific kind of bit-cutting the `short` type has no use at all.\nIf we ever decide that we do want a shorter integer type we can always de-deprecate `short`.\n\nWe will introduce several new types:\n\n### Strings\n\nThe name of the new string type is `String`, and string literals are marked by placing them in quotation marks. Example:\n\n````\nString a\nSet a to \"Some Text\"\n````\n\nComparison operators for strings are provided in the usual way.\n\nBecause we are discontinuing bad script workarounds, most ambiguity regarding string literals should have been removed already (we will not accept local variable names and instructions within quotation marks any more). Only a single problem case remains:\n\nIf a function requires an ID, is it a literal ID or is it the name of a string variable that contains an ID? Example:\n\n````\nString Fargoth\nSet Fargoth to \"player\"\nFargoth -> AddItem \"Gold_001\", 100\n````\n\nWe cannot make a rule that forbids the use of local variable names that are also IDs, because that would allow random content files to break scripts in other, unrelated content files.\n\nTherefore, the solution here is to simply give the local variable precedence over the ID. If the script author intended to give money to `Fargoth`, he should not have created a local variable with the same name.\n\nNote that global variables are not an issue here, because global variables are IDs and IDs need to be unique (with certain exceptions that are not relevant here).\n\nTo remove even the last remaining potential problems with ambiguity, we will also introduce a new string literal that can only be a literal and never a string variable name. These literals are preceded by an `L` character. Utilising this feature, the aforementioned code block could be rewritten as is shown below, if the script author absolutely insists on having a local variable named `Fargoth`:\n\n````\nString Fargoth\nSet Fargoth to \"player\"\nL\"Fargoth\" -> AddItem \"Gold_001\", 100\n````\n\n### Instance References\n\nThe name of the new reference type is `Ref`. It can reference an instance in the world or in a container or be a null reference.\n\nSince we currently have no way to reference an instance persistently, the use of the `Ref` type is limited for the time being (see section *Variable Scope* for further details).\n\nReferences can be used in any place that would otherwise allow an ID that stand for an existing reference. The rules for strings regarding ambiguity apply to references in the same way.\n\nA reference will implicitly cast to 0 (a null reference) or 1 (not a null reference) when used in a numeric expression.\n\n**Note**: A reference pointing to the contents of a container points to a stack of items and not a single item.\n\nWe introduce the following keywords:\n\n* `Self`: A value of type `Ref` (the instance the script is currently running on, only available in local scripts and dialogue scripts)\n* `NullRef`: A literal of type `Ref` (null reference)\n* `GetId(r)`: Returns the ID of the reference `r` as a string value\n* ``GetContainer(r)`: Returns the reference of the container `r` is in (or a null reference if `r` is not in a container)\n* `GetCell(r)`: Returns the ID string of the cell `r` is in (either directly or via a container)\n* `SearchActive(id, container = 0)`: Returns a reference with the given ID within the active cells. If `container != 0`, also check in containers; returns a null reference if no reference is found\n* `SearchIn(id, id2)`: Returns a reference with the given ID within something (`id2`) that can contain references; this can be a cell, a worldspace, a container, a creature or an NPC. If `id2` represents an instance, a `Ref` variable can be given instead; returns a null reference if no reference is found\n\n### Lists\n\nA single-type list will be available for the following types:\n\n* `long`\n* `float`\n* `String`\n* `Ref`\n\nWe will not support mixed-type lists because that would require a major change to the type system. Internally and functionally, a list will work like an array. The type names for lists are:\n\n* `LongList`\n* `FloatList`\n* `StringList`\n* `RefList`\n\nList literals are given as a comma-separated list of values in square brackets. Empty list literals are allowed. In the context of lists, we generally allow implicit promotion of integer to float types.\n\nWe support the following list operations:\n\n* `l1[i]`: Returns member `i` (indexing begins at 0)\n* `GetSize(l1)`: Returns the number of elements in `l1` as an integer\n* `l1 + l2`: Returns a concatenated list consisting of the members of `l1` followed by the members of `l2`\n* `GetSubset(l1, l2)`: Returns the common subset of the elements of `l1` and `l2`\n* `HasElement(l1, e)`: Returns 0 (if `e` is not element of `l1`) or 1 (otherwise)\n* `GetIndex(l1, e)`: Returns the index of the first occurrence of `e` in `l1` (or -1 if none)\n* `Minimum(l1)`: Returns the minimum of the elements in `l1` (only works with numerical types)\n* `Maximum(l1)`: Returns the maximum of the elements in `l1` (only works with numerical types)\n\nand the following list instructions:\n\n* `Set l1[i] To e`: Sets list element `i` of list `l1` to value `e` (indexing begins at 0)\n* `Sort(l1, cf=\"\")`: Sorts list elements into ascending order (if `cf` is an empty string) or via comparison function script `cf`\n* `Filter(l1, ff)`: Filters list `l1` by filter function `ff` (only keeps elements that do not return 0)\n* `Reverse(l1)`: Reverses order of list elements of list `l1`\n* `Append(l1, e)`: Appends element `e` to list `l1`\n* `Append(l1, l2)`: Appends elements of list `l2` to list `l1`\n* `Remove(l1, e)`: Removes all elements equal to `e` from list `l1`\n* `Remove(l1, l2)`: Removes all elements from list `l1` which are also in list `l2`\n* `InsertAt(l1, e, i)`: Inserts element `e` into list `l1` at position `i` (moving the element at index `i` one position upwards and extending the size of the list by one)\n* `RemoveAt(l1, i)`: Removes the element at position `i` from list `l1` (moving elements with an index higher than `i` down by one and reducing the size of the list by one)\n* `Resize(l1, n)`: Set size of list `l1` to `n`; if the list is extended, additional elements are initialised with default values\n\nVariable names used in the lists above:\n\n* `l1` and `l2` are lists / list literals of the same element type\n* `i` is an integer value and is used as list index\n* `n` is an integer value\n* `e` is a variable/literal of the element type of `l1` and `l2`\n* `cf` is the ID of a comparison function script that takes two element-type arguments and returns an integer (-1, 0, 1)\n* `ff` is the ID of a filter function script that takes one element-type argument and returns an integer (0, 1)\n\n### List Aliases\n\nWe define three more type aliases:\n\n* `Vector3`: A `FloatList` of 3 members, used for location or rotation\n* `Vector4`: A `FloatList` of 4 members (location and rotation around z-axis)\n* `Vector6`: A `FloatList` of 6 members (location and rotation)\n\nThese aliases are not separate types.\n\nIn the specification of additional functions and instructions, values of these types are specified as `v3`, `v4`, and `v6` respectively. Passing shorter lists is allowed and will set the missing elements to 0.\n\n**Note**: All rotations are specified in degrees.\n\n## Variable Scope\n\nAll variable types will be available at all scopes with the exception of reference variables. Reference variables will be limited to true local scope (see below), because all other scopes require writing the variable to a saved game file and we currently don't have a general method to serialise instance references. This is a task for stage 2 or more likely OpenMW 2.0.\n\nThe global and the local scope remain unchanged. We will add two more scopes:\n\n### True Local Variables\n\nWhat the Morrowind scripting language describes as local variables does not match the concept of local variables in other languages. In case of local scripts, Morrowind local variables function as member variables. In case of global scripts, Morrowind local variables function as static variables.\n\nThere are several reasons for the introduction of true local variables:\n\n* Less pollution of local dialogue variable names\n* Less state that needs to be saved\n* Prevention of potential scripting errors when the state of a local variable is maintained across multiple script executions and the script does not re-initialise the variable at the beginning of a new run\n\nWe will call this scope **true local variable** to distinguish it from old local variables.\n\nDeclaration of true local variables will look like this:\n\n````\nLocal long a = 1\nLocal long b\n````\n\nIf no default value is given, the variable is default-initialised (value 0, empty string, empty list, null reference).\n\n### Record Variables\n\nA common problem (especially relevant for large projects with complex functionality) is that there is no place other than global variables to store additional state that is not specific to an instance. A global function with multiple variables can be used instead, but that is just a workaround.\n\nFurthermore, there is currently no way to add new state to other objects (object in the OOP sense, not in the Morrowind sense) other than instances.\n\nTherefore, we introduce record variables, which are a generalisation of old local variables in local scripts.\n\nThe respective records will be enhanced by an optional list of subrecords that can declare record variables and their default values.\n\nObvious candidates for this feature are:\n\n* Objects\n* Cells\n* Regions\n* Worldspaces\n* Factions\n\n**Note**: Object record variables are essentially the same as old local variables declared in a local script - only that they are declared in an object record instead.\n\nWe will introduce the following record variable functions:\n\n* `Has{Type}(vn)`: Returns 1 if record/reference has a long/float/string/list record variable of name `vn`, 0 otherwise\n* `Get{Type}(vn)`: Returns the value of record/reference variable `vn`. Note that we don't use the x.y syntax here, because we need to explicitly specify the type to allow proper compile-time error checking for reference variables.\n\nand the following record variable instructions:\n\n* `Set{Type}(vn, val)`: Sets variable `vn` to value `val`. It is considered an error if this variable does not exist.\n\nWithin all functions and instructions, the record/reference is specified via the `->` operator in the usual way. When using object records (via ID), the first instance found is used instead of the object record.\n\nVariable names used in the lists above:\n\n* `id` is a `String`\n* `vn` is a `String`\n* `ref`is a reference of type `Ref`\n* `val` is a value according to `{Type}`\n\nFinally, `{Type}` stands for `long`, `float`, `String`, `LongList`, `FloatList`, or `StringList`.\n\nWe can fold the access of local variables in global scripts into the new record variable system, even though global scripts do not have explicit record variables. The new functions for record variable access make the old x.y syntax obsolete and, therefore, we declare it as deprecated.\n\n## Control Structures\n\nWe will add a `For` loop that works on lists. To avoid unnecessary complexity in the language and to encourage the use of lists, we will not have an index-based `For` loop (these can easily be simulated via `While`).\n\nExample:\n\n````\nFor x In l\n  do something with x\nEndFor\n````\n\nWe will add the `break` and `continue` keywords both for `For` and `While` loops.\n\nWe may add a `switch-case` construct, but this is most likely a stage-2 task since there are many different ways to do `switch-case` and no obviously superior solution. This will most likely require extended discussion and design work.\n\n## General Additions\n\nThe vanilla scripting language is missing a few pieces of basic functionality. Therefore, we need to add the following kinds of instructions:\n\n* Trigonometric functions (`Cos`, `Sin`, `Tan`, `Acos`, `Asin`, `Atan`, `Atan2`, `DegToRad`, `RadToDeg`)\n* Logical boolean function (`And`, `Or`, `Not`, `Xor`)\n* Other mathematical functions (`Abs`, `Floor`, `Ceil`, `Clamp`, `Lerp`, `Sign`)\n\n## Object Management\n\nWe will add a function that moves an instance into the world. This feature (especially the `move` function) preludes a concept of persistent instance identity, which is most likely a stage-2 or OpenMW-2.0 feature. For now, these functions are required to deal with state attached to the instance in the form of record variables or old local variables respectively.\n\n* `MoveObjectToWorld(target, v6)`: Moves the instance from current location to world location `target` at coordinates `v6`\n* `CloneObjectToWorld(target, v6)`: Adds a copy of the instance `target` to world location `target` at coordinates `v6`\n\n**Note**: In all cases, reference specification for objects to be moved/cloned can be done via the `->` operator in the usual way.\n\n**Note**: Moving or cloning out of a container will only move or clone a single item, not the whole stack.\n\nThe `target` value represents the target cell or worldspace (see section *References to Cells in Data Structures* for details)\n\nWe will add replacement functions for object-placement-related functions without declaring the original functions deprecated.\n\nInstructions:\n\n* `SetRotation(v3)`: Sets the instance's rotation; actors ignore the first two elements (replaces `SetAngle`)\n* `SetPosition(v3)`: Sets the instance's position (replaces `SetPos`)\n* `Teleport(target, v6, strict = 0)`: Teleports the instance to location specified by `target` and `v6`; actors ignore the first two elements; if `strict != 0`, no corrections are made to the specified location, otherwise the location may be adjusted for safety (replaces `Position` and `PositionCell`)\n* `Spawn(objectId, target, v6, strict = 0)`: Same as `Teleport`, but spawns a new object `objectId` (replaces `PlaceItem` and `PlaceItemCell`)\n\nFunctions:\n\n* `GetRotation`: Returns the instance's rotation as a `Vector3` (replaces `GetAngle`)\n* `GetPosition`: Returns the instance's position as a `Vector3` (replaces `GetPos`)\n\n## Object-Type-Specific Additions\n\nWe will add functions to modify and query other non-item-specific state that is already present in the cellref class and therefore already written to saved game files.\n\nInstructions:\n\n* `SetTrap(trapId)`: Sets the instance's trap ID to `trapId`\n* `SetKey(trapId)`: Sets the instance's key ID to `keyId`\n* `SetTeleport(target, v4)`: Sets the instance's teleport location to the location specified by `target` and `v4` (does not affect teleport flag)\n\nFunctions:\n\n* `GetTrap`: Return the instance's trap ID\n* `GetKey`: Return the instance's key ID\n* `GetTeleportTarget`: Return the instance's teleport target\n* `GetTeleportPosition`: Returns the instance's teleport location and zrot as `Vector4`\n\n## Object-Types\n\nWe introduce compile-time constants for object types (Weapons, Activators and so on). These have integer values. The constants must be named consistently in such a way that name collisions are unlikely (e.g. `TypeWeapon`, `TypeActivator`).\n\nWe add a function that returns the type of an object or instance:\n\n* `GetObjectType`\n\n## Queries\n\nA query returns a list of instances within a certain area. Along with the description of the area, the query takes a list of object types (`t`) as an additional argument.\n\n* `QuerySphere t, v3, r`\n* `QueryBox t, v3_tl, v3_br` (this is an axis-aligned box)\n* `QueryCylinder t, v3, r, height` (the cylinder is aligned along the z-axis, `v3` specifies the middle point of the bottom circle)\n\n## Text Formatting\n\nThe vanilla scripting language provides text formatting in a single place (`MessageBox` instruction). This is insufficient:\n\n1. We will need text formatting in many places, independently of message boxes.\n2. The `MessageBox` text formatting sucks.\n\nWe will deprecate `MessageBox`. As a replacement, we will introduce new instructions for creating message boxes (see following subsection and *GUI* section) without a formatting feature and separate text formatting functions.\n\n* `Str(v, p = -1)`: Returns a `String` containing the string representation of the numeric value `v`. `p` specifies the number of digits after the decimal point. If `p == -1`, a suitable value depending on the type of `v` will be chosen\n* `LFormat(s, a)`: Returns a `String` equal to the `String` `s` with all occurrences of %x replaced with the respective element indexed x from the string list `a`\n* `Join(l, s)`: Returns a `String` consisting of the members of the `StringList` `l` separated by the `String` `s`\n\n## Multiple-Choice Message Boxes\n\nWe add two new functions for showing multiple-choice message boxes:\n\n* `MakeChoice t, sl, f`\n* `MakeBranchChoice t, sl, fl`\n\nArguments are as follows:\n\n* `t` is the message box's text\n* `sl` is a list of strings, defining the available options\n* `f` is the ID of a script function that is called when an option is selected; the function takes one `long` argument (the option index, starting at 0)\n* `fl` is a list of script function names with the same length as `sl`; the functions do not take any arguments; empty strings in the list are allowed, which makes OpenMW ignore the respective choice\n\n## Functions\n\nWe extend scripts to become callable functions with argument passing and a return type.\n\n### Declaration\n\nThe syntax of the begin statement is extended in the following way:\n\n````\nBegin <function-name> { ( <list-of-arguments> { -> return-type } ) }\n````\n\n**Note**: `{}` denotes optional parts.\n\nArgument lists are allowed to be empty and are comma-separated. Elements in argument lists are type-name pairs. Elements can be given default values in a C++ fashion.\n\nArguments and return values are for all intents and purposes true local variables. Therefore, the `Ref` type is available.\n\n### Calling\n\nA function can be called by its name followed by parentheses. Arguments are listed inside the parentheses separated by commas.\n\nWe may at some point add the use of named arguments for function calling (comparable to Python), but this is most likely a stage-2 feature.\n\nIf the function has a return type, the function is evaluated as an expression with a type corresponding to the return type.\n\nCalling a function pauses the execution of the calling script, executes the called script and then resumes the execution of the calling script. This is different from the `StartScript` and `StopScript` instructions. The `StartScript` instruction is not modified (i.e., no argument passing).\n\nA function call must be performed via a function name literal. The function name cannot be given as a string variable, since this would make it impossible to check for the correctness of the function signature at compile time.\n\nLocal and global scripts must not have any arguments without default values.\n\n## Script Hooks\n\nWe introduce two new record types (hook, hooked script). The hooked script is a generalisation of the concept of a start-up script. We may decide to fold the start-up script record type into the hook record type.\n\n### Hooked Scripts\n\nA hooked script record binds a script to a hook record. More than one hooked script record per hook record is allowed.\n\nThe ID of a hook record is built from the hook record ID followed by a `$` followed by the script name.\n\nExample:\n\n````\nStartup$KillFargoth\n````\n\nBy building hook IDs in this way, we allow content files to delete hooked script records in dependencies without the need for an additional ID that identifies individual hook records.\n\n**Note**: If more than one script is attached to a hook, the order in which the scripts are executed is, at this point, implementation-defined and, thus, scripts must not depend on a specific order.\n\n### Hooks\n\nA hook record declares a new hook. Each hook record contains a list of argument types with optional default values (again comparable to C++ function calling).\n\nThere will be no return values (problematic since there can be multiple scripts per hook).\n\nWe provide system hooks within the namespace `sys` that are called in specific situations (see the section about de-hardcoding for some examples). Content developers may also provide their own hooks (user hooks).\nSystem hooks are not added to content files, since they cannot be modified by content developers anyway. We will instead inject the system hook records on content-file load.\n\nSystem hooks are triggered by the engine. System hooks and user hooks can be triggered explicitly with a new script instruction.\n\n## Script Slots\n\nWe define the term script slot as an optional subrecord in an object record that contains the name of a script. The default script slot for most object types is the \"Run-Once-Per-Frame\" slot, a.k.a. local script.\n\nThe default slot does not take any function arguments, but other slots can. The function arguments of a script attached to a slot need to match the hardcoded argument list of the slot.\n\n**Note**: If two scripts attached to the same object both define a (non-true) local variable with the same name, there will be only one variable. It is an error if the type of these variables don't match.\n\n### Additional Slots\n\nWe add slots for `OnX`-type keywords to object types where applicable. The relevant keywords are:\n\n* `OnActivate`\n* `OnDeath`\n* `OnKnockout`\n* `OnMurder`\n* `OnPCAdd`\n* `OnPCDrop`\n* `OnPcEquip`\n* `OnHitMe`\n* `OnPCRepair`\n* `OnPCSoulGemUse`\n* `OnRepair`\n\n### Custom Slots\n\nWe may allow the addition of custom slots (defined by the content developer), though this is an advanced feature more likely to be implemented in stage 2. Further details need to be explored.\n\n## Namespaces\n\nThe namespace of a script is determined by its ID. For example a script with the ID `foo::Bar` would be placed in the namespace `foo`.\n\nIDs in a script refer to the local namespace by default, meaning the ID `a` in the script `b::c` would refer to `b::a` if such an ID exists, or to `::a` otherwise.\n\n## Other Script Instructions\n\nThis is a collection of script instructions that fit nowhere else:\n\n* `GetPcTarget`: Returns the reference of the instance the player is currently looking at (crosshair); can be a null reference\n* `GetMultiplayer`: Always returns 0\n* `GetPlayers`: Returns a list of references to all player-controlled instances; this list contains a single reference to the instance of object `Player`\n\n## Other Script Hooks\n\nThis is a collection of script hooks that fit nowhere else:\n\n* `sys::NewGameStarted`: Executed when a new game is started\n\n# Cells, Worldspaces & Areas\n\n## Interior vs. Exterior\n\nThe distinction between interior and exterior cells stopped making sense with the release of the Tribunal add-on. Therefore, we should drop this terminology and replace it with **Unpaged Cells** (interior) and **Paged Cells** (exterior).\n\n## Unpaged Cells\n\nUnpaged cells take the place of old interior cells. The only difference between old interior cells and unpaged cells is the worldspace.\n\nBy default, each unpaged cell has an associated worldspace record with the same ID. Furthermore, the unpaged-cell record is enhanced by an optional subrecord that specifies an alternative worldspace (if present).\n\nMost of the cell configuration data is moved out of the cell record and into the worldspace record.\n\nBy moving out the worldspace into a separate record, we can unify worldspace data between interior and exterior cells. We can also associate multiple interior cells with the same worldspace or interior cells with an exterior worldspace. This should reduce duplicated data significantly.\n\n## Paged Cells\n\nPaged cells remain essentially unchanged from old exterior cells, the only major difference being the ID and the worldspace.\n\nCurrently, there is no uniform ID scheme for cells. Interior cells are named via an ID, exterior cells are named via a cell index (x, y). This needs to be changed, since we need a more consistent way to address cells.\n\nThe editor already gives exterior cells an ID (currently in the form of `#x,y`). The new scheme (relevant at least for the editor and scripting) will be `worldspace::x,y` (the cell will have the ID `x,y` in the namespace `worldspace`). We may also consider to replace the two integer coordinates in the cell record with the string ID. The performance impact of this change should be insignificant.\n\nThe default exterior worldspace will be called `default` (previously called `sys::default`). Therefore, the old exterior cell 0, 0 will be called `default:0,0`. We are moving `default` out of `sys` to avoid creating an exception to the rule that content files are not allowed to create records in `sys`.\n\n## Worldspace Records\n\nWorldspace records are shared by paged and unpaged cells (i.e., there are no interior and exterior worldspaces).\n\nWorldspace records contain the following fields:\n\n* Water: Default Water Level, Water ID (see de-hardcoding). **Note**: Script instructions that modify the water level will now modify the water level of a worldspace instead the one of a cell.\n* Ambient Light: 4 values\n* Sky: Sky ID (analogous to Water ID, most likely not part of stage 1). **Note**: A worldspace cannot have both an ambient-light and a sky subrecord.\n* Terrain: No data (might be better implemented as a flag); presence indicates that worldspace has terrain; ignored by unpaged cells\n\nAll fields are optional. If a field is missing, the respective cell/worldspace element is not present.\n\n## References to Cells in Data Structures\n\nVanilla references to cells are generally based on the ID of the cell. There are no fields in vanilla Morrowind's data structures that reference individual exterior cells.\n\nWe keep these reference fields, but change their meaning:\n\n1. Check whether it is the ID of an unpaged cell. If yes, use this cell.\n2. Check whether it is a worldspace. If yes, use the paged cells in this worldspace.\n3. Otherwise, error.\n\n## Scripts\n\n### Water Level\n\nThe existing script instructions that deal with water level can be safely extended to the new system. We will add an optional argument at the end of the argument list of each instruction (`GetWaterLevel`, `ModWaterLevel`, and `SetWaterLevel`) which specifies the worldspace the instruction is acting on. If the argument is missing, the instruction affects the current worldspace instead.\n\n**Note**: This behaviour is different from vanilla Morrowind in regards to exterior cells.\n\n### New Instructions\n\nWe add the following script instructions:\n\n* `EnumerateActiveCells`: Returns a `StringList` containing the IDs of all cells currently active; paged cells are listed individually\n* `GetWorldspace(c)`: Returns the ID of the worldspace the cell with the ID `c` is in\n* `GetWater(w)`: Returns the ID of the water used in the worldspace with the ID `w`\n* `GetSky(w)`: Returns the ID of the sky used in the worldspace with the ID `w`\n\n### Script hooks\n\nWe add four new script hooks:\n\n* `RegionEntered`\n* `RegionExited`\n* `WorldspaceEntered`\n* `WorldspaceExited`\n\nAll four hooks take the ID of the region or worldspace as arguments and are executed when the player enters or exits a region or a worldspace.\n\n## Areas\n\nThe ability to designate a section of a worldspace with a specific ID that can be checked or referenced has many uses. A few examples:\n\n* Give an NPC certain dialogue topics only in specific places\n* Limit wandering NPCs to an area\n* Add special/magical effects to a location\n\nCurrently, our ability to do that is largely limited to cells. This is a problem because of two reasons:\n\n* The fixed-size, square nature of cells makes areas unreasonably inflexible.\n* We can't have overlapping areas.\n\n### Record\n\nWe introduce a new record (\"Area\") that consists of the following fields:\n\n* Worldspace ID\n* Polygon: A list of 2D coordinates defining a surface on the xy-plane\n* Min-Height: z-coordinate at which the area starts\n* Max-Height: z-coordinate at which the area ends\n* Enter script ID (string, optional): Script function that is called when the player enters the area\n* Exit script ID (string, optional): Script function that is called when the player exits the area (must also be called in case of teleportation)\n* Inside script ID (string, optional): Script function that is called while the player is in the area.\n* Inside script delta (float, optional): Minimum time between two executions of the inside-script function.\n* Composite (integer, optional): If this flag is set, the area will be ignored by OpenMW except as a part of a joined area. **Note**: Composite areas are not accessible by script instructions and do not run any scripts of their own.\n\nAll script functions take the ID of the area as a parameter.\n\nWhenever a cell or a worldspace is referenced to check or define locations, the ID of an area can be used instead.\n\n### Script Functions\n\nWe add the following script functions:\n\n* `GetAreas(v3)`: Returns a `StringList` containing the IDs of all areas `v3` is in\n* `InArea(id)`: Returns 1 (instance is in area `id`) or 0 (otherwise)\n\n## Joined Areas\n\nA joined area is an area that consists of multiple unjoined areas. Joined areas can contain both composite and non-composite areas. A joined area can stretch across multiple worldspaces. Scripts do not distinguish between areas and joined areas.\n\n### Record\n\nWe introduce a new record (\"Joined Area\") that consists of the following fields:\n\n* Enter script ID (string, optional): Script function that is called when the player enters the area\n* Exit script ID (string, optional): Script function that is called when the player exits the area (must also be called in case of teleportation)\n* Inside script ID (string, optional): Script function that is called while the player is in the area.\n* Inside script delta (float, optional): Minimum time between two executions of the inside-script function.\n\n# Item Interaction & Item Management\n\n## Deletion\n\nWe will add an instruction to delete instances via a reference variable. Current solutions are insufficient because they can not target specific items (when in a container) and require different approaches depending on whether an instance is in a container or in the world. Self-deletion must be safe. For the sake of consistency, we extend this instruction to work on non-item objects too.\n\n* `Delete` (reference specification for instance to be deleted via the `->` operator in the usual way)\n\n## Container\n\nWe will add a function that returns the contents of a container as a list of references. **Note**: In this case, the term \"container\" also includes creatures and NPCs.\n\n* `GetContents` (reference specification for container via the `->` operator in the usual way)\n\nWe will add a function that moves/clones an item instance into a container (actual container or actor). This feature (especially the `move` function) preludes a concept of persistent instance identity, which is most likely a stage-2 or OpenMW-2.0 feature. For now, these functions are required to deal with state attached to the instance in the form of record variables / old local variables. (In all cases, the reference specification for the item to be moved/cloned works via the `->` operator in the usual way.)\n\n* `MoveItemToContainer(ref/id, count = 1)`: Move item from current location to container specified by `ref/id`\n* `CloneItemToContainer(ref/id, count = 1)`: Add a copy of item to container specified by `ref/id`\n\nThe `count` argument is ignored when the original item is not in a container.\n\n## Other Item-Related Instructions\n\nInstructions:\n\n* `SetItemHealth(health)`: Sets item's current health\n* `SetItemCharge(charge)`: Sets item's current charge\n* `SetItemOwner(owner)`: Sets item's owner ID\n* `SetItemSoul(soul)`: Sets item's soul ID (soul gems only)\n* `SetItemFaction(faction)`: Sets item's faction ID\n* `SetItemFactionRank(rank)`: Sets item's faction rank\n\nFunctions:\n\n* `IsItem`: Returns 1 if reference is an item, 0 otherwise\n* `GetItemHealth`: Returns item's current health\n* `GetItemMaxHealth`: Returns item's maximum health\n* `GetItemCharge`: Returns item's current charge\n* `GetItemMaxCharge`: Returns item's maximum charge\n* `GetItemOwner`: Returns item's owner ID\n* `GetSoulItem`: Returns item's soul ID (soul gems only)\n* `GetItemFaction`: Returns item's faction ID\n* `GetItemFactionRank`: Returns item's faction rank\n\n## Item Tags\n\nCurrently, there is no customisable way of categorising items. To compensate for this shortcoming, we introduce item tags.\n\nAn item tag is a string that is attached to an object record. An object record can have multiple tags.\n\nObjects also have implicit item tags that are determined by their type (e.g., every weapon object has a tag \"Weapon\" even without the object record explicitly containing this tag). We may introduce other kinds of implicit tags (e.g. weapon types, enchanted items).\n\nThe de-hardcoding introduces additional item tags.\n\nItem tags are immutable at runtime and can be queried via script instructions:\n\n* `GetItemTags(id)`: Returns a `StringList` of tags for the object `id`; as usual, instead of an ID a reference variable can be used\n* `HasitemTag(id, tag)`: Returns 1 or 0 depending on `id` having the tag `tag`\n\nUsing these instructions on non-item objects returns an empty list or 0 respectively.\n\nItem tags can be used to organise container windows (see *GUI* section).\n\nA few examples of tags that content developers may come up with:\n\n* \"Quest\": A quest item\n* \"Vendor\": An item that has no other use than to sell it to a vendor\n\nWe may suggest some default tags in the documentation (or even the editor itself) to encourage more consistent tag use by content developers.\n\n## Interactions\n\nWe enhance the way how the player interacts with the world, especially via items.\n\n### Methods of Interactions\n\nThere are three methods of interaction:\n\n* The player presses the attack button while holding an interaction item in his hand and targeting an object in the world. This feature exists in vanilla Morrowind only within the Security skill. We generalise this kind of interaction (see *Held Items* in the *De-Hardcoding* section). We also allow this feature for weapons held in the main hand: If an interaction is possible, the interaction takes the place of the attack. If the interaction is ignored or refused, the attack proceeds.\n* The player drags an item from a container onto an instance in the world. This kind of interaction does not exist in vanilla Morrowind.\n* The player drags an item from a container onto another item in another (or the same) container. This kind of interaction does not exist in vanilla Morrowind.\n\n### Interaction Subrecords\n\nAll item-object record types are enhanced by a new optional subrecord that holds the name of the ItemItemInteraction script.\n\nAll object record types (including items) are enhanced by a new optional subrecord that holds the name of the ItemObjectInteraction script.\n\n### Interaction Chain\n\nCustom interactions are handled by running through a sequence of scripts. Each script can either ignore the interaction, accept it, or refuse it.\n\nIf the action is accepted or refused, the chain stops. If the action is refused or the chain runs to its end without any script accepting it, the player will be notified via a sound effect. We may either re-purpose an existing sound effect or acquire a new one.\n\nWe add two new script instructions:\n\n* `AcceptInteraction`\n* `RefuseInteraction`\n\nUsing any of these instructions outside of an interaction chain is an error. We use these instructions instead of return values, because a part of the interaction chain works via hooks which do not provide return values.\n\nAll interaction chain functions share the following signature:\n\n* reference to item A\n* reference to item/object B\n* reference to actor performing the interaction (player for now, we may extend that later)\n* integer: 0 if interaction was initiated via drag & drop, 1 if interaction was initiated via attack button\n\n### Item-Item Chain:\n\n* Hook `sys::PreItemItemInteraction`\n* `ItemItemInteraction script of item A\n* `ItemItemInteraction script of item B\n* Hook `sys::ItemItemInteraction`\n\n### Item-Object Chain:\n\nIf the object is also an item the Item-Item-Chain is used instead.\n\n* Hook `sys::PreItemObjectInteraction`\n* `ItemObjectInteraction` script of item A\n* `ItemObjectInteraction` script of object B\n* Hook `sys::ItemObjectInteraction`\n\n# De-Hardcoding\n\nThis section describes the first batch of de-hardcoding tasks. This is the core part of stage 1 (*The Grand De-Hardcoding*). We are aiming mostly for low-hanging, but highly profitable fruits here. More complex de-hardcoding (especially tasks that require more extensive script support) will follow in stage 2.\n\n## GMSTs\n\nMany GMSTs will be integrated into other records, which will effectively remove the GMST. We may consider actually removing the GMST record during the load process.\n\nWe will add three new types of GMSTs:\n\n* `IntegerList`\n* `FloatList`\n* `StringList`\n\nWe may consider allowing content files to create new GMSTs outside of the `sys` namespace. This would make the GMST record type more consistent with other record types. To make this addition useful, we need to add script functions to read GMSTs:\n\n* `Get{Type}Gmst()`\n\n`{Type}` can be of type `long`, `float`, `String`, `Longlist`, `FloatList`, or `StringList`.\n\n## Fallback Values\n\nThe *openmw.cfg* file contains a set of fallback values. These were extracted from the *Morrowind.ini* file. As the name indicates, we consider these values as a fallback for legacy-format content files only. Our goal is to move all these values to content file format. In some cases, we may also decide to declare a fallback value obsolete and not use it at all.\n\nAll usable values should be migrated to new GMST records, unless they are already covered by other parts of the de-hardcoding.\n\n## General Scripting Enhancements\n\nWe will introduce new script functions:\n\n* `Enumerate{x}`: Returns a `StringList` of the IDs of all records of the respective type; `{x}` is either \"Skills\", \"Races\", \"Classes\", \"DynamicStats\", \"Attributes\", \"WeaponTypes\", \"ArmorTypes\", \"Specialisations\", \"MagicSchools\", \"Factions\" or \"Birthsigns\"\n\n## Dynamic Stats\n\nWe will unify dynamic stats and make them configurable. For stage 1, we will not allow the deletion of existing dynamic stats (they are used in too many parts of the engine). But we will allow new dynamic stats.\n\nWe add a new record type for dynamic stats. Records for Health (`sys::Health`), Fatigue (`sys::Fatigue`), and Magicka (`sys::Magicka`) will be created by the engine when loading older omwgame files (these records are mandatory for newer omwgame files).\n\nA dynamic-stat record contains the following information:\n\n* Name (taken from GMSTs for default stats)\n* Tooltip Text (taken from GMSTs for default stats)\n* Colour\n* (optional) PrevID: ID of the stat the engine will try to subsequently sort this stat in (similar to info records)\n* (optional) NextID: ID of the stat the engine will try to antecedently sort this stat in (similar to info records)\n* (optional) Vital: Flag which specifies whether the player dies if the stat drops to 0\n* (optional) Level-Up Function: ID of the stat's level-up function, which takes the reference of the character as argument and returns an `long` value representing the maximum stat change achievable with a level-up\n* (optional) Time-Passed Function: ID of the stat's time-passed functionm, which takes the reference of the character and the duration as arguments and returns a `float` that is used to modify the current stat value (the function is not used when waiting or sleeping)\n* (optional) Wait Function: ID of the stat's wait function, which takes the reference of the character, the duration, and a sleep flag (integer) as arguments and returns a `float` that is used to modify the current stat value\n* Default Value: Value that is used as the maximum value of the stat if an actor does not have this stat (possible if the stat was defined in an add-on and not in the base game)\n\nScripts for default stats are injected as necessary. Some of these scripts require access to GMST values. We need to figure out how to implement this: either grab the value from the GMST when adding the script or adding a script function to read GMST values. The latter is easier but will not allow us to remove the redundant GMST records easily.\n\nThe currently existing `Get`, `Set`, and `Mod` instructions are flagged as deprecated and replaced by a single instruction for each operation that takes the stat ID as an argument. We need separate instructions to deal with base, modified and current values.\n\n## Attributes\n\nWe will unify attributes and make them configurable. For stage 1 we will not allow the deletion of existing attributes (they are used in too many parts of the engine). But we will allow new attributes.\n\nWe add a new record type for attributes. Records for the existing attributes (`sys::NameOfAttribute`) will be created by the engine when loading older omwgame files (these records are mandatory for newer omwgame files).\n\nAn attribute record contains the following information:\n\n* Name (taken from GMSTs for default attributes)\n* Tooltip Text (taken from GMSTs for default attributes)\n* (optional) PrevID: ID of the attribute the engine will try to subsequently sort this attribute in (similar to info records)\n* (optional) NextID: ID of the attribute the engine will try to antecedently sort this attribute in (similar to info records)\n* Default Value: Value that is used as the maximum value of the attribute if an actor does not have this attribute (possible if the attribute was defined in an add-on and not in the base game)\n\n**Note**: All records that reference attributes need the respective fields to be changed from integers to strings.\n\nThe currently existing `Get`, `Set`, and `Mod` instructions are flagged as deprecated and replaced by a single instruction for each operation that takes the attribute ID as an argument. We need separate instructions to deal with base, modified and current values.\n\nAdditionally, we will add a new GMST `sys::ClassAttributes` of type `long`. This GMST specifies the number of favoured attributes that a class has. The value defaults to 2.\n\n## Weapon Types\n\nWe will unify weapon types and make them configurable.\n\nWe add a new record type for weapon types. Records for the existing weapon types (`sys::NameOfType`) will be created by the engine when loading older omwgame files.\n\nA weapon type record contains the following information:\n\n* Name (taken from GMSTs for default types)\n* Handling Type: Defines how the weapon is held and what animations play when attacking\n* Hit Test Script: Name of the hit test script used (see *Combat* subsection)\n\nFor stage 1, the weapon type record is still very basic. Its purpose is mostly to allow better de-hardcoding of skills. We may expand on this in the future. However, there are already possible uses, e.g., a weapon type \"Ancient Blades\" which requires a separate skill.\n\nWe add a new script function:\n\n* `GetWeaponTypeId id`: Returns the weapon type ID of an instance or an object with the ID `id`\n\n**Note**: `GetWeaponType` is already taken by Tribunal and we can not repurpose this name without breaking compatibility. \n\n## Armour Types\n\nIn vanilla Morrowind, armour types exist only implicitly. There is no record and no option to select armour types. Armour types are determined by weight only.\n\nWe will keep implicit armour types as an option, but we'll also add armour types explicitly as a new type of record.\n\nRecords for the existing types (`sys::LightArmorType`, `sys::MediumArmorType`, `sys::HeavyArmorType`) will be created by the engine when loading older omwgame files (these records are mandatory for newer omwgame files).\n\nAn armour type record contains the following information:\n\n* Name (taken from GMSTs for default types)\n* (optional) Minimum weight\n\nAdditionally, armour object records are extended by the optional field \"Armour type\".\n\nIf an armour object has no armour type specified, its type is determined by its weight according to the following algorithm:\n\n1. Consider all armour types with a minimum weight field.\n2. Exclude types that have a minimum weight larger than the weight of the object.\n3. Pick the type with the largest minimum weight.\n\nFor stage 1, the armour type record is still very basic. Its purpose is mostly to allow better de-hardcoding of skills. We may expand on this in the future. However, there are already possible uses, e.g., imitating TES IV: Oblivion by eliminating medium armour or adding a new armour type such as \"Ancient Armour\" which requires a separate skill.\n\nWe add another script function:\n\n* `GetArmorTypeId id`: Returns the armour type ID of an instance or an object with the ID `id`\n\n**Note**: For the sake of consistency, we've adopted the naming scheme introduced for weapon types.\n\n## Specialisation\n\nWe will unify specialisations and make them configurable.\n\nWe add a new record type for specialisations. Records for Combat (`sys::CombatSpec`), Stealth (`sys::StealthSpec`), and Magic (`sys::MagicSpec`) will be created by the engine when loading older omwgame files.\n\nA specialisation record contains the following information:\n\n* Name (taken from GMSTs for default types)\n\nWe add a new script instruction:\n\n* `GetSpecialization id`: Returns the specialisation of a class or a skill with the given ID `id`\n\n## Magic Schools\n\nWe will unify magic schools and make them configurable.\n\nWe add a new record type for magic schools. Records for the existing magic schools (`sys::AlterationMagicSchool`, `sys::ConjurationMagicSchool`, etc.) will be created by the engine when loading older omwgame files.\n\nA magic school record contains the following information:\n\n* Name (taken from GMSTs for default schools)\n* Specialisation (`sys::MagicSpec` for default schools)\n* Resource (`sys::Magicka` for default schools)\n\nA use for the specialisation field would be, e.g., dividing magic into the two categories \"Divine Magic\" and \"Arcane Magic\" and having characters specialise accordingly.\n\n## Skills\n\nSkills are a complex topic and we won't achieve full de-hardcoding in stage 1 - and most likely there will never be a 100% complete de-hardcoding. But there is still a lot we can do with skills.\n\nCurrently, skills exist as indexed records. We need to move these over to ID-based records. The exiting indices are translated to IDs of the form `sys::NameOfSkill`.\n\nWe add a new subrecord type (see subsection *Function Subrecords* below). Each skill can have zero, one, or multiple function subrecords.\n\nThe following skills are too tightly bound into the engine to allow their easy removal:\n\n* Acrobatics\n* Alchemy\n* Armorer\n* Athletics\n* Block\n* Enchant\n* Hand-to-hand\n* Mercantile\n* Sneak\n* Speechcraft\n* Unarmored\n\nTherefore, we will (for now) forbid the deletion of the respective skill records. All other default skills can be deleted.\n\n### Script Instructions\n\nThe currently existing `Get`, `Set`, and `Mod` instructions are flagged as deprecated and replaced by a single instruction for each operation that takes the skill ID as an argument. We need separate instructions to deal with base and modified values.\n\nWe also introduce new script instructions:\n\n* `UseSkill id, v`: Progresses the skill `id` by a value of `v` (`float`).\n* `UseValueSkill Id, i`: Progresses the skill `id` by the value given by the use-value field with index `i` (0, 1, 2 or 3)\n* `Get{x}SkillLevel s_id`: Returns the skill level of a function subrecord with `TargetId` `s_id` (`String`). `{x}` is either \"Armor\", \"Weapon\", or \"Magic\"; see the next subsection for details.\n\n**Note**: We do not de-hardcode the fixed number of use-value fields. The usefulness of these fields for new skills is most likely limited. The `UseSkill` instruction, which bypasses the use-value fields, should be sufficient in most cases. We only add the `UseValueSkill` instructions to better support script interactions with default skills.\n\n### Function Subrecords\n\nA function subrecord describes how a skill interacts with hardcoded engine functions. A function record consists of a function ID (we use an index here, because the list of functions will definitely not be extensible by content files) and additional arguments.\n\nFor stage 1, we require only one additional argument:\n\n* `TargetId` (single `String` value)\n\nStage 1 will introduce three function IDs:\n\n* Armour Skill\n* Weapon Skill\n* Magic Skill\n\nWe do not forbid non-unique function subrecords, i.e., two skills may have identical function subrecords, e.g., two skills governing light armour. If there is more than one skill with the same function subrecord and the skill level for this function needs to be considered, we use the maximum of all relevant skill levels.\n\n### Armour Skills\n\nWe make armour skills fully customisable by introducing a function ID for armour types. Any skill that has a function subrecord with this ID is an armour skill. For default armour skills loaded from older omwgame files, we will inject function subrecords during the loading process.\n\nThe `TargetId` of an armour function subrecord indicates the armour type that the skill is governing. \n\nThe default skills are:\n\n* \"Heavy Armor\"\n* \"Light Armor\"\n* \"Medium Armor\"\n\n### Weapon Skills\n\nWe make weapon skills fully customisable by introducing a function ID for weapon types. Any skill that has a function subrecord with this ID is a weapon skill. For default weapon skills loaded from older omwgame files, we will inject function subrecords during the loading process.\n\nThe `TargetId` of a weapon function subrecord indicates the weapon type that the skill is governing. \n\nThe default skills are:\n\n* \"Axe\"\n* \"Blunt Weapon\"\n* \"Long Blade\"\n* \"Marksman\"\n* \"Short Blade\"\n* \"Spear\"\n\n### Magic Skills\n\nWe make magic skills fully customisable by introducing a function ID for magic schools. Any skill that has a function subrecord with this ID is a magic skill. For default magic skills loaded from older omwgame files, we will inject function subrecords during the loading process.\n\nThe `TargetId` of a magic school function subrecord indicates the magic school that the skill is governing. \n\nThe default skills are:\n\n* \"Alteration\"\n* \"Conjuration\"\n* \"Destruction\"\n* \"Illusion\"\n* \"Mysticism\"\n* \"Restoration\"\n\n## Weather\n\nWe make weather types customisable by moving from hardcoded, index-based weather effects to ID-based weather types stored in content files. To achieve this, we introduce a new record type (\"Weather\"). When loading older omwgame files, we will inject weather records for the ten default weather types (`sys::WeatherAsh`, `sys::WeatherBlight`, etc.).\n\nA weather record is made up of subrecords, each describing a weather effect. A weather record can have any number of weather effect subrecords.\n\nThere are four types of weather effect subrecords:\n\n* \"Magic Effect\": A magic effect that is applied to all actors within active cells while the respective weather type is active\n* \"Sky\": Modifications to the visuals of the sky (e.g, clouds)\n* \"Particles\": Particle effects (e.g., rain)\n* \"Event\": Events that can happen with a certain probability while the weather type is active (e.g., lightning); events happen at random locations\n\nMagic effect subrecords contain the ID of the respective magic effect. All other subrecords contain an integer ID specifying the effect and (if necessary) additional parameters.\n\nAll effects described in these subrecords affect active cells only.\n\n### Sky\n\nTODO:\n- Should cover all existing sky effects and possibly new ones\n\n### Particles\n\nTODO:\n- Should cover all existing particle effects and possibly new ones\n\n### Event\n\nWe will add the following event IDs:\n\n* \"0\": Scripted event; additional data consists of the name of the script to be run when the event is triggered; the script takes the following arguments: worldspace (`String`), location (`Vector3`), weather ID (`String`)\n* \"1\": Lightning\n\nIn addition to the event ID and event-ID-specific data, event subrecords have an \"Event Trigger\" block:\n\n* Chance: Floating number in the range between 0 and 1; the chance per unit of time that the event happens (we may want to make the unit of time globally configurable with a new GMST)\n* (optional) Min-Distance: The minimum distance from the player at which the effect can take place (0 by default)\n* (optional) Max-Distance: The maximum distance from the player at which the effect can take place (infinite by default)\n\n### Scripts\n\nWe need to introduce new script instructions regarding the weather, since the existing instructions are based on a fixed number of weather types or integer-indexed weather types. These instructions can not be salvaged and we therefore declare them deprecated.\n\nNew script instructions:\n\n* `SetWeather(regionId, weatherId)`: Sets the current weather for the region with the ID `regionId` to `weatherId` (replaces `ChangeWeather`)\n* `GetWeather(regionId)`: Returns the current weather ID for the region with the ID `regionId` (replaces `GetCurrentWeather`)\n* `SetRegionWeather(regionId, weatherId, chance)`: Modifies the entry `weatherId` in the weather table of the region with the ID `regionId`; `chance` is of type `long`; we relax the requirement that the sum of the chance values needs to be 100 (replaces `ModRegion`)\n* `UpdateRegionWeather(regionId)`: Forces a new roll on the weather table of the region with the ID `regionId`\n\n## Water\n\nWe currently have but a single water type that is hardcoded. We name this water type `sys::Water` and inject a suitable record of the new type (see below) during loading of older omwgame files.\n\nWe generalise the concept of water to the concept of liquid by introducing a new record type (\"Liquid\"). Liquid records can be used both for different-looking water types (e.g., more swampy water, water with different colouration) but also for completely different liquid types (e.g. lava).\n\n**Note**: Morrowind only uses one liquid type, which we refer to as \"water\". (While lava does exist in the game, it is handled with activators, rather than as a liquid type)\n\nTo support the water type `sys::Water`, we also need to add a new magic effect (`sys::SuffocationEffect`).\n\nLiquid records are referenced both in worldspace records (see *Cells, Worldspaces & Areas* section) and in the body of liquid records (see *Misc* section).\n\nA liquid record consists of the following fields:\n\n* Effects: IDs of zero or more magic effects which are applied to actors while in the liquid\n* Submerged Effects: IDs of zero or more magic effects that are applied to actors while completely submerged in the liquid\n* Liquid Type ID: `long`, hardcoded\n* Additional parameters, specific to liquid type\n\nWe need to support at least one liquid type (ID 0: Water) from the start. Other types can be added.\n\nTODO:\n- Are there other ways to handle visuals for liquid types that require less hardcoding?\n- Use of shaders?\n\n## Magic Effects\n\nCurrently, we have a fixed number of magic effects that are referenced by an integer index.\n\nWe move over to effect records with a string-ID-based scheme.\n\nWe also introduce a new effect (`sys::SuffocationEffect`) by injecting a record when loading from older omwgame files. This effect triggers the normal procedure for running out of air.\n\n**Note**: This will require creating a new effect icon, which will be stored internally along with other resources that are part of the game engine.\n\nEffect records can be deleted without restrictions. A content developer can add new effect records - again without restrictions.\n\nAn effect record consists of the following fields:\n\n* Name (currently stored in a GMST)\n* (optional) Effect Function: Contains the ID of a script function that is called when the magic effect takes effect; the function takes two arguments: the reference of the effects source (this would be a spellcaster in case of a spell or an item in case of an enchantment) and a list of target references\n* (optional) Wear-Off Function: Contains the ID of a script function that is called when the magic effect ends (only relevant for non-instant effects); the function takes the same two arguments as the \"Effect Function\" field\n\nFor the existing effects, we will inject scripts that match the previously hardcoded effects when loading from an older omwgame file. This will require the addition of a significant number of new script instructions that are all trivial, since they will just call existing engine functions.\n\nIt is important to generalise as much as possible when creating these new script functions, e.g., we won't have a `DivineIntervention` script instruction. Instead we add a `GetClosestMarker` function, use existing functions for querying position and cell and, then, use the new `Teleport` instruction.\n\n## Input\n\nWe allow the addition of customisable input functions that can be bound to keyboard buttons in the usual way and can trigger the execution of scripts. Existing input bindings are not affected by this.\n\nTo this end, we introduce a new record type (\"Input\"):\n\n* Label\n* (optional) Tooltip: `String` value\n* Default Key: `long` value\n* (optional) Key-Press Event Script: Contains the ID of a script that is executed when the key is pressed\n* (optional) Key-Release Event Script: Contains the ID of a script that is executed when the key is released\n* (optional) Key-Hold Event Script: Contains the ID of a script that is executed when the key is pressed and held\n* Key-Hold Delta: `float` value describing the minimum time between two executions of the \"Key-Hold Event Script\"; defaults to a reasonable value\n\n## Held Items\n\nWe merge the probe and lockpick object types into a new object type: \"Held Item\". The Security-skill-related functions are handled via the `ItemObjectInteraction` script subrecord. When loading older omwgame files, a suitable script in the `sys` namespace is injected for probing and lock picking each. When transforming probe and lockpick records into held-item records, an `ItemObjectInteraction` subrecord referencing the respective script is injected.\n\n**Note**: If we enhance the animation system, we may need to add additional subrecords that specify the pose for holding the item or the animation for using it.\n\n## Pickpocketing\n\nWe add a new GMST (`sys::ReversePickpocketing`) of type `long` that indicates if reverse pickpocketing is allowed (value other than 0) or not (value 0).\n\nWe move the function for checking if pickpocketing succeeded from the C++ code to a new script function (`sys::PickpocketTest`). We inject this function when loading older omwgame files.\n\nThe function takes the following arguments:\n\n* thiefId: Reference to the thief (actor)\n* victimId: Reference to the victim (actor)\n* stolenItemId: Reference to the item to be stolen\n* reversePickpocket: `long` flag; set to 0 for regular pickpocketing, set to 1 for reverse pickpocketing\n\nThe function returns 1 if the pickpocket attempt succeeded and 0 if it failed.\n\nItems with the `noPick` tag are excluded from pickpocketing.\n\n## Combat\n\nDe-hardcoding combat is a monumental task that we can not hope to complete within the time frame of stage 1. We will de-hardcode some parts that do not depend on large-scale improvements to animation and can be handled with the scripting improvements in stage 1. Any further enhancements are left for stage 2 or, more likely, OpenMW 2.0.\n\n### Hits\n\nWe move the hit test check from C++ code to script functions. The following functions will be used:\n\n* `<NameOfMeleeHitTest>(weaponId, targetId)`: Function for melee weapons defined in the corresponding WeaponType record; `weaponId` refers to the weapon used, `targetId` refers to the ID of the target\n* `<NameOfRangedHitTest>(weaponId, targetId, ammunitionId)`: Function for ramged weapons defined in the corresponding WeaponType record; `weaponId` refers to the weapon used and `targetId` refers to the ID of the target, and `ammunitionId` refers to the ammunition used by the ranged weapon\n* `sys::UnarmedHitTest(weaponId, targetId)`: Default function for unarmed attacks; `weaponId` refers to the weapon used and `targetId` refers to the ID of the target\n\nAll functions return 0 if the attack misses and a value other than 0 on a successful hit.\n\n`sys::UnarmedHitTest` and default functions for armed-hit tests (`sys::MeleeHitTest` and `sys::RangedHitTest`) are injected when loading older omwgame files.\n\n### Script Enhancements\n\nWe add a new script function:\n\n* `GetTargetRef`: Returns the current target of an actor; returns a null reference if the actor is not in combat or if it is used on an instance that is not an actor\n\nWe add two new script instructions:\n\n* `DeclareWar l`: Similar to `StartCombat`, but specifies a whole group of enemies; `l` is a list of references\n* `DeclarePeace l`: Ends the hostilities resulting from previous combat towards the actors listed in the reference list `l`. **Note**: We may need to add a grace period after this instruction during which attacks do not cause hostility again, so we can handle non-instant attacks.\n\n### Script Hooks\n\nWe add three new script hooks:\n\n* `sys::Kill`: Executed whenever an actor is killed; scripts for this hook take the following two arguments: reference to the target and reference to the actor who killed the target (may be a null reference)\n* `sys::CombatStarted`: Executed when the player enters combat\n* `sys::CombatEnded`: Executed when the player exits combat\n\n## Music\n\n### Playlists\n\nWe add a new record type: PlayList\n\nThe playlist record has the following fields:\n\n* Probability: Float [0.0-1.0]\n* Music Directory (optional): String\n* Title (can appear more than once): String\n\nThe titles on the playlist are all the titles in the music directory and the titles listed explicitly.\n\nWhen playing a playlist, first the probability is considered to randomly decide if a track is played or not. Then a track from the list is chosen.\n\nIf a playlist is played in loop mode the process above is repeated whenever:\n\n* The current track ends\n* No track is playing at reasonably chosen time intervals\n\n### Background and Event Music\n\nWe distinguish between two types of music:\n\n* Background Music\n* Event Music\n\nBackground music is music that (potentially) runs all the time, unless explicitly stopped. Background music is always looping.\n\nEvent music is played only on certain events. Event music can be played looped or only a single time. In the former case event music needs to be stopped explicitly. Once event music ends background music is resumed exactly where it was interrupted.\n\nBackground and event music persists through save-load-cycles.\n\n### Script Enhancements\n\nWe add several new script instructions:\n\n* PlayBackgroundMusic (t)\n* PlaylistBackgroundMusic (pl)\n* StopBackgroundMusic\n* PlayEventMusic (t, priority, loop=0, force=0)\n* PlaylistEventMusic (pl, priority, loop=0, force=0)\n* StopEventMusic (priority)\n\nArguments:\n\n* t (string): a single title (specified by its location in the data directory)\n* pl (string): a playlist (specified by its ID)\n* loop (integer): looping or not\n* force (integer): Terminate any other running event music first. If this flag is 0 and there is already event music playing the new event music is ignored. Event music provided by content developers will usually not set this flag.\n* priority (integer): Priority for event music. If there are multiple instances of active event music only the one with the highest priority will play. There can only ever be one instance of event music for any priority level. If event music of higher priority stops and there is event music of lower priority the event music of lower priority starts to play.\n\nNote: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart. Changes in looping behaviour are considered though.\n\nNew games starting with a new format omwgame file can use priorities as the developer sees fit. For old games where automatic injection of scripts and music records take place we define the following priorities:\n\n* 10: Combat music\n* 0: Basic event music\n\n### Transition of Existing System\n\nWe completely replace the old music system with a new script-based one. The StreamMusic script instruction is flagged as deprecated.\n\nNew records are injected when loading from an older omwgame file.\n\nWe add two playlists (sys::ExplorePlaylist and sys::CombatPlaylist) which are pointed to the usual directories and play with a probability of 1.0.\n\nWe add a GMST (sys::DefaultBackgroundMusic) with a default value of \"sys::ExplorePlaylist\". Whenever starting a new game or loading a game with no background music running the playlist specified in this GMST is played as background music. If the GMST holds an empty string no background music is played by default.\n\nWe add a script to the sys::CombatStarted and sys::CombatEnded hook each. The former plays sys::CombatPlaylist looped and forced. The latter stops event music.\n\n### Location-Based Background Music\n\nWe add an optional string sub-record to the records listed below. This field contains a single track or a playlist that determine the background music for the respective location.\n\n* Worldspace\n* Region\n* Area\n* Cell\n\nMore specific entries (towards the bottom of the list) take precedence over more general entries. Global background music (controlled via script instructions) has the lowest priority.\n\n## Character Creation\n\nThe process of character creation currently allows for very little customisation. We redo the character creation process with the following goals in mind:\n\n* Allow more control over how the individual steps are chained together\n* Allow additional steps (both future hardcoded enhancements like additional visual customisation and custom steps entirely defined in content files)\n* Allow replacement of existing steps with alternative methods\n\nCurrently the character creation process runs through these steps:\n\n1. Choose name\n2. Choose race and appearance\n3. Choose a method for class selection and perform class selection (3 methods)\n4. Choose birthsign\n5. Review\n\n### Chaining\n\nWe add two new GMSTs to control how the steps of character creation are chained together:\n\n* sys::NewCharBack: Integer; allow going back to previous steps (not being able to go back also implies that there is no going forward)\n* sys::NewCharSteps: String-List: List of scripts associated with the steps of the character creation process. The scripts are listed ordered according to the sequence they are executed in.\n\nEach character creation step has a an associated script that is called when the step is reached (either via a EnableX script instruction or via the player going back or forth).\n\n### Script Instructions\n\nWe add several new script instructions.\n\n* CharacterCreationRun (n): Opens the respective GUI/window\n* SetPlayerName (name)\n* SetPlayerRace (id)\n* SetPlayerFace (id)\n* SetPlayerHair (id)\n* SetPlayerSex (i): 0 or 1\n* SetPlayerClass (id)\n* SetPlayerBirthsign (id)\n* CharacterCreationBack: Go back to previous step\n* CharacterCreationNext: Go to next step (ignored if the next step had not been accessed before)\n\nn is an integer argument with one of several hardcoded integer values:\n\n* 0: Name GUI\n* 1: Race GUI\n* 2: Class selection via picking an existing class\n* 3: Class selection via creating a class\n* 4: Birthsign GUI\n* 5: Review GUI\n\n### Transition of Existing System\n\nWhen loading older omwgame files we inject two GMSTs (sys::NewCharBack and sys::NewCharSteps) as well as several scripts.\n\nFor each step we add a new script that consists of only a single line:\n\n````\nCharacterCreationRun n\n````\n\nwhere n is the appropriate index.\n\nOnly steps #3 will be handled differently than described above.\n\nThe class selection method GUI can be implemented entirely with a message box. Therefore we scrap the hard-coded method selection window entirely.\n\nLikewise the character selection method of answering a set of quests can be implemented via a set of message boxes. Therefore we scrap this window too.\n\n## Level-Up\n\nThe vanilla (character-)level-up system allows almost no modification. We keep the existing hard-coded system, but make it more configurable, and we also allow content developers to completely replace the existing system.\n\n### State Unveiling\n\nChecks for a level up are based on the skill progression since the last level up. In vanilla this state is not accessible to content developers or players.\n\nWe address this issue by turning these hidden variables into record variables attached to the player character. The variables have the type long and a name matching the ID of the skill, and are created on the fly whenever the engine detects a skill level up. Note that regular script instructions that change skill level do not count towards level ups.\n\nWe also add another record variable (LevelUpCount) that counts the number of major and minor skill level-ups.\n\nBy binding these stats to a character instance instead of using global stats we are also making preparations for optional future level up mechanics for NPCs (companions or otherwise). This is most likely a task for stage 2 though.\n\n### Trainers\n\nSome NPCs are flagged as skill trainers. These trainers can train the player in their three best skills. Skill levels gained via training count towards level-up. We make the following enhancements to this mechanism:\n\n* New GMST (sys::SkillTrainCount, integer): Replaces the hard-coded value of 3 skills.\n* New script (sys::SkillTrainingTest): Tests if NPC can train player in a skill (returns 0 or 1). This test is performed after the faction standing test and after the skill list is trimmed down via sys::SkillTrainCount. The function takes the following arguments: ref to player, ref to NPC, ID of skill\n* New script (sys::SkillTrainingApplied): Executed after training has been completed. Arguments are the same as with sys::SkillTrainingTest.\n\nWhen loading from an old omwgame file, GMST and scripts are injected. sys::SkillTrainCount defaults to 3, sys::SkillTrainingTest performs the usual tests on skill level and attributes and sys::SkillTrainingApplied defaults to an empty script.\n\n### Level-Up Test\n\nThe level-up test is performed when either the player rests (by default via T key) or when the ShowRestMenu function is called.\n\nThe test is performed by calling the function sys::LevelUpTest, which returns either 0 or 1. If 0 is returned no further action is taken. If a value other than 0 is returned the usual level-up procedure continues. Note that this function is allowed to initiate a custom level-up procedure and return 0.\n\nThe function takes two arguments:\n\n* A ref to the player\n* An integer that indicates what started the level-up test (-1 for resting)\n\nWe enhance the ShowRestMenu instruction with an optional long argument (defaults to 0) that is passed to the sys::LevelUpTest function as second argument.\n\nWhen loading from an older omwgame file we inject a sys::LevelUpTest script that matches the hardcoded version of the level-up test.\n\n### Level-Up Procedure\n\nWe keep the level-up procedure but replace several hardcoded values with GMSTs.\n\n* Number of major skills: sys::MajorSkillCount (default: 5)\n* Number of minor skills: sys::MinorSkillCount (default: 5)\n* Number of attributes to increase during level-up: sys::AttributeLevelUp (default: 3)\n\nWe add a new function (sys::AttributeIncrease) that determines the number of points an attribute can be increased. The function takes a reference to the player and the ID of the attribute as argument and returns the number of attribute points. As usual we inject a matching function that matches the hardcoded implementation.\n\nWe add localisation records (see section about localisation) of the form default::LevelUpMessageX, where X is a positive integer number. When loading older omwgame files these records are injected based on level up fallback values. We use the default namespace instead of sys, because content developers need to be able to add their own records.\n\nWe add a script (sys::LevelUpFinished) that is called after the level-up procedure has been completed. The script takes a reference to the player character as its only argument.\n\nThe default implementation (injected when loading from an older omwgame file) resets the skill record variables and reduces the LevelUpCount variable by 10.\n\n### Level-Up Image\n\nThe level up dialogue contains an image that depends on how the skill increase is distributed among specialisations. The function that decides about this image is very simple. However, since specialisations won't be hardcoded any more, we can not simply pass these values into the function without first establishing more involved script support than planned for stage 1.\n\nThis is actually not a problem since we can simply track the increase values in record variables, which may also prove useful for other purposes.\n\nWith this modification the C++ function (MWGui::LevelupDialog::getLevelupClassImage) can be directly translated into a script function (sys::LevelUpImage).\n\nNote that this change does not allow for easy addition of new classes to the scheme. This is not a feature present in vanilla MW either. We may decide to investigate other options than simply translating the C++ function into a script function.\n\n## Trading\n\n### Pricing\n\nThe algorithm for trading (implemented in MWMechnics::Trading::haggle) can be translated straightforward into a script function once all stage 1 script enhancements are in place.\n\n### Restocking Money\n\nCurrently the frequency of money restocking is controlled by a the GMST fBarterGoldResetDelay. Money is always reset to the original value.\n\nWe make all of these aspects configurable by adding a new field to all actor record types (NPC, creature) that contains a script ID.\n\nThe script takes a reference to the actor as its single argument.\n\nWhen loading older omwgame files we inject a script (sys::UpdateGold) and inject this ID into all actor records that are flagged for merchant services. We also add a record variable (last restock).\n\nThe script checks the current time against the last restock variable and the GMST and resets the gold if necessary.\n\n### Restocking Items\n\nCurrently items are restocked after every trade. Items in the merchant's inventory and in all containers owned by the merchant are restocked to their original count.\n\nThis process is complicated by the existence of levelled item lists, which makes it too complex to re-implement in a script.\n\nWe will make several changes to increase the flexibility of item restocking.\n\nFirst we move the refill operation from after a trade is completed to before a trade is completed. This should not affect existing content.\n\nWe add three more scripts (optional, specified via new string fields in the actor's record) that hook into the refill process.\n\n1. Check if a refill should be performed; called at the beginning of every trade cycle. Arguments: ref to actor, integer (1 if first trade since talking to the merchant, 0 otherwise). Returns an integer (0 no refill required, otherwise refill is required). If this script is not present, a refill is performed).\n2. Called for every item that the refill algorithm flags for removal. Arguments: ref to actor, ref to item. Returns an integer (0 if item should be kept, otherwise item is removed). If this script is not present, the removal takes place.\n3. Called at the end of refill procedure. This gives content authors the opportunity to make other modifications. Arguments: ref to actor. No return value.\n\n## Fast-Travel\n\nMorrowind offers a wide selection of fast travel options. Each of these fall into one of three categories:\n\n1. Spells and teleport items\n2. Travel to fixed locations via paying a travel NPC (boat, slit strider, gondola, guild guide)\n3. The propylon chamber system\n\nWith vanilla Morrowind and other stage 1 changes #1 and #3 are already sufficiently configurable. #2 is not.\n\nIn stage 1 we will improve the flexibility of the travel network system and also its usability both for content creators and for players.\n\n### Travel Network Record\n\nWe introduce a new record type (Travel Network) that consists of the following fields:\n\n* Colour (integer)\n* ID of Cost-Script (string): Takes three arguments (ref to travelling character, ID of source location record, ID of destination location record) and returns a long (number of coins). This is the value before bartering and followers are taken into consideration.\n* ID of TravelTime-Script (string): Takes three arguments (ref to travelling character, ID of source location record, ID of destination location record) and returns a float (duration in hours)\n* ID of TravelCompleted-Script (string, optional): Takes three arguments (ref to travelling character, ID of source location record, ID of destination location record). Executed when a character has finished travelling.\n* Auto-Knowledge (integer, flag): Is the travel network fully known from the beginning of the game (!=0) or do locations need to be discovered to be usable (0).\n* Visible (integer, flag): Is the travel network visible on the map?\n\n### Travel Location Record\n\nWe introduce a new record type (Travel Location) that consists of the following fields:\n\n* Travel Network ID\n* Location worldspace (String)\n* Location position and orientation (Vector4)\n* Parent worldspace (String, optional)\n* Parent position (Vector3, optional)\n* List of IDs for travel locations that can be reached from this travel location\n\nBy formalising travel locations in this way we make it easier to modify travel locations later on (currently a modder would have to go through all travel NPCs of the same network, if he wanted to move a travel location) and to build visualisation features (see below).\n\n### Travel Sub-records in NPC records\n\nWe keep the existing sub-record structures for travel since there is no automated way of transiting them to the new scheme. This task will be left to mod developers.\n\nNPCs supporting the new scheme will have a single field that specifies the ID of the associated travel location record. \n\n### Transition of Existing System\n\nWhen loading older omwgame files we inject suitable travel network records. Unfortunately there is no procedural method to distinguish between boat, slit strider and gondola, so these will all have to be grouped together into an sys::TravelOthers network. It will be up to mod developers to untangle these.\nThe travel network records will be created with the visible flag set to 0, because old-style travel data isn't available at load time and therefore visualisation can not work.\n\nTravel locations can not be automatically transitioned to the new system. This task is left to mod developers.\n\n### Block List\n\nThe layout of a travel network is determined by the data in the content files and can not be changed during the game. However we do offer several methods to configure a network during the game.\n\n* Location Block List: Individual locations can be dynamically blocked, either for incoming or outgoing connections or both.\n* Connection Block List: Connections between two locations can be blocked dynamically. This blocking is directed, meaning blocking the connection from A to B does not automatically block the connection from B to A.\n\n### Visualisation\n\nWe offer visualisation somewhat similar to [this](http://en.uesp.net/wiki/Morrowind:Transport).\n\nEach travel network has a colour associated with it that will be used to mark connections. If a travel location has a parent section, the parent section will be used instead to determine the location (e.g. exterior location for guild guides inside a building).\n\nOnly networks flagged as visible are drawn. We may add a GUI element that allows showing/hiding of visible networks either as a group or individually.\n\n### Script Enhancements\n\nWe introduce several new script functions:\n\n* GetTravelLocationWorldspace (id): Returns String\n* GetTravelLocation (id): Returns Vector4\n* GetTravelParentLocationWorldspace (id): Returns String\n* GetTravelParentLocation (id): Returns Vector3\n* GetTravelLocationList (id, ignore_blocked=0): Returns a list containing the IDs of all locations of the travel network id\n* GetTravelDestinationList (id, ignore_blocked=0): Return list of IDs of destination travel locations for the travel location id\n\nWe also introduce several new script instructions:\n\n* SetTravelVisibility (id, visibility): Sets the visibility (flag) of a travel network (id)\n* RevealTravelLocation (id): Flag a travel location as known\n* BlockTravelConnection (source-id, destination-id, blocked): Block or unblock a connection between two locations\n* BlockTravelLocation (id, incoming-blocked, outgoing-blocked): Block a travel location.\n\n# NPCs & AI\n\n## Navmeshes\n\nThe current system of waypoints is limited. It is unlikely that we can improve it much. This makes pathfinding one of the few cases were we seriously need to consider completely abandoning an existing system in favour of a new one (navmeshes in this case).\n\n## Waypoints\n\nWaypoints can still be useful, but in a different way. Content developers will often designate a specific location for a NPC to stand at or walk to. Currently the easiest method to achieve this is to place an instance at the location, note down the coordinates and then enter them into a script or a field in a record. This is not an efficient approach and also causes maintainability issues, when a location that is used in multiple parts of a content file needs to be moved.\n\nWe introduce a new record type (Waypoint) with the following fields:\n\n* Position (Vector3)\n* Worldspace/Cell Id (String; cell takes precedence over worldspace)\n\nWe also add a new script function:\n\n* GetWaypointPosition (id): Returns a Vector3\n\nWaypoints are not part of the cell data structures because they need to be accessed independently of cells and we don't want to search through (potentially) every single cell every time a waypoint is referenced.\n\n## NPC Schedules\n\nNPC schedules are an advanced topic and as such would be a better fit for stage 2. However schedules are also a very iconic feature with a large impact on the playing experience. Therefore we will consider them for stage 1. The following is a preliminary proposal that is in need of more design work.\n\n### Active and Passive Cells\n\nWe run schedules only in active cells. But we also need to consider passive cells.\n\nHere are a few examples. For simplicity let's consider a one-dimensional cell grid with the usual pattern of one cell around of the player cell being active.\n\nFor this example let the player be in cell 0. Now consider NPC A, who is in cell 2 (not active). He has a schedule that takes him to cell 1 sometimes. As a player we would expect to see him from the distance then.\n\nAnother example with the same setup as above: While A is moving within cell 2 towards cell 1, the player relocates to cell 2. He would to see A make his way towards cell 1. Moreover if the player moves back to cell 0 and then returns to cell 2, he would expect to see A having made some progress.\n\nConclusion: We must track all actors whose schedules can take them into active cells.\n\nIt is not clear yet how to achieve this goal best. In the most simple implementation we could just estimate the time it takes to perform a task in a schedule and spawn the actor into active cells accordingly. This would however not cover the second example.\n\nAlternatively we could invent a semi-active cell state and apply it to all cells that contain NPCs whose schedules can touch the active cells. This semi-active state would not run any scripts, nor would it render or run animations, and it may use a simplified/limited form of physics. This approach would cover all cases. However, it has the potential for a severe performance impact.\n\nAgain alternatively, we may offer both implementations and switch between them base on settings/available processing power.\n\n### Actor Tracking\n\nIndependently from the chosen implementation, we need to track all actors whose schedules are relevant to the active cells. This is a problem because with the default data structures we would at least need to scan every single cell on start-up for relevant actors. The performance impact of such an approach is most likely not acceptable.\n\nTherefore information about which actor's schedule is relevant to which cell needs to be stored in a separate data structure that exists outside of the cell records.\n\n### Schedule Record\n\nWe add a new record type (Schedule). A schedule consists of a list of task sub-records.\n\nEach task sub-record consists of:\n\n* A time of the day range\n* An AI package\n* Ignore flag (see next section)\n\nThe time table of a schedule is allowed to have holes (e.g schedule 1 ending at 10:00 and schedule 2 beginning at 10:15).\n\nTasks may also overlap partially, but no two tasks may start at the same time.\n\nWhen picking a task from the schedule, all tasks which contain the current time are considered. Of these the task with the earliest start time is chosen.\n\nA new task is picked only when the current one has expired.\n\n### Managing Actor Schedules\n\nSchedules can be assigned to actors either in the actor record or with a new script instruction.\n\nWhen explicitly switching from one schedule to another, tasks with the ignore flag are ignored. This feature is intended for transitory tasks, that are pointless if the previous task hasn't been performed.\n\nWe add script instructions that allow for querying the current schedule of an actor. We also add a script instruction to prematurely end a task.\n\n## AI Packages\n\nTo accommodate the NPC schedule feature and other enhancements to the AI system properly we need to redo the AI package system. We will deprecate the old packages.\n\nThis is an early draft of the new system and will almost certainly require more design work before we can cut it up into individual tasks.\n\n### Idle\n\nIdle activities are currently bound to the Wander package. This is suboptimal, both because other packages could benefit from it and because storing the idle function in a package instead of the NPC makes it hard to give the NPC a characteristic idle pattern.\n\nWe add a list of idle chances for each available idle animation to the NPC record. Ideally this list should be extensible, but first we need to improve the animation system so that the number of idle animations isn't hard-coded any more. This may be a stage 2 task.\n\nEach package contains two fields related to idling behaviour:\n\n* Chance: A value between 0 and 1 that specifies the chance (per reasonable unit of time) of an idle animation to be executed.\n* Dialogue Chance: A value between 0 and 1 that specifies the chance of an idle dialogue if an idle animation is executed.\n\nWe may decide to use a scale of 0 to 100 instead of 0 to 1 to increase usability for less mathematically minded content developers.\n\nThe old wander package keeps its old idle list. When active it disables the new idle system.\n\nIf a NPC does not have any active packages the idle function is still active. In this case Chance and Dialogue Chance are determined by two new GMSTs, which default to 0 when loading older omwgame files.\n\n### Package Roles\n\nWe categorise package into four roles:\n\n* Legacy: Old packages, generated either via script instructions or via AI package list in the actor record. Removed once they run out (if not flagged for repeat). Deprecated.\n* Schedule: Inserted by the schedule system. Can not be directly manipulated via script instructions.\n* Manual: New packages, generated via new script instructions.\n* Situation: Inserted via other game mechanics (e.g. combat, breath). Can not be directly manipulated via script instructions.\n\nPackages of a type lower in the list take precedence over packages higher in the list. For the roles schedule and situation there can be only one package per actor at a time.\n\nWe add new script instructions to query the package state of an actor and to insert and remove manual packages.\n\n### Duration\n\nEach package has two fields that describe its duration:\n\n* Duration itself: Package will self-delete once the current time is larger than the start time plus the duration\n* Repeat flag: Package adds itself again at the end of the end of the duration\n\nPackages lacking these fields persist indefinitely unless explicitly removed.\n\nSome legacy packages already have these fields. Schedule packages will not have these fields since the removal is controlled entirely by the schedule system.\n\n### Updates Packages\n\nWe translate the packages AiFollow, AiActivate and AiTravel into the new format.\n\nAiFollow and AiTravel specify a location by being given a set of coordinates and an optional cell ID. We will replace part of the package with a different format.\n\nThere are two possible locations sub-records of which each package may only contain one:\n\n* A set of coordinates and a worldspace/cell ID (not optional)\n* The ID of a (new type) waypoint (no worldspace ID required, since the waypoint includes that)\n\nThe other old-style packages require more extensive modifications.\n\n### AiEscort\n\nWe can largely keep AiEscort with the following modifications:\n\n* We use the new format to describe the target location (see above)\n* Instead of a single location the package lists a sequence of locations, which will allow content developers to make actors escort the player along certain paths and around dangerous areas without resorting to multiple AI packages.\n\n### AiWander\n\nInstead of a location and distance based AI wander, we add a new package that limits the wandering to a single area.\n\nThere shouldn't be much need for this package, since AiPatrol and AiVisit offer better alternatives. But we offer it anyway to cover cases where neither of the new packages is seen as useful.\n\n### AiPatrol\n\nThis is a new package with no direct equivalent in the old system. The package contains the following fields:\n\n* A list of locations (in the new format)\n* A loop flag\n\nThe actor will walk from one location to the next. Once he has reached the last location he will either continue with the first location (if the loop flag is set) or walk down the list of locations in opposite direction and then start over (if the loop flag is not set).\n\nThe obvious use case for this package is a guard patrolling a perimeter or a place.\n\n### AiVisit\n\nThis is a new package with no direct equivalent in the old system. The package contains a list where each list item contains the following fields:\n\n* Location\n* Duration (float)\n* Chance (float)\n\nAn actor with this package will randomly choose a location from the list according to the chance field (locations with higher chance are picked more often), then move to this location and stay there until the duration has expired and then continue from the top.\n\nThis package is intended as an alternative to the old AiWander. Hopefully (by giving the NPCs actual places to go to instead of just wandering around aimlessly) it can achieve NPC behaviour that is less silly looking and at least gives the appearance of making sense.\n\n# Localisation\n\n## Problems\n\nThere are two fundamental problems with the way localisation is handled in vanilla MW:\n\n* Localised strings are used as internal identifiers that are referenced by other parts of the content\n* Localised strings are intermixed with other records\n\nThis results in multiple issues:\n\n* When localising the developer must hunt down all references to the localised strings and change them manually (cell names and topics).\n* When updating a localisation to a new version of the content file the translator has to manually pierce together his old translation and the new content file.\n* Changing a string that is used as an internal reference breaks the connection to the referenced records in relation to the non-localised file, which in turn makes it impossible to utilise automated tools that help for the localisation process.\n* Content files of mismatching languages generally can't be combined in a content file list. Mixing languages is generally not desirable for playing, but may sometimes be unavoidable. The ability to mix languages is highly desirable for developers (e.g. debugging).\n\n## Encoding\n\nWe already use UTF-8 internally. For post-1.0 versions of content file formats we will also use UTF-8 exclusively.\n\n## Localisation Content Files\n\nLocalisation will be contained in separate content files (omwlang) that are to be loaded on top of the content file to be localised.\n\nThe launcher will treat a content file and its localisation file as a single entity (see section about Identifying Meta-Data).\n\nWe may add a launcher language setting that determines the default language chosen by the launcher. This needs to be a separate option from the legacy encoding setting, which is currently also called language setting.\n\n## Localisation Records\n\nA common localisation scheme is to use the English text as a key. This way non-localised text does not need to bother with the localisation mechanism at all.\n\nThis is not a good idea, both in general and particularly in our case:\n\n* The assumption that English is the base language fails in certain areas of the MW community.\n* Text does not always translate 1-to-1 between languages. A popular example is the term Free Software. The free has two meanings: free as in free speech and free as in free beer. But in other languages (e.g. German) there are separate words for these two meanings. By using the word free as a key in multiple places with different meanings it would be impossible to correctly localise the text to German.\n* The original text might change through an update (fixed spelling mistake is an obvious use-case here). If the text is used as key that breaks the connection between the text in the original content file and the updates content file and therefore forces the translator to figure out what is going on and then make manual corrections.\n\nInstead we use an ID based scheme. We add a new type of record (localisation) that contains as a single field the localised text.\n\nIn legacy content files user visible text is contained in other records. On loading of these files we move this text out of these records and into separate localisation files. The ID of the localisation record will be generated from the ID of the other record with a suitable suffix (e.g. $name).\n\nBy factoring out all user visible text into a separate record type we make it possible for the editor to present the text to the translator in a single table.\n\nWe will allow the editing of the localisation record within the UI (table or dialogue) of the main record, so that for a content developer who is not involved in localisation the workflow is not being made more complicated.\n\n## Fallback-Strings\n\nThe openmw.cfg file contains a collection of fallback strings that were taken from Morrowind.ini. No localisation mechanism is provided (other than swapping out Morrowind.ini files).\n\nOpenMW uses these strings only when loading older content files. However \"older\" is a less specific term here than throughout the rest of this document. As OpenMW progresses through feature releases we will continue to add new user visible strings (e.g. labels for new user settings). Therefore we will need a fully functional localisation system for fallback strings alongside the primary record-based localisation system.\n\nWe will allow an optional language-code prefix (lp) in the key:\n\n````\nfallback-lp-somekey=Some Text\n````\n\nWhen looking up fallback strings the preferred language is chosen by the default language setting in the launcher (we may decide to provide a separate setting for fallback strings if this doesn't result in an overly complicated settings UI).\n\nIf a fallback string with this language-code is available it will be used. If no such fallback string is available, the default fallback string (without language-code prefix) will be used. The default fallback string must exist for all fallback strings with language codes. Otherwise the cfg file must be considered defective.\n\nWe add an option to the importer tool to import strings only, which then will be merged (with a user-specified language-code prefix) into an existing cfg file.\n\nNon-legacy fallback strings (added after 1.0) will be added to the global cfg file instead. We will accept localised versions of these strings provided by the community. Since these strings are not part of vanilla MW, there are no legal issues with including these strings in the official repository.\n\n## Cell Names\n\nThe handling of cell names in vanilla MW is one of the major localisation problems that need to be addressed post 1.0. Cell names are used both as the text that is presented to the user and as the internal identifier by which cells are referenced. This is further complicated by a system that allows the use of partial names.\n\nWe turn the name field (the ID) into an ID-only field. The actual user visible name is moved into an optional localisation record.\n\nWe allow referencing of cells by both ID and name. If the name is used, we emit an unobtrusive warning message.\n\nThis change does not fix the problem of already existing localisations that have modified the names of cells. See the section about localisation mapping below for a workaround.\n\n## Topics\n\nThe situation with dialogue topics is similar to the cell situation and we will apply the same fix, though with a minor modification.\n\nWhen referencing a topic by the localised text (instead of the ID) from within a dialogue text (this includes journal entries) we do not emit a warning message. Requiring the use of IDs within the dialogue text would be too much of a burden to the content developers.\n\n## Scripts\n\nVanilla scripts can contain user visible string literals. This interferes with localisation in an unacceptable way.\n\nUser visible string literals within scripts exist in three forms:\n\n1. MessageBox strings\n2. Topics (in AddTopic instruction)\n3. Cell Names (in various location related instructions)\n\nThere is nothing we can do to enforce a solution for #1 without causing unreasonable complications for script authors. But we can at least offer options.\n\n* We add a variant of the new LFormat script function (called just Format) which takes as its first parameter the ID of a localisation record instead of a string literal.\n* We add a script function called Text which takes the ID of a localisation record as argument and returns the matching string.\n\nIssues #2 and #3 can be enforced at least partially. We declare the use of user visible strings (instead of IDs) deprecated. For all new script instructions using the user visible string instead of the ID results in an error. Old instructions produce a warning instead.\n\n## Localisation Mapping\n\nThe new localisation scheme leaves existing localisations unaddressed. Localisations of Morrowind.esm break the connection of cell names and topics in relation to the un-localised Morrowind.esm (English).\n\nWe provide a workaround for this problem by introducing localisation mapping. Localisation mapping is a language-specific list of ID/user visible text pairs, provided as a text file. A similar scheme exists for the Russian localisation. We may decide to simple extend the existing implementation of this feature in OpenMW to provide localisation mapping.\n\nRegarding the localisation mapping files two important points need to be considered:\n\n* The files can not be generated automatically. Theoretically we could develop a tool that checks for cell and topic records in two Morrowind.esm files that match in fields other than ID and other user visible text. However this would still require manual checking and seems overall a lot of work for little gain.\n* We should not generate and distribute these files within the OpenMW project because of copyright reasons. This task would be left to the modding community.\n\n# GUI\n\n## Argument against general GUI customisation\n\nUnder general GUI customisation we understand a fully featured GUI framework that allows content developers to build their own GUI. This is an argument against such an approach.\n\nWhat are the use cases for a full-scale GUI framework? They seem to fall into two general categories:\n\n1. Alternatives for existing GUI elements (e.g. new inventory system, new load/save GUI, new alchemy GUI)\n2. GUI for a completely new game feature that does not exist in vanilla Morrowind\n\nWe have handled some instances of case #1 in the OpenMW engine itself already. These seem to do their job and have been well received. It makes sense to continue with this approach and keep this kind of GUI enhancements in the core project instead of handing it over to mod developers. The needs of total conversions in this regard can be met with a skinning system and additional configuration options (see below).\n\nThe second case is a little different. If we want enable mod developers to add new skills in the same style as for example alchemy we need a full GUI framework. The question is do we want to encourage this kind of content? Morrowind and also OpenMW are already very heavy on the GUI. A feature set that encourages a lighter use of GUI elements (in favour of more world interactivity instead) could result in overall better content quality.\n\nAt a smaller scale the need for interactive GUI features can be fulfilled by individual GUI elements (see below) that can be used from a regular script, similar to multiple choice message boxes.\n\nNote that by going down this route we do not block the addition of a full-scale GUI framework at a later time, if we should decide that we need one after all. All the features described here are useful in general and can be translated into a full-scale solution.\n\n## Skinning\n\nUnder the term skinning we understand changes to the look of a GUI without affecting the functionality. Vanilla Morrowind already provides a certain level of skinning-capacity though textures and Morrowind.ini settings. We are going to expand on these and clean up the skinning process. After 1.0 all skinning should be done exclusively through content files and associated resources files. Skinning does not belong in ini/cfg files.\n\n### Windows\n\nIn general we do not need to provide skinning options for individual windows. Instead we aim for a single set of options that are applied to all windows. This will ensure consistency throughout the GUI.\n\nThis is a preliminary list of elements that require customisation:\n\n* Window border/furniture textures and sizes\n* Text (Font, Colour, Size)\n* Window background\n* Other Widgets (e.g. Frames)\n\n### HUD\n\nMorrowind (and OpenMW 1.0) has only 4 HUD elements:\n\n* Mini-map\n* Active effect list\n* Dynamic stats bars with enemy health bar and selected weapon and spell display\n* Notifications (non-interactive message boxes)\n\nFor all these elements we need the ability to customise the the size and the position (Notifications are a special case here, see below). But we also need to give attention to individual HUD elements (see subsections below).\n\n### Mini-Map\n\nAt the very least we should make the width and the height of the mini-map configurable independently from each other. But we also should consider allowing other shapes (maybe defined via a texture).\n\n### Active effect list\n\nWe need to make the orientation of the effect list configurable (left, right, down, up).\n\n### Dynamic Stats\n\nDynamic stats are more complicated since they merge three elements:\n\n* Dynamic stats\n* Active weapon and spell\n* Enemy health bar\n\nThe first two go together well enough, but we should provide an option to decouple the enemy health bar and turn it into its own HUD elements.\n\nWe also need to consider that after 1.0 we won't always have three dynamic stats. There could be more or (in stage 2) less. The HUD element must be built in a way that it can adjust its layout accordingly without manual layouting.\n\n### Notifications\n\nCurrently we have a single hard-coded notification area (bottom centre of screen), where up to a fixed number of text paragraphs can be displayed in a message box style GUI element.\n\nWe introduce a new type of record (NotificationArea) with the following fields:\n\n* Position (top/centre/bottom, left/centre/right)\n* Scale\n* Maximum number of messages\n* Duration factor (scales how long a message stays on screen, depending on the length of the message)\n* Message box layout (size, textures)\n* Text (horizontal alignment, size)\n* Text-Wrap (on or off)\n* Default style (see below)\n\nWhen loading older omwgame files we inject a NotificationaArea record (sys::Messages) that matches the old hard-coded notification area.\n\nWe add a new script instructions that replaces the non-interactive variant of the old MessageBox instruction.\n\n* Message text, style = \"\", area = \"sys::Messages\"): If style is an empty string, the default style for the notification area is used.\n\n### Notification styles\n\nWe introduce a new type of record (NotificationStyle) with the following fields:\n\n* Colour\n* Font (including options for bold and italic)\n\nWhen loading older omwgame files we inject a NotificationStyle record (sys::DefaultMessageStyle) that matches the old hard-coded notification area.\n\n## New Input-Elements\n\nWe add a couple of new input elements that are each represented as a separate window:\n\n* Item selection: Takes a list of references and returns the list and an index. List is presented as a horizontal icon view of the items.\n* List selection: Takes a list of strings and returns the list and an index. List is presented as a vertical list of text items.\n* Numerical input: Takes a range, a precision value and a default value and returns a float.\n* Text input: Takes a default value and returns a string.\n\nAdditionally each window has a title bar, an okay button and and optional cancel button.\n\nFor each element we add a new script instruction that brings up the respective element. These instructions take the following arguments:\n\n* The input values listed above.\n* The ID of a script function that is called when the okay button is pressed\n* (optional) The ID of a script function that is pressed when the cancel button is pressed. If this argument is missing (or the ID string is empty) no cancel button is shown.\n* Window title text\n\n## Party HUD Element\n\nWe add an optional HUD element that shows the status of companions. This HUD element can be switched on/off and configured in the user settings.\n\nFor multiplayer we can also show the status of other players. There need to be visual distinction between companions and other players.\n\n## Container Windows\n\nThe vanilla Morrowind container windows (inventory, NPC inventory on loot/trade, actual container) has a very poor usability.\n\nMain issues are:\n\n* Icons are not distinguishable enough. Users need to regularly utilise tooltips to identify an item which defeats the purpose of the icon. This problems is caused both by the large number of different items in Morrowind and by the small icon size which does not leave much room for visual differentiation.\n* The available tools (item category tabs) are insufficient to manage a large inventory.\n\nWe need to improve upon this issue, but we also need to stay close to the original look and feel. We will make three improvements in a way that is as unobtrusive as possible:\n\n### View Modes\n\nWe offer three view modes similar to classical file browser:\n\n* Small Icon Grid: The vanilla Morrowind mode. If no small icon is available, we use a down-scaled version of the large icon.\n* Large Icon Grid: Like the vanilla Morrowind mode, but with larger icons (at least twice, but probably three times the size). We add a new field to all item type records that contains name of the large icon texture. If no large icon is available an up-scaled version of the small icon is used.\n* Table: One line per item (or item-stack). The columns shown are configurable in the user-settings.\n\nThe view mode is selected by three buttons that could be placed in the same row as the category tabs.\n\n### Search\n\nWe add a search box (either fixed if we can make space for it or as a pop-up). If the search box is not empty only those items are shown those names contain the search string.\n\n### Categories\n\nWe make item categories configurable via content files by introducing a new record (ItemCategory) with the following fields:\n\n* Category name\n* Item filter\n\nThe item filter will be based on item tags (see section Item-Interactions & -Management). These need to be boolean expressions (e.g. tag1 and tag2 or tag3 or not tag4) since a single tag won't allow for enough customisation. Since this is an issue sensitive to scaling (people have large inventories) we should aim for an implementation that is simple and fast.\n\nWhen loading older omwgame files we inject ItemCategory records for the vanilla categories.\n\nWe may explore an alternative GUI layout consisting of a vertical bar (maybe on the right side of the window) that contains an icon for each category instead of text.\n\nThe current horizontal text-based category selector is problematic for two reasons:\n\n* Since we hand over the creation of categories to mod developers we can expect a large number of them and therefore scaling becomes an issue. Horizontally arranged text items scale terribly because text items also primarily extend horizontally. There are workarounds for this problem but these are either bad or add alternative interface (e.g. a pull-down menu listing all tabs) which isn't a great solution either. Under no circumstances will we use the workaround that adds two arrow buttons left and right of the tab bar to change the visible section of the tab bar. This is an anti-pattern and everyone who has ever committed the horrendous crime of implementing this GUI-atrocity deserves to end up in usability hell.\n* In table mode we already have a bar at the top of the window. Two bars on the same window border give a cluttered, unclean appearance. \n\nIf we do use an alternative layout, we need to add an icon field to the ItemCategory record and we also need to consider whether we drop the vanilla one completely or let the user choose. Unless there is strong opposition (this is a significant change from Vanilla after all) we should choose the former option. If we opt for a less radical change we still should switch from text to icons to counter the scaling problem, even if we stick with the horizontal layout.\n\n## Character State\n\nCurrently there is no way to attach additional status information to items or the player character and show these in the appropriate GUI elements.\n\nWith record variables we already have the means to store additional stats. We only need a way to indicate to OpenMW that we want to display these stats.\n\nTo this end we add a new record type (CustomStat) with the following fields:\n\n* Type (item or character)\n* Label\n* Tooltip (optional)\n* Name of the record variable\n\nItem stats will be shown in the item tooltip. Character stats will be shown on the right side of the character stats window.\n\nWe fold bounty value and reputation into this system and inject suitable CustomStat records when loading older omwgame files.\n\nWe will also fold skill progression since level up (the number of skill gains for major and minor skills) into this system but without proving a CustomStat record (since this stat isn't displayed in Vanilla). This task is left to content developers (see below).\n\n### Skill Increases\n\nThe number of skill increases is an important value for the levelling process. Unfortunately these values are not presented to the player in Vanilla.\n\nWe add a new GMST (sys::TrackSkills) of type integer. When loading older omwgame files we inject this record with a default value of 0.\n\nIf the GMST is not 0 we show skill progression in two places:\n\n* The tooltip of the skill (number of skill levels gains)\n* The tooltip of the attribute (total number of level gains for all skills associated with this attribute)\n\n# Graphics\n\nTODO\n\nRandom collection of ideas that may be feasible or not:\n\nidea? simple text on instance via a new object type, changeable both during content development and during gameplay via script (e.g. sign post)\n\nidea? celestial de-hardcoding (number of moons, number of suns, custom objects, configurable movement across the sky)\n\nidea? animation de-hardcoding/allow additional animations; possible use-cases:\n\n* more idle animations\n* sitting animations\n* item handling animations (take a drink from a mug, hold a fishing rod, carry a box)\n\n# Editor\n\n## Scripting\n\nWe will use Python as a scripting language for OpenMW-CS. Adding a scripting language is a major task and for stage 1 we will most likely be limited to the basics. More advanced support can be added later.\n\nSandboxing isn't an issue here, since if we want editor extensions to be useful they can't be sandboxed anyway.\n\n### API\n\nWe expose the following API:\n\n* Read-Access to all records\n* Read-Access to the content file list\n* Read-Access to the resources lists\n* Command issuing \n\n### Operation Scripts\n\nWe let scripts to run as operations (exclusively for now). In the context of OpenMW-CS the term operation means the following:\n\n* An operation runs in a separate thread and has read only access to the content data\n* Multiple operations can run concurrently, but only one operation of each type (e.g. saving, verifier, python script) at a time\n* While at least one operation is running, the content data can not be modified\n* The operation can be terminated by the user at any time\n* The operation reports back to the main thread via messages (shown in a table like with the verifier) and progress state (represented as progress bar)\n\nA script operation may issue commands. These commands are queued up and then executed in the main thread once the operation has finished.\n\nThe operations script defines the GUI element it is triggered from; either through an API or an associated resource (depending on how we decide to manage Python script in OpenMW-CS).\n\nFor stage 1 we allow operation scripts to add menu items to the main menu and to context menus. We will not add a tools main menu item under which scripts are grouped, because this is akin to a misc category, the most useless category of all. We may consider allowing operation scripts to create their own top-level main menu items.\n\n## Debugging\n\nWe will expand on the use of OpenMW-CS as a debugging tool. To support this feature we require an easy to use IPC method (preferably platform-independent). Performance is of less importance since we are unlikely to send large amounts of data between OpenMW and OpenMW-CS.\n\nThe potential for debugging tools is almost unlimited. But for stage 1 we most likely will only implement some basic tools. Two easily implementable tools with great usability value are listed below.\n\nNote that all debugging tools will require that OpenMW is started from OpenMW-CS.\n\n### Marking\n\nWe add a new keyboard shortcut to OpenMW (only available when started from OpenMW-CS). When used OpenMW will look at the instance under the mouse pointer (or the crosshair if the mouse pointer is hidden). If this instance is part of a content file (i.e. not created during play) OpenMW will send the cell and RefID of the instance to OpenMW-CS. OpenMW-CS inserts the instance info into a table.\n\nThis table functions in a similar way to the verifier table; allowing the content developer to jump directly to records that have been identified as being in need of attention.\n\n### Script Error Reporting\n\nOpenMW currently reports script errors and warnings to the console only. When started from OpenMW-CS OpenMW will also send these errors back to OpenMW-CS which will present it in a verifier-like table to the user.\n\n## Namespaces\n\nWe will encourage the use of a single main namespace per content file (the content developer is free to add sub-namespaces within this namespace). To this end we will add a namespace field to the content file header.\n\nWhen adding new IDs the namespace from the header will be inserted into the input field as a default.\n\nWe also add function that produces a warning if the user tries to create an ID outside this namespace and outside the special namespaces project, session and default. This function can be disabled via the user-settings. If the content file doesn't have a main namespace (the default for all older content files) this function is inactive.\n\n## Help\n\nOpenMW-CS currently has very little in-application help. Improving this will be an ongoing process. For stage 1 we will focus on scripting.\n\nWe will add a keyboard shortcut and a context menu item to the script editor. When activated while the cursor is on a keyword we show a help text that explains the syntax and the function.\n\nIdeally we would want to use the same help text within the application and in our documentation. A way to handle this needs to be determined.\n\nThe location where we show the help text needs to be determined. For script editor views we could reuse the widget that displays errors. Or we could add a separate subview type for help text. We may decide to add a help main menu item from which the help system can be navigated.\n\n## Spell Checker\n\nWe will add a spell checker by utilising an existing spell check solution. All names (e.g. items, cells, races) will be implicitly added to the list of known words.\n\n## Porting Tools\n\nUnder the term porting we understand the process of moving a content file from one version of a dependency to another version of the same dependency.\n\nAt the simplest level this requires only changing the dependency information but we will add additional functions that help with the process.\n\nFor the porting of localisation content files (i.e. a new version of a content file is released and the localisation needs to adapt to it) we will add the following functions:\n\n* OpenMW-CS will look up all localisation records that have been added in the new version and present these to the user in a list\n* OpenMW-CS will look up all localisation records that have been modified in the new version and present these to the user in a list that shows both the old and the new value\n\n# Misc\n\nThis is a random collection of other enhancements that don't fit anywhere else.\n\n## Content User Settings\n\nCurrently there is no clean way for content files to provide their own user settings.\n\nTo compensate for this shortcoming we introduce a new record type (UserSetting) and a script hook  and we rearrange the user settings window.\n\n### UserSettings Record\n\nWe add a new record type that describes user settings options. This record type offers several kinds of user settings but does not specify the layout of the GUI elements that are used to represent the settings. We strictly separate the data and the representation (skinning).\n\nUser settings data is stored in GMSTs.\n\nA user settings record consists of the following fields:\n\n* ID of the GMST used to store the record\n* Category ID\n* Label (string)\n* Tooltip (string, optional)\n* Type of setting (integer, hard-coded)\n* Additional data\n\nThe following user setting types are available:\n\n* bool (type 0): GMST is an integer; GUI is a check box; additional data: default value\n* numeric, integer (type 1): GMST is an integer; GUI is a spin box; additional data: upper bound, lower bound, default value\n* numeric, float (type 2): GMST is a float; GUI is a spin box; additional data; upper bound, lower bound, precision, default value\n* list (type 3) GMST is an integer; GUI is a combo box; additional data; list of strings for combo box text, default value\n* slider (type 4): GMST is a float; GUI is a slider; additional data; upper bound, lower bound, default value\n\n### Categories\n\nUser settings categories are represented as separate pages/tabs in the GUI. We specify categories via category IDs (strings). Currently there does not appear to be a need for a separate user settings category record, since this record would have no data.\n\nContent files can create new categories (simply by referencing them in a user settings record) or add to existing categories (including the vanilla ones). We should consider adding a couple of additional default categories (including localised labels, but empty and invisible until populated by content files) to help content developers organise their settings in a coherent way.\n\nThe categories in vanilla MW are General, Audio, Controls and Graphics. Content files can add settings to these too, but we should consider reorganisation. General is not a good category. We should consider splitting it up. We should also consider moving the key bindings into their own category. A separate key bindings category would have to be the only exception to the rule that content files are allowed to add settings to pre-existing categories.\n\n### Hook\n\nWe add a new hook (sys::SettingsChanged) that is triggered whenever a change to the user settings is mode. The hook function takes a list of strings as argument, containing the IDs of user settings that have been changed.\n\n### Settings Window\n\nThe current design of the user settings window in OpenMW is not scalable (it can not accommodate more settings without getting significantly worse). Therefore a complete redesign is required. We should aim for something that resembles the OpenMW-CS user settings window.\n\nThis also provides an opportunity to add a GUI to many cfg file only settings that got added prior to 1.0. But we should consider if some of these wouldn't be better suited for bare GMSTs without user setting.\n\n## Launcher Skinning\n\nCurrently our launcher has a generic look with some Morrowind branding. This is not desirable for total conversions.\n\nTo compensate for this shortcoming we introduce launcher skinning. The launcher skinning is determined by the currently selected omwgame file. We add an new, optional record type that is allowed only in omwgame files.\n\nThe exact scope of this new record remains to be determined, but icons and backgrounds are obvious candidates.\n\n## Body of Liquid\n\nCurrently we at most have a single body of liquid in each cell that covers the entire cell. This makes it impossible to create additional lakes that are not at sea level.\n\nTo compensate for this shortcoming we introduce a new object type: LiquidBody.\n\nActors within a LiquidBody instance behave in the same way as actors in the regular cell water (drowning, swimming).\n\nA LiquidBody object record has the following fields:\n\n* Liquid ID (see de-hardcoding section, water sub-section)\n* Height (float)\n* Shape\n\nShape is a closed Bézier curve that can be edited in the editor. This shape defines a surface (the liquid surface). The depths of liquid body is defined by the height value.\n\nWe can imply that only the surface of the LiquidShape instance is exposed. The other sides do not require rendering.\n\n## Dagoth Ur Fix Un-Fix\n\nWe introduced a fix for a defect in Morrowind.esm by blocking remote access to instances of the object dagoth_ur_1 via mod instructions for dynamic stats. We will now bind this fix to a new integer GMST in the sys namespace. This will allow content developers to disable this fix in their mods (hopefully after giving poor old Dagoth Ur a bit more health).\n\n## Comment Subrecords\n\nWe add a new optional sub-record to all top-level records. This subrecord contains a string that can be used for writing down notes and additional documentation by content developers.\n\nOpenMW does not need to read these records. If we find that comment subrecords add too much bloat to the record data structures we may decide to skip loading them in OpenMW and even keep them out of the data structures used by OpenMW.\n\nOpenMW-CS will add the comment sub-record as a regular data field. We will provide an option to show the comments as tooltips in various places.\n\nNote that the vanilla esm format already contains a similar field in the land texture record. This field needs to be folded into the new command sub-record.\n\n## No Pause Mode\n\nWe add four new GMSTs of type integer (default value 1):\n\n* sys::StatusPause\n* sys::DialogPause\n* sys::ReadingPause\n* sys::CraftingPause\n\nThese decide if the game is paused when in status mode (right-click), dialogue mode, journal mode or any of the crafting modes (e.g. alchemy). To handle sys::DialogPause==0 properly we ignore  dialogues initiated by NPCs or via script, if the player is already in a dialogue.\n\n## Instance Persistence\n\nWe add a new optional field to all object records and instance subrecords, a single enum-like integer.\n\nThe value in the object record is considered by OpenMW-CS when creating new instances of that object only (it functions as a default value). OpenMW considers the value in the object record when spawning a new instance during gameplay.\n\nThe possible values of the new field are as follows:\n\n* Persistent: Instance can be changed and is included in the save file (this matches the vanilla/OpenMW 1.0 behaviour).\n* Transient: Instance can be changed, but is not stored in the save file\n* TransientNoSync: Same as Transient; in a multiplayer environment the instance does not need to be synchronised across multiple clients\n* Immutable: Instance can not be changed and is not stored in the save file. An attempt to manipulate the instance via a script results in an error. Attempting to spawn an immutable instance into the world also results in an error.\n\nIf not present the field defaults to Persistent, except for static objects and instances for which it defaults to Immutable.\n\nThe DontSaveObject script instructions (ignored in OpenMW 1.0) can be used to switch a persistent instance to transient state. This function is meant primarily for (marginally) improved backwards compatibility. We do not encourage the use of DontSaveObject for new content.\n\nImmutable statics are a marginal departure from vanilla Morrowind, since statics can be manipulated to some degree. However since that never worked properly in the first place it is not very likely that we break anything and even in case of breakage a quick and simple fix to the content is available. Combined with the large benefits of this feature (drastically reduced save game file size, for one thing) taking the risk of introducing an incompatibility seems worthwhile.\n\n## Multi-Player\n\nOpenMW currently does not support multi-player and is unlikely to do so in the near or intermediate future. But we still can make improvements that aid the multi-player project alongside OpenMW and especially content developers that wish to improve the multi-player experience of their content.\n\nOur goal for multi-player (both in regards to the separate project and if/when multi-player gets integrated into OpenMW) is for content to be usable both in single and in multi-player. This goal isn't fully achievable without special attention from content developers. No matter how we approach multi-player some assumption made by content developers for single-player will be broken, which will then result in some broken content. The best we can hope for is to minimise the breakage.\n\nTo this end we will introduce features that will allow content developers to include support for multi-player; mostly in the form of script instructions. These feature must be designed in a way that does not interfere with single-player. Several examples can be found throughout this document.\n\nIn the same vein we also provide optimisation options for multi-player, i.e. ways to flag state as not requiring synchronisation across server and clients.\n\n### Content Files\n\nThere will always be content files that do not take multi-player into consideration (Morrowind.esm being the obvious case, but some or most current content developers may fall into the same category). This can easily be addressed by putting another content file with fixes on top of the content file (provided the original file had a suitable license).\n\nWe will mark up these content files in a similar way as we mark localisations, so that the launcher can automatically match them to their respective main content file and automatically enable them when running multi-player.\n\n### Scripting\n\nScripting is the biggest issue in regards to multi-player. The current implementation of the multi-player project executes scripts client-side. This is unlikely to work. Anything related to random numbers is an obvious source for (potentially) game-breaking de-syncs. If we dig deeper we will probably find more sources of problems.\n\nOn the other hand scripts that contain the ID player can not be executed server-side, because there would be no way to know which player is meant.\n\nThe changes required to address these issues are substantial. This is not a task for near future post-1.0 development or even intermediate future development.\n\nWhat we can do now is to help content developers create their content multi-player ready, so that when multi-player becomes available in OpenMW their content will work out of the box (minus the unavoidable bugs that come from not being able to test content).\n\n### Player\n\nWe can have the script engine detect if a script contains the ID player and if that is the case let it execute on the client instead of the server. However that is only a workaround and it is not reliable. Obvious cases where this could fail are:\n\n* Function calls where one of the involved scripts including the ID player and the other doesn't\n* The ID player being used not explicitly but via an ID variable.\n\nIt would still make sense to implement this workaround as a fallback.\n\nFor a proper solution to the problem we need to engineer the scripting language in a specific way and provide guidance to content developers how to use it in a multi-player compatible style.\n\nIn particular we need to provide features that allow for the avoidance of the ID literal *Player*.\n\nIn some cases the solution is obvious. For example the script slot for OnActivate can pass the activating actor as an argument to the script. Such a script may be able to operate in a more generalised way that does not require an explicit reference to the player. If the script needs to distinguish between player actors and non-player actors it can check the received actor reference against the list of player references.\n\nThere are also many script functions that imply the player (e.g. EnableBirthMenu). These must be changed to work on an actor instead.\n\nThis is a complex issue that will require more design and deliberation before we can act on it.\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "parse_cmake\nsphinx==1.8.5\ndocutils==0.17.1\n"
  },
  {
    "path": "docs/source/_static/.keep",
    "content": ""
  },
  {
    "path": "docs/source/_static/figures.css",
    "content": ".figure img {\n    display: block;\n    margin: auto;\n    box-shadow: 10px 10px 40px -5px #bbbbbb;\n}"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# OpenMW documentation build configuration file, created by\n# sphinx-quickstart on Wed May 14 15:16:35 2014.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\nimport os\nimport sys\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\nproject_root = os.path.abspath('../../')\nsys.path.insert(0, project_root)\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\nneeds_sphinx = '1.7'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.doctest',\n    'sphinx.ext.todo',\n    'sphinx.ext.coverage',\n    'sphinx.ext.viewcode',\n    'sphinx.ext.autosectionlabel',\n]\n\n#autosectionlabel_prefix_document = True\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'OpenMW'\ncopyright = u'2020, OpenMW Team'\n\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\n# The full version, including alpha/beta/rc tags.\n\nrelease = version = \"UNRELEASED\"\n\n\ndef get_openmw_version(haystack):\n    needle = 'OPENMW_VERSION_MAJOR'\n    line_counter = 0\n    for hay in haystack:\n        if needle in str(hay):\n            break\n        line_counter += 1\n\n    version = '.'.join([haystack[line_counter][1][1].contents,\n                        haystack[line_counter+1][1][1].contents,\n                        haystack[line_counter+2][1][1].contents])\n    return version\n\n\ntry:\n    from parse_cmake import parsing\n    cmake_raw = open(project_root+'/CMakeLists.txt', 'r').read()\n    cmake_data = parsing.parse(cmake_raw)\n    release = version = get_openmw_version(cmake_data)\n\nexcept Exception as ex:\n    print(\"WARNING: Version will be set to '{0}' because: '{1}'.\".format(release, str(ex)))\n    import traceback; traceback.print_exc()\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = cpp\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = []\n\n# The reST default role (used for this markup: `text`) to use for all\n# documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n# If true, keep warnings as \"system message\" paragraphs in the built documents.\n#keep_warnings = False\n\nprimary_domain = 'c'\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\nhtml_theme = 'sphinx_rtd_theme'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\n#html_theme_path = []\n\n#html_css_files = 'figures.css'         use this once Sphinx 1.8 is released!!!\n\ndef setup(app):\n    app.add_stylesheet('figures.css')\n\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = 'OpenMW Documentation'\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = 'https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/_static/images/openmw.png'\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = 'https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/_static/images/favicon.png'\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = [\n                    '_static'\n                    ]\n\n# Add any extra paths that contain custom files (such as robots.txt or\n# .htaccess) here, relative to this directory. These files are copied\n# directly to the root of the documentation.\n#html_extra_path = []\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'OpenMWdoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n# The paper size ('letterpaper' or 'a4paper').\n#'papersize': 'letterpaper',\n\n# The font size ('10pt', '11pt' or '12pt').\n#'pointsize': '10pt',\n\n# Additional stuff for the LaTeX preamble.\n#'preamble': '',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n  ('index', 'OpenMW.tex', u'OpenMW Documentation',\n   u'Bret Curtis', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('index', 'openmw', u'OpenMW Documentation',\n     [u'Bret Curtis'], 1)\n]\n\n# If true, show URL addresses after external links.\n#man_show_urls = False\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n  ('index', 'OpenMW', u'OpenMW Documentation',\n   u'Bret Curtis', 'OpenMW', 'One line description of project.',\n   'Miscellaneous'),\n]\n\n# Documents to append as an appendix to all manuals.\n#texinfo_appendices = []\n\n# If false, no module index is generated.\n#texinfo_domain_indices = True\n\n# How to display URL addresses: 'footnote', 'no', or 'inline'.\n#texinfo_show_urls = 'footnote'\n\n# If true, do not generate a @detailmenu in the \"Top\" node's menu.\n#texinfo_no_detailmenu = False\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": "Welcome to OpenMW's Documentation!\n==================================\n\n.. toctree::\n\t:caption: Table of Contents\n\t:maxdepth: 3\n\n\tmanuals/index\n\treference/index\n"
  },
  {
    "path": "docs/source/manuals/index.rst",
    "content": "User Manuals\n============\n\n.. toctree::\n    :maxdepth: 2\n\n    openmw-cs/index\n    installation/index\n"
  },
  {
    "path": "docs/source/manuals/installation/common-problems.rst",
    "content": "###############\nCommon Problems\n###############\n\nERROR: Unknown fallback name: FontColor_color_header\n####################################################\n\n:Symptoms:\n\tOpenMW crashes at startup with\n\t``ERROR: Unknown fallback name: FontColor_color_header``\n\tmessage at the end of ``openmw.log``, located `here </docs/source/reference/modding/paths>`_.\n\n:Cause:\n\tThe OpenMW `configuration file </docs/source/reference/modding/paths>`_ ``openmw.cfg``\n\tis severely lacking and missing fallback values\n\tbecause \"Settings Importer\" was not run correctly.\n\n:Fix:\n\tRe-run \"Settings Importer\" from the OpenMW launcher.\n\nInstalling game files via Steam on macOS: DISK WRITE ERROR\n##########################################################\n\n:Symptoms:\n\tSteam stages the download for Morrowind, but does not proceed.\n\tThe download will read \"Paused: DISK WRITE ERROR\".\n\n:Cause:\n\tThe OpenMW `configuration file </docs/source/reference/modding/paths>`_ ``openmw.cfg``\n\tis severely lacking and missing fallback values\n\tbecause \"Settings Importer\" was not run correctly.\n\n:Fix:\n\tOpen appmanifest_22320.acf in your favorite text editor.\n\tLocate or create an entry under the \"StateFlags\" entry titled \"installdir\"\n\tand give it the value \"Morrowind\".\n\tYour file should now look something like this::\n\n\t\t\"AppState\"\n\t\t{\n\t\t\t\"appid\"         \"22320\"\n\t\t\t\"Universe\"              \"1\"\n\t\t\t\"name\"          \"The Elder Scrolls III: Morrowind\"\n\t\t\t\"StateFlags\"            \"4\"\n\t\t\t\"installdir\"            \"Morrowind\"\n\n\t\t\t[other entries]\n\t\t}\n\n\tRestart the Steam client. The download should now proceed.\n\nIn-game textures show up as pink\n################################\n\n:Symptoms:\n    Some textures don't show up and are replaced by pink \"filler\" textures.\n\n:Cause:\n    Textures shipped with Morrowind are compressed with S3TC, a texture compression algorithm that was patented in\n    the United States until October 2017. Software drivers and operating system distributions released before that date\n    may not include support for this algorithm.\n\n:Fix:\n    Upgrade your graphics drivers and/or operating system to the latest version.\n\nMusic plays, but only a black screen is shown\n#############################################\n\n:Symptoms:\n    When launching OpenMW, audio is heard but there is only a black screen.\n\n:Cause:\n    This can occur when you did not import the Morrowind.ini file when the launcher asked for it.\n\n:Fix:\n    To fix it, you need to delete the `launcher.cfg` file from your configuration path\n    ([Path description on Wiki](https://wiki.openmw.org/index.php?title=Paths)), then start the launcher, select your\n    Morrowind installation, and when the launcher asks whether the `Morrowind.ini` file should be imported, make sure\n    to select \"Yes\"."
  },
  {
    "path": "docs/source/manuals/installation/index.rst",
    "content": "##################\nInstallation Guide\n##################\n\nIn order to use OpenMW, you must install both the engine and the game files for a compatible game.\n\n.. toctree::\n\t:maxdepth: 2\n\n\tinstall-openmw\n\tinstall-game-files\n\tcommon-problems"
  },
  {
    "path": "docs/source/manuals/installation/install-game-files.rst",
    "content": "##################\nInstall Game Files\n##################\n\nOpenMW is a complete game engine that can either run `Morrowind`_\nor original projects created with OpenMW-CS, such as `Example Suite`_.\n\nMorrowind\n#########\n\nRunning the Morrowind Installation Wizard\n=========================================\n\n#.\tLaunch the OpenMW Launcher\n#.\tLaunch the Installation Wizard\n\n\t.. note::\n\t\tIf you are prompted with an error message stating\n\t\t\"Could not find the Data Files location,\"\n\t\tclick the \"Run Installation Wizard\" button.\n\t.. note::\n\t\tIf you arrive at the main screen, click the \"Settings\" tab,\n\t\tand then click the \"Run Installation Wizard\" button.\n\n#.\tFollow further instructions below\n\tto install Morrowind from either a retail CD or an existing installation.\n\n\t-\t**Morrowind (from retail CD)**\n\n\t\t#.\tMake sure that the retail CD is in your computer's CD/DVD drive\n\t\t\tand the Installation Wizard is running.\n\t\t#.\tOn the \"Select Installation Method\" screen of the Installation Wizard,\n\t\t\tchoose \"Install Morrowind to a New Location\" and click the \"Next\" button.\n\t\t#.\tChoose a location to install Morrowind to your hard drive\n\t\t\t(or accept the suggested location) and click the \"Next\" button.\n\t\t#.\tSelect your preferred language for the installation\n\t\t\tand click the \"Next\" button\n\t\t#.\tSelect which official expansions (Tribunal or Bloodmoon) should be installed.\n\t\t\tFor best results, it is recommended to have both expansions installed.\n\t\t#.\tClick the \"Install\" button.\n\n\t-\t**Morrowind (from existing installation)**\n\n\t\t#.\tOn the \"Select Installation Method\" screen of the Installation Wizard,\n\t\t\tchoose \"Select an existing Morrowind installation\" and click the \"Next\" button\n\t\t#.\tSelect an installation. If nothing is detected, click browse.\n\t\t#.\tNavigate to the directory containing the file ``Morrowind.esm`` and select that file.\n\n#.\tYou will be asked if you wish to import settings from ``Morrowind.ini``.\n\tSelect \"Import\", otherwise OpenMW will not work.\n\t(You do not need to check the box \"Include selected masters and plugins\").\n#.\tThe OpenMW launcher window should now open.\n\tSwitch to the \"Data Files\" tab and check the box to the left of ``Morrowind.esm``.\n#.\tYou are now ready to play!\n\nInstalling Morrowind\n====================\n\n-----------------\nRetail CD and GOG\n-----------------\n\nWindows users can run the installer if they haven't already.\nBy default, both Bethesda's official installer on the retail CD\nand the GOG installer install to ``C:\\Program Files\\Bethesda Softworks\\Morrowind``.\nYou will find ``Morrowind.esm`` there.\n\nUsers of other platforms running Wine, will find it at\n``~/.wine/drive_c/Program Files/Bethesda Softworks/Morrowind``\n\n-----\nSteam\n-----\n\nWindows\n-------\n\nWindows users can download Morrowind through Steam.\nAfterwards, you can point OpenMW to the Steam install location at\n``C:\\Program Files\\Steam\\SteamApps\\common\\Morrowind\\Data Files\\``\nand find ``Morrowind.esm`` there.\n\nmacOS\n-----\n\nIf you are running macOS, you can also download Morrowind through Steam:\n\n#.\tNavigate to ``/Users/YOUR_USERNAME_HERE/Library/Application Support/Steam/steamapps/``\n#.\tCreate a file called ``appmanifest_22320.acf``\n\t(the number is based on its `Steam App ID <https://steamdb.info/app/22320/>`_).\n\tIf using TextEdit,\n\tmake sure that your document is in plain text mode by going to the menu bar\n\tand choosing \"Format\" -> \"Make Plain Text\".\n\tAlso, ensure that it's not named with the extension ``.acf.txt``.\n\tAdd the following into that file::\n\n\t\t\"AppState\"\n\t\t{\n\t\t\t\"AppID\" \"22320\"\n\t\t\t\"Universe\" \"1\"\n\t\t\t\"StateFlags\" \"1026\"\n\t\t\t\"installdir\" \"The Elder Scrolls III - Morrowind\"\n\t\t}\n\n#.\tLaunch the Steam client and let it download. You can then find ``Morrowind.esm`` at\n\t``~/Library/Application Support/Steam/steamapps/common/The Elder Scrolls III - Morrowind/Data Files/``\n\nLinux  \n----\nDebian/Ubuntu - using \"Steam Proton\" & \"OpenMW launcher\". \n----\n#. Install Steam from \"Ubuntu Software\" Center  \n#. Enable Proton (basically WINE under the hood). This is done in the Steam client menu drop down. Select, \"Steam | Settings\" then in the \"SteamPlay\" section check the box next to \"enable steam play for all other titles\"  \n#. Now Morrowind should be selectable in your game list (as long as you own it). You can install it like any other game, choose to install it and remember the directory path of the location you pick.\n#. Once the game files are installed, we can now install the open OpenMW Engine. I used \"OpenMW launcher\" from \"Ubuntu Software\" Center this has a wizard to help with the basic setup of OpenMW.  \n#. Launch \"OpenMW launcher\" and follow the setup wizard, when asked, point it at the location you installed Morrowind to, we will be looking for the directory that contains the Morrowing.esm file, for example '/steam library/steamapps/common/Morrowind/Data Files/'.\n#. Everything should now be in place, click that big \"PLAY\" button and fire up OpenMW.\n\nNb. Bloodmoon.esm needs to be below Tribunal.esm in your datafiles list, if you dont have the right order a red \"!\" will apear next to the filename in the datafiles section of the OpenMW launcher, just drag bloodmoon below tribunal to fix it.\n\nWine\n----\n\nUsers of other platforms running Wine can run Steam within it\nand find ``Morrowind.esm`` at\n``~/.wine/drive_c/Program Files/Steam/SteamApps/common/Morrowind/Data Files/``.\n\nExample Suite\n#############\n\nExample Suite is a demo showing the capabilities of the OpenMW engine.\nAt this time, it requires Morrowind to be installed to run,\nbut does not use any assets from it.\nIn the future, it will be possible to run without installing Morrowind first.\n\n#.\t`Install Morrowind <Installing Morrowind_>`_\n#.\t`Download the latest version <https://github.com/OpenMW/example-suite/releases>`_\n#.\tFollow the platform-specific instructions in the zip file's ``Installation.md`` file.\n\n"
  },
  {
    "path": "docs/source/manuals/installation/install-openmw.rst",
    "content": "==============\nInstall OpenMW\n==============\n\nThe (easier) Binary Way\n=======================\n\nIf you're not sure what any of the different methods mean, you should probably stick to this one.\nSimply download the latest version for your operating system from\n`github.com/OpenMW/openmw/releases <https://github.com/OpenMW/openmw/releases>`_\nand run the install package once downloaded. It's now installed!\n\n\t.. note::\n\t\tThere is no need to uninstall previous versions\n\t\tas OpenMW automatically installs into a separate directory for each new version.\n\t\tYour saves and configuration are compatible and accessible between versions.\n\nThe (bleeding edge) Source Way\n==============================\n\nVisit the `Development Environment Setup <https://wiki.openmw.org/index.php?title=Development_Environment_Setup>`_\nsection of the Wiki for detailed instructions on how to build the engine.\n\nThe Ubuntu Way\n==============\n\nA `Launchpad PPA <https://launchpad.net/~openmw/+archive/openmw>`_ is available.\nAdd it and install OpenMW::\n\n\t$ sudo add-apt-repository ppa:openmw/openmw\n\t$ sudo apt-get update\n\t$ sudo apt-get install openmw openmw-launcher\n\n.. note::\n\tOpenMW-CS must be installed separately by typing::\n\n\t\t$ sudo apt-get install openmw-cs\n\nThe Arch Linux Way\n==================\n\nThe binary package is available in the official [community] Repositories.\nTo install, simply run the following as root (or in sudo)::\n\n\t# pacman -S openmw\n\nThe Void Linux Way\n==================\n\nThe binary package is available in the official Repository\nTo install simply run the following as root (or in sudo)::\n\n\t# xbps-install openmw\n\nThe Debian Way\n==============\n\nOpenMW is available from the unstable (sid) repository of Debian contrib\nand can be easily installed if you are using testing or unstable.\nHowever, it depends on several packages which are not in stable,\nso it is not possible to install OpenMW in Wheezy without creating a FrankenDebian.\nThis is not recommended or supported.\n\n"
  },
  {
    "path": "docs/source/manuals/openmw-cs/files-and-directories.rst",
    "content": "Files and Directories\n#####################\n\nIn this chapter of the manual we will cover the usage of files and directories\nby OpenMW CS. Files and directories are file system concepts of your operating\nsystem, so we will not be going into specifics about that, we will only focus\non what is relevant to OpenMW CS.\n\n\nBasics\n******\n\n\nDirectories\n===========\n\nOpenMW and OpenMW CS use multiple directories on the file system. First of all\nthere is a *user directory* that holds configuration files and a number of\ndifferent sub-directories. The location of the user directory is hard-coded\ninto the CS and depends on your operating system.\n\n================  =========================================\nOperating System  User Directory\n================  =========================================\nGNU/Linux         ``~/.config/openmw/``\nOS X              ``~/Library/Application Support/openmw/``\nWindows           ``C:\\Users\\ *Username* \\Documents\\my games\\OpenMW``\n================  =========================================\n\nIn addition to to this single hard-coded directory both OpenMW and OpenMW CS\nneed a place to search for actual data files of the game: textures, 3D models,\nsounds and record files that store objects in game; dialogues and so on. These\nfiles are called *content files*. We support multiple such paths (we call them\n*data paths*) as specified in the configuration. Usually one data path points\nto the directory where the original Morrowind game is either installed or\nunpacked to. You are free to specify as many data paths as you would like,\nhowever, there is one special data path that, as described later, which is used\nto store newly created content files.\n\n\nContent files\n=============\n\nThe original Morrowind engine by Bethesda Softworks uses two types of content\nfiles: `ESM` (master) and `ESP` (plugin). The distinction between those two is\nnot clear, and often confusing. One would expect the `ESM` (master) file to be\nused to specify one master, which is then modified by the `ESP` plugins. And\nindeed: this is the basic idea. However, the official expansions were also made\nas ESM files, even though they could essentially be described as really large\nplugins, and therefore should have been `ESP` files. There were technical\nreasons behind this decision – somewhat valid in the case of the original\nengine, but clearly it is better to create a system that can be used in a more\nsensible way.  OpenMW achieves this with our own content file types.\n\nWe support both ESM and ESP files, but in order to make use of new features in\nOpenMW one should consider using new file types designed with our engine in\nmind: *game* files and *addon* files, collectively called *content files*.\n\n\nOpenMW content files\n--------------------\n\nThe concepts of *Game* and *Addon* files are somewhat similar to the old\nconcept of *ESM* and *ESP*, but more strictly enforced. It is quite\nstraight-forward: If you want to make new game using OpenMW as the engine (a\nso called *total conversion*) you should create a game file. If you want to\ncreate an addon for an existing game file create an addon file. Nothing else\nmatters; the only distinction you should consider is if your project is about\nchanging another game or creating a new one. Simple as that.\n\nAnother simple thing about content files are the extensions: we are using\n``.omwaddon`` for addon files and ``.omwgame`` for game files.\n\n\nMorrowind content files\n-----------------------\n\nUsing our content files is recommended for projects that are intended to use\nthe OpenMW engine. However, some players might wish to still use the\noriginal Morrowind engine. In addition thousands of *ESP*/*ESM* files were\ncreated since 2002, some of them with really outstanding content. Because of\nthis OpenMW CS simply has no other choice but to support *ESP*/*ESM* files. If\nyou decide to choose *ESP*/*ESM* file instead of using our own content file\ntypes you are most likely aiming at compatibility with the original engine. This\nsubject is covered in its own chapter of this manual.\n\n\n.. TODO This paragraph sounds weird\n\nThe actual creation of new files is described in the next chapter. Here we are\ngoing to focus only on the details you need to know in order to create your\nfirst OpenMW CS file while fully understanding your needs. For now let’s just\nremember that content files are created inside the user directory in the the\n``data`` subdirectory (that is the one special data directory mentioned\nearlier).\n\n\nDependencies\n------------\n\nSince an addon is supposed to change the game it follows that it also depends\non the said game to work. We can conceptualise this with an example: your\nmodification is changing the price of an iron sword, but what if there is no\niron sword in game? That's right: we get nonsense. What you want to do is tie\nyour addon to the files you are changing. Those can be either game files (for\nexample when making an expansion island for a game) or other addon files\n(making a house on said island). Obviously It is a good idea to be dependent\nonly on files that are really changed in your addon, but sadly there is no\nother way to achieve this than knowing what you want to do. Again, please\nremember that this section of the manual does not cover creating the content\nfiles – it is only a theoretical introduction to the subject. For now just keep\nin mind that dependencies exist, and is up to you to decide whether your\ncontent file should depend on other content files.\n\nGame files are not intended to have any dependencies for a very simple reasons:\nthe player is using only one game file (excluding original and the dirty\nESP/ESM system) at a time and therefore no game file can depend on another game\nfile, and since a game file makes the base for addon files it can not depend on\naddon files.\n\n\nProject files\n-------------\n\nProject files act as containers for data not used by the OpenMW game engine\nitself, but still useful for OpenMW CS. The shining examples of this data\ncategory are without doubt record filters (described in a later chapter of the\nmanual). As a mod author you probably do not need or want to distribute project\nfiles at all, they are meant to be used only by you and your team.\n\n.. TODO \"you and your team\": is that correct?\n\nAs you would imagine, project files make sense only in combination with actual\ncontent files. In fact, each time you start to work on new content file and a\nproject file was not found, one will be created. The extension of project files\nis ``.project``. The whole name of the project file is the whole name of the\ncontent file with appended extension. For instance a ``swords.omwaddon`` file\nis associated with a ``swords.omwaddon.project`` file.\n\nProject files are stored inside the user directory, in the ``projects``\nsubdirectory. This is the path location for both freshly created project files,\nand a place where OpenMW CS looks for already existing files.\n\n\nResource files\n==============\n\n.. TODO  This paragraph sounds weird\n\nUnless we are talking about a fully text based game, like Zork or Rogue, one\nwould expect that a video game is using some media files: 3D models with\ntextures, images acting as icons, sounds and anything else. Since content\nfiles, no matter whether they are *ESP*, *ESM* or new OpenMW file type, do not\ncontain any of those, it is clear that they have to be delivered with a\ndifferent file. It is also clear that this, let’s call it “resources file“,\nhas to be supported by the engine. Without code handling those files it is\nnothing more than a mathematical abstraction – something, that lacks meaning\nfor human beings.  Therefore this section must cover ways to add resources\nfiles to your content file, and point out what is supported. We are going to do\njust that.  Later, you will learn how to make use of those files in your\ncontent.\n\n\nAudio\n-----\n\nOpenMW uses FFmpeg_ for audio playback, and so we support every audio type\nsupported by that library. This makes a huge list. Below is only small portion\nof the supported file types.\n\nmp3 (MPEG-1 Part 3 Layer 3)\n   A popular audio file format and de facto standard for storing audio. Used by\n   the Morrowind game.\n\nogg\n   An open source, multimedia container file using a high quality Vorbis_ audio\n   codec. Recommended.\n\n\nVideo\n-----\n\nVideo As in the case of audio files, we are using FFmepg to decode video files.\nThe list of supported files is long, we will cover only the most significant.\n\nbik\n   Videos used by the original Morrowind game.\n\nmp4\n   A multimedia container which use more advanced codecs (MPEG-4 Parts 2, 3 and\n   10) with a better audio and video compression rate, but also requiring more\n   CPU intensive decoding – this makes it probably less suited for storing\n   sounds in computer games, but good for videos.\n\nwebm\n   A new, shiny and open source video format with excellent compression. It\n   needs quite a lot of processing power to be decoded, but since game logic is\n   not running during cutscenes we can recommend it for use with OpenMW.\n\nogv\n   Alternative, open source container using Theora_ codec for video and Vorbis for audio.\n\n\n\nTextures and images\n-------------------\n\nThe original Morrowind game uses *DDS* and *TGA* files for all kinds of two\ndimensional images and textures alike. In addition, the engine supported *BMP*\nfiles for some reason (*BMP* is a terrible format for a video game). We also\nsupport an extended set of image files – including *JPEG* and *PNG*. *JPEG* and\n*PNG* files can be useful in some cases, for instance a *JPEG* file is a valid\noption for skybox texture and *PNG* can useful for masks. However, please keep\nin mind that *JPEG* images can grow to large sizes quickly and are not the best\noption with a DirectX rendering backend. You probably still want to use *DDS*\nfiles for textures.\n\n\n\n.. Hyperlink targets for the entire document\n\n.. _FFmpeg: https://ffmpeg.org\n.. _Vorbis: http://www.vorbis.com\n.. _Theora: https://www.theora.org\n"
  },
  {
    "path": "docs/source/manuals/openmw-cs/foreword.rst",
    "content": "Foreword\n########\n\n<Some introductory lines here, explain nomenclature and abbreviations>\n\n\nHow to read the manual\n**********************\n\nThe manual can be roughly divided into two parts: a tutorial part consisting of\nthe first two (three) chapters and the reference manual. We recommend all\nreaders to work through the tutorials first, there you will be guided through\nthe creation of a fairly simple mod where you can familiarise yourself with the\nrecord-based interface. The tutorials are very simple and teach you only what\nis necessary for the task, each one can be completed in under half an hour. It\nis strongly recommended to do the tutorials in order.\n\nWhen you are familiar with the CS in general and want to write your own mods it\nis time to move on to the reference part of the manual. The reference chapters\ncan be read out of order, each one covers only one topic.\n\n"
  },
  {
    "path": "docs/source/manuals/openmw-cs/index.rst",
    "content": "OpenMW CS User Manual\n#####################\n\nThe following document is the complete user manual for *OpenMW CS*, the\nconstruction set for the OpenMW game engine. It is intended to serve both as an\nintroduction and a reference for the application. Even if you are familiar with\nmodding *The Elder Scrolls III: Morrowind* you should at least read the first\nfew chapters to familiarise yourself with the new interface.\n\n.. warning::\n    OpenMW CS is still software in development. The manual does not cover any of\n    its shortcomings, it is written as if everything was working as intended.\n    Please report any software problems as bugs in the software, not errors in\n    the manual.\n\n.. toctree::\n    :caption: Table of Contents\n    :maxdepth: 2\n\n    foreword\n    tour\n    files-and-directories\n    starting-dialog\n    tables\n    record-types\n    record-filters\n"
  },
  {
    "path": "docs/source/manuals/openmw-cs/record-filters.rst",
    "content": "Record Filters\n##############\n\nFilters are a key element of the OpenMW CS user interface, they allow rapid and\neasy access to records presented in all tables. In order to use this\napplication effectively you need to familiarise yourself with all the concepts\nand instructions explained in this chapter. The filter system is somewhat\nunusual at first glance, but once you understand the basics it will be fairly\nintuitive and easy to use\n\nFilters are a key element to using the OpenMW CS efficiently by allowing you to\nnarrow down the table entries very quickly and find what you are looking for.\nThe filter system might appear unusual at first, you don't just type in a word\nand get all instances where it occurs, instead filters are first-class objects\nin the CS with their own table. This allows you to define very specific filters\nfor your project and store them on disc to use in the next session. The CS\nallows you fine-grained control, you can choose whether to make a filter\npersistent between session, only for one session or use a one-off filter by\ntyping it directly into the filter field.\n\n\n\nTerms used\n**********\n\nFilter\n   A Filter is generally speaking a tool able to filter the elements of a\n   table, that is select some elements while discarding others, according to\n   some criteria. These criteria are written using their own syntax.\n\nCriterion\n   A criterion describes some condition a record needs to satisfy in order to\n   be selected. They are written using a special syntax which is explained\n   below. We can logically combine multiple criteria in a filter for finer\n   control.\n\nExpression\n   Expressions are how we perform filtering. They look like functions in a\n   programming language: they have a name and accept a number of arguments.\n   The expression evaluates to either ``true`` or ``false`` for every record in\n   the table. The arguments are expressions themselves.\n\nArity\n   The arity of an expression tells us how many arguments it takes. Expressions\n   taking no arguments are called *nullary*, those taking one argument are\n   known as *unary* expressions and those taking two arguments are called\n   *binary*.\n\n\n\nInterface\n*********\n\nAbove each table there is a text field which is used to enter a filter: either\none predefined by the OpenMW CS developers or one  made by you. Another\nimportant element is the filter table found under *View* → *Filters*. You\nshould see the default filters made by the OpenMW team in the table. The table\nhas the columns *Filter*, *Description* and *Modified*.\n\nID\n   A unique name used to refer to this filter. Note that every ID has a\n   scope prefix, we will explain these soon.\n\nModified\n   This is the same as for all the other records, it tells us whether the\n   filter is *added* or *removed*. Filters are specific to a project instead of\n   a content file, they have no effect on the game itself.\n\nFilter\n   The actual contents of the filter are given here using the filter syntax.\n   Change the expressions to modify what the filter returns.\n\nDescription\n   A textual description of what the filter does.\n\n\n\nUsing predefined filters\n************************\n\nTo use a filter you have to type its ID into the filter field above a table.\n\nFor instance, try to opening the objects table (under the world menu) and type\ninto the filters field ``project::weapons``. As soon as you complete the text\nthe table will show only the weapons. The string ``project::weapons`` is the ID\nof one of the predefined filters. This means that in order to use the filter\ninside the table you type its name inside the filter field.\n\nFilter IDs follow these general conventions:\n\n- IDs of filters for a specific record type contain usually the name of a\n  specific group. For instance the ``project::weapons`` filter contains the\n  term ``weapons``. Plural form is always used.\n\n- When filtering a specific subgroup the ID is prefixed with the name of the\n  more general filter. For instance ``project::weaponssilver`` will filter only\n  silver weapons and ``project::weaponsmagical`` will filter only magical\n  weapons.\n\n- There are few exceptions from the above rule. For instance there are\n  ``project::added``, ``project::removed``, ``project::modified`` and\n  ``project::base``. You might except something more like\n  ``project::statusadded`` but in this case requiring these extra characters\n  would not improve readability.\n\nWe strongly recommend you take a look at the filters table right now to see\nwhat you can filter with the defaults. Try using the default filters first\nbefore writing you own.\n\n\n\nWriting your own filters\n************************\n\nAs mentioned before, filters are just another type of record in the OpenMW CS.\nTo create a new filter you will have to add a new record to the *Filters* table\nand set its properties to your liking. Filters are created by combining\nexisting filters into more complex ones.\n\n\nScopes\n======\n\nEvery default filter has the prefix ``project``. This is a *scope*, a mechanism\nthat determines the lifetime of the filter. These are the supported scopes:\n\n``project::``\n   Indicates that the filter is to be used throughout the project in multiple\n   sessions. You can restart the CS and the filter will still be there.\n\n``session::``\n   Indicates that the filter is not stored between multiple sessions and once\n   you quit the OpenMW CS application the filter will be gone. Until then it\n   can be found inside the filters table.\n\nProject-filters are stored in an internal project file, not final content file\nmeant for the player. Keep in mind when collaborating with other modders that\nyou need to share the same project file.\n \n\n\nWriting expressions\n===================\n\nThe syntax for expressions is as follows:\n\n.. code::\n\n   <name>\n   <name>(<arg1>)\n   <name>(<arg1>, <arg2>, ..., <argn>)\n\nWhere ``<name>`` is the name of the expression, such as ``string`` and the\n``<arg>`` are expressions themselves. A nullary expression consists only of its\nname. A unary expression contains its argument within a pair of parentheses\nfollowing the name. If there is more than one argument they are separated by\ncommas inside the parentheses.\n\nAn example of a binary expression is ``string(\"Record Type\", weapon)``; the\nname is ``string``, and it takes two arguments which are strings of string\ntype. The meaning of arguments depends on the expression itself. In this case\nthe first argument is the name of a record column and the second field is the\nvalues we want to test it against.\n\nStrings are sequences of characters and are case-insensitive. If a string\ncontains spaces it must be quoted, otherwise the quotes are optional and\nignored.\n\n\nConstant Expressions\n--------------------\n\nThese expressions take no arguments and always return the same result.\n\n``true``\n   Always evaluates to ``true``.\n\n``false``\n   Always evaluates to ``false``.\n\n\nComparison Expressions\n----------------------\n\n``string(<column>, <value>)``\n   The ``<value>`` is a regular expression pattern. The expressions evaluates\n   to ``true`` when the value of a record in ``<column>`` matches the pattern.\n   Since the majority of the columns contain string values, ``string`` is among\n   the most often used expressions. Examples:\n\n   ``string(\"Record Type\", \"Weapon\")``\n      Will evaluate to ``true`` for all records containing ``Weapon`` in the\n      *Record Type* column cell.\n\n   ``string(\"Portable\", \"true\")``\n      Will evaluate to ``true`` [#]_ for all records containing word ``true`` inside\n      *Portable* column cell.\n\n.. [#] There is no Boolean (``true`` or ``false``) value in the OpenMW CS. You\n       should use a string for those.\n\n       \n``value(<value>, (<lower>, <upper>))``\n   Match a value type, such as a number, with a range of possible values. The\n   argument ``<value>`` is the string name of the value we want to compare, the\n   second argument is a pair of lower and upper bounds for the range interval.\n\n   One can use either parentheses ``()`` or brackets ``[]`` to surround the\n   pair. Brackets are inclusive and parentheses are exclusive. We can also mix\n   both styles:\n\n   .. code::\n\n      value(\"Weight\", [20, 50))\n\n   This will match any objects with a weight greater or equal to 20 and\n   strictly less than 50.\n\n\nLogical Expressions\n-------------------\n\n``not <expression>``\n   Logically negates the result of an expression. If ``<expression>`` evaluates\n   to ``true`` the negation is ``false``, and if ``<expression>`` evaluates to\n   ``false`` the negation is ``true``. Note that there are no parentheses\n   around the argument.\n\n``or(<expr1>, <expr2>, ..., <exprN>)``\n   Logical disjunction, evaluates to ``true`` if at least one argument\n   evaluates to ``true`` as well, otherwise the expression evaluates to\n   ``false``.\n\n   As an example assume we want to filter for both NPCs and creatures; the\n   expression for that use-case is\n\n   .. code::\n      \n      or(string(\"record type\", \"npc\"), string(\"record type\", \"creature\"))\n\n   In this particular case only one argument can evaluate to ``true``, but one\n   can write expressions where multiple arguments can be ``true`` at a time.\n\n``or(<expr1>, <expr2>, ..., <exprN>)``\n   Logical conjunction, evaluates to ``true`` if and only if all arguments\n   evaluate to ``true`` as well, otherwise the expression evaluates to\n   ``false``.\n\n   As an example assume we want to filter for weapons weighting less than a hundred\n   units The expression for that use-case is\n\n   .. code::\n      \n      and(string(\"record type\", \"weapon\"), value(\"weight\", (0, 100)))\n\n\nAnonymous filters\n=================\n\nCreating a whole new filter when you only intend to use it once can be\ncumbersome. For that reason the OpenMW CS supports *anonymous* filters which\ncan be typed directly into the filters field of a table. They are not stored\nanywhere, when you clear the field the filter is gone forever.\n\nIn order to define an anonymous filter you type an exclamation mark as the\nfirst character into the field followed by the filter definition (e.g.\n``!string(\"Record Type\", weapon)`` to filter only for weapons).\n\n\n\nCreating and saving filters\n***************************\n\nFilters are managed the same way as other records: go to the filters table,\nright click and select the option *Add Record* from the context menu. You are\ngiven a choice between project- or session scope. Choose the scope from the\ndropdown and type in your desired ID for the filter. A newly created filter\ndoes nothing since it still lacks expressions. In order to add your queries you\nhave to edit the filter record.\n\n\nReplacing the default filters set\n=================================\n\nOpenMW CS allows you to substitute the default filter set for the entire\napplication. This will affect the default filters for all content files that\nhave not been edited on this computer and user account.\n\nCreate a new content file, add the desired filters, remove the undesired ones\nand save. Now rename the *project* file to ``defaultfilters`` and make sure the\n``.omwaddon.project`` file extension is removed. This file will act as a\ntemplate for all new files from now on. If you wish to go back to the\nold default set rename or remove this custom file.\n"
  },
  {
    "path": "docs/source/manuals/openmw-cs/record-types.rst",
    "content": "Record Types\n############\n\nA game world contains many items, such as chests, weapons and monsters. All\nthese items are merely instances of templates we call *Objects*. The OpenMW CS\n*Objects* table contains information about each of these template objects, such\nas its value and weight in the case of items, or an aggression level in the\ncase of NPCs.\n\nThe following is a list of all Record Types and what you can tell OpenMW CS\nabout each of them.\n\nActivator\n   Activators can have a script attached to them. As long as the cell this\n   object is in is active the script will be run once per frame.\n\nPotion\n   This is a potion which is not self-made. It has an Icon for your inventory,\n   weight, coin value, and an attribute called *Auto Calc* set to ``False``.\n   This means that the effects of this potion are pre-configured. This does not\n   happen when the player makes their own potion.\n\nApparatus\n   This is a tool to make potions. Again there’s an icon for your inventory as\n   well as a weight and a coin value. It also has a *Quality* value attached to\n   it: the higher the number, the better the effect on your potions will be.\n   The *Apparatus Type* describes if the item is a *Calcinator*, *Retort*,\n   *Alembic* or *Mortar & Pestle*.\n\nArmor\n   This type of item adds *Enchantment Points* to the mix. Every piece of\n   clothing or armor has a \"pool\" of potential *Magicka* that gets unlocked\n   when the player enchants it. Strong enchantments consume more magicka from\n   this pool: the stronger the enchantment, the more *Enchantment Points* each\n   cast will take up. *Health* means the amount of hit points this piece of\n   armor has. If it sustains enough damage, the armor will be destroyed.\n   Finally, *Armor Value* tells the game how much points to add to the player\n   character’s *Armor Rating*.\n\nBook\n   This includes scrolls and notes. For the game to make the distinction\n   between books and scrolls, an extra property, *Scroll*, has been added.\n   Under the *Skill* column a scroll or book can have an in-game skill listed.\n   Reading this item will raise the player’s level in that specific skill.\n\nClothing\n   These items work just like armors, but confer no protective properties.\n   Rather than *Armor Type*, these items have a *Clothing Type*.\n\nContainer\n   This is all the stuff that stores items, from chests to sacks to plants. Its\n   *Capacity* shows how much stuff you can put in the container. You can\n   compare it to the maximum allowed load a player character can carry. A\n   container, however, will just refuse to take the item in question when it\n   gets \"over-encumbered\". Organic Containers are containers such as plants.\n   Containers that respawn are not safe to store stuff in. After a certain\n   amount of time they will reset to their default contents, meaning that\n   everything in them is gone forever.\n\nCreature\n   These can be monsters, animals and the like.\n\n"
  },
  {
    "path": "docs/source/manuals/openmw-cs/starting-dialog.rst",
    "content": "OpenMW CS Starting Dialog\n#########################\n\nIn this chapter we will cover starting up OpenMW CS and the starting interface.\nStart the CS the way intended for your operating system and you will be\npresented with a window and three main buttons and a small button with a\nwrench-icon. The wrench will open the configuration dialog which we will cover\nlater. The three main buttons are the following:\n\nCreate A New Game\n   Choose this option if you want to create an original game that does not\n   depend on any other content files. The distinction between game and addon in\n   the original Morrowind engine was somewhat blurry, but OpenMW is very strict\n   about it: regardless of how large your addon is, if it depends on another\n   content file it is not an original game.\n\nCreate A New Addon\n   Choose this option if you want to create an extension to an existing game.\n   An addon can depend on other addons as well optionally, but it *must* depend\n   on a game.\n\nEdit A Content File\n   Choose this option is you wish to edit an existing content file, regardless\n   of whether it is a game or an addon.\n\nWhether you create a game or an addon, a data file and a project file will be\ngenerated for you in you user directory.\n\nYou will have to choose a name for your content file and if you chose to create\nan addon you will also have to chose a number of dependencies. You have to\nchoose exactly one game and you can choose an arbitrary amount of addon\ndependencies.  For the sake of simplicity and maintainability choose only the\naddons you actually want to depend on. Also keep in mind that your dependencies\nmight have dependencies of their own, you have to depend on those as well. If\none of your dependencies needs something it will be indicated by a warning sign\nand automatically include its dependencies when you choose it.\n\nIf you want to edit an existing content file you will be presented with a\nsimilar dialog, except you don't get to choose a file name (because you are\nediting files that already exist).\n"
  },
  {
    "path": "docs/source/manuals/openmw-cs/tables.rst",
    "content": "Tables\n######\n\nIf you have launched OpenMW CS already and played around with it for a bit, you\nwill have noticed that the interface is made entirely of tables. This does not\nmean it works just like a spreadsheet application though, it would be more\naccurate to think of databases instead. Due to the vast amounts of information\ninvolved with Morrowind tables made the most sense. You have to be able to spot\ninformation quickly and be able to change them on the fly.\n\n\nUsed Terms\n**********\n\nRecord\n   An entry in OpenMW CS representing an item, location, sound, NPC or anything\n   else.\n\nInstance, Object\n   When an item is placed in the world, it does not create a whole new record\n   each time, but an *instance* of the *object*.\n   \n   For example, the game world might contain a lot of exquisite belts on\n   different NPCs and in many crates, but they all refer to one specific\n   instance: the Exquisite Belt record. In this case, all those belts in crates\n   and on NPCs are instances. The central Exquisite Belt instance is called an\n   *object*. This allows modders to make changes to all items of the same type\n   in one place.\n   \n   If you wanted all exquisite belts to have 4000 enchantment points rather\n   than 400, you would only need to change the object Exquisite Belt rather\n   than all exquisite belt instances individually.\n\nSome columns are recurring throughout OpenMW CS, they show up in (nearly) every\ntable.\n\nID\n   Each item, location, sound, etc. gets the same unique identifier in both\n   OpenMW CS and Morrowind. This is usually a very self-explanatory name. For\n   example, the ID for the (unique) black pants of Caius Cosades is\n   ``Caius_pants``. This allows players to manipulate the game in many ways.\n   For example, they could add these pants to their inventory by opening the\n   console and entering: ``player- >addItem Caius_pants``. In both Morrowind\n   and OpenMW CS the ID is the primary way to identify all these different\n   parts of the game.\n\nModified\n   This column shows what has happened (if anything) to this record. There are\n   four possible states in which it can exist:\n\n   Base\n      The record is unmodified and from a content file other than the one\n      currently being edited.\n\n   Added\n      This record has been added in the currently content file.\n\n   Modified\n      Similar to *base*, but has been changed in some way.\n\n   Deleted\n      Similar to *base*, but has been removed as an entry. This does not mean,\n      however, that the occurrences in the game itself have been removed! For\n      example, if you were to remove the ``CharGen_Bed`` entry from\n      ``morrowind.esm``, it does not mean the bedroll in the basement of the\n      Census and Excise Office in Seyda Neen will be gone. You will have to\n      delete that instance yourself or make sure that that object is replaced\n      by something that still exists otherwise the player will get crashes in\n      the worst case scenario.\n\n\n\nWorld Screens\n*************\n\nThe contents of the game world can be changed by choosing one of the options in\nthe appropriate menu at the top of the screen.\n\n\nRegions\n=======\n\nThis describes the general areas of Vvardenfell. Each of these areas has\ndifferent rules about things such as encounters and weather.\n\nName\n   This is how the game will show the player's location in-game.\n\nMapColour\n   This is a six-digit hexadecimal representation of the colour used to\n   identify the region on the map available in *World* → *Region Map*.\n\nSleep Encounter\n   These are the rules for what kinds of enemies the player might encounter\n   when sleeping outside in the wilderness.\n\n\nCells\n=====\n\nExpansive worlds such as Vvardenfell, with all its items, NPCs, etc. have a lot\ngoing on simultaneously. But if the player is in Balmora, why would the\ncomputer need to keep track the exact locations of NPCs walking through the\ncorridors in a Vivec canton? All that work would be quite useless and bring\nthe player's system down to its knees! So the world has been divided up into\nsquares we call *cells*.  Once your character enters a cell, the game will load\neverything that is going on in that cell so the player can interact with it.\n\nIn the original Morrowind this could be seen when a small loading bar would\nappear near the bottom of the screen while travelling; the player had just\nentered a new cell and the game had to load all the items and NPCs. The *Cells*\nscreen in OpenMW CS provides you with a list of cells in the game, both the\ninterior cells (houses, dungeons, mines, etc.) and the exterior cells (the\noutside world).\n\nSleep Forbidden\n   Can the player sleep on the floor? In most cities it is forbidden to sleep\n   outside. Sleeping in the wilderness carries its own risks of attack, though,\n   and this entry lets you decide if a player should be allowed to sleep on the\n   floor in this cell or not.\n\nInterior Water\n   Should water be rendered in this interior cell? The game world consists of\n   an endless ocean at height 0, then the landscape is added. If part of the\n   landscape goes below height 0, the player will see water.\n\n   Setting the cell’s Interior Water to true tells the game that this cell that\n   there needs to be water at height 0. This is useful for dungeons or mines\n   that have water in them.\n\n   Setting the cell’s Interior Water to ``false`` tells the game that the water\n   at height 0 should not be used. This flag is useless for outside cells.\n\nInterior Sky\n   Should this interior cell have a sky? This is a rather unique case. The\n   Tribunal expansion took place in a city on the mainland. Normally this would\n   require the city to be composed of exterior cells so it has a sky, weather\n   and the like. But if the player is in an exterior cell and were to look at\n   their in-game map, they would see Vvardenfell with an overview of all\n   exterior cells. The player would have to see the city’s very own map, as if\n   they were walking around in an interior cell.\n   \n   So the developers decided to create a workaround and take a bit of both: The\n   whole city would technically work exactly like an interior cell, but it\n   would need a sky as if it was an exterior cell. That is what this is. This\n   is why the vast majority of the cells you will find in this screen will have\n   this option set to false: It is only meant for these \"fake exteriors\".\n\nRegion\n   To which Region does this cell belong? This has an impact on the way the\n   game handles weather and encounters in this area. It is also possible for a\n   cell not to belong to any region.\n\n\nObjects\n=======\n\nThis is a library of all the items, triggers, containers, NPCs, etc. in the\ngame. There are several kinds of Record Types. Depending on which type a record\nis, it will need specific information to function. For example, an NPC needs a\nvalue attached to its aggression level. A chest, of course, does not. All\nRecord Types contain at least a 3D model or else the player would not see them.\nUsually they also have a *Name*, which is what the players sees when they hover\ntheir reticle over the object during the game.\n\nPlease refer to the Record Types chapter for an overview of what each type of\nobject does and what you can tell OpenMW CS about these objects.\n\n"
  },
  {
    "path": "docs/source/manuals/openmw-cs/tour.rst",
    "content": "A Tour through OpenMW CS: making a magic ring\n#############################################\n\nIn this first chapter we will create a mod that adds a new ring with a simple\nenchantment to the game. The ring will give its wearer a permanent Night Vision\neffect while being worn. You do not need previous Morrowind modding experience,\nbut you should be familiar with the game itself. There will be no\nscripting necessary, we can achieve everything using just what the base game\noffers out of the box. Before continuing make sure that OpenMW is properly\ninstalled and playable.\n\n\nAdding the ring to the game's records\n*************************************\n\nIn this first section we will define what our new ring is, what it looks like,\nand what it does. Getting it to work is the first step before we go further.\n\n\nStarting up OpenMW CS\n=====================\n\nWe will start by launching OpenMW CS, the location of the program depends on\nyour operating system. You will be presented with a dialogue with three\noptions: create a new game, create a new addon, edit a content file.\n\n.. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/opening-dialogue.png\n   :alt: Opening dialogue with three option and setting button (the wrench)\n\nThe first option is for creating an entirely new game, that's not what we want.\nWe want to edit an existing game, so choose the second option. When you save your\naddon you can use the third option to open it again.\n\nYou will be presented with another window where you get to choose the content to\nedit and the name of your project. Then we have to select at least the base game and\noptionally a number of other addons we want to depend on. The name of the\nproject is arbitrary, it will be used to identify the addon later in the OpenMW\nlauncher.\n\n.. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/new-project.png\n   :alt: Creation dialogue for a new project, pick content modules and name\n\nChoose Morrowind as your content file and enter `Ring of Night Vision` as the\nname. We could also choose further content files as dependencies if we wanted\nto, but for this mod the base game is enough.\n\nOnce the addon has been created you will be presented with a table. If you see\na blank window rather than a table choose *World* → *Objects* from the menu.\n\n.. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/new-project.png\n   :alt: The table showing all object records in the game.\n\nLet's talk about the interface for a second. Every window in OpenMW CS has\n*panels*, these are often but not always tables. You can close a panel by\nclicking the small \"X\" on the title bar of the panel, or you can detach it by\neither dragging the title bar or clicking the icon with the two windows. A\ndetached panel can be re-attached to a window by dragging it by the title bar\non top of the window.\n\nNow let's look at the panel itself: we have a filter text field, a very large\ntable and a status bar. The filter will be very useful when we want to find an\nentry in the table, but for now it is irrelevant. The table you are looking at\ncontains all objects in the game, these can be items, NPCs, creatures,\nwhatever. Every object is an entry in that table, visible as a row. The columns\nof the table are the attributes of each object.\n\nMorrowind uses something called a *relational database* for game data. If you\nare not familiar with the term, it means that every type of thing can be\nexpressed as a *table*: there is a table for objects, a table for enchantments,\na table for icons, one for meshes and so on. Properties of an entry must be\nsimple values, like numbers or text strings. If we want a more complicated\nproperty we need to reference an entry from another table. There are a few\nexceptions to this though, some tables do have subtables. The effects of\nenchantments are one of those exceptions.\n\n\nDefining a new record\n=====================\n\nEnough talk, let's create the new ring now. Right-click anywhere in the objects\ntable, choose `Add Record` and the status bar will change into an input field.\nWe need to enter an *ID* (short for *identifier*) and pick the type. The\nidentifier is a unique name by which the ring can later be identified; I have\nchosen `ring_night_vision`. For the type choose *Clothing*.\n\n.. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/add-record.png\n   :alt: Enter the ID and type of the new ring\n\nThe table should jump right to our newly created record, if not read further\nbelow how to use filters to find a record by ID. Notice that the *Modified*\ncolumn now shows that this record is new. Records can also be *Base*\n(unmodified), *Modified* and *Deleted*. The other fields are still empty since\nwe created this record from nothing. We can double-click a table cell while\nholding Shift to edit it (this is a configurable shortcut), but there is a\nbetter way: right-click the row of our new record and chose *Edit Record*, a\nnew panel will open.\n\nWe can right-click the row of our new record and select *Edit Record*, a\nnew panel will open. Alternatively we can also define a configurable shortcut\ninstead of using the context menu; the default is double-clicking while\nholding down the shift key.\n\n\n.. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/edit-record.png\n   :alt: Edit the properties of the record in a separate panel\n\nYou can set the name, weight and coin value as you like, I chose `Ring of Night\nVision`, `0.1` and `2500` respectively. Make sure you set the *Clothing Type*\nto *Ring*. We could set the other properties manually as well, but unless you\nhave an exceptional memory for identifiers and never make typos that's not\nfeasible. What we are going to do instead is find the records we want in their\nrespective tables and assign them from there.\n\n\nFinding records using filters\n=============================\n\nWe will add an icon first. Open the *Icons* table the same way you opened the\n*Objects* table: in the menu click *Assets* → *Icons*. If the window gets too\ncrowded remember that you can detach panels. The table is huge and not every\nring icon starts with \"ring\", so we have to use filters to find what we want.\n\nFilters are a central element of OpenMW CS and a major departure from how the\noriginal Morrowind CS was used. In fact, filters are so important that they\nhave their own table as well. We won't be going that far for now though. There\nare three types of filters: *Project filters* are part of the project and are\nstored in the project file, *session filter* are only valid until you exit the\nCS, and finally *instant filter* which are used only once and typed directly\ninto the *Filter* field.\n\nFor this tutorial we will only use instant filters. We type the definition of\nthe filter directly into the filter field rather than the name of an existing\nfilter. To signify that we are using an instant filter the have to use `!` as\nthe first character. Type the following into the field:\n\n.. code::\n\n   !string(\"id\", \".*ring.*\")\n\nA filter is defined by a number of *queries* which can be logically linked. For\nnow all that matters is that the `string(<property>, <pattern>)` query will check\nwhether `<property>` matches `<pattern>`. The pattern is a regular expression,\nif you don't know about them you should learn their syntax. For now all that\nmatters is that `.` stands for any character and `*` stands for any amount,\neven zero. In other words, we are looking for all entries which have an ID that\ncontains the word \"ring\" somewhere in it. This is a pretty dumb pattern because\nit will also match words like \"ringmail\", but it's good enough for now.\n\nIf you have typed the filter definition properly the text should change from\nred to black and our table will be narrowed down a lot. Browse for an icon you\nlike and drag & drop its table row onto the *Icon* field of our new ring.\n\nThat's it, you have now assigned a reference to an entry in another table to\nthe ring entry in the *Objects* table. Repeat the same process for the 3D\nmodel, you can find the *Meshes* table under *Assets* → *Meshes*.\n\n\nAdding the enchantment\n======================\n\nPutting everything you have learned so far to practice we can add the final and\nmost important part to our new ring: the enchantment. You know enough to\nperform the following steps without guidance: Open the *Enchantments* table\n(*Mechanics* → *Enchantments*) and create a new entry with the ID `Cats Eye`.\nEdit it so that it has *Constant Effect* enchantment type.\n\nTo add an effect to the enchantment right-click the *Magic Effects* table and\nchoose *Add new row*. You can edit the effects by right-clicking their table\ncells.  Set the effect to *NightEye*, range to *Self*, and both magnitudes to\n`50`. The other properties are irrelevant.\n\nOnce you are done add the new enchantment to our ring. That's it, we now have a\ncomplete enchanted ring to play with. Let's take it for a test ride.\n\n\nPlaying your new addon\n======================\n\nLaunch OpenMW and in the launcher under *Data Files* check your addon. Load a\ngame and open the console. We have only defined the ring, but we haven't placed\nany instance of it anywhere in the game world, so we have to create one. In the\nconsole type:\n\n.. code::\n\n   player->AddItem \"ring_night_vision\" 1\n\nThe part in quotation marks is the ID of our ring, you have to adjust it if you\nchose a different ID. Exit the console and you should find a new ring in your\ninventory. Equip it and you will instantly receive the *Night Vision* effect\nfor your character.\n\n\nConclusion\n==========\n\nIn this tutorial we have learned how to create a new addon, what tables are and\nhow to create new records. We have also taken a very brief glimpse at the\nsyntax of filters, a feature you will be using a lot when creating larger mods.\n\nThis mod is a pure addition, it does not change any of the existing records.\nHowever, if you want to actually present appealing content to the player rather\nthan just offering abstract definitions you will have to change the game's\ncontent. In the next tutorial we will learn how to place the ring in the game\nworld so the player can find it legitimately.\n\n\n\nAdding the ring to the game's world\n***********************************\n\nNow that we have defined the ring it is time add it to the game world so the\nplayer can find it legitimately. We will add the ring to a merchant, place it\nin a chest, and put it somewhere in plain sight. To this end we will have to\nactually modify the contents of the game.\n\nAdding to an NPC\n================\n\nThe simplest way is probably to add it to the inventory of a shopkeeper.\nAn obvious candidate is Arrille in Seyda Neen - he's quick to find in a new game\nand he's easy to find in the CS as his name comes early alphabetically.\n\n.. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/Ring_to_Arrille.png\n   :alt: Putting the ring into Arrille's inventory\n\nOpen the CS and open the *Objects* table (*World* → *Objects*).\nScroll down to Arrille, or use a filter like !string(\"ID\",\"arrille\").\n\nOpen another pane to edit him - either right click and select edit or use the\nshortcut (default is shift double-click). Scroll down to the inventory section\nand right click to add a new row. Type in the id of the ring (or find it in the\nobject pane, and drag and drop). Set the number of rings for him to stock - with\na negative number indicating that he will restock again to maintain that level.\n\nHowever, it's an attractive item, so he will probably wear it rather than sell it.\nSo set his stock level too high for him to wear them all (3 works, 2 might do).\n\nAnother possibility, again in Seyda Neen making it easy to access, would be for\nFargoth to give it to the player in exchange for his healing ring.\n\n.. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/Ring_to_Fargoth_1.png\n   :alt: Editing Fargoth to give ring to player\n\nOpen the *Topicinfo* Table (*Characters* → *Topic Infos*). Use a filter !string(Topic,ring)\nand select the row with a response starting with \"You found it!\". Edit the record,\nfirstly by adding a bit more to the response, then by adding a line to the script\nto give the ring to the player - the same as used earlier in the console\n\n.. code::\n\n   player->AddItem \"ring_night_vision\" 1\n\n.. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/Ring_to_Fargoth_2.png\n   :alt: Editing Fargoth to give ring to player\n\nPlacing in a chest\n==================\n\nFor this example we will use the small chest intended for lockpick practice,\nlocated in the Census and Excise Office in Seyda Neen.\n\nFirst we need the ID of the chest - this can be obtained either by clicking on it in the console\nin the game, or by applying a similar process in the CS -\n\nWorld/Cells\n\nSelect \"Seyda Neen, Census and Excise Office\"\n\nRight-click and select \"View\"\n\nUse mouse wheel to zoom in/out, and mouse plus WASD keys to navigate\n\nClick on the small chest\n\nEither way, you should find the ID, which is \"chest_small_02_lockprac\".\n\nOpen the Objects table (World/Objects) and scroll down to find this item.\n\nAlternatively use the Edit/Search facility, selecting ID rather than text,\nenter \"lockprac\" (without the quotes) into the search box, press \"Search\",\nwhich should return two rows, then select the \"Container\" one rather than the \"Instance\"\n\nRight-click and \"Edit Record\".\n\nRight-click the \"Content\" section and select \"Add a row\"\n\nSet the Item ID of the new row to be your new ring - simplest way is probably to open the Objects\ntable if it's not already open, sort on the \"Modified\" column which should bring the ring,\nwith its status of \"Added\" to the top, then drag and drop to the chest row.\n\nIncrease the Count to 1.\n\nSave the addon, then test to ensure it works - e.g. start a new game and lockpick the chest.\n\nPlacing in plain sight\n======================\n\nLet's hide the Ring of Night vision in the cabin of the [Ancient Shipwreck]\n(https://en.uesp.net/wiki/Morrowind:Ancient_Shipwreck), a derelict vessel\nsoutheast of Dagon Fel. Open the list of Cells (*World* → *Cells*) and find\n\"Ancient Shipwreck, Cabin\".\n\nThis will open a visualization of the cabin. You can navigate around the scene\njust like you would when playing Morrowind. Use the WASD keys to move forward,\nbackwards, and sideways. Click and drag with the left mouse button to change the\ndirection you are looking. Navigate to the table in the cabin.\n\nIf you've closed the Objects table, reopen it via *World* → *Objects*. Navigate\nto your Ring of Night Vision (you can find it easily if you sort by the \"Modified\"\ncolumn). Drag the ring from the Objects table onto the table in the Cell view.\n\nNow let's move the ring to the precise location we want. Hover over the ring and\nclick the middle mouse button. If you don't have a middle mouse button, you can\nselect an alternative command by going to *Edit* → *Preferences…* (Windows, Linux)\nor *OpenMW* → *Preferences…* (macOS). Go to the Key Bindings section and choose\n\"Scene\" from the dropdown menu. Then click on the button for \"Primary Select\" and\nchoose an alternative binding.\n\nAfter you have switched to movement mode, you will see several arrows. Clicking\nand dragging them with the right mouse button will allow you to move the object\nin the direction you want.\n\nIf you'd like an easy way to test this, you can start OpenMW with the [game\narguments](https://wiki.openmw.org/index.php?title=Testing)\n`--start=\"Ancient Shipwreck, Cabin\" --skip-menu`. This will place you right in\nthe cell and allow you to pick up and equip the ring in order to check that it\nworks.\n\nNavigation in the CS\n====================\nThis is probably a suitable place to start talking about how navigation differs from TESCS\nin vanilla Morrowind.\n\nThere is advice in Scripting for Dummies, the definitive manual for Morrowind Scripting:\n\"If you give your scripts a common tag, that will make it easier to jump between the\ndifferent scripts of your project, e.g. start every script name with AA_Scriptname\nthis will put them right at the beginning of the list and keep them neatly together.\"\n\nThis is valid for the rather poorer navigation facilities there, but it's not sensible for\nthe OpenMW CS. Some modders took it further, and started script names and object id with numbers,\ntypically \"1\", to bring the items even earlier in the default alphabetical sorts. In fact\nthe CS won't allow names/ids to start with numbers or to include \".\".\n\nThere are better options available:\n\nFiltering, which isn't available at all in TESCS - put in a filter like\n\n!string(\"ID\",\".*ring.*\")\n\nto find all IDs which contain the string \"ring\"\n\nSorting, which is available in some parts of TESCS, but not for scripts (other than script names being\nsorted in ascending order)- hence the recommendation\nTypically the \"Modified\" column is useful here - most items will have \"Base\" status, unchanged from\nthe base game.\n\n\"Added\" status\" will cover those items added in this addon.\n\n\"Modified\" status will cover items from the base game which have been modified in this addon.\n\nClick on the top of the column to toggle between ascending and descending order - thus between \"Added\"\nand \"Modified\" at the top. Or put your desired modified status into a filter then sort alphabetically\non a different column.\n\n\n\nChecking your new addon\n=======================\n\nLaunch OpenMW and in the launcher under *Data Files* check your addon, if it's not\nalready checked. Load a game and make your way to Seyda Neen - or start a new game.\n\nCheck whether Arrille has one (or more) for sale, and whether Fargoth give you one\nwhen you return his healing ring.\n"
  },
  {
    "path": "docs/source/reference/documentationHowTo.rst",
    "content": "#######################################\nSo you want to help with documentation?\n#######################################\n\nOr a beginner's guide to writing docs without having to deal with more techie stuff than you have to.\n#####################################################################################################\n\nIntro\n=====\n\nThe premise of this guide is that you would like to help out the OpenMW project beyond play-testing for bugs and such, \n*buuuuut* you're like me and don't really know how to code. \nThis has the rather pesky side effect of you not really knowing about all the tools like GitHub and such. \nWhile many of these tools are super handy and great to know how to use, \nnot everyone has the actual need and desire to learn the ins and outs of them. \nSince we would like as much help fleshing out the user documentation as possible, \nI wrote this guide to lower the barrier of entry into contributing to the project.\n\n*However*, as much as I will try to guide you through all the tedious setup and day-to-day stuff, \nyou will eventually have to learn to write using ReST (reStructuredText) formatting. \nSince you're probably like me when I started helping and don't know wtf ReST is, never fear. \nIt's an incredibly simple language that is easy to read in plain text form that can then be converted automatically \ninto different types of documents like PDFs and html for webpages.\n\nBaby Steps\n==========\n\nCreate an account on GitLab (https://gitlab.com), and sign in.\n(Github probably works too, though some details will differ. More details later – maybe.)\n\nGo to the OpenMW project on GitLab (https://gitlab.com/OpenMW/openmw)\nNavigate to whatever documentation you want to tackle.\nChoose Repository and Files in the menu on the left, then docs and source in the tree in the centre.\n\nDon’t overlook the tutorial-style-guide.txt there for some tips to get you started.\n\nOpen whichever file you want to tackle – probably within the manuals or reference directories.\nThere’s also a dropdown box to the right of edit, at the top of the left menu, \nwhich offers options such as new file or directory, or upload file, with “+” to close that dropdown box.\n\nClick on \"Edit\" towards top right which will reveal the underlying version, \nrather than the version displayed to normal readers. Use \"Write\" and \"Preview\" to switch between the two views.\n\nWhen you have made the appropriate changes, and checked them in Preview mode, click the Green \"Commit changes\" button at the bottom.\nThis should add a branch, with a default name such as patch-1, to your own repository, and add a Merge Request to the main OpenMW Project.\n\n[More details on the commit and review process]\n\nChanges between submission and acceptance of the Merge Request - just make them in your branch, then press the Commit button there.\n\nGoing Deeper\n============\nSo here's what you're gonna be learning how to set up:\n\n1.\t`GitHub`_\n2.\t`PyCharm`_\n3.\t`Sphinx`_\n4.\t`Sample PR`_\n\nGitHub\n======\n\nGitHub is the website the OpenMW project is hosted on. It utilizes Git, which is a version control system, \nmeaning it helps us all collaborate on the project without interfering with each others' work. \nThe commands are a little annoying because there is a certain amount of undescriptive jargon, \nbut for the most part, what you need to know is very simple and I'll walk you through it. \nThere are three main parts that you should know:\n\n1.\tThe OpenMW repository\n2.\tYour online repository\n3.\tYour local repository\n\nThe master OpenMW repository is where all of our work comes together and where the most current version of the source code resides. \nA repository, also called repo, is a directory or the main folder that holds a project. \nYou will need to create your own account on GitHub so you can *fork* the OpenMW repository. \nForking is just when you clone a project into a repository on your own account so you can make changes however you like \nwithout accidentally messing up the original project. \nNow, you could add and edit files on GitHub.com directly through your online repository, \nhowever it's much easier to work on them on your own computer in your local repository. \nLocal just refers to the fact that it's physically stored on your computer's hard drive. Here are the easy steps for doing all this:\n\n1.\tGo to GitHub.com and sign up for a free account.\n2.\tNavigate to the master OpenMW repo at: https://github.com/OpenMW/openmw\n3.\tIn the upper right corner, click on the button that says \"Fork\". This should take you to the newly created fork in your own account ``<username>/openmw``.\n\nNow you have an online repository that is the exact copy of the OpenMW master. To set up your local repository, we're going to use PyCharm.\n\nIf you want more info I recommend reading this guide: https://readwrite.com/2013/09/30/understanding-github-a-journey-for-beginners-part-1/\n\nPyCharm\n=======\n\nPyCharm is what's known as an IDE, which stands for integrated development environment. \nAll this means is that it's for writing code and has a bunch of built-in features that make it easier to do so. \nIn this case, PyCharm is made for the language Python, which is what Sphinx is written in. \nWe won't actually be touching any of the Python, but some of the built-in features are extremely useful. \nLet's start setting it up:\n\n1.\tGo to https://www.jetbrains.com/pycharm/download/\n2.\tSelect your OS, then download the free Community version.\n3.\tLocate and install.\n4.\tRun the program and let it load.\n5.\tNow we're going to connect it to our GitHub account and let it create the local repository by itself. In the welcome menu, go to the bottom right where it says configure and select Settings/Preferences.\n6.\tClick Version Control and select GitHub.\n7.\tClick Create API Token and enter your GitHub username and password in the dialogue box, then click Login.\n8.\tThis should allow PyCharm to automatically connect to GitHub, but go ahead and click Test just to be sure.\n9.\tClick Apply and OK to save the settings.\n10.\tBack in the welcome window, click \"Check out from version control\" and select GitHub.\n\n\t.. note::\n\t\t\tAfter this step, it should log in to your GitHub. If not, you probably messed up the Token creation. \n\t\t\tIf you're on Mac, you may come across and error complaining about XCode and admin priviledges. If this happens, \n\t\t\topen Terminal and type: ``sudo xcodebuild -license`` Read through the license and agree. \n\t\t\tThis should fix the error and allow you to log in.\n\n11.\tIn Git Repository URL, select your OpenMW repository and click Clone\n\nCongrats! You now have the OpenMW source code on your computer and you can begin making changes and contributing. \nIf you're reading this guide though, you probably won't have any idea how to do that, \nso let's go through setting up Sphinx, then I'll go through it.\n\nSphinx\n======\n\nSo far I've mentioned ReST (reStructuredText) a couple times, but what is it, and what is Sphinx? \nThe most basic explanation is that ReST is the markup language (like HTML is the markup language for webpages) \nand Sphinx is the program that goes through and builds the actual document so you can read it in a more visually pleasing way. \nFor a much more detailed explanation, I recommend: https://coderwall.com/p/vemncg/what-is-the-difference-rest-docutils-sphinx-readthedocs\n\nThis will be the most technical section as we have to use the command prompt or terminal to install Python and Sphinx. \nI had intended to give you a universal explanation on how to install both, \nbut it would drastically increase the length of this guide. \nThe tutorial on the Sphinx website is really just going to be better than anything I write here, \nso please refer to their guide here: https://www.sphinx-doc.org/en/stable/install.html\n\nHopefully you now have Python and Sphinx installed. ...\n\nNow you should have everything installed and running so you can collaborate on documentation properly. \nLet's go through a few more brief GitHub basics. There are really only 4 things you will be using regularly:\n\n1.\tRebase\n2.\tCommit\n3.\tPush\n4.\tPull request (PR)\n\nRebasing means you're taking all changes in one branch and applying them directly on top of another branch. \nThis is slightly different than a merge which compares the two branches and makes another state combining the two. \nThe difference is slight, but we use the rebase because it keeps the history cleaner. \nYou will always rebase your local repository from the OpenMW master repository. \nThis ensures you have all the most up to date changes before working on stuff so there is less chance of conflicts that \nneed to be resolved when your branch is merged back into the master. \nA commit is basically just stating which files you want to mark as ready to be \"pushed\" to your online repository. \nA push is just copying those \"committed\" changes to your online repo.\n(Commit and push can be combined in one step in PyCharm, so yay) \nOnce you've pushed all the changes you need to contribute something to the project, you will then submit a pull request, \nso called because you are *requesting* that the project maintainers \"pull\"\n and merge the changes you've made into the project master repository. One of the project maintainers will probably ask \n you to make some corrections or clarifications. Go back and repeat this process to make those changes, \n and repeat until they're good enough to get merged.\n\nSo to go over all that again. You rebase *every* time you start working on something to ensure you're working on the most \nupdated version (I do literally every time I open PyCharm). Then make your edits. \nYou commit and push from your local repo to your online repo. \nThen you submit a pull request and people can review your changes before they get merged into the project master! \nOr in list form:\n\n1.\tRebase local repo from OpenMW master\n2.\tMake your edits\n3.\tCommit and push your local edits to your online repo\n4.\tGo online and submit a pull request\n5.\tRepeat steps 1-4 until someone approves and merges your PR\n\nPreview Documentation\n*********************\n\nYou will probably find it helpful to be able to preview any documentation you've made. \nI often forget necessary syntax and this allows me to double check my work before submitting a PR. \nLuckily, PyCharm has a handy built-in feature that allows you to easily generate the docs.\n\n1.\tIn the top right corner of the PyCharm window, select the drop-down menu and select `Edit Configurations`.\n2.\tIn the `Run/Debug Configurations` dialogue, click the green plus button in the top left and select `Python Docs > Sphinx Tasks`.\n3.\tUnder the Configuration tab, make sure the following are filled out:\n\t\t:Name:\t\t<whatever will help you remember what this is, just make sure you name it or it won't save>\n\t\t:Command:\thtml\n\t\t:Input:\t\t<path-to-your-PyCharm-openmw-directory/docs/source>\n\t\t:Output:\t<wherever you want the build files to be>\n4.\tClick `Apply`, then `OK`.\n\nNow in order to generate the documentation on your computer to preview them, \njust click the green play button in the top right, next to the drop down menu with the name you chose above selected. \nSphinx will run and you can view the resulting documentation wherever you chose Output to be, above. \nThe window that Sphinx runs in will also show any errors that occur during the build in red, \nwhich should help you find typos and missing/incorrect syntax.\n\nGitLab integration in PyCharm\n=============================\n\nAs most of the hosting of OpenMW has moved to Gitlab, we should encourage the use of GitLab, \nthough GitHub will continue to be supported.\n\nAdd a couple of plugins to PyCharm - see general instructions at https://www.jetbrains.com/help/pycharm/installing-updating-and-uninstalling-repository-plugins.html\n\nFor Linux/Windows - (MacOS is a little different)\n\n1. File/Settings/Plugins \n2. Browse Repositories\n3. Filter with “GitLab”\n4. Install “GitLab Integration Plugin”, \n5. Follow the accompanying instructions to register your GitLab account (after restarting PyCharm) - File/Settings/Other Settings/Gitlab Integration\n6. Install “GitLab Projects”\n\nWithin your account on GitLab\n\n1. Fork OpenMW if you haven’t already done so\n2. Select Settings from the dropdown box in your Avatar (top right)\n3. Select Access Tokens from the list on the left \n4. Enter a name for application that will use it – say “PyCharm”\n5. Set an expiry date\n6. Check  the “api” box\n7. Create the token, and use it to complete the setup of the \"GitLab Integration Plugin\" above.\n\n\nSample PR\n=========\n\nComing soon...\n"
  },
  {
    "path": "docs/source/reference/index.rst",
    "content": "##################\nReference Material\n##################\n\n.. toctree::\n\t:maxdepth: 2\n\n\tmodding/index\n\tdocumentationHowTo"
  },
  {
    "path": "docs/source/reference/modding/custom-models/index.rst",
    "content": "#############\nCustom Models\n#############\n\nCustom models can be imported into OpenMW using a variety of formats. Models for the majority of in-game objects are assigned through openMW-CS :doc:`/manuals/openmw-cs/index`.\n\nSome models, however, are essential for OpenMW to even run. These include player and NPC animations, and meshes for the sky. They are assigned in the ``settings.cfg`` file, with more information available in :doc:`/reference/modding/settings/models` .\n\nBelow is a quick overview of supported formats, followed by separate articles with further look at the pipelines.\n\n* **COLLADA** has no license restrictions and is suitable for modding as well as standalone games based on the OpenMW engine. It supports static and animated models. While it doesn't yet work in all parts of the engine, work is being done to resolve the remaining limitations.\n\n* **OSG native** has no license restrictions, but currently supports only static, non-animated models.\n\n* **NIF** is the proprietary format used in the original Morrowind game. It supports static and animated models and everything else the format included in the original game.\n\n.. toctree::\n\t:caption: Table of Contents\n\t:maxdepth: 1\n\n\tpipeline-blender-collada\n\tpipeline-blender-osgnative\n\tpipeline-blender-nif\n"
  },
  {
    "path": "docs/source/reference/modding/custom-models/pipeline-blender-collada.rst",
    "content": "##############################\nBlender to OpenMW with COLLADA\n##############################\n\nRequirements\n************\nTo use the Blender to OpenMW pipeline via COLLADA, you will need the following.\n\n* `OpenMW 0.47 <https://openmw.org/downloads/>`_ or later\n* `Blender 2.81 <https://www.blender.org/download/>`_ or later. Latest confirmed, working version is Blender 2.91\n* `Better COLLADA Exporter <https://github.com/unelsson/collada-exporter>`_ tuned for OpenMW\n* A model you would like to export\n\n\nStatic Models\n*************\nStatic models are those that don't have any animations included in the exported file. First, let's take a look at how the fundamental properties of a scene in Blender translate to a COLLADA model suitable for use in OpenMW. These apply the same to static and animated models.\n\nLocation\n========\n\nObjects keep their visual location and origin they had in the original scene.\n\nRotation\n========\n\n* Blender’s +Z axis is up axis in OpenMW\n* Blender’s +Y axis is front axis in OpenMW\n* Blender’s X axis is left-right axis in OpenMW\n\nScale\n=====\n\nScale ratio between Blender and OpenMW is 70 to 1. This means 70 units in Blender translate to 1 m in OpenMW.\n\nHowever, a scale factor like this is impractical to work with. A better approach is to work with a scale of 1 Blender unit = 1 m and apply the 70 scale factor at export. The exporter will automatically scale all object, mesh, armature and animation data.\n\nMaterials\n=========\n\nOpenMW uses the classic, specular material setup and currently doesn't use the more mainstream `PBR <https://en.wikipedia.org/wiki/Physically_based_rendering>`_ way. In Blender, the mesh needs a default material with a diffuse texture connected to the ``Base Color`` socket. This is enough for the material to be included in the exported COLLADA file.\n\nAdditional texture types, such as specular or normal maps, will be automatically recognized and used by OpenMW. They need an identical base name as the diffuse texture, a suffix, and be in the same folder. How to enable this and what suffix is used for what texture type is covered in more detail in :doc:`../../modding/texture-modding/texture-basics`.\n\nCollision Shapes\n================\n\nIn Blender, create an empty and name it ``collision``. Any mesh that is a child of this empty will be used for physics collision and will not be visible. There can be multiple child meshes under ``collision`` and they will all contribute to the collision shapes. The meshes themselves can have an arbitrary name, it's only the name of the empty that is important. The ``tcb`` command in OpenMW's in-game console will make the collision shapes visible and you will be able to inspect them.\n\nExporter Settings\n=================\n\nFor static models, use the following exporter settings. Before export, select all objects you wish to include in the exported file and have the \"Selected Objects\" option enabled. Without this, the exporter could fail.\n\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/custom-models/_static/dae_exporter_static.jpg\n    :align: center\n\n\nAnimated Models\n***************\n\nAnimated models are those where a hierarchy of bones, known as armature, deforms the mesh and makes things move. Besides the topics covered above, the following requirements apply.\n\nArmature\n========\n\n* A single armature per COLLADA file is advised to avoid any potential problems.\n* There needs to be a single top-most bone in the armature’s hierarchy, where both the deformation and control bones fall under it.\n* Not all bones need to be exported. By disabing the bone’s “Deform” property and using the corresponding option in the exporter, it is possible to export only the bones needed for animation.\n\n\nAnimations\n==========\n\nEvery action in Blender is exported as its own animation clip in COLLADA. Actions you don't wish to export need to have \"-noexp\" added to their name, with the corresponding option enabled in the exporter.\n\nDue to current limitations of the format and exporter, the keyframes of individual actions must not overlap with other actions. The keyframes need to be manually offset to a unique range on the timeline as shown in this example.\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/custom-models/_static/dae_animations_on_timeline.jpg\n    :align: center\n\nTextkeys\n--------\n\nThe exported COLLADA file requires a corresponding textkeys file for OpenMW to properly read the animations. Textkeys is a .txt file containing animation definitions. Textkeys file is placed in the same folder as the model and uses a name matching the model. \n\n    - ``OpenMWDude.dae``\n    - ``OpenMWDude.txt``\n\nTextkeys use a simple format as shown in the example. Name, start and stop values can be taken from the corresponding COLLADA file for each ``<animation_clip>``.\n    \n.. code::\n\n    idle: start 0.03333333333333333\n    idle: stop 2.033333333333333\n    runforward: start 2.0666666666666664\n    runforward: stop 3.0666666666666664\n    runback: start 3.1\n    runback: stop 4.1\n    ...\n    \n\nRoot Motion\n===========\n\nOpenMW can read the movement of the root (top-most in hierarchy) bone and use it to move objects in the game world. For this to work, the root bone must be animated to move through space. The root bone must, in its default pose, be alligned with the world.\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/custom-models/_static/dae_rig_root.jpg\n    :align: center\n\n\nExporter Settings\n=================\n\nFor animated models, use the following exporter settings. Before export, select all objects you wish to include in the exported file and have the \"Selected Objects\" option enabled. Without this, the exporter could fail.\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/custom-models/_static/dae_exporter_animated.jpg\n    :align: center\n\n\n\n\n\n \n \n\n"
  },
  {
    "path": "docs/source/reference/modding/custom-models/pipeline-blender-nif.rst",
    "content": "##########################\nBlender to OpenMW with NIF\n##########################\n\nThere is a lot of information available around the Internet on how to work with NIF files. We recommend you refer to https://www.niftools.org/ for more information.\n\nFor Blender specifically, you will need the following requirements.\n\nRequirements\n------------\n* `OpenMW <https://openmw.org/downloads/>`_\n* `Blender 2.8+ <https://www.blender.org/download/>`_\n* Either `Niftools addon <https://github.com/niftools/blender_niftools_addonr>`_\n* Or `Morrowind Blender Plugin <https://blender-morrowind.readthedocs.io/en/latest/index.html>`_\n* A model you would like to export\n\n\n"
  },
  {
    "path": "docs/source/reference/modding/custom-models/pipeline-blender-osgnative.rst",
    "content": "#################################\nBlender to OpenMW with OSG native\n#################################\n\nThis article explains how to export a model from Blender to OpenMW using the OSG model format. It supports only basic, static models.\nFor more details on the format, refer to `this forum post <https://forum.openmw.org/viewtopic.php?f=20&t=2949&p=35514#p35514>`_.\n\nPrerequisites\n#############\n\n- OpenMW 0.38 or later\n- Blender 2.60 or later\n- OSG exporter add-on for Blender\n- A Blender model you would like to export\n\nInstalling the exporter\n#######################\n\n#.\tDownload the\n\t`OSG export script\n\t<https://github.com/openmw/osgexport/blob/release/blender-2.5/build/osgexport-0.14.2.zip?raw=true>`_\n#.\tOpen Blender and go to File -> User Preferences\n#.\tSelect Add-ons -> Install from File, then select the downloaded `.zip`\n#.\tEnter \"osg\" into the search bar, then tick the checkbox next to the add-on to enable it\n#.\tNow click Save user setting so the exporter stays enabled when you re-start Blender\n\nYou can now export your models using the OSG model (osgt) entry in the File -> Export menu.\n\nModel's location\n################\n\nThe model needs to be at 0,0,0 coordinates in Blender,\nas this is where its origin will always be when exported.\nIf the model is offset from 0,0,0 in Blender,\nit will be offset from its origin in the exported file as well.\n\nModel's rotation\n################\n\n- Blender's Z axis is up axis in OpenMW\n- Blender's Y axis is front axis in OpenMW\n- Blender's X axis is left-right axis in OpenMW\n- Visual rotation is taken into account when exporting\n\nModel's scale\n#############\n\nBlender:OpenMW model scale is 70:1,\nwhich means 70 Blender units (BU) translate into 1m in OpenMW.\nUsing this scale, any models you make will fit with the existing ones.\nThe scale does not need to be applied,\nthe exporter will always use the visual scale of the model.\nHowever, 70 is an odd number to work with so here's an alternative workflow:\n\n-\tIn Blender, use a scale of 1BU = 1m which is a nice scale and ratio to work with.\n\tHave all models use this scale.\n-\tBefore exporting a model, scale it up by a factor of 70.\n-\tAfter exporting, undo the model's scale change and continue working as normal\n\t(in the future a preferable way would be to apply the scale through the exporter)\n\nPutting the model in-game\n#########################\n\nPlace the exported model in the Meshes sub-folder of a data folder recognized by the game,\ne.g. the Morrowind Data Files folder, or the local data folder.\nPlace all required textures in the Textures sub-folder.\nNow start OpenMW-CS, create a new addon file\nand you should see your mesh in the Assets -> Meshes table.\nGo ahead and make some object use your mesh.\nYou can now preview by right clicking on the object -> Preview to see what the mesh will look like.\n\nConverting the model to binary\n##############################\n\nWhen the model behaves to our liking,\nwe can think about converting it from the \"osgt\" text format to the \"osgb\" binary format\nso that it's smaller in size - and thus faster to load.\n\nTo do this, simply invoke the osgconv tool.\nThis tool should be included with your distribution of OpensceneGraph.\n\n`osgconv -O WriteImageHint=UseExternal model.osgt model.osgb`\n\nOf course, you can convert the binary model back to text format as well:\n\n`osgconv -O WriteImageHint=UseExternal model.osgb model.osgt`\n\nNote the use of `-O WriteHint=UseExternal` option.\nEnabling this option is desirable because it keeps the textures as external file references,\nrather than embedding the textures within the model file.\nEmbedded textures have disadvantages such as being hard to inspect,\nand impossible to share between model files.\n\nUsing shaders/normal maps\n#########################\n\nSee :ref:`OSG Native Files`\n \n\n"
  },
  {
    "path": "docs/source/reference/modding/differences.rst",
    "content": "Modding OpenMW vs Morrowind\n#################################\n\nA brief overview of the differences between the two engines.\n============================================================\n\nOpenMW is designed to be able to use all the normal Morrowind mod files such as ESM/ESP plugins, texture replacers,\nmesh replacers, etc.\n\n.. warning::\n\tAll external programs and libraries that depend on ``morrowind.exe`` cannot function with OpenMW. \n\tThis means you should assume mods dependent on Morrowind Graphics Extender, Morrowind Code Patch, \n\tMorrowind Script Extender, etc, will *not* work correctly, nor will the tools themselves.\n\nMultiple Data Folders\n---------------------\n\nThe largest difference between OpenMW and Morrowind in terms of data structure is OpenMW's support of multiple data folders. \nThis has many advantages, especially when it comes to uninstalling mods and preventing unintentional overwrites of files.\n\n.. warning::\n\tMost mods can still be installed into the root OpenMW data folder, but this is not recommended.\n\nTo install mods via this new feature:\n\n#.\tOpen ``openmw.cfg`` with your preferred text editor. It is located as described in :doc:`paths` and *not* in your OpenMW root directory.\n#.\tFind or search for ``data=``. This is located very near the bottom of the file.\n#.\tAdd a new line below this line and make a new entry of the format ``data=\"path/to/your/mod\"``\n#.\tMake as many of these entries as you need for each mod folder you want to include.\n#.\tSave ``openmw.cfg``\n\n.. note::\n\tAll mod folders must adhere to the same file structure as ``~/Morrowind/Data Files/``.\n\n.. TODO create a PATHS ReST file that I can reference instead of the Wiki.\n\nTo uninstall these mods simply delete that mod's respective ``data=`` entry.\nThe mods are loaded in the order of these entries, with the top being overwritten by mods added towards the bottom.\n\n.. note::\n\tMods that depend on ESM/ESP plugins can be rearranged within the OpenMW Launcher, \n\tbut mesh/texture replacer mods can only be reordered by moving their ``data=`` entry.\n\nOpenMW Launcher\n---------------\n\nThe launcher included with OpenMW is similar to the original Morrowind Launcher. \nGo to the Data Files tab to enable and disable plugins. You can also drag list items to modify the load order. \nContent lists can be created at the bottom by clicking the New Content List button, creating a list name, \nthen setting up a new modlist. This is helpful for different player profiles and testing out different load orders.\n\n.. TODO use a substitution image for the New Content List button.\n\nSettings.cfg\n------------\n\nThe ``settings.cfg`` file is essentially the same as the INI files for Morrowind. \nIt is located in the same directory as ``openmw.cfg``. This is where many video, audio, GUI, input, etc. \nsettings can be modified. Some are available in-game, but many are only available in this configuration file. \nPlease see https://wiki.openmw.org/index.php?title=Settings for the complete listing.\n\n.. TODO Create a proper ReST document tree for all the settings rather than Wiki.\n\nOpen Source Resources Support\n-----------------------------\n\nWhile OpenMW supports all of the original files that Morrowind supported, \nwe've expanded support to many open source file formats. These are summarized below:\n\n<this will be a table of the type of file, the morrowind supported file, and the OpenMW supported file formats>\n"
  },
  {
    "path": "docs/source/reference/modding/extended.rst",
    "content": "Extended modding feature overview\n#################################\n\nOpenMW supports some extended modding features out of the box.\nThese features can conflict with mods, and such situations should be handled as any other mod conflict by patching the mods themselves.\nThe engine itself does not provide any kind of blacklisting of incompatible mods.\n\n\nNative herbalism support\n------------------------\n\nIn OpenMW it is possible to add one or more NiSwitchNodes with a ``HerbalismSwitch`` name.\nEvery switch node should have at least two child nodes (the first one represents the unharvested container, the second one - the harvested container).\nIf an organic container's mesh has such nodes, it is considered to be a plant. During activation, a window with the plant's content is not shown,\nOpenMW transfers the contents directly into the player's inventory, triggers a theft crime event if the plant is owned and toggles the harvested model state.\n\nIt is also possible to use scripts with ``OnActivate`` command for such containers. For example, when player needs a tool to harvest a plant (e.g. a pickaxe for ore).\n\nKeep in mind that the collision detection system ignores switch nodes, so add a ``RootCollisionNode`` or ``NCO`` NiStringExtraData to harvestable meshes.\n\nAdvantages of described approach over old herbalism mods:\n\n1. There is no need to spawn separate \"harvested\" objects\n\n2. There is no need to attach a script to every container\n\n3. It supports an ownership check (the original engine without MWSE does not)\n\n4. It does not alter original respawn mechanics\n\nAn example of mod which uses this feature is `Graphic Herbalism`_.\n\nAnimated containers support\n---------------------------\n\nIt is possible to attach opening/closing animations for containers. To do this, you need to create a KF-file for the container with the following groups:\n\n1. ``ContainerOpen`` (with ``Start``, ``Loot`` and ``Stop`` keys)\n\n2. ``ContainerClose`` (with ``Start`` and ``Stop`` keys)\n\nThe ``Loot`` key for ``ContainerOpen`` allows to play a part of opening animation in the background.\n\nFor example, with the following setup, the opening animation has 1.0 sec duration and shows the container window in the middle of opening animation:\n\n::\n\n    0.0: ContainerOpen: start\n    0.5: ContainerOpen: loot\n    1.0: ContainerOpen: stop\n\nIt is also possible to attach opening/closing sounds to container's animations:\n\n::\n\n    1.0: ContainerClose: start\n    1.01: Sound: AC_dw_drawer_close\n    2.0: ContainerClose: stop\n\nThe text key approach is the same as the one used for sound playback in animations in general\nNote that the sound starting time value is slightly higher than the closing animation start, otherwise sound will be played at the end of opening animation in this example.\n\nIt is important to assign a RootCollisionNode to the container mesh -- the collision shape will depend on the animation state otherwise, and this can have a performance impact.\n\nAdvantages of described approach over old animated containers mods:\n\n1. There is no need to attach a script to every container\n\n2. Part of the opening animation can be played in the background, so we do not waste the player's time\n\nAn example of a mod which uses this feature is `OpenMW Containers Animated`_.\n\n\nDay/night state support\n-----------------------\n\nIt is possible to add one or more NiSwitchNodes named ``NightDaySwitch``.\nEvery such switch should have at least two child nodes\n(the first node represents the normal node state, the second one represents the node state during night,\nthe optional third node represents the node state during daytime in interior cells).\n\nThe behavior of such a model:\n\n1. During the day in exteriors, it is in the \"normal\" mode (child 0).\n\n2. During the night in exteriors, it is in the \"night\" mode (child 1).\n\n3. During the day in interiors, it is in the \"normal\" mode or \"interior day\" mode (child 2) depending on weather.\n\n4. During the night in interiors, it is in the \"normal\" mode.\n\nThe actual state toggling time depends on the sunrise/sunset time settings in `openmw.cfg`:\n\n::\n\n    fallback=Weather_Sunrise_Time,6\n    fallback=Weather_Sunset_Time,18\n    fallback=Weather_Sunrise_Duration,2\n    fallback=Weather_Sunset_Duration,2\n\nThese settings lead to the \"night\" starting at 20:00 and ending at 6:00.\n\nThe engine checks if the weather is bright enough to support the \"interior day\" mode using the Glare_View setting. If it is >= 0.5, the engine considers the weather bright.\n\n::\n\n    fallback=Weather_Clear_Glare_View,1\n    fallback=Weather_Foggy_Glare_View,0.25\n\nWith these settings, the \"interior day\" mode would be used for Clear weather, but would not be used for Foggy weather.\n\nKeep in mind that the engine will not update the weather type after a teleportation to a different region if the player did not move to an exterior cell in the new region yet.\n\nThis feature can be used to implement street illumination, glowing windows, etc.\n\nAdvantages of the described approach over old mods with glowing windows:\n\n1. There is no need to spawn additional objects for day and night mode\n\n2. There is no need to attach a script to every switchable object\n\nAn example of a mod which uses this feature is `Glow in the Dahrk`_.\n\n\nPer-group animation files support\n---------------------------------\n\nIn the original engine it is possible to add a custom animation file to NPC to override some animations (usually idle ones).\nIn OpenMW it is possible to override animations via the same file for all actors which use a given basic animation file.\n\nIf you want to override animations for all biped actors (which use the xbase_anim.nif skeleton), you can put your animations in the\n``Animations/xbase_anim`` folder in your ``Data Files``. You can also have them in a data folder with a higher priority.\nIn this case any biped actor without a custom animation will use your animations, but – if he has additional animations – they have a higher priority.\n\nFor example, all biped actors in Morrowind normally use the same spellcasting animations, so overriding xbase_anim spellcasting animations is sufficient.\nIf you want to override walking animations, you should override ``xbase_anim_female`` and ``xbase_anim_kna`` animations -- these are used for women and beast races, and\n– because they have their own walking animations – they override ones which come from ``xbase_anim`` and its loose overrides.\n\nTo enable this feature, you should have this line in your settings.cfg:\n\n::\n\n    [Game]\n    use additional anim sources = true\n\nAn example of a mod which uses this feature is `Almalexia's Cast for Beasts`_.\n\n\nWeapon sheathing support\n------------------------\n\nIn OpenMW it is possible to display equipped, but not currently wielded weapons on the actor's model, including quivers and scabbards.\n\nThis feature conflicts with old mods which use scripted scabbards, arrows with particles or decorative quivers (attached to the left pauldron, for example).\n\n1. Basics\n\nThe minimum you need is the ``xbase_anim_sh.nif`` file from the `Weapon Sheathing`_ mod and this line in your settings.cfg:\n\n::\n\n    [Game]\n    weapon sheathing = true\n\nThe ``xbase_anim_sh.nif`` contains default placement points for different weapon types.\nThat way you'll get Gothic-style weapon sheathing for all biped actors (without quivers and scabbards).\n\n2. Scabbards\n\nFor a scabbard to be displayed, you need a mesh with an ``_sh`` suffix. For example, if the weapon has a model named foo.nif, the scabbard model must be named foo_sh.nif.\n\nThere should be an least two nodes in the sheath file:\n\n``Bip01 Weapon`` - represents the weapon itself (may be just a grip for sword, for example). It is not shown when the weapon is drawn.\n\n``Bip01 Sheath`` - represents scabbards, quivers, etc. It is shown always when the weapon is equipped.\n\nYou can move or rotate nodes if the default placement from the ``xbase_anim_sh.nif`` does not look good for your weapon.\n\nIf you want to exempt a specific weapon from using this feature, you can create a stub sheath mesh with just one root node.\n\nIf you want to use the common weapon mesh, but with custom placement, you can create a sheath mesh with an empty ``Bip01 Weapon`` node and move it as you want.\n\n3. Quivers\n\nTo show the quiver for a ranged weapon, you need these nodes in the sheath file:\n\n``Bip01 Sheath`` node, as for scabbards\n\n``Bip01 Ammo`` node to show ammunition in the quiver\n\n``Bip01 Weapon`` to show the weapon itself (not needed for throwing weapons)\n\nThe ``Bip01 Ammo`` should have some empty child nodes, to which the engine will attach ammunition nodes.\n\nThe appearance and count of shown ammunition depends on type and count of equipped ammunition. If the ammunition has a wrong type (e.g. bolts for bow), it won't be shown.\n\nIt is important to make sure the names of empty nodes start with ``\"Bip01 \"``, or the engine will optimize them out.\n\nAn example of a mod which uses this feature is `Weapon Sheathing`_.\n\n\nSkeleton extensions\n-------------------\n\nIt is possible to inject custom bones into actor skeletons:\n\n::\n\n    [Game]\n    use additional anim sources = true\n\nIf this setting is enabled, OpenMW will seek for modified skeletons in the ``Animations/[skeleton name]`` folder in your ``Data Files``.\nFor example, the biped creature skeleton folder is ``Animations/xbase_anim``, the female NPCs skeleton folder is ``Animations/xbase_anim_female``,\nthe beast race skeleton folder is ``Animations/xbase_anim_kna``.\nNote that these are the third person view skeletons, and the first person view skeleton will have a different name.\n\nOpenMW scans every NIF file in such a folder for nodes which have \"BONE\" NiStringExtraData.\nIt is recommended to give such nodes names that start with \"Bip01 \" so that the mesh optimizer doesn't try to optimize them out.\nThen OpenMW copies all found nodes to related skeleton. To determine the bone to which the new node should be attached,\nOpenMW checks the name of the parent node of the new node in the original NIF file.\nFor example, to attach a custom weapon bone, you'll need to follow this NIF record hierarchy:\n\n::\n\n    NiNode \"root\"\n        NiNode \"Bip01 L Hand\"\n            NiNode \"Weapon Bone Left\"\n                NiStringExtraData \"BONE\"\n\nOpenMW will detect ``Weapon Bone Left`` node and attach it to ``Bip01 L Hand`` bone of the target skeleton.\n\nAn example of a mod which uses this feature is `Weapon Sheathing`_.\n\n\nExtended weapon animations\n--------------------------\n\nIt is possible to use unique animation groups for different weapon types.\nThey are not mandatory, and the currently hardcoded weapon types will fall back to existing generic animations.\nEvery weapon type has an attack animation group and a suffix for the movement animation groups.\nFor example, long blades use ``weapononehand`` attack animation group, ``idle1h`` idle animation group, ``jump1h`` jumping animation group, etc.\nThis is the full table of supported animation groups:\n\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|  Weapon type  |  Animation group  |  Movement suffix |   Attack (fallback)  |   Suffix (fallback)   |      Attach bone      |\n+===============+===================+==================+======================+=======================+=======================+\n|  Short blade  | shortbladeonehand |        1s        |    weapononehand     |          1h           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n| Long blade 1H |   weapononehand   |        1h        |                      |                       |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n| Long blade 2H |   weapontwohand   |        2c        |                      |                       |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|   Blunt 1H    |   bluntonehand    |        1b        |    weapononehand     |          1h           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|   Blunt 2H    |   blunttwohand    |        2b        |    weapontwohand     |          2c           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|    Axe 1H     |   bluntonehand    |        1b        |    weapononehand     |          1h           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|    Axe 2H     |   blunttwohand    |        2b        |    weapontwohand     |          2c           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n| Blunt 2H wide |   weapontwowide   |        2w        |    weapontwohand     |          2c           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|     Spear     |   weapontwowide   |        2w        |    weapontwohand     |          2c           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|      Bow      |    bowandarrow    |        bow       |                      |          1h           |    Weapon Bone Left   |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|    Crossbow   |     crossbow      |     crossbow     |                      |          1h           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n|     Thrown    |    throwweapon    |        1t        |                      |          1h           |      Weapon Bone      |\n+---------------+-------------------+------------------+----------------------+-----------------------+-----------------------+\n\nNote that bows can be attached to the \"Weapon Bone Left\" bone if it is present in shooter's skeleton, and if it is not, \"Weapon Bone\" is used as a fallback.\n\nAlso it is possible to add a \"Bip01 Arrow\" bone to actor skeletons. In this case OpenMW attaches arrows to this bone instead of ArrowBone in the bow mesh.\nSuch approach allows to implement better shooting animations (for example, beast races have tail, so quivers should be attached under different angle and\ndefault arrow fetching animation does not look good).\n\nGroundcover support\n-------------------\n\nGroundcover objects is a special kind of objects (e.g. grass), which can be used to improve visual fidelity.\nThey use these assumptions:\n\n1. Each object is independent, so part of objects can be removed from scene without causing graphical artifacts.\n\n2. Groundover should not have collisions.\n\n3. They are not important for some parts of game scene (e.g. local map).\n\n4. They can not be moved or disabled on the fly.\n\n5. They can not be interacted with.\n\nAs result, such objects can be treated in the separate way:\n\n1. It is possible to tweak groundcover objects density.\n\n2. It is possible to safely merge such objects even near player.\n\n3. Such objects can be animated (to simulate wind, for example).\n\n4. Some parts of processing can be skipped.\n\nFor example, we do not need to have collision or animation objects for groundcover,\ndo not need to render groundcover on the map, do not need to render it for the whole visible area (which can be very large with Distant Terrain). It allows to increase performance a lot.\n\nGeneral advices to create assets for this feature:\n\n1. Alpha properties from Nif files are not used, a unified alpha settings are used (alpha testing, \"greater of equal\" function, 128/255 threshold).\n\n2. Use a single NiTriShape in groundocver mesh, or at least use same properties (texture, alpha, material, etc), so OpenMW can merge them on the fly. Otherwise animations may not work properly.\n\n3. Smooth fading does not work for meshes, which have textures without alpha (e.g. rock).\n\nGroundcover mods can be registered in the openmw.cfg via \"groundcover\" entries instead of \"content\" ones:\n\n::\n\n    groundcover=my_grass_mod.esp\n\nEvery static from such mod is treated as a groundcover object.\nAlso groundcover detection should be enabled via settings.cfg:\n\n::\n\n    [Groundcover]\n    enabled = true\n\n.. _`Graphic Herbalism`: https://www.nexusmods.com/morrowind/mods/46599\n.. _`OpenMW Containers Animated`: https://www.nexusmods.com/morrowind/mods/46232\n.. _`Glow in the Dahrk`: https://www.nexusmods.com/morrowind/mods/45886\n.. _`Almalexia's Cast for Beasts`: https://www.nexusmods.com/morrowind/mods/45853\n.. _`Weapon sheathing`: https://www.nexusmods.com/morrowind/mods/46069\n"
  },
  {
    "path": "docs/source/reference/modding/font.rst",
    "content": "Fonts\n#####\n\nMorrowind .fnt fonts\n--------------------\n\nMorrowind uses a custom ``.fnt`` file format. It is not compatible with the Windows Font File ``.fnt`` format.\nTo our knowledge, the format is undocumented.\n\nOpenMW can load this format and convert it on the fly into something usable \n(see font loader `source code <https://github.com/OpenMW/openmw/blob/master/components/fontloader/fontloader.cpp#L210>`_). \nYou can use --export-fonts command line option to write the converted font\n(a PNG image and an XML file describing the position of each glyph in the image) to the current directory.\n\nTrueType fonts\n--------------\n\nUnlike vanilla Morrowind, OpenMW directly supports TrueType (``.ttf``) fonts.\n\nThis is the recommended way to install replacement fonts.\n\n\t1.\tDownload `TrueType fonts for OpenMW <https://www.nexusmods.com/morrowind/mods/46854>`_\n\t2.\tPlace the ``Fonts`` folder from archive to the configuration folder. Use :doc:`paths` article to find the folder.\n\nNow Fonts folder should include ``openmw_font.xml`` file and three ``.ttf`` files.\n\nIf desired, you can now delete the ``Data Files/Fonts`` directory.\n\nIt is also possible to adjust the font size and resolution via ``settings.cfg`` file::\n\n\t\t\t[GUI]\n\t\t\tfont size = 16\n\t\t\tttf resolution = 96\n\nThe ``font size`` setting accepts clamped values in range from 12 to 20 while ``ttf resolution`` setting accepts values from 48 to 960.\n\nAny Resolution or Size properties in the XML file have no effect because the engine settings override them.\n\nThe engine automatically takes UI scaling factor into account, so don't account for it when tweaking the settings.\n\nBitmap fonts\n------------\n\nMorrowind ``.fnt`` files are essentially a bitmap font, but using them is discouraged because they don't have Unicode support. \nMyGUI has its own format for bitmap fonts. An example can be seen by using the --export-fonts command line option (see above), \nwhich converts Morrowind ``.fnt`` to a MyGUI bitmap font. \nThis is the recommended format to use if you wish to edit Morrowind's bitmap font or create a new bitmap font.\n"
  },
  {
    "path": "docs/source/reference/modding/foreword.rst",
    "content": "Foreword\n########\n\nOpenMW is a complete game engine built to be content agnostic. \nThe majority of this guide is applicable to any non-Morrowind project using its engine. \nThat being said, it was designed with the extensive modding community of Morrowind in mind. \nTherefore, if you are already familiar with modding in Morrowind, \nyou will likely be able to start modding in OpenMW with little to no instruction. \nWe do recommend you at least refer to :doc:`differences` to find out about what's different between OpenMW and the \noriginal Morrowind engine. For everyone else, or just a good refresher, read on!"
  },
  {
    "path": "docs/source/reference/modding/index.rst",
    "content": "OpenMW Modding Reference\n########################\n\nThe following document is the complete reference guide to modifying, or\nmodding, your OpenMW setup. It does not cover content creation itself,\nonly how to alter or add to your OpenMW gameplay experience. To learn more\nabout creating new content for OpenMW, please refer to\n:doc:`/manuals/openmw-cs/index`.\n\n.. warning::\n\tOpenMW is still software in development. This manual does not cover any\n\tof its shortcomings. It is written as if everything was working as\n\tintended. Please report any software problems as bugs in the software,\n\tnot errors in the manual.\n\n.. toctree::\n\t:caption: Table of Contents\n\t:maxdepth: 2\n\n\tforeword\n\tdifferences\n\tmod-install\n\topenmw-game-template\n\tsettings/index\n\ttexture-modding/index\n\tcustom-models/index\n\tfont\n\tsky-system\n\textended\n\tpaths\n"
  },
  {
    "path": "docs/source/reference/modding/mod-install.rst",
    "content": "How To Install and Use Mods\n###########################\n\nThe following is a detailed guide on how to install and enable mods in OpenMW using best practices.\n\nInstall\n-------\n\n#.\tYour mod probably comes in some kind of archive, such as ``.zip``, ``.rar``, ``.7z``, or something along those lines. Unpack this archive into its own folder.\n#.\tEnsure the structure of this folder is correct.\n\n\t#.\tLocate the plugin files, ``.esp`` or ``.omwaddon``, or possibly ``.esm``. The folder containing the plugin files we will call your *data folder*\n\t#.\tCheck that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*.\n\t#.\tNote that not all mods have a plugin, and not all mods have resources, but they must at minimum have one or the other.\n\t\n\t.. note::\n\t\tThere may be multiple levels of folders, but the location of the plugins must be the same as the resource folders.\n\n#.\tOpen your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in :doc:`paths` and *not* in your OpenMW root directory.\n#.\tFind or search for ``data=``. This is located very near the bottom of the file. If you are using Morrowind, this first entry should already point to your Morrowind data directory, ``Data Files``; otherwise it will point to your game file, ``.omwgame``.\n#.\tCreate a new line underneath and type: ``data=\"path/to/your/data folder\"`` Remember, the *data folder* is where your mod's plugin files are. The double quotes around this path name are *required*.\n#.\tIf your mod contains resources in a ``.bsa`` file, go to near the top of the file, locate the entries like ''fallback-archive=Morrowind.bsa'' and create a new line underneath and type: ``fallback-archive=<name of your bsa>.bsa''``.\n\n.. note::\n\tSome text editors, such as TextEdit on Mac, will auto-correct your double quotes to typographical \"curly\"\n\tquotes instead of leaving them as the proper neutral vertical quotes ``\"\"``.\n\n#.\tSave your ``openmw.cfg`` file.\n\nYou have now installed your mod. Any simple replacer mods that only contain resource files such as meshes or \ntextures will now automatically be loaded in the order of their ``data=*`` entry. \nThis is important to note because replacer mods that replace the same resource will overwrite previous ones as you go down the list.\n\nEnable\n------\n\nAny mods that have plugin files must be enabled to work. \nMaster game files and plugin files can only be enabled if they have been properly installed within a *data folder* as described above.\n\n#.\tOpen the OpenMW Launcher.\n#.\tClick on the Data Files tab.\n#.\tIn the Content List box, select the content list you wish to modify in the dropdown menu, or make a new one by:\n\n\t#.\tClick the New Content List button and enter the name of your content list, then click OK. New lists are useful for keeping track of the mods used for different characters or for different games if you play more than one game using OpenMW.\n\t#.\tIn the Content box, select your game file (``.esm`` or ``.omwgame``) from the dropdown menu.\n\t\n#.\tNow you must activate the plugins you wish to use by checking the box next to their entry in the Content box list.\n#.\tLoad order can be changed simply by dragging the entries around within the list. Mods are loaded from the top down, so if one plugin depends on another, it must be lower on the list.\n#.\tClick Play to run OpenMW with your game and enabled mods!\n"
  },
  {
    "path": "docs/source/reference/modding/openmw-game-template.rst",
    "content": "####################\nOpenMW Game Template\n####################\n\nOpenMW Game Template, or simply, the Template, is a set of base assets required\nfor OpenMW to run. These include ``template.omwgame`` along with models, textures,\nfonts, and UI content. The Template can be used as a foundation for a standalone\ngame in OpenMW, without requiring any of the original, copyrighted Morrowind assets.\nWith the exception of ``Pelagiad.ttf`` font file, the Template is released as\n`public domain <https://creativecommons.org/publicdomain/zero/1.0/>`_.\n\n\nInstallation\n************\n\nThe Template is installed the same way you would install a mod, with general\ninstructions available at :doc:`mod-install`. It can be downloaded from\n`its repository <https://gitlab.com/OpenMW/example-suite>`_ and requires\nOpenMW 0.47 or later.\n\nAfter getting the Template, extract its ``/data`` folder to somewhere on your disk.\n\n.. note::\n\n    It's adviseable to not put the Template files in the same folder as your\n    Morrowind files. This is especially valid when you don't wish to mix the two games\n    and use the Template as a foundation for a standalone game.\n\n\nDefine paths to .omwgame and data files\n=======================================\n\nOpenMW needs to be told where to look for the Template files. This is done in\n``openmw.cfg`` file where ``content=`` tells OpenMW which .omwgame file to use\nand ``data=`` tells OpenMW what folders to look for meshes, textures, audio,\nand other assets. The required lines would look like this, but with the paths\nof course different on your system.\n\n.. code::\n\n    content=template.omwgame\n    data=\"/home/someuser/example-suite/data\"\n    data=\"/home/someuser/example-suite\"\n\n\nRemove references to Morrowind files\n====================================\n\nIn case you have Morrowind installed and have run OpenMW's installation wizard,\nyou need to remove or comment out the following lines from ``openmw.cfg``.\nNot doing so will either produce errors or load Morrowind content, which you\nprobably do not want when you are making your own game.\n\n.. code::\n\n    fallback-archive=Morrowind.bsa\n    fallback-archive=Tribunal.bsa\n    fallback-archive=Bloodmoon.bsa\n    content=Morrowind.esm\n    content=Tribunal.esm\n    content=Bloodmoon.esm\n    data=\"/home/someuser/.wine/dosdevices/c:/Morrowind/Data Files\"\n\n\nDefine paths to essential models\n================================\n\nCertain models, essential to OpenMW, cannot be assigned through OpenMW-CS but\nare instead assigned through ``settings.cfg``. These models are player and NPC\nanimations, and meshes for the sky. In ``settings.cfg`` used by your OpenMW\ninstall, add the following lines under the ``[Models]`` section.\n\n.. code::\n\n    xbaseanim = meshes/BasicPlayer.dae\n    baseanim = meshes/BasicPlayer.dae\n    xbaseanim1st = meshes/BasicPlayer.dae\n    baseanimkna = meshes/BasicPlayer.dae\n    baseanimkna1st = meshes/BasicPlayer.dae\n    xbaseanimfemale = meshes/BasicPlayer.dae\n    baseanimfemale = meshes/BasicPlayer.dae\n    baseanimfemale1st = meshes/BasicPlayer.dae\n    xargonianswimkna = meshes/BasicPlayer.dae\n    xbaseanimkf = meshes/BasicPlayer.dae\n    xbaseanim1stkf = meshes/BasicPlayer.dae\n    xbaseanimfemalekf = meshes/BasicPlayer.dae\n    xargonianswimknakf = meshes/BasicPlayer.dae\n    skyatmosphere = meshes/sky_atmosphere.dae\n    skyclouds = meshes/sky_clouds_01.osgt\n    skynight01 = meshes/sky_night_01.osgt\n\n\nAs a convenience the Template repository includes a ``settings.cfg`` containing\nthese same lines which can be copied and pasted. However, do not use the file\nto simply overwrite the ``settings.cfg`` used by your OpenMW installation.\n\n\nCopying the UI files\n====================\n\nThe Template includes a ``resources/mygui`` folder. The contents of this folder\nneed to be copied to ``resources/mygui`` folder found in your OpenMW installation\nfolder. Overwrite any files aready in this folder. These files provide the\nUI font, its definition, and some minor UI tweaks.\n\n.. code::\n\n    openmw_box.skin.xml\n    openmw_button.skin.xml\n    openmw_font.xml\n    openmw_windows.skin.xml\n    Pelagiad.ttf\n\n\nRun OpenMW Launcher\n*******************\n\nAfter completing all the steps, run OpenMW Launcher and make sure ``template.omwgame``\nis selected in *Data Files* tab. Then, run the game and enjoy an empty island. It is not\nempty though! It is full of potential to start making your very own game on the\nOpenMW engine. Good luck! \n\n"
  },
  {
    "path": "docs/source/reference/modding/paths.rst",
    "content": "Paths\n#####\n\nThe following describes the locations for the various OpenMW file paths for different types of files on different operating systems.\n\n.. note::\n\tActual location depends on your computer's setup. Username, harddrive, and language may vary.\n\nConfiguration files and log files\n---------------------------------\n\n+--------------+-----------------------------------------------------------------------------------------------+\n| OS           | Location                                                                                      |\n+==============+===============================================================================================+\n| Linux        | ``$HOME/.config/openmw``                                                                      |\n+--------------+-----------------------------------------------------------------------------------------------+\n| Mac          | ``$HOME/Library/Preferences/openmw``                                                          |\n+--------------+---------------+-------------------------------------------------------------------------------+\n| Windows      | File Explorer | ``Documents\\My Games\\OpenMW``                                                 |\n|              |               |                                                                               |\n|              | PowerShell    | ``Join-Path ([environment]::GetFolderPath(\"mydocuments\")) \"My Games\\OpenMW\"`` |\n|              |               |                                                                               |\n|              | Example       | ``C:\\Users\\Username\\Documents\\My Games\\OpenMW``                               |\n+--------------+---------------+-------------------------------------------------------------------------------+\n\nSavegames\n---------\n\n+--------------+-----------------------------------------------------------------------------------------------------+\n| OS           | Location                                                                                            |\n+==============+=====================================================================================================+\n| Linux        | ``$HOME/.config/openmw/saves``                                                                      |\n+--------------+-----------------------------------------------------------------------------------------------------+\n| Mac          | ``$HOME/Library/Application\\ Support/openmw/saves``                                                 |\n+--------------+---------------+-------------------------------------------------------------------------------------+\n| Windows      | File Explorer | ``Documents\\My Games\\OpenMW\\saves``                                                 |\n|              |               |                                                                                     |\n|              | PowerShell    | ``Join-Path ([environment]::GetFolderPath(\"mydocuments\")) \"My Games\\OpenMW\\saves\"`` |\n|              |               |                                                                                     |\n|              | Example       | ``C:\\Users\\Username\\Documents\\My Games\\OpenMW\\saves``                               |\n+--------------+---------------+-------------------------------------------------------------------------------------+\n\nScreenshots\n-----------\n\n+--------------+-----------------------------------------------------------------------------------------------------------+\n| OS           | Location                                                                                                  |\n+==============+===========================================================================================================+\n| Linux        | ``$HOME/.local/share/openmw/screenshots``                                                                 |\n+--------------+-----------------------------------------------------------------------------------------------------------+\n| Mac          | ``$HOME/Library/Application\\ Support/openmw/screenshots``                                                 |\n+--------------+---------------+-------------------------------------------------------------------------------------------+\n| Windows      | File Explorer | ``Documents\\My Games\\OpenMW\\screenshots``                                                 |\n|              |               |                                                                                           |\n|              | PowerShell    | ``Join-Path ([environment]::GetFolderPath(\"mydocuments\")) \"My Games\\OpenMW\\screenshots\"`` |\n|              |               |                                                                                           |\n|              | Example       | ``C:\\Users\\Username\\Documents\\My Games\\OpenMW\\screenshots``                               |\n+--------------+---------------+-------------------------------------------------------------------------------------------+\n"
  },
  {
    "path": "docs/source/reference/modding/settings/GUI.rst",
    "content": "GUI Settings\n############\n\nscaling factor\n--------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.5 to 8.0\n:Default:\t1.0\n\nThis setting scales GUI windows.\nA value of 1.0 results in the normal scale. Larger values are useful to increase the scale of the GUI for high resolution displays.\n\nThis setting can be configured in the Interface section of Advanced tab of the launcher.\n\nfont size\n---------\n\n:Type:\t\tinteger\n:Range:\t\t12 to 20\n:Default:\t16\n\nAllows to specify glyph size for in-game fonts.\nNote: default bitmap fonts are supposed to work with 16px size, otherwise glyphs will be blurry.\nTrueType fonts do not have this issue.\n\nttf resolution\n--------------\n\n:Type:\t\tinteger\n:Range:\t\t48 to 960\n:Default:\t96\n\nAllows to specify resolution for in-game TrueType fonts.\nNote: actual resolution depends on \"scaling factor\" setting value, this value is for 1.0 scaling factor.\n\nmenu transparency\n-----------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 (transparent) to 1.0 (opaque)\n:Default:\t0.84\n\nThis setting controls the transparency of the GUI windows.\nThis setting can be adjusted in game with the Menu Transparency slider in the Prefs panel of the Options menu.\n\ntooltip delay\n-------------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0.0\n:Default:\t0.0\n\nThis value determines the number of seconds between when you begin hovering over an item and when its tooltip appears.\nThis setting only affects the tooltip delay for objects under the crosshair in GUI mode windows.\n\nThe tooltip displays context sensitive information on the selected GUI element,\nsuch as weight, value, damage, armor rating, magical effects, and detailed description.\n\nThis setting can be adjusted between 0.0 and 1.0 in game\nwith the Menu Help Delay slider in the Prefs panel of the Options menu.\n\nstretch menu background\n-----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nStretch or shrink the main menu screen, loading splash screens, introductory movie,\nand cut scenes to fill the specified video resolution, distorting their aspect ratio.\nThe Bethesda provided assets have a 4:3 aspect ratio, but other assets are permitted to have other aspect ratios.\nIf this setting is false, the assets will be centered in their correct aspect ratio,\nwith black bars filling the remainder of the screen.\n\nThis setting can be configured in the Interface section of Advanced tab of the launcher.\n\nsubtitles\n---------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nEnable or disable subtitles for NPC spoken dialog (and some sound effects).\nSubtitles will appear in a tool tip box in the lower center of the screen.\n\nThis setting can be toggled in game with the Subtitles button in the Prefs panel of Options menu.\n\nhit fader\n---------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nThis setting enables or disables the \"red flash\" overlay that provides a visual clue when the character has taken damage.\n\nIf this setting is disabled, the player will \"bleed\" like NPCs do.\n\nThis setting can only be configured by editing the settings configuration file.\n\nwerewolf overlay\n----------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nEnable or disable the werewolf visual effect in first-person mode.\n\nThis setting can only be configured by editing the settings configuration file.\n\ncolor background owned\n----------------------\n\n:Type:\t\tRGBA floating point\n:Range:\t\t0.0 to 1.0\n:Default:\t0.15 0.0 0.0 1.0 (dark red)\n\nThe following two settings determine the background color of the tool tip and the crosshair\nwhen hovering over an item owned by an NPC.\nThe color definitions are composed of four floating point values between 0.0 and 1.0 inclusive,\nrepresenting the red, green, blue and alpha channels. The alpha value is currently ignored.\nThe crosshair color will have no effect if the crosshair setting in the HUD section is disabled.\n\nThis setting can only be configured by editing the settings configuration file.\nThis setting has no effect if the show owned setting in the Game Settings Section is false.\n\ncolor crosshair owned\n---------------------\n\n:Type:\t\tRGBA floating point\n:Range:\t\t0.0 to 1.0\n:Default:\t1.0 0.15 0.15 1.0 (bright red)\n\nThis setting sets the color of the crosshair when hovering over an item owned by an NPC.\nThe value is composed of four floating point values representing the red, green, blue and alpha channels.\nThe alpha value is currently ignored.\n\nThis setting can only be configured by editing the settings configuration file.\nThis setting has no effect if the crosshair setting in the HUD Settings Section is false.\nThis setting has no effect if the show owned setting in the Game Settings Section is false.\n\ncolor topic enable\n------------------\n\n:Type:      boolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting controls whether the topics available in the dialogue topic list are coloured according to their state.\nSee 'color topic specific' and 'color topic exhausted' for details.\n\ncolor topic specific\n--------------------\n\n:Type:\t\tRGBA floating point\n:Range:\t\t0.0 to 1.0\n:Default:\tempty\n\nThis setting overrides the colour of dialogue topics that have a response unique to the actors speaking.\nThe value is composed of four floating point values representing the red, green, blue and alpha channels.\nThe alpha value is currently ignored.\n\nA topic response is considered unique if its Actor filter field contains the speaking actor's object ID and hasn't yet been read.\n\ncolor topic exhausted\n---------------------\n\n:Type:\t\tRGBA floating point\n:Range:\t\t0.0 to 1.0\n:Default:\tempty\n\nThis setting overrides the colour of dialogue topics which have been \"exhausted\" by the player.\nThe value is composed of four floating point values representing the red, green, blue and alpha channels.\nThe alpha value is currently ignored.\n\nA topic is considered \"exhausted\" if the response the player is about to see has already been seen.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/HUD.rst",
    "content": "HUD Settings\n############\n\ncrosshair\n---------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nThis setting determines whether the crosshair or reticle is displayed. \nEnabling the crosshair provides more immediate feedback about which object is currently the focus of actions.\nSome players perceive that disabling the crosshair provides a more immersive experience.\nAnother common use is to disable the crosshair for screen shots.\n\nAs an alternative to this setting, the complete GUI, including the crosshair, may be toggled at runtime with the F11 key.\n\nThis setting can be toggled with the Crosshair button in the Prefs panel of the Options menu.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/camera.rst",
    "content": "Camera Settings\n###############\n\nnear clip\n---------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0\n:Default:\t3.0\n\nThis setting controls the distance to the near clipping plane. The value must be greater than zero.\nValues greater than approximately 18.0 will occasionally clip objects in the world in front of the character.\nValues greater than approximately 8.0 will clip the character's hands in first person view\nand/or the back of their head in third person view.\n\nThis setting can only be configured by editing the settings configuration file.\n\nsmall feature culling\n---------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nThis setting determines whether objects that render to a few pixels or smaller will be culled (not drawn).\nIt generally improves performance to enable this feature,\nand by definition the culled objects will be very small on screen.\nThe size in pixels for an object to be considered 'small' is controlled by a separate setting.\n\nThis setting can only be configured by editing the settings configuration file.\n\nsmall feature culling pixel size\n--------------------------------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0\n:Default:\t2.0\n\nControls the cutoff in pixels for the 'small feature culling' setting,\nwhich will have no effect if 'small feature culling' is disabled.\n\nThis setting can only be configured by editing the settings configuration file.\n\nviewing distance\n----------------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0\n:Default:\t7168.0\n\nThis value controls the maximum visible distance (also called the far clipping plane).\nLarger values significantly improve rendering in exterior spaces,\nbut also increase the amount of rendered geometry and significantly reduce the frame rate.\nNote that when cells are visible before loading, the geometry will \"pop-in\" suddenly,\ncreating a jarring visual effect. To prevent this effect, this value should not be greater than:\n\n\tCellSizeInUnits * CellGridRadius - 1024\n\nThe CellSizeInUnits is the size of a game cell in units (8192 by default), CellGridRadius determines how many\nneighboring cells to current one to load (1 by default - 3x3 grid), and 1024 is the threshold distance for loading a new cell.\nThe field of view setting also interacts with this setting because the view frustum end is a plane,\nso you can see further at the edges of the screen than you should be able to.\nThis can be observed in game by looking at distant objects\nand rotating the camera so the objects are near the edge of the screen.\nAs a result, this distance is recommended to further be reduced to avoid pop-in for wide fields of view\nand long viewing distances near the edges of the screen if distant terrain and object paging are not used.\n\nReductions of up to 25% or more can be required to completely eliminate this pop-in.\nSuch situations are unusual and probably not worth the performance penalty introduced\nby loading geometry obscured by fog in the center of the screen.\nSee RenderingManager::configureFog for the relevant source code.\n\nThis setting can be adjusted in game from the ridiculously low value of 2500 units to a maximum of 7168 units\nusing the View Distance slider in the Detail tab of the Video panel of the Options menu, unless distant terrain is enabled,\nin which case the maximum is increased to 81920 units.\n\nfield of view\n-------------\n\n:Type:\t\tfloating point\n:Range:\t\t1-179\n:Default:\t55.0\n\nSets the camera field of view in degrees. Recommended values range from 30 degrees to 110 degrees.\nSmall values provide a very narrow field of view that creates a \"zoomed in\" effect,\nwhile large values cause distortion at the edges of the screen.\nThe \"field of view\" setting interacts with aspect ratio of your video resolution in that more square aspect ratios\n(e.g. 4:3) need a wider field of view to more resemble the same field of view on a widescreen (e.g. 16:9) monitor.\n\nThis setting can be changed in game using the Field of View slider from the Video tab of the Video panel of the Options menu.\n\nfirst person field of view\n--------------------------\n\n:Type:\t\tfloating point\n:Range:\t\t1-179\n:Default:\t55.0\n\nThis setting controls the field of view for first person meshes such as the player's hands and held objects.\nIt is not recommended to change this value from its default value\nbecause the Bethesda provided Morrowind assets do not adapt well to large values,\nwhile small values can result in the hands not being visible.\n\nThis setting can only be configured by editing the settings configuration file.\n\nthird person camera distance\n----------------------------\n\n:Type:\t\tfloating point\n:Range:\t\t30-800\n:Default:\t192.0\n\nDistance from the camera to the character in third person mode.\n\nThis setting can be changed in game using \"Zoom In\" / \"Zoom Out\" controls.\n\nview over shoulder\n------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting controls third person view mode.\nFalse: View is centered on the character's head. Crosshair is hidden.\nTrue: In non-combat mode camera is positioned behind the character's shoulder. Crosshair is visible in third person mode as well.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nview over shoulder offset\n-------------------------\n\n:Type:\t\t2D vector floating point\n:Range:\t\tAny\n:Default:\t30 -10\n\nThis setting makes sense only if 'view over shoulder' is enabled. Controls horizontal (first number) and vertical (second number) offset of the camera in third person mode.\nRecommened values: 30 -10 for the right shoulder, -30 -10 for the left shoulder.\n\nThis setting can only be configured by editing the settings configuration file.\n\nauto switch shoulder\n--------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nThis setting makes difference only in third person mode if 'view over shoulder' is enabled.\nWhen player is close to an obstacle, automatically switches camera to the shoulder that is farther away from the obstacle.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nzoom out when move coef\n-----------------------\n\n:Type:\t\tfloating point\n:Range:\t\tAny\n:Default:\t20\n\nThis setting makes difference only in third person mode if 'view over shoulder' is enabled.\nSlightly pulls camera away (or closer in case of negative value) when the character moves. To disable set it to zero.\n\nThis setting can only be configured by editing the settings configuration file.\n\npreview if stand still\n----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nMakes difference only in third person mode.\nIf enabled then the character rotation is not synchonized with the camera rotation while the character doesn't move and not in combat mode.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\ndeferred preview rotation\n-------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nMakes difference only in third person mode.\nIf enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode.\nIf disabled then the camera rotates rather than the character.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nhead bobbing\n------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nEnables head bobbing when move in first person mode.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nhead bobbing step\n-----------------\n\n:Type:\t\tfloating point\n:Range:\t\t>0\n:Default:\t90.0\n\nMakes diffence only in first person mode if 'head bobbing' is enabled.\nLength of each step.\n\nThis setting can only be configured by editing the settings configuration file.\n\nhead bobbing height\n-------------------\n\n:Type:\t\tfloating point\n:Range:\t\tAny\n:Default:\t3.0\n\nMakes diffence only in first person mode if 'head bobbing' is enabled.\nAmplitude of the head bobbing.\n\nThis setting can only be configured by editing the settings configuration file.\n\nhead bobbing roll\n-----------------\n\n:Type:\t\tfloating point\n:Range:\t\t0-90\n:Default:\t0.2\n\nMakes diffence only in first person mode if 'head bobbing' is enabled.\nMaximum roll angle in degrees.\n\nThis setting can only be configured by editing the settings configuration file.\n\n"
  },
  {
    "path": "docs/source/reference/modding/settings/cells.rst",
    "content": "Cells Settings\n##############\n\npreload enabled\n---------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nControls whether textures and objects will be pre-loaded in background threads.\nThis setting being enabled should result in a reduced amount of loading screens, no impact on frame rate,\nand a varying amount of additional RAM usage, depending on how the preloader was configured (see the below settings).\nThe default preloading settings with vanilla game files should only use negligible amounts of RAM, however,\nwhen using high-res texture and model replacers\nit may be necessary to tweak these settings to prevent the game from running out of memory.\n\nThe effects of (pre-)loading can be observed on the in-game statistics panel brought up with the 'F4' key.\n\nAll settings starting with 'preload' in this section will have no effect if preloading is disabled,\nand can only be configured by editing the settings configuration file.\n\n\npreload num threads\n-------------------\n\n:Type:\t\tinteger\n:Range:\t\t>=1\n:Default:\t1\n\nControls the number of worker threads used for preloading operations.\nIn addition to the preloading threads, OpenMW uses a main thread, a sound streaming thread, and a graphics thread.\nTherefore, the default setting of one preloading thread will result in a total of 4 threads used,\nwhich should work well with quad-core CPUs. If you have additional cores to spare,\nconsider increasing the number of preloading threads to 2 or 3 for a boost in preloading performance.\nFaster preloading will reduce the chance that a cell could not be completely loaded before the player moves into it,\nand hence reduce the chance of seeing loading screens or frame drops.\nThis may be especially relevant when the player moves at high speed\nand/or a large number of objects are preloaded due to large viewing distance.\n\nA value of 4 or higher is not recommended.\nWith 4 or more threads, improvements will start to diminish due to file reading and synchronization bottlenecks.\n\npreload exterior grid\n---------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nControls whether adjacent cells are preloaded when the player moves close to an exterior cell border.\n\npreload fast travel\n-------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nControls whether fast travel destinations are preloaded when the player moves close to a travel service.\nBecause the game can not predict the destination that the player will choose,\nall possible destinations will be preloaded. This setting is disabled by default\ndue to the adverse effect on memory usage caused by the preloading of all possible destinations.\n\npreload doors\n-------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nControls whether locations behind a door are preloaded when the player moves close to the door.\n\npreload distance\n----------------\n\n:Type:\t\tfloating point\n:Range:\t\t>0\n:Default:\t1000\n\nControls the distance in in-game units that is considered the player being 'close' to a preloading trigger.\nUsed by all the preloading mechanisms i.e. 'preload exterior grid', 'preload fast travel' and 'preload doors'.\n\nFor measurement purposes, the distance to an object in-game can be observed by opening the console,\nclicking on the object and typing 'getdistance player'.\n\npreload instances\n-----------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nControls whether or not objects are also pre-instanced on top of being pre-loaded.\nInstancing happens when the same object is placed more than once in the cell,\nand to be sure that any modifications to one instance do not affect the other,\nthe game will create independent copies (instances) of the object.\nIf this setting is enabled, the creation of instances will be done in the preloading thread;\notherwise, instancing will only happen in the main thread once the cell is actually loaded.\n\nEnabling this setting should reduce the chance of frame drops when transitioning into a preloaded cell,\nbut will also result in some additional memory usage.\n\npreload cell cache min\n----------------------\n\n:Type:\t\tinteger\n:Range:\t\t>0\n:Default:\t12\n\nThe minimum number of preloaded cells that will be kept in the cache.\nOnce the number of preloaded cells in the cache exceeds this setting,\nthe game may start to expire preloaded cells based on the 'preload cell expiry delay' setting,\nstarting with the oldest cell.\nWhen a preloaded cell expires, all the assets that were loaded for it will also expire\nand will have to be loaded again the next time the cell is requested for preloading.\n\npreload cell cache max\n----------------------\n\n:Type:\t\tinteger\n:Range:\t\t>0\n:Default:\t20\n\nThe maximum number of cells that will ever be in pre-loaded state simultaneously.\nThis setting is intended to put a cap on the amount of memory that could potentially be used by preload state.\n\npreload cell expiry delay\n-------------------------\n\n:Type:\t\tfloating point\n:Range:\t\t>=0\n:Default:\t5\n\nThe amount of time (in seconds) that a preloaded cell will stay in cache after it is no longer referenced or required,\nfor example, after the player has moved away from a door without entering it.\n\nprediction time\n---------------\n\n:Type:\t\tfloating point\n:Range:\t\t>=0\n:Default:\t1\n\nThe amount of time (in seconds) in the future to predict the player position for. \nThis predicted position is used to preload any cells and/or distant terrain required at that position.\n\nThis setting will only have an effect if 'preload enabled' is set or the 'distant terrain' in the Terrain section is set.\n\nIncreasing this setting from its default may help if your computer/hard disk is too slow to preload in time and you see\nloading screens and/or lag spikes.\n\ncache expiry delay\n------------------\n\n:Type:\t\tfloating point\n:Range:\t\t>=0\n:Default:\t5\n\nThe amount of time (in seconds) that a preloaded texture or object will stay in cache\nafter it is no longer referenced or required, for example, when all cells containing this texture have been unloaded.\n\ntarget framerate\n----------------\n:Type:          floating point\n:Range:         >0\n:Default:       60\n\nAffects the time to be set aside each frame for graphics preloading operations.\nThe game will distribute the preloading over several frames so as to not go under the specified framerate. \nFor best results, set this value to the monitor's refresh rate. If you still experience stutters on turning around, \nyou can try a lower value, although the framerate during loading will suffer a bit in that case.\n\npointers cache size\n-------------------\n\n:Type:\t\tinteger\n:Range:\t\t40 to 1000\n:Default:\t40\n\nThe count of object pointers that will be saved for a faster search by object ID.\nThis is a temporary setting that can be used to mitigate scripting performance issues with certain game files. \nIf your profiler (press F3 twice) displays a large overhead for the Scripting section, try increasing this setting. \n"
  },
  {
    "path": "docs/source/reference/modding/settings/fog.rst",
    "content": "Fog Settings\n############\n\nuse distant fog\n---------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting overhauls the behavior of fog calculations.\n\nNormally the fog start and end distance are proportional to the viewing distance\nand use the fog depth set in the fallback settings.\n\nEnabling this setting separates the fog distance from the viewing distance and fallback settings and makes fog distance\nand apparent density dependent on the weather and the current location according to the settings below.\n\nUnfortunately specific weather-dependent fog factor and offset parameters are currently hard-coded.\nThey are based off the default settings of MGE XE.\n\n+--------------+------------+--------+\n| Weather Type | Fog Factor | Offset |\n+==============+============+========+\n| Clear        | 1.0        | 0.0    |\n+--------------+------------+--------+\n| Cloudy       | 0.9        | 0.0    |\n+--------------+------------+--------+\n| Foggy        | 0.2        | 0.3    |\n+--------------+------------+--------+\n| Overcast     | 0.7        | 0.0    |\n+--------------+------------+--------+\n| Rain         | 0.5        | 0.1    |\n+--------------+------------+--------+\n| Thunderstorm | 0.5        | 0.2    |\n+--------------+------------+--------+\n| Ashstorm     | 0.2        | 0.5    |\n+--------------+------------+--------+\n| Blight       | 0.2        | 0.6    |\n+--------------+------------+--------+\n| Snow         | 0.5        | 0.4    |\n+--------------+------------+--------+\n| Blizzard     | 0.16       | 0.7    |\n+--------------+------------+--------+\n\nNon-underwater fog start and end distance are calculated like this according to these parameters::\n\n\tfog start distance = fog factor * (base fog start distance - fog offset * base fog end distance)\n\tfog end distance = fog factor * (1.0 - fog offset) * base fog end distance\n\nUnderwater fog distance is used as-is.\n\nA negative fog start distance means that the fog starts behind the camera\nso the entirety of the scene will be at least partially fogged.\n\nA negative fog end distance means that the fog ends behind the camera\nso the entirety of the scene will be completely submerged in the fog.\n\nFog end distance should be larger than the fog start distance.\n\nThis setting and all further settings can only be configured by editing the settings configuration file.\n\ndistant land fog start\n----------------------\n\n:Type:\t\tfloating point\n:Range:\t\tThe whole range of 32-bit floating point\n:Default:\t16384 (2 cells)\n\nThis is the base fog start distance used for distant fog calculations in exterior locations.\n\ndistant land fog end\n--------------------\n\n:Type:\t\tfloating point\n:Range:\t\tThe whole range of 32-bit floating point\n:Default:\t40960 (5 cells)\n\nThis is the base fog end distance used for distant fog calculations in exterior locations.\n\ndistant underwater fog start\n----------------------------\n\n:Type:\t\tfloating point\n:Range:\t\tThe whole range of 32-bit floating point\n:Default:\t-4096\n\nThis is the base fog start distance used for distant fog calculations in underwater locations.\n\ndistant underwater fog end\n--------------------------\n\n:Type:\t\tfloating point\n:Range:\t\tThe whole range of 32-bit floating point\n:Default:\t2457.6\n\nThis is the base fog end distance used for distant fog calculations in underwater locations.\n\ndistant interior fog start\n--------------------------\n\n:Type:\t\tfloating point\n:Range:\t\tThe whole range of 32-bit floating point\n:Default:\t0\n\nThis is the base fog start distance used for distant fog calculations in interior locations.\n\ndistant interior fog end\n------------------------\n\n:Type:\t\tfloating point\n:Range:\t\tThe whole range of 32-bit floating point\n:Default:\t16384 (2 cells)\n\nThis is the base fog end distance used for distant fog calculations in interior locations.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/game.rst",
    "content": "Game Settings\n#############\n\nshow owned\n----------\n\n:Type:\t\tinteger\n:Range:\t\t0, 1, 2, 3\n:Default:\t0\n\nEnable visual clues for items owned by NPCs when the crosshair is on the object.\nIf the setting is 0, no clues are provided which is the default Morrowind behaviour.\nIf the setting is 1, the background of the tool tip for the object is highlighted\nin the colour specified by the colour background owned setting in the GUI Settings Section.\nIf the setting is 2, the crosshair is the colour of the colour crosshair owned setting in the GUI Settings section.\nIf the setting is 3, both the tool tip background and the crosshair are coloured.\nThe crosshair is not visible if crosshair is false.\n\nThis setting can be configured in Advanced tab of the launcher.\n\nshow projectile damage\n----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this setting is true, the damage bonus of arrows and bolts will show on their tooltip.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nshow melee info\n---------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this setting is true, the reach and speed of weapons will show on their tooltip.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nshow enchant chance\n-------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nWhether or not the chance of success will be displayed in the enchanting menu.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nbest attack\n-----------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this setting is true, the player character will always use the most powerful attack when striking with a weapon\n(chop, slash or thrust). If this setting is false,\nthe type of attack is determined by the direction that the character is moving at the time the attack begins.\n\nThis setting can be toggled with the Always Use Best Attack button in the Prefs panel of the Options menu.\n\ncan loot during death animation\n-------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nIf this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, \nif they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.\n\nIf this setting is false, player has to wait until end of death animation in all cases.\nMakes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder.\nConflicts with mannequin mods, which use SkipAnim to prevent end of death animation.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\ndifficulty\n----------\n\n:Type:\t\tinteger\n:Range:\t\t-500 to 500\n:Default:\t0\n\nThis setting adjusts the difficulty of the game and is intended to be in the range -100 to 100 inclusive.\nGiven the default game setting for fDifficultyMult of 5.0,\na value of -100 results in the player taking 80% of the usual damage, doing 6 times the normal damage.\nA value of 100 results in the player taking 6 times as much damage, while inflicting only 80% of the usual damage.\nValues below -500 will result in the player receiving no damage,\nand values above 500 will result in the player inflicting no damage.\n\nThis setting can be controlled in game with the Difficulty slider in the Prefs panel of the Options menu.\n\nactors processing range\n-----------------------\n\n:Type:\t\tinteger\n:Range:\t\t3584 to 7168\n:Default:\t7168\n\nThis setting specifies the actor state update distance from the player in game units.\nActor state update includes AI, animations, and physics processing.\nActors close to this distance softly fade in and out instead of appearing or disappearing abruptly.\nKeep in mind that actors running Travel AI packages are always active to avoid\nissues in mods with long-range AiTravel packages (for example, patrols, caravans and travellers).\n\nThis setting can be controlled in game with the \"Actors Processing Range\" slider in the Prefs panel of the Options menu.\n\nclassic reflected absorb spells behavior\n----------------------------------------\n\n:Type:\t\tboolean\n:Range: \tTrue/False\n:Default:\tTrue\n\nIf this setting is true, effects of Absorb spells which were reflected by the target are not mirrored,\nand the caster will absorb their own stat resulting in no effect on either the caster and the target.\nThis makes the gameplay as a mage easier, but these spells become imbalanced.\nThis is how Morrowind behaves.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nuse magic item animations\n-------------------------\n\n:Type:\t\tboolean\n:Range: \tTrue/False\n:Default:\tFalse\n\nIf this setting is true, the engine will use casting animations for magic items, including scrolls.\nOtherwise, there will be no casting animations, just as in original engine\n\nThis setting can only be configured by editing the settings configuration file.\n\nshow effect duration\n--------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nShow the remaining duration of magic effects and lights if this setting is true.\nThe remaining duration is displayed in the tooltip by hovering over the magical effect.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nenchanted weapons are magical\n-----------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nMake enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have.\nThis is how Morrowind behaves.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nprevent merchant equipping\n--------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nPrevent merchants from equipping items that are sold to them.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nfollowers attack on sight\n-------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nMake player followers and escorters start combat with enemies who have started combat with them or the player.\nOtherwise they wait for the enemies or the player to do an attack first.\nPlease note this setting has not been extensively tested and could have side effects with certain quests.\nThis setting can be toggled in Advanced tab of the launcher.\n\nshield sheathing\n----------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this setting is true, OpenMW will utilize shield sheathing-compatible assets to display holstered shields.\n\nTo make use of this, you need to have an xbase_anim_sh.nif file with weapon bones that will be injected into the skeleton.\nAlso you can use additional _sh meshes for more precise shield placement.\nWarning: this feature may conflict with mods that use pseudo-shields to emulate item in actor's hand (e.g. books, baskets, pick axes).\nTo avoid conflicts, you can use _sh mesh without \"Bip01 Sheath\" node for such \"shields\" meshes, or declare its bodypart as Clothing type, not as Armor.\nAlso you can use an _sh node with empty \"Bip01 Sheath\" node.\nIn this case the engine will use basic shield model, but will use transformations from the \"Bip01 Sheath\" node.\n\nweapon sheathing\n----------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this setting is true, OpenMW will utilize weapon sheathing-compatible assets to display holstered weapons.\n\nTo make use of this, you need to have an xbase_anim_sh.nif file with weapon bones that will be injected into the skeleton.\nAdditional _sh suffix models are not essential for weapon sheathing to work but will act as quivers or scabbards for the weapons they correspond to.\n\nuse additional anim sources\n---------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAllow the engine to load additional animation sources when enabled.\nFor example, if the main animation mesh has name Meshes/x.nif, \nthe engine will load all KF-files from Animations/x folder and its child folders.\nThis can be useful if you want to use several animation replacers without merging them.\nAttention: animations from AnimKit have their own format and are not supposed to be directly loaded in-game!\nThis setting can only be configured by editing the settings configuration file.\n\nbarter disposition change is permanent\n--------------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this setting is true, \ndisposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them.\nThis imitates the option that Morrowind Code Patch offers.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nonly appropriate ammunition bypasses resistance\n-----------------------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this setting is true, you will have to use the appropriate ammunition to bypass normal weapon resistance (or weakness).\nAn enchanted bow with chitin arrows will no longer be enough for the purpose, while a steel longbow with glass arrows will still work.\nThis was previously the default engine behavior that diverged from Morrowind design.\n\nThis setting can be toggled in Advanced tab of the launcher.\n\nstrength influences hand to hand\n--------------------------------\n\n:Type:\t\tinteger\n:Range:\t\t0, 1, 2\n:Default:\t0\n\nThis setting controls the behavior of factoring of Strength attribute into hand-to-hand damage, which is using the formula\nMorrowind Code Patch uses for its equivalent feature: damage is multiplied by its value divided by 40.\n\n0: Strength attribute is ignored\n1: Strength attribute is factored in damage from any actor\n2: Strength attribute is factored in damage from any actor except werewolves\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nnormalise race speed\n--------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nBy default race weight is factored into horizontal movement speed like in Morrowind.\nFor example, an NPC which has 1.2 race weight is faster than an NPC with the exact same stats and weight 1.0 by a factor of 120%.\nIf this setting is true, race weight is ignored in the calculations which allows for a movement behavior\nequivalent to the one introduced by the equivalent Morrowind Code Patch feature.\nThis makes the movement speed behavior more fair between different races.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nprojectiles enchant multiplier\n------------------------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 to 1.0\n:Default:\t0.0\n\nThe value of this setting determines how many projectiles (thrown weapons, arrows and bolts) you can enchant at once according to the following formula:\n\ncount = (soul gem charge * projectiles enchant multiplier) / enchantment strength\n\nA value of 0 means that you can only enchant one projectile.\nIf you want to have Morrowind Code Patch-like count of projectiles being enchanted at once, set this value to 0.25 (i.e. 25% of the charge).\n\nThis setting can only be configured by editing the settings configuration file.\n\nuncapped damage fatigue\n-----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThere are four ways to decrease an actor's Fatigue stat in Morrowind gameplay mechanics:\nDrain, Absorb, Damage Fatigue magic effects and hand-to-hand combat.\nHowever, in Morrowind you can't knock down an actor with a Damage Fatigue spell or an Absorb Fatigue spell.\nMorrowind Code Patch adds an option to make it possible for Damage Fatigue spells. This is the equivalent of that option.\n\nSetting the value of this setting to true will remove the 0 lower cap from the value,\nallowing Damage Fatigue to reduce Fatigue to a value below zero.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nturn to movement direction\n--------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAffects side and diagonal movement. Enabling this setting makes movement more realistic.\n\nIf disabled then the whole character's body is pointed to the direction of view. Diagonal movement has no special animation and causes sliding.\n\nIf enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it changes straight right and straight left movement as well. Also turns the whole body up or down when swimming according to the movement direction.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nsmooth movement\n---------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nMakes NPCs and player movement more smooth.\n\nRecommended to use with \"turn to movement direction\" enabled.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nsmooth movement player turning delay\n------------------------------------\n\n:Type:\t\tfloating point\n:Range:\t\t>= 0.01\n:Default:\t0.333\n\nMax delay of turning (in seconds) if player drastically changes direction on the run. Makes sense only if \"smooth movement\" is enabled.\n\nThis setting can only be configured by editing the settings configuration file.\n\nNPCs avoid collisions\n---------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf enabled NPCs apply evasion maneuver to avoid collisions with others.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nNPCs give way\n-------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nStanding NPCs give way to moving ones. Works only if 'NPCs avoid collisions' is enabled.\n\nThis setting can only be configured by editing the settings configuration file.\n\nswim upward correction\n----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nMakes player swim a bit upward from the line of sight. Applies only in third person mode. Intended to make simpler swimming without diving.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nswim upward coef\n----------------\n\n:Type:\t\tfloating point\n:Range:\t\t-1.0 to 1.0\n:Default:\t0.2\n\nRegulates strength of the \"swim upward correction\" effect (if enabled).\nMakes player swim a bit upward (or downward in case of negative value) from the line of sight. Recommened range of values is from 0.0 to 0.25.\n\nThis setting can only be configured by editing the settings configuration file.\n\ntrainers training skills based on base skill\n--------------------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThe trainers in Morrowind choose their proposed training skills based on their 3 best attributes.\n\nIf disabled then the 3 best skills of trainers and the training limits take into account fortified/drained trainer skill.\n\nIf enabled then the 3 best skills of trainers and the training limits are based on the trainer base skills.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\nalways allow stealing from knocked out actors\n---------------------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nBy Bethesda's design, in the latest released version of Morrowind pickpocketing is impossible during combat,\neven if the fighting NPC is knocked out.\n\nThis setting allows the player to steal items from fighting NPCs that were knocked out if enabled.\n\nThis setting can be controlled in Advanced tab of the launcher.\n\ngraphic herbalism\n-----------------\n\n:Type:      boolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nSome mods add harvestable container models. When this setting is enabled, activating a container using a harvestable model will visually harvest from it instead of opening the menu.\n\nWhen this setting is turned off or when activating a regular container, the menu will open as usual.\n\nallow actors to follow over water surface\n---------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nIf enabled actors will always find path over the water surface when following other actors. This makes OpenMW behaviour closer to the vanilla engine.\n\nIf disabled actors without the ability to swim will not follow other actors to the water.\n\n.. note::\n    Has effect only when Navigator is enabled.\n\nThis setting can be controlled in Advanced tab of the launcher.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/general.rst",
    "content": "General Settings\n################\n\nanisotropy\n----------\n\n:Type:\t\tinteger\n:Range:\t\t0 to 16\n:Default:\t4\n\nSet the maximum anisotropic filtering on textures.\nAnisotropic filtering is a method of enhancing the image quality of textures\non surfaces that are at oblique viewing angles with respect to the camera. Valid values range from 0 to 16.\nModern video cards can often perform 8 or 16 anisotropic filtering with a minimal performance impact.\nThis effect of this setting can be seen in the Video panel of the Options menu by finding a location with straight lines\n(striped rugs and Balmora cobblestones work well) radiating into the distance, and adjusting the anisotropy slider.\n\nThis setting can be changed in game\nusing the Anisotropy slider in the Detail tab of the Video panel of the Options menu.\n\nscreenshot format\n-----------------\n\n:Type:\t\tstring\n:Range:\t\tjpg, png, tga\n:Default:\tpng\n\nSpecify the format for screen shots taken by pressing the screen shot key (bound to F12 by default).\nThis setting should be the file extension commonly associated with the desired format.\nThe formats supported will be determined at compilation, but \"jpg\", \"png\", and \"tga\" should be allowed.\n\nThis setting can be configured in Advanced tab of the launcher.\n\ntexture mag filter\n------------------\n\n:Type:\t\tstring\n:Range:\t\tnearest, linear\n:Default:\tlinear\n\nSet the texture magnification filter type.\n\ntexture min filter\n------------------\n\n:Type:\t\tstring\n:Range:\t\tnearest, linear\n:Default:\tlinear\n\nSet the texture minification filter type.\n\ntexture mipmap\n--------------\n\n:Type:\t\tstring\n:Range:\t\tnone, nearest, linear\n:Default:\tnearest\n\nSet the texture mipmap type to control the method mipmaps are created.\nMipmapping is a way of reducing the processing power needed during minification\nby pregenerating a series of smaller textures.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/groundcover.rst",
    "content": "Groundcover Settings\n####################\n\nenabled\n-------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAllows the engine to use groundcover.\nGroundcover objects are static objects which come from ESP files, registered via\n\"groundcover\" entries from openmw.cfg rather than \"content\" ones.\nWe assume that groundcover objects have no collisions, can not be moved or interacted with,\nso we can merge them to pages and animate them indifferently from distance from player.\n\nThis setting can only be configured by editing the settings configuration file.\n\ndensity\n-------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 (0%) to 1.0 (100%)\n:Default:\t1.0\n\nDetermines how many groundcover instances from content files\nare used in the game. Can affect performance a lot.\n\nThis setting can only be configured by editing the settings configuration file.\n\nrendering distance\n------------------\n\n:Type:\t\tfloating point\n:Range:\t\t>= 0.0\n:Default:\t6144.0\n\nDetermines on which distance in game units grass pages are rendered.\nMay affect performance a lot.\n\nThis setting can only be configured by editing the settings configuration file.\n\nmin chunk size\n--------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.125, 0.25, 0.5, 1.0\n:Default:\t0.5\n\nDetermines a minimum size of groundcover chunks in cells. For example, with 0.5 value\nchunks near player will have size 4096x4096 game units. Larger chunks reduce CPU usage\n(Draw and Cull bars), but can increase GPU usage (GPU bar) since culling becomes less efficient.\nSmaller values do an opposite.\n\nThis setting can only be configured by editing the settings configuration file.\n\nstomp mode\n----------\n\n:Type:\t\tinteger\n:Range:\t\t0, 1, 2\n:Default:\t2\n\nDetermines whether grass should respond to the player treading on it.\n\n.. list-table:: Modes\n\t:header-rows: 1\n\t* - Mode number\n\t  - Meaning\n\t* - 0\n\t  - Grass cannot be trampled.\n\t* - 1\n\t  - The player's XY position is taken into account.\n\t* - 2\n\t  - The player's height above the ground is taken into account, too.\n\nIn MGE XE, which existing grass mods were made for, only the player's XY position was taken into account.\nHowever, presumably due to a bug, jumping straight up would change the XY position, so grass *does* respond to the player jumping.\nLevitating above grass in MGE XE still considers it stood-on, which can look bad.\nOpenMW's height-aware system ensures grass does not act as if it's being stood on when the player levitates above it, but that means grass rapidly snaps back to its original position when the player jumps out of it.\nTherefore, it is not recommended to use MGE XE's intensity constants if this setting is set to 2, i.e. :ref:`stomp intensity` should be 0 or 1 when :ref:`stomp mode` is 2.\n\nstomp intensity\n---------------\n\n:Type:\t\tinteger\n:Range:\t\t0, 1, 2\n:Default:\t1\n\nHow far away from the player grass can be before it's unaffected by being trod on, and how far it moves when it is.\n\n.. list-table:: Presets\n\t:header-rows: 1\n\t* - Preset number\n\t  - Range (Units)\n\t  - Distance (Units)\n\t  - Description\n\t* - 2\n\t  - 150\n\t  - 60\n\t  - MGE XE levels. Generally excessive/comical, but what existing mods were made with in mind.\n\t* - 1\n\t  - 80\n\t  - 40\n\t  - Reduced levels. Usually looks better.\n\t* - 0\n\t  - 50\n\t  - 20\n\t  - Gentle levels.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/index.rst",
    "content": "###############################\nAdvanced Settings Configuration\n###############################\n\nThis part of the guide will cover how to make modifications to the more arcane settings in OpenMW,\nmost of which are not available from in-game menus, to optimize or customize your OpenMW experience.\nIf you are familiar with ``.ini`` tweaks in Morrowind or the other games, this will be quite similar.\nAll settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory.\nSee :doc:`../paths` for this location.\n\nWhen creating a new game based on the OpenMW engine, it may be desirable to change the default settings - the defaults are chosen for compatibility with Morrowind.\nThis can be done by editing ``defaults.bin`` in the OpenMW installation directory without rebuilding OpenMW itself.\nIf you're using a custom fork of OpenMW, ``files/settings-default.cfg`` in the source repository should be edited instead.\nTo edit ``defaults.bin``, base64 decode it, make any changes, and then base64 encode it again.\n\nIf you feel a need to edit the default settings for any other reason than when creating a new OpenMW-based game, you should not.\nWe may be able to accommodate your desired workflow some other way if you make a feature request.\n\nChanging Settings\n#################\n\n#.\tOnce you have located your ``settings.cfg`` file, open it in a plain text editor.\n#.\tFind the setting(s) you wish to change in the following pages.\n#.\tIf the setting is not already in ``settings.cfg``,\n\tadd it by copy and pasting the name exactly as written in this guide.\n#.\tSet the value of the setting by typing ``= <value>`` after the setting on the same line,\n\tusing an appropriate value in place of ``<value>``.\n#.\tIf this is the first setting from it's category that you're adding,\n\tbe sure to add the heading in square brackets ``[]`` above it using just the setting type,\n\ti.e. without the word \"Settings\".\n\n\tFor example, to delay tooltips popping up by 1 second, add the line ``tooltip delay = 1.0``.\n\tThen to the line above, type ``[GUI]``, as the tooltip delay setting comes from the \"GUI Settings\" section.\n\nAlthough this guide attempts to be comprehensive and up to date,\nyou will always be able to find the full list of settings available and their default values in ``settings-default.cfg``,\navailable in the ``files`` directory of our source repo, or by base64 decoding ``defaults.bin`` in your main OpenMW installation directory.\nThis has changed compared to previous versions of OpenMW as more users were confused by the existence of a file they weren't supposed to edit\nthan were helped by the existence of a file listing settings they could edit in a different file.\nThe ranges included with each setting are the physically possible ranges, not recommendations.\n\n.. warning::\n\tAs the title suggests, these are advanced settings.\n\tIf digging around plain text files and manually editing settings sounds scary to you,\n\tyou may want to steer clear of altering these files. That being said,\n\tthis guide should be plenty clear enough that you can find the setting you want to change and safely edit it.\n\n.. toctree::\n\t:caption: Table of Contents\n\t:maxdepth: 2\n\n\tcamera\n\tcells\n\tfog\n\tgroundcover\n\tmap\n\tGUI\n\tHUD\n\tgame\n\tgeneral\n\tshaders\n\tshadows\n\tinput\n\tsaves\n\tsound\n\tterrain\n\tvideo\n\twater\n\twindows\n\tnavigator\n\tphysics\n\tmodels\n"
  },
  {
    "path": "docs/source/reference/modding/settings/input.rst",
    "content": "Input Settings\n##############\n\ngrab cursor\n-----------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nOpenMW will capture control of the cursor if this setting is true.\n\nIn \"look mode\", OpenMW will center the cursor regardless of the value of this setting\n(since the cursor/crosshair is always centered in the OpenMW window).\nHowever, in GUI mode, this setting determines the behavior when the cursor is moved outside the OpenMW window.\nIf true, the cursor movement stops at the edge of the window preventing access to other applications.\nIf false, the cursor is allowed to move freely on the desktop.\n\nThis setting does not apply to the screen where escape has been pressed, where the cursor is never captured.\nRegardless of this setting \"Alt-Tab\" or some other operating system dependent key sequence can be used\nto allow the operating system to regain control of the mouse cursor.\nThis setting interacts with the minimize on focus loss setting by affecting what counts as a focus loss.\nSpecifically on a two-screen configuration it may be more convenient to access the second screen with setting disabled.\n\nNote for developers: it's desirable to have this setting disabled when running the game in a debugger,\nto prevent the mouse cursor from becoming unusable when the game pauses on a breakpoint.\n\nThis setting can only be configured by editing the settings configuration file.\n\ntoggle sneak\n------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting causes the behavior of the sneak key (bound to Ctrl by default)\nto toggle sneaking on and off rather than requiring the key to be held down while sneaking.\nPlayers that spend significant time sneaking may find the character easier to control with this option enabled.\n\nThis setting can be toggled in the launcher under \"Advanced\" -> \"Game Mechanics\" -> \"Toggle sneak\".\n\nalways run\n----------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this setting is true, the character is running by default, otherwise the character is walking by default.\nThe shift key will temporarily invert this setting, and the caps lock key will invert this setting while it's \"locked\".\nThis setting is updated every time you exit the game,\nbased on whether the caps lock key was on or off at the time you exited.\n\nThis settings can be toggled in game by pressing the CapsLock key and exiting.\n\ncamera sensitivity\n------------------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0\n:Default:\t1.0\n\nThis setting controls the overall camera/mouse sensitivity when not in GUI mode.\nThe default sensitivity is 1.0, with smaller values requiring more mouse movement,\nand larger values requiring less.\nThis setting does not affect mouse speed in GUI mode,\nwhich is instead controlled by your operating system mouse speed setting.\n\nThis setting can be changed with the Camera Sensitivity slider in the Controls panel of the Options menu.\n\ncamera y multiplier\n-------------------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0\n:Default:\t1.0\n\nThis setting controls the vertical camera/mouse sensitivity relative to the horizontal sensitivity\n(see camera sensitivity above). It is multiplicative with the previous setting,\nmeaning that it should remain set at 1.0 unless the player desires to have different sensitivities in the two axes.\n\nThis setting can only be configured by editing the settings configuration file.\n\ninvert x axis\n-------------\n\n:Type:      boolean\n:Range:     True/False\n:Default:   False\n\n\nInvert the horizontal axis while not in GUI mode.\nIf this setting is true, moving the mouse to the left will cause the view to rotate counter-clockwise,\nwhile moving it to the right will cause the view to rotate clockwise. This setting does not affect cursor movement in GUI mode.\n\nThis setting can be toggled in game with the Invert X Axis button in the Controls panel of the Options menu.\n\ninvert y axis\n-------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nInvert the vertical axis while not in GUI mode.\nIf this setting is true, moving the mouse away from the player will look down,\nwhile moving it towards the player will look up. This setting does not affect cursor movement in GUI mode.\n\nThis setting can be toggled in game with the Invert Y Axis button in the Controls panel of the Options menu.\n\nenable controller\n-----------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nEnable support of controller input — or rather not ignore controller events,\nwhich are always sent if a controller is present and detected.\nDisabling this setting can be useful for working around controller-related issues or for setting up split-screen gameplay configurations.\n\nThis setting can be toggled in game with the Enable Joystick button in the Controls panel of the Options menu.\n\ngamepad cursor speed\n--------------------\n\n:Type: float\n:Range: >0\n:Default: 1.0\n\nThis setting controls the speed of the cursor within GUI mode when using the joystick.\nThis setting has no effect on the camera rotation speed, which is controlled by the\ncamera sensitivity setting.\n\nThis setting can only be configured by editing the settings configuration file.\n\njoystick dead zone\n------------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 to 0.5\n:Default:\t0.1\n\nThis setting controls the radius of dead zone (where an input is discarded) for joystick axes.\nNote that third-party software can provide its own dead zones. In this case OpenmW-specific setting dead zone can be disabled (0.0).\n\nThis setting can only be configured by editing the settings configuration file.\n\nenable gyroscope\n----------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nEnable the support of camera rotation based on the information supplied from the gyroscope through SDL.\n\nThis setting can only be configured by editing the settings configuration file.\n\ngyro horizontal axis\n--------------------\n\n:Type:      string\n:Range:     x, y, z, -x, -y, -z\n:Default:   -x\n\nThis setting sets up an axis of the gyroscope as the horizontal camera axis.\nMinus sign swaps the positive and negative direction of the axis.\nKeep in mind that while this setting corresponds to the landscape mode of the display,\nthe portrait mode or any other mode will have this axis corrected automatically.\n\nThis setting can only be configured by editing the settings configuration file.\n\ngyro vertical axis\n------------------\n\n:Type:      string\n:Range:     x, y, z, -x, -y, -z\n:Default:   y\n\nThis setting sets up an axis of the gyroscope as the vertical camera axis.\nMinus sign swaps the positive and negative direction of the axis.\nKeep in mind that while this setting corresponds to the landscape mode of the display,\nthe portrait mode or any other mode will have this axis corrected automatically.\n\nThis setting can only be configured by editing the settings configuration file.\n\ngyro input threshold\n--------------------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0\n:Default:\t0.01\n\nThis setting determines the minimum value of the rotation that will be accepted.\nIt allows to avoid crosshair oscillation due to gyroscope \"noise\".\n\nThis setting can only be configured by editing the settings configuration file.\n\ngyro horizontal sensitivity\n---------------------------\n\n:Type: float\n:Range: >0\n:Default: 1.0\n\nThis setting controls the overall gyroscope horizontal sensitivity.\nThe smaller this sensitivity is, the less visible effect the device rotation\nwill have on the horizontal camera rotation, and vice versa.\n\nThis setting can only be configured by editing the settings configuration file.\n\ngyro vertical sensitivity\n-------------------------\n\n:Type: float\n:Range: >0\n:Default: 1.0\n\nThis setting controls the overall gyroscope vertical sensitivity.\nThe smaller this sensitivity is, the less visible effect the device\nrotation will have on the vertical camera rotation, and vice versa.\n\nThis setting can only be configured by editing the settings configuration file.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/map.rst",
    "content": "Map Settings\n############\n\nglobal\n------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this value is true, the map window will display the world map, otherwise the local map. \nThe setting updates automatically when pressing the local/world map switch button on the map window.\n\nglobal map cell size\n--------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 1\n:Default:\t18\n\nThis setting adjusts the scale of the world map in the GUI mode map window.\nThe value is the width in pixels of each cell in the map, so larger values result in larger more detailed world maps,\nwhile smaller values result in smaller less detailed world maps.\nHowever, the native resolution of the map source material appears to be 9 pixels per unexplored cell\nand approximately 18 pixels per explored cell, so values larger than 36 don't produce much additional detail.\nSimilarly, the size of place markers is currently fixed at 12 pixels,\nso values smaller than this result in overlapping place markers.\nValues from 12 to 36 are recommended. For reference, Vvardenfell is approximately 41x36 cells.\n\n.. Warning::\n\tChanging this setting affects saved games. The currently explored area is stored as an image\n\tin the save file that's overlayed on the default world map in game.\n\tWhen you increase the resolution of the map, the overlay of earlier saved games will be scaled up on load,\n\tand appear blurry. When you visit the cell again, the overlay for that cell is regenerated at the new resolution,\n\tso the blurry areas can be corrected by revisiting all the cells you've already visited.\n\nThis setting can not be configured except by editing the settings configuration file.\n\nlocal map hud widget size\n-------------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 1\n:Default:\t256\n\nThis setting controls the zoom level for the HUD map widget (the map in the lower right corner of the window).\nA value of 64 results in the HUD map widget displaying one entire exterior cell.\nSince the GUI mode map displays 3x3 cells, a value of approximately 21 displays the same area as the GUI mode map.\nLarger values increase the level of zoom,\nwhile smaller values are wasteful since there's no map data to display beyond the 3x3 cell grid.\n\nNote that the actual size of the widget is always the same on the screen\nunless the scaling factor setting in the \"GUI\" section is changed.\nIncreasing both the scaling factor of the GUI and this setting does result in a higher resolution HUD map,\nunfortunately with a scaled direction pointer on top of it.\n\nThis setting can not be configured except by editing the settings configuration file.\n\nlocal map hud fog of war\n------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting enables fog of war rendering on the HUD map.\nDefault is Off since with default settings the map is so small that the fog would not obscure anything,\njust darken the edges slightly.\n\nlocal map resolution\n--------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 1\n:Default:\t256\n\nThis setting controls the resolution of the GUI mode local map window.\nLarger values generally increase the visible detail in map.\nIf this setting is half the local map widget size or smaller, the map will generally be be fairly blurry.\nSetting both options to the same value results in a map with good detail.\nValues that exceed the local map widget size setting by more than a factor of two\nare unlikely to provide much of an improvement in detail since they're subsequently scaled back\nto the approximately the map widget size before display.\nThe video resolution settings interacts with this setting in that regard.\n\n.. warning::\n\tIncreasing this setting can increase cell load times,\n\tbecause the map is rendered on demand each time you enter a new cell.\n\tLarge values may exceed video card limits or exhaust VRAM.\n\nThis setting can not be configured except by editing the settings configuration file.\n\nlocal map widget size\n---------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 1\n:Default:\t512\n\nThis setting controls the canvas size of the GUI mode local map window.\nLarger values result in a larger physical map size on screen,\nand typically require more panning to see all available portions of the map.\nThis larger size also enables an overall greater level of detail if the local map resolution setting is also increased.\n\nThis setting can not be configured except by editing the settings configuration file.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/models.rst",
    "content": "Models Settings\n###############\n\nload unsupported nif files\n--------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAllow the engine to load arbitrary NIF files as long as they appear to be valid.\n\nOpenMW has limited and **experimental** support for NIF files\nthat Morrowind itself cannot load, which normally goes unused.\n\nIf enabled, this setting allows the NIF loader to make use of that functionality.\n\n.. warning::\n\tYou must keep in mind that since the mentioned support is experimental,\n\tloading unsupported NIF files may fail, and the degree of this failure may vary.\n\t\n\tIn milder cases, OpenMW will reject the file anyway because\n\tit lacks a definition for a certain record type that the file may use.\n\t\n\tIn more severe cases OpenMW's incomplete understanding of a record type\n\tcan lead to memory corruption, freezes or even crashes.\n\t\n\t**Do not enable** this if you're not so sure that you know what you're doing.\n\nTo help debug possible issues OpenMW will log its progress in loading\nevery file that uses an unsupported NIF version.\n\nxbaseanim\n---------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/xbase_anim.nif\n\nPath to the file used for 3rd person base animation model that looks also for the corresponding kf-file.\n\n.. note::\n\tIf you are using the COLLADA format, you don't need to separate the files as they are separated between .nif and .kf files. It works if you plug the same COLLADA file into all animation-related entries, just make sure there is a corresponding textkeys file. You can read more about the textkeys in :doc:`../../modding/custom-models/pipeline-blender-collada`.\n\nbaseanim\n--------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/base_anim.nif\n\nPath to the file used for 3rd person base model with textkeys-data.\n\nxbaseanim1st\n------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/xbase_anim.1st.nif\n\nPath to the file used for 1st person base animation model that looks also for corresponding kf-file.\n\nbaseanimkna\n-----------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/base_animkna.nif\n\nPath to the file used for 3rd person beast race base model with textkeys-data.\n\nbaseanimkna1st\n--------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/base_animkna.1st.nif\n\nPath to the file used for 1st person beast race base animation model.\n\nxbaseanimfemale\n---------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/xbase_anim_female.nif\n\nPath to the file used for 3rd person female base animation model.\n\nbaseanimfemale\n--------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/base_anim_female.nif\n\nPath to the file used for 3rd person female base model with textkeys-data.\n\nbaseanimfemale1st\n-----------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/base_anim_female.1st.nif\n\nPath to the file used for 1st person female base model with textkeys-data.\n\nwolfskin\n--------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/wolf/skin.nif\n\nPath to the file used for 3rd person werewolf skin.\n\nwolfskin1st\n-----------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/wolf/skin.1st.nif\n\nPath to the file used for 1st person werewolf skin.\n\nxargonianswimkna\n----------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/xargonian_swimkna.nif\n\nPath to the file used for Argonian swimkna.\n\nxbaseanimkf\n-----------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/xbase_anim.kf\n\nFile to load xbaseanim 3rd person animations.\n\nxbaseanim1stkf\n--------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/xbase_anim.1st.kf\n\nFile to load xbaseanim 3rd person animations.\n\nxbaseanimfemalekf\n-----------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/xbase_anim_female.kf\n\nFile to load xbaseanim animations from.\n\nxargonianswimknakf\n------------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/xargonian_swimkna.kf\n\nFile to load xargonianswimkna animations from.\n\nskyatmosphere\n-------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/sky_atmosphere.nif\n\nPath to the file used for the sky atmosphere mesh, which is one of the three meshes needed to render the sky. It's used to make the top half of the sky blue and renders in front of the background color.\n\nskyclouds\n---------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/sky_clouds_01.nif.\n\nPath to the file used for the sky clouds mesh, which is one of the three meshes needed to render the sky. It displays a scrolling texture of clouds in front of the atmosphere mesh and background color\n\nskynight01\n----------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/sky_night_01.nif\n\nPath to the file used for the sky stars mesh, which is one of the three meshes needed to render the sky. During night, it displays a texture with stars in front of the atmosphere and behind the clouds. If skynight02 is present, skynight01 will not be used.\n\nskynight02\n----------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/sky_night_02.nif\n\nPath to the file used for the sky stars mesh, which is one of the three meshes needed to render the sky. During night, it displays a texture with stars in front of the atmosphere and behind the clouds. If it's present it will be used instead of skynight01.\n\nweatherashcloud\n---------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/ashcloud.nif\n\nPath to the file used for the ash clouds weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect.\n\nweatherblightcloud\n------------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/blightcloud.nif\n\nPath to the file used for the blight clouds weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect.\n\nweathersnow\n-----------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/snow.nif\n\nPath to the file used for the snow falling weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect.\n\nweatherblizzard\n---------------\n\n:Type:\t\tstring\n:Range:\t\t\n:Default:\tmeshes/blizzard.nif\n\nPath to the file used for the blizzard clouds weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/navigator.rst",
    "content": "Navigator Settings\n################\n\nMain settings\n*************\n\nThis section is for players.\n\nenable\n------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nEnable navigator.\nWhen enabled background threads are started to build nav mesh for world geometry.\nPathfinding system uses nav mesh to build paths.\nWhen disabled only pathgrid is used to build paths.\nSingle-core CPU systems may have big performance impact on exiting interior location and moving across exterior world.\nMay slightly affect performance on multi-core CPU systems.\nMulti-core CPU systems may have different latency for nav mesh update depending on other settings and system performance.\nMoving across external world, entering/exiting location produce nav mesh update.\nNPC and creatures may not be able to find path before nav mesh is built around them.\nTry to disable this if you want to have old fashioned AI which doesn't know where to go when you stand behind that stone and casting a firebolt.\n\nmax tiles number\n----------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t512\n\nNumber of tiles at nav mesh.\nNav mesh covers circle area around player.\nThis option allows to set an explicit limit for nav mesh size, how many tiles should fit into circle.\nIf actor is inside this area it able to find path over nav mesh.\nIncreasing this value may decrease performance.\n\n.. note::\n    Don't expect infinite nav mesh size increasing.\n    This condition is always true: ``max tiles number * max polygons per tile <= 4194304``.\n    It's a limitation of `Recastnavigation <https://github.com/recastnavigation/recastnavigation>`_ library.\n\nwait until min distance to player\n------------------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t5\n\nDistance in navmesh tiles around the player to keep loading screen until navigation mesh is generated.\nAllows to complete cell loading only when minimal navigation mesh area is generated to correctly find path for actors\nnearby the player. Increasing this value will keep loading screen longer but will slightly increase nav mesh generation\nspeed on systems bound by CPU. Zero means no waiting.\n\nAdvanced settings\n*****************\n\nThis section is for advanced PC uses who understands concepts of OS thread and memory.\n\nasync nav mesh updater threads\n------------------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 1\n:Default:\t1\n\nNumber of background threads to update nav mesh.\nIncreasing this value may decrease performance, but also may decrease or increase nav mesh update latency depending on number of CPU cores.\nOn systems with not less than 4 CPU cores latency dependens approximately like 1/log(n) from number of threads.\nDon't expect twice better latency by doubling this value.\n\nmax nav mesh tiles cache size\n-----------------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t268435456\n\nMaximum total cached size of all nav mesh tiles in bytes.\nSetting greater than zero value will reduce nav mesh update latency for previously visited locations.\nIncreasing this value may increase total memory consumption, but potentially will reduce latency for recently visited locations.\nLimit this value by total available physical memory minus base game memory consumption and other applications.\nGame will not eat all memory at once.\nMemory will be consumed in approximately linear dependency from number of nav mesh updates.\nBut only for new locations or already dropped from cache.\n\nmin update interval ms\n----------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t250\n\nMinimum time duration required to pass before next navmesh update for the same tile in milliseconds.\nOnly tiles affected where objects are transformed.\nNext update for tile with added or removed object will not be delayed.\nVisible ingame effect is navmesh update around opening or closing door.\nPrimary usage is for rotating signs like in Seyda Neen at Arrille's Tradehouse entrance.\nDecreasing this value may increase CPU usage by background threads.\n\nDeveloper's settings\n********************\n\nThis section is for developers or anyone who wants to investigate how nav mesh system works in OpenMW.\n\nenable write recast mesh to file\n--------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nWrite recast mesh to file in .obj format for each use to update nav mesh.\nOption is used to find out what world geometry is used to build nav mesh.\nPotentially decreases performance.\n\nenable write nav mesh to file\n-----------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nWrite nav mesh to file to be able to open by RecastDemo application.\nUsually it is more usefull to have both enable write recast mesh to file and this options enabled.\nRecastDemo supports .obj files.\nPotentially decreases performance.\n\nenable recast mesh file name revision\n-------------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nWrite each recast mesh file with revision in name.\nOtherwise will rewrite same file.\nIf it is unclear when geometry is changed use this option to dump multiple files for each state.\n\nenable nav mesh file name revision\n----------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nWrite each nav mesh file with revision in name.\nOtherwise will rewrite same file.\nIf it is unclear when nav mesh is changed use this option to dump multiple files for each state.\n\nrecast mesh path prefix\n-----------------------\n\n:Type:\t\tstring\n:Range:\t\tfile system path\n:Default:\t\"\"\n\nWrite recast mesh file at path with this prefix.\n\nnav mesh path prefix\n--------------------\n\n:Type:\t\tstring\n:Range:\t\tfile system path\n:Default:\t\"\"\n\nWrite nav mesh file at path with this prefix.\n\nenable nav mesh render\n----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nRender nav mesh.\nAllows to do in-game debug.\nEvery nav mesh is visible and every update is noticable.\nPotentially decreases performance.\n\nenable agents paths render\n-------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nRender agents paths.\nMake visible all NPC's and creaure's plans where they are going.\nWorks even if Navigator is disabled.\nPotentially decreases performance.\n\nenable recast mesh render\n----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nRender recast mesh that is built as set of culled tiles from physical mesh.\nShould show similar mesh to physical one.\nLittle difference can be a result of floating point error.\nAbsent pieces usually mean a bug in recast mesh tiles building.\nAllows to do in-game debug.\nPotentially decreases performance.\n\nExpert settings\n***************\n\nThis section is for developers who wants to go deeper into Detournavigator component logic.\n\nrecast scale factor\n-------------------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0.0\n:Default:\t0.029411764705882353\n\nScale of nav mesh coordinates to world coordinates. Recastnavigation builds voxels for world geometry.\nBasically voxel size is 1 / \"cell size\". To reduce amount of voxels we apply scale factor, to make voxel size\n\"recast scale factor\" / \"cell size\". Default value calculates by this equation:\nsStepSizeUp * \"recast scale factor\" / \"cell size\" = 5 (max climb height should be equal to 4 voxels).\nChanging this value will change generated nav mesh. Some locations may become unavailable for NPC and creatures.\nPay attention to slopes and roofs when change it. Increasing this value will reduce nav mesh update latency.\n\nmax polygon path size\n---------------------\n\n:Type:\t\tinteger\n:Range:\t\t> 0\n:Default:\t1024\n\nMaximum size of path over polygons.\n\nmax smooth path size\n--------------------\n\n:Type:\t\tinteger\n:Range:\t\t> 0\n:Default:\t1024\n\nMaximum size of smoothed path.\n\nExpert Recastnavigation related settings\n****************************************\n\nThis section is for OpenMW developers who knows about `Recastnavigation <https://github.com/recastnavigation/recastnavigation>`_ library and understands how it works.\n\ncell height\n-----------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0.0\n:Default:\t0.2\n\nThe z-axis cell size to use for fields.\nDefines voxel/grid/cell size. So their values have significant\nside effects on all parameters defined in voxel units.\nThe minimum value for this parameter depends on the platform's floating point\naccuracy, with the practical minimum usually around 0.05.\nSame default value is used in RecastDemo.\n\ncell size\n---------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0.0\n:Default:\t0.2\n\nThe xy-plane cell size to use for fields.\nDefines voxel/grid/cell size. So their values have significant\nside effects on all parameters defined in voxel units.\nThe minimum value for this parameter depends on the platform's floating point\naccuracy, with the practical minimum usually around 0.05.\nSame default value is used in RecastDemo.\n\ndetail sample dist\n------------------\n\n:Type:\t\tfloating point\n:Range:\t\t= 0.0 or >= 0.9\n:Default:\t6.0\n\nSets the sampling distance to use when generating the detail mesh.\n\ndetail sample max error\n-----------------------\n\n:Type:\t\tfloating point\n:Range:\t\t>= 0.0\n:Default:\t1.0\n\nThe maximum distance the detail mesh surface should deviate from heightfield data.\n\nmax simplification error\n------------------------\n\n:Type:\t\tfloating point\n:Range:\t\t>= 0.0\n:Default:\t1.3\n\nThe maximum distance a simplfied contour's border edges should deviate the original raw contour.\n\ntile size\n---------\n\n:Type:\t\tinteger\n:Range:\t\t> 0\n:Default:\t128\n\nThe width and height of each tile.\n\nborder size\n-----------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t16\n\nThe size of the non-navigable border around the heightfield.\n\nmax edge len\n------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t12\n\nThe maximum allowed length for contour edges along the border of the mesh.\n\nmax nav mesh query nodes\n------------------------\n\n:Type:\t\tinteger\n:Range:\t\t0 < value <= 65535\n:Default:\t2048\n\nMaximum number of search nodes.\n\nmax polygons per tile\n---------------------\n\n:Type:\t\tinteger\n:Range:\t\t2^n, 0 < n < 22\n:Default:\t4096\n\nMaximum number of polygons per nav mesh tile. Maximum number of nav mesh tiles depends on\nthis value. 22 bits is a limit to store both tile identifier and polygon identifier (tiles = 2^(22 - log2(polygons))).\nSee `recastnavigation <https://github.com/recastnavigation/recastnavigation>`_ for more details.\n\n.. Warning::\n    Lower value may lead to ignored world geometry on nav mesh.\n    Greater value will reduce number of nav mesh tiles.\n    This condition is always true: ``max tiles number * max polygons per tile <= 4194304``.\n    It's a limitation of `Recastnavigation <https://github.com/recastnavigation/recastnavigation>`_ library.\n\nmax verts per poly\n------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 3\n:Default:\t6\n\nThe maximum number of vertices allowed for polygons generated during the contour to polygon conversion process.\n\nregion merge size\n-----------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t20\n\nAny regions with a span count smaller than this value will, if possible, be merged with larger regions.\n\nregion min size\n---------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t8\n\nThe minimum number of cells allowed to form isolated island areas.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/physics.rst",
    "content": "Physics Settings\n################\n\nasync num threads\n-----------------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t0\n\nDetermines how many threads will be spawned to compute physics update in the background (that is, process actors movement). A value of 0 means that the update will be performed in the main thread.\nA value greater than 1 requires the Bullet library be compiled with multithreading support. If that's not the case, a warning will be written in ``openmw.log`` and a value of 1 will be used.\n\nlineofsight keep inactive cache\n-------------------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= -1\n:Default:\t0\n\nThe line of sight determines if 2 actors can see each other (without taking into account game mechanics such as invisibility or sneaking). It is used by some scripts (the getLOS function), by the AI (to determine if an actor should start combat or chase an opponent) and for functionnalities such as greetings or turning NPC head toward an object.\nThis parameters determine for how long a cache of request should be kept warm. It depends on :ref:`async num threads` being > 0, otherwise a value of -1 will be used. If a request is not found in the cache, it is always fulfilled immediately. In case Bullet is compiled without multithreading support, non-cached requests involve blocking the async thread(s), which might hurt performance.\nA value of -1 means no caching.\nA value of 0 means that for as long as a request is made (after the first one), it will be preemptively \"refreshed\" in the async thread, without blocking neither the main thread nor the async thread.\nAny value > 0 is the number of frames for which the values are kept in cache even if the results was not requested again.\nIf Bullet is compiled with multithreading support, requests are non blocking, it is better to set this parameter to -1.\n\ndefer aabb update\n-----------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nAxis-aligned bounding box (aabb for short) are used by Bullet for collision detection. They should be updated anytime a physical object is modified (for instance moved) for collision detection to be correct.\nThis parameter control wether the update should be done as soon as the object is modified (the default), which involves blocking the async thread(s), or queue the modifications to update them as a batch before the collision detections. It depends on :ref:`async num threads` being > 0, otherwise it will be disabled.\nDisabling this parameter is intended as an aid for debugging collisions detection issues.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/saves.rst",
    "content": "Saves Settings\n##############\n\ncharacter\n---------\n\n:Type:\t\tstring\n:Range:\n:Default:\t\"\"\n\nThis contains the default character for the Load Game menu and is automatically updated when a different character is selected.\n\nautosave\n--------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nThis setting determines whether the game will be automatically saved when the character rests.\n\nThis setting can be toggled in game with the Auto-Save when Rest button in the Prefs panel of the Options menu.\n\ntimeplayed\n----------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting determines whether the amount of the time the player has spent playing will be displayed\nfor each saved game in the Load menu. Currently, the counter includes time spent in menus, including the pause menu, \nbut does not include time spent with the game window minimized.\n\nThis setting can only be configured by editing the settings configuration file.\n\nmax quicksaves\n--------------\n\n:Type:\t\tinteger\n:Range:\t\t>0\n:Default:\t1\n\nThis setting determines how many quicksave and autosave slots you can have at a time.  If greater than 1, \nquicksaves will be sequentially created each time you quicksave.  Once the maximum number of quicksaves has been reached, \nthe oldest quicksave will be recycled the next time you perform a quicksave.\n\nThis setting can only be configured by editing the settings configuration file.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/shaders.rst",
    "content": "Shaders Settings\n################\n\nforce shaders\n-------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nForce rendering with shaders. By default, only bump-mapped objects will use shaders.\nEnabling this option may cause slightly different visuals if the \"clamp lighting\" option is set to false.\nOtherwise, there should not be a visual difference.\n\nPlease note enabling shaders has a significant performance impact on most systems.\n\nforce per pixel lighting\n------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nForce the use of per pixel lighting. By default, only bump mapped objects use per-pixel lighting.\nHas no effect if the 'force shaders' option is false.\nEnabling per-pixel lighting results in visual differences to the original MW engine.\nIt is not recommended to enable this option when using vanilla Morrowind files,\nbecause certain lights in Morrowind rely on vertex lighting to look as intended.\nNote that groundcover shaders ignore this setting.\n\nclamp lighting\n--------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nRestrict the amount of lighting that an object can receive to a maximum of (1,1,1).\nOnly affects objects that render with shaders (see 'force shaders' option).\nAlways affects terrain.\n\nLeaving this option at its default makes the lighting compatible with Morrowind's fixed-function method,\nbut the lighting may appear dull and there might be colour shifts.\nSetting this option to 'false' results in more dynamic lighting.\n\nauto use object normal maps\n---------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this option is enabled, normal maps are automatically recognized and used if they are named appropriately\n(see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds).\nIf this option is disabled,\nnormal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file). Affects objects.\n\nauto use object specular maps\n-----------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf this option is enabled, specular maps are automatically recognized and used if they are named appropriately\n(see 'specular map pattern', e.g. for a base texture foo.dds,\nthe specular map texture would have to be named foo_spec.dds).\nIf this option is disabled, normal maps are only used if they are explicitly listed within the mesh file\n(.osg file, not supported in .nif files). Affects objects.\n\nauto use terrain normal maps\n----------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nSee 'auto use object normal maps'. Affects terrain.\n\nauto use terrain specular maps\n------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nIf a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map.\nThe texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel.\n\nnormal map pattern\n------------------\n\n:Type:\t\tstring\n:Range:\n:Default:\t_n\n\nThe filename pattern to probe for when detecting normal maps\n(see 'auto use object normal maps', 'auto use terrain normal maps')\n\nnormal height map pattern\n-------------------------\n\n:Type:\t\tstring\n:Range:\n:Default:\t_nh\n\nAlternative filename pattern to probe for when detecting normal maps.\nFiles with this pattern are expected to include 'height' in the alpha channel.\nThis height is used for parallax effects. Works for both terrain and objects.\n\nspecular map pattern\n--------------------\n\n:Type:\t\tstring\n:Range:\n:Default:\t_spec\n\nThe filename pattern to probe for when detecting object specular maps (see 'auto use object specular maps')\n\nterrain specular map pattern\n----------------------------\n\n:Type:\t\tstring\n:Range:\n:Default:\t_diffusespec\n\nThe filename pattern to probe for when detecting terrain specular maps (see 'auto use terrain specular maps')\n\napply lighting to environment maps\n----------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nNormally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark.\nMorrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option.\nAffected objects will use shaders.\n\nradial fog\n----------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nBy default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen.\nThis setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.\nNote that the rendering will act as if you have 'force shaders' option enabled with this on, which means that shaders will be used to render all objects and the terrain.\n\nlighting method\n---------------\n\n:Type:\t\tstring\n:Range:\t\tlegacy|shaders compatibility|shaders\n:Default:\tdefault\n\nSets the internal handling of light sources.\n\n'legacy' is restricted to 8 lights per object and emulates fixed function\npipeline compatible lighting.\n\n'shaders compatibility' removes the light limit controllable through :ref:`max\nlights` and follows a modifed attenuation formula which can drastically reduce\nlight popping and seams. This mode also enables lighting on groundcover and a\nconfigurable light fade. It is recommended to use this with older hardware and a\nlight limit closer to 8. Because of its wide range of compatibility it is set as\nthe default.\n\n'shaders' carries all of the benefits that 'shaders compatibility' does, but\nuses a modern approach that allows for a higher :ref:`max lights` count with\nlittle to no performance penalties on modern hardware. It is recommended to use\nthis mode when supported and where the GPU is not a bottleneck. On some weaker\ndevices, using this mode along with :ref:`force per pixel lighting` can carry\nperformance penalties.\n\nWhen enabled, groundcover lighting is forced to be vertex lighting, unless\nnormal maps are provided. This is due to some groundcover mods using the Z-Up\nnormals technique to avoid some common issues with shading. As a consequence,\nper pixel lighting would give undesirable results.\n\nNote that the rendering will act as if you have 'force shaders' option enabled\nwhen not set to 'legacy'. This means that shaders will be used to render all objects and\nthe terrain.\n\nlight bounds multiplier\n-----------------------\n\n:Type:\t\tfloat\n:Range:\t\t0.0-5.0\n:Default:\t1.65\n\nControls the bounding sphere radius of point lights, which is used to determine\nif an object should receive lighting from a particular light source. Note, this\nhas no direct effect on the overall illumination of lights. Larger multipliers\nwill allow for smoother transitions of light sources, but may require an\nincrease in :ref:`max lights` and thus carries a performance penalty. This\nespecially helps with abrupt light popping with handheld light sources such as\ntorches and lanterns.\n\nThis setting has no effect if :ref:`lighting method` is 'legacy'.\n\nmaximum light distance\n----------------------\n\n:Type:\t\tfloat\n:Range:\t\tThe whole range of 32-bit floating point\n:Default:\t8192\n\nThe maximum distance from the camera that lights will be illuminated, applies to\nboth interiors and exteriors. A lower distance will improve performance. Set\nthis to a non-positive value to disable fading.\n\nThis setting has no effect if :ref:`lighting method` is 'legacy'.\n\nlight fade start\n----------------\n\n:Type:\t\tfloat\n:Range:\t\t0.0-1.0\n:Default:\t0.85\n\nThe fraction of the maximum distance at which lights will begin to fade away.\nTweaking it will make the transition proportionally more or less smooth.\n\nThis setting has no effect if the :ref:`maximum light distance` is non-positive\nor :ref:`lighting method` is 'legacy'.\n\nmax lights\n----------\n\n:Type:\t\tinteger\n:Range:\t\t2-64\n:Default:\t8\n\nSets the maximum number of lights that each object can receive lighting from.\nIncreasing this too much can cause significant performance loss, especially if\n:ref:`lighting method` is not set to 'shaders' or :ref:`force per pixel\nlighting` is on.\n\nThis setting has no effect if :ref:`lighting method` is 'legacy'.\n\nminimum interior brightness\n------------------------\n\n:Type:\t\tfloat\n:Range:\t\t0.0-1.0\n:Default:\t0.08\n\nSets the minimum interior ambient brightness for interior cells when\n:ref:`lighting method` is not 'legacy'. A consequence of the new lighting system\nis that interiors will sometimes be darker since light sources now have sensible\nfall-offs. A couple solutions are to either add more lights or increase their\nradii to compensate, but these require content changes. For best results it is\nrecommended to set this to 0.0 to retain the colors that level designers\nintended. If brighter interiors are wanted, however, this setting should be\nincreased. Note, it is advised to keep this number small (< 0.1) to avoid the\naforementioned changes in visuals.\n\nThis setting has no effect if :ref:`lighting method` is 'legacy'.\n\nantialias alpha test\n---------------------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nConvert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage when :ref:`antialiasing` is on.\nThis allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation.\nWhen MSAA is off, this setting will have no visible effect, but might have a performance cost.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/shadows.rst",
    "content": "Shadows Settings\n################\n\nMain settings\n*************\n\nenable shadows\n--------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nEnable or disable the rendering of shadows.\nUnlike in the original Morrowind engine, 'Shadow Mapping' is used, which can have a performance impact, but has more realistic results.\nBear in mind that this will force OpenMW to use shaders as if :ref:`force shaders` was enabled.\nA keen developer may be able to implement compatibility with fixed-function mode using the advice of `this post <https://github.com/OpenMW/openmw/pull/1547#issuecomment-369657381>`_, but it may be more difficult than it seems.\n\nnumber of shadow maps\n---------------------\n\n:Type:\t\tinteger\n:Range:\t\t1 to 8, but higher values may conflict with other texture effects\n:Default:\t3\n\nControl how many shadow maps to use - more of these means each shadow map texel covers less area, producing better-looking shadows, but may decrease performance.\nUsing too many shadow maps will lead to them overriding texture slots used for other effects, producing unpleasant artefacts.\nA value of three is recommended in most cases, but other values may produce better results or performance.\n\nmaximum shadow map distance\n---------------------------\n\n:Type:\t\tfloat\n:Range:\t\tThe whole range of 32-bit floating point\n:Default:\t8192\n\nThe maximum distance from the camera shadows cover, limiting their overall area coverage\nand improving their quality and performance at the cost of removing shadows of distant objects or terrain.\nSet this to a non-positive value to remove the limit.\n\nshadow fade start\n-------------------\n\n:Type:\t\tfloat\n:Range:\t\t0.0-1.0\n:Default:\t0.9\n\nThe fraction of the maximum shadow map distance at which the shadows will begin to fade away.\nTweaking it will make the transition proportionally more or less smooth.\nThis setting has no effect if the maximum shadow map distance is non-positive (infinite).\n\nallow shadow map overlap\n------------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nIf true, allow shadow maps to overlap.\nCounter-intuitively, will produce much better results when the light is behind the camera.\nWhen enabled, OpenMW uses Cascaded Shadow Maps and when disabled, it uses Parallel Split Shadow Maps.\n\nenable debug hud\n----------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nEnable or disable the debug hud to see what the shadow map(s) contain.\nThis setting is only recommended for developers, bug reporting and advanced users performing fine-tuning of shadow settings.\n\nenable debug overlay\n--------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nEnable or disable the debug overlay to see the area covered by each shadow map.\nThis setting is only recommended for developers, bug reporting and advanced users performing fine-tuning of shadow settings.\n\ncompute scene bounds\n--------------------\n\n:Type:\t\tstring\n:Range:\t\tprimitives|bounds|none\n:Default:\tbounding volumes\n\nTwo different ways to make better use of shadow map(s) by making them cover a smaller area.\nWhile primitives give better shadows at expense of more CPU, bounds gives better performance overall but with lower quality shadows. There is also the ability to disable this computation with none.\n\nshadow map resolution\n---------------------\n\n:Type:\t\tinteger\n:Range:\t\tDependent on GPU/driver combination\n:Default:\t1024\n\nControl How large to make the shadow map(s).\nHigher values increase GPU load but can produce better-looking results.\nPower-of-two values may turn out to be faster than smaller values which are not powers of two on some GPU/driver combinations.\n\nactor shadows\n-------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAllow actors to cast shadows.\nPotentially decreases performance.\n\nplayer shadows\n--------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAllow the player to cast shadows.\nPotentially decreases performance.\n\nterrain shadows\n---------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAllow terrain to cast shadows.\nPotentially decreases performance.\n\nobject shadows\n--------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAllow static objects to cast shadows.\nPotentially decreases performance.\n\nenable indoor shadows\n---------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nAllow shadows indoors.\nDue to limitations with Morrowind's data, only actors can cast shadows indoors without the ceiling casting a shadow everywhere.\nSome might feel this is distracting as shadows can be cast through other objects, so indoor shadows can be disabled completely.\n\nExpert settings\n***************\n\nThese settings are probably too complicated for regular users to judge what might be good values to set them to.\nIf you've got a good understanding of how shadow mapping works, or you've got enough time to try a large set of values, you may get better results tuning these yourself.\nCopying values from another user who's done careful tuning is the recommended way of arriving at an optimal value for these settings.\n\nUnderstanding what some of these do might be easier for people who've read `this paper on Parallel Split Shadow Maps <https://pdfs.semanticscholar.org/15a9/f2a7cf6b1494f45799617c017bd42659d753.pdf>`_ and understood how they interact with the transformation used with Light Space Perspective Shadow Maps.\n\npolygon offset factor\n---------------------\n\n:Type:\t\tfloat\n:Range:\t\tTheoretically the whole range of 32-bit floating point, but values just above 1.0 are most sensible.\n:Default:\t1.1\n\nUsed as the factor parameter for the polygon offset used for shadow map rendering.\nHigher values reduce shadow flicker, but risk increasing Peter Panning.\nSee `the OpenGL documentation for glPolygonOffset <https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml>`_ for details.\n\npolygon offset units\n---------------------\n\n:Type:\t\tfloat\n:Range:\t\tTheoretically the whole range of 32-bit floating point, but values between 1 and 10 are most sensible.\n:Default:\t4.0\n\nUsed as the units parameter for the polygon offset used for shadow map rendering.\nHigher values reduce shadow flicker, but risk increasing Peter Panning.\nSee `the OpenGL documentation for glPolygonOffset <https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml>`_ for details.\n\nnormal offset distance\n----------------------\n\n:Type:\t\tfloat\n:Range:\t\tTheoretically the whole range of 32-bit floating point, but values between 0 and 2 are most sensible.\n:Default:\t1.0\n\nHow far along the surface normal to project shadow coordinates.\nHigher values significantly reduce shadow flicker, usually with a lower increase of Peter Panning than the Polygon Offset settings.\nThis value is in in-game units, so 1.0 is roughly 1.4 cm.\n\nuse front face culling\n----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nExcludes theoretically unnecessary faces from shadow maps, slightly increasing performance.\nIn practice, Peter Panning can be much less visible with these faces included, so if you have high polygon offset values, leaving this off may help minimise the side effects.\n\nsplit point uniform logarithmic ratio\n-------------------------------------\n\n:Type:\t\tfloat\n:Range:\t\t0.0-1.0 for sensible results. Other values may 'work' but could behave bizarrely.\n:Default:\t0.5\n\nControls the ratio of :math:`C_i^{log}` versus :math:`C_i^{uniform}` used to form the Practical Split Scheme as described in the linked paper.\nWhen using a larger-than-default viewing distance and distant terrain, and you have `allow shadow map overlap`_ enabled, larger values will prevent nearby shadows losing quality.\nIt is therefore recommended that this isn't left at the default when the viewing distance is changed.\n\nsplit point bias\n----------------\n\n:Type:\t\tfloat\n:Range:\t\tAny value supported by C++ floats on your platform, although undesirable behaviour is more likely to appear the further the value is from zero.\n:Default:\t0.0\n\nThe :math:`\\delta_{bias}` parameter used to form the Practical Split Scheme as described in the linked paper.\n\nminimum lispsm near far ratio\n-----------------------------\n\n:Type:\t\tfloat\n:Range:\t\tMust be greater than zero.\n:Default:\t0.25\n\nControls the minimum near/far ratio for the Light Space Perspective Shadow Map transformation.\nHelps prevent too much detail being brought towards the camera at the expense of detail further from the camera.\nIncreasing this pushes detail further away by moving the frustum apex further from the near plane.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/sound.rst",
    "content": "Sound Settings\n##############\n\ndevice\n------\n\n:Type:\t\tstring\n:Range:\n:Default:\t\"\"\n\nThis setting determines which audio device to use. A blank or missing setting means to use the default device,\nwhich should usually be sufficient, but if you need to explicitly specify a device use this setting.\n\nThe names of detected devices can be found in the openmw.log file in your configuration directory.\n\nThis setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher.\n\nmaster volume\n-------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 (silent) to 1.0 (maximum volume)\n:Default:\t1.0\n\nThis setting controls the overall volume.\nThe master volume is multiplied with specific volume settings to determine the final volume.\n\nThis setting can be changed in game using the Master slider from the Audio panel of the Options menu.\n\nfootsteps volume\n----------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 (silent) to 1.0 (maximum volume)\n:Default:\t0.2\n\nThis setting controls the volume of footsteps from the character and other actors.\n\nThis setting can be changed in game using the Footsteps slider from the Audio panel of the Options menu.\n\nmusic volume\n------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 (silent) to 1.0 (maximum volume)\n:Default:\t0.5\n\nThis setting controls the volume for music tracks.\n\nThis setting can be changed in game using the Music slider from the Audio panel of the Options menu.\n\nsfx volume\n----------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 (silent) to 1.0 (maximum volume)\n:Default:\t1.0\n\nThis setting controls the volume for special sound effects such as combat noises.\n\nThis setting can be changed in game using the Effects slider from the Audio panel of the Options menu.\n\nvoice volume\n------------\n\n:Type:\t\tfloating point\n:Range:\t\t0.0 (silent) to 1.0 (maximum volume)\n:Default:\t0.8\n\nThis setting controls the volume for spoken dialogue from NPCs.\n\nThis setting can be changed in game using the Voice slider from the Audio panel of the Options menu.\n\nbuffer cache min\n----------------\n\n:Type:\t\tinteger\n:Range:\t\t> 0\n:Default:\t14\n\nThis setting determines the minimum size of the sound buffer cache in megabytes.\nWhen the cache reaches the size specified by the buffer cache max setting,\nold buffers will be unloaded until it's using no more memory than specified by this setting.\nThis setting must be less than or equal to the buffer cache max setting.\n\nThis setting can only be configured by editing the settings configuration file.\n\nbuffer cache max\n----------------\n\n:Type:\t\tinteger\n:Range:\t\t> 0\n:Default:\t16\n\nThis setting determines the maximum size of the sound buffer cache in megabytes. When the cache reaches this size,\nold buffers will be unloaded until it reaches the size specified by the buffer cache min setting.\nThis setting must be greater than or equal to the buffer cache min setting.\n\nThis setting can only be configured by editing the settings configuration file.\n\nhrtf enable\n-----------\n\n:Type:\t\tinteger\n:Range:\t\t-1, 0, 1\n:Default:\t-1\n\nThis setting determines whether to enable head-related transfer function (HRTF) audio processing.\nHRTF audio processing creates the perception of sounds occurring in a three dimensional space when wearing headphones.\nEnabling HRTF may also require an OpenAL Soft version greater than 1.17.0,\nand possibly some operating system configuration.\nA value of 0 disables HRTF processing, while a value of 1 explicitly enables HRTF processing.\nThe default value is -1, which should enable the feature automatically for most users when possible.\nThis setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher.\n\nhrtf\n----\n\n:Type:\t\tstring\n:Range:\n:Default:\t\"\"\n\nThis setting specifies which HRTF profile to use when HRTF is enabled. Blank means use the default.\nThis setting has no effect if HRTF is not enabled based on the hrtf enable setting.\nAllowed values for this field are enumerated in openmw.log file is an HRTF enabled audio system is installed.\n\nThe default value is empty, which uses the default profile.\nThis setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/terrain.rst",
    "content": "Terrain Settings\n################\n\ndistant terrain\n---------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nControls whether the engine will use paging (chunking) and LOD algorithms to load the terrain of the entire world at all times.\nOtherwise, only the terrain of the surrounding cells is loaded.\n\n.. note::\n\tWhen enabling distant terrain, make sure the 'viewing distance' in the camera section is set to a larger value so\n\tthat you can actually see the additional terrain and objects.\n\nTo avoid frame drops as the player moves around, nearby terrain pages are always preloaded in the background,\nregardless of the preloading settings in the 'Cells' section,\nbut the preloading of terrain behind a door or a travel destination, for example,\nwill still be controlled by cell preloading settings.\n\nThe distant terrain engine is currently considered experimental and may receive updates in the future.\n\nvertex lod mod\n--------------\n\n:Type:      integer\n:Range:     any\n:Default:   0\n\nControls only the Vertex LOD of the terrain. The amount of terrain chunks and the detail of composite maps is left unchanged.\n\nMust be changed in increments of 1. Each increment will double (for positive values) or halve (for negative values) the number of vertices rendered.\nFor example: -2 means 4x reduced detail, +3 means 8x increased detail.\n\nNote this setting will typically not affect near terrain. When set to increase detail, the detail of near terrain can not be increased\nbecause the detail is simply not there in the data files, and when set to reduce detail,\nthe detail of near terrain will not be reduced because it was already less detailed than the far terrain (in view relative terms) to begin with.\n\nlod factor\n----------\n\n:Type:\t\tfloat\n:Range:\t\t>0\n:Default:\t1.0\n\nControls the level of detail if distant terrain is enabled.\nHigher values increase detail at the cost of performance, lower values reduce detail but increase performance.\n\nNote: it also changes how the Quad Tree is split.\nIncreasing detail with this setting results in the visible terrain being divided into more chunks,\nwhere as reducing detail with this setting would reduce the number of chunks.\n\nFewer terrain chunks is faster for rendering, but on the other hand a larger proportion of the entire terrain\nmust be rebuilt when LOD levels change as the camera moves.\nThis could result in frame drops if moving across the map at high speed.\n\nFor this reason, it is not recommended to change this setting if you want to change the LOD.\nIf you want to do that, first try using the 'vertex lod mod' setting to configure the detail of the terrain outlines\nto your liking and then use 'composite map resolution' to configure the texture detail to your liking.\nBut these settings can only be changed in multiples of two, so you may want to adjust 'lod factor' afterwards for even more fine-tuning.\n\ncomposite map level\n-------------------\n\n:Type:\t\tinteger\n:Range:\t\t>= -3\n:Default:\t0\n\nControls at which minimum size (in 2^value cell units) terrain chunks will start to use a composite map instead of the high-detail textures.\nWith value -3 composite maps are used everywhere.\n\nA composite map is a pre-rendered texture that contains all the texture layers combined.\nNote that resolution of composite maps is currently always fixed at 'composite map resolution',\nregardless of the resolution of the underlying terrain textures.\nIf high resolution texture replacers are used, it is recommended to increase 'composite map resolution' setting value.\n\ncomposite map resolution\n------------------------\n\n:Type:\t\tinteger\n:Range:\t\t>0\n:Default:\t512\n\nControls the resolution of composite maps. Larger values result in increased detail,\nbut may take longer to prepare and thus could result in longer loading times and an increased chance of frame drops during play.\nAs with most other texture resolution settings, it's most efficient to use values that are powers of two.\n\nAn easy way to observe changes to loading time is to load a save in an interior next to an exterior door\n(so it will start preloding terrain) and watch how long it takes for the 'Composite' counter on the F4 panel to fall to zero.\n\nmax composite geometry size\n---------------------------\n\n:Type:\t\tfloat\n:Range:\t\t>=1.0\n:Default:\t4.0\n\nControls the maximum size of simple composite geometry chunk in cell units. With small values there will more draw calls and small textures,\nbut higher values create more overdraw (not every texture layer is used everywhere).\n\nobject paging\n-------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nControls whether the engine will use paging (chunking) algorithms to load non-terrain objects\noutside of the active cell grid.\n\nDepending on the settings below every object in the game world has a chance\nto be batched and be visible in the game world, effectively allowing\nthe engine to render distant objects with a relatively low performance impact automatically.\n\nIn general, an object is more likely to be batched if the number of the object's vertices\nand the corresponding memory cost of merging the object is low compared to\nthe expected number of the draw calls that are going to be optimized out.\nThis memory cost and the saved number of draw calls shall be called\nthe \"merging cost\" and the \"merging benefit\" in the following documentation.\n\nObjects that are scripted to disappear from the game world\nwill be handled properly as long as their scripts have a chance to actually disable them.\n\nThis setting has no effect if distant terrain is disabled.\n\nobject paging active grid\n-------------------------\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nControls whether the objects in the active cells use the mentioned paging algorithms.\nActive grid paging significantly improves the framerate when your setup is CPU-limited.\n\n.. note::\n\tGiven that only 8 light sources may affect an object at a time at the moment,\n\tlighting issues arising due to merged objects being considered a single object\n\tmay disrupt your gameplay experience.\n\nobject paging merge factor\n--------------------------\n:Type:\t\tfloat\n:Range:\t\t>0\n:Default:\t250.0\n\nAffects the likelyhood of objects being merged.\nHigher values improve the framerate at the cost of memory.\n\nTechnically this is implemented as a multiplier to the merging benefit, and since\nan object has a lot of vertices, sometimes in terms of hundreds and thousands,\nand doesn't often need as much draw calls to be rendered (typically that number is in 1 or 2 digits)\nthis value needs to be large enough, as this is what makes\nthe merging cost and the merging benefit actually comparable for the sake of paging.\n\nobject paging min size\n----------------------\n:Type:\t\tfloat\n:Range:\t\t>0\n:Default:\t0.01\n\nControls how large an object must be to be visible in the scene.\nThe object's size is divided by its distance to the camera\nand the result of the division is compared with this value.\nThe smaller this value is, the more objects you will see in the scene.\n\nobject paging min size merge factor\n-----------------------------------\n:Type:\t\tfloat\n:Range:\t\t>0\n:Default:\t0.3\n\nThis setting gives inexpensive objects a chance to be rendered from a greater distance\neven if the engine would rather discard them according to the previous setting.\n\nIt controls the factor that the minimum size is multiplied by\nroughly according to the following formula:\n\n\tfactor = merge cost * min size cost multiplier / merge benefit\n\t\n\tfactor = factor + (1 - factor) * min size merge factor\n\nSince the larger this factor is, the smaller chance a large object has to be rendered,\ndecreasing this value makes more objects visible in the scene\nwithout impacting the performance as dramatically as the minimum size setting.\n\nobject paging min size cost multiplier\n--------------------------------------\n:Type:\t\tfloat\n:Range:\t\t>0\n:Default:\t25.0\n\nThis setting adjusts the calculated cost of merging an object used in the mentioned functionality.\nThe larger this value is, the less expensive objects can be before they are discarded.\nSee the formula above to figure out the math.\n\nobject paging debug batches\n---------------------------\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis debug setting allows you to see what objects have been merged in the scene\nby making them colored randomly.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/video.rst",
    "content": "Video Settings\n##############\n\nresolution x\n------------\n\n:Type:\t\tinteger\n:Range:\t\t> 0\n:Default:\t800\n\nThis setting determines the horizontal resolution of the OpenMW game window.\nLarger values produce more detailed images within the constraints of your graphics hardware,\nbut may reduce the frame rate.\n\nThe window resolution can be selected from a menu of common screen sizes\nin the Video tab of the Video Panel of the Options menu, or in the Graphics tab of the OpenMW Launcher.\nThe horizontal resolution can also be set to a custom value in the Graphics tab of the OpenMW Launcher.\n\nresolution y\n------------\n\n:Type:\t\tinteger\n:Range:\t\t> 0\n:Default:\t600\n\nThis setting determines the vertical resolution of the OpenMW game window.\nLarger values produce more detailed images within the constraints of your graphics hardware,\nbut may reduce the frame rate.\n\nThe window resolution can be selected from a menu of common screen sizes\nin the Video tab of the Video Panel of the Options menu, or in the Graphics tab of the OpenMW Launcher.\nThe vertical resolution can also be set to a custom value in the Graphics tab of the OpenMW Launcher.\n\nfullscreen\n----------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting determines whether the entire screen is used for the specified resolution.\n\nThis setting can be toggled in game using the Fullscreen button in the Video tab of the Video panel in the Options menu.\nIt can also be toggled with the Full Screen check box in the Graphics tab of the OpenMW Launcher.\n\nscreen\n------\n\n:Type:\t\tinteger\n:Range:\t\t>= 0\n:Default:\t0\n\nThis setting determines which screen the game will open on in multi-monitor configurations.\nThis setting is particularly important when the fullscreen setting is true,\nsince this is the only way to control which screen is used,\nbut it can also be used to control which screen a normal window or a borderless window opens on as well.\nThe screens are numbered in increasing order, beginning with 0.\n\nThis setting can be selected from a pull down menu in the Graphics tab of the OpenMW Launcher,\nbut cannot be changed during game play.\n\nminimize on focus loss\n----------------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nMinimize the OpenMW window if it loses cursor focus. This setting is primarily useful for single screen configurations,\nso that the OpenMW screen in full screen mode can be minimized\nwhen the operating system regains control of the mouse and keyboard.\nOn multiple screen configurations, disabling this option makes it easier to switch between screens while playing OpenMW.\n\n.. Note::\n\tA minimized game window consumes less system resources and produces less heat,\n\tsince the game does not need to render in minimized state.\n\tIt is therefore advisable to minimize the game during pauses\n\t(either via use of this setting, or by minimizing the window manually).\n\nThis setting has no effect if the fullscreen setting is false.\n\nDeveloper note: corresponds to SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS.\n\nThis setting can only be configured by editing the settings configuration file.\n\nwindow border\n-------------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tTrue\n\nThis setting determines whether there's an operating system border drawn around the OpenMW window.\nIf this setting is true, the window can be moved and resized with the operating system window controls.\nIf this setting is false, the window has no operating system border.\n\nThis setting has no effect if the fullscreen setting is true.\n\nThis setting can be toggled in game using the Window Border button\nin the Video tab of the Video panel in the Options menu.\nIt can also be toggled with the Window Border check box in the OpenMW Launcher.\n\nantialiasing\n------------\n\n:Type:\t\tinteger\n:Range:\t\t0, 2, 4, 8, 16\n:Default:\t0\n\nThis setting controls anti-aliasing. Anti-aliasing is a technique designed to improve the appearance of polygon edges,\nso they do not appear to be \"jagged\".\nAnti-aliasing can smooth these edges at the cost of a minor reduction in the frame rate.\nA value of 0 disables anti-aliasing.\nOther powers of two (e.g. 2, 4, 8, 16) are supported according to the capabilities of your graphics hardware.\nHigher values do a better job of smoothing out the image but have a greater impact on frame rate.\n\nThis setting can be configured from a list of valid choices in the Graphics panel of the OpenMW Launcher,\nbut cannot be changed during game play\ndue to a technical limitation that may be addressed in a future version of OpenMW.\n\nvsync\n-----\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting determines whether frame draws are synchronized with the vertical refresh rate of your monitor.\nEnabling this setting can reduce screen tearing,\na visual defect caused by updating the image buffer in the middle of a screen draw.\nEnabling this option typically implies limiting the framerate to the refresh rate of your monitor,\nbut may also introduce additional delays caused by having to wait until the appropriate time\n(the vertical blanking interval) to draw a frame, and a loss in mouse responsiveness known as 'input lag'.\n\nThis setting can be adjusted in game using the VSync button in the Video tab of the Video panel in the Options menu.\nIt can also be changed by toggling the Vertical Sync check box in the Graphics tab of the OpenMW Launcher.\n\nframerate limit\n---------------\n\n:Type:\t\tfloating point\n:Range:\t\t>= 0.0\n:Default:\t300\n\nThis setting determines the maximum frame rate in frames per second.\nIf this setting is 0.0, the frame rate is unlimited.\n\nThere are several reasons to consider capping your frame rate,\nespecially if you're already experiencing a relatively high frame rate (greater than 60 frames per second).\nLower frame rates will consume less power and generate less heat and noise.\nFrame rates above 60 frames per second rarely produce perceptible improvements in visual quality,\nbut may improve input responsiveness.\nCapping the frame rate may in some situations reduce the perception of choppiness\n(highly variable frame rates during game play) by lowering the peak frame rates.\n\nThis setting interacts with the vsync setting in the Video section\nin the sense that enabling vertical sync limits the frame rate to the refresh rate of your monitor\n(often 60 frames per second).\nChoosing to limit the frame rate using this setting instead of vsync may reduce input lag\ndue to the game not having to wait for the vertical blanking interval.\n\ncontrast\n--------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0.0\n:Default:\t1.0\n\nThis setting controls the contrast correction for all video in the game.\n\nThis setting can only be configured by editing the settings configuration file. \nIt has been reported to not work on some Linux systems.\n\ngamma\n-----\n\n:Type:\t\tfloating point\n:Range:\t\t> 0.0\n:Default:\t1.0\n\nThis setting controls the gamma correction for all video in the game.\nGamma is an exponent that makes colors brighter if greater than 1.0 and darker if less than 1.0.\n\nThis setting can be changed in the Detail tab of the Video panel of the Options menu.\nIt has been reported to not work on some Linux systems, \nand therefore the in-game setting in the Options menu has been disabled on Linux systems.\n"
  },
  {
    "path": "docs/source/reference/modding/settings/water.rst",
    "content": "Water Settings\n##############\n\n.. note::\n\tThe settings for the water shader are difficult to describe,\n\tbut can be seen immediately in the Water tab of the Video panel in the Options menu.\n\tChanges there will be saved to these settings.\n\tIt is suggested to stand on the shore of a moderately broad body of water with trees or other objects\n\ton the far shore to test reflection textures,\n\tunderwater plants in shallow water near by to test refraction textures,\n\tand some deep water visible from your location to test deep water visibility.\n\nshader\n------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting enables or disables the water shader, which results in much more realistic looking water surfaces,\nincluding reflected objects and a more detailed wavy surface.\n\nThis setting can be toggled with the Shader button in the Water tab of the Video panel of the Options menu.\n\nrtt size\n--------\n\n:Type:\t\tinteger\n:Range:\t\t> 0\n:Default:\t512\n\nThe setting determines the size of the texture used for reflection and refraction (if enabled).\nFor reflection, the texture size determines the detail of reflected images on the surface of the water.\nFor refraction, the texture size determines the detail of the objects on the other side of the plane of water\n(which have a wavy appearance caused by the refraction).\nRTT is an acronym for Render To Texture which allows rendering of the scene to be saved as a texture.\nHigher values produces better visuals and result in a marginally lower frame rate depending on your graphics hardware.\n\nIn the Water tab of the Video panel of the Options menu, the choices are Low (512), Medium (1024) and High (2048).\nThis setting has no effect if the shader setting is false.\nIt is recommended to use values that are a power of two because this results in more efficient use of video hardware.\n\nThis setting has no effect if the shader setting is false.\n\nrefraction\n----------\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n:Default:\tFalse\n\nThis setting enables the refraction rendering feature of the water shader.\nRefraction causes deep water to be more opaque\nand objects seen through the plane of the water to have a wavy appearance.\nEnabling this feature results in better visuals, and a marginally lower frame rate depending on your graphics hardware.\n\nThis setting has no effect if the shader setting is false.\n\nThis setting can be toggled with the 'Refraction' button in the Water tab of the Video panel of the Options menu.\n\nreflection detail\n-----------------\n\n:Type:\t\tinteger\n:Range:\t\t0, 1, 2, 3, 4, 5\n:Default:\t2\n\nControls what kinds of things are rendered in water reflections.\n\n0: only sky is reflected\n1: terrain is also reflected\n2: statics, activators, and doors are also reflected\n3: items, containers, and particles are also reflected\n4: actors are also reflected\n5: groundcover objects are also reflected\n\nIn interiors the lowest level is 2.\nThis setting can be changed ingame with the \"Reflection shader detail\" dropdown under the Water tab of the Video panel in the Options menu.\n\nsmall feature culling pixel size\n--------------------------------\n\n:Type:\t\tfloating point\n:Range:\t\t> 0\n:Default:\t20.0\n\nControls the cutoff in pixels for small feature culling - see the 'Camera' section for more details,\nhowever this setting in the 'Water' section applies specifically to objects rendered in water reflection\nand refraction textures.\n\nThe setting 'rtt size' interacts with this setting\nbecause it controls how large a pixel on the water texture (technically called a texel) is in pixels on the screen.\n\nThis setting will have no effect if the shader setting is false,\nor the 'small feature culling' (in the 'Camera' section) is disabled.\n\nThis setting can only be configured by editing the settings configuration file.\n\nrefraction scale\n----------------\n\n:Type:\t\tfloating point\n:Range:\t\t0 to 1\n:Default:\t1.0\n\nSimulates light rays refracting when transitioning from air to water, which causes the space under water look scaled down\nin height when viewed from above the water surface. Though adding realism, the setting can cause distortion which can\nmake for example aiming at enemies in water more challenging, so it is off by default (i.e. set to 1.0). To get a realistic\nlook of real-life water, set the value to 0.75.\n\nThis setting only applies if water shader is on and refractions are enabled. Note that if refractions are enabled and this\nsetting if off, there will still be small refractions caused by the water waves, which however do not cause such significant\ndistortion.\n\n.. warning::\n    The `refraction scale` is currently mutually exclusive to underwater shadows. Setting this to any value except 1.0\n    will cause underwater shadows to be disabled. This will be addressed in issue https://gitlab.com/OpenMW/openmw/-/issues/5709\n"
  },
  {
    "path": "docs/source/reference/modding/settings/windows.rst",
    "content": "Windows Settings\n################\n\n:Type:\tfloating point\n:Range:\t0.0 to 1.0\n\nThis section controls the location and size of each window in GUI mode.\nEach setting is a floating point number representing a *fraction*\nof the resolution x or resolution y setting in the Video Settings Section.\nThe X and Y values locate the top left corner of the window,\nwhile the W value determines the width of the window and the H value determines the height of the window.\n\nUnlike the documentation for most sections which lists the exact setting name,\nthis page instead lists the names of the windows.\nFor example, to configure the alchemy window, the actual settings would be::\n\n\talchemy x = 0.25\n\talchemy y = 0.25\n\talchemy h = 0.5\n\talchemy w = 0.5\n\nEach window in the GUI mode remembers it's previous location when exiting the game.\nBy far the easiest way to configure these settings is to simply move the windows around in game.\nHand editing the configuration file might result in some fine tuning for alignment,\nbut the settings will be overwritten if a window is moved.\n\n.. note::\n\tTo scale the windows, making the widgets proportionally larger, \n\tsee the scaling factor setting in the GUI section instead.\n\n:Type:\t\tboolean\n:Range:\t\tTrue/False\n\nThis section controls the state of pinnable windows: pinned or not.\nFor example, to pin only the map window, the actual settings will be::\n\n\tinventory pin = false\n\tmap pin = true\n\tstats pin = false\n\tspells pin = false\n\nThe pinnable window can be pinned/unpinned by clicking on a button in the right upper corner of the window.\n\nstats\n-----\n\n:Default:\n\tx = 0.015\n\n\ty = 0.015\n\n\th = 0.45\n\n\tw = 0.4275\n\n\tpin = false\n\nThe stats window, displaying level, race, class, skills and stats.\nActivated by clicking on any of the three bars in the lower left corner of the HUD.\n\nspells\n------\n\n:Default:\n\tx = 0.63\n\n\ty = 0.39\n\n\th = 0.36\n\n\tw = 0.51\n\n\tpin = false\n\nThe spells window, displaying powers, spells, and magical items.\nActivated by clicking on the spells widget (third from left) in the bottom left corner of the HUD.\n\nmap\n---\n\n:Default:\n\tx = 0.63\n\n\ty = 0.015\n\n\th = 0.36\n\n\tw = 0.37\n\n\tpin = false\n\nThe local and world map window.\nActivated by clicking on the map widget in the bottom right corner of the HUD.\n\ninventory\n---------\n\n:Default:\n\tx = 0.015\n\n\ty = 0.54\n\n\th = 0.45\n\n\tw = 0.38\n\n\tpin = false\n\nThe inventory window, displaying the paper doll and possessions,\nwhen activated by clicking on the inventory widget (second from left) in the bottom left corner of the HUD.\n\ninventory container\n-------------------\n\n:Default:\n\tx = 0.015\n\n\ty = 0.54\n\n\th = 0.45\n\n\tw = 0.38\n\nThe player's inventory window while searching a container, showing the contents of the character's inventory.\nActivated by clicking on a container. The same window is used for searching dead bodies, and pickpocketing people.\n\ninventory barter\n----------------\n\n:Default:\n\tx = 0.015\n\n\ty = 0.54\n\n\th = 0.45\n\n\tw = 0.38\n\nThe player's inventory window while bartering. It displays goods owned by the character while bartering.\nActivated by clicking on the Barter choice in the dialog window for an NPC.\n\ninventory companion\n-------------------\n\n:Default:\n\tx = 0.015\n\n\ty = 0.54\n\n\th = 0.45\n\n\tw = 0.38\n\nThe player's inventory window while interacting with a companion.\nThe companion windows were added in the Tribunal expansion, but are available everywhere in the OpenMW engine.\n\ncontainer\n---------\n\n:Default:\n\tx = 0.49\n\n\ty = 0.54\n\n\th = 0.39\n\n\tw = 0.38\n\nThe container window, showing the contents of the container. Activated by clicking on a container.\nThe same window is used for searching dead bodies, and pickpocketing people.\n\nbarter\n------\n\n:Default:\n\tx = 0.6\n\n\ty = 0.27\n\n\th = 0.38\n\n\tw = 0.63\n\nThe NPC bartering window, displaying goods owned by the shopkeeper while bartering.\nActivated by clicking on the Barter choice in the dialog window for an NPC.\n\ncompanion\n---------\n\n:Default:\n\tx = 0.6\n\n\ty = 0.27\n\n\th = 0.38\n\n\tw = 0.63\n\nThe NPC's inventory window while interacting with a companion.\nThe companion windows were added in the Tribunal expansion, but are available everywhere in the OpenMW engine.\n\ndialogue\n--------\n\n:Default:\n\tx = 0.15\n\n\ty = 0.5\n\n\th = 0.7\n\n\tw = 0.45\n\nThe dialogue window, for talking with NPCs.\nActivated by clicking on a NPC.\n\nalchemy\n-------\n\n:Default:\n\tx = 0.25\n\n\ty = 0.25\n\n\th = 0.5\n\n\tw = 0.5\n\nThe alchemy window, for crafting potions.\nActivated by dragging an alchemy tool on to the rag doll.\nUnlike most other windows, this window hides all other windows when opened.\n\nconsole\n-------\n\n:Default:\n\tx = 0.015\n\n\ty = 0.015\n\n\th = 1.0\n\n\tw = 0.5\n\nThe console command window.\nActivated by pressing the tilde (~) key.\n"
  },
  {
    "path": "docs/source/reference/modding/sky-system.rst",
    "content": "########################\nSky System and Structure\n########################\n\nOverview\n********\n\nThe sky system is made from multiple components that contribute to the final result.\n\n1. Background colour and fog\n2. Atmosphere\n3. Clouds\n4. Stars\n5. Sun\n6. Moons\n7. Weather effects\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/_static/sky-system-overview.png\n    :align: center\n\nBackground Colour and Fog\n*************************\n\nEven when nothing else is being rendered, OpenMW will always show the basic background colour. The distant fog effect is based off this colour as well. Other elements of the sky are rendered on top of the background to compose the sky. The colour of the background changes depending on the time of day and current weather.\n\nAtmosphere\n**********\n\nA mesh that contributes the blue colour of the sky. It is a rough cylinder in shape without the bottom face and with the normals pointing inwards. During the day, it is light blue and transitions to a very dark blue, almost black, during the night. \n\nTowards the bottom edge the mesh gradually becomes transparent and blends with the background colour. Transparency is done per vertex and OpenMW decides which vertices are transparent based on their index. This adds a requirement for a very strict vertex and face order on this mesh to blend properly.\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/_static/sky-system-mesh-atmosphere.jpg\n    :align: center\n\nClouds\n******\n\nA mesh that renders a tiling, scrolling texture of the clouds. It is a flat dome in shape with the normals pointing inwards. Towards the boundary edge the mesh becomes transparent and blends with the background colour. As with the atmosphere, there is a very strict vertex order on this mesh to blend properly.\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/_static/sky-system-mesh-clouds.jpg\n    :align: center\n\nBy default, the UVs of this mesh are scaled so the clouds texture repeats five times across the sky. The weather system blends between different cloud textures depending on the current weather. Speed of the clouds moving across the sky can be set per weather type.\n\nStars\n*****\n\nA dome shaped mesh that shows the stars during the night. It starts to become visible during sunset and goes back to transparent during sunrise. At its bottom edge it blends to transparency which is defined with the vertex colour. White is full opacity while black is full transparency. The mesh ends above the horizon to prevent the stars being visible near the horizon when underwater.\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/_static/sky-system-mesh-stars.jpg\n    :align: center\n\nIt can use multiple textures to show different star constellations. In addition, extra meshes can be used to blend nebulae across the sky.\n\nSun\n***\n\nThe sun is a billboard that moves across the sky. It is composed of two texures. One shows the regular sun sphere, the other is the sun glare that is added on top of the sun. Glare strength adjusts dynamically depending on how obstructed the view to the sun is.\n\nMoons\n*****\n\nThe moons are two separate billboards moving across the sky and are both rendered the same way. First, a circle texture is used to mask the background. A moon texture is then added on top of the mask. Depending on the current moon phase, a variant of the moon texture is used. The texture on top is additively blended so any transparent area is achieved with black pixels. The following image shows all the separate textures needed for one moon.\n\n.. image:: https://gitlab.com/OpenMW/openmw-docs/-/raw/master/docs/source/reference/modding/_static/sky-system-moon-textures.jpg\n    :align: center\n\n\nWeather Effects\n***************\n\nThese are particle emitters used to display weather phenomena such as rain, snow, or dust clouds. Originally, Morrowind used .nif files with a number of planes and baked animations. In OpenMW, these effects are done through code and are currently hardcoded. The particle emmitters emit particles around the player in a big enough area so it looks like the whole world is affected by the weather.\n\nSettings\n********\n\nColour and other settings for each weather type can be edited in ``openmw.cfg`` configuration file.\n\n"
  },
  {
    "path": "docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst",
    "content": "====================================\nNormal maps from Morrowind to OpenMW\n====================================\n\n- `General introduction to normal map conversion`_\n    - `OpenMW normal-mapping`_\n    - `Activating normal-mapping shaders in OpenMW`_\n    - `Morrowind bump-mapping`_\n    - `MGE XE normal-mapping`_\n- `Converting PeterBitt's Scamp Replacer`_ (Mod made for the MGE XE PBR prototype)\n    - `Tutorial - MGE`_\n- `Converting Lougian's Hlaalu Bump mapped`_ (Morrowind's bump-mapping, part 1: *without* custom models)\n    - `Tutorial - Morrowind, Part 1`_\n- `Converting Apel's Various Things - Sacks`_ (Morrowind's bump-mapping, part 2: *with* custom models)\n    - `Tutorial - Morrowind, Part 2`_\n\nGeneral introduction to normal map conversion\n------------------------------------------------\n\n:Authors: Joakim (Lysol) Berg, Alexei (Capo) Dobrohotov\n:Updated: 2020-03-03\n\nThis page has general information and tutorials on how normal-mapping works in OpenMW and how you can make mods using\nthe old environment-mapped bump-mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most\n(in)famous one to previously give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work better in OpenMW.\n\n*Note:* The conversion made in the `Converting Apel's Various Things - Sacks`_-part of this tutorial require the use of the application NifSkope_.\n\n*Another note:* I will use the terms bump-mapping and normal-mapping simultaneously.\nNormal-mapping is one form of bump-mapping. In other words, normal-mapping is bump-mapping,\nbut bump-mapping isn't necessarily normal-mapping.\nThere are several techniques for bump-mapping, and normal-mapping is the most common one today.\n\nSo let's get on with it.\n\nOpenMW normal-mapping\n************************\n\nNormal-mapping in OpenMW works in a very simple way: The engine just looks for a texture with a *_n.dds* suffix,\nand you're done.\n\nSo to expand on this a bit, let's take a look at how a model looks up textures.\n\nLet us assume we have the model *example.nif*. In this model file,\nthere should be a tag (NiTexturingProperty) that states what textures it should use and where to find them. Typically,\nthe model's base (diffuse) texture reference will point to something named like *exampletexture_01.dds*. This texture is supposed to be located directly in the\nTextures folder since it does not state anything else.\nModders tend to group textures for custom-made models in dedicated folders to keep track of them easily,\nso it might be something like *./Textures/moddername/exampletexture_02.dds*.\n\nOpenMW will pick the diffuse map file path from the mesh, e.g.\n*exampletexture_01.dds*, and look up a texture named *exampletexture_01_n.dds*.\nThat file will be the normal map if it's present. Simple.\n\nActivating normal-mapping shaders in OpenMW\n*******************************************\n\nBefore normal (and specular and parallax) maps can show up in OpenMW, their auto-detection needs to be turned on in\nsettings.cfg_-file. Add these rows where it would make sense:\n\n::\n\n    [Shaders]\n    auto use object normal maps = true\n    auto use terrain normal maps = true\n\n    auto use object specular maps = true\n    auto use terrain specular maps = true\n\nSee OpenMW's wiki page about `texture modding`_ to read more about it.\n\nMorrowind bump-mapping\n*****************************************************\n\n**Conversion difficulty:**\n*Varies. Sometimes quick and easy, sometimes time-consuming and hard.*\n\nYou might have bumped (pun intended) on a few bump-mapped texture packs for Morrowind that require\nMorrowind Code Patch (MCP). OpenMW supports them, and like MCP can optionally apply lighting after environment maps\nare processed which makes bump-mapped models look a bit better,\ncan make use of the gloss map channel in the bump map and can apply bump-mapping to skinned models.\nAdd this to settings.cfg_-file:\n\n::\n\n    [Shaders]\n    apply lighting to environment maps = true\n\nBut sometimes you may want them to look a bit better than in vanilla.\nTechnically you aren't supposed to convert bump maps because they shouldn't be normal maps that are supported by OpenMW as well,\nbut artists may use actual normal maps as bump maps either because they look better in vanilla... or because they're lazy.\nIn this case you can benefit from OpenMW's normal-mapping support by using these bump maps the way normal maps are used.\nThis means that you will have to drop the bump-mapping references from the model and sometimes rename the texture.\n\nMGE XE normal-mapping\n***************************************\n\n**Conversion difficulty:**\n*Easy*\n\nThe most recent feature on this topic is that the Morrowind Graphics Extender (MGE) finally started to support real\nnormal-mapping in an experimental version available here: `MGE XE`_ (you can't use MGE with OpenMW!).\nNot only this but it also adds full support for physically based rendering (PBR),\nmaking it one step ahead of OpenMW in terms of texturing techniques. However,\nOpenMW will probably have this feature in the future too – and let's hope that OpenMW and MGE will handle PBR in a\nsimilar fashion in the future so that mods can be used for both MGE and OpenMW without any hassle.\n\nI haven't researched that much on the MGE variant yet but it does support real implementation of normal-mapping,\nmaking it really easy to convert mods made for MGE into OpenMW (I'm only talking about the normal map textures though).\nThere's some kind of text file if I understood it correctly that MGE uses to find the normal map.\nOpenMW does not need this, you just have to make sure the normal map has the same name as the diffuse texture but with\nthe correct suffix after.\n\nNow, on to the tutorials.\n\nConverting PeterBitt's Scamp Replacer\n-------------------------------------\n**Mod made for the MGE XE PBR prototype**\n\n:Authors: Joakim (Lysol) Berg\n:Updated: 2016-11-11\n\nSo, let's say you've found out that PeterBitt_ makes awesome models and textures featuring physically based rendering\n(PBR) and normal maps. Let's say that you tried to run his `PBR Scamp Replacer`_ in OpenMW and that you were greatly\ndisappointed when the normal map didn't seem to work. Lastly, let's say you came here, looking for some answers.\nAm I right? Great. Because you've come to the right place!\n\n*A quick note before we begin*: Please note that you can only use the normal map texture and not the rest of the materials,\nsince PBR isn't implemented in OpenMW yet. Sometimes PBR textures can look dull without all of the texture files,\nso have that in mind.\n\nTutorial - MGE\n**************\n\nIn this tutorial, I will use PeterBitt's `PBR Scamp Replacer`_ as an example,\nbut any mod featuring PBR that requires the PBR version of MGE will do,\nprovided it also includes a normal map (which it probably does).\n\nSo, follow these steps:\n\n#. Go to the Nexus page for PeterBitt's `PBR Scamp Replacer`_\n#. Go to the *files* tab and download the main file and the \"PBR materials\" file.\n#. Extract the main file as if you'd install a normal mod (**Pro tip**: Install using OpenMW's `Multiple data folders`_ function!)\n#. Now, open the PBR materials file:\n    - Go to ``./Materials/PB/``.\n    - Select the ``tx_Scamp_normals.dds`` file, which is, obviously, the normal map texture.\n    - Extract this file to the place you extracted the main file to, but in the subdirectory ``./Textures/PB/``.\n#. Rename your newly extracted file (``tx_Scamp_normals.dds``) to ``tx_Scamp_n.dds`` (which is exactly the same name as the diffuse texture file, except for the added *_n* suffix before the filename extention).\n#. You're actually done!\n\nSo as you might notice, converting these mods is very simple and takes just a couple of minutes.\nIt's more or less just a matter of renaming and moving a few files.\n\nI totally recommend you to also try this on PeterBitt's Nix Hound replacer and Flash3113's various replacers.\nIt should be the same principle to get those to work.\n\nAnd let's hope that some one implements PBR shaders to OpenMW too,\nso that we can use all the material files of these mods in the future.\n\nConverting Lougian's Hlaalu Bump mapped\n---------------------------------------\n**Mod made for Morrowind's bump-mapping, without custom models**\n\n:Authors: Joakim (Lysol) Berg, Alexei (Capo) Dobrohotov\n:Updated: 2020-03-03\n\nConverting normal maps made for the Morrowind's bump-mapping can be really easy or a real pain,\ndepending on a few circumstances. In this tutorial, we will look at a very easy,\nalthough in some cases a bit time-consuming, example.\n\nTutorial - Morrowind, Part 1\n**********************\n\nWe will be converting a quite popular texture replacer of the Hlaalu architecture, namely Lougian's `Hlaalu Bump mapped`_.\nSince this is just a texture pack and not a model replacer,\nwe can convert the mod in a few minutes by just renaming a few dozen files and by *not* extracting the included model\n(``.nif``) files when installing the mod.\n\n#. Download Lougian's `Hlaalu Bump mapped`_.\n#. Install the mod by extracting the ``./Textures`` folder to a data folder the way you usually install mods (**Pro tip**: Install using OpenMW's `Multiple data folders`_ function!).\n    - Again, yes, *only* the ``./Textures`` folder. Do not extract the Meshes folder. They are there to make Morrowind bump-mapping work.\n#. Go to your new texture folder. If you installed the mod like I recommended, you won't have any trouble finding the files. If you instead placed all your files in Morrowinds main Data Files folder (sigh), you need to check with the mod's .rar file to see what files you should look for. Because you'll be scrolling through a lot of files.\n#. Find all the textures related to the texture pack in the Textures folder and take note of all the ones that ends with a *_nm.dds*.\n#. The *_nm.dds* files are normal map files. OpenMW's standard format is to have the normal maps with a *_n.dds* instead. Rename all the normal map textures to only have a *_n.dds* instead of the *_nm.dds*.\n    - As a nice bonus to this tutorial, this pack actually included one specularity texture too. We should use it of course. It's the one called \"``tx_glass_amber_02_reflection.dds``\". For OpenMW to recognize this file and use it as a specular map, you need to change the *_reflection.dds* part to *_spec.dds*, resulting in the name ``tx_glass_amber_01_spec.dds``.\n#. That should be it. Really simple, but I do know that it takes a few minutes to rename all those files.\n\nNow – if the mod you want to change includes custom made models it gets a bit more complicated I'm afraid.\nBut that is for the next tutorial.\n\nConverting Apel's Various Things - Sacks\n----------------------------------------\n**Mod made for Morrowind bump-mapping, with custom models**\n\n:Authors: Joakim (Lysol) Berg, Alexei (Capostrophic) Dobrohotov\n:Updated: 2020-03-03\n\nIn part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``)\nfiles so that the bump maps could be loaded as normal maps.\nWe ignored those model files since they are not needed with OpenMW. In this tutorial however,\nwe will convert a mod that includes new, custom-made models. In other words, we cannot just ignore those files this time.\n\nTutorial - Morrowind, Part 2\n**********************\n\nThe sacks included in Apel's `Various Things - Sacks`_ come in two versions – without bump-mapping, and with bump-mapping.\nSince we want the glory of normal-mapping in our OpenMW setup, we will go with the bump-mapped version.\n\n#. Start by downloading Apel's `Various Things - Sacks`_ from Nexus.\n#. Once downloaded, install it the way you'd normally install your mods (**Pro tip**: Install using OpenMW's `Multiple data folders`_ function!).\n#. Now, if you ran the mod right away, your sacks may look... wetter than expected. This is because the mod assumes you have the MCP feature which makes the sacks less shiny enabled. You can have its equivalent enabled to make the sacks look like in Morrowind with MCP, or you may proceed on the tutorial.\n#. We need to fix this by removing some tags in the model files. You need to download NifSkope_ for this, which, again, only have binaries available for Windows.\n#. Go the place where you installed the mod and go to ``./Meshes/o/`` to find the model files.\n    - If you installed the mod like I suggested, finding the files will be easy as a pie, but if you installed it by dropping everything into your main Morrowind Data Files folder, then you'll have to scroll a lot to find them. Check the mod's zip file for the file names of the models if this is the case. The same thing applies to when fixing the textures.\n#. Open up each of the models in NifSkope and look for these certain blocks_:\n    - NiTextureEffect\n    - NiSourceTexture with the value that appears to be a normal map file, in this mod, they have the suffix *_nm.dds*.\n#. Remove all these tags by selecting them one at a time and press right click>Block>Remove Branch. (Ctrl-Del)\n#. Repeat this on all the affected models.\n#. If you launch OpenMW now, you'll `no longer have wet models`_. But one thing is missing. Can you see it? It's actually hard to spot on still pictures, but we have no normal maps here.\n#. Now, go back to the root of where you installed the mod. Now go to ``./Textures/`` and you'll find the texture files in question.\n#. OpenMW detects normal maps if they have the same name as the base diffuse texture, but with a *_n.dds* suffix. In this mod, the normal maps has a suffix of *_nm.dds*. Change all the files that ends with *_nm.dds* to instead end with *_n.dds*.\n#. Finally, `we are done`_!\n\nSince these models have one or two textures applied to them, the fix was not that time-consuming. The process continues to work for more complex models that use more textures, but looking through each category for texture effects and normal mapped textures rapidly becomes tedious. Luckily, NifSkope provides a feature to do the same automatically.\n\nRight-click in NifSkope to access the *Spells* dropdown menu, also available via the top bar, hover over the *Blocks* section, and `choose the action to Remove by ID`_. You can then input the RegEx expression ``^NiTextureEffect`` (directing it to remove any block whose name starts with \"NiTextureEffect\") to automatically remove all texture effect blocks within the NIF. This also has the helpful side effect of listing `all the blocks within the NIF in the bottom section`_, allowing you to additionally root out any blocks referencing *_nm.dds* textures without having to painstakingly open each category.\n\n.. _`Netch Bump mapped`: https://www.nexusmods.com/morrowind/mods/42851/?\n.. _`Hlaalu Bump mapped`: https://www.nexusmods.com/morrowind/mods/42396/?\n.. _`On the Rocks`: http://mw.modhistory.com/download-44-14107\n.. _`texture modding`: https://wiki.openmw.org/index.php?title=TextureModding\n.. _`MGE XE`: https://www.nexusmods.com/morrowind/mods/26348/?\n.. _PeterBitt: https://www.nexusmods.com/morrowind/users/4381248/?\n.. _`PBR Scamp Replacer`: https://www.nexusmods.com/morrowind/mods/44314/?\n.. _settings.cfg: https://wiki.openmw.org/index.php?title=Settings\n.. _`Multiple data folders`: https://wiki.openmw.org/index.php?title=Mod_installation\n.. _`Various Things - Sacks`: https://www.nexusmods.com/morrowind/mods/42558/?\n.. _NifSkope: https://wiki.openmw.org/index.php?title=Tools#NifSkope\n.. _Blocks: https://imgur.com/VmQC0WG\n.. _`no longer have wet models`: https://imgur.com/vu1k7n1\n.. _`we are done`: https://imgur.com/yyZxlTw\n.. _`choose the action to Remove by ID`: https://imgur.com/a/qs2t0tC\n.. _`all the blocks within the NIF in the bottom section`: https://imgur.com/a/UFFNyWt\n"
  },
  {
    "path": "docs/source/reference/modding/texture-modding/index.rst",
    "content": "######################\nOpenMW Texture Modding\n######################\n\nAlthough texture modding requires external tools not supported by the OpenMW team,\nadding and modifying textures is an important part of content creation.\nTherefore, we've included the following in hopes of helping modders transition\nto texture modding in OpenMW.\n\n.. toctree::\n\t:caption: Table of Contents\n\t:maxdepth: 2\n\n\ttexture-basics\n\tconvert-bump-mapped-mods\n"
  },
  {
    "path": "docs/source/reference/modding/texture-modding/texture-basics.rst",
    "content": "autosectionlabel_prefix_document = True\n\n######################\nTexture Modding Basics\n######################\n\nOpenMW supports new texture mapping techniques\nthat were not available in the vanilla Morrowind engine.\n\n`Normal mapping`_ is a technique used to fake lighting of bumps,\ncracks and other small details.\n\n`Specular mapping`_ is used to vary the shininess/specularity along the surface of an object.\n\nThe prerequisite to using these techniques are\n`shaders <https://en.wikipedia.org/wiki/Shader>`_.\nOpenMW automatically uses shaders for objects with these mapping techniques.\n\nNormal Mapping\n##############\n\nTo plug in a normal map, you name the normal map as the diffuse texture but with a specified suffix after. OpenMW will then recognise the file and load it as a normal map, provided you have set up your settings file correctly. \n\nContent creators need to know that OpenMW uses the DX format for normal maps, and not the OpenGL format as one otherwise might expect. See an example of the difference between the two formats `here <https://i.stack.imgur.com/kf9jo.png>`_. Make sure your normal maps are created according to the DX style.\n\nSee the section `Automatic use`_ further down below for detailed information.\n\nSpecular Mapping\n################\n\nThe RGB channels of the specular map are used as the specular color.\nThe alpha channel specifies shininess in range [0, 255].\nIf a specular map is used, it will override the shininess and specular color\nset in the NiMaterialProperty / osg::Material.\n\nMorrowind format NIF files do not support normal maps or specular maps.\nIn order to use them anyway, see the next section.\n\nAutomatic Use\n#############\n\nSimply create the textures with appropriate naming convention\n(e.g. when the base texture is called foo.dds,\nthe normal map would have to be called foo_n.dds).\nTo enable this automatic use based on filename pattern,\nyou will have to add the following to your\n`settings.cfg </source/reference/modding/paths>`_ file::\n\n\t[Shaders]\n\tauto use object normal maps = true\n\n\tauto use object specular maps = true\n\n\tnormal map pattern = _n\n\tnormal height map pattern = _nh\n\n\tspecular map pattern = _spec\n\nAdditionally, a normal map with the `_nh` pattern enables\nthe use of the normal map's alpha channel as height information.\nThis can be used by a `parallax mapping <https://en.wikipedia.org/wiki/Parallax_mapping>`_\nshader to offset the texture depending on the viewing angle and height,\ncreating a fake 3D effect.\n\nThe above settings are not enabled by default to prevent incompatibilities\nwith mods that may be inadvertently using these naming schemes.\n\nOn the topic of shader settings,\nyou may be interested in these three settings as well: :ref:`force shaders`,\n:ref:`force per pixel lighting`, and :ref:`clamp lighting`.\nIn particular, `clamp lighting = false` makes normal maps look much better!\n\nTerrain\n#######\n\nThe terrain shader also supports normal, normal-height and specular maps,\nwith one difference compared to objects:\nthe specular value must be packed into the layer texture's alpha channel.\n\nFor example, if you wanted to add specular mapping to a terrain layer called rock.dds,\nyou would copy this texture to a new file called rock_diffusespec.dds,\nand then edit its alpha channel to set the specular intensity.\n\nThe relevant settings are::\n\n\t[Shaders]\n\tauto use terrain normal maps = true\n\n\tauto use terrain specular maps = true\n\n\tterrain specular map pattern = _diffusespec\n\n\t# Also used for terrain normal maps\n\tnormal map pattern = _n\n\tnormal height map pattern = _nh\n\nOSG native files\n################\n\nOpenMW supports all the above shader features for meshes in the Native Mesh Format.\nTo have the shader generator recognize specific textures,\nthe `osg::Texture2D` must be named appropriately.\n\nAvailable texture types are the following (most of which also have NIF equivalents):\n\n:diffuseMap: base texture\n:normalMap: normal map, as described earlier\n:normalHeightMap: same as normal map, but including height information in alpha channel to be used for parallax effects\n:emissiveMap: controls the material's emission, useful for objects that glow in the dark\n:darkMap: multiplied onto the base texture\n:detailMap: multiplied by 2 and then multiplied onto the base texture\n:envMap: spherical environment map\n:specularMap: specular map, as described earlier\n\nThe first texture unit automatically acts as diffuseMap if no recognized type is specified.\n\nExample: `.osgt` file excerpt of a normal mapped mesh::\n\n\tTextureModeList 2 {\n\t\tData 1 {\n\t\t\tGL_TEXTURE_2D ON\n\t\t}\n\t\tData 1 {\n\t\t\tGL_TEXTURE_2D ON\n\t\t}\n\t}\n\tTextureAttributeList 2 {\n\t\tData 1 {\n\t\t\tosg::Texture2D {\n\t\t\t\tUniqueID 37\n\t\t\t\tName \"diffuseMap\"\n\t\t\t\tWRAP_S REPEAT\n\t\t\t\tWRAP_T REPEAT\n\t\t\t\tWRAP_R REPEAT\n\t\t\t\tMIN_FILTER LINEAR_MIPMAP_LINEAR\n\t\t\t\tMAG_FILTER LINEAR\n\t\t\t\tImage TRUE {\n\t\t\t\t\tUniqueID 60\n\t\t\t\t\tFileName \"textures/BuddhaStatue_Dif.jpg\"\n\t\t\t\t\tWriteHint 2 2\n\t\t\t\t}\n\t\t\t}\n\t\t\tValue OFF\n\t\t}\n\t\tData 1 {\n\t\t\tosg::Texture2D {\n\t\t\t\tUniqueID 38\n\t\t\t\tName \"normalMap\"\n\t\t\t\tWRAP_S REPEAT\n\t\t\t\tWRAP_T REPEAT\n\t\t\t\tWRAP_R REPEAT\n\t\t\t\tMIN_FILTER LINEAR_MIPMAP_LINEAR\n\t\t\t\tMAG_FILTER LINEAR\n\t\t\t\tImage TRUE {\n\t\t\t\t\tUniqueID 61\n\t\t\t\t\tFileName \"textures/BuddhaStatue_Nor.jpg\"\n\t\t\t\t\tWriteHint 2 2\n\t\t\t\t}\n\t\t\t}\n\t\t\tValue OFF\n\t\t}\n\t}\n\n"
  },
  {
    "path": "docs/source/tutorial-style-guide.txt",
    "content": "####################\nTutorial Style Guide\n####################\n\nPlease contact Ravenwing about any questions relating to this guide.\n\nForeword\n--------\n\nI shall try to be as brief as possible without sacrificing clarity, just as you should be when writing documentation.\nThe SCOPE of this guide is limited to all non-source code documentation.\n\nReferences\n----------\n\n-Syntax: reStructuredText (ReST): http://docutils.sourceforge.net/rst.html\n-Parser: Sphinx: http://www.sphinx-doc.org/en/stable/\n-British and English Spelling: https://www.spellzone.com/pages/british-american.cfm\n\nLanguage\n--------\n\nBritish English\nSorry, I'm American and I'll probably have a million relapses,\nbut this seems to have been decided and uniformity is key!\n\nText Wrapping\n-------------\n\nAfter discussing the various benefits of manual and automatic linefeeds,\nwe've come to the conclusion that we should use semantic linefeeds with a hard max at 120 characters.\nBasically this boils down manually inserting a line break where it makes sense within the phrasing.\nI prefer to keep it at commas and periods because those are universal places people pause,\neven while reading in their head.\nIt may look a little funny in plain text, but it is readable,\nand none of this makes a difference in the final documentation.\n\nThe reason this makes the most sense is GitHub won't text wrap automatically in RST documents,\nand it also compares files based on lines.\nThis means you're less likely to run into conflicts and simply makes it easier to view changes.\n\nIndentations\n------------\n\nThis isn't as important, especially since Sphinx converts tabs to spaces,\nbut I find it much easier to keep large blocks of things aligned if you just tab.\nIf you can edit your tab width, please set it to 4 spaces.\n\nSpaces\n------\n\nUse only one space after each sentence. Some people were taught two spaces, but this is a carry-over from typewriters.\nEven though your text editor is probably using monospaced characters like a typewriter,\nthe formats Sphinx is converting into will make all the adjustments they need to be beautifully legible.\n\nCommas\n------\n\nOxford comma. Use it. I know this goes against using British English, but this is technical writing.\nYou cannot assume our audience will know from context if the last two items in a list are grouped or not.\nI also prefer parentheticals to commas around gerund phrases.\n\nFiles and Extensions\n--------------------\n\nWhen referring to a filename or filepath use the double back quotes as for inline literals. (e.g. ``morrowind.exe``)\nWhen referring to a file extension by itself, use ``.lowerCaseExtension``. (e.g. ``.esm``)\n\nIf referring to a folder or file by a general name, use *emphasis*\nIf referring to a folder by its actual name, even without the path, use ``inline literal``"
  },
  {
    "path": "extern/Base64/Base64.h",
    "content": "#ifndef _MACARON_BASE64_H_\n#define _MACARON_BASE64_H_\n\n/**\n * The MIT License (MIT)\n * Copyright (c) 2016 tomykaira\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include <string>\n\nnamespace Base64 {\n\nclass Base64 {\n public:\n\n  static std::string Encode(const std::string data) {\n    static constexpr char sEncodingTable[] = {\n      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',\n      'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',\n      'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',\n      'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',\n      'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',\n      'o', 'p', 'q', 'r', 's', 't', 'u', 'v',\n      'w', 'x', 'y', 'z', '0', '1', '2', '3',\n      '4', '5', '6', '7', '8', '9', '+', '/'\n    };\n\n    size_t in_len = data.size();\n    size_t out_len = 4 * ((in_len + 2) / 3);\n    std::string ret(out_len, '\\0');\n    size_t i;\n    char *p = ret.data();\n\n    for (i = 0; i < in_len - 2; i += 3) {\n      *p++ = sEncodingTable[(data[i] >> 2) & 0x3F];\n      *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];\n      *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)];\n      *p++ = sEncodingTable[data[i + 2] & 0x3F];\n    }\n    if (i < in_len) {\n      *p++ = sEncodingTable[(data[i] >> 2) & 0x3F];\n      if (i == (in_len - 1)) {\n        *p++ = sEncodingTable[((data[i] & 0x3) << 4)];\n        *p++ = '=';\n      }\n      else {\n        *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];\n        *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)];\n      }\n      *p++ = '=';\n    }\n\n    return ret;\n  }\n\n  static std::string Decode(const std::string& input, std::string& out) {\n    static constexpr unsigned char kDecodingTable[] = {\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,\n      52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,\n      64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,\n      15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,\n      64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\n      41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64\n    };\n\n    size_t in_len = input.size();\n    if (in_len % 4 != 0) return \"Input data size is not a multiple of 4\";\n\n    if (in_len == 0)\n    {\n        out = \"\";\n        return \"\";\n    }\n\n    size_t out_len = in_len / 4 * 3;\n    if (input[in_len - 1] == '=') out_len--;\n    if (input[in_len - 2] == '=') out_len--;\n\n    out.resize(out_len);\n\n    for (size_t i = 0, j = 0; i < in_len;) {\n      uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];\n      uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];\n      uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];\n      uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];\n\n      uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6);\n\n      if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF;\n      if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF;\n      if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF;\n    }\n\n    return \"\";\n  }\n\n};\n\n}\n\n#endif /* _MACARON_BASE64_H_ */\n"
  },
  {
    "path": "extern/Base64/CMakeLists.txt",
    "content": "add_library(Base64 INTERFACE)\ntarget_include_directories(Base64 INTERFACE .)"
  },
  {
    "path": "extern/CMakeLists.txt",
    "content": "# SPDX-License-Identifier: GPL-3.0-or-later\n\n# Like `FetchContent_MakeAvailable` but passes EXCLUDE_FROM_ALL to `add_subdirectory`.\nmacro(FetchContent_MakeAvailableExcludeFromAll)\n    foreach(contentName IN ITEMS ${ARGV})\n        string(TOLOWER ${contentName} contentNameLower)\n        FetchContent_GetProperties(${contentName})\n        if(NOT ${contentNameLower}_POPULATED)\n            FetchContent_Populate(${contentName})\n            if(EXISTS ${${contentNameLower}_SOURCE_DIR}/CMakeLists.txt)\n                add_subdirectory(${${contentNameLower}_SOURCE_DIR}\n                    ${${contentNameLower}_BINARY_DIR} EXCLUDE_FROM_ALL)\n            endif()\n        endif()\n    endforeach()\nendmacro()\n\nif(NOT OPENMW_USE_SYSTEM_BULLET)\n    cmake_minimum_required(VERSION 3.11) # for FetchContent\n\n    set(BUILD_BULLET3 OFF CACHE BOOL \"\")\n    set(BUILD_EXTRAS OFF CACHE BOOL \"\")\n    set(BUILD_OPENGL3_DEMOS OFF CACHE BOOL \"\")\n    set(BUILD_UNIT_TESTS OFF CACHE BOOL \"\")\n    set(BUILD_BULLET2_DEMOS OFF CACHE BOOL \"\")\n    set(BUILD_CLSOCKET OFF CACHE BOOL \"\")\n    set(BUILD_ENET OFF CACHE BOOL \"\")\n    set(BUILD_CPU_DEMOS OFF CACHE BOOL \"\")\n    set(BUILD_EGL OFF CACHE BOOL \"\")\n\n    set(USE_DOUBLE_PRECISION ON CACHE BOOL \"\")\n    set(BULLET2_MULTITHREADING ON CACHE BOOL \"\")\n\n    if(BULLET_STATIC)\n        set(BUILD_SHARED_LIBS OFF CACHE BOOL \"\" FORCE)\n    else()\n        set(BUILD_SHARED_LIBS ON CACHE BOOL \"\" FORCE)\n    endif()\n\n    if(MSVC)\n        # this setting is badly named - having it off forces the static runtime library,\n        # but having it on does nothing, letting the defaults get used.\n        # OpenMW uses the defaults, and you can't mix and match.\n        set(USE_MSVC_RUNTIME_LIBRARY_DLL ON CACHE BOOL \"\" FORCE)\n    endif()\n\n    # May 7, 2021\n    include(FetchContent)\n    FetchContent_Declare(bullet\n        URL https://github.com/bulletphysics/bullet3/archive/refs/tags/3.17.tar.gz\n        URL_HASH MD5=7711bce9a49c289a08ecda34eaa0f32e\n        SOURCE_DIR fetched/bullet\n    )\n    FetchContent_MakeAvailableExcludeFromAll(bullet)\n\n    set(BULLET_INCLUDE_DIRS ${bullet_SOURCE_DIR}/src PARENT_SCOPE)\n\n    # The order here is important to work around a bug in Bullet:\n    # https://github.com/bulletphysics/bullet3/issues/3233\n    set(BULLET_LIBRARIES BulletCollision LinearMath PARENT_SCOPE)\nendif()\n\nif(NOT OPENMW_USE_SYSTEM_MYGUI)\n    cmake_minimum_required(VERSION 3.11) # for FetchContent\n\n    set(MYGUI_RENDERSYSTEM 4 CACHE STRING \"\")\n    set(MYGUI_DISABLE_PLUGINS TRUE CACHE BOOL \"\")\n    set(MYGUI_BUILD_DEMOS OFF CACHE BOOL \"\")\n    set(MYGUI_BUILD_PLUGINS OFF CACHE BOOL \"\")\n    set(MYGUI_BUILD_TOOLS OFF CACHE BOOL \"\")\n\n    # We appear to be using some obsolete properties in the XML.\n    # See https://gitlab.com/OpenMW/openmw/-/issues/5896\n    set(MYGUI_DONT_USE_OBSOLETE OFF CACHE BOOL \"\")\n\n    if(MYGUI_STATIC)\n        set(BUILD_SHARED_LIBS OFF CACHE BOOL \"\" FORCE)\n    else()\n        set(BUILD_SHARED_LIBS ON CACHE BOOL \"\" FORCE)\n    endif()\n\n    # master on 13 Mar 2021\n    include(FetchContent)\n    FetchContent_Declare(mygui\n        URL https://github.com/MyGUI/mygui/archive/59c1388b942721887d18743ada15f1906ff11a1f.zip\n        URL_HASH MD5=0a64c9cccc8f96dc8c08172175e68e1c\n        SOURCE_DIR fetched/mygui\n    )\n    FetchContent_MakeAvailableExcludeFromAll(mygui)\n\n    set(MyGUI_INCLUDE_DIRS ${mygui_SOURCE_DIR}/MyGUIEngine/include PARENT_SCOPE)\n    set(MyGUI_LIBRARIES MyGUIEngine PARENT_SCOPE)\nendif()\n\nif(NOT OPENMW_USE_SYSTEM_OSG)\n    cmake_minimum_required(VERSION 3.11) # for FetchContent\n\n    set(BUILD_OSG_APPLICATIONS OFF CACHE BOOL \"\")\n    set(BUILD_OSG_DEPRECATED_SERIALIZERS OFF CACHE BOOL \"\")\n    set(OSG_FIND_3RD_PARTY_DEPS OFF CACHE BOOL \"\")\n\n    set(BUILD_OSG_PLUGINS_BY_DEFAULT OFF CACHE BOOL \"\")\n    set(BUILD_OSG_PLUGIN_BMP ON CACHE BOOL \"\")\n    set(BUILD_OSG_PLUGIN_DDS ON CACHE BOOL \"\")\n    set(BUILD_OSG_PLUGIN_FREETYPE ON CACHE BOOL \"\")\n    set(BUILD_OSG_PLUGIN_JPEG ON CACHE BOOL \"\")\n    set(BUILD_OSG_PLUGIN_OSG ON CACHE BOOL \"\")\n    set(BUILD_OSG_PLUGIN_PNG ON CACHE BOOL \"\")\n    set(BUILD_OSG_PLUGIN_TGA ON CACHE BOOL \"\")\n    set(BUILD_OSG_PLUGIN_KTX ON CACHE BOOL \"\")\n\n    set(OSG_USE_FLOAT_MATRIX ON CACHE BOOL \"\")\n    set(OSG_USE_FLOAT_PLANE ON CACHE BOOL \"\")\n    set(OSG_USE_FLOAT_QUAT ON CACHE BOOL \"\")\n\n    set(OPENGL_PROFILE \"GL2\" CACHE STRING \"\")\n\n    if(OSG_STATIC)\n        set(BUILD_SHARED_LIBS OFF CACHE BOOL \"\" FORCE)\n        set(DYNAMIC_OPENTHREADS OFF CACHE BOOL \"\" FORCE)\n        set(DYNAMIC_OPENSCENEGRAPH OFF CACHE BOOL \"\" FORCE)\n    else()\n        set(BUILD_SHARED_LIBS ON CACHE BOOL \"\" FORCE)\n        set(DYNAMIC_OPENTHREADS ON CACHE BOOL \"\" FORCE)\n        set(DYNAMIC_OPENSCENEGRAPH ON CACHE BOOL \"\" FORCE)\n    endif()\n    mark_as_advanced(DYNAMIC_OPENTHREADS DYNAMIC_OPENSCENEGRAPH)\n\n    if(WIN32)\n        # OSG here inherits C++17 language level because it doesn't specify its own.\n        #\n        # OSG's `using namespace std` interferes with Windows header files.\n        #\n        # See https://developercommunity.visualstudio.com/t/error-c2872-byte-ambiguous-symbol/93889\n        #\n        # An alternative way to work around this without changing the language level is:\n        #\n        # add_compile_definitions(_HAS_STD_BYTE=0)\n        #\n        # TODO: Put OSG into its own scope so that this does not leak into Recast below.\n        set(CMAKE_CXX_STANDARD 11)\n\n        if(MSVC)\n            set(OSG_MSVC_VERSIONED_DLL OFF CACHE BOOL \"\")\n        endif()\n    endif()\n\n    # branch OpenSceneGraph-3.6 on 23 Jan 2021.\n    include(FetchContent)\n    FetchContent_Declare(osg\n        URL https://github.com/OpenMW/osg/archive/e65f47c4ab3a0b53cc19f517961671e5f840a08d.zip\n        URL_HASH MD5=0c967fe48d80744f6956f6b0b67ef7c6\n        SOURCE_DIR fetched/osg\n    )\n    FetchContent_MakeAvailableExcludeFromAll(osg)\n\n    set(OPENSCENEGRAPH_INCLUDE_DIRS ${osg_SOURCE_DIR}/include ${osg_BINARY_DIR}/include PARENT_SCOPE)\n    set(OSG_LIBRARIES OpenThreads osg PARENT_SCOPE)\n    foreach(_name ${USED_OSG_COMPONENTS})\n        string(TOUPPER ${_name} _name_uc)\n        set(${_name_uc}_LIBRARIES ${_name} PARENT_SCOPE)\n    endforeach()\n    foreach(_name ${USED_OSG_PLUGINS})\n        string(TOUPPER ${_name} _name_uc)\n        set(${_name_uc}_LIBRARY ${_name} PARENT_SCOPE)\n    endforeach()\nendif()\n\nif(NOT OPENMW_USE_SYSTEM_RECASTNAVIGATION)\n    if(RECASTNAVIGATION_STATIC)\n        set(BUILD_SHARED_LIBS OFF)\n    else()\n        set(BUILD_SHARED_LIBS ON)\n    endif()\n\n    set(RECASTNAVIGATION_DEMO OFF CACHE BOOL \"\")\n    set(RECASTNAVIGATION_TESTS OFF CACHE BOOL \"\")\n    set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL \"\")\n\n    # master on 15 Feb 2021\n    include(FetchContent)\n    FetchContent_Declare(recastnavigation\n        URL https://github.com/recastnavigation/recastnavigation/archive/e75adf86f91eb3082220085e42dda62679f9a3ea.zip\n        URL_HASH MD5=af905d121ef9d1cdfa979b0495cba059\n        SOURCE_DIR fetched/recastnavigation\n    )\n    FetchContent_MakeAvailableExcludeFromAll(recastnavigation)\nendif()\n"
  },
  {
    "path": "extern/LuaBridge/List.h",
    "content": "// https://github.com/vinniefalco/LuaBridge\n//\n// Copyright 2018, Dmitry Tarakanov\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <LuaBridge/detail/Stack.h>\n\n#include <list>\n\nnamespace luabridge {\n\ntemplate <class T>\nstruct Stack <std::list <T> >\n{\n  static void push(lua_State* L, std::list <T> const& list)\n  {\n    lua_createtable (L, static_cast <int> (list.size ()), 0);\n    typename std::list <T>::const_iterator item = list.begin();\n    for (std::size_t i = 1; i <= list.size (); ++i)\n    {\n      lua_pushinteger (L, static_cast <lua_Integer> (i));\n      Stack <T>::push (L, *item);\n      lua_settable (L, -3);\n      ++item;\n    }\n  }\n\n  static std::list <T> get(lua_State* L, int index)\n  {\n    if (!lua_istable(L, index))\n    {\n      luaL_error(L, \"#%d argments must be table\", index);\n    }\n\n    std::list <T> list;\n\n    int const absindex = lua_absindex (L, index);\n    lua_pushnil (L);\n    while (lua_next (L, absindex) != 0)\n    {\n      list.push_back (Stack <T>::get (L, -1));\n      lua_pop (L, 1);\n    }\n    return list;\n  }\n};\n\ntemplate <class T>\nstruct Stack <std::list <T> const&> : Stack <std::list <T> >\n{\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/LuaBridge.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2007, Nathan Reed\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n// All #include dependencies are listed here\n// instead of in the individual header files.\n//\n\n#define LUABRIDGE_MAJOR_VERSION 2\n#define LUABRIDGE_MINOR_VERSION 0\n#define LUABRIDGE_VERSION 200\n\n#ifndef LUA_VERSION_NUM\n#error \"Lua headers must be included prior to LuaBridge ones\"\n#endif\n\n\n#include <LuaBridge/detail/LuaHelpers.h>\n#include <LuaBridge/detail/TypeTraits.h>\n#include <LuaBridge/detail/TypeList.h>\n#include <LuaBridge/detail/FuncTraits.h>\n#include <LuaBridge/detail/Constructor.h>\n#include <LuaBridge/detail/ClassInfo.h>\n#include <LuaBridge/detail/LuaException.h>\n#include <LuaBridge/detail/LuaRef.h>\n#include <LuaBridge/detail/Iterator.h>\n#include <LuaBridge/detail/Userdata.h>\n#include <LuaBridge/detail/CFunctions.h>\n#include <LuaBridge/detail/Security.h>\n#include <LuaBridge/detail/Stack.h>\n#include <LuaBridge/detail/Namespace.h>\n"
  },
  {
    "path": "extern/LuaBridge/Map.h",
    "content": "// https://github.com/vinniefalco/LuaBridge\n//\n// Copyright 2018, Dmitry Tarakanov\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <LuaBridge/detail/Stack.h>\n\n#include <map>\n\nnamespace luabridge {\n\ntemplate <class K, class V>\nstruct Stack <std::map <K, V> >\n{\n  typedef std::map <K, V> Map;\n\n  static void push(lua_State* L, const Map& map)\n  {\n    lua_createtable (L, 0, static_cast <int> (map.size ()));\n    typedef typename Map::const_iterator ConstIter;\n    for (ConstIter i = map.begin(); i != map.end(); ++i)\n    {\n      Stack <K>::push (L, i->first);\n      Stack <V>::push (L, i->second);\n      lua_settable (L, -3);\n    }\n  }\n\n  static Map get(lua_State* L, int index)\n  {\n    if (!lua_istable(L, index))\n    {\n      luaL_error(L, \"#%d argments must be table\", index);\n    }\n\n    Map map;\n    int const absindex = lua_absindex (L, index);\n    lua_pushnil (L);\n    while (lua_next (L, absindex) != 0)\n    {\n      map.emplace (Stack <K>::get (L, -2), Stack <V>::get (L, -1));\n      lua_pop (L, 1);\n    }\n    return map;\n  }\n};\n\ntemplate <class K, class V>\nstruct Stack <std::map <K, V> const&> : Stack <std::map <K, V> >\n{\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/RefCountedObject.h",
    "content": "//==============================================================================\n/*\n  https://github.com/vinniefalco/LuaBridge\n\n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2004-11 by Raw Material Software Ltd.\n\n  This is a derivative work used by permission from part of\n  JUCE, available at http://www.rawaterialsoftware.com\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n\n  This file incorporates work covered by the following copyright and\n  permission notice:  \n\n    This file is part of the JUCE library - \"Jules' Utility Class Extensions\"\n    Copyright 2004-11 by Raw Material Software Ltd.\n*/\n//==============================================================================\n\n#pragma once\n\n//#define LUABRIDGE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1\n\n#include <LuaBridge/detail/TypeTraits.h>\n\n#include <cassert>\n\nnamespace luabridge {\n\n//==============================================================================\n/**\n  Adds reference-counting to an object.\n\n  To add reference-counting to a class, derive it from this class, and\n  use the RefCountedObjectPtr class to point to it.\n\n  e.g. @code\n  class MyClass : public RefCountedObjectType\n  {\n      void foo();\n\n      // This is a neat way of declaring a typedef for a pointer class,\n      // rather than typing out the full templated name each time..\n      typedef RefCountedObjectPtr<MyClass> Ptr;\n  };\n\n  MyClass::Ptr p = new MyClass();\n  MyClass::Ptr p2 = p;\n  p = 0;\n  p2->foo();\n  @endcode\n\n  Once a new RefCountedObjectType has been assigned to a pointer, be\n  careful not to delete the object manually.\n*/\ntemplate <class CounterType>\nclass RefCountedObjectType\n{\npublic:\n  //==============================================================================\n  /** Increments the object's reference count.\n\n      This is done automatically by the smart pointer, but is public just\n      in case it's needed for nefarious purposes.\n  */\n  inline void incReferenceCount() const\n  {\n    ++refCount;\n  }\n\n  /** Decreases the object's reference count.\n\n      If the count gets to zero, the object will be deleted.\n  */\n  inline void decReferenceCount() const\n  {\n    assert (getReferenceCount() > 0);\n\n    if (--refCount == 0)\n      delete this;\n  }\n\n  /** Returns the object's current reference count. */\n  inline int getReferenceCount() const\n  {\n    return static_cast <int> (refCount);\n  }\n\nprotected:\n  //==============================================================================\n  /** Creates the reference-counted object (with an initial ref count of zero). */\n  RefCountedObjectType() : refCount ()\n  {\n  }\n\n  /** Destructor. */\n  virtual ~RefCountedObjectType()\n  {\n    // it's dangerous to delete an object that's still referenced by something else!\n    assert (getReferenceCount() == 0);\n  }\n\nprivate:\n  //==============================================================================\n  CounterType mutable refCount;\n};\n\n//==============================================================================\n\n/** Non thread-safe reference counted object.\n\n    This creates a RefCountedObjectType that uses a non-atomic integer\n    as the counter.\n*/\ntypedef RefCountedObjectType <int> RefCountedObject;\n\n//==============================================================================\n/**\n    A smart-pointer class which points to a reference-counted object.\n\n    The template parameter specifies the class of the object you want to point\n    to - the easiest way to make a class reference-countable is to simply make\n    it inherit from RefCountedObjectType, but if you need to, you could roll\n    your own reference-countable class by implementing a pair of methods called\n    incReferenceCount() and decReferenceCount().\n\n    When using this class, you'll probably want to create a typedef to\n    abbreviate the full templated name - e.g.\n\n    @code\n    \n    typedef RefCountedObjectPtr <MyClass> MyClassPtr;\n    \n    @endcode\n*/\ntemplate <class ReferenceCountedObjectClass>\nclass RefCountedObjectPtr\n{\npublic:\n  /** The class being referenced by this pointer. */\n  typedef ReferenceCountedObjectClass ReferencedType;\n\n  //==============================================================================\n  /** Creates a pointer to a null object. */\n  inline RefCountedObjectPtr() : referencedObject (0)\n  {\n  }\n\n  /** Creates a pointer to an object.\n\n      This will increment the object's reference-count if it is non-null.\n  */\n  inline RefCountedObjectPtr (ReferenceCountedObjectClass* const refCountedObject)\n      : referencedObject (refCountedObject)\n  {\n    if (refCountedObject != 0)\n        refCountedObject->incReferenceCount();\n  }\n\n  /** Copies another pointer.\n      This will increment the object's reference-count (if it is non-null).\n  */\n  inline RefCountedObjectPtr (const RefCountedObjectPtr& other)\n      : referencedObject (other.referencedObject)\n  {\n    if (referencedObject != 0)\n        referencedObject->incReferenceCount();\n  }\n\n#if LUABRIDGE_COMPILER_SUPPORTS_MOVE_SEMANTICS\n  /** Takes-over the object from another pointer. */\n  inline RefCountedObjectPtr (RefCountedObjectPtr&& other)\n      : referencedObject (other.referencedObject)\n  {\n    other.referencedObject = 0;\n  }\n#endif\n\n  /** Copies another pointer.\n      This will increment the object's reference-count (if it is non-null).\n  */\n  template <class DerivedClass>\n  inline RefCountedObjectPtr (const RefCountedObjectPtr<DerivedClass>& other)\n      : referencedObject (static_cast <ReferenceCountedObjectClass*> (other.getObject()))\n  {\n    if (referencedObject != 0)\n      referencedObject->incReferenceCount();\n  }\n\n  /** Changes this pointer to point at a different object.\n\n      The reference count of the old object is decremented, and it might be\n      deleted if it hits zero. The new object's count is incremented.\n  */\n  RefCountedObjectPtr& operator= (const RefCountedObjectPtr& other)\n  {\n    return operator= (other.referencedObject);\n  }\n\n  /** Changes this pointer to point at a different object.\n\n      The reference count of the old object is decremented, and it might be\n      deleted if it hits zero. The new object's count is incremented.\n  */\n  template <class DerivedClass>\n  RefCountedObjectPtr& operator= (const RefCountedObjectPtr<DerivedClass>& other)\n  {\n    return operator= (static_cast <ReferenceCountedObjectClass*> (other.getObject()));\n  }\n\n#if LUABRIDGE_COMPILER_SUPPORTS_MOVE_SEMANTICS\n  /** Takes-over the object from another pointer. */\n  RefCountedObjectPtr& operator= (RefCountedObjectPtr&& other)\n  {\n    std::swap (referencedObject, other.referencedObject);\n    return *this;\n  }\n#endif\n\n  /** Changes this pointer to point at a different object.\n\n      The reference count of the old object is decremented, and it might be\n      deleted if it hits zero. The new object's count is incremented.\n  */\n  RefCountedObjectPtr& operator= (ReferenceCountedObjectClass* const newObject)\n  {\n    if (referencedObject != newObject)\n    {\n      if (newObject != 0)\n        newObject->incReferenceCount();\n\n      ReferenceCountedObjectClass* const oldObject = referencedObject;\n      referencedObject = newObject;\n\n      if (oldObject != 0)\n        oldObject->decReferenceCount();\n    }\n\n    return *this;\n  }\n\n  /** Destructor.\n\n      This will decrement the object's reference-count, and may delete it if it\n      gets to zero.\n  */\n  inline ~RefCountedObjectPtr()\n  {\n    if (referencedObject != 0)\n      referencedObject->decReferenceCount();\n  }\n\n  /** Returns the object that this pointer references.\n      The pointer returned may be zero, of course.\n  */\n  inline operator ReferenceCountedObjectClass*() const\n  {\n    return referencedObject;\n  }\n\n  // the -> operator is called on the referenced object\n  inline ReferenceCountedObjectClass* operator->() const\n  {\n    return referencedObject;\n  }\n\n  /** Returns the object that this pointer references.\n      The pointer returned may be zero, of course.\n  */\n  inline ReferenceCountedObjectClass* getObject() const\n  {\n    return referencedObject;\n  }\n\nprivate:\n  //==============================================================================\n  ReferenceCountedObjectClass* referencedObject;\n};\n\n/** Compares two ReferenceCountedObjectPointers. */\ntemplate <class ReferenceCountedObjectClass>\nbool operator== (const RefCountedObjectPtr<ReferenceCountedObjectClass>& object1, ReferenceCountedObjectClass* const object2)\n{\n    return object1.getObject() == object2;\n}\n\n/** Compares two ReferenceCountedObjectPointers. */\ntemplate <class ReferenceCountedObjectClass>\nbool operator== (const RefCountedObjectPtr<ReferenceCountedObjectClass>& object1, const RefCountedObjectPtr<ReferenceCountedObjectClass>& object2)\n{\n    return object1.getObject() == object2.getObject();\n}\n\n/** Compares two ReferenceCountedObjectPointers. */\ntemplate <class ReferenceCountedObjectClass>\nbool operator== (ReferenceCountedObjectClass* object1, RefCountedObjectPtr<ReferenceCountedObjectClass>& object2)\n{\n    return object1 == object2.getObject();\n}\n\n/** Compares two ReferenceCountedObjectPointers. */\ntemplate <class ReferenceCountedObjectClass>\nbool operator!= (const RefCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectClass* object2)\n{\n    return object1.getObject() != object2;\n}\n\n/** Compares two ReferenceCountedObjectPointers. */\ntemplate <class ReferenceCountedObjectClass>\nbool operator!= (const RefCountedObjectPtr<ReferenceCountedObjectClass>& object1, RefCountedObjectPtr<ReferenceCountedObjectClass>& object2)\n{\n    return object1.getObject() != object2.getObject();\n}\n\n/** Compares two ReferenceCountedObjectPointers. */\ntemplate <class ReferenceCountedObjectClass>\nbool operator!= (ReferenceCountedObjectClass* object1, RefCountedObjectPtr<ReferenceCountedObjectClass>& object2)\n{\n    return object1 != object2.getObject();\n}\n\n//==============================================================================\n\ntemplate <class T>\nstruct ContainerTraits <RefCountedObjectPtr <T> >\n{\n  typedef T Type;\n\n  static T* get (RefCountedObjectPtr <T> const& c)\n  {\n    return c.getObject ();\n  }\n};\n\n//==============================================================================\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/RefCountedPtr.h",
    "content": "//==============================================================================\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2019, Dmitry Tarakanov\n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2007, Nathan Reed\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <unordered_map>\n#include \"RefCountedObject.h\"\n\nnamespace luabridge {\n\n//==============================================================================\n/**\n  Support for our RefCountedPtr.\n*/\nstruct RefCountedPtrBase\n{\n  // Declaration of container for the refcounts\n  typedef std::unordered_map <const void *, int> RefCountsType;\n\nprotected:\n  RefCountsType& getRefCounts () const\n  {\n    static RefCountsType refcounts;\n    return refcounts ;\n  }\n};\n\n//==============================================================================\n/**\n  A reference counted smart pointer.\n\n  The api is compatible with boost::RefCountedPtr and std::RefCountedPtr, in the\n  sense that it implements a strict subset of the functionality.\n\n  This implementation uses a hash table to look up the reference count\n  associated with a particular pointer.\n\n  @tparam T The class type.\n\n  @todo Decompose RefCountedPtr using a policy. At a minimum, the underlying\n        reference count should be policy based (to support atomic operations)\n        and the delete behavior should be policy based (to support custom\n        disposal methods).\n\n  @todo Provide an intrusive version of RefCountedPtr.\n*/\ntemplate <class T>\nclass RefCountedPtr : private RefCountedPtrBase\n{\npublic:\n  template <typename Other>\n  struct rebind\n  {\n    typedef RefCountedPtr <Other> other;\n  };\n\n  /** Construct as nullptr or from existing pointer to T.\n\n      @param p The optional, existing pointer to assign from.\n  */\n  RefCountedPtr (T* p = 0) : m_p (p)\n  {\n    ++getRefCounts () [m_p];\n  }\n\n  /** Construct from another RefCountedPtr.\n\n      @param rhs The RefCountedPtr to assign from.\n  */\n  RefCountedPtr (RefCountedPtr <T> const& rhs) : m_p (rhs.get())\n  {\n    ++getRefCounts () [m_p];\n  }\n\n  /** Construct from a RefCountedPtr of a different type.\n\n      @invariant A pointer to U must be convertible to a pointer to T.\n\n      @param  rhs The RefCountedPtr to assign from.\n      @tparam U   The other object type.\n  */\n  template <typename U>\n  RefCountedPtr (RefCountedPtr <U> const& rhs) : m_p (static_cast <T*> (rhs.get()))\n  {\n    ++getRefCounts () [m_p];\n  }\n\n  /** Release the object.\n\n      If there are no more references then the object is deleted.\n  */\n  ~RefCountedPtr ()\n  {\n    reset();\n  }\n\n  /** Assign from another RefCountedPtr.\n\n      @param  rhs The RefCountedPtr to assign from.\n      @return     A reference to the RefCountedPtr.\n  */\n  RefCountedPtr <T>& operator= (RefCountedPtr <T> const& rhs)\n  {\n    if (m_p != rhs.m_p)\n    {\n      reset ();\n      m_p = rhs.m_p;\n      ++getRefCounts () [m_p];\n    }\n    return *this;\n  }\n\n  /** Assign from another RefCountedPtr of a different type.\n\n      @note A pointer to U must be convertible to a pointer to T.\n\n      @tparam U   The other object type.\n      @param  rhs The other RefCountedPtr to assign from.\n      @return     A reference to the RefCountedPtr.\n  */\n  template <typename U>\n  RefCountedPtr <T>& operator= (RefCountedPtr <U> const& rhs)\n  {\n    reset ();\n    m_p = static_cast <T*> (rhs.get());\n    ++getRefCounts () [m_p];\n    return *this;\n  }\n\n  /** Retrieve the raw pointer.\n\n      @return A pointer to the object.\n  */\n  T* get () const\n  {\n    return m_p;\n  }\n\n  /** Retrieve the raw pointer.\n\n      @return A pointer to the object.\n  */\n  T* operator* () const\n  {\n    return m_p;\n  }\n\n  /** Retrieve the raw pointer.\n\n      @return A pointer to the object.\n  */\n  T* operator-> () const\n  {\n    return m_p;\n  }\n\n  /** Determine the number of references.\n\n      @note This is not thread-safe.\n\n      @return The number of active references.\n  */\n  long use_count () const\n  {\n    return getRefCounts () [m_p];\n  }\n\n  /** Release the pointer.\n\n      The reference count is decremented. If the reference count reaches\n      zero, the object is deleted.\n  */\n  void reset ()\n  {\n    if (m_p != 0)\n    {\n      if (--getRefCounts () [m_p] <= 0)\n        delete m_p;\n\n      m_p = 0;\n    }\n  }\n\nprivate:\n  T* m_p;\n};\n\ntemplate <class T>\nbool operator== (const RefCountedPtr <T>& lhs, const RefCountedPtr <T>& rhs)\n{\n  return lhs.get () == rhs.get ();\n}\n\ntemplate <class T>\nbool operator!= (const RefCountedPtr <T>& lhs, const RefCountedPtr <T>& rhs)\n{\n  return lhs.get() != rhs.get();\n}\n\n//==============================================================================\n\n// forward declaration\ntemplate <class T>\nstruct ContainerTraits;\n\ntemplate <class T>\nstruct ContainerTraits <RefCountedPtr <T> >\n{\n  typedef T Type;\n\n  static T* get (RefCountedPtr <T> const& c)\n  {\n    return c.get ();\n  }\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/Vector.h",
    "content": "// https://github.com/vinniefalco/LuaBridge\n//\n// Copyright 2018, Dmitry Tarakanov\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <LuaBridge/detail/Stack.h>\n\n#include <vector>\n\nnamespace luabridge {\n\ntemplate <class T>\nstruct Stack <std::vector <T> >\n{\n  static void push(lua_State* L, std::vector <T> const& vector)\n  {\n    lua_createtable (L, static_cast <int> (vector.size ()), 0);\n    for (std::size_t i = 0; i < vector.size (); ++i)\n    {\n      lua_pushinteger (L, static_cast <lua_Integer> (i + 1));\n      Stack <T>::push (L, vector [i]);\n      lua_settable (L, -3);\n    }\n  }\n\n  static std::vector <T> get(lua_State* L, int index)\n  {\n    if (!lua_istable(L, index))\n    {\n      luaL_error(L, \"#%d argments must be table\", index);\n    }\n\n    std::vector <T> vector;\n    vector.reserve (static_cast <std::size_t> (get_length (L, index)));\n\n    int const absindex = lua_absindex (L, index);\n    lua_pushnil (L);\n    while (lua_next (L, absindex) != 0)\n    {\n      vector.push_back (Stack <T>::get (L, -1));\n      lua_pop (L, 1);\n    }\n    return vector;\n  }\n};\n\ntemplate <class T>\nstruct Stack <std::vector <T> const&> : Stack <std::vector <T> >\n{\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/CFunctions.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n\n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <string>\n\nnamespace luabridge {\n\n// We use a structure so we can define everything in the header.\n//\nstruct CFunc\n{\n  //----------------------------------------------------------------------------\n  /**\n      __index metamethod for a namespace or class static members.\n\n      This handles:\n        Retrieving functions and class static methods, stored in the metatable.\n        Reading global and class static data, stored in the __propget table.\n        Reading global and class properties, stored in the __propget table.\n  */\n  static int indexMetaMethod (lua_State* L)\n  {\n    int result = 0;\n    lua_getmetatable (L, 1);                // push metatable of arg1\n    for (;;)\n    {\n      lua_pushvalue (L, 2);                 // push key arg2\n      lua_rawget (L, -2);                   // lookup key in metatable\n      if (lua_isnil (L, -1))                // not found\n      {\n        lua_pop (L, 1);                     // discard nil\n        rawgetfield (L, -1, \"__propget\");   // lookup __propget in metatable\n        lua_pushvalue (L, 2);               // push key arg2\n        lua_rawget (L, -2);                 // lookup key in __propget\n        lua_remove (L, -2);                 // discard __propget\n        if (lua_iscfunction (L, -1))\n        {\n          lua_remove (L, -2);               // discard metatable\n          lua_pushvalue (L, 1);             // push arg1\n          lua_call (L, 1, 1);               // call cfunction\n          result = 1;\n          break;\n        }\n        else\n        {\n          assert (lua_isnil (L, -1));\n          lua_pop (L, 1);                   // discard nil and fall through\n        }\n      }\n      else\n      {\n        assert (lua_istable (L, -1) || lua_iscfunction (L, -1));\n        lua_remove (L, -2);\n        result = 1;\n        break;\n      }\n\n      rawgetfield (L, -1, \"__parent\");\n      if (lua_istable (L, -1))\n      {\n        // Remove metatable and repeat the search in __parent.\n        lua_remove (L, -2);\n      }\n      else\n      {\n        // Discard metatable and return nil.\n        assert (lua_isnil (L, -1));\n        lua_remove (L, -2);\n        result = 1;\n        break;\n      }\n    }\n\n    return result;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      __newindex metamethod for a namespace or class static members.\n\n      The __propset table stores proxy functions for assignment to:\n        Global and class static data.\n        Global and class properties.\n  */\n  static int newindexMetaMethod (lua_State* L)\n  {\n    int result = 0;\n    lua_getmetatable (L, 1);                // push metatable of arg1\n    for (;;)\n    {\n      rawgetfield (L, -1, \"__propset\");     // lookup __propset in metatable\n      assert (lua_istable (L, -1));\n      lua_pushvalue (L, 2);                 // push key arg2\n      lua_rawget (L, -2);                   // lookup key in __propset\n      lua_remove (L, -2);                   // discard __propset\n      if (lua_iscfunction (L, -1))          // ensure value is a cfunction\n      {\n        lua_remove (L, -2);                 // discard metatable\n        lua_pushvalue (L, 3);               // push new value arg3\n        lua_call (L, 1, 0);                 // call cfunction\n        result = 0;\n        break;\n      }\n      else\n      {\n        assert (lua_isnil (L, -1));\n        lua_pop (L, 1);\n      }\n\n      rawgetfield (L, -1, \"__parent\");\n      if (lua_istable (L, -1))\n      {\n        // Remove metatable and repeat the search in __parent.\n        lua_remove (L, -2);\n      }\n      else\n      {\n        assert (lua_isnil (L, -1));\n        lua_pop (L, 2);\n        result = luaL_error (L, \"no writable variable '%s'\", lua_tostring (L, 2));\n      }\n    }\n\n    return result;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      lua_CFunction to report an error writing to a read-only value.\n\n      The name of the variable is in the first upvalue.\n  */\n  static int readOnlyError (lua_State* L)\n  {\n    std::string s;\n\n    s = s + \"'\" + lua_tostring (L, lua_upvalueindex (1)) + \"' is read-only\";\n\n    return luaL_error (L, s.c_str ());\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      lua_CFunction to get a variable.\n\n      This is used for global variables or class static data members.\n\n      The pointer to the data is in the first upvalue.\n  */\n  template <class T>\n  static int getVariable (lua_State* L)\n  {\n    assert (lua_islightuserdata (L, lua_upvalueindex (1)));\n    T const* ptr = static_cast <T const*> (lua_touserdata (L, lua_upvalueindex (1)));\n    assert (ptr != 0);\n    Stack <T>::push (L, *ptr);\n    return 1;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      lua_CFunction to set a variable.\n\n      This is used for global variables or class static data members.\n\n      The pointer to the data is in the first upvalue.\n  */\n  template <class T>\n  static int setVariable (lua_State* L)\n  {\n    assert (lua_islightuserdata (L, lua_upvalueindex (1)));\n    T* ptr = static_cast <T*> (lua_touserdata (L, lua_upvalueindex (1)));\n    assert (ptr != 0);\n    *ptr = Stack <T>::get (L, 1);\n    return 0;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      lua_CFunction to call a function with a return value.\n\n      This is used for global functions, global properties, class static methods,\n      and class static properties.\n\n      The function pointer is in the first upvalue.\n  */\n  template <class FnPtr,\n    class ReturnType = typename FuncTraits <FnPtr>::ReturnType>\n    struct Call\n  {\n    typedef typename FuncTraits <FnPtr>::Params Params;\n    static int f (lua_State* L)\n    {\n      assert (isfulluserdata (L, lua_upvalueindex (1)));\n      FnPtr const& fnptr = *static_cast <FnPtr const*> (lua_touserdata (L, lua_upvalueindex (1)));\n      assert (fnptr != 0);\n      ArgList <Params> args (L);\n      Stack <typename FuncTraits <FnPtr>::ReturnType>::push (L, FuncTraits <FnPtr>::call (fnptr, args));\n      return 1;\n    }\n  };\n\n  //----------------------------------------------------------------------------\n  /**\n      lua_CFunction to call a function with no return value.\n\n      This is used for global functions, global properties, class static methods,\n      and class static properties.\n\n      The function pointer is in the first upvalue.\n  */\n  template <class FnPtr>\n  struct Call <FnPtr, void>\n  {\n    typedef typename FuncTraits <FnPtr>::Params Params;\n    static int f (lua_State* L)\n    {\n      assert (isfulluserdata (L, lua_upvalueindex (1)));\n      FnPtr const& fnptr = *static_cast <FnPtr const*> (lua_touserdata (L, lua_upvalueindex (1)));\n      assert (fnptr != 0);\n      ArgList <Params> args (L);\n      FuncTraits <FnPtr>::call (fnptr, args);\n      return 0;\n    }\n  };\n\n  //----------------------------------------------------------------------------\n  /**\n      lua_CFunction to call a class member function with a return value.\n\n      The member function pointer is in the first upvalue.\n      The class userdata object is at the top of the Lua stack.\n  */\n  template <class MemFnPtr,\n    class ReturnType = typename FuncTraits <MemFnPtr>::ReturnType>\n    struct CallMember\n  {\n    typedef typename FuncTraits <MemFnPtr>::ClassType T;\n    typedef typename FuncTraits <MemFnPtr>::Params Params;\n\n    static int f (lua_State* L)\n    {\n      assert (isfulluserdata (L, lua_upvalueindex (1)));\n      T* const t = Userdata::get <T> (L, 1, false);\n      MemFnPtr const& fnptr = *static_cast <MemFnPtr const*> (lua_touserdata (L, lua_upvalueindex (1)));\n      assert (fnptr != 0);\n      ArgList <Params, 2> args (L);\n      Stack <ReturnType>::push (L, FuncTraits <MemFnPtr>::call (t, fnptr, args));\n      return 1;\n    }\n  };\n\n  template <class MemFnPtr,\n    class ReturnType = typename FuncTraits <MemFnPtr>::ReturnType>\n    struct CallConstMember\n  {\n    typedef typename FuncTraits <MemFnPtr>::ClassType T;\n    typedef typename FuncTraits <MemFnPtr>::Params Params;\n\n    static int f (lua_State* L)\n    {\n      assert (isfulluserdata (L, lua_upvalueindex (1)));\n      T const* const t = Userdata::get <T> (L, 1, true);\n      MemFnPtr const& fnptr = *static_cast <MemFnPtr const*> (lua_touserdata (L, lua_upvalueindex (1)));\n      assert (fnptr != 0);\n      ArgList <Params, 2> args (L);\n      Stack <ReturnType>::push (L, FuncTraits <MemFnPtr>::call (t, fnptr, args));\n      return 1;\n    }\n  };\n\n  //----------------------------------------------------------------------------\n  /**\n      lua_CFunction to call a class member function with no return value.\n\n      The member function pointer is in the first upvalue.\n      The class userdata object is at the top of the Lua stack.\n  */\n  template <class MemFnPtr>\n  struct CallMember <MemFnPtr, void>\n  {\n    typedef typename FuncTraits <MemFnPtr>::ClassType T;\n    typedef typename FuncTraits <MemFnPtr>::Params Params;\n\n    static int f (lua_State* L)\n    {\n      assert (isfulluserdata (L, lua_upvalueindex (1)));\n      T* const t = Userdata::get <T> (L, 1, false);\n      MemFnPtr const& fnptr = *static_cast <MemFnPtr const*> (lua_touserdata (L, lua_upvalueindex (1)));\n      assert (fnptr != 0);\n      ArgList <Params, 2> args (L);\n      FuncTraits <MemFnPtr>::call (t, fnptr, args);\n      return 0;\n    }\n  };\n\n  template <class MemFnPtr>\n  struct CallConstMember <MemFnPtr, void>\n  {\n    typedef typename FuncTraits <MemFnPtr>::ClassType T;\n    typedef typename FuncTraits <MemFnPtr>::Params Params;\n\n    static int f (lua_State* L)\n    {\n      assert (isfulluserdata (L, lua_upvalueindex (1)));\n      T const* const t = Userdata::get <T> (L, 1, true);\n      MemFnPtr const& fnptr = *static_cast <MemFnPtr const*> (lua_touserdata (L, lua_upvalueindex (1)));\n      assert (fnptr != 0);\n      ArgList <Params, 2> args (L);\n      FuncTraits <MemFnPtr>::call (t, fnptr, args);\n      return 0;\n    }\n  };\n\n  //--------------------------------------------------------------------------\n  /**\n      lua_CFunction to call a class member lua_CFunction.\n\n      The member function pointer is in the first upvalue.\n      The class userdata object is at the top of the Lua stack.\n  */\n  template <class T>\n  struct CallMemberCFunction\n  {\n    static int f (lua_State* L)\n    {\n      assert (isfulluserdata (L, lua_upvalueindex (1)));\n      typedef int (T::*MFP)(lua_State* L);\n      T* const t = Userdata::get <T> (L, 1, false);\n      MFP const& fnptr = *static_cast <MFP const*> (lua_touserdata (L, lua_upvalueindex (1)));\n      assert (fnptr != 0);\n      return (t->*fnptr) (L);\n    }\n  };\n\n  template <class T>\n  struct CallConstMemberCFunction\n  {\n    static int f (lua_State* L)\n    {\n      assert (isfulluserdata (L, lua_upvalueindex (1)));\n      typedef int (T::*MFP)(lua_State* L);\n      T const* const t = Userdata::get <T> (L, 1, true);\n      MFP const& fnptr = *static_cast <MFP const*> (lua_touserdata (L, lua_upvalueindex (1)));\n      assert (fnptr != 0);\n      return (t->*fnptr) (L);\n    }\n  };\n\n  //--------------------------------------------------------------------------\n\n  // SFINAE Helpers\n\n  template <class MemFnPtr, bool isConst>\n  struct CallMemberFunctionHelper\n  {\n    static void add (lua_State* L, char const* name, MemFnPtr mf)\n    {\n      new (lua_newuserdata (L, sizeof (MemFnPtr))) MemFnPtr (mf);\n      lua_pushcclosure (L, &CallConstMember <MemFnPtr>::f, 1);\n      lua_pushvalue (L, -1);\n      rawsetfield (L, -5, name); // const table\n      rawsetfield (L, -3, name); // class table\n    }\n  };\n\n  template <class MemFnPtr>\n  struct CallMemberFunctionHelper <MemFnPtr, false>\n  {\n    static void add (lua_State* L, char const* name, MemFnPtr mf)\n    {\n      new (lua_newuserdata (L, sizeof (MemFnPtr))) MemFnPtr (mf);\n      lua_pushcclosure (L, &CallMember <MemFnPtr>::f, 1);\n      rawsetfield (L, -3, name); // class table\n    }\n  };\n\n  //--------------------------------------------------------------------------\n  /**\n      __gc metamethod for a class.\n  */\n  template <class C>\n  static int gcMetaMethod (lua_State* L)\n  {\n    Userdata* const ud = Userdata::getExact <C> (L, 1);\n    ud->~Userdata ();\n    return 0;\n  }\n\n  //--------------------------------------------------------------------------\n  /**\n      lua_CFunction to get a class data member.\n\n      The pointer-to-member is in the first upvalue.\n      The class userdata object is at the top of the Lua stack.\n  */\n  template <class C, typename T>\n  static int getProperty (lua_State* L)\n  {\n    C const* const c = Userdata::get <C> (L, 1, true);\n    T C::** mp = static_cast <T C::**> (lua_touserdata (L, lua_upvalueindex (1)));\n    Stack <T>::push (L, c->**mp);\n    return 1;\n  }\n\n  //--------------------------------------------------------------------------\n  /**\n      lua_CFunction to set a class data member.\n\n      The pointer-to-member is in the first upvalue.\n      The class userdata object is at the top of the Lua stack.\n  */\n  template <class C, typename T>\n  static int setProperty (lua_State* L)\n  {\n    C* const c = Userdata::get <C> (L, 1, false);\n    T C::** mp = static_cast <T C::**> (lua_touserdata (L, lua_upvalueindex (1)));\n    c->**mp = Stack <T>::get (L, 2);\n    return 0;\n  }\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/ClassInfo.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\nnamespace luabridge {\n\n/** Unique Lua registry keys for a class.\n\n    Each registered class inserts three keys into the registry, whose\n    values are the corresponding static, class, and const metatables. This\n    allows a quick and reliable lookup for a metatable from a template type.\n*/\ntemplate <class T>\nclass ClassInfo\n{\npublic:\n  /** Get the key for the static table.\n\n      The static table holds the static data members, static properties, and\n      static member functions for a class.\n  */\n  static void const* getStaticKey ()\n  {\n    static char value;\n    return &value;\n  }\n\n  /** Get the key for the class table.\n\n      The class table holds the data members, properties, and member functions\n      of a class. Read-only data and properties, and const member functions are\n      also placed here (to save a lookup in the const table).\n  */\n  static void const* getClassKey ()\n  {\n    static char value;\n    return &value;\n  }\n\n  /** Get the key for the const table.\n\n      The const table holds read-only data members and properties, and const\n      member functions of a class.\n  */\n  static void const* getConstKey ()\n  {\n    static char value;\n    return &value;\n  }\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/Constructor.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2007, Nathan Reed\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\nnamespace luabridge {\n\n/*\n* Constructor generators.  These templates allow you to call operator new and\n* pass the contents of a type/value list to the Constructor.  Like the\n* function pointer containers, these are only defined up to 8 parameters.\n*/\n\n/** Constructor generators.\n\n    These templates call operator new with the contents of a type/value\n    list passed to the Constructor with up to 8 parameters. Two versions\n    of call() are provided. One performs a regular new, the other performs\n    a placement new.\n*/\ntemplate <class T, typename List>\nstruct Constructor {};\n\ntemplate <class T>\nstruct Constructor <T, None>\n{\n  static T* call (TypeListValues <None> const&)\n  {\n    return new T;\n  }\n  static T* call (void* mem, TypeListValues <None> const&)\n  {\n    return new (mem) T;\n  }\n};\n\ntemplate <class T, class P1>\nstruct Constructor <T, TypeList <P1> >\n{\n  static T* call (const TypeListValues<TypeList <P1> > &tvl)\n  {\n    return new T(tvl.hd);\n  }\n  static T* call (void* mem, const TypeListValues<TypeList <P1> > &tvl)\n  {\n    return new (mem) T(tvl.hd);\n  }\n};\n\ntemplate <class T, class P1, class P2>\nstruct Constructor <T, TypeList <P1, TypeList <P2> > >\n{\n  static T* call (const TypeListValues<TypeList <P1, TypeList <P2> > > &tvl)\n  {\n    return new T(tvl.hd, tvl.tl.hd);\n  }\n  static T* call (void* mem, const TypeListValues<TypeList <P1, TypeList <P2> > > &tvl)\n  {\n    return new (mem) T(tvl.hd, tvl.tl.hd);\n  }\n};\n\ntemplate <class T, class P1, class P2, class P3>\nstruct Constructor <T, TypeList <P1, TypeList <P2, TypeList <P3> > > >\n{\n  static T* call (const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3> > > > &tvl)\n  {\n    return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n  static T* call (void* mem, const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3> > > > &tvl)\n  {\n    return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class P1, class P2, class P3, class P4>\nstruct Constructor <T, TypeList <P1, TypeList <P2, TypeList <P3,\n  TypeList <P4> > > > >\n{\n  static T* call (const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4> > > > > &tvl)\n  {\n    return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n  static T* call (void* mem, const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4> > > > > &tvl)\n  {\n    return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class P1, class P2, class P3, class P4,\n  class P5>\nstruct Constructor <T, TypeList <P1, TypeList <P2, TypeList <P3,\n  TypeList <P4, TypeList <P5> > > > > >\n{\n  static T* call (const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4, TypeList <P5> > > > > > &tvl)\n  {\n    return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd);\n  }\n  static T* call (void* mem, const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4, TypeList <P5> > > > > > &tvl)\n  {\n    return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class P1, class P2, class P3, class P4,\n  class P5, class P6>\nstruct Constructor <T, TypeList <P1, TypeList <P2, TypeList <P3,\n  TypeList <P4, TypeList <P5, TypeList <P6> > > > > > >\n{\n  static T* call (const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6> > > > > > > &tvl)\n  {\n    return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n  static T* call (void* mem, const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6> > > > > > > &tvl)\n  {\n    return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class P1, class P2, class P3, class P4,\n  class P5, class P6, class P7>\nstruct Constructor <T, TypeList <P1, TypeList <P2, TypeList <P3,\n  TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7> > > > > > > >\n{\n  static T* call (const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6,\n    TypeList <P7> > > > > > > > &tvl)\n  {\n    return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n  static T* call (void* mem, const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6,\n    TypeList <P7> > > > > > > > &tvl)\n  {\n    return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class P1, class P2, class P3, class P4,\n  class P5, class P6, class P7, class P8>\nstruct Constructor <T, TypeList <P1, TypeList <P2, TypeList <P3,\n  TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7, \n  TypeList <P8> > > > > > > > >\n{\n  static T* call (const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6,\n    TypeList <P7, TypeList <P8> > > > > > > > > &tvl)\n  {\n    return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n  static T* call (void* mem, const TypeListValues<TypeList <P1, TypeList <P2,\n    TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6,\n    TypeList <P7, TypeList <P8> > > > > > > > > &tvl)\n  {\n    return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/FuncTraits.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\nnamespace luabridge {\n\n/**\n  Since the throw specification is part of a function signature, the FuncTraits\n  family of templates needs to be specialized for both types. The\n  LUABRIDGE_THROWSPEC macro controls whether we use the 'throw ()' form, or\n  'noexcept' (if C++11 is available) to distinguish the functions.\n*/\n#if defined (__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__clang__) || defined(__GNUC__) || \\\n    (defined (_MSC_VER) && (_MSC_VER >= 1700))\n// Do not define LUABRIDGE_THROWSPEC since the Xcode and gcc  compilers do not\n// distinguish the throw specification in the function signature.\n#else\n// Visual Studio 10 and earlier pay too much mind to useless throw() spec.\n//\n# define LUABRIDGE_THROWSPEC throw()\n#endif\n\n//==============================================================================\n/**\n    Traits for function pointers.\n\n    There are three types of functions: global, non-const member, and const\n    member. These templates determine the type of function, which class type it\n    belongs to if it is a class member, the const-ness if it is a member\n    function, and the type information for the return value and argument list.\n\n    Expansions are provided for functions with up to 8 parameters. This can be\n    manually extended, or expanded to an arbitrary amount using C++11 features.\n*/\ntemplate <class MemFn, class D = MemFn>\nstruct FuncTraits\n{\n};\n\n/* Ordinary function pointers. */\n\ntemplate <class R, class D>\nstruct FuncTraits <R (*) (), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef None Params;\n  static R call (D fp, TypeListValues <Params>)\n  {\n    return fp ();\n  }\n};\n\ntemplate <class R, class P1, class D>\nstruct FuncTraits <R (*) (P1), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1> Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class D>\nstruct FuncTraits <R (*) (P1, P2), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2> > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class D>\nstruct FuncTraits <R (*) (P1, P2, P3), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3> > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4> > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4, P5), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5> > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4, P5, P6), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5,  TypeList <P6> > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4, P5, P6, P7), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7> > > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4, P5, P6, P7, P8), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7, TypeList <P8> > > > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\n/* Windows: WINAPI (a.k.a. __stdcall) function pointers. */\n\n#ifdef _M_IX86 // Windows 32bit only\n\ntemplate <class R, class D>\nstruct FuncTraits <R (__stdcall *) (), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef None Params;\n  static R call (D fp, TypeListValues <Params>)\n  {\n    return fp ();\n  }\n};\n\ntemplate <class R, class P1, class D>\nstruct FuncTraits <R (__stdcall *) (P1), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1> Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class D>\nstruct FuncTraits <R (__stdcall *) (P1, P2), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2> > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class D>\nstruct FuncTraits <R (__stdcall *) (P1, P2, P3), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3> > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class D>\nstruct FuncTraits <R (__stdcall *) (P1, P2, P3, P4), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4> > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class D>\nstruct FuncTraits <R (__stdcall *) (P1, P2, P3, P4, P5), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5> > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class D>\nstruct FuncTraits <R (__stdcall *) (P1, P2, P3, P4, P5, P6), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6> > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class D>\nstruct FuncTraits <R (__stdcall *) (P1, P2, P3, P4, P5, P6, P7), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7> > > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class D>\nstruct FuncTraits <R (__stdcall *) (P1, P2, P3, P4, P5, P6, P7, P8), D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7, TypeList <P8> > > > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\n#endif // _M_IX86\n\n/* Non-const member function pointers. */\n\ntemplate <class T, class R, class D>\nstruct FuncTraits <R (T::*) (), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef None Params;\n  static R call (T* obj, D fp, TypeListValues <Params>)\n  {\n    return (obj->*fp)();\n  }\n};\n\ntemplate <class T, class R, class P1, class D>\nstruct FuncTraits <R (T::*) (P1), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1> Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class D>\nstruct FuncTraits <R (T::*) (P1, P2), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2> > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3> > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4> > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5> > > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6> > > > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6, P7), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7> > > > > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6, P7, P8), D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7, TypeList <P8> > > > > > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\n/* Const member function pointers. */\n\ntemplate <class T, class R, class D>\nstruct FuncTraits <R (T::*) () const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef None Params;\n  static R call (T const* obj, D fp, TypeListValues <Params>)\n  {\n    return (obj->*fp)();\n  }\n};\n\ntemplate <class T, class R, class P1, class D>\nstruct FuncTraits <R (T::*) (P1) const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1> Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class D>\nstruct FuncTraits <R (T::*) (P1, P2) const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2> > Params;\n  static R call (T const* obj, R (T::*fp) (P1, P2) const,\n    TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3) const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3> > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4) const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4> > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5) const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5> > > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6) const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6> > > > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6, P7) const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7> > > > > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6, P7, P8) const, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7, TypeList <P8> > > > > > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\n#if defined (LUABRIDGE_THROWSPEC)\n\n/* Ordinary function pointers. */\n\ntemplate <class R, class D>\nstruct FuncTraits <R (*) () LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef None Params;\n  static R call (D fp, TypeListValues <Params> const&)\n  {\n    return fp ();\n  }\n};\n\ntemplate <class R, class P1, class D>\nstruct FuncTraits <R (*) (P1) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1> Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class D>\nstruct FuncTraits <R (*) (P1, P2) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2> > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class D>\nstruct FuncTraits <R (*) (P1, P2, P3) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3> > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4> > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4, P5) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5> > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4, P5, P6) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5,  TypeList <P6> > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4, P5, P6, P7) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7> > > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class D>\nstruct FuncTraits <R (*) (P1, P2, P3, P4, P5, P6, P7, P8) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = false;\n  typedef D DeclType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7, TypeList <P8> > > > > > > > Params;\n  static R call (D fp, TypeListValues <Params> tvl)\n  {\n    return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\n/* Non-const member function pointers with THROWSPEC. */\n\ntemplate <class T, class R, class D>\nstruct FuncTraits <R (T::*) () LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef None Params;\n  static R call (T* obj, D fp, TypeListValues <Params> const&)\n  {\n    return (obj->*fp)();\n  }\n};\n\ntemplate <class T, class R, class P1, class D>\nstruct FuncTraits <R (T::*) (P1) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1> Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class D>\nstruct FuncTraits <R (T::*) (P1, P2) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2> > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3> > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4> > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5> > > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6> > > > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6, P7) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7> > > > > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6, P7, P8) LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = false;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7, TypeList <P8> > > > > > > > Params;\n  static R call (T* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\n/* Const member function pointers with THROWSPEC. */\n\ntemplate <class T, class R, class D>\nstruct FuncTraits <R (T::*) () const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef None Params;\n  static R call (T const* obj, D fp, TypeListValues <Params>)\n  {\n    return (obj->*fp)();\n  }\n};\n\ntemplate <class T, class R, class P1, class D>\nstruct FuncTraits <R (T::*) (P1) const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1> Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class D>\nstruct FuncTraits <R (T::*) (P1, P2) const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2> > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3) const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3> > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4) const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4> > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5) const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5> > > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd,\n      tvl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6) const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6> > > > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6, P7) const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7> > > > > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\ntemplate <class T, class R, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class D>\nstruct FuncTraits <R (T::*) (P1, P2, P3, P4, P5, P6, P7, P8) const LUABRIDGE_THROWSPEC, D>\n{\n  static bool const isMemberFunction = true;\n  static bool const isConstMemberFunction = true;\n  typedef D DeclType;\n  typedef T ClassType;\n  typedef R ReturnType;\n  typedef TypeList <P1, TypeList <P2, TypeList <P3, TypeList <P4, TypeList <P5, TypeList <P6, TypeList <P7, TypeList <P8> > > > > > > > Params;\n  static R call (T const* obj, D fp, TypeListValues <Params> tvl)\n  {\n    return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd);\n  }\n};\n\n#endif\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/Iterator.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <LuaBridge/detail/LuaRef.h>\n\n#include <utility>\n\nnamespace luabridge {\n\n/** Allows table iteration.\n*/\nclass Iterator\n{\nprivate:\n  lua_State* m_L;\n  LuaRef m_table;\n  LuaRef m_key;\n  LuaRef m_value;\n\n  void next ()\n  {\n    m_table.push ();\n    m_key.push ();\n    if (lua_next (m_L, -2))\n    {\n      m_value.pop ();\n      m_key.pop ();\n    }\n    else\n    {\n      m_key = Nil ();\n      m_value = Nil ();\n    }\n    lua_pop (m_L, 1);\n  }\n\npublic:\n  explicit Iterator (const LuaRef& table, bool isEnd = false)\n    : m_L (table.state ())\n    , m_table (table)\n    , m_key (table.state ()) // m_key is nil\n    , m_value (table.state ()) // m_value is nil\n  {\n    if (!isEnd)\n    {\n      next (); // get the first (key, value) pair from table\n    }\n  }\n\n  lua_State* state () const\n  {\n    return m_L;\n  }\n\n  std::pair<LuaRef, LuaRef> operator* () const\n  {\n    return std::make_pair (m_key, m_value);\n  }\n\n  LuaRef operator-> () const\n  {\n    return m_value;\n  }\n\n  bool operator!= (const Iterator& rhs) const\n  {\n    assert (m_L == rhs.m_L);\n    return !m_table.rawequal (rhs.m_table) || !m_key.rawequal (rhs.m_key);\n  }\n\n  Iterator& operator++ ()\n  {\n    if (isNil())\n    {\n      // if the iterator reaches the end, do nothing\n      return *this;\n    }\n    else\n    {\n      next();\n      return *this;\n    }\n  }\n\n  bool isNil () const\n  {\n    return m_key.isNil ();\n  }\n\n  LuaRef key () const\n  {\n    return m_key;\n  }\n\n  LuaRef value () const\n  {\n    return m_value;\n  }\n\nprivate:\n  // Don't use postfix increment, it is less efficient\n  Iterator operator++ (int);\n};\n\nclass Range\n{\n  Iterator m_begin;\n  Iterator m_end;\n\npublic:\n  Range (const Iterator& begin, const Iterator& end)\n    : m_begin (begin)\n    , m_end (end)\n  {\n  }\n\n  const Iterator& begin () const { return m_begin; }\n  const Iterator& end () const { return m_end; }\n};\n\ninline Range pairs(const LuaRef& table)\n{\n  return Range (Iterator (table, false), Iterator (table, true));\n}\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/LuaException.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2008, Nigel Atkinson <suprapilot+LuaCode@gmail.com>\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <exception>\n#include <string>\n\nnamespace luabridge {\n\nclass LuaException : public std::exception \n{\nprivate:\n  lua_State* m_L;\n  std::string m_what;\n\npublic:\n  //----------------------------------------------------------------------------\n  /**\n      Construct a LuaException after a lua_pcall().\n  */\n  LuaException (lua_State* L, int /*code*/)\n    : m_L (L)\n  {\n    whatFromStack ();\n  }\n\n  //----------------------------------------------------------------------------\n\n  LuaException (lua_State *L,\n                char const*,\n                char const*,\n                long)\n    : m_L (L)\n  {\n    whatFromStack ();\n  }\n\n  //----------------------------------------------------------------------------\n\n  ~LuaException() throw ()\n  {\n  }\n\n  //----------------------------------------------------------------------------\n\n  char const* what() const throw ()\n  {\n    return m_what.c_str();\n  }\n\n  //============================================================================\n  /**\n      Throw an exception.\n\n      This centralizes all the exceptions thrown, so that we can set\n      breakpoints before the stack is unwound, or otherwise customize the\n      behavior.\n  */\n  template <class Exception>\n  static void Throw (Exception e)\n  {\n    throw e;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Wrapper for lua_pcall that throws.\n  */\n  static void pcall (lua_State* L, int nargs = 0, int nresults = 0, int msgh = 0)\n  {\n    int code = lua_pcall (L, nargs, nresults, msgh);\n\n    if (code != LUABRIDGE_LUA_OK)\n      Throw (LuaException (L, code));\n  }\n\n  //----------------------------------------------------------------------------\n\nprotected:\n  void whatFromStack ()\n  {\n    if (lua_gettop (m_L) > 0)\n    {\n      char const* s = lua_tostring (m_L, -1);\n      m_what = s ? s : \"\";\n    }\n    else\n    {\n      // stack is empty\n      m_what = \"missing error\";\n    }\n  }\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/LuaHelpers.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2007, Nathan Reed\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <cassert>\n\nnamespace luabridge {\n\n// These are for Lua versions prior to 5.2.0.\n//\n#if LUA_VERSION_NUM < 502\ninline int lua_absindex (lua_State* L, int idx)\n{\n  if (idx > LUA_REGISTRYINDEX && idx < 0)\n    return lua_gettop (L) + idx + 1;\n  else\n    return idx;\n}\n\ninline void lua_rawgetp (lua_State* L, int idx, void const* p)\n{\n  idx = lua_absindex (L, idx);\n  lua_pushlightuserdata (L, const_cast <void*> (p));\n  lua_rawget (L,idx);\n}\n\ninline void lua_rawsetp (lua_State* L, int idx, void const* p)\n{\n  idx = lua_absindex (L, idx);\n  lua_pushlightuserdata (L, const_cast <void*> (p));\n  // put key behind value\n  lua_insert (L, -2);\n  lua_rawset (L, idx);\n}\n\n#define LUA_OPEQ 1\n#define LUA_OPLT 2\n#define LUA_OPLE 3\n\ninline int lua_compare (lua_State* L, int idx1, int idx2, int op)\n{\n  switch (op)\n  {\n  case LUA_OPEQ:\n    return lua_equal (L, idx1, idx2);\n    break;\n\n  case LUA_OPLT:\n    return lua_lessthan (L, idx1, idx2);\n    break;\n\n  case LUA_OPLE:\n    return lua_equal (L, idx1, idx2) || lua_lessthan (L, idx1, idx2);\n    break;\n\n  default:\n    return 0;\n  };\n}\n\ninline int get_length (lua_State* L, int idx)\n{\n  return int (lua_objlen (L, idx));\n}\n\n#else\ninline int get_length (lua_State* L, int idx)\n{\n  lua_len (L, idx);\n  int len = int (luaL_checknumber (L, -1));\n  lua_pop (L, 1);\n  return len;\n}\n\n#endif\n\n#ifndef LUA_OK\n# define LUABRIDGE_LUA_OK 0\n#else\n# define LUABRIDGE_LUA_OK LUA_OK\n#endif\n\n/** Get a table value, bypassing metamethods.\n*/  \ninline void rawgetfield (lua_State* L, int index, char const* key)\n{\n  assert (lua_istable (L, index));\n  index = lua_absindex (L, index);\n  lua_pushstring (L, key);\n  lua_rawget (L, index);\n}\n\n/** Set a table value, bypassing metamethods.\n*/  \ninline void rawsetfield (lua_State* L, int index, char const* key)\n{\n  assert (lua_istable (L, index));\n  index = lua_absindex (L, index);\n  lua_pushstring (L, key);\n  lua_insert (L, -2);\n  lua_rawset (L, index);\n}\n\n/** Returns true if the value is a full userdata (not light).\n*/\ninline bool isfulluserdata (lua_State* L, int index)\n{\n  return lua_isuserdata (L, index) && !lua_islightuserdata (L, index);\n}\n\n/** Test lua_State objects for global equality.\n\n    This can determine if two different lua_State objects really point\n    to the same global state, such as when using coroutines.\n\n    @note This is used for assertions.\n*/\ninline bool equalstates (lua_State* L1, lua_State* L2)\n{\n  return lua_topointer (L1, LUA_REGISTRYINDEX) ==\n         lua_topointer (L2, LUA_REGISTRYINDEX);\n}\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/LuaRef.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n\n  Copyright 2018, Dmitry Tarakanov\n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2008, Nigel Atkinson <suprapilot+LuaCode@gmail.com>\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <LuaBridge/detail/LuaException.h>\n#include <LuaBridge/detail/Stack.h>\n\n#include <iostream>\n#include <string>\n#include <vector>\n#include <map>\n\nnamespace luabridge {\n\n//------------------------------------------------------------------------------\n/**\n    Type tag for representing LUA_TNIL.\n\n    Construct one of these using `Nil()` to represent a Lua nil. This is faster\n    than creating a reference in the registry to nil. Example:\n\n        LuaRef t (LuaRef::createTable (L));\n        ...\n        t [\"k\"] = Nil(); // assign nil\n*/\nstruct Nil\n{\n};\n\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for Nil.\n*/\ntemplate <>\nstruct Stack <Nil>\n{\n  static void push (lua_State* L, Nil)\n  {\n    lua_pushnil (L);\n  }\n};\n\n/**\n * Base class for LuaRef and table value proxy classes.\n */\ntemplate <class Impl, class LuaRef>\nclass LuaRefBase\n{\nprotected:\n  //----------------------------------------------------------------------------\n  /**\n      Pop the Lua stack.\n\n      Pops the specified number of stack items on destruction. We use this\n      when returning objects, to avoid an explicit temporary variable, since\n      the destructor executes after the return statement. For example:\n\n          template <class U>\n          U cast (lua_State* L)\n          {\n            StackPop p (L, 1);\n            ...\n            return U (); // dtor called after this line\n          }\n\n      @note The `StackPop` object must always be a named local variable.\n  */\n  class StackPop\n  {\n  public:\n    /** Create a StackPop object.\n\n        @param count The number of stack entries to pop on destruction.\n    */\n    StackPop (lua_State* L, int count)\n      : m_L (L)\n      , m_count (count)\n    {\n    }\n\n    ~StackPop ()\n    {\n      lua_pop (m_L, m_count);\n    }\n\n  private:\n    lua_State* m_L;\n    int m_count;\n  };\n\n  friend struct Stack <LuaRef>;\n\n  //----------------------------------------------------------------------------\n  /**\n      Type tag for stack construction.\n  */\n  struct FromStack { };\n\n  LuaRefBase (lua_State* L)\n    : m_L (L)\n  {\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Create a reference to this ref.\n\n      This is used internally.\n  */\n  int createRef () const\n  {\n    impl ().push ();\n    return luaL_ref (m_L, LUA_REGISTRYINDEX);\n  }\n\npublic:\n  //----------------------------------------------------------------------------\n  /**\n      converts to a string using luas tostring function\n  */\n  std::string tostring() const\n  {\n    lua_getglobal (m_L, \"tostring\");\n    impl ().push ();\n    lua_call (m_L, 1, 1);\n    const char* str = lua_tostring (m_L, -1);\n    lua_pop (m_L, 1);\n    return std::string(str);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Print a text description of the value to a stream.\n\n      This is used for diagnostics.\n  */\n  void print (std::ostream& os) const\n  {\n    switch (type ())\n    {\n    case LUA_TNIL:\n      os << \"nil\";\n      break;\n\n    case LUA_TNUMBER:\n      os << cast <lua_Number> ();\n      break;\n\n    case LUA_TBOOLEAN:\n      os << (cast <bool> () ? \"true\" : \"false\");\n      break;\n\n    case LUA_TSTRING:\n      os << '\"' << cast <std::string> () << '\"';\n      break;\n\n    case LUA_TTABLE:\n      os << \"table: \" << tostring();\n      break;\n\n    case LUA_TFUNCTION:\n      os << \"function: \" << tostring();\n      break;\n\n    case LUA_TUSERDATA:\n      os << \"userdata: \" << tostring();\n      break;\n\n    case LUA_TTHREAD:\n      os << \"thread: \" << tostring();\n      break;\n\n    case LUA_TLIGHTUSERDATA:\n      os << \"lightuserdata: \" << tostring();\n      break;\n\n    default:\n      os << \"unknown\";\n      break;\n    }\n  }\n\n  //------------------------------------------------------------------------------\n  /**\n      Write a LuaRef to a stream.\n\n      This allows LuaRef and table proxies to work with streams.\n  */\n  friend std::ostream& operator<< (std::ostream& os, LuaRefBase const& ref)\n  {\n    ref.print (os);\n    return os;\n  }\n\n  //============================================================================\n  //\n  // This group of member functions is mirrored in Proxy\n  //\n\n  /** Retrieve the lua_State associated with the reference.\n  */\n  lua_State* state () const\n  {\n    return m_L;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Place the object onto the Lua stack.\n  */\n  void push (lua_State* L) const\n  {\n    assert (equalstates (L, m_L));\n    (void) L;\n    impl ().push ();\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Pop the top of Lua stack and assign the ref to m_ref\n  */\n  void pop (lua_State* L)\n  {\n    assert (equalstates (L, m_L));\n    (void) L;\n    impl ().pop ();\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Determine the object type.\n\n      The return values are the same as for `lua_type`.\n  */\n  /** @{ */\n  int type () const\n  {\n    impl ().push ();\n    StackPop p (m_L, 1);\n    return lua_type (m_L, -1);\n  }\n\n  // should never happen\n  // bool isNone () const { return m_ref == LUA_NOREF; }\n\n  bool isNil () const { return type () == LUA_TNIL; }\n  bool isBool () const { return type () == LUA_TBOOLEAN; }\n  bool isNumber () const { return type () == LUA_TNUMBER; }\n  bool isString () const { return type () == LUA_TSTRING; }\n  bool isTable () const { return type () == LUA_TTABLE; }\n  bool isFunction () const { return type () == LUA_TFUNCTION; }\n  bool isUserdata () const { return type () == LUA_TUSERDATA; }\n  bool isThread () const { return type () == LUA_TTHREAD; }\n  bool isLightUserdata () const { return type () == LUA_TLIGHTUSERDATA; }\n\n  /** @} */\n\n  //----------------------------------------------------------------------------\n  /**\n      Perform an explicit conversion.\n  */\n  template <class T>\n  T cast () const\n  {\n    StackPop p (m_L, 1);\n    impl ().push ();\n\n    // lua_gettop is used because Userdata::getClass() doesn't handle\n    // negative stack indexes.\n    //\n    return Stack <T>::get (m_L, lua_gettop (m_L));\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Universal implicit conversion operator.\n\n      NOTE: Visual Studio 2010 and 2012 have a bug where this function\n            is not used. See:\n\n      http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/e30b2664-a92d-445c-9db2-e8e0fbde2014\n      https://connect.microsoft.com/VisualStudio/feedback/details/771509/correct-code-doesnt-compile\n\n          // This code snippet fails to compile in vs2010,vs2012\n          struct S {\n            template <class T> operator T () const { return T (); }\n          };\n          int main () {\n            S () || false;\n            return 0;\n          }\n  */\n  template <class T>\n  operator T () const\n  {\n    return cast <T> ();\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Universal comparison operators.\n  */\n  /** @{ */\n  template <class T>\n  bool operator== (T rhs) const\n  {\n    StackPop p (m_L, 2);\n    impl ().push ();\n    Stack <T>::push (m_L, rhs);\n    return lua_compare (m_L, -2, -1, LUA_OPEQ) == 1;\n  }\n\n  template <class T>\n  bool operator< (T rhs) const\n  {\n    StackPop p (m_L, 2);\n    impl ().push ();;\n    Stack <T>::push (m_L, rhs);\n    int lhsType = lua_type (m_L, -2);\n    int rhsType = lua_type (m_L, -1);\n    if (lhsType != rhsType)\n    {\n      return lhsType < rhsType;\n    }\n    return lua_compare (m_L, -2, -1, LUA_OPLT) == 1;\n  }\n\n  template <class T>\n  bool operator<= (T rhs) const\n  {\n    StackPop p (m_L, 2);\n    impl ().push ();;\n    Stack <T>::push (m_L, rhs);\n    int lhsType = lua_type (m_L, -2);\n    int rhsType = lua_type (m_L, -1);\n    if (lhsType != rhsType)\n    {\n      return lhsType <= rhsType;\n    }\n    return lua_compare (m_L, -2, -1, LUA_OPLE) == 1;\n  }\n\n  template <class T>\n  bool operator> (T rhs) const\n  {\n    StackPop p (m_L, 2);\n    impl ().push ();;\n    Stack <T>::push (m_L, rhs);\n    int lhsType = lua_type (m_L, -2);\n    int rhsType = lua_type (m_L, -1);\n    if (lhsType != rhsType)\n    {\n      return lhsType > rhsType;\n    }\n    return lua_compare (m_L, -1, -2, LUA_OPLT) == 1;\n  }\n\n  template <class T>\n  bool operator>= (T rhs) const\n  {\n    StackPop p (m_L, 2);\n    impl ().push ();;\n    Stack <T>::push (m_L, rhs);\n    int lhsType = lua_type (m_L, -2);\n    int rhsType = lua_type (m_L, -1);\n    if (lhsType != rhsType)\n    {\n      return lhsType >= rhsType;\n    }\n    return lua_compare (m_L, -1, -2, LUA_OPLE) == 1;\n  }\n\n  template <class T>\n  bool rawequal (T rhs) const\n  {\n    StackPop p (m_L, 2);\n    impl ().push ();;\n    Stack <T>::push (m_L, rhs);\n    return lua_rawequal (m_L, -1, -2) == 1;\n  }\n  /** @} */\n\n  //----------------------------------------------------------------------------\n  /**\n      Append a value to the table.\n\n      If the table is a sequence this will add another element to it.\n  */\n  template <class T>\n  void append (T v) const\n  {\n    impl ().push ();;\n    Stack <T>::push (m_L, v);\n    luaL_ref (m_L, -2);\n    lua_pop (m_L, 1);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Call the length operator.\n\n      This is identical to applying the Lua # operator.\n  */\n  int length () const\n  {\n    StackPop p (m_L, 1);\n    impl ().push ();;\n    return get_length (m_L, -1);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Call Lua code.\n\n      These overloads allow Lua code to be called with up to 8 parameters.\n      The return value is provided as a LuaRef (which may be LUA_REFNIL).\n      If an error occurs, a LuaException is thrown.\n  */\n  /** @{ */\n  LuaRef operator() () const\n  {\n    impl ().push ();;\n    LuaException::pcall (m_L, 0, 1);\n    return LuaRef::fromStack (m_L);\n  }\n\n  template <class P1>\n  LuaRef operator() (P1 p1) const\n  {\n    impl ().push ();;\n    Stack <P1>::push (m_L, p1);\n    LuaException::pcall (m_L, 1, 1);\n    return LuaRef::fromStack (m_L);\n  }\n\n  template <class P1, class P2>\n  LuaRef operator() (P1 p1, P2 p2) const\n  {\n    impl ().push ();;\n    Stack <P1>::push (m_L, p1);\n    Stack <P2>::push (m_L, p2);\n    LuaException::pcall (m_L, 2, 1);\n    return LuaRef::fromStack (m_L);\n  }\n\n  template <class P1, class P2, class P3>\n  LuaRef operator() (P1 p1, P2 p2, P3 p3) const\n  {\n    impl ().push ();;\n    Stack <P1>::push (m_L, p1);\n    Stack <P2>::push (m_L, p2);\n    Stack <P3>::push (m_L, p3);\n    LuaException::pcall (m_L, 3, 1);\n    return LuaRef::fromStack (m_L);\n  }\n\n  template <class P1, class P2, class P3, class P4>\n  LuaRef operator() (P1 p1, P2 p2, P3 p3, P4 p4) const\n  {\n    impl ().push ();;\n    Stack <P1>::push (m_L, p1);\n    Stack <P2>::push (m_L, p2);\n    Stack <P3>::push (m_L, p3);\n    Stack <P4>::push (m_L, p4);\n    LuaException::pcall (m_L, 4, 1);\n    return LuaRef::fromStack (m_L);\n  }\n\n  template <class P1, class P2, class P3, class P4, class P5>\n  LuaRef operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const\n  {\n    impl ().push ();;\n    Stack <P1>::push (m_L, p1);\n    Stack <P2>::push (m_L, p2);\n    Stack <P3>::push (m_L, p3);\n    Stack <P4>::push (m_L, p4);\n    Stack <P5>::push (m_L, p5);\n    LuaException::pcall (m_L, 5, 1);\n    return LuaRef::fromStack (m_L);\n  }\n\n  template <class P1, class P2, class P3, class P4, class P5, class P6>\n  LuaRef operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) const\n  {\n    impl ().push ();;\n    Stack <P1>::push (m_L, p1);\n    Stack <P2>::push (m_L, p2);\n    Stack <P3>::push (m_L, p3);\n    Stack <P4>::push (m_L, p4);\n    Stack <P5>::push (m_L, p5);\n    Stack <P6>::push (m_L, p6);\n    LuaException::pcall (m_L, 6, 1);\n    return LuaRef::fromStack (m_L);\n  }\n\n  template <class P1, class P2, class P3, class P4, class P5, class P6, class P7>\n  LuaRef operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) const\n  {\n    impl ().push ();;\n    Stack <P1>::push (m_L, p1);\n    Stack <P2>::push (m_L, p2);\n    Stack <P3>::push (m_L, p3);\n    Stack <P4>::push (m_L, p4);\n    Stack <P5>::push (m_L, p5);\n    Stack <P6>::push (m_L, p6);\n    Stack <P7>::push (m_L, p7);\n    LuaException::pcall (m_L, 7, 1);\n    return LuaRef::fromStack (m_L);\n  }\n\n  template <class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>\n  LuaRef operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) const\n  {\n    impl ().push ();\n    Stack <P1>::push (m_L, p1);\n    Stack <P2>::push (m_L, p2);\n    Stack <P3>::push (m_L, p3);\n    Stack <P4>::push (m_L, p4);\n    Stack <P5>::push (m_L, p5);\n    Stack <P6>::push (m_L, p6);\n    Stack <P7>::push (m_L, p7);\n    Stack <P8>::push (m_L, p8);\n    LuaException::pcall (m_L, 8, 1);\n    return LuaRef::fromStack (m_L);\n  }\n  /** @} */\n\n  //============================================================================\n\nprotected:\n  lua_State* m_L;\n\nprivate:\n  const Impl& impl() const\n  {\n    return static_cast <const Impl&> (*this);\n  }\n\n  Impl& impl()\n  {\n    return static_cast <Impl&> (*this);\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\n    Lightweight reference to a Lua object.\n\n    The reference is maintained for the lifetime of the C++ object.\n*/\nclass LuaRef : public LuaRefBase <LuaRef, LuaRef>\n{\n  //----------------------------------------------------------------------------\n  /**\n      A proxy for representing table values.\n  */\n  class Proxy : public LuaRefBase <Proxy, LuaRef>\n  {\n    friend class LuaRef;\n\n  public:\n    //--------------------------------------------------------------------------\n    /**\n        Construct a Proxy from a table value.\n\n        The table is in the registry, and the key is at the top of the stack.\n        The key is popped off the stack.\n    */\n    Proxy (lua_State* L, int tableRef)\n      : LuaRefBase (L)\n      , m_tableRef (LUA_NOREF)\n      , m_keyRef (luaL_ref (L, LUA_REGISTRYINDEX))\n    {\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, tableRef);\n      m_tableRef = luaL_ref (L, LUA_REGISTRYINDEX);\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Create a Proxy via copy constructor.\n\n        It is best to avoid code paths that invoke this, because it creates\n        an extra temporary Lua reference. Typically this is done by passing\n        the Proxy parameter as a `const` reference.\n    */\n    Proxy (Proxy const& other)\n      : LuaRefBase (other.m_L)\n      , m_tableRef (LUA_NOREF)\n      , m_keyRef (LUA_NOREF)\n    {\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, other.m_tableRef);\n      m_tableRef = luaL_ref (m_L, LUA_REGISTRYINDEX);\n\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, other.m_keyRef);\n      m_keyRef = luaL_ref (m_L, LUA_REGISTRYINDEX);\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Destroy the proxy.\n\n        This does not destroy the table value.\n    */\n    ~Proxy ()\n    {\n      luaL_unref (m_L, LUA_REGISTRYINDEX, m_keyRef);\n      luaL_unref (m_L, LUA_REGISTRYINDEX, m_tableRef);\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Assign a new value to this table key.\n\n        This may invoke metamethods.\n    */\n    template <class T>\n    Proxy& operator= (T v)\n    {\n      StackPop p (m_L, 1);\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_tableRef);\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_keyRef);\n      Stack <T>::push (m_L, v);\n      lua_settable (m_L, -3);\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Assign a new value to this table key.\n\n        The assignment is raw, no metamethods are invoked.\n    */\n    template <class T>\n    Proxy& rawset (T v)\n    {\n      StackPop p (m_L, 1);\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_tableRef);\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_keyRef);\n      Stack <T>::push (m_L, v);\n      lua_rawset (m_L, -3);\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Push the value onto the Lua stack.\n    */\n    using LuaRefBase::push;\n\n    void push () const\n    {\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_tableRef);\n      lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_keyRef);\n      lua_gettable (m_L, -2);\n      lua_remove (m_L, -2); // remove the table\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Access a table value using a key.\n\n        This invokes metamethods.\n    */\n    template <class T>\n    Proxy operator[] (T key) const\n    {\n      return LuaRef (*this) [key];\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Access a table value using a key.\n\n        The operation is raw, metamethods are not invoked. The result is\n        passed by value and may not be modified.\n    */\n    template <class T>\n    LuaRef rawget (T key) const\n    {\n      return LuaRef (*this).rawget (key);\n    }\n\n  private:\n    int m_tableRef;\n    int m_keyRef;\n  };\n\n  friend struct Stack <Proxy>;\n\n  //----------------------------------------------------------------------------\n  /**\n      Create a reference to an object at the top of the Lua stack and pop it.\n\n      This constructor is private and not invoked directly.\n      Instead, use the `fromStack` function.\n\n      @note The object is popped.\n  */\n  LuaRef (lua_State* L, FromStack)\n    : LuaRefBase (L)\n    , m_ref (luaL_ref (m_L, LUA_REGISTRYINDEX))\n  {\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Create a reference to an object on the Lua stack.\n\n      This constructor is private and not invoked directly.\n      Instead, use the `fromStack` function.\n\n      @note The object is not popped.\n  */\n  LuaRef (lua_State* L, int index, FromStack)\n    : LuaRefBase (L)\n    , m_ref (LUA_NOREF)\n  {\n    lua_pushvalue (m_L, index);\n    m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX);\n  }\n\n\npublic:\n  //----------------------------------------------------------------------------\n  /**\n      Create a nil reference.\n\n      The LuaRef may be assigned later.\n  */\n  LuaRef (lua_State* L)\n    : LuaRefBase (L)\n    , m_ref (LUA_NOREF)\n  {\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Create a reference to a value.\n  */\n  template <class T>\n  LuaRef (lua_State* L, T v)\n    : LuaRefBase (L)\n    , m_ref (LUA_NOREF)\n  {\n    Stack <T>::push (m_L, v);\n    m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Create a reference to a table value.\n  */\n  LuaRef (Proxy const& v)\n    : LuaRefBase (v.state ())\n    , m_ref (v.createRef ())\n  {\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Create a new reference to an existing reference.\n  */\n  LuaRef (LuaRef const& other)\n    : LuaRefBase (other.m_L)\n    , m_ref (other.createRef ())\n  {\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Destroy a reference.\n\n      The corresponding Lua registry reference will be released.\n\n      @note If the state refers to a thread, it is the responsibility of the\n            caller to ensure that the thread still exists when the LuaRef\n            is destroyed.\n  */\n  ~LuaRef ()\n  {\n    luaL_unref (m_L, LUA_REGISTRYINDEX, m_ref);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Return a LuaRef from a top stack item.\n\n      The stack item is not popped.\n  */\n  static LuaRef fromStack (lua_State* L)\n  {\n    return LuaRef (L, FromStack ());\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Return a LuaRef from a stack item.\n\n      The stack item is not popped.\n  */\n  static LuaRef fromStack (lua_State* L, int index)\n  {\n    lua_pushvalue (L, index);\n    return LuaRef (L, FromStack ());\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Create a new empty table and return a reference to it.\n\n      It is also possible to use the free function `newTable`.\n\n      @see ::luabridge::newTable\n  */\n  static LuaRef newTable (lua_State* L)\n  {\n    lua_newtable (L);\n    return LuaRef (L, FromStack ());\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Return a reference to a named global.\n\n      It is also possible to use the free function `getGlobal`.\n\n      @see ::luabridge::getGlobal\n  */\n  static LuaRef getGlobal (lua_State *L, char const* name)\n  {\n    lua_getglobal (L, name);\n    return LuaRef (L, FromStack ());\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Assign another LuaRef to this LuaRef.\n  */\n  LuaRef& operator= (LuaRef const& rhs)\n  {\n    LuaRef ref (rhs);\n    swap (ref);\n    return *this;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Assign Proxy to this LuaRef.\n  */\n  LuaRef& operator= (LuaRef::Proxy const& rhs)\n  {\n    LuaRef ref (rhs);\n    swap (ref);\n    return *this;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n  Assign nil to this LuaRef.\n  */\n  LuaRef& operator= (Nil const&)\n  {\n    LuaRef ref (m_L);\n    swap (ref);\n    return *this;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Assign a different value to this LuaRef.\n  */\n  template <class T>\n  LuaRef& operator= (T rhs)\n  {\n    LuaRef ref (m_L, rhs);\n    swap (ref);\n    return *this;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Place the object onto the Lua stack.\n  */\n  using LuaRefBase::push;\n\n  void push () const\n  {\n    lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_ref);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Pop the top of Lua stack and assign the ref to m_ref\n  */\n  void pop ()\n  {\n    luaL_unref (m_L, LUA_REGISTRYINDEX, m_ref);\n    m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Access a table value using a key.\n\n      This invokes metamethods.\n  */\n  template <class T>\n  Proxy operator[] (T key) const\n  {\n    Stack <T>::push (m_L, key);\n    return Proxy (m_L, m_ref);\n  }\n\n    //--------------------------------------------------------------------------\n    /**\n        Access a table value using a key.\n\n        The operation is raw, metamethods are not invoked. The result is\n        passed by value and may not be modified.\n    */\n    template <class T>\n    LuaRef rawget (T key) const\n    {\n      StackPop (m_L, 1);\n      push (m_L);\n      Stack <T>::push (m_L, key);\n      lua_rawget (m_L, -2);\n      return LuaRef (m_L, FromStack ());\n    }\n\nprivate:\n  void swap (LuaRef& other)\n  {\n    std::swap (m_L, other.m_L);\n    std::swap (m_ref, other.m_ref);\n  }\n\n  int m_ref;\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for LuaRef.\n*/\ntemplate <>\nstruct Stack <LuaRef>\n{\n  // The value is const& to prevent a copy construction.\n  //\n  static void push (lua_State* L, LuaRef const& v)\n  {\n    v.push (L);\n  }\n\n  static LuaRef get (lua_State* L, int index)\n  {\n    return LuaRef::fromStack (L, index);\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for Proxy.\n*/\ntemplate <>\nstruct Stack <LuaRef::Proxy>\n{\n  // The value is const& to prevent a copy construction.\n  //\n  static void push (lua_State* L, LuaRef::Proxy const& v)\n  {\n    v.push (L);\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\n    Create a reference to a new, empty table.\n\n    This is a syntactic abbreviation for LuaRef::newTable().\n*/\ninline LuaRef newTable (lua_State* L)\n{\n  return LuaRef::newTable (L);\n}\n\n//------------------------------------------------------------------------------\n/**\n    Create a reference to a value in the global table.\n\n    This is a syntactic abbreviation for LuaRef::getGlobal().\n*/\ninline LuaRef getGlobal (lua_State *L, char const* name)\n{\n  return LuaRef::getGlobal (L, name);\n}\n\n//------------------------------------------------------------------------------\n\n// more C++-like cast syntax\n//\ntemplate<class T>\nT LuaRef_cast(LuaRef const& lr)\n{\n  return lr.cast<T>();\n}\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/Namespace.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2007, Nathan Reed\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <LuaBridge/detail/Security.h>\n#include <LuaBridge/detail/TypeTraits.h>\n\n#include <stdexcept>\n#include <string>\n\nnamespace luabridge {\n\n/** Provides C++ to Lua registration capabilities.\n\n    This class is not instantiated directly, call `getGlobalNamespace` to start\n    the registration process.\n*/\nclass Namespace\n{\nprivate:\n  Namespace& operator= (Namespace const& other);\n\n  lua_State* const L;\n  int mutable m_stackSize;\n\nprivate:\n  //============================================================================\n  /**\n    Error reporting.\n\n    VF: This function looks handy, why aren't we using it?\n  */\n#if 0\n  static int luaError (lua_State* L, std::string message)\n  {\n    assert (lua_isstring (L, lua_upvalueindex (1)));\n    std::string s;\n\n    // Get information on the caller's caller to format the message,\n    // so the error appears to originate from the Lua source.\n    lua_Debug ar;\n    int result = lua_getstack (L, 2, &ar);\n    if (result != 0)\n    {\n      lua_getinfo (L, \"Sl\", &ar);\n      s = ar.short_src;\n      if (ar.currentline != -1)\n      {\n        // poor mans int to string to avoid <strstrream>.\n        lua_pushnumber (L, ar.currentline);\n        s = s + \":\" + lua_tostring (L, -1) + \": \";\n        lua_pop (L, 1);\n      }\n    }\n\n    s = s + message;\n\n    return luaL_error (L, s.c_str ());\n  }\n#endif\n\n  //----------------------------------------------------------------------------\n  /**\n    Pop the Lua stack.\n  */\n  void pop (int n) const\n  {\n    if (m_stackSize >= n && lua_gettop (L) >= n)\n    {\n      lua_pop (L, n);\n      m_stackSize -= n;\n    }\n    else\n    {\n      throw std::logic_error (\"invalid stack\");\n    }\n  }\n\nprivate:\n  /**\n    Factored base to reduce template instantiations.\n  */\n  class ClassBase\n  {\n  private:\n    ClassBase& operator= (ClassBase const& other);\n\n  protected:\n    friend class Namespace;\n\n    lua_State* const L;\n    int mutable m_stackSize;\n\n  protected:\n    //--------------------------------------------------------------------------\n    /**\n      __index metamethod for a class.\n\n      This implements member functions, data members, and property members.\n      Functions are stored in the metatable and const metatable. Data members\n      and property members are in the __propget table.\n\n      If the key is not found, the search proceeds up the hierarchy of base\n      classes.\n    */\n    static int indexMetaMethod (lua_State* L)\n    {\n      int result = 0;\n\n      assert (lua_isuserdata (L, 1));               // warn on security bypass\n      lua_getmetatable (L, 1);                      // get metatable for object\n      for (;;)\n      {\n        lua_pushvalue (L, 2);                       // push key arg2\n        lua_rawget (L, -2);                         // lookup key in metatable\n        if (lua_iscfunction (L, -1))                // ensure its a cfunction\n        {\n          lua_remove (L, -2);                       // remove metatable\n          result = 1;\n          break;\n        }\n        else if (lua_isnil (L, -1))\n        {\n          lua_pop (L, 1);\n        }\n        else\n        {\n          lua_pop (L, 2);\n          throw std::logic_error (\"not a cfunction\");\n        }\n\n        rawgetfield (L, -1, \"__propget\");           // get __propget table\n        if (lua_istable (L, -1))                    // ensure it is a table\n        {\n          lua_pushvalue (L, 2);                     // push key arg2\n          lua_rawget (L, -2);                       // lookup key in __propget\n          lua_remove (L, -2);                       // remove __propget\n          if (lua_iscfunction (L, -1))              // ensure its a cfunction\n          {\n            lua_remove (L, -2);                     // remove metatable\n            lua_pushvalue (L, 1);                   // push class arg1\n            lua_call (L, 1, 1);\n            result = 1;\n            break;\n          }\n          else if (lua_isnil (L, -1))\n          {\n            lua_pop (L, 1);\n          }\n          else\n          {\n            lua_pop (L, 2);\n\n            // We only put cfunctions into __propget.\n            throw std::logic_error (\"not a cfunction\");\n          }\n        }\n        else\n        {\n          lua_pop (L, 2);\n\n          // __propget is missing, or not a table.\n          throw std::logic_error (\"missing __propget table\");\n        }\n\n        // Repeat the lookup in the __parent metafield,\n        // or return nil if the field doesn't exist.\n        rawgetfield (L, -1, \"__parent\");\n        if (lua_istable (L, -1))\n        {\n          // Remove metatable and repeat the search in __parent.\n          lua_remove (L, -2);\n        }\n        else if (lua_isnil (L, -1))\n        {\n          result = 1;\n          break;\n        }\n        else\n        {\n          lua_pop (L, 2);\n\n          throw std::logic_error (\"__parent is not a table\");\n        }\n      }\n\n      return result;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      __newindex metamethod for classes.\n\n      This supports writable variables and properties on class objects. The\n      corresponding object is passed in the first parameter to the set function.\n    */\n    static int newindexMetaMethod (lua_State* L)\n    {\n      int result = 0;\n\n      lua_getmetatable (L, 1);\n\n      for (;;)\n      {\n        // Check __propset\n        rawgetfield (L, -1, \"__propset\");\n        if (!lua_isnil (L, -1))\n        {\n          lua_pushvalue (L, 2);\n          lua_rawget (L, -2);\n          if (!lua_isnil (L, -1))\n          {\n            // found it, call the setFunction.\n            assert (lua_isfunction (L, -1));\n            lua_pushvalue (L, 1);\n            lua_pushvalue (L, 3);\n            lua_call (L, 2, 0);\n            result = 0;\n            break;\n          }\n          lua_pop (L, 1);\n        }\n        lua_pop (L, 1);\n\n        // Repeat the lookup in the __parent metafield.\n        rawgetfield (L, -1, \"__parent\");\n        if (lua_isnil (L, -1))\n        {\n          // Either the property or __parent must exist.\n          result = luaL_error (L,\n            \"no member named '%s'\", lua_tostring (L, 2));\n        }\n        lua_remove (L, -2);\n      }\n\n      return result;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Create the const table.\n    */\n    void createConstTable (char const* name)\n    {\n      lua_newtable (L);\n      lua_pushvalue (L, -1);\n      lua_setmetatable (L, -2);\n      lua_pushboolean (L, 1);\n      lua_rawsetp (L, -2, getIdentityKey ());\n      lua_pushstring (L, (std::string (\"const \") + name).c_str ());\n      rawsetfield (L, -2, \"__type\");\n      lua_pushcfunction (L, &indexMetaMethod);\n      rawsetfield (L, -2, \"__index\");\n      lua_pushcfunction (L, &newindexMetaMethod);\n      rawsetfield (L, -2, \"__newindex\");\n      lua_newtable (L);\n      rawsetfield (L, -2, \"__propget\");\n      \n      if (Security::hideMetatables ())\n      {\n        lua_pushnil (L);\n        rawsetfield (L, -2, \"__metatable\");\n      }\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Create the class table.\n\n      The Lua stack should have the const table on top.\n    */\n    void createClassTable (char const* name)\n    {\n      lua_newtable (L);\n      lua_pushvalue (L, -1);\n      lua_setmetatable (L, -2);\n      lua_pushboolean (L, 1);\n      lua_rawsetp (L, -2, getIdentityKey ());\n      lua_pushstring (L, name);\n      rawsetfield (L, -2, \"__type\");\n      lua_pushcfunction (L, &indexMetaMethod);\n      rawsetfield (L, -2, \"__index\");\n      lua_pushcfunction (L, &newindexMetaMethod);\n      rawsetfield (L, -2, \"__newindex\");\n      lua_newtable (L);\n      rawsetfield (L, -2, \"__propget\");\n      lua_newtable (L);\n      rawsetfield (L, -2, \"__propset\");\n\n      lua_pushvalue (L, -2);\n      rawsetfield (L, -2, \"__const\"); // point to const table\n\n      lua_pushvalue (L, -1);\n      rawsetfield (L, -3, \"__class\"); // point const table to class table\n\n      if (Security::hideMetatables ())\n      {\n        lua_pushnil (L);\n        rawsetfield (L, -2, \"__metatable\");\n      }\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Create the static table.\n\n      The Lua stack should have:\n        -1 class table\n        -2 const table\n        -3 enclosing namespace\n    */\n    void createStaticTable (char const* name)\n    {\n      lua_newtable (L);\n      lua_newtable (L);\n      lua_pushvalue (L, -1);\n      lua_setmetatable (L, -3);\n      lua_insert (L, -2);\n      rawsetfield (L, -5, name);\n\n#if 0\n      lua_pushlightuserdata (L, this);\n      lua_pushcclosure (L, &tostringMetaMethod, 1);\n      rawsetfield (L, -2, \"__tostring\");\n#endif\n      lua_pushcfunction (L, &CFunc::indexMetaMethod);\n      rawsetfield (L, -2, \"__index\");\n      lua_pushcfunction (L, &CFunc::newindexMetaMethod);\n      rawsetfield (L, -2, \"__newindex\");\n      lua_newtable (L);\n      rawsetfield (L, -2, \"__propget\");\n      lua_newtable (L);\n      rawsetfield (L, -2, \"__propset\");\n\n      lua_pushvalue (L, -2);\n      rawsetfield (L, -2, \"__class\"); // point to class table\n\n      if (Security::hideMetatables ())\n      {\n        lua_pushnil (L);\n        rawsetfield (L, -2, \"__metatable\");\n      }\n    }\n\n    //==========================================================================\n    /**\n      lua_CFunction to construct a class object wrapped in a container.\n    */\n    template <class Params, class C>\n    static int ctorContainerProxy (lua_State* L)\n    {\n      typedef typename ContainerTraits <C>::Type T;\n      ArgList <Params, 2> args (L);\n      T* const p = Constructor <T, Params>::call (args);\n      UserdataSharedHelper <C, false>::push (L, p);\n      return 1;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      lua_CFunction to construct a class object in-place in the userdata.\n    */\n    template <class Params, class T>\n    static int ctorPlacementProxy (lua_State* L)\n    {\n      ArgList <Params, 2> args (L);\n      Constructor <T, Params>::call (UserdataValue <T>::place (L), args);\n      return 1;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Pop the Lua stack.\n    */\n    void pop (int n) const\n    {\n      if (m_stackSize >= n && lua_gettop (L) >= n)\n      {\n        lua_pop (L, n);\n        m_stackSize -= n;\n      }\n      else\n      {\n        throw std::logic_error (\"invalid stack\");\n      }\n    }\n\n  public:\n    //--------------------------------------------------------------------------\n    explicit ClassBase (lua_State* L_)\n      : L (L_)\n      , m_stackSize (0)\n    {\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Copy Constructor.\n    */\n    ClassBase (ClassBase const& other)\n      : L (other.L)\n      , m_stackSize (0)\n    {\n      m_stackSize = other.m_stackSize;\n      other.m_stackSize = 0;\n    }\n\n    ~ClassBase ()\n    {\n      pop (m_stackSize);\n    }\n  };\n\n  //============================================================================\n  //\n  // Class\n  //\n  //============================================================================\n  /**\n    Provides a class registration in a lua_State.\n\n    After contstruction the Lua stack holds these objects:\n      -1 static table\n      -2 class table\n      -3 const table\n      -4 (enclosing namespace)\n  */\n  template <class T>\n  class Class : public ClassBase\n  {\n  public:\n    //==========================================================================\n    /**\n      Register a new class or add to an existing class registration.\n    */\n    Class (char const* name, Namespace const* parent) : ClassBase (parent->L)\n    {\n      m_stackSize = parent->m_stackSize + 3;\n      parent->m_stackSize = 0;\n\n      assert (lua_istable (L, -1));\n      rawgetfield (L, -1, name);\n      \n      if (lua_isnil (L, -1))\n      {\n        lua_pop (L, 1);\n\n        createConstTable (name);\n        lua_pushcfunction (L, &CFunc::gcMetaMethod <T>);\n        rawsetfield (L, -2, \"__gc\");\n\n        createClassTable (name);\n        lua_pushcfunction (L, &CFunc::gcMetaMethod <T>);\n        rawsetfield (L, -2, \"__gc\");\n\n        createStaticTable (name);\n\n        // Map T back to its tables.\n        lua_pushvalue (L, -1);\n        lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getStaticKey ());\n        lua_pushvalue (L, -2);\n        lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());\n        lua_pushvalue (L, -3);\n        lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey ());\n      }\n      else\n      {\n        // Map T back from its stored tables\n        lua_rawgetp(L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey());\n        lua_rawgetp(L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey());\n\n        // Reverse the top 3 stack elements\n        lua_insert (L, -3);\n        lua_insert (L, -2);\n      }\n    }\n\n    //==========================================================================\n    /**\n      Derive a new class.\n    */\n    Class (char const* name, Namespace const* parent, void const* const staticKey)\n      : ClassBase (parent->L)\n    {\n      m_stackSize = parent->m_stackSize + 3;\n      parent->m_stackSize = 0;\n\n      assert (lua_istable (L, -1));\n\n      createConstTable (name);\n      lua_pushcfunction (L, &CFunc::gcMetaMethod <T>);\n      rawsetfield (L, -2, \"__gc\");\n\n      createClassTable (name);\n      lua_pushcfunction (L, &CFunc::gcMetaMethod <T>);\n      rawsetfield (L, -2, \"__gc\");\n\n      createStaticTable (name);\n\n      lua_rawgetp (L, LUA_REGISTRYINDEX, staticKey);\n      assert (lua_istable (L, -1));\n      rawgetfield (L, -1, \"__class\");\n      assert (lua_istable (L, -1));\n      rawgetfield (L, -1, \"__const\");\n      assert (lua_istable (L, -1));\n\n      rawsetfield (L, -6, \"__parent\");\n      rawsetfield (L, -4, \"__parent\");\n      rawsetfield (L, -2, \"__parent\");\n\n      lua_pushvalue (L, -1);\n      lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getStaticKey ());\n      lua_pushvalue (L, -2);\n      lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());\n      lua_pushvalue (L, -3);\n      lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey ());\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Continue registration in the enclosing namespace.\n    */\n    Namespace endClass ()\n    {\n      return Namespace (this);\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Add or replace a static data member.\n    */\n    template <class U>\n    Class <T>& addStaticData (char const* name, U* pu, bool isWritable = true)\n    {\n      assert (lua_istable (L, -1));\n\n      rawgetfield (L, -1, \"__propget\");\n      assert (lua_istable (L, -1));\n      lua_pushlightuserdata (L, pu);\n      lua_pushcclosure (L, &CFunc::getVariable <U>, 1);\n      rawsetfield (L, -2, name);\n      lua_pop (L, 1);\n\n      rawgetfield (L, -1, \"__propset\");\n      assert (lua_istable (L, -1));\n      if (isWritable)\n      {\n        lua_pushlightuserdata (L, pu);\n        lua_pushcclosure (L, &CFunc::setVariable <U>, 1);\n      }\n      else\n      {\n        lua_pushstring (L, name);\n        lua_pushcclosure (L, &CFunc::readOnlyError, 1);\n      }\n      rawsetfield (L, -2, name);\n      lua_pop (L, 1);\n\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Add or replace a static property member.\n\n      If the set function is null, the property is read-only.\n    */\n    template <class U>\n    Class <T>& addStaticProperty (char const* name, U (*get)(), void (*set)(U) = 0)\n    {\n      typedef U (*get_t)();\n      typedef void (*set_t)(U);\n      \n      assert (lua_istable (L, -1));\n\n      rawgetfield (L, -1, \"__propget\");\n      assert (lua_istable (L, -1));\n      new (lua_newuserdata (L, sizeof (get))) get_t (get);\n      lua_pushcclosure (L, &CFunc::Call <U (*) (void)>::f, 1);\n      rawsetfield (L, -2, name);\n      lua_pop (L, 1);\n\n      rawgetfield (L, -1, \"__propset\");\n      assert (lua_istable (L, -1));\n      if (set != 0)\n      {\n        new (lua_newuserdata (L, sizeof (set))) set_t (set);\n        lua_pushcclosure (L, &CFunc::Call <void (*) (U)>::f, 1);\n      }\n      else\n      {\n        lua_pushstring (L, name);\n        lua_pushcclosure (L, &CFunc::readOnlyError, 1);\n      }\n      rawsetfield (L, -2, name);\n      lua_pop (L, 1);\n\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Add or replace a static member function.\n    */\n    template <class FP>\n    Class <T>& addStaticFunction (char const* name, FP const fp)\n    {\n      new (lua_newuserdata (L, sizeof (fp))) FP (fp);\n      lua_pushcclosure (L, &CFunc::Call <FP>::f, 1);\n      rawsetfield (L, -2, name);\n\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Add or replace a lua_CFunction.\n    */\n    Class <T>& addStaticCFunction (char const* name, int (*const fp)(lua_State*))\n    {\n      lua_pushcfunction (L, fp);\n      rawsetfield (L, -2, name);\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Add or replace a data member.\n    */\n    template <class U>\n    Class <T>& addData (char const* name, const U T::* mp, bool isWritable = true)\n    {\n      typedef const U T::*mp_t;\n\n      // Add to __propget in class and const tables.\n      {\n        rawgetfield (L, -2, \"__propget\");\n        rawgetfield (L, -4, \"__propget\");\n        new (lua_newuserdata (L, sizeof (mp_t))) mp_t (mp);\n        lua_pushcclosure (L, &CFunc::getProperty <T,U>, 1);\n        lua_pushvalue (L, -1);\n        rawsetfield (L, -4, name);\n        rawsetfield (L, -2, name);\n        lua_pop (L, 2);\n      }\n\n      if (isWritable)\n      {\n        // Add to __propset in class table.\n        rawgetfield (L, -2, \"__propset\");\n        assert (lua_istable (L, -1));\n        new (lua_newuserdata (L, sizeof (mp_t))) mp_t (mp);\n        lua_pushcclosure (L, &CFunc::setProperty <T,U>, 1);\n        rawsetfield (L, -2, name);\n        lua_pop (L, 1);\n      }\n\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Add or replace a property member.\n    */\n    template <class TG, class TS>\n    Class <T>& addProperty (char const* name, TG (T::* get) () const, void (T::* set) (TS))\n    {\n      // Add to __propget in class and const tables.\n      {\n        rawgetfield (L, -2, \"__propget\");\n        rawgetfield (L, -4, \"__propget\");\n        typedef TG (T::*get_t) () const;\n        new (lua_newuserdata (L, sizeof (get_t))) get_t (get);\n        lua_pushcclosure (L, &CFunc::CallConstMember <get_t>::f, 1);\n        lua_pushvalue (L, -1);\n        rawsetfield (L, -4, name);\n        rawsetfield (L, -2, name);\n        lua_pop (L, 2);\n      }\n\n      {\n        // Add to __propset in class table.\n        rawgetfield (L, -2, \"__propset\");\n        assert (lua_istable (L, -1));\n        typedef void (T::* set_t) (TS);\n        new (lua_newuserdata (L, sizeof (set_t))) set_t (set);\n        lua_pushcclosure (L, &CFunc::CallMember <set_t>::f, 1);\n        rawsetfield (L, -2, name);\n        lua_pop (L, 1);\n      }\n\n      return *this;\n    }\n\n    // read-only\n    template <class TG>\n    Class <T>& addProperty (char const* name, TG (T::* get) () const)\n    {\n      // Add to __propget in class and const tables.\n      rawgetfield (L, -2, \"__propget\");\n      rawgetfield (L, -4, \"__propget\");\n      typedef TG (T::*get_t) () const;\n      new (lua_newuserdata (L, sizeof (get_t))) get_t (get);\n      lua_pushcclosure (L, &CFunc::CallConstMember <get_t>::f, 1);\n      lua_pushvalue (L, -1);\n      rawsetfield (L, -4, name);\n      rawsetfield (L, -2, name);\n      lua_pop (L, 2);\n\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Add or replace a property member, by proxy.\n\n      When a class is closed for modification and does not provide (or cannot\n      provide) the function signatures necessary to implement get or set for\n      a property, this will allow non-member functions act as proxies.\n\n      Both the get and the set functions require a T const* and T* in the first\n      argument respectively.\n    */\n    template <class TG, class TS = TG>\n    Class <T>& addProperty (char const* name, TG (*get) (T const*), void (*set) (T*, TS) = 0)\n    {\n      // Add to __propget in class and const tables.\n      {\n        rawgetfield (L, -2, \"__propget\");\n        rawgetfield (L, -4, \"__propget\");\n        typedef TG (*get_t) (T const*);\n        new (lua_newuserdata (L, sizeof (get_t))) get_t (get);\n        lua_pushcclosure (L, &CFunc::Call <get_t>::f, 1);\n        lua_pushvalue (L, -1);\n        rawsetfield (L, -4, name);\n        rawsetfield (L, -2, name);\n        lua_pop (L, 2);\n      }\n\n      if (set != 0)\n      {\n        // Add to __propset in class table.\n        rawgetfield (L, -2, \"__propset\");\n        assert (lua_istable (L, -1));\n        typedef void (*set_t) (T*, TS);\n        new (lua_newuserdata (L, sizeof (set_t))) set_t (set);\n        lua_pushcclosure (L, &CFunc::Call <set_t>::f, 1);\n        rawsetfield (L, -2, name);\n        lua_pop (L, 1);\n      }\n\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Add or replace a member function.\n    */\n    template <class MemFn>\n    Class <T>& addFunction (char const* name, MemFn mf)\n    {\n      static const std::string GC = \"__gc\";\n      if (name == GC)\n      {\n        throw std::logic_error (GC + \" metamethod registration is forbidden\");\n      }\n      CFunc::CallMemberFunctionHelper <MemFn, FuncTraits <MemFn>::isConstMemberFunction>::add (L, name, mf);\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Add or replace a member lua_CFunction.\n    */\n    Class <T>& addCFunction (char const* name, int (T::*mfp)(lua_State*))\n    {\n      typedef int (T::*MFP)(lua_State*);\n      assert (lua_istable (L, -1));\n      new (lua_newuserdata (L, sizeof (mfp))) MFP (mfp);\n      lua_pushcclosure (L, &CFunc::CallMemberCFunction <T>::f, 1);\n      rawsetfield (L, -3, name); // class table\n\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n        Add or replace a const member lua_CFunction.\n    */\n    Class <T>& addCFunction (char const* name, int (T::*mfp)(lua_State*) const)\n    {\n      typedef int (T::*MFP)(lua_State*) const;\n      assert (lua_istable (L, -1));\n      new (lua_newuserdata (L, sizeof (mfp))) MFP (mfp);\n      lua_pushcclosure (L, &CFunc::CallConstMemberCFunction <T>::f, 1);\n      lua_pushvalue (L, -1);\n      rawsetfield (L, -5, name); // const table\n      rawsetfield (L, -3, name); // class table\n\n      return *this;\n    }\n\n    //--------------------------------------------------------------------------\n    /**\n      Add or replace a primary Constructor.\n\n      The primary Constructor is invoked when calling the class type table\n      like a function.\n\n      The template parameter should be a function pointer type that matches\n      the desired Constructor (since you can't take the address of a Constructor\n      and pass it as an argument).\n    */\n    template <class MemFn, class C>\n    Class <T>& addConstructor ()\n    {\n      lua_pushcclosure (L,\n        &ctorContainerProxy <typename FuncTraits <MemFn>::Params, C>, 0);\n      rawsetfield(L, -2, \"__call\");\n\n      return *this;\n    }\n\n    template <class MemFn>\n    Class <T>& addConstructor ()\n    {\n      lua_pushcclosure (L,\n        &ctorPlacementProxy <typename FuncTraits <MemFn>::Params, T>, 0);\n      rawsetfield(L, -2, \"__call\");\n\n      return *this;\n    }\n  };\n\nprivate:\n  //----------------------------------------------------------------------------\n  /**\n      Open the global namespace for registrations.\n  */\n  explicit Namespace (lua_State* L_)\n    : L (L_)\n    , m_stackSize (1)\n  {\n    lua_getglobal (L, \"_G\");\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Open a namespace for registrations.\n\n      The namespace is created if it doesn't already exist.\n      The parent namespace is at the top of the Lua stack.\n  */\n  Namespace (char const* name, Namespace const* parent)\n    : L (parent->L)\n    , m_stackSize (0)\n  {\n    m_stackSize = parent->m_stackSize + 1;\n    parent->m_stackSize = 0;\n\n    assert (lua_istable (L, -1));\n    rawgetfield (L, -1, name);\n    if (lua_isnil (L, -1))\n    {\n      lua_pop (L, 1);\n\n      lua_newtable (L);\n      lua_pushvalue (L, -1);\n      lua_setmetatable (L, -2);\n      lua_pushcfunction (L, &CFunc::indexMetaMethod);\n      rawsetfield (L, -2, \"__index\");\n      lua_pushcfunction (L, &CFunc::newindexMetaMethod);\n      rawsetfield (L, -2, \"__newindex\");\n      lua_newtable (L);\n      rawsetfield (L, -2, \"__propget\");\n      lua_newtable (L);\n      rawsetfield (L, -2, \"__propset\");\n      lua_pushvalue (L, -1);\n      rawsetfield (L, -3, name);\n#if 0\n      lua_pushcfunction (L, &tostringMetaMethod);\n      rawsetfield (L, -2, \"__tostring\");\n#endif\n    }\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Creates a continued registration from a child namespace.\n  */\n  explicit Namespace (Namespace const* child)\n    : L (child->L)\n    , m_stackSize (0)\n  {\n    m_stackSize = child->m_stackSize - 1;\n    child->m_stackSize = 1;\n    child->pop (1);\n\n    // It is not necessary or valid to call\n    // endNamespace() for the global namespace!\n    //\n    assert (m_stackSize != 0);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Creates a continued registration from a child class.\n  */\n  explicit Namespace (ClassBase const* child)\n    : L (child->L)\n    , m_stackSize (0)\n  {\n    m_stackSize = child->m_stackSize - 3;\n    child->m_stackSize = 3;\n    child->pop (3);\n  }\n\npublic:\n  //----------------------------------------------------------------------------\n  /**\n      Copy Constructor.\n\n      Ownership of the stack is transferred to the new object. This happens\n      when the compiler emits temporaries to hold these objects while chaining\n      registrations across namespaces.\n  */\n  Namespace (Namespace const& other) : L (other.L)\n  {\n    m_stackSize = other.m_stackSize;\n    other.m_stackSize = 0;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Closes this namespace registration.\n  */\n  ~Namespace ()\n  {\n    pop (m_stackSize);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Open the global namespace.\n  */\n  static Namespace getGlobalNamespace (lua_State* L)\n  {\n    return Namespace (L);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Open a new or existing namespace for registrations.\n  */\n  Namespace beginNamespace (char const* name)\n  {\n    return Namespace (name, this);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Continue namespace registration in the parent.\n\n      Do not use this on the global namespace.\n  */\n  Namespace endNamespace ()\n  {\n    return Namespace (this);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Add or replace a variable.\n  */\n  template <class T>\n  Namespace& addVariable (char const* name, T* pt, bool isWritable = true)\n  {\n    if (m_stackSize == 1)\n    {\n      throw std::logic_error (\"Unsupported addVariable on global namespace\");\n    }\n\n    assert (lua_istable (L, -1));\n\n    rawgetfield (L, -1, \"__propget\");\n    assert (lua_istable (L, -1));\n    lua_pushlightuserdata (L, pt);\n    lua_pushcclosure (L, &CFunc::getVariable <T>, 1);\n    rawsetfield (L, -2, name);\n    lua_pop (L, 1);\n\n    rawgetfield (L, -1, \"__propset\");\n    assert (lua_istable (L, -1));\n    if (isWritable)\n    {\n      lua_pushlightuserdata (L, pt);\n      lua_pushcclosure (L, &CFunc::setVariable <T>, 1);\n    }\n    else\n    {\n      lua_pushstring (L, name);\n      lua_pushcclosure (L, &CFunc::readOnlyError, 1);\n    }\n    rawsetfield (L, -2, name);\n    lua_pop (L, 1);\n\n    return *this;\n  }\n  \n  //----------------------------------------------------------------------------\n  /**\n      Add or replace a property.\n\n      If the set function is omitted or null, the property is read-only.\n  */\n  template <class TG, class TS = TG>\n  Namespace& addProperty (char const* name, TG (*get) (), void (*set)(TS) = 0)\n  {\n    if (m_stackSize == 1)\n    {\n      throw std::logic_error (\"Unsupported addProperty on global namespace\");\n    }\n\n    assert (lua_istable (L, -1));\n\n    rawgetfield (L, -1, \"__propget\");\n    assert (lua_istable (L, -1));\n    typedef TG (*get_t) ();\n    new (lua_newuserdata (L, sizeof (get_t))) get_t (get);\n    lua_pushcclosure (L, &CFunc::Call <TG (*) (void)>::f, 1);\n    rawsetfield (L, -2, name);\n    lua_pop (L, 1);\n\n    rawgetfield (L, -1, \"__propset\");\n    assert (lua_istable (L, -1));\n    if (set != 0)\n    {\n      typedef void (*set_t) (TS);\n      new (lua_newuserdata (L, sizeof (set_t))) set_t (set);\n      lua_pushcclosure (L, &CFunc::Call <void (*) (TS)>::f, 1);\n    }\n    else\n    {\n      lua_pushstring (L, name);\n      lua_pushcclosure (L, &CFunc::readOnlyError, 1);\n    }\n    rawsetfield (L, -2, name);\n    lua_pop (L, 1);\n\n    return *this;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Add or replace a free function.\n  */\n  template <class FP>\n  Namespace& addFunction (char const* name, FP const fp)\n  {\n    assert (lua_istable (L, -1));\n\n    new (lua_newuserdata (L, sizeof (fp))) FP (fp);\n    lua_pushcclosure (L, &CFunc::Call <FP>::f, 1);\n    rawsetfield (L, -2, name);\n\n    return *this;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Add or replace a lua_CFunction.\n  */\n  Namespace& addCFunction (char const* name, int (*const fp)(lua_State*))\n  {\n    lua_pushcfunction (L, fp);\n    rawsetfield (L, -2, name);\n\n    return *this;\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Open a new or existing class for registrations.\n  */\n  template <class T>\n  Class <T> beginClass (char const* name)\n  {\n    return Class <T> (name, this);\n  }\n\n  //----------------------------------------------------------------------------\n  /**\n      Derive a new class for registrations.\n\n      To continue registrations for the class later, use beginClass().\n      Do not call deriveClass() again.\n  */\n  template <class T, class U>\n  Class <T> deriveClass (char const* name)\n  {\n    return Class <T> (name, this, ClassInfo <U>::getStaticKey ());\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\n    Retrieve the global namespace.\n\n    It is recommended to put your namespace inside the global namespace, and\n    then add your classes and functions to it, rather than adding many classes\n    and functions directly to the global namespace.\n*/\ninline Namespace getGlobalNamespace (lua_State* L)\n{\n  return Namespace::getGlobalNamespace (L);\n}\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/Security.h",
    "content": "#pragma once\n\nnamespace luabridge {\n\n//------------------------------------------------------------------------------\n/**\nsecurity options.\n*/\nclass Security\n{\npublic:\n  static bool hideMetatables()\n  {\n    return getSettings().hideMetatables;\n  }\n\n  static void setHideMetatables(bool shouldHide)\n  {\n    getSettings().hideMetatables = shouldHide;\n  }\n\nprivate:\n  struct Settings\n  {\n    Settings() : hideMetatables(true)\n    {\n    }\n\n    bool hideMetatables;\n  };\n\n  static Settings& getSettings()\n  {\n    static Settings settings;\n    return settings;\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\nPush an object onto the Lua stack.\n*/\ntemplate <class T>\ninline void push(lua_State* L, T t)\n{\n  Stack <T>::push(L, t);\n}\n\n//------------------------------------------------------------------------------\n/**\nSet a global value in the lua_State.\n\n@note This works on any type specialized by `Stack`, including `LuaRef` and\nits table proxies.\n*/\ntemplate <class T>\ninline void setGlobal(lua_State* L, T t, char const* name)\n{\n  push(L, t);\n  lua_setglobal(L, name);\n}\n\n//------------------------------------------------------------------------------\n/**\nChange whether or not metatables are hidden (on by default).\n*/\ninline void setHideMetatables(bool shouldHide)\n{\n  Security::setHideMetatables(shouldHide);\n}\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/Stack.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n\n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2007, Nathan Reed\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <LuaBridge/detail/LuaHelpers.h>\n\n#include <string>\n\nnamespace luabridge {\n\ntemplate <class T>\nstruct Stack;\n\n//------------------------------------------------------------------------------\n/**\n    Receive the lua_State* as an argument.\n*/\ntemplate <>\nstruct Stack <lua_State*>\n{\n  static lua_State* get (lua_State* L, int)\n  {\n    return L;\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\n    Push a lua_CFunction.\n*/\ntemplate <>\nstruct Stack <lua_CFunction>\n{\n  static void push (lua_State* L, lua_CFunction f)\n  {\n    lua_pushcfunction (L, f);\n  }\n\n  static lua_CFunction get (lua_State* L, int index)\n  {\n    return lua_tocfunction (L, index);\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `int`.\n*/\ntemplate <>\nstruct Stack <int>\n{\n  static inline void push (lua_State* L, int value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  \n  static inline int get (lua_State* L, int index)\n  {\n    return static_cast <int> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <int const&> : public Stack <int>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `unsigned int`.\n*/\ntemplate <>\nstruct Stack <unsigned int>\n{\n  static inline void push (lua_State* L, unsigned int value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  \n  static inline unsigned int get (lua_State* L, int index)\n  {\n    return static_cast <unsigned int> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <unsigned int const&> : Stack <unsigned int>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `unsigned char`.\n*/\ntemplate <>\nstruct Stack <unsigned char>\n{\n  static inline void push (lua_State* L, unsigned char value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  \n  static inline unsigned char get (lua_State* L, int index)\n  {\n    return static_cast <unsigned char> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <unsigned char const&> : Stack <unsigned char>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `short`.\n*/\ntemplate <>\nstruct Stack <short>\n{\n  static inline void push (lua_State* L, short value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  \n  static inline short get (lua_State* L, int index)\n  {\n    return static_cast <short> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <short const&> : Stack <short>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `unsigned short`.\n*/\ntemplate <>\nstruct Stack <unsigned short>\n{\n  static inline void push (lua_State* L, unsigned short value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  \n  static inline unsigned short get (lua_State* L, int index)\n  {\n    return static_cast <unsigned short> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <unsigned short const&> : Stack <unsigned short>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `long`.\n*/\ntemplate <>\nstruct Stack <long>\n{\n  static inline void push (lua_State* L, long value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  \n  static inline long get (lua_State* L, int index)\n  {\n    return static_cast <long> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <long const&> : public Stack <long>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `unsigned long`.\n*/\ntemplate <>\nstruct Stack <unsigned long>\n{\n  static inline void push (lua_State* L, unsigned long value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  \n  static inline unsigned long get (lua_State* L, int index)\n  {\n    return static_cast <unsigned long> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <unsigned long const&> : public Stack <unsigned long>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `float`.\n*/\ntemplate <>\nstruct Stack <float>\n{\n  static inline void push (lua_State* L, float value)\n  {\n    lua_pushnumber (L, static_cast <lua_Number> (value));\n  }\n  \n  static inline float get (lua_State* L, int index)\n  {\n    return static_cast <float> (luaL_checknumber (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <float const&> : Stack <float>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `double`.\n*/\ntemplate <> struct Stack <double>\n{\n  static inline void push (lua_State* L, double value)\n  {\n    lua_pushnumber (L, static_cast <lua_Number> (value));\n  }\n  \n  static inline double get (lua_State* L, int index)\n  {\n    return static_cast <double> (luaL_checknumber (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <double const&> : Stack <double>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `bool`.\n*/\ntemplate <>\nstruct Stack <bool>\n{\n  static inline void push (lua_State* L, bool value)\n  {\n    lua_pushboolean (L, value ? 1 : 0);\n  }\n  \n  static inline bool get (lua_State* L, int index)\n  {\n    return lua_toboolean (L, index) ? true : false;\n  }\n};\n\ntemplate <>\nstruct Stack <bool const&> : Stack <bool>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `char`.\n*/\ntemplate <>\nstruct Stack <char>\n{\n  static inline void push (lua_State* L, char value)\n  {\n    lua_pushlstring (L, &value, 1);\n  }\n  \n  static inline char get (lua_State* L, int index)\n  {\n    return luaL_checkstring (L, index) [0];\n  }\n};\n\ntemplate <>\nstruct Stack <char const&> : Stack <char>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `const char*`.\n*/\ntemplate <>\nstruct Stack <char const*>\n{\n  static inline void push (lua_State* L, char const* str)\n  {\n    if (str != 0)\n      lua_pushstring (L, str);\n    else\n      lua_pushnil (L);\n  }\n\n  static inline char const* get (lua_State* L, int index)\n  {\n    return lua_isnil (L, index) ? 0 : luaL_checkstring (L, index);\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\n    Stack specialization for `std::string`.\n*/\ntemplate <>\nstruct Stack <std::string>\n{\n  static inline void push (lua_State* L, std::string const& str)\n  {\n    lua_pushlstring (L, str.data (), str.size());\n  }\n\n  static inline std::string get (lua_State* L, int index)\n  {\n    size_t len;\n    const char *str = luaL_checklstring (L, index, &len);\n    return std::string (str, len);\n  }\n};\n\ntemplate <>\nstruct Stack <std::string const&> : Stack <std::string>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\nStack specialization for `long long`.\n*/\ntemplate <>\nstruct Stack <long long>\n{\n  static inline void push (lua_State* L, long long value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  static inline long long get (lua_State* L, int index)\n  {\n    return static_cast <long long> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <long long const&> : public Stack <long long>\n{\n};\n\n//------------------------------------------------------------------------------\n/**\nStack specialization for `unsigned long long`.\n*/\ntemplate <>\nstruct Stack <unsigned long long>\n{\n  static inline void push (lua_State* L, unsigned long long value)\n  {\n    lua_pushinteger (L, static_cast <lua_Integer> (value));\n  }\n  static inline unsigned long long get (lua_State* L, int index)\n  {\n    return static_cast <unsigned long long> (luaL_checkinteger (L, index));\n  }\n};\n\ntemplate <>\nstruct Stack <unsigned long long const&> : Stack <unsigned long long>\n{\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/TypeList.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2007, Nathan Reed\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n\n  This file incorporates work covered by the following copyright and\n  permission notice:  \n\n    The Loki Library\n    Copyright (c) 2001 by Andrei Alexandrescu\n    This code accompanies the book:\n    Alexandrescu, Andrei. \"Modern C++ Design: Generic Programming and Design \n        Patterns Applied\". Copyright (c) 2001. Addison-Wesley.\n    Permission to use, copy, modify, distribute and sell this software for any \n        purpose is hereby granted without fee, provided that the above copyright \n        notice appear in all copies and that both that copyright notice and this \n        permission notice appear in supporting documentation.\n    The author or Addison-Welsey Longman make no representations about the \n        suitability of this software for any purpose. It is provided \"as is\" \n        without express or implied warranty.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <LuaBridge/detail/Stack.h>\n\n#include <string>\n#include <typeinfo>\n\nnamespace luabridge {\n\n/**\n  None type means void parameters or return value.\n*/\ntypedef void None;\n\ntemplate <typename Head, typename Tail = None>\nstruct TypeList\n{\n};\n\n/**\n  A TypeList with actual values.\n*/\ntemplate <typename List>\nstruct TypeListValues\n{\n  static std::string const tostring (bool)\n  {\n    return \"\";\n  }\n};\n\n/**\n  TypeListValues recursive template definition.\n*/\ntemplate <typename Head, typename Tail>\nstruct TypeListValues <TypeList <Head, Tail> >\n{\n  Head hd;\n  TypeListValues <Tail> tl;\n\n  TypeListValues (Head hd_, TypeListValues <Tail> const& tl_)\n    : hd (hd_), tl (tl_)\n  {\n  }\n\n  static std::string const tostring (bool comma = false)\n  {\n    std::string s;\n\n    if (comma)\n      s = \", \";\n\n    s = s + typeid (Head).name ();\n\n    return s + TypeListValues <Tail>::tostring (true);\n  }\n};\n\n// Specializations of type/value list for head types that are references and\n// const-references.  We need to handle these specially since we can't count\n// on the referenced object hanging around for the lifetime of the list.\n\ntemplate <typename Head, typename Tail>\nstruct TypeListValues <TypeList <Head&, Tail> >\n{\n  Head hd;\n  TypeListValues <Tail> tl;\n\n  TypeListValues (Head& hd_, TypeListValues <Tail> const& tl_)\n    : hd (hd_), tl (tl_)\n  {\n  }\n\n  static std::string const tostring (bool comma = false)\n  {\n    std::string s;\n\n    if (comma)\n      s = \", \";\n\n    s = s + typeid (Head).name () + \"&\";\n\n    return s + TypeListValues <Tail>::tostring (true);\n  }\n};\n\ntemplate <typename Head, typename Tail>\nstruct TypeListValues <TypeList <Head const&, Tail> >\n{\n  Head hd;\n  TypeListValues <Tail> tl;\n\n  TypeListValues (Head const& hd_, const TypeListValues <Tail>& tl_)\n    : hd (hd_), tl (tl_)\n  {\n  }\n\n  static std::string const tostring (bool comma = false)\n  {\n    std::string s;\n\n    if (comma)\n      s = \", \";\n\n    s = s + typeid (Head).name () + \" const&\";\n\n    return s + TypeListValues <Tail>::tostring (true);\n  }\n};\n\n//==============================================================================\n/**\n  Subclass of a TypeListValues constructable from the Lua stack.\n*/\n\ntemplate <typename List, int Start = 1>\nstruct ArgList\n{\n};\n\ntemplate <int Start>\nstruct ArgList <None, Start> : public TypeListValues <None>\n{\n  ArgList (lua_State*)\n  {\n  }\n};\n\ntemplate <typename Head, typename Tail, int Start>\nstruct ArgList <TypeList <Head, Tail>, Start>\n  : public TypeListValues <TypeList <Head, Tail> >\n{\n  ArgList (lua_State* L)\n    : TypeListValues <TypeList <Head, Tail> > (Stack <Head>::get (L, Start),\n                                            ArgList <Tail, Start + 1> (L))\n  {\n  }\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/TypeTraits.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\nnamespace luabridge {\n\n//------------------------------------------------------------------------------\n/**\n    Container traits.\n\n    Unspecialized ContainerTraits has the isNotContainer typedef for SFINAE.\n    All user defined containers must supply an appropriate specialization for\n    ContinerTraits (without the typedef isNotContainer). The containers that\n    come with LuaBridge also come with the appropriate ContainerTraits\n    specialization. See the corresponding declaration for details.\n\n    A specialization of ContainerTraits for some generic type ContainerType\n    looks like this:\n\n        template <class T>\n        struct ContainerTraits <ContainerType <T> >\n        {\n          typedef typename T Type;\n\n          static T* get (ContainerType <T> const& c)\n          {\n            return c.get (); // Implementation-dependent on ContainerType\n          }\n        };\n*/\ntemplate <class T>\nstruct ContainerTraits\n{\n  typedef bool isNotContainer;\n  typedef T Type;\n};\n\n//------------------------------------------------------------------------------\n/**\n    Type traits.\n\n    Specializations return information about a type.\n*/\nstruct TypeTraits\n{\n  /** Determine if type T is a container.\n\n      To be considered a container, there must be a specialization of\n      ContainerTraits with the required fields.\n  */\n  template <typename T>\n  class isContainer\n  {\n  private:\n    typedef char yes[1]; // sizeof (yes) == 1\n    typedef char no [2]; // sizeof (no)  == 2\n\n    template <typename C>\n    static no& test (typename C::isNotContainer*);\n \n    template <typename>\n    static yes& test (...);\n \n  public:\n    static const bool value = sizeof (test <ContainerTraits <T> >(0)) == sizeof (yes);\n  };\n\n  /** Determine if T is const qualified.\n  */\n  /** @{ */\n  template <class T>\n  struct isConst\n  {\n    static bool const value = false;\n  };\n\n  template <class T>\n  struct isConst <T const>\n  {\n    static bool const value = true;\n  };\n  /** @} */\n\n  /** Remove the const qualifier from T.\n  */\n  /** @{ */\n  template <class T>\n  struct removeConst\n  {\n    typedef T Type;\n  };\n\n  template <class T>\n  struct removeConst <T const>\n  {\n    typedef T Type;\n  };\n  /**@}*/\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/Userdata.h",
    "content": "//------------------------------------------------------------------------------\n/*\n  https://github.com/vinniefalco/LuaBridge\n  \n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <LuaBridge/detail/TypeList.h>\n\n#include <cassert>\n\nnamespace luabridge {\n\n//==============================================================================\n/**\n  Return the identity pointer for our lightuserdata tokens.\n\n  LuaBridge metatables are tagged with a security \"token.\" The token is a\n  lightuserdata created from the identity pointer, used as a key in the\n  metatable. The value is a boolean = true, although any value could have been\n  used.\n\n  Because of Lua's dynamic typing and our improvised system of imposing C++\n  class structure, there is the possibility that executing scripts may\n  knowingly or unknowingly cause invalid data to get passed to the C functions\n  created by LuaBridge. In particular, our security model addresses the\n  following:\n\n  Notes:\n    1. Scripts cannot create a userdata (ignoring the debug lib).\n    2. Scripts cannot create a lightuserdata (ignoring the debug lib).\n    3. Scripts cannot set the metatable on a userdata.\n    4. Our identity key is a unique pointer in the process.\n    5. Our metatables have a lightuserdata identity key / value pair.\n    6. Our metatables have \"__metatable\" set to a boolean = false.\n    7. Our lightuserdata is unique.\n*/\ninline void* getIdentityKey ()\n{\n  static char value;\n  return &value;\n}\n\n/**\n  Interface to a class pointer retrievable from a userdata.\n*/\nclass Userdata\n{\nprotected:\n  void* m_p; // subclasses must set this\n\n  //--------------------------------------------------------------------------\n  /**\n    Get an untyped pointer to the contained class.\n  */\n  inline void* const getPointer ()\n  {\n    return m_p;\n  }\n\nprivate:\n  //--------------------------------------------------------------------------\n  /**\n    Validate and retrieve a Userdata on the stack.\n\n    The Userdata must exactly match the corresponding class table or\n    const table, or else a Lua error is raised. This is used for the\n    __gc metamethod.\n  */\n  static Userdata* getExactClass (lua_State* L,\n                                  int narg,\n                                  void const* classKey)\n  {\n    Userdata* ud = 0;\n    int const index = lua_absindex (L, narg);\n\n    bool mismatch = false;\n    char const* got = 0;\n\n    lua_rawgetp (L, LUA_REGISTRYINDEX, classKey);\n    assert (lua_istable (L, -1));\n\n    // Make sure we have a userdata.\n    if (!lua_isuserdata (L, index))\n      mismatch = true;\n\n    // Make sure it's metatable is ours.\n    if (!mismatch)\n    {\n      lua_getmetatable (L, index);\n      lua_rawgetp (L, -1, getIdentityKey ());\n      if (lua_isboolean (L, -1))\n      {\n        lua_pop (L, 1);\n      }\n      else\n      {\n        lua_pop (L, 2);\n        mismatch = true;\n      }      \n    }\n\n    if (!mismatch)\n    {\n      if (lua_rawequal (L, -1, -2))\n      {\n        // Matches class table.\n        lua_pop (L, 2);\n        ud = static_cast <Userdata*> (lua_touserdata (L, index));\n      }\n      else\n      {\n        rawgetfield (L, -2, \"__const\");\n        if (lua_rawequal (L, -1, -2))\n        {\n          // Matches const table\n          lua_pop (L, 3);\n          ud = static_cast <Userdata*> (lua_touserdata (L, index));\n        }\n        else\n        {\n          // Mismatch, but its one of ours so get a type name.\n          rawgetfield (L, -2, \"__type\");\n          lua_insert (L, -4);\n          lua_pop (L, 2);\n          got = lua_tostring (L, -2);\n          mismatch = true;\n        }\n      }\n    }\n\n    if (mismatch)\n    {\n      rawgetfield (L, -1, \"__type\");\n      assert (lua_type (L, -1) == LUA_TSTRING);\n      char const* const expected = lua_tostring (L, -1);\n\n      if (got == 0)\n        got = lua_typename (L, lua_type (L, index));\n\n      char const* const msg = lua_pushfstring (\n        L, \"%s expected, got %s\", expected, got);\n\n      if (narg > 0)\n        luaL_argerror (L, narg, msg);\n      else\n        lua_error (L);\n    }\n\n    return ud;\n  }\n\n  //--------------------------------------------------------------------------\n  /**\n    Validate and retrieve a Userdata on the stack.\n\n    The Userdata must be derived from or the same as the given base class,\n    identified by the key. If canBeConst is false, generates an error if\n    the resulting Userdata represents to a const object. We do the type check\n    first so that the error message is informative.\n  */\n  static Userdata* getClass (lua_State* L,\n                             int index,\n                             void const* baseClassKey,\n                             bool canBeConst)\n  {\n    index = index > 0 ? index : lua_absindex (L, index);\n\n    Userdata* ud = 0;\n\n    bool mismatch = false;\n    char const* got = 0;\n\n    lua_rawgetp (L, LUA_REGISTRYINDEX, baseClassKey);\n    if (!lua_istable (L, -1))\n    {\n      throw std::logic_error (\"The class is not registered in LuaBridge\");\n    }\n\n    // Make sure we have a userdata.\n    if (lua_isuserdata (L, index))\n    {\n      // Make sure it's metatable is ours.\n      lua_getmetatable (L, index);\n      lua_rawgetp (L, -1, getIdentityKey ());\n      if (lua_isboolean (L, -1))\n      {\n        lua_pop (L, 1);\n\n        // If __const is present, object is NOT const.\n        rawgetfield (L, -1, \"__const\");\n        assert (lua_istable (L, -1) || lua_isnil (L, -1));\n        bool const isConst = lua_isnil (L, -1);\n        lua_pop (L, 1);\n\n        // Replace the class table with the const table if needed.\n        if (isConst)\n        {\n          rawgetfield (L, -2, \"__const\");\n          assert (lua_istable (L, -1));\n          lua_replace (L, -3);\n        }\n\n        for (;;)\n        {\n          if (lua_rawequal (L, -1, -2))\n          {\n            lua_pop (L, 2);\n\n            // Match, now check const-ness.\n            if (isConst && !canBeConst)\n            {\n              luaL_argerror (L, index, \"cannot be const\");\n            }\n            else\n            {\n              ud = static_cast <Userdata*> (lua_touserdata (L, index));\n              break;\n            }\n          }\n          else\n          {\n            // Replace current metatable with it's base class.\n            rawgetfield (L, -1, \"__parent\");\n/*\nud\nclass metatable\nud metatable\nud __parent (nil)\n*/\n\n            if (lua_isnil (L, -1))\n            {\n              lua_remove (L, -1);\n              // Mismatch, but its one of ours so get a type name.\n              rawgetfield (L, -1, \"__type\");\n              lua_insert (L, -3);\n              lua_pop (L, 1);\n              got = lua_tostring (L, -2);\n              mismatch = true;\n              break;\n            }\n            else\n            {\n              lua_remove (L, -2);\n            }\n          }\n        }\n      }\n      else\n      {\n        lua_pop (L, 2);\n        mismatch = true;\n      }      \n    }\n    else\n    {\n      mismatch = true;\n    }\n\n    if (mismatch)\n    {\n      assert (lua_type (L, -1) == LUA_TTABLE);\n      rawgetfield (L, -1, \"__type\");\n      assert (lua_type (L, -1) == LUA_TSTRING);\n      char const* const expected = lua_tostring (L, -1);\n\n      if (got == 0)\n        got = lua_typename (L, lua_type (L, index));\n\n      char const* const msg = lua_pushfstring (\n        L, \"%s expected, got %s\", expected, got);\n\n      luaL_argerror (L, index, msg);\n    }\n\n    return ud;\n  }\n\npublic:\n  virtual ~Userdata () { }\n\n  //--------------------------------------------------------------------------\n  /**\n    Returns the Userdata* if the class on the Lua stack matches.\n\n    If the class does not match, a Lua error is raised.\n  */\n  template <class T>\n  static inline Userdata* getExact (lua_State* L, int index)\n  {\n    return getExactClass (L, index, ClassInfo <T>::getClassKey ());\n  }\n\n  //--------------------------------------------------------------------------\n  /**\n    Get a pointer to the class from the Lua stack.\n\n    If the object is not the class or a subclass, or it violates the\n    const-ness, a Lua error is raised.\n  */\n  template <class T>\n  static inline T* get (lua_State* L, int index, bool canBeConst)\n  {\n    if (lua_isnil (L, index))\n      return 0;\n    else\n      return static_cast <T*> (getClass (L, index,\n        ClassInfo <T>::getClassKey (), canBeConst)->getPointer ());\n  }\n};\n\n//----------------------------------------------------------------------------\n/**\n  Wraps a class object stored in a Lua userdata.\n\n  The lifetime of the object is managed by Lua. The object is constructed\n  inside the userdata using placement new.\n*/\ntemplate <class T>\nclass UserdataValue : public Userdata\n{\nprivate:\n  UserdataValue <T> (UserdataValue <T> const&);\n  UserdataValue <T> operator= (UserdataValue <T> const&);\n\n  char m_storage [sizeof (T)];\n\n  inline T* getObject ()\n  {\n    // If this fails to compile it means you forgot to provide\n    // a Container specialization for your container!\n    //\n    return reinterpret_cast <T*> (&m_storage [0]);\n  }\n\nprivate:\n  /**\n    Used for placement construction.\n  */\n  UserdataValue ()\n  {\n    m_p = getObject ();\n  }\n\n  ~UserdataValue ()\n  {\n    getObject ()->~T ();\n  }\n\npublic:\n  /**\n    Push a T via placement new.\n\n    The caller is responsible for calling placement new using the\n    returned uninitialized storage.\n  */\n  static void* place (lua_State* const L)\n  {\n    UserdataValue <T>* const ud = new (\n      lua_newuserdata (L, sizeof (UserdataValue <T>))) UserdataValue <T> ();\n    lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());\n    if (!lua_istable (L, -1))\n    {\n      throw std::logic_error (\"The class is not registered in LuaBridge\");\n    }\n    lua_setmetatable (L, -2);\n    return ud->getPointer ();\n  }\n\n  /**\n    Push T via copy construction from U.\n  */\n  template <class U>\n  static inline void push (lua_State* const L, U const& u)\n  {\n    new (place (L)) U (u);\n  }\n};\n\n//----------------------------------------------------------------------------\n/**\n  Wraps a pointer to a class object inside a Lua userdata.\n\n  The lifetime of the object is managed by C++.\n*/\nclass UserdataPtr : public Userdata\n{\nprivate:\n  UserdataPtr (UserdataPtr const&);\n  UserdataPtr operator= (UserdataPtr const&);\n\nprivate:\n  /** Push non-const pointer to object using metatable key.\n  */\n  static void push (lua_State* L, void* const p, void const* const key)\n  {\n    if (p)\n    {\n      new (lua_newuserdata (L, sizeof (UserdataPtr))) UserdataPtr (p);\n      lua_rawgetp (L, LUA_REGISTRYINDEX, key);\n      if (!lua_istable (L, -1))\n      {\n        throw std::logic_error (\"The class is not registered in LuaBridge\");\n      }\n      lua_setmetatable (L, -2);\n    }\n    else\n    {\n      lua_pushnil (L);\n    }\n  }\n\n  /** Push const pointer to object using metatable key.\n  */\n  static void push (lua_State* L, void const* const p, void const* const key)\n  {\n    if (p)\n    {\n      new (lua_newuserdata (L, sizeof (UserdataPtr)))\n        UserdataPtr (const_cast <void*> (p));\n      lua_rawgetp (L, LUA_REGISTRYINDEX, key);\n      if (!lua_istable (L, -1))\n      {\n        throw std::logic_error (\"The class is not registered in LuaBridge\");\n      }\n      lua_setmetatable (L, -2);\n    }\n    else\n    {\n      lua_pushnil (L);\n    }\n  }\n\n  explicit UserdataPtr (void* const p)\n  {\n    m_p = p;\n\n    // Can't construct with a null pointer!\n    //\n    assert (m_p != 0);\n  }\n\npublic:\n  /** Push non-const pointer to object.\n  */\n  template <class T>\n  static inline void push (lua_State* const L, T* const p)\n  {\n    if (p)\n      push (L, p, ClassInfo <T>::getClassKey ());\n    else\n      lua_pushnil (L);\n  }\n\n  /** Push const pointer to object.\n  */\n  template <class T>\n  static inline void push (lua_State* const L, T const* const p)\n  {\n    if (p)\n      push (L, p, ClassInfo <T>::getConstKey ());\n    else\n      lua_pushnil (L);\n  }\n};\n\n//============================================================================\n/**\n  Wraps a container thet references a class object.\n\n  The template argument C is the container type, ContainerTraits must be\n  specialized on C or else a compile error will result.\n*/\ntemplate <class C>\nclass UserdataShared : public Userdata\n{\nprivate:\n  UserdataShared (UserdataShared <C> const&);\n  UserdataShared <C>& operator= (UserdataShared <C> const&);\n\n  typedef typename TypeTraits::removeConst <\n    typename ContainerTraits <C>::Type>::Type T;\n\n  C m_c;\n\nprivate:\n  ~UserdataShared ()\n  {\n  }\n\npublic:\n  /**\n    Construct from a container to the class or a derived class.\n  */\n  template <class U>\n  explicit UserdataShared (U const& u) : m_c (u)\n  {\n    m_p = const_cast <void*> (reinterpret_cast <void const*> (\n        (ContainerTraits <C>::get (m_c))));\n  }\n\n  /**\n    Construct from a pointer to the class or a derived class.\n  */\n  template <class U>\n  explicit UserdataShared (U* u) : m_c (u)\n  {\n    m_p = const_cast <void*> (reinterpret_cast <void const*> (\n        (ContainerTraits <C>::get (m_c))));\n  }\n};\n\n//----------------------------------------------------------------------------\n//\n// SFINAE helpers.\n//\n\n// non-const objects\ntemplate <class C, bool makeObjectConst>\nstruct UserdataSharedHelper\n{\n  typedef typename TypeTraits::removeConst <\n    typename ContainerTraits <C>::Type>::Type T;\n\n  static void push (lua_State* L, C const& c)\n  {\n    if (ContainerTraits <C>::get (c) != 0)\n    {\n      new (lua_newuserdata (L, sizeof (UserdataShared <C>))) UserdataShared <C> (c);\n      lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());\n      // If this goes off it means the class T is unregistered!\n      assert (lua_istable (L, -1));\n      lua_setmetatable (L, -2);\n    }\n    else\n    {\n      lua_pushnil (L);\n    }\n  }\n\n  static void push (lua_State* L, T* const t)\n  {\n    if (t)\n    {\n      new (lua_newuserdata (L, sizeof (UserdataShared <C>))) UserdataShared <C> (t);\n      lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());\n      // If this goes off it means the class T is unregistered!\n      assert (lua_istable (L, -1));\n      lua_setmetatable (L, -2);\n    }\n    else\n    {\n      lua_pushnil (L);\n    }\n  }\n};\n\n// const objects\ntemplate <class C>\nstruct UserdataSharedHelper <C, true>\n{\n  typedef typename TypeTraits::removeConst <\n    typename ContainerTraits <C>::Type>::Type T;\n\n  static void push (lua_State* L, C const& c)\n  {\n    if (ContainerTraits <C>::get (c) != 0)\n    {\n      new (lua_newuserdata (L, sizeof (UserdataShared <C>))) UserdataShared <C> (c);\n      lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey ());\n      // If this goes off it means the class T is unregistered!\n      assert (lua_istable (L, -1));\n      lua_setmetatable (L, -2);\n    }\n    else\n    {\n      lua_pushnil (L);\n    }\n  }\n\n  static void push (lua_State* L, T* const t)\n  {\n    if (t)\n    {\n      new (lua_newuserdata (L, sizeof (UserdataShared <C>))) UserdataShared <C> (t);\n      lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey ());\n      // If this goes off it means the class T is unregistered!\n      assert (lua_istable (L, -1));\n      lua_setmetatable (L, -2);\n    }\n    else\n    {\n      lua_pushnil (L);\n    }\n  }\n};\n\n/**\n  Pass by container.\n\n  The container controls the object lifetime. Typically this will be a\n  lifetime shared by C++ and Lua using a reference count. Because of type\n  erasure, containers like std::shared_ptr will not work. Containers must\n  either be of the intrusive variety, or in the style of the RefCountedPtr\n  type provided by LuaBridge (that uses a global hash table).\n*/\ntemplate <class C, bool byContainer>\nstruct StackHelper\n{\n  static inline void push (lua_State* L, C const& c)\n  {\n    UserdataSharedHelper <C,\n      TypeTraits::isConst <typename ContainerTraits <C>::Type>::value>::push (L, c);\n  }\n\n  typedef typename TypeTraits::removeConst <\n    typename ContainerTraits <C>::Type>::Type T;\n\n  static inline C get (lua_State* L, int index)\n  {\n    return Userdata::get <T> (L, index, true);\n  }\n};\n\n/**\n  Pass by value.\n\n  Lifetime is managed by Lua. A C++ function which accesses a pointer or\n  reference to an object outside the activation record in which it was\n  retrieved may result in undefined behavior if Lua garbage collected it.\n*/\ntemplate <class T>\nstruct StackHelper <T, false>\n{\n  static inline void push (lua_State* L, T const& t)\n  {\n    UserdataValue <T>::push (L, t);\n  }\n\n  static inline T const& get (lua_State* L, int index)\n  {\n    return *Userdata::get <T> (L, index, true);\n  }\n};\n\n//==============================================================================\n\n/**\n  Lua stack conversions for class objects passed by value.\n*/\ntemplate <class T>\nstruct Stack\n{\npublic:\n  static inline void push (lua_State* L, T const& t)\n  {\n    StackHelper <T,\n      TypeTraits::isContainer <T>::value>::push (L, t);\n  }\n\n  static inline T get (lua_State* L, int index)\n  {\n    return StackHelper <T,\n      TypeTraits::isContainer <T>::value>::get (L, index);\n  }\n};\n\n//------------------------------------------------------------------------------\n/**\n  Lua stack conversions for pointers and references to class objects.\n\n  Lifetime is managed by C++. Lua code which remembers a reference to the\n  value may result in undefined behavior if C++ destroys the object. The\n  handling of the const and volatile qualifiers happens in UserdataPtr.\n*/\n\n// pointer\ntemplate <class T>\nstruct Stack <T*>\n{\n  static inline void push (lua_State* L, T* const p)\n  {\n    UserdataPtr::push (L, p);\n  }\n\n  static inline T* const get (lua_State* L, int index)\n  {\n    return Userdata::get <T> (L, index, false);\n  }\n};\n\n// Strips the const off the right side of *\ntemplate <class T>\nstruct Stack <T* const>\n{\n  static inline void push (lua_State* L, T* const p)\n  {\n    UserdataPtr::push (L, p);\n  }\n\n  static inline T* const get (lua_State* L, int index)\n  {\n    return Userdata::get <T> (L, index, false);\n  }\n};\n\n// pointer to const\ntemplate <class T>\nstruct Stack <T const*>\n{\n  static inline void push (lua_State* L, T const* const p)\n  {\n    UserdataPtr::push (L, p);\n  }\n\n  static inline T const* const get (lua_State* L, int index)\n  {\n    return Userdata::get <T> (L, index, true);\n  }\n};\n\n// Strips the const off the right side of *\ntemplate <class T>\nstruct Stack <T const* const>\n{\n  static inline void push (lua_State* L, T const* const p)\n  {\n    UserdataPtr::push (L, p);\n  }\n\n  static inline T const* const get (lua_State* L, int index)\n  {\n    return Userdata::get <T> (L, index, true);\n  }\n};\n\n// reference\ntemplate <class T>\nstruct Stack <T&>\n{\n  static inline void push (lua_State* L, T& t)\n  {\n    UserdataPtr::push (L, &t);\n  }\n\n  static T& get (lua_State* L, int index)\n  {\n    T* const t = Userdata::get <T> (L, index, false);\n    if (!t)\n      luaL_error (L, \"nil passed to reference\");\n    return *t;\n  }\n};\n\ntemplate <class C, bool byContainer>\nstruct RefStackHelper\n{\n  typedef C return_type;  \n\t\n  static inline void push (lua_State* L, C const& t)\n  {\n    UserdataSharedHelper <C,\n      TypeTraits::isConst <typename ContainerTraits <C>::Type>::value>::push (L, t);\n  }\n\n  typedef typename TypeTraits::removeConst <\n    typename ContainerTraits <C>::Type>::Type T;\n\n  static return_type get (lua_State* L, int index)\n  {\n    return Userdata::get <T> (L, index, true);\n  }\n};\n\ntemplate <class T>\nstruct RefStackHelper <T, false>\n{\n  typedef T const& return_type;  \n\t\n\tstatic inline void push (lua_State* L, T const& t)\n\t{\n\t  UserdataPtr::push (L, &t);\n\t}\n\n  static return_type get (lua_State* L, int index)\n  {\n    T const* const t = Userdata::get <T> (L, index, true);\n\n    if (!t)\n      luaL_error (L, \"nil passed to reference\");\n    return *t;\n  }\n    \n};\n\n// reference to const\ntemplate <class T>\nstruct Stack <T const&>\n{\n  typedef RefStackHelper <T, TypeTraits::isContainer <T>::value> helper_t;\n  \n  static inline void push (lua_State* L, T const& t)\n  {\n    helper_t::push (L, t);\n  }\n\n  static typename helper_t::return_type get (lua_State* L, int index)\n  {\n    return helper_t::get (L, index);\n  }\n};\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/LuaBridge/detail/dump.h",
    "content": "//==============================================================================\n/*\n  https://github.com/vinniefalco/LuaBridge\n\n  Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>\n  Copyright 2007, Nathan Reed\n\n  License: The MIT License (http://www.opensource.org/licenses/mit-license.php)\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all\n  copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  SOFTWARE.\n*/\n//==============================================================================\n\n#pragma once\n\n#include <sstream>\n#include <string>\n\nnamespace luabridge {\n\nstd::string dumpLuaState(lua_State *L) {\n\tstd::stringstream ostr;\n\tint i;\n\tint top = lua_gettop(L);\n\tostr << \"top=\" << top << \":\\n\";\n\tfor (i = 1; i <= top; ++i) {\n\t\tint t = lua_type(L, i);\n\t\tswitch(t) {\n\t\tcase LUA_TSTRING:\n\t\t\tostr << \"  \" << i << \": '\" << lua_tostring(L, i) << \"'\\n\";\n\t\t\tbreak;\n\t\tcase LUA_TBOOLEAN:\n\t\t\tostr << \"  \" << i << \": \" << \n\t\t\t\t\t(lua_toboolean(L, i) ? \"true\" : \"false\") << \"\\n\";\n\t\t\tbreak;\n\t\tcase LUA_TNUMBER:\n\t\t\tostr << \"  \" << i << \": \" << lua_tonumber(L, i) << \"\\n\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tostr << \"  \" << i << \": TYPE=\" << lua_typename(L, t) << \"\\n\";\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn ostr.str();\n}\n\n} // namespace luabridge\n"
  },
  {
    "path": "extern/PicoSHA2/picosha2.h",
    "content": "/*\nThe MIT License (MIT)\n\nCopyright (C) 2017 okdshin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n#ifndef PICOSHA2_H\n#define PICOSHA2_H\n// picosha2:20140213\n\n#ifndef PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR\n#define PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR \\\n    1048576  //=1024*1024: default is 1MB memory\n#endif\n\n#include <algorithm>\n#include <cassert>\n#include <iterator>\n#include <sstream>\n#include <vector>\n#include <fstream>\nnamespace picosha2 {\ntypedef unsigned long word_t;\ntypedef unsigned char byte_t;\n\nstatic const size_t k_digest_size = 32;\n\nnamespace detail {\ninline byte_t mask_8bit(byte_t x) { return x & 0xff; }\n\ninline word_t mask_32bit(word_t x) { return x & 0xffffffff; }\n\nconst word_t add_constant[64] = {\n    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,\n    0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\n    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,\n    0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,\n    0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\n    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,\n    0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,\n    0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\n    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};\n\nconst word_t initial_message_digest[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372,\n                                          0xa54ff53a, 0x510e527f, 0x9b05688c,\n                                          0x1f83d9ab, 0x5be0cd19};\n\ninline word_t ch(word_t x, word_t y, word_t z) { return (x & y) ^ ((~x) & z); }\n\ninline word_t maj(word_t x, word_t y, word_t z) {\n    return (x & y) ^ (x & z) ^ (y & z);\n}\n\ninline word_t rotr(word_t x, std::size_t n) {\n    assert(n < 32);\n    return mask_32bit((x >> n) | (x << (32 - n)));\n}\n\ninline word_t bsig0(word_t x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); }\n\ninline word_t bsig1(word_t x) { return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); }\n\ninline word_t shr(word_t x, std::size_t n) {\n    assert(n < 32);\n    return x >> n;\n}\n\ninline word_t ssig0(word_t x) { return rotr(x, 7) ^ rotr(x, 18) ^ shr(x, 3); }\n\ninline word_t ssig1(word_t x) { return rotr(x, 17) ^ rotr(x, 19) ^ shr(x, 10); }\n\ntemplate <typename RaIter1, typename RaIter2>\nvoid hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 last) {\n    assert(first + 64 == last);\n    static_cast<void>(last);  // for avoiding unused-variable warning\n    word_t w[64];\n    std::fill(w, w + 64, 0);\n    for (std::size_t i = 0; i < 16; ++i) {\n        w[i] = (static_cast<word_t>(mask_8bit(*(first + i * 4))) << 24) |\n               (static_cast<word_t>(mask_8bit(*(first + i * 4 + 1))) << 16) |\n               (static_cast<word_t>(mask_8bit(*(first + i * 4 + 2))) << 8) |\n               (static_cast<word_t>(mask_8bit(*(first + i * 4 + 3))));\n    }\n    for (std::size_t i = 16; i < 64; ++i) {\n        w[i] = mask_32bit(ssig1(w[i - 2]) + w[i - 7] + ssig0(w[i - 15]) +\n                          w[i - 16]);\n    }\n\n    word_t a = *message_digest;\n    word_t b = *(message_digest + 1);\n    word_t c = *(message_digest + 2);\n    word_t d = *(message_digest + 3);\n    word_t e = *(message_digest + 4);\n    word_t f = *(message_digest + 5);\n    word_t g = *(message_digest + 6);\n    word_t h = *(message_digest + 7);\n\n    for (std::size_t i = 0; i < 64; ++i) {\n        word_t temp1 = h + bsig1(e) + ch(e, f, g) + add_constant[i] + w[i];\n        word_t temp2 = bsig0(a) + maj(a, b, c);\n        h = g;\n        g = f;\n        f = e;\n        e = mask_32bit(d + temp1);\n        d = c;\n        c = b;\n        b = a;\n        a = mask_32bit(temp1 + temp2);\n    }\n    *message_digest += a;\n    *(message_digest + 1) += b;\n    *(message_digest + 2) += c;\n    *(message_digest + 3) += d;\n    *(message_digest + 4) += e;\n    *(message_digest + 5) += f;\n    *(message_digest + 6) += g;\n    *(message_digest + 7) += h;\n    for (std::size_t i = 0; i < 8; ++i) {\n        *(message_digest + i) = mask_32bit(*(message_digest + i));\n    }\n}\n\n}  // namespace detail\n\ntemplate <typename InIter>\nvoid output_hex(InIter first, InIter last, std::ostream& os) {\n    os.setf(std::ios::hex, std::ios::basefield);\n    while (first != last) {\n        os.width(2);\n        os.fill('0');\n        os << static_cast<unsigned int>(*first);\n        ++first;\n    }\n    os.setf(std::ios::dec, std::ios::basefield);\n}\n\ntemplate <typename InIter>\nvoid bytes_to_hex_string(InIter first, InIter last, std::string& hex_str) {\n    std::ostringstream oss;\n    output_hex(first, last, oss);\n    hex_str.assign(oss.str());\n}\n\ntemplate <typename InContainer>\nvoid bytes_to_hex_string(const InContainer& bytes, std::string& hex_str) {\n    bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str);\n}\n\ntemplate <typename InIter>\nstd::string bytes_to_hex_string(InIter first, InIter last) {\n    std::string hex_str;\n    bytes_to_hex_string(first, last, hex_str);\n    return hex_str;\n}\n\ntemplate <typename InContainer>\nstd::string bytes_to_hex_string(const InContainer& bytes) {\n    std::string hex_str;\n    bytes_to_hex_string(bytes, hex_str);\n    return hex_str;\n}\n\nclass hash256_one_by_one {\n   public:\n    hash256_one_by_one() { init(); }\n\n    void init() {\n        buffer_.clear();\n        std::fill(data_length_digits_, data_length_digits_ + 4, 0);\n        std::copy(detail::initial_message_digest,\n                  detail::initial_message_digest + 8, h_);\n    }\n\n    template <typename RaIter>\n    void process(RaIter first, RaIter last) {\n        add_to_data_length(static_cast<word_t>(std::distance(first, last)));\n        std::copy(first, last, std::back_inserter(buffer_));\n        std::size_t i = 0;\n        for (; i + 64 <= buffer_.size(); i += 64) {\n            detail::hash256_block(h_, buffer_.begin() + i,\n                                  buffer_.begin() + i + 64);\n        }\n        buffer_.erase(buffer_.begin(), buffer_.begin() + i);\n    }\n\n    void finish() {\n        byte_t temp[64];\n        std::fill(temp, temp + 64, 0);\n        std::size_t remains = buffer_.size();\n        std::copy(buffer_.begin(), buffer_.end(), temp);\n        temp[remains] = 0x80;\n\n        if (remains > 55) {\n            std::fill(temp + remains + 1, temp + 64, 0);\n            detail::hash256_block(h_, temp, temp + 64);\n            std::fill(temp, temp + 64 - 4, 0);\n        } else {\n            std::fill(temp + remains + 1, temp + 64 - 4, 0);\n        }\n\n        write_data_bit_length(&(temp[56]));\n        detail::hash256_block(h_, temp, temp + 64);\n    }\n\n    template <typename OutIter>\n    void get_hash_bytes(OutIter first, OutIter last) const {\n        for (const word_t* iter = h_; iter != h_ + 8; ++iter) {\n            for (std::size_t i = 0; i < 4 && first != last; ++i) {\n                *(first++) = detail::mask_8bit(\n                    static_cast<byte_t>((*iter >> (24 - 8 * i))));\n            }\n        }\n    }\n\n   private:\n    void add_to_data_length(word_t n) {\n        word_t carry = 0;\n        data_length_digits_[0] += n;\n        for (std::size_t i = 0; i < 4; ++i) {\n            data_length_digits_[i] += carry;\n            if (data_length_digits_[i] >= 65536u) {\n                carry = data_length_digits_[i] >> 16;\n                data_length_digits_[i] &= 65535u;\n            } else {\n                break;\n            }\n        }\n    }\n    void write_data_bit_length(byte_t* begin) {\n        word_t data_bit_length_digits[4];\n        std::copy(data_length_digits_, data_length_digits_ + 4,\n                  data_bit_length_digits);\n\n        // convert byte length to bit length (multiply 8 or shift 3 times left)\n        word_t carry = 0;\n        for (std::size_t i = 0; i < 4; ++i) {\n            word_t before_val = data_bit_length_digits[i];\n            data_bit_length_digits[i] <<= 3;\n            data_bit_length_digits[i] |= carry;\n            data_bit_length_digits[i] &= 65535u;\n            carry = (before_val >> (16 - 3)) & 65535u;\n        }\n\n        // write data_bit_length\n        for (int i = 3; i >= 0; --i) {\n            (*begin++) = static_cast<byte_t>(data_bit_length_digits[i] >> 8);\n            (*begin++) = static_cast<byte_t>(data_bit_length_digits[i]);\n        }\n    }\n    std::vector<byte_t> buffer_;\n    word_t data_length_digits_[4];  // as 64bit integer (16bit x 4 integer)\n    word_t h_[8];\n};\n\ninline void get_hash_hex_string(const hash256_one_by_one& hasher,\n                                std::string& hex_str) {\n    byte_t hash[k_digest_size];\n    hasher.get_hash_bytes(hash, hash + k_digest_size);\n    return bytes_to_hex_string(hash, hash + k_digest_size, hex_str);\n}\n\ninline std::string get_hash_hex_string(const hash256_one_by_one& hasher) {\n    std::string hex_str;\n    get_hash_hex_string(hasher, hex_str);\n    return hex_str;\n}\n\nnamespace impl {\ntemplate <typename RaIter, typename OutIter>\nvoid hash256_impl(RaIter first, RaIter last, OutIter first2, OutIter last2, int,\n                  std::random_access_iterator_tag) {\n    hash256_one_by_one hasher;\n    // hasher.init();\n    hasher.process(first, last);\n    hasher.finish();\n    hasher.get_hash_bytes(first2, last2);\n}\n\ntemplate <typename InputIter, typename OutIter>\nvoid hash256_impl(InputIter first, InputIter last, OutIter first2,\n                  OutIter last2, int buffer_size, std::input_iterator_tag) {\n    std::vector<byte_t> buffer(buffer_size);\n    hash256_one_by_one hasher;\n    // hasher.init();\n    while (first != last) {\n        int size = buffer_size;\n        for (int i = 0; i != buffer_size; ++i, ++first) {\n            if (first == last) {\n                size = i;\n                break;\n            }\n            buffer[i] = *first;\n        }\n        hasher.process(buffer.begin(), buffer.begin() + size);\n    }\n    hasher.finish();\n    hasher.get_hash_bytes(first2, last2);\n}\n}\n\ntemplate <typename InIter, typename OutIter>\nvoid hash256(InIter first, InIter last, OutIter first2, OutIter last2,\n             int buffer_size = PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR) {\n    picosha2::impl::hash256_impl(\n        first, last, first2, last2, buffer_size,\n        typename std::iterator_traits<InIter>::iterator_category());\n}\n\ntemplate <typename InIter, typename OutContainer>\nvoid hash256(InIter first, InIter last, OutContainer& dst) {\n    hash256(first, last, dst.begin(), dst.end());\n}\n\ntemplate <typename InContainer, typename OutIter>\nvoid hash256(const InContainer& src, OutIter first, OutIter last) {\n    hash256(src.begin(), src.end(), first, last);\n}\n\ntemplate <typename InContainer, typename OutContainer>\nvoid hash256(const InContainer& src, OutContainer& dst) {\n    hash256(src.begin(), src.end(), dst.begin(), dst.end());\n}\n\ntemplate <typename InIter>\nvoid hash256_hex_string(InIter first, InIter last, std::string& hex_str) {\n    byte_t hashed[k_digest_size];\n    hash256(first, last, hashed, hashed + k_digest_size);\n    std::ostringstream oss;\n    output_hex(hashed, hashed + k_digest_size, oss);\n    hex_str.assign(oss.str());\n}\n\ntemplate <typename InIter>\nstd::string hash256_hex_string(InIter first, InIter last) {\n    std::string hex_str;\n    hash256_hex_string(first, last, hex_str);\n    return hex_str;\n}\n\ninline void hash256_hex_string(const std::string& src, std::string& hex_str) {\n    hash256_hex_string(src.begin(), src.end(), hex_str);\n}\n\ntemplate <typename InContainer>\nvoid hash256_hex_string(const InContainer& src, std::string& hex_str) {\n    hash256_hex_string(src.begin(), src.end(), hex_str);\n}\n\ntemplate <typename InContainer>\nstd::string hash256_hex_string(const InContainer& src) {\n    return hash256_hex_string(src.begin(), src.end());\n}\ntemplate<typename OutIter>void hash256(std::ifstream& f, OutIter first, OutIter last){\n    hash256(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>(), first,last);\n\n}\n}// namespace picosha2\n#endif  // PICOSHA2_H\n"
  },
  {
    "path": "extern/oics/CMakeLists.txt",
    "content": "add_library(oics STATIC\n    ICSChannel.cpp\n    ICSControl.cpp\n    ICSInputControlSystem.cpp\n    ICSInputControlSystem_keyboard.cpp\n    ICSInputControlSystem_mouse.cpp\n    ICSInputControlSystem_joystick.cpp\n    ICSPrerequisites.h\n)\n\nif(USE_SYSTEM_TINYXML)\n    target_link_libraries(oics ${TinyXML_LIBRARIES})\nelse()\n    add_library(local_tinyxml STATIC\n        tinyxml.cpp\n        tinyxmlparser.cpp\n        tinyxmlerror.cpp\n        tinystr.cpp\n    )\n    target_link_libraries(oics local_tinyxml)\nendif()\n"
  },
  {
    "path": "extern/oics/ICSChannel.cpp",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#include \"ICSInputControlSystem.h\"\r\n\r\n#define\tB1(t) (t*t)\r\n#define\tB2(t) (2*t*(1-t))\r\n#define\tB3(t) ((1-t)*(1-t))\r\n\r\nnamespace ICS\r\n{\r\n\tChannel::Channel(int number, float initialValue\r\n\t\t\t, float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep)\r\n            : mNumber(number)\r\n            , mValue(initialValue)\r\n\t\t\t, mSymmetricAt(symmetricAt)\r\n\t\t\t, mBezierStep(bezierStep)\r\n            , mEnabled(true)\r\n    { \r\n\t\tmBezierMidPoint.x = bezierMidPointX;\r\n\t\tmBezierMidPoint.y = bezierMidPointY;\r\n\r\n\t\tsetBezierFunction(bezierMidPointY, bezierMidPointX, symmetricAt, bezierStep);\r\n\t} \r\n\r\n    void Channel::setEnabled(bool enabled)\r\n    {\r\n        mEnabled = enabled;\r\n    }\r\n\r\n\tfloat Channel::getValue()\r\n\t{\r\n\t\tif(mValue == 0 || mValue == 1)\r\n\t\t{\r\n\t\t\treturn mValue;\r\n\t\t}\r\n\r\n        assert (!mBezierFunction.empty());\r\n        BezierFunction::iterator it = mBezierFunction.begin();\r\n        //size_t size_minus_1 = mBezierFunction.size() - 1;\r\n        BezierFunction::iterator last = mBezierFunction.end();\r\n        --last;\r\n        for ( ; it != last ; )\r\n        {\r\n            BezierPoint left = (*it);\r\n            BezierPoint right = (*(++it));\r\n\r\n            if( (left.x <= mValue) && (right.x > mValue) )\r\n            {\r\n                float val = left.y - (left.x - mValue) * (left.y - right.y) / (left.x - right.x);\r\n\r\n                return std::max<float>(0.0,std::min<float>(1.0, val));\r\n            }\r\n        }\r\n\r\n\t\treturn -1; \r\n\t}\r\n\r\n    void Channel::setValue(float value)\r\n    {\r\n        float previousValue = this->getValue();\r\n    \r\n        mValue = value;\r\n\r\n        if(previousValue != value && mEnabled)\r\n        {\r\n            notifyListeners(previousValue);\r\n        }\r\n    }\r\n\r\n    void Channel::notifyListeners(float previousValue)\r\n    {\r\n        std::list<ChannelListener*>::iterator pos = mListeners.begin();\r\n        while (pos != mListeners.end())\r\n        {\r\n            (*pos)->channelChanged((Channel*)this, this->getValue(), previousValue);\r\n            ++pos;\r\n        }\r\n    }\r\n\r\n\tvoid Channel::addControl(Control* control, Channel::ChannelDirection dir, float percentage)\r\n\t{\r\n\t\tControlChannelBinderItem ccBinderItem;\r\n\t\tccBinderItem.control = control;\r\n\t\tccBinderItem.direction = dir;\r\n\t\tccBinderItem.percentage = percentage;\r\n\r\n\t\tmAttachedControls.push_back(ccBinderItem);\r\n\t}\r\n\r\n\tChannel::ControlChannelBinderItem Channel::getAttachedControlBinding(Control* control)\r\n\t{\r\n\t\tfor(std::vector<ControlChannelBinderItem>::iterator it = mAttachedControls.begin() ;\r\n\t\t\tit != mAttachedControls.end() ; it++)\r\n\t\t{\r\n\t\t\tif((*it).control == control)\r\n\t\t\t{\r\n\t\t\t\treturn (*it);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tControlChannelBinderItem nullBinderItem;\r\n\t\tnullBinderItem.control = NULL;\r\n\t\tnullBinderItem.direction = Channel/*::ChannelDirection*/::DIRECT;\r\n\t\tnullBinderItem.percentage = 0;\r\n\t\treturn nullBinderItem;\r\n\t}\r\n\r\n\tvoid Channel::update()\r\n\t{\r\n        if(this->getControlsCount() == 1)\r\n\t\t{\r\n\t\t\tControlChannelBinderItem ccBinderItem = mAttachedControls.back();\r\n\t\t\tfloat diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue();\r\n\r\n\t\t\tif(ccBinderItem.direction == ICS::Channel::DIRECT)\r\n\t\t\t{\r\n\t\t\t\tthis->setValue(ccBinderItem.control->getInitialValue() + (ccBinderItem.percentage * diff));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tthis->setValue(ccBinderItem.control->getInitialValue() - (ccBinderItem.percentage * diff));\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfloat val = 0;\r\n\t\t\tstd::vector<ControlChannelBinderItem>::const_iterator it;\r\n\t\t\tfor(it=mAttachedControls.begin(); it!=mAttachedControls.end(); ++it)\r\n\t\t\t{\r\n\t\t\t\tControlChannelBinderItem ccBinderItem = (*it);\r\n\t\t\t\tfloat diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue();\r\n\r\n\t\t\t\tif(ccBinderItem.direction == ICS::Channel::DIRECT)\r\n\t\t\t\t{\r\n\t\t\t\t\tval += (ccBinderItem.percentage * diff);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tval -= (ccBinderItem.percentage * diff);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif(mAttachedControls.size() > 0)\r\n\t\t\t{\r\n\t\t\t\tthis->setValue(mAttachedControls.begin()->control->getInitialValue() + val);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid Channel::setBezierFunction(float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep)\r\n\t{\r\n\t\tmBezierMidPoint.x = bezierMidPointX;\r\n\t\tmBezierMidPoint.y = bezierMidPointY;\r\n\t\tmBezierStep = bezierStep;\r\n\t\tmSymmetricAt = symmetricAt;\r\n\r\n\t\tmBezierFunction.clear();\r\n\r\n\t\tBezierPoint start;\r\n\t\tstart.x = 0;\r\n\t\tstart.y = 0;\r\n\r\n\t\tBezierPoint end;\r\n\t\tend.x = 1;\r\n\t\tend.y = 1;\r\n\t\tmBezierFunction.push_front(end);\r\n\r\n\t\tFilterInterval interval;\r\n\t\tinterval.startX = start.x;\r\n\t\tinterval.startY = start.y;\r\n\t\tinterval.midX = mBezierMidPoint.x;\r\n\t\tinterval.midY = mBezierMidPoint.y;\r\n\t\tinterval.endX = end.x;\r\n\t\tinterval.endY = end.y;\r\n\t\tinterval.step = bezierStep;\r\n\t\tmIntervals.push_back(interval);\r\n\r\n\t\tif(!(mBezierMidPoint.x == 0.5 && mBezierMidPoint.y == 0.5))\r\n\t\t{\r\n\t\t\tfloat t = mBezierStep;\r\n\t\t\twhile(t < 1)\r\n\t\t\t{\r\n\t\t\t\tBezierPoint p;\r\n\t\t\t\tp.x = start.x * B1(t) + mBezierMidPoint.x * B2(t) + end.x * B3(t);\r\n\t\t\t\tp.y = start.y * B1(t) + mBezierMidPoint.y * B2(t) + end.y * B3(t);\r\n\t\t\t\tmBezierFunction.push_front(p);\r\n\r\n\t\t\t\tt += mBezierStep;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tmBezierFunction.push_front(start);\r\n\t}\r\n\r\n\tvoid Channel::addBezierInterval(float startX, float startY, float midX, float midY\r\n\t\t\t, float endX, float endY, float step)\r\n\t{\r\n\t\tFilterInterval interval;\r\n\t\tinterval.startX = startX;\r\n\t\tinterval.startY = startY;\r\n\t\tinterval.midX = midX;\r\n\t\tinterval.midY = midY;\r\n\t\tinterval.endX = endX;\r\n\t\tinterval.endY = endY;\r\n\t\tinterval.step = step;\r\n\t\tmIntervals.push_back(interval);\r\n\r\n\t\tfloat t = 0;\r\n\t\twhile(t <= 1)\r\n\t\t{\r\n\t\t\tBezierPoint p;\r\n\t\t\tp.x = startX * B1(t) + midX * B2(t) + endX * B3(t);\r\n\t\t\tp.y = startY * B1(t) + midY * B2(t) + endY * B3(t);\r\n\r\n\t\t\tBezierFunction::iterator it = mBezierFunction.begin();\r\n\t\t\twhile( it != mBezierFunction.end() )\r\n\t\t\t{\r\n\t\t\t\tBezierPoint left = (*it);\r\n\t\t\t\tBezierPoint right; \r\n\t\t\t\t++it;\r\n\t\t\t\tif( it != mBezierFunction.end() )\r\n\t\t\t\t{\r\n\t\t\t\t\tright = (*it);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tright.x = endX;\r\n\t\t\t\t\tright.y = endY;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(p.x > left.x && p.x < right.x)\r\n\t\t\t\t{\r\n\t\t\t\t\tmBezierFunction.insert(it, p);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tt += 1.0f / ((endX-startX)/step);\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "extern/oics/ICSChannel.h",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#ifndef OICS_Channel_H_\r\n#define OICS_Channel_H_\r\n\r\n#include \"ICSPrerequisites.h\"\r\n\r\n#include \"ICSChannelListener.h\"\r\n\r\nnamespace ICS\r\n{\t\r\n\tstruct FilterInterval{\r\n\t\t//std::string type; //! @todo uncomment when more types implemented\r\n\t\tfloat startX;\r\n\t\tfloat startY;\r\n\t\tfloat midX;\r\n\t\tfloat midY;\r\n\t\tfloat endX;\r\n\t\tfloat endY;\r\n\t\tfloat step;\r\n\t};\r\n\r\n\ttypedef std::list<FilterInterval> IntervalList;\r\n\r\n\tclass DllExport Channel\r\n\t{\r\n\tpublic:\r\n\t\tenum ChannelDirection\r\n\t\t{\r\n\t\t\tINVERSE = -1, DIRECT = 1\r\n\t\t};\r\n\r\n\t\ttypedef struct {\r\n\t\t\tChannelDirection direction;\r\n\t\t\tfloat percentage;\r\n\t\t\tControl* control;\r\n\t\t} ControlChannelBinderItem;\r\n\r\n\r\n\t\tChannel(int number, float initialValue = 0.5\r\n\t\t\t, float bezierMidPointY = 0.5, float bezierMidPointX = 0.5\r\n\t\t\t, float symmetricAt = 0, float bezierStep = 0.2); //! @todo implement symetry\r\n\t\t~Channel(){};\r\n\r\n\t\tvoid setValue(float value);\r\n\t\tfloat getValue();\r\n\r\n        inline int getNumber(){ return mNumber; };\r\n\r\n\t\tvoid addControl(Control* control, Channel::ChannelDirection dir, float percentage);\r\n\t\tinline size_t getControlsCount(){ return mAttachedControls.size(); };\r\n\t\tstd::vector<ControlChannelBinderItem> getAttachedControls(){ return mAttachedControls; };\r\n\t\tControlChannelBinderItem getAttachedControlBinding(Control* control);\r\n\r\n        void addListener(ChannelListener* ob){ mListeners.push_back(ob); };\r\n\t    void removeListener(ChannelListener* ob){ mListeners.remove(ob); };\r\n\r\n\t\tvoid update();\r\n\r\n\t\tvoid setBezierFunction(float bezierMidPointY, float bezierMidPointX = 0.5\r\n\t\t\t, float symmetricAt = 0, float bezierStep = 0.2);\r\n\r\n\t\tvoid addBezierInterval(float startX, float startY, float midX, float midY\r\n\t\t\t, float endX, float endY, float step = 0.1);\r\n\r\n\t\tIntervalList& getIntervals(){ return mIntervals; };\r\n\r\n        void setEnabled(bool enabled);\r\n\r\n    protected:\r\n\r\n        int mNumber;\r\n\t\tfloat mValue;\r\n\t\t\r\n\t\tstruct BezierPoint{\r\n\t\t\tfloat x;\r\n\t\t\tfloat y;\r\n\t\t\tbool operator < (const BezierPoint& other){ return x < other.x; }\r\n\t\t};\r\n\r\n\t\ttypedef std::list<BezierPoint> BezierFunction;\r\n\r\n\t\tBezierPoint mBezierMidPoint;\r\n\t\tBezierFunction mBezierFunction;\r\n\t\tfloat mSymmetricAt;\r\n\t\tfloat mBezierStep;\r\n\r\n\t\tIntervalList mIntervals;\r\n\r\n\t\tstd::vector<ControlChannelBinderItem> mAttachedControls;\r\n\r\n        std::list<ChannelListener* > mListeners;\r\n        void notifyListeners(float previousValue);\r\n\r\n        bool mEnabled;\r\n\r\n\t};\r\n\r\n}\r\n\r\n\r\n#endif\r\n"
  },
  {
    "path": "extern/oics/ICSChannelListener.h",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#ifndef _ChannelListener_H_\r\n#define _ChannelListener_H_\r\n\r\n#include \"ICSPrerequisites.h\"\r\n\r\n#include \"ICSChannel.h\"\r\n\r\nnamespace ICS\r\n{\r\n\t\r\n\tclass DllExport ChannelListener\r\n    {\r\n    public:\r\n        virtual void channelChanged(Channel* channel, float currentValue, float previousValue) = 0;\r\n\r\n        virtual ~ChannelListener() = default;\r\n    };\r\n\r\n}\r\n\r\n\r\n#endif\r\n"
  },
  {
    "path": "extern/oics/ICSControl.cpp",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#include \"ICSInputControlSystem.h\"\r\n\r\n#include \"ICSControl.h\"\r\n\r\nnamespace ICS\r\n{\r\n    Control::Control(const std::string& name, bool autoChangeDirectionOnLimitsAfterStop, bool autoReverseToInitialValue\r\n\t\t, float initialValue, float stepSize, float stepsPerSeconds, bool axisBindable)\r\n\t: mValue(initialValue)\r\n\t , mInitialValue(initialValue)\r\n\t , mName(name)\r\n\t , mStepSize(stepSize)\r\n\t , mStepsPerSeconds(stepsPerSeconds)\r\n\t , mAutoReverseToInitialValue(autoReverseToInitialValue)\r\n\t , mIgnoreAutoReverse(false)\r\n\t , mAutoChangeDirectionOnLimitsAfterStop(autoChangeDirectionOnLimitsAfterStop)\r\n\t , mAxisBindable(axisBindable)\r\n\t , currentChangingDirection(STOP)\r\n\t{\r\n\r\n\t}\r\n\r\n\tControl::~Control()\r\n\t{\r\n\t\tmAttachedChannels.clear();\r\n\t}\r\n\r\n\tvoid Control::setValue(float value)\r\n\t{\r\n\t\tfloat previousValue = mValue;\r\n\r\n\t\tmValue = std::max<float>(0.0,std::min<float>(1.0,value));\r\n\r\n\t\tif(mValue != previousValue)\r\n\t\t{\r\n\t\t\tupdateChannels();\r\n\r\n\t\t\tnotifyListeners(previousValue);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid Control::attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage)\r\n\t{\r\n\t\tmAttachedChannels.push_back(channel);\r\n\t\tchannel->addControl(this, direction, percentage);\r\n\t}\r\n\r\n\tvoid Control::updateChannels()\r\n    {\r\n        std::list<Channel*>::iterator pos = mAttachedChannels.begin();\r\n        while (pos != mAttachedChannels.end())\r\n        {\r\n            ((Channel* )(*pos))->update();\r\n            ++pos;\r\n        }\r\n    }\r\n\r\n\tvoid Control::notifyListeners(float previousValue)\r\n    {\r\n        std::list<ControlListener*>::iterator pos = mListeners.begin();\r\n        while (pos != mListeners.end())\r\n        {\r\n            (*pos)->controlChanged((Control*)this, this->getValue(), previousValue);\r\n            ++pos;\r\n        }\r\n    }\r\n\r\n\tvoid Control::setChangingDirection(ControlChangingDirection direction)\r\n\t{\r\n\t\tcurrentChangingDirection = direction;\r\n\t\tmPendingActions.push_back(direction);\r\n\t}\r\n\r\n\tvoid Control::update(float timeSinceLastFrame)\r\n\t{\r\n        if(!mPendingActions.empty())\r\n\t\t{\r\n\t\t\tsize_t timedActionsCount = 0;\r\n\r\n\t\t\tstd::list<Control::ControlChangingDirection>::iterator cached_end = mPendingActions.end();\r\n\t\t\tfor(std::list<Control::ControlChangingDirection>::iterator it = mPendingActions.begin() ;\r\n                it != cached_end ; ++it)\r\n\t\t\t{\r\n\t\t\t\tif( (*it) != Control::STOP )\r\n\t\t\t\t{\r\n\t\t\t\t\ttimedActionsCount++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tfloat timeSinceLastFramePart = timeSinceLastFrame / std::max<size_t>(1, timedActionsCount);\r\n\t\t\tfor(std::list<Control::ControlChangingDirection>::iterator it = mPendingActions.begin() ;\r\n                it != cached_end ; ++it)\r\n\t\t\t{\r\n\t\t\t\tif( (*it) != Control::STOP )\r\n\t\t\t\t{\r\n\t\t\t\t\tthis->setValue(mValue +\r\n\t\t\t\t\t\t(((int)(*it)) * mStepSize * mStepsPerSeconds * (timeSinceLastFramePart)));\r\n\t\t\t\t}\r\n\t\t\t\telse if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue )\r\n\t\t\t\t{\r\n\r\n\t\t\t\t\tif(mValue > mInitialValue)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tthis->setValue( std::max<float>( mInitialValue,\r\n\t\t\t\t\t\t\tmValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart))));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if(mValue < mInitialValue)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tthis->setValue( std::min<float>( mInitialValue,\r\n\t\t\t\t\t\t\tmValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart))));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tmPendingActions.clear();\r\n\t\t}\r\n\t\telse if( currentChangingDirection != Control::STOP )\r\n\t\t{\r\n\t\t\tthis->setValue(mValue +\r\n\t\t\t\t(((int)currentChangingDirection) * mStepSize * mStepsPerSeconds * (timeSinceLastFrame)));\r\n\t\t}\r\n\t\telse if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue )\r\n\t\t{\r\n\t\t\tif(mValue > mInitialValue)\r\n\t\t\t{\r\n\t\t\t\tthis->setValue( std::max<float>( mInitialValue,\r\n\t\t\t\t\tmValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFrame))));\r\n\t\t\t}\r\n\t\t\telse if(mValue < mInitialValue)\r\n\t\t\t{\r\n\t\t\t\tthis->setValue( std::min<float>( mInitialValue,\r\n\t\t\t\t\tmValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFrame))));\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "extern/oics/ICSControl.h",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#ifndef OICS_Control_H_\r\n#define OICS_Control_H_\r\n\r\n#include \"ICSPrerequisites.h\"\r\n\r\n#include \"ICSChannel.h\"\r\n#include \"ICSControlListener.h\"\r\n\r\nnamespace ICS\r\n{\r\n\r\n    class DllExport Control\r\n\t{\r\n\tpublic:\r\n\r\n\t\tenum ControlChangingDirection\r\n\t\t{\r\n\t\t\tDECREASE = -1, STOP = 0, INCREASE = 1\r\n\t\t};\r\n\r\n        Control(const std::string& name, bool autoChangeDirectionOnLimitsAfterStop = false, bool autoReverseToInitialValue = false, float initialValue = 0.5, float stepSize = 0.1, float stepsPerSeconds = 2.0, bool axisBindable = true);\r\n\t\t~Control();\r\n\r\n\t\tvoid setChangingDirection(ControlChangingDirection direction);\r\n\t\tinline ControlChangingDirection getChangingDirection(){ return currentChangingDirection; };\r\n\r\n\t\tvoid setValue(float value);\r\n\t\tinline float getValue(){ return mValue; };\r\n\t\tinline float getInitialValue(){ return mInitialValue; };\n\t\tinline void setInitialValue(float i) {mInitialValue = i;};\r\n\r\n\t\tvoid attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage = 1.0);\r\n\t\tstd::list<Channel*> getAttachedChannels(){ return mAttachedChannels; };\r\n\r\n\t\tinline float getStepSize(){ return mStepSize; };\r\n\t\tinline float getStepsPerSeconds(){ return mStepsPerSeconds; };\r\n\r\n\t\tinline void setIgnoreAutoReverse(bool value){ mIgnoreAutoReverse = value; }; // mouse disable autoreverse\r\n\t\tinline bool isAutoReverseIgnored(){ return mIgnoreAutoReverse; };\r\n\t\tinline bool getAutoReverse(){ return mAutoReverseToInitialValue; };\r\n\r\n\t\tinline bool getAutoChangeDirectionOnLimitsAfterStop(){ return mAutoChangeDirectionOnLimitsAfterStop; };\r\n\r\n\t\tinline std::string getName(){ return mName; };\r\n\r\n\t\tinline bool isAxisBindable(){ return mAxisBindable; };\r\n\t\tinline void setAxisBindable(bool value){ mAxisBindable = value; };\r\n\r\n\t\tinline void addListener(ControlListener* ob){ mListeners.push_back(ob); };\r\n\t    inline void removeListener(ControlListener* ob){ mListeners.remove(ob); };\r\n\r\n\t\tvoid update(float timeSinceLastFrame);\r\n\r\n\tprotected:\r\n\t\tfloat mValue;\r\n\t\tfloat mInitialValue;\r\n\t\tstd::string mName;\r\n\t\tfloat mStepSize;\r\n\t\tfloat mStepsPerSeconds;\r\n\t\tbool mAutoReverseToInitialValue;\r\n\t\tbool mIgnoreAutoReverse;\r\n\t\tbool mAutoChangeDirectionOnLimitsAfterStop;\r\n\t\tbool mAxisBindable;\r\n\r\n\t\tControl::ControlChangingDirection currentChangingDirection;\r\n\t\tstd::list<Channel*> mAttachedChannels;\r\n\r\n\t\tstd::list<ControlListener*> mListeners;\r\n\r\n\t\tstd::list<Control::ControlChangingDirection> mPendingActions;\r\n\r\n\tprotected:\r\n\r\n\t\tvoid updateChannels();\r\n\t    void notifyListeners(float previousValue);\r\n\r\n\t};\r\n\r\n}\r\n\r\n\r\n#endif\r\n"
  },
  {
    "path": "extern/oics/ICSControlListener.h",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#ifndef _ControlListener_H_\r\n#define _ControlListener_H_\r\n\r\n#include \"ICSPrerequisites.h\"\r\n\r\n#include \"ICSControl.h\"\r\n\r\nnamespace ICS\r\n{\r\n\t\r\n\tclass DllExport ControlListener\r\n    {\r\n    public:\r\n        virtual void controlChanged(Control* control, float currentValue, float previousValue) = 0;\r\n\r\n        virtual ~ControlListener() = default;\r\n    };\r\n\r\n}\r\n\r\n\r\n#endif\r\n"
  },
  {
    "path": "extern/oics/ICSInputControlSystem.cpp",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#include \"ICSInputControlSystem.h\"\r\n\r\nnamespace ICS\r\n{\r\n\tconst float ICS_MAX = std::numeric_limits<float>::max();\r\n\r\n\tInputControlSystem::InputControlSystem(std::string file, bool active\r\n\t\t, DetectingBindingListener* detectingBindingListener\r\n\t\t, InputControlSystemLog* log, size_t channelCount)\r\n\t\t: mFileName(file)\r\n\t\t, mDeadZone(0.1f)\r\n\t\t, mLog(log)\r\n\t\t, mDetectingBindingListener(detectingBindingListener)\r\n\t\t, mDetectingBindingControl(NULL)\r\n        , mDetectingBindingDirection(Control::STOP)\r\n        , mXmouseAxisBinded(false), mYmouseAxisBinded(false)\r\n        , mClientWidth(1)\r\n        , mClientHeight(1)\r\n\t{\r\n\t\tICS_LOG(\" - Creating InputControlSystem - \");\r\n\r\n\t\tthis->mActive = active;\r\n\r\n\t\tICS_LOG(\"Channel count = \" + ToString<size_t>(channelCount) );\r\n\t\tfor(size_t i=0;i<channelCount;i++)\r\n\t\t{\r\n\t\t\tmChannels.push_back(new Channel((int)i));\r\n\t\t}\r\n\r\n\t\tif(file != \"\")\r\n\t\t{\r\n\t\t\tTiXmlDocument* xmlDoc;\r\n\t\t\tTiXmlElement* xmlRoot;\r\n\r\n\t\t\tICS_LOG(\"Loading file \\\"\"+file+\"\\\"\");\r\n\r\n\t\t\txmlDoc = new TiXmlDocument(file.c_str());\r\n\t\t\txmlDoc->LoadFile();\r\n\r\n\t\t\tif(xmlDoc->Error())\r\n\t\t\t{\r\n\t\t\t\tstd::ostringstream message;\r\n\t\t\t\tmessage << \"TinyXml reported an error reading \\\"\"+ file + \"\\\". Row \" <<\r\n\t\t\t\t\t(int)xmlDoc->ErrorRow() << \", Col \" << (int)xmlDoc->ErrorCol() << \": \" <<\r\n\t\t\t\t\txmlDoc->ErrorDesc() ;\r\n\t\t\t\tICS_LOG(message.str());\r\n\r\n\t\t\t\tdelete xmlDoc;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\txmlRoot = xmlDoc->RootElement();\r\n\t\t\tif(std::string(xmlRoot->Value()) != \"Controller\") {\r\n\t\t\t\tICS_LOG(\"Error: Invalid Controller file. Missing <Controller> element.\");\r\n\t\t\t\tdelete xmlDoc;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tTiXmlElement* xmlControl = xmlRoot->FirstChildElement(\"Control\");\r\n\r\n\t        size_t controlChannelCount = 0;\r\n\t\t\twhile(xmlControl)\r\n\t        {\r\n\t            TiXmlElement* xmlChannel = xmlControl->FirstChildElement(\"Channel\");\r\n\t\t\t\twhile(xmlChannel)\r\n\t\t\t\t{\r\n\t\t\t\t\tcontrolChannelCount = std::max(channelCount, FromString<size_t>(xmlChannel->Attribute(\"number\"))+1);\r\n\r\n\t\t\t\t\txmlChannel = xmlChannel->NextSiblingElement(\"Channel\");\r\n\t\t\t\t}\r\n\r\n\t            xmlControl = xmlControl->NextSiblingElement(\"Control\");\r\n\t        }\r\n\r\n\t\t\tstatic const size_t channelsCountLimit = 65536;\r\n\t\t\tif (controlChannelCount > channelsCountLimit)\r\n\t\t\t{\r\n\t\t\t\tICS_LOG(\"Warning: requested channels count (\" + ToString<size_t>(controlChannelCount) + \") exceeds allowed maximum (\" + ToString<size_t>(channelsCountLimit) + \"), clamping it\");\r\n\t\t\t\tcontrolChannelCount = channelsCountLimit;\r\n\t\t\t}\r\n\r\n\t\t\tif(controlChannelCount > channelCount)\r\n\t\t\t{\r\n\t\t\t\tsize_t dif = controlChannelCount - channelCount;\r\n\t\t\t\tICS_LOG(\"Warning: default channel count exceeded. Adding \" + ToString<size_t>(dif) + \" channels\" );\r\n\t\t\t\tfor(size_t i = channelCount ; i < controlChannelCount ; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tmChannels.push_back(new Channel((int)i));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tICS_LOG(\"Applying filters to channels\");\r\n\t\t\t//<ChannelFilter number=\"0\">\r\n\t\t\t//\t<interval type=\"bezier\" startX=\"0.0\" startY=\"0.0\" midX=\"0.25\" midY=\"0.5\" endX=\"0.5\" endY=\"0.5\" step=\"0.1\" />\r\n\t\t\t//\t<interval type=\"bezier\" startX=\"0.5\" startY=\"0.5\" midX=\"0.75\" midY=\"0.5\" endX=\"1.0\" endY=\"1.0\" step=\"0.1\" />\r\n\t\t\t//</ChannelFilter>\r\n\r\n\t\t\tTiXmlElement* xmlChannelFilter = xmlRoot->FirstChildElement(\"ChannelFilter\");\r\n\t\t\twhile(xmlChannelFilter)\r\n\t\t\t{\r\n\t\t\t\tsize_t ch = FromString<size_t>(xmlChannelFilter->Attribute(\"number\"));\r\n\t\t\t\tif(ch >= controlChannelCount)\r\n\t\t\t\t{\r\n\t\t\t\t\tICS_LOG(\"ERROR: channel number (ch) is out of range\");\r\n\t\t\t\t\txmlChannelFilter = xmlChannelFilter->NextSiblingElement(\"ChannelFilter\");\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tTiXmlElement* xmlInterval = xmlChannelFilter->FirstChildElement(\"Interval\");\r\n\t\t\t\twhile(xmlInterval)\r\n\t\t\t\t{\r\n\t\t\t\t\tstd::string type = xmlInterval->Attribute(\"type\");\r\n\r\n\t\t\t\t\tif(type == \"bezier\")\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfloat startX = FromString<float>(xmlInterval->Attribute(\"startX\"));\r\n\t\t\t\t\t\tfloat startY = FromString<float>(xmlInterval->Attribute(\"startY\"));\r\n\t\t\t\t\t\tfloat midX = FromString<float>(xmlInterval->Attribute(\"midX\"));\r\n\t\t\t\t\t\tfloat midY = FromString<float>(xmlInterval->Attribute(\"midY\"));\r\n\t\t\t\t\t\tfloat endX = FromString<float>(xmlInterval->Attribute(\"endX\"));\r\n\t\t\t\t\t\tfloat endY = FromString<float>(xmlInterval->Attribute(\"endY\"));\r\n\r\n                        float step = FromString<float>(xmlInterval->Attribute(\"step\"));\r\n\r\n\t\t\t\t\t\tICS_LOG(\"Applying Bezier filter to channel [number=\"\r\n\t\t\t\t\t\t\t+ ToString<size_t>(ch) + \", startX=\"\r\n\t\t\t\t\t\t\t+ ToString<float>(startX) + \", startY=\"\r\n\t\t\t\t\t\t\t+ ToString<float>(startY) + \", midX=\"\r\n\t\t\t\t\t\t\t+ ToString<float>(midX) + \", midY=\"\r\n\t\t\t\t\t\t\t+ ToString<float>(midY) + \", endX=\"\r\n\t\t\t\t\t\t\t+ ToString<float>(endX) + \", endY=\"\r\n\t\t\t\t\t\t\t+ ToString<float>(endY) + \", step=\"\r\n\t\t\t\t\t\t\t+ ToString<float>(step) + \"]\");\r\n\r\n\t\t\t\t\t\tmChannels.at(ch)->addBezierInterval(startX, startY, midX, midY, endX, endY, step);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\txmlInterval = xmlInterval->NextSiblingElement(\"Interval\");\r\n\t\t\t\t}\r\n\r\n\t\t\t\txmlChannelFilter = xmlChannelFilter->NextSiblingElement(\"ChannelFilter\");\r\n\t\t\t}\r\n\r\n\t\t\txmlControl = xmlRoot->FirstChildElement(\"Control\");\r\n\t\t\twhile(xmlControl)\r\n\t\t\t{\r\n\t\t\t\tbool axisBindable = true;\r\n\t\t\t\tif(xmlControl->Attribute(\"axisBindable\"))\r\n\t\t\t\t{\r\n\t\t\t\t\taxisBindable = (std::string( xmlControl->Attribute(\"axisBindable\") ) == \"true\");\r\n\t\t\t\t}\r\n\r\n\t\t\t\tICS_LOG(\"Adding Control [name=\"\r\n\t\t\t\t\t+ std::string( xmlControl->Attribute(\"name\") ) + \", autoChangeDirectionOnLimitsAfterStop=\"\r\n\t\t\t\t\t+ std::string( xmlControl->Attribute(\"autoChangeDirectionOnLimitsAfterStop\") ) + \", autoReverseToInitialValue=\"\r\n\t\t\t\t\t+ std::string( xmlControl->Attribute(\"autoReverseToInitialValue\") ) + \", initialValue=\"\r\n\t\t\t\t\t+ std::string( xmlControl->Attribute(\"initialValue\") ) + \", stepSize=\"\r\n\t\t\t\t\t+ std::string( xmlControl->Attribute(\"stepSize\") ) + \", stepsPerSeconds=\"\r\n\t\t\t\t\t+ std::string( xmlControl->Attribute(\"stepsPerSeconds\") ) + \", axisBindable=\"\r\n\t\t\t\t\t+ std::string( (axisBindable)? \"true\" : \"false\" ) + \"]\");\r\n\r\n\t\t\t\tfloat _stepSize = 0;\r\n\t\t\t\tif(xmlControl->Attribute(\"stepSize\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tstd::string value(xmlControl->Attribute(\"stepSize\"));\r\n\t\t\t\t\tif(value == \"MAX\")\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t_stepSize = ICS_MAX;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t_stepSize = FromString<float>(value.c_str());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tICS_LOG(\"Warning: no stepSize value found. Default value is 0.\");\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfloat _stepsPerSeconds = 0;\r\n\t\t\t\tif(xmlControl->Attribute(\"stepsPerSeconds\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tstd::string value(xmlControl->Attribute(\"stepsPerSeconds\"));\r\n\t\t\t\t\tif(value == \"MAX\")\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t_stepsPerSeconds = ICS_MAX;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t_stepsPerSeconds = FromString<float>(value.c_str());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tICS_LOG(\"Warning: no stepSize value found. Default value is 0.\");\r\n\t\t\t\t}\r\n\r\n\t\t\t\taddControl( new Control(xmlControl->Attribute(\"name\")\r\n\t\t\t\t\t, std::string( xmlControl->Attribute(\"autoChangeDirectionOnLimitsAfterStop\") ) == \"true\"\r\n\t\t\t\t\t, std::string( xmlControl->Attribute(\"autoReverseToInitialValue\") ) == \"true\"\r\n\t\t\t\t\t, FromString<float>(xmlControl->Attribute(\"initialValue\"))\r\n\t\t\t\t\t, _stepSize\r\n\t\t\t\t\t, _stepsPerSeconds\r\n\t\t\t\t\t, axisBindable) );\r\n\r\n\t\t\t\tloadKeyBinders(xmlControl);\r\n\r\n\t\t\t\tloadMouseAxisBinders(xmlControl);\r\n\r\n\t\t\t\tloadMouseButtonBinders(xmlControl);\r\n\r\n\t\t\t\tloadJoystickAxisBinders(xmlControl);\r\n\r\n\t\t\t\tloadJoystickButtonBinders(xmlControl);\r\n\r\n\t\t\t\t/* ----------------------------------------------------------------------------------------\r\n\t\t\t\t * OPENMW CODE STARTS HERE\r\n\t\t\t\t * Mouse Wheel support added by Michael Stopa (Stomy) */\r\n\r\n\t\t\t\tloadMouseWheelBinders(xmlControl);\r\n\r\n\t\t\t\t/* OPENMW CODE ENDS HERE\r\n\t\t\t\t * ------------------------------------------------------------------------------------- */\r\n\r\n\t\t\t\t// Attach controls to channels\r\n\t\t\t\tTiXmlElement* xmlChannel = xmlControl->FirstChildElement(\"Channel\");\r\n\t\t\t\twhile(xmlChannel)\r\n\t\t\t\t{\r\n\t\t\t\t\tICS_LOG(\"\\tAttaching control to channel [number=\"\r\n\t\t\t\t\t\t+ std::string( xmlChannel->Attribute(\"number\") ) + \", direction=\"\r\n\t\t\t\t\t\t+ std::string( xmlChannel->Attribute(\"direction\") ) + \"]\");\r\n\r\n\t\t\t\t\tfloat percentage = 1;\r\n\t\t\t\t\tif(xmlChannel->Attribute(\"percentage\"))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif(StringIsNumber<float>(xmlChannel->Attribute(\"percentage\")))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tfloat val = FromString<float>(xmlChannel->Attribute(\"percentage\"));\r\n\t\t\t\t\t\t\tif(val > 1 || val < 0)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tICS_LOG(\"ERROR: attaching percentage value range is [0,1]\");\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tpercentage = val;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tICS_LOG(\"ERROR: attaching percentage value range is [0,1]\");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tsize_t chNumber = FromString<size_t>(xmlChannel->Attribute(\"number\"));\r\n\t\t\t\t\tif(chNumber >= controlChannelCount)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tICS_LOG(\"ERROR: channel number (chNumber) is out of range\");\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif(std::string(xmlChannel->Attribute(\"direction\")) == \"DIRECT\")\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tmControls.back()->attachChannel(mChannels[ chNumber ],Channel::DIRECT, percentage);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if(std::string(xmlChannel->Attribute(\"direction\")) == \"INVERSE\")\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tmControls.back()->attachChannel(mChannels[ chNumber ],Channel::INVERSE, percentage);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\txmlChannel = xmlChannel->NextSiblingElement(\"Channel\");\r\n\t\t\t\t}\r\n\r\n\t\t\t\txmlControl = xmlControl->NextSiblingElement(\"Control\");\r\n\t\t\t}\r\n\r\n\t\t\tstd::vector<Channel *>::const_iterator o;\r\n\t\t\tfor(o = mChannels.begin(); o != mChannels.end(); ++o)\r\n\t\t\t{\r\n\t\t\t\t(*o)->update();\r\n\t\t\t}\r\n\r\n\t\t\tdelete xmlDoc;\r\n\t\t}\r\n\r\n\t\tICS_LOG(\" - InputControlSystem Created - \");\r\n\t}\r\n\r\n\tInputControlSystem::~InputControlSystem()\r\n\t{\r\n\t\tICS_LOG(\" - Deleting InputControlSystem (\" + mFileName + \") - \");\r\n\r\n\t\tmJoystickIDList.clear();\r\n\r\n\t\tstd::vector<Channel *>::const_iterator o;\r\n\t\tfor(o = mChannels.begin(); o != mChannels.end(); ++o)\r\n\t\t{\r\n\t\t\tdelete (*o);\r\n\t\t}\r\n\t\tmChannels.clear();\r\n\r\n\t\tstd::vector<Control *>::const_iterator o2;\r\n\t\tfor(o2 = mControls.begin(); o2 != mControls.end(); ++o2)\r\n\t\t{\r\n\t\t\tdelete (*o2);\r\n\t\t}\r\n\t\tmControls.clear();\r\n\r\n\t\tmControlsKeyBinderMap.clear();\r\n\t\tmControlsMouseButtonBinderMap.clear();\r\n\t\tmControlsJoystickButtonBinderMap.clear();\r\n\r\n\t\t/* ----------------------------------------------------------------------------------------\r\n\t\t * OPENMW CODE STARTS HERE\r\n\t\t * Mouse Wheel support added by Michael Stopa (Stomy) */\r\n\r\n\t\tmControlsMouseWheelBinderMap.clear();\r\n\r\n\t\t/* OPENMW CODE ENDS HERE\r\n\t\t * ------------------------------------------------------------------------------------- */\r\n\r\n\t\tICS_LOG(\" - InputControlSystem deleted - \");\r\n\t}\r\n\r\n\tstd::string InputControlSystem::getBaseFileName()\r\n\t{\r\n\t\tsize_t found = mFileName.find_last_of(\"/\\\\\");\r\n\t\tstd::string file = mFileName.substr(found+1);\r\n\r\n\t\treturn file.substr(0, file.find_last_of(\".\"));\r\n\t}\r\n\r\n\tbool InputControlSystem::save(std::string fileName)\r\n\t{\r\n\t\tif(fileName != \"\")\r\n\t\t{\r\n\t\t\tmFileName = fileName;\r\n\t\t}\r\n\r\n\t\tTiXmlDocument doc(  mFileName.c_str() );\r\n\r\n\t\tTiXmlDeclaration dec;\r\n\t\tdec.Parse( \"<?xml version='1.0' encoding='utf-8'?>\", 0, TIXML_ENCODING_UNKNOWN );\r\n\t\tdoc.InsertEndChild(dec);\r\n\r\n\t\tTiXmlElement Controller( \"Controller\" );\r\n\r\n        for(std::vector<Channel*>::const_iterator o = mChannels.begin() ; o != mChannels.end(); ++o)\r\n\t\t{\r\n\t\t\tICS::IntervalList intervals = (*o)->getIntervals();\r\n\r\n\t\t\tif(intervals.size() > 1) // all channels have a default linear filter\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement ChannelFilter( \"ChannelFilter\" );\r\n\r\n\t\t\t\tChannelFilter.SetAttribute(\"number\", ToString<int>((*o)->getNumber()).c_str());\r\n\r\n\t\t\t\tICS::IntervalList::const_iterator interval = intervals.begin();\r\n\t\t\t\twhile( interval != intervals.end() )\r\n\t\t\t\t{\r\n\t\t\t\t\t// if not default linear filter\r\n\t\t\t\t\tif(!( interval->step == 0.2f\r\n\t\t\t\t\t\t&& interval->startX == 0.0f\r\n\t\t\t\t\t\t&& interval->startY == 0.0f\r\n\t\t\t\t\t\t&& interval->midX == 0.5f\r\n\t\t\t\t\t\t&& interval->midY == 0.5f\r\n\t\t\t\t\t\t&& interval->endX == 1.0f\r\n\t\t\t\t\t\t&& interval->endY == 1.0f ))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tTiXmlElement XMLInterval( \"Interval\" );\r\n\r\n\t\t\t\t\t\tXMLInterval.SetAttribute(\"type\", \"bezier\");\r\n\t\t\t\t\t\tXMLInterval.SetAttribute(\"step\", ToString<float>(interval->step).c_str());\r\n\r\n\t\t\t\t\t\tXMLInterval.SetAttribute(\"startX\", ToString<float>(interval->startX).c_str());\r\n\t\t\t\t\t\tXMLInterval.SetAttribute(\"startY\", ToString<float>(interval->startY).c_str());\r\n\t\t\t\t\t\tXMLInterval.SetAttribute(\"midX\", ToString<float>(interval->midX).c_str());\r\n\t\t\t\t\t\tXMLInterval.SetAttribute(\"midY\", ToString<float>(interval->midY).c_str());\r\n\t\t\t\t\t\tXMLInterval.SetAttribute(\"endX\", ToString<float>(interval->endX).c_str());\r\n\t\t\t\t\t\tXMLInterval.SetAttribute(\"endY\", ToString<float>(interval->endY).c_str());\r\n\r\n\t\t\t\t\t\tChannelFilter.InsertEndChild(XMLInterval);\r\n\t\t\t\t\t}\r\n\r\n                    ++interval;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tController.InsertEndChild(ChannelFilter);\r\n\t\t\t}\r\n\t\t}\r\n\r\n        for(std::vector<Control*>::const_iterator o = mControls.begin() ; o != mControls.end(); ++o)\r\n\t\t{\r\n\t\t\tTiXmlElement control( \"Control\" );\r\n\r\n\t\t\tcontrol.SetAttribute( \"name\", (*o)->getName().c_str() );\r\n\t\t\tif((*o)->getAutoChangeDirectionOnLimitsAfterStop())\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"autoChangeDirectionOnLimitsAfterStop\", \"true\" );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"autoChangeDirectionOnLimitsAfterStop\", \"false\" );\r\n\t\t\t}\r\n\t\t\tif((*o)->getAutoReverse())\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"autoReverseToInitialValue\", \"true\" );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"autoReverseToInitialValue\", \"false\" );\r\n\t\t\t}\r\n\t\t\tcontrol.SetAttribute( \"initialValue\", ToString<float>((*o)->getInitialValue()).c_str() );\r\n\r\n\t\t\tif((*o)->getStepSize() == ICS_MAX)\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"stepSize\", \"MAX\" );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"stepSize\", ToString<float>((*o)->getStepSize()).c_str() );\r\n\t\t\t}\r\n\r\n\t\t\tif((*o)->getStepsPerSeconds() == ICS_MAX)\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"stepsPerSeconds\", \"MAX\" );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"stepsPerSeconds\", ToString<float>((*o)->getStepsPerSeconds()).c_str() );\r\n\t\t\t}\r\n\r\n\t\t\tif(!(*o)->isAxisBindable())\r\n\t\t\t{\r\n\t\t\t\tcontrol.SetAttribute( \"axisBindable\", \"false\" );\r\n\t\t\t}\r\n\r\n            if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) != SDL_SCANCODE_UNKNOWN)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement keyBinder( \"KeyBinder\" );\r\n\r\n                keyBinder.SetAttribute( \"key\", ToString<int>(\r\n\t\t\t\t\tgetKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );\r\n\t\t\t\tkeyBinder.SetAttribute( \"direction\", \"INCREASE\" );\r\n\t\t\t\tcontrol.InsertEndChild(keyBinder);\r\n\t\t\t}\r\n\r\n            if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) != SDL_SCANCODE_UNKNOWN)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement keyBinder( \"KeyBinder\" );\r\n\r\n                keyBinder.SetAttribute( \"key\", ToString<int>(\r\n\t\t\t\t\tgetKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );\r\n\t\t\t\tkeyBinder.SetAttribute( \"direction\", \"DECREASE\" );\r\n\t\t\t\tcontrol.InsertEndChild(keyBinder);\r\n\t\t\t}\r\n\r\n\t\t\tif(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)\r\n\t\t\t\t!= InputControlSystem/*::NamedAxis*/::UNASSIGNED)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement binder( \"MouseBinder\" );\r\n\r\n\t\t\t\tInputControlSystem::NamedAxis axis =\r\n\t\t\t\t\tgetMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE);\r\n\t\t\t\tif(axis == InputControlSystem/*::NamedAxis*/::X)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"axis\", \"X\" );\r\n\t\t\t\t}\r\n\t\t\t\telse if(axis == InputControlSystem/*::NamedAxis*/::Y)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"axis\", \"Y\" );\r\n\t\t\t\t}\r\n\t\t\t\telse if(axis == InputControlSystem/*::NamedAxis*/::Z)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"axis\", \"Z\" );\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbinder.SetAttribute( \"direction\", \"INCREASE\" );\r\n\t\t\t\tcontrol.InsertEndChild(binder);\r\n\t\t\t}\r\n\r\n\t\t\tif(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)\r\n\t\t\t\t!= InputControlSystem/*::NamedAxis*/::UNASSIGNED)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement binder( \"MouseBinder\" );\r\n\r\n\t\t\t\tInputControlSystem::NamedAxis axis =\r\n\t\t\t\t\tgetMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE);\r\n\t\t\t\tif(axis == InputControlSystem/*::NamedAxis*/::X)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"axis\", \"X\" );\r\n\t\t\t\t}\r\n\t\t\t\telse if(axis == InputControlSystem/*::NamedAxis*/::Y)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"axis\", \"Y\" );\r\n\t\t\t\t}\r\n\t\t\t\telse if(axis == InputControlSystem/*::NamedAxis*/::Z)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"axis\", \"Z\" );\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbinder.SetAttribute( \"direction\", \"DECREASE\" );\r\n\t\t\t\tcontrol.InsertEndChild(binder);\r\n\t\t\t}\r\n\r\n\t\t\t/* ----------------------------------------------------------------------------------------\r\n\t\t\t * OPENMW CODE STARTS HERE\r\n\t\t\t * Mouse Wheel support added by Stomy */\r\n\r\n\t\t\tif(getMouseWheelBinding(*o, Control::INCREASE) != MouseWheelClick::UNASSIGNED)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement binder( \"MouseWheelBinder\" );\r\n\t\t\t\tMouseWheelClick click = getMouseWheelBinding(*o, Control::INCREASE);\r\n\t\t\t\tbool skip = false;\r\n\t\t\t\tswitch (click) {\r\n\t\t\t\t\tcase MouseWheelClick::UP: {\r\n\t\t\t\t\t\tbinder.SetAttribute(\"button\", \"UP\");\r\n\t\t\t\t\t} break;\r\n\t\t\t\t\tcase MouseWheelClick::DOWN: {\r\n\t\t\t\t\t\tbinder.SetAttribute(\"button\", \"DOWN\");\r\n\t\t\t\t\t} break;\r\n\t\t\t\t\tcase MouseWheelClick::RIGHT: {\r\n\t\t\t\t\t\tbinder.SetAttribute(\"button\", \"RIGHT\");\r\n\t\t\t\t\t} break;\r\n\t\t\t\t\tcase MouseWheelClick::LEFT: {\r\n\t\t\t\t\t\tbinder.SetAttribute(\"button\", \"LEFT\");\r\n\t\t\t\t\t} break;\r\n\t\t\t\t\tdefault: {\r\n\t\t\t\t\t\tskip = true;\r\n\t\t\t\t\t} break;\r\n\t\t\t\t}\r\n\t\t\t\tbinder.SetAttribute( \"direction\", \"INCREASE\" );\r\n\t\t\t\tif (!skip)\r\n\t\t\t\t\tcontrol.InsertEndChild(binder);\r\n\t\t\t}\r\n\r\n\t\t\tif(getMouseWheelBinding(*o, Control::DECREASE) != MouseWheelClick::UNASSIGNED)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement binder( \"MouseWheelBinder\" );\r\n\t\t\t\tMouseWheelClick click = getMouseWheelBinding(*o, Control::INCREASE);\r\n\t\t\t\tbool skip = false;\r\n\t\t\t\tswitch (click) {\r\n\t\t\t\t\tcase MouseWheelClick::UP: {\r\n\t\t\t\t\t\tbinder.SetAttribute(\"button\", \"UP\");\r\n\t\t\t\t\t} break;\r\n\t\t\t\t\tcase MouseWheelClick::DOWN: {\r\n\t\t\t\t\t\tbinder.SetAttribute(\"button\", \"DOWN\");\r\n\t\t\t\t\t} break;\r\n\t\t\t\t\tcase MouseWheelClick::RIGHT: {\r\n\t\t\t\t\t\tbinder.SetAttribute(\"button\", \"RIGHT\");\r\n\t\t\t\t\t} break;\r\n\t\t\t\t\tcase MouseWheelClick::LEFT: {\r\n\t\t\t\t\t\tbinder.SetAttribute(\"button\", \"LEFT\");\r\n\t\t\t\t\t} break;\r\n\t\t\t\t\tdefault: {\r\n\t\t\t\t\t\tskip = true;\r\n\t\t\t\t\t} break;\r\n\t\t\t\t}\r\n\t\t\t\tbinder.SetAttribute( \"direction\", \"DECREASE\" );\r\n\t\t\t\tif (!skip)\r\n\t\t\t\t\tcontrol.InsertEndChild(binder);\r\n\t\t\t}\r\n\r\n\t\t\t/* OPENMW CODE ENDS HERE\r\n\t\t\t * ------------------------------------------------------------------------------------- */\r\n\r\n\t\t\tif(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)\r\n\t\t\t\t!= ICS_MAX_DEVICE_BUTTONS)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement binder( \"MouseButtonBinder\" );\r\n\r\n\t\t\t\tunsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE);\r\n\t\t\t\tif(button == SDL_BUTTON_LEFT)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"button\", \"LEFT\" );\r\n\t\t\t\t}\r\n\t\t\t\telse if(button == SDL_BUTTON_MIDDLE)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"button\", \"MIDDLE\" );\r\n\t\t\t\t}\r\n\t\t\t\telse if(button == SDL_BUTTON_RIGHT)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"button\", \"RIGHT\" );\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"button\", ToString<unsigned int>(button).c_str() );\r\n\t\t\t\t}\r\n\t\t\t\tbinder.SetAttribute( \"direction\", \"INCREASE\" );\r\n\t\t\t\tcontrol.InsertEndChild(binder);\r\n\t\t\t}\r\n\r\n\t\t\tif(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)\r\n\t\t\t\t!= ICS_MAX_DEVICE_BUTTONS)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement binder( \"MouseButtonBinder\" );\r\n\r\n\t\t\t\tunsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE);\r\n\t\t\t\tif(button == SDL_BUTTON_LEFT)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"button\", \"LEFT\" );\r\n\t\t\t\t}\r\n\t\t\t\telse if(button == SDL_BUTTON_MIDDLE)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"button\", \"MIDDLE\" );\r\n\t\t\t\t}\r\n\t\t\t\telse if(button == SDL_BUTTON_RIGHT)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"button\", \"RIGHT\" );\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"button\", ToString<unsigned int>(button).c_str() );\r\n\t\t\t\t}\r\n\t\t\t\tbinder.SetAttribute( \"direction\", \"DECREASE\" );\r\n\t\t\t\tcontrol.InsertEndChild(binder);\r\n\t\t\t}\r\n\t\t\tJoystickIDList::const_iterator it = mJoystickIDList.begin();\r\n\t\t\twhile(it!=mJoystickIDList.end())\r\n\t\t\t{\r\n                int deviceID = *it;\r\n                if(getJoystickAxisBinding(*o, deviceID, Control/*::ControlChangingDirection*/::INCREASE)\r\n                    != /*NamedAxis::*/UNASSIGNED)\r\n                {\r\n                    TiXmlElement binder( \"JoystickAxisBinder\" );\r\n\r\n                    binder.SetAttribute( \"axis\", ToString<int>(\r\n                        getJoystickAxisBinding(*o, deviceID, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );\r\n\r\n                    binder.SetAttribute( \"direction\", \"INCREASE\" );\r\n\r\n                    binder.SetAttribute( \"deviceId\", deviceID ); //completely useless, but required for backwards compatability\r\n\r\n                    control.InsertEndChild(binder);\r\n                }\r\n\r\n                if(getJoystickAxisBinding(*o, deviceID, Control/*::ControlChangingDirection*/::DECREASE)\r\n                    != /*NamedAxis::*/UNASSIGNED)\r\n                {\r\n                    TiXmlElement binder( \"JoystickAxisBinder\" );\r\n\r\n                    binder.SetAttribute( \"axis\", ToString<int>(\r\n                        getJoystickAxisBinding(*o, deviceID, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );\r\n\r\n                    binder.SetAttribute( \"direction\", \"DECREASE\" );\r\n\r\n                    binder.SetAttribute( \"deviceId\", deviceID ); //completely useless, but required for backwards compatability\r\n\r\n                    control.InsertEndChild(binder);\r\n                }\r\n\r\n                if(getJoystickButtonBinding(*o, deviceID, Control/*::ControlChangingDirection*/::INCREASE)\r\n                    != ICS_MAX_DEVICE_BUTTONS)\r\n                {\r\n                    TiXmlElement binder( \"JoystickButtonBinder\" );\r\n\r\n                    binder.SetAttribute( \"button\", ToString<unsigned int>(\r\n                        getJoystickButtonBinding(*o, deviceID, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );\r\n\r\n                    binder.SetAttribute( \"direction\", \"INCREASE\" );\r\n\r\n                    binder.SetAttribute( \"deviceId\", deviceID ); //completely useless, but required for backwards compatability\r\n\r\n                    control.InsertEndChild(binder);\r\n                }\r\n\r\n                if(getJoystickButtonBinding(*o, deviceID, Control/*::ControlChangingDirection*/::DECREASE)\r\n                    != ICS_MAX_DEVICE_BUTTONS)\r\n                {\r\n                    TiXmlElement binder( \"JoystickButtonBinder\" );\r\n\r\n                    binder.SetAttribute( \"button\", ToString<unsigned int>(\r\n                        getJoystickButtonBinding(*o, deviceID, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );\r\n\r\n                    binder.SetAttribute( \"direction\", \"DECREASE\" );\r\n\r\n                    binder.SetAttribute( \"deviceId\", deviceID ); //completely useless, but required for backwards compatability\r\n\r\n                    control.InsertEndChild(binder);\r\n                }\r\n                it++;\r\n            }\r\n\r\n\r\n\t\t\tstd::list<Channel*> channels = (*o)->getAttachedChannels();\r\n\t\t\tfor(std::list<Channel*>::iterator it = channels.begin() ;\r\n                it != channels.end() ; ++it)\r\n\t\t\t{\r\n\t\t\t\tTiXmlElement binder( \"Channel\" );\r\n\r\n\t\t\t\tbinder.SetAttribute( \"number\", ToString<int>((*it)->getNumber()).c_str() );\r\n\r\n\t\t\t\tChannel::ChannelDirection direction = (*it)->getAttachedControlBinding(*o).direction;\r\n\t\t\t\tif(direction == Channel/*::ChannelDirection*/::DIRECT)\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"direction\", \"DIRECT\" );\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tbinder.SetAttribute( \"direction\", \"INVERSE\" );\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfloat percentage = (*it)->getAttachedControlBinding(*o).percentage;\r\n\t\t\t\tbinder.SetAttribute( \"percentage\", ToString<float>(percentage).c_str() );\r\n\r\n\t\t\t\tcontrol.InsertEndChild(binder);\r\n\t\t\t}\r\n\r\n\t\t\tController.InsertEndChild(control);\r\n\t\t}\r\n\r\n\t\tdoc.InsertEndChild(Controller);\r\n\t\treturn doc.SaveFile();\r\n\t}\r\n\r\n\tvoid InputControlSystem::update(float lTimeSinceLastFrame)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\r\n\t\t\tstd::vector<Control *>::const_iterator it;\r\n\t\t\tfor(it=mControls.begin(); it!=mControls.end(); ++it)\r\n\t\t\t{\r\n\t\t\t\t(*it)->update(lTimeSinceLastFrame);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t//! @todo Future versions should consider channel exponentials and mixtures, so\r\n\t\t// after updating Controls, Channels should be updated according to their values\r\n\t}\r\n\r\n\tfloat InputControlSystem::getChannelValue(int i)\r\n\t{\r\n\t\treturn std::max<float>(0.0,std::min<float>(1.0,mChannels[i]->getValue()));\r\n\t}\r\n\r\n\tfloat InputControlSystem::getControlValue(int i)\r\n\t{\r\n\t\treturn mControls[i]->getValue();\r\n\t}\r\n\r\n\tControl* InputControlSystem::findControl(std::string name)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\r\n\t\t\tstd::vector<Control *>::const_iterator it;\r\n\t\t\tfor(it = mControls.begin(); it != mControls.end(); ++it)\r\n\t\t\t{\r\n\t\t\t\tif( ((Control*)(*it))->getName() == name)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn (Control*)(*it);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tvoid InputControlSystem::enableDetectingBindingState(Control* control\r\n\t\t, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tmDetectingBindingControl = control;\r\n\t\tmDetectingBindingDirection = direction;\r\n\r\n\t\tmMouseAxisBindingInitialValues[0] = ICS_MOUSE_AXIS_BINDING_NULL_VALUE;\r\n\t}\r\n\r\n    bool InputControlSystem::detectingBindingState()\r\n    {\r\n        return mDetectingBindingControl != NULL;\r\n    }\r\n\r\n\tvoid InputControlSystem::cancelDetectingBindingState()\r\n\t{\r\n\t\tmDetectingBindingControl = NULL;\r\n\t}\r\n\r\n    std::string InputControlSystem::scancodeToString(SDL_Scancode key)\r\n\t{\r\n        SDL_Keycode code = SDL_GetKeyFromScancode(key);\r\n        if (code == SDLK_UNKNOWN)\r\n            return std::string(SDL_GetScancodeName(key));\r\n        else\r\n            return std::string(SDL_GetKeyName(code));\r\n\t}\r\n\r\n    void InputControlSystem::adjustMouseRegion(Uint16 width, Uint16 height)\r\n    {\r\n        mClientWidth = width;\r\n        mClientHeight = height;\r\n    }\r\n}\r\n"
  },
  {
    "path": "extern/oics/ICSInputControlSystem.h",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#ifndef OICS_InputControlSystem_H_\r\n#define OICS_InputControlSystem_H_\r\n\r\n#include \"ICSPrerequisites.h\"\r\n\r\n#include \"ICSControl.h\"\r\n#include \"ICSChannel.h\"\r\n\r\n#define ICS_LOG(text) if(mLog) mLog->logMessage( (\"ICS: \" + std::string(text)).c_str() );\r\n#define ICS_MAX_JOYSTICK_AXIS 16\r\n#define ICS_MOUSE_BINDING_MARGIN 30\r\n#define ICS_JOYSTICK_AXIS_BINDING_MARGIN 10000\r\n#define ICS_JOYSTICK_SLIDER_BINDING_MARGIN 10000\r\n#define ICS_MOUSE_AXIS_BINDING_NULL_VALUE std::numeric_limits<int>::max()\r\n\r\nnamespace ICS\r\n{\r\n\tclass DllExport InputControlSystemLog\r\n\t{\r\n\tpublic:\r\n\t\tvirtual void logMessage(const char* text) = 0;\r\n\r\n\t\tvirtual ~InputControlSystemLog() = default;\r\n\t};\r\n\r\n    class DllExport InputControlSystem\r\n    {\r\n\r\n\tpublic:\r\n\r\n\t\tenum NamedAxis { X = -1, Y = -2, Z = -3, UNASSIGNED = -4 };\r\n\t\tenum POVAxis { NorthSouth = 0, EastWest = 1 };\r\n\r\n\t\ttypedef NamedAxis MouseAxis; // MouseAxis is deprecated. It will be removed in future versions\r\n\r\n\t\ttypedef std::map<int, SDL_GameController*> JoystickInstanceMap;\r\n\t\ttypedef std::list<int> JoystickIDList;\r\n\r\n\t\ttypedef struct\r\n\t\t{\r\n\t\t\tint index;\r\n\t\t\tPOVAxis axis;\r\n\t\t} POVBindingPair;\r\n\r\n\t\tInputControlSystem(std::string file = \"\", bool active = true\r\n\t\t\t, DetectingBindingListener* detectingBindingListener = NULL\r\n\t\t\t, InputControlSystemLog* log = NULL, size_t channelCount = 16);\r\n\t\t~InputControlSystem();\r\n\r\n\t\tstd::string getFileName(){ return mFileName; };\r\n\t\tstd::string getBaseFileName();\r\n\r\n\t\tvoid setDetectingBindingListener(DetectingBindingListener* detectingBindingListener){ mDetectingBindingListener = detectingBindingListener; };\r\n\t\tDetectingBindingListener* getDetectingBindingListener(){ return mDetectingBindingListener; };\r\n\r\n\t\tvoid setJoystickDeadZone(float deadZone){ mDeadZone = deadZone; };\r\n\r\n\t\t// in seconds\r\n\t\tvoid update(float timeSinceLastFrame);\r\n\r\n        inline Channel* getChannel(int i){ return mChannels[i]; };\r\n\t\tfloat getChannelValue(int i);\r\n\t\tinline int getChannelCount(){ return (int)mChannels.size(); };\r\n\r\n\t\tinline Control* getControl(int i){ return mControls[i]; };\r\n\t\tfloat getControlValue(int i);\r\n\t\tinline int getControlCount(){ return (int)mControls.size(); };\r\n\t\tinline void addControl(Control* control){ mControls.push_back(control); };\r\n\r\n\t\tControl* findControl(std::string name);\r\n\r\n\t\tinline void activate(){ this->mActive = true; };\r\n\t\tinline void deactivate(){ this->mActive = false; };\r\n\r\n\t\tvoid controllerAdded  (int deviceID, const SDL_ControllerDeviceEvent &args);\r\n\t\tvoid controllerRemoved(const SDL_ControllerDeviceEvent &args);\r\n\t\tJoystickIDList& getJoystickIdList(){ return mJoystickIDList; };\r\n\t\tJoystickInstanceMap& getJoystickInstanceMap(){ return mJoystickInstanceMap; };\r\n\r\n\t\t// MouseListener\r\n        void mouseMoved(const SDL_MouseMotionEvent &evt);\r\n        void mousePressed(const SDL_MouseButtonEvent &evt, Uint8);\r\n        void mouseReleased(const SDL_MouseButtonEvent &evt, Uint8);\r\n\r\n\t\t// KeyListener\r\n        void keyPressed(const SDL_KeyboardEvent &evt);\r\n        void keyReleased(const SDL_KeyboardEvent &evt);\r\n\r\n\t\t// ControllerListener\r\n        void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &evt);\r\n        void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &evt);\r\n        void axisMoved(int deviceID, const SDL_ControllerAxisEvent &evt);\r\n\r\n        void addKeyBinding(Control* control, SDL_Scancode key, Control::ControlChangingDirection direction);\r\n        bool isKeyBound(SDL_Scancode key) const;\r\n\t\tvoid addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction);\r\n\t\tvoid addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction);\r\n        bool isMouseButtonBound(unsigned int button) const;\r\n        void addJoystickAxisBinding(Control* control, int deviceID, int axis, Control::ControlChangingDirection direction);\r\n\t\tvoid addJoystickButtonBinding(Control* control, int deviceID, unsigned int button, Control::ControlChangingDirection direction);\r\n\t\tbool isJoystickButtonBound(int deviceID, unsigned int button) const;\r\n\t\tbool isJoystickAxisBound(int deviceID, unsigned int axis) const;\r\n        void removeKeyBinding(SDL_Scancode key);\r\n\t\tvoid removeMouseAxisBinding(NamedAxis axis);\r\n\t\tvoid removeMouseButtonBinding(unsigned int button);\r\n\t\tvoid removeJoystickAxisBinding(int deviceID, int axis);\r\n\t\tvoid removeJoystickButtonBinding(int deviceID, unsigned int button);\r\n\r\n        SDL_Scancode getKeyBinding(Control* control, ICS::Control::ControlChangingDirection direction);\r\n\t\tNamedAxis getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction);\r\n\t\tunsigned int getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction);\r\n\t\tint getJoystickAxisBinding(Control* control, int deviceID, ICS::Control::ControlChangingDirection direction);\r\n\t\tunsigned int getJoystickButtonBinding(Control* control, int deviceID, ICS::Control::ControlChangingDirection direction);\r\n\r\n        std::string scancodeToString(SDL_Scancode key);\r\n\r\n\t\tvoid enableDetectingBindingState(Control* control, Control::ControlChangingDirection direction);\r\n\t\tvoid cancelDetectingBindingState();\r\n        bool detectingBindingState();\r\n\r\n\t\tbool save(std::string fileName = \"\");\r\n\r\n\t\tvoid adjustMouseRegion (Uint16 width, Uint16 height);\r\n\r\n\tprotected:\r\n\r\n\t\tvoid loadKeyBinders(TiXmlElement* xmlControlNode);\r\n\t\tvoid loadMouseAxisBinders(TiXmlElement* xmlControlNode);\r\n\t\tvoid loadMouseButtonBinders(TiXmlElement* xmlControlNode);\r\n\t\tvoid loadJoystickAxisBinders(TiXmlElement* xmlControlNode);\r\n\t\tvoid loadJoystickButtonBinders(TiXmlElement* xmlControlNode);\r\n\t\tvoid loadJoystickPOVBinders(TiXmlElement* xmlControlNode);\r\n\t\tvoid loadJoystickSliderBinders(TiXmlElement* xmlControlNode);\r\n\r\n\t\tvoid addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction);\r\n\t\tvoid removeMouseAxisBinding_(int axis);\r\n\r\n\tprotected:\r\n\r\n\t\ttypedef struct {\r\n\t\t\tControl::ControlChangingDirection direction;\r\n\t\t\tControl* control;\r\n\t\t} ControlKeyBinderItem;\r\n\r\n\t\ttypedef ControlKeyBinderItem ControlAxisBinderItem;\r\n\t\ttypedef ControlKeyBinderItem ControlButtonBinderItem;\r\n\t\ttypedef ControlKeyBinderItem ControlPOVBinderItem;\r\n\t\ttypedef ControlKeyBinderItem ControlSliderBinderItem;\r\n\r\n\t\ttypedef struct {\r\n\t\t\tControl* control;\r\n\t\t\tControl::ControlChangingDirection direction;\r\n\t\t} PendingActionItem;\r\n\r\n\t\tstd::list<PendingActionItem> mPendingActions;\r\n\r\n\t\tstd::string mFileName;\r\n\r\n\t\tfloat mDeadZone;\r\n\r\n        typedef std::map<SDL_Scancode, ControlKeyBinderItem> ControlsKeyBinderMapType;\t// <Scancode, [direction, control]>\r\n\t\ttypedef std::map<int, ControlAxisBinderItem> ControlsAxisBinderMapType;\t\t\t// <axis, [direction, control]>\r\n\t\ttypedef std::map<int, ControlButtonBinderItem> ControlsButtonBinderMapType;\t\t// <button, [direction, control]>\r\n\r\n\t\ttypedef std::map<int, ControlsAxisBinderMapType> JoystickAxisBinderMapType;\t\t\t\t\t// <joystick_id, <axis, [direction, control]> >\r\n\t\ttypedef std::map<int, ControlsButtonBinderMapType> JoystickButtonBinderMapType;\t\t\t\t// <joystick_id, <button, [direction, control]> >\r\n\r\n\t\tControlsAxisBinderMapType mControlsMouseAxisBinderMap;\t\t\t// <axis, [direction, control]>\r\n\t\tControlsButtonBinderMapType mControlsMouseButtonBinderMap;\t\t// <int, [direction, control]>\r\n\t\tJoystickAxisBinderMapType mControlsJoystickAxisBinderMap;\t\t// <axis, [direction, control]>\r\n\t\tJoystickButtonBinderMapType mControlsJoystickButtonBinderMap;\t// <button, [direction, control]>\r\n\r\n\t\tstd::vector<Control *> mControls;\r\n\t\tstd::vector<Channel *> mChannels;\r\n\r\n\t\tControlsKeyBinderMapType mControlsKeyBinderMap;\r\n\r\n\t\tbool mActive;\r\n\t\tInputControlSystemLog* mLog;\r\n\r\n\t\tDetectingBindingListener* mDetectingBindingListener;\r\n\t\tControl* mDetectingBindingControl;\r\n\t\tControl::ControlChangingDirection mDetectingBindingDirection;\r\n\r\n\t\tbool mXmouseAxisBinded;\r\n\t\tbool mYmouseAxisBinded;\r\n\r\n\t\tJoystickIDList mJoystickIDList;\r\n\t\tJoystickInstanceMap mJoystickInstanceMap;\r\n\r\n\t\tint mMouseAxisBindingInitialValues[3];\r\n\r\n\tprivate:\r\n\r\n\t\tUint16 mClientWidth;\r\n\t\tUint16 mClientHeight;\r\n\r\n\t\t/* ----------------------------------------------------------------------------------------\r\n\t\t * OPENMW CODE STARTS HERE\r\n\t\t * Mouse Wheel support added by Michael Stopa (Stomy) */\r\n\r\n\tpublic:\r\n\t\tenum class MouseWheelClick : int { UNASSIGNED = 0, UP = 1, DOWN = 2, LEFT = 3, RIGHT = 4};\r\n\r\n\t\tvoid mouseWheelMoved(const SDL_MouseWheelEvent &evt);\r\n\t\tvoid addMouseWheelBinding(Control* control, MouseWheelClick click, Control::ControlChangingDirection direction);\r\n\t\tvoid removeMouseWheelBinding(MouseWheelClick click);\r\n\t\tMouseWheelClick getMouseWheelBinding(Control* control, ICS::Control::ControlChangingDirection direction);\r\n\t\tbool isMouseWheelBound(MouseWheelClick button) const;\r\n\r\n\tprotected:\r\n\t\tvoid loadMouseWheelBinders(TiXmlElement* xmlControlNode);\r\n\t\tControlsButtonBinderMapType mControlsMouseWheelBinderMap;\r\n\r\n\t\t/* OPENMW CODE ENDS HERE\r\n\t\t * ------------------------------------------------------------------------------------- */\r\n\t};\r\n\r\n\tclass DllExport DetectingBindingListener\r\n\t{\r\n\tpublic:\r\n\t\tvirtual void keyBindingDetected(InputControlSystem* ICS, Control* control\r\n            , SDL_Scancode key, Control::ControlChangingDirection direction);\r\n\r\n\t\tvirtual void mouseAxisBindingDetected(InputControlSystem* ICS, Control* control\r\n\t\t\t, InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction);\r\n\r\n\t\tvirtual void mouseButtonBindingDetected(InputControlSystem* ICS, Control* control\r\n\t\t\t, unsigned int button, Control::ControlChangingDirection direction);\r\n\r\n\t\tvirtual void joystickAxisBindingDetected(InputControlSystem* ICS, int deviceID, Control* control\r\n\t\t\t, int axis, Control::ControlChangingDirection direction);\r\n\r\n\t\tvirtual void joystickButtonBindingDetected(InputControlSystem* ICS, int deviceID, Control* control\r\n\t\t\t, unsigned int button, Control::ControlChangingDirection direction);\r\n\r\n\t\t/* ----------------------------------------------------------------------------------------\r\n\t\t * OPENMW CODE STARTS HERE\r\n\t\t * Mouse Wheel support added by Michael Stopa (Stomy) */\r\n\r\n\t\tvirtual void mouseWheelBindingDetected(InputControlSystem* ICS, Control* control,\r\n\t\t                                       InputControlSystem::MouseWheelClick click,\r\n\t\t                                       Control::ControlChangingDirection direction);\r\n\r\n        virtual ~DetectingBindingListener() = default;\r\n\r\n\t\t/* OPENMW CODE ENDS HERE\r\n\t\t * ------------------------------------------------------------------------------------- */\r\n\t};\r\n\r\n\textern const float ICS_MAX;\r\n}\r\n\r\n\r\n#endif\r\n"
  },
  {
    "path": "extern/oics/ICSInputControlSystem_joystick.cpp",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#include \"ICSInputControlSystem.h\"\r\n\r\n#define SDL_JOY_AXIS_MIN -32768\r\n#define SDL_JOY_AXIS_MAX 32767\r\n\r\nnamespace ICS\r\n{\r\n\t// load xml\r\n\tvoid InputControlSystem::loadJoystickAxisBinders(TiXmlElement* xmlControlNode)\r\n\t{\r\n\t\tTiXmlElement* xmlJoystickBinder = xmlControlNode->FirstChildElement(\"JoystickAxisBinder\");\r\n\t\twhile(xmlJoystickBinder)\r\n\t\t{\r\n\t\t\tControl::ControlChangingDirection dir = Control::STOP;\r\n\t\t\tif(std::string(xmlJoystickBinder->Attribute(\"direction\")) == \"INCREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::INCREASE;\r\n\t\t\t}\r\n\t\t\telse if(std::string(xmlJoystickBinder->Attribute(\"direction\")) == \"DECREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::DECREASE;\r\n\t\t\t}\r\n\r\n\t\t\taddJoystickAxisBinding(mControls.back(), FromString<int>(xmlJoystickBinder->Attribute(\"deviceId\")), FromString<int>(xmlJoystickBinder->Attribute(\"axis\")), dir);\r\n\r\n\t\t\txmlJoystickBinder = xmlJoystickBinder->NextSiblingElement(\"JoystickAxisBinder\");\r\n\t\t}\r\n\t}\r\n\r\n\tvoid InputControlSystem::loadJoystickButtonBinders(TiXmlElement* xmlControlNode)\r\n\t{\r\n\t\tTiXmlElement* xmlJoystickButtonBinder = xmlControlNode->FirstChildElement(\"JoystickButtonBinder\");\r\n\t\twhile(xmlJoystickButtonBinder)\r\n\t\t{\r\n\t\t\tControl::ControlChangingDirection dir = Control::STOP;\r\n\t\t\tif(std::string(xmlJoystickButtonBinder->Attribute(\"direction\")) == \"INCREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::INCREASE;\r\n\t\t\t}\r\n\t\t\telse if(std::string(xmlJoystickButtonBinder->Attribute(\"direction\")) == \"DECREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::DECREASE;\r\n\t\t\t}\r\n\r\n\t\t\taddJoystickButtonBinding(mControls.back(), FromString<int>(xmlJoystickButtonBinder->Attribute(\"deviceId\")), FromString<int>(xmlJoystickButtonBinder->Attribute(\"button\")), dir);\n\r\n\t\t\txmlJoystickButtonBinder = xmlJoystickButtonBinder->NextSiblingElement(\"JoystickButtonBinder\");\r\n\t\t}\r\n\t}\r\n\r\n\t// add bindings\r\n\tvoid InputControlSystem::addJoystickAxisBinding(Control* control, int deviceID, int axis, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tif (std::find(mJoystickIDList.begin(), mJoystickIDList.end(), deviceID) == mJoystickIDList.end())\r\n\t\t\tmJoystickIDList.push_back(deviceID);\r\n\r\n\t\tICS_LOG(\"\\tAdding AxisBinder [axis=\"\r\n\t\t\t+ ToString<int>(axis)       + \", deviceID=\"\n\t\t\t+ ToString<int>(deviceID)   + \", direction=\"\r\n\t\t\t+ ToString<int>(direction)  + \"]\");\n\n        control->setValue(0.5f); //all joystick axis start at .5, so do that\r\n\r\n\t\tControlAxisBinderItem controlAxisBinderItem;\r\n\t\tcontrolAxisBinderItem.control = control;\r\n\t\tcontrolAxisBinderItem.direction = direction;\r\n\t\tmControlsJoystickAxisBinderMap[deviceID][axis] = controlAxisBinderItem;\r\n\t}\r\n\r\n\tvoid InputControlSystem::addJoystickButtonBinding(Control* control, int deviceID, unsigned int button, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tif (std::find(mJoystickIDList.begin(), mJoystickIDList.end(), deviceID) == mJoystickIDList.end())\r\n\t\t\tmJoystickIDList.push_back(deviceID); // Hack: add the device to the list so bindings are saved in save() even when joystick is not connected\r\n\r\n\t\tICS_LOG(\"\\tAdding JoystickButtonBinder [button=\"\r\n\t\t\t+ ToString<int>(button)     + \", deviceID=\"\n\t\t\t+ ToString<int>(deviceID)   + \", direction=\"\r\n\t\t\t+ ToString<int>(direction)  + \"]\");\r\n\r\n\t\tControlButtonBinderItem controlJoystickButtonBinderItem;\r\n\t\tcontrolJoystickButtonBinderItem.direction = direction;\r\n\t\tcontrolJoystickButtonBinderItem.control = control;\r\n\t\tmControlsJoystickButtonBinderMap[deviceID][button] = controlJoystickButtonBinderItem;\r\n\t}\r\n\r\n\tbool InputControlSystem::isJoystickButtonBound(int deviceID, unsigned int button) const\r\n\t{\r\n\t\tJoystickButtonBinderMapType::const_iterator found = mControlsJoystickButtonBinderMap.find(deviceID);\r\n\t\tif (found == mControlsJoystickButtonBinderMap.end())\r\n\t\t\treturn false;\r\n\r\n\t\treturn (found->second.find(button) != found->second.end());\r\n\t}\r\n\r\n\tbool InputControlSystem::isJoystickAxisBound(int deviceID, unsigned int axis) const\r\n\t{\r\n\t\tJoystickAxisBinderMapType::const_iterator found = mControlsJoystickAxisBinderMap.find(deviceID);\r\n\t\tif (found == mControlsJoystickAxisBinderMap.end())\r\n\t\t\treturn false;\r\n\r\n\t\treturn (found->second.find(axis) != found->second.end());\r\n\t}\r\n\r\n\t// get bindings\r\n\tint InputControlSystem::getJoystickAxisBinding(Control* control, int deviceID, ICS::Control::ControlChangingDirection direction)\r\n\t{\n        if(mControlsJoystickAxisBinderMap.find(deviceID) != mControlsJoystickAxisBinderMap.end())\n\t\t{\r\n            ControlsAxisBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceID].begin();\r\n            while(it != mControlsJoystickAxisBinderMap[deviceID].end())\r\n            {\r\n                if(it->first >= 0 && it->second.control == control && it->second.direction == direction)\r\n                {\r\n                    return it->first;\r\n                }\r\n                ++it;\r\n            }\n        }\r\n\r\n\t\treturn /*NamedAxis::*/UNASSIGNED;\r\n\t}\r\n\r\n\tunsigned int InputControlSystem::getJoystickButtonBinding(Control* control, int deviceID, ICS::Control::ControlChangingDirection direction)\r\n\t{\n        if(mControlsJoystickButtonBinderMap.find(deviceID) != mControlsJoystickButtonBinderMap.end())\n\t\t{\r\n            ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceID].begin();\r\n            while(it != mControlsJoystickButtonBinderMap[deviceID].end())\r\n            {\r\n                if(it->second.control == control && it->second.direction == direction)\r\n                {\r\n                    return it->first;\r\n                }\r\n                ++it;\r\n            }\n        }\r\n\r\n\t\treturn ICS_MAX_DEVICE_BUTTONS;\r\n\t}\r\n\r\n\t// remove bindings\r\n\tvoid InputControlSystem::removeJoystickAxisBinding(int deviceID, int axis)\r\n\t{\n        if(mControlsJoystickAxisBinderMap.find(deviceID) != mControlsJoystickAxisBinderMap.end())\n\t\t{\r\n            ControlsAxisBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceID].find(axis);\r\n            if(it != mControlsJoystickAxisBinderMap[deviceID].end())\r\n            {\r\n                mControlsJoystickAxisBinderMap[deviceID].erase(it);\r\n            }\n        }\r\n\t}\r\n\r\n\tvoid InputControlSystem::removeJoystickButtonBinding(int deviceID, unsigned int button)\r\n\t{\n        if(mControlsJoystickButtonBinderMap.find(deviceID) != mControlsJoystickButtonBinderMap.end())\n\t\t{\r\n            ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceID].find(button);\r\n            if(it != mControlsJoystickButtonBinderMap[deviceID].end())\r\n            {\r\n                mControlsJoystickButtonBinderMap[deviceID].erase(it);\r\n            }\n        }\r\n\t}\r\n\r\n\t// joyStick listeners\r\n    void InputControlSystem::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &evt)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\r\n\t\t\tif(!mDetectingBindingControl)\r\n\t\t\t{\n                if(mControlsJoystickButtonBinderMap.find(deviceID) != mControlsJoystickButtonBinderMap.end())\n                {\r\n                    ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[deviceID].find(evt.button);\r\n                    if(it != mControlsJoystickButtonBinderMap[deviceID].end())\r\n                    {\r\n                        it->second.control->setIgnoreAutoReverse(false);\r\n                        if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())\r\n                        {\r\n                            it->second.control->setChangingDirection(it->second.direction);\r\n                        }\r\n                        else\r\n                        {\r\n                            if(it->second.control->getValue() == 1)\r\n                            {\r\n                                it->second.control->setChangingDirection(Control::DECREASE);\r\n                            }\r\n                            else if(it->second.control->getValue() == 0)\r\n                            {\r\n                                it->second.control->setChangingDirection(Control::INCREASE);\r\n                            }\r\n                        }\r\n                    }\n                }\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n    void InputControlSystem::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &evt)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\n            if(!mDetectingBindingControl)\n            {\n                if(mControlsJoystickButtonBinderMap.find(deviceID) != mControlsJoystickButtonBinderMap.end())\n                {\r\n                    ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[deviceID].find(evt.button);\r\n                    if(it != mControlsJoystickButtonBinderMap[deviceID].end())\r\n                    {\r\n                        it->second.control->setChangingDirection(Control::STOP);\r\n                    }\n                }\n            }\n            else if(mDetectingBindingListener)\r\n\t\t\t{\r\n\t\t\t\tmDetectingBindingListener->joystickButtonBindingDetected(this, deviceID,\r\n\t\t\t\t\tmDetectingBindingControl, evt.button, mDetectingBindingDirection);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n    void InputControlSystem::axisMoved(int deviceID, const SDL_ControllerAxisEvent &evt)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\r\n\t\t\tif(!mDetectingBindingControl)\r\n\t\t\t{\n                if(mControlsJoystickAxisBinderMap.find(deviceID) != mControlsJoystickAxisBinderMap.end())\n                {\r\n                    ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[deviceID][evt.axis]; // joystic axis start at 0 index\r\n                    Control* ctrl = joystickBinderItem.control;\r\n                    if(ctrl)\r\n                    {\r\n                        ctrl->setIgnoreAutoReverse(true);\r\n\r\n                        float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MIN;\r\n                        float valDisplaced = (float)(evt.value - SDL_JOY_AXIS_MIN);\n                        float percent = valDisplaced / axisRange * (1+mDeadZone*2) - mDeadZone; //Assures all values, 0 through 1, are seen\n                        if(percent > .5-mDeadZone && percent < .5+mDeadZone) //close enough to center\n                            percent = .5;\n                        else if(percent > .5)\n                            percent -= mDeadZone;\n                        else\n                            percent += mDeadZone;\r\n\r\n                        if(joystickBinderItem.direction == Control::INCREASE)\r\n                        {\r\n                            ctrl->setValue( percent );\r\n                        }\r\n                        else if(joystickBinderItem.direction == Control::DECREASE)\r\n                        {\r\n                            ctrl->setValue( 1 - ( percent ) );\r\n                        }\r\n                    }\n                }\r\n\t\t\t}\r\n\t\t\telse if(mDetectingBindingListener)\r\n\t\t\t{\r\n\t\t\t\t//ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.which ][ axis ]; // joystic axis start at 0 index\r\n\t\t\t\t//Control* ctrl = joystickBinderItem.control;\r\n\t\t\t\t//if(ctrl && ctrl->isAxisBindable())\r\n\t\t\t\tif(mDetectingBindingControl->isAxisBindable())\r\n\t\t\t\t{\r\n\t\t\t\t\tif( abs( evt.value ) > ICS_JOYSTICK_AXIS_BINDING_MARGIN)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmDetectingBindingListener->joystickAxisBindingDetected(this, deviceID,\r\n\t\t\t\t\t\t\tmDetectingBindingControl, evt.axis, mDetectingBindingDirection);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\n\n    void InputControlSystem::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &args)\r\n\t{\r\n\t\tICS_LOG(\"Adding joystick (index: \" + ToString<int>(args.which) + \")\");\n\t\tSDL_GameController* cntrl = SDL_GameControllerOpen(args.which);\n        int instanceID = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(cntrl));\n        if(std::find(mJoystickIDList.begin(), mJoystickIDList.end(), deviceID)==mJoystickIDList.end())\n        {\r\n            for(int j = 0 ; j < ICS_MAX_JOYSTICK_AXIS ; j++)\r\n            {\r\n                if(mControlsJoystickAxisBinderMap[deviceID].find(j) == mControlsJoystickAxisBinderMap[deviceID].end())\r\n                {\r\n                    ControlAxisBinderItem controlJoystickBinderItem;\r\n                    controlJoystickBinderItem.direction = Control::STOP;\r\n                    controlJoystickBinderItem.control = NULL;\r\n                    mControlsJoystickAxisBinderMap[deviceID][j] = controlJoystickBinderItem;\r\n                }\r\n            }\n            mJoystickIDList.push_front(deviceID);\n\t\t}\n\r\n\t\tmJoystickInstanceMap[instanceID] = cntrl;\r\n\t}\n\tvoid InputControlSystem::controllerRemoved(const SDL_ControllerDeviceEvent &args)\n\t{\n        ICS_LOG(\"Removing joystick (instance id: \" + ToString<int>(args.which) + \")\");\n        if(mJoystickInstanceMap.count(args.which)!=0)\n        {\n            SDL_GameControllerClose(mJoystickInstanceMap.at(args.which));\n            mJoystickInstanceMap.erase(args.which);\n        }\n\t}\r\n\r\n\t// joystick auto bindings\r\n\tvoid DetectingBindingListener::joystickAxisBindingDetected(InputControlSystem* ICS, int deviceID, Control* control, int axis, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\t// if the joystick axis is used by another control, remove it\r\n\t\tICS->removeJoystickAxisBinding(deviceID, axis);\r\n\r\n\t\t// if the control has an axis assigned, remove it\r\n\t\tint oldAxis = ICS->getJoystickAxisBinding(control, deviceID, direction);\r\n\t\tif(oldAxis != InputControlSystem::UNASSIGNED)\r\n\t\t{\r\n\t\t\tICS->removeJoystickAxisBinding(deviceID, oldAxis);\r\n\t\t}\r\n\r\n\t\tICS->addJoystickAxisBinding(control, deviceID, axis, direction);\r\n\t\tICS->cancelDetectingBindingState();\r\n\t}\r\n\tvoid DetectingBindingListener::joystickButtonBindingDetected(InputControlSystem* ICS, int deviceID, Control* control\r\n\t\t, unsigned int button, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\t// if the joystick button is used by another control, remove it\r\n\t\tICS->removeJoystickButtonBinding(deviceID, button);\r\n\r\n\t\t// if the control has a joystick button assigned, remove it\r\n\t\tunsigned int oldButton = ICS->getJoystickButtonBinding(control, deviceID, direction);\r\n\t\tif(oldButton != ICS_MAX_DEVICE_BUTTONS)\r\n\t\t{\r\n\t\t\tICS->removeJoystickButtonBinding(deviceID, oldButton);\r\n\t\t}\r\n\r\n\t\tICS->addJoystickButtonBinding(control, deviceID, button, direction);\r\n\t\tICS->cancelDetectingBindingState();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "extern/oics/ICSInputControlSystem_keyboard.cpp",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#include \"ICSInputControlSystem.h\"\r\n\r\nnamespace ICS\r\n{\r\n\tvoid InputControlSystem::loadKeyBinders(TiXmlElement* xmlControlNode)\r\n\t{\r\n\t\tTiXmlElement* xmlKeyBinder = xmlControlNode->FirstChildElement(\"KeyBinder\");    \r\n\t\twhile(xmlKeyBinder)\r\n\t\t{\r\n\t\t\tControl::ControlChangingDirection dir = Control::STOP;\r\n\t\t\tif(std::string(xmlKeyBinder->Attribute(\"direction\")) == \"INCREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::INCREASE;\r\n\t\t\t}\r\n\t\t\telse if(std::string(xmlKeyBinder->Attribute(\"direction\")) == \"DECREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::DECREASE;\r\n\t\t\t}\r\n\r\n            addKeyBinding(mControls.back(), SDL_Scancode(FromString<int>(xmlKeyBinder->Attribute(\"key\"))), dir);\r\n\r\n\t\t\txmlKeyBinder = xmlKeyBinder->NextSiblingElement(\"KeyBinder\");\r\n\t\t}\r\n\t}\r\n\r\n    void InputControlSystem::addKeyBinding(Control* control, SDL_Scancode key, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tICS_LOG(\"\\tAdding KeyBinder [key=\"\r\n            + scancodeToString(key) + \", direction=\"\r\n\t\t\t+ ToString<int>(direction) + \"]\");\r\n\r\n\t\tControlKeyBinderItem controlKeyBinderItem;        \r\n\t\tcontrolKeyBinderItem.control = control;\r\n\t\tcontrolKeyBinderItem.direction = direction;\r\n\t\tmControlsKeyBinderMap[ key ] = controlKeyBinderItem;\r\n\t}\r\n\r\n    bool InputControlSystem::isKeyBound(SDL_Scancode key) const\r\n    {\r\n        return mControlsKeyBinderMap.find(key) != mControlsKeyBinderMap.end();\r\n    }\r\n\r\n    void InputControlSystem::removeKeyBinding(SDL_Scancode key)\r\n\t{\r\n\t\tControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.find(key);\r\n\t\tif(it != mControlsKeyBinderMap.end())\r\n\t\t{\r\n\t\t\tmControlsKeyBinderMap.erase(it);\r\n\t\t}\r\n\t}\r\n\r\n    SDL_Scancode InputControlSystem::getKeyBinding(Control* control\r\n\t\t, ICS::Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.begin();\r\n\t\twhile(it != mControlsKeyBinderMap.end())\r\n\t\t{\r\n\t\t\tif(it->second.control == control && it->second.direction == direction)\r\n\t\t\t{\r\n\t\t\t\treturn it->first;\r\n\t\t\t}\r\n            ++it;\r\n\t\t}\r\n\r\n        return SDL_SCANCODE_UNKNOWN;\r\n\t}\r\n    void InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\r\n\t\t\tif(!mDetectingBindingControl)\r\n\t\t\t{\r\n                ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.keysym.scancode);\r\n\t\t\t\tif(it != mControlsKeyBinderMap.end())\r\n\t\t\t\t{\r\n\t\t\t\t\tit->second.control->setIgnoreAutoReverse(false);\r\n\t\t\t\t\tif(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tit->second.control->setChangingDirection(it->second.direction);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{                   \r\n\t\t\t\t\t\tif(it->second.control->getValue() == 1)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tit->second.control->setChangingDirection(Control::DECREASE);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if(it->second.control->getValue() == 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tit->second.control->setChangingDirection(Control::INCREASE);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if(mDetectingBindingListener)\r\n\t\t\t{\r\n\t\t\t\tmDetectingBindingListener->keyBindingDetected(this,\r\n                    mDetectingBindingControl, evt.keysym.scancode, mDetectingBindingDirection);\r\n\t\t\t}\r\n\t\t}\r\n    }\r\n\r\n    void InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\r\n            ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.keysym.scancode);\r\n\t\t\tif(it != mControlsKeyBinderMap.end())\r\n\t\t\t{\r\n\t\t\t\tit->second.control->setChangingDirection(Control::STOP);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control\r\n        , SDL_Scancode key, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\t// if the key is used by another control, remove it\r\n\t\tICS->removeKeyBinding(key);\r\n\r\n\t\t// if the control has a key assigned, remove it\r\n        SDL_Scancode oldKey = ICS->getKeyBinding(control, direction);\r\n        if(oldKey != SDL_SCANCODE_UNKNOWN)\r\n\t\t{\r\n\t\t\tICS->removeKeyBinding(oldKey);\r\n\t\t}\r\n\r\n\t\tICS->addKeyBinding(control, key, direction);\r\n\t\tICS->cancelDetectingBindingState();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "extern/oics/ICSInputControlSystem_mouse.cpp",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#include \"ICSInputControlSystem.h\"\r\n\r\nnamespace ICS\r\n{\r\n\t// load xml\r\n\tvoid InputControlSystem::loadMouseAxisBinders(TiXmlElement* xmlControlNode)\r\n\t{\r\n\t\tTiXmlElement* xmlMouseBinder = xmlControlNode->FirstChildElement(\"MouseBinder\");    \r\n\t\twhile(xmlMouseBinder)\r\n\t\t{\r\n\t\t\tControl::ControlChangingDirection dir = Control::STOP;\r\n\t\t\tif(std::string(xmlMouseBinder->Attribute(\"direction\")) == \"INCREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::INCREASE;\r\n\t\t\t}\r\n\t\t\telse if(std::string(xmlMouseBinder->Attribute(\"direction\")) == \"DECREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::DECREASE;\r\n\t\t\t}\r\n\r\n\t\t\tNamedAxis axis = /*NamedAxis::*/ X;\r\n\t\t\tif((*xmlMouseBinder->Attribute(\"axis\")) == 'Y')\r\n\t\t\t{\r\n\t\t\t\taxis = /*NamedAxis::*/ Y;\r\n\t\t\t} \r\n\t\t\telse if((*xmlMouseBinder->Attribute(\"axis\")) == 'Z')\r\n\t\t\t{\r\n\t\t\t\taxis = /*NamedAxis::*/ Z;\r\n\t\t\t} \r\n\r\n\t\t\taddMouseAxisBinding(mControls.back(), axis, dir);\r\n\r\n\t\t\txmlMouseBinder = xmlMouseBinder->NextSiblingElement(\"MouseBinder\");\r\n\t\t}\r\n\t}\r\n\r\n\tvoid InputControlSystem::loadMouseButtonBinders(TiXmlElement* xmlControlNode)\r\n\t{\r\n\t\tTiXmlElement* xmlMouseButtonBinder = xmlControlNode->FirstChildElement(\"MouseButtonBinder\");    \r\n\t\twhile(xmlMouseButtonBinder)\r\n\t\t{\r\n\t\t\tControl::ControlChangingDirection dir = Control::STOP;\r\n\t\t\tif(std::string(xmlMouseButtonBinder->Attribute(\"direction\")) == \"INCREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::INCREASE;\r\n\t\t\t}\r\n\t\t\telse if(std::string(xmlMouseButtonBinder->Attribute(\"direction\")) == \"DECREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::DECREASE;\r\n\t\t\t}\r\n\r\n\t\t\tint button = 0;\r\n\t\t\tif(std::string(xmlMouseButtonBinder->Attribute(\"button\")) == \"LEFT\")\r\n\t\t\t{\r\n\t\t\t\tbutton = SDL_BUTTON_LEFT;\r\n\t\t\t} \r\n\t\t\telse if(std::string(xmlMouseButtonBinder->Attribute(\"button\")) == \"RIGHT\")\r\n\t\t\t{\r\n\t\t\t\tbutton = SDL_BUTTON_RIGHT;\r\n\t\t\t} \r\n\t\t\telse if(std::string(xmlMouseButtonBinder->Attribute(\"button\")) == \"MIDDLE\")\r\n\t\t\t{\r\n\t\t\t\tbutton = SDL_BUTTON_MIDDLE;\r\n\t\t\t} \r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tbutton = FromString<int>(xmlMouseButtonBinder->Attribute(\"button\"));\r\n\t\t\t}\r\n\r\n\t\t\taddMouseButtonBinding(mControls.back(), button, dir);\r\n\r\n\t\t\txmlMouseButtonBinder = xmlMouseButtonBinder->NextSiblingElement(\"MouseButtonBinder\");\r\n\t\t}\r\n\t}\r\n\r\n\r\n\t// add bindings\r\n\tvoid InputControlSystem::addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tif(axis == /*NamedAxis::*/X)\r\n\t\t{\r\n\t\t\tmXmouseAxisBinded = true;\r\n\t\t}\r\n\t\telse if(axis == /*NamedAxis::*/Y)\r\n\t\t{\r\n\t\t\tmYmouseAxisBinded = true;\r\n\t\t}\t\t\r\n\r\n\t\taddMouseAxisBinding_(control, axis, direction);\r\n\t}\r\n\r\n\t/*protected*/ void InputControlSystem::addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tICS_LOG(\"\\tAdding AxisBinder [axis=\"\r\n\t\t\t+ ToString<int>(axis) + \", direction=\"\r\n\t\t\t+ ToString<int>(direction) + \"]\");\r\n\r\n\t\tControlAxisBinderItem controlAxisBinderItem;\r\n\t\tcontrolAxisBinderItem.control = control;\r\n\t\tcontrolAxisBinderItem.direction = direction;\r\n\t\tmControlsMouseAxisBinderMap[ axis ] = controlAxisBinderItem; \r\n\t}\r\n\r\n\tvoid InputControlSystem::addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tICS_LOG(\"\\tAdding MouseButtonBinder [button=\"\r\n\t\t\t+ ToString<int>(button) + \", direction=\"\r\n\t\t\t+ ToString<int>(direction) + \"]\");\r\n\r\n\t\tControlButtonBinderItem controlMouseButtonBinderItem;\r\n\t\tcontrolMouseButtonBinderItem.direction = direction;\r\n\t\tcontrolMouseButtonBinderItem.control = control;\r\n\t\tmControlsMouseButtonBinderMap[ button ] = controlMouseButtonBinderItem;\r\n\t}\r\n\r\n\tbool InputControlSystem::isMouseButtonBound(unsigned int button) const\r\n\t{\r\n\t\treturn mControlsMouseButtonBinderMap.find(button) != mControlsMouseButtonBinderMap.end();\r\n\t}\r\n\r\n\t// get bindings\r\n\tInputControlSystem::NamedAxis InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin();\r\n\t\twhile(it != mControlsMouseAxisBinderMap.end())\r\n\t\t{\r\n\t\t\tif(it->first < 0 && it->second.control == control && it->second.direction == direction)\r\n\t\t\t{\r\n\t\t\t\treturn (InputControlSystem::NamedAxis)(it->first);\r\n\t\t\t}\r\n\t\t\t++it;\r\n\t\t}\r\n\r\n\t\treturn /*NamedAxis::*/UNASSIGNED;\r\n\t}\r\n\r\n\t//int InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction)\r\n\t//{\r\n\t//\tControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin();\r\n\t//\twhile(it != mControlsMouseAxisBinderMap.end())\r\n\t//\t{\r\n\t//\t\tif(it->first >= 0 && it->second.control == control && it->second.direction == direction)\r\n\t//\t\t{\r\n\t//\t\t\treturn it->first;\r\n\t//\t\t}\r\n\t//\t\tit++;\r\n\t//\t}\r\n\r\n\t//\treturn /*NamedAxis::*/UNASSIGNED;\r\n\t//}\r\n\r\n\tunsigned int InputControlSystem::getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.begin();\r\n\t\twhile(it != mControlsMouseButtonBinderMap.end())\r\n\t\t{\r\n\t\t\tif(it->second.control == control && it->second.direction == direction)\r\n\t\t\t{\r\n\t\t\t\treturn it->first;\r\n\t\t\t}\r\n\t\t\t++it;\r\n\t\t}\r\n\r\n\t\treturn ICS_MAX_DEVICE_BUTTONS;\r\n\t}\r\n\r\n\t// remove bindings\r\n\tvoid InputControlSystem::removeMouseAxisBinding(NamedAxis axis)\r\n\t{\r\n\t\tif(axis == /*NamedAxis::*/X)\r\n\t\t{\r\n\t\t\tmXmouseAxisBinded = false;\r\n\t\t}\r\n\t\telse if(axis == /*NamedAxis::*/Y)\r\n\t\t{\r\n\t\t\tmYmouseAxisBinded = false;\r\n\t\t}\t\t\r\n\r\n\t\tremoveMouseAxisBinding_(axis);\r\n\t}\r\n\t/*protected*/ void InputControlSystem::removeMouseAxisBinding_(int axis)\r\n\t{\r\n\t\tControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.find(axis);\r\n\t\tif(it != mControlsMouseAxisBinderMap.end())\r\n\t\t{\r\n\t\t\tmControlsMouseAxisBinderMap.erase(it);\r\n\t\t}\r\n\t}\t\t\r\n\r\n\r\n\tvoid InputControlSystem::removeMouseButtonBinding(unsigned int button)\r\n\t{\r\n\t\tControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.find(button);\r\n\t\tif(it != mControlsMouseButtonBinderMap.end())\r\n\t\t{\r\n\t\t\tmControlsMouseButtonBinderMap.erase(it);\r\n\t\t}\r\n\t}\r\n\r\n\t// mouse Listeners\r\n    void InputControlSystem::mouseMoved(const SDL_MouseMotionEvent& evt)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\r\n            if(!mDetectingBindingControl)\r\n\t\t\t{\r\n\t\t\t\tif(mXmouseAxisBinded && evt.xrel)\r\n\t\t\t\t{\r\n\t\t\t\t\tControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/X ];\r\n\t\t\t\t\tControl* ctrl = mouseBinderItem.control;\r\n\t\t\t\t\tctrl->setIgnoreAutoReverse(true);\r\n\t\t\t\t\tif(mouseBinderItem.direction == Control::INCREASE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tctrl->setValue( float( (evt.x) / float(mClientWidth) ) );\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if(mouseBinderItem.direction == Control::DECREASE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tctrl->setValue( 1 - float( evt.x / float(mClientWidth) ) );\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(mYmouseAxisBinded && evt.yrel)\r\n\t\t\t\t{\r\n\t\t\t\t\tControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/Y ];\r\n\t\t\t\t\tControl* ctrl = mouseBinderItem.control;\r\n\t\t\t\t\tctrl->setIgnoreAutoReverse(true);\r\n\t\t\t\t\tif(mouseBinderItem.direction == Control::INCREASE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tctrl->setValue( float( (evt.y) / float(mClientHeight) ) );\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if(mouseBinderItem.direction == Control::DECREASE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tctrl->setValue( 1 - float( evt.y / float(mClientHeight) ) );\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//! @todo Whats the range of the Z axis?\r\n\t\t\t\t/*if(evt.state.Z.rel)\r\n\t\t\t\t{\r\n\t\t\t\t\tControlAxisBinderItem mouseBinderItem = mControlsAxisBinderMap[ NamedAxis::Z ];\r\n\t\t\t\t\tControl* ctrl = mouseBinderItem.control;\r\n\t\t\t\t\tctrl->setIgnoreAutoReverse(true);\r\n\t\t\t\t\tif(mouseBinderItem.direction == Control::INCREASE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tctrl->setValue( float( (evt.state.Z.abs) / float(evt.state.¿width?) ) );\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if(mouseBinderItem.direction == Control::DECREASE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tctrl->setValue( float( (1 - evt.state.Z.abs) / float(evt.state.¿width?) ) );\r\n\t\t\t\t\t}\r\n\t\t\t\t}*/\r\n\t\t\t}\r\n\t\t\telse if(mDetectingBindingListener)\r\n\t\t\t{\r\n\t\t\t\tif(mDetectingBindingControl->isAxisBindable())\r\n\t\t\t\t{\r\n\t\t\t\t\tif(mMouseAxisBindingInitialValues[0] == ICS_MOUSE_AXIS_BINDING_NULL_VALUE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmMouseAxisBindingInitialValues[0] = 0;\r\n\t\t\t\t\t\tmMouseAxisBindingInitialValues[1] = 0;\r\n\t\t\t\t\t\tmMouseAxisBindingInitialValues[2] = 0;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tmMouseAxisBindingInitialValues[0] += evt.xrel;\r\n\t\t\t\t\tmMouseAxisBindingInitialValues[1] += evt.yrel;\r\n                    // mMouseAxisBindingInitialValues[2] += evt.zrel;\r\n\r\n\t\t\t\t\tif( abs(mMouseAxisBindingInitialValues[0]) > ICS_MOUSE_BINDING_MARGIN )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmDetectingBindingListener->mouseAxisBindingDetected(this,\r\n\t\t\t\t\t\t\tmDetectingBindingControl, X, mDetectingBindingDirection);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if( abs(mMouseAxisBindingInitialValues[1]) > ICS_MOUSE_BINDING_MARGIN )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmDetectingBindingListener->mouseAxisBindingDetected(this,\r\n\t\t\t\t\t\t\tmDetectingBindingControl, Y, mDetectingBindingDirection);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if( abs(mMouseAxisBindingInitialValues[2]) > ICS_MOUSE_BINDING_MARGIN )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmDetectingBindingListener->mouseAxisBindingDetected(this,\r\n\t\t\t\t\t\t\tmDetectingBindingControl, Z, mDetectingBindingDirection);\t\t\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n    void InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn)\r\n\t{\r\n\t\tif(mActive)\r\n\t\t{\r\n\t\t\tif(!mDetectingBindingControl)\r\n\t\t\t{\r\n\t\t\t\tControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn);\r\n\t\t\t\tif(it != mControlsMouseButtonBinderMap.end())\r\n\t\t\t\t{\r\n\t\t\t\t\tit->second.control->setIgnoreAutoReverse(false);\r\n\t\t\t\t\tif(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tit->second.control->setChangingDirection(it->second.direction);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{                   \r\n\t\t\t\t\t\tif(it->second.control->getValue() == 1)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tit->second.control->setChangingDirection(Control::DECREASE);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if(it->second.control->getValue() == 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tit->second.control->setChangingDirection(Control::INCREASE);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n    void InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn)\r\n\t{\t\t\r\n\t\tif(mActive)\r\n\t\t{\r\n            if (!mDetectingBindingControl)\r\n            {\r\n                ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn);\r\n                if(it != mControlsMouseButtonBinderMap.end())\r\n                {\r\n                    it->second.control->setChangingDirection(Control::STOP);\r\n                }\r\n            }\r\n            else if(mDetectingBindingListener)\r\n            {\r\n                mDetectingBindingListener->mouseButtonBindingDetected(this,\r\n                    mDetectingBindingControl, btn, mDetectingBindingDirection);\r\n            }\r\n\t\t}\r\n\t}\r\n\r\n\t// mouse auto bindings\r\n\tvoid DetectingBindingListener::mouseAxisBindingDetected(InputControlSystem* ICS, Control* control\r\n\t\t\t, InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\t// if the mouse axis is used by another control, remove it\r\n\t\tICS->removeMouseAxisBinding(axis);\r\n\r\n\t\t// if the control has an axis assigned, remove it\r\n\t\tInputControlSystem::NamedAxis oldAxis = ICS->getMouseAxisBinding(control, direction);\r\n\t\tif(oldAxis != InputControlSystem::UNASSIGNED) \r\n\t\t{\r\n\t\t\tICS->removeMouseAxisBinding(oldAxis);\r\n\t\t}\r\n\r\n\t\tICS->addMouseAxisBinding(control, axis, direction);\r\n\t\tICS->cancelDetectingBindingState();\r\n\t}\r\n\r\n\tvoid DetectingBindingListener::mouseButtonBindingDetected(InputControlSystem* ICS, Control* control\r\n\t\t\t, unsigned int button, Control::ControlChangingDirection direction)\r\n\t{\r\n\t\t// if the mouse button is used by another control, remove it\r\n\t\tICS->removeMouseButtonBinding(button);\r\n\r\n\t\t// if the control has a mouse button assigned, remove it\r\n\t\tunsigned int oldButton = ICS->getMouseButtonBinding(control, direction);\r\n\t\tif(oldButton != ICS_MAX_DEVICE_BUTTONS)\r\n\t\t{\r\n\t\t\tICS->removeMouseButtonBinding(oldButton);\r\n\t\t}\r\n\r\n\t\tICS->addMouseButtonBinding(control, button, direction);\r\n\t\tICS->cancelDetectingBindingState();\r\n\t}\r\n\r\n\t/* ----------------------------------------------------------------------------------------\r\n\t* OPENMW CODE STARTS HERE\r\n\t* Mouse Wheel support added by Michael Stopa (Stomy) */\r\n\r\n\tvoid InputControlSystem::loadMouseWheelBinders(TiXmlElement* xmlControlNode)\r\n\t{\r\n\t\tTiXmlElement* xmlMouseWheelBinder = xmlControlNode->FirstChildElement(\"MouseWheelBinder\");\r\n\t\twhile (xmlMouseWheelBinder)\r\n\t\t{\r\n\t\t\tControl::ControlChangingDirection dir = Control::STOP;\r\n\t\t\tif (std::string(xmlMouseWheelBinder->Attribute(\"direction\")) == \"INCREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::INCREASE;\r\n\t\t\t}\r\n\t\t\telse if (std::string(xmlMouseWheelBinder->Attribute(\"direction\")) == \"DECREASE\")\r\n\t\t\t{\r\n\t\t\t\tdir = Control::DECREASE;\r\n\t\t\t}\r\n\r\n\t\t\tMouseWheelClick click = MouseWheelClick::UNASSIGNED;\r\n\t\t\tif (std::string(xmlMouseWheelBinder->Attribute(\"button\")) == \"UP\")\r\n\t\t\t{\r\n\t\t\t\tclick = MouseWheelClick::UP;\r\n\t\t\t}\r\n\t\t\telse if (std::string(xmlMouseWheelBinder->Attribute(\"button\")) == \"DOWN\")\r\n\t\t\t{\r\n\t\t\t\tclick = MouseWheelClick::DOWN;\r\n\t\t\t}\r\n\t\t\telse if (std::string(xmlMouseWheelBinder->Attribute(\"button\")) == \"DOWN\")\r\n\t\t\t{\r\n\t\t\t\tclick = MouseWheelClick::RIGHT;\r\n\t\t\t}\r\n\t\t\telse if (std::string(xmlMouseWheelBinder->Attribute(\"button\")) == \"DOWN\")\r\n\t\t\t{\r\n\t\t\t\tclick = MouseWheelClick::LEFT;\r\n\t\t\t}\r\n\r\n\t\t\taddMouseWheelBinding(mControls.back(), click, dir);\r\n\t\t\txmlMouseWheelBinder = xmlMouseWheelBinder->NextSiblingElement(\"MouseWheelBinder\");\r\n\t\t}\r\n\t}\r\n\r\n\tvoid InputControlSystem::addMouseWheelBinding(\r\n\t\t\tControl* control,\r\n\t\t\tMouseWheelClick click,\r\n\t\t\tControl::ControlChangingDirection direction)\r\n\t{\r\n\t\tICS_LOG(\"\\tAdding MouseWheelBinder [button=\"\r\n\t\t\t+ ToString<int>(static_cast<int>(click)) + \", direction=\"\r\n\t\t\t+ ToString<int>(direction) + \"]\");\r\n\r\n\t\tControlButtonBinderItem controlButtonBinderItem;\r\n\t\tcontrolButtonBinderItem.control = control;\r\n\t\tcontrolButtonBinderItem.direction = direction;\r\n\t\tmControlsMouseWheelBinderMap[static_cast<int>(click)] = controlButtonBinderItem;\r\n\t}\r\n\r\n\tvoid InputControlSystem::removeMouseWheelBinding(MouseWheelClick click)\r\n\t{\r\n\t\tControlsAxisBinderMapType::iterator it = mControlsMouseWheelBinderMap.find(static_cast<int>(click));\r\n\t\tif (it != mControlsMouseWheelBinderMap.end())\r\n\t\t{\r\n\t\t\tmControlsMouseWheelBinderMap.erase(it);\r\n\t\t}\r\n\t}\r\n\r\n\tInputControlSystem::MouseWheelClick InputControlSystem::getMouseWheelBinding(\r\n\t\t\tControl* control,\r\n\t\t\tICS::Control::ControlChangingDirection direction)\r\n\t{\r\n\t\tControlsAxisBinderMapType::iterator it = mControlsMouseWheelBinderMap.begin();\r\n\t\twhile (it != mControlsMouseWheelBinderMap.end())\r\n\t\t{\r\n\t\t\tif (it->first > 0 && it->second.control == control && it->second.direction == direction)\r\n\t\t\t{\r\n\t\t\t\treturn (MouseWheelClick)(it->first);\r\n\t\t\t}\r\n\t\t\t++it;\r\n\t\t}\r\n\r\n\t\treturn MouseWheelClick::UNASSIGNED;\r\n\t}\r\n\r\n\tbool InputControlSystem::isMouseWheelBound(MouseWheelClick button) const\r\n\t{\r\n\t\treturn mControlsMouseWheelBinderMap.find(static_cast<int>(button)) != mControlsMouseWheelBinderMap.end();\r\n\t}\r\n\r\n\tvoid InputControlSystem::mouseWheelMoved(const SDL_MouseWheelEvent &evt)\r\n\t{\r\n\t\tif (mActive)\r\n\t\t{\r\n\t\t\tMouseWheelClick click = MouseWheelClick::UNASSIGNED;\r\n\t\t\tint value;\r\n\t\t\tif (evt.y != 0)\r\n\t\t\t{\r\n\t\t\t\tvalue = evt.y;\r\n\t\t\t\tif (evt.direction == SDL_MOUSEWHEEL_FLIPPED)\r\n\t\t\t\t\tvalue *= -1;\r\n\t\t\t\tif (value > 0)\r\n\t\t\t\t\tclick = MouseWheelClick::UP;\r\n\t\t\t\telse\r\n\t\t\t\t\tclick = MouseWheelClick::DOWN;\r\n\t\t\t}\r\n\t\t\telse if (evt.x != 0)\r\n\t\t\t{\r\n\t\t\t\tvalue = evt.x;\r\n\t\t\t\tif (evt.direction == SDL_MOUSEWHEEL_FLIPPED)\r\n\t\t\t\t\tvalue *= -1;\r\n\t\t\t\tif (value > 0)\r\n\t\t\t\t\tclick = MouseWheelClick::RIGHT;\r\n\t\t\t\telse\r\n\t\t\t\t\tclick = MouseWheelClick::LEFT;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\treturn;\r\n\r\n\t\t\tif(!mDetectingBindingControl)\r\n\t\t\t{\r\n\t\t\t\t// This assumes a single event is sent for every single mouse wheel direction, if they are\r\n\t\t\t\t// buffered up then all bindings will have to be resolved on every invocation of this function\r\n\r\n\t\t\t\tControlButtonBinderItem wheelBinderItem = mControlsMouseWheelBinderMap[static_cast<int>(click)];\r\n\t\t\t\tControl* control = wheelBinderItem.control;\r\n\t\t\t\tif (control)\r\n\t\t\t\t{\r\n\t\t\t\t\tcontrol->setIgnoreAutoReverse(false);\r\n\t\t\t\t\tif (wheelBinderItem.direction == Control::INCREASE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcontrol->setValue(static_cast<float>(std::abs(value)));\r\n\t\t\t\t\t\tcontrol->setChangingDirection(Control::STOP);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (wheelBinderItem.direction == Control::DECREASE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcontrol->setValue(static_cast<float>(std::abs(value)));\r\n\t\t\t\t\t\tcontrol->setChangingDirection(Control::STOP);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if(mDetectingBindingListener)\r\n\t\t\t{\r\n\t\t\t\tmDetectingBindingListener->mouseWheelBindingDetected(this,\r\n\t\t\t\t\tmDetectingBindingControl, click, mDetectingBindingDirection);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid DetectingBindingListener::mouseWheelBindingDetected(\r\n\t\t\tInputControlSystem* ICS,\r\n\t\t\tControl* control,\r\n\t\t\tInputControlSystem::MouseWheelClick click,\r\n\t\t\tControl::ControlChangingDirection direction)\r\n\t{\r\n\t\tICS->removeMouseWheelBinding(click);\r\n\r\n\t\tInputControlSystem::MouseWheelClick oldClick = ICS->getMouseWheelBinding(control, direction);\r\n\t\tif (oldClick != InputControlSystem::MouseWheelClick::UNASSIGNED)\r\n\t\t{\r\n\t\t\tICS->removeMouseWheelBinding(oldClick);\r\n\t\t}\r\n\r\n\t\tICS->addMouseWheelBinding(control, click, direction);\r\n\t\tICS->cancelDetectingBindingState();\r\n\t}\r\n\r\n\t/* OPENMW CODE ENDS HERE\r\n\t* ------------------------------------------------------------------------------------- */\r\n}\r\n"
  },
  {
    "path": "extern/oics/ICSPrerequisites.cpp",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n#include \"ICSPrerequisites.h\"\r\n"
  },
  {
    "path": "extern/oics/ICSPrerequisites.h",
    "content": "/* -------------------------------------------------------\r\nCopyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)\r\n\r\nPermission is hereby granted, free of charge, to any\r\nperson obtaining a copy of this software and associated\r\ndocumentation files (the \"Software\"), to deal in the\r\nSoftware without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the\r\nSoftware, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice\r\nshall be included in all copies or substantial portions of\r\nthe Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\r\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\r\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\r\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\r\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n------------------------------------------------------- */\r\n\r\n//! @todo add mouse wheel support\r\n\r\n#ifndef _InputControlSystem_Prerequisites_H_\r\n#define _InputControlSystem_Prerequisites_H_\r\n\r\n/// Include external headers\r\n#include <sstream>\r\n#include <fstream>\r\n#include <vector>\r\n#include <map>\r\n#include <list>\r\n#include <limits>\r\n#include <algorithm> /* std::min and std::max for MSVC 2013 */\r\n\r\n#include \"tinyxml.h\"\r\n\r\n#include \"SDL_keyboard.h\"\r\n#include \"SDL_mouse.h\"\r\n#include \"SDL_joystick.h\"\r\n#include \"SDL_events.h\"\r\n\r\n/// Define the dll export qualifier if compiling for Windows\r\n\r\n/*\r\n#ifdef ICS_PLATFORM_WIN32\r\n   #ifdef ICS_LIB\r\n     #define DllExport __declspec (dllexport)\r\n   #else\r\n     #define DllExport __declspec (dllimport)\r\n   #endif\r\n#else\r\n   #define DllExport\r\n#endif\r\n*/\r\n#define DllExport\r\n\r\n// Define some macros\r\n#define ICS_DEPRECATED __declspec(deprecated(\"Deprecated. It will be removed in future versions.\"))\r\n\r\n/// Version defines\r\n#define ICS_VERSION_MAJOR 0\r\n#define ICS_VERSION_MINOR 4\r\n#define ICS_VERSION_PATCH 0\r\n\r\n#define ICS_MAX_DEVICE_BUTTONS 30\r\n\r\nnamespace ICS\r\n{\r\n\ttemplate <typename T>\r\n\tbool StringIsNumber ( const std::string &Text )\r\n\t{\r\n\t\tstd::stringstream ss(Text);\r\n\t\tT result;\r\n        return (ss >> result) ? true : false;\r\n\t}\r\n\r\n\t// from http://www.cplusplus.com/forum/articles/9645/\r\n\ttemplate <typename T>\r\n\tstd::string ToString ( T value )\r\n\t{\r\n\t\tstd::stringstream ss;\r\n\t\tss << value;\r\n\t\treturn ss.str();\r\n\t}\r\n\r\n\t// from http://www.cplusplus.com/forum/articles/9645/\r\n\ttemplate <typename T>\r\n\tT FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a\r\n\t{\t\t\t\t\t\t\t\t\t\t\t//character array as argument\r\n\t\tstd::stringstream ss(Text);\r\n\t\tT result;\r\n        return (ss >> result) ? result : 0;\r\n\t}\r\n\r\n\tclass InputControlSystem;\r\n    class Channel;\r\n    class ChannelListener;\r\n    class Control;\r\n\tclass ControlListener;\r\n\tclass DetectingBindingListener;\r\n\tclass InputControlSystemLog;\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "extern/oics/tinystr.cpp",
    "content": "/*\nwww.sourceforge.net/projects/tinyxml\nOriginal file by Yves Berquin.\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any\ndamages arising from the use of this software.\n\nPermission is granted to anyone to use this software for any\npurpose, including commercial applications, and to alter it and\nredistribute it freely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must\nnot claim that you wrote the original software. If you use this\nsoftware in a product, an acknowledgment in the product documentation\nwould be appreciated but is not required.\n\n2. Altered source versions must be plainly marked as such, and\nmust not be misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source\ndistribution.\n*/\n\n/*\n * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005.\n */\n\n\n#ifndef TIXML_USE_STL\n\n#include \"tinystr.h\"\n\n// Error value for find primitive\nconst TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);\n\n\n// Null rep.\nTiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\\0' } };\n\n\nvoid TiXmlString::reserve (size_type cap)\n{\n\tif (cap > capacity())\n\t{\n\t\tTiXmlString tmp;\n\t\ttmp.init(length(), cap);\n\t\tmemcpy(tmp.start(), data(), length());\n\t\tswap(tmp);\n\t}\n}\n\n\nTiXmlString& TiXmlString::assign(const char* str, size_type len)\n{\n\tsize_type cap = capacity();\n\tif (len > cap || cap > 3*(len + 8))\n\t{\n\t\tTiXmlString tmp;\n\t\ttmp.init(len);\n\t\tmemcpy(tmp.start(), str, len);\n\t\tswap(tmp);\n\t}\n\telse\n\t{\n\t\tmemmove(start(), str, len);\n\t\tset_size(len);\n\t}\n\treturn *this;\n}\n\n\nTiXmlString& TiXmlString::append(const char* str, size_type len)\n{\n\tsize_type newsize = length() + len;\n\tif (newsize > capacity())\n\t{\n\t\treserve (newsize + capacity());\n\t}\n\tmemmove(finish(), str, len);\n\tset_size(newsize);\n\treturn *this;\n}\n\n\nTiXmlString operator + (const TiXmlString & a, const TiXmlString & b)\n{\n\tTiXmlString tmp;\n\ttmp.reserve(a.length() + b.length());\n\ttmp += a;\n\ttmp += b;\n\treturn tmp;\n}\n\nTiXmlString operator + (const TiXmlString & a, const char* b)\n{\n\tTiXmlString tmp;\n\tTiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );\n\ttmp.reserve(a.length() + b_len);\n\ttmp += a;\n\ttmp.append(b, b_len);\n\treturn tmp;\n}\n\nTiXmlString operator + (const char* a, const TiXmlString & b)\n{\n\tTiXmlString tmp;\n\tTiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );\n\ttmp.reserve(a_len + b.length());\n\ttmp.append(a, a_len);\n\ttmp += b;\n\treturn tmp;\n}\n\n\n#endif\t// TIXML_USE_STL\n"
  },
  {
    "path": "extern/oics/tinystr.h",
    "content": "/*\nwww.sourceforge.net/projects/tinyxml\nOriginal file by Yves Berquin.\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any\ndamages arising from the use of this software.\n\nPermission is granted to anyone to use this software for any\npurpose, including commercial applications, and to alter it and\nredistribute it freely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must\nnot claim that you wrote the original software. If you use this\nsoftware in a product, an acknowledgment in the product documentation\nwould be appreciated but is not required.\n\n2. Altered source versions must be plainly marked as such, and\nmust not be misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source\ndistribution.\n*/\n\n/*\n * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.\n *\n * - completely rewritten. compact, clean, and fast implementation.\n * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)\n * - fixed reserve() to work as per specification.\n * - fixed buggy compares operator==(), operator<(), and operator>()\n * - fixed operator+=() to take a const ref argument, following spec.\n * - added \"copy\" constructor with length, and most compare operators.\n * - added swap(), clear(), size(), capacity(), operator+().\n */\n\n#ifndef TIXML_USE_STL\n\n#ifndef TIXML_STRING_INCLUDED\n#define TIXML_STRING_INCLUDED\n\n#include <assert.h>\n#include <string.h>\n\n/*\tThe support for explicit isn't that universal, and it isn't really\n\trequired - it is used to check that the TiXmlString class isn't incorrectly\n\tused. Be nice to old compilers and macro it here:\n*/\n#if defined(_MSC_VER) && (_MSC_VER >= 1200 )\n\t// Microsoft visual studio, version 6 and higher.\n\t#define TIXML_EXPLICIT explicit\n#elif defined(__GNUC__) && (__GNUC__ >= 3 )\n\t// GCC version 3 and higher.s\n\t#define TIXML_EXPLICIT explicit\n#else\n\t#define TIXML_EXPLICIT\n#endif\n\n\n/*\n   TiXmlString is an emulation of a subset of the std::string template.\n   Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.\n   Only the member functions relevant to the TinyXML project have been implemented.\n   The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase\n   a string and there's no more room, we allocate a buffer twice as big as we need.\n*/\nclass TiXmlString\n{\n  public :\n\t// The size type used\n  \ttypedef size_t size_type;\n\n\t// Error value for find primitive\n\tstatic const size_type npos; // = -1;\n\n\n\t// TiXmlString empty constructor\n\tTiXmlString () : rep_(&nullrep_)\n\t{\n\t}\n\n\t// TiXmlString copy constructor\n\tTiXmlString ( const TiXmlString & copy) : rep_(0)\n\t{\n\t\tinit(copy.length());\n\t\tmemcpy(start(), copy.data(), length());\n\t}\n\n\t// TiXmlString constructor, based on a string\n\tTIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)\n\t{\n\t\tinit( static_cast<size_type>( strlen(copy) ));\n\t\tmemcpy(start(), copy, length());\n\t}\n\n\t// TiXmlString constructor, based on a string\n\tTIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)\n\t{\n\t\tinit(len);\n\t\tmemcpy(start(), str, len);\n\t}\n\n\t// TiXmlString destructor\n\t~TiXmlString ()\n\t{\n\t\tquit();\n\t}\n\n\t// = operator\n\tTiXmlString& operator = (const char * copy)\n\t{\n\t\treturn assign( copy, (size_type)strlen(copy));\n\t}\n\n\t// = operator\n\tTiXmlString& operator = (const TiXmlString & copy)\n\t{\n\t\treturn assign(copy.start(), copy.length());\n\t}\n\n\n\t// += operator. Maps to append\n\tTiXmlString& operator += (const char * suffix)\n\t{\n\t\treturn append(suffix, static_cast<size_type>( strlen(suffix) ));\n\t}\n\n\t// += operator. Maps to append\n\tTiXmlString& operator += (char single)\n\t{\n\t\treturn append(&single, 1);\n\t}\n\n\t// += operator. Maps to append\n\tTiXmlString& operator += (const TiXmlString & suffix)\n\t{\n\t\treturn append(suffix.data(), suffix.length());\n\t}\n\n\n\t// Convert a TiXmlString into a null-terminated char *\n\tconst char * c_str () const { return rep_->str; }\n\n\t// Convert a TiXmlString into a char * (need not be null terminated).\n\tconst char * data () const { return rep_->str; }\n\n\t// Return the length of a TiXmlString\n\tsize_type length () const { return rep_->size; }\n\n\t// Alias for length()\n\tsize_type size () const { return rep_->size; }\n\n\t// Checks if a TiXmlString is empty\n\tbool empty () const { return rep_->size == 0; }\n\n\t// Return capacity of string\n\tsize_type capacity () const { return rep_->capacity; }\n\n\n\t// single char extraction\n\tconst char& at (size_type index) const\n\t{\n\t\tassert( index < length() );\n\t\treturn rep_->str[ index ];\n\t}\n\n\t// [] operator\n\tchar& operator [] (size_type index) const\n\t{\n\t\tassert( index < length() );\n\t\treturn rep_->str[ index ];\n\t}\n\n\t// find a char in a string. Return TiXmlString::npos if not found\n\tsize_type find (char lookup) const\n\t{\n\t\treturn find(lookup, 0);\n\t}\n\n\t// find a char in a string from an offset. Return TiXmlString::npos if not found\n\tsize_type find (char tofind, size_type offset) const\n\t{\n\t\tif (offset >= length()) return npos;\n\n\t\tfor (const char* p = c_str() + offset; *p != '\\0'; ++p)\n\t\t{\n\t\t   if (*p == tofind) return static_cast< size_type >( p - c_str() );\n\t\t}\n\t\treturn npos;\n\t}\n\n\tvoid clear ()\n\t{\n\t\t//Lee:\n\t\t//The original was just too strange, though correct:\n\t\t//\tTiXmlString().swap(*this);\n\t\t//Instead use the quit & re-init:\n\t\tquit();\n\t\tinit(0,0);\n\t}\n\n\t/*\tFunction to reserve a big amount of data when we know we'll need it. Be aware that this\n\t\tfunction DOES NOT clear the content of the TiXmlString if any exists.\n\t*/\n\tvoid reserve (size_type cap);\n\n\tTiXmlString& assign (const char* str, size_type len);\n\n\tTiXmlString& append (const char* str, size_type len);\n\n\tvoid swap (TiXmlString& other)\n\t{\n\t\tRep* r = rep_;\n\t\trep_ = other.rep_;\n\t\tother.rep_ = r;\n\t}\n\n  private:\n\n\tvoid init(size_type sz) { init(sz, sz); }\n\tvoid set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\\0'; }\n\tchar* start() const { return rep_->str; }\n\tchar* finish() const { return rep_->str + rep_->size; }\n\n\tstruct Rep\n\t{\n\t\tsize_type size, capacity;\n\t\tchar str[1];\n\t};\n\n\tvoid init(size_type sz, size_type cap)\n\t{\n\t\tif (cap)\n\t\t{\n\t\t\t// Lee: the original form:\n\t\t\t//\trep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));\n\t\t\t// doesn't work in some cases of new being overloaded. Switching\n\t\t\t// to the normal allocation, although use an 'int' for systems\n\t\t\t// that are overly picky about structure alignment.\n\t\t\tconst size_type bytesNeeded = sizeof(Rep) + cap;\n\t\t\tconst size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); \n\t\t\trep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );\n\n\t\t\trep_->str[ rep_->size = sz ] = '\\0';\n\t\t\trep_->capacity = cap;\n\t\t}\n\t\telse\n\t\t{\n\t\t\trep_ = &nullrep_;\n\t\t}\n\t}\n\n\tvoid quit()\n\t{\n\t\tif (rep_ != &nullrep_)\n\t\t{\n\t\t\t// The rep_ is really an array of ints. (see the allocator, above).\n\t\t\t// Cast it back before delete, so the compiler won't incorrectly call destructors.\n\t\t\tdelete [] ( reinterpret_cast<int*>( rep_ ) );\n\t\t}\n\t}\n\n\tRep * rep_;\n\tstatic Rep nullrep_;\n\n} ;\n\n\ninline bool operator == (const TiXmlString & a, const TiXmlString & b)\n{\n\treturn\t( a.length() == b.length() )\t\t\t\t// optimization on some platforms\n\t\t   && ( strcmp(a.c_str(), b.c_str()) == 0 );\t// actual compare\n}\ninline bool operator < (const TiXmlString & a, const TiXmlString & b)\n{\n\treturn strcmp(a.c_str(), b.c_str()) < 0;\n}\n\ninline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }\ninline bool operator >  (const TiXmlString & a, const TiXmlString & b) { return b < a; }\ninline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }\ninline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }\n\ninline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }\ninline bool operator == (const char* a, const TiXmlString & b) { return b == a; }\ninline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }\ninline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }\n\nTiXmlString operator + (const TiXmlString & a, const TiXmlString & b);\nTiXmlString operator + (const TiXmlString & a, const char* b);\nTiXmlString operator + (const char* a, const TiXmlString & b);\n\n\n/*\n   TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.\n   Only the operators that we need for TinyXML have been developped.\n*/\nclass TiXmlOutStream : public TiXmlString\n{\npublic :\n\n\t// TiXmlOutStream << operator.\n\tTiXmlOutStream & operator << (const TiXmlString & in)\n\t{\n\t\t*this += in;\n\t\treturn *this;\n\t}\n\n\t// TiXmlOutStream << operator.\n\tTiXmlOutStream & operator << (const char * in)\n\t{\n\t\t*this += in;\n\t\treturn *this;\n\t}\n\n} ;\n\n#endif\t// TIXML_STRING_INCLUDED\n#endif\t// TIXML_USE_STL\n"
  },
  {
    "path": "extern/oics/tinyxml.cpp",
    "content": "/*\nwww.sourceforge.net/projects/tinyxml\nOriginal code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any\ndamages arising from the use of this software.\n\nPermission is granted to anyone to use this software for any\npurpose, including commercial applications, and to alter it and\nredistribute it freely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must\nnot claim that you wrote the original software. If you use this\nsoftware in a product, an acknowledgment in the product documentation\nwould be appreciated but is not required.\n\n2. Altered source versions must be plainly marked as such, and\nmust not be misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source\ndistribution.\n*/\n\n#include <ctype.h>\n\n#ifdef TIXML_USE_STL\n#include <sstream>\n#include <iostream>\n#endif\n\n#include \"tinyxml.h\"\n\n#ifdef _WIN32\n#include <windows.h> // import MultiByteToWideChar\n#endif\n\n\nbool TiXmlBase::condenseWhiteSpace = true;\n\n// Microsoft compiler security\nFILE* TiXmlFOpen( const char* filename, const char* mode )\n{\n\t#if defined(_WIN32)\n\t\tFILE* fp = 0;\n\t\tsize_t len = strlen(filename);\n\t\twchar_t *wname = new wchar_t[len + 1];\n\t\tmemset(wname, 0, sizeof(*wname) * (len + 1));\n\t\twchar_t wmode[32] = { 0 };\n\n\t\tMultiByteToWideChar(CP_UTF8, 0, filename, static_cast<int>(len), wname, static_cast<int>(len));\n\t\tMultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, sizeof(wmode) / sizeof(*wmode));\n\n\t\t#if defined(_MSC_VER) && (_MSC_VER >= 1400 )\n\t\terrno_t err = _wfopen_s( &fp, wname, wmode );\n\t\t#else\n\t\tfp = _wfopen(wname, wmode);\n\t\t#endif\n\t\tdelete[] wname;\n\n\t\treturn fp;\n\t#else\n\t\treturn fopen( filename, mode );\n\t#endif\n}\n\nvoid TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString )\n{\n\tint i=0;\n\n\twhile( i<(int)str.length() )\n\t{\n\t\tunsigned char c = (unsigned char) str[i];\n\n\t\tif (\tc == '&' \n\t\t\t && i < ( (int)str.length() - 2 )\n\t\t\t && str[i+1] == '#'\n\t\t\t && str[i+2] == 'x' )\n\t\t{\n\t\t\t// Hexadecimal character reference.\n\t\t\t// Pass through unchanged.\n\t\t\t// &#xA9;\t-- copyright symbol, for example.\n\t\t\t//\n\t\t\t// The -1 is a bug fix from Rob Laveaux. It keeps\n\t\t\t// an overflow from happening if there is no ';'.\n\t\t\t// There are actually 2 ways to exit this loop -\n\t\t\t// while fails (error case) and break (semicolon found).\n\t\t\t// However, there is no mechanism (currently) for\n\t\t\t// this function to return an error.\n\t\t\twhile ( i<(int)str.length()-1 )\n\t\t\t{\n\t\t\t\toutString->append( str.c_str() + i, 1 );\n\t\t\t\t++i;\n\t\t\t\tif ( str[i] == ';' )\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse if ( c == '&' )\n\t\t{\n\t\t\toutString->append( entity[0].str, entity[0].strLength );\n\t\t\t++i;\n\t\t}\n\t\telse if ( c == '<' )\n\t\t{\n\t\t\toutString->append( entity[1].str, entity[1].strLength );\n\t\t\t++i;\n\t\t}\n\t\telse if ( c == '>' )\n\t\t{\n\t\t\toutString->append( entity[2].str, entity[2].strLength );\n\t\t\t++i;\n\t\t}\n\t\telse if ( c == '\\\"' )\n\t\t{\n\t\t\toutString->append( entity[3].str, entity[3].strLength );\n\t\t\t++i;\n\t\t}\n\t\telse if ( c == '\\'' )\n\t\t{\n\t\t\toutString->append( entity[4].str, entity[4].strLength );\n\t\t\t++i;\n\t\t}\n\t\telse if ( c < 32 )\n\t\t{\n\t\t\t// Easy pass at non-alpha/numeric/symbol\n\t\t\t// Below 32 is symbolic.\n\t\t\tchar buf[ 32 ];\n\t\t\t\n\t\t\t#if defined(TIXML_SNPRINTF)\t\t\n\t\t\t\tTIXML_SNPRINTF( buf, sizeof(buf), \"&#x%02X;\", (unsigned) ( c & 0xff ) );\n\t\t\t#else\n\t\t\t\tsprintf( buf, \"&#x%02X;\", (unsigned) ( c & 0xff ) );\n\t\t\t#endif\t\t\n\n\t\t\t//*ME:\twarning C4267: convert 'size_t' to 'int'\n\t\t\t//*ME:\tInt-Cast to make compiler happy ...\n\t\t\toutString->append( buf, (int)strlen( buf ) );\n\t\t\t++i;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//char realc = (char) c;\n\t\t\t//outString->append( &realc, 1 );\n\t\t\t*outString += (char) c;\t// somewhat more efficient function call.\n\t\t\t++i;\n\t\t}\n\t}\n}\n\n\nTiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()\n{\n\tparent = 0;\n\ttype = _type;\n\tfirstChild = 0;\n\tlastChild = 0;\n\tprev = 0;\n\tnext = 0;\n}\n\n\nTiXmlNode::~TiXmlNode()\n{\n\tTiXmlNode* node = firstChild;\n\tTiXmlNode* temp = 0;\n\n\twhile ( node )\n\t{\n\t\ttemp = node;\n\t\tnode = node->next;\n\t\tdelete temp;\n\t}\t\n}\n\n\nvoid TiXmlNode::CopyTo( TiXmlNode* target ) const\n{\n\ttarget->SetValue (value.c_str() );\n\ttarget->userData = userData; \n}\n\n\nvoid TiXmlNode::Clear()\n{\n\tTiXmlNode* node = firstChild;\n\tTiXmlNode* temp = 0;\n\n\twhile ( node )\n\t{\n\t\ttemp = node;\n\t\tnode = node->next;\n\t\tdelete temp;\n\t}\t\n\n\tfirstChild = 0;\n\tlastChild = 0;\n}\n\n\nTiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )\n{\n\tassert( node->parent == 0 || node->parent == this );\n\tassert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );\n\n\tif ( node->Type() == TiXmlNode::DOCUMENT )\n\t{\n\t\tdelete node;\n\t\tif ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn 0;\n\t}\n\n\tnode->parent = this;\n\n\tnode->prev = lastChild;\n\tnode->next = 0;\n\n\tif ( lastChild )\n\t\tlastChild->next = node;\n\telse\n\t\tfirstChild = node;\t\t\t// it was an empty list.\n\n\tlastChild = node;\n\treturn node;\n}\n\n\nTiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )\n{\n\tif ( addThis.Type() == TiXmlNode::DOCUMENT )\n\t{\n\t\tif ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn 0;\n\t}\n\tTiXmlNode* node = addThis.Clone();\n\tif ( !node )\n\t\treturn 0;\n\n\treturn LinkEndChild( node );\n}\n\n\nTiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )\n{\t\n\tif ( !beforeThis || beforeThis->parent != this ) {\n\t\treturn 0;\n\t}\n\tif ( addThis.Type() == TiXmlNode::DOCUMENT )\n\t{\n\t\tif ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn 0;\n\t}\n\n\tTiXmlNode* node = addThis.Clone();\n\tif ( !node )\n\t\treturn 0;\n\tnode->parent = this;\n\n\tnode->next = beforeThis;\n\tnode->prev = beforeThis->prev;\n\tif ( beforeThis->prev )\n\t{\n\t\tbeforeThis->prev->next = node;\n\t}\n\telse\n\t{\n\t\tassert( firstChild == beforeThis );\n\t\tfirstChild = node;\n\t}\n\tbeforeThis->prev = node;\n\treturn node;\n}\n\n\nTiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )\n{\n\tif ( !afterThis || afterThis->parent != this ) {\n\t\treturn 0;\n\t}\n\tif ( addThis.Type() == TiXmlNode::DOCUMENT )\n\t{\n\t\tif ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn 0;\n\t}\n\n\tTiXmlNode* node = addThis.Clone();\n\tif ( !node )\n\t\treturn 0;\n\tnode->parent = this;\n\n\tnode->prev = afterThis;\n\tnode->next = afterThis->next;\n\tif ( afterThis->next )\n\t{\n\t\tafterThis->next->prev = node;\n\t}\n\telse\n\t{\n\t\tassert( lastChild == afterThis );\n\t\tlastChild = node;\n\t}\n\tafterThis->next = node;\n\treturn node;\n}\n\n\nTiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )\n{\n\tif ( replaceThis->parent != this )\n\t\treturn 0;\n\n\tTiXmlNode* node = withThis.Clone();\n\tif ( !node )\n\t\treturn 0;\n\n\tnode->next = replaceThis->next;\n\tnode->prev = replaceThis->prev;\n\n\tif ( replaceThis->next )\n\t\treplaceThis->next->prev = node;\n\telse\n\t\tlastChild = node;\n\n\tif ( replaceThis->prev )\n\t\treplaceThis->prev->next = node;\n\telse\n\t\tfirstChild = node;\n\n\tdelete replaceThis;\n\tnode->parent = this;\n\treturn node;\n}\n\n\nbool TiXmlNode::RemoveChild( TiXmlNode* removeThis )\n{\n\tif ( removeThis->parent != this )\n\t{\t\n\t\tassert( 0 );\n\t\treturn false;\n\t}\n\n\tif ( removeThis->next )\n\t\tremoveThis->next->prev = removeThis->prev;\n\telse\n\t\tlastChild = removeThis->prev;\n\n\tif ( removeThis->prev )\n\t\tremoveThis->prev->next = removeThis->next;\n\telse\n\t\tfirstChild = removeThis->next;\n\n\tdelete removeThis;\n\treturn true;\n}\n\nconst TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const\n{\n\tconst TiXmlNode* node;\n\tfor ( node = firstChild; node; node = node->next )\n\t{\n\t\tif ( strcmp( node->Value(), _value ) == 0 )\n\t\t\treturn node;\n\t}\n\treturn 0;\n}\n\n\nconst TiXmlNode* TiXmlNode::LastChild( const char * _value ) const\n{\n\tconst TiXmlNode* node;\n\tfor ( node = lastChild; node; node = node->prev )\n\t{\n\t\tif ( strcmp( node->Value(), _value ) == 0 )\n\t\t\treturn node;\n\t}\n\treturn 0;\n}\n\n\nconst TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const\n{\n\tif ( !previous )\n\t{\n\t\treturn FirstChild();\n\t}\n\telse\n\t{\n\t\tassert( previous->parent == this );\n\t\treturn previous->NextSibling();\n\t}\n}\n\n\nconst TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const\n{\n\tif ( !previous )\n\t{\n\t\treturn FirstChild( val );\n\t}\n\telse\n\t{\n\t\tassert( previous->parent == this );\n\t\treturn previous->NextSibling( val );\n\t}\n}\n\n\nconst TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const \n{\n\tconst TiXmlNode* node;\n\tfor ( node = next; node; node = node->next )\n\t{\n\t\tif ( strcmp( node->Value(), _value ) == 0 )\n\t\t\treturn node;\n\t}\n\treturn 0;\n}\n\n\nconst TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const\n{\n\tconst TiXmlNode* node;\n\tfor ( node = prev; node; node = node->prev )\n\t{\n\t\tif ( strcmp( node->Value(), _value ) == 0 )\n\t\t\treturn node;\n\t}\n\treturn 0;\n}\n\n\nvoid TiXmlElement::RemoveAttribute( const char * name )\n{\n\t#ifdef TIXML_USE_STL\n\tTIXML_STRING str( name );\n\tTiXmlAttribute* node = attributeSet.Find( str );\n\t#else\n\tTiXmlAttribute* node = attributeSet.Find( name );\n\t#endif\n\tif ( node )\n\t{\n\t\tattributeSet.Remove( node );\n\t\tdelete node;\n\t}\n}\n\nconst TiXmlElement* TiXmlNode::FirstChildElement() const\n{\n\tconst TiXmlNode* node;\n\n\tfor (\tnode = FirstChild();\n\t\t\tnode;\n\t\t\tnode = node->NextSibling() )\n\t{\n\t\tif ( node->ToElement() )\n\t\t\treturn node->ToElement();\n\t}\n\treturn 0;\n}\n\n\nconst TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const\n{\n\tconst TiXmlNode* node;\n\n\tfor (\tnode = FirstChild( _value );\n\t\t\tnode;\n\t\t\tnode = node->NextSibling( _value ) )\n\t{\n\t\tif ( node->ToElement() )\n\t\t\treturn node->ToElement();\n\t}\n\treturn 0;\n}\n\n\nconst TiXmlElement* TiXmlNode::NextSiblingElement() const\n{\n\tconst TiXmlNode* node;\n\n\tfor (\tnode = NextSibling();\n\t\t\tnode;\n\t\t\tnode = node->NextSibling() )\n\t{\n\t\tif ( node->ToElement() )\n\t\t\treturn node->ToElement();\n\t}\n\treturn 0;\n}\n\n\nconst TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const\n{\n\tconst TiXmlNode* node;\n\n\tfor (\tnode = NextSibling( _value );\n\t\t\tnode;\n\t\t\tnode = node->NextSibling( _value ) )\n\t{\n\t\tif ( node->ToElement() )\n\t\t\treturn node->ToElement();\n\t}\n\treturn 0;\n}\n\n\nconst TiXmlDocument* TiXmlNode::GetDocument() const\n{\n\tconst TiXmlNode* node;\n\n\tfor( node = this; node; node = node->parent )\n\t{\n\t\tif ( node->ToDocument() )\n\t\t\treturn node->ToDocument();\n\t}\n\treturn 0;\n}\n\n\nTiXmlElement::TiXmlElement (const char * _value)\n\t: TiXmlNode( TiXmlNode::ELEMENT )\n{\n\tfirstChild = lastChild = 0;\n\tvalue = _value;\n}\n\n\n#ifdef TIXML_USE_STL\nTiXmlElement::TiXmlElement( const std::string& _value ) \n\t: TiXmlNode( TiXmlNode::ELEMENT )\n{\n\tfirstChild = lastChild = 0;\n\tvalue = _value;\n}\n#endif\n\n\nTiXmlElement::TiXmlElement( const TiXmlElement& copy)\n\t: TiXmlNode( TiXmlNode::ELEMENT )\n{\n\tfirstChild = lastChild = 0;\n\tcopy.CopyTo( this );\t\n}\n\n\nvoid TiXmlElement::operator=( const TiXmlElement& base )\n{\n\tClearThis();\n\tbase.CopyTo( this );\n}\n\n\nTiXmlElement::~TiXmlElement()\n{\n\tClearThis();\n}\n\n\nvoid TiXmlElement::ClearThis()\n{\n\tClear();\n\twhile( attributeSet.First() )\n\t{\n\t\tTiXmlAttribute* node = attributeSet.First();\n\t\tattributeSet.Remove( node );\n\t\tdelete node;\n\t}\n}\n\n\nconst char* TiXmlElement::Attribute( const char* name ) const\n{\n\tconst TiXmlAttribute* node = attributeSet.Find( name );\n\tif ( node )\n\t\treturn node->Value();\n\treturn 0;\n}\n\n\n#ifdef TIXML_USE_STL\nconst std::string* TiXmlElement::Attribute( const std::string& name ) const\n{\n\tconst TiXmlAttribute* node = attributeSet.Find( name );\n\tif ( node )\n\t\treturn &node->ValueStr();\n\treturn 0;\n}\n#endif\n\n\nconst char* TiXmlElement::Attribute( const char* name, int* i ) const\n{\n\tconst char* s = Attribute( name );\n\tif ( i )\n\t{\n\t\tif ( s ) {\n\t\t\t*i = atoi( s );\n\t\t}\n\t\telse {\n\t\t\t*i = 0;\n\t\t}\n\t}\n\treturn s;\n}\n\n\n#ifdef TIXML_USE_STL\nconst std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const\n{\n\tconst std::string* s = Attribute( name );\n\tif ( i )\n\t{\n\t\tif ( s ) {\n\t\t\t*i = atoi( s->c_str() );\n\t\t}\n\t\telse {\n\t\t\t*i = 0;\n\t\t}\n\t}\n\treturn s;\n}\n#endif\n\n\nconst char* TiXmlElement::Attribute( const char* name, double* d ) const\n{\n\tconst char* s = Attribute( name );\n\tif ( d )\n\t{\n\t\tif ( s ) {\n\t\t\t*d = atof( s );\n\t\t}\n\t\telse {\n\t\t\t*d = 0;\n\t\t}\n\t}\n\treturn s;\n}\n\n\n#ifdef TIXML_USE_STL\nconst std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const\n{\n\tconst std::string* s = Attribute( name );\n\tif ( d )\n\t{\n\t\tif ( s ) {\n\t\t\t*d = atof( s->c_str() );\n\t\t}\n\t\telse {\n\t\t\t*d = 0;\n\t\t}\n\t}\n\treturn s;\n}\n#endif\n\n\nint TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const\n{\n\tconst TiXmlAttribute* node = attributeSet.Find( name );\n\tif ( !node )\n\t\treturn TIXML_NO_ATTRIBUTE;\n\treturn node->QueryIntValue( ival );\n}\n\n\n#ifdef TIXML_USE_STL\nint TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const\n{\n\tconst TiXmlAttribute* node = attributeSet.Find( name );\n\tif ( !node )\n\t\treturn TIXML_NO_ATTRIBUTE;\n\treturn node->QueryIntValue( ival );\n}\n#endif\n\n\nint TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const\n{\n\tconst TiXmlAttribute* node = attributeSet.Find( name );\n\tif ( !node )\n\t\treturn TIXML_NO_ATTRIBUTE;\n\treturn node->QueryDoubleValue( dval );\n}\n\n\n#ifdef TIXML_USE_STL\nint TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const\n{\n\tconst TiXmlAttribute* node = attributeSet.Find( name );\n\tif ( !node )\n\t\treturn TIXML_NO_ATTRIBUTE;\n\treturn node->QueryDoubleValue( dval );\n}\n#endif\n\n\nvoid TiXmlElement::SetAttribute( const char * name, int val )\n{\t\n\tchar buf[64];\n\t#if defined(TIXML_SNPRINTF)\t\t\n\t\tTIXML_SNPRINTF( buf, sizeof(buf), \"%d\", val );\n\t#else\n\t\tsprintf( buf, \"%d\", val );\n\t#endif\n\tSetAttribute( name, buf );\n}\n\n\n#ifdef TIXML_USE_STL\nvoid TiXmlElement::SetAttribute( const std::string& name, int val )\n{\t\n   std::ostringstream oss;\n   oss << val;\n   SetAttribute( name, oss.str() );\n}\n#endif\n\n\nvoid TiXmlElement::SetDoubleAttribute( const char * name, double val )\n{\t\n\tchar buf[256];\n\t#if defined(TIXML_SNPRINTF)\t\t\n               TIXML_SNPRINTF( buf, sizeof(buf), \"%f\", val );\n\t#else\n               sprintf( buf, \"%f\", val );\n\t#endif\n\tSetAttribute( name, buf );\n}\n\n\nvoid TiXmlElement::SetAttribute( const char * cname, const char * cvalue )\n{\n\t#ifdef TIXML_USE_STL\n\tTIXML_STRING _name( cname );\n\tTIXML_STRING _value( cvalue );\n\t#else\n\tconst char* _name = cname;\n\tconst char* _value = cvalue;\n\t#endif\n\n\tTiXmlAttribute* node = attributeSet.Find( _name );\n\tif ( node )\n\t{\n\t\tnode->SetValue( _value );\n\t\treturn;\n\t}\n\n\tTiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue );\n\tif ( attrib )\n\t{\n\t\tattributeSet.Add( attrib );\n\t}\n\telse\n\t{\n\t\tTiXmlDocument* document = GetDocument();\n\t\tif ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t}\n}\n\n\n#ifdef TIXML_USE_STL\nvoid TiXmlElement::SetAttribute( const std::string& name, const std::string& _value )\n{\n\tTiXmlAttribute* node = attributeSet.Find( name );\n\tif ( node )\n\t{\n\t\tnode->SetValue( _value );\n\t\treturn;\n\t}\n\n\tTiXmlAttribute* attrib = new TiXmlAttribute( name, _value );\n\tif ( attrib )\n\t{\n\t\tattributeSet.Add( attrib );\n\t}\n\telse\n\t{\n\t\tTiXmlDocument* document = GetDocument();\n\t\tif ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t}\n}\n#endif\n\n\nvoid TiXmlElement::Print( FILE* cfile, int depth ) const\n{\n\tint i;\n\tassert( cfile );\n\tfor ( i=0; i<depth; i++ ) {\n\t\tfprintf( cfile, \"\t\" );\n\t}\n\n\tfprintf( cfile, \"<%s\", value.c_str() );\n\n\tconst TiXmlAttribute* attrib;\n\tfor ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )\n\t{\n\t\tfprintf( cfile, \" \" );\n\t\tattrib->Print( cfile, depth );\n\t}\n\n\t// There are 3 different formatting approaches:\n\t// 1) An element without children is printed as a <foo /> node\n\t// 2) An element with only a text child is printed as <foo> text </foo>\n\t// 3) An element with children is printed on multiple lines.\n\tTiXmlNode* node;\n\tif ( !firstChild )\n\t{\n\t\tfprintf( cfile, \" />\" );\n\t}\n\telse if ( firstChild == lastChild && firstChild->ToText() )\n\t{\n\t\tfprintf( cfile, \">\" );\n\t\tfirstChild->Print( cfile, depth + 1 );\n\t\tfprintf( cfile, \"</%s>\", value.c_str() );\n\t}\n\telse\n\t{\n\t\tfprintf( cfile, \">\" );\n\n\t\tfor ( node = firstChild; node; node=node->NextSibling() )\n\t\t{\n\t\t\tif ( !node->ToText() )\n\t\t\t{\n\t\t\t\tfprintf( cfile, \"\\n\" );\n\t\t\t}\n\t\t\tnode->Print( cfile, depth+1 );\n\t\t}\n\t\tfprintf( cfile, \"\\n\" );\n\t\tfor( i=0; i<depth; ++i ) {\n\t\t\tfprintf( cfile, \"\t\" );\n\t\t}\n\t\tfprintf( cfile, \"</%s>\", value.c_str() );\n\t}\n}\n\n\nvoid TiXmlElement::CopyTo( TiXmlElement* target ) const\n{\n\t// superclass:\n\tTiXmlNode::CopyTo( target );\n\n\t// Element class: \n\t// Clone the attributes, then clone the children.\n\tconst TiXmlAttribute* attribute = 0;\n\tfor(\tattribute = attributeSet.First();\n\tattribute;\n\tattribute = attribute->Next() )\n\t{\n\t\ttarget->SetAttribute( attribute->Name(), attribute->Value() );\n\t}\n\n\tTiXmlNode* node = 0;\n\tfor ( node = firstChild; node; node = node->NextSibling() )\n\t{\n\t\ttarget->LinkEndChild( node->Clone() );\n\t}\n}\n\nbool TiXmlElement::Accept( TiXmlVisitor* visitor ) const\n{\n\tif ( visitor->VisitEnter( *this, attributeSet.First() ) ) \n\t{\n\t\tfor ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )\n\t\t{\n\t\t\tif ( !node->Accept( visitor ) )\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn visitor->VisitExit( *this );\n}\n\n\nTiXmlNode* TiXmlElement::Clone() const\n{\n\tTiXmlElement* clone = new TiXmlElement( Value() );\n\tif ( !clone )\n\t\treturn 0;\n\n\tCopyTo( clone );\n\treturn clone;\n}\n\n\nconst char* TiXmlElement::GetText() const\n{\n\tconst TiXmlNode* child = this->FirstChild();\n\tif ( child ) {\n\t\tconst TiXmlText* childText = child->ToText();\n\t\tif ( childText ) {\n\t\t\treturn childText->Value();\n\t\t}\n\t}\n\treturn 0;\n}\n\n\nTiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT )\n{\n\ttabsize = 4;\n\tuseMicrosoftBOM = false;\n\tClearError();\n}\n\nTiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )\n{\n\ttabsize = 4;\n\tuseMicrosoftBOM = false;\n\tvalue = documentName;\n\tClearError();\n}\n\n\n#ifdef TIXML_USE_STL\nTiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )\n{\n\ttabsize = 4;\n\tuseMicrosoftBOM = false;\n\tvalue = documentName;\n\tClearError();\n}\n#endif\n\n\nTiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT )\n{\n\tcopy.CopyTo( this );\n}\n\n\nvoid TiXmlDocument::operator=( const TiXmlDocument& copy )\n{\n\tClear();\n\tcopy.CopyTo( this );\n}\n\n\nbool TiXmlDocument::LoadFile( TiXmlEncoding encoding )\n{\n\t// See STL_STRING_BUG below.\n\t//StringToBuffer buf( value );\n\n\treturn LoadFile( Value(), encoding );\n}\n\n\nbool TiXmlDocument::SaveFile() const\n{\n\t// See STL_STRING_BUG below.\n//\tStringToBuffer buf( value );\n//\n//\tif ( buf.buffer && SaveFile( buf.buffer ) )\n//\t\treturn true;\n//\n//\treturn false;\n\treturn SaveFile( Value() );\n}\n\nbool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )\n{\n\t// There was a really terrifying little bug here. The code:\n\t//\t\tvalue = filename\n\t// in the STL case, cause the assignment method of the std::string to\n\t// be called. What is strange, is that the std::string had the same\n\t// address as it's c_str() method, and so bad things happen. Looks\n\t// like a bug in the Microsoft STL implementation.\n\t// Add an extra string to avoid the crash.\n\tTIXML_STRING filename( _filename );\n\tvalue = filename;\n\n\t// reading in binary mode so that tinyxml can normalize the EOL\n\tFILE* file = TiXmlFOpen( value.c_str (), \"rb\" );\t\n\n\tif ( file )\n\t{\n\t\tbool result = LoadFile( file, encoding );\n\t\tfclose( file );\n\t\treturn result;\n\t}\n\telse\n\t{\n\t\tSetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn false;\n\t}\n}\n\nbool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )\n{\n\tif ( !file ) \n\t{\n\t\tSetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn false;\n\t}\n\n\t// Delete the existing data:\n\tClear();\n\tlocation.Clear();\n\n\t// Get the file size, so we can pre-allocate the string. HUGE speed impact.\n\tlong length = 0;\n\tfseek( file, 0, SEEK_END );\n\tlength = ftell( file );\n\tfseek( file, 0, SEEK_SET );\n\n\t// Strange case, but good to handle up front.\n\tif ( length <= 0 )\n\t{\n\t\tSetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn false;\n\t}\n\n\t// If we have a file, assume it is all one big XML file, and read it in.\n\t// The document parser may decide the document ends sooner than the entire file, however.\n\tTIXML_STRING data;\n\tdata.reserve( length );\n\n\t// Subtle bug here. TinyXml did use fgets. But from the XML spec:\n\t// 2.11 End-of-Line Handling\n\t// <snip>\n\t// <quote>\n\t// ...the XML processor MUST behave as if it normalized all line breaks in external \n\t// parsed entities (including the document entity) on input, before parsing, by translating \n\t// both the two-character sequence #xD #xA and any #xD that is not followed by #xA to \n\t// a single #xA character.\n\t// </quote>\n\t//\n\t// It is not clear fgets does that, and certainly isn't clear it works cross platform. \n\t// Generally, you expect fgets to translate from the convention of the OS to the c/unix\n\t// convention, and not work generally.\n\n\t/*\n\twhile( fgets( buf, sizeof(buf), file ) )\n\t{\n\t\tdata += buf;\n\t}\n\t*/\n\n\tchar* buf = new char[ length+1 ];\n\tbuf[0] = 0;\n\n\tif ( fread( buf, length, 1, file ) != 1 ) {\n\t\tdelete [] buf;\n\t\tSetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn false;\n\t}\n\n\tconst char* p = buf;\t// the read head\n\tchar* q = buf;\t\t\t// the write head\n\tconst char CR = 0x0d;\n\tconst char LF = 0x0a;\n\n\tbuf[length] = 0;\n\twhile( *p ) {\n\t\tassert( p < (buf+length) );\n\t\tassert( q <= (buf+length) );\n\t\tassert( q <= p );\n\n\t\tif ( *p == CR ) {\n\t\t\t*q++ = LF;\n\t\t\tp++;\n\t\t\tif ( *p == LF ) {\t\t// check for CR+LF (and skip LF)\n\t\t\t\tp++;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t*q++ = *p++;\n\t\t}\n\t}\n\tassert( q <= (buf+length) );\n\t*q = 0;\n\n\tParse( buf, 0, encoding );\n\n\tdelete [] buf;\n\treturn !Error();\n}\n\n\nbool TiXmlDocument::SaveFile( const char * filename ) const\n{\n\t// The old c stuff lives on...\n\tFILE* fp = TiXmlFOpen( filename, \"w\" );\n\tif ( fp )\n\t{\n\t\tbool result = SaveFile( fp );\n\t\tfclose( fp );\n\t\treturn result;\n\t}\n\treturn false;\n}\n\n\nbool TiXmlDocument::SaveFile( FILE* fp ) const\n{\n\tif ( useMicrosoftBOM ) \n\t{\n\t\tconst unsigned char TIXML_UTF_LEAD_0 = 0xefU;\n\t\tconst unsigned char TIXML_UTF_LEAD_1 = 0xbbU;\n\t\tconst unsigned char TIXML_UTF_LEAD_2 = 0xbfU;\n\n\t\tfputc( TIXML_UTF_LEAD_0, fp );\n\t\tfputc( TIXML_UTF_LEAD_1, fp );\n\t\tfputc( TIXML_UTF_LEAD_2, fp );\n\t}\n\tPrint( fp, 0 );\n\treturn (ferror(fp) == 0);\n}\n\n\nvoid TiXmlDocument::CopyTo( TiXmlDocument* target ) const\n{\n\tTiXmlNode::CopyTo( target );\n\n\ttarget->error = error;\n\ttarget->errorId = errorId;\n\ttarget->errorDesc = errorDesc;\n\ttarget->tabsize = tabsize;\n\ttarget->errorLocation = errorLocation;\n\ttarget->useMicrosoftBOM = useMicrosoftBOM;\n\n\tTiXmlNode* node = 0;\n\tfor ( node = firstChild; node; node = node->NextSibling() )\n\t{\n\t\ttarget->LinkEndChild( node->Clone() );\n\t}\t\n}\n\n\nTiXmlNode* TiXmlDocument::Clone() const\n{\n\tTiXmlDocument* clone = new TiXmlDocument();\n\tif ( !clone )\n\t\treturn 0;\n\n\tCopyTo( clone );\n\treturn clone;\n}\n\n\nvoid TiXmlDocument::Print( FILE* cfile, int depth ) const\n{\n\tassert( cfile );\n\tfor ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )\n\t{\n\t\tnode->Print( cfile, depth );\n\t\tfprintf( cfile, \"\\n\" );\n\t}\n}\n\n\nbool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const\n{\n\tif ( visitor->VisitEnter( *this ) )\n\t{\n\t\tfor ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )\n\t\t{\n\t\t\tif ( !node->Accept( visitor ) )\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn visitor->VisitExit( *this );\n}\n\n\nconst TiXmlAttribute* TiXmlAttribute::Next() const\n{\n\t// We are using knowledge of the sentinel. The sentinel\n\t// have a value or name.\n\tif ( next->value.empty() && next->name.empty() )\n\t\treturn 0;\n\treturn next;\n}\n\n/*\nTiXmlAttribute* TiXmlAttribute::Next()\n{\n\t// We are using knowledge of the sentinel. The sentinel\n\t// have a value or name.\n\tif ( next->value.empty() && next->name.empty() )\n\t\treturn 0;\n\treturn next;\n}\n*/\n\nconst TiXmlAttribute* TiXmlAttribute::Previous() const\n{\n\t// We are using knowledge of the sentinel. The sentinel\n\t// have a value or name.\n\tif ( prev->value.empty() && prev->name.empty() )\n\t\treturn 0;\n\treturn prev;\n}\n\n/*\nTiXmlAttribute* TiXmlAttribute::Previous()\n{\n\t// We are using knowledge of the sentinel. The sentinel\n\t// have a value or name.\n\tif ( prev->value.empty() && prev->name.empty() )\n\t\treturn 0;\n\treturn prev;\n}\n*/\n\nvoid TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const\n{\n\tTIXML_STRING n, v;\n\n\tEncodeString( name, &n );\n\tEncodeString( value, &v );\n\n\tif (value.find ('\\\"') == TIXML_STRING::npos) {\n\t\tif ( cfile ) {\n\t\tfprintf (cfile, \"%s=\\\"%s\\\"\", n.c_str(), v.c_str() );\n\t\t}\n\t\tif ( str ) {\n\t\t\t(*str) += n; (*str) += \"=\\\"\"; (*str) += v; (*str) += \"\\\"\";\n\t\t}\n\t}\n\telse {\n\t\tif ( cfile ) {\n\t\tfprintf (cfile, \"%s='%s'\", n.c_str(), v.c_str() );\n\t\t}\n\t\tif ( str ) {\n\t\t\t(*str) += n; (*str) += \"='\"; (*str) += v; (*str) += \"'\";\n\t\t}\n\t}\n}\n\n\nint TiXmlAttribute::QueryIntValue( int* ival ) const\n{\n\tif ( TIXML_SSCANF( value.c_str(), \"%d\", ival ) == 1 )\n\t\treturn TIXML_SUCCESS;\n\treturn TIXML_WRONG_TYPE;\n}\n\nint TiXmlAttribute::QueryDoubleValue( double* dval ) const\n{\n\tif ( TIXML_SSCANF( value.c_str(), \"%lf\", dval ) == 1 )\n\t\treturn TIXML_SUCCESS;\n\treturn TIXML_WRONG_TYPE;\n}\n\nvoid TiXmlAttribute::SetIntValue( int _value )\n{\n\tchar buf [64];\n\t#if defined(TIXML_SNPRINTF)\t\t\n\t\tTIXML_SNPRINTF(buf, sizeof(buf), \"%d\", _value);\n\t#else\n\t\tsprintf (buf, \"%d\", _value);\n\t#endif\n\tSetValue (buf);\n}\n\nvoid TiXmlAttribute::SetDoubleValue( double _value )\n{\n\tchar buf [256];\n\t#if defined(TIXML_SNPRINTF)\t\t\n               TIXML_SNPRINTF( buf, sizeof(buf), \"%f\", _value);\n\t#else\n               sprintf (buf, \"%f\", _value);\n\t#endif\n\tSetValue (buf);\n}\n\nint TiXmlAttribute::IntValue() const\n{\n\treturn atoi (value.c_str ());\n}\n\ndouble  TiXmlAttribute::DoubleValue() const\n{\n\treturn atof (value.c_str ());\n}\n\n\nTiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT )\n{\n\tcopy.CopyTo( this );\n}\n\n\nvoid TiXmlComment::operator=( const TiXmlComment& base )\n{\n\tClear();\n\tbase.CopyTo( this );\n}\n\n\nvoid TiXmlComment::Print( FILE* cfile, int depth ) const\n{\n\tassert( cfile );\n\tfor ( int i=0; i<depth; i++ )\n\t{\n\t\tfprintf( cfile,  \"\t\" );\n\t}\n\tfprintf( cfile, \"<!--%s-->\", value.c_str() );\n}\n\n\nvoid TiXmlComment::CopyTo( TiXmlComment* target ) const\n{\n\tTiXmlNode::CopyTo( target );\n}\n\n\nbool TiXmlComment::Accept( TiXmlVisitor* visitor ) const\n{\n\treturn visitor->Visit( *this );\n}\n\n\nTiXmlNode* TiXmlComment::Clone() const\n{\n\tTiXmlComment* clone = new TiXmlComment();\n\n\tif ( !clone )\n\t\treturn 0;\n\n\tCopyTo( clone );\n\treturn clone;\n}\n\n\nvoid TiXmlText::Print( FILE* cfile, int depth ) const\n{\n\tassert( cfile );\n\tif ( cdata )\n\t{\n\t\tint i;\n\t\tfprintf( cfile, \"\\n\" );\n\t\tfor ( i=0; i<depth; i++ ) {\n\t\t\tfprintf( cfile, \"\t\" );\n\t\t}\n\t\tfprintf( cfile, \"<![CDATA[%s]]>\\n\", value.c_str() );\t// unformatted output\n\t}\n\telse\n\t{\n\t\tTIXML_STRING buffer;\n\t\tEncodeString( value, &buffer );\n\t\tfprintf( cfile, \"%s\", buffer.c_str() );\n\t}\n}\n\n\nvoid TiXmlText::CopyTo( TiXmlText* target ) const\n{\n\tTiXmlNode::CopyTo( target );\n\ttarget->cdata = cdata;\n}\n\n\nbool TiXmlText::Accept( TiXmlVisitor* visitor ) const\n{\n\treturn visitor->Visit( *this );\n}\n\n\nTiXmlNode* TiXmlText::Clone() const\n{\t\n\tTiXmlText* clone = 0;\n\tclone = new TiXmlText( \"\" );\n\n\tif ( !clone )\n\t\treturn 0;\n\n\tCopyTo( clone );\n\treturn clone;\n}\n\n\nTiXmlDeclaration::TiXmlDeclaration( const char * _version,\n\t\t\t\t\t\t\t\t\tconst char * _encoding,\n\t\t\t\t\t\t\t\t\tconst char * _standalone )\n\t: TiXmlNode( TiXmlNode::DECLARATION )\n{\n\tversion = _version;\n\tencoding = _encoding;\n\tstandalone = _standalone;\n}\n\n\n#ifdef TIXML_USE_STL\nTiXmlDeclaration::TiXmlDeclaration(\tconst std::string& _version,\n\t\t\t\t\t\t\t\t\tconst std::string& _encoding,\n\t\t\t\t\t\t\t\t\tconst std::string& _standalone )\n\t: TiXmlNode( TiXmlNode::DECLARATION )\n{\n\tversion = _version;\n\tencoding = _encoding;\n\tstandalone = _standalone;\n}\n#endif\n\n\nTiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )\n\t: TiXmlNode( TiXmlNode::DECLARATION )\n{\n\tcopy.CopyTo( this );\t\n}\n\n\nvoid TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )\n{\n\tClear();\n\tcopy.CopyTo( this );\n}\n\n\nvoid TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const\n{\n\tif ( cfile ) fprintf( cfile, \"<?xml \" );\n\tif ( str )\t (*str) += \"<?xml \";\n\n\tif ( !version.empty() ) {\n\t\tif ( cfile ) fprintf (cfile, \"version=\\\"%s\\\" \", version.c_str ());\n\t\tif ( str ) { (*str) += \"version=\\\"\"; (*str) += version; (*str) += \"\\\" \"; }\n\t}\n\tif ( !encoding.empty() ) {\n\t\tif ( cfile ) fprintf (cfile, \"encoding=\\\"%s\\\" \", encoding.c_str ());\n\t\tif ( str ) { (*str) += \"encoding=\\\"\"; (*str) += encoding; (*str) += \"\\\" \"; }\n\t}\n\tif ( !standalone.empty() ) {\n\t\tif ( cfile ) fprintf (cfile, \"standalone=\\\"%s\\\" \", standalone.c_str ());\n\t\tif ( str ) { (*str) += \"standalone=\\\"\"; (*str) += standalone; (*str) += \"\\\" \"; }\n\t}\n\tif ( cfile ) fprintf( cfile, \"?>\" );\n\tif ( str )\t (*str) += \"?>\";\n}\n\n\nvoid TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const\n{\n\tTiXmlNode::CopyTo( target );\n\n\ttarget->version = version;\n\ttarget->encoding = encoding;\n\ttarget->standalone = standalone;\n}\n\n\nbool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const\n{\n\treturn visitor->Visit( *this );\n}\n\n\nTiXmlNode* TiXmlDeclaration::Clone() const\n{\t\n\tTiXmlDeclaration* clone = new TiXmlDeclaration();\n\n\tif ( !clone )\n\t\treturn 0;\n\n\tCopyTo( clone );\n\treturn clone;\n}\n\n\nvoid TiXmlUnknown::Print( FILE* cfile, int depth ) const\n{\n\tfor ( int i=0; i<depth; i++ )\n\t\tfprintf( cfile, \"\t\" );\n\tfprintf( cfile, \"<%s>\", value.c_str() );\n}\n\n\nvoid TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const\n{\n\tTiXmlNode::CopyTo( target );\n}\n\n\nbool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const\n{\n\treturn visitor->Visit( *this );\n}\n\n\nTiXmlNode* TiXmlUnknown::Clone() const\n{\n\tTiXmlUnknown* clone = new TiXmlUnknown();\n\n\tif ( !clone )\n\t\treturn 0;\n\n\tCopyTo( clone );\n\treturn clone;\n}\n\n\nTiXmlAttributeSet::TiXmlAttributeSet()\n{\n\tsentinel.next = &sentinel;\n\tsentinel.prev = &sentinel;\n}\n\n\nTiXmlAttributeSet::~TiXmlAttributeSet()\n{\n\tassert( sentinel.next == &sentinel );\n\tassert( sentinel.prev == &sentinel );\n}\n\n\nvoid TiXmlAttributeSet::Add( TiXmlAttribute* addMe )\n{\n\t#ifdef TIXML_USE_STL\n\tassert( !Find( TIXML_STRING( addMe->Name() ) ) );\t// Shouldn't be multiply adding to the set.\n\t#else\n\tassert( !Find( addMe->Name() ) );\t// Shouldn't be multiply adding to the set.\n\t#endif\n\n\taddMe->next = &sentinel;\n\taddMe->prev = sentinel.prev;\n\n\tsentinel.prev->next = addMe;\n\tsentinel.prev\t  = addMe;\n}\n\nvoid TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )\n{\n\tTiXmlAttribute* node;\n\n\tfor( node = sentinel.next; node != &sentinel; node = node->next )\n\t{\n\t\tif ( node == removeMe )\n\t\t{\n\t\t\tnode->prev->next = node->next;\n\t\t\tnode->next->prev = node->prev;\n\t\t\tnode->next = 0;\n\t\t\tnode->prev = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\tassert( 0 );\t\t// we tried to remove a non-linked attribute.\n}\n\n\n#ifdef TIXML_USE_STL\nconst TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const\n{\n\tfor( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )\n\t{\n\t\tif ( node->name == name )\n\t\t\treturn node;\n\t}\n\treturn 0;\n}\n\n/*\nTiXmlAttribute*\tTiXmlAttributeSet::Find( const std::string& name )\n{\n\tfor( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )\n\t{\n\t\tif ( node->name == name )\n\t\t\treturn node;\n\t}\n\treturn 0;\n}\n*/\n#endif\n\n\nconst TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const\n{\n\tfor( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )\n\t{\n\t\tif ( strcmp( node->name.c_str(), name ) == 0 )\n\t\t\treturn node;\n\t}\n\treturn 0;\n}\n\n/*\nTiXmlAttribute*\tTiXmlAttributeSet::Find( const char* name )\n{\n\tfor( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )\n\t{\n\t\tif ( strcmp( node->name.c_str(), name ) == 0 )\n\t\t\treturn node;\n\t}\n\treturn 0;\n}\n*/\n\n#ifdef TIXML_USE_STL\t\nstd::istream& operator>> (std::istream & in, TiXmlNode & base)\n{\n\tTIXML_STRING tag;\n\ttag.reserve( 8 * 1000 );\n\tbase.StreamIn( &in, &tag );\n\n\tbase.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );\n\treturn in;\n}\n#endif\n\n\n#ifdef TIXML_USE_STL\t\nstd::ostream& operator<< (std::ostream & out, const TiXmlNode & base)\n{\n\tTiXmlPrinter printer;\n\tprinter.SetStreamPrinting();\n\tbase.Accept( &printer );\n\tout << printer.Str();\n\n\treturn out;\n}\n\n\nstd::string& operator<< (std::string& out, const TiXmlNode& base )\n{\n\tTiXmlPrinter printer;\n\tprinter.SetStreamPrinting();\n\tbase.Accept( &printer );\n\tout.append( printer.Str() );\n\n\treturn out;\n}\n#endif\n\n\nTiXmlHandle TiXmlHandle::FirstChild() const\n{\n\tif ( node )\n\t{\n\t\tTiXmlNode* child = node->FirstChild();\n\t\tif ( child )\n\t\t\treturn TiXmlHandle( child );\n\t}\n\treturn TiXmlHandle( 0 );\n}\n\n\nTiXmlHandle TiXmlHandle::FirstChild( const char * value ) const\n{\n\tif ( node )\n\t{\n\t\tTiXmlNode* child = node->FirstChild( value );\n\t\tif ( child )\n\t\t\treturn TiXmlHandle( child );\n\t}\n\treturn TiXmlHandle( 0 );\n}\n\n\nTiXmlHandle TiXmlHandle::FirstChildElement() const\n{\n\tif ( node )\n\t{\n\t\tTiXmlElement* child = node->FirstChildElement();\n\t\tif ( child )\n\t\t\treturn TiXmlHandle( child );\n\t}\n\treturn TiXmlHandle( 0 );\n}\n\n\nTiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const\n{\n\tif ( node )\n\t{\n\t\tTiXmlElement* child = node->FirstChildElement( value );\n\t\tif ( child )\n\t\t\treturn TiXmlHandle( child );\n\t}\n\treturn TiXmlHandle( 0 );\n}\n\n\nTiXmlHandle TiXmlHandle::Child( int count ) const\n{\n\tif ( node )\n\t{\n\t\tint i;\n\t\tTiXmlNode* child = node->FirstChild();\n\t\tfor (\ti=0;\n\t\t\t\tchild && i<count;\n\t\t\t\tchild = child->NextSibling(), ++i )\n\t\t{\n\t\t\t// nothing\n\t\t}\n\t\tif ( child )\n\t\t\treturn TiXmlHandle( child );\n\t}\n\treturn TiXmlHandle( 0 );\n}\n\n\nTiXmlHandle TiXmlHandle::Child( const char* value, int count ) const\n{\n\tif ( node )\n\t{\n\t\tint i;\n\t\tTiXmlNode* child = node->FirstChild( value );\n\t\tfor (\ti=0;\n\t\t\t\tchild && i<count;\n\t\t\t\tchild = child->NextSibling( value ), ++i )\n\t\t{\n\t\t\t// nothing\n\t\t}\n\t\tif ( child )\n\t\t\treturn TiXmlHandle( child );\n\t}\n\treturn TiXmlHandle( 0 );\n}\n\n\nTiXmlHandle TiXmlHandle::ChildElement( int count ) const\n{\n\tif ( node )\n\t{\n\t\tint i;\n\t\tTiXmlElement* child = node->FirstChildElement();\n\t\tfor (\ti=0;\n\t\t\t\tchild && i<count;\n\t\t\t\tchild = child->NextSiblingElement(), ++i )\n\t\t{\n\t\t\t// nothing\n\t\t}\n\t\tif ( child )\n\t\t\treturn TiXmlHandle( child );\n\t}\n\treturn TiXmlHandle( 0 );\n}\n\n\nTiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const\n{\n\tif ( node )\n\t{\n\t\tint i;\n\t\tTiXmlElement* child = node->FirstChildElement( value );\n\t\tfor (\ti=0;\n\t\t\t\tchild && i<count;\n\t\t\t\tchild = child->NextSiblingElement( value ), ++i )\n\t\t{\n\t\t\t// nothing\n\t\t}\n\t\tif ( child )\n\t\t\treturn TiXmlHandle( child );\n\t}\n\treturn TiXmlHandle( 0 );\n}\n\n\nbool TiXmlPrinter::VisitEnter( const TiXmlDocument& )\n{\n\treturn true;\n}\n\nbool TiXmlPrinter::VisitExit( const TiXmlDocument& )\n{\n\treturn true;\n}\n\nbool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute )\n{\n\tDoIndent();\n\tbuffer += \"<\";\n\tbuffer += element.Value();\n\n\tfor( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() )\n\t{\n\t\tbuffer += \" \";\n\t\tattrib->Print( 0, 0, &buffer );\n\t}\n\n\tif ( !element.FirstChild() ) \n\t{\n\t\tbuffer += \" />\";\n\t\tDoLineBreak();\n\t}\n\telse \n\t{\n\t\tbuffer += \">\";\n\t\tif (\telement.FirstChild()->ToText()\n\t\t\t  && element.LastChild() == element.FirstChild()\n\t\t\t  && element.FirstChild()->ToText()->CDATA() == false )\n\t\t{\n\t\t\tsimpleTextPrint = true;\n\t\t\t// no DoLineBreak()!\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDoLineBreak();\n\t\t}\n\t}\n\t++depth;\t\n\treturn true;\n}\n\n\nbool TiXmlPrinter::VisitExit( const TiXmlElement& element )\n{\n\t--depth;\n\tif ( !element.FirstChild() ) \n\t{\n\t\t// nothing.\n\t}\n\telse \n\t{\n\t\tif ( simpleTextPrint )\n\t\t{\n\t\t\tsimpleTextPrint = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDoIndent();\n\t\t}\n\t\tbuffer += \"</\";\n\t\tbuffer += element.Value();\n\t\tbuffer += \">\";\n\t\tDoLineBreak();\n\t}\n\treturn true;\n}\n\n\nbool TiXmlPrinter::Visit( const TiXmlText& text )\n{\n\tif ( text.CDATA() )\n\t{\n\t\tDoIndent();\n\t\tbuffer += \"<![CDATA[\";\n\t\tbuffer += text.Value();\n\t\tbuffer += \"]]>\";\n\t\tDoLineBreak();\n\t}\n\telse if ( simpleTextPrint )\n\t{\n\t\tTIXML_STRING str;\n\t\tTiXmlBase::EncodeString( text.ValueTStr(), &str );\n\t\tbuffer += str;\n\t}\n\telse\n\t{\n\t\tDoIndent();\n\t\tTIXML_STRING str;\n\t\tTiXmlBase::EncodeString( text.ValueTStr(), &str );\n\t\tbuffer += str;\n\t\tDoLineBreak();\n\t}\n\treturn true;\n}\n\n\nbool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration )\n{\n\tDoIndent();\n\tdeclaration.Print( 0, 0, &buffer );\n\tDoLineBreak();\n\treturn true;\n}\n\n\nbool TiXmlPrinter::Visit( const TiXmlComment& comment )\n{\n\tDoIndent();\n\tbuffer += \"<!--\";\n\tbuffer += comment.Value();\n\tbuffer += \"-->\";\n\tDoLineBreak();\n\treturn true;\n}\n\n\nbool TiXmlPrinter::Visit( const TiXmlUnknown& unknown )\n{\n\tDoIndent();\n\tbuffer += \"<\";\n\tbuffer += unknown.Value();\n\tbuffer += \">\";\n\tDoLineBreak();\n\treturn true;\n}\n\n"
  },
  {
    "path": "extern/oics/tinyxml.h",
    "content": "/*\nwww.sourceforge.net/projects/tinyxml\nOriginal code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any\ndamages arising from the use of this software.\n\nPermission is granted to anyone to use this software for any\npurpose, including commercial applications, and to alter it and\nredistribute it freely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must\nnot claim that you wrote the original software. If you use this\nsoftware in a product, an acknowledgment in the product documentation\nwould be appreciated but is not required.\n\n2. Altered source versions must be plainly marked as such, and\nmust not be misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source\ndistribution.\n*/\n//#define TIXML_USE_STL\n\n#ifndef TINYXML_INCLUDED\n#define TINYXML_INCLUDED\n\n#ifdef _MSC_VER\n#pragma warning( push )\n#pragma warning( disable : 4530 )\n#pragma warning( disable : 4786 )\n#endif\n\n#include <ctype.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n// Help out windows:\n#if defined( _DEBUG ) && !defined( DEBUG )\n#define DEBUG\n#endif\n\n#ifdef TIXML_USE_STL\n\t#include <string>\n \t#include <iostream>\n\t#include <sstream>\n\t#define TIXML_STRING\t\tstd::string\n#else\n\t#include \"tinystr.h\"\n\t#define TIXML_STRING\t\tTiXmlString\n#endif\n\n// Deprecated library function hell. Compilers want to use the\n// new safe versions. This probably doesn't fully address the problem,\n// but it gets closer. There are too many compilers for me to fully\n// test. If you get compilation troubles, undefine TIXML_SAFE\n#define TIXML_SAFE\n\n#ifdef TIXML_SAFE\n\t#if defined(_MSC_VER) && (_MSC_VER >= 1400 )\n\t\t// Microsoft visual studio, version 2005 and higher.\n\t\t#define TIXML_SNPRINTF _snprintf_s\n\t\t#define TIXML_SNSCANF  _snscanf_s\n\t\t#define TIXML_SSCANF   sscanf_s\n\t#elif defined(_MSC_VER) && (_MSC_VER >= 1200 )\n\t\t// Microsoft visual studio, version 6 and higher.\n\t\t//#pragma message( \"Using _sn* functions.\" )\n\t\t#define TIXML_SNPRINTF _snprintf\n\t\t#define TIXML_SNSCANF  _snscanf\n\t\t#define TIXML_SSCANF   sscanf\n\t#elif defined(__GNUC__) && (__GNUC__ >= 3 )\n\t\t// GCC version 3 and higher.s\n\t\t//#warning( \"Using sn* functions.\" )\n\t\t#define TIXML_SNPRINTF snprintf\n\t\t#define TIXML_SNSCANF  snscanf\n\t\t#define TIXML_SSCANF   sscanf\n\t#else\n\t\t#define TIXML_SSCANF   sscanf\n\t#endif\n#endif\t\n\nclass TiXmlDocument;\nclass TiXmlElement;\nclass TiXmlComment;\nclass TiXmlUnknown;\nclass TiXmlAttribute;\nclass TiXmlText;\nclass TiXmlDeclaration;\nclass TiXmlParsingData;\n\nconst int TIXML_MAJOR_VERSION = 2;\nconst int TIXML_MINOR_VERSION = 5;\nconst int TIXML_PATCH_VERSION = 3;\n\n/*\tInternal structure for tracking location of items \n\tin the XML file.\n*/\nstruct TiXmlCursor\n{\n\tTiXmlCursor()\t\t{ Clear(); }\n\tvoid Clear()\t\t{ row = col = -1; }\n\n\tint row;\t// 0 based.\n\tint col;\t// 0 based.\n};\n\n\n/**\n\tIf you call the Accept() method, it requires being passed a TiXmlVisitor\n\tclass to handle callbacks. For nodes that contain other nodes (Document, Element)\n\tyou will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves\n\tare simple called with Visit().\n\n\tIf you return 'true' from a Visit method, recursive parsing will continue. If you return\n\tfalse, <b>no children of this node or its sibilings</b> will be Visited.\n\n\tAll flavors of Visit methods have a default implementation that returns 'true' (continue \n\tvisiting). You need to only override methods that are interesting to you.\n\n\tGenerally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting.\n\n\tYou should never change the document from a callback.\n\n\t@sa TiXmlNode::Accept()\n*/\nclass TiXmlVisitor\n{\npublic:\n\tvirtual ~TiXmlVisitor() {}\n\n\t/// Visit a document.\n\tvirtual bool VisitEnter( const TiXmlDocument& /*doc*/ )\t\t\t{ return true; }\n\t/// Visit a document.\n\tvirtual bool VisitExit( const TiXmlDocument& /*doc*/ )\t\t\t{ return true; }\n\n\t/// Visit an element.\n\tvirtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ )\t{ return true; }\n\t/// Visit an element.\n\tvirtual bool VisitExit( const TiXmlElement& /*element*/ )\t\t{ return true; }\n\n\t/// Visit a declaration\n\tvirtual bool Visit( const TiXmlDeclaration& /*declaration*/ )\t{ return true; }\n\t/// Visit a text node\n\tvirtual bool Visit( const TiXmlText& /*text*/ )\t\t\t\t\t{ return true; }\n\t/// Visit a comment node\n\tvirtual bool Visit( const TiXmlComment& /*comment*/ )\t\t\t{ return true; }\n\t/// Visit an unknow node\n\tvirtual bool Visit( const TiXmlUnknown& /*unknown*/ )\t\t\t{ return true; }\n};\n\n// Only used by Attribute::Query functions\nenum \n{ \n\tTIXML_SUCCESS,\n\tTIXML_NO_ATTRIBUTE,\n\tTIXML_WRONG_TYPE\n};\n\n\n// Used by the parsing routines.\nenum TiXmlEncoding\n{\n\tTIXML_ENCODING_UNKNOWN,\n\tTIXML_ENCODING_UTF8,\n\tTIXML_ENCODING_LEGACY\n};\n\nconst TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;\n\n/** TiXmlBase is a base class for every class in TinyXml.\n\tIt does little except to establish that TinyXml classes\n\tcan be printed and provide some utility functions.\n\n\tIn XML, the document and elements can contain\n\tother elements and other types of nodes.\n\n\t@verbatim\n\tA Document can contain:\tElement\t(container or leaf)\n\t\t\t\t\t\t\tComment (leaf)\n\t\t\t\t\t\t\tUnknown (leaf)\n\t\t\t\t\t\t\tDeclaration( leaf )\n\n\tAn Element can contain:\tElement (container or leaf)\n\t\t\t\t\t\t\tText\t(leaf)\n\t\t\t\t\t\t\tAttributes (not on tree)\n\t\t\t\t\t\t\tComment (leaf)\n\t\t\t\t\t\t\tUnknown (leaf)\n\n\tA Decleration contains: Attributes (not on tree)\n\t@endverbatim\n*/\nclass TiXmlBase\n{\n\tfriend class TiXmlNode;\n\tfriend class TiXmlElement;\n\tfriend class TiXmlDocument;\n\npublic:\n\tTiXmlBase()\t:\tuserData(0)\t\t{}\n\tvirtual ~TiXmlBase()\t\t\t{}\n\n\t/**\tAll TinyXml classes can print themselves to a filestream\n\t\tor the string class (TiXmlString in non-STL mode, std::string\n\t\tin STL mode.) Either or both cfile and str can be null.\n\t\t\n\t\tThis is a formatted print, and will insert \n\t\ttabs and newlines.\n\t\t\n\t\t(For an unformatted stream, use the << operator.)\n\t*/\n\tvirtual void Print( FILE* cfile, int depth ) const = 0;\n\n\t/**\tThe world does not agree on whether white space should be kept or\n\t\tnot. In order to make everyone happy, these global, static functions\n\t\tare provided to set whether or not TinyXml will condense all white space\n\t\tinto a single space or not. The default is to condense. Note changing this\n\t\tvalue is not thread safe.\n\t*/\n\tstatic void SetCondenseWhiteSpace( bool condense )\t\t{ condenseWhiteSpace = condense; }\n\n\t/// Return the current white space setting.\n\tstatic bool IsWhiteSpaceCondensed()\t\t\t\t\t\t{ return condenseWhiteSpace; }\n\n\t/** Return the position, in the original source file, of this node or attribute.\n\t\tThe row and column are 1-based. (That is the first row and first column is\n\t\t1,1). If the returns values are 0 or less, then the parser does not have\n\t\ta row and column value.\n\n\t\tGenerally, the row and column value will be set when the TiXmlDocument::Load(),\n\t\tTiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set\n\t\twhen the DOM was created from operator>>.\n\n\t\tThe values reflect the initial load. Once the DOM is modified programmatically\n\t\t(by adding or changing nodes and attributes) the new values will NOT update to\n\t\treflect changes in the document.\n\n\t\tThere is a minor performance cost to computing the row and column. Computation\n\t\tcan be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.\n\n\t\t@sa TiXmlDocument::SetTabSize()\n\t*/\n\tint Row() const\t\t\t{ return location.row + 1; }\n\tint Column() const\t\t{ return location.col + 1; }\t///< See Row()\n\n\tvoid  SetUserData( void* user )\t\t\t{ userData = user; }\t///< Set a pointer to arbitrary user data.\n\tvoid* GetUserData()\t\t\t\t\t\t{ return userData; }\t///< Get a pointer to arbitrary user data.\n\tconst void* GetUserData() const \t\t{ return userData; }\t///< Get a pointer to arbitrary user data.\n\n\t// Table that returs, for a given lead byte, the total number of bytes\n\t// in the UTF-8 sequence.\n\tstatic const int utf8ByteTable[256];\n\n\tvirtual const char* Parse(\tconst char* p, \n\t\t\t\t\t\t\t\tTiXmlParsingData* data, \n\t\t\t\t\t\t\t\tTiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;\n\n\t/** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, \n\t\tor they will be transformed into entities!\n\t*/\n\tstatic void EncodeString( const TIXML_STRING& str, TIXML_STRING* out );\n\n\tenum\n\t{\n\t\tTIXML_NO_ERROR = 0,\n\t\tTIXML_ERROR,\n\t\tTIXML_ERROR_OPENING_FILE,\n\t\tTIXML_ERROR_OUT_OF_MEMORY,\n\t\tTIXML_ERROR_PARSING_ELEMENT,\n\t\tTIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,\n\t\tTIXML_ERROR_READING_ELEMENT_VALUE,\n\t\tTIXML_ERROR_READING_ATTRIBUTES,\n\t\tTIXML_ERROR_PARSING_EMPTY,\n\t\tTIXML_ERROR_READING_END_TAG,\n\t\tTIXML_ERROR_PARSING_UNKNOWN,\n\t\tTIXML_ERROR_PARSING_COMMENT,\n\t\tTIXML_ERROR_PARSING_DECLARATION,\n\t\tTIXML_ERROR_DOCUMENT_EMPTY,\n\t\tTIXML_ERROR_EMBEDDED_NULL,\n\t\tTIXML_ERROR_PARSING_CDATA,\n\t\tTIXML_ERROR_DOCUMENT_TOP_ONLY,\n\n\t\tTIXML_ERROR_STRING_COUNT\n\t};\n\nprotected:\n\n\tstatic const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );\n\tinline static bool IsWhiteSpace( char c )\t\t\n\t{ \n\t\treturn ( isspace( (unsigned char) c ) || c == '\\n' || c == '\\r' ); \n\t}\n\tinline static bool IsWhiteSpace( int c )\n\t{\n\t\tif ( c < 256 )\n\t\t\treturn IsWhiteSpace( (char) c );\n\t\treturn false;\t// Again, only truly correct for English/Latin...but usually works.\n\t}\n\n\t#ifdef TIXML_USE_STL\n\tstatic bool\tStreamWhiteSpace( std::istream * in, TIXML_STRING * tag );\n\tstatic bool StreamTo( std::istream * in, int character, TIXML_STRING * tag );\n\t#endif\n\n\t/*\tReads an XML name into the string provided. Returns\n\t\ta pointer just past the last character of the name,\n\t\tor 0 if the function has an error.\n\t*/\n\tstatic const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );\n\n\t/*\tReads text. Returns a pointer past the given end tag.\n\t\tWickedly complex options, but it keeps the (sensitive) code in one place.\n\t*/\n\tstatic const char* ReadText(\tconst char* in,\t\t\t\t// where to start\n\t\t\t\t\t\t\t\t\tTIXML_STRING* text,\t\t\t// the string read\n\t\t\t\t\t\t\t\t\tbool ignoreWhiteSpace,\t\t// whether to keep the white space\n\t\t\t\t\t\t\t\t\tconst char* endTag,\t\t\t// what ends this text\n\t\t\t\t\t\t\t\t\tbool ignoreCase,\t\t\t// whether to ignore case in the end tag\n\t\t\t\t\t\t\t\t\tTiXmlEncoding encoding );\t// the current encoding\n\n\t// If an entity has been found, transform it into a character.\n\tstatic const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );\n\n\t// Get a character, while interpreting entities.\n\t// The length can be from 0 to 4 bytes.\n\tinline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )\n\t{\n\t\tassert( p );\n\t\tif ( encoding == TIXML_ENCODING_UTF8 )\n\t\t{\n\t\t\t*length = utf8ByteTable[ *((const unsigned char*)p) ];\n\t\t\tassert( *length >= 0 && *length < 5 );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*length = 1;\n\t\t}\n\n\t\tif ( *length == 1 )\n\t\t{\n\t\t\tif ( *p == '&' )\n\t\t\t\treturn GetEntity( p, _value, length, encoding );\n\t\t\t*_value = *p;\n\t\t\treturn p+1;\n\t\t}\n\t\telse if ( *length )\n\t\t{\n\t\t\t//strncpy( _value, p, *length );\t// lots of compilers don't like this function (unsafe),\n\t\t\t\t\t\t\t\t\t\t\t\t// and the null terminator isn't needed\n\t\t\tfor( int i=0; i<*length && p[i]; ++i ) {\n\t\t\t\t_value[i] = p[i];\n\t\t\t}\n\t\t\treturn p + (*length);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Not valid text.\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t// Return true if the next characters in the stream are any of the endTag sequences.\n\t// Ignore case only works for english, and should only be relied on when comparing\n\t// to English words: StringEqual( p, \"version\", true ) is fine.\n\tstatic bool StringEqual(\tconst char* p,\n\t\t\t\t\t\t\t\tconst char* endTag,\n\t\t\t\t\t\t\t\tbool ignoreCase,\n\t\t\t\t\t\t\t\tTiXmlEncoding encoding );\n\n\tstatic const char* errorString[ TIXML_ERROR_STRING_COUNT ];\n\n\tTiXmlCursor location;\n\n\t/// Field containing a generic user pointer\n\tvoid*\t\t\tuserData;\n\t\n\t// None of these methods are reliable for any language except English.\n\t// Good for approximation, not great for accuracy.\n\tstatic int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );\n\tstatic int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );\n\tinline static int ToLower( int v, TiXmlEncoding encoding )\n\t{\n\t\tif ( encoding == TIXML_ENCODING_UTF8 )\n\t\t{\n\t\t\tif ( v < 128 ) return tolower( v );\n\t\t\treturn v;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn tolower( v );\n\t\t}\n\t}\n\tstatic void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );\n\nprivate:\n\tTiXmlBase( const TiXmlBase& );\t\t\t\t// not implemented.\n\tvoid operator=( const TiXmlBase& base );\t// not allowed.\n\n\tstruct Entity\n\t{\n\t\tconst char*\t str;\n\t\tunsigned int\tstrLength;\n\t\tchar\t\t\tchr;\n\t};\n\tenum\n\t{\n\t\tNUM_ENTITY = 5,\n\t\tMAX_ENTITY_LENGTH = 6\n\n\t};\n\tstatic Entity entity[ NUM_ENTITY ];\n\tstatic bool condenseWhiteSpace;\n};\n\n\n/** The parent class for everything in the Document Object Model.\n\t(Except for attributes).\n\tNodes have siblings, a parent, and children. A node can be\n\tin a document, or stand on its own. The type of a TiXmlNode\n\tcan be queried, and it can be cast to its more defined type.\n*/\nclass TiXmlNode : public TiXmlBase\n{\n\tfriend class TiXmlDocument;\n\tfriend class TiXmlElement;\n\npublic:\n\t#ifdef TIXML_USE_STL\t\n\n\t\t/** An input stream operator, for every class. Tolerant of newlines and\n\t\t\tformatting, but doesn't expect them.\n\t\t*/\n\t\tfriend std::istream& operator >> (std::istream& in, TiXmlNode& base);\n\n\t\t/** An output stream operator, for every class. Note that this outputs\n\t\t\twithout any newlines or formatting, as opposed to Print(), which\n\t\t\tincludes tabs and new lines.\n\n\t\t\tThe operator<< and operator>> are not completely symmetric. Writing\n\t\t\ta node to a stream is very well defined. You'll get a nice stream\n\t\t\tof output, without any extra whitespace or newlines.\n\t\t\t\n\t\t\tBut reading is not as well defined. (As it always is.) If you create\n\t\t\ta TiXmlElement (for example) and read that from an input stream,\n\t\t\tthe text needs to define an element or junk will result. This is\n\t\t\ttrue of all input streams, but it's worth keeping in mind.\n\n\t\t\tA TiXmlDocument will read nodes until it reads a root element, and\n\t\t\tall the children of that root element.\n\t\t*/\t\n\t\tfriend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);\n\n\t\t/// Appends the XML node or attribute to a std::string.\n\t\tfriend std::string& operator<< (std::string& out, const TiXmlNode& base );\n\n\t#endif\n\n\t/** The types of XML nodes supported by TinyXml. (All the\n\t\t\tunsupported types are picked up by UNKNOWN.)\n\t*/\n\tenum NodeType\n\t{\n\t\tDOCUMENT,\n\t\tELEMENT,\n\t\tCOMMENT,\n\t\tUNKNOWN,\n\t\tTEXT,\n\t\tDECLARATION,\n\t\tTYPECOUNT\n\t};\n\n\tvirtual ~TiXmlNode();\n\n\t/** The meaning of 'value' changes for the specific type of\n\t\tTiXmlNode.\n\t\t@verbatim\n\t\tDocument:\tfilename of the xml file\n\t\tElement:\tname of the element\n\t\tComment:\tthe comment text\n\t\tUnknown:\tthe tag contents\n\t\tText:\t\tthe text string\n\t\t@endverbatim\n\n\t\tThe subclasses will wrap this function.\n\t*/\n\tconst char *Value() const { return value.c_str (); }\n\n\t#ifdef TIXML_USE_STL\n\t/** Return Value() as a std::string. If you only use STL,\n\t\tthis is more efficient than calling Value().\n\t\tOnly available in STL mode.\n\t*/\n\tconst std::string& ValueStr() const { return value; }\n\t#endif\n\n\tconst TIXML_STRING& ValueTStr() const { return value; }\n\n\t/** Changes the value of the node. Defined as:\n\t\t@verbatim\n\t\tDocument:\tfilename of the xml file\n\t\tElement:\tname of the element\n\t\tComment:\tthe comment text\n\t\tUnknown:\tthe tag contents\n\t\tText:\t\tthe text string\n\t\t@endverbatim\n\t*/\n\tvoid SetValue(const char * _value) { value = _value;}\n\n\t#ifdef TIXML_USE_STL\n\t/// STL std::string form.\n\tvoid SetValue( const std::string& _value )\t{ value = _value; }\n\t#endif\n\n\t/// Delete all the children of this node. Does not affect 'this'.\n\tvoid Clear();\n\n\t/// One step up the DOM.\n\tTiXmlNode* Parent()\t\t\t\t\t\t\t{ return parent; }\n\tconst TiXmlNode* Parent() const\t\t\t\t{ return parent; }\n\n\tconst TiXmlNode* FirstChild()\tconst\t\t{ return firstChild; }\t///< The first child of this node. Will be null if there are no children.\n\tTiXmlNode* FirstChild()\t\t\t\t\t\t{ return firstChild; }\n\tconst TiXmlNode* FirstChild( const char * value ) const;\t\t\t///< The first child of this node with the matching 'value'. Will be null if none found.\n\t/// The first child of this node with the matching 'value'. Will be null if none found.\n\tTiXmlNode* FirstChild( const char * _value ) {\n\t\t// Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe)\n\t\t// call the method, cast the return back to non-const.\n\t\treturn const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value ));\n\t}\n\tconst TiXmlNode* LastChild() const\t{ return lastChild; }\t\t/// The last child of this node. Will be null if there are no children.\n\tTiXmlNode* LastChild()\t{ return lastChild; }\n\t\n\tconst TiXmlNode* LastChild( const char * value ) const;\t\t\t/// The last child of this node matching 'value'. Will be null if there are no children.\n\tTiXmlNode* LastChild( const char * _value ) {\n\t\treturn const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value ));\n\t}\n\n\t#ifdef TIXML_USE_STL\n\tconst TiXmlNode* FirstChild( const std::string& _value ) const\t{\treturn FirstChild (_value.c_str ());\t}\t///< STL std::string form.\n\tTiXmlNode* FirstChild( const std::string& _value )\t\t\t\t{\treturn FirstChild (_value.c_str ());\t}\t///< STL std::string form.\n\tconst TiXmlNode* LastChild( const std::string& _value ) const\t{\treturn LastChild (_value.c_str ());\t}\t///< STL std::string form.\n\tTiXmlNode* LastChild( const std::string& _value )\t\t\t\t{\treturn LastChild (_value.c_str ());\t}\t///< STL std::string form.\n\t#endif\n\n\t/** An alternate way to walk the children of a node.\n\t\tOne way to iterate over nodes is:\n\t\t@verbatim\n\t\t\tfor( child = parent->FirstChild(); child; child = child->NextSibling() )\n\t\t@endverbatim\n\n\t\tIterateChildren does the same thing with the syntax:\n\t\t@verbatim\n\t\t\tchild = 0;\n\t\t\twhile( child = parent->IterateChildren( child ) )\n\t\t@endverbatim\n\n\t\tIterateChildren takes the previous child as input and finds\n\t\tthe next one. If the previous child is null, it returns the\n\t\tfirst. IterateChildren will return null when done.\n\t*/\n\tconst TiXmlNode* IterateChildren( const TiXmlNode* previous ) const;\n\tTiXmlNode* IterateChildren( const TiXmlNode* previous ) {\n\t\treturn const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) );\n\t}\n\n\t/// This flavor of IterateChildren searches for children with a particular 'value'\n\tconst TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const;\n\tTiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) {\n\t\treturn const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) );\n\t}\n\n\t#ifdef TIXML_USE_STL\n\tconst TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const\t{\treturn IterateChildren (_value.c_str (), previous);\t}\t///< STL std::string form.\n\tTiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) {\treturn IterateChildren (_value.c_str (), previous);\t}\t///< STL std::string form.\n\t#endif\n\n\t/** Add a new node related to this. Adds a child past the LastChild.\n\t\tReturns a pointer to the new object or NULL if an error occurred.\n\t*/\n\tTiXmlNode* InsertEndChild( const TiXmlNode& addThis );\n\n\n\t/** Add a new node related to this. Adds a child past the LastChild.\n\n\t\tNOTE: the node to be added is passed by pointer, and will be\n\t\thenceforth owned (and deleted) by tinyXml. This method is efficient\n\t\tand avoids an extra copy, but should be used with care as it\n\t\tuses a different memory model than the other insert functions.\n\n\t\t@sa InsertEndChild\n\t*/\n\tTiXmlNode* LinkEndChild( TiXmlNode* addThis );\n\n\t/** Add a new node related to this. Adds a child before the specified child.\n\t\tReturns a pointer to the new object or NULL if an error occurred.\n\t*/\n\tTiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );\n\n\t/** Add a new node related to this. Adds a child after the specified child.\n\t\tReturns a pointer to the new object or NULL if an error occurred.\n\t*/\n\tTiXmlNode* InsertAfterChild(  TiXmlNode* afterThis, const TiXmlNode& addThis );\n\n\t/** Replace a child of this node.\n\t\tReturns a pointer to the new object or NULL if an error occurred.\n\t*/\n\tTiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );\n\n\t/// Delete a child of this node.\n\tbool RemoveChild( TiXmlNode* removeThis );\n\n\t/// Navigate to a sibling node.\n\tconst TiXmlNode* PreviousSibling() const\t\t\t{ return prev; }\n\tTiXmlNode* PreviousSibling()\t\t\t\t\t\t{ return prev; }\n\n\t/// Navigate to a sibling node.\n\tconst TiXmlNode* PreviousSibling( const char * ) const;\n\tTiXmlNode* PreviousSibling( const char *_prev ) {\n\t\treturn const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) );\n\t}\n\n\t#ifdef TIXML_USE_STL\n\tconst TiXmlNode* PreviousSibling( const std::string& _value ) const\t{\treturn PreviousSibling (_value.c_str ());\t}\t///< STL std::string form.\n\tTiXmlNode* PreviousSibling( const std::string& _value ) \t\t\t{\treturn PreviousSibling (_value.c_str ());\t}\t///< STL std::string form.\n\tconst TiXmlNode* NextSibling( const std::string& _value) const\t\t{\treturn NextSibling (_value.c_str ());\t}\t///< STL std::string form.\n\tTiXmlNode* NextSibling( const std::string& _value) \t\t\t\t\t{\treturn NextSibling (_value.c_str ());\t}\t///< STL std::string form.\n\t#endif\n\n\t/// Navigate to a sibling node.\n\tconst TiXmlNode* NextSibling() const\t\t\t\t{ return next; }\n\tTiXmlNode* NextSibling()\t\t\t\t\t\t\t{ return next; }\n\n\t/// Navigate to a sibling node with the given 'value'.\n\tconst TiXmlNode* NextSibling( const char * ) const;\n\tTiXmlNode* NextSibling( const char* _next ) {\n\t\treturn const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) );\n\t}\n\n\t/** Convenience function to get through elements.\n\t\tCalls NextSibling and ToElement. Will skip all non-Element\n\t\tnodes. Returns 0 if there is not another element.\n\t*/\n\tconst TiXmlElement* NextSiblingElement() const;\n\tTiXmlElement* NextSiblingElement() {\n\t\treturn const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() );\n\t}\n\n\t/** Convenience function to get through elements.\n\t\tCalls NextSibling and ToElement. Will skip all non-Element\n\t\tnodes. Returns 0 if there is not another element.\n\t*/\n\tconst TiXmlElement* NextSiblingElement( const char * ) const;\n\tTiXmlElement* NextSiblingElement( const char *_next ) {\n\t\treturn const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) );\n\t}\n\n\t#ifdef TIXML_USE_STL\n\tconst TiXmlElement* NextSiblingElement( const std::string& _value) const\t{\treturn NextSiblingElement (_value.c_str ());\t}\t///< STL std::string form.\n\tTiXmlElement* NextSiblingElement( const std::string& _value)\t\t\t\t{\treturn NextSiblingElement (_value.c_str ());\t}\t///< STL std::string form.\n\t#endif\n\n\t/// Convenience function to get through elements.\n\tconst TiXmlElement* FirstChildElement()\tconst;\n\tTiXmlElement* FirstChildElement() {\n\t\treturn const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() );\n\t}\n\n\t/// Convenience function to get through elements.\n\tconst TiXmlElement* FirstChildElement( const char * _value ) const;\n\tTiXmlElement* FirstChildElement( const char * _value ) {\n\t\treturn const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) );\n\t}\n\n\t#ifdef TIXML_USE_STL\n\tconst TiXmlElement* FirstChildElement( const std::string& _value ) const\t{\treturn FirstChildElement (_value.c_str ());\t}\t///< STL std::string form.\n\tTiXmlElement* FirstChildElement( const std::string& _value )\t\t\t\t{\treturn FirstChildElement (_value.c_str ());\t}\t///< STL std::string form.\n\t#endif\n\n\t/** Query the type (as an enumerated value, above) of this node.\n\t\tThe possible types are: DOCUMENT, ELEMENT, COMMENT,\n\t\t\t\t\t\t\t\tUNKNOWN, TEXT, and DECLARATION.\n\t*/\n\tint Type() const\t{ return type; }\n\n\t/** Return a pointer to the Document this node lives in.\n\t\tReturns null if not in a document.\n\t*/\n\tconst TiXmlDocument* GetDocument() const;\n\tTiXmlDocument* GetDocument() {\n\t\treturn const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() );\n\t}\n\n\t/// Returns true if this node has no children.\n\tbool NoChildren() const\t\t\t\t\t\t{ return !firstChild; }\n\n\tvirtual const TiXmlDocument*\tToDocument()\tconst { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual const TiXmlElement*\t ToElement()\t const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual const TiXmlComment*\t ToComment()\t const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual const TiXmlUnknown*\t ToUnknown()\t const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual const TiXmlText*\t\tToText()\t\tconst { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\n\tvirtual TiXmlDocument*\t\t  ToDocument()\t{ return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual TiXmlElement*\t\t   ToElement()\t\t{ return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual TiXmlComment*\t\t   ToComment()\t { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual TiXmlUnknown*\t\t   ToUnknown()\t\t{ return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual TiXmlText*\t\t\t\tToText()\t\t{ return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\tvirtual TiXmlDeclaration*\t   ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.\n\n\t/** Create an exact duplicate of this node and return it. The memory must be deleted\n\t\tby the caller. \n\t*/\n\tvirtual TiXmlNode* Clone() const = 0;\n\n\t/** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the \n\t\tXML tree will be conditionally visited and the host will be called back\n\t\tvia the TiXmlVisitor interface.\n\n\t\tThis is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse\n\t\tthe XML for the callbacks, so the performance of TinyXML is unchanged by using this\n\t\tinterface versus any other.)\n\n\t\tThe interface has been based on ideas from:\n\n\t\t- http://www.saxproject.org/\n\t\t- http://c2.com/cgi/wiki?HierarchicalVisitorPattern \n\n\t\tWhich are both good references for \"visiting\".\n\n\t\tAn example of using Accept():\n\t\t@verbatim\n\t\tTiXmlPrinter printer;\n\t\ttinyxmlDoc.Accept( &printer );\n\t\tconst char* xmlcstr = printer.CStr();\n\t\t@endverbatim\n\t*/\n\tvirtual bool Accept( TiXmlVisitor* visitor ) const = 0;\n\nprotected:\n\tTiXmlNode( NodeType _type );\n\n\t// Copy to the allocated object. Shared functionality between Clone, Copy constructor,\n\t// and the assignment operator.\n\tvoid CopyTo( TiXmlNode* target ) const;\n\n\t#ifdef TIXML_USE_STL\n\t\t// The real work of the input operator.\n\tvirtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0;\n\t#endif\n\n\t// Figure out what is at *p, and parse it. Returns null if it is not an xml node.\n\tTiXmlNode* Identify( const char* start, TiXmlEncoding encoding );\n\n\tTiXmlNode*\t\tparent;\n\tNodeType\t\ttype;\n\n\tTiXmlNode*\t\tfirstChild;\n\tTiXmlNode*\t\tlastChild;\n\n\tTIXML_STRING\tvalue;\n\n\tTiXmlNode*\t\tprev;\n\tTiXmlNode*\t\tnext;\n\nprivate:\n\tTiXmlNode( const TiXmlNode& );\t\t\t\t// not implemented.\n\tvoid operator=( const TiXmlNode& base );\t// not allowed.\n};\n\n\n/** An attribute is a name-value pair. Elements have an arbitrary\n\tnumber of attributes, each with a unique name.\n\n\t@note The attributes are not TiXmlNodes, since they are not\n\t\t  part of the tinyXML document object model. There are other\n\t\t  suggested ways to look at this problem.\n*/\nclass TiXmlAttribute : public TiXmlBase\n{\n\tfriend class TiXmlAttributeSet;\n\npublic:\n\t/// Construct an empty attribute.\n\tTiXmlAttribute() : TiXmlBase()\n\t{\n\t\tdocument = 0;\n\t\tprev = next = 0;\n\t}\n\n\t#ifdef TIXML_USE_STL\n\t/// std::string constructor.\n\tTiXmlAttribute( const std::string& _name, const std::string& _value )\n\t{\n\t\tname = _name;\n\t\tvalue = _value;\n\t\tdocument = 0;\n\t\tprev = next = 0;\n\t}\n\t#endif\n\n\t/// Construct an attribute with a name and value.\n\tTiXmlAttribute( const char * _name, const char * _value )\n\t{\n\t\tname = _name;\n\t\tvalue = _value;\n\t\tdocument = 0;\n\t\tprev = next = 0;\n\t}\n\n\tconst char*\t\tName()  const\t\t{ return name.c_str(); }\t\t///< Return the name of this attribute.\n\tconst char*\t\tValue() const\t\t{ return value.c_str(); }\t\t///< Return the value of this attribute.\n\t#ifdef TIXML_USE_STL\n\tconst std::string& ValueStr() const\t{ return value; }\t\t\t\t///< Return the value of this attribute.\n\t#endif\n\tint\t\t\t\tIntValue() const;\t\t\t\t\t\t\t\t\t///< Return the value of this attribute, converted to an integer.\n\tdouble\t\t\tDoubleValue() const;\t\t\t\t\t\t\t\t///< Return the value of this attribute, converted to a double.\n\n\t// Get the tinyxml string representation\n\tconst TIXML_STRING& NameTStr() const { return name; }\n\n\t/** QueryIntValue examines the value string. It is an alternative to the\n\t\tIntValue() method with richer error checking.\n\t\tIf the value is an integer, it is stored in 'value' and \n\t\tthe call returns TIXML_SUCCESS. If it is not\n\t\tan integer, it returns TIXML_WRONG_TYPE.\n\n\t\tA specialized but useful call. Note that for success it returns 0,\n\t\twhich is the opposite of almost all other TinyXml calls.\n\t*/\n\tint QueryIntValue( int* _value ) const;\n\t/// QueryDoubleValue examines the value string. See QueryIntValue().\n\tint QueryDoubleValue( double* _value ) const;\n\n\tvoid SetName( const char* _name )\t{ name = _name; }\t\t\t\t///< Set the name of this attribute.\n\tvoid SetValue( const char* _value )\t{ value = _value; }\t\t\t\t///< Set the value.\n\n\tvoid SetIntValue( int _value );\t\t\t\t\t\t\t\t\t\t///< Set the value from an integer.\n\tvoid SetDoubleValue( double _value );\t\t\t\t\t\t\t\t///< Set the value from a double.\n\n\t#ifdef TIXML_USE_STL\n\t/// STL std::string form.\n\tvoid SetName( const std::string& _name )\t{ name = _name; }\t\n\t/// STL std::string form.\t\n\tvoid SetValue( const std::string& _value )\t{ value = _value; }\n\t#endif\n\n\t/// Get the next sibling attribute in the DOM. Returns null at end.\n\tconst TiXmlAttribute* Next() const;\n\tTiXmlAttribute* Next() {\n\t\treturn const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); \n\t}\n\n\t/// Get the previous sibling attribute in the DOM. Returns null at beginning.\n\tconst TiXmlAttribute* Previous() const;\n\tTiXmlAttribute* Previous() {\n\t\treturn const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); \n\t}\n\n\tbool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }\n\tbool operator<( const TiXmlAttribute& rhs )\t const { return name < rhs.name; }\n\tbool operator>( const TiXmlAttribute& rhs )  const { return name > rhs.name; }\n\n\t/*\tAttribute parsing starts: first letter of the name\n\t\t\t\t\t\t returns: the next char after the value end quote\n\t*/\n\tconst char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) override;\n\n\t// Prints this Attribute to a FILE stream.\n\tvoid Print( FILE* cfile, int depth ) const override\n    {\n\t\tPrint( cfile, depth, 0 );\n\t}\n\tvoid Print( FILE* cfile, int depth, TIXML_STRING* str ) const;\n\n\t// [internal use]\n\t// Set the document pointer so the attribute can report errors.\n\tvoid SetDocument( TiXmlDocument* doc )\t{ document = doc; }\n\nprivate:\n\tTiXmlAttribute( const TiXmlAttribute& );\t\t\t\t// not implemented.\n\tvoid operator=( const TiXmlAttribute& base );\t// not allowed.\n\n\tTiXmlDocument*\tdocument;\t// A pointer back to a document, for error reporting.\n\tTIXML_STRING name;\n\tTIXML_STRING value;\n\tTiXmlAttribute*\tprev;\n\tTiXmlAttribute*\tnext;\n};\n\n\n/*\tA class used to manage a group of attributes.\n\tIt is only used internally, both by the ELEMENT and the DECLARATION.\n\t\n\tThe set can be changed transparent to the Element and Declaration\n\tclasses that use it, but NOT transparent to the Attribute\n\twhich has to implement a next() and previous() method. Which makes\n\tit a bit problematic and prevents the use of STL.\n\n\tThis version is implemented with circular lists because:\n\t\t- I like circular lists\n\t\t- it demonstrates some independence from the (typical) doubly linked list.\n*/\nclass TiXmlAttributeSet\n{\npublic:\n\tTiXmlAttributeSet();\n\t~TiXmlAttributeSet();\n\n\tvoid Add( TiXmlAttribute* attribute );\n\tvoid Remove( TiXmlAttribute* attribute );\n\n\tconst TiXmlAttribute* First()\tconst\t{ return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }\n\tTiXmlAttribute* First()\t\t\t\t\t{ return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }\n\tconst TiXmlAttribute* Last() const\t\t{ return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }\n\tTiXmlAttribute* Last()\t\t\t\t\t{ return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }\n\n\tconst TiXmlAttribute*\tFind( const char* _name ) const;\n\tTiXmlAttribute*\tFind( const char* _name ) {\n\t\treturn const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );\n\t}\n\t#ifdef TIXML_USE_STL\n\tconst TiXmlAttribute*\tFind( const std::string& _name ) const;\n\tTiXmlAttribute*\tFind( const std::string& _name ) {\n\t\treturn const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );\n\t}\n\n\t#endif\n\nprivate:\n\t//*ME:\tBecause of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),\n\t//*ME:\tthis class must be also use a hidden/disabled copy-constructor !!!\n\tTiXmlAttributeSet( const TiXmlAttributeSet& );\t// not allowed\n\tvoid operator=( const TiXmlAttributeSet& );\t// not allowed (as TiXmlAttribute)\n\n\tTiXmlAttribute sentinel;\n};\n\n\n/** The element is a container class. It has a value, the element name,\n\tand can contain other elements, text, comments, and unknowns.\n\tElements also contain an arbitrary number of attributes.\n*/\nclass TiXmlElement : public TiXmlNode\n{\npublic:\n\t/// Construct an element.\n\tTiXmlElement (const char * in_value);\n\n\t#ifdef TIXML_USE_STL\n\t/// std::string constructor.\n\tTiXmlElement( const std::string& _value );\n\t#endif\n\n\tTiXmlElement( const TiXmlElement& );\n\n\tvoid operator=( const TiXmlElement& base );\n\n\tvirtual ~TiXmlElement();\n\n\t/** Given an attribute name, Attribute() returns the value\n\t\tfor the attribute of that name, or null if none exists.\n\t*/\n\tconst char* Attribute( const char* name ) const;\n\n\t/** Given an attribute name, Attribute() returns the value\n\t\tfor the attribute of that name, or null if none exists.\n\t\tIf the attribute exists and can be converted to an integer,\n\t\tthe integer value will be put in the return 'i', if 'i'\n\t\tis non-null.\n\t*/\n\tconst char* Attribute( const char* name, int* i ) const;\n\n\t/** Given an attribute name, Attribute() returns the value\n\t\tfor the attribute of that name, or null if none exists.\n\t\tIf the attribute exists and can be converted to an double,\n\t\tthe double value will be put in the return 'd', if 'd'\n\t\tis non-null.\n\t*/\n\tconst char* Attribute( const char* name, double* d ) const;\n\n\t/** QueryIntAttribute examines the attribute - it is an alternative to the\n\t\tAttribute() method with richer error checking.\n\t\tIf the attribute is an integer, it is stored in 'value' and \n\t\tthe call returns TIXML_SUCCESS. If it is not\n\t\tan integer, it returns TIXML_WRONG_TYPE. If the attribute\n\t\tdoes not exist, then TIXML_NO_ATTRIBUTE is returned.\n\t*/\t\n\tint QueryIntAttribute( const char* name, int* _value ) const;\n\t/// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().\n\tint QueryDoubleAttribute( const char* name, double* _value ) const;\n\t/// QueryFloatAttribute examines the attribute - see QueryIntAttribute().\n\tint QueryFloatAttribute( const char* name, float* _value ) const {\n\t\tdouble d;\n\t\tint result = QueryDoubleAttribute( name, &d );\n\t\tif ( result == TIXML_SUCCESS ) {\n\t\t\t*_value = (float)d;\n\t\t}\n\t\treturn result;\n\t}\n\n\t#ifdef TIXML_USE_STL\n\t/** Template form of the attribute query which will try to read the\n\t\tattribute into the specified type. Very easy, very powerful, but\n\t\tbe careful to make sure to call this with the correct type.\n\t\t\n\t\tNOTE: This method doesn't work correctly for 'string' types.\n\n\t\t@return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE\n\t*/\n\ttemplate< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const\n\t{\n\t\tconst TiXmlAttribute* node = attributeSet.Find( name );\n\t\tif ( !node )\n\t\t\treturn TIXML_NO_ATTRIBUTE;\n\n\t\tstd::stringstream sstream( node->ValueStr() );\n\t\tsstream >> *outValue;\n\t\tif ( !sstream.fail() )\n\t\t\treturn TIXML_SUCCESS;\n\t\treturn TIXML_WRONG_TYPE;\n\t}\n\t/*\n\t This is - in theory - a bug fix for \"QueryValueAtribute returns truncated std::string\"\n\t but template specialization is hard to get working cross-compiler. Leaving the bug for now.\n\t \n\t// The above will fail for std::string because the space character is used as a seperator.\n\t// Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string\n\ttemplate<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const\n\t{\n\t\tconst TiXmlAttribute* node = attributeSet.Find( name );\n\t\tif ( !node )\n\t\t\treturn TIXML_NO_ATTRIBUTE;\n\t\t*outValue = node->ValueStr();\n\t\treturn TIXML_SUCCESS;\n\t}\n\t*/\n\t#endif\n\n\t/** Sets an attribute of name to a given value. The attribute\n\t\twill be created if it does not exist, or changed if it does.\n\t*/\n\tvoid SetAttribute( const char* name, const char * _value );\n\n\t#ifdef TIXML_USE_STL\n\tconst std::string* Attribute( const std::string& name ) const;\n\tconst std::string* Attribute( const std::string& name, int* i ) const;\n\tconst std::string* Attribute( const std::string& name, double* d ) const;\n\tint QueryIntAttribute( const std::string& name, int* _value ) const;\n\tint QueryDoubleAttribute( const std::string& name, double* _value ) const;\n\n\t/// STL std::string form.\n\tvoid SetAttribute( const std::string& name, const std::string& _value );\n\t///< STL std::string form.\n\tvoid SetAttribute( const std::string& name, int _value );\n\t#endif\n\n\t/** Sets an attribute of name to a given value. The attribute\n\t\twill be created if it does not exist, or changed if it does.\n\t*/\n\tvoid SetAttribute( const char * name, int value );\n\n\t/** Sets an attribute of name to a given value. The attribute\n\t\twill be created if it does not exist, or changed if it does.\n\t*/\n\tvoid SetDoubleAttribute( const char * name, double value );\n\n\t/** Deletes an attribute with the given name.\n\t*/\n\tvoid RemoveAttribute( const char * name );\n\t#ifdef TIXML_USE_STL\n\tvoid RemoveAttribute( const std::string& name )\t{\tRemoveAttribute (name.c_str ());\t}\t///< STL std::string form.\n\t#endif\n\n\tconst TiXmlAttribute* FirstAttribute() const\t{ return attributeSet.First(); }\t\t///< Access the first attribute in this element.\n\tTiXmlAttribute* FirstAttribute() \t\t\t\t{ return attributeSet.First(); }\n\tconst TiXmlAttribute* LastAttribute()\tconst \t{ return attributeSet.Last(); }\t\t///< Access the last attribute in this element.\n\tTiXmlAttribute* LastAttribute()\t\t\t\t\t{ return attributeSet.Last(); }\n\n\t/** Convenience function for easy access to the text inside an element. Although easy\n\t\tand concise, GetText() is limited compared to getting the TiXmlText child\n\t\tand accessing it directly.\n\t\n\t\tIf the first child of 'this' is a TiXmlText, the GetText()\n\t\treturns the character string of the Text node, else null is returned.\n\n\t\tThis is a convenient method for getting the text of simple contained text:\n\t\t@verbatim\n\t\t<foo>This is text</foo>\n\t\tconst char* str = fooElement->GetText();\n\t\t@endverbatim\n\n\t\t'str' will be a pointer to \"This is text\". \n\t\t\n\t\tNote that this function can be misleading. If the element foo was created from\n\t\tthis XML:\n\t\t@verbatim\n\t\t<foo><b>This is text</b></foo> \n\t\t@endverbatim\n\n\t\tthen the value of str would be null. The first child node isn't a text node, it is\n\t\tanother element. From this XML:\n\t\t@verbatim\n\t\t<foo>This is <b>text</b></foo> \n\t\t@endverbatim\n\t\tGetText() will return \"This is \".\n\n\t\tWARNING: GetText() accesses a child node - don't become confused with the \n\t\t\t\t similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are \n\t\t\t\t safe type casts on the referenced node.\n\t*/\n\tconst char* GetText() const;\n\n\t/// Creates a new Element and returns it - the returned element is a copy.\n\tTiXmlNode* Clone() const override;\n\t// Print the Element to a FILE stream.\n\tvoid Print( FILE* cfile, int depth ) const override;\n\n\t/*\tAttribtue parsing starts: next char past '<'\n\t\t\t\t\t\t returns: next char past '>'\n\t*/\n\tconst char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) override;\n\n\tconst TiXmlElement*\t ToElement() const override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\tTiXmlElement* ToElement() override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\n\t/** Walk the XML tree visiting this node and all of its children. \n\t*/\n\tbool Accept( TiXmlVisitor* visitor ) const override;\n\nprotected:\n\n\tvoid CopyTo( TiXmlElement* target ) const;\n\tvoid ClearThis();\t// like clear, but initializes 'this' object as well\n\n\t// Used to be public [internal use]\n\t#ifdef TIXML_USE_STL\n\tvirtual void StreamIn( std::istream * in, TIXML_STRING * tag ) override;\n\t#endif\n\t/*\t[internal use]\n\t\tReads the \"value\" of the element -- another element, or text.\n\t\tThis should terminate with the current end tag.\n\t*/\n\tconst char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );\n\nprivate:\n\n\tTiXmlAttributeSet attributeSet;\n};\n\n\n/**\tAn XML comment.\n*/\nclass TiXmlComment : public TiXmlNode\n{\npublic:\n\t/// Constructs an empty comment.\n\tTiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {}\n\t/// Construct a comment from text.\n\tTiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) {\n\t\tSetValue( _value );\n\t}\n\tTiXmlComment( const TiXmlComment& );\n\tvoid operator=( const TiXmlComment& base );\n\n\tvirtual ~TiXmlComment()\t{}\n\n\t/// Returns a copy of this Comment.\n\tTiXmlNode* Clone() const override;\n\t// Write this Comment to a FILE stream.\n\tvoid Print( FILE* cfile, int depth ) const override;\n\n\t/*\tAttribtue parsing starts: at the ! of the !--\n\t\t\t\t\t\t returns: next char past '>'\n\t*/\n\tconst char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) override;\n\n\tconst TiXmlComment* ToComment() const override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\tTiXmlComment* ToComment() override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\n\t/** Walk the XML tree visiting this node and all of its children. \n\t*/\n\tbool Accept( TiXmlVisitor* visitor ) const override;\n\nprotected:\n\tvoid CopyTo( TiXmlComment* target ) const;\n\n\t// used to be public\n\t#ifdef TIXML_USE_STL\n\tvirtual void StreamIn( std::istream * in, TIXML_STRING * tag ) override;\n\t#endif\n//\tvirtual void StreamOut( TIXML_OSTREAM * out ) const;\n\nprivate:\n\n};\n\n\n/** XML text. A text node can have 2 ways to output the next. \"normal\" output \n\tand CDATA. It will default to the mode it was parsed from the XML file and\n\tyou generally want to leave it alone, but you can change the output mode with \n\tSetCDATA() and query it with CDATA().\n*/\nclass TiXmlText : public TiXmlNode\n{\n\tfriend class TiXmlElement;\npublic:\n\t/** Constructor for text element. By default, it is treated as \n\t\tnormal, encoded text. If you want it be output as a CDATA text\n\t\telement, set the parameter _cdata to 'true'\n\t*/\n\tTiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT)\n\t{\n\t\tSetValue( initValue );\n\t\tcdata = false;\n\t}\n\tvirtual ~TiXmlText() {}\n\n\t#ifdef TIXML_USE_STL\n\t/// Constructor.\n\tTiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT)\n\t{\n\t\tSetValue( initValue );\n\t\tcdata = false;\n\t}\n\t#endif\n\n\tTiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT )\t{ copy.CopyTo( this ); }\n\tvoid operator=( const TiXmlText& base )\t\t\t\t\t\t\t \t{ base.CopyTo( this ); }\n\n\t// Write this text object to a FILE stream.\n\tvoid Print( FILE* cfile, int depth ) const override;\n\n\t/// Queries whether this represents text using a CDATA section.\n\tbool CDATA() const\t\t\t\t{ return cdata; }\n\t/// Turns on or off a CDATA representation of text.\n\tvoid SetCDATA( bool _cdata )\t{ cdata = _cdata; }\n\n\tconst char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) override;\n\n\tconst TiXmlText* ToText() const override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\tTiXmlText* ToText() override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\n\t/** Walk the XML tree visiting this node and all of its children. \n\t*/\n\tbool Accept( TiXmlVisitor* content ) const override;\n\nprotected :\n\t///  [internal use] Creates a new Element and returns it.\n\tTiXmlNode* Clone() const override;\n\tvoid CopyTo( TiXmlText* target ) const;\n\n\tbool Blank() const;\t// returns true if all white space and new lines\n\t// [internal use]\n\t#ifdef TIXML_USE_STL\n\tvirtual void StreamIn( std::istream * in, TIXML_STRING * tag ) override;\n\t#endif\n\nprivate:\n\tbool cdata;\t\t\t// true if this should be input and output as a CDATA style text element\n};\n\n\n/** In correct XML the declaration is the first entry in the file.\n\t@verbatim\n\t\t<?xml version=\"1.0\" standalone=\"yes\"?>\n\t@endverbatim\n\n\tTinyXml will happily read or write files without a declaration,\n\thowever. There are 3 possible attributes to the declaration:\n\tversion, encoding, and standalone.\n\n\tNote: In this version of the code, the attributes are\n\thandled as special cases, not generic attributes, simply\n\tbecause there can only be at most 3 and they are always the same.\n*/\nclass TiXmlDeclaration : public TiXmlNode\n{\npublic:\n\t/// Construct an empty declaration.\n\tTiXmlDeclaration()   : TiXmlNode( TiXmlNode::DECLARATION ) {}\n\n#ifdef TIXML_USE_STL\n\t/// Constructor.\n\tTiXmlDeclaration(\tconst std::string& _version,\n\t\t\t\t\t\tconst std::string& _encoding,\n\t\t\t\t\t\tconst std::string& _standalone );\n#endif\n\n\t/// Construct.\n\tTiXmlDeclaration(\tconst char* _version,\n\t\t\t\t\t\tconst char* _encoding,\n\t\t\t\t\t\tconst char* _standalone );\n\n\tTiXmlDeclaration( const TiXmlDeclaration& copy );\n\tvoid operator=( const TiXmlDeclaration& copy );\n\n\tvirtual ~TiXmlDeclaration()\t{}\n\n\t/// Version. Will return an empty string if none was found.\n\tconst char *Version() const\t\t\t{ return version.c_str (); }\n\t/// Encoding. Will return an empty string if none was found.\n\tconst char *Encoding() const\t\t{ return encoding.c_str (); }\n\t/// Is this a standalone document?\n\tconst char *Standalone() const\t\t{ return standalone.c_str (); }\n\n\t/// Creates a copy of this Declaration and returns it.\n\tTiXmlNode* Clone() const override;\n\t// Print this declaration to a FILE stream.\n\tvirtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;\n\tvoid Print( FILE* cfile, int depth ) const override\n    {\n\t\tPrint( cfile, depth, 0 );\n\t}\n\n\tconst char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) override;\n\n\tconst TiXmlDeclaration* ToDeclaration() const override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\tTiXmlDeclaration* ToDeclaration()override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\n\t/** Walk the XML tree visiting this node and all of its children. \n\t*/\n\tbool Accept( TiXmlVisitor* visitor ) const override;\n\nprotected:\n\tvoid CopyTo( TiXmlDeclaration* target ) const;\n\t// used to be public\n\t#ifdef TIXML_USE_STL\n\tvirtual void StreamIn( std::istream * in, TIXML_STRING * tag ) override;\n\t#endif\n\nprivate:\n\n\tTIXML_STRING version;\n\tTIXML_STRING encoding;\n\tTIXML_STRING standalone;\n};\n\n\n/** Any tag that tinyXml doesn't recognize is saved as an\n\tunknown. It is a tag of text, but should not be modified.\n\tIt will be written back to the XML, unchanged, when the file\n\tis saved.\n\n\tDTD tags get thrown into TiXmlUnknowns.\n*/\nclass TiXmlUnknown : public TiXmlNode\n{\npublic:\n\tTiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN )\t{}\n\tvirtual ~TiXmlUnknown() {}\n\n\tTiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN )\t\t{ copy.CopyTo( this ); }\n\tvoid operator=( const TiXmlUnknown& copy )\t\t\t\t\t\t\t\t\t\t{ copy.CopyTo( this ); }\n\n\t/// Creates a copy of this Unknown and returns it.\n\tTiXmlNode* Clone() const override;\n\t// Print this Unknown to a FILE stream.\n\tvoid Print( FILE* cfile, int depth ) const override;\n\n\tconst char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) override;\n\n\tconst TiXmlUnknown*\t ToUnknown() const override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\tTiXmlUnknown* ToUnknown()override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\n\t/** Walk the XML tree visiting this node and all of its children. \n\t*/\n\tbool Accept( TiXmlVisitor* content ) const override;\n\nprotected:\n\tvoid CopyTo( TiXmlUnknown* target ) const;\n\n\t#ifdef TIXML_USE_STL\n\tvirtual void StreamIn( std::istream * in, TIXML_STRING * tag ) override;\n\t#endif\n\nprivate:\n\n};\n\n\n/** Always the top level node. A document binds together all the\n\tXML pieces. It can be saved, loaded, and printed to the screen.\n\tThe 'value' of a document node is the xml file name.\n*/\nclass TiXmlDocument : public TiXmlNode\n{\npublic:\n\t/// Create an empty document, that has no name.\n\tTiXmlDocument();\n\t/// Create a document with a name. The name of the document is also the filename of the xml.\n\tTiXmlDocument( const char * documentName );\n\n\t#ifdef TIXML_USE_STL\n\t/// Constructor.\n\tTiXmlDocument( const std::string& documentName );\n\t#endif\n\n\tTiXmlDocument( const TiXmlDocument& copy );\n\tvoid operator=( const TiXmlDocument& copy );\n\n\tvirtual ~TiXmlDocument() {}\n\n\t/** Load a file using the current document value.\n\t\tReturns true if successful. Will delete any existing\n\t\tdocument data before loading.\n\t*/\n\tbool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );\n\t/// Save a file using the current document value. Returns true if successful.\n\tbool SaveFile() const;\n\t/// Load a file using the given filename. Returns true if successful.\n\tbool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );\n\t/// Save a file using the given filename. Returns true if successful.\n\tbool SaveFile( const char * filename ) const;\n\t/** Load a file using the given FILE*. Returns true if successful. Note that this method\n\t\tdoesn't stream - the entire object pointed at by the FILE*\n\t\twill be interpreted as an XML file. TinyXML doesn't stream in XML from the current\n\t\tfile location. Streaming may be added in the future.\n\t*/\n\tbool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );\n\t/// Save a file using the given FILE*. Returns true if successful.\n\tbool SaveFile( FILE* ) const;\n\n\t#ifdef TIXML_USE_STL\n\tbool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING )\t\t\t///< STL std::string version.\n\t{\n//\t\tStringToBuffer f( filename );\n//\t\treturn ( f.buffer && LoadFile( f.buffer, encoding ));\n\t\treturn LoadFile( filename.c_str(), encoding );\n\t}\n\tbool SaveFile( const std::string& filename ) const\t\t///< STL std::string version.\n\t{\n//\t\tStringToBuffer f( filename );\n//\t\treturn ( f.buffer && SaveFile( f.buffer ));\n\t\treturn SaveFile( filename.c_str() );\n\t}\n\t#endif\n\n\t/** Parse the given null terminated block of xml data. Passing in an encoding to this\n\t\tmethod (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml\n\t\tto use that encoding, regardless of what TinyXml might otherwise try to detect.\n\t*/\n\tconst char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) override;\n\n\t/** Get the root element -- the only top level element -- of the document.\n\t\tIn well formed XML, there should only be one. TinyXml is tolerant of\n\t\tmultiple elements at the document level.\n\t*/\n\tconst TiXmlElement* RootElement() const\t\t{ return FirstChildElement(); }\n\tTiXmlElement* RootElement()\t\t\t\t\t{ return FirstChildElement(); }\n\n\t/** If an error occurs, Error will be set to true. Also,\n\t\t- The ErrorId() will contain the integer identifier of the error (not generally useful)\n\t\t- The ErrorDesc() method will return the name of the error. (very useful)\n\t\t- The ErrorRow() and ErrorCol() will return the location of the error (if known)\n\t*/\t\n\tbool Error() const\t\t\t\t\t\t{ return error; }\n\n\t/// Contains a textual (english) description of the error if one occurs.\n\tconst char * ErrorDesc() const\t{ return errorDesc.c_str (); }\n\n\t/** Generally, you probably want the error string ( ErrorDesc() ). But if you\n\t\tprefer the ErrorId, this function will fetch it.\n\t*/\n\tint ErrorId()\tconst\t\t\t\t{ return errorId; }\n\n\t/** Returns the location (if known) of the error. The first column is column 1, \n\t\tand the first row is row 1. A value of 0 means the row and column wasn't applicable\n\t\t(memory errors, for example, have no row/column) or the parser lost the error. (An\n\t\terror in the error reporting, in that case.)\n\n\t\t@sa SetTabSize, Row, Column\n\t*/\n\tint ErrorRow() const\t{ return errorLocation.row+1; }\n\tint ErrorCol() const\t{ return errorLocation.col+1; }\t///< The column where the error occurred. See ErrorRow()\n\n\t/** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())\n\t\tto report the correct values for row and column. It does not change the output\n\t\tor input in any way.\n\t\t\n\t\tBy calling this method, with a tab size\n\t\tgreater than 0, the row and column of each node and attribute is stored\n\t\twhen the file is loaded. Very useful for tracking the DOM back in to\n\t\tthe source file.\n\n\t\tThe tab size is required for calculating the location of nodes. If not\n\t\tset, the default of 4 is used. The tabsize is set per document. Setting\n\t\tthe tabsize to 0 disables row/column tracking.\n\n\t\tNote that row and column tracking is not supported when using operator>>.\n\n\t\tThe tab size needs to be enabled before the parse or load. Correct usage:\n\t\t@verbatim\n\t\tTiXmlDocument doc;\n\t\tdoc.SetTabSize( 8 );\n\t\tdoc.Load( \"myfile.xml\" );\n\t\t@endverbatim\n\n\t\t@sa Row, Column\n\t*/\n\tvoid SetTabSize( int _tabsize )\t\t{ tabsize = _tabsize; }\n\n\tint TabSize() const\t{ return tabsize; }\n\n\t/** If you have handled the error, it can be reset with this call. The error\n\t\tstate is automatically cleared if you Parse a new XML block.\n\t*/\n\tvoid ClearError()\t\t\t\t\t\t{\terror = false; \n\t\t\t\t\t\t\t\t\t\t\t\terrorId = 0; \n\t\t\t\t\t\t\t\t\t\t\t\terrorDesc = \"\"; \n\t\t\t\t\t\t\t\t\t\t\t\terrorLocation.row = errorLocation.col = 0; \n\t\t\t\t\t\t\t\t\t\t\t\t//errorLocation.last = 0; \n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t/** Write the document to standard out using formatted printing (\"pretty print\"). */\n\tvoid Print() const\t\t\t\t\t\t{ Print( stdout, 0 ); }\n\n\t/* Write the document to a string using formatted printing (\"pretty print\"). This\n\t\twill allocate a character array (new char[]) and return it as a pointer. The\n\t\tcalling code pust call delete[] on the return char* to avoid a memory leak.\n\t*/\n\t//char* PrintToMemory() const; \n\n\t/// Print this Document to a FILE stream.\n\tvoid Print( FILE* cfile, int depth = 0 ) const override;\n\t// [internal use]\n\tvoid SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );\n\n\tconst TiXmlDocument* ToDocument() const override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\tTiXmlDocument*\tToDocument() override { return this; } ///< Cast to a more defined type. Will return null not of the requested type.\n\n\t/** Walk the XML tree visiting this node and all of its children. \n\t*/\n\tbool Accept( TiXmlVisitor* content ) const override;\n\nprotected :\n\t// [internal use]\n\tTiXmlNode* Clone() const override;\n\t#ifdef TIXML_USE_STL\n\tvirtual void StreamIn( std::istream * in, TIXML_STRING * tag ) override;\n\t#endif\n\nprivate:\n\tvoid CopyTo( TiXmlDocument* target ) const;\n\n\tbool error;\n\tint  errorId;\n\tTIXML_STRING errorDesc;\n\tint tabsize;\n\tTiXmlCursor errorLocation;\n\tbool useMicrosoftBOM;\t\t// the UTF-8 BOM were found when read. Note this, and try to write.\n};\n\n\n/**\n\tA TiXmlHandle is a class that wraps a node pointer with null checks; this is\n\tan incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml\n\tDOM structure. It is a separate utility class.\n\n\tTake an example:\n\t@verbatim\n\t<Document>\n\t\t<Element attributeA = \"valueA\">\n\t\t\t<Child attributeB = \"value1\" />\n\t\t\t<Child attributeB = \"value2\" />\n\t\t</Element>\n\t<Document>\n\t@endverbatim\n\n\tAssuming you want the value of \"attributeB\" in the 2nd \"Child\" element, it's very \n\teasy to write a *lot* of code that looks like:\n\n\t@verbatim\n\tTiXmlElement* root = document.FirstChildElement( \"Document\" );\n\tif ( root )\n\t{\n\t\tTiXmlElement* element = root->FirstChildElement( \"Element\" );\n\t\tif ( element )\n\t\t{\n\t\t\tTiXmlElement* child = element->FirstChildElement( \"Child\" );\n\t\t\tif ( child )\n\t\t\t{\n\t\t\t\tTiXmlElement* child2 = child->NextSiblingElement( \"Child\" );\n\t\t\t\tif ( child2 )\n\t\t\t\t{\n\t\t\t\t\t// Finally do something useful.\n\t@endverbatim\n\n\tAnd that doesn't even cover \"else\" cases. TiXmlHandle addresses the verbosity\n\tof such code. A TiXmlHandle checks for null\tpointers so it is perfectly safe \n\tand correct to use:\n\n\t@verbatim\n\tTiXmlHandle docHandle( &document );\n\tTiXmlElement* child2 = docHandle.FirstChild( \"Document\" ).FirstChild( \"Element\" ).Child( \"Child\", 1 ).ToElement();\n\tif ( child2 )\n\t{\n\t\t// do something useful\n\t@endverbatim\n\n\tWhich is MUCH more concise and useful.\n\n\tIt is also safe to copy handles - internally they are nothing more than node pointers.\n\t@verbatim\n\tTiXmlHandle handleCopy = handle;\n\t@endverbatim\n\n\tWhat they should not be used for is iteration:\n\n\t@verbatim\n\tint i=0; \n\twhile ( true )\n\t{\n\t\tTiXmlElement* child = docHandle.FirstChild( \"Document\" ).FirstChild( \"Element\" ).Child( \"Child\", i ).ToElement();\n\t\tif ( !child )\n\t\t\tbreak;\n\t\t// do something\n\t\t++i;\n\t}\n\t@endverbatim\n\n\tIt seems reasonable, but it is in fact two embedded while loops. The Child method is \n\ta linear walk to find the element, so this code would iterate much more than it needs \n\tto. Instead, prefer:\n\n\t@verbatim\n\tTiXmlElement* child = docHandle.FirstChild( \"Document\" ).FirstChild( \"Element\" ).FirstChild( \"Child\" ).ToElement();\n\n\tfor( child; child; child=child->NextSiblingElement() )\n\t{\n\t\t// do something\n\t}\n\t@endverbatim\n*/\nclass TiXmlHandle\n{\npublic:\n\t/// Create a handle from any node (at any depth of the tree.) This can be a null pointer.\n\tTiXmlHandle( TiXmlNode* _node )\t\t\t\t\t{ this->node = _node; }\n\t/// Copy constructor\n\tTiXmlHandle( const TiXmlHandle& ref )\t\t\t{ this->node = ref.node; }\n\tTiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; }\n\n\t/// Return a handle to the first child node.\n\tTiXmlHandle FirstChild() const;\n\t/// Return a handle to the first child node with the given name.\n\tTiXmlHandle FirstChild( const char * value ) const;\n\t/// Return a handle to the first child element.\n\tTiXmlHandle FirstChildElement() const;\n\t/// Return a handle to the first child element with the given name.\n\tTiXmlHandle FirstChildElement( const char * value ) const;\n\n\t/** Return a handle to the \"index\" child with the given name. \n\t\tThe first child is 0, the second 1, etc.\n\t*/\n\tTiXmlHandle Child( const char* value, int index ) const;\n\t/** Return a handle to the \"index\" child. \n\t\tThe first child is 0, the second 1, etc.\n\t*/\n\tTiXmlHandle Child( int index ) const;\n\t/** Return a handle to the \"index\" child element with the given name. \n\t\tThe first child element is 0, the second 1, etc. Note that only TiXmlElements\n\t\tare indexed: other types are not counted.\n\t*/\n\tTiXmlHandle ChildElement( const char* value, int index ) const;\n\t/** Return a handle to the \"index\" child element. \n\t\tThe first child element is 0, the second 1, etc. Note that only TiXmlElements\n\t\tare indexed: other types are not counted.\n\t*/\n\tTiXmlHandle ChildElement( int index ) const;\n\n\t#ifdef TIXML_USE_STL\n\tTiXmlHandle FirstChild( const std::string& _value ) const\t\t\t\t{ return FirstChild( _value.c_str() ); }\n\tTiXmlHandle FirstChildElement( const std::string& _value ) const\t\t{ return FirstChildElement( _value.c_str() ); }\n\n\tTiXmlHandle Child( const std::string& _value, int index ) const\t\t\t{ return Child( _value.c_str(), index ); }\n\tTiXmlHandle ChildElement( const std::string& _value, int index ) const\t{ return ChildElement( _value.c_str(), index ); }\n\t#endif\n\n\t/** Return the handle as a TiXmlNode. This may return null.\n\t*/\n\tTiXmlNode* ToNode() const\t\t\t{ return node; } \n\t/** Return the handle as a TiXmlElement. This may return null.\n\t*/\n\tTiXmlElement* ToElement() const\t\t{ return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }\n\t/**\tReturn the handle as a TiXmlText. This may return null.\n\t*/\n\tTiXmlText* ToText() const\t\t\t{ return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }\n\t/** Return the handle as a TiXmlUnknown. This may return null.\n\t*/\n\tTiXmlUnknown* ToUnknown() const\t\t{ return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }\n\n\t/** @deprecated use ToNode. \n\t\tReturn the handle as a TiXmlNode. This may return null.\n\t*/\n\tTiXmlNode* Node() const\t\t\t{ return ToNode(); } \n\t/** @deprecated use ToElement. \n\t\tReturn the handle as a TiXmlElement. This may return null.\n\t*/\n\tTiXmlElement* Element() const\t{ return ToElement(); }\n\t/**\t@deprecated use ToText()\n\t\tReturn the handle as a TiXmlText. This may return null.\n\t*/\n\tTiXmlText* Text() const\t\t\t{ return ToText(); }\n\t/** @deprecated use ToUnknown()\n\t\tReturn the handle as a TiXmlUnknown. This may return null.\n\t*/\n\tTiXmlUnknown* Unknown() const\t{ return ToUnknown(); }\n\nprivate:\n\tTiXmlNode* node;\n};\n\n\n/** Print to memory functionality. The TiXmlPrinter is useful when you need to:\n\n\t-# Print to memory (especially in non-STL mode)\n\t-# Control formatting (line endings, etc.)\n\n\tWhen constructed, the TiXmlPrinter is in its default \"pretty printing\" mode.\n\tBefore calling Accept() you can call methods to control the printing\n\tof the XML document. After TiXmlNode::Accept() is called, the printed document can\n\tbe accessed via the CStr(), Str(), and Size() methods.\n\n\tTiXmlPrinter uses the Visitor API.\n\t@verbatim\n\tTiXmlPrinter printer;\n\tprinter.SetIndent( \"\\t\" );\n\n\tdoc.Accept( &printer );\n\tfprintf( stdout, \"%s\", printer.CStr() );\n\t@endverbatim\n*/\nclass TiXmlPrinter : public TiXmlVisitor\n{\npublic:\n\tTiXmlPrinter() : depth( 0 ), simpleTextPrint( false ),\n\t\t\t\t\t buffer(), indent( \"\t\" ), lineBreak( \"\\n\" ) {}\n\n\tbool VisitEnter( const TiXmlDocument& doc ) override;\n\tbool VisitExit( const TiXmlDocument& doc ) override;\n\n\tbool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) override;\n\tbool VisitExit( const TiXmlElement& element ) override;\n\n\tbool Visit( const TiXmlDeclaration& declaration ) override;\n\tbool Visit( const TiXmlText& text ) override;\n\tbool Visit( const TiXmlComment& comment ) override;\n\tbool Visit( const TiXmlUnknown& unknown ) override;\n\n\t/** Set the indent characters for printing. By default 4 spaces\n\t\tbut tab (\\t) is also useful, or null/empty string for no indentation.\n\t*/\n\tvoid SetIndent( const char* _indent )\t\t\t{ indent = _indent ? _indent : \"\" ; }\n\t/// Query the indention string.\n\tconst char* Indent()\t\t\t\t\t\t\t{ return indent.c_str(); }\n\t/** Set the line breaking string. By default set to newline (\\n). \n\t\tSome operating systems prefer other characters, or can be\n\t\tset to the null/empty string for no indenation.\n\t*/\n\tvoid SetLineBreak( const char* _lineBreak )\t\t{ lineBreak = _lineBreak ? _lineBreak : \"\"; }\n\t/// Query the current line breaking string.\n\tconst char* LineBreak()\t\t\t\t\t\t\t{ return lineBreak.c_str(); }\n\n\t/** Switch over to \"stream printing\" which is the most dense formatting without \n\t\tlinebreaks. Common when the XML is needed for network transmission.\n\t*/\n\tvoid SetStreamPrinting()\t\t\t\t\t\t{ indent = \"\";\n\t\t\t\t\t\t\t\t\t\t\t\t\t  lineBreak = \"\";\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\t\n\t/// Return the result.\n\tconst char* CStr()\t\t\t\t\t\t\t\t{ return buffer.c_str(); }\n\t/// Return the length of the result string.\n\tsize_t Size()\t\t\t\t\t\t\t\t\t{ return buffer.size(); }\n\n\t#ifdef TIXML_USE_STL\n\t/// Return the result.\n\tconst std::string& Str()\t\t\t\t\t\t{ return buffer; }\n\t#endif\n\nprivate:\n\tvoid DoIndent()\t{\n\t\tfor( int i=0; i<depth; ++i )\n\t\t\tbuffer += indent;\n\t}\n\tvoid DoLineBreak() {\n\t\tbuffer += lineBreak;\n\t}\n\n\tint depth;\n\tbool simpleTextPrint;\n\tTIXML_STRING buffer;\n\tTIXML_STRING indent;\n\tTIXML_STRING lineBreak;\n};\n\n\n#ifdef _MSC_VER\n#pragma warning( pop )\n#endif\n\n#endif\n\n"
  },
  {
    "path": "extern/oics/tinyxmlerror.cpp",
    "content": "/*\nwww.sourceforge.net/projects/tinyxml\nOriginal code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)\n\nThis software is provided 'as-is', without any express or implied \nwarranty. In no event will the authors be held liable for any \ndamages arising from the use of this software.\n\nPermission is granted to anyone to use this software for any \npurpose, including commercial applications, and to alter it and \nredistribute it freely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must\nnot claim that you wrote the original software. If you use this\nsoftware in a product, an acknowledgment in the product documentation\nwould be appreciated but is not required.\n\n2. Altered source versions must be plainly marked as such, and\nmust not be misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source\ndistribution.\n*/\n\n#include \"tinyxml.h\"\n\n// The goal of the seperate error file is to make the first\n// step towards localization. tinyxml (currently) only supports\n// english error messages, but the could now be translated.\n//\n// It also cleans up the code a bit.\n//\n\nconst char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =\n{\n\t\"No error\",\n\t\"Error\",\n\t\"Failed to open file\",\n\t\"Memory allocation failed.\",\n\t\"Error parsing Element.\",\n\t\"Failed to read Element name\",\n\t\"Error reading Element value.\",\n\t\"Error reading Attributes.\",\n\t\"Error: empty tag.\",\n\t\"Error reading end tag.\",\n\t\"Error parsing Unknown.\",\n\t\"Error parsing Comment.\",\n\t\"Error parsing Declaration.\",\n\t\"Error document empty.\",\n\t\"Error null (0) or unexpected EOF found in input stream.\",\n\t\"Error parsing CDATA.\",\n\t\"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.\",\n};\n"
  },
  {
    "path": "extern/oics/tinyxmlparser.cpp",
    "content": "/*\nwww.sourceforge.net/projects/tinyxml\nOriginal code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any\ndamages arising from the use of this software.\n\nPermission is granted to anyone to use this software for any\npurpose, including commercial applications, and to alter it and\nredistribute it freely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must\nnot claim that you wrote the original software. If you use this\nsoftware in a product, an acknowledgment in the product documentation\nwould be appreciated but is not required.\n\n2. Altered source versions must be plainly marked as such, and\nmust not be misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source\ndistribution.\n*/\n\n#include <ctype.h>\n#include <stddef.h>\n\n#include \"tinyxml.h\"\n\n//#define DEBUG_PARSER\n#if defined( DEBUG_PARSER )\n#\tif defined( DEBUG ) && defined( _MSC_VER )\n#\t\tinclude <windows.h>\n#\t\tdefine TIXML_LOG OutputDebugString\n#\telse\n#\t\tdefine TIXML_LOG printf\n#\tendif\n#endif\n\n// Note tha \"PutString\" hardcodes the same list. This\n// is less flexible than it appears. Changing the entries\n// or order will break putstring.\nTiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] =\n{\n\t{ \"&amp;\",  5, '&' },\n\t{ \"&lt;\",   4, '<' },\n\t{ \"&gt;\",   4, '>' },\n\t{ \"&quot;\", 6, '\\\"' },\n\t{ \"&apos;\", 6, '\\'' }\n};\n\n// Bunch of unicode info at:\n//\t\thttps://www.unicode.org/faq/utf_bom.html\n// Including the basic of this table, which determines the #bytes in the\n// sequence from the lead byte. 1 placed for invalid sequences --\n// although the result will be junk, pass it through as much as possible.\n// Beware of the non-characters in UTF-8:\n//\t\t\t\tef bb bf (Microsoft \"lead bytes\")\n//\t\t\t\tef bf be\n//\t\t\t\tef bf bf\n\nconst unsigned char TIXML_UTF_LEAD_0 = 0xefU;\nconst unsigned char TIXML_UTF_LEAD_1 = 0xbbU;\nconst unsigned char TIXML_UTF_LEAD_2 = 0xbfU;\n\nconst int TiXmlBase::utf8ByteTable[256] =\n{\n\t//\t0\t1\t2\t3\t4\t5\t6\t7\t8\t9\ta\tb\tc\td\te\tf\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x00\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x10\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x20\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x30\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x40\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x50\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x60\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x70\tEnd of ASCII range\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x80 0x80 to 0xc1 invalid\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0x90\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0xa0\n\t\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t// 0xb0\n\t\t1,\t1,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t// 0xc0 0xc2 to 0xdf 2 byte\n\t\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t2,\t// 0xd0\n\t\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t3,\t// 0xe0 0xe0 to 0xef 3 byte\n\t\t4,\t4,\t4,\t4,\t4,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1,\t1\t// 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid\n};\n\n\nvoid TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )\n{\n\tconst unsigned long BYTE_MASK = 0xBF;\n\tconst unsigned long BYTE_MARK = 0x80;\n\tconst unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };\n\n\tif (input < 0x80)\n\t\t*length = 1;\n\telse if ( input < 0x800 )\n\t\t*length = 2;\n\telse if ( input < 0x10000 )\n\t\t*length = 3;\n\telse if ( input < 0x200000 )\n\t\t*length = 4;\n\telse\n\t\t{ *length = 0; return; }\t// This code won't covert this correctly anyway.\n\n\toutput += *length;\n\n    int lengthLeft = *length;\n    while (lengthLeft > 1)\n    {\n        --output;\n        *output = (char)((input | BYTE_MARK) & BYTE_MASK);\n        input >>= 6;\n        --lengthLeft;\n    }\n\n    --output;\n    *output = (char)(input | FIRST_BYTE_MARK[*length]);\n}\n\n\n/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ )\n{\n\t// This will only work for low-ascii, everything else is assumed to be a valid\n\t// letter. I'm not sure this is the best approach, but it is quite tricky trying\n\t// to figure out alhabetical vs. not across encoding. So take a very\n\t// conservative approach.\n\n//\tif ( encoding == TIXML_ENCODING_UTF8 )\n//\t{\n\t\tif ( anyByte < 127 )\n\t\t\treturn isalpha( anyByte );\n\t\telse\n\t\t\treturn 1;\t// What else to do? The unicode set is huge...get the english ones right.\n//\t}\n//\telse\n//\t{\n//\t\treturn isalpha( anyByte );\n//\t}\n}\n\n\n/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ )\n{\n\t// This will only work for low-ascii, everything else is assumed to be a valid\n\t// letter. I'm not sure this is the best approach, but it is quite tricky trying\n\t// to figure out alhabetical vs. not across encoding. So take a very\n\t// conservative approach.\n\n//\tif ( encoding == TIXML_ENCODING_UTF8 )\n//\t{\n\t\tif ( anyByte < 127 )\n\t\t\treturn isalnum( anyByte );\n\t\telse\n\t\t\treturn 1;\t// What else to do? The unicode set is huge...get the english ones right.\n//\t}\n//\telse\n//\t{\n//\t\treturn isalnum( anyByte );\n//\t}\n}\n\n\nclass TiXmlParsingData\n{\n\tfriend class TiXmlDocument;\n  public:\n\tvoid Stamp( const char* now, TiXmlEncoding encoding );\n\n\tconst TiXmlCursor& Cursor()\t{ return cursor; }\n\n  private:\n\t// Only used by the document!\n\tTiXmlParsingData( const char* start, int _tabsize, int row, int col )\n\t{\n\t\tassert( start );\n\t\tstamp = start;\n\t\ttabsize = _tabsize;\n\t\tcursor.row = row;\n\t\tcursor.col = col;\n\t}\n\n\tTiXmlCursor\t\tcursor;\n\tconst char*\t\tstamp;\n\tint\t\t\t\ttabsize;\n};\n\n\nvoid TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding )\n{\n\tassert( now );\n\n\t// Do nothing if the tabsize is 0.\n\tif ( tabsize < 1 )\n\t{\n\t\treturn;\n\t}\n\n\t// Get the current row, column.\n\tint row = cursor.row;\n\tint col = cursor.col;\n\tconst char* p = stamp;\n\tassert( p );\n\n\twhile ( p < now )\n\t{\n\t\t// Treat p as unsigned, so we have a happy compiler.\n\t\tconst unsigned char* pU = (const unsigned char*)p;\n\n\t\t// Code contributed by Fletcher Dunn: (modified by lee)\n\t\tswitch (*pU) {\n\t\t\tcase 0:\n\t\t\t\t// We *should* never get here, but in case we do, don't\n\t\t\t\t// advance past the terminating null character, ever\n\t\t\t\treturn;\n\n\t\t\tcase '\\r':\n\t\t\t\t// bump down to the next line\n\t\t\t\t++row;\n\t\t\t\tcol = 0;\n\t\t\t\t// Eat the character\n\t\t\t\t++p;\n\n\t\t\t\t// Check for \\r\\n sequence, and treat this as a single character\n\t\t\t\tif (*p == '\\n') {\n\t\t\t\t\t++p;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase '\\n':\n\t\t\t\t// bump down to the next line\n\t\t\t\t++row;\n\t\t\t\tcol = 0;\n\n\t\t\t\t// Eat the character\n\t\t\t\t++p;\n\n\t\t\t\t// Check for \\n\\r sequence, and treat this as a single\n\t\t\t\t// character.  (Yes, this bizarre thing does occur still\n\t\t\t\t// on some arcane platforms...)\n\t\t\t\tif (*p == '\\r') {\n\t\t\t\t\t++p;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase '\\t':\n\t\t\t\t// Eat the character\n\t\t\t\t++p;\n\n\t\t\t\t// Skip to next tab stop\n\t\t\t\tcol = (col / tabsize + 1) * tabsize;\n\t\t\t\tbreak;\n\n\t\t\tcase TIXML_UTF_LEAD_0:\n\t\t\t\tif ( encoding == TIXML_ENCODING_UTF8 )\n\t\t\t\t{\n\t\t\t\t\tif ( *(p+1) && *(p+2) )\n\t\t\t\t\t{\n\t\t\t\t\t\t// In these cases, don't advance the column. These are\n\t\t\t\t\t\t// 0-width spaces.\n\t\t\t\t\t\tif ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 )\n\t\t\t\t\t\t\tp += 3;\n\t\t\t\t\t\telse if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU )\n\t\t\t\t\t\t\tp += 3;\n\t\t\t\t\t\telse if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU )\n\t\t\t\t\t\t\tp += 3;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{ p +=3; ++col; }\t// A normal character.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t++p;\n\t\t\t\t\t++col;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tif ( encoding == TIXML_ENCODING_UTF8 )\n\t\t\t\t{\n\t\t\t\t\t// Eat the 1 to 4 byte utf8 character.\n\t\t\t\t\tint step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)];\n\t\t\t\t\tif ( step == 0 )\n\t\t\t\t\t\tstep = 1;\t\t// Error case from bad encoding, but handle gracefully.\n\t\t\t\t\tp += step;\n\n\t\t\t\t\t// Just advance one column, of course.\n\t\t\t\t\t++col;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t++p;\n\t\t\t\t\t++col;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tcursor.row = row;\n\tcursor.col = col;\n\tassert( cursor.row >= -1 );\n\tassert( cursor.col >= -1 );\n\tstamp = p;\n\tassert( stamp );\n}\n\n\nconst char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )\n{\n\tif ( !p || !*p )\n\t{\n\t\treturn 0;\n\t}\n\tif ( encoding == TIXML_ENCODING_UTF8 )\n\t{\n\t\twhile ( *p )\n\t\t{\n\t\t\tconst unsigned char* pU = (const unsigned char*)p;\n\n\t\t\t// Skip the stupid Microsoft UTF-8 Byte order marks\n\t\t\tif (\t*(pU+0)==TIXML_UTF_LEAD_0\n\t\t\t\t && *(pU+1)==TIXML_UTF_LEAD_1\n\t\t\t\t && *(pU+2)==TIXML_UTF_LEAD_2 )\n\t\t\t{\n\t\t\t\tp += 3;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if(*(pU+0)==TIXML_UTF_LEAD_0\n\t\t\t\t && *(pU+1)==0xbfU\n\t\t\t\t && *(pU+2)==0xbeU )\n\t\t\t{\n\t\t\t\tp += 3;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if(*(pU+0)==TIXML_UTF_LEAD_0\n\t\t\t\t && *(pU+1)==0xbfU\n\t\t\t\t && *(pU+2)==0xbfU )\n\t\t\t{\n\t\t\t\tp += 3;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( IsWhiteSpace( *p ) || *p == '\\n' || *p =='\\r' )\t\t// Still using old rules for white space.\n\t\t\t\t++p;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t{\n\t\twhile ( (*p && IsWhiteSpace( *p )) || *p == '\\n' || *p =='\\r' )\n\t\t\t++p;\n\t}\n\n\treturn p;\n}\n\n#ifdef TIXML_USE_STL\n/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag )\n{\n\tfor( ;; )\n\t{\n\t\tif ( !in->good() ) return false;\n\n\t\tint c = in->peek();\n\t\t// At this scope, we can't get to a document. So fail silently.\n\t\tif ( !IsWhiteSpace( c ) || c <= 0 )\n\t\t\treturn true;\n\n\t\t*tag += (char) in->get();\n\t}\n}\n\n/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag )\n{\n\t//assert( character > 0 && character < 128 );\t// else it won't work in utf-8\n\twhile ( in->good() )\n\t{\n\t\tint c = in->peek();\n\t\tif ( c == character )\n\t\t\treturn true;\n\t\tif ( c <= 0 )\t\t// Silent failure: can't get document at this scope\n\t\t\treturn false;\n\n\t\tin->get();\n\t\t*tag += (char) c;\n\t}\n\treturn false;\n}\n#endif\n\n// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The\n// \"assign\" optimization removes over 10% of the execution time.\n//\nconst char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )\n{\n\t// Oddly, not supported on some comilers,\n\t//name->clear();\n\t// So use this:\n\t*name = \"\";\n\tassert( p );\n\n\t// Names start with letters or underscores.\n\t// Of course, in unicode, tinyxml has no idea what a letter *is*. The\n\t// algorithm is generous.\n\t//\n\t// After that, they can be letters, underscores, numbers,\n\t// hyphens, or colons. (Colons are valid ony for namespaces,\n\t// but tinyxml can't tell namespaces from names.)\n\tif (\tp && *p\n\t\t && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )\n\t{\n\t\tconst char* start = p;\n\t\twhile(\t\tp && *p\n\t\t\t\t&&\t(\t\tIsAlphaNum( (unsigned char ) *p, encoding )\n\t\t\t\t\t\t || *p == '_'\n\t\t\t\t\t\t || *p == '-'\n\t\t\t\t\t\t || *p == '.'\n\t\t\t\t\t\t || *p == ':' ) )\n\t\t{\n\t\t\t//(*name) += *p; // expensive\n\t\t\t++p;\n\t\t}\n\t\tif ( p-start > 0 ) {\n\t\t\tname->assign( start, p-start );\n\t\t}\n\t\treturn p;\n\t}\n\treturn 0;\n}\n\nconst char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding )\n{\n\t// Presume an entity, and pull it out.\n\tTIXML_STRING ent;\n\tint i;\n\t*length = 0;\n\n\tif ( *(p+1) && *(p+1) == '#' && *(p+2) )\n\t{\n\t\tunsigned long ucs = 0;\n\t\tptrdiff_t delta = 0;\n\t\tunsigned mult = 1;\n\n\t\tif ( *(p+2) == 'x' )\n\t\t{\n\t\t\t// Hexadecimal.\n\t\t\tif ( !*(p+3) ) return 0;\n\n\t\t\tconst char* q = p+3;\n\t\t\tq = strchr( q, ';' );\n\n\t\t\tif ( !q || !*q ) return 0;\n\n\t\t\tdelta = q-p;\n\t\t\t--q;\n\n\t\t\twhile ( *q != 'x' )\n\t\t\t{\n\t\t\t\tif ( *q >= '0' && *q <= '9' )\n\t\t\t\t\tucs += mult * (*q - '0');\n\t\t\t\telse if ( *q >= 'a' && *q <= 'f' )\n\t\t\t\t\tucs += mult * (*q - 'a' + 10);\n\t\t\t\telse if ( *q >= 'A' && *q <= 'F' )\n\t\t\t\t\tucs += mult * (*q - 'A' + 10 );\n\t\t\t\telse\n\t\t\t\t\treturn 0;\n\t\t\t\tmult *= 16;\n\t\t\t\t--q;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Decimal.\n\t\t\tif ( !*(p+2) ) return 0;\n\n\t\t\tconst char* q = p+2;\n\t\t\tq = strchr( q, ';' );\n\n\t\t\tif ( !q || !*q ) return 0;\n\n\t\t\tdelta = q-p;\n\t\t\t--q;\n\n\t\t\twhile ( *q != '#' )\n\t\t\t{\n\t\t\t\tif ( *q >= '0' && *q <= '9' )\n\t\t\t\t\tucs += mult * (*q - '0');\n\t\t\t\telse\n\t\t\t\t\treturn 0;\n\t\t\t\tmult *= 10;\n\t\t\t\t--q;\n\t\t\t}\n\t\t}\n\t\tif ( encoding == TIXML_ENCODING_UTF8 )\n\t\t{\n\t\t\t// convert the UCS to UTF-8\n\t\t\tConvertUTF32ToUTF8( ucs, value, length );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*value = (char)ucs;\n\t\t\t*length = 1;\n\t\t}\n\t\treturn p + delta + 1;\n\t}\n\n\t// Now try to match it.\n\tfor( i=0; i<NUM_ENTITY; ++i )\n\t{\n\t\tif ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )\n\t\t{\n\t\t\tassert( strlen( entity[i].str ) == entity[i].strLength );\n\t\t\t*value = entity[i].chr;\n\t\t\t*length = 1;\n\t\t\treturn ( p + entity[i].strLength );\n\t\t}\n\t}\n\n\t// So it wasn't an entity, its unrecognized, or something like that.\n\t*value = *p;\t// Don't put back the last one, since we return it!\n\t//*length = 1;\t// Leave unrecognized entities - this doesn't really work.\n\t\t\t\t\t// Just writes strange XML.\n\treturn p+1;\n}\n\n\nbool TiXmlBase::StringEqual( const char* p,\n\t\t\t\t\t\t\t const char* tag,\n\t\t\t\t\t\t\t bool ignoreCase,\n\t\t\t\t\t\t\t TiXmlEncoding encoding )\n{\n\tassert( p );\n\tassert( tag );\n\tif ( !p || !*p )\n\t{\n\t\tassert( 0 );\n\t\treturn false;\n\t}\n\n\tconst char* q = p;\n\n\tif ( ignoreCase )\n\t{\n\t\twhile ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )\n\t\t{\n\t\t\t++q;\n\t\t\t++tag;\n\t\t}\n\n\t\tif ( *tag == 0 )\n\t\t\treturn true;\n\t}\n\telse\n\t{\n\t\twhile ( *q && *tag && *q == *tag )\n\t\t{\n\t\t\t++q;\n\t\t\t++tag;\n\t\t}\n\n\t\tif ( *tag == 0 )\t\t// Have we found the end of the tag, and everything equal?\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nconst char* TiXmlBase::ReadText(\tconst char* p,\n\t\t\t\t\t\t\t\t\tTIXML_STRING * text,\n\t\t\t\t\t\t\t\t\tbool trimWhiteSpace,\n\t\t\t\t\t\t\t\t\tconst char* endTag,\n\t\t\t\t\t\t\t\t\tbool caseInsensitive,\n\t\t\t\t\t\t\t\t\tTiXmlEncoding encoding )\n{\n\t*text = \"\";\n\tif (\t!trimWhiteSpace\t\t\t// certain tags always keep whitespace\n\t\t || !condenseWhiteSpace )\t// if true, whitespace is always kept\n\t{\n\t\t// Keep all the white space.\n\t\twhile (\t   p && *p\n\t\t\t\t&& !StringEqual( p, endTag, caseInsensitive, encoding )\n\t\t\t  )\n\t\t{\n\t\t\tint len;\n\t\t\tchar cArr[4] = { 0, 0, 0, 0 };\n\t\t\tp = GetChar( p, cArr, &len, encoding );\n\t\t\ttext->append( cArr, len );\n\t\t}\n\t}\n\telse\n\t{\n\t\tbool whitespace = false;\n\n\t\t// Remove leading white space:\n\t\tp = SkipWhiteSpace( p, encoding );\n\t\twhile (\t   p && *p\n\t\t\t\t&& !StringEqual( p, endTag, caseInsensitive, encoding ) )\n\t\t{\n\t\t\tif ( *p == '\\r' || *p == '\\n' )\n\t\t\t{\n\t\t\t\twhitespace = true;\n\t\t\t\t++p;\n\t\t\t}\n\t\t\telse if ( IsWhiteSpace( *p ) )\n\t\t\t{\n\t\t\t\twhitespace = true;\n\t\t\t\t++p;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// If we've found whitespace, add it before the\n\t\t\t\t// new character. Any whitespace just becomes a space.\n\t\t\t\tif ( whitespace )\n\t\t\t\t{\n\t\t\t\t\t(*text) += ' ';\n\t\t\t\t\twhitespace = false;\n\t\t\t\t}\n\t\t\t\tint len;\n\t\t\t\tchar cArr[4] = { 0, 0, 0, 0 };\n\t\t\t\tp = GetChar( p, cArr, &len, encoding );\n\t\t\t\tif ( len == 1 )\n\t\t\t\t\t(*text) += cArr[0];\t// more efficient\n\t\t\t\telse\n\t\t\t\t\ttext->append( cArr, len );\n\t\t\t}\n\t\t}\n\t}\n\tif ( p )\n\t\tp += strlen( endTag );\n\treturn p;\n}\n\n#ifdef TIXML_USE_STL\n\nvoid TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag )\n{\n\t// The basic issue with a document is that we don't know what we're\n\t// streaming. Read something presumed to be a tag (and hope), then\n\t// identify it, and call the appropriate stream method on the tag.\n\t//\n\t// This \"pre-streaming\" will never read the closing \">\" so the\n\t// sub-tag can orient itself.\n\n\tif ( !StreamTo( in, '<', tag ) )\n\t{\n\t\tSetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn;\n\t}\n\n\twhile ( in->good() )\n\t{\n\t\tint tagIndex = (int) tag->length();\n\t\twhile ( in->good() && in->peek() != '>' )\n\t\t{\n\t\t\tint c = in->get();\n\t\t\tif ( c <= 0 )\n\t\t\t{\n\t\t\t\tSetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t(*tag) += (char) c;\n\t\t}\n\n\t\tif ( in->good() )\n\t\t{\n\t\t\t// We now have something we presume to be a node of\n\t\t\t// some sort. Identify it, and call the node to\n\t\t\t// continue streaming.\n\t\t\tTiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );\n\n\t\t\tif ( node )\n\t\t\t{\n\t\t\t\tnode->StreamIn( in, tag );\n\t\t\t\tbool isElement = node->ToElement() != 0;\n\t\t\t\tdelete node;\n\t\t\t\tnode = 0;\n\n\t\t\t\t// If this is the root element, we're done. Parsing will be\n\t\t\t\t// done by the >> operator.\n\t\t\t\tif ( isElement )\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\t// We should have returned sooner.\n\tSetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );\n}\n\n#endif\n\nconst char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )\n{\n\tClearError();\n\n\t// Parse away, at the document level. Since a document\n\t// contains nothing but other tags, most of what happens\n\t// here is skipping white space.\n\tif ( !p || !*p )\n\t{\n\t\tSetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn 0;\n\t}\n\n\t// Note that, for a document, this needs to come\n\t// before the while space skip, so that parsing\n\t// starts from the pointer we are given.\n\tlocation.Clear();\n\tif ( prevData )\n\t{\n\t\tlocation.row = prevData->cursor.row;\n\t\tlocation.col = prevData->cursor.col;\n\t}\n\telse\n\t{\n\t\tlocation.row = 0;\n\t\tlocation.col = 0;\n\t}\n\tTiXmlParsingData data( p, TabSize(), location.row, location.col );\n\tlocation = data.Cursor();\n\n\tif ( encoding == TIXML_ENCODING_UNKNOWN )\n\t{\n\t\t// Check for the Microsoft UTF-8 lead bytes.\n\t\tconst unsigned char* pU = (const unsigned char*)p;\n\t\tif (\t*(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0\n\t\t\t && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1\n\t\t\t && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )\n\t\t{\n\t\t\tencoding = TIXML_ENCODING_UTF8;\n\t\t\tuseMicrosoftBOM = true;\n\t\t}\n\t}\n\n\tp = SkipWhiteSpace( p, encoding );\n\tif ( !p )\n\t{\n\t\tSetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\treturn 0;\n\t}\n\n\twhile ( p && *p )\n\t{\n\t\tTiXmlNode* node = Identify( p, encoding );\n\t\tif ( node )\n\t\t{\n\t\t\tp = node->Parse( p, &data, encoding );\n\t\t\tLinkEndChild( node );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t// Did we get encoding info?\n\t\tif (\tencoding == TIXML_ENCODING_UNKNOWN\n\t\t\t && node->ToDeclaration() )\n\t\t{\n\t\t\tTiXmlDeclaration* dec = node->ToDeclaration();\n\t\t\tconst char* enc = dec->Encoding();\n\t\t\tassert( enc );\n\n\t\t\tif ( *enc == 0 )\n\t\t\t\tencoding = TIXML_ENCODING_UTF8;\n\t\t\telse if ( StringEqual( enc, \"UTF-8\", true, TIXML_ENCODING_UNKNOWN ) )\n\t\t\t\tencoding = TIXML_ENCODING_UTF8;\n\t\t\telse if ( StringEqual( enc, \"UTF8\", true, TIXML_ENCODING_UNKNOWN ) )\n\t\t\t\tencoding = TIXML_ENCODING_UTF8;\t// incorrect, but be nice\n\t\t\telse\n\t\t\t\tencoding = TIXML_ENCODING_LEGACY;\n\t\t}\n\n\t\tp = SkipWhiteSpace( p, encoding );\n\t}\n\n\t// Was this empty?\n\tif ( !firstChild ) {\n\t\tSetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );\n\t\treturn 0;\n\t}\n\n\t// All is well.\n\treturn p;\n}\n\nvoid TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding )\n{\n\t// The first error in a chain is more accurate - don't set again!\n\tif ( error )\n\t\treturn;\n\n\tassert( err > 0 && err < TIXML_ERROR_STRING_COUNT );\n\terror   = true;\n\terrorId = err;\n\terrorDesc = errorString[ errorId ];\n\n\terrorLocation.Clear();\n\tif ( pError && data )\n\t{\n\t\tdata->Stamp( pError, encoding );\n\t\terrorLocation = data->Cursor();\n\t}\n}\n\n\nTiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )\n{\n\tTiXmlNode* returnNode = 0;\n\n\tp = SkipWhiteSpace( p, encoding );\n\tif( !p || !*p || *p != '<' )\n\t{\n\t\treturn 0;\n\t}\n\n\tTiXmlDocument* doc = GetDocument();\n\tp = SkipWhiteSpace( p, encoding );\n\n\tif ( !p || !*p )\n\t{\n\t\treturn 0;\n\t}\n\n\t// What is this thing?\n\t// - Elements start with a letter or underscore, but xml is reserved.\n\t// - Comments: <!--\n\t// - Decleration: <?xml\n\t// - Everthing else is unknown to tinyxml.\n\t//\n\n\tconst char* xmlHeader = { \"<?xml\" };\n\tconst char* commentHeader = { \"<!--\" };\n\tconst char* dtdHeader = { \"<!\" };\n\tconst char* cdataHeader = { \"<![CDATA[\" };\n\n\tif ( StringEqual( p, xmlHeader, true, encoding ) )\n\t{\n\t\t#ifdef DEBUG_PARSER\n\t\t\tTIXML_LOG( \"XML parsing Declaration\\n\" );\n\t\t#endif\n\t\treturnNode = new TiXmlDeclaration();\n\t}\n\telse if ( StringEqual( p, commentHeader, false, encoding ) )\n\t{\n\t\t#ifdef DEBUG_PARSER\n\t\t\tTIXML_LOG( \"XML parsing Comment\\n\" );\n\t\t#endif\n\t\treturnNode = new TiXmlComment();\n\t}\n\telse if ( StringEqual( p, cdataHeader, false, encoding ) )\n\t{\n\t\t#ifdef DEBUG_PARSER\n\t\t\tTIXML_LOG( \"XML parsing CDATA\\n\" );\n\t\t#endif\n\t\tTiXmlText* text = new TiXmlText( \"\" );\n\t\ttext->SetCDATA( true );\n\t\treturnNode = text;\n\t}\n\telse if ( StringEqual( p, dtdHeader, false, encoding ) )\n\t{\n\t\t#ifdef DEBUG_PARSER\n\t\t\tTIXML_LOG( \"XML parsing Unknown(1)\\n\" );\n\t\t#endif\n\t\treturnNode = new TiXmlUnknown();\n\t}\n\telse if (\tIsAlpha( *(p+1), encoding )\n\t\t\t  || *(p+1) == '_' )\n\t{\n\t\t#ifdef DEBUG_PARSER\n\t\t\tTIXML_LOG( \"XML parsing Element\\n\" );\n\t\t#endif\n\t\treturnNode = new TiXmlElement( \"\" );\n\t}\n\telse\n\t{\n\t\t#ifdef DEBUG_PARSER\n\t\t\tTIXML_LOG( \"XML parsing Unknown(2)\\n\" );\n\t\t#endif\n\t\treturnNode = new TiXmlUnknown();\n\t}\n\n\tif ( returnNode )\n\t{\n\t\t// Set the parent, so it can report errors\n\t\treturnNode->parent = this;\n\t}\n\telse\n\t{\n\t\tif ( doc )\n\t\t\tdoc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t}\n\treturn returnNode;\n}\n\n#ifdef TIXML_USE_STL\n\nvoid TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag)\n{\n\t// We're called with some amount of pre-parsing. That is, some of \"this\"\n\t// element is in \"tag\". Go ahead and stream to the closing \">\"\n\twhile( in->good() )\n\t{\n\t\tint c = in->get();\n\t\tif ( c <= 0 )\n\t\t{\n\t\t\tTiXmlDocument* document = GetDocument();\n\t\t\tif ( document )\n\t\t\t\tdocument->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\treturn;\n\t\t}\n\t\t(*tag) += (char) c ;\n\n\t\tif ( c == '>' )\n\t\t\tbreak;\n\t}\n\n\tif ( tag->length() < 3 ) return;\n\n\t// Okay...if we are a \"/>\" tag, then we're done. We've read a complete tag.\n\t// If not, identify and stream.\n\n\tif (\ttag->at( tag->length() - 1 ) == '>'\n\t\t && tag->at( tag->length() - 2 ) == '/' )\n\t{\n\t\t// All good!\n\t\treturn;\n\t}\n\telse if ( tag->at( tag->length() - 1 ) == '>' )\n\t{\n\t\t// There is more. Could be:\n\t\t//\t\ttext\n\t\t//\t\tcdata text (which looks like another node)\n\t\t//\t\tclosing tag\n\t\t//\t\tanother node.\n\t\tfor ( ;; )\n\t\t{\n\t\t\tStreamWhiteSpace( in, tag );\n\n\t\t\t// Do we have text?\n\t\t\tif ( in->good() && in->peek() != '<' )\n\t\t\t{\n\t\t\t\t// Yep, text.\n\t\t\t\tTiXmlText text( \"\" );\n\t\t\t\ttext.StreamIn( in, tag );\n\n\t\t\t\t// What follows text is a closing tag or another node.\n\t\t\t\t// Go around again and figure it out.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// We now have either a closing tag...or another node.\n\t\t\t// We should be at a \"<\", regardless.\n\t\t\tif ( !in->good() ) return;\n\t\t\tassert( in->peek() == '<' );\n\t\t\tint tagIndex = (int) tag->length();\n\n\t\t\tbool closingTag = false;\n\t\t\tbool firstCharFound = false;\n\n\t\t\tfor( ;; )\n\t\t\t{\n\t\t\t\tif ( !in->good() )\n\t\t\t\t\treturn;\n\n\t\t\t\tint c = in->peek();\n\t\t\t\tif ( c <= 0 )\n\t\t\t\t{\n\t\t\t\t\tTiXmlDocument* document = GetDocument();\n\t\t\t\t\tif ( document )\n\t\t\t\t\t\tdocument->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif ( c == '>' )\n\t\t\t\t\tbreak;\n\n\t\t\t\t*tag += (char) c;\n\t\t\t\tin->get();\n\n\t\t\t\t// Early out if we find the CDATA id.\n\t\t\t\tif ( c == '[' && tag->size() >= 9 )\n\t\t\t\t{\n\t\t\t\t\tsize_t len = tag->size();\n\t\t\t\t\tconst char* start = tag->c_str() + len - 9;\n\t\t\t\t\tif ( strcmp( start, \"<![CDATA[\" ) == 0 ) {\n\t\t\t\t\t\tassert( !closingTag );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )\n\t\t\t\t{\n\t\t\t\t\tfirstCharFound = true;\n\t\t\t\t\tif ( c == '/' )\n\t\t\t\t\t\tclosingTag = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If it was a closing tag, then read in the closing '>' to clean up the input stream.\n\t\t\t// If it was not, the streaming will be done by the tag.\n\t\t\tif ( closingTag )\n\t\t\t{\n\t\t\t\tif ( !in->good() )\n\t\t\t\t\treturn;\n\n\t\t\t\tint c = in->get();\n\t\t\t\tif ( c <= 0 )\n\t\t\t\t{\n\t\t\t\t\tTiXmlDocument* document = GetDocument();\n\t\t\t\t\tif ( document )\n\t\t\t\t\t\tdocument->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tassert( c == '>' );\n\t\t\t\t*tag += (char) c;\n\n\t\t\t\t// We are done, once we've found our closing tag.\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// If not a closing tag, id it, and stream.\n\t\t\t\tconst char* tagloc = tag->c_str() + tagIndex;\n\t\t\t\tTiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );\n\t\t\t\tif ( !node )\n\t\t\t\t\treturn;\n\t\t\t\tnode->StreamIn( in, tag );\n\t\t\t\tdelete node;\n\t\t\t\tnode = 0;\n\n\t\t\t\t// No return: go around from the beginning: text, closing tag, or node.\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\nconst char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )\n{\n\tp = SkipWhiteSpace( p, encoding );\n\tTiXmlDocument* document = GetDocument();\n\n\tif ( !p || !*p )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );\n\t\treturn 0;\n\t}\n\n\tif ( data )\n\t{\n\t\tdata->Stamp( p, encoding );\n\t\tlocation = data->Cursor();\n\t}\n\n\tif ( *p != '<' )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );\n\t\treturn 0;\n\t}\n\n\tp = SkipWhiteSpace( p+1, encoding );\n\n\t// Read the name.\n\tconst char* pErr = p;\n\n\tp = ReadName( p, &value, encoding );\n\tif ( !p || !*p )\n\t{\n\t\tif ( document )\tdocument->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );\n\t\treturn 0;\n\t}\n\n\tTIXML_STRING endTag (\"</\");\n\tendTag += value;\n\tendTag += \">\";\n\n\t// Check for and read attributes. Also look for an empty\n\t// tag or an end tag.\n\twhile ( p && *p )\n\t{\n\t\tpErr = p;\n\t\tp = SkipWhiteSpace( p, encoding );\n\t\tif ( !p || !*p )\n\t\t{\n\t\t\tif ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );\n\t\t\treturn 0;\n\t\t}\n\t\tif ( *p == '/' )\n\t\t{\n\t\t\t++p;\n\t\t\t// Empty tag.\n\t\t\tif ( *p  != '>' )\n\t\t\t{\n\t\t\t\tif ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn (p+1);\n\t\t}\n\t\telse if ( *p == '>' )\n\t\t{\n\t\t\t// Done with attributes (if there were any.)\n\t\t\t// Read the value -- which can include other\n\t\t\t// elements -- read the end tag, and return.\n\t\t\t++p;\n\t\t\tp = ReadValue( p, data, encoding );\t\t// Note this is an Element method, and will set the error if one happens.\n\t\t\tif ( !p || !*p ) {\n\t\t\t\t// We were looking for the end tag, but found nothing.\n\t\t\t\t// Fix for [ 1663758 ] Failure to report error on bad XML\n\t\t\t\tif ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t// We should find the end tag now\n\t\t\tif ( StringEqual( p, endTag.c_str(), false, encoding ) )\n\t\t\t{\n\t\t\t\tp += endTag.length();\n\t\t\t\treturn p;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Try to read an attribute:\n\t\t\tTiXmlAttribute* attrib = new TiXmlAttribute();\n\t\t\tif ( !attrib )\n\t\t\t{\n\t\t\t\tif ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding );\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tattrib->SetDocument( document );\n\t\t\tpErr = p;\n\t\t\tp = attrib->Parse( p, data, encoding );\n\n\t\t\tif ( !p || !*p )\n\t\t\t{\n\t\t\t\tif ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );\n\t\t\t\tdelete attrib;\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t// Handle the strange case of double attributes:\n\t\t\t#ifdef TIXML_USE_STL\n\t\t\tTiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );\n\t\t\t#else\n\t\t\tTiXmlAttribute* node = attributeSet.Find( attrib->Name() );\n\t\t\t#endif\n\t\t\tif ( node )\n\t\t\t{\n\t\t\t\tnode->SetValue( attrib->Value() );\n\t\t\t\tdelete attrib;\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tattributeSet.Add( attrib );\n\t\t}\n\t}\n\treturn p;\n}\n\n\nconst char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )\n{\n\tTiXmlDocument* document = GetDocument();\n\n\t// Read in text and elements in any order.\n\tconst char* pWithWhiteSpace = p;\n\tp = SkipWhiteSpace( p, encoding );\n\n\twhile ( p && *p )\n\t{\n\t\tif ( *p != '<' )\n\t\t{\n\t\t\t// Take what we have, make a text element.\n\t\t\tTiXmlText* textNode = new TiXmlText( \"\" );\n\n\t\t\tif ( !textNode )\n\t\t\t{\n\t\t\t\tif ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding );\n\t\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif ( TiXmlBase::IsWhiteSpaceCondensed() )\n\t\t\t{\n\t\t\t\tp = textNode->Parse( p, data, encoding );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Special case: we want to keep the white space\n\t\t\t\t// so that leading spaces aren't removed.\n\t\t\t\tp = textNode->Parse( pWithWhiteSpace, data, encoding );\n\t\t\t}\n\n\t\t\tif ( !textNode->Blank() )\n\t\t\t\tLinkEndChild( textNode );\n\t\t\telse\n\t\t\t\tdelete textNode;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// We hit a '<'\n\t\t\t// Have we hit a new element or an end tag? This could also be\n\t\t\t// a TiXmlText in the \"CDATA\" style.\n\t\t\tif ( StringEqual( p, \"</\", false, encoding ) )\n\t\t\t{\n\t\t\t\treturn p;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tTiXmlNode* node = Identify( p, encoding );\n\t\t\t\tif ( node )\n\t\t\t\t{\n\t\t\t\t\tp = node->Parse( p, data, encoding );\n\t\t\t\t\tLinkEndChild( node );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tpWithWhiteSpace = p;\n\t\tp = SkipWhiteSpace( p, encoding );\n\t}\n\n\tif ( !p )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );\n\t}\n\treturn p;\n}\n\n\n#ifdef TIXML_USE_STL\nvoid TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag )\n{\n\twhile ( in->good() )\n\t{\n\t\tint c = in->get();\n\t\tif ( c <= 0 )\n\t\t{\n\t\t\tTiXmlDocument* document = GetDocument();\n\t\t\tif ( document )\n\t\t\t\tdocument->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\treturn;\n\t\t}\n\t\t(*tag) += (char) c;\n\n\t\tif ( c == '>' )\n\t\t{\n\t\t\t// All is well.\n\t\t\treturn;\n\t\t}\n\t}\n}\n#endif\n\n\nconst char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )\n{\n\tTiXmlDocument* document = GetDocument();\n\tp = SkipWhiteSpace( p, encoding );\n\n\tif ( data )\n\t{\n\t\tdata->Stamp( p, encoding );\n\t\tlocation = data->Cursor();\n\t}\n\tif ( !p || !*p || *p != '<' )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );\n\t\treturn 0;\n\t}\n\t++p;\n\tvalue = \"\";\n\n\twhile ( p && *p && *p != '>' )\n\t{\n\t\tvalue += *p;\n\t\t++p;\n\t}\n\n\tif ( !p || !*p )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );\n        return 0;\n\t}\n\tif ( *p == '>' )\n\t\treturn p+1;\n\treturn p;\n}\n\n#ifdef TIXML_USE_STL\nvoid TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag )\n{\n\twhile ( in->good() )\n\t{\n\t\tint c = in->get();\n\t\tif ( c <= 0 )\n\t\t{\n\t\t\tTiXmlDocument* document = GetDocument();\n\t\t\tif ( document )\n\t\t\t\tdocument->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\treturn;\n\t\t}\n\n\t\t(*tag) += (char) c;\n\n\t\tif ( c == '>'\n\t\t\t && tag->at( tag->length() - 2 ) == '-'\n\t\t\t && tag->at( tag->length() - 3 ) == '-' )\n\t\t{\n\t\t\t// All is well.\n\t\t\treturn;\n\t\t}\n\t}\n}\n#endif\n\n\nconst char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )\n{\n\tTiXmlDocument* document = GetDocument();\n\tvalue = \"\";\n\n\tp = SkipWhiteSpace( p, encoding );\n\n\tif ( data )\n\t{\n\t\tdata->Stamp( p, encoding );\n\t\tlocation = data->Cursor();\n\t}\n\tconst char* startTag = \"<!--\";\n\tconst char* endTag   = \"-->\";\n\n\tif ( !StringEqual( p, startTag, false, encoding ) )\n\t{\n\t\tdocument->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );\n\t\treturn 0;\n\t}\n\tp += strlen( startTag );\n\n\t// [ 1475201 ] TinyXML parses entities in comments\n\t// Oops - ReadText doesn't work, because we don't want to parse the entities.\n\t// p = ReadText( p, &value, false, endTag, false, encoding );\n\t//\n\t// from the XML spec:\n\t/*\n\t [Definition: Comments may appear anywhere in a document outside other markup; in addition,\n\t\t\t\t  they may appear within the document type declaration at places allowed by the grammar.\n\t\t\t\t  They are not part of the document's character data; an XML processor MAY, but need not,\n\t\t\t\t  make it possible for an application to retrieve the text of comments. For compatibility,\n\t\t\t\t  the string \"--\" (double-hyphen) MUST NOT occur within comments.] Parameter entity\n\t\t\t\t  references MUST NOT be recognized within comments.\n\n\t\t\t\t  An example of a comment:\n\n\t\t\t\t  <!-- declarations for <head> & <body> -->\n\t*/\n\n\tvalue = \"\";\n\t// Keep all the white space.\n\twhile (\tp && *p && !StringEqual( p, endTag, false, encoding ) )\n\t{\n\t\tvalue.append( p, 1 );\n\t\t++p;\n\t}\n\tif ( p )\n\t\tp += strlen( endTag );\n\n\treturn p;\n}\n\n\nconst char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )\n{\n\tp = SkipWhiteSpace( p, encoding );\n\tif ( !p || !*p ) return 0;\n\n//\tint tabsize = 4;\n//\tif ( document )\n//\t\ttabsize = document->TabSize();\n\n\tif ( data )\n\t{\n\t\tdata->Stamp( p, encoding );\n\t\tlocation = data->Cursor();\n\t}\n\t// Read the name, the '=' and the value.\n\tconst char* pErr = p;\n\tp = ReadName( p, &name, encoding );\n\tif ( !p || !*p )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );\n\t\treturn 0;\n\t}\n\tp = SkipWhiteSpace( p, encoding );\n\tif ( !p || !*p || *p != '=' )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );\n\t\treturn 0;\n\t}\n\n\t++p;\t// skip '='\n\tp = SkipWhiteSpace( p, encoding );\n\tif ( !p || !*p )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );\n\t\treturn 0;\n\t}\n\n\tconst char* end;\n\tconst char SINGLE_QUOTE = '\\'';\n\tconst char DOUBLE_QUOTE = '\\\"';\n\n\tif ( *p == SINGLE_QUOTE )\n\t{\n\t\t++p;\n\t\tend = \"\\'\";\t\t// single quote in string\n\t\tp = ReadText( p, &value, false, end, false, encoding );\n\t}\n\telse if ( *p == DOUBLE_QUOTE )\n\t{\n\t\t++p;\n\t\tend = \"\\\"\";\t\t// double quote in string\n\t\tp = ReadText( p, &value, false, end, false, encoding );\n\t}\n\telse\n\t{\n\t\t// All attribute values should be in single or double quotes.\n\t\t// But this is such a common error that the parser will try\n\t\t// its best, even without them.\n\t\tvalue = \"\";\n\t\twhile (\tp && *p\t\t\t\t\t\t\t\t\t\t\t// existence\n\t\t\t\t&& !IsWhiteSpace( *p ) && *p != '\\n' && *p != '\\r'\t// whitespace\n\t\t\t\t&& *p != '/' && *p != '>' )\t\t\t\t\t\t\t// tag end\n\t\t{\n\t\t\tif ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) {\n\t\t\t\t// [ 1451649 ] Attribute values with trailing quotes not handled correctly\n\t\t\t\t// We did not have an opening quote but seem to have a\n\t\t\t\t// closing one. Give up and throw an error.\n\t\t\t\tif ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tvalue += *p;\n\t\t\t++p;\n\t\t}\n\t}\n\treturn p;\n}\n\n#ifdef TIXML_USE_STL\nvoid TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag )\n{\n\twhile ( in->good() )\n\t{\n\t\tint c = in->peek();\n\t\tif ( !cdata && (c == '<' ) )\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tif ( c <= 0 )\n\t\t{\n\t\t\tTiXmlDocument* document = GetDocument();\n\t\t\tif ( document )\n\t\t\t\tdocument->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\treturn;\n\t\t}\n\n\t\t(*tag) += (char) c;\n\t\tin->get();\t// \"commits\" the peek made above\n\n\t\tif ( cdata && c == '>' && tag->size() >= 3 ) {\n\t\t\tsize_t len = tag->size();\n\t\t\tif ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) {\n\t\t\t\t// terminator of cdata.\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\nconst char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )\n{\n\tvalue = \"\";\n\tTiXmlDocument* document = GetDocument();\n\n\tif ( data )\n\t{\n\t\tdata->Stamp( p, encoding );\n\t\tlocation = data->Cursor();\n\t}\n\n\tconst char* const startTag = \"<![CDATA[\";\n\tconst char* const endTag   = \"]]>\";\n\n\tif ( cdata || StringEqual( p, startTag, false, encoding ) )\n\t{\n\t\tcdata = true;\n\n\t\tif ( !StringEqual( p, startTag, false, encoding ) )\n\t\t{\n\t\t\tdocument->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding );\n\t\t\treturn 0;\n\t\t}\n\t\tp += strlen( startTag );\n\n\t\t// Keep all the white space, ignore the encoding, etc.\n\t\twhile (\t   p && *p\n\t\t\t\t&& !StringEqual( p, endTag, false, encoding )\n\t\t\t  )\n\t\t{\n\t\t\tvalue += *p;\n\t\t\t++p;\n\t\t}\n\n\t\tTIXML_STRING dummy;\n\t\tp = ReadText( p, &dummy, false, endTag, false, encoding );\n\t\treturn p;\n\t}\n\telse\n\t{\n\t\tbool ignoreWhite = true;\n\n\t\tconst char* end = \"<\";\n\t\tp = ReadText( p, &value, ignoreWhite, end, false, encoding );\n\t\tif ( p )\n\t\t\treturn p-1;\t// don't truncate the '<'\n\t\treturn 0;\n\t}\n}\n\n#ifdef TIXML_USE_STL\nvoid TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag )\n{\n\twhile ( in->good() )\n\t{\n\t\tint c = in->get();\n\t\tif ( c <= 0 )\n\t\t{\n\t\t\tTiXmlDocument* document = GetDocument();\n\t\t\tif ( document )\n\t\t\t\tdocument->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );\n\t\t\treturn;\n\t\t}\n\t\t(*tag) += (char) c;\n\n\t\tif ( c == '>' )\n\t\t{\n\t\t\t// All is well.\n\t\t\treturn;\n\t\t}\n\t}\n}\n#endif\n\nconst char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding )\n{\n\tp = SkipWhiteSpace( p, _encoding );\n\t// Find the beginning, find the end, and look for\n\t// the stuff in-between.\n\tTiXmlDocument* document = GetDocument();\n\tif ( !p || !*p || !StringEqual( p, \"<?xml\", true, _encoding ) )\n\t{\n\t\tif ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );\n\t\treturn 0;\n\t}\n\tif ( data )\n\t{\n\t\tdata->Stamp( p, _encoding );\n\t\tlocation = data->Cursor();\n\t}\n\tp += 5;\n\n\tversion = \"\";\n\tencoding = \"\";\n\tstandalone = \"\";\n\n\twhile ( p && *p )\n\t{\n\t\tif ( *p == '>' )\n\t\t{\n\t\t\t++p;\n\t\t\treturn p;\n\t\t}\n\n\t\tp = SkipWhiteSpace( p, _encoding );\n\t\tif ( StringEqual( p, \"version\", true, _encoding ) )\n\t\t{\n\t\t\tTiXmlAttribute attrib;\n\t\t\tp = attrib.Parse( p, data, _encoding );\n\t\t\tversion = attrib.Value();\n\t\t}\n\t\telse if ( StringEqual( p, \"encoding\", true, _encoding ) )\n\t\t{\n\t\t\tTiXmlAttribute attrib;\n\t\t\tp = attrib.Parse( p, data, _encoding );\n\t\t\tencoding = attrib.Value();\n\t\t}\n\t\telse if ( StringEqual( p, \"standalone\", true, _encoding ) )\n\t\t{\n\t\t\tTiXmlAttribute attrib;\n\t\t\tp = attrib.Parse( p, data, _encoding );\n\t\t\tstandalone = attrib.Value();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Read over whatever it is.\n\t\t\twhile( p && *p && *p != '>' && !IsWhiteSpace( *p ) )\n\t\t\t\t++p;\n\t\t}\n\t}\n\treturn 0;\n}\n\nbool TiXmlText::Blank() const\n{\n\tfor ( unsigned i=0; i<value.length(); i++ )\n\t\tif ( !IsWhiteSpace( value[i] ) )\n\t\t\treturn false;\n\treturn true;\n}\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/CMakeLists.txt",
    "content": "set(OSG_FFMPEG_VIDEOPLAYER_LIBRARY \"osg-ffmpeg-videoplayer\")\n\n# Sources\n\nset(OSG_FFMPEG_VIDEOPLAYER_SOURCE_FILES\n    videoplayer.cpp\n    videostate.cpp\n    videodefs.hpp\n    audiodecoder.cpp\n    audiofactory.hpp\n)\n\ninclude_directories(${FFmpeg_INCLUDE_DIRS})\nadd_library(${OSG_FFMPEG_VIDEOPLAYER_LIBRARY} STATIC ${OSG_FFMPEG_VIDEOPLAYER_SOURCE_FILES})\ntarget_link_libraries(${OSG_FFMPEG_VIDEOPLAYER_LIBRARY} ${FFmpeg_LIBRARIES} ${Boost_THREAD_LIBRARY})\ntarget_link_libraries(${OSG_FFMPEG_VIDEOPLAYER_LIBRARY} ${OSG_LIBRARIES})\n\nlink_directories(${CMAKE_CURRENT_BINARY_DIR})\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/License.txt",
    "content": "Copyright (c) 2014 scrawl, Chris Robinson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/audiodecoder.cpp",
    "content": "#include \"audiodecoder.hpp\"\n\n#include <algorithm>\n#include <stdexcept>\n#include <string>\n\n#if defined(_MSC_VER)\n    #pragma warning (push)\n    #pragma warning (disable : 4244)\n#endif\n\nextern \"C\"\n{\n    #include <libswresample/swresample.h>\n}\n\n#if defined(_MSC_VER)\n    #pragma warning (pop)\n#endif\n\n#include \"videostate.hpp\"\n\nnamespace\n{\n    void fail(const std::string &str)\n    {\n        throw std::runtime_error(str);\n    }\n\n    const double AUDIO_DIFF_AVG_NB = 20;\n}\n\nnamespace Video\n{\n\n// Moved to implementation file, so that HAVE_SWRESAMPLE is used at library compile time only\nstruct AudioResampler\n{\n    AudioResampler()\n        : mSwr(nullptr)\n    {\n    }\n\n    ~AudioResampler()\n    {\n        swr_free(&mSwr);\n    }\n\n    SwrContext* mSwr;\n};\n\nMovieAudioDecoder::MovieAudioDecoder(VideoState* videoState)\n    : mVideoState(videoState)\n    , mAVStream(*videoState->audio_st)\n    , mOutputSampleFormat(AV_SAMPLE_FMT_NONE)\n    , mOutputChannelLayout(0)\n    , mOutputSampleRate(0)\n    , mFramePos(0)\n    , mFrameSize(0)\n    , mAudioClock(0.0)\n    , mDataBuf(nullptr)\n    , mFrameData(nullptr)\n    , mDataBufLen(0)\n    , mFrame(av_frame_alloc())\n    , mGetNextPacket(true)\n    , mAudioDiffAccum(0.0)\n    , mAudioDiffAvgCoef(exp(log(0.01 / AUDIO_DIFF_AVG_NB)))\n    /* Correct audio only if larger error than this */\n    , mAudioDiffThreshold(2.0 * 0.050/* 50 ms */)\n    , mAudioDiffAvgCount(0)\n{\n    mAudioResampler.reset(new AudioResampler());\n\n    const AVCodec *codec = avcodec_find_decoder(mAVStream->codecpar->codec_id);\n    if(!codec)\n    {\n        std::string ss = \"No codec found for id \" +\n                            std::to_string(mAVStream->codecpar->codec_id);\n        throw std::runtime_error(ss);\n    }\n\n    AVCodecContext *avctx = avcodec_alloc_context3(codec);\n    avcodec_parameters_to_context(avctx, mAVStream->codecpar);\n\n// This is not needed anymore above FFMpeg version 4.0\n#if LIBAVCODEC_VERSION_INT < 3805796\n    av_codec_set_pkt_timebase(avctx, mAVStream->time_base);\n#endif\n\n    mAudioContext = avctx;\n\n    if(avcodec_open2(mAudioContext, codec, nullptr) < 0)\n        throw std::runtime_error(std::string(\"Failed to open audio codec \") + codec->long_name);\n}\n\nMovieAudioDecoder::~MovieAudioDecoder()\n{\n    if(mAudioContext)\n        avcodec_free_context(&mAudioContext);\n\n    av_frame_free(&mFrame);\n    av_freep(&mDataBuf);\n}\n\nvoid MovieAudioDecoder::setupFormat()\n{\n    if (mAudioResampler->mSwr)\n        return; // already set up\n\n    AVSampleFormat inputSampleFormat = mAudioContext->sample_fmt;\n\n    uint64_t inputChannelLayout = mAudioContext->channel_layout;\n    if (inputChannelLayout == 0)\n        inputChannelLayout = av_get_default_channel_layout(mAudioContext->channels);\n\n    int inputSampleRate = mAudioContext->sample_rate;\n\n    mOutputSampleRate = inputSampleRate;\n    mOutputSampleFormat = inputSampleFormat;\n    mOutputChannelLayout = inputChannelLayout;\n    adjustAudioSettings(mOutputSampleFormat, mOutputChannelLayout, mOutputSampleRate);\n\n    if (inputSampleFormat != mOutputSampleFormat\n            || inputChannelLayout != mOutputChannelLayout\n            || inputSampleRate != mOutputSampleRate)\n    {\n        mAudioResampler->mSwr = swr_alloc_set_opts(mAudioResampler->mSwr,\n                          mOutputChannelLayout,\n                          mOutputSampleFormat,\n                          mOutputSampleRate,\n                          inputChannelLayout,\n                          inputSampleFormat,\n                          inputSampleRate,\n                          0,                             // logging level offset\n                          nullptr);                      // log context\n        if(!mAudioResampler->mSwr)\n            fail(std::string(\"Couldn't allocate SwrContext\"));\n        if(swr_init(mAudioResampler->mSwr) < 0)\n            fail(std::string(\"Couldn't initialize SwrContext\"));\n    }\n}\n\nint MovieAudioDecoder::synchronize_audio()\n{\n    if(mVideoState->av_sync_type == AV_SYNC_AUDIO_MASTER)\n        return 0;\n\n    int sample_skip = 0;\n\n    // accumulate the clock difference\n    double diff = mVideoState->get_master_clock() - mVideoState->get_audio_clock();\n    mAudioDiffAccum = diff + mAudioDiffAvgCoef * mAudioDiffAccum;\n    if(mAudioDiffAvgCount < AUDIO_DIFF_AVG_NB)\n        mAudioDiffAvgCount++;\n    else\n    {\n        double avg_diff = mAudioDiffAccum * (1.0 - mAudioDiffAvgCoef);\n        if(fabs(avg_diff) >= mAudioDiffThreshold)\n        {\n            int n = av_get_bytes_per_sample(mOutputSampleFormat) *\n                    av_get_channel_layout_nb_channels(mOutputChannelLayout);\n            sample_skip = ((int)(diff * mAudioContext->sample_rate) * n);\n        }\n    }\n\n    return sample_skip;\n}\n\nint MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip)\n{\n    AVPacket *pkt = &mPacket;\n\n    for(;;)\n    {\n        /* send the packet with the compressed data to the decoder */\n        int ret = 0;\n        if (mGetNextPacket)\n            ret = avcodec_send_packet(mAudioContext, pkt);\n\n        /* read all the output frames (in general there may be any number of them */\n        while (ret >= 0)\n        {\n            ret = avcodec_receive_frame(mAudioContext, frame);\n            if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))\n            {\n                // EAGAIN means that we need additional packages to decode this frame.\n                // AVERROR_EOF means the end of package.\n                mGetNextPacket = true;\n                break;\n            }\n            else if (ret < 0)\n            {\n                // Error encountered. Stop to decode audio stream.\n                av_packet_unref(&mPacket);\n                mGetNextPacket = true;\n                return -1;\n            }\n\n            if(frame->nb_samples <= 0)\n                continue;\n\n            if(mAudioResampler->mSwr)\n            {\n                if(!mDataBuf || mDataBufLen < frame->nb_samples)\n                {\n                    av_freep(&mDataBuf);\n                    if(av_samples_alloc(&mDataBuf, nullptr, av_get_channel_layout_nb_channels(mOutputChannelLayout),\n                                        frame->nb_samples, mOutputSampleFormat, 0) < 0)\n                        break;\n                    else\n                        mDataBufLen = frame->nb_samples;\n                }\n\n                if(swr_convert(mAudioResampler->mSwr, (uint8_t**)&mDataBuf, frame->nb_samples,\n                    (const uint8_t**)frame->extended_data, frame->nb_samples) < 0)\n                {\n                    break;\n                }\n                mFrameData = &mDataBuf;\n            }\n            else\n                mFrameData = &frame->data[0];\n\n            int result = frame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) *\n                    av_get_bytes_per_sample(mOutputSampleFormat);\n\n            /* We have data, return it and come back for more later */\n            mGetNextPacket = false;\n            return result;\n        }\n\n        av_packet_unref(pkt);\n        mGetNextPacket = true;\n\n        /* next packet */\n        if(mVideoState->audioq.get(pkt, mVideoState) < 0)\n            return -1;\n\n        if(pkt->data == mVideoState->mFlushPktData)\n        {\n            avcodec_flush_buffers(mAudioContext);\n            mAudioDiffAccum = 0.0;\n            mAudioDiffAvgCount = 0;\n            mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts;\n            sample_skip = 0;\n\n            if(mVideoState->audioq.get(pkt, mVideoState) < 0)\n                return -1;\n        }\n\n        /* if update, update the audio clock w/pts */\n        if(pkt->pts != AV_NOPTS_VALUE)\n            mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts;\n    }\n}\n\nsize_t MovieAudioDecoder::read(char *stream, size_t len)\n{\n    if (mVideoState->mPaused)\n    {\n        // fill the buffer with silence\n        size_t sampleSize = av_get_bytes_per_sample(mOutputSampleFormat);\n        char* data[1];\n        data[0] = stream;\n        av_samples_set_silence((uint8_t**)data, 0, static_cast<int>(len/sampleSize), 1, mOutputSampleFormat);\n        return len;\n    }\n\n    int sample_skip = synchronize_audio();\n    size_t total = 0;\n\n    while(total < len)\n    {\n        if(mFramePos >= mFrameSize)\n        {\n            /* We have already sent all our data; get more */\n            mFrameSize = audio_decode_frame(mFrame, sample_skip);\n            if(mFrameSize < 0)\n            {\n                /* If error, we're done */\n                break;\n            }\n\n            mFramePos = std::min<ssize_t>(mFrameSize, sample_skip);\n            if(sample_skip > 0 || mFrameSize > -sample_skip)\n                sample_skip -= static_cast<int>(mFramePos);\n            continue;\n        }\n\n        size_t len1 = len - total;\n        if(mFramePos >= 0)\n        {\n            len1 = std::min<size_t>(len1, mFrameSize-mFramePos);\n            memcpy(stream, mFrameData[0]+mFramePos, len1);\n        }\n        else\n        {\n            len1 = std::min<size_t>(len1, -mFramePos);\n\n            int n = av_get_bytes_per_sample(mOutputSampleFormat)\n                    * av_get_channel_layout_nb_channels(mOutputChannelLayout);\n\n            /* add samples by copying the first sample*/\n            if(n == 1)\n                memset(stream, *mFrameData[0], len1);\n            else if(n == 2)\n            {\n                const int16_t val = *((int16_t*)mFrameData[0]);\n                for(size_t nb = 0;nb < len1;nb += n)\n                    *((int16_t*)(stream+nb)) = val;\n            }\n            else if(n == 4)\n            {\n                const int32_t val = *((int32_t*)mFrameData[0]);\n                for(size_t nb = 0;nb < len1;nb += n)\n                    *((int32_t*)(stream+nb)) = val;\n            }\n            else if(n == 8)\n            {\n                const int64_t val = *((int64_t*)mFrameData[0]);\n                for(size_t nb = 0;nb < len1;nb += n)\n                    *((int64_t*)(stream+nb)) = val;\n            }\n            else\n            {\n                for(size_t nb = 0;nb < len1;nb += n)\n                    memcpy(stream+nb, mFrameData[0], n);\n            }\n        }\n\n        total += len1;\n        stream += len1;\n        mFramePos += len1;\n    }\n\n    return total;\n}\n\ndouble MovieAudioDecoder::getAudioClock()\n{\n    return mAudioClock;\n}\n\nint MovieAudioDecoder::getOutputSampleRate() const\n{\n    return mOutputSampleRate;\n}\n\nuint64_t MovieAudioDecoder::getOutputChannelLayout() const\n{\n    return mOutputChannelLayout;\n}\n\nAVSampleFormat MovieAudioDecoder::getOutputSampleFormat() const\n{\n    return mOutputSampleFormat;\n}\n\n}\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/audiodecoder.hpp",
    "content": "#ifndef VIDEOPLAYER_AUDIODECODER_H\n#define VIDEOPLAYER_AUDIODECODER_H\n\n#include <stdint.h>\n\n#include <new>\n#include <memory>\n\n#if defined(_MSC_VER)\n    #pragma warning (push)\n    #pragma warning (disable : 4244)\n#endif\n\nextern \"C\"\n{\n    #include <libavutil/avutil.h>\n    #include <libavcodec/avcodec.h>\n    #include <libavformat/avformat.h>\n    #include <libavutil/channel_layout.h>\n}\n\n#if defined(_MSC_VER)\n    #pragma warning (pop)\n#endif\n\n#if defined(_WIN32) && !defined(__MINGW32__)\n#include <basetsd.h>\n\ntypedef SSIZE_T ssize_t;\n#endif\n\nnamespace Video\n{\n\nstruct AudioResampler;\n\nstruct VideoState;\n\nclass MovieAudioDecoder\n{\nprotected:\n    VideoState *mVideoState;\n    AVCodecContext* mAudioContext;\n    AVStream *mAVStream;\n    enum AVSampleFormat mOutputSampleFormat;\n    uint64_t mOutputChannelLayout;\n    int mOutputSampleRate;\n    ssize_t mFramePos;\n    ssize_t mFrameSize;\n    double mAudioClock;\n\nprivate:\n    struct AutoAVPacket : public AVPacket {\n        AutoAVPacket(int size=0)\n        {\n            if(av_new_packet(this, size) < 0)\n                throw std::bad_alloc();\n        }\n        ~AutoAVPacket()\n        { av_packet_unref(this); }\n    };\n\n\n    std::unique_ptr<AudioResampler> mAudioResampler;\n\n    uint8_t *mDataBuf;\n    uint8_t **mFrameData;\n    int mDataBufLen;\n\n    AutoAVPacket mPacket;\n    AVFrame *mFrame;\n    bool mGetNextPacket;\n\n    /* averaging filter for audio sync */\n    double mAudioDiffAccum;\n    const double mAudioDiffAvgCoef;\n    const double mAudioDiffThreshold;\n    int mAudioDiffAvgCount;\n\n    /* Add or subtract samples to get a better sync, return number of bytes to\n     * skip (negative means to duplicate). */\n    int synchronize_audio();\n\n    /// @param sample_skip If seeking happened, the sample_skip variable will be reset to 0.\n    int audio_decode_frame(AVFrame *frame, int &sample_skip);\n\npublic:\n    MovieAudioDecoder(VideoState *is);\n    virtual ~MovieAudioDecoder();\n\n    int getOutputSampleRate() const;\n    AVSampleFormat getOutputSampleFormat() const;\n    uint64_t getOutputChannelLayout() const;\n\n    void setupFormat();\n\n    /// Adjust the given audio settings to the application's needs. The data given by the read() function will\n    /// be in the desired format written to this function's parameters.\n    /// @par Depending on the application, we may want either fixed settings, or a \"closest supported match\"\n    /// for the input that does not incur precision loss (e.g. planar -> non-planar format).\n    virtual void adjustAudioSettings(AVSampleFormat& sampleFormat, uint64_t& channelLayout, int& sampleRate) = 0;\n\n    /// Return the current offset in seconds from the beginning of the audio stream.\n    /// @par An internal clock exists in the mAudioClock member, and is used in the default implementation. However,\n    /// for an accurate clock, it's best to also take the current offset in the audio buffer into account.\n    virtual double getAudioClock();\n\n    /// This is the main interface to be used by the user's audio library.\n    /// @par Request filling the \\a stream with \\a len number of bytes.\n    /// @return The number of bytes read (may not be the requested number if we arrived at the end of the audio stream)\n    size_t read(char *stream, size_t len);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/audiofactory.hpp",
    "content": "#ifndef VIDEO_MOVIEAUDIOFACTORY_H\n#define VIDEO_MOVIEAUDIOFACTORY_H\n\n#include \"audiodecoder.hpp\"\n\n\nnamespace Video\n{\n\nclass MovieAudioFactory\n{\npublic:\n    virtual std::shared_ptr<MovieAudioDecoder> createDecoder(VideoState* videoState) = 0;\n    virtual ~MovieAudioFactory() {}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/videodefs.hpp",
    "content": "#ifndef VIDEOPLAYER_DEFS_H\n#define VIDEOPLAYER_DEFS_H\n\nnamespace Video\n{\n\nenum {\n    AV_SYNC_AUDIO_MASTER, // Play audio with no frame drops, sync video to audio\n    AV_SYNC_VIDEO_MASTER, // Play video with no frame drops, sync audio to video\n    AV_SYNC_EXTERNAL_MASTER, // Sync audio and video to an external clock\n\n    AV_SYNC_DEFAULT = AV_SYNC_EXTERNAL_MASTER\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/videoplayer.cpp",
    "content": "#include \"videoplayer.hpp\"\n\n#include <iostream>\n\n#include <osg/Texture2D>\n\n#include \"audiofactory.hpp\"\n#include \"videostate.hpp\"\n\nnamespace Video\n{\n\nVideoPlayer::VideoPlayer()\n    : mState(nullptr)\n{\n\n}\n\nVideoPlayer::~VideoPlayer()\n{\n    if(mState)\n        close();\n}\n\nvoid VideoPlayer::setAudioFactory(MovieAudioFactory *factory)\n{\n    mAudioFactory.reset(factory);\n}\n\nvoid VideoPlayer::playVideo(std::shared_ptr<std::istream> inputstream, const std::string& name)\n{\n    if(mState)\n        close();\n\n    try {\n        mState = new VideoState;\n        mState->setAudioFactory(mAudioFactory.get());\n        mState->init(inputstream, name);\n\n        // wait until we have the first picture\n        while (mState->video_st && !mState->mTexture.get())\n        {\n            if (!mState->update())\n                break;\n        }\n    }\n    catch(std::exception& e) {\n        std::cerr<< \"Failed to play video: \"<<e.what() <<std::endl;\n        close();\n    }\n}\n\nbool VideoPlayer::update ()\n{\n    if(mState)\n        return mState->update();\n    return false;\n}\n\nosg::ref_ptr<osg::Texture2D> VideoPlayer::getVideoTexture()\n{\n    if (mState)\n        return mState->mTexture;\n    return osg::ref_ptr<osg::Texture2D>();\n}\n\nint VideoPlayer::getVideoWidth()\n{\n    int width=0;\n    if (mState && mState->mTexture.get() && mState->mTexture->getImage())\n        width = mState->mTexture->getImage()->s();\n    return width;\n}\n\nint VideoPlayer::getVideoHeight()\n{\n    int height=0;\n    if (mState && mState->mTexture.get() && mState->mTexture->getImage())\n        height = mState->mTexture->getImage()->t();\n    return height;\n}\n\nvoid VideoPlayer::close()\n{\n    if(mState)\n    {\n        mState->deinit();\n\n        delete mState;\n        mState = nullptr;\n    }\n}\n\nbool VideoPlayer::hasAudioStream()\n{\n    return mState && mState->audio_st != nullptr;\n}\n\nvoid VideoPlayer::play()\n{\n    if (mState)\n        mState->setPaused(false);\n}\n\nvoid VideoPlayer::pause()\n{\n    if (mState)\n        mState->setPaused(true);\n}\n\nbool VideoPlayer::isPaused()\n{\n    if (mState)\n        return mState->mPaused;\n    return true;\n}\n\ndouble VideoPlayer::getCurrentTime()\n{\n    if (mState)\n        return mState->get_master_clock();\n    return 0.0;\n}\n\nvoid VideoPlayer::seek(double time)\n{\n    if (mState)\n        mState->seekTo(time);\n}\n\ndouble VideoPlayer::getDuration()\n{\n    if (mState)\n        return mState->getDuration();\n    return 0.0;\n}\n\n}\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/videoplayer.hpp",
    "content": "#ifndef VIDEOPLAYER_H\n#define VIDEOPLAYER_H\n\n#include <stdint.h>\n\n#include <string>\n#include <memory>\n\n#include <iosfwd>\n\n#include <osg/Texture2D>\n\nnamespace osg\n{\n    class Texture2D;\n}\n\nnamespace Video\n{\n\n    struct VideoState;\n    class MovieAudioFactory;\n\n    /**\n     * @brief Plays a video on an osg texture.\n     */\n    class VideoPlayer\n    {\n    public:\n        VideoPlayer();\n        ~VideoPlayer();\n\n        /// @brief Set the MovieAudioFactory to use.\n        /// @par This class must be implemented by the user and is responsible for reading the decoded audio data.\n        /// @note If you do not set up a MovieAudioFactory, then audio streams will be ignored and the video will be played with no sound.\n        /// @note Takes ownership of the passed pointer.\n        void setAudioFactory (MovieAudioFactory* factory);\n\n        /// Return true if a video is currently playing and it has an audio stream.\n        bool hasAudioStream();\n\n        /// Play the given video. If a video is already playing, the old video is closed first.\n        /// @note The video will be unpaused by default. Use the pause() and play() methods to control pausing.\n        /// @param name A name for the video stream - only used for logging purposes.\n        void playVideo (std::shared_ptr<std::istream> inputstream, const std::string& name);\n\n        /// Get the current playback time position in the video, in seconds\n        double getCurrentTime();\n\n        /// Get the duration of the video in seconds\n        double getDuration();\n\n        /// Seek to the specified time position in the video\n        void seek(double time);\n\n        void play();\n        void pause();\n        bool isPaused();\n\n        /// This should be called every frame by the user to update the video texture.\n        /// @return Returns true if the video is still playing, false if we have reached the end of the video stream.\n        bool update();\n\n        /// Stop the currently playing video, if a video is playing.\n        void close();\n\n        /// Return the texture of the currently playing video, or a null pointer if no video is playing.\n        osg::ref_ptr<osg::Texture2D> getVideoTexture();\n\n        /// Return the width of the currently playing video, or 0 if no video is playing.\n        int getVideoWidth();\n        /// Return the height of the currently playing video, or 0 if no video is playing.\n        int getVideoHeight();\n\n\n    private:\n        VideoState* mState;\n\n        std::unique_ptr<MovieAudioFactory> mAudioFactory;\n    };\n\n}\n\n#endif\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/videostate.cpp",
    "content": "#include \"videostate.hpp\"\n\n#include <algorithm>\n#include <cassert>\n#include <cstddef>\n#include <iostream>\n#include <thread>\n#include <chrono>\n\n#include <osg/Texture2D>\n\n#if defined(_MSC_VER)\n    #pragma warning (push)\n    #pragma warning (disable : 4244)\n#endif\n\nextern \"C\"\n{\n    #include <libavcodec/avcodec.h>\n    #include <libavformat/avformat.h>\n    #include <libswscale/swscale.h>\n    #include <libavutil/time.h>\n}\n\n#if defined(_MSC_VER)\n    #pragma warning (pop)\n#endif\n\nstatic const char* flushString = \"FLUSH\";\nstruct FlushPacket : AVPacket\n{\n    FlushPacket()\n        : AVPacket()\n    {\n        data = ( (uint8_t*)flushString);\n    }\n};\n\nstatic FlushPacket flush_pkt;\n\n#include \"videoplayer.hpp\"\n#include \"audiodecoder.hpp\"\n#include \"audiofactory.hpp\"\n\nnamespace\n{\n    const int MAX_AUDIOQ_SIZE = (5 * 16 * 1024);\n    const int MAX_VIDEOQ_SIZE = (5 * 256 * 1024);\n\n    struct AVPacketUnref\n    {\n        void operator()(AVPacket* packet) const\n        {\n            av_packet_unref(packet);\n        }\n    };\n\n    struct AVFrameFree\n    {\n        void operator()(AVFrame* frame) const\n        {\n            av_frame_free(&frame);\n        }\n    };\n\n    template<class T>\n    struct AVFree\n    {\n        void operator()(T* frame) const\n        {\n            av_free(&frame);\n        }\n    };\n}\n\nnamespace Video\n{\n\nVideoState::VideoState()\n    : mAudioFactory(nullptr)\n    , format_ctx(nullptr)\n    , video_ctx(nullptr)\n    , audio_ctx(nullptr)\n    , av_sync_type(AV_SYNC_DEFAULT)\n    , audio_st(nullptr)\n    , video_st(nullptr), frame_last_pts(0.0)\n    , video_clock(0.0), sws_context(nullptr)\n    , sws_context_w(0), sws_context_h(0)\n    , pictq_size(0), pictq_rindex(0), pictq_windex(0)\n    , mSeekRequested(false)\n    , mSeekPos(0)\n    , mVideoEnded(false)\n    , mPaused(false)\n    , mQuit(false)\n{\n    mFlushPktData = flush_pkt.data;\n\n// This is not needed anymore above FFMpeg version 4.0\n#if LIBAVCODEC_VERSION_INT < 3805796\n    av_register_all();\n#endif\n}\n\nVideoState::~VideoState()\n{\n    deinit();\n}\n\nvoid VideoState::setAudioFactory(MovieAudioFactory *factory)\n{\n    mAudioFactory = factory;\n}\n\n\nvoid PacketQueue::put(AVPacket *pkt)\n{\n    std::unique_ptr<AVPacketList, AVFree<AVPacketList>> pkt1(static_cast<AVPacketList*>(av_malloc(sizeof(AVPacketList))));\n    if(!pkt1) throw std::bad_alloc();\n\n    if(pkt == &flush_pkt)\n        pkt1->pkt = *pkt;\n    else\n        av_packet_move_ref(&pkt1->pkt, pkt);\n\n    pkt1->next = nullptr;\n\n    std::lock_guard<std::mutex> lock(this->mutex);\n    AVPacketList* ptr = pkt1.release();\n    if(!last_pkt)\n        this->first_pkt = ptr;\n    else\n        this->last_pkt->next = ptr;\n    this->last_pkt = ptr;\n    this->nb_packets++;\n    this->size += ptr->pkt.size;\n    this->cond.notify_one();\n}\n\nint PacketQueue::get(AVPacket *pkt, VideoState *is)\n{\n    std::unique_lock<std::mutex> lock(this->mutex);\n    while(!is->mQuit)\n    {\n        AVPacketList *pkt1 = this->first_pkt;\n        if(pkt1)\n        {\n            this->first_pkt = pkt1->next;\n            if(!this->first_pkt)\n                this->last_pkt = nullptr;\n            this->nb_packets--;\n            this->size -= pkt1->pkt.size;\n\n            av_packet_unref(pkt);\n            av_packet_move_ref(pkt, &pkt1->pkt);\n            av_free(pkt1);\n\n            return 1;\n        }\n\n        if(this->flushing)\n            break;\n        this->cond.wait(lock);\n    }\n\n    return -1;\n}\n\nvoid PacketQueue::flush()\n{\n    this->flushing = true;\n    this->cond.notify_one();\n}\n\nvoid PacketQueue::clear()\n{\n    AVPacketList *pkt, *pkt1;\n\n    std::lock_guard<std::mutex> lock(this->mutex);\n    for(pkt = this->first_pkt; pkt != nullptr; pkt = pkt1)\n    {\n        pkt1 = pkt->next;\n        if (pkt->pkt.data != flush_pkt.data)\n            av_packet_unref(&pkt->pkt);\n        av_freep(&pkt);\n    }\n    this->last_pkt = nullptr;\n    this->first_pkt = nullptr;\n    this->nb_packets = 0;\n    this->size = 0;\n}\n\nint VideoPicture::set_dimensions(int w, int h) {\n  if (this->rgbaFrame != nullptr && this->rgbaFrame->width == w &&\n      this->rgbaFrame->height == h) {\n    return 0;\n  }\n\n  std::unique_ptr<AVFrame, VideoPicture::AVFrameDeleter> frame{\n      av_frame_alloc()};\n  if (frame == nullptr) {\n    std::cerr << \"av_frame_alloc failed\" << std::endl;\n    return -1;\n  }\n\n  constexpr AVPixelFormat kPixFmt = AV_PIX_FMT_RGBA;\n  frame->format = kPixFmt;\n  frame->width = w;\n  frame->height = h;\n  if (av_image_alloc(frame->data, frame->linesize, frame->width, frame->height,\n                     kPixFmt, 1) < 0) {\n    std::cerr << \"av_image_alloc failed\" << std::endl;\n    return -1;\n  }\n\n  this->rgbaFrame = std::move(frame);\n  return 0;\n}\n\nvoid VideoPicture::AVFrameDeleter::operator()(AVFrame* frame) const\n{\n    av_freep(frame->data);\n    av_frame_free(&frame);\n}\n\nint VideoState::istream_read(void *user_data, uint8_t *buf, int buf_size)\n{\n    try\n    {\n        std::istream& stream = *static_cast<VideoState*>(user_data)->stream;\n        stream.clear();\n        stream.read((char*)buf, buf_size);\n        return stream.gcount();\n    }\n    catch (std::exception& )\n    {\n        return 0;\n    }\n}\n\nint VideoState::istream_write(void *, uint8_t *, int)\n{\n    throw std::runtime_error(\"can't write to read-only stream\");\n}\n\nint64_t VideoState::istream_seek(void *user_data, int64_t offset, int whence)\n{\n    std::istream& stream = *static_cast<VideoState*>(user_data)->stream;\n\n    whence &= ~AVSEEK_FORCE;\n\n    stream.clear();\n\n    if(whence == AVSEEK_SIZE)\n    {\n        size_t prev = stream.tellg();\n        stream.seekg(0, std::ios_base::end);\n        size_t size = stream.tellg();\n        stream.seekg(prev, std::ios_base::beg);\n        return size;\n    }\n\n    if(whence == SEEK_SET)\n        stream.seekg(offset, std::ios_base::beg);\n    else if(whence == SEEK_CUR)\n        stream.seekg(offset, std::ios_base::cur);\n    else if(whence == SEEK_END)\n        stream.seekg(offset, std::ios_base::end);\n    else\n        return -1;\n\n    return stream.tellg();\n}\n\nvoid VideoState::video_display(VideoPicture *vp)\n{\n    if(this->video_ctx->width != 0 && this->video_ctx->height != 0)\n    {\n        if (!mTexture.get())\n        {\n            mTexture = new osg::Texture2D;\n            mTexture->setDataVariance(osg::Object::DYNAMIC);\n            mTexture->setResizeNonPowerOfTwoHint(false);\n            mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);\n            mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);\n        }\n\n        osg::ref_ptr<osg::Image> image = new osg::Image;\n\n        image->setImage(this->video_ctx->width, this->video_ctx->height,\n                        1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, vp->rgbaFrame->data[0], osg::Image::NO_DELETE);\n\n        mTexture->setImage(image);\n    }\n}\n\nvoid VideoState::video_refresh()\n{\n    std::lock_guard<std::mutex> lock(this->pictq_mutex);\n    if(this->pictq_size == 0)\n        return;\n\n    if (this->av_sync_type == AV_SYNC_VIDEO_MASTER)\n    {\n        VideoPicture* vp = &this->pictq[this->pictq_rindex];\n        this->video_display(vp);\n\n        this->pictq_rindex = (pictq_rindex+1) % VIDEO_PICTURE_ARRAY_SIZE;\n        this->frame_last_pts = vp->pts;\n        this->pictq_size--;\n        this->pictq_cond.notify_one();\n    }\n    else\n    {\n        const float threshold = 0.03f;\n        if (this->pictq[pictq_rindex].pts > this->get_master_clock() + threshold)\n            return; // not ready yet to show this picture\n\n        // TODO: the conversion to RGBA is done in the decoding thread, so if a picture is skipped here, then it was\n        // unnecessarily converted. But we may want to replace the conversion by a pixel shader anyway (see comment in queue_picture)\n        int i=0;\n        for (; i<this->pictq_size-1; ++i)\n        {\n            if (this->pictq[pictq_rindex].pts + threshold <= this->get_master_clock())\n                this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_ARRAY_SIZE; // not enough time to show this picture\n            else\n                break;\n        }\n\n        assert (this->pictq_rindex < VIDEO_PICTURE_ARRAY_SIZE);\n        VideoPicture* vp = &this->pictq[this->pictq_rindex];\n\n        this->video_display(vp);\n\n        this->frame_last_pts = vp->pts;\n\n        this->pictq_size -= i;\n        // update queue for next picture\n        this->pictq_size--;\n        this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_ARRAY_SIZE;\n        this->pictq_cond.notify_one();\n    }\n}\n\n\nint VideoState::queue_picture(const AVFrame &pFrame, double pts)\n{\n    VideoPicture *vp;\n\n    /* wait until we have a new pic */\n    {\n        std::unique_lock<std::mutex> lock(this->pictq_mutex);\n        while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->mQuit)\n            this->pictq_cond.wait_for(lock, std::chrono::milliseconds(1));\n    }\n    if(this->mQuit)\n        return -1;\n\n    std::lock_guard<std::mutex> lock(this->pictq_mutex);\n\n    // windex is set to 0 initially\n    vp = &this->pictq[this->pictq_windex];\n\n    // Convert the image into RGBA format\n    // TODO: we could do this in a pixel shader instead, if the source format\n    // matches a commonly used format (ie YUV420P)\n    const int w = pFrame.width;\n    const int h = pFrame.height;\n    if(this->sws_context == nullptr || this->sws_context_w != w || this->sws_context_h != h)\n    {\n        if (this->sws_context != nullptr)\n            sws_freeContext(this->sws_context);\n        this->sws_context = sws_getContext(w, h, this->video_ctx->pix_fmt,\n                                           w, h, AV_PIX_FMT_RGBA, SWS_BICUBIC,\n                                           nullptr, nullptr, nullptr);\n        if(this->sws_context == nullptr)\n            throw std::runtime_error(\"Cannot initialize the conversion context!\\n\");\n        this->sws_context_w = w;\n        this->sws_context_h = h;\n    }\n\n    vp->pts = pts;\n    if (vp->set_dimensions(w, h) < 0)\n        return -1;\n\n    sws_scale(this->sws_context, pFrame.data, pFrame.linesize,\n              0, this->video_ctx->height, vp->rgbaFrame->data, vp->rgbaFrame->linesize);\n\n    // now we inform our display thread that we have a pic ready\n    this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_ARRAY_SIZE;\n    this->pictq_size++;\n\n    return 0;\n}\n\ndouble VideoState::synchronize_video(const AVFrame &src_frame, double pts)\n{\n    double frame_delay;\n\n    /* if we have pts, set video clock to it */\n    if(pts != 0)\n        this->video_clock = pts;\n    else\n        pts = this->video_clock;\n\n    /* update the video clock */\n    frame_delay = av_q2d(this->video_ctx->pkt_timebase);\n\n    /* if we are repeating a frame, adjust clock accordingly */\n    frame_delay += src_frame.repeat_pict * (frame_delay * 0.5);\n    this->video_clock += frame_delay;\n\n    return pts;\n}\n\nclass VideoThread\n{\npublic:\n    VideoThread(VideoState* self)\n        : mVideoState(self)\n        , mThread([this]\n        {\n            try\n            {\n                run();\n            }\n            catch(std::exception& e)\n            {\n                std::cerr << \"An error occurred playing the video: \" << e.what () << std::endl;\n            }\n        })\n    {\n    }\n\n    ~VideoThread()\n    {\n        mThread.join();\n    }\n\n    void run()\n    {\n        VideoState* self = mVideoState;\n        AVPacket packetData;\n        av_init_packet(&packetData);\n        std::unique_ptr<AVPacket, AVPacketUnref> packet(&packetData);\n        std::unique_ptr<AVFrame, AVFrameFree> pFrame{av_frame_alloc()};\n\n        while(self->videoq.get(packet.get(), self) >= 0)\n        {\n            if(packet->data == flush_pkt.data)\n            {\n                avcodec_flush_buffers(self->video_ctx);\n\n                self->pictq_mutex.lock();\n                self->pictq_size = 0;\n                self->pictq_rindex = 0;\n                self->pictq_windex = 0;\n                self->pictq_mutex.unlock();\n\n                self->frame_last_pts = packet->pts * av_q2d((*self->video_st)->time_base);\n                continue;\n            }\n\n            // Decode video frame\n            int ret = avcodec_send_packet(self->video_ctx, packet.get());\n            // EAGAIN is not expected\n            if (ret < 0)\n                throw std::runtime_error(\"Error decoding video frame\");\n\n            while (!ret)\n            {\n                ret = avcodec_receive_frame(self->video_ctx, pFrame.get());\n                if (!ret)\n                {\n                    double pts = pFrame->best_effort_timestamp;\n                    pts *= av_q2d((*self->video_st)->time_base);\n\n                    pts = self->synchronize_video(*pFrame, pts);\n\n                    if(self->queue_picture(*pFrame, pts) < 0)\n                        break;\n                }\n            }\n        }\n    }\n\nprivate:\n    VideoState* mVideoState;\n    std::thread mThread;\n};\n\nclass ParseThread\n{\npublic:\n    ParseThread(VideoState* self)\n        : mVideoState(self)\n        , mThread([this] { run(); })\n    {\n    }\n\n    ~ParseThread()\n    {\n        mThread.join();\n    }\n\n    void run()\n    {\n        VideoState* self = mVideoState;\n\n        AVFormatContext *pFormatCtx = self->format_ctx;\n        AVPacket packetData;\n        av_init_packet(&packetData);\n        std::unique_ptr<AVPacket, AVPacketUnref> packet(&packetData);\n\n        try\n        {\n            if(!self->video_st && !self->audio_st)\n                throw std::runtime_error(\"No streams to decode\");\n\n            // main decode loop\n            while(!self->mQuit)\n            {\n                if(self->mSeekRequested)\n                {\n                    uint64_t seek_target = self->mSeekPos;\n                    int streamIndex = -1;\n\n                    int videoStreamIndex = -1;;\n                    int audioStreamIndex = -1;\n                    if (self->video_st)\n                        videoStreamIndex = self->video_st - self->format_ctx->streams;\n                    if (self->audio_st)\n                        audioStreamIndex = self->audio_st - self->format_ctx->streams;\n\n                    if(videoStreamIndex >= 0)\n                        streamIndex = videoStreamIndex;\n                    else if(audioStreamIndex >= 0)\n                        streamIndex = audioStreamIndex;\n\n                    uint64_t timestamp = seek_target;\n\n                    // QtCreator's highlighter doesn't like AV_TIME_BASE_Q's {} initializer for some reason\n                    AVRational avTimeBaseQ = AVRational(); // = AV_TIME_BASE_Q;\n                    avTimeBaseQ.num = 1;\n                    avTimeBaseQ.den = AV_TIME_BASE;\n\n                    if(streamIndex >= 0)\n                        timestamp = av_rescale_q(seek_target, avTimeBaseQ, self->format_ctx->streams[streamIndex]->time_base);\n\n                    // AVSEEK_FLAG_BACKWARD appears to be needed, otherwise ffmpeg may seek to a keyframe *after* the given time\n                    // we want to seek to any keyframe *before* the given time, so we can continue decoding as normal from there on\n                    if(av_seek_frame(self->format_ctx, streamIndex, timestamp, AVSEEK_FLAG_BACKWARD) < 0)\n                    {\n// In the FFMpeg 4.0 a \"filename\" field was replaced by \"url\"\n#if LIBAVCODEC_VERSION_INT < 3805796\n                        std::cerr << \"Error seeking \" << self->format_ctx->filename << std::endl;\n#else\n                        std::cerr << \"Error seeking \" << self->format_ctx->url << std::endl;\n#endif\n                    }\n                    else\n                    {\n                        // Clear the packet queues and put a special packet with the new clock time\n                        if(audioStreamIndex >= 0)\n                        {\n                            self->audioq.clear();\n                            flush_pkt.pts = av_rescale_q(seek_target, avTimeBaseQ,\n                                self->format_ctx->streams[audioStreamIndex]->time_base);\n                            self->audioq.put(&flush_pkt);\n                        }\n                        if(videoStreamIndex >= 0)\n                        {\n                            self->videoq.clear();\n                            flush_pkt.pts = av_rescale_q(seek_target, avTimeBaseQ,\n                                self->format_ctx->streams[videoStreamIndex]->time_base);\n                            self->videoq.put(&flush_pkt);\n                        }\n                        self->pictq_mutex.lock();\n                        self->pictq_size = 0;\n                        self->pictq_rindex = 0;\n                        self->pictq_windex = 0;\n                        self->pictq_mutex.unlock();\n                        self->mExternalClock.set(seek_target);\n                    }\n                    self->mSeekRequested = false;\n                }\n\n\n                if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) ||\n                   (self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE))\n                {\n                    std::this_thread::sleep_for(std::chrono::milliseconds(10));\n                    continue;\n                }\n\n                if(av_read_frame(pFormatCtx, packet.get()) < 0)\n                {\n                    if (self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0 && self->pictq_size == 0)\n                        self->mVideoEnded = true;\n                    continue;\n                }\n                else\n                    self->mVideoEnded = false;\n\n                // Is this a packet from the video stream?\n                if(self->video_st && packet->stream_index == self->video_st-pFormatCtx->streams)\n                    self->videoq.put(packet.get());\n                else if(self->audio_st && packet->stream_index == self->audio_st-pFormatCtx->streams)\n                    self->audioq.put(packet.get());\n                else\n                    av_packet_unref(packet.get());\n            }\n        }\n        catch(std::exception& e) {\n            std::cerr << \"An error occurred playing the video: \" << e.what () << std::endl;\n        }\n\n        self->mQuit = true;\n    }\n\nprivate:\n    VideoState* mVideoState;\n    std::thread mThread;\n};\n\n\nbool VideoState::update()\n{\n    this->video_refresh();\n    return !this->mVideoEnded;\n}\n\n\nint VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx)\n{\n    const AVCodec *codec;\n\n    if(stream_index < 0 || stream_index >= static_cast<int>(pFormatCtx->nb_streams))\n        return -1;\n\n    // Get a pointer to the codec context for the video stream\n    codec = avcodec_find_decoder(pFormatCtx->streams[stream_index]->codecpar->codec_id);\n    if(!codec)\n    {\n        fprintf(stderr, \"Unsupported codec!\\n\");\n        return -1;\n    }\n\n    switch(pFormatCtx->streams[stream_index]->codecpar->codec_type)\n    {\n    case AVMEDIA_TYPE_AUDIO:\n        this->audio_st = pFormatCtx->streams + stream_index;\n\n        // Get a pointer to the codec context for the video stream\n        this->audio_ctx = avcodec_alloc_context3(codec);\n        avcodec_parameters_to_context(this->audio_ctx, pFormatCtx->streams[stream_index]->codecpar);\n\n// This is not needed anymore above FFMpeg version 4.0\n#if LIBAVCODEC_VERSION_INT < 3805796\n        av_codec_set_pkt_timebase(this->audio_ctx, pFormatCtx->streams[stream_index]->time_base);\n#endif\n\n        if (avcodec_open2(this->audio_ctx, codec, nullptr) < 0)\n        {\n            fprintf(stderr, \"Unsupported codec!\\n\");\n            return -1;\n        }\n\n        if (!mAudioFactory)\n        {\n            std::cerr << \"No audio factory registered, can not play audio stream\" << std::endl;\n            avcodec_free_context(&this->audio_ctx);\n            this->audio_st = nullptr;\n            return -1;\n        }\n\n        mAudioDecoder = mAudioFactory->createDecoder(this);\n        if (!mAudioDecoder.get())\n        {\n            std::cerr << \"Failed to create audio decoder, can not play audio stream\" << std::endl;\n            avcodec_free_context(&this->audio_ctx);\n            this->audio_st = nullptr;\n            return -1;\n        }\n        mAudioDecoder->setupFormat();\n        break;\n\n    case AVMEDIA_TYPE_VIDEO:\n        this->video_st = pFormatCtx->streams + stream_index;\n\n        // Get a pointer to the codec context for the video stream\n        this->video_ctx = avcodec_alloc_context3(codec);\n        avcodec_parameters_to_context(this->video_ctx, pFormatCtx->streams[stream_index]->codecpar);\n\n// This is not needed anymore above FFMpeg version 4.0\n#if LIBAVCODEC_VERSION_INT < 3805796\n        av_codec_set_pkt_timebase(this->video_ctx, pFormatCtx->streams[stream_index]->time_base);\n#endif\n\n        if (avcodec_open2(this->video_ctx, codec, nullptr) < 0)\n        {\n            fprintf(stderr, \"Unsupported codec!\\n\");\n            return -1;\n        }\n\n        this->video_thread.reset(new VideoThread(this));\n        break;\n\n    default:\n        break;\n    }\n\n    return 0;\n}\n\nvoid VideoState::init(std::shared_ptr<std::istream> inputstream, const std::string &name)\n{\n    int video_index = -1;\n    int audio_index = -1;\n    unsigned int i;\n\n    this->av_sync_type = AV_SYNC_DEFAULT;\n    this->mQuit = false;\n\n    this->stream = inputstream;\n    if(!this->stream.get())\n        throw std::runtime_error(\"Failed to open video resource\");\n\n    AVIOContext *ioCtx = avio_alloc_context(nullptr, 0, 0, this, istream_read, istream_write, istream_seek);\n    if(!ioCtx) throw std::runtime_error(\"Failed to allocate AVIOContext\");\n\n    this->format_ctx = avformat_alloc_context();\n    if(this->format_ctx)\n        this->format_ctx->pb = ioCtx;\n\n    // Open video file\n    ///\n    /// format_ctx->pb->buffer must be freed by hand,\n    /// if not, valgrind will show memleak, see:\n    ///\n    /// https://trac.ffmpeg.org/ticket/1357\n    ///\n    if(!this->format_ctx || avformat_open_input(&this->format_ctx, name.c_str(), nullptr, nullptr))\n    {\n        if (this->format_ctx != nullptr)\n        {\n          if (this->format_ctx->pb != nullptr)\n          {\n              av_freep(&this->format_ctx->pb->buffer);\n#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100)\n              avio_context_free(&this->format_ctx->pb);\n#else\n              av_freep(&this->format_ctx->pb);\n#endif\n          }\n        }\n        // \"Note that a user-supplied AVFormatContext will be freed on failure.\"\n        this->format_ctx = nullptr;\n#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100)\n        avio_context_free(&ioCtx);\n#else\n        av_freep(&ioCtx);\n#endif\n        throw std::runtime_error(\"Failed to open video input\");\n    }\n\n    // Retrieve stream information\n    if(avformat_find_stream_info(this->format_ctx, nullptr) < 0)\n        throw std::runtime_error(\"Failed to retrieve stream information\");\n\n    // Dump information about file onto standard error\n    av_dump_format(this->format_ctx, 0, name.c_str(), 0);\n\n    for(i = 0;i < this->format_ctx->nb_streams;i++)\n    {\n        if(this->format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0)\n            video_index = i;\n        if(this->format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0)\n            audio_index = i;\n    }\n\n    mExternalClock.set(0);\n\n    if(audio_index >= 0)\n        this->stream_open(audio_index, this->format_ctx);\n\n    if(video_index >= 0)\n    {\n        this->stream_open(video_index, this->format_ctx);\n    }\n\n\n    this->parse_thread.reset(new ParseThread(this));\n}\n\nvoid VideoState::deinit()\n{\n    this->mQuit = true;\n\n    this->audioq.flush();\n    this->videoq.flush();\n\n    mAudioDecoder.reset();\n\n    if (this->parse_thread.get())\n    {\n        this->parse_thread.reset();\n    }\n    if (this->video_thread.get())\n    {\n        this->video_thread.reset();\n    }\n\n    if(this->audio_ctx)\n        avcodec_free_context(&this->audio_ctx);\n    this->audio_st = nullptr;\n    this->audio_ctx = nullptr;\n    if(this->video_ctx)\n        avcodec_free_context(&this->video_ctx);\n    this->video_st = nullptr;\n    this->video_ctx = nullptr;\n\n    if(this->sws_context)\n        sws_freeContext(this->sws_context);\n    this->sws_context = nullptr;\n\n    if(this->format_ctx)\n    {\n        ///\n        /// format_ctx->pb->buffer must be freed by hand,\n        /// if not, valgrind will show memleak, see:\n        ///\n        /// https://trac.ffmpeg.org/ticket/1357\n        ///\n        if (this->format_ctx->pb != nullptr)\n        {\n            av_freep(&this->format_ctx->pb->buffer);\n#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100)\n            avio_context_free(&this->format_ctx->pb);\n#else\n            av_freep(&this->format_ctx->pb);\n#endif\n        }\n        avformat_close_input(&this->format_ctx);\n    }\n\n    if (mTexture)\n    {\n        // reset Image separately, it's pointing to *this and there might still be outside references to mTexture\n        mTexture->setImage(nullptr);\n        mTexture = nullptr;\n    }\n\n    // Dellocate RGBA frame queue.\n    for (std::size_t i = 0; i < VIDEO_PICTURE_ARRAY_SIZE; ++i)\n        this->pictq[i].rgbaFrame = nullptr;\n\n}\n\ndouble VideoState::get_external_clock()\n{\n    return mExternalClock.get() / 1000000.0;\n}\n\ndouble VideoState::get_master_clock()\n{\n    if(this->av_sync_type == AV_SYNC_VIDEO_MASTER)\n        return this->get_video_clock();\n    if(this->av_sync_type == AV_SYNC_AUDIO_MASTER)\n        return this->get_audio_clock();\n    return this->get_external_clock();\n}\n\ndouble VideoState::get_video_clock()\n{\n    return this->frame_last_pts;\n}\n\ndouble VideoState::get_audio_clock()\n{\n    if (!mAudioDecoder.get())\n        return 0.0;\n    return mAudioDecoder->getAudioClock();\n}\n\nvoid VideoState::setPaused(bool isPaused)\n{\n    this->mPaused = isPaused;\n    mExternalClock.setPaused(isPaused);\n}\n\nvoid VideoState::seekTo(double time)\n{\n    time = std::max(0.0, time);\n    time = std::min(getDuration(), time);\n    mSeekPos = (uint64_t) (time * AV_TIME_BASE);\n    mSeekRequested = true;\n}\n\ndouble VideoState::getDuration()\n{\n    return this->format_ctx->duration / 1000000.0;\n}\n\n\nExternalClock::ExternalClock()\n    : mTimeBase(av_gettime())\n    , mPausedAt(0)\n    , mPaused(false)\n{\n}\n\nvoid ExternalClock::setPaused(bool paused)\n{\n    std::lock_guard<std::mutex> lock(mMutex);\n    if (mPaused == paused)\n        return;\n    if (paused)\n    {\n        mPausedAt = av_gettime() - mTimeBase;\n    }\n    else\n        mTimeBase = av_gettime() - mPausedAt;\n    mPaused = paused;\n}\n\nuint64_t ExternalClock::get()\n{\n    std::lock_guard<std::mutex> lock(mMutex);\n    if (mPaused)\n        return mPausedAt;\n    else\n        return av_gettime() - mTimeBase;\n}\n\nvoid ExternalClock::set(uint64_t time)\n{\n    std::lock_guard<std::mutex> lock(mMutex);\n    mTimeBase = av_gettime() - time;\n    mPausedAt = time;\n}\n\n}\n\n"
  },
  {
    "path": "extern/osg-ffmpeg-videoplayer/videostate.hpp",
    "content": "#ifndef VIDEOPLAYER_VIDEOSTATE_H\n#define VIDEOPLAYER_VIDEOSTATE_H\n\n#include <stdint.h>\n#include <atomic>\n#include <vector>\n#include <memory>\n#include <string>\n#include <mutex>\n#include <condition_variable>\n\n#include <osg/ref_ptr>\nnamespace osg\n{\n    class Texture2D;\n}\n\n#if defined(_MSC_VER)\n    #pragma warning (push)\n    #pragma warning (disable : 4244)\n#endif\n\nextern \"C\"\n{\n#include <libavcodec/avcodec.h>\n#include <libavformat/avformat.h>\n#include <libavutil/imgutils.h>\n#include <libavutil/channel_layout.h>\n\n// From version 54.56 binkaudio encoding format changed from S16 to FLTP. See:\n// https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d\n// https://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872\n#include <libswresample/swresample.h>\n}\n\n#if defined(_MSC_VER)\n    #pragma warning (pop)\n#endif\n\n#include \"videodefs.hpp\"\n\n#define VIDEO_PICTURE_QUEUE_SIZE 50\n// allocate one extra to make sure we do not overwrite the osg::Image currently set on the texture\n#define VIDEO_PICTURE_ARRAY_SIZE (VIDEO_PICTURE_QUEUE_SIZE+1)\n\nextern \"C\"\n{\n    struct SwsContext;\n    struct AVPacketList;\n    struct AVPacket;\n    struct AVFormatContext;\n    struct AVStream;\n    struct AVFrame;\n}\n\nnamespace Video\n{\n\nstruct VideoState;\n\nclass MovieAudioFactory;\nclass MovieAudioDecoder;\nclass VideoThread;\nclass ParseThread;\n\nstruct ExternalClock\n{\n    ExternalClock();\n\n    uint64_t mTimeBase;\n    uint64_t mPausedAt;\n    bool mPaused;\n\n    std::mutex mMutex;\n\n    void setPaused(bool paused);\n    uint64_t get();\n    void set(uint64_t time);\n};\n\nstruct PacketQueue {\n    PacketQueue()\n      : first_pkt(nullptr), last_pkt(nullptr), flushing(false), nb_packets(0), size(0)\n    { }\n    ~PacketQueue()\n    { clear(); }\n\n    AVPacketList *first_pkt, *last_pkt;\n    std::atomic<bool> flushing;\n    std::atomic<int> nb_packets;\n    std::atomic<int> size;\n\n    std::mutex mutex;\n    std::condition_variable cond;\n\n    void put(AVPacket *pkt);\n    int get(AVPacket *pkt, VideoState *is);\n\n    void flush();\n    void clear();\n};\n\nstruct VideoPicture {\n    VideoPicture() : pts(0.0)\n    { }\n\n    struct AVFrameDeleter {\n        void operator()(AVFrame* frame) const;\n    };\n\n    // Sets frame dimensions.\n    // Must be called before writing to `rgbaFrame`.\n    // Return -1 on error.\n    int set_dimensions(int w, int h);\n\n    std::unique_ptr<AVFrame, AVFrameDeleter> rgbaFrame;\n    double pts;\n};\n\nstruct VideoState {\n    VideoState();\n    ~VideoState();\n\n    void setAudioFactory(MovieAudioFactory* factory);\n\n    void init(std::shared_ptr<std::istream> inputstream, const std::string& name);\n    void deinit();\n\n    void setPaused(bool isPaused);\n    void seekTo(double time);\n\n    double getDuration();\n\n    int stream_open(int stream_index, AVFormatContext *pFormatCtx);\n\n    bool update();\n\n    static void video_thread_loop(VideoState *is);\n    static void decode_thread_loop(VideoState *is);\n\n    void video_display(VideoPicture* vp);\n    void video_refresh();\n\n    int queue_picture(const AVFrame &pFrame, double pts);\n    double synchronize_video(const AVFrame &src_frame, double pts);\n\n    double get_audio_clock();\n    double get_video_clock();\n    double get_external_clock();\n    double get_master_clock();\n\n    static int istream_read(void *user_data, uint8_t *buf, int buf_size);\n    static int istream_write(void *user_data, uint8_t *buf, int buf_size);\n    static int64_t istream_seek(void *user_data, int64_t offset, int whence);\n\n    osg::ref_ptr<osg::Texture2D> mTexture;\n\n    MovieAudioFactory* mAudioFactory;\n    std::shared_ptr<MovieAudioDecoder> mAudioDecoder;\n\n    ExternalClock mExternalClock;\n\n    std::shared_ptr<std::istream> stream;\n    AVFormatContext* format_ctx;\n    AVCodecContext* video_ctx;\n    AVCodecContext* audio_ctx;\n\n    int av_sync_type;\n\n    AVStream**  audio_st;\n    PacketQueue audioq;\n\n    uint8_t* mFlushPktData;\n\n    AVStream**  video_st;\n    double      frame_last_pts;\n    double      video_clock; ///<pts of last decoded frame / predicted pts of next decoded frame\n    PacketQueue videoq;\n    SwsContext*  sws_context;\n    int sws_context_w, sws_context_h;\n    VideoPicture pictq[VIDEO_PICTURE_ARRAY_SIZE];\n    int          pictq_size, pictq_rindex, pictq_windex;\n    std::mutex pictq_mutex;\n    std::condition_variable pictq_cond;\n\n    std::unique_ptr<ParseThread> parse_thread;\n    std::unique_ptr<VideoThread> video_thread;\n\n    std::atomic<bool> mSeekRequested;\n    uint64_t mSeekPos;\n\n    std::atomic<bool> mVideoEnded;\n    std::atomic<bool> mPaused;\n    std::atomic<bool> mQuit;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "extern/osgQt/CMakeLists.txt",
    "content": "set(OSGQT_LIBRARY \"osgQt\")\n\n# Sources\n\nset(OSGQT_SOURCE_FILES\n    GraphicsWindowQt.cpp\n)\n\nadd_library(${OSGQT_LIBRARY} STATIC ${OSGQT_SOURCE_FILES})\n\ntarget_link_libraries(${OSGQT_LIBRARY} Qt5::Core Qt5::OpenGL)\n\nlink_directories(${CMAKE_CURRENT_BINARY_DIR})\n\nset(EXTERN_OSGQT_LIBRARY ${OSGQT_LIBRARY} PARENT_SCOPE)\n"
  },
  {
    "path": "extern/osgQt/GraphicsWindowQt",
    "content": "/* -*-c++-*- OpenSceneGraph - Copyright (C) 2009 Wang Rui\n *\n * This library is open source and may be redistributed and/or modified under\n * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or\n * (at your option) any later version.  The full license is in LICENSE file\n * included with this distribution, and on the openscenegraph.org website.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * OpenSceneGraph Public License for more details.\n*/\n\n#ifndef OSGVIEWER_GRAPHICSWINDOWQT\n#define OSGVIEWER_GRAPHICSWINDOWQT\n\n#include <osgViewer/GraphicsWindow>\n\n#include <QMutex>\n#include <QEvent>\n#include <QQueue>\n#include <QSet>\n#include <QGLWidget>\n\nclass QInputEvent;\nclass QGestureEvent;\n\nnamespace osgViewer {\n    class ViewerBase;\n}\n\nnamespace osgQt\n{\n\n// forward declarations\nclass GraphicsWindowQt;\n\nclass GLWidget : public QGLWidget\n{\n    typedef QGLWidget inherited;\n\npublic:\n\n    GLWidget( QWidget* parent = nullptr, const QGLWidget* shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags());\n    GLWidget( QGLContext* context, QWidget* parent = nullptr, const QGLWidget* shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags());\n    GLWidget( const QGLFormat& format, QWidget* parent = nullptr, const QGLWidget* shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags());\n    virtual ~GLWidget();\n\n    inline void setGraphicsWindow( GraphicsWindowQt* gw ) { _gw = gw; }\n    inline GraphicsWindowQt* getGraphicsWindow() { return _gw; }\n    inline const GraphicsWindowQt* getGraphicsWindow() const { return _gw; }\n\nprotected:\n\n    int getNumDeferredEvents()\n    {\n        QMutexLocker lock(&_deferredEventQueueMutex);\n        return _deferredEventQueue.count();\n    }\n    void enqueueDeferredEvent(QEvent::Type eventType, QEvent::Type removeEventType = QEvent::None)\n    {\n        QMutexLocker lock(&_deferredEventQueueMutex);\n\n        if (removeEventType != QEvent::None)\n        {\n            if (_deferredEventQueue.removeOne(removeEventType))\n                _eventCompressor.remove(eventType);\n        }\n\n        if (_eventCompressor.find(eventType) == _eventCompressor.end())\n        {\n            _deferredEventQueue.enqueue(eventType);\n            _eventCompressor.insert(eventType);\n        }\n    }\n    void processDeferredEvents();\n\n    friend class GraphicsWindowQt;\n    GraphicsWindowQt* _gw;\n\n    QMutex _deferredEventQueueMutex;\n    QQueue<QEvent::Type> _deferredEventQueue;\n    QSet<QEvent::Type> _eventCompressor;\n\n    qreal _devicePixelRatio;\n\n    void resizeEvent( QResizeEvent* event ) override;\n    void moveEvent( QMoveEvent* event ) override;\n    void glDraw() override;\n    bool event( QEvent* event ) override;\n};\n\nclass GraphicsWindowQt : public osgViewer::GraphicsWindow\n{\npublic:\n    GraphicsWindowQt( osg::GraphicsContext::Traits* traits, QWidget* parent = nullptr, const QGLWidget* shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags() );\n    GraphicsWindowQt( GLWidget* widget );\n    virtual ~GraphicsWindowQt();\n\n    inline GLWidget* getGLWidget() { return _widget; }\n    inline const GLWidget* getGLWidget() const { return _widget; }\n\n    /// deprecated\n    inline GLWidget* getGraphWidget() { return _widget; }\n    /// deprecated\n    inline const GLWidget* getGraphWidget() const { return _widget; }\n\n    struct WindowData : public osg::Referenced\n    {\n        WindowData( GLWidget* widget = nullptr, QWidget* parent = nullptr ): _widget(widget), _parent(parent) {}\n        GLWidget* _widget;\n        QWidget* _parent;\n    };\n\n    bool init( QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f );\n\n    static QGLFormat traits2qglFormat( const osg::GraphicsContext::Traits* traits );\n    static void qglFormat2traits( const QGLFormat& format, osg::GraphicsContext::Traits* traits );\n    static osg::GraphicsContext::Traits* createTraits( const QGLWidget* widget );\n\n    bool setWindowRectangleImplementation( int x, int y, int width, int height ) override;\n    void getWindowRectangle( int& x, int& y, int& width, int& height ) override;\n    bool setWindowDecorationImplementation( bool windowDecoration ) override;\n    bool getWindowDecoration() const override;\n    void grabFocus() override;\n    void grabFocusIfPointerInWindow( )override;\n    void raiseWindow() override;\n    void setWindowName( const std::string& name ) override;\n    std::string getWindowName() override;\n    void useCursor( bool cursorOn ) override;\n    void setCursor( MouseCursor cursor ) override;\n\n    bool valid() const override;\n    bool realizeImplementation() override;\n    bool isRealizedImplementation() const override;\n    void closeImplementation() override;\n    bool makeCurrentImplementation() override;\n    bool releaseContextImplementation() override;\n    void swapBuffersImplementation() override;\n    void runOperations() override;\n\n    void requestWarpPointer( float x, float y ) override;\n\nprotected:\n\n    friend class GLWidget;\n    GLWidget* _widget;\n    bool _ownsWidget;\n    QCursor _currentCursor;\n    bool _realized;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "extern/osgQt/GraphicsWindowQt.cpp",
    "content": "/* -*-c++-*- OpenSceneGraph - Copyright (C) 2009 Wang Rui\n *\n * This library is open source and may be redistributed and/or modified under\n * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or\n * (at your option) any later version.  The full license is in LICENSE file\n * included with this distribution, and on the openscenegraph.org website.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * OpenSceneGraph Public License for more details.\n*/\n\n#include \"GraphicsWindowQt\"\n\n#include <osg/DeleteHandler>\n#include <osgViewer/ViewerBase>\n#include <QInputEvent>\n#include <QPointer>\n#include <QWindow>\n\nusing namespace osgQt;\n\n#define GETDEVICEPIXELRATIO() devicePixelRatio()\n\nGLWidget::GLWidget( QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f)\n: QGLWidget(parent, shareWidget, f), _gw( nullptr )\n{\n    _devicePixelRatio = GETDEVICEPIXELRATIO();\n}\n\nGLWidget::GLWidget( QGLContext* context, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f)\n: QGLWidget(context, parent, shareWidget, f), _gw( nullptr )\n{\n    _devicePixelRatio = GETDEVICEPIXELRATIO();\n}\n\nGLWidget::GLWidget( const QGLFormat& format, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f)\n: QGLWidget(format, parent, shareWidget, f), _gw( nullptr )\n{\n    _devicePixelRatio = GETDEVICEPIXELRATIO();\n}\n\nGLWidget::~GLWidget()\n{\n    // close GraphicsWindowQt and remove the reference to us\n    if( _gw )\n    {\n        _gw->close();\n        _gw->_widget = nullptr;\n        _gw = nullptr;\n    }\n}\n\nvoid GLWidget::processDeferredEvents()\n{\n    QQueue<QEvent::Type> deferredEventQueueCopy;\n    {\n        QMutexLocker lock(&_deferredEventQueueMutex);\n        deferredEventQueueCopy = _deferredEventQueue;\n        _eventCompressor.clear();\n        _deferredEventQueue.clear();\n    }\n\n    while (!deferredEventQueueCopy.isEmpty())\n    {\n        QEvent event(deferredEventQueueCopy.dequeue());\n        QGLWidget::event(&event);\n    }\n}\n\nbool GLWidget::event( QEvent* event )\n{\n\n    // QEvent::Hide\n    //\n    // workaround \"Qt-workaround\" that does glFinish before hiding the widget\n    // (the Qt workaround was seen at least in Qt 4.6.3 and 4.7.0)\n    //\n    // Qt makes the context current, performs glFinish, and releases the context.\n    // This makes the problem in OSG multithreaded environment as the context\n    // is active in another thread, thus it can not be made current for the purpose\n    // of glFinish in this thread.\n\n    // QEvent::ParentChange\n    //\n    // Reparenting GLWidget may create a new underlying window and a new GL context.\n    // Qt will then call doneCurrent on the GL context about to be deleted. The thread\n    // where old GL context was current has no longer current context to render to and\n    // we cannot make new GL context current in this thread.\n\n    // We workaround above problems by deferring execution of problematic event requests.\n    // These events has to be enqueue and executed later in a main GUI thread (GUI operations\n    // outside the main thread are not allowed) just before makeCurrent is called from the\n    // right thread. The good place for doing that is right after swap in a swapBuffersImplementation.\n\n    if (event->type() == QEvent::Hide)\n    {\n        // enqueue only the last of QEvent::Hide and QEvent::Show\n        enqueueDeferredEvent(QEvent::Hide, QEvent::Show);\n        return true;\n    }\n    else if (event->type() == QEvent::Show)\n    {\n        // enqueue only the last of QEvent::Show or QEvent::Hide\n        enqueueDeferredEvent(QEvent::Show, QEvent::Hide);\n        return true;\n    }\n    else if (event->type() == QEvent::ParentChange)\n    {\n        // enqueue only the last QEvent::ParentChange\n        enqueueDeferredEvent(QEvent::ParentChange);\n        return true;\n    }\n    else if (event->type() == QEvent::PlatformSurface && static_cast<QPlatformSurfaceEvent*>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)\n    {\n        if (_gw)\n            _gw->close();\n    }\n\n    // perform regular event handling\n    return QGLWidget::event( event );\n}\n\nvoid GLWidget::resizeEvent( QResizeEvent* event )\n{\n    if (_gw == nullptr || !_gw->valid())\n        return;\n    const QSize& size = event->size();\n\n    int scaled_width = static_cast<int>(size.width()*_devicePixelRatio);\n    int scaled_height = static_cast<int>(size.height()*_devicePixelRatio);\n    _gw->resized( x(), y(), scaled_width,  scaled_height);\n    _gw->getEventQueue()->windowResize( x(), y(), scaled_width, scaled_height );\n    _gw->requestRedraw();\n}\n\nvoid GLWidget::moveEvent( QMoveEvent* event )\n{\n    if (_gw == nullptr || !_gw->valid())\n        return;\n    const QPoint& pos = event->pos();\n    int scaled_width = static_cast<int>(width()*_devicePixelRatio);\n    int scaled_height = static_cast<int>(height()*_devicePixelRatio);\n    _gw->resized( pos.x(), pos.y(), scaled_width,  scaled_height );\n    _gw->getEventQueue()->windowResize( pos.x(), pos.y(), scaled_width,  scaled_height );\n}\n\nvoid GLWidget::glDraw()\n{\n    _gw->requestRedraw();\n}\n\nGraphicsWindowQt::GraphicsWindowQt( osg::GraphicsContext::Traits* traits, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f )\n:   _realized(false)\n{\n\n    _widget = nullptr;\n    _traits = traits;\n    init( parent, shareWidget, f );\n}\n\nGraphicsWindowQt::GraphicsWindowQt( GLWidget* widget )\n:   _realized(false)\n{\n    _widget = widget;\n    _traits = _widget ? createTraits( _widget ) : new osg::GraphicsContext::Traits;\n    init( nullptr, nullptr, Qt::WindowFlags() );\n}\n\nGraphicsWindowQt::~GraphicsWindowQt()\n{\n    close();\n\n    // remove reference from GLWidget\n    if ( _widget )\n        _widget->_gw = nullptr;\n}\n\nbool GraphicsWindowQt::init( QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f )\n{\n    // update _widget and parent by WindowData\n    WindowData* windowData = _traits.get() ? dynamic_cast<WindowData*>(_traits->inheritedWindowData.get()) : nullptr;\n    if ( !_widget )\n        _widget = windowData ? windowData->_widget : nullptr;\n    if ( !parent )\n        parent = windowData ? windowData->_parent : nullptr;\n\n    // create widget if it does not exist\n    _ownsWidget = _widget == nullptr;\n    if ( !_widget )\n    {\n        // shareWidget\n        if ( !shareWidget ) {\n            GraphicsWindowQt* sharedContextQt = dynamic_cast<GraphicsWindowQt*>(_traits->sharedContext.get());\n            if ( sharedContextQt )\n                shareWidget = sharedContextQt->getGLWidget();\n        }\n\n        // WindowFlags\n        Qt::WindowFlags flags = f | Qt::Window | Qt::CustomizeWindowHint;\n        if ( _traits->windowDecoration )\n            flags |= Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;\n\n        // create widget\n        _widget = new GLWidget( traits2qglFormat( _traits.get() ), parent, shareWidget, flags );\n    }\n\n    // set widget name and position\n    // (do not set it when we inherited the widget)\n    if ( _ownsWidget )\n    {\n        _widget->setWindowTitle( _traits->windowName.c_str() );\n        _widget->move( _traits->x, _traits->y );\n        if ( !_traits->supportsResize ) _widget->setFixedSize( _traits->width, _traits->height );\n        else _widget->resize( _traits->width, _traits->height );\n    }\n\n    // initialize widget properties\n    _widget->setAutoBufferSwap( false );\n    _widget->setMouseTracking( true );\n    _widget->setGraphicsWindow( this );\n    useCursor( _traits->useCursor );\n\n    // initialize State\n    setState( new osg::State );\n    getState()->setGraphicsContext(this);\n\n    // initialize contextID\n    if ( _traits.valid() && _traits->sharedContext.valid() )\n    {\n        getState()->setContextID( _traits->sharedContext->getState()->getContextID() );\n        incrementContextIDUsageCount( getState()->getContextID() );\n    }\n    else\n    {\n        getState()->setContextID( osg::GraphicsContext::createNewContextID() );\n    }\n\n    // make sure the event queue has the correct window rectangle size and input range\n    getEventQueue()->syncWindowRectangleWithGraphicsContext();\n\n    return true;\n}\n\nQGLFormat GraphicsWindowQt::traits2qglFormat( const osg::GraphicsContext::Traits* traits )\n{\n    QGLFormat format( QGLFormat::defaultFormat() );\n\n    format.setAlphaBufferSize( traits->alpha );\n    format.setRedBufferSize( traits->red );\n    format.setGreenBufferSize( traits->green );\n    format.setBlueBufferSize( traits->blue );\n    format.setDepthBufferSize( traits->depth );\n    format.setStencilBufferSize( traits->stencil );\n    format.setSampleBuffers( traits->sampleBuffers );\n    format.setSamples( traits->samples );\n\n    format.setAlpha( traits->alpha>0 );\n    format.setDepth( traits->depth>0 );\n    format.setStencil( traits->stencil>0 );\n    format.setDoubleBuffer( traits->doubleBuffer );\n    format.setSwapInterval( traits->vsync ? 1 : 0 );\n    format.setStereo( traits->quadBufferStereo ? 1 : 0 );\n\n    return format;\n}\n\nvoid GraphicsWindowQt::qglFormat2traits( const QGLFormat& format, osg::GraphicsContext::Traits* traits )\n{\n    traits->red = format.redBufferSize();\n    traits->green = format.greenBufferSize();\n    traits->blue = format.blueBufferSize();\n    traits->alpha = format.alpha() ? format.alphaBufferSize() : 0;\n    traits->depth = format.depth() ? format.depthBufferSize() : 0;\n    traits->stencil = format.stencil() ? format.stencilBufferSize() : 0;\n\n    traits->sampleBuffers = format.sampleBuffers() ? 1 : 0;\n    traits->samples = format.samples();\n\n    traits->quadBufferStereo = format.stereo();\n    traits->doubleBuffer = format.doubleBuffer();\n\n    traits->vsync = format.swapInterval() >= 1;\n}\n\nosg::GraphicsContext::Traits* GraphicsWindowQt::createTraits( const QGLWidget* widget )\n{\n    osg::GraphicsContext::Traits *traits = new osg::GraphicsContext::Traits;\n\n    qglFormat2traits( widget->format(), traits );\n\n    QRect r = widget->geometry();\n    traits->x = r.x();\n    traits->y = r.y();\n    traits->width = r.width();\n    traits->height = r.height();\n\n    traits->windowName = widget->windowTitle().toLocal8Bit().data();\n    Qt::WindowFlags f = widget->windowFlags();\n    traits->windowDecoration = ( f & Qt::WindowTitleHint ) &&\n                            ( f & Qt::WindowMinMaxButtonsHint ) &&\n                            ( f & Qt::WindowSystemMenuHint );\n    QSizePolicy sp = widget->sizePolicy();\n    traits->supportsResize = sp.horizontalPolicy() != QSizePolicy::Fixed ||\n                            sp.verticalPolicy() != QSizePolicy::Fixed;\n\n    return traits;\n}\n\nbool GraphicsWindowQt::setWindowRectangleImplementation( int x, int y, int width, int height )\n{\n    if ( _widget == nullptr )\n        return false;\n\n    _widget->setGeometry( x, y, width, height );\n    return true;\n}\n\nvoid GraphicsWindowQt::getWindowRectangle( int& x, int& y, int& width, int& height )\n{\n    if ( _widget )\n    {\n        const QRect& geom = _widget->geometry();\n        x = geom.x();\n        y = geom.y();\n        width = geom.width();\n        height = geom.height();\n    }\n}\n\nbool GraphicsWindowQt::setWindowDecorationImplementation( bool windowDecoration )\n{\n    Qt::WindowFlags flags = Qt::Window|Qt::CustomizeWindowHint;//|Qt::WindowStaysOnTopHint;\n    if ( windowDecoration )\n        flags |= Qt::WindowTitleHint|Qt::WindowMinMaxButtonsHint|Qt::WindowSystemMenuHint;\n    _traits->windowDecoration = windowDecoration;\n\n    if ( _widget )\n    {\n        _widget->setWindowFlags( flags );\n\n        return true;\n    }\n\n    return false;\n}\n\nbool GraphicsWindowQt::getWindowDecoration() const\n{\n    return _traits->windowDecoration;\n}\n\nvoid GraphicsWindowQt::grabFocus()\n{\n    if ( _widget )\n        _widget->setFocus( Qt::ActiveWindowFocusReason );\n}\n\nvoid GraphicsWindowQt::grabFocusIfPointerInWindow()\n{\n    if ( _widget->underMouse() )\n        _widget->setFocus( Qt::ActiveWindowFocusReason );\n}\n\nvoid GraphicsWindowQt::raiseWindow()\n{\n    if ( _widget )\n        _widget->raise();\n}\n\nvoid GraphicsWindowQt::setWindowName( const std::string& name )\n{\n    if ( _widget )\n        _widget->setWindowTitle( name.c_str() );\n}\n\nstd::string GraphicsWindowQt::getWindowName()\n{\n    return _widget ? _widget->windowTitle().toStdString() : \"\";\n}\n\nvoid GraphicsWindowQt::useCursor( bool cursorOn )\n{\n    if ( _widget )\n    {\n        _traits->useCursor = cursorOn;\n        if ( !cursorOn ) _widget->setCursor( Qt::BlankCursor );\n        else _widget->setCursor( _currentCursor );\n    }\n}\n\nvoid GraphicsWindowQt::setCursor( MouseCursor cursor )\n{\n    if ( cursor==InheritCursor && _widget )\n    {\n        _widget->unsetCursor();\n    }\n\n    switch ( cursor )\n    {\n    case NoCursor: _currentCursor = Qt::BlankCursor; break;\n    case RightArrowCursor: case LeftArrowCursor: _currentCursor = Qt::ArrowCursor; break;\n    case InfoCursor: _currentCursor = Qt::SizeAllCursor; break;\n    case DestroyCursor: _currentCursor = Qt::ForbiddenCursor; break;\n    case HelpCursor: _currentCursor = Qt::WhatsThisCursor; break;\n    case CycleCursor: _currentCursor = Qt::ForbiddenCursor; break;\n    case SprayCursor: _currentCursor = Qt::SizeAllCursor; break;\n    case WaitCursor: _currentCursor = Qt::WaitCursor; break;\n    case TextCursor: _currentCursor = Qt::IBeamCursor; break;\n    case CrosshairCursor: _currentCursor = Qt::CrossCursor; break;\n    case HandCursor: _currentCursor = Qt::OpenHandCursor; break;\n    case UpDownCursor: _currentCursor = Qt::SizeVerCursor; break;\n    case LeftRightCursor: _currentCursor = Qt::SizeHorCursor; break;\n    case TopSideCursor: case BottomSideCursor: _currentCursor = Qt::UpArrowCursor; break;\n    case LeftSideCursor: case RightSideCursor: _currentCursor = Qt::SizeHorCursor; break;\n    case TopLeftCorner: _currentCursor = Qt::SizeBDiagCursor; break;\n    case TopRightCorner: _currentCursor = Qt::SizeFDiagCursor; break;\n    case BottomRightCorner: _currentCursor = Qt::SizeBDiagCursor; break;\n    case BottomLeftCorner: _currentCursor = Qt::SizeFDiagCursor; break;\n    default: break;\n    };\n    if ( _widget ) _widget->setCursor( _currentCursor );\n}\n\nbool GraphicsWindowQt::valid() const\n{\n    return _widget && _widget->isValid();\n}\n\nbool GraphicsWindowQt::realizeImplementation()\n{\n    // save the current context\n    // note: this will save only Qt-based contexts\n    const QGLContext *savedContext = QGLContext::currentContext();\n\n    // initialize GL context for the widget\n    if ( !valid() )\n        _widget->glInit();\n\n    // make current\n    _realized = true;\n    bool result = makeCurrent();\n    _realized = false;\n\n    // fail if we do not have current context\n    if ( !result )\n    {\n        if ( savedContext )\n            const_cast< QGLContext* >( savedContext )->makeCurrent();\n\n        OSG_WARN << \"Window realize: Can make context current.\" << std::endl;\n        return false;\n    }\n\n    _realized = true;\n\n    // make sure the event queue has the correct window rectangle size and input range\n    getEventQueue()->syncWindowRectangleWithGraphicsContext();\n\n    // make this window's context not current\n    // note: this must be done as we will probably make the context current from another thread\n    //       and it is not allowed to have one context current in two threads\n    if( !releaseContext() )\n        OSG_WARN << \"Window realize: Can not release context.\" << std::endl;\n\n    // restore previous context\n    if ( savedContext )\n        const_cast< QGLContext* >( savedContext )->makeCurrent();\n\n    return true;\n}\n\nbool GraphicsWindowQt::isRealizedImplementation() const\n{\n    return _realized;\n}\n\nvoid GraphicsWindowQt::closeImplementation()\n{\n    if ( _widget )\n        _widget->close();\n    _realized = false;\n}\n\nvoid GraphicsWindowQt::runOperations()\n{\n    // While in graphics thread this is last chance to do something useful before\n    // graphics thread will execute its operations.\n    if (_widget->getNumDeferredEvents() > 0)\n        _widget->processDeferredEvents();\n\n    if (QGLContext::currentContext() != _widget->context())\n        _widget->makeCurrent();\n\n    GraphicsWindow::runOperations();\n}\n\nbool GraphicsWindowQt::makeCurrentImplementation()\n{\n    if (_widget->getNumDeferredEvents() > 0)\n        _widget->processDeferredEvents();\n\n    _widget->makeCurrent();\n\n    return true;\n}\n\nbool GraphicsWindowQt::releaseContextImplementation()\n{\n    _widget->doneCurrent();\n    return true;\n}\n\nvoid GraphicsWindowQt::swapBuffersImplementation()\n{\n    // QOpenGLContext complains if we swap on an non-exposed QWindow\n    if (!_widget || !_widget->windowHandle()->isExposed())\n        return;\n\n    // FIXME: the processDeferredEvents should really be executed in a GUI (main) thread context but\n    // I couln't find any reliable way to do this. For now, lets hope non of *GUI thread only operations* will\n    // be executed in a QGLWidget::event handler. On the other hand, calling GUI only operations in the\n    // QGLWidget event handler is an indication of a Qt bug.\n    if (_widget->getNumDeferredEvents() > 0)\n        _widget->processDeferredEvents();\n\n    // We need to call makeCurrent here to restore our previously current context\n    // which may be changed by the processDeferredEvents function.\n    _widget->makeCurrent();\n    _widget->swapBuffers();\n}\n\nvoid GraphicsWindowQt::requestWarpPointer( float x, float y )\n{\n    if ( _widget )\n        QCursor::setPos( _widget->mapToGlobal(QPoint((int)x,(int)y)) );\n}\n\n\nclass QtWindowingSystem : public osg::GraphicsContext::WindowingSystemInterface\n{\npublic:\n\n    QtWindowingSystem()\n    {\n        OSG_INFO << \"QtWindowingSystemInterface()\" << std::endl;\n    }\n\n    ~QtWindowingSystem()\n    {\n        if (osg::Referenced::getDeleteHandler())\n        {\n            osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);\n            osg::Referenced::getDeleteHandler()->flushAll();\n        }\n    }\n\n    // Access the Qt windowing system through this singleton class.\n    static QtWindowingSystem* getInterface()\n    {\n        static QtWindowingSystem* qtInterface = new QtWindowingSystem;\n        return qtInterface;\n    }\n\n    // Return the number of screens present in the system\n    unsigned int getNumScreens( const osg::GraphicsContext::ScreenIdentifier& /*si*/ ) override\n    {\n        OSG_WARN << \"osgQt: getNumScreens() not implemented yet.\" << std::endl;\n        return 0;\n    }\n\n    // Return the resolution of specified screen\n    // (0,0) is returned if screen is unknown\n    void getScreenSettings( const osg::GraphicsContext::ScreenIdentifier& /*si*/, osg::GraphicsContext::ScreenSettings & /*resolution*/ ) override\n    {\n        OSG_WARN << \"osgQt: getScreenSettings() not implemented yet.\" << std::endl;\n    }\n\n    // Set the resolution for given screen\n    bool setScreenSettings( const osg::GraphicsContext::ScreenIdentifier& /*si*/, const osg::GraphicsContext::ScreenSettings & /*resolution*/ ) override\n    {\n        OSG_WARN << \"osgQt: setScreenSettings() not implemented yet.\" << std::endl;\n        return false;\n    }\n\n    // Enumerates available resolutions\n    void enumerateScreenSettings( const osg::GraphicsContext::ScreenIdentifier& /*screenIdentifier*/, osg::GraphicsContext::ScreenSettingsList & /*resolution*/ ) override\n    {\n        OSG_WARN << \"osgQt: enumerateScreenSettings() not implemented yet.\" << std::endl;\n    }\n\n    // Create a graphics context with given traits\n    osg::GraphicsContext* createGraphicsContext( osg::GraphicsContext::Traits* traits ) override\n    {\n        if (traits->pbuffer)\n        {\n            OSG_WARN << \"osgQt: createGraphicsContext - pbuffer not implemented yet.\" << std::endl;\n            return nullptr;\n        }\n        else\n        {\n            osg::ref_ptr< GraphicsWindowQt > window = new GraphicsWindowQt( traits );\n            if (window->valid()) return window.release();\n            else return nullptr;\n        }\n    }\n\nprivate:\n\n    // No implementation for these\n    QtWindowingSystem( const QtWindowingSystem& );\n    QtWindowingSystem& operator=( const QtWindowingSystem& );\n};\n\n"
  },
  {
    "path": "files/CMakeLists.txt",
    "content": "add_subdirectory(mygui)\nadd_subdirectory(shaders)\nadd_subdirectory(vfs)\n"
  },
  {
    "path": "files/gamecontrollerdb.txt",
    "content": "# Game Controller DB for SDL in post 2.0.6 format\n# Source: https://github.com/gabomdq/SDL_GameControllerDB\n# Windows\n03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,\n03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,\n03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,\n03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,\n03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,\n03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,\n03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,\n03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,\n03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,\n030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,\n03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,\n030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000bc2000000055000000000000,Betop BFM Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\n03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\n030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,\n0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\n03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,\n03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\n030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\n030000005e040000ff02000000000000,Controller (Xbox One For Windows) - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\n030000005e040000ea02000000000000,Controller (Xbox One For Windows) - Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\n03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,\n03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\n03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,\n030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,\n03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,\n030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,\n03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,\n030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\n78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,\n03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\n03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,\n03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,\n030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\n03000000ac0500003d03000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,\n03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\n03000000f0250000c283000000000000,Gioteck,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000f0250000c383000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000f0250000c483000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,\n03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,\n030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,\n03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,\n03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\n030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\n03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n030000006e0500000a20000000000000,JC-DUX60 ELECOM MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,\n030000006e0500000520000000000000,JC-P301U,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,\n030000006e0500000320000000000000,JC-U3613M (DInput),a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,\n030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,\n030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,\n030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,\n030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,\n030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,\n03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows,\n03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\n030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,\n030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700008433000000000000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700008483000000000000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700006252000000000000,Mad Catz Micro C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,\n03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,\n03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\n03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\n030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,\n0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,\n03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000380700006382000000000000,MLG GamePad PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\n03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,\n030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,\n03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\n03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,\n030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows,\n03000000550900001472000000000000,NVIDIA Controller v01.04,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows,\n030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows,\n03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows,\n03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,\n03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,\n03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,\n030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\n03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,\n03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,\n03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\n030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\n03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,\n03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,\n030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,\n030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows,\n03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,\n030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000003807000056a8000000000000,PS3 RF pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\n030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,\n03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,\n03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,\n03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,\n03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,\n03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\n03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,\n0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,\n0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,\n030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\n03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\n03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\n03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b5,x:b0,y:b1,platform:Windows,\n03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,platform:Windows,\n03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\n03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\n03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\n03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,\n03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\n03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\n03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\n0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,\n030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,\n03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,\n03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,\n03000000790000001c18000000000000,STK-7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,\n03000000d620000011a7000000000000,Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000004f04000007d0000000000000,T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000004f0400000ab1000000000000,T.16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,\n03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\n030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,\n030000004f04000023b3000000000000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\n030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,\n030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,\n03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,\n03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,\n03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\n03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000300f00000701000000000000,USB 4-Axis 12-Button Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\n03000000341a00002308000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,platform:Windows,\n030000006b1400000203000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n03000000790000000a00000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\n03000000f0250000c183000000000000,USB gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\n03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\n03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\n03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\n03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\n03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,\n030000005e0400008e02000000007801,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\n03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\nxinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\n03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\n\n# Mac OS X\n030000008f0e00000300000009010000,2In1 USB Joystick,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,\n03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,\n03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,\n03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,\n03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,\n03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,\n03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,\n03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,\n030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,\n03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X,\n03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,\n030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000000d0f0000ee00000000010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,\n03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,\n03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,\n030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,\n030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,\n030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n030000006d04000018c2000000010000,Logitech RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,\n0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,\n03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,\n03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,\n030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,\n030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,\n030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,\n030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,\n03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,\n030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,\n030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n03000000321500000204000000010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,\n030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,\n0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,\n03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,\n030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,\n03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,\n030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,\n030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,\n03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,\n03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,\n03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,\n03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,\n030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,\n030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,\n03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,\n03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,\n03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,\n050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,\n050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,\n030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,\n030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,\n030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\n030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,\n03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,\n03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\n\n# Linux\n03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\n05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\n05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\n03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\n05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\n05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,\n05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,\n05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,\n05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,\n030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,\n05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,\n05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,\n05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,\n03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,\n03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,\n03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\n03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,\n03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,\n03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,\n03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,\n030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,\n03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,\n0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\n030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\n030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,\n030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,\n030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,\n0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\n06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,\n03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\n030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,\n030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,\n030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,\n030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,\n03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,\n050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,\n03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,\n0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,\n030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,\n03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,\n03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,\n030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,\n050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,\n030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,\n050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,\n030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux,\n030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,platform:Linux,\n030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,\n05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,\n03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\n03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n0300000079000000d218000011010000,MAGIC-NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,\n03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,\n0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,\n03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,\n030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,\n030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,\n030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,\n05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,\n05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,\n05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,\n03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,\n030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,\n050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\n050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,\n05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\n030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,\n03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,\n03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,\n05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,\n03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,\n05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,\n05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,\n03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\n030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,\n03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\n030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\n030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\n050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\n050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\n060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\n030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\n03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,\n030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,\n030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,\n050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,\n0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,\n0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,\n030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,\n03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,\n03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,\n03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,\n03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,\n03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,\n03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,\n03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\n03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\n03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,\n03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\n03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\n03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,\n030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\n03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\n03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\n03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\n05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\n05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\n03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,\n0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,\n030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,\n030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,\n030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,\n030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\n030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,\n03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,\n03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,\n03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,\n03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,\n03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,\n03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,\n05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,\n030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,\n030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,\n030000005e040000ea02000001030000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,\n03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\n05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,\n03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,\nxinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\n03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\n\n# Android\n05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\n05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,\n64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,\n050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android,\n37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\n4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\n61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\n050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\n050000005509000010720000ffff3f00,NVIDIA Controller v01.03,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\n050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\n050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,\n050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,\n35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,\n050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,\n05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,\n05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,\n5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,\n050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android,\n050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\n050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,\n34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,\n\n# iOS\n05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,\n05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,\n4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,\n4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,\n05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,\n05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,\n05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,\n"
  },
  {
    "path": "files/launcher/icons/tango/index.theme",
    "content": "[Icon Theme]\nName=Tango\nComment=Tango Theme\nInherits=default\nDirectories=16x16,48x48\n\n[16x16]\nSize=16\n\n[48x48]\nSize=48"
  },
  {
    "path": "files/launcher/launcher.qrc",
    "content": "<!DOCTYPE RCC><RCC version=\"1.0\">\n<qresource prefix=\"images\">\n    <file alias=\"clear.png\">images/clear.png</file>\n    <file alias=\"down.png\">images/down.png</file>\n    <file alias=\"openmw.png\">images/openmw.png</file>\n    <file alias=\"openmw-plugin.png\">images/openmw-plugin.png</file>\n    <file alias=\"openmw-header.png\">images/openmw-header.png</file>\n    <file alias=\"preferences.png\">images/preferences.png</file>\n    <file alias=\"preferences-advanced.png\">images/preferences-advanced.png</file>\n    <file alias=\"preferences-video.png\">images/preferences-video.png</file>\n    <file alias=\"playpage-background.png\">images/playpage-background.png</file>\n</qresource>\n<qresource prefix=\"icons/tango\">\n    <file alias=\"index.theme\">icons/tango/index.theme</file>\n    <file alias=\"48x48/emblem-system.png\">icons/tango/48x48/emblem-system.png</file>\n    <file alias=\"48x48/preferences-system.png\">icons/tango/48x48/preferences-system.png</file>\n    <file alias=\"48x48/video-display.png\">icons/tango/48x48/video-display.png</file>\n    <file alias=\"16x16/document-new.png\">icons/tango/16x16/document-new.png</file>\n    <file alias=\"16x16/edit-copy.png\">icons/tango/16x16/edit-copy.png</file>\n    <file alias=\"16x16/edit-delete.png\">icons/tango/16x16/edit-delete.png</file>\n    <file alias=\"16x16/go-bottom.png\">icons/tango/16x16/go-bottom.png</file>\n    <file alias=\"16x16/go-down.png\">icons/tango/16x16/go-down.png</file>\n    <file alias=\"16x16/go-top.png\">icons/tango/16x16/go-top.png</file>\n    <file alias=\"16x16/go-up.png\">icons/tango/16x16/go-up.png</file>\n</qresource>\n</RCC>\n"
  },
  {
    "path": "files/mac/openmw-Info.plist.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"https://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleIconFile</key>\n\t<string>OpenMW.icns</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>English</string>\n\t<key>CFBundleExecutable</key>\n\t<string>openmw-launcher</string>\n    <key>CFBundleIdentifier</key>\n    <string>org.openmw.openmw</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleLongVersionString</key>\n\t<string></string>\n\t<key>CFBundleName</key>\n\t<string>OpenMW</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n    <key>CFBundleShortVersionString</key>\n    <string>${OPENMW_VERSION}</string>\n\t<key>CFBundleVersion</key>\n\t<string>${OPENMW_VERSION}</string>\n\t<key>CSResourcesFileMapped</key>\n\t<true/>\n\t<key>LSRequiresCarbon</key>\n\t<true/>\n\t<key>NSHumanReadableCopyright</key>\n\t<string></string>\n\t<key>NSHighResolutionCapable</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "files/mac/openmw-cs-Info.plist.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"https://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>English</string>\n\t<key>CFBundleExecutable</key>\n\t<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>\n\t<key>CFBundleGetInfoString</key>\n\t<string>${MACOSX_BUNDLE_INFO_STRING}</string>\n\t<key>CFBundleIconFile</key>\n\t<string>${MACOSX_BUNDLE_ICON_FILE}</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleLongVersionString</key>\n\t<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>\n\t<key>CFBundleName</key>\n\t<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>\n\t<key>CSResourcesFileMapped</key>\n\t<true/>\n\t<key>LSRequiresCarbon</key>\n\t<true/>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>${MACOSX_BUNDLE_COPYRIGHT}</string>\n\t<key>NSHighResolutionCapable</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "files/mac/qt.conf",
    "content": "[Paths]\nPlugins = PlugIns\n"
  },
  {
    "path": "files/mygui/CMakeLists.txt",
    "content": "if (NOT DEFINED OPENMW_MYGUI_FILES_ROOT)\n    return()\nendif()\n\n# Copy resource files into the build directory\nset(SDIR ${CMAKE_CURRENT_SOURCE_DIR})\nset(DDIRRELATIVE resources/mygui)\n\nset(MYGUI_FILES\n    core.skin\n    core.xml\n    core_layouteditor.xml\n    openmw_alchemy_window.layout\n    openmw_book.layout\n    openmw_box.skin.xml\n    openmw_button.skin.xml\n    openmw_chargen_birth.layout\n    openmw_chargen_class_description.layout\n    openmw_chargen_class.layout\n    openmw_chargen_create_class.layout\n    openmw_chargen_generate_class_result.layout\n    openmw_chargen_race.layout\n    openmw_chargen_review.layout\n    openmw_chargen_select_attribute.layout\n    openmw_chargen_select_skill.layout\n    openmw_chargen_select_specialization.layout\n    openmw_confirmation_dialog.layout\n    openmw_console.layout\n    openmw_console.skin.xml\n    openmw_container_window.layout\n    openmw_count_window.layout\n    openmw_dialogue_window.layout\n    openmw_dialogue_window.skin.xml\n    openmw_edit.skin.xml\n    openmw_font.xml\n    openmw_hud_box.skin.xml\n    openmw_hud_energybar.skin.xml\n    openmw_hud.layout\n    openmw_infobox.layout\n    openmw_interactive_messagebox.layout\n    openmw_interactive_messagebox_notransp.layout\n    openmw_inventory_window.layout\n    openmw_journal.layout\n    openmw_journal.skin.xml\n    openmw_layers.xml\n    openmw_list.skin.xml\n    openmw_mainmenu.layout\n    openmw_mainmenu.skin.xml\n    openmw_map_window.layout\n    openmw_map_window.skin.xml\n    openmw_messagebox.layout\n    openmw_pointer.xml\n    openmw_progress.skin.xml\n    openmw_resources.xml\n    openmw_scroll.layout\n    openmw_scroll.skin.xml\n    openmw_settings_window.layout\n    openmw_settings.xml\n    openmw_spell_window.layout\n    openmw_stats_window.layout\n    openmw_text_input.layout\n    openmw_text.skin.xml\n    openmw_tooltips.layout\n    openmw_trade_window.layout\n    openmw_spell_buying_window.layout\n    openmw_windows.skin.xml\n    openmw_quickkeys_menu.layout\n    openmw_quickkeys_menu_assign.layout\n    openmw_itemselection_dialog.layout\n    openmw_magicselection_dialog.layout\n    openmw_spell_buying_window.layout\n    openmw_loading_screen.layout\n    openmw_levelup_dialog.layout\n    openmw_wait_dialog.layout\n    openmw_wait_dialog_progressbar.layout\n    openmw_spellcreation_dialog.layout\n    openmw_edit_effect.layout\n    openmw_enchanting_dialog.layout\n    openmw_trainingwindow.layout\n    openmw_travel_window.layout\n    openmw_persuasion_dialog.layout\n    openmw_merchantrepair.layout\n    openmw_repair.layout\n    openmw_companion_window.layout\n    openmw_savegame_dialog.layout\n    openmw_recharge_dialog.layout\n    openmw_screen_fader.layout\n    openmw_screen_fader_hit.layout\n    openmw_edit_note.layout\n    openmw_debug_window.layout\n    openmw_debug_window.skin.xml\n    openmw_jail_screen.layout\n    DejaVuLGCSansMono.ttf\n    ../launcher/images/openmw.png\n    # Start of tes3mp addition\n    ../tes3mp/tes3mp_logo.png\n    # End of tes3mp addition\n    OpenMWResourcePlugin.xml\n    skins.xml\n\n    tes3mp_login.layout\n    tes3mp_login.skin.xml\n    tes3mp_dialog_list.layout\n    tes3mp_chat.layout\n    tes3mp_chat.skin.xml\n    tes3mp_text_input.layout\n    RussoOne-Regular.ttf\n)\n\n\ncopy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_MYGUI_FILES_ROOT} ${DDIRRELATIVE} \"${MYGUI_FILES}\")\n"
  },
  {
    "path": "files/mygui/DejaVuFontLicense.txt",
    "content": "Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.\nGlyphs imported from Arev fonts are (c) Tavmjong Bah (see below)\n\nBitstream Vera Fonts Copyright\n------------------------------\n\nCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is\na trademark of Bitstream, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof the fonts accompanying this license (\"Fonts\") and associated\ndocumentation files (the \"Font Software\"), to reproduce and distribute the\nFont Software, including without limitation the rights to use, copy, merge,\npublish, distribute, and/or sell copies of the Font Software, and to permit\npersons to whom the Font Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright and trademark notices and this permission notice shall\nbe included in all copies of one or more of the Font Software typefaces.\n\nThe Font Software may be modified, altered, or added to, and in particular\nthe designs of glyphs or characters in the Fonts may be modified and\nadditional glyphs or characters may be added to the Fonts, only if the fonts\nare renamed to names not containing either the words \"Bitstream\" or the word\n\"Vera\".\n\nThis License becomes null and void to the extent applicable to Fonts or Font\nSoftware that has been modified and is distributed under the \"Bitstream\nVera\" names.\n\nThe Font Software may be sold as part of a larger software package but no\ncopy of one or more of the Font Software typefaces may be sold by itself.\n\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,\nTRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME\nFOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING\nANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF\nTHE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE\nFONT SOFTWARE.\n\nExcept as contained in this notice, the names of Gnome, the Gnome\nFoundation, and Bitstream Inc., shall not be used in advertising or\notherwise to promote the sale, use or other dealings in this Font Software\nwithout prior written authorization from the Gnome Foundation or Bitstream\nInc., respectively. For further information, contact: fonts at gnome dot\norg. \n\nArev Fonts Copyright\n------------------------------\n\nCopyright (c) 2006 by Tavmjong Bah. All Rights Reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the fonts accompanying this license (\"Fonts\") and\nassociated documentation files (the \"Font Software\"), to reproduce\nand distribute the modifications to the Bitstream Vera Font Software,\nincluding without limitation the rights to use, copy, merge, publish,\ndistribute, and/or sell copies of the Font Software, and to permit\npersons to whom the Font Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright and trademark notices and this permission notice\nshall be included in all copies of one or more of the Font Software\ntypefaces.\n\nThe Font Software may be modified, altered, or added to, and in\nparticular the designs of glyphs or characters in the Fonts may be\nmodified and additional glyphs or characters may be added to the\nFonts, only if the fonts are renamed to names not containing either\nthe words \"Tavmjong Bah\" or the word \"Arev\".\n\nThis License becomes null and void to the extent applicable to Fonts\nor Font Software that has been modified and is distributed under the \n\"Tavmjong Bah Arev\" names.\n\nThe Font Software may be sold as part of a larger software package but\nno copy of one or more of the Font Software typefaces may be sold by\nitself.\n\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL\nTAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n\nExcept as contained in this notice, the name of Tavmjong Bah shall not\nbe used in advertising or otherwise to promote the sale, use or other\ndealings in this Font Software without prior written authorization\nfrom Tavmjong Bah. For further information, contact: tavmjong @ free\n. fr.\n\n$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $\n"
  },
  {
    "path": "files/mygui/OpenMWResourcePlugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI>\n    <MyGUI type=\"Widgets\">\n        <!-- New widget class definitions -->\n        <Widget name=\"ImageButton\">\n            <Property key=\"DefaultSkin\" value=\"ImageBox\"/>\n            <Property key=\"Skin\" value=\"ImageBox\" group=\"Plugin\" name=\"ImageButton\"/>\n            <Property key=\"Parent\" value=\"true\"/>\n            <Property key=\"Child\" value=\"true\"/>\n            <Parameter key=\"ImageHighlighted\" value=\"String\"/>\n            <Parameter key=\"ImagePushed\" value=\"String\"/>\n            <Parameter key=\"ImageNormal\" value=\"String\"/>\n        </Widget>\n        <Widget name=\"VBox\">\n            <Property key=\"DefaultSkin\" value=\"\"/>\n            <Property key=\"Skin\" value=\"\" group=\"Plugin\" name=\"VBox\"/>\n            <Property key=\"Parent\" value=\"true\"/>\n            <Property key=\"Child\" value=\"true\"/>\n            <Parameter key=\"Spacing\" value=\"1 int\"/>\n            <Parameter key=\"Padding\" value=\"1 int\"/>\n            <Parameter key=\"AutoResize\" value=\"Bool\"/>\n        </Widget>\n        <Widget name=\"HBox\">\n            <Property key=\"DefaultSkin\" value=\"\"/>\n            <Property key=\"Skin\" value=\"\" group=\"Plugin\" name=\"HBox\"/>\n            <Property key=\"Parent\" value=\"true\"/>\n            <Property key=\"Child\" value=\"true\"/>\n            <Parameter key=\"Spacing\" value=\"1 int\"/>\n            <Parameter key=\"Padding\" value=\"1 int\"/>\n            <Parameter key=\"AutoResize\" value=\"Bool\"/>\n        </Widget>\n        <Widget name=\"AutoSizedButton\">\n            <Property key=\"Base\" value=\"Button\"/>\n            <Property key=\"DefaultSkin\" value=\"MW_Button\"/>\n            <Property key=\"Skin\" value=\"MW_Button\" group=\"Plugin\" name=\"AutoSizedButton\"/>\n            <Property key=\"Parent\" value=\"true\"/>\n            <Property key=\"Child\" value=\"true\"/>\n            <Parameter key=\"ExpandDirection\" value=\"Align\"/>\n        </Widget>\n        <Widget name=\"AutoSizedTextBox\">\n            <Property key=\"Base\" value=\"TextBox\"/>\n            <Property key=\"DefaultSkin\" value=\"SandText\"/>\n            <Property key=\"Skin\" value=\"SandText\" group=\"Plugin\" name=\"AutoSizedTextBox\"/>\n            <Property key=\"Parent\" value=\"true\"/>\n            <Property key=\"Child\" value=\"true\"/>\n            <Parameter key=\"ExpandDirection\" value=\"Align\"/>\n        </Widget>\n        <Widget name=\"AutoSizedEditBox\">\n            <Property key=\"Base\" value=\"EditBox\"/>\n            <Property key=\"DefaultSkin\" value=\"SandText\"/>\n            <Property key=\"Skin\" value=\"SandText\" group=\"Plugin\" name=\"AutoSizedEditBox\"/>\n            <Property key=\"Parent\" value=\"true\"/>\n            <Property key=\"Child\" value=\"true\"/>\n            <Parameter key=\"ExpandDirection\" value=\"Align\"/>\n        </Widget>\n        <Widget name=\"MWList\">\n            <Property key=\"DefaultSkin\" value=\"MW_SimpleList\"/>\n            <Property key=\"Skin\" value=\"MW_SimpleList\" group=\"Plugin\" name=\"MWList\"/>\n            <Property key=\"Parent\" value=\"true\"/>\n            <Property key=\"Child\" value=\"true\"/>\n            <Parameter key=\"ListItemSkin\" value=\"Skin\"/>\n        </Widget>\n    </MyGUI>\n    <MyGUI type=\"Plugin\">\n        <Plugin>\n            <Source>./Plugin_MyGUI_OpenMW_Resources</Source>\n            <Source build=\"Debug\">./Plugin_MyGUI_OpenMW_Resources_d</Source>\n        </Plugin>\n    </MyGUI>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/core.skin",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <Resource type=\"ResourceSkin\" name=\"TextBox\" size=\"16 16\">\n        <Property key=\"TextAlign\" value = \"ALIGN_DEFAULT\" />\n        <Property key=\"TextColour\" value = \"0.7 0.7 0.7\" />\n\n        <BasisSkin type=\"SimpleText\" offset = \"0 0 16 16\"  align = \"ALIGN_STRETCH\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"RotatingSkin\" size=\"16 16\">\n        <BasisSkin type=\"RotatingSkin\" offset=\"0 0 16 16\" align=\"Stretch\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"ImageBox\" size=\"16 16\">\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 16\"/>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/core.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI>\n    <MyGUI type=\"List\">\n        <List file=\"skins.xml\" />\n        <List file=\"openmw_layers.xml\" />\n        <List file=\"openmw_pointer.xml\" />\n        <List file=\"openmw_settings.xml\" />\n    </MyGUI>\n</MyGUI>\n\n"
  },
  {
    "path": "files/mygui/core_layouteditor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI>\n    <MyGUI type=\"List\">\n        <List file=\"OpenMWResourcePlugin.xml\"/> <!-- Must be first -->\n        <List file=\"skins.xml\"/>\n        <List file=\"openmw_settings.xml\"/>\n    </MyGUI>\n</MyGUI>\n\n"
  },
  {
    "path": "files/mygui/openmw_alchemy_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window_NoCaption\" layer=\"Windows\" align=\"Center\" position=\"0 0 588 444\" name=\"_Main\">\n       <Property key=\"MinSize\" value=\"425 360\"/>\n\n        <!-- Name -->\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"10 8 65 30\">\n            <Property key=\"Caption\" value=\"#{sName}\"/>\n            <Property key=\"TextAlign\" value=\"Left\"/>\n        </Widget>\n\n        <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"70 8 492 30\" align=\"Top Left HStretch\" name=\"NameEdit\">\n        </Widget>\n\n\n        <!-- Apparatus -->\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"10 40 260 24\">\n            <Property key=\"Caption\" value=\"#{sApparatus}\"/>\n            <Property key=\"TextAlign\" value=\"Left\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"10 66 260 50\">\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"0 0 50 50\" name=\"Apparatus1\"/>\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"60 0 50 50\" name=\"Apparatus2\"/>\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"120 0 50 50\" name=\"Apparatus3\"/>\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"180 0 50 50\" name=\"Apparatus4\"/>\n\n        </Widget>\n\n\n        <!-- Used Ingredients -->\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"10 120 260 24\">\n            <Property key=\"Caption\" value=\"#{sIngredients}\"/>\n            <Property key=\"TextAlign\" value=\"Left\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"10 146 260 50\">\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"0 0 50 50\" name=\"Ingredient1\"/>\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"60 0 50 50\" name=\"Ingredient2\"/>\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"120 0 50 50\" name=\"Ingredient3\"/>\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"180 0 50 50\" name=\"Ingredient4\"/>\n\n        </Widget>\n\n\n        <!-- Available Ingredients -->\n\n        <Widget type=\"ItemView\" skin=\"MW_ItemView\" position=\"10 206 552 132\" name=\"ItemView\" align=\"Left Top Stretch\"/>\n\n        <!-- Created Effects -->\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"250 40 320 24\">\n            <Property key=\"Caption\" value=\"#{sCreatedEffects}\"/>\n            <Property key=\"TextAlign\" value=\"Left\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"250 66 312 130\" align=\"Top Left HStretch\">\n            <Widget type=\"Widget\" skin=\"\" position=\"4 4 316 122\" name=\"CreatedEffects\" align=\"HStretch\"/>\n        </Widget>\n\n        <!-- Filters -->\n\n        <Widget type=\"HBox\" skin=\"MW_Box\" position=\"10 333 552 39\" align=\"Bottom HStretch\">\n            <Property key=\"Padding\" value=\"5\"/>\n            <Property key=\"Spacing\" value=\"10\"/>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"10 2 1 1\" name=\"FilterType\">\n                <Property key=\"Caption\" value=\"#{sIngredients}\"/> <!-- default value, can be either sIngredients of sMagicEffects -->\n            </Widget>\n            <Widget type=\"ComboBox\" skin=\"MW_ComboBox\" position=\"0 2 0 24\" name=\"FilterValue\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Property key=\"ModeDrop\" value=\"false\"/>\n            </Widget>\n        </Widget>\n\n\n        <!-- Buttons -->\n\n        <Widget type=\"HBox\" skin=\"\" position=\"10 374 552 28\" align=\"Bottom HStretch\">\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"DecreaseButton\">\n                <Property key=\"Caption\" value=\" - \"/>\n                <Property key=\"NeedKey\" value=\"false\"/>\n            </Widget>\n\n            <Widget type=\"NumericEditBox\" skin=\"MW_TextEdit\" position=\"0 0 96 25\" name=\"BrewCount\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"IncreaseButton\">\n                <Property key=\"Caption\" value=\" + \"/>\n                <Property key=\"NeedKey\" value=\"false\"/>\n            </Widget>\n\n            <Widget type=\"Spacer\"/>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CreateButton\">\n                <Property key=\"Caption\" value=\"#{sCreate}\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                <Property key=\"Caption\" value=\"#{sCancel}\"/>\n            </Widget>\n\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_book.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n\n<Widget type=\"Window\" skin=\"\" layer=\"JournalBooks\" align=\"Left Top\" position=\"0 0 584 398\" name=\"_Main\">\n\n    <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"-71 0 728 398\" align=\"Left Top\" name=\"JImage\">\n        <Property key=\"ImageTexture\" value=\"textures\\tx_menubook.dds\"/>\n        <Widget type=\"Widget\" position=\"71 0 584 398\" align=\"Left Top\">\n\n            <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"40 358 64 32\" name=\"TakeButton\">\n                <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_take_over.dds\"/>\n                <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_take_idle.dds\"/>\n                <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_take_pressed.dds\"/>\n            </Widget>\n\n            <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"205 358 48 32\" name=\"PrevPageBTN\">\n                <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_prev_over.dds\"/>\n                <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_prev_idle.dds\"/>\n                <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_prev_pressed.dds\"/>\n            </Widget>\n            <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"330 358 48 32\" name=\"NextPageBTN\">\n                <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_next_over.dds\"/>\n                <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_next_idle.dds\"/>\n                <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_next_pressed.dds\"/>\n            </Widget>\n\n            <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"488 358 48 32\" name=\"CloseButton\">\n                <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_close_over.dds\"/>\n                <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_close_idle.dds\"/>\n                <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_close_pressed.dds\"/>\n            </Widget>\n\n            <Widget type=\"TextBox\" skin=\"NormalText\" position=\"30 358 250 16\" name=\"LeftPageNumber\">\n                <Property key=\"FontName\" value=\"Journalbook Magic Cards\"/>\n                <Property key=\"TextColour\" value=\"0 0 0\"/>\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n            <Widget type=\"TextBox\" skin=\"NormalText\" position=\"310 358 250 16\" name=\"RightPageNumber\">\n                <Property key=\"FontName\" value=\"Journalbook Magic Cards\"/>\n                <Property key=\"TextColour\" value=\"0 0 0\"/>\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n\n            <Widget type=\"Widget\" skin=\"\" position=\"30 15 250 328\" name=\"LeftPage\"/>\n            <Widget type=\"Widget\" skin=\"\" position=\"310 15 250 328\" name=\"RightPage\"/>\n        </Widget>\n    </Widget>\n</Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_box.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- This file defines the box you see around many GUI elements, such\nas around the sections of the stats window, or around popup info windows -->\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <!-- Box borders -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"IB_T\" texture=\"textures\\menu_thin_border_top.dds\">\n        <BasisSkin type=\"TileRect\" align=\"HStretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"IB_B\" texture=\"textures\\menu_thin_border_bottom.dds\">\n        <BasisSkin type=\"TileRect\" align=\"HStretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"IB_L\" texture=\"textures\\menu_thin_border_left.dds\">\n        <BasisSkin type=\"TileRect\" align=\"VStretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"IB_R\" texture=\"textures\\menu_thin_border_right.dds\">\n        <BasisSkin type=\"TileRect\" align=\"VStretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"IB_TL\" texture=\"textures\\menu_thin_border_top_left_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"IB_TR\" texture=\"textures\\menu_thin_border_top_right_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"IB_BL\" texture=\"textures\\menu_thin_border_bottom_left_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"IB_BR\" texture=\"textures\\menu_thin_border_bottom_right_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Main box definition -->\n    <Resource type=\"ResourceSkin\" name=\"MW_Box\" size=\"516 516\">\n        <Child type=\"Widget\" skin=\"IB_T\" offset=\"2 0 512 2\" align=\"Top HStretch\"/>\n        <Child type=\"Widget\" skin=\"IB_B\" offset=\"2 514 512 2\" align=\"Bottom HStretch\"/>\n        <Child type=\"Widget\" skin=\"IB_L\" offset=\"0 2 2 512\" align=\"Left VStretch\"/>\n        <Child type=\"Widget\" skin=\"IB_R\" offset=\"514 2 2 512\" align=\"Right VStretch\"/>\n        <Child type=\"Widget\" skin=\"IB_TL\" offset=\"0 0 2 2\" align=\"Top Left\"/>\n        <Child type=\"Widget\" skin=\"IB_TR\" offset=\"514 0 2 2\" align=\"Top Right\"/>\n        <Child type=\"Widget\" skin=\"IB_BL\" offset=\"0 514 2 2\" align=\"Bottom Left\"/>\n        <Child type=\"Widget\" skin=\"IB_BR\" offset=\"514 514 2 2\" align=\"Bottom Right\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Box_Overlay\" size=\"516 516\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_button.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <!-- Button graphics -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"BTN_Top\" texture=\"textures\\menu_button_frame_top.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"TileRect\" align=\"HStretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"BTN_Bottom\" texture=\"textures\\menu_button_frame_bottom.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"TileRect\" align=\"HStretch\">\n            <State name=\"normal\" >\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"BTN_Left\" texture=\"textures\\menu_button_frame_left.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"TileRect\" align=\"VStretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"BTN_Right\" texture=\"textures\\menu_button_frame_right.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"TileRect\" align=\"VStretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"BTN_TopLeft\" texture=\"textures\\menu_button_frame_top_left_corner.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"BTN_TopRight\" texture=\"textures\\menu_button_frame_top_right_corner.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"BTN_BottomLeft\" texture=\"textures\\menu_button_frame_bottom_left_corner.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"BTN_BottomRight\" texture=\"textures\\menu_button_frame_bottom_right_corner.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Button widget -->\n    <Resource type=\"ResourceSkin\" name=\"MW_Button\" size=\"136 24\" version=\"3.2.1\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Center\"/>\n\t<Property key=\"NeedKey\" value=\"true\"/>\n\n        <Child type=\"Widget\" skin=\"BTN_Left\" offset=\"0 4 4 16\" align=\"VStretch Left\"/>\n        <Child type=\"Widget\" skin=\"BTN_Right\" offset=\"132 4 4 16\" align=\"VStretch Right\"/>\n        <Child type=\"Widget\" skin=\"BTN_Top\" offset=\"4 0 128 4\" align=\"HStretch Top\"/>\n        <Child type=\"Widget\" skin=\"BTN_Bottom\" offset=\"4 20 128 4\" align=\"HStretch Bottom\"/>\n        <Child type=\"Widget\" skin=\"BTN_TopLeft\" offset=\"0 0 4 4\" align=\"Top Left\"/>\n        <Child type=\"Widget\" skin=\"BTN_TopRight\" offset=\"132 0 4 4\" align=\"Top Right\"/>\n        <Child type=\"Widget\" skin=\"BTN_BottomLeft\" offset=\"0 20 4 4\" align=\"Bottom Left\"/>\n        <Child type=\"Widget\" skin=\"BTN_BottomRight\" offset=\"132 20 4 4\" align=\"Bottom Right\"/>\n\n        <BasisSkin type=\"SimpleText\" offset=\"4 3 128 16\" align=\"Stretch\">\n            <State name=\"disabled\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal\" colour=\"#{fontcolour=normal}\" shift=\"0\"/>\n            <State name=\"highlighted\" colour=\"#{fontcolour=normal_over}\" shift=\"0\"/>\n            <State name=\"pushed\" colour=\"#{fontcolour=normal_pressed}\" shift=\"0\"/>\n            <State name=\"disabled_checked\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal_checked\" colour=\"#{fontcolour=active}\" shift=\"0\"/>\n            <State name=\"highlighted_checked\" colour=\"#{fontcolour=active_over}\" shift=\"0\"/>\n            <State name=\"pushed_checked\" colour=\"#{fontcolour=active_pressed}\" shift=\"0\"/>\n        </BasisSkin>\n   </Resource>\n\n    <!-- Button widget, 4 px padding on the right -->\n    <Resource type=\"ResourceSkin\" name=\"MW_Button_RightPadding\" size=\"140 24\" version=\"3.2.1\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Center\"/>\n\n        <!-- padding -->\n        <Child type=\"Widget\" skin=\"\" offset=\"136 0 4 24\" align=\"Right VStretch\"/>\n\n        <Child type=\"Widget\" skin=\"BTN_Left\" offset=\"0 4 4 16\" align=\"VStretch Left\"/>\n        <Child type=\"Widget\" skin=\"BTN_Right\" offset=\"132 4 4 16\" align=\"VStretch Right\"/>\n        <Child type=\"Widget\" skin=\"BTN_Top\" offset=\"4 0 128 4\" align=\"HStretch Top\"/>\n        <Child type=\"Widget\" skin=\"BTN_Bottom\" offset=\"4 20 128 4\" align=\"HStretch Bottom\"/>\n        <Child type=\"Widget\" skin=\"BTN_TopLeft\" offset=\"0 0 4 4\" align=\"Top Left\"/>\n        <Child type=\"Widget\" skin=\"BTN_TopRight\" offset=\"132 0 4 4\" align=\"Top Right\"/>\n        <Child type=\"Widget\" skin=\"BTN_BottomLeft\" offset=\"0 20 4 4\" align=\"Bottom Left\"/>\n        <Child type=\"Widget\" skin=\"BTN_BottomRight\" offset=\"132 20 4 4\" align=\"Bottom Right\"/>\n        <Child type=\"Widget\" skin=\"BTN_BottomRight\" offset=\"132 20 4 4\" align=\"Bottom Right\"/>\n\n        <BasisSkin type=\"SimpleText\" offset=\"4 3 128 16\" align=\"Stretch\">\n            <State name=\"disabled\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal\" colour=\"#{fontcolour=normal}\" shift=\"0\"/>\n            <State name=\"highlighted\" colour=\"#{fontcolour=normal_over}\" shift=\"0\"/>\n            <State name=\"pushed\" colour=\"#{fontcolour=normal_pressed}\" shift=\"0\"/>\n            <State name=\"disabled_checked\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal_checked\" colour=\"#{fontcolour=active}\" shift=\"0\"/>\n            <State name=\"highlighted_checked\" colour=\"#{fontcolour=active_over}\" shift=\"0\"/>\n            <State name=\"pushed_checked\" colour=\"#{fontcolour=active_pressed}\" shift=\"0\"/>\n        </BasisSkin>\n   </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_birth.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 527 378\" align=\"Center\" name=\"_Main\">\n\n        <!-- Birthsign list -->\n        <Widget type=\"ListBox\" skin=\"MW_List\" position=\"8 8 232 137\" name=\"BirthsignList\"/>\n\n        <!-- Birthsign image -->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"248 8 263 137\" align=\"Left Top\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"2 2 259 133\" name=\"BirthsignImage\" align=\"Left Top\"/>\n        </Widget>\n\n        <!-- Spell list -->\n        <Widget type=\"ScrollView\" skin=\"MW_ScrollView\" position=\"8 160 507 170\" align=\"Left Top\" name=\"SpellArea\">\n            <Property key=\"CanvasAlign\" value=\"Left\"/>\n        </Widget>>\n\n        <!-- Dialog buttons -->\n        <Widget type=\"HBox\" position=\"0 338 511 28\">\n            <Widget type=\"Spacer\"/>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"BackButton\">\n                <Property key=\"Caption\" value=\"#{sBack}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OKButton\">\n                <Property key=\"Caption\" value=\"OK\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_class.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 491 316\" align=\"Center\" name=\"_Main\">\n\n        <!-- Class list -->\n        <Widget type=\"ListBox\" skin=\"MW_List\" position=\"8 8 194 138\" name=\"ClassList\"/>\n\n        <!-- Class image -->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"210 8 265 138\" align=\"Left Top\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"2 2 261 134\" name=\"ClassImage\" align=\"Left Top\"/>\n        </Widget>\n\n        <!-- Specialization -->\n        <Widget type=\"Widget\" skin=\"\" position=\"8 156 484 178\" align=\"Left Top\">\n\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"0 0 166 18\" name=\"SpecializationT\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sChooseClassMenu1}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sCreateClassMenuHelp1}\"/>\n            </Widget>\n\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 18 166 18\" name=\"SpecializationName\" align=\"Left Top\">\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n\n            <!-- Favorite Attributes -->\n            <Widget type=\"VBox\" skin=\"\" position=\"0 39 166 76\" align=\"Stretch\">\n                <Property key=\"Spacing\" value=\"0\"/>\n\n                <!-- Favorite Attributes -->\n                <Widget type=\"AutoSizedEditBox\" skin=\"HeaderText\" position=\"0 0 166 22\" name=\"FavoriteAttributesT\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"#{sChooseClassMenu2}\"/>\n                    <Property key=\"TextAlign\" value=\"Left Top\"/>\n                    <Property key=\"Static\" value=\"true\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                    <UserString key=\"Caption_Text\" value=\"#{sCreateClassMenuHelp2}\"/>\n                    <Property key=\"MultiLine\" value=\"true\"/>\n                    <Property key=\"WordWrap\" value=\"true\"/>\n                </Widget>\n\n                <Widget type=\"MWAttribute\" skin=\"MW_StatNameButton\" position=\"0 0 166 18\" name=\"FavoriteAttribute0\" align=\"Left Top\"/>\n                <Widget type=\"MWAttribute\" skin=\"MW_StatNameButton\" position=\"0 0 166 18\" name=\"FavoriteAttribute1\" align=\"Left Top\"/>\n             </Widget>\n\n            <!-- Major Skills -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"166 0 162 18\" name=\"MajorSkillT\" align=\"Left Top\">\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <Property key=\"Caption\" value=\"#{sChooseClassMenu3}\"/>\n            </Widget>\n\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"166 18 166 18\" name=\"MajorSkill0\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"166 36 166 18\" name=\"MajorSkill1\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"166 54 166 18\" name=\"MajorSkill2\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"166 72 166 18\" name=\"MajorSkill3\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"166 90 166 18\" name=\"MajorSkill4\" align=\"Left Top\"/>\n\n            <!-- Minor Skills -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"332 0 166 18\" name=\"MinorSkillT\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sChooseClassMenu4}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"332 18 166 18\" name=\"MinorSkill0\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"332 36 166 18\" name=\"MinorSkill1\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"332 54 166 18\" name=\"MinorSkill2\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"332 72 166 18\" name=\"MinorSkill3\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatName\" position=\"332 90 166 18\" name=\"MinorSkill4\" align=\"Left Top\"/>\n\n        </Widget>\n\n        <!-- Dialog buttons -->\n        <Widget type=\"HBox\" position=\"0 276 475 28\">\n            <Widget type=\"Spacer\"/>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"BackButton\">\n                <Property key=\"Caption\" value=\"#{sBack}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OKButton\">\n                <Property key=\"Caption\" value=\"#{sOK}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_class_description.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 244 248\" align=\"Center\" name=\"_Main\">\n\n        <!-- Edit box -->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 8 220 192\" align=\"Stretch\"/>\n\n        <Widget type=\"EditBox\" skin=\"MW_TextBoxEdit\" position=\"10 10 218 190\" name=\"TextEdit\" align=\"Left Top Stretch\">\n            <Property key=\"MultiLine\" value=\"true\"/>\n            <Property key=\"VisibleVScroll\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n        </Widget>\n\n        <!-- Dialog buttons -->\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"171 208 57 24\" name=\"OKButton\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"Enter\"/>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_create_class.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 498 198\" align=\"Center\" name=\"_Main\">\n\n        <!-- Class name -->\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"8 8 52 23\" name=\"LabelT\" align=\"Left Top\">\n            <Property key=\"Caption\" value=\"#{sName}:\"/>\n            <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n        </Widget>\n        <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"72 8 410 23\" name=\"EditName\" align=\"HStretch Top\">\n            <Property key=\"Caption\" value=\"#{sCustomClassName}\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"8 38 480 116\" align=\"Stretch\">\n\n            <!-- Specialization -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"0 0 156 18\" name=\"SpecializationT\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sChooseClassMenu1}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sCreateClassMenuHelp1}\"/>\n            </Widget>\n\n            <Widget type=\"Button\" skin=\"SandTextButton\" position=\"0 18 166 18\" name=\"SpecializationName\" align=\"Left Top\">\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n\n            <Widget type=\"VBox\" skin=\"\" position=\"0 41 166 76\" align=\"Stretch\">\n                <Property key=\"Spacing\" value=\"0\"/>\n\n                <!-- Favorite Attributes -->\n                <Widget type=\"AutoSizedEditBox\" skin=\"HeaderText\" position=\"0 0 166 18\" name=\"FavoriteAttributesT\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"#{sChooseClassMenu2}\"/>\n                    <Property key=\"TextAlign\" value=\"Left Top\"/>\n                    <Property key=\"Static\" value=\"true\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                    <UserString key=\"Caption_Text\" value=\"#{sCreateClassMenuHelp2}\"/>\n                    <Property key=\"MultiLine\" value=\"true\"/>\n                    <Property key=\"WordWrap\" value=\"true\"/>\n                </Widget>\n\n                <Widget type=\"MWAttribute\" skin=\"MW_StatNameButton\" position=\"0 0 166 18\" name=\"FavoriteAttribute0\" align=\"Left Top\"/>\n                <Widget type=\"MWAttribute\" skin=\"MW_StatNameButton\" position=\"0 0 166 18\" name=\"FavoriteAttribute1\" align=\"Left Top\"/>\n             </Widget>\n\n            <!-- Major Skills -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"166 0 166 18\" name=\"MajorSkillT\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sChooseClassMenu3}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n            </Widget>\n\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"166 18 166 18\" name=\"MajorSkill0\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"166 36 166 18\" name=\"MajorSkill1\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"166 54 166 18\" name=\"MajorSkill2\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"166 72 166 18\" name=\"MajorSkill3\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"166 90 166 18\" name=\"MajorSkill4\" align=\"Left Top\"/>\n\n            <!-- Minor Skills -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"332 0 166 18\" name=\"MinorSkillT\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sChooseClassMenu4}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"332 18 166 18\" name=\"MinorSkill0\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"332 36 166 18\" name=\"MinorSkill1\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"332 54 166 18\" name=\"MinorSkill2\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"332 72 166 18\" name=\"MinorSkill3\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"332 90 166 18\" name=\"MinorSkill4\" align=\"Left Top\"/>\n\n        </Widget>\n\n        <!-- Dialog buttons -->\n        <Widget type=\"HBox\" position=\"0 158 482 28\">\n            <Widget type=\"Spacer\"/>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"DescriptionButton\">\n                <Property key=\"Caption\" value=\"#{sCreateClassMenu1}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"BackButton\">\n                <Property key=\"Caption\" value=\"#{sBack}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OKButton\">\n                <Property key=\"Caption\" value=\"#{sOK}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_generate_class_result.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"VBox\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 289 256\" align=\"Center\" name=\"_Main\">\n        <Property key=\"AutoResize\" value=\"true\"/>\n        <Property key=\"Padding\" value=\"8\"/>\n        <Property key=\"Spacing\" value=\"8\"/>\n\n        <!-- Class image -->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 8 265 138\" align=\"Left Top\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"2 2 261 134\" name=\"ClassImage\" align=\"Left Top\"/>\n        </Widget>\n\n        <!-- Class text -->\n        <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 152 265 40\" name=\"ReflectT\" align=\"Left Top\">\n            <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n            <Property key=\"MultiLine\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"Static\" value=\"true\"/>\n        </Widget>\n        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"8 183 265 23\" name=\"ClassName\" align=\"Left Top\">\n            <Property key=\"Caption\" value=\"[Class]\"/>\n            <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n        </Widget>\n\n        <!-- Dialog buttons -->\n        <Widget type=\"HBox\" position=\"0 216 273 28\">\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"BackButton\">\n                <Property key=\"Caption\" value=\"#{sBack}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OKButton\">\n                <Property key=\"Caption\" value=\"#{sOK}\"/>\n            </Widget>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_race.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 640 433\" align=\"Center\" name=\"_Main\">\n\n        <!-- Appearance -->\n        <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"8 16 241 18\" name=\"AppearanceT\" align=\"Left Top\">\n            <Property key=\"Caption\" value=\"Appearance\"/>\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n        </Widget>\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 39 241 220\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"2 2 237 216\" align=\"Stretch\" name=\"PreviewImage\"/>\n        </Widget>\n\n        <!-- Sliders -->\n        <!-- Rotation of head -->\n        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"8 270 241 14\" name=\"HeadRotate\"/>\n\n        <!-- Gender choice -->\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 298 15 14\">\n            <Widget type=\"Button\" skin=\"MW_ArrowLeft\" position=\"2 2 10 10\" align=\"Left VStretch\" name=\"PrevGenderButton\"/>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"25 294 205 18\" name=\"GenderChoiceT\"/>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"234 298 15 14\">\n            <Widget type=\"Button\" skin=\"MW_ArrowRight\" position=\"2 2 10 10\" align=\"Right VStretch\" name=\"NextGenderButton\"/>\n        </Widget>\n\n        <!-- Face choice -->\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 320 15 14\">\n            <Widget type=\"Button\" skin=\"MW_ArrowLeft\" position=\"2 2 10 10\" align=\"Left VStretch\" name=\"PrevFaceButton\"/>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"25 316 205 18\" name=\"FaceChoiceT\"/>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"234 320 15 14\">\n            <Widget type=\"Button\" skin=\"MW_ArrowRight\" position=\"2 2 10 10\" align=\"Right VStretch\" name=\"NextFaceButton\"/>\n        </Widget>\n\n        <!-- Hair choice -->\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 342 15 14\">\n            <Widget type=\"Button\" skin=\"MW_ArrowLeft\" position=\"2 2 10 10\" align=\"Left VStretch\" name=\"PrevHairButton\"/>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"25 338 205 18\" name=\"HairChoiceT\"/>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"234 342 15 14\">\n            <Widget type=\"Button\" skin=\"MW_ArrowRight\" position=\"2 2 10 10\" align=\"Right VStretch\" name=\"NextHairButton\"/>\n        </Widget>\n\n        <!-- Race -->\n\n        <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"261 16 160 18\" name=\"RaceT\" align=\"Left Top\">\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n        </Widget>\n        <Widget type=\"ListBox\" skin=\"MW_List\" position=\"264 39 160 150\" name=\"RaceList\">\n        </Widget>\n\n        <!-- Spell powers -->\n        <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"261 210 160 18\" name=\"SpellPowerT\" align=\"Left Top\">\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n        </Widget>\n        <!-- Spell power sub-widgets will be placed here, no skin to make it invisible -->\n        <Widget type=\"Widget\" skin=\"\" position=\"261 230 350 140\" name=\"SpellPowerList\"/>\n\n        <!-- Skill bonus -->\n        <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"432 39 190 18\" name=\"SkillsT\" align=\"Left Top\">\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n        </Widget>\n        <!-- Skill bonus sub-widgets will be placed here, no skin to make it invisible -->\n        <Widget type=\"Widget\" skin=\"\" position=\"432 59 190 360\" name=\"SkillList\"/>\n\n        <!-- Dialog buttons -->\n        <Widget type=\"HBox\" position=\"0 393 626 28\">\n            <Widget type=\"Spacer\"/>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"471 397 53 23\" name=\"BackButton\">\n                <Property key=\"Caption\" value=\"#{sBack}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"532 397 42 23\" name=\"OKButton\">\n                <Property key=\"Caption\" value=\"#{sOK}\"/>\n            </Widget>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_review.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 541 428\" align=\"Center\" name=\"_Main\">\n\n        <!-- Player Name, Race, Class and Birthsign -->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 8 265 126\">\n            <Widget type=\"Button\" skin=\"MW_Button\" position=\"8 8 64 23\" name=\"NameButton\">\n                <Property key=\"Caption\" value=\"#{sName}\"/>\n            </Widget>\n            <Widget type=\"Button\" skin=\"MW_Button\" position=\"8 37 56 23\" name=\"RaceButton\">\n                <Property key=\"Caption\" value=\"#{sRace}\"/>\n            </Widget>\n            <Widget type=\"Button\" skin=\"MW_Button\" position=\"8 66 56 23\" name=\"ClassButton\">\n                <Property key=\"Caption\" value=\"#{sClass}\"/>\n            </Widget>\n            <Widget type=\"Button\" skin=\"MW_Button\" position=\"8 95 54 23\" name=\"SignButton\">\n                <Property key=\"Caption\" value=\"#{sBirthSign}\"/>\n            </Widget>\n            <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"97 10 161 18\" name=\"NameText\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"97 39 161 18\" name=\"RaceText\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"97 68 161 18\" name=\"ClassText\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"97 97 161 18\" name=\"SignText\"/>\n        </Widget>\n\n        <!-- Player Health, Magicka and Fatigue -->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 144 265 72\">\n            <Widget type=\"MWDynamicStat\" skin=\"MW_DynamicStat_Red\" position=\"8 8 249 18\" name=\"Health\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n                <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\health.dds\"/>\n                <Property key=\"Caption\" value=\"#{sHealth}\"/>\n            </Widget>\n            <Widget type=\"MWDynamicStat\" skin=\"MW_DynamicStat_Blue\" position=\"8 27 249 18\" name=\"Magicka\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n                <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\magicka.dds\"/>\n                <Property key=\"Caption\" value=\"#{sMagic}\"/>\n            </Widget>\n            <Widget type=\"MWDynamicStat\" skin=\"MW_DynamicStat_Green\" position=\"8 46 249 18\" name=\"Fatigue\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n                <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\fatigue.dds\"/>\n                <Property key=\"Caption\" value=\"#{sFatigue}\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Player attributes -->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 224 265 156\">\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameValue\" position=\"8 4 250 18\" name=\"Attribute0\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeStrength}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sStrDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_strength.dds\"/>\n            </Widget>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameValue\" position=\"8 22 250 18\" name=\"Attribute1\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeIntelligence}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sIntDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_int.dds\"/>\n            </Widget>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameValue\" position=\"8 40 250 18\" name=\"Attribute2\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeWillpower}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sWilDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_wilpower.dds\"/>\n            </Widget>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameValue\" position=\"8 58 250 18\" name=\"Attribute3\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeAgility}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sAgiDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_agility.dds\"/>\n            </Widget>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameValue\" position=\"8 76 250 18\" name=\"Attribute4\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeSpeed}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sSpdDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_speed.dds\"/>\n            </Widget>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameValue\" position=\"8 94 250 18\" name=\"Attribute5\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeEndurance}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sEndDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_endurance.dds\"/>\n            </Widget>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameValue\" position=\"8 112 250 18\" name=\"Attribute6\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributePersonality}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sPerDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_personality.dds\"/>\n            </Widget>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameValue\" position=\"8 130 250 18\" name=\"Attribute7\">\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeLuck}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sLucDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_luck.dds\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Player Skills -->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"281 7 244 372\" align=\"Left VStretch\" name=\"Skills\">\n            <Widget type=\"ScrollView\" skin=\"MW_ScrollView\" position=\"8 6 232 362\" align=\"Stretch\" name=\"SkillView\"/>\n        </Widget>\n\n        <!-- Dialogue Buttons -->\n        <Widget type=\"HBox\" position=\"0 388 525 24\">\n            <Widget type=\"Spacer\"/>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"BackButton\">\n                <Property key=\"Caption\" value=\"#{sBack}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OKButton\">\n                <Property key=\"Caption\" value=\"#{sOK}\"/>\n            </Widget>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_select_attribute.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 247 231\" align=\"Center\" name=\"_Main\">\n        <Widget type=\"Widget\" skin=\"\" position=\"14 14 216 204\" align=\"HCenter VStretch\">\n\n            <!-- Label -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"0 0 216 18\" name=\"LabelT\" align=\"HCenter Top\">\n                <Property key=\"Caption\" value=\"#{sAttributesMenu1}\"/>\n                <Property key=\"TextAlign\" value=\"HCenter Top\"/>\n            </Widget>\n\n            <!-- Attribute list -->\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameButtonC\" position=\"0 28 216 18\" name=\"Attribute0\" align=\"Left Top\"/>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameButtonC\" position=\"0 46 216 18\" name=\"Attribute1\" align=\"Left Top\"/>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameButtonC\" position=\"0 64 216 18\" name=\"Attribute2\" align=\"Left Top\"/>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameButtonC\" position=\"0 82 216 18\" name=\"Attribute3\" align=\"Left Top\"/>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameButtonC\" position=\"0 100 216 18\" name=\"Attribute4\" align=\"Left Top\"/>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameButtonC\" position=\"0 118 216 18\" name=\"Attribute5\" align=\"Left Top\"/>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameButtonC\" position=\"0 136 216 18\" name=\"Attribute6\" align=\"Left Top\"/>\n            <Widget type=\"MWAttribute\" skin=\"MW_StatNameButtonC\" position=\"0 154 216 18\" name=\"Attribute7\" align=\"Left Top\"/>\n\n            <!-- Dialog buttons -->\n            <Widget type=\"HBox\" position=\"0 175 216 28\">\n                <Widget type=\"Spacer\" />\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" align=\"Right Bottom\" name=\"CancelButton\">\n                    <Property key=\"ExpandDirection\" value=\"Left Up\"/>\n                    <Property key=\"Caption\" value=\"#{sCancel}\"/>\n                </Widget>\n            </Widget>\n\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_select_skill.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 487 275\" align=\"Center\" name=\"_Main\">\n        <Widget type=\"Widget\" skin=\"\" position=\"17 14 457 246\" align=\"Stretch\">\n\n            <!-- Label -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"0 0 457 18\" name=\"LabelT\" align=\"HCenter Top\">\n                <Property key=\"Caption\" value=\"#{sSkillsMenu1}\"/>\n                <Property key=\"TextAlign\" value=\"HCenter Top\"/>\n            </Widget>\n\n            <!-- Combat list -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"0 32 154 18\" name=\"CombatLabelT\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sSpecializationCombat}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 50 154 18\" name=\"CombatSkill0\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 68 154 18\" name=\"CombatSkill1\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 86 154 18\" name=\"CombatSkill2\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 104 154 18\" name=\"CombatSkill3\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 122 154 18\" name=\"CombatSkill4\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 140 154 18\" name=\"CombatSkill5\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 158 154 18\" name=\"CombatSkill6\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 176 154 18\" name=\"CombatSkill7\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"0 194 154 18\" name=\"CombatSkill8\" align=\"Left Top\"/>\n\n            <!-- Magic list -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"158 32 154 18\" name=\"MagicLabelT\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sSpecializationMagic}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 50 154 18\" name=\"MagicSkill0\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 68 154 18\" name=\"MagicSkill1\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 86 154 18\" name=\"MagicSkill2\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 104 154 18\" name=\"MagicSkill3\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 122 154 18\" name=\"MagicSkill4\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 140 154 18\" name=\"MagicSkill5\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 158 154 18\" name=\"MagicSkill6\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 176 154 18\" name=\"MagicSkill7\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"158 194 154 18\" name=\"MagicSkill8\" align=\"Left Top\"/>\n\n            <!-- Stealth list -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"316 32 131 18\" name=\"StealthLabelT\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sSpecializationStealth}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 50 154 18\" name=\"StealthSkill0\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 68 154 18\" name=\"StealthSkill1\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 86 154 18\" name=\"StealthSkill2\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 104 154 18\" name=\"StealthSkill3\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 122 154 18\" name=\"StealthSkill4\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 140 154 18\" name=\"StealthSkill5\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 158 154 18\" name=\"StealthSkill6\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 176 154 18\" name=\"StealthSkill7\" align=\"Left Top\"/>\n            <Widget type=\"MWSkill\" skin=\"MW_StatNameButton\" position=\"316 194 154 18\" name=\"StealthSkill8\" align=\"Left Top\"/>\n\n            <!-- Dialog buttons -->\n            <Widget type=\"HBox\" position=\"0 218 457 28\">\n                <Widget type=\"Spacer\" />\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" align=\"Right Bottom\" name=\"CancelButton\">\n                    <Property key=\"ExpandDirection\" value=\"Left Up\"/>\n                    <Property key=\"Caption\" value=\"#{sCancel}\"/>\n                </Widget>\n            </Widget>\n\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_chargen_select_specialization.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <!-- correct size is 247 144, adjust when skin is changed to a dialog -->\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 247 144\" align=\"Center\" name=\"_Main\">\n        <Widget type=\"Widget\" skin=\"\" position=\"14 14 216 116\" align=\"Stretch\">\n\n            <!-- Label -->\n            <Widget type=\"TextBox\" skin=\"HeaderText\" position=\"0 0 216 18\" name=\"LabelT\" align=\"Center Top\">\n                <Property key=\"Caption\" value=\"#{sSpecializationMenu1}\"/>\n                <Property key=\"TextAlign\" value=\"Center Top\"/>\n            </Widget>\n\n            <!-- Specialization list -->\n            <Widget type=\"Button\" skin=\"SandTextButton\" position=\"0 28 216 18\" name=\"Specialization0\" align=\"Left Top\">\n                <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n            </Widget>\n            <Widget type=\"Button\" skin=\"SandTextButton\" position=\"0 46 216 18\" name=\"Specialization1\" align=\"Left Top\">\n                <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n            </Widget>\n            <Widget type=\"Button\" skin=\"SandTextButton\" position=\"0 64 216 18\" name=\"Specialization2\" align=\"Left Top\">\n                <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n            </Widget>\n\n            <!-- Dialog buttons -->\n            <Widget type=\"HBox\" position=\"0 88 216 28\">\n                <Widget type=\"Spacer\" />\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" align=\"Right Bottom\" name=\"CancelButton\">\n                    <Property key=\"ExpandDirection\" value=\"Left Up\"/>\n                    <Property key=\"Caption\" value=\"#{sCancel}\"/>\n                </Widget>\n            </Widget>\n\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_companion_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window\" layer=\"Windows\" position=\"0 0 600 300\" name=\"_Main\">\n        <Property key=\"MinSize\" value=\"245 145\"/>\n\n        <!-- Search box-->\n        <Widget type=\"HBox\"  position=\"5 5 575 23\" align=\"Left Top HStretch\" name=\"_Filter\">\n            <Widget type=\"EditBox\" skin=\"MW_TextBoxEditWithBorder\" position=\"0 0 0 23\" name=\"FilterEdit\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <UserString key=\"AcceptTab\" value=\"true\"/>\n            </Widget>\n        </Widget>\n        <!-- Items -->\n        <Widget type=\"ItemView\" skin=\"MW_ItemView\" position=\"5 33 575 197\" name=\"ItemView\" align=\"Left Top Stretch\">\n        </Widget>\n\n        <Widget type=\"HBox\" position=\"5 235 575 24\" align=\"Bottom HStretch\">\n            <Widget type=\"MWDynamicStat\" skin=\"MW_ChargeBar_Blue\" position=\"8 8 212 24\" name=\"EncumbranceBar\" align=\"Left Top HStretch\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"ProfitLabel\">\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CloseButton\">\n                <Property key=\"Caption\" value=\"#{sClose}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_confirmation_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 360 130\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Visible\" value=\"false\"/>\n\n        <Widget type=\"EditBox\" skin=\"MW_TextEditClient\" position=\"16 8 338 130\" name=\"Message\" align=\"Center Top\">\n            <Property key=\"FontName\" value=\"Default\"/>\n            <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n            <Property key=\"Static\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"MultiLine\" value=\"true\"/>\n            <Property key=\"NeedKey\" value=\"false\"/>\n        </Widget>\n\n        <Widget type=\"VBox\" position=\"0 89 352 28\" align=\"Center Bottom\">\n            <Widget type=\"HBox\">\n                <Property key=\"Spacing\" value=\"8\"/>\n\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OkButton\" align=\"Center Bottom\">\n                    <Property key=\"Caption\" value=\"#{sYes}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\" align=\"Center Bottom\">\n                    <Property key=\"Caption\" value=\"#{sNo}\"/>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"Widget\"/>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_console.layout",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n  <Widget type=\"Window\" skin=\"MW_Window\" position=\"0 0 400 400\" layer=\"Windows\" name=\"_Main\">\n    <Property key=\"Caption\" value=\"#{sConsoleTitle}\"/>\n    <Property key=\"MinSize\" value=\"40 40\"/>\n    <Property key=\"Visible\" value=\"false\"/>\n\n    <!-- Log window -->\n    <Widget type=\"EditBox\" skin=\"MW_TextBoxEdit\" position=\"5 5 380 328\" align=\"Stretch\" name=\"list_History\">\n        <Property key=\"MultiLine\" value=\"1\"/>\n        <Property key=\"ReadOnly\" value=\"true\"/>\n        <Property key=\"FontName\" value=\"MonoFont\"/>\n        <Property key=\"TextAlign\" value=\"Left Top\"/>\n        <Property key=\"TextColour\" value=\"1 1 1\"/>\n        <Property key=\"InvertSelected\" value=\"false\"/>\n        <Property key=\"WordWrap\" value=\"true\"/>\n    </Widget>\n\n    <!-- Command line -->\n    <Widget type=\"EditBox\" skin=\"MW_ConsoleCommand\" position=\"0 338 384 28\" align=\"HStretch Bottom\" name=\"edit_Command\">\n        <Property key=\"InvertSelected\" value=\"false\"/>\n        <UserString key=\"AcceptTab\" value=\"true\"/>\n    </Widget>\n\n  </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_console.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n\n\n    <!-- Console Input -->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_EditClient\" size=\"10 10\">\n        <Property key=\"FontName\" value=\"MonoFont\"/>\n        <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n        <Property key=\"TextColour\" value=\"1 1 1\"/>\n        <BasisSkin type=\"EditText\" offset=\"0 0 10 10\" align=\"Stretch\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ConsoleCommand\" size=\"29 28\">\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 29 26\" align=\"Bottom Stretch\"/>\n        <Child type=\"TextBox\" skin=\"MW_EditClient\" offset=\"4 2 19 22\" align=\"Bottom Stretch\" name=\"Client\"/>\n    </Resource>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_container_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window\" layer=\"Windows\" position=\"0 0 600 300\" name=\"_Main\">\n        <Property key=\"MinSize\" value=\"245 145\"/>\n        <Property key=\"Visible\" value=\"false\"/>\n\n        <!-- Items -->\n        <Widget type=\"ItemView\" skin=\"MW_ItemView\" position=\"5 5 575 225\" name=\"ItemView\" align=\"Left Top Stretch\">\n        </Widget>\n\n        <Widget type=\"HBox\" position=\"0 235 580 28\" align=\"Right Bottom\">\n            <Widget type=\"Spacer\"/>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"DisposeCorpseButton\" align=\"Right Bottom\">\n                <Property key=\"Caption\" value=\"#{sDisposeofCorpse}\"/>\n                <Property key=\"Visible\" value=\"false\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"TakeButton\" align=\"Right Bottom\">\n                <Property key=\"Caption\" value=\"#{sTakeAll}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CloseButton\" align=\"Right Bottom\">\n                <Property key=\"Caption\" value=\"#{sClose}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_count_window.layout",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\" version=\"3.2.0\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" position=\"0 0 600 128\" align=\"Center\" layer=\"Windows\" name=\"_Main\">\n        <Property key=\"Visible\" value=\"false\"/>\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 4 592 24\" align=\"Left Top HStretch\" name=\"LabelText\">\n            <Property key=\"TextAlign\" value=\"Center\"/>\n        </Widget>\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"12 36 521 24\" align=\"Left Top HStretch\" name=\"ItemText\">\n            <Property key=\"TextAlign\" value=\"Left\"/>\n        </Widget>\n        <Widget type=\"NumericEditBox\" skin=\"MW_TextEdit\" position=\"535 36 50 24\" align=\"Right Top\" name=\"ItemEdit\">\n            <Property key=\"TextAlign\" value=\"Center\"/>\n        </Widget>\n        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"7 67 578 18\" align=\"Left Top HStretch\" name=\"CountSlider\">\n            <Property key=\"MoveToClick\" value=\"true\"/>\n            <Property key=\"Page\" value=\"1\"/>\n            <Property key=\"WheelPage\" value=\"1\"/>\n        </Widget>\n        <Widget type=\"HBox\" skin=\"\" position=\"0 91 592 28\" align=\"Center Bottom HStretch\">\n            <Widget type=\"Spacer\" />\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 53 28\" align=\"Left Top\" name=\"OkButton\">\n                <Property key=\"Caption\" value=\"#{sOk}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 86 28\" align=\"Right Top\" name=\"CancelButton\">\n                <Property key=\"Caption\" value=\"#{sCancel}\"/>\n            </Widget>\n            <Widget type=\"Spacer\" />\n        </Widget>\n    </Widget>\n    <CodeGeneratorSettings/>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_debug_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n  <Widget type=\"Window\" skin=\"\" position=\"0 0 400 400\" layer=\"Debug\" name=\"_Main\" align=\"Stretch\">\n    <Property key=\"Caption\" value=\"Debug\"/>\n    <Property key=\"Visible\" value=\"false\"/>\n\n    <Widget type=\"Widget\" skin=\"DialogBG\" position_real=\"0 0 1 1\" name=\"Background\" align=\"Stretch\">\n        <Property key=\"Alpha\" value=\"0.5\"/>\n    </Widget>\n\n    <Widget type=\"TabControl\" skin=\"TabControl\" position_real=\"0 0 1 1\" align=\"Stretch\" name=\"TabControl\">\n        <Property key=\"ButtonAutoWidth\" value=\"true\"/>\n    </Widget>\n\n  </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_debug_window.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <Resource type=\"ResourceSkin\" name=\"LogEdit\" size=\"512 20\">\n        <Property key=\"WordWrap\" value=\"true\"/>\n        <Property key=\"MultiLine\" value=\"true\"/>\n        <Property key=\"FontName\" value=\"MonoFont\"/>\n        <Property key=\"TextAlign\" value=\"Left Top\"/>\n        <Property key=\"TextColour\" value=\"1 1 1\"/>\n        <Property key=\"MaxTextLength\" value=\"10000000\"/>\n        <Property key=\"InvertSelected\" value=\"false\"/>\n\n        <Child type=\"TextBox\" skin=\"MW_TextEditClient\" offset=\"2 2 490 18\" align=\"Stretch\" name=\"Client\"/>\n\n        <Child type=\"ScrollBar\" skin=\"MW_VScroll\" offset=\"494 3 14 14\" align=\"Right VStretch\" name=\"VScroll\"/>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_dialogue_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window\" layer=\"Windows\" position=\"0 0 588 433\" name=\"_Main\">\n        <Property key=\"MinSize\" value=\"380 230\"/>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 8 381 381\" align=\"Stretch\"/>\n\n        <Widget type=\"Widget\" position=\"15 15 364 370\" align=\"Left Top Stretch\">\n            <Widget type=\"BookPage\" skin=\"MW_BookPage\" position=\"0 0 364 370\" name=\"History\" align=\"Left Top Stretch\">\n            </Widget>\n        </Widget>\n\n        <Widget type=\"ScrollBar\" skin=\"MW_VScroll\" position=\"370 13 14 371\" align=\"Right VStretch\" name=\"VScroll\">\n            <Property key=\"Visible\" value=\"false\"/>\n        </Widget>\n\n        <!-- The disposition bar-->\n        <Widget type=\"ProgressBar\" skin=\"MW_Progress_Blue\" position=\"398 8 166 18\"\n            align=\"Right Top\" name=\"Disposition\">\n            <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"0 0 166 14\" name=\"DispositionText\" align=\"Right VCenter\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n             </Widget>\n        </Widget>\n        <!-- The list of topics -->\n        <Widget type=\"MWList\" skin=\"MW_SimpleList\" position=\"398 31 166 328\" name=\"TopicsList\" align=\"Right VStretch\">\n        </Widget>\n\n        <!-- The Goodbye button -->\n        <Widget type=\"Button\" skin=\"MW_Button\" position=\"398 366 166 23\" name=\"ByeButton\" align=\"Right Bottom\">\n            <Property key=\"Caption\" value=\"#{sGoodbye}\"/>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_dialogue_window.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_edit.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n\n    <!-- Text edit widget -->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_TextEditClient\" size=\"10 10\">\n        <Property key=\"TextColour\" value=\"#{fontcolour=normal}\"/>\n\n        <BasisSkin type=\"EditText\" offset=\"0 0 10 10\" align=\"Stretch\"/>\n\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_TextEdit\" size=\"512 20\">\n\n        <!-- Borders -->\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 512 20\" align=\"Stretch\"/>\n\n        <!-- Input -->\n\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n        <Property key=\"TextColour\" value=\"#{fontcolour=normal}\"/>\n\n        <Child type=\"TextBox\" skin=\"MW_TextEditClient\" offset=\"4 0 502 18\" align=\"Stretch\" name=\"Client\"/>\n\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_TextBoxEdit\" size=\"512 20\">\n\n        <Property key=\"FontName\" value=\"Default\"/>\n\n        <Property key=\"TextAlign\" value=\"Left Top\"/>\n\n        <Property key=\"TextColour\" value=\"#{fontcolour=normal}\"/>\n\n        <Child type=\"TextBox\" skin=\"MW_TextEditClient\" offset=\"2 2 490 18\" align=\"Stretch\" name=\"Client\"/>\n\n        <Child type=\"ScrollBar\" skin=\"MW_VScroll\" offset=\"494 3 14 14\" align=\"Right VStretch\" name=\"VScroll\"/>\n\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_TextBoxEditWithBorder\" size=\"512 20\">\n\n        <!-- Borders -->\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 512 20\" align=\"Stretch\"/>\n\n        <Property key=\"FontName\" value=\"Default\"/>\n\n        <Property key=\"TextAlign\" value=\"Left Top\"/>\n\n        <Property key=\"TextColour\" value=\"#{fontcolour=normal}\"/>\n\n        <Child type=\"TextBox\" skin=\"MW_TextEditClient\" offset=\"2 2 490 18\" align=\"Stretch\" name=\"Client\"/>\n\n        <Child type=\"ScrollBar\" skin=\"MW_VScroll\" offset=\"494 3 14 14\" align=\"Right VStretch\" name=\"VScroll\"/>\n\n    </Resource>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_edit_effect.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 362 310\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"8 12 16 16\" name=\"EffectImage\">\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"36 8 400 24\" name=\"EffectName\">\n            <Property key=\"TextAlign\" value=\"Left HCenter\"/>\n        </Widget>\n\n\n        <!-- Range -->\n        <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"8 36 400 24\">\n            <Property key=\"Caption\" value=\"#{sRange}\"/>\n            <Property key=\"TextAlign\" value=\"Left HCenter\"/>\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n            <UserString key=\"Caption_Text\" value=\"#{sRangeDes}\"/>\n        </Widget>\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"130 36 0 24\" name=\"RangeButton\">\n            <Property key=\"Caption\" value=\"#{sRangeTouch}\"/>\n        </Widget>\n\n        <!-- Magnitude -->\n        <Widget type=\"Widget\" position=\"8 80 400 70\" name=\"MagnitudeBox\">\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"0 0 400 24\">\n                <Property key=\"Caption\" value=\"#{sMagnitude}\"/>\n                <Property key=\"TextAlign\" value=\"Left HCenter\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sMagnitudeDes}\"/>\n            </Widget>\n\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"122 0 210 20\" name=\"MagnitudeMinValue\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <Property key=\"Caption\" value=\"0\"/>\n            </Widget>\n            <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"122 20 210 13\" name=\"MagnitudeMinSlider\">\n                <Property key=\"Range\" value=\"100\"/>\n                <Property key=\"Page\" value=\"1\"/>\n            </Widget>\n\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"122 32 210 20\" name=\"MagnitudeMaxValue\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <Property key=\"Caption\" value=\"0\"/>\n            </Widget>\n            <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"122 52 210 13\" name=\"MagnitudeMaxSlider\">\n                <Property key=\"Range\" value=\"100\"/>\n                <Property key=\"Page\" value=\"1\"/>\n            </Widget>\n        </Widget>\n\n\n        <!-- Duration -->\n        <Widget type=\"Widget\" position=\"8 153 400 40\" name=\"DurationBox\">\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"0 20 400 24\">\n                <Property key=\"Caption\" value=\"#{sDuration}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sDurationDes}\"/>\n            </Widget>\n\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"122 0 210 20\" name=\"DurationValue\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <Property key=\"Caption\" value=\"0\"/>\n            </Widget>\n            <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"122 20 210 13\" name=\"DurationSlider\">\n                <Property key=\"Range\" value=\"1440\"/>\n                <Property key=\"Page\" value=\"1\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Area -->\n        <Widget type=\"Widget\" position=\"8 197 400 40\" name=\"AreaBox\">\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"0 20 400 24\" name=\"AreaText\">\n                <Property key=\"Caption\" value=\"#{sArea}\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sAreaDes}\"/>\n            </Widget>\n\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"122 0 210 20\" name=\"AreaValue\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <Property key=\"Caption\" value=\"0\"/>\n            </Widget>\n            <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"122 20 210 13\" name=\"AreaSlider\">\n                <Property key=\"Range\" value=\"51\"/>\n                <Property key=\"Page\" value=\"1\"/>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"HBox\" position=\"8 266 336 24\">\n            <Widget type=\"Spacer\"/>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"DeleteButton\">\n                <Property key=\"Caption\" value=\"#{sDelete}\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OkButton\">\n                <Property key=\"Caption\" value=\"#{sOk}\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                <Property key=\"Caption\" value=\"#{sCancel}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_edit_note.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 336 242\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"13 13 200 16\">\n            <Property key=\"Caption\" value=\"#{sEditNote}\"/>\n        </Widget>\n\n        <Widget type=\"EditBox\" skin=\"MW_TextBoxEditWithBorder\" position=\"13 38 303 150\" name=\"TextEdit\" align=\"Left Top Stretch\">\n            <Property key=\"MultiLine\" value=\"true\"/>\n            <Property key=\"VisibleVScroll\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n        </Widget>\n\n        <Widget type=\"HBox\" position=\"13 200 303 24\">\n            <Property key=\"Spacing\" value=\"6\"/>\n            <Widget type=\"Spacer\"/>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"DeleteButton\">\n                <Property key=\"Caption\" value=\"#{sDelete}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OkButton\">\n                <Property key=\"Caption\" value=\"#{sOk}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                <Property key=\"Caption\" value=\"#{sCancel}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_enchanting_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 560 448\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"HBox\" position=\"12 9 273 30\">\n            <Property key=\"Spacing\" value=\"8\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                <Property key=\"Caption\" value=\"#{sName}\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp8}\"/>\n            </Widget>\n\n            <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"0 0 30 30\" name=\"NameEdit\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n\n            <Widget type=\"Widget\">\n            </Widget>\n\n        </Widget>\n\n        <!-- Item -->\n\n        <Widget type=\"HBox\" position=\"12 48 265 59\">\n            <Property key=\"Spacing\" value=\"8\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                <Property key=\"Caption\" value=\"#{sItem}\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp1}\"/>\n            </Widget>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"0 0 50 50\" name=\"ItemBox\">\n            </Widget>\n\n            <Widget type=\"Spacer\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                <Property key=\"Caption\" value=\"#{sSoulGem}\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp2}\"/>\n            </Widget>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"0 0 50 50\" name=\"SoulBox\">\n            </Widget>\n\n        </Widget>\n\n        <!-- Values -->\n        <Widget type=\"VBox\" position=\"320 7 222 96\">\n            <Widget type=\"HBox\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                    <Property key=\"Caption\" value=\"#{sEnchantmentMenu3}:\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                    <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp3}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"Enchantment\">\n                    <Property key=\"Caption\" value=\"1\"/>\n                    <Property key=\"TextAlign\" value=\"Right VCenter\"/>\n                    <UserString key=\"HStretch\" value=\"true\"/>\n                </Widget>\n            </Widget>\n            <Widget type=\"HBox\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                    <Property key=\"Caption\" value=\"#{sCastCost}:\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                    <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp4}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"CastCost\">\n                    <Property key=\"Caption\" value=\"39\"/>\n                    <Property key=\"TextAlign\" value=\"Right VCenter\"/>\n                    <UserString key=\"HStretch\" value=\"true\"/>\n                </Widget>\n            </Widget>\n            <Widget type=\"HBox\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                    <Property key=\"Caption\" value=\"#{sCharges}\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                    <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp5}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"Charge\">\n                    <Property key=\"Caption\" value=\"39\"/>\n                    <Property key=\"TextAlign\" value=\"Right VCenter\"/>\n                    <UserString key=\"HStretch\" value=\"true\"/>\n                </Widget>\n            </Widget>\n            <Widget type=\"HBox\" name=\"ChanceLayout\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                    <Property key=\"Caption\" value=\"#{sEnchantmentMenu6}:\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"SuccessChance\">\n                    <Property key=\"Caption\" value=\"39\"/>\n                    <Property key=\"TextAlign\" value=\"Right VCenter\"/>\n                    <UserString key=\"HStretch\" value=\"true\"/>\n                </Widget>\n            </Widget>\n        </Widget>\n\n        <!-- Available effects -->\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"12 109 300 24\">\n            <Property key=\"Caption\" value=\"#{sMagicEffects}\"/>\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n            <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp9}\"/>\n        </Widget>\n        <Widget type=\"MWList\" skin=\"MW_SimpleList\" position=\"12 136 202 269\" name=\"AvailableEffects\">\n        </Widget>\n\n        <!-- Used effects -->\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"226 109 300 24\">\n            <Property key=\"Caption\" value=\"#{sEffects}\"/>\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n            <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp10}\"/>\n        </Widget>\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"226 136 316 269\">\n            <Widget type=\"ScrollView\" skin=\"MW_ScrollViewH\" position=\"4 4 308 261\" name=\"UsedEffects\">\n                <Property key=\"CanvasAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"HBox\" position=\"0 392 558 60\">\n            <Property key=\"Padding\" value=\"16\"/>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"TypeButton\">\n                <Property key=\"Caption\" value=\"Constant effect\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp7}\"/>\n            </Widget>\n\n            <Widget type=\"Spacer\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" name=\"PriceTextLabel\">\n                <Property key=\"Caption\" value=\"#{sBarterDialog7}\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sEnchantmentHelp6}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"PriceLabel\">\n                <Property key=\"Caption\" value=\"0\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"BuyButton\">\n                <Property key=\"Caption\" value=\"#{sBuy}\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                <Property key=\"Caption\" value=\"#{sCancel}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_font.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <Resource type=\"ResourceTrueTypeFont\" name=\"MonoFont\">\n        <Property key=\"Source\" value=\"DejaVuLGCSansMono.ttf\"/>\n        <Property key=\"Antialias\" value=\"false\"/>\n        <Property key=\"TabWidth\" value=\"8\"/>\n        <Property key=\"OffsetHeight\" value=\"0\"/>\n        <Codes>\n            <Code range=\"33 126\"/>\n            <Code range=\"192 382\"/>\n            <Code range=\"1025 1105\"/>\n            <Code range=\"8470\"/>\n            <Code hide=\"128\"/>\n            <Code hide=\"1026 1039\"/>\n            <Code hide=\"1104\"/>\n        </Codes>\n    </Resource>\n    <Resource type=\"ResourceTrueTypeFont\" name=\"Russo\">\n        <Property key=\"Source\" value=\"RussoOne-Regular.ttf\"/>\n        <Property key=\"Size\" value=\"11\"/>\n        <Property key=\"Resolution\" value=\"96\"/>\n        <Property key=\"Antialias\" value=\"false\"/>\n        <Property key=\"TabWidth\" value=\"8\"/>\n        <Property key=\"OffsetHeight\" value=\"0\"/>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_hud.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Widget\" layer=\"HUD\" position=\"0 0 300 200\" name=\"_Main\" align=\"Stretch\">\n        <!-- Energy bars -->\n        <Widget type=\"ProgressBar\" skin=\"MW_EnergyBar_Yellow\" position=\"13 131 65 12\" align=\"Left Bottom\" name=\"EnemyHealth\">\n            <Property key=\"Visible\" value=\"false\"/>\n        </Widget>\n        <Widget type=\"Button\" skin=\"\" position=\"13 146 65 12\" align=\"Left Bottom\" name=\"HealthFrame\">\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n            <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\health.dds\"/>\n            <Widget type=\"ProgressBar\" skin=\"MW_EnergyBar_Red\" position=\"0 0 65 12\" align=\"Left Bottom\" name=\"Health\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n        </Widget>\n        <Widget type=\"Button\" skin=\"\" position=\"13 161 65 12\" align=\"Left Bottom\" name=\"MagickaFrame\">\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n            <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\magicka.dds\"/>\n            <Widget type=\"ProgressBar\" skin=\"MW_EnergyBar_Blue\" position=\"0 0 65 12\" align=\"Left Bottom\" name=\"Magicka\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n        </Widget>\n        <Widget type=\"Button\" skin=\"\" position=\"13 176 65 12\" align=\"Left Bottom\" name=\"FatigueFrame\">\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n            <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\fatigue.dds\"/>\n            <Widget type=\"ProgressBar\" skin=\"MW_EnergyBar_Green\" position=\"0 0 65 12\" align=\"Left Bottom\" name=\"Stamina\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Drowning bar -->\n        <Widget type=\"Window\" skin=\"MW_Dialog\" position=\"0 36 230 58\" align=\"Center Top\" name=\"DrowningFrame\">\n            <Property key=\"Visible\" value=\"false\"/>\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 3 222 24\" name=\"DrowningTitle\" align=\"Center Top HStretch\">\n                <Property key=\"Caption\" value=\"#{sBreath}\"/>\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <Property key=\"TextShadow\" value=\"true\"/>\n                <Property key=\"TextShadowColour\" value=\"0 0 0\"/>\n            </Widget>\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"11 29 200 10\" align=\"Stretch\" name=\"BoundingBox\"/>\n            <Widget type=\"ProgressBar\" skin=\"MW_Progress_Drowning_Full\" position=\"13 31 196 6\" align=\"Center Top\" name=\"Drowning\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n            <Widget type=\"Widget\" skin=\"MW_Progress_Drowning_Small\" position=\"15 33 192 2\" align=\"Center Top\" name=\"Flash\"/>\n        </Widget>\n\n        <!-- Equipped weapon/selected spell name display for a few seconds after it changes -->\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"13 118 270 24\" name=\"WeaponSpellName\" align=\"Left Bottom HStretch\">\n            <Property key=\"Visible\" value=\"false\"/>\n            <Property key=\"TextAlign\" value=\"Left\"/>\n            <Property key=\"TextShadow\" value=\"true\"/>\n            <Property key=\"TextShadowColour\" value=\"0 0 0\"/>\n            <Property key=\"NeedMouse\" value=\"false\"/>\n        </Widget>\n\n        <!-- Equipped weapon box -->\n        <Widget type=\"Button\" skin=\"\" position=\"82 146 36 41\" align=\"Left Bottom\" name=\"WeapBox\">\n            <Widget type=\"Widget\" skin=\"HUD_Box\" position=\"0 0 36 36\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n                <Widget type=\"ItemWidget\" skin=\"MW_ItemIconNoShadow\" position=\"-3 -3 42 42\" align=\"Left Top\" name=\"WeapImage\">\n                    <Property key=\"NeedMouse\" value=\"false\"/>\n                </Widget>\n            </Widget>\n            <Widget type=\"ProgressBar\" skin=\"MW_EnergyBar_Weapon\" position=\"0 36 36 6\" align=\"Left Bottom\" name=\"WeapStatus\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Selected spell box -->\n        <Widget type=\"Button\" position=\"122 146 36 41\" align=\"Left Bottom\" name=\"SpellBox\">\n            <Widget type=\"Widget\" skin=\"HUD_Box\" position=\"0 0 36 36\">\n                <Widget type=\"SpellWidget\" skin=\"MW_ItemIconNoShadow\" position=\"-3 -3 42 42\" align=\"Left Top\" name=\"SpellImage\"/>\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n            <Widget type=\"ProgressBar\" skin=\"MW_EnergyBar_Magic\" position=\"0 36 36 6\" align=\"Left Bottom\" name=\"SpellStatus\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Sneak indicator box -->\n        <Widget type=\"Button\" skin=\"\" position=\"162 146 36 36\" align=\"Left Bottom\" name=\"SneakBox\">\n            <Property key=\"Visible\" value=\"false\"/>\n            <Widget type=\"Widget\" skin=\"HUD_Box\" position=\"0 0 36 36\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"2 2 32 32\" align=\"Left Top\" name=\"SneakImage\">\n                    <Property key=\"NeedMouse\" value=\"false\"/>\n                    <Property key=\"ImageTexture\" value=\"icons\\k\\stealth_sneak.dds\"/>\n                </Widget>\n            </Widget>\n        </Widget>\n\n        <!-- Spell effects box -->\n        <Widget type=\"Widget\" skin=\"HUD_Box_Transparent\" position=\"199 168 20 20\" align=\"Right Bottom\" name=\"EffectBox\">\n        </Widget>\n\n        <!-- Cell name display when cell changes -->\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 89 288 24\" name=\"CellName\" align=\"Left Bottom HStretch\">\n            <Property key=\"Visible\" value=\"false\"/>\n            <Property key=\"TextAlign\" value=\"Right\"/>\n            <Property key=\"TextShadow\" value=\"true\"/>\n            <Property key=\"TextShadowColour\" value=\"0 0 0\"/>\n            <Property key=\"NeedMouse\" value=\"false\"/>\n        </Widget>\n\n        <!-- Map box -->\n        <Widget type=\"Widget\" skin=\"\" position=\"223 123 65 65\" name=\"MiniMapBox\" align=\"Right Bottom\">\n            <Widget type=\"Widget\" skin=\"HUD_Box\" position=\"0 0 65 65\" align=\"Center\">\n\n                <Widget type=\"ScrollView\" skin=\"MW_MapView\" position=\"2 2 61 61\" align=\"Left Bottom\" name=\"MiniMap\">\n                    <Widget type=\"ImageBox\" skin=\"RotatingSkin\" position=\"0 0 32 32\" align=\"Bottom Left\" name=\"Compass\">\n                        <Property key=\"ImageTexture\" value=\"textures\\compass.dds\"/>\n                    </Widget>\n\n                    <Widget type=\"Button\" skin=\"\" position_real=\"0 0 1 1\" name=\"MiniMapButton\" align=\"Stretch\">\n                        <Property key=\"Depth\" value=\"10\"/>\n                    </Widget>\n\n                </Widget>\n\n            </Widget>\n        </Widget>\n\n        <!-- Crosshair -->\n        <Widget type=\"ImageBox\" skin=\"HUD_Crosshair\" position=\"0 0 32 32\" align=\"Center Center\" name=\"Crosshair\">\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_hud_box.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n\n    <!-- The 'box' frame that surrounds various HUD items and other elements -->\n\n    <Resource type=\"ResourceSkin\" name=\"HUD_Box\" size=\"40 40\">\n\n        <!-- The interior of the box -->\n\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"0 0 40 40\" align=\"Stretch\"/>\n\n        <!-- Borders -->\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 40 40\" align=\"Left Stretch\"/>\n\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"HUD_Box_Transparent\" size=\"40 40\">\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 40 40\" align=\"Left Stretch\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"HUD_Box_NoTransp\" size=\"40 40\">\n\n        <!-- The interior of the box -->\n\n        <Child type=\"Widget\" skin=\"DialogBG\" offset=\"0 0 40 40\" align=\"Stretch\"/>\n\n        <!-- Borders -->\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 40 40\" align=\"Left Stretch\"/>\n\n    </Resource>\n    \n    <Resource type=\"ResourceSkin\" name=\"HUD_Box_NoTransp_Owned\" size=\"40 40\">\n\n        <!-- The interior of the box -->\n\n        <Child type=\"Widget\" skin=\"DialogBG_NoTransp_Owned\" offset=\"0 0 40 40\" align=\"Stretch\"/>\n\n        <!-- Borders -->\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 40 40\" align=\"Left Stretch\"/>\n\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"HUD_Box_Owned\" size=\"40 40\">\n\n        <!-- The interior of the box -->\n\n        <Child type=\"Widget\" skin=\"DialogBG_Owned\" offset=\"0 0 40 40\" align=\"Stretch\"/>\n\n        <!-- Borders -->\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 40 40\" align=\"Left Stretch\"/>\n\n    </Resource>\n\n    <!-- Defines crosshair color -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HUD_Crosshair\" texture=\"textures\\target.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Defines owned crosshair color -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HUD_Crosshair_Owned\" texture=\"textures\\target.dds\">\n        <Property key=\"Colour\" value=\"#{setting=GUI,color crosshair owned}\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_hud_energybar.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <!-- Energy bar frame graphics -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HUD_Bar_Top\" texture=\"textures\\menu_small_energy_bar_top.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HUD_Bar_Bottom\" texture=\"textures\\menu_small_energy_bar_bottom.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HUD_Bar_Side\" texture=\"textures\\menu_small_energy_bar_vert.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Progress bar track, various colors -->\n    <Resource type=\"ResourceSkin\" name=\"MW_BarTrack_Red\" size=\"16 8\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"#{fontcolour=health}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 8\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 8\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_BarTrack_Green\" size=\"16 8\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"#{fontcolour=fatigue}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 8\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 8\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_BarTrack_Blue\" size=\"16 8\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"#{fontcolour=magic}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 8\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 8\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_BarTrack_Yellow\" size=\"16 8\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"1 1 0\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 8\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 8\"/>\n        </BasisSkin>\n    </Resource>\n\n\n    <Resource type=\"ResourceSkin\" name=\"MW_BarTrack_Magic\" size=\"16 2\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"#{fontcolour=magic_fill}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 2\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 2\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_BarTrack_Weapon\" size=\"16 2\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"#{fontcolour=weapon_fill}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 2\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 2\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_EnergyBar_Magic\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_BarTrack_Magic\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_EnergyBar_Weapon\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_BarTrack_Weapon\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_EnergyBar_Red\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_BarTrack_Red\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_EnergyBar_Green\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_BarTrack_Green\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_EnergyBar_Blue\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_BarTrack_Blue\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_EnergyBar_Yellow\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_BarTrack_Yellow\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_infobox.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 545 265\" align=\"Center\" name=\"_Main\">\n\n        <!-- Edit box -->\n        <Widget type=\"Widget\" skin=\"\" position=\"14 14 516 70\" name=\"TextBox\" align=\"Top HCenter\">\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"4 4 508 62\" name=\"Text\" align=\"Top HCenter\">\n                <Property key=\"WordWrap\" value=\"1\" />\n            </Widget>\n        </Widget>\n\n        <!-- Button bar, buttons are created as children -->\n        <Widget type=\"Widget\" skin=\"\" position=\"72 98 450 150\" name=\"ButtonBar\" align=\"Top HCenter\" />\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_interactive_messagebox.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"MessageBox\" position=\"0 0 500 400\" align=\"Center\" name=\"_Main\">\n        <Widget type=\"EditBox\" skin=\"MW_TextEditClient\" position=\"10 10 490 20\" align=\"Left Top Stretch\" name=\"message\">\n            <Property key=\"FontName\" value=\"Default\"/>\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"Static\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"MultiLine\" value=\"1\"/>\n            <Property key=\"VisibleVScroll\" value=\"1\"/>\n            <Property key=\"NeedKey\" value=\"false\"/>\n        </Widget>\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 500 400\" align=\"Stretch\" name=\"buttons\">\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_interactive_messagebox_notransp.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"MessageBox\" position=\"0 0 500 400\" align=\"Center\" name=\"_Main\">\n        <Widget type=\"EditBox\" skin=\"MW_TextEditClient\" position=\"10 10 490 20\" align=\"Left Top Stretch\" name=\"message\">\n            <Property key=\"FontName\" value=\"Default\"/>\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"Static\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"MultiLine\" value=\"1\"/>\n            <Property key=\"VisibleVScroll\" value=\"1\"/>\n            <Property key=\"NeedKey\" value=\"false\"/>\n        </Widget>\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 500 400\" align=\"Stretch\" name=\"buttons\">\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_inventory_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window_Pinnable\" layer=\"Windows\" position=\"0 0 600 300\" name=\"_Main\">\n        <Property key=\"MinSize\" value=\"40 40\"/>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 224 223\" align=\"Left Top\" name=\"LeftPane\">\n\n            <!-- Player encumbrance -->\n            <Widget type=\"MWDynamicStat\" skin=\"MW_ChargeBar_Blue\" position=\"8 8 212 24\" name=\"EncumbranceBar\" align=\"Left Top HStretch\">\n            </Widget>\n\n            <!-- Avatar -->\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 38 212 185\" name=\"Avatar\" align=\"Left Top Stretch\">\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"3 3 206 158\" align=\"Stretch\" name=\"AvatarImage\">\n                    <UserString key=\"ToolTipType\" value=\"AvatarItemSelection\"/>\n                </Widget>\n                <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"0 161 212 24\" align=\"HStretch Bottom\" name=\"ArmorRating\">\n                    <Property key=\"NeedMouse\" value=\"false\"/>\n                </Widget>\n            </Widget>\n\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"228 0 350 223\" align=\"Left Top\" name=\"RightPane\">\n\n            <!-- Items in inventory -->\n            <Widget type=\"ItemView\" skin=\"MW_ItemView\" position=\"0 38 350 185\" name=\"ItemView\" align=\"Left Top Stretch\">\n            </Widget>\n\n            <!-- Categories -->\n            <Widget type=\"HBox\" position=\"0 6 350 28\" align=\"Left Top HStretch\" name=\"Categories\">\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 60 24\" name=\"AllButton\">\n                    <Property key=\"Caption\" value=\"#{sAllTab}\"/>\n                    <Property key=\"NeedKey\" value=\"false\"/>\n                </Widget>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 60 24\" name=\"WeaponButton\">\n                    <Property key=\"Caption\" value=\"#{sWeaponTab}\"/>\n                    <Property key=\"NeedKey\" value=\"false\"/>\n                </Widget>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 60 24\" name=\"ApparelButton\">\n                    <Property key=\"Caption\" value=\"#{sApparelTab}\"/>\n                    <Property key=\"NeedKey\" value=\"false\"/>\n                </Widget>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 60 24\" name=\"MagicButton\">\n                    <Property key=\"Caption\" value=\"#{sMagicTab}\"/>\n                    <Property key=\"NeedKey\" value=\"false\"/>\n                </Widget>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 60 24\" name=\"MiscButton\">\n                    <Property key=\"Caption\" value=\"#{sMiscTab}\"/>\n                    <Property key=\"NeedKey\" value=\"false\"/>\n                </Widget>\n                <!-- Search box-->\n                <Widget type=\"EditBox\" skin=\"MW_TextBoxEditWithBorder\" position=\"0 0 0 23\" name=\"FilterEdit\">\n                    <UserString key=\"HStretch\" value=\"true\"/>\n                    <UserString key=\"AcceptTab\" value=\"true\"/>\n                </Widget>\n            </Widget>\n\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_itemselection_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" position=\"0 0 380 285\" layer=\"Windows\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 8 300 18\" name=\"Label\"/>\n\n        <Widget type=\"ItemView\" skin=\"MW_ItemView\" position=\"8 34 355 200\" name=\"ItemView\" align=\"Left Top Stretch\">\n        </Widget>\n\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"340 240 24 24\" name=\"CancelButton\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sCancel}\"/>\n        </Widget>\n\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_jail_screen.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"LoadingScreen\" position=\"0 0 300 48\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"16 4 260 18\" align=\"Stretch\" name=\"LoadingText\">\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"Caption\" value=\"#{sInPrisonTitle}\"/>\n        </Widget>\n\n        <Widget type=\"ScrollBar\" skin=\"MW_ProgressScroll_Loading\" position=\"16 26 260 6\" name=\"ProgressBar\">\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_journal.layout",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n\n  <Widget type=\"Window\" skin=\"\" layer=\"JournalBooks\" align=\"Left|Top\" position=\"0 0 565 390\" name=\"_Main\">\n    <!-- pages -->\n    <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"-70 0 705 390\" align=\"Top|Right\" name=\"JImage\">\n    <Property key=\"ImageTexture\" value=\"textures\\tx_menubook.dds\"/>\n    <Widget type=\"Widget\" position=\"70 0 565 390\" align=\"Top|Right\">\n\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"40 350 64 32\" name=\"OptionsBTN\">\n        <!-- Image set at runtime since it may not be available in all versions of the game -->\n      </Widget>\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"205 350 48 32\" name=\"PrevPageBTN\">\n        <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_prev_over.dds\"/>\n        <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_prev_idle.dds\"/>\n        <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_prev_pressed.dds\"/>\n      </Widget>\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"300 350 48 32\" name=\"NextPageBTN\">\n        <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_next_over.dds\"/>\n        <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_next_idle.dds\"/>\n        <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_next_pressed.dds\"/>\n      </Widget>\n\n      <Widget type=\"TextBox\" skin=\"NormalText\" position=\"150 350 32 16\" name=\"PageOneNum\">\n        <Property key=\"FontName\" value=\"Journalbook Magic Cards\"/>\n        <Property key=\"TextColour\" value=\"0 0 0\"/>\n      </Widget>\n\n      <Widget type=\"TextBox\" skin=\"NormalText\" position=\"410 350 32 16\" name=\"PageTwoNum\">\n        <Property key=\"FontName\" value=\"Journalbook Magic Cards\"/>\n        <Property key=\"TextColour\" value=\"0 0 0\"/>\n      </Widget>\n\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"460 350 48 32\" name=\"CloseBTN\">\n        <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_close_over.dds\"/>\n        <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_close_idle.dds\"/>\n        <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_close_pressed.dds\"/>\n      </Widget>\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"460 350 64 32\" name=\"JournalBTN\">\n        <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_journal_over.dds\"/>\n        <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_journal_idle.dds\"/>\n        <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_journal_pressed.dds\"/>\n      </Widget>\n\n\n      <!-- text pages -->\n      <Widget type=\"BookPage\" skin=\"MW_BookPage\" position=\"30 22 240 320\" name=\"LeftBookPage\"/>\n      <Widget type=\"BookPage\" skin=\"MW_BookPage\" position=\"295 22 240 320\" name=\"RightBookPage\"/>\n\n      <!-- options overlay -->\n    </Widget>\n    </Widget>\n\n    <!-- \"options\" -->\n    <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"293 0 386 350\" name=\"OptionsOverlay\">\n      <Property key=\"ImageTexture\" value=\"textures\\tx_menubook_bookmark.dds\"/>\n\n\n      <Widget type=\"BookPage\" skin=\"MW_BookPage\" position=\"20 10 92 260\" name=\"LeftTopicIndex\"/>\n      <Widget type=\"BookPage\" skin=\"MW_BookPage\" position=\"75 10 92 260\" name=\"CenterTopicIndex\"/>\n      <Widget type=\"BookPage\" skin=\"MW_BookPage\" position=\"130 10 92 260\" name=\"RightTopicIndex\"/>\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"76 15 96 32\" Align=\"Top|Left\" name=\"ShowActiveBTN\">\n        <!-- Image set at runtime since it may not be available in all versions of the game -->\n        <Property key=\"TextureRect\" value=\"0 0 96 32\"/>\n      </Widget>\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"83 15 72 32\" Align=\"Top|Left\" name=\"ShowAllBTN\">\n        <!-- Image set at runtime since it may not be available in all versions of the game -->\n        <Property key=\"TextureRect\" value=\"0 0 72 32\"/>\n      </Widget>\n\n      <Widget type=\"MWList\" skin=\"MW_QuestList\" position=\"8 40 226 212\" name=\"TopicsList\" align=\"Right VStretch\">\n      </Widget>\n\n      <Widget type=\"MWList\" skin=\"MW_QuestList\" position=\"8 40 226 212\" name=\"QuestsList\" align=\"Right VStretch\">\n      </Widget>\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"20 265 56 32\" Align=\"Top|Left\" name=\"TopicsBTN\">\n        <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_topics_over.dds\"/>\n        <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_topics_idle.dds\"/>\n        <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_topics_pressed.dds\"/>\n      </Widget>\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"130 265 56 32\" Align=\"Top|Left\" name=\"QuestsBTN\">\n        <!-- Image set at runtime since it may not be available in all versions of the game -->\n      </Widget>\n\n      <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"92 290 56 32\" Align=\"Top|Left\" name=\"CancelBTN\">\n        <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_cancel_over.dds\"/>\n        <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_cancel_idle.dds\"/>\n        <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_cancel_pressed.dds\"/>\n      </Widget>\n    </Widget>\n  </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_journal.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <Resource type=\"ResourceSkin\" name=\"MW_BookClient\" size=\"10 10\">\n            <Property key=\"FontName\" value=\"Journalbook Magic Cards\"/>\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n            <Property key=\"TextColour\" value=\"0 0 0\"/>\n            <BasisSkin type=\"EditText\" offset=\"0 0 10 10\" align=\"Stretch\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_BookPage\" size=\"0 0 50 50\">\n            <BasisSkin type=\"PageDisplay\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_QuestList\" size=\"516 516\" align=\"Left Top\">\n            <Property key=\"ListItemSkin\" value=\"MW_QuestLink\"/>\n\n            <Child type=\"Widget\" skin=\"\" offset=\"3 3 510 510\" align=\"Top Left Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_QuestLink\" size=\"5 5\">\n        <Property key=\"FontName\" value=\"Journalbook Magic Cards\"/>\n        <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n\n        <BasisSkin type=\"SimpleText\" offset=\"2 0 1 5\" align=\"Stretch\">\n            <State name=\"normal\" colour=\"#{fontcolour=journal_topic}\" shift=\"0\"/>\n            <State name=\"highlighted\" colour=\"#{fontcolour=journal_topic_over}\" shift=\"0\"/>\n            <State name=\"pushed\" colour=\"#{fontcolour=journal_topic_pressed}\" shift=\"0\"/>\n            <State name=\"normal_checked\" colour=\"0.2 0.2 0.2\" shift=\"0\"/>\n            <State name=\"highlighted_checked\" colour=\"0.4 0.4 0.4\" shift=\"0\"/>\n            <State name=\"pushed_checked\" colour=\"0.5 0.5 0.5\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_layers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layer\" version=\"1.0\">\n    <Layer name=\"Scene\" overlapped=\"false\" pick=\"false\"/>\n    <Layer name=\"Overlay\" overlapped=\"false\" pick=\"false\"/>\n    <Layer name=\"AdditiveOverlay\" type=\"AdditiveLayer\" pick=\"false\"/>\n    <Layer name=\"HUD\" overlapped=\"false\" pick=\"true\"/>\n    <Layer name=\"Menu\" overlapped=\"false\" pick=\"true\"/>\n    <Layer name=\"MainMenu\" overlapped=\"true\" pick=\"true\"/>\n    <Layer name=\"JournalBooks\" type=\"ScalingLayer\" pick=\"true\">\n        <Property key=\"Size\" value=\"600 520\"/>\n    </Layer>\n    <Layer name=\"Windows\" overlapped=\"true\" pick=\"true\"/>\n    <Layer name=\"Debug\" overlapped=\"true\" pick=\"true\"/>\n    <Layer name=\"Popup\" overlapped=\"true\" pick=\"true\"/>\n    <Layer name=\"Notification\" overlapped=\"false\" pick=\"false\"/>\n    <Layer name=\"DragAndDrop\" overlapped=\"false\" pick=\"false\"/>\n    <Layer name=\"LoadingScreen\" overlapped=\"false\" pick=\"true\"/>\n    <Layer name=\"MessageBox\" overlapped=\"false\" pick=\"true\"/>\n    <Layer name=\"InputBlocker\" overlapped=\"false\" pick=\"true\"/>\n    <Layer name=\"Pointer\" overlapped=\"false\" pick=\"false\"/>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_levelup_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"VBox\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 0 0\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Padding\" value=\"10\"/>\n        <Property key=\"Spacing\" value=\"8\"/>\n        <Property key=\"AutoResize\" value=\"true\"/>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"28 14 391 198\">\n            <UserString key=\"HStretch\" value=\"false\"/>\n            <UserString key=\"VStretch\" value=\"false\"/>\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" name=\"ClassImage\" position=\"4 4 383 190\">\n            </Widget>\n        </Widget>\n\n        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 218 391 24\" name=\"LevelText\">\n        </Widget>\n\n        <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"36 280 391 24\" name=\"LevelDescription\">\n            <Property key=\"MultiLine\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"Static\" value=\"true\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 150 16\" name=\"Coins\">\n            <UserString key=\"HStretch\" value=\"true\"/>\n            <UserString key=\"VStretch\" value=\"false\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"0 280 420 84\" name=\"AssignWidget\">\n            <UserString key=\"HStretch\" value=\"false\"/>\n            <UserString key=\"VStretch\" value=\"false\"/>\n\n            <Widget type=\"TextBox\" skin=\"SandTextVCenter\" position=\"32 0 100 20\" name=\"AttribMultiplier1\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextVCenter\" position=\"32 20 100 20\" name=\"AttribMultiplier2\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextVCenter\" position=\"32 40 100 20\" name=\"AttribMultiplier3\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextVCenter\" position=\"32 60 100 20\" name=\"AttribMultiplier4\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextVCenter\" position=\"218 0 100 20\" name=\"AttribMultiplier5\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextVCenter\" position=\"218 20 100 20\" name=\"AttribMultiplier6\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextVCenter\" position=\"218 40 100 20\" name=\"AttribMultiplier7\"/>\n            <Widget type=\"TextBox\" skin=\"SandTextVCenter\" position=\"218 60 100 20\" name=\"AttribMultiplier8\"/>\n\n            <Widget type=\"HBox\" position=\"52 0 200 20\">\n                <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" name=\"Attrib1\">\n                    <UserString key=\"TextPadding\" value=\"0 0\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeStrength}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sStrDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_strength.dds\"/>\n                    <Property key=\"Caption\" value=\"#{sAttributeStrength}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"AttribVal1\">\n                </Widget>\n            </Widget>\n\n            <Widget type=\"HBox\" position=\"52 20 200 20\">\n                <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" name=\"Attrib2\">\n                    <UserString key=\"TextPadding\" value=\"0 0\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeIntelligence}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sIntDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_int.dds\"/>\n                    <Property key=\"Caption\" value=\"#{sAttributeIntelligence}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"AttribVal2\">\n                </Widget>\n            </Widget>\n\n            <Widget type=\"HBox\" position=\"52 40 200 20\">\n                <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" name=\"Attrib3\">\n                    <UserString key=\"TextPadding\" value=\"0 0\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeWillpower}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sWilDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_wilpower.dds\"/>\n                    <Property key=\"Caption\" value=\"#{sAttributeWillpower}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"AttribVal3\">\n                </Widget>\n            </Widget>\n\n            <Widget type=\"HBox\" position=\"52 60 200 20\">\n                <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" name=\"Attrib4\">\n                    <UserString key=\"TextPadding\" value=\"0 0\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeAgility}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sAgiDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_agility.dds\"/>\n                    <Property key=\"Caption\" value=\"#{sAttributeAgility}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"AttribVal4\">\n                </Widget>\n            </Widget>\n\n\n            <Widget type=\"HBox\" position=\"238 0 200 20\">\n                <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" name=\"Attrib5\">\n                    <UserString key=\"TextPadding\" value=\"0 0\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeSpeed}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sSpdDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_speed.dds\"/>\n                    <Property key=\"Caption\" value=\"#{sAttributeSpeed}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"AttribVal5\">\n                </Widget>\n            </Widget>\n\n            <Widget type=\"HBox\" position=\"238 20 200 20\">\n                <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" name=\"Attrib6\">\n                    <UserString key=\"TextPadding\" value=\"0 0\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeEndurance}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sEndDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_endurance.dds\"/>\n                    <Property key=\"Caption\" value=\"#{sAttributeEndurance}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"AttribVal6\">\n                </Widget>\n            </Widget>\n\n            <Widget type=\"HBox\" position=\"238 40 200 20\">\n                <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" name=\"Attrib7\">\n                <UserString key=\"TextPadding\" value=\"0 0\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributePersonality}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sPerDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_personality.dds\"/>\n                    <Property key=\"Caption\" value=\"#{sAttributePersonality}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"AttribVal7\">\n                </Widget>\n            </Widget>\n\n            <Widget type=\"HBox\" position=\"238 60 200 20\">\n                <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" name=\"Attrib8\">\n                <UserString key=\"TextPadding\" value=\"0 0\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeLuck}\"/>\n                <UserString key=\"Caption_AttributeDescription\" value=\"#{sLucDesc}\"/>\n                <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_luck.dds\"/>\n                    <Property key=\"Caption\" value=\"#{sAttributeLuck}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"AttribVal8\">\n                </Widget>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"HBox\" skin=\"\" position=\"0 0 330 24\">\n            <UserString key=\"HStretch\" value=\"true\"/>\n            <Widget type=\"Spacer\"/>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 0 24\" name=\"OkButton\">\n                <Property key=\"Caption\" value=\"#{sOk}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_list.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n\n    <!-- Horizontal Scrollbar -->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_HScroll\" size=\"90 14\">\n        <Property key=\"TrackRangeMargins\" value=\"20 19\"/>\n        <Property key=\"MinTrackSize\" value=\"14\"/>\n        <Property key=\"VerticalAlignment\" value=\"false\"/>\n        <Property key=\"MoveToClick\" value=\"true\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Property key=\"WheelPage\" value=\"36\"/>\n        <Property key=\"ViewPage\" value=\"36\"/>\n        <Property key=\"Page\" value=\"36\"/>\n\n        <!-- Tracker must be last to be on top and receive mouse events -->\n\n        <Child type=\"Button\" skin=\"MW_Box\" offset=\"18 0 54 14\" align=\"Stretch\" name=\"Background\"/>\n\n        <!-- These are only provided to get mouse input, they should have no skin and be transparent -->\n\n        <Child type=\"Button\" skin=\"MW_ScrollEmptyPart\" offset=\"14 0 24 14\" align=\"Top HStretch\" name=\"FirstPart\"/>\n        <Child type=\"Button\" skin=\"MW_ScrollEmptyPart\" offset=\"52 0 24 14\" align=\"Top HStretch\" name=\"SecondPart\"/>\n\n        <!-- Arrows -->\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 15 14\" align=\"Left VStretch\"/>\n        <Child type=\"Button\" skin=\"MW_ArrowLeft\" offset=\"2 2 10 10\" align=\"Left VStretch\" name=\"Start\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"75 0 15 14\" align=\"Right VStretch\"/>\n        <Child type=\"Button\" skin=\"MW_ArrowRight\" offset=\"77 2 10 10\" align=\"Right VStretch\" name=\"End\"/>\n\n        <Child type=\"Button\" skin=\"MW_ScrollTrackH\" offset=\"38 2 30 9\" align=\"Left VStretch\" name=\"Track\"/>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"MW_ScrollTrackH\" texture=\"textures\\omw_menu_scroll_center_h.dds\">\n        <Property key=\"MinTrackSize\" value=\"14\"/>\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Vertical Scrollbar -->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_VScroll\" size=\"14 90\">\n        <Property key=\"TrackRangeMargins\" value=\"20 19\"/>\n        <Property key=\"MinTrackSize\" value=\"14\"/>\n        <Property key=\"MoveToClick\" value=\"true\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Property key=\"WheelPage\" value=\"36\"/>\n        <Property key=\"ViewPage\" value=\"36\"/>\n        <Property key=\"Page\" value=\"36\"/>\n\n        <!-- Background widget trick that must go first to be placed behind Track and FirstPart/SecondPart widgets, provides the bar texture -->\n\n        <Child type=\"Button\" skin=\"MW_Box\" offset=\"0 18 14 55\" align=\"Stretch\" name=\"Background\"/>\n\n        <!-- These are only provided to get mouse input, they should have no skin and be transparent -->\n\n        <Child type=\"Button\" skin=\"MW_ScrollEmptyPart\" offset=\"0 14 24 14\" align=\"Left VStretch\" name=\"FirstPart\"/>\n        <Child type=\"Button\" skin=\"MW_ScrollEmptyPart\" offset=\"0 52 24 14\" align=\"Left VStretch\" name=\"SecondPart\"/>\n\n        <!-- Arrows -->\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 14 15\" align=\"Top HStretch\"/>\n        <Child type=\"Button\" skin=\"MW_ArrowUp\" offset=\"2 2 10 10\" align=\"Top HStretch\" name=\"Start\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 76 14 15\" align=\"Bottom HStretch\"/>\n        <Child type=\"Button\" skin=\"MW_ArrowDown\" offset=\"2 78 10 10\" align=\"Bottom HStretch\" name=\"End\"/>\n\n        <!-- Tracker must be last to be on top and receive mouse events -->\n\n        <Child type=\"Button\" skin=\"MW_ScrollTrackV\" offset=\"2 40 9 30\" align=\"Top HStretch\" name=\"Track\"/>\n\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"MW_ScrollTrackV\" texture=\"textures\\omw_menu_scroll_center_v.dds\">\n        <Property key=\"MinTrackSize\" value=\"14\"/>\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Empty space in scrollbar -->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ScrollEmptyPart\" size=\"16 16\" >\n    </Resource>\n\n    <!-- Header text -->\n\n    <Resource type=\"ResourceSkin\" name=\"HeaderText\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Center\"/>\n        <Property key=\"TextColour\" value=\"#{fontcolour=header}\"/>\n\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\"/>\n    </Resource>\n\n    <!-- list skin -->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ListLine\" size=\"5 5\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n\n        <BasisSkin type=\"SimpleText\" offset=\"2 0 1 5\" align=\"Stretch\">\n            <State name=\"disabled\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal\" colour=\"#{fontcolour=normal}\" shift=\"0\"/>\n            <State name=\"highlighted\" colour=\"#{fontcolour=normal_over}\" shift=\"0\"/>\n            <State name=\"pushed\" colour=\"#{fontcolour=normal_pressed}\" shift=\"0\"/>\n            <State name=\"disabled_checked\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal_checked\" colour=\"#{fontcolour=active}\" shift=\"0\"/>\n            <State name=\"highlighted_checked\" colour=\"#{fontcolour=active_over}\" shift=\"0\"/>\n            <State name=\"pushed_checked\" colour=\"#{fontcolour=active_pressed}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_List\" size=\"516 516\" align=\"Left Top\">\n        <Property key=\"NeedKey\" value=\"true\"/>\n        <Property key=\"SkinLine\" value=\"MW_ListLine\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n\n        <Child type=\"ScrollBar\" skin=\"MW_VScroll\" offset=\"498 4 14 508\" align=\"Right VStretch\" name=\"VScroll\"/>\n\n        <Child type=\"Widget\" skin=\"\" offset=\"3 4 493 508\" align=\"Stretch\" name=\"Client\"/>\n\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_PopupList\" size=\"516 516\" align=\"Left Top\">\n        <Property key=\"NeedKey\" value=\"true\"/>\n        <Property key=\"SkinLine\" value=\"MW_ListLine\"/>\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n\n        <Child type=\"ScrollBar\" skin=\"MW_VScroll\" offset=\"498 3 14 509\" align=\"Right VStretch\" name=\"VScroll\"/>\n\n        <Child type=\"Widget\" skin=\"\" offset=\"3 3 493 509\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_PopupListNoTransp\" size=\"516 516\" align=\"Left Top\">\n        <Property key=\"NeedKey\" value=\"true\"/>\n        <Property key=\"SkinLine\" value=\"MW_ListLine\"/>\n\n        <Child type=\"Widget\" skin=\"FullBlackBG\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n\n        <Child type=\"ScrollBar\" skin=\"MW_VScroll\" offset=\"498 3 14 509\" align=\"Right VStretch\" name=\"VScroll\"/>\n\n        <Child type=\"Widget\" skin=\"\" offset=\"3 3 493 509\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ItemView\" size=\"516 516\" align=\"Left Top\">\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n\n        <Child type=\"ScrollView\" skin=\"MW_ScrollViewH\" offset=\"3 3 509 509\" align=\"Stretch\" name=\"ScrollView\">\n            <Property key=\"CanvasAlign\" value=\"Left Top\"/>\n            <Property key=\"NeedMouse\" value=\"true\"/>\n        </Child>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ItemChargeView\" size=\"516 516\" align=\"Left Top\">\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n\n        <Child type=\"ScrollView\" skin=\"MW_ScrollView\" offset=\"3 3 509 509\" align=\"Stretch\" name=\"ScrollView\">\n            <Property key=\"CanvasAlign\" value=\"Left Top\"/>\n            <Property key=\"NeedMouse\" value=\"true\"/>\n        </Child>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_SpellView\" size=\"516 516\" align=\"Left Top\">\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n\n        <Child type=\"ScrollView\" skin=\"MW_ScrollView\" offset=\"3 3 509 509\" align=\"Stretch\" name=\"ScrollView\">\n            <Property key=\"CanvasAlign\" value=\"Left Top\"/>\n            <Property key=\"NeedMouse\" value=\"true\"/>\n        </Child>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_SimpleList\" size=\"516 516\" align=\"Left Top\">\n        <Property key=\"ListItemSkin\" value=\"MW_ListLine\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 516 516\" align=\"Stretch\"/>\n\n        <Child type=\"Widget\" skin=\"\" offset=\"3 3 510 510\" align=\"Top Left Stretch\" name=\"Client\"/>\n\n    </Resource>\n\n    <!-- Horizontal line -->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_HLine\" size=\"512 10\" texture=\"textures\\menu_thin_border_top.dds\">\n        <BasisSkin type=\"SubSkin\" offset=\"0 0 512 2\" align=\"Bottom HStretch\">\n            <State name=\"normal\" offset=\"0 0 512 2\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Arrows -->\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"MW_ArrowLeft\" texture=\"textures\\omw_menu_scroll_left.dds\">\n        <BasisSkin type=\"SubSkin\" align=\"Stretch\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"MW_ArrowRight\" texture=\"textures\\omw_menu_scroll_right.dds\">\n        <BasisSkin type=\"SubSkin\" align=\"Stretch\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"MW_ArrowUp\" texture=\"textures\\omw_menu_scroll_up.dds\">\n        <BasisSkin type=\"SubSkin\" align=\"Stretch\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"MW_ArrowDown\" texture=\"textures\\omw_menu_scroll_down.dds\">\n        <BasisSkin type=\"SubSkin\" align=\"Stretch\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_loading_screen.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <!-- The entire screen -->\n    <Widget type=\"Widget\" layer=\"LoadingScreen\" position=\"0 0 300 300\" name=\"_Main\" align=\"Stretch\">\n\n        <Widget type=\"Window\" skin=\"MW_Dialog\" position=\"0 245 300 48\" align=\"Bottom HCenter\" name=\"LoadingBox\">\n\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"16 5 260 18\" align=\"Stretch\" name=\"LoadingText\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n            </Widget>\n\n            <Widget type=\"ScrollBar\" skin=\"MW_ProgressScroll_Loading\" position=\"16 26 260 6\" align=\"Top HCenter\" name=\"ProgressBar\">\n            </Widget>\n\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_magicselection_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" position=\"0 0 330 370\" layer=\"Windows\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 8 292 24\">\n            <Property key=\"Caption\" value=\"#{sMagicSelectTitle}\"/>\n        </Widget>\n\n        <Widget type=\"SpellView\" skin=\"MW_SpellView\" position=\"8 38 306 285\" align=\"Left Top Stretch\" name=\"MagicList\">\n        </Widget>\n\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\" position=\"284 330 32 24\" align=\"Right Bottom\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sCancel}\"/>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_mainmenu.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <!-- The entire screen -->\n    <Widget type=\"Widget\" layer=\"MainMenu\" position=\"0 0 300 300\" name=\"_Main\">\n        <Widget type=\"EditBox\" skin=\"MW_TextBoxEdit\" position=\"0 250 280 50\" align=\"Bottom Right\" name=\"VersionText\">\n            <Property key=\"TextAlign\" value=\"Right\"/>\n            <Property key=\"TextShadow\" value=\"true\"/>\n            <Property key=\"TextShadowColour\" value=\"0 0 0\"/>\n            <Property key=\"ReadOnly\" value=\"true\"/>\n            <Property key=\"MultiLine\" value=\"true\"/>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_mainmenu.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_map_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window_Pinnable\" layer=\"Windows\" position=\"0 0 300 300\" name=\"_Main\">\n        <Property key=\"MinSize\" value=\"40 40\"/>\n\n        <!-- Local map -->\n        <Widget type=\"ScrollView\" skin=\"MW_MapView\" position=\"0 0 284 264\" align=\"Stretch\" name=\"LocalMap\">\n            <Widget type=\"ImageBox\" skin=\"RotatingSkin\" position=\"0 0 32 32\" align=\"Top Left\" name=\"CompassLocal\">\n                <Property key=\"ImageTexture\" value=\"textures\\compass.dds\"/>\n            </Widget>\n\n            <Widget type=\"Button\" skin=\"\" position_real=\"0 0 1 1\" name=\"EventBoxLocal\" align=\"Stretch\">\n                <Property key=\"Depth\" value=\"10\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Global map -->\n        <Widget type=\"ScrollView\" skin=\"MW_MapView\" position=\"0 0 284 264\" align=\"Stretch\" name=\"GlobalMap\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position_real=\"0 0 1 1\" align=\"Stretch\" name=\"GlobalMapImage\">\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position_real=\"0 0 1 1\" align=\"Stretch\" name=\"GlobalMapOverlay\"/>\n            </Widget>\n\n            <Widget type=\"ImageBox\" skin=\"RotatingSkin\" position=\"0 0 32 32\" align=\"Top Left\" name=\"CompassGlobal\">\n                <Property key=\"ImageTexture\" value=\"textures\\compass.dds\"/>\n            </Widget>\n\n            <Widget type=\"Button\" skin=\"\" position_real=\"0 0 1 1\" name=\"EventBoxGlobal\" align=\"Stretch\"/>\n        </Widget>\n\n        <!-- World button -->\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"213 233 61 22\" align=\"Bottom Right\" name=\"WorldButton\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"NeedKey\" value=\"false\"/>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_map_window.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <Resource type=\"ResourceSkin\" name=\"MW_MapView\" size=\"516 516\">\n        <Property key=\"NeedKey\" value=\"false\"/>\n        <Child type=\"Widget\" skin=\"\" offset=\"0 0 516 516\" align=\"Stretch\" name=\"Client\"/>\n\n        <!-- invisible scroll bars, needed for setting the view offset -->\n        <Child type=\"ScrollBar\" skin=\"\" offset=\"0 0 0 0\" align=\"Default\" name=\"VScroll\"/>\n        <Child type=\"ScrollBar\" skin=\"\" offset=\"0 0 0 0\" align=\"Default\" name=\"HScroll\"/>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_merchantrepair.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 418 248\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Visible\" value=\"false\"/>\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 24 418 24\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sRepairServiceTitle}\"/>\n        </Widget>\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 3 418 24\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"Caption\" value=\"#{sServiceRepairTitle}\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"10 46 389 156\" align=\"Left Stretch\">\n            <Widget type=\"ScrollView\" skin=\"MW_ScrollView\" position=\"4 4 381 145\" align=\"Left Top Stretch\" name=\"RepairView\">\n                <Property key=\"CanvasAlign\" value=\"Left\"/>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"10 208 300 24\" name=\"PlayerGold\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Left\"/>\n        </Widget>\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"343 209 56 24\" name=\"OkButton\" align=\"Right Top\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sOK}\"/>\n        </Widget>\n\n    </Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_messagebox.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"VBox\" skin=\"HUD_Box\" layer=\"Notification\" position=\"0 0 0 0\" name=\"_Main\">\n        <Property key=\"Padding\" value=\"10\"/>\n        <Property key=\"AutoResize\" value=\"true\"/>\n\n        <Widget type=\"AutoSizedEditBox\" skin=\"MW_TextEditClient\" position=\"0 0 300 0\" name=\"message\" align=\"Left Top\">\n            <Property key=\"FontName\" value=\"Default\"/>\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"Static\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"MultiLine\" value=\"1\"/>\n            <Property key=\"VisibleVScroll\" value=\"1\"/>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_persuasion_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 220 192\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 4 220 24\">\n            <Property key=\"Caption\" value=\"#{sPersuasionMenuTitle}\"/>\n            <Property key=\"TextAlign\" value=\"Center\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 32 196 115\">\n            <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" position=\"4 0 0 18\" name=\"AdmireButton\">\n                <Property key=\"Caption\" value=\"#{sAdmire}\"/>\n                <Property key=\"TextAlign\" value=\"Left\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" position=\"4 18 0 18\" name=\"IntimidateButton\">\n                <Property key=\"Caption\" value=\"#{sIntimidate}\"/>\n                <Property key=\"TextAlign\" value=\"Left\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" position=\"4 36 0 18\" name=\"TauntButton\">\n                <Property key=\"Caption\" value=\"#{sTaunt}\"/>\n                <Property key=\"TextAlign\" value=\"Left\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" position=\"4 54 0 18\" name=\"Bribe10Button\">\n                <Property key=\"Caption\" value=\"#{sBribe 10 Gold}\"/>\n                <Property key=\"TextAlign\" value=\"Left\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" position=\"4 72 0 18\" name=\"Bribe100Button\">\n                <Property key=\"Caption\" value=\"#{sBribe 100 Gold}\"/>\n                <Property key=\"TextAlign\" value=\"Left\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"SandTextButton\" position=\"4 90 0 18\" name=\"Bribe1000Button\">\n                <Property key=\"Caption\" value=\"#{sBribe 1000 Gold}\"/>\n                <Property key=\"TextAlign\" value=\"Left\"/>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 152 208 24\" name=\"GoldLabel\">\n            <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n        </Widget>\n\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"204 154 0 24\" name=\"CancelButton\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sCancel}\"/>\n        </Widget>\n\n    </Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_pointer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\">\n    <Resource type=\"ResourceImageSetPointer\" name=\"arrow\">\n        <Property key=\"Point\" value=\"7 0\"/>\n        <Property key=\"Size\" value=\"32 32\"/>\n        <Property key=\"Resource\" value=\"ArrowPointerImage\"/>\n        <Property key=\"Rotation\" value=\"0\"/>\n    </Resource>\n    <Resource type=\"ResourceImageSetPointer\" name=\"hresize\">\n        <Property key=\"Point\" value=\"16 14\"/>\n        <Property key=\"Size\" value=\"32 32\"/>\n        <Property key=\"Resource\" value=\"HResizePointerImage\"/>\n        <Property key=\"Rotation\" value=\"0\"/>\n    </Resource>\n    <Resource type=\"ResourceImageSetPointer\" name=\"vresize\">\n        <Property key=\"Point\" value=\"17 16\"/>\n        <Property key=\"Size\" value=\"32 32\"/>\n        <Property key=\"Resource\" value=\"HResizePointerImage\"/>\n        <Property key=\"Rotation\" value=\"90\"/>\n    </Resource>\n    <Resource type=\"ResourceImageSetPointer\" name=\"dresize\">\n        <Property key=\"Point\" value=\"17 15\"/>\n        <Property key=\"Size\" value=\"32 32\"/>\n        <Property key=\"Resource\" value=\"HResizePointerImage\"/>\n        <Property key=\"Rotation\" value=\"-45\"/>\n    </Resource>\n    <Resource type=\"ResourceImageSetPointer\" name=\"dresize2\">\n        <Property key=\"Point\" value=\"15 15\"/>\n        <Property key=\"Size\" value=\"32 32\"/>\n        <Property key=\"Resource\" value=\"HResizePointerImage\"/>\n        <Property key=\"Rotation\" value=\"45\"/>\n    </Resource>\n    <Resource type=\"ResourceImageSetPointer\" name=\"drop_ground\">\n        <Property key=\"Point\" value=\"0 24\"/>\n        <Property key=\"Size\" value=\"32 32\"/>\n        <Property key=\"Resource\" value=\"DropGroundPointerImage\"/>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_progress.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <!-- Progress bar track, various colors -->\n    <Resource type=\"ResourceSkin\" name=\"MW_Track_Red\" size=\"2 14\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"#{fontcolour=health}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 16\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_Track_Blue\" size=\"2 14\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"#{fontcolour=magic}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 16\">\n            <State name=\"normal\" offset=\"0 0 16 16\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_Track_Green\" size=\"2 14\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"#{fontcolour=fatigue}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 16\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_Progress_Drowning_Full_Small\" size=\"16 2\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"0.235 0.745 0.745\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 2\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 2\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_Progress_Drowning_Small\" size=\"16 2\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"1 0 0\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 2\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 2\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"MW_Progress_Loading_Small\" size=\"16 2\" texture=\"textures\\menu_bar_gray.dds\" >\n        <Property key=\"Colour\" value=\"0 0.815 0.820\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 16 2\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 16 2\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"ProgressText\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"VCenter HCenter\"/>\n        <Property key=\"TextColour\" value=\"#{fontcolour=normal}\"/>\n        <Property key=\"TextShadow\" value=\"true\"/>\n\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\"/>\n    </Resource>\n\n    <!-- Main energy bar widget definitions. There's one for each color.-->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Progress_Red\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_Track_Red\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"TransparentBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Progress_Green\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_Track_Green\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"TransparentBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Progress_Blue\" size=\"64 12\">\n        <Property key=\"TrackSkin\" value=\"MW_Track_Blue\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 12\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"TransparentBG\" offset=\"2 2 60 8\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Progress_Drowning_Full\" size=\"64 6\">\n        <Property key=\"TrackSkin\" value=\"MW_Progress_Drowning_Full_Small\"/>\n        <Property key=\"TrackFill\" value=\"1\"/>\n        <Child type=\"Widget\" skin=\"TransparentBG\" offset=\"2 2 60 2\" align=\"Stretch\" name=\"Client\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ProgressScroll_Loading\" size=\"64 6\">\n        <Property key=\"TrackFill\" value=\"1\"/>\n        <Property key=\"TrackRangeMargins\" value=\"0 0\"/>\n        <Property key=\"MinTrackSize\" value=\"1\"/>\n        <Property key=\"VerticalAlignment\" value=\"false\"/>\n        <Property key=\"MoveToClick\" value=\"false\"/>\n\n        <Child type=\"Widget\" skin=\"TransparentBG\" offset=\"2 2 60 2\" align=\"Stretch\" name=\"Client\"/>\n        <Child type=\"Button\" skin=\"MW_Progress_Loading_Small\" offset=\"0 0 1 6\" align=\"Left VStretch\" name=\"Track\"/>\n\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 64 6\" align=\"Stretch\"/>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_quickkeys_menu.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" position=\"0 0 370 230\" layer=\"Windows\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 8 354 18\">\n            <Property key=\"Caption\" value=\"#{sQuickMenuTitle}\"/>\n            <Property key=\"TextAlign\" value=\"Center\"/>\n        </Widget>\n\n        <Widget type=\"EditBox\" skin=\"SandText\" position=\"8 26 354 18\" name=\"InstructionLabel\" align=\"Left Top VStretch\">\n            <Property key=\"Caption\" value=\"#{sQuickMenuInstruc}\"/>\n            <Property key=\"MultiLine\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"Static\" value=\"true\"/>\n            <Property key=\"TextAlign\" value=\"Center\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"15 55 332 128\" align=\"Left Bottom HCenter\">\n\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"0 0 60 60\" name=\"QuickKey1\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"68 0 60 60\" name=\"QuickKey2\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"136 0 60 60\" name=\"QuickKey3\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"204 0 60 60\" name=\"QuickKey4\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"272 0 60 60\" name=\"QuickKey5\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"0 67 60 60\" name=\"QuickKey6\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"68 67 60 60\" name=\"QuickKey7\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"136 67 60 60\" name=\"QuickKey8\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"204 67 60 60\" name=\"QuickKey9\"/>\n            <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"272 67 60 60\" name=\"QuickKey10\"/>\n\n        </Widget>\n\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OKButton\" position=\"315 190 32 24\" align=\"Right Bottom\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sOK}\"/>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_quickkeys_menu_assign.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_DialogNoTransp\" position=\"0 0 16 154\" layer=\"Windows\" name=\"_Main\">\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 8 0 18\" name=\"Label\">\n            <Property key=\"Caption\" value=\"#{sQuickMenu1}\"/>\n            <Property key=\"TextAlign\" value=\"Center\"/>\n        </Widget>\n\n        <Widget type=\"Button\" skin=\"MW_Button\" position=\"8 28 0 24\" name=\"ItemButton\">\n            <Property key=\"Caption\" value=\"#{sQuickMenu2}\"/>\n        </Widget>\n\n        <Widget type=\"Button\" skin=\"MW_Button\" position=\"8 56 0 24\" name=\"MagicButton\">\n            <Property key=\"Caption\" value=\"#{sQuickMenu3}\"/>\n        </Widget>\n\n        <Widget type=\"Button\" skin=\"MW_Button\" position=\"8 84 0 24\" name=\"UnassignButton\">\n            <Property key=\"Caption\" value=\"#{sQuickMenu4}\"/>\n        </Widget>\n\n\n        <Widget type=\"Button\" skin=\"MW_Button\" position=\"8 112 0 24\" name=\"CancelButton\">\n            <Property key=\"Caption\" value=\"#{sCancel}\"/>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_recharge_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"VBox\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 330 385\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Padding\" value=\"8\"/>\n        <Property key=\"Spacing\" value=\"8\"/>\n\n        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"Label\">\n            <Property key=\"Caption\" value=\"#{sRechargeEnchantment}\"/>\n        </Widget>\n\n        <Widget type=\"ItemChargeView\" skin=\"MW_ItemChargeView\" align=\"Left Stretch\" name=\"Box\">\n            <UserString key=\"VStretch\" value=\"true\"/>\n            <UserString key=\"HStretch\" value=\"true\"/>\n        </Widget>\n\n        <Widget type=\"HBox\">\n            <UserString key=\"HStretch\" value=\"true\"/>\n                <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"0 0 44 44\" name=\"GemIcon\">\n                </Widget>\n\n                <Widget type=\"HBox\" name=\"GemBox\">\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"ChargeLabel\">\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                        <Property key=\"Caption\" value=\"#{sCharges}\"/>\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                    </Widget>\n                </Widget>\n\n               <Widget type=\"Spacer\"/>\n\n               <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                   <Property key=\"Caption\" value=\"#{sCancel}\"/>\n               </Widget>\n        </Widget>\n    </Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_repair.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"VBox\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 330 385\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Padding\" value=\"8\"/>\n        <Property key=\"Spacing\" value=\"8\"/>\n\n        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"Label\">\n            <Property key=\"Caption\" value=\"#{sRepairServiceTitle}\"/>\n        </Widget>      \n\n        <Widget type=\"ItemChargeView\" skin=\"MW_ItemChargeView\" align=\"Left Stretch\" name=\"RepairBox\">\n            <UserString key=\"VStretch\" value=\"true\"/>\n            <UserString key=\"HStretch\" value=\"true\"/>\n        </Widget>\n\n        <Widget type=\"HBox\">\n            <UserString key=\"HStretch\" value=\"true\"/>\n                <Widget type=\"ItemWidget\" skin=\"MW_ItemIconBox\" position=\"0 0 44 44\" name=\"ToolIcon\">\n                </Widget>\n\n                <Widget type=\"HBox\" name=\"ToolBox\">\n                    <Widget type=\"VBox\">\n                        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"UsesLabel\">\n                            <UserString key=\"HStretch\" value=\"true\"/>\n                            <Property key=\"Caption\" value=\"#{sUses}\"/>\n                            <Property key=\"TextAlign\" value=\"Left\"/>\n                        </Widget>\n                        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"QualityLabel\">\n                            <UserString key=\"HStretch\" value=\"true\"/>\n                            <Property key=\"Caption\" value=\"#{sQuality}\"/>\n                            <Property key=\"TextAlign\" value=\"Left\"/>\n                        </Widget>\n                    </Widget>\n                </Widget>\n                \n               <Widget type=\"Spacer\"/>\n\n               <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                   <Property key=\"Caption\" value=\"#{sCancel}\"/>\n               </Widget>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_resources.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\">\n\n    <!-- Cursors -->\n    <Resource type=\"ResourceImageSet\" name=\"ArrowPointerImage\">\n        <Group name=\"Pointer\" texture=\"textures\\tx_cursor.dds\" size=\"32 32\">\n            <Index name=\"Pointer\" >\n                <Frame point=\"0 0\"/>\n            </Index>\n        </Group>\n    </Resource>\n    <Resource type=\"ResourceImageSet\" name=\"HResizePointerImage\">\n        <Group name=\"Pointer\" texture=\"textures\\tx_cursormove.dds\" size=\"32 32\">\n            <Index name=\"Pointer\" >\n                <Frame point=\"0 0\"/>\n            </Index>\n        </Group>\n    </Resource>\n    <Resource type=\"ResourceImageSet\" name=\"DropGroundPointerImage\">\n        <Group name=\"Pointer\" texture=\"textures\\cursor_drop_ground.dds\" size=\"32 32\">\n            <Index name=\"Pointer\" >\n                <Frame point=\"0 0\"/>\n            </Index>\n        </Group>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MarkerButton\" size=\"8 8\" texture=\"textures\\door_icon.dds\">\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 8 8\" align=\"Stretch\">\n            <State name=\"normal\" offset=\"0 0 8 8\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"CustomMarkerButton\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 16 16\" name=\"Root\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"0 0 16 16\" align=\"Stretch\">\n                <Property key=\"ImageTexture\" value=\"icons\\map_marker_red.dds\"/>\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"TabControl\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"5 5 89 60\" name=\"Root\">\n            <UserString key=\"ButtonSkin\" value=\"MW_Button_RightPadding\"/>\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"0 28 89 32\" align=\"Left Top Stretch\">\n                <Widget type=\"Widget\" skin=\"\" position=\"4 4 81 28\" align=\"Left Top Stretch\" name=\"TabItem\"/>\n            </Widget>\n            <Widget type=\"Widget\" skin=\"\" position=\"0 0 89 23\" align=\"HStretch Top\" name=\"HeaderPlace\">\n                <Widget type=\"Widget\" skin=\"\" position=\"52 0 37 23\" name=\"Controls\">\n                </Widget>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"TabControlInner\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 5 89 60\" name=\"Root\">\n            <UserString key=\"ButtonSkin\" value=\"MW_Button_RightPadding\"/>\n            <Widget type=\"Widget\" skin=\"\" position=\"0 28 89 32\" align=\"Left Top Stretch\" name=\"TabItem\"/>\n\n            <Widget type=\"Widget\" skin=\"\" position=\"0 0 89 23\" align=\"HStretch Top\" name=\"HeaderPlace\">\n                <Widget type=\"Widget\" skin=\"\" position=\"52 0 37 23\" name=\"Controls\">\n                </Widget>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"MW_StatNameValue\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 200 18\" name=\"Root\">\n            <Property key=\"NeedMouse\" value=\"true\"/>\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 168 18\" align=\"Left HStretch\" name=\"StatName\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n                <Property key=\"TextAlign\" value=\"Left\"/>\n            </Widget>\n            <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"168 0 32 18\" align=\"Right Top\" name=\"StatValue\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n                <Property key=\"TextAlign\" value=\"Right\"/>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"MW_StatName\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 200 18\" name=\"Root\">\n            <Property key=\"NeedMouse\" value=\"true\"/>\n            <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 200 18\" align=\"Left HStretch\" name=\"StatName\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"MW_StatNameButton\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 200 18\" name=\"Root\">\n            <Widget type=\"Button\" skin=\"SandTextButton\" position=\"0 0 200 18\" align=\"Left HStretch\" name=\"StatNameButton\">\n                <Property key=\"NeedMouse\" value=\"true\"/>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"MW_ComboBox\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"65 10 100 26\" name=\"Root\">\n            <Property key=\"MaxListLength\" value=\"200\"/>\n            <Property key=\"SmoothShow\" value=\"true\"/>\n            <Property key=\"ModeDrop\" value=\"true\"/>\n\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"0 0 100 26\" align=\"Stretch\">\n                <Widget type=\"TextBox\" skin=\"SandText\" position=\"6 3 71 20\" align=\"Stretch\" name=\"Client\">\n                    <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n                </Widget>\n                <Widget type=\"ListBox\" skin=\"MW_PopupListNoTransp\" position=\"65 38 100 200\" style=\"Popup\" layer=\"Popup\" name=\"List\">\n                    <Property key=\"Visible\" value=\"false\"/>\n                </Widget>\n                <Widget type=\"Button\" skin=\"MW_ArrowDown\" position=\"79 7 14 11\" align=\"Right VCenter\" name=\"Button\"/>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"MW_ItemIcon\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 42 42\" name=\"Root\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"0 0 42 42\" align=\"Stretch\" name=\"Frame\">\n                <Widget type=\"TextBox\" skin=\"CountText\" position=\"5 19 32 18\" align=\"Right Bottom\" name=\"Text\">\n                    <Property key=\"TextAlign\" value=\"Right\"/>\n                    <Property key=\"TextShadow\" value=\"true\"/>\n                </Widget>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"5 5 32 32\" align=\"Stretch\" name=\"Item\"/>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"9 9 32 32\" align=\"Stretch\" name=\"ItemShadow\">\n                    <Property key=\"Colour\" value=\"0 0 0\"/>\n                    <Property key=\"Alpha\" value=\"0.5\"/>\n                </Widget>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"MW_ItemIconNoShadow\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 42 42\" name=\"Root\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"0 0 42 42\" align=\"Stretch\" name=\"Frame\">\n                <Widget type=\"TextBox\" skin=\"CountText\" position=\"5 19 32 18\" align=\"Right Bottom\" name=\"Text\">\n                    <Property key=\"TextAlign\" value=\"Right\"/>\n                    <Property key=\"TextShadow\" value=\"true\"/>\n                </Widget>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"5 5 32 32\" align=\"Stretch\" name=\"Item\"/>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"MW_ItemIconSmall\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 32 32\" name=\"Root\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"0 0 32 32\" align=\"Stretch\" name=\"Frame\">\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"0 0 32 32\" align=\"Stretch\" name=\"Item\"/>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"4 4 32 32\" align=\"Stretch\" name=\"ItemShadow\">\n                    <Property key=\"Colour\" value=\"0 0 0\"/>\n                    <Property key=\"Alpha\" value=\"0.5\"/>\n                </Widget>\n            </Widget>\n        </Widget>\n    </Resource>\n\n    <Resource type=\"ResourceLayout\" name=\"MW_ItemIconBox\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 50 50\" name=\"Root\">\n            <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"4 4 42 42\" align=\"Center\" name=\"Frame\">\n                <Widget type=\"TextBox\" skin=\"CountText\" position=\"5 19 32 18\" align=\"Right Bottom\" name=\"Text\">\n                    <Property key=\"TextAlign\" value=\"Right\"/>\n                    <Property key=\"TextShadow\" value=\"true\"/>\n                </Widget>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"5 5 32 32\" align=\"Center\" name=\"Item\"/>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"9 9 32 32\" align=\"Stretch\" name=\"ItemShadow\">\n                    <Property key=\"Colour\" value=\"0 0 0\"/>\n                    <Property key=\"Alpha\" value=\"0.5\"/>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"Widget\" skin=\"MW_Box_Overlay\" position=\"0 0 50 50\" align=\"Stretch\"/>\n        </Widget>\n    </Resource>\n\n    <!-- Same as MW_Caption, but reserves some free space on the right for the Pin button -\n       i.e. not allowing the caption label to stretch there, but still showing the tiling background. -->\n    <Resource type=\"ResourceLayout\" name=\"MW_Caption_Pin\" version=\"3.2.0\">\n        <Widget type=\"Widget\" skin=\"\" position=\"0 0 88 20\" name=\"Root\">\n            <Widget type=\"Widget\" skin=\"HB_ALL\" position=\"0 0 30 20\" align=\"Default\" name=\"Left\"/>\n            <Widget type=\"Widget\" skin=\"\" position=\"0 0 69 20\" align=\"Stretch\">\n                <Widget type=\"TextBox\" skin=\"SandText\" position=\"30 0 28 20\" align=\"Left VStretch\" name=\"Client\">\n                    <Property key=\"FontName\" value=\"Default\"/>\n                    <Property key=\"TextAlign\" value=\"Center\"/>\n                </Widget>\n            </Widget>\n            <Widget type=\"Widget\" skin=\"HB_ALL\" position=\"0 0 30 20\" align=\"Right\" name=\"Right\"/>\n        </Widget>\n    </Resource>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_savegame_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"VBox\" skin=\"MW_DialogNoTransp\" layer=\"Windows\" position=\"0 0 600 400\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Padding\" value=\"8\"/>\n        <Property key=\"Spacing\" value=\"8\"/>\n\n        <Widget type=\"HBox\" skin=\"\">\n            <UserString key=\"HStretch\" value=\"true\"/>\n            <UserString key=\"VStretch\" value=\"true\"/>\n            <Property key=\"Spacing\" value=\"8\"/>\n\n            <Widget type=\"VBox\" skin=\"\">\n\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <UserString key=\"VStretch\" value=\"true\"/>\n                <Property key=\"Spacing\" value=\"8\"/>\n\n                <Widget type=\"ComboBox\" skin=\"MW_ComboBox\" position=\"0 0 200 24\" name=\"SelectCharacter\">\n                    <Property key=\"Caption\" value=\"Select Character\"/>\n                    <UserString key=\"HStretch\" value=\"true\"/>\n                </Widget>\n\n\n                <Widget type=\"ListBox\" skin=\"MW_List\" position=\"0 0 200 200\" name=\"SaveList\">\n                    <UserString key=\"HStretch\" value=\"true\"/>\n                    <UserString key=\"VStretch\" value=\"true\"/>\n                </Widget>\n\n            </Widget>\n\n            <Widget type=\"VBox\" skin=\"\">\n                <UserString key=\"HStretch\" value=\"false\"/>\n                <UserString key=\"VStretch\" value=\"true\"/>\n                <Property key=\"Spacing\" value=\"8\"/>\n\n                <Widget type=\"Widget\" skin=\"\" position=\"0 0 200 24\" name=\"Spacer\">\n                    <Property key=\"Visible\" value=\"false\"/>\n                </Widget>\n\n                <Widget type=\"Widget\" skin=\"MW_Box\" position=\"0 0 263 137\">\n                    <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"2 2 259 133\" name=\"Screenshot\"/>\n                </Widget>\n\n                <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"0 0 263 0\" name=\"InfoText\">\n                    <Property key=\"Static\" value=\"true\"/>\n                    <Property key=\"MultiLine\" value=\"true\"/>\n                    <Property key=\"WordWrap\" value=\"true\"/>\n                    <Property key=\"NeedKey\" value=\"false\"/>\n                </Widget>\n\n\n            </Widget>\n\n        </Widget>\n\n        <!--\n            Start of tes3mp addition\n\n            Note that saves are singleplayer-only\n        -->\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"58 58 500 18\" align=\"Left Top\" name=\"SaveWarning\">\n            <Property key=\"Caption\" value=\"Note: You will create an OpenMW singleplayer-only save.\"/>\n        </Widget>\n        <!--\n            End of tes3mp addition\n        -->\n\n        <Widget type=\"HBox\" skin=\"\">\n            <Property key=\"Spacing\" value=\"4\"/>\n\n            <UserString key=\"HStretch\" value=\"true\"/>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"DeleteButton\">\n                <Property key=\"Caption\" value=\"#{sDeleteGame}\"/>\n            </Widget>\n\n            <Widget type=\"EditBox\" skin=\"MW_TextEdit\" name=\"SaveNameEdit\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <UserString key=\"VStretch\" value=\"true\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OkButton\">\n                <Property key=\"Caption\" value=\"#{sOk}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                <Property key=\"Caption\" value=\"#{sCancel}\"/>\n            </Widget>\n        </Widget>\n\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_screen_fader.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"ImageBox\" skin=\"ImageBox\" layer=\"Overlay\" position=\"0 0 300 200\" name=\"_Main\" align=\"Stretch\">\n        <Property key=\"ImageTexture\" value=\"black\"/>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_screen_fader_hit.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"ImageBox\" skin=\"ImageBox\" layer=\"AdditiveOverlay\" position=\"0 0 300 200\" name=\"_Main\" align=\"Stretch\">\n        <Property key=\"ImageTexture\" value=\"black\"/>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_scroll.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n\n<Widget type=\"Window\" skin=\"\" layer=\"JournalBooks\" position=\"0 0 512 372\" name=\"_Main\">\n\n    <Widget type=\"ImageBox\" skin=\"ImageBox\" position_real=\"0 0 1 1\" name=\"ScrollImage\">     \n        <Property key=\"ImageTexture\" value=\"textures\\scroll.dds\"/>\n\n        <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"12 14 128 32\" name=\"TakeButton\">\n            <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_take_over.dds\"/>\n            <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_take_idle.dds\"/>\n            <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_take_pressed.dds\"/>\n        </Widget>\n\n        <Widget type=\"ImageButton\" skin=\"ImageBox\" position=\"415 20 128 32\" name=\"CloseButton\">\n            <Property key=\"ImageHighlighted\" value=\"textures\\tx_menubook_close_over.dds\"/>\n            <Property key=\"ImageNormal\" value=\"textures\\tx_menubook_close_idle.dds\"/>\n            <Property key=\"ImagePushed\" value=\"textures\\tx_menubook_close_pressed.dds\"/>\n        </Widget>\n\n        <Widget type=\"ScrollView\" skin=\"MW_ScrollView\" position=\"60 84 410 235\" name=\"TextView\">\n            <Property key=\"CanvasSize\" value=\"410 900\"/>\n        </Widget>\n\n    </Widget>\n</Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_scroll.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ScrollView\" size=\"516 516\">\n        <Property key=\"NeedKey\" value=\"false\"/>\n        <Child type=\"Widget\" skin=\"\" offset=\"0 0 502 516\" align=\"Stretch\" name=\"Client\"/>\n        <Child type=\"ScrollBar\" skin=\"MW_VScroll\" offset=\"498 3 14 513\" align=\"Right Top VStretch\" name=\"VScroll\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ScrollViewH\" size=\"516 516\">\n        <Property key=\"NeedKey\" value=\"false\"/>\n        <Child type=\"Widget\" skin=\"\" offset=\"0 0 516 502\" align=\"Stretch\" name=\"Client\"/>\n        <Child type=\"ScrollBar\" skin=\"MW_HScroll\" offset=\"3 498 513 14\" align=\"Left Bottom HStretch\" name=\"HScroll\"/>\n    </Resource>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI>\n    <MyGUI type=\"Font\">\n        <Property key=\"Default\" value=\"Magic Cards\"/>\n\n    </MyGUI>\n    <MyGUI type=\"Pointer\">\n        <Property key=\"Default\" value=\"arrow\"/>\n        <Property key=\"Layer\" value=\"Pointer\"/>\n    </MyGUI>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_settings_window.layout",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\" version=\"3.2.0\">\n    <Widget type=\"Window\" skin=\"MW_Window\" position=\"0 0 400 485\" layer=\"Windows\" name=\"_Main\">\n        <Property key=\"MinSize\" value=\"430 446\"/>\n        <Widget type=\"TabControl\" skin=\"TabControl\" position=\"8 8 368 405\" align=\"Stretch\" name=\"SettingsTab\">\n            <Property key=\"ButtonAutoWidth\" value=\"true\"/>\n            <Widget type=\"TabItem\" skin=\"\" position=\"4 32 360 358\">\n                <Property key=\"Caption\" value=\"  #{sPrefs}  \"/>\n                <Widget type=\"Widget\" skin=\"\" position=\"4 4 352 54\" align=\"Left Top HStretch\">\n                    <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 0 352 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sTransparency_Menu}\"/>\n                    </Widget>\n                    <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 20 352 14\" align=\"Left Top HStretch\">\n                        <Property key=\"Range\" value=\"10000\"/>\n                        <Property key=\"Page\" value=\"300\"/>\n                        <UserString key=\"SettingType\" value=\"Slider\"/>\n                        <UserString key=\"SettingCategory\" value=\"GUI\"/>\n                        <UserString key=\"SettingName\" value=\"menu transparency\"/>\n                        <UserString key=\"SettingValueType\" value=\"Float\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 38 352 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sFull}\"/>\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 38 352 16\" align=\"Right Top\">\n                        <Property key=\"Caption\" value=\"#{sNone}\"/>\n                        <Property key=\"TextAlign\" value=\"Right\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"Widget\" skin=\"\" position=\"4 64 352 54\" align=\"Left Top HStretch\">\n                    <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 0 352 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sMenu_Help_Delay}\"/>\n                    </Widget>\n                    <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 20 352 14\" align=\"Left Top HStretch\">\n                        <Property key=\"Range\" value=\"10000\"/>\n                        <Property key=\"Page\" value=\"300\"/>\n                        <UserString key=\"SettingType\" value=\"Slider\"/>\n                        <UserString key=\"SettingCategory\" value=\"GUI\"/>\n                        <UserString key=\"SettingName\" value=\"tooltip delay\"/>\n                        <UserString key=\"SettingValueType\" value=\"Float\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 38 352 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sFast}\"/>\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 38 352 16\" align=\"Right Top\">\n                        <Property key=\"Caption\" value=\"#{sSlow}\"/>\n                        <Property key=\"TextAlign\" value=\"Right\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"Widget\" skin=\"\" position=\"4 124 352 54\" align=\"Left Top HStretch\">\n                    <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 0 352 16\" align=\"Left Top\" name=\"DifficultyText\">\n                        <Property key=\"Caption\" value=\"#{sDifficulty}\"/>\n                    </Widget>\n                    <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 20 352 14\" align=\"Left Top HStretch\">\n                        <Property key=\"Range\" value=\"201\"/>\n                        <Property key=\"Page\" value=\"1\"/>\n                        <UserString key=\"SettingType\" value=\"Slider\"/>\n                        <UserString key=\"SettingCategory\" value=\"Game\"/>\n                        <UserString key=\"SettingName\" value=\"difficulty\"/>\n                        <UserString key=\"SettingValueType\" value=\"Integer\"/>\n                        <UserString key=\"SettingMin\" value=\"-100\"/>\n                        <UserString key=\"SettingMax\" value=\"100\"/>\n                        <UserString key=\"SettingLabelWidget\" value=\"DifficultyText\"/>\n                        <UserString key=\"SettingLabelCaption\" value=\"#{sDifficulty} (%s)\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 38 352 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sEasy}\"/>\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 38 352 16\" align=\"Right Top\">\n                        <Property key=\"Caption\" value=\"#{sHard}\"/>\n                        <Property key=\"TextAlign\" value=\"Right\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"Widget\" skin=\"\" position=\"4 184 352 54\" align=\"Left Top HStretch\">\n                    <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 0 352 16\" align=\"Left Top\" name=\"ActorProcessingText\">\n                        <Property key=\"Caption\" value=\"Actors Processing Range\"/>\n                    </Widget>\n                    <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 20 352 14\" align=\"Left Top HStretch\">\n                        <Property key=\"Range\" value=\"3584\"/>\n                        <Property key=\"Page\" value=\"300\"/>\n                        <UserString key=\"SettingType\" value=\"Slider\"/>\n                        <UserString key=\"SettingCategory\" value=\"Game\"/>\n                        <UserString key=\"SettingName\" value=\"actors processing range\"/>\n                        <UserString key=\"SettingValueType\" value=\"Integer\"/>\n                        <UserString key=\"SettingMin\" value=\"3584\"/>\n                        <UserString key=\"SettingMax\" value=\"7168\"/>\n                        <UserString key=\"SettingLabelWidget\" value=\"ActorProcessingText\"/>\n                        <UserString key=\"SettingLabelCaption\" value=\"Actors Processing Range (%s)\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 38 352 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sLow}\"/>\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 38 352 16\" align=\"Right Top\">\n                        <Property key=\"Caption\" value=\"#{sHigh}\"/>\n                        <Property key=\"TextAlign\" value=\"Right\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"HBox\" skin=\"\" position=\"4 250 260 24\">\n                    <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\">\n                        <UserString key=\"SettingCategory\" value=\"Saves\"/>\n                        <UserString key=\"SettingName\" value=\"autosave\"/>\n                        <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                    </Widget>\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 95 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sQuick_Save}\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"HBox\" skin=\"\" position=\"4 280 260 24\">\n                    <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\">\n                        <UserString key=\"SettingCategory\" value=\"Game\"/>\n                        <UserString key=\"SettingName\" value=\"best attack\"/>\n                        <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                    </Widget>\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 89 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sBestAttack}\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"HBox\" skin=\"\" position=\"4 310 260 24\">\n                    <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\">\n                        <UserString key=\"SettingCategory\" value=\"GUI\"/>\n                        <UserString key=\"SettingName\" value=\"subtitles\"/>\n                        <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                    </Widget>\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 71 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sSubtitles}\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"HBox\" skin=\"\" position=\"4 340 260 24\">\n                    <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\">\n                        <UserString key=\"SettingCategory\" value=\"HUD\"/>\n                        <UserString key=\"SettingName\" value=\"crosshair\"/>\n                        <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                    </Widget>\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 80 16\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sCursorOff}\"/>\n                    </Widget>\n                </Widget>\n            </Widget>\n            <Widget type=\"TabItem\" skin=\"\" position=\"4 32 360 308\">\n                <Property key=\"Caption\" value=\"  #{sAudio}  \"/>\n                <Widget type=\"TextBox\" skin=\"NormalText\" position=\"4 4 352 18\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"#{sMaster}\"/>\n                </Widget>\n                <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"4 28 352 18\" align=\"HStretch Top\">\n                    <Property key=\"Range\" value=\"10000\"/>\n                    <Property key=\"Page\" value=\"300\"/>\n                    <UserString key=\"SettingType\" value=\"Slider\"/>\n                    <UserString key=\"SettingCategory\" value=\"Sound\"/>\n                    <UserString key=\"SettingName\" value=\"master volume\"/>\n                    <UserString key=\"SettingValueType\" value=\"Float\"/>\n                </Widget>\n                <Widget type=\"TextBox\" skin=\"NormalText\" position=\"4 54 352 18\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"#{sVoice}\"/>\n                </Widget>\n                <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"4 78 352 18\" align=\"HStretch Top\">\n                    <Property key=\"Range\" value=\"10000\"/>\n                    <Property key=\"Page\" value=\"300\"/>\n                    <UserString key=\"SettingType\" value=\"Slider\"/>\n                    <UserString key=\"SettingCategory\" value=\"Sound\"/>\n                    <UserString key=\"SettingName\" value=\"voice volume\"/>\n                    <UserString key=\"SettingValueType\" value=\"Float\"/>\n                </Widget>\n                <Widget type=\"TextBox\" skin=\"NormalText\" position=\"4 104 352 18\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"#{sEffects}\"/>\n                </Widget>\n                <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"4 128 352 18\" align=\"HStretch Top\">\n                    <Property key=\"Range\" value=\"10000\"/>\n                    <Property key=\"Page\" value=\"300\"/>\n                    <UserString key=\"SettingType\" value=\"Slider\"/>\n                    <UserString key=\"SettingCategory\" value=\"Sound\"/>\n                    <UserString key=\"SettingName\" value=\"sfx volume\"/>\n                    <UserString key=\"SettingValueType\" value=\"Float\"/>\n                </Widget>\n                <Widget type=\"TextBox\" skin=\"NormalText\" position=\"4 154 352 18\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"#{sFootsteps}\"/>\n                </Widget>\n                <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"4 178 352 18\" align=\"HStretch Top\">\n                    <Property key=\"Range\" value=\"10000\"/>\n                    <Property key=\"Page\" value=\"300\"/>\n                    <UserString key=\"SettingType\" value=\"Slider\"/>\n                    <UserString key=\"SettingCategory\" value=\"Sound\"/>\n                    <UserString key=\"SettingName\" value=\"footsteps volume\"/>\n                    <UserString key=\"SettingValueType\" value=\"Float\"/>\n                </Widget>\n                <Widget type=\"TextBox\" skin=\"NormalText\" position=\"4 204 352 18\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"#{sMusic}\"/>\n                </Widget>\n                <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"4 228 352 18\" align=\"HStretch Top\">\n                    <Property key=\"Range\" value=\"10000\"/>\n                    <Property key=\"Page\" value=\"300\"/>\n                    <UserString key=\"SettingType\" value=\"Slider\"/>\n                    <UserString key=\"SettingCategory\" value=\"Sound\"/>\n                    <UserString key=\"SettingName\" value=\"music volume\"/>\n                    <UserString key=\"SettingValueType\" value=\"Float\"/>\n                </Widget>\n            </Widget>\n            <Widget type=\"TabItem\" skin=\"\" position=\"4 32 360 308\">\n                <Property key=\"Caption\" value=\"  #{sControls}  \"/>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"4 4 137 24\" align=\"Left Top\" name=\"KeyboardButton\">\n                    <Property key=\"Caption\" value=\"Mouse/Keyboard\"/>\n                </Widget>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"145 4 137 24\" align=\"Left Top\" name=\"ControllerButton\">\n                    <Property key=\"Caption\" value=\"Controller\"/>\n                </Widget>\n                <Widget type=\"Widget\" skin=\"MW_Box\" position=\"4 34 352 154\" align=\"Stretch\">\n                    <Widget type=\"ScrollView\" skin=\"MW_ScrollView\" position=\"4 4 344 146\" align=\"Stretch\" name=\"ControlsBox\">\n                        <Property key=\"CanvasAlign\" value=\"Left Top\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"4 194 137 24\" align=\"Left Bottom\" name=\"ResetControlsButton\">\n                    <Property key=\"Caption\" value=\"#{sControlsMenu1}\"/>\n                </Widget>\n                <Widget type=\"HBox\" skin=\"\" position=\"4 224 380 24\" align=\"Left Bottom\">\n                    <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Bottom\">\n                        <UserString key=\"SettingCategory\" value=\"Input\"/>\n                        <UserString key=\"SettingName\" value=\"invert x axis\"/>\n                        <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                    </Widget>\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 78 16\" align=\"Left Bottom\">\n                        <Property key=\"Caption\" value=\"Invert X Axis\"/>\n                    </Widget>\n                    <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"28 0 24 24\" align=\"Left Bottom\">\n                        <UserString key=\"SettingCategory\" value=\"Input\"/>\n                        <UserString key=\"SettingName\" value=\"invert y axis\"/>\n                        <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                    </Widget>\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"56 4 78 16\" align=\"Left Bottom\">\n                        <Property key=\"Caption\" value=\"#{sMouseFlip}\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"TextBox\" skin=\"NormalText\" position=\"4 254 352 18\" align=\"Left Bottom\">\n                    <Property key=\"Caption\" value=\"Camera Sensitivity\"/>\n                </Widget>\n                <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"4 278 352 18\" align=\"HStretch Bottom\">\n                    <Property key=\"Range\" value=\"10000\"/>\n                    <Property key=\"Page\" value=\"300\"/>\n                    <UserString key=\"SettingType\" value=\"Slider\"/>\n                    <UserString key=\"SettingCategory\" value=\"Input\"/>\n                    <UserString key=\"SettingName\" value=\"camera sensitivity\"/>\n                    <UserString key=\"SettingValueType\" value=\"Float\"/>\n                    <UserString key=\"SettingMin\" value=\"0.2\"/>\n                    <UserString key=\"SettingMax\" value=\"5.0\"/>\n                </Widget>\n                <Widget type=\"TextBox\" skin=\"SandText\" position=\"4 302 352 18\" align=\"Left Bottom\">\n                    <Property key=\"Caption\" value=\"#{sLow}\"/>\n                    <Property key=\"TextAlign\" value=\"Left\"/>\n                </Widget>\n                <Widget type=\"TextBox\" skin=\"SandText\" position=\"4 302 352 18\" align=\"Right Bottom\">\n                    <Property key=\"Caption\" value=\"#{sHigh}\"/>\n                    <Property key=\"TextAlign\" value=\"Right\"/>\n                </Widget>\n                <Widget type=\"HBox\" skin=\"\" position=\"4 324 352 24\" align=\"Left Bottom\">\n                    <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Bottom\">\n                        <UserString key=\"SettingCategory\" value=\"Input\"/>\n                        <UserString key=\"SettingName\" value=\"enable controller\"/>\n                        <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                    </Widget>\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 78 16\" align=\"Left Bottom\">\n                        <Property key=\"Caption\" value=\"#{sEnableJoystick}\"/>\n                    </Widget>\n                </Widget>\n            </Widget>\n            <Widget type=\"TabItem\" skin=\"\" position=\"4 32 360 308\">\n                <Property key=\"Caption\" value=\"  #{sVideo}  \"/>\n                <Widget type=\"TabControl\" skin=\"TabControlInner\" position=\"4 4 352 390\" align=\"Stretch\">\n                    <Property key=\"ButtonAutoWidth\" value=\"true\"/>\n                    <Widget type=\"TabItem\" skin=\"\" position=\"0 28 352 268\" align=\"Stretch\">\n                        <Property key=\"Caption\" value=\"  Video  \"/>\n                        <Widget type=\"ListBox\" skin=\"MW_List\" position=\"0 4 170 170\" align=\"Left Top\" name=\"ResolutionList\"/>\n                        <Widget type=\"HBox\" skin=\"\" position=\"182 4 300 24\">\n                            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"FullscreenButton\">\n                                <UserString key=\"SettingCategory\" value=\"Video\"/>\n                                <UserString key=\"SettingName\" value=\"fullscreen\"/>\n                                <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                            </Widget>\n                            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 70 16\" align=\"Left Top\">\n                                <Property key=\"Caption\" value=\"Fullscreen\"/>\n                            </Widget>\n                        </Widget>\n                        <Widget type=\"HBox\" skin=\"\" position=\"182 34 300 24\">\n                            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"VSyncButton\">\n                                <UserString key=\"SettingCategory\" value=\"Video\"/>\n                                <UserString key=\"SettingName\" value=\"vsync\"/>\n                                <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                            </Widget>\n                            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 48 16\" align=\"Left Top\">\n                                <Property key=\"Caption\" value=\"VSync\"/>\n                            </Widget>\n                        </Widget>\n                        <Widget type=\"HBox\" skin=\"\" position=\"182 64 300 24\">\n                            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"WindowBorderButton\">\n                                <UserString key=\"SettingCategory\" value=\"Video\"/>\n                                <UserString key=\"SettingName\" value=\"window border\"/>\n                                <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                            </Widget>\n                            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 48 16\" align=\"Left Top\">\n                                <Property key=\"Caption\" value=\"Window border\"/>\n                            </Widget>\n                        </Widget>\n\n                        <Widget type=\"TextBox\" skin=\"SandText\" position=\"182 94 300 32\" align=\"Left Top\">\n                            <Property key=\"Caption\" value=\"Hint: press F3 to show \\nthe current frame rate.\"/>\n                        </Widget>\n\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 198 352 18\" align=\"Left Top\" name=\"FovText\">\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 222 352 18\" align=\"HStretch Top\">\n                            <Property key=\"Range\" value=\"81\"/>\n                            <Property key=\"Page\" value=\"1\"/>\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingCategory\" value=\"Camera\"/>\n                            <UserString key=\"SettingName\" value=\"field of view\"/>\n                            <UserString key=\"SettingValueType\" value=\"Integer\"/>\n                            <UserString key=\"SettingMin\" value=\"30\"/>\n                            <UserString key=\"SettingMax\" value=\"110\"/>\n                            <UserString key=\"SettingLabelWidget\" value=\"FovText\"/>\n                            <UserString key=\"SettingLabelCaption\" value=\"Field of View (%s)\"/>\n                        </Widget>\n                        <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 246 352 18\" align=\"Left Top\">\n                            <Property key=\"Caption\" value=\"#{sLow}\"/>\n                            <Property key=\"TextAlign\" value=\"Left\"/>\n                        </Widget>\n                        <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 246 352 18\" align=\"Right Top\">\n                            <Property key=\"Caption\" value=\"#{sHigh}\"/>\n                            <Property key=\"TextAlign\" value=\"Right\"/>\n                        </Widget>\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 268 352 18\" align=\"Left Top\" name=\"GammaText\">\n                            <Property key=\"Caption\" value=\"#{sGamma_Correction}\"/>\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 292 352 18\" align=\"HStretch Top\" name=\"GammaSlider\">\n                            <Property key=\"Range\" value=\"10000\"/>\n                            <Property key=\"Page\" value=\"300\"/>\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingCategory\" value=\"Video\"/>\n                            <UserString key=\"SettingName\" value=\"gamma\"/>\n                            <UserString key=\"SettingValueType\" value=\"Float\"/>\n                            <UserString key=\"SettingMin\" value=\"0.1\"/>\n                            <UserString key=\"SettingMax\" value=\"3.0\"/>\n                        </Widget>\n                        <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 316 352 18\" align=\"Left Top\" name=\"GammaTextDark\">\n                            <Property key=\"Caption\" value=\"#{sDark_Gamma}\"/>\n                            <Property key=\"TextAlign\" value=\"Left\"/>\n                        </Widget>\n                        <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 316 352 18\" align=\"Right Top\" name=\"GammaTextLight\">\n                            <Property key=\"Caption\" value=\"#{sLight_Gamma}\"/>\n                            <Property key=\"TextAlign\" value=\"Right\"/>\n                        </Widget>\n                    </Widget>\n                    <Widget type=\"TabItem\" skin=\"\" position=\"0 28 352 268\">\n                        <Property key=\"Caption\" value=\"  Detail  \"/>\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"4 4 300 24\" align=\"Left Top\">\n                            <Property key=\"Caption\" value=\"Texture Filtering\"/>\n                        </Widget>\n                        <Widget type=\"ComboBox\" skin=\"MW_ComboBox\" position=\"14 28 110 24\" align=\"Left Top\" name=\"TextureFilteringButton\">\n                            <Property key=\"AddItem\" value=\"Bilinear\"/>\n                            <Property key=\"AddItem\" value=\"Trilinear\"/>\n                        </Widget>\n                        <Widget type=\"Widget\" skin=\"\" position=\"184 4 300 50\" align=\"Left Top\" name=\"AnisotropyBox\">\n                            <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 0 300 24\" align=\"Left Top\" name=\"AnisotropyLabel\">\n                                <Property key=\"Caption\" value=\"Anisotropy\"/>\n                            </Widget>\n                            <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 28 150 18\" align=\"Left Top\">\n                                <Property key=\"Range\" value=\"17\"/>\n                                <Property key=\"Page\" value=\"1\"/>\n                                <UserString key=\"SettingType\" value=\"Slider\"/>\n                                <UserString key=\"SettingCategory\" value=\"General\"/>\n                                <UserString key=\"SettingName\" value=\"anisotropy\"/>\n                                <UserString key=\"SettingLabelWidget\" value=\"AnisotropyLabel\"/>\n                                <UserString key=\"SettingLabelCaption\" value=\"Anisotropy (%s)\"/>\n                            </Widget>\n                        </Widget>\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 130 352 18\" align=\"Left Top\" name=\"RenderDistanceLabel\">\n                            <Property key=\"Caption\" value=\"#{sRender_Distance}\"/>\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 154 352 18\" align=\"HStretch Top\" name=\"RenderingDistanceSlider\">\n                            <Property key=\"Range\" value=\"4669\"/>\n                            <Property key=\"Page\" value=\"128\"/>\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingCategory\" value=\"Camera\"/>\n                            <UserString key=\"SettingName\" value=\"viewing distance\"/>\n                            <UserString key=\"SettingValueType\" value=\"Integer\"/>\n                            <UserString key=\"SettingMin\" value=\"2500\"/>\n                            <UserString key=\"SettingMax\" value=\"7168\"/>\n                            <UserString key=\"SettingLabelWidget\" value=\"RenderDistanceLabel\"/>\n                            <UserString key=\"SettingLabelCaption\" value=\"#{sRender_Distance} (%s)\"/>\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 154 352 18\" align=\"HStretch Top\" name=\"LargeRenderingDistanceSlider\">\n                            <Property key=\"Range\" value=\"79421\"/>\n                            <Property key=\"Page\" value=\"2048\"/>\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingCategory\" value=\"Camera\"/>\n                            <UserString key=\"SettingName\" value=\"viewing distance\"/>\n                            <UserString key=\"SettingValueType\" value=\"Cell\"/>\n                            <UserString key=\"SettingMin\" value=\"2500\"/>\n                            <UserString key=\"SettingMax\" value=\"81920\"/>\n                            <UserString key=\"SettingLabelWidget\" value=\"RenderDistanceLabel\"/>\n                            <UserString key=\"SettingLabelCaption\" value=\"#{sRender_Distance} (x%s)\"/>\n                        </Widget>\n                        <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 178 352 18\" align=\"Left Top\">\n                            <Property key=\"Caption\" value=\"#{sNear}\"/>\n                            <Property key=\"TextAlign\" value=\"Left\"/>\n                        </Widget>\n                        <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 178 352 18\" align=\"Right Top\">\n                            <Property key=\"Caption\" value=\"#{sFar}\"/>\n                            <Property key=\"TextAlign\" value=\"Right\"/>\n                        </Widget>\n                    </Widget>\n                    <Widget type=\"TabItem\" skin=\"\" position=\"0 28 352 268\">\n                        <Property key=\"Caption\" value=\"  Water  \"/>\n                        <Widget type=\"HBox\" skin=\"\" position=\"4 4 350 24\">\n                            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\">\n                                <UserString key=\"SettingCategory\" value=\"Water\"/>\n                                <UserString key=\"SettingName\" value=\"shader\"/>\n                                <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                            </Widget>\n                            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 75 16\" align=\"Left Top\">\n                                <Property key=\"Caption\" value=\"Water shader\"/>\n                            </Widget>\n                        </Widget>\n                        <Widget type=\"Widget\" skin=\"\" position=\"24 32 300 230\">\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 0 350 24\">\n                                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\">\n                                    <UserString key=\"SettingCategory\" value=\"Water\"/>\n                                    <UserString key=\"SettingName\" value=\"refraction\"/>\n                                    <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 79 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"Refraction\"/>\n                                </Widget>\n                            </Widget>\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 28 350 24\">\n                                <Widget type=\"ComboBox\" skin=\"MW_ComboBox\" position=\"0 0 85 24\" align=\"Left Top\" name=\"WaterTextureSize\">\n                                    <Property key=\"AddItem\" value=\"Low\"/>\n                                    <Property key=\"AddItem\" value=\"Medium\"/>\n                                    <Property key=\"AddItem\" value=\"High\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"64 4 90 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"Texture quality\"/>\n                                </Widget>\n                            </Widget>\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 56 350 24\">\n                                <Widget type=\"ComboBox\" skin=\"MW_ComboBox\" position=\"0 0 115 24\" align=\"Left Top\" name=\"WaterReflectionDetail\">\n                                    <Property key=\"AddItem\" value=\"Off\"/>\n                                    <Property key=\"AddItem\" value=\"Terrain\"/>\n                                    <Property key=\"AddItem\" value=\"World\"/>\n                                    <Property key=\"AddItem\" value=\"Objects\"/>\n                                    <Property key=\"AddItem\" value=\"Actors\"/>\n                                    <Property key=\"AddItem\" value=\"Groundcover\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"64 4 90 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"Reflection shader detail\"/>\n                                </Widget>\n                            </Widget>\n                        </Widget>\n\n                    </Widget>\n\n                    <Widget type=\"TabItem\" skin=\"\" position=\"4 32 360 308\">\n                        <Property key=\"Caption\" value=\"  Lights  \"/>\n                        <!-- Lighting Method -->\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 4 170 18\" align=\"Left Top\">\n                            <Property key=\"Caption\" value=\"Lighting Method:\"/>\n                        </Widget>\n                        <Widget type=\"ComboBox\" skin=\"MW_ComboBox\" position=\"0 28 170 24\" align=\"Left Top\" name=\"LightingMethodButton\">\n                            <Property key=\"AddItem\" value=\"legacy\"/>\n                            <Property key=\"AddItem\" value=\"shaders compatibility\"/>\n                            <Property key=\"AddItem\" value=\"shaders\"/>\n                        </Widget>\n                        <!-- Max Lights -->\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"178 4 160 18\" align=\"Left Top\" name=\"MaxLightsText\">\n                            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                            <UserString key=\"Caption_Text\" value=\"Default: 8\\nMaximum number of lights per object.\\n\\nA low number near default will cause light popping similar to what you would see with legacy lighting.\"/>\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"\" position=\"0 0 0 0\">\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingCategory\" value=\"Shaders\"/>\n                            <UserString key=\"SettingName\" value=\"max lights\"/>\n                            <UserString key=\"SettingValueType\" value=\"Integer\"/>\n                            <UserString key=\"SettingLabelWidget\" value=\"MaxLightsText\"/>\n                            <UserString key=\"SettingLabelCaption\" value=\"Max Lights: (%s)\"/>\n                        </Widget>\n                        <Widget type=\"ComboBox\" skin=\"MW_ComboBox\" position=\"178 28 170 24\" align=\"Left Top\" name=\"MaxLights\">\n                            <Property key=\"AddItem\" value=\"8\"/>\n                            <Property key=\"AddItem\" value=\"16\"/>\n                            <Property key=\"AddItem\" value=\"24\"/>\n                            <Property key=\"AddItem\" value=\"32\"/>\n                        </Widget>\n                        <Widget type=\"ImageBox\" skin=\"MW_HLine\" position=\"0 54 360 18\" align=\"Top HStretch\"/>\n                        <!-- Light Fade Start -->\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 78 352 18\" align=\"Left Top\" name=\"MaxLightDistanceText\">\n                            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                            <UserString key=\"Caption_Text\" value=\"Default: 8192 (units)\\nMaximum distance at which lights will appear.\\n\\nSet this to 0.0 to disable it.\"/>\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 104 352 18\" align=\"HStretch Top\">\n                            <Property key=\"Range\" value=\"8192\"/>\n                            <Property key=\"Page\" value=\"128\"/>\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingMin\" value=\"0\"/>\n                            <UserString key=\"SettingMax\" value=\"8192\"/>\n                            <UserString key=\"SettingCategory\" value=\"Shaders\"/>\n                            <UserString key=\"SettingName\" value=\"maximum light distance\"/>\n                            <UserString key=\"SettingValueType\" value=\"Integer\"/>\n                            <UserString key=\"SettingLabelWidget\" value=\"MaxLightDistanceText\"/>\n                            <UserString key=\"SettingLabelCaption\" value=\"Maximum Light Distance (%s)\"/>\n                        </Widget>\n                        <!-- Light Fade Multiplier -->\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 128 352 18\" align=\"Left Top\" name=\"LightFadeMultiplierText\">\n                            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                            <UserString key=\"Caption_Text\" value=\"Default: 0.85\\nFraction of maximum distance at which lights will start to fade.\\n\\nSet this to a low value for slower transitions or a high value for quicker transitions.\"/>\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 152 352 18\" align=\"HStretch Top\">\n                            <Property key=\"Range\" value=\"10000\"/>\n                            <Property key=\"Page\" value=\"100\"/>\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingCategory\" value=\"Shaders\"/>\n                            <UserString key=\"SettingName\" value=\"light fade start\"/>\n                            <UserString key=\"SettingValueType\" value=\"Float\"/>\n                            <UserString key=\"SettingMin\" value=\"0.0\"/>\n                            <UserString key=\"SettingMax\" value=\"1.0\"/>\n                            <UserString key=\"SettingLabelWidget\" value=\"LightFadeMultiplierText\"/>\n                            <UserString key=\"SettingLabelCaption\" value=\"Fade Start Multiplier (%s)\"/>\n                        </Widget>\n                        <!-- Bounding Sphere Multiplier -->\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 176 352 18\" align=\"Left Top\" name=\"BoundingSphereMultText\">\n                            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                            <UserString key=\"Caption_Text\" value=\"Default: 1.65\\nMultipler for bounding sphere of lights.\\nHigher numbers allows for smooth falloff but require an increase in number of max lights.\\n\\nDoes not effect the illumination or strength of lights.\"/>\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 200 352 18\" align=\"HStretch Top\">\n                            <Property key=\"Range\" value=\"500000\"/>\n                            <Property key=\"Page\" value=\"1000\"/>\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingMin\" value=\"0.0\"/>\n                            <UserString key=\"SettingMax\" value=\"5.0\"/>\n                            <UserString key=\"SettingCategory\" value=\"Shaders\"/>\n                            <UserString key=\"SettingName\" value=\"light bounds multiplier\"/>\n                            <UserString key=\"SettingValueType\" value=\"Float\"/>\n                            <UserString key=\"SettingLabelWidget\" value=\"BoundingSphereMultText\"/>\n                            <UserString key=\"SettingLabelCaption\" value=\"Bounding Sphere Multiplier (%s)\"/>\n                        </Widget>\n                        <!-- Minimum Ambient Brightness -->\n                        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 224 352 18\" align=\"Left Top\" name=\"MinimumBrightnessText\">\n                            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                            <UserString key=\"Caption_Text\" value=\"Default: 0.08\\nMinimum ambient interior brightness.\\n\\nIncrease this if you feel interiors are too dark.\"/>\n                        </Widget>\n                        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" position=\"0 248 352 18\" align=\"HStretch Top\">\n                            <Property key=\"Range\" value=\"10000\"/>\n                            <Property key=\"Page\" value=\"100\"/>\n                            <UserString key=\"SettingType\" value=\"Slider\"/>\n                            <UserString key=\"SettingCategory\" value=\"Shaders\"/>\n                            <UserString key=\"SettingName\" value=\"minimum interior brightness\"/>\n                            <UserString key=\"SettingValueType\" value=\"Float\"/>\n                            <UserString key=\"SettingLabelWidget\" value=\"MinimumBrightnessText\"/>\n                            <UserString key=\"SettingLabelCaption\" value=\"Minimum Interior Brightness (%s)\"/>\n                        </Widget>\n                        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 290 0 0\" align=\"Top Left\" name=\"LightsResetButton\">\n                            <Property key=\"Caption\" value=\"Reset To Defaults\"/>\n                        </Widget>\n                    </Widget>\n                    <!--\n                    <Widget type=\"TabItem\" skin=\"\" position=\"0 28 352 268\">\n                    <Widget type=\"Widget\" skin=\"\" position=\"0 28 352 268\">\n                        <Property key=\"Visible\" value=\"false\"/>\n                        <Property key=\"Caption\" value=\"  Shadows  \"/>\n                        <Widget type=\"HBox\" skin=\"\" position=\"4 4 350 24\">\n                            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"ShadowsEnabledButton\">\n                                <UserString key=\"SettingCategory\" value=\"Shadows\"/>\n                                <UserString key=\"SettingName\" value=\"enabled\"/>\n                                <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                            </Widget>\n                            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 65 16\" align=\"Left Top\">\n                                <Property key=\"Caption\" value=\"Shadows\"/>\n                            </Widget>\n                        </Widget>\n                        <Widget type=\"Widget\" skin=\"\" position=\"24 32 300 230\">\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 0 350 24\">\n                                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"ShadowsLargeDistance\">\n                                    <UserString key=\"SettingCategory\" value=\"Shadows\"/>\n                                    <UserString key=\"SettingName\" value=\"split\"/>\n                                    <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 182 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"Large distance (PSSM3)\"/>\n                                </Widget>\n                            </Widget>\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 28 350 24\">\n                                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"TerrainShadows\">\n                                    <UserString key=\"SettingCategory\" value=\"Shadows\"/>\n                                    <UserString key=\"SettingName\" value=\"terrain shadows\"/>\n                                    <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 122 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"Terrain shadows\"/>\n                                </Widget>\n                            </Widget>\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 56 350 24\">\n                                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"ActorShadows\">\n                                    <UserString key=\"SettingCategory\" value=\"Shadows\"/>\n                                    <UserString key=\"SettingName\" value=\"actor shadows\"/>\n                                    <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 108 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"Actor shadows\"/>\n                                </Widget>\n                            </Widget>\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 84 350 24\">\n                                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"StaticsShadows\">\n                                    <UserString key=\"SettingCategory\" value=\"Shadows\"/>\n                                    <UserString key=\"SettingName\" value=\"statics shadows\"/>\n                                    <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 111 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"World shadows\"/>\n                                </Widget>\n                            </Widget>\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 112 350 24\">\n                                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"0 0 24 24\" align=\"Left Top\" name=\"MiscShadows\">\n                                    <UserString key=\"SettingCategory\" value=\"Shadows\"/>\n                                    <UserString key=\"SettingName\" value=\"misc shadows\"/>\n                                    <UserString key=\"SettingType\" value=\"CheckButton\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"28 4 102 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"Misc shadows\"/>\n                                </Widget>\n                            </Widget>\n                            <Widget type=\"HBox\" skin=\"\" position=\"4 140 350 24\">\n                                <Widget type=\"ComboBox\" skin=\"MW_ComboBox\" position=\"0 0 60 24\" align=\"Left Top\" name=\"ShadowsTextureSize\">\n                                    <Property key=\"AddItem\" value=\"512\"/>\n                                    <Property key=\"AddItem\" value=\"1024\"/>\n                                    <Property key=\"AddItem\" value=\"2048\"/>\n                                    <Property key=\"AddItem\" value=\"4096\"/>\n                                </Widget>\n                                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"64 4 90 16\" align=\"Left Top\">\n                                    <Property key=\"Caption\" value=\"Texture size\"/>\n                                </Widget>\n                            </Widget>\n                        </Widget>\n                    </Widget>\n                    -->\n                </Widget>\n            </Widget>\n        </Widget>\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"320 420 56 24\" align=\"Right Bottom\" name=\"OkButton\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sOK}\"/>\n        </Widget>\n    </Widget>\n    <CodeGeneratorSettings/>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_spell_buying_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 450 288\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Visible\" value=\"false\"/>\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 24 450 18\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sSpellServiceTitle}\"/>\n        </Widget>\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 4 434 18\" align=\"Center Top\">\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"Caption\" value=\"#{sServiceSpellsTitle}\"/>\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"6 41 429 204\" align=\"Left Stretch\">\n            <Widget type=\"ScrollView\" skin=\"MW_ScrollView\" position=\"2 2 427 198\" align=\"Left Top Stretch\" name=\"SpellsView\">\n                <Property key=\"CanvasAlign\" value=\"Left\"/>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 250 24 24\" name=\"PlayerGold\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Right\"/>\n        </Widget>\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"375 251 60 24\" name=\"CancelButton\" align=\"Right Top\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sOK}\"/>\n        </Widget>\n\n    </Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_spell_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window_Pinnable\" layer=\"Windows\" position=\"0 0 300 600\" name=\"_Main\">\n        <Property key=\"MinSize\" value=\"40 40\"/>\n\n        <!-- Effect box-->\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 8 268 23\" align=\"Left Top HStretch\">\n            <Widget type=\"Widget\" skin=\"\" position=\"2 1 264 20\" align=\"Left Top Stretch\" name=\"EffectsBox\"/>\n        </Widget>\n\n        <!-- Spell list -->\n        <Widget type=\"SpellView\" skin=\"MW_SpellView\" position=\"8 38 268 490\" align=\"Left Top Stretch\" name=\"SpellView\">\n        </Widget>\n\n        <Widget type=\"HBox\" position=\"8 535 268 23\" align=\"Right Bottom HStretch\">\n            <Widget type=\"Spacer\"/>\n            <!-- Spell deletion button -->\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" align=\"Right Bottom\" position=\"8 535 0 23\" name=\"DeleteSpellButton\">\n                <Property key=\"Caption\" value=\"#{sDelete}\"/>\n                <Property key=\"NeedKey\" value=\"false\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Search box-->\n        <Widget type=\"EditBox\" skin=\"MW_TextBoxEditWithBorder\" position=\"8 535 268 23\" align=\"Left Bottom HStretch\" name=\"FilterEdit\">\n            <UserString key=\"AcceptTab\" value=\"true\"/>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_spellcreation_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 560 394\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"HBox\" position=\"12 12 250 30\">\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                <Property key=\"Caption\" value=\"#{sName}\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sSpellmakingHelp1}\"/>\n            </Widget>\n\n            <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"0 0 30 30\" name=\"NameEdit\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n\n            <Widget type=\"Widget\"/>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"280 0 300 24\">\n            <Property key=\"Caption\" value=\"#{sEnchantmentMenu4}\"/>\n            <Property key=\"TextAlign\" value=\"Left HCenter\"/>\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n            <UserString key=\"Caption_Text\" value=\"#{sSpellmakingHelp2}\"/>\n        </Widget>\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"280 0 258 24\" name=\"MagickaCost\">\n            <Property key=\"TextAlign\" value=\"Right HCenter\"/>\n            <Property key=\"NeedMouse\" value=\"false\"/>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"280 24 300 24\">\n            <Property key=\"Caption\" value=\"#{sSpellmakingMenu1}\"/>\n            <Property key=\"TextAlign\" value=\"Left HCenter\"/>\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n            <UserString key=\"Caption_Text\" value=\"#{sSpellmakingHelp3}\"/>\n        </Widget>\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"280 24 258 24\" name=\"SuccessChance\">\n            <Property key=\"TextAlign\" value=\"Right HCenter\"/>\n            <Property key=\"NeedMouse\" value=\"false\"/>\n        </Widget>\n\n        <!-- Available effects -->\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"12 48 300 24\">\n            <Property key=\"Caption\" value=\"#{sMagicEffects}\"/>\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n            <UserString key=\"Caption_Text\" value=\"#{sSpellmakingHelp5}\"/>\n        </Widget>\n        <Widget type=\"MWList\" skin=\"MW_SimpleList\" position=\"12 76 202 269\" name=\"AvailableEffects\">\n        </Widget>\n\n        <!-- Used effects -->\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"226 48 300 24\">\n            <Property key=\"Caption\" value=\"#{sEffects}\"/>\n            <UserString key=\"ToolTipType\" value=\"Layout\"/>\n            <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n            <UserString key=\"Caption_Text\" value=\"#{sSpellmakingHelp6}\"/>\n        </Widget>\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"226 76 316 269\">\n            <Widget type=\"ScrollView\" skin=\"MW_ScrollViewH\" position=\"4 4 308 261\" name=\"UsedEffects\">\n                <Property key=\"CanvasAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"HBox\" position=\"0 336 558 60\">\n            <Property key=\"Padding\" value=\"16\"/>\n\n            <Widget type=\"Spacer\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\">\n                <Property key=\"Caption\" value=\"#{sBarterDialog7}\"/>\n                <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                <UserString key=\"ToolTipLayout\" value=\"TextToolTip\"/>\n                <UserString key=\"Caption_Text\" value=\"#{sSpellmakingHelp4}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"PriceLabel\">\n            </Widget>\n\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"BuyButton\">\n                <Property key=\"Caption\" value=\"#{sBuy}\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                <Property key=\"Caption\" value=\"#{sCancel}\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_stats_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window_Pinnable\" layer=\"Windows\" position=\"0 0 500 342\" name=\"_Main\">\n        <Property key=\"MinSize\" value=\"244 114\"/>\n        <Widget type=\"Widget\" skin=\"\" name=\"LeftPane\" position=\"0 0 220 342\">\n            <UserString key=\"LeftOffsetWidth\" value=\"24\"/>\n            <UserString key=\"LeftPaneRatio\" value=\"0.44\"/>\n\n            <!-- Player health stats -->\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 8 212 62\" align=\"Left Top HStretch\">\n                <!-- Health -->\n                <Widget type=\"Widget\" skin=\"\" position=\"4 4 204 18\" name=\"Health\" align=\"Left Top HStretch\">\n                    <Property key=\"NeedMouse\" value=\"true\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n                    <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\health.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 0 70 18\" name=\"Health_str\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                        <Property key=\"Caption\" value=\"#{sHealth}\"/>\n                    </Widget>\n                    <Widget type=\"ProgressBar\" skin=\"MW_Progress_Red\" position=\"74 0 130 18\" name=\"HBar\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                        <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"0 0 130 14\" name=\"HBarT\" align=\"Right VCenter\">\n                            <Property key=\"NeedMouse\" value=\"false\"/>\n                        </Widget>\n                    </Widget>\n                </Widget>\n\n                <!-- Magicka -->\n                <Widget type=\"Widget\" skin=\"\" position=\"4 22 204 18\" name=\"Magicka\" align=\"Left Top HStretch\">\n                    <Property key=\"NeedMouse\" value=\"true\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n                    <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\magicka.dds\"/>\n\n                    <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 0 70 18\" name=\"Magicka_str\" align=\"Left Top HStretch\">\n                        <Property key=\"Caption\" value=\"#{sMagic}\"/>\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n\n                    <Widget type=\"ProgressBar\" skin=\"MW_Progress_Blue\" position=\"74 0 130 18\" name=\"MBar\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                        <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"0 0 130 14\" name=\"MBarT\" align=\"Right VCenter\">\n                            <Property key=\"NeedMouse\" value=\"false\"/>\n                        </Widget>\n                    </Widget>\n                </Widget>\n\n                <!-- Fatigue -->\n                <Widget type=\"Widget\" skin=\"\" position=\"4 40 204 18\" name=\"Fatigue\" align=\"Left Top HStretch\">\n                    <Property key=\"NeedMouse\" value=\"true\"/>\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"HealthToolTip\"/>\n                    <UserString key=\"ImageTexture_HealthImage\" value=\"icons\\k\\fatigue.dds\"/>\n\n                    <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 0 70 18\" name=\"Fatigue_str\" align=\"Left Top HStretch\">\n                        <Property key=\"Caption\" value=\"#{sFatigue}\"/>\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n\n                    <Widget type=\"ProgressBar\" skin=\"MW_Progress_Green\" position=\"74 0 130 18\" name=\"FBar\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                        <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"0 0 130 14\" name=\"FBarT\" align=\"Right VCenter\">\n                            <Property key=\"NeedMouse\" value=\"false\"/>\n                        </Widget>\n                    </Widget>\n                </Widget>\n            </Widget>\n\n            <!-- Player level, race and class -->\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 78 212 62\" align=\"Top HStretch\">\n                <Widget type=\"HBox\" position=\"4 4 204 18\" align=\"Top HStretch\">\n                    <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"0 0 200 18\" name=\"Level_str\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sLevel}\"/>\n                        <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                        <UserString key=\"ToolTipLayout\" value=\"LevelToolTip\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"200 0 40 18\" name=\"LevelText\" align=\"Right Top\">\n                        <Property key=\"TextAlign\" value=\"Right Top\"/>\n                        <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                        <UserString key=\"ToolTipLayout\" value=\"LevelToolTip\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n\n                </Widget>\n                <Widget type=\"HBox\" position=\"4 24 204 18\" align=\"Top HStretch\">\n                    <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"0 0 95 18\" name=\"Race_str\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sRace}\"/>\n                        <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                        <UserString key=\"ToolTipLayout\" value=\"RaceToolTip\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"104 0 200 18\" name=\"RaceText\" align=\"Left Top\">\n                        <Property key=\"TextAlign\" value=\"Right Top\"/>\n                        <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                        <UserString key=\"ToolTipLayout\" value=\"RaceToolTip\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n                </Widget>\n                <Widget type=\"HBox\" position=\"4 42 204 18\" align=\"Top HStretch\">\n                    <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"0 0 95 18\" name=\"Class_str\" align=\"Left Top\">\n                        <Property key=\"Caption\" value=\"#{sClass}\"/>\n                        <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                        <UserString key=\"ToolTipLayout\" value=\"ClassToolTip\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"104 0 200 18\" name=\"ClassText\" align=\"Left Top\">\n                        <Property key=\"TextAlign\" value=\"Right Top\"/>\n                        <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                        <UserString key=\"ToolTipLayout\" value=\"ClassToolTip\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 148 212 152\" align=\"Left Top Stretch\">\n            <!-- TODO: this should be a scroll view -->\n            <Widget type=\"Widget\" skin=\"\" position=\"4 4 204 144\" align=\"Left Top Stretch\">\n                <Widget type=\"Button\" skin=\"\" position=\"0 0 204 18\" name=\"Attrib1Box\" align=\"Left Top HStretch\">\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeStrength}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sStrDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_strength.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 160 18\" name=\"Attrib1\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"160 0 44 18\" name=\"AttribVal1\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                </Widget>\n\n                <Widget type=\"Button\" skin=\"\" position=\"0 18 204 18\" name=\"Attrib2Box\" align=\"Left Top HStretch\">\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeIntelligence}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sIntDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_int.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 160 18\" name=\"Attrib2\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"160 0 44 18\" name=\"AttribVal2\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                </Widget>\n\n                <Widget type=\"Button\" skin=\"\" position=\"0 36 204 18\" name=\"Attrib3Box\" align=\"Left Top HStretch\">\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeWillpower}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sWilDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_wilpower.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 160 18\" name=\"Attrib3\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"160 0 44 18\" name=\"AttribVal3\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                </Widget>\n\n                <Widget type=\"Button\" skin=\"\" position=\"0 54 204 18\" name=\"Attrib4Box\" align=\"Left Top HStretch\">\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeAgility}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sAgiDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_agility.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 160 18\" name=\"Attrib4\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"160 0 44 18\" name=\"AttribVal4\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                </Widget>\n\n                <Widget type=\"Button\" skin=\"\" position=\"0 72 204 18\" name=\"Attrib5Box\" align=\"Left Top HStretch\">\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeSpeed}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sSpdDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_speed.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 160 18\" name=\"Attrib5\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"160 0 44 18\" name=\"AttribVal5\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                </Widget>\n\n                <Widget type=\"Button\" skin=\"\" position=\"0 90 204 18\" name=\"Attrib6Box\" align=\"Left Top HStretch\">\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeEndurance}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sEndDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_endurance.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 160 18\" name=\"Attrib6\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"160 0 44 18\" name=\"AttribVal6\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                </Widget>\n\n                <Widget type=\"Button\" skin=\"\" position=\"0 108 204 18\" name=\"Attrib7Box\" align=\"Left Top HStretch\">\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributePersonality}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sPerDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_personality.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 160 18\" name=\"Attrib7\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"160 0 44 18\" name=\"AttribVal7\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                </Widget>\n\n                <Widget type=\"Button\" skin=\"\" position=\"0 126 204 18\" name=\"Attrib8Box\" align=\"Left Top HStretch\">\n                    <UserString key=\"ToolTipType\" value=\"Layout\"/>\n                    <UserString key=\"ToolTipLayout\" value=\"AttributeToolTip\"/>\n                    <UserString key=\"Caption_AttributeName\" value=\"#{sAttributeLuck}\"/>\n                    <UserString key=\"Caption_AttributeDescription\" value=\"#{sLucDesc}\"/>\n                    <UserString key=\"ImageTexture_AttributeImage\" value=\"icons\\k\\attribute_luck.dds\"/>\n                    <Widget type=\"TextBox\" skin=\"SandText\" position=\"0 0 160 18\" name=\"Attrib8\" align=\"Left Top HStretch\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                    <Widget type=\"TextBox\" skin=\"SandTextRight\" position=\"160 0 44 18\" name=\"AttribVal8\" align=\"Right Top\">\n                        <Property key=\"NeedMouse\" value=\"false\"/>\n                    </Widget>\n                </Widget>\n            </Widget>\n            </Widget>\n\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" name=\"RightPane\" position=\"220 0 280 342\">\n\n            <!-- Player skills, factions, birthsign and reputation -->\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"8 8 248 292\" align=\"Left Stretch\" name=\"Skills\">\n                <Widget type=\"ScrollView\" skin=\"MW_ScrollView\" position=\"4 4 240 284\" align=\"Left Top Stretch\" name=\"SkillView\">\n                    <Property key=\"CanvasAlign\" value=\"Left Top\"/>\n                </Widget>\n            </Widget>\n\n        </Widget>\n\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_text.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n\n<!--\n\nUnknown colors from Morrowind.ini:\ncolor_focus=80,80,80 # ????\ncolor_misc=0,205,205 # ????\n\n-->\n\n    <Resource type=\"ResourceSkin\" name=\"NormalText\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Left Bottom\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"normal\" colour=\"#{fontcolour=header}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"SandText\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Left Bottom\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 -1 16 16\" align=\"Stretch\">\n            <State name=\"normal\" colour=\"#{fontcolour=normal}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"SandTextVCenter\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"normal\" colour=\"#{fontcolour=normal}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"SandTextRight\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Right Bottom\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"normal\" colour=\"#{fontcolour=normal}\"/>\n            <State name=\"increased\" colour=\"#{fontcolour=positive}\"/>\n            <State name=\"decreased\" colour=\"#{fontcolour=negative}\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"SandBrightText\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Left Bottom\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"normal\" colour=\"#{fontcolour=header}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"CountText\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"normal\" colour=\"#{fontcolour=count}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_StatNameC\" size=\"200 18\">\n        <Child type=\"TextBoxC\" skin=\"SandText\" offset=\"0 0 200 18\" align=\"Left HStretch\" name=\"StatName\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"SandTextButtonC\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n        <Property key=\"NeedKey\" value=\"true\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"disabled\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal\" colour=\"#{fontcolour=normal}\" shift=\"0\"/>\n            <State name=\"highlighted\" colour=\"#{fontcolour=normal_over}\" shift=\"0\"/>\n            <State name=\"pushed\" colour=\"#{fontcolour=normal_pressed}\" shift=\"0\"/>\n            <State name=\"disabled_checked\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal_checked\" colour=\"#{fontcolour=active}\" shift=\"0\"/>\n            <State name=\"highlighted_checked\" colour=\"#{fontcolour=active_over}\" shift=\"0\"/>\n            <State name=\"pushed_checked\" colour=\"#{fontcolour=active_pressed}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"SandTextButton\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Left Bottom\"/>\n        <Property key=\"NeedKey\" value=\"true\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"disabled\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal\" colour=\"#{fontcolour=normal}\" shift=\"0\"/>\n            <State name=\"highlighted\" colour=\"#{fontcolour=normal_over}\" shift=\"0\"/>\n            <State name=\"pushed\" colour=\"#{fontcolour=normal_pressed}\" shift=\"0\"/>\n            <State name=\"disabled_checked\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal_checked\" colour=\"#{fontcolour=active}\" shift=\"0\"/>\n            <State name=\"highlighted_checked\" colour=\"#{fontcolour=active_over}\" shift=\"0\"/>\n            <State name=\"pushed_checked\" colour=\"#{fontcolour=active_pressed}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"SandTextButtonDisabled\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"TextAlign\" value=\"Left Bottom\"/>\n        <Property key=\"NeedKey\" value=\"true\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"normal\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"SpellTextUnequipped\" size=\"16 16\">\n        <Property key=\"FontName\" value=\"Default\"/>\n        <Property key=\"NeedKey\" value=\"true\"/>\n        <Property key=\"TextAlign\" value=\"Left Bottom\"/>\n        <BasisSkin type=\"SimpleText\" offset=\"0 0 16 16\" align=\"Stretch\">\n            <State name=\"disabled\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"highlighted\" colour=\"#{fontcolour=disabled_over}\" shift=\"0\"/>\n            <State name=\"pushed\" colour=\"#{fontcolour=disabled_pressed}\" shift=\"0\"/>\n            <State name=\"disabled_checked\" colour=\"#{fontcolour=disabled}\" shift=\"0\"/>\n            <State name=\"normal_checked\" colour=\"#{fontcolour=active}\" shift=\"0\"/>\n            <State name=\"highlighted_checked\" colour=\"#{fontcolour=active_over}\" shift=\"0\"/>\n            <State name=\"pushed_checked\" colour=\"#{fontcolour=active_pressed}\" shift=\"0\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_StatNameButtonC\" size=\"200 18\">\n        <Child type=\"Button\" skin=\"SandTextButtonC\" offset=\"0 0 200 18\" align=\"Left HStretch\" name=\"StatNameButton\"/>\n    </Resource>\n\n\n\n    <Resource type=\"ResourceSkin\" name=\"MW_StatNameValueButton\" size=\"200 18\">\n        <Child type=\"Button\" skin=\"SandText\" offset=\"0 0 160 18\" align=\"Left HStretch\" name=\"StatNameButton\"/>\n        <Child type=\"Button\" skin=\"SandTextRight\" offset=\"160 0 40 18\" align=\"Right Top\" name=\"StatValueButton\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_EffectImage\" size=\"200 24\">\n        <Child type=\"ImageBox\" skin=\"ImageBox\" offset=\"4 4 16 16\" align=\"Left Top\" name=\"Image\"/>\n        <Child type=\"TextBox\" skin=\"SandText\" offset=\"24 0 176 20\" align=\"VCenter HStretch\" name=\"Text\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ChargeBar\" size=\"204 18\">\n        <Child type=\"ProgressBar\" skin=\"MW_Progress_Red\" offset=\"0 0 204 18\" align=\"Right Top Stretch\" name=\"Bar\"/>\n        <Child type=\"TextBox\" skin=\"ProgressText\" offset=\"0 0 204 14\" align=\"Stretch\" name=\"BarText\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_ChargeBar_Blue\" size=\"204 18\">\n        <Child type=\"ProgressBar\" skin=\"MW_Progress_Blue\" offset=\"0 0 204 18\" align=\"Right Top Stretch\" name=\"Bar\"/>\n        <Child type=\"TextBox\" skin=\"ProgressText\" offset=\"0 0 204 16\" align=\"Right Top Stretch\" name=\"BarText\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_DynamicStat_Red\" size=\"204 18\">\n        <Child type=\"TextBox\" skin=\"SandText\" offset=\"0 0 100 18\" align=\"Left Top\" name=\"Text\"/>\n        <Child type=\"ProgressBar\" skin=\"MW_Progress_Red\" offset=\"74 0 130 18\" align=\"Right Top\" name=\"Bar\"/>\n        <Child type=\"TextBox\" skin=\"ProgressText\" offset=\"74 0 130 14\" align=\"Right Top\" name=\"BarText\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_DynamicStat_Blue\" size=\"204 18\">\n        <Child type=\"TextBox\" skin=\"SandText\" offset=\"0 0 100 18\" align=\"Left Top\" name=\"Text\"/>\n        <Child type=\"ProgressBar\" skin=\"MW_Progress_Blue\" offset=\"74 0 130 18\" align=\"Right Top\" name=\"Bar\"/>\n        <Child type=\"TextBox\" skin=\"ProgressText\" offset=\"74 0 130 14\" align=\"Right Top\" name=\"BarText\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_DynamicStat_Green\" size=\"204 18\">\n        <Child type=\"TextBox\" skin=\"SandText\" offset=\"0 0 100 18\" align=\"Left Top\" name=\"Text\"/>\n        <Child type=\"ProgressBar\" skin=\"MW_Progress_Green\" offset=\"74 0 130 18\" align=\"Right Top\" name=\"Bar\"/>\n        <Child type=\"TextBox\" skin=\"ProgressText\" offset=\"74 0 130 14\" align=\"Right Top\" name=\"BarText\"/>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_text_input.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 320 97\" align=\"Center\" name=\"_Main\">\n\n        <!-- Appearance -->\n        <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"6 6 300 18\" name=\"LabelT\" align=\"Left Top\">\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n        </Widget>\n        <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"6 28 300 30\" name=\"TextEdit\" align=\"Left Top\"/>\n\n        <!-- Dialog buttons -->\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"264 60 42 23\" name=\"OKButton\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"OK\"/>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_tooltips.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Widget\" layer=\"Popup\" position=\"0 0 300 300\" name=\"_Main\">\n\n        <!-- Dynamically constructed tooltip goes here -->\n        <Widget type=\"Widget\" skin=\"HUD_Box_NoTransp\" position=\"0 0 300 300\" align=\"Stretch\" name=\"DynamicToolTipBox\">\n        </Widget>\n\n        <!-- Text tooltip, one line -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 300 300\" align=\"Stretch\" name=\"TextToolTipOneLine\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"8 8 284 284\" align=\"Left Top\" name=\"TextOneLine\">\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Text tooltip, multiline -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 0\" align=\"Stretch\" name=\"TextToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"6\"/>\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 8 268 0\" align=\"Left Top\" name=\"Text\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Faction tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 0\" align=\"Stretch\" name=\"FactionToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 8 436 0\" align=\"Left Top\" name=\"FactionText\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Race tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 56\" align=\"Stretch\" name=\"RaceToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n            <Property key=\"Spacing\" value=\"8\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"8 8 0 18\" align=\"Left Top\" name=\"CenteredCaption\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 30 430 18\" align=\"Left Top\" name=\"CenteredCaptionText\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"Shrink\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Specialization tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 56\" align=\"Stretch\" name=\"SpecializationToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"8 8 140 18\" align=\"Left Top\" name=\"Caption\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"0 30 140 18\" align=\"Left Top\" name=\"ColumnText\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Class tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 78\" align=\"Stretch\" name=\"ClassToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n            <Property key=\"Spacing\" value=\"8\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"8 8 0 18\" align=\"Left Top HStretch\" name=\"ClassName\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 30 430 18\" align=\"Left Top\" name=\"ClassDescription\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"Shrink\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"8 52 0 18\" align=\"Left Bottom\" name=\"ClassSpecialisation\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Hand-to-hand tooltip -->\n        <Widget type=\"HBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 300 300\" align=\"Stretch\" name=\"HandToHandToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"6\"/>\n\n            <Widget type=\"VBox\">\n                <UserString key=\"VStretch\" value=\"true\"/>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"8 8 32 32\" align=\"Left Top\" name=\"HandToHandImage\"/>\n                <Widget type=\"Spacer\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"44 8 248 284\" align=\"Left Top\" name=\"HandToHandText\">\n                <Property key=\"TextAlign\" value=\"Center\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Health/Magicka/Fatigue tooltip -->\n        <Widget type=\"HBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 0\" align=\"Stretch\" name=\"HealthToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"14\"/>\n            <Property key=\"Spacing\" value=\"8\"/>\n\n            <Widget type=\"VBox\">\n                <UserString key=\"VStretch\" value=\"true\"/>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"8 8 32 32\" align=\"Left Top\" name=\"HealthImage\"/>\n                <Widget type=\"Spacer\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"44 8 392 0\" align=\"Left Top\" name=\"HealthDescription\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"Shrink\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Attribute tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 0\" align=\"Stretch\" name=\"AttributeToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n            <Property key=\"Spacing\" value=\"8\"/>\n\n            <Widget type=\"HBox\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Property key=\"Spacing\" value=\"8\"/>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"8 8 32 32\" align=\"Left Top\" name=\"AttributeImage\"/>\n\n                <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"44 8 0 32\" align=\"Left Top\" name=\"AttributeName\">\n                    <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n                    <UserString key=\"HStretch\" value=\"true\"/>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 44 436 248\" align=\"Left Top\" name=\"AttributeDescription\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"Shrink\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Skill tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 98\" align=\"Stretch\" name=\"SkillToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n\n            <Widget type=\"HBox\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Property key=\"Spacing\" value=\"8\"/>\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"8 8 32 32\" align=\"Left Top\" name=\"SkillImage\"/>\n\n                <Widget type=\"VBox\">\n                    <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"44 8 0 16\" align=\"Left Top\" name=\"SkillName\">\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"44 24 0 16\" align=\"Left Top\" name=\"SkillAttribute\">\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"Widget\" skin=\"\" position=\"0 0 0 2\" align=\"Left Top\" />\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 44 430 0\" align=\"Left Top\" name=\"SkillDescription\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"Shrink\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n\n            <Widget type=\"Widget\" skin=\"\" position=\"0 0 0 2\" align=\"Left Top\" />\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"8 48 200 18\" align=\"Left Bottom\" name=\"SkillMaxed\">\n                <Property key=\"Caption\" value=\"#{sSkillMaxReached}\"/>\n                <Property key=\"TextAlign\" value=\"Center\"/>\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n            <Widget type=\"VBox\" name=\"SkillProgressVBox\">\n                <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"8 48 200 18\" align=\"Left Bottom\">\n                    <Property key=\"Caption\" value=\"#{sSkillProgress}\"/>\n                    <Property key=\"TextAlign\" value=\"Center\"/>\n                </Widget>\n\n                <Widget type=\"ProgressBar\" skin=\"MW_Progress_Red\" position=\"50 70 200 20\" align=\"HCenter Bottom\" name=\"SkillProgress\">\n                    <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"0 0 200 16\" align=\"Stretch\" name=\"SkillProgressText\">\n                        <Property key=\"TextAlign\" value=\"Center\"/>\n                    </Widget>\n                </Widget>\n            </Widget>\n        </Widget>\n\n        <!-- Skill tooltip (without progress bar) -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 52\" align=\"Stretch\" name=\"SkillNoProgressToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n\n            <Widget type=\"HBox\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Property key=\"Spacing\" value=\"8\"/>\n\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"8 8 32 32\" align=\"Left Top\" name=\"SkillNoProgressImage\"/>\n\n                <Widget type=\"VBox\">\n                    <Widget type=\"AutoSizedEditBox\" skin=\"NormalText\" position=\"44 8 0 16\" align=\"Left Top\" name=\"SkillNoProgressName\">\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"44 24 0 16\" align=\"Left Top\" name=\"SkillNoProgressAttribute\">\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"Widget\" skin=\"\" position=\"0 0 0 2\" align=\"Left Top\" />\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 44 430 0\" align=\"Left Top\" name=\"SkillNoProgressDescription\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Level tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 300 58\" align=\"HStretch\" name=\"LevelToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"8 8 284 18\" align=\"Left Top\">\n                <Property key=\"Caption\" value=\"#{sLevelProgress}\"/>\n                <Property key=\"TextAlign\" value=\"Center\"/>\n            </Widget>\n\n            <Widget type=\"ProgressBar\" skin=\"MW_Progress_Red\" position=\"50 30 180 20\" align=\"HCenter Bottom\" name=\"LevelProgress\">\n                <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"0 0 180 20\" align=\"HCenter\" name=\"LevelProgressText\">\n                    <Property key=\"TextAlign\" value=\"HCenter Top\"/>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"0 0 0 0\" align=\"Left Top\" name=\"LevelDetailText\">\n                <Property key=\"TextAlign\" value=\"HCenter Top\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Birthsign tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 300 300\" align=\"Stretch\" name=\"BirthSignToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n\n            <!-- Birthsign image -->\n            <Widget type=\"Widget\" skin=\"MW_Box\" position=\"18 13 263 137\" align=\"Top HCenter\">\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"2 2 259 133\" name=\"BirthSignImage\" align=\"Left Top\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"8 154 284 138\" align=\"Top\" name=\"BirthSignText\">\n                <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Magic effect tooltip -->\n        <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" position=\"0 0 0 0\" align=\"Stretch\" name=\"MagicEffectToolTip\">\n            <Property key=\"AutoResize\" value=\"true\"/>\n            <Property key=\"Padding\" value=\"8\"/>\n\n            <Widget type=\"HBox\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <Property key=\"Spacing\" value=\"8\"/>\n\n                <Widget type=\"ImageBox\" skin=\"ImageBox\" position=\"8 8 32 32\" align=\"Left Top\" name=\"MagicEffectImage\"/>\n\n                <Widget type=\"VBox\">\n                    <Widget type=\"AutoSizedTextBox\" skin=\"NormalText\" position=\"44 8 404 16\" align=\"Left Top\" name=\"MagicEffectName\">\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n\n\n                    <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"44 24 404 16\" align=\"Left Top\" name=\"MagicEffectSchool\">\n                        <Property key=\"TextAlign\" value=\"Left\"/>\n                        <UserString key=\"HStretch\" value=\"true\"/>\n                    </Widget>\n                </Widget>\n\n            </Widget>\n\n            <Widget type=\"AutoSizedEditBox\" skin=\"SandText\" position=\"8 44 436 0\" align=\"Left Top\" name=\"MagicEffectDescription\">\n                <Property key=\"MultiLine\" value=\"true\"/>\n                <Property key=\"WordWrap\" value=\"true\"/>\n                <Property key=\"TextAlign\" value=\"Left Top\"/>\n            </Widget>\n        </Widget>\n\n    </Widget>\n</MyGUI>\n\n"
  },
  {
    "path": "files/mygui/openmw_trade_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Window\" layer=\"Windows\" position=\"0 0 600 360\" name=\"_Main\">\n        <Property key=\"Visible\" value=\"false\"/>\n        <Property key=\"MinSize\" value=\"428 245\"/>\n\n        <!-- Categories -->\n        <Widget type=\"HBox\" position=\"8 8 566 24\" align=\"Left Top HStretch\" name=\"Categories\">\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"AllButton\">\n                <Property key=\"Caption\" value=\"#{sAllTab}\"/>\n                <Property key=\"NeedKey\" value=\"false\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"WeaponButton\">\n                <Property key=\"Caption\" value=\"#{sWeaponTab}\"/>\n                <Property key=\"NeedKey\" value=\"false\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"ApparelButton\">\n                <Property key=\"Caption\" value=\"#{sApparelTab}\"/>\n                <Property key=\"NeedKey\" value=\"false\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"MagicButton\">\n                <Property key=\"Caption\" value=\"#{sMagicTab}\"/>\n                <Property key=\"NeedKey\" value=\"false\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"MiscButton\">\n                <Property key=\"Caption\" value=\"#{sMiscTab}\"/>\n                <Property key=\"NeedKey\" value=\"false\"/>\n            </Widget>\n            <!-- Search box-->\n            <Widget type=\"EditBox\" skin=\"MW_TextBoxEditWithBorder\" position=\"0 0 0 23\" name=\"FilterEdit\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n                <UserString key=\"AcceptTab\" value=\"true\"/>\n            </Widget>\n        </Widget>\n\n        <!-- Items -->\n        <Widget type=\"ItemView\" skin=\"MW_ItemView\" position=\"8 38 566 185\" name=\"ItemView\" align=\"Left Top Stretch\">\n        </Widget>\n\n        <Widget type=\"Widget\" skin=\"\" position=\"8 231 566 92\" name=\"BottomPane\" align=\"Left Bottom HStretch\">\n            <Widget type=\"HBox\" position=\"0 0 566 24\" align=\"Left Top HStretch\">\n                <Widget type=\"Button\" skin=\"MW_Button\" position=\"0 0 40 24\" name=\"IncreaseButton\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"+\"/>\n                    <Property key=\"NeedKey\" value=\"false\"/>\n                </Widget>\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"48 0 140 24\" name=\"TotalBalanceLabel\" align=\"Left Top\">\n                    <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n                </Widget>\n                <Widget type=\"Spacer\" />\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"0 0 374 24\" name=\"PlayerGold\" align=\"Right Top\">\n                    <Property key=\"TextAlign\" value=\"Right\"/>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"HBox\" position=\"0 28 566 28\" align=\"Left Top HStretch\">\n                <Widget type=\"Button\" skin=\"MW_Button\" position=\"0 0 40 24\" name=\"DecreaseButton\" align=\"Left Top\">\n                    <Property key=\"Caption\" value=\"-\"/>\n                    <Property key=\"NeedKey\" value=\"false\"/>\n                </Widget>\n                <Widget type=\"NumericEditBox\" skin=\"MW_TextEdit\" position=\"48 0 140 24\" name=\"TotalBalance\" align=\"Left Top\">\n                    <Property key=\"TextAlign\" value=\"Center\"/>\n                </Widget>\n                <Widget type=\"Spacer\" />\n                <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" position=\"0 0 374 24\" name=\"MerchantGold\" align=\"Right Top\">\n                    <Property key=\"TextAlign\" value=\"Right\"/>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"HBox\" position=\"0 60 566 28\" align=\"Left Bottom HStretch\">\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"MaxSaleButton\">\n                    <Property key=\"Caption\" value=\"#{sMaxSale}\"/>\n                </Widget>\n                <Widget type=\"Spacer\" />\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OfferButton\">\n                    <Property key=\"Caption\" value=\"#{sBarterDialog8}\"/>\n                </Widget>\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                    <Property key=\"Caption\" value=\"#{sCancel}\"/>\n                </Widget>\n            </Widget>\n\n        </Widget>\n\n    </Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_trainingwindow.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 319 200\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 5 319 24\" name=\"Select\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"Caption\" value=\"#{sServiceTrainingTitle}\"/>\n        </Widget>\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"5 30 319 24\" name=\"Travel\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sTrainingServiceTitle}\"/>\n        </Widget>\n\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"6 54 299 100\" align=\"Left Top\" name=\"TrainingOptions\">\n        </Widget>\n\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 161 200 24\" name=\"PlayerGold\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Left\"/>\n        </Widget>\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"244 161 60 24\" name=\"CancelButton\" align=\"Right Top\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sCancel}\"/>\n        </Widget>\n\n    </Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_travel_window.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 500 250\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Visible\" value=\"false\"/>\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"8 22 24 24\" name=\"Select\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Right\"/>\n            <Property key=\"Caption\" value=\"#{sTravelServiceTitle}\"/>\n        </Widget>\n        <Widget type=\"TextBox\" skin=\"NormalText\" position=\"0 4 484 24\" name=\"Travel\" align=\"Center Top\">\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"Caption\" value=\"#{sServiceTravelTitle}\"/>\n        </Widget>\n\n\n        <Widget type=\"Widget\" skin=\"MW_Box\" position=\"6 42 480 165\" align=\"Left Stretch\">\n            <Widget type=\"ScrollView\" skin=\"MW_ScrollView\" position=\"4 4 472 160\" align=\"Left Top Stretch\" name=\"DestinationsView\">\n                <Property key=\"CanvasAlign\" value=\"Left\"/>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"TextBox\" skin=\"SandText\" position=\"6 211 24 24\" name=\"PlayerGold\" align=\"Right Top\">\n            <Property key=\"TextAlign\" value=\"Right\"/>\n        </Widget>\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"425 212 60 24\" name=\"CancelButton\" align=\"Right Top\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"#{sCancel}\"/>\n        </Widget>\n\n    </Widget>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_wait_dialog.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"VBox\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 600 200\" align=\"Center\" name=\"_Main\">\n        <Property key=\"Padding\" value=\"8\"/>\n        <Property key=\"Spacing\" value=\"8\"/>\n        <Property key=\"AutoResize\" value=\"true\"/>\n\n        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"DateTimeText\">\n            <Property key=\"Caption\" value=\"24 Herzfeuer (Tag 24) 2 a.m.\"/>\n        </Widget>\n\n        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"RestText\">\n        </Widget>\n\n        <Widget type=\"AutoSizedTextBox\" skin=\"SandText\" name=\"HourText\">\n        </Widget>\n\n        <Widget type=\"ScrollBar\" skin=\"MW_HScroll\" name=\"HourSlider\" position=\"0 0 0 18\">\n            <Property key=\"MoveToClick\" value=\"true\"/>\n            <Property key=\"Range\" value=\"24\"/>\n            <Property key=\"Page\" value=\"1\"/>\n            <Property key=\"WheelPage\" value=\"1\"/>\n            <UserString key=\"HStretch\" value=\"true\"/>\n        </Widget>\n\n        <Widget type=\"HBox\">\n            <UserString key=\"HStretch\" value=\"true\"/>\n            <Widget type=\"Spacer\"/>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"UntilHealedButton\">\n                <Property key=\"Caption\" value=\"#{sUntilHealed}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"WaitButton\">\n                <Property key=\"Caption\" value=\"#{sRest}\"/>\n            </Widget>\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"CancelButton\">\n                <Property key=\"Caption\" value=\"#{sCancel}\"/>\n            </Widget>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_wait_dialog_progressbar.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 219 40\" align=\"Center\" name=\"_Main\">\n\n        <Widget type=\"ProgressBar\" skin=\"MW_Progress_Blue\" position=\"5 6 199 20\" name=\"ProgressBar\">\n            <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"0 0 199 16\" name=\"ProgressText\" align=\"Right VCenter\">\n                <Property key=\"NeedMouse\" value=\"false\"/>\n            </Widget>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/openmw_windows.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n    <!-- Defines a partially transparent background -->\n    <Resource type=\"ResourceSkin\" name=\"BlackBG\" size=\"8 8\" texture=\"transparent\">\n        <Property key=\"Colour\" value=\"#{fontcolour=background}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 8 8\">\n            <State name=\"normal\" offset=\"0 0 8 8\"/>\n        </BasisSkin>\n    </Resource>\n\n     <!-- Defines a non-transparent background -->\n    <Resource type=\"ResourceSkin\" name=\"FullBlackBG\" size=\"8 8\" texture=\"black\">\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 8 8\">\n            <State name=\"normal\" offset=\"0 0 8 8\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Defines a non-transparent background -->\n     <Resource type=\"ResourceSkin\" name=\"TransparentBG\" size=\"8 8\" texture=\"transparent\">\n        <Property key=\"Colour\" value=\"#{fontcolour=background}\"/>\n    </Resource>\n\n    <!-- Define the borders for pin button (up) -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_B\" texture=\"textures\\menu_rightbuttonup_bottom.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_BR\" texture=\"textures\\menu_rightbuttonup_bottom_right.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_R\" texture=\"textures\\menu_rightbuttonup_right.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_TR\" texture=\"textures\\menu_rightbuttonup_top_right.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_T\" texture=\"textures\\menu_rightbuttonup_top.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_TL\" texture=\"textures\\menu_rightbuttonup_top_left.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_L\" texture=\"textures\\menu_rightbuttonup_left.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_BL\" texture=\"textures\\menu_rightbuttonup_bottom_left.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PU_С\" texture=\"textures\\menu_rightbuttonup_center.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Define the borders for pin button (down) -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_B\" texture=\"textures\\menu_rightbuttondown_bottom.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_BR\" texture=\"textures\\menu_rightbuttondown_bottom_right.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_R\" texture=\"textures\\menu_rightbuttondown_right.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_TR\" texture=\"textures\\menu_rightbuttondown_top_right.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_T\" texture=\"textures\\menu_rightbuttondown_top.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_TL\" texture=\"textures\\menu_rightbuttondown_top_left.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_L\" texture=\"textures\\menu_rightbuttondown_left.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_BL\" texture=\"textures\\menu_rightbuttondown_bottom_left.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"PD_С\" texture=\"textures\\menu_rightbuttondown_center.dds\">\n        <Property key=\"NeedMouse\" value=\"false\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Define the pin button skin -->\n    <Resource type=\"ResourceSkin\" name=\"PinUp\" size=\"19 19\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n        <Child type=\"Widget\" skin=\"PU_С\" offset=\"2 2 15 15\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PU_B\" offset=\"2 17 15 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PU_BR\" offset=\"17 17 2 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PU_R\" offset=\"17 2 2 15\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PU_TR\" offset=\"17 0 2 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PU_T\" offset=\"2 0 15 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PU_TL\" offset=\"0 0 2 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PU_L\" offset=\"0 2 2 15\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PU_BL\" offset=\"0 17 2 2\" align=\"Stretch\"/>\n    </Resource>\n    <Resource type=\"ResourceSkin\" name=\"PinDown\" size=\"19 19\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n        <Child type=\"Widget\" skin=\"PD_С\" offset=\"2 2 15 15\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PD_B\" offset=\"2 17 15 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PD_BR\" offset=\"17 17 2 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PD_R\" offset=\"17 2 2 15\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PD_TR\" offset=\"17 0 2 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PD_T\" offset=\"2 0 15 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PD_TL\" offset=\"0 0 2 2\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PD_L\" offset=\"0 2 2 15\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"PD_BL\" offset=\"0 17 2 2\" align=\"Stretch\"/>\n    </Resource>\n\n    <!-- Defines a pure black background -->\n    <Resource type=\"ResourceSkin\" name=\"DialogBG\" size=\"8 8\" texture=\"white\">\n        <Property key=\"Colour\" value=\"#{fontcolour=background}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 8 8\">\n            <State name=\"normal\" offset=\"0 0 8 8\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Defines a owned background -->\n    <Resource type=\"ResourceSkin\" name=\"DialogBG_NoTransp_Owned\" size=\"8 8\" texture=\"white\">\n        <Property key=\"Colour\" value=\"#{setting=GUI,color background owned}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 8 8\">\n            <State name=\"normal\" offset=\"0 0 8 8\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"DialogBG_Owned\" size=\"8 8\" texture=\"transparent\">\n        <Property key=\"Colour\" value=\"#{setting=GUI,color background owned}\"/>\n        <BasisSkin type=\"MainSkin\" offset=\"0 0 8 8\">\n            <State name=\"normal\" offset=\"0 0 8 8\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- These define the dialog borders -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"DB_B\" texture=\"textures\\menu_thick_border_bottom.dds\">\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"DB_R\" texture=\"textures\\menu_thick_border_right.dds\">\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"DB_T\" texture=\"textures\\menu_thick_border_top.dds\">\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"DB_L\" texture=\"textures\\menu_thick_border_left.dds\">\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Dialog border corners -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"DB_BR\" texture=\"textures\\menu_thick_border_bottom_right_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"DB_BL\" texture=\"textures\\menu_thick_border_bottom_left_corner.dds\">\n        <Property key=\"Pointer\" value=\"dresize2\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"DB_TR\" texture=\"textures\\menu_thick_border_top_right_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"DB_TL\" texture=\"textures\\menu_thick_border_top_left_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- These define the window borders -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_B\" texture=\"textures\\menu_thick_border_bottom.dds\">\n        <Property key=\"Pointer\" value=\"vresize\"/>\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_R\" texture=\"textures\\menu_thick_border_right.dds\">\n        <Property key=\"Pointer\" value=\"hresize\"/>\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_T\" texture=\"textures\\menu_thick_border_top.dds\">\n        <Property key=\"Pointer\" value=\"vresize\"/>\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_L\" texture=\"textures\\menu_thick_border_left.dds\">\n        <Property key=\"Pointer\" value=\"hresize\"/>\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Window border corners -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_BR\" texture=\"textures\\menu_thick_border_bottom_right_corner.dds\">\n        <Property key=\"Pointer\" value=\"dresize\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_BL\" texture=\"textures\\menu_thick_border_bottom_left_corner.dds\">\n        <Property key=\"Pointer\" value=\"dresize2\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_TR\" texture=\"textures\\menu_thick_border_top_right_corner.dds\">\n        <Property key=\"Pointer\" value=\"dresize2\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_TL\" texture=\"textures\\menu_thick_border_top_left_corner.dds\">\n        <Property key=\"Pointer\" value=\"dresize\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- Expanded border corners, to get larger diagonal corner pointer area -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_TL_T\" texture=\"textures\\menu_thick_border_top.dds\">\n        <Property key=\"Pointer\" value=\"dresize\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_TL_B\" texture=\"textures\\menu_thick_border_left.dds\">\n        <Property key=\"Pointer\" value=\"dresize\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_TR_T\" texture=\"textures\\menu_thick_border_top.dds\">\n        <Property key=\"Pointer\" value=\"dresize2\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_TR_B\" texture=\"textures\\menu_thick_border_right.dds\">\n        <Property key=\"Pointer\" value=\"dresize2\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_BL_T\" texture=\"textures\\menu_thick_border_left.dds\">\n        <Property key=\"Pointer\" value=\"dresize2\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_BL_B\" texture=\"textures\\menu_thick_border_bottom.dds\">\n        <Property key=\"Pointer\" value=\"dresize2\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_BR_T\" texture=\"textures\\menu_thick_border_right.dds\">\n        <Property key=\"Pointer\" value=\"dresize\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"TB_BR_B\" texture=\"textures\\menu_thick_border_bottom.dds\">\n        <Property key=\"Pointer\" value=\"dresize\"/>\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <!-- These parts defines the 'blocks' to the left and right of the caption -->\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_MID\" texture=\"textures\\menu_head_block_middle.dds\">\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_TOP\" texture=\"textures\\menu_head_block_top.dds\">\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_BOTTOM\" texture=\"textures\\menu_head_block_bottom.dds\">\n        <BasisSkin type=\"TileRect\" align=\"Stretch\">\n            <State name=\"normal\">\n                <Property key=\"TileH\" value=\"true\"/>\n                <Property key=\"TileV\" value=\"true\"/>\n            </State>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_LEFT\" texture=\"textures\\menu_head_block_left.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_RIGHT\" texture=\"textures\\menu_head_block_right.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_TR\" texture=\"textures\\menu_head_block_top_right_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_TL\" texture=\"textures\\menu_head_block_top_left_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_BR\" texture=\"textures\\menu_head_block_bottom_right_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n    <Resource type=\"AutoSizedResourceSkin\" name=\"HB_BL\" texture=\"textures\\menu_head_block_bottom_left_corner.dds\">\n        <BasisSkin type=\"MainSkin\">\n            <State name=\"normal\"/>\n        </BasisSkin>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"HB_ALL\" size=\"260 20\">\n        <Child type=\"Widget\" skin=\"HB_MID\" offset=\"2 2 256 16\" align=\"Top Left HStretch\"/>\n        <Child type=\"Widget\" skin=\"HB_TOP\" offset=\"2 0 256 2\" align=\"Top HStretch HStretch\"/>\n        <Child type=\"Widget\" skin=\"HB_BOTTOM\" offset=\"2 18 256 2\" align=\"Bottom HStretch\"/>\n        <Child type=\"Widget\" skin=\"HB_LEFT\" offset=\"0 2 2 16\" align=\"Top Left\"/>\n        <Child type=\"Widget\" skin=\"HB_RIGHT\" offset=\"258 2 2 16\" align=\"Top Right\"/>\n        <Child type=\"Widget\" skin=\"HB_TR\" offset=\"258 0 2 2\" align=\"Top Right\"/>\n        <Child type=\"Widget\" skin=\"HB_TL\" offset=\"0 0 2 2\" align=\"Top Left\"/>\n        <Child type=\"Widget\" skin=\"HB_BR\" offset=\"258 18 2 2\" align=\"Bottom Right\"/>\n        <Child type=\"Widget\" skin=\"HB_BL\" offset=\"0 18 2 2\" align=\"Bottom Left\"/>\n    </Resource>\n\n    <!-- The actual caption. It contains the edges of the blocks on\n         its sides as well -->\n    <Resource type=\"ResourceSkin\" name=\"MW_Caption\" size=\"88 20\">\n        <Property key=\"TextAlign\" value=\"Center\"/>\n\n        <Child type=\"Widget\" skin=\"HB_ALL\" offset=\"0 0 30 20\" align=\"Default\" name=\"Left\"/>\n        <Child type=\"TextBox\" skin=\"SandText\" offset=\"30 0 28 20\" align=\"Left VStretch\" name=\"Client\"/>\n        <Child type=\"Widget\" skin=\"HB_ALL\" offset=\"0 0 30 20\" align=\"Right\" name=\"Right\"/>\n    </Resource>\n\n<!-- ----------------------------------------------------\n\n                 WINDOW DEFINITION\n\n------------------------------------------------------ -->\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Window\" size=\"256 256\">\n        <Property key=\"TextAlign\" value=\"Center\"/>\n        <Property key=\"Snap\" value=\"true\"/>\n        <Property key=\"MinSize\" value=\"64 64\"/>\n\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"0 0 256 256\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"\" offset=\"8 28 240 220\" align=\"Stretch\" name=\"Client\"/>\n\n        <!-- Outer Borders -->\n        <Child type=\"Widget\" skin=\"TB_T\" offset=\"14 0 228 4\" align=\"Top HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 0 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_L\" offset=\"0 14 4 228\" align=\"Left VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_R\" offset=\"252 14 4 228\" align=\"Right VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_B\" offset=\"14 252 228 4\" align=\"Bottom HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 0 1\"/>\n        </Child>\n\n        <!-- OB: Top Left -->\n        <Child type=\"Widget\" skin=\"TB_TL_T\" offset=\"4 0 10 4\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL\" offset=\"0 0 4 4\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL_B\" offset=\"0 4 4 10\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n\n        <!-- OB: Top Right -->\n        <Child type=\"Widget\" skin=\"TB_TR_T\" offset=\"242 0 10 4\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR\" offset=\"252 0 4 4\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR_B\" offset=\"252 4 4 10\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n\n        <!-- OB: Bottom Left -->\n        <Child type=\"Widget\" skin=\"TB_BL_T\" offset=\"0 242 4 10\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL\" offset=\"0 252 4 4\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL_B\" offset=\"4 252 10 4\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n\n        <!-- OB: Bottom Right -->\n        <Child type=\"Widget\" skin=\"TB_BR_T\" offset=\"252 242 4 10\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR\" offset=\"252 252 4 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR_B\" offset=\"242 252 10 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n\n        <!-- Inner Borders -->\n        <Child type=\"Widget\" skin=\"TB_T\" offset=\"18 24 220 4\" align=\"Top HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 0 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_L\" offset=\"4 38 4 200\" align=\"Left VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_R\" offset=\"248 38 4 200\" align=\"Right VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_B\" offset=\"18 248 220 4\" align=\"Bottom HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 0 1\"/>\n        </Child>\n\n        <!-- IB: Top Left -->\n        <Child type=\"Widget\" skin=\"TB_TL_T\" offset=\"8 24 10 4\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL\" offset=\"4 24 4 4\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL_B\" offset=\"4 28 4 10\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n\n        <!-- IB: Top Right -->\n        <Child type=\"Widget\" skin=\"TB_TR_T\" offset=\"238 24 10 4\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR\" offset=\"248 24 4 4\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR_B\" offset=\"248 28 4 10\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n\n        <!-- IB: Bottom Left -->\n        <Child type=\"Widget\" skin=\"TB_BL_T\" offset=\"4 238 4 10\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL\" offset=\"4 248 4 4\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL_B\" offset=\"8 248 10 4\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n\n        <!-- IB: Bottom Right -->\n        <Child type=\"Widget\" skin=\"TB_BR_T\" offset=\"248 238 4 10\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR\" offset=\"248 248 4 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR_B\" offset=\"238 248 10 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n\n        <Child type=\"WindowCaption\" skin=\"MW_Caption\" offset=\"4 4 248 20\" align=\"HStretch Top\" name=\"Caption\">\n        </Child>\n\n        <!-- This invisible button makes it possible to move the\n             window by dragging the caption. -->\n        <Child type=\"Button\" offset=\"4 4 248 20\" align=\"HStretch Top\" name=\"Action\">\n            <Property key=\"SupportDoubleClick\" value=\"1\"/>\n            <Property key=\"Scale\" value=\"1 1 0 0\"/>\n        </Child>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Window_NoCaption\" size=\"256 256\">\n        <Property key=\"TextAlign\" value=\"Center\"/>\n        <Property key=\"Snap\" value=\"true\"/>\n        <Property key=\"MinSize\" value=\"64 64\"/>\n\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\" 0 0 256 256\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"\" offset=\"8 28 240 220\" align=\"Stretch\" name=\"Client\"/>\n\n        <!-- Outer Borders -->\n        <Child type=\"Widget\" skin=\"TB_T\" offset=\"14 0 228 4\" align=\"Top HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 0 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_L\" offset=\"0 14 4 228\" align=\"Left VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_R\" offset=\"252 14 4 228\" align=\"Right VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_B\" offset=\"14 252 228 4\" align=\"Bottom HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 0 1\"/>\n        </Child>\n\n        <!-- OB: Top Left -->\n        <Child type=\"Widget\" skin=\"TB_TL_T\" offset=\"4 0 10 4\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL\" offset=\"0 0 4 4\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL_B\" offset=\"0 4 4 10\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n\n        <!-- OB: Top Right -->\n        <Child type=\"Widget\" skin=\"TB_TR_T\" offset=\"242 0 10 4\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR\" offset=\"252 0 4 4\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR_B\" offset=\"252 4 4 10\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n\n        <!-- OB: Bottom Left -->\n        <Child type=\"Widget\" skin=\"TB_BL_T\" offset=\"0 242 4 10\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL\" offset=\"0 252 4 4\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL_B\" offset=\"4 252 10 4\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n\n        <!-- OB: Bottom Right -->\n        <Child type=\"Widget\" skin=\"TB_BR_T\" offset=\"252 242 4 10\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR\" offset=\"252 252 4 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR_B\" offset=\"242 252 10 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n\n        <!-- Inner Borders -->\n        <Child type=\"Widget\" skin=\"TB_T\" offset=\"18 24 220 4\" align=\"Top HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 0 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_L\" offset=\"4 38 4 200\" align=\"Left VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_R\" offset=\"248 38 4 200\" align=\"Right VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_B\" offset=\"18 248 220 4\" align=\"Bottom HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 0 1\"/>\n        </Child>\n\n        <!-- IB: Top Left -->\n        <Child type=\"Widget\" skin=\"TB_TL_T\" offset=\"8 24 10 4\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL\" offset=\"4 24 4 4\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL_B\" offset=\"4 28 4 10\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n\n        <!-- IB: Top Right -->\n        <Child type=\"Widget\" skin=\"TB_TR_T\" offset=\"238 24 10 4\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR\" offset=\"248 24 4 4\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR_B\" offset=\"248 28 4 10\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n\n        <!-- IB: Bottom Left -->\n        <Child type=\"Widget\" skin=\"TB_BL_T\" offset=\"4 238 4 10\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL\" offset=\"4 248 4 4\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL_B\" offset=\"8 248 10 4\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n\n        <!-- IB: Bottom Right -->\n        <Child type=\"Widget\" skin=\"TB_BR_T\" offset=\"248 238 4 10\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR\" offset=\"248 248 4 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR_B\" offset=\"238 248 10 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n\n        <!-- Caption -->\n        <Child type=\"Widget\" skin=\"HB_ALL\" offset=\"4 4 248 20\" align=\"Top HStretch\">\n            <Property key=\"Scale\" value=\"1 1 0 0\"/>\n        </Child>\n\n        <!-- This invisible button makes it possible to move the\n             window by dragging the caption. -->\n        <Child type=\"Button\" offset=\"4 4 248 20\" align=\"HStretch Top\" name=\"Action\">\n            <Property key=\"SupportDoubleClick\" value=\"1\"/>\n            <Property key=\"Scale\" value=\"1 1 0 0\"/>\n        </Child>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Window_Pinnable\" size=\"256 256\">\n        <Property key=\"TextAlign\" value=\"Center\"/>\n        <Property key=\"Snap\" value=\"true\"/>\n        <Property key=\"MinSize\" value=\"64 64\"/>\n\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\" 0 0 256 256\" align=\"Stretch\"/>\n        <Child type=\"Widget\" skin=\"\" offset=\"8 28 240 220\" align=\"Stretch\" name=\"Client\"/>\n\n        <!-- Outer Borders -->\n        <Child type=\"Widget\" skin=\"TB_T\" offset=\"14 0 228 4\" align=\"Top HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 0 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_L\" offset=\"0 14 4 228\" align=\"Left VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_R\" offset=\"252 14 4 228\" align=\"Right VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_B\" offset=\"14 252 228 4\" align=\"Bottom HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 0 1\"/>\n        </Child>\n\n        <!-- OB: Top Left -->\n        <Child type=\"Widget\" skin=\"TB_TL_T\" offset=\"4 0 10 4\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL\" offset=\"0 0 4 4\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL_B\" offset=\"0 4 4 10\" align=\"Left Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n\n        <!-- OB: Top Right -->\n        <Child type=\"Widget\" skin=\"TB_TR_T\" offset=\"242 0 10 4\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR\" offset=\"252 0 4 4\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR_B\" offset=\"252 4 4 10\" align=\"Right Top\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n\n        <!-- OB: Bottom Left -->\n        <Child type=\"Widget\" skin=\"TB_BL_T\" offset=\"0 242 4 10\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL\" offset=\"0 252 4 4\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL_B\" offset=\"4 252 10 4\" align=\"Left Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n\n        <!-- OB: Bottom Right -->\n        <Child type=\"Widget\" skin=\"TB_BR_T\" offset=\"252 242 4 10\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR\" offset=\"252 252 4 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR_B\" offset=\"242 252 10 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n\n        <!-- Inner Borders -->\n        <Child type=\"Widget\" skin=\"TB_T\" offset=\"18 24 220 4\" align=\"Top HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 0 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_L\" offset=\"4 38 4 200\" align=\"Left VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_R\" offset=\"248 38 4 200\" align=\"Right VStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_B\" offset=\"18 248 220 4\" align=\"Bottom HStretch\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 0 1\"/>\n        </Child>\n\n        <!-- IB: Top Left -->\n        <Child type=\"Widget\" skin=\"TB_TL_T\" offset=\"8 24 10 4\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL\" offset=\"4 24 4 4\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TL_B\" offset=\"4 28 4 10\" align=\"Top Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n\n        <!-- IB: Top Right -->\n        <Child type=\"Widget\" skin=\"TB_TR_T\" offset=\"238 24 10 4\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR\" offset=\"248 24 4 4\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_TR_B\" offset=\"248 28 4 10\" align=\"Top Right\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n\n        <!-- IB: Bottom Left -->\n        <Child type=\"Widget\" skin=\"TB_BL_T\" offset=\"4 238 4 10\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL\" offset=\"4 248 4 4\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BL_B\" offset=\"8 248 10 4\" align=\"Bottom Left\" name=\"Action\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n\n        <!-- IB: Bottom Right -->\n        <Child type=\"Widget\" skin=\"TB_BR_T\" offset=\"248 238 4 10\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR\" offset=\"248 248 4 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"TB_BR_B\" offset=\"238 248 10 4\" align=\"Right Bottom\" name=\"Action\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n\n        <!-- Caption -->\n        <Child type=\"WindowCaption\" skin=\"MW_Caption_Pin\" offset=\"4 4 248 20\" align=\"HStretch Top\" name=\"Caption\">\n        </Child>\n\n        <!-- This invisible button makes it possible to move the\n             window by dragging the caption. -->\n        <Child type=\"Button\" offset=\"4 4 248 20\" align=\"HStretch Top\" name=\"Action\">\n            <Property key=\"SupportDoubleClick\" value=\"1\"/>\n            <Property key=\"Scale\" value=\"1 1 0 0\"/>\n        </Child>\n\n        <Child type=\"Button\" skin=\"PinUp\" offset=\"232 4 19 19\" align=\"Right Top\" name=\"Button\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_Dialog\" size=\"256 54\">\n        <Child type=\"Widget\" skin=\"BlackBG\" offset=\"4 4 248 46\" align=\"Stretch\" name=\"Client\"/>\n\n        <!-- Outer borders -->\n        <Child type=\"Widget\" skin=\"DB_T\" offset=\"4 0 248 4\" align=\"Top HStretch\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 1 0 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_L\" offset=\"0 4 4 46\" align=\"Left VStretch\" name=\"Border\">\n            <Property key=\"Scale\" value=\"1 0 -1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_B\" offset=\"4 50 248 4\" align=\"Bottom HStretch\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 0 0 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_R\" offset=\"252 4 4 46\" align=\"Right VStretch\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 0 1 0\"/>\n        </Child>\n\n        <Child type=\"Widget\" skin=\"DB_BR\" offset=\"252 50 4 4\" align=\"Right Bottom\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_BL\" offset=\"0 50 4 4\" align=\"Left Bottom\" name=\"Border\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_TR\" offset=\"252 0 4 4\" align=\"Right Top\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_TL\" offset=\"0 0 4 4\" align=\"Left Top\" name=\"Border\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"MW_DialogNoTransp\" size=\"256 54\">\n        <Child type=\"Widget\" skin=\"FullBlackBG\" offset=\"4 4 248 46\" align=\"Stretch\" name=\"Client\"/>\n\n        <!-- Outer borders -->\n        <Child type=\"Widget\" skin=\"DB_T\" offset=\"4 0 248 4\" align=\"Top HStretch\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 1 0 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_L\" offset=\"0 4 4 46\" align=\"Left VStretch\" name=\"Border\">\n            <Property key=\"Scale\" value=\"1 0 -1 0\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_B\" offset=\"4 50 248 4\" align=\"Bottom HStretch\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 0 0 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_R\" offset=\"252 4 4 46\" align=\"Right VStretch\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 0 1 0\"/>\n        </Child>\n\n        <Child type=\"Widget\" skin=\"DB_BR\" offset=\"252 50 4 4\" align=\"Right Bottom\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 0 1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_BL\" offset=\"0 50 4 4\" align=\"Left Bottom\" name=\"Border\">\n            <Property key=\"Scale\" value=\"1 0 -1 1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_TR\" offset=\"252 0 4 4\" align=\"Right Top\" name=\"Border\">\n            <Property key=\"Scale\" value=\"0 1 1 -1\"/>\n        </Child>\n        <Child type=\"Widget\" skin=\"DB_TL\" offset=\"0 0 4 4\" align=\"Left Top\" name=\"Border\">\n            <Property key=\"Scale\" value=\"1 1 -1 -1\"/>\n        </Child>\n    </Resource>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/skins.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI>\n    <MyGUI type=\"List\">\n        <List file=\"core.skin\"/>\n        <List file=\"openmw_resources.xml\"/>\n        <List file=\"openmw_font.xml\"/>\n        <List file=\"openmw_text.skin.xml\"/>\n        <List file=\"openmw_windows.skin.xml\"/>\n        <List file=\"openmw_button.skin.xml\"/>\n        <List file=\"openmw_list.skin.xml\"/>\n        <List file=\"openmw_edit.skin.xml\"/>\n        <List file=\"openmw_box.skin.xml\"/>\n        <List file=\"openmw_progress.skin.xml\"/>\n        <List file=\"openmw_hud_energybar.skin.xml\"/>\n        <List file=\"openmw_hud_box.skin.xml\"/>\n        <List file=\"openmw_mainmenu.skin.xml\"/>\n        <List file=\"openmw_console.skin.xml\"/>\n        <List file=\"openmw_journal.skin.xml\"/>\n        <List file=\"openmw_map_window.skin.xml\"/>\n        <List file=\"openmw_dialogue_window.skin.xml\"/>\n        <List file=\"openmw_scroll.skin.xml\"/>\n        <List file=\"openmw_debug_window.skin.xml\" />\n        <List file=\"tes3mp_chat.skin.xml\" />\n    </MyGUI>\n</MyGUI>\n\n"
  },
  {
    "path": "files/mygui/tes3mp_chat.layout",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\">\n  <Widget type=\"Window\" skin=\"MW_Window\" position=\"0 0 400 400\" layer=\"Windows\" name=\"_Main\">\n    <Property key=\"Caption\" value=\"#{sConsoleTitle}\"/>\n    <Property key=\"MinSize\" value=\"40 40\"/>\n    <Property key=\"Visible\" value=\"false\"/>\n\n    <!-- Log window -->\n    <Widget type=\"EditBox\" skin=\"MW_TextBoxEdit\" position=\"5 5 380 328\" align=\"Stretch\" name=\"list_History\">\n        <Property key=\"MultiLine\" value=\"1\"/>\n        <Property key=\"ReadOnly\" value=\"true\"/>\n        <Property key=\"FontName\" value=\"Russo\"/>\n        <Property key=\"TextAlign\" value=\"Left Top\"/>\n        <Property key=\"TextColour\" value=\"1 1 1\"/>\n        <Property key=\"InvertSelected\" value=\"false\"/>\n        <Property key=\"WordWrap\" value=\"true\"/>\n    </Widget>\n\n    <!-- Command line -->\n    <Widget type=\"EditBox\" skin=\"TES3MP_ChatMsg\" position=\"0 338 384 28\" align=\"HStretch Bottom\" name=\"edit_Command\">\n        <Property key=\"InvertSelected\" value=\"false\"/>\n        <UserString key=\"AcceptTab\" value=\"true\"/>\n    </Widget>\n\n  </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/tes3mp_chat.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Resource\" version=\"1.1\">\n\n\n    <!-- Console Input -->\n\n    <Resource type=\"ResourceSkin\" name=\"TES3MP_EditClient\" size=\"10 10\">\n        <Property key=\"FontName\" value=\"Russo\"/>\n        <Property key=\"TextAlign\" value=\"Left VCenter\"/>\n        <Property key=\"TextColour\" value=\"1 1 1\"/>\n        <BasisSkin type=\"EditText\" offset=\"0 0 10 10\" align=\"Stretch\"/>\n    </Resource>\n\n    <Resource type=\"ResourceSkin\" name=\"TES3MP_ChatMsg\" size=\"29 28\">\n        <Child type=\"Widget\" skin=\"MW_Box\" offset=\"0 0 29 26\" align=\"Bottom Stretch\"/>\n        <Child type=\"TextBox\" skin=\"TES3MP_EditClient\" offset=\"4 2 19 22\" align=\"Bottom Stretch\" name=\"Client\"/>\n    </Resource>\n\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/tes3mp_dialog_list.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" layer=\"Windows\" position=\"0 0 400 400\" name=\"_Main\">\n        <Property key=\"Visible\" value=\"false\"/>\n\n        <Widget type=\"EditBox\" skin=\"MW_TextEditClient\" position=\"13 8 368 48\" name=\"Message\" align=\"Center Top\">\n            <Property key=\"FontName\" value=\"Default\"/>\n            <Property key=\"TextAlign\" value=\"Top HCenter\"/>\n            <Property key=\"Static\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n            <Property key=\"MultiLine\" value=\"true\"/>\n        </Widget>\n\n        <Widget type=\"ListBox\" skin=\"MW_List\" position=\"13 56 368 290\" align=\"Left Top\" name=\"ListBox\"/>\n\n        <Widget type=\"VBox\" position=\"0 362 392 24\" align=\"Right Bottom\">\n            <Widget type=\"HBox\">\n                <Property key=\"Spacing\" value=\"8\"/>\n\n                <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OkButton\" align=\"Right Bottom\">\n                    <Property key=\"Caption\" value=\"OK\"/>\n                </Widget>\n            </Widget>\n\n            <Widget type=\"Widget\"/>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/tes3mp_login.layout",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI type=\"Layout\" version=\"3.2.0\">\n    <Widget type=\"Window\" skin=\"MW_Dialog\" position=\"0 0 600 600\" layer=\"Windows\" name=\"_Main\">\n        <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"6 6 300 18\" align=\"Left Top\" name=\"LabelName\">\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n            <Property key=\"Caption\" value=\"Login\"/>\n        </Widget>\n        <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"6 28 300 30\" align=\"Left Top\" name=\"EditLogin\"/>\n        <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"6 58 300 18\" align=\"Left Top\" name=\"LabelServer\">\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n            <Property key=\"Caption\" value=\"Server address and port\"/>\n        </Widget>\n        <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"6 80 300 30\" align=\"Left Top\" name=\"EditServer\"/>\n        <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"306 80 80 30\" align=\"Left Top\" name=\"EditPort\"/>\n\n        <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" position=\"540 560 42 23\" name=\"ButtonConnect\">\n            <Property key=\"ExpandDirection\" value=\"Left\"/>\n            <Property key=\"Caption\" value=\"Connect\"/>\n        </Widget>\n\n    </Widget>\n    <CodeGeneratorSettings/>\n</MyGUI>\n"
  },
  {
    "path": "files/mygui/tes3mp_login.skin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MyGUI>\n\n</MyGUI>"
  },
  {
    "path": "files/mygui/tes3mp_text_input.layout",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<MyGUI type=\"Layout\">\n    <Widget type=\"VBox\" skin=\"HUD_Box_NoTransp\" layer=\"Windows\" align=\"Center\" name=\"_Main\">\n        <Property key=\"AutoResize\" value=\"true\"/>\n        <Property key=\"Padding\" value=\"10\"/>\n        <Property key=\"Spacing\" value=\"12\"/>\n\n        <!-- Appearance -->\n        <Widget type=\"TextBox\" skin=\"ProgressText\" position=\"6 6 300 18\" name=\"LabelT\" align=\"Left Top\">\n            <Property key=\"TextAlign\" value=\"Left Top\"/>\n        </Widget>\n        <Widget type=\"EditBox\" skin=\"MW_TextEdit\" position=\"6 28 300 30\" name=\"TextEdit\" align=\"Left Top\"/>\n\n        <!-- Dialog buttons -->\n        <Widget type=\"HBox\" skin=\"\">\n            <UserString key=\"HStretch\" value=\"true\"/>\n            <Widget type=\"Widget\">\n                <UserString key=\"HStretch\" value=\"true\"/>\n            </Widget>\n\n            <Widget type=\"AutoSizedButton\" skin=\"MW_Button\" name=\"OKButton\">\n                <Property key=\"Caption\" value=\"OK\"/>\n            </Widget>\n        </Widget>\n\n        <Widget type=\"AutoSizedEditBox\" skin=\"HeaderText\" position=\"6 96 300 0\" name=\"TextNote\" align=\"Left Top\">\n            <Property key=\"TextAlign\" value=\"Center\"/>\n            <Property key=\"MultiLine\" value=\"true\"/>\n            <Property key=\"WordWrap\" value=\"true\"/>\n        </Widget>\n    </Widget>\n</MyGUI>\n"
  },
  {
    "path": "files/opencs/raster/scene-exterior-parts/composite_the_icons.sh",
    "content": "#!/bin/bash\n\n# Run this from the \"parts\" directory to composite the final 32 icons\n# Also creates status (masked) variants of the icons while at it\n\nset -e\n\nmkdir -p status\ni=0\n\nfor grid in '' '*_grid*'; do\nfor arrows in '' '*_arrows*'; do\nfor cell_marker in '' '*_cell_marker*'; do\n    files=$(echo $grid $arrows $cell_marker | tr ' ' '\\n' | sort -n | tr '\\n' ' ')\n    convert *backdrop* $files *terrain* -background transparent -mosaic \\\n            -crop '23x48+25+0' *mask* -mosaic status/$i.png\n    i=$((i+1))\ndone;done;done;\n"
  },
  {
    "path": "files/opencs/raster/scene-view-parts/README",
    "content": "This directory contains the 15 layers exported from the SVG in scalable/,\nas well as a BASH+imagemagick script to composite them into the final 32 icons.\n\nIf you edit the SVG (Inkscape is strongly recommended for editing it), export\nthe layers into raster overwriting these files and run the script to assemble\nthe final icons.\n\nEnjoy my icons!\n--\nSergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
  },
  {
    "path": "files/opencs/raster/scene-view-parts/composite_the_32_icons.sh",
    "content": "#!/bin/bash\n\n# Run this from the \"parts\" directory to composite the final 32 icons\n# Also creates status (masked) variants of the icons while at it\n\nset -e\n\nmkdir -p composited\nmkdir -p status\ni=0\n\nfor terrain in '' '*_terrain_*'; do\nfor fog in '' '*_fog_*'; do\nfor water in '' '*_water_*'; do\nfor pathgrid in '' '*_pathgrid*'; do\nfor references in '' '*_bridge*'; do\n    files=$(echo $water $terrain $references $fog $pathgrid | tr ' ' '\\n' | sort -n | tr '\\n' ' ')\n    convert *sky* $files -layers flatten composited/$i.png\n    convert *sky* $files *mask* -layers flatten status/$i.png\n    i=$((i+1))\ndone;done;done;done;done"
  },
  {
    "path": "files/opencs/resources.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/\">\n        <file>openmw-cs.png</file>\n        <file>activator.png</file>\n        <file alias=\"list-added\">list-added.png</file>\n        <file>apparatus.png</file>\n        <file>armor.png</file>\n        <file>attribute.png</file>\n        <file>list-base.png</file>\n        <file>birthsign.png</file>\n        <file>body-part.png</file>\n        <file>book.png</file>\n        <file>cell.png</file>\n        <file>class.png</file>\n        <file>clothing.png</file>\n        <file>container.png</file>\n        <file>creature.png</file>\n        <file>debug-profile.png</file>\n        <file>dialogue-info.png</file>\n        <file>dialogue-journal.png</file>\n        <file>dialogue-regular.png</file>\n        <file>dialogue-greeting.png</file>\n        <file>dialogue-persuasion.png</file>\n        <file>dialogue-voice.png</file>\n        <file>dialogue-topics.png</file>\n        <file>dialogue-topic-infos.png</file>\n        <file>journal-topic-infos.png</file>\n        <file>journal-topics.png</file>\n        <file>door.png</file>\n        <file>enchantment.png</file>\n        <file>error-log.png</file>\n        <file>faction.png</file>\n        <file>filter.png</file>\n        <file>global-variable.png</file>\n        <file>gmst.png</file>\n        <file>info.png</file>\n        <file>ingredient.png</file>\n        <file>instance.png</file>\n        <file>land-heightmap.png</file>\n        <file>land-texture.png</file>\n        <file>levelled-creature.png</file>\n        <file>levelled-item.png</file>\n        <file>light.png</file>\n        <file>lockpick.png</file>\n        <file>magic-effect.png</file>\n        <file>magicrabbit.png</file>\n        <file>map.png</file>\n        <file>probe.png</file>\n        <file>menu-close.png</file>\n        <file>menu-exit.png</file>\n        <file>menu-new-addon.png</file>\n        <file>menu-new-game.png</file>\n        <file>menu-new-window.png</file>\n        <file>menu-merge.png</file>\n        <file>menu-open.png</file>\n        <file>menu-preferences.png</file>\n        <file>menu-reload.png</file>\n        <file>menu-redo.png</file>\n        <file>menu-save.png</file>\n        <file>menu-search.png</file>\n        <file>menu-status-bar.png</file>\n        <file>menu-undo.png</file>\n        <file>menu-verify.png</file>\n        <file>metadata.png</file>\n        <file>miscellaneous.png</file>\n        <file alias=\"list-modified\">list-modified.png</file>\n        <file>npc.png</file>\n        <file>object.png</file>\n        <file>pathgrid.png</file>\n        <file>potion.png</file>\n        <file>qt.png</file>\n        <file>race.png</file>\n        <file>random-item.png</file>\n        <file>random.png</file>\n        <file alias=\"list-removed\">list-removed.png</file>\n        <file>region.png</file>\n        <file>region-map.png</file>\n        <file>repair.png</file>\n        <file>run-log.png</file>\n        <file>run-openmw.png</file>\n        <file>scene.png</file>\n        <file>script.png</file>\n        <file>skill.png</file>\n        <file>start-script.png</file>\n        <file>stop-openmw.png</file>\n        <file>sound-generator.png</file>\n        <file>sound.png</file>\n        <file>spell.png</file>\n        <file>static.png</file>\n        <file>weapon.png</file>\n        <file>multitype.png</file>\n        <file alias=\"record-next\">record-next.png</file>\n        <file alias=\"record-previous\">record-previous.png</file>\n        <file alias=\"record-down\">record-down.png</file>\n        <file alias=\"record-up\">record-up.png</file>\n        <file alias=\"edit-delete\">record-delete.png</file>\n        <file alias=\"edit-preview\">record-preview.png</file>\n        <file alias=\"edit-clone\">record-clone.png</file>\n        <file alias=\"edit-add\">record-add.png</file>\n        <file alias=\"edit-edit\">record-edit.png</file>\n        <file alias=\"edit-touch\">record-touch.png</file>\n        <file alias=\"edit-undo\">record-revert.png</file>\n        <file alias=\"resources-icon\">resources-icon.png</file>\n        <file alias=\"resources-mesh\">resources-mesh.png</file>\n        <file alias=\"resources-music\">resources-music.png</file>\n        <file alias=\"resources-sound\">resources-sound.png</file>\n        <file alias=\"resources-texture\">resources-texture.png</file>\n        <file alias=\"resources-video\">resources-video.png</file>\n        <file alias=\"placeholder\">placeholder.png</file>\n    </qresource>\n    <qresource prefix=\"/startup\">\n        <file alias=\"create-addon\">raster/startup/big/create-addon.png</file>\n        <file alias=\"create-game\">raster/startup/big/new-game.png</file>\n        <file alias=\"edit-content\">raster/startup/big/edit-content.png</file>\n        <file alias=\"configure\">raster/startup/small/configure.png</file>\n    </qresource>\n    <qresource prefix=\"/scenetoolbar\">\n        <file alias=\"night\">lighting-moon.png</file>\n        <file alias=\"day\">lighting-sun.png</file>\n        <file alias=\"bright\">lighting-lamp.png</file>\n        <file alias=\"1st-person\">camera-first-person.png</file>\n        <file alias=\"free-camera\">camera-free.png</file>\n        <file alias=\"orbiting-camera\">camera-orbit.png</file>\n        <file alias=\"play\">run-game.png</file>\n        <file alias=\"scene-view-1\">scene-view-instance.png</file>\n        <file alias=\"scene-view-16\">scene-view-terrain.png</file>\n        <file alias=\"scene-view-4\">scene-view-water.png</file>\n        <file alias=\"scene-view-2\">scene-view-pathgrid.png</file>\n        <file alias=\"scene-view-8\">scene-view-fog.png</file>\n        <file alias=\"scene-view-c0\">scene-view-status-0.png</file>\n        <file alias=\"scene-view-c1\">scene-view-status-1.png</file>\n        <file alias=\"scene-view-c2\">scene-view-status-2.png</file>\n        <file alias=\"scene-view-c3\">scene-view-status-3.png</file>\n        <file alias=\"scene-view-c4\">scene-view-status-4.png</file>\n        <file alias=\"scene-view-c5\">scene-view-status-5.png</file>\n        <file alias=\"scene-view-c6\">scene-view-status-6.png</file>\n        <file alias=\"scene-view-c7\">scene-view-status-7.png</file>\n        <file alias=\"scene-view-c8\">scene-view-status-8.png</file>\n        <file alias=\"scene-view-c9\">scene-view-status-9.png</file>\n        <file alias=\"scene-view-c10\">scene-view-status-10.png</file>\n        <file alias=\"scene-view-c11\">scene-view-status-11.png</file>\n        <file alias=\"scene-view-c12\">scene-view-status-12.png</file>\n        <file alias=\"scene-view-c13\">scene-view-status-13.png</file>\n        <file alias=\"scene-view-c14\">scene-view-status-14.png</file>\n        <file alias=\"scene-view-c15\">scene-view-status-15.png</file>\n        <file alias=\"scene-view-c16\">scene-view-status-16.png</file>\n        <file alias=\"scene-view-c17\">scene-view-status-17.png</file>\n        <file alias=\"scene-view-c18\">scene-view-status-18.png</file>\n        <file alias=\"scene-view-c19\">scene-view-status-19.png</file>\n        <file alias=\"scene-view-c20\">scene-view-status-20.png</file>\n        <file alias=\"scene-view-c21\">scene-view-status-21.png</file>\n        <file alias=\"scene-view-c22\">scene-view-status-22.png</file>\n        <file alias=\"scene-view-c23\">scene-view-status-23.png</file>\n        <file alias=\"scene-view-c24\">scene-view-status-24.png</file>\n        <file alias=\"scene-view-c25\">scene-view-status-25.png</file>\n        <file alias=\"scene-view-c26\">scene-view-status-26.png</file>\n        <file alias=\"scene-view-c27\">scene-view-status-27.png</file>\n        <file alias=\"scene-view-c28\">scene-view-status-28.png</file>\n        <file alias=\"scene-view-c29\">scene-view-status-29.png</file>\n        <file alias=\"scene-view-c30\">scene-view-status-30.png</file>\n        <file alias=\"scene-view-c31\">scene-view-status-31.png</file>\n        <file alias=\"scene-view-marker-2\">scene-exterior-arrows.png</file>\n        <file alias=\"scene-view-marker-4\">scene-exterior-borders.png</file>\n        <file alias=\"scene-view-marker-1\">scene-exterior-markers.png</file>\n        <file alias=\"scene-view-marker-c0\">scene-exterior-status-0.png</file>\n        <file alias=\"scene-view-marker-c1\">scene-exterior-status-1.png</file>\n        <file alias=\"scene-view-marker-c2\">scene-exterior-status-2.png</file>\n        <file alias=\"scene-view-marker-c3\">scene-exterior-status-3.png</file>\n        <file alias=\"scene-view-marker-c4\">scene-exterior-status-4.png</file>\n        <file alias=\"scene-view-marker-c5\">scene-exterior-status-5.png</file>\n        <file alias=\"scene-view-marker-c6\">scene-exterior-status-6.png</file>\n        <file alias=\"scene-view-marker-c7\">scene-exterior-status-7.png</file>\n        <file alias=\"editing-instance\">editing-instance.png</file>\n        <file alias=\"editing-pathgrid\">editing-pathgrid.png</file>\n        <file alias=\"editing-terrain-movement\">editing-terrain-movement.png</file>\n        <file alias=\"editing-terrain-shape\">editing-terrain-shape.png</file>\n        <file alias=\"editing-terrain-texture\">editing-terrain-texture.png</file>\n        <file alias=\"editing-terrain-vertex-paint\">editing-terrain-vertex-paint.png</file>\n        <file alias=\"transform-move\">transform-move.png</file>\n        <file alias=\"transform-rotate\">transform-rotate.png</file>\n        <file alias=\"transform-scale\">transform-scale.png</file>\n        <file alias=\"selection-mode-cube\">selection-mode-cube.png</file>\n        <file alias=\"selection-mode-cube-corner\">selection-mode-cube-corner.png</file>\n        <file alias=\"selection-mode-cube-sphere\">selection-mode-cube-sphere.png</file>\n        <file alias=\"brush-point\">brush-point.png</file>\n        <file alias=\"brush-square\">brush-square.png</file>\n        <file alias=\"brush-circle\">brush-circle.png</file>\n        <file alias=\"brush-custom\">brush-custom.png</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "files/openmw-cs.cfg",
    "content": ""
  },
  {
    "path": "files/openmw.appdata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\nCopyright 2015 Alexandre Moine <nobrakal@gmail.com>\nCopyright 2020 Bret Curtis <psi29a@gmail.com>\n-->\n<component type=\"desktop\">\n <id>org.openmw.launcher.desktop</id>\n <metadata_license>CC0-1.0</metadata_license>\n <project_license>GPL-3.0-or-later</project_license>\n <name>OpenMW</name>\n <summary>Unofficial open source engine re-implementation of the game Morrowind</summary>\n <description>\n  <p>\n  OpenMW is a new engine for 2002's Game of the Year, The Elder Scrolls 3: Morrowind.\n  </p>\n  <p>\n  It aims to be a fully playable (and improved!), open source implementation of the game's engine and functionality (including mods). \n  </p>\n  <p>\n  You will still need the original game data to play OpenMW.\n  </p>\n </description>\n <launchable type=\"desktop-id\">org.openmw.launcher.desktop</launchable>\n <screenshots>\n  <screenshot type=\"default\">\n   <image>https://wiki.openmw.org/images/b/b2/Openmw_0.11.1_launcher_1.png</image>\n   <caption>The OpenMW launcher</caption>\n  </screenshot>\n  <screenshot>\n   <image>https://wiki.openmw.org/images/f/f1/Screenshot_mournhold_plaza_0.35.png</image>\n   <caption>The Mournhold's plaza on OpenMW</caption>\n  </screenshot>\n  <screenshot>\n   <image>https://wiki.openmw.org/images/5/5b/Screenshot_Vivec_seen_from_Ebonheart_0.35.png</image>\n   <caption>Vivec seen from Ebonheart on OpenMW</caption>\n  </screenshot>\n  <screenshot>\n   <image>http://wiki.openmw.org/images/a/a3/0.40_Screenshot-Balmora_3.png</image>\n   <caption>Balmora at morning on OpenMW</caption>\n  </screenshot>\n </screenshots>\n <categories>\n  <category>Game</category>\n  <category>RolePlaying</category>\n </categories>\n <releases>\n  <release version=\"@OPENMW_VERSION@\" date=\"@OPENMW_VERSION_COMMITDATE@\"/>\n </releases>\n <url type=\"homepage\">https://openmw.org</url>\n <url type=\"bugtracker\">https://gitlab.com/OpenMW/openmw/issues</url>\n <url type=\"faq\">https://openmw.org/faq/</url>\n <content_rating type=\"oars-1.1\" />\n</component>\n"
  },
  {
    "path": "files/openmw.cfg",
    "content": "# This is the global openmw.cfg file. Do not modify!\n# Modifications should be done on the user openmw.cfg file instead\n# (see: https://openmw.readthedocs.io/en/master/reference/modding/paths.html)\n\ndata=${MORROWIND_DATA_FILES}\ndata-local=\"?userdata?data\"\nresources=${OPENMW_RESOURCE_FILES}\nscript-blacklist=Museum\nscript-blacklist=MockChangeScript\nscript-blacklist=doortestwarp\nscript-blacklist=WereChange2Script\nscript-blacklist=wereDreamScript2\nscript-blacklist=wereDreamScript3\n\n# lighting\nfallback=LightAttenuation_UseConstant,0\nfallback=LightAttenuation_ConstantValue,0.0\nfallback=LightAttenuation_UseLinear,1\nfallback=LightAttenuation_LinearMethod,1\nfallback=LightAttenuation_LinearValue,3.0\nfallback=LightAttenuation_LinearRadiusMult,1.0\nfallback=LightAttenuation_UseQuadratic,0\nfallback=LightAttenuation_QuadraticMethod,2\nfallback=LightAttenuation_QuadraticValue,16.0\nfallback=LightAttenuation_QuadraticRadiusMult,1.0\nfallback=LightAttenuation_OutQuadInLin,0\n\n# inventory\nfallback=Inventory_DirectionalDiffuseR,1.0\nfallback=Inventory_DirectionalDiffuseG,1.0\nfallback=Inventory_DirectionalDiffuseB,1.0\nfallback=Inventory_DirectionalAmbientR,0.0\nfallback=Inventory_DirectionalAmbientG,0.0\nfallback=Inventory_DirectionalAmbientB,0.0\nfallback=Inventory_DirectionalRotationX,110\nfallback=Inventory_DirectionalRotationY,90\nfallback=Inventory_UniformScaling,0\n\n# water effects\nfallback=Water_Map_Alpha,0.4\nfallback=Water_World_Alpha,0.75\nfallback=Water_SurfaceTextureSize,128\nfallback=Water_SurfaceTileCount,10\nfallback=Water_SurfaceFPS,12\nfallback=Water_SurfaceTexture,water\nfallback=Water_SurfaceFrameCount,32\nfallback=Water_TileTextureDivisor,4.75\nfallback=Water_RippleTexture,ripple\nfallback=Water_RippleFrameCount,4\nfallback=Water_RippleLifetime,3.0\nfallback=Water_MaxNumberRipples,75\nfallback=Water_RippleScale,0.15, 6.5\nfallback=Water_RippleRotSpeed,0.5\nfallback=Water_RippleAlphas,0.7, 0.1, 0.01\nfallback=Water_NearWaterRadius,1000\nfallback=Water_NearWaterPoints,8\nfallback=Water_NearWaterUnderwaterFreq,0.3\nfallback=Water_NearWaterUnderwaterVolume,0.9\nfallback=Water_NearWaterIndoorTolerance,512.0\nfallback=Water_NearWaterOutdoorTolerance,1024.0\nfallback=Water_NearWaterIndoorID,Water Layer\nfallback=Water_NearWaterOutdoorID,Water Layer\nfallback=Water_UnderwaterSunriseFog,3\nfallback=Water_UnderwaterDayFog,2.5\nfallback=Water_UnderwaterSunsetFog,3\nfallback=Water_UnderwaterNightFog,4\nfallback=Water_UnderwaterIndoorFog,3\nfallback=Water_UnderwaterColor,012,030,037\nfallback=Water_UnderwaterColorWeight,0.85\n\n# fonts\nfallback=Fonts_Font_0,magic_cards_regular\nfallback=Fonts_Font_1,century_gothic_font_regular\nfallback=Fonts_Font_2,daedric_font\nfallback=FontColor_color_normal,202,165,96\nfallback=FontColor_color_normal_over,223,201,159\nfallback=FontColor_color_normal_pressed,243,237,221\nfallback=FontColor_color_active,96,112,202\nfallback=FontColor_color_active_over,159,169,223\nfallback=FontColor_color_active_pressed,223,226,244\nfallback=FontColor_color_disabled,179,168,135\nfallback=FontColor_color_disabled_over,223,201,159\nfallback=FontColor_color_disabled_pressed,243,237,221\nfallback=FontColor_color_link,112,126,207\nfallback=FontColor_color_link_over,143,155,218\nfallback=FontColor_color_link_pressed,175,184,228\nfallback=FontColor_color_journal_link,37,49,112\nfallback=FontColor_color_journal_link_over,58,77,175\nfallback=FontColor_color_journal_link_pressed,112,126,207\nfallback=FontColor_color_journal_topic,0,0,0\nfallback=FontColor_color_journal_topic_over,58,77,175\nfallback=FontColor_color_journal_topic_pressed,112,126,207\nfallback=FontColor_color_answer,150,50,30\nfallback=FontColor_color_answer_over,223,201,159\nfallback=FontColor_color_answer_pressed,243,237,221\nfallback=FontColor_color_header,223,201,159\nfallback=FontColor_color_notify,223,201,159\nfallback=FontColor_color_big_normal,202,165,96\nfallback=FontColor_color_big_normal_over,223,201,159\nfallback=FontColor_color_big_normal_pressed,243,237,221\nfallback=FontColor_color_big_link,112,126,207\nfallback=FontColor_color_big_link_over,143,155,218\nfallback=FontColor_color_big_link_pressed,175,184,228\nfallback=FontColor_color_big_answer,150,50,30\nfallback=FontColor_color_big_answer_over,223,201,159\nfallback=FontColor_color_big_answer_pressed,243,237,22\nfallback=FontColor_color_big_header,223,201,159\nfallback=FontColor_color_big_notify,223,201,159\nfallback=FontColor_color_background,0,0,0\nfallback=FontColor_color_focus,80,80,80\nfallback=FontColor_color_health,200,60,30\nfallback=FontColor_color_magic,53,69,159\nfallback=FontColor_color_fatigue,0,150,60\nfallback=FontColor_color_misc,0,205,205\nfallback=FontColor_color_weapon_fill,200,60,30\nfallback=FontColor_color_magic_fill,200,60,30\nfallback=FontColor_color_positive,223,201,159\nfallback=FontColor_color_negative,200,60,30\nfallback=FontColor_color_count,223,201,159\n\n# leveling\nfallback=Level_Up_Level2,The last few days have opened your eyes: you realize now that there is more to the life than you thought. An uncertain future awaits you.\nfallback=Level_Up_Level3,You finally come to understand that great doings start from hard work and determination.\nfallback=Level_Up_Level4,After reminiscing upon all that time you lost in your youth, it's as clear as day to you now: this - this is the life, and all the experience you gain gives you the tools you need to succeed in it.\nfallback=Level_Up_Level5,Things seem to be working out for you. As you become more capable, the world is opening up.\nfallback=Level_Up_Level6,With every push you did to gain knowledge, and yet your thirst for it increased, you realize there is so much more to learn still.\nfallback=Level_Up_Level7,You dig deep within yourself, trying to finally become the one who you thought you would be... but there's much to keep digging through.\nfallback=Level_Up_Level8,Success doesn't come easily, but you make it appear that way to others.\nfallback=Level_Up_Level9,Everything may slowly become a second nature to you, but it also can turn just a bit more dangerous.\nfallback=Level_Up_Level10,Nothing in life is easy, or it wouldn't have been worth the blood, sweat and tears.\nfallback=Level_Up_Level11,Working smarter, not harder is something even a barbarian could benefit from.\nfallback=Level_Up_Level12,Perhaps some would call you delusional for keeping at it, but you know better. Your dilegence has already paid off, after all.\nfallback=Level_Up_Level13,One night's sleep is all the difference between something being difficult and something being easy.\nfallback=Level_Up_Level14,After waking up, you wonder: could today be the best day in your life? You never know.\nfallback=Level_Up_Level15,Ouch! You lean back feeling your whole body for what seems the first time. You'll be more mindful in the future -- you can only live once.\nfallback=Level_Up_Level16,Trusting your instincts has gotten you this far, but now it's clear to you that you need to be smarter to survive.\nfallback=Level_Up_Level17,You're forging your spirit in the crucible that is experience. Be ever vigilant.\nfallback=Level_Up_Level18,The frustrations of the day before vanish as you wake up. Today is a new day.\nfallback=Level_Up_Level19,Today isn't yesterday, you feel that deep inside. You know that there is still more to this life.\nfallback=Level_Up_Level20,Luck has never been a factor in your success -- that's obvious from the scars on your body and from the number of trails you've endured.\nfallback=Level_Up_Default,Through sheer force of will, you push onwards and upwards. The toil of your persistence is paying off.\n\n# character creation\nfallback=Question_1_Question,Before you lies a creature you have never seen before. Its hind leg is caught in a hunter's trap, and you can tell that the leg is broken. What will you do?\nfallback=Question_1_AnswerOne,Pull out you knife for a short and merciful kill.\nfallback=Question_1_AnswerTwo,Reach into your herbal pouch to find something to ease its suffering before putting it to sleep.\nfallback=Question_1_AnswerThree,Leave the leg alone, but take the time to observe and learn from this odd creature.\nfallback=Question_1_Sound,Voice\\CharGen\\QA1.mp3\nfallback=Question_2_Question,Your mother suggests you to help with work around your family household. Do you decide to...\nfallback=Question_2_AnswerOne,Help your father with fence post replacement?\nfallback=Question_2_AnswerTwo,Collect a few herbs in the forest for tonight's supper?\nfallback=Question_2_AnswerThree,Help the fish to find their way into the kitchen?\nfallback=Question_2_Sound,Voice\\CharGen\\QA2.mp3\nfallback=Question_3_Question,Your brother teases you mercilessly in front of everyone with embarrassing details you would rather have forgotten.\nfallback=Question_3_AnswerOne,Give him a black-eye and dare him to keep it up.\nfallback=Question_3_AnswerTwo,Beat him to the punch and play it up -- if you can control the negative, then he can't embarrass you.\nfallback=Question_3_AnswerThree,Wait until he sleeps, put his hand in a bowl of water and call everyone around the next day to see the results.\nfallback=Question_3_Sound,Voice\\CharGen\\QA3.mp3\nfallback=Question_4_Question,Rumor has it that the King's security console has a new tool at their disposal for sniffing out the truth, which could be used for reading minds.\nfallback=Question_4_AnswerOne,You recoil at the thought of someone being able to read your thoughts. It's not that you think something that you'll ever act on it.\nfallback=Question_4_AnswerTwo,For those who have done nothing, there is nothing to fear. This is just yet another utility for catching thieves, murderers and plots against the crown.\nfallback=Question_4_AnswerThree,While you loath the idea of someone reading your mind, you accept that it does have its uses if tempered by law and common sense. You wouldn't believe the mind of a madman.\nfallback=Question_4_Sound,Voice\\CharGen\\QA4.mp3\nfallback=Question_5_Question,You are returning home from the market after acquiring supplies, and notice that one of the merchants had given you too much back in change.\nfallback=Question_5_AnswerOne,How dreadful, what if it was you? You head back to the merchant.\nfallback=Question_5_AnswerTwo,Happy days indeed. Put that extra money towards the needs of your family?\nfallback=Question_5_AnswerThree,You win some and you lose some. In this case you have won and they have lost, and the oversight is the merchant's problem, not yours, right?\nfallback=Question_5_Sound,Voice\\CharGen\\QA5.mp3\nfallback=Question_6_Question,While at market, a noble yells out to the city watch about a theft. As you turn around a man bumps into you and drops a sack, startled he darts into the crowd.\nfallback=Question_6_AnswerOne,You pick up the sack and head over to the noble to return his property?\nfallback=Question_6_AnswerTwo,Just walk away, the last thing you need is someone thinking you had anything to do with it?\nfallback=Question_6_AnswerThree,Finders, keepers... you whistle as you walk away with the bag hidden?\nfallback=Question_6_Sound,Voice\\CharGen\\QA6.mp3\nfallback=Question_7_Question,If it's one thing you hate, it's cleaning out the stalls. It has to be done before sunset before the critters return. On your way there an old friend greets you and offers to help if you promise to help them in the future.\nfallback=Question_7_AnswerOne,You thank him for the offer but would rather get it over with and not make promises you can't keep?\nfallback=Question_7_AnswerTwo,You reason that two pairs of hands are better than one regardless of the task?\nfallback=Question_7_AnswerThree,You say that it sounds great and anything is better than cleaning the stalls?\nfallback=Question_7_Sound,Voice\\CharGen\\QA7.mp3\nfallback=Question_8_Question,You just climbed down the ladder after working on the roof. Your mother thanks you for the hard work but just at the moment you notice that a hammer is about to fall down on her head.\nfallback=Question_8_AnswerOne,You lunge at your mother, pushing her out the way while the hammer falls on top of you?\nfallback=Question_8_AnswerTwo,Try to use the ladder to intercept the hammer before it lands on her?\nfallback=Question_8_AnswerThree,Warn her to take a step back?\nfallback=Question_8_Sound,Voice\\CharGen\\QA8.mp3\nfallback=Question_9_Question,It's the end of the week, and you have just got your wages for your hard work. You decide to take the quick way back home, darting into a alley only to be confronted by ruffians who demand that you empty your pockets.\nfallback=Question_9_AnswerOne,You tell them to go pack sand, planting your feet and raising your fists?\nfallback=Question_9_AnswerTwo,Acting dejected, you turn your wages over. You know that you can count on your friends to help you track these brigands down and recover what's yours?\nfallback=Question_9_AnswerThree,Tossing the sack into the air, you charge the leader who's attention is squarely focused on the coins in flight?\nfallback=Question_9_Sound,Voice\\CharGen\\QA9.mp3\nfallback=Question_10_Question,Upon coming to town, you see a clown running at you with an angry mob following after him.\nfallback=Question_10_AnswerOne,Stand your ground, you hate clowns?\nfallback=Question_10_AnswerTwo,Move out of the way, letting fate decide the clown's fate?\nfallback=Question_10_AnswerThree,Come to the clown's aid, clowns are people too?\nfallback=Question_10_Sound,Voice\\CharGen\\QA10.mp3\n\n# blood splatters\nfallback=Blood_Model_0,BloodSplat.nif\nfallback=Blood_Model_1,BloodSplat2.nif\nfallback=Blood_Model_2,BloodSplat3.nif\nfallback=Blood_Texture_0,Tx_Blood.dds\nfallback=Blood_Texture_1,Tx_Blood_White.dds\nfallback=Blood_Texture_2,Tx_Blood_Gold.dds\nfallback=Blood_Texture_Name_0,Default (Red)\nfallback=Blood_Texture_Name_1,Skeleton (White)\nfallback=Blood_Texture_Name_2,Metal Sparks (Gold)\n\n# movies\nfallback=Movies_Project_Logo,openmw_project_logo.webm\nfallback=Movies_Game_Logo,openmw_example_suite_logo.webm\nfallback=Movies_New_Game,new_game.webm\nfallback=Movies_Loading,loading.webm\nfallback=Movies_Options_Menu,options_menu.webm\n\n# weather\nfallback=Weather_Thunderstorm_Thunder_Sound_ID_0,Thunder0\nfallback=Weather_Thunderstorm_Thunder_Sound_ID_1,Thunder1\nfallback=Weather_Thunderstorm_Thunder_Sound_ID_2,Thunder2\nfallback=Weather_Thunderstorm_Thunder_Sound_ID_3,Thunder3\nfallback=Weather_Sunrise_Time,6\nfallback=Weather_Sunset_Time,18\nfallback=Weather_Sunrise_Duration,2\nfallback=Weather_Sunset_Duration,2\nfallback=Weather_Hours_Between_Weather_Changes,20\nfallback=Weather_Thunderstorm_Thunder_Frequency,.4\nfallback=Weather_Thunderstorm_Thunder_Threshold,0.6\nfallback=Weather_EnvReduceColor,255,255,255,255\nfallback=Weather_LerpCloseColor,037,046,048,255\nfallback=Weather_BumpFadeColor,230,239,255,255\nfallback=Weather_AlphaReduce,0.35\nfallback=Weather_Minimum_Time_Between_Environmental_Sounds,1.0\nfallback=Weather_Maximum_Time_Between_Environmental_Sounds,5.0\nfallback=Weather_Sun_Glare_Fader_Max,0.5\nfallback=Weather_Sun_Glare_Fader_Angle_Max,30.0\nfallback=Weather_Sun_Glare_Fader_Color,222,095,039\nfallback=Weather_Timescale_Clouds,0\nfallback=Weather_Precip_Gravity,575\nfallback=Weather_Rain_Ripples,1\nfallback=Weather_Rain_Ripple_Radius,1024\nfallback=Weather_Rain_Ripples_Per_Drop,1\nfallback=Weather_Rain_Ripple_Scale,0.3\nfallback=Weather_Rain_Ripple_Speed,1.0\nfallback=Weather_Fog_Depth_Change_Speed,3\nfallback=Weather_Sky_Pre-Sunrise_Time,.5\nfallback=Weather_Sky_Post-Sunrise_Time,1\nfallback=Weather_Sky_Pre-Sunset_Time,1.5\nfallback=Weather_Sky_Post-Sunset_Time,.5\nfallback=Weather_Ambient_Pre-Sunrise_Time,.5\nfallback=Weather_Ambient_Post-Sunrise_Time,2\nfallback=Weather_Ambient_Pre-Sunset_Time,1\nfallback=Weather_Ambient_Post-Sunset_Time,1.25\nfallback=Weather_Fog_Pre-Sunrise_Time,.5\nfallback=Weather_Fog_Post-Sunrise_Time,1\nfallback=Weather_Fog_Pre-Sunset_Time,2\nfallback=Weather_Fog_Post-Sunset_Time,1\nfallback=Weather_Sun_Pre-Sunrise_Time,0\nfallback=Weather_Sun_Post-Sunrise_Time,0\nfallback=Weather_Sun_Pre-Sunset_Time,1\nfallback=Weather_Sun_Post-Sunset_Time,1.25\nfallback=Weather_Stars_Post-Sunset_Start,1\nfallback=Weather_Stars_Pre-Sunrise_Finish,2\nfallback=Weather_Stars_Fading_Duration,2\nfallback=Weather_Snow_Ripples,0\nfallback=Weather_Snow_Ripple_Radius,1024\nfallback=Weather_Snow_Ripples_Per_Flake,1\nfallback=Weather_Snow_Ripple_Scale,0.3\nfallback=Weather_Snow_Ripple_Speed,1.0\nfallback=Weather_Snow_Gravity_Scale,0.1\nfallback=Weather_Snow_High_Kill,700\nfallback=Weather_Snow_Low_Kill,150\nfallback=Weather_Clear_Cloud_Texture,Tx_Sky_Clear.dds\nfallback=Weather_Clear_Clouds_Maximum_Percent,1.0\nfallback=Weather_Clear_Transition_Delta,.015\nfallback=Weather_Clear_Sky_Sunrise_Color,117,141,164\nfallback=Weather_Clear_Sky_Day_Color,095,135,203\nfallback=Weather_Clear_Sky_Sunset_Color,056,089,129\nfallback=Weather_Clear_Sky_Night_Color,009,010,011\nfallback=Weather_Clear_Fog_Sunrise_Color,255,189,157\nfallback=Weather_Clear_Fog_Day_Color,206,227,255\nfallback=Weather_Clear_Fog_Sunset_Color,255,189,157\nfallback=Weather_Clear_Fog_Night_Color,009,010,011\nfallback=Weather_Clear_Ambient_Sunrise_Color,047,066,096\nfallback=Weather_Clear_Ambient_Day_Color,137,140,160\nfallback=Weather_Clear_Ambient_Sunset_Color,068,075,096\nfallback=Weather_Clear_Ambient_Night_Color,032,035,042\nfallback=Weather_Clear_Sun_Sunrise_Color,242,159,119\nfallback=Weather_Clear_Sun_Day_Color,255,252,238\nfallback=Weather_Clear_Sun_Sunset_Color,255,114,079\nfallback=Weather_Clear_Sun_Night_Color,059,097,176\nfallback=Weather_Clear_Sun_Disc_Sunset_Color,255,189,157\nfallback=Weather_Clear_Land_Fog_Day_Depth,.69\nfallback=Weather_Clear_Land_Fog_Night_Depth,.69\nfallback=Weather_Clear_Wind_Speed,.1\nfallback=Weather_Clear_Cloud_Speed,1.25\nfallback=Weather_Clear_Glare_View,1\nfallback=Weather_Clear_Ambient_Loop_Sound_ID,None\nfallback=Weather_Cloudy_Cloud_Texture,Tx_Sky_Cloudy.dds\nfallback=Weather_Cloudy_Clouds_Maximum_Percent,1.0\nfallback=Weather_Cloudy_Transition_Delta,.015\nfallback=Weather_Cloudy_Sky_Sunrise_Color,126,158,173\nfallback=Weather_Cloudy_Sky_Day_Color,117,160,215\nfallback=Weather_Cloudy_Sky_Sunset_Color,111,114,159\nfallback=Weather_Cloudy_Sky_Night_Color,009,010,011\nfallback=Weather_Cloudy_Fog_Sunrise_Color,255,207,149\nfallback=Weather_Cloudy_Fog_Day_Color,245,235,224\nfallback=Weather_Cloudy_Fog_Sunset_Color,255,155,106\nfallback=Weather_Cloudy_Fog_Night_Color,009,010,011\nfallback=Weather_Cloudy_Ambient_Sunrise_Color,066,074,087\nfallback=Weather_Cloudy_Ambient_Day_Color,137,145,160\nfallback=Weather_Cloudy_Ambient_Sunset_Color,071,080,092\nfallback=Weather_Cloudy_Ambient_Night_Color,032,039,054\nfallback=Weather_Cloudy_Sun_Sunrise_Color,241,177,099\nfallback=Weather_Cloudy_Sun_Day_Color,255,236,221\nfallback=Weather_Cloudy_Sun_Sunset_Color,255,089,000\nfallback=Weather_Cloudy_Sun_Night_Color,077,091,124\nfallback=Weather_Cloudy_Sun_Disc_Sunset_Color,255,202,179\nfallback=Weather_Cloudy_Land_Fog_Day_Depth,.72\nfallback=Weather_Cloudy_Land_Fog_Night_Depth,.72\nfallback=Weather_Cloudy_Wind_Speed,.2\nfallback=Weather_Cloudy_Cloud_Speed,2\nfallback=Weather_Cloudy_Glare_View,1\nfallback=Weather_Cloudy_Ambient_Loop_Sound_ID,None\nfallback=Weather_Foggy_Cloud_Texture,Tx_Sky_Foggy.dds\nfallback=Weather_Foggy_Clouds_Maximum_Percent,1.0\nfallback=Weather_Foggy_Transition_Delta,.015\nfallback=Weather_Foggy_Sky_Sunrise_Color,197,190,180\nfallback=Weather_Foggy_Sky_Day_Color,184,211,228\nfallback=Weather_Foggy_Sky_Sunset_Color,142,159,176\nfallback=Weather_Foggy_Sky_Night_Color,018,023,028\nfallback=Weather_Foggy_Fog_Sunrise_Color,173,164,148\nfallback=Weather_Foggy_Fog_Day_Color,150,187,209\nfallback=Weather_Foggy_Fog_Sunset_Color,113,135,157\nfallback=Weather_Foggy_Fog_Night_Color,019,024,029\nfallback=Weather_Foggy_Ambient_Sunrise_Color,048,043,037\nfallback=Weather_Foggy_Ambient_Day_Color,092,109,120\nfallback=Weather_Foggy_Ambient_Sunset_Color,029,053,076\nfallback=Weather_Foggy_Ambient_Night_Color,028,033,039\nfallback=Weather_Foggy_Sun_Sunrise_Color,177,162,137\nfallback=Weather_Foggy_Sun_Day_Color,111,131,151\nfallback=Weather_Foggy_Sun_Sunset_Color,125,157,189\nfallback=Weather_Foggy_Sun_Night_Color,081,100,119\nfallback=Weather_Foggy_Sun_Disc_Sunset_Color,223,223,223\nfallback=Weather_Foggy_Land_Fog_Day_Depth,1.0\nfallback=Weather_Foggy_Land_Fog_Night_Depth,1.9\nfallback=Weather_Foggy_Wind_Speed,0\nfallback=Weather_Foggy_Cloud_Speed,1.25\nfallback=Weather_Foggy_Glare_View,0.25\nfallback=Weather_Foggy_Ambient_Loop_Sound_ID,None\nfallback=Weather_Thunderstorm_Cloud_Texture,Tx_Sky_Thunder.dds\nfallback=Weather_Thunderstorm_Clouds_Maximum_Percent,0.66\nfallback=Weather_Thunderstorm_Transition_Delta,.030\nfallback=Weather_Thunderstorm_Sky_Sunrise_Color,035,036,039\nfallback=Weather_Thunderstorm_Sky_Day_Color,097,104,115\nfallback=Weather_Thunderstorm_Sky_Sunset_Color,035,036,039\nfallback=Weather_Thunderstorm_Sky_Night_Color,019,020,022\nfallback=Weather_Thunderstorm_Fog_Sunrise_Color,070,074,085\nfallback=Weather_Thunderstorm_Fog_Day_Color,097,104,115\nfallback=Weather_Thunderstorm_Fog_Sunset_Color,070,074,085\nfallback=Weather_Thunderstorm_Fog_Night_Color,019,020,022\nfallback=Weather_Thunderstorm_Ambient_Sunrise_Color,054,054,054\nfallback=Weather_Thunderstorm_Ambient_Day_Color,090,090,090\nfallback=Weather_Thunderstorm_Ambient_Sunset_Color,054,054,054\nfallback=Weather_Thunderstorm_Ambient_Night_Color,049,051,054\nfallback=Weather_Thunderstorm_Sun_Sunrise_Color,091,099,122\nfallback=Weather_Thunderstorm_Sun_Day_Color,138,144,155\nfallback=Weather_Thunderstorm_Sun_Sunset_Color,096,101,117\nfallback=Weather_Thunderstorm_Sun_Night_Color,055,076,110\nfallback=Weather_Thunderstorm_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Thunderstorm_Land_Fog_Day_Depth,1\nfallback=Weather_Thunderstorm_Land_Fog_Night_Depth,1.15\nfallback=Weather_Thunderstorm_Wind_Speed,.5\nfallback=Weather_Thunderstorm_Cloud_Speed,3\nfallback=Weather_Thunderstorm_Glare_View,0\nfallback=Weather_Thunderstorm_Rain_Loop_Sound_ID,rain heavy\nfallback=Weather_Thunderstorm_Using_Precip,1\nfallback=Weather_Thunderstorm_Rain_Diameter,600\nfallback=Weather_Thunderstorm_Rain_Height_Min,200\nfallback=Weather_Thunderstorm_Rain_Height_Max,700\nfallback=Weather_Thunderstorm_Rain_Threshold,0.6\nfallback=Weather_Thunderstorm_Max_Raindrops,650\nfallback=Weather_Thunderstorm_Rain_Entrance_Speed,5\nfallback=Weather_Thunderstorm_Ambient_Loop_Sound_ID,None\nfallback=Weather_Thunderstorm_Flash_Decrement,4\nfallback=Weather_Rain_Cloud_Texture,Tx_Sky_Rainy.dds\nfallback=Weather_Rain_Clouds_Maximum_Percent,0.66\nfallback=Weather_Rain_Transition_Delta,.015\nfallback=Weather_Rain_Sky_Sunrise_Color,071,074,075\nfallback=Weather_Rain_Sky_Day_Color,116,120,122\nfallback=Weather_Rain_Sky_Sunset_Color,073,073,073\nfallback=Weather_Rain_Sky_Night_Color,024,025,026\nfallback=Weather_Rain_Fog_Sunrise_Color,071,074,075\nfallback=Weather_Rain_Fog_Day_Color,116,120,122\nfallback=Weather_Rain_Fog_Sunset_Color,073,073,073\nfallback=Weather_Rain_Fog_Night_Color,024,025,026\nfallback=Weather_Rain_Ambient_Sunrise_Color,097,090,088\nfallback=Weather_Rain_Ambient_Day_Color,105,110,113\nfallback=Weather_Rain_Ambient_Sunset_Color,088,097,097\nfallback=Weather_Rain_Ambient_Night_Color,050,055,067\nfallback=Weather_Rain_Sun_Sunrise_Color,131,122,120\nfallback=Weather_Rain_Sun_Day_Color,149,157,170\nfallback=Weather_Rain_Sun_Sunset_Color,120,126,131\nfallback=Weather_Rain_Sun_Night_Color,050,062,101\nfallback=Weather_Rain_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Rain_Land_Fog_Day_Depth,.8\nfallback=Weather_Rain_Land_Fog_Night_Depth,.8\nfallback=Weather_Rain_Wind_Speed,.3\nfallback=Weather_Rain_Cloud_Speed,2\nfallback=Weather_Rain_Glare_View,0\nfallback=Weather_Rain_Rain_Loop_Sound_ID,Rain\nfallback=Weather_Rain_Using_Precip,1\nfallback=Weather_Rain_Rain_Diameter,600\nfallback=Weather_Rain_Rain_Height_Min,200\nfallback=Weather_Rain_Rain_Height_Max,700\nfallback=Weather_Rain_Rain_Threshold,0.6\nfallback=Weather_Rain_Rain_Entrance_Speed,7\nfallback=Weather_Rain_Ambient_Loop_Sound_ID,None\nfallback=Weather_Rain_Max_Raindrops,450\nfallback=Weather_Overcast_Cloud_Texture,Tx_Sky_Overcast.dds\nfallback=Weather_Overcast_Clouds_Maximum_Percent,1.0\nfallback=Weather_Overcast_Transition_Delta,.015\nfallback=Weather_Overcast_Sky_Sunrise_Color,091,099,106\nfallback=Weather_Overcast_Sky_Day_Color,143,146,149\nfallback=Weather_Overcast_Sky_Sunset_Color,108,115,121\nfallback=Weather_Overcast_Sky_Night_Color,019,022,025\nfallback=Weather_Overcast_Fog_Sunrise_Color,091,099,106\nfallback=Weather_Overcast_Fog_Day_Color,143,146,149\nfallback=Weather_Overcast_Fog_Sunset_Color,108,115,121\nfallback=Weather_Overcast_Fog_Night_Color,019,022,025\nfallback=Weather_Overcast_Ambient_Sunrise_Color,084,088,092\nfallback=Weather_Overcast_Ambient_Day_Color,093,096,105\nfallback=Weather_Overcast_Ambient_Sunset_Color,083,077,075\nfallback=Weather_Overcast_Ambient_Night_Color,057,060,066\nfallback=Weather_Overcast_Sun_Sunrise_Color,087,125,163\nfallback=Weather_Overcast_Sun_Day_Color,163,169,183\nfallback=Weather_Overcast_Sun_Sunset_Color,085,103,157\nfallback=Weather_Overcast_Sun_Night_Color,032,054,100\nfallback=Weather_Overcast_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Overcast_Land_Fog_Day_Depth,.70\nfallback=Weather_Overcast_Land_Fog_Night_Depth,.70\nfallback=Weather_Overcast_Wind_Speed,.2\nfallback=Weather_Overcast_Cloud_Speed,1.5\nfallback=Weather_Overcast_Glare_View,0\nfallback=Weather_Overcast_Ambient_Loop_Sound_ID,None\nfallback=Weather_Snow_Cloud_Texture,Tx_Sky_Snow.dds\nfallback=Weather_Snow_Clouds_Maximum_Percent,1.0\nfallback=Weather_Snow_Transition_Delta,.015\nfallback=Weather_Snow_Sky_Sunrise_Color,106,091,091\nfallback=Weather_Snow_Sky_Day_Color,153,158,166\nfallback=Weather_Snow_Sky_Sunset_Color,096,115,134\nfallback=Weather_Snow_Sky_Night_Color,031,035,039\nfallback=Weather_Snow_Fog_Sunrise_Color,106,091,091\nfallback=Weather_Snow_Fog_Day_Color,153,158,166\nfallback=Weather_Snow_Fog_Sunset_Color,096,115,134\nfallback=Weather_Snow_Fog_Night_Color,031,035,039\nfallback=Weather_Snow_Ambient_Sunrise_Color,092,084,084\nfallback=Weather_Snow_Ambient_Day_Color,093,096,105\nfallback=Weather_Snow_Ambient_Sunset_Color,070,079,087\nfallback=Weather_Snow_Ambient_Night_Color,049,058,068\nfallback=Weather_Snow_Sun_Sunrise_Color,141,109,109\nfallback=Weather_Snow_Sun_Day_Color,163,169,183\nfallback=Weather_Snow_Sun_Sunset_Color,101,121,141\nfallback=Weather_Snow_Sun_Night_Color,055,066,077\nfallback=Weather_Snow_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Snow_Land_Fog_Day_Depth,1.0\nfallback=Weather_Snow_Land_Fog_Night_Depth,1.2\nfallback=Weather_Snow_Wind_Speed,0\nfallback=Weather_Snow_Cloud_Speed,1.5\nfallback=Weather_Snow_Glare_View,0\nfallback=Weather_Snow_Snow_Diameter,800\nfallback=Weather_Snow_Snow_Height_Min,400\nfallback=Weather_Snow_Snow_Height_Max,700\nfallback=Weather_Snow_Snow_Entrance_Speed,6\nfallback=Weather_Snow_Max_Snowflakes,750\nfallback=Weather_Snow_Ambient_Loop_Sound_ID,None\nfallback=Weather_Snow_Snow_Threshold,0.5\nfallback=Weather_Blizzard_Cloud_Texture,Tx_Sky_Blizzard.dds\nfallback=Weather_Blizzard_Clouds_Maximum_Percent,1.0\nfallback=Weather_Blizzard_Transition_Delta,.030\nfallback=Weather_Blizzard_Sky_Sunrise_Color,091,099,106\nfallback=Weather_Blizzard_Sky_Day_Color,121,133,145\nfallback=Weather_Blizzard_Sky_Sunset_Color,108,115,121\nfallback=Weather_Blizzard_Sky_Night_Color,027,029,031\nfallback=Weather_Blizzard_Fog_Sunrise_Color,091,099,106\nfallback=Weather_Blizzard_Fog_Day_Color,121,133,145\nfallback=Weather_Blizzard_Fog_Sunset_Color,108,115,121\nfallback=Weather_Blizzard_Fog_Night_Color,021,024,028\nfallback=Weather_Blizzard_Ambient_Sunrise_Color,084,088,092\nfallback=Weather_Blizzard_Ambient_Day_Color,093,096,105\nfallback=Weather_Blizzard_Ambient_Sunset_Color,083,077,075\nfallback=Weather_Blizzard_Ambient_Night_Color,053,062,070\nfallback=Weather_Blizzard_Sun_Sunrise_Color,114,128,146\nfallback=Weather_Blizzard_Sun_Day_Color,163,169,183\nfallback=Weather_Blizzard_Sun_Sunset_Color,106,114,136\nfallback=Weather_Blizzard_Sun_Night_Color,057,066,074\nfallback=Weather_Blizzard_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Blizzard_Land_Fog_Day_Depth,2.8\nfallback=Weather_Blizzard_Land_Fog_Night_Depth,3.0\nfallback=Weather_Blizzard_Wind_Speed,.9\nfallback=Weather_Blizzard_Cloud_Speed,7.5\nfallback=Weather_Blizzard_Glare_View,0\nfallback=Weather_Blizzard_Ambient_Loop_Sound_ID, Blizzard\nfallback=Weather_Blizzard_Storm_Threshold,.50\n"
  },
  {
    "path": "files/openmw.cfg.local",
    "content": "# This is the local openmw.cfg file. Do not modify!\n# Modifications should be done on the user openmw.cfg file instead\n# (see: https://openmw.readthedocs.io/en/master/reference/modding/paths.html)\n\ndata=\"?global?data\"\ndata=./data\ndata-local=\"?userdata?data\"\nresources=./resources\nscript-blacklist=Museum\nscript-blacklist=MockChangeScript\nscript-blacklist=doortestwarp\nscript-blacklist=WereChange2Script\nscript-blacklist=wereDreamScript2\nscript-blacklist=wereDreamScript3\n\n# lighting\nfallback=LightAttenuation_UseConstant,0\nfallback=LightAttenuation_ConstantValue,0.0\nfallback=LightAttenuation_UseLinear,1\nfallback=LightAttenuation_LinearMethod,1\nfallback=LightAttenuation_LinearValue,3.0\nfallback=LightAttenuation_LinearRadiusMult,1.0\nfallback=LightAttenuation_UseQuadratic,0\nfallback=LightAttenuation_QuadraticMethod,2\nfallback=LightAttenuation_QuadraticValue,16.0\nfallback=LightAttenuation_QuadraticRadiusMult,1.0\nfallback=LightAttenuation_OutQuadInLin,0\n\n# inventory\nfallback=Inventory_DirectionalDiffuseR,1.0\nfallback=Inventory_DirectionalDiffuseG,1.0\nfallback=Inventory_DirectionalDiffuseB,1.0\nfallback=Inventory_DirectionalAmbientR,0.0\nfallback=Inventory_DirectionalAmbientG,0.0\nfallback=Inventory_DirectionalAmbientB,0.0\nfallback=Inventory_DirectionalRotationX,110\nfallback=Inventory_DirectionalRotationY,90\nfallback=Inventory_UniformScaling,0\n\n# water effects\nfallback=Water_Map_Alpha,0.4\nfallback=Water_World_Alpha,0.75\nfallback=Water_SurfaceTextureSize,128\nfallback=Water_SurfaceTileCount,10\nfallback=Water_SurfaceFPS,12\nfallback=Water_SurfaceTexture,water\nfallback=Water_SurfaceFrameCount,32\nfallback=Water_TileTextureDivisor,4.75\nfallback=Water_RippleTexture,ripple\nfallback=Water_RippleFrameCount,4\nfallback=Water_RippleLifetime,3.0\nfallback=Water_MaxNumberRipples,75\nfallback=Water_RippleScale,0.15, 6.5\nfallback=Water_RippleRotSpeed,0.5\nfallback=Water_RippleAlphas,0.7, 0.1, 0.01\nfallback=Water_NearWaterRadius,1000\nfallback=Water_NearWaterPoints,8\nfallback=Water_NearWaterUnderwaterFreq,0.3\nfallback=Water_NearWaterUnderwaterVolume,0.9\nfallback=Water_NearWaterIndoorTolerance,512.0\nfallback=Water_NearWaterOutdoorTolerance,1024.0\nfallback=Water_NearWaterIndoorID,Water Layer\nfallback=Water_NearWaterOutdoorID,Water Layer\nfallback=Water_UnderwaterSunriseFog,3\nfallback=Water_UnderwaterDayFog,2.5\nfallback=Water_UnderwaterSunsetFog,3\nfallback=Water_UnderwaterNightFog,4\nfallback=Water_UnderwaterIndoorFog,3\nfallback=Water_UnderwaterColor,012,030,037\nfallback=Water_UnderwaterColorWeight,0.85\n\n# fonts\nfallback=Fonts_Font_0,magic_cards_regular\nfallback=Fonts_Font_1,century_gothic_font_regular\nfallback=Fonts_Font_2,daedric_font\nfallback=FontColor_color_normal,202,165,96\nfallback=FontColor_color_normal_over,223,201,159\nfallback=FontColor_color_normal_pressed,243,237,221\nfallback=FontColor_color_active,96,112,202\nfallback=FontColor_color_active_over,159,169,223\nfallback=FontColor_color_active_pressed,223,226,244\nfallback=FontColor_color_disabled,179,168,135\nfallback=FontColor_color_disabled_over,223,201,159\nfallback=FontColor_color_disabled_pressed,243,237,221\nfallback=FontColor_color_link,112,126,207\nfallback=FontColor_color_link_over,143,155,218\nfallback=FontColor_color_link_pressed,175,184,228\nfallback=FontColor_color_journal_link,37,49,112\nfallback=FontColor_color_journal_link_over,58,77,175\nfallback=FontColor_color_journal_link_pressed,112,126,207\nfallback=FontColor_color_journal_topic,0,0,0\nfallback=FontColor_color_journal_topic_over,58,77,175\nfallback=FontColor_color_journal_topic_pressed,112,126,207\nfallback=FontColor_color_answer,150,50,30\nfallback=FontColor_color_answer_over,223,201,159\nfallback=FontColor_color_answer_pressed,243,237,221\nfallback=FontColor_color_header,223,201,159\nfallback=FontColor_color_notify,223,201,159\nfallback=FontColor_color_big_normal,202,165,96\nfallback=FontColor_color_big_normal_over,223,201,159\nfallback=FontColor_color_big_normal_pressed,243,237,221\nfallback=FontColor_color_big_link,112,126,207\nfallback=FontColor_color_big_link_over,143,155,218\nfallback=FontColor_color_big_link_pressed,175,184,228\nfallback=FontColor_color_big_answer,150,50,30\nfallback=FontColor_color_big_answer_over,223,201,159\nfallback=FontColor_color_big_answer_pressed,243,237,22\nfallback=FontColor_color_big_header,223,201,159\nfallback=FontColor_color_big_notify,223,201,159\nfallback=FontColor_color_background,0,0,0\nfallback=FontColor_color_focus,80,80,80\nfallback=FontColor_color_health,200,60,30\nfallback=FontColor_color_magic,53,69,159\nfallback=FontColor_color_fatigue,0,150,60\nfallback=FontColor_color_misc,0,205,205\nfallback=FontColor_color_weapon_fill,200,60,30\nfallback=FontColor_color_magic_fill,200,60,30\nfallback=FontColor_color_positive,223,201,159\nfallback=FontColor_color_negative,200,60,30\nfallback=FontColor_color_count,223,201,159\n\n# leveling\nfallback=Level_Up_Level2,The last few days have opened your eyes: you realize now that there is more to the life than you thought. An uncertain future awaits you.\nfallback=Level_Up_Level3,You finally come to understand that great doings start from hard work and determination.\nfallback=Level_Up_Level4,After reminiscing upon all that time you lost in your youth, it's as clear as day to you now: this - this is the life, and all the experience you gain gives you the tools you need to succeed in it.\nfallback=Level_Up_Level5,Things seem to be working out for you. As you become more capable, the world is opening up.\nfallback=Level_Up_Level6,With every push you did to gain knowledge, and yet your thirst for it increased, you realize there is so much more to learn still.\nfallback=Level_Up_Level7,You dig deep within yourself, trying to finally become the one who you thought you would be... but there's much to keep digging through.\nfallback=Level_Up_Level8,Success doesn't come easily, but you make it appear that way to others.\nfallback=Level_Up_Level9,Everything may slowly become a second nature to you, but it also can turn just a bit more dangerous.\nfallback=Level_Up_Level10,Nothing in life is easy, or it wouldn't have been worth the blood, sweat and tears.\nfallback=Level_Up_Level11,Working smarter, not harder is something even a barbarian could benefit from.\nfallback=Level_Up_Level12,Perhaps some would call you delusional for keeping at it, but you know better. Your dilegence has already paid off, after all.\nfallback=Level_Up_Level13,One night's sleep is all the difference between something being difficult and something being easy.\nfallback=Level_Up_Level14,After waking up, you wonder: could today be the best day in your life? You never know.\nfallback=Level_Up_Level15,Ouch! You lean back feeling your whole body for what seems the first time. You'll be more mindful in the future -- you can only live once.\nfallback=Level_Up_Level16,Trusting your instincts has gotten you this far, but now it's clear to you that you need to be smarter to survive.\nfallback=Level_Up_Level17,You're forging your spirit in the crucible that is experience. Be ever vigilant.\nfallback=Level_Up_Level18,The frustrations of the day before vanish as you wake up. Today is a new day.\nfallback=Level_Up_Level19,Today isn't yesterday, you feel that deep inside. You know that there is still more to this life.\nfallback=Level_Up_Level20,Luck has never been a factor in your success -- that's obvious from the scars on your body and from the number of trails you've endured.\nfallback=Level_Up_Default,Through sheer force of will, you push onwards and upwards. The toil of your persistence is paying off.\n\n# character creation\nfallback=Question_1_Question,Before you lies a creature you have never seen before. Its hind leg is caught in a hunter's trap, and you can tell that the leg is broken. What will you do?\nfallback=Question_1_AnswerOne,Pull out you knife for a short and merciful kill.\nfallback=Question_1_AnswerTwo,Reach into your herbal pouch to find something to ease its suffering before putting it to sleep.\nfallback=Question_1_AnswerThree,Leave the leg alone, but take the time to observe and learn from this odd creature.\nfallback=Question_1_Sound,Voice\\CharGen\\QA1.mp3\nfallback=Question_2_Question,Your mother suggests you to help with work around your family household. Do you decide to...\nfallback=Question_2_AnswerOne,Help your father with fence post replacement?\nfallback=Question_2_AnswerTwo,Collect a few herbs in the forest for tonight's supper?\nfallback=Question_2_AnswerThree,Help the fish to find their way into the kitchen?\nfallback=Question_2_Sound,Voice\\CharGen\\QA2.mp3\nfallback=Question_3_Question,Your brother teases you mercilessly in front of everyone with embarrassing details you would rather have forgotten.\nfallback=Question_3_AnswerOne,Give him a black-eye and dare him to keep it up.\nfallback=Question_3_AnswerTwo,Beat him to the punch and play it up -- if you can control the negative, then he can't embarrass you.\nfallback=Question_3_AnswerThree,Wait until he sleeps, put his hand in a bowl of water and call everyone around the next day to see the results.\nfallback=Question_3_Sound,Voice\\CharGen\\QA3.mp3\nfallback=Question_4_Question,Rumor has it that the King's security console has a new tool at their disposal for sniffing out the truth, which could be used for reading minds.\nfallback=Question_4_AnswerOne,You recoil at the thought of someone being able to read your thoughts. It's not that you think something that you'll ever act on it.\nfallback=Question_4_AnswerTwo,For those who have done nothing, there is nothing to fear. This is just yet another utility for catching thieves, murderers and plots against the crown.\nfallback=Question_4_AnswerThree,While you loath the idea of someone reading your mind, you accept that it does have its uses if tempered by law and common sense. You wouldn't believe the mind of a madman.\nfallback=Question_4_Sound,Voice\\CharGen\\QA4.mp3\nfallback=Question_5_Question,You are returning home from the market after acquiring supplies, and notice that one of the merchants had given you too much back in change.\nfallback=Question_5_AnswerOne,How dreadful, what if it was you? You head back to the merchant.\nfallback=Question_5_AnswerTwo,Happy days indeed. Put that extra money towards the needs of your family?\nfallback=Question_5_AnswerThree,You win some and you lose some. In this case you have won and they have lost, and the oversight is the merchant's problem, not yours, right?\nfallback=Question_5_Sound,Voice\\CharGen\\QA5.mp3\nfallback=Question_6_Question,While at market, a noble yells out to the city watch about a theft. As you turn around a man bumps into you and drops a sack, startled he darts into the crowd.\nfallback=Question_6_AnswerOne,You pick up the sack and head over to the noble to return his property?\nfallback=Question_6_AnswerTwo,Just walk away, the last thing you need is someone thinking you had anything to do with it?\nfallback=Question_6_AnswerThree,Finders, keepers... you whistle as you walk away with the bag hidden?\nfallback=Question_6_Sound,Voice\\CharGen\\QA6.mp3\nfallback=Question_7_Question,If it's one thing you hate, it's cleaning out the stalls. It has to be done before sunset before the critters return. On your way there an old friend greets you and offers to help if you promise to help them in the future.\nfallback=Question_7_AnswerOne,You thank him for the offer but would rather get it over with and not make promises you can't keep?\nfallback=Question_7_AnswerTwo,You reason that two pairs of hands are better than one regardless of the task?\nfallback=Question_7_AnswerThree,You say that it sounds great and anything is better than cleaning the stalls?\nfallback=Question_7_Sound,Voice\\CharGen\\QA7.mp3\nfallback=Question_8_Question,You just climbed down the ladder after working on the roof. Your mother thanks you for the hard work but just at the moment you notice that a hammer is about to fall down on her head.\nfallback=Question_8_AnswerOne,You lunge at your mother, pushing her out the way while the hammer falls on top of you?\nfallback=Question_8_AnswerTwo,Try to use the ladder to intercept the hammer before it lands on her?\nfallback=Question_8_AnswerThree,Warn her to take a step back?\nfallback=Question_8_Sound,Voice\\CharGen\\QA8.mp3\nfallback=Question_9_Question,It's the end of the week, and you have just got your wages for your hard work. You decide to take the quick way back home, darting into a alley only to be confronted by ruffians who demand that you empty your pockets.\nfallback=Question_9_AnswerOne,You tell them to go pack sand, planting your feet and raising your fists?\nfallback=Question_9_AnswerTwo,Acting dejected, you turn your wages over. You know that you can count on your friends to help you track these brigands down and recover what's yours?\nfallback=Question_9_AnswerThree,Tossing the sack into the air, you charge the leader who's attention is squarely focused on the coins in flight?\nfallback=Question_9_Sound,Voice\\CharGen\\QA9.mp3\nfallback=Question_10_Question,Upon coming to town, you see a clown running at you with an angry mob following after him.\nfallback=Question_10_AnswerOne,Stand your ground, you hate clowns?\nfallback=Question_10_AnswerTwo,Move out of the way, letting fate decide the clown's fate?\nfallback=Question_10_AnswerThree,Come to the clown's aid, clowns are people too?\nfallback=Question_10_Sound,Voice\\CharGen\\QA10.mp3\n\n# blood splatters\nfallback=Blood_Model_0,BloodSplat.nif\nfallback=Blood_Model_1,BloodSplat2.nif\nfallback=Blood_Model_2,BloodSplat3.nif\nfallback=Blood_Texture_0,Tx_Blood.dds\nfallback=Blood_Texture_1,Tx_Blood_White.dds\nfallback=Blood_Texture_2,Tx_Blood_Gold.dds\nfallback=Blood_Texture_Name_0,Default (Red)\nfallback=Blood_Texture_Name_1,Skeleton (White)\nfallback=Blood_Texture_Name_2,Metal Sparks (Gold)\n\n# movies\nfallback=Movies_Project_Logo,openmw_project_logo.webm\nfallback=Movies_Game_Logo,openmw_example_suite_logo.webm\nfallback=Movies_New_Game,new_game.webm\nfallback=Movies_Loading,loading.webm\nfallback=Movies_Options_Menu,options_menu.webm\n\n# weather\nfallback=Weather_Thunderstorm_Thunder_Sound_ID_0,Thunder0\nfallback=Weather_Thunderstorm_Thunder_Sound_ID_1,Thunder1\nfallback=Weather_Thunderstorm_Thunder_Sound_ID_2,Thunder2\nfallback=Weather_Thunderstorm_Thunder_Sound_ID_3,Thunder3\nfallback=Weather_Sunrise_Time,6\nfallback=Weather_Sunset_Time,18\nfallback=Weather_Sunrise_Duration,2\nfallback=Weather_Sunset_Duration,2\nfallback=Weather_Hours_Between_Weather_Changes,20\nfallback=Weather_Thunderstorm_Thunder_Frequency,.4\nfallback=Weather_Thunderstorm_Thunder_Threshold,0.6\nfallback=Weather_EnvReduceColor,255,255,255,255\nfallback=Weather_LerpCloseColor,037,046,048,255\nfallback=Weather_BumpFadeColor,230,239,255,255\nfallback=Weather_AlphaReduce,0.35\nfallback=Weather_Minimum_Time_Between_Environmental_Sounds,1.0\nfallback=Weather_Maximum_Time_Between_Environmental_Sounds,5.0\nfallback=Weather_Sun_Glare_Fader_Max,0.5\nfallback=Weather_Sun_Glare_Fader_Angle_Max,30.0\nfallback=Weather_Sun_Glare_Fader_Color,222,095,039\nfallback=Weather_Timescale_Clouds,0\nfallback=Weather_Precip_Gravity,575\nfallback=Weather_Rain_Ripples,1\nfallback=Weather_Rain_Ripple_Radius,1024\nfallback=Weather_Rain_Ripples_Per_Drop,1\nfallback=Weather_Rain_Ripple_Scale,0.3\nfallback=Weather_Rain_Ripple_Speed,1.0\nfallback=Weather_Fog_Depth_Change_Speed,3\nfallback=Weather_Sky_Pre-Sunrise_Time,.5\nfallback=Weather_Sky_Post-Sunrise_Time,1\nfallback=Weather_Sky_Pre-Sunset_Time,1.5\nfallback=Weather_Sky_Post-Sunset_Time,.5\nfallback=Weather_Ambient_Pre-Sunrise_Time,.5\nfallback=Weather_Ambient_Post-Sunrise_Time,2\nfallback=Weather_Ambient_Pre-Sunset_Time,1\nfallback=Weather_Ambient_Post-Sunset_Time,1.25\nfallback=Weather_Fog_Pre-Sunrise_Time,.5\nfallback=Weather_Fog_Post-Sunrise_Time,1\nfallback=Weather_Fog_Pre-Sunset_Time,2\nfallback=Weather_Fog_Post-Sunset_Time,1\nfallback=Weather_Sun_Pre-Sunrise_Time,0\nfallback=Weather_Sun_Post-Sunrise_Time,0\nfallback=Weather_Sun_Pre-Sunset_Time,1\nfallback=Weather_Sun_Post-Sunset_Time,1.25\nfallback=Weather_Stars_Post-Sunset_Start,1\nfallback=Weather_Stars_Pre-Sunrise_Finish,2\nfallback=Weather_Stars_Fading_Duration,2\nfallback=Weather_Snow_Ripples,0\nfallback=Weather_Snow_Ripple_Radius,1024\nfallback=Weather_Snow_Ripples_Per_Flake,1\nfallback=Weather_Snow_Ripple_Scale,0.3\nfallback=Weather_Snow_Ripple_Speed,1.0\nfallback=Weather_Snow_Gravity_Scale,0.1\nfallback=Weather_Snow_High_Kill,700\nfallback=Weather_Snow_Low_Kill,150\nfallback=Weather_Clear_Cloud_Texture,Tx_Sky_Clear.dds\nfallback=Weather_Clear_Clouds_Maximum_Percent,1.0\nfallback=Weather_Clear_Transition_Delta,.015\nfallback=Weather_Clear_Sky_Sunrise_Color,117,141,164\nfallback=Weather_Clear_Sky_Day_Color,095,135,203\nfallback=Weather_Clear_Sky_Sunset_Color,056,089,129\nfallback=Weather_Clear_Sky_Night_Color,009,010,011\nfallback=Weather_Clear_Fog_Sunrise_Color,255,189,157\nfallback=Weather_Clear_Fog_Day_Color,206,227,255\nfallback=Weather_Clear_Fog_Sunset_Color,255,189,157\nfallback=Weather_Clear_Fog_Night_Color,009,010,011\nfallback=Weather_Clear_Ambient_Sunrise_Color,047,066,096\nfallback=Weather_Clear_Ambient_Day_Color,137,140,160\nfallback=Weather_Clear_Ambient_Sunset_Color,068,075,096\nfallback=Weather_Clear_Ambient_Night_Color,032,035,042\nfallback=Weather_Clear_Sun_Sunrise_Color,242,159,119\nfallback=Weather_Clear_Sun_Day_Color,255,252,238\nfallback=Weather_Clear_Sun_Sunset_Color,255,114,079\nfallback=Weather_Clear_Sun_Night_Color,059,097,176\nfallback=Weather_Clear_Sun_Disc_Sunset_Color,255,189,157\nfallback=Weather_Clear_Land_Fog_Day_Depth,.69\nfallback=Weather_Clear_Land_Fog_Night_Depth,.69\nfallback=Weather_Clear_Wind_Speed,.1\nfallback=Weather_Clear_Cloud_Speed,1.25\nfallback=Weather_Clear_Glare_View,1\nfallback=Weather_Clear_Ambient_Loop_Sound_ID,None\nfallback=Weather_Cloudy_Cloud_Texture,Tx_Sky_Cloudy.dds\nfallback=Weather_Cloudy_Clouds_Maximum_Percent,1.0\nfallback=Weather_Cloudy_Transition_Delta,.015\nfallback=Weather_Cloudy_Sky_Sunrise_Color,126,158,173\nfallback=Weather_Cloudy_Sky_Day_Color,117,160,215\nfallback=Weather_Cloudy_Sky_Sunset_Color,111,114,159\nfallback=Weather_Cloudy_Sky_Night_Color,009,010,011\nfallback=Weather_Cloudy_Fog_Sunrise_Color,255,207,149\nfallback=Weather_Cloudy_Fog_Day_Color,245,235,224\nfallback=Weather_Cloudy_Fog_Sunset_Color,255,155,106\nfallback=Weather_Cloudy_Fog_Night_Color,009,010,011\nfallback=Weather_Cloudy_Ambient_Sunrise_Color,066,074,087\nfallback=Weather_Cloudy_Ambient_Day_Color,137,145,160\nfallback=Weather_Cloudy_Ambient_Sunset_Color,071,080,092\nfallback=Weather_Cloudy_Ambient_Night_Color,032,039,054\nfallback=Weather_Cloudy_Sun_Sunrise_Color,241,177,099\nfallback=Weather_Cloudy_Sun_Day_Color,255,236,221\nfallback=Weather_Cloudy_Sun_Sunset_Color,255,089,000\nfallback=Weather_Cloudy_Sun_Night_Color,077,091,124\nfallback=Weather_Cloudy_Sun_Disc_Sunset_Color,255,202,179\nfallback=Weather_Cloudy_Land_Fog_Day_Depth,.72\nfallback=Weather_Cloudy_Land_Fog_Night_Depth,.72\nfallback=Weather_Cloudy_Wind_Speed,.2\nfallback=Weather_Cloudy_Cloud_Speed,2\nfallback=Weather_Cloudy_Glare_View,1\nfallback=Weather_Cloudy_Ambient_Loop_Sound_ID,None\nfallback=Weather_Foggy_Cloud_Texture,Tx_Sky_Foggy.dds\nfallback=Weather_Foggy_Clouds_Maximum_Percent,1.0\nfallback=Weather_Foggy_Transition_Delta,.015\nfallback=Weather_Foggy_Sky_Sunrise_Color,197,190,180\nfallback=Weather_Foggy_Sky_Day_Color,184,211,228\nfallback=Weather_Foggy_Sky_Sunset_Color,142,159,176\nfallback=Weather_Foggy_Sky_Night_Color,018,023,028\nfallback=Weather_Foggy_Fog_Sunrise_Color,173,164,148\nfallback=Weather_Foggy_Fog_Day_Color,150,187,209\nfallback=Weather_Foggy_Fog_Sunset_Color,113,135,157\nfallback=Weather_Foggy_Fog_Night_Color,019,024,029\nfallback=Weather_Foggy_Ambient_Sunrise_Color,048,043,037\nfallback=Weather_Foggy_Ambient_Day_Color,092,109,120\nfallback=Weather_Foggy_Ambient_Sunset_Color,029,053,076\nfallback=Weather_Foggy_Ambient_Night_Color,028,033,039\nfallback=Weather_Foggy_Sun_Sunrise_Color,177,162,137\nfallback=Weather_Foggy_Sun_Day_Color,111,131,151\nfallback=Weather_Foggy_Sun_Sunset_Color,125,157,189\nfallback=Weather_Foggy_Sun_Night_Color,081,100,119\nfallback=Weather_Foggy_Sun_Disc_Sunset_Color,223,223,223\nfallback=Weather_Foggy_Land_Fog_Day_Depth,1.0\nfallback=Weather_Foggy_Land_Fog_Night_Depth,1.9\nfallback=Weather_Foggy_Wind_Speed,0\nfallback=Weather_Foggy_Cloud_Speed,1.25\nfallback=Weather_Foggy_Glare_View,0.25\nfallback=Weather_Foggy_Ambient_Loop_Sound_ID,None\nfallback=Weather_Thunderstorm_Cloud_Texture,Tx_Sky_Thunder.dds\nfallback=Weather_Thunderstorm_Clouds_Maximum_Percent,0.66\nfallback=Weather_Thunderstorm_Transition_Delta,.030\nfallback=Weather_Thunderstorm_Sky_Sunrise_Color,035,036,039\nfallback=Weather_Thunderstorm_Sky_Day_Color,097,104,115\nfallback=Weather_Thunderstorm_Sky_Sunset_Color,035,036,039\nfallback=Weather_Thunderstorm_Sky_Night_Color,019,020,022\nfallback=Weather_Thunderstorm_Fog_Sunrise_Color,070,074,085\nfallback=Weather_Thunderstorm_Fog_Day_Color,097,104,115\nfallback=Weather_Thunderstorm_Fog_Sunset_Color,070,074,085\nfallback=Weather_Thunderstorm_Fog_Night_Color,019,020,022\nfallback=Weather_Thunderstorm_Ambient_Sunrise_Color,054,054,054\nfallback=Weather_Thunderstorm_Ambient_Day_Color,090,090,090\nfallback=Weather_Thunderstorm_Ambient_Sunset_Color,054,054,054\nfallback=Weather_Thunderstorm_Ambient_Night_Color,049,051,054\nfallback=Weather_Thunderstorm_Sun_Sunrise_Color,091,099,122\nfallback=Weather_Thunderstorm_Sun_Day_Color,138,144,155\nfallback=Weather_Thunderstorm_Sun_Sunset_Color,096,101,117\nfallback=Weather_Thunderstorm_Sun_Night_Color,055,076,110\nfallback=Weather_Thunderstorm_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Thunderstorm_Land_Fog_Day_Depth,1\nfallback=Weather_Thunderstorm_Land_Fog_Night_Depth,1.15\nfallback=Weather_Thunderstorm_Wind_Speed,.5\nfallback=Weather_Thunderstorm_Cloud_Speed,3\nfallback=Weather_Thunderstorm_Glare_View,0\nfallback=Weather_Thunderstorm_Rain_Loop_Sound_ID,rain heavy\nfallback=Weather_Thunderstorm_Using_Precip,1\nfallback=Weather_Thunderstorm_Rain_Diameter,600\nfallback=Weather_Thunderstorm_Rain_Height_Min,200\nfallback=Weather_Thunderstorm_Rain_Height_Max,700\nfallback=Weather_Thunderstorm_Rain_Threshold,0.6\nfallback=Weather_Thunderstorm_Max_Raindrops,650\nfallback=Weather_Thunderstorm_Rain_Entrance_Speed,5\nfallback=Weather_Thunderstorm_Ambient_Loop_Sound_ID,None\nfallback=Weather_Thunderstorm_Flash_Decrement,4\nfallback=Weather_Rain_Cloud_Texture,Tx_Sky_Rainy.dds\nfallback=Weather_Rain_Clouds_Maximum_Percent,0.66\nfallback=Weather_Rain_Transition_Delta,.015\nfallback=Weather_Rain_Sky_Sunrise_Color,071,074,075\nfallback=Weather_Rain_Sky_Day_Color,116,120,122\nfallback=Weather_Rain_Sky_Sunset_Color,073,073,073\nfallback=Weather_Rain_Sky_Night_Color,024,025,026\nfallback=Weather_Rain_Fog_Sunrise_Color,071,074,075\nfallback=Weather_Rain_Fog_Day_Color,116,120,122\nfallback=Weather_Rain_Fog_Sunset_Color,073,073,073\nfallback=Weather_Rain_Fog_Night_Color,024,025,026\nfallback=Weather_Rain_Ambient_Sunrise_Color,097,090,088\nfallback=Weather_Rain_Ambient_Day_Color,105,110,113\nfallback=Weather_Rain_Ambient_Sunset_Color,088,097,097\nfallback=Weather_Rain_Ambient_Night_Color,050,055,067\nfallback=Weather_Rain_Sun_Sunrise_Color,131,122,120\nfallback=Weather_Rain_Sun_Day_Color,149,157,170\nfallback=Weather_Rain_Sun_Sunset_Color,120,126,131\nfallback=Weather_Rain_Sun_Night_Color,050,062,101\nfallback=Weather_Rain_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Rain_Land_Fog_Day_Depth,.8\nfallback=Weather_Rain_Land_Fog_Night_Depth,.8\nfallback=Weather_Rain_Wind_Speed,.3\nfallback=Weather_Rain_Cloud_Speed,2\nfallback=Weather_Rain_Glare_View,0\nfallback=Weather_Rain_Rain_Loop_Sound_ID,Rain\nfallback=Weather_Rain_Using_Precip,1\nfallback=Weather_Rain_Rain_Diameter,600\nfallback=Weather_Rain_Rain_Height_Min,200\nfallback=Weather_Rain_Rain_Height_Max,700\nfallback=Weather_Rain_Rain_Threshold,0.6\nfallback=Weather_Rain_Rain_Entrance_Speed,7\nfallback=Weather_Rain_Ambient_Loop_Sound_ID,None\nfallback=Weather_Rain_Max_Raindrops,450\nfallback=Weather_Overcast_Cloud_Texture,Tx_Sky_Overcast.dds\nfallback=Weather_Overcast_Clouds_Maximum_Percent,1.0\nfallback=Weather_Overcast_Transition_Delta,.015\nfallback=Weather_Overcast_Sky_Sunrise_Color,091,099,106\nfallback=Weather_Overcast_Sky_Day_Color,143,146,149\nfallback=Weather_Overcast_Sky_Sunset_Color,108,115,121\nfallback=Weather_Overcast_Sky_Night_Color,019,022,025\nfallback=Weather_Overcast_Fog_Sunrise_Color,091,099,106\nfallback=Weather_Overcast_Fog_Day_Color,143,146,149\nfallback=Weather_Overcast_Fog_Sunset_Color,108,115,121\nfallback=Weather_Overcast_Fog_Night_Color,019,022,025\nfallback=Weather_Overcast_Ambient_Sunrise_Color,084,088,092\nfallback=Weather_Overcast_Ambient_Day_Color,093,096,105\nfallback=Weather_Overcast_Ambient_Sunset_Color,083,077,075\nfallback=Weather_Overcast_Ambient_Night_Color,057,060,066\nfallback=Weather_Overcast_Sun_Sunrise_Color,087,125,163\nfallback=Weather_Overcast_Sun_Day_Color,163,169,183\nfallback=Weather_Overcast_Sun_Sunset_Color,085,103,157\nfallback=Weather_Overcast_Sun_Night_Color,032,054,100\nfallback=Weather_Overcast_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Overcast_Land_Fog_Day_Depth,.70\nfallback=Weather_Overcast_Land_Fog_Night_Depth,.70\nfallback=Weather_Overcast_Wind_Speed,.2\nfallback=Weather_Overcast_Cloud_Speed,1.5\nfallback=Weather_Overcast_Glare_View,0\nfallback=Weather_Overcast_Ambient_Loop_Sound_ID,None\nfallback=Weather_Snow_Cloud_Texture,Tx_Sky_Snow.dds\nfallback=Weather_Snow_Clouds_Maximum_Percent,1.0\nfallback=Weather_Snow_Transition_Delta,.015\nfallback=Weather_Snow_Sky_Sunrise_Color,106,091,091\nfallback=Weather_Snow_Sky_Day_Color,153,158,166\nfallback=Weather_Snow_Sky_Sunset_Color,096,115,134\nfallback=Weather_Snow_Sky_Night_Color,031,035,039\nfallback=Weather_Snow_Fog_Sunrise_Color,106,091,091\nfallback=Weather_Snow_Fog_Day_Color,153,158,166\nfallback=Weather_Snow_Fog_Sunset_Color,096,115,134\nfallback=Weather_Snow_Fog_Night_Color,031,035,039\nfallback=Weather_Snow_Ambient_Sunrise_Color,092,084,084\nfallback=Weather_Snow_Ambient_Day_Color,093,096,105\nfallback=Weather_Snow_Ambient_Sunset_Color,070,079,087\nfallback=Weather_Snow_Ambient_Night_Color,049,058,068\nfallback=Weather_Snow_Sun_Sunrise_Color,141,109,109\nfallback=Weather_Snow_Sun_Day_Color,163,169,183\nfallback=Weather_Snow_Sun_Sunset_Color,101,121,141\nfallback=Weather_Snow_Sun_Night_Color,055,066,077\nfallback=Weather_Snow_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Snow_Land_Fog_Day_Depth,1.0\nfallback=Weather_Snow_Land_Fog_Night_Depth,1.2\nfallback=Weather_Snow_Wind_Speed,0\nfallback=Weather_Snow_Cloud_Speed,1.5\nfallback=Weather_Snow_Glare_View,0\nfallback=Weather_Snow_Snow_Diameter,800\nfallback=Weather_Snow_Snow_Height_Min,400\nfallback=Weather_Snow_Snow_Height_Max,700\nfallback=Weather_Snow_Snow_Entrance_Speed,6\nfallback=Weather_Snow_Max_Snowflakes,750\nfallback=Weather_Snow_Ambient_Loop_Sound_ID,None\nfallback=Weather_Snow_Snow_Threshold,0.5\nfallback=Weather_Blizzard_Cloud_Texture,Tx_Sky_Blizzard.dds\nfallback=Weather_Blizzard_Clouds_Maximum_Percent,1.0\nfallback=Weather_Blizzard_Transition_Delta,.030\nfallback=Weather_Blizzard_Sky_Sunrise_Color,091,099,106\nfallback=Weather_Blizzard_Sky_Day_Color,121,133,145\nfallback=Weather_Blizzard_Sky_Sunset_Color,108,115,121\nfallback=Weather_Blizzard_Sky_Night_Color,027,029,031\nfallback=Weather_Blizzard_Fog_Sunrise_Color,091,099,106\nfallback=Weather_Blizzard_Fog_Day_Color,121,133,145\nfallback=Weather_Blizzard_Fog_Sunset_Color,108,115,121\nfallback=Weather_Blizzard_Fog_Night_Color,021,024,028\nfallback=Weather_Blizzard_Ambient_Sunrise_Color,084,088,092\nfallback=Weather_Blizzard_Ambient_Day_Color,093,096,105\nfallback=Weather_Blizzard_Ambient_Sunset_Color,083,077,075\nfallback=Weather_Blizzard_Ambient_Night_Color,053,062,070\nfallback=Weather_Blizzard_Sun_Sunrise_Color,114,128,146\nfallback=Weather_Blizzard_Sun_Day_Color,163,169,183\nfallback=Weather_Blizzard_Sun_Sunset_Color,106,114,136\nfallback=Weather_Blizzard_Sun_Night_Color,057,066,074\nfallback=Weather_Blizzard_Sun_Disc_Sunset_Color,128,128,128\nfallback=Weather_Blizzard_Land_Fog_Day_Depth,2.8\nfallback=Weather_Blizzard_Land_Fog_Night_Depth,3.0\nfallback=Weather_Blizzard_Wind_Speed,.9\nfallback=Weather_Blizzard_Cloud_Speed,7.5\nfallback=Weather_Blizzard_Glare_View,0\nfallback=Weather_Blizzard_Ambient_Loop_Sound_ID, Blizzard\nfallback=Weather_Blizzard_Storm_Threshold,.50\n"
  },
  {
    "path": "files/org.openmw.cs.desktop",
    "content": "[Desktop Entry]\nType=Application\nName=OpenMW Content Editor\nGenericName=Content Editor\nComment=A replacement for the Morrowind Construction Set.\nKeywords=Morrowind;Construction Set;Creation Kit Editor;Set;Kit;\nTryExec=openmw-cs\nExec=openmw-cs\nIcon=openmw-cs\nCategories=Game;RolePlaying;\n"
  },
  {
    "path": "files/org.openmw.launcher.desktop",
    "content": "[Desktop Entry]\nType=Application\nName=OpenMW Launcher\nGenericName=Role Playing Game\nComment=An engine replacement for The Elder Scrolls III: Morrowind\nKeywords=Morrowind;Reimplementation Mods;esm;bsa;\nTryExec=openmw-launcher\nExec=openmw-launcher\nIcon=openmw\nCategories=Game;RolePlaying;\n"
  },
  {
    "path": "files/settings-default.cfg",
    "content": "# WARNING: If this file is named settings-default.cfg or was generated\n# from defaults.bin, then editing this file might have no effect, as\n# these settings may be overwritten by your user settings.cfg file\n# (see documentation for its location).\n#\n# This file provides minimal documentation for each setting, and\n# ranges of recommended values.  For detailed explanations of the\n# significance of each setting, interaction with other settings, hard\n# limits on value ranges and more information in general, please read\n# the detailed documentation at:\n#\n#   https://openmw.readthedocs.io/en/master/reference/modding/settings/index.html\n#\n\n[Camera]\n\n# Near clipping plane (>0.0, e.g. 0.01 to 18.0).\nnear clip = 3\n\n# Cull objects that occupy less than 'small feature culling pixel size' on the screen.\nsmall feature culling = true\n\nsmall feature culling pixel size = 2.0\n\n# Maximum visible distance. Caution: this setting\n# can dramatically affect performance, see documentation for details.\nviewing distance = 7168.0\n\n# Camera field of view in degrees (e.g. 30.0 to 110.0).\n# Does not affect the player's hands in the first person camera.\nfield of view = 60.0\n\n# Field of view for first person meshes (i.e. the player's hands)\n# Best to leave this at the default since vanilla assets are not complete enough to adapt to high FoV's. Too low FoV would clip the hands off screen.\nfirst person field of view = 60.0\n\n# Distance from the camera to the character in third person mode.\nthird person camera distance = 192\n\n# If enabled then third person camera is positioned above character's shoulder and crosshair is visible.\nview over shoulder = false\n\n# Makes sense only if 'view over shoulder' is true. First number is horizontal offset (negative value means offset to the left), second number is vertical offset.\nview over shoulder offset = 30 -10\n\n# Switch shoulder automatically when player is close to an obstacle.\nauto switch shoulder = true\n\n# Slightly pulls camera away when the character moves. Works only in 'view over shoulder' mode. Set to 0 to disable.\nzoom out when move coef = 20\n\n# Automatically enable preview mode when player doesn't move.\npreview if stand still = false\n\n# Rotate the character to the view direction after exiting preview mode.\ndeferred preview rotation = true\n\n# Enables head bobbing in first person mode\nhead bobbing = false\n\n# Length of each step\nhead bobbing step = 90.0\n\n# Amplitude of the bobbing effect\nhead bobbing height = 3.0\n\n# Maximum camera roll angle (degrees)\nhead bobbing roll = 0.2\n\n[Cells]\n\n# Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled.\npreload enabled = true\n\n# The number of threads to be used for preloading operations.\npreload num threads = 1\n\n# Preload adjacent cells when moving close to an exterior cell border.\npreload exterior grid = true\n\n# Preload possible fast travel destinations.\npreload fast travel = false\n\n# Preload the locations that doors lead to.\npreload doors = true\n\n# Preloading distance threshold\npreload distance = 1000\n\n# Controls whether or not the nodes/collision shapes are pre-\"instanced\" (i.e. cloned) when a cell is preloaded.\n# Enabling this option slightly reduces the time it takes to transition into a preloaded cell, but also results in higher memory usage\n# proportional to the number of cells that are preloaded.\npreload instances = true\n\n# The minimum amount of cells in the preload cache before unused cells start to get thrown out (see \"preload cell expiry delay\").\n# This value should be lower or equal to 'preload cell cache max'.\npreload cell cache min = 12\n\n# The maximum amount of cells in the preload cache. A too high value could cause you to run out of memory.\n# You may need to reduce this setting when running lots of mods or high-res texture replacers.\npreload cell cache max = 20\n\n# How long to keep preloaded cells in cache after they're no longer referenced/required (in seconds)\npreload cell expiry delay = 5\n\n# The predicted position of the player N seconds in the future will be used for preloading cells and distant terrain\nprediction time = 1\n\n# How long to keep models/textures/collision shapes in cache after they're no longer referenced/required (in seconds)\ncache expiry delay = 5\n\n# Affects the time to be set aside each frame for graphics preloading operations\ntarget framerate = 60\n\n# The count of pointers, that will be saved for a faster search by object ID.\npointers cache size = 40\n\n[Terrain]\n\n# If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells\ndistant terrain = false\n\n# Controls how the Quad Tree is split. This affects Vertex LOD, Texture LOD and load times. Values > 1 increase detail, values < 1 reduce detail.\nlod factor = 1.0\n\n# Controls only the Vertex LOD. Change in increments of 1, each change doubles (or halves) the number of vertices. Values > 0 increase detail, values < 0 reduce detail.\nvertex lod mod = 0\n\n# Controls when the distant terrain will flip to composited textures instead of high-detail textures, should be >= -3.\n# Higher value is more detailed textures.\ncomposite map level = 0\n\n# Controls the resolution of composite maps.\ncomposite map resolution = 512\n\n# Controls the maximum size of composite geometry, should be >= 1.0. With low values there will be many small chunks, with high values - lesser count of bigger chunks.\nmax composite geometry size = 4.0\n\n# Use object paging for non active cells\nobject paging = true\n\n# Use object paging for active cells grid\nobject paging active grid = true\n\n# Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory.\nobject paging merge factor = 250\n\n# Cull objects smaller than this size divided by distance\nobject paging min size = 0.01\n\n# Adjusts 'min size' based on merging decision. Allows inexpensive objects to be rendered from a greater distance.\nobject paging min size merge factor = 0.3\n\n# Controls how inexpensive an object needs to be to utilize 'min size merge factor'.\nobject paging min size cost multiplier = 25\n\n# Assign a random color to merged batches.\nobject paging debug batches = false\n\n[Fog]\n\n# If true, use extended fog parameters for distant terrain not controlled by\n# viewing distance. If false, use the standard fog calculations.\nuse distant fog = false\n\ndistant land fog start = 16384\n\ndistant land fog end = 40960\n\ndistant underwater fog start = -4096\n\ndistant underwater fog end = 2457.6\n\ndistant interior fog start = 0\n\ndistant interior fog end = 16384\n\n[Map]\n\n# Size of each exterior cell in pixels in the world map. (e.g. 12 to 24).\n# Warning: affects explored areas in save files, see documentation.\nglobal map cell size = 18\n\n# Zoom level in pixels for HUD map widget.  64 is one cell, 128 is 1/4\n# cell, 256 is 1/8 cell.  See documentation for details. (e.g. 64 to 256).\nlocal map hud widget size = 256\n\n# Enables Fog of War rendering on the HUD map. Default is Off since with default settings\n# the map is so small that the fog would not obscure anything, just darken the edges slightly.\nlocal map hud fog of war = false\n\n# Resolution of local map in GUI window in pixels.  See documentation\n# for details which may affect cell load performance. (e.g. 128 to 1024).\nlocal map resolution = 256\n\n# Size of local map in GUI window in pixels.  (e.g. 256 to 1024).\nlocal map widget size = 512\n\n# If true, map in world mode, otherwise in local mode\nglobal = false\n\n[GUI]\n\n# Scales GUI window and widget size. (<1.0 is smaller, >1.0 is larger).\nscaling factor = 1.0\n\n# Size of in-game fonts\nfont size = 16\n\n# Resolution of TrueType fonts glyphs\nttf resolution = 96\n\n# Transparency of GUI windows (0.0 to 1.0, transparent to opaque).\nmenu transparency = 0.84\n\n# Time until tool tip appears when hovering over an object (0.0 is\n# instantly, 1.0 is the maximum delay of about 1.5 seconds).\ntooltip delay = 0.0\n\n# Stretch menus, load screens, etc. to the window aspect ratio.\nstretch menu background = false\n\n# Subtitles for NPC spoken dialog and some sound effects.\nsubtitles = false\n\n# Red flash visually showing player damage.\nhit fader = true\n\n# Werewolf overlay border around screen or window.\nwerewolf overlay = true\n\n# Color for tool tips and crosshair when owned by an NPC (R G B A).\ncolor background owned = 0.15 0.0 0.0 1.0\ncolor crosshair owned = 1.0 0.15 0.15 1.0\n\n# Controls whether Arrow keys, Movement keys, Tab/Shift-Tab and Spacebar/Enter/Activate may be used to navigate GUI buttons.\nkeyboard navigation = true\n\n# Apply the defined color to dialogue topic.\ncolor topic enable = false\n\n# The color of dialogue topic keywords that gives unique actor responses\n# Format R G B A or empty for no special formatting\n# Default to blue\ncolor topic specific = 0.45 0.5 0.8 1\n\n# The color of dialogue topic keywords that gives already read responses\n# Format R G B A or empty for no special formatting\n# Default to grey\ncolor topic exhausted = 0.3 0.3 0.3 1\n\n[HUD]\n\n# Displays the crosshair or reticle when not in GUI mode.\ncrosshair = true\n\n[Game]\n\n# Color crosshair and tool tip when object is owned by an NPC. (O is\n# no color, 1 is tool tip only, 2 is crosshair only, and 3 is both).\nshow owned = 0\n\n# Show damage bonus of arrow and bolts.\nshow projectile damage = false\n\n# Show additional weapon info: reach and attack speed\nshow melee info = false\n\n# Show success probability in self-enchant dialog\nshow enchant chance = false\n\n# Always use the best mode of attack: e.g. chop, slash or thrust.\nbest attack = false\n\n# Difficulty.  Expressed as damage dealt and received. (e.g. -100 to 100).\ndifficulty = 0\n\n# The maximum range of actor AI, animations and physics updates.\nactors processing range = 7168\n\n# Make reflected Absorb spells have no practical effect, like in Morrowind.\nclassic reflected absorb spells behavior = true\n\n# Show duration of magic effect and lights in the spells window.\nshow effect duration = false\n\n# Prevent merchants from equipping items that are sold to them.\nprevent merchant equipping = false\n\n# Make enchanted weaponry without Magical flag bypass normal weapons resistance\nenchanted weapons are magical = true\n\n# Make player followers and escorters start combat with enemies who have started combat with them\n# or the player. Otherwise they wait for the enemies or the player to do an attack first.\nfollowers attack on sight = false\n\n# Can loot non-fighting actors during death animation\ncan loot during death animation = true\n\n# Make the value of filled soul gems dependent only on soul magnitude (with formula from the Morrowind Code Patch)\nrebalance soul gem values = false\n\n# Allow to load per-group KF-files from Animations folder\nuse additional anim sources = false\n\n# Make the disposition change of merchants caused by barter dealings permanent\nbarter disposition change is permanent = false\n\n# Uses the MCP formula (damage * (strength / 40)) to factor Strength into hand-to-hand combat.\n# (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and\n# 2 means werewolves are ignored)\nstrength influences hand to hand = 0\n\n# Render holstered weapons (with quivers and scabbards), requires modded assets\nweapon sheathing = false\n\n# Render holstered shield when it is not in actor's hands, requires modded assets\nshield sheathing = false\n\n# Allow non-standard ammunition solely to bypass normal weapon resistance or weakness\nonly appropriate ammunition bypasses resistance = false\n\n# Use casting animations for magic items, just as for spells\nuse magic item animations = false\n\n# Don't use race weight in NPC movement speed calculations\nnormalise race speed = false\n\n# Adjusts the number of projectiles you can enchant at once:\n# count = (soul gem charge * projectiles enchant multiplier) / enchantment strength\n# A value of 0 means that you can only enchant one projectile.\nprojectiles enchant multiplier = 0\n\n# Make Damage Fatigue magic effect uncapped like Drain Fatigue effect.\n# This means that unlike Morrowind you will be able to knock down actors using this effect.\nuncapped damage fatigue = false\n\n# Turn lower body to movement direction. 'true' makes diagonal movement more realistic.\nturn to movement direction = false\n\n# Makes all movements of NPCs and player more smooth.\nsmooth movement = false\n\n# Max delay of turning (in seconds) if player drastically changes direction on the run.\nsmooth movement player turning delay = 0.333\n\n# All actors avoid collisions with other actors.\nNPCs avoid collisions = false\n\n# Give way to moving actors when idle. Requires 'NPCs avoid collisions' to be enabled.\nNPCs give way = true\n\n# Makes player swim a bit upward from the line of sight.\nswim upward correction = false\n\n# Strength of the 'swim upward correction' effect (if enabled).\nswim upward coef = 0.2\n\n# Make the training skills proposed by a trainer based on its base attribute instead of its modified ones\ntrainers training skills based on base skill = false\n\n# Make stealing items from NPCs that were knocked down possible during combat.\nalways allow stealing from knocked out actors = false\n\n# Enables visually harvesting plants for models that support it.\ngraphic herbalism = true\n\n# Give actors an ability to swim over water surface when they follow other actor independently from their ability to swim\n# (true, false)\nallow actors to follow over water surface = true\n\n[General]\n\n# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).\nanisotropy = 4\n\n# File format for screenshots.  (jpg, png, tga, and possibly more).\nscreenshot format = png\n\n# Texture magnification filter type.  (nearest or linear).\ntexture mag filter = linear\n\n# Texture minification filter type.  (nearest or linear).\ntexture min filter = linear\n\n# Texture mipmap type.  (none, nearest, or linear).\ntexture mipmap = nearest\n\n[Shaders]\n\n# Force rendering with shaders. By default, only bump-mapped objects will use shaders.\n# Enabling this option may cause slightly different visuals if the \"clamp lighting\" option\n# is set to false. Otherwise, there should not be a visual difference.\nforce shaders = false\n\n# Force the use of per pixel lighting. By default, only bump mapped objects use per-pixel lighting.\n# Has no effect if the 'force shaders' option is false.\n# Enabling per-pixel lighting can result in visual differences to the original MW engine. It is not\n# recommended to enable this option when using vanilla Morrowind files, because certain lights in Morrowind\n# rely on vertex lighting to look as intended.\nforce per pixel lighting = false\n\n# Restrict the amount of lighting that an object can receive to a maximum of (1,1,1).\n# Only affects objects that render with shaders (see 'force shaders' option). Always affects terrain.\n# Setting this option to 'true' results in fixed-function compatible lighting, but the lighting\n# may appear 'dull' and there might be color shifts.\n# Setting this option to 'false' results in more realistic lighting.\nclamp lighting = true\n\n# If this option is enabled, normal maps are automatically recognized and used if they are named appropriately\n# (see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds).\n# If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file).\n# Affects objects.\nauto use object normal maps = false\n\n# If this option is enabled, specular maps are automatically recognized and used if they are named appropriately\n# (see 'specular map pattern', e.g. for a base texture foo.dds, the specular map texture would have to be named foo_spec.dds).\n# If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.osg file, not supported in .nif files).\n# Affects objects.\nauto use object specular maps = false\n\n# See 'auto use object normal maps'. Affects terrain.\nauto use terrain normal maps = false\n\n# If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. The texture\n# must contain the layer color in the RGB channel (as usual), and a specular multiplier in the alpha channel.\nauto use terrain specular maps = false\n\n# The filename pattern to probe for when detecting normal maps (see 'auto use object normal maps', 'auto use terrain normal maps')\nnormal map pattern = _n\n\n# Alternative filename pattern to probe for when detecting normal maps. Files with this pattern are expected to include 'height' in the alpha channel.\n# This height is used for parallax effects. Works for both terrain and objects.\nnormal height map pattern = _nh\n\n# The filename pattern to probe for when detecting object specular maps (see 'auto use object specular maps')\nspecular map pattern = _spec\n\n# The filename pattern to probe for when detecting terrain specular maps (see 'auto use terrain specular maps')\nterrain specular map pattern = _diffusespec\n\n# Apply lighting to reflections on the environment-mapped objects like in Morrowind Code Patch.\n# Affected objects use shaders.\napply lighting to environment maps = false\n\n# Determine fog intensity based on the distance from the eye point.\n# This makes fogging independent from the viewing angle. Shaders will be used to render all objects.\nradial fog = false\n\n# Internal handling of lights, ignored if 'force shaders' is off. \"legacy\"\n# provides fixed function pipeline emulation.\"shaders compatibility\" (default)\n# uncaps the light limit, enables groundcover lighting, and uses a modified\n# attenuation formula to reduce popping and light seams. \"shaders\" comes with\n# all these benefits and is meant for larger light limits, but may not be\n# supported on older hardware and may be slower on weaker hardware when\n# 'force per pixel lighting' is enabled.\nlighting method = shaders compatibility\n\n# Sets the bounding sphere multiplier of light sources if 'lighting method' is\n# not 'legacy'. These are used to determine if an object should receive\n# lighting. Higher values will allow for smoother transitions of light sources,\n# but may carry a performance cost and requires a higher number of 'max lights'\n# set.\nlight bounds multiplier = 1.65\n\n# The distance from the camera at which lights fade away completely.\n# Set to 0 to disable fading.\nmaximum light distance = 8192\n\n# Fraction of the maximum distance at which lights begin to gradually fade away.\nlight fade start = 0.85\n\n# Set maximum number of lights per object.\n# When 'lighting method' is set to 'legacy', this setting will have no effect.\nmax lights = 8\n\n# Sets minimum ambient brightness of interior cells. Levels below this threshold will have their\n# ambient values adjusted to balance the darker interiors.\n# When 'lighting method' is set to 'legacy', this setting will have no effect.\nminimum interior brightness = 0.08\n\n# Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage.\n# This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation.\n# When MSAA is off, this setting will have no visible effect, but might have a performance cost.\nantialias alpha test = false\n\n[Input]\n\n# Capture control of the cursor prevent movement outside the window.\ngrab cursor = true\n\n# Key controlling sneak toggles setting instead of being held down.\ntoggle sneak = false\n\n# Player is running by default.\nalways run = false\n\n# Camera sensitivity when not in GUI mode. (>0.0, e.g. 0.1 to 5.0).\ncamera sensitivity = 1.0\n\n# Vertical camera sensitivity multiplier when not in GUI mode.\n# (>0.0, Because it's a multiplier values should be near 1.0)\ncamera y multiplier = 1.0\n\n# Invert the horizontal axis while not in GUI mode.\ninvert x axis = false\n\n# Invert the vertical axis while not in GUI mode.\ninvert y axis = false\n\n# Enable controller support.\nenable controller = true\n\n# Emulated gamepad cursor sensitivity.\ngamepad cursor speed = 1.0\n\n# Set dead zone for joysticks (gamepad or on-screen ones)\njoystick dead zone = 0.1\n\n# Enable gyroscope support.\nenable gyroscope = false\n\n# Gyroscope axis that corresponds to horizontal camera axis.\ngyro horizontal axis = -x\n\n# Gyroscope axis that corresponds to vertical camera axis.\ngyro vertical axis = y\n\n# The minimum gyroscope movement that is able to rotate the camera.\ngyro input threshold = 0.01\n\n# Horizontal camera axis sensitivity to gyroscope movement.\ngyro horizontal sensitivity = 1.0\n\n# Vertical camera axis sensitivity to gyroscope movement.\ngyro vertical sensitivity = 1.0\n\n[Saves]\n\n# Name of last character played, and default for loading save files.\ncharacter =\n\n# Automatically save the game whenever the player rests.\nautosave = true\n\n# Display the time played on each save file in the load menu.\ntimeplayed = false\n\n# The maximum number of quick (or auto) save slots to have.\n# If all slots are used, the  oldest save is reused\nmax quicksaves = 1\n\n[Sound]\n\n# Name of audio device file.  Blank means use the default device.\ndevice =\n\n# Volumes are 0.0 for silent and 1.0 for the maximum volume.\n\n# Master volume.  Controls all other volumes.\nmaster volume = 1.0\n\n# Footsteps volume.\nfootsteps volume = 0.2\n\n# Music tracks volume.\nmusic volume = 0.5\n\n# Sound effects volume.\nsfx volume = 1.0\n\n# Voice dialog volume.\nvoice volume = 0.8\n\n# Minimum size to use for the sound buffer cache, in MB. When the cache is\n# filled, old buffers will be unloaded until it's using no more than this much\n# memory. Must be less than or equal to 'buffer cache max'.\nbuffer cache min = 56\n\n# Maximum size to use for the sound buffer cache, in MB. The cache can use up\n# to this much memory until old buffers get purged.\nbuffer cache max = 64\n\n# Specifies whether to enable HRTF processing. Valid values are: -1 = auto,\n# 0 = off, 1 = on.\nhrtf enable = -1\n\n# Specifies which HRTF to use when HRTF is used. Blank means use the default.\nhrtf =\n\n[Video]\n\n# Resolution of the OpenMW window or screen.\nresolution x = 800\nresolution y = 600\n\n# OpenMW takes complete control of the screen.\nfullscreen = false\n\n# Determines which screen OpenMW is on.  (>=0).\nscreen = 0\n\n# Minimize OpenMW if it loses cursor or keyboard focus.\nminimize on focus loss = true\n\n# An operating system border is drawn around the OpenMW window.\nwindow border = true\n\n# Anti-aliasing reduces jagged polygon edges.  (0, 2, 4, 8, 16).\nantialiasing = 0\n\n# Enable vertical syncing to reduce tearing defects.\nvsync = false\n\n# Maximum frames per second. 0.0 is unlimited, or >0.0 to limit.\nframerate limit = 300\n\n# Game video contrast.  (>0.0).  No effect in Linux.\ncontrast = 1.0\n\n# Video gamma setting.  (>0.0).  No effect in Linux.\ngamma = 1.0\n\n# Type of screenshot to take (regular, cylindrical, spherical or planet), optionally followed by\n# screenshot width, height and cubemap resolution in pixels. (e.g. spherical 1600 1000 1200)\nscreenshot type = regular\n\n[Water]\n\n# Enable water shader with reflections and optionally refraction.\nshader = false\n\n# Reflection and refraction texture size in pixels. (512, 1024, 2048).\nrtt size = 512\n\n# Enable refraction which affects visibility through water plane.\nrefraction = false\n\n# Draw objects on water reflections.\nreflection detail = 2\n\n# Overrides the value in '[Camera] small feature culling pixel size' specifically for water reflection/refraction textures.\nsmall feature culling pixel size = 20.0\n\n# By what factor water downscales objects. Only works with water shader and refractions on.\nrefraction scale = 1.0\n\n[Windows]\n\n# Location and sizes of windows as a fraction of the OpenMW window or\n# screen size. (0.0 to 1.0).  X & Y, Width & Height.\n\n# Stats window displaying level, race, class, skills and stats.\nstats x = 0.015\nstats y = 0.015\nstats w = 0.4275\nstats h = 0.45\nstats maximized x = 0.0\nstats maximized y = 0.0\nstats maximized w = 1.0\nstats maximized h = 1.0\nstats pin = false\nstats hidden = false\nstats maximized = false\n\n# Spells window displaying powers, spells, and magical items.\nspells x = 0.63\nspells y = 0.39\nspells w = 0.36\nspells h = 0.51\nspells maximized x = 0.0\nspells maximized y = 0.0\nspells maximized w = 1.0\nspells maximized h = 1.0\nspells pin = false\nspells hidden = false\nspells maximized = false\n\n# Local and world map window.\nmap x = 0.63\nmap y = 0.015\nmap w = 0.36\nmap h = 0.37\nmap maximized x = 0.0\nmap maximized y = 0.0\nmap maximized w = 1.0\nmap maximized h = 1.0\nmap pin = false\nmap hidden = false\nmap maximized = false\n\n# Player inventory window when explicitly opened.\ninventory x = 0.0\ninventory y = 0.54\ninventory w = 0.45\ninventory h = 0.38\ninventory maximized x = 0.0\ninventory maximized y = 0.0\ninventory maximized w = 1.0\ninventory maximized h = 1.0\ninventory pin = false\ninventory hidden = false\ninventory maximized = false\n\n# Player inventory window when searching a container.\ninventory container x = 0.015\ninventory container y = 0.54\ninventory container w = 0.45\ninventory container h = 0.38\ninventory container maximized x = 0.0\ninventory container maximized y = 0.5\ninventory container maximized w = 1.0\ninventory container maximized h = 0.5\ninventory container maximized = false\n\n# Player inventory window when bartering with a shopkeeper.\ninventory barter x = 0.015\ninventory barter y = 0.54\ninventory barter w = 0.45\ninventory barter h = 0.38\ninventory barter maximized x = 0.0\ninventory barter maximized y = 0.5\ninventory barter maximized w = 1.0\ninventory barter maximized h = 0.5\ninventory barter maximized = false\n\n# Player inventory window when trading with a companion.\ninventory companion x = 0.015\ninventory companion y = 0.54\ninventory companion w = 0.45\ninventory companion h = 0.38\ninventory companion maximized x = 0.0\ninventory companion maximized y = 0.5\ninventory companion maximized w = 1.0\ninventory companion maximized h = 0.5\ninventory companion maximized = false\n\n# Dialog window for talking with NPCs.\ndialogue x = 0.15\ndialogue y = 0.5\ndialogue w = 0.7\ndialogue h = 0.45\ndialogue maximized x = 0.0\ndialogue maximized y = 0.0\ndialogue maximized w = 1.0\ndialogue maximized h = 1.0\ndialogue maximized = false\n\n# Alchemy window for crafting potions.\nalchemy x = 0.25\nalchemy y = 0.25\nalchemy w = 0.5\nalchemy h = 0.5\nalchemy maximized x = 0.0\nalchemy maximized y = 0.0\nalchemy maximized w = 1.0\nalchemy maximized h = 1.0\nalchemy maximized = false\n\n# Console command window for debugging commands.\nconsole x = 0.015\nconsole y = 0.015\nconsole w = 1.0\nconsole h = 0.5\nconsole maximized x = 0.0\nconsole maximized y = 0.0\nconsole maximized w = 1.0\nconsole maximized h = 1.0\nconsole maximized = false\n\n# Container inventory when searching a container.\ncontainer x = 0.49\ncontainer y = 0.54\ncontainer w = 0.39\ncontainer h = 0.38\ncontainer maximized x = 0.0\ncontainer maximized y = 0.0\ncontainer maximized w = 1.0\ncontainer maximized h = 0.5\ncontainer maximized = false\n\n# NPC inventory window when bartering with a shopkeeper.\nbarter x = 0.6\nbarter y = 0.27\nbarter w = 0.38\nbarter h = 0.63\nbarter maximized x = 0.0\nbarter maximized y = 0.0\nbarter maximized w = 1.0\nbarter maximized h = 0.5\nbarter maximized = false\n\n# NPC inventory window when trading with a companion.\ncompanion x = 0.6\ncompanion y = 0.27\ncompanion w = 0.38\ncompanion h = 0.63\ncompanion maximized x = 0.0\ncompanion maximized y = 0.0\ncompanion maximized w = 1.0\ncompanion maximized h = 0.5\ncompanion maximized = false\n\n[Navigator]\n\n# Enable navigator (true, false). When enabled background threads are started to build navmesh for world geometry.\n# Pathfinding system uses navmesh to build paths. When disabled only pathgrid is used to build paths.\nenable = true\n\n# Scale of NavMesh coordinates to world coordinates (value > 0.0). Recastnavigation builds voxels for world geometry.\n# Basically voxel size is 1 / \"cell size\". To reduce amount of voxels we apply scale factor, to make voxel size\n# \"recast scale factor\" / \"cell size\". Default value calculates by this equation:\n# sStepSizeUp * \"recast scale factor\" / \"cell size\" = 5 (max climb height should be equal to 4 voxels)\nrecast scale factor = 0.029411764705882353\n\n# The z-axis cell size to use for fields. (value > 0.0)\n# Defines voxel/grid/cell size. So their values have significant\n# side effects on all parameters defined in voxel units.\n# The minimum value for this parameter depends on the platform's floating point\n# accuracy, with the practical minimum usually around 0.05.\n# Same default value is used in RecastDemo.\ncell height = 0.2\n\n# The xy-plane cell size to use for fields. (value > 0.0)\n# Defines voxel/grid/cell size. So their values have significant\n# side effects on all parameters defined in voxel units.\n# The minimum value for this parameter depends on the platform's floating point\n# accuracy, with the practical minimum usually around 0.05.\n# Same default value is used in RecastDemo.\ncell size = 0.2\n\n# Sets the sampling distance to use when generating the detail mesh. (value = 0.0 or value >= 0.9)\ndetail sample dist = 6.0\n\n# The maximum distance the detail mesh surface should deviate from heightfield data. (value >= 0.0)\ndetail sample max error = 1.0\n\n# The maximum distance a simplfied contour's border edges should deviate the original raw contour. (value >= 0.0)\nmax simplification error = 1.3\n\n# The width and height of each tile. (value > 0)\ntile size = 128\n\n# The size of the non-navigable border around the heightfield. (value >= 0)\nborder size = 16\n\n# The maximum allowed length for contour edges along the border of the mesh. (value >= 0)\nmax edge len = 12\n\n# Maximum number of search nodes. (0 < value <= 65535)\nmax nav mesh query nodes = 2048\n\n# Maximum number of polygons per navmesh tile (value = 2^n, 0 < n < 22). Maximum number of navmesh tiles depends on\n# this value. 22 bits is a limit to store both tile identifier and polygon identifier (tiles = 2^(22 - log2(polygons))).\n# See recastnavigation for more details.\nmax polygons per tile = 4096\n\n# The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process. (value >= 3)\nmax verts per poly = 6\n\n# Any regions with a span count smaller than this value will, if possible, be merged with larger regions. (value >= 0)\nregion merge size = 20\n\n# The minimum number of cells allowed to form isolated island areas. (value >= 0)\nregion min size = 8\n\n# Number of background threads to update nav mesh (value >= 1)\nasync nav mesh updater threads = 1\n\n# Maximum total cached size of all nav mesh tiles in bytes (value >= 0)\nmax nav mesh tiles cache size = 268435456\n\n# Maximum size of path over polygons (value > 0)\nmax polygon path size = 1024\n\n# Maximum size of smoothed path (value > 0)\nmax smooth path size = 1024\n\n# Write recast mesh to file in .obj format for each use to update nav mesh (true, false)\nenable write recast mesh to file = false\n\n# Write NavMesh to file to be able to open by RecastDemo (true, false)\nenable write nav mesh to file = false\n\n# Write each recast mesh file with revision in name. Otherwise will rewrite same file. (true, false)\nenable recast mesh file name revision = false\n\n# Write each nav mesh file with revision in name. Otherwise will rewrite same file. (true, false)\nenable nav mesh file name revision = false\n\n# Write recast mesh file at path with this prefix\nrecast mesh path prefix =\n\n# Write nav mesh file at path with this prefix\nnav mesh path prefix =\n\n# Render nav mesh (true, false)\nenable nav mesh render = false\n\n# Render agents paths (true, false)\nenable agents paths render = false\n\n# Render recast mesh (true, false)\nenable recast mesh render = false\n\n# Max number of navmesh tiles (value >= 0)\nmax tiles number = 512\n\n# Min time duration for the same tile update in milliseconds (value >= 0)\nmin update interval ms = 250\n\n# Keep loading screen until navmesh is generated around the player for all tiles within manhattan distance (value >= 0).\n# Distance is measured in the number of tiles and can be only an integer value.\nwait until min distance to player = 5\n\n[Shadows]\n\n# Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if \"[Shaders]/force shaders\" was set to true.\nenable shadows = false\n\n# How many shadow maps to use - more of these means each shadow map texel covers less area, producing better looking shadows, but may decrease performance.\nnumber of shadow maps = 3\n\n# The distance from the camera at which shadows fade away completely. Set to 0 to make the distance infinite.\nmaximum shadow map distance = 8192\n\n# Fraction of the maximum distance at which shadows begin to gradually fade away.\nshadow fade start = 0.9\n\n# If true, allow shadow maps to overlap. Counter-intuitively, will produce better results when the light is behind the camera. When enabled, OpenMW uses Cascaded Shadow Maps and when disabled, it uses Parallel Split Shadow Maps.\nallow shadow map overlap = true\n\n# Indirectly controls where to split the shadow map(s). Values closer to 1.0 bring more detail closer to the camera (potentially excessively so), and values closer to 0.0 spread it more evenly across the whole viewing distance. 0.5 is recommended for most viewing distances by the original Parallel Split Shadow Maps paper, but this does not take into account use of a Light Space Perspective transformation, so other values may be preferable. If some of the terms used here go over your head, you might not want to change this, especially not without reading the associated papers first. When \"allow shadow map overlap\" is combined with a higher-than-default viewing distance, values closer to 1.0 will prevent nearby shadows losing a lot of quality.\nsplit point uniform logarithmic ratio = 0.5\n\n# Indirectly controls where to split the shadow map(s). Positive values move split points away from the camera and negative values move them towards the camera. Intended to be used in conjunction with changes to 'split point uniform logarithmic ratio' to counteract side effects, but may cause additional, more serious side effects. Read the Parallel Split Shadow Maps paper by F Zhang et al before changing.\nsplit point bias = 0.0\n\n# Enable the debug hud to see what the shadow map(s) contain.\nenable debug hud = false\n\n# Enable the debug overlay to see where each shadow map affects.\nenable debug overlay = false\n\n# Used to set the type of tight scene bound calculation method to be used by the shadow map that covers a smaller area. \"bounds\" (default) is less precise shadows but better performance or \"primitives\" for more precise shadows at expense of CPU.\ncompute scene bounds = bounds\n\n# How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations.\nshadow map resolution = 1024\n\n# Controls the minimum near/far ratio for the Light Space Perspective Shadow Map transformation. Helps prevent too much detail being brought towards the camera at the expense of detail further from the camera. Increasing this pushes detail further away.\nminimum lispsm near far ratio = 0.25\n\n# Used as the factor parameter for the polygon offset used for shadow map rendering. Higher values reduce shadow flicker, but risk increasing Peter Panning. See https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml for details.\npolygon offset factor = 1.1\n\n# Used as the units parameter for the polygon offset used for shadow map rendering. Higher values reduce shadow flicker, but risk increasing Peter Panning. See https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml for details.\npolygon offset units = 4.0\n\n# How far along the surface normal to project shadow coordinates. Higher values significantly reduce shadow flicker, usually with a lower increase of Peter Panning than the Polygon Offset settings. This value is in in-game units, so 1.0 is roughly 1.4 cm.\nnormal offset distance = 1.0\n\n# Excludes theoretically unnecessary faces from shadow maps, slightly increasing performance. In practice, Peter Panning can be much less visible with these faces included, so if you have high polygon offset values, leave this off to minimise the side effects.\nuse front face culling = false\n\n# Allow actors to cast shadows. Potentially decreases performance.\nactor shadows = false\n\n# Allow the player to cast shadows. Potentially decreases performance.\nplayer shadows = false\n\n# Allow terrain to cast shadows. Potentially decreases performance.\nterrain shadows = false\n\n# Allow world objects to cast shadows. Potentially decreases performance.\nobject shadows = false\n\n# Allow shadows indoors. Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.\nenable indoor shadows = true\n\n[Physics]\n# Set the number of background threads used for physics.\n# If no background threads are used, physics calculations are processed in the main thread\n# and the settings below have no effect.\nasync num threads = 0\n\n# Set the number of frames an inactive line-of-sight request will be kept\n# refreshed in the background physics thread cache.\n# If this is set to -1, line-of-sight requests are never cached.\nlineofsight keep inactive cache = 0\n\n# Defer bounding boxes update until collision detection.\ndefer aabb update = true\n\n[Models]\n\n# Attempt to load any valid NIF file regardless of its version and track the progress.\n# Loading arbitrary meshes is not advised and may cause instability.\nload unsupported nif files = false\n\n# 3rd person base animation model that looks also for the corresponding kf-file\nxbaseanim = meshes/xbase_anim.nif\n\n# 3rd person base model with textkeys-data\nbaseanim = meshes/base_anim.nif\n\n# 1st person base animation model that looks also for corresponding kf-file\nxbaseanim1st = meshes/xbase_anim.1st.nif\n\n# 3rd person beast race base model with textkeys-data\nbaseanimkna = meshes/base_animkna.nif\n\n# 1st person beast race base animation model\nbaseanimkna1st = meshes/base_animkna.1st.nif\n\n# 3rd person female base animation model\nxbaseanimfemale = meshes/xbase_anim_female.nif\n\n# 3rd person female base model with textkeys-data\nbaseanimfemale = meshes/base_anim_female.nif\n\n# 1st person female base model with textkeys-data\nbaseanimfemale1st = meshes/base_anim_female.1st.nif\n\n# 3rd person werewolf skin\nwolfskin = meshes/wolf/skin.nif\n\n# 1st person werewolf skin\nwolfskin1st = meshes/wolf/skin.1st.nif\n\n# Argonian smimkna\nxargonianswimkna = meshes/xargonian_swimkna.nif\n\n# File to load xbaseanim 3rd person animations\nxbaseanimkf = meshes/xbase_anim.kf\n\n# File to load xbaseanim 3rd person animations\nxbaseanim1stkf = meshes/xbase_anim.1st.kf\n\n# File to load xbaseanim animations from\nxbaseanimfemalekf = meshes/xbase_anim_female.kf\n\n# File to load xargonianswimkna animations from\nxargonianswimknakf = meshes/xargonian_swimkna.kf\n\n# Sky atmosphere mesh\nskyatmosphere = meshes/sky_atmosphere.nif\n\n# Sky clouds mesh \nskyclouds = meshes/sky_clouds_01.nif\n\n# Sky stars mesh 01\nskynight01 = meshes/sky_night_01.nif\n\n# Sky stars mesh 02\nskynight02 = meshes/sky_night_02.nif\n\n# Ash clouds weather effect\nweatherashcloud = meshes/ashcloud.nif\n\n# Blight clouds weather effect\nweatherblightcloud = meshes/blightcloud.nif\n\n# Snow falling weather effect\nweathersnow = meshes/snow.nif\n\n# Blizzard weather effect\nweatherblizzard = meshes/blizzard.nif\n\n[Groundcover]\n\n# enable separate groundcover handling\nenabled = false\n\n# A groundcover density (0.0 <= value <= 1.0)\n# 1.0 means 100% density\ndensity = 1.0\n\n# A maximum distance in game units on which groundcover is rendered.\nrendering distance = 6144.0\n\n# A minimum size of groundcover chunk in cells (0.125, 0.25, 0.5, 1.0)\nmin chunk size = 0.5\n\n# Whether grass should respond to the player treading on it.\n# 0 - Grass cannot be trampled.\n# 1 - The player's XY position is taken into account.\n# 2 - The player's height above the ground is taken into account, too.\nstomp mode = 2\n\n# How far away from the player grass can be before it's unaffected by being trod on, and how far it moves when it is.\n# 2 - MGE XE levels. Generally excessive, but what existing mods were made with in mind\n# 1 - Reduced levels.\n# 0 - Gentle levels.\nstomp intensity = 1\n"
  },
  {
    "path": "files/shaders/CMakeLists.txt",
    "content": "if (NOT DEFINED OPENMW_SHADERS_ROOT)\n    return()\nendif()\n\n# Copy resource files into the build directory\nset(SDIR ${CMAKE_CURRENT_SOURCE_DIR})\nset(DDIRRELATIVE resources/shaders)\n\nset(SHADER_FILES\n    groundcover_vertex.glsl\n    groundcover_fragment.glsl\n    water_vertex.glsl\n    water_fragment.glsl\n    water_nm.png\n    alpha.glsl\n    objects_vertex.glsl\n    objects_fragment.glsl\n    terrain_vertex.glsl\n    terrain_fragment.glsl\n    lighting.glsl\n    lighting_util.glsl\n    parallax.glsl\n    s360_fragment.glsl\n    s360_vertex.glsl\n    shadows_vertex.glsl\n    shadows_fragment.glsl\n    shadowcasting_vertex.glsl\n    shadowcasting_fragment.glsl\n    vertexcolors.glsl\n    nv_default_vertex.glsl\n    nv_default_fragment.glsl\n    nv_nolighting_vertex.glsl\n    nv_nolighting_fragment.glsl\n)\n\ncopy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} \"${SHADER_FILES}\")\n"
  },
  {
    "path": "files/shaders/alpha.glsl",
    "content": "\n#define FUNC_NEVER                          512 // 0x0200\n#define FUNC_LESS                           513 // 0x0201\n#define FUNC_EQUAL                          514 // 0x0202\n#define FUNC_LEQUAL                         515 // 0x0203\n#define FUNC_GREATER                        516 // 0x0204\n#define FUNC_NOTEQUAL                       517 // 0x0205\n#define FUNC_GEQUAL                         518 // 0x0206\n#define FUNC_ALWAYS                         519 // 0x0207\n\n#if @alphaFunc != FUNC_ALWAYS && @alphaFunc != FUNC_NEVER\nuniform float alphaRef;\n#endif\n\nfloat mipmapLevel(vec2 scaleduv)\n{\n    vec2 dUVdx = dFdx(scaleduv);\n    vec2 dUVdy = dFdy(scaleduv);\n    float maxDUVSquared = max(dot(dUVdx, dUVdx), dot(dUVdy, dUVdy));\n    return max(0.0, 0.5 * log2(maxDUVSquared));\n}\n\nfloat coveragePreservingAlphaScale(sampler2D diffuseMap, vec2 uv)\n{\n    #if @adjustCoverage\n        vec2 textureSize;\n        #if @useGPUShader4\n            textureSize = textureSize2D(diffuseMap, 0);\n        #else\n            textureSize = vec2(256.0);\n        #endif\n            return 1.0 + mipmapLevel(uv * textureSize) * 0.25;\n    #else\n        return 1.0;\n    #endif\n}\n\nvoid alphaTest()\n{\n    #if @alphaToCoverage \n        float coverageAlpha = (gl_FragData[0].a - clamp(alphaRef, 0.0001, 0.9999)) / max(fwidth(gl_FragData[0].a), 0.0001) + 0.5;\n\n        // Some functions don't make sense with A2C or are a pain to think about and no meshes use them anyway\n        // Use regular alpha testing in such cases until someone complains.\n        #if @alphaFunc == FUNC_NEVER\n            discard;\n        #elif @alphaFunc == FUNC_LESS\n            gl_FragData[0].a = 1.0 - coverageAlpha;\n        #elif @alphaFunc == FUNC_EQUAL\n            if (gl_FragData[0].a != alphaRef)\n                discard;\n        #elif @alphaFunc == FUNC_LEQUAL\n            gl_FragData[0].a = 1.0 - coverageAlpha;\n        #elif @alphaFunc == FUNC_GREATER\n            gl_FragData[0].a = coverageAlpha;\n        #elif @alphaFunc == FUNC_NOTEQUAL\n            if (gl_FragData[0].a == alphaRef)\n                discard;\n        #elif @alphaFunc == FUNC_GEQUAL\n            gl_FragData[0].a = coverageAlpha;\n        #endif\n    #else\n        #if @alphaFunc == FUNC_NEVER\n            discard;\n        #elif @alphaFunc == FUNC_LESS\n            if (gl_FragData[0].a >= alphaRef)\n                discard;\n        #elif @alphaFunc == FUNC_EQUAL\n            if (gl_FragData[0].a != alphaRef)\n                discard;\n        #elif @alphaFunc == FUNC_LEQUAL\n            if (gl_FragData[0].a > alphaRef)\n                discard;\n        #elif @alphaFunc == FUNC_GREATER\n            if (gl_FragData[0].a <= alphaRef)\n                discard;\n        #elif @alphaFunc == FUNC_NOTEQUAL\n            if (gl_FragData[0].a == alphaRef)\n                discard;\n        #elif @alphaFunc == FUNC_GEQUAL\n            if (gl_FragData[0].a < alphaRef)\n                discard;\n        #endif\n    #endif\n}"
  },
  {
    "path": "files/shaders/groundcover_fragment.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\n#define GROUNDCOVER\n\n#if @diffuseMap\nuniform sampler2D diffuseMap;\nvarying vec2 diffuseMapUV;\n#endif\n\n#if @normalMap\nuniform sampler2D normalMap;\nvarying vec2 normalMapUV;\nvarying vec4 passTangent;\n#endif\n\n// Other shaders respect forcePPL, but legacy groundcover mods were designed to work with vertex lighting.\n// They may do not look as intended with per-pixel lighting, so ignore this setting for now.\n#define PER_PIXEL_LIGHTING @normalMap\n\nvarying float euclideanDepth;\nvarying float linearDepth;\n\n#if PER_PIXEL_LIGHTING\nvarying vec3 passViewPos;\nvarying vec3 passNormal;\n#else\ncentroid varying vec3 passLighting;\ncentroid varying vec3 shadowDiffuseLighting;\n#endif\n\n#include \"shadows_fragment.glsl\"\n#include \"lighting.glsl\"\n#include \"alpha.glsl\"\n\nvoid main()\n{\n#if @normalMap\n    vec4 normalTex = texture2D(normalMap, normalMapUV);\n\n    vec3 normalizedNormal = normalize(passNormal);\n    vec3 normalizedTangent = normalize(passTangent.xyz);\n    vec3 binormal = cross(normalizedTangent, normalizedNormal) * passTangent.w;\n    mat3 tbnTranspose = mat3(normalizedTangent, binormal, normalizedNormal);\n\n    vec3 viewNormal = gl_NormalMatrix * normalize(tbnTranspose * (normalTex.xyz * 2.0 - 1.0));\n#endif\n\n#if @diffuseMap\n    gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);\n#else\n    gl_FragData[0] = vec4(1.0);\n#endif\n\n    if (euclideanDepth > @groundcoverFadeStart)\n        gl_FragData[0].a *= 1.0-smoothstep(@groundcoverFadeStart, @groundcoverFadeEnd, euclideanDepth);\n\n    alphaTest();\n\n    float shadowing = unshadowedLightRatio(linearDepth);\n\n    vec3 lighting;\n#if !PER_PIXEL_LIGHTING\n    lighting = passLighting + shadowDiffuseLighting * shadowing;\n#else\n    vec3 diffuseLight, ambientLight;\n    doLighting(passViewPos, normalize(viewNormal), shadowing, diffuseLight, ambientLight);\n    lighting = diffuseLight + ambientLight;\n    clampLightingResult(lighting);\n#endif\n\n    gl_FragData[0].xyz *= lighting;\n\n#if @radialFog\n    float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#else\n    float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#endif\n    gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);\n\n    applyShadowDebugOverlay();\n}\n"
  },
  {
    "path": "files/shaders/groundcover_vertex.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\n#define GROUNDCOVER\n\nattribute vec4 aOffset;\nattribute vec3 aRotation;\n\n#if @diffuseMap\nvarying vec2 diffuseMapUV;\n#endif\n\n#if @normalMap\nvarying vec2 normalMapUV;\nvarying vec4 passTangent;\n#endif\n\n// Other shaders respect forcePPL, but legacy groundcover mods were designed to work with vertex lighting.\n// They may do not look as intended with per-pixel lighting, so ignore this setting for now.\n#define PER_PIXEL_LIGHTING @normalMap\n\nvarying float euclideanDepth;\nvarying float linearDepth;\n\n#if PER_PIXEL_LIGHTING\nvarying vec3 passViewPos;\nvarying vec3 passNormal;\n#else\ncentroid varying vec3 passLighting;\ncentroid varying vec3 shadowDiffuseLighting;\n#endif\n\n#include \"shadows_vertex.glsl\"\n#include \"lighting.glsl\"\n\nuniform float osg_SimulationTime;\nuniform mat4 osg_ViewMatrixInverse;\nuniform mat4 osg_ViewMatrix;\nuniform float windSpeed;\nuniform vec3 playerPos;\n\n#if @groundcoverStompMode == 0\n#else\n    #define STOMP 1\n    #if @groundcoverStompMode == 2\n        #define STOMP_HEIGHT_SENSITIVE 1\n    #endif\n    #define STOMP_INTENSITY_LEVEL @groundcoverStompIntensity\n#endif\n\nvec2 groundcoverDisplacement(in vec3 worldpos, float h)\n{\n    vec2 windDirection = vec2(1.0);\n    vec3 footPos = playerPos;\n    vec3 windVec = vec3(windSpeed * windDirection, 1.0);\n\n    float v = length(windVec);\n    vec2 displace = vec2(2.0 * windVec + 0.1);\n    vec2 harmonics = vec2(0.0);\n\n    harmonics += vec2((1.0 - 0.10*v) * sin(1.0*osg_SimulationTime + worldpos.xy / 1100.0));\n    harmonics += vec2((1.0 - 0.04*v) * cos(2.0*osg_SimulationTime + worldpos.xy / 750.0));\n    harmonics += vec2((1.0 + 0.14*v) * sin(3.0*osg_SimulationTime + worldpos.xy / 500.0));\n    harmonics += vec2((1.0 + 0.28*v) * sin(5.0*osg_SimulationTime + worldpos.xy / 200.0));\n\n    vec2 stomp = vec2(0.0);\n#if STOMP\n    float d = length(worldpos.xy - footPos.xy);\n#if STOMP_INTENSITY_LEVEL == 0\n    // Gentle intensity\n    const float STOMP_RANGE = 50.0; // maximum distance from player that grass is affected by stomping\n    const float STOMP_DISTANCE = 20.0; // maximum distance stomping can move grass\n#elif STOMP_INTENSITY_LEVEL == 1\n    // Reduced intensity\n    const float STOMP_RANGE = 80.0;\n    const float STOMP_DISTANCE = 40.0;\n#elif STOMP_INTENSITY_LEVEL == 2\n    // MGE XE intensity\n    const float STOMP_RANGE = 150.0;\n    const float STOMP_DISTANCE = 60.0;\n#endif\n    if (d < STOMP_RANGE && d > 0.0)\n        stomp = (STOMP_DISTANCE / d - STOMP_DISTANCE / STOMP_RANGE) * (worldpos.xy - footPos.xy);\n\n#ifdef STOMP_HEIGHT_SENSITIVE\n    stomp *= clamp((worldpos.z - footPos.z) / h, 0.0, 1.0);\n#endif\n#endif\n\n    return clamp(0.02 * h, 0.0, 1.0) * (harmonics * displace + stomp);\n}\n\nmat4 rotation(in vec3 angle)\n{\n    float sin_x = sin(angle.x);\n    float cos_x = cos(angle.x);\n    float sin_y = sin(angle.y);\n    float cos_y = cos(angle.y);\n    float sin_z = sin(angle.z);\n    float cos_z = cos(angle.z);\n\n    return mat4(\n        cos_z*cos_y+sin_x*sin_y*sin_z, -sin_z*cos_x, cos_z*sin_y+sin_z*sin_x*cos_y, 0.0,\n        sin_z*cos_y+cos_z*sin_x*sin_y, cos_z*cos_x, sin_z*sin_y-cos_z*sin_x*cos_y, 0.0,\n        -sin_y*cos_x, sin_x, cos_x*cos_y, 0.0,\n        0.0, 0.0, 0.0, 1.0);\n}\n\nmat3 rotation3(in mat4 rot4)\n{\n    return mat3(\n        rot4[0].xyz,\n        rot4[1].xyz,\n        rot4[2].xyz);\n}\n\nvoid main(void)\n{\n    vec3 position = aOffset.xyz;\n    float scale = aOffset.w;\n\n    mat4 rotation = rotation(aRotation);\n    vec4 displacedVertex = rotation * scale * gl_Vertex;\n\n    displacedVertex = vec4(displacedVertex.xyz + position, 1.0);\n\n    vec4 worldPos = osg_ViewMatrixInverse * gl_ModelViewMatrix * displacedVertex;\n    worldPos.xy += groundcoverDisplacement(worldPos.xyz, gl_Vertex.z);\n    vec4 viewPos = osg_ViewMatrix * worldPos;\n\n    gl_ClipVertex = viewPos;\n    euclideanDepth = length(viewPos.xyz);\n\n    if (length(gl_ModelViewMatrix * vec4(position, 1.0)) > @groundcoverFadeEnd)\n        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n    else\n        gl_Position = gl_ProjectionMatrix * viewPos;\n\n    linearDepth = gl_Position.z;\n\n#if (!PER_PIXEL_LIGHTING || @shadows_enabled)\n    vec3 viewNormal = normalize((gl_NormalMatrix * rotation3(rotation) * gl_Normal).xyz);\n#endif\n\n#if @diffuseMap\n    diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy;\n#endif\n\n#if @normalMap\n    normalMapUV = (gl_TextureMatrix[@normalMapUV] * gl_MultiTexCoord@normalMapUV).xy;\n    passTangent = gl_MultiTexCoord7.xyzw * rotation;\n#endif\n\n#if PER_PIXEL_LIGHTING\n    passViewPos = viewPos.xyz;\n    passNormal = rotation3(rotation) * gl_Normal.xyz;\n#else\n    vec3 diffuseLight, ambientLight;\n    doLighting(viewPos.xyz, viewNormal, diffuseLight, ambientLight, shadowDiffuseLighting);\n    passLighting = diffuseLight + ambientLight;\n    clampLightingResult(passLighting);\n#endif\n\n#if (@shadows_enabled)\n    setupShadowCoords(viewPos, viewNormal);\n#endif\n}\n"
  },
  {
    "path": "files/shaders/lighting.glsl",
    "content": "#include \"lighting_util.glsl\"\n\nvoid perLightSun(out vec3 diffuseOut, vec3 viewPos, vec3 viewNormal)\n{\n    vec3 lightDir = normalize(lcalcPosition(0));\n    float lambert = dot(viewNormal.xyz, lightDir);\n\n#ifndef GROUNDCOVER\n    lambert = max(lambert, 0.0);\n#else\n    float eyeCosine = dot(normalize(viewPos), viewNormal.xyz);\n    if (lambert < 0.0)\n    {\n        lambert = -lambert;\n        eyeCosine = -eyeCosine;\n    }\n    lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0);\n#endif\n\n    diffuseOut = lcalcDiffuse(0).xyz * lambert;\n}\n\nvoid perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal)\n{\n    vec3 lightPos = lcalcPosition(lightIndex) - viewPos;\n    float lightDistance = length(lightPos);\n\n// cull non-FFP point lighting by radius, light is guaranteed to not fall outside this bound with our cutoff\n#if !@lightingMethodFFP\n    float radius = lcalcRadius(lightIndex);\n\n    if (lightDistance > radius * 2.0)\n    {\n        ambientOut = vec3(0.0);\n        diffuseOut = vec3(0.0);\n        return;\n    }\n#endif\n\n    lightPos = normalize(lightPos);\n\n    float illumination = lcalcIllumination(lightIndex, lightDistance);\n    ambientOut = lcalcAmbient(lightIndex) * illumination;\n    float lambert = dot(viewNormal.xyz, lightPos) * illumination;\n\n#ifndef GROUNDCOVER\n    lambert = max(lambert, 0.0);\n#else\n    float eyeCosine = dot(normalize(viewPos), viewNormal.xyz);\n    if (lambert < 0.0)\n    {\n        lambert = -lambert;\n        eyeCosine = -eyeCosine;\n    }\n    lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0);\n#endif\n\n    diffuseOut = lcalcDiffuse(lightIndex) * lambert;\n}\n\n#if PER_PIXEL_LIGHTING\nvoid doLighting(vec3 viewPos, vec3 viewNormal, float shadowing, out vec3 diffuseLight, out vec3 ambientLight)\n#else\nvoid doLighting(vec3 viewPos, vec3 viewNormal, out vec3 diffuseLight, out vec3 ambientLight, out vec3 shadowDiffuse)\n#endif\n{\n    vec3 ambientOut, diffuseOut;\n\n    perLightSun(diffuseOut, viewPos, viewNormal);\n    ambientLight = gl_LightModel.ambient.xyz;\n#if PER_PIXEL_LIGHTING\n    diffuseLight = diffuseOut * shadowing;\n#else\n    shadowDiffuse = diffuseOut;\n    diffuseLight = vec3(0.0);\n#endif\n\n    for (int i = @startLight; i < @endLight; ++i)\n    {\n#if @lightingMethodUBO\n        perLightPoint(ambientOut, diffuseOut, PointLightIndex[i], viewPos, viewNormal);\n#else\n        perLightPoint(ambientOut, diffuseOut, i, viewPos, viewNormal);\n#endif\n        ambientLight += ambientOut;\n        diffuseLight += diffuseOut;\n    }\n}\n\nvec3 getSpecular(vec3 viewNormal, vec3 viewDirection, float shininess, vec3 matSpec)\n{\n    vec3 lightDir = normalize(lcalcPosition(0));\n    float NdotL = dot(viewNormal, lightDir);\n    if (NdotL <= 0.0)\n        return vec3(0.0);\n    vec3 halfVec = normalize(lightDir - viewDirection);\n    float NdotH = dot(viewNormal, halfVec);\n    return pow(max(NdotH, 0.0), max(1e-4, shininess)) * lcalcSpecular(0).xyz * matSpec;\n}\n"
  },
  {
    "path": "files/shaders/lighting_util.glsl",
    "content": "#if !@lightingMethodFFP\nfloat quickstep(float x)\n{\n    x = clamp(x, 0.0, 1.0);\n    x = 1.0 - x*x;\n    x = 1.0 - x*x;\n    return x;\n}\n#endif\n\n#if @lightingMethodUBO\n\nconst int mask = int(0xff);\nconst ivec4 shift = ivec4(int(0), int(8), int(16), int(24));\n\nvec3 unpackRGB(int data)\n{\n    return vec3( (float(((data >> shift.x) & mask)) / 255.0)\n                ,(float(((data >> shift.y) & mask)) / 255.0)\n                ,(float(((data >> shift.z) & mask)) / 255.0));\n}\n\nvec4 unpackRGBA(int data)\n{\n    return vec4( (float(((data >> shift.x) & mask)) / 255.0)\n                ,(float(((data >> shift.y) & mask)) / 255.0)\n                ,(float(((data >> shift.z) & mask)) / 255.0)\n                ,(float(((data >> shift.w) & mask)) / 255.0));\n}\n\n/* Layout:\npackedColors: 8-bit unsigned RGB packed as (diffuse, ambient, specular).\n              sign bit is stored in unused alpha component\nattenuation: constant, linear, quadratic, light radius (as defined in content)\n*/\nstruct LightData\n{\n    ivec4 packedColors;\n    vec4 position;\n    vec4 attenuation;\n};\n\nuniform int PointLightIndex[@maxLights];\nuniform int PointLightCount;\n\n// Defaults to shared layout. If we ever move to GLSL 140, std140 layout should be considered\nuniform LightBufferBinding\n{\n    LightData LightBuffer[@maxLightsInScene];\n};\n\n#elif @lightingMethodPerObjectUniform\n\n/* Layout:\n--------------------------------------- -----------\n|  pos_x  |  ambi_r  |  diff_r  |  spec_r         |\n|  pos_y  |  ambi_g  |  diff_g  |  spec_g         |\n|  pos_z  |  ambi_b  |  diff_b  |  spec_b         |\n|  att_c  |  att_l   |  att_q   |  radius/spec_a  |\n --------------------------------------------------\n*/\nuniform mat4 LightBuffer[@maxLights];\nuniform int PointLightCount;\n\n#endif\n\n#if !@lightingMethodFFP\nfloat lcalcRadius(int lightIndex)\n{\n#if @lightingMethodPerObjectUniform\n    return @getLight[lightIndex][3].w;\n#else\n    return @getLight[lightIndex].attenuation.w;\n#endif\n}\n#endif\n\nfloat lcalcIllumination(int lightIndex, float lightDistance)\n{\n#if @lightingMethodPerObjectUniform\n    float illumination = clamp(1.0 / (@getLight[lightIndex][0].w + @getLight[lightIndex][1].w * lightDistance + @getLight[lightIndex][2].w * lightDistance * lightDistance), 0.0, 1.0);\n    return (illumination * (1.0 - quickstep((lightDistance / lcalcRadius(lightIndex)) - 1.0)));\n#elif @lightingMethodUBO\n    float illumination = clamp(1.0 / (@getLight[lightIndex].attenuation.x + @getLight[lightIndex].attenuation.y * lightDistance + @getLight[lightIndex].attenuation.z * lightDistance * lightDistance), 0.0, 1.0);\n    return (illumination * (1.0 - quickstep((lightDistance / lcalcRadius(lightIndex)) - 1.0)));\n#else\n    return clamp(1.0 / (@getLight[lightIndex].constantAttenuation + @getLight[lightIndex].linearAttenuation * lightDistance + @getLight[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0);\n#endif\n}\n\nvec3 lcalcPosition(int lightIndex)\n{\n#if @lightingMethodPerObjectUniform\n    return @getLight[lightIndex][0].xyz;\n#else\n    return @getLight[lightIndex].position.xyz;\n#endif\n}\n\nvec3 lcalcDiffuse(int lightIndex)\n{\n#if @lightingMethodPerObjectUniform\n    return @getLight[lightIndex][2].xyz;\n#elif @lightingMethodUBO\n    return unpackRGB(@getLight[lightIndex].packedColors.x) * float(@getLight[lightIndex].packedColors.w);\n#else\n    return @getLight[lightIndex].diffuse.xyz;\n#endif\n}\n\nvec3 lcalcAmbient(int lightIndex)\n{\n#if @lightingMethodPerObjectUniform\n    return @getLight[lightIndex][1].xyz;\n#elif @lightingMethodUBO\n    return unpackRGB(@getLight[lightIndex].packedColors.y);\n#else\n    return @getLight[lightIndex].ambient.xyz;\n#endif\n}\n\nvec4 lcalcSpecular(int lightIndex)\n{\n#if @lightingMethodPerObjectUniform\n    return @getLight[lightIndex][3];\n#elif @lightingMethodUBO\n    return unpackRGBA(@getLight[lightIndex].packedColors.z);\n#else\n    return @getLight[lightIndex].specular;\n#endif\n}\n\nvoid clampLightingResult(inout vec3 lighting)\n{\n#if @clamp\n    lighting = clamp(lighting, vec3(0.0), vec3(1.0));\n#else\n    lighting = max(lighting, 0.0);\n#endif\n}\n"
  },
  {
    "path": "files/shaders/nv_default_fragment.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\n#if @diffuseMap\nuniform sampler2D diffuseMap;\nvarying vec2 diffuseMapUV;\n#endif\n\n#if @emissiveMap\nuniform sampler2D emissiveMap;\nvarying vec2 emissiveMapUV;\n#endif\n\n#if @normalMap\nuniform sampler2D normalMap;\nvarying vec2 normalMapUV;\nvarying vec4 passTangent;\n#endif\n\nuniform bool noAlpha;\n\nvarying float euclideanDepth;\nvarying float linearDepth;\n\n#define PER_PIXEL_LIGHTING 1\n\nvarying vec3 passViewPos;\nvarying vec3 passNormal;\n\n#include \"vertexcolors.glsl\"\n#include \"shadows_fragment.glsl\"\n#include \"lighting.glsl\"\n#include \"alpha.glsl\"\n\nuniform float emissiveMult;\n\nvoid main()\n{\n#if @diffuseMap\n    gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);\n    gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, adjustedDiffuseUV);\n#else\n    gl_FragData[0] = vec4(1.0);\n#endif\n\n    vec4 diffuseColor = getDiffuseColor();\n    gl_FragData[0].a *= diffuseColor.a;\n    alphaTest();\n\n#if @normalMap\n    vec4 normalTex = texture2D(normalMap, normalMapUV);\n\n    vec3 normalizedNormal = normalize(passNormal);\n    vec3 normalizedTangent = normalize(passTangent.xyz);\n    vec3 binormal = cross(normalizedTangent, normalizedNormal) * passTangent.w;\n    mat3 tbnTranspose = mat3(normalizedTangent, binormal, normalizedNormal);\n\n    vec3 viewNormal = gl_NormalMatrix * normalize(tbnTranspose * (normalTex.xyz * 2.0 - 1.0));\n#else\n    vec3 viewNormal = gl_NormalMatrix * normalize(passNormal);\n#endif\n\n    float shadowing = unshadowedLightRatio(linearDepth);\n    vec3 diffuseLight, ambientLight;\n    doLighting(passViewPos, normalize(viewNormal), shadowing, diffuseLight, ambientLight);\n    vec3 emission = getEmissionColor().xyz * emissiveMult;\n#if @emissiveMap\n    emission *= texture2D(emissiveMap, emissiveMapUV).xyz;\n#endif\n    vec3 lighting = diffuseColor.xyz * diffuseLight + getAmbientColor().xyz * ambientLight + emission;\n\n    clampLightingResult(lighting);\n\n    gl_FragData[0].xyz *= lighting;\n\n    float shininess = gl_FrontMaterial.shininess;\n    vec3 matSpec = getSpecularColor().xyz;\n#if @normalMap\n    matSpec *= normalTex.a;\n#endif\n\n    if (matSpec != vec3(0.0))\n        gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing;\n#if @radialFog\n    float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#else\n    float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#endif\n    gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);\n\n#if @translucentFramebuffer\n    if (noAlpha)\n        gl_FragData[0].a = 1.0;\n#endif\n\n    applyShadowDebugOverlay();\n}\n"
  },
  {
    "path": "files/shaders/nv_default_vertex.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\n#if @diffuseMap\nvarying vec2 diffuseMapUV;\n#endif\n\n#if @emissiveMap\nvarying vec2 emissiveMapUV;\n#endif\n\n#if @normalMap\nvarying vec2 normalMapUV;\nvarying vec4 passTangent;\n#endif\n\nvarying float euclideanDepth;\nvarying float linearDepth;\n\nvarying vec3 passViewPos;\nvarying vec3 passNormal;\n\n#define PER_PIXEL_LIGHTING 1\n\n#include \"vertexcolors.glsl\"\n#include \"shadows_vertex.glsl\"\n#include \"lighting.glsl\"\n\nvoid main(void)\n{\n    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\n    vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);\n    gl_ClipVertex = viewPos;\n    euclideanDepth = length(viewPos.xyz);\n    linearDepth = gl_Position.z;\n\n#if @diffuseMap\n    diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy;\n#endif\n\n#if @emissiveMap\n    emissiveMapUV = (gl_TextureMatrix[@emissiveMapUV] * gl_MultiTexCoord@emissiveMapUV).xy;\n#endif\n\n#if @normalMap\n    normalMapUV = (gl_TextureMatrix[@normalMapUV] * gl_MultiTexCoord@normalMapUV).xy;\n    passTangent = gl_MultiTexCoord7.xyzw;\n#endif\n\n    passColor = gl_Color;\n    passViewPos = viewPos.xyz;\n    passNormal = gl_Normal.xyz;\n\n#if @shadows_enabled\n    vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);\n    setupShadowCoords(viewPos, viewNormal);\n#endif\n}\n"
  },
  {
    "path": "files/shaders/nv_nolighting_fragment.glsl",
    "content": "#version 120\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\n#if @diffuseMap\nuniform sampler2D diffuseMap;\nvarying vec2 diffuseMapUV;\n#endif\n\nuniform bool noAlpha;\n\n#if @radialFog\nvarying float euclideanDepth;\n#else\nvarying float linearDepth;\n#endif\n\nuniform bool useFalloff;\n\nvarying float passFalloff;\n\n#include \"vertexcolors.glsl\"\n#include \"alpha.glsl\"\n\nvoid main()\n{\n#if @diffuseMap\n    gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);\n    gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, diffuseMapUV);\n#else\n    gl_FragData[0] = vec4(1.0);\n#endif\n\n    gl_FragData[0] *= getDiffuseColor();\n\n    if (useFalloff)\n        gl_FragData[0].a *= passFalloff;\n\n    alphaTest();\n\n#if @radialFog\n    float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#else\n    float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#endif\n\n#if @translucentFramebuffer\n    if (noAlpha)\n        gl_FragData[0].a = 1.0;\n#endif\n\n    gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);\n}\n"
  },
  {
    "path": "files/shaders/nv_nolighting_vertex.glsl",
    "content": "#version 120\n\n#if @diffuseMap\nvarying vec2 diffuseMapUV;\n#endif\n\n#if @radialFog\nvarying float euclideanDepth;\n#else\nvarying float linearDepth;\n#endif\n\nuniform bool useFalloff;\nuniform vec4 falloffParams;\n\nvarying vec3 passViewPos;\nvarying float passFalloff;\n\n#include \"vertexcolors.glsl\"\n\nvoid main(void)\n{\n    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\n    vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);\n    gl_ClipVertex = viewPos;\n#if @radialFog\n    euclideanDepth = length(viewPos.xyz);\n#else\n    linearDepth = gl_Position.z;\n#endif\n\n#if @diffuseMap\n    diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy;\n#endif\n\n    passColor = gl_Color;\n    if (useFalloff)\n    {\n        vec3 viewNormal = gl_NormalMatrix * normalize(gl_Normal.xyz);\n        vec3 viewDir = normalize(viewPos.xyz);\n        float viewAngle = abs(dot(viewNormal, viewDir));\n        passFalloff = smoothstep(falloffParams.y, falloffParams.x, viewAngle);\n    }\n    else\n    {\n        passFalloff = 1.0;\n    }\n}\n"
  },
  {
    "path": "files/shaders/objects_fragment.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\n#if @diffuseMap\nuniform sampler2D diffuseMap;\nvarying vec2 diffuseMapUV;\n#endif\n\n#if @darkMap\nuniform sampler2D darkMap;\nvarying vec2 darkMapUV;\n#endif\n\n#if @detailMap\nuniform sampler2D detailMap;\nvarying vec2 detailMapUV;\n#endif\n\n#if @decalMap\nuniform sampler2D decalMap;\nvarying vec2 decalMapUV;\n#endif\n\n#if @emissiveMap\nuniform sampler2D emissiveMap;\nvarying vec2 emissiveMapUV;\n#endif\n\n#if @normalMap\nuniform sampler2D normalMap;\nvarying vec2 normalMapUV;\nvarying vec4 passTangent;\n#endif\n\n#if @envMap\nuniform sampler2D envMap;\nvarying vec2 envMapUV;\nuniform vec4 envMapColor;\n#endif\n\n#if @specularMap\nuniform sampler2D specularMap;\nvarying vec2 specularMapUV;\n#endif\n\n#if @bumpMap\nuniform sampler2D bumpMap;\nvarying vec2 bumpMapUV;\nuniform vec2 envMapLumaBias;\nuniform mat2 bumpMapMatrix;\n#endif\n\nuniform bool simpleWater;\nuniform bool noAlpha;\n\nvarying float euclideanDepth;\nvarying float linearDepth;\n\n#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)\n\n#if !PER_PIXEL_LIGHTING\ncentroid varying vec3 passLighting;\ncentroid varying vec3 shadowDiffuseLighting;\n#else\nuniform float emissiveMult;\n#endif\nvarying vec3 passViewPos;\nvarying vec3 passNormal;\n\n#include \"vertexcolors.glsl\"\n#include \"shadows_fragment.glsl\"\n#include \"lighting.glsl\"\n#include \"parallax.glsl\"\n#include \"alpha.glsl\"\n\nvoid main()\n{\n#if @diffuseMap\n    vec2 adjustedDiffuseUV = diffuseMapUV;\n#endif\n\n#if @normalMap\n    vec4 normalTex = texture2D(normalMap, normalMapUV);\n\n    vec3 normalizedNormal = normalize(passNormal);\n    vec3 normalizedTangent = normalize(passTangent.xyz);\n    vec3 binormal = cross(normalizedTangent, normalizedNormal) * passTangent.w;\n    mat3 tbnTranspose = mat3(normalizedTangent, binormal, normalizedNormal);\n\n    vec3 viewNormal = gl_NormalMatrix * normalize(tbnTranspose * (normalTex.xyz * 2.0 - 1.0));\n#endif\n\n#if (!@normalMap && (@parallax || @forcePPL))\n    vec3 viewNormal = gl_NormalMatrix * normalize(passNormal);\n#endif\n\n#if @parallax\n    vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz;\n    vec3 objectPos = (gl_ModelViewMatrixInverse * vec4(passViewPos, 1)).xyz;\n    vec3 eyeDir = normalize(cameraPos - objectPos);\n    vec2 offset = getParallaxOffset(eyeDir, tbnTranspose, normalTex.a, (passTangent.w > 0.0) ? -1.f : 1.f);\n    adjustedDiffuseUV += offset; // only offset diffuse for now, other textures are more likely to be using a completely different UV set\n\n    // TODO: check not working as the same UV buffer is being bound to different targets\n    // if diffuseMapUV == normalMapUV\n#if 1\n    // fetch a new normal using updated coordinates\n    normalTex = texture2D(normalMap, adjustedDiffuseUV);\n    viewNormal = gl_NormalMatrix * normalize(tbnTranspose * (normalTex.xyz * 2.0 - 1.0));\n#endif\n\n#endif\n\n#if @diffuseMap\n    gl_FragData[0] = texture2D(diffuseMap, adjustedDiffuseUV);\n    gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, adjustedDiffuseUV);\n#else\n    gl_FragData[0] = vec4(1.0);\n#endif\n\n    vec4 diffuseColor = getDiffuseColor();\n    gl_FragData[0].a *= diffuseColor.a;\n    alphaTest();\n\n#if @detailMap\n    gl_FragData[0].xyz *= texture2D(detailMap, detailMapUV).xyz * 2.0;\n#endif\n\n#if @darkMap\n    gl_FragData[0].xyz *= texture2D(darkMap, darkMapUV).xyz;\n#endif\n\n#if @decalMap\n    vec4 decalTex = texture2D(decalMap, decalMapUV);\n    gl_FragData[0].xyz = mix(gl_FragData[0].xyz, decalTex.xyz, decalTex.a);\n#endif\n\n#if @envMap\n\n    vec2 envTexCoordGen = envMapUV;\n    float envLuma = 1.0;\n\n#if @normalMap\n    // if using normal map + env map, take advantage of per-pixel normals for envTexCoordGen\n    vec3 viewVec = normalize(passViewPos.xyz);\n    vec3 r = reflect( viewVec, viewNormal );\n    float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) );\n    envTexCoordGen = vec2(r.x/m + 0.5, r.y/m + 0.5);\n#endif\n\n#if @bumpMap\n    vec4 bumpTex = texture2D(bumpMap, bumpMapUV);\n    envTexCoordGen += bumpTex.rg * bumpMapMatrix;\n    envLuma = clamp(bumpTex.b * envMapLumaBias.x + envMapLumaBias.y, 0.0, 1.0);\n#endif\n\n#if @preLightEnv\n    gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma;\n#endif\n\n#endif\n\n    float shadowing = unshadowedLightRatio(linearDepth);\n    vec3 lighting;\n#if !PER_PIXEL_LIGHTING\n    lighting = passLighting + shadowDiffuseLighting * shadowing;\n#else\n    vec3 diffuseLight, ambientLight;\n    doLighting(passViewPos, normalize(viewNormal), shadowing, diffuseLight, ambientLight);\n    vec3 emission = getEmissionColor().xyz * emissiveMult;\n    lighting = diffuseColor.xyz * diffuseLight + getAmbientColor().xyz * ambientLight + emission;\n    clampLightingResult(lighting);\n#endif\n\n    gl_FragData[0].xyz *= lighting;\n\n#if @envMap && !@preLightEnv\n    gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma;\n#endif\n\n#if @emissiveMap\n    gl_FragData[0].xyz += texture2D(emissiveMap, emissiveMapUV).xyz;\n#endif\n\n#if @specularMap\n    vec4 specTex = texture2D(specularMap, specularMapUV);\n    float shininess = specTex.a * 255.0;\n    vec3 matSpec = specTex.xyz;\n#else\n    float shininess = gl_FrontMaterial.shininess;\n    vec3 matSpec = getSpecularColor().xyz;\n#endif\n\n    if (matSpec != vec3(0.0))\n    {\n#if (!@normalMap && !@parallax && !@forcePPL)\n        vec3 viewNormal = gl_NormalMatrix * normalize(passNormal);\n#endif\n        gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing;\n    }\n#if @radialFog\n    float depth;\n    // For the less detailed mesh of simple water we need to recalculate depth on per-pixel basis\n    if (simpleWater)\n        depth = length(passViewPos);\n    else\n        depth = euclideanDepth;\n    float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#else\n    float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#endif\n    gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);\n\n#if @translucentFramebuffer\n    // having testing & blending isn't enough - we need to write an opaque pixel to be opaque\n    if (noAlpha)\n        gl_FragData[0].a = 1.0;\n#endif\n\n    applyShadowDebugOverlay();\n}\n"
  },
  {
    "path": "files/shaders/objects_vertex.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\n#if @diffuseMap\nvarying vec2 diffuseMapUV;\n#endif\n\n#if @darkMap\nvarying vec2 darkMapUV;\n#endif\n\n#if @detailMap\nvarying vec2 detailMapUV;\n#endif\n\n#if @decalMap\nvarying vec2 decalMapUV;\n#endif\n\n#if @emissiveMap\nvarying vec2 emissiveMapUV;\n#endif\n\n#if @normalMap\nvarying vec2 normalMapUV;\nvarying vec4 passTangent;\n#endif\n\n#if @envMap\nvarying vec2 envMapUV;\n#endif\n\n#if @bumpMap\nvarying vec2 bumpMapUV;\n#endif\n\n#if @specularMap\nvarying vec2 specularMapUV;\n#endif\n\nvarying float euclideanDepth;\nvarying float linearDepth;\n\n#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)\n\n#if !PER_PIXEL_LIGHTING\ncentroid varying vec3 passLighting;\ncentroid varying vec3 shadowDiffuseLighting;\nuniform float emissiveMult;\n#endif\nvarying vec3 passViewPos;\nvarying vec3 passNormal;\n\n#include \"vertexcolors.glsl\"\n#include \"shadows_vertex.glsl\"\n\n#include \"lighting.glsl\"\n\nvoid main(void)\n{\n    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\n    vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);\n    gl_ClipVertex = viewPos;\n    euclideanDepth = length(viewPos.xyz);\n    linearDepth = gl_Position.z;\n\n#if (@envMap || !PER_PIXEL_LIGHTING || @shadows_enabled)\n    vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);\n#endif\n\n#if @envMap\n    vec3 viewVec = normalize(viewPos.xyz);\n    vec3 r = reflect( viewVec, viewNormal );\n    float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) );\n    envMapUV = vec2(r.x/m + 0.5, r.y/m + 0.5);\n#endif\n\n#if @diffuseMap\n    diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy;\n#endif\n\n#if @darkMap\n    darkMapUV = (gl_TextureMatrix[@darkMapUV] * gl_MultiTexCoord@darkMapUV).xy;\n#endif\n\n#if @detailMap\n    detailMapUV = (gl_TextureMatrix[@detailMapUV] * gl_MultiTexCoord@detailMapUV).xy;\n#endif\n\n#if @decalMap\n    decalMapUV = (gl_TextureMatrix[@decalMapUV] * gl_MultiTexCoord@decalMapUV).xy;\n#endif\n\n#if @emissiveMap\n    emissiveMapUV = (gl_TextureMatrix[@emissiveMapUV] * gl_MultiTexCoord@emissiveMapUV).xy;\n#endif\n\n#if @normalMap\n    normalMapUV = (gl_TextureMatrix[@normalMapUV] * gl_MultiTexCoord@normalMapUV).xy;\n    passTangent = gl_MultiTexCoord7.xyzw;\n#endif\n\n#if @bumpMap\n    bumpMapUV = (gl_TextureMatrix[@bumpMapUV] * gl_MultiTexCoord@bumpMapUV).xy;\n#endif\n\n#if @specularMap\n    specularMapUV = (gl_TextureMatrix[@specularMapUV] * gl_MultiTexCoord@specularMapUV).xy;\n#endif\n\n    passColor = gl_Color;\n    passViewPos = viewPos.xyz;\n    passNormal = gl_Normal.xyz;\n\n#if !PER_PIXEL_LIGHTING\n    vec3 diffuseLight, ambientLight;\n    doLighting(viewPos.xyz, viewNormal, diffuseLight, ambientLight, shadowDiffuseLighting);\n    vec3 emission = getEmissionColor().xyz * emissiveMult;\n    passLighting = getDiffuseColor().xyz * diffuseLight + getAmbientColor().xyz * ambientLight + emission;\n    clampLightingResult(passLighting);\n    shadowDiffuseLighting *= getDiffuseColor().xyz;\n#endif\n\n#if (@shadows_enabled)\n    setupShadowCoords(viewPos, viewNormal);\n#endif\n}\n"
  },
  {
    "path": "files/shaders/parallax.glsl",
    "content": "#define PARALLAX_SCALE 0.04\n#define PARALLAX_BIAS -0.02\n\nvec2 getParallaxOffset(vec3 eyeDir, mat3 tbnTranspose, float height, float flipY)\n{\n    vec3 TSeyeDir = normalize(eyeDir * tbnTranspose);\n    return vec2(TSeyeDir.x, TSeyeDir.y * flipY) * ( height * PARALLAX_SCALE + PARALLAX_BIAS );\n}\n"
  },
  {
    "path": "files/shaders/s360_fragment.glsl",
    "content": "#version 120\n\nvarying vec2 uv;\nuniform samplerCube cubeMap;\nuniform int mapping;\n\n#define PI 3.1415926535\n\nvec3 sphericalCoords(vec2 coords)\n{\n    coords.x = -1 * coords.x * 2 * PI;\n    coords.y = (coords.y - 0.5) * PI;\n \n    vec3 result = vec3(0.0,cos(coords.y),sin(coords.y));\n    result = vec3(cos(coords.x) * result.y,sin(coords.x) * result.y,result.z);\n \n    return result;\n}\n\nvec3 cylindricalCoords(vec2 coords)\n{\n    return normalize(vec3(cos(-1 * coords.x * 2 * PI),sin(-1 * coords.x * 2 * PI),coords.y * 2.0 - 1.0));\n}\n\nvec3 planetCoords(vec2 coords)\n{\n    vec2 fromCenter = coords - vec2(0.5,0.5);\n\n    float magnitude = length(fromCenter);\n\n    fromCenter = normalize(fromCenter);\n\n    float dotProduct = dot(fromCenter,vec2(0.0,1.0));\n\n    coords.x = coords.x > 0.5 ? 0.5 - (dotProduct + 1.0) / 4.0 : 0.5 + (dotProduct + 1.0) / 4.0;\n    coords.y = max(0.0,1.0 - pow(magnitude / 0.5,0.5));\n    return sphericalCoords(coords);\n}\n\nvoid main(void)\n{\n    vec3 c;\n\n    if (mapping == 0)\n        c = sphericalCoords(uv);\n    else if (mapping == 1)\n        c = cylindricalCoords(uv);\n    else\n        c = planetCoords(uv);\n\n    gl_FragData[0] = textureCube(cubeMap,c);\n}\n"
  },
  {
    "path": "files/shaders/s360_vertex.glsl",
    "content": "#version 120\n\nvarying vec2 uv;\n\nvoid main(void)\n{\n    gl_Position = gl_Vertex;\n    uv = (gl_Vertex.xy * vec2(1.0,-1.0) + vec2(1.0)) / 2;\n}\n"
  },
  {
    "path": "files/shaders/shadowcasting_fragment.glsl",
    "content": "#version 120\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\nuniform sampler2D diffuseMap;\nvarying vec2 diffuseMapUV;\n\nvarying float alphaPassthrough;\n\nuniform bool useDiffuseMapForShadowAlpha;\nuniform bool alphaTestShadows;\n\n#include \"alpha.glsl\"\n\nvoid main()\n{\n    gl_FragData[0].rgb = vec3(1.0);\n    if (useDiffuseMapForShadowAlpha)\n        gl_FragData[0].a = texture2D(diffuseMap, diffuseMapUV).a * alphaPassthrough;\n    else\n        gl_FragData[0].a = alphaPassthrough;\n\n    alphaTest();\n\n    // Prevent translucent things casting shadow (including the player using an invisibility effect).\n    // This replaces alpha blending, which obviously doesn't work with depth buffers\n    if (alphaTestShadows && gl_FragData[0].a <= 0.5)\n        discard;\n}\n"
  },
  {
    "path": "files/shaders/shadowcasting_vertex.glsl",
    "content": "#version 120\n\nvarying vec2 diffuseMapUV;\n\nvarying float alphaPassthrough;\n\nuniform int colorMode;\nuniform bool useDiffuseMapForShadowAlpha = true;\nuniform bool alphaTestShadows = true;\n\nvoid main(void)\n{\n    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\n    vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);\n    gl_ClipVertex = viewPos;\n\n    if (useDiffuseMapForShadowAlpha)\n        diffuseMapUV = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;\n    else\n        diffuseMapUV = vec2(0.0); // Avoid undefined behaviour if running on hardware predating the concept of dynamically uniform expressions\n    if (colorMode == 2)\n        alphaPassthrough = gl_Color.a;\n    else\n        // This is uniform, so if it's too low, we might be able to put the position/clip vertex outside the view frustum and skip the fragment shader and rasteriser\n        alphaPassthrough = gl_FrontMaterial.diffuse.a;\n}\n"
  },
  {
    "path": "files/shaders/shadows_fragment.glsl",
    "content": "#define SHADOWS @shadows_enabled\n\n#if SHADOWS\n    uniform float maximumShadowMapDistance;\n    uniform float shadowFadeStart;\n    @foreach shadow_texture_unit_index @shadow_texture_unit_list\n        uniform sampler2DShadow shadowTexture@shadow_texture_unit_index;\n        varying vec4 shadowSpaceCoords@shadow_texture_unit_index;\n\n#if @perspectiveShadowMaps\n        varying vec4 shadowRegionCoords@shadow_texture_unit_index;\n#endif\n    @endforeach\n#endif // SHADOWS\n\nfloat unshadowedLightRatio(float distance)\n{\n    float shadowing = 1.0;\n#if SHADOWS\n#if @limitShadowMapDistance\n    float fade = clamp((distance - shadowFadeStart) / (maximumShadowMapDistance - shadowFadeStart), 0.0, 1.0);\n    if (fade == 1.0)\n        return shadowing;\n#endif\n    #if @shadowMapsOverlap\n        bool doneShadows = false;\n        @foreach shadow_texture_unit_index @shadow_texture_unit_list\n            if (!doneShadows)\n            {\n                vec3 shadowXYZ = shadowSpaceCoords@shadow_texture_unit_index.xyz / shadowSpaceCoords@shadow_texture_unit_index.w;\n#if @perspectiveShadowMaps\n                vec3 shadowRegionXYZ = shadowRegionCoords@shadow_texture_unit_index.xyz / shadowRegionCoords@shadow_texture_unit_index.w;\n#endif\n                if (all(lessThan(shadowXYZ.xy, vec2(1.0, 1.0))) && all(greaterThan(shadowXYZ.xy, vec2(0.0, 0.0))))\n                {\n                    shadowing = min(shadow2DProj(shadowTexture@shadow_texture_unit_index, shadowSpaceCoords@shadow_texture_unit_index).r, shadowing);\n\n                    \n                    doneShadows = all(lessThan(shadowXYZ, vec3(0.95, 0.95, 1.0))) && all(greaterThan(shadowXYZ, vec3(0.05, 0.05, 0.0)));\n#if @perspectiveShadowMaps\n                    doneShadows = doneShadows && all(lessThan(shadowRegionXYZ, vec3(1.0, 1.0, 1.0))) && all(greaterThan(shadowRegionXYZ.xy, vec2(-1.0, -1.0)));\n#endif\n                }\n            }\n        @endforeach\n    #else\n        @foreach shadow_texture_unit_index @shadow_texture_unit_list\n            shadowing = min(shadow2DProj(shadowTexture@shadow_texture_unit_index, shadowSpaceCoords@shadow_texture_unit_index).r, shadowing);\n        @endforeach\n    #endif\n#if @limitShadowMapDistance\n    shadowing = mix(shadowing, 1.0, fade);\n#endif\n#endif // SHADOWS\n    return shadowing;\n}\n\nvoid applyShadowDebugOverlay()\n{\n#if SHADOWS && @useShadowDebugOverlay\n    bool doneOverlay = false;\n    float colourIndex = 0.0;\n    @foreach shadow_texture_unit_index @shadow_texture_unit_list\n        if (!doneOverlay)\n        {\n            vec3 shadowXYZ = shadowSpaceCoords@shadow_texture_unit_index.xyz / shadowSpaceCoords@shadow_texture_unit_index.w;\n#if @perspectiveShadowMaps\n            vec3 shadowRegionXYZ = shadowRegionCoords@shadow_texture_unit_index.xyz / shadowRegionCoords@shadow_texture_unit_index.w;\n#endif\n            if (all(lessThan(shadowXYZ.xy, vec2(1.0, 1.0))) && all(greaterThan(shadowXYZ.xy, vec2(0.0, 0.0))))\n            {\n                colourIndex = mod(@shadow_texture_unit_index.0, 3.0);\n\t\t        if (colourIndex < 1.0)\n\t\t\t        gl_FragData[0].x += 0.1;\n\t\t        else if (colourIndex < 2.0)\n\t\t\t        gl_FragData[0].y += 0.1;\n\t\t        else\n\t\t\t        gl_FragData[0].z += 0.1;\n\n                doneOverlay = all(lessThan(shadowXYZ, vec3(0.95, 0.95, 1.0))) && all(greaterThan(shadowXYZ, vec3(0.05, 0.05, 0.0)));\n#if @perspectiveShadowMaps\n                doneOverlay = doneOverlay && all(lessThan(shadowRegionXYZ.xyz, vec3(1.0, 1.0, 1.0))) && all(greaterThan(shadowRegionXYZ.xy, vec2(-1.0, -1.0)));\n#endif\n            }\n        }\n    @endforeach\n#endif // SHADOWS\n}"
  },
  {
    "path": "files/shaders/shadows_vertex.glsl",
    "content": "#define SHADOWS @shadows_enabled\n\n#if SHADOWS\n    @foreach shadow_texture_unit_index @shadow_texture_unit_list\n        uniform int shadowTextureUnit@shadow_texture_unit_index;\n        varying vec4 shadowSpaceCoords@shadow_texture_unit_index;\n\n#if @perspectiveShadowMaps\n        uniform mat4 validRegionMatrix@shadow_texture_unit_index;\n        varying vec4 shadowRegionCoords@shadow_texture_unit_index;\n#endif\n    @endforeach\n\n    // Enabling this may reduce peter panning. Probably unnecessary.\n    const bool onlyNormalOffsetUV = false;\n#endif // SHADOWS\n\nvoid setupShadowCoords(vec4 viewPos, vec3 viewNormal)\n{\n#if SHADOWS\n    // This matrix has the opposite handedness to the others used here, so multiplication must have the vector to the left. Alternatively it could be transposed after construction, but that's extra work for the GPU just to make the code look a tiny bit cleaner.\n    mat4 eyePlaneMat;\n    vec4 shadowOffset;\n    @foreach shadow_texture_unit_index @shadow_texture_unit_list\n        eyePlaneMat = mat4(gl_EyePlaneS[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneT[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneR[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneQ[shadowTextureUnit@shadow_texture_unit_index]);\n\n#if @perspectiveShadowMaps\n        shadowRegionCoords@shadow_texture_unit_index = validRegionMatrix@shadow_texture_unit_index * viewPos;\n#endif\n        \n#if @disableNormalOffsetShadows\n        shadowSpaceCoords@shadow_texture_unit_index = viewPos * eyePlaneMat;\n#else\n        shadowOffset = vec4(viewNormal * @shadowNormalOffset, 0.0);\n\n        if (onlyNormalOffsetUV)\n        {\n            shadowSpaceCoords@shadow_texture_unit_index = viewPos * eyePlaneMat;\n\n            vec4 lightSpaceXY = viewPos + shadowOffset;\n            lightSpaceXY = lightSpaceXY * eyePlaneMat;\n\n            shadowSpaceCoords@shadow_texture_unit_index.xy = lightSpaceXY.xy;\n        }\n        else\n        {\n            vec4 offsetViewPosition = viewPos + shadowOffset;\n            shadowSpaceCoords@shadow_texture_unit_index = offsetViewPosition * eyePlaneMat;\n        }\n#endif\n    @endforeach\n#endif // SHADOWS\n}"
  },
  {
    "path": "files/shaders/terrain_fragment.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\nvarying vec2 uv;\n\nuniform sampler2D diffuseMap;\n\n#if @normalMap\nuniform sampler2D normalMap;\n#endif\n\n#if @blendMap\nuniform sampler2D blendMap;\n#endif\n\nvarying float euclideanDepth;\nvarying float linearDepth;\n\n#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)\n\n#if !PER_PIXEL_LIGHTING\ncentroid varying vec3 passLighting;\ncentroid varying vec3 shadowDiffuseLighting;\n#endif\nvarying vec3 passViewPos;\nvarying vec3 passNormal;\n\n#include \"vertexcolors.glsl\"\n#include \"shadows_fragment.glsl\"\n#include \"lighting.glsl\"\n#include \"parallax.glsl\"\n\nvoid main()\n{\n    vec2 adjustedUV = (gl_TextureMatrix[0] * vec4(uv, 0.0, 1.0)).xy;\n\n#if @normalMap\n    vec4 normalTex = texture2D(normalMap, adjustedUV);\n\n    vec3 normalizedNormal = normalize(passNormal);\n    vec3 tangent = vec3(1.0, 0.0, 0.0);\n    vec3 binormal = normalize(cross(tangent, normalizedNormal));\n    tangent = normalize(cross(normalizedNormal, binormal)); // note, now we need to re-cross to derive tangent again because it wasn't orthonormal\n    mat3 tbnTranspose = mat3(tangent, binormal, normalizedNormal);\n\n    vec3 viewNormal = normalize(gl_NormalMatrix * (tbnTranspose * (normalTex.xyz * 2.0 - 1.0)));\n#endif\n\n#if (!@normalMap && (@parallax || @forcePPL))\n    vec3 viewNormal = gl_NormalMatrix * normalize(passNormal);\n#endif\n\n#if @parallax\n    vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz;\n    vec3 objectPos = (gl_ModelViewMatrixInverse * vec4(passViewPos, 1)).xyz;\n    vec3 eyeDir = normalize(cameraPos - objectPos);\n    adjustedUV += getParallaxOffset(eyeDir, tbnTranspose, normalTex.a, 1.f);\n\n    // update normal using new coordinates\n    normalTex = texture2D(normalMap, adjustedUV);\n    viewNormal = normalize(gl_NormalMatrix * (tbnTranspose * (normalTex.xyz * 2.0 - 1.0)));\n#endif\n\n    vec4 diffuseTex = texture2D(diffuseMap, adjustedUV);\n    gl_FragData[0] = vec4(diffuseTex.xyz, 1.0);\n\n#if @blendMap\n    vec2 blendMapUV = (gl_TextureMatrix[1] * vec4(uv, 0.0, 1.0)).xy;\n    gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a;\n#endif\n\n    vec4 diffuseColor = getDiffuseColor();\n    gl_FragData[0].a *= diffuseColor.a;\n\n    float shadowing = unshadowedLightRatio(linearDepth);\n    vec3 lighting;\n#if !PER_PIXEL_LIGHTING\n    lighting = passLighting + shadowDiffuseLighting * shadowing;\n#else\n    vec3 diffuseLight, ambientLight;\n    doLighting(passViewPos, normalize(viewNormal), shadowing, diffuseLight, ambientLight);\n    lighting = diffuseColor.xyz * diffuseLight + getAmbientColor().xyz * ambientLight + getEmissionColor().xyz;\n    clampLightingResult(lighting);\n#endif\n\n    gl_FragData[0].xyz *= lighting;\n\n#if @specularMap\n    float shininess = 128.0; // TODO: make configurable\n    vec3 matSpec = vec3(diffuseTex.a);\n#else\n    float shininess = gl_FrontMaterial.shininess;\n    vec3 matSpec = getSpecularColor().xyz;\n#endif\n\n    if (matSpec != vec3(0.0))\n    {\n#if (!@normalMap && !@parallax && !@forcePPL)\n        vec3 viewNormal = gl_NormalMatrix * normalize(passNormal);\n#endif\n        gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing;\n    }\n\n#if @radialFog\n    float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#else\n    float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#endif\n    gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);\n\n    applyShadowDebugOverlay();\n}\n"
  },
  {
    "path": "files/shaders/terrain_vertex.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\nvarying vec2 uv;\nvarying float euclideanDepth;\nvarying float linearDepth;\n\n#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)\n\n#if !PER_PIXEL_LIGHTING\ncentroid varying vec3 passLighting;\ncentroid varying vec3 shadowDiffuseLighting;\n#endif\nvarying vec3 passViewPos;\nvarying vec3 passNormal;\n\n#include \"vertexcolors.glsl\"\n#include \"shadows_vertex.glsl\"\n\n#include \"lighting.glsl\"\n\nvoid main(void)\n{\n    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\n    vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);\n    gl_ClipVertex = viewPos;\n    euclideanDepth = length(viewPos.xyz);\n    linearDepth = gl_Position.z;\n\n#if (!PER_PIXEL_LIGHTING || @shadows_enabled)\n    vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);\n#endif\n\n    passColor = gl_Color;\n    passNormal = gl_Normal.xyz;\n    passViewPos = viewPos.xyz;\n\n#if !PER_PIXEL_LIGHTING\n    vec3 diffuseLight, ambientLight;\n    doLighting(viewPos.xyz, viewNormal, diffuseLight, ambientLight, shadowDiffuseLighting);\n    passLighting = getDiffuseColor().xyz * diffuseLight + getAmbientColor().xyz * ambientLight + getEmissionColor().xyz;\n    clampLightingResult(passLighting);\n    shadowDiffuseLighting *= getDiffuseColor().xyz;\n#endif\n\n    uv = gl_MultiTexCoord0.xy;\n\n#if (@shadows_enabled)\n    setupShadowCoords(viewPos, viewNormal);\n#endif\n}\n"
  },
  {
    "path": "files/shaders/vertexcolors.glsl",
    "content": "centroid varying vec4 passColor;\n\nuniform int colorMode;\n\nconst int ColorMode_None = 0;\nconst int ColorMode_Emission = 1;\nconst int ColorMode_AmbientAndDiffuse = 2;\nconst int ColorMode_Ambient = 3;\nconst int ColorMode_Diffuse = 4;\nconst int ColorMode_Specular = 5;\n\nvec4 getEmissionColor()\n{\n    if (colorMode == ColorMode_Emission)\n        return passColor;\n    return gl_FrontMaterial.emission;\n}\n\nvec4 getAmbientColor()\n{\n    if (colorMode == ColorMode_AmbientAndDiffuse || colorMode == ColorMode_Ambient)\n        return passColor;\n    return gl_FrontMaterial.ambient;\n}\n\nvec4 getDiffuseColor()\n{\n    if (colorMode == ColorMode_AmbientAndDiffuse || colorMode == ColorMode_Diffuse)\n        return passColor;\n    return gl_FrontMaterial.diffuse;\n}\n\nvec4 getSpecularColor()\n{\n    if (colorMode == ColorMode_Specular)\n        return passColor;\n    return gl_FrontMaterial.specular;\n}\n"
  },
  {
    "path": "files/shaders/water_fragment.glsl",
    "content": "#version 120\n\n#if @useUBO\n    #extension GL_ARB_uniform_buffer_object : require\n#endif\n\n#if @useGPUShader4\n    #extension GL_EXT_gpu_shader4: require\n#endif\n\n#define REFRACTION @refraction_enabled\n\n// Inspired by Blender GLSL Water by martinsh ( https://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html )\n\n// tweakables -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\n\nconst float VISIBILITY = 2500.0;\n\nconst float BIG_WAVES_X = 0.1; // strength of big waves\nconst float BIG_WAVES_Y = 0.1;\n\nconst float MID_WAVES_X = 0.1; // strength of middle sized waves\nconst float MID_WAVES_Y = 0.1;\nconst float MID_WAVES_RAIN_X = 0.2;\nconst float MID_WAVES_RAIN_Y = 0.2;\n\nconst float SMALL_WAVES_X = 0.1; // strength of small waves\nconst float SMALL_WAVES_Y = 0.1;\nconst float SMALL_WAVES_RAIN_X = 0.3;\nconst float SMALL_WAVES_RAIN_Y = 0.3;\n\nconst float WAVE_CHOPPYNESS = 0.05;                // wave choppyness\nconst float WAVE_SCALE = 75.0;                     // overall wave scale\n\nconst float BUMP = 0.5;                            // overall water surface bumpiness\nconst float BUMP_RAIN = 2.5;\nconst float REFL_BUMP = 0.10;                      // reflection distortion amount\nconst float REFR_BUMP = 0.07;                      // refraction distortion amount\n\nconst float SCATTER_AMOUNT = 0.3;                  // amount of sunlight scattering\nconst vec3 SCATTER_COLOUR = vec3(0.0,1.0,0.95);    // colour of sunlight scattering\n\nconst vec3 SUN_EXT = vec3(0.45, 0.55, 0.68);       //sunlight extinction\n\nconst float SPEC_HARDNESS = 256.0;                 // specular highlights hardness\n\nconst float BUMP_SUPPRESS_DEPTH = 300.0;           // at what water depth bumpmap will be suppressed for reflections and refractions (prevents artifacts at shores)\n\nconst vec2 WIND_DIR = vec2(0.5f, -0.8f);\nconst float WIND_SPEED = 0.2f;\n\nconst vec3 WATER_COLOR = vec3(0.090195, 0.115685, 0.12745);\n\n// ---------------- rain ripples related stuff ---------------------\n\nconst float RAIN_RIPPLE_GAPS = 5.0;\nconst float RAIN_RIPPLE_RADIUS = 0.1;\n\nvec2 randOffset(vec2 c)\n{\n  return fract(vec2(\n          c.x * c.y / 8.0 + c.y * 0.3 + c.x * 0.2,\n          c.x * c.y / 14.0 + c.y * 0.5 + c.x * 0.7));\n}\n\nfloat randPhase(vec2 c)\n{\n  return fract((c.x * c.y) /  (c.x + c.y + 0.1));\n}\n\nvec4 circle(vec2 coords, vec2 i_part, float phase)\n{\n  vec2 center = vec2(0.5,0.5) + (0.5 - RAIN_RIPPLE_RADIUS) * (2.0 * randOffset(i_part) - 1.0);\n  vec2 toCenter = coords - center;\n  float d = length(toCenter);\n\n  float r = RAIN_RIPPLE_RADIUS * phase;\n\n  if (d > r)\n    return vec4(0.0,0.0,1.0,0.0);\n\n  float sinValue = (sin(d / r * 1.2) + 0.7) / 2.0;\n\n  float height = (1.0 - abs(phase)) * pow(sinValue,3.0);\n\n  vec3 normal = normalize(mix(vec3(0.0,0.0,1.0),vec3(normalize(toCenter),0.0),height));\n\n  return vec4(normal,height);\n}\n\nvec4 rain(vec2 uv, float time)\n{\n  vec2 i_part = floor(uv * RAIN_RIPPLE_GAPS);\n  vec2 f_part = fract(uv * RAIN_RIPPLE_GAPS);\n  return circle(f_part,i_part,fract(time * 1.2 + randPhase(i_part)));\n}\n\nvec4 rainCombined(vec2 uv, float time)     // returns ripple normal in xyz and ripple height in w\n{\n  return\n    rain(uv,time) +\n    rain(uv + vec2(10.5,5.7),time) +\n    rain(uv * 0.75 + vec2(3.7,18.9),time) +\n    rain(uv * 0.9 + vec2(5.7,30.1),time) +\n    rain(uv * 0.8 + vec2(1.2,3.0),time);\n}\n\n// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -\n\nfloat fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)\n  {\n    float c = abs(dot(Incoming, Normal));\n    float g = eta * eta - 1.0 + c * c;\n    float result;\n\n    if(g > 0.0) {\n        g = sqrt(g);\n        float A =(g - c)/(g + c);\n        float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0);\n        result = 0.5 * A * A *(1.0 + B * B);\n    }\n    else\n        result = 1.0;  /* TIR (no refracted component) */\n\n    return result;\n  }\n\nvec2 normalCoords(vec2 uv, float scale, float speed, float time, float timer1, float timer2, vec3 previousNormal)\n  {\n    return uv * (WAVE_SCALE * scale) + WIND_DIR * time * (WIND_SPEED * speed) -(previousNormal.xy/previousNormal.zz) * WAVE_CHOPPYNESS + vec2(time * timer1,time * timer2);\n  }\n\nvarying vec3 screenCoordsPassthrough;\nvarying vec4 position;\nvarying float linearDepth;\n\nuniform sampler2D normalMap;\n\nuniform sampler2D reflectionMap;\n#if REFRACTION\nuniform sampler2D refractionMap;\nuniform sampler2D refractionDepthMap;\n#endif\n\nuniform float osg_SimulationTime;\n\nuniform float near;\nuniform float far;\nuniform vec3 nodePosition;\n\nuniform float rainIntensity;\n\n#define PER_PIXEL_LIGHTING 0\n\n#include \"shadows_fragment.glsl\"\n#include \"lighting.glsl\"\n\nfloat frustumDepth;\n\nfloat linearizeDepth(float depth)\n  {\n    float z_n = 2.0 * depth - 1.0;\n    depth = 2.0 * near * far / (far + near - z_n * frustumDepth);\n    return depth;\n  }\n\nvoid main(void)\n{\n    frustumDepth = abs(far - near);\n    vec3 worldPos = position.xyz + nodePosition.xyz;\n    vec2 UV = worldPos.xy / (8192.0*5.0) * 3.0;\n    UV.y *= -1.0;\n\n    float shadow = unshadowedLightRatio(linearDepth);\n\n    vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z;\n    screenCoords.y = (1.0-screenCoords.y);\n\n    #define waterTimer osg_SimulationTime\n\n    vec3 normal0 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.05, 0.04, waterTimer, -0.015, -0.005, vec3(0.0,0.0,0.0))).rgb - 1.0;\n    vec3 normal1 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.1,  0.08, waterTimer,  0.02,   0.015, normal0)).rgb - 1.0;\n    vec3 normal2 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.25, 0.07, waterTimer, -0.04,  -0.03,  normal1)).rgb - 1.0;\n    vec3 normal3 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.5,  0.09, waterTimer,  0.03,   0.04,  normal2)).rgb - 1.0;\n    vec3 normal4 = 2.0 * texture2D(normalMap,normalCoords(UV, 1.0,  0.4,  waterTimer, -0.02,   0.1,   normal3)).rgb - 1.0;\n    vec3 normal5 = 2.0 * texture2D(normalMap,normalCoords(UV, 2.0,  0.7,  waterTimer,  0.1,   -0.06,  normal4)).rgb - 1.0;\n\n    vec4 rainRipple;\n\n    if (rainIntensity > 0.01)\n      rainRipple = rainCombined(position.xy / 1000.0,waterTimer) * clamp(rainIntensity,0.0,1.0);\n    else\n      rainRipple = vec4(0.0);\n\n    vec3 rippleAdd = rainRipple.xyz * rainRipple.w * 10.0;\n\n    vec2 bigWaves = vec2(BIG_WAVES_X,BIG_WAVES_Y);\n    vec2 midWaves = mix(vec2(MID_WAVES_X,MID_WAVES_Y),vec2(MID_WAVES_RAIN_X,MID_WAVES_RAIN_Y),rainIntensity);\n    vec2 smallWaves = mix(vec2(SMALL_WAVES_X,SMALL_WAVES_Y),vec2(SMALL_WAVES_RAIN_X,SMALL_WAVES_RAIN_Y),rainIntensity);\n    float bump = mix(BUMP,BUMP_RAIN,rainIntensity);\n\n    vec3 normal = (normal0 * bigWaves.x + normal1 * bigWaves.y + normal2 * midWaves.x +\n                   normal3 * midWaves.y + normal4 * smallWaves.x + normal5 * smallWaves.y + rippleAdd);\n    normal = normalize(vec3(-normal.x * bump, -normal.y * bump, normal.z));\n\n    vec3 lVec = normalize((gl_ModelViewMatrixInverse * vec4(lcalcPosition(0).xyz, 0.0)).xyz);\n    vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz;\n    vec3 vVec = normalize(position.xyz - cameraPos.xyz);\n\n    float sunFade = length(gl_LightModel.ambient.xyz);\n\n    // fresnel\n    float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); // air to water; water to air\n    float fresnel = clamp(fresnel_dielectric(vVec, normal, ior), 0.0, 1.0);\n\n    float radialise = 1.0;\n\n#if @radialFog\n    float radialDepth = distance(position.xyz, cameraPos);\n    // TODO: Figure out how to properly radialise refraction depth and thus underwater fog\n    // while avoiding oddities when the water plane is close to the clipping plane\n    // radialise = radialDepth / linearDepth;\n#endif\n\n    vec2 screenCoordsOffset = normal.xy * REFL_BUMP;\n#if REFRACTION\n    float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x) * radialise;\n    float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x) * radialise;\n    float surfaceDepth = linearizeDepth(gl_FragCoord.z) * radialise;\n    float realWaterDepth = depthSample - surfaceDepth;  // undistorted water depth in view direction, independent of frustum\n    screenCoordsOffset *= clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1);\n#endif\n    // reflection\n    vec3 reflection = texture2D(reflectionMap, screenCoords + screenCoordsOffset).rgb;\n\n    // specular\n    float specular = pow(max(dot(reflect(vVec, normal), lVec), 0.0),SPEC_HARDNESS) * shadow;\n\n    vec3 waterColor = WATER_COLOR * sunFade;\n\n    vec4 sunSpec = lcalcSpecular(0);\n\n#if REFRACTION\n    // refraction\n    vec3 refraction = texture2D(refractionMap, screenCoords - screenCoordsOffset).rgb;\n\n    // brighten up the refraction underwater\n    if (cameraPos.z < 0.0)\n        refraction = clamp(refraction * 1.5, 0.0, 1.0);\n    else\n        refraction = mix(refraction, waterColor, clamp(depthSampleDistorted/VISIBILITY, 0.0, 1.0));\n\n    // sunlight scattering\n    // normal for sunlight scattering\n    vec3 lNormal = (normal0 * bigWaves.x * 0.5 + normal1 * bigWaves.y * 0.5 + normal2 * midWaves.x * 0.2 +\n                    normal3 * midWaves.y * 0.2 + normal4 * smallWaves.x * 0.1 + normal5 * smallWaves.y * 0.1 + rippleAdd);\n    lNormal = normalize(vec3(-lNormal.x * bump, -lNormal.y * bump, lNormal.z));\n    float sunHeight = lVec.z;\n    vec3 scatterColour = mix(SCATTER_COLOUR*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0));\n    vec3 lR = reflect(lVec, lNormal);\n    float lightScatter = clamp(dot(lVec,lNormal)*0.7+0.3, 0.0, 1.0) * clamp(dot(lR, vVec)*2.0-1.2, 0.0, 1.0) * SCATTER_AMOUNT * sunFade * clamp(1.0-exp(-sunHeight), 0.0, 1.0);\n    gl_FragData[0].xyz = mix( mix(refraction,  scatterColour,  lightScatter),  reflection,  fresnel) + specular * sunSpec.xyz + vec3(rainRipple.w) * 0.2;\n    gl_FragData[0].w = 1.0;\n#else\n    gl_FragData[0].xyz = mix(reflection,  waterColor,  (1.0-fresnel)*0.5) + specular * sunSpec.xyz + vec3(rainRipple.w) * 0.7;\n    gl_FragData[0].w = clamp(fresnel*6.0 + specular * sunSpec.w, 0.0, 1.0);     //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0);\n#endif\n\n    // fog\n#if @radialFog\n    float fogValue = clamp((radialDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#else\n    float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);\n#endif\n    gl_FragData[0].xyz = mix(gl_FragData[0].xyz,  gl_Fog.color.xyz,  fogValue);\n\n    applyShadowDebugOverlay();\n}\n"
  },
  {
    "path": "files/shaders/water_vertex.glsl",
    "content": "#version 120\n    \nvarying vec3  screenCoordsPassthrough;\nvarying vec4  position;\nvarying float linearDepth;\n\n#include \"shadows_vertex.glsl\"\n\nvoid main(void)\n{\n    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\n    mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0,\n                         0.0, -0.5, 0.0, 0.0,\n                         0.0, 0.0, 0.5, 0.0,\n                         0.5, 0.5, 0.5, 1.0);\n\n    vec4 texcoordProj = ((scalemat) * ( gl_Position));\n    screenCoordsPassthrough = texcoordProj.xyw;\n\n    position = gl_Vertex;\n\n    linearDepth = gl_Position.z;\n\n    setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz));\n}\n"
  },
  {
    "path": "files/tes3mp/browser.rc",
    "content": "IDI_ICON1               ICON    DISCARDABLE     \"tes3mp.ico\"\n"
  },
  {
    "path": "files/tes3mp/tes3mp-client-default.cfg",
    "content": "[General]\ndestinationAddress = localhost\nport = 25565\npassword =\n# 0 - Verbose (spam), 1 - Info, 2 - Warnings, 3 - Errors, 4 - Only fatal errors\nlogLevel = 0\n\n[Master]\naddress = master.tes3mp.com\nport = 25561\n\n[Chat]\n# Use https://wiki.libsdl.org/SDL_Keycode to find the correct key codes when rebinding\n#\n# For chatting\nkeySay = Y\n# For enabling or disabling the chat window\nkeyChatMode = F2\nx = 0\ny = 0\nw = 390\nh = 250\n# How long the message will be displayed in hidden mode\ndelay = 5.0\n"
  },
  {
    "path": "files/tes3mp/tes3mp-server-default.cfg",
    "content": "[General]\n# The default localAddress of 0.0.0.0 makes the server reachable at all of its local addresses\n# You almost never have to change this\nlocalAddress = 0.0.0.0\nport = 25565\nmaximumPlayers = 64\nhostname = TES3MP server\n# 0 - Verbose (spam), 1 - Info, 2 - Warnings, 3 - Errors, 4 - Only fatal errors\nlogLevel = 1\npassword =\n\n[Plugins]\nhome = ./server\nplugins = serverCore.lua\n\n[MasterServer]\nenabled = true\naddress = master.tes3mp.com\nport = 25561\nrate = 10000\n"
  },
  {
    "path": "files/tes3mp/tes3mp.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n    <assemblyIdentity type=\"win32\" name=\"tes3mp\" version=\"1.0.0.0\"/>\n    <description>tes3mp</description>\n    <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n        <security>\n            <requestedPrivileges>\n                <requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\"/>\n            </requestedPrivileges>\n        </security>\n    </trustInfo>\n    <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n        <windowsSettings xmlns:ws=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">\n            <ws:dpiAware>True</ws:dpiAware>\n        </windowsSettings>\n    </application>\n</assembly>\n\n"
  },
  {
    "path": "files/tes3mp/tes3mp.rc",
    "content": "IDI_ICON1               ICON    DISCARDABLE     \"tes3mp.ico\"\nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST \"tes3mp.exe.manifest\"\n"
  },
  {
    "path": "files/tes3mp/ui/Main.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\"MainWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>800</width>\n    <height>600</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>tes3mp Server Browser</string>\n  </property>\n  <widget class=\"QWidget\" name=\"centralwidget\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <item>\n     <widget class=\"QTabWidget\" name=\"tabWidget\">\n      <property name=\"currentIndex\">\n       <number>0</number>\n      </property>\n      <widget class=\"QWidget\" name=\"tabBrowser\">\n       <attribute name=\"title\">\n        <string>Browser</string>\n       </attribute>\n       <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n        <item>\n         <widget class=\"QTableView\" name=\"tblServerBrowser\">\n          <property name=\"selectionMode\">\n           <enum>QAbstractItemView::SingleSelection</enum>\n          </property>\n          <property name=\"selectionBehavior\">\n           <enum>QAbstractItemView::SelectRows</enum>\n          </property>\n          <property name=\"sortingEnabled\">\n           <bool>true</bool>\n          </property>\n          <attribute name=\"verticalHeaderVisible\">\n           <bool>false</bool>\n          </attribute>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n      <widget class=\"QWidget\" name=\"tabFavorites\">\n       <attribute name=\"title\">\n        <string>Favorites</string>\n       </attribute>\n       <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n        <item>\n         <widget class=\"QTableView\" name=\"tblFavorites\">\n          <property name=\"selectionMode\">\n           <enum>QAbstractItemView::SingleSelection</enum>\n          </property>\n          <property name=\"selectionBehavior\">\n           <enum>QAbstractItemView::SelectRows</enum>\n          </property>\n          <property name=\"sortingEnabled\">\n           <bool>true</bool>\n          </property>\n          <attribute name=\"verticalHeaderVisible\">\n           <bool>false</bool>\n          </attribute>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"QGroupBox\" name=\"groupBoxFilters\">\n      <property name=\"title\">\n       <string>Filters</string>\n      </property>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n         <item>\n          <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n           <item>\n            <widget class=\"QLabel\" name=\"lblMaxLatency\">\n             <property name=\"text\">\n              <string>Max latency:</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QComboBox\" name=\"comboLatency\">\n             <item>\n              <property name=\"text\">\n               <string>All</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>&lt;50</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>&lt;100</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>&lt;150</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>&lt;200</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>&lt;250</string>\n              </property>\n             </item>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n           <item>\n            <widget class=\"QLabel\" name=\"lblGameMode\">\n             <property name=\"text\">\n              <string>Game mode</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLineEdit\" name=\"leGamemode\"/>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer_2\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_7\">\n         <item>\n          <widget class=\"QCheckBox\" name=\"cBoxNotFull\">\n           <property name=\"text\">\n            <string>Not full</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QCheckBox\" name=\"cBoxWithPlayers\">\n           <property name=\"text\">\n            <string>With players</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QCheckBox\" name=\"cBBoxWOPass\">\n           <property name=\"text\">\n            <string>No password</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n  <widget class=\"QToolBar\" name=\"toolBar\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"windowTitle\">\n    <string>toolBar</string>\n   </property>\n   <property name=\"movable\">\n    <bool>false</bool>\n   </property>\n   <property name=\"floatable\">\n    <bool>false</bool>\n   </property>\n   <attribute name=\"toolBarArea\">\n    <enum>TopToolBarArea</enum>\n   </attribute>\n   <attribute name=\"toolBarBreak\">\n    <bool>false</bool>\n   </attribute>\n   <addaction name=\"actionPlay\"/>\n   <addaction name=\"separator\"/>\n   <addaction name=\"actionRefresh\"/>\n  </widget>\n  <action name=\"actionAdd\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Add</string>\n   </property>\n  </action>\n  <action name=\"actionDelete\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Delete</string>\n   </property>\n  </action>\n  <action name=\"actionRefresh\">\n   <property name=\"text\">\n    <string>Refresh</string>\n   </property>\n  </action>\n  <action name=\"actionPlay\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Play</string>\n   </property>\n  </action>\n  <action name=\"actionAdd_by_IP\">\n   <property name=\"text\">\n    <string>Add by address</string>\n   </property>\n  </action>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/tes3mp/ui/ServerInfo.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>Dialog</class>\n <widget class=\"QDialog\" name=\"Dialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>430</width>\n    <height>447</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Connect</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"text\">\n        <string>Server Name:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"lblName\">\n       <property name=\"text\">\n        <string notr=\"true\">TextLabel</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n     <item>\n      <widget class=\"QLabel\" name=\"label_5\">\n       <property name=\"text\">\n        <string>Address:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLineEdit\" name=\"leAddr\">\n       <property name=\"text\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"readOnly\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n     <item>\n      <widget class=\"QLabel\" name=\"label_7\">\n       <property name=\"text\">\n        <string>Players:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"lblPlayers\">\n       <property name=\"text\">\n        <string notr=\"true\">0 / 0</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_3\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n     <item>\n      <widget class=\"QLabel\" name=\"label_8\">\n       <property name=\"text\">\n        <string>Ping:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"lblPing\">\n       <property name=\"text\">\n        <string notr=\"true\">-1</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_4\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>Players:</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QListWidget\" name=\"listPlayers\">\n     <property name=\"selectionMode\">\n      <enum>QAbstractItemView::NoSelection</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabW\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"PagePlugins\">\n      <attribute name=\"title\">\n       <string>Plugins</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n       <item>\n        <widget class=\"QListWidget\" name=\"listPlugins\">\n         <property name=\"selectionMode\">\n          <enum>QAbstractItemView::NoSelection</enum>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"PageRules\">\n      <attribute name=\"title\">\n       <string>Rules</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n       <item>\n        <widget class=\"QListWidget\" name=\"listRules\"/>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"btnCancel\">\n       <property name=\"text\">\n        <string>Cancel</string>\n       </property>\n       <property name=\"default\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"btnRefresh\">\n       <property name=\"text\">\n        <string>Refresh</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"btnConnect\">\n       <property name=\"enabled\">\n        <bool>false</bool>\n       </property>\n       <property name=\"text\">\n        <string>Connect</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>btnCancel</sender>\n   <signal>clicked()</signal>\n   <receiver>Dialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>489</x>\n     <y>524</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>316</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>btnConnect</sender>\n   <signal>clicked()</signal>\n   <receiver>Dialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>580</x>\n     <y>524</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>316</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "files/tes3mp-browser.desktop",
    "content": "[Desktop Entry]\nType=Application\nName=TES3MP Server Browser\nGenericName=Server Browser\nComment=Multiplayer extension for TES3.\nKeywords=Morrowind;Multiplayer;Server Browser;TES;openmw;\nTryExec=tes3mp-browser\nExec=tes3mp-browser\nIcon=openmw\nCategories=Game;RolePlaying;\n"
  },
  {
    "path": "files/ui/advancedpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AdvancedPage</class>\n <widget class=\"QWidget\" name=\"AdvancedPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>732</width>\n    <height>487</height>\n   </rect>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"pageVerticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"AdvancedTabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"GameMechanics\">\n      <attribute name=\"title\">\n       <string>Game Mechanics</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <layout class=\"QGridLayout\" name=\"gridLayout_3\">\n         <item row=\"3\" column=\"1\">\n          <widget class=\"QCheckBox\" name=\"enchantedWeaponsMagicalCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make enchanted weaponry without Magical flag bypass normal weapons resistance, like in Morrowind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Enchanted weapons are magical</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <widget class=\"QCheckBox\" name=\"swimUpwardCorrectionCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Makes player swim a bit upward from the line of sight. Applies only in third person mode. Intended to make simpler swimming without diving.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Swim upward correction</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"6\" column=\"0\">\n          <widget class=\"QCheckBox\" name=\"avoidCollisionsCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled NPCs apply evasion maneuver to avoid collisions with others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>NPCs avoid collisions</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"0\">\n          <widget class=\"QCheckBox\" name=\"enableNavigatorCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable navigator. When enabled background threads are started to build nav mesh for world geometry. Pathfinding system uses nav mesh to build paths. When disabled only pathgrid is used to build paths. Single-core CPU systems may have big performance impact on exiting interior location and moving across exterior world. May slightly affect performance on multi-core CPU systems. Multi-core CPU systems may have different latency for nav mesh update depending on other settings and system performance. Moving across external world, entering/exiting location produce nav mesh update. NPC and creatures may not be able to find path before nav mesh is built around them. Try to disable this if you want to have old fashioned AI which doesn’t know where to go when you stand behind that stone and casting a firebolt.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Build nav mesh for world geometry</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QCheckBox\" name=\"toggleSneakCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This setting causes the behavior of the sneak key (bound to Ctrl by default) to toggle sneaking on and off rather than requiring the key to be held down while sneaking. Players that spend significant time sneaking may find the character easier to control with this option enabled. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Toggle sneak</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"0\">\n          <widget class=\"QCheckBox\" name=\"permanentBarterDispositionChangeCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make disposition change of merchants caused by trading permanent.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Permanent barter disposition changes</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"1\">\n          <widget class=\"QCheckBox\" name=\"normaliseRaceSpeedCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Don't use race weight in NPC movement speed calculations.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Racial variation in speed fix</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QCheckBox\" name=\"uncappedDamageFatigueCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make Damage Fatigue magic effect uncapped like Drain Fatigue effect.&lt;/p&gt;&lt;p&gt;This means that unlike Morrowind you will be able to knock down actors using this effect.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Uncapped Damage Fatigue</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\">\n          <widget class=\"QCheckBox\" name=\"canLootDuringDeathAnimationCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.&lt;/p&gt;&lt;p&gt;If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Can loot during death animation</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QCheckBox\" name=\"rebalanceSoulGemValuesCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make the value of filled soul gems dependent only on soul magnitude.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Soulgem values rebalance</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"0\">\n          <widget class=\"QCheckBox\" name=\"followersAttackOnSightCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Followers defend immediately</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"1\">\n          <widget class=\"QCheckBox\" name=\"classicReflectedAbsorbSpellsCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Effects of reflected Absorb spells are not mirrored -- like in Morrowind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Classic reflected Absorb spells behavior</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"1\">\n          <widget class=\"QCheckBox\" name=\"stealingFromKnockedOutCheckBox\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make stealing items from NPCs that were knocked down possible during combat.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Always allow stealing from knocked out actors</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"6\" column=\"1\">\n          <widget class=\"QCheckBox\" name=\"allowNPCToFollowOverWaterSurfaceCheckBox\">\n           <property name=\"toolTip\">\n            <string>Give NPC an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled.</string>\n           </property>\n           <property name=\"text\">\n            <string>Always allow NPC to follow over water surface</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"requireAppropriateAmmunitionCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allow non-standard ammunition solely to bypass normal weapon resistance or weakness.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Only appropriate ammunition bypasses normal weapon resistance</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalUnarmedStrengthLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"unarmedFactorsStrengthLabel\">\n           <property name=\"text\">\n            <string>Factor strength into hand-to-hand combat:</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"unarmedFactorsStrengthComboBox\">\n           <property name=\"currentIndex\">\n            <number>0</number>\n           </property>\n           <item>\n            <property name=\"text\">\n             <string>Off</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Affect werewolves</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Do not affect werewolves</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer_2\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalPhysicsThreadsLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"physicsThreadsLabel\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;How many threads will be spawned to compute physics update in the background. A value of 0 means that the update will be performed in the main thread.&lt;/p&gt;&lt;p&gt;A value greater than 1 requires the Bullet library be compiled with multithreading support.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>Background physics threads</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"physicsThreadsSpinBox\"/>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer_4\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer_3\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>40</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"Visuals\">\n      <attribute name=\"title\">\n       <string>Visuals</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QScrollArea\" name=\"visualsScrollArea\">\n         <property name=\"widgetResizable\">\n          <bool>true</bool>\n         </property>\n         <widget class=\"QWidget\" name=\"visualsScrollAreaWidgetContents\">\n          <property name=\"geometry\">\n           <rect>\n            <x>0</x>\n            <y>0</y>\n            <width>645</width>\n            <height>413</height>\n           </rect>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"animationsGroup\">\n             <property name=\"title\">\n              <string>Animations</string>\n             </property>\n             <layout class=\"QGridLayout\" name=\"animationsLayout\">\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QCheckBox\" name=\"magicItemAnimationsCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use casting animations for magic items, just as for spells.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Use magic item animation</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"smoothMovementCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Makes NPCs and player movement more smooth. Recommended to use with &quot;turn to movement direction&quot; enabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Smooth movement</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <widget class=\"QCheckBox\" name=\"animSourcesCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load per-group KF-files and skeleton files from Animations folder&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Use additional animation sources</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"turnToMovementDirectionCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Affects side and diagonal movement. Enabling this setting makes movement more realistic.&lt;/p&gt;&lt;p&gt;If disabled then the whole character's body is pointed to the direction of view. Diagonal movement has no special animation and causes sliding.&lt;/p&gt;&lt;p&gt;If enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it changes straight right and straight left movement as well. Also turns the whole body up or down when swimming according to the movement direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Turn to movement direction</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"0\">\n               <layout class=\"QVBoxLayout\" name=\"sheathingLayout\">\n                <property name=\"leftMargin\">\n                 <number>20</number>\n                </property>\n                <item>\n                 <widget class=\"QCheckBox\" name=\"weaponSheathingCheckBox\">\n                  <property name=\"enabled\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"toolTip\">\n                   <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Render holstered weapons (with quivers and scabbards), requires modded assets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>Weapon sheathing</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QCheckBox\" name=\"shieldSheathingCheckBox\">\n                  <property name=\"enabled\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"toolTip\">\n                   <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Render holstered shield, requires modded assets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>Shield sheathing</string>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QGroupBox\" name=\"shadersGroup\">\n             <property name=\"title\">\n              <string>Shaders</string>\n             </property>\n             <layout class=\"QGridLayout\" name=\"shaderLayout\">\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QCheckBox\" name=\"autoUseObjectNormalMapsCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this option is enabled, normal maps are automatically recognized and used if they are named appropriately\n                  (see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds).\n                  If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file). Affects objects.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Auto use object normal maps</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"autoUseTerrainNormalMapsCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;See 'auto use object normal maps'. Affects terrain.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Auto use terrain normal maps</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <widget class=\"QCheckBox\" name=\"autoUseObjectSpecularMapsCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this option is enabled, specular maps are automatically recognized and used if they are named appropriately\n                  (see 'specular map pattern', e.g. for a base texture foo.dds,\n                  the specular map texture would have to be named foo_spec.dds).\n                  If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file\n                  (.osg file, not supported in .nif files). Affects objects.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Auto use object specular maps</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"autoUseTerrainSpecularMapsCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. The texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Auto use terrain specular maps</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"0\">\n               <widget class=\"QCheckBox\" name=\"bumpMapLocalLightingCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark.\n                  Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option.\n                  Affected objects will use shaders.\n                  &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Bump/reflect map local lighting</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"radialFogCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen.\n                  This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Radial fog</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QGroupBox\" name=\"terrainGroup\">\n             <property name=\"title\">\n              <string>Terrain</string>\n             </property>\n             <layout class=\"QGridLayout\" name=\"terrainLayout\">\n              <item row=\"0\" column=\"0\">\n               <layout class=\"QHBoxLayout\" name=\"viewingDistanceLayout\">\n                <item>\n                 <widget class=\"QLabel\" name=\"viewingDistanceLabel\">\n                  <property name=\"text\">\n                   <string>Viewing distance</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QDoubleSpinBox\" name=\"viewingDistanceComboBox\">\n                  <property name=\"suffix\">\n                   <string> Cells</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <double>0.000000000000000</double>\n                  </property>\n                  <property name=\"singleStep\">\n                   <double>0.500000000000000</double>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <layout class=\"QHBoxLayout\" name=\"objectPagingMinSizeLayout\">\n                <item>\n                 <widget class=\"QLabel\" name=\"objectPagingMinSizeLabel\">\n                  <property name=\"toolTip\">\n                   <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls how large an object must be to be visible in the scene. The object’s size is divided by its distance to the camera and the result of the division is compared with this value. The smaller this value is, the more objects you will see in the scene.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>Object paging min size</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QDoubleSpinBox\" name=\"objectPagingMinSizeComboBox\">\n                  <property name=\"decimals\">\n                   <number>3</number>\n                  </property>\n                  <property name=\"minimum\">\n                   <double>0.000000000000000</double>\n                  </property>\n                  <property name=\"maximum\">\n                   <double>0.250000000000000</double>\n                  </property>\n                  <property name=\"singleStep\">\n                   <double>0.005000000000000</double>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item row=\"2\" column=\"0\">\n               <widget class=\"QCheckBox\" name=\"distantLandCheckBox\">\n                <property name=\"toolTip\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Distant land</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"3\" column=\"0\">\n               <layout class=\"QVBoxLayout\" name=\"distantLandLayout\">\n                <property name=\"leftMargin\">\n                 <number>20</number>\n                </property>\n                <item>\n                 <widget class=\"QCheckBox\" name=\"activeGridObjectPagingCheckBox\">\n                  <property name=\"toolTip\">\n                   <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use object paging for active cells grid.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>Active grid object paging</string>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"Audio\">\n      <attribute name=\"title\">\n       <string>Audio</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"audioDeviceSelectorLabel\">\n           <property name=\"toolTip\">\n            <string>Select your preferred audio device.</string>\n           </property>\n           <property name=\"text\">\n            <string>Audio Device</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"audioDeviceSelectorComboBox\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>283</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"currentIndex\">\n            <number>0</number>\n           </property>\n           <item>\n            <property name=\"text\">\n             <string>Default</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"enableHRTFLabel\">\n           <property name=\"toolTip\">\n            <string>This setting controls HRTF, which simulates 3D sound on stereo systems.</string>\n           </property>\n           <property name=\"text\">\n            <string>HRTF</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"enableHRTFComboBox\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>283</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"currentIndex\">\n            <number>0</number>\n           </property>\n           <item>\n            <property name=\"text\">\n             <string>Automatic</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Off</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>On</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"hrtfProfileSelectorLabel\">\n           <property name=\"toolTip\">\n            <string>Select your preferred HRTF profile.</string>\n           </property>\n           <property name=\"text\">\n            <string>HRTF Profile</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"hrtfProfileSelectorComboBox\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>283</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"currentIndex\">\n            <number>0</number>\n           </property>\n           <item>\n            <property name=\"text\">\n             <string>Default</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>0</width>\n           <height>0</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"cameraSettings\">\n      <attribute name=\"title\">\n       <string>Camera</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QCheckBox\" name=\"viewOverShoulderCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This setting controls third person view mode.&lt;/p&gt;&lt;p&gt;False: View is centered on the character's head. Crosshair is hidden.\nTrue: In non-combat mode camera is positioned behind the character's shoulder. Crosshair is visible in third person mode as well.\n&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>View over the shoulder</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"autoSwitchShoulderCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When player is close to an obstacle, automatically switches camera to the shoulder that is farther away from the obstacle.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Auto switch shoulder</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"viewOverShoulderVerticalLayout\">\n         <property name=\"leftMargin\">\n          <number>20</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <layout class=\"QHBoxLayout\" name=\"defaultShoulderHorizontalLayout\">\n           <property name=\"leftMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"topMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"rightMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"bottomMargin\">\n            <number>0</number>\n           </property>\n           <item>\n            <widget class=\"QLabel\" name=\"defaultShoulderLabel\">\n             <property name=\"text\">\n              <string>Default shoulder:</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QComboBox\" name=\"defaultShoulderComboBox\">\n             <property name=\"currentIndex\">\n              <number>0</number>\n             </property>\n             <item>\n              <property name=\"text\">\n               <string>Right</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>Left</string>\n              </property>\n             </item>\n            </widget>\n           </item>\n           <item>\n            <spacer name=\"horizontalSpacer_3\">\n             <property name=\"orientation\">\n              <enum>Qt::Horizontal</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>40</width>\n               <height>20</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"previewIfStandStillCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled then the character rotation is not synchonized with the camera rotation while the character doesn't move and not in combat mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Preview if stand still</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"deferredPreviewRotationCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode. If disabled then the camera rotates rather than the character.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Deferred preview rotation</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"headBobbingCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables head bobbing when move in first person mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Head bobbing in 1st person mode</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>0</width>\n           <height>0</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"Interface\">\n      <attribute name=\"title\">\n       <string>Interface</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n         <item alignment=\"Qt::AlignRight\">\n          <widget class=\"QLabel\" name=\"showOwnedLabel\">\n           <property name=\"text\">\n            <string>Show owned:</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"showOwnedComboBox\">\n           <property name=\"currentIndex\">\n            <number>1</number>\n           </property>\n           <item>\n            <property name=\"text\">\n             <string>Off</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Tool Tip Only</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Crosshair Only</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Tool Tip and Crosshair</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item>\n          <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\"/>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n         <item>\n          <widget class=\"QLabel\" name=\"scalingLabel\">\n           <property name=\"toolTip\">\n            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This setting scales GUI windows. A value of 1.0 results in the normal scale.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n           </property>\n           <property name=\"text\">\n            <string>GUI scaling factor</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QDoubleSpinBox\" name=\"scalingSpinBox\">\n           <property name=\"decimals\">\n            <number>2</number>\n           </property>\n           <property name=\"minimum\">\n            <double>0.500000000000000</double>\n           </property>\n           <property name=\"maximum\">\n            <double>8.000000000000000</double>\n           </property>\n           <property name=\"singleStep\">\n            <double>0.250000000000000</double>\n           </property>\n           <property name=\"value\">\n            <double>1.000000000000000</double>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer_5\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"showEffectDurationCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. &lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Show effect duration</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"showEnchantChanceCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether or not the chance of success will be displayed in the enchanting menu.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Show enchant chance</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"showMeleeInfoCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, melee weapons reach and speed will be shown on item tooltip.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Show melee info</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"showProjectileDamageCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, damage bonus of arrows and bolts will be shown on item tooltip.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Show projectile damage</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"changeDialogTopicsCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, dialogue topics will have a different color if the topic is specific to the NPC you're talking to or the topic was previously seen. Color can be changed in settings.cfg.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Change dialogue topic color</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"stretchBackgroundCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Stretch menus, load screens, etc. to the window aspect ratio.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Stretch menu background</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"graphicHerbalismCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, containers supporting graphic herbalism will do so instead of opening the menu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Enable graphic herbalism</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer_2\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>40</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"BugFixes\">\n      <attribute name=\"title\">\n       <string>Bug Fixes</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QCheckBox\" name=\"preventMerchantEquippingCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Prevents merchants from equipping items that are sold to them.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Merchant equipping fix</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"trainersTrainingSkillsBasedOnBaseSkillCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Trainers now only choose which skills to train using their base skill points, allowing mercantile improving effects to be used without making mercantile an offered skill.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Trainers choose their training skills based on their base skill points</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>0</width>\n           <height>0</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"Miscellaneous\">\n      <attribute name=\"title\">\n       <string>Miscellaneous</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"scrollAreaVerticalLayout\">\n       <item>\n        <widget class=\"QGroupBox\" name=\"savesGroup\">\n         <property name=\"title\">\n          <string>Saves</string>\n         </property>\n         <layout class=\"QVBoxLayout\" name=\"savesGroupVerticalLayout\">\n          <item>\n           <widget class=\"QCheckBox\" name=\"timePlayedCheckbox\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Add &quot;Time Played&quot; to saves</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"maximumQuicksavesLayout\">\n            <item>\n             <widget class=\"QLabel\" name=\"maximumQuicksavesLabel\">\n              <property name=\"text\">\n               <string>Maximum Quicksaves</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QSpinBox\" name=\"maximumQuicksavesComboBox\">\n              <property name=\"minimum\">\n               <number>1</number>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"otherGroup\">\n         <property name=\"title\">\n          <string>Other</string>\n         </property>\n         <layout class=\"QVBoxLayout\" name=\"otherGroupVerticalLayout\">\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"screenshotFormatLayout\">\n            <item alignment=\"Qt::AlignRight\">\n             <widget class=\"QLabel\" name=\"screenshotFormatLabel\">\n              <property name=\"text\">\n               <string>Screenshot Format</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QComboBox\" name=\"screenshotFormatComboBox\">\n              <item>\n               <property name=\"text\">\n                <string>JPG</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>PNG</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>TGA</string>\n               </property>\n              </item>\n             </widget>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>0</width>\n           <height>0</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"Testing\">\n      <attribute name=\"title\">\n       <string>Testing</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"testingLabel\">\n         <property name=\"text\">\n          <string>These settings are intended for testing mods and will cause issues if used for normal gameplay.</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"Line\" name=\"testingLine\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"grabCursorCheckBox\">\n         <property name=\"toolTip\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;OpenMW will capture control of the cursor if this setting is true.&lt;/p&gt;&lt;p&gt;In “look mode”, OpenMW will center the cursor regardless of the value of this setting (since the cursor/crosshair is always centered in the OpenMW window). However, in GUI mode, this setting determines the behavior when the cursor is moved outside the OpenMW window. If true, the cursor movement stops at the edge of the window preventing access to other applications. If false, the cursor is allowed to move freely on the desktop.&lt;/p&gt;&lt;p&gt;This setting does not apply to the screen where escape has been pressed, where the cursor is never captured. Regardless of this setting “Alt-Tab” or some other operating system dependent key sequence can be used to allow the operating system to regain control of the mouse cursor. This setting interacts with the minimize on focus loss setting by affecting what counts as a focus loss. Specifically on a two-screen configuration it may be more convenient to access the second screen with setting disabled.&lt;/p&gt;&lt;p&gt;Note for developers: it’s desirable to have this setting disabled when running the game in a debugger, to prevent the mouse cursor from becoming unusable when the game pauses on a breakpoint.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"text\">\n          <string>Grab cursor</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"skipMenuCheckBox\">\n         <property name=\"text\">\n          <string>Skip menu and generate default character</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"startDefaultCharacterAtHorizontalLayout\">\n         <item>\n          <spacer name=\"startDefaultCharacterAtHorizontalSpacer\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Fixed</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>0</width>\n             <height>0</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"startDefaultCharacterAtLabel\">\n           <property name=\"text\">\n            <string>Start default character at</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"startDefaultCharacterAtField\">\n           <property name=\"placeholderText\">\n            <string>default cell</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"label\">\n         <property name=\"text\">\n          <string>Run script after startup:</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"runScriptAfterStartupHorizontalLayout\">\n         <item>\n          <widget class=\"QLineEdit\" name=\"runScriptAfterStartupField\"/>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"runScriptAfterStartupBrowseButton\">\n           <property name=\"text\">\n            <string>Browse…</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>0</width>\n           <height>0</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/contentselector.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ContentSelector</class>\n <widget class=\"QWidget\" name=\"ContentSelector\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>518</width>\n    <height>436</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"contextMenuPolicy\">\n   <enum>Qt::DefaultContextMenu</enum>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QGroupBox\" name=\"contentGroupBox\">\n     <property name=\"title\">\n      <string>Content</string>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n      <property name=\"bottomMargin\">\n       <number>3</number>\n      </property>\n      <item>\n       <widget class=\"QWidget\" name=\"gameSelectorGroup\" native=\"true\">\n        <property name=\"focusPolicy\">\n         <enum>Qt::NoFocus</enum>\n        </property>\n        <layout class=\"QHBoxLayout\" name=\"horizLayout\">\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"ContentSelectorView::ComboBox\" name=\"gameFileView\">\n           <property name=\"editable\">\n            <bool>false</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"searchFilter\"/>\n         </item>\n         <item>\n          <widget class=\"QToolButton\" name=\"refreshButton\">\n           <property name=\"enabled\">\n            <bool>true</bool>\n           </property>\n           <property name=\"autoRaise\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QTableView\" name=\"addonView\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"contextMenuPolicy\">\n         <enum>Qt::DefaultContextMenu</enum>\n        </property>\n        <property name=\"acceptDrops\">\n         <bool>true</bool>\n        </property>\n        <property name=\"editTriggers\">\n         <set>QAbstractItemView::NoEditTriggers</set>\n        </property>\n        <property name=\"dragEnabled\">\n         <bool>true</bool>\n        </property>\n        <property name=\"dragDropOverwriteMode\">\n         <bool>false</bool>\n        </property>\n        <property name=\"dragDropMode\">\n         <enum>QAbstractItemView::DragDrop</enum>\n        </property>\n        <property name=\"defaultDropAction\">\n         <enum>Qt::MoveAction</enum>\n        </property>\n        <property name=\"alternatingRowColors\">\n         <bool>true</bool>\n        </property>\n        <property name=\"selectionMode\">\n         <enum>QAbstractItemView::ExtendedSelection</enum>\n        </property>\n        <property name=\"selectionBehavior\">\n         <enum>QAbstractItemView::SelectRows</enum>\n        </property>\n        <property name=\"textElideMode\">\n         <enum>Qt::ElideLeft</enum>\n        </property>\n        <property name=\"showGrid\">\n         <bool>false</bool>\n        </property>\n        <attribute name=\"horizontalHeaderVisible\">\n         <bool>false</bool>\n        </attribute>\n        <attribute name=\"horizontalHeaderStretchLastSection\">\n         <bool>true</bool>\n        </attribute>\n        <attribute name=\"verticalHeaderVisible\">\n         <bool>false</bool>\n        </attribute>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ContentSelectorView::ComboBox</class>\n   <extends>QComboBox</extends>\n   <header>components/contentselector/view/combobox.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/datafilespage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DataFilesPage</class>\n <widget class=\"QWidget\" name=\"DataFilesPage\">\n  <property name=\"contextMenuPolicy\">\n   <enum>Qt::DefaultContextMenu</enum>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QWidget\" name=\"contentSelectorWidget\" native=\"true\"/>\n   </item>\n   <item>\n    <widget class=\"QGroupBox\" name=\"profileGroupBox\">\n     <property name=\"focusPolicy\">\n      <enum>Qt::NoFocus</enum>\n     </property>\n     <property name=\"title\">\n      <string>Content List</string>\n     </property>\n     <property name=\"flat\">\n      <bool>false</bool>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n      <property name=\"spacing\">\n       <number>6</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>3</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>6</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>6</number>\n      </property>\n      <item>\n       <widget class=\"ProfilesComboBox\" name=\"profilesComboBox\">\n        <property name=\"enabled\">\n         <bool>true</bool>\n        </property>\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"toolTip\">\n         <string>Select a content list</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QToolButton\" name=\"newProfileButton\">\n        <property name=\"toolTip\">\n         <string>New Content List</string>\n        </property>\n        <property name=\"text\">\n         <string>&amp;New Content List</string>\n        </property>\n        <property name=\"autoRaise\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QToolButton\" name=\"cloneProfileButton\">\n        <property name=\"toolTip\">\n         <string>Clone Content List</string>\n        </property>\n        <property name=\"text\">\n         <string>Clone Content List</string>\n        </property>\n        <property name=\"autoRaise\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QToolButton\" name=\"deleteProfileButton\">\n        <property name=\"toolTip\">\n         <string>Delete Content List</string>\n        </property>\n        <property name=\"text\">\n         <string>Delete Content List</string>\n        </property>\n        <property name=\"autoRaise\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n  <action name=\"newProfileAction\">\n   <property name=\"icon\">\n    <iconset theme=\"document-new\">\n     <normaloff/>\n    </iconset>\n   </property>\n   <property name=\"text\">\n    <string>New Content List</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>New Content List</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+N</string>\n   </property>\n  </action>\n  <action name=\"cloneProfileAction\">\n   <property name=\"icon\">\n    <iconset theme=\"edit-copy\">\n     <normaloff/>\n    </iconset>\n   </property>\n   <property name=\"text\">\n    <string>Clone Content List</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Clone Content List</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+G</string>\n   </property>\n  </action>\n  <action name=\"deleteProfileAction\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset theme=\"edit-delete\">\n     <normaloff/>\n    </iconset>\n   </property>\n   <property name=\"text\">\n    <string>Delete Content List</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Delete Content List</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+D</string>\n   </property>\n  </action>\n  <action name=\"checkAction\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Check Selection</string>\n   </property>\n  </action>\n  <action name=\"uncheckAction\">\n   <property name=\"text\">\n    <string>Uncheck Selection</string>\n   </property>\n  </action>\n  <action name=\"refreshDataFilesAction\">\n   <property name=\"icon\">\n    <iconset theme=\"view-refresh\">\n     <normaloff/>\n    </iconset>\n   </property>\n   <property name=\"text\">\n    <string>Refresh Data Files</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Refresh Data Files</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+R</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ProfilesComboBox</class>\n   <extends>QComboBox</extends>\n   <header>apps/launcher/utils/profilescombobox.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/filedialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>FileDialog</class>\n <widget class=\"QWidget\" name=\"FileDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>518</width>\n    <height>109</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"contextMenuPolicy\">\n   <enum>Qt::DefaultContextMenu</enum>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QWidget\" name=\"contentSelectorWidget\" native=\"true\"/>\n   </item>\n   <item>\n    <widget class=\"QGroupBox\" name=\"projectGroupBox\">\n     <property name=\"focusPolicy\">\n      <enum>Qt::NoFocus</enum>\n     </property>\n     <property name=\"title\">\n      <string>Project Name</string>\n     </property>\n     <property name=\"flat\">\n      <bool>false</bool>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"projectGroupBoxLayout\">\n      <property name=\"spacing\">\n       <number>6</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>3</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>6</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>6</number>\n      </property>\n      <item>\n       <widget class=\"QWidget\" name=\"projectWidget\" native=\"true\">\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n         <item>\n          <widget class=\"QDialogButtonBox\" name=\"projectButtonBox\">\n           <property name=\"standardButtons\">\n            <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/graphicspage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GraphicsPage</class>\n <widget class=\"QWidget\" name=\"GraphicsPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>650</width>\n    <height>340</height>\n   </rect>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item alignment=\"Qt::AlignTop\">\n    <widget class=\"QTabWidget\" name=\"DisplayTabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"DisplayWrapper\">\n      <attribute name=\"title\">\n       <string>Display</string>\n      </attribute>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n       <item>\n        <widget class=\"QWidget\" name=\"DisplayWidget\">\n         <layout class=\"QGridLayout\" name=\"gridLayout_4\" columnstretch=\"1,0\">\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QLabel\" name=\"antiAliasingLabel\">\n            <property name=\"text\">\n             <string>Anti-aliasing:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"6\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"framerateLimitCheckBox\">\n            <property name=\"text\">\n             <string>Framerate Limit:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"1\">\n           <layout class=\"QGridLayout\" name=\"resolutionLayout\">\n            <item row=\"1\" column=\"2\">\n             <layout class=\"QHBoxLayout\" name=\"customResolutionLayout\" stretch=\"1,0,1\">\n              <item>\n               <widget class=\"QSpinBox\" name=\"customWidthSpinBox\">\n                <property name=\"minimum\">\n                 <number>800</number>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"multiplyLabel\">\n                <property name=\"text\">\n                 <string> x </string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QSpinBox\" name=\"customHeightSpinBox\">\n                <property name=\"minimum\">\n                 <number>600</number>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n            <item row=\"1\" column=\"1\">\n             <widget class=\"QRadioButton\" name=\"customRadioButton\">\n              <property name=\"text\">\n               <string>Custom:</string>\n              </property>\n             </widget>\n            </item>\n            <item row=\"0\" column=\"1\">\n             <widget class=\"QRadioButton\" name=\"standardRadioButton\">\n              <property name=\"text\">\n               <string>Standard:</string>\n              </property>\n              <property name=\"checkable\">\n               <bool>true</bool>\n              </property>\n             </widget>\n            </item>\n            <item row=\"0\" column=\"2\">\n             <widget class=\"QComboBox\" name=\"resolutionComboBox\"/>\n            </item>\n           </layout>\n          </item>\n          <item row=\"6\" column=\"1\">\n           <widget class=\"QDoubleSpinBox\" name=\"framerateLimitSpinBox\">\n            <property name=\"enabled\">\n             <bool>false</bool>\n            </property>\n            <property name=\"suffix\">\n             <string> FPS</string>\n            </property>\n            <property name=\"decimals\">\n             <number>1</number>\n            </property>\n            <property name=\"minimum\">\n             <double>1</double>\n            </property>\n            <property name=\"maximum\">\n             <double>1000</double>\n            </property>\n            <property name=\"singleStep\">\n             <double>15</double>\n            </property>\n            <property name=\"value\">\n             <double>300</double>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"windowBorderCheckBox\">\n            <property name=\"text\">\n             <string>Window Border</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"fullScreenCheckBox\">\n            <property name=\"text\">\n             <string>Full Screen</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"0\">\n           <widget class=\"QLabel\" name=\"resolutionLabel\">\n            <property name=\"text\">\n             <string>Resolution:</string>\n            </property>\n            <property name=\"alignment\">\n             <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"1\">\n           <widget class=\"QComboBox\" name=\"screenComboBox\"/>\n          </item>\n          <item row=\"4\" column=\"0\">\n           <widget class=\"QLabel\" name=\"screenLabel\">\n            <property name=\"text\">\n             <string>Screen:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"1\">\n           <widget class=\"QComboBox\" name=\"antiAliasingComboBox\">\n            <item>\n             <property name=\"text\">\n              <string>0</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>2</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>4</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>8</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>16</string>\n             </property>\n            </item>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"vSyncCheckBox\">\n            <property name=\"text\">\n             <string>Vertical Sync</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"LightingWrapper\">\n      <attribute name=\"title\">\n       <string>Lighting</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"lightingLayout\">\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"lightingMethodLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"lightingMethodLabel\">\n           <property name=\"text\">\n            <string>Lighting Method:</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"lightingMethodComboBox\">\n           <item>\n            <property name=\"text\">\n             <string>legacy</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>shaders compatibility</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>shaders</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>40</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"ShadowWrapper\">\n      <attribute name=\"title\">\n       <string>Shadows</string>\n      </attribute>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <item>\n        <widget class=\"QWidget\" name=\"ShadowWidget\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n           <horstretch>1</horstretch>\n           <verstretch>1</verstretch>\n          </sizepolicy>\n         </property>\n         <layout class=\"QGridLayout\" name=\"shadowsLayout\" columnstretch=\"0,0\">\n\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"playerShadowsCheckBox\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable shadows exclusively for the player character. May have a very minor performance impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Enable Player Shadows</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"actorShadowsCheckBox\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable shadows for NPCs and creatures besides the player character. May have a minor performance impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Enable Actor Shadows</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"objectShadowsCheckBox\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable shadows for primarily inanimate objects. May have a significant performance impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Enable Object Shadows</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"terrainShadowsCheckBox\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable shadows for the terrain including distant terrain. May have a significant performance and shadow quality impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Enable Terrain Shadows</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"indoorShadowsCheckBox\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.&lt;/p&gt;&lt;p&gt;Has no effect if actor/player shadows are not enabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Enable Indoor Shadows</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"0\">\n           <widget class=\"QLabel\" name=\"shadowComputeSceneBoundsLabel\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Type of \"compute scene bounds\" computation method to be used. Bounds (default) for good balance between performance and shadow quality, primitives for better looking shadows or none for no computation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Shadow Near Far Computation Method:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"1\">compute scene bounds\n           <widget class=\"QComboBox\" name=\"shadowComputeSceneBoundsComboBox\">\n            <item>\n             <property name=\"text\">\n              <string>bounds</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>primitives</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>none</string>\n             </property>\n            </item>\n           </widget>\n          </item>\n          <item row=\"6\" column=\"0\">\n           <widget class=\"QLabel\" name=\"shadowResolutionLabel\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The resolution of each individual shadow map. Increasing it significantly improves shadow quality but may have a minor performance impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Shadow Map Resolution:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"6\" column=\"1\">\n           <widget class=\"QComboBox\" name=\"shadowResolutionComboBox\">\n            <item>\n             <property name=\"text\">\n              <string>512</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>1024</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>2048</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>4096</string>\n             </property>\n            </item>\n           </widget>\n          </item>\n          <item row=\"7\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"shadowDistanceCheckBox\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The distance from the camera at which shadows completely disappear.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Shadow Distance Limit:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"7\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"shadowDistanceSpinBox\">\n            <property name=\"enabled\">\n             <bool>false</bool>\n            </property>\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;64 game units is 1 real life yard or about 0.9 m&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"suffix\">\n             <string> unit(s)</string>\n            </property>\n            <property name=\"minimum\">\n             <number>512</number>\n            </property>\n            <property name=\"maximum\">\n             <number>81920</number>\n            </property>\n            <property name=\"value\">\n             <number>8192</number>\n            </property>\n           </widget>\n          </item>\n          <item row=\"8\" column=\"0\">\n           <widget class=\"QLabel\" name=\"fadeStartLabel\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The fraction of the limit above at which shadows begin to gradually fade away.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Fade Start Multiplier:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"8\" column=\"1\">\n           <widget class=\"QDoubleSpinBox\" name=\"fadeStartSpinBox\">\n            <property name=\"enabled\">\n             <bool>false</bool>\n            </property>\n            <property name=\"decimals\">\n             <number>2</number>\n            </property>\n            <property name=\"minimum\">\n             <double>0</double>\n            </property>\n            <property name=\"maximum\">\n             <double>1</double>\n            </property>\n            <property name=\"value\">\n             <double>0.90</double>\n            </property>\n           </widget>\n          </item>\n\n         </layout>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/mainwindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\"MainWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>720</width>\n    <height>565</height>\n   </rect>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>720</width>\n    <height>565</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>OpenMW Launcher</string>\n  </property>\n  <property name=\"windowIcon\">\n   <iconset resource=\"../launcher/launcher.qrc\">\n    <normaloff>:/images/openmw.png</normaloff>:/images/openmw.png</iconset>\n  </property>\n  <widget class=\"QWidget\" name=\"centralwidget\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n    <item>\n     <widget class=\"QListWidget\" name=\"iconWidget\">\n      <property name=\"minimumSize\">\n       <size>\n        <width>400</width>\n        <height>80</height>\n       </size>\n      </property>\n      <property name=\"maximumSize\">\n       <size>\n        <width>16777215</width>\n        <height>80</height>\n       </size>\n      </property>\n      <property name=\"styleSheet\">\n       <string notr=\"true\">#iconWidget {\n    background-image: url(&quot;:/images/openmw-header.png&quot;);\n    background-color: palette(base);\n    background-repeat: no-repeat;\n    background-attachment: scroll;\n    background-position: right;\n}\n</string>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"QStackedWidget\" name=\"pagesWidget\"/>\n    </item>\n    <item>\n     <widget class=\"Line\" name=\"line\">\n      <property name=\"orientation\">\n       <enum>Qt::Horizontal</enum>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n      <item>\n       <widget class=\"QLabel\" name=\"versionLabel\">\n        <property name=\"text\">\n         <string>OpenMW version</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <spacer name=\"horizontalSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Horizontal</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>40</width>\n          <height>20</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n      <item>\n       <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n        <property name=\"standardButtons\">\n         <set>QDialogButtonBox::Close</set>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources>\n  <include location=\"../launcher/launcher.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/playpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>PlayPage</class>\n <widget class=\"QWidget\" name=\"PlayPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>274</width>\n    <height>317</height>\n   </rect>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QWidget\" name=\"Scroll\" native=\"true\">\n     <property name=\"styleSheet\">\n      <string notr=\"true\">#Scroll {\n    background-image: url(&quot;:/images/playpage-background.png&quot;);\n    background-repeat: no-repeat;\n    background-position: top;\n}\n</string>\n     </property>\n     <layout class=\"QGridLayout\" name=\"gridLayout\">\n      <property name=\"leftMargin\">\n       <number>30</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>100</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>30</number>\n      </property>\n      <item row=\"4\" column=\"1\">\n       <widget class=\"QComboBox\" name=\"profilesComboBox\">\n        <property name=\"styleSheet\">\n         <string notr=\"true\">#profilesComboBox {\n    padding: 1px 18px 1px 3px;\n\n    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:0.2 rgba(0, 0, 0, 25), stop:1 white);\n    border-width: 1px;\n    border-color: rgba(0, 0, 0, 125);\n    border-style: solid;\n    border-radius: 2px;\n\n    font-size: 12pt;\n\tcolor: black;\n}\n\n/*QComboBox gets the &quot;on&quot; state when the popup is open */\n#profilesComboBox:!editable:on {\n    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,\n        stop:0 rgba(0, 0, 0, 75),\n        stop:0.1 rgba(0, 0, 0, 15),\n        stop:0.2 rgba(255, 255, 255, 55));\n\n    border: 1px solid rgba(0, 0, 0, 55);\n}\n\n#profilesComboBox:on { /* shift the text when the popup opens */\n    padding-top: 3px;\n    padding-left: 4px;\n}\n\n#profilesComboBox::drop-down {\n    subcontrol-origin: padding;\n    subcontrol-position: top right;\n\n    border-width: 1px;\n    border-left-width: 1px;\n    border-left-color: darkgray;\n    border-left-style: solid; /* just a single line */\n    border-top-right-radius: 3px; /* same radius as the QComboBox */\n    border-bottom-right-radius: 3px;\n}\n\n#profilesComboBox::down-arrow {\n    image: url(&quot;:/images/down.png&quot;);\n}\n\n#profilesComboBox::down-arrow:on { /* shift the arrow when popup is open */\n    top: 1px;\n    left: 1px;\n}\n\n#profilesComboBox QAbstractItemView {\n    border: 0px;\n}</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"1\">\n       <widget class=\"QLabel\" name=\"profileLabel\">\n        <property name=\"styleSheet\">\n         <string notr=\"true\">#profileLabel {\n    font-size: 18pt;\n\tcolor: black;\n}\n</string>\n        </property>\n        <property name=\"text\">\n         <string>Current Content List:</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"1\">\n       <widget class=\"QPushButton\" name=\"playButton\">\n        <property name=\"minimumSize\">\n         <size>\n          <width>200</width>\n          <height>85</height>\n         </size>\n        </property>\n        <property name=\"maximumSize\">\n         <size>\n          <width>200</width>\n          <height>85</height>\n         </size>\n        </property>\n        <property name=\"styleSheet\">\n         <string notr=\"true\">#playButton {\n    height: 50px;\n    margin-bottom: 30px;\n\n    background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1,\n        stop:0 rgba(255, 255, 255, 200),\n        stop:0.1 rgba(255, 255, 255, 15),\n        stop:0.49 rgba(255, 255, 255, 75),\n        stop:0.5 rgba(0, 0, 0, 0),\n        stop:0.9 rgba(0, 0, 0, 55),\n        stop:1 rgba(0, 0, 0, 100));\n\n    font-size: 26pt;\n    color: black;\n\n    border-right: 1px solid rgba(0, 0, 0, 155);\n    border-left: 1px solid rgba(0, 0, 0, 55);\n    border-top: 1px solid rgba(0, 0, 0, 55);\n    border-bottom: 1px solid rgba(0, 0, 0, 155);\n\n    border-radius: 5px;\n}\n\n#playButton:hover {\n    border-bottom: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));\n    border-top: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));\n    border-right: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));\n    border-left: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));\n    border-width: 2px;\n    border-style: solid;\n}\n\n#playButton:pressed {\n    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,\n        stop:0 rgba(0, 0, 0, 75),\n        stop:0.1 rgba(0, 0, 0, 15),\n        stop:0.2 rgba(255, 255, 255, 55)\n        stop:0.95 rgba(255, 255, 255, 55),\n        stop:1 rgba(255, 255, 255, 155));\n\n    border: 1px solid rgba(0, 0, 0, 55);\n}</string>\n        </property>\n        <property name=\"text\">\n         <string>Play</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"5\" column=\"1\">\n       <spacer name=\"verticalSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Vertical</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>20</width>\n          <height>40</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/settingspage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SettingsPage</class>\n <widget class=\"QWidget\" name=\"SettingsPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>514</width>\n    <height>397</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QGroupBox\" name=\"generalGroup\">\n     <property name=\"title\">\n      <string>Morrowind Content Language</string>\n     </property>\n     <layout class=\"QGridLayout\" name=\"gridLayout\">\n      <item row=\"0\" column=\"0\">\n       <widget class=\"QComboBox\" name=\"languageComboBox\">\n        <property name=\"minimumSize\">\n         <size>\n          <width>250</width>\n          <height>0</height>\n         </size>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"1\">\n       <spacer name=\"horizontalSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Horizontal</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>40</width>\n          <height>20</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QGroupBox\" name=\"wizardGroup\">\n     <property name=\"title\">\n      <string>Morrowind Installation Wizard</string>\n     </property>\n     <layout class=\"QGridLayout\" name=\"gridLayout_3\" columnstretch=\"1,1\">\n      <item row=\"1\" column=\"1\">\n       <spacer name=\"horizontalSpacer_2\">\n        <property name=\"orientation\">\n         <enum>Qt::Horizontal</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>40</width>\n          <height>20</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n      <item row=\"1\" column=\"0\">\n       <widget class=\"QPushButton\" name=\"wizardButton\">\n        <property name=\"text\">\n         <string>Run &amp;Installation Wizard</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QGroupBox\" name=\"importerGroup\">\n     <property name=\"title\">\n      <string>Morrowind Settings Importer</string>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n      <item>\n       <layout class=\"QGridLayout\" name=\"gridLayout_2\" rowstretch=\"0,0\" columnstretch=\"1,0\">\n        <item row=\"0\" column=\"0\">\n         <widget class=\"QLabel\" name=\"importerLabel\">\n          <property name=\"text\">\n           <string>File to import settings from:</string>\n          </property>\n         </widget>\n        </item>\n        <item row=\"1\" column=\"0\">\n         <widget class=\"QComboBox\" name=\"settingsComboBox\"/>\n        </item>\n        <item row=\"1\" column=\"1\">\n         <widget class=\"QPushButton\" name=\"browseButton\">\n          <property name=\"text\">\n           <string>Browse...</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <widget class=\"QCheckBox\" name=\"addonsCheckBox\">\n        <property name=\"text\">\n         <string>Import add-on and plugin selection (creates a new Content List)</string>\n        </property>\n        <property name=\"checked\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <layout class=\"QGridLayout\" name=\"gridLayout_4\" columnstretch=\"1,1\">\n        <item row=\"2\" column=\"1\">\n         <spacer name=\"horizontalSpacer_3\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>40</width>\n            <height>20</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n        <item row=\"2\" column=\"0\">\n         <widget class=\"QPushButton\" name=\"importerButton\">\n          <property name=\"text\">\n           <string>Run &amp;Settings Importer</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <widget class=\"QProgressBar\" name=\"progressBar\">\n        <property name=\"maximum\">\n         <number>4</number>\n        </property>\n        <property name=\"textVisible\">\n         <bool>false</bool>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/componentselectionpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ComponentSelectionPage</class>\n <widget class=\"QWizardPage\" name=\"ComponentSelectionPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>448</width>\n    <height>387</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Select Components</string>\n  </property>\n  <property name=\"subTitle\">\n   <string>Which components should be installed?</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to install expansions later by re-running this Wizard.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"componentsLabel\">\n     <property name=\"text\">\n      <string>Selected components:</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"ComponentListWidget\" name=\"componentsList\"/>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>81</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ComponentListWidget</class>\n   <extends>QListWidget</extends>\n   <header>apps/wizard/utils/componentlistwidget.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/conclusionpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ConclusionPage</class>\n <widget class=\"QWizardPage\" name=\"ConclusionPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>398</width>\n    <height>298</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Completing the OpenMW Wizard</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"textLabel\">\n     <property name=\"text\">\n      <string>Placeholder</string>\n     </property>\n     <property name=\"textFormat\">\n      <enum>Qt::RichText</enum>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n     <property name=\"openExternalLinks\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/existinginstallationpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ExistingInstallationPage</class>\n <widget class=\"QWizardPage\" name=\"ExistingInstallationPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>394</width>\n    <height>294</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Select Existing Installation</string>\n  </property>\n  <property name=\"subTitle\">\n   <string>Select an existing installation for OpenMW to use or modify.</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"installationsLabel\">\n     <property name=\"text\">\n      <string>Detected installations:</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QListWidget\" name=\"installationsList\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"browseButton\">\n       <property name=\"text\">\n        <string>Browse...</string>\n       </property>\n       <property name=\"icon\">\n        <iconset theme=\"folder\">\n         <normaloff/>\n        </iconset>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/importpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ImportPage</class>\n <widget class=\"QWizardPage\" name=\"ImportPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>508</width>\n    <height>322</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Import Settings</string>\n  </property>\n  <property name=\"subTitle\">\n   <string>Import settings from the Morrowind installation.</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"infoLabel\">\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;OpenMW needs to import settings from the Morrowind configuration file in order to function properly.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to import settings later by re-running this Wizard.&lt;/p&gt;&lt;p/&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QCheckBox\" name=\"importCheckBox\">\n     <property name=\"text\">\n      <string>Import settings from Morrowind.ini</string>\n     </property>\n     <property name=\"checked\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QCheckBox\" name=\"addonsCheckBox\">\n     <property name=\"text\">\n      <string>Import add-on and plugin selection</string>\n     </property>\n     <property name=\"checked\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/installationpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>InstallationPage</class>\n <widget class=\"QWizardPage\" name=\"InstallationPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>514</width>\n    <height>419</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Installing</string>\n  </property>\n  <property name=\"subTitle\">\n   <string>Please wait while Morrowind is installed on your computer.</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"installProgressLabel\">\n     <property name=\"text\">\n      <string/>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QProgressBar\" name=\"installProgressBar\">\n     <property name=\"value\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPlainTextEdit\" name=\"logTextEdit\">\n     <property name=\"undoRedoEnabled\">\n      <bool>false</bool>\n     </property>\n     <property name=\"readOnly\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/installationtargetpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>InstallationTargetPage</class>\n <widget class=\"QWizardPage\" name=\"InstallationTargetPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Select Installation Destination</string>\n  </property>\n  <property name=\"subTitle\">\n   <string>Where should Morrowind be installed?</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"folderIconLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n       <property name=\"textFormat\">\n        <enum>Qt::RichText</enum>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"infoLabel\">\n       <property name=\"text\">\n        <string>Morrowind will be installed to the following location. </string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QLineEdit\" name=\"targetLineEdit\"/>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"browseButton\">\n       <property name=\"text\">\n        <string>Browse...</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources>\n  <include location=\"../../wizard/wizard.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/intropage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>IntroPage</class>\n <widget class=\"QWizardPage\" name=\"IntroPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>472</width>\n    <height>368</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Welcome to the OpenMW Wizard</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"textLabel\">\n     <property name=\"text\">\n      <string>This Wizard will help you install Morrowind and its add-ons for OpenMW to use.</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/languageselectionpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>LanguageSelectionPage</class>\n <widget class=\"QWizardPage\" name=\"LanguageSelectionPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>396</width>\n    <height>296</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Select Morrowind Language</string>\n  </property>\n  <property name=\"subTitle\">\n   <string>What is the language of the Morrowind installation?</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"flagIconLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/preferences-desktop-locale.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n       <property name=\"textFormat\">\n        <enum>Qt::RichText</enum>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"infoLabel\">\n       <property name=\"text\">\n        <string>Select the language of the Morrowind installation.</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QComboBox\" name=\"languageComboBox\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>250</width>\n         <height>0</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>230</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/ui/wizard/methodselectionpage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MethodSelectionPage</class>\n <widget class=\"QWizardPage\" name=\"MethodSelectionPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>396</width>\n    <height>296</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>WizardPage</string>\n  </property>\n  <property name=\"title\">\n   <string>Select Installation Method</string>\n  </property>\n  <property name=\"subTitle\">\n   <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select how you would like to install &lt;i&gt;The Elder Scrolls III: Morrowind&lt;/i&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QRadioButton\" name=\"retailDiscRadioButton\">\n     <property name=\"styleSheet\">\n      <string notr=\"true\">font-weight:bold;</string>\n     </property>\n     <property name=\"text\">\n      <string>Retail CD/DVD</string>\n     </property>\n     <property name=\"checked\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"retailDiscLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Maximum</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"installerIconLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/system-installer.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n       <property name=\"textFormat\">\n        <enum>Qt::RichText</enum>\n       </property>\n       <property name=\"scaledContents\">\n        <bool>false</bool>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n       </property>\n       <property name=\"buddy\">\n        <cstring>retailDiscRadioButton</cstring>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"retailDiscLabel\">\n       <property name=\"text\">\n        <string>Install from a retail disc to a new location.</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QRadioButton\" name=\"existingLocationRadioButton\">\n     <property name=\"styleSheet\">\n      <string notr=\"true\">font-weight:bold</string>\n     </property>\n     <property name=\"text\">\n      <string>Existing Installation</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"existingLocationLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Maximum</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"folderIconLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n       <property name=\"textFormat\">\n        <enum>Qt::RichText</enum>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"existingLocationLabel\">\n       <property name=\"text\">\n        <string>Select an existing installation.</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"Line\" name=\"line\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Don't have a copy?</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"buyGameLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer_3\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Maximum</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"buyIconLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/dollar.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QCommandLinkButton\" name=\"buyLinkButton\">\n       <property name=\"text\">\n        <string>Buy the game</string>\n       </property>\n       <property name=\"checkable\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_4\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "files/version.in",
    "content": "@OPENMW_VERSION@\n@OPENMW_VERSION_COMMITHASH@\n@OPENMW_VERSION_TAGHASH@\n"
  },
  {
    "path": "files/vfs/CMakeLists.txt",
    "content": "if (NOT DEFINED OPENMW_MYGUI_FILES_ROOT)\n    return()\nendif()\n\n# Copy resource files into the build directory\nset(SDIR ${CMAKE_CURRENT_SOURCE_DIR})\nset(DDIRRELATIVE resources/vfs/textures)\n\nset(TEXTURE_FILES\n    textures/omw_menu_scroll_down.dds\n    textures/omw_menu_scroll_up.dds\n    textures/omw_menu_scroll_left.dds\n    textures/omw_menu_scroll_right.dds\n    textures/omw_menu_scroll_center_h.dds\n    textures/omw_menu_scroll_center_v.dds\n)\n\ncopy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_MYGUI_FILES_ROOT} ${DDIRRELATIVE} \"${TEXTURE_FILES}\")\n"
  },
  {
    "path": "files/windows/launcher.rc",
    "content": "IDI_ICON1               ICON    DISCARDABLE     \"openmw.ico\"\n"
  },
  {
    "path": "files/windows/opencs.rc",
    "content": "IDI_ICON1               ICON    DISCARDABLE     \"openmw-cs.ico\"\n"
  },
  {
    "path": "files/windows/openmw-wizard.rc",
    "content": "IDI_ICON1               ICON    DISCARDABLE     \"openmw-wizard.ico\"\n"
  },
  {
    "path": "files/windows/openmw.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n    <assemblyIdentity type=\"win32\" name=\"openmw\" version=\"1.0.0.0\"/>\n    <description>openmw</description>\n    <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n        <security>\n            <requestedPrivileges>\n                <requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\"/>\n            </requestedPrivileges>\n        </security>\n    </trustInfo>\n    <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n        <windowsSettings xmlns:ws=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">\n            <ws:dpiAware>True</ws:dpiAware>\n        </windowsSettings>\n    </application>\n</assembly>\n\n"
  },
  {
    "path": "files/windows/openmw.rc",
    "content": "IDI_ICON1               ICON    DISCARDABLE     \"openmw.ico\"\nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST \"openmw.exe.manifest\"\n"
  },
  {
    "path": "files/wizard/icons/tango/index.theme",
    "content": "[Icon Theme]\nName=Tango\nComment=Tango Theme\nInherits=default\nDirectories=48x48\n\n[48x48]\nSize=48"
  },
  {
    "path": "files/wizard/wizard.qrc",
    "content": "<RCC>\n  <qresource prefix=\"icons/tango\">\n    <file alias=\"48x48/preferences-desktop-locale.png\">icons/tango/48x48/preferences-desktop-locale.png</file>\n    <file alias=\"index.theme\">icons/tango/index.theme</file>\n    <file alias=\"48x48/folder.png\">icons/tango/48x48/folder.png</file>\n    <file alias=\"48x48/system-installer.png\">icons/tango/48x48/system-installer.png</file>\n    <file alias=\"48x48/dollar.png\">icons/tango/48x48/dollar.png</file>\n  </qresource>\n  <qresource prefix=\"images\">\n    <file alias=\"intropage-background.png\">images/intropage-background.png</file>    \n    <file alias=\"openmw-wizard.png\">images/openmw-wizard.png</file>\n  </qresource>\n</RCC>\n"
  },
  {
    "path": "manual/opencs/.gitignore",
    "content": "*.backup\n*.aux\n*.log\n*.toc\n*.pdf\n*.out\n"
  },
  {
    "path": "manual/opencs/creating_file.tex",
    "content": "\\section{OpenCS starting dialog}\r\n\\subsection{Introduction}\r\nThe great day has come. Today, you shall open \\OCS{} application. And when you do this, you shall see our starting dialog window that holds three buttons\r\nthat can bring both pain and happiness. So just do this, please.\r\n\r\n\\subsection{Basics}\r\nBack to the manual? Great! As you can see, the starting window holds just three buttons. Since you are already familiar with our files system, they come\r\nto you with no surprise.\\\\\r\n\r\nFirst, there is a \\textbf{Create A New Game} button. Clearly, you should press it when you want to create a game file. Than, what \\textbf{Create A New Addon} button do?\r\nYes! You are right! This button will create any addon content file (and new project file associated with it)! Wonderful! And what the last remaining button do? \\textbf{Edit A Content File}? Well, it comes with no surprise that this should be used when you need to alter existing content file, either a game or addon.\\\\\r\n\r\n\\paragraph{Selecting Files For New Addon}\r\nAs We wrote earlier, both \\OMW{} and \\OCS{} are operating with dependency idea in mind. As You remember you should only depend on files you are actually using. But how?\\\\\r\nIt is simple. When you click either \\textbf{Create new Addon} you will be asked to choose those with a new dialog window. The window is using vertical layout, first you should consider the the top element, the one that allows you to select a game file with drop down menu. Since we are operating on the assumption that there is only one game file loaded at the time, you can depend only on one game file. Next, choose addons that you want to use in your addon with checkboxes.\\\\\r\n\r\nThe last thing to do is to name your your addon and click create.\r\n\r\n\\paragraph{Selecting File for Editing}\r\nClicking \\textbf{Edit A Content File} will show somewhat similar window. Here you should select your Game file with drop down menu. If you want to edit this game file, simply click \\textbf{OK} button. If you want to alter addon depending on that file, mark it with check-box and than click \\textbf{Ok} button.\r\n\r\n\\subsection{Advanced}\r\nIf you are paying attention, you noticed any extra icon with wrench. This one will open small settings window. Those are general OpenCS settings. We will cover this is separate section.\\\\\r\n\r\nAnd that would be it. There is no point spending more time here. We should go forward now."
  },
  {
    "path": "manual/opencs/files_and_directories.tex",
    "content": "\\section{Files and Directories}\r\n\\subsection{Introduction}\r\nThis section of the manual describes the directories and file types used by OpenCS. A file is a resource for storing data (e.g. .exe, .jpg, .txt), \r\nwhereas a directory is a folder or file system structure which points to these files (or other directories). You are most likely already familiar \r\nwith these concepts.\r\n\r\n\\subsection{Used terms} %TODO\r\n\r\n\\subsection{Basics}\r\n\r\n\\paragraph{Directories}\r\nOpenMW and \\OCS{} store their files in multiple directories. Firstly, there is the \\textbf{user directory} that holds configuration\r\nfiles and several other folders. The location of the user directory is hard coded for each supported operating system.\r\n\r\n%TODO list paths.\r\nIn addition to the user directory, both \\OMW{} and \\OCS{} need a place to store the game’s actual data files: for example, the\r\ntextures, models, sounds and records of in-game objects. We support multiple paths to these files (termed \\textbf{data paths}),\r\nas specified in the configuration. Usually, one data path points to the directory where \\MW{} is installed; however, you are\r\nfree to specify as many data paths as you would like. In addition, one particular data path, as described below, is used to store\r\nnewly created content files.\r\n\r\n\\paragraph{Content files}\r\n\\BS{} \\MW{} engine uses two file types: ESM (master) and ESP (plugin). The distinction between the two is often confusing.\r\nYou would expect that the ESM (master) file is used to specify a single master which is modified by the ESP files (plugins), and indeed:\r\nthis is the basic idea. However, the original expansions are also ESM files, even though they can be described as very large plugins.\r\nThere were technical reasons behind this decision -- somewhat valid in the case of the original engine -- but a more logical file system is\r\nmuch preferable. \\OMW{} achieves this through the creation of our own types of content file.\r\n\r\nWe support both ESM and ESP files, but, in order to make use of \\OMW{}'s new features, one should consider using new file types designed\r\nwith our engine in mind: game files and addon files, collectively termed \\textbf{content files}.\r\n\r\n\\subparagraph{OpenMW content files}\r\nThe distinction between game and addon files is similar to that between ESM and ESP, however their relationship to each other is\r\nstrictly defined -– the former are always master files, and the latter are always plugins. If you want to make a new game using the \\OMW{}\r\nengine (i.e. a ``total conversion''), you should create a game file. If you want to create an addon for an existing game file, simply\r\ncreate an addon file. Nothing else matters: the only distinction you should consider is whether your project involves changing another game, \r\nor creating a new one. Simple as that.\r\n\r\nFurthermore, our content files’ extensions are .omwaddon for addon files and .omwgame for game files.\r\n\r\n%TODO describe what content files contains. and what not.\r\n\\subparagraph{\\MW{} content files}\r\nUsing our content files is the recommended solution for projects that employ the \\OMW{} engine. However, some players will wish to use \r\nthe original \\MW{} engine, despite its large flaws and lacking features\\footnote{If this is wrong, we are a very successful project. Yay!}. \r\nIn addition, since 2002, thousands of ESP/ESM files have been created, some with truly outstanding content. Because of this, \\OCS{} \r\nwill support ESP/ESM files, although this will impose limitations on the user. If you do decide to use ESP/ESM files rather than our own content \r\nfiles, you are most likely aiming for original engine compatibility. This subject is covered in the very last section of the manual. \r\n%not finished TODO add the said section. Most likely when more features are present.\r\n\r\nThe actual creation of new files is described in the next chapter. Here we are going to focus only on the essential information needed\r\nto create your first \\OCS{} file. For now, let's jut remember that content files are stored in the user directory, in the \\textbf{data}\r\nsubfolder (the particular data directory mentioned above).\r\n\r\n\\subparagraph{Dependencies}\r\nSince addons aim to modify an existing game, it is logical that they also depend on the said game: otherwise they will not function.\r\nFor example, your modification changes the price of iron swords. But what if there are no iron swords in the game? That is right:\r\nit is nonsense. Therefore, it is necessary to make your addon a dependency of other content files. These can be either game files\r\n(e.g. an entirely new island), or other addon files (e.g. a house on the island). It is a good idea for addons to depend only on the\r\ncontent files they modify, but this is up to the end user to determine.\r\n\r\nGame files do not depend on any other content files, as they act as master files. A player can only use one game file at a time\r\n(although this does not apply to the original and dirty ESP/ESM system).\r\n\r\n%\\subparagraph{Loading order} %TODO\r\n\\paragraph{Project files}\r\nProject files contain data not used by the \\OMW{} game engine but which are still needed by OpenCS. Good examples of this data type\r\nare the record filters (described below). As a mod author, you probably do not need and/or want to distribute project files at all, \r\nas they are meant to be used only by you.\r\n\r\nSince project files govern how content files are used in OpenCS, they are always used in conjunction with your specific project.\r\nIn fact, each time work commences on a content file that does not have a corresponding project file, a new project file will be created. \r\n\r\nThe project file extension is ``.project''. The name of the project file is the whole name of the content file with appended extensions. \r\nFor instance, a content file named swords.omwaddon is associated with the project file swords.omwaddon.project.\r\n\r\n%TODO where are they stored.\r\nProject files are stored inside the user directory, in the \\textbf{projects} subfolder. This is both the location of newly created \r\nproject files, and the place where \\OCS{} looks for already existing files.\r\n\r\n\\paragraph{Resource files}\r\n%textures, sounds, whatever\r\nThe vast majority of modern video games use what we shall term \\textbf{resource files}: models, textures, icons, sounds and so on.\r\nESPs, ESMs and \\OMW{} content files do not contain these files, merely instructions on how they are used. It follows that the \\OMW{}\r\nengine must be capable of supporting these resource files in order for them to function. Therefore this section covers ways to add \r\nresource files to your content file, and outlines which formats are supported. Later, you will learn how to make use of these files \r\nin your content.\r\n\r\n\\subparagraph{Audio}\r\nOpenMW utilises {FFmpeg} for audio playback, so we support every audio type supported by this library. This is a huge list.\r\nBelow is only a small sample of supported file types.\r\n\r\n\\begin{description}\r\n \\item mp3 ({MPEG}-1 {Part 3 Layer 3}) A popular audio file format and the \\textit{de facto} standard for storing audio. Used by \r\n the \\MW{} game.\r\n \\item ogg Open source, multimedia container file which uses the high quality vorbis audio codec. Recommended.\r\n\\end{description}\r\n\r\n\\subparagraph{Video}\r\nAs in the case of audio files, we use {FFmpeg} to decode video files. The list of supported files is long -– only the most \r\nsignificant will be covered.\r\n\r\n\\begin{description}\r\n \\item bik Format used by the original \\MW{} game.\r\n \\item mp4 Multimedia container which use more advanced codecs ({MPEG-4 Parts 2,3,10}) with a better audio and video compression rate,\r\n but which require more {CPU} intensive decoding -- this probably makes it less suited for storing sounds in computer games, but\r\n good for videos.\r\n \\item webm A new, shiny and open source video format with excellent compression. It needs quite a lot of processing power to be decoded,\r\n but since game logic is not running during cut scenes we can recommend it for use with \\OMW.\r\n \\item ogv An alternative, open source container using theora codec for video and vorbis for audio.\r\n\\end{description}\r\n\r\n\\subparagraph{Textures and images}\r\n\\MW{} uses {DDS} and {TGA} files for all kinds of two dimensional images and textures. In addition, the original engine supported BMP\r\nfiles (although {BMP} is a terrible format for a video game). We also support an extended set of image files -- including {JPEG} and {PNG}.\r\nJPEG and PNG files can be useful in some cases. For instance, a JPEG file is a valid option for a skybox texture and PNG can useful for masks.\r\nHowever, keep in mind that a JPEG can grow large quickly and so are not the best option with a {DirectX} rendering backend. DDS files\r\nare therefore recommended for textures. \r\n%\\subparagraph{Meshes} %TODO once we will support something more than just nifs"
  },
  {
    "path": "manual/opencs/filters.tex",
    "content": "\\section{Record filters}\r\n\\subsection{Introduction}\r\nFilters are the key element of \\OCS{} use cases by allowing rapid and easy access to the searched records presented in all tables.\r\nTherefore: in order to use this application fully effective you should make sure that all concepts and instructions written in\r\nthe this section of the manual are perfectly clear to you.\r\n\r\nDo not be afraid though, filters are fairly intuitive and easy to use.\r\n\r\n\\subsubsection{Used Terms}\r\n\r\n\\begin{description}\r\n \\item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according\r\n to the some criteria. In case of \\OCS: records are being filtered according to the criteria of user choice. Criteria are written\r\n down in language with simple syntax.\r\n \\item[Criteria] describes condition under with any any record is being select by the filter.\r\n \\item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is:\r\n written with correct syntax.\r\n \\item[Expression] is way we are actually performing filtering. Filter can be treated as ``functions'': accepts arguments, and evaluates\r\n either to the true or false for every column record at the time.\r\n \\item[N-ary] is any expression that expects one or more expressions as arguments. It is useful for grouping two (or more) other expressions\r\n together in order to create filter that will check for criteria placed in two (again: or more) columns (logical \\textit{or}, \\textit{and}).\r\n \\item[unary] is any expression that expects one other expression. The example is \\textit{not} expression. In fact \\textit{not} is the only useful\r\n unary expression in \\OCS{} record filters.\r\n \\item[nullary] is expression that does not accepts other expressions. It accepts arguments specified later.\r\n\\end{description}\r\n\r\n\\subsubsection{Basics}\r\nIn fact you do not need to learn everything about filters in order to use them. In fact all you need to know to achieve decent productivity\r\nwith \\OCS{} is inside basics section.\r\n\r\n\\subsubsection{Interface}\r\nAbove each table there is a field that is used to enter filter: either predefined by the \\OMW{} developers or made by you, the user.\r\nYou probably noticed it before. However there is also completely new element, although using familiar table layout. Go to the application\r\nmenu view, and click filters. You should see set of default filters, made by the \\OMW{} team in the table with the following columns: filter,\r\ndescription and modified.\r\n\r\n\\begin{description}\r\n \\item[ID] contains the name of the filter.\r\n \\item[Modified] just like in all other tables you have seen so far modified indicates if a filter was added, modified or removed.\r\n \\item[Filter] column containing expression of the filter.\r\n \\item[Description] contains the short description of the filter function. Do not expect any surprises there.\r\n\\end{description}\r\n\r\nSo let's learn how to actually use those to speed up your work.\r\n\\subsubsection{Using predefined filters}\r\nUsing those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables\r\ntable and type in the filters field the following: \\mono{project::weapons}. As soon as you complete the text, table will magicly alter\r\nand will show only the weapons. As you could noticed \\mono{project::weapons} is nothing else than a~ID of one of the predefined filters. That is it:\r\nin order to use the filter inside the table you simply type it is name inside the filter field.\r\n\r\nTo make life easier filter IDs follow simple convention.\r\n\r\n\\begin{itemize}\r\n \\item Filter ID filtering a specific record type contains usually the name of a specific group. For instance \\mono{project::weapons} filter\r\n contains the word weapons (did you noticed?). Plural form is always used.\r\n \\item When filtering specific subgroup the ID starts just like in the case of general filter. For instance \\mono{project::weaponssilver} will\r\n filter only silver weapons (new mechanic introduced by the \\BM{}, silver weapons deal double damage against werewolfs) and\r\n \\mono{project::weaponsmagical} will filter only magical weapons (able to hurt ghosts and other supernatural creatures).\r\n \\item There are few exceptions from the above rule. For instance there is a \\mono{project::added}, \\mono{project::removed},\r\n       \\mono{project::modyfied}, \\mono{project::base}.\r\n You would probably except something more like \\mono{project::statusadded} but in this case typing this few extra characters would only\r\n help to break your keyboard faster.\r\n\\end{itemize}\r\n\r\nWe strongly recommend to take a look at the filters table right now to see what you can filter with that. And try using it! It is very simple.\r\n\r\n\\subsection{Advanced}\r\nBack to the manual? Great.\r\n\r\nIf you want to create your own filter you have to know exactly what do you want to get in order to translate this into the expressions.\r\nFinally, you will have to write this with correct syntax. As a result table will show only desired rows.\r\n\r\nAdvance subsection covers everything that you need to know in order to create any filter you may want to %TODO the filter part is actually wrong\r\n\\subsubsection{Namespaces}\r\nDid you noticed that every default filter has \\mono{project::} prefix? It is a \\textit{namespace}, a~term borrowed from the \\CPP{} language.\r\nIn case of \\OCS{} namespace always means scope of the said object\\footnote{You are not supposed to understand this at the moment.}.\r\nBut what does it mean in case of filters? Well, short explanation is actually simple.\r\n\\begin{description}\r\n \\item[project::] namespace indicates that filter is used with the project, in multiple sessions. You can restart \\OCS{} and filter\r\n is still there.\r\n \\item[session::] namespace indicates that filter is not stored trough multiple sessions and once you will quit \\OCS{} (close session)\r\n the filter will be gone. Forever! Until then it can be found inside the filters table.\r\n\\end{description}\r\nIn addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored (even during single session)\r\nanywhere and as the name implies they are supposed to be created when needed only once. Good thing about the one-shot filters is that\r\nyou do not need to open filters table in order to create it. Instead you just type it directly inside the filter field, starting with\r\nexclamation mark: ``!''.\r\n\r\nStill, you may wonder how you are supposed to write expressions, what expressions you should use, and what syntax looks like. Let's start\r\nwith nullary expressions that will allow you to create a basic filter.\r\n\r\n\\subsubsection{Nullary expressions}\r\nAll nullary expressions are used in similar manner. First off: you have to write it is name (for instance: \\mono{string}) and secondly:\r\ncondition that will be checked inside brackets (for instance \\mono{string(something, something)}). If conditions of your expression will be meet\r\nby a record (technical speaking: expression will evaluate to true) the record will show up in the table.\r\n\r\nIt is clear that you need to know what are you checking, that is: what column of the table contains information that you are interested\r\nin and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column\r\nyou want to see, while the second one sets desired value inside of the cell. To separate column argument from the value argument use comma.\r\n\r\n\\paragraph{String -- string(``column'', ``value'')}\r\nString in programmers language is often\\footnote{Often, not always. There are different programming languages using slightly different terms.}\r\njust a word for anything composed of characters. In case of \\OCS{} this is in fact true for every value inside the column that is not composed\r\nof the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string expression\\footnote{There is no\r\nBoolean (``true'' or ``false'') value in the \\OCS. You should use string for those.}. String evaluates to true,\r\nwhen record contains in the specified column exactly the same value as specified.\r\n\r\nSince majority of the columns contain string values, string is among the most often used expressions. Examples:\r\n\\begin{itemize}\r\n \\item \\mono{string(``Record Type'', ``Weapon'')} -- will evaluate to true for all records containing \\mono{Weapon} in the \\mono{Record Type} column cell.\r\n This group contains every weapon (including arrows and bolts) found in the game.\r\n \\item \\mono{string(``Portable'', ``true'')} -- will evaluate to true for all records containing word true inside \\mono{Portable} column cell.\r\n This group contains every portable light sources (lanterns, torches etc.).\r\n\\end{itemize}\r\nThis is probably enough to create around 90 string filters you will eventually need. However, this expression is even more powerful\r\n-- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched\r\nby one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression:\r\n\\mono{string(\"armor type\", \".* gauntlet\"))} because \\mono{.*} in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet.\r\nThere are left and right gauntlets in the \\MW{} so this will evaluate to true for both. Simple, isn't it?\r\n\r\nCreating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, we are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned\r\n\\mono{.*} is needed and therefore the following description of regexps can be skipped by vast majority of readers.\r\n\r\nBefore working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple:\r\nwhen you are writing any word, you are using strictly defined letters -- that is: letters create a word. What you want to do with regular\r\nexpression is to use set of rules that will match to many words. It is not that difficult to see what it's needed to do so: first,\r\nyou will clearly need way to determinate what letters you want to match (word is composed by letters).\r\n\r\nBefore introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string.\r\nYou surely should know about \\mono{\\textasciicircum} anchor and \\mono{\\textdollar}. Putting \\mono{\\textasciicircum} will tell to \\OCS{}\r\nto look on the beginning of string, while \\mono{\\textdollar} is used to mark the end of it. For instance, pattern\r\n\\mono{\\textasciicircum{}Pink.* elephant.\\textdollar} will match any sentence beginning with the word \\mono{Pink} and ending with\r\n\\mono{ elephant.}. Pink fat elephant. Pink cute elephant. It does not matter what is in between because \\mono{.*} is used.\r\n\r\nYou have already seen the power of the simple \\mono{.*}. But what if you want to chose between only two (or more) letters? Well, this is when\r\n\\mono{[|]} comes in handy. If you write something like: \\mono{\\textasciicircum[a|k].*} you are simply telling \\OCS{} to filter anything that\r\nstarts with either \\mono{a} or \\mono{k}. Using \\mono{\\textasciicircum[a|k|l].*} will work in the same manner, that is; it will also cover strings starting with \\mono{l} as well.\r\n\r\nWhat if you want to match more than just one latter? Just use \\mono{(|)}. It is pretty similar to the above one letter as you see, but it is\r\nused to fit more than just one character. For instance: \\mono{\\textasciicircum(Pink|Green).* (elephant|crocodile).\\textdollar} will be\r\ntrue for all sentences starting with \\mono{Pink} or \\mono{Green} and ending with either \\mono{elephant.} or \\mono{crocodile.}.\r\n\r\nRegular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on\r\nQt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use \\OCS{} effectively.\r\n\r\n\\paragraph{Value -- value(``value'', (``open'', ``close''))}\r\nWhile string expression covers vast group of columns containing string values, there are in fact columns with just numerical values like\r\n``weight``. To filter those we need a value expression. This one works in similar manner to the string filter: first token name and criteria\r\ninside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range.\r\nAs you would imagine the range can be specified as including a border value, or excluding. We are using two types of brackets for this:\r\n\\begin{itemize}\r\n \\item To include value use [] brackets. For value equal 5, expression \\mono{value(something, [5, 10])} will evaluate to true.\r\n \\item To exclude value use () brackets. For value equal 5, expression \\mono{value(something, (5, 10))} will evaluate to false.\r\n \\item Mixing brackets is completely legal. For value equal 10, expression \\mono{value(something, [5, 10)} will evaluate to true.\r\n The same expression will evaluate to false for value equal 10.\r\n\\end{itemize}\r\n\r\n\\paragraph{``true'' and ``false''}\r\nNullary \\textit{true} and \\textit{false} do not accept any arguments, and always evaluates to true (in case of \\textit{true})\r\nand false (in case of \\textit{false})  no matter what. The main usage of this expressions is the give users ability to quickly\r\ndisable some part of the filter that makes heavy use of the logical expressions.\r\n\r\n\\subsubsection{Logical expressions}\r\nThis subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the \\OCS{} is logical\r\n\\textit{not}, while the remaining binary expressions are: \\textit{or}, \\textit{and}. This clearly makes them (from the user point of view)\r\nbelonging to the same group of logical expressions.\r\n\r\n\\paragraph{not -- not expression()}\r\nSometimes you may be in need of reversing the output of the expression. This is where \\textit{not} comes in handy. Adding \\textit{not} before\r\nexpression will revert it: if expression was returning true, it will return false; if it was returning false, it will return true. Parenthesis are not needed: \\textit{not} will revert only the first expression following it.\r\n\r\nTo show this on know example, let's consider the \\mono{string(\"armor type\", \".* gauntlet\"))} filter. As we mentioned earlier this will return true\r\nfor every gauntlet found in game. In order to show everything, but gauntlets we simply do \\mono{not string(\"armor type\", \".* gauntlet\"))}.\r\nThis is probably not the most useful filter on earth. The real value of \\textit{not} expression shines when combined with\r\n\\textit{or}, \\textit{and} filters.\r\n\r\n\\paragraph{or -- or(expression1(), expression2())}\r\n\\textit{Or} is a expression that will return true if one of the arguments evaluates to true. You can use two or more arguments, separated by the comma.\r\n\r\n\\textit{Or} expression is useful when showing two different group of records is needed. For instance the standard actor filter is using the following\r\n\\mono{or(string(``record type'', npc), string(``record type'', creature))} and will show both npcs and creatures.\r\n\r\n\\paragraph{and -- and(expression1(), expression2())}\r\n\\textit{And} is a expression that will return true if all arguments evaluates to true. As in the case of ``or'' you can use two or more arguments,\r\nseparated by a comma.\r\nAs we mentioned earlier in the \\textit{not} filter, combining \\textit{not} with \\textit{and} can be very useful. For instance to show all armor types,\r\nexcluding gauntlets you can write the following: \\mono{and (not string(``armor type'', ``.* gauntlet''), string(``Record Type'', ``Armor''))}.\r\n\r\n\\subsubsection{Creating and saving filter}\r\nIn order to create and save new filter, you should go to the filters table, right click and select option ``add record'' from the context menu.\r\nA horizontal widget group at the bottom of the table should show up. From there you should select a namespace responsible for scope of\r\nthe filter (described earlier) and desired ID of the filter. After pressing OK button new entry will show up in the filters table. This filter\r\ndoes nothing at the moment, since it still lacks expressions. In order to add your formula simply double click the filter cell of the new entry\r\nand write it down there.\r\nDone! You are free to use your filter.\r\n\r\n\\subsubsection{Replacing the default filters set}\r\nOpenCS allows you to substitute default filters set provided by us, with your own filters. In order to do so you should create a new project,\r\nadd desired filters, remove undesired and save. Rename the file to the ``defaultfilters'' (do not forget to remove .omwaddon.project extension)\r\nand place it inside your configuration directory.\r\n\r\nThe file acts as template for all new project files from now. If you wish to go back to the old default set, simply rename or remove the custom file.\r\n"
  },
  {
    "path": "manual/opencs/main.tex",
    "content": "\\documentclass[american]{article}\r\n\\usepackage[T1]{fontenc}\r\n\\usepackage{babel}\r\n\\usepackage{txfonts}      % Public Times New Roman text & math font\r\n\\usepackage[pdftex]{graphicx}\r\n\\usepackage[final,colorlinks,pdftex,pdfpagelabels=true]{hyperref}\r\n\\author{OpenMW Team}\r\n\r\n\\def\\pdfBorderAttrs{/Border [0 0 0] } % No border arround Links\r\n\r\n\\def\\CPP{{C\\kern-.05em\\raise.23ex\\hbox{+\\kern-.05em+}}}\r\n\\hypersetup{%\r\n   pdfauthor={Copyright \\textcopyright{} OpenMW Team },\r\n   pdftitle={OpenCS user manual}\r\n}\r\n\\def\\mono{\\texttt}\r\n\\def\\MW{\\textit{Morrowind\\texttrademark{}}}\r\n\\def\\TB{\\textit{Tribunal}}\r\n\\def\\BM{\\textit{Bloodmon}}\r\n\\def\\BS{Bethesda Softworks}\r\n\\def\\OMW{\\hbox{OpenMW}}\r\n\\def\\OCS{\\hbox{OpenCS}}\r\n\r\n\\begin{document}\r\n\r\n\\title{OpenCS User Manual}\r\n\\maketitle\r\n\\newpage\r\n\\tableofcontents{}\r\n\\newpage\r\n\\input{files_and_directories}\r\n\\input{creating_file}\r\n\\input{windows}\r\n\\input{tables}\r\n\\input{recordtypes}\r\n\\input{filters}\r\n\\end{document}\r\n"
  },
  {
    "path": "manual/opencs/recordmodification.tex",
    "content": "\\section{Records Modification}\n\n\\subsection{Introduction}\nSo far you learned how to browse trough records stored inside the content files, but not how to modify them using the \\OCS{} editor. Although browsing is certainly a useful ability on it's own, You probably counted on doing actual editing with this editor. There are few ways user can alter records stored in the content files, each suited for certain class of a problem. In this section We will describe how to do change records using tables interface and edit panel.\n\n\\subsubsection{Glossary}\n\\begin{description}\n  \\item[Edit Panel] Interface element used inside the \\OCS{} to present records data for editing. Unlike table it shows only one record at the time. However it also presents fields that are not visible inside the table. It is also safe to say that Edit Panel presents data in way that is easier to read thanks to it's horizontal layout.\n\\end{description}\n\n\\subsection{Edit Panel Interface}\nEdit Panel is designed to aid you with record modification tasks. As It has been said, it uses vertical layout and presents some additional fields when compared with the table -- and some fields, even if they are actually displayed in the table, clearly ill-suited for modification inside of them (this applies to fields that holds long text strings -- like descriptions). It also displays visual difference between non-editable field and editable.\\\\\nTo open edit panel, please open context menu on any record and choose edit action. This will open edit panel in the same window as your table and will present you the record fields. First data fields are actually not user editable and presented in the form of the text labels at the top of the edit panel. Lower data fields are presented in the form of actually user-editable widgets. Those includes spinboxes, text edits and text fields\\footnote{Those are actually a valid terms used to describe classes of the user interface elements. If you don't understand those, don't worry -- those are very standard {GUI} elements present in almost every application since the rise of the desktop metaphor.}. Once you will finish editing one of those fields, data will be updated. There is no apply button of any sort -- simply use one of those widgets and be merry.\\\\\nIn addition to that you probably noticed some icons in the bar located at the very bottom of the edit panel. Those can be used to perform the following actions:\n\n\\begin{description}\n  \\item[Preview] This will launch simple preview panel -- which will be described later.\n  \\item[Next] This will switch edit panel to the next record. It is worth noticing that edit panel will skip deleted records.\n  \\item[Prev] Do We really need to say what this button does? I guess we should! Well, this will switch edit panel to former record. Deleted records are skipped.\n\\end{description}\n\n\\subsection{Verification tool}\nAs you could notice there is nothing that can stop you from breaking the game by violating record fields logic, and yet -- it is something that you are always trying to avoid. To address this problem \\OCS{} utilizes so called verification tool (or verifer as many prefer to call it) that basically goes trough all records and checks if it contains any illogical fields. This includes for instance torch duration equal 0\\footnote{Interestingly negative values are perfectly fine: they indicate that light source has no duration limit at all. There are records like this in the original game.} or characters without name, race or any other record with a mandatory field missing.\\\\\nThis tool is even more useful than it seems. If you somehow delete race that is used by some of the characters, all those characters will be suddenly broken. As a rule of thumb it is a good idea to use verifer before saving your content file.\\\\\nTo launch this useful tool %don't remember, todo...\nResults are presented as a yet another table with short (and hopefully descriptive enough) description of the identified problem. It is worth noticing that some records located in the \\MW{} esm files are listed by the verification tool -- it is not fault of our tool: those records are just broken. For instance, you actually may find the 0 duration torch. However, those records are usually not placed in game world itself -- and that's good since \\MW{} game engine will crash if player equip light source like this!\\footnote{We would like to thanks \\BS{} for such a useful testing material. It makes us feel special.}\n"
  },
  {
    "path": "manual/opencs/recordtypes.tex",
    "content": "\\section{Record Types}\r\n\r\n\\subsection{Introduction}\r\nA gameworld contains many items, such as chests, weapons and monsters. All these items are merely instances of templates that we call \\textbf{Objects}. The OpenCS \\textbf{Objects} table contains information about each of these template objects, eg. its value and weight in the case of items and an aggression level in the case of NPCs.\r\n\r\nLet's go through all Record Types and discuss what you can tell OpenCS about them.\r\n\r\n\\begin{description}\r\n \\item[Activator:] When the player enters the same cell as this object, a script is started. Often it also has a \\textbf{Script} attached to it, though it not mandatory. These scripts are small bits of code written in a special scripting language that OpenCS can read and interpret.\r\n \\item[Potion:] This is a potion that is not self-made. It has an \\textbf{Icon} for your inventory, Aside from the self-explanatory \\textbf{Weight} and \\textbf{Coin Value}, it has an attribute called \\textbf{Auto Calc} set to ``False''. This means that the effects of this potion are pre-configured. This does not happen when the player makes their own potion.\r\n \\item[Apparatus:] This is a tool to make potions. Again there's an icon for your inventory as well as a weight and a coin value. It also has a \\textbf{Quality} value attached to it: higher the number, the better the effect on your potions will be. The \\textbf{Apparatus Type} describes if the item is a Calcinator, Retort, Alembic or Mortar \\& Pestle. Each has a different effect on the potion the player makes. For more information on this subject, please refer to the \\href{http://www.uesp.net/wiki/Morrowind:Alchemy#Tools}{UESP page on Alchemy Tools}.\r\n \\item[Armor:] This type of item adds \\textbf{Enchantment Points} to the mix. Every piece of clothing or armor has a ''pool'' of potential Magicka that gets unlocked when you enchant it. Strong enchantments consume more Magicka from this pool: the stronger the enchantment, the more Enchantment Points each cast will take up. For more information on this subject, please refer to the \\href{http://www.uesp.net/wiki/Morrowind:Enchant}{Enchant page on UESP}. \\textbf{Health} means the amount of hit points this piece of armor has. If it sustains enough damage, the armor will be destroyed. Finally, \\textbf{Armor Value} tells the game how much points to add to the player character's Armor Rating.\r\n \\item[Book:] This includes scrolls and notes. For the game to make the distinction between books and scrolls, an extra property, \\textbf{Scroll}, has been added. Under the \\textbf{Skill} column a scroll or book can have an in-game skill listed. Reading this item will raise the player's level in that specific skill. For more information on this, please refer to the \\href{http://www.uesp.net/wiki/Morrowind:Skill_Books}{Skill Books page on UESP}.\r\n \\item[Clothing:] These items work just like Armors, but confer no protective properties. Rather than ``Armor Type'', these items have a ``Clothing Type''.\r\n \\item[Container:] This is all the stuff that stores items, from chests to sacks to plants. Its \\textbf{Capacity} shows how much stuff you can put in the container. You can compare it to the maximum allowed load a player character can carry (who will get over-encumbered and unable to move if he crosses this threshold). A container, however, will just refuse to take the item in question when it gets ''over-encumbered''. \\textbf{Organic Container}s are containers such as plants. Containers that \\textbf{Respawn} are not safe to store stuff in. After a certain amount of time they will reset to their default contents, meaning that everything in it is gone forever.\r\n \\item[Creature:] These can be monsters, animals and the like. \r\n \r\n\\end{description}\r\n"
  },
  {
    "path": "manual/opencs/tables.tex",
    "content": "\\section{Tables}\r\n\r\n\\subsection{Introduction}\r\nIf you have launched \\OCS{} already and played around with it for a bit, you have probably gotten the impression that it contains lots of tables.\r\nYou'd be spot on: \\OCS{} is built around using tables. This does not mean it works just like Microsoft Excel or Libre Office Calc, though. \r\nDue to the vast amounts of information involved with \\MW, tables just made the most sense. You have to be able to spot information quickly\r\nand be able to change them on the fly.\r\nLet's browse through the various screens and see what all these tables show.\r\n\r\n\\subsection{Used Terms}\r\n\r\n\\subsubsection{Glossary}\r\n\r\n\\begin{description}\r\n \\item[Record:] An entry in \\OCS{} representing an item, location, sound, NPC or anything else.\r\n\r\n \\item[Instance, Object:] When an item is placed in the world, it isn't an isolated and unique object. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record in the game's library: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are \\textbf{instances}. The central Exquisite Belt record is called a \\textbf{object}. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the \\textbf{object} Exquisite Belt rather than all exquisite belts \\textbf{instances} individually.\r\n\\end{description}\r\n\r\n\\subsubsection{Recurring Terms}\r\nSome columns are recurring throughout \\OCS. They show up in (nearly) every table in \\OCS.\r\n\r\n\\begin{description}\r\n\\item[ID] Many items in the OpenCS database have a unique identifier in both OpenCS and Morrowind. This is usually a very self-explanatory name. For example, the ID for the (unique) black pants of Caius Cosades is ``Caius\\_pants''. This allows you to manipulate the game in many ways. For example, you could add these pants to your inventory by simply opening the console and write: ``player->addItem Caius\\_pants''. Either way, in both Morrowind and OpenCS, the ID is the primary way to identify all these different parts of the game.\r\n\\item[Modified] This column shows what has happened (if something has happened) to this record. There are four possible states in which it can exist. \r\n\\item[Base] means that this record is part of the base game and is in its original state. Usually, if you create a mod, the base game is Morrowind with\r\noptionally the Bloodmoon and Tribunal expansions.\r\n\\item[Added] means that this record was not in the base game and has been added by a~modder.\r\n\\item[Modified] means that the record is part of the base game, but has been changed in some way.\r\n\\item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences\r\nin the game itself have been removed! For example, if you remove the CharGen\\_Bed entry from morrowind.esm, it does not mean the bedroll in the basement\r\nof the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that instance yourself or make sure that that object is replaced\r\nby something that still exists otherwise you will get crashes in the worst case scenario.\r\n\\end{description}\r\n\r\n\\subsection{World Screens}\r\nThe contents of the game world can be changed by choosing one of the options in the appropriate menu at the top of the screen.\r\n\r\n\\subsubsection{Regions}\r\nThis describes the general areas of the gameworld. Each of these areas has different rules about things such as encounters and weather.\r\n\r\n\\begin{description}\r\n \\item[Name:] This is how the game will show your location in-game.\r\n \\item[Map Colour:] This is a six-digit hexadecimal representation of the color used to identify the region on the map available in\r\n World > Region Map. If you do not have an application with a color picker, you can use your favorite search engine to find a color picker on-line.\r\n \\item[Sleep Encounter:] These are the rules for what kind of enemies you might encounter when you sleep outside in the wild.\r\n\\end{description}\r\n\r\n\\subsubsection{Cells}\r\nExpansive worlds such as Vvardenfell, with all its items, NPCs, etc. have a lot going on simultaneously. But if you are in Balmora,\r\nwhy would the computer need to keep track the exact locations of NPCs walking through the corridors in a Vivec canton? All that work would\r\nbe quite useless and bring your system to its knees! So the world has been divided up into squares we call \"cells\". Once your character enters a cell,\r\nthe game will load everything that is going on in that cell so you can interact with it.\r\n\r\nIn the original \\MW{} this could be seen when you were traveling and you would see a small loading bar at the bottom of the screen;\r\nyou had just entered a new cell and the game would have to load all the items and NPCs. The Cells screen in \\OCS{} provides you with a list of cells\r\nin the game, both the interior cells (houses, dungeons, mines, etc.) and the exterior cells (the outside world).\r\n\r\n\\begin{description}\r\n \\item[Sleep Forbidden:] Can the player sleep on the floor? In most cities it is forbidden to sleep outside. Sleeping in the wild carries its\r\n own risks of attack, though, and this entry lets you decide if a player should be allowed to sleep on the floor in this cell or not.\r\n \r\n \\item[Interior Water:] Should water be rendered in this interior cell? The game world consists of an endless ocean at height 0. Then the landscape\r\n is added. If part of the landscape goes below height 0, the player will see water. (See illustration.)\r\n \r\n Setting the cell's Interior Water to true tells the game that this cell is both an interior cell (inside a building, for example, rather than\r\n in the open air) but that there still needs to be water at height 0. This is useful for dungeons or mines that have water in them.\r\n \r\n Setting the cell's Interior Water to false tells the game that the water at height 0 should not be used. Remember that cells that are in\r\n the outside world are exterior cells and should thus \\textit{always} be set to false!\r\n \r\n \\item[Interior Sky:] Should this interior cell have a sky? This is a rather unique case. The \\TB{} expansion took place in a city on \r\n the mainland. Normally this would require the city to be composed of exterior cells so it has a sky, weather and the like. But if the player is\r\n in an exterior cell and looks at his in-game map, he sees the map of the gameworld with an overview of all exterior cells. The player would have to see\r\n the city's very own map, as if he was walking around in an interior cell.\r\n \r\n So the developers decided to create a workaround and take a bit of both: The whole city would technically work exactly like an interior cell,\r\n but it would need a sky as if it was an exterior cell. That is what this is. This is why the vast majority of the cells you will find in this screen\r\n will have this option set to false: It is only meant for these \"fake exteriors\".\r\n \r\n \\item[Region:] To which Region does this cell belong? This has an impact on the way the game handles weather and encounters in this area.\r\n It is also possible for a cell not to belong to any region.\r\n \r\n\\end{description}\r\n\r\n\\subsubsection{Objects}\r\nThis is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type\r\na record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course,\r\ndoes not. All Record Types contain at least a~model. How else would the player see them? Usually they also have a Name, which is what you see\r\nwhen you hover your reticle  over the object.\r\n\r\nThis is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, does not. All Record Types contain at least a model. How else would the player see them? Usually they also have a Name, which is what you see when you hover your reticle over the object.\r\n\r\nPlease refer to the Record Types section for an overview of what each type of object does and what you can tell OpenCS about these objects.\r\n"
  },
  {
    "path": "manual/opencs/windows.tex",
    "content": "\\section{Windows}\r\n\\subsection{Introduction}\r\nThis section describes the multiple windows interface of the \\OCS{} editor. This design principle was chosen in order\r\nto extend the flexibility of the editor, especially on the multiple screens setups and on environments providing advanced\r\nwindows management features, like for instance: multiple desktops found commonly on many open source desktop environments.\r\nHowever, it is enough to have a single large screen to see the advantages of this concept.\r\n\r\nOpenCS windows interface is easy to describe and understand. In fact we decided to minimize use of many windows concepts\r\napplied commonly in various applications. For instance dialog windows are really hard to find in the \\OCS. You are free to try,\r\nthough.\r\n\r\nBecause of this, and the fact that we expect that user is familiar with other applications using windows this section is mostly\r\nfocused on practical ways of organizing work with the \\OCS.\r\n\r\n\\subsection{Basics}\r\nAfter starting \\OCS{} and choosing content files to use a editor window should show up. It probably does not look surprising:\r\nthere is a menubar at the top, and there is a~large empty area. That is it: a brand new \\OCS{} window contains only menubar\r\nand statusbar. In order to make it a little bit more useful you probably want to enable some panels\\footnote{Also known as widgets.}.\r\nYou are free to do so, just try to explore the menubar.\r\n\r\nYou probably founded out the way to enable and disable some interesting tables, but those will be described later. For now, let's\r\njust focus on the windows itself.\r\n\r\n\\paragraph{Creating new windows}\r\nis easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect,\r\nit is also blank, and you are free to add any of the \\OCS{} panels.\r\n\r\n\\paragraph{Closing opened window}\r\nis also easy! Simply close that window decoration button. We suspect that you knew that already, but better to be sure. \r\nClosing last \\OCS{} window will also terminate application session.\r\n\r\n\\paragraph{Multi-everything}\r\nis the main foundation of \\OCS{} interface. You are free to create as many windows as you want to, free to populate it with \r\nany panels you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and\r\nyou are wonder if you are able to have one hundred \\OCS{} windows showing panels of the same type, well most likely you are\r\nable to do so.\r\n\r\nThe principle behind this design decision is easy to see for \\BS{} made editor, but maybe not so clear for users who are\r\njust about to begin their wonderful journey of modding.\r\n\r\n\\subsection{Advanced}\r\nSo why? Why this is created in such manner. The answer is frankly simple: because it is effective. When creating a mod, you often\r\nhave to work only with just one table. For instance you are just balancing weapons damage and other statistics. It makes sense\r\nto have all the space for just that one table. More often, you are required to work with two and switch them from time to time.\r\nAll major graphical environments commonly present in operating systems comes with switcher feature, that is a key shortcut to change\r\nactive window. It is very effective and fast when you have only two windows, each holding only one table. Sometimes you have to work\r\nwith two at the time, and with one from time to time. Here, you can have one window holding two tables, and second holding just one.\r\n\r\nOpenCS is designed to simply make sense and do not slowdown users. It is as simple as possible (but not simpler), and uses one\r\nflexible approach in all cases.\r\n\r\nThere is no point in digging deeper in the windows of \\OCS. Let's explore panels, starting with tables.\r\n\r\n%We should write some tips and tricks here."
  },
  {
    "path": "tes3mp-changelog.md",
    "content": "0.8.1\n-----\n\n* Allow GameSettings packet to override clientside VR settings\n* Include projectile origins in packets for attacking and casting, necessary for VR compatibility\n\n0.8.0\n-----\n\n* Synchronization of custom records for interior cells, allowing for on-the-fly creation of entirely new interiors or ones based on existing interiors\n* Synchronization of custom records for clientside scripts, allowing for on-the-fly creation or overriding of MWScripts\n* Synchronization of custom records for containers, doors, activators, statics, ingredients, apparatuses, lockpicks, probes, repair tools, lights, body parts, sounds and record-based game settings\n* Synchronization of clientside local variables based on which clientside scripts are set to be synchronized by the server\n* Synchronization of clientside global variables based on rules defined for each one in the server scripts, with the possibility of also creating new clientside globals on-the-fly\n* Synchronization of active spell effects for players and actors that no longer relies on players witnessing the spellcasting associated with the effects\n* Synchronization of death animations for players and actors\n* Synchronization of merchant inventories and gold pools\n* Synchronization of environmental sounds made by players, e.g. when picking up or dropping items, when opening or closing chests and doors\n* Synchronization of plant harvesting from herbalism mods\n* Reworked attack synchronization with more precision, fixing skipped attacks for creatures with fast attack animations and making melee attack animations not depend on the direction a player or actor is moving in on the current client\n* Reworked spellcasting synchronization, fixing improper failed spellcasting for actors with fast casting animations and reducing desyncs by not tying spellcasting for non-local actors and players to their animations on the current client\n* Reworked equipment synchronization for players with less spammy packets\n* Packet for resetting interior and exterior cells on-the-fly\n* Packet for setting players as allies so they don't break each other's stealth and so their companions don't fight back when attacked by mistake\n* Packet for overriding cell destinations, useful for moving players to instances of interiors instead of regular interiors\n* Packet for tracking hits received by objects\n* Packet for tracking dialogue choices selected by players\n* Packet for tracking the cooldowns of powers used by players\n* Allow GameSettings packet to override clientside game settings\n* Allow custom records to override AI services for NPCs, alarm and flee values for NPCs and creatures, as well as scales, blood types, soul values and attack damage for creatures\n* Fix freezes related to having too many players in a single interior cell\n* Fix packet spam from some situations where actors start fights with players during dialogue\n* Fix ObjectActivate packets not being sent when picking up items from inventory screen\n* Fix ObjectDelete packets not being sent when non-container items are purchased from merchants who own them\n* Fix PlayerItemUse packets not being sent when using quick keys\n* Fix ConsoleCommand packets being ignored when sent by the server without a cell being associated with them\n* Fix desyncs caused by late-arriving ActorAuthority packets by sending the packets to everyone on the server instead of only the players who have the associated cell loaded\n* Fix indexes used when synchronizing the unassigning of quick keys\n* Fix stealing being ignored when using Take All on owned containers\n* Fix guards ignoring fights between players and other guards\n* Prevent players from attacking while in persuasion dialogues with NPCs\n* Prevent players from sending DoorState packets when not logged in\n* Prevent NPCs from saying their dying words if they have been loaded up as dead from the server\n* Prevent players and actors from playing their equipping sounds when they are first encountered\n* Alleviate actor-related desyncs by not ignoring an entire packet when one of the actors referenced in it cannot be found\n* Track kills caused through spell effects that do damage over time\n* Track uses of the console by players\n* Add serverside script functions for saving the identities of summoners\n* Implement hashing for player passwords\n\n0.7.0\n-----\n\n* Synchronization of custom records for spells, potions, enchantments, creatures, NPCs, armor, books, clothing, miscellaneous items and weapons\n* Synchronization of weather\n* Synchronization of world map exploration\n* Synchronization of spells cast through items\n* Synchronization of player reputations\n* Reworked attack synchronization now accounts for \"cast on strike\" enchantments, knockdowns and projectile speeds\n* Reworked container synchronization that relies on server approval of every container change\n* Reworked object synchronization that relies on server approval of every action on objects and tracks whether actions originated from regular gameplay, the console or from ingame scripts\n* Reworked time synchronization that preserves ingame date and does not allow for any unilateral clientside time changes\n* Reworked summoning synchronization that prevents duplicate summons and retains summon durations\n* Reworked inventory, equipment, attribute, skill and dynamic stat packets that are sent more often while containing minimal instead of maximal data\n* Packet for activation of objects\n* Packet for using items in inventory\n* Packet for actor AI\n* Packet for actor deaths, used to track their killers\n* Packet for overriding collision on objects\n* Packet for setting momentum of players\n* Packet for saving and loading marked locations and selected spells\n* Player scales and disguises as creatures are now included in shapeshift packets\n* Soulgem souls are now included in item and object placement packets\n* Physics framerate caps and enforced log levels are now included in game settings packets\n* Journal entry timestamps are now included in journal packets\n* The value of the OnPCDrop variable for player-placed objects is now preserved by being included in object placement packets\n* Video play packets no longer have hardcoded synchronization\n* Death packets now include clear information about the killers, and deaths through spells are no longer treated as suicides\n* Inventory packets no longer include bound items, preventing them from becoming persistent upon quitting\n* Fix broken check in equipment synchronization that was preventing throwing weapon synchronization from working\n* Fix rare inability to start up game caused by use of potentially invalid pointer for storing cell unloads\n* Death as a werewolf now removes status as known werewolf, to prevent infinite aggression from NPCs\n* Death as a criminal now prevents witnesses to previous crimes from continuing combat\n* Players are now kicked from the server when failing to reply to its handshake\n* Players can no longer open up their rest menu before logging in\n* NPC followers now follow players correctly from interiors to exteriors and vice versa, and are able to follow non-authority players\n* NPCs can now start combat through dialogue with non-authority players\n* The console window now displays the unique index of a selected object\n\n0.6.2\n-----\n\n* Packet for quick keys\n* Packet for player sounds\n* Packet for player animations\n* Packet for console commands\n* Enchantment charge values are now included in item and object placement packets\n* Settings packet can now be used to separately enable or disable resting in beds, resting in the wilderness and waiting\n* Changes in attribute and skill modifiers now send their respective packets\n* Attribute and skill packets using 0 as a modifier can now remove all attribute or skill fortification effects from a player\n* Completion of vendor trades and skill training now sends inventory packets\n* Item drag and dropping is now finished when arrested or when moved to another cell by the server\n* Window minimization no longer pauses the game\n* Actor speech captions are now searched for in dialogues instead of being sent in packets\n\n0.6.1\n-----\n\n* Record player positions before their last cell change\n* Remove inertia from players who are teleported via a packet\n* Fix data loss caused by server packets attempting to add invalid items, spells and quests\n* Fix client crashes caused by items with state disabling scripts, as well as their side effects of making players disappear to others\n* Fix server crashes caused by improper interior initialization when exterior 0, 0 is loaded\n* Fix server freezes caused by infinite topic packet spam from local scripts\n* Fix spell casting probability synchronization\n* Prevent players from using excessively long names\n\n0.6.0\n-----\n\n* Synchronization of NPC & creature positions, stats, equipment, speech, attacks, spells & animations\n* Reworked server browser with major stability fixes and additional information about servers\n* Synchronization of client & server plugins (non-automated)\n* Synchronization of leveled & summoned creature spawns\n* Synchronization of rotation animations\n* Synchronization of journal entries\n* Synchronization of faction ranks, reputation and expulsion states\n* Synchronization of dialogue topics\n* Synchronization of kill counts checked in quest progression\n* Synchronization of trap states\n* Synchronization of object states (i.e. whether they are enabled or disabled)\n* Synchronization of player werewolf states\n* Synchronization of time of day\n* Packet for saving and loading of read skill books\n* Fix to messagebox buttons for client scripts\n* Fix to synchronization of object locking/unlocking\n* Packet for jailing players\n* Packet for setting the difficulty for each player\n* Bandwidth optimization for all packet types\n* Various fixes to client & server crashes\n\n0.5.2\n-----\n\n* Fix to server crash caused by not deleting empty cells upon players disconnecting\n* Fix to client freezes caused by invalid spells and races\n* Fix to players not being spawned when using other base master files\n* Fix to visual glitch where flying players bounced up and down continuously\n\n0.5.1\n-----\n\n* Fix to server crash caused by incorrect cell comparison added by bandwidth optimization changes\n* Fix to server browser freeze caused by connection failures and incomplete data received from master server\n\n0.5.0\n-----\n\n* Server browser\n* Synchronization of containers\n* Reworked world packets allowing for the saving and loading of world state, including container state\n* Bandwidth optimization by forwarding the most frequent player packets only to other players in the same loaded cells\n\n0.4.1\n-----\n\n* Packet for saving and loading spellbooks\n\n0.4.0\n-----\n\n* Synchronization of spells\n* Packet for saving and loading inventories\n* Being in a menu no longer prevents you from sending packets about your client\n* Fixes to freezes and problems caused by players moving around in cells from expansions/plugins that are not loaded by others\n\n0.3.0\n-----\n\n* Synchronization of world object removal, placement, scaling, locking and unlocking\n* Synchronization of local, global and member script variables for specific scripts\n* Synchronization for the setdelete, placeat, setscale, lock and unlock console commands\n* Player markers on minimap\n* Death reasons in chat\n* Fix to client freeze related to players logging in at the same time\n* Fix to server crash related to sending information about invalid players\n\n0.2.0\n-----\n\n* Packets for saving and loading classes, birthsigns, dynamic stats, levels, level progress, attribute bonuses from skill increases and progress towards skill increases\n* Version checking to prevent mismatches between clients and servers\n\n0.0.1b\n------\n\n* Synchronization of attributes and skills\n* Fix to memory leaks related to player initialization on Windows servers\n* Fix to various graphical glitches\n* Main menu buttons for starting, saving and loading games are now disabled\n\n0.0.1a\n------\n\n* Combat animation\n* Synchronization of melee and ranged (bow, crossbow, throwable weapons) combat\n* Synchronization of health, magicka, fatigue and death\n* Synchronization of cell changes\n* Server-side Lua scripting\n* Chat\n\n0.0.1\n-----\n\n* Initial networking and packet architecture\n* Synchronization of player character generation\n* Synchronization of player position\n* Synchronization of player attack states (unarmed, armed with a weapon, using a spell)\n* Synchronization of movement and jump animations\n"
  },
  {
    "path": "tes3mp-credits.md",
    "content": "TES3MP Credits\n==============\n\nC++ programmers\n---------------\n\n    David Cernat - World, NPC & quest sync, player sync improvements, state saving & loading, extensive scripting\n    Stanislav Zhukov (Koncord) - Foundation for networking & scripting systems, player sync, server browser & master server\n\n\nDeployment scripts\n------------------\n\n    Grim Kriegor\n\n\nCommunity administrators\n------------------------\n\n    Volk Milit (Ja'Virr-Dar)\n    Testman\n\n\nCommunity moderators\n--------------------\n\n    Learwolf\n    Michael Fitzmayer (mupf)\n    Nac\n    NicholasAH\n    Shnatsel\n    Snapjaw\n\n\nArt\n---\n\n    Texafornian - TES3MP logo\n\n\nSuper special thanks\n--------------------\n\n    Alexander Ovsyannikov\n    Bret Curtis (psi29a)\n    Gabriel Pascu (iGrebla)\n    greetasdf\n    Jason Ginther\n    Learwolf\n    Mads Buvik Sandvei (madsbuvi)\n    Marc Zinnschlag (Zini)\n    Mitch (Zaberius)\n    scrawl\n    Testman\n\n\nSpecial thanks\n--------------\n\n    Aesylwinn\n    Battlerax - Various small fixes\n    Boyos\n    Brandon Guffey\n    Caledonii\n    Camul\n    David Wery\n    Delphik\n    DestinedToDie\n    Donovan Ando\n    DrunkenMonk\n    Gluka\n    Goodevil\n    Ignatious\n    James Wards of Gore Corps LAN Club (gorecorps.co.nz)\n    Jeff Russell\n    Jeremy M.\n    Kyle Willey of Loreshaper Games (steempeak.com/@loreshapergames)\n    Lewis Sadlier\n    Luc Keating\n    Mark Epstein aka StirlizZ\n    Michael Zagar (Zoops)\n    Nkfree\n    Olaxan\n    ppsychrite\n    Rhiyo\n    Ryan Gage\n    Scorcio\n    Simon Nemes\n    Snorkel the Dolphin\n    Swims-in-Shadows aka StrayHALO_MAN\n    Texafornian\n    Thrud\n    Wraith\n    Zach Wild\n    Zaphida\n    OpenMW for creating an amazing open source project\n\n"
  }
]